First set of new device support, features and cleanups for IIO in the 4.21 cycle
Along with the headline feature of 5 new drivers, we have the substantial addition of auxilliary sensor support on the lsm6sdx parts for ST. There has also been a good set of staging cleanup in this period with more underway. An ever increasing number of devices supported with just a new ID which is a good sign that at least some manufacturers are continuing to stabilise their interfaces. New device support, * ad7124 - New driver supporting Analog Devices' ad7124-4 and ad7124-8 parts with the inevitable DT binding. * ad7949 - New driver supporting Analog Devices' ad7949, AD7682 and AD7689 ADCs. * rm3100 - New driver supporting PNIs RM3100 magnometer with bindings and vendor prefix. * ti-dac7311 - New driver supporting DAC7311, DAC6311 and DAC5311 TI DACs, with DT bindings. * vcnl5035 - New driver supporting the light sensor part of the VCNL4035, with DT bindings Features, * bindings - Add a generic ADC channel binding as we keep reinventing this wheel. * adc128s052 - Add IDs for additional pin compatible parts. - Add APCI ID seen on E3940 UP squared boards. * ad_sigma_delta - Allow for custom data register overiding default. * kxcjk1013 - Add KIOX0009 ACPI ID as seen on the Acer One 10. * lsm6dsx - Rework leading to... - External sensor support using the built in I2C master. - Initial support for a slave lis2mdl magnetometer. * meson-saradc - Add temperature sensor support and bindings. * st_magn - New ID for lsm9dsl_magn with bindings - New ID for lis3de accelerometer * tpl0102 - Add supprot for IIO_AVAIL_RANGE to report the range available from this device to userspace and in kernel users. Cleanups and minor fixes * tools - Allow outside specification of CFLAGS * ad2s90 - Handle and spi_read error. - Handle spi_setup failure - Drop a pointless assignment. - Prevent a potentail race by moving device registration to after all other setup. - Add missing scale attribute. - Add a sanity check on channel type before trying to read it. * ad2s1210 - Move to modern gpio descriptors. - Drop a gpioin flag which made no sense as far as we can tell. - Add dt table (bindings doc to follow when this is ready for moving out of staging). * ad5933 - Drop camel-case naming of ext_clk_hz. - White space fixes. * ad7150 - Local variable to shorten overly long line. - Alignment and line break fixes. * ad7280a - Handle an error path that was previously ignored. - Use crc8.h to build the crc table replacing custom code. - Avoid unecessary cast. - Power down the device if an error happens in probe - Use devm routines to simplify probe and remove. * ad7606 - Alignment fixes. * ad7780 - This worked as long as by coincidence an uninitialized value was 0. Lets not rely on that. - Ensure gain update is only used with the ad778x chips that actually support it. - Tidy up pattern mask generation. - Read regulator when scale is requested (which should be infrequent) as it might have changed from initialization. * ad7816 - Move to modern gpio descriptors - Don't use a busy_pin for ad7818 as there isn't one. - Ensure RD/WR and CONVST pins are outputs (previously they were brought up as inputs which doesn't seem to make any sense) - DT id table. * adc128s052 - SPDX * adt7316 - Alignment fix. - Fix data reading. When using I2C the driver never actually used the value read. This has been broken a very long time hence no rush to fix it now + the driver is undergoing a lot of cleanup. - Sanity check that the i2c read didn't fail to actually read anything. * dpot-dac - Mark a switch full through with slightly different text so that gcc doesn't warn on it. * gyro-adc - Fix a wrong file in the MAINTAINERS entry and add binding doc to the listed files. * ina2xx - Add some early returns to clarify error paths in switch. * lsm6dsx - MAINTAINERS entry. * max11100 - SPDX * max9611 - SPDX * mcp4131 - use of_device_get_match_data in preference to spi_get_device_id approach. * rcar-adc - SPDX * sc27xx - Add ADC conversion timeout support to avoid possible fault. * ssp_sensors - Don't free managed resources manually. * st-magn - Add a comment to avoid future confusion over when to use -magn postfix (on multi chip in package parts) - Add BDU register for LIS3MDL where it seems to have been missed. * st-sensors - Minor spelling, grammar etc fixes. * tpl0102 - Use a pointer rather than an index of an array to improve conciseness. -----BEGIN PGP SIGNATURE----- iQJFBAABCAAvFiEEbilms4eEBlKRJoGxVIU0mcT0FogFAlv1wDARHGppYzIzQGtl cm5lbC5vcmcACgkQVIU0mcT0Fohf9Q//fzNk9RoY98awaEmHRW7PLnHe1WKyQTV5 M13EaFwm/NeWfT1kaNd/heZDnwucB34ONrcueNcM8MfwzPwWaL9UvSqEnSWIuTae hKdn/BMTfy168xhu95YPjHw4X42HwcXYlHL9XFsA0WERgaWsQu2EwIy1/3XRgIrt QPQVyJNUkLZmBuGcd2UhxzdTiLuh/hs7FFBg037hxaR77slWYKqAzwVa6eyz/jvo tRT9RE50O1FT3eDkBSw91nkamFcmFyyA2j4HQ33Aitjs5O1ML95xEJ0tMAznZfKn zAGCCUJjFYBvWPOpQ8xBA3tplFRGIyVhPJ5ZU+B2vZcxVXQ3IFZNqKRXfrUkAXpf PK2+05+HjHk1g7Ms1Tj4lF5AtP/nEQ8dvmGk6/nDU7Gt4Ytrpb0DhAuMGmN4S5ih zwwh+c6fLGCMz7KE8AoegOn3WF/FW1ZppTRjOWgqt7OP6lgoChPuTV5HGzxiZgr+ MzY/fytbXTnmfTL1ZvZYPdOSjkHcS9+8rwu1Jirmb1Y5Qt3qpNAUsdsnt965mQaE bHmuqw8QJN31goTEsqvjnsFhH57dJ1HbmG0MSrF8XB0bQB5Xzbkd+RV4fls1J+SG XwpIzQLhPxF5da6G+I/SIgLX6nASacJp9hHEN/ZuM3IKr2UW8FCpCH6JmyX0i6OU dPjHsXrGTuI= =78Y3 -----END PGP SIGNATURE----- Merge tag 'iio-for-4.21a' of git://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio into staging-testing Jonathan writes: First set of new device support, features and cleanups for IIO in the 4.21 cycle Along with the headline feature of 5 new drivers, we have the substantial addition of auxilliary sensor support on the lsm6sdx parts for ST. There has also been a good set of staging cleanup in this period with more underway. An ever increasing number of devices supported with just a new ID which is a good sign that at least some manufacturers are continuing to stabilise their interfaces. New device support, * ad7124 - New driver supporting Analog Devices' ad7124-4 and ad7124-8 parts with the inevitable DT binding. * ad7949 - New driver supporting Analog Devices' ad7949, AD7682 and AD7689 ADCs. * rm3100 - New driver supporting PNIs RM3100 magnometer with bindings and vendor prefix. * ti-dac7311 - New driver supporting DAC7311, DAC6311 and DAC5311 TI DACs, with DT bindings. * vcnl5035 - New driver supporting the light sensor part of the VCNL4035, with DT bindings Features, * bindings - Add a generic ADC channel binding as we keep reinventing this wheel. * adc128s052 - Add IDs for additional pin compatible parts. - Add APCI ID seen on E3940 UP squared boards. * ad_sigma_delta - Allow for custom data register overiding default. * kxcjk1013 - Add KIOX0009 ACPI ID as seen on the Acer One 10. * lsm6dsx - Rework leading to... - External sensor support using the built in I2C master. - Initial support for a slave lis2mdl magnetometer. * meson-saradc - Add temperature sensor support and bindings. * st_magn - New ID for lsm9dsl_magn with bindings - New ID for lis3de accelerometer * tpl0102 - Add supprot for IIO_AVAIL_RANGE to report the range available from this device to userspace and in kernel users. Cleanups and minor fixes * tools - Allow outside specification of CFLAGS * ad2s90 - Handle and spi_read error. - Handle spi_setup failure - Drop a pointless assignment. - Prevent a potentail race by moving device registration to after all other setup. - Add missing scale attribute. - Add a sanity check on channel type before trying to read it. * ad2s1210 - Move to modern gpio descriptors. - Drop a gpioin flag which made no sense as far as we can tell. - Add dt table (bindings doc to follow when this is ready for moving out of staging). * ad5933 - Drop camel-case naming of ext_clk_hz. - White space fixes. * ad7150 - Local variable to shorten overly long line. - Alignment and line break fixes. * ad7280a - Handle an error path that was previously ignored. - Use crc8.h to build the crc table replacing custom code. - Avoid unecessary cast. - Power down the device if an error happens in probe - Use devm routines to simplify probe and remove. * ad7606 - Alignment fixes. * ad7780 - This worked as long as by coincidence an uninitialized value was 0. Lets not rely on that. - Ensure gain update is only used with the ad778x chips that actually support it. - Tidy up pattern mask generation. - Read regulator when scale is requested (which should be infrequent) as it might have changed from initialization. * ad7816 - Move to modern gpio descriptors - Don't use a busy_pin for ad7818 as there isn't one. - Ensure RD/WR and CONVST pins are outputs (previously they were brought up as inputs which doesn't seem to make any sense) - DT id table. * adc128s052 - SPDX * adt7316 - Alignment fix. - Fix data reading. When using I2C the driver never actually used the value read. This has been broken a very long time hence no rush to fix it now + the driver is undergoing a lot of cleanup. - Sanity check that the i2c read didn't fail to actually read anything. * dpot-dac - Mark a switch full through with slightly different text so that gcc doesn't warn on it. * gyro-adc - Fix a wrong file in the MAINTAINERS entry and add binding doc to the listed files. * ina2xx - Add some early returns to clarify error paths in switch. * lsm6dsx - MAINTAINERS entry. * max11100 - SPDX * max9611 - SPDX * mcp4131 - use of_device_get_match_data in preference to spi_get_device_id approach. * rcar-adc - SPDX * sc27xx - Add ADC conversion timeout support to avoid possible fault. * ssp_sensors - Don't free managed resources manually. * st-magn - Add a comment to avoid future confusion over when to use -magn postfix (on multi chip in package parts) - Add BDU register for LIS3MDL where it seems to have been missed. * st-sensors - Minor spelling, grammar etc fixes. * tpl0102 - Use a pointer rather than an index of an array to improve conciseness. * tag 'iio-for-4.21a' of git://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio: (80 commits) Staging: iio: adt7316: Add an extra check for 'ret' equals to 0 Staging: iio: adt7316: Fix i2c data reading, set the data field dt-bindings: iio: adc: Add docs for ad7124 iio: adc: Add ad7124 support dt-bindings: iio: adc: Add common ADCs properties to a separate file iio: ad_sigma_delta: Allow to provide custom data register address staging: iio: ad7816: Add device tree table. iio: imu: st_lsm6dsx: add entry in MAINTAINERS file iio: potentiometer: mcp4131: use of_device_get_match_data() staging: iio: adc: ad7280a: use devm_* APIs staging: iio: adc: ad7280a: power down the device on error in probe dt-bindings: iio: imu: st_lsm6dsx: add support to i2c pullup resistors iio: imu: st_lsm6dsx: add hw FIFO support to i2c controller iio: imu: st_lsm6dsx: add st_lsm6dsx_push_tagged_data routine iio: imu: st_lsm6dsx: add i2c embedded controller support iio: imu: st_lsm6dsx: introduce st_lsm6dsx_sensor_set_enable routine iio: imu: st_lsm6dsx: introduce ST_LSM6DSX_ID_EXT sensor ids iio: imu: st_lsm6dsx: remove static from st_lsm6dsx_set_watermark iio: imu: st_lsm6dsx: reload trimming parameter at bootstrap iio: imu: st_lsm6dsx: introduce locked read/write utility routines ...
This commit is contained in:
commit
7c0bc65c84
16
Documentation/devicetree/bindings/iio/adc/ad7949.txt
Normal file
16
Documentation/devicetree/bindings/iio/adc/ad7949.txt
Normal file
@ -0,0 +1,16 @@
|
||||
* Analog Devices AD7949/AD7682/AD7689
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be one of
|
||||
* "adi,ad7949"
|
||||
* "adi,ad7682"
|
||||
* "adi,ad7689"
|
||||
- reg: spi chip select number for the device
|
||||
- vref-supply: The regulator supply for ADC reference voltage
|
||||
|
||||
Example:
|
||||
adc@0 {
|
||||
compatible = "adi,ad7949";
|
||||
reg = <0>;
|
||||
vref-supply = <&vdd_supply>;
|
||||
};
|
23
Documentation/devicetree/bindings/iio/adc/adc.txt
Normal file
23
Documentation/devicetree/bindings/iio/adc/adc.txt
Normal file
@ -0,0 +1,23 @@
|
||||
Common ADCs properties
|
||||
|
||||
Optional properties for child nodes:
|
||||
- bipolar : Boolean, if set the channel is used in bipolar mode.
|
||||
- diff-channels : Differential channels muxed for this ADC. The first value
|
||||
specifies the positive input pin, the second value the negative
|
||||
input pin.
|
||||
|
||||
Example:
|
||||
adc@0 {
|
||||
compatible = "some,adc";
|
||||
...
|
||||
channel@0 {
|
||||
bipolar;
|
||||
diff-channels = <0 1>;
|
||||
...
|
||||
};
|
||||
|
||||
channel@1 {
|
||||
diff-channels = <2 3>;
|
||||
...
|
||||
};
|
||||
};
|
75
Documentation/devicetree/bindings/iio/adc/adi,ad7124.txt
Normal file
75
Documentation/devicetree/bindings/iio/adc/adi,ad7124.txt
Normal file
@ -0,0 +1,75 @@
|
||||
Analog Devices AD7124 ADC device driver
|
||||
|
||||
Required properties for the AD7124:
|
||||
- compatible: Must be one of "adi,ad7124-4" or "adi,ad7124-8"
|
||||
- reg: SPI chip select number for the device
|
||||
- spi-max-frequency: Max SPI frequency to use
|
||||
see: Documentation/devicetree/bindings/spi/spi-bus.txt
|
||||
- clocks: phandle to the master clock (mclk)
|
||||
see: Documentation/devicetree/bindings/clock/clock-bindings.txt
|
||||
- clock-names: Must be "mclk".
|
||||
- interrupts: IRQ line for the ADC
|
||||
see: Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
|
||||
|
||||
Required properties:
|
||||
* #address-cells: Must be 1.
|
||||
* #size-cells: Must be 0.
|
||||
|
||||
Subnode(s) represent the external channels which are connected to the ADC.
|
||||
Each subnode represents one channel and has the following properties:
|
||||
Required properties:
|
||||
* reg: The channel number. It can have up to 4 channels on ad7124-4
|
||||
and 8 channels on ad7124-8, numbered from 0 to 15.
|
||||
* diff-channels: see: Documentation/devicetree/bindings/iio/adc/adc.txt
|
||||
|
||||
Optional properties:
|
||||
* bipolar: see: Documentation/devicetree/bindings/iio/adc/adc.txt
|
||||
* adi,reference-select: Select the reference source to use when
|
||||
converting on the the specific channel. Valid values are:
|
||||
0: REFIN1(+)/REFIN1(−).
|
||||
1: REFIN2(+)/REFIN2(−).
|
||||
3: AVDD
|
||||
If this field is left empty, internal reference is selected.
|
||||
|
||||
Optional properties:
|
||||
- refin1-supply: refin1 supply can be used as reference for conversion.
|
||||
- refin2-supply: refin2 supply can be used as reference for conversion.
|
||||
- avdd-supply: avdd supply can be used as reference for conversion.
|
||||
|
||||
Example:
|
||||
adc@0 {
|
||||
compatible = "adi,ad7124-4";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <5000000>;
|
||||
interrupts = <25 2>;
|
||||
interrupt-parent = <&gpio>;
|
||||
refin1-supply = <&adc_vref>;
|
||||
clocks = <&ad7124_mclk>;
|
||||
clock-names = "mclk";
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
channel@0 {
|
||||
reg = <0>;
|
||||
diff-channels = <0 1>;
|
||||
adi,reference-select = <0>;
|
||||
};
|
||||
|
||||
channel@1 {
|
||||
reg = <1>;
|
||||
bipolar;
|
||||
diff-channels = <2 3>;
|
||||
adi,reference-select = <0>;
|
||||
};
|
||||
|
||||
channel@2 {
|
||||
reg = <2>;
|
||||
diff-channels = <4 5>;
|
||||
};
|
||||
|
||||
channel@3 {
|
||||
reg = <3>;
|
||||
diff-channels = <6 7>;
|
||||
};
|
||||
};
|
@ -22,6 +22,12 @@ Required properties:
|
||||
- vref-supply: the regulator supply for the ADC reference voltage
|
||||
- #io-channel-cells: must be 1, see ../iio-bindings.txt
|
||||
|
||||
Optional properties:
|
||||
- nvmem-cells: phandle to the temperature_calib eFuse cells
|
||||
- nvmem-cell-names: if present (to enable the temperature sensor
|
||||
calibration) this must contain "temperature_calib"
|
||||
|
||||
|
||||
Example:
|
||||
saradc: adc@8680 {
|
||||
compatible = "amlogic,meson-gxl-saradc", "amlogic,meson-saradc";
|
||||
|
@ -1,7 +1,14 @@
|
||||
* Texas Instruments' ADC128S052, ADC122S021 and ADC124S021 ADC chip
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "ti,adc128s052", "ti,adc122s021" or "ti,adc124s021"
|
||||
- compatible: Should be one of:
|
||||
- "ti,adc128s052"
|
||||
- "ti,adc122s021"
|
||||
- "ti,adc122s051"
|
||||
- "ti,adc122s101"
|
||||
- "ti,adc124s021"
|
||||
- "ti,adc124s051"
|
||||
- "ti,adc124s101"
|
||||
- reg: spi chip select number for the device
|
||||
- vref-supply: The regulator supply for ADC reference voltage
|
||||
|
||||
|
23
Documentation/devicetree/bindings/iio/dac/ti,dac7311.txt
Normal file
23
Documentation/devicetree/bindings/iio/dac/ti,dac7311.txt
Normal file
@ -0,0 +1,23 @@
|
||||
TI DAC7311 device tree bindings
|
||||
|
||||
Required properties:
|
||||
- compatible: must be set to:
|
||||
* "ti,dac7311"
|
||||
* "ti,dac6311"
|
||||
* "ti,dac5311"
|
||||
- reg: spi chip select number for the device
|
||||
- vref-supply: The regulator supply for ADC reference voltage
|
||||
|
||||
Optional properties:
|
||||
- spi-max-frequency: Max SPI frequency to use
|
||||
|
||||
Example:
|
||||
|
||||
spi_master {
|
||||
dac@0 {
|
||||
compatible = "ti,dac7311";
|
||||
reg = <0>; /* CS0 */
|
||||
spi-max-frequency = <1000000>;
|
||||
vref-supply = <&vdd_supply>;
|
||||
};
|
||||
};
|
@ -13,6 +13,7 @@ Required properties:
|
||||
Optional properties:
|
||||
- st,drdy-int-pin: the pin on the package that will be used to signal
|
||||
"data ready" (valid values: 1 or 2).
|
||||
- st,pullups : enable/disable internal i2c controller pullup resistors.
|
||||
- drive-open-drain: the interrupt/data ready line will be configured
|
||||
as open drain, which is useful if several sensors share the same
|
||||
interrupt line. This is a boolean property.
|
||||
|
18
Documentation/devicetree/bindings/iio/light/vcnl4035.txt
Normal file
18
Documentation/devicetree/bindings/iio/light/vcnl4035.txt
Normal file
@ -0,0 +1,18 @@
|
||||
VISHAY VCNL4035 - Ambient Light and proximity sensor
|
||||
|
||||
Link to datasheet: https://www.vishay.com/docs/84251/vcnl4035x01.pdf
|
||||
|
||||
Required properties:
|
||||
|
||||
-compatible: should be "vishay,vcnl4035"
|
||||
-reg: I2C address of the sensor, should be 0x60
|
||||
-interrupts: interrupt mapping for GPIO IRQ (level active low)
|
||||
|
||||
Example:
|
||||
|
||||
light-sensor@60 {
|
||||
compatible = "vishay,vcnl4035";
|
||||
reg = <0x60>;
|
||||
interrupt-parent = <&gpio4>;
|
||||
interrupts = <11 IRQ_TYPE_LEVEL_LOW>;
|
||||
};
|
@ -0,0 +1,20 @@
|
||||
* PNI RM3100 3-axis magnetometer sensor
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : should be "pni,rm3100"
|
||||
- reg : the I2C address or SPI chip select number of the sensor.
|
||||
|
||||
Optional properties:
|
||||
|
||||
- interrupts: data ready (DRDY) from the chip.
|
||||
The interrupts can be triggered on level high.
|
||||
|
||||
Example:
|
||||
|
||||
rm3100: rm3100@20 {
|
||||
compatible = "pni,rm3100";
|
||||
reg = <0x20>;
|
||||
interrupt-parent = <&gpio0>;
|
||||
interrupts = <4 IRQ_TYPE_LEVEL_HIGH>;
|
||||
};
|
@ -48,6 +48,7 @@ Accelerometers:
|
||||
- st,lis3l02dq
|
||||
- st,lis2dw12
|
||||
- st,lis3dhh
|
||||
- st,lis3de
|
||||
|
||||
Gyroscopes:
|
||||
- st,l3g4200d-gyro
|
||||
@ -67,6 +68,7 @@ Magnetometers:
|
||||
- st,lsm303dlm-magn
|
||||
- st,lis3mdl-magn
|
||||
- st,lis2mdl
|
||||
- st,lsm9ds1-magn
|
||||
|
||||
Pressure sensors:
|
||||
- st,lps001wp-press
|
||||
|
@ -303,6 +303,7 @@ pixcir PIXCIR MICROELECTRONICS Co., Ltd
|
||||
plathome Plat'Home Co., Ltd.
|
||||
plda PLDA
|
||||
plx Broadcom Corporation (formerly PLX Technology)
|
||||
pni PNI Sensor Corporation
|
||||
portwell Portwell Inc.
|
||||
poslab Poslab Technology Co., Ltd.
|
||||
powervr PowerVR (deprecated, use img)
|
||||
@ -415,6 +416,7 @@ vamrs Vamrs Ltd.
|
||||
variscite Variscite Ltd.
|
||||
via VIA Technologies, Inc.
|
||||
virtio Virtual I/O Device Specification, developed by the OASIS consortium
|
||||
vishay Vishay Intertechnology, Inc
|
||||
vitesse Vitesse Semiconductor Corporation
|
||||
vivante Vivante Corporation
|
||||
vocore VoCore Studio
|
||||
|
26
MAINTAINERS
26
MAINTAINERS
@ -845,6 +845,14 @@ S: Supported
|
||||
F: drivers/iio/dac/ad5758.c
|
||||
F: Documentation/devicetree/bindings/iio/dac/ad5758.txt
|
||||
|
||||
ANALOG DEVICES INC AD7124 DRIVER
|
||||
M: Stefan Popa <stefan.popa@analog.com>
|
||||
L: linux-iio@vger.kernel.org
|
||||
W: http://ez.analog.com/community/linux-device-drivers
|
||||
S: Supported
|
||||
F: drivers/iio/adc/ad7124.c
|
||||
F: Documentation/devicetree/bindings/iio/adc/adi,ad7124.txt
|
||||
|
||||
ANALOG DEVICES INC AD9389B DRIVER
|
||||
M: Hans Verkuil <hans.verkuil@cisco.com>
|
||||
L: linux-media@vger.kernel.org
|
||||
@ -11858,6 +11866,13 @@ M: "Rafael J. Wysocki" <rafael.j.wysocki@intel.com>
|
||||
S: Maintained
|
||||
F: drivers/pnp/
|
||||
|
||||
PNI RM3100 IIO DRIVER
|
||||
M: Song Qiang <songqiang1304521@gmail.com>
|
||||
L: linux-iio@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/iio/magnetometer/rm3100*
|
||||
F: Documentation/devicetree/bindings/iio/magnetometer/pni,rm3100.txt
|
||||
|
||||
POSIX CLOCKS and TIMERS
|
||||
M: Thomas Gleixner <tglx@linutronix.de>
|
||||
L: linux-kernel@vger.kernel.org
|
||||
@ -12641,7 +12656,8 @@ RENESAS R-CAR GYROADC DRIVER
|
||||
M: Marek Vasut <marek.vasut@gmail.com>
|
||||
L: linux-iio@vger.kernel.org
|
||||
S: Supported
|
||||
F: drivers/iio/adc/rcar_gyro_adc.c
|
||||
F: Documentation/devicetree/bindings/iio/adc/renesas,gyroadc.txt
|
||||
F: drivers/iio/adc/rcar-gyroadc.c
|
||||
|
||||
RENESAS R-CAR I2C DRIVERS
|
||||
M: Wolfram Sang <wsa+renesas@sang-engineering.com>
|
||||
@ -14048,6 +14064,14 @@ M: Jan-Benedict Glaw <jbglaw@lug-owl.de>
|
||||
S: Maintained
|
||||
F: arch/alpha/kernel/srm_env.c
|
||||
|
||||
ST LSM6DSx IMU IIO DRIVER
|
||||
M: Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
|
||||
L: linux-iio@vger.kernel.org
|
||||
W: http://www.st.com/
|
||||
S: Maintained
|
||||
F: drivers/iio/imu/st_lsm6dsx/
|
||||
F: Documentation/devicetree/bindings/iio/imu/st_lsm6dsx.txt
|
||||
|
||||
ST STM32 I2C/SMBUS DRIVER
|
||||
M: Pierre-Yves MORDRET <pierre-yves.mordret@st.com>
|
||||
L: linux-i2c@vger.kernel.org
|
||||
|
@ -223,7 +223,7 @@ config IIO_ST_ACCEL_3AXIS
|
||||
Say yes here to build support for STMicroelectronics accelerometers:
|
||||
LSM303DLH, LSM303DLHC, LIS3DH, LSM330D, LSM330DL, LSM330DLC,
|
||||
LIS331DLH, LSM303DL, LSM303DLM, LSM330, LIS2DH12, H3LIS331DL,
|
||||
LNG2DM
|
||||
LNG2DM, LIS3DE
|
||||
|
||||
This driver can also be built as a module. If so, these modules
|
||||
will be created:
|
||||
|
@ -1489,6 +1489,7 @@ static const struct acpi_device_id kx_acpi_match[] = {
|
||||
{"KXCJ1013", KXCJK1013},
|
||||
{"KXCJ1008", KXCJ91008},
|
||||
{"KXCJ9000", KXCJ91008},
|
||||
{"KIOX0009", KXTJ21009},
|
||||
{"KIOX000A", KXCJ91008},
|
||||
{"KXTJ1009", KXTJ21009},
|
||||
{"SMO8500", KXCJ91008},
|
||||
|
@ -56,6 +56,7 @@ enum st_accel_type {
|
||||
#define LNG2DM_ACCEL_DEV_NAME "lng2dm"
|
||||
#define LIS2DW12_ACCEL_DEV_NAME "lis2dw12"
|
||||
#define LIS3DHH_ACCEL_DEV_NAME "lis3dhh"
|
||||
#define LIS3DE_ACCEL_DEV_NAME "lis3de"
|
||||
|
||||
/**
|
||||
* struct st_sensors_platform_data - default accel platform data
|
||||
|
@ -103,6 +103,7 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = {
|
||||
[4] = LSM330DLC_ACCEL_DEV_NAME,
|
||||
[5] = LSM303AGR_ACCEL_DEV_NAME,
|
||||
[6] = LIS2DH12_ACCEL_DEV_NAME,
|
||||
[7] = LIS3DE_ACCEL_DEV_NAME,
|
||||
},
|
||||
.ch = (struct iio_chan_spec *)st_accel_12bit_channels,
|
||||
.odr = {
|
||||
|
@ -98,6 +98,10 @@ static const struct of_device_id st_accel_of_match[] = {
|
||||
.compatible = "st,lis2dw12",
|
||||
.data = LIS2DW12_ACCEL_DEV_NAME,
|
||||
},
|
||||
{
|
||||
.compatible = "st,lis3de",
|
||||
.data = LIS3DE_ACCEL_DEV_NAME,
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, st_accel_of_match);
|
||||
@ -135,6 +139,7 @@ static const struct i2c_device_id st_accel_id_table[] = {
|
||||
{ LIS331DL_ACCEL_DEV_NAME },
|
||||
{ LIS3LV02DL_ACCEL_DEV_NAME },
|
||||
{ LIS2DW12_ACCEL_DEV_NAME },
|
||||
{ LIS3DE_ACCEL_DEV_NAME },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, st_accel_id_table);
|
||||
|
@ -90,6 +90,10 @@ static const struct of_device_id st_accel_of_match[] = {
|
||||
.compatible = "st,lis3dhh",
|
||||
.data = LIS3DHH_ACCEL_DEV_NAME,
|
||||
},
|
||||
{
|
||||
.compatible = "st,lis3de",
|
||||
.data = LIS3DE_ACCEL_DEV_NAME,
|
||||
},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, st_accel_of_match);
|
||||
@ -143,6 +147,7 @@ static const struct spi_device_id st_accel_id_table[] = {
|
||||
{ LIS3LV02DL_ACCEL_DEV_NAME },
|
||||
{ LIS2DW12_ACCEL_DEV_NAME },
|
||||
{ LIS3DHH_ACCEL_DEV_NAME },
|
||||
{ LIS3DE_ACCEL_DEV_NAME },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, st_accel_id_table);
|
||||
|
@ -10,6 +10,17 @@ config AD_SIGMA_DELTA
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
|
||||
config AD7124
|
||||
tristate "Analog Devices AD7124 and similar sigma-delta ADCs driver"
|
||||
depends on SPI_MASTER
|
||||
select AD_SIGMA_DELTA
|
||||
help
|
||||
Say yes here to build support for Analog Devices AD7124-4 and AD7124-8
|
||||
SPI analog to digital converters (ADC).
|
||||
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called ad7124.
|
||||
|
||||
config AD7266
|
||||
tristate "Analog Devices AD7265/AD7266 ADC driver"
|
||||
depends on SPI_MASTER
|
||||
@ -116,6 +127,16 @@ config AD7923
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ad7923.
|
||||
|
||||
config AD7949
|
||||
tristate "Analog Devices AD7949 and similar ADCs driver"
|
||||
depends on SPI
|
||||
help
|
||||
Say yes here to build support for Analog Devices
|
||||
AD7949, AD7682, AD7689 8 Channel ADCs.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ad7949.
|
||||
|
||||
config AD799X
|
||||
tristate "Analog Devices AD799x ADC driver"
|
||||
depends on I2C
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
# When adding new entries keep the list in alphabetical order
|
||||
obj-$(CONFIG_AD_SIGMA_DELTA) += ad_sigma_delta.o
|
||||
obj-$(CONFIG_AD7124) += ad7124.o
|
||||
obj-$(CONFIG_AD7266) += ad7266.o
|
||||
obj-$(CONFIG_AD7291) += ad7291.o
|
||||
obj-$(CONFIG_AD7298) += ad7298.o
|
||||
@ -14,6 +15,7 @@ obj-$(CONFIG_AD7766) += ad7766.o
|
||||
obj-$(CONFIG_AD7791) += ad7791.o
|
||||
obj-$(CONFIG_AD7793) += ad7793.o
|
||||
obj-$(CONFIG_AD7887) += ad7887.o
|
||||
obj-$(CONFIG_AD7949) += ad7949.o
|
||||
obj-$(CONFIG_AD799X) += ad799x.o
|
||||
obj-$(CONFIG_ASPEED_ADC) += aspeed_adc.o
|
||||
obj-$(CONFIG_AT91_ADC) += at91_adc.o
|
||||
|
684
drivers/iio/adc/ad7124.c
Normal file
684
drivers/iio/adc/ad7124.c
Normal file
@ -0,0 +1,684 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* AD7124 SPI ADC driver
|
||||
*
|
||||
* Copyright 2018 Analog Devices Inc.
|
||||
*/
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/adc/ad_sigma_delta.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
/* AD7124 registers */
|
||||
#define AD7124_COMMS 0x00
|
||||
#define AD7124_STATUS 0x00
|
||||
#define AD7124_ADC_CONTROL 0x01
|
||||
#define AD7124_DATA 0x02
|
||||
#define AD7124_IO_CONTROL_1 0x03
|
||||
#define AD7124_IO_CONTROL_2 0x04
|
||||
#define AD7124_ID 0x05
|
||||
#define AD7124_ERROR 0x06
|
||||
#define AD7124_ERROR_EN 0x07
|
||||
#define AD7124_MCLK_COUNT 0x08
|
||||
#define AD7124_CHANNEL(x) (0x09 + (x))
|
||||
#define AD7124_CONFIG(x) (0x19 + (x))
|
||||
#define AD7124_FILTER(x) (0x21 + (x))
|
||||
#define AD7124_OFFSET(x) (0x29 + (x))
|
||||
#define AD7124_GAIN(x) (0x31 + (x))
|
||||
|
||||
/* AD7124_STATUS */
|
||||
#define AD7124_STATUS_POR_FLAG_MSK BIT(4)
|
||||
|
||||
/* AD7124_ADC_CONTROL */
|
||||
#define AD7124_ADC_CTRL_PWR_MSK GENMASK(7, 6)
|
||||
#define AD7124_ADC_CTRL_PWR(x) FIELD_PREP(AD7124_ADC_CTRL_PWR_MSK, x)
|
||||
#define AD7124_ADC_CTRL_MODE_MSK GENMASK(5, 2)
|
||||
#define AD7124_ADC_CTRL_MODE(x) FIELD_PREP(AD7124_ADC_CTRL_MODE_MSK, x)
|
||||
|
||||
/* AD7124_CHANNEL_X */
|
||||
#define AD7124_CHANNEL_EN_MSK BIT(15)
|
||||
#define AD7124_CHANNEL_EN(x) FIELD_PREP(AD7124_CHANNEL_EN_MSK, x)
|
||||
#define AD7124_CHANNEL_SETUP_MSK GENMASK(14, 12)
|
||||
#define AD7124_CHANNEL_SETUP(x) FIELD_PREP(AD7124_CHANNEL_SETUP_MSK, x)
|
||||
#define AD7124_CHANNEL_AINP_MSK GENMASK(9, 5)
|
||||
#define AD7124_CHANNEL_AINP(x) FIELD_PREP(AD7124_CHANNEL_AINP_MSK, x)
|
||||
#define AD7124_CHANNEL_AINM_MSK GENMASK(4, 0)
|
||||
#define AD7124_CHANNEL_AINM(x) FIELD_PREP(AD7124_CHANNEL_AINM_MSK, x)
|
||||
|
||||
/* AD7124_CONFIG_X */
|
||||
#define AD7124_CONFIG_BIPOLAR_MSK BIT(11)
|
||||
#define AD7124_CONFIG_BIPOLAR(x) FIELD_PREP(AD7124_CONFIG_BIPOLAR_MSK, x)
|
||||
#define AD7124_CONFIG_REF_SEL_MSK GENMASK(4, 3)
|
||||
#define AD7124_CONFIG_REF_SEL(x) FIELD_PREP(AD7124_CONFIG_REF_SEL_MSK, x)
|
||||
#define AD7124_CONFIG_PGA_MSK GENMASK(2, 0)
|
||||
#define AD7124_CONFIG_PGA(x) FIELD_PREP(AD7124_CONFIG_PGA_MSK, x)
|
||||
|
||||
/* AD7124_FILTER_X */
|
||||
#define AD7124_FILTER_FS_MSK GENMASK(10, 0)
|
||||
#define AD7124_FILTER_FS(x) FIELD_PREP(AD7124_FILTER_FS_MSK, x)
|
||||
|
||||
enum ad7124_ids {
|
||||
ID_AD7124_4,
|
||||
ID_AD7124_8,
|
||||
};
|
||||
|
||||
enum ad7124_ref_sel {
|
||||
AD7124_REFIN1,
|
||||
AD7124_REFIN2,
|
||||
AD7124_INT_REF,
|
||||
AD7124_AVDD_REF,
|
||||
};
|
||||
|
||||
enum ad7124_power_mode {
|
||||
AD7124_LOW_POWER,
|
||||
AD7124_MID_POWER,
|
||||
AD7124_FULL_POWER,
|
||||
};
|
||||
|
||||
static const unsigned int ad7124_gain[8] = {
|
||||
1, 2, 4, 8, 16, 32, 64, 128
|
||||
};
|
||||
|
||||
static const int ad7124_master_clk_freq_hz[3] = {
|
||||
[AD7124_LOW_POWER] = 76800,
|
||||
[AD7124_MID_POWER] = 153600,
|
||||
[AD7124_FULL_POWER] = 614400,
|
||||
};
|
||||
|
||||
static const char * const ad7124_ref_names[] = {
|
||||
[AD7124_REFIN1] = "refin1",
|
||||
[AD7124_REFIN2] = "refin2",
|
||||
[AD7124_INT_REF] = "int",
|
||||
[AD7124_AVDD_REF] = "avdd",
|
||||
};
|
||||
|
||||
struct ad7124_chip_info {
|
||||
unsigned int num_inputs;
|
||||
};
|
||||
|
||||
struct ad7124_channel_config {
|
||||
enum ad7124_ref_sel refsel;
|
||||
bool bipolar;
|
||||
unsigned int ain;
|
||||
unsigned int vref_mv;
|
||||
unsigned int pga_bits;
|
||||
unsigned int odr;
|
||||
};
|
||||
|
||||
struct ad7124_state {
|
||||
const struct ad7124_chip_info *chip_info;
|
||||
struct ad_sigma_delta sd;
|
||||
struct ad7124_channel_config channel_config[4];
|
||||
struct regulator *vref[4];
|
||||
struct clk *mclk;
|
||||
unsigned int adc_control;
|
||||
unsigned int num_channels;
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec ad7124_channel_template = {
|
||||
.type = IIO_VOLTAGE,
|
||||
.indexed = 1,
|
||||
.differential = 1,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_OFFSET) |
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ),
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
.realbits = 24,
|
||||
.storagebits = 32,
|
||||
.shift = 8,
|
||||
.endianness = IIO_BE,
|
||||
},
|
||||
};
|
||||
|
||||
static struct ad7124_chip_info ad7124_chip_info_tbl[] = {
|
||||
[ID_AD7124_4] = {
|
||||
.num_inputs = 8,
|
||||
},
|
||||
[ID_AD7124_8] = {
|
||||
.num_inputs = 16,
|
||||
},
|
||||
};
|
||||
|
||||
static int ad7124_find_closest_match(const int *array,
|
||||
unsigned int size, int val)
|
||||
{
|
||||
int i, idx;
|
||||
unsigned int diff_new, diff_old;
|
||||
|
||||
diff_old = U32_MAX;
|
||||
idx = 0;
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
diff_new = abs(val - array[i]);
|
||||
if (diff_new < diff_old) {
|
||||
diff_old = diff_new;
|
||||
idx = i;
|
||||
}
|
||||
}
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
static int ad7124_spi_write_mask(struct ad7124_state *st,
|
||||
unsigned int addr,
|
||||
unsigned long mask,
|
||||
unsigned int val,
|
||||
unsigned int bytes)
|
||||
{
|
||||
unsigned int readval;
|
||||
int ret;
|
||||
|
||||
ret = ad_sd_read_reg(&st->sd, addr, bytes, &readval);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
readval &= ~mask;
|
||||
readval |= val;
|
||||
|
||||
return ad_sd_write_reg(&st->sd, addr, bytes, readval);
|
||||
}
|
||||
|
||||
static int ad7124_set_mode(struct ad_sigma_delta *sd,
|
||||
enum ad_sigma_delta_mode mode)
|
||||
{
|
||||
struct ad7124_state *st = container_of(sd, struct ad7124_state, sd);
|
||||
|
||||
st->adc_control &= ~AD7124_ADC_CTRL_MODE_MSK;
|
||||
st->adc_control |= AD7124_ADC_CTRL_MODE(mode);
|
||||
|
||||
return ad_sd_write_reg(&st->sd, AD7124_ADC_CONTROL, 2, st->adc_control);
|
||||
}
|
||||
|
||||
static int ad7124_set_channel(struct ad_sigma_delta *sd, unsigned int channel)
|
||||
{
|
||||
struct ad7124_state *st = container_of(sd, struct ad7124_state, sd);
|
||||
unsigned int val;
|
||||
|
||||
val = st->channel_config[channel].ain | AD7124_CHANNEL_EN(1) |
|
||||
AD7124_CHANNEL_SETUP(channel);
|
||||
|
||||
return ad_sd_write_reg(&st->sd, AD7124_CHANNEL(channel), 2, val);
|
||||
}
|
||||
|
||||
static const struct ad_sigma_delta_info ad7124_sigma_delta_info = {
|
||||
.set_channel = ad7124_set_channel,
|
||||
.set_mode = ad7124_set_mode,
|
||||
.has_registers = true,
|
||||
.addr_shift = 0,
|
||||
.read_mask = BIT(6),
|
||||
.data_reg = AD7124_DATA,
|
||||
};
|
||||
|
||||
static int ad7124_set_channel_odr(struct ad7124_state *st,
|
||||
unsigned int channel,
|
||||
unsigned int odr)
|
||||
{
|
||||
unsigned int fclk, odr_sel_bits;
|
||||
int ret;
|
||||
|
||||
fclk = clk_get_rate(st->mclk);
|
||||
/*
|
||||
* FS[10:0] = fCLK / (fADC x 32) where:
|
||||
* fADC is the output data rate
|
||||
* fCLK is the master clock frequency
|
||||
* FS[10:0] are the bits in the filter register
|
||||
* FS[10:0] can have a value from 1 to 2047
|
||||
*/
|
||||
odr_sel_bits = DIV_ROUND_CLOSEST(fclk, odr * 32);
|
||||
if (odr_sel_bits < 1)
|
||||
odr_sel_bits = 1;
|
||||
else if (odr_sel_bits > 2047)
|
||||
odr_sel_bits = 2047;
|
||||
|
||||
ret = ad7124_spi_write_mask(st, AD7124_FILTER(channel),
|
||||
AD7124_FILTER_FS_MSK,
|
||||
AD7124_FILTER_FS(odr_sel_bits), 3);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
/* fADC = fCLK / (FS[10:0] x 32) */
|
||||
st->channel_config[channel].odr =
|
||||
DIV_ROUND_CLOSEST(fclk, odr_sel_bits * 32);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ad7124_set_channel_gain(struct ad7124_state *st,
|
||||
unsigned int channel,
|
||||
unsigned int gain)
|
||||
{
|
||||
unsigned int res;
|
||||
int ret;
|
||||
|
||||
res = ad7124_find_closest_match(ad7124_gain,
|
||||
ARRAY_SIZE(ad7124_gain), gain);
|
||||
ret = ad7124_spi_write_mask(st, AD7124_CONFIG(channel),
|
||||
AD7124_CONFIG_PGA_MSK,
|
||||
AD7124_CONFIG_PGA(res), 2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
st->channel_config[channel].pga_bits = res;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ad7124_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long info)
|
||||
{
|
||||
struct ad7124_state *st = iio_priv(indio_dev);
|
||||
int idx, ret;
|
||||
|
||||
switch (info) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = ad_sigma_delta_single_conversion(indio_dev, chan, val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* After the conversion is performed, disable the channel */
|
||||
ret = ad_sd_write_reg(&st->sd,
|
||||
AD7124_CHANNEL(chan->address), 2,
|
||||
st->channel_config[chan->address].ain |
|
||||
AD7124_CHANNEL_EN(0));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
idx = st->channel_config[chan->address].pga_bits;
|
||||
*val = st->channel_config[chan->address].vref_mv;
|
||||
if (st->channel_config[chan->address].bipolar)
|
||||
*val2 = chan->scan_type.realbits - 1 + idx;
|
||||
else
|
||||
*val2 = chan->scan_type.realbits + idx;
|
||||
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
if (st->channel_config[chan->address].bipolar)
|
||||
*val = -(1 << (chan->scan_type.realbits - 1));
|
||||
else
|
||||
*val = 0;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
*val = st->channel_config[chan->address].odr;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int ad7124_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long info)
|
||||
{
|
||||
struct ad7124_state *st = iio_priv(indio_dev);
|
||||
unsigned int res, gain, full_scale, vref;
|
||||
|
||||
switch (info) {
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
if (val2 != 0)
|
||||
return -EINVAL;
|
||||
|
||||
return ad7124_set_channel_odr(st, chan->address, val);
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
if (val != 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (st->channel_config[chan->address].bipolar)
|
||||
full_scale = 1 << (chan->scan_type.realbits - 1);
|
||||
else
|
||||
full_scale = 1 << chan->scan_type.realbits;
|
||||
|
||||
vref = st->channel_config[chan->address].vref_mv * 1000000LL;
|
||||
res = DIV_ROUND_CLOSEST(vref, full_scale);
|
||||
gain = DIV_ROUND_CLOSEST(res, val2);
|
||||
|
||||
return ad7124_set_channel_gain(st, chan->address, gain);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static IIO_CONST_ATTR(in_voltage_scale_available,
|
||||
"0.000001164 0.000002328 0.000004656 0.000009313 0.000018626 0.000037252 0.000074505 0.000149011 0.000298023");
|
||||
|
||||
static struct attribute *ad7124_attributes[] = {
|
||||
&iio_const_attr_in_voltage_scale_available.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group ad7124_attrs_group = {
|
||||
.attrs = ad7124_attributes,
|
||||
};
|
||||
|
||||
static const struct iio_info ad7124_info = {
|
||||
.read_raw = ad7124_read_raw,
|
||||
.write_raw = ad7124_write_raw,
|
||||
.validate_trigger = ad_sd_validate_trigger,
|
||||
.attrs = &ad7124_attrs_group,
|
||||
};
|
||||
|
||||
static int ad7124_soft_reset(struct ad7124_state *st)
|
||||
{
|
||||
unsigned int readval, timeout;
|
||||
int ret;
|
||||
|
||||
ret = ad_sd_reset(&st->sd, 64);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
timeout = 100;
|
||||
do {
|
||||
ret = ad_sd_read_reg(&st->sd, AD7124_STATUS, 1, &readval);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (!(readval & AD7124_STATUS_POR_FLAG_MSK))
|
||||
return 0;
|
||||
|
||||
/* The AD7124 requires typically 2ms to power up and settle */
|
||||
usleep_range(100, 2000);
|
||||
} while (--timeout);
|
||||
|
||||
dev_err(&st->sd.spi->dev, "Soft reset failed\n");
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static int ad7124_init_channel_vref(struct ad7124_state *st,
|
||||
unsigned int channel_number)
|
||||
{
|
||||
unsigned int refsel = st->channel_config[channel_number].refsel;
|
||||
|
||||
switch (refsel) {
|
||||
case AD7124_REFIN1:
|
||||
case AD7124_REFIN2:
|
||||
case AD7124_AVDD_REF:
|
||||
if (IS_ERR(st->vref[refsel])) {
|
||||
dev_err(&st->sd.spi->dev,
|
||||
"Error, trying to use external voltage reference without a %s regulator.\n",
|
||||
ad7124_ref_names[refsel]);
|
||||
return PTR_ERR(st->vref[refsel]);
|
||||
}
|
||||
st->channel_config[channel_number].vref_mv =
|
||||
regulator_get_voltage(st->vref[refsel]);
|
||||
/* Conversion from uV to mV */
|
||||
st->channel_config[channel_number].vref_mv /= 1000;
|
||||
break;
|
||||
case AD7124_INT_REF:
|
||||
st->channel_config[channel_number].vref_mv = 2500;
|
||||
break;
|
||||
default:
|
||||
dev_err(&st->sd.spi->dev, "Invalid reference %d\n", refsel);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ad7124_of_parse_channel_config(struct iio_dev *indio_dev,
|
||||
struct device_node *np)
|
||||
{
|
||||
struct ad7124_state *st = iio_priv(indio_dev);
|
||||
struct device_node *child;
|
||||
struct iio_chan_spec *chan;
|
||||
unsigned int ain[2], channel = 0, tmp;
|
||||
int ret;
|
||||
|
||||
st->num_channels = of_get_available_child_count(np);
|
||||
if (!st->num_channels) {
|
||||
dev_err(indio_dev->dev.parent, "no channel children\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
chan = devm_kcalloc(indio_dev->dev.parent, st->num_channels,
|
||||
sizeof(*chan), GFP_KERNEL);
|
||||
if (!chan)
|
||||
return -ENOMEM;
|
||||
|
||||
indio_dev->channels = chan;
|
||||
indio_dev->num_channels = st->num_channels;
|
||||
|
||||
for_each_available_child_of_node(np, child) {
|
||||
ret = of_property_read_u32(child, "reg", &channel);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
ret = of_property_read_u32_array(child, "diff-channels",
|
||||
ain, 2);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
if (ain[0] >= st->chip_info->num_inputs ||
|
||||
ain[1] >= st->chip_info->num_inputs) {
|
||||
dev_err(indio_dev->dev.parent,
|
||||
"Input pin number out of range.\n");
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
st->channel_config[channel].ain = AD7124_CHANNEL_AINP(ain[0]) |
|
||||
AD7124_CHANNEL_AINM(ain[1]);
|
||||
st->channel_config[channel].bipolar =
|
||||
of_property_read_bool(child, "bipolar");
|
||||
|
||||
ret = of_property_read_u32(child, "adi,reference-select", &tmp);
|
||||
if (ret)
|
||||
st->channel_config[channel].refsel = AD7124_INT_REF;
|
||||
else
|
||||
st->channel_config[channel].refsel = tmp;
|
||||
|
||||
*chan = ad7124_channel_template;
|
||||
chan->address = channel;
|
||||
chan->scan_index = channel;
|
||||
chan->channel = ain[0];
|
||||
chan->channel2 = ain[1];
|
||||
|
||||
chan++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
err:
|
||||
of_node_put(child);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad7124_setup(struct ad7124_state *st)
|
||||
{
|
||||
unsigned int val, fclk, power_mode;
|
||||
int i, ret;
|
||||
|
||||
fclk = clk_get_rate(st->mclk);
|
||||
if (!fclk)
|
||||
return -EINVAL;
|
||||
|
||||
/* The power mode changes the master clock frequency */
|
||||
power_mode = ad7124_find_closest_match(ad7124_master_clk_freq_hz,
|
||||
ARRAY_SIZE(ad7124_master_clk_freq_hz),
|
||||
fclk);
|
||||
if (fclk != ad7124_master_clk_freq_hz[power_mode]) {
|
||||
ret = clk_set_rate(st->mclk, fclk);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Set the power mode */
|
||||
st->adc_control &= ~AD7124_ADC_CTRL_PWR_MSK;
|
||||
st->adc_control |= AD7124_ADC_CTRL_PWR(power_mode);
|
||||
ret = ad_sd_write_reg(&st->sd, AD7124_ADC_CONTROL, 2, st->adc_control);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < st->num_channels; i++) {
|
||||
val = st->channel_config[i].ain | AD7124_CHANNEL_SETUP(i);
|
||||
ret = ad_sd_write_reg(&st->sd, AD7124_CHANNEL(i), 2, val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = ad7124_init_channel_vref(st, i);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
val = AD7124_CONFIG_BIPOLAR(st->channel_config[i].bipolar) |
|
||||
AD7124_CONFIG_REF_SEL(st->channel_config[i].refsel);
|
||||
ret = ad_sd_write_reg(&st->sd, AD7124_CONFIG(i), 2, val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
/*
|
||||
* 9.38 SPS is the minimum output data rate supported
|
||||
* regardless of the selected power mode. Round it up to 10 and
|
||||
* set all the enabled channels to this default value.
|
||||
*/
|
||||
ret = ad7124_set_channel_odr(st, i, 10);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad7124_probe(struct spi_device *spi)
|
||||
{
|
||||
const struct spi_device_id *id;
|
||||
struct ad7124_state *st;
|
||||
struct iio_dev *indio_dev;
|
||||
int i, ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
|
||||
id = spi_get_device_id(spi);
|
||||
st->chip_info = &ad7124_chip_info_tbl[id->driver_data];
|
||||
|
||||
ad_sd_init(&st->sd, indio_dev, spi, &ad7124_sigma_delta_info);
|
||||
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->name = spi_get_device_id(spi)->name;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &ad7124_info;
|
||||
|
||||
ret = ad7124_of_parse_channel_config(indio_dev, spi->dev.of_node);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(st->vref); i++) {
|
||||
if (i == AD7124_INT_REF)
|
||||
continue;
|
||||
|
||||
st->vref[i] = devm_regulator_get_optional(&spi->dev,
|
||||
ad7124_ref_names[i]);
|
||||
if (PTR_ERR(st->vref[i]) == -ENODEV)
|
||||
continue;
|
||||
else if (IS_ERR(st->vref[i]))
|
||||
return PTR_ERR(st->vref[i]);
|
||||
|
||||
ret = regulator_enable(st->vref[i]);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
st->mclk = devm_clk_get(&spi->dev, "mclk");
|
||||
if (IS_ERR(st->mclk)) {
|
||||
ret = PTR_ERR(st->mclk);
|
||||
goto error_regulator_disable;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(st->mclk);
|
||||
if (ret < 0)
|
||||
goto error_regulator_disable;
|
||||
|
||||
ret = ad7124_soft_reset(st);
|
||||
if (ret < 0)
|
||||
goto error_clk_disable_unprepare;
|
||||
|
||||
ret = ad7124_setup(st);
|
||||
if (ret < 0)
|
||||
goto error_clk_disable_unprepare;
|
||||
|
||||
ret = ad_sd_setup_buffer_and_trigger(indio_dev);
|
||||
if (ret < 0)
|
||||
goto error_clk_disable_unprepare;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret < 0) {
|
||||
dev_err(&spi->dev, "Failed to register iio device\n");
|
||||
goto error_remove_trigger;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error_remove_trigger:
|
||||
ad_sd_cleanup_buffer_and_trigger(indio_dev);
|
||||
error_clk_disable_unprepare:
|
||||
clk_disable_unprepare(st->mclk);
|
||||
error_regulator_disable:
|
||||
for (i = ARRAY_SIZE(st->vref) - 1; i >= 0; i--) {
|
||||
if (!IS_ERR_OR_NULL(st->vref[i]))
|
||||
regulator_disable(st->vref[i]);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad7124_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct ad7124_state *st = iio_priv(indio_dev);
|
||||
int i;
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
ad_sd_cleanup_buffer_and_trigger(indio_dev);
|
||||
clk_disable_unprepare(st->mclk);
|
||||
|
||||
for (i = ARRAY_SIZE(st->vref) - 1; i >= 0; i--) {
|
||||
if (!IS_ERR_OR_NULL(st->vref[i]))
|
||||
regulator_disable(st->vref[i]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id ad7124_id_table[] = {
|
||||
{ "ad7124-4", ID_AD7124_4 },
|
||||
{ "ad7124-8", ID_AD7124_8 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ad7124_id_table);
|
||||
|
||||
static const struct of_device_id ad7124_of_match[] = {
|
||||
{ .compatible = "adi,ad7124-4" },
|
||||
{ .compatible = "adi,ad7124-8" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ad7124_of_match);
|
||||
|
||||
static struct spi_driver ad71124_driver = {
|
||||
.driver = {
|
||||
.name = "ad7124",
|
||||
.of_match_table = ad7124_of_match,
|
||||
},
|
||||
.probe = ad7124_probe,
|
||||
.remove = ad7124_remove,
|
||||
.id_table = ad7124_id_table,
|
||||
};
|
||||
module_spi_driver(ad71124_driver);
|
||||
|
||||
MODULE_AUTHOR("Stefan Popa <stefan.popa@analog.com>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD7124 SPI driver");
|
||||
MODULE_LICENSE("GPL");
|
347
drivers/iio/adc/ad7949.c
Normal file
347
drivers/iio/adc/ad7949.c
Normal file
@ -0,0 +1,347 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* ad7949.c - Analog Devices ADC driver 14/16 bits 4/8 channels
|
||||
*
|
||||
* Copyright (C) 2018 CMC NV
|
||||
*
|
||||
* http://www.analog.com/media/en/technical-documentation/data-sheets/AD7949.pdf
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#define AD7949_MASK_CHANNEL_SEL GENMASK(9, 7)
|
||||
#define AD7949_MASK_TOTAL GENMASK(13, 0)
|
||||
#define AD7949_OFFSET_CHANNEL_SEL 7
|
||||
#define AD7949_CFG_READ_BACK 0x1
|
||||
#define AD7949_CFG_REG_SIZE_BITS 14
|
||||
|
||||
enum {
|
||||
ID_AD7949 = 0,
|
||||
ID_AD7682,
|
||||
ID_AD7689,
|
||||
};
|
||||
|
||||
struct ad7949_adc_spec {
|
||||
u8 num_channels;
|
||||
u8 resolution;
|
||||
};
|
||||
|
||||
static const struct ad7949_adc_spec ad7949_adc_spec[] = {
|
||||
[ID_AD7949] = { .num_channels = 8, .resolution = 14 },
|
||||
[ID_AD7682] = { .num_channels = 4, .resolution = 16 },
|
||||
[ID_AD7689] = { .num_channels = 8, .resolution = 16 },
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ad7949_adc_chip - AD ADC chip
|
||||
* @lock: protects write sequences
|
||||
* @vref: regulator generating Vref
|
||||
* @iio_dev: reference to iio structure
|
||||
* @spi: reference to spi structure
|
||||
* @resolution: resolution of the chip
|
||||
* @cfg: copy of the configuration register
|
||||
* @current_channel: current channel in use
|
||||
* @buffer: buffer to send / receive data to / from device
|
||||
*/
|
||||
struct ad7949_adc_chip {
|
||||
struct mutex lock;
|
||||
struct regulator *vref;
|
||||
struct iio_dev *indio_dev;
|
||||
struct spi_device *spi;
|
||||
u8 resolution;
|
||||
u16 cfg;
|
||||
unsigned int current_channel;
|
||||
u32 buffer ____cacheline_aligned;
|
||||
};
|
||||
|
||||
static bool ad7949_spi_cfg_is_read_back(struct ad7949_adc_chip *ad7949_adc)
|
||||
{
|
||||
if (!(ad7949_adc->cfg & AD7949_CFG_READ_BACK))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int ad7949_spi_bits_per_word(struct ad7949_adc_chip *ad7949_adc)
|
||||
{
|
||||
int ret = ad7949_adc->resolution;
|
||||
|
||||
if (ad7949_spi_cfg_is_read_back(ad7949_adc))
|
||||
ret += AD7949_CFG_REG_SIZE_BITS;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad7949_spi_write_cfg(struct ad7949_adc_chip *ad7949_adc, u16 val,
|
||||
u16 mask)
|
||||
{
|
||||
int ret;
|
||||
int bits_per_word = ad7949_spi_bits_per_word(ad7949_adc);
|
||||
int shift = bits_per_word - AD7949_CFG_REG_SIZE_BITS;
|
||||
struct spi_message msg;
|
||||
struct spi_transfer tx[] = {
|
||||
{
|
||||
.tx_buf = &ad7949_adc->buffer,
|
||||
.len = 4,
|
||||
.bits_per_word = bits_per_word,
|
||||
},
|
||||
};
|
||||
|
||||
ad7949_adc->cfg = (val & mask) | (ad7949_adc->cfg & ~mask);
|
||||
ad7949_adc->buffer = ad7949_adc->cfg << shift;
|
||||
spi_message_init_with_transfers(&msg, tx, 1);
|
||||
ret = spi_sync(ad7949_adc->spi, &msg);
|
||||
|
||||
/*
|
||||
* This delay is to avoid a new request before the required time to
|
||||
* send a new command to the device
|
||||
*/
|
||||
udelay(2);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad7949_spi_read_channel(struct ad7949_adc_chip *ad7949_adc, int *val,
|
||||
unsigned int channel)
|
||||
{
|
||||
int ret;
|
||||
int bits_per_word = ad7949_spi_bits_per_word(ad7949_adc);
|
||||
int mask = GENMASK(ad7949_adc->resolution, 0);
|
||||
struct spi_message msg;
|
||||
struct spi_transfer tx[] = {
|
||||
{
|
||||
.rx_buf = &ad7949_adc->buffer,
|
||||
.len = 4,
|
||||
.bits_per_word = bits_per_word,
|
||||
},
|
||||
};
|
||||
|
||||
ret = ad7949_spi_write_cfg(ad7949_adc,
|
||||
channel << AD7949_OFFSET_CHANNEL_SEL,
|
||||
AD7949_MASK_CHANNEL_SEL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ad7949_adc->buffer = 0;
|
||||
spi_message_init_with_transfers(&msg, tx, 1);
|
||||
ret = spi_sync(ad7949_adc->spi, &msg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* This delay is to avoid a new request before the required time to
|
||||
* send a new command to the device
|
||||
*/
|
||||
udelay(2);
|
||||
|
||||
ad7949_adc->current_channel = channel;
|
||||
|
||||
if (ad7949_spi_cfg_is_read_back(ad7949_adc))
|
||||
*val = (ad7949_adc->buffer >> AD7949_CFG_REG_SIZE_BITS) & mask;
|
||||
else
|
||||
*val = ad7949_adc->buffer & mask;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define AD7949_ADC_CHANNEL(chan) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = (chan), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec ad7949_adc_channels[] = {
|
||||
AD7949_ADC_CHANNEL(0),
|
||||
AD7949_ADC_CHANNEL(1),
|
||||
AD7949_ADC_CHANNEL(2),
|
||||
AD7949_ADC_CHANNEL(3),
|
||||
AD7949_ADC_CHANNEL(4),
|
||||
AD7949_ADC_CHANNEL(5),
|
||||
AD7949_ADC_CHANNEL(6),
|
||||
AD7949_ADC_CHANNEL(7),
|
||||
};
|
||||
|
||||
static int ad7949_spi_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct ad7949_adc_chip *ad7949_adc = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
if (!val)
|
||||
return -EINVAL;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
mutex_lock(&ad7949_adc->lock);
|
||||
ret = ad7949_spi_read_channel(ad7949_adc, val, chan->channel);
|
||||
mutex_unlock(&ad7949_adc->lock);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
ret = regulator_get_voltage(ad7949_adc->vref);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = ret / 5000;
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int ad7949_spi_reg_access(struct iio_dev *indio_dev,
|
||||
unsigned int reg, unsigned int writeval,
|
||||
unsigned int *readval)
|
||||
{
|
||||
struct ad7949_adc_chip *ad7949_adc = iio_priv(indio_dev);
|
||||
int ret = 0;
|
||||
|
||||
if (readval)
|
||||
*readval = ad7949_adc->cfg;
|
||||
else
|
||||
ret = ad7949_spi_write_cfg(ad7949_adc,
|
||||
writeval & AD7949_MASK_TOTAL, AD7949_MASK_TOTAL);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct iio_info ad7949_spi_info = {
|
||||
.read_raw = ad7949_spi_read_raw,
|
||||
.debugfs_reg_access = ad7949_spi_reg_access,
|
||||
};
|
||||
|
||||
static int ad7949_spi_init(struct ad7949_adc_chip *ad7949_adc)
|
||||
{
|
||||
int ret;
|
||||
int val;
|
||||
|
||||
/* Sequencer disabled, CFG readback disabled, IN0 as default channel */
|
||||
ad7949_adc->current_channel = 0;
|
||||
ret = ad7949_spi_write_cfg(ad7949_adc, 0x3C79, AD7949_MASK_TOTAL);
|
||||
|
||||
/*
|
||||
* Do two dummy conversions to apply the first configuration setting.
|
||||
* Required only after the start up of the device.
|
||||
*/
|
||||
ad7949_spi_read_channel(ad7949_adc, &val, ad7949_adc->current_channel);
|
||||
ad7949_spi_read_channel(ad7949_adc, &val, ad7949_adc->current_channel);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad7949_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
struct device *dev = &spi->dev;
|
||||
const struct ad7949_adc_spec *spec;
|
||||
struct ad7949_adc_chip *ad7949_adc;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(dev, sizeof(*ad7949_adc));
|
||||
if (!indio_dev) {
|
||||
dev_err(dev, "can not allocate iio device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
indio_dev->dev.parent = dev;
|
||||
indio_dev->dev.of_node = dev->of_node;
|
||||
indio_dev->info = &ad7949_spi_info;
|
||||
indio_dev->name = spi_get_device_id(spi)->name;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = ad7949_adc_channels;
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
|
||||
ad7949_adc = iio_priv(indio_dev);
|
||||
ad7949_adc->indio_dev = indio_dev;
|
||||
ad7949_adc->spi = spi;
|
||||
|
||||
spec = &ad7949_adc_spec[spi_get_device_id(spi)->driver_data];
|
||||
indio_dev->num_channels = spec->num_channels;
|
||||
ad7949_adc->resolution = spec->resolution;
|
||||
|
||||
ad7949_adc->vref = devm_regulator_get(dev, "vref");
|
||||
if (IS_ERR(ad7949_adc->vref)) {
|
||||
dev_err(dev, "fail to request regulator\n");
|
||||
return PTR_ERR(ad7949_adc->vref);
|
||||
}
|
||||
|
||||
ret = regulator_enable(ad7949_adc->vref);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "fail to enable regulator\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
mutex_init(&ad7949_adc->lock);
|
||||
|
||||
ret = ad7949_spi_init(ad7949_adc);
|
||||
if (ret) {
|
||||
dev_err(dev, "enable to init this device: %d\n", ret);
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret) {
|
||||
dev_err(dev, "fail to register iio device: %d\n", ret);
|
||||
goto err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
mutex_destroy(&ad7949_adc->lock);
|
||||
regulator_disable(ad7949_adc->vref);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad7949_spi_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct ad7949_adc_chip *ad7949_adc = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
mutex_destroy(&ad7949_adc->lock);
|
||||
regulator_disable(ad7949_adc->vref);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id ad7949_spi_of_id[] = {
|
||||
{ .compatible = "adi,ad7949" },
|
||||
{ .compatible = "adi,ad7682" },
|
||||
{ .compatible = "adi,ad7689" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ad7949_spi_of_id);
|
||||
|
||||
static const struct spi_device_id ad7949_spi_id[] = {
|
||||
{ "ad7949", ID_AD7949 },
|
||||
{ "ad7682", ID_AD7682 },
|
||||
{ "ad7689", ID_AD7689 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ad7949_spi_id);
|
||||
|
||||
static struct spi_driver ad7949_spi_driver = {
|
||||
.driver = {
|
||||
.name = "ad7949",
|
||||
.of_match_table = ad7949_spi_of_id,
|
||||
},
|
||||
.probe = ad7949_spi_probe,
|
||||
.remove = ad7949_spi_remove,
|
||||
.id_table = ad7949_spi_id,
|
||||
};
|
||||
module_spi_driver(ad7949_spi_driver);
|
||||
|
||||
MODULE_AUTHOR("Charles-Antoine Couret <charles-antoine.couret@essensium.com>");
|
||||
MODULE_DESCRIPTION("Analog Devices 14/16-bit 8-channel ADC driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -278,6 +278,7 @@ int ad_sigma_delta_single_conversion(struct iio_dev *indio_dev,
|
||||
{
|
||||
struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev);
|
||||
unsigned int sample, raw_sample;
|
||||
unsigned int data_reg;
|
||||
int ret = 0;
|
||||
|
||||
if (iio_buffer_enabled(indio_dev))
|
||||
@ -305,7 +306,12 @@ int ad_sigma_delta_single_conversion(struct iio_dev *indio_dev,
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ret = ad_sd_read_reg(sigma_delta, AD_SD_REG_DATA,
|
||||
if (sigma_delta->info->data_reg != 0)
|
||||
data_reg = sigma_delta->info->data_reg;
|
||||
else
|
||||
data_reg = AD_SD_REG_DATA;
|
||||
|
||||
ret = ad_sd_read_reg(sigma_delta, data_reg,
|
||||
DIV_ROUND_UP(chan->scan_type.realbits + chan->scan_type.shift, 8),
|
||||
&raw_sample);
|
||||
|
||||
@ -392,6 +398,7 @@ static irqreturn_t ad_sd_trigger_handler(int irq, void *p)
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev);
|
||||
unsigned int reg_size;
|
||||
unsigned int data_reg;
|
||||
uint8_t data[16];
|
||||
int ret;
|
||||
|
||||
@ -401,18 +408,23 @@ static irqreturn_t ad_sd_trigger_handler(int irq, void *p)
|
||||
indio_dev->channels[0].scan_type.shift;
|
||||
reg_size = DIV_ROUND_UP(reg_size, 8);
|
||||
|
||||
if (sigma_delta->info->data_reg != 0)
|
||||
data_reg = sigma_delta->info->data_reg;
|
||||
else
|
||||
data_reg = AD_SD_REG_DATA;
|
||||
|
||||
switch (reg_size) {
|
||||
case 4:
|
||||
case 2:
|
||||
case 1:
|
||||
ret = ad_sd_read_reg_raw(sigma_delta, AD_SD_REG_DATA,
|
||||
reg_size, &data[0]);
|
||||
ret = ad_sd_read_reg_raw(sigma_delta, data_reg, reg_size,
|
||||
&data[0]);
|
||||
break;
|
||||
case 3:
|
||||
/* We store 24 bit samples in a 32 bit word. Keep the upper
|
||||
* byte set to zero. */
|
||||
ret = ad_sd_read_reg_raw(sigma_delta, AD_SD_REG_DATA,
|
||||
reg_size, &data[1]);
|
||||
ret = ad_sd_read_reg_raw(sigma_delta, data_reg, reg_size,
|
||||
&data[1]);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -250,6 +250,7 @@ static int ina2xx_read_raw(struct iio_dev *indio_dev,
|
||||
*val2 = chip->shunt_resistor_uohm;
|
||||
return IIO_VAL_FRACTIONAL;
|
||||
}
|
||||
return -EINVAL;
|
||||
|
||||
case IIO_CHAN_INFO_HARDWAREGAIN:
|
||||
switch (chan->address) {
|
||||
@ -262,6 +263,7 @@ static int ina2xx_read_raw(struct iio_dev *indio_dev,
|
||||
*val = chip->range_vbus == 32 ? 1 : 2;
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
|
@ -1,13 +1,10 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* iio/adc/max11100.c
|
||||
* Maxim max11100 ADC Driver with IIO interface
|
||||
*
|
||||
* Copyright (C) 2016-17 Renesas Electronics Corporation
|
||||
* Copyright (C) 2016-17 Jacopo Mondi
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include <linux/delay.h>
|
||||
#include <linux/kernel.h>
|
||||
|
@ -1,3 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* iio/adc/max9611.c
|
||||
*
|
||||
@ -5,10 +6,6 @@
|
||||
* 12-bit ADC interface.
|
||||
*
|
||||
* Copyright (C) 2017 Jacopo Mondi
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
/*
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include <linux/io.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/nvmem-consumer.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_irq.h>
|
||||
@ -165,6 +166,14 @@
|
||||
|
||||
#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_TEMP_OFFSET 27
|
||||
|
||||
/* temperature sensor calibration information in eFuse */
|
||||
#define MESON_SAR_ADC_EFUSE_BYTES 4
|
||||
#define MESON_SAR_ADC_EFUSE_BYTE3_UPPER_ADC_VAL GENMASK(6, 0)
|
||||
#define MESON_SAR_ADC_EFUSE_BYTE3_IS_CALIBRATED BIT(7)
|
||||
|
||||
/* for use with IIO_VAL_INT_PLUS_MICRO */
|
||||
#define MILLION 1000000
|
||||
|
||||
@ -175,16 +184,25 @@
|
||||
.address = _chan, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_AVERAGE_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
|
||||
BIT(IIO_CHAN_INFO_CALIBBIAS) | \
|
||||
.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_CH"#_chan, \
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: the hardware supports IIO_TEMP for channel 6 as well which is
|
||||
* currently not supported by this driver.
|
||||
*/
|
||||
#define MESON_SAR_ADC_TEMP_CHAN(_chan) { \
|
||||
.type = IIO_TEMP, \
|
||||
.channel = _chan, \
|
||||
.address = MESON_SAR_ADC_VOLTAGE_AND_TEMP_CHANNEL, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_AVERAGE_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) | \
|
||||
BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_CALIBBIAS) | \
|
||||
BIT(IIO_CHAN_INFO_CALIBSCALE), \
|
||||
.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),
|
||||
@ -197,6 +215,19 @@ static const struct iio_chan_spec meson_sar_adc_iio_channels[] = {
|
||||
IIO_CHAN_SOFT_TIMESTAMP(8),
|
||||
};
|
||||
|
||||
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_avg_mode {
|
||||
NO_AVERAGING = 0x0,
|
||||
MEAN_AVERAGING = 0x1,
|
||||
@ -225,6 +256,9 @@ struct meson_sar_adc_param {
|
||||
u32 bandgap_reg;
|
||||
unsigned int resolution;
|
||||
const struct regmap_config *regmap_config;
|
||||
u8 temperature_trimming_bits;
|
||||
unsigned int temperature_multiplier;
|
||||
unsigned int temperature_divider;
|
||||
};
|
||||
|
||||
struct meson_sar_adc_data {
|
||||
@ -246,6 +280,9 @@ struct meson_sar_adc_priv {
|
||||
struct completion done;
|
||||
int calibbias;
|
||||
int calibscale;
|
||||
bool temperature_sensor_calibrated;
|
||||
u8 temperature_sensor_coefficient;
|
||||
u16 temperature_sensor_adc_val;
|
||||
};
|
||||
|
||||
static const struct regmap_config meson_sar_adc_regmap_config_gxbb = {
|
||||
@ -389,9 +426,16 @@ static void meson_sar_adc_enable_channel(struct iio_dev *indio_dev,
|
||||
MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_MUX_SEL_MASK,
|
||||
regval);
|
||||
|
||||
if (chan->address == 6)
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELTA_10,
|
||||
MESON_SAR_ADC_DELTA_10_TEMP_SEL, 0);
|
||||
if (chan->address == MESON_SAR_ADC_VOLTAGE_AND_TEMP_CHANNEL) {
|
||||
if (chan->type == IIO_TEMP)
|
||||
regval = MESON_SAR_ADC_DELTA_10_TEMP_SEL;
|
||||
else
|
||||
regval = 0;
|
||||
|
||||
regmap_update_bits(priv->regmap,
|
||||
MESON_SAR_ADC_DELTA_10,
|
||||
MESON_SAR_ADC_DELTA_10_TEMP_SEL, regval);
|
||||
}
|
||||
}
|
||||
|
||||
static void meson_sar_adc_set_chan7_mux(struct iio_dev *indio_dev,
|
||||
@ -506,8 +550,12 @@ static int meson_sar_adc_get_sample(struct iio_dev *indio_dev,
|
||||
enum meson_sar_adc_num_samples avg_samples,
|
||||
int *val)
|
||||
{
|
||||
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
if (chan->type == IIO_TEMP && !priv->temperature_sensor_calibrated)
|
||||
return -ENOTSUPP;
|
||||
|
||||
ret = meson_sar_adc_lock(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
@ -555,16 +603,30 @@ static int meson_sar_adc_iio_info_read_raw(struct iio_dev *indio_dev,
|
||||
break;
|
||||
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
ret = regulator_get_voltage(priv->vref);
|
||||
if (ret < 0) {
|
||||
dev_err(indio_dev->dev.parent,
|
||||
"failed to get vref voltage: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
if (chan->type == IIO_VOLTAGE) {
|
||||
ret = regulator_get_voltage(priv->vref);
|
||||
if (ret < 0) {
|
||||
dev_err(indio_dev->dev.parent,
|
||||
"failed to get vref voltage: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*val = ret / 1000;
|
||||
*val2 = priv->param->resolution;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
*val = ret / 1000;
|
||||
*val2 = priv->param->resolution;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
} else if (chan->type == IIO_TEMP) {
|
||||
/* SoC specific multiplier and divider */
|
||||
*val = priv->param->temperature_multiplier;
|
||||
*val2 = priv->param->temperature_divider;
|
||||
|
||||
/* celsius to millicelsius */
|
||||
*val *= 1000;
|
||||
|
||||
return IIO_VAL_FRACTIONAL;
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
*val = priv->calibbias;
|
||||
@ -575,6 +637,13 @@ static int meson_sar_adc_iio_info_read_raw(struct iio_dev *indio_dev,
|
||||
*val2 = priv->calibscale % MILLION;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
*val = DIV_ROUND_CLOSEST(MESON_SAR_ADC_TEMP_OFFSET *
|
||||
priv->param->temperature_divider,
|
||||
priv->param->temperature_multiplier);
|
||||
*val -= priv->temperature_sensor_adc_val;
|
||||
return IIO_VAL_INT;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -625,6 +694,65 @@ static int meson_sar_adc_clk_init(struct iio_dev *indio_dev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int meson_sar_adc_temp_sensor_init(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
|
||||
u8 *buf, trimming_bits, trimming_mask, upper_adc_val;
|
||||
struct nvmem_cell *temperature_calib;
|
||||
size_t read_len;
|
||||
int ret;
|
||||
|
||||
temperature_calib = devm_nvmem_cell_get(&indio_dev->dev,
|
||||
"temperature_calib");
|
||||
if (IS_ERR(temperature_calib)) {
|
||||
ret = PTR_ERR(temperature_calib);
|
||||
|
||||
/*
|
||||
* leave the temperature sensor disabled if no calibration data
|
||||
* was passed via nvmem-cells.
|
||||
*/
|
||||
if (ret == -ENODEV)
|
||||
return 0;
|
||||
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(indio_dev->dev.parent,
|
||||
"failed to get temperature_calib cell\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
read_len = MESON_SAR_ADC_EFUSE_BYTES;
|
||||
buf = nvmem_cell_read(temperature_calib, &read_len);
|
||||
if (IS_ERR(buf)) {
|
||||
dev_err(indio_dev->dev.parent,
|
||||
"failed to read temperature_calib cell\n");
|
||||
return PTR_ERR(buf);
|
||||
} else if (read_len != MESON_SAR_ADC_EFUSE_BYTES) {
|
||||
kfree(buf);
|
||||
dev_err(indio_dev->dev.parent,
|
||||
"invalid read size of temperature_calib cell\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
trimming_bits = priv->param->temperature_trimming_bits;
|
||||
trimming_mask = BIT(trimming_bits) - 1;
|
||||
|
||||
priv->temperature_sensor_calibrated =
|
||||
buf[3] & MESON_SAR_ADC_EFUSE_BYTE3_IS_CALIBRATED;
|
||||
priv->temperature_sensor_coefficient = buf[2] & trimming_mask;
|
||||
|
||||
upper_adc_val = FIELD_GET(MESON_SAR_ADC_EFUSE_BYTE3_UPPER_ADC_VAL,
|
||||
buf[3]);
|
||||
|
||||
priv->temperature_sensor_adc_val = buf[2];
|
||||
priv->temperature_sensor_adc_val |= upper_adc_val << BITS_PER_BYTE;
|
||||
priv->temperature_sensor_adc_val >>= trimming_bits;
|
||||
|
||||
kfree(buf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int meson_sar_adc_init(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
|
||||
@ -649,10 +777,12 @@ static int meson_sar_adc_init(struct iio_dev *indio_dev)
|
||||
|
||||
meson_sar_adc_stop_sample_engine(indio_dev);
|
||||
|
||||
/* update the channel 6 MUX to select the temperature sensor */
|
||||
/*
|
||||
* disable this bit as seems to be only relevant for Meson6 (based
|
||||
* on the vendor driver), which we don't support at the moment.
|
||||
*/
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0,
|
||||
MESON_SAR_ADC_REG0_ADC_TEMP_SEN_SEL,
|
||||
MESON_SAR_ADC_REG0_ADC_TEMP_SEN_SEL);
|
||||
MESON_SAR_ADC_REG0_ADC_TEMP_SEN_SEL, 0);
|
||||
|
||||
/* disable all channels by default */
|
||||
regmap_write(priv->regmap, MESON_SAR_ADC_CHAN_LIST, 0x0);
|
||||
@ -709,6 +839,29 @@ static int meson_sar_adc_init(struct iio_dev *indio_dev)
|
||||
regval |= MESON_SAR_ADC_AUX_SW_XP_DRIVE_SW;
|
||||
regmap_write(priv->regmap, MESON_SAR_ADC_AUX_SW, regval);
|
||||
|
||||
if (priv->temperature_sensor_calibrated) {
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELTA_10,
|
||||
MESON_SAR_ADC_DELTA_10_TS_REVE1,
|
||||
MESON_SAR_ADC_DELTA_10_TS_REVE1);
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELTA_10,
|
||||
MESON_SAR_ADC_DELTA_10_TS_REVE0,
|
||||
MESON_SAR_ADC_DELTA_10_TS_REVE0);
|
||||
|
||||
/*
|
||||
* set bits [3:0] of the TSC (temperature sensor coefficient)
|
||||
* to get the correct values when reading the temperature.
|
||||
*/
|
||||
regval = FIELD_PREP(MESON_SAR_ADC_DELTA_10_TS_C_MASK,
|
||||
priv->temperature_sensor_coefficient);
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELTA_10,
|
||||
MESON_SAR_ADC_DELTA_10_TS_C_MASK, regval);
|
||||
} else {
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELTA_10,
|
||||
MESON_SAR_ADC_DELTA_10_TS_REVE1, 0);
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELTA_10,
|
||||
MESON_SAR_ADC_DELTA_10_TS_REVE0, 0);
|
||||
}
|
||||
|
||||
ret = clk_set_parent(priv->adc_sel_clk, priv->clkin);
|
||||
if (ret) {
|
||||
dev_err(indio_dev->dev.parent,
|
||||
@ -894,6 +1047,17 @@ static const struct meson_sar_adc_param meson_sar_adc_meson8_param = {
|
||||
.bandgap_reg = MESON_SAR_ADC_DELTA_10,
|
||||
.regmap_config = &meson_sar_adc_regmap_config_meson8,
|
||||
.resolution = 10,
|
||||
.temperature_trimming_bits = 4,
|
||||
.temperature_multiplier = 18 * 10000,
|
||||
.temperature_divider = 1024 * 10 * 85,
|
||||
};
|
||||
|
||||
static const struct meson_sar_adc_param meson_sar_adc_meson8b_param = {
|
||||
.has_bl30_integration = false,
|
||||
.clock_rate = 1150000,
|
||||
.bandgap_reg = MESON_SAR_ADC_DELTA_10,
|
||||
.regmap_config = &meson_sar_adc_regmap_config_meson8,
|
||||
.resolution = 10,
|
||||
};
|
||||
|
||||
static const struct meson_sar_adc_param meson_sar_adc_gxbb_param = {
|
||||
@ -918,12 +1082,12 @@ static const struct meson_sar_adc_data meson_sar_adc_meson8_data = {
|
||||
};
|
||||
|
||||
static const struct meson_sar_adc_data meson_sar_adc_meson8b_data = {
|
||||
.param = &meson_sar_adc_meson8_param,
|
||||
.param = &meson_sar_adc_meson8b_param,
|
||||
.name = "meson-meson8b-saradc",
|
||||
};
|
||||
|
||||
static const struct meson_sar_adc_data meson_sar_adc_meson8m2_data = {
|
||||
.param = &meson_sar_adc_meson8_param,
|
||||
.param = &meson_sar_adc_meson8b_param,
|
||||
.name = "meson-meson8m2-saradc",
|
||||
};
|
||||
|
||||
@ -1009,9 +1173,6 @@ static int meson_sar_adc_probe(struct platform_device *pdev)
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &meson_sar_adc_iio_info;
|
||||
|
||||
indio_dev->channels = meson_sar_adc_iio_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(meson_sar_adc_iio_channels);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(base))
|
||||
@ -1078,6 +1239,22 @@ static int meson_sar_adc_probe(struct platform_device *pdev)
|
||||
|
||||
priv->calibscale = MILLION;
|
||||
|
||||
if (priv->param->temperature_trimming_bits) {
|
||||
ret = meson_sar_adc_temp_sensor_init(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (priv->temperature_sensor_calibrated) {
|
||||
indio_dev->channels = meson_sar_adc_and_temp_iio_channels;
|
||||
indio_dev->num_channels =
|
||||
ARRAY_SIZE(meson_sar_adc_and_temp_iio_channels);
|
||||
} else {
|
||||
indio_dev->channels = meson_sar_adc_iio_channels;
|
||||
indio_dev->num_channels =
|
||||
ARRAY_SIZE(meson_sar_adc_iio_channels);
|
||||
}
|
||||
|
||||
ret = meson_sar_adc_init(indio_dev);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
@ -1,17 +1,8 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Renesas R-Car GyroADC driver
|
||||
*
|
||||
* Copyright 2016 Marek Vasut <marek.vasut@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
|
@ -52,6 +52,9 @@
|
||||
/* Timeout (ms) for the trylock of hardware spinlocks */
|
||||
#define SC27XX_ADC_HWLOCK_TIMEOUT 5000
|
||||
|
||||
/* Timeout (ms) for ADC data conversion according to ADC datasheet */
|
||||
#define SC27XX_ADC_RDY_TIMEOUT 100
|
||||
|
||||
/* Maximum ADC channel number */
|
||||
#define SC27XX_ADC_CHANNEL_MAX 32
|
||||
|
||||
@ -223,7 +226,14 @@ static int sc27xx_adc_read(struct sc27xx_adc_data *data, int channel,
|
||||
if (ret)
|
||||
goto disable_adc;
|
||||
|
||||
wait_for_completion(&data->completion);
|
||||
ret = wait_for_completion_timeout(&data->completion,
|
||||
msecs_to_jiffies(SC27XX_ADC_RDY_TIMEOUT));
|
||||
if (!ret) {
|
||||
dev_err(data->dev, "read ADC data timeout\n");
|
||||
ret = -ETIMEDOUT;
|
||||
} else {
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
disable_adc:
|
||||
regmap_update_bits(data->regmap, data->base + SC27XX_ADC_CTL,
|
||||
|
@ -1,3 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2014 Angelo Compagnucci <angelo.compagnucci@gmail.com>
|
||||
*
|
||||
@ -6,16 +7,14 @@
|
||||
* http://www.ti.com/lit/ds/symlink/adc128s052.pdf
|
||||
* http://www.ti.com/lit/ds/symlink/adc122s021.pdf
|
||||
* http://www.ti.com/lit/ds/symlink/adc124s021.pdf
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
struct adc128_configuration {
|
||||
@ -135,10 +134,15 @@ static const struct iio_info adc128_info = {
|
||||
static int adc128_probe(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
unsigned int config;
|
||||
struct adc128 *adc;
|
||||
int config = spi_get_device_id(spi)->driver_data;
|
||||
int ret;
|
||||
|
||||
if (dev_fwnode(&spi->dev))
|
||||
config = (unsigned long) device_get_match_data(&spi->dev);
|
||||
else
|
||||
config = spi_get_device_id(spi)->driver_data;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
@ -186,23 +190,40 @@ static int adc128_remove(struct spi_device *spi)
|
||||
static const struct of_device_id adc128_of_match[] = {
|
||||
{ .compatible = "ti,adc128s052", },
|
||||
{ .compatible = "ti,adc122s021", },
|
||||
{ .compatible = "ti,adc122s051", },
|
||||
{ .compatible = "ti,adc122s101", },
|
||||
{ .compatible = "ti,adc124s021", },
|
||||
{ .compatible = "ti,adc124s051", },
|
||||
{ .compatible = "ti,adc124s101", },
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, adc128_of_match);
|
||||
|
||||
static const struct spi_device_id adc128_id[] = {
|
||||
{ "adc128s052", 0}, /* index into adc128_config */
|
||||
{ "adc122s021", 1},
|
||||
{ "adc124s021", 2},
|
||||
{ "adc128s052", 0 }, /* index into adc128_config */
|
||||
{ "adc122s021", 1 },
|
||||
{ "adc122s051", 1 },
|
||||
{ "adc122s101", 1 },
|
||||
{ "adc124s021", 2 },
|
||||
{ "adc124s051", 2 },
|
||||
{ "adc124s101", 2 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, adc128_id);
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
static const struct acpi_device_id adc128_acpi_match[] = {
|
||||
{ "AANT1280", 2 }, /* ADC124S021 compatible ACPI ID */
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, adc128_acpi_match);
|
||||
#endif
|
||||
|
||||
static struct spi_driver adc128_driver = {
|
||||
.driver = {
|
||||
.name = "adc128s052",
|
||||
.of_match_table = of_match_ptr(adc128_of_match),
|
||||
.acpi_match_table = ACPI_PTR(adc128_acpi_match),
|
||||
},
|
||||
.probe = adc128_probe,
|
||||
.remove = adc128_remove,
|
||||
|
@ -462,43 +462,35 @@ static struct ssp_data *ssp_parse_dt(struct device *dev)
|
||||
|
||||
data->mcu_ap_gpio = of_get_named_gpio(node, "mcu-ap-gpios", 0);
|
||||
if (data->mcu_ap_gpio < 0)
|
||||
goto err_free_pd;
|
||||
return NULL;
|
||||
|
||||
data->ap_mcu_gpio = of_get_named_gpio(node, "ap-mcu-gpios", 0);
|
||||
if (data->ap_mcu_gpio < 0)
|
||||
goto err_free_pd;
|
||||
return NULL;
|
||||
|
||||
data->mcu_reset_gpio = of_get_named_gpio(node, "mcu-reset-gpios", 0);
|
||||
if (data->mcu_reset_gpio < 0)
|
||||
goto err_free_pd;
|
||||
return NULL;
|
||||
|
||||
ret = devm_gpio_request_one(dev, data->ap_mcu_gpio, GPIOF_OUT_INIT_HIGH,
|
||||
"ap-mcu-gpios");
|
||||
if (ret)
|
||||
goto err_free_pd;
|
||||
return NULL;
|
||||
|
||||
ret = devm_gpio_request_one(dev, data->mcu_reset_gpio,
|
||||
GPIOF_OUT_INIT_HIGH, "mcu-reset-gpios");
|
||||
if (ret)
|
||||
goto err_ap_mcu;
|
||||
return NULL;
|
||||
|
||||
match = of_match_node(ssp_of_match, node);
|
||||
if (!match)
|
||||
goto err_mcu_reset_gpio;
|
||||
return NULL;
|
||||
|
||||
data->sensorhub_info = match->data;
|
||||
|
||||
dev_set_drvdata(dev, data);
|
||||
|
||||
return data;
|
||||
|
||||
err_mcu_reset_gpio:
|
||||
devm_gpio_free(dev, data->mcu_reset_gpio);
|
||||
err_ap_mcu:
|
||||
devm_gpio_free(dev, data->ap_mcu_gpio);
|
||||
err_free_pd:
|
||||
devm_kfree(dev, data);
|
||||
return NULL;
|
||||
}
|
||||
#else
|
||||
static struct ssp_data *ssp_parse_dt(struct device *pdev)
|
||||
|
@ -133,7 +133,7 @@ static int st_sensors_match_fs(struct st_sensor_settings *sensor_settings,
|
||||
|
||||
for (i = 0; i < ST_SENSORS_FULLSCALE_AVL_MAX; i++) {
|
||||
if (sensor_settings->fs.fs_avl[i].num == 0)
|
||||
goto st_sensors_match_odr_error;
|
||||
return ret;
|
||||
|
||||
if (sensor_settings->fs.fs_avl[i].num == fs) {
|
||||
*index_fs_avl = i;
|
||||
@ -142,7 +142,6 @@ static int st_sensors_match_fs(struct st_sensor_settings *sensor_settings,
|
||||
}
|
||||
}
|
||||
|
||||
st_sensors_match_odr_error:
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -104,7 +104,7 @@ static irqreturn_t st_sensors_irq_thread(int irq, void *p)
|
||||
return IRQ_HANDLED;
|
||||
|
||||
/*
|
||||
* If we are using egde IRQs, new samples arrived while processing
|
||||
* If we are using edge IRQs, new samples arrived while processing
|
||||
* the IRQ and those may be missed unless we pick them here, so poll
|
||||
* again. If the sensor delivery frequency is very high, this thread
|
||||
* turns into a polled loop handler.
|
||||
@ -148,7 +148,7 @@ int st_sensors_allocate_trigger(struct iio_dev *indio_dev,
|
||||
if (!sdata->sensor_settings->drdy_irq.addr_ihl) {
|
||||
dev_err(&indio_dev->dev,
|
||||
"falling/low specified for IRQ "
|
||||
"but hardware only support rising/high: "
|
||||
"but hardware supports only rising/high: "
|
||||
"will request rising/high\n");
|
||||
if (irq_trig == IRQF_TRIGGER_FALLING)
|
||||
irq_trig = IRQF_TRIGGER_RISING;
|
||||
|
@ -366,6 +366,15 @@ config TI_DAC5571
|
||||
|
||||
If compiled as a module, it will be called ti-dac5571.
|
||||
|
||||
config TI_DAC7311
|
||||
tristate "Texas Instruments 8/10/12-bit 1-channel DAC driver"
|
||||
depends on SPI
|
||||
help
|
||||
Driver for the Texas Instruments
|
||||
DAC7311, DAC6311, DAC5311.
|
||||
|
||||
If compiled as a module, it will be called ti-dac7311.
|
||||
|
||||
config VF610_DAC
|
||||
tristate "Vybrid vf610 DAC driver"
|
||||
depends on OF
|
||||
|
@ -40,4 +40,5 @@ obj-$(CONFIG_STM32_DAC_CORE) += stm32-dac-core.o
|
||||
obj-$(CONFIG_STM32_DAC) += stm32-dac.o
|
||||
obj-$(CONFIG_TI_DAC082S085) += ti-dac082s085.o
|
||||
obj-$(CONFIG_TI_DAC5571) += ti-dac5571.o
|
||||
obj-$(CONFIG_TI_DAC7311) += ti-dac7311.o
|
||||
obj-$(CONFIG_VF610_DAC) += vf610_dac.o
|
||||
|
@ -74,11 +74,11 @@ static int dpot_dac_read_raw(struct iio_dev *indio_dev,
|
||||
case IIO_VAL_INT:
|
||||
/*
|
||||
* Convert integer scale to fractional scale by
|
||||
* setting the denominator (val2) to one...
|
||||
* setting the denominator (val2) to one, and...
|
||||
*/
|
||||
*val2 = 1;
|
||||
ret = IIO_VAL_FRACTIONAL;
|
||||
/* ...and fall through. */
|
||||
/* fall through */
|
||||
case IIO_VAL_FRACTIONAL:
|
||||
*val *= regulator_get_voltage(dac->vref) / 1000;
|
||||
*val2 *= dac->max_ohms;
|
||||
|
338
drivers/iio/dac/ti-dac7311.c
Normal file
338
drivers/iio/dac/ti-dac7311.c
Normal file
@ -0,0 +1,338 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* ti-dac7311.c - Texas Instruments 8/10/12-bit 1-channel DAC driver
|
||||
*
|
||||
* Copyright (C) 2018 CMC NV
|
||||
*
|
||||
* http://www.ti.com/lit/ds/symlink/dac7311.pdf
|
||||
*/
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
enum {
|
||||
ID_DAC5311 = 0,
|
||||
ID_DAC6311,
|
||||
ID_DAC7311,
|
||||
};
|
||||
|
||||
enum {
|
||||
POWER_1KOHM_TO_GND = 0,
|
||||
POWER_100KOHM_TO_GND,
|
||||
POWER_TRI_STATE,
|
||||
};
|
||||
|
||||
struct ti_dac_spec {
|
||||
u8 resolution;
|
||||
};
|
||||
|
||||
static const struct ti_dac_spec ti_dac_spec[] = {
|
||||
[ID_DAC5311] = { .resolution = 8 },
|
||||
[ID_DAC6311] = { .resolution = 10 },
|
||||
[ID_DAC7311] = { .resolution = 12 },
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ti_dac_chip - TI DAC chip
|
||||
* @lock: protects write sequences
|
||||
* @vref: regulator generating Vref
|
||||
* @spi: SPI device to send data to the device
|
||||
* @val: cached value
|
||||
* @powerdown: whether the chip is powered down
|
||||
* @powerdown_mode: selected by the user
|
||||
* @resolution: resolution of the chip
|
||||
* @buf: buffer for transfer data
|
||||
*/
|
||||
struct ti_dac_chip {
|
||||
struct mutex lock;
|
||||
struct regulator *vref;
|
||||
struct spi_device *spi;
|
||||
u16 val;
|
||||
bool powerdown;
|
||||
u8 powerdown_mode;
|
||||
u8 resolution;
|
||||
u8 buf[2] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
static u8 ti_dac_get_power(struct ti_dac_chip *ti_dac, bool powerdown)
|
||||
{
|
||||
if (powerdown)
|
||||
return ti_dac->powerdown_mode + 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ti_dac_cmd(struct ti_dac_chip *ti_dac, u8 power, u16 val)
|
||||
{
|
||||
u8 shift = 14 - ti_dac->resolution;
|
||||
|
||||
ti_dac->buf[0] = (val << shift) & 0xFF;
|
||||
ti_dac->buf[1] = (power << 6) | (val >> (8 - shift));
|
||||
return spi_write(ti_dac->spi, ti_dac->buf, 2);
|
||||
}
|
||||
|
||||
static const char * const ti_dac_powerdown_modes[] = {
|
||||
"1kohm_to_gnd",
|
||||
"100kohm_to_gnd",
|
||||
"three_state",
|
||||
};
|
||||
|
||||
static int ti_dac_get_powerdown_mode(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan)
|
||||
{
|
||||
struct ti_dac_chip *ti_dac = iio_priv(indio_dev);
|
||||
|
||||
return ti_dac->powerdown_mode;
|
||||
}
|
||||
|
||||
static int ti_dac_set_powerdown_mode(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
unsigned int mode)
|
||||
{
|
||||
struct ti_dac_chip *ti_dac = iio_priv(indio_dev);
|
||||
|
||||
ti_dac->powerdown_mode = mode;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct iio_enum ti_dac_powerdown_mode = {
|
||||
.items = ti_dac_powerdown_modes,
|
||||
.num_items = ARRAY_SIZE(ti_dac_powerdown_modes),
|
||||
.get = ti_dac_get_powerdown_mode,
|
||||
.set = ti_dac_set_powerdown_mode,
|
||||
};
|
||||
|
||||
static ssize_t ti_dac_read_powerdown(struct iio_dev *indio_dev,
|
||||
uintptr_t private,
|
||||
const struct iio_chan_spec *chan,
|
||||
char *buf)
|
||||
{
|
||||
struct ti_dac_chip *ti_dac = iio_priv(indio_dev);
|
||||
|
||||
return sprintf(buf, "%d\n", ti_dac->powerdown);
|
||||
}
|
||||
|
||||
static ssize_t ti_dac_write_powerdown(struct iio_dev *indio_dev,
|
||||
uintptr_t private,
|
||||
const struct iio_chan_spec *chan,
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
struct ti_dac_chip *ti_dac = iio_priv(indio_dev);
|
||||
bool powerdown;
|
||||
u8 power;
|
||||
int ret;
|
||||
|
||||
ret = strtobool(buf, &powerdown);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
power = ti_dac_get_power(ti_dac, powerdown);
|
||||
|
||||
mutex_lock(&ti_dac->lock);
|
||||
ret = ti_dac_cmd(ti_dac, power, 0);
|
||||
if (!ret)
|
||||
ti_dac->powerdown = powerdown;
|
||||
mutex_unlock(&ti_dac->lock);
|
||||
|
||||
return ret ? ret : len;
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec_ext_info ti_dac_ext_info[] = {
|
||||
{
|
||||
.name = "powerdown",
|
||||
.read = ti_dac_read_powerdown,
|
||||
.write = ti_dac_write_powerdown,
|
||||
.shared = IIO_SHARED_BY_TYPE,
|
||||
},
|
||||
IIO_ENUM("powerdown_mode", IIO_SHARED_BY_TYPE, &ti_dac_powerdown_mode),
|
||||
IIO_ENUM_AVAILABLE("powerdown_mode", &ti_dac_powerdown_mode),
|
||||
{ },
|
||||
};
|
||||
|
||||
#define TI_DAC_CHANNEL(chan) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.channel = (chan), \
|
||||
.output = true, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.ext_info = ti_dac_ext_info, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec ti_dac_channels[] = {
|
||||
TI_DAC_CHANNEL(0),
|
||||
};
|
||||
|
||||
static int ti_dac_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct ti_dac_chip *ti_dac = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
*val = ti_dac->val;
|
||||
return IIO_VAL_INT;
|
||||
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
ret = regulator_get_voltage(ti_dac->vref);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = ret / 1000;
|
||||
*val2 = ti_dac->resolution;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int ti_dac_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
struct ti_dac_chip *ti_dac = iio_priv(indio_dev);
|
||||
u8 power = ti_dac_get_power(ti_dac, ti_dac->powerdown);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (ti_dac->val == val)
|
||||
return 0;
|
||||
|
||||
if (val >= (1 << ti_dac->resolution) || val < 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (ti_dac->powerdown)
|
||||
return -EBUSY;
|
||||
|
||||
mutex_lock(&ti_dac->lock);
|
||||
ret = ti_dac_cmd(ti_dac, power, val);
|
||||
if (!ret)
|
||||
ti_dac->val = val;
|
||||
mutex_unlock(&ti_dac->lock);
|
||||
break;
|
||||
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ti_dac_write_raw_get_fmt(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, long mask)
|
||||
{
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
static const struct iio_info ti_dac_info = {
|
||||
.read_raw = ti_dac_read_raw,
|
||||
.write_raw = ti_dac_write_raw,
|
||||
.write_raw_get_fmt = ti_dac_write_raw_get_fmt,
|
||||
};
|
||||
|
||||
static int ti_dac_probe(struct spi_device *spi)
|
||||
{
|
||||
struct device *dev = &spi->dev;
|
||||
const struct ti_dac_spec *spec;
|
||||
struct ti_dac_chip *ti_dac;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(dev, sizeof(*ti_dac));
|
||||
if (!indio_dev) {
|
||||
dev_err(dev, "can not allocate iio device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
spi->mode = SPI_MODE_1;
|
||||
spi->bits_per_word = 16;
|
||||
spi_setup(spi);
|
||||
|
||||
indio_dev->dev.parent = dev;
|
||||
indio_dev->dev.of_node = spi->dev.of_node;
|
||||
indio_dev->info = &ti_dac_info;
|
||||
indio_dev->name = spi_get_device_id(spi)->name;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = ti_dac_channels;
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
|
||||
ti_dac = iio_priv(indio_dev);
|
||||
ti_dac->powerdown = false;
|
||||
ti_dac->spi = spi;
|
||||
|
||||
spec = &ti_dac_spec[spi_get_device_id(spi)->driver_data];
|
||||
indio_dev->num_channels = 1;
|
||||
ti_dac->resolution = spec->resolution;
|
||||
|
||||
ti_dac->vref = devm_regulator_get(dev, "vref");
|
||||
if (IS_ERR(ti_dac->vref)) {
|
||||
dev_err(dev, "error to get regulator\n");
|
||||
return PTR_ERR(ti_dac->vref);
|
||||
}
|
||||
|
||||
ret = regulator_enable(ti_dac->vref);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "can not enable regulator\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
mutex_init(&ti_dac->lock);
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret) {
|
||||
dev_err(dev, "fail to register iio device: %d\n", ret);
|
||||
goto err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
mutex_destroy(&ti_dac->lock);
|
||||
regulator_disable(ti_dac->vref);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ti_dac_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct ti_dac_chip *ti_dac = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
mutex_destroy(&ti_dac->lock);
|
||||
regulator_disable(ti_dac->vref);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id ti_dac_of_id[] = {
|
||||
{ .compatible = "ti,dac5311" },
|
||||
{ .compatible = "ti,dac6311" },
|
||||
{ .compatible = "ti,dac7311" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ti_dac_of_id);
|
||||
|
||||
static const struct spi_device_id ti_dac_spi_id[] = {
|
||||
{ "dac5311", ID_DAC5311 },
|
||||
{ "dac6311", ID_DAC6311 },
|
||||
{ "dac7311", ID_DAC7311 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ti_dac_spi_id);
|
||||
|
||||
static struct spi_driver ti_dac_driver = {
|
||||
.driver = {
|
||||
.name = "ti-dac7311",
|
||||
.of_match_table = ti_dac_of_id,
|
||||
},
|
||||
.probe = ti_dac_probe,
|
||||
.remove = ti_dac_remove,
|
||||
.id_table = ti_dac_spi_id,
|
||||
};
|
||||
module_spi_driver(ti_dac_driver);
|
||||
|
||||
MODULE_AUTHOR("Charles-Antoine Couret <charles-antoine.couret@essensium.com>");
|
||||
MODULE_DESCRIPTION("Texas Instruments 8/10/12-bit 1-channel DAC driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -1,4 +1,5 @@
|
||||
st_lsm6dsx-y := st_lsm6dsx_core.o st_lsm6dsx_buffer.o
|
||||
st_lsm6dsx-y := st_lsm6dsx_core.o st_lsm6dsx_buffer.o \
|
||||
st_lsm6dsx_shub.o
|
||||
|
||||
obj-$(CONFIG_IIO_ST_LSM6DSX) += st_lsm6dsx.o
|
||||
obj-$(CONFIG_IIO_ST_LSM6DSX_I2C) += st_lsm6dsx_i2c.o
|
||||
|
@ -43,6 +43,24 @@ enum st_lsm6dsx_hw_id {
|
||||
* ST_LSM6DSX_TAGGED_SAMPLE_SIZE)
|
||||
#define ST_LSM6DSX_SHIFT_VAL(val, mask) (((val) << __ffs(mask)) & (mask))
|
||||
|
||||
#define ST_LSM6DSX_CHANNEL(chan_type, addr, mod, scan_idx) \
|
||||
{ \
|
||||
.type = chan_type, \
|
||||
.address = addr, \
|
||||
.modified = 1, \
|
||||
.channel2 = mod, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
|
||||
.scan_index = scan_idx, \
|
||||
.scan_type = { \
|
||||
.sign = 's', \
|
||||
.realbits = 16, \
|
||||
.storagebits = 16, \
|
||||
.endianness = IIO_LE, \
|
||||
}, \
|
||||
}
|
||||
|
||||
struct st_lsm6dsx_reg {
|
||||
u8 addr;
|
||||
u8 mask;
|
||||
@ -50,6 +68,28 @@ struct st_lsm6dsx_reg {
|
||||
|
||||
struct st_lsm6dsx_hw;
|
||||
|
||||
struct st_lsm6dsx_odr {
|
||||
u16 hz;
|
||||
u8 val;
|
||||
};
|
||||
|
||||
#define ST_LSM6DSX_ODR_LIST_SIZE 6
|
||||
struct st_lsm6dsx_odr_table_entry {
|
||||
struct st_lsm6dsx_reg reg;
|
||||
struct st_lsm6dsx_odr odr_avl[ST_LSM6DSX_ODR_LIST_SIZE];
|
||||
};
|
||||
|
||||
struct st_lsm6dsx_fs {
|
||||
u32 gain;
|
||||
u8 val;
|
||||
};
|
||||
|
||||
#define ST_LSM6DSX_FS_LIST_SIZE 4
|
||||
struct st_lsm6dsx_fs_table_entry {
|
||||
struct st_lsm6dsx_reg reg;
|
||||
struct st_lsm6dsx_fs fs_avl[ST_LSM6DSX_FS_LIST_SIZE];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct st_lsm6dsx_fifo_ops - ST IMU FIFO settings
|
||||
* @read_fifo: Read FIFO callback.
|
||||
@ -84,6 +124,70 @@ struct st_lsm6dsx_hw_ts_settings {
|
||||
struct st_lsm6dsx_reg decimator;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct st_lsm6dsx_shub_settings - ST IMU hw i2c controller settings
|
||||
* @page_mux: register page mux info (addr + mask).
|
||||
* @master_en: master config register info (addr + mask).
|
||||
* @pullup_en: i2c controller pull-up register info (addr + mask).
|
||||
* @aux_sens: aux sensor register info (addr + mask).
|
||||
* @wr_once: write_once register info (addr + mask).
|
||||
* @shub_out: sensor hub first output register info.
|
||||
* @slv0_addr: slave0 address in secondary page.
|
||||
* @dw_slv0_addr: slave0 write register address in secondary page.
|
||||
* @batch_en: Enable/disable FIFO batching.
|
||||
*/
|
||||
struct st_lsm6dsx_shub_settings {
|
||||
struct st_lsm6dsx_reg page_mux;
|
||||
struct st_lsm6dsx_reg master_en;
|
||||
struct st_lsm6dsx_reg pullup_en;
|
||||
struct st_lsm6dsx_reg aux_sens;
|
||||
struct st_lsm6dsx_reg wr_once;
|
||||
u8 shub_out;
|
||||
u8 slv0_addr;
|
||||
u8 dw_slv0_addr;
|
||||
u8 batch_en;
|
||||
};
|
||||
|
||||
enum st_lsm6dsx_ext_sensor_id {
|
||||
ST_LSM6DSX_ID_MAGN,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct st_lsm6dsx_ext_dev_settings - i2c controller slave settings
|
||||
* @i2c_addr: I2c slave address list.
|
||||
* @wai: Wai address info.
|
||||
* @id: external sensor id.
|
||||
* @odr: Output data rate of the sensor [Hz].
|
||||
* @gain: Configured sensor sensitivity.
|
||||
* @temp_comp: Temperature compensation register info (addr + mask).
|
||||
* @pwr_table: Power on register info (addr + mask).
|
||||
* @off_canc: Offset cancellation register info (addr + mask).
|
||||
* @bdu: Block data update register info (addr + mask).
|
||||
* @out: Output register info.
|
||||
*/
|
||||
struct st_lsm6dsx_ext_dev_settings {
|
||||
u8 i2c_addr[2];
|
||||
struct {
|
||||
u8 addr;
|
||||
u8 val;
|
||||
} wai;
|
||||
enum st_lsm6dsx_ext_sensor_id id;
|
||||
struct st_lsm6dsx_odr_table_entry odr_table;
|
||||
struct st_lsm6dsx_fs_table_entry fs_table;
|
||||
struct st_lsm6dsx_reg temp_comp;
|
||||
struct {
|
||||
struct st_lsm6dsx_reg reg;
|
||||
u8 off_val;
|
||||
u8 on_val;
|
||||
} pwr_table;
|
||||
struct st_lsm6dsx_reg off_canc;
|
||||
struct st_lsm6dsx_reg bdu;
|
||||
struct {
|
||||
u8 addr;
|
||||
u8 len;
|
||||
} out;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct st_lsm6dsx_settings - ST IMU sensor settings
|
||||
* @wai: Sensor WhoAmI default value.
|
||||
@ -93,6 +197,7 @@ struct st_lsm6dsx_hw_ts_settings {
|
||||
* @batch: List of FIFO batching register info (addr + mask).
|
||||
* @fifo_ops: Sensor hw FIFO parameters.
|
||||
* @ts_settings: Hw timer related settings.
|
||||
* @shub_settings: i2c controller related settings.
|
||||
*/
|
||||
struct st_lsm6dsx_settings {
|
||||
u8 wai;
|
||||
@ -102,11 +207,15 @@ struct st_lsm6dsx_settings {
|
||||
struct st_lsm6dsx_reg batch[ST_LSM6DSX_MAX_ID];
|
||||
struct st_lsm6dsx_fifo_ops fifo_ops;
|
||||
struct st_lsm6dsx_hw_ts_settings ts_settings;
|
||||
struct st_lsm6dsx_shub_settings shub_settings;
|
||||
};
|
||||
|
||||
enum st_lsm6dsx_sensor_id {
|
||||
ST_LSM6DSX_ID_ACC,
|
||||
ST_LSM6DSX_ID_GYRO,
|
||||
ST_LSM6DSX_ID_ACC,
|
||||
ST_LSM6DSX_ID_EXT0,
|
||||
ST_LSM6DSX_ID_EXT1,
|
||||
ST_LSM6DSX_ID_EXT2,
|
||||
ST_LSM6DSX_ID_MAX,
|
||||
};
|
||||
|
||||
@ -126,6 +235,7 @@ enum st_lsm6dsx_fifo_mode {
|
||||
* @sip: Number of samples in a given pattern.
|
||||
* @decimator: FIFO decimation factor.
|
||||
* @ts_ref: Sensor timestamp reference for hw one.
|
||||
* @ext_info: Sensor settings if it is connected to i2c controller
|
||||
*/
|
||||
struct st_lsm6dsx_sensor {
|
||||
char name[32];
|
||||
@ -139,6 +249,11 @@ struct st_lsm6dsx_sensor {
|
||||
u8 sip;
|
||||
u8 decimator;
|
||||
s64 ts_ref;
|
||||
|
||||
struct {
|
||||
const struct st_lsm6dsx_ext_dev_settings *settings;
|
||||
u8 addr;
|
||||
} ext_info;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -148,6 +263,7 @@ struct st_lsm6dsx_sensor {
|
||||
* @irq: Device interrupt line (I2C or SPI).
|
||||
* @fifo_lock: Mutex to prevent concurrent access to the hw FIFO.
|
||||
* @conf_lock: Mutex to prevent concurrent FIFO configuration update.
|
||||
* @page_lock: Mutex to prevent concurrent memory page configuration.
|
||||
* @fifo_mode: FIFO operating mode supported by the device.
|
||||
* @enable_mask: Enabled sensor bitmask.
|
||||
* @ts_sip: Total number of timestamp samples in a given pattern.
|
||||
@ -163,6 +279,7 @@ struct st_lsm6dsx_hw {
|
||||
|
||||
struct mutex fifo_lock;
|
||||
struct mutex conf_lock;
|
||||
struct mutex page_lock;
|
||||
|
||||
enum st_lsm6dsx_fifo_mode fifo_mode;
|
||||
u8 enable_mask;
|
||||
@ -176,13 +293,15 @@ struct st_lsm6dsx_hw {
|
||||
const struct st_lsm6dsx_settings *settings;
|
||||
};
|
||||
|
||||
static const unsigned long st_lsm6dsx_available_scan_masks[] = {0x7, 0x0};
|
||||
extern const struct dev_pm_ops st_lsm6dsx_pm_ops;
|
||||
|
||||
int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, const char *name,
|
||||
struct regmap *regmap);
|
||||
int st_lsm6dsx_sensor_enable(struct st_lsm6dsx_sensor *sensor);
|
||||
int st_lsm6dsx_sensor_disable(struct st_lsm6dsx_sensor *sensor);
|
||||
int st_lsm6dsx_sensor_set_enable(struct st_lsm6dsx_sensor *sensor,
|
||||
bool enable);
|
||||
int st_lsm6dsx_fifo_setup(struct st_lsm6dsx_hw *hw);
|
||||
int st_lsm6dsx_set_watermark(struct iio_dev *iio_dev, unsigned int val);
|
||||
int st_lsm6dsx_update_watermark(struct st_lsm6dsx_sensor *sensor,
|
||||
u16 watermark);
|
||||
int st_lsm6dsx_flush_fifo(struct st_lsm6dsx_hw *hw);
|
||||
@ -191,5 +310,47 @@ int st_lsm6dsx_set_fifo_mode(struct st_lsm6dsx_hw *hw,
|
||||
int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw);
|
||||
int st_lsm6dsx_read_tagged_fifo(struct st_lsm6dsx_hw *hw);
|
||||
int st_lsm6dsx_check_odr(struct st_lsm6dsx_sensor *sensor, u16 odr, u8 *val);
|
||||
int st_lsm6dsx_shub_probe(struct st_lsm6dsx_hw *hw, const char *name);
|
||||
int st_lsm6dsx_shub_set_enable(struct st_lsm6dsx_sensor *sensor, bool enable);
|
||||
int st_lsm6dsx_set_page(struct st_lsm6dsx_hw *hw, bool enable);
|
||||
|
||||
static inline int
|
||||
st_lsm6dsx_update_bits_locked(struct st_lsm6dsx_hw *hw, unsigned int addr,
|
||||
unsigned int mask, unsigned int val)
|
||||
{
|
||||
int err;
|
||||
|
||||
mutex_lock(&hw->page_lock);
|
||||
err = regmap_update_bits(hw->regmap, addr, mask, val);
|
||||
mutex_unlock(&hw->page_lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static inline int
|
||||
st_lsm6dsx_read_locked(struct st_lsm6dsx_hw *hw, unsigned int addr,
|
||||
void *val, unsigned int len)
|
||||
{
|
||||
int err;
|
||||
|
||||
mutex_lock(&hw->page_lock);
|
||||
err = regmap_bulk_read(hw->regmap, addr, val, len);
|
||||
mutex_unlock(&hw->page_lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static inline int
|
||||
st_lsm6dsx_write_locked(struct st_lsm6dsx_hw *hw, unsigned int addr,
|
||||
unsigned int val)
|
||||
{
|
||||
int err;
|
||||
|
||||
mutex_lock(&hw->page_lock);
|
||||
err = regmap_write(hw->regmap, addr, val);
|
||||
mutex_unlock(&hw->page_lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
#endif /* ST_LSM6DSX_H */
|
||||
|
@ -68,6 +68,9 @@ enum st_lsm6dsx_fifo_tag {
|
||||
ST_LSM6DSX_GYRO_TAG = 0x01,
|
||||
ST_LSM6DSX_ACC_TAG = 0x02,
|
||||
ST_LSM6DSX_TS_TAG = 0x04,
|
||||
ST_LSM6DSX_EXT0_TAG = 0x0f,
|
||||
ST_LSM6DSX_EXT1_TAG = 0x10,
|
||||
ST_LSM6DSX_EXT2_TAG = 0x11,
|
||||
};
|
||||
|
||||
static const
|
||||
@ -102,6 +105,9 @@ static void st_lsm6dsx_get_max_min_odr(struct st_lsm6dsx_hw *hw,
|
||||
|
||||
*max_odr = 0, *min_odr = ~0;
|
||||
for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
|
||||
if (!hw->iio_devs[i])
|
||||
continue;
|
||||
|
||||
sensor = iio_priv(hw->iio_devs[i]);
|
||||
|
||||
if (!(hw->enable_mask & BIT(sensor->id)))
|
||||
@ -125,6 +131,9 @@ static int st_lsm6dsx_update_decimators(struct st_lsm6dsx_hw *hw)
|
||||
for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
|
||||
const struct st_lsm6dsx_reg *dec_reg;
|
||||
|
||||
if (!hw->iio_devs[i])
|
||||
continue;
|
||||
|
||||
sensor = iio_priv(hw->iio_devs[i]);
|
||||
/* update fifo decimators and sample in pattern */
|
||||
if (hw->enable_mask & BIT(sensor->id)) {
|
||||
@ -142,8 +151,9 @@ static int st_lsm6dsx_update_decimators(struct st_lsm6dsx_hw *hw)
|
||||
if (dec_reg->addr) {
|
||||
int val = ST_LSM6DSX_SHIFT_VAL(data, dec_reg->mask);
|
||||
|
||||
err = regmap_update_bits(hw->regmap, dec_reg->addr,
|
||||
dec_reg->mask, val);
|
||||
err = st_lsm6dsx_update_bits_locked(hw, dec_reg->addr,
|
||||
dec_reg->mask,
|
||||
val);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
@ -162,8 +172,8 @@ static int st_lsm6dsx_update_decimators(struct st_lsm6dsx_hw *hw)
|
||||
int val, ts_dec = !!hw->ts_sip;
|
||||
|
||||
val = ST_LSM6DSX_SHIFT_VAL(ts_dec, ts_dec_reg->mask);
|
||||
err = regmap_update_bits(hw->regmap, ts_dec_reg->addr,
|
||||
ts_dec_reg->mask, val);
|
||||
err = st_lsm6dsx_update_bits_locked(hw, ts_dec_reg->addr,
|
||||
ts_dec_reg->mask, val);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
@ -171,12 +181,12 @@ static int st_lsm6dsx_update_decimators(struct st_lsm6dsx_hw *hw)
|
||||
int st_lsm6dsx_set_fifo_mode(struct st_lsm6dsx_hw *hw,
|
||||
enum st_lsm6dsx_fifo_mode fifo_mode)
|
||||
{
|
||||
unsigned int data;
|
||||
int err;
|
||||
|
||||
err = regmap_update_bits(hw->regmap, ST_LSM6DSX_REG_FIFO_MODE_ADDR,
|
||||
ST_LSM6DSX_FIFO_MODE_MASK,
|
||||
FIELD_PREP(ST_LSM6DSX_FIFO_MODE_MASK,
|
||||
fifo_mode));
|
||||
data = FIELD_PREP(ST_LSM6DSX_FIFO_MODE_MASK, fifo_mode);
|
||||
err = st_lsm6dsx_update_bits_locked(hw, ST_LSM6DSX_REG_FIFO_MODE_ADDR,
|
||||
ST_LSM6DSX_FIFO_MODE_MASK, data);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
@ -207,15 +217,15 @@ static int st_lsm6dsx_set_fifo_odr(struct st_lsm6dsx_sensor *sensor,
|
||||
data = 0;
|
||||
}
|
||||
val = ST_LSM6DSX_SHIFT_VAL(data, batch_reg->mask);
|
||||
return regmap_update_bits(hw->regmap, batch_reg->addr,
|
||||
batch_reg->mask, val);
|
||||
return st_lsm6dsx_update_bits_locked(hw, batch_reg->addr,
|
||||
batch_reg->mask, val);
|
||||
} else {
|
||||
data = hw->enable_mask ? ST_LSM6DSX_MAX_FIFO_ODR_VAL : 0;
|
||||
return regmap_update_bits(hw->regmap,
|
||||
ST_LSM6DSX_REG_FIFO_MODE_ADDR,
|
||||
ST_LSM6DSX_FIFO_ODR_MASK,
|
||||
FIELD_PREP(ST_LSM6DSX_FIFO_ODR_MASK,
|
||||
data));
|
||||
return st_lsm6dsx_update_bits_locked(hw,
|
||||
ST_LSM6DSX_REG_FIFO_MODE_ADDR,
|
||||
ST_LSM6DSX_FIFO_ODR_MASK,
|
||||
FIELD_PREP(ST_LSM6DSX_FIFO_ODR_MASK,
|
||||
data));
|
||||
}
|
||||
}
|
||||
|
||||
@ -231,6 +241,9 @@ int st_lsm6dsx_update_watermark(struct st_lsm6dsx_sensor *sensor, u16 watermark)
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
|
||||
if (!hw->iio_devs[i])
|
||||
continue;
|
||||
|
||||
cur_sensor = iio_priv(hw->iio_devs[i]);
|
||||
|
||||
if (!(hw->enable_mask & BIT(cur_sensor->id)))
|
||||
@ -246,19 +259,23 @@ int st_lsm6dsx_update_watermark(struct st_lsm6dsx_sensor *sensor, u16 watermark)
|
||||
fifo_watermark = (fifo_watermark / hw->sip) * hw->sip;
|
||||
fifo_watermark = fifo_watermark * hw->settings->fifo_ops.th_wl;
|
||||
|
||||
mutex_lock(&hw->page_lock);
|
||||
err = regmap_read(hw->regmap, hw->settings->fifo_ops.fifo_th.addr + 1,
|
||||
&data);
|
||||
if (err < 0)
|
||||
return err;
|
||||
goto out;
|
||||
|
||||
fifo_th_mask = hw->settings->fifo_ops.fifo_th.mask;
|
||||
fifo_watermark = ((data << 8) & ~fifo_th_mask) |
|
||||
(fifo_watermark & fifo_th_mask);
|
||||
|
||||
wdata = cpu_to_le16(fifo_watermark);
|
||||
return regmap_bulk_write(hw->regmap,
|
||||
hw->settings->fifo_ops.fifo_th.addr,
|
||||
&wdata, sizeof(wdata));
|
||||
err = regmap_bulk_write(hw->regmap,
|
||||
hw->settings->fifo_ops.fifo_th.addr,
|
||||
&wdata, sizeof(wdata));
|
||||
out:
|
||||
mutex_unlock(&hw->page_lock);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int st_lsm6dsx_reset_hw_ts(struct st_lsm6dsx_hw *hw)
|
||||
@ -267,12 +284,15 @@ static int st_lsm6dsx_reset_hw_ts(struct st_lsm6dsx_hw *hw)
|
||||
int i, err;
|
||||
|
||||
/* reset hw ts counter */
|
||||
err = regmap_write(hw->regmap, ST_LSM6DSX_REG_TS_RESET_ADDR,
|
||||
ST_LSM6DSX_TS_RESET_VAL);
|
||||
err = st_lsm6dsx_write_locked(hw, ST_LSM6DSX_REG_TS_RESET_ADDR,
|
||||
ST_LSM6DSX_TS_RESET_VAL);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
|
||||
if (!hw->iio_devs[i])
|
||||
continue;
|
||||
|
||||
sensor = iio_priv(hw->iio_devs[i]);
|
||||
/*
|
||||
* store enable buffer timestamp as reference for
|
||||
@ -297,8 +317,8 @@ static inline int st_lsm6dsx_read_block(struct st_lsm6dsx_hw *hw, u8 addr,
|
||||
while (read_len < data_len) {
|
||||
word_len = min_t(unsigned int, data_len - read_len,
|
||||
max_word_len);
|
||||
err = regmap_bulk_read(hw->regmap, addr, data + read_len,
|
||||
word_len);
|
||||
err = st_lsm6dsx_read_locked(hw, addr, data + read_len,
|
||||
word_len);
|
||||
if (err < 0)
|
||||
return err;
|
||||
read_len += word_len;
|
||||
@ -328,9 +348,9 @@ int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw)
|
||||
__le16 fifo_status;
|
||||
s64 ts = 0;
|
||||
|
||||
err = regmap_bulk_read(hw->regmap,
|
||||
hw->settings->fifo_ops.fifo_diff.addr,
|
||||
&fifo_status, sizeof(fifo_status));
|
||||
err = st_lsm6dsx_read_locked(hw,
|
||||
hw->settings->fifo_ops.fifo_diff.addr,
|
||||
&fifo_status, sizeof(fifo_status));
|
||||
if (err < 0) {
|
||||
dev_err(hw->dev, "failed to read fifo status (err=%d)\n",
|
||||
err);
|
||||
@ -436,6 +456,55 @@ int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw)
|
||||
return read_len;
|
||||
}
|
||||
|
||||
static int
|
||||
st_lsm6dsx_push_tagged_data(struct st_lsm6dsx_hw *hw, u8 tag,
|
||||
u8 *data, s64 ts)
|
||||
{
|
||||
struct st_lsm6dsx_sensor *sensor;
|
||||
struct iio_dev *iio_dev;
|
||||
|
||||
/*
|
||||
* EXT_TAG are managed in FIFO fashion so ST_LSM6DSX_EXT0_TAG
|
||||
* corresponds to the first enabled channel, ST_LSM6DSX_EXT1_TAG
|
||||
* to the second one and ST_LSM6DSX_EXT2_TAG to the last enabled
|
||||
* channel
|
||||
*/
|
||||
switch (tag) {
|
||||
case ST_LSM6DSX_GYRO_TAG:
|
||||
iio_dev = hw->iio_devs[ST_LSM6DSX_ID_GYRO];
|
||||
break;
|
||||
case ST_LSM6DSX_ACC_TAG:
|
||||
iio_dev = hw->iio_devs[ST_LSM6DSX_ID_ACC];
|
||||
break;
|
||||
case ST_LSM6DSX_EXT0_TAG:
|
||||
if (hw->enable_mask & BIT(ST_LSM6DSX_ID_EXT0))
|
||||
iio_dev = hw->iio_devs[ST_LSM6DSX_ID_EXT0];
|
||||
else if (hw->enable_mask & BIT(ST_LSM6DSX_ID_EXT1))
|
||||
iio_dev = hw->iio_devs[ST_LSM6DSX_ID_EXT1];
|
||||
else
|
||||
iio_dev = hw->iio_devs[ST_LSM6DSX_ID_EXT2];
|
||||
break;
|
||||
case ST_LSM6DSX_EXT1_TAG:
|
||||
if ((hw->enable_mask & BIT(ST_LSM6DSX_ID_EXT0)) &&
|
||||
(hw->enable_mask & BIT(ST_LSM6DSX_ID_EXT1)))
|
||||
iio_dev = hw->iio_devs[ST_LSM6DSX_ID_EXT1];
|
||||
else
|
||||
iio_dev = hw->iio_devs[ST_LSM6DSX_ID_EXT2];
|
||||
break;
|
||||
case ST_LSM6DSX_EXT2_TAG:
|
||||
iio_dev = hw->iio_devs[ST_LSM6DSX_ID_EXT2];
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
sensor = iio_priv(iio_dev);
|
||||
iio_push_to_buffers_with_timestamp(iio_dev, data,
|
||||
ts + sensor->ts_ref);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* st_lsm6dsx_read_tagged_fifo() - LSM6DSO read FIFO routine
|
||||
* @hw: Pointer to instance of struct st_lsm6dsx_hw.
|
||||
@ -455,9 +524,9 @@ int st_lsm6dsx_read_tagged_fifo(struct st_lsm6dsx_hw *hw)
|
||||
__le16 fifo_status;
|
||||
s64 ts = 0;
|
||||
|
||||
err = regmap_bulk_read(hw->regmap,
|
||||
hw->settings->fifo_ops.fifo_diff.addr,
|
||||
&fifo_status, sizeof(fifo_status));
|
||||
err = st_lsm6dsx_read_locked(hw,
|
||||
hw->settings->fifo_ops.fifo_diff.addr,
|
||||
&fifo_status, sizeof(fifo_status));
|
||||
if (err < 0) {
|
||||
dev_err(hw->dev, "failed to read fifo status (err=%d)\n",
|
||||
err);
|
||||
@ -491,8 +560,7 @@ int st_lsm6dsx_read_tagged_fifo(struct st_lsm6dsx_hw *hw)
|
||||
ST_LSM6DSX_SAMPLE_SIZE);
|
||||
|
||||
tag = hw->buff[i] >> 3;
|
||||
switch (tag) {
|
||||
case ST_LSM6DSX_TS_TAG:
|
||||
if (tag == ST_LSM6DSX_TS_TAG) {
|
||||
/*
|
||||
* hw timestamp is 4B long and it is stored
|
||||
* in FIFO according to this schema:
|
||||
@ -509,19 +577,9 @@ int st_lsm6dsx_read_tagged_fifo(struct st_lsm6dsx_hw *hw)
|
||||
if (!reset_ts && ts >= 0xffff0000)
|
||||
reset_ts = true;
|
||||
ts *= ST_LSM6DSX_TS_SENSITIVITY;
|
||||
break;
|
||||
case ST_LSM6DSX_GYRO_TAG:
|
||||
iio_push_to_buffers_with_timestamp(
|
||||
hw->iio_devs[ST_LSM6DSX_ID_GYRO],
|
||||
iio_buff, gyro_sensor->ts_ref + ts);
|
||||
break;
|
||||
case ST_LSM6DSX_ACC_TAG:
|
||||
iio_push_to_buffers_with_timestamp(
|
||||
hw->iio_devs[ST_LSM6DSX_ID_ACC],
|
||||
iio_buff, acc_sensor->ts_ref + ts);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
} else {
|
||||
st_lsm6dsx_push_tagged_data(hw, tag, iio_buff,
|
||||
ts);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -562,20 +620,22 @@ static int st_lsm6dsx_update_fifo(struct iio_dev *iio_dev, bool enable)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (enable) {
|
||||
err = st_lsm6dsx_sensor_enable(sensor);
|
||||
if (sensor->id == ST_LSM6DSX_ID_EXT0 ||
|
||||
sensor->id == ST_LSM6DSX_ID_EXT1 ||
|
||||
sensor->id == ST_LSM6DSX_ID_EXT2) {
|
||||
err = st_lsm6dsx_shub_set_enable(sensor, enable);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
} else {
|
||||
err = st_lsm6dsx_sensor_disable(sensor);
|
||||
err = st_lsm6dsx_sensor_set_enable(sensor, enable);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
err = st_lsm6dsx_set_fifo_odr(sensor, enable);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = st_lsm6dsx_set_fifo_odr(sensor, enable);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
err = st_lsm6dsx_update_decimators(hw);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
@ -690,6 +750,9 @@ int st_lsm6dsx_fifo_setup(struct st_lsm6dsx_hw *hw)
|
||||
}
|
||||
|
||||
for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
|
||||
if (!hw->iio_devs[i])
|
||||
continue;
|
||||
|
||||
buffer = devm_iio_kfifo_allocate(hw->dev);
|
||||
if (!buffer)
|
||||
return -ENOMEM;
|
||||
|
@ -56,6 +56,7 @@
|
||||
#define ST_LSM6DSX_REG_WHOAMI_ADDR 0x0f
|
||||
#define ST_LSM6DSX_REG_RESET_ADDR 0x12
|
||||
#define ST_LSM6DSX_REG_RESET_MASK BIT(0)
|
||||
#define ST_LSM6DSX_REG_BOOT_MASK BIT(7)
|
||||
#define ST_LSM6DSX_REG_BDU_ADDR 0x12
|
||||
#define ST_LSM6DSX_REG_BDU_MASK BIT(6)
|
||||
#define ST_LSM6DSX_REG_INT2_ON_INT1_ADDR 0x13
|
||||
@ -87,17 +88,6 @@
|
||||
#define ST_LSM6DSX_GYRO_FS_1000_GAIN IIO_DEGREE_TO_RAD(35000)
|
||||
#define ST_LSM6DSX_GYRO_FS_2000_GAIN IIO_DEGREE_TO_RAD(70000)
|
||||
|
||||
struct st_lsm6dsx_odr {
|
||||
u16 hz;
|
||||
u8 val;
|
||||
};
|
||||
|
||||
#define ST_LSM6DSX_ODR_LIST_SIZE 6
|
||||
struct st_lsm6dsx_odr_table_entry {
|
||||
struct st_lsm6dsx_reg reg;
|
||||
struct st_lsm6dsx_odr odr_avl[ST_LSM6DSX_ODR_LIST_SIZE];
|
||||
};
|
||||
|
||||
static const struct st_lsm6dsx_odr_table_entry st_lsm6dsx_odr_table[] = {
|
||||
[ST_LSM6DSX_ID_ACC] = {
|
||||
.reg = {
|
||||
@ -125,17 +115,6 @@ static const struct st_lsm6dsx_odr_table_entry st_lsm6dsx_odr_table[] = {
|
||||
}
|
||||
};
|
||||
|
||||
struct st_lsm6dsx_fs {
|
||||
u32 gain;
|
||||
u8 val;
|
||||
};
|
||||
|
||||
#define ST_LSM6DSX_FS_LIST_SIZE 4
|
||||
struct st_lsm6dsx_fs_table_entry {
|
||||
struct st_lsm6dsx_reg reg;
|
||||
struct st_lsm6dsx_fs fs_avl[ST_LSM6DSX_FS_LIST_SIZE];
|
||||
};
|
||||
|
||||
static const struct st_lsm6dsx_fs_table_entry st_lsm6dsx_fs_table[] = {
|
||||
[ST_LSM6DSX_ID_ACC] = {
|
||||
.reg = {
|
||||
@ -341,27 +320,35 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
|
||||
.mask = GENMASK(7, 6),
|
||||
},
|
||||
},
|
||||
.shub_settings = {
|
||||
.page_mux = {
|
||||
.addr = 0x01,
|
||||
.mask = BIT(6),
|
||||
},
|
||||
.master_en = {
|
||||
.addr = 0x14,
|
||||
.mask = BIT(2),
|
||||
},
|
||||
.pullup_en = {
|
||||
.addr = 0x14,
|
||||
.mask = BIT(3),
|
||||
},
|
||||
.aux_sens = {
|
||||
.addr = 0x14,
|
||||
.mask = GENMASK(1, 0),
|
||||
},
|
||||
.wr_once = {
|
||||
.addr = 0x14,
|
||||
.mask = BIT(6),
|
||||
},
|
||||
.shub_out = 0x02,
|
||||
.slv0_addr = 0x15,
|
||||
.dw_slv0_addr = 0x21,
|
||||
.batch_en = BIT(3),
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
#define ST_LSM6DSX_CHANNEL(chan_type, addr, mod, scan_idx) \
|
||||
{ \
|
||||
.type = chan_type, \
|
||||
.address = addr, \
|
||||
.modified = 1, \
|
||||
.channel2 = mod, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
|
||||
.scan_index = scan_idx, \
|
||||
.scan_type = { \
|
||||
.sign = 's', \
|
||||
.realbits = 16, \
|
||||
.storagebits = 16, \
|
||||
.endianness = IIO_LE, \
|
||||
}, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec st_lsm6dsx_acc_channels[] = {
|
||||
ST_LSM6DSX_CHANNEL(IIO_ACCEL, ST_LSM6DSX_REG_ACC_OUT_X_L_ADDR,
|
||||
IIO_MOD_X, 0),
|
||||
@ -382,6 +369,21 @@ static const struct iio_chan_spec st_lsm6dsx_gyro_channels[] = {
|
||||
IIO_CHAN_SOFT_TIMESTAMP(3),
|
||||
};
|
||||
|
||||
int st_lsm6dsx_set_page(struct st_lsm6dsx_hw *hw, bool enable)
|
||||
{
|
||||
const struct st_lsm6dsx_shub_settings *hub_settings;
|
||||
unsigned int data;
|
||||
int err;
|
||||
|
||||
hub_settings = &hw->settings->shub_settings;
|
||||
data = ST_LSM6DSX_SHIFT_VAL(enable, hub_settings->page_mux.mask);
|
||||
err = regmap_update_bits(hw->regmap, hub_settings->page_mux.addr,
|
||||
hub_settings->page_mux.mask, data);
|
||||
usleep_range(100, 150);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int st_lsm6dsx_check_whoami(struct st_lsm6dsx_hw *hw, int id)
|
||||
{
|
||||
int err, i, j, data;
|
||||
@ -421,6 +423,7 @@ static int st_lsm6dsx_set_full_scale(struct st_lsm6dsx_sensor *sensor,
|
||||
{
|
||||
struct st_lsm6dsx_hw *hw = sensor->hw;
|
||||
const struct st_lsm6dsx_reg *reg;
|
||||
unsigned int data;
|
||||
int i, err;
|
||||
u8 val;
|
||||
|
||||
@ -433,8 +436,8 @@ static int st_lsm6dsx_set_full_scale(struct st_lsm6dsx_sensor *sensor,
|
||||
|
||||
val = st_lsm6dsx_fs_table[sensor->id].fs_avl[i].val;
|
||||
reg = &st_lsm6dsx_fs_table[sensor->id].reg;
|
||||
err = regmap_update_bits(hw->regmap, reg->addr, reg->mask,
|
||||
ST_LSM6DSX_SHIFT_VAL(val, reg->mask));
|
||||
data = ST_LSM6DSX_SHIFT_VAL(val, reg->mask);
|
||||
err = st_lsm6dsx_update_bits_locked(hw, reg->addr, reg->mask, data);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
@ -448,7 +451,11 @@ int st_lsm6dsx_check_odr(struct st_lsm6dsx_sensor *sensor, u16 odr, u8 *val)
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ST_LSM6DSX_ODR_LIST_SIZE; i++)
|
||||
if (st_lsm6dsx_odr_table[sensor->id].odr_avl[i].hz == odr)
|
||||
/*
|
||||
* ext devices can run at different odr respect to
|
||||
* accel sensor
|
||||
*/
|
||||
if (st_lsm6dsx_odr_table[sensor->id].odr_avl[i].hz >= odr)
|
||||
break;
|
||||
|
||||
if (i == ST_LSM6DSX_ODR_LIST_SIZE)
|
||||
@ -459,48 +466,86 @@ int st_lsm6dsx_check_odr(struct st_lsm6dsx_sensor *sensor, u16 odr, u8 *val)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int st_lsm6dsx_set_odr(struct st_lsm6dsx_sensor *sensor, u16 odr)
|
||||
static u16 st_lsm6dsx_check_odr_dependency(struct st_lsm6dsx_hw *hw, u16 odr,
|
||||
enum st_lsm6dsx_sensor_id id)
|
||||
{
|
||||
struct st_lsm6dsx_hw *hw = sensor->hw;
|
||||
const struct st_lsm6dsx_reg *reg;
|
||||
int err;
|
||||
u8 val;
|
||||
struct st_lsm6dsx_sensor *ref = iio_priv(hw->iio_devs[id]);
|
||||
|
||||
err = st_lsm6dsx_check_odr(sensor, odr, &val);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
reg = &st_lsm6dsx_odr_table[sensor->id].reg;
|
||||
return regmap_update_bits(hw->regmap, reg->addr, reg->mask,
|
||||
ST_LSM6DSX_SHIFT_VAL(val, reg->mask));
|
||||
if (odr > 0) {
|
||||
if (hw->enable_mask & BIT(id))
|
||||
return max_t(u16, ref->odr, odr);
|
||||
else
|
||||
return odr;
|
||||
} else {
|
||||
return (hw->enable_mask & BIT(id)) ? ref->odr : 0;
|
||||
}
|
||||
}
|
||||
|
||||
int st_lsm6dsx_sensor_enable(struct st_lsm6dsx_sensor *sensor)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = st_lsm6dsx_set_odr(sensor, sensor->odr);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
sensor->hw->enable_mask |= BIT(sensor->id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int st_lsm6dsx_sensor_disable(struct st_lsm6dsx_sensor *sensor)
|
||||
static int st_lsm6dsx_set_odr(struct st_lsm6dsx_sensor *sensor, u16 req_odr)
|
||||
{
|
||||
struct st_lsm6dsx_sensor *ref_sensor = sensor;
|
||||
struct st_lsm6dsx_hw *hw = sensor->hw;
|
||||
const struct st_lsm6dsx_reg *reg;
|
||||
unsigned int data;
|
||||
u8 val = 0;
|
||||
int err;
|
||||
|
||||
reg = &st_lsm6dsx_odr_table[sensor->id].reg;
|
||||
err = regmap_update_bits(hw->regmap, reg->addr, reg->mask,
|
||||
ST_LSM6DSX_SHIFT_VAL(0, reg->mask));
|
||||
switch (sensor->id) {
|
||||
case ST_LSM6DSX_ID_EXT0:
|
||||
case ST_LSM6DSX_ID_EXT1:
|
||||
case ST_LSM6DSX_ID_EXT2:
|
||||
case ST_LSM6DSX_ID_ACC: {
|
||||
u16 odr;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* i2c embedded controller relies on the accelerometer sensor as
|
||||
* bus read/write trigger so we need to enable accel device
|
||||
* at odr = max(accel_odr, ext_odr) in order to properly
|
||||
* communicate with i2c slave devices
|
||||
*/
|
||||
ref_sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_ACC]);
|
||||
for (i = ST_LSM6DSX_ID_ACC; i < ST_LSM6DSX_ID_MAX; i++) {
|
||||
if (!hw->iio_devs[i] || i == sensor->id)
|
||||
continue;
|
||||
|
||||
odr = st_lsm6dsx_check_odr_dependency(hw, req_odr, i);
|
||||
if (odr != req_odr)
|
||||
/* device already configured */
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (req_odr > 0) {
|
||||
err = st_lsm6dsx_check_odr(ref_sensor, req_odr, &val);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
reg = &st_lsm6dsx_odr_table[ref_sensor->id].reg;
|
||||
data = ST_LSM6DSX_SHIFT_VAL(val, reg->mask);
|
||||
return st_lsm6dsx_update_bits_locked(hw, reg->addr, reg->mask, data);
|
||||
}
|
||||
|
||||
int st_lsm6dsx_sensor_set_enable(struct st_lsm6dsx_sensor *sensor,
|
||||
bool enable)
|
||||
{
|
||||
struct st_lsm6dsx_hw *hw = sensor->hw;
|
||||
u16 odr = enable ? sensor->odr : 0;
|
||||
int err;
|
||||
|
||||
err = st_lsm6dsx_set_odr(sensor, odr);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
sensor->hw->enable_mask &= ~BIT(sensor->id);
|
||||
if (enable)
|
||||
hw->enable_mask |= BIT(sensor->id);
|
||||
else
|
||||
hw->enable_mask &= ~BIT(sensor->id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -512,18 +557,18 @@ static int st_lsm6dsx_read_oneshot(struct st_lsm6dsx_sensor *sensor,
|
||||
int err, delay;
|
||||
__le16 data;
|
||||
|
||||
err = st_lsm6dsx_sensor_enable(sensor);
|
||||
err = st_lsm6dsx_sensor_set_enable(sensor, true);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
delay = 1000000 / sensor->odr;
|
||||
usleep_range(delay, 2 * delay);
|
||||
|
||||
err = regmap_bulk_read(hw->regmap, addr, &data, sizeof(data));
|
||||
err = st_lsm6dsx_read_locked(hw, addr, &data, sizeof(data));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
st_lsm6dsx_sensor_disable(sensor);
|
||||
st_lsm6dsx_sensor_set_enable(sensor, false);
|
||||
|
||||
*val = (s16)le16_to_cpu(data);
|
||||
|
||||
@ -596,7 +641,7 @@ static int st_lsm6dsx_write_raw(struct iio_dev *iio_dev,
|
||||
return err;
|
||||
}
|
||||
|
||||
static int st_lsm6dsx_set_watermark(struct iio_dev *iio_dev, unsigned int val)
|
||||
int st_lsm6dsx_set_watermark(struct iio_dev *iio_dev, unsigned int val)
|
||||
{
|
||||
struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev);
|
||||
struct st_lsm6dsx_hw *hw = sensor->hw;
|
||||
@ -692,8 +737,6 @@ static const struct iio_info st_lsm6dsx_gyro_info = {
|
||||
.hwfifo_set_watermark = st_lsm6dsx_set_watermark,
|
||||
};
|
||||
|
||||
static const unsigned long st_lsm6dsx_available_scan_masks[] = {0x7, 0x0};
|
||||
|
||||
static int st_lsm6dsx_of_get_drdy_pin(struct st_lsm6dsx_hw *hw, int *drdy_pin)
|
||||
{
|
||||
struct device_node *np = hw->dev->of_node;
|
||||
@ -732,6 +775,51 @@ static int st_lsm6dsx_get_drdy_reg(struct st_lsm6dsx_hw *hw, u8 *drdy_reg)
|
||||
return err;
|
||||
}
|
||||
|
||||
static int st_lsm6dsx_init_shub(struct st_lsm6dsx_hw *hw)
|
||||
{
|
||||
const struct st_lsm6dsx_shub_settings *hub_settings;
|
||||
struct device_node *np = hw->dev->of_node;
|
||||
struct st_sensors_platform_data *pdata;
|
||||
unsigned int data;
|
||||
int err = 0;
|
||||
|
||||
hub_settings = &hw->settings->shub_settings;
|
||||
|
||||
pdata = (struct st_sensors_platform_data *)hw->dev->platform_data;
|
||||
if ((np && of_property_read_bool(np, "st,pullups")) ||
|
||||
(pdata && pdata->pullups)) {
|
||||
err = st_lsm6dsx_set_page(hw, true);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
data = ST_LSM6DSX_SHIFT_VAL(1, hub_settings->pullup_en.mask);
|
||||
err = regmap_update_bits(hw->regmap,
|
||||
hub_settings->pullup_en.addr,
|
||||
hub_settings->pullup_en.mask, data);
|
||||
|
||||
st_lsm6dsx_set_page(hw, false);
|
||||
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (hub_settings->aux_sens.addr) {
|
||||
/* configure aux sensors */
|
||||
err = st_lsm6dsx_set_page(hw, true);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
data = ST_LSM6DSX_SHIFT_VAL(3, hub_settings->aux_sens.mask);
|
||||
err = regmap_update_bits(hw->regmap,
|
||||
hub_settings->aux_sens.addr,
|
||||
hub_settings->aux_sens.mask, data);
|
||||
|
||||
st_lsm6dsx_set_page(hw, false);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int st_lsm6dsx_init_hw_timer(struct st_lsm6dsx_hw *hw)
|
||||
{
|
||||
const struct st_lsm6dsx_hw_ts_settings *ts_settings;
|
||||
@ -775,12 +863,23 @@ static int st_lsm6dsx_init_device(struct st_lsm6dsx_hw *hw)
|
||||
u8 drdy_int_reg;
|
||||
int err;
|
||||
|
||||
err = regmap_write(hw->regmap, ST_LSM6DSX_REG_RESET_ADDR,
|
||||
ST_LSM6DSX_REG_RESET_MASK);
|
||||
/* device sw reset */
|
||||
err = regmap_update_bits(hw->regmap, ST_LSM6DSX_REG_RESET_ADDR,
|
||||
ST_LSM6DSX_REG_RESET_MASK,
|
||||
FIELD_PREP(ST_LSM6DSX_REG_RESET_MASK, 1));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
msleep(200);
|
||||
msleep(50);
|
||||
|
||||
/* reload trimming parameter */
|
||||
err = regmap_update_bits(hw->regmap, ST_LSM6DSX_REG_RESET_ADDR,
|
||||
ST_LSM6DSX_REG_BOOT_MASK,
|
||||
FIELD_PREP(ST_LSM6DSX_REG_BOOT_MASK, 1));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
msleep(50);
|
||||
|
||||
/* enable Block Data Update */
|
||||
err = regmap_update_bits(hw->regmap, ST_LSM6DSX_REG_BDU_ADDR,
|
||||
@ -801,6 +900,10 @@ static int st_lsm6dsx_init_device(struct st_lsm6dsx_hw *hw)
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = st_lsm6dsx_init_shub(hw);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return st_lsm6dsx_init_hw_timer(hw);
|
||||
}
|
||||
|
||||
@ -854,6 +957,7 @@ static struct iio_dev *st_lsm6dsx_alloc_iiodev(struct st_lsm6dsx_hw *hw,
|
||||
int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, const char *name,
|
||||
struct regmap *regmap)
|
||||
{
|
||||
const struct st_lsm6dsx_shub_settings *hub_settings;
|
||||
struct st_lsm6dsx_hw *hw;
|
||||
int i, err;
|
||||
|
||||
@ -865,6 +969,7 @@ int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, const char *name,
|
||||
|
||||
mutex_init(&hw->fifo_lock);
|
||||
mutex_init(&hw->conf_lock);
|
||||
mutex_init(&hw->page_lock);
|
||||
|
||||
hw->buff = devm_kzalloc(dev, ST_LSM6DSX_BUFF_SIZE, GFP_KERNEL);
|
||||
if (!hw->buff)
|
||||
@ -878,7 +983,7 @@ int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, const char *name,
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
|
||||
for (i = 0; i < ST_LSM6DSX_ID_EXT0; i++) {
|
||||
hw->iio_devs[i] = st_lsm6dsx_alloc_iiodev(hw, i, name);
|
||||
if (!hw->iio_devs[i])
|
||||
return -ENOMEM;
|
||||
@ -888,6 +993,13 @@ int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, const char *name,
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
hub_settings = &hw->settings->shub_settings;
|
||||
if (hub_settings->master_en.addr) {
|
||||
err = st_lsm6dsx_shub_probe(hw, name);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (hw->irq > 0) {
|
||||
err = st_lsm6dsx_fifo_setup(hw);
|
||||
if (err < 0)
|
||||
@ -895,6 +1007,9 @@ int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, const char *name,
|
||||
}
|
||||
|
||||
for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
|
||||
if (!hw->iio_devs[i])
|
||||
continue;
|
||||
|
||||
err = devm_iio_device_register(hw->dev, hw->iio_devs[i]);
|
||||
if (err)
|
||||
return err;
|
||||
@ -909,16 +1024,21 @@ static int __maybe_unused st_lsm6dsx_suspend(struct device *dev)
|
||||
struct st_lsm6dsx_hw *hw = dev_get_drvdata(dev);
|
||||
struct st_lsm6dsx_sensor *sensor;
|
||||
const struct st_lsm6dsx_reg *reg;
|
||||
unsigned int data;
|
||||
int i, err = 0;
|
||||
|
||||
for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
|
||||
if (!hw->iio_devs[i])
|
||||
continue;
|
||||
|
||||
sensor = iio_priv(hw->iio_devs[i]);
|
||||
if (!(hw->enable_mask & BIT(sensor->id)))
|
||||
continue;
|
||||
|
||||
reg = &st_lsm6dsx_odr_table[sensor->id].reg;
|
||||
err = regmap_update_bits(hw->regmap, reg->addr, reg->mask,
|
||||
ST_LSM6DSX_SHIFT_VAL(0, reg->mask));
|
||||
data = ST_LSM6DSX_SHIFT_VAL(0, reg->mask);
|
||||
err = st_lsm6dsx_update_bits_locked(hw, reg->addr, reg->mask,
|
||||
data);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
@ -936,6 +1056,9 @@ static int __maybe_unused st_lsm6dsx_resume(struct device *dev)
|
||||
int i, err = 0;
|
||||
|
||||
for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
|
||||
if (!hw->iio_devs[i])
|
||||
continue;
|
||||
|
||||
sensor = iio_priv(hw->iio_devs[i]);
|
||||
if (!(hw->enable_mask & BIT(sensor->id)))
|
||||
continue;
|
||||
|
777
drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c
Normal file
777
drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c
Normal file
@ -0,0 +1,777 @@
|
||||
/*
|
||||
* STMicroelectronics st_lsm6dsx i2c controller driver
|
||||
*
|
||||
* i2c controller embedded in lsm6dx series can connect up to four
|
||||
* slave devices using accelerometer sensor as trigger for i2c
|
||||
* read/write operations. Current implementation relies on SLV0 channel
|
||||
* for slave configuration and SLV{1,2,3} to read data and push them into
|
||||
* the hw FIFO
|
||||
*
|
||||
* Copyright (C) 2018 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/bitfield.h>
|
||||
|
||||
#include "st_lsm6dsx.h"
|
||||
|
||||
#define ST_LSM6DSX_MAX_SLV_NUM 3
|
||||
#define ST_LSM6DSX_SLV_ADDR(n, base) ((base) + (n) * 3)
|
||||
#define ST_LSM6DSX_SLV_SUB_ADDR(n, base) ((base) + 1 + (n) * 3)
|
||||
#define ST_LSM6DSX_SLV_CONFIG(n, base) ((base) + 2 + (n) * 3)
|
||||
|
||||
#define ST_LS6DSX_READ_OP_MASK GENMASK(2, 0)
|
||||
|
||||
static const struct st_lsm6dsx_ext_dev_settings st_lsm6dsx_ext_dev_table[] = {
|
||||
/* LIS2MDL */
|
||||
{
|
||||
.i2c_addr = { 0x1e },
|
||||
.wai = {
|
||||
.addr = 0x4f,
|
||||
.val = 0x40,
|
||||
},
|
||||
.id = ST_LSM6DSX_ID_MAGN,
|
||||
.odr_table = {
|
||||
.reg = {
|
||||
.addr = 0x60,
|
||||
.mask = GENMASK(3, 2),
|
||||
},
|
||||
.odr_avl[0] = { 10, 0x0 },
|
||||
.odr_avl[1] = { 20, 0x1 },
|
||||
.odr_avl[2] = { 50, 0x2 },
|
||||
.odr_avl[3] = { 100, 0x3 },
|
||||
},
|
||||
.fs_table = {
|
||||
.fs_avl[0] = {
|
||||
.gain = 1500,
|
||||
.val = 0x0,
|
||||
}, /* 1500 uG/LSB */
|
||||
},
|
||||
.temp_comp = {
|
||||
.addr = 0x60,
|
||||
.mask = BIT(7),
|
||||
},
|
||||
.pwr_table = {
|
||||
.reg = {
|
||||
.addr = 0x60,
|
||||
.mask = GENMASK(1, 0),
|
||||
},
|
||||
.off_val = 0x2,
|
||||
.on_val = 0x0,
|
||||
},
|
||||
.off_canc = {
|
||||
.addr = 0x61,
|
||||
.mask = BIT(1),
|
||||
},
|
||||
.bdu = {
|
||||
.addr = 0x62,
|
||||
.mask = BIT(4),
|
||||
},
|
||||
.out = {
|
||||
.addr = 0x68,
|
||||
.len = 6,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static void st_lsm6dsx_shub_wait_complete(struct st_lsm6dsx_hw *hw)
|
||||
{
|
||||
struct st_lsm6dsx_sensor *sensor;
|
||||
|
||||
sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_ACC]);
|
||||
msleep((2000U / sensor->odr) + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* st_lsm6dsx_shub_read_reg - read i2c controller register
|
||||
*
|
||||
* Read st_lsm6dsx i2c controller register
|
||||
*/
|
||||
static int st_lsm6dsx_shub_read_reg(struct st_lsm6dsx_hw *hw, u8 addr,
|
||||
u8 *data, int len)
|
||||
{
|
||||
const struct st_lsm6dsx_shub_settings *hub_settings;
|
||||
int err;
|
||||
|
||||
mutex_lock(&hw->page_lock);
|
||||
|
||||
hub_settings = &hw->settings->shub_settings;
|
||||
err = st_lsm6dsx_set_page(hw, true);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
err = regmap_bulk_read(hw->regmap, addr, data, len);
|
||||
|
||||
st_lsm6dsx_set_page(hw, false);
|
||||
out:
|
||||
mutex_unlock(&hw->page_lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* st_lsm6dsx_shub_write_reg - write i2c controller register
|
||||
*
|
||||
* Write st_lsm6dsx i2c controller register
|
||||
*/
|
||||
static int st_lsm6dsx_shub_write_reg(struct st_lsm6dsx_hw *hw, u8 addr,
|
||||
u8 *data, int len)
|
||||
{
|
||||
int err;
|
||||
|
||||
mutex_lock(&hw->page_lock);
|
||||
err = st_lsm6dsx_set_page(hw, true);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
err = regmap_bulk_write(hw->regmap, addr, data, len);
|
||||
|
||||
st_lsm6dsx_set_page(hw, false);
|
||||
out:
|
||||
mutex_unlock(&hw->page_lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int
|
||||
st_lsm6dsx_shub_write_reg_with_mask(struct st_lsm6dsx_hw *hw, u8 addr,
|
||||
u8 mask, u8 val)
|
||||
{
|
||||
int err;
|
||||
|
||||
mutex_lock(&hw->page_lock);
|
||||
err = st_lsm6dsx_set_page(hw, true);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
err = regmap_update_bits(hw->regmap, addr, mask, val);
|
||||
|
||||
st_lsm6dsx_set_page(hw, false);
|
||||
out:
|
||||
mutex_unlock(&hw->page_lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int st_lsm6dsx_shub_master_enable(struct st_lsm6dsx_sensor *sensor,
|
||||
bool enable)
|
||||
{
|
||||
const struct st_lsm6dsx_shub_settings *hub_settings;
|
||||
struct st_lsm6dsx_hw *hw = sensor->hw;
|
||||
unsigned int data;
|
||||
int err;
|
||||
|
||||
/* enable acc sensor as trigger */
|
||||
err = st_lsm6dsx_sensor_set_enable(sensor, enable);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
mutex_lock(&hw->page_lock);
|
||||
|
||||
hub_settings = &hw->settings->shub_settings;
|
||||
err = st_lsm6dsx_set_page(hw, true);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
data = ST_LSM6DSX_SHIFT_VAL(enable, hub_settings->master_en.mask);
|
||||
err = regmap_update_bits(hw->regmap, hub_settings->master_en.addr,
|
||||
hub_settings->master_en.mask, data);
|
||||
|
||||
st_lsm6dsx_set_page(hw, false);
|
||||
out:
|
||||
mutex_unlock(&hw->page_lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* st_lsm6dsx_shub_read - read data from slave device register
|
||||
*
|
||||
* Read data from slave device register. SLV0 is used for
|
||||
* one-shot read operation
|
||||
*/
|
||||
static int
|
||||
st_lsm6dsx_shub_read(struct st_lsm6dsx_sensor *sensor, u8 addr,
|
||||
u8 *data, int len)
|
||||
{
|
||||
const struct st_lsm6dsx_shub_settings *hub_settings;
|
||||
struct st_lsm6dsx_hw *hw = sensor->hw;
|
||||
u8 config[3], slv_addr;
|
||||
int err;
|
||||
|
||||
hub_settings = &hw->settings->shub_settings;
|
||||
slv_addr = ST_LSM6DSX_SLV_ADDR(0, hub_settings->slv0_addr);
|
||||
|
||||
config[0] = (sensor->ext_info.addr << 1) | 1;
|
||||
config[1] = addr;
|
||||
config[2] = len & ST_LS6DSX_READ_OP_MASK;
|
||||
|
||||
err = st_lsm6dsx_shub_write_reg(hw, slv_addr, config,
|
||||
sizeof(config));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = st_lsm6dsx_shub_master_enable(sensor, true);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
st_lsm6dsx_shub_wait_complete(hw);
|
||||
|
||||
err = st_lsm6dsx_shub_read_reg(hw, hub_settings->shub_out, data,
|
||||
len & ST_LS6DSX_READ_OP_MASK);
|
||||
|
||||
st_lsm6dsx_shub_master_enable(sensor, false);
|
||||
|
||||
memset(config, 0, sizeof(config));
|
||||
return st_lsm6dsx_shub_write_reg(hw, slv_addr, config,
|
||||
sizeof(config));
|
||||
}
|
||||
|
||||
/**
|
||||
* st_lsm6dsx_shub_write - write data to slave device register
|
||||
*
|
||||
* Write data from slave device register. SLV0 is used for
|
||||
* one-shot write operation
|
||||
*/
|
||||
static int
|
||||
st_lsm6dsx_shub_write(struct st_lsm6dsx_sensor *sensor, u8 addr,
|
||||
u8 *data, int len)
|
||||
{
|
||||
const struct st_lsm6dsx_shub_settings *hub_settings;
|
||||
struct st_lsm6dsx_hw *hw = sensor->hw;
|
||||
u8 config[2], slv_addr;
|
||||
int err, i;
|
||||
|
||||
hub_settings = &hw->settings->shub_settings;
|
||||
if (hub_settings->wr_once.addr) {
|
||||
unsigned int data;
|
||||
|
||||
data = ST_LSM6DSX_SHIFT_VAL(1, hub_settings->wr_once.mask);
|
||||
err = st_lsm6dsx_shub_write_reg_with_mask(hw,
|
||||
hub_settings->wr_once.addr,
|
||||
hub_settings->wr_once.mask,
|
||||
data);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
slv_addr = ST_LSM6DSX_SLV_ADDR(0, hub_settings->slv0_addr);
|
||||
config[0] = sensor->ext_info.addr << 1;
|
||||
for (i = 0 ; i < len; i++) {
|
||||
config[1] = addr + i;
|
||||
|
||||
err = st_lsm6dsx_shub_write_reg(hw, slv_addr, config,
|
||||
sizeof(config));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = st_lsm6dsx_shub_write_reg(hw, hub_settings->dw_slv0_addr,
|
||||
&data[i], 1);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = st_lsm6dsx_shub_master_enable(sensor, true);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
st_lsm6dsx_shub_wait_complete(hw);
|
||||
|
||||
st_lsm6dsx_shub_master_enable(sensor, false);
|
||||
}
|
||||
|
||||
memset(config, 0, sizeof(config));
|
||||
return st_lsm6dsx_shub_write_reg(hw, slv_addr, config, sizeof(config));
|
||||
}
|
||||
|
||||
static int
|
||||
st_lsm6dsx_shub_write_with_mask(struct st_lsm6dsx_sensor *sensor,
|
||||
u8 addr, u8 mask, u8 val)
|
||||
{
|
||||
int err;
|
||||
u8 data;
|
||||
|
||||
err = st_lsm6dsx_shub_read(sensor, addr, &data, sizeof(data));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
data = ((data & ~mask) | (val << __ffs(mask) & mask));
|
||||
|
||||
return st_lsm6dsx_shub_write(sensor, addr, &data, sizeof(data));
|
||||
}
|
||||
|
||||
static int
|
||||
st_lsm6dsx_shub_get_odr_val(struct st_lsm6dsx_sensor *sensor,
|
||||
u16 odr, u16 *val)
|
||||
{
|
||||
const struct st_lsm6dsx_ext_dev_settings *settings;
|
||||
int i;
|
||||
|
||||
settings = sensor->ext_info.settings;
|
||||
for (i = 0; i < ST_LSM6DSX_ODR_LIST_SIZE; i++)
|
||||
if (settings->odr_table.odr_avl[i].hz == odr)
|
||||
break;
|
||||
|
||||
if (i == ST_LSM6DSX_ODR_LIST_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
*val = settings->odr_table.odr_avl[i].val;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
st_lsm6dsx_shub_set_odr(struct st_lsm6dsx_sensor *sensor, u16 odr)
|
||||
{
|
||||
const struct st_lsm6dsx_ext_dev_settings *settings;
|
||||
u16 val;
|
||||
int err;
|
||||
|
||||
err = st_lsm6dsx_shub_get_odr_val(sensor, odr, &val);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
settings = sensor->ext_info.settings;
|
||||
return st_lsm6dsx_shub_write_with_mask(sensor,
|
||||
settings->odr_table.reg.addr,
|
||||
settings->odr_table.reg.mask,
|
||||
val);
|
||||
}
|
||||
|
||||
/* use SLV{1,2,3} for FIFO read operations */
|
||||
static int
|
||||
st_lsm6dsx_shub_config_channels(struct st_lsm6dsx_sensor *sensor,
|
||||
bool enable)
|
||||
{
|
||||
const struct st_lsm6dsx_shub_settings *hub_settings;
|
||||
const struct st_lsm6dsx_ext_dev_settings *settings;
|
||||
u8 config[9] = {}, enable_mask, slv_addr;
|
||||
struct st_lsm6dsx_hw *hw = sensor->hw;
|
||||
struct st_lsm6dsx_sensor *cur_sensor;
|
||||
int i, j = 0;
|
||||
|
||||
hub_settings = &hw->settings->shub_settings;
|
||||
if (enable)
|
||||
enable_mask = hw->enable_mask | BIT(sensor->id);
|
||||
else
|
||||
enable_mask = hw->enable_mask & ~BIT(sensor->id);
|
||||
|
||||
for (i = ST_LSM6DSX_ID_EXT0; i <= ST_LSM6DSX_ID_EXT2; i++) {
|
||||
if (!hw->iio_devs[i])
|
||||
continue;
|
||||
|
||||
cur_sensor = iio_priv(hw->iio_devs[i]);
|
||||
if (!(enable_mask & BIT(cur_sensor->id)))
|
||||
continue;
|
||||
|
||||
settings = cur_sensor->ext_info.settings;
|
||||
config[j] = (sensor->ext_info.addr << 1) | 1;
|
||||
config[j + 1] = settings->out.addr;
|
||||
config[j + 2] = (settings->out.len & ST_LS6DSX_READ_OP_MASK) |
|
||||
hub_settings->batch_en;
|
||||
j += 3;
|
||||
}
|
||||
|
||||
slv_addr = ST_LSM6DSX_SLV_ADDR(1, hub_settings->slv0_addr);
|
||||
return st_lsm6dsx_shub_write_reg(hw, slv_addr, config,
|
||||
sizeof(config));
|
||||
}
|
||||
|
||||
int st_lsm6dsx_shub_set_enable(struct st_lsm6dsx_sensor *sensor, bool enable)
|
||||
{
|
||||
const struct st_lsm6dsx_ext_dev_settings *settings;
|
||||
int err;
|
||||
|
||||
err = st_lsm6dsx_shub_config_channels(sensor, enable);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
settings = sensor->ext_info.settings;
|
||||
if (enable) {
|
||||
err = st_lsm6dsx_shub_set_odr(sensor, sensor->odr);
|
||||
if (err < 0)
|
||||
return err;
|
||||
} else {
|
||||
err = st_lsm6dsx_shub_write_with_mask(sensor,
|
||||
settings->odr_table.reg.addr,
|
||||
settings->odr_table.reg.mask, 0);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (settings->pwr_table.reg.addr) {
|
||||
u8 val;
|
||||
|
||||
val = enable ? settings->pwr_table.on_val
|
||||
: settings->pwr_table.off_val;
|
||||
err = st_lsm6dsx_shub_write_with_mask(sensor,
|
||||
settings->pwr_table.reg.addr,
|
||||
settings->pwr_table.reg.mask, val);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
return st_lsm6dsx_shub_master_enable(sensor, enable);
|
||||
}
|
||||
|
||||
static int
|
||||
st_lsm6dsx_shub_read_oneshot(struct st_lsm6dsx_sensor *sensor,
|
||||
struct iio_chan_spec const *ch,
|
||||
int *val)
|
||||
{
|
||||
int err, delay, len = ch->scan_type.realbits >> 3;
|
||||
__le16 data;
|
||||
|
||||
err = st_lsm6dsx_shub_set_enable(sensor, true);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
delay = 1000000 / sensor->odr;
|
||||
usleep_range(delay, 2 * delay);
|
||||
|
||||
err = st_lsm6dsx_shub_read(sensor, ch->address, (u8 *)&data, len);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
st_lsm6dsx_shub_set_enable(sensor, false);
|
||||
|
||||
switch (len) {
|
||||
case 2:
|
||||
*val = (s16)le16_to_cpu(data);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
static int
|
||||
st_lsm6dsx_shub_read_raw(struct iio_dev *iio_dev,
|
||||
struct iio_chan_spec const *ch,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = iio_device_claim_direct_mode(iio_dev);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
ret = st_lsm6dsx_shub_read_oneshot(sensor, ch, val);
|
||||
iio_device_release_direct_mode(iio_dev);
|
||||
break;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
*val = sensor->odr;
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = 0;
|
||||
*val2 = sensor->gain;
|
||||
ret = IIO_VAL_INT_PLUS_MICRO;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
st_lsm6dsx_shub_write_raw(struct iio_dev *iio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev);
|
||||
int err;
|
||||
|
||||
err = iio_device_claim_direct_mode(iio_dev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SAMP_FREQ: {
|
||||
u16 data;
|
||||
|
||||
err = st_lsm6dsx_shub_get_odr_val(sensor, val, &data);
|
||||
if (!err)
|
||||
sensor->odr = val;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
iio_device_release_direct_mode(iio_dev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
st_lsm6dsx_shub_sampling_freq_avail(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct st_lsm6dsx_sensor *sensor = iio_priv(dev_get_drvdata(dev));
|
||||
const struct st_lsm6dsx_ext_dev_settings *settings;
|
||||
int i, len = 0;
|
||||
|
||||
settings = sensor->ext_info.settings;
|
||||
for (i = 0; i < ST_LSM6DSX_ODR_LIST_SIZE; i++) {
|
||||
u16 val = settings->odr_table.odr_avl[i].hz;
|
||||
|
||||
if (val > 0)
|
||||
len += scnprintf(buf + len, PAGE_SIZE - len, "%d ",
|
||||
val);
|
||||
}
|
||||
buf[len - 1] = '\n';
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t st_lsm6dsx_shub_scale_avail(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct st_lsm6dsx_sensor *sensor = iio_priv(dev_get_drvdata(dev));
|
||||
const struct st_lsm6dsx_ext_dev_settings *settings;
|
||||
int i, len = 0;
|
||||
|
||||
settings = sensor->ext_info.settings;
|
||||
for (i = 0; i < ST_LSM6DSX_FS_LIST_SIZE; i++) {
|
||||
u16 val = settings->fs_table.fs_avl[i].gain;
|
||||
|
||||
if (val > 0)
|
||||
len += scnprintf(buf + len, PAGE_SIZE - len, "0.%06u ",
|
||||
val);
|
||||
}
|
||||
buf[len - 1] = '\n';
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(st_lsm6dsx_shub_sampling_freq_avail);
|
||||
static IIO_DEVICE_ATTR(in_scale_available, 0444,
|
||||
st_lsm6dsx_shub_scale_avail, NULL, 0);
|
||||
static struct attribute *st_lsm6dsx_ext_attributes[] = {
|
||||
&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
|
||||
&iio_dev_attr_in_scale_available.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group st_lsm6dsx_ext_attribute_group = {
|
||||
.attrs = st_lsm6dsx_ext_attributes,
|
||||
};
|
||||
|
||||
static const struct iio_info st_lsm6dsx_ext_info = {
|
||||
.attrs = &st_lsm6dsx_ext_attribute_group,
|
||||
.read_raw = st_lsm6dsx_shub_read_raw,
|
||||
.write_raw = st_lsm6dsx_shub_write_raw,
|
||||
.hwfifo_set_watermark = st_lsm6dsx_set_watermark,
|
||||
};
|
||||
|
||||
static struct iio_dev *
|
||||
st_lsm6dsx_shub_alloc_iiodev(struct st_lsm6dsx_hw *hw,
|
||||
enum st_lsm6dsx_sensor_id id,
|
||||
const struct st_lsm6dsx_ext_dev_settings *info,
|
||||
u8 i2c_addr, const char *name)
|
||||
{
|
||||
struct iio_chan_spec *ext_channels;
|
||||
struct st_lsm6dsx_sensor *sensor;
|
||||
struct iio_dev *iio_dev;
|
||||
|
||||
iio_dev = devm_iio_device_alloc(hw->dev, sizeof(*sensor));
|
||||
if (!iio_dev)
|
||||
return NULL;
|
||||
|
||||
iio_dev->modes = INDIO_DIRECT_MODE;
|
||||
iio_dev->dev.parent = hw->dev;
|
||||
iio_dev->info = &st_lsm6dsx_ext_info;
|
||||
|
||||
sensor = iio_priv(iio_dev);
|
||||
sensor->id = id;
|
||||
sensor->hw = hw;
|
||||
sensor->odr = info->odr_table.odr_avl[0].hz;
|
||||
sensor->gain = info->fs_table.fs_avl[0].gain;
|
||||
sensor->ext_info.settings = info;
|
||||
sensor->ext_info.addr = i2c_addr;
|
||||
sensor->watermark = 1;
|
||||
|
||||
switch (info->id) {
|
||||
case ST_LSM6DSX_ID_MAGN: {
|
||||
const struct iio_chan_spec magn_channels[] = {
|
||||
ST_LSM6DSX_CHANNEL(IIO_MAGN, info->out.addr,
|
||||
IIO_MOD_X, 0),
|
||||
ST_LSM6DSX_CHANNEL(IIO_MAGN, info->out.addr + 2,
|
||||
IIO_MOD_Y, 1),
|
||||
ST_LSM6DSX_CHANNEL(IIO_MAGN, info->out.addr + 4,
|
||||
IIO_MOD_Z, 2),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(3),
|
||||
};
|
||||
|
||||
ext_channels = devm_kzalloc(hw->dev, sizeof(magn_channels),
|
||||
GFP_KERNEL);
|
||||
if (!ext_channels)
|
||||
return NULL;
|
||||
|
||||
memcpy(ext_channels, magn_channels, sizeof(magn_channels));
|
||||
iio_dev->available_scan_masks = st_lsm6dsx_available_scan_masks;
|
||||
iio_dev->channels = ext_channels;
|
||||
iio_dev->num_channels = ARRAY_SIZE(magn_channels);
|
||||
|
||||
scnprintf(sensor->name, sizeof(sensor->name), "%s_magn",
|
||||
name);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
iio_dev->name = sensor->name;
|
||||
|
||||
return iio_dev;
|
||||
}
|
||||
|
||||
static int st_lsm6dsx_shub_init_device(struct st_lsm6dsx_sensor *sensor)
|
||||
{
|
||||
const struct st_lsm6dsx_ext_dev_settings *settings;
|
||||
int err;
|
||||
|
||||
settings = sensor->ext_info.settings;
|
||||
if (settings->bdu.addr) {
|
||||
err = st_lsm6dsx_shub_write_with_mask(sensor,
|
||||
settings->bdu.addr,
|
||||
settings->bdu.mask, 1);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (settings->temp_comp.addr) {
|
||||
err = st_lsm6dsx_shub_write_with_mask(sensor,
|
||||
settings->temp_comp.addr,
|
||||
settings->temp_comp.mask, 1);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (settings->off_canc.addr) {
|
||||
err = st_lsm6dsx_shub_write_with_mask(sensor,
|
||||
settings->off_canc.addr,
|
||||
settings->off_canc.mask, 1);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
st_lsm6dsx_shub_check_wai(struct st_lsm6dsx_hw *hw, u8 *i2c_addr,
|
||||
const struct st_lsm6dsx_ext_dev_settings *settings)
|
||||
{
|
||||
const struct st_lsm6dsx_shub_settings *hub_settings;
|
||||
struct st_lsm6dsx_sensor *sensor;
|
||||
u8 config[3], data, slv_addr;
|
||||
bool found = false;
|
||||
int i, err;
|
||||
|
||||
hub_settings = &hw->settings->shub_settings;
|
||||
slv_addr = ST_LSM6DSX_SLV_ADDR(0, hub_settings->slv0_addr);
|
||||
sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_ACC]);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(settings->i2c_addr); i++) {
|
||||
if (!settings->i2c_addr[i])
|
||||
continue;
|
||||
|
||||
/* read wai slave register */
|
||||
config[0] = (settings->i2c_addr[i] << 1) | 0x1;
|
||||
config[1] = settings->wai.addr;
|
||||
config[2] = 0x1;
|
||||
|
||||
err = st_lsm6dsx_shub_write_reg(hw, slv_addr, config,
|
||||
sizeof(config));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = st_lsm6dsx_shub_master_enable(sensor, true);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
st_lsm6dsx_shub_wait_complete(hw);
|
||||
|
||||
err = st_lsm6dsx_shub_read_reg(hw,
|
||||
hub_settings->shub_out,
|
||||
&data, sizeof(data));
|
||||
|
||||
st_lsm6dsx_shub_master_enable(sensor, false);
|
||||
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (data != settings->wai.val)
|
||||
continue;
|
||||
|
||||
*i2c_addr = settings->i2c_addr[i];
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
/* reset SLV0 channel */
|
||||
memset(config, 0, sizeof(config));
|
||||
err = st_lsm6dsx_shub_write_reg(hw, slv_addr, config,
|
||||
sizeof(config));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return found ? 0 : -ENODEV;
|
||||
}
|
||||
|
||||
int st_lsm6dsx_shub_probe(struct st_lsm6dsx_hw *hw, const char *name)
|
||||
{
|
||||
enum st_lsm6dsx_sensor_id id = ST_LSM6DSX_ID_EXT0;
|
||||
struct st_lsm6dsx_sensor *sensor;
|
||||
int err, i, num_ext_dev = 0;
|
||||
u8 i2c_addr = 0;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(st_lsm6dsx_ext_dev_table); i++) {
|
||||
err = st_lsm6dsx_shub_check_wai(hw, &i2c_addr,
|
||||
&st_lsm6dsx_ext_dev_table[i]);
|
||||
if (err == -ENODEV)
|
||||
continue;
|
||||
else if (err < 0)
|
||||
return err;
|
||||
|
||||
hw->iio_devs[id] = st_lsm6dsx_shub_alloc_iiodev(hw, id,
|
||||
&st_lsm6dsx_ext_dev_table[i],
|
||||
i2c_addr, name);
|
||||
if (!hw->iio_devs[id])
|
||||
return -ENOMEM;
|
||||
|
||||
sensor = iio_priv(hw->iio_devs[id]);
|
||||
err = st_lsm6dsx_shub_init_device(sensor);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (++num_ext_dev >= ST_LSM6DSX_MAX_SLV_NUM)
|
||||
break;
|
||||
id++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -460,6 +460,19 @@ config VCNL4000
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called vcnl4000.
|
||||
|
||||
config VCNL4035
|
||||
tristate "VCNL4035 combined ALS and proximity sensor"
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
select REGMAP_I2C
|
||||
depends on I2C
|
||||
help
|
||||
Say Y here if you want to build a driver for the Vishay VCNL4035,
|
||||
combined ambient light (ALS) and proximity sensor. Currently only ALS
|
||||
function is available.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called vcnl4035.
|
||||
|
||||
config VEML6070
|
||||
tristate "VEML6070 UV A light sensor"
|
||||
depends on I2C
|
||||
|
@ -45,6 +45,7 @@ obj-$(CONFIG_TSL2772) += tsl2772.o
|
||||
obj-$(CONFIG_TSL4531) += tsl4531.o
|
||||
obj-$(CONFIG_US5182D) += us5182d.o
|
||||
obj-$(CONFIG_VCNL4000) += vcnl4000.o
|
||||
obj-$(CONFIG_VCNL4035) += vcnl4035.o
|
||||
obj-$(CONFIG_VEML6070) += veml6070.o
|
||||
obj-$(CONFIG_VL6180) += vl6180.o
|
||||
obj-$(CONFIG_ZOPT2201) += zopt2201.o
|
||||
|
676
drivers/iio/light/vcnl4035.c
Normal file
676
drivers/iio/light/vcnl4035.c
Normal file
@ -0,0 +1,676 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* VCNL4035 Ambient Light and Proximity Sensor - 7-bit I2C slave address 0x60
|
||||
*
|
||||
* Copyright (c) 2018, DENX Software Engineering GmbH
|
||||
* Author: Parthiban Nallathambi <pn@denx.de>
|
||||
*
|
||||
* TODO: Proximity
|
||||
*/
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/events.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
|
||||
#define VCNL4035_DRV_NAME "vcnl4035"
|
||||
#define VCNL4035_IRQ_NAME "vcnl4035_event"
|
||||
#define VCNL4035_REGMAP_NAME "vcnl4035_regmap"
|
||||
|
||||
/* Device registers */
|
||||
#define VCNL4035_ALS_CONF 0x00
|
||||
#define VCNL4035_ALS_THDH 0x01
|
||||
#define VCNL4035_ALS_THDL 0x02
|
||||
#define VCNL4035_ALS_DATA 0x0B
|
||||
#define VCNL4035_WHITE_DATA 0x0C
|
||||
#define VCNL4035_INT_FLAG 0x0D
|
||||
#define VCNL4035_DEV_ID 0x0E
|
||||
|
||||
/* Register masks */
|
||||
#define VCNL4035_MODE_ALS_MASK BIT(0)
|
||||
#define VCNL4035_MODE_ALS_WHITE_CHAN BIT(8)
|
||||
#define VCNL4035_MODE_ALS_INT_MASK BIT(1)
|
||||
#define VCNL4035_ALS_IT_MASK GENMASK(7, 5)
|
||||
#define VCNL4035_ALS_PERS_MASK GENMASK(3, 2)
|
||||
#define VCNL4035_INT_ALS_IF_H_MASK BIT(12)
|
||||
#define VCNL4035_INT_ALS_IF_L_MASK BIT(13)
|
||||
|
||||
/* Default values */
|
||||
#define VCNL4035_MODE_ALS_ENABLE BIT(0)
|
||||
#define VCNL4035_MODE_ALS_DISABLE 0x00
|
||||
#define VCNL4035_MODE_ALS_INT_ENABLE BIT(1)
|
||||
#define VCNL4035_MODE_ALS_INT_DISABLE 0
|
||||
#define VCNL4035_DEV_ID_VAL 0x80
|
||||
#define VCNL4035_ALS_IT_DEFAULT 0x01
|
||||
#define VCNL4035_ALS_PERS_DEFAULT 0x00
|
||||
#define VCNL4035_ALS_THDH_DEFAULT 5000
|
||||
#define VCNL4035_ALS_THDL_DEFAULT 100
|
||||
#define VCNL4035_SLEEP_DELAY_MS 2000
|
||||
|
||||
struct vcnl4035_data {
|
||||
struct i2c_client *client;
|
||||
struct regmap *regmap;
|
||||
unsigned int als_it_val;
|
||||
unsigned int als_persistence;
|
||||
unsigned int als_thresh_low;
|
||||
unsigned int als_thresh_high;
|
||||
struct iio_trigger *drdy_trigger0;
|
||||
};
|
||||
|
||||
static inline bool vcnl4035_is_triggered(struct vcnl4035_data *data)
|
||||
{
|
||||
int ret;
|
||||
int reg;
|
||||
|
||||
ret = regmap_read(data->regmap, VCNL4035_INT_FLAG, ®);
|
||||
if (ret < 0)
|
||||
return false;
|
||||
|
||||
return !!(reg &
|
||||
(VCNL4035_INT_ALS_IF_H_MASK | VCNL4035_INT_ALS_IF_L_MASK));
|
||||
}
|
||||
|
||||
static irqreturn_t vcnl4035_drdy_irq_thread(int irq, void *private)
|
||||
{
|
||||
struct iio_dev *indio_dev = private;
|
||||
struct vcnl4035_data *data = iio_priv(indio_dev);
|
||||
|
||||
if (vcnl4035_is_triggered(data)) {
|
||||
iio_push_event(indio_dev, IIO_UNMOD_EVENT_CODE(IIO_LIGHT,
|
||||
0,
|
||||
IIO_EV_TYPE_THRESH,
|
||||
IIO_EV_DIR_EITHER),
|
||||
iio_get_time_ns(indio_dev));
|
||||
iio_trigger_poll_chained(data->drdy_trigger0);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
/* Triggered buffer */
|
||||
static irqreturn_t vcnl4035_trigger_consumer_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct vcnl4035_data *data = iio_priv(indio_dev);
|
||||
u8 buffer[ALIGN(sizeof(u16), sizeof(s64)) + sizeof(s64)];
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(data->regmap, VCNL4035_ALS_DATA, (int *)buffer);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev,
|
||||
"Trigger consumer can't read from sensor.\n");
|
||||
goto fail_read;
|
||||
}
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, buffer,
|
||||
iio_get_time_ns(indio_dev));
|
||||
|
||||
fail_read:
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int vcnl4035_als_drdy_set_state(struct iio_trigger *trigger,
|
||||
bool enable_drdy)
|
||||
{
|
||||
struct iio_dev *indio_dev = iio_trigger_get_drvdata(trigger);
|
||||
struct vcnl4035_data *data = iio_priv(indio_dev);
|
||||
int val = enable_drdy ? VCNL4035_MODE_ALS_INT_ENABLE :
|
||||
VCNL4035_MODE_ALS_INT_DISABLE;
|
||||
|
||||
return regmap_update_bits(data->regmap, VCNL4035_ALS_CONF,
|
||||
VCNL4035_MODE_ALS_INT_MASK,
|
||||
val);
|
||||
}
|
||||
|
||||
static const struct iio_trigger_ops vcnl4035_trigger_ops = {
|
||||
.validate_device = iio_trigger_validate_own_device,
|
||||
.set_trigger_state = vcnl4035_als_drdy_set_state,
|
||||
};
|
||||
|
||||
static int vcnl4035_set_pm_runtime_state(struct vcnl4035_data *data, bool on)
|
||||
{
|
||||
int ret;
|
||||
struct device *dev = &data->client->dev;
|
||||
|
||||
if (on) {
|
||||
ret = pm_runtime_get_sync(dev);
|
||||
if (ret < 0)
|
||||
pm_runtime_put_noidle(dev);
|
||||
} else {
|
||||
pm_runtime_mark_last_busy(dev);
|
||||
ret = pm_runtime_put_autosuspend(dev);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Device IT INT Time (ms) Scale (lux/step)
|
||||
* 000 50 0.064
|
||||
* 001 100 0.032
|
||||
* 010 200 0.016
|
||||
* 100 400 0.008
|
||||
* 101 - 111 800 0.004
|
||||
* Values are proportional, so ALS INT is selected for input due to
|
||||
* simplicity reason. Integration time value and scaling is
|
||||
* calculated based on device INT value
|
||||
*
|
||||
* Raw value needs to be scaled using ALS steps
|
||||
*/
|
||||
static int vcnl4035_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val,
|
||||
int *val2, long mask)
|
||||
{
|
||||
struct vcnl4035_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
int raw_data;
|
||||
unsigned int reg;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = vcnl4035_set_pm_runtime_state(data, true);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = iio_device_claim_direct_mode(indio_dev);
|
||||
if (!ret) {
|
||||
if (chan->channel)
|
||||
reg = VCNL4035_ALS_DATA;
|
||||
else
|
||||
reg = VCNL4035_WHITE_DATA;
|
||||
ret = regmap_read(data->regmap, reg, &raw_data);
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
if (!ret) {
|
||||
*val = raw_data;
|
||||
ret = IIO_VAL_INT;
|
||||
}
|
||||
}
|
||||
vcnl4035_set_pm_runtime_state(data, false);
|
||||
return ret;
|
||||
case IIO_CHAN_INFO_INT_TIME:
|
||||
*val = 50;
|
||||
if (data->als_it_val)
|
||||
*val = data->als_it_val * 100;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = 64;
|
||||
if (!data->als_it_val)
|
||||
*val2 = 1000;
|
||||
else
|
||||
*val2 = data->als_it_val * 2 * 1000;
|
||||
return IIO_VAL_FRACTIONAL;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int vcnl4035_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
int ret;
|
||||
struct vcnl4035_data *data = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_INT_TIME:
|
||||
if (val <= 0 || val > 800)
|
||||
return -EINVAL;
|
||||
|
||||
ret = vcnl4035_set_pm_runtime_state(data, true);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = regmap_update_bits(data->regmap, VCNL4035_ALS_CONF,
|
||||
VCNL4035_ALS_IT_MASK,
|
||||
val / 100);
|
||||
if (!ret)
|
||||
data->als_it_val = val / 100;
|
||||
|
||||
vcnl4035_set_pm_runtime_state(data, false);
|
||||
return ret;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
/* No direct ABI for persistence and threshold, so eventing */
|
||||
static int vcnl4035_read_thresh(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 vcnl4035_data *data = iio_priv(indio_dev);
|
||||
|
||||
switch (info) {
|
||||
case IIO_EV_INFO_VALUE:
|
||||
switch (dir) {
|
||||
case IIO_EV_DIR_RISING:
|
||||
*val = data->als_thresh_high;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_EV_DIR_FALLING:
|
||||
*val = data->als_thresh_low;
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
case IIO_EV_INFO_PERIOD:
|
||||
*val = data->als_persistence;
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static int vcnl4035_write_thresh(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 vcnl4035_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (info) {
|
||||
case IIO_EV_INFO_VALUE:
|
||||
/* 16 bit threshold range 0 - 65535 */
|
||||
if (val < 0 || val > 65535)
|
||||
return -EINVAL;
|
||||
if (dir == IIO_EV_DIR_RISING) {
|
||||
if (val < data->als_thresh_low)
|
||||
return -EINVAL;
|
||||
ret = regmap_write(data->regmap, VCNL4035_ALS_THDH,
|
||||
val);
|
||||
if (ret)
|
||||
return ret;
|
||||
data->als_thresh_high = val;
|
||||
} else {
|
||||
if (val > data->als_thresh_high)
|
||||
return -EINVAL;
|
||||
ret = regmap_write(data->regmap, VCNL4035_ALS_THDL,
|
||||
val);
|
||||
if (ret)
|
||||
return ret;
|
||||
data->als_thresh_low = val;
|
||||
}
|
||||
return ret;
|
||||
case IIO_EV_INFO_PERIOD:
|
||||
/* allow only 1 2 4 8 as persistence value */
|
||||
if (val < 0 || val > 8 || hweight8(val) != 1)
|
||||
return -EINVAL;
|
||||
ret = regmap_update_bits(data->regmap, VCNL4035_ALS_CONF,
|
||||
VCNL4035_ALS_PERS_MASK, val);
|
||||
if (!ret)
|
||||
data->als_persistence = val;
|
||||
return ret;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static IIO_CONST_ATTR_INT_TIME_AVAIL("50 100 200 400 800");
|
||||
|
||||
static struct attribute *vcnl4035_attributes[] = {
|
||||
&iio_const_attr_integration_time_available.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group vcnl4035_attribute_group = {
|
||||
.attrs = vcnl4035_attributes,
|
||||
};
|
||||
|
||||
static const struct iio_info vcnl4035_info = {
|
||||
.read_raw = vcnl4035_read_raw,
|
||||
.write_raw = vcnl4035_write_raw,
|
||||
.read_event_value = vcnl4035_read_thresh,
|
||||
.write_event_value = vcnl4035_write_thresh,
|
||||
.attrs = &vcnl4035_attribute_group,
|
||||
};
|
||||
|
||||
static const struct iio_event_spec vcnl4035_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_PERIOD),
|
||||
},
|
||||
};
|
||||
|
||||
enum vcnl4035_scan_index_order {
|
||||
VCNL4035_CHAN_INDEX_LIGHT,
|
||||
VCNL4035_CHAN_INDEX_WHITE_LED,
|
||||
};
|
||||
|
||||
static const struct iio_buffer_setup_ops iio_triggered_buffer_setup_ops = {
|
||||
.validate_scan_mask = &iio_validate_scan_mask_onehot,
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec vcnl4035_channels[] = {
|
||||
{
|
||||
.type = IIO_LIGHT,
|
||||
.channel = 0,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_INT_TIME) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
.event_spec = vcnl4035_event_spec,
|
||||
.num_event_specs = ARRAY_SIZE(vcnl4035_event_spec),
|
||||
.scan_index = VCNL4035_CHAN_INDEX_LIGHT,
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
.realbits = 16,
|
||||
.storagebits = 16,
|
||||
.endianness = IIO_LE,
|
||||
},
|
||||
},
|
||||
{
|
||||
.type = IIO_INTENSITY,
|
||||
.channel = 1,
|
||||
.modified = 1,
|
||||
.channel2 = IIO_MOD_LIGHT_BOTH,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.scan_index = VCNL4035_CHAN_INDEX_WHITE_LED,
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
.realbits = 16,
|
||||
.storagebits = 16,
|
||||
.endianness = IIO_LE,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static int vcnl4035_set_als_power_state(struct vcnl4035_data *data, u8 status)
|
||||
{
|
||||
return regmap_update_bits(data->regmap, VCNL4035_ALS_CONF,
|
||||
VCNL4035_MODE_ALS_MASK,
|
||||
status);
|
||||
}
|
||||
|
||||
static int vcnl4035_init(struct vcnl4035_data *data)
|
||||
{
|
||||
int ret;
|
||||
int id;
|
||||
|
||||
ret = regmap_read(data->regmap, VCNL4035_DEV_ID, &id);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev, "Failed to read DEV_ID register\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (id != VCNL4035_DEV_ID_VAL) {
|
||||
dev_err(&data->client->dev, "Wrong id, got %x, expected %x\n",
|
||||
id, VCNL4035_DEV_ID_VAL);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ret = vcnl4035_set_als_power_state(data, VCNL4035_MODE_ALS_ENABLE);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* ALS white channel enable */
|
||||
ret = regmap_update_bits(data->regmap, VCNL4035_ALS_CONF,
|
||||
VCNL4035_MODE_ALS_WHITE_CHAN,
|
||||
1);
|
||||
if (ret) {
|
||||
dev_err(&data->client->dev, "set white channel enable %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* set default integration time - 100 ms for ALS */
|
||||
ret = regmap_update_bits(data->regmap, VCNL4035_ALS_CONF,
|
||||
VCNL4035_ALS_IT_MASK,
|
||||
VCNL4035_ALS_IT_DEFAULT);
|
||||
if (ret) {
|
||||
dev_err(&data->client->dev, "set default ALS IT returned %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
data->als_it_val = VCNL4035_ALS_IT_DEFAULT;
|
||||
|
||||
/* set default persistence time - 1 for ALS */
|
||||
ret = regmap_update_bits(data->regmap, VCNL4035_ALS_CONF,
|
||||
VCNL4035_ALS_PERS_MASK,
|
||||
VCNL4035_ALS_PERS_DEFAULT);
|
||||
if (ret) {
|
||||
dev_err(&data->client->dev, "set default PERS returned %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
data->als_persistence = VCNL4035_ALS_PERS_DEFAULT;
|
||||
|
||||
/* set default HIGH threshold for ALS */
|
||||
ret = regmap_write(data->regmap, VCNL4035_ALS_THDH,
|
||||
VCNL4035_ALS_THDH_DEFAULT);
|
||||
if (ret) {
|
||||
dev_err(&data->client->dev, "set default THDH returned %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
data->als_thresh_high = VCNL4035_ALS_THDH_DEFAULT;
|
||||
|
||||
/* set default LOW threshold for ALS */
|
||||
ret = regmap_write(data->regmap, VCNL4035_ALS_THDL,
|
||||
VCNL4035_ALS_THDL_DEFAULT);
|
||||
if (ret) {
|
||||
dev_err(&data->client->dev, "set default THDL returned %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
data->als_thresh_low = VCNL4035_ALS_THDL_DEFAULT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool vcnl4035_is_volatile_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case VCNL4035_ALS_CONF:
|
||||
case VCNL4035_DEV_ID:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct regmap_config vcnl4035_regmap_config = {
|
||||
.name = VCNL4035_REGMAP_NAME,
|
||||
.reg_bits = 8,
|
||||
.val_bits = 16,
|
||||
.max_register = VCNL4035_DEV_ID,
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
.volatile_reg = vcnl4035_is_volatile_reg,
|
||||
.val_format_endian = REGMAP_ENDIAN_LITTLE,
|
||||
};
|
||||
|
||||
static int vcnl4035_probe_trigger(struct iio_dev *indio_dev)
|
||||
{
|
||||
int ret;
|
||||
struct vcnl4035_data *data = iio_priv(indio_dev);
|
||||
|
||||
data->drdy_trigger0 = devm_iio_trigger_alloc(
|
||||
indio_dev->dev.parent,
|
||||
"%s-dev%d", indio_dev->name, indio_dev->id);
|
||||
if (!data->drdy_trigger0)
|
||||
return -ENOMEM;
|
||||
|
||||
data->drdy_trigger0->dev.parent = indio_dev->dev.parent;
|
||||
data->drdy_trigger0->ops = &vcnl4035_trigger_ops;
|
||||
iio_trigger_set_drvdata(data->drdy_trigger0, indio_dev);
|
||||
ret = devm_iio_trigger_register(indio_dev->dev.parent,
|
||||
data->drdy_trigger0);
|
||||
if (ret) {
|
||||
dev_err(&data->client->dev, "iio trigger register failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Trigger setup */
|
||||
ret = devm_iio_triggered_buffer_setup(indio_dev->dev.parent, indio_dev,
|
||||
NULL, vcnl4035_trigger_consumer_handler,
|
||||
&iio_triggered_buffer_setup_ops);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev, "iio triggered buffer setup failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* IRQ to trigger mapping */
|
||||
ret = devm_request_threaded_irq(&data->client->dev, data->client->irq,
|
||||
NULL, vcnl4035_drdy_irq_thread,
|
||||
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
|
||||
VCNL4035_IRQ_NAME, indio_dev);
|
||||
if (ret < 0)
|
||||
dev_err(&data->client->dev, "request irq %d for trigger0 failed\n",
|
||||
data->client->irq);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int vcnl4035_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct vcnl4035_data *data;
|
||||
struct iio_dev *indio_dev;
|
||||
struct regmap *regmap;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
regmap = devm_regmap_init_i2c(client, &vcnl4035_regmap_config);
|
||||
if (IS_ERR(regmap)) {
|
||||
dev_err(&client->dev, "regmap_init failed!\n");
|
||||
return PTR_ERR(regmap);
|
||||
}
|
||||
|
||||
data = iio_priv(indio_dev);
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
data->client = client;
|
||||
data->regmap = regmap;
|
||||
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->info = &vcnl4035_info;
|
||||
indio_dev->name = VCNL4035_DRV_NAME;
|
||||
indio_dev->channels = vcnl4035_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(vcnl4035_channels);
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
ret = vcnl4035_init(data);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "vcnl4035 chip init failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (client->irq > 0) {
|
||||
ret = vcnl4035_probe_trigger(indio_dev);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "vcnl4035 unable init trigger\n");
|
||||
goto fail_poweroff;
|
||||
}
|
||||
}
|
||||
|
||||
ret = pm_runtime_set_active(&client->dev);
|
||||
if (ret < 0)
|
||||
goto fail_poweroff;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret < 0)
|
||||
goto fail_poweroff;
|
||||
|
||||
pm_runtime_enable(&client->dev);
|
||||
pm_runtime_set_autosuspend_delay(&client->dev, VCNL4035_SLEEP_DELAY_MS);
|
||||
pm_runtime_use_autosuspend(&client->dev);
|
||||
|
||||
return 0;
|
||||
|
||||
fail_poweroff:
|
||||
vcnl4035_set_als_power_state(data, VCNL4035_MODE_ALS_DISABLE);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int vcnl4035_remove(struct i2c_client *client)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
|
||||
pm_runtime_dont_use_autosuspend(&client->dev);
|
||||
pm_runtime_disable(&client->dev);
|
||||
iio_device_unregister(indio_dev);
|
||||
pm_runtime_set_suspended(&client->dev);
|
||||
|
||||
return vcnl4035_set_als_power_state(iio_priv(indio_dev),
|
||||
VCNL4035_MODE_ALS_DISABLE);
|
||||
}
|
||||
|
||||
static int __maybe_unused vcnl4035_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
|
||||
struct vcnl4035_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = vcnl4035_set_als_power_state(data, VCNL4035_MODE_ALS_DISABLE);
|
||||
regcache_mark_dirty(data->regmap);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __maybe_unused vcnl4035_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
|
||||
struct vcnl4035_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
regcache_sync(data->regmap);
|
||||
ret = vcnl4035_set_als_power_state(data, VCNL4035_MODE_ALS_ENABLE);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* wait for 1 ALS integration cycle */
|
||||
msleep(data->als_it_val * 100);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops vcnl4035_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
|
||||
pm_runtime_force_resume)
|
||||
SET_RUNTIME_PM_OPS(vcnl4035_runtime_suspend,
|
||||
vcnl4035_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
static const struct of_device_id vcnl4035_of_match[] = {
|
||||
{ .compatible = "vishay,vcnl4035", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, vcnl4035_of_match);
|
||||
|
||||
static struct i2c_driver vcnl4035_driver = {
|
||||
.driver = {
|
||||
.name = VCNL4035_DRV_NAME,
|
||||
.pm = &vcnl4035_pm_ops,
|
||||
.of_match_table = vcnl4035_of_match,
|
||||
},
|
||||
.probe = vcnl4035_probe,
|
||||
.remove = vcnl4035_remove,
|
||||
};
|
||||
|
||||
module_i2c_driver(vcnl4035_driver);
|
||||
|
||||
MODULE_AUTHOR("Parthiban Nallathambi <pn@denx.de>");
|
||||
MODULE_DESCRIPTION("VCNL4035 Ambient Light Sensor driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -175,4 +175,33 @@ config SENSORS_HMC5843_SPI
|
||||
- hmc5843_core (core functions)
|
||||
- hmc5843_spi (support for HMC5983)
|
||||
|
||||
config SENSORS_RM3100
|
||||
tristate
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
|
||||
config SENSORS_RM3100_I2C
|
||||
tristate "PNI RM3100 3-Axis Magnetometer (I2C)"
|
||||
depends on I2C
|
||||
select SENSORS_RM3100
|
||||
select REGMAP_I2C
|
||||
help
|
||||
Say Y here to add support for the PNI RM3100 3-Axis Magnetometer.
|
||||
|
||||
This driver can also be compiled as a module.
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called rm3100-i2c.
|
||||
|
||||
config SENSORS_RM3100_SPI
|
||||
tristate "PNI RM3100 3-Axis Magnetometer (SPI)"
|
||||
depends on SPI_MASTER
|
||||
select SENSORS_RM3100
|
||||
select REGMAP_SPI
|
||||
help
|
||||
Say Y here to add support for the PNI RM3100 3-Axis Magnetometer.
|
||||
|
||||
This driver can also be compiled as a module.
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called rm3100-spi.
|
||||
|
||||
endmenu
|
||||
|
@ -24,3 +24,7 @@ obj-$(CONFIG_IIO_ST_MAGN_SPI_3AXIS) += st_magn_spi.o
|
||||
obj-$(CONFIG_SENSORS_HMC5843) += hmc5843_core.o
|
||||
obj-$(CONFIG_SENSORS_HMC5843_I2C) += hmc5843_i2c.o
|
||||
obj-$(CONFIG_SENSORS_HMC5843_SPI) += hmc5843_spi.o
|
||||
|
||||
obj-$(CONFIG_SENSORS_RM3100) += rm3100-core.o
|
||||
obj-$(CONFIG_SENSORS_RM3100_I2C) += rm3100-i2c.o
|
||||
obj-$(CONFIG_SENSORS_RM3100_SPI) += rm3100-spi.o
|
||||
|
616
drivers/iio/magnetometer/rm3100-core.c
Normal file
616
drivers/iio/magnetometer/rm3100-core.c
Normal file
@ -0,0 +1,616 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* PNI RM3100 3-axis geomagnetic sensor driver core.
|
||||
*
|
||||
* Copyright (C) 2018 Song Qiang <songqiang1304521@gmail.com>
|
||||
*
|
||||
* User Manual available at
|
||||
* <https://www.pnicorp.com/download/rm3100-user-manual/>
|
||||
*
|
||||
* TODO: event generation, pm.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
|
||||
#include "rm3100.h"
|
||||
|
||||
/* Cycle Count Registers. */
|
||||
#define RM3100_REG_CC_X 0x05
|
||||
#define RM3100_REG_CC_Y 0x07
|
||||
#define RM3100_REG_CC_Z 0x09
|
||||
|
||||
/* Poll Measurement Mode register. */
|
||||
#define RM3100_REG_POLL 0x00
|
||||
#define RM3100_POLL_X BIT(4)
|
||||
#define RM3100_POLL_Y BIT(5)
|
||||
#define RM3100_POLL_Z BIT(6)
|
||||
|
||||
/* Continuous Measurement Mode register. */
|
||||
#define RM3100_REG_CMM 0x01
|
||||
#define RM3100_CMM_START BIT(0)
|
||||
#define RM3100_CMM_X BIT(4)
|
||||
#define RM3100_CMM_Y BIT(5)
|
||||
#define RM3100_CMM_Z BIT(6)
|
||||
|
||||
/* TiMe Rate Configuration register. */
|
||||
#define RM3100_REG_TMRC 0x0B
|
||||
#define RM3100_TMRC_OFFSET 0x92
|
||||
|
||||
/* Result Status register. */
|
||||
#define RM3100_REG_STATUS 0x34
|
||||
#define RM3100_STATUS_DRDY BIT(7)
|
||||
|
||||
/* Measurement result registers. */
|
||||
#define RM3100_REG_MX2 0x24
|
||||
#define RM3100_REG_MY2 0x27
|
||||
#define RM3100_REG_MZ2 0x2a
|
||||
|
||||
#define RM3100_W_REG_START RM3100_REG_POLL
|
||||
#define RM3100_W_REG_END RM3100_REG_TMRC
|
||||
#define RM3100_R_REG_START RM3100_REG_POLL
|
||||
#define RM3100_R_REG_END RM3100_REG_STATUS
|
||||
#define RM3100_V_REG_START RM3100_REG_POLL
|
||||
#define RM3100_V_REG_END RM3100_REG_STATUS
|
||||
|
||||
/*
|
||||
* This is computed by hand, is the sum of channel storage bits and padding
|
||||
* bits, which is 4+4+4+12=24 in here.
|
||||
*/
|
||||
#define RM3100_SCAN_BYTES 24
|
||||
|
||||
#define RM3100_CMM_AXIS_SHIFT 4
|
||||
|
||||
struct rm3100_data {
|
||||
struct regmap *regmap;
|
||||
struct completion measuring_done;
|
||||
bool use_interrupt;
|
||||
int conversion_time;
|
||||
int scale;
|
||||
u8 buffer[RM3100_SCAN_BYTES];
|
||||
struct iio_trigger *drdy_trig;
|
||||
|
||||
/*
|
||||
* This lock is for protecting the consistency of series of i2c
|
||||
* operations, that is, to make sure a measurement process will
|
||||
* not be interrupted by a set frequency operation, which should
|
||||
* be taken where a series of i2c operation starts, released where
|
||||
* the operation ends.
|
||||
*/
|
||||
struct mutex lock;
|
||||
};
|
||||
|
||||
static const struct regmap_range rm3100_readable_ranges[] = {
|
||||
regmap_reg_range(RM3100_R_REG_START, RM3100_R_REG_END),
|
||||
};
|
||||
|
||||
const struct regmap_access_table rm3100_readable_table = {
|
||||
.yes_ranges = rm3100_readable_ranges,
|
||||
.n_yes_ranges = ARRAY_SIZE(rm3100_readable_ranges),
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(rm3100_readable_table);
|
||||
|
||||
static const struct regmap_range rm3100_writable_ranges[] = {
|
||||
regmap_reg_range(RM3100_W_REG_START, RM3100_W_REG_END),
|
||||
};
|
||||
|
||||
const struct regmap_access_table rm3100_writable_table = {
|
||||
.yes_ranges = rm3100_writable_ranges,
|
||||
.n_yes_ranges = ARRAY_SIZE(rm3100_writable_ranges),
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(rm3100_writable_table);
|
||||
|
||||
static const struct regmap_range rm3100_volatile_ranges[] = {
|
||||
regmap_reg_range(RM3100_V_REG_START, RM3100_V_REG_END),
|
||||
};
|
||||
|
||||
const struct regmap_access_table rm3100_volatile_table = {
|
||||
.yes_ranges = rm3100_volatile_ranges,
|
||||
.n_yes_ranges = ARRAY_SIZE(rm3100_volatile_ranges),
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(rm3100_volatile_table);
|
||||
|
||||
static irqreturn_t rm3100_thread_fn(int irq, void *d)
|
||||
{
|
||||
struct iio_dev *indio_dev = d;
|
||||
struct rm3100_data *data = iio_priv(indio_dev);
|
||||
|
||||
/*
|
||||
* Write operation to any register or read operation
|
||||
* to first byte of results will clear the interrupt.
|
||||
*/
|
||||
regmap_write(data->regmap, RM3100_REG_POLL, 0);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t rm3100_irq_handler(int irq, void *d)
|
||||
{
|
||||
struct iio_dev *indio_dev = d;
|
||||
struct rm3100_data *data = iio_priv(indio_dev);
|
||||
|
||||
switch (indio_dev->currentmode) {
|
||||
case INDIO_DIRECT_MODE:
|
||||
complete(&data->measuring_done);
|
||||
break;
|
||||
case INDIO_BUFFER_TRIGGERED:
|
||||
iio_trigger_poll(data->drdy_trig);
|
||||
break;
|
||||
default:
|
||||
dev_err(indio_dev->dev.parent,
|
||||
"device mode out of control, current mode: %d",
|
||||
indio_dev->currentmode);
|
||||
}
|
||||
|
||||
return IRQ_WAKE_THREAD;
|
||||
}
|
||||
|
||||
static int rm3100_wait_measurement(struct rm3100_data *data)
|
||||
{
|
||||
struct regmap *regmap = data->regmap;
|
||||
unsigned int val;
|
||||
int tries = 20;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* A read cycle of 400kbits i2c bus is about 20us, plus the time
|
||||
* used for scheduling, a read cycle of fast mode of this device
|
||||
* can reach 1.7ms, it may be possible for data to arrive just
|
||||
* after we check the RM3100_REG_STATUS. In this case, irq_handler is
|
||||
* called before measuring_done is reinitialized, it will wait
|
||||
* forever for data that has already been ready.
|
||||
* Reinitialize measuring_done before looking up makes sure we
|
||||
* will always capture interrupt no matter when it happens.
|
||||
*/
|
||||
if (data->use_interrupt)
|
||||
reinit_completion(&data->measuring_done);
|
||||
|
||||
ret = regmap_read(regmap, RM3100_REG_STATUS, &val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if ((val & RM3100_STATUS_DRDY) != RM3100_STATUS_DRDY) {
|
||||
if (data->use_interrupt) {
|
||||
ret = wait_for_completion_timeout(&data->measuring_done,
|
||||
msecs_to_jiffies(data->conversion_time));
|
||||
if (!ret)
|
||||
return -ETIMEDOUT;
|
||||
} else {
|
||||
do {
|
||||
usleep_range(1000, 5000);
|
||||
|
||||
ret = regmap_read(regmap, RM3100_REG_STATUS,
|
||||
&val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (val & RM3100_STATUS_DRDY)
|
||||
break;
|
||||
} while (--tries);
|
||||
if (!tries)
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rm3100_read_mag(struct rm3100_data *data, int idx, int *val)
|
||||
{
|
||||
struct regmap *regmap = data->regmap;
|
||||
u8 buffer[3];
|
||||
int ret;
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
ret = regmap_write(regmap, RM3100_REG_POLL, BIT(4 + idx));
|
||||
if (ret < 0)
|
||||
goto unlock_return;
|
||||
|
||||
ret = rm3100_wait_measurement(data);
|
||||
if (ret < 0)
|
||||
goto unlock_return;
|
||||
|
||||
ret = regmap_bulk_read(regmap, RM3100_REG_MX2 + 3 * idx, buffer, 3);
|
||||
if (ret < 0)
|
||||
goto unlock_return;
|
||||
mutex_unlock(&data->lock);
|
||||
|
||||
*val = sign_extend32((buffer[0] << 16) | (buffer[1] << 8) | buffer[2],
|
||||
23);
|
||||
|
||||
return IIO_VAL_INT;
|
||||
|
||||
unlock_return:
|
||||
mutex_unlock(&data->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define RM3100_CHANNEL(axis, idx) \
|
||||
{ \
|
||||
.type = IIO_MAGN, \
|
||||
.modified = 1, \
|
||||
.channel2 = IIO_MOD_##axis, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ), \
|
||||
.scan_index = idx, \
|
||||
.scan_type = { \
|
||||
.sign = 's', \
|
||||
.realbits = 24, \
|
||||
.storagebits = 32, \
|
||||
.shift = 8, \
|
||||
.endianness = IIO_BE, \
|
||||
}, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec rm3100_channels[] = {
|
||||
RM3100_CHANNEL(X, 0),
|
||||
RM3100_CHANNEL(Y, 1),
|
||||
RM3100_CHANNEL(Z, 2),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(3),
|
||||
};
|
||||
|
||||
static IIO_CONST_ATTR_SAMP_FREQ_AVAIL(
|
||||
"600 300 150 75 37 18 9 4.5 2.3 1.2 0.6 0.3 0.015 0.075"
|
||||
);
|
||||
|
||||
static struct attribute *rm3100_attributes[] = {
|
||||
&iio_const_attr_sampling_frequency_available.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group rm3100_attribute_group = {
|
||||
.attrs = rm3100_attributes,
|
||||
};
|
||||
|
||||
#define RM3100_SAMP_NUM 14
|
||||
|
||||
/*
|
||||
* Frequency : rm3100_samp_rates[][0].rm3100_samp_rates[][1]Hz.
|
||||
* Time between reading: rm3100_sam_rates[][2]ms.
|
||||
* The first one is actually 1.7ms.
|
||||
*/
|
||||
static const int rm3100_samp_rates[RM3100_SAMP_NUM][3] = {
|
||||
{600, 0, 2}, {300, 0, 3}, {150, 0, 7}, {75, 0, 13}, {37, 0, 27},
|
||||
{18, 0, 55}, {9, 0, 110}, {4, 500000, 220}, {2, 300000, 440},
|
||||
{1, 200000, 800}, {0, 600000, 1600}, {0, 300000, 3300},
|
||||
{0, 15000, 6700}, {0, 75000, 13000}
|
||||
};
|
||||
|
||||
static int rm3100_get_samp_freq(struct rm3100_data *data, int *val, int *val2)
|
||||
{
|
||||
unsigned int tmp;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
ret = regmap_read(data->regmap, RM3100_REG_TMRC, &tmp);
|
||||
mutex_unlock(&data->lock);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = rm3100_samp_rates[tmp - RM3100_TMRC_OFFSET][0];
|
||||
*val2 = rm3100_samp_rates[tmp - RM3100_TMRC_OFFSET][1];
|
||||
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
}
|
||||
|
||||
static int rm3100_set_cycle_count(struct rm3100_data *data, int val)
|
||||
{
|
||||
int ret;
|
||||
u8 i;
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
ret = regmap_write(data->regmap, RM3100_REG_CC_X + 2 * i, val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* The scale of this sensor depends on the cycle count value, these
|
||||
* three values are corresponding to the cycle count value 50, 100,
|
||||
* 200. scale = output / gain * 10^4.
|
||||
*/
|
||||
switch (val) {
|
||||
case 50:
|
||||
data->scale = 500;
|
||||
break;
|
||||
case 100:
|
||||
data->scale = 263;
|
||||
break;
|
||||
/*
|
||||
* case 200:
|
||||
* This function will never be called by users' code, so here we
|
||||
* assume that it will never get a wrong parameter.
|
||||
*/
|
||||
default:
|
||||
data->scale = 133;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rm3100_set_samp_freq(struct iio_dev *indio_dev, int val, int val2)
|
||||
{
|
||||
struct rm3100_data *data = iio_priv(indio_dev);
|
||||
struct regmap *regmap = data->regmap;
|
||||
unsigned int cycle_count;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
/* All cycle count registers use the same value. */
|
||||
ret = regmap_read(regmap, RM3100_REG_CC_X, &cycle_count);
|
||||
if (ret < 0)
|
||||
goto unlock_return;
|
||||
|
||||
for (i = 0; i < RM3100_SAMP_NUM; i++) {
|
||||
if (val == rm3100_samp_rates[i][0] &&
|
||||
val2 == rm3100_samp_rates[i][1])
|
||||
break;
|
||||
}
|
||||
if (i == RM3100_SAMP_NUM) {
|
||||
ret = -EINVAL;
|
||||
goto unlock_return;
|
||||
}
|
||||
|
||||
ret = regmap_write(regmap, RM3100_REG_TMRC, i + RM3100_TMRC_OFFSET);
|
||||
if (ret < 0)
|
||||
goto unlock_return;
|
||||
|
||||
/* Checking if cycle count registers need changing. */
|
||||
if (val == 600 && cycle_count == 200) {
|
||||
ret = rm3100_set_cycle_count(data, 100);
|
||||
if (ret < 0)
|
||||
goto unlock_return;
|
||||
} else if (val != 600 && cycle_count == 100) {
|
||||
ret = rm3100_set_cycle_count(data, 200);
|
||||
if (ret < 0)
|
||||
goto unlock_return;
|
||||
}
|
||||
|
||||
if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED) {
|
||||
/* Writing TMRC registers requires CMM reset. */
|
||||
ret = regmap_write(regmap, RM3100_REG_CMM, 0);
|
||||
if (ret < 0)
|
||||
goto unlock_return;
|
||||
ret = regmap_write(data->regmap, RM3100_REG_CMM,
|
||||
(*indio_dev->active_scan_mask & 0x7) <<
|
||||
RM3100_CMM_AXIS_SHIFT | RM3100_CMM_START);
|
||||
if (ret < 0)
|
||||
goto unlock_return;
|
||||
}
|
||||
mutex_unlock(&data->lock);
|
||||
|
||||
data->conversion_time = rm3100_samp_rates[i][2] * 2;
|
||||
return 0;
|
||||
|
||||
unlock_return:
|
||||
mutex_unlock(&data->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rm3100_read_raw(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct rm3100_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = iio_device_claim_direct_mode(indio_dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = rm3100_read_mag(data, chan->scan_index, val);
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
|
||||
return ret;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = 0;
|
||||
*val2 = data->scale;
|
||||
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
return rm3100_get_samp_freq(data, val, val2);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int rm3100_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
return rm3100_set_samp_freq(indio_dev, val, val2);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct iio_info rm3100_info = {
|
||||
.attrs = &rm3100_attribute_group,
|
||||
.read_raw = rm3100_read_raw,
|
||||
.write_raw = rm3100_write_raw,
|
||||
};
|
||||
|
||||
static int rm3100_buffer_preenable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct rm3100_data *data = iio_priv(indio_dev);
|
||||
|
||||
/* Starting channels enabled. */
|
||||
return regmap_write(data->regmap, RM3100_REG_CMM,
|
||||
(*indio_dev->active_scan_mask & 0x7) << RM3100_CMM_AXIS_SHIFT |
|
||||
RM3100_CMM_START);
|
||||
}
|
||||
|
||||
static int rm3100_buffer_postdisable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct rm3100_data *data = iio_priv(indio_dev);
|
||||
|
||||
return regmap_write(data->regmap, RM3100_REG_CMM, 0);
|
||||
}
|
||||
|
||||
static const struct iio_buffer_setup_ops rm3100_buffer_ops = {
|
||||
.preenable = rm3100_buffer_preenable,
|
||||
.postenable = iio_triggered_buffer_postenable,
|
||||
.predisable = iio_triggered_buffer_predisable,
|
||||
.postdisable = rm3100_buffer_postdisable,
|
||||
};
|
||||
|
||||
static irqreturn_t rm3100_trigger_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
unsigned long scan_mask = *indio_dev->active_scan_mask;
|
||||
unsigned int mask_len = indio_dev->masklength;
|
||||
struct rm3100_data *data = iio_priv(indio_dev);
|
||||
struct regmap *regmap = data->regmap;
|
||||
int ret, i, bit;
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
switch (scan_mask) {
|
||||
case BIT(0) | BIT(1) | BIT(2):
|
||||
ret = regmap_bulk_read(regmap, RM3100_REG_MX2, data->buffer, 9);
|
||||
mutex_unlock(&data->lock);
|
||||
if (ret < 0)
|
||||
goto done;
|
||||
/* Convert XXXYYYZZZxxx to XXXxYYYxZZZx. x for paddings. */
|
||||
for (i = 2; i > 0; i--)
|
||||
memmove(data->buffer + i * 4, data->buffer + i * 3, 3);
|
||||
break;
|
||||
case BIT(0) | BIT(1):
|
||||
ret = regmap_bulk_read(regmap, RM3100_REG_MX2, data->buffer, 6);
|
||||
mutex_unlock(&data->lock);
|
||||
if (ret < 0)
|
||||
goto done;
|
||||
memmove(data->buffer + 4, data->buffer + 3, 3);
|
||||
break;
|
||||
case BIT(1) | BIT(2):
|
||||
ret = regmap_bulk_read(regmap, RM3100_REG_MY2, data->buffer, 6);
|
||||
mutex_unlock(&data->lock);
|
||||
if (ret < 0)
|
||||
goto done;
|
||||
memmove(data->buffer + 4, data->buffer + 3, 3);
|
||||
break;
|
||||
case BIT(0) | BIT(2):
|
||||
ret = regmap_bulk_read(regmap, RM3100_REG_MX2, data->buffer, 9);
|
||||
mutex_unlock(&data->lock);
|
||||
if (ret < 0)
|
||||
goto done;
|
||||
memmove(data->buffer + 4, data->buffer + 6, 3);
|
||||
break;
|
||||
default:
|
||||
for_each_set_bit(bit, &scan_mask, mask_len) {
|
||||
ret = regmap_bulk_read(regmap, RM3100_REG_MX2 + 3 * bit,
|
||||
data->buffer, 3);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&data->lock);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&data->lock);
|
||||
}
|
||||
/*
|
||||
* Always using the same buffer so that we wouldn't need to set the
|
||||
* paddings to 0 in case of leaking any data.
|
||||
*/
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
|
||||
pf->timestamp);
|
||||
done:
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
int rm3100_common_probe(struct device *dev, struct regmap *regmap, int irq)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct rm3100_data *data;
|
||||
unsigned int tmp;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
data = iio_priv(indio_dev);
|
||||
data->regmap = regmap;
|
||||
|
||||
mutex_init(&data->lock);
|
||||
|
||||
indio_dev->dev.parent = dev;
|
||||
indio_dev->name = "rm3100";
|
||||
indio_dev->info = &rm3100_info;
|
||||
indio_dev->channels = rm3100_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(rm3100_channels);
|
||||
indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_TRIGGERED;
|
||||
indio_dev->currentmode = INDIO_DIRECT_MODE;
|
||||
|
||||
if (!irq)
|
||||
data->use_interrupt = false;
|
||||
else {
|
||||
data->use_interrupt = true;
|
||||
|
||||
init_completion(&data->measuring_done);
|
||||
ret = devm_request_threaded_irq(dev,
|
||||
irq,
|
||||
rm3100_irq_handler,
|
||||
rm3100_thread_fn,
|
||||
IRQF_TRIGGER_HIGH |
|
||||
IRQF_ONESHOT,
|
||||
indio_dev->name,
|
||||
indio_dev);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "request irq line failed.\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
data->drdy_trig = devm_iio_trigger_alloc(dev, "%s-drdy%d",
|
||||
indio_dev->name,
|
||||
indio_dev->id);
|
||||
if (!data->drdy_trig)
|
||||
return -ENOMEM;
|
||||
|
||||
data->drdy_trig->dev.parent = dev;
|
||||
ret = devm_iio_trigger_register(dev, data->drdy_trig);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
|
||||
&iio_pollfunc_store_time,
|
||||
rm3100_trigger_handler,
|
||||
&rm3100_buffer_ops);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = regmap_read(regmap, RM3100_REG_TMRC, &tmp);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
/* Initializing max wait time, which is double conversion time. */
|
||||
data->conversion_time = rm3100_samp_rates[tmp - RM3100_TMRC_OFFSET][2]
|
||||
* 2;
|
||||
|
||||
/* Cycle count values may not be what we want. */
|
||||
if ((tmp - RM3100_TMRC_OFFSET) == 0)
|
||||
rm3100_set_cycle_count(data, 100);
|
||||
else
|
||||
rm3100_set_cycle_count(data, 200);
|
||||
|
||||
return devm_iio_device_register(dev, indio_dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rm3100_common_probe);
|
||||
|
||||
MODULE_AUTHOR("Song Qiang <songqiang1304521@gmail.com>");
|
||||
MODULE_DESCRIPTION("PNI RM3100 3-axis magnetometer i2c driver");
|
||||
MODULE_LICENSE("GPL v2");
|
54
drivers/iio/magnetometer/rm3100-i2c.c
Normal file
54
drivers/iio/magnetometer/rm3100-i2c.c
Normal file
@ -0,0 +1,54 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Support for PNI RM3100 3-axis geomagnetic sensor on a i2c bus.
|
||||
*
|
||||
* Copyright (C) 2018 Song Qiang <songqiang1304521@gmail.com>
|
||||
*
|
||||
* i2c slave address: 0x20 + SA1 << 1 + SA0.
|
||||
*/
|
||||
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include "rm3100.h"
|
||||
|
||||
static const struct regmap_config rm3100_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
|
||||
.rd_table = &rm3100_readable_table,
|
||||
.wr_table = &rm3100_writable_table,
|
||||
.volatile_table = &rm3100_volatile_table,
|
||||
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
};
|
||||
|
||||
static int rm3100_probe(struct i2c_client *client)
|
||||
{
|
||||
struct regmap *regmap;
|
||||
|
||||
regmap = devm_regmap_init_i2c(client, &rm3100_regmap_config);
|
||||
if (IS_ERR(regmap))
|
||||
return PTR_ERR(regmap);
|
||||
|
||||
return rm3100_common_probe(&client->dev, regmap, client->irq);
|
||||
}
|
||||
|
||||
static const struct of_device_id rm3100_dt_match[] = {
|
||||
{ .compatible = "pni,rm3100", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rm3100_dt_match);
|
||||
|
||||
static struct i2c_driver rm3100_driver = {
|
||||
.driver = {
|
||||
.name = "rm3100-i2c",
|
||||
.of_match_table = rm3100_dt_match,
|
||||
},
|
||||
.probe_new = rm3100_probe,
|
||||
};
|
||||
module_i2c_driver(rm3100_driver);
|
||||
|
||||
MODULE_AUTHOR("Song Qiang <songqiang1304521@gmail.com>");
|
||||
MODULE_DESCRIPTION("PNI RM3100 3-axis magnetometer i2c driver");
|
||||
MODULE_LICENSE("GPL v2");
|
64
drivers/iio/magnetometer/rm3100-spi.c
Normal file
64
drivers/iio/magnetometer/rm3100-spi.c
Normal file
@ -0,0 +1,64 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Support for PNI RM3100 3-axis geomagnetic sensor on a spi bus.
|
||||
*
|
||||
* Copyright (C) 2018 Song Qiang <songqiang1304521@gmail.com>
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#include "rm3100.h"
|
||||
|
||||
static const struct regmap_config rm3100_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
|
||||
.rd_table = &rm3100_readable_table,
|
||||
.wr_table = &rm3100_writable_table,
|
||||
.volatile_table = &rm3100_volatile_table,
|
||||
|
||||
.read_flag_mask = 0x80,
|
||||
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
};
|
||||
|
||||
static int rm3100_probe(struct spi_device *spi)
|
||||
{
|
||||
struct regmap *regmap;
|
||||
int ret;
|
||||
|
||||
/* Actually this device supports both mode 0 and mode 3. */
|
||||
spi->mode = SPI_MODE_0;
|
||||
/* Data rates cannot exceed 1Mbits. */
|
||||
spi->max_speed_hz = 1000000;
|
||||
spi->bits_per_word = 8;
|
||||
ret = spi_setup(spi);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
regmap = devm_regmap_init_spi(spi, &rm3100_regmap_config);
|
||||
if (IS_ERR(regmap))
|
||||
return PTR_ERR(regmap);
|
||||
|
||||
return rm3100_common_probe(&spi->dev, regmap, spi->irq);
|
||||
}
|
||||
|
||||
static const struct of_device_id rm3100_dt_match[] = {
|
||||
{ .compatible = "pni,rm3100", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rm3100_dt_match);
|
||||
|
||||
static struct spi_driver rm3100_driver = {
|
||||
.driver = {
|
||||
.name = "rm3100-spi",
|
||||
.of_match_table = rm3100_dt_match,
|
||||
},
|
||||
.probe = rm3100_probe,
|
||||
};
|
||||
module_spi_driver(rm3100_driver);
|
||||
|
||||
MODULE_AUTHOR("Song Qiang <songqiang1304521@gmail.com>");
|
||||
MODULE_DESCRIPTION("PNI RM3100 3-axis magnetometer spi driver");
|
||||
MODULE_LICENSE("GPL v2");
|
17
drivers/iio/magnetometer/rm3100.h
Normal file
17
drivers/iio/magnetometer/rm3100.h
Normal file
@ -0,0 +1,17 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (C) 2018 Song Qiang <songqiang1304521@gmail.com>
|
||||
*/
|
||||
|
||||
#ifndef RM3100_CORE_H
|
||||
#define RM3100_CORE_H
|
||||
|
||||
#include <linux/regmap.h>
|
||||
|
||||
extern const struct regmap_access_table rm3100_readable_table;
|
||||
extern const struct regmap_access_table rm3100_writable_table;
|
||||
extern const struct regmap_access_table rm3100_volatile_table;
|
||||
|
||||
int rm3100_common_probe(struct device *dev, struct regmap *regmap, int irq);
|
||||
|
||||
#endif /* RM3100_CORE_H */
|
@ -20,6 +20,7 @@
|
||||
#define LIS3MDL_MAGN_DEV_NAME "lis3mdl"
|
||||
#define LSM303AGR_MAGN_DEV_NAME "lsm303agr_magn"
|
||||
#define LIS2MDL_MAGN_DEV_NAME "lis2mdl"
|
||||
#define LSM9DS1_MAGN_DEV_NAME "lsm9ds1_magn"
|
||||
|
||||
int st_magn_common_probe(struct iio_dev *indio_dev);
|
||||
void st_magn_common_remove(struct iio_dev *indio_dev);
|
||||
|
@ -29,9 +29,9 @@
|
||||
#define ST_MAGN_NUMBER_DATA_CHANNELS 3
|
||||
|
||||
/* DEFAULT VALUE FOR SENSORS */
|
||||
#define ST_MAGN_DEFAULT_OUT_X_H_ADDR 0X03
|
||||
#define ST_MAGN_DEFAULT_OUT_Y_H_ADDR 0X07
|
||||
#define ST_MAGN_DEFAULT_OUT_Z_H_ADDR 0X05
|
||||
#define ST_MAGN_DEFAULT_OUT_X_H_ADDR 0x03
|
||||
#define ST_MAGN_DEFAULT_OUT_Y_H_ADDR 0x07
|
||||
#define ST_MAGN_DEFAULT_OUT_Z_H_ADDR 0x05
|
||||
|
||||
/* FULLSCALE */
|
||||
#define ST_MAGN_FS_AVL_1300MG 1300
|
||||
@ -267,6 +267,7 @@ static const struct st_sensor_settings st_magn_sensors_settings[] = {
|
||||
.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
|
||||
.sensors_supported = {
|
||||
[0] = LIS3MDL_MAGN_DEV_NAME,
|
||||
[1] = LSM9DS1_MAGN_DEV_NAME,
|
||||
},
|
||||
.ch = (struct iio_chan_spec *)st_magn_2_16bit_channels,
|
||||
.odr = {
|
||||
@ -315,6 +316,10 @@ static const struct st_sensor_settings st_magn_sensors_settings[] = {
|
||||
},
|
||||
},
|
||||
},
|
||||
.bdu = {
|
||||
.addr = 0x24,
|
||||
.mask = 0x40,
|
||||
},
|
||||
.drdy_irq = {
|
||||
/* drdy line is routed drdy pin */
|
||||
.stat_drdy = {
|
||||
|
@ -44,6 +44,10 @@ static const struct of_device_id st_magn_of_match[] = {
|
||||
.compatible = "st,lis2mdl",
|
||||
.data = LIS2MDL_MAGN_DEV_NAME,
|
||||
},
|
||||
{
|
||||
.compatible = "st,lsm9ds1-magn",
|
||||
.data = LSM9DS1_MAGN_DEV_NAME,
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, st_magn_of_match);
|
||||
@ -90,6 +94,7 @@ static const struct i2c_device_id st_magn_id_table[] = {
|
||||
{ LIS3MDL_MAGN_DEV_NAME },
|
||||
{ LSM303AGR_MAGN_DEV_NAME },
|
||||
{ LIS2MDL_MAGN_DEV_NAME },
|
||||
{ LSM9DS1_MAGN_DEV_NAME },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, st_magn_id_table);
|
||||
|
@ -23,6 +23,8 @@
|
||||
* For new single-chip sensors use <device_name> as compatible string.
|
||||
* For old single-chip devices keep <device_name>-magn to maintain
|
||||
* compatibility
|
||||
* For multi-chip devices, use <device_name>-magn to distinguish which
|
||||
* capability is being used
|
||||
*/
|
||||
static const struct of_device_id st_magn_of_match[] = {
|
||||
{
|
||||
@ -37,6 +39,10 @@ static const struct of_device_id st_magn_of_match[] = {
|
||||
.compatible = "st,lis2mdl",
|
||||
.data = LIS2MDL_MAGN_DEV_NAME,
|
||||
},
|
||||
{
|
||||
.compatible = "st,lsm9ds1-magn",
|
||||
.data = LSM9DS1_MAGN_DEV_NAME,
|
||||
},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, st_magn_of_match);
|
||||
@ -79,6 +85,7 @@ static const struct spi_device_id st_magn_id_table[] = {
|
||||
{ LIS3MDL_MAGN_DEV_NAME },
|
||||
{ LSM303AGR_MAGN_DEV_NAME },
|
||||
{ LIS2MDL_MAGN_DEV_NAME },
|
||||
{ LSM9DS1_MAGN_DEV_NAME },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, st_magn_id_table);
|
||||
|
@ -42,6 +42,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#define MCP4131_WRITE (0x00 << 2)
|
||||
@ -243,7 +244,7 @@ static int mcp4131_probe(struct spi_device *spi)
|
||||
{
|
||||
int err;
|
||||
struct device *dev = &spi->dev;
|
||||
unsigned long devid = spi_get_device_id(spi)->driver_data;
|
||||
unsigned long devid;
|
||||
struct mcp4131_data *data;
|
||||
struct iio_dev *indio_dev;
|
||||
|
||||
@ -254,7 +255,11 @@ static int mcp4131_probe(struct spi_device *spi)
|
||||
data = iio_priv(indio_dev);
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
data->spi = spi;
|
||||
data->cfg = &mcp4131_cfg[devid];
|
||||
data->cfg = of_device_get_match_data(&spi->dev);
|
||||
if (!data->cfg) {
|
||||
devid = spi_get_device_id(spi)->driver_data;
|
||||
data->cfg = &mcp4131_cfg[devid];
|
||||
}
|
||||
|
||||
mutex_init(&data->lock);
|
||||
|
||||
@ -273,7 +278,6 @@ static int mcp4131_probe(struct spi_device *spi)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_OF)
|
||||
static const struct of_device_id mcp4131_dt_ids[] = {
|
||||
{ .compatible = "microchip,mcp4131-502",
|
||||
.data = &mcp4131_cfg[MCP413x_502] },
|
||||
@ -406,7 +410,6 @@ static const struct of_device_id mcp4131_dt_ids[] = {
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mcp4131_dt_ids);
|
||||
#endif /* CONFIG_OF */
|
||||
|
||||
static const struct spi_device_id mcp4131_id[] = {
|
||||
{ "mcp4131-502", MCP413x_502 },
|
||||
|
@ -15,7 +15,7 @@
|
||||
|
||||
struct tpl0102_cfg {
|
||||
int wipers;
|
||||
int max_pos;
|
||||
int avail[3];
|
||||
int kohms;
|
||||
};
|
||||
|
||||
@ -28,16 +28,16 @@ enum tpl0102_type {
|
||||
|
||||
static const struct tpl0102_cfg tpl0102_cfg[] = {
|
||||
/* on-semiconductor parts */
|
||||
[CAT5140_503] = { .wipers = 1, .max_pos = 256, .kohms = 50, },
|
||||
[CAT5140_104] = { .wipers = 1, .max_pos = 256, .kohms = 100, },
|
||||
[CAT5140_503] = { .wipers = 1, .avail = { 0, 1, 255 }, .kohms = 50, },
|
||||
[CAT5140_104] = { .wipers = 1, .avail = { 0, 1, 255 }, .kohms = 100, },
|
||||
/* ti parts */
|
||||
[TPL0102_104] = { .wipers = 2, .max_pos = 256, .kohms = 100 },
|
||||
[TPL0401_103] = { .wipers = 1, .max_pos = 128, .kohms = 10, },
|
||||
[TPL0102_104] = { .wipers = 2, .avail = { 0, 1, 255 }, .kohms = 100 },
|
||||
[TPL0401_103] = { .wipers = 1, .avail = { 0, 1, 127 }, .kohms = 10, },
|
||||
};
|
||||
|
||||
struct tpl0102_data {
|
||||
struct regmap *regmap;
|
||||
unsigned long devid;
|
||||
const struct tpl0102_cfg *cfg;
|
||||
};
|
||||
|
||||
static const struct regmap_config tpl0102_regmap_config = {
|
||||
@ -52,6 +52,7 @@ static const struct regmap_config tpl0102_regmap_config = {
|
||||
.channel = (ch), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.info_mask_separate_available = BIT(IIO_CHAN_INFO_RAW), \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec tpl0102_channels[] = {
|
||||
@ -72,14 +73,32 @@ static int tpl0102_read_raw(struct iio_dev *indio_dev,
|
||||
return ret ? ret : IIO_VAL_INT;
|
||||
}
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = 1000 * tpl0102_cfg[data->devid].kohms;
|
||||
*val2 = tpl0102_cfg[data->devid].max_pos;
|
||||
*val = 1000 * data->cfg->kohms;
|
||||
*val2 = data->cfg->avail[2] + 1;
|
||||
return IIO_VAL_FRACTIONAL;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int tpl0102_read_avail(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
const int **vals, int *type, int *length,
|
||||
long mask)
|
||||
{
|
||||
struct tpl0102_data *data = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
*length = ARRAY_SIZE(data->cfg->avail);
|
||||
*vals = data->cfg->avail;
|
||||
*type = IIO_VAL_INT;
|
||||
return IIO_AVAIL_RANGE;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int tpl0102_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
@ -89,7 +108,7 @@ static int tpl0102_write_raw(struct iio_dev *indio_dev,
|
||||
if (mask != IIO_CHAN_INFO_RAW)
|
||||
return -EINVAL;
|
||||
|
||||
if (val >= tpl0102_cfg[data->devid].max_pos || val < 0)
|
||||
if (val > data->cfg->avail[2] || val < 0)
|
||||
return -EINVAL;
|
||||
|
||||
return regmap_write(data->regmap, chan->channel, val);
|
||||
@ -97,6 +116,7 @@ static int tpl0102_write_raw(struct iio_dev *indio_dev,
|
||||
|
||||
static const struct iio_info tpl0102_info = {
|
||||
.read_raw = tpl0102_read_raw,
|
||||
.read_avail = tpl0102_read_avail,
|
||||
.write_raw = tpl0102_write_raw,
|
||||
};
|
||||
|
||||
@ -113,7 +133,7 @@ static int tpl0102_probe(struct i2c_client *client,
|
||||
data = iio_priv(indio_dev);
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
|
||||
data->devid = id->driver_data;
|
||||
data->cfg = &tpl0102_cfg[id->driver_data];
|
||||
data->regmap = devm_regmap_init_i2c(client, &tpl0102_regmap_config);
|
||||
if (IS_ERR(data->regmap)) {
|
||||
dev_err(dev, "regmap initialization failed\n");
|
||||
@ -123,7 +143,7 @@ static int tpl0102_probe(struct i2c_client *client,
|
||||
indio_dev->dev.parent = dev;
|
||||
indio_dev->info = &tpl0102_info;
|
||||
indio_dev->channels = tpl0102_channels;
|
||||
indio_dev->num_channels = tpl0102_cfg[data->devid].wipers;
|
||||
indio_dev->num_channels = data->cfg->wipers;
|
||||
indio_dev->name = client->name;
|
||||
|
||||
return devm_iio_device_register(dev, indio_dev);
|
||||
|
@ -73,6 +73,7 @@ config AD7192
|
||||
config AD7280
|
||||
tristate "Analog Devices AD7280A Lithium Ion Battery Monitoring System"
|
||||
depends on SPI
|
||||
select CRC8
|
||||
help
|
||||
Say yes here to build support for Analog Devices AD7280A
|
||||
Lithium Ion Battery Monitoring System.
|
||||
|
@ -6,6 +6,7 @@
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/crc8.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
@ -121,8 +122,6 @@ static unsigned int ad7280a_devaddr(unsigned int addr)
|
||||
* P(x) = x^8 + x^5 + x^3 + x^2 + x^1 + x^0 = 0b100101111 => 0x2F
|
||||
*/
|
||||
#define POLYNOM 0x2F
|
||||
#define POLYNOM_ORDER 8
|
||||
#define HIGHBIT (1 << (POLYNOM_ORDER - 1))
|
||||
|
||||
struct ad7280_state {
|
||||
struct spi_device *spi;
|
||||
@ -131,7 +130,7 @@ struct ad7280_state {
|
||||
int slave_num;
|
||||
int scan_cnt;
|
||||
int readback_delay_us;
|
||||
unsigned char crc_tab[256];
|
||||
unsigned char crc_tab[CRC8_TABLE_SIZE];
|
||||
unsigned char ctrl_hb;
|
||||
unsigned char ctrl_lb;
|
||||
unsigned char cell_threshhigh;
|
||||
@ -144,23 +143,6 @@ struct ad7280_state {
|
||||
__be32 buf[2] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
static void ad7280_crc8_build_table(unsigned char *crc_tab)
|
||||
{
|
||||
unsigned char bit, crc;
|
||||
int cnt, i;
|
||||
|
||||
for (cnt = 0; cnt < 256; cnt++) {
|
||||
crc = cnt;
|
||||
for (i = 0; i < 8; i++) {
|
||||
bit = crc & HIGHBIT;
|
||||
crc <<= 1;
|
||||
if (bit)
|
||||
crc ^= POLYNOM;
|
||||
}
|
||||
crc_tab[cnt] = crc;
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned char ad7280_calc_crc8(unsigned char *crc_tab, unsigned int val)
|
||||
{
|
||||
unsigned char crc;
|
||||
@ -256,7 +238,9 @@ static int ad7280_read(struct ad7280_state *st, unsigned int devaddr,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
__ad7280_read32(st, &tmp);
|
||||
ret = __ad7280_read32(st, &tmp);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (ad7280_check_crc(st, tmp))
|
||||
return -EIO;
|
||||
@ -294,7 +278,9 @@ static int ad7280_read_channel(struct ad7280_state *st, unsigned int devaddr,
|
||||
|
||||
ad7280_delay(st);
|
||||
|
||||
__ad7280_read32(st, &tmp);
|
||||
ret = __ad7280_read32(st, &tmp);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (ad7280_check_crc(st, tmp))
|
||||
return -EIO;
|
||||
@ -327,7 +313,9 @@ static int ad7280_read_all_channels(struct ad7280_state *st, unsigned int cnt,
|
||||
ad7280_delay(st);
|
||||
|
||||
for (i = 0; i < cnt; i++) {
|
||||
__ad7280_read32(st, &tmp);
|
||||
ret = __ad7280_read32(st, &tmp);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (ad7280_check_crc(st, tmp))
|
||||
return -EIO;
|
||||
@ -342,6 +330,14 @@ static int ad7280_read_all_channels(struct ad7280_state *st, unsigned int cnt,
|
||||
return sum;
|
||||
}
|
||||
|
||||
static void ad7280_sw_power_down(void *data)
|
||||
{
|
||||
struct ad7280_state *st = data;
|
||||
|
||||
ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CONTROL_HB, 1,
|
||||
AD7280A_CTRL_HB_PWRDN_SW | st->ctrl_hb);
|
||||
}
|
||||
|
||||
static int ad7280_chain_setup(struct ad7280_state *st)
|
||||
{
|
||||
unsigned int val, n;
|
||||
@ -362,26 +358,38 @@ static int ad7280_chain_setup(struct ad7280_state *st)
|
||||
AD7280A_CTRL_LB_MUST_SET |
|
||||
st->ctrl_lb);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto error_power_down;
|
||||
|
||||
ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_READ, 1,
|
||||
AD7280A_CONTROL_LB << 2);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto error_power_down;
|
||||
|
||||
for (n = 0; n <= AD7280A_MAX_CHAIN; n++) {
|
||||
__ad7280_read32(st, &val);
|
||||
ret = __ad7280_read32(st, &val);
|
||||
if (ret)
|
||||
goto error_power_down;
|
||||
|
||||
if (val == 0)
|
||||
return n - 1;
|
||||
|
||||
if (ad7280_check_crc(st, val))
|
||||
return -EIO;
|
||||
if (ad7280_check_crc(st, val)) {
|
||||
ret = -EIO;
|
||||
goto error_power_down;
|
||||
}
|
||||
|
||||
if (n != ad7280a_devaddr(val >> 27))
|
||||
return -EIO;
|
||||
if (n != ad7280a_devaddr(val >> 27)) {
|
||||
ret = -EIO;
|
||||
goto error_power_down;
|
||||
}
|
||||
}
|
||||
ret = -EFAULT;
|
||||
|
||||
return -EFAULT;
|
||||
error_power_down:
|
||||
ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CONTROL_HB, 1,
|
||||
AD7280A_CTRL_HB_PWRDN_SW | st->ctrl_hb);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t ad7280_show_balance_sw(struct device *dev,
|
||||
@ -492,8 +500,8 @@ static int ad7280_channel_init(struct ad7280_state *st)
|
||||
{
|
||||
int dev, ch, cnt;
|
||||
|
||||
st->channels = kcalloc((st->slave_num + 1) * 12 + 2,
|
||||
sizeof(*st->channels), GFP_KERNEL);
|
||||
st->channels = devm_kcalloc(&st->spi->dev, (st->slave_num + 1) * 12 + 2,
|
||||
sizeof(*st->channels), GFP_KERNEL);
|
||||
if (!st->channels)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -552,16 +560,18 @@ static int ad7280_channel_init(struct ad7280_state *st)
|
||||
static int ad7280_attr_init(struct ad7280_state *st)
|
||||
{
|
||||
int dev, ch, cnt;
|
||||
unsigned int index;
|
||||
|
||||
st->iio_attr = kcalloc(2, sizeof(*st->iio_attr) *
|
||||
(st->slave_num + 1) * AD7280A_CELLS_PER_DEV,
|
||||
GFP_KERNEL);
|
||||
st->iio_attr = devm_kcalloc(&st->spi->dev, 2, sizeof(*st->iio_attr) *
|
||||
(st->slave_num + 1) * AD7280A_CELLS_PER_DEV,
|
||||
GFP_KERNEL);
|
||||
if (!st->iio_attr)
|
||||
return -ENOMEM;
|
||||
|
||||
for (dev = 0, cnt = 0; dev <= st->slave_num; dev++)
|
||||
for (ch = AD7280A_CELL_VOLTAGE_1; ch <= AD7280A_CELL_VOLTAGE_6;
|
||||
ch++, cnt++) {
|
||||
index = dev * AD7280A_CELLS_PER_DEV + ch;
|
||||
st->iio_attr[cnt].address =
|
||||
ad7280a_devaddr(dev) << 8 | ch;
|
||||
st->iio_attr[cnt].dev_attr.attr.mode =
|
||||
@ -571,10 +581,9 @@ static int ad7280_attr_init(struct ad7280_state *st)
|
||||
st->iio_attr[cnt].dev_attr.store =
|
||||
ad7280_store_balance_sw;
|
||||
st->iio_attr[cnt].dev_attr.attr.name =
|
||||
kasprintf(GFP_KERNEL,
|
||||
"in%d-in%d_balance_switch_en",
|
||||
dev * AD7280A_CELLS_PER_DEV + ch,
|
||||
dev * AD7280A_CELLS_PER_DEV + ch + 1);
|
||||
devm_kasprintf(&st->spi->dev, GFP_KERNEL,
|
||||
"in%d-in%d_balance_switch_en",
|
||||
index, index + 1);
|
||||
ad7280_attributes[cnt] =
|
||||
&st->iio_attr[cnt].dev_attr.attr;
|
||||
cnt++;
|
||||
@ -588,10 +597,9 @@ static int ad7280_attr_init(struct ad7280_state *st)
|
||||
st->iio_attr[cnt].dev_attr.store =
|
||||
ad7280_store_balance_timer;
|
||||
st->iio_attr[cnt].dev_attr.attr.name =
|
||||
kasprintf(GFP_KERNEL,
|
||||
"in%d-in%d_balance_timer",
|
||||
dev * AD7280A_CELLS_PER_DEV + ch,
|
||||
dev * AD7280A_CELLS_PER_DEV + ch + 1);
|
||||
devm_kasprintf(&st->spi->dev, GFP_KERNEL,
|
||||
"in%d-in%d_balance_timer",
|
||||
index, index + 1);
|
||||
ad7280_attributes[cnt] =
|
||||
&st->iio_attr[cnt].dev_attr.attr;
|
||||
}
|
||||
@ -610,7 +618,7 @@ static ssize_t ad7280_read_channel_config(struct device *dev,
|
||||
struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
|
||||
unsigned int val;
|
||||
|
||||
switch ((u32)this_attr->address) {
|
||||
switch (this_attr->address) {
|
||||
case AD7280A_CELL_OVERVOLTAGE:
|
||||
val = 1000 + (st->cell_threshhigh * 1568) / 100;
|
||||
break;
|
||||
@ -646,7 +654,7 @@ static ssize_t ad7280_write_channel_config(struct device *dev,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
switch ((u32)this_attr->address) {
|
||||
switch (this_attr->address) {
|
||||
case AD7280A_CELL_OVERVOLTAGE:
|
||||
case AD7280A_CELL_UNDERVOLTAGE:
|
||||
val = ((val - 1000) * 100) / 1568; /* LSB 15.68mV */
|
||||
@ -662,7 +670,7 @@ static ssize_t ad7280_write_channel_config(struct device *dev,
|
||||
val = clamp(val, 0L, 0xFFL);
|
||||
|
||||
mutex_lock(&st->lock);
|
||||
switch ((u32)this_attr->address) {
|
||||
switch (this_attr->address) {
|
||||
case AD7280A_CELL_OVERVOLTAGE:
|
||||
st->cell_threshhigh = val;
|
||||
break;
|
||||
@ -857,7 +865,7 @@ static int ad7280_probe(struct spi_device *spi)
|
||||
if (!pdata)
|
||||
pdata = &ad7793_default_pdata;
|
||||
|
||||
ad7280_crc8_build_table(st->crc_tab);
|
||||
crc8_populate_msb(st->crc_tab, POLYNOM);
|
||||
|
||||
st->spi->max_speed_hz = AD7280A_MAX_SPI_CLK_HZ;
|
||||
st->spi->mode = SPI_MODE_1;
|
||||
@ -872,6 +880,10 @@ static int ad7280_probe(struct spi_device *spi)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = devm_add_action_or_reset(&spi->dev, ad7280_sw_power_down, st);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
st->slave_num = ret;
|
||||
st->scan_cnt = (st->slave_num + 1) * AD7280A_NUM_CH;
|
||||
st->cell_threshhigh = 0xFF;
|
||||
@ -909,65 +921,37 @@ static int ad7280_probe(struct spi_device *spi)
|
||||
|
||||
ret = ad7280_attr_init(st);
|
||||
if (ret < 0)
|
||||
goto error_free_channels;
|
||||
return ret;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
ret = devm_iio_device_register(&spi->dev, indio_dev);
|
||||
if (ret)
|
||||
goto error_free_attr;
|
||||
return ret;
|
||||
|
||||
if (spi->irq > 0) {
|
||||
ret = ad7280_write(st, AD7280A_DEVADDR_MASTER,
|
||||
AD7280A_ALERT, 1,
|
||||
AD7280A_ALERT_RELAY_SIG_CHAIN_DOWN);
|
||||
if (ret)
|
||||
goto error_unregister;
|
||||
return ret;
|
||||
|
||||
ret = ad7280_write(st, ad7280a_devaddr(st->slave_num),
|
||||
AD7280A_ALERT, 0,
|
||||
AD7280A_ALERT_GEN_STATIC_HIGH |
|
||||
(pdata->chain_last_alert_ignore & 0xF));
|
||||
if (ret)
|
||||
goto error_unregister;
|
||||
return ret;
|
||||
|
||||
ret = request_threaded_irq(spi->irq,
|
||||
NULL,
|
||||
ad7280_event_handler,
|
||||
IRQF_TRIGGER_FALLING |
|
||||
IRQF_ONESHOT,
|
||||
indio_dev->name,
|
||||
indio_dev);
|
||||
ret = devm_request_threaded_irq(&spi->dev, spi->irq,
|
||||
NULL,
|
||||
ad7280_event_handler,
|
||||
IRQF_TRIGGER_FALLING |
|
||||
IRQF_ONESHOT,
|
||||
indio_dev->name,
|
||||
indio_dev);
|
||||
if (ret)
|
||||
goto error_unregister;
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
error_unregister:
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
error_free_attr:
|
||||
kfree(st->iio_attr);
|
||||
|
||||
error_free_channels:
|
||||
kfree(st->channels);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad7280_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct ad7280_state *st = iio_priv(indio_dev);
|
||||
|
||||
if (spi->irq > 0)
|
||||
free_irq(spi->irq, indio_dev);
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CONTROL_HB, 1,
|
||||
AD7280A_CTRL_HB_PWRDN_SW | st->ctrl_hb);
|
||||
|
||||
kfree(st->channels);
|
||||
kfree(st->iio_attr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -982,7 +966,6 @@ static struct spi_driver ad7280_driver = {
|
||||
.name = "ad7280",
|
||||
},
|
||||
.probe = ad7280_probe,
|
||||
.remove = ad7280_remove,
|
||||
.id_table = ad7280_id,
|
||||
};
|
||||
module_spi_driver(ad7280_driver);
|
||||
|
@ -374,7 +374,7 @@ static int ad7606_request_gpios(struct ad7606_state *st)
|
||||
return 0;
|
||||
|
||||
st->gpio_os = devm_gpiod_get_array_optional(dev, "oversampling-ratio",
|
||||
GPIOD_OUT_LOW);
|
||||
GPIOD_OUT_LOW);
|
||||
return PTR_ERR_OR_ZERO(st->gpio_os);
|
||||
}
|
||||
|
||||
|
@ -22,19 +22,28 @@
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/adc/ad_sigma_delta.h>
|
||||
|
||||
#define AD7780_RDY BIT(7)
|
||||
#define AD7780_FILTER BIT(6)
|
||||
#define AD7780_ERR BIT(5)
|
||||
#define AD7780_ID1 BIT(4)
|
||||
#define AD7780_ID0 BIT(3)
|
||||
#define AD7780_GAIN BIT(2)
|
||||
#define AD7780_PAT1 BIT(1)
|
||||
#define AD7780_PAT0 BIT(0)
|
||||
#define AD7780_RDY BIT(7)
|
||||
#define AD7780_FILTER BIT(6)
|
||||
#define AD7780_ERR BIT(5)
|
||||
#define AD7780_ID1 BIT(4)
|
||||
#define AD7780_ID0 BIT(3)
|
||||
#define AD7780_GAIN BIT(2)
|
||||
#define AD7780_PAT1 BIT(1)
|
||||
#define AD7780_PAT0 BIT(0)
|
||||
|
||||
#define AD7780_PATTERN (AD7780_PAT0)
|
||||
#define AD7780_PATTERN_MASK (AD7780_PAT0 | AD7780_PAT1)
|
||||
|
||||
#define AD7170_PAT2 BIT(2)
|
||||
|
||||
#define AD7170_PATTERN (AD7780_PAT0 | AD7170_PAT2)
|
||||
#define AD7170_PATTERN_MASK (AD7780_PAT0 | AD7780_PAT1 | AD7170_PAT2)
|
||||
|
||||
struct ad7780_chip_info {
|
||||
struct iio_chan_spec channel;
|
||||
unsigned int pattern_mask;
|
||||
unsigned int pattern;
|
||||
bool is_ad778x;
|
||||
};
|
||||
|
||||
struct ad7780_state {
|
||||
@ -42,7 +51,6 @@ struct ad7780_state {
|
||||
struct regulator *reg;
|
||||
struct gpio_desc *powerdown_gpio;
|
||||
unsigned int gain;
|
||||
u16 int_vref_mv;
|
||||
|
||||
struct ad_sigma_delta sd;
|
||||
};
|
||||
@ -87,16 +95,20 @@ static int ad7780_read_raw(struct iio_dev *indio_dev,
|
||||
long m)
|
||||
{
|
||||
struct ad7780_state *st = iio_priv(indio_dev);
|
||||
int voltage_uv;
|
||||
|
||||
switch (m) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
return ad_sigma_delta_single_conversion(indio_dev, chan, val);
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = st->int_vref_mv * st->gain;
|
||||
voltage_uv = regulator_get_voltage(st->reg);
|
||||
if (voltage_uv < 0)
|
||||
return voltage_uv;
|
||||
*val = (voltage_uv / 1000) * st->gain;
|
||||
*val2 = chan->scan_type.realbits - 1;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
*val -= (1 << (chan->scan_type.realbits - 1));
|
||||
*val = -(1 << (chan->scan_type.realbits - 1));
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
@ -113,10 +125,12 @@ static int ad7780_postprocess_sample(struct ad_sigma_delta *sigma_delta,
|
||||
((raw_sample & chip_info->pattern_mask) != chip_info->pattern))
|
||||
return -EIO;
|
||||
|
||||
if (raw_sample & AD7780_GAIN)
|
||||
st->gain = 1;
|
||||
else
|
||||
st->gain = 128;
|
||||
if (chip_info->is_ad778x) {
|
||||
if (raw_sample & AD7780_GAIN)
|
||||
st->gain = 1;
|
||||
else
|
||||
st->gain = 128;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -133,23 +147,27 @@ static const struct ad_sigma_delta_info ad7780_sigma_delta_info = {
|
||||
static const struct ad7780_chip_info ad7780_chip_info_tbl[] = {
|
||||
[ID_AD7170] = {
|
||||
.channel = AD7780_CHANNEL(12, 24),
|
||||
.pattern = 0x5,
|
||||
.pattern_mask = 0x7,
|
||||
.pattern = AD7170_PATTERN,
|
||||
.pattern_mask = AD7170_PATTERN_MASK,
|
||||
.is_ad778x = false,
|
||||
},
|
||||
[ID_AD7171] = {
|
||||
.channel = AD7780_CHANNEL(16, 24),
|
||||
.pattern = 0x5,
|
||||
.pattern_mask = 0x7,
|
||||
.pattern = AD7170_PATTERN,
|
||||
.pattern_mask = AD7170_PATTERN_MASK,
|
||||
.is_ad778x = false,
|
||||
},
|
||||
[ID_AD7780] = {
|
||||
.channel = AD7780_CHANNEL(24, 32),
|
||||
.pattern = 0x1,
|
||||
.pattern_mask = 0x3,
|
||||
.pattern = AD7780_PATTERN,
|
||||
.pattern_mask = AD7780_PATTERN_MASK,
|
||||
.is_ad778x = true,
|
||||
},
|
||||
[ID_AD7781] = {
|
||||
.channel = AD7780_CHANNEL(20, 32),
|
||||
.pattern = 0x1,
|
||||
.pattern_mask = 0x3,
|
||||
.pattern = AD7780_PATTERN,
|
||||
.pattern_mask = AD7780_PATTERN_MASK,
|
||||
.is_ad778x = true,
|
||||
},
|
||||
};
|
||||
|
||||
@ -161,7 +179,7 @@ static int ad7780_probe(struct spi_device *spi)
|
||||
{
|
||||
struct ad7780_state *st;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret, voltage_uv = 0;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
||||
if (!indio_dev)
|
||||
@ -181,16 +199,10 @@ static int ad7780_probe(struct spi_device *spi)
|
||||
dev_err(&spi->dev, "Failed to enable specified AVdd supply\n");
|
||||
return ret;
|
||||
}
|
||||
voltage_uv = regulator_get_voltage(st->reg);
|
||||
|
||||
st->chip_info =
|
||||
&ad7780_chip_info_tbl[spi_get_device_id(spi)->driver_data];
|
||||
|
||||
if (voltage_uv)
|
||||
st->int_vref_mv = voltage_uv / 1000;
|
||||
else
|
||||
dev_warn(&spi->dev, "Reference voltage unspecified\n");
|
||||
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
|
@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
@ -43,15 +43,22 @@
|
||||
*/
|
||||
|
||||
struct ad7816_chip_info {
|
||||
kernel_ulong_t id;
|
||||
struct spi_device *spi_dev;
|
||||
u16 rdwr_pin;
|
||||
u16 convert_pin;
|
||||
u16 busy_pin;
|
||||
struct gpio_desc *rdwr_pin;
|
||||
struct gpio_desc *convert_pin;
|
||||
struct gpio_desc *busy_pin;
|
||||
u8 oti_data[AD7816_CS_MAX + 1];
|
||||
u8 channel_id; /* 0 always be temperature */
|
||||
u8 mode;
|
||||
};
|
||||
|
||||
enum ad7816_type {
|
||||
ID_AD7816,
|
||||
ID_AD7817,
|
||||
ID_AD7818,
|
||||
};
|
||||
|
||||
/*
|
||||
* ad7816 data access by SPI
|
||||
*/
|
||||
@ -61,28 +68,30 @@ static int ad7816_spi_read(struct ad7816_chip_info *chip, u16 *data)
|
||||
int ret = 0;
|
||||
__be16 buf;
|
||||
|
||||
gpio_set_value(chip->rdwr_pin, 1);
|
||||
gpio_set_value(chip->rdwr_pin, 0);
|
||||
gpiod_set_value(chip->rdwr_pin, 1);
|
||||
gpiod_set_value(chip->rdwr_pin, 0);
|
||||
ret = spi_write(spi_dev, &chip->channel_id, sizeof(chip->channel_id));
|
||||
if (ret < 0) {
|
||||
dev_err(&spi_dev->dev, "SPI channel setting error\n");
|
||||
return ret;
|
||||
}
|
||||
gpio_set_value(chip->rdwr_pin, 1);
|
||||
gpiod_set_value(chip->rdwr_pin, 1);
|
||||
|
||||
if (chip->mode == AD7816_PD) { /* operating mode 2 */
|
||||
gpio_set_value(chip->convert_pin, 1);
|
||||
gpio_set_value(chip->convert_pin, 0);
|
||||
gpiod_set_value(chip->convert_pin, 1);
|
||||
gpiod_set_value(chip->convert_pin, 0);
|
||||
} else { /* operating mode 1 */
|
||||
gpio_set_value(chip->convert_pin, 0);
|
||||
gpio_set_value(chip->convert_pin, 1);
|
||||
gpiod_set_value(chip->convert_pin, 0);
|
||||
gpiod_set_value(chip->convert_pin, 1);
|
||||
}
|
||||
|
||||
while (gpio_get_value(chip->busy_pin))
|
||||
cpu_relax();
|
||||
if (chip->id == ID_AD7816 || chip->id == ID_AD7817) {
|
||||
while (gpiod_get_value(chip->busy_pin))
|
||||
cpu_relax();
|
||||
}
|
||||
|
||||
gpio_set_value(chip->rdwr_pin, 0);
|
||||
gpio_set_value(chip->rdwr_pin, 1);
|
||||
gpiod_set_value(chip->rdwr_pin, 0);
|
||||
gpiod_set_value(chip->rdwr_pin, 1);
|
||||
ret = spi_read(spi_dev, &buf, sizeof(*data));
|
||||
if (ret < 0) {
|
||||
dev_err(&spi_dev->dev, "SPI data read error\n");
|
||||
@ -99,8 +108,8 @@ static int ad7816_spi_write(struct ad7816_chip_info *chip, u8 data)
|
||||
struct spi_device *spi_dev = chip->spi_dev;
|
||||
int ret = 0;
|
||||
|
||||
gpio_set_value(chip->rdwr_pin, 1);
|
||||
gpio_set_value(chip->rdwr_pin, 0);
|
||||
gpiod_set_value(chip->rdwr_pin, 1);
|
||||
gpiod_set_value(chip->rdwr_pin, 0);
|
||||
ret = spi_write(spi_dev, &data, sizeof(data));
|
||||
if (ret < 0)
|
||||
dev_err(&spi_dev->dev, "SPI oti data write error\n");
|
||||
@ -129,10 +138,10 @@ static ssize_t ad7816_store_mode(struct device *dev,
|
||||
struct ad7816_chip_info *chip = iio_priv(indio_dev);
|
||||
|
||||
if (strcmp(buf, "full")) {
|
||||
gpio_set_value(chip->rdwr_pin, 1);
|
||||
gpiod_set_value(chip->rdwr_pin, 1);
|
||||
chip->mode = AD7816_FULL;
|
||||
} else {
|
||||
gpio_set_value(chip->rdwr_pin, 0);
|
||||
gpiod_set_value(chip->rdwr_pin, 0);
|
||||
chip->mode = AD7816_PD;
|
||||
}
|
||||
|
||||
@ -345,15 +354,9 @@ static int ad7816_probe(struct spi_device *spi_dev)
|
||||
{
|
||||
struct ad7816_chip_info *chip;
|
||||
struct iio_dev *indio_dev;
|
||||
unsigned short *pins = dev_get_platdata(&spi_dev->dev);
|
||||
int ret = 0;
|
||||
int i;
|
||||
|
||||
if (!pins) {
|
||||
dev_err(&spi_dev->dev, "No necessary GPIO platform data.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi_dev->dev, sizeof(*chip));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
@ -364,34 +367,33 @@ static int ad7816_probe(struct spi_device *spi_dev)
|
||||
chip->spi_dev = spi_dev;
|
||||
for (i = 0; i <= AD7816_CS_MAX; i++)
|
||||
chip->oti_data[i] = 203;
|
||||
chip->rdwr_pin = pins[0];
|
||||
chip->convert_pin = pins[1];
|
||||
chip->busy_pin = pins[2];
|
||||
|
||||
ret = devm_gpio_request(&spi_dev->dev, chip->rdwr_pin,
|
||||
spi_get_device_id(spi_dev)->name);
|
||||
if (ret) {
|
||||
dev_err(&spi_dev->dev, "Fail to request rdwr gpio PIN %d.\n",
|
||||
chip->rdwr_pin);
|
||||
chip->id = spi_get_device_id(spi_dev)->driver_data;
|
||||
chip->rdwr_pin = devm_gpiod_get(&spi_dev->dev, "rdwr", GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(chip->rdwr_pin)) {
|
||||
ret = PTR_ERR(chip->rdwr_pin);
|
||||
dev_err(&spi_dev->dev, "Failed to request rdwr GPIO: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
gpio_direction_input(chip->rdwr_pin);
|
||||
ret = devm_gpio_request(&spi_dev->dev, chip->convert_pin,
|
||||
spi_get_device_id(spi_dev)->name);
|
||||
if (ret) {
|
||||
dev_err(&spi_dev->dev, "Fail to request convert gpio PIN %d.\n",
|
||||
chip->convert_pin);
|
||||
chip->convert_pin = devm_gpiod_get(&spi_dev->dev, "convert",
|
||||
GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(chip->convert_pin)) {
|
||||
ret = PTR_ERR(chip->convert_pin);
|
||||
dev_err(&spi_dev->dev, "Failed to request convert GPIO: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
gpio_direction_input(chip->convert_pin);
|
||||
ret = devm_gpio_request(&spi_dev->dev, chip->busy_pin,
|
||||
spi_get_device_id(spi_dev)->name);
|
||||
if (ret) {
|
||||
dev_err(&spi_dev->dev, "Fail to request busy gpio PIN %d.\n",
|
||||
chip->busy_pin);
|
||||
return ret;
|
||||
if (chip->id == ID_AD7816 || chip->id == ID_AD7817) {
|
||||
chip->busy_pin = devm_gpiod_get(&spi_dev->dev, "busy",
|
||||
GPIOD_IN);
|
||||
if (IS_ERR(chip->busy_pin)) {
|
||||
ret = PTR_ERR(chip->busy_pin);
|
||||
dev_err(&spi_dev->dev, "Failed to request busy GPIO: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
gpio_direction_input(chip->busy_pin);
|
||||
|
||||
indio_dev->name = spi_get_device_id(spi_dev)->name;
|
||||
indio_dev->dev.parent = &spi_dev->dev;
|
||||
@ -420,10 +422,18 @@ static int ad7816_probe(struct spi_device *spi_dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id ad7816_of_match[] = {
|
||||
{ .compatible = "adi,ad7816", },
|
||||
{ .compatible = "adi,ad7817", },
|
||||
{ .compatible = "adi,ad7818", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ad7816_of_match);
|
||||
|
||||
static const struct spi_device_id ad7816_id[] = {
|
||||
{ "ad7816", 0 },
|
||||
{ "ad7817", 0 },
|
||||
{ "ad7818", 0 },
|
||||
{ "ad7816", ID_AD7816 },
|
||||
{ "ad7817", ID_AD7817 },
|
||||
{ "ad7818", ID_AD7818 },
|
||||
{}
|
||||
};
|
||||
|
||||
@ -432,6 +442,7 @@ MODULE_DEVICE_TABLE(spi, ad7816_id);
|
||||
static struct spi_driver ad7816_driver = {
|
||||
.driver = {
|
||||
.name = "ad7816",
|
||||
.of_match_table = ad7816_of_match,
|
||||
},
|
||||
.probe = ad7816_probe,
|
||||
.id_table = ad7816_id,
|
||||
|
@ -30,11 +30,17 @@ static int adt7316_i2c_read(void *client, u8 reg, u8 *data)
|
||||
}
|
||||
|
||||
ret = i2c_smbus_read_byte(client);
|
||||
|
||||
if (!ret)
|
||||
return -EIO;
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(&cl->dev, "I2C read error\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
*data = ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -31,6 +31,6 @@ extern const struct dev_pm_ops adt7316_pm_ops;
|
||||
#define ADT7316_PM_OPS NULL
|
||||
#endif
|
||||
int adt7316_probe(struct device *dev, struct adt7316_bus *bus,
|
||||
const char *name);
|
||||
const char *name);
|
||||
|
||||
#endif
|
||||
|
@ -102,18 +102,19 @@ static int ad7150_read_raw(struct iio_dev *indio_dev,
|
||||
{
|
||||
int ret;
|
||||
struct ad7150_chip_info *chip = iio_priv(indio_dev);
|
||||
int channel = chan->channel;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = i2c_smbus_read_word_data(chip->client,
|
||||
ad7150_addresses[chan->channel][0]);
|
||||
ad7150_addresses[channel][0]);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = swab16(ret);
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_AVERAGE_RAW:
|
||||
ret = i2c_smbus_read_word_data(chip->client,
|
||||
ad7150_addresses[chan->channel][1]);
|
||||
ad7150_addresses[channel][1]);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = swab16(ret);
|
||||
@ -182,8 +183,8 @@ static int ad7150_write_event_params(struct iio_dev *indio_dev,
|
||||
case IIO_EV_TYPE_THRESH:
|
||||
value = chip->threshold[rising][chan];
|
||||
return i2c_smbus_write_word_data(chip->client,
|
||||
ad7150_addresses[chan][3],
|
||||
swab16(value));
|
||||
ad7150_addresses[chan][3],
|
||||
swab16(value));
|
||||
case IIO_EV_TYPE_MAG_ADAPTIVE:
|
||||
sens = chip->mag_sensitivity[rising][chan];
|
||||
timeout = chip->mag_timeout[rising][chan];
|
||||
|
@ -84,13 +84,13 @@
|
||||
|
||||
/**
|
||||
* struct ad5933_platform_data - platform specific data
|
||||
* @ext_clk_Hz: the external clock frequency in Hz, if not set
|
||||
* @ext_clk_hz: the external clock frequency in Hz, if not set
|
||||
* the driver uses the internal clock (16.776 MHz)
|
||||
* @vref_mv: the external reference voltage in millivolt
|
||||
*/
|
||||
|
||||
struct ad5933_platform_data {
|
||||
unsigned long ext_clk_Hz;
|
||||
unsigned long ext_clk_hz;
|
||||
unsigned short vref_mv;
|
||||
};
|
||||
|
||||
@ -210,7 +210,7 @@ static int ad5933_set_freq(struct ad5933_state *st,
|
||||
u8 d8[4];
|
||||
} dat;
|
||||
|
||||
freqreg = (u64) freq * (u64) (1 << 27);
|
||||
freqreg = (u64)freq * (u64)(1 << 27);
|
||||
do_div(freqreg, st->mclk_hz / 4);
|
||||
|
||||
switch (reg) {
|
||||
@ -267,7 +267,6 @@ static void ad5933_calc_out_ranges(struct ad5933_state *st)
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
st->range_avail[i] = normalized_3v3[i] * st->vref_mv / 3300;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
@ -726,8 +725,8 @@ static int ad5933_probe(struct i2c_client *client,
|
||||
else
|
||||
st->vref_mv = pdata->vref_mv;
|
||||
|
||||
if (pdata->ext_clk_Hz) {
|
||||
st->mclk_hz = pdata->ext_clk_Hz;
|
||||
if (pdata->ext_clk_hz) {
|
||||
st->mclk_hz = pdata->ext_clk_hz;
|
||||
st->ctrl_lb = AD5933_CTRL_EXT_SYSCLK;
|
||||
} else {
|
||||
st->mclk_hz = AD5933_INT_OSC_FREQ_Hz;
|
||||
|
@ -15,12 +15,11 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include "ad2s1210.h"
|
||||
|
||||
#define DRV_NAME "ad2s1210"
|
||||
|
||||
@ -67,12 +66,33 @@ enum ad2s1210_mode {
|
||||
MOD_RESERVED,
|
||||
};
|
||||
|
||||
enum ad2s1210_gpios {
|
||||
AD2S1210_SAMPLE,
|
||||
AD2S1210_A0,
|
||||
AD2S1210_A1,
|
||||
AD2S1210_RES0,
|
||||
AD2S1210_RES1,
|
||||
};
|
||||
|
||||
struct ad2s1210_gpio {
|
||||
const char *name;
|
||||
unsigned long flags;
|
||||
};
|
||||
|
||||
static const struct ad2s1210_gpio gpios[] = {
|
||||
[AD2S1210_SAMPLE] = { .name = "adi,sample", .flags = GPIOD_OUT_LOW },
|
||||
[AD2S1210_A0] = { .name = "adi,a0", .flags = GPIOD_OUT_LOW },
|
||||
[AD2S1210_A1] = { .name = "adi,a1", .flags = GPIOD_OUT_LOW },
|
||||
[AD2S1210_RES0] = { .name = "adi,res0", .flags = GPIOD_OUT_LOW },
|
||||
[AD2S1210_RES1] = { .name = "adi,res1", .flags = GPIOD_OUT_LOW },
|
||||
};
|
||||
|
||||
static const unsigned int ad2s1210_resolution_value[] = { 10, 12, 14, 16 };
|
||||
|
||||
struct ad2s1210_state {
|
||||
const struct ad2s1210_platform_data *pdata;
|
||||
struct mutex lock;
|
||||
struct spi_device *sdev;
|
||||
struct gpio_desc *gpios[5];
|
||||
unsigned int fclkin;
|
||||
unsigned int fexcit;
|
||||
bool hysteresis;
|
||||
@ -91,8 +111,8 @@ static const int ad2s1210_mode_vals[4][2] = {
|
||||
static inline void ad2s1210_set_mode(enum ad2s1210_mode mode,
|
||||
struct ad2s1210_state *st)
|
||||
{
|
||||
gpio_set_value(st->pdata->a[0], ad2s1210_mode_vals[mode][0]);
|
||||
gpio_set_value(st->pdata->a[1], ad2s1210_mode_vals[mode][1]);
|
||||
gpiod_set_value(st->gpios[AD2S1210_A0], ad2s1210_mode_vals[mode][0]);
|
||||
gpiod_set_value(st->gpios[AD2S1210_A1], ad2s1210_mode_vals[mode][1]);
|
||||
st->mode = mode;
|
||||
}
|
||||
|
||||
@ -150,24 +170,16 @@ int ad2s1210_update_frequency_control_word(struct ad2s1210_state *st)
|
||||
return ad2s1210_config_write(st, fcw);
|
||||
}
|
||||
|
||||
static unsigned char ad2s1210_read_resolution_pin(struct ad2s1210_state *st)
|
||||
{
|
||||
int resolution = (gpio_get_value(st->pdata->res[0]) << 1) |
|
||||
gpio_get_value(st->pdata->res[1]);
|
||||
|
||||
return ad2s1210_resolution_value[resolution];
|
||||
}
|
||||
|
||||
static const int ad2s1210_res_pins[4][2] = {
|
||||
{ 0, 0 }, {0, 1}, {1, 0}, {1, 1}
|
||||
};
|
||||
|
||||
static inline void ad2s1210_set_resolution_pin(struct ad2s1210_state *st)
|
||||
{
|
||||
gpio_set_value(st->pdata->res[0],
|
||||
ad2s1210_res_pins[(st->resolution - 10) / 2][0]);
|
||||
gpio_set_value(st->pdata->res[1],
|
||||
ad2s1210_res_pins[(st->resolution - 10) / 2][1]);
|
||||
gpiod_set_value(st->gpios[AD2S1210_RES0],
|
||||
ad2s1210_res_pins[(st->resolution - 10) / 2][0]);
|
||||
gpiod_set_value(st->gpios[AD2S1210_RES1],
|
||||
ad2s1210_res_pins[(st->resolution - 10) / 2][1]);
|
||||
}
|
||||
|
||||
static inline int ad2s1210_soft_reset(struct ad2s1210_state *st)
|
||||
@ -301,15 +313,9 @@ static ssize_t ad2s1210_store_control(struct device *dev,
|
||||
"ad2s1210: write control register fail\n");
|
||||
goto error_ret;
|
||||
}
|
||||
st->resolution
|
||||
= ad2s1210_resolution_value[data & AD2S1210_SET_RESOLUTION];
|
||||
if (st->pdata->gpioin) {
|
||||
data = ad2s1210_read_resolution_pin(st);
|
||||
if (data != st->resolution)
|
||||
dev_warn(dev, "ad2s1210: resolution settings not match\n");
|
||||
} else {
|
||||
ad2s1210_set_resolution_pin(st);
|
||||
}
|
||||
st->resolution =
|
||||
ad2s1210_resolution_value[data & AD2S1210_SET_RESOLUTION];
|
||||
ad2s1210_set_resolution_pin(st);
|
||||
ret = len;
|
||||
st->hysteresis = !!(data & AD2S1210_ENABLE_HYSTERESIS);
|
||||
|
||||
@ -363,15 +369,9 @@ static ssize_t ad2s1210_store_resolution(struct device *dev,
|
||||
dev_err(dev, "ad2s1210: setting resolution fail\n");
|
||||
goto error_ret;
|
||||
}
|
||||
st->resolution
|
||||
= ad2s1210_resolution_value[data & AD2S1210_SET_RESOLUTION];
|
||||
if (st->pdata->gpioin) {
|
||||
data = ad2s1210_read_resolution_pin(st);
|
||||
if (data != st->resolution)
|
||||
dev_warn(dev, "ad2s1210: resolution settings not match\n");
|
||||
} else {
|
||||
ad2s1210_set_resolution_pin(st);
|
||||
}
|
||||
st->resolution =
|
||||
ad2s1210_resolution_value[data & AD2S1210_SET_RESOLUTION];
|
||||
ad2s1210_set_resolution_pin(st);
|
||||
ret = len;
|
||||
error_ret:
|
||||
mutex_unlock(&st->lock);
|
||||
@ -401,15 +401,15 @@ static ssize_t ad2s1210_clear_fault(struct device *dev,
|
||||
int ret;
|
||||
|
||||
mutex_lock(&st->lock);
|
||||
gpio_set_value(st->pdata->sample, 0);
|
||||
gpiod_set_value(st->gpios[AD2S1210_SAMPLE], 0);
|
||||
/* delay (2 * tck + 20) nano seconds */
|
||||
udelay(1);
|
||||
gpio_set_value(st->pdata->sample, 1);
|
||||
gpiod_set_value(st->gpios[AD2S1210_SAMPLE], 1);
|
||||
ret = ad2s1210_config_read(st, AD2S1210_REG_FAULT);
|
||||
if (ret < 0)
|
||||
goto error_ret;
|
||||
gpio_set_value(st->pdata->sample, 0);
|
||||
gpio_set_value(st->pdata->sample, 1);
|
||||
gpiod_set_value(st->gpios[AD2S1210_SAMPLE], 0);
|
||||
gpiod_set_value(st->gpios[AD2S1210_SAMPLE], 1);
|
||||
error_ret:
|
||||
mutex_unlock(&st->lock);
|
||||
|
||||
@ -466,7 +466,7 @@ static int ad2s1210_read_raw(struct iio_dev *indio_dev,
|
||||
s16 vel;
|
||||
|
||||
mutex_lock(&st->lock);
|
||||
gpio_set_value(st->pdata->sample, 0);
|
||||
gpiod_set_value(st->gpios[AD2S1210_SAMPLE], 0);
|
||||
/* delay (6 * tck + 20) nano seconds */
|
||||
udelay(1);
|
||||
|
||||
@ -512,7 +512,7 @@ static int ad2s1210_read_raw(struct iio_dev *indio_dev,
|
||||
}
|
||||
|
||||
error_ret:
|
||||
gpio_set_value(st->pdata->sample, 1);
|
||||
gpiod_set_value(st->gpios[AD2S1210_SAMPLE], 1);
|
||||
/* delay (2 * tck + 20) nano seconds */
|
||||
udelay(1);
|
||||
mutex_unlock(&st->lock);
|
||||
@ -592,10 +592,7 @@ static int ad2s1210_initial(struct ad2s1210_state *st)
|
||||
int ret;
|
||||
|
||||
mutex_lock(&st->lock);
|
||||
if (st->pdata->gpioin)
|
||||
st->resolution = ad2s1210_read_resolution_pin(st);
|
||||
else
|
||||
ad2s1210_set_resolution_pin(st);
|
||||
ad2s1210_set_resolution_pin(st);
|
||||
|
||||
ret = ad2s1210_config_write(st, AD2S1210_REG_CONTROL);
|
||||
if (ret < 0)
|
||||
@ -630,30 +627,22 @@ static const struct iio_info ad2s1210_info = {
|
||||
|
||||
static int ad2s1210_setup_gpios(struct ad2s1210_state *st)
|
||||
{
|
||||
unsigned long flags = st->pdata->gpioin ? GPIOF_DIR_IN : GPIOF_DIR_OUT;
|
||||
struct gpio ad2s1210_gpios[] = {
|
||||
{ st->pdata->sample, GPIOF_DIR_IN, "sample" },
|
||||
{ st->pdata->a[0], flags, "a0" },
|
||||
{ st->pdata->a[1], flags, "a1" },
|
||||
{ st->pdata->res[0], flags, "res0" },
|
||||
{ st->pdata->res[0], flags, "res1" },
|
||||
};
|
||||
struct spi_device *spi = st->sdev;
|
||||
int i, ret;
|
||||
|
||||
return gpio_request_array(ad2s1210_gpios, ARRAY_SIZE(ad2s1210_gpios));
|
||||
}
|
||||
for (i = 0; i < ARRAY_SIZE(gpios); i++) {
|
||||
st->gpios[i] = devm_gpiod_get(&spi->dev, gpios[i].name,
|
||||
gpios[i].flags);
|
||||
if (IS_ERR(st->gpios[i])) {
|
||||
ret = PTR_ERR(st->gpios[i]);
|
||||
dev_err(&spi->dev,
|
||||
"ad2s1210: failed to request %s GPIO: %d\n",
|
||||
gpios[i].name, ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
static void ad2s1210_free_gpios(struct ad2s1210_state *st)
|
||||
{
|
||||
unsigned long flags = st->pdata->gpioin ? GPIOF_DIR_IN : GPIOF_DIR_OUT;
|
||||
struct gpio ad2s1210_gpios[] = {
|
||||
{ st->pdata->sample, GPIOF_DIR_IN, "sample" },
|
||||
{ st->pdata->a[0], flags, "a0" },
|
||||
{ st->pdata->a[1], flags, "a1" },
|
||||
{ st->pdata->res[0], flags, "res0" },
|
||||
{ st->pdata->res[0], flags, "res1" },
|
||||
};
|
||||
|
||||
gpio_free_array(ad2s1210_gpios, ARRAY_SIZE(ad2s1210_gpios));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ad2s1210_probe(struct spi_device *spi)
|
||||
@ -669,7 +658,6 @@ static int ad2s1210_probe(struct spi_device *spi)
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
st = iio_priv(indio_dev);
|
||||
st->pdata = spi->dev.platform_data;
|
||||
ret = ad2s1210_setup_gpios(st);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
@ -692,7 +680,7 @@ static int ad2s1210_probe(struct spi_device *spi)
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto error_free_gpios;
|
||||
return ret;
|
||||
|
||||
st->fclkin = spi->max_speed_hz;
|
||||
spi->mode = SPI_MODE_3;
|
||||
@ -700,10 +688,6 @@ static int ad2s1210_probe(struct spi_device *spi)
|
||||
ad2s1210_initial(st);
|
||||
|
||||
return 0;
|
||||
|
||||
error_free_gpios:
|
||||
ad2s1210_free_gpios(st);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad2s1210_remove(struct spi_device *spi)
|
||||
@ -711,11 +695,16 @@ static int ad2s1210_remove(struct spi_device *spi)
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
ad2s1210_free_gpios(iio_priv(indio_dev));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id ad2s1210_of_match[] = {
|
||||
{ .compatible = "adi,ad2s1210", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ad2s1210_of_match);
|
||||
|
||||
static const struct spi_device_id ad2s1210_id[] = {
|
||||
{ "ad2s1210" },
|
||||
{}
|
||||
@ -725,6 +714,7 @@ MODULE_DEVICE_TABLE(spi, ad2s1210_id);
|
||||
static struct spi_driver ad2s1210_driver = {
|
||||
.driver = {
|
||||
.name = DRV_NAME,
|
||||
.of_match_table = of_match_ptr(ad2s1210_of_match),
|
||||
},
|
||||
.probe = ad2s1210_probe,
|
||||
.remove = ad2s1210_remove,
|
||||
|
@ -1,20 +0,0 @@
|
||||
/*
|
||||
* ad2s1210.h plaform data for the ADI Resolver to Digital Converters:
|
||||
* AD2S1210
|
||||
*
|
||||
* Copyright (c) 2010-2010 Analog Devices Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#ifndef _AD2S1210_H
|
||||
#define _AD2S1210_H
|
||||
|
||||
struct ad2s1210_platform_data {
|
||||
unsigned int sample;
|
||||
unsigned int a[2];
|
||||
unsigned int res[2];
|
||||
bool gpioin;
|
||||
};
|
||||
#endif /* _AD2S1210_H */
|
@ -34,16 +34,32 @@ static int ad2s90_read_raw(struct iio_dev *indio_dev,
|
||||
int ret;
|
||||
struct ad2s90_state *st = iio_priv(indio_dev);
|
||||
|
||||
mutex_lock(&st->lock);
|
||||
ret = spi_read(st->sdev, st->rx, 2);
|
||||
if (ret)
|
||||
goto error_ret;
|
||||
*val = (((u16)(st->rx[0])) << 4) | ((st->rx[1] & 0xF0) >> 4);
|
||||
if (chan->type != IIO_ANGL)
|
||||
return -EINVAL;
|
||||
|
||||
error_ret:
|
||||
mutex_unlock(&st->lock);
|
||||
switch (m) {
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
/* 2 * Pi / 2^12 */
|
||||
*val = 6283; /* mV */
|
||||
*val2 = 12;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
mutex_lock(&st->lock);
|
||||
ret = spi_read(st->sdev, st->rx, 2);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&st->lock);
|
||||
return ret;
|
||||
}
|
||||
*val = (((u16)(st->rx[0])) << 4) | ((st->rx[1] & 0xF0) >> 4);
|
||||
|
||||
return IIO_VAL_INT;
|
||||
mutex_unlock(&st->lock);
|
||||
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct iio_info ad2s90_info = {
|
||||
@ -54,14 +70,14 @@ static const struct iio_chan_spec ad2s90_chan = {
|
||||
.type = IIO_ANGL,
|
||||
.indexed = 1,
|
||||
.channel = 0,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
|
||||
};
|
||||
|
||||
static int ad2s90_probe(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct ad2s90_state *st;
|
||||
int ret = 0;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
||||
if (!indio_dev)
|
||||
@ -78,16 +94,17 @@ static int ad2s90_probe(struct spi_device *spi)
|
||||
indio_dev->num_channels = 1;
|
||||
indio_dev->name = spi_get_device_id(spi)->name;
|
||||
|
||||
ret = devm_iio_device_register(indio_dev->dev.parent, indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* need 600ns between CS and the first falling edge of SCLK */
|
||||
spi->max_speed_hz = 830000;
|
||||
spi->mode = SPI_MODE_3;
|
||||
spi_setup(spi);
|
||||
ret = spi_setup(spi);
|
||||
|
||||
return 0;
|
||||
if (ret < 0) {
|
||||
dev_err(&spi->dev, "spi_setup failed!\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return devm_iio_device_register(indio_dev->dev.parent, indio_dev);
|
||||
}
|
||||
|
||||
static const struct spi_device_id ad2s90_id[] = {
|
||||
|
@ -39,6 +39,8 @@ struct iio_dev;
|
||||
* if there is just one read-only sample data shift register.
|
||||
* @addr_shift: Shift of the register address in the communications register.
|
||||
* @read_mask: Mask for the communications register having the read bit set.
|
||||
* @data_reg: Address of the data register, if 0 the default address of 0x3 will
|
||||
* be used.
|
||||
*/
|
||||
struct ad_sigma_delta_info {
|
||||
int (*set_channel)(struct ad_sigma_delta *, unsigned int channel);
|
||||
@ -47,6 +49,7 @@ struct ad_sigma_delta_info {
|
||||
bool has_registers;
|
||||
unsigned int addr_shift;
|
||||
unsigned int read_mask;
|
||||
unsigned int data_reg;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -40,7 +40,7 @@
|
||||
#define ST_SENSORS_DEFAULT_STAT_ADDR 0x27
|
||||
|
||||
#define ST_SENSORS_MAX_NAME 17
|
||||
#define ST_SENSORS_MAX_4WAI 7
|
||||
#define ST_SENSORS_MAX_4WAI 8
|
||||
|
||||
#define ST_SENSORS_LSM_CHANNELS(device_type, mask, index, mod, \
|
||||
ch2, s, endian, rbits, sbits, addr) \
|
||||
|
@ -18,11 +18,13 @@
|
||||
* Accelerometer DRDY on LSM330 available only on pin 1 (see datasheet).
|
||||
* @open_drain: set the interrupt line to be open drain if possible.
|
||||
* @spi_3wire: enable spi-3wire mode.
|
||||
* @pullups: enable/disable i2c controller pullup resistors.
|
||||
*/
|
||||
struct st_sensors_platform_data {
|
||||
u8 drdy_int_pin;
|
||||
bool open_drain;
|
||||
bool spi_3wire;
|
||||
bool pullups;
|
||||
};
|
||||
|
||||
#endif /* ST_SENSORS_PDATA_H */
|
||||
|
@ -12,7 +12,7 @@ endif
|
||||
# (this improves performance and avoids hard-to-debug behaviour);
|
||||
MAKEFLAGS += -r
|
||||
|
||||
CFLAGS += -O2 -Wall -g -D_GNU_SOURCE -I$(OUTPUT)include
|
||||
override CFLAGS += -O2 -Wall -g -D_GNU_SOURCE -I$(OUTPUT)include
|
||||
|
||||
ALL_TARGETS := iio_event_monitor lsiio iio_generic_buffer
|
||||
ALL_PROGRAMS := $(patsubst %,$(OUTPUT)%,$(ALL_TARGETS))
|
||||
|
Loading…
Reference in New Issue
Block a user