First round of new driver, new functionality and cleanups for IIO in the 4.4 cycle
New device support * APDS9960 ALS + proximity driver * bmg160 SPI devices. * HDC100x humidity sensors * Holt HI-8435 threshold detector * mma8453Q accelerometer added to the mma8452 driver * mma86452FC and mma8653FC accelerometers added to the mma8452 driver * mxc4005 accelerometer * PulsedLight LIDAR * SensorTech VZ89x volatile organic compound sensor * UPISEMI uS5182d ALS and proximity sensors New core functionality * triggered events - use triggers to check for changes in threshold type detectors on devices with out interrupt support. First user is the holt comparator. * chemical concentration and resistance channel types. New driver functionality * vf610 - buffer support. - followup coccinelle warning fix. Core rework * buffers - break out callback buffer to own module. - move buffer implementations to a new subdirectory * percolate the error code form iio_event_getfd out to userspace rather than giving a missleading error later on. Cleanups * adddac drivers - use BIT macro where appropriate. * meter drivers - use BIT macro where appropriate. * ad7303 - add an OF match table to line up with the binding docs. * adc128s052 - add an OF match table to line up with the binding docs. * adf4350 - add an OF match table to line up with the binding docs * as3935 - add an OF match table to line up with the binding docs. * berlin2-adc - use GENMASK and BIT for masks - prevent attempting to sample multiple channels at once by moving a mutex scop - coding style cleanups * bmg150_magn - kconfig sort order was wrong - fix it. * bmg160 - use i2c regmap and drop all uses of i2c_client - separate i2c and core driver * cc10001_adc - kconfig sort order was wrong - fix it. * evgen (dummy driver helper module) - move interrupt generation to irq_work to reduce differences between the dummy driver and real hardware drivers. * hmc5843 - set the name dynamically rather than to a fixed value for one of the suported parts. - export module alias information to allow autoprobing of module. * lpc32xx - on failure to get resource or irq return -ENXIO as uppose to -EBUSY * max1027 - set .of_match_table to actually allow OF style matching. * max5821 - add MODULE_DEVICE_TABLE for OF table. * mma8452 - refactor to separate out chip specific data. - add freefall / motion interrupt source for devices that do their interrupts slightly differently. - update copywrite notice. - leave naming of events directory in sysfs to the core * mcp320x - set .of_match_table so that it can be use for OF style matching. * mlx90614 - Implement filter configuration (note the datasheet changed as a result of the driver reviews to include the values we needed ;) * opt3001 - drop .owner field as assigned by platform driver core. * si7020 - replace a bitmask on the humidity values with a more correct range check. * stk310 - improved error handling. - use BIT macro where appropriate and use the resulting defines instead of magic numbers in the code. - fix indentation * st-sensors - add debugfs register read hook * tsl4531 - fix error handling in check_id * twl6030 - fix module autoload for OF * iio-trig-sysfs - document add and remove attribute * trigger in staging - code alignment fixes. - braces on both branches of if statement if needed for one. * xilinx-xadc - push interrupts into hardirq context as there isn't much in them any more and it avoids breaking PREEMPT_RT builds due to the use of a spinlock between the hardirq and the thread. Tools * event-monitor - report unsupported events. We keep expanding what can come from drivers so give a helpful error if one turns up in an out of date userspace program. * generic-buffer - helpful message about needing to enable a channel to start the buffer. -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iQIcBAABCAAGBQJWBDeHAAoJEFSFNJnE9BaIgSQP/183S1WJTqpsrz3APZY4qQLU m8Jv6VCvtSENpryUxn6MtfPuVeBa8LwhciAtelsJfVvu2O902VAOcsgMhgp3cz7Y daTXio/XhdA7YJvuOkycLD3CdWJ7Gb3+nieBmcEb0YMAMIgchMR+gI/LYR/htISz Req9EM8C/4sU5hn3r92mVZFMiV5XWsApCv61A6IzXdaz4YgiEjtFb8NF87IPlFq0 Rpg3BG4/9Ka1tUaZs6a98sWXqudCzHSetKk9GLFbnhoo6xC+C33b06Z+fSinAZmn FsVGfQ/I6xhFJmCJLifLq/voN9gbDuebUQCPIYPfu7Oby29JcK95P60ZHM954s2M DGTCQ44GTReA2IqRvxlgQDU/TBUTwWKKDnuelw6+387t/wT0tnnfkwFrps1xPqGy k1qHVViofzf3iSKdDKeQyMjDoO+nMUv0DdkzYNz4xCPQIhGMRXA/Q+U81tJvJHZl rwhHVruFHyhI1/sTvTMJ/5+F7lL7LieFdCTVnzl0QblzMskGpLy5d/fMJn8OLVqa 6PiA5x3799U2o67wtCYH+RoSdQj1U+qEFkg7Oe+nVvlpbs/vfdR2dI3wYfp8459l mjafTgW8R+7tXYKU+iSKA4660QIN9LR6byBqyxX97mjyYxn3ml3VWJ44ztqfhjZd 1ZyZxsWa2y5b6L017kJN =E+jf -----END PGP SIGNATURE----- Merge tag 'iio-for-4.4a' of git://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio into staging-next Jonathan writes: First round of new driver, new functionality and cleanups for IIO in the 4.4 cycle New device support * APDS9960 ALS + proximity driver * bmg160 SPI devices. * HDC100x humidity sensors * Holt HI-8435 threshold detector * mma8453Q accelerometer added to the mma8452 driver * mma86452FC and mma8653FC accelerometers added to the mma8452 driver * mxc4005 accelerometer * PulsedLight LIDAR * SensorTech VZ89x volatile organic compound sensor * UPISEMI uS5182d ALS and proximity sensors New core functionality * triggered events - use triggers to check for changes in threshold type detectors on devices with out interrupt support. First user is the holt comparator. * chemical concentration and resistance channel types. New driver functionality * vf610 - buffer support. - followup coccinelle warning fix. Core rework * buffers - break out callback buffer to own module. - move buffer implementations to a new subdirectory * percolate the error code form iio_event_getfd out to userspace rather than giving a missleading error later on. Cleanups * adddac drivers - use BIT macro where appropriate. * meter drivers - use BIT macro where appropriate. * ad7303 - add an OF match table to line up with the binding docs. * adc128s052 - add an OF match table to line up with the binding docs. * adf4350 - add an OF match table to line up with the binding docs * as3935 - add an OF match table to line up with the binding docs. * berlin2-adc - use GENMASK and BIT for masks - prevent attempting to sample multiple channels at once by moving a mutex scop - coding style cleanups * bmg150_magn - kconfig sort order was wrong - fix it. * bmg160 - use i2c regmap and drop all uses of i2c_client - separate i2c and core driver * cc10001_adc - kconfig sort order was wrong - fix it. * evgen (dummy driver helper module) - move interrupt generation to irq_work to reduce differences between the dummy driver and real hardware drivers. * hmc5843 - set the name dynamically rather than to a fixed value for one of the suported parts. - export module alias information to allow autoprobing of module. * lpc32xx - on failure to get resource or irq return -ENXIO as uppose to -EBUSY * max1027 - set .of_match_table to actually allow OF style matching. * max5821 - add MODULE_DEVICE_TABLE for OF table. * mma8452 - refactor to separate out chip specific data. - add freefall / motion interrupt source for devices that do their interrupts slightly differently. - update copywrite notice. - leave naming of events directory in sysfs to the core * mcp320x - set .of_match_table so that it can be use for OF style matching. * mlx90614 - Implement filter configuration (note the datasheet changed as a result of the driver reviews to include the values we needed ;) * opt3001 - drop .owner field as assigned by platform driver core. * si7020 - replace a bitmask on the humidity values with a more correct range check. * stk310 - improved error handling. - use BIT macro where appropriate and use the resulting defines instead of magic numbers in the code. - fix indentation * st-sensors - add debugfs register read hook * tsl4531 - fix error handling in check_id * twl6030 - fix module autoload for OF * iio-trig-sysfs - document add and remove attribute * trigger in staging - code alignment fixes. - braces on both branches of if statement if needed for one. * xilinx-xadc - push interrupts into hardirq context as there isn't much in them any more and it avoids breaking PREEMPT_RT builds due to the use of a spinlock between the hardirq and the thread. Tools * event-monitor - report unsupported events. We keep expanding what can come from drivers so give a helpful error if one turns up in an out of date userspace program. * generic-buffer - helpful message about needing to enable a channel to start the buffer.
This commit is contained in:
commit
9f827d8099
@ -581,6 +581,7 @@ What: /sys/.../iio:deviceX/events/in_voltageY_supply_thresh_rising_en
|
||||
What: /sys/.../iio:deviceX/events/in_voltageY_supply_thresh_falling_en
|
||||
What: /sys/.../iio:deviceX/events/in_voltageY_thresh_rising_en
|
||||
What: /sys/.../iio:deviceX/events/in_voltageY_thresh_falling_en
|
||||
What: /sys/.../iio:deviceX/events/in_voltageY_thresh_either_en
|
||||
What: /sys/.../iio:deviceX/events/in_tempY_thresh_rising_en
|
||||
What: /sys/.../iio:deviceX/events/in_tempY_thresh_falling_en
|
||||
KernelVersion: 2.6.37
|
||||
@ -1459,3 +1460,22 @@ Description:
|
||||
measurements and return the average value as output data. Each
|
||||
value resulted from <type>[_name]_oversampling_ratio measurements
|
||||
is considered as one sample for <type>[_name]_sampling_frequency.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_concentration_raw
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_concentrationX_raw
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_concentration_co2_raw
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_concentrationX_co2_raw
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_concentration_voc_raw
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_concentrationX_voc_raw
|
||||
KernelVersion: 4.3
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Raw (unscaled no offset etc.) percentage reading of a substance.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_resistance_raw
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_resistanceX_raw
|
||||
KernelVersion: 4.3
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Raw (unscaled no offset etc.) resistance reading that can be processed
|
||||
into an ohm value.
|
||||
|
43
Documentation/ABI/testing/sysfs-bus-iio-adc-hi8435
Normal file
43
Documentation/ABI/testing/sysfs-bus-iio-adc-hi8435
Normal file
@ -0,0 +1,43 @@
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_sensing_mode
|
||||
Date: August 2015
|
||||
KernelVersion: 4.2.0
|
||||
Contact: source@cogentembedded.com
|
||||
Description:
|
||||
Program sensor type for threshold detector inputs.
|
||||
Could be either "GND-Open" or "Supply-Open" mode. Y is a
|
||||
threshold detector input channel. Channels 0..7, 8..15, 16..23
|
||||
and 24..31 has common sensor types.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/events/in_voltageY_thresh_falling_value
|
||||
Date: August 2015
|
||||
KernelVersion: 4.2.0
|
||||
Contact: source@cogentembedded.com
|
||||
Description:
|
||||
Channel Y low voltage threshold. If sensor input voltage goes lower then
|
||||
this value then the threshold falling event is pushed.
|
||||
Depending on in_voltageY_sensing_mode the low voltage threshold
|
||||
is separately set for "GND-Open" and "Supply-Open" modes.
|
||||
Channels 0..31 have common low threshold values, but could have different
|
||||
sensing_modes.
|
||||
The low voltage threshold range is between 2..21V.
|
||||
Hysteresis between low and high thresholds can not be lower then 2 and
|
||||
can not be odd.
|
||||
If falling threshold results hysteresis to odd value then rising
|
||||
threshold is automatically subtracted by one.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/events/in_voltageY_thresh_rising_value
|
||||
Date: August 2015
|
||||
KernelVersion: 4.2.0
|
||||
Contact: source@cogentembedded.com
|
||||
Description:
|
||||
Channel Y high voltage threshold. If sensor input voltage goes higher then
|
||||
this value then the threshold rising event is pushed.
|
||||
Depending on in_voltageY_sensing_mode the high voltage threshold
|
||||
is separately set for "GND-Open" and "Supply-Open" modes.
|
||||
Channels 0..31 have common high threshold values, but could have different
|
||||
sensing_modes.
|
||||
The high voltage threshold range is between 3..22V.
|
||||
Hysteresis between low and high thresholds can not be lower then 2 and
|
||||
can not be odd.
|
||||
If rising threshold results hysteresis to odd value then falling
|
||||
threshold is automatically appended by one.
|
7
Documentation/ABI/testing/sysfs-bus-iio-chemical-vz89x
Normal file
7
Documentation/ABI/testing/sysfs-bus-iio-chemical-vz89x
Normal file
@ -0,0 +1,7 @@
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_concentration_VOC_short_raw
|
||||
Date: September 2015
|
||||
KernelVersion: 4.3
|
||||
Contact: Matt Ranostay <mranostay@gmail.com>
|
||||
Description:
|
||||
Get the raw calibration VOC value from the sensor.
|
||||
This value has little application outside of calibration.
|
9
Documentation/ABI/testing/sysfs-bus-iio-humidity-hdc100x
Normal file
9
Documentation/ABI/testing/sysfs-bus-iio-humidity-hdc100x
Normal file
@ -0,0 +1,9 @@
|
||||
What: /sys/bus/iio/devices/iio:deviceX/out_current_heater_raw
|
||||
What: /sys/bus/iio/devices/iio:deviceX/out_current_heater_raw_available
|
||||
KernelVersion: 4.3
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Controls the heater device within the humidity sensor to get
|
||||
rid of excess condensation.
|
||||
|
||||
Valid control values are 0 = OFF, and 1 = ON.
|
@ -18,3 +18,25 @@ Description:
|
||||
trigger. In order to associate the trigger with an IIO device
|
||||
one should write this name string to
|
||||
/sys/bus/iio/devices/iio:deviceY/trigger/current_trigger.
|
||||
|
||||
What: /sys/bus/iio/devices/iio_sysfs_trigger/add_trigger
|
||||
KernelVersion: 2.6.39
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
This attribute is provided by the iio-trig-sysfs stand-alone
|
||||
driver and it is used to activate the creation of a new trigger.
|
||||
In order to achieve this, one should write a positive integer
|
||||
into the associated file, which will serve as the id of the
|
||||
trigger. If the trigger with the specified id is already present
|
||||
in the system, an invalid argument message will be returned.
|
||||
|
||||
What: /sys/bus/iio/devices/iio_sysfs_trigger/remove_trigger
|
||||
KernelVersion: 2.6.39
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
This attribute is used to unregister and delete a previously
|
||||
created trigger from the list of available triggers. In order to
|
||||
achieve this, one should write a positive integer into the
|
||||
associated file, representing the id of the trigger that needs
|
||||
to be removed. If the trigger can't be found, an invalid
|
||||
argument message will be returned to the user.
|
||||
|
@ -578,7 +578,7 @@
|
||||
work together.
|
||||
</para>
|
||||
<sect2 id="iiotrigbufsetup"> <title> IIO triggered buffer setup</title>
|
||||
!Edrivers/iio/industrialio-triggered-buffer.c
|
||||
!Edrivers/iio/buffer/industrialio-triggered-buffer.c
|
||||
!Finclude/linux/iio/iio.h iio_buffer_setup_ops
|
||||
|
||||
|
||||
|
@ -54,7 +54,6 @@ epson,rx8581 I2C-BUS INTERFACE REAL TIME CLOCK MODULE
|
||||
fsl,mag3110 MAG3110: Xtrinsic High Accuracy, 3D Magnetometer
|
||||
fsl,mc13892 MC13892: Power Management Integrated Circuit (PMIC) for i.MX35/51
|
||||
fsl,mma8450 MMA8450Q: Xtrinsic Low-power, 3-axis Xtrinsic Accelerometer
|
||||
fsl,mma8452 MMA8452Q: 3-axis 12-bit / 8-bit Digital Accelerometer
|
||||
fsl,mpr121 MPR121: Proximity Capacitive Touch Sensor Controller
|
||||
fsl,sgtl5000 SGTL5000: Ultra Low-Power Audio Codec
|
||||
gmt,g751 G751: Digital Temperature Sensor and Thermal Watchdog with Two-Wire Interface
|
||||
@ -80,6 +79,7 @@ oki,ml86v7667 OKI ML86V7667 video decoder
|
||||
ovti,ov5642 OV5642: Color CMOS QSXGA (5-megapixel) Image Sensor with OmniBSI and Embedded TrueFocus
|
||||
pericom,pt7c4338 Real-time Clock Module
|
||||
plx,pex8648 48-Lane, 12-Port PCI Express Gen 2 (5.0 GT/s) Switch
|
||||
pulsedlight,lidar-lite-v2 Pulsedlight LIDAR range-finding sensor
|
||||
ramtron,24c64 i2c serial eeprom (24cxx)
|
||||
ricoh,r2025sd I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC
|
||||
ricoh,r2221tl I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC
|
||||
@ -88,6 +88,7 @@ ricoh,rs5c372b I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC
|
||||
ricoh,rv5c386 I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC
|
||||
ricoh,rv5c387a I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC
|
||||
samsung,24ad0xd1 S524AD0XF1 (128K/256K-bit Serial EEPROM for Low Power)
|
||||
sgx,vz89x SGX Sensortech VZ89X Sensors
|
||||
sii,s35390a 2-wire CMOS real-time clock
|
||||
skyworks,sky81452 Skyworks SKY81452: Six-Channel White LED Driver with Touch Panel Bias Supply
|
||||
st-micro,24c256 i2c serial eeprom (24cxx)
|
||||
|
24
Documentation/devicetree/bindings/iio/accel/mma8452.txt
Normal file
24
Documentation/devicetree/bindings/iio/accel/mma8452.txt
Normal file
@ -0,0 +1,24 @@
|
||||
Freescale MMA8452Q, MMA8453Q, MMA8652FC or MMA8653FC triaxial accelerometer
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: should contain one of
|
||||
* "fsl,mma8452"
|
||||
* "fsl,mma8453"
|
||||
* "fsl,mma8652"
|
||||
* "fsl,mma8653"
|
||||
- reg: the I2C address of the chip
|
||||
|
||||
Optional properties:
|
||||
|
||||
- interrupt-parent: should be the phandle for the interrupt controller
|
||||
- interrupts: interrupt mapping for GPIO IRQ
|
||||
|
||||
Example:
|
||||
|
||||
mma8453fc@1d {
|
||||
compatible = "fsl,mma8453";
|
||||
reg = <0x1d>;
|
||||
interrupt-parent = <&gpio1>;
|
||||
interrupts = <5 0>;
|
||||
};
|
21
Documentation/devicetree/bindings/iio/adc/hi8435.txt
Normal file
21
Documentation/devicetree/bindings/iio/adc/hi8435.txt
Normal file
@ -0,0 +1,21 @@
|
||||
Holt Integrated Circuits HI-8435 threshold detector bindings
|
||||
|
||||
Required properties:
|
||||
- compatible: should be "holt,hi8435"
|
||||
- reg: spi chip select number for the device
|
||||
|
||||
Recommended properties:
|
||||
- spi-max-frequency: definition as per
|
||||
Documentation/devicetree/bindings/spi/spi-bus.txt
|
||||
|
||||
Optional properties:
|
||||
- gpios: GPIO used for controlling the reset pin
|
||||
|
||||
Example:
|
||||
sensor@0 {
|
||||
compatible = "holt,hi8435";
|
||||
reg = <0>;
|
||||
gpios = <&gpio6 1 0>;
|
||||
|
||||
spi-max-frequency = <1000000>;
|
||||
};
|
22
Documentation/devicetree/bindings/iio/light/apds9960.txt
Normal file
22
Documentation/devicetree/bindings/iio/light/apds9960.txt
Normal file
@ -0,0 +1,22 @@
|
||||
* Avago APDS9960 gesture/RGB/ALS/proximity sensor
|
||||
|
||||
http://www.avagotech.com/docs/AV02-4191EN
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: must be "avago,apds9960"
|
||||
- reg: the I2c address of the sensor
|
||||
- interrupt-parent: should be the phandle for the interrupt controller
|
||||
- interrupts : the sole interrupt generated by the device
|
||||
|
||||
Refer to interrupt-controller/interrupts.txt for generic interrupt client
|
||||
node bindings.
|
||||
|
||||
Example:
|
||||
|
||||
apds9960@39 {
|
||||
compatible = "avago,apds9960";
|
||||
reg = <0x39>;
|
||||
interrupt-parent = <&gpio1>;
|
||||
interrupts = <16 1>;
|
||||
};
|
34
Documentation/devicetree/bindings/iio/light/us5182d.txt
Normal file
34
Documentation/devicetree/bindings/iio/light/us5182d.txt
Normal file
@ -0,0 +1,34 @@
|
||||
* UPISEMI us5182d I2C ALS and Proximity sensor
|
||||
|
||||
Required properties:
|
||||
- compatible: must be "upisemi,usd5182"
|
||||
- reg: the I2C address of the device
|
||||
|
||||
Optional properties:
|
||||
- upisemi,glass-coef: glass attenuation factor - compensation factor of
|
||||
resolution 1000 for material transmittance.
|
||||
- upisemi,dark-ths: array of 8 elements containing 16-bit thresholds (adc
|
||||
counts) corresponding to every scale.
|
||||
- upisemi,upper-dark-gain: 8-bit dark gain compensation factor(4 int and 4
|
||||
fractional bits - Q4.4) applied when light > threshold
|
||||
- upisemi,lower-dark-gain: 8-bit dark gain compensation factor(4 int and 4
|
||||
fractional bits - Q4.4) applied when light < threshold
|
||||
|
||||
If the optional properties are not specified these factors will default to the
|
||||
values in the below example.
|
||||
The glass-coef defaults to no compensation for the covering material.
|
||||
The threshold array defaults to experimental values that work with US5182D
|
||||
sensor on evaluation board - roughly between 12-32 lux.
|
||||
There will be no dark-gain compensation by default when ALS > thresh
|
||||
(0 * dark-gain), and a 1.35 compensation factor when ALS < thresh.
|
||||
|
||||
Example:
|
||||
|
||||
usd5182@39 {
|
||||
compatible = "upisemi,usd5182";
|
||||
reg = <0x39>;
|
||||
upisemi,glass-coef = < 1000 >;
|
||||
upisemi,dark-ths = /bits/ 16 <170 200 512 512 800 2000 4000 8000>;
|
||||
upisemi,upper-dark-gain = /bits/ 8 <0x00>;
|
||||
upisemi,lower-dark-gain = /bits/ 8 <0x16>;
|
||||
};
|
@ -101,6 +101,7 @@ himax Himax Technologies, Inc.
|
||||
hisilicon Hisilicon Limited.
|
||||
hit Hitachi Ltd.
|
||||
hitex Hitex Development Tools
|
||||
holt Holt Integrated Circuits, Inc.
|
||||
honeywell Honeywell
|
||||
hp Hewlett Packard
|
||||
i2se I2SE GmbH
|
||||
@ -169,6 +170,7 @@ phytec PHYTEC Messtechnik GmbH
|
||||
picochip Picochip Ltd
|
||||
plathome Plat'Home Co., Ltd.
|
||||
pixcir PIXCIR MICROELECTRONICS Co., Ltd
|
||||
pulsedlight PulsedLight, Inc
|
||||
powervr PowerVR (deprecated, use img)
|
||||
qca Qualcomm Atheros, Inc.
|
||||
qcom Qualcomm Technologies, Inc
|
||||
@ -191,6 +193,7 @@ sbs Smart Battery System
|
||||
schindler Schindler
|
||||
seagate Seagate Technology PLC
|
||||
semtech Semtech Corporation
|
||||
sgx SGX Sensortech
|
||||
sharp Sharp Corporation
|
||||
sil Silicon Image
|
||||
silabs Silicon Laboratories
|
||||
@ -223,6 +226,7 @@ toshiba Toshiba Corporation
|
||||
toumaz Toumaz
|
||||
tplink TP-LINK Technologies Co., Ltd.
|
||||
truly Truly Semiconductors Limited
|
||||
upisemi uPI Semiconductor Corp.
|
||||
usi Universal Scientific Industrial Co., Ltd.
|
||||
v3 V3 Semiconductor
|
||||
variscite Variscite Ltd.
|
||||
|
@ -6918,6 +6918,13 @@ S: Supported
|
||||
F: include/linux/mlx5/
|
||||
F: drivers/infiniband/hw/mlx5/
|
||||
|
||||
MELEXIS MLX90614 DRIVER
|
||||
M: Crt Mori <cmo@melexis.com>
|
||||
L: linux-iio@vger.kernel.org
|
||||
W: http://www.melexis.com
|
||||
S: Supported
|
||||
F: drivers/iio/temperature/mlx90614.c
|
||||
|
||||
MN88472 MEDIA DRIVER
|
||||
M: Antti Palosaari <crope@iki.fi>
|
||||
L: linux-media@vger.kernel.org
|
||||
|
@ -19,27 +19,7 @@ config IIO_BUFFER
|
||||
acquisition methods.
|
||||
|
||||
if IIO_BUFFER
|
||||
|
||||
config IIO_BUFFER_CB
|
||||
bool "IIO callback buffer used for push in-kernel interfaces"
|
||||
help
|
||||
Should be selected by any drivers that do in-kernel push
|
||||
usage. That is, those where the data is pushed to the consumer.
|
||||
|
||||
config IIO_KFIFO_BUF
|
||||
tristate "Industrial I/O buffering based on kfifo"
|
||||
help
|
||||
A simple fifo based on kfifo. Note that this currently provides
|
||||
no buffer events so it is up to userspace to work out how
|
||||
often to read from the buffer.
|
||||
|
||||
config IIO_TRIGGERED_BUFFER
|
||||
tristate
|
||||
select IIO_TRIGGER
|
||||
select IIO_KFIFO_BUF
|
||||
help
|
||||
Provides helper functions for setting up triggered buffers.
|
||||
|
||||
source "drivers/iio/buffer/Kconfig"
|
||||
endif # IIO_BUFFER
|
||||
|
||||
config IIO_TRIGGER
|
||||
@ -58,9 +38,16 @@ config IIO_CONSUMERS_PER_TRIGGER
|
||||
This value controls the maximum number of consumers that a
|
||||
given trigger may handle. Default is 2.
|
||||
|
||||
config IIO_TRIGGERED_EVENT
|
||||
tristate
|
||||
select IIO_TRIGGER
|
||||
help
|
||||
Provides helper functions for setting up triggered events.
|
||||
|
||||
source "drivers/iio/accel/Kconfig"
|
||||
source "drivers/iio/adc/Kconfig"
|
||||
source "drivers/iio/amplifiers/Kconfig"
|
||||
source "drivers/iio/chemical/Kconfig"
|
||||
source "drivers/iio/common/Kconfig"
|
||||
source "drivers/iio/dac/Kconfig"
|
||||
source "drivers/iio/frequency/Kconfig"
|
||||
|
@ -6,14 +6,14 @@ obj-$(CONFIG_IIO) += industrialio.o
|
||||
industrialio-y := industrialio-core.o industrialio-event.o inkern.o
|
||||
industrialio-$(CONFIG_IIO_BUFFER) += industrialio-buffer.o
|
||||
industrialio-$(CONFIG_IIO_TRIGGER) += industrialio-trigger.o
|
||||
industrialio-$(CONFIG_IIO_BUFFER_CB) += buffer_cb.o
|
||||
|
||||
obj-$(CONFIG_IIO_TRIGGERED_BUFFER) += industrialio-triggered-buffer.o
|
||||
obj-$(CONFIG_IIO_KFIFO_BUF) += kfifo_buf.o
|
||||
obj-$(CONFIG_IIO_TRIGGERED_EVENT) += industrialio-triggered-event.o
|
||||
|
||||
obj-y += accel/
|
||||
obj-y += adc/
|
||||
obj-y += amplifiers/
|
||||
obj-y += buffer/
|
||||
obj-y += chemical/
|
||||
obj-y += common/
|
||||
obj-y += dac/
|
||||
obj-y += gyro/
|
||||
|
@ -100,13 +100,13 @@ config KXCJK1013
|
||||
be called kxcjk-1013.
|
||||
|
||||
config MMA8452
|
||||
tristate "Freescale MMA8452Q Accelerometer Driver"
|
||||
tristate "Freescale MMA8452Q and similar Accelerometers Driver"
|
||||
depends on I2C
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
Say yes here to build support for the Freescale MMA8452Q 3-axis
|
||||
accelerometer.
|
||||
Say yes here to build support for the following Freescale 3-axis
|
||||
accelerometers: MMA8452Q, MMA8453Q, MMA8652FC, MMA8653FC.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called mma8452.
|
||||
@ -137,6 +137,19 @@ config MMA9553
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called mma9553.
|
||||
|
||||
config MXC4005
|
||||
tristate "Memsic MXC4005XC 3-Axis Accelerometer Driver"
|
||||
depends on I2C
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
select REGMAP_I2C
|
||||
help
|
||||
Say yes here to build support for the Memsic MXC4005XC 3-axis
|
||||
accelerometer.
|
||||
|
||||
To compile this driver as a module, choose M. The module will be
|
||||
called mxc4005.
|
||||
|
||||
config STK8312
|
||||
tristate "Sensortek STK8312 3-Axis Accelerometer Driver"
|
||||
depends on I2C
|
||||
|
@ -14,6 +14,8 @@ obj-$(CONFIG_MMA9551_CORE) += mma9551_core.o
|
||||
obj-$(CONFIG_MMA9551) += mma9551.o
|
||||
obj-$(CONFIG_MMA9553) += mma9553.o
|
||||
|
||||
obj-$(CONFIG_MXC4005) += mxc4005.o
|
||||
|
||||
obj-$(CONFIG_STK8312) += stk8312.o
|
||||
obj-$(CONFIG_STK8BA50) += stk8ba50.o
|
||||
|
||||
|
@ -1,6 +1,12 @@
|
||||
/*
|
||||
* mma8452.c - Support for Freescale MMA8452Q 3-axis 12-bit accelerometer
|
||||
* mma8452.c - Support for following Freescale 3-axis accelerometers:
|
||||
*
|
||||
* MMA8452Q (12 bit)
|
||||
* MMA8453Q (10 bit)
|
||||
* MMA8652FC (12 bit)
|
||||
* MMA8653FC (10 bit)
|
||||
*
|
||||
* Copyright 2015 Martin Kepplinger <martin.kepplinger@theobroma-systems.com>
|
||||
* Copyright 2014 Peter Meerwald <pmeerw@pmeerw.net>
|
||||
*
|
||||
* This file is subject to the terms and conditions of version 2 of
|
||||
@ -22,10 +28,11 @@
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/iio/events.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/of_device.h>
|
||||
|
||||
#define MMA8452_STATUS 0x00
|
||||
#define MMA8452_STATUS_DRDY (BIT(2) | BIT(1) | BIT(0))
|
||||
#define MMA8452_OUT_X 0x01 /* MSB first, 12-bit */
|
||||
#define MMA8452_OUT_X 0x01 /* MSB first */
|
||||
#define MMA8452_OUT_Y 0x03
|
||||
#define MMA8452_OUT_Z 0x05
|
||||
#define MMA8452_INT_SRC 0x0c
|
||||
@ -38,6 +45,16 @@
|
||||
#define MMA8452_DATA_CFG_HPF_MASK BIT(4)
|
||||
#define MMA8452_HP_FILTER_CUTOFF 0x0f
|
||||
#define MMA8452_HP_FILTER_CUTOFF_SEL_MASK GENMASK(1, 0)
|
||||
#define MMA8452_FF_MT_CFG 0x15
|
||||
#define MMA8452_FF_MT_CFG_OAE BIT(6)
|
||||
#define MMA8452_FF_MT_CFG_ELE BIT(7)
|
||||
#define MMA8452_FF_MT_SRC 0x16
|
||||
#define MMA8452_FF_MT_SRC_XHE BIT(1)
|
||||
#define MMA8452_FF_MT_SRC_YHE BIT(3)
|
||||
#define MMA8452_FF_MT_SRC_ZHE BIT(5)
|
||||
#define MMA8452_FF_MT_THS 0x17
|
||||
#define MMA8452_FF_MT_THS_MASK 0x7f
|
||||
#define MMA8452_FF_MT_COUNT 0x18
|
||||
#define MMA8452_TRANSIENT_CFG 0x1d
|
||||
#define MMA8452_TRANSIENT_CFG_HPF_BYP BIT(0)
|
||||
#define MMA8452_TRANSIENT_CFG_CHAN(chan) BIT(chan + 1)
|
||||
@ -65,15 +82,65 @@
|
||||
#define MMA8452_MAX_REG 0x31
|
||||
|
||||
#define MMA8452_INT_DRDY BIT(0)
|
||||
#define MMA8452_INT_FF_MT BIT(2)
|
||||
#define MMA8452_INT_TRANS BIT(5)
|
||||
|
||||
#define MMA8452_DEVICE_ID 0x2a
|
||||
#define MMA8453_DEVICE_ID 0x3a
|
||||
#define MMA8652_DEVICE_ID 0x4a
|
||||
#define MMA8653_DEVICE_ID 0x5a
|
||||
|
||||
struct mma8452_data {
|
||||
struct i2c_client *client;
|
||||
struct mutex lock;
|
||||
u8 ctrl_reg1;
|
||||
u8 data_cfg;
|
||||
const struct mma_chip_info *chip_info;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mma_chip_info - chip specific data for Freescale's accelerometers
|
||||
* @chip_id: WHO_AM_I register's value
|
||||
* @channels: struct iio_chan_spec matching the device's
|
||||
* capabilities
|
||||
* @num_channels: number of channels
|
||||
* @mma_scales: scale factors for converting register values
|
||||
* to m/s^2; 3 modes: 2g, 4g, 8g; 2 integers
|
||||
* per mode: m/s^2 and micro m/s^2
|
||||
* @ev_cfg: event config register address
|
||||
* @ev_cfg_ele: latch bit in event config register
|
||||
* @ev_cfg_chan_shift: number of the bit to enable events in X
|
||||
* direction; in event config register
|
||||
* @ev_src: event source register address
|
||||
* @ev_src_xe: bit in event source register that indicates
|
||||
* an event in X direction
|
||||
* @ev_src_ye: bit in event source register that indicates
|
||||
* an event in Y direction
|
||||
* @ev_src_ze: bit in event source register that indicates
|
||||
* an event in Z direction
|
||||
* @ev_ths: event threshold register address
|
||||
* @ev_ths_mask: mask for the threshold value
|
||||
* @ev_count: event count (period) register address
|
||||
*
|
||||
* Since not all chips supported by the driver support comparing high pass
|
||||
* filtered data for events (interrupts), different interrupt sources are
|
||||
* used for different chips and the relevant registers are included here.
|
||||
*/
|
||||
struct mma_chip_info {
|
||||
u8 chip_id;
|
||||
const struct iio_chan_spec *channels;
|
||||
int num_channels;
|
||||
const int mma_scales[3][2];
|
||||
u8 ev_cfg;
|
||||
u8 ev_cfg_ele;
|
||||
u8 ev_cfg_chan_shift;
|
||||
u8 ev_src;
|
||||
u8 ev_src_xe;
|
||||
u8 ev_src_ye;
|
||||
u8 ev_src_ze;
|
||||
u8 ev_ths;
|
||||
u8 ev_ths_mask;
|
||||
u8 ev_count;
|
||||
};
|
||||
|
||||
static int mma8452_drdy(struct mma8452_data *data)
|
||||
@ -143,16 +210,6 @@ static const int mma8452_samp_freq[8][2] = {
|
||||
{6, 250000}, {1, 560000}
|
||||
};
|
||||
|
||||
/*
|
||||
* Hardware has fullscale of -2G, -4G, -8G corresponding to raw value -2048
|
||||
* The userspace interface uses m/s^2 and we declare micro units
|
||||
* So scale factor is given by:
|
||||
* g * N * 1000000 / 2048 for N = 2, 4, 8 and g = 9.80665
|
||||
*/
|
||||
static const int mma8452_scales[3][2] = {
|
||||
{0, 9577}, {0, 19154}, {0, 38307}
|
||||
};
|
||||
|
||||
/* Datasheet table 35 (step time vs sample frequency) */
|
||||
static const int mma8452_transient_time_step_us[8] = {
|
||||
1250,
|
||||
@ -189,8 +246,11 @@ static ssize_t mma8452_show_scale_avail(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return mma8452_show_int_plus_micros(buf, mma8452_scales,
|
||||
ARRAY_SIZE(mma8452_scales));
|
||||
struct mma8452_data *data = iio_priv(i2c_get_clientdata(
|
||||
to_i2c_client(dev)));
|
||||
|
||||
return mma8452_show_int_plus_micros(buf, data->chip_info->mma_scales,
|
||||
ARRAY_SIZE(data->chip_info->mma_scales));
|
||||
}
|
||||
|
||||
static ssize_t mma8452_show_hp_cutoff_avail(struct device *dev,
|
||||
@ -221,9 +281,8 @@ static int mma8452_get_samp_freq_index(struct mma8452_data *data,
|
||||
|
||||
static int mma8452_get_scale_index(struct mma8452_data *data, int val, int val2)
|
||||
{
|
||||
return mma8452_get_int_plus_micros_index(mma8452_scales,
|
||||
ARRAY_SIZE(mma8452_scales),
|
||||
val, val2);
|
||||
return mma8452_get_int_plus_micros_index(data->chip_info->mma_scales,
|
||||
ARRAY_SIZE(data->chip_info->mma_scales), val, val2);
|
||||
}
|
||||
|
||||
static int mma8452_get_hp_filter_index(struct mma8452_data *data,
|
||||
@ -270,14 +329,15 @@ static int mma8452_read_raw(struct iio_dev *indio_dev,
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = sign_extend32(be16_to_cpu(buffer[chan->scan_index]) >> 4,
|
||||
11);
|
||||
*val = sign_extend32(be16_to_cpu(
|
||||
buffer[chan->scan_index]) >> chan->scan_type.shift,
|
||||
chan->scan_type.realbits - 1);
|
||||
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
i = data->data_cfg & MMA8452_DATA_CFG_FS_MASK;
|
||||
*val = mma8452_scales[i][0];
|
||||
*val2 = mma8452_scales[i][1];
|
||||
*val = data->chip_info->mma_scales[i][0];
|
||||
*val2 = data->chip_info->mma_scales[i][1];
|
||||
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
@ -439,17 +499,17 @@ static int mma8452_read_thresh(struct iio_dev *indio_dev,
|
||||
switch (info) {
|
||||
case IIO_EV_INFO_VALUE:
|
||||
ret = i2c_smbus_read_byte_data(data->client,
|
||||
MMA8452_TRANSIENT_THS);
|
||||
data->chip_info->ev_ths);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = ret & MMA8452_TRANSIENT_THS_MASK;
|
||||
*val = ret & data->chip_info->ev_ths_mask;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
|
||||
case IIO_EV_INFO_PERIOD:
|
||||
ret = i2c_smbus_read_byte_data(data->client,
|
||||
MMA8452_TRANSIENT_COUNT);
|
||||
data->chip_info->ev_count);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@ -497,7 +557,8 @@ static int mma8452_write_thresh(struct iio_dev *indio_dev,
|
||||
if (val < 0 || val > MMA8452_TRANSIENT_THS_MASK)
|
||||
return -EINVAL;
|
||||
|
||||
return mma8452_change_config(data, MMA8452_TRANSIENT_THS, val);
|
||||
return mma8452_change_config(data, data->chip_info->ev_ths,
|
||||
val);
|
||||
|
||||
case IIO_EV_INFO_PERIOD:
|
||||
steps = (val * USEC_PER_SEC + val2) /
|
||||
@ -507,7 +568,7 @@ static int mma8452_write_thresh(struct iio_dev *indio_dev,
|
||||
if (steps < 0 || steps > 0xff)
|
||||
return -EINVAL;
|
||||
|
||||
return mma8452_change_config(data, MMA8452_TRANSIENT_COUNT,
|
||||
return mma8452_change_config(data, data->chip_info->ev_count,
|
||||
steps);
|
||||
|
||||
case IIO_EV_INFO_HIGH_PASS_FILTER_3DB:
|
||||
@ -538,13 +599,15 @@ static int mma8452_read_event_config(struct iio_dev *indio_dev,
|
||||
enum iio_event_direction dir)
|
||||
{
|
||||
struct mma8452_data *data = iio_priv(indio_dev);
|
||||
const struct mma_chip_info *chip = data->chip_info;
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(data->client, MMA8452_TRANSIENT_CFG);
|
||||
ret = i2c_smbus_read_byte_data(data->client,
|
||||
data->chip_info->ev_cfg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return ret & MMA8452_TRANSIENT_CFG_CHAN(chan->scan_index) ? 1 : 0;
|
||||
return !!(ret & BIT(chan->scan_index + chip->ev_cfg_chan_shift));
|
||||
}
|
||||
|
||||
static int mma8452_write_event_config(struct iio_dev *indio_dev,
|
||||
@ -554,20 +617,22 @@ static int mma8452_write_event_config(struct iio_dev *indio_dev,
|
||||
int state)
|
||||
{
|
||||
struct mma8452_data *data = iio_priv(indio_dev);
|
||||
const struct mma_chip_info *chip = data->chip_info;
|
||||
int val;
|
||||
|
||||
val = i2c_smbus_read_byte_data(data->client, MMA8452_TRANSIENT_CFG);
|
||||
val = i2c_smbus_read_byte_data(data->client, chip->ev_cfg);
|
||||
if (val < 0)
|
||||
return val;
|
||||
|
||||
if (state)
|
||||
val |= MMA8452_TRANSIENT_CFG_CHAN(chan->scan_index);
|
||||
val |= BIT(chan->scan_index + chip->ev_cfg_chan_shift);
|
||||
else
|
||||
val &= ~MMA8452_TRANSIENT_CFG_CHAN(chan->scan_index);
|
||||
val &= ~BIT(chan->scan_index + chip->ev_cfg_chan_shift);
|
||||
|
||||
val |= MMA8452_TRANSIENT_CFG_ELE;
|
||||
val |= chip->ev_cfg_ele;
|
||||
val |= MMA8452_FF_MT_CFG_OAE;
|
||||
|
||||
return mma8452_change_config(data, MMA8452_TRANSIENT_CFG, val);
|
||||
return mma8452_change_config(data, chip->ev_cfg, val);
|
||||
}
|
||||
|
||||
static void mma8452_transient_interrupt(struct iio_dev *indio_dev)
|
||||
@ -576,25 +641,25 @@ static void mma8452_transient_interrupt(struct iio_dev *indio_dev)
|
||||
s64 ts = iio_get_time_ns();
|
||||
int src;
|
||||
|
||||
src = i2c_smbus_read_byte_data(data->client, MMA8452_TRANSIENT_SRC);
|
||||
src = i2c_smbus_read_byte_data(data->client, data->chip_info->ev_src);
|
||||
if (src < 0)
|
||||
return;
|
||||
|
||||
if (src & MMA8452_TRANSIENT_SRC_XTRANSE)
|
||||
if (src & data->chip_info->ev_src_xe)
|
||||
iio_push_event(indio_dev,
|
||||
IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_X,
|
||||
IIO_EV_TYPE_MAG,
|
||||
IIO_EV_DIR_RISING),
|
||||
ts);
|
||||
|
||||
if (src & MMA8452_TRANSIENT_SRC_YTRANSE)
|
||||
if (src & data->chip_info->ev_src_ye)
|
||||
iio_push_event(indio_dev,
|
||||
IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_Y,
|
||||
IIO_EV_TYPE_MAG,
|
||||
IIO_EV_DIR_RISING),
|
||||
ts);
|
||||
|
||||
if (src & MMA8452_TRANSIENT_SRC_ZTRANSE)
|
||||
if (src & data->chip_info->ev_src_ze)
|
||||
iio_push_event(indio_dev,
|
||||
IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_Z,
|
||||
IIO_EV_TYPE_MAG,
|
||||
@ -606,6 +671,7 @@ static irqreturn_t mma8452_interrupt(int irq, void *p)
|
||||
{
|
||||
struct iio_dev *indio_dev = p;
|
||||
struct mma8452_data *data = iio_priv(indio_dev);
|
||||
const struct mma_chip_info *chip = data->chip_info;
|
||||
int ret = IRQ_NONE;
|
||||
int src;
|
||||
|
||||
@ -618,7 +684,10 @@ static irqreturn_t mma8452_interrupt(int irq, void *p)
|
||||
ret = IRQ_HANDLED;
|
||||
}
|
||||
|
||||
if (src & MMA8452_INT_TRANS) {
|
||||
if ((src & MMA8452_INT_TRANS &&
|
||||
chip->ev_src == MMA8452_TRANSIENT_SRC) ||
|
||||
(src & MMA8452_INT_FF_MT &&
|
||||
chip->ev_src == MMA8452_FF_MT_SRC)) {
|
||||
mma8452_transient_interrupt(indio_dev);
|
||||
ret = IRQ_HANDLED;
|
||||
}
|
||||
@ -680,6 +749,16 @@ static const struct iio_event_spec mma8452_transient_event[] = {
|
||||
},
|
||||
};
|
||||
|
||||
static const struct iio_event_spec mma8452_motion_event[] = {
|
||||
{
|
||||
.type = IIO_EV_TYPE_MAG,
|
||||
.dir = IIO_EV_DIR_RISING,
|
||||
.mask_separate = BIT(IIO_EV_INFO_ENABLE),
|
||||
.mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) |
|
||||
BIT(IIO_EV_INFO_PERIOD)
|
||||
},
|
||||
};
|
||||
|
||||
/*
|
||||
* Threshold is configured in fixed 8G/127 steps regardless of
|
||||
* currently selected scale for measurement.
|
||||
@ -693,10 +772,9 @@ static struct attribute *mma8452_event_attributes[] = {
|
||||
|
||||
static struct attribute_group mma8452_event_attribute_group = {
|
||||
.attrs = mma8452_event_attributes,
|
||||
.name = "events",
|
||||
};
|
||||
|
||||
#define MMA8452_CHANNEL(axis, idx) { \
|
||||
#define MMA8452_CHANNEL(axis, idx, bits) { \
|
||||
.type = IIO_ACCEL, \
|
||||
.modified = 1, \
|
||||
.channel2 = IIO_MOD_##axis, \
|
||||
@ -708,22 +786,144 @@ static struct attribute_group mma8452_event_attribute_group = {
|
||||
.scan_index = idx, \
|
||||
.scan_type = { \
|
||||
.sign = 's', \
|
||||
.realbits = 12, \
|
||||
.realbits = (bits), \
|
||||
.storagebits = 16, \
|
||||
.shift = 4, \
|
||||
.shift = 16 - (bits), \
|
||||
.endianness = IIO_BE, \
|
||||
}, \
|
||||
.event_spec = mma8452_transient_event, \
|
||||
.num_event_specs = ARRAY_SIZE(mma8452_transient_event), \
|
||||
}
|
||||
|
||||
#define MMA8652_CHANNEL(axis, idx, bits) { \
|
||||
.type = IIO_ACCEL, \
|
||||
.modified = 1, \
|
||||
.channel2 = IIO_MOD_##axis, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_CALIBBIAS), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
|
||||
BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.scan_index = idx, \
|
||||
.scan_type = { \
|
||||
.sign = 's', \
|
||||
.realbits = (bits), \
|
||||
.storagebits = 16, \
|
||||
.shift = 16 - (bits), \
|
||||
.endianness = IIO_BE, \
|
||||
}, \
|
||||
.event_spec = mma8452_motion_event, \
|
||||
.num_event_specs = ARRAY_SIZE(mma8452_motion_event), \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec mma8452_channels[] = {
|
||||
MMA8452_CHANNEL(X, 0),
|
||||
MMA8452_CHANNEL(Y, 1),
|
||||
MMA8452_CHANNEL(Z, 2),
|
||||
MMA8452_CHANNEL(X, 0, 12),
|
||||
MMA8452_CHANNEL(Y, 1, 12),
|
||||
MMA8452_CHANNEL(Z, 2, 12),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(3),
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec mma8453_channels[] = {
|
||||
MMA8452_CHANNEL(X, 0, 10),
|
||||
MMA8452_CHANNEL(Y, 1, 10),
|
||||
MMA8452_CHANNEL(Z, 2, 10),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(3),
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec mma8652_channels[] = {
|
||||
MMA8652_CHANNEL(X, 0, 12),
|
||||
MMA8652_CHANNEL(Y, 1, 12),
|
||||
MMA8652_CHANNEL(Z, 2, 12),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(3),
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec mma8653_channels[] = {
|
||||
MMA8652_CHANNEL(X, 0, 10),
|
||||
MMA8652_CHANNEL(Y, 1, 10),
|
||||
MMA8652_CHANNEL(Z, 2, 10),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(3),
|
||||
};
|
||||
|
||||
enum {
|
||||
mma8452,
|
||||
mma8453,
|
||||
mma8652,
|
||||
mma8653,
|
||||
};
|
||||
|
||||
static const struct mma_chip_info mma_chip_info_table[] = {
|
||||
[mma8452] = {
|
||||
.chip_id = MMA8452_DEVICE_ID,
|
||||
.channels = mma8452_channels,
|
||||
.num_channels = ARRAY_SIZE(mma8452_channels),
|
||||
/*
|
||||
* Hardware has fullscale of -2G, -4G, -8G corresponding to
|
||||
* raw value -2048 for 12 bit or -512 for 10 bit.
|
||||
* The userspace interface uses m/s^2 and we declare micro units
|
||||
* So scale factor for 12 bit here is given by:
|
||||
* g * N * 1000000 / 2048 for N = 2, 4, 8 and g=9.80665
|
||||
*/
|
||||
.mma_scales = { {0, 9577}, {0, 19154}, {0, 38307} },
|
||||
.ev_cfg = MMA8452_TRANSIENT_CFG,
|
||||
.ev_cfg_ele = MMA8452_TRANSIENT_CFG_ELE,
|
||||
.ev_cfg_chan_shift = 1,
|
||||
.ev_src = MMA8452_TRANSIENT_SRC,
|
||||
.ev_src_xe = MMA8452_TRANSIENT_SRC_XTRANSE,
|
||||
.ev_src_ye = MMA8452_TRANSIENT_SRC_YTRANSE,
|
||||
.ev_src_ze = MMA8452_TRANSIENT_SRC_ZTRANSE,
|
||||
.ev_ths = MMA8452_TRANSIENT_THS,
|
||||
.ev_ths_mask = MMA8452_TRANSIENT_THS_MASK,
|
||||
.ev_count = MMA8452_TRANSIENT_COUNT,
|
||||
},
|
||||
[mma8453] = {
|
||||
.chip_id = MMA8453_DEVICE_ID,
|
||||
.channels = mma8453_channels,
|
||||
.num_channels = ARRAY_SIZE(mma8453_channels),
|
||||
.mma_scales = { {0, 38307}, {0, 76614}, {0, 153228} },
|
||||
.ev_cfg = MMA8452_TRANSIENT_CFG,
|
||||
.ev_cfg_ele = MMA8452_TRANSIENT_CFG_ELE,
|
||||
.ev_cfg_chan_shift = 1,
|
||||
.ev_src = MMA8452_TRANSIENT_SRC,
|
||||
.ev_src_xe = MMA8452_TRANSIENT_SRC_XTRANSE,
|
||||
.ev_src_ye = MMA8452_TRANSIENT_SRC_YTRANSE,
|
||||
.ev_src_ze = MMA8452_TRANSIENT_SRC_ZTRANSE,
|
||||
.ev_ths = MMA8452_TRANSIENT_THS,
|
||||
.ev_ths_mask = MMA8452_TRANSIENT_THS_MASK,
|
||||
.ev_count = MMA8452_TRANSIENT_COUNT,
|
||||
},
|
||||
[mma8652] = {
|
||||
.chip_id = MMA8652_DEVICE_ID,
|
||||
.channels = mma8652_channels,
|
||||
.num_channels = ARRAY_SIZE(mma8652_channels),
|
||||
.mma_scales = { {0, 9577}, {0, 19154}, {0, 38307} },
|
||||
.ev_cfg = MMA8452_FF_MT_CFG,
|
||||
.ev_cfg_ele = MMA8452_FF_MT_CFG_ELE,
|
||||
.ev_cfg_chan_shift = 3,
|
||||
.ev_src = MMA8452_FF_MT_SRC,
|
||||
.ev_src_xe = MMA8452_FF_MT_SRC_XHE,
|
||||
.ev_src_ye = MMA8452_FF_MT_SRC_YHE,
|
||||
.ev_src_ze = MMA8452_FF_MT_SRC_ZHE,
|
||||
.ev_ths = MMA8452_FF_MT_THS,
|
||||
.ev_ths_mask = MMA8452_FF_MT_THS_MASK,
|
||||
.ev_count = MMA8452_FF_MT_COUNT,
|
||||
},
|
||||
[mma8653] = {
|
||||
.chip_id = MMA8653_DEVICE_ID,
|
||||
.channels = mma8653_channels,
|
||||
.num_channels = ARRAY_SIZE(mma8653_channels),
|
||||
.mma_scales = { {0, 38307}, {0, 76614}, {0, 153228} },
|
||||
.ev_cfg = MMA8452_FF_MT_CFG,
|
||||
.ev_cfg_ele = MMA8452_FF_MT_CFG_ELE,
|
||||
.ev_cfg_chan_shift = 3,
|
||||
.ev_src = MMA8452_FF_MT_SRC,
|
||||
.ev_src_xe = MMA8452_FF_MT_SRC_XHE,
|
||||
.ev_src_ye = MMA8452_FF_MT_SRC_YHE,
|
||||
.ev_src_ze = MMA8452_FF_MT_SRC_ZHE,
|
||||
.ev_ths = MMA8452_FF_MT_THS,
|
||||
.ev_ths_mask = MMA8452_FF_MT_THS_MASK,
|
||||
.ev_count = MMA8452_FF_MT_COUNT,
|
||||
},
|
||||
};
|
||||
|
||||
static struct attribute *mma8452_attributes[] = {
|
||||
&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
|
||||
&iio_dev_attr_in_accel_scale_available.dev_attr.attr,
|
||||
@ -841,18 +1041,28 @@ static int mma8452_reset(struct i2c_client *client)
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static const struct of_device_id mma8452_dt_ids[] = {
|
||||
{ .compatible = "fsl,mma8452", .data = &mma_chip_info_table[mma8452] },
|
||||
{ .compatible = "fsl,mma8453", .data = &mma_chip_info_table[mma8453] },
|
||||
{ .compatible = "fsl,mma8652", .data = &mma_chip_info_table[mma8652] },
|
||||
{ .compatible = "fsl,mma8653", .data = &mma_chip_info_table[mma8653] },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mma8452_dt_ids);
|
||||
|
||||
static int mma8452_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct mma8452_data *data;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret;
|
||||
const struct of_device_id *match;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(client, MMA8452_WHO_AM_I);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret != MMA8452_DEVICE_ID)
|
||||
match = of_match_device(mma8452_dt_ids, &client->dev);
|
||||
if (!match) {
|
||||
dev_err(&client->dev, "unknown device model\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
@ -861,14 +1071,33 @@ static int mma8452_probe(struct i2c_client *client,
|
||||
data = iio_priv(indio_dev);
|
||||
data->client = client;
|
||||
mutex_init(&data->lock);
|
||||
data->chip_info = match->data;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(client, MMA8452_WHO_AM_I);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
switch (ret) {
|
||||
case MMA8452_DEVICE_ID:
|
||||
case MMA8453_DEVICE_ID:
|
||||
case MMA8652_DEVICE_ID:
|
||||
case MMA8653_DEVICE_ID:
|
||||
if (ret == data->chip_info->chip_id)
|
||||
break;
|
||||
default:
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
dev_info(&client->dev, "registering %s accelerometer; ID 0x%x\n",
|
||||
match->compatible, data->chip_info->chip_id);
|
||||
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
indio_dev->info = &mma8452_info;
|
||||
indio_dev->name = id->name;
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = mma8452_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(mma8452_channels);
|
||||
indio_dev->channels = data->chip_info->channels;
|
||||
indio_dev->num_channels = data->chip_info->num_channels;
|
||||
indio_dev->available_scan_masks = mma8452_scan_masks;
|
||||
|
||||
ret = mma8452_reset(client);
|
||||
@ -892,13 +1121,15 @@ static int mma8452_probe(struct i2c_client *client,
|
||||
|
||||
if (client->irq) {
|
||||
/*
|
||||
* Although we enable the transient interrupt source once and
|
||||
* for all here the transient event detection itself is not
|
||||
* enabled until userspace asks for it by
|
||||
* mma8452_write_event_config()
|
||||
* Although we enable the interrupt sources once and for
|
||||
* all here the event detection itself is not enabled until
|
||||
* userspace asks for it by mma8452_write_event_config()
|
||||
*/
|
||||
int supported_interrupts = MMA8452_INT_DRDY | MMA8452_INT_TRANS;
|
||||
int enabled_interrupts = MMA8452_INT_TRANS;
|
||||
int supported_interrupts = MMA8452_INT_DRDY |
|
||||
MMA8452_INT_TRANS |
|
||||
MMA8452_INT_FF_MT;
|
||||
int enabled_interrupts = MMA8452_INT_TRANS |
|
||||
MMA8452_INT_FF_MT;
|
||||
|
||||
/* Assume wired to INT1 pin */
|
||||
ret = i2c_smbus_write_byte_data(client,
|
||||
@ -987,17 +1218,14 @@ static SIMPLE_DEV_PM_OPS(mma8452_pm_ops, mma8452_suspend, mma8452_resume);
|
||||
#endif
|
||||
|
||||
static const struct i2c_device_id mma8452_id[] = {
|
||||
{ "mma8452", 0 },
|
||||
{ "mma8452", mma8452 },
|
||||
{ "mma8453", mma8453 },
|
||||
{ "mma8652", mma8652 },
|
||||
{ "mma8653", mma8653 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, mma8452_id);
|
||||
|
||||
static const struct of_device_id mma8452_dt_ids[] = {
|
||||
{ .compatible = "fsl,mma8452" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mma8452_dt_ids);
|
||||
|
||||
static struct i2c_driver mma8452_driver = {
|
||||
.driver = {
|
||||
.name = "mma8452",
|
||||
|
567
drivers/iio/accel/mxc4005.c
Normal file
567
drivers/iio/accel/mxc4005.c
Normal file
@ -0,0 +1,567 @@
|
||||
/*
|
||||
* 3-axis accelerometer driver for MXC4005XC Memsic sensor
|
||||
*
|
||||
* Copyright (c) 2014, Intel Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
|
||||
#define MXC4005_DRV_NAME "mxc4005"
|
||||
#define MXC4005_IRQ_NAME "mxc4005_event"
|
||||
#define MXC4005_REGMAP_NAME "mxc4005_regmap"
|
||||
|
||||
#define MXC4005_REG_XOUT_UPPER 0x03
|
||||
#define MXC4005_REG_XOUT_LOWER 0x04
|
||||
#define MXC4005_REG_YOUT_UPPER 0x05
|
||||
#define MXC4005_REG_YOUT_LOWER 0x06
|
||||
#define MXC4005_REG_ZOUT_UPPER 0x07
|
||||
#define MXC4005_REG_ZOUT_LOWER 0x08
|
||||
|
||||
#define MXC4005_REG_INT_MASK1 0x0B
|
||||
#define MXC4005_REG_INT_MASK1_BIT_DRDYE 0x01
|
||||
|
||||
#define MXC4005_REG_INT_CLR1 0x01
|
||||
#define MXC4005_REG_INT_CLR1_BIT_DRDYC 0x01
|
||||
|
||||
#define MXC4005_REG_CONTROL 0x0D
|
||||
#define MXC4005_REG_CONTROL_MASK_FSR GENMASK(6, 5)
|
||||
#define MXC4005_CONTROL_FSR_SHIFT 5
|
||||
|
||||
#define MXC4005_REG_DEVICE_ID 0x0E
|
||||
|
||||
enum mxc4005_axis {
|
||||
AXIS_X,
|
||||
AXIS_Y,
|
||||
AXIS_Z,
|
||||
};
|
||||
|
||||
enum mxc4005_range {
|
||||
MXC4005_RANGE_2G,
|
||||
MXC4005_RANGE_4G,
|
||||
MXC4005_RANGE_8G,
|
||||
};
|
||||
|
||||
struct mxc4005_data {
|
||||
struct device *dev;
|
||||
struct mutex mutex;
|
||||
struct regmap *regmap;
|
||||
struct iio_trigger *dready_trig;
|
||||
__be16 buffer[8];
|
||||
bool trigger_enabled;
|
||||
};
|
||||
|
||||
/*
|
||||
* MXC4005 can operate in the following ranges:
|
||||
* +/- 2G, 4G, 8G (the default +/-2G)
|
||||
*
|
||||
* (2 + 2) * 9.81 / (2^12 - 1) = 0.009582
|
||||
* (4 + 4) * 9.81 / (2^12 - 1) = 0.019164
|
||||
* (8 + 8) * 9.81 / (2^12 - 1) = 0.038329
|
||||
*/
|
||||
static const struct {
|
||||
u8 range;
|
||||
int scale;
|
||||
} mxc4005_scale_table[] = {
|
||||
{MXC4005_RANGE_2G, 9582},
|
||||
{MXC4005_RANGE_4G, 19164},
|
||||
{MXC4005_RANGE_8G, 38329},
|
||||
};
|
||||
|
||||
|
||||
static IIO_CONST_ATTR(in_accel_scale_available, "0.009582 0.019164 0.038329");
|
||||
|
||||
static struct attribute *mxc4005_attributes[] = {
|
||||
&iio_const_attr_in_accel_scale_available.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group mxc4005_attrs_group = {
|
||||
.attrs = mxc4005_attributes,
|
||||
};
|
||||
|
||||
static bool mxc4005_is_readable_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case MXC4005_REG_XOUT_UPPER:
|
||||
case MXC4005_REG_XOUT_LOWER:
|
||||
case MXC4005_REG_YOUT_UPPER:
|
||||
case MXC4005_REG_YOUT_LOWER:
|
||||
case MXC4005_REG_ZOUT_UPPER:
|
||||
case MXC4005_REG_ZOUT_LOWER:
|
||||
case MXC4005_REG_DEVICE_ID:
|
||||
case MXC4005_REG_CONTROL:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool mxc4005_is_writeable_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case MXC4005_REG_INT_CLR1:
|
||||
case MXC4005_REG_INT_MASK1:
|
||||
case MXC4005_REG_CONTROL:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct regmap_config mxc4005_regmap_config = {
|
||||
.name = MXC4005_REGMAP_NAME,
|
||||
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
|
||||
.max_register = MXC4005_REG_DEVICE_ID,
|
||||
|
||||
.readable_reg = mxc4005_is_readable_reg,
|
||||
.writeable_reg = mxc4005_is_writeable_reg,
|
||||
};
|
||||
|
||||
static int mxc4005_read_xyz(struct mxc4005_data *data)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = regmap_bulk_read(data->regmap, MXC4005_REG_XOUT_UPPER,
|
||||
(u8 *) data->buffer, sizeof(data->buffer));
|
||||
if (ret < 0) {
|
||||
dev_err(data->dev, "failed to read axes\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mxc4005_read_axis(struct mxc4005_data *data,
|
||||
unsigned int addr)
|
||||
{
|
||||
__be16 reg;
|
||||
int ret;
|
||||
|
||||
ret = regmap_bulk_read(data->regmap, addr, (u8 *) ®, sizeof(reg));
|
||||
if (ret < 0) {
|
||||
dev_err(data->dev, "failed to read reg %02x\n", addr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return be16_to_cpu(reg);
|
||||
}
|
||||
|
||||
static int mxc4005_read_scale(struct mxc4005_data *data)
|
||||
{
|
||||
unsigned int reg;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
ret = regmap_read(data->regmap, MXC4005_REG_CONTROL, ®);
|
||||
if (ret < 0) {
|
||||
dev_err(data->dev, "failed to read reg_control\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
i = reg >> MXC4005_CONTROL_FSR_SHIFT;
|
||||
|
||||
if (i < 0 || i >= ARRAY_SIZE(mxc4005_scale_table))
|
||||
return -EINVAL;
|
||||
|
||||
return mxc4005_scale_table[i].scale;
|
||||
}
|
||||
|
||||
static int mxc4005_set_scale(struct mxc4005_data *data, int val)
|
||||
{
|
||||
unsigned int reg;
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(mxc4005_scale_table); i++) {
|
||||
if (mxc4005_scale_table[i].scale == val) {
|
||||
reg = i << MXC4005_CONTROL_FSR_SHIFT;
|
||||
ret = regmap_update_bits(data->regmap,
|
||||
MXC4005_REG_CONTROL,
|
||||
MXC4005_REG_CONTROL_MASK_FSR,
|
||||
reg);
|
||||
if (ret < 0)
|
||||
dev_err(data->dev,
|
||||
"failed to write reg_control\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int mxc4005_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct mxc4005_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
switch (chan->type) {
|
||||
case IIO_ACCEL:
|
||||
if (iio_buffer_enabled(indio_dev))
|
||||
return -EBUSY;
|
||||
|
||||
ret = mxc4005_read_axis(data, chan->address);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = sign_extend32(ret >> chan->scan_type.shift,
|
||||
chan->scan_type.realbits - 1);
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
ret = mxc4005_read_scale(data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = 0;
|
||||
*val2 = ret;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int mxc4005_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
struct mxc4005_data *data = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
if (val != 0)
|
||||
return -EINVAL;
|
||||
|
||||
return mxc4005_set_scale(data, val2);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct iio_info mxc4005_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = mxc4005_read_raw,
|
||||
.write_raw = mxc4005_write_raw,
|
||||
.attrs = &mxc4005_attrs_group,
|
||||
};
|
||||
|
||||
static const unsigned long mxc4005_scan_masks[] = {
|
||||
BIT(AXIS_X) | BIT(AXIS_Y) | BIT(AXIS_Z),
|
||||
0
|
||||
};
|
||||
|
||||
#define MXC4005_CHANNEL(_axis, _addr) { \
|
||||
.type = IIO_ACCEL, \
|
||||
.modified = 1, \
|
||||
.channel2 = IIO_MOD_##_axis, \
|
||||
.address = _addr, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.scan_index = AXIS_##_axis, \
|
||||
.scan_type = { \
|
||||
.sign = 's', \
|
||||
.realbits = 12, \
|
||||
.storagebits = 16, \
|
||||
.shift = 4, \
|
||||
.endianness = IIO_BE, \
|
||||
}, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec mxc4005_channels[] = {
|
||||
MXC4005_CHANNEL(X, MXC4005_REG_XOUT_UPPER),
|
||||
MXC4005_CHANNEL(Y, MXC4005_REG_YOUT_UPPER),
|
||||
MXC4005_CHANNEL(Z, MXC4005_REG_ZOUT_UPPER),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(3),
|
||||
};
|
||||
|
||||
static irqreturn_t mxc4005_trigger_handler(int irq, void *private)
|
||||
{
|
||||
struct iio_poll_func *pf = private;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct mxc4005_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = mxc4005_read_xyz(data);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
|
||||
pf->timestamp);
|
||||
|
||||
err:
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int mxc4005_clr_intr(struct mxc4005_data *data)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* clear interrupt */
|
||||
ret = regmap_write(data->regmap, MXC4005_REG_INT_CLR1,
|
||||
MXC4005_REG_INT_CLR1_BIT_DRDYC);
|
||||
if (ret < 0) {
|
||||
dev_err(data->dev, "failed to write to reg_int_clr1\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mxc4005_set_trigger_state(struct iio_trigger *trig,
|
||||
bool state)
|
||||
{
|
||||
struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
|
||||
struct mxc4005_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&data->mutex);
|
||||
if (state) {
|
||||
ret = regmap_write(data->regmap, MXC4005_REG_INT_MASK1,
|
||||
MXC4005_REG_INT_MASK1_BIT_DRDYE);
|
||||
} else {
|
||||
ret = regmap_write(data->regmap, MXC4005_REG_INT_MASK1,
|
||||
~MXC4005_REG_INT_MASK1_BIT_DRDYE);
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&data->mutex);
|
||||
dev_err(data->dev, "failed to update reg_int_mask1");
|
||||
return ret;
|
||||
}
|
||||
|
||||
data->trigger_enabled = state;
|
||||
mutex_unlock(&data->mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mxc4005_trigger_try_reen(struct iio_trigger *trig)
|
||||
{
|
||||
struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
|
||||
struct mxc4005_data *data = iio_priv(indio_dev);
|
||||
|
||||
if (!data->dready_trig)
|
||||
return 0;
|
||||
|
||||
return mxc4005_clr_intr(data);
|
||||
}
|
||||
|
||||
static const struct iio_trigger_ops mxc4005_trigger_ops = {
|
||||
.set_trigger_state = mxc4005_set_trigger_state,
|
||||
.try_reenable = mxc4005_trigger_try_reen,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int mxc4005_gpio_probe(struct i2c_client *client,
|
||||
struct mxc4005_data *data)
|
||||
{
|
||||
struct device *dev;
|
||||
struct gpio_desc *gpio;
|
||||
int ret;
|
||||
|
||||
if (!client)
|
||||
return -EINVAL;
|
||||
|
||||
dev = &client->dev;
|
||||
|
||||
gpio = devm_gpiod_get_index(dev, "mxc4005_int", 0, GPIOD_IN);
|
||||
if (IS_ERR(gpio)) {
|
||||
dev_err(dev, "failed to get acpi gpio index\n");
|
||||
return PTR_ERR(gpio);
|
||||
}
|
||||
|
||||
ret = gpiod_to_irq(gpio);
|
||||
|
||||
dev_dbg(dev, "GPIO resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mxc4005_chip_init(struct mxc4005_data *data)
|
||||
{
|
||||
int ret;
|
||||
unsigned int reg;
|
||||
|
||||
ret = regmap_read(data->regmap, MXC4005_REG_DEVICE_ID, ®);
|
||||
if (ret < 0) {
|
||||
dev_err(data->dev, "failed to read chip id\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_dbg(data->dev, "MXC4005 chip id %02x\n", reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mxc4005_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct mxc4005_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, &mxc4005_regmap_config);
|
||||
if (IS_ERR(regmap)) {
|
||||
dev_err(&client->dev, "failed to initialize regmap\n");
|
||||
return PTR_ERR(regmap);
|
||||
}
|
||||
|
||||
data = iio_priv(indio_dev);
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
data->dev = &client->dev;
|
||||
data->regmap = regmap;
|
||||
|
||||
ret = mxc4005_chip_init(data);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "failed to initialize chip\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
mutex_init(&data->mutex);
|
||||
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->channels = mxc4005_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(mxc4005_channels);
|
||||
indio_dev->available_scan_masks = mxc4005_scan_masks;
|
||||
indio_dev->name = MXC4005_DRV_NAME;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &mxc4005_info;
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev,
|
||||
iio_pollfunc_store_time,
|
||||
mxc4005_trigger_handler,
|
||||
NULL);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev,
|
||||
"failed to setup iio triggered buffer\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (client->irq < 0)
|
||||
client->irq = mxc4005_gpio_probe(client, data);
|
||||
|
||||
if (client->irq > 0) {
|
||||
data->dready_trig = devm_iio_trigger_alloc(&client->dev,
|
||||
"%s-dev%d",
|
||||
indio_dev->name,
|
||||
indio_dev->id);
|
||||
if (!data->dready_trig)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = devm_request_threaded_irq(&client->dev, client->irq,
|
||||
iio_trigger_generic_data_rdy_poll,
|
||||
NULL,
|
||||
IRQF_TRIGGER_FALLING |
|
||||
IRQF_ONESHOT,
|
||||
MXC4005_IRQ_NAME,
|
||||
data->dready_trig);
|
||||
if (ret) {
|
||||
dev_err(&client->dev,
|
||||
"failed to init threaded irq\n");
|
||||
goto err_buffer_cleanup;
|
||||
}
|
||||
|
||||
data->dready_trig->dev.parent = &client->dev;
|
||||
data->dready_trig->ops = &mxc4005_trigger_ops;
|
||||
iio_trigger_set_drvdata(data->dready_trig, indio_dev);
|
||||
indio_dev->trig = data->dready_trig;
|
||||
iio_trigger_get(indio_dev->trig);
|
||||
ret = iio_trigger_register(data->dready_trig);
|
||||
if (ret) {
|
||||
dev_err(&client->dev,
|
||||
"failed to register trigger\n");
|
||||
goto err_trigger_unregister;
|
||||
}
|
||||
}
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev,
|
||||
"unable to register iio device %d\n", ret);
|
||||
goto err_buffer_cleanup;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_trigger_unregister:
|
||||
iio_trigger_unregister(data->dready_trig);
|
||||
err_buffer_cleanup:
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mxc4005_remove(struct i2c_client *client)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
struct mxc4005_data *data = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
if (data->dready_trig)
|
||||
iio_trigger_unregister(data->dready_trig);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct acpi_device_id mxc4005_acpi_match[] = {
|
||||
{"MXC4005", 0},
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, mxc4005_acpi_match);
|
||||
|
||||
static const struct i2c_device_id mxc4005_id[] = {
|
||||
{"mxc4005", 0},
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, mxc4005_id);
|
||||
|
||||
static struct i2c_driver mxc4005_driver = {
|
||||
.driver = {
|
||||
.name = MXC4005_DRV_NAME,
|
||||
.acpi_match_table = ACPI_PTR(mxc4005_acpi_match),
|
||||
},
|
||||
.probe = mxc4005_probe,
|
||||
.remove = mxc4005_remove,
|
||||
.id_table = mxc4005_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(mxc4005_driver);
|
||||
|
||||
MODULE_AUTHOR("Teodora Baluta <teodora.baluta@intel.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("MXC4005 3-axis accelerometer driver");
|
@ -618,6 +618,7 @@ static const struct iio_info accel_info = {
|
||||
.attrs = &st_accel_attribute_group,
|
||||
.read_raw = &st_accel_read_raw,
|
||||
.write_raw = &st_accel_write_raw,
|
||||
.debugfs_reg_access = &st_sensors_debugfs_reg_access,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_IIO_TRIGGER
|
||||
|
@ -149,6 +149,17 @@ config BERLIN2_ADC
|
||||
Marvell Berlin2 ADC driver. This ADC has 8 channels, with one used for
|
||||
temperature measurement.
|
||||
|
||||
config CC10001_ADC
|
||||
tristate "Cosmic Circuits 10001 ADC driver"
|
||||
depends on HAS_IOMEM && HAVE_CLK && REGULATOR
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
Say yes here to build support for Cosmic Circuits 10001 ADC.
|
||||
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called cc10001_adc.
|
||||
|
||||
config DA9150_GPADC
|
||||
tristate "Dialog DA9150 GPADC driver support"
|
||||
depends on MFD_DA9150
|
||||
@ -161,17 +172,6 @@ config DA9150_GPADC
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called berlin2-adc.
|
||||
|
||||
config CC10001_ADC
|
||||
tristate "Cosmic Circuits 10001 ADC driver"
|
||||
depends on HAS_IOMEM && HAVE_CLK && REGULATOR
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
Say yes here to build support for Cosmic Circuits 10001 ADC.
|
||||
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called cc10001_adc.
|
||||
|
||||
config EXYNOS_ADC
|
||||
tristate "Exynos ADC driver support"
|
||||
depends on ARCH_EXYNOS || ARCH_S3C24XX || ARCH_S3C64XX || (OF && COMPILE_TEST)
|
||||
@ -183,6 +183,17 @@ config EXYNOS_ADC
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called exynos_adc.
|
||||
|
||||
config HI8435
|
||||
tristate "Holt Integrated Circuits HI-8435 threshold detector"
|
||||
select IIO_TRIGGERED_EVENT
|
||||
depends on SPI
|
||||
help
|
||||
If you say yes here you get support for Holt Integrated Circuits
|
||||
HI-8435 chip.
|
||||
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called hi8435.
|
||||
|
||||
config LP8788_ADC
|
||||
tristate "LP8788 ADC driver"
|
||||
depends on MFD_LP8788
|
||||
@ -361,6 +372,8 @@ config TWL6030_GPADC
|
||||
config VF610_ADC
|
||||
tristate "Freescale vf610 ADC driver"
|
||||
depends on OF
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
Say yes here to support for Vybrid board analog-to-digital converter.
|
||||
Since the IP is used for i.MX6SLX, the driver also support i.MX6SLX.
|
||||
|
@ -16,9 +16,10 @@ obj-$(CONFIG_AD799X) += ad799x.o
|
||||
obj-$(CONFIG_AT91_ADC) += at91_adc.o
|
||||
obj-$(CONFIG_AXP288_ADC) += axp288_adc.o
|
||||
obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o
|
||||
obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o
|
||||
obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o
|
||||
obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o
|
||||
obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
|
||||
obj-$(CONFIG_HI8435) += hi8435.o
|
||||
obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
|
||||
obj-$(CONFIG_MAX1027) += max1027.o
|
||||
obj-$(CONFIG_MAX1363) += max1363.o
|
||||
|
@ -27,13 +27,13 @@
|
||||
#define BERLIN2_SM_CTRL_SM_SOC_INT BIT(1)
|
||||
#define BERLIN2_SM_CTRL_SOC_SM_INT BIT(2)
|
||||
#define BERLIN2_SM_CTRL_ADC_SEL(x) ((x) << 5) /* 0-15 */
|
||||
#define BERLIN2_SM_CTRL_ADC_SEL_MASK (0xf << 5)
|
||||
#define BERLIN2_SM_CTRL_ADC_SEL_MASK GENMASK(8, 5)
|
||||
#define BERLIN2_SM_CTRL_ADC_POWER BIT(9)
|
||||
#define BERLIN2_SM_CTRL_ADC_CLKSEL_DIV2 (0x0 << 10)
|
||||
#define BERLIN2_SM_CTRL_ADC_CLKSEL_DIV3 (0x1 << 10)
|
||||
#define BERLIN2_SM_CTRL_ADC_CLKSEL_DIV4 (0x2 << 10)
|
||||
#define BERLIN2_SM_CTRL_ADC_CLKSEL_DIV8 (0x3 << 10)
|
||||
#define BERLIN2_SM_CTRL_ADC_CLKSEL_MASK (0x3 << 10)
|
||||
#define BERLIN2_SM_CTRL_ADC_CLKSEL_MASK GENMASK(11, 10)
|
||||
#define BERLIN2_SM_CTRL_ADC_START BIT(12)
|
||||
#define BERLIN2_SM_CTRL_ADC_RESET BIT(13)
|
||||
#define BERLIN2_SM_CTRL_ADC_BANDGAP_RDY BIT(14)
|
||||
@ -50,7 +50,7 @@
|
||||
#define BERLIN2_SM_CTRL_TSEN_MODE_10_50 (0x1 << 22) /* 10-50 C */
|
||||
#define BERLIN2_SM_CTRL_TSEN_RESET BIT(29)
|
||||
#define BERLIN2_SM_ADC_DATA 0x20
|
||||
#define BERLIN2_SM_ADC_MASK 0x3ff
|
||||
#define BERLIN2_SM_ADC_MASK GENMASK(9, 0)
|
||||
#define BERLIN2_SM_ADC_STATUS 0x1c
|
||||
#define BERLIN2_SM_ADC_STATUS_DATA_RDY(x) BIT(x) /* 0-15 */
|
||||
#define BERLIN2_SM_ADC_STATUS_DATA_RDY_MASK GENMASK(15, 0)
|
||||
@ -65,9 +65,9 @@
|
||||
#define BERLIN2_SM_TSEN_CTRL_START BIT(8)
|
||||
#define BERLIN2_SM_TSEN_CTRL_SETTLING_4 (0x0 << 21) /* 4 us */
|
||||
#define BERLIN2_SM_TSEN_CTRL_SETTLING_12 (0x1 << 21) /* 12 us */
|
||||
#define BERLIN2_SM_TSEN_CTRL_SETTLING_MASK (0x1 << 21)
|
||||
#define BERLIN2_SM_TSEN_CTRL_SETTLING_MASK BIT(21)
|
||||
#define BERLIN2_SM_TSEN_CTRL_TRIM(x) ((x) << 22)
|
||||
#define BERLIN2_SM_TSEN_CTRL_TRIM_MASK (0xf << 22)
|
||||
#define BERLIN2_SM_TSEN_CTRL_TRIM_MASK GENMASK(25, 22)
|
||||
|
||||
struct berlin2_adc_priv {
|
||||
struct regmap *regmap;
|
||||
@ -78,13 +78,13 @@ struct berlin2_adc_priv {
|
||||
};
|
||||
|
||||
#define BERLIN2_ADC_CHANNEL(n, t) \
|
||||
{ \
|
||||
.channel = n, \
|
||||
.datasheet_name = "channel"#n, \
|
||||
.type = t, \
|
||||
.indexed = 1, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
}
|
||||
{ \
|
||||
.channel = n, \
|
||||
.datasheet_name = "channel"#n, \
|
||||
.type = t, \
|
||||
.indexed = 1, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec berlin2_adc_channels[] = {
|
||||
BERLIN2_ADC_CHANNEL(0, IIO_VOLTAGE), /* external input */
|
||||
@ -111,18 +111,24 @@ static int berlin2_adc_read(struct iio_dev *indio_dev, int channel)
|
||||
|
||||
mutex_lock(&priv->lock);
|
||||
|
||||
/* Enable the interrupts */
|
||||
regmap_write(priv->regmap, BERLIN2_SM_ADC_STATUS,
|
||||
BERLIN2_SM_ADC_STATUS_INT_EN(channel));
|
||||
|
||||
/* Configure the ADC */
|
||||
regmap_update_bits(priv->regmap, BERLIN2_SM_CTRL,
|
||||
BERLIN2_SM_CTRL_ADC_RESET | BERLIN2_SM_CTRL_ADC_SEL_MASK
|
||||
| BERLIN2_SM_CTRL_ADC_START,
|
||||
BERLIN2_SM_CTRL_ADC_SEL(channel) | BERLIN2_SM_CTRL_ADC_START);
|
||||
BERLIN2_SM_CTRL_ADC_RESET |
|
||||
BERLIN2_SM_CTRL_ADC_SEL_MASK |
|
||||
BERLIN2_SM_CTRL_ADC_START,
|
||||
BERLIN2_SM_CTRL_ADC_SEL(channel) |
|
||||
BERLIN2_SM_CTRL_ADC_START);
|
||||
|
||||
ret = wait_event_interruptible_timeout(priv->wq, priv->data_available,
|
||||
msecs_to_jiffies(1000));
|
||||
msecs_to_jiffies(1000));
|
||||
|
||||
/* Disable the interrupts */
|
||||
regmap_update_bits(priv->regmap, BERLIN2_SM_ADC_STATUS,
|
||||
BERLIN2_SM_ADC_STATUS_INT_EN(channel), 0);
|
||||
BERLIN2_SM_ADC_STATUS_INT_EN(channel), 0);
|
||||
|
||||
if (ret == 0)
|
||||
ret = -ETIMEDOUT;
|
||||
@ -132,7 +138,7 @@ static int berlin2_adc_read(struct iio_dev *indio_dev, int channel)
|
||||
}
|
||||
|
||||
regmap_update_bits(priv->regmap, BERLIN2_SM_CTRL,
|
||||
BERLIN2_SM_CTRL_ADC_START, 0);
|
||||
BERLIN2_SM_CTRL_ADC_START, 0);
|
||||
|
||||
data = priv->data;
|
||||
priv->data_available = false;
|
||||
@ -149,24 +155,31 @@ static int berlin2_adc_tsen_read(struct iio_dev *indio_dev)
|
||||
|
||||
mutex_lock(&priv->lock);
|
||||
|
||||
/* Enable interrupts */
|
||||
regmap_write(priv->regmap, BERLIN2_SM_TSEN_STATUS,
|
||||
BERLIN2_SM_TSEN_STATUS_INT_EN);
|
||||
|
||||
/* Configure the ADC */
|
||||
regmap_update_bits(priv->regmap, BERLIN2_SM_CTRL,
|
||||
BERLIN2_SM_CTRL_TSEN_RESET | BERLIN2_SM_CTRL_ADC_ROTATE,
|
||||
BERLIN2_SM_CTRL_ADC_ROTATE);
|
||||
BERLIN2_SM_CTRL_TSEN_RESET |
|
||||
BERLIN2_SM_CTRL_ADC_ROTATE,
|
||||
BERLIN2_SM_CTRL_ADC_ROTATE);
|
||||
|
||||
/* Configure the temperature sensor */
|
||||
regmap_update_bits(priv->regmap, BERLIN2_SM_TSEN_CTRL,
|
||||
BERLIN2_SM_TSEN_CTRL_TRIM_MASK | BERLIN2_SM_TSEN_CTRL_SETTLING_MASK
|
||||
| BERLIN2_SM_TSEN_CTRL_START,
|
||||
BERLIN2_SM_TSEN_CTRL_TRIM(3) | BERLIN2_SM_TSEN_CTRL_SETTLING_12
|
||||
| BERLIN2_SM_TSEN_CTRL_START);
|
||||
BERLIN2_SM_TSEN_CTRL_TRIM_MASK |
|
||||
BERLIN2_SM_TSEN_CTRL_SETTLING_MASK |
|
||||
BERLIN2_SM_TSEN_CTRL_START,
|
||||
BERLIN2_SM_TSEN_CTRL_TRIM(3) |
|
||||
BERLIN2_SM_TSEN_CTRL_SETTLING_12 |
|
||||
BERLIN2_SM_TSEN_CTRL_START);
|
||||
|
||||
ret = wait_event_interruptible_timeout(priv->wq, priv->data_available,
|
||||
msecs_to_jiffies(1000));
|
||||
msecs_to_jiffies(1000));
|
||||
|
||||
/* Disable interrupts */
|
||||
regmap_update_bits(priv->regmap, BERLIN2_SM_TSEN_STATUS,
|
||||
BERLIN2_SM_TSEN_STATUS_INT_EN, 0);
|
||||
BERLIN2_SM_TSEN_STATUS_INT_EN, 0);
|
||||
|
||||
if (ret == 0)
|
||||
ret = -ETIMEDOUT;
|
||||
@ -176,7 +189,7 @@ static int berlin2_adc_tsen_read(struct iio_dev *indio_dev)
|
||||
}
|
||||
|
||||
regmap_update_bits(priv->regmap, BERLIN2_SM_TSEN_CTRL,
|
||||
BERLIN2_SM_TSEN_CTRL_START, 0);
|
||||
BERLIN2_SM_TSEN_CTRL_START, 0);
|
||||
|
||||
data = priv->data;
|
||||
priv->data_available = false;
|
||||
@ -187,10 +200,9 @@ static int berlin2_adc_tsen_read(struct iio_dev *indio_dev)
|
||||
}
|
||||
|
||||
static int berlin2_adc_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val, int *val2,
|
||||
long mask)
|
||||
struct iio_chan_spec const *chan, int *val,
|
||||
int *val2, long mask)
|
||||
{
|
||||
struct berlin2_adc_priv *priv = iio_priv(indio_dev);
|
||||
int temp;
|
||||
|
||||
switch (mask) {
|
||||
@ -198,10 +210,6 @@ static int berlin2_adc_read_raw(struct iio_dev *indio_dev,
|
||||
if (chan->type != IIO_VOLTAGE)
|
||||
return -EINVAL;
|
||||
|
||||
/* Enable the interrupts */
|
||||
regmap_write(priv->regmap, BERLIN2_SM_ADC_STATUS,
|
||||
BERLIN2_SM_ADC_STATUS_INT_EN(chan->channel));
|
||||
|
||||
*val = berlin2_adc_read(indio_dev, chan->channel);
|
||||
if (*val < 0)
|
||||
return *val;
|
||||
@ -211,10 +219,6 @@ static int berlin2_adc_read_raw(struct iio_dev *indio_dev,
|
||||
if (chan->type != IIO_TEMP)
|
||||
return -EINVAL;
|
||||
|
||||
/* Enable interrupts */
|
||||
regmap_write(priv->regmap, BERLIN2_SM_TSEN_STATUS,
|
||||
BERLIN2_SM_TSEN_STATUS_INT_EN);
|
||||
|
||||
temp = berlin2_adc_tsen_read(indio_dev);
|
||||
if (temp < 0)
|
||||
return temp;
|
||||
@ -306,12 +310,12 @@ static int berlin2_adc_probe(struct platform_device *pdev)
|
||||
return tsen_irq;
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, irq, berlin2_adc_irq, 0,
|
||||
pdev->dev.driver->name, indio_dev);
|
||||
pdev->dev.driver->name, indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, tsen_irq, berlin2_adc_tsen_irq,
|
||||
0, pdev->dev.driver->name, indio_dev);
|
||||
0, pdev->dev.driver->name, indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -328,13 +332,14 @@ static int berlin2_adc_probe(struct platform_device *pdev)
|
||||
|
||||
/* Power up the ADC */
|
||||
regmap_update_bits(priv->regmap, BERLIN2_SM_CTRL,
|
||||
BERLIN2_SM_CTRL_ADC_POWER, BERLIN2_SM_CTRL_ADC_POWER);
|
||||
BERLIN2_SM_CTRL_ADC_POWER,
|
||||
BERLIN2_SM_CTRL_ADC_POWER);
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret) {
|
||||
/* Power down the ADC */
|
||||
regmap_update_bits(priv->regmap, BERLIN2_SM_CTRL,
|
||||
BERLIN2_SM_CTRL_ADC_POWER, 0);
|
||||
BERLIN2_SM_CTRL_ADC_POWER, 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -350,7 +355,7 @@ static int berlin2_adc_remove(struct platform_device *pdev)
|
||||
|
||||
/* Power down the ADC */
|
||||
regmap_update_bits(priv->regmap, BERLIN2_SM_CTRL,
|
||||
BERLIN2_SM_CTRL_ADC_POWER, 0);
|
||||
BERLIN2_SM_CTRL_ADC_POWER, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
534
drivers/iio/adc/hi8435.c
Normal file
534
drivers/iio/adc/hi8435.c
Normal file
@ -0,0 +1,534 @@
|
||||
/*
|
||||
* Holt Integrated Circuits HI-8435 threshold detector driver
|
||||
*
|
||||
* Copyright (C) 2015 Zodiac Inflight Innovations
|
||||
* Copyright (C) 2015 Cogent Embedded, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <linux/delay.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_event.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
|
||||
#define DRV_NAME "hi8435"
|
||||
|
||||
/* Register offsets for HI-8435 */
|
||||
#define HI8435_CTRL_REG 0x02
|
||||
#define HI8435_PSEN_REG 0x04
|
||||
#define HI8435_TMDATA_REG 0x1E
|
||||
#define HI8435_GOCENHYS_REG 0x3A
|
||||
#define HI8435_SOCENHYS_REG 0x3C
|
||||
#define HI8435_SO7_0_REG 0x10
|
||||
#define HI8435_SO15_8_REG 0x12
|
||||
#define HI8435_SO23_16_REG 0x14
|
||||
#define HI8435_SO31_24_REG 0x16
|
||||
#define HI8435_SO31_0_REG 0x78
|
||||
|
||||
#define HI8435_WRITE_OPCODE 0x00
|
||||
#define HI8435_READ_OPCODE 0x80
|
||||
|
||||
/* CTRL register bits */
|
||||
#define HI8435_CTRL_TEST 0x01
|
||||
#define HI8435_CTRL_SRST 0x02
|
||||
|
||||
struct hi8435_priv {
|
||||
struct spi_device *spi;
|
||||
struct mutex lock;
|
||||
|
||||
unsigned long event_scan_mask; /* soft mask/unmask channels events */
|
||||
unsigned int event_prev_val;
|
||||
|
||||
unsigned threshold_lo[2]; /* GND-Open and Supply-Open thresholds */
|
||||
unsigned threshold_hi[2]; /* GND-Open and Supply-Open thresholds */
|
||||
u8 reg_buffer[3] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
static int hi8435_readb(struct hi8435_priv *priv, u8 reg, u8 *val)
|
||||
{
|
||||
reg |= HI8435_READ_OPCODE;
|
||||
return spi_write_then_read(priv->spi, ®, 1, val, 1);
|
||||
}
|
||||
|
||||
static int hi8435_readw(struct hi8435_priv *priv, u8 reg, u16 *val)
|
||||
{
|
||||
int ret;
|
||||
__be16 be_val;
|
||||
|
||||
reg |= HI8435_READ_OPCODE;
|
||||
ret = spi_write_then_read(priv->spi, ®, 1, &be_val, 2);
|
||||
*val = be16_to_cpu(be_val);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hi8435_readl(struct hi8435_priv *priv, u8 reg, u32 *val)
|
||||
{
|
||||
int ret;
|
||||
__be32 be_val;
|
||||
|
||||
reg |= HI8435_READ_OPCODE;
|
||||
ret = spi_write_then_read(priv->spi, ®, 1, &be_val, 4);
|
||||
*val = be32_to_cpu(be_val);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hi8435_writeb(struct hi8435_priv *priv, u8 reg, u8 val)
|
||||
{
|
||||
priv->reg_buffer[0] = reg | HI8435_WRITE_OPCODE;
|
||||
priv->reg_buffer[1] = val;
|
||||
|
||||
return spi_write(priv->spi, priv->reg_buffer, 2);
|
||||
}
|
||||
|
||||
static int hi8435_writew(struct hi8435_priv *priv, u8 reg, u16 val)
|
||||
{
|
||||
priv->reg_buffer[0] = reg | HI8435_WRITE_OPCODE;
|
||||
priv->reg_buffer[1] = (val >> 8) & 0xff;
|
||||
priv->reg_buffer[2] = val & 0xff;
|
||||
|
||||
return spi_write(priv->spi, priv->reg_buffer, 3);
|
||||
}
|
||||
|
||||
static int hi8435_read_event_config(struct iio_dev *idev,
|
||||
const struct iio_chan_spec *chan,
|
||||
enum iio_event_type type,
|
||||
enum iio_event_direction dir)
|
||||
{
|
||||
struct hi8435_priv *priv = iio_priv(idev);
|
||||
|
||||
return !!(priv->event_scan_mask & BIT(chan->channel));
|
||||
}
|
||||
|
||||
static int hi8435_write_event_config(struct iio_dev *idev,
|
||||
const struct iio_chan_spec *chan,
|
||||
enum iio_event_type type,
|
||||
enum iio_event_direction dir, int state)
|
||||
{
|
||||
struct hi8435_priv *priv = iio_priv(idev);
|
||||
|
||||
priv->event_scan_mask &= ~BIT(chan->channel);
|
||||
if (state)
|
||||
priv->event_scan_mask |= BIT(chan->channel);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hi8435_read_event_value(struct iio_dev *idev,
|
||||
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 hi8435_priv *priv = iio_priv(idev);
|
||||
int ret;
|
||||
u8 mode, psen;
|
||||
u16 reg;
|
||||
|
||||
ret = hi8435_readb(priv, HI8435_PSEN_REG, &psen);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Supply-Open or GND-Open sensing mode */
|
||||
mode = !!(psen & BIT(chan->channel / 8));
|
||||
|
||||
ret = hi8435_readw(priv, mode ? HI8435_SOCENHYS_REG :
|
||||
HI8435_GOCENHYS_REG, ®);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (dir == IIO_EV_DIR_FALLING)
|
||||
*val = ((reg & 0xff) - (reg >> 8)) / 2;
|
||||
else if (dir == IIO_EV_DIR_RISING)
|
||||
*val = ((reg & 0xff) + (reg >> 8)) / 2;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
static int hi8435_write_event_value(struct iio_dev *idev,
|
||||
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 hi8435_priv *priv = iio_priv(idev);
|
||||
int ret;
|
||||
u8 mode, psen;
|
||||
u16 reg;
|
||||
|
||||
ret = hi8435_readb(priv, HI8435_PSEN_REG, &psen);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Supply-Open or GND-Open sensing mode */
|
||||
mode = !!(psen & BIT(chan->channel / 8));
|
||||
|
||||
ret = hi8435_readw(priv, mode ? HI8435_SOCENHYS_REG :
|
||||
HI8435_GOCENHYS_REG, ®);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (dir == IIO_EV_DIR_FALLING) {
|
||||
/* falling threshold range 2..21V, hysteresis minimum 2V */
|
||||
if (val < 2 || val > 21 || (val + 2) > priv->threshold_hi[mode])
|
||||
return -EINVAL;
|
||||
|
||||
if (val == priv->threshold_lo[mode])
|
||||
return 0;
|
||||
|
||||
priv->threshold_lo[mode] = val;
|
||||
|
||||
/* hysteresis must not be odd */
|
||||
if ((priv->threshold_hi[mode] - priv->threshold_lo[mode]) % 2)
|
||||
priv->threshold_hi[mode]--;
|
||||
} else if (dir == IIO_EV_DIR_RISING) {
|
||||
/* rising threshold range 3..22V, hysteresis minimum 2V */
|
||||
if (val < 3 || val > 22 || val < (priv->threshold_lo[mode] + 2))
|
||||
return -EINVAL;
|
||||
|
||||
if (val == priv->threshold_hi[mode])
|
||||
return 0;
|
||||
|
||||
priv->threshold_hi[mode] = val;
|
||||
|
||||
/* hysteresis must not be odd */
|
||||
if ((priv->threshold_hi[mode] - priv->threshold_lo[mode]) % 2)
|
||||
priv->threshold_lo[mode]++;
|
||||
}
|
||||
|
||||
/* program thresholds */
|
||||
mutex_lock(&priv->lock);
|
||||
|
||||
ret = hi8435_readw(priv, mode ? HI8435_SOCENHYS_REG :
|
||||
HI8435_GOCENHYS_REG, ®);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&priv->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* hysteresis */
|
||||
reg = priv->threshold_hi[mode] - priv->threshold_lo[mode];
|
||||
reg <<= 8;
|
||||
/* threshold center */
|
||||
reg |= (priv->threshold_hi[mode] + priv->threshold_lo[mode]);
|
||||
|
||||
ret = hi8435_writew(priv, mode ? HI8435_SOCENHYS_REG :
|
||||
HI8435_GOCENHYS_REG, reg);
|
||||
|
||||
mutex_unlock(&priv->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hi8435_debugfs_reg_access(struct iio_dev *idev,
|
||||
unsigned reg, unsigned writeval,
|
||||
unsigned *readval)
|
||||
{
|
||||
struct hi8435_priv *priv = iio_priv(idev);
|
||||
int ret;
|
||||
u8 val;
|
||||
|
||||
if (readval != NULL) {
|
||||
ret = hi8435_readb(priv, reg, &val);
|
||||
*readval = val;
|
||||
} else {
|
||||
val = (u8)writeval;
|
||||
ret = hi8435_writeb(priv, reg, val);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct iio_event_spec hi8435_events[] = {
|
||||
{
|
||||
.type = IIO_EV_TYPE_THRESH,
|
||||
.dir = IIO_EV_DIR_RISING,
|
||||
.mask_separate = BIT(IIO_EV_INFO_VALUE),
|
||||
}, {
|
||||
.type = IIO_EV_TYPE_THRESH,
|
||||
.dir = IIO_EV_DIR_FALLING,
|
||||
.mask_separate = BIT(IIO_EV_INFO_VALUE),
|
||||
}, {
|
||||
.type = IIO_EV_TYPE_THRESH,
|
||||
.dir = IIO_EV_DIR_EITHER,
|
||||
.mask_separate = BIT(IIO_EV_INFO_ENABLE),
|
||||
},
|
||||
};
|
||||
|
||||
static int hi8435_get_sensing_mode(struct iio_dev *idev,
|
||||
const struct iio_chan_spec *chan)
|
||||
{
|
||||
struct hi8435_priv *priv = iio_priv(idev);
|
||||
int ret;
|
||||
u8 reg;
|
||||
|
||||
ret = hi8435_readb(priv, HI8435_PSEN_REG, ®);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return !!(reg & BIT(chan->channel / 8));
|
||||
}
|
||||
|
||||
static int hi8435_set_sensing_mode(struct iio_dev *idev,
|
||||
const struct iio_chan_spec *chan,
|
||||
unsigned int mode)
|
||||
{
|
||||
struct hi8435_priv *priv = iio_priv(idev);
|
||||
int ret;
|
||||
u8 reg;
|
||||
|
||||
mutex_lock(&priv->lock);
|
||||
|
||||
ret = hi8435_readb(priv, HI8435_PSEN_REG, ®);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&priv->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
reg &= ~BIT(chan->channel / 8);
|
||||
if (mode)
|
||||
reg |= BIT(chan->channel / 8);
|
||||
|
||||
ret = hi8435_writeb(priv, HI8435_PSEN_REG, reg);
|
||||
|
||||
mutex_unlock(&priv->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const char * const hi8435_sensing_modes[] = { "GND-Open",
|
||||
"Supply-Open" };
|
||||
|
||||
static const struct iio_enum hi8435_sensing_mode = {
|
||||
.items = hi8435_sensing_modes,
|
||||
.num_items = ARRAY_SIZE(hi8435_sensing_modes),
|
||||
.get = hi8435_get_sensing_mode,
|
||||
.set = hi8435_set_sensing_mode,
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec_ext_info hi8435_ext_info[] = {
|
||||
IIO_ENUM("sensing_mode", IIO_SEPARATE, &hi8435_sensing_mode),
|
||||
{},
|
||||
};
|
||||
|
||||
#define HI8435_VOLTAGE_CHANNEL(num) \
|
||||
{ \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = num, \
|
||||
.event_spec = hi8435_events, \
|
||||
.num_event_specs = ARRAY_SIZE(hi8435_events), \
|
||||
.ext_info = hi8435_ext_info, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec hi8435_channels[] = {
|
||||
HI8435_VOLTAGE_CHANNEL(0),
|
||||
HI8435_VOLTAGE_CHANNEL(1),
|
||||
HI8435_VOLTAGE_CHANNEL(2),
|
||||
HI8435_VOLTAGE_CHANNEL(3),
|
||||
HI8435_VOLTAGE_CHANNEL(4),
|
||||
HI8435_VOLTAGE_CHANNEL(5),
|
||||
HI8435_VOLTAGE_CHANNEL(6),
|
||||
HI8435_VOLTAGE_CHANNEL(7),
|
||||
HI8435_VOLTAGE_CHANNEL(8),
|
||||
HI8435_VOLTAGE_CHANNEL(9),
|
||||
HI8435_VOLTAGE_CHANNEL(10),
|
||||
HI8435_VOLTAGE_CHANNEL(11),
|
||||
HI8435_VOLTAGE_CHANNEL(12),
|
||||
HI8435_VOLTAGE_CHANNEL(13),
|
||||
HI8435_VOLTAGE_CHANNEL(14),
|
||||
HI8435_VOLTAGE_CHANNEL(15),
|
||||
HI8435_VOLTAGE_CHANNEL(16),
|
||||
HI8435_VOLTAGE_CHANNEL(17),
|
||||
HI8435_VOLTAGE_CHANNEL(18),
|
||||
HI8435_VOLTAGE_CHANNEL(19),
|
||||
HI8435_VOLTAGE_CHANNEL(20),
|
||||
HI8435_VOLTAGE_CHANNEL(21),
|
||||
HI8435_VOLTAGE_CHANNEL(22),
|
||||
HI8435_VOLTAGE_CHANNEL(23),
|
||||
HI8435_VOLTAGE_CHANNEL(24),
|
||||
HI8435_VOLTAGE_CHANNEL(25),
|
||||
HI8435_VOLTAGE_CHANNEL(26),
|
||||
HI8435_VOLTAGE_CHANNEL(27),
|
||||
HI8435_VOLTAGE_CHANNEL(28),
|
||||
HI8435_VOLTAGE_CHANNEL(29),
|
||||
HI8435_VOLTAGE_CHANNEL(30),
|
||||
HI8435_VOLTAGE_CHANNEL(31),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(32),
|
||||
};
|
||||
|
||||
static const struct iio_info hi8435_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_event_config = &hi8435_read_event_config,
|
||||
.write_event_config = hi8435_write_event_config,
|
||||
.read_event_value = &hi8435_read_event_value,
|
||||
.write_event_value = &hi8435_write_event_value,
|
||||
.debugfs_reg_access = &hi8435_debugfs_reg_access,
|
||||
};
|
||||
|
||||
static void hi8435_iio_push_event(struct iio_dev *idev, unsigned int val)
|
||||
{
|
||||
struct hi8435_priv *priv = iio_priv(idev);
|
||||
enum iio_event_direction dir;
|
||||
unsigned int i;
|
||||
unsigned int status = priv->event_prev_val ^ val;
|
||||
|
||||
if (!status)
|
||||
return;
|
||||
|
||||
for_each_set_bit(i, &priv->event_scan_mask, 32) {
|
||||
if (status & BIT(i)) {
|
||||
dir = val & BIT(i) ? IIO_EV_DIR_RISING :
|
||||
IIO_EV_DIR_FALLING;
|
||||
iio_push_event(idev,
|
||||
IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, i,
|
||||
IIO_EV_TYPE_THRESH, dir),
|
||||
iio_get_time_ns());
|
||||
}
|
||||
}
|
||||
|
||||
priv->event_prev_val = val;
|
||||
}
|
||||
|
||||
static irqreturn_t hi8435_trigger_handler(int irq, void *private)
|
||||
{
|
||||
struct iio_poll_func *pf = private;
|
||||
struct iio_dev *idev = pf->indio_dev;
|
||||
struct hi8435_priv *priv = iio_priv(idev);
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
ret = hi8435_readl(priv, HI8435_SO31_0_REG, &val);
|
||||
if (ret < 0)
|
||||
goto err_read;
|
||||
|
||||
hi8435_iio_push_event(idev, val);
|
||||
|
||||
err_read:
|
||||
iio_trigger_notify_done(idev->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int hi8435_probe(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *idev;
|
||||
struct hi8435_priv *priv;
|
||||
struct gpio_desc *reset_gpio;
|
||||
int ret;
|
||||
|
||||
idev = devm_iio_device_alloc(&spi->dev, sizeof(*priv));
|
||||
if (!idev)
|
||||
return -ENOMEM;
|
||||
|
||||
priv = iio_priv(idev);
|
||||
priv->spi = spi;
|
||||
|
||||
reset_gpio = devm_gpiod_get(&spi->dev, NULL, GPIOD_OUT_LOW);
|
||||
if (IS_ERR(reset_gpio)) {
|
||||
/* chip s/w reset if h/w reset failed */
|
||||
hi8435_writeb(priv, HI8435_CTRL_REG, HI8435_CTRL_SRST);
|
||||
hi8435_writeb(priv, HI8435_CTRL_REG, 0);
|
||||
} else {
|
||||
udelay(5);
|
||||
gpiod_set_value(reset_gpio, 1);
|
||||
}
|
||||
|
||||
spi_set_drvdata(spi, idev);
|
||||
mutex_init(&priv->lock);
|
||||
|
||||
idev->dev.parent = &spi->dev;
|
||||
idev->name = spi_get_device_id(spi)->name;
|
||||
idev->modes = INDIO_DIRECT_MODE;
|
||||
idev->info = &hi8435_info;
|
||||
idev->channels = hi8435_channels;
|
||||
idev->num_channels = ARRAY_SIZE(hi8435_channels);
|
||||
|
||||
/* unmask all events */
|
||||
priv->event_scan_mask = ~(0);
|
||||
/*
|
||||
* There is a restriction in the chip - the hysteresis can not be odd.
|
||||
* If the hysteresis is set to odd value then chip gets into lock state
|
||||
* and not functional anymore.
|
||||
* After chip reset the thresholds are in undefined state, so we need to
|
||||
* initialize thresholds to some initial values and then prevent
|
||||
* userspace setting odd hysteresis.
|
||||
*
|
||||
* Set threshold low voltage to 2V, threshold high voltage to 4V
|
||||
* for both GND-Open and Supply-Open sensing modes.
|
||||
*/
|
||||
priv->threshold_lo[0] = priv->threshold_lo[1] = 2;
|
||||
priv->threshold_hi[0] = priv->threshold_hi[1] = 4;
|
||||
hi8435_writew(priv, HI8435_GOCENHYS_REG, 0x206);
|
||||
hi8435_writew(priv, HI8435_SOCENHYS_REG, 0x206);
|
||||
|
||||
ret = iio_triggered_event_setup(idev, NULL, hi8435_trigger_handler);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = iio_device_register(idev);
|
||||
if (ret < 0) {
|
||||
dev_err(&spi->dev, "unable to register device\n");
|
||||
goto unregister_triggered_event;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
unregister_triggered_event:
|
||||
iio_triggered_event_cleanup(idev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hi8435_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *idev = spi_get_drvdata(spi);
|
||||
|
||||
iio_device_unregister(idev);
|
||||
iio_triggered_event_cleanup(idev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id hi8435_dt_ids[] = {
|
||||
{ .compatible = "holt,hi8435" },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, hi8435_dt_ids);
|
||||
|
||||
static const struct spi_device_id hi8435_id[] = {
|
||||
{ "hi8435", 0},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, hi8435_id);
|
||||
|
||||
static struct spi_driver hi8435_driver = {
|
||||
.driver = {
|
||||
.name = DRV_NAME,
|
||||
.of_match_table = of_match_ptr(hi8435_dt_ids),
|
||||
},
|
||||
.probe = hi8435_probe,
|
||||
.remove = hi8435_remove,
|
||||
.id_table = hi8435_id,
|
||||
};
|
||||
module_spi_driver(hi8435_driver);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Vladimir Barinov");
|
||||
MODULE_DESCRIPTION("HI-8435 threshold detector");
|
@ -508,6 +508,7 @@ static int max1027_remove(struct spi_device *spi)
|
||||
static struct spi_driver max1027_driver = {
|
||||
.driver = {
|
||||
.name = "max1027",
|
||||
.of_match_table = of_match_ptr(max1027_adc_dt_ids),
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = max1027_probe,
|
||||
|
@ -404,6 +404,7 @@ MODULE_DEVICE_TABLE(spi, mcp320x_id);
|
||||
static struct spi_driver mcp320x_driver = {
|
||||
.driver = {
|
||||
.name = "mcp320x",
|
||||
.of_match_table = of_match_ptr(mcp320x_dt_ids),
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = mcp320x_probe,
|
||||
|
@ -174,6 +174,13 @@ static int adc128_remove(struct spi_device *spi)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id adc128_of_match[] = {
|
||||
{ .compatible = "ti,adc128s052", },
|
||||
{ .compatible = "ti,adc122s021", },
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, adc128_of_match);
|
||||
|
||||
static const struct spi_device_id adc128_id[] = {
|
||||
{ "adc128s052", 0}, /* index into adc128_config */
|
||||
{ "adc122s021", 1},
|
||||
@ -184,6 +191,7 @@ MODULE_DEVICE_TABLE(spi, adc128_id);
|
||||
static struct spi_driver adc128_driver = {
|
||||
.driver = {
|
||||
.name = "adc128s052",
|
||||
.of_match_table = of_match_ptr(adc128_of_match),
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = adc128_probe,
|
||||
|
@ -875,6 +875,7 @@ static const struct of_device_id of_twl6030_match_tbl[] = {
|
||||
},
|
||||
{ /* end */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, of_twl6030_match_tbl);
|
||||
|
||||
static int twl6030_gpadc_probe(struct platform_device *pdev)
|
||||
{
|
||||
|
@ -34,8 +34,11 @@
|
||||
#include <linux/err.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/driver.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
|
||||
/* This will be the driver name the kernel reports */
|
||||
#define DRIVER_NAME "vf610-adc"
|
||||
@ -170,6 +173,7 @@ struct vf610_adc {
|
||||
u32 sample_freq_avail[5];
|
||||
|
||||
struct completion completion;
|
||||
u16 buffer[8];
|
||||
};
|
||||
|
||||
static const u32 vf610_hw_avgs[] = { 1, 4, 8, 16, 32 };
|
||||
@ -505,12 +509,24 @@ static const struct iio_chan_spec_ext_info vf610_ext_info[] = {
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ), \
|
||||
.ext_info = vf610_ext_info, \
|
||||
.scan_index = (_idx), \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = 12, \
|
||||
.storagebits = 16, \
|
||||
}, \
|
||||
}
|
||||
|
||||
#define VF610_ADC_TEMPERATURE_CHAN(_idx, _chan_type) { \
|
||||
.type = (_chan_type), \
|
||||
.channel = (_idx), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \
|
||||
.scan_index = (_idx), \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = 12, \
|
||||
.storagebits = 16, \
|
||||
}, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec vf610_adc_iio_channels[] = {
|
||||
@ -531,6 +547,7 @@ static const struct iio_chan_spec vf610_adc_iio_channels[] = {
|
||||
VF610_ADC_CHAN(14, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(15, IIO_VOLTAGE),
|
||||
VF610_ADC_TEMPERATURE_CHAN(26, IIO_TEMP),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(32),
|
||||
/* sentinel */
|
||||
};
|
||||
|
||||
@ -559,13 +576,20 @@ static int vf610_adc_read_data(struct vf610_adc *info)
|
||||
|
||||
static irqreturn_t vf610_adc_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct vf610_adc *info = (struct vf610_adc *)dev_id;
|
||||
struct iio_dev *indio_dev = (struct iio_dev *)dev_id;
|
||||
struct vf610_adc *info = iio_priv(indio_dev);
|
||||
int coco;
|
||||
|
||||
coco = readl(info->regs + VF610_REG_ADC_HS);
|
||||
if (coco & VF610_ADC_HS_COCO0) {
|
||||
info->value = vf610_adc_read_data(info);
|
||||
complete(&info->completion);
|
||||
if (iio_buffer_enabled(indio_dev)) {
|
||||
info->buffer[0] = info->value;
|
||||
iio_push_to_buffers_with_timestamp(indio_dev,
|
||||
info->buffer, iio_get_time_ns());
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
} else
|
||||
complete(&info->completion);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
@ -613,8 +637,12 @@ static int vf610_read_raw(struct iio_dev *indio_dev,
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
case IIO_CHAN_INFO_PROCESSED:
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
reinit_completion(&info->completion);
|
||||
if (iio_buffer_enabled(indio_dev)) {
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
reinit_completion(&info->completion);
|
||||
hc_cfg = VF610_ADC_ADCHC(chan->channel);
|
||||
hc_cfg |= VF610_ADC_AIEN;
|
||||
writel(hc_cfg, info->regs + VF610_REG_ADC_HC0);
|
||||
@ -694,6 +722,56 @@ static int vf610_write_raw(struct iio_dev *indio_dev,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int vf610_adc_buffer_postenable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct vf610_adc *info = iio_priv(indio_dev);
|
||||
unsigned int channel;
|
||||
int ret;
|
||||
int val;
|
||||
|
||||
ret = iio_triggered_buffer_postenable(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
val = readl(info->regs + VF610_REG_ADC_GC);
|
||||
val |= VF610_ADC_ADCON;
|
||||
writel(val, info->regs + VF610_REG_ADC_GC);
|
||||
|
||||
channel = find_first_bit(indio_dev->active_scan_mask,
|
||||
indio_dev->masklength);
|
||||
|
||||
val = VF610_ADC_ADCHC(channel);
|
||||
val |= VF610_ADC_AIEN;
|
||||
|
||||
writel(val, info->regs + VF610_REG_ADC_HC0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vf610_adc_buffer_predisable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct vf610_adc *info = iio_priv(indio_dev);
|
||||
unsigned int hc_cfg = 0;
|
||||
int val;
|
||||
|
||||
val = readl(info->regs + VF610_REG_ADC_GC);
|
||||
val &= ~VF610_ADC_ADCON;
|
||||
writel(val, info->regs + VF610_REG_ADC_GC);
|
||||
|
||||
hc_cfg |= VF610_ADC_CONV_DISABLE;
|
||||
hc_cfg &= ~VF610_ADC_AIEN;
|
||||
|
||||
writel(hc_cfg, info->regs + VF610_REG_ADC_HC0);
|
||||
|
||||
return iio_triggered_buffer_predisable(indio_dev);
|
||||
}
|
||||
|
||||
static const struct iio_buffer_setup_ops iio_triggered_buffer_setup_ops = {
|
||||
.postenable = &vf610_adc_buffer_postenable,
|
||||
.predisable = &vf610_adc_buffer_predisable,
|
||||
.validate_scan_mask = &iio_validate_scan_mask_onehot,
|
||||
};
|
||||
|
||||
static int vf610_adc_reg_access(struct iio_dev *indio_dev,
|
||||
unsigned reg, unsigned writeval,
|
||||
unsigned *readval)
|
||||
@ -753,7 +831,7 @@ static int vf610_adc_probe(struct platform_device *pdev)
|
||||
|
||||
ret = devm_request_irq(info->dev, irq,
|
||||
vf610_adc_isr, 0,
|
||||
dev_name(&pdev->dev), info);
|
||||
dev_name(&pdev->dev), indio_dev);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed requesting irq, irq = %d\n", irq);
|
||||
return ret;
|
||||
@ -806,15 +884,23 @@ static int vf610_adc_probe(struct platform_device *pdev)
|
||||
vf610_adc_cfg_init(info);
|
||||
vf610_adc_hw_init(info);
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
|
||||
NULL, &iio_triggered_buffer_setup_ops);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "Couldn't initialise the buffer\n");
|
||||
goto error_iio_device_register;
|
||||
}
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Couldn't register the device.\n");
|
||||
goto error_iio_device_register;
|
||||
goto error_adc_buffer_init;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
|
||||
error_adc_buffer_init:
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
error_iio_device_register:
|
||||
clk_disable_unprepare(info->clk);
|
||||
error_adc_clk_enable:
|
||||
@ -829,6 +915,7 @@ static int vf610_adc_remove(struct platform_device *pdev)
|
||||
struct vf610_adc *info = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
regulator_disable(info->vref);
|
||||
clk_disable_unprepare(info->clk);
|
||||
|
||||
|
@ -273,33 +273,13 @@ static void xadc_zynq_unmask_worker(struct work_struct *work)
|
||||
schedule_delayed_work(&xadc->zynq_unmask_work,
|
||||
msecs_to_jiffies(XADC_ZYNQ_UNMASK_TIMEOUT));
|
||||
}
|
||||
}
|
||||
|
||||
static irqreturn_t xadc_zynq_threaded_interrupt_handler(int irq, void *devid)
|
||||
{
|
||||
struct iio_dev *indio_dev = devid;
|
||||
struct xadc *xadc = iio_priv(indio_dev);
|
||||
unsigned int alarm;
|
||||
|
||||
spin_lock_irq(&xadc->lock);
|
||||
alarm = xadc->zynq_alarm;
|
||||
xadc->zynq_alarm = 0;
|
||||
spin_unlock_irq(&xadc->lock);
|
||||
|
||||
xadc_handle_events(indio_dev, xadc_zynq_transform_alarm(alarm));
|
||||
|
||||
/* unmask the required interrupts in timer. */
|
||||
schedule_delayed_work(&xadc->zynq_unmask_work,
|
||||
msecs_to_jiffies(XADC_ZYNQ_UNMASK_TIMEOUT));
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t xadc_zynq_interrupt_handler(int irq, void *devid)
|
||||
{
|
||||
struct iio_dev *indio_dev = devid;
|
||||
struct xadc *xadc = iio_priv(indio_dev);
|
||||
irqreturn_t ret = IRQ_HANDLED;
|
||||
uint32_t status;
|
||||
|
||||
xadc_read_reg(xadc, XADC_ZYNQ_REG_INTSTS, &status);
|
||||
@ -321,18 +301,23 @@ static irqreturn_t xadc_zynq_interrupt_handler(int irq, void *devid)
|
||||
|
||||
status &= XADC_ZYNQ_INT_ALARM_MASK;
|
||||
if (status) {
|
||||
xadc->zynq_alarm |= status;
|
||||
xadc->zynq_masked_alarm |= status;
|
||||
/*
|
||||
* mask the current event interrupt,
|
||||
* unmask it when the interrupt is no more active.
|
||||
*/
|
||||
xadc_zynq_update_intmsk(xadc, 0, 0);
|
||||
ret = IRQ_WAKE_THREAD;
|
||||
|
||||
xadc_handle_events(indio_dev,
|
||||
xadc_zynq_transform_alarm(status));
|
||||
|
||||
/* unmask the required interrupts in timer. */
|
||||
schedule_delayed_work(&xadc->zynq_unmask_work,
|
||||
msecs_to_jiffies(XADC_ZYNQ_UNMASK_TIMEOUT));
|
||||
}
|
||||
spin_unlock(&xadc->lock);
|
||||
|
||||
return ret;
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
#define XADC_ZYNQ_TCK_RATE_MAX 50000000
|
||||
@ -437,7 +422,6 @@ static const struct xadc_ops xadc_zynq_ops = {
|
||||
.setup = xadc_zynq_setup,
|
||||
.get_dclk_rate = xadc_zynq_get_dclk_rate,
|
||||
.interrupt_handler = xadc_zynq_interrupt_handler,
|
||||
.threaded_interrupt_handler = xadc_zynq_threaded_interrupt_handler,
|
||||
.update_alarm = xadc_zynq_update_alarm,
|
||||
};
|
||||
|
||||
@ -1225,9 +1209,8 @@ static int xadc_probe(struct platform_device *pdev)
|
||||
if (ret)
|
||||
goto err_free_samplerate_trigger;
|
||||
|
||||
ret = request_threaded_irq(irq, xadc->ops->interrupt_handler,
|
||||
xadc->ops->threaded_interrupt_handler,
|
||||
0, dev_name(&pdev->dev), indio_dev);
|
||||
ret = request_irq(irq, xadc->ops->interrupt_handler, 0,
|
||||
dev_name(&pdev->dev), indio_dev);
|
||||
if (ret)
|
||||
goto err_clk_disable_unprepare;
|
||||
|
||||
|
@ -60,7 +60,6 @@ struct xadc {
|
||||
|
||||
enum xadc_external_mux_mode external_mux_mode;
|
||||
|
||||
unsigned int zynq_alarm;
|
||||
unsigned int zynq_masked_alarm;
|
||||
unsigned int zynq_intmask;
|
||||
struct delayed_work zynq_unmask_work;
|
||||
@ -79,7 +78,6 @@ struct xadc_ops {
|
||||
void (*update_alarm)(struct xadc *, unsigned int);
|
||||
unsigned long (*get_dclk_rate)(struct xadc *);
|
||||
irqreturn_t (*interrupt_handler)(int, void *);
|
||||
irqreturn_t (*threaded_interrupt_handler)(int, void *);
|
||||
|
||||
unsigned int flags;
|
||||
};
|
||||
|
@ -195,6 +195,7 @@ static const struct spi_device_id ad8366_id[] = {
|
||||
{"ad8366", 0},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ad8366_id);
|
||||
|
||||
static struct spi_driver ad8366_driver = {
|
||||
.driver = {
|
||||
|
24
drivers/iio/buffer/Kconfig
Normal file
24
drivers/iio/buffer/Kconfig
Normal file
@ -0,0 +1,24 @@
|
||||
#
|
||||
# Industrial I/O generic buffer implementations
|
||||
#
|
||||
# When adding new entries keep the list in alphabetical order
|
||||
|
||||
config IIO_BUFFER_CB
|
||||
tristate "IIO callback buffer used for push in-kernel interfaces"
|
||||
help
|
||||
Should be selected by any drivers that do in-kernel push
|
||||
usage. That is, those where the data is pushed to the consumer.
|
||||
|
||||
config IIO_KFIFO_BUF
|
||||
tristate "Industrial I/O buffering based on kfifo"
|
||||
help
|
||||
A simple fifo based on kfifo. Note that this currently provides
|
||||
no buffer events so it is up to userspace to work out how
|
||||
often to read from the buffer.
|
||||
|
||||
config IIO_TRIGGERED_BUFFER
|
||||
tristate
|
||||
select IIO_TRIGGER
|
||||
select IIO_KFIFO_BUF
|
||||
help
|
||||
Provides helper functions for setting up triggered buffers.
|
8
drivers/iio/buffer/Makefile
Normal file
8
drivers/iio/buffer/Makefile
Normal file
@ -0,0 +1,8 @@
|
||||
#
|
||||
# Makefile for the industrial I/O buffer implementations
|
||||
#
|
||||
|
||||
# When adding new entries keep the list in alphabetical order
|
||||
obj-$(CONFIG_IIO_BUFFER_CB) += industrialio-buffer-cb.o
|
||||
obj-$(CONFIG_IIO_TRIGGERED_BUFFER) += industrialio-triggered-buffer.o
|
||||
obj-$(CONFIG_IIO_KFIFO_BUF) += kfifo_buf.o
|
@ -1,4 +1,12 @@
|
||||
/* The industrial I/O callback buffer
|
||||
*
|
||||
* 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/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/export.h>
|
||||
@ -124,3 +132,7 @@ struct iio_channel
|
||||
return cb_buffer->channels;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iio_channel_cb_get_channels);
|
||||
|
||||
MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>");
|
||||
MODULE_DESCRIPTION("Industrial I/O callback buffer");
|
||||
MODULE_LICENSE("GPL");
|
15
drivers/iio/chemical/Kconfig
Normal file
15
drivers/iio/chemical/Kconfig
Normal file
@ -0,0 +1,15 @@
|
||||
#
|
||||
# Chemical sensors
|
||||
#
|
||||
|
||||
menu "Chemical Sensors"
|
||||
|
||||
config VZ89X
|
||||
tristate "SGX Sensortech MiCS VZ89X VOC sensor"
|
||||
depends on I2C
|
||||
help
|
||||
Say Y here to build I2C interface support for the SGX
|
||||
Sensortech MiCS VZ89X VOC (Volatile Organic Compounds)
|
||||
sensors
|
||||
|
||||
endmenu
|
6
drivers/iio/chemical/Makefile
Normal file
6
drivers/iio/chemical/Makefile
Normal file
@ -0,0 +1,6 @@
|
||||
#
|
||||
# Makefile for IIO chemical sensors
|
||||
#
|
||||
|
||||
# When adding new entries keep the list in alphabetical order
|
||||
obj-$(CONFIG_VZ89X) += vz89x.o
|
237
drivers/iio/chemical/vz89x.c
Normal file
237
drivers/iio/chemical/vz89x.c
Normal file
@ -0,0 +1,237 @@
|
||||
/*
|
||||
* vz89x.c - Support for SGX Sensortech MiCS VZ89X VOC sensors
|
||||
*
|
||||
* Copyright (C) 2015 Matt Ranostay <mranostay@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>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/i2c.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
#define VZ89X_REG_MEASUREMENT 0x09
|
||||
#define VZ89X_REG_MEASUREMENT_SIZE 6
|
||||
|
||||
#define VZ89X_VOC_CO2_IDX 0
|
||||
#define VZ89X_VOC_SHORT_IDX 1
|
||||
#define VZ89X_VOC_TVOC_IDX 2
|
||||
#define VZ89X_VOC_RESISTANCE_IDX 3
|
||||
|
||||
struct vz89x_data {
|
||||
struct i2c_client *client;
|
||||
struct mutex lock;
|
||||
unsigned long last_update;
|
||||
|
||||
u8 buffer[VZ89X_REG_MEASUREMENT_SIZE];
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec vz89x_channels[] = {
|
||||
{
|
||||
.type = IIO_CONCENTRATION,
|
||||
.channel2 = IIO_MOD_CO2,
|
||||
.modified = 1,
|
||||
.info_mask_separate =
|
||||
BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_RAW),
|
||||
.address = VZ89X_VOC_CO2_IDX,
|
||||
},
|
||||
{
|
||||
.type = IIO_CONCENTRATION,
|
||||
.channel2 = IIO_MOD_VOC,
|
||||
.modified = 1,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.address = VZ89X_VOC_SHORT_IDX,
|
||||
.extend_name = "short",
|
||||
},
|
||||
{
|
||||
.type = IIO_CONCENTRATION,
|
||||
.channel2 = IIO_MOD_VOC,
|
||||
.modified = 1,
|
||||
.info_mask_separate =
|
||||
BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_RAW),
|
||||
.address = VZ89X_VOC_TVOC_IDX,
|
||||
},
|
||||
{
|
||||
.type = IIO_RESISTANCE,
|
||||
.info_mask_separate =
|
||||
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
|
||||
.address = VZ89X_VOC_RESISTANCE_IDX,
|
||||
},
|
||||
};
|
||||
|
||||
static IIO_CONST_ATTR(in_concentration_co2_scale, "0.00000698689");
|
||||
static IIO_CONST_ATTR(in_concentration_voc_scale, "0.00000000436681223");
|
||||
|
||||
static struct attribute *vz89x_attributes[] = {
|
||||
&iio_const_attr_in_concentration_co2_scale.dev_attr.attr,
|
||||
&iio_const_attr_in_concentration_voc_scale.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group vz89x_attrs_group = {
|
||||
.attrs = vz89x_attributes,
|
||||
};
|
||||
|
||||
static int vz89x_get_measurement(struct vz89x_data *data)
|
||||
{
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
/* sensor can only be polled once a second max per datasheet */
|
||||
if (!time_after(jiffies, data->last_update + HZ))
|
||||
return 0;
|
||||
|
||||
ret = i2c_smbus_write_word_data(data->client,
|
||||
VZ89X_REG_MEASUREMENT, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < VZ89X_REG_MEASUREMENT_SIZE; i++) {
|
||||
ret = i2c_smbus_read_byte(data->client);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
data->buffer[i] = ret;
|
||||
}
|
||||
|
||||
data->last_update = jiffies;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vz89x_get_resistance_reading(struct vz89x_data *data)
|
||||
{
|
||||
u8 *buf = &data->buffer[VZ89X_VOC_TVOC_IDX];
|
||||
|
||||
return buf[0] | (buf[1] << 8) | (buf[2] << 16);
|
||||
}
|
||||
|
||||
static int vz89x_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val,
|
||||
int *val2, long mask)
|
||||
{
|
||||
struct vz89x_data *data = iio_priv(indio_dev);
|
||||
int ret = -EINVAL;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
mutex_lock(&data->lock);
|
||||
ret = vz89x_get_measurement(data);
|
||||
mutex_unlock(&data->lock);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
switch (chan->address) {
|
||||
case VZ89X_VOC_CO2_IDX:
|
||||
case VZ89X_VOC_SHORT_IDX:
|
||||
case VZ89X_VOC_TVOC_IDX:
|
||||
*val = data->buffer[chan->address];
|
||||
return IIO_VAL_INT;
|
||||
case VZ89X_VOC_RESISTANCE_IDX:
|
||||
*val = vz89x_get_resistance_reading(data);
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
switch (chan->type) {
|
||||
case IIO_RESISTANCE:
|
||||
*val = 10;
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
switch (chan->address) {
|
||||
case VZ89X_VOC_CO2_IDX:
|
||||
*val = 44;
|
||||
*val2 = 250000;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
case VZ89X_VOC_TVOC_IDX:
|
||||
*val = -13;
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct iio_info vz89x_info = {
|
||||
.attrs = &vz89x_attrs_group,
|
||||
.read_raw = vz89x_read_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int vz89x_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct vz89x_data *data;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA |
|
||||
I2C_FUNC_SMBUS_BYTE))
|
||||
return -ENODEV;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
data = iio_priv(indio_dev);
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
data->client = client;
|
||||
data->last_update = jiffies - HZ;
|
||||
mutex_init(&data->lock);
|
||||
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->info = &vz89x_info,
|
||||
indio_dev->name = dev_name(&client->dev);
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
indio_dev->channels = vz89x_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(vz89x_channels);
|
||||
|
||||
return devm_iio_device_register(&client->dev, indio_dev);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id vz89x_id[] = {
|
||||
{ "vz89x", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, vz89x_id);
|
||||
|
||||
static const struct of_device_id vz89x_dt_ids[] = {
|
||||
{ .compatible = "sgx,vz89x" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, vz89x_dt_ids);
|
||||
|
||||
static struct i2c_driver vz89x_driver = {
|
||||
.driver = {
|
||||
.name = "vz89x",
|
||||
.of_match_table = of_match_ptr(vz89x_dt_ids),
|
||||
},
|
||||
.probe = vz89x_probe,
|
||||
.id_table = vz89x_id,
|
||||
};
|
||||
module_i2c_driver(vz89x_driver);
|
||||
|
||||
MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
|
||||
MODULE_DESCRIPTION("SGX Sensortech MiCS VZ89X VOC sensors");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -44,6 +44,28 @@ st_sensors_write_data_with_mask_error:
|
||||
return err;
|
||||
}
|
||||
|
||||
int st_sensors_debugfs_reg_access(struct iio_dev *indio_dev,
|
||||
unsigned reg, unsigned writeval,
|
||||
unsigned *readval)
|
||||
{
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
u8 readdata;
|
||||
int err;
|
||||
|
||||
if (!readval)
|
||||
return sdata->tf->write_byte(&sdata->tb, sdata->dev,
|
||||
(u8)reg, (u8)writeval);
|
||||
|
||||
err = sdata->tf->read_byte(&sdata->tb, sdata->dev, (u8)reg, &readdata);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
*readval = (unsigned)readdata;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(st_sensors_debugfs_reg_access);
|
||||
|
||||
static int st_sensors_match_odr(struct st_sensor_settings *sensor_settings,
|
||||
unsigned int odr, struct st_sensor_odr_avl *odr_out)
|
||||
{
|
||||
|
@ -281,6 +281,12 @@ static int ad7303_remove(struct spi_device *spi)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id ad7303_spi_of_match[] = {
|
||||
{ .compatible = "adi,ad7303", },
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ad7303_spi_of_match);
|
||||
|
||||
static const struct spi_device_id ad7303_spi_ids[] = {
|
||||
{ "ad7303", 0 },
|
||||
{}
|
||||
@ -290,6 +296,7 @@ MODULE_DEVICE_TABLE(spi, ad7303_spi_ids);
|
||||
static struct spi_driver ad7303_driver = {
|
||||
.driver = {
|
||||
.name = "ad7303",
|
||||
.of_match_table = of_match_ptr(ad7303_spi_of_match),
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = ad7303_probe,
|
||||
|
@ -387,6 +387,7 @@ static const struct of_device_id max5821_of_match[] = {
|
||||
{ .compatible = "maxim,max5821" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, max5821_of_match);
|
||||
|
||||
static struct i2c_driver max5821_driver = {
|
||||
.driver = {
|
||||
|
@ -616,15 +616,24 @@ static int adf4350_remove(struct spi_device *spi)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id adf4350_of_match[] = {
|
||||
{ .compatible = "adi,adf4350", },
|
||||
{ .compatible = "adi,adf4351", },
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, adf4350_of_match);
|
||||
|
||||
static const struct spi_device_id adf4350_id[] = {
|
||||
{"adf4350", 4350},
|
||||
{"adf4351", 4351},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, adf4350_id);
|
||||
|
||||
static struct spi_driver adf4350_driver = {
|
||||
.driver = {
|
||||
.name = "adf4350",
|
||||
.of_match_table = of_match_ptr(adf4350_of_match),
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = adf4350_probe,
|
||||
|
@ -52,15 +52,26 @@ config ADXRS450
|
||||
|
||||
config BMG160
|
||||
tristate "BOSCH BMG160 Gyro Sensor"
|
||||
depends on I2C
|
||||
depends on (I2C || SPI_MASTER)
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
select BMG160_I2C if (I2C)
|
||||
select BMG160_SPI if (SPI)
|
||||
help
|
||||
Say yes here to build support for Bosch BMG160 Tri-axis Gyro Sensor
|
||||
driver. This driver also supports BMI055 gyroscope.
|
||||
Say yes here to build support for BOSCH BMG160 Tri-axis Gyro Sensor
|
||||
driver connected via I2C or SPI. This driver also supports BMI055
|
||||
gyroscope.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called bmg160.
|
||||
will be called bmg160_i2c or bmg160_spi.
|
||||
|
||||
config BMG160_I2C
|
||||
tristate
|
||||
select REGMAP_I2C
|
||||
|
||||
config BMG160_SPI
|
||||
tristate
|
||||
select REGMAP_SPI
|
||||
|
||||
config HID_SENSOR_GYRO_3D
|
||||
depends on HID_SENSOR_HUB
|
||||
|
@ -8,7 +8,9 @@ obj-$(CONFIG_ADIS16130) += adis16130.o
|
||||
obj-$(CONFIG_ADIS16136) += adis16136.o
|
||||
obj-$(CONFIG_ADIS16260) += adis16260.o
|
||||
obj-$(CONFIG_ADXRS450) += adxrs450.o
|
||||
obj-$(CONFIG_BMG160) += bmg160.o
|
||||
obj-$(CONFIG_BMG160) += bmg160_core.o
|
||||
obj-$(CONFIG_BMG160_I2C) += bmg160_i2c.o
|
||||
obj-$(CONFIG_BMG160_SPI) += bmg160_spi.o
|
||||
|
||||
obj-$(CONFIG_HID_SENSOR_GYRO_3D) += hid-sensor-gyro-3d.o
|
||||
|
||||
|
10
drivers/iio/gyro/bmg160.h
Normal file
10
drivers/iio/gyro/bmg160.h
Normal file
@ -0,0 +1,10 @@
|
||||
#ifndef BMG160_H_
|
||||
#define BMG160_H_
|
||||
|
||||
extern const struct dev_pm_ops bmg160_pm_ops;
|
||||
|
||||
int bmg160_core_probe(struct device *dev, struct regmap *regmap, int irq,
|
||||
const char *name);
|
||||
void bmg160_core_remove(struct device *dev);
|
||||
|
||||
#endif /* BMG160_H_ */
|
@ -13,7 +13,6 @@
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/slab.h>
|
||||
@ -28,8 +27,9 @@
|
||||
#include <linux/iio/events.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/regmap.h>
|
||||
#include "bmg160.h"
|
||||
|
||||
#define BMG160_DRV_NAME "bmg160"
|
||||
#define BMG160_IRQ_NAME "bmg160_event"
|
||||
#define BMG160_GPIO_NAME "gpio_int"
|
||||
|
||||
@ -97,7 +97,8 @@
|
||||
#define BMG160_AUTO_SUSPEND_DELAY_MS 2000
|
||||
|
||||
struct bmg160_data {
|
||||
struct i2c_client *client;
|
||||
struct device *dev;
|
||||
struct regmap *regmap;
|
||||
struct iio_trigger *dready_trig;
|
||||
struct iio_trigger *motion_trig;
|
||||
struct mutex mutex;
|
||||
@ -108,6 +109,7 @@ struct bmg160_data {
|
||||
int slope_thres;
|
||||
bool dready_trigger_on;
|
||||
bool motion_trigger_on;
|
||||
int irq;
|
||||
};
|
||||
|
||||
enum bmg160_axis {
|
||||
@ -138,10 +140,9 @@ static int bmg160_set_mode(struct bmg160_data *data, u8 mode)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(data->client,
|
||||
BMG160_REG_PMU_LPW, mode);
|
||||
ret = regmap_write(data->regmap, BMG160_REG_PMU_LPW, mode);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev, "Error writing reg_pmu_lpw\n");
|
||||
dev_err(data->dev, "Error writing reg_pmu_lpw\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -169,10 +170,9 @@ static int bmg160_set_bw(struct bmg160_data *data, int val)
|
||||
if (bw_bits < 0)
|
||||
return bw_bits;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(data->client, BMG160_REG_PMU_BW,
|
||||
bw_bits);
|
||||
ret = regmap_write(data->regmap, BMG160_REG_PMU_BW, bw_bits);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev, "Error writing reg_pmu_bw\n");
|
||||
dev_err(data->dev, "Error writing reg_pmu_bw\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -184,16 +184,17 @@ static int bmg160_set_bw(struct bmg160_data *data, int val)
|
||||
static int bmg160_chip_init(struct bmg160_data *data)
|
||||
{
|
||||
int ret;
|
||||
unsigned int val;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(data->client, BMG160_REG_CHIP_ID);
|
||||
ret = regmap_read(data->regmap, BMG160_REG_CHIP_ID, &val);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev, "Error reading reg_chip_id\n");
|
||||
dev_err(data->dev, "Error reading reg_chip_id\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_dbg(&data->client->dev, "Chip Id %x\n", ret);
|
||||
if (ret != BMG160_CHIP_ID_VAL) {
|
||||
dev_err(&data->client->dev, "invalid chip %x\n", ret);
|
||||
dev_dbg(data->dev, "Chip Id %x\n", val);
|
||||
if (val != BMG160_CHIP_ID_VAL) {
|
||||
dev_err(data->dev, "invalid chip %x\n", val);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
@ -210,42 +211,33 @@ static int bmg160_chip_init(struct bmg160_data *data)
|
||||
return ret;
|
||||
|
||||
/* Set Default Range */
|
||||
ret = i2c_smbus_write_byte_data(data->client,
|
||||
BMG160_REG_RANGE,
|
||||
BMG160_RANGE_500DPS);
|
||||
ret = regmap_write(data->regmap, BMG160_REG_RANGE, BMG160_RANGE_500DPS);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev, "Error writing reg_range\n");
|
||||
dev_err(data->dev, "Error writing reg_range\n");
|
||||
return ret;
|
||||
}
|
||||
data->dps_range = BMG160_RANGE_500DPS;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(data->client, BMG160_REG_SLOPE_THRES);
|
||||
ret = regmap_read(data->regmap, BMG160_REG_SLOPE_THRES, &val);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev, "Error reading reg_slope_thres\n");
|
||||
dev_err(data->dev, "Error reading reg_slope_thres\n");
|
||||
return ret;
|
||||
}
|
||||
data->slope_thres = ret;
|
||||
data->slope_thres = val;
|
||||
|
||||
/* Set default interrupt mode */
|
||||
ret = i2c_smbus_read_byte_data(data->client, BMG160_REG_INT_EN_1);
|
||||
ret = regmap_update_bits(data->regmap, BMG160_REG_INT_EN_1,
|
||||
BMG160_INT1_BIT_OD, 0);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev, "Error reading reg_int_en_1\n");
|
||||
return ret;
|
||||
}
|
||||
ret &= ~BMG160_INT1_BIT_OD;
|
||||
ret = i2c_smbus_write_byte_data(data->client,
|
||||
BMG160_REG_INT_EN_1, ret);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev, "Error writing reg_int_en_1\n");
|
||||
dev_err(data->dev, "Error updating bits in reg_int_en_1\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = i2c_smbus_write_byte_data(data->client,
|
||||
BMG160_REG_INT_RST_LATCH,
|
||||
BMG160_INT_MODE_LATCH_INT |
|
||||
BMG160_INT_MODE_LATCH_RESET);
|
||||
ret = regmap_write(data->regmap, BMG160_REG_INT_RST_LATCH,
|
||||
BMG160_INT_MODE_LATCH_INT |
|
||||
BMG160_INT_MODE_LATCH_RESET);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev,
|
||||
dev_err(data->dev,
|
||||
"Error writing reg_motion_intr\n");
|
||||
return ret;
|
||||
}
|
||||
@ -259,17 +251,17 @@ static int bmg160_set_power_state(struct bmg160_data *data, bool on)
|
||||
int ret;
|
||||
|
||||
if (on)
|
||||
ret = pm_runtime_get_sync(&data->client->dev);
|
||||
ret = pm_runtime_get_sync(data->dev);
|
||||
else {
|
||||
pm_runtime_mark_last_busy(&data->client->dev);
|
||||
ret = pm_runtime_put_autosuspend(&data->client->dev);
|
||||
pm_runtime_mark_last_busy(data->dev);
|
||||
ret = pm_runtime_put_autosuspend(data->dev);
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev,
|
||||
dev_err(data->dev,
|
||||
"Failed: bmg160_set_power_state for %d\n", on);
|
||||
if (on)
|
||||
pm_runtime_put_noidle(&data->client->dev);
|
||||
pm_runtime_put_noidle(data->dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -284,43 +276,30 @@ static int bmg160_setup_any_motion_interrupt(struct bmg160_data *data,
|
||||
int ret;
|
||||
|
||||
/* Enable/Disable INT_MAP0 mapping */
|
||||
ret = i2c_smbus_read_byte_data(data->client, BMG160_REG_INT_MAP_0);
|
||||
ret = regmap_update_bits(data->regmap, BMG160_REG_INT_MAP_0,
|
||||
BMG160_INT_MAP_0_BIT_ANY,
|
||||
(status ? BMG160_INT_MAP_0_BIT_ANY : 0));
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev, "Error reading reg_int_map0\n");
|
||||
return ret;
|
||||
}
|
||||
if (status)
|
||||
ret |= BMG160_INT_MAP_0_BIT_ANY;
|
||||
else
|
||||
ret &= ~BMG160_INT_MAP_0_BIT_ANY;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(data->client,
|
||||
BMG160_REG_INT_MAP_0,
|
||||
ret);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev, "Error writing reg_int_map0\n");
|
||||
dev_err(data->dev, "Error updating bits reg_int_map0\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Enable/Disable slope interrupts */
|
||||
if (status) {
|
||||
/* Update slope thres */
|
||||
ret = i2c_smbus_write_byte_data(data->client,
|
||||
BMG160_REG_SLOPE_THRES,
|
||||
data->slope_thres);
|
||||
ret = regmap_write(data->regmap, BMG160_REG_SLOPE_THRES,
|
||||
data->slope_thres);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev,
|
||||
dev_err(data->dev,
|
||||
"Error writing reg_slope_thres\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = i2c_smbus_write_byte_data(data->client,
|
||||
BMG160_REG_MOTION_INTR,
|
||||
BMG160_INT_MOTION_X |
|
||||
BMG160_INT_MOTION_Y |
|
||||
BMG160_INT_MOTION_Z);
|
||||
ret = regmap_write(data->regmap, BMG160_REG_MOTION_INTR,
|
||||
BMG160_INT_MOTION_X | BMG160_INT_MOTION_Y |
|
||||
BMG160_INT_MOTION_Z);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev,
|
||||
dev_err(data->dev,
|
||||
"Error writing reg_motion_intr\n");
|
||||
return ret;
|
||||
}
|
||||
@ -331,28 +310,26 @@ static int bmg160_setup_any_motion_interrupt(struct bmg160_data *data,
|
||||
* to set latched mode, we will be flooded anyway with INTR
|
||||
*/
|
||||
if (!data->dready_trigger_on) {
|
||||
ret = i2c_smbus_write_byte_data(data->client,
|
||||
BMG160_REG_INT_RST_LATCH,
|
||||
BMG160_INT_MODE_LATCH_INT |
|
||||
BMG160_INT_MODE_LATCH_RESET);
|
||||
ret = regmap_write(data->regmap,
|
||||
BMG160_REG_INT_RST_LATCH,
|
||||
BMG160_INT_MODE_LATCH_INT |
|
||||
BMG160_INT_MODE_LATCH_RESET);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev,
|
||||
dev_err(data->dev,
|
||||
"Error writing reg_rst_latch\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = i2c_smbus_write_byte_data(data->client,
|
||||
BMG160_REG_INT_EN_0,
|
||||
BMG160_DATA_ENABLE_INT);
|
||||
ret = regmap_write(data->regmap, BMG160_REG_INT_EN_0,
|
||||
BMG160_DATA_ENABLE_INT);
|
||||
|
||||
} else
|
||||
ret = i2c_smbus_write_byte_data(data->client,
|
||||
BMG160_REG_INT_EN_0,
|
||||
0);
|
||||
} else {
|
||||
ret = regmap_write(data->regmap, BMG160_REG_INT_EN_0, 0);
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev, "Error writing reg_int_en0\n");
|
||||
dev_err(data->dev, "Error writing reg_int_en0\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -365,59 +342,43 @@ static int bmg160_setup_new_data_interrupt(struct bmg160_data *data,
|
||||
int ret;
|
||||
|
||||
/* Enable/Disable INT_MAP1 mapping */
|
||||
ret = i2c_smbus_read_byte_data(data->client, BMG160_REG_INT_MAP_1);
|
||||
ret = regmap_update_bits(data->regmap, BMG160_REG_INT_MAP_1,
|
||||
BMG160_INT_MAP_1_BIT_NEW_DATA,
|
||||
(status ? BMG160_INT_MAP_1_BIT_NEW_DATA : 0));
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev, "Error reading reg_int_map1\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (status)
|
||||
ret |= BMG160_INT_MAP_1_BIT_NEW_DATA;
|
||||
else
|
||||
ret &= ~BMG160_INT_MAP_1_BIT_NEW_DATA;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(data->client,
|
||||
BMG160_REG_INT_MAP_1,
|
||||
ret);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev, "Error writing reg_int_map1\n");
|
||||
dev_err(data->dev, "Error updating bits in reg_int_map1\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (status) {
|
||||
ret = i2c_smbus_write_byte_data(data->client,
|
||||
BMG160_REG_INT_RST_LATCH,
|
||||
BMG160_INT_MODE_NON_LATCH_INT |
|
||||
BMG160_INT_MODE_LATCH_RESET);
|
||||
ret = regmap_write(data->regmap, BMG160_REG_INT_RST_LATCH,
|
||||
BMG160_INT_MODE_NON_LATCH_INT |
|
||||
BMG160_INT_MODE_LATCH_RESET);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev,
|
||||
dev_err(data->dev,
|
||||
"Error writing reg_rst_latch\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = i2c_smbus_write_byte_data(data->client,
|
||||
BMG160_REG_INT_EN_0,
|
||||
BMG160_DATA_ENABLE_INT);
|
||||
ret = regmap_write(data->regmap, BMG160_REG_INT_EN_0,
|
||||
BMG160_DATA_ENABLE_INT);
|
||||
|
||||
} else {
|
||||
/* Restore interrupt mode */
|
||||
ret = i2c_smbus_write_byte_data(data->client,
|
||||
BMG160_REG_INT_RST_LATCH,
|
||||
BMG160_INT_MODE_LATCH_INT |
|
||||
BMG160_INT_MODE_LATCH_RESET);
|
||||
ret = regmap_write(data->regmap, BMG160_REG_INT_RST_LATCH,
|
||||
BMG160_INT_MODE_LATCH_INT |
|
||||
BMG160_INT_MODE_LATCH_RESET);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev,
|
||||
dev_err(data->dev,
|
||||
"Error writing reg_rst_latch\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = i2c_smbus_write_byte_data(data->client,
|
||||
BMG160_REG_INT_EN_0,
|
||||
0);
|
||||
ret = regmap_write(data->regmap, BMG160_REG_INT_EN_0, 0);
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev, "Error writing reg_int_en0\n");
|
||||
dev_err(data->dev, "Error writing reg_int_en0\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -444,12 +405,10 @@ static int bmg160_set_scale(struct bmg160_data *data, int val)
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(bmg160_scale_table); ++i) {
|
||||
if (bmg160_scale_table[i].scale == val) {
|
||||
ret = i2c_smbus_write_byte_data(
|
||||
data->client,
|
||||
BMG160_REG_RANGE,
|
||||
bmg160_scale_table[i].dps_range);
|
||||
ret = regmap_write(data->regmap, BMG160_REG_RANGE,
|
||||
bmg160_scale_table[i].dps_range);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev,
|
||||
dev_err(data->dev,
|
||||
"Error writing reg_range\n");
|
||||
return ret;
|
||||
}
|
||||
@ -464,6 +423,7 @@ static int bmg160_set_scale(struct bmg160_data *data, int val)
|
||||
static int bmg160_get_temp(struct bmg160_data *data, int *val)
|
||||
{
|
||||
int ret;
|
||||
unsigned int raw_val;
|
||||
|
||||
mutex_lock(&data->mutex);
|
||||
ret = bmg160_set_power_state(data, true);
|
||||
@ -472,15 +432,15 @@ static int bmg160_get_temp(struct bmg160_data *data, int *val)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = i2c_smbus_read_byte_data(data->client, BMG160_REG_TEMP);
|
||||
ret = regmap_read(data->regmap, BMG160_REG_TEMP, &raw_val);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev, "Error reading reg_temp\n");
|
||||
dev_err(data->dev, "Error reading reg_temp\n");
|
||||
bmg160_set_power_state(data, false);
|
||||
mutex_unlock(&data->mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*val = sign_extend32(ret, 7);
|
||||
*val = sign_extend32(raw_val, 7);
|
||||
ret = bmg160_set_power_state(data, false);
|
||||
mutex_unlock(&data->mutex);
|
||||
if (ret < 0)
|
||||
@ -492,6 +452,7 @@ static int bmg160_get_temp(struct bmg160_data *data, int *val)
|
||||
static int bmg160_get_axis(struct bmg160_data *data, int axis, int *val)
|
||||
{
|
||||
int ret;
|
||||
unsigned int raw_val;
|
||||
|
||||
mutex_lock(&data->mutex);
|
||||
ret = bmg160_set_power_state(data, true);
|
||||
@ -500,15 +461,16 @@ static int bmg160_get_axis(struct bmg160_data *data, int axis, int *val)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = i2c_smbus_read_word_data(data->client, BMG160_AXIS_TO_REG(axis));
|
||||
ret = regmap_bulk_read(data->regmap, BMG160_AXIS_TO_REG(axis), &raw_val,
|
||||
2);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev, "Error reading axis %d\n", axis);
|
||||
dev_err(data->dev, "Error reading axis %d\n", axis);
|
||||
bmg160_set_power_state(data, false);
|
||||
mutex_unlock(&data->mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*val = sign_extend32(ret, 15);
|
||||
*val = sign_extend32(raw_val, 15);
|
||||
ret = bmg160_set_power_state(data, false);
|
||||
mutex_unlock(&data->mutex);
|
||||
if (ret < 0)
|
||||
@ -807,12 +769,13 @@ static irqreturn_t bmg160_trigger_handler(int irq, void *p)
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct bmg160_data *data = iio_priv(indio_dev);
|
||||
int bit, ret, i = 0;
|
||||
unsigned int val;
|
||||
|
||||
mutex_lock(&data->mutex);
|
||||
for_each_set_bit(bit, indio_dev->active_scan_mask,
|
||||
indio_dev->masklength) {
|
||||
ret = i2c_smbus_read_word_data(data->client,
|
||||
BMG160_AXIS_TO_REG(bit));
|
||||
ret = regmap_bulk_read(data->regmap, BMG160_AXIS_TO_REG(bit),
|
||||
&val, 2);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&data->mutex);
|
||||
goto err;
|
||||
@ -840,12 +803,11 @@ static int bmg160_trig_try_reen(struct iio_trigger *trig)
|
||||
return 0;
|
||||
|
||||
/* Set latched mode interrupt and clear any latched interrupt */
|
||||
ret = i2c_smbus_write_byte_data(data->client,
|
||||
BMG160_REG_INT_RST_LATCH,
|
||||
BMG160_INT_MODE_LATCH_INT |
|
||||
BMG160_INT_MODE_LATCH_RESET);
|
||||
ret = regmap_write(data->regmap, BMG160_REG_INT_RST_LATCH,
|
||||
BMG160_INT_MODE_LATCH_INT |
|
||||
BMG160_INT_MODE_LATCH_RESET);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev, "Error writing reg_rst_latch\n");
|
||||
dev_err(data->dev, "Error writing reg_rst_latch\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -907,33 +869,34 @@ static irqreturn_t bmg160_event_handler(int irq, void *private)
|
||||
struct bmg160_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
int dir;
|
||||
unsigned int val;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(data->client, BMG160_REG_INT_STATUS_2);
|
||||
ret = regmap_read(data->regmap, BMG160_REG_INT_STATUS_2, &val);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev, "Error reading reg_int_status2\n");
|
||||
dev_err(data->dev, "Error reading reg_int_status2\n");
|
||||
goto ack_intr_status;
|
||||
}
|
||||
|
||||
if (ret & 0x08)
|
||||
if (val & 0x08)
|
||||
dir = IIO_EV_DIR_RISING;
|
||||
else
|
||||
dir = IIO_EV_DIR_FALLING;
|
||||
|
||||
if (ret & BMG160_ANY_MOTION_BIT_X)
|
||||
if (val & BMG160_ANY_MOTION_BIT_X)
|
||||
iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ANGL_VEL,
|
||||
0,
|
||||
IIO_MOD_X,
|
||||
IIO_EV_TYPE_ROC,
|
||||
dir),
|
||||
iio_get_time_ns());
|
||||
if (ret & BMG160_ANY_MOTION_BIT_Y)
|
||||
if (val & BMG160_ANY_MOTION_BIT_Y)
|
||||
iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ANGL_VEL,
|
||||
0,
|
||||
IIO_MOD_Y,
|
||||
IIO_EV_TYPE_ROC,
|
||||
dir),
|
||||
iio_get_time_ns());
|
||||
if (ret & BMG160_ANY_MOTION_BIT_Z)
|
||||
if (val & BMG160_ANY_MOTION_BIT_Z)
|
||||
iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ANGL_VEL,
|
||||
0,
|
||||
IIO_MOD_Z,
|
||||
@ -943,12 +906,11 @@ static irqreturn_t bmg160_event_handler(int irq, void *private)
|
||||
|
||||
ack_intr_status:
|
||||
if (!data->dready_trigger_on) {
|
||||
ret = i2c_smbus_write_byte_data(data->client,
|
||||
BMG160_REG_INT_RST_LATCH,
|
||||
BMG160_INT_MODE_LATCH_INT |
|
||||
BMG160_INT_MODE_LATCH_RESET);
|
||||
ret = regmap_write(data->regmap, BMG160_REG_INT_RST_LATCH,
|
||||
BMG160_INT_MODE_LATCH_INT |
|
||||
BMG160_INT_MODE_LATCH_RESET);
|
||||
if (ret < 0)
|
||||
dev_err(&data->client->dev,
|
||||
dev_err(data->dev,
|
||||
"Error writing reg_rst_latch\n");
|
||||
}
|
||||
|
||||
@ -993,18 +955,13 @@ static const struct iio_buffer_setup_ops bmg160_buffer_setup_ops = {
|
||||
.postdisable = bmg160_buffer_postdisable,
|
||||
};
|
||||
|
||||
static int bmg160_gpio_probe(struct i2c_client *client,
|
||||
struct bmg160_data *data)
|
||||
static int bmg160_gpio_probe(struct bmg160_data *data)
|
||||
|
||||
{
|
||||
struct device *dev;
|
||||
struct gpio_desc *gpio;
|
||||
int ret;
|
||||
|
||||
if (!client)
|
||||
return -EINVAL;
|
||||
|
||||
dev = &client->dev;
|
||||
dev = data->dev;
|
||||
|
||||
/* data ready gpio interrupt pin */
|
||||
gpio = devm_gpiod_get_index(dev, BMG160_GPIO_NAME, 0, GPIOD_IN);
|
||||
@ -1013,11 +970,12 @@ static int bmg160_gpio_probe(struct i2c_client *client,
|
||||
return PTR_ERR(gpio);
|
||||
}
|
||||
|
||||
ret = gpiod_to_irq(gpio);
|
||||
data->irq = gpiod_to_irq(gpio);
|
||||
|
||||
dev_dbg(dev, "GPIO resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret);
|
||||
dev_dbg(dev, "GPIO resource, no:%d irq:%d\n", desc_to_gpio(gpio),
|
||||
data->irq);
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *bmg160_match_acpi_device(struct device *dev)
|
||||
@ -1031,21 +989,22 @@ static const char *bmg160_match_acpi_device(struct device *dev)
|
||||
return dev_name(dev);
|
||||
}
|
||||
|
||||
static int bmg160_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
int bmg160_core_probe(struct device *dev, struct regmap *regmap, int irq,
|
||||
const char *name)
|
||||
{
|
||||
struct bmg160_data *data;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret;
|
||||
const char *name = NULL;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
||||
indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
data = iio_priv(indio_dev);
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
data->client = client;
|
||||
dev_set_drvdata(dev, indio_dev);
|
||||
data->dev = dev;
|
||||
data->irq = irq;
|
||||
data->regmap = regmap;
|
||||
|
||||
ret = bmg160_chip_init(data);
|
||||
if (ret < 0)
|
||||
@ -1053,25 +1012,22 @@ static int bmg160_probe(struct i2c_client *client,
|
||||
|
||||
mutex_init(&data->mutex);
|
||||
|
||||
if (id)
|
||||
name = id->name;
|
||||
if (ACPI_HANDLE(dev))
|
||||
name = bmg160_match_acpi_device(dev);
|
||||
|
||||
if (ACPI_HANDLE(&client->dev))
|
||||
name = bmg160_match_acpi_device(&client->dev);
|
||||
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->dev.parent = dev;
|
||||
indio_dev->channels = bmg160_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(bmg160_channels);
|
||||
indio_dev->name = name;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &bmg160_info;
|
||||
|
||||
if (client->irq <= 0)
|
||||
client->irq = bmg160_gpio_probe(client, data);
|
||||
if (data->irq <= 0)
|
||||
bmg160_gpio_probe(data);
|
||||
|
||||
if (client->irq > 0) {
|
||||
ret = devm_request_threaded_irq(&client->dev,
|
||||
client->irq,
|
||||
if (data->irq > 0) {
|
||||
ret = devm_request_threaded_irq(dev,
|
||||
data->irq,
|
||||
bmg160_data_rdy_trig_poll,
|
||||
bmg160_event_handler,
|
||||
IRQF_TRIGGER_RISING,
|
||||
@ -1080,28 +1036,28 @@ static int bmg160_probe(struct i2c_client *client,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
data->dready_trig = devm_iio_trigger_alloc(&client->dev,
|
||||
data->dready_trig = devm_iio_trigger_alloc(dev,
|
||||
"%s-dev%d",
|
||||
indio_dev->name,
|
||||
indio_dev->id);
|
||||
if (!data->dready_trig)
|
||||
return -ENOMEM;
|
||||
|
||||
data->motion_trig = devm_iio_trigger_alloc(&client->dev,
|
||||
data->motion_trig = devm_iio_trigger_alloc(dev,
|
||||
"%s-any-motion-dev%d",
|
||||
indio_dev->name,
|
||||
indio_dev->id);
|
||||
if (!data->motion_trig)
|
||||
return -ENOMEM;
|
||||
|
||||
data->dready_trig->dev.parent = &client->dev;
|
||||
data->dready_trig->dev.parent = dev;
|
||||
data->dready_trig->ops = &bmg160_trigger_ops;
|
||||
iio_trigger_set_drvdata(data->dready_trig, indio_dev);
|
||||
ret = iio_trigger_register(data->dready_trig);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
data->motion_trig->dev.parent = &client->dev;
|
||||
data->motion_trig->dev.parent = dev;
|
||||
data->motion_trig->ops = &bmg160_trigger_ops;
|
||||
iio_trigger_set_drvdata(data->motion_trig, indio_dev);
|
||||
ret = iio_trigger_register(data->motion_trig);
|
||||
@ -1116,25 +1072,25 @@ static int bmg160_probe(struct i2c_client *client,
|
||||
bmg160_trigger_handler,
|
||||
&bmg160_buffer_setup_ops);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev,
|
||||
dev_err(dev,
|
||||
"iio triggered buffer setup failed\n");
|
||||
goto err_trigger_unregister;
|
||||
}
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "unable to register iio device\n");
|
||||
dev_err(dev, "unable to register iio device\n");
|
||||
goto err_buffer_cleanup;
|
||||
}
|
||||
|
||||
ret = pm_runtime_set_active(&client->dev);
|
||||
ret = pm_runtime_set_active(dev);
|
||||
if (ret)
|
||||
goto err_iio_unregister;
|
||||
|
||||
pm_runtime_enable(&client->dev);
|
||||
pm_runtime_set_autosuspend_delay(&client->dev,
|
||||
pm_runtime_enable(dev);
|
||||
pm_runtime_set_autosuspend_delay(dev,
|
||||
BMG160_AUTO_SUSPEND_DELAY_MS);
|
||||
pm_runtime_use_autosuspend(&client->dev);
|
||||
pm_runtime_use_autosuspend(dev);
|
||||
|
||||
return 0;
|
||||
|
||||
@ -1150,15 +1106,16 @@ err_trigger_unregister:
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(bmg160_core_probe);
|
||||
|
||||
static int bmg160_remove(struct i2c_client *client)
|
||||
void bmg160_core_remove(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct bmg160_data *data = iio_priv(indio_dev);
|
||||
|
||||
pm_runtime_disable(&client->dev);
|
||||
pm_runtime_set_suspended(&client->dev);
|
||||
pm_runtime_put_noidle(&client->dev);
|
||||
pm_runtime_disable(dev);
|
||||
pm_runtime_set_suspended(dev);
|
||||
pm_runtime_put_noidle(dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
@ -1171,14 +1128,13 @@ static int bmg160_remove(struct i2c_client *client)
|
||||
mutex_lock(&data->mutex);
|
||||
bmg160_set_mode(data, BMG160_MODE_DEEP_SUSPEND);
|
||||
mutex_unlock(&data->mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(bmg160_core_remove);
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int bmg160_suspend(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct bmg160_data *data = iio_priv(indio_dev);
|
||||
|
||||
mutex_lock(&data->mutex);
|
||||
@ -1190,7 +1146,7 @@ static int bmg160_suspend(struct device *dev)
|
||||
|
||||
static int bmg160_resume(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct bmg160_data *data = iio_priv(indio_dev);
|
||||
|
||||
mutex_lock(&data->mutex);
|
||||
@ -1206,13 +1162,13 @@ static int bmg160_resume(struct device *dev)
|
||||
#ifdef CONFIG_PM
|
||||
static int bmg160_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct bmg160_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = bmg160_set_mode(data, BMG160_MODE_SUSPEND);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev, "set mode failed\n");
|
||||
dev_err(data->dev, "set mode failed\n");
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
@ -1221,7 +1177,7 @@ static int bmg160_runtime_suspend(struct device *dev)
|
||||
|
||||
static int bmg160_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct bmg160_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
@ -1235,39 +1191,12 @@ static int bmg160_runtime_resume(struct device *dev)
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops bmg160_pm_ops = {
|
||||
const struct dev_pm_ops bmg160_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(bmg160_suspend, bmg160_resume)
|
||||
SET_RUNTIME_PM_OPS(bmg160_runtime_suspend,
|
||||
bmg160_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
static const struct acpi_device_id bmg160_acpi_match[] = {
|
||||
{"BMG0160", 0},
|
||||
{"BMI055B", 0},
|
||||
{},
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(acpi, bmg160_acpi_match);
|
||||
|
||||
static const struct i2c_device_id bmg160_id[] = {
|
||||
{"bmg160", 0},
|
||||
{"bmi055_gyro", 0},
|
||||
{}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(i2c, bmg160_id);
|
||||
|
||||
static struct i2c_driver bmg160_driver = {
|
||||
.driver = {
|
||||
.name = BMG160_DRV_NAME,
|
||||
.acpi_match_table = ACPI_PTR(bmg160_acpi_match),
|
||||
.pm = &bmg160_pm_ops,
|
||||
},
|
||||
.probe = bmg160_probe,
|
||||
.remove = bmg160_remove,
|
||||
.id_table = bmg160_id,
|
||||
};
|
||||
module_i2c_driver(bmg160_driver);
|
||||
EXPORT_SYMBOL_GPL(bmg160_pm_ops);
|
||||
|
||||
MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
71
drivers/iio/gyro/bmg160_i2c.c
Normal file
71
drivers/iio/gyro/bmg160_i2c.c
Normal file
@ -0,0 +1,71 @@
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/acpi.h>
|
||||
|
||||
#include "bmg160.h"
|
||||
|
||||
static const struct regmap_config bmg160_regmap_i2c_conf = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.max_register = 0x3f
|
||||
};
|
||||
|
||||
static int bmg160_i2c_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct regmap *regmap;
|
||||
const char *name = NULL;
|
||||
|
||||
regmap = devm_regmap_init_i2c(client, &bmg160_regmap_i2c_conf);
|
||||
if (IS_ERR(regmap)) {
|
||||
dev_err(&client->dev, "Failed to register i2c regmap %d\n",
|
||||
(int)PTR_ERR(regmap));
|
||||
return PTR_ERR(regmap);
|
||||
}
|
||||
|
||||
if (id)
|
||||
name = id->name;
|
||||
|
||||
return bmg160_core_probe(&client->dev, regmap, client->irq, name);
|
||||
}
|
||||
|
||||
static int bmg160_i2c_remove(struct i2c_client *client)
|
||||
{
|
||||
bmg160_core_remove(&client->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct acpi_device_id bmg160_acpi_match[] = {
|
||||
{"BMG0160", 0},
|
||||
{"BMI055B", 0},
|
||||
{},
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(acpi, bmg160_acpi_match);
|
||||
|
||||
static const struct i2c_device_id bmg160_i2c_id[] = {
|
||||
{"bmg160", 0},
|
||||
{"bmi055_gyro", 0},
|
||||
{}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(i2c, bmg160_i2c_id);
|
||||
|
||||
static struct i2c_driver bmg160_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "bmg160_i2c",
|
||||
.acpi_match_table = ACPI_PTR(bmg160_acpi_match),
|
||||
.pm = &bmg160_pm_ops,
|
||||
},
|
||||
.probe = bmg160_i2c_probe,
|
||||
.remove = bmg160_i2c_remove,
|
||||
.id_table = bmg160_i2c_id,
|
||||
};
|
||||
module_i2c_driver(bmg160_i2c_driver);
|
||||
|
||||
MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("BMG160 I2C Gyro driver");
|
57
drivers/iio/gyro/bmg160_spi.c
Normal file
57
drivers/iio/gyro/bmg160_spi.c
Normal file
@ -0,0 +1,57 @@
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include "bmg160.h"
|
||||
|
||||
static const struct regmap_config bmg160_regmap_spi_conf = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.max_register = 0x3f,
|
||||
};
|
||||
|
||||
static int bmg160_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
struct regmap *regmap;
|
||||
const struct spi_device_id *id = spi_get_device_id(spi);
|
||||
|
||||
regmap = devm_regmap_init_spi(spi, &bmg160_regmap_spi_conf);
|
||||
if (IS_ERR(regmap)) {
|
||||
dev_err(&spi->dev, "Failed to register spi regmap %d\n",
|
||||
(int)PTR_ERR(regmap));
|
||||
return PTR_ERR(regmap);
|
||||
}
|
||||
|
||||
return bmg160_core_probe(&spi->dev, regmap, spi->irq, id->name);
|
||||
}
|
||||
|
||||
static int bmg160_spi_remove(struct spi_device *spi)
|
||||
{
|
||||
bmg160_core_remove(&spi->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id bmg160_spi_id[] = {
|
||||
{"bmg160", 0},
|
||||
{"bmi055_gyro", 0},
|
||||
{}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(spi, bmg160_spi_id);
|
||||
|
||||
static struct spi_driver bmg160_spi_driver = {
|
||||
.driver = {
|
||||
.name = "bmg160_spi",
|
||||
.pm = &bmg160_pm_ops,
|
||||
},
|
||||
.probe = bmg160_spi_probe,
|
||||
.remove = bmg160_spi_remove,
|
||||
.id_table = bmg160_spi_id,
|
||||
};
|
||||
module_spi_driver(bmg160_spi_driver);
|
||||
|
||||
MODULE_AUTHOR("Markus Pargmann <mpa@pengutronix.de>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("BMG160 SPI Gyro driver");
|
@ -383,6 +383,7 @@ static const struct iio_info gyro_info = {
|
||||
.attrs = &st_gyro_attribute_group,
|
||||
.read_raw = &st_gyro_read_raw,
|
||||
.write_raw = &st_gyro_write_raw,
|
||||
.debugfs_reg_access = &st_sensors_debugfs_reg_access,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_IIO_TRIGGER
|
||||
|
@ -12,6 +12,16 @@ config DHT11
|
||||
Other sensors should work as well as long as they speak the
|
||||
same protocol.
|
||||
|
||||
config HDC100X
|
||||
tristate "TI HDC100x relative humidity and temperature sensor"
|
||||
depends on I2C
|
||||
help
|
||||
Say yes here to build support for the TI HDC100x series of
|
||||
relative humidity and temperature sensors.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called hdc100x.
|
||||
|
||||
config SI7005
|
||||
tristate "SI7005 relative humidity and temperature sensor"
|
||||
depends on I2C
|
||||
|
@ -3,5 +3,6 @@
|
||||
#
|
||||
|
||||
obj-$(CONFIG_DHT11) += dht11.o
|
||||
obj-$(CONFIG_HDC100X) += hdc100x.o
|
||||
obj-$(CONFIG_SI7005) += si7005.o
|
||||
obj-$(CONFIG_SI7020) += si7020.o
|
||||
|
319
drivers/iio/humidity/hdc100x.c
Normal file
319
drivers/iio/humidity/hdc100x.c
Normal file
@ -0,0 +1,319 @@
|
||||
/*
|
||||
* hdc100x.c - Support for the TI HDC100x temperature + humidity sensors
|
||||
*
|
||||
* Copyright (C) 2015 Matt Ranostay <mranostay@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/delay.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/i2c.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
#define HDC100X_REG_TEMP 0x00
|
||||
#define HDC100X_REG_HUMIDITY 0x01
|
||||
|
||||
#define HDC100X_REG_CONFIG 0x02
|
||||
#define HDC100X_REG_CONFIG_HEATER_EN BIT(13)
|
||||
|
||||
struct hdc100x_data {
|
||||
struct i2c_client *client;
|
||||
struct mutex lock;
|
||||
u16 config;
|
||||
|
||||
/* integration time of the sensor */
|
||||
int adc_int_us[2];
|
||||
};
|
||||
|
||||
/* integration time in us */
|
||||
static const int hdc100x_int_time[][3] = {
|
||||
{ 6350, 3650, 0 }, /* IIO_TEMP channel*/
|
||||
{ 6500, 3850, 2500 }, /* IIO_HUMIDITYRELATIVE channel */
|
||||
};
|
||||
|
||||
/* HDC100X_REG_CONFIG shift and mask values */
|
||||
static const struct {
|
||||
int shift;
|
||||
int mask;
|
||||
} hdc100x_resolution_shift[2] = {
|
||||
{ /* IIO_TEMP channel */
|
||||
.shift = 10,
|
||||
.mask = 1
|
||||
},
|
||||
{ /* IIO_HUMIDITYRELATIVE channel */
|
||||
.shift = 8,
|
||||
.mask = 2,
|
||||
},
|
||||
};
|
||||
|
||||
static IIO_CONST_ATTR(temp_integration_time_available,
|
||||
"0.00365 0.00635");
|
||||
|
||||
static IIO_CONST_ATTR(humidityrelative_integration_time_available,
|
||||
"0.0025 0.00385 0.0065");
|
||||
|
||||
static IIO_CONST_ATTR(out_current_heater_raw_available,
|
||||
"0 1");
|
||||
|
||||
static struct attribute *hdc100x_attributes[] = {
|
||||
&iio_const_attr_temp_integration_time_available.dev_attr.attr,
|
||||
&iio_const_attr_humidityrelative_integration_time_available.dev_attr.attr,
|
||||
&iio_const_attr_out_current_heater_raw_available.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static struct attribute_group hdc100x_attribute_group = {
|
||||
.attrs = hdc100x_attributes,
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec hdc100x_channels[] = {
|
||||
{
|
||||
.type = IIO_TEMP,
|
||||
.address = HDC100X_REG_TEMP,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_INT_TIME) |
|
||||
BIT(IIO_CHAN_INFO_OFFSET),
|
||||
},
|
||||
{
|
||||
.type = IIO_HUMIDITYRELATIVE,
|
||||
.address = HDC100X_REG_HUMIDITY,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_INT_TIME)
|
||||
},
|
||||
{
|
||||
.type = IIO_CURRENT,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.extend_name = "heater",
|
||||
.output = 1,
|
||||
},
|
||||
};
|
||||
|
||||
static int hdc100x_update_config(struct hdc100x_data *data, int mask, int val)
|
||||
{
|
||||
int tmp = (~mask & data->config) | val;
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_write_word_swapped(data->client,
|
||||
HDC100X_REG_CONFIG, tmp);
|
||||
if (!ret)
|
||||
data->config = tmp;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hdc100x_set_it_time(struct hdc100x_data *data, int chan, int val2)
|
||||
{
|
||||
int shift = hdc100x_resolution_shift[chan].shift;
|
||||
int ret = -EINVAL;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(hdc100x_int_time[chan]); i++) {
|
||||
if (val2 && val2 == hdc100x_int_time[chan][i]) {
|
||||
ret = hdc100x_update_config(data,
|
||||
hdc100x_resolution_shift[chan].mask << shift,
|
||||
i << shift);
|
||||
if (!ret)
|
||||
data->adc_int_us[chan] = val2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hdc100x_get_measurement(struct hdc100x_data *data,
|
||||
struct iio_chan_spec const *chan)
|
||||
{
|
||||
struct i2c_client *client = data->client;
|
||||
int delay = data->adc_int_us[chan->address];
|
||||
int ret;
|
||||
int val;
|
||||
|
||||
/* start measurement */
|
||||
ret = i2c_smbus_write_byte(client, chan->address);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "cannot start measurement");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* wait for integration time to pass */
|
||||
usleep_range(delay, delay + 1000);
|
||||
|
||||
/*
|
||||
* i2c_smbus_read_word_data cannot() be used here due to the command
|
||||
* value not being understood and causes NAKs preventing any reading
|
||||
* from being accessed.
|
||||
*/
|
||||
ret = i2c_smbus_read_byte(client);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "cannot read high byte measurement");
|
||||
return ret;
|
||||
}
|
||||
val = ret << 6;
|
||||
|
||||
ret = i2c_smbus_read_byte(client);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "cannot read low byte measurement");
|
||||
return ret;
|
||||
}
|
||||
val |= ret >> 2;
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static int hdc100x_get_heater_status(struct hdc100x_data *data)
|
||||
{
|
||||
return !!(data->config & HDC100X_REG_CONFIG_HEATER_EN);
|
||||
}
|
||||
|
||||
static int hdc100x_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val,
|
||||
int *val2, long mask)
|
||||
{
|
||||
struct hdc100x_data *data = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW: {
|
||||
int ret;
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
if (chan->type == IIO_CURRENT) {
|
||||
*val = hdc100x_get_heater_status(data);
|
||||
ret = IIO_VAL_INT;
|
||||
} else {
|
||||
ret = hdc100x_get_measurement(data, chan);
|
||||
if (ret >= 0) {
|
||||
*val = ret;
|
||||
ret = IIO_VAL_INT;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&data->lock);
|
||||
return ret;
|
||||
}
|
||||
case IIO_CHAN_INFO_INT_TIME:
|
||||
*val = 0;
|
||||
*val2 = data->adc_int_us[chan->address];
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
if (chan->type == IIO_TEMP) {
|
||||
*val = 165;
|
||||
*val2 = 65536 >> 2;
|
||||
return IIO_VAL_FRACTIONAL;
|
||||
} else {
|
||||
*val = 0;
|
||||
*val2 = 10000;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
}
|
||||
break;
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
*val = -40;
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int hdc100x_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
struct hdc100x_data *data = iio_priv(indio_dev);
|
||||
int ret = -EINVAL;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_INT_TIME:
|
||||
if (val != 0)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
ret = hdc100x_set_it_time(data, chan->address, val2);
|
||||
mutex_unlock(&data->lock);
|
||||
return ret;
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (chan->type != IIO_CURRENT || val2 != 0)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
ret = hdc100x_update_config(data, HDC100X_REG_CONFIG_HEATER_EN,
|
||||
val ? HDC100X_REG_CONFIG_HEATER_EN : 0);
|
||||
mutex_unlock(&data->lock);
|
||||
return ret;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct iio_info hdc100x_info = {
|
||||
.read_raw = hdc100x_read_raw,
|
||||
.write_raw = hdc100x_write_raw,
|
||||
.attrs = &hdc100x_attribute_group,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int hdc100x_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct hdc100x_data *data;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter,
|
||||
I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BYTE))
|
||||
return -ENODEV;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
data = iio_priv(indio_dev);
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
data->client = client;
|
||||
mutex_init(&data->lock);
|
||||
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->name = dev_name(&client->dev);
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &hdc100x_info;
|
||||
|
||||
indio_dev->channels = hdc100x_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(hdc100x_channels);
|
||||
|
||||
/* be sure we are in a known state */
|
||||
hdc100x_set_it_time(data, 0, hdc100x_int_time[0][0]);
|
||||
hdc100x_set_it_time(data, 1, hdc100x_int_time[1][0]);
|
||||
|
||||
return devm_iio_device_register(&client->dev, indio_dev);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id hdc100x_id[] = {
|
||||
{ "hdc100x", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, hdc100x_id);
|
||||
|
||||
static struct i2c_driver hdc100x_driver = {
|
||||
.driver = {
|
||||
.name = "hdc100x",
|
||||
},
|
||||
.probe = hdc100x_probe,
|
||||
.id_table = hdc100x_id,
|
||||
};
|
||||
module_i2c_driver(hdc100x_driver);
|
||||
|
||||
MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
|
||||
MODULE_DESCRIPTION("TI HDC100x humidity and temperature sensor driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -57,8 +57,12 @@ static int si7020_read_raw(struct iio_dev *indio_dev,
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = ret >> 2;
|
||||
/*
|
||||
* Humidity values can slightly exceed the 0-100%RH
|
||||
* range and should be corrected by software
|
||||
*/
|
||||
if (chan->type == IIO_HUMIDITYRELATIVE)
|
||||
*val &= GENMASK(11, 0);
|
||||
*val = clamp_val(*val, 786, 13893);
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
if (chan->type == IIO_TEMP)
|
||||
|
@ -75,6 +75,8 @@ static const char * const iio_chan_type_name_spec[] = {
|
||||
[IIO_ENERGY] = "energy",
|
||||
[IIO_DISTANCE] = "distance",
|
||||
[IIO_VELOCITY] = "velocity",
|
||||
[IIO_CONCENTRATION] = "concentration",
|
||||
[IIO_RESISTANCE] = "resistance",
|
||||
};
|
||||
|
||||
static const char * const iio_modifier_names[] = {
|
||||
@ -111,6 +113,8 @@ static const char * const iio_modifier_names[] = {
|
||||
[IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z] = "sqrt(x^2+y^2+z^2)",
|
||||
[IIO_MOD_I] = "i",
|
||||
[IIO_MOD_Q] = "q",
|
||||
[IIO_MOD_CO2] = "co2",
|
||||
[IIO_MOD_VOC] = "voc",
|
||||
};
|
||||
|
||||
/* relies on pairs of these shared then separate */
|
||||
@ -962,7 +966,7 @@ static void iio_device_unregister_sysfs(struct iio_dev *indio_dev)
|
||||
static void iio_dev_release(struct device *device)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(device);
|
||||
if (indio_dev->modes & INDIO_BUFFER_TRIGGERED)
|
||||
if (indio_dev->modes & (INDIO_BUFFER_TRIGGERED | INDIO_EVENT_TRIGGERED))
|
||||
iio_device_unregister_trigger_consumer(indio_dev);
|
||||
iio_device_unregister_eventset(indio_dev);
|
||||
iio_device_unregister_sysfs(indio_dev);
|
||||
@ -1153,6 +1157,8 @@ static long iio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||||
|
||||
if (cmd == IIO_GET_EVENT_FD_IOCTL) {
|
||||
fd = iio_event_getfd(indio_dev);
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
if (copy_to_user(ip, &fd, sizeof(fd)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
@ -1241,7 +1247,7 @@ int iio_device_register(struct iio_dev *indio_dev)
|
||||
"Failed to register event set\n");
|
||||
goto error_free_sysfs;
|
||||
}
|
||||
if (indio_dev->modes & INDIO_BUFFER_TRIGGERED)
|
||||
if (indio_dev->modes & (INDIO_BUFFER_TRIGGERED | INDIO_EVENT_TRIGGERED))
|
||||
iio_device_register_trigger_consumer(indio_dev);
|
||||
|
||||
if ((indio_dev->modes & INDIO_ALL_BUFFER_MODES) &&
|
||||
|
@ -366,10 +366,18 @@ static ssize_t iio_trigger_write_current(struct device *dev,
|
||||
|
||||
indio_dev->trig = trig;
|
||||
|
||||
if (oldtrig)
|
||||
if (oldtrig) {
|
||||
if (indio_dev->modes & INDIO_EVENT_TRIGGERED)
|
||||
iio_trigger_detach_poll_func(oldtrig,
|
||||
indio_dev->pollfunc_event);
|
||||
iio_trigger_put(oldtrig);
|
||||
if (indio_dev->trig)
|
||||
}
|
||||
if (indio_dev->trig) {
|
||||
iio_trigger_get(indio_dev->trig);
|
||||
if (indio_dev->modes & INDIO_EVENT_TRIGGERED)
|
||||
iio_trigger_attach_poll_func(indio_dev->trig,
|
||||
indio_dev->pollfunc_event);
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
68
drivers/iio/industrialio-triggered-event.c
Normal file
68
drivers/iio/industrialio-triggered-event.c
Normal file
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Cogent Embedded, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/triggered_event.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
|
||||
/**
|
||||
* iio_triggered_event_setup() - Setup pollfunc_event for triggered event
|
||||
* @indio_dev: IIO device structure
|
||||
* @h: Function which will be used as pollfunc_event top half
|
||||
* @thread: Function which will be used as pollfunc_event bottom half
|
||||
*
|
||||
* This function combines some common tasks which will normally be performed
|
||||
* when setting up a triggered event. It will allocate the pollfunc_event and
|
||||
* set mode to use it for triggered event.
|
||||
*
|
||||
* Before calling this function the indio_dev structure should already be
|
||||
* completely initialized, but not yet registered. In practice this means that
|
||||
* this function should be called right before iio_device_register().
|
||||
*
|
||||
* To free the resources allocated by this function call
|
||||
* iio_triggered_event_cleanup().
|
||||
*/
|
||||
int iio_triggered_event_setup(struct iio_dev *indio_dev,
|
||||
irqreturn_t (*h)(int irq, void *p),
|
||||
irqreturn_t (*thread)(int irq, void *p))
|
||||
{
|
||||
indio_dev->pollfunc_event = iio_alloc_pollfunc(h,
|
||||
thread,
|
||||
IRQF_ONESHOT,
|
||||
indio_dev,
|
||||
"%s_consumer%d",
|
||||
indio_dev->name,
|
||||
indio_dev->id);
|
||||
if (indio_dev->pollfunc_event == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Flag that events polling is possible */
|
||||
indio_dev->modes |= INDIO_EVENT_TRIGGERED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(iio_triggered_event_setup);
|
||||
|
||||
/**
|
||||
* iio_triggered_event_cleanup() - Free resources allocated by iio_triggered_event_setup()
|
||||
* @indio_dev: IIO device structure
|
||||
*/
|
||||
void iio_triggered_event_cleanup(struct iio_dev *indio_dev)
|
||||
{
|
||||
indio_dev->modes &= ~INDIO_EVENT_TRIGGERED;
|
||||
iio_dealloc_pollfunc(indio_dev->pollfunc_event);
|
||||
}
|
||||
EXPORT_SYMBOL(iio_triggered_event_cleanup);
|
||||
|
||||
MODULE_AUTHOR("Vladimir Barinov");
|
||||
MODULE_DESCRIPTION("IIO helper functions for setting up triggered events");
|
||||
MODULE_LICENSE("GPL");
|
@ -50,6 +50,19 @@ config APDS9300
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called apds9300.
|
||||
|
||||
config APDS9960
|
||||
tristate "Avago APDS9960 gesture/RGB/ALS/proximity sensor"
|
||||
select REGMAP_I2C
|
||||
select IIO_BUFFER
|
||||
select IIO_KFIFO_BUF
|
||||
depends on I2C
|
||||
help
|
||||
Say Y here to build I2C interface support for the Avago
|
||||
APDS9960 gesture/RGB/ALS/proximity sensor.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called apds9960
|
||||
|
||||
config BH1750
|
||||
tristate "ROHM BH1750 ambient light sensor"
|
||||
depends on I2C
|
||||
@ -287,6 +300,16 @@ config TSL4531
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called tsl4531.
|
||||
|
||||
config US5182D
|
||||
tristate "UPISEMI light and proximity sensor"
|
||||
depends on I2C
|
||||
help
|
||||
If you say yes here you get support for the UPISEMI US5182D
|
||||
ambient light and proximity sensor.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called us5182d.
|
||||
|
||||
config VCNL4000
|
||||
tristate "VCNL4000 combined ALS and proximity sensor"
|
||||
depends on I2C
|
||||
|
@ -7,6 +7,7 @@ obj-$(CONFIG_ACPI_ALS) += acpi-als.o
|
||||
obj-$(CONFIG_ADJD_S311) += adjd_s311.o
|
||||
obj-$(CONFIG_AL3320A) += al3320a.o
|
||||
obj-$(CONFIG_APDS9300) += apds9300.o
|
||||
obj-$(CONFIG_APDS9960) += apds9960.o
|
||||
obj-$(CONFIG_BH1750) += bh1750.o
|
||||
obj-$(CONFIG_CM32181) += cm32181.o
|
||||
obj-$(CONFIG_CM3232) += cm3232.o
|
||||
@ -27,4 +28,5 @@ obj-$(CONFIG_STK3310) += stk3310.o
|
||||
obj-$(CONFIG_TCS3414) += tcs3414.o
|
||||
obj-$(CONFIG_TCS3472) += tcs3472.o
|
||||
obj-$(CONFIG_TSL4531) += tsl4531.o
|
||||
obj-$(CONFIG_US5182D) += us5182d.o
|
||||
obj-$(CONFIG_VCNL4000) += vcnl4000.o
|
||||
|
1130
drivers/iio/light/apds9960.c
Normal file
1130
drivers/iio/light/apds9960.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -793,7 +793,6 @@ static struct i2c_driver opt3001_driver = {
|
||||
.driver = {
|
||||
.name = "opt3001",
|
||||
.of_match_table = of_match_ptr(opt3001_of_match),
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -35,8 +35,8 @@
|
||||
#define STK3310_REG_ID 0x3E
|
||||
#define STK3310_MAX_REG 0x80
|
||||
|
||||
#define STK3310_STATE_EN_PS 0x01
|
||||
#define STK3310_STATE_EN_ALS 0x02
|
||||
#define STK3310_STATE_EN_PS BIT(0)
|
||||
#define STK3310_STATE_EN_ALS BIT(1)
|
||||
#define STK3310_STATE_STANDBY 0x00
|
||||
|
||||
#define STK3310_CHIP_ID_VAL 0x13
|
||||
@ -241,8 +241,11 @@ static int stk3310_write_event(struct iio_dev *indio_dev,
|
||||
struct stk3310_data *data = iio_priv(indio_dev);
|
||||
struct i2c_client *client = data->client;
|
||||
|
||||
regmap_field_read(data->reg_ps_gain, &index);
|
||||
if (val > stk3310_ps_max[index])
|
||||
ret = regmap_field_read(data->reg_ps_gain, &index);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (val < 0 || val > stk3310_ps_max[index])
|
||||
return -EINVAL;
|
||||
|
||||
if (dir == IIO_EV_DIR_RISING)
|
||||
@ -266,9 +269,12 @@ static int stk3310_read_event_config(struct iio_dev *indio_dev,
|
||||
enum iio_event_direction dir)
|
||||
{
|
||||
unsigned int event_val;
|
||||
int ret;
|
||||
struct stk3310_data *data = iio_priv(indio_dev);
|
||||
|
||||
regmap_field_read(data->reg_int_ps, &event_val);
|
||||
ret = regmap_field_read(data->reg_int_ps, &event_val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return event_val;
|
||||
}
|
||||
@ -307,14 +313,16 @@ static int stk3310_read_raw(struct iio_dev *indio_dev,
|
||||
struct stk3310_data *data = iio_priv(indio_dev);
|
||||
struct i2c_client *client = data->client;
|
||||
|
||||
if (chan->type != IIO_LIGHT && chan->type != IIO_PROXIMITY)
|
||||
return -EINVAL;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (chan->type == IIO_LIGHT)
|
||||
reg = STK3310_REG_ALS_DATA_MSB;
|
||||
else if (chan->type == IIO_PROXIMITY)
|
||||
reg = STK3310_REG_PS_DATA_MSB;
|
||||
else
|
||||
return -EINVAL;
|
||||
reg = STK3310_REG_PS_DATA_MSB;
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
ret = regmap_bulk_read(data->regmap, reg, &buf, 2);
|
||||
if (ret < 0) {
|
||||
@ -327,17 +335,23 @@ static int stk3310_read_raw(struct iio_dev *indio_dev,
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_INT_TIME:
|
||||
if (chan->type == IIO_LIGHT)
|
||||
regmap_field_read(data->reg_als_it, &index);
|
||||
ret = regmap_field_read(data->reg_als_it, &index);
|
||||
else
|
||||
regmap_field_read(data->reg_ps_it, &index);
|
||||
ret = regmap_field_read(data->reg_ps_it, &index);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = stk3310_it_table[index][0];
|
||||
*val2 = stk3310_it_table[index][1];
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
if (chan->type == IIO_LIGHT)
|
||||
regmap_field_read(data->reg_als_gain, &index);
|
||||
ret = regmap_field_read(data->reg_als_gain, &index);
|
||||
else
|
||||
regmap_field_read(data->reg_ps_gain, &index);
|
||||
ret = regmap_field_read(data->reg_ps_gain, &index);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = stk3310_scale_table[index][0];
|
||||
*val2 = stk3310_scale_table[index][1];
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
@ -354,6 +368,9 @@ static int stk3310_write_raw(struct iio_dev *indio_dev,
|
||||
int index;
|
||||
struct stk3310_data *data = iio_priv(indio_dev);
|
||||
|
||||
if (chan->type != IIO_LIGHT && chan->type != IIO_PROXIMITY)
|
||||
return -EINVAL;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_INT_TIME:
|
||||
index = stk3310_get_index(stk3310_it_table,
|
||||
@ -368,7 +385,7 @@ static int stk3310_write_raw(struct iio_dev *indio_dev,
|
||||
ret = regmap_field_write(data->reg_ps_it, index);
|
||||
if (ret < 0)
|
||||
dev_err(&data->client->dev,
|
||||
"sensor configuration failed\n");
|
||||
"sensor configuration failed\n");
|
||||
mutex_unlock(&data->lock);
|
||||
return ret;
|
||||
|
||||
@ -385,7 +402,7 @@ static int stk3310_write_raw(struct iio_dev *indio_dev,
|
||||
ret = regmap_field_write(data->reg_ps_gain, index);
|
||||
if (ret < 0)
|
||||
dev_err(&data->client->dev,
|
||||
"sensor configuration failed\n");
|
||||
"sensor configuration failed\n");
|
||||
mutex_unlock(&data->lock);
|
||||
return ret;
|
||||
}
|
||||
@ -419,8 +436,8 @@ static int stk3310_set_state(struct stk3310_data *data, u8 state)
|
||||
dev_err(&client->dev, "failed to change sensor state\n");
|
||||
} else if (state != STK3310_STATE_STANDBY) {
|
||||
/* Don't reset the 'enabled' flags if we're going in standby */
|
||||
data->ps_enabled = !!(state & 0x01);
|
||||
data->als_enabled = !!(state & 0x02);
|
||||
data->ps_enabled = !!(state & STK3310_STATE_EN_PS);
|
||||
data->als_enabled = !!(state & STK3310_STATE_EN_ALS);
|
||||
}
|
||||
mutex_unlock(&data->lock);
|
||||
|
||||
@ -435,7 +452,10 @@ static int stk3310_init(struct iio_dev *indio_dev)
|
||||
struct stk3310_data *data = iio_priv(indio_dev);
|
||||
struct i2c_client *client = data->client;
|
||||
|
||||
regmap_read(data->regmap, STK3310_REG_ID, &chipid);
|
||||
ret = regmap_read(data->regmap, STK3310_REG_ID, &chipid);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (chipid != STK3310_CHIP_ID_VAL &&
|
||||
chipid != STK3311_CHIP_ID_VAL) {
|
||||
dev_err(&client->dev, "invalid chip id: 0x%x\n", chipid);
|
||||
@ -604,8 +624,13 @@ static int stk3310_probe(struct i2c_client *client,
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (client->irq < 0)
|
||||
if (client->irq < 0) {
|
||||
client->irq = stk3310_gpio_probe(client);
|
||||
if (client->irq < 0) {
|
||||
ret = client->irq;
|
||||
goto err_standby;
|
||||
}
|
||||
}
|
||||
|
||||
if (client->irq >= 0) {
|
||||
ret = devm_request_threaded_irq(&client->dev, client->irq,
|
||||
@ -614,17 +639,23 @@ static int stk3310_probe(struct i2c_client *client,
|
||||
IRQF_TRIGGER_FALLING |
|
||||
IRQF_ONESHOT,
|
||||
STK3310_EVENT, indio_dev);
|
||||
if (ret < 0)
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "request irq %d failed\n",
|
||||
client->irq);
|
||||
client->irq);
|
||||
goto err_standby;
|
||||
}
|
||||
}
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "device_register failed\n");
|
||||
stk3310_set_state(data, STK3310_STATE_STANDBY);
|
||||
goto err_standby;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_standby:
|
||||
stk3310_set_state(data, STK3310_STATE_STANDBY);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -648,7 +679,7 @@ static int stk3310_suspend(struct device *dev)
|
||||
|
||||
static int stk3310_resume(struct device *dev)
|
||||
{
|
||||
int state = 0;
|
||||
u8 state = 0;
|
||||
struct stk3310_data *data;
|
||||
|
||||
data = iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
|
||||
|
@ -158,9 +158,9 @@ static int tsl4531_check_id(struct i2c_client *client)
|
||||
case TSL45313_ID:
|
||||
case TSL45315_ID:
|
||||
case TSL45317_ID:
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
default:
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
|
||||
@ -180,9 +180,10 @@ static int tsl4531_probe(struct i2c_client *client,
|
||||
data->client = client;
|
||||
mutex_init(&data->lock);
|
||||
|
||||
if (!tsl4531_check_id(client)) {
|
||||
ret = tsl4531_check_id(client);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "no TSL4531 sensor\n");
|
||||
return -ENODEV;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = i2c_smbus_write_byte_data(data->client, TSL4531_CONTROL,
|
||||
|
507
drivers/iio/light/us5182d.c
Normal file
507
drivers/iio/light/us5182d.c
Normal file
@ -0,0 +1,507 @@
|
||||
/*
|
||||
* Copyright (c) 2015 Intel Corporation
|
||||
*
|
||||
* Driver for UPISEMI us5182d Proximity and Ambient Light Sensor.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope 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.
|
||||
*
|
||||
* To do: Interrupt support.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/mutex.h>
|
||||
|
||||
#define US5182D_REG_CFG0 0x00
|
||||
#define US5182D_CFG0_ONESHOT_EN BIT(6)
|
||||
#define US5182D_CFG0_SHUTDOWN_EN BIT(7)
|
||||
#define US5182D_CFG0_WORD_ENABLE BIT(0)
|
||||
|
||||
#define US5182D_REG_CFG1 0x01
|
||||
#define US5182D_CFG1_ALS_RES16 BIT(4)
|
||||
#define US5182D_CFG1_AGAIN_DEFAULT 0x00
|
||||
|
||||
#define US5182D_REG_CFG2 0x02
|
||||
#define US5182D_CFG2_PX_RES16 BIT(4)
|
||||
#define US5182D_CFG2_PXGAIN_DEFAULT BIT(2)
|
||||
|
||||
#define US5182D_REG_CFG3 0x03
|
||||
#define US5182D_CFG3_LED_CURRENT100 (BIT(4) | BIT(5))
|
||||
|
||||
#define US5182D_REG_CFG4 0x10
|
||||
|
||||
/*
|
||||
* Registers for tuning the auto dark current cancelling feature.
|
||||
* DARK_TH(reg 0x27,0x28) - threshold (counts) for auto dark cancelling.
|
||||
* when ALS > DARK_TH --> ALS_Code = ALS - Upper(0x2A) * Dark
|
||||
* when ALS < DARK_TH --> ALS_Code = ALS - Lower(0x29) * Dark
|
||||
*/
|
||||
#define US5182D_REG_UDARK_TH 0x27
|
||||
#define US5182D_REG_DARK_AUTO_EN 0x2b
|
||||
#define US5182D_REG_AUTO_LDARK_GAIN 0x29
|
||||
#define US5182D_REG_AUTO_HDARK_GAIN 0x2a
|
||||
|
||||
#define US5182D_OPMODE_ALS 0x01
|
||||
#define US5182D_OPMODE_PX 0x02
|
||||
#define US5182D_OPMODE_SHIFT 4
|
||||
|
||||
#define US5182D_REG_DARK_AUTO_EN_DEFAULT 0x80
|
||||
#define US5182D_REG_AUTO_LDARK_GAIN_DEFAULT 0x16
|
||||
#define US5182D_REG_AUTO_HDARK_GAIN_DEFAULT 0x00
|
||||
|
||||
#define US5182D_REG_ADL 0x0c
|
||||
#define US5182D_REG_PDL 0x0e
|
||||
|
||||
#define US5182D_REG_MODE_STORE 0x21
|
||||
#define US5182D_STORE_MODE 0x01
|
||||
|
||||
#define US5182D_REG_CHIPID 0xb2
|
||||
|
||||
#define US5182D_OPMODE_MASK GENMASK(5, 4)
|
||||
#define US5182D_AGAIN_MASK 0x07
|
||||
#define US5182D_RESET_CHIP 0x01
|
||||
|
||||
#define US5182D_CHIPID 0x26
|
||||
#define US5182D_DRV_NAME "us5182d"
|
||||
|
||||
#define US5182D_GA_RESOLUTION 1000
|
||||
|
||||
#define US5182D_READ_BYTE 1
|
||||
#define US5182D_READ_WORD 2
|
||||
#define US5182D_OPSTORE_SLEEP_TIME 20 /* ms */
|
||||
|
||||
/* Available ranges: [12354, 7065, 3998, 2202, 1285, 498, 256, 138] lux */
|
||||
static const int us5182d_scales[] = {188500, 107800, 61000, 33600, 19600, 7600,
|
||||
3900, 2100};
|
||||
|
||||
/*
|
||||
* Experimental thresholds that work with US5182D sensor on evaluation board
|
||||
* roughly between 12-32 lux
|
||||
*/
|
||||
static u16 us5182d_dark_ths_vals[] = {170, 200, 512, 512, 800, 2000, 4000,
|
||||
8000};
|
||||
|
||||
enum mode {
|
||||
US5182D_ALS_PX,
|
||||
US5182D_ALS_ONLY,
|
||||
US5182D_PX_ONLY
|
||||
};
|
||||
|
||||
struct us5182d_data {
|
||||
struct i2c_client *client;
|
||||
struct mutex lock;
|
||||
|
||||
/* Glass attenuation factor */
|
||||
u32 ga;
|
||||
|
||||
/* Dark gain tuning */
|
||||
u8 lower_dark_gain;
|
||||
u8 upper_dark_gain;
|
||||
u16 *us5182d_dark_ths;
|
||||
|
||||
u8 opmode;
|
||||
};
|
||||
|
||||
static IIO_CONST_ATTR(in_illuminance_scale_available,
|
||||
"0.0021 0.0039 0.0076 0.0196 0.0336 0.061 0.1078 0.1885");
|
||||
|
||||
static struct attribute *us5182d_attrs[] = {
|
||||
&iio_const_attr_in_illuminance_scale_available.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group us5182d_attr_group = {
|
||||
.attrs = us5182d_attrs,
|
||||
};
|
||||
|
||||
static const struct {
|
||||
u8 reg;
|
||||
u8 val;
|
||||
} us5182d_regvals[] = {
|
||||
{US5182D_REG_CFG0, (US5182D_CFG0_SHUTDOWN_EN |
|
||||
US5182D_CFG0_WORD_ENABLE)},
|
||||
{US5182D_REG_CFG1, US5182D_CFG1_ALS_RES16},
|
||||
{US5182D_REG_CFG2, (US5182D_CFG2_PX_RES16 |
|
||||
US5182D_CFG2_PXGAIN_DEFAULT)},
|
||||
{US5182D_REG_CFG3, US5182D_CFG3_LED_CURRENT100},
|
||||
{US5182D_REG_MODE_STORE, US5182D_STORE_MODE},
|
||||
{US5182D_REG_CFG4, 0x00},
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec us5182d_channels[] = {
|
||||
{
|
||||
.type = IIO_LIGHT,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
},
|
||||
{
|
||||
.type = IIO_PROXIMITY,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
}
|
||||
};
|
||||
|
||||
static int us5182d_get_als(struct us5182d_data *data)
|
||||
{
|
||||
int ret;
|
||||
unsigned long result;
|
||||
|
||||
ret = i2c_smbus_read_word_data(data->client,
|
||||
US5182D_REG_ADL);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
result = ret * data->ga / US5182D_GA_RESOLUTION;
|
||||
if (result > 0xffff)
|
||||
result = 0xffff;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int us5182d_set_opmode(struct us5182d_data *data, u8 mode)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(data->client, US5182D_REG_CFG0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* In oneshot mode the chip will power itself down after taking the
|
||||
* required measurement.
|
||||
*/
|
||||
ret = ret | US5182D_CFG0_ONESHOT_EN;
|
||||
|
||||
/* update mode */
|
||||
ret = ret & ~US5182D_OPMODE_MASK;
|
||||
ret = ret | (mode << US5182D_OPMODE_SHIFT);
|
||||
|
||||
/*
|
||||
* After updating the operating mode, the chip requires that
|
||||
* the operation is stored, by writing 1 in the STORE_MODE
|
||||
* register (auto-clearing).
|
||||
*/
|
||||
ret = i2c_smbus_write_byte_data(data->client, US5182D_REG_CFG0, ret);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (mode == data->opmode)
|
||||
return 0;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(data->client, US5182D_REG_MODE_STORE,
|
||||
US5182D_STORE_MODE);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
data->opmode = mode;
|
||||
msleep(US5182D_OPSTORE_SLEEP_TIME);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int us5182d_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val,
|
||||
int *val2, long mask)
|
||||
{
|
||||
struct us5182d_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
switch (chan->type) {
|
||||
case IIO_LIGHT:
|
||||
mutex_lock(&data->lock);
|
||||
ret = us5182d_set_opmode(data, US5182D_OPMODE_ALS);
|
||||
if (ret < 0)
|
||||
goto out_err;
|
||||
|
||||
ret = us5182d_get_als(data);
|
||||
if (ret < 0)
|
||||
goto out_err;
|
||||
mutex_unlock(&data->lock);
|
||||
*val = ret;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_PROXIMITY:
|
||||
mutex_lock(&data->lock);
|
||||
ret = us5182d_set_opmode(data, US5182D_OPMODE_PX);
|
||||
if (ret < 0)
|
||||
goto out_err;
|
||||
|
||||
ret = i2c_smbus_read_word_data(data->client,
|
||||
US5182D_REG_PDL);
|
||||
if (ret < 0)
|
||||
goto out_err;
|
||||
mutex_unlock(&data->lock);
|
||||
*val = ret;
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
ret = i2c_smbus_read_byte_data(data->client, US5182D_REG_CFG1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = 0;
|
||||
*val2 = us5182d_scales[ret & US5182D_AGAIN_MASK];
|
||||
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
out_err:
|
||||
mutex_unlock(&data->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* us5182d_update_dark_th - update Darh_Th registers
|
||||
* @data us5182d_data structure
|
||||
* @index index in us5182d_dark_ths array to use for the updated value
|
||||
*
|
||||
* Function needs to be called with a lock held because it needs two i2c write
|
||||
* byte operations as these registers (0x27 0x28) don't work in word mode
|
||||
* accessing.
|
||||
*/
|
||||
static int us5182d_update_dark_th(struct us5182d_data *data, int index)
|
||||
{
|
||||
__be16 dark_th = cpu_to_be16(data->us5182d_dark_ths[index]);
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(data->client, US5182D_REG_UDARK_TH,
|
||||
((u8 *)&dark_th)[0]);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return i2c_smbus_write_byte_data(data->client, US5182D_REG_UDARK_TH + 1,
|
||||
((u8 *)&dark_th)[1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* us5182d_apply_scale - update the ALS scale
|
||||
* @data us5182d_data structure
|
||||
* @index index in us5182d_scales array to use for the updated value
|
||||
*
|
||||
* Function needs to be called with a lock held as we're having more than one
|
||||
* i2c operation.
|
||||
*/
|
||||
static int us5182d_apply_scale(struct us5182d_data *data, int index)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(data->client, US5182D_REG_CFG1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = ret & (~US5182D_AGAIN_MASK);
|
||||
ret |= index;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(data->client, US5182D_REG_CFG1, ret);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return us5182d_update_dark_th(data, index);
|
||||
}
|
||||
|
||||
static int us5182d_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int val,
|
||||
int val2, long mask)
|
||||
{
|
||||
struct us5182d_data *data = iio_priv(indio_dev);
|
||||
int ret, i;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
if (val != 0)
|
||||
return -EINVAL;
|
||||
for (i = 0; i < ARRAY_SIZE(us5182d_scales); i++)
|
||||
if (val2 == us5182d_scales[i]) {
|
||||
mutex_lock(&data->lock);
|
||||
ret = us5182d_apply_scale(data, i);
|
||||
mutex_unlock(&data->lock);
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct iio_info us5182d_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = us5182d_read_raw,
|
||||
.write_raw = us5182d_write_raw,
|
||||
.attrs = &us5182d_attr_group,
|
||||
};
|
||||
|
||||
static int us5182d_reset(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct us5182d_data *data = iio_priv(indio_dev);
|
||||
|
||||
return i2c_smbus_write_byte_data(data->client, US5182D_REG_CFG3,
|
||||
US5182D_RESET_CHIP);
|
||||
}
|
||||
|
||||
static int us5182d_init(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct us5182d_data *data = iio_priv(indio_dev);
|
||||
int i, ret;
|
||||
|
||||
ret = us5182d_reset(indio_dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
data->opmode = 0;
|
||||
for (i = 0; i < ARRAY_SIZE(us5182d_regvals); i++) {
|
||||
ret = i2c_smbus_write_byte_data(data->client,
|
||||
us5182d_regvals[i].reg,
|
||||
us5182d_regvals[i].val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void us5182d_get_platform_data(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct us5182d_data *data = iio_priv(indio_dev);
|
||||
|
||||
if (device_property_read_u32(&data->client->dev, "upisemi,glass-coef",
|
||||
&data->ga))
|
||||
data->ga = US5182D_GA_RESOLUTION;
|
||||
if (device_property_read_u16_array(&data->client->dev,
|
||||
"upisemi,dark-ths",
|
||||
data->us5182d_dark_ths,
|
||||
ARRAY_SIZE(us5182d_dark_ths_vals)))
|
||||
data->us5182d_dark_ths = us5182d_dark_ths_vals;
|
||||
if (device_property_read_u8(&data->client->dev,
|
||||
"upisemi,upper-dark-gain",
|
||||
&data->upper_dark_gain))
|
||||
data->upper_dark_gain = US5182D_REG_AUTO_HDARK_GAIN_DEFAULT;
|
||||
if (device_property_read_u8(&data->client->dev,
|
||||
"upisemi,lower-dark-gain",
|
||||
&data->lower_dark_gain))
|
||||
data->lower_dark_gain = US5182D_REG_AUTO_LDARK_GAIN_DEFAULT;
|
||||
}
|
||||
|
||||
static int us5182d_dark_gain_config(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct us5182d_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = us5182d_update_dark_th(data, US5182D_CFG1_AGAIN_DEFAULT);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(data->client,
|
||||
US5182D_REG_AUTO_LDARK_GAIN,
|
||||
data->lower_dark_gain);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(data->client,
|
||||
US5182D_REG_AUTO_HDARK_GAIN,
|
||||
data->upper_dark_gain);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return i2c_smbus_write_byte_data(data->client, US5182D_REG_DARK_AUTO_EN,
|
||||
US5182D_REG_DARK_AUTO_EN_DEFAULT);
|
||||
}
|
||||
|
||||
static int us5182d_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct us5182d_data *data;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
data = iio_priv(indio_dev);
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
data->client = client;
|
||||
|
||||
mutex_init(&data->lock);
|
||||
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->info = &us5182d_info;
|
||||
indio_dev->name = US5182D_DRV_NAME;
|
||||
indio_dev->channels = us5182d_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(us5182d_channels);
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(data->client, US5182D_REG_CHIPID);
|
||||
if (ret != US5182D_CHIPID) {
|
||||
dev_err(&data->client->dev,
|
||||
"Failed to detect US5182 light chip\n");
|
||||
return (ret < 0) ? ret : -ENODEV;
|
||||
}
|
||||
|
||||
us5182d_get_platform_data(indio_dev);
|
||||
ret = us5182d_init(indio_dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = us5182d_dark_gain_config(indio_dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return iio_device_register(indio_dev);
|
||||
}
|
||||
|
||||
static int us5182d_remove(struct i2c_client *client)
|
||||
{
|
||||
iio_device_unregister(i2c_get_clientdata(client));
|
||||
return i2c_smbus_write_byte_data(client, US5182D_REG_CFG0,
|
||||
US5182D_CFG0_SHUTDOWN_EN);
|
||||
}
|
||||
|
||||
static const struct acpi_device_id us5182d_acpi_match[] = {
|
||||
{ "USD5182", 0},
|
||||
{}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(acpi, us5182d_acpi_match);
|
||||
|
||||
static const struct i2c_device_id us5182d_id[] = {
|
||||
{"usd5182", 0},
|
||||
{}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(i2c, us5182d_id);
|
||||
|
||||
static struct i2c_driver us5182d_driver = {
|
||||
.driver = {
|
||||
.name = US5182D_DRV_NAME,
|
||||
.acpi_match_table = ACPI_PTR(us5182d_acpi_match),
|
||||
},
|
||||
.probe = us5182d_probe,
|
||||
.remove = us5182d_remove,
|
||||
.id_table = us5182d_id,
|
||||
|
||||
};
|
||||
module_i2c_driver(us5182d_driver);
|
||||
|
||||
MODULE_AUTHOR("Adriana Reus <adriana.reus@intel.com>");
|
||||
MODULE_DESCRIPTION("Driver for us5182d Proximity and Light Sensor");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -24,6 +24,24 @@ config AK09911
|
||||
help
|
||||
Deprecated: AK09911 is now supported by AK8975 driver.
|
||||
|
||||
config BMC150_MAGN
|
||||
tristate "Bosch BMC150 Magnetometer Driver"
|
||||
depends on I2C
|
||||
select REGMAP_I2C
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
Say yes here to build support for the BMC150 magnetometer.
|
||||
|
||||
Currently this only supports the device via an i2c interface.
|
||||
|
||||
This is a combo module with both accelerometer and magnetometer.
|
||||
This driver is only implementing magnetometer part, which has
|
||||
its own address and register map.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called bmc150_magn.
|
||||
|
||||
config MAG3110
|
||||
tristate "Freescale MAG3110 3-Axis Magnetometer"
|
||||
depends on I2C
|
||||
@ -87,19 +105,4 @@ config IIO_ST_MAGN_SPI_3AXIS
|
||||
depends on IIO_ST_MAGN_3AXIS
|
||||
depends on IIO_ST_SENSORS_SPI
|
||||
|
||||
config BMC150_MAGN
|
||||
tristate "Bosch BMC150 Magnetometer Driver"
|
||||
depends on I2C
|
||||
select REGMAP_I2C
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
Say yes here to build support for the BMC150 magnetometer.
|
||||
|
||||
Currently this only supports the device via an i2c interface.
|
||||
|
||||
This is a combo module with both accelerometer and magnetometer.
|
||||
This driver is only implementing magnetometer part, which has
|
||||
its own address and register map.
|
||||
|
||||
endmenu
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
# When adding new entries keep the list in alphabetical order
|
||||
obj-$(CONFIG_AK8975) += ak8975.o
|
||||
obj-$(CONFIG_BMC150_MAGN) += bmc150_magn.o
|
||||
obj-$(CONFIG_MAG3110) += mag3110.o
|
||||
obj-$(CONFIG_HID_SENSOR_MAGNETOMETER_3D) += hid-sensor-magn-3d.o
|
||||
obj-$(CONFIG_MMC35240) += mmc35240.o
|
||||
@ -14,5 +15,3 @@ st_magn-$(CONFIG_IIO_BUFFER) += st_magn_buffer.o
|
||||
|
||||
obj-$(CONFIG_IIO_ST_MAGN_I2C_3AXIS) += st_magn_i2c.o
|
||||
obj-$(CONFIG_IIO_ST_MAGN_SPI_3AXIS) += st_magn_spi.o
|
||||
|
||||
obj-$(CONFIG_BMC150_MAGN) += bmc150_magn.o
|
||||
|
@ -560,6 +560,7 @@ static const struct iio_info magn_info = {
|
||||
.attrs = &st_magn_attribute_group,
|
||||
.read_raw = &st_magn_read_raw,
|
||||
.write_raw = &st_magn_write_raw,
|
||||
.debugfs_reg_access = &st_sensors_debugfs_reg_access,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_IIO_TRIGGER
|
||||
|
@ -400,6 +400,7 @@ static const struct iio_info press_info = {
|
||||
.attrs = &st_press_attribute_group,
|
||||
.read_raw = &st_press_read_raw,
|
||||
.write_raw = &st_press_write_raw,
|
||||
.debugfs_reg_access = &st_sensors_debugfs_reg_access,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_IIO_TRIGGER
|
||||
|
@ -20,6 +20,18 @@ endmenu
|
||||
|
||||
menu "Proximity sensors"
|
||||
|
||||
config LIDAR_LITE_V2
|
||||
tristate "PulsedLight LIDAR sensor"
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
depends on I2C
|
||||
help
|
||||
Say Y to build a driver for PulsedLight LIDAR range finding
|
||||
sensor.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called pulsedlight-lite-v2
|
||||
|
||||
config SX9500
|
||||
tristate "SX9500 Semtech proximity sensor"
|
||||
select IIO_BUFFER
|
||||
|
@ -4,4 +4,5 @@
|
||||
|
||||
# When adding new entries keep the list in alphabetical order
|
||||
obj-$(CONFIG_AS3935) += as3935.o
|
||||
obj-$(CONFIG_LIDAR_LITE_V2) += pulsedlight-lidar-lite-v2.o
|
||||
obj-$(CONFIG_SX9500) += sx9500.o
|
||||
|
@ -434,6 +434,12 @@ static int as3935_remove(struct spi_device *spi)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id as3935_of_match[] = {
|
||||
{ .compatible = "ams,as3935", },
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, as3935_of_match);
|
||||
|
||||
static const struct spi_device_id as3935_id[] = {
|
||||
{"as3935", 0},
|
||||
{},
|
||||
@ -443,6 +449,7 @@ MODULE_DEVICE_TABLE(spi, as3935_id);
|
||||
static struct spi_driver as3935_driver = {
|
||||
.driver = {
|
||||
.name = "as3935",
|
||||
.of_match_table = of_match_ptr(as3935_of_match),
|
||||
.owner = THIS_MODULE,
|
||||
.pm = AS3935_PM_OPS,
|
||||
},
|
||||
|
288
drivers/iio/proximity/pulsedlight-lidar-lite-v2.c
Normal file
288
drivers/iio/proximity/pulsedlight-lidar-lite-v2.c
Normal file
@ -0,0 +1,288 @@
|
||||
/*
|
||||
* pulsedlight-lidar-lite-v2.c - Support for PulsedLight LIDAR sensor
|
||||
*
|
||||
* Copyright (C) 2015 Matt Ranostay <mranostay@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.
|
||||
*
|
||||
* TODO: runtime pm, interrupt mode, and signal strength reporting
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
|
||||
#define LIDAR_REG_CONTROL 0x00
|
||||
#define LIDAR_REG_CONTROL_ACQUIRE BIT(2)
|
||||
|
||||
#define LIDAR_REG_STATUS 0x01
|
||||
#define LIDAR_REG_STATUS_INVALID BIT(3)
|
||||
#define LIDAR_REG_STATUS_READY BIT(0)
|
||||
|
||||
#define LIDAR_REG_DATA_HBYTE 0x0f
|
||||
#define LIDAR_REG_DATA_LBYTE 0x10
|
||||
|
||||
#define LIDAR_DRV_NAME "lidar"
|
||||
|
||||
struct lidar_data {
|
||||
struct iio_dev *indio_dev;
|
||||
struct i2c_client *client;
|
||||
|
||||
u16 buffer[8]; /* 2 byte distance + 8 byte timestamp */
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec lidar_channels[] = {
|
||||
{
|
||||
.type = IIO_DISTANCE,
|
||||
.info_mask_separate =
|
||||
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
|
||||
.scan_index = 0,
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
.realbits = 16,
|
||||
.storagebits = 16,
|
||||
},
|
||||
},
|
||||
IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
};
|
||||
|
||||
static int lidar_read_byte(struct lidar_data *data, int reg)
|
||||
{
|
||||
struct i2c_client *client = data->client;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Device needs a STOP condition between address write, and data read
|
||||
* so in turn i2c_smbus_read_byte_data cannot be used
|
||||
*/
|
||||
|
||||
ret = i2c_smbus_write_byte(client, reg);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "cannot write addr value");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = i2c_smbus_read_byte(client);
|
||||
if (ret < 0)
|
||||
dev_err(&client->dev, "cannot read data value");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int lidar_write_control(struct lidar_data *data, int val)
|
||||
{
|
||||
return i2c_smbus_write_byte_data(data->client, LIDAR_REG_CONTROL, val);
|
||||
}
|
||||
|
||||
static int lidar_read_measurement(struct lidar_data *data, u16 *reg)
|
||||
{
|
||||
int ret;
|
||||
int val;
|
||||
|
||||
ret = lidar_read_byte(data, LIDAR_REG_DATA_HBYTE);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
val = ret << 8;
|
||||
|
||||
ret = lidar_read_byte(data, LIDAR_REG_DATA_LBYTE);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
val |= ret;
|
||||
*reg = val;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lidar_get_measurement(struct lidar_data *data, u16 *reg)
|
||||
{
|
||||
struct i2c_client *client = data->client;
|
||||
int tries = 10;
|
||||
int ret;
|
||||
|
||||
/* start sample */
|
||||
ret = lidar_write_control(data, LIDAR_REG_CONTROL_ACQUIRE);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "cannot send start measurement command");
|
||||
return ret;
|
||||
}
|
||||
|
||||
while (tries--) {
|
||||
usleep_range(1000, 2000);
|
||||
|
||||
ret = lidar_read_byte(data, LIDAR_REG_STATUS);
|
||||
if (ret < 0)
|
||||
break;
|
||||
|
||||
/* return 0 since laser is likely pointed out of range */
|
||||
if (ret & LIDAR_REG_STATUS_INVALID) {
|
||||
*reg = 0;
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
/* sample ready to read */
|
||||
if (!(ret & LIDAR_REG_STATUS_READY)) {
|
||||
ret = lidar_read_measurement(data, reg);
|
||||
break;
|
||||
}
|
||||
ret = -EIO;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int lidar_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct lidar_data *data = iio_priv(indio_dev);
|
||||
int ret = -EINVAL;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
|
||||
if (iio_buffer_enabled(indio_dev) && mask == IIO_CHAN_INFO_RAW) {
|
||||
ret = -EBUSY;
|
||||
goto error_busy;
|
||||
}
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW: {
|
||||
u16 reg;
|
||||
|
||||
ret = lidar_get_measurement(data, ®);
|
||||
if (!ret) {
|
||||
*val = reg;
|
||||
ret = IIO_VAL_INT;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = 0;
|
||||
*val2 = 10000;
|
||||
ret = IIO_VAL_INT_PLUS_MICRO;
|
||||
break;
|
||||
}
|
||||
|
||||
error_busy:
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static irqreturn_t lidar_trigger_handler(int irq, void *private)
|
||||
{
|
||||
struct iio_poll_func *pf = private;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct lidar_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = lidar_get_measurement(data, data->buffer);
|
||||
if (!ret) {
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
|
||||
iio_get_time_ns());
|
||||
} else {
|
||||
dev_err(&data->client->dev, "cannot read LIDAR measurement");
|
||||
}
|
||||
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static const struct iio_info lidar_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = lidar_read_raw,
|
||||
};
|
||||
|
||||
static int lidar_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct lidar_data *data;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
indio_dev->info = &lidar_info;
|
||||
indio_dev->name = LIDAR_DRV_NAME;
|
||||
indio_dev->channels = lidar_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(lidar_channels);
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
data = iio_priv(indio_dev);
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
|
||||
data->client = client;
|
||||
data->indio_dev = indio_dev;
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, NULL,
|
||||
lidar_trigger_handler, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto error_unreg_buffer;
|
||||
|
||||
return 0;
|
||||
|
||||
error_unreg_buffer:
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int lidar_remove(struct i2c_client *client)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id lidar_id[] = {
|
||||
{"lidar-lite-v2", 0},
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, lidar_id);
|
||||
|
||||
static const struct of_device_id lidar_dt_ids[] = {
|
||||
{ .compatible = "pulsedlight,lidar-lite-v2" },
|
||||
{ }
|
||||
};
|
||||
|
||||
static struct i2c_driver lidar_driver = {
|
||||
.driver = {
|
||||
.name = LIDAR_DRV_NAME,
|
||||
.of_match_table = of_match_ptr(lidar_dt_ids),
|
||||
},
|
||||
.probe = lidar_probe,
|
||||
.remove = lidar_remove,
|
||||
.id_table = lidar_id,
|
||||
};
|
||||
module_i2c_driver(lidar_driver);
|
||||
|
||||
MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
|
||||
MODULE_DESCRIPTION("PulsedLight LIDAR sensor");
|
||||
MODULE_LICENSE("GPL");
|
@ -3,6 +3,7 @@
|
||||
*
|
||||
* Copyright (c) 2014 Peter Meerwald <pmeerw@pmeerw.net>
|
||||
* Copyright (c) 2015 Essensium NV
|
||||
* Copyright (c) 2015 Melexis
|
||||
*
|
||||
* This file is subject to the terms and conditions of version 2 of
|
||||
* the GNU General Public License. See the file COPYING in the main
|
||||
@ -20,7 +21,6 @@
|
||||
* always has a pull-up so we do not need an extra GPIO to drive it high. If
|
||||
* the "wakeup" GPIO is not given, power management will be disabled.
|
||||
*
|
||||
* TODO: filter configuration
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
@ -32,6 +32,7 @@
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
#define MLX90614_OP_RAM 0x00
|
||||
#define MLX90614_OP_EEPROM 0x20
|
||||
@ -79,6 +80,20 @@ struct mlx90614_data {
|
||||
unsigned long ready_timestamp; /* in jiffies */
|
||||
};
|
||||
|
||||
/* Bandwidth values for IIR filtering */
|
||||
static const int mlx90614_iir_values[] = {77, 31, 20, 15, 723, 153, 110, 86};
|
||||
static IIO_CONST_ATTR(in_temp_object_filter_low_pass_3db_frequency_available,
|
||||
"0.15 0.20 0.31 0.77 0.86 1.10 1.53 7.23");
|
||||
|
||||
static struct attribute *mlx90614_attributes[] = {
|
||||
&iio_const_attr_in_temp_object_filter_low_pass_3db_frequency_available.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group mlx90614_attr_group = {
|
||||
.attrs = mlx90614_attributes,
|
||||
};
|
||||
|
||||
/*
|
||||
* Erase an address and write word.
|
||||
* The mutex must be locked before calling.
|
||||
@ -117,6 +132,42 @@ static s32 mlx90614_write_word(const struct i2c_client *client, u8 command,
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the IIR value inside mlx90614_iir_values array and return its position
|
||||
* which is equivalent to the bit value in sensor register
|
||||
*/
|
||||
static inline s32 mlx90614_iir_search(const struct i2c_client *client,
|
||||
int value)
|
||||
{
|
||||
int i;
|
||||
s32 ret;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(mlx90614_iir_values); ++i) {
|
||||
if (value == mlx90614_iir_values[i])
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == ARRAY_SIZE(mlx90614_iir_values))
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* CONFIG register values must not be changed so
|
||||
* we must read them before we actually write
|
||||
* changes
|
||||
*/
|
||||
ret = i2c_smbus_read_word_data(client, MLX90614_CONFIG);
|
||||
if (ret > 0)
|
||||
return ret;
|
||||
|
||||
/* Write changed values */
|
||||
ret = mlx90614_write_word(client, MLX90614_CONFIG,
|
||||
(i << MLX90614_CONFIG_IIR_SHIFT) |
|
||||
(((u16) ((0x7 << MLX90614_CONFIG_FIR_SHIFT) |
|
||||
((u16) ret & (~((u16) MLX90614_CONFIG_FIR_MASK))))) &
|
||||
(~(u16) MLX90614_CONFIG_IIR_MASK)));
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
/*
|
||||
* If @startup is true, make sure MLX90614_TIMING_STARTUP ms have elapsed since
|
||||
@ -236,6 +287,21 @@ static int mlx90614_read_raw(struct iio_dev *indio_dev,
|
||||
*val2 = ret * MLX90614_CONST_EMISSIVITY_RESOLUTION;
|
||||
}
|
||||
return IIO_VAL_INT_PLUS_NANO;
|
||||
case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: /* IIR setting with
|
||||
FIR = 1024 */
|
||||
mlx90614_power_get(data, false);
|
||||
mutex_lock(&data->lock);
|
||||
ret = i2c_smbus_read_word_data(data->client, MLX90614_CONFIG);
|
||||
mutex_unlock(&data->lock);
|
||||
mlx90614_power_put(data);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = mlx90614_iir_values[ret & MLX90614_CONFIG_IIR_MASK] / 100;
|
||||
*val2 = (mlx90614_iir_values[ret & MLX90614_CONFIG_IIR_MASK] % 100) *
|
||||
10000;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -262,6 +328,18 @@ static int mlx90614_write_raw(struct iio_dev *indio_dev,
|
||||
mutex_unlock(&data->lock);
|
||||
mlx90614_power_put(data);
|
||||
|
||||
return ret;
|
||||
case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: /* IIR Filter setting */
|
||||
if (val < 0 || val2 < 0)
|
||||
return -EINVAL;
|
||||
|
||||
mlx90614_power_get(data, false);
|
||||
mutex_lock(&data->lock);
|
||||
ret = mlx90614_iir_search(data->client,
|
||||
val * 100 + val2 / 10000);
|
||||
mutex_unlock(&data->lock);
|
||||
mlx90614_power_put(data);
|
||||
|
||||
return ret;
|
||||
default:
|
||||
return -EINVAL;
|
||||
@ -275,6 +353,8 @@ static int mlx90614_write_raw_get_fmt(struct iio_dev *indio_dev,
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_CALIBEMISSIVITY:
|
||||
return IIO_VAL_INT_PLUS_NANO;
|
||||
case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -294,7 +374,8 @@ static const struct iio_chan_spec mlx90614_channels[] = {
|
||||
.modified = 1,
|
||||
.channel2 = IIO_MOD_TEMP_OBJECT,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_CALIBEMISSIVITY),
|
||||
BIT(IIO_CHAN_INFO_CALIBEMISSIVITY) |
|
||||
BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY),
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
},
|
||||
@ -305,7 +386,8 @@ static const struct iio_chan_spec mlx90614_channels[] = {
|
||||
.channel = 1,
|
||||
.channel2 = IIO_MOD_TEMP_OBJECT,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_CALIBEMISSIVITY),
|
||||
BIT(IIO_CHAN_INFO_CALIBEMISSIVITY) |
|
||||
BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY),
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
},
|
||||
@ -315,6 +397,7 @@ static const struct iio_info mlx90614_info = {
|
||||
.read_raw = mlx90614_read_raw,
|
||||
.write_raw = mlx90614_write_raw,
|
||||
.write_raw_get_fmt = mlx90614_write_raw_get_fmt,
|
||||
.attrs = &mlx90614_attr_group,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
@ -569,5 +652,6 @@ module_i2c_driver(mlx90614_driver);
|
||||
|
||||
MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>");
|
||||
MODULE_AUTHOR("Vianney le Clément de Saint-Marcq <vianney.leclement@essensium.com>");
|
||||
MODULE_AUTHOR("Crt Mori <cmo@melexis.com>");
|
||||
MODULE_DESCRIPTION("Melexis MLX90614 contactless IR temperature sensor driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -137,7 +137,7 @@ static int lpc32xx_adc_probe(struct platform_device *pdev)
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "failed to get platform I/O memory\n");
|
||||
return -EBUSY;
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
iodev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));
|
||||
@ -162,7 +162,7 @@ static int lpc32xx_adc_probe(struct platform_device *pdev)
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq <= 0) {
|
||||
dev_err(&pdev->dev, "failed getting interrupt resource\n");
|
||||
return -EINVAL;
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
retval = devm_request_irq(&pdev->dev, irq, lpc32xx_adc_isr, 0,
|
||||
|
@ -24,9 +24,21 @@
|
||||
#include "iio_dummy_evgen.h"
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/irq_work.h>
|
||||
|
||||
/* Fiddly bit of faking and irq without hardware */
|
||||
#define IIO_EVENTGEN_NO 10
|
||||
|
||||
/**
|
||||
* struct iio_dummy_handle_irq - helper struct to simulate interrupt generation
|
||||
* @work: irq_work used to run handlers from hardirq context
|
||||
* @irq: fake irq line number to trigger an interrupt
|
||||
*/
|
||||
struct iio_dummy_handle_irq {
|
||||
struct irq_work work;
|
||||
int irq;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct iio_dummy_evgen - evgen state
|
||||
* @chip: irq chip we are faking
|
||||
@ -35,6 +47,7 @@
|
||||
* @inuse: mask of which irqs are connected
|
||||
* @regs: irq regs we are faking
|
||||
* @lock: protect the evgen state
|
||||
* @handler: helper for a 'hardware-like' interrupt simulation
|
||||
*/
|
||||
struct iio_dummy_eventgen {
|
||||
struct irq_chip chip;
|
||||
@ -43,6 +56,7 @@ struct iio_dummy_eventgen {
|
||||
bool inuse[IIO_EVENTGEN_NO];
|
||||
struct iio_dummy_regs regs[IIO_EVENTGEN_NO];
|
||||
struct mutex lock;
|
||||
struct iio_dummy_handle_irq handler;
|
||||
};
|
||||
|
||||
/* We can only ever have one instance of this 'device' */
|
||||
@ -67,6 +81,14 @@ static void iio_dummy_event_irqunmask(struct irq_data *d)
|
||||
evgen->enabled[d->irq - evgen->base] = true;
|
||||
}
|
||||
|
||||
static void iio_dummy_work_handler(struct irq_work *work)
|
||||
{
|
||||
struct iio_dummy_handle_irq *irq_handler;
|
||||
|
||||
irq_handler = container_of(work, struct iio_dummy_handle_irq, work);
|
||||
handle_simple_irq(irq_handler->irq, irq_to_desc(irq_handler->irq));
|
||||
}
|
||||
|
||||
static int iio_dummy_evgen_create(void)
|
||||
{
|
||||
int ret, i;
|
||||
@ -91,6 +113,7 @@ static int iio_dummy_evgen_create(void)
|
||||
IRQ_NOREQUEST | IRQ_NOAUTOEN,
|
||||
IRQ_NOPROBE);
|
||||
}
|
||||
init_irq_work(&iio_evgen->handler.work, iio_dummy_work_handler);
|
||||
mutex_init(&iio_evgen->lock);
|
||||
return 0;
|
||||
}
|
||||
@ -169,8 +192,9 @@ static ssize_t iio_evgen_poke(struct device *dev,
|
||||
iio_evgen->regs[this_attr->address].reg_id = this_attr->address;
|
||||
iio_evgen->regs[this_attr->address].reg_data = event;
|
||||
|
||||
iio_evgen->handler.irq = iio_evgen->base + this_attr->address;
|
||||
if (iio_evgen->enabled[this_attr->address])
|
||||
handle_nested_irq(iio_evgen->base + this_attr->address);
|
||||
irq_work_queue(&iio_evgen->handler.work);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
@ -46,6 +46,7 @@ struct iio_dummy_state {
|
||||
int event_irq;
|
||||
int event_val;
|
||||
bool event_en;
|
||||
s64 event_timestamp;
|
||||
#endif /* CONFIG_IIO_SIMPLE_DUMMY_EVENTS */
|
||||
};
|
||||
|
||||
|
@ -153,6 +153,15 @@ int iio_simple_dummy_write_event_value(struct iio_dev *indio_dev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t iio_simple_dummy_get_timestamp(int irq, void *private)
|
||||
{
|
||||
struct iio_dev *indio_dev = private;
|
||||
struct iio_dummy_state *st = iio_priv(indio_dev);
|
||||
|
||||
st->event_timestamp = iio_get_time_ns();
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* iio_simple_dummy_event_handler() - identify and pass on event
|
||||
* @irq: irq of event line
|
||||
@ -177,7 +186,7 @@ static irqreturn_t iio_simple_dummy_event_handler(int irq, void *private)
|
||||
IIO_EVENT_CODE(IIO_VOLTAGE, 0, 0,
|
||||
IIO_EV_DIR_RISING,
|
||||
IIO_EV_TYPE_THRESH, 0, 0, 0),
|
||||
iio_get_time_ns());
|
||||
st->event_timestamp);
|
||||
break;
|
||||
case 1:
|
||||
if (st->activity_running > st->event_val)
|
||||
@ -187,7 +196,7 @@ static irqreturn_t iio_simple_dummy_event_handler(int irq, void *private)
|
||||
IIO_EV_DIR_RISING,
|
||||
IIO_EV_TYPE_THRESH,
|
||||
0, 0, 0),
|
||||
iio_get_time_ns());
|
||||
st->event_timestamp);
|
||||
break;
|
||||
case 2:
|
||||
if (st->activity_walking < st->event_val)
|
||||
@ -197,14 +206,14 @@ static irqreturn_t iio_simple_dummy_event_handler(int irq, void *private)
|
||||
IIO_EV_DIR_FALLING,
|
||||
IIO_EV_TYPE_THRESH,
|
||||
0, 0, 0),
|
||||
iio_get_time_ns());
|
||||
st->event_timestamp);
|
||||
break;
|
||||
case 3:
|
||||
iio_push_event(indio_dev,
|
||||
IIO_EVENT_CODE(IIO_STEPS, 0, IIO_NO_MOD,
|
||||
IIO_EV_DIR_NONE,
|
||||
IIO_EV_TYPE_CHANGE, 0, 0, 0),
|
||||
iio_get_time_ns());
|
||||
st->event_timestamp);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@ -238,7 +247,7 @@ int iio_simple_dummy_events_register(struct iio_dev *indio_dev)
|
||||
st->regs = iio_dummy_evgen_get_regs(st->event_irq);
|
||||
|
||||
ret = request_threaded_irq(st->event_irq,
|
||||
NULL,
|
||||
&iio_simple_dummy_get_timestamp,
|
||||
&iio_simple_dummy_event_handler,
|
||||
IRQF_ONESHOT,
|
||||
"iio_simple_event",
|
||||
|
@ -48,7 +48,7 @@ struct hmc5843_data {
|
||||
};
|
||||
|
||||
int hmc5843_common_probe(struct device *dev, struct regmap *regmap,
|
||||
enum hmc5843_ids id);
|
||||
enum hmc5843_ids id, const char *name);
|
||||
int hmc5843_common_remove(struct device *dev);
|
||||
|
||||
int hmc5843_common_suspend(struct device *dev);
|
||||
|
@ -577,7 +577,7 @@ int hmc5843_common_resume(struct device *dev)
|
||||
EXPORT_SYMBOL(hmc5843_common_resume);
|
||||
|
||||
int hmc5843_common_probe(struct device *dev, struct regmap *regmap,
|
||||
enum hmc5843_ids id)
|
||||
enum hmc5843_ids id, const char *name)
|
||||
{
|
||||
struct hmc5843_data *data;
|
||||
struct iio_dev *indio_dev;
|
||||
@ -597,7 +597,7 @@ int hmc5843_common_probe(struct device *dev, struct regmap *regmap,
|
||||
mutex_init(&data->lock);
|
||||
|
||||
indio_dev->dev.parent = dev;
|
||||
indio_dev->name = dev->driver->name;
|
||||
indio_dev->name = name;
|
||||
indio_dev->info = &hmc5843_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = data->variant->channels;
|
||||
|
@ -61,7 +61,7 @@ static int hmc5843_i2c_probe(struct i2c_client *cli,
|
||||
{
|
||||
return hmc5843_common_probe(&cli->dev,
|
||||
devm_regmap_init_i2c(cli, &hmc5843_i2c_regmap_config),
|
||||
id->driver_data);
|
||||
id->driver_data, id->name);
|
||||
}
|
||||
|
||||
static int hmc5843_i2c_remove(struct i2c_client *client)
|
||||
|
@ -59,6 +59,7 @@ static const struct regmap_config hmc5843_spi_regmap_config = {
|
||||
static int hmc5843_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
int ret;
|
||||
const struct spi_device_id *id = spi_get_device_id(spi);
|
||||
|
||||
spi->mode = SPI_MODE_3;
|
||||
spi->max_speed_hz = 8000000;
|
||||
@ -69,7 +70,7 @@ static int hmc5843_spi_probe(struct spi_device *spi)
|
||||
|
||||
return hmc5843_common_probe(&spi->dev,
|
||||
devm_regmap_init_spi(spi, &hmc5843_spi_regmap_config),
|
||||
HMC5983_ID);
|
||||
id->driver_data, id->name);
|
||||
}
|
||||
|
||||
static int hmc5843_spi_remove(struct spi_device *spi)
|
||||
@ -81,6 +82,7 @@ static const struct spi_device_id hmc5843_id[] = {
|
||||
{ "hmc5983", HMC5983_ID },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, hmc5843_id);
|
||||
|
||||
static struct spi_driver hmc5843_driver = {
|
||||
.driver = {
|
||||
|
@ -271,6 +271,10 @@ void st_sensors_power_enable(struct iio_dev *indio_dev);
|
||||
|
||||
void st_sensors_power_disable(struct iio_dev *indio_dev);
|
||||
|
||||
int st_sensors_debugfs_reg_access(struct iio_dev *indio_dev,
|
||||
unsigned reg, unsigned writeval,
|
||||
unsigned *readval);
|
||||
|
||||
int st_sensors_set_odr(struct iio_dev *indio_dev, unsigned int odr);
|
||||
|
||||
int st_sensors_set_dataready_irq(struct iio_dev *indio_dev, bool enable);
|
||||
|
@ -294,6 +294,7 @@ static inline s64 iio_get_time_ns(void)
|
||||
#define INDIO_BUFFER_TRIGGERED 0x02
|
||||
#define INDIO_BUFFER_SOFTWARE 0x04
|
||||
#define INDIO_BUFFER_HARDWARE 0x08
|
||||
#define INDIO_EVENT_TRIGGERED 0x10
|
||||
|
||||
#define INDIO_ALL_BUFFER_MODES \
|
||||
(INDIO_BUFFER_TRIGGERED | INDIO_BUFFER_HARDWARE | INDIO_BUFFER_SOFTWARE)
|
||||
@ -457,6 +458,7 @@ struct iio_buffer_setup_ops {
|
||||
* @scan_index_timestamp:[INTERN] cache of the index to the timestamp
|
||||
* @trig: [INTERN] current device trigger (buffer modes)
|
||||
* @pollfunc: [DRIVER] function run on trigger being received
|
||||
* @pollfunc_event: [DRIVER] function run on events trigger being received
|
||||
* @channels: [DRIVER] channel specification structure table
|
||||
* @num_channels: [DRIVER] number of channels specified in @channels.
|
||||
* @channel_attr_list: [INTERN] keep track of automatically created channel
|
||||
@ -495,6 +497,7 @@ struct iio_dev {
|
||||
unsigned scan_index_timestamp;
|
||||
struct iio_trigger *trig;
|
||||
struct iio_poll_func *pollfunc;
|
||||
struct iio_poll_func *pollfunc_event;
|
||||
|
||||
struct iio_chan_spec const *channels;
|
||||
int num_channels;
|
||||
|
11
include/linux/iio/triggered_event.h
Normal file
11
include/linux/iio/triggered_event.h
Normal file
@ -0,0 +1,11 @@
|
||||
#ifndef _LINUX_IIO_TRIGGERED_EVENT_H_
|
||||
#define _LINUX_IIO_TRIGGERED_EVENT_H_
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
int iio_triggered_event_setup(struct iio_dev *indio_dev,
|
||||
irqreturn_t (*h)(int irq, void *p),
|
||||
irqreturn_t (*thread)(int irq, void *p));
|
||||
void iio_triggered_event_cleanup(struct iio_dev *indio_dev);
|
||||
|
||||
#endif
|
@ -35,6 +35,8 @@ enum iio_chan_type {
|
||||
IIO_ENERGY,
|
||||
IIO_DISTANCE,
|
||||
IIO_VELOCITY,
|
||||
IIO_CONCENTRATION,
|
||||
IIO_RESISTANCE,
|
||||
};
|
||||
|
||||
enum iio_modifier {
|
||||
@ -72,6 +74,8 @@ enum iio_modifier {
|
||||
IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z,
|
||||
IIO_MOD_I,
|
||||
IIO_MOD_Q,
|
||||
IIO_MOD_CO2,
|
||||
IIO_MOD_VOC,
|
||||
};
|
||||
|
||||
enum iio_event_type {
|
||||
|
@ -328,6 +328,15 @@ int main(int argc, char **argv)
|
||||
"diag %s\n", dev_dir_name);
|
||||
goto error_free_triggername;
|
||||
}
|
||||
if (!num_channels) {
|
||||
fprintf(stderr,
|
||||
"No channels are enabled, we have nothing to scan.\n");
|
||||
fprintf(stderr, "Enable channels manually in "
|
||||
FORMAT_SCAN_ELEMENTS_DIR
|
||||
"/*_en and try again.\n", dev_dir_name);
|
||||
ret = -ENOENT;
|
||||
goto error_free_triggername;
|
||||
}
|
||||
|
||||
/*
|
||||
* Construct the directory name for the associated buffer.
|
||||
|
@ -284,7 +284,11 @@ int main(int argc, char **argv)
|
||||
ret = ioctl(fd, IIO_GET_EVENT_FD_IOCTL, &event_fd);
|
||||
if (ret == -1 || event_fd == -1) {
|
||||
ret = -errno;
|
||||
fprintf(stderr, "Failed to retrieve event fd\n");
|
||||
if (ret == -ENODEV)
|
||||
fprintf(stderr,
|
||||
"This device does not support events\n");
|
||||
else
|
||||
fprintf(stderr, "Failed to retrieve event fd\n");
|
||||
if (close(fd) == -1)
|
||||
perror("Failed to close character device file");
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user