mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-01-09 06:43:09 +00:00
1st set of IIO new device support, features and cleanup for 6.6
New device support * adi,ad8366 - Add support for the HMC792 digital attenuator (mostly chip specific data) * alwinner,sun20i-gpadc - New driver for the integrated ADC on a number of allwinner SoCs including dt-binding documentation. * microchip,mcp4728 - New driver for this quad channel DAC. Includes dt-bindings. * miramems, da280 - Add ID for DA217 accelerometer which is compatible with the da280. * murata,irs-d200 - New driver for this passive infrared sensor typically used for human detection. Includes bindings and a few pieces of new ABI to cover a case of needing to count a number of repeats of an event before reporting it. * rohm,bu27008 - Add initial support for the BU27010 RGB + flickering sensor to this driver. Substantial refactoring was needed to enable this. Features * adi,admv8818 - Add mode that bypasses the input and output filters. * amlogic,meson - Support control of the MUX on channel 7, exposed as multiple channels. - Support channel labels. * sensirion,scd4x - Add pressure compensation. Controlled via an 'output' pressure channel. * ti,lmp92040 - Add IIO buffered supported (read via chrdev). * vishay,vcnl4000 - Add proximity interrupt support for vcnl4200. - Add proximity integration time control for vcnl4200. - Add illuminance integration time control for vcnl4040 and vcnl4200. - Add calibration bias, proximity and illuminance event period, and oversampling ratio control for vcnl4040 and vncl4200. Cleanup and minor fixes * core - Tidy up handling of set_trigger_state() callback return values to consistently assume no positive return values. - Use min() rather than min_t() in a case where types were clearly the same. - Drop some else statements that follow continue with a loop or a returns. - White space and comment format cleanup. - Use sysfs_match_string() helper to improve readability. - Use krealloc_array() to make it explicit a krealloc is for an array of structures, not just one. * tools - Tidy up potential overflow in array index. * tree wide - Fix up includes for DT related headers. - Drop some error prints in places where as similar error message is printed by the function being called. - Tidy up handling of return value from platform_get_irq() to no longer take into account 0 as a value that might be returned. Similar for fwnode_irq_get(). * adi,ad7192 - Add missing error check and improved debug logging. - Use sysfs_emit_at() rather than open coded variant. * adi,adis16475 - Drop unused scan element enum entries. - Specify that a few more devices support burst32 mode. * adi,admv1013 - Enable all required regulators and document as required in the dt-binding. * adi,admv1014 - Make all regulators required in the dt-binding as the device needs them all enabled. * adi,adxl313 - Fix wrong enum values being used in the i2c_device_id table. - Use i2c_get_match_data() to reduce open coded handling of the various id tables. * allwinner,gpadc - Make the kconfig text more specific to make space for separate drivers for other Allwinner devices. * amlogic,meson - Drop unused timestamp channels as no buffer support. - Various minor reorganizations to enable addition of support channel 7 MUX. - Initialize some default values to account for potential previous user since reboot. * qcom,spmi-adc5 - Add ADC5_GPIO2_100K_PU support to driver to line up with bindings. * qcom,spmi-adc7 - Use predefined channel ID definitions rather than values. * invensense, common - Factor out the timestamp handling to a module used by both mpu6050 and icm42600. * invensense,mpu6050 - Read as many FIFO elements as possible in one bus access. * men,s188 - Drop redundant initialization of driver owner field. * microchip,mcp4018 and mcp4531 - Use i2c_get_match_data() instead of open coding. Includes making the data format the same for the i2c_device_id and firmware match tables. * semtech,sx9310 - dt-bindings: Add reference to IIO schema to provide the label property. * semtech,sx9324 - dt-bindings: Add reference to IIO schema to provide the label property. * st,stm32-adc - Use devm_platform_get_and_ioremap_resource() instead of open coded version. * st,stm-lptimer-trigger - Drop setting platform drvdata as it wasn't then used. * ti,ads1015 - Fix wrong dt binding description of ti,datarate for some devices. * vishay,vcnl4200 - Move to switch statements for channel type checking to make later additions simpler. -----BEGIN PGP SIGNATURE----- iQJFBAABCAAvFiEEbilms4eEBlKRJoGxVIU0mcT0FogFAmTTydERHGppYzIzQGtl cm5lbC5vcmcACgkQVIU0mcT0Foj9sw//QDch/sLMb1bEiJTHDwECuSgm+jWth8gi CdG3GQerBU2U+eQkf2jgG73HhfHccanKWGAHfkdOLSblcFWJL6Q+MSJCNu4J3kGY 2XUkdazXMXRbfqoq3vqnC4u6mEMXGTpq8JZoAz7XXCs8GcW/loyqV2E54cbQJxCJ KSqCWIhoWAU7/PQkgOJLBnDirMt+rEwUObifG4PJcOYIPq17qS+XhpMVPydNfYgg 3PJ+jK4Gg6cZsI3lW7vaBoLvzYRn+Ri2vVc2ZnwyLI/y9rEJPjt4aXt9FZbw10Eg S/7twD1Ns5n8X6IS3VFA4A4ITu0VFX3pjOg9WLEpkqRJqLt1n2R9pcnTkD6x4a0r 6LZvmLij9R6ozPZKlalncyOPZamnEcIxyqyuqcEEFDAF6/Mf+VznwBQM8HDhQ/CO 3OAGkNprtXIvKAz/xiGx9r8z8d5Gt1lD+vclcDcf6U8YwEkMWrc95EVIumcNOkG2 qNESgzDvOh92aHpKfwrsHproNKcKbtm9he1BrN5u28G+UBA1jsPg15hmYVkv/Hd3 v6tgvkxVSn2xS94K2BhqjLzOgXgl4EmbSewg5AfPVNYM1RZrMLJvBky1/81JJmsD 565dcF/vE3/hguFX5lCOKGTxT88pwFNHth26ypAOXuwhUNz+jxp9NJ0Adn/IR8Kp hA/SfjALiDA= =hTrR -----END PGP SIGNATURE----- Merge tag 'iio-for-6.6a' of https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio into char-misc-next Jonathan writes: 1st set of IIO new device support, features and cleanup for 6.6 New device support * adi,ad8366 - Add support for the HMC792 digital attenuator (mostly chip specific data) * alwinner,sun20i-gpadc - New driver for the integrated ADC on a number of allwinner SoCs including dt-binding documentation. * microchip,mcp4728 - New driver for this quad channel DAC. Includes dt-bindings. * miramems, da280 - Add ID for DA217 accelerometer which is compatible with the da280. * murata,irs-d200 - New driver for this passive infrared sensor typically used for human detection. Includes bindings and a few pieces of new ABI to cover a case of needing to count a number of repeats of an event before reporting it. * rohm,bu27008 - Add initial support for the BU27010 RGB + flickering sensor to this driver. Substantial refactoring was needed to enable this. Features * adi,admv8818 - Add mode that bypasses the input and output filters. * amlogic,meson - Support control of the MUX on channel 7, exposed as multiple channels. - Support channel labels. * sensirion,scd4x - Add pressure compensation. Controlled via an 'output' pressure channel. * ti,lmp92040 - Add IIO buffered supported (read via chrdev). * vishay,vcnl4000 - Add proximity interrupt support for vcnl4200. - Add proximity integration time control for vcnl4200. - Add illuminance integration time control for vcnl4040 and vcnl4200. - Add calibration bias, proximity and illuminance event period, and oversampling ratio control for vcnl4040 and vncl4200. Cleanup and minor fixes * core - Tidy up handling of set_trigger_state() callback return values to consistently assume no positive return values. - Use min() rather than min_t() in a case where types were clearly the same. - Drop some else statements that follow continue with a loop or a returns. - White space and comment format cleanup. - Use sysfs_match_string() helper to improve readability. - Use krealloc_array() to make it explicit a krealloc is for an array of structures, not just one. * tools - Tidy up potential overflow in array index. * tree wide - Fix up includes for DT related headers. - Drop some error prints in places where as similar error message is printed by the function being called. - Tidy up handling of return value from platform_get_irq() to no longer take into account 0 as a value that might be returned. Similar for fwnode_irq_get(). * adi,ad7192 - Add missing error check and improved debug logging. - Use sysfs_emit_at() rather than open coded variant. * adi,adis16475 - Drop unused scan element enum entries. - Specify that a few more devices support burst32 mode. * adi,admv1013 - Enable all required regulators and document as required in the dt-binding. * adi,admv1014 - Make all regulators required in the dt-binding as the device needs them all enabled. * adi,adxl313 - Fix wrong enum values being used in the i2c_device_id table. - Use i2c_get_match_data() to reduce open coded handling of the various id tables. * allwinner,gpadc - Make the kconfig text more specific to make space for separate drivers for other Allwinner devices. * amlogic,meson - Drop unused timestamp channels as no buffer support. - Various minor reorganizations to enable addition of support channel 7 MUX. - Initialize some default values to account for potential previous user since reboot. * qcom,spmi-adc5 - Add ADC5_GPIO2_100K_PU support to driver to line up with bindings. * qcom,spmi-adc7 - Use predefined channel ID definitions rather than values. * invensense, common - Factor out the timestamp handling to a module used by both mpu6050 and icm42600. * invensense,mpu6050 - Read as many FIFO elements as possible in one bus access. * men,s188 - Drop redundant initialization of driver owner field. * microchip,mcp4018 and mcp4531 - Use i2c_get_match_data() instead of open coding. Includes making the data format the same for the i2c_device_id and firmware match tables. * semtech,sx9310 - dt-bindings: Add reference to IIO schema to provide the label property. * semtech,sx9324 - dt-bindings: Add reference to IIO schema to provide the label property. * st,stm32-adc - Use devm_platform_get_and_ioremap_resource() instead of open coded version. * st,stm-lptimer-trigger - Drop setting platform drvdata as it wasn't then used. * ti,ads1015 - Fix wrong dt binding description of ti,datarate for some devices. * vishay,vcnl4200 - Move to switch statements for channel type checking to make later additions simpler. * tag 'iio-for-6.6a' of https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio: (73 commits) Documentation: ABI: testing: admv8818: add bypass drivers: iio: filter: admv8818: add bypass mode iio: light: bd27008: Support BD27010 RGB iio: light: bu27008: add chip info dt-bindings: iio: ROHM BU27010 RGBC + flickering sensor iio: add MCP4728 I2C DAC driver dt-bindings: iio: dac: add mcp4728.yaml drivers: iio: admv1013: add vcc regulators dt-bindings: iio: admv1013: add vcc regulators iio: trigger: stm32-lptimer-trigger: remove unneeded platform_set_drvdata() iio: adc: men_z188_adc: Remove redundant initialization owner in men_z188_driver dt-bindings: iio: admv1014: make all regs required iio: cdc: ad7150: relax return value check for IRQ get iio: mb1232: relax return value check for IRQ get iio: adc: fix the return value handle for platform_get_irq() tools: iio: iio_generic_buffer: Fix some integer type and calculation iio: potentiometer: mcp4531: Use i2c_get_match_data() iio: potentiometer: mcp4018: Use i2c_get_match_data() iio: core: Fix issues and style of the comments iio: core: Switch to krealloc_array() ...
This commit is contained in:
commit
43fbd50649
@ -2163,3 +2163,19 @@ Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
An example format is 16-bytes, 2-digits-per-byte, HEX-string
|
||||
representing the sensor unique ID number.
|
||||
|
||||
What: /sys/.../events/in_proximity_thresh_either_runningperiod
|
||||
KernelVersion: 6.6
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
A running period of time (in seconds) for which
|
||||
in_proximity_thresh_either_runningcount amount of conditions
|
||||
must occur before an event is generated. If direction is not
|
||||
specified then this period applies to both directions.
|
||||
|
||||
What: /sys/.../events/in_proximity_thresh_either_runningcount
|
||||
KernelVersion: 6.6
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Number of conditions that must occur, during a running
|
||||
period, before an event is generated.
|
||||
|
@ -7,6 +7,8 @@ Description:
|
||||
|
||||
- auto -> Adjust bandpass filter to track changes in input clock rate.
|
||||
- manual -> disable/unregister the clock rate notifier / input clock tracking.
|
||||
- bypass -> bypass low pass filter, high pass filter and disable/unregister
|
||||
the clock rate notifier
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/filter_mode
|
||||
KernelVersion:
|
||||
|
@ -0,0 +1,91 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/adc/allwinner,sun20i-d1-gpadc.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Allwinner D1 General Purpose ADC
|
||||
|
||||
maintainers:
|
||||
- Maksim Kiselev <bigunclemax@gmail.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- allwinner,sun20i-d1-gpadc
|
||||
|
||||
"#io-channel-cells":
|
||||
const: 1
|
||||
|
||||
"#address-cells":
|
||||
const: 1
|
||||
|
||||
"#size-cells":
|
||||
const: 0
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
resets:
|
||||
maxItems: 1
|
||||
|
||||
patternProperties:
|
||||
"^channel@[0-9a-f]+$":
|
||||
$ref: adc.yaml
|
||||
type: object
|
||||
description:
|
||||
Represents the internal channels of the ADC.
|
||||
|
||||
properties:
|
||||
reg:
|
||||
items:
|
||||
minimum: 0
|
||||
maximum: 15
|
||||
|
||||
required:
|
||||
- reg
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
required:
|
||||
- "#io-channel-cells"
|
||||
- clocks
|
||||
- compatible
|
||||
- interrupts
|
||||
- reg
|
||||
- resets
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/sun20i-d1-ccu.h>
|
||||
#include <dt-bindings/reset/sun20i-d1-ccu.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
gpadc: adc@2009000 {
|
||||
compatible = "allwinner,sun20i-d1-gpadc";
|
||||
reg = <0x2009000 0x400>;
|
||||
clocks = <&ccu CLK_BUS_GPADC>;
|
||||
resets = <&ccu RST_BUS_GPADC>;
|
||||
interrupts = <73 IRQ_TYPE_LEVEL_HIGH>;
|
||||
#io-channel-cells = <1>;
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
channel@0 {
|
||||
reg = <0>;
|
||||
};
|
||||
|
||||
channel@1 {
|
||||
reg = <1>;
|
||||
};
|
||||
};
|
||||
...
|
@ -78,9 +78,9 @@ patternProperties:
|
||||
ti,datarate:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
minimum: 0
|
||||
maximum: 6
|
||||
maximum: 7
|
||||
description: |
|
||||
Data acquisition rate in samples per second
|
||||
Data acquisition rate in samples per second for ADS1015, TLA2024
|
||||
0: 128
|
||||
1: 250
|
||||
2: 490
|
||||
@ -88,6 +88,17 @@ patternProperties:
|
||||
4: 1600 (default)
|
||||
5: 2400
|
||||
6: 3300
|
||||
7: 3300
|
||||
|
||||
Data acquisition rate in samples per second for ADS1115
|
||||
0: 8
|
||||
1: 16
|
||||
2: 32
|
||||
3: 64
|
||||
4: 128 (default)
|
||||
5: 250
|
||||
6: 475
|
||||
7: 860
|
||||
|
||||
required:
|
||||
- reg
|
||||
|
@ -0,0 +1,49 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
|
||||
$id: http://devicetree.org/schemas/iio/dac/microchip,mcp4728.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Microchip MCP4728 DAC
|
||||
|
||||
maintainers:
|
||||
- Andrea Collamati <andrea.collamati@gmail.com>
|
||||
|
||||
description: |
|
||||
MCP4728 is a quad channel, 12-bit voltage output
|
||||
Digital-to-Analog Converter with non-volatile
|
||||
memory and I2C compatible Serial Interface.
|
||||
https://www.microchip.com/en-us/product/mcp4728
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: microchip,mcp4728
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
vdd-supply:
|
||||
description: |
|
||||
Provides both power and acts as the reference supply on the MCP4728
|
||||
when Internal Vref is not selected.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- vdd-supply
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
dac@60 {
|
||||
compatible = "microchip,mcp4728";
|
||||
reg = <0x60>;
|
||||
vdd-supply = <&vdac_vdd>;
|
||||
};
|
||||
};
|
@ -39,6 +39,46 @@ properties:
|
||||
description:
|
||||
Analog voltage regulator.
|
||||
|
||||
vcc-drv-supply:
|
||||
description:
|
||||
RF Driver voltage regulator.
|
||||
|
||||
vcc2-drv-supply:
|
||||
description:
|
||||
RF predriver voltage regulator.
|
||||
|
||||
vcc-vva-supply:
|
||||
description:
|
||||
VVA Control Circuit voltage regulator.
|
||||
|
||||
vcc-amp1-supply:
|
||||
description:
|
||||
RF Amplifier 1 voltage regulator.
|
||||
|
||||
vcc-amp2-supply:
|
||||
description:
|
||||
RF Amplifier 2 voltage regulator.
|
||||
|
||||
vcc-env-supply:
|
||||
description:
|
||||
Envelope Detector voltage regulator.
|
||||
|
||||
vcc-bg-supply:
|
||||
description:
|
||||
Mixer Chip Band Gap Circuit voltage regulator.
|
||||
|
||||
vcc-bg2-supply:
|
||||
description:
|
||||
VGA Chip Band Gap Circuit voltage regulator.
|
||||
|
||||
vcc-mixer-supply:
|
||||
description:
|
||||
Mixer voltage regulator.
|
||||
|
||||
vcc-quad-supply:
|
||||
description:
|
||||
Quadruppler voltage regulator.
|
||||
|
||||
adi,detector-enable:
|
||||
description:
|
||||
Enable the Envelope Detector available at output pins VENV_P and
|
||||
@ -69,6 +109,16 @@ required:
|
||||
- clocks
|
||||
- clock-names
|
||||
- vcm-supply
|
||||
- vcc-drv-supply
|
||||
- vcc2-drv-supply
|
||||
- vcc-vva-supply
|
||||
- vcc-amp1-supply
|
||||
- vcc-amp2-supply
|
||||
- vcc-env-supply
|
||||
- vcc-bg-supply
|
||||
- vcc-bg2-supply
|
||||
- vcc-mixer-supply
|
||||
- vcc-quad-supply
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/spi/spi-peripheral-props.yaml#
|
||||
@ -87,6 +137,16 @@ examples:
|
||||
clocks = <&admv1013_lo>;
|
||||
clock-names = "lo_in";
|
||||
vcm-supply = <&vcm>;
|
||||
vcc-drv-supply = <&vcc_drv>;
|
||||
vcc2-drv-supply = <&vcc2_drv>;
|
||||
vcc-vva-supply = <&vcc_vva>;
|
||||
vcc-amp1-supply = <&vcc_amp1>;
|
||||
vcc-amp2-supply = <&vcc_amp2>;
|
||||
vcc-env-supply = <&vcc_env>;
|
||||
vcc-bg-supply = <&vcc_bg>;
|
||||
vcc-bg2-supply = <&vcc_bg2>;
|
||||
vcc-mixer-supply = <&vcc_mixer>;
|
||||
vcc-quad-supply = <&vcc_quad>;
|
||||
adi,quad-se-mode = "diff";
|
||||
adi,detector-enable;
|
||||
};
|
||||
|
@ -103,6 +103,14 @@ required:
|
||||
- clocks
|
||||
- clock-names
|
||||
- vcm-supply
|
||||
- vcc-if-bb-supply
|
||||
- vcc-vga-supply
|
||||
- vcc-vva-supply
|
||||
- vcc-lna-3p3-supply
|
||||
- vcc-lna-1p5-supply
|
||||
- vcc-bg-supply
|
||||
- vcc-quad-supply
|
||||
- vcc-mixer-supply
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/spi/spi-peripheral-props.yaml#
|
||||
|
@ -0,0 +1,49 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/light/rohm,bu27010.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: ROHM BU27010 color sensor
|
||||
|
||||
maintainers:
|
||||
- Matti Vaittinen <mazziesaccount@gmail.com>
|
||||
|
||||
description: |
|
||||
The ROHM BU27010 is a sensor with 6 photodiodes (red, green, blue, clear,
|
||||
IR and flickering detection) with five configurable channels. Red, green
|
||||
and flickering detection being always available and two out of the rest
|
||||
three (blue, clear, IR) can be selected to be simultaneously measured.
|
||||
Typical application is adjusting LCD/OLED backlight of TVs, mobile phones
|
||||
and tablet PCs.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: rohm,bu27010
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
vdd-supply: true
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- vdd-supply
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
light-sensor@38 {
|
||||
compatible = "rohm,bu27010";
|
||||
reg = <0x38>;
|
||||
};
|
||||
};
|
@ -0,0 +1,60 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/proximity/murata,irsd200.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Murata IRS-D200 PIR sensor
|
||||
|
||||
maintainers:
|
||||
- Waqar Hameed <waqar.hameed@axis.com>
|
||||
|
||||
description:
|
||||
PIR sensor for human detection.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: murata,irsd200
|
||||
|
||||
reg:
|
||||
items:
|
||||
- enum:
|
||||
- 0x48
|
||||
- 0x49
|
||||
description: |
|
||||
When the AD pin is connected to GND, the slave address is 0x48.
|
||||
When the AD pin is connected to VDD, the slave address is 0x49.
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
description:
|
||||
Type should be IRQ_TYPE_EDGE_RISING.
|
||||
|
||||
vdd-supply:
|
||||
description:
|
||||
3.3 V supply voltage.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- vdd-supply
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
proximity@48 {
|
||||
compatible = "murata,irsd200";
|
||||
reg = <0x48>;
|
||||
interrupts = <24 IRQ_TYPE_EDGE_RISING>;
|
||||
vdd-supply = <®ulator_3v3>;
|
||||
};
|
||||
};
|
||||
...
|
@ -15,6 +15,9 @@ description: |
|
||||
Specifications about the devices can be found at:
|
||||
https://www.semtech.com/products/smart-sensing/sar-sensors/sx9310
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/iio/iio.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
@ -102,7 +105,7 @@ required:
|
||||
- reg
|
||||
- "#io-channel-cells"
|
||||
|
||||
additionalProperties: false
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
|
@ -13,6 +13,9 @@ maintainers:
|
||||
description: |
|
||||
Semtech's SX9324 proximity sensor.
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/iio/iio.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: semtech,sx9324
|
||||
@ -167,7 +170,7 @@ required:
|
||||
- reg
|
||||
- "#io-channel-cells"
|
||||
|
||||
additionalProperties: false
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
|
@ -40,8 +40,8 @@ static const struct regmap_config adxl31x_i2c_regmap_config[] = {
|
||||
|
||||
static const struct i2c_device_id adxl313_i2c_id[] = {
|
||||
{ .name = "adxl312", .driver_data = (kernel_ulong_t)&adxl31x_chip_info[ADXL312] },
|
||||
{ .name = "adxl313", .driver_data = (kernel_ulong_t)&adxl31x_chip_info[ADXL312] },
|
||||
{ .name = "adxl314", .driver_data = (kernel_ulong_t)&adxl31x_chip_info[ADXL312] },
|
||||
{ .name = "adxl313", .driver_data = (kernel_ulong_t)&adxl31x_chip_info[ADXL313] },
|
||||
{ .name = "adxl314", .driver_data = (kernel_ulong_t)&adxl31x_chip_info[ADXL314] },
|
||||
{ }
|
||||
};
|
||||
|
||||
@ -65,9 +65,7 @@ static int adxl313_i2c_probe(struct i2c_client *client)
|
||||
* Retrieves device specific data as a pointer to a
|
||||
* adxl313_chip_info structure
|
||||
*/
|
||||
chip_data = device_get_match_data(&client->dev);
|
||||
if (!chip_data)
|
||||
chip_data = (const struct adxl313_chip_info *)i2c_match_id(adxl313_i2c_id, client)->driver_data;
|
||||
chip_data = i2c_get_match_data(client);
|
||||
|
||||
regmap = devm_regmap_init_i2c(client,
|
||||
&adxl31x_i2c_regmap_config[chip_data->type]);
|
||||
|
@ -24,19 +24,10 @@ static int adxl355_i2c_probe(struct i2c_client *client)
|
||||
{
|
||||
struct regmap *regmap;
|
||||
const struct adxl355_chip_info *chip_data;
|
||||
const struct i2c_device_id *adxl355;
|
||||
|
||||
chip_data = device_get_match_data(&client->dev);
|
||||
if (!chip_data) {
|
||||
adxl355 = to_i2c_driver(client->dev.driver)->id_table;
|
||||
if (!adxl355)
|
||||
return -EINVAL;
|
||||
|
||||
chip_data = (void *)i2c_match_id(adxl355, client)->driver_data;
|
||||
|
||||
if (!chip_data)
|
||||
return -EINVAL;
|
||||
}
|
||||
chip_data = i2c_get_match_data(client);
|
||||
if (!chip_data)
|
||||
return -ENODEV;
|
||||
|
||||
regmap = devm_regmap_init_i2c(client, &adxl355_i2c_regmap_config);
|
||||
if (IS_ERR(regmap)) {
|
||||
|
@ -8,7 +8,6 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#include "adxl372.h"
|
||||
|
@ -16,7 +16,6 @@
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* IIO driver for the MiraMEMS DA280 3-axis accelerometer and
|
||||
* IIO driver for the MiraMEMS DA217 and DA280 3-axis accelerometer and
|
||||
* IIO driver for the MiraMEMS DA226 2-axis accelerometer
|
||||
*
|
||||
* Copyright (c) 2016 Hans de Goede <hdegoede@redhat.com>
|
||||
@ -23,7 +23,7 @@
|
||||
#define DA280_MODE_ENABLE 0x1e
|
||||
#define DA280_MODE_DISABLE 0x9e
|
||||
|
||||
enum da280_chipset { da226, da280 };
|
||||
enum da280_chipset { da217, da226, da280 };
|
||||
|
||||
/*
|
||||
* a value of + or -4096 corresponds to + or - 1G
|
||||
@ -134,7 +134,10 @@ static int da280_probe(struct i2c_client *client)
|
||||
chip = id->driver_data;
|
||||
}
|
||||
|
||||
if (chip == da226) {
|
||||
if (chip == da217) {
|
||||
indio_dev->name = "da217";
|
||||
indio_dev->num_channels = 3;
|
||||
} else if (chip == da226) {
|
||||
indio_dev->name = "da226";
|
||||
indio_dev->num_channels = 2;
|
||||
} else {
|
||||
@ -166,12 +169,14 @@ static int da280_resume(struct device *dev)
|
||||
static DEFINE_SIMPLE_DEV_PM_OPS(da280_pm_ops, da280_suspend, da280_resume);
|
||||
|
||||
static const struct acpi_device_id da280_acpi_match[] = {
|
||||
{"NSA2513", da217},
|
||||
{"MIRAACC", da280},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, da280_acpi_match);
|
||||
|
||||
static const struct i2c_device_id da280_i2c_id[] = {
|
||||
{ "da217", da217 },
|
||||
{ "da226", da226 },
|
||||
{ "da280", da280 },
|
||||
{}
|
||||
|
@ -2,7 +2,6 @@
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
|
@ -28,7 +28,7 @@
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/iio/events.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
@ -1116,7 +1116,7 @@ config STMPE_ADC
|
||||
built-in ADC block (stmpe811).
|
||||
|
||||
config SUN4I_GPADC
|
||||
tristate "Support for the Allwinner SoCs GPADC"
|
||||
tristate "Allwinner A10/A13/A31 and similar GPADCs driver"
|
||||
depends on IIO
|
||||
depends on MFD_SUN4I_GPADC || MACH_SUN8I
|
||||
depends on THERMAL || !THERMAL_OF
|
||||
@ -1134,6 +1134,16 @@ config SUN4I_GPADC
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called sun4i-gpadc-iio.
|
||||
|
||||
config SUN20I_GPADC
|
||||
tristate "Allwinner D1/T113s/T507/R329 and similar GPADCs driver"
|
||||
depends on ARCH_SUNXI || COMPILE_TEST
|
||||
help
|
||||
Say yes here to build support for Allwinner (D1, T113, T507 and R329)
|
||||
SoCs GPADC. This ADC provides up to 16 channels.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called sun20i-gpadc-iio.
|
||||
|
||||
config TI_ADC081C
|
||||
tristate "Texas Instruments ADC081C/ADC101C/ADC121C family"
|
||||
depends on I2C
|
||||
|
@ -96,6 +96,7 @@ obj-$(CONFIG_RZG2L_ADC) += rzg2l_adc.o
|
||||
obj-$(CONFIG_SC27XX_ADC) += sc27xx_adc.o
|
||||
obj-$(CONFIG_SPEAR_ADC) += spear_adc.o
|
||||
obj-$(CONFIG_SUN4I_GPADC) += sun4i-gpadc-iio.o
|
||||
obj-$(CONFIG_SUN20I_GPADC) += sun20i-gpadc-iio.o
|
||||
obj-$(CONFIG_STM32_ADC_CORE) += stm32-adc-core.o
|
||||
obj-$(CONFIG_STM32_ADC) += stm32-adc.o
|
||||
obj-$(CONFIG_STM32_DFSDM_CORE) += stm32-dfsdm-core.o
|
||||
|
@ -1099,14 +1099,12 @@ static int ab8500_gpadc_probe(struct platform_device *pdev)
|
||||
|
||||
gpadc->irq_sw = platform_get_irq_byname(pdev, "SW_CONV_END");
|
||||
if (gpadc->irq_sw < 0)
|
||||
return dev_err_probe(dev, gpadc->irq_sw,
|
||||
"failed to get platform sw_conv_end irq\n");
|
||||
return gpadc->irq_sw;
|
||||
|
||||
if (is_ab8500(gpadc->ab8500)) {
|
||||
gpadc->irq_hw = platform_get_irq_byname(pdev, "HW_CONV_END");
|
||||
if (gpadc->irq_hw < 0)
|
||||
return dev_err_probe(dev, gpadc->irq_hw,
|
||||
"failed to get platform hw_conv_end irq\n");
|
||||
return gpadc->irq_hw;
|
||||
} else {
|
||||
gpadc->irq_hw = 0;
|
||||
}
|
||||
|
@ -14,7 +14,7 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/kfifo.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
#include <linux/err.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
@ -402,8 +402,8 @@ static int ad7192_setup(struct iio_dev *indio_dev, struct device_node *np)
|
||||
id &= AD7192_ID_MASK;
|
||||
|
||||
if (id != st->chip_info->chip_id)
|
||||
dev_warn(&st->sd.spi->dev, "device ID query failed (0x%X)\n",
|
||||
id);
|
||||
dev_warn(&st->sd.spi->dev, "device ID query failed (0x%X != 0x%X)\n",
|
||||
id, st->chip_info->chip_id);
|
||||
|
||||
st->mode = AD7192_MODE_SEL(AD7192_MODE_IDLE) |
|
||||
AD7192_MODE_CLKSRC(st->clock_sel) |
|
||||
@ -561,9 +561,8 @@ static ssize_t ad7192_show_filter_avail(struct device *dev,
|
||||
ad7192_get_available_filter_freq(st, freq_avail);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(freq_avail); i++)
|
||||
len += scnprintf(buf + len, PAGE_SIZE - len,
|
||||
"%d.%d ", freq_avail[i] / 1000,
|
||||
freq_avail[i] % 1000);
|
||||
len += sysfs_emit_at(buf, len, "%d.%03d ", freq_avail[i] / 1000,
|
||||
freq_avail[i] % 1000);
|
||||
|
||||
buf[len - 1] = '\n';
|
||||
|
||||
@ -972,11 +971,6 @@ static void ad7192_reg_disable(void *reg)
|
||||
regulator_disable(reg);
|
||||
}
|
||||
|
||||
static void ad7192_clk_disable(void *clk)
|
||||
{
|
||||
clk_disable_unprepare(clk);
|
||||
}
|
||||
|
||||
static int ad7192_probe(struct spi_device *spi)
|
||||
{
|
||||
struct ad7192_state *st;
|
||||
@ -1036,7 +1030,9 @@ static int ad7192_probe(struct spi_device *spi)
|
||||
else
|
||||
indio_dev->info = &ad7192_info;
|
||||
|
||||
ad_sd_init(&st->sd, indio_dev, spi, &ad7192_sigma_delta_info);
|
||||
ret = ad_sd_init(&st->sd, indio_dev, spi, &ad7192_sigma_delta_info);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = devm_ad_sd_setup_buffer_and_trigger(&spi->dev, indio_dev);
|
||||
if (ret)
|
||||
@ -1044,7 +1040,7 @@ static int ad7192_probe(struct spi_device *spi)
|
||||
|
||||
st->fclk = AD7192_INT_FREQ_MHZ;
|
||||
|
||||
st->mclk = devm_clk_get_optional(&spi->dev, "mclk");
|
||||
st->mclk = devm_clk_get_optional_enabled(&spi->dev, "mclk");
|
||||
if (IS_ERR(st->mclk))
|
||||
return PTR_ERR(st->mclk);
|
||||
|
||||
@ -1052,15 +1048,6 @@ static int ad7192_probe(struct spi_device *spi)
|
||||
|
||||
if (st->clock_sel == AD7192_CLK_EXT_MCLK1_2 ||
|
||||
st->clock_sel == AD7192_CLK_EXT_MCLK2) {
|
||||
ret = clk_prepare_enable(st->mclk);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = devm_add_action_or_reset(&spi->dev, ad7192_clk_disable,
|
||||
st->mclk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
st->fclk = clk_get_rate(st->mclk);
|
||||
if (!ad7192_valid_external_frequency(st->fclk)) {
|
||||
dev_err(&spi->dev,
|
||||
|
@ -13,7 +13,7 @@
|
||||
#include <linux/err.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
|
@ -11,8 +11,9 @@
|
||||
#include <linux/io.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
|
@ -16,7 +16,6 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
|
@ -540,8 +540,8 @@ static int iproc_adc_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
adc_priv->irqno = platform_get_irq(pdev, 0);
|
||||
if (adc_priv->irqno <= 0)
|
||||
return -ENODEV;
|
||||
if (adc_priv->irqno < 0)
|
||||
return adc_priv->irqno;
|
||||
|
||||
ret = regmap_update_bits(adc_priv->regmap, IPROC_REGCTL2,
|
||||
IPROC_ADC_AUXIN_SCAN_ENA, 0);
|
||||
|
@ -9,7 +9,6 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
|
@ -496,7 +496,7 @@ static int imx7d_adc_probe(struct platform_device *pdev)
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0)
|
||||
return dev_err_probe(dev, irq, "Failed getting irq\n");
|
||||
return irq;
|
||||
|
||||
info->clk = devm_clk_get(dev, "adc");
|
||||
if (IS_ERR(info->clk))
|
||||
|
@ -28,7 +28,7 @@
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/sched/task.h>
|
||||
#include <linux/util_macros.h>
|
||||
|
@ -176,8 +176,8 @@ static int lpc32xx_adc_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq <= 0)
|
||||
return -ENXIO;
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
retval = devm_request_irq(&pdev->dev, irq, lpc32xx_adc_isr, 0,
|
||||
LPC32XXAD_NAME, st);
|
||||
|
@ -161,7 +161,6 @@ MODULE_DEVICE_TABLE(mcb, men_z188_ids);
|
||||
static struct mcb_driver men_z188_driver = {
|
||||
.driver = {
|
||||
.name = "z188-adc",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = men_z188_probe,
|
||||
.remove = men_z188_remove,
|
||||
|
@ -17,7 +17,6 @@
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
@ -156,6 +155,10 @@
|
||||
*/
|
||||
#define MESON_SAR_ADC_REG11 0x2c
|
||||
#define MESON_SAR_ADC_REG11_BANDGAP_EN BIT(13)
|
||||
#define MESON_SAR_ADC_REG11_CMV_SEL BIT(6)
|
||||
#define MESON_SAR_ADC_REG11_VREF_VOLTAGE BIT(5)
|
||||
#define MESON_SAR_ADC_REG11_EOC BIT(1)
|
||||
#define MESON_SAR_ADC_REG11_VREF_SEL BIT(0)
|
||||
|
||||
#define MESON_SAR_ADC_REG13 0x34
|
||||
#define MESON_SAR_ADC_REG13_12BIT_CALIBRATION_MASK GENMASK(13, 8)
|
||||
@ -163,6 +166,7 @@
|
||||
#define MESON_SAR_ADC_MAX_FIFO_SIZE 32
|
||||
#define MESON_SAR_ADC_TIMEOUT 100 /* ms */
|
||||
#define MESON_SAR_ADC_VOLTAGE_AND_TEMP_CHANNEL 6
|
||||
#define MESON_SAR_ADC_VOLTAGE_AND_MUX_CHANNEL 7
|
||||
#define MESON_SAR_ADC_TEMP_OFFSET 27
|
||||
|
||||
/* temperature sensor calibration information in eFuse */
|
||||
@ -202,29 +206,22 @@
|
||||
.datasheet_name = "TEMP_SENSOR", \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec meson_sar_adc_iio_channels[] = {
|
||||
MESON_SAR_ADC_CHAN(0),
|
||||
MESON_SAR_ADC_CHAN(1),
|
||||
MESON_SAR_ADC_CHAN(2),
|
||||
MESON_SAR_ADC_CHAN(3),
|
||||
MESON_SAR_ADC_CHAN(4),
|
||||
MESON_SAR_ADC_CHAN(5),
|
||||
MESON_SAR_ADC_CHAN(6),
|
||||
MESON_SAR_ADC_CHAN(7),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(8),
|
||||
};
|
||||
#define MESON_SAR_ADC_MUX(_chan, _sel) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.channel = _chan, \
|
||||
.indexed = 1, \
|
||||
.address = MESON_SAR_ADC_VOLTAGE_AND_MUX_CHANNEL, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_AVERAGE_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_CALIBBIAS) | \
|
||||
BIT(IIO_CHAN_INFO_CALIBSCALE), \
|
||||
.datasheet_name = "SAR_ADC_MUX_"#_sel, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec meson_sar_adc_and_temp_iio_channels[] = {
|
||||
MESON_SAR_ADC_CHAN(0),
|
||||
MESON_SAR_ADC_CHAN(1),
|
||||
MESON_SAR_ADC_CHAN(2),
|
||||
MESON_SAR_ADC_CHAN(3),
|
||||
MESON_SAR_ADC_CHAN(4),
|
||||
MESON_SAR_ADC_CHAN(5),
|
||||
MESON_SAR_ADC_CHAN(6),
|
||||
MESON_SAR_ADC_CHAN(7),
|
||||
MESON_SAR_ADC_TEMP_CHAN(8),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(9),
|
||||
enum meson_sar_adc_vref_sel {
|
||||
VREF_CALIBATION_VOLTAGE = 0,
|
||||
VREF_VDDA = 1,
|
||||
};
|
||||
|
||||
enum meson_sar_adc_avg_mode {
|
||||
@ -249,6 +246,72 @@ enum meson_sar_adc_chan7_mux_sel {
|
||||
CHAN7_MUX_CH7_INPUT = 0x7,
|
||||
};
|
||||
|
||||
enum meson_sar_adc_channel_index {
|
||||
NUM_CHAN_0,
|
||||
NUM_CHAN_1,
|
||||
NUM_CHAN_2,
|
||||
NUM_CHAN_3,
|
||||
NUM_CHAN_4,
|
||||
NUM_CHAN_5,
|
||||
NUM_CHAN_6,
|
||||
NUM_CHAN_7,
|
||||
NUM_CHAN_TEMP,
|
||||
NUM_MUX_0_VSS,
|
||||
NUM_MUX_1_VDD_DIV4,
|
||||
NUM_MUX_2_VDD_DIV2,
|
||||
NUM_MUX_3_VDD_MUL3_DIV4,
|
||||
NUM_MUX_4_VDD,
|
||||
};
|
||||
|
||||
static enum meson_sar_adc_chan7_mux_sel chan7_mux_values[] = {
|
||||
CHAN7_MUX_VSS,
|
||||
CHAN7_MUX_VDD_DIV4,
|
||||
CHAN7_MUX_VDD_DIV2,
|
||||
CHAN7_MUX_VDD_MUL3_DIV4,
|
||||
CHAN7_MUX_VDD,
|
||||
};
|
||||
|
||||
static const char * const chan7_mux_names[] = {
|
||||
[CHAN7_MUX_VSS] = "gnd",
|
||||
[CHAN7_MUX_VDD_DIV4] = "0.25vdd",
|
||||
[CHAN7_MUX_VDD_DIV2] = "0.5vdd",
|
||||
[CHAN7_MUX_VDD_MUL3_DIV4] = "0.75vdd",
|
||||
[CHAN7_MUX_VDD] = "vdd",
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec meson_sar_adc_iio_channels[] = {
|
||||
MESON_SAR_ADC_CHAN(NUM_CHAN_0),
|
||||
MESON_SAR_ADC_CHAN(NUM_CHAN_1),
|
||||
MESON_SAR_ADC_CHAN(NUM_CHAN_2),
|
||||
MESON_SAR_ADC_CHAN(NUM_CHAN_3),
|
||||
MESON_SAR_ADC_CHAN(NUM_CHAN_4),
|
||||
MESON_SAR_ADC_CHAN(NUM_CHAN_5),
|
||||
MESON_SAR_ADC_CHAN(NUM_CHAN_6),
|
||||
MESON_SAR_ADC_CHAN(NUM_CHAN_7),
|
||||
MESON_SAR_ADC_MUX(NUM_MUX_0_VSS, 0),
|
||||
MESON_SAR_ADC_MUX(NUM_MUX_1_VDD_DIV4, 1),
|
||||
MESON_SAR_ADC_MUX(NUM_MUX_2_VDD_DIV2, 2),
|
||||
MESON_SAR_ADC_MUX(NUM_MUX_3_VDD_MUL3_DIV4, 3),
|
||||
MESON_SAR_ADC_MUX(NUM_MUX_4_VDD, 4),
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec meson_sar_adc_and_temp_iio_channels[] = {
|
||||
MESON_SAR_ADC_CHAN(NUM_CHAN_0),
|
||||
MESON_SAR_ADC_CHAN(NUM_CHAN_1),
|
||||
MESON_SAR_ADC_CHAN(NUM_CHAN_2),
|
||||
MESON_SAR_ADC_CHAN(NUM_CHAN_3),
|
||||
MESON_SAR_ADC_CHAN(NUM_CHAN_4),
|
||||
MESON_SAR_ADC_CHAN(NUM_CHAN_5),
|
||||
MESON_SAR_ADC_CHAN(NUM_CHAN_6),
|
||||
MESON_SAR_ADC_CHAN(NUM_CHAN_7),
|
||||
MESON_SAR_ADC_TEMP_CHAN(NUM_CHAN_TEMP),
|
||||
MESON_SAR_ADC_MUX(NUM_MUX_0_VSS, 0),
|
||||
MESON_SAR_ADC_MUX(NUM_MUX_1_VDD_DIV4, 1),
|
||||
MESON_SAR_ADC_MUX(NUM_MUX_2_VDD_DIV2, 2),
|
||||
MESON_SAR_ADC_MUX(NUM_MUX_3_VDD_MUL3_DIV4, 3),
|
||||
MESON_SAR_ADC_MUX(NUM_MUX_4_VDD, 4),
|
||||
};
|
||||
|
||||
struct meson_sar_adc_param {
|
||||
bool has_bl30_integration;
|
||||
unsigned long clock_rate;
|
||||
@ -258,6 +321,13 @@ struct meson_sar_adc_param {
|
||||
u8 temperature_trimming_bits;
|
||||
unsigned int temperature_multiplier;
|
||||
unsigned int temperature_divider;
|
||||
u8 disable_ring_counter;
|
||||
bool has_reg11;
|
||||
bool has_vref_select;
|
||||
u8 vref_select;
|
||||
u8 cmv_select;
|
||||
u8 adc_eoc;
|
||||
enum meson_sar_adc_vref_sel vref_volatge;
|
||||
};
|
||||
|
||||
struct meson_sar_adc_data {
|
||||
@ -285,6 +355,7 @@ struct meson_sar_adc_priv {
|
||||
bool temperature_sensor_calibrated;
|
||||
u8 temperature_sensor_coefficient;
|
||||
u16 temperature_sensor_adc_val;
|
||||
enum meson_sar_adc_chan7_mux_sel chan7_mux_sel;
|
||||
};
|
||||
|
||||
static const struct regmap_config meson_sar_adc_regmap_config_gxbb = {
|
||||
@ -301,6 +372,17 @@ static const struct regmap_config meson_sar_adc_regmap_config_meson8 = {
|
||||
.max_register = MESON_SAR_ADC_DELTA_10,
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec *
|
||||
find_channel_by_num(struct iio_dev *indio_dev, int num)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < indio_dev->num_channels; i++)
|
||||
if (indio_dev->channels[i].channel == num)
|
||||
return &indio_dev->channels[i];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static unsigned int meson_sar_adc_get_fifo_count(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
|
||||
@ -338,6 +420,21 @@ static int meson_sar_adc_wait_busy_clear(struct iio_dev *indio_dev)
|
||||
1, 10000);
|
||||
}
|
||||
|
||||
static void meson_sar_adc_set_chan7_mux(struct iio_dev *indio_dev,
|
||||
enum meson_sar_adc_chan7_mux_sel sel)
|
||||
{
|
||||
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
|
||||
u32 regval;
|
||||
|
||||
regval = FIELD_PREP(MESON_SAR_ADC_REG3_CTRL_CHAN7_MUX_SEL_MASK, sel);
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG3,
|
||||
MESON_SAR_ADC_REG3_CTRL_CHAN7_MUX_SEL_MASK, regval);
|
||||
|
||||
usleep_range(10, 20);
|
||||
|
||||
priv->chan7_mux_sel = sel;
|
||||
}
|
||||
|
||||
static int meson_sar_adc_read_raw_sample(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
int *val)
|
||||
@ -431,22 +528,18 @@ static void meson_sar_adc_enable_channel(struct iio_dev *indio_dev,
|
||||
regmap_update_bits(priv->regmap,
|
||||
MESON_SAR_ADC_DELTA_10,
|
||||
MESON_SAR_ADC_DELTA_10_TEMP_SEL, regval);
|
||||
} else if (chan->address == MESON_SAR_ADC_VOLTAGE_AND_MUX_CHANNEL) {
|
||||
enum meson_sar_adc_chan7_mux_sel sel;
|
||||
|
||||
if (chan->channel == NUM_CHAN_7)
|
||||
sel = CHAN7_MUX_CH7_INPUT;
|
||||
else
|
||||
sel = chan7_mux_values[chan->channel - NUM_MUX_0_VSS];
|
||||
if (sel != priv->chan7_mux_sel)
|
||||
meson_sar_adc_set_chan7_mux(indio_dev, sel);
|
||||
}
|
||||
}
|
||||
|
||||
static void meson_sar_adc_set_chan7_mux(struct iio_dev *indio_dev,
|
||||
enum meson_sar_adc_chan7_mux_sel sel)
|
||||
{
|
||||
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
|
||||
u32 regval;
|
||||
|
||||
regval = FIELD_PREP(MESON_SAR_ADC_REG3_CTRL_CHAN7_MUX_SEL_MASK, sel);
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG3,
|
||||
MESON_SAR_ADC_REG3_CTRL_CHAN7_MUX_SEL_MASK, regval);
|
||||
|
||||
usleep_range(10, 20);
|
||||
}
|
||||
|
||||
static void meson_sar_adc_start_sample_engine(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
|
||||
@ -821,6 +914,22 @@ static int meson_sar_adc_init(struct iio_dev *indio_dev)
|
||||
MESON_SAR_ADC_CHAN_10_SW_CHAN1_MUX_SEL_MASK,
|
||||
regval);
|
||||
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_CHAN_10_SW,
|
||||
MESON_SAR_ADC_CHAN_10_SW_CHAN0_XP_DRIVE_SW,
|
||||
MESON_SAR_ADC_CHAN_10_SW_CHAN0_XP_DRIVE_SW);
|
||||
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_CHAN_10_SW,
|
||||
MESON_SAR_ADC_CHAN_10_SW_CHAN0_YP_DRIVE_SW,
|
||||
MESON_SAR_ADC_CHAN_10_SW_CHAN0_YP_DRIVE_SW);
|
||||
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_CHAN_10_SW,
|
||||
MESON_SAR_ADC_CHAN_10_SW_CHAN1_XP_DRIVE_SW,
|
||||
MESON_SAR_ADC_CHAN_10_SW_CHAN1_XP_DRIVE_SW);
|
||||
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_CHAN_10_SW,
|
||||
MESON_SAR_ADC_CHAN_10_SW_CHAN1_YP_DRIVE_SW,
|
||||
MESON_SAR_ADC_CHAN_10_SW_CHAN1_YP_DRIVE_SW);
|
||||
|
||||
/*
|
||||
* set up the input channel muxes in MESON_SAR_ADC_AUX_SW
|
||||
* (2 = SAR_ADC_CH2, 3 = SAR_ADC_CH3, ...) and enable
|
||||
@ -873,6 +982,35 @@ static int meson_sar_adc_init(struct iio_dev *indio_dev)
|
||||
MESON_SAR_ADC_DELTA_10_TS_REVE0, 0);
|
||||
}
|
||||
|
||||
regval = FIELD_PREP(MESON_SAR_ADC_REG3_CTRL_CONT_RING_COUNTER_EN,
|
||||
priv->param->disable_ring_counter);
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG3,
|
||||
MESON_SAR_ADC_REG3_CTRL_CONT_RING_COUNTER_EN,
|
||||
regval);
|
||||
|
||||
if (priv->param->has_reg11) {
|
||||
regval = FIELD_PREP(MESON_SAR_ADC_REG11_EOC, priv->param->adc_eoc);
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG11,
|
||||
MESON_SAR_ADC_REG11_EOC, regval);
|
||||
|
||||
if (priv->param->has_vref_select) {
|
||||
regval = FIELD_PREP(MESON_SAR_ADC_REG11_VREF_SEL,
|
||||
priv->param->vref_select);
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG11,
|
||||
MESON_SAR_ADC_REG11_VREF_SEL, regval);
|
||||
}
|
||||
|
||||
regval = FIELD_PREP(MESON_SAR_ADC_REG11_VREF_VOLTAGE,
|
||||
priv->param->vref_volatge);
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG11,
|
||||
MESON_SAR_ADC_REG11_VREF_VOLTAGE, regval);
|
||||
|
||||
regval = FIELD_PREP(MESON_SAR_ADC_REG11_CMV_SEL,
|
||||
priv->param->cmv_select);
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG11,
|
||||
MESON_SAR_ADC_REG11_CMV_SEL, regval);
|
||||
}
|
||||
|
||||
ret = clk_set_parent(priv->adc_sel_clk, priv->clkin);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "failed to set adc parent to clkin\n");
|
||||
@ -1016,7 +1154,8 @@ static int meson_sar_adc_calib(struct iio_dev *indio_dev)
|
||||
meson_sar_adc_set_chan7_mux(indio_dev, CHAN7_MUX_VDD_DIV4);
|
||||
usleep_range(10, 20);
|
||||
ret = meson_sar_adc_get_sample(indio_dev,
|
||||
&indio_dev->channels[7],
|
||||
find_channel_by_num(indio_dev,
|
||||
NUM_MUX_1_VDD_DIV4),
|
||||
MEAN_AVERAGING, EIGHT_SAMPLES, &value0);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
@ -1024,7 +1163,8 @@ static int meson_sar_adc_calib(struct iio_dev *indio_dev)
|
||||
meson_sar_adc_set_chan7_mux(indio_dev, CHAN7_MUX_VDD_MUL3_DIV4);
|
||||
usleep_range(10, 20);
|
||||
ret = meson_sar_adc_get_sample(indio_dev,
|
||||
&indio_dev->channels[7],
|
||||
find_channel_by_num(indio_dev,
|
||||
NUM_MUX_3_VDD_MUL3_DIV4),
|
||||
MEAN_AVERAGING, EIGHT_SAMPLES, &value1);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
@ -1045,8 +1185,23 @@ static int meson_sar_adc_calib(struct iio_dev *indio_dev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int read_label(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
char *label)
|
||||
{
|
||||
if (chan->type == IIO_TEMP)
|
||||
return sprintf(label, "temp-sensor\n");
|
||||
if (chan->type == IIO_VOLTAGE && chan->channel >= NUM_MUX_0_VSS)
|
||||
return sprintf(label, "%s\n",
|
||||
chan7_mux_names[chan->channel - NUM_MUX_0_VSS]);
|
||||
if (chan->type == IIO_VOLTAGE)
|
||||
return sprintf(label, "channel-%d\n", chan->channel);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct iio_info meson_sar_adc_iio_info = {
|
||||
.read_raw = meson_sar_adc_iio_info_read_raw,
|
||||
.read_label = read_label,
|
||||
};
|
||||
|
||||
static const struct meson_sar_adc_param meson_sar_adc_meson8_param = {
|
||||
@ -1077,6 +1232,9 @@ static const struct meson_sar_adc_param meson_sar_adc_gxbb_param = {
|
||||
.bandgap_reg = MESON_SAR_ADC_REG11,
|
||||
.regmap_config = &meson_sar_adc_regmap_config_gxbb,
|
||||
.resolution = 10,
|
||||
.has_reg11 = true,
|
||||
.vref_volatge = 1,
|
||||
.cmv_select = 1,
|
||||
};
|
||||
|
||||
static const struct meson_sar_adc_param meson_sar_adc_gxl_param = {
|
||||
@ -1085,6 +1243,10 @@ static const struct meson_sar_adc_param meson_sar_adc_gxl_param = {
|
||||
.bandgap_reg = MESON_SAR_ADC_REG11,
|
||||
.regmap_config = &meson_sar_adc_regmap_config_gxbb,
|
||||
.resolution = 12,
|
||||
.disable_ring_counter = 1,
|
||||
.has_reg11 = true,
|
||||
.vref_volatge = 1,
|
||||
.cmv_select = 1,
|
||||
};
|
||||
|
||||
static const struct meson_sar_adc_param meson_sar_adc_g12a_param = {
|
||||
@ -1093,6 +1255,11 @@ static const struct meson_sar_adc_param meson_sar_adc_g12a_param = {
|
||||
.bandgap_reg = MESON_SAR_ADC_REG11,
|
||||
.regmap_config = &meson_sar_adc_regmap_config_gxbb,
|
||||
.resolution = 12,
|
||||
.disable_ring_counter = 1,
|
||||
.has_reg11 = true,
|
||||
.adc_eoc = 1,
|
||||
.has_vref_select = true,
|
||||
.vref_select = VREF_VDDA,
|
||||
};
|
||||
|
||||
static const struct meson_sar_adc_data meson_sar_adc_meson8_data = {
|
||||
|
@ -244,8 +244,8 @@ static int npcm_adc_probe(struct platform_device *pdev)
|
||||
info->adc_sample_hz = clk_get_rate(info->adc_clk) / ((div + 1) * 2);
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq <= 0) {
|
||||
ret = -EINVAL;
|
||||
if (irq < 0) {
|
||||
ret = irq;
|
||||
goto err_disable_clk;
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,6 @@
|
||||
#include <linux/mfd/palmas.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/iio/events.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/machine.h>
|
||||
@ -916,8 +915,7 @@ static int palmas_gpadc_probe(struct platform_device *pdev)
|
||||
|
||||
adc->irq_auto_0 = platform_get_irq(pdev, 1);
|
||||
if (adc->irq_auto_0 < 0)
|
||||
return dev_err_probe(adc->dev, adc->irq_auto_0,
|
||||
"get auto0 irq failed\n");
|
||||
return adc->irq_auto_0;
|
||||
|
||||
ret = devm_request_threaded_irq(&pdev->dev, adc->irq_auto_0, NULL,
|
||||
palmas_gpadc_irq_auto, IRQF_ONESHOT,
|
||||
@ -929,8 +927,7 @@ static int palmas_gpadc_probe(struct platform_device *pdev)
|
||||
|
||||
adc->irq_auto_1 = platform_get_irq(pdev, 2);
|
||||
if (adc->irq_auto_1 < 0)
|
||||
return dev_err_probe(adc->dev, adc->irq_auto_1,
|
||||
"get auto1 irq failed\n");
|
||||
return adc->irq_auto_1;
|
||||
|
||||
ret = devm_request_threaded_irq(&pdev->dev, adc->irq_auto_1, NULL,
|
||||
palmas_gpadc_irq_auto, IRQF_ONESHOT,
|
||||
|
@ -555,6 +555,8 @@ static const struct adc5_channels adc5_chans_pmic[ADC5_MAX_CHANNEL] = {
|
||||
SCALE_HW_CALIB_PM5_SMB_TEMP)
|
||||
[ADC5_GPIO1_100K_PU] = ADC5_CHAN_TEMP("gpio1_100k_pu", 0,
|
||||
SCALE_HW_CALIB_THERM_100K_PULLUP)
|
||||
[ADC5_GPIO2_100K_PU] = ADC5_CHAN_TEMP("gpio2_100k_pu", 0,
|
||||
SCALE_HW_CALIB_THERM_100K_PULLUP)
|
||||
[ADC5_GPIO3_100K_PU] = ADC5_CHAN_TEMP("gpio3_100k_pu", 0,
|
||||
SCALE_HW_CALIB_THERM_100K_PULLUP)
|
||||
[ADC5_GPIO4_100K_PU] = ADC5_CHAN_TEMP("gpio4_100k_pu", 0,
|
||||
|
@ -13,7 +13,6 @@
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
|
@ -11,7 +11,6 @@
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/delay.h>
|
||||
@ -467,7 +466,7 @@ static int rockchip_saradc_probe(struct platform_device *pdev)
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0)
|
||||
return dev_err_probe(&pdev->dev, irq, "failed to get irq\n");
|
||||
return irq;
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, irq, rockchip_saradc_isr,
|
||||
0, dev_name(&pdev->dev), info);
|
||||
|
@ -7,7 +7,6 @@
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/nvmem-consumer.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
@ -310,8 +310,8 @@ static int spear_adc_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq <= 0) {
|
||||
ret = -EINVAL;
|
||||
if (irq < 0) {
|
||||
ret = irq;
|
||||
goto errout2;
|
||||
}
|
||||
|
||||
|
@ -18,6 +18,8 @@
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
@ -723,8 +725,7 @@ static int stm32_adc_probe(struct platform_device *pdev)
|
||||
priv->nb_adc_max = priv->cfg->num_adcs;
|
||||
spin_lock_init(&priv->common.lock);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
priv->common.base = devm_ioremap_resource(&pdev->dev, res);
|
||||
priv->common.base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
|
||||
if (IS_ERR(priv->common.base))
|
||||
return PTR_ERR(priv->common.base);
|
||||
priv->common.phys_base = res->start;
|
||||
|
@ -19,7 +19,8 @@
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
|
@ -12,8 +12,10 @@
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
|
@ -14,7 +14,7 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mfd/stmpe.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/device.h>
|
||||
|
||||
|
276
drivers/iio/adc/sun20i-gpadc-iio.c
Normal file
276
drivers/iio/adc/sun20i-gpadc-iio.c
Normal file
@ -0,0 +1,276 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* GPADC driver for sunxi platforms (D1, T113-S3 and R329)
|
||||
* Copyright (c) 2023 Maksim Kiselev <bigunclemax@gmail.com>
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/reset.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
|
||||
#define SUN20I_GPADC_DRIVER_NAME "sun20i-gpadc"
|
||||
|
||||
/* Register map definition */
|
||||
#define SUN20I_GPADC_SR 0x00
|
||||
#define SUN20I_GPADC_CTRL 0x04
|
||||
#define SUN20I_GPADC_CS_EN 0x08
|
||||
#define SUN20I_GPADC_FIFO_INTC 0x0c
|
||||
#define SUN20I_GPADC_FIFO_INTS 0x10
|
||||
#define SUN20I_GPADC_FIFO_DATA 0X14
|
||||
#define SUN20I_GPADC_CB_DATA 0X18
|
||||
#define SUN20I_GPADC_DATAL_INTC 0x20
|
||||
#define SUN20I_GPADC_DATAH_INTC 0x24
|
||||
#define SUN20I_GPADC_DATA_INTC 0x28
|
||||
#define SUN20I_GPADC_DATAL_INTS 0x30
|
||||
#define SUN20I_GPADC_DATAH_INTS 0x34
|
||||
#define SUN20I_GPADC_DATA_INTS 0x38
|
||||
#define SUN20I_GPADC_CH_CMP_DATA(x) (0x40 + (x) * 4)
|
||||
#define SUN20I_GPADC_CH_DATA(x) (0x80 + (x) * 4)
|
||||
|
||||
#define SUN20I_GPADC_CTRL_ADC_AUTOCALI_EN_MASK BIT(23)
|
||||
#define SUN20I_GPADC_CTRL_WORK_MODE_MASK GENMASK(19, 18)
|
||||
#define SUN20I_GPADC_CTRL_ADC_EN_MASK BIT(16)
|
||||
#define SUN20I_GPADC_CS_EN_ADC_CH(x) BIT(x)
|
||||
#define SUN20I_GPADC_DATA_INTC_CH_DATA_IRQ_EN(x) BIT(x)
|
||||
|
||||
#define SUN20I_GPADC_WORK_MODE_SINGLE 0
|
||||
|
||||
struct sun20i_gpadc_iio {
|
||||
void __iomem *regs;
|
||||
struct completion completion;
|
||||
int last_channel;
|
||||
/*
|
||||
* Lock to protect the device state during a potential concurrent
|
||||
* read access from userspace. Reading a raw value requires a sequence
|
||||
* of register writes, then a wait for a completion callback,
|
||||
* and finally a register read, during which userspace could issue
|
||||
* another read request. This lock protects a read access from
|
||||
* ocurring before another one has finished.
|
||||
*/
|
||||
struct mutex lock;
|
||||
};
|
||||
|
||||
static int sun20i_gpadc_adc_read(struct sun20i_gpadc_iio *info,
|
||||
struct iio_chan_spec const *chan, int *val)
|
||||
{
|
||||
u32 ctrl;
|
||||
int ret = IIO_VAL_INT;
|
||||
|
||||
mutex_lock(&info->lock);
|
||||
|
||||
reinit_completion(&info->completion);
|
||||
|
||||
if (info->last_channel != chan->channel) {
|
||||
info->last_channel = chan->channel;
|
||||
|
||||
/* enable the analog input channel */
|
||||
writel(SUN20I_GPADC_CS_EN_ADC_CH(chan->channel),
|
||||
info->regs + SUN20I_GPADC_CS_EN);
|
||||
|
||||
/* enable the data irq for input channel */
|
||||
writel(SUN20I_GPADC_DATA_INTC_CH_DATA_IRQ_EN(chan->channel),
|
||||
info->regs + SUN20I_GPADC_DATA_INTC);
|
||||
}
|
||||
|
||||
/* enable the ADC function */
|
||||
ctrl = readl(info->regs + SUN20I_GPADC_CTRL);
|
||||
ctrl |= FIELD_PREP(SUN20I_GPADC_CTRL_ADC_EN_MASK, 1);
|
||||
writel(ctrl, info->regs + SUN20I_GPADC_CTRL);
|
||||
|
||||
/*
|
||||
* According to the datasheet maximum acquire time(TACQ) can be
|
||||
* (65535+1)/24Mhz and conversion time(CONV_TIME) is always constant
|
||||
* and equal to 14/24Mhz, so (TACQ+CONV_TIME) <= 2.73125ms.
|
||||
* A 10ms delay should be enough to make sure an interrupt occurs in
|
||||
* normal conditions. If it doesn't occur, then there is a timeout.
|
||||
*/
|
||||
if (!wait_for_completion_timeout(&info->completion, msecs_to_jiffies(10))) {
|
||||
ret = -ETIMEDOUT;
|
||||
goto err_unlock;
|
||||
}
|
||||
|
||||
/* read the ADC data */
|
||||
*val = readl(info->regs + SUN20I_GPADC_CH_DATA(chan->channel));
|
||||
|
||||
err_unlock:
|
||||
mutex_unlock(&info->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sun20i_gpadc_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val,
|
||||
int *val2, long mask)
|
||||
{
|
||||
struct sun20i_gpadc_iio *info = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
return sun20i_gpadc_adc_read(info, chan, val);
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
/* value in mv = 1800mV / 4096 raw */
|
||||
*val = 1800;
|
||||
*val2 = 12;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static irqreturn_t sun20i_gpadc_irq_handler(int irq, void *data)
|
||||
{
|
||||
struct sun20i_gpadc_iio *info = data;
|
||||
|
||||
/* clear data interrupt status register */
|
||||
writel(GENMASK(31, 0), info->regs + SUN20I_GPADC_DATA_INTS);
|
||||
|
||||
complete(&info->completion);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static const struct iio_info sun20i_gpadc_iio_info = {
|
||||
.read_raw = sun20i_gpadc_read_raw,
|
||||
};
|
||||
|
||||
static void sun20i_gpadc_reset_assert(void *data)
|
||||
{
|
||||
struct reset_control *rst = data;
|
||||
|
||||
reset_control_assert(rst);
|
||||
}
|
||||
|
||||
static int sun20i_gpadc_alloc_channels(struct iio_dev *indio_dev,
|
||||
struct device *dev)
|
||||
{
|
||||
unsigned int channel;
|
||||
int num_channels, i, ret;
|
||||
struct iio_chan_spec *channels;
|
||||
struct fwnode_handle *node;
|
||||
|
||||
num_channels = device_get_child_node_count(dev);
|
||||
if (num_channels == 0)
|
||||
return dev_err_probe(dev, -ENODEV, "no channel children\n");
|
||||
|
||||
channels = devm_kcalloc(dev, num_channels, sizeof(*channels),
|
||||
GFP_KERNEL);
|
||||
if (!channels)
|
||||
return -ENOMEM;
|
||||
|
||||
i = 0;
|
||||
device_for_each_child_node(dev, node) {
|
||||
ret = fwnode_property_read_u32(node, "reg", &channel);
|
||||
if (ret) {
|
||||
fwnode_handle_put(node);
|
||||
return dev_err_probe(dev, ret, "invalid channel number\n");
|
||||
}
|
||||
|
||||
channels[i].type = IIO_VOLTAGE;
|
||||
channels[i].indexed = 1;
|
||||
channels[i].channel = channel;
|
||||
channels[i].info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
|
||||
channels[i].info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE);
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
indio_dev->channels = channels;
|
||||
indio_dev->num_channels = num_channels;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sun20i_gpadc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct iio_dev *indio_dev;
|
||||
struct sun20i_gpadc_iio *info;
|
||||
struct reset_control *rst;
|
||||
struct clk *clk;
|
||||
int irq;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(dev, sizeof(*info));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
info = iio_priv(indio_dev);
|
||||
info->last_channel = -1;
|
||||
|
||||
mutex_init(&info->lock);
|
||||
init_completion(&info->completion);
|
||||
|
||||
ret = sun20i_gpadc_alloc_channels(indio_dev, dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
indio_dev->info = &sun20i_gpadc_iio_info;
|
||||
indio_dev->name = SUN20I_GPADC_DRIVER_NAME;
|
||||
|
||||
info->regs = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(info->regs))
|
||||
return PTR_ERR(info->regs);
|
||||
|
||||
clk = devm_clk_get_enabled(dev, NULL);
|
||||
if (IS_ERR(clk))
|
||||
return dev_err_probe(dev, PTR_ERR(clk), "failed to enable bus clock\n");
|
||||
|
||||
rst = devm_reset_control_get_exclusive(dev, NULL);
|
||||
if (IS_ERR(rst))
|
||||
return dev_err_probe(dev, PTR_ERR(rst), "failed to get reset control\n");
|
||||
|
||||
ret = reset_control_deassert(rst);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "failed to deassert reset\n");
|
||||
|
||||
ret = devm_add_action_or_reset(dev, sun20i_gpadc_reset_assert, rst);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
ret = devm_request_irq(dev, irq, sun20i_gpadc_irq_handler, 0,
|
||||
dev_name(dev), info);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "failed requesting irq %d\n", irq);
|
||||
|
||||
writel(FIELD_PREP(SUN20I_GPADC_CTRL_ADC_AUTOCALI_EN_MASK, 1) |
|
||||
FIELD_PREP(SUN20I_GPADC_CTRL_WORK_MODE_MASK, SUN20I_GPADC_WORK_MODE_SINGLE),
|
||||
info->regs + SUN20I_GPADC_CTRL);
|
||||
|
||||
ret = devm_iio_device_register(dev, indio_dev);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "could not register the device\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id sun20i_gpadc_of_id[] = {
|
||||
{ .compatible = "allwinner,sun20i-d1-gpadc" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sun20i_gpadc_of_id);
|
||||
|
||||
static struct platform_driver sun20i_gpadc_driver = {
|
||||
.driver = {
|
||||
.name = SUN20I_GPADC_DRIVER_NAME,
|
||||
.of_match_table = sun20i_gpadc_of_id,
|
||||
},
|
||||
.probe = sun20i_gpadc_probe,
|
||||
};
|
||||
module_platform_driver(sun20i_gpadc_driver);
|
||||
|
||||
MODULE_DESCRIPTION("ADC driver for sunxi platforms");
|
||||
MODULE_AUTHOR("Maksim Kiselev <bigunclemax@gmail.com>");
|
||||
MODULE_LICENSE("GPL");
|
@ -24,7 +24,6 @@
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
|
@ -16,7 +16,10 @@
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/driver.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
|
||||
#define TI_LMP92064_REG_CONFIG_A 0x0000
|
||||
#define TI_LMP92064_REG_CONFIG_B 0x0001
|
||||
@ -91,6 +94,12 @@ static const struct iio_chan_spec lmp92064_adc_channels[] = {
|
||||
.address = TI_LMP92064_CHAN_INC,
|
||||
.info_mask_separate =
|
||||
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
|
||||
.scan_index = TI_LMP92064_CHAN_INC,
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
.realbits = 12,
|
||||
.storagebits = 16,
|
||||
},
|
||||
.datasheet_name = "INC",
|
||||
},
|
||||
{
|
||||
@ -98,8 +107,20 @@ static const struct iio_chan_spec lmp92064_adc_channels[] = {
|
||||
.address = TI_LMP92064_CHAN_INV,
|
||||
.info_mask_separate =
|
||||
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
|
||||
.scan_index = TI_LMP92064_CHAN_INV,
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
.realbits = 12,
|
||||
.storagebits = 16,
|
||||
},
|
||||
.datasheet_name = "INV",
|
||||
},
|
||||
IIO_CHAN_SOFT_TIMESTAMP(2),
|
||||
};
|
||||
|
||||
static const unsigned long lmp92064_scan_masks[] = {
|
||||
BIT(TI_LMP92064_CHAN_INC) | BIT(TI_LMP92064_CHAN_INV),
|
||||
0
|
||||
};
|
||||
|
||||
static int lmp92064_read_meas(struct lmp92064_adc_priv *priv, u16 *res)
|
||||
@ -171,6 +192,32 @@ static int lmp92064_read_raw(struct iio_dev *indio_dev,
|
||||
}
|
||||
}
|
||||
|
||||
static irqreturn_t lmp92064_trigger_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct lmp92064_adc_priv *priv = iio_priv(indio_dev);
|
||||
struct {
|
||||
u16 values[2];
|
||||
int64_t timestamp __aligned(8);
|
||||
} data;
|
||||
int ret;
|
||||
|
||||
memset(&data, 0, sizeof(data));
|
||||
|
||||
ret = lmp92064_read_meas(priv, data.values);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, &data,
|
||||
iio_get_time_ns(indio_dev));
|
||||
|
||||
err:
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int lmp92064_reset(struct lmp92064_adc_priv *priv,
|
||||
struct gpio_desc *gpio_reset)
|
||||
{
|
||||
@ -301,6 +348,12 @@ static int lmp92064_adc_probe(struct spi_device *spi)
|
||||
indio_dev->channels = lmp92064_adc_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(lmp92064_adc_channels);
|
||||
indio_dev->info = &lmp92064_adc_info;
|
||||
indio_dev->available_scan_masks = lmp92064_scan_masks;
|
||||
|
||||
ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
|
||||
lmp92064_trigger_handler, NULL);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Failed to setup buffered read\n");
|
||||
|
||||
return devm_iio_device_register(dev, indio_dev);
|
||||
}
|
||||
|
@ -14,7 +14,6 @@
|
||||
#include <linux/io.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/iio/machine.h>
|
||||
#include <linux/iio/driver.h>
|
||||
#include <linux/iopoll.h>
|
||||
|
@ -18,6 +18,7 @@ config AD8366
|
||||
AD8366 Dual-Digital Variable Gain Amplifier (VGA)
|
||||
ADA4961 BiCMOS RF Digital Gain Amplifier (DGA)
|
||||
ADL5240 Digitally controlled variable gain amplifier (VGA)
|
||||
HMC792A 0.25 dB LSB GaAs MMIC 6-Bit Digital Attenuator
|
||||
HMC1119 0.25 dB LSB, 7-Bit, Silicon Digital Attenuator
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
|
@ -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)
|
||||
* HMC792A 0.25 dB LSB GaAs MMIC 6-Bit Digital Attenuator
|
||||
* HMC1119 0.25 dB LSB, 7-Bit, Silicon Digital Attenuator
|
||||
*
|
||||
* Copyright 2012-2019 Analog Devices Inc.
|
||||
@ -28,6 +29,7 @@ enum ad8366_type {
|
||||
ID_AD8366,
|
||||
ID_ADA4961,
|
||||
ID_ADL5240,
|
||||
ID_HMC792,
|
||||
ID_HMC1119,
|
||||
};
|
||||
|
||||
@ -64,6 +66,10 @@ static struct ad8366_info ad8366_infos[] = {
|
||||
.gain_min = -11500,
|
||||
.gain_max = 20000,
|
||||
},
|
||||
[ID_HMC792] = {
|
||||
.gain_min = -15750,
|
||||
.gain_max = 0,
|
||||
},
|
||||
[ID_HMC1119] = {
|
||||
.gain_min = -31750,
|
||||
.gain_max = 0,
|
||||
@ -90,6 +96,7 @@ static int ad8366_write(struct iio_dev *indio_dev,
|
||||
case ID_ADL5240:
|
||||
st->data[0] = (ch_a & 0x3F);
|
||||
break;
|
||||
case ID_HMC792:
|
||||
case ID_HMC1119:
|
||||
st->data[0] = ch_a;
|
||||
break;
|
||||
@ -127,6 +134,9 @@ static int ad8366_read_raw(struct iio_dev *indio_dev,
|
||||
case ID_ADL5240:
|
||||
gain = 20000 - 31500 + code * 500;
|
||||
break;
|
||||
case ID_HMC792:
|
||||
gain = -1 * code * 500;
|
||||
break;
|
||||
case ID_HMC1119:
|
||||
gain = -1 * code * 250;
|
||||
break;
|
||||
@ -176,6 +186,9 @@ static int ad8366_write_raw(struct iio_dev *indio_dev,
|
||||
case ID_ADL5240:
|
||||
code = ((gain - 500 - 20000) / 500) & 0x3F;
|
||||
break;
|
||||
case ID_HMC792:
|
||||
code = (abs(gain) / 500) & 0x3F;
|
||||
break;
|
||||
case ID_HMC1119:
|
||||
code = (abs(gain) / 250) & 0x7F;
|
||||
break;
|
||||
@ -261,6 +274,7 @@ static int ad8366_probe(struct spi_device *spi)
|
||||
break;
|
||||
case ID_ADA4961:
|
||||
case ID_ADL5240:
|
||||
case ID_HMC792:
|
||||
case ID_HMC1119:
|
||||
st->reset_gpio = devm_gpiod_get_optional(&spi->dev, "reset", GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(st->reset_gpio)) {
|
||||
@ -314,6 +328,7 @@ static const struct spi_device_id ad8366_id[] = {
|
||||
{"ad8366", ID_AD8366},
|
||||
{"ada4961", ID_ADA4961},
|
||||
{"adl5240", ID_ADL5240},
|
||||
{"hmc792a", ID_HMC792},
|
||||
{"hmc1119", ID_HMC1119},
|
||||
{}
|
||||
};
|
||||
|
@ -541,6 +541,7 @@ static int ad7150_probe(struct i2c_client *client)
|
||||
const struct i2c_device_id *id = i2c_client_get_device_id(client);
|
||||
struct ad7150_chip_info *chip;
|
||||
struct iio_dev *indio_dev;
|
||||
bool use_irq = true;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip));
|
||||
@ -561,14 +562,13 @@ static int ad7150_probe(struct i2c_client *client)
|
||||
|
||||
chip->interrupts[0] = fwnode_irq_get(dev_fwnode(&client->dev), 0);
|
||||
if (chip->interrupts[0] < 0)
|
||||
return chip->interrupts[0];
|
||||
if (id->driver_data == AD7150) {
|
||||
use_irq = false;
|
||||
else if (id->driver_data == AD7150) {
|
||||
chip->interrupts[1] = fwnode_irq_get(dev_fwnode(&client->dev), 1);
|
||||
if (chip->interrupts[1] < 0)
|
||||
return chip->interrupts[1];
|
||||
use_irq = false;
|
||||
}
|
||||
if (chip->interrupts[0] &&
|
||||
(id->driver_data == AD7151 || chip->interrupts[1])) {
|
||||
if (use_irq) {
|
||||
irq_set_status_flags(chip->interrupts[0], IRQ_NOAUTOEN);
|
||||
ret = devm_request_threaded_irq(&client->dev,
|
||||
chip->interrupts[0],
|
||||
|
@ -36,6 +36,8 @@
|
||||
#define SCD4X_WRITE_BUF_SIZE 5
|
||||
#define SCD4X_FRC_MIN_PPM 0
|
||||
#define SCD4X_FRC_MAX_PPM 2000
|
||||
#define SCD4X_PRESSURE_COMP_MIN_MBAR 700
|
||||
#define SCD4X_PRESSURE_COMP_MAX_MBAR 1200
|
||||
#define SCD4X_READY_MASK 0x01
|
||||
|
||||
/*Commands SCD4X*/
|
||||
@ -45,6 +47,8 @@ enum scd4x_cmd {
|
||||
CMD_STOP_MEAS = 0x3f86,
|
||||
CMD_SET_TEMP_OFFSET = 0x241d,
|
||||
CMD_GET_TEMP_OFFSET = 0x2318,
|
||||
CMD_SET_AMB_PRESSURE = 0xe000,
|
||||
CMD_GET_AMB_PRESSURE = 0xe000,
|
||||
CMD_FRC = 0x362f,
|
||||
CMD_SET_ASC = 0x2416,
|
||||
CMD_GET_ASC = 0x2313,
|
||||
@ -137,7 +141,8 @@ static int scd4x_read(struct scd4x_state *state, enum scd4x_cmd cmd,
|
||||
* Measurement needs to be stopped before sending commands.
|
||||
* Except for reading measurement and data ready command.
|
||||
*/
|
||||
if ((cmd != CMD_GET_DATA_READY) && (cmd != CMD_READ_MEAS)) {
|
||||
if ((cmd != CMD_GET_DATA_READY) && (cmd != CMD_READ_MEAS) &&
|
||||
(cmd != CMD_GET_AMB_PRESSURE)) {
|
||||
ret = scd4x_send_command(state, CMD_STOP_MEAS);
|
||||
if (ret)
|
||||
return ret;
|
||||
@ -166,7 +171,8 @@ static int scd4x_read(struct scd4x_state *state, enum scd4x_cmd cmd,
|
||||
}
|
||||
|
||||
/* start measurement */
|
||||
if ((cmd != CMD_GET_DATA_READY) && (cmd != CMD_READ_MEAS)) {
|
||||
if ((cmd != CMD_GET_DATA_READY) && (cmd != CMD_READ_MEAS) &&
|
||||
(cmd != CMD_GET_AMB_PRESSURE)) {
|
||||
ret = scd4x_send_command(state, CMD_START_MEAS);
|
||||
if (ret)
|
||||
return ret;
|
||||
@ -188,9 +194,11 @@ static int scd4x_write(struct scd4x_state *state, enum scd4x_cmd cmd, uint16_t a
|
||||
buf[4] = crc;
|
||||
|
||||
/* measurement needs to be stopped before sending commands */
|
||||
ret = scd4x_send_command(state, CMD_STOP_MEAS);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (cmd != CMD_SET_AMB_PRESSURE) {
|
||||
ret = scd4x_send_command(state, CMD_STOP_MEAS);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* execution time */
|
||||
msleep_interruptible(500);
|
||||
@ -200,7 +208,7 @@ static int scd4x_write(struct scd4x_state *state, enum scd4x_cmd cmd, uint16_t a
|
||||
return ret;
|
||||
|
||||
/* start measurement, except for forced calibration command */
|
||||
if (cmd != CMD_FRC) {
|
||||
if ((cmd != CMD_FRC) && (cmd != CMD_SET_AMB_PRESSURE)) {
|
||||
ret = scd4x_send_command(state, CMD_START_MEAS);
|
||||
if (ret)
|
||||
return ret;
|
||||
@ -338,6 +346,18 @@ static int scd4x_read_raw(struct iio_dev *indio_dev,
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (chan->output) {
|
||||
mutex_lock(&state->lock);
|
||||
ret = scd4x_read(state, CMD_GET_AMB_PRESSURE, &tmp, sizeof(tmp));
|
||||
mutex_unlock(&state->lock);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*val = be16_to_cpu(tmp);
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
ret = iio_device_claim_direct_mode(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
@ -386,6 +406,25 @@ static int scd4x_read_raw(struct iio_dev *indio_dev,
|
||||
}
|
||||
}
|
||||
|
||||
static const int scd4x_pressure_calibbias_available[] = {
|
||||
SCD4X_PRESSURE_COMP_MIN_MBAR, 1, SCD4X_PRESSURE_COMP_MAX_MBAR,
|
||||
};
|
||||
|
||||
static int scd4x_read_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_RAW:
|
||||
*vals = scd4x_pressure_calibbias_available;
|
||||
*type = IIO_VAL_INT;
|
||||
|
||||
return IIO_AVAIL_RANGE;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
||||
static int scd4x_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
@ -399,6 +438,21 @@ static int scd4x_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const
|
||||
mutex_unlock(&state->lock);
|
||||
|
||||
return ret;
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
switch (chan->type) {
|
||||
case IIO_PRESSURE:
|
||||
if (val < SCD4X_PRESSURE_COMP_MIN_MBAR ||
|
||||
val > SCD4X_PRESSURE_COMP_MAX_MBAR)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&state->lock);
|
||||
ret = scd4x_write(state, CMD_SET_AMB_PRESSURE, val);
|
||||
mutex_unlock(&state->lock);
|
||||
|
||||
return ret;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -503,9 +557,22 @@ static const struct iio_info scd4x_info = {
|
||||
.attrs = &scd4x_attr_group,
|
||||
.read_raw = scd4x_read_raw,
|
||||
.write_raw = scd4x_write_raw,
|
||||
.read_avail = scd4x_read_avail,
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec scd4x_channels[] = {
|
||||
{
|
||||
/*
|
||||
* this channel is special in a sense we are pretending that
|
||||
* sensor is able to change measurement chamber pressure but in
|
||||
* fact we're just setting pressure compensation value
|
||||
*/
|
||||
.type = IIO_PRESSURE,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.info_mask_separate_available = BIT(IIO_CHAN_INFO_RAW),
|
||||
.output = 1,
|
||||
.scan_index = -1,
|
||||
},
|
||||
{
|
||||
.type = IIO_CONCENTRATION,
|
||||
.channel2 = IIO_MOD_CO2,
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
source "drivers/iio/common/cros_ec_sensors/Kconfig"
|
||||
source "drivers/iio/common/hid-sensors/Kconfig"
|
||||
source "drivers/iio/common/inv_sensors/Kconfig"
|
||||
source "drivers/iio/common/ms_sensors/Kconfig"
|
||||
source "drivers/iio/common/scmi_sensors/Kconfig"
|
||||
source "drivers/iio/common/ssp_sensors/Kconfig"
|
||||
|
@ -10,6 +10,7 @@
|
||||
# When adding new entries keep the list in alphabetical order
|
||||
obj-y += cros_ec_sensors/
|
||||
obj-y += hid-sensors/
|
||||
obj-y += inv_sensors/
|
||||
obj-y += ms_sensors/
|
||||
obj-y += scmi_sensors/
|
||||
obj-y += ssp_sensors/
|
||||
|
7
drivers/iio/common/inv_sensors/Kconfig
Normal file
7
drivers/iio/common/inv_sensors/Kconfig
Normal file
@ -0,0 +1,7 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
#
|
||||
# TDK-InvenSense sensors common library
|
||||
#
|
||||
|
||||
config IIO_INV_SENSORS_TIMESTAMP
|
||||
tristate
|
6
drivers/iio/common/inv_sensors/Makefile
Normal file
6
drivers/iio/common/inv_sensors/Makefile
Normal file
@ -0,0 +1,6 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# Makefile for TDK-InvenSense sensors module.
|
||||
#
|
||||
|
||||
obj-$(CONFIG_IIO_INV_SENSORS_TIMESTAMP) += inv_sensors_timestamp.o
|
@ -3,25 +3,23 @@
|
||||
* Copyright (C) 2020 Invensense, Inc.
|
||||
*/
|
||||
|
||||
#include <linux/errno.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/math64.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include "inv_icm42600.h"
|
||||
#include "inv_icm42600_timestamp.h"
|
||||
#include <linux/iio/common/inv_sensors_timestamp.h>
|
||||
|
||||
/* internal chip period is 32kHz, 31250ns */
|
||||
#define INV_ICM42600_TIMESTAMP_PERIOD 31250
|
||||
/* allow a jitter of +/- 2% */
|
||||
#define INV_ICM42600_TIMESTAMP_JITTER 2
|
||||
/* compute min and max periods accepted */
|
||||
#define INV_ICM42600_TIMESTAMP_MIN_PERIOD(_p) \
|
||||
(((_p) * (100 - INV_ICM42600_TIMESTAMP_JITTER)) / 100)
|
||||
#define INV_ICM42600_TIMESTAMP_MAX_PERIOD(_p) \
|
||||
(((_p) * (100 + INV_ICM42600_TIMESTAMP_JITTER)) / 100)
|
||||
/* compute jitter, min and max following jitter in per mille */
|
||||
#define INV_SENSORS_TIMESTAMP_JITTER(_val, _jitter) \
|
||||
(div_s64((_val) * (_jitter), 1000))
|
||||
#define INV_SENSORS_TIMESTAMP_MIN(_val, _jitter) \
|
||||
(((_val) * (1000 - (_jitter))) / 1000)
|
||||
#define INV_SENSORS_TIMESTAMP_MAX(_val, _jitter) \
|
||||
(((_val) * (1000 + (_jitter))) / 1000)
|
||||
|
||||
/* Add a new value inside an accumulator and update the estimate value */
|
||||
static void inv_update_acc(struct inv_icm42600_timestamp_acc *acc, uint32_t val)
|
||||
static void inv_update_acc(struct inv_sensors_timestamp_acc *acc, uint32_t val)
|
||||
{
|
||||
uint64_t sum = 0;
|
||||
size_t i;
|
||||
@ -40,65 +38,57 @@ static void inv_update_acc(struct inv_icm42600_timestamp_acc *acc, uint32_t val)
|
||||
acc->val = div_u64(sum, i);
|
||||
}
|
||||
|
||||
void inv_icm42600_timestamp_init(struct inv_icm42600_timestamp *ts,
|
||||
uint32_t period)
|
||||
void inv_sensors_timestamp_init(struct inv_sensors_timestamp *ts,
|
||||
const struct inv_sensors_timestamp_chip *chip)
|
||||
{
|
||||
/* initial odr for sensor after reset is 1kHz */
|
||||
const uint32_t default_period = 1000000;
|
||||
memset(ts, 0, sizeof(*ts));
|
||||
|
||||
/* save chip parameters and compute min and max clock period */
|
||||
ts->chip = *chip;
|
||||
ts->min_period = INV_SENSORS_TIMESTAMP_MIN(chip->clock_period, chip->jitter);
|
||||
ts->max_period = INV_SENSORS_TIMESTAMP_MAX(chip->clock_period, chip->jitter);
|
||||
|
||||
/* current multiplier and period values after reset */
|
||||
ts->mult = default_period / INV_ICM42600_TIMESTAMP_PERIOD;
|
||||
ts->period = default_period;
|
||||
/* new set multiplier is the one from chip initialization */
|
||||
ts->new_mult = period / INV_ICM42600_TIMESTAMP_PERIOD;
|
||||
ts->mult = chip->init_period / chip->clock_period;
|
||||
ts->period = chip->init_period;
|
||||
|
||||
/* use theoretical value for chip period */
|
||||
inv_update_acc(&ts->chip_period, INV_ICM42600_TIMESTAMP_PERIOD);
|
||||
inv_update_acc(&ts->chip_period, chip->clock_period);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(inv_sensors_timestamp_init, IIO_INV_SENSORS_TIMESTAMP);
|
||||
|
||||
int inv_icm42600_timestamp_setup(struct inv_icm42600_state *st)
|
||||
{
|
||||
unsigned int val;
|
||||
|
||||
/* enable timestamp register */
|
||||
val = INV_ICM42600_TMST_CONFIG_TMST_TO_REGS_EN |
|
||||
INV_ICM42600_TMST_CONFIG_TMST_EN;
|
||||
return regmap_update_bits(st->map, INV_ICM42600_REG_TMST_CONFIG,
|
||||
INV_ICM42600_TMST_CONFIG_MASK, val);
|
||||
}
|
||||
|
||||
int inv_icm42600_timestamp_update_odr(struct inv_icm42600_timestamp *ts,
|
||||
uint32_t period, bool fifo)
|
||||
int inv_sensors_timestamp_update_odr(struct inv_sensors_timestamp *ts,
|
||||
uint32_t period, bool fifo)
|
||||
{
|
||||
/* when FIFO is on, prevent odr change if one is already pending */
|
||||
if (fifo && ts->new_mult != 0)
|
||||
return -EAGAIN;
|
||||
|
||||
ts->new_mult = period / INV_ICM42600_TIMESTAMP_PERIOD;
|
||||
ts->new_mult = period / ts->chip.clock_period;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(inv_sensors_timestamp_update_odr, IIO_INV_SENSORS_TIMESTAMP);
|
||||
|
||||
static bool inv_validate_period(uint32_t period, uint32_t mult)
|
||||
static bool inv_validate_period(struct inv_sensors_timestamp *ts, uint32_t period, uint32_t mult)
|
||||
{
|
||||
const uint32_t chip_period = INV_ICM42600_TIMESTAMP_PERIOD;
|
||||
uint32_t period_min, period_max;
|
||||
|
||||
/* check that period is acceptable */
|
||||
period_min = INV_ICM42600_TIMESTAMP_MIN_PERIOD(chip_period) * mult;
|
||||
period_max = INV_ICM42600_TIMESTAMP_MAX_PERIOD(chip_period) * mult;
|
||||
period_min = ts->min_period * mult;
|
||||
period_max = ts->max_period * mult;
|
||||
if (period > period_min && period < period_max)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool inv_update_chip_period(struct inv_icm42600_timestamp *ts,
|
||||
uint32_t mult, uint32_t period)
|
||||
static bool inv_update_chip_period(struct inv_sensors_timestamp *ts,
|
||||
uint32_t mult, uint32_t period)
|
||||
{
|
||||
uint32_t new_chip_period;
|
||||
|
||||
if (!inv_validate_period(period, mult))
|
||||
if (!inv_validate_period(ts, period, mult))
|
||||
return false;
|
||||
|
||||
/* update chip internal period estimation */
|
||||
@ -109,7 +99,7 @@ static bool inv_update_chip_period(struct inv_icm42600_timestamp *ts,
|
||||
return true;
|
||||
}
|
||||
|
||||
static void inv_align_timestamp_it(struct inv_icm42600_timestamp *ts)
|
||||
static void inv_align_timestamp_it(struct inv_sensors_timestamp *ts)
|
||||
{
|
||||
int64_t delta, jitter;
|
||||
int64_t adjust;
|
||||
@ -118,7 +108,7 @@ static void inv_align_timestamp_it(struct inv_icm42600_timestamp *ts)
|
||||
delta = ts->it.lo - ts->timestamp;
|
||||
|
||||
/* adjust timestamp while respecting jitter */
|
||||
jitter = div_s64((int64_t)ts->period * INV_ICM42600_TIMESTAMP_JITTER, 100);
|
||||
jitter = INV_SENSORS_TIMESTAMP_JITTER((int64_t)ts->period, ts->chip.jitter);
|
||||
if (delta > jitter)
|
||||
adjust = jitter;
|
||||
else if (delta < -jitter)
|
||||
@ -129,13 +119,13 @@ static void inv_align_timestamp_it(struct inv_icm42600_timestamp *ts)
|
||||
ts->timestamp += adjust;
|
||||
}
|
||||
|
||||
void inv_icm42600_timestamp_interrupt(struct inv_icm42600_timestamp *ts,
|
||||
void inv_sensors_timestamp_interrupt(struct inv_sensors_timestamp *ts,
|
||||
uint32_t fifo_period, size_t fifo_nb,
|
||||
size_t sensor_nb, int64_t timestamp)
|
||||
{
|
||||
struct inv_icm42600_timestamp_interval *it;
|
||||
struct inv_sensors_timestamp_interval *it;
|
||||
int64_t delta, interval;
|
||||
const uint32_t fifo_mult = fifo_period / INV_ICM42600_TIMESTAMP_PERIOD;
|
||||
const uint32_t fifo_mult = fifo_period / ts->chip.clock_period;
|
||||
uint32_t period = ts->period;
|
||||
bool valid = false;
|
||||
|
||||
@ -165,10 +155,11 @@ void inv_icm42600_timestamp_interrupt(struct inv_icm42600_timestamp *ts,
|
||||
if (valid)
|
||||
inv_align_timestamp_it(ts);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(inv_sensors_timestamp_interrupt, IIO_INV_SENSORS_TIMESTAMP);
|
||||
|
||||
void inv_icm42600_timestamp_apply_odr(struct inv_icm42600_timestamp *ts,
|
||||
uint32_t fifo_period, size_t fifo_nb,
|
||||
unsigned int fifo_no)
|
||||
void inv_sensors_timestamp_apply_odr(struct inv_sensors_timestamp *ts,
|
||||
uint32_t fifo_period, size_t fifo_nb,
|
||||
unsigned int fifo_no)
|
||||
{
|
||||
int64_t interval;
|
||||
uint32_t fifo_mult;
|
||||
@ -189,10 +180,15 @@ void inv_icm42600_timestamp_apply_odr(struct inv_icm42600_timestamp *ts,
|
||||
*/
|
||||
if (ts->timestamp != 0) {
|
||||
/* compute measured fifo period */
|
||||
fifo_mult = fifo_period / INV_ICM42600_TIMESTAMP_PERIOD;
|
||||
fifo_mult = fifo_period / ts->chip.clock_period;
|
||||
fifo_period = fifo_mult * ts->chip_period.val;
|
||||
/* computes time interval between interrupt and this sample */
|
||||
interval = (int64_t)(fifo_nb - fifo_no) * (int64_t)fifo_period;
|
||||
ts->timestamp = ts->it.up - interval;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(inv_sensors_timestamp_apply_odr, IIO_INV_SENSORS_TIMESTAMP);
|
||||
|
||||
MODULE_AUTHOR("InvenSense, Inc.");
|
||||
MODULE_DESCRIPTION("InvenSense sensors timestamp module");
|
||||
MODULE_LICENSE("GPL");
|
@ -389,6 +389,17 @@ config MCP4725
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called mcp4725.
|
||||
|
||||
config MCP4728
|
||||
tristate "MCP4728 DAC driver"
|
||||
depends on I2C
|
||||
help
|
||||
Say Y here if you want to build a driver for the Microchip
|
||||
MCP4728 quad channel, 12-bit digital-to-analog converter (DAC)
|
||||
with I2C interface.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called mcp4728.
|
||||
|
||||
config MCP4922
|
||||
tristate "MCP4902, MCP4912, MCP4922 DAC driver"
|
||||
depends on SPI
|
||||
|
@ -41,6 +41,7 @@ obj-$(CONFIG_MAX517) += max517.o
|
||||
obj-$(CONFIG_MAX5522) += max5522.o
|
||||
obj-$(CONFIG_MAX5821) += max5821.o
|
||||
obj-$(CONFIG_MCP4725) += mcp4725.o
|
||||
obj-$(CONFIG_MCP4728) += mcp4728.o
|
||||
obj-$(CONFIG_MCP4922) += mcp4922.o
|
||||
obj-$(CONFIG_STM32_DAC_CORE) += stm32-dac-core.o
|
||||
obj-$(CONFIG_STM32_DAC) += stm32-dac.o
|
||||
|
618
drivers/iio/dac/mcp4728.c
Normal file
618
drivers/iio/dac/mcp4728.c
Normal file
@ -0,0 +1,618 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Support for Microchip MCP4728
|
||||
*
|
||||
* Copyright (C) 2023 Andrea Collamati <andrea.collamati@gmail.com>
|
||||
*
|
||||
* Based on mcp4725 by Peter Meerwald <pmeerw@pmeerw.net>
|
||||
*
|
||||
* Driver for the Microchip I2C 12-bit digital-to-analog quad channels
|
||||
* converter (DAC).
|
||||
*
|
||||
* (7-bit I2C slave address 0x60, the three LSBs can be configured in
|
||||
* hardware)
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#define MCP4728_RESOLUTION 12
|
||||
#define MCP4728_N_CHANNELS 4
|
||||
|
||||
#define MCP4728_CMD_MASK GENMASK(7, 3)
|
||||
#define MCP4728_CHSEL_MASK GENMASK(2, 1)
|
||||
#define MCP4728_UDAC_MASK BIT(0)
|
||||
|
||||
#define MCP4728_VREF_MASK BIT(7)
|
||||
#define MCP4728_PDMODE_MASK GENMASK(6, 5)
|
||||
#define MCP4728_GAIN_MASK BIT(4)
|
||||
|
||||
#define MCP4728_DAC_H_MASK GENMASK(3, 0)
|
||||
#define MCP4728_DAC_L_MASK GENMASK(7, 0)
|
||||
|
||||
#define MCP4728_RDY_MASK BIT(7)
|
||||
|
||||
#define MCP4728_MW_CMD 0x08 /* Multiwrite Command */
|
||||
#define MCP4728_SW_CMD 0x0A /* Sequential Write Command with EEPROM */
|
||||
|
||||
#define MCP4728_READ_RESPONSE_LEN (MCP4728_N_CHANNELS * 3 * 2)
|
||||
#define MCP4728_WRITE_EEPROM_LEN (1 + MCP4728_N_CHANNELS * 2)
|
||||
|
||||
enum vref_mode {
|
||||
MCP4728_VREF_EXTERNAL_VDD = 0,
|
||||
MCP4728_VREF_INTERNAL_2048mV = 1,
|
||||
};
|
||||
|
||||
enum gain_mode {
|
||||
MCP4728_GAIN_X1 = 0,
|
||||
MCP4728_GAIN_X2 = 1,
|
||||
};
|
||||
|
||||
enum iio_powerdown_mode {
|
||||
MCP4728_IIO_1K,
|
||||
MCP4728_IIO_100K,
|
||||
MCP4728_IIO_500K,
|
||||
};
|
||||
|
||||
struct mcp4728_channel_data {
|
||||
enum vref_mode ref_mode;
|
||||
enum iio_powerdown_mode pd_mode;
|
||||
enum gain_mode g_mode;
|
||||
u16 dac_value;
|
||||
};
|
||||
|
||||
/* MCP4728 Full Scale Ranges
|
||||
* the device available ranges are
|
||||
* - VREF = VDD FSR = from 0.0V to VDD
|
||||
* - VREF = Internal Gain = 1 FSR = from 0.0V to VREF
|
||||
* - VREF = Internal Gain = 2 FSR = from 0.0V to 2*VREF
|
||||
*/
|
||||
enum mcp4728_scale {
|
||||
MCP4728_SCALE_VDD,
|
||||
MCP4728_SCALE_VINT_NO_GAIN,
|
||||
MCP4728_SCALE_VINT_GAIN_X2,
|
||||
MCP4728_N_SCALES
|
||||
};
|
||||
|
||||
struct mcp4728_data {
|
||||
struct i2c_client *client;
|
||||
struct regulator *vdd_reg;
|
||||
bool powerdown;
|
||||
int scales_avail[MCP4728_N_SCALES * 2];
|
||||
struct mcp4728_channel_data chdata[MCP4728_N_CHANNELS];
|
||||
};
|
||||
|
||||
#define MCP4728_CHAN(chan) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.output = 1, \
|
||||
.indexed = 1, \
|
||||
.channel = chan, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.ext_info = mcp4728_ext_info, \
|
||||
}
|
||||
|
||||
static int mcp4728_suspend(struct device *dev);
|
||||
static int mcp4728_resume(struct device *dev);
|
||||
|
||||
static ssize_t mcp4728_store_eeprom(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct mcp4728_data *data = iio_priv(indio_dev);
|
||||
u8 outbuf[MCP4728_WRITE_EEPROM_LEN];
|
||||
int tries = 20;
|
||||
u8 inbuf[3];
|
||||
bool state;
|
||||
int ret;
|
||||
unsigned int i;
|
||||
|
||||
ret = kstrtobool(buf, &state);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (!state)
|
||||
return 0;
|
||||
|
||||
outbuf[0] = FIELD_PREP(MCP4728_CMD_MASK, MCP4728_SW_CMD);
|
||||
|
||||
for (i = 0; i < MCP4728_N_CHANNELS; i++) {
|
||||
struct mcp4728_channel_data *ch = &data->chdata[i];
|
||||
int offset = 1 + i * 2;
|
||||
|
||||
outbuf[offset] = FIELD_PREP(MCP4728_VREF_MASK, ch->ref_mode);
|
||||
|
||||
if (data->powerdown) {
|
||||
u8 mcp4728_pd_mode = ch->pd_mode + 1;
|
||||
|
||||
outbuf[offset] |= FIELD_PREP(MCP4728_PDMODE_MASK,
|
||||
mcp4728_pd_mode);
|
||||
}
|
||||
|
||||
outbuf[offset] |= FIELD_PREP(MCP4728_GAIN_MASK, ch->g_mode);
|
||||
outbuf[offset] |=
|
||||
FIELD_PREP(MCP4728_DAC_H_MASK, ch->dac_value >> 8);
|
||||
outbuf[offset + 1] =
|
||||
FIELD_PREP(MCP4728_DAC_L_MASK, ch->dac_value);
|
||||
}
|
||||
|
||||
ret = i2c_master_send(data->client, outbuf, MCP4728_WRITE_EEPROM_LEN);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
else if (ret != MCP4728_WRITE_EEPROM_LEN)
|
||||
return -EIO;
|
||||
|
||||
/* wait RDY signal for write complete, takes up to 50ms */
|
||||
while (tries--) {
|
||||
msleep(20);
|
||||
ret = i2c_master_recv(data->client, inbuf, 3);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
else if (ret != 3)
|
||||
return -EIO;
|
||||
|
||||
if (FIELD_GET(MCP4728_RDY_MASK, inbuf[0]))
|
||||
break;
|
||||
}
|
||||
|
||||
if (tries < 0) {
|
||||
dev_err(&data->client->dev, "%s failed, incomplete\n",
|
||||
__func__);
|
||||
return -EIO;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
static IIO_DEVICE_ATTR(store_eeprom, 0200, NULL, mcp4728_store_eeprom, 0);
|
||||
|
||||
static struct attribute *mcp4728_attributes[] = {
|
||||
&iio_dev_attr_store_eeprom.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group mcp4728_attribute_group = {
|
||||
.attrs = mcp4728_attributes,
|
||||
};
|
||||
|
||||
static int mcp4728_program_channel_cfg(int channel, struct iio_dev *indio_dev)
|
||||
{
|
||||
struct mcp4728_data *data = iio_priv(indio_dev);
|
||||
struct mcp4728_channel_data *ch = &data->chdata[channel];
|
||||
u8 outbuf[3];
|
||||
int ret;
|
||||
|
||||
outbuf[0] = FIELD_PREP(MCP4728_CMD_MASK, MCP4728_MW_CMD);
|
||||
outbuf[0] |= FIELD_PREP(MCP4728_CHSEL_MASK, channel);
|
||||
outbuf[0] |= FIELD_PREP(MCP4728_UDAC_MASK, 0);
|
||||
|
||||
outbuf[1] = FIELD_PREP(MCP4728_VREF_MASK, ch->ref_mode);
|
||||
|
||||
if (data->powerdown)
|
||||
outbuf[1] |= FIELD_PREP(MCP4728_PDMODE_MASK, ch->pd_mode + 1);
|
||||
|
||||
outbuf[1] |= FIELD_PREP(MCP4728_GAIN_MASK, ch->g_mode);
|
||||
outbuf[1] |= FIELD_PREP(MCP4728_DAC_H_MASK, ch->dac_value >> 8);
|
||||
outbuf[2] = FIELD_PREP(MCP4728_DAC_L_MASK, ch->dac_value);
|
||||
|
||||
ret = i2c_master_send(data->client, outbuf, 3);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
else if (ret != 3)
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *const mcp4728_powerdown_modes[] = { "1kohm_to_gnd",
|
||||
"100kohm_to_gnd",
|
||||
"500kohm_to_gnd" };
|
||||
|
||||
static int mcp4728_get_powerdown_mode(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan)
|
||||
{
|
||||
struct mcp4728_data *data = iio_priv(indio_dev);
|
||||
|
||||
return data->chdata[chan->channel].pd_mode;
|
||||
}
|
||||
|
||||
static int mcp4728_set_powerdown_mode(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
unsigned int mode)
|
||||
{
|
||||
struct mcp4728_data *data = iio_priv(indio_dev);
|
||||
|
||||
data->chdata[chan->channel].pd_mode = mode;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t mcp4728_read_powerdown(struct iio_dev *indio_dev,
|
||||
uintptr_t private,
|
||||
const struct iio_chan_spec *chan,
|
||||
char *buf)
|
||||
{
|
||||
struct mcp4728_data *data = iio_priv(indio_dev);
|
||||
|
||||
return sysfs_emit(buf, "%d\n", data->powerdown);
|
||||
}
|
||||
|
||||
static ssize_t mcp4728_write_powerdown(struct iio_dev *indio_dev,
|
||||
uintptr_t private,
|
||||
const struct iio_chan_spec *chan,
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
struct mcp4728_data *data = iio_priv(indio_dev);
|
||||
bool state;
|
||||
int ret;
|
||||
|
||||
ret = kstrtobool(buf, &state);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (state)
|
||||
ret = mcp4728_suspend(&data->client->dev);
|
||||
else
|
||||
ret = mcp4728_resume(&data->client->dev);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static const struct iio_enum mcp4728_powerdown_mode_enum = {
|
||||
.items = mcp4728_powerdown_modes,
|
||||
.num_items = ARRAY_SIZE(mcp4728_powerdown_modes),
|
||||
.get = mcp4728_get_powerdown_mode,
|
||||
.set = mcp4728_set_powerdown_mode,
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec_ext_info mcp4728_ext_info[] = {
|
||||
{
|
||||
.name = "powerdown",
|
||||
.read = mcp4728_read_powerdown,
|
||||
.write = mcp4728_write_powerdown,
|
||||
.shared = IIO_SEPARATE,
|
||||
},
|
||||
IIO_ENUM("powerdown_mode", IIO_SEPARATE, &mcp4728_powerdown_mode_enum),
|
||||
IIO_ENUM_AVAILABLE("powerdown_mode", IIO_SHARED_BY_TYPE,
|
||||
&mcp4728_powerdown_mode_enum),
|
||||
{},
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec mcp4728_channels[MCP4728_N_CHANNELS] = {
|
||||
MCP4728_CHAN(0),
|
||||
MCP4728_CHAN(1),
|
||||
MCP4728_CHAN(2),
|
||||
MCP4728_CHAN(3),
|
||||
};
|
||||
|
||||
static void mcp4728_get_scale_avail(enum mcp4728_scale scale,
|
||||
struct mcp4728_data *data, int *val,
|
||||
int *val2)
|
||||
{
|
||||
*val = data->scales_avail[scale * 2];
|
||||
*val2 = data->scales_avail[scale * 2 + 1];
|
||||
}
|
||||
|
||||
static void mcp4728_get_scale(int channel, struct mcp4728_data *data, int *val,
|
||||
int *val2)
|
||||
{
|
||||
int ref_mode = data->chdata[channel].ref_mode;
|
||||
int g_mode = data->chdata[channel].g_mode;
|
||||
|
||||
if (ref_mode == MCP4728_VREF_EXTERNAL_VDD) {
|
||||
mcp4728_get_scale_avail(MCP4728_SCALE_VDD, data, val, val2);
|
||||
} else {
|
||||
if (g_mode == MCP4728_GAIN_X1) {
|
||||
mcp4728_get_scale_avail(MCP4728_SCALE_VINT_NO_GAIN,
|
||||
data, val, val2);
|
||||
} else {
|
||||
mcp4728_get_scale_avail(MCP4728_SCALE_VINT_GAIN_X2,
|
||||
data, val, val2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int mcp4728_find_matching_scale(struct mcp4728_data *data, int val,
|
||||
int val2)
|
||||
{
|
||||
for (int i = 0; i < MCP4728_N_SCALES; i++) {
|
||||
if (data->scales_avail[i * 2] == val &&
|
||||
data->scales_avail[i * 2 + 1] == val2)
|
||||
return i;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int mcp4728_set_scale(int channel, struct mcp4728_data *data, int val,
|
||||
int val2)
|
||||
{
|
||||
int scale = mcp4728_find_matching_scale(data, val, val2);
|
||||
|
||||
if (scale < 0)
|
||||
return scale;
|
||||
|
||||
switch (scale) {
|
||||
case MCP4728_SCALE_VDD:
|
||||
data->chdata[channel].ref_mode = MCP4728_VREF_EXTERNAL_VDD;
|
||||
return 0;
|
||||
case MCP4728_SCALE_VINT_NO_GAIN:
|
||||
data->chdata[channel].ref_mode = MCP4728_VREF_INTERNAL_2048mV;
|
||||
data->chdata[channel].g_mode = MCP4728_GAIN_X1;
|
||||
return 0;
|
||||
case MCP4728_SCALE_VINT_GAIN_X2:
|
||||
data->chdata[channel].ref_mode = MCP4728_VREF_INTERNAL_2048mV;
|
||||
data->chdata[channel].g_mode = MCP4728_GAIN_X2;
|
||||
return 0;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int mcp4728_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val,
|
||||
int *val2, long mask)
|
||||
{
|
||||
struct mcp4728_data *data = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
*val = data->chdata[chan->channel].dac_value;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
mcp4728_get_scale(chan->channel, data, val, val2);
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int mcp4728_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int val,
|
||||
int val2, long mask)
|
||||
{
|
||||
struct mcp4728_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (val < 0 || val > GENMASK(MCP4728_RESOLUTION - 1, 0))
|
||||
return -EINVAL;
|
||||
data->chdata[chan->channel].dac_value = val;
|
||||
return mcp4728_program_channel_cfg(chan->channel, indio_dev);
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
ret = mcp4728_set_scale(chan->channel, data, val, val2);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return mcp4728_program_channel_cfg(chan->channel, indio_dev);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static void mcp4728_init_scale_avail(enum mcp4728_scale scale, int vref_mv,
|
||||
struct mcp4728_data *data)
|
||||
{
|
||||
s64 tmp;
|
||||
int value_micro;
|
||||
int value_int;
|
||||
|
||||
tmp = (s64)vref_mv * 1000000LL >> MCP4728_RESOLUTION;
|
||||
value_int = div_s64_rem(tmp, 1000000LL, &value_micro);
|
||||
|
||||
data->scales_avail[scale * 2] = value_int;
|
||||
data->scales_avail[scale * 2 + 1] = value_micro;
|
||||
}
|
||||
|
||||
static int mcp4728_init_scales_avail(struct mcp4728_data *data)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = regulator_get_voltage(data->vdd_reg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
mcp4728_init_scale_avail(MCP4728_SCALE_VDD, ret / 1000, data);
|
||||
mcp4728_init_scale_avail(MCP4728_SCALE_VINT_NO_GAIN, 2048, data);
|
||||
mcp4728_init_scale_avail(MCP4728_SCALE_VINT_GAIN_X2, 4096, data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mcp4728_read_avail(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
const int **vals, int *type, int *length,
|
||||
long info)
|
||||
{
|
||||
struct mcp4728_data *data = iio_priv(indio_dev);
|
||||
|
||||
switch (info) {
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*type = IIO_VAL_INT_PLUS_MICRO;
|
||||
|
||||
switch (chan->type) {
|
||||
case IIO_VOLTAGE:
|
||||
*vals = data->scales_avail;
|
||||
*length = MCP4728_N_SCALES * 2;
|
||||
return IIO_AVAIL_LIST;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct iio_info mcp4728_info = {
|
||||
.read_raw = mcp4728_read_raw,
|
||||
.write_raw = mcp4728_write_raw,
|
||||
.read_avail = &mcp4728_read_avail,
|
||||
.attrs = &mcp4728_attribute_group,
|
||||
};
|
||||
|
||||
static int mcp4728_suspend(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct mcp4728_data *data = iio_priv(indio_dev);
|
||||
unsigned int i;
|
||||
|
||||
data->powerdown = true;
|
||||
|
||||
for (i = 0; i < MCP4728_N_CHANNELS; i++) {
|
||||
int err = mcp4728_program_channel_cfg(i, indio_dev);
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mcp4728_resume(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct mcp4728_data *data = iio_priv(indio_dev);
|
||||
int err = 0;
|
||||
unsigned int i;
|
||||
|
||||
data->powerdown = false;
|
||||
|
||||
for (i = 0; i < MCP4728_N_CHANNELS; i++) {
|
||||
int ret = mcp4728_program_channel_cfg(i, indio_dev);
|
||||
|
||||
if (ret)
|
||||
err = ret;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static DEFINE_SIMPLE_DEV_PM_OPS(mcp4728_pm_ops, mcp4728_suspend,
|
||||
mcp4728_resume);
|
||||
|
||||
static int mcp4728_init_channels_data(struct mcp4728_data *data)
|
||||
{
|
||||
u8 inbuf[MCP4728_READ_RESPONSE_LEN];
|
||||
int ret;
|
||||
unsigned int i;
|
||||
|
||||
ret = i2c_master_recv(data->client, inbuf, MCP4728_READ_RESPONSE_LEN);
|
||||
if (ret < 0) {
|
||||
return dev_err_probe(&data->client->dev, ret,
|
||||
"failed to read mcp4728 conf.\n");
|
||||
} else if (ret != MCP4728_READ_RESPONSE_LEN) {
|
||||
return dev_err_probe(&data->client->dev, -EIO,
|
||||
"failed to read mcp4728 conf. Wrong Response Len ret=%d\n",
|
||||
ret);
|
||||
}
|
||||
|
||||
for (i = 0; i < MCP4728_N_CHANNELS; i++) {
|
||||
struct mcp4728_channel_data *ch = &data->chdata[i];
|
||||
u8 r2 = inbuf[i * 6 + 1];
|
||||
u8 r3 = inbuf[i * 6 + 2];
|
||||
|
||||
ch->dac_value = FIELD_GET(MCP4728_DAC_H_MASK, r2) << 8 |
|
||||
FIELD_GET(MCP4728_DAC_L_MASK, r3);
|
||||
ch->ref_mode = FIELD_GET(MCP4728_VREF_MASK, r2);
|
||||
ch->pd_mode = FIELD_GET(MCP4728_PDMODE_MASK, r2);
|
||||
ch->g_mode = FIELD_GET(MCP4728_GAIN_MASK, r2);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mcp4728_reg_disable(void *reg)
|
||||
{
|
||||
regulator_disable(reg);
|
||||
}
|
||||
|
||||
static int mcp4728_probe(struct i2c_client *client)
|
||||
{
|
||||
const struct i2c_device_id *id = i2c_client_get_device_id(client);
|
||||
struct mcp4728_data *data;
|
||||
struct iio_dev *indio_dev;
|
||||
int err;
|
||||
|
||||
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;
|
||||
|
||||
data->vdd_reg = devm_regulator_get(&client->dev, "vdd");
|
||||
if (IS_ERR(data->vdd_reg))
|
||||
return PTR_ERR(data->vdd_reg);
|
||||
|
||||
err = regulator_enable(data->vdd_reg);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = devm_add_action_or_reset(&client->dev, mcp4728_reg_disable,
|
||||
data->vdd_reg);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/*
|
||||
* MCP4728 has internal EEPROM that save each channel boot
|
||||
* configuration. It means that device configuration is unknown to the
|
||||
* driver at kernel boot. mcp4728_init_channels_data() reads back DAC
|
||||
* settings and stores them in data structure.
|
||||
*/
|
||||
err = mcp4728_init_channels_data(data);
|
||||
if (err) {
|
||||
return dev_err_probe(&client->dev, err,
|
||||
"failed to read mcp4728 current configuration\n");
|
||||
}
|
||||
|
||||
err = mcp4728_init_scales_avail(data);
|
||||
if (err) {
|
||||
return dev_err_probe(&client->dev, err,
|
||||
"failed to init scales\n");
|
||||
}
|
||||
|
||||
indio_dev->name = id->name;
|
||||
indio_dev->info = &mcp4728_info;
|
||||
indio_dev->channels = mcp4728_channels;
|
||||
indio_dev->num_channels = MCP4728_N_CHANNELS;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
return devm_iio_device_register(&client->dev, indio_dev);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id mcp4728_id[] = {
|
||||
{ "mcp4728", 0 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, mcp4728_id);
|
||||
|
||||
static const struct of_device_id mcp4728_of_match[] = {
|
||||
{ .compatible = "microchip,mcp4728" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mcp4728_of_match);
|
||||
|
||||
static struct i2c_driver mcp4728_driver = {
|
||||
.driver = {
|
||||
.name = "mcp4728",
|
||||
.of_match_table = mcp4728_of_match,
|
||||
.pm = pm_sleep_ptr(&mcp4728_pm_ops),
|
||||
},
|
||||
.probe = mcp4728_probe,
|
||||
.id_table = mcp4728_id,
|
||||
};
|
||||
module_i2c_driver(mcp4728_driver);
|
||||
|
||||
MODULE_AUTHOR("Andrea Collamati <andrea.collamati@gmail.com>");
|
||||
MODULE_DESCRIPTION("MCP4728 12-bit DAC");
|
||||
MODULE_LICENSE("GPL");
|
@ -78,6 +78,7 @@ enum {
|
||||
enum {
|
||||
ADMV8818_AUTO_MODE,
|
||||
ADMV8818_MANUAL_MODE,
|
||||
ADMV8818_BYPASS_MODE,
|
||||
};
|
||||
|
||||
struct admv8818_state {
|
||||
@ -114,7 +115,8 @@ static const struct regmap_config admv8818_regmap_config = {
|
||||
|
||||
static const char * const admv8818_modes[] = {
|
||||
[0] = "auto",
|
||||
[1] = "manual"
|
||||
[1] = "manual",
|
||||
[2] = "bypass"
|
||||
};
|
||||
|
||||
static int __admv8818_hpf_select(struct admv8818_state *st, u64 freq)
|
||||
@ -394,6 +396,36 @@ static int admv8818_reg_access(struct iio_dev *indio_dev,
|
||||
return regmap_write(st->regmap, reg, write_val);
|
||||
}
|
||||
|
||||
static int admv8818_filter_bypass(struct admv8818_state *st)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&st->lock);
|
||||
|
||||
ret = regmap_update_bits(st->regmap, ADMV8818_REG_WR0_SW,
|
||||
ADMV8818_SW_IN_SET_WR0_MSK |
|
||||
ADMV8818_SW_IN_WR0_MSK |
|
||||
ADMV8818_SW_OUT_SET_WR0_MSK |
|
||||
ADMV8818_SW_OUT_WR0_MSK,
|
||||
FIELD_PREP(ADMV8818_SW_IN_SET_WR0_MSK, 1) |
|
||||
FIELD_PREP(ADMV8818_SW_IN_WR0_MSK, 0) |
|
||||
FIELD_PREP(ADMV8818_SW_OUT_SET_WR0_MSK, 1) |
|
||||
FIELD_PREP(ADMV8818_SW_OUT_WR0_MSK, 0));
|
||||
if (ret)
|
||||
goto exit;
|
||||
|
||||
ret = regmap_update_bits(st->regmap, ADMV8818_REG_WR0_FILTER,
|
||||
ADMV8818_HPF_WR0_MSK |
|
||||
ADMV8818_LPF_WR0_MSK,
|
||||
FIELD_PREP(ADMV8818_HPF_WR0_MSK, 0) |
|
||||
FIELD_PREP(ADMV8818_LPF_WR0_MSK, 0));
|
||||
|
||||
exit:
|
||||
mutex_unlock(&st->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int admv8818_get_mode(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan)
|
||||
{
|
||||
@ -411,14 +443,22 @@ static int admv8818_set_mode(struct iio_dev *indio_dev,
|
||||
|
||||
if (!st->clkin) {
|
||||
if (mode == ADMV8818_MANUAL_MODE)
|
||||
return 0;
|
||||
goto set_mode;
|
||||
|
||||
if (mode == ADMV8818_BYPASS_MODE) {
|
||||
ret = admv8818_filter_bypass(st);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
goto set_mode;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (mode) {
|
||||
case ADMV8818_AUTO_MODE:
|
||||
if (!st->filter_mode)
|
||||
if (st->filter_mode == ADMV8818_AUTO_MODE)
|
||||
return 0;
|
||||
|
||||
ret = clk_prepare_enable(st->clkin);
|
||||
@ -434,20 +474,27 @@ static int admv8818_set_mode(struct iio_dev *indio_dev,
|
||||
|
||||
break;
|
||||
case ADMV8818_MANUAL_MODE:
|
||||
if (st->filter_mode)
|
||||
return 0;
|
||||
case ADMV8818_BYPASS_MODE:
|
||||
if (st->filter_mode == ADMV8818_AUTO_MODE) {
|
||||
clk_disable_unprepare(st->clkin);
|
||||
|
||||
clk_disable_unprepare(st->clkin);
|
||||
ret = clk_notifier_unregister(st->clkin, &st->nb);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = clk_notifier_unregister(st->clkin, &st->nb);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (mode == ADMV8818_BYPASS_MODE) {
|
||||
ret = admv8818_filter_bypass(st);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
set_mode:
|
||||
st->filter_mode = mode;
|
||||
|
||||
return ret;
|
||||
|
@ -379,6 +379,11 @@ static const struct iio_info admv1013_info = {
|
||||
.debugfs_reg_access = &admv1013_reg_access,
|
||||
};
|
||||
|
||||
static const char * const admv1013_vcc_regs[] = {
|
||||
"vcc-drv", "vcc2-drv", "vcc-vva", "vcc-amp1", "vcc-amp2",
|
||||
"vcc-env", "vcc-bg", "vcc-bg2", "vcc-mixer", "vcc-quad"
|
||||
};
|
||||
|
||||
static int admv1013_freq_change(struct notifier_block *nb, unsigned long action, void *data)
|
||||
{
|
||||
struct admv1013_state *st = container_of(nb, struct admv1013_state, nb);
|
||||
@ -554,6 +559,15 @@ static int admv1013_properties_parse(struct admv1013_state *st)
|
||||
return dev_err_probe(&spi->dev, PTR_ERR(st->reg),
|
||||
"failed to get the common-mode voltage\n");
|
||||
|
||||
ret = devm_regulator_bulk_get_enable(&st->spi->dev,
|
||||
ARRAY_SIZE(admv1013_vcc_regs),
|
||||
admv1013_vcc_regs);
|
||||
if (ret) {
|
||||
dev_err_probe(&spi->dev, ret,
|
||||
"Failed to request VCC regulators\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -115,8 +115,6 @@ enum {
|
||||
ADIS16475_SCAN_ACCEL_Y,
|
||||
ADIS16475_SCAN_ACCEL_Z,
|
||||
ADIS16475_SCAN_TEMP,
|
||||
ADIS16475_SCAN_DIAG_S_FLAGS,
|
||||
ADIS16475_SCAN_CRC_FAILURE,
|
||||
};
|
||||
|
||||
static bool low_rate_allow;
|
||||
@ -728,6 +726,7 @@ static const struct adis16475_chip_info adis16475_chip_info[] = {
|
||||
.max_dec = 1999,
|
||||
.sync = adis16475_sync_mode,
|
||||
.num_sync = ARRAY_SIZE(adis16475_sync_mode),
|
||||
.has_burst32 = true,
|
||||
.adis_data = ADIS16475_DATA(16477, &adis16475_timeouts),
|
||||
},
|
||||
[ADIS16477_2] = {
|
||||
@ -743,6 +742,7 @@ static const struct adis16475_chip_info adis16475_chip_info[] = {
|
||||
.max_dec = 1999,
|
||||
.sync = adis16475_sync_mode,
|
||||
.num_sync = ARRAY_SIZE(adis16475_sync_mode),
|
||||
.has_burst32 = true,
|
||||
.adis_data = ADIS16475_DATA(16477, &adis16475_timeouts),
|
||||
},
|
||||
[ADIS16477_3] = {
|
||||
@ -758,6 +758,7 @@ static const struct adis16475_chip_info adis16475_chip_info[] = {
|
||||
.max_dec = 1999,
|
||||
.sync = adis16475_sync_mode,
|
||||
.num_sync = ARRAY_SIZE(adis16475_sync_mode),
|
||||
.has_burst32 = true,
|
||||
.adis_data = ADIS16475_DATA(16477, &adis16475_timeouts),
|
||||
},
|
||||
[ADIS16465_1] = {
|
||||
|
@ -3,6 +3,7 @@
|
||||
config INV_ICM42600
|
||||
tristate
|
||||
select IIO_BUFFER
|
||||
select IIO_INV_SENSORS_TIMESTAMP
|
||||
|
||||
config INV_ICM42600_I2C
|
||||
tristate "InvenSense ICM-426xx I2C driver"
|
||||
|
@ -6,7 +6,6 @@ inv-icm42600-y += inv_icm42600_gyro.o
|
||||
inv-icm42600-y += inv_icm42600_accel.o
|
||||
inv-icm42600-y += inv_icm42600_temp.o
|
||||
inv-icm42600-y += inv_icm42600_buffer.o
|
||||
inv-icm42600-y += inv_icm42600_timestamp.o
|
||||
|
||||
obj-$(CONFIG_INV_ICM42600_I2C) += inv-icm42600-i2c.o
|
||||
inv-icm42600-i2c-y += inv_icm42600_i2c.o
|
||||
|
@ -10,14 +10,15 @@
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/math64.h>
|
||||
#include <linux/iio/iio.h>
|
||||
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/common/inv_sensors_timestamp.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/kfifo_buf.h>
|
||||
|
||||
#include "inv_icm42600.h"
|
||||
#include "inv_icm42600_temp.h"
|
||||
#include "inv_icm42600_buffer.h"
|
||||
#include "inv_icm42600_timestamp.h"
|
||||
|
||||
#define INV_ICM42600_ACCEL_CHAN(_modifier, _index, _ext_info) \
|
||||
{ \
|
||||
@ -98,7 +99,7 @@ static int inv_icm42600_accel_update_scan_mode(struct iio_dev *indio_dev,
|
||||
const unsigned long *scan_mask)
|
||||
{
|
||||
struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev);
|
||||
struct inv_icm42600_timestamp *ts = iio_priv(indio_dev);
|
||||
struct inv_sensors_timestamp *ts = iio_priv(indio_dev);
|
||||
struct inv_icm42600_sensor_conf conf = INV_ICM42600_SENSOR_CONF_INIT;
|
||||
unsigned int fifo_en = 0;
|
||||
unsigned int sleep_temp = 0;
|
||||
@ -126,7 +127,7 @@ static int inv_icm42600_accel_update_scan_mode(struct iio_dev *indio_dev,
|
||||
}
|
||||
|
||||
/* update data FIFO write */
|
||||
inv_icm42600_timestamp_apply_odr(ts, 0, 0, 0);
|
||||
inv_sensors_timestamp_apply_odr(ts, 0, 0, 0);
|
||||
ret = inv_icm42600_buffer_set_fifo_en(st, fifo_en | st->fifo.en);
|
||||
if (ret)
|
||||
goto out_unlock;
|
||||
@ -311,7 +312,7 @@ static int inv_icm42600_accel_write_odr(struct iio_dev *indio_dev,
|
||||
int val, int val2)
|
||||
{
|
||||
struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev);
|
||||
struct inv_icm42600_timestamp *ts = iio_priv(indio_dev);
|
||||
struct inv_sensors_timestamp *ts = iio_priv(indio_dev);
|
||||
struct device *dev = regmap_get_device(st->map);
|
||||
unsigned int idx;
|
||||
struct inv_icm42600_sensor_conf conf = INV_ICM42600_SENSOR_CONF_INIT;
|
||||
@ -330,8 +331,8 @@ static int inv_icm42600_accel_write_odr(struct iio_dev *indio_dev,
|
||||
pm_runtime_get_sync(dev);
|
||||
mutex_lock(&st->lock);
|
||||
|
||||
ret = inv_icm42600_timestamp_update_odr(ts, inv_icm42600_odr_to_period(conf.odr),
|
||||
iio_buffer_enabled(indio_dev));
|
||||
ret = inv_sensors_timestamp_update_odr(ts, inv_icm42600_odr_to_period(conf.odr),
|
||||
iio_buffer_enabled(indio_dev));
|
||||
if (ret)
|
||||
goto out_unlock;
|
||||
|
||||
@ -707,7 +708,8 @@ struct iio_dev *inv_icm42600_accel_init(struct inv_icm42600_state *st)
|
||||
{
|
||||
struct device *dev = regmap_get_device(st->map);
|
||||
const char *name;
|
||||
struct inv_icm42600_timestamp *ts;
|
||||
struct inv_sensors_timestamp_chip ts_chip;
|
||||
struct inv_sensors_timestamp *ts;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret;
|
||||
|
||||
@ -719,8 +721,15 @@ struct iio_dev *inv_icm42600_accel_init(struct inv_icm42600_state *st)
|
||||
if (!indio_dev)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
/*
|
||||
* clock period is 32kHz (31250ns)
|
||||
* jitter is +/- 2% (20 per mille)
|
||||
*/
|
||||
ts_chip.clock_period = 31250;
|
||||
ts_chip.jitter = 20;
|
||||
ts_chip.init_period = inv_icm42600_odr_to_period(st->conf.accel.odr);
|
||||
ts = iio_priv(indio_dev);
|
||||
inv_icm42600_timestamp_init(ts, inv_icm42600_odr_to_period(st->conf.accel.odr));
|
||||
inv_sensors_timestamp_init(ts, &ts_chip);
|
||||
|
||||
iio_device_set_drvdata(indio_dev, st);
|
||||
indio_dev->name = name;
|
||||
@ -745,7 +754,7 @@ struct iio_dev *inv_icm42600_accel_init(struct inv_icm42600_state *st)
|
||||
int inv_icm42600_accel_parse_fifo(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev);
|
||||
struct inv_icm42600_timestamp *ts = iio_priv(indio_dev);
|
||||
struct inv_sensors_timestamp *ts = iio_priv(indio_dev);
|
||||
ssize_t i, size;
|
||||
unsigned int no;
|
||||
const void *accel, *gyro, *timestamp;
|
||||
@ -768,15 +777,15 @@ int inv_icm42600_accel_parse_fifo(struct iio_dev *indio_dev)
|
||||
|
||||
/* update odr */
|
||||
if (odr & INV_ICM42600_SENSOR_ACCEL)
|
||||
inv_icm42600_timestamp_apply_odr(ts, st->fifo.period,
|
||||
st->fifo.nb.total, no);
|
||||
inv_sensors_timestamp_apply_odr(ts, st->fifo.period,
|
||||
st->fifo.nb.total, no);
|
||||
|
||||
/* buffer is copied to userspace, zeroing it to avoid any data leak */
|
||||
memset(&buffer, 0, sizeof(buffer));
|
||||
memcpy(&buffer.accel, accel, sizeof(buffer.accel));
|
||||
/* convert 8 bits FIFO temperature in high resolution format */
|
||||
buffer.temp = temp ? (*temp * 64) : 0;
|
||||
ts_val = inv_icm42600_timestamp_pop(ts);
|
||||
ts_val = inv_sensors_timestamp_pop(ts);
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, &buffer, ts_val);
|
||||
}
|
||||
|
||||
|
@ -9,11 +9,12 @@
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/iio/iio.h>
|
||||
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/common/inv_sensors_timestamp.h>
|
||||
#include <linux/iio/iio.h>
|
||||
|
||||
#include "inv_icm42600.h"
|
||||
#include "inv_icm42600_timestamp.h"
|
||||
#include "inv_icm42600_buffer.h"
|
||||
|
||||
/* FIFO header: 1 byte */
|
||||
@ -275,12 +276,12 @@ static int inv_icm42600_buffer_preenable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev);
|
||||
struct device *dev = regmap_get_device(st->map);
|
||||
struct inv_icm42600_timestamp *ts = iio_priv(indio_dev);
|
||||
struct inv_sensors_timestamp *ts = iio_priv(indio_dev);
|
||||
|
||||
pm_runtime_get_sync(dev);
|
||||
|
||||
mutex_lock(&st->lock);
|
||||
inv_icm42600_timestamp_reset(ts);
|
||||
inv_sensors_timestamp_reset(ts);
|
||||
mutex_unlock(&st->lock);
|
||||
|
||||
return 0;
|
||||
@ -504,7 +505,7 @@ int inv_icm42600_buffer_fifo_read(struct inv_icm42600_state *st,
|
||||
|
||||
int inv_icm42600_buffer_fifo_parse(struct inv_icm42600_state *st)
|
||||
{
|
||||
struct inv_icm42600_timestamp *ts;
|
||||
struct inv_sensors_timestamp *ts;
|
||||
int ret;
|
||||
|
||||
if (st->fifo.nb.total == 0)
|
||||
@ -512,8 +513,8 @@ int inv_icm42600_buffer_fifo_parse(struct inv_icm42600_state *st)
|
||||
|
||||
/* handle gyroscope timestamp and FIFO data parsing */
|
||||
ts = iio_priv(st->indio_gyro);
|
||||
inv_icm42600_timestamp_interrupt(ts, st->fifo.period, st->fifo.nb.total,
|
||||
st->fifo.nb.gyro, st->timestamp.gyro);
|
||||
inv_sensors_timestamp_interrupt(ts, st->fifo.period, st->fifo.nb.total,
|
||||
st->fifo.nb.gyro, st->timestamp.gyro);
|
||||
if (st->fifo.nb.gyro > 0) {
|
||||
ret = inv_icm42600_gyro_parse_fifo(st->indio_gyro);
|
||||
if (ret)
|
||||
@ -522,8 +523,8 @@ int inv_icm42600_buffer_fifo_parse(struct inv_icm42600_state *st)
|
||||
|
||||
/* handle accelerometer timestamp and FIFO data parsing */
|
||||
ts = iio_priv(st->indio_accel);
|
||||
inv_icm42600_timestamp_interrupt(ts, st->fifo.period, st->fifo.nb.total,
|
||||
st->fifo.nb.accel, st->timestamp.accel);
|
||||
inv_sensors_timestamp_interrupt(ts, st->fifo.period, st->fifo.nb.total,
|
||||
st->fifo.nb.accel, st->timestamp.accel);
|
||||
if (st->fifo.nb.accel > 0) {
|
||||
ret = inv_icm42600_accel_parse_fifo(st->indio_accel);
|
||||
if (ret)
|
||||
@ -536,7 +537,7 @@ int inv_icm42600_buffer_fifo_parse(struct inv_icm42600_state *st)
|
||||
int inv_icm42600_buffer_hwfifo_flush(struct inv_icm42600_state *st,
|
||||
unsigned int count)
|
||||
{
|
||||
struct inv_icm42600_timestamp *ts;
|
||||
struct inv_sensors_timestamp *ts;
|
||||
int64_t gyro_ts, accel_ts;
|
||||
int ret;
|
||||
|
||||
@ -552,9 +553,9 @@ int inv_icm42600_buffer_hwfifo_flush(struct inv_icm42600_state *st,
|
||||
|
||||
if (st->fifo.nb.gyro > 0) {
|
||||
ts = iio_priv(st->indio_gyro);
|
||||
inv_icm42600_timestamp_interrupt(ts, st->fifo.period,
|
||||
st->fifo.nb.total, st->fifo.nb.gyro,
|
||||
gyro_ts);
|
||||
inv_sensors_timestamp_interrupt(ts, st->fifo.period,
|
||||
st->fifo.nb.total, st->fifo.nb.gyro,
|
||||
gyro_ts);
|
||||
ret = inv_icm42600_gyro_parse_fifo(st->indio_gyro);
|
||||
if (ret)
|
||||
return ret;
|
||||
@ -562,9 +563,9 @@ int inv_icm42600_buffer_hwfifo_flush(struct inv_icm42600_state *st,
|
||||
|
||||
if (st->fifo.nb.accel > 0) {
|
||||
ts = iio_priv(st->indio_accel);
|
||||
inv_icm42600_timestamp_interrupt(ts, st->fifo.period,
|
||||
st->fifo.nb.total, st->fifo.nb.accel,
|
||||
accel_ts);
|
||||
inv_sensors_timestamp_interrupt(ts, st->fifo.period,
|
||||
st->fifo.nb.total, st->fifo.nb.accel,
|
||||
accel_ts);
|
||||
ret = inv_icm42600_accel_parse_fifo(st->indio_accel);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -15,11 +15,11 @@
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
|
||||
#include "inv_icm42600.h"
|
||||
#include "inv_icm42600_buffer.h"
|
||||
#include "inv_icm42600_timestamp.h"
|
||||
|
||||
static const struct regmap_range_cfg inv_icm42600_regmap_ranges[] = {
|
||||
{
|
||||
@ -516,6 +516,17 @@ static int inv_icm42600_irq_init(struct inv_icm42600_state *st, int irq,
|
||||
"inv_icm42600", st);
|
||||
}
|
||||
|
||||
static int inv_icm42600_timestamp_setup(struct inv_icm42600_state *st)
|
||||
{
|
||||
unsigned int val;
|
||||
|
||||
/* enable timestamp register */
|
||||
val = INV_ICM42600_TMST_CONFIG_TMST_TO_REGS_EN |
|
||||
INV_ICM42600_TMST_CONFIG_TMST_EN;
|
||||
return regmap_update_bits(st->map, INV_ICM42600_REG_TMST_CONFIG,
|
||||
INV_ICM42600_TMST_CONFIG_MASK, val);
|
||||
}
|
||||
|
||||
static int inv_icm42600_enable_regulator_vddio(struct inv_icm42600_state *st)
|
||||
{
|
||||
int ret;
|
||||
@ -788,3 +799,4 @@ EXPORT_NS_GPL_DEV_PM_OPS(inv_icm42600_pm_ops, IIO_ICM42600) = {
|
||||
MODULE_AUTHOR("InvenSense, Inc.");
|
||||
MODULE_DESCRIPTION("InvenSense ICM-426xx device driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_IMPORT_NS(IIO_INV_SENSORS_TIMESTAMP);
|
||||
|
@ -10,14 +10,15 @@
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/math64.h>
|
||||
#include <linux/iio/iio.h>
|
||||
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/common/inv_sensors_timestamp.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/kfifo_buf.h>
|
||||
|
||||
#include "inv_icm42600.h"
|
||||
#include "inv_icm42600_temp.h"
|
||||
#include "inv_icm42600_buffer.h"
|
||||
#include "inv_icm42600_timestamp.h"
|
||||
|
||||
#define INV_ICM42600_GYRO_CHAN(_modifier, _index, _ext_info) \
|
||||
{ \
|
||||
@ -98,7 +99,7 @@ static int inv_icm42600_gyro_update_scan_mode(struct iio_dev *indio_dev,
|
||||
const unsigned long *scan_mask)
|
||||
{
|
||||
struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev);
|
||||
struct inv_icm42600_timestamp *ts = iio_priv(indio_dev);
|
||||
struct inv_sensors_timestamp *ts = iio_priv(indio_dev);
|
||||
struct inv_icm42600_sensor_conf conf = INV_ICM42600_SENSOR_CONF_INIT;
|
||||
unsigned int fifo_en = 0;
|
||||
unsigned int sleep_gyro = 0;
|
||||
@ -126,7 +127,7 @@ static int inv_icm42600_gyro_update_scan_mode(struct iio_dev *indio_dev,
|
||||
}
|
||||
|
||||
/* update data FIFO write */
|
||||
inv_icm42600_timestamp_apply_odr(ts, 0, 0, 0);
|
||||
inv_sensors_timestamp_apply_odr(ts, 0, 0, 0);
|
||||
ret = inv_icm42600_buffer_set_fifo_en(st, fifo_en | st->fifo.en);
|
||||
if (ret)
|
||||
goto out_unlock;
|
||||
@ -323,7 +324,7 @@ static int inv_icm42600_gyro_write_odr(struct iio_dev *indio_dev,
|
||||
int val, int val2)
|
||||
{
|
||||
struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev);
|
||||
struct inv_icm42600_timestamp *ts = iio_priv(indio_dev);
|
||||
struct inv_sensors_timestamp *ts = iio_priv(indio_dev);
|
||||
struct device *dev = regmap_get_device(st->map);
|
||||
unsigned int idx;
|
||||
struct inv_icm42600_sensor_conf conf = INV_ICM42600_SENSOR_CONF_INIT;
|
||||
@ -342,8 +343,8 @@ static int inv_icm42600_gyro_write_odr(struct iio_dev *indio_dev,
|
||||
pm_runtime_get_sync(dev);
|
||||
mutex_lock(&st->lock);
|
||||
|
||||
ret = inv_icm42600_timestamp_update_odr(ts, inv_icm42600_odr_to_period(conf.odr),
|
||||
iio_buffer_enabled(indio_dev));
|
||||
ret = inv_sensors_timestamp_update_odr(ts, inv_icm42600_odr_to_period(conf.odr),
|
||||
iio_buffer_enabled(indio_dev));
|
||||
if (ret)
|
||||
goto out_unlock;
|
||||
|
||||
@ -718,7 +719,8 @@ struct iio_dev *inv_icm42600_gyro_init(struct inv_icm42600_state *st)
|
||||
{
|
||||
struct device *dev = regmap_get_device(st->map);
|
||||
const char *name;
|
||||
struct inv_icm42600_timestamp *ts;
|
||||
struct inv_sensors_timestamp_chip ts_chip;
|
||||
struct inv_sensors_timestamp *ts;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret;
|
||||
|
||||
@ -730,8 +732,15 @@ struct iio_dev *inv_icm42600_gyro_init(struct inv_icm42600_state *st)
|
||||
if (!indio_dev)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
/*
|
||||
* clock period is 32kHz (31250ns)
|
||||
* jitter is +/- 2% (20 per mille)
|
||||
*/
|
||||
ts_chip.clock_period = 31250;
|
||||
ts_chip.jitter = 20;
|
||||
ts_chip.init_period = inv_icm42600_odr_to_period(st->conf.accel.odr);
|
||||
ts = iio_priv(indio_dev);
|
||||
inv_icm42600_timestamp_init(ts, inv_icm42600_odr_to_period(st->conf.gyro.odr));
|
||||
inv_sensors_timestamp_init(ts, &ts_chip);
|
||||
|
||||
iio_device_set_drvdata(indio_dev, st);
|
||||
indio_dev->name = name;
|
||||
@ -757,7 +766,7 @@ struct iio_dev *inv_icm42600_gyro_init(struct inv_icm42600_state *st)
|
||||
int inv_icm42600_gyro_parse_fifo(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev);
|
||||
struct inv_icm42600_timestamp *ts = iio_priv(indio_dev);
|
||||
struct inv_sensors_timestamp *ts = iio_priv(indio_dev);
|
||||
ssize_t i, size;
|
||||
unsigned int no;
|
||||
const void *accel, *gyro, *timestamp;
|
||||
@ -780,15 +789,15 @@ int inv_icm42600_gyro_parse_fifo(struct iio_dev *indio_dev)
|
||||
|
||||
/* update odr */
|
||||
if (odr & INV_ICM42600_SENSOR_GYRO)
|
||||
inv_icm42600_timestamp_apply_odr(ts, st->fifo.period,
|
||||
st->fifo.nb.total, no);
|
||||
inv_sensors_timestamp_apply_odr(ts, st->fifo.period,
|
||||
st->fifo.nb.total, no);
|
||||
|
||||
/* buffer is copied to userspace, zeroing it to avoid any data leak */
|
||||
memset(&buffer, 0, sizeof(buffer));
|
||||
memcpy(&buffer.gyro, gyro, sizeof(buffer.gyro));
|
||||
/* convert 8 bits FIFO temperature in high resolution format */
|
||||
buffer.temp = temp ? (*temp * 64) : 0;
|
||||
ts_val = inv_icm42600_timestamp_pop(ts);
|
||||
ts_val = inv_sensors_timestamp_pop(ts);
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, &buffer, ts_val);
|
||||
}
|
||||
|
||||
|
@ -1,85 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright (C) 2020 Invensense, Inc.
|
||||
*/
|
||||
|
||||
#ifndef INV_ICM42600_TIMESTAMP_H_
|
||||
#define INV_ICM42600_TIMESTAMP_H_
|
||||
|
||||
#include <linux/kernel.h>
|
||||
|
||||
struct inv_icm42600_state;
|
||||
|
||||
/**
|
||||
* struct inv_icm42600_timestamp_interval - timestamps interval
|
||||
* @lo: interval lower bound
|
||||
* @up: interval upper bound
|
||||
*/
|
||||
struct inv_icm42600_timestamp_interval {
|
||||
int64_t lo;
|
||||
int64_t up;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct inv_icm42600_timestamp_acc - accumulator for computing an estimation
|
||||
* @val: current estimation of the value, the mean of all values
|
||||
* @idx: current index of the next free place in values table
|
||||
* @values: table of all measured values, use for computing the mean
|
||||
*/
|
||||
struct inv_icm42600_timestamp_acc {
|
||||
uint32_t val;
|
||||
size_t idx;
|
||||
uint32_t values[32];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct inv_icm42600_timestamp - timestamp management states
|
||||
* @it: interrupts interval timestamps
|
||||
* @timestamp: store last timestamp for computing next data timestamp
|
||||
* @mult: current internal period multiplier
|
||||
* @new_mult: new set internal period multiplier (not yet effective)
|
||||
* @period: measured current period of the sensor
|
||||
* @chip_period: accumulator for computing internal chip period
|
||||
*/
|
||||
struct inv_icm42600_timestamp {
|
||||
struct inv_icm42600_timestamp_interval it;
|
||||
int64_t timestamp;
|
||||
uint32_t mult;
|
||||
uint32_t new_mult;
|
||||
uint32_t period;
|
||||
struct inv_icm42600_timestamp_acc chip_period;
|
||||
};
|
||||
|
||||
void inv_icm42600_timestamp_init(struct inv_icm42600_timestamp *ts,
|
||||
uint32_t period);
|
||||
|
||||
int inv_icm42600_timestamp_setup(struct inv_icm42600_state *st);
|
||||
|
||||
int inv_icm42600_timestamp_update_odr(struct inv_icm42600_timestamp *ts,
|
||||
uint32_t period, bool fifo);
|
||||
|
||||
void inv_icm42600_timestamp_interrupt(struct inv_icm42600_timestamp *ts,
|
||||
uint32_t fifo_period, size_t fifo_nb,
|
||||
size_t sensor_nb, int64_t timestamp);
|
||||
|
||||
static inline int64_t
|
||||
inv_icm42600_timestamp_pop(struct inv_icm42600_timestamp *ts)
|
||||
{
|
||||
ts->timestamp += ts->period;
|
||||
return ts->timestamp;
|
||||
}
|
||||
|
||||
void inv_icm42600_timestamp_apply_odr(struct inv_icm42600_timestamp *ts,
|
||||
uint32_t fifo_period, size_t fifo_nb,
|
||||
unsigned int fifo_no);
|
||||
|
||||
static inline void
|
||||
inv_icm42600_timestamp_reset(struct inv_icm42600_timestamp *ts)
|
||||
{
|
||||
const struct inv_icm42600_timestamp_interval interval_init = {0LL, 0LL};
|
||||
|
||||
ts->it = interval_init;
|
||||
ts->timestamp = 0;
|
||||
}
|
||||
|
||||
#endif
|
@ -7,6 +7,7 @@ config INV_MPU6050_IIO
|
||||
tristate
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
select IIO_INV_SENSORS_TIMESTAMP
|
||||
|
||||
config INV_MPU6050_I2C
|
||||
tristate "Invensense MPU6050 devices (I2C)"
|
||||
|
@ -12,12 +12,15 @@
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include <linux/iio/common/inv_sensors_timestamp.h>
|
||||
#include <linux/iio/iio.h>
|
||||
|
||||
#include "inv_mpu_iio.h"
|
||||
#include "inv_mpu_magn.h"
|
||||
|
||||
@ -521,6 +524,7 @@ static int inv_mpu6050_init_config(struct iio_dev *indio_dev)
|
||||
int result;
|
||||
u8 d;
|
||||
struct inv_mpu6050_state *st = iio_priv(indio_dev);
|
||||
struct inv_sensors_timestamp_chip timestamp;
|
||||
|
||||
result = inv_mpu6050_set_gyro_fsr(st, st->chip_config.fsr);
|
||||
if (result)
|
||||
@ -544,12 +548,12 @@ static int inv_mpu6050_init_config(struct iio_dev *indio_dev)
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
/*
|
||||
* Internal chip period is 1ms (1kHz).
|
||||
* Let's use at the beginning the theorical value before measuring
|
||||
* with interrupt timestamps.
|
||||
*/
|
||||
st->chip_period = NSEC_PER_MSEC;
|
||||
/* clock jitter is +/- 2% */
|
||||
timestamp.clock_period = NSEC_PER_SEC / INV_MPU6050_INTERNAL_FREQ_HZ;
|
||||
timestamp.jitter = 20;
|
||||
timestamp.init_period =
|
||||
NSEC_PER_SEC / INV_MPU6050_DIVIDER_TO_FIFO_RATE(st->chip_config.divider);
|
||||
inv_sensors_timestamp_init(&st->timestamp, ×tamp);
|
||||
|
||||
/* magn chip init, noop if not present in the chip */
|
||||
result = inv_mpu_magn_probe(st);
|
||||
@ -936,6 +940,8 @@ inv_mpu6050_fifo_rate_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
int fifo_rate;
|
||||
u32 fifo_period;
|
||||
bool fifo_on;
|
||||
u8 d;
|
||||
int result;
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
@ -952,12 +958,21 @@ inv_mpu6050_fifo_rate_store(struct device *dev, struct device_attribute *attr,
|
||||
d = INV_MPU6050_FIFO_RATE_TO_DIVIDER(fifo_rate);
|
||||
/* compute back the fifo rate to handle truncation cases */
|
||||
fifo_rate = INV_MPU6050_DIVIDER_TO_FIFO_RATE(d);
|
||||
fifo_period = NSEC_PER_SEC / fifo_rate;
|
||||
|
||||
mutex_lock(&st->lock);
|
||||
if (d == st->chip_config.divider) {
|
||||
result = 0;
|
||||
goto fifo_rate_fail_unlock;
|
||||
}
|
||||
|
||||
fifo_on = st->chip_config.accl_fifo_enable ||
|
||||
st->chip_config.gyro_fifo_enable ||
|
||||
st->chip_config.magn_fifo_enable;
|
||||
result = inv_sensors_timestamp_update_odr(&st->timestamp, fifo_period, fifo_on);
|
||||
if (result)
|
||||
goto fifo_rate_fail_unlock;
|
||||
|
||||
result = pm_runtime_resume_and_get(pdev);
|
||||
if (result)
|
||||
goto fifo_rate_fail_unlock;
|
||||
@ -1330,6 +1345,9 @@ static int inv_check_and_setup_chip(struct inv_mpu6050_state *st)
|
||||
st->reg = hw_info[st->chip_type].reg;
|
||||
memcpy(&st->chip_config, hw_info[st->chip_type].config,
|
||||
sizeof(st->chip_config));
|
||||
st->data = devm_kzalloc(regmap_get_device(st->map), st->hw->fifo_size, GFP_KERNEL);
|
||||
if (st->data == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
/* check chip self-identification */
|
||||
result = regmap_read(st->map, INV_MPU6050_REG_WHOAMI, ®val);
|
||||
@ -1785,3 +1803,4 @@ EXPORT_NS_GPL_DEV_PM_OPS(inv_mpu_pmops, IIO_MPU6050) = {
|
||||
MODULE_AUTHOR("Invensense Corporation");
|
||||
MODULE_DESCRIPTION("Invensense device MPU6050 driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_IMPORT_NS(IIO_INV_SENSORS_TIMESTAMP);
|
||||
|
@ -9,15 +9,17 @@
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i2c-mux.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/platform_data/invensense_mpu6050.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/common/inv_sensors_timestamp.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/kfifo_buf.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/platform_data/invensense_mpu6050.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
/**
|
||||
* struct inv_mpu6050_reg_map - Notable registers.
|
||||
@ -170,16 +172,14 @@ struct inv_mpu6050_hw {
|
||||
* @map regmap pointer.
|
||||
* @irq interrupt number.
|
||||
* @irq_mask the int_pin_cfg mask to configure interrupt type.
|
||||
* @chip_period: chip internal period estimation (~1kHz).
|
||||
* @it_timestamp: timestamp from previous interrupt.
|
||||
* @data_timestamp: timestamp for next data sample.
|
||||
* @timestamp: timestamping module
|
||||
* @vdd_supply: VDD voltage regulator for the chip.
|
||||
* @vddio_supply I/O voltage regulator for the chip.
|
||||
* @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
|
||||
* @data: dma safe buffer used for bulk reads.
|
||||
* @data: read buffer used for bulk reads.
|
||||
*/
|
||||
struct inv_mpu6050_state {
|
||||
struct mutex lock;
|
||||
@ -196,16 +196,14 @@ struct inv_mpu6050_state {
|
||||
int irq;
|
||||
u8 irq_mask;
|
||||
unsigned skip_samples;
|
||||
s64 chip_period;
|
||||
s64 it_timestamp;
|
||||
s64 data_timestamp;
|
||||
struct inv_sensors_timestamp timestamp;
|
||||
struct regulator *vdd_supply;
|
||||
struct regulator *vddio_supply;
|
||||
bool magn_disabled;
|
||||
s32 magn_raw_to_gauss[3];
|
||||
struct iio_mount_matrix magn_orient;
|
||||
unsigned int suspended_sensors;
|
||||
u8 data[INV_MPU6050_OUTPUT_DATA_SIZE] __aligned(IIO_DMA_MINALIGN);
|
||||
u8 *data;
|
||||
};
|
||||
|
||||
/*register and associated bit definition*/
|
||||
|
@ -13,82 +13,11 @@
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/math64.h>
|
||||
|
||||
#include <linux/iio/common/inv_sensors_timestamp.h>
|
||||
|
||||
#include "inv_mpu_iio.h"
|
||||
|
||||
/**
|
||||
* inv_mpu6050_update_period() - Update chip internal period estimation
|
||||
*
|
||||
* @st: driver state
|
||||
* @timestamp: the interrupt timestamp
|
||||
* @nb: number of data set in the fifo
|
||||
*
|
||||
* This function uses interrupt timestamps to estimate the chip period and
|
||||
* to choose the data timestamp to come.
|
||||
*/
|
||||
static void inv_mpu6050_update_period(struct inv_mpu6050_state *st,
|
||||
s64 timestamp, size_t nb)
|
||||
{
|
||||
/* Period boundaries for accepting timestamp */
|
||||
const s64 period_min =
|
||||
(NSEC_PER_MSEC * (100 - INV_MPU6050_TS_PERIOD_JITTER)) / 100;
|
||||
const s64 period_max =
|
||||
(NSEC_PER_MSEC * (100 + INV_MPU6050_TS_PERIOD_JITTER)) / 100;
|
||||
const s32 divider = INV_MPU6050_FREQ_DIVIDER(st);
|
||||
s64 delta, interval;
|
||||
bool use_it_timestamp = false;
|
||||
|
||||
if (st->it_timestamp == 0) {
|
||||
/* not initialized, forced to use it_timestamp */
|
||||
use_it_timestamp = true;
|
||||
} else if (nb == 1) {
|
||||
/*
|
||||
* Validate the use of it timestamp by checking if interrupt
|
||||
* has been delayed.
|
||||
* nb > 1 means interrupt was delayed for more than 1 sample,
|
||||
* so it's obviously not good.
|
||||
* Compute the chip period between 2 interrupts for validating.
|
||||
*/
|
||||
delta = div_s64(timestamp - st->it_timestamp, divider);
|
||||
if (delta > period_min && delta < period_max) {
|
||||
/* update chip period and use it timestamp */
|
||||
st->chip_period = (st->chip_period + delta) / 2;
|
||||
use_it_timestamp = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (use_it_timestamp) {
|
||||
/*
|
||||
* Manage case of multiple samples in the fifo (nb > 1):
|
||||
* compute timestamp corresponding to the first sample using
|
||||
* estimated chip period.
|
||||
*/
|
||||
interval = (nb - 1) * st->chip_period * divider;
|
||||
st->data_timestamp = timestamp - interval;
|
||||
}
|
||||
|
||||
/* save it timestamp */
|
||||
st->it_timestamp = timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* inv_mpu6050_get_timestamp() - Return the current data timestamp
|
||||
*
|
||||
* @st: driver state
|
||||
* @return: current data timestamp
|
||||
*
|
||||
* This function returns the current data timestamp and prepares for next one.
|
||||
*/
|
||||
static s64 inv_mpu6050_get_timestamp(struct inv_mpu6050_state *st)
|
||||
{
|
||||
s64 ts;
|
||||
|
||||
/* return current data timestamp and increment */
|
||||
ts = st->data_timestamp;
|
||||
st->data_timestamp += st->chip_period * INV_MPU6050_FREQ_DIVIDER(st);
|
||||
|
||||
return ts;
|
||||
}
|
||||
|
||||
static int inv_reset_fifo(struct iio_dev *indio_dev)
|
||||
{
|
||||
int result;
|
||||
@ -121,7 +50,9 @@ irqreturn_t inv_mpu6050_read_fifo(int irq, void *p)
|
||||
size_t bytes_per_datum;
|
||||
int result;
|
||||
u16 fifo_count;
|
||||
u32 fifo_period;
|
||||
s64 timestamp;
|
||||
u8 data[INV_MPU6050_OUTPUT_DATA_SIZE];
|
||||
int int_status;
|
||||
size_t i, nb;
|
||||
|
||||
@ -175,21 +106,30 @@ irqreturn_t inv_mpu6050_read_fifo(int irq, void *p)
|
||||
goto flush_fifo;
|
||||
}
|
||||
|
||||
/* compute and process all complete datum */
|
||||
/* compute and process only all complete datum */
|
||||
nb = fifo_count / bytes_per_datum;
|
||||
inv_mpu6050_update_period(st, pf->timestamp, nb);
|
||||
fifo_count = nb * bytes_per_datum;
|
||||
/* Each FIFO data contains all sensors, so same number for FIFO and sensor data */
|
||||
fifo_period = NSEC_PER_SEC / INV_MPU6050_DIVIDER_TO_FIFO_RATE(st->chip_config.divider);
|
||||
inv_sensors_timestamp_interrupt(&st->timestamp, fifo_period, nb, nb, pf->timestamp);
|
||||
inv_sensors_timestamp_apply_odr(&st->timestamp, fifo_period, nb, 0);
|
||||
|
||||
/* clear internal data buffer for avoiding kernel data leak */
|
||||
memset(data, 0, sizeof(data));
|
||||
|
||||
/* read all data once and process every samples */
|
||||
result = regmap_noinc_read(st->map, st->reg->fifo_r_w, st->data, fifo_count);
|
||||
if (result)
|
||||
goto flush_fifo;
|
||||
for (i = 0; i < nb; ++i) {
|
||||
result = regmap_noinc_read(st->map, st->reg->fifo_r_w,
|
||||
st->data, bytes_per_datum);
|
||||
if (result)
|
||||
goto flush_fifo;
|
||||
/* skip first samples if needed */
|
||||
if (st->skip_samples) {
|
||||
st->skip_samples--;
|
||||
continue;
|
||||
}
|
||||
timestamp = inv_mpu6050_get_timestamp(st);
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, st->data, timestamp);
|
||||
memcpy(data, &st->data[i * bytes_per_datum], bytes_per_datum);
|
||||
timestamp = inv_sensors_timestamp_pop(&st->timestamp);
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, data, timestamp);
|
||||
}
|
||||
|
||||
end_session:
|
||||
|
@ -4,6 +4,9 @@
|
||||
*/
|
||||
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include <linux/iio/common/inv_sensors_timestamp.h>
|
||||
|
||||
#include "inv_mpu_iio.h"
|
||||
|
||||
static unsigned int inv_scan_query_mpu6050(struct iio_dev *indio_dev)
|
||||
@ -106,7 +109,8 @@ int inv_mpu6050_prepare_fifo(struct inv_mpu6050_state *st, bool enable)
|
||||
int ret;
|
||||
|
||||
if (enable) {
|
||||
st->it_timestamp = 0;
|
||||
/* reset timestamping */
|
||||
inv_sensors_timestamp_reset(&st->timestamp);
|
||||
/* reset FIFO */
|
||||
d = st->chip_config.user_ctrl | INV_MPU6050_BIT_FIFO_RST;
|
||||
ret = regmap_write(st->map, st->reg->user_ctrl, d);
|
||||
|
@ -1,5 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/* The industrial I/O core
|
||||
/*
|
||||
* The industrial I/O core
|
||||
*
|
||||
* Copyright (c) 2008 Jonathan Cameron
|
||||
*
|
||||
@ -183,7 +184,9 @@ static const char * const iio_chan_info_postfix[] = {
|
||||
* @indio_dev: Device structure whose ID is being queried
|
||||
*
|
||||
* The IIO device ID is a unique index used for example for the naming
|
||||
* of the character device /dev/iio\:device[ID]
|
||||
* of the character device /dev/iio\:device[ID].
|
||||
*
|
||||
* Returns: Unique ID for the device.
|
||||
*/
|
||||
int iio_device_id(struct iio_dev *indio_dev)
|
||||
{
|
||||
@ -196,14 +199,16 @@ EXPORT_SYMBOL_GPL(iio_device_id);
|
||||
/**
|
||||
* iio_buffer_enabled() - helper function to test if the buffer is enabled
|
||||
* @indio_dev: IIO device structure for device
|
||||
*
|
||||
* Returns: True, if the buffer is enabled.
|
||||
*/
|
||||
bool iio_buffer_enabled(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev);
|
||||
|
||||
return iio_dev_opaque->currentmode
|
||||
& (INDIO_BUFFER_TRIGGERED | INDIO_BUFFER_HARDWARE |
|
||||
INDIO_BUFFER_SOFTWARE);
|
||||
return iio_dev_opaque->currentmode &
|
||||
(INDIO_BUFFER_HARDWARE | INDIO_BUFFER_SOFTWARE |
|
||||
INDIO_BUFFER_TRIGGERED);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iio_buffer_enabled);
|
||||
|
||||
@ -225,6 +230,9 @@ EXPORT_SYMBOL_GPL(iio_get_debugfs_dentry);
|
||||
* iio_find_channel_from_si() - get channel from its scan index
|
||||
* @indio_dev: device
|
||||
* @si: scan index to match
|
||||
*
|
||||
* Returns:
|
||||
* Constant pointer to iio_chan_spec, if scan index matches, NULL on failure.
|
||||
*/
|
||||
const struct iio_chan_spec
|
||||
*iio_find_channel_from_si(struct iio_dev *indio_dev, int si)
|
||||
@ -249,7 +257,9 @@ EXPORT_SYMBOL(iio_read_const_attr);
|
||||
/**
|
||||
* iio_device_set_clock() - Set current timestamping clock for the device
|
||||
* @indio_dev: IIO device structure containing the device
|
||||
* @clock_id: timestamping clock posix identifier to set.
|
||||
* @clock_id: timestamping clock POSIX identifier to set.
|
||||
*
|
||||
* Returns: 0 on success, or a negative error code.
|
||||
*/
|
||||
int iio_device_set_clock(struct iio_dev *indio_dev, clockid_t clock_id)
|
||||
{
|
||||
@ -275,6 +285,8 @@ EXPORT_SYMBOL(iio_device_set_clock);
|
||||
/**
|
||||
* iio_device_get_clock() - Retrieve current timestamping clock for the device
|
||||
* @indio_dev: IIO device structure containing the device
|
||||
*
|
||||
* Returns: Clock ID of the current timestamping clock for the device.
|
||||
*/
|
||||
clockid_t iio_device_get_clock(const struct iio_dev *indio_dev)
|
||||
{
|
||||
@ -287,6 +299,8 @@ EXPORT_SYMBOL(iio_device_get_clock);
|
||||
/**
|
||||
* iio_get_time_ns() - utility function to get a time stamp for events etc
|
||||
* @indio_dev: device
|
||||
*
|
||||
* Returns: Timestamp of the event in nanoseconds.
|
||||
*/
|
||||
s64 iio_get_time_ns(const struct iio_dev *indio_dev)
|
||||
{
|
||||
@ -372,8 +386,8 @@ static ssize_t iio_debugfs_read_reg(struct file *file, char __user *userbuf,
|
||||
}
|
||||
|
||||
iio_dev_opaque->read_buf_len = snprintf(iio_dev_opaque->read_buf,
|
||||
sizeof(iio_dev_opaque->read_buf),
|
||||
"0x%X\n", val);
|
||||
sizeof(iio_dev_opaque->read_buf),
|
||||
"0x%X\n", val);
|
||||
|
||||
return simple_read_from_buffer(userbuf, count, ppos,
|
||||
iio_dev_opaque->read_buf,
|
||||
@ -389,7 +403,7 @@ static ssize_t iio_debugfs_write_reg(struct file *file,
|
||||
char buf[80];
|
||||
int ret;
|
||||
|
||||
count = min_t(size_t, count, (sizeof(buf)-1));
|
||||
count = min(count, sizeof(buf) - 1);
|
||||
if (copy_from_user(buf, userbuf, count))
|
||||
return -EFAULT;
|
||||
|
||||
@ -476,8 +490,7 @@ static ssize_t iio_read_channel_ext_info(struct device *dev,
|
||||
|
||||
static ssize_t iio_write_channel_ext_info(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf,
|
||||
size_t len)
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
|
||||
@ -524,7 +537,7 @@ ssize_t iio_enum_read(struct iio_dev *indio_dev,
|
||||
i = e->get(indio_dev, chan);
|
||||
if (i < 0)
|
||||
return i;
|
||||
else if (i >= e->num_items || !e->items[i])
|
||||
if (i >= e->num_items || !e->items[i])
|
||||
return -EINVAL;
|
||||
|
||||
return sysfs_emit(buf, "%s\n", e->items[i]);
|
||||
@ -569,9 +582,9 @@ static int iio_setup_mount_idmatrix(const struct device *dev,
|
||||
ssize_t iio_show_mount_matrix(struct iio_dev *indio_dev, uintptr_t priv,
|
||||
const struct iio_chan_spec *chan, char *buf)
|
||||
{
|
||||
const struct iio_mount_matrix *mtx = ((iio_get_mount_matrix_t *)
|
||||
priv)(indio_dev, chan);
|
||||
const struct iio_mount_matrix *mtx;
|
||||
|
||||
mtx = ((iio_get_mount_matrix_t *)priv)(indio_dev, chan);
|
||||
if (IS_ERR(mtx))
|
||||
return PTR_ERR(mtx);
|
||||
|
||||
@ -594,7 +607,7 @@ EXPORT_SYMBOL_GPL(iio_show_mount_matrix);
|
||||
* If device is assigned no mounting matrix property, a default 3x3 identity
|
||||
* matrix will be filled in.
|
||||
*
|
||||
* Return: 0 if success, or a negative error code on failure.
|
||||
* Returns: 0 if success, or a negative error code on failure.
|
||||
*/
|
||||
int iio_read_mount_matrix(struct device *dev, struct iio_mount_matrix *matrix)
|
||||
{
|
||||
@ -692,9 +705,9 @@ static ssize_t __iio_format_value(char *buf, size_t offset, unsigned int type,
|
||||
* @vals: Pointer to the values, exact meaning depends on the
|
||||
* type parameter.
|
||||
*
|
||||
* Return: 0 by default, a negative number on failure or the
|
||||
* total number of characters written for a type that belongs
|
||||
* to the IIO_VAL_* constant.
|
||||
* Returns:
|
||||
* 0 by default, a negative number on failure or the total number of characters
|
||||
* written for a type that belongs to the IIO_VAL_* constant.
|
||||
*/
|
||||
ssize_t iio_format_value(char *buf, unsigned int type, int size, int *vals)
|
||||
{
|
||||
@ -847,8 +860,8 @@ static ssize_t iio_read_channel_info_avail(struct device *dev,
|
||||
* @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.
|
||||
* Returns:
|
||||
* 0 on success, or a negative error code if the string could not be parsed.
|
||||
*/
|
||||
static int __iio_str_to_fixpoint(const char *str, int fract_mult,
|
||||
int *integer, int *fract, bool scale_db)
|
||||
@ -917,8 +930,8 @@ static int __iio_str_to_fixpoint(const char *str, int fract_mult,
|
||||
* @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.
|
||||
* 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)
|
||||
@ -1009,14 +1022,12 @@ int __iio_device_attr_init(struct device_attribute *dev_attr,
|
||||
if (chan->modified && (shared_by == IIO_SEPARATE)) {
|
||||
if (chan->extend_name)
|
||||
full_postfix = kasprintf(GFP_KERNEL, "%s_%s_%s",
|
||||
iio_modifier_names[chan
|
||||
->channel2],
|
||||
iio_modifier_names[chan->channel2],
|
||||
chan->extend_name,
|
||||
postfix);
|
||||
else
|
||||
full_postfix = kasprintf(GFP_KERNEL, "%s_%s",
|
||||
iio_modifier_names[chan
|
||||
->channel2],
|
||||
iio_modifier_names[chan->channel2],
|
||||
postfix);
|
||||
} else {
|
||||
if (chan->extend_name == NULL || shared_by != IIO_SEPARATE)
|
||||
@ -1217,7 +1228,7 @@ static int iio_device_add_info_mask_type(struct iio_dev *indio_dev,
|
||||
&iio_dev_opaque->channel_attr_list);
|
||||
if ((ret == -EBUSY) && (shared_by != IIO_SEPARATE))
|
||||
continue;
|
||||
else if (ret < 0)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
attrcount++;
|
||||
}
|
||||
@ -1255,7 +1266,7 @@ static int iio_device_add_info_mask_type_avail(struct iio_dev *indio_dev,
|
||||
kfree(avail_postfix);
|
||||
if ((ret == -EBUSY) && (shared_by != IIO_SEPARATE))
|
||||
continue;
|
||||
else if (ret < 0)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
attrcount++;
|
||||
}
|
||||
@ -1400,50 +1411,42 @@ static ssize_t label_show(struct device *dev, struct device_attribute *attr,
|
||||
|
||||
static DEVICE_ATTR_RO(label);
|
||||
|
||||
static const char * const clock_names[] = {
|
||||
[CLOCK_REALTIME] = "realtime",
|
||||
[CLOCK_MONOTONIC] = "monotonic",
|
||||
[CLOCK_PROCESS_CPUTIME_ID] = "process_cputime_id",
|
||||
[CLOCK_THREAD_CPUTIME_ID] = "thread_cputime_id",
|
||||
[CLOCK_MONOTONIC_RAW] = "monotonic_raw",
|
||||
[CLOCK_REALTIME_COARSE] = "realtime_coarse",
|
||||
[CLOCK_MONOTONIC_COARSE] = "monotonic_coarse",
|
||||
[CLOCK_BOOTTIME] = "boottime",
|
||||
[CLOCK_REALTIME_ALARM] = "realtime_alarm",
|
||||
[CLOCK_BOOTTIME_ALARM] = "boottime_alarm",
|
||||
[CLOCK_SGI_CYCLE] = "sgi_cycle",
|
||||
[CLOCK_TAI] = "tai",
|
||||
};
|
||||
|
||||
static ssize_t current_timestamp_clock_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
const struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
const clockid_t clk = iio_device_get_clock(indio_dev);
|
||||
const char *name;
|
||||
ssize_t sz;
|
||||
|
||||
switch (clk) {
|
||||
case CLOCK_REALTIME:
|
||||
name = "realtime\n";
|
||||
sz = sizeof("realtime\n");
|
||||
break;
|
||||
case CLOCK_MONOTONIC:
|
||||
name = "monotonic\n";
|
||||
sz = sizeof("monotonic\n");
|
||||
break;
|
||||
case CLOCK_MONOTONIC_RAW:
|
||||
name = "monotonic_raw\n";
|
||||
sz = sizeof("monotonic_raw\n");
|
||||
break;
|
||||
case CLOCK_REALTIME_COARSE:
|
||||
name = "realtime_coarse\n";
|
||||
sz = sizeof("realtime_coarse\n");
|
||||
break;
|
||||
case CLOCK_MONOTONIC_COARSE:
|
||||
name = "monotonic_coarse\n";
|
||||
sz = sizeof("monotonic_coarse\n");
|
||||
break;
|
||||
case CLOCK_BOOTTIME:
|
||||
name = "boottime\n";
|
||||
sz = sizeof("boottime\n");
|
||||
break;
|
||||
case CLOCK_TAI:
|
||||
name = "tai\n";
|
||||
sz = sizeof("tai\n");
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
|
||||
memcpy(buf, name, sz);
|
||||
return sz;
|
||||
return sysfs_emit(buf, "%s\n", clock_names[clk]);
|
||||
}
|
||||
|
||||
static ssize_t current_timestamp_clock_store(struct device *dev,
|
||||
@ -1453,22 +1456,23 @@ static ssize_t current_timestamp_clock_store(struct device *dev,
|
||||
clockid_t clk;
|
||||
int ret;
|
||||
|
||||
if (sysfs_streq(buf, "realtime"))
|
||||
clk = CLOCK_REALTIME;
|
||||
else if (sysfs_streq(buf, "monotonic"))
|
||||
clk = CLOCK_MONOTONIC;
|
||||
else if (sysfs_streq(buf, "monotonic_raw"))
|
||||
clk = CLOCK_MONOTONIC_RAW;
|
||||
else if (sysfs_streq(buf, "realtime_coarse"))
|
||||
clk = CLOCK_REALTIME_COARSE;
|
||||
else if (sysfs_streq(buf, "monotonic_coarse"))
|
||||
clk = CLOCK_MONOTONIC_COARSE;
|
||||
else if (sysfs_streq(buf, "boottime"))
|
||||
clk = CLOCK_BOOTTIME;
|
||||
else if (sysfs_streq(buf, "tai"))
|
||||
clk = CLOCK_TAI;
|
||||
else
|
||||
ret = sysfs_match_string(clock_names, buf);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
clk = ret;
|
||||
|
||||
switch (clk) {
|
||||
case CLOCK_REALTIME:
|
||||
case CLOCK_MONOTONIC:
|
||||
case CLOCK_MONOTONIC_RAW:
|
||||
case CLOCK_REALTIME_COARSE:
|
||||
case CLOCK_MONOTONIC_COARSE:
|
||||
case CLOCK_BOOTTIME:
|
||||
case CLOCK_TAI:
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = iio_device_set_clock(dev_to_iio_dev(dev), clk);
|
||||
if (ret)
|
||||
@ -1484,7 +1488,7 @@ int iio_device_register_sysfs_group(struct iio_dev *indio_dev,
|
||||
const struct attribute_group **new, **old = iio_dev_opaque->groups;
|
||||
unsigned int cnt = iio_dev_opaque->groupcounter;
|
||||
|
||||
new = krealloc(old, sizeof(*new) * (cnt + 2), GFP_KERNEL);
|
||||
new = krealloc_array(old, cnt + 2, sizeof(*new), GFP_KERNEL);
|
||||
if (!new)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -1621,7 +1625,10 @@ const struct device_type iio_device_type = {
|
||||
* iio_device_alloc() - allocate an iio_dev from a driver
|
||||
* @parent: Parent device.
|
||||
* @sizeof_priv: Space to allocate for private structure.
|
||||
**/
|
||||
*
|
||||
* Returns:
|
||||
* Pointer to allocated iio_dev on success, NULL on failure.
|
||||
*/
|
||||
struct iio_dev *iio_device_alloc(struct device *parent, int sizeof_priv)
|
||||
{
|
||||
struct iio_dev_opaque *iio_dev_opaque;
|
||||
@ -1677,7 +1684,7 @@ EXPORT_SYMBOL(iio_device_alloc);
|
||||
/**
|
||||
* iio_device_free() - free an iio_dev from a driver
|
||||
* @dev: the iio_dev associated with the device
|
||||
**/
|
||||
*/
|
||||
void iio_device_free(struct iio_dev *dev)
|
||||
{
|
||||
if (dev)
|
||||
@ -1698,7 +1705,7 @@ static void devm_iio_device_release(void *iio_dev)
|
||||
* Managed iio_device_alloc. iio_dev allocated with this function is
|
||||
* automatically freed on driver detach.
|
||||
*
|
||||
* RETURNS:
|
||||
* Returns:
|
||||
* Pointer to allocated iio_dev on success, NULL on failure.
|
||||
*/
|
||||
struct iio_dev *devm_iio_device_alloc(struct device *parent, int sizeof_priv)
|
||||
@ -1725,8 +1732,8 @@ EXPORT_SYMBOL_GPL(devm_iio_device_alloc);
|
||||
* @filp: File structure for iio device used to keep and later access
|
||||
* private data
|
||||
*
|
||||
* Return: 0 on success or -EBUSY if the device is already opened
|
||||
**/
|
||||
* Returns: 0 on success or -EBUSY if the device is already opened
|
||||
*/
|
||||
static int iio_chrdev_open(struct inode *inode, struct file *filp)
|
||||
{
|
||||
struct iio_dev_opaque *iio_dev_opaque =
|
||||
@ -1759,7 +1766,7 @@ static int iio_chrdev_open(struct inode *inode, struct file *filp)
|
||||
* @inode: Inode structure pointer for the char device
|
||||
* @filp: File structure pointer for the char device
|
||||
*
|
||||
* Return: 0 for successful release
|
||||
* Returns: 0 for successful release.
|
||||
*/
|
||||
static int iio_chrdev_release(struct inode *inode, struct file *filp)
|
||||
{
|
||||
@ -1798,7 +1805,7 @@ static long iio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||||
|
||||
mutex_lock(&iio_dev_opaque->info_exist_lock);
|
||||
|
||||
/**
|
||||
/*
|
||||
* The NULL check here is required to prevent crashing when a device
|
||||
* is being removed while userspace would still have open file handles
|
||||
* to try to access this device.
|
||||
@ -1975,7 +1982,7 @@ EXPORT_SYMBOL(__iio_device_register);
|
||||
/**
|
||||
* iio_device_unregister() - unregister a device from the IIO subsystem
|
||||
* @indio_dev: Device structure representing the device.
|
||||
**/
|
||||
*/
|
||||
void iio_device_unregister(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev);
|
||||
@ -2026,7 +2033,7 @@ EXPORT_SYMBOL_GPL(__devm_iio_device_register);
|
||||
*
|
||||
* Use with iio_device_release_direct_mode()
|
||||
*
|
||||
* Returns: 0 on success, -EBUSY on failure
|
||||
* Returns: 0 on success, -EBUSY on failure.
|
||||
*/
|
||||
int iio_device_claim_direct_mode(struct iio_dev *indio_dev)
|
||||
{
|
||||
|
@ -252,6 +252,8 @@ static const char * const iio_ev_info_text[] = {
|
||||
[IIO_EV_INFO_TIMEOUT] = "timeout",
|
||||
[IIO_EV_INFO_RESET_TIMEOUT] = "reset_timeout",
|
||||
[IIO_EV_INFO_TAP2_MIN_DELAY] = "tap2_min_delay",
|
||||
[IIO_EV_INFO_RUNNING_PERIOD] = "runningperiod",
|
||||
[IIO_EV_INFO_RUNNING_COUNT] = "runningcount",
|
||||
};
|
||||
|
||||
static enum iio_event_direction iio_ev_attr_dir(struct iio_dev_attr *attr)
|
||||
|
@ -313,7 +313,7 @@ int iio_trigger_attach_poll_func(struct iio_trigger *trig,
|
||||
/* Enable trigger in driver */
|
||||
if (trig->ops && trig->ops->set_trigger_state && notinuse) {
|
||||
ret = trig->ops->set_trigger_state(trig, true);
|
||||
if (ret < 0)
|
||||
if (ret)
|
||||
goto out_free_irq;
|
||||
}
|
||||
|
||||
|
@ -227,7 +227,7 @@ static int cm3605_probe(struct platform_device *pdev)
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
ret = dev_err_probe(dev, irq, "failed to get irq\n");
|
||||
ret = irq;
|
||||
goto out_disable_aset;
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,8 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* BU27008 ROHM Colour Sensor
|
||||
* ROHM Colour Sensor driver for
|
||||
* - BU27008 RGBC sensor
|
||||
* - BU27010 RGBC + Flickering sensor
|
||||
*
|
||||
* Copyright (c) 2023, ROHM Semiconductor.
|
||||
*/
|
||||
@ -22,6 +24,25 @@
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
|
||||
/*
|
||||
* A word about register address and mask definitions.
|
||||
*
|
||||
* At a quick glance to the data-sheet register tables, the BU27010 has all the
|
||||
* registers that the BU27008 has. On top of that the BU27010 adds couple of new
|
||||
* ones.
|
||||
*
|
||||
* So, all definitions BU27008_REG_* are there also for BU27010 but none of the
|
||||
* BU27010_REG_* are present on BU27008. This makes sense as BU27010 just adds
|
||||
* some features (Flicker FIFO, more power control) on top of the BU27008.
|
||||
*
|
||||
* Unfortunately, some of the wheel has been re-invented. Even though the names
|
||||
* of the registers have stayed the same, pretty much all of the functionality
|
||||
* provided by the registers has changed place. Contents of all MODE_CONTROL
|
||||
* registers on BU27008 and BU27010 are different.
|
||||
*
|
||||
* Chip-specific mapping from register addresses/bits to functionality is done
|
||||
* in bu27_chip_data structures.
|
||||
*/
|
||||
#define BU27008_REG_SYSTEM_CONTROL 0x40
|
||||
#define BU27008_MASK_SW_RESET BIT(7)
|
||||
#define BU27008_MASK_PART_ID GENMASK(5, 0)
|
||||
@ -52,6 +73,56 @@
|
||||
#define BU27008_REG_MANUFACTURER_ID 0x92
|
||||
#define BU27008_REG_MAX BU27008_REG_MANUFACTURER_ID
|
||||
|
||||
/* BU27010 specific definitions */
|
||||
|
||||
#define BU27010_MASK_SW_RESET BIT(7)
|
||||
#define BU27010_ID 0x1b
|
||||
#define BU27010_REG_POWER 0x3e
|
||||
#define BU27010_MASK_POWER BIT(0)
|
||||
|
||||
#define BU27010_REG_RESET 0x3f
|
||||
#define BU27010_MASK_RESET BIT(0)
|
||||
#define BU27010_RESET_RELEASE BU27010_MASK_RESET
|
||||
|
||||
#define BU27010_MASK_MEAS_EN BIT(1)
|
||||
|
||||
#define BU27010_MASK_CHAN_SEL GENMASK(7, 6)
|
||||
#define BU27010_MASK_MEAS_MODE GENMASK(5, 4)
|
||||
#define BU27010_MASK_RGBC_GAIN GENMASK(3, 0)
|
||||
|
||||
#define BU27010_MASK_DATA3_GAIN GENMASK(7, 6)
|
||||
#define BU27010_MASK_DATA2_GAIN GENMASK(5, 4)
|
||||
#define BU27010_MASK_DATA1_GAIN GENMASK(3, 2)
|
||||
#define BU27010_MASK_DATA0_GAIN GENMASK(1, 0)
|
||||
|
||||
#define BU27010_MASK_FLC_MODE BIT(7)
|
||||
#define BU27010_MASK_FLC_GAIN GENMASK(4, 0)
|
||||
|
||||
#define BU27010_REG_MODE_CONTROL4 0x44
|
||||
/* If flicker is ever to be supported the IRQ must be handled as a field */
|
||||
#define BU27010_IRQ_DIS_ALL GENMASK(1, 0)
|
||||
#define BU27010_DRDY_EN BIT(0)
|
||||
#define BU27010_MASK_INT_SEL GENMASK(1, 0)
|
||||
|
||||
#define BU27010_REG_MODE_CONTROL5 0x45
|
||||
#define BU27010_MASK_RGB_VALID BIT(7)
|
||||
#define BU27010_MASK_FLC_VALID BIT(6)
|
||||
#define BU27010_MASK_WAIT_EN BIT(3)
|
||||
#define BU27010_MASK_FIFO_EN BIT(2)
|
||||
#define BU27010_MASK_RGB_EN BIT(1)
|
||||
#define BU27010_MASK_FLC_EN BIT(0)
|
||||
|
||||
#define BU27010_REG_DATA_FLICKER_LO 0x56
|
||||
#define BU27010_MASK_DATA_FLICKER_HI GENMASK(2, 0)
|
||||
#define BU27010_REG_FLICKER_COUNT 0x5a
|
||||
#define BU27010_REG_FIFO_LEVEL_LO 0x5b
|
||||
#define BU27010_MASK_FIFO_LEVEL_HI BIT(0)
|
||||
#define BU27010_REG_FIFO_DATA_LO 0x5d
|
||||
#define BU27010_REG_FIFO_DATA_HI 0x5e
|
||||
#define BU27010_MASK_FIFO_DATA_HI GENMASK(2, 0)
|
||||
#define BU27010_REG_MANUFACTURER_ID 0x92
|
||||
#define BU27010_REG_MAX BU27010_REG_MANUFACTURER_ID
|
||||
|
||||
/**
|
||||
* enum bu27008_chan_type - BU27008 channel types
|
||||
* @BU27008_RED: Red channel. Always via data0.
|
||||
@ -117,6 +188,17 @@ static const unsigned long bu27008_scan_masks[] = {
|
||||
*/
|
||||
#define BU27008_SCALE_1X 16
|
||||
|
||||
/*
|
||||
* On BU27010 available scales with gain 1x - 4096x,
|
||||
* timings 55, 100, 200, 400 mS. Time impacts to gain: 1x, 2x, 4x, 8x.
|
||||
*
|
||||
* => Max total gain is HWGAIN * gain by integration time (8 * 4096)
|
||||
*
|
||||
* Using NANO precision for scale we must use scale 64x corresponding gain 1x
|
||||
* to avoid precision loss.
|
||||
*/
|
||||
#define BU27010_SCALE_1X 64
|
||||
|
||||
/* See the data sheet for the "Gain Setting" table */
|
||||
#define BU27008_GSEL_1X 0x00
|
||||
#define BU27008_GSEL_4X 0x08
|
||||
@ -152,10 +234,44 @@ static const struct iio_gain_sel_pair bu27008_gains_ir[] = {
|
||||
GAIN_SCALE_GAIN(1024, BU27008_GSEL_1024X),
|
||||
};
|
||||
|
||||
#define BU27010_GSEL_1X 0x00 /* 000000 */
|
||||
#define BU27010_GSEL_4X 0x08 /* 001000 */
|
||||
#define BU27010_GSEL_16X 0x09 /* 001001 */
|
||||
#define BU27010_GSEL_64X 0x0e /* 001110 */
|
||||
#define BU27010_GSEL_256X 0x1e /* 011110 */
|
||||
#define BU27010_GSEL_1024X 0x2e /* 101110 */
|
||||
#define BU27010_GSEL_4096X 0x3f /* 111111 */
|
||||
|
||||
static const struct iio_gain_sel_pair bu27010_gains[] = {
|
||||
GAIN_SCALE_GAIN(1, BU27010_GSEL_1X),
|
||||
GAIN_SCALE_GAIN(4, BU27010_GSEL_4X),
|
||||
GAIN_SCALE_GAIN(16, BU27010_GSEL_16X),
|
||||
GAIN_SCALE_GAIN(64, BU27010_GSEL_64X),
|
||||
GAIN_SCALE_GAIN(256, BU27010_GSEL_256X),
|
||||
GAIN_SCALE_GAIN(1024, BU27010_GSEL_1024X),
|
||||
GAIN_SCALE_GAIN(4096, BU27010_GSEL_4096X),
|
||||
};
|
||||
|
||||
static const struct iio_gain_sel_pair bu27010_gains_ir[] = {
|
||||
GAIN_SCALE_GAIN(2, BU27010_GSEL_1X),
|
||||
GAIN_SCALE_GAIN(4, BU27010_GSEL_4X),
|
||||
GAIN_SCALE_GAIN(16, BU27010_GSEL_16X),
|
||||
GAIN_SCALE_GAIN(64, BU27010_GSEL_64X),
|
||||
GAIN_SCALE_GAIN(256, BU27010_GSEL_256X),
|
||||
GAIN_SCALE_GAIN(1024, BU27010_GSEL_1024X),
|
||||
GAIN_SCALE_GAIN(4096, BU27010_GSEL_4096X),
|
||||
};
|
||||
|
||||
#define BU27008_MEAS_MODE_100MS 0x00
|
||||
#define BU27008_MEAS_MODE_55MS 0x01
|
||||
#define BU27008_MEAS_MODE_200MS 0x02
|
||||
#define BU27008_MEAS_MODE_400MS 0x04
|
||||
|
||||
#define BU27010_MEAS_MODE_100MS 0x00
|
||||
#define BU27010_MEAS_MODE_55MS 0x03
|
||||
#define BU27010_MEAS_MODE_200MS 0x01
|
||||
#define BU27010_MEAS_MODE_400MS 0x02
|
||||
|
||||
#define BU27008_MEAS_TIME_MAX_MS 400
|
||||
|
||||
static const struct iio_itime_sel_mul bu27008_itimes[] = {
|
||||
@ -165,6 +281,13 @@ static const struct iio_itime_sel_mul bu27008_itimes[] = {
|
||||
GAIN_SCALE_ITIME_US(55000, BU27008_MEAS_MODE_55MS, 1),
|
||||
};
|
||||
|
||||
static const struct iio_itime_sel_mul bu27010_itimes[] = {
|
||||
GAIN_SCALE_ITIME_US(400000, BU27010_MEAS_MODE_400MS, 8),
|
||||
GAIN_SCALE_ITIME_US(200000, BU27010_MEAS_MODE_200MS, 4),
|
||||
GAIN_SCALE_ITIME_US(100000, BU27010_MEAS_MODE_100MS, 2),
|
||||
GAIN_SCALE_ITIME_US(55000, BU27010_MEAS_MODE_55MS, 1),
|
||||
};
|
||||
|
||||
/*
|
||||
* All the RGBC channels share the same gain.
|
||||
* IR gain can be fine-tuned from the gain set for the RGBC by 2 bit, but this
|
||||
@ -211,7 +334,35 @@ static const struct iio_chan_spec bu27008_channels[] = {
|
||||
IIO_CHAN_SOFT_TIMESTAMP(BU27008_NUM_CHANS),
|
||||
};
|
||||
|
||||
struct bu27008_data;
|
||||
|
||||
struct bu27_chip_data {
|
||||
const char *name;
|
||||
int (*chip_init)(struct bu27008_data *data);
|
||||
int (*get_gain_sel)(struct bu27008_data *data, int *sel);
|
||||
int (*write_gain_sel)(struct bu27008_data *data, int sel);
|
||||
const struct regmap_config *regmap_cfg;
|
||||
const struct iio_gain_sel_pair *gains;
|
||||
const struct iio_gain_sel_pair *gains_ir;
|
||||
const struct iio_itime_sel_mul *itimes;
|
||||
int num_gains;
|
||||
int num_gains_ir;
|
||||
int num_itimes;
|
||||
int scale1x;
|
||||
|
||||
int drdy_en_reg;
|
||||
int drdy_en_mask;
|
||||
int meas_en_reg;
|
||||
int meas_en_mask;
|
||||
int valid_reg;
|
||||
int chan_sel_reg;
|
||||
int chan_sel_mask;
|
||||
int int_time_mask;
|
||||
u8 part_id;
|
||||
};
|
||||
|
||||
struct bu27008_data {
|
||||
const struct bu27_chip_data *cd;
|
||||
struct regmap *regmap;
|
||||
struct iio_trigger *trig;
|
||||
struct device *dev;
|
||||
@ -240,11 +391,29 @@ static const struct regmap_range bu27008_volatile_ranges[] = {
|
||||
},
|
||||
};
|
||||
|
||||
static const struct regmap_range bu27010_volatile_ranges[] = {
|
||||
{
|
||||
.range_min = BU27010_REG_RESET, /* RSTB */
|
||||
.range_max = BU27008_REG_SYSTEM_CONTROL, /* RESET */
|
||||
}, {
|
||||
.range_min = BU27010_REG_MODE_CONTROL5, /* VALID bits */
|
||||
.range_max = BU27010_REG_MODE_CONTROL5,
|
||||
}, {
|
||||
.range_min = BU27008_REG_DATA0_LO,
|
||||
.range_max = BU27010_REG_FIFO_DATA_HI,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct regmap_access_table bu27008_volatile_regs = {
|
||||
.yes_ranges = &bu27008_volatile_ranges[0],
|
||||
.n_yes_ranges = ARRAY_SIZE(bu27008_volatile_ranges),
|
||||
};
|
||||
|
||||
static const struct regmap_access_table bu27010_volatile_regs = {
|
||||
.yes_ranges = &bu27010_volatile_ranges[0],
|
||||
.n_yes_ranges = ARRAY_SIZE(bu27010_volatile_ranges),
|
||||
};
|
||||
|
||||
static const struct regmap_range bu27008_read_only_ranges[] = {
|
||||
{
|
||||
.range_min = BU27008_REG_DATA0_LO,
|
||||
@ -255,11 +424,26 @@ static const struct regmap_range bu27008_read_only_ranges[] = {
|
||||
},
|
||||
};
|
||||
|
||||
static const struct regmap_range bu27010_read_only_ranges[] = {
|
||||
{
|
||||
.range_min = BU27008_REG_DATA0_LO,
|
||||
.range_max = BU27010_REG_FIFO_DATA_HI,
|
||||
}, {
|
||||
.range_min = BU27010_REG_MANUFACTURER_ID,
|
||||
.range_max = BU27010_REG_MANUFACTURER_ID,
|
||||
}
|
||||
};
|
||||
|
||||
static const struct regmap_access_table bu27008_ro_regs = {
|
||||
.no_ranges = &bu27008_read_only_ranges[0],
|
||||
.n_no_ranges = ARRAY_SIZE(bu27008_read_only_ranges),
|
||||
};
|
||||
|
||||
static const struct regmap_access_table bu27010_ro_regs = {
|
||||
.no_ranges = &bu27010_read_only_ranges[0],
|
||||
.n_no_ranges = ARRAY_SIZE(bu27010_read_only_ranges),
|
||||
};
|
||||
|
||||
static const struct regmap_config bu27008_regmap = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
@ -282,50 +466,16 @@ static const struct regmap_config bu27008_regmap = {
|
||||
.disable_locking = true,
|
||||
};
|
||||
|
||||
#define BU27008_MAX_VALID_RESULT_WAIT_US 50000
|
||||
#define BU27008_VALID_RESULT_WAIT_QUANTA_US 1000
|
||||
static const struct regmap_config bu27010_regmap = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
|
||||
static int bu27008_chan_read_data(struct bu27008_data *data, int reg, int *val)
|
||||
{
|
||||
int ret, valid;
|
||||
__le16 tmp;
|
||||
|
||||
ret = regmap_read_poll_timeout(data->regmap, BU27008_REG_MODE_CONTROL3,
|
||||
valid, (valid & BU27008_MASK_VALID),
|
||||
BU27008_VALID_RESULT_WAIT_QUANTA_US,
|
||||
BU27008_MAX_VALID_RESULT_WAIT_US);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_bulk_read(data->regmap, reg, &tmp, sizeof(tmp));
|
||||
if (ret)
|
||||
dev_err(data->dev, "Reading channel data failed\n");
|
||||
|
||||
*val = le16_to_cpu(tmp);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bu27008_get_gain(struct bu27008_data *data, struct iio_gts *gts, int *gain)
|
||||
{
|
||||
int ret, sel;
|
||||
|
||||
ret = regmap_read(data->regmap, BU27008_REG_MODE_CONTROL2, &sel);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
sel = FIELD_GET(BU27008_MASK_RGBC_GAIN, sel);
|
||||
|
||||
ret = iio_gts_find_gain_by_sel(gts, sel);
|
||||
if (ret < 0) {
|
||||
dev_err(data->dev, "unknown gain value 0x%x\n", sel);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*gain = ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
.max_register = BU27010_REG_MAX,
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
.volatile_table = &bu27010_volatile_regs,
|
||||
.wr_table = &bu27010_ro_regs,
|
||||
.disable_locking = true,
|
||||
};
|
||||
|
||||
static int bu27008_write_gain_sel(struct bu27008_data *data, int sel)
|
||||
{
|
||||
@ -368,6 +518,264 @@ static int bu27008_write_gain_sel(struct bu27008_data *data, int sel)
|
||||
BU27008_MASK_RGBC_GAIN, regval);
|
||||
}
|
||||
|
||||
static int bu27010_write_gain_sel(struct bu27008_data *data, int sel)
|
||||
{
|
||||
unsigned int regval;
|
||||
int ret, chan_selector;
|
||||
|
||||
/*
|
||||
* Gain 'selector' is composed of two registers. Selector is 6bit value,
|
||||
* 4 high bits being the RGBC gain fieild in MODE_CONTROL1 register and
|
||||
* two low bits being the channel specific gain in MODE_CONTROL2.
|
||||
*
|
||||
* Let's take the 4 high bits of whole 6 bit selector, and prepare
|
||||
* the MODE_CONTROL1 value (RGBC gain part).
|
||||
*/
|
||||
regval = FIELD_PREP(BU27010_MASK_RGBC_GAIN, (sel >> 2));
|
||||
|
||||
ret = regmap_update_bits(data->regmap, BU27008_REG_MODE_CONTROL1,
|
||||
BU27010_MASK_RGBC_GAIN, regval);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Two low two bits of the selector must be written for all 4
|
||||
* channels in the MODE_CONTROL2 register. Copy these two bits for
|
||||
* all channels.
|
||||
*/
|
||||
chan_selector = sel & GENMASK(1, 0);
|
||||
|
||||
regval = FIELD_PREP(BU27010_MASK_DATA0_GAIN, chan_selector);
|
||||
regval |= FIELD_PREP(BU27010_MASK_DATA1_GAIN, chan_selector);
|
||||
regval |= FIELD_PREP(BU27010_MASK_DATA2_GAIN, chan_selector);
|
||||
regval |= FIELD_PREP(BU27010_MASK_DATA3_GAIN, chan_selector);
|
||||
|
||||
return regmap_write(data->regmap, BU27008_REG_MODE_CONTROL2, regval);
|
||||
}
|
||||
|
||||
static int bu27008_get_gain_sel(struct bu27008_data *data, int *sel)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* If we always "lock" the gain selectors for all channels to prevent
|
||||
* unsupported configs, then it does not matter which channel is used
|
||||
* we can just return selector from any of them.
|
||||
*
|
||||
* This, however is not true if we decide to support only 4X and 16X
|
||||
* and then individual gains for channels. Currently this is not the
|
||||
* case.
|
||||
*
|
||||
* If we some day decide to support individual gains, then we need to
|
||||
* have channel information here.
|
||||
*/
|
||||
|
||||
ret = regmap_read(data->regmap, BU27008_REG_MODE_CONTROL2, sel);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*sel = FIELD_GET(BU27008_MASK_RGBC_GAIN, *sel);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bu27010_get_gain_sel(struct bu27008_data *data, int *sel)
|
||||
{
|
||||
int ret, tmp;
|
||||
|
||||
/*
|
||||
* We always "lock" the gain selectors for all channels to prevent
|
||||
* unsupported configs. It does not matter which channel is used
|
||||
* we can just return selector from any of them.
|
||||
*
|
||||
* Read the channel0 gain.
|
||||
*/
|
||||
ret = regmap_read(data->regmap, BU27008_REG_MODE_CONTROL2, sel);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*sel = FIELD_GET(BU27010_MASK_DATA0_GAIN, *sel);
|
||||
|
||||
/* Read the shared gain */
|
||||
ret = regmap_read(data->regmap, BU27008_REG_MODE_CONTROL1, &tmp);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* The gain selector is made as a combination of common RGBC gain and
|
||||
* the channel specific gain. The channel specific gain forms the low
|
||||
* bits of selector and RGBC gain is appended right after it.
|
||||
*
|
||||
* Compose the selector from channel0 gain and shared RGBC gain.
|
||||
*/
|
||||
*sel |= FIELD_GET(BU27010_MASK_RGBC_GAIN, tmp) << fls(BU27010_MASK_DATA0_GAIN);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bu27008_chip_init(struct bu27008_data *data)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = regmap_write_bits(data->regmap, BU27008_REG_SYSTEM_CONTROL,
|
||||
BU27008_MASK_SW_RESET, BU27008_MASK_SW_RESET);
|
||||
if (ret)
|
||||
return dev_err_probe(data->dev, ret, "Sensor reset failed\n");
|
||||
|
||||
/*
|
||||
* The data-sheet does not tell how long performing the IC reset takes.
|
||||
* However, the data-sheet says the minimum time it takes the IC to be
|
||||
* able to take inputs after power is applied, is 100 uS. I'd assume
|
||||
* > 1 mS is enough.
|
||||
*/
|
||||
msleep(1);
|
||||
|
||||
ret = regmap_reinit_cache(data->regmap, data->cd->regmap_cfg);
|
||||
if (ret)
|
||||
dev_err(data->dev, "Failed to reinit reg cache\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bu27010_chip_init(struct bu27008_data *data)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = regmap_write_bits(data->regmap, BU27008_REG_SYSTEM_CONTROL,
|
||||
BU27010_MASK_SW_RESET, BU27010_MASK_SW_RESET);
|
||||
if (ret)
|
||||
return dev_err_probe(data->dev, ret, "Sensor reset failed\n");
|
||||
|
||||
msleep(1);
|
||||
|
||||
/* Power ON*/
|
||||
ret = regmap_write_bits(data->regmap, BU27010_REG_POWER,
|
||||
BU27010_MASK_POWER, BU27010_MASK_POWER);
|
||||
if (ret)
|
||||
return dev_err_probe(data->dev, ret, "Sensor power-on failed\n");
|
||||
|
||||
msleep(1);
|
||||
|
||||
/* Release blocks from reset */
|
||||
ret = regmap_write_bits(data->regmap, BU27010_REG_RESET,
|
||||
BU27010_MASK_RESET, BU27010_RESET_RELEASE);
|
||||
if (ret)
|
||||
return dev_err_probe(data->dev, ret, "Sensor powering failed\n");
|
||||
|
||||
msleep(1);
|
||||
|
||||
/*
|
||||
* The IRQ enabling on BU27010 is done in a peculiar way. The IRQ
|
||||
* enabling is not a bit mask where individual IRQs could be enabled but
|
||||
* a field which values are:
|
||||
* 00 => IRQs disabled
|
||||
* 01 => Data-ready (RGBC/IR)
|
||||
* 10 => Data-ready (flicker)
|
||||
* 11 => Flicker FIFO
|
||||
*
|
||||
* So, only one IRQ can be enabled at a time and enabling for example
|
||||
* flicker FIFO would automagically disable data-ready IRQ.
|
||||
*
|
||||
* Currently the driver does not support the flicker. Hence, we can
|
||||
* just treat the RGBC data-ready as single bit which can be enabled /
|
||||
* disabled. This works for as long as the second bit in the field
|
||||
* stays zero. Here we ensure it gets zeroed.
|
||||
*/
|
||||
return regmap_clear_bits(data->regmap, BU27010_REG_MODE_CONTROL4,
|
||||
BU27010_IRQ_DIS_ALL);
|
||||
}
|
||||
|
||||
static const struct bu27_chip_data bu27010_chip = {
|
||||
.name = "bu27010",
|
||||
.chip_init = bu27010_chip_init,
|
||||
.get_gain_sel = bu27010_get_gain_sel,
|
||||
.write_gain_sel = bu27010_write_gain_sel,
|
||||
.regmap_cfg = &bu27010_regmap,
|
||||
.gains = &bu27010_gains[0],
|
||||
.gains_ir = &bu27010_gains_ir[0],
|
||||
.itimes = &bu27010_itimes[0],
|
||||
.num_gains = ARRAY_SIZE(bu27010_gains),
|
||||
.num_gains_ir = ARRAY_SIZE(bu27010_gains_ir),
|
||||
.num_itimes = ARRAY_SIZE(bu27010_itimes),
|
||||
.scale1x = BU27010_SCALE_1X,
|
||||
.drdy_en_reg = BU27010_REG_MODE_CONTROL4,
|
||||
.drdy_en_mask = BU27010_DRDY_EN,
|
||||
.meas_en_reg = BU27010_REG_MODE_CONTROL5,
|
||||
.meas_en_mask = BU27010_MASK_MEAS_EN,
|
||||
.valid_reg = BU27010_REG_MODE_CONTROL5,
|
||||
.chan_sel_reg = BU27008_REG_MODE_CONTROL1,
|
||||
.chan_sel_mask = BU27010_MASK_CHAN_SEL,
|
||||
.int_time_mask = BU27010_MASK_MEAS_MODE,
|
||||
.part_id = BU27010_ID,
|
||||
};
|
||||
|
||||
static const struct bu27_chip_data bu27008_chip = {
|
||||
.name = "bu27008",
|
||||
.chip_init = bu27008_chip_init,
|
||||
.get_gain_sel = bu27008_get_gain_sel,
|
||||
.write_gain_sel = bu27008_write_gain_sel,
|
||||
.regmap_cfg = &bu27008_regmap,
|
||||
.gains = &bu27008_gains[0],
|
||||
.gains_ir = &bu27008_gains_ir[0],
|
||||
.itimes = &bu27008_itimes[0],
|
||||
.num_gains = ARRAY_SIZE(bu27008_gains),
|
||||
.num_gains_ir = ARRAY_SIZE(bu27008_gains_ir),
|
||||
.num_itimes = ARRAY_SIZE(bu27008_itimes),
|
||||
.scale1x = BU27008_SCALE_1X,
|
||||
.drdy_en_reg = BU27008_REG_MODE_CONTROL3,
|
||||
.drdy_en_mask = BU27008_MASK_INT_EN,
|
||||
.valid_reg = BU27008_REG_MODE_CONTROL3,
|
||||
.meas_en_reg = BU27008_REG_MODE_CONTROL3,
|
||||
.meas_en_mask = BU27008_MASK_MEAS_EN,
|
||||
.chan_sel_reg = BU27008_REG_MODE_CONTROL3,
|
||||
.chan_sel_mask = BU27008_MASK_CHAN_SEL,
|
||||
.int_time_mask = BU27008_MASK_MEAS_MODE,
|
||||
.part_id = BU27008_ID,
|
||||
};
|
||||
|
||||
#define BU27008_MAX_VALID_RESULT_WAIT_US 50000
|
||||
#define BU27008_VALID_RESULT_WAIT_QUANTA_US 1000
|
||||
|
||||
static int bu27008_chan_read_data(struct bu27008_data *data, int reg, int *val)
|
||||
{
|
||||
int ret, valid;
|
||||
__le16 tmp;
|
||||
|
||||
ret = regmap_read_poll_timeout(data->regmap, data->cd->valid_reg,
|
||||
valid, (valid & BU27008_MASK_VALID),
|
||||
BU27008_VALID_RESULT_WAIT_QUANTA_US,
|
||||
BU27008_MAX_VALID_RESULT_WAIT_US);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_bulk_read(data->regmap, reg, &tmp, sizeof(tmp));
|
||||
if (ret)
|
||||
dev_err(data->dev, "Reading channel data failed\n");
|
||||
|
||||
*val = le16_to_cpu(tmp);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bu27008_get_gain(struct bu27008_data *data, struct iio_gts *gts, int *gain)
|
||||
{
|
||||
int ret, sel;
|
||||
|
||||
ret = data->cd->get_gain_sel(data, &sel);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = iio_gts_find_gain_by_sel(gts, sel);
|
||||
if (ret < 0) {
|
||||
dev_err(data->dev, "unknown gain value 0x%x\n", sel);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*gain = ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bu27008_set_gain(struct bu27008_data *data, int gain)
|
||||
{
|
||||
int ret;
|
||||
@ -376,7 +784,7 @@ static int bu27008_set_gain(struct bu27008_data *data, int gain)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return bu27008_write_gain_sel(data, ret);
|
||||
return data->cd->write_gain_sel(data, ret);
|
||||
}
|
||||
|
||||
static int bu27008_get_int_time_sel(struct bu27008_data *data, int *sel)
|
||||
@ -384,15 +792,23 @@ static int bu27008_get_int_time_sel(struct bu27008_data *data, int *sel)
|
||||
int ret, val;
|
||||
|
||||
ret = regmap_read(data->regmap, BU27008_REG_MODE_CONTROL1, &val);
|
||||
*sel = FIELD_GET(BU27008_MASK_MEAS_MODE, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return ret;
|
||||
val &= data->cd->int_time_mask;
|
||||
val >>= ffs(data->cd->int_time_mask) - 1;
|
||||
|
||||
*sel = val;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bu27008_set_int_time_sel(struct bu27008_data *data, int sel)
|
||||
{
|
||||
sel <<= ffs(data->cd->int_time_mask) - 1;
|
||||
|
||||
return regmap_update_bits(data->regmap, BU27008_REG_MODE_CONTROL1,
|
||||
BU27008_MASK_MEAS_MODE, sel);
|
||||
data->cd->int_time_mask, sel);
|
||||
}
|
||||
|
||||
static int bu27008_get_int_time_us(struct bu27008_data *data)
|
||||
@ -448,8 +864,7 @@ static int bu27008_set_int_time(struct bu27008_data *data, int time)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return regmap_update_bits(data->regmap, BU27008_REG_MODE_CONTROL1,
|
||||
BU27008_MASK_MEAS_MODE, ret);
|
||||
return bu27008_set_int_time_sel(data, ret);
|
||||
}
|
||||
|
||||
/* Try to change the time so that the scale is maintained */
|
||||
@ -527,10 +942,13 @@ static int bu27008_try_set_int_time(struct bu27008_data *data, int int_time_new)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bu27008_meas_set(struct bu27008_data *data, int state)
|
||||
static int bu27008_meas_set(struct bu27008_data *data, bool enable)
|
||||
{
|
||||
return regmap_update_bits(data->regmap, BU27008_REG_MODE_CONTROL3,
|
||||
BU27008_MASK_MEAS_EN, state);
|
||||
if (enable)
|
||||
return regmap_set_bits(data->regmap, data->cd->meas_en_reg,
|
||||
data->cd->meas_en_mask);
|
||||
return regmap_clear_bits(data->regmap, data->cd->meas_en_reg,
|
||||
data->cd->meas_en_mask);
|
||||
}
|
||||
|
||||
static int bu27008_chan_cfg(struct bu27008_data *data,
|
||||
@ -543,9 +961,15 @@ static int bu27008_chan_cfg(struct bu27008_data *data,
|
||||
else
|
||||
chan_sel = BU27008_CLEAR2_IR3;
|
||||
|
||||
chan_sel = FIELD_PREP(BU27008_MASK_CHAN_SEL, chan_sel);
|
||||
/*
|
||||
* prepare bitfield for channel sel. The FIELD_PREP works only when
|
||||
* mask is constant. In our case the mask is assigned based on the
|
||||
* chip type. Hence the open-coded FIELD_PREP here. We don't bother
|
||||
* zeroing the irrelevant bits though - update_bits takes care of that.
|
||||
*/
|
||||
chan_sel <<= ffs(data->cd->chan_sel_mask) - 1;
|
||||
|
||||
return regmap_update_bits(data->regmap, BU27008_REG_MODE_CONTROL3,
|
||||
return regmap_update_bits(data->regmap, data->cd->chan_sel_reg,
|
||||
BU27008_MASK_CHAN_SEL, chan_sel);
|
||||
}
|
||||
|
||||
@ -558,7 +982,7 @@ static int bu27008_read_one(struct bu27008_data *data, struct iio_dev *idev,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = bu27008_meas_set(data, BU27008_MEAS_EN);
|
||||
ret = bu27008_meas_set(data, true);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -574,7 +998,7 @@ static int bu27008_read_one(struct bu27008_data *data, struct iio_dev *idev,
|
||||
if (!ret)
|
||||
ret = IIO_VAL_INT;
|
||||
|
||||
if (bu27008_meas_set(data, BU27008_MEAS_DIS))
|
||||
if (bu27008_meas_set(data, false))
|
||||
dev_warn(data->dev, "measurement disabling failed\n");
|
||||
|
||||
return ret;
|
||||
@ -669,7 +1093,7 @@ static int bu27008_set_scale(struct bu27008_data *data,
|
||||
goto unlock_out;
|
||||
|
||||
}
|
||||
ret = bu27008_write_gain_sel(data, gain_sel);
|
||||
ret = data->cd->write_gain_sel(data, gain_sel);
|
||||
|
||||
unlock_out:
|
||||
mutex_unlock(&data->mutex);
|
||||
@ -747,10 +1171,10 @@ static int bu27008_update_scan_mode(struct iio_dev *idev,
|
||||
chan_sel = BU27008_CLEAR2_IR3;
|
||||
}
|
||||
|
||||
chan_sel = FIELD_PREP(BU27008_MASK_CHAN_SEL, chan_sel);
|
||||
chan_sel <<= ffs(data->cd->chan_sel_mask) - 1;
|
||||
|
||||
return regmap_update_bits(data->regmap, BU27008_REG_MODE_CONTROL3,
|
||||
BU27008_MASK_CHAN_SEL, chan_sel);
|
||||
return regmap_update_bits(data->regmap, data->cd->chan_sel_reg,
|
||||
data->cd->chan_sel_mask, chan_sel);
|
||||
}
|
||||
|
||||
static const struct iio_info bu27008_info = {
|
||||
@ -761,46 +1185,18 @@ static const struct iio_info bu27008_info = {
|
||||
.validate_trigger = iio_validate_own_trigger,
|
||||
};
|
||||
|
||||
static int bu27008_chip_init(struct bu27008_data *data)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = regmap_write_bits(data->regmap, BU27008_REG_SYSTEM_CONTROL,
|
||||
BU27008_MASK_SW_RESET, BU27008_MASK_SW_RESET);
|
||||
if (ret)
|
||||
return dev_err_probe(data->dev, ret, "Sensor reset failed\n");
|
||||
|
||||
/*
|
||||
* The data-sheet does not tell how long performing the IC reset takes.
|
||||
* However, the data-sheet says the minimum time it takes the IC to be
|
||||
* able to take inputs after power is applied, is 100 uS. I'd assume
|
||||
* > 1 mS is enough.
|
||||
*/
|
||||
msleep(1);
|
||||
|
||||
ret = regmap_reinit_cache(data->regmap, &bu27008_regmap);
|
||||
if (ret)
|
||||
dev_err(data->dev, "Failed to reinit reg cache\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bu27008_set_drdy_irq(struct bu27008_data *data, int state)
|
||||
{
|
||||
return regmap_update_bits(data->regmap, BU27008_REG_MODE_CONTROL3,
|
||||
BU27008_MASK_INT_EN, state);
|
||||
}
|
||||
|
||||
static int bu27008_trigger_set_state(struct iio_trigger *trig,
|
||||
bool state)
|
||||
static int bu27008_trigger_set_state(struct iio_trigger *trig, bool state)
|
||||
{
|
||||
struct bu27008_data *data = iio_trigger_get_drvdata(trig);
|
||||
int ret;
|
||||
|
||||
|
||||
if (state)
|
||||
ret = bu27008_set_drdy_irq(data, BU27008_INT_EN);
|
||||
ret = regmap_set_bits(data->regmap, data->cd->drdy_en_reg,
|
||||
data->cd->drdy_en_mask);
|
||||
else
|
||||
ret = bu27008_set_drdy_irq(data, BU27008_INT_DIS);
|
||||
ret = regmap_clear_bits(data->regmap, data->cd->drdy_en_reg,
|
||||
data->cd->drdy_en_mask);
|
||||
if (ret)
|
||||
dev_err(data->dev, "Failed to set trigger state\n");
|
||||
|
||||
@ -836,7 +1232,7 @@ static irqreturn_t bu27008_trigger_handler(int irq, void *p)
|
||||
* After some measurements, it seems reading the
|
||||
* BU27008_REG_MODE_CONTROL3 debounces the IRQ line
|
||||
*/
|
||||
ret = regmap_read(data->regmap, BU27008_REG_MODE_CONTROL3, &dummy);
|
||||
ret = regmap_read(data->regmap, data->cd->valid_reg, &dummy);
|
||||
if (ret < 0)
|
||||
goto err_read;
|
||||
|
||||
@ -856,14 +1252,14 @@ static int bu27008_buffer_preenable(struct iio_dev *idev)
|
||||
{
|
||||
struct bu27008_data *data = iio_priv(idev);
|
||||
|
||||
return bu27008_meas_set(data, BU27008_MEAS_EN);
|
||||
return bu27008_meas_set(data, true);
|
||||
}
|
||||
|
||||
static int bu27008_buffer_postdisable(struct iio_dev *idev)
|
||||
{
|
||||
struct bu27008_data *data = iio_priv(idev);
|
||||
|
||||
return bu27008_meas_set(data, BU27008_MEAS_DIS);
|
||||
return bu27008_meas_set(data, false);
|
||||
}
|
||||
|
||||
static const struct iio_buffer_setup_ops bu27008_buffer_ops = {
|
||||
@ -936,11 +1332,6 @@ static int bu27008_probe(struct i2c_client *i2c)
|
||||
struct iio_dev *idev;
|
||||
int ret;
|
||||
|
||||
regmap = devm_regmap_init_i2c(i2c, &bu27008_regmap);
|
||||
if (IS_ERR(regmap))
|
||||
return dev_err_probe(dev, PTR_ERR(regmap),
|
||||
"Failed to initialize Regmap\n");
|
||||
|
||||
idev = devm_iio_device_alloc(dev, sizeof(*data));
|
||||
if (!idev)
|
||||
return -ENOMEM;
|
||||
@ -951,24 +1342,34 @@ static int bu27008_probe(struct i2c_client *i2c)
|
||||
|
||||
data = iio_priv(idev);
|
||||
|
||||
data->cd = device_get_match_data(&i2c->dev);
|
||||
if (!data->cd)
|
||||
return -ENODEV;
|
||||
|
||||
regmap = devm_regmap_init_i2c(i2c, data->cd->regmap_cfg);
|
||||
if (IS_ERR(regmap))
|
||||
return dev_err_probe(dev, PTR_ERR(regmap),
|
||||
"Failed to initialize Regmap\n");
|
||||
|
||||
|
||||
ret = regmap_read(regmap, BU27008_REG_SYSTEM_CONTROL, ®);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Failed to access sensor\n");
|
||||
|
||||
part_id = FIELD_GET(BU27008_MASK_PART_ID, reg);
|
||||
|
||||
if (part_id != BU27008_ID)
|
||||
if (part_id != data->cd->part_id)
|
||||
dev_warn(dev, "unknown device 0x%x\n", part_id);
|
||||
|
||||
ret = devm_iio_init_iio_gts(dev, BU27008_SCALE_1X, 0, bu27008_gains,
|
||||
ARRAY_SIZE(bu27008_gains), bu27008_itimes,
|
||||
ARRAY_SIZE(bu27008_itimes), &data->gts);
|
||||
ret = devm_iio_init_iio_gts(dev, data->cd->scale1x, 0, data->cd->gains,
|
||||
data->cd->num_gains, data->cd->itimes,
|
||||
data->cd->num_itimes, &data->gts);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = devm_iio_init_iio_gts(dev, BU27008_SCALE_1X, 0, bu27008_gains_ir,
|
||||
ARRAY_SIZE(bu27008_gains_ir), bu27008_itimes,
|
||||
ARRAY_SIZE(bu27008_itimes), &data->gts_ir);
|
||||
ret = devm_iio_init_iio_gts(dev, data->cd->scale1x, 0, data->cd->gains_ir,
|
||||
data->cd->num_gains_ir, data->cd->itimes,
|
||||
data->cd->num_itimes, &data->gts_ir);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -979,12 +1380,12 @@ static int bu27008_probe(struct i2c_client *i2c)
|
||||
|
||||
idev->channels = bu27008_channels;
|
||||
idev->num_channels = ARRAY_SIZE(bu27008_channels);
|
||||
idev->name = "bu27008";
|
||||
idev->name = data->cd->name;
|
||||
idev->info = &bu27008_info;
|
||||
idev->modes = INDIO_DIRECT_MODE;
|
||||
idev->available_scan_masks = bu27008_scan_masks;
|
||||
|
||||
ret = bu27008_chip_init(data);
|
||||
ret = data->cd->chip_init(data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -1005,7 +1406,8 @@ static int bu27008_probe(struct i2c_client *i2c)
|
||||
}
|
||||
|
||||
static const struct of_device_id bu27008_of_match[] = {
|
||||
{ .compatible = "rohm,bu27008" },
|
||||
{ .compatible = "rohm,bu27008", .data = &bu27008_chip },
|
||||
{ .compatible = "rohm,bu27010", .data = &bu27010_chip },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, bu27008_of_match);
|
||||
@ -1020,7 +1422,7 @@ static struct i2c_driver bu27008_i2c_driver = {
|
||||
};
|
||||
module_i2c_driver(bu27008_i2c_driver);
|
||||
|
||||
MODULE_DESCRIPTION("ROHM BU27008 colour sensor driver");
|
||||
MODULE_DESCRIPTION("ROHM BU27008 and BU27010 colour sensor driver");
|
||||
MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_IMPORT_NS(IIO_GTS_HELPER);
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include <linux/delay.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/units.h>
|
||||
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/events.h>
|
||||
@ -60,11 +61,15 @@
|
||||
|
||||
#define VCNL4200_AL_CONF 0x00 /* Ambient light configuration */
|
||||
#define VCNL4200_PS_CONF1 0x03 /* Proximity configuration */
|
||||
#define VCNL4200_PS_CONF3 0x04 /* Proximity configuration */
|
||||
#define VCNL4040_PS_THDL_LM 0x06 /* Proximity threshold low */
|
||||
#define VCNL4040_PS_THDH_LM 0x07 /* Proximity threshold high */
|
||||
#define VCNL4040_ALS_THDL_LM 0x02 /* Ambient light threshold low */
|
||||
#define VCNL4040_ALS_THDH_LM 0x01 /* Ambient light threshold high */
|
||||
#define VCNL4200_PS_DATA 0x08 /* Proximity data */
|
||||
#define VCNL4200_AL_DATA 0x09 /* Ambient light data */
|
||||
#define VCNL4040_INT_FLAGS 0x0b /* Interrupt register */
|
||||
#define VCNL4200_INT_FLAGS 0x0d /* Interrupt register */
|
||||
#define VCNL4200_DEV_ID 0x0e /* Device ID, slave address and version */
|
||||
|
||||
#define VCNL4040_DEV_ID 0x0c /* Device ID and version */
|
||||
@ -79,11 +84,19 @@
|
||||
#define VCNL4000_SELF_TIMED_EN BIT(0) /* start self-timed measurement */
|
||||
|
||||
#define VCNL4040_ALS_CONF_ALS_SHUTDOWN BIT(0)
|
||||
#define VCNL4040_ALS_CONF_IT GENMASK(7, 6) /* Ambient integration time */
|
||||
#define VCNL4040_ALS_CONF_INT_EN BIT(1) /* Ambient light Interrupt enable */
|
||||
#define VCNL4040_ALS_CONF_PERS GENMASK(3, 2) /* Ambient interrupt persistence setting */
|
||||
#define VCNL4040_PS_CONF1_PS_SHUTDOWN BIT(0)
|
||||
#define VCNL4040_PS_CONF2_PS_IT GENMASK(3, 1) /* Proximity integration time */
|
||||
#define VCNL4040_CONF1_PS_PERS GENMASK(5, 4) /* Proximity interrupt persistence setting */
|
||||
#define VCNL4040_PS_CONF2_PS_INT GENMASK(9, 8) /* Proximity interrupt mode */
|
||||
#define VCNL4040_PS_CONF3_MPS GENMASK(6, 5) /* Proximity multi pulse number */
|
||||
#define VCNL4040_PS_MS_LED_I GENMASK(10, 8) /* Proximity current */
|
||||
#define VCNL4040_PS_IF_AWAY BIT(8) /* Proximity event cross low threshold */
|
||||
#define VCNL4040_PS_IF_CLOSE BIT(9) /* Proximity event cross high threshold */
|
||||
#define VCNL4040_ALS_RISING BIT(12) /* Ambient Light cross high threshold */
|
||||
#define VCNL4040_ALS_FALLING BIT(13) /* Ambient Light cross low threshold */
|
||||
|
||||
/* Bit masks for interrupt registers. */
|
||||
#define VCNL4010_INT_THR_SEL BIT(0) /* Select threshold interrupt source */
|
||||
@ -123,6 +136,44 @@ static const int vcnl4040_ps_it_times[][2] = {
|
||||
{0, 800},
|
||||
};
|
||||
|
||||
static const int vcnl4200_ps_it_times[][2] = {
|
||||
{0, 96},
|
||||
{0, 144},
|
||||
{0, 192},
|
||||
{0, 384},
|
||||
{0, 768},
|
||||
{0, 864},
|
||||
};
|
||||
|
||||
static const int vcnl4040_als_it_times[][2] = {
|
||||
{0, 80000},
|
||||
{0, 160000},
|
||||
{0, 320000},
|
||||
{0, 640000},
|
||||
};
|
||||
|
||||
static const int vcnl4200_als_it_times[][2] = {
|
||||
{0, 50000},
|
||||
{0, 100000},
|
||||
{0, 200000},
|
||||
{0, 400000},
|
||||
};
|
||||
|
||||
static const int vcnl4040_ps_calibbias_ua[][2] = {
|
||||
{0, 50000},
|
||||
{0, 75000},
|
||||
{0, 100000},
|
||||
{0, 120000},
|
||||
{0, 140000},
|
||||
{0, 160000},
|
||||
{0, 180000},
|
||||
{0, 200000},
|
||||
};
|
||||
|
||||
static const int vcnl4040_als_persistence[] = {1, 2, 4, 8};
|
||||
static const int vcnl4040_ps_persistence[] = {1, 2, 3, 4};
|
||||
static const int vcnl4040_ps_oversampling_ratio[] = {1, 2, 4, 8};
|
||||
|
||||
#define VCNL4000_SLEEP_DELAY_MS 2000 /* before we enter pm_runtime_suspend */
|
||||
|
||||
enum vcnl4000_device_ids {
|
||||
@ -145,6 +196,7 @@ struct vcnl4000_data {
|
||||
int rev;
|
||||
int al_scale;
|
||||
u8 ps_int; /* proximity interrupt mode */
|
||||
u8 als_int; /* ambient light interrupt mode*/
|
||||
const struct vcnl4000_chip_spec *chip_spec;
|
||||
struct mutex vcnl4000_lock;
|
||||
struct vcnl4200_channel vcnl4200_al;
|
||||
@ -164,6 +216,13 @@ struct vcnl4000_chip_spec {
|
||||
int (*set_power_state)(struct vcnl4000_data *data, bool on);
|
||||
irqreturn_t (*irq_thread)(int irq, void *priv);
|
||||
irqreturn_t (*trig_buffer_func)(int irq, void *priv);
|
||||
|
||||
u8 int_reg;
|
||||
const int(*ps_it_times)[][2];
|
||||
const int num_ps_it_times;
|
||||
const int(*als_it_times)[][2];
|
||||
const int num_als_it_times;
|
||||
const unsigned int ulux_step;
|
||||
};
|
||||
|
||||
static const struct i2c_device_id vcnl4000_id[] = {
|
||||
@ -263,7 +322,7 @@ static int vcnl4200_set_power_state(struct vcnl4000_data *data, bool on)
|
||||
int ret;
|
||||
|
||||
/* Do not power down if interrupts are enabled */
|
||||
if (!on && data->ps_int)
|
||||
if (!on && (data->ps_int || data->als_int))
|
||||
return 0;
|
||||
|
||||
ret = vcnl4000_write_als_enable(data, on);
|
||||
@ -308,6 +367,7 @@ static int vcnl4200_init(struct vcnl4000_data *data)
|
||||
|
||||
data->rev = (ret >> 8) & 0xf;
|
||||
data->ps_int = 0;
|
||||
data->als_int = 0;
|
||||
|
||||
data->vcnl4200_al.reg = VCNL4200_AL_DATA;
|
||||
data->vcnl4200_ps.reg = VCNL4200_PS_DATA;
|
||||
@ -317,16 +377,15 @@ static int vcnl4200_init(struct vcnl4000_data *data)
|
||||
data->vcnl4200_al.sampling_rate = ktime_set(0, 60000 * 1000);
|
||||
/* Default wait time is 4.8ms, add 20% tolerance. */
|
||||
data->vcnl4200_ps.sampling_rate = ktime_set(0, 5760 * 1000);
|
||||
data->al_scale = 24000;
|
||||
break;
|
||||
case VCNL4040_PROD_ID:
|
||||
/* Default wait time is 80ms, add 20% tolerance. */
|
||||
data->vcnl4200_al.sampling_rate = ktime_set(0, 96000 * 1000);
|
||||
/* Default wait time is 5ms, add 20% tolerance. */
|
||||
data->vcnl4200_ps.sampling_rate = ktime_set(0, 6000 * 1000);
|
||||
data->al_scale = 120000;
|
||||
break;
|
||||
}
|
||||
data->al_scale = data->chip_spec->ulux_step;
|
||||
mutex_init(&data->vcnl4200_al.lock);
|
||||
mutex_init(&data->vcnl4200_ps.lock);
|
||||
|
||||
@ -496,6 +555,60 @@ static int vcnl4000_set_pm_runtime_state(struct vcnl4000_data *data, bool on)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int vcnl4040_read_als_it(struct vcnl4000_data *data, int *val, int *val2)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = FIELD_GET(VCNL4040_ALS_CONF_IT, ret);
|
||||
if (ret >= data->chip_spec->num_als_it_times)
|
||||
return -EINVAL;
|
||||
|
||||
*val = (*data->chip_spec->als_it_times)[ret][0];
|
||||
*val2 = (*data->chip_spec->als_it_times)[ret][1];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t vcnl4040_write_als_it(struct vcnl4000_data *data, int val)
|
||||
{
|
||||
unsigned int i;
|
||||
int ret;
|
||||
u16 regval;
|
||||
|
||||
for (i = 0; i < data->chip_spec->num_als_it_times; i++) {
|
||||
if (val == (*data->chip_spec->als_it_times)[i][1])
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == data->chip_spec->num_als_it_times)
|
||||
return -EINVAL;
|
||||
|
||||
data->vcnl4200_al.sampling_rate = ktime_set(0, val * 1200);
|
||||
data->al_scale = div_u64(mul_u32_u32(data->chip_spec->ulux_step,
|
||||
(*data->chip_spec->als_it_times)[0][1]),
|
||||
val);
|
||||
|
||||
mutex_lock(&data->vcnl4000_lock);
|
||||
|
||||
ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF);
|
||||
if (ret < 0)
|
||||
goto out_unlock;
|
||||
|
||||
regval = FIELD_PREP(VCNL4040_ALS_CONF_IT, i);
|
||||
regval |= (ret & ~VCNL4040_ALS_CONF_IT);
|
||||
ret = i2c_smbus_write_word_data(data->client,
|
||||
VCNL4200_AL_CONF,
|
||||
regval);
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&data->vcnl4000_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int vcnl4040_read_ps_it(struct vcnl4000_data *data, int *val, int *val2)
|
||||
{
|
||||
int ret;
|
||||
@ -506,11 +619,11 @@ static int vcnl4040_read_ps_it(struct vcnl4000_data *data, int *val, int *val2)
|
||||
|
||||
ret = FIELD_GET(VCNL4040_PS_CONF2_PS_IT, ret);
|
||||
|
||||
if (ret >= ARRAY_SIZE(vcnl4040_ps_it_times))
|
||||
if (ret >= data->chip_spec->num_ps_it_times)
|
||||
return -EINVAL;
|
||||
|
||||
*val = vcnl4040_ps_it_times[ret][0];
|
||||
*val2 = vcnl4040_ps_it_times[ret][1];
|
||||
*val = (*data->chip_spec->ps_it_times)[ret][0];
|
||||
*val2 = (*data->chip_spec->ps_it_times)[ret][1];
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -521,8 +634,8 @@ static ssize_t vcnl4040_write_ps_it(struct vcnl4000_data *data, int val)
|
||||
int ret, index = -1;
|
||||
u16 regval;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(vcnl4040_ps_it_times); i++) {
|
||||
if (val == vcnl4040_ps_it_times[i][1]) {
|
||||
for (i = 0; i < data->chip_spec->num_ps_it_times; i++) {
|
||||
if (val == (*data->chip_spec->ps_it_times)[i][1]) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
@ -531,6 +644,8 @@ static ssize_t vcnl4040_write_ps_it(struct vcnl4000_data *data, int val)
|
||||
if (index < 0)
|
||||
return -EINVAL;
|
||||
|
||||
data->vcnl4200_ps.sampling_rate = ktime_set(0, val * 60 * NSEC_PER_USEC);
|
||||
|
||||
mutex_lock(&data->vcnl4000_lock);
|
||||
|
||||
ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1);
|
||||
@ -547,6 +662,224 @@ static ssize_t vcnl4040_write_ps_it(struct vcnl4000_data *data, int val)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t vcnl4040_read_als_period(struct vcnl4000_data *data, int *val, int *val2)
|
||||
{
|
||||
int ret, ret_pers, it;
|
||||
int64_t val_c;
|
||||
|
||||
ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret_pers = FIELD_GET(VCNL4040_ALS_CONF_PERS, ret);
|
||||
if (ret_pers >= ARRAY_SIZE(vcnl4040_als_persistence))
|
||||
return -EINVAL;
|
||||
|
||||
it = FIELD_GET(VCNL4040_ALS_CONF_IT, ret);
|
||||
if (it >= data->chip_spec->num_als_it_times)
|
||||
return -EINVAL;
|
||||
|
||||
val_c = mul_u32_u32((*data->chip_spec->als_it_times)[it][1],
|
||||
vcnl4040_als_persistence[ret_pers]);
|
||||
*val = div_u64_rem(val_c, MICRO, val2);
|
||||
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
}
|
||||
|
||||
static ssize_t vcnl4040_write_als_period(struct vcnl4000_data *data, int val, int val2)
|
||||
{
|
||||
unsigned int i;
|
||||
int ret, it;
|
||||
u16 regval;
|
||||
u64 val_n = mul_u32_u32(val, MICRO) + val2;
|
||||
|
||||
ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
it = FIELD_GET(VCNL4040_ALS_CONF_IT, ret);
|
||||
if (it >= data->chip_spec->num_als_it_times)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(vcnl4040_als_persistence) - 1; i++) {
|
||||
if (val_n < mul_u32_u32(vcnl4040_als_persistence[i],
|
||||
(*data->chip_spec->als_it_times)[it][1]))
|
||||
break;
|
||||
}
|
||||
|
||||
mutex_lock(&data->vcnl4000_lock);
|
||||
|
||||
ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF);
|
||||
if (ret < 0)
|
||||
goto out_unlock;
|
||||
|
||||
regval = FIELD_PREP(VCNL4040_ALS_CONF_PERS, i);
|
||||
regval |= (ret & ~VCNL4040_ALS_CONF_PERS);
|
||||
ret = i2c_smbus_write_word_data(data->client, VCNL4200_AL_CONF,
|
||||
regval);
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&data->vcnl4000_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t vcnl4040_read_ps_period(struct vcnl4000_data *data, int *val, int *val2)
|
||||
{
|
||||
int ret, ret_pers, it;
|
||||
|
||||
ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret_pers = FIELD_GET(VCNL4040_CONF1_PS_PERS, ret);
|
||||
if (ret_pers >= ARRAY_SIZE(vcnl4040_ps_persistence))
|
||||
return -EINVAL;
|
||||
|
||||
it = FIELD_GET(VCNL4040_PS_CONF2_PS_IT, ret);
|
||||
if (it >= data->chip_spec->num_ps_it_times)
|
||||
return -EINVAL;
|
||||
|
||||
*val = (*data->chip_spec->ps_it_times)[it][0];
|
||||
*val2 = (*data->chip_spec->ps_it_times)[it][1] *
|
||||
vcnl4040_ps_persistence[ret_pers];
|
||||
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
}
|
||||
|
||||
static ssize_t vcnl4040_write_ps_period(struct vcnl4000_data *data, int val, int val2)
|
||||
{
|
||||
int ret, it, i;
|
||||
u16 regval;
|
||||
|
||||
ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
it = FIELD_GET(VCNL4040_PS_CONF2_PS_IT, ret);
|
||||
if (it >= data->chip_spec->num_ps_it_times)
|
||||
return -EINVAL;
|
||||
|
||||
if (val > 0)
|
||||
i = ARRAY_SIZE(vcnl4040_ps_persistence) - 1;
|
||||
else {
|
||||
for (i = 0; i < ARRAY_SIZE(vcnl4040_ps_persistence) - 1; i++) {
|
||||
if (val2 <= vcnl4040_ps_persistence[i] *
|
||||
(*data->chip_spec->ps_it_times)[it][1])
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_lock(&data->vcnl4000_lock);
|
||||
|
||||
ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1);
|
||||
if (ret < 0)
|
||||
goto out_unlock;
|
||||
|
||||
regval = FIELD_PREP(VCNL4040_CONF1_PS_PERS, i);
|
||||
regval |= (ret & ~VCNL4040_CONF1_PS_PERS);
|
||||
ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1,
|
||||
regval);
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&data->vcnl4000_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t vcnl4040_read_ps_oversampling_ratio(struct vcnl4000_data *data, int *val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF3);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = FIELD_GET(VCNL4040_PS_CONF3_MPS, ret);
|
||||
if (ret >= ARRAY_SIZE(vcnl4040_ps_oversampling_ratio))
|
||||
return -EINVAL;
|
||||
|
||||
*val = vcnl4040_ps_oversampling_ratio[ret];
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t vcnl4040_write_ps_oversampling_ratio(struct vcnl4000_data *data, int val)
|
||||
{
|
||||
unsigned int i;
|
||||
int ret;
|
||||
u16 regval;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(vcnl4040_ps_oversampling_ratio); i++) {
|
||||
if (val == vcnl4040_ps_oversampling_ratio[i])
|
||||
break;
|
||||
}
|
||||
|
||||
if (i >= ARRAY_SIZE(vcnl4040_ps_oversampling_ratio))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&data->vcnl4000_lock);
|
||||
|
||||
ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF3);
|
||||
if (ret < 0)
|
||||
goto out_unlock;
|
||||
|
||||
regval = FIELD_PREP(VCNL4040_PS_CONF3_MPS, i);
|
||||
regval |= (ret & ~VCNL4040_PS_CONF3_MPS);
|
||||
ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF3,
|
||||
regval);
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&data->vcnl4000_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t vcnl4040_read_ps_calibbias(struct vcnl4000_data *data, int *val, int *val2)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF3);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = FIELD_GET(VCNL4040_PS_MS_LED_I, ret);
|
||||
if (ret >= ARRAY_SIZE(vcnl4040_ps_calibbias_ua))
|
||||
return -EINVAL;
|
||||
|
||||
*val = vcnl4040_ps_calibbias_ua[ret][0];
|
||||
*val2 = vcnl4040_ps_calibbias_ua[ret][1];
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t vcnl4040_write_ps_calibbias(struct vcnl4000_data *data, int val)
|
||||
{
|
||||
unsigned int i;
|
||||
int ret;
|
||||
u16 regval;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(vcnl4040_ps_calibbias_ua); i++) {
|
||||
if (val == vcnl4040_ps_calibbias_ua[i][1])
|
||||
break;
|
||||
}
|
||||
|
||||
if (i >= ARRAY_SIZE(vcnl4040_ps_calibbias_ua))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&data->vcnl4000_lock);
|
||||
|
||||
ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF3);
|
||||
if (ret < 0)
|
||||
goto out_unlock;
|
||||
|
||||
regval = (ret & ~VCNL4040_PS_MS_LED_I);
|
||||
regval |= FIELD_PREP(VCNL4040_PS_MS_LED_I, i);
|
||||
ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF3,
|
||||
regval);
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&data->vcnl4000_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int vcnl4000_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
@ -584,12 +917,39 @@ static int vcnl4000_read_raw(struct iio_dev *indio_dev,
|
||||
*val2 = data->al_scale;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
case IIO_CHAN_INFO_INT_TIME:
|
||||
if (chan->type != IIO_PROXIMITY)
|
||||
switch (chan->type) {
|
||||
case IIO_LIGHT:
|
||||
ret = vcnl4040_read_als_it(data, val, val2);
|
||||
break;
|
||||
case IIO_PROXIMITY:
|
||||
ret = vcnl4040_read_ps_it(data, val, val2);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
ret = vcnl4040_read_ps_it(data, val, val2);
|
||||
}
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
|
||||
switch (chan->type) {
|
||||
case IIO_PROXIMITY:
|
||||
ret = vcnl4040_read_ps_oversampling_ratio(data, val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
switch (chan->type) {
|
||||
case IIO_PROXIMITY:
|
||||
ret = vcnl4040_read_ps_calibbias(data, val, val2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -605,9 +965,28 @@ static int vcnl4040_write_raw(struct iio_dev *indio_dev,
|
||||
case IIO_CHAN_INFO_INT_TIME:
|
||||
if (val != 0)
|
||||
return -EINVAL;
|
||||
if (chan->type != IIO_PROXIMITY)
|
||||
switch (chan->type) {
|
||||
case IIO_LIGHT:
|
||||
return vcnl4040_write_als_it(data, val2);
|
||||
case IIO_PROXIMITY:
|
||||
return vcnl4040_write_ps_it(data, val2);
|
||||
default:
|
||||
return -EINVAL;
|
||||
return vcnl4040_write_ps_it(data, val2);
|
||||
}
|
||||
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
|
||||
switch (chan->type) {
|
||||
case IIO_PROXIMITY:
|
||||
return vcnl4040_write_ps_oversampling_ratio(data, val);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
switch (chan->type) {
|
||||
case IIO_PROXIMITY:
|
||||
return vcnl4040_write_ps_calibbias(data, val2);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -618,12 +997,44 @@ static int vcnl4040_read_avail(struct iio_dev *indio_dev,
|
||||
const int **vals, int *type, int *length,
|
||||
long mask)
|
||||
{
|
||||
struct vcnl4000_data *data = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_INT_TIME:
|
||||
*vals = (int *)vcnl4040_ps_it_times;
|
||||
switch (chan->type) {
|
||||
case IIO_LIGHT:
|
||||
*vals = (int *)(*data->chip_spec->als_it_times);
|
||||
*length = 2 * data->chip_spec->num_als_it_times;
|
||||
break;
|
||||
case IIO_PROXIMITY:
|
||||
*vals = (int *)(*data->chip_spec->ps_it_times);
|
||||
*length = 2 * data->chip_spec->num_ps_it_times;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
*type = IIO_VAL_INT_PLUS_MICRO;
|
||||
*length = 2 * ARRAY_SIZE(vcnl4040_ps_it_times);
|
||||
return IIO_AVAIL_LIST;
|
||||
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
|
||||
switch (chan->type) {
|
||||
case IIO_PROXIMITY:
|
||||
*vals = (int *)vcnl4040_ps_oversampling_ratio;
|
||||
*length = ARRAY_SIZE(vcnl4040_ps_oversampling_ratio);
|
||||
*type = IIO_VAL_INT;
|
||||
return IIO_AVAIL_LIST;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
switch (chan->type) {
|
||||
case IIO_PROXIMITY:
|
||||
*vals = (int *)vcnl4040_ps_calibbias_ua;
|
||||
*length = 2 * ARRAY_SIZE(vcnl4040_ps_calibbias_ua);
|
||||
*type = IIO_VAL_INT_PLUS_MICRO;
|
||||
return IIO_AVAIL_LIST;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -818,24 +1229,58 @@ static int vcnl4040_read_event(struct iio_dev *indio_dev,
|
||||
int ret;
|
||||
struct vcnl4000_data *data = iio_priv(indio_dev);
|
||||
|
||||
switch (dir) {
|
||||
case IIO_EV_DIR_RISING:
|
||||
ret = i2c_smbus_read_word_data(data->client,
|
||||
VCNL4040_PS_THDH_LM);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = ret;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_EV_DIR_FALLING:
|
||||
ret = i2c_smbus_read_word_data(data->client,
|
||||
VCNL4040_PS_THDL_LM);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = ret;
|
||||
return IIO_VAL_INT;
|
||||
switch (chan->type) {
|
||||
case IIO_LIGHT:
|
||||
switch (info) {
|
||||
case IIO_EV_INFO_PERIOD:
|
||||
return vcnl4040_read_als_period(data, val, val2);
|
||||
case IIO_EV_INFO_VALUE:
|
||||
switch (dir) {
|
||||
case IIO_EV_DIR_RISING:
|
||||
ret = i2c_smbus_read_word_data(data->client,
|
||||
VCNL4040_ALS_THDH_LM);
|
||||
break;
|
||||
case IIO_EV_DIR_FALLING:
|
||||
ret = i2c_smbus_read_word_data(data->client,
|
||||
VCNL4040_ALS_THDL_LM);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
case IIO_PROXIMITY:
|
||||
switch (info) {
|
||||
case IIO_EV_INFO_PERIOD:
|
||||
return vcnl4040_read_ps_period(data, val, val2);
|
||||
case IIO_EV_INFO_VALUE:
|
||||
switch (dir) {
|
||||
case IIO_EV_DIR_RISING:
|
||||
ret = i2c_smbus_read_word_data(data->client,
|
||||
VCNL4040_PS_THDH_LM);
|
||||
break;
|
||||
case IIO_EV_DIR_FALLING:
|
||||
ret = i2c_smbus_read_word_data(data->client,
|
||||
VCNL4040_PS_THDL_LM);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = ret;
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
static int vcnl4040_write_event(struct iio_dev *indio_dev,
|
||||
@ -848,22 +1293,61 @@ static int vcnl4040_write_event(struct iio_dev *indio_dev,
|
||||
int ret;
|
||||
struct vcnl4000_data *data = iio_priv(indio_dev);
|
||||
|
||||
switch (dir) {
|
||||
case IIO_EV_DIR_RISING:
|
||||
ret = i2c_smbus_write_word_data(data->client,
|
||||
VCNL4040_PS_THDH_LM, val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_EV_DIR_FALLING:
|
||||
ret = i2c_smbus_write_word_data(data->client,
|
||||
VCNL4040_PS_THDL_LM, val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return IIO_VAL_INT;
|
||||
switch (chan->type) {
|
||||
case IIO_LIGHT:
|
||||
switch (info) {
|
||||
case IIO_EV_INFO_PERIOD:
|
||||
return vcnl4040_write_als_period(data, val, val2);
|
||||
case IIO_EV_INFO_VALUE:
|
||||
switch (dir) {
|
||||
case IIO_EV_DIR_RISING:
|
||||
ret = i2c_smbus_write_word_data(data->client,
|
||||
VCNL4040_ALS_THDH_LM,
|
||||
val);
|
||||
break;
|
||||
case IIO_EV_DIR_FALLING:
|
||||
ret = i2c_smbus_write_word_data(data->client,
|
||||
VCNL4040_ALS_THDL_LM,
|
||||
val);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
case IIO_PROXIMITY:
|
||||
switch (info) {
|
||||
case IIO_EV_INFO_PERIOD:
|
||||
return vcnl4040_write_ps_period(data, val, val2);
|
||||
case IIO_EV_INFO_VALUE:
|
||||
switch (dir) {
|
||||
case IIO_EV_DIR_RISING:
|
||||
ret = i2c_smbus_write_word_data(data->client,
|
||||
VCNL4040_PS_THDH_LM,
|
||||
val);
|
||||
break;
|
||||
case IIO_EV_DIR_FALLING:
|
||||
ret = i2c_smbus_write_word_data(data->client,
|
||||
VCNL4040_PS_THDL_LM,
|
||||
val);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
static bool vcnl4010_is_thr_enabled(struct vcnl4000_data *data)
|
||||
@ -956,15 +1440,28 @@ static int vcnl4040_read_event_config(struct iio_dev *indio_dev,
|
||||
int ret;
|
||||
struct vcnl4000_data *data = iio_priv(indio_dev);
|
||||
|
||||
ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
switch (chan->type) {
|
||||
case IIO_LIGHT:
|
||||
ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
data->ps_int = FIELD_GET(VCNL4040_PS_CONF2_PS_INT, ret);
|
||||
data->als_int = FIELD_GET(VCNL4040_ALS_CONF_INT_EN, ret);
|
||||
|
||||
return (dir == IIO_EV_DIR_RISING) ?
|
||||
FIELD_GET(VCNL4040_PS_IF_AWAY, ret) :
|
||||
FIELD_GET(VCNL4040_PS_IF_CLOSE, ret);
|
||||
return data->als_int;
|
||||
case IIO_PROXIMITY:
|
||||
ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
data->ps_int = FIELD_GET(VCNL4040_PS_CONF2_PS_INT, ret);
|
||||
|
||||
return (dir == IIO_EV_DIR_RISING) ?
|
||||
FIELD_GET(VCNL4040_PS_IF_AWAY, ret) :
|
||||
FIELD_GET(VCNL4040_PS_IF_CLOSE, ret);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int vcnl4040_write_event_config(struct iio_dev *indio_dev,
|
||||
@ -972,29 +1469,51 @@ static int vcnl4040_write_event_config(struct iio_dev *indio_dev,
|
||||
enum iio_event_type type,
|
||||
enum iio_event_direction dir, int state)
|
||||
{
|
||||
int ret;
|
||||
int ret = -EINVAL;
|
||||
u16 val, mask;
|
||||
struct vcnl4000_data *data = iio_priv(indio_dev);
|
||||
|
||||
mutex_lock(&data->vcnl4000_lock);
|
||||
|
||||
ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
switch (chan->type) {
|
||||
case IIO_LIGHT:
|
||||
ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
if (dir == IIO_EV_DIR_RISING)
|
||||
mask = VCNL4040_PS_IF_AWAY;
|
||||
else
|
||||
mask = VCNL4040_PS_IF_CLOSE;
|
||||
mask = VCNL4040_ALS_CONF_INT_EN;
|
||||
if (state)
|
||||
val = (ret | mask);
|
||||
else
|
||||
val = (ret & ~mask);
|
||||
|
||||
val = state ? (ret | mask) : (ret & ~mask);
|
||||
data->als_int = FIELD_GET(VCNL4040_ALS_CONF_INT_EN, val);
|
||||
ret = i2c_smbus_write_word_data(data->client, VCNL4200_AL_CONF,
|
||||
val);
|
||||
break;
|
||||
case IIO_PROXIMITY:
|
||||
ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
data->ps_int = FIELD_GET(VCNL4040_PS_CONF2_PS_INT, val);
|
||||
ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, val);
|
||||
if (dir == IIO_EV_DIR_RISING)
|
||||
mask = VCNL4040_PS_IF_AWAY;
|
||||
else
|
||||
mask = VCNL4040_PS_IF_CLOSE;
|
||||
|
||||
val = state ? (ret | mask) : (ret & ~mask);
|
||||
|
||||
data->ps_int = FIELD_GET(VCNL4040_PS_CONF2_PS_INT, val);
|
||||
ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1,
|
||||
val);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
mutex_unlock(&data->vcnl4000_lock);
|
||||
data->chip_spec->set_power_state(data, data->ps_int != 0);
|
||||
data->chip_spec->set_power_state(data, data->ps_int || data->als_int);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -1005,7 +1524,7 @@ static irqreturn_t vcnl4040_irq_thread(int irq, void *p)
|
||||
struct vcnl4000_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_read_word_data(data->client, VCNL4040_INT_FLAGS);
|
||||
ret = i2c_smbus_read_word_data(data->client, data->chip_spec->int_reg);
|
||||
if (ret < 0)
|
||||
return IRQ_HANDLED;
|
||||
|
||||
@ -1025,6 +1544,22 @@ static irqreturn_t vcnl4040_irq_thread(int irq, void *p)
|
||||
iio_get_time_ns(indio_dev));
|
||||
}
|
||||
|
||||
if (ret & VCNL4040_ALS_FALLING) {
|
||||
iio_push_event(indio_dev,
|
||||
IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0,
|
||||
IIO_EV_TYPE_THRESH,
|
||||
IIO_EV_DIR_FALLING),
|
||||
iio_get_time_ns(indio_dev));
|
||||
}
|
||||
|
||||
if (ret & VCNL4040_ALS_RISING) {
|
||||
iio_push_event(indio_dev,
|
||||
IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0,
|
||||
IIO_EV_TYPE_THRESH,
|
||||
IIO_EV_DIR_RISING),
|
||||
iio_get_time_ns(indio_dev));
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
@ -1191,6 +1726,22 @@ static const struct iio_event_spec vcnl4000_event_spec[] = {
|
||||
}
|
||||
};
|
||||
|
||||
static const struct iio_event_spec vcnl4040_als_event_spec[] = {
|
||||
{
|
||||
.type = IIO_EV_TYPE_THRESH,
|
||||
.dir = IIO_EV_DIR_RISING,
|
||||
.mask_separate = BIT(IIO_EV_INFO_VALUE),
|
||||
}, {
|
||||
.type = IIO_EV_TYPE_THRESH,
|
||||
.dir = IIO_EV_DIR_FALLING,
|
||||
.mask_separate = BIT(IIO_EV_INFO_VALUE),
|
||||
}, {
|
||||
.type = IIO_EV_TYPE_THRESH,
|
||||
.dir = IIO_EV_DIR_EITHER,
|
||||
.mask_separate = BIT(IIO_EV_INFO_ENABLE) | BIT(IIO_EV_INFO_PERIOD),
|
||||
},
|
||||
};
|
||||
|
||||
static const struct iio_event_spec vcnl4040_event_spec[] = {
|
||||
{
|
||||
.type = IIO_EV_TYPE_THRESH,
|
||||
@ -1200,6 +1751,10 @@ static const struct iio_event_spec vcnl4040_event_spec[] = {
|
||||
.type = IIO_EV_TYPE_THRESH,
|
||||
.dir = IIO_EV_DIR_FALLING,
|
||||
.mask_separate = BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_ENABLE),
|
||||
}, {
|
||||
.type = IIO_EV_TYPE_THRESH,
|
||||
.dir = IIO_EV_DIR_EITHER,
|
||||
.mask_separate = BIT(IIO_EV_INFO_PERIOD),
|
||||
},
|
||||
};
|
||||
|
||||
@ -1244,12 +1799,20 @@ static const struct iio_chan_spec vcnl4040_channels[] = {
|
||||
{
|
||||
.type = IIO_LIGHT,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_INT_TIME),
|
||||
.info_mask_separate_available = BIT(IIO_CHAN_INFO_INT_TIME),
|
||||
.event_spec = vcnl4040_als_event_spec,
|
||||
.num_event_specs = ARRAY_SIZE(vcnl4040_als_event_spec),
|
||||
}, {
|
||||
.type = IIO_PROXIMITY,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_INT_TIME),
|
||||
.info_mask_separate_available = BIT(IIO_CHAN_INFO_INT_TIME),
|
||||
BIT(IIO_CHAN_INFO_INT_TIME) |
|
||||
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) |
|
||||
BIT(IIO_CHAN_INFO_CALIBBIAS),
|
||||
.info_mask_separate_available = BIT(IIO_CHAN_INFO_INT_TIME) |
|
||||
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) |
|
||||
BIT(IIO_CHAN_INFO_CALIBBIAS),
|
||||
.ext_info = vcnl4000_ext_info,
|
||||
.event_spec = vcnl4040_event_spec,
|
||||
.num_event_specs = ARRAY_SIZE(vcnl4040_event_spec),
|
||||
@ -1314,6 +1877,12 @@ static const struct vcnl4000_chip_spec vcnl4000_chip_spec_cfg[] = {
|
||||
.num_channels = ARRAY_SIZE(vcnl4040_channels),
|
||||
.info = &vcnl4040_info,
|
||||
.irq_thread = vcnl4040_irq_thread,
|
||||
.int_reg = VCNL4040_INT_FLAGS,
|
||||
.ps_it_times = &vcnl4040_ps_it_times,
|
||||
.num_ps_it_times = ARRAY_SIZE(vcnl4040_ps_it_times),
|
||||
.als_it_times = &vcnl4040_als_it_times,
|
||||
.num_als_it_times = ARRAY_SIZE(vcnl4040_als_it_times),
|
||||
.ulux_step = 100000,
|
||||
},
|
||||
[VCNL4200] = {
|
||||
.prod = "VCNL4200",
|
||||
@ -1321,9 +1890,16 @@ static const struct vcnl4000_chip_spec vcnl4000_chip_spec_cfg[] = {
|
||||
.measure_light = vcnl4200_measure_light,
|
||||
.measure_proximity = vcnl4200_measure_proximity,
|
||||
.set_power_state = vcnl4200_set_power_state,
|
||||
.channels = vcnl4000_channels,
|
||||
.channels = vcnl4040_channels,
|
||||
.num_channels = ARRAY_SIZE(vcnl4000_channels),
|
||||
.info = &vcnl4000_info,
|
||||
.info = &vcnl4040_info,
|
||||
.irq_thread = vcnl4040_irq_thread,
|
||||
.int_reg = VCNL4200_INT_FLAGS,
|
||||
.ps_it_times = &vcnl4200_ps_it_times,
|
||||
.num_ps_it_times = ARRAY_SIZE(vcnl4200_ps_it_times),
|
||||
.als_it_times = &vcnl4200_als_it_times,
|
||||
.num_als_it_times = ARRAY_SIZE(vcnl4200_als_it_times),
|
||||
.ulux_step = 24000,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -99,20 +99,25 @@ static const struct iio_info mcp4018_info = {
|
||||
.write_raw = mcp4018_write_raw,
|
||||
};
|
||||
|
||||
#define MCP4018_ID_TABLE(_name, cfg) { \
|
||||
.name = _name, \
|
||||
.driver_data = (kernel_ulong_t)&mcp4018_cfg[cfg], \
|
||||
}
|
||||
|
||||
static const struct i2c_device_id mcp4018_id[] = {
|
||||
{ "mcp4017-502", MCP4018_502 },
|
||||
{ "mcp4017-103", MCP4018_103 },
|
||||
{ "mcp4017-503", MCP4018_503 },
|
||||
{ "mcp4017-104", MCP4018_104 },
|
||||
{ "mcp4018-502", MCP4018_502 },
|
||||
{ "mcp4018-103", MCP4018_103 },
|
||||
{ "mcp4018-503", MCP4018_503 },
|
||||
{ "mcp4018-104", MCP4018_104 },
|
||||
{ "mcp4019-502", MCP4018_502 },
|
||||
{ "mcp4019-103", MCP4018_103 },
|
||||
{ "mcp4019-503", MCP4018_503 },
|
||||
{ "mcp4019-104", MCP4018_104 },
|
||||
{}
|
||||
MCP4018_ID_TABLE("mcp4017-502", MCP4018_502),
|
||||
MCP4018_ID_TABLE("mcp4017-103", MCP4018_103),
|
||||
MCP4018_ID_TABLE("mcp4017-503", MCP4018_503),
|
||||
MCP4018_ID_TABLE("mcp4017-104", MCP4018_104),
|
||||
MCP4018_ID_TABLE("mcp4018-502", MCP4018_502),
|
||||
MCP4018_ID_TABLE("mcp4018-103", MCP4018_103),
|
||||
MCP4018_ID_TABLE("mcp4018-503", MCP4018_503),
|
||||
MCP4018_ID_TABLE("mcp4018-104", MCP4018_104),
|
||||
MCP4018_ID_TABLE("mcp4019-502", MCP4018_502),
|
||||
MCP4018_ID_TABLE("mcp4019-103", MCP4018_103),
|
||||
MCP4018_ID_TABLE("mcp4019-503", MCP4018_503),
|
||||
MCP4018_ID_TABLE("mcp4019-104", MCP4018_104),
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, mcp4018_id);
|
||||
|
||||
@ -157,9 +162,7 @@ static int mcp4018_probe(struct i2c_client *client)
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
data->client = client;
|
||||
|
||||
data->cfg = device_get_match_data(dev);
|
||||
if (!data->cfg)
|
||||
data->cfg = &mcp4018_cfg[i2c_match_id(mcp4018_id, client)->driver_data];
|
||||
data->cfg = i2c_get_match_data(client);
|
||||
|
||||
indio_dev->info = &mcp4018_info;
|
||||
indio_dev->channels = &mcp4018_channel;
|
||||
|
@ -206,72 +206,77 @@ static const struct iio_info mcp4531_info = {
|
||||
.write_raw = mcp4531_write_raw,
|
||||
};
|
||||
|
||||
#define MCP4531_ID_TABLE(_name, cfg) { \
|
||||
.name = _name, \
|
||||
.driver_data = (kernel_ulong_t)&mcp4531_cfg[cfg], \
|
||||
}
|
||||
|
||||
static const struct i2c_device_id mcp4531_id[] = {
|
||||
{ "mcp4531-502", MCP453x_502 },
|
||||
{ "mcp4531-103", MCP453x_103 },
|
||||
{ "mcp4531-503", MCP453x_503 },
|
||||
{ "mcp4531-104", MCP453x_104 },
|
||||
{ "mcp4532-502", MCP453x_502 },
|
||||
{ "mcp4532-103", MCP453x_103 },
|
||||
{ "mcp4532-503", MCP453x_503 },
|
||||
{ "mcp4532-104", MCP453x_104 },
|
||||
{ "mcp4541-502", MCP454x_502 },
|
||||
{ "mcp4541-103", MCP454x_103 },
|
||||
{ "mcp4541-503", MCP454x_503 },
|
||||
{ "mcp4541-104", MCP454x_104 },
|
||||
{ "mcp4542-502", MCP454x_502 },
|
||||
{ "mcp4542-103", MCP454x_103 },
|
||||
{ "mcp4542-503", MCP454x_503 },
|
||||
{ "mcp4542-104", MCP454x_104 },
|
||||
{ "mcp4551-502", MCP455x_502 },
|
||||
{ "mcp4551-103", MCP455x_103 },
|
||||
{ "mcp4551-503", MCP455x_503 },
|
||||
{ "mcp4551-104", MCP455x_104 },
|
||||
{ "mcp4552-502", MCP455x_502 },
|
||||
{ "mcp4552-103", MCP455x_103 },
|
||||
{ "mcp4552-503", MCP455x_503 },
|
||||
{ "mcp4552-104", MCP455x_104 },
|
||||
{ "mcp4561-502", MCP456x_502 },
|
||||
{ "mcp4561-103", MCP456x_103 },
|
||||
{ "mcp4561-503", MCP456x_503 },
|
||||
{ "mcp4561-104", MCP456x_104 },
|
||||
{ "mcp4562-502", MCP456x_502 },
|
||||
{ "mcp4562-103", MCP456x_103 },
|
||||
{ "mcp4562-503", MCP456x_503 },
|
||||
{ "mcp4562-104", MCP456x_104 },
|
||||
{ "mcp4631-502", MCP463x_502 },
|
||||
{ "mcp4631-103", MCP463x_103 },
|
||||
{ "mcp4631-503", MCP463x_503 },
|
||||
{ "mcp4631-104", MCP463x_104 },
|
||||
{ "mcp4632-502", MCP463x_502 },
|
||||
{ "mcp4632-103", MCP463x_103 },
|
||||
{ "mcp4632-503", MCP463x_503 },
|
||||
{ "mcp4632-104", MCP463x_104 },
|
||||
{ "mcp4641-502", MCP464x_502 },
|
||||
{ "mcp4641-103", MCP464x_103 },
|
||||
{ "mcp4641-503", MCP464x_503 },
|
||||
{ "mcp4641-104", MCP464x_104 },
|
||||
{ "mcp4642-502", MCP464x_502 },
|
||||
{ "mcp4642-103", MCP464x_103 },
|
||||
{ "mcp4642-503", MCP464x_503 },
|
||||
{ "mcp4642-104", MCP464x_104 },
|
||||
{ "mcp4651-502", MCP465x_502 },
|
||||
{ "mcp4651-103", MCP465x_103 },
|
||||
{ "mcp4651-503", MCP465x_503 },
|
||||
{ "mcp4651-104", MCP465x_104 },
|
||||
{ "mcp4652-502", MCP465x_502 },
|
||||
{ "mcp4652-103", MCP465x_103 },
|
||||
{ "mcp4652-503", MCP465x_503 },
|
||||
{ "mcp4652-104", MCP465x_104 },
|
||||
{ "mcp4661-502", MCP466x_502 },
|
||||
{ "mcp4661-103", MCP466x_103 },
|
||||
{ "mcp4661-503", MCP466x_503 },
|
||||
{ "mcp4661-104", MCP466x_104 },
|
||||
{ "mcp4662-502", MCP466x_502 },
|
||||
{ "mcp4662-103", MCP466x_103 },
|
||||
{ "mcp4662-503", MCP466x_503 },
|
||||
{ "mcp4662-104", MCP466x_104 },
|
||||
{}
|
||||
MCP4531_ID_TABLE("mcp4531-502", MCP453x_502),
|
||||
MCP4531_ID_TABLE("mcp4531-103", MCP453x_103),
|
||||
MCP4531_ID_TABLE("mcp4531-503", MCP453x_503),
|
||||
MCP4531_ID_TABLE("mcp4531-104", MCP453x_104),
|
||||
MCP4531_ID_TABLE("mcp4532-502", MCP453x_502),
|
||||
MCP4531_ID_TABLE("mcp4532-103", MCP453x_103),
|
||||
MCP4531_ID_TABLE("mcp4532-503", MCP453x_503),
|
||||
MCP4531_ID_TABLE("mcp4532-104", MCP453x_104),
|
||||
MCP4531_ID_TABLE("mcp4541-502", MCP454x_502),
|
||||
MCP4531_ID_TABLE("mcp4541-103", MCP454x_103),
|
||||
MCP4531_ID_TABLE("mcp4541-503", MCP454x_503),
|
||||
MCP4531_ID_TABLE("mcp4541-104", MCP454x_104),
|
||||
MCP4531_ID_TABLE("mcp4542-502", MCP454x_502),
|
||||
MCP4531_ID_TABLE("mcp4542-103", MCP454x_103),
|
||||
MCP4531_ID_TABLE("mcp4542-503", MCP454x_503),
|
||||
MCP4531_ID_TABLE("mcp4542-104", MCP454x_104),
|
||||
MCP4531_ID_TABLE("mcp4551-502", MCP455x_502),
|
||||
MCP4531_ID_TABLE("mcp4551-103", MCP455x_103),
|
||||
MCP4531_ID_TABLE("mcp4551-503", MCP455x_503),
|
||||
MCP4531_ID_TABLE("mcp4551-104", MCP455x_104),
|
||||
MCP4531_ID_TABLE("mcp4552-502", MCP455x_502),
|
||||
MCP4531_ID_TABLE("mcp4552-103", MCP455x_103),
|
||||
MCP4531_ID_TABLE("mcp4552-503", MCP455x_503),
|
||||
MCP4531_ID_TABLE("mcp4552-104", MCP455x_104),
|
||||
MCP4531_ID_TABLE("mcp4561-502", MCP456x_502),
|
||||
MCP4531_ID_TABLE("mcp4561-103", MCP456x_103),
|
||||
MCP4531_ID_TABLE("mcp4561-503", MCP456x_503),
|
||||
MCP4531_ID_TABLE("mcp4561-104", MCP456x_104),
|
||||
MCP4531_ID_TABLE("mcp4562-502", MCP456x_502),
|
||||
MCP4531_ID_TABLE("mcp4562-103", MCP456x_103),
|
||||
MCP4531_ID_TABLE("mcp4562-503", MCP456x_503),
|
||||
MCP4531_ID_TABLE("mcp4562-104", MCP456x_104),
|
||||
MCP4531_ID_TABLE("mcp4631-502", MCP463x_502),
|
||||
MCP4531_ID_TABLE("mcp4631-103", MCP463x_103),
|
||||
MCP4531_ID_TABLE("mcp4631-503", MCP463x_503),
|
||||
MCP4531_ID_TABLE("mcp4631-104", MCP463x_104),
|
||||
MCP4531_ID_TABLE("mcp4632-502", MCP463x_502),
|
||||
MCP4531_ID_TABLE("mcp4632-103", MCP463x_103),
|
||||
MCP4531_ID_TABLE("mcp4632-503", MCP463x_503),
|
||||
MCP4531_ID_TABLE("mcp4632-104", MCP463x_104),
|
||||
MCP4531_ID_TABLE("mcp4641-502", MCP464x_502),
|
||||
MCP4531_ID_TABLE("mcp4641-103", MCP464x_103),
|
||||
MCP4531_ID_TABLE("mcp4641-503", MCP464x_503),
|
||||
MCP4531_ID_TABLE("mcp4641-104", MCP464x_104),
|
||||
MCP4531_ID_TABLE("mcp4642-502", MCP464x_502),
|
||||
MCP4531_ID_TABLE("mcp4642-103", MCP464x_103),
|
||||
MCP4531_ID_TABLE("mcp4642-503", MCP464x_503),
|
||||
MCP4531_ID_TABLE("mcp4642-104", MCP464x_104),
|
||||
MCP4531_ID_TABLE("mcp4651-502", MCP465x_502),
|
||||
MCP4531_ID_TABLE("mcp4651-103", MCP465x_103),
|
||||
MCP4531_ID_TABLE("mcp4651-503", MCP465x_503),
|
||||
MCP4531_ID_TABLE("mcp4651-104", MCP465x_104),
|
||||
MCP4531_ID_TABLE("mcp4652-502", MCP465x_502),
|
||||
MCP4531_ID_TABLE("mcp4652-103", MCP465x_103),
|
||||
MCP4531_ID_TABLE("mcp4652-503", MCP465x_503),
|
||||
MCP4531_ID_TABLE("mcp4652-104", MCP465x_104),
|
||||
MCP4531_ID_TABLE("mcp4661-502", MCP466x_502),
|
||||
MCP4531_ID_TABLE("mcp4661-103", MCP466x_103),
|
||||
MCP4531_ID_TABLE("mcp4661-503", MCP466x_503),
|
||||
MCP4531_ID_TABLE("mcp4661-104", MCP466x_104),
|
||||
MCP4531_ID_TABLE("mcp4662-502", MCP466x_502),
|
||||
MCP4531_ID_TABLE("mcp4662-103", MCP466x_103),
|
||||
MCP4531_ID_TABLE("mcp4662-503", MCP466x_503),
|
||||
MCP4531_ID_TABLE("mcp4662-104", MCP466x_104),
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, mcp4531_id);
|
||||
|
||||
@ -368,9 +373,7 @@ static int mcp4531_probe(struct i2c_client *client)
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
data->client = client;
|
||||
|
||||
data->cfg = device_get_match_data(dev);
|
||||
if (!data->cfg)
|
||||
data->cfg = &mcp4531_cfg[i2c_match_id(mcp4531_id, client)->driver_data];
|
||||
data->cfg = i2c_get_match_data(client);
|
||||
|
||||
indio_dev->info = &mcp4531_info;
|
||||
indio_dev->channels = mcp4531_channels;
|
||||
|
@ -32,6 +32,18 @@ config CROS_EC_MKBP_PROXIMITY
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called cros_ec_mkbp_proximity.
|
||||
|
||||
config IRSD200
|
||||
tristate "Murata IRS-D200 PIR sensor"
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
select REGMAP_I2C
|
||||
depends on I2C
|
||||
help
|
||||
Say Y here to build a driver for the Murata IRS-D200 PIR sensor.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called irsd200.
|
||||
|
||||
config ISL29501
|
||||
tristate "Intersil ISL29501 Time Of Flight sensor"
|
||||
depends on I2C
|
||||
|
@ -6,6 +6,7 @@
|
||||
# When adding new entries keep the list in alphabetical order
|
||||
obj-$(CONFIG_AS3935) += as3935.o
|
||||
obj-$(CONFIG_CROS_EC_MKBP_PROXIMITY) += cros_ec_mkbp_proximity.o
|
||||
obj-$(CONFIG_IRSD200) += irsd200.o
|
||||
obj-$(CONFIG_ISL29501) += isl29501.o
|
||||
obj-$(CONFIG_LIDAR_LITE_V2) += pulsedlight-lidar-lite-v2.o
|
||||
obj-$(CONFIG_MB1232) += mb1232.o
|
||||
|
958
drivers/iio/proximity/irsd200.c
Normal file
958
drivers/iio/proximity/irsd200.c
Normal file
@ -0,0 +1,958 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Driver for Murata IRS-D200 PIR sensor.
|
||||
*
|
||||
* Copyright (C) 2023 Axis Communications AB
|
||||
*/
|
||||
|
||||
#include <asm/unaligned.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/events.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/iio/types.h>
|
||||
|
||||
#define IRS_DRV_NAME "irsd200"
|
||||
|
||||
/* Registers. */
|
||||
#define IRS_REG_OP 0x00 /* Operation mode. */
|
||||
#define IRS_REG_DATA_LO 0x02 /* Sensor data LSB. */
|
||||
#define IRS_REG_DATA_HI 0x03 /* Sensor data MSB. */
|
||||
#define IRS_REG_STATUS 0x04 /* Interrupt status. */
|
||||
#define IRS_REG_COUNT 0x05 /* Count of exceeding threshold. */
|
||||
#define IRS_REG_DATA_RATE 0x06 /* Output data rate. */
|
||||
#define IRS_REG_FILTER 0x07 /* High-pass and low-pass filter. */
|
||||
#define IRS_REG_INTR 0x09 /* Interrupt mode. */
|
||||
#define IRS_REG_NR_COUNT 0x0a /* Number of counts before interrupt. */
|
||||
#define IRS_REG_THR_HI 0x0b /* Upper threshold. */
|
||||
#define IRS_REG_THR_LO 0x0c /* Lower threshold. */
|
||||
#define IRS_REG_TIMER_LO 0x0d /* Timer setting LSB. */
|
||||
#define IRS_REG_TIMER_HI 0x0e /* Timer setting MSB. */
|
||||
|
||||
/* Interrupt status bits. */
|
||||
#define IRS_INTR_DATA 0 /* Data update. */
|
||||
#define IRS_INTR_TIMER 1 /* Timer expiration. */
|
||||
#define IRS_INTR_COUNT_THR_AND 2 /* Count "AND" threshold. */
|
||||
#define IRS_INTR_COUNT_THR_OR 3 /* Count "OR" threshold. */
|
||||
|
||||
/* Operation states. */
|
||||
#define IRS_OP_ACTIVE 0x00
|
||||
#define IRS_OP_SLEEP 0x01
|
||||
|
||||
/*
|
||||
* Quantization scale value for threshold. Used for conversion from/to register
|
||||
* value.
|
||||
*/
|
||||
#define IRS_THR_QUANT_SCALE 128
|
||||
|
||||
#define IRS_UPPER_COUNT(count) FIELD_GET(GENMASK(7, 4), count)
|
||||
#define IRS_LOWER_COUNT(count) FIELD_GET(GENMASK(3, 0), count)
|
||||
|
||||
/* Index corresponds to the value of IRS_REG_DATA_RATE register. */
|
||||
static const int irsd200_data_rates[] = {
|
||||
50,
|
||||
100,
|
||||
};
|
||||
|
||||
/* Index corresponds to the (field) value of IRS_REG_FILTER register. */
|
||||
static const unsigned int irsd200_lp_filter_freq[] = {
|
||||
10,
|
||||
7,
|
||||
};
|
||||
|
||||
/*
|
||||
* Index corresponds to the (field) value of IRS_REG_FILTER register. Note that
|
||||
* this represents a fractional value (e.g the first value corresponds to 3 / 10
|
||||
* = 0.3 Hz).
|
||||
*/
|
||||
static const unsigned int irsd200_hp_filter_freq[][2] = {
|
||||
{ 3, 10 },
|
||||
{ 5, 10 },
|
||||
};
|
||||
|
||||
/* Register fields. */
|
||||
enum irsd200_regfield {
|
||||
/* Data interrupt. */
|
||||
IRS_REGF_INTR_DATA,
|
||||
/* Timer interrupt. */
|
||||
IRS_REGF_INTR_TIMER,
|
||||
/* AND count threshold interrupt. */
|
||||
IRS_REGF_INTR_COUNT_THR_AND,
|
||||
/* OR count threshold interrupt. */
|
||||
IRS_REGF_INTR_COUNT_THR_OR,
|
||||
|
||||
/* Low-pass filter frequency. */
|
||||
IRS_REGF_LP_FILTER,
|
||||
/* High-pass filter frequency. */
|
||||
IRS_REGF_HP_FILTER,
|
||||
|
||||
/* Sentinel value. */
|
||||
IRS_REGF_MAX
|
||||
};
|
||||
|
||||
static const struct reg_field irsd200_regfields[] = {
|
||||
[IRS_REGF_INTR_DATA] =
|
||||
REG_FIELD(IRS_REG_INTR, IRS_INTR_DATA, IRS_INTR_DATA),
|
||||
[IRS_REGF_INTR_TIMER] =
|
||||
REG_FIELD(IRS_REG_INTR, IRS_INTR_TIMER, IRS_INTR_TIMER),
|
||||
[IRS_REGF_INTR_COUNT_THR_AND] = REG_FIELD(
|
||||
IRS_REG_INTR, IRS_INTR_COUNT_THR_AND, IRS_INTR_COUNT_THR_AND),
|
||||
[IRS_REGF_INTR_COUNT_THR_OR] = REG_FIELD(
|
||||
IRS_REG_INTR, IRS_INTR_COUNT_THR_OR, IRS_INTR_COUNT_THR_OR),
|
||||
|
||||
[IRS_REGF_LP_FILTER] = REG_FIELD(IRS_REG_FILTER, 1, 1),
|
||||
[IRS_REGF_HP_FILTER] = REG_FIELD(IRS_REG_FILTER, 0, 0),
|
||||
};
|
||||
|
||||
static const struct regmap_config irsd200_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.max_register = IRS_REG_TIMER_HI,
|
||||
};
|
||||
|
||||
struct irsd200_data {
|
||||
struct regmap *regmap;
|
||||
struct regmap_field *regfields[IRS_REGF_MAX];
|
||||
struct device *dev;
|
||||
};
|
||||
|
||||
static int irsd200_setup(struct irsd200_data *data)
|
||||
{
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
/* Disable all interrupt sources. */
|
||||
ret = regmap_write(data->regmap, IRS_REG_INTR, 0);
|
||||
if (ret) {
|
||||
dev_err(data->dev, "Could not set interrupt sources (%d)\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Set operation to active. */
|
||||
ret = regmap_write(data->regmap, IRS_REG_OP, IRS_OP_ACTIVE);
|
||||
if (ret) {
|
||||
dev_err(data->dev, "Could not set operation mode (%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Clear threshold count. */
|
||||
ret = regmap_read(data->regmap, IRS_REG_COUNT, &val);
|
||||
if (ret) {
|
||||
dev_err(data->dev, "Could not clear threshold count (%d)\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Clear status. */
|
||||
ret = regmap_write(data->regmap, IRS_REG_STATUS, 0x0f);
|
||||
if (ret) {
|
||||
dev_err(data->dev, "Could not clear status (%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int irsd200_read_threshold(struct irsd200_data *data,
|
||||
enum iio_event_direction dir, int *val)
|
||||
{
|
||||
unsigned int regval;
|
||||
unsigned int reg;
|
||||
int scale;
|
||||
int ret;
|
||||
|
||||
/* Set quantization scale. */
|
||||
if (dir == IIO_EV_DIR_RISING) {
|
||||
scale = IRS_THR_QUANT_SCALE;
|
||||
reg = IRS_REG_THR_HI;
|
||||
} else if (dir == IIO_EV_DIR_FALLING) {
|
||||
scale = -IRS_THR_QUANT_SCALE;
|
||||
reg = IRS_REG_THR_LO;
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = regmap_read(data->regmap, reg, ®val);
|
||||
if (ret) {
|
||||
dev_err(data->dev, "Could not read threshold (%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*val = ((int)regval) * scale;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int irsd200_write_threshold(struct irsd200_data *data,
|
||||
enum iio_event_direction dir, int val)
|
||||
{
|
||||
unsigned int regval;
|
||||
unsigned int reg;
|
||||
int scale;
|
||||
int ret;
|
||||
|
||||
/* Set quantization scale. */
|
||||
if (dir == IIO_EV_DIR_RISING) {
|
||||
if (val < 0)
|
||||
return -ERANGE;
|
||||
|
||||
scale = IRS_THR_QUANT_SCALE;
|
||||
reg = IRS_REG_THR_HI;
|
||||
} else if (dir == IIO_EV_DIR_FALLING) {
|
||||
if (val > 0)
|
||||
return -ERANGE;
|
||||
|
||||
scale = -IRS_THR_QUANT_SCALE;
|
||||
reg = IRS_REG_THR_LO;
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
regval = val / scale;
|
||||
|
||||
if (regval >= BIT(8))
|
||||
return -ERANGE;
|
||||
|
||||
ret = regmap_write(data->regmap, reg, regval);
|
||||
if (ret) {
|
||||
dev_err(data->dev, "Could not write threshold (%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int irsd200_read_data(struct irsd200_data *data, s16 *val)
|
||||
{
|
||||
__le16 buf;
|
||||
int ret;
|
||||
|
||||
ret = regmap_bulk_read(data->regmap, IRS_REG_DATA_LO, &buf,
|
||||
sizeof(buf));
|
||||
if (ret) {
|
||||
dev_err(data->dev, "Could not bulk read data (%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*val = le16_to_cpu(buf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int irsd200_read_data_rate(struct irsd200_data *data, int *val)
|
||||
{
|
||||
unsigned int regval;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(data->regmap, IRS_REG_DATA_RATE, ®val);
|
||||
if (ret) {
|
||||
dev_err(data->dev, "Could not read data rate (%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (regval >= ARRAY_SIZE(irsd200_data_rates))
|
||||
return -ERANGE;
|
||||
|
||||
*val = irsd200_data_rates[regval];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int irsd200_write_data_rate(struct irsd200_data *data, int val)
|
||||
{
|
||||
size_t idx;
|
||||
int ret;
|
||||
|
||||
for (idx = 0; idx < ARRAY_SIZE(irsd200_data_rates); ++idx) {
|
||||
if (irsd200_data_rates[idx] == val)
|
||||
break;
|
||||
}
|
||||
|
||||
if (idx == ARRAY_SIZE(irsd200_data_rates))
|
||||
return -ERANGE;
|
||||
|
||||
ret = regmap_write(data->regmap, IRS_REG_DATA_RATE, idx);
|
||||
if (ret) {
|
||||
dev_err(data->dev, "Could not write data rate (%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Data sheet says the device needs 3 seconds of settling time. The
|
||||
* device operates normally during this period though. This is more of a
|
||||
* "guarantee" than trying to prevent other user space reads/writes.
|
||||
*/
|
||||
ssleep(3);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int irsd200_read_timer(struct irsd200_data *data, int *val, int *val2)
|
||||
{
|
||||
__le16 buf;
|
||||
int ret;
|
||||
|
||||
ret = regmap_bulk_read(data->regmap, IRS_REG_TIMER_LO, &buf,
|
||||
sizeof(buf));
|
||||
if (ret) {
|
||||
dev_err(data->dev, "Could not bulk read timer (%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = irsd200_read_data_rate(data, val2);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*val = le16_to_cpu(buf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int irsd200_write_timer(struct irsd200_data *data, int val, int val2)
|
||||
{
|
||||
unsigned int regval;
|
||||
int data_rate;
|
||||
__le16 buf;
|
||||
int ret;
|
||||
|
||||
if (val < 0 || val2 < 0)
|
||||
return -ERANGE;
|
||||
|
||||
ret = irsd200_read_data_rate(data, &data_rate);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Quantize from seconds. */
|
||||
regval = val * data_rate + (val2 * data_rate) / 1000000;
|
||||
|
||||
/* Value is 10 bits. */
|
||||
if (regval >= BIT(10))
|
||||
return -ERANGE;
|
||||
|
||||
buf = cpu_to_le16((u16)regval);
|
||||
|
||||
ret = regmap_bulk_write(data->regmap, IRS_REG_TIMER_LO, &buf,
|
||||
sizeof(buf));
|
||||
if (ret) {
|
||||
dev_err(data->dev, "Could not bulk write timer (%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int irsd200_read_nr_count(struct irsd200_data *data, int *val)
|
||||
{
|
||||
unsigned int regval;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(data->regmap, IRS_REG_NR_COUNT, ®val);
|
||||
if (ret) {
|
||||
dev_err(data->dev, "Could not read nr count (%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*val = regval;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int irsd200_write_nr_count(struct irsd200_data *data, int val)
|
||||
{
|
||||
unsigned int regval;
|
||||
int ret;
|
||||
|
||||
/* A value of zero means that IRS_REG_STATUS is never set. */
|
||||
if (val <= 0 || val >= 8)
|
||||
return -ERANGE;
|
||||
|
||||
regval = val;
|
||||
|
||||
if (regval >= 2) {
|
||||
/*
|
||||
* According to the data sheet, timer must be also set in this
|
||||
* case (i.e. be non-zero). Check and enforce that.
|
||||
*/
|
||||
ret = irsd200_read_timer(data, &val, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (val == 0) {
|
||||
dev_err(data->dev,
|
||||
"Timer must be non-zero when nr count is %u\n",
|
||||
regval);
|
||||
return -EPERM;
|
||||
}
|
||||
}
|
||||
|
||||
ret = regmap_write(data->regmap, IRS_REG_NR_COUNT, regval);
|
||||
if (ret) {
|
||||
dev_err(data->dev, "Could not write nr count (%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int irsd200_read_lp_filter(struct irsd200_data *data, int *val)
|
||||
{
|
||||
unsigned int regval;
|
||||
int ret;
|
||||
|
||||
ret = regmap_field_read(data->regfields[IRS_REGF_LP_FILTER], ®val);
|
||||
if (ret) {
|
||||
dev_err(data->dev, "Could not read lp filter frequency (%d)\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*val = irsd200_lp_filter_freq[regval];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int irsd200_write_lp_filter(struct irsd200_data *data, int val)
|
||||
{
|
||||
size_t idx;
|
||||
int ret;
|
||||
|
||||
for (idx = 0; idx < ARRAY_SIZE(irsd200_lp_filter_freq); ++idx) {
|
||||
if (irsd200_lp_filter_freq[idx] == val)
|
||||
break;
|
||||
}
|
||||
|
||||
if (idx == ARRAY_SIZE(irsd200_lp_filter_freq))
|
||||
return -ERANGE;
|
||||
|
||||
ret = regmap_field_write(data->regfields[IRS_REGF_LP_FILTER], idx);
|
||||
if (ret) {
|
||||
dev_err(data->dev, "Could not write lp filter frequency (%d)\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int irsd200_read_hp_filter(struct irsd200_data *data, int *val,
|
||||
int *val2)
|
||||
{
|
||||
unsigned int regval;
|
||||
int ret;
|
||||
|
||||
ret = regmap_field_read(data->regfields[IRS_REGF_HP_FILTER], ®val);
|
||||
if (ret) {
|
||||
dev_err(data->dev, "Could not read hp filter frequency (%d)\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*val = irsd200_hp_filter_freq[regval][0];
|
||||
*val2 = irsd200_hp_filter_freq[regval][1];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int irsd200_write_hp_filter(struct irsd200_data *data, int val, int val2)
|
||||
{
|
||||
size_t idx;
|
||||
int ret;
|
||||
|
||||
/* Truncate fractional part to one digit. */
|
||||
val2 /= 100000;
|
||||
|
||||
for (idx = 0; idx < ARRAY_SIZE(irsd200_hp_filter_freq); ++idx) {
|
||||
if (irsd200_hp_filter_freq[idx][0] == val2)
|
||||
break;
|
||||
}
|
||||
|
||||
if (idx == ARRAY_SIZE(irsd200_hp_filter_freq) || val != 0)
|
||||
return -ERANGE;
|
||||
|
||||
ret = regmap_field_write(data->regfields[IRS_REGF_HP_FILTER], idx);
|
||||
if (ret) {
|
||||
dev_err(data->dev, "Could not write hp filter frequency (%d)\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int irsd200_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val,
|
||||
int *val2, long mask)
|
||||
{
|
||||
struct irsd200_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
s16 buf;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = irsd200_read_data(data, &buf);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*val = buf;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
ret = irsd200_read_data_rate(data, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
|
||||
ret = irsd200_read_lp_filter(data, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY:
|
||||
ret = irsd200_read_hp_filter(data, val, val2);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return IIO_VAL_FRACTIONAL;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int irsd200_read_avail(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
const int **vals, int *type, int *length,
|
||||
long mask)
|
||||
{
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
*vals = irsd200_data_rates;
|
||||
*type = IIO_VAL_INT;
|
||||
*length = ARRAY_SIZE(irsd200_data_rates);
|
||||
return IIO_AVAIL_LIST;
|
||||
case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
|
||||
*vals = irsd200_lp_filter_freq;
|
||||
*type = IIO_VAL_INT;
|
||||
*length = ARRAY_SIZE(irsd200_lp_filter_freq);
|
||||
return IIO_AVAIL_LIST;
|
||||
case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY:
|
||||
*vals = (int *)irsd200_hp_filter_freq;
|
||||
*type = IIO_VAL_FRACTIONAL;
|
||||
*length = 2 * ARRAY_SIZE(irsd200_hp_filter_freq);
|
||||
return IIO_AVAIL_LIST;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int irsd200_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int val,
|
||||
int val2, long mask)
|
||||
{
|
||||
struct irsd200_data *data = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
return irsd200_write_data_rate(data, val);
|
||||
case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
|
||||
return irsd200_write_lp_filter(data, val);
|
||||
case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY:
|
||||
return irsd200_write_hp_filter(data, val, val2);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int irsd200_read_event(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
enum iio_event_type type,
|
||||
enum iio_event_direction dir,
|
||||
enum iio_event_info info, int *val, int *val2)
|
||||
{
|
||||
struct irsd200_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (info) {
|
||||
case IIO_EV_INFO_VALUE:
|
||||
ret = irsd200_read_threshold(data, dir, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
case IIO_EV_INFO_RUNNING_PERIOD:
|
||||
ret = irsd200_read_timer(data, val, val2);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return IIO_VAL_FRACTIONAL;
|
||||
case IIO_EV_INFO_RUNNING_COUNT:
|
||||
ret = irsd200_read_nr_count(data, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int irsd200_write_event(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
enum iio_event_type type,
|
||||
enum iio_event_direction dir,
|
||||
enum iio_event_info info, int val, int val2)
|
||||
{
|
||||
struct irsd200_data *data = iio_priv(indio_dev);
|
||||
|
||||
switch (info) {
|
||||
case IIO_EV_INFO_VALUE:
|
||||
return irsd200_write_threshold(data, dir, val);
|
||||
case IIO_EV_INFO_RUNNING_PERIOD:
|
||||
return irsd200_write_timer(data, val, val2);
|
||||
case IIO_EV_INFO_RUNNING_COUNT:
|
||||
return irsd200_write_nr_count(data, val);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int irsd200_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 irsd200_data *data = iio_priv(indio_dev);
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
switch (type) {
|
||||
case IIO_EV_TYPE_THRESH:
|
||||
ret = regmap_field_read(
|
||||
data->regfields[IRS_REGF_INTR_COUNT_THR_OR], &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return val;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int irsd200_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 irsd200_data *data = iio_priv(indio_dev);
|
||||
unsigned int tmp;
|
||||
int ret;
|
||||
|
||||
switch (type) {
|
||||
case IIO_EV_TYPE_THRESH:
|
||||
/* Clear the count register (by reading from it). */
|
||||
ret = regmap_read(data->regmap, IRS_REG_COUNT, &tmp);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return regmap_field_write(
|
||||
data->regfields[IRS_REGF_INTR_COUNT_THR_OR], !!state);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static irqreturn_t irsd200_irq_thread(int irq, void *dev_id)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_id;
|
||||
struct irsd200_data *data = iio_priv(indio_dev);
|
||||
enum iio_event_direction dir;
|
||||
unsigned int lower_count;
|
||||
unsigned int upper_count;
|
||||
unsigned int status = 0;
|
||||
unsigned int source = 0;
|
||||
unsigned int clear = 0;
|
||||
unsigned int count = 0;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(data->regmap, IRS_REG_INTR, &source);
|
||||
if (ret) {
|
||||
dev_err(data->dev, "Could not read interrupt source (%d)\n",
|
||||
ret);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
ret = regmap_read(data->regmap, IRS_REG_STATUS, &status);
|
||||
if (ret) {
|
||||
dev_err(data->dev, "Could not acknowledge interrupt (%d)\n",
|
||||
ret);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
if (status & BIT(IRS_INTR_DATA) && iio_buffer_enabled(indio_dev)) {
|
||||
iio_trigger_poll_nested(indio_dev->trig);
|
||||
clear |= BIT(IRS_INTR_DATA);
|
||||
}
|
||||
|
||||
if (status & BIT(IRS_INTR_COUNT_THR_OR) &&
|
||||
source & BIT(IRS_INTR_COUNT_THR_OR)) {
|
||||
/*
|
||||
* The register value resets to zero after reading. We therefore
|
||||
* need to read once and manually extract the lower and upper
|
||||
* count register fields.
|
||||
*/
|
||||
ret = regmap_read(data->regmap, IRS_REG_COUNT, &count);
|
||||
if (ret)
|
||||
dev_err(data->dev, "Could not read count (%d)\n", ret);
|
||||
|
||||
upper_count = IRS_UPPER_COUNT(count);
|
||||
lower_count = IRS_LOWER_COUNT(count);
|
||||
|
||||
/*
|
||||
* We only check the OR mode to be able to push events for
|
||||
* rising and falling thresholds. AND mode is covered when both
|
||||
* upper and lower count is non-zero, and is signaled with
|
||||
* IIO_EV_DIR_EITHER.
|
||||
*/
|
||||
if (upper_count && !lower_count)
|
||||
dir = IIO_EV_DIR_RISING;
|
||||
else if (!upper_count && lower_count)
|
||||
dir = IIO_EV_DIR_FALLING;
|
||||
else
|
||||
dir = IIO_EV_DIR_EITHER;
|
||||
|
||||
iio_push_event(indio_dev,
|
||||
IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0,
|
||||
IIO_EV_TYPE_THRESH, dir),
|
||||
iio_get_time_ns(indio_dev));
|
||||
|
||||
/*
|
||||
* The OR mode will always trigger when the AND mode does, but
|
||||
* not vice versa. However, it seems like the AND bit needs to
|
||||
* be cleared if data capture _and_ threshold count interrupts
|
||||
* are desirable, even though it hasn't explicitly been selected
|
||||
* (with IRS_REG_INTR). Either way, it doesn't hurt...
|
||||
*/
|
||||
clear |= BIT(IRS_INTR_COUNT_THR_OR) |
|
||||
BIT(IRS_INTR_COUNT_THR_AND);
|
||||
}
|
||||
|
||||
if (!clear)
|
||||
return IRQ_NONE;
|
||||
|
||||
ret = regmap_write(data->regmap, IRS_REG_STATUS, clear);
|
||||
if (ret)
|
||||
dev_err(data->dev,
|
||||
"Could not clear interrupt status (%d)\n", ret);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t irsd200_trigger_handler(int irq, void *pollf)
|
||||
{
|
||||
struct iio_dev *indio_dev = ((struct iio_poll_func *)pollf)->indio_dev;
|
||||
struct irsd200_data *data = iio_priv(indio_dev);
|
||||
s16 buf = 0;
|
||||
int ret;
|
||||
|
||||
ret = irsd200_read_data(data, &buf);
|
||||
if (ret)
|
||||
goto end;
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, &buf,
|
||||
iio_get_time_ns(indio_dev));
|
||||
|
||||
end:
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int irsd200_set_trigger_state(struct iio_trigger *trig, bool state)
|
||||
{
|
||||
struct irsd200_data *data = iio_trigger_get_drvdata(trig);
|
||||
int ret;
|
||||
|
||||
ret = regmap_field_write(data->regfields[IRS_REGF_INTR_DATA], state);
|
||||
if (ret) {
|
||||
dev_err(data->dev, "Could not %s data interrupt source (%d)\n",
|
||||
state ? "enable" : "disable", ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct iio_info irsd200_info = {
|
||||
.read_raw = irsd200_read_raw,
|
||||
.read_avail = irsd200_read_avail,
|
||||
.write_raw = irsd200_write_raw,
|
||||
.read_event_value = irsd200_read_event,
|
||||
.write_event_value = irsd200_write_event,
|
||||
.read_event_config = irsd200_read_event_config,
|
||||
.write_event_config = irsd200_write_event_config,
|
||||
};
|
||||
|
||||
static const struct iio_trigger_ops irsd200_trigger_ops = {
|
||||
.set_trigger_state = irsd200_set_trigger_state,
|
||||
.validate_device = iio_trigger_validate_own_device,
|
||||
};
|
||||
|
||||
static const struct iio_event_spec irsd200_event_spec[] = {
|
||||
{
|
||||
.type = IIO_EV_TYPE_THRESH,
|
||||
.dir = IIO_EV_DIR_RISING,
|
||||
.mask_separate = BIT(IIO_EV_INFO_VALUE),
|
||||
},
|
||||
{
|
||||
.type = IIO_EV_TYPE_THRESH,
|
||||
.dir = IIO_EV_DIR_FALLING,
|
||||
.mask_separate = BIT(IIO_EV_INFO_VALUE),
|
||||
},
|
||||
{
|
||||
.type = IIO_EV_TYPE_THRESH,
|
||||
.dir = IIO_EV_DIR_EITHER,
|
||||
.mask_separate =
|
||||
BIT(IIO_EV_INFO_RUNNING_PERIOD) |
|
||||
BIT(IIO_EV_INFO_RUNNING_COUNT) |
|
||||
BIT(IIO_EV_INFO_ENABLE),
|
||||
},
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec irsd200_channels[] = {
|
||||
{
|
||||
.type = IIO_PROXIMITY,
|
||||
.info_mask_separate =
|
||||
BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
|
||||
BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY) |
|
||||
BIT(IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY),
|
||||
.info_mask_separate_available =
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
|
||||
BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY) |
|
||||
BIT(IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY),
|
||||
.event_spec = irsd200_event_spec,
|
||||
.num_event_specs = ARRAY_SIZE(irsd200_event_spec),
|
||||
.scan_type = {
|
||||
.sign = 's',
|
||||
.realbits = 16,
|
||||
.storagebits = 16,
|
||||
.endianness = IIO_CPU,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static int irsd200_probe(struct i2c_client *client)
|
||||
{
|
||||
struct iio_trigger *trigger;
|
||||
struct irsd200_data *data;
|
||||
struct iio_dev *indio_dev;
|
||||
size_t i;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
return dev_err_probe(&client->dev, -ENOMEM,
|
||||
"Could not allocate iio device\n");
|
||||
|
||||
data = iio_priv(indio_dev);
|
||||
data->dev = &client->dev;
|
||||
|
||||
data->regmap = devm_regmap_init_i2c(client, &irsd200_regmap_config);
|
||||
if (IS_ERR(data->regmap))
|
||||
return dev_err_probe(data->dev, PTR_ERR(data->regmap),
|
||||
"Could not initialize regmap\n");
|
||||
|
||||
for (i = 0; i < IRS_REGF_MAX; ++i) {
|
||||
data->regfields[i] = devm_regmap_field_alloc(
|
||||
data->dev, data->regmap, irsd200_regfields[i]);
|
||||
if (IS_ERR(data->regfields[i]))
|
||||
return dev_err_probe(
|
||||
data->dev, PTR_ERR(data->regfields[i]),
|
||||
"Could not allocate register field %zu\n", i);
|
||||
}
|
||||
|
||||
ret = devm_regulator_get_enable(data->dev, "vdd");
|
||||
if (ret)
|
||||
return dev_err_probe(
|
||||
data->dev, ret,
|
||||
"Could not get and enable regulator (%d)\n", ret);
|
||||
|
||||
ret = irsd200_setup(data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
indio_dev->info = &irsd200_info;
|
||||
indio_dev->name = IRS_DRV_NAME;
|
||||
indio_dev->channels = irsd200_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(irsd200_channels);
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
if (!client->irq)
|
||||
return dev_err_probe(data->dev, -ENXIO, "No irq available\n");
|
||||
|
||||
ret = devm_iio_triggered_buffer_setup(data->dev, indio_dev, NULL,
|
||||
irsd200_trigger_handler, NULL);
|
||||
if (ret)
|
||||
return dev_err_probe(
|
||||
data->dev, ret,
|
||||
"Could not setup iio triggered buffer (%d)\n", ret);
|
||||
|
||||
ret = devm_request_threaded_irq(data->dev, client->irq, NULL,
|
||||
irsd200_irq_thread,
|
||||
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
|
||||
NULL, indio_dev);
|
||||
if (ret)
|
||||
return dev_err_probe(data->dev, ret,
|
||||
"Could not request irq (%d)\n", ret);
|
||||
|
||||
trigger = devm_iio_trigger_alloc(data->dev, "%s-dev%d", indio_dev->name,
|
||||
iio_device_id(indio_dev));
|
||||
if (!trigger)
|
||||
return dev_err_probe(data->dev, -ENOMEM,
|
||||
"Could not allocate iio trigger\n");
|
||||
|
||||
trigger->ops = &irsd200_trigger_ops;
|
||||
iio_trigger_set_drvdata(trigger, data);
|
||||
|
||||
ret = devm_iio_trigger_register(data->dev, trigger);
|
||||
if (ret)
|
||||
return dev_err_probe(data->dev, ret,
|
||||
"Could not register iio trigger (%d)\n",
|
||||
ret);
|
||||
|
||||
ret = devm_iio_device_register(data->dev, indio_dev);
|
||||
if (ret)
|
||||
return dev_err_probe(data->dev, ret,
|
||||
"Could not register iio device (%d)\n",
|
||||
ret);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id irsd200_of_match[] = {
|
||||
{
|
||||
.compatible = "murata,irsd200",
|
||||
},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, irsd200_of_match);
|
||||
|
||||
static struct i2c_driver irsd200_driver = {
|
||||
.driver = {
|
||||
.name = IRS_DRV_NAME,
|
||||
.of_match_table = irsd200_of_match,
|
||||
},
|
||||
.probe = irsd200_probe,
|
||||
};
|
||||
module_i2c_driver(irsd200_driver);
|
||||
|
||||
MODULE_AUTHOR("Waqar Hameed <waqar.hameed@axis.com>");
|
||||
MODULE_DESCRIPTION("Murata IRS-D200 PIR sensor driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -12,7 +12,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
|
@ -76,7 +76,7 @@ static s16 mb1232_read_distance(struct mb1232_data *data)
|
||||
goto error_unlock;
|
||||
}
|
||||
|
||||
if (data->irqnr >= 0) {
|
||||
if (data->irqnr > 0) {
|
||||
/* it cannot take more than 100 ms */
|
||||
ret = wait_for_completion_killable_timeout(&data->ranging,
|
||||
HZ/10);
|
||||
@ -212,10 +212,7 @@ static int mb1232_probe(struct i2c_client *client)
|
||||
init_completion(&data->ranging);
|
||||
|
||||
data->irqnr = fwnode_irq_get(dev_fwnode(&client->dev), 0);
|
||||
if (data->irqnr <= 0) {
|
||||
/* usage of interrupt is optional */
|
||||
data->irqnr = -1;
|
||||
} else {
|
||||
if (data->irqnr > 0) {
|
||||
ret = devm_request_irq(dev, data->irqnr, mb1232_handle_irq,
|
||||
IRQF_TRIGGER_FALLING, id->name, indio_dev);
|
||||
if (ret < 0) {
|
||||
|
@ -27,8 +27,8 @@
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
|
@ -73,7 +73,6 @@ static int stm32_lptim_trigger_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct stm32_lptim_trigger *priv;
|
||||
u32 index;
|
||||
int ret;
|
||||
|
||||
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
@ -88,13 +87,7 @@ static int stm32_lptim_trigger_probe(struct platform_device *pdev)
|
||||
priv->dev = &pdev->dev;
|
||||
priv->trg = stm32_lptim_triggers[index];
|
||||
|
||||
ret = stm32_lptim_setup_trig(priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
platform_set_drvdata(pdev, priv);
|
||||
|
||||
return 0;
|
||||
return stm32_lptim_setup_trig(priv);
|
||||
}
|
||||
|
||||
static const struct of_device_id stm32_lptim_trig_of_match[] = {
|
||||
|
@ -6,58 +6,60 @@
|
||||
#ifndef _DT_BINDINGS_QCOM_SPMI_VADC_PM8350_H
|
||||
#define _DT_BINDINGS_QCOM_SPMI_VADC_PM8350_H
|
||||
|
||||
/* ADC channels for PM8350_ADC for PMIC7 */
|
||||
#define PM8350_ADC7_REF_GND(sid) ((sid) << 8 | 0x0)
|
||||
#define PM8350_ADC7_1P25VREF(sid) ((sid) << 8 | 0x01)
|
||||
#define PM8350_ADC7_VREF_VADC(sid) ((sid) << 8 | 0x02)
|
||||
#define PM8350_ADC7_DIE_TEMP(sid) ((sid) << 8 | 0x03)
|
||||
#include <dt-bindings/iio/qcom,spmi-vadc.h>
|
||||
|
||||
#define PM8350_ADC7_AMUX_THM1(sid) ((sid) << 8 | 0x04)
|
||||
#define PM8350_ADC7_AMUX_THM2(sid) ((sid) << 8 | 0x05)
|
||||
#define PM8350_ADC7_AMUX_THM3(sid) ((sid) << 8 | 0x06)
|
||||
#define PM8350_ADC7_AMUX_THM4(sid) ((sid) << 8 | 0x07)
|
||||
#define PM8350_ADC7_AMUX_THM5(sid) ((sid) << 8 | 0x08)
|
||||
#define PM8350_ADC7_GPIO1(sid) ((sid) << 8 | 0x0a)
|
||||
#define PM8350_ADC7_GPIO2(sid) ((sid) << 8 | 0x0b)
|
||||
#define PM8350_ADC7_GPIO3(sid) ((sid) << 8 | 0x0c)
|
||||
#define PM8350_ADC7_GPIO4(sid) ((sid) << 8 | 0x0d)
|
||||
/* ADC channels for PM8350_ADC for PMIC7 */
|
||||
#define PM8350_ADC7_REF_GND(sid) ((sid) << 8 | ADC7_REF_GND)
|
||||
#define PM8350_ADC7_1P25VREF(sid) ((sid) << 8 | ADC7_1P25VREF)
|
||||
#define PM8350_ADC7_VREF_VADC(sid) ((sid) << 8 | ADC7_VREF_VADC)
|
||||
#define PM8350_ADC7_DIE_TEMP(sid) ((sid) << 8 | ADC7_DIE_TEMP)
|
||||
|
||||
#define PM8350_ADC7_AMUX_THM1(sid) ((sid) << 8 | ADC7_AMUX_THM1)
|
||||
#define PM8350_ADC7_AMUX_THM2(sid) ((sid) << 8 | ADC7_AMUX_THM2)
|
||||
#define PM8350_ADC7_AMUX_THM3(sid) ((sid) << 8 | ADC7_AMUX_THM3)
|
||||
#define PM8350_ADC7_AMUX_THM4(sid) ((sid) << 8 | ADC7_AMUX_THM4)
|
||||
#define PM8350_ADC7_AMUX_THM5(sid) ((sid) << 8 | ADC7_AMUX_THM5)
|
||||
#define PM8350_ADC7_GPIO1(sid) ((sid) << 8 | ADC7_GPIO1)
|
||||
#define PM8350_ADC7_GPIO2(sid) ((sid) << 8 | ADC7_GPIO2)
|
||||
#define PM8350_ADC7_GPIO3(sid) ((sid) << 8 | ADC7_GPIO3)
|
||||
#define PM8350_ADC7_GPIO4(sid) ((sid) << 8 | ADC7_GPIO4)
|
||||
|
||||
/* 30k pull-up1 */
|
||||
#define PM8350_ADC7_AMUX_THM1_30K_PU(sid) ((sid) << 8 | 0x24)
|
||||
#define PM8350_ADC7_AMUX_THM2_30K_PU(sid) ((sid) << 8 | 0x25)
|
||||
#define PM8350_ADC7_AMUX_THM3_30K_PU(sid) ((sid) << 8 | 0x26)
|
||||
#define PM8350_ADC7_AMUX_THM4_30K_PU(sid) ((sid) << 8 | 0x27)
|
||||
#define PM8350_ADC7_AMUX_THM5_30K_PU(sid) ((sid) << 8 | 0x28)
|
||||
#define PM8350_ADC7_GPIO1_30K_PU(sid) ((sid) << 8 | 0x2a)
|
||||
#define PM8350_ADC7_GPIO2_30K_PU(sid) ((sid) << 8 | 0x2b)
|
||||
#define PM8350_ADC7_GPIO3_30K_PU(sid) ((sid) << 8 | 0x2c)
|
||||
#define PM8350_ADC7_GPIO4_30K_PU(sid) ((sid) << 8 | 0x2d)
|
||||
#define PM8350_ADC7_AMUX_THM1_30K_PU(sid) ((sid) << 8 | ADC7_AMUX_THM1_30K_PU)
|
||||
#define PM8350_ADC7_AMUX_THM2_30K_PU(sid) ((sid) << 8 | ADC7_AMUX_THM2_30K_PU)
|
||||
#define PM8350_ADC7_AMUX_THM3_30K_PU(sid) ((sid) << 8 | ADC7_AMUX_THM3_30K_PU)
|
||||
#define PM8350_ADC7_AMUX_THM4_30K_PU(sid) ((sid) << 8 | ADC7_AMUX_THM4_30K_PU)
|
||||
#define PM8350_ADC7_AMUX_THM5_30K_PU(sid) ((sid) << 8 | ADC7_AMUX_THM5_30K_PU)
|
||||
#define PM8350_ADC7_GPIO1_30K_PU(sid) ((sid) << 8 | ADC7_GPIO1_30K_PU)
|
||||
#define PM8350_ADC7_GPIO2_30K_PU(sid) ((sid) << 8 | ADC7_GPIO2_30K_PU)
|
||||
#define PM8350_ADC7_GPIO3_30K_PU(sid) ((sid) << 8 | ADC7_GPIO3_30K_PU)
|
||||
#define PM8350_ADC7_GPIO4_30K_PU(sid) ((sid) << 8 | ADC7_GPIO4_30K_PU)
|
||||
|
||||
/* 100k pull-up2 */
|
||||
#define PM8350_ADC7_AMUX_THM1_100K_PU(sid) ((sid) << 8 | 0x44)
|
||||
#define PM8350_ADC7_AMUX_THM2_100K_PU(sid) ((sid) << 8 | 0x45)
|
||||
#define PM8350_ADC7_AMUX_THM3_100K_PU(sid) ((sid) << 8 | 0x46)
|
||||
#define PM8350_ADC7_AMUX_THM4_100K_PU(sid) ((sid) << 8 | 0x47)
|
||||
#define PM8350_ADC7_AMUX_THM5_100K_PU(sid) ((sid) << 8 | 0x48)
|
||||
#define PM8350_ADC7_GPIO1_100K_PU(sid) ((sid) << 8 | 0x4a)
|
||||
#define PM8350_ADC7_GPIO2_100K_PU(sid) ((sid) << 8 | 0x4b)
|
||||
#define PM8350_ADC7_GPIO3_100K_PU(sid) ((sid) << 8 | 0x4c)
|
||||
#define PM8350_ADC7_GPIO4_100K_PU(sid) ((sid) << 8 | 0x4d)
|
||||
#define PM8350_ADC7_AMUX_THM1_100K_PU(sid) ((sid) << 8 | ADC7_AMUX_THM1_100K_PU)
|
||||
#define PM8350_ADC7_AMUX_THM2_100K_PU(sid) ((sid) << 8 | ADC7_AMUX_THM2_100K_PU)
|
||||
#define PM8350_ADC7_AMUX_THM3_100K_PU(sid) ((sid) << 8 | ADC7_AMUX_THM3_100K_PU)
|
||||
#define PM8350_ADC7_AMUX_THM4_100K_PU(sid) ((sid) << 8 | ADC7_AMUX_THM4_100K_PU)
|
||||
#define PM8350_ADC7_AMUX_THM5_100K_PU(sid) ((sid) << 8 | ADC7_AMUX_THM5_100K_PU)
|
||||
#define PM8350_ADC7_GPIO1_100K_PU(sid) ((sid) << 8 | ADC7_GPIO1_100K_PU)
|
||||
#define PM8350_ADC7_GPIO2_100K_PU(sid) ((sid) << 8 | ADC7_GPIO2_100K_PU)
|
||||
#define PM8350_ADC7_GPIO3_100K_PU(sid) ((sid) << 8 | ADC7_GPIO3_100K_PU)
|
||||
#define PM8350_ADC7_GPIO4_100K_PU(sid) ((sid) << 8 | ADC7_GPIO4_100K_PU)
|
||||
|
||||
/* 400k pull-up3 */
|
||||
#define PM8350_ADC7_AMUX_THM1_400K_PU(sid) ((sid) << 8 | 0x64)
|
||||
#define PM8350_ADC7_AMUX_THM2_400K_PU(sid) ((sid) << 8 | 0x65)
|
||||
#define PM8350_ADC7_AMUX_THM3_400K_PU(sid) ((sid) << 8 | 0x66)
|
||||
#define PM8350_ADC7_AMUX_THM4_400K_PU(sid) ((sid) << 8 | 0x67)
|
||||
#define PM8350_ADC7_AMUX_THM5_400K_PU(sid) ((sid) << 8 | 0x68)
|
||||
#define PM8350_ADC7_GPIO1_400K_PU(sid) ((sid) << 8 | 0x6a)
|
||||
#define PM8350_ADC7_GPIO2_400K_PU(sid) ((sid) << 8 | 0x6b)
|
||||
#define PM8350_ADC7_GPIO3_400K_PU(sid) ((sid) << 8 | 0x6c)
|
||||
#define PM8350_ADC7_GPIO4_400K_PU(sid) ((sid) << 8 | 0x6d)
|
||||
#define PM8350_ADC7_AMUX_THM1_400K_PU(sid) ((sid) << 8 | ADC7_AMUX_THM1_400K_PU)
|
||||
#define PM8350_ADC7_AMUX_THM2_400K_PU(sid) ((sid) << 8 | ADC7_AMUX_THM2_400K_PU)
|
||||
#define PM8350_ADC7_AMUX_THM3_400K_PU(sid) ((sid) << 8 | ADC7_AMUX_THM3_400K_PU)
|
||||
#define PM8350_ADC7_AMUX_THM4_400K_PU(sid) ((sid) << 8 | ADC7_AMUX_THM4_400K_PU)
|
||||
#define PM8350_ADC7_AMUX_THM5_400K_PU(sid) ((sid) << 8 | ADC7_AMUX_THM5_400K_PU)
|
||||
#define PM8350_ADC7_GPIO1_400K_PU(sid) ((sid) << 8 | ADC7_GPIO1_400K_PU)
|
||||
#define PM8350_ADC7_GPIO2_400K_PU(sid) ((sid) << 8 | ADC7_GPIO2_400K_PU)
|
||||
#define PM8350_ADC7_GPIO3_400K_PU(sid) ((sid) << 8 | ADC7_GPIO3_400K_PU)
|
||||
#define PM8350_ADC7_GPIO4_400K_PU(sid) ((sid) << 8 | ADC7_GPIO4_400K_PU)
|
||||
|
||||
/* 1/3 Divider */
|
||||
#define PM8350_ADC7_GPIO4_DIV3(sid) ((sid) << 8 | 0x8d)
|
||||
#define PM8350_ADC7_GPIO4_DIV3(sid) ((sid) << 8 | ADC7_GPIO4_DIV3)
|
||||
|
||||
#define PM8350_ADC7_VPH_PWR(sid) ((sid) << 8 | 0x8e)
|
||||
#define PM8350_ADC7_VPH_PWR(sid) ((sid) << 8 | ADC7_VPH_PWR)
|
||||
|
||||
#endif /* _DT_BINDINGS_QCOM_SPMI_VADC_PM8350_H */
|
||||
|
@ -10,79 +10,81 @@
|
||||
#define PM8350B_SID 3
|
||||
#endif
|
||||
|
||||
#include <dt-bindings/iio/qcom,spmi-vadc.h>
|
||||
|
||||
/* ADC channels for PM8350B_ADC for PMIC7 */
|
||||
#define PM8350B_ADC7_REF_GND (PM8350B_SID << 8 | 0x0)
|
||||
#define PM8350B_ADC7_1P25VREF (PM8350B_SID << 8 | 0x01)
|
||||
#define PM8350B_ADC7_VREF_VADC (PM8350B_SID << 8 | 0x02)
|
||||
#define PM8350B_ADC7_DIE_TEMP (PM8350B_SID << 8 | 0x03)
|
||||
#define PM8350B_ADC7_REF_GND (PM8350B_SID << 8 | ADC7_REF_GND)
|
||||
#define PM8350B_ADC7_1P25VREF (PM8350B_SID << 8 | ADC7_1P25VREF)
|
||||
#define PM8350B_ADC7_VREF_VADC (PM8350B_SID << 8 | ADC7_VREF_VADC)
|
||||
#define PM8350B_ADC7_DIE_TEMP (PM8350B_SID << 8 | ADC7_DIE_TEMP)
|
||||
|
||||
#define PM8350B_ADC7_AMUX_THM1 (PM8350B_SID << 8 | 0x04)
|
||||
#define PM8350B_ADC7_AMUX_THM2 (PM8350B_SID << 8 | 0x05)
|
||||
#define PM8350B_ADC7_AMUX_THM3 (PM8350B_SID << 8 | 0x06)
|
||||
#define PM8350B_ADC7_AMUX_THM4 (PM8350B_SID << 8 | 0x07)
|
||||
#define PM8350B_ADC7_AMUX_THM5 (PM8350B_SID << 8 | 0x08)
|
||||
#define PM8350B_ADC7_AMUX_THM6 (PM8350B_SID << 8 | 0x09)
|
||||
#define PM8350B_ADC7_GPIO1 (PM8350B_SID << 8 | 0x0a)
|
||||
#define PM8350B_ADC7_GPIO2 (PM8350B_SID << 8 | 0x0b)
|
||||
#define PM8350B_ADC7_GPIO3 (PM8350B_SID << 8 | 0x0c)
|
||||
#define PM8350B_ADC7_GPIO4 (PM8350B_SID << 8 | 0x0d)
|
||||
#define PM8350B_ADC7_AMUX_THM1 (PM8350B_SID << 8 | ADC7_AMUX_THM1)
|
||||
#define PM8350B_ADC7_AMUX_THM2 (PM8350B_SID << 8 | ADC7_AMUX_THM2)
|
||||
#define PM8350B_ADC7_AMUX_THM3 (PM8350B_SID << 8 | ADC7_AMUX_THM3)
|
||||
#define PM8350B_ADC7_AMUX_THM4 (PM8350B_SID << 8 | ADC7_AMUX_THM4)
|
||||
#define PM8350B_ADC7_AMUX_THM5 (PM8350B_SID << 8 | ADC7_AMUX_THM5)
|
||||
#define PM8350B_ADC7_AMUX_THM6 (PM8350B_SID << 8 | ADC7_AMUX_THM6)
|
||||
#define PM8350B_ADC7_GPIO1 (PM8350B_SID << 8 | ADC7_GPIO1)
|
||||
#define PM8350B_ADC7_GPIO2 (PM8350B_SID << 8 | ADC7_GPIO2)
|
||||
#define PM8350B_ADC7_GPIO3 (PM8350B_SID << 8 | ADC7_GPIO3)
|
||||
#define PM8350B_ADC7_GPIO4 (PM8350B_SID << 8 | ADC7_GPIO4)
|
||||
|
||||
#define PM8350B_ADC7_CHG_TEMP (PM8350B_SID << 8 | 0x10)
|
||||
#define PM8350B_ADC7_USB_IN_V_16 (PM8350B_SID << 8 | 0x11)
|
||||
#define PM8350B_ADC7_VDC_16 (PM8350B_SID << 8 | 0x12)
|
||||
#define PM8350B_ADC7_CC1_ID (PM8350B_SID << 8 | 0x13)
|
||||
#define PM8350B_ADC7_VREF_BAT_THERM (PM8350B_SID << 8 | 0x15)
|
||||
#define PM8350B_ADC7_IIN_FB (PM8350B_SID << 8 | 0x17)
|
||||
#define PM8350B_ADC7_CHG_TEMP (PM8350B_SID << 8 | ADC7_CHG_TEMP)
|
||||
#define PM8350B_ADC7_USB_IN_V_16 (PM8350B_SID << 8 | ADC7_USB_IN_V_16)
|
||||
#define PM8350B_ADC7_VDC_16 (PM8350B_SID << 8 | ADC7_VDC_16)
|
||||
#define PM8350B_ADC7_CC1_ID (PM8350B_SID << 8 | ADC7_CC1_ID)
|
||||
#define PM8350B_ADC7_VREF_BAT_THERM (PM8350B_SID << 8 | ADC7_VREF_BAT_THERM)
|
||||
#define PM8350B_ADC7_IIN_FB (PM8350B_SID << 8 | ADC7_IIN_FB)
|
||||
|
||||
/* 30k pull-up1 */
|
||||
#define PM8350B_ADC7_AMUX_THM1_30K_PU (PM8350B_SID << 8 | 0x24)
|
||||
#define PM8350B_ADC7_AMUX_THM2_30K_PU (PM8350B_SID << 8 | 0x25)
|
||||
#define PM8350B_ADC7_AMUX_THM3_30K_PU (PM8350B_SID << 8 | 0x26)
|
||||
#define PM8350B_ADC7_AMUX_THM4_30K_PU (PM8350B_SID << 8 | 0x27)
|
||||
#define PM8350B_ADC7_AMUX_THM5_30K_PU (PM8350B_SID << 8 | 0x28)
|
||||
#define PM8350B_ADC7_AMUX_THM6_30K_PU (PM8350B_SID << 8 | 0x29)
|
||||
#define PM8350B_ADC7_GPIO1_30K_PU (PM8350B_SID << 8 | 0x2a)
|
||||
#define PM8350B_ADC7_GPIO2_30K_PU (PM8350B_SID << 8 | 0x2b)
|
||||
#define PM8350B_ADC7_GPIO3_30K_PU (PM8350B_SID << 8 | 0x2c)
|
||||
#define PM8350B_ADC7_GPIO4_30K_PU (PM8350B_SID << 8 | 0x2d)
|
||||
#define PM8350B_ADC7_CC1_ID_30K_PU (PM8350B_SID << 8 | 0x33)
|
||||
#define PM8350B_ADC7_AMUX_THM1_30K_PU (PM8350B_SID << 8 | ADC7_AMUX_THM1_30K_PU)
|
||||
#define PM8350B_ADC7_AMUX_THM2_30K_PU (PM8350B_SID << 8 | ADC7_AMUX_THM2_30K_PU)
|
||||
#define PM8350B_ADC7_AMUX_THM3_30K_PU (PM8350B_SID << 8 | ADC7_AMUX_THM3_30K_PU)
|
||||
#define PM8350B_ADC7_AMUX_THM4_30K_PU (PM8350B_SID << 8 | ADC7_AMUX_THM4_30K_PU)
|
||||
#define PM8350B_ADC7_AMUX_THM5_30K_PU (PM8350B_SID << 8 | ADC7_AMUX_THM5_30K_PU)
|
||||
#define PM8350B_ADC7_AMUX_THM6_30K_PU (PM8350B_SID << 8 | ADC7_AMUX_THM6_30K_PU)
|
||||
#define PM8350B_ADC7_GPIO1_30K_PU (PM8350B_SID << 8 | ADC7_GPIO1_30K_PU)
|
||||
#define PM8350B_ADC7_GPIO2_30K_PU (PM8350B_SID << 8 | ADC7_GPIO2_30K_PU)
|
||||
#define PM8350B_ADC7_GPIO3_30K_PU (PM8350B_SID << 8 | ADC7_GPIO3_30K_PU)
|
||||
#define PM8350B_ADC7_GPIO4_30K_PU (PM8350B_SID << 8 | ADC7_GPIO4_30K_PU)
|
||||
#define PM8350B_ADC7_CC1_ID_30K_PU (PM8350B_SID << 8 | ADC7_CC1_ID_30K_PU)
|
||||
|
||||
/* 100k pull-up2 */
|
||||
#define PM8350B_ADC7_AMUX_THM1_100K_PU (PM8350B_SID << 8 | 0x44)
|
||||
#define PM8350B_ADC7_AMUX_THM2_100K_PU (PM8350B_SID << 8 | 0x45)
|
||||
#define PM8350B_ADC7_AMUX_THM3_100K_PU (PM8350B_SID << 8 | 0x46)
|
||||
#define PM8350B_ADC7_AMUX_THM4_100K_PU (PM8350B_SID << 8 | 0x47)
|
||||
#define PM8350B_ADC7_AMUX_THM5_100K_PU (PM8350B_SID << 8 | 0x48)
|
||||
#define PM8350B_ADC7_AMUX_THM6_100K_PU (PM8350B_SID << 8 | 0x49)
|
||||
#define PM8350B_ADC7_GPIO1_100K_PU (PM8350B_SID << 8 | 0x4a)
|
||||
#define PM8350B_ADC7_GPIO2_100K_PU (PM8350B_SID << 8 | 0x4b)
|
||||
#define PM8350B_ADC7_GPIO3_100K_PU (PM8350B_SID << 8 | 0x4c)
|
||||
#define PM8350B_ADC7_GPIO4_100K_PU (PM8350B_SID << 8 | 0x4d)
|
||||
#define PM8350B_ADC7_CC1_ID_100K_PU (PM8350B_SID << 8 | 0x53)
|
||||
#define PM8350B_ADC7_AMUX_THM1_100K_PU (PM8350B_SID << 8 | ADC7_AMUX_THM1_100K_PU)
|
||||
#define PM8350B_ADC7_AMUX_THM2_100K_PU (PM8350B_SID << 8 | ADC7_AMUX_THM2_100K_PU)
|
||||
#define PM8350B_ADC7_AMUX_THM3_100K_PU (PM8350B_SID << 8 | ADC7_AMUX_THM3_100K_PU)
|
||||
#define PM8350B_ADC7_AMUX_THM4_100K_PU (PM8350B_SID << 8 | ADC7_AMUX_THM4_100K_PU)
|
||||
#define PM8350B_ADC7_AMUX_THM5_100K_PU (PM8350B_SID << 8 | ADC7_AMUX_THM5_100K_PU)
|
||||
#define PM8350B_ADC7_AMUX_THM6_100K_PU (PM8350B_SID << 8 | ADC7_AMUX_THM6_100K_PU)
|
||||
#define PM8350B_ADC7_GPIO1_100K_PU (PM8350B_SID << 8 | ADC7_GPIO1_100K_PU)
|
||||
#define PM8350B_ADC7_GPIO2_100K_PU (PM8350B_SID << 8 | ADC7_GPIO2_100K_PU)
|
||||
#define PM8350B_ADC7_GPIO3_100K_PU (PM8350B_SID << 8 | ADC7_GPIO3_100K_PU)
|
||||
#define PM8350B_ADC7_GPIO4_100K_PU (PM8350B_SID << 8 | ADC7_GPIO4_100K_PU)
|
||||
#define PM8350B_ADC7_CC1_ID_100K_PU (PM8350B_SID << 8 | ADC7_CC1_ID_100K_PU)
|
||||
|
||||
/* 400k pull-up3 */
|
||||
#define PM8350B_ADC7_AMUX_THM1_400K_PU (PM8350B_SID << 8 | 0x64)
|
||||
#define PM8350B_ADC7_AMUX_THM2_400K_PU (PM8350B_SID << 8 | 0x65)
|
||||
#define PM8350B_ADC7_AMUX_THM3_400K_PU (PM8350B_SID << 8 | 0x66)
|
||||
#define PM8350B_ADC7_AMUX_THM4_400K_PU (PM8350B_SID << 8 | 0x67)
|
||||
#define PM8350B_ADC7_AMUX_THM5_400K_PU (PM8350B_SID << 8 | 0x68)
|
||||
#define PM8350B_ADC7_AMUX_THM6_400K_PU (PM8350B_SID << 8 | 0x69)
|
||||
#define PM8350B_ADC7_GPIO1_400K_PU (PM8350B_SID << 8 | 0x6a)
|
||||
#define PM8350B_ADC7_GPIO2_400K_PU (PM8350B_SID << 8 | 0x6b)
|
||||
#define PM8350B_ADC7_GPIO3_400K_PU (PM8350B_SID << 8 | 0x6c)
|
||||
#define PM8350B_ADC7_GPIO4_400K_PU (PM8350B_SID << 8 | 0x6d)
|
||||
#define PM8350B_ADC7_CC1_ID_400K_PU (PM8350B_SID << 8 | 0x73)
|
||||
#define PM8350B_ADC7_AMUX_THM1_400K_PU (PM8350B_SID << 8 | ADC7_AMUX_THM1_400K_PU)
|
||||
#define PM8350B_ADC7_AMUX_THM2_400K_PU (PM8350B_SID << 8 | ADC7_AMUX_THM2_400K_PU)
|
||||
#define PM8350B_ADC7_AMUX_THM3_400K_PU (PM8350B_SID << 8 | ADC7_AMUX_THM3_400K_PU)
|
||||
#define PM8350B_ADC7_AMUX_THM4_400K_PU (PM8350B_SID << 8 | ADC7_AMUX_THM4_400K_PU)
|
||||
#define PM8350B_ADC7_AMUX_THM5_400K_PU (PM8350B_SID << 8 | ADC7_AMUX_THM5_400K_PU)
|
||||
#define PM8350B_ADC7_AMUX_THM6_400K_PU (PM8350B_SID << 8 | ADC7_AMUX_THM6_400K_PU)
|
||||
#define PM8350B_ADC7_GPIO1_400K_PU (PM8350B_SID << 8 | ADC7_GPIO1_400K_PU)
|
||||
#define PM8350B_ADC7_GPIO2_400K_PU (PM8350B_SID << 8 | ADC7_GPIO2_400K_PU)
|
||||
#define PM8350B_ADC7_GPIO3_400K_PU (PM8350B_SID << 8 | ADC7_GPIO3_400K_PU)
|
||||
#define PM8350B_ADC7_GPIO4_400K_PU (PM8350B_SID << 8 | ADC7_GPIO4_400K_PU)
|
||||
#define PM8350B_ADC7_CC1_ID_400K_PU (PM8350B_SID << 8 | ADC7_CC1_ID_400K_PU)
|
||||
|
||||
/* 1/3 Divider */
|
||||
#define PM8350B_ADC7_GPIO1_DIV3 (PM8350B_SID << 8 | 0x8a)
|
||||
#define PM8350B_ADC7_GPIO2_DIV3 (PM8350B_SID << 8 | 0x8b)
|
||||
#define PM8350B_ADC7_GPIO3_DIV3 (PM8350B_SID << 8 | 0x8c)
|
||||
#define PM8350B_ADC7_GPIO4_DIV3 (PM8350B_SID << 8 | 0x8d)
|
||||
#define PM8350B_ADC7_GPIO1_DIV3 (PM8350B_SID << 8 | ADC7_GPIO1_DIV3)
|
||||
#define PM8350B_ADC7_GPIO2_DIV3 (PM8350B_SID << 8 | ADC7_GPIO2_DIV3)
|
||||
#define PM8350B_ADC7_GPIO3_DIV3 (PM8350B_SID << 8 | ADC7_GPIO3_DIV3)
|
||||
#define PM8350B_ADC7_GPIO4_DIV3 (PM8350B_SID << 8 | ADC7_GPIO4_DIV3)
|
||||
|
||||
#define PM8350B_ADC7_VPH_PWR (PM8350B_SID << 8 | 0x8e)
|
||||
#define PM8350B_ADC7_VBAT_SNS (PM8350B_SID << 8 | 0x8f)
|
||||
#define PM8350B_ADC7_VPH_PWR (PM8350B_SID << 8 | ADC7_VPH_PWR)
|
||||
#define PM8350B_ADC7_VBAT_SNS (PM8350B_SID << 8 | ADC7_VBAT_SNS)
|
||||
|
||||
#define PM8350B_ADC7_SBUx (PM8350B_SID << 8 | 0x94)
|
||||
#define PM8350B_ADC7_VBAT_2S_MID (PM8350B_SID << 8 | 0x96)
|
||||
#define PM8350B_ADC7_SBUx (PM8350B_SID << 8 | ADC7_SBU)
|
||||
#define PM8350B_ADC7_VBAT_2S_MID (PM8350B_SID << 8 | ADC7_VBAT_2S_MID)
|
||||
|
||||
#endif /* _DT_BINDINGS_QCOM_SPMI_VADC_PM8350B_H */
|
||||
|
@ -10,37 +10,39 @@
|
||||
#define PMK8350_SID 0
|
||||
#endif
|
||||
|
||||
/* ADC channels for PMK8350_ADC for PMIC7 */
|
||||
#define PMK8350_ADC7_REF_GND (PMK8350_SID << 8 | 0x0)
|
||||
#define PMK8350_ADC7_1P25VREF (PMK8350_SID << 8 | 0x01)
|
||||
#define PMK8350_ADC7_VREF_VADC (PMK8350_SID << 8 | 0x02)
|
||||
#define PMK8350_ADC7_DIE_TEMP (PMK8350_SID << 8 | 0x03)
|
||||
#include <dt-bindings/iio/qcom,spmi-vadc.h>
|
||||
|
||||
#define PMK8350_ADC7_AMUX_THM1 (PMK8350_SID << 8 | 0x04)
|
||||
#define PMK8350_ADC7_AMUX_THM2 (PMK8350_SID << 8 | 0x05)
|
||||
#define PMK8350_ADC7_AMUX_THM3 (PMK8350_SID << 8 | 0x06)
|
||||
#define PMK8350_ADC7_AMUX_THM4 (PMK8350_SID << 8 | 0x07)
|
||||
#define PMK8350_ADC7_AMUX_THM5 (PMK8350_SID << 8 | 0x08)
|
||||
/* ADC channels for PMK8350_ADC for PMIC7 */
|
||||
#define PMK8350_ADC7_REF_GND (PMK8350_SID << 8 | ADC7_REF_GND)
|
||||
#define PMK8350_ADC7_1P25VREF (PMK8350_SID << 8 | ADC7_1P25VREF)
|
||||
#define PMK8350_ADC7_VREF_VADC (PMK8350_SID << 8 | ADC7_VREF_VADC)
|
||||
#define PMK8350_ADC7_DIE_TEMP (PMK8350_SID << 8 | ADC7_DIE_TEMP)
|
||||
|
||||
#define PMK8350_ADC7_AMUX_THM1 (PMK8350_SID << 8 | ADC7_AMUX_THM1)
|
||||
#define PMK8350_ADC7_AMUX_THM2 (PMK8350_SID << 8 | ADC7_AMUX_THM2)
|
||||
#define PMK8350_ADC7_AMUX_THM3 (PMK8350_SID << 8 | ADC7_AMUX_THM3)
|
||||
#define PMK8350_ADC7_AMUX_THM4 (PMK8350_SID << 8 | ADC7_AMUX_THM4)
|
||||
#define PMK8350_ADC7_AMUX_THM5 (PMK8350_SID << 8 | ADC7_AMUX_THM5)
|
||||
|
||||
/* 30k pull-up1 */
|
||||
#define PMK8350_ADC7_AMUX_THM1_30K_PU (PMK8350_SID << 8 | 0x24)
|
||||
#define PMK8350_ADC7_AMUX_THM2_30K_PU (PMK8350_SID << 8 | 0x25)
|
||||
#define PMK8350_ADC7_AMUX_THM3_30K_PU (PMK8350_SID << 8 | 0x26)
|
||||
#define PMK8350_ADC7_AMUX_THM4_30K_PU (PMK8350_SID << 8 | 0x27)
|
||||
#define PMK8350_ADC7_AMUX_THM5_30K_PU (PMK8350_SID << 8 | 0x28)
|
||||
#define PMK8350_ADC7_AMUX_THM1_30K_PU (PMK8350_SID << 8 | ADC7_AMUX_THM1_30K_PU)
|
||||
#define PMK8350_ADC7_AMUX_THM2_30K_PU (PMK8350_SID << 8 | ADC7_AMUX_THM2_30K_PU)
|
||||
#define PMK8350_ADC7_AMUX_THM3_30K_PU (PMK8350_SID << 8 | ADC7_AMUX_THM3_30K_PU)
|
||||
#define PMK8350_ADC7_AMUX_THM4_30K_PU (PMK8350_SID << 8 | ADC7_AMUX_THM4_30K_PU)
|
||||
#define PMK8350_ADC7_AMUX_THM5_30K_PU (PMK8350_SID << 8 | ADC7_AMUX_THM5_30K_PU)
|
||||
|
||||
/* 100k pull-up2 */
|
||||
#define PMK8350_ADC7_AMUX_THM1_100K_PU (PMK8350_SID << 8 | 0x44)
|
||||
#define PMK8350_ADC7_AMUX_THM2_100K_PU (PMK8350_SID << 8 | 0x45)
|
||||
#define PMK8350_ADC7_AMUX_THM3_100K_PU (PMK8350_SID << 8 | 0x46)
|
||||
#define PMK8350_ADC7_AMUX_THM4_100K_PU (PMK8350_SID << 8 | 0x47)
|
||||
#define PMK8350_ADC7_AMUX_THM5_100K_PU (PMK8350_SID << 8 | 0x48)
|
||||
#define PMK8350_ADC7_AMUX_THM1_100K_PU (PMK8350_SID << 8 | ADC7_AMUX_THM1_100K_PU)
|
||||
#define PMK8350_ADC7_AMUX_THM2_100K_PU (PMK8350_SID << 8 | ADC7_AMUX_THM2_100K_PU)
|
||||
#define PMK8350_ADC7_AMUX_THM3_100K_PU (PMK8350_SID << 8 | ADC7_AMUX_THM3_100K_PU)
|
||||
#define PMK8350_ADC7_AMUX_THM4_100K_PU (PMK8350_SID << 8 | ADC7_AMUX_THM4_100K_PU)
|
||||
#define PMK8350_ADC7_AMUX_THM5_100K_PU (PMK8350_SID << 8 | ADC7_AMUX_THM5_100K_PU)
|
||||
|
||||
/* 400k pull-up3 */
|
||||
#define PMK8350_ADC7_AMUX_THM1_400K_PU (PMK8350_SID << 8 | 0x64)
|
||||
#define PMK8350_ADC7_AMUX_THM2_400K_PU (PMK8350_SID << 8 | 0x65)
|
||||
#define PMK8350_ADC7_AMUX_THM3_400K_PU (PMK8350_SID << 8 | 0x66)
|
||||
#define PMK8350_ADC7_AMUX_THM4_400K_PU (PMK8350_SID << 8 | 0x67)
|
||||
#define PMK8350_ADC7_AMUX_THM5_400K_PU (PMK8350_SID << 8 | 0x68)
|
||||
#define PMK8350_ADC7_AMUX_THM1_400K_PU (PMK8350_SID << 8 | ADC7_AMUX_THM1_400K_PU)
|
||||
#define PMK8350_ADC7_AMUX_THM2_400K_PU (PMK8350_SID << 8 | ADC7_AMUX_THM2_400K_PU)
|
||||
#define PMK8350_ADC7_AMUX_THM3_400K_PU (PMK8350_SID << 8 | ADC7_AMUX_THM3_400K_PU)
|
||||
#define PMK8350_ADC7_AMUX_THM4_400K_PU (PMK8350_SID << 8 | ADC7_AMUX_THM4_400K_PU)
|
||||
#define PMK8350_ADC7_AMUX_THM5_400K_PU (PMK8350_SID << 8 | ADC7_AMUX_THM5_400K_PU)
|
||||
|
||||
#endif /* _DT_BINDINGS_QCOM_SPMI_VADC_PMK8350_H */
|
||||
|
@ -10,19 +10,21 @@
|
||||
#define PMR735A_SID 4
|
||||
#endif
|
||||
|
||||
/* ADC channels for PMR735A_ADC for PMIC7 */
|
||||
#define PMR735A_ADC7_REF_GND (PMR735A_SID << 8 | 0x0)
|
||||
#define PMR735A_ADC7_1P25VREF (PMR735A_SID << 8 | 0x01)
|
||||
#define PMR735A_ADC7_VREF_VADC (PMR735A_SID << 8 | 0x02)
|
||||
#define PMR735A_ADC7_DIE_TEMP (PMR735A_SID << 8 | 0x03)
|
||||
#include <dt-bindings/iio/qcom,spmi-vadc.h>
|
||||
|
||||
#define PMR735A_ADC7_GPIO1 (PMR735A_SID << 8 | 0x0a)
|
||||
#define PMR735A_ADC7_GPIO2 (PMR735A_SID << 8 | 0x0b)
|
||||
#define PMR735A_ADC7_GPIO3 (PMR735A_SID << 8 | 0x0c)
|
||||
/* ADC channels for PMR735A_ADC for PMIC7 */
|
||||
#define PMR735A_ADC7_REF_GND (PMR735A_SID << 8 | ADC7_REF_GND)
|
||||
#define PMR735A_ADC7_1P25VREF (PMR735A_SID << 8 | ADC7_1P25VREF)
|
||||
#define PMR735A_ADC7_VREF_VADC (PMR735A_SID << 8 | ADC7_VREF_VADC)
|
||||
#define PMR735A_ADC7_DIE_TEMP (PMR735A_SID << 8 | ADC7_DIE_TEMP)
|
||||
|
||||
#define PMR735A_ADC7_GPIO1 (PMR735A_SID << 8 | ADC7_GPIO1)
|
||||
#define PMR735A_ADC7_GPIO2 (PMR735A_SID << 8 | ADC7_GPIO2)
|
||||
#define PMR735A_ADC7_GPIO3 (PMR735A_SID << 8 | ADC7_GPIO3)
|
||||
|
||||
/* 100k pull-up2 */
|
||||
#define PMR735A_ADC7_GPIO1_100K_PU (PMR735A_SID << 8 | 0x4a)
|
||||
#define PMR735A_ADC7_GPIO2_100K_PU (PMR735A_SID << 8 | 0x4b)
|
||||
#define PMR735A_ADC7_GPIO3_100K_PU (PMR735A_SID << 8 | 0x4c)
|
||||
#define PMR735A_ADC7_GPIO1_100K_PU (PMR735A_SID << 8 | ADC7_GPIO1_100K_PU)
|
||||
#define PMR735A_ADC7_GPIO2_100K_PU (PMR735A_SID << 8 | ADC7_GPIO2_100K_PU)
|
||||
#define PMR735A_ADC7_GPIO3_100K_PU (PMR735A_SID << 8 | ADC7_GPIO3_100K_PU)
|
||||
|
||||
#endif /* _DT_BINDINGS_QCOM_SPMI_VADC_PMR735A_H */
|
||||
|
@ -10,19 +10,21 @@
|
||||
#define PMR735B_SID 5
|
||||
#endif
|
||||
|
||||
/* ADC channels for PMR735B_ADC for PMIC7 */
|
||||
#define PMR735B_ADC7_REF_GND (PMR735B_SID << 8 | 0x0)
|
||||
#define PMR735B_ADC7_1P25VREF (PMR735B_SID << 8 | 0x01)
|
||||
#define PMR735B_ADC7_VREF_VADC (PMR735B_SID << 8 | 0x02)
|
||||
#define PMR735B_ADC7_DIE_TEMP (PMR735B_SID << 8 | 0x03)
|
||||
#include <dt-bindings/iio/qcom,spmi-vadc.h>
|
||||
|
||||
#define PMR735B_ADC7_GPIO1 (PMR735B_SID << 8 | 0x0a)
|
||||
#define PMR735B_ADC7_GPIO2 (PMR735B_SID << 8 | 0x0b)
|
||||
#define PMR735B_ADC7_GPIO3 (PMR735B_SID << 8 | 0x0c)
|
||||
/* ADC channels for PMR735B_ADC for PMIC7 */
|
||||
#define PMR735B_ADC7_REF_GND (PMR735B_SID << 8 | ADC7_REF_GND)
|
||||
#define PMR735B_ADC7_1P25VREF (PMR735B_SID << 8 | ADC7_1P25VREF)
|
||||
#define PMR735B_ADC7_VREF_VADC (PMR735B_SID << 8 | ADC7_VREF_VADC)
|
||||
#define PMR735B_ADC7_DIE_TEMP (PMR735B_SID << 8 | ADC7_DIE_TEMP)
|
||||
|
||||
#define PMR735B_ADC7_GPIO1 (PMR735B_SID << 8 | ADC7_GPIO1)
|
||||
#define PMR735B_ADC7_GPIO2 (PMR735B_SID << 8 | ADC7_GPIO2)
|
||||
#define PMR735B_ADC7_GPIO3 (PMR735B_SID << 8 | ADC7_GPIO3)
|
||||
|
||||
/* 100k pull-up2 */
|
||||
#define PMR735B_ADC7_GPIO1_100K_PU (PMR735B_SID << 8 | 0x4a)
|
||||
#define PMR735B_ADC7_GPIO2_100K_PU (PMR735B_SID << 8 | 0x4b)
|
||||
#define PMR735B_ADC7_GPIO3_100K_PU (PMR735B_SID << 8 | 0x4c)
|
||||
#define PMR735B_ADC7_GPIO1_100K_PU (PMR735B_SID << 8 | ADC7_GPIO1_100K_PU)
|
||||
#define PMR735B_ADC7_GPIO2_100K_PU (PMR735B_SID << 8 | ADC7_GPIO2_100K_PU)
|
||||
#define PMR735B_ADC7_GPIO3_100K_PU (PMR735B_SID << 8 | ADC7_GPIO3_100K_PU)
|
||||
|
||||
#endif /* _DT_BINDINGS_QCOM_SPMI_VADC_PMR735B_H */
|
||||
|
95
include/linux/iio/common/inv_sensors_timestamp.h
Normal file
95
include/linux/iio/common/inv_sensors_timestamp.h
Normal file
@ -0,0 +1,95 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright (C) 2020 Invensense, Inc.
|
||||
*/
|
||||
|
||||
#ifndef INV_SENSORS_TIMESTAMP_H_
|
||||
#define INV_SENSORS_TIMESTAMP_H_
|
||||
|
||||
/**
|
||||
* struct inv_sensors_timestamp_chip - chip internal properties
|
||||
* @clock_period: internal clock period in ns
|
||||
* @jitter: acceptable jitter in per-mille
|
||||
* @init_period: chip initial period at reset in ns
|
||||
*/
|
||||
struct inv_sensors_timestamp_chip {
|
||||
uint32_t clock_period;
|
||||
uint32_t jitter;
|
||||
uint32_t init_period;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct inv_sensors_timestamp_interval - timestamps interval
|
||||
* @lo: interval lower bound
|
||||
* @up: interval upper bound
|
||||
*/
|
||||
struct inv_sensors_timestamp_interval {
|
||||
int64_t lo;
|
||||
int64_t up;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct inv_sensors_timestamp_acc - accumulator for computing an estimation
|
||||
* @val: current estimation of the value, the mean of all values
|
||||
* @idx: current index of the next free place in values table
|
||||
* @values: table of all measured values, use for computing the mean
|
||||
*/
|
||||
struct inv_sensors_timestamp_acc {
|
||||
uint32_t val;
|
||||
size_t idx;
|
||||
uint32_t values[32];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct inv_sensors_timestamp - timestamp management states
|
||||
* @chip: chip internal characteristics
|
||||
* @min_period: minimal acceptable clock period
|
||||
* @max_period: maximal acceptable clock period
|
||||
* @it: interrupts interval timestamps
|
||||
* @timestamp: store last timestamp for computing next data timestamp
|
||||
* @mult: current internal period multiplier
|
||||
* @new_mult: new set internal period multiplier (not yet effective)
|
||||
* @period: measured current period of the sensor
|
||||
* @chip_period: accumulator for computing internal chip period
|
||||
*/
|
||||
struct inv_sensors_timestamp {
|
||||
struct inv_sensors_timestamp_chip chip;
|
||||
uint32_t min_period;
|
||||
uint32_t max_period;
|
||||
struct inv_sensors_timestamp_interval it;
|
||||
int64_t timestamp;
|
||||
uint32_t mult;
|
||||
uint32_t new_mult;
|
||||
uint32_t period;
|
||||
struct inv_sensors_timestamp_acc chip_period;
|
||||
};
|
||||
|
||||
void inv_sensors_timestamp_init(struct inv_sensors_timestamp *ts,
|
||||
const struct inv_sensors_timestamp_chip *chip);
|
||||
|
||||
int inv_sensors_timestamp_update_odr(struct inv_sensors_timestamp *ts,
|
||||
uint32_t period, bool fifo);
|
||||
|
||||
void inv_sensors_timestamp_interrupt(struct inv_sensors_timestamp *ts,
|
||||
uint32_t fifo_period, size_t fifo_nb,
|
||||
size_t sensor_nb, int64_t timestamp);
|
||||
|
||||
static inline int64_t inv_sensors_timestamp_pop(struct inv_sensors_timestamp *ts)
|
||||
{
|
||||
ts->timestamp += ts->period;
|
||||
return ts->timestamp;
|
||||
}
|
||||
|
||||
void inv_sensors_timestamp_apply_odr(struct inv_sensors_timestamp *ts,
|
||||
uint32_t fifo_period, size_t fifo_nb,
|
||||
unsigned int fifo_no);
|
||||
|
||||
static inline void inv_sensors_timestamp_reset(struct inv_sensors_timestamp *ts)
|
||||
{
|
||||
const struct inv_sensors_timestamp_interval interval_init = {0LL, 0LL};
|
||||
|
||||
ts->it = interval_init;
|
||||
ts->timestamp = 0;
|
||||
}
|
||||
|
||||
#endif
|
@ -19,6 +19,8 @@ enum iio_event_info {
|
||||
IIO_EV_INFO_TIMEOUT,
|
||||
IIO_EV_INFO_RESET_TIMEOUT,
|
||||
IIO_EV_INFO_TAP2_MIN_DELAY,
|
||||
IIO_EV_INFO_RUNNING_PERIOD,
|
||||
IIO_EV_INFO_RUNNING_COUNT,
|
||||
};
|
||||
|
||||
#define IIO_VAL_INT 1
|
||||
|
@ -51,9 +51,9 @@ enum autochan {
|
||||
* Has the side effect of filling the channels[i].location values used
|
||||
* in processing the buffer output.
|
||||
**/
|
||||
static int size_from_channelarray(struct iio_channel_info *channels, int num_channels)
|
||||
static unsigned int size_from_channelarray(struct iio_channel_info *channels, int num_channels)
|
||||
{
|
||||
int bytes = 0;
|
||||
unsigned int bytes = 0;
|
||||
int i = 0;
|
||||
|
||||
while (i < num_channels) {
|
||||
@ -348,7 +348,7 @@ int main(int argc, char **argv)
|
||||
ssize_t read_size;
|
||||
int dev_num = -1, trig_num = -1;
|
||||
char *buffer_access = NULL;
|
||||
int scan_size;
|
||||
unsigned int scan_size;
|
||||
int noevents = 0;
|
||||
int notrigger = 0;
|
||||
char *dummy;
|
||||
@ -674,7 +674,16 @@ int main(int argc, char **argv)
|
||||
}
|
||||
|
||||
scan_size = size_from_channelarray(channels, num_channels);
|
||||
data = malloc(scan_size * buf_len);
|
||||
|
||||
size_t total_buf_len = scan_size * buf_len;
|
||||
|
||||
if (scan_size > 0 && total_buf_len / scan_size != buf_len) {
|
||||
ret = -EFAULT;
|
||||
perror("Integer overflow happened when calculate scan_size * buf_len");
|
||||
goto error;
|
||||
}
|
||||
|
||||
data = malloc(total_buf_len);
|
||||
if (!data) {
|
||||
ret = -ENOMEM;
|
||||
goto error;
|
||||
|
Loading…
Reference in New Issue
Block a user