Staging/IIO patches for 4.10-rc1

Here's the "big" staging/iio pull request for 4.10-rc1.
 
 Not as big as 4.9 was, but still just over a thousand changes.  We
 almost broke even of lines added vs. removed, as the slicoss driver was
 removed (got a "clean" driver for the same hardware through the netdev
 tree), and some iio drivers were also dropped, but I think we ended up
 adding a few thousand lines to the source tree in the end.  Other than
 that it's a lot of minor fixes all over the place, nothing major stands
 out at all.
 
 All of these have been in linux-next for a while.  There will be a merge
 conflict with Al's vfs tree in the lustre code, but the resolution for
 that should be pretty simple, that too has been in linux-next.
 
 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 -----BEGIN PGP SIGNATURE-----
 
 iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCWFAk1Q8cZ3JlZ0Brcm9h
 aC5jb20ACgkQMUfUDdst+ymikACg05b0h/iVTTH18474PXXnzw6jk9IAn0gI2fx9
 cqp2MglTvphhrXzddL7V
 =MeTw
 -----END PGP SIGNATURE-----

Merge tag 'staging-4.10-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging

Pull staging/IIO updates from Greg KH:
 "Here's the "big" staging/iio pull request for 4.10-rc1.

  Not as big as 4.9 was, but still just over a thousand changes. We
  almost broke even of lines added vs. removed, as the slicoss driver
  was removed (got a "clean" driver for the same hardware through the
  netdev tree), and some iio drivers were also dropped, but I think we
  ended up adding a few thousand lines to the source tree in the end.
  Other than that it's a lot of minor fixes all over the place, nothing
  major stands out at all.

  All of these have been in linux-next for a while. There will be a
  merge conflict with Al's vfs tree in the lustre code, but the
  resolution for that should be pretty simple, that too has been in
  linux-next"

* tag 'staging-4.10-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging: (1002 commits)
  staging: comedi: comedidev.h: Document usage of 'detach' handler
  staging: fsl-mc: remove unnecessary info prints from bus driver
  staging: fsl-mc: add sysfs ABI doc
  staging/lustre/o2iblnd: Fix misspelled attemps->attempts
  staging/lustre/o2iblnd: Fix misspelling intialized->intialized
  staging/lustre: Convert all bare unsigned to unsigned int
  staging/lustre/socklnd: Fix whitespace problem
  staging/lustre/o2iblnd: Add missing space
  staging/lustre/lnetselftest: Fix potential integer overflow
  staging: greybus: audio_module: remove redundant OOM message
  staging: dgnc: Fix lines longer than 80 characters
  staging: dgnc: fix blank line after '{' warnings.
  staging/android: remove Sync Framework tasks from TODO
  staging/lustre/osc: Revert erroneous list_for_each_entry_safe use
  staging: slicoss: remove the staging driver
  staging: lustre: libcfs: remove lnet upcall code
  staging: lustre: remove set but unused variables
  staging: lustre: osc: set lock data for readahead lock
  staging: lustre: import: don't reconnect during connect interpret
  staging: lustre: clio: remove mtime check in vvp_io_fault_start()
  ...
This commit is contained in:
Linus Torvalds 2016-12-13 11:35:00 -08:00
Родитель 5266e70335 3e0f9b2ca8
Коммит 72cca7baf4
685 изменённых файлов: 33577 добавлений и 31432 удалений

Просмотреть файл

@ -0,0 +1,21 @@
What: /sys/bus/fsl-mc/drivers/.../bind
Date: December 2016
Contact: stuart.yoder@nxp.com
Description:
Writing a device location to this file will cause
the driver to attempt to bind to the device found at
this location. The format for the location is Object.Id
and is the same as found in /sys/bus/fsl-mc/devices/.
For example:
# echo dpni.2 > /sys/bus/fsl-mc/drivers/fsl_dpaa2_eth/bind
What: /sys/bus/fsl-mc/drivers/.../unbind
Date: December 2016
Contact: stuart.yoder@nxp.com
Description:
Writing a device location to this file will cause the
driver to attempt to unbind from the device found at
this location. The format for the location is Object.Id
and is the same as found in /sys/bus/fsl-mc/devices/.
For example:
# echo dpni.2 > /sys/bus/fsl-mc/drivers/fsl_dpaa2_eth/unbind

Просмотреть файл

@ -329,6 +329,7 @@ What: /sys/bus/iio/devices/iio:deviceX/in_pressure_scale
What: /sys/bus/iio/devices/iio:deviceX/in_humidityrelative_scale
What: /sys/bus/iio/devices/iio:deviceX/in_velocity_sqrt(x^2+y^2+z^2)_scale
What: /sys/bus/iio/devices/iio:deviceX/in_illuminance_scale
What: /sys/bus/iio/devices/iio:deviceX/in_countY_scale
KernelVersion: 2.6.35
Contact: linux-iio@vger.kernel.org
Description:
@ -1579,3 +1580,20 @@ Contact: linux-iio@vger.kernel.org
Description:
Raw (unscaled no offset etc.) electric conductivity reading that
can be processed to siemens per meter.
What: /sys/bus/iio/devices/iio:deviceX/in_countY_raw
KernelVersion: 4.9
Contact: linux-iio@vger.kernel.org
Description:
Raw counter device counts from channel Y. For quadrature
counters, multiplication by an available [Y]_scale results in
the counts of a single quadrature signal phase from channel Y.
What: /sys/bus/iio/devices/iio:deviceX/in_indexY_raw
KernelVersion: 4.9
Contact: linux-iio@vger.kernel.org
Description:
Raw counter device index value from channel Y. This attribute
provides an absolute positional reference (e.g. a pulse once per
revolution) which may be used to home positional systems as
required.

Просмотреть файл

@ -0,0 +1,36 @@
What: /sys/bus/iio/devices/iio:deviceX/in_altvoltageY_invert
Date: October 2016
KernelVersion: 4.9
Contact: Peter Rosin <peda@axentia.se>
Description:
The DAC is used to find the peak level of an alternating
voltage input signal by a binary search using the output
of a comparator wired to an interrupt pin. Like so:
_
| \
input +------>-------|+ \
| \
.-------. | }---.
| | | / |
| dac|-->--|- / |
| | |_/ |
| | |
| | |
| irq|------<-------'
| |
'-------'
The boolean invert attribute (0/1) should be set when the
input signal is centered around the maximum value of the
dac instead of zero. The envelope detector will search
from below in this case and will also invert the result.
The edge/level of the interrupt is also switched to its
opposite value.
What: /sys/bus/iio/devices/iio:deviceX/in_altvoltageY_compare_interval
Date: October 2016
KernelVersion: 4.9
Contact: Peter Rosin <peda@axentia.se>
Description:
Number of milliseconds to wait for the comparator in each
step of the binary search for the input peak level. Needs
to relate to the frequency of the input signal.

Просмотреть файл

@ -0,0 +1,125 @@
What: /sys/bus/iio/devices/iio:deviceX/in_count_count_direction_available
What: /sys/bus/iio/devices/iio:deviceX/in_count_count_mode_available
What: /sys/bus/iio/devices/iio:deviceX/in_count_noise_error_available
What: /sys/bus/iio/devices/iio:deviceX/in_count_quadrature_mode_available
What: /sys/bus/iio/devices/iio:deviceX/in_index_index_polarity_available
What: /sys/bus/iio/devices/iio:deviceX/in_index_synchronous_mode_available
KernelVersion: 4.9
Contact: linux-iio@vger.kernel.org
Description:
Discrete set of available values for the respective counter
configuration are listed in this file.
What: /sys/bus/iio/devices/iio:deviceX/in_countY_count_direction
KernelVersion: 4.9
Contact: linux-iio@vger.kernel.org
Description:
Read-only attribute that indicates whether the counter for
channel Y is counting up or down.
What: /sys/bus/iio/devices/iio:deviceX/in_countY_count_mode
KernelVersion: 4.9
Contact: linux-iio@vger.kernel.org
Description:
Count mode for channel Y. Four count modes are available:
normal, range limit, non-recycle, and modulo-n. The preset value
for channel Y is used by the count mode where required.
Normal:
Counting is continuous in either direction.
Range Limit:
An upper or lower limit is set, mimicking limit switches
in the mechanical counterpart. The upper limit is set to
the preset value, while the lower limit is set to 0. The
counter freezes at count = preset when counting up, and
at count = 0 when counting down. At either of these
limits, the counting is resumed only when the count
direction is reversed.
Non-recycle:
Counter is disabled whenever a 24-bit count overflow or
underflow takes place. The counter is re-enabled when a
new count value is loaded to the counter via a preset
operation or write to raw.
Modulo-N:
A count boundary is set between 0 and the preset value.
The counter is reset to 0 at count = preset when
counting up, while the counter is set to the preset
value at count = 0 when counting down; the counter does
not freeze at the bundary points, but counts
continuously throughout.
What: /sys/bus/iio/devices/iio:deviceX/in_countY_noise_error
KernelVersion: 4.9
Contact: linux-iio@vger.kernel.org
Description:
Read-only attribute that indicates whether excessive noise is
present at the channel Y count inputs in quadrature clock mode;
irrelevant in non-quadrature clock mode.
What: /sys/bus/iio/devices/iio:deviceX/in_countY_preset
KernelVersion: 4.9
Contact: linux-iio@vger.kernel.org
Description:
If the counter device supports preset registers, the preset
count for channel Y is provided by this attribute.
What: /sys/bus/iio/devices/iio:deviceX/in_countY_quadrature_mode
KernelVersion: 4.9
Contact: linux-iio@vger.kernel.org
Description:
Configure channel Y counter for non-quadrature or quadrature
clock mode. Selecting non-quadrature clock mode will disable
synchronous load mode. In quadrature clock mode, the channel Y
scale attribute selects the encoder phase division (scale of 1
selects full-cycle, scale of 0.5 selects half-cycle, scale of
0.25 selects quarter-cycle) processed by the channel Y counter.
Non-quadrature:
The filter and decoder circuit are bypassed. Encoder A
input serves as the count input and B as the UP/DOWN
direction control input, with B = 1 selecting UP Count
mode and B = 0 selecting Down Count mode.
Quadrature:
Encoder A and B inputs are digitally filtered and
decoded for UP/DN clock.
What: /sys/bus/iio/devices/iio:deviceX/in_countY_set_to_preset_on_index
KernelVersion: 4.9
Contact: linux-iio@vger.kernel.org
Description:
Whether to set channel Y counter with channel Y preset value
when channel Y index input is active, or continuously count.
Valid attribute values are boolean.
What: /sys/bus/iio/devices/iio:deviceX/in_indexY_index_polarity
KernelVersion: 4.9
Contact: linux-iio@vger.kernel.org
Description:
Active level of channel Y index input; irrelevant in
non-synchronous load mode.
What: /sys/bus/iio/devices/iio:deviceX/in_indexY_synchronous_mode
KernelVersion: 4.9
Contact: linux-iio@vger.kernel.org
Description:
Configure channel Y counter for non-synchronous or synchronous
load mode. Synchronous load mode cannot be selected in
non-quadrature clock mode.
Non-synchronous:
A logic low level is the active level at this index
input. The index function (as enabled via
set_to_preset_on_index) is performed directly on the
active level of the index input.
Synchronous:
Intended for interfacing with encoder Index output in
quadrature clock mode. The active level is configured
via index_polarity. The index function (as enabled via
set_to_preset_on_index) is performed synchronously with
the quadrature clock on the active level of the index
input.

Просмотреть файл

@ -0,0 +1,18 @@
What: /sys/bus/iio/devices/iio:deviceX/calibrate
Date: July 2015
KernelVersion: 4.7
Contact: linux-iio@vger.kernel.org
Description:
Writing '1' will perform a FOC (Fast Online Calibration). The
corresponding calibration offsets can be read from *_calibbias
entries.
What: /sys/bus/iio/devices/iio:deviceX/location
Date: July 2015
KernelVersion: 4.7
Contact: linux-iio@vger.kernel.org
Description:
This attribute returns a string with the physical location where
the motion sensor is placed. For example, in a laptop a motion
sensor can be located on the base or on the lid. Current valid
values are 'base' and 'lid'.

Просмотреть файл

@ -0,0 +1,8 @@
What: /sys/bus/iio/devices/iio:deviceX/out_voltageY_raw_available
Date: October 2016
KernelVersion: 4.9
Contact: Peter Rosin <peda@axentia.se>
Description:
The range of available values represented as the minimum value,
the step and the maximum value, all enclosed in square brackets.
Example: [0 1 256]

Просмотреть файл

@ -0,0 +1,19 @@
What: /sys/bus/iio/devices/iio:deviceX/proximity_on_chip_ambient_infrared_suppression
Date: January 2011
KernelVersion: 2.6.37
Contact: linux-iio@vger.kernel.org
Description:
From ISL29018 Data Sheet (FN6619.4, Oct 8, 2012) regarding the
infrared suppression:
Scheme 0, makes full n (4, 8, 12, 16) bits (unsigned) proximity
detection. The range of Scheme 0 proximity count is from 0 to
2^n. Logic 1 of this bit, Scheme 1, makes n-1 (3, 7, 11, 15)
bits (2's complementary) proximity_less_ambient detection. The
range of Scheme 1 proximity count is from -2^(n-1) to 2^(n-1).
The sign bit is extended for resolutions less than 16. While
Scheme 0 has wider dynamic range, Scheme 1 proximity detection
is less affected by the ambient IR noise variation.
0 Sensing IR from LED and ambient
1 Sensing IR from LED with ambient IR rejection

Просмотреть файл

@ -1,18 +1,18 @@
What: /sys/bus/iio/devices/device[n]/lux_table
KernelVersion: 2.6.37
Contact: linux-iio@vger.kernel.org
Description:
This property gets/sets the table of coefficients
used in calculating illuminance in lux.
What: /sys/bus/iio/devices/device[n]/illuminance0_calibrate
What: /sys/bus/iio/devices/device[n]/in_illuminance_calibrate
KernelVersion: 2.6.37
Contact: linux-iio@vger.kernel.org
Description:
This property causes an internal calibration of the als gain trim
value which is later used in calculating illuminance in lux.
What: /sys/bus/iio/devices/device[n]/illuminance0_input_target
What: /sys/bus/iio/devices/device[n]/in_illuminance_lux_table
KernelVersion: 2.6.37
Contact: linux-iio@vger.kernel.org
Description:
This property gets/sets the table of coefficients
used in calculating illuminance in lux.
What: /sys/bus/iio/devices/device[n]/in_illuminance_input_target
KernelVersion: 2.6.37
Contact: linux-iio@vger.kernel.org
Description:

Просмотреть файл

@ -0,0 +1,8 @@
What: /sys/bus/iio/devices/iio:deviceX/out_resistance_raw_available
Date: October 2016
KernelVersion: 4.9
Contact: Peter Rosin <peda@axentia.se>
Description:
The range of available values represented as the minimum value,
the step and the maximum value, all enclosed in square brackets.
Example: [0 1 256]

Просмотреть файл

@ -39,11 +39,13 @@ dallas,ds75 Digital Thermometer and Thermostat
dlg,da9053 DA9053: flexible system level PMIC with multicore support
dlg,da9063 DA9063: system PMIC for quad-core application processors
domintech,dmard09 DMARD09: 3-axis Accelerometer
domintech,dmard10 DMARD10: 3-axis Accelerometer
epson,rx8010 I2C-BUS INTERFACE REAL TIME CLOCK MODULE
epson,rx8025 High-Stability. I2C-Bus INTERFACE REAL TIME CLOCK MODULE
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,mma7660 MMA7660FC: 3-Axis Orientation/Motion Detection Sensor
fsl,mma8450 MMA8450Q: Xtrinsic Low-power, 3-axis Xtrinsic Accelerometer
fsl,mpl3115 MPL3115: Absolute Digital Pressure Sensor
fsl,mpr121 MPR121: Proximity Capacitive Touch Sensor Controller
@ -57,6 +59,7 @@ maxim,max1237 Low-Power, 4-/12-Channel, 2-Wire Serial, 12-Bit ADCs
maxim,max6625 9-Bit/12-Bit Temperature Sensors with I²C-Compatible Serial Interface
mc,rv3029c2 Real Time Clock Module with I2C-Bus
mcube,mc3230 mCube 3-axis 8-bit digital accelerometer
memsic,mxc6225 MEMSIC 2-axis 8-bit digital accelerometer
microchip,mcp4531-502 Microchip 7-bit Single I2C Digital Potentiometer (5k)
microchip,mcp4531-103 Microchip 7-bit Single I2C Digital Potentiometer (10k)
microchip,mcp4531-503 Microchip 7-bit Single I2C Digital Potentiometer (50k)
@ -121,6 +124,9 @@ microchip,mcp4662-502 Microchip 8-bit Dual I2C Digital Potentiometer with NV Mem
microchip,mcp4662-103 Microchip 8-bit Dual I2C Digital Potentiometer with NV Memory (10k)
microchip,mcp4662-503 Microchip 8-bit Dual I2C Digital Potentiometer with NV Memory (50k)
microchip,mcp4662-104 Microchip 8-bit Dual I2C Digital Potentiometer with NV Memory (100k)
miramems,da226 MiraMEMS DA226 2-axis 14-bit digital accelerometer
miramems,da280 MiraMEMS DA280 3-axis 14-bit digital accelerometer
miramems,da311 MiraMEMS DA311 3-axis 12-bit digital accelerometer
national,lm63 Temperature sensor with integrated fan control
national,lm75 I2C TEMP SENSOR
national,lm80 Serial Interface ACPI-Compatible Microprocessor System Hardware Monitor
@ -146,6 +152,7 @@ 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
silabs,si7020 Relative Humidity and Temperature Sensors
skyworks,sky81452 Skyworks SKY81452: Six-Channel White LED Driver with Touch Panel Bias Supply
st,24c256 i2c serial eeprom (24cxx)
st,m41t00 Serial real-time clock (RTC)

Просмотреть файл

@ -0,0 +1,54 @@
Bindings for ADC envelope detector using a DAC and a comparator
The DAC is used to find the peak level of an alternating voltage input
signal by a binary search using the output of a comparator wired to
an interrupt pin. Like so:
_
| \
input +------>-------|+ \
| \
.-------. | }---.
| | | / |
| dac|-->--|- / |
| | |_/ |
| | |
| | |
| irq|------<-------'
| |
'-------'
Required properties:
- compatible: Should be "axentia,tse850-envelope-detector"
- io-channels: Channel node of the dac to be used for comparator input.
- io-channel-names: Should be "dac".
- interrupt specification for one client interrupt,
see ../../interrupt-controller/interrupts.txt for details.
- interrupt-names: Should be "comp".
Example:
&i2c {
dpot: mcp4651-104@28 {
compatible = "microchip,mcp4651-104";
reg = <0x28>;
#io-channel-cells = <1>;
};
};
dac: dac {
compatible = "dpot-dac";
vref-supply = <&reg_3v3>;
io-channels = <&dpot 0>;
io-channel-names = "dpot";
#io-channel-cells = <1>;
};
envelope-detector {
compatible = "axentia,tse850-envelope-detector";
io-channels = <&dac 0>;
io-channel-names = "dac";
interrupt-parent = <&gpio>;
interrupts = <3 IRQ_TYPE_EDGE_FALLING>;
interrupt-names = "comp";
};

Просмотреть файл

@ -0,0 +1,83 @@
STMicroelectronics STM32 ADC device driver
STM32 ADC is a successive approximation analog-to-digital converter.
It has several multiplexed input channels. Conversions can be performed
in single, continuous, scan or discontinuous mode. Result of the ADC is
stored in a left-aligned or right-aligned 32-bit data register.
Conversions can be launched in software or using hardware triggers.
The analog watchdog feature allows the application to detect if the input
voltage goes beyond the user-defined, higher or lower thresholds.
Each STM32 ADC block can have up to 3 ADC instances.
Each instance supports two contexts to manage conversions, each one has its
own configurable sequence and trigger:
- regular conversion can be done in sequence, running in background
- injected conversions have higher priority, and so have the ability to
interrupt regular conversion sequence (either triggered in SW or HW).
Regular sequence is resumed, in case it has been interrupted.
Contents of a stm32 adc root node:
-----------------------------------
Required properties:
- compatible: Should be "st,stm32f4-adc-core".
- reg: Offset and length of the ADC block register set.
- interrupts: Must contain the interrupt for ADC block.
- clocks: Clock for the analog circuitry (common to all ADCs).
- clock-names: Must be "adc".
- interrupt-controller: Identifies the controller node as interrupt-parent
- vref-supply: Phandle to the vref input analog reference voltage.
- #interrupt-cells = <1>;
- #address-cells = <1>;
- #size-cells = <0>;
Optional properties:
- A pinctrl state named "default" for each ADC channel may be defined to set
inX ADC pins in mode of operation for analog input on external pin.
Contents of a stm32 adc child node:
-----------------------------------
An ADC block node should contain at least one subnode, representing an
ADC instance available on the machine.
Required properties:
- compatible: Should be "st,stm32f4-adc".
- reg: Offset of ADC instance in ADC block (e.g. may be 0x0, 0x100, 0x200).
- clocks: Input clock private to this ADC instance.
- interrupt-parent: Phandle to the parent interrupt controller.
- interrupts: IRQ Line for the ADC (e.g. may be 0 for adc@0, 1 for adc@100 or
2 for adc@200).
- st,adc-channels: List of single-ended channels muxed for this ADC.
It can have up to 16 channels, numbered from 0 to 15 (resp. for in0..in15).
- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers" in
Documentation/devicetree/bindings/iio/iio-bindings.txt
Example:
adc: adc@40012000 {
compatible = "st,stm32f4-adc-core";
reg = <0x40012000 0x400>;
interrupts = <18>;
clocks = <&rcc 0 168>;
clock-names = "adc";
vref-supply = <&reg_vref>;
interrupt-controller;
pinctrl-names = "default";
pinctrl-0 = <&adc3_in8_pin>;
#interrupt-cells = <1>;
#address-cells = <1>;
#size-cells = <0>;
adc@0 {
compatible = "st,stm32f4-adc";
#io-channel-cells = <1>;
reg = <0x0>;
clocks = <&rcc 0 168>;
interrupt-parent = <&adc>;
interrupts = <0>;
st,adc-channels = <8>;
};
...
other adc child nodes follow...
};

Просмотреть файл

@ -3,6 +3,7 @@
Required properties:
- compatible: Should be "ti,adc141s626" or "ti,adc161s626"
- reg: spi chip select number for the device
- vdda-supply: supply voltage to VDDA pin
Recommended properties:
- spi-max-frequency: Definition as per
@ -11,6 +12,7 @@ Recommended properties:
Example:
adc@0 {
compatible = "ti,adc161s626";
vdda-supply = <&vdda_fixed>;
reg = <0>;
spi-max-frequency = <4300000>;
};

Просмотреть файл

@ -0,0 +1,41 @@
Bindings for DAC emulation using a digital potentiometer
It is assumed that the dpot is used as a voltage divider between the
current dpot wiper setting and the maximum resistance of the dpot. The
divided voltage is provided by a vref regulator.
.------.
.-----------. | |
| vref |--' .---.
| regulator |--. | |
'-----------' | | d |
| | p |
| | o | wiper
| | t |<---------+
| | |
| '---' dac output voltage
| |
'------+------------+
Required properties:
- compatible: Should be "dpot-dac"
- vref-supply: The regulator supplying the voltage divider.
- io-channels: Channel node of the dpot to be used for the voltage division.
- io-channel-names: Should be "dpot".
Example:
&i2c {
dpot: mcp4651-503@28 {
compatible = "microchip,mcp4651-503";
reg = <0x28>;
#io-channel-cells = <1>;
};
};
dac {
compatible = "dpot-dac";
vref-supply = <&reg_3v3>;
io-channels = <&dpot 0>;
io-channel-names = "dpot";
};

Просмотреть файл

@ -0,0 +1,35 @@
Microchip mcp4725 and mcp4726 DAC device driver
Required properties:
- compatible: Must be "microchip,mcp4725" or "microchip,mcp4726"
- reg: Should contain the DAC I2C address
- vdd-supply: Phandle to the Vdd power supply. This supply is used as a
voltage reference on mcp4725. It is used as a voltage reference on
mcp4726 if there is no vref-supply specified.
Optional properties (valid only for mcp4726):
- vref-supply: Optional phandle to the Vref power supply. Vref pin is
used as a voltage reference when this supply is specified.
- microchip,vref-buffered: Boolean to enable buffering of the external
Vref pin. This boolean is not valid without the vref-supply. Quoting
the datasheet: This is offered in cases where the reference voltage
does not have the current capability not to drop its voltage when
connected to the internal resistor ladder circuit.
Examples:
/* simple mcp4725 */
mcp4725@60 {
compatible = "microchip,mcp4725";
reg = <0x60>;
vdd-supply = <&vdac_vdd>;
};
/* mcp4726 with the buffered external reference voltage */
mcp4726@60 {
compatible = "microchip,mcp4726";
reg = <0x60>;
vdd-supply = <&vdac_vdd>;
vref-supply = <&vdac_vref>;
microchip,vref-buffered;
};

Просмотреть файл

@ -0,0 +1,46 @@
Invensense MPU-3050 Gyroscope device tree bindings
Required properties:
- compatible : should be "invensense,mpu3050"
- reg : the I2C address of the sensor
Optional properties:
- interrupt-parent : should be the phandle for the interrupt controller
- interrupts : interrupt mapping for the trigger interrupt from the
internal oscillator. The following IRQ modes are supported:
IRQ_TYPE_EDGE_RISING, IRQ_TYPE_EDGE_FALLING, IRQ_TYPE_LEVEL_HIGH and
IRQ_TYPE_LEVEL_LOW. The driver should detect and configure the hardware
for the desired interrupt type.
- vdd-supply : supply regulator for the main power voltage.
- vlogic-supply : supply regulator for the signal voltage.
- mount-matrix : see iio/mount-matrix.txt
Optional subnodes:
- The MPU-3050 will pass through and forward the I2C signals from the
incoming I2C bus, alternatively drive traffic to a slave device (usually
an accelerometer) on its own initiative. Therefore is supports a subnode
i2c gate node. For details see: i2c/i2c-gate.txt
Example:
mpu3050@68 {
compatible = "invensense,mpu3050";
reg = <0x68>;
interrupt-parent = <&foo>;
interrupts = <12 IRQ_TYPE_EDGE_FALLING>;
vdd-supply = <&bar>;
vlogic-supply = <&baz>;
/* External I2C interface */
i2c-gate {
#address-cells = <1>;
#size-cells = <0>;
fnord@18 {
compatible = "fnord";
reg = <0x18>;
interrupt-parent = <&foo>;
interrupts = <13 IRQ_TYPE_EDGE_FALLING>;
};
};
};

Просмотреть файл

@ -0,0 +1,22 @@
* HTS221 STM humidity + temperature sensor
Required properties:
- compatible: should be "st,hts221"
- reg: i2c address of the sensor / spi cs line
Optional properties:
- interrupt-parent: should be the phandle for the interrupt controller
- interrupts: interrupt mapping for IRQ. It should be configured with
flags IRQ_TYPE_LEVEL_HIGH or IRQ_TYPE_EDGE_RISING.
Refer to interrupt-controller/interrupts.txt for generic interrupt
client node bindings.
Example:
hts221@5f {
compatible = "st,hts221";
reg = <0x5f>;
interrupt-parent = <&gpio0>;
interrupts = <0 IRQ_TYPE_EDGE_RISING>;
};

Просмотреть файл

@ -0,0 +1,28 @@
* ISL 29018/29023/29035 I2C ALS, Proximity, and Infrared sensor
Required properties:
- compatible: Should be one of
"isil,isl29018"
"isil,isl29023"
"isil,isl29035"
- reg: the I2C address of the device
Optional properties:
- 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.
- vcc-supply: phandle to the regulator that provides power to the sensor.
Example:
isl29018@44 {
compatible = "isil,isl29018";
reg = <0x44>;
interrupt-parent = <&gpio>;
interrupts = <TEGRA_GPIO(Z, 2) IRQ_TYPE_LEVEL_HIGH>;
};

Просмотреть файл

@ -0,0 +1,26 @@
* TAOS TSL 2580/2581/2583 ALS sensor
Required properties:
- compatible: Should be one of
"amstaos,tsl2580"
"amstaos,tsl2581"
"amstaos,tsl2583"
- reg: the I2C address of the device
Optional properties:
- 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.
- vcc-supply: phandle to the regulator that provides power to the sensor.
Example:
tsl2581@29 {
compatible = "amstaos,tsl2581";
reg = <0x29>;
};

Просмотреть файл

@ -0,0 +1,30 @@
* Texas Instruments LMP91000 potentiostat
http://www.ti.com/lit/ds/symlink/lmp91000.pdf
Required properties:
- compatible: should be "ti,lmp91000"
- reg: the I2C address of the device
- io-channels: the phandle of the iio provider
- ti,external-tia-resistor: if the property ti,tia-gain-ohm is not defined this
needs to be set to signal that an external resistor value is being used.
Optional properties:
- ti,tia-gain-ohm: ohm value of the internal resistor for the transimpedance
amplifier. Must be 2750, 3500, 7000, 14000, 35000, 120000, or 350000 ohms.
- ti,rload-ohm: ohm value of the internal resistor load applied to the gas
sensor. Must be 10, 33, 50, or 100 (default) ohms.
Example:
lmp91000@48 {
compatible = "ti,lmp91000";
reg = <0x48>;
ti,tia-gain-ohm = <7500>;
ti,rload = <100>;
io-channels = <&adc>;
};

Просмотреть файл

@ -42,6 +42,7 @@ Accelerometers:
- st,lsm303agr-accel
- st,lis2dh12-accel
- st,h3lis331dl-accel
- st,lng2dm-accel
Gyroscopes:
- st,l3g4200d-gyro

Просмотреть файл

@ -39,6 +39,7 @@ auo AU Optronics Corporation
auvidea Auvidea GmbH
avago Avago Technologies
avic Shanghai AVIC Optoelectronics Co., Ltd.
axentia Axentia Technologies AB
axis Axis Communications AB
boe BOE Technology Group Co., Ltd.
bosch Bosch Sensortec GmbH
@ -160,16 +161,19 @@ lltc Linear Technology Corporation
lsi LSI Corp. (LSI Logic)
marvell Marvell Technology Group Ltd.
maxim Maxim Integrated Products
mcube mCube
meas Measurement Specialties
mediatek MediaTek Inc.
melexis Melexis N.V.
melfas MELFAS Inc.
memsic MEMSIC Inc.
merrii Merrii Technology Co., Ltd.
micrel Micrel Inc.
microchip Microchip Technology Inc.
microcrystal Micro Crystal AG
micron Micron Technology Inc.
minix MINIX Technology Ltd.
miramems MiraMEMS Sensing Technology Co., Ltd.
mitsubishi Mitsubishi Electric Corporation
mosaixtech Mosaix Technologies, Inc.
moxa Moxa

Просмотреть файл

@ -260,6 +260,12 @@ L: linux-gpio@vger.kernel.org
S: Maintained
F: drivers/gpio/gpio-104-idio-16.c
ACCES 104-QUAD-8 IIO DRIVER
M: William Breathitt Gray <vilhelm.gray@gmail.com>
L: linux-iio@vger.kernel.org
S: Maintained
F: drivers/iio/counter/104-quad-8.c
ACENIC DRIVER
M: Jes Sorensen <jes@trained-monkey.org>
L: linux-acenic@sunsite.dk
@ -803,7 +809,7 @@ S: Supported
F: drivers/iio/*/ad*
X: drivers/iio/*/adjd*
F: drivers/staging/iio/*/ad*
F: staging/iio/trigger/iio-trig-bfin-timer.c
F: drivers/staging/iio/trigger/iio-trig-bfin-timer.c
ANALOG DEVICES INC DMA DRIVERS
M: Lars-Peter Clausen <lars@metafoo.de>
@ -2612,6 +2618,7 @@ L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
T: git git://git.kernel.org/pub/scm/linux/kernel/git/rpi/linux-rpi.git
S: Maintained
N: bcm2835
F: drivers/staging/vc04_services
BROADCOM BCM47XX MIPS ARCHITECTURE
M: Hauke Mehrtens <hauke@hauke-m.de>
@ -5192,13 +5199,6 @@ F: sound/soc/fsl/fsl*
F: sound/soc/fsl/imx*
F: sound/soc/fsl/mpc8610_hpcd.c
FREESCALE QORIQ MANAGEMENT COMPLEX DRIVER
M: "J. German Rivera" <German.Rivera@freescale.com>
M: Stuart Yoder <stuart.yoder@nxp.com>
L: linux-kernel@vger.kernel.org
S: Maintained
F: drivers/staging/fsl-mc/
FREEVXFS FILESYSTEM
M: Christoph Hellwig <hch@infradead.org>
W: ftp://ftp.openlinux.org/pub/people/hch/vxfs
@ -6215,6 +6215,22 @@ L: linux-media@vger.kernel.org
S: Maintained
F: drivers/media/rc/iguanair.c
IIO DIGITAL POTENTIOMETER DAC
M: Peter Rosin <peda@axentia.se>
L: linux-iio@vger.kernel.org
S: Maintained
F: Documentation/ABI/testing/sysfs-bus-iio-dac-dpot-dac
F: Documentation/devicetree/bindings/iio/dac/dpot-dac.txt
F: drivers/iio/dac/dpot-dac.c
IIO ENVELOPE DETECTOR
M: Peter Rosin <peda@axentia.se>
L: linux-iio@vger.kernel.org
S: Maintained
F: Documentation/ABI/testing/sysfs-bus-iio-adc-envelope-detector
F: Documentation/devicetree/bindings/iio/adc/envelope-detector.txt
F: drivers/iio/adc/envelope-detector.c
IIO SUBSYSTEM AND DRIVERS
M: Jonathan Cameron <jic23@kernel.org>
R: Hartmut Knaack <knaack.h@gmx.de>
@ -6596,6 +6612,13 @@ S: Maintained
F: arch/x86/include/asm/pmc_core.h
F: drivers/platform/x86/intel_pmc_core*
INVENSENSE MPU-3050 GYROSCOPE DRIVER
M: Linus Walleij <linus.walleij@linaro.org>
L: linux-iio@vger.kernel.org
S: Maintained
F: drivers/iio/gyro/mpu3050*
F: Documentation/devicetree/bindings/iio/gyroscope/inv,mpu3050.txt
IOC3 ETHERNET DRIVER
M: Ralf Baechle <ralf@linux-mips.org>
L: linux-mips@linux-mips.org
@ -7803,6 +7826,7 @@ MCP4531 MICROCHIP DIGITAL POTENTIOMETER DRIVER
M: Peter Rosin <peda@axentia.se>
L: linux-iio@vger.kernel.org
S: Maintained
F: Documentation/ABI/testing/sysfs-bus-iio-potentiometer-mcp4531
F: drivers/iio/potentiometer/mcp4531.c
MEASUREMENT COMPUTING CIO-DAC IIO DRIVER
@ -10056,6 +10080,12 @@ F: fs/qnx4/
F: include/uapi/linux/qnx4_fs.h
F: include/uapi/linux/qnxtypes.h
QORIQ DPAA2 FSL-MC BUS DRIVER
M: Stuart Yoder <stuart.yoder@nxp.com>
L: linux-kernel@vger.kernel.org
S: Maintained
F: drivers/staging/fsl-mc/
QT1010 MEDIA DRIVER
M: Antti Palosaari <crope@iki.fi>
L: linux-media@vger.kernel.org
@ -11647,12 +11677,6 @@ L: linux-fbdev@vger.kernel.org
S: Maintained
F: drivers/staging/sm750fb/
STAGING - SLICOSS
M: Lior Dotan <liodot@gmail.com>
M: Christopher Harrer <charrer@alacritech.com>
S: Odd Fixes
F: drivers/staging/slicoss/
STAGING - SPEAKUP CONSOLE SPEECH DRIVER
M: William Hubbs <w.d.hubbs@gmail.com>
M: Chris Brannon <chris@the-brannons.com>

Просмотреть файл

@ -73,6 +73,7 @@ source "drivers/iio/adc/Kconfig"
source "drivers/iio/amplifiers/Kconfig"
source "drivers/iio/chemical/Kconfig"
source "drivers/iio/common/Kconfig"
source "drivers/iio/counter/Kconfig"
source "drivers/iio/dac/Kconfig"
source "drivers/iio/dummy/Kconfig"
source "drivers/iio/frequency/Kconfig"
@ -87,6 +88,7 @@ if IIO_TRIGGER
source "drivers/iio/trigger/Kconfig"
endif #IIO_TRIGGER
source "drivers/iio/potentiometer/Kconfig"
source "drivers/iio/potentiostat/Kconfig"
source "drivers/iio/pressure/Kconfig"
source "drivers/iio/proximity/Kconfig"
source "drivers/iio/temperature/Kconfig"

Просмотреть файл

@ -18,6 +18,7 @@ obj-y += amplifiers/
obj-y += buffer/
obj-y += chemical/
obj-y += common/
obj-y += counter/
obj-y += dac/
obj-y += dummy/
obj-y += gyro/
@ -29,6 +30,7 @@ obj-y += light/
obj-y += magnetometer/
obj-y += orientation/
obj-y += potentiometer/
obj-y += potentiostat/
obj-y += pressure/
obj-y += proximity/
obj-y += temperature/

Просмотреть файл

@ -52,6 +52,26 @@ config BMC150_ACCEL_SPI
tristate
select REGMAP_SPI
config DA280
tristate "MiraMEMS DA280 3-axis 14-bit digital accelerometer driver"
depends on I2C
help
Say yes here to build support for the MiraMEMS DA280 3-axis 14-bit
digital accelerometer.
To compile this driver as a module, choose M here: the
module will be called da280.
config DA311
tristate "MiraMEMS DA311 3-axis 12-bit digital accelerometer driver"
depends on I2C
help
Say yes here to build support for the MiraMEMS DA311 3-axis 12-bit
digital accelerometer.
To compile this driver as a module, choose M here: the
module will be called da311.
config DMARD06
tristate "Domintech DMARD06 Digital Accelerometer Driver"
depends on OF || COMPILE_TEST
@ -73,6 +93,16 @@ config DMARD09
Choosing M will build the driver as a module. If so, the module
will be called dmard09.
config DMARD10
tristate "Domintech DMARD10 3-axis Accelerometer Driver"
depends on I2C
help
Say yes here to get support for the Domintech DMARD10 3-axis
accelerometer.
Choosing M will build the driver as a module. If so, the module
will be called dmard10.
config HID_SENSOR_ACCEL_3D
depends on HID_SENSOR_HUB
select IIO_BUFFER
@ -97,7 +127,8 @@ config IIO_ST_ACCEL_3AXIS
help
Say yes here to build support for STMicroelectronics accelerometers:
LSM303DLH, LSM303DLHC, LIS3DH, LSM330D, LSM330DL, LSM330DLC,
LIS331DLH, LSM303DL, LSM303DLM, LSM330, LIS2DH12, H3LIS331DL.
LIS331DLH, LSM303DL, LSM303DLM, LSM330, LIS2DH12, H3LIS331DL,
LNG2DM
This driver can also be built as a module. If so, these modules
will be created:
@ -273,6 +304,18 @@ config MXC6255
To compile this driver as a module, choose M here: the module will be
called mxc6255.
config SCA3000
select IIO_BUFFER
select IIO_KFIFO_BUF
depends on SPI
tristate "VTI SCA3000 series accelerometers"
help
Say Y here to build support for the VTI SCA3000 series of SPI
accelerometers. These devices use a hardware ring buffer.
To compile this driver as a module, say M here: the module will be
called sca3000.
config STK8312
tristate "Sensortek STK8312 3-Axis Accelerometer Driver"
depends on I2C

Просмотреть файл

@ -8,8 +8,11 @@ obj-$(CONFIG_BMA220) += bma220_spi.o
obj-$(CONFIG_BMC150_ACCEL) += bmc150-accel-core.o
obj-$(CONFIG_BMC150_ACCEL_I2C) += bmc150-accel-i2c.o
obj-$(CONFIG_BMC150_ACCEL_SPI) += bmc150-accel-spi.o
obj-$(CONFIG_DA280) += da280.o
obj-$(CONFIG_DA311) += da311.o
obj-$(CONFIG_DMARD06) += dmard06.o
obj-$(CONFIG_DMARD09) += dmard09.o
obj-$(CONFIG_DMARD10) += dmard10.o
obj-$(CONFIG_HID_SENSOR_ACCEL_3D) += hid-sensor-accel-3d.o
obj-$(CONFIG_KXCJK1013) += kxcjk-1013.o
obj-$(CONFIG_KXSD9) += kxsd9.o
@ -32,6 +35,8 @@ obj-$(CONFIG_MMA9553) += mma9553.o
obj-$(CONFIG_MXC4005) += mxc4005.o
obj-$(CONFIG_MXC6255) += mxc6255.o
obj-$(CONFIG_SCA3000) += sca3000.o
obj-$(CONFIG_STK8312) += stk8312.o
obj-$(CONFIG_STK8BA50) += stk8ba50.o

183
drivers/iio/accel/da280.c Normal file
Просмотреть файл

@ -0,0 +1,183 @@
/**
* IIO driver for the MiraMEMS DA280 3-axis accelerometer and
* IIO driver for the MiraMEMS DA226 2-axis accelerometer
*
* Copyright (c) 2016 Hans de Goede <hdegoede@redhat.com>
*
* 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.
*/
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/byteorder/generic.h>
#define DA280_REG_CHIP_ID 0x01
#define DA280_REG_ACC_X_LSB 0x02
#define DA280_REG_ACC_Y_LSB 0x04
#define DA280_REG_ACC_Z_LSB 0x06
#define DA280_REG_MODE_BW 0x11
#define DA280_CHIP_ID 0x13
#define DA280_MODE_ENABLE 0x1e
#define DA280_MODE_DISABLE 0x9e
enum { da226, da280 };
/*
* a value of + or -4096 corresponds to + or - 1G
* scale = 9.81 / 4096 = 0.002395019
*/
static const int da280_nscale = 2395019;
#define DA280_CHANNEL(reg, axis) { \
.type = IIO_ACCEL, \
.address = reg, \
.modified = 1, \
.channel2 = IIO_MOD_##axis, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
}
static const struct iio_chan_spec da280_channels[] = {
DA280_CHANNEL(DA280_REG_ACC_X_LSB, X),
DA280_CHANNEL(DA280_REG_ACC_Y_LSB, Y),
DA280_CHANNEL(DA280_REG_ACC_Z_LSB, Z),
};
struct da280_data {
struct i2c_client *client;
};
static int da280_enable(struct i2c_client *client, bool enable)
{
u8 data = enable ? DA280_MODE_ENABLE : DA280_MODE_DISABLE;
return i2c_smbus_write_byte_data(client, DA280_REG_MODE_BW, data);
}
static int da280_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct da280_data *data = iio_priv(indio_dev);
int ret;
switch (mask) {
case IIO_CHAN_INFO_RAW:
ret = i2c_smbus_read_word_data(data->client, chan->address);
if (ret < 0)
return ret;
/*
* Values are 14 bits, stored as 16 bits with the 2
* least significant bits always 0.
*/
*val = (short)ret >> 2;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
*val = 0;
*val2 = da280_nscale;
return IIO_VAL_INT_PLUS_NANO;
default:
return -EINVAL;
}
}
static const struct iio_info da280_info = {
.driver_module = THIS_MODULE,
.read_raw = da280_read_raw,
};
static int da280_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int ret;
struct iio_dev *indio_dev;
struct da280_data *data;
ret = i2c_smbus_read_byte_data(client, DA280_REG_CHIP_ID);
if (ret != DA280_CHIP_ID)
return (ret < 0) ? ret : -ENODEV;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
data = iio_priv(indio_dev);
data->client = client;
i2c_set_clientdata(client, indio_dev);
indio_dev->dev.parent = &client->dev;
indio_dev->info = &da280_info;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = da280_channels;
if (id->driver_data == da226) {
indio_dev->name = "da226";
indio_dev->num_channels = 2;
} else {
indio_dev->name = "da280";
indio_dev->num_channels = 3;
}
ret = da280_enable(client, true);
if (ret < 0)
return ret;
ret = iio_device_register(indio_dev);
if (ret < 0) {
dev_err(&client->dev, "device_register failed\n");
da280_enable(client, false);
}
return ret;
}
static int da280_remove(struct i2c_client *client)
{
struct iio_dev *indio_dev = i2c_get_clientdata(client);
iio_device_unregister(indio_dev);
return da280_enable(client, false);
}
#ifdef CONFIG_PM_SLEEP
static int da280_suspend(struct device *dev)
{
return da280_enable(to_i2c_client(dev), false);
}
static int da280_resume(struct device *dev)
{
return da280_enable(to_i2c_client(dev), true);
}
#endif
static SIMPLE_DEV_PM_OPS(da280_pm_ops, da280_suspend, da280_resume);
static const struct i2c_device_id da280_i2c_id[] = {
{ "da226", da226 },
{ "da280", da280 },
{}
};
MODULE_DEVICE_TABLE(i2c, da280_i2c_id);
static struct i2c_driver da280_driver = {
.driver = {
.name = "da280",
.pm = &da280_pm_ops,
},
.probe = da280_probe,
.remove = da280_remove,
.id_table = da280_i2c_id,
};
module_i2c_driver(da280_driver);
MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
MODULE_DESCRIPTION("MiraMEMS DA280 3-Axis Accelerometer driver");
MODULE_LICENSE("GPL v2");

305
drivers/iio/accel/da311.c Normal file
Просмотреть файл

@ -0,0 +1,305 @@
/**
* IIO driver for the MiraMEMS DA311 3-axis accelerometer
*
* Copyright (c) 2016 Hans de Goede <hdegoede@redhat.com>
* Copyright (c) 2011-2013 MiraMEMS Sensing Technology Co., Ltd.
*
* 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.
*/
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/byteorder/generic.h>
#define DA311_CHIP_ID 0x13
/*
* Note register addressed go from 0 - 0x3f and then wrap.
* For some reason there are 2 banks with 0 - 0x3f addresses,
* rather then a single 0-0x7f bank.
*/
/* Bank 0 regs */
#define DA311_REG_BANK 0x0000
#define DA311_REG_LDO_REG 0x0006
#define DA311_REG_CHIP_ID 0x000f
#define DA311_REG_TEMP_CFG_REG 0x001f
#define DA311_REG_CTRL_REG1 0x0020
#define DA311_REG_CTRL_REG3 0x0022
#define DA311_REG_CTRL_REG4 0x0023
#define DA311_REG_CTRL_REG5 0x0024
#define DA311_REG_CTRL_REG6 0x0025
#define DA311_REG_STATUS_REG 0x0027
#define DA311_REG_OUT_X_L 0x0028
#define DA311_REG_OUT_X_H 0x0029
#define DA311_REG_OUT_Y_L 0x002a
#define DA311_REG_OUT_Y_H 0x002b
#define DA311_REG_OUT_Z_L 0x002c
#define DA311_REG_OUT_Z_H 0x002d
#define DA311_REG_INT1_CFG 0x0030
#define DA311_REG_INT1_SRC 0x0031
#define DA311_REG_INT1_THS 0x0032
#define DA311_REG_INT1_DURATION 0x0033
#define DA311_REG_INT2_CFG 0x0034
#define DA311_REG_INT2_SRC 0x0035
#define DA311_REG_INT2_THS 0x0036
#define DA311_REG_INT2_DURATION 0x0037
#define DA311_REG_CLICK_CFG 0x0038
#define DA311_REG_CLICK_SRC 0x0039
#define DA311_REG_CLICK_THS 0x003a
#define DA311_REG_TIME_LIMIT 0x003b
#define DA311_REG_TIME_LATENCY 0x003c
#define DA311_REG_TIME_WINDOW 0x003d
/* Bank 1 regs */
#define DA311_REG_SOFT_RESET 0x0105
#define DA311_REG_OTP_XOFF_L 0x0110
#define DA311_REG_OTP_XOFF_H 0x0111
#define DA311_REG_OTP_YOFF_L 0x0112
#define DA311_REG_OTP_YOFF_H 0x0113
#define DA311_REG_OTP_ZOFF_L 0x0114
#define DA311_REG_OTP_ZOFF_H 0x0115
#define DA311_REG_OTP_XSO 0x0116
#define DA311_REG_OTP_YSO 0x0117
#define DA311_REG_OTP_ZSO 0x0118
#define DA311_REG_OTP_TRIM_OSC 0x011b
#define DA311_REG_LPF_ABSOLUTE 0x011c
#define DA311_REG_TEMP_OFF1 0x0127
#define DA311_REG_TEMP_OFF2 0x0128
#define DA311_REG_TEMP_OFF3 0x0129
#define DA311_REG_OTP_TRIM_THERM_H 0x011a
/*
* a value of + or -1024 corresponds to + or - 1G
* scale = 9.81 / 1024 = 0.009580078
*/
static const int da311_nscale = 9580078;
#define DA311_CHANNEL(reg, axis) { \
.type = IIO_ACCEL, \
.address = reg, \
.modified = 1, \
.channel2 = IIO_MOD_##axis, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
}
static const struct iio_chan_spec da311_channels[] = {
/* | 0x80 comes from the android driver */
DA311_CHANNEL(DA311_REG_OUT_X_L | 0x80, X),
DA311_CHANNEL(DA311_REG_OUT_Y_L | 0x80, Y),
DA311_CHANNEL(DA311_REG_OUT_Z_L | 0x80, Z),
};
struct da311_data {
struct i2c_client *client;
};
static int da311_register_mask_write(struct i2c_client *client, u16 addr,
u8 mask, u8 data)
{
int ret;
u8 tmp_data = 0;
if (addr & 0xff00) {
/* Select bank 1 */
ret = i2c_smbus_write_byte_data(client, DA311_REG_BANK, 0x01);
if (ret < 0)
return ret;
}
if (mask != 0xff) {
ret = i2c_smbus_read_byte_data(client, addr);
if (ret < 0)
return ret;
tmp_data = ret;
}
tmp_data &= ~mask;
tmp_data |= data & mask;
ret = i2c_smbus_write_byte_data(client, addr & 0xff, tmp_data);
if (ret < 0)
return ret;
if (addr & 0xff00) {
/* Back to bank 0 */
ret = i2c_smbus_write_byte_data(client, DA311_REG_BANK, 0x00);
if (ret < 0)
return ret;
}
return 0;
}
/* Init sequence taken from the android driver */
static int da311_reset(struct i2c_client *client)
{
const struct {
u16 addr;
u8 mask;
u8 data;
} init_data[] = {
{ DA311_REG_TEMP_CFG_REG, 0xff, 0x08 },
{ DA311_REG_CTRL_REG5, 0xff, 0x80 },
{ DA311_REG_CTRL_REG4, 0x30, 0x00 },
{ DA311_REG_CTRL_REG1, 0xff, 0x6f },
{ DA311_REG_TEMP_CFG_REG, 0xff, 0x88 },
{ DA311_REG_LDO_REG, 0xff, 0x02 },
{ DA311_REG_OTP_TRIM_OSC, 0xff, 0x27 },
{ DA311_REG_LPF_ABSOLUTE, 0xff, 0x30 },
{ DA311_REG_TEMP_OFF1, 0xff, 0x3f },
{ DA311_REG_TEMP_OFF2, 0xff, 0xff },
{ DA311_REG_TEMP_OFF3, 0xff, 0x0f },
};
int i, ret;
/* Reset */
ret = da311_register_mask_write(client, DA311_REG_SOFT_RESET,
0xff, 0xaa);
if (ret < 0)
return ret;
for (i = 0; i < ARRAY_SIZE(init_data); i++) {
ret = da311_register_mask_write(client,
init_data[i].addr,
init_data[i].mask,
init_data[i].data);
if (ret < 0)
return ret;
}
return 0;
}
static int da311_enable(struct i2c_client *client, bool enable)
{
u8 data = enable ? 0x00 : 0x20;
return da311_register_mask_write(client, DA311_REG_TEMP_CFG_REG,
0x20, data);
}
static int da311_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct da311_data *data = iio_priv(indio_dev);
int ret;
switch (mask) {
case IIO_CHAN_INFO_RAW:
ret = i2c_smbus_read_word_data(data->client, chan->address);
if (ret < 0)
return ret;
/*
* Values are 12 bits, stored as 16 bits with the 4
* least significant bits always 0.
*/
*val = (short)ret >> 4;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
*val = 0;
*val2 = da311_nscale;
return IIO_VAL_INT_PLUS_NANO;
default:
return -EINVAL;
}
}
static const struct iio_info da311_info = {
.driver_module = THIS_MODULE,
.read_raw = da311_read_raw,
};
static int da311_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int ret;
struct iio_dev *indio_dev;
struct da311_data *data;
ret = i2c_smbus_read_byte_data(client, DA311_REG_CHIP_ID);
if (ret != DA311_CHIP_ID)
return (ret < 0) ? ret : -ENODEV;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
data = iio_priv(indio_dev);
data->client = client;
i2c_set_clientdata(client, indio_dev);
indio_dev->dev.parent = &client->dev;
indio_dev->info = &da311_info;
indio_dev->name = "da311";
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = da311_channels;
indio_dev->num_channels = ARRAY_SIZE(da311_channels);
ret = da311_reset(client);
if (ret < 0)
return ret;
ret = da311_enable(client, true);
if (ret < 0)
return ret;
ret = iio_device_register(indio_dev);
if (ret < 0) {
dev_err(&client->dev, "device_register failed\n");
da311_enable(client, false);
}
return ret;
}
static int da311_remove(struct i2c_client *client)
{
struct iio_dev *indio_dev = i2c_get_clientdata(client);
iio_device_unregister(indio_dev);
return da311_enable(client, false);
}
#ifdef CONFIG_PM_SLEEP
static int da311_suspend(struct device *dev)
{
return da311_enable(to_i2c_client(dev), false);
}
static int da311_resume(struct device *dev)
{
return da311_enable(to_i2c_client(dev), true);
}
#endif
static SIMPLE_DEV_PM_OPS(da311_pm_ops, da311_suspend, da311_resume);
static const struct i2c_device_id da311_i2c_id[] = {
{"da311", 0},
{}
};
MODULE_DEVICE_TABLE(i2c, da311_i2c_id);
static struct i2c_driver da311_driver = {
.driver = {
.name = "da311",
.pm = &da311_pm_ops,
},
.probe = da311_probe,
.remove = da311_remove,
.id_table = da311_i2c_id,
};
module_i2c_driver(da311_driver);
MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
MODULE_DESCRIPTION("MiraMEMS DA311 3-Axis Accelerometer driver");
MODULE_LICENSE("GPL v2");

266
drivers/iio/accel/dmard10.c Normal file
Просмотреть файл

@ -0,0 +1,266 @@
/**
* IIO driver for the 3-axis accelerometer Domintech ARD10.
*
* Copyright (c) 2016 Hans de Goede <hdegoede@redhat.com>
* Copyright (c) 2012 Domintech Technology Co., Ltd
*
* 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.
*/
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/byteorder/generic.h>
#define DMARD10_REG_ACTR 0x00
#define DMARD10_REG_AFEM 0x0c
#define DMARD10_REG_STADR 0x12
#define DMARD10_REG_STAINT 0x1c
#define DMARD10_REG_MISC2 0x1f
#define DMARD10_REG_PD 0x21
#define DMARD10_MODE_OFF 0x00
#define DMARD10_MODE_STANDBY 0x02
#define DMARD10_MODE_ACTIVE 0x06
#define DMARD10_MODE_READ_OTP 0x12
#define DMARD10_MODE_RESET_DATA_PATH 0x82
/* AFEN set 1, ATM[2:0]=b'000 (normal), EN_Z/Y/X/T=1 */
#define DMARD10_VALUE_AFEM_AFEN_NORMAL 0x8f
/* ODR[3:0]=b'0111 (100Hz), CCK[3:0]=b'0100 (204.8kHZ) */
#define DMARD10_VALUE_CKSEL_ODR_100_204 0x74
/* INTC[6:5]=b'00 */
#define DMARD10_VALUE_INTC 0x00
/* TAP1/TAP2 Average 2 */
#define DMARD10_VALUE_TAPNS_AVE_2 0x11
#define DMARD10_VALUE_STADR 0x55
#define DMARD10_VALUE_STAINT 0xaa
#define DMARD10_VALUE_MISC2_OSCA_EN 0x08
#define DMARD10_VALUE_PD_RST 0x52
/* Offsets into the buffer read in dmard10_read_raw() */
#define DMARD10_X_OFFSET 1
#define DMARD10_Y_OFFSET 2
#define DMARD10_Z_OFFSET 3
/*
* a value of + or -128 corresponds to + or - 1G
* scale = 9.81 / 128 = 0.076640625
*/
static const int dmard10_nscale = 76640625;
#define DMARD10_CHANNEL(reg, axis) { \
.type = IIO_ACCEL, \
.address = reg, \
.modified = 1, \
.channel2 = IIO_MOD_##axis, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
}
static const struct iio_chan_spec dmard10_channels[] = {
DMARD10_CHANNEL(DMARD10_X_OFFSET, X),
DMARD10_CHANNEL(DMARD10_Y_OFFSET, Y),
DMARD10_CHANNEL(DMARD10_Z_OFFSET, Z),
};
struct dmard10_data {
struct i2c_client *client;
};
/* Init sequence taken from the android driver */
static int dmard10_reset(struct i2c_client *client)
{
unsigned char buffer[7];
int ret;
/* 1. Powerdown reset */
ret = i2c_smbus_write_byte_data(client, DMARD10_REG_PD,
DMARD10_VALUE_PD_RST);
if (ret < 0)
return ret;
/*
* 2. ACTR => Standby mode => Download OTP to parameter reg =>
* Standby mode => Reset data path => Standby mode
*/
buffer[0] = DMARD10_REG_ACTR;
buffer[1] = DMARD10_MODE_STANDBY;
buffer[2] = DMARD10_MODE_READ_OTP;
buffer[3] = DMARD10_MODE_STANDBY;
buffer[4] = DMARD10_MODE_RESET_DATA_PATH;
buffer[5] = DMARD10_MODE_STANDBY;
ret = i2c_master_send(client, buffer, 6);
if (ret < 0)
return ret;
/* 3. OSCA_EN = 1, TSTO = b'000 (INT1 = normal, TEST0 = normal) */
ret = i2c_smbus_write_byte_data(client, DMARD10_REG_MISC2,
DMARD10_VALUE_MISC2_OSCA_EN);
if (ret < 0)
return ret;
/* 4. AFEN = 1 (AFE will powerdown after ADC) */
buffer[0] = DMARD10_REG_AFEM;
buffer[1] = DMARD10_VALUE_AFEM_AFEN_NORMAL;
buffer[2] = DMARD10_VALUE_CKSEL_ODR_100_204;
buffer[3] = DMARD10_VALUE_INTC;
buffer[4] = DMARD10_VALUE_TAPNS_AVE_2;
buffer[5] = 0x00; /* DLYC, no delay timing */
buffer[6] = 0x07; /* INTD=1 push-pull, INTA=1 active high, AUTOT=1 */
ret = i2c_master_send(client, buffer, 7);
if (ret < 0)
return ret;
/* 5. Activation mode */
ret = i2c_smbus_write_byte_data(client, DMARD10_REG_ACTR,
DMARD10_MODE_ACTIVE);
if (ret < 0)
return ret;
return 0;
}
/* Shutdown sequence taken from the android driver */
static int dmard10_shutdown(struct i2c_client *client)
{
unsigned char buffer[3];
buffer[0] = DMARD10_REG_ACTR;
buffer[1] = DMARD10_MODE_STANDBY;
buffer[2] = DMARD10_MODE_OFF;
return i2c_master_send(client, buffer, 3);
}
static int dmard10_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct dmard10_data *data = iio_priv(indio_dev);
__le16 buf[4];
int ret;
switch (mask) {
case IIO_CHAN_INFO_RAW:
/*
* Read 8 bytes starting at the REG_STADR register, trying to
* read the individual X, Y, Z registers will always read 0.
*/
ret = i2c_smbus_read_i2c_block_data(data->client,
DMARD10_REG_STADR,
sizeof(buf), (u8 *)buf);
if (ret < 0)
return ret;
ret = le16_to_cpu(buf[chan->address]);
*val = sign_extend32(ret, 12);
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
*val = 0;
*val2 = dmard10_nscale;
return IIO_VAL_INT_PLUS_NANO;
default:
return -EINVAL;
}
}
static const struct iio_info dmard10_info = {
.driver_module = THIS_MODULE,
.read_raw = dmard10_read_raw,
};
static int dmard10_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int ret;
struct iio_dev *indio_dev;
struct dmard10_data *data;
/* These 2 registers have special POR reset values used for id */
ret = i2c_smbus_read_byte_data(client, DMARD10_REG_STADR);
if (ret != DMARD10_VALUE_STADR)
return (ret < 0) ? ret : -ENODEV;
ret = i2c_smbus_read_byte_data(client, DMARD10_REG_STAINT);
if (ret != DMARD10_VALUE_STAINT)
return (ret < 0) ? ret : -ENODEV;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (!indio_dev) {
dev_err(&client->dev, "iio allocation failed!\n");
return -ENOMEM;
}
data = iio_priv(indio_dev);
data->client = client;
i2c_set_clientdata(client, indio_dev);
indio_dev->dev.parent = &client->dev;
indio_dev->info = &dmard10_info;
indio_dev->name = "dmard10";
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = dmard10_channels;
indio_dev->num_channels = ARRAY_SIZE(dmard10_channels);
ret = dmard10_reset(client);
if (ret < 0)
return ret;
ret = iio_device_register(indio_dev);
if (ret < 0) {
dev_err(&client->dev, "device_register failed\n");
dmard10_shutdown(client);
}
return ret;
}
static int dmard10_remove(struct i2c_client *client)
{
struct iio_dev *indio_dev = i2c_get_clientdata(client);
iio_device_unregister(indio_dev);
return dmard10_shutdown(client);
}
#ifdef CONFIG_PM_SLEEP
static int dmard10_suspend(struct device *dev)
{
return dmard10_shutdown(to_i2c_client(dev));
}
static int dmard10_resume(struct device *dev)
{
return dmard10_reset(to_i2c_client(dev));
}
#endif
static SIMPLE_DEV_PM_OPS(dmard10_pm_ops, dmard10_suspend, dmard10_resume);
static const struct i2c_device_id dmard10_i2c_id[] = {
{"dmard10", 0},
{}
};
MODULE_DEVICE_TABLE(i2c, dmard10_i2c_id);
static struct i2c_driver dmard10_driver = {
.driver = {
.name = "dmard10",
.pm = &dmard10_pm_ops,
},
.probe = dmard10_probe,
.remove = dmard10_remove,
.id_table = dmard10_i2c_id,
};
module_i2c_driver(dmard10_driver);
MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
MODULE_DESCRIPTION("Domintech ARD10 3-Axis Accelerometer driver");
MODULE_LICENSE("GPL v2");

Просмотреть файл

@ -39,7 +39,7 @@
#define MMA7660_SCALE_AVAIL "0.467142857"
const int mma7660_nscale = 467142857;
static const int mma7660_nscale = 467142857;
#define MMA7660_CHANNEL(reg, axis) { \
.type = IIO_ACCEL, \

Просмотреть файл

@ -459,12 +459,14 @@ static int mma8452_read_raw(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_RAW:
if (iio_buffer_enabled(indio_dev))
return -EBUSY;
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
mutex_lock(&data->lock);
ret = mma8452_read(data, buffer);
mutex_unlock(&data->lock);
iio_device_release_direct_mode(indio_dev);
if (ret < 0)
return ret;
@ -664,37 +666,46 @@ static int mma8452_write_raw(struct iio_dev *indio_dev,
struct mma8452_data *data = iio_priv(indio_dev);
int i, ret;
if (iio_buffer_enabled(indio_dev))
return -EBUSY;
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
i = mma8452_get_samp_freq_index(data, val, val2);
if (i < 0)
return i;
if (i < 0) {
ret = i;
break;
}
data->ctrl_reg1 &= ~MMA8452_CTRL_DR_MASK;
data->ctrl_reg1 |= i << MMA8452_CTRL_DR_SHIFT;
return mma8452_change_config(data, MMA8452_CTRL_REG1,
data->ctrl_reg1);
ret = mma8452_change_config(data, MMA8452_CTRL_REG1,
data->ctrl_reg1);
break;
case IIO_CHAN_INFO_SCALE:
i = mma8452_get_scale_index(data, val, val2);
if (i < 0)
return i;
if (i < 0) {
ret = i;
break;
}
data->data_cfg &= ~MMA8452_DATA_CFG_FS_MASK;
data->data_cfg |= i;
return mma8452_change_config(data, MMA8452_DATA_CFG,
data->data_cfg);
ret = mma8452_change_config(data, MMA8452_DATA_CFG,
data->data_cfg);
break;
case IIO_CHAN_INFO_CALIBBIAS:
if (val < -128 || val > 127)
return -EINVAL;
if (val < -128 || val > 127) {
ret = -EINVAL;
break;
}
return mma8452_change_config(data,
MMA8452_OFF_X + chan->scan_index,
val);
ret = mma8452_change_config(data,
MMA8452_OFF_X + chan->scan_index,
val);
break;
case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY:
if (val == 0 && val2 == 0) {
@ -703,23 +714,30 @@ static int mma8452_write_raw(struct iio_dev *indio_dev,
data->data_cfg |= MMA8452_DATA_CFG_HPF_MASK;
ret = mma8452_set_hp_filter_frequency(data, val, val2);
if (ret < 0)
return ret;
break;
}
return mma8452_change_config(data, MMA8452_DATA_CFG,
ret = mma8452_change_config(data, MMA8452_DATA_CFG,
data->data_cfg);
break;
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
ret = mma8452_get_odr_index(data);
for (i = 0; i < ARRAY_SIZE(mma8452_os_ratio); i++) {
if (mma8452_os_ratio[i][ret] == val)
return mma8452_set_power_mode(data, i);
if (mma8452_os_ratio[i][ret] == val) {
ret = mma8452_set_power_mode(data, i);
break;
}
}
break;
default:
return -EINVAL;
ret = -EINVAL;
break;
}
iio_device_release_direct_mode(indio_dev);
return ret;
}
static int mma8452_read_thresh(struct iio_dev *indio_dev,
@ -1347,20 +1365,9 @@ static int mma8452_data_rdy_trigger_set_state(struct iio_trigger *trig,
return mma8452_change_config(data, MMA8452_CTRL_REG4, reg);
}
static int mma8452_validate_device(struct iio_trigger *trig,
struct iio_dev *indio_dev)
{
struct iio_dev *indio = iio_trigger_get_drvdata(trig);
if (indio != indio_dev)
return -EINVAL;
return 0;
}
static const struct iio_trigger_ops mma8452_trigger_ops = {
.set_trigger_state = mma8452_data_rdy_trigger_set_state,
.validate_device = mma8452_validate_device,
.validate_device = iio_trigger_validate_own_device,
.owner = THIS_MODULE,
};

1576
drivers/iio/accel/sca3000.c Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -30,6 +30,7 @@
#define LSM303AGR_ACCEL_DEV_NAME "lsm303agr_accel"
#define LIS2DH12_ACCEL_DEV_NAME "lis2dh12_accel"
#define LIS3L02DQ_ACCEL_DEV_NAME "lis3l02dq"
#define LNG2DM_ACCEL_DEV_NAME "lng2dm"
/**
* struct st_sensors_platform_data - default accel platform data

Просмотреть файл

@ -43,194 +43,6 @@
#define ST_ACCEL_FS_AVL_200G 200
#define ST_ACCEL_FS_AVL_400G 400
/* CUSTOM VALUES FOR SENSOR 1 */
#define ST_ACCEL_1_WAI_EXP 0x33
#define ST_ACCEL_1_ODR_ADDR 0x20
#define ST_ACCEL_1_ODR_MASK 0xf0
#define ST_ACCEL_1_ODR_AVL_1HZ_VAL 0x01
#define ST_ACCEL_1_ODR_AVL_10HZ_VAL 0x02
#define ST_ACCEL_1_ODR_AVL_25HZ_VAL 0x03
#define ST_ACCEL_1_ODR_AVL_50HZ_VAL 0x04
#define ST_ACCEL_1_ODR_AVL_100HZ_VAL 0x05
#define ST_ACCEL_1_ODR_AVL_200HZ_VAL 0x06
#define ST_ACCEL_1_ODR_AVL_400HZ_VAL 0x07
#define ST_ACCEL_1_ODR_AVL_1600HZ_VAL 0x08
#define ST_ACCEL_1_FS_ADDR 0x23
#define ST_ACCEL_1_FS_MASK 0x30
#define ST_ACCEL_1_FS_AVL_2_VAL 0x00
#define ST_ACCEL_1_FS_AVL_4_VAL 0x01
#define ST_ACCEL_1_FS_AVL_8_VAL 0x02
#define ST_ACCEL_1_FS_AVL_16_VAL 0x03
#define ST_ACCEL_1_FS_AVL_2_GAIN IIO_G_TO_M_S_2(1000)
#define ST_ACCEL_1_FS_AVL_4_GAIN IIO_G_TO_M_S_2(2000)
#define ST_ACCEL_1_FS_AVL_8_GAIN IIO_G_TO_M_S_2(4000)
#define ST_ACCEL_1_FS_AVL_16_GAIN IIO_G_TO_M_S_2(12000)
#define ST_ACCEL_1_BDU_ADDR 0x23
#define ST_ACCEL_1_BDU_MASK 0x80
#define ST_ACCEL_1_DRDY_IRQ_ADDR 0x22
#define ST_ACCEL_1_DRDY_IRQ_INT1_MASK 0x10
#define ST_ACCEL_1_DRDY_IRQ_INT2_MASK 0x08
#define ST_ACCEL_1_IHL_IRQ_ADDR 0x25
#define ST_ACCEL_1_IHL_IRQ_MASK 0x02
#define ST_ACCEL_1_MULTIREAD_BIT true
/* CUSTOM VALUES FOR SENSOR 2 */
#define ST_ACCEL_2_WAI_EXP 0x32
#define ST_ACCEL_2_ODR_ADDR 0x20
#define ST_ACCEL_2_ODR_MASK 0x18
#define ST_ACCEL_2_ODR_AVL_50HZ_VAL 0x00
#define ST_ACCEL_2_ODR_AVL_100HZ_VAL 0x01
#define ST_ACCEL_2_ODR_AVL_400HZ_VAL 0x02
#define ST_ACCEL_2_ODR_AVL_1000HZ_VAL 0x03
#define ST_ACCEL_2_PW_ADDR 0x20
#define ST_ACCEL_2_PW_MASK 0xe0
#define ST_ACCEL_2_FS_ADDR 0x23
#define ST_ACCEL_2_FS_MASK 0x30
#define ST_ACCEL_2_FS_AVL_2_VAL 0X00
#define ST_ACCEL_2_FS_AVL_4_VAL 0X01
#define ST_ACCEL_2_FS_AVL_8_VAL 0x03
#define ST_ACCEL_2_FS_AVL_2_GAIN IIO_G_TO_M_S_2(1000)
#define ST_ACCEL_2_FS_AVL_4_GAIN IIO_G_TO_M_S_2(2000)
#define ST_ACCEL_2_FS_AVL_8_GAIN IIO_G_TO_M_S_2(3900)
#define ST_ACCEL_2_BDU_ADDR 0x23
#define ST_ACCEL_2_BDU_MASK 0x80
#define ST_ACCEL_2_DRDY_IRQ_ADDR 0x22
#define ST_ACCEL_2_DRDY_IRQ_INT1_MASK 0x02
#define ST_ACCEL_2_DRDY_IRQ_INT2_MASK 0x10
#define ST_ACCEL_2_IHL_IRQ_ADDR 0x22
#define ST_ACCEL_2_IHL_IRQ_MASK 0x80
#define ST_ACCEL_2_OD_IRQ_ADDR 0x22
#define ST_ACCEL_2_OD_IRQ_MASK 0x40
#define ST_ACCEL_2_MULTIREAD_BIT true
/* CUSTOM VALUES FOR SENSOR 3 */
#define ST_ACCEL_3_WAI_EXP 0x40
#define ST_ACCEL_3_ODR_ADDR 0x20
#define ST_ACCEL_3_ODR_MASK 0xf0
#define ST_ACCEL_3_ODR_AVL_3HZ_VAL 0x01
#define ST_ACCEL_3_ODR_AVL_6HZ_VAL 0x02
#define ST_ACCEL_3_ODR_AVL_12HZ_VAL 0x03
#define ST_ACCEL_3_ODR_AVL_25HZ_VAL 0x04
#define ST_ACCEL_3_ODR_AVL_50HZ_VAL 0x05
#define ST_ACCEL_3_ODR_AVL_100HZ_VAL 0x06
#define ST_ACCEL_3_ODR_AVL_200HZ_VAL 0x07
#define ST_ACCEL_3_ODR_AVL_400HZ_VAL 0x08
#define ST_ACCEL_3_ODR_AVL_800HZ_VAL 0x09
#define ST_ACCEL_3_ODR_AVL_1600HZ_VAL 0x0a
#define ST_ACCEL_3_FS_ADDR 0x24
#define ST_ACCEL_3_FS_MASK 0x38
#define ST_ACCEL_3_FS_AVL_2_VAL 0X00
#define ST_ACCEL_3_FS_AVL_4_VAL 0X01
#define ST_ACCEL_3_FS_AVL_6_VAL 0x02
#define ST_ACCEL_3_FS_AVL_8_VAL 0x03
#define ST_ACCEL_3_FS_AVL_16_VAL 0x04
#define ST_ACCEL_3_FS_AVL_2_GAIN IIO_G_TO_M_S_2(61)
#define ST_ACCEL_3_FS_AVL_4_GAIN IIO_G_TO_M_S_2(122)
#define ST_ACCEL_3_FS_AVL_6_GAIN IIO_G_TO_M_S_2(183)
#define ST_ACCEL_3_FS_AVL_8_GAIN IIO_G_TO_M_S_2(244)
#define ST_ACCEL_3_FS_AVL_16_GAIN IIO_G_TO_M_S_2(732)
#define ST_ACCEL_3_BDU_ADDR 0x20
#define ST_ACCEL_3_BDU_MASK 0x08
#define ST_ACCEL_3_DRDY_IRQ_ADDR 0x23
#define ST_ACCEL_3_DRDY_IRQ_INT1_MASK 0x80
#define ST_ACCEL_3_DRDY_IRQ_INT2_MASK 0x00
#define ST_ACCEL_3_IHL_IRQ_ADDR 0x23
#define ST_ACCEL_3_IHL_IRQ_MASK 0x40
#define ST_ACCEL_3_IG1_EN_ADDR 0x23
#define ST_ACCEL_3_IG1_EN_MASK 0x08
#define ST_ACCEL_3_MULTIREAD_BIT false
/* CUSTOM VALUES FOR SENSOR 4 */
#define ST_ACCEL_4_WAI_EXP 0x3a
#define ST_ACCEL_4_ODR_ADDR 0x20
#define ST_ACCEL_4_ODR_MASK 0x30 /* DF1 and DF0 */
#define ST_ACCEL_4_ODR_AVL_40HZ_VAL 0x00
#define ST_ACCEL_4_ODR_AVL_160HZ_VAL 0x01
#define ST_ACCEL_4_ODR_AVL_640HZ_VAL 0x02
#define ST_ACCEL_4_ODR_AVL_2560HZ_VAL 0x03
#define ST_ACCEL_4_PW_ADDR 0x20
#define ST_ACCEL_4_PW_MASK 0xc0
#define ST_ACCEL_4_FS_ADDR 0x21
#define ST_ACCEL_4_FS_MASK 0x80
#define ST_ACCEL_4_FS_AVL_2_VAL 0X00
#define ST_ACCEL_4_FS_AVL_6_VAL 0X01
#define ST_ACCEL_4_FS_AVL_2_GAIN IIO_G_TO_M_S_2(1024)
#define ST_ACCEL_4_FS_AVL_6_GAIN IIO_G_TO_M_S_2(340)
#define ST_ACCEL_4_BDU_ADDR 0x21
#define ST_ACCEL_4_BDU_MASK 0x40
#define ST_ACCEL_4_DRDY_IRQ_ADDR 0x21
#define ST_ACCEL_4_DRDY_IRQ_INT1_MASK 0x04
#define ST_ACCEL_4_MULTIREAD_BIT true
/* CUSTOM VALUES FOR SENSOR 5 */
#define ST_ACCEL_5_WAI_EXP 0x3b
#define ST_ACCEL_5_ODR_ADDR 0x20
#define ST_ACCEL_5_ODR_MASK 0x80
#define ST_ACCEL_5_ODR_AVL_100HZ_VAL 0x00
#define ST_ACCEL_5_ODR_AVL_400HZ_VAL 0x01
#define ST_ACCEL_5_PW_ADDR 0x20
#define ST_ACCEL_5_PW_MASK 0x40
#define ST_ACCEL_5_FS_ADDR 0x20
#define ST_ACCEL_5_FS_MASK 0x20
#define ST_ACCEL_5_FS_AVL_2_VAL 0X00
#define ST_ACCEL_5_FS_AVL_8_VAL 0X01
/* TODO: check these resulting gain settings, these are not in the datsheet */
#define ST_ACCEL_5_FS_AVL_2_GAIN IIO_G_TO_M_S_2(18000)
#define ST_ACCEL_5_FS_AVL_8_GAIN IIO_G_TO_M_S_2(72000)
#define ST_ACCEL_5_DRDY_IRQ_ADDR 0x22
#define ST_ACCEL_5_DRDY_IRQ_INT1_MASK 0x04
#define ST_ACCEL_5_DRDY_IRQ_INT2_MASK 0x20
#define ST_ACCEL_5_IHL_IRQ_ADDR 0x22
#define ST_ACCEL_5_IHL_IRQ_MASK 0x80
#define ST_ACCEL_5_OD_IRQ_ADDR 0x22
#define ST_ACCEL_5_OD_IRQ_MASK 0x40
#define ST_ACCEL_5_IG1_EN_ADDR 0x21
#define ST_ACCEL_5_IG1_EN_MASK 0x08
#define ST_ACCEL_5_MULTIREAD_BIT false
/* CUSTOM VALUES FOR SENSOR 6 */
#define ST_ACCEL_6_WAI_EXP 0x32
#define ST_ACCEL_6_ODR_ADDR 0x20
#define ST_ACCEL_6_ODR_MASK 0x18
#define ST_ACCEL_6_ODR_AVL_50HZ_VAL 0x00
#define ST_ACCEL_6_ODR_AVL_100HZ_VAL 0x01
#define ST_ACCEL_6_ODR_AVL_400HZ_VAL 0x02
#define ST_ACCEL_6_ODR_AVL_1000HZ_VAL 0x03
#define ST_ACCEL_6_PW_ADDR 0x20
#define ST_ACCEL_6_PW_MASK 0x20
#define ST_ACCEL_6_FS_ADDR 0x23
#define ST_ACCEL_6_FS_MASK 0x30
#define ST_ACCEL_6_FS_AVL_100_VAL 0x00
#define ST_ACCEL_6_FS_AVL_200_VAL 0x01
#define ST_ACCEL_6_FS_AVL_400_VAL 0x03
#define ST_ACCEL_6_FS_AVL_100_GAIN IIO_G_TO_M_S_2(49000)
#define ST_ACCEL_6_FS_AVL_200_GAIN IIO_G_TO_M_S_2(98000)
#define ST_ACCEL_6_FS_AVL_400_GAIN IIO_G_TO_M_S_2(195000)
#define ST_ACCEL_6_BDU_ADDR 0x23
#define ST_ACCEL_6_BDU_MASK 0x80
#define ST_ACCEL_6_DRDY_IRQ_ADDR 0x22
#define ST_ACCEL_6_DRDY_IRQ_INT1_MASK 0x02
#define ST_ACCEL_6_DRDY_IRQ_INT2_MASK 0x10
#define ST_ACCEL_6_IHL_IRQ_ADDR 0x22
#define ST_ACCEL_6_IHL_IRQ_MASK 0x80
#define ST_ACCEL_6_MULTIREAD_BIT true
/* CUSTOM VALUES FOR SENSOR 7 */
#define ST_ACCEL_7_ODR_ADDR 0x20
#define ST_ACCEL_7_ODR_MASK 0x30
#define ST_ACCEL_7_ODR_AVL_280HZ_VAL 0x00
#define ST_ACCEL_7_ODR_AVL_560HZ_VAL 0x01
#define ST_ACCEL_7_ODR_AVL_1120HZ_VAL 0x02
#define ST_ACCEL_7_ODR_AVL_4480HZ_VAL 0x03
#define ST_ACCEL_7_PW_ADDR 0x20
#define ST_ACCEL_7_PW_MASK 0xc0
#define ST_ACCEL_7_FS_AVL_2_GAIN IIO_G_TO_M_S_2(488)
#define ST_ACCEL_7_BDU_ADDR 0x21
#define ST_ACCEL_7_BDU_MASK 0x40
#define ST_ACCEL_7_DRDY_IRQ_ADDR 0x21
#define ST_ACCEL_7_DRDY_IRQ_INT1_MASK 0x04
#define ST_ACCEL_7_MULTIREAD_BIT false
static const struct iio_chan_spec st_accel_8bit_channels[] = {
ST_SENSORS_LSM_CHANNELS(IIO_ACCEL,
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
@ -281,7 +93,7 @@ static const struct iio_chan_spec st_accel_16bit_channels[] = {
static const struct st_sensor_settings st_accel_sensors_settings[] = {
{
.wai = ST_ACCEL_1_WAI_EXP,
.wai = 0x33,
.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
.sensors_supported = {
[0] = LIS3DH_ACCEL_DEV_NAME,
@ -294,22 +106,22 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = {
},
.ch = (struct iio_chan_spec *)st_accel_12bit_channels,
.odr = {
.addr = ST_ACCEL_1_ODR_ADDR,
.mask = ST_ACCEL_1_ODR_MASK,
.addr = 0x20,
.mask = 0xf0,
.odr_avl = {
{ 1, ST_ACCEL_1_ODR_AVL_1HZ_VAL, },
{ 10, ST_ACCEL_1_ODR_AVL_10HZ_VAL, },
{ 25, ST_ACCEL_1_ODR_AVL_25HZ_VAL, },
{ 50, ST_ACCEL_1_ODR_AVL_50HZ_VAL, },
{ 100, ST_ACCEL_1_ODR_AVL_100HZ_VAL, },
{ 200, ST_ACCEL_1_ODR_AVL_200HZ_VAL, },
{ 400, ST_ACCEL_1_ODR_AVL_400HZ_VAL, },
{ 1600, ST_ACCEL_1_ODR_AVL_1600HZ_VAL, },
{ .hz = 1, .value = 0x01, },
{ .hz = 10, .value = 0x02, },
{ .hz = 25, .value = 0x03, },
{ .hz = 50, .value = 0x04, },
{ .hz = 100, .value = 0x05, },
{ .hz = 200, .value = 0x06, },
{ .hz = 400, .value = 0x07, },
{ .hz = 1600, .value = 0x08, },
},
},
.pw = {
.addr = ST_ACCEL_1_ODR_ADDR,
.mask = ST_ACCEL_1_ODR_MASK,
.addr = 0x20,
.mask = 0xf0,
.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
},
.enable_axis = {
@ -317,48 +129,48 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = {
.mask = ST_SENSORS_DEFAULT_AXIS_MASK,
},
.fs = {
.addr = ST_ACCEL_1_FS_ADDR,
.mask = ST_ACCEL_1_FS_MASK,
.addr = 0x23,
.mask = 0x30,
.fs_avl = {
[0] = {
.num = ST_ACCEL_FS_AVL_2G,
.value = ST_ACCEL_1_FS_AVL_2_VAL,
.gain = ST_ACCEL_1_FS_AVL_2_GAIN,
.value = 0x00,
.gain = IIO_G_TO_M_S_2(1000),
},
[1] = {
.num = ST_ACCEL_FS_AVL_4G,
.value = ST_ACCEL_1_FS_AVL_4_VAL,
.gain = ST_ACCEL_1_FS_AVL_4_GAIN,
.value = 0x01,
.gain = IIO_G_TO_M_S_2(2000),
},
[2] = {
.num = ST_ACCEL_FS_AVL_8G,
.value = ST_ACCEL_1_FS_AVL_8_VAL,
.gain = ST_ACCEL_1_FS_AVL_8_GAIN,
.value = 0x02,
.gain = IIO_G_TO_M_S_2(4000),
},
[3] = {
.num = ST_ACCEL_FS_AVL_16G,
.value = ST_ACCEL_1_FS_AVL_16_VAL,
.gain = ST_ACCEL_1_FS_AVL_16_GAIN,
.value = 0x03,
.gain = IIO_G_TO_M_S_2(12000),
},
},
},
.bdu = {
.addr = ST_ACCEL_1_BDU_ADDR,
.mask = ST_ACCEL_1_BDU_MASK,
.addr = 0x23,
.mask = 0x80,
},
.drdy_irq = {
.addr = ST_ACCEL_1_DRDY_IRQ_ADDR,
.mask_int1 = ST_ACCEL_1_DRDY_IRQ_INT1_MASK,
.mask_int2 = ST_ACCEL_1_DRDY_IRQ_INT2_MASK,
.addr_ihl = ST_ACCEL_1_IHL_IRQ_ADDR,
.mask_ihl = ST_ACCEL_1_IHL_IRQ_MASK,
.addr = 0x22,
.mask_int1 = 0x10,
.mask_int2 = 0x08,
.addr_ihl = 0x25,
.mask_ihl = 0x02,
.addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR,
},
.multi_read_bit = ST_ACCEL_1_MULTIREAD_BIT,
.multi_read_bit = true,
.bootime = 2,
},
{
.wai = ST_ACCEL_2_WAI_EXP,
.wai = 0x32,
.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
.sensors_supported = {
[0] = LIS331DLH_ACCEL_DEV_NAME,
@ -368,18 +180,18 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = {
},
.ch = (struct iio_chan_spec *)st_accel_12bit_channels,
.odr = {
.addr = ST_ACCEL_2_ODR_ADDR,
.mask = ST_ACCEL_2_ODR_MASK,
.addr = 0x20,
.mask = 0x18,
.odr_avl = {
{ 50, ST_ACCEL_2_ODR_AVL_50HZ_VAL, },
{ 100, ST_ACCEL_2_ODR_AVL_100HZ_VAL, },
{ 400, ST_ACCEL_2_ODR_AVL_400HZ_VAL, },
{ 1000, ST_ACCEL_2_ODR_AVL_1000HZ_VAL, },
{ .hz = 50, .value = 0x00, },
{ .hz = 100, .value = 0x01, },
{ .hz = 400, .value = 0x02, },
{ .hz = 1000, .value = 0x03, },
},
},
.pw = {
.addr = ST_ACCEL_2_PW_ADDR,
.mask = ST_ACCEL_2_PW_MASK,
.addr = 0x20,
.mask = 0xe0,
.value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE,
.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
},
@ -388,69 +200,69 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = {
.mask = ST_SENSORS_DEFAULT_AXIS_MASK,
},
.fs = {
.addr = ST_ACCEL_2_FS_ADDR,
.mask = ST_ACCEL_2_FS_MASK,
.addr = 0x23,
.mask = 0x30,
.fs_avl = {
[0] = {
.num = ST_ACCEL_FS_AVL_2G,
.value = ST_ACCEL_2_FS_AVL_2_VAL,
.gain = ST_ACCEL_2_FS_AVL_2_GAIN,
.value = 0x00,
.gain = IIO_G_TO_M_S_2(1000),
},
[1] = {
.num = ST_ACCEL_FS_AVL_4G,
.value = ST_ACCEL_2_FS_AVL_4_VAL,
.gain = ST_ACCEL_2_FS_AVL_4_GAIN,
.value = 0x01,
.gain = IIO_G_TO_M_S_2(2000),
},
[2] = {
.num = ST_ACCEL_FS_AVL_8G,
.value = ST_ACCEL_2_FS_AVL_8_VAL,
.gain = ST_ACCEL_2_FS_AVL_8_GAIN,
.value = 0x03,
.gain = IIO_G_TO_M_S_2(3900),
},
},
},
.bdu = {
.addr = ST_ACCEL_2_BDU_ADDR,
.mask = ST_ACCEL_2_BDU_MASK,
.addr = 0x23,
.mask = 0x80,
},
.drdy_irq = {
.addr = ST_ACCEL_2_DRDY_IRQ_ADDR,
.mask_int1 = ST_ACCEL_2_DRDY_IRQ_INT1_MASK,
.mask_int2 = ST_ACCEL_2_DRDY_IRQ_INT2_MASK,
.addr_ihl = ST_ACCEL_2_IHL_IRQ_ADDR,
.mask_ihl = ST_ACCEL_2_IHL_IRQ_MASK,
.addr_od = ST_ACCEL_2_OD_IRQ_ADDR,
.mask_od = ST_ACCEL_2_OD_IRQ_MASK,
.addr = 0x22,
.mask_int1 = 0x02,
.mask_int2 = 0x10,
.addr_ihl = 0x22,
.mask_ihl = 0x80,
.addr_od = 0x22,
.mask_od = 0x40,
.addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR,
},
.multi_read_bit = ST_ACCEL_2_MULTIREAD_BIT,
.multi_read_bit = true,
.bootime = 2,
},
{
.wai = ST_ACCEL_3_WAI_EXP,
.wai = 0x40,
.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
.sensors_supported = {
[0] = LSM330_ACCEL_DEV_NAME,
},
.ch = (struct iio_chan_spec *)st_accel_16bit_channels,
.odr = {
.addr = ST_ACCEL_3_ODR_ADDR,
.mask = ST_ACCEL_3_ODR_MASK,
.addr = 0x20,
.mask = 0xf0,
.odr_avl = {
{ 3, ST_ACCEL_3_ODR_AVL_3HZ_VAL },
{ 6, ST_ACCEL_3_ODR_AVL_6HZ_VAL, },
{ 12, ST_ACCEL_3_ODR_AVL_12HZ_VAL, },
{ 25, ST_ACCEL_3_ODR_AVL_25HZ_VAL, },
{ 50, ST_ACCEL_3_ODR_AVL_50HZ_VAL, },
{ 100, ST_ACCEL_3_ODR_AVL_100HZ_VAL, },
{ 200, ST_ACCEL_3_ODR_AVL_200HZ_VAL, },
{ 400, ST_ACCEL_3_ODR_AVL_400HZ_VAL, },
{ 800, ST_ACCEL_3_ODR_AVL_800HZ_VAL, },
{ 1600, ST_ACCEL_3_ODR_AVL_1600HZ_VAL, },
{ .hz = 3, .value = 0x01, },
{ .hz = 6, .value = 0x02, },
{ .hz = 12, .value = 0x03, },
{ .hz = 25, .value = 0x04, },
{ .hz = 50, .value = 0x05, },
{ .hz = 100, .value = 0x06, },
{ .hz = 200, .value = 0x07, },
{ .hz = 400, .value = 0x08, },
{ .hz = 800, .value = 0x09, },
{ .hz = 1600, .value = 0x0a, },
},
},
.pw = {
.addr = ST_ACCEL_3_ODR_ADDR,
.mask = ST_ACCEL_3_ODR_MASK,
.addr = 0x20,
.mask = 0xf0,
.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
},
.enable_axis = {
@ -458,75 +270,75 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = {
.mask = ST_SENSORS_DEFAULT_AXIS_MASK,
},
.fs = {
.addr = ST_ACCEL_3_FS_ADDR,
.mask = ST_ACCEL_3_FS_MASK,
.addr = 0x24,
.mask = 0x38,
.fs_avl = {
[0] = {
.num = ST_ACCEL_FS_AVL_2G,
.value = ST_ACCEL_3_FS_AVL_2_VAL,
.gain = ST_ACCEL_3_FS_AVL_2_GAIN,
.value = 0x00,
.gain = IIO_G_TO_M_S_2(61),
},
[1] = {
.num = ST_ACCEL_FS_AVL_4G,
.value = ST_ACCEL_3_FS_AVL_4_VAL,
.gain = ST_ACCEL_3_FS_AVL_4_GAIN,
.value = 0x01,
.gain = IIO_G_TO_M_S_2(122),
},
[2] = {
.num = ST_ACCEL_FS_AVL_6G,
.value = ST_ACCEL_3_FS_AVL_6_VAL,
.gain = ST_ACCEL_3_FS_AVL_6_GAIN,
.value = 0x02,
.gain = IIO_G_TO_M_S_2(183),
},
[3] = {
.num = ST_ACCEL_FS_AVL_8G,
.value = ST_ACCEL_3_FS_AVL_8_VAL,
.gain = ST_ACCEL_3_FS_AVL_8_GAIN,
.value = 0x03,
.gain = IIO_G_TO_M_S_2(244),
},
[4] = {
.num = ST_ACCEL_FS_AVL_16G,
.value = ST_ACCEL_3_FS_AVL_16_VAL,
.gain = ST_ACCEL_3_FS_AVL_16_GAIN,
.value = 0x04,
.gain = IIO_G_TO_M_S_2(732),
},
},
},
.bdu = {
.addr = ST_ACCEL_3_BDU_ADDR,
.mask = ST_ACCEL_3_BDU_MASK,
.addr = 0x20,
.mask = 0x08,
},
.drdy_irq = {
.addr = ST_ACCEL_3_DRDY_IRQ_ADDR,
.mask_int1 = ST_ACCEL_3_DRDY_IRQ_INT1_MASK,
.mask_int2 = ST_ACCEL_3_DRDY_IRQ_INT2_MASK,
.addr_ihl = ST_ACCEL_3_IHL_IRQ_ADDR,
.mask_ihl = ST_ACCEL_3_IHL_IRQ_MASK,
.addr = 0x23,
.mask_int1 = 0x80,
.mask_int2 = 0x00,
.addr_ihl = 0x23,
.mask_ihl = 0x40,
.addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR,
.ig1 = {
.en_addr = ST_ACCEL_3_IG1_EN_ADDR,
.en_mask = ST_ACCEL_3_IG1_EN_MASK,
.en_addr = 0x23,
.en_mask = 0x08,
},
},
.multi_read_bit = ST_ACCEL_3_MULTIREAD_BIT,
.multi_read_bit = false,
.bootime = 2,
},
{
.wai = ST_ACCEL_4_WAI_EXP,
.wai = 0x3a,
.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
.sensors_supported = {
[0] = LIS3LV02DL_ACCEL_DEV_NAME,
},
.ch = (struct iio_chan_spec *)st_accel_12bit_channels,
.odr = {
.addr = ST_ACCEL_4_ODR_ADDR,
.mask = ST_ACCEL_4_ODR_MASK,
.addr = 0x20,
.mask = 0x30, /* DF1 and DF0 */
.odr_avl = {
{ 40, ST_ACCEL_4_ODR_AVL_40HZ_VAL },
{ 160, ST_ACCEL_4_ODR_AVL_160HZ_VAL, },
{ 640, ST_ACCEL_4_ODR_AVL_640HZ_VAL, },
{ 2560, ST_ACCEL_4_ODR_AVL_2560HZ_VAL, },
{ .hz = 40, .value = 0x00, },
{ .hz = 160, .value = 0x01, },
{ .hz = 640, .value = 0x02, },
{ .hz = 2560, .value = 0x03, },
},
},
.pw = {
.addr = ST_ACCEL_4_PW_ADDR,
.mask = ST_ACCEL_4_PW_MASK,
.addr = 0x20,
.mask = 0xc0,
.value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE,
.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
},
@ -535,51 +347,51 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = {
.mask = ST_SENSORS_DEFAULT_AXIS_MASK,
},
.fs = {
.addr = ST_ACCEL_4_FS_ADDR,
.mask = ST_ACCEL_4_FS_MASK,
.addr = 0x21,
.mask = 0x80,
.fs_avl = {
[0] = {
.num = ST_ACCEL_FS_AVL_2G,
.value = ST_ACCEL_4_FS_AVL_2_VAL,
.gain = ST_ACCEL_4_FS_AVL_2_GAIN,
.value = 0x00,
.gain = IIO_G_TO_M_S_2(1024),
},
[1] = {
.num = ST_ACCEL_FS_AVL_6G,
.value = ST_ACCEL_4_FS_AVL_6_VAL,
.gain = ST_ACCEL_4_FS_AVL_6_GAIN,
.value = 0x01,
.gain = IIO_G_TO_M_S_2(340),
},
},
},
.bdu = {
.addr = ST_ACCEL_4_BDU_ADDR,
.mask = ST_ACCEL_4_BDU_MASK,
.addr = 0x21,
.mask = 0x40,
},
.drdy_irq = {
.addr = ST_ACCEL_4_DRDY_IRQ_ADDR,
.mask_int1 = ST_ACCEL_4_DRDY_IRQ_INT1_MASK,
.addr = 0x21,
.mask_int1 = 0x04,
.addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR,
},
.multi_read_bit = ST_ACCEL_4_MULTIREAD_BIT,
.multi_read_bit = true,
.bootime = 2, /* guess */
},
{
.wai = ST_ACCEL_5_WAI_EXP,
.wai = 0x3b,
.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
.sensors_supported = {
[0] = LIS331DL_ACCEL_DEV_NAME,
},
.ch = (struct iio_chan_spec *)st_accel_8bit_channels,
.odr = {
.addr = ST_ACCEL_5_ODR_ADDR,
.mask = ST_ACCEL_5_ODR_MASK,
.addr = 0x20,
.mask = 0x80,
.odr_avl = {
{ 100, ST_ACCEL_5_ODR_AVL_100HZ_VAL },
{ 400, ST_ACCEL_5_ODR_AVL_400HZ_VAL, },
{ .hz = 100, .value = 0x00, },
{ .hz = 400, .value = 0x01, },
},
},
.pw = {
.addr = ST_ACCEL_5_PW_ADDR,
.mask = ST_ACCEL_5_PW_MASK,
.addr = 0x20,
.mask = 0x40,
.value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE,
.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
},
@ -588,54 +400,58 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = {
.mask = ST_SENSORS_DEFAULT_AXIS_MASK,
},
.fs = {
.addr = ST_ACCEL_5_FS_ADDR,
.mask = ST_ACCEL_5_FS_MASK,
.addr = 0x20,
.mask = 0x20,
/*
* TODO: check these resulting gain settings, these are
* not in the datsheet
*/
.fs_avl = {
[0] = {
.num = ST_ACCEL_FS_AVL_2G,
.value = ST_ACCEL_5_FS_AVL_2_VAL,
.gain = ST_ACCEL_5_FS_AVL_2_GAIN,
.value = 0x00,
.gain = IIO_G_TO_M_S_2(18000),
},
[1] = {
.num = ST_ACCEL_FS_AVL_8G,
.value = ST_ACCEL_5_FS_AVL_8_VAL,
.gain = ST_ACCEL_5_FS_AVL_8_GAIN,
.value = 0x01,
.gain = IIO_G_TO_M_S_2(72000),
},
},
},
.drdy_irq = {
.addr = ST_ACCEL_5_DRDY_IRQ_ADDR,
.mask_int1 = ST_ACCEL_5_DRDY_IRQ_INT1_MASK,
.mask_int2 = ST_ACCEL_5_DRDY_IRQ_INT2_MASK,
.addr_ihl = ST_ACCEL_5_IHL_IRQ_ADDR,
.mask_ihl = ST_ACCEL_5_IHL_IRQ_MASK,
.addr_od = ST_ACCEL_5_OD_IRQ_ADDR,
.mask_od = ST_ACCEL_5_OD_IRQ_MASK,
.addr = 0x22,
.mask_int1 = 0x04,
.mask_int2 = 0x20,
.addr_ihl = 0x22,
.mask_ihl = 0x80,
.addr_od = 0x22,
.mask_od = 0x40,
.addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR,
},
.multi_read_bit = ST_ACCEL_5_MULTIREAD_BIT,
.multi_read_bit = false,
.bootime = 2, /* guess */
},
{
.wai = ST_ACCEL_6_WAI_EXP,
.wai = 0x32,
.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
.sensors_supported = {
[0] = H3LIS331DL_DRIVER_NAME,
},
.ch = (struct iio_chan_spec *)st_accel_12bit_channels,
.odr = {
.addr = ST_ACCEL_6_ODR_ADDR,
.mask = ST_ACCEL_6_ODR_MASK,
.addr = 0x20,
.mask = 0x18,
.odr_avl = {
{ 50, ST_ACCEL_6_ODR_AVL_50HZ_VAL },
{ 100, ST_ACCEL_6_ODR_AVL_100HZ_VAL, },
{ 400, ST_ACCEL_6_ODR_AVL_400HZ_VAL, },
{ 1000, ST_ACCEL_6_ODR_AVL_1000HZ_VAL, },
{ .hz = 50, .value = 0x00, },
{ .hz = 100, .value = 0x01, },
{ .hz = 400, .value = 0x02, },
{ .hz = 1000, .value = 0x03, },
},
},
.pw = {
.addr = ST_ACCEL_6_PW_ADDR,
.mask = ST_ACCEL_6_PW_MASK,
.addr = 0x20,
.mask = 0x20,
.value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE,
.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
},
@ -644,38 +460,38 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = {
.mask = ST_SENSORS_DEFAULT_AXIS_MASK,
},
.fs = {
.addr = ST_ACCEL_6_FS_ADDR,
.mask = ST_ACCEL_6_FS_MASK,
.addr = 0x23,
.mask = 0x30,
.fs_avl = {
[0] = {
.num = ST_ACCEL_FS_AVL_100G,
.value = ST_ACCEL_6_FS_AVL_100_VAL,
.gain = ST_ACCEL_6_FS_AVL_100_GAIN,
.value = 0x00,
.gain = IIO_G_TO_M_S_2(49000),
},
[1] = {
.num = ST_ACCEL_FS_AVL_200G,
.value = ST_ACCEL_6_FS_AVL_200_VAL,
.gain = ST_ACCEL_6_FS_AVL_200_GAIN,
.value = 0x01,
.gain = IIO_G_TO_M_S_2(98000),
},
[2] = {
.num = ST_ACCEL_FS_AVL_400G,
.value = ST_ACCEL_6_FS_AVL_400_VAL,
.gain = ST_ACCEL_6_FS_AVL_400_GAIN,
.value = 0x03,
.gain = IIO_G_TO_M_S_2(195000),
},
},
},
.bdu = {
.addr = ST_ACCEL_6_BDU_ADDR,
.mask = ST_ACCEL_6_BDU_MASK,
.addr = 0x23,
.mask = 0x80,
},
.drdy_irq = {
.addr = ST_ACCEL_6_DRDY_IRQ_ADDR,
.mask_int1 = ST_ACCEL_6_DRDY_IRQ_INT1_MASK,
.mask_int2 = ST_ACCEL_6_DRDY_IRQ_INT2_MASK,
.addr_ihl = ST_ACCEL_6_IHL_IRQ_ADDR,
.mask_ihl = ST_ACCEL_6_IHL_IRQ_MASK,
.addr = 0x22,
.mask_int1 = 0x02,
.mask_int2 = 0x10,
.addr_ihl = 0x22,
.mask_ihl = 0x80,
},
.multi_read_bit = ST_ACCEL_6_MULTIREAD_BIT,
.multi_read_bit = true,
.bootime = 2,
},
{
@ -685,18 +501,18 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = {
},
.ch = (struct iio_chan_spec *)st_accel_12bit_channels,
.odr = {
.addr = ST_ACCEL_7_ODR_ADDR,
.mask = ST_ACCEL_7_ODR_MASK,
.addr = 0x20,
.mask = 0x30,
.odr_avl = {
{ 280, ST_ACCEL_7_ODR_AVL_280HZ_VAL, },
{ 560, ST_ACCEL_7_ODR_AVL_560HZ_VAL, },
{ 1120, ST_ACCEL_7_ODR_AVL_1120HZ_VAL, },
{ 4480, ST_ACCEL_7_ODR_AVL_4480HZ_VAL, },
{ .hz = 280, .value = 0x00, },
{ .hz = 560, .value = 0x01, },
{ .hz = 1120, .value = 0x02, },
{ .hz = 4480, .value = 0x03, },
},
},
.pw = {
.addr = ST_ACCEL_7_PW_ADDR,
.mask = ST_ACCEL_7_PW_MASK,
.addr = 0x20,
.mask = 0xc0,
.value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE,
.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
},
@ -708,7 +524,7 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = {
.fs_avl = {
[0] = {
.num = ST_ACCEL_FS_AVL_2G,
.gain = ST_ACCEL_7_FS_AVL_2_GAIN,
.gain = IIO_G_TO_M_S_2(488),
},
},
},
@ -719,11 +535,78 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = {
.bdu = {
},
.drdy_irq = {
.addr = ST_ACCEL_7_DRDY_IRQ_ADDR,
.mask_int1 = ST_ACCEL_7_DRDY_IRQ_INT1_MASK,
.addr = 0x21,
.mask_int1 = 0x04,
.addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR,
},
.multi_read_bit = ST_ACCEL_7_MULTIREAD_BIT,
.multi_read_bit = false,
.bootime = 2,
},
{
.wai = 0x33,
.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
.sensors_supported = {
[0] = LNG2DM_ACCEL_DEV_NAME,
},
.ch = (struct iio_chan_spec *)st_accel_8bit_channels,
.odr = {
.addr = 0x20,
.mask = 0xf0,
.odr_avl = {
{ .hz = 1, .value = 0x01, },
{ .hz = 10, .value = 0x02, },
{ .hz = 25, .value = 0x03, },
{ .hz = 50, .value = 0x04, },
{ .hz = 100, .value = 0x05, },
{ .hz = 200, .value = 0x06, },
{ .hz = 400, .value = 0x07, },
{ .hz = 1600, .value = 0x08, },
},
},
.pw = {
.addr = 0x20,
.mask = 0xf0,
.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
},
.enable_axis = {
.addr = ST_SENSORS_DEFAULT_AXIS_ADDR,
.mask = ST_SENSORS_DEFAULT_AXIS_MASK,
},
.fs = {
.addr = 0x23,
.mask = 0x30,
.fs_avl = {
[0] = {
.num = ST_ACCEL_FS_AVL_2G,
.value = 0x00,
.gain = IIO_G_TO_M_S_2(15600),
},
[1] = {
.num = ST_ACCEL_FS_AVL_4G,
.value = 0x01,
.gain = IIO_G_TO_M_S_2(31200),
},
[2] = {
.num = ST_ACCEL_FS_AVL_8G,
.value = 0x02,
.gain = IIO_G_TO_M_S_2(62500),
},
[3] = {
.num = ST_ACCEL_FS_AVL_16G,
.value = 0x03,
.gain = IIO_G_TO_M_S_2(187500),
},
},
},
.drdy_irq = {
.addr = 0x22,
.mask_int1 = 0x10,
.mask_int2 = 0x08,
.addr_ihl = 0x25,
.mask_ihl = 0x02,
.addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR,
},
.multi_read_bit = true,
.bootime = 2,
},
};

Просмотреть файл

@ -84,6 +84,10 @@ static const struct of_device_id st_accel_of_match[] = {
.compatible = "st,lis3l02dq",
.data = LIS3L02DQ_ACCEL_DEV_NAME,
},
{
.compatible = "st,lng2dm-accel",
.data = LNG2DM_ACCEL_DEV_NAME,
},
{},
};
MODULE_DEVICE_TABLE(of, st_accel_of_match);
@ -135,6 +139,7 @@ static const struct i2c_device_id st_accel_id_table[] = {
{ LSM303AGR_ACCEL_DEV_NAME },
{ LIS2DH12_ACCEL_DEV_NAME },
{ LIS3L02DQ_ACCEL_DEV_NAME },
{ LNG2DM_ACCEL_DEV_NAME },
{},
};
MODULE_DEVICE_TABLE(i2c, st_accel_id_table);

Просмотреть файл

@ -60,6 +60,7 @@ static const struct spi_device_id st_accel_id_table[] = {
{ LSM303AGR_ACCEL_DEV_NAME },
{ LIS2DH12_ACCEL_DEV_NAME },
{ LIS3L02DQ_ACCEL_DEV_NAME },
{ LNG2DM_ACCEL_DEV_NAME },
{},
};
MODULE_DEVICE_TABLE(spi, st_accel_id_table);

Просмотреть файл

@ -58,6 +58,18 @@ config AD7476
To compile this driver as a module, choose M here: the
module will be called ad7476.
config AD7766
tristate "Analog Devices AD7766/AD7767 ADC driver"
depends on SPI_MASTER
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
help
Say yes here to build support for Analog Devices AD7766, AD7766-1,
AD7766-2, AD7767, AD7767-1, AD7767-2 SPI analog to digital converters.
To compile this driver as a module, choose M here: the module will be
called ad7766.
config AD7791
tristate "Analog Devices AD7791 ADC driver"
depends on SPI
@ -195,6 +207,16 @@ config DA9150_GPADC
To compile this driver as a module, choose M here: the module will be
called berlin2-adc.
config ENVELOPE_DETECTOR
tristate "Envelope detector using a DAC and a comparator"
depends on OF
help
Say yes here to build support for an envelope detector using a DAC
and a comparator.
To compile this driver as a module, choose M here: the module will be
called envelope-detector.
config EXYNOS_ADC
tristate "Exynos ADC driver support"
depends on ARCH_EXYNOS || ARCH_S3C24XX || ARCH_S3C64XX || (OF && COMPILE_TEST)
@ -419,6 +441,28 @@ config ROCKCHIP_SARADC
To compile this driver as a module, choose M here: the
module will be called rockchip_saradc.
config STM32_ADC_CORE
tristate "STMicroelectronics STM32 adc core"
depends on ARCH_STM32 || COMPILE_TEST
depends on OF
depends on REGULATOR
help
Select this option to enable the core driver for STMicroelectronics
STM32 analog-to-digital converter (ADC).
This driver can also be built as a module. If so, the module
will be called stm32-adc-core.
config STM32_ADC
tristate "STMicroelectronics STM32 adc"
depends on STM32_ADC_CORE
help
Say yes here to build support for STMicroelectronics stm32 Analog
to Digital Converter (ADC).
This driver can also be built as a module. If so, the module
will be called stm32-adc.
config STX104
tristate "Apex Embedded Systems STX104 driver"
depends on X86 && ISA_BUS_API
@ -449,6 +493,8 @@ config TI_ADC081C
config TI_ADC0832
tristate "Texas Instruments ADC0831/ADC0832/ADC0834/ADC0838"
depends on SPI
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
help
If you say yes here you get support for Texas Instruments ADC0831,
ADC0832, ADC0834, ADC0838 ADC chips.

Просмотреть файл

@ -9,6 +9,7 @@ obj-$(CONFIG_AD7291) += ad7291.o
obj-$(CONFIG_AD7298) += ad7298.o
obj-$(CONFIG_AD7923) += ad7923.o
obj-$(CONFIG_AD7476) += ad7476.o
obj-$(CONFIG_AD7766) += ad7766.o
obj-$(CONFIG_AD7791) += ad7791.o
obj-$(CONFIG_AD7793) += ad7793.o
obj-$(CONFIG_AD7887) += ad7887.o
@ -20,6 +21,7 @@ obj-$(CONFIG_BCM_IPROC_ADC) += bcm_iproc_adc.o
obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o
obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o
obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o
obj-$(CONFIG_ENVELOPE_DETECTOR) += envelope-detector.o
obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
obj-$(CONFIG_FSL_MX25_ADC) += fsl-imx25-gcq.o
obj-$(CONFIG_HI8435) += hi8435.o
@ -41,6 +43,8 @@ obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o
obj-$(CONFIG_QCOM_SPMI_VADC) += qcom-spmi-vadc.o
obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
obj-$(CONFIG_STX104) += stx104.o
obj-$(CONFIG_STM32_ADC_CORE) += stm32-adc-core.o
obj-$(CONFIG_STM32_ADC) += stm32-adc.o
obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
obj-$(CONFIG_TI_ADC0832) += ti-adc0832.o
obj-$(CONFIG_TI_ADC12138) += ti-adc12138.o

330
drivers/iio/adc/ad7766.c Normal file
Просмотреть файл

@ -0,0 +1,330 @@
/*
* AD7766/AD7767 SPI ADC driver
*
* Copyright 2016 Analog Devices Inc.
*
* Licensed under the GPL-2 or later.
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/gpio/consumer.h>
#include <linux/module.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/spi/spi.h>
#include <linux/iio/iio.h>
#include <linux/iio/buffer.h>
#include <linux/iio/trigger.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
struct ad7766_chip_info {
unsigned int decimation_factor;
};
enum {
AD7766_SUPPLY_AVDD = 0,
AD7766_SUPPLY_DVDD = 1,
AD7766_SUPPLY_VREF = 2,
AD7766_NUM_SUPPLIES = 3
};
struct ad7766 {
const struct ad7766_chip_info *chip_info;
struct spi_device *spi;
struct clk *mclk;
struct gpio_desc *pd_gpio;
struct regulator_bulk_data reg[AD7766_NUM_SUPPLIES];
struct iio_trigger *trig;
struct spi_transfer xfer;
struct spi_message msg;
/*
* DMA (thus cache coherency maintenance) requires the
* transfer buffers to live in their own cache lines.
* Make the buffer large enough for one 24 bit sample and one 64 bit
* aligned 64 bit timestamp.
*/
unsigned char data[ALIGN(3, sizeof(s64)) + sizeof(s64)]
____cacheline_aligned;
};
/*
* AD7766 and AD7767 variations are interface compatible, the main difference is
* analog performance. Both parts will use the same ID.
*/
enum ad7766_device_ids {
ID_AD7766,
ID_AD7766_1,
ID_AD7766_2,
};
static irqreturn_t ad7766_trigger_handler(int irq, void *p)
{
struct iio_poll_func *pf = p;
struct iio_dev *indio_dev = pf->indio_dev;
struct ad7766 *ad7766 = iio_priv(indio_dev);
int ret;
ret = spi_sync(ad7766->spi, &ad7766->msg);
if (ret < 0)
goto done;
iio_push_to_buffers_with_timestamp(indio_dev, ad7766->data,
pf->timestamp);
done:
iio_trigger_notify_done(indio_dev->trig);
return IRQ_HANDLED;
}
static int ad7766_preenable(struct iio_dev *indio_dev)
{
struct ad7766 *ad7766 = iio_priv(indio_dev);
int ret;
ret = regulator_bulk_enable(ARRAY_SIZE(ad7766->reg), ad7766->reg);
if (ret < 0) {
dev_err(&ad7766->spi->dev, "Failed to enable supplies: %d\n",
ret);
return ret;
}
ret = clk_prepare_enable(ad7766->mclk);
if (ret < 0) {
dev_err(&ad7766->spi->dev, "Failed to enable MCLK: %d\n", ret);
regulator_bulk_disable(ARRAY_SIZE(ad7766->reg), ad7766->reg);
return ret;
}
if (ad7766->pd_gpio)
gpiod_set_value(ad7766->pd_gpio, 0);
return 0;
}
static int ad7766_postdisable(struct iio_dev *indio_dev)
{
struct ad7766 *ad7766 = iio_priv(indio_dev);
if (ad7766->pd_gpio)
gpiod_set_value(ad7766->pd_gpio, 1);
/*
* The PD pin is synchronous to the clock, so give it some time to
* notice the change before we disable the clock.
*/
msleep(20);
clk_disable_unprepare(ad7766->mclk);
regulator_bulk_disable(ARRAY_SIZE(ad7766->reg), ad7766->reg);
return 0;
}
static int ad7766_read_raw(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan, int *val, int *val2, long info)
{
struct ad7766 *ad7766 = iio_priv(indio_dev);
struct regulator *vref = ad7766->reg[AD7766_SUPPLY_VREF].consumer;
int scale_uv;
switch (info) {
case IIO_CHAN_INFO_SCALE:
scale_uv = regulator_get_voltage(vref);
if (scale_uv < 0)
return scale_uv;
*val = scale_uv / 1000;
*val2 = chan->scan_type.realbits;
return IIO_VAL_FRACTIONAL_LOG2;
case IIO_CHAN_INFO_SAMP_FREQ:
*val = clk_get_rate(ad7766->mclk) /
ad7766->chip_info->decimation_factor;
return IIO_VAL_INT;
}
return -EINVAL;
}
static const struct iio_chan_spec ad7766_channels[] = {
{
.type = IIO_VOLTAGE,
.indexed = 1,
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
.scan_type = {
.sign = 's',
.realbits = 24,
.storagebits = 32,
.endianness = IIO_BE,
},
},
IIO_CHAN_SOFT_TIMESTAMP(1),
};
static const struct ad7766_chip_info ad7766_chip_info[] = {
[ID_AD7766] = {
.decimation_factor = 8,
},
[ID_AD7766_1] = {
.decimation_factor = 16,
},
[ID_AD7766_2] = {
.decimation_factor = 32,
},
};
static const struct iio_buffer_setup_ops ad7766_buffer_setup_ops = {
.preenable = &ad7766_preenable,
.postenable = &iio_triggered_buffer_postenable,
.predisable = &iio_triggered_buffer_predisable,
.postdisable = &ad7766_postdisable,
};
static const struct iio_info ad7766_info = {
.driver_module = THIS_MODULE,
.read_raw = &ad7766_read_raw,
};
static irqreturn_t ad7766_irq(int irq, void *private)
{
iio_trigger_poll(private);
return IRQ_HANDLED;
}
static int ad7766_set_trigger_state(struct iio_trigger *trig, bool enable)
{
struct ad7766 *ad7766 = iio_trigger_get_drvdata(trig);
if (enable)
enable_irq(ad7766->spi->irq);
else
disable_irq(ad7766->spi->irq);
return 0;
}
static const struct iio_trigger_ops ad7766_trigger_ops = {
.owner = THIS_MODULE,
.set_trigger_state = ad7766_set_trigger_state,
.validate_device = iio_trigger_validate_own_device,
};
static int ad7766_probe(struct spi_device *spi)
{
const struct spi_device_id *id = spi_get_device_id(spi);
struct iio_dev *indio_dev;
struct ad7766 *ad7766;
int ret;
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*ad7766));
if (!indio_dev)
return -ENOMEM;
ad7766 = iio_priv(indio_dev);
ad7766->chip_info = &ad7766_chip_info[id->driver_data];
ad7766->mclk = devm_clk_get(&spi->dev, "mclk");
if (IS_ERR(ad7766->mclk))
return PTR_ERR(ad7766->mclk);
ad7766->reg[AD7766_SUPPLY_AVDD].supply = "avdd";
ad7766->reg[AD7766_SUPPLY_DVDD].supply = "dvdd";
ad7766->reg[AD7766_SUPPLY_VREF].supply = "vref";
ret = devm_regulator_bulk_get(&spi->dev, ARRAY_SIZE(ad7766->reg),
ad7766->reg);
if (ret)
return ret;
ad7766->pd_gpio = devm_gpiod_get_optional(&spi->dev, "powerdown",
GPIOD_OUT_HIGH);
if (IS_ERR(ad7766->pd_gpio))
return PTR_ERR(ad7766->pd_gpio);
indio_dev->dev.parent = &spi->dev;
indio_dev->name = spi_get_device_id(spi)->name;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = ad7766_channels;
indio_dev->num_channels = ARRAY_SIZE(ad7766_channels);
indio_dev->info = &ad7766_info;
if (spi->irq > 0) {
ad7766->trig = devm_iio_trigger_alloc(&spi->dev, "%s-dev%d",
indio_dev->name, indio_dev->id);
if (!ad7766->trig)
return -ENOMEM;
ad7766->trig->ops = &ad7766_trigger_ops;
ad7766->trig->dev.parent = &spi->dev;
iio_trigger_set_drvdata(ad7766->trig, ad7766);
ret = devm_request_irq(&spi->dev, spi->irq, ad7766_irq,
IRQF_TRIGGER_FALLING, dev_name(&spi->dev),
ad7766->trig);
if (ret < 0)
return ret;
/*
* The device generates interrupts as long as it is powered up.
* Some platforms might not allow the option to power it down so
* disable the interrupt to avoid extra load on the system
*/
disable_irq(spi->irq);
ret = devm_iio_trigger_register(&spi->dev, ad7766->trig);
if (ret)
return ret;
}
spi_set_drvdata(spi, indio_dev);
ad7766->spi = spi;
/* First byte always 0 */
ad7766->xfer.rx_buf = &ad7766->data[1];
ad7766->xfer.len = 3;
spi_message_init(&ad7766->msg);
spi_message_add_tail(&ad7766->xfer, &ad7766->msg);
ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev,
&iio_pollfunc_store_time, &ad7766_trigger_handler,
&ad7766_buffer_setup_ops);
if (ret)
return ret;
ret = devm_iio_device_register(&spi->dev, indio_dev);
if (ret)
return ret;
return 0;
}
static const struct spi_device_id ad7766_id[] = {
{"ad7766", ID_AD7766},
{"ad7766-1", ID_AD7766_1},
{"ad7766-2", ID_AD7766_2},
{"ad7767", ID_AD7766},
{"ad7767-1", ID_AD7766_1},
{"ad7767-2", ID_AD7766_2},
{}
};
MODULE_DEVICE_TABLE(spi, ad7766_id);
static struct spi_driver ad7766_driver = {
.driver = {
.name = "ad7766",
},
.probe = ad7766_probe,
.id_table = ad7766_id,
};
module_spi_driver(ad7766_driver);
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
MODULE_DESCRIPTION("Analog Devices AD7766 and AD7767 ADCs driver support");
MODULE_LICENSE("GPL v2");

Просмотреть файл

@ -30,6 +30,7 @@
#include <linux/iio/trigger.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/pinctrl/consumer.h>
/* Registers */
#define AT91_ADC_CR 0x00 /* Control Register */
@ -1347,6 +1348,32 @@ static int at91_adc_remove(struct platform_device *pdev)
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int at91_adc_suspend(struct device *dev)
{
struct iio_dev *idev = platform_get_drvdata(to_platform_device(dev));
struct at91_adc_state *st = iio_priv(idev);
pinctrl_pm_select_sleep_state(dev);
clk_disable_unprepare(st->clk);
return 0;
}
static int at91_adc_resume(struct device *dev)
{
struct iio_dev *idev = platform_get_drvdata(to_platform_device(dev));
struct at91_adc_state *st = iio_priv(idev);
clk_prepare_enable(st->clk);
pinctrl_pm_select_default_state(dev);
return 0;
}
#endif
static SIMPLE_DEV_PM_OPS(at91_adc_pm_ops, at91_adc_suspend, at91_adc_resume);
static struct at91_adc_caps at91sam9260_caps = {
.calc_startup_ticks = calc_startup_ticks_9260,
.num_channels = 4,
@ -1441,6 +1468,7 @@ static struct platform_driver at91_adc_driver = {
.driver = {
.name = DRIVER_NAME,
.of_match_table = of_match_ptr(at91_adc_dt_ids),
.pm = &at91_adc_pm_ops,
},
};

Просмотреть файл

@ -0,0 +1,422 @@
/*
* Driver for an envelope detector using a DAC and a comparator
*
* Copyright (C) 2016 Axentia Technologies AB
*
* Author: Peter Rosin <peda@axentia.se>
*
* 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.
*/
/*
* The DAC is used to find the peak level of an alternating voltage input
* signal by a binary search using the output of a comparator wired to
* an interrupt pin. Like so:
* _
* | \
* input +------>-------|+ \
* | \
* .-------. | }---.
* | | | / |
* | dac|-->--|- / |
* | | |_/ |
* | | |
* | | |
* | irq|------<-------'
* | |
* '-------'
*/
#include <linux/completion.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/iio/consumer.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/spinlock.h>
#include <linux/workqueue.h>
struct envelope {
spinlock_t comp_lock; /* protects comp */
int comp;
struct mutex read_lock; /* protects everything else */
int comp_irq;
u32 comp_irq_trigger;
u32 comp_irq_trigger_inv;
struct iio_channel *dac;
struct delayed_work comp_timeout;
unsigned int comp_interval;
bool invert;
u32 dac_max;
int high;
int level;
int low;
struct completion done;
};
/*
* The envelope_detector_comp_latch function works together with the compare
* interrupt service routine below (envelope_detector_comp_isr) as a latch
* (one-bit memory) for if the interrupt has triggered since last calling
* this function.
* The ..._comp_isr function disables the interrupt so that the cpu does not
* need to service a possible interrupt flood from the comparator when no-one
* cares anyway, and this ..._comp_latch function reenables them again if
* needed.
*/
static int envelope_detector_comp_latch(struct envelope *env)
{
int comp;
spin_lock_irq(&env->comp_lock);
comp = env->comp;
env->comp = 0;
spin_unlock_irq(&env->comp_lock);
if (!comp)
return 0;
/*
* The irq was disabled, and is reenabled just now.
* But there might have been a pending irq that
* happened while the irq was disabled that fires
* just as the irq is reenabled. That is not what
* is desired.
*/
enable_irq(env->comp_irq);
/* So, synchronize this possibly pending irq... */
synchronize_irq(env->comp_irq);
/* ...and redo the whole dance. */
spin_lock_irq(&env->comp_lock);
comp = env->comp;
env->comp = 0;
spin_unlock_irq(&env->comp_lock);
if (comp)
enable_irq(env->comp_irq);
return 1;
}
static irqreturn_t envelope_detector_comp_isr(int irq, void *ctx)
{
struct envelope *env = ctx;
spin_lock(&env->comp_lock);
env->comp = 1;
disable_irq_nosync(env->comp_irq);
spin_unlock(&env->comp_lock);
return IRQ_HANDLED;
}
static void envelope_detector_setup_compare(struct envelope *env)
{
int ret;
/*
* Do a binary search for the peak input level, and stop
* when that level is "trapped" between two adjacent DAC
* values.
* When invert is active, use the midpoint floor so that
* env->level ends up as env->low when the termination
* criteria below is fulfilled, and use the midpoint
* ceiling when invert is not active so that env->level
* ends up as env->high in that case.
*/
env->level = (env->high + env->low + !env->invert) / 2;
if (env->high == env->low + 1) {
complete(&env->done);
return;
}
/* Set a "safe" DAC level (if there is such a thing)... */
ret = iio_write_channel_raw(env->dac, env->invert ? 0 : env->dac_max);
if (ret < 0)
goto err;
/* ...clear the comparison result... */
envelope_detector_comp_latch(env);
/* ...set the real DAC level... */
ret = iio_write_channel_raw(env->dac, env->level);
if (ret < 0)
goto err;
/* ...and wait for a bit to see if the latch catches anything. */
schedule_delayed_work(&env->comp_timeout,
msecs_to_jiffies(env->comp_interval));
return;
err:
env->level = ret;
complete(&env->done);
}
static void envelope_detector_timeout(struct work_struct *work)
{
struct envelope *env = container_of(work, struct envelope,
comp_timeout.work);
/* Adjust low/high depending on the latch content... */
if (!envelope_detector_comp_latch(env) ^ !env->invert)
env->low = env->level;
else
env->high = env->level;
/* ...and continue the search. */
envelope_detector_setup_compare(env);
}
static int envelope_detector_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct envelope *env = iio_priv(indio_dev);
int ret;
switch (mask) {
case IIO_CHAN_INFO_RAW:
/*
* When invert is active, start with high=max+1 and low=0
* since we will end up with the low value when the
* termination criteria is fulfilled (rounding down). And
* start with high=max and low=-1 when invert is not active
* since we will end up with the high value in that case.
* This ensures that the returned value in both cases are
* in the same range as the DAC and is a value that has not
* triggered the comparator.
*/
mutex_lock(&env->read_lock);
env->high = env->dac_max + env->invert;
env->low = -1 + env->invert;
envelope_detector_setup_compare(env);
wait_for_completion(&env->done);
if (env->level < 0) {
ret = env->level;
goto err_unlock;
}
*val = env->invert ? env->dac_max - env->level : env->level;
mutex_unlock(&env->read_lock);
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
return iio_read_channel_scale(env->dac, val, val2);
}
return -EINVAL;
err_unlock:
mutex_unlock(&env->read_lock);
return ret;
}
static ssize_t envelope_show_invert(struct iio_dev *indio_dev,
uintptr_t private,
struct iio_chan_spec const *ch, char *buf)
{
struct envelope *env = iio_priv(indio_dev);
return sprintf(buf, "%u\n", env->invert);
}
static ssize_t envelope_store_invert(struct iio_dev *indio_dev,
uintptr_t private,
struct iio_chan_spec const *ch,
const char *buf, size_t len)
{
struct envelope *env = iio_priv(indio_dev);
unsigned long invert;
int ret;
u32 trigger;
ret = kstrtoul(buf, 0, &invert);
if (ret < 0)
return ret;
if (invert > 1)
return -EINVAL;
trigger = invert ? env->comp_irq_trigger_inv : env->comp_irq_trigger;
mutex_lock(&env->read_lock);
if (invert != env->invert)
ret = irq_set_irq_type(env->comp_irq, trigger);
if (!ret) {
env->invert = invert;
ret = len;
}
mutex_unlock(&env->read_lock);
return ret;
}
static ssize_t envelope_show_comp_interval(struct iio_dev *indio_dev,
uintptr_t private,
struct iio_chan_spec const *ch,
char *buf)
{
struct envelope *env = iio_priv(indio_dev);
return sprintf(buf, "%u\n", env->comp_interval);
}
static ssize_t envelope_store_comp_interval(struct iio_dev *indio_dev,
uintptr_t private,
struct iio_chan_spec const *ch,
const char *buf, size_t len)
{
struct envelope *env = iio_priv(indio_dev);
unsigned long interval;
int ret;
ret = kstrtoul(buf, 0, &interval);
if (ret < 0)
return ret;
if (interval > 1000)
return -EINVAL;
mutex_lock(&env->read_lock);
env->comp_interval = interval;
mutex_unlock(&env->read_lock);
return len;
}
static const struct iio_chan_spec_ext_info envelope_detector_ext_info[] = {
{ .name = "invert",
.read = envelope_show_invert,
.write = envelope_store_invert, },
{ .name = "compare_interval",
.read = envelope_show_comp_interval,
.write = envelope_store_comp_interval, },
{ /* sentinel */ }
};
static const struct iio_chan_spec envelope_detector_iio_channel = {
.type = IIO_ALTVOLTAGE,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW)
| BIT(IIO_CHAN_INFO_SCALE),
.ext_info = envelope_detector_ext_info,
.indexed = 1,
};
static const struct iio_info envelope_detector_info = {
.read_raw = &envelope_detector_read_raw,
.driver_module = THIS_MODULE,
};
static int envelope_detector_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct iio_dev *indio_dev;
struct envelope *env;
enum iio_chan_type type;
int ret;
indio_dev = devm_iio_device_alloc(dev, sizeof(*env));
if (!indio_dev)
return -ENOMEM;
platform_set_drvdata(pdev, indio_dev);
env = iio_priv(indio_dev);
env->comp_interval = 50; /* some sensible default? */
spin_lock_init(&env->comp_lock);
mutex_init(&env->read_lock);
init_completion(&env->done);
INIT_DELAYED_WORK(&env->comp_timeout, envelope_detector_timeout);
indio_dev->name = dev_name(dev);
indio_dev->dev.parent = dev;
indio_dev->dev.of_node = dev->of_node;
indio_dev->info = &envelope_detector_info;
indio_dev->channels = &envelope_detector_iio_channel;
indio_dev->num_channels = 1;
env->dac = devm_iio_channel_get(dev, "dac");
if (IS_ERR(env->dac)) {
if (PTR_ERR(env->dac) != -EPROBE_DEFER)
dev_err(dev, "failed to get dac input channel\n");
return PTR_ERR(env->dac);
}
env->comp_irq = platform_get_irq_byname(pdev, "comp");
if (env->comp_irq < 0) {
if (env->comp_irq != -EPROBE_DEFER)
dev_err(dev, "failed to get compare interrupt\n");
return env->comp_irq;
}
ret = devm_request_irq(dev, env->comp_irq, envelope_detector_comp_isr,
0, "envelope-detector", env);
if (ret) {
if (ret != -EPROBE_DEFER)
dev_err(dev, "failed to request interrupt\n");
return ret;
}
env->comp_irq_trigger = irq_get_trigger_type(env->comp_irq);
if (env->comp_irq_trigger & IRQF_TRIGGER_RISING)
env->comp_irq_trigger_inv |= IRQF_TRIGGER_FALLING;
if (env->comp_irq_trigger & IRQF_TRIGGER_FALLING)
env->comp_irq_trigger_inv |= IRQF_TRIGGER_RISING;
if (env->comp_irq_trigger & IRQF_TRIGGER_HIGH)
env->comp_irq_trigger_inv |= IRQF_TRIGGER_LOW;
if (env->comp_irq_trigger & IRQF_TRIGGER_LOW)
env->comp_irq_trigger_inv |= IRQF_TRIGGER_HIGH;
ret = iio_get_channel_type(env->dac, &type);
if (ret < 0)
return ret;
if (type != IIO_VOLTAGE) {
dev_err(dev, "dac is of the wrong type\n");
return -EINVAL;
}
ret = iio_read_max_channel_raw(env->dac, &env->dac_max);
if (ret < 0) {
dev_err(dev, "dac does not indicate its raw maximum value\n");
return ret;
}
return devm_iio_device_register(dev, indio_dev);
}
static const struct of_device_id envelope_detector_match[] = {
{ .compatible = "axentia,tse850-envelope-detector", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, envelope_detector_match);
static struct platform_driver envelope_detector_driver = {
.probe = envelope_detector_probe,
.driver = {
.name = "iio-envelope-detector",
.of_match_table = envelope_detector_match,
},
};
module_platform_driver(envelope_detector_driver);
MODULE_DESCRIPTION("Envelope detector using a DAC and a comparator");
MODULE_AUTHOR("Peter Rosin <peda@axentia.se>");
MODULE_LICENSE("GPL v2");

Просмотреть файл

@ -238,7 +238,9 @@ static int max1027_read_single_value(struct iio_dev *indio_dev,
/* Configure conversion register with the requested chan */
st->reg = MAX1027_CONV_REG | MAX1027_CHAN(chan->channel) |
MAX1027_NOSCAN | !!(chan->type == IIO_TEMP);
MAX1027_NOSCAN;
if (chan->type == IIO_TEMP)
st->reg |= MAX1027_TEMP;
ret = spi_write(st->spi, &st->reg, 1);
if (ret < 0) {
dev_err(&indio_dev->dev,
@ -360,17 +362,6 @@ static int max1027_set_trigger_state(struct iio_trigger *trig, bool state)
return 0;
}
static int max1027_validate_device(struct iio_trigger *trig,
struct iio_dev *indio_dev)
{
struct iio_dev *indio = iio_trigger_get_drvdata(trig);
if (indio != indio_dev)
return -EINVAL;
return 0;
}
static irqreturn_t max1027_trigger_handler(int irq, void *private)
{
struct iio_poll_func *pf = (struct iio_poll_func *)private;
@ -391,7 +382,7 @@ static irqreturn_t max1027_trigger_handler(int irq, void *private)
static const struct iio_trigger_ops max1027_trigger_ops = {
.owner = THIS_MODULE,
.validate_device = &max1027_validate_device,
.validate_device = &iio_trigger_validate_own_device,
.set_trigger_state = &max1027_set_trigger_state,
};

Просмотреть файл

@ -0,0 +1,303 @@
/*
* This file is part of STM32 ADC driver
*
* Copyright (C) 2016, STMicroelectronics - All Rights Reserved
* Author: Fabrice Gasnier <fabrice.gasnier@st.com>.
*
* Inspired from: fsl-imx25-tsadc
*
* License type: GPLv2
*
* 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 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.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/clk.h>
#include <linux/interrupt.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/irqdesc.h>
#include <linux/irqdomain.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include "stm32-adc-core.h"
/* STM32F4 - common registers for all ADC instances: 1, 2 & 3 */
#define STM32F4_ADC_CSR (STM32_ADCX_COMN_OFFSET + 0x00)
#define STM32F4_ADC_CCR (STM32_ADCX_COMN_OFFSET + 0x04)
/* STM32F4_ADC_CSR - bit fields */
#define STM32F4_EOC3 BIT(17)
#define STM32F4_EOC2 BIT(9)
#define STM32F4_EOC1 BIT(1)
/* STM32F4_ADC_CCR - bit fields */
#define STM32F4_ADC_ADCPRE_SHIFT 16
#define STM32F4_ADC_ADCPRE_MASK GENMASK(17, 16)
/* STM32 F4 maximum analog clock rate (from datasheet) */
#define STM32F4_ADC_MAX_CLK_RATE 36000000
/**
* struct stm32_adc_priv - stm32 ADC core private data
* @irq: irq for ADC block
* @domain: irq domain reference
* @aclk: clock reference for the analog circuitry
* @vref: regulator reference
* @common: common data for all ADC instances
*/
struct stm32_adc_priv {
int irq;
struct irq_domain *domain;
struct clk *aclk;
struct regulator *vref;
struct stm32_adc_common common;
};
static struct stm32_adc_priv *to_stm32_adc_priv(struct stm32_adc_common *com)
{
return container_of(com, struct stm32_adc_priv, common);
}
/* STM32F4 ADC internal common clock prescaler division ratios */
static int stm32f4_pclk_div[] = {2, 4, 6, 8};
/**
* stm32f4_adc_clk_sel() - Select stm32f4 ADC common clock prescaler
* @priv: stm32 ADC core private data
* Select clock prescaler used for analog conversions, before using ADC.
*/
static int stm32f4_adc_clk_sel(struct platform_device *pdev,
struct stm32_adc_priv *priv)
{
unsigned long rate;
u32 val;
int i;
rate = clk_get_rate(priv->aclk);
for (i = 0; i < ARRAY_SIZE(stm32f4_pclk_div); i++) {
if ((rate / stm32f4_pclk_div[i]) <= STM32F4_ADC_MAX_CLK_RATE)
break;
}
if (i >= ARRAY_SIZE(stm32f4_pclk_div))
return -EINVAL;
val = readl_relaxed(priv->common.base + STM32F4_ADC_CCR);
val &= ~STM32F4_ADC_ADCPRE_MASK;
val |= i << STM32F4_ADC_ADCPRE_SHIFT;
writel_relaxed(val, priv->common.base + STM32F4_ADC_CCR);
dev_dbg(&pdev->dev, "Using analog clock source at %ld kHz\n",
rate / (stm32f4_pclk_div[i] * 1000));
return 0;
}
/* ADC common interrupt for all instances */
static void stm32_adc_irq_handler(struct irq_desc *desc)
{
struct stm32_adc_priv *priv = irq_desc_get_handler_data(desc);
struct irq_chip *chip = irq_desc_get_chip(desc);
u32 status;
chained_irq_enter(chip, desc);
status = readl_relaxed(priv->common.base + STM32F4_ADC_CSR);
if (status & STM32F4_EOC1)
generic_handle_irq(irq_find_mapping(priv->domain, 0));
if (status & STM32F4_EOC2)
generic_handle_irq(irq_find_mapping(priv->domain, 1));
if (status & STM32F4_EOC3)
generic_handle_irq(irq_find_mapping(priv->domain, 2));
chained_irq_exit(chip, desc);
};
static int stm32_adc_domain_map(struct irq_domain *d, unsigned int irq,
irq_hw_number_t hwirq)
{
irq_set_chip_data(irq, d->host_data);
irq_set_chip_and_handler(irq, &dummy_irq_chip, handle_level_irq);
return 0;
}
static void stm32_adc_domain_unmap(struct irq_domain *d, unsigned int irq)
{
irq_set_chip_and_handler(irq, NULL, NULL);
irq_set_chip_data(irq, NULL);
}
static const struct irq_domain_ops stm32_adc_domain_ops = {
.map = stm32_adc_domain_map,
.unmap = stm32_adc_domain_unmap,
.xlate = irq_domain_xlate_onecell,
};
static int stm32_adc_irq_probe(struct platform_device *pdev,
struct stm32_adc_priv *priv)
{
struct device_node *np = pdev->dev.of_node;
priv->irq = platform_get_irq(pdev, 0);
if (priv->irq < 0) {
dev_err(&pdev->dev, "failed to get irq\n");
return priv->irq;
}
priv->domain = irq_domain_add_simple(np, STM32_ADC_MAX_ADCS, 0,
&stm32_adc_domain_ops,
priv);
if (!priv->domain) {
dev_err(&pdev->dev, "Failed to add irq domain\n");
return -ENOMEM;
}
irq_set_chained_handler(priv->irq, stm32_adc_irq_handler);
irq_set_handler_data(priv->irq, priv);
return 0;
}
static void stm32_adc_irq_remove(struct platform_device *pdev,
struct stm32_adc_priv *priv)
{
int hwirq;
for (hwirq = 0; hwirq < STM32_ADC_MAX_ADCS; hwirq++)
irq_dispose_mapping(irq_find_mapping(priv->domain, hwirq));
irq_domain_remove(priv->domain);
irq_set_chained_handler(priv->irq, NULL);
}
static int stm32_adc_probe(struct platform_device *pdev)
{
struct stm32_adc_priv *priv;
struct device_node *np = pdev->dev.of_node;
struct resource *res;
int ret;
if (!pdev->dev.of_node)
return -ENODEV;
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
priv->common.base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(priv->common.base))
return PTR_ERR(priv->common.base);
priv->vref = devm_regulator_get(&pdev->dev, "vref");
if (IS_ERR(priv->vref)) {
ret = PTR_ERR(priv->vref);
dev_err(&pdev->dev, "vref get failed, %d\n", ret);
return ret;
}
ret = regulator_enable(priv->vref);
if (ret < 0) {
dev_err(&pdev->dev, "vref enable failed\n");
return ret;
}
ret = regulator_get_voltage(priv->vref);
if (ret < 0) {
dev_err(&pdev->dev, "vref get voltage failed, %d\n", ret);
goto err_regulator_disable;
}
priv->common.vref_mv = ret / 1000;
dev_dbg(&pdev->dev, "vref+=%dmV\n", priv->common.vref_mv);
priv->aclk = devm_clk_get(&pdev->dev, "adc");
if (IS_ERR(priv->aclk)) {
ret = PTR_ERR(priv->aclk);
dev_err(&pdev->dev, "Can't get 'adc' clock\n");
goto err_regulator_disable;
}
ret = clk_prepare_enable(priv->aclk);
if (ret < 0) {
dev_err(&pdev->dev, "adc clk enable failed\n");
goto err_regulator_disable;
}
ret = stm32f4_adc_clk_sel(pdev, priv);
if (ret < 0) {
dev_err(&pdev->dev, "adc clk selection failed\n");
goto err_clk_disable;
}
ret = stm32_adc_irq_probe(pdev, priv);
if (ret < 0)
goto err_clk_disable;
platform_set_drvdata(pdev, &priv->common);
ret = of_platform_populate(np, NULL, NULL, &pdev->dev);
if (ret < 0) {
dev_err(&pdev->dev, "failed to populate DT children\n");
goto err_irq_remove;
}
return 0;
err_irq_remove:
stm32_adc_irq_remove(pdev, priv);
err_clk_disable:
clk_disable_unprepare(priv->aclk);
err_regulator_disable:
regulator_disable(priv->vref);
return ret;
}
static int stm32_adc_remove(struct platform_device *pdev)
{
struct stm32_adc_common *common = platform_get_drvdata(pdev);
struct stm32_adc_priv *priv = to_stm32_adc_priv(common);
of_platform_depopulate(&pdev->dev);
stm32_adc_irq_remove(pdev, priv);
clk_disable_unprepare(priv->aclk);
regulator_disable(priv->vref);
return 0;
}
static const struct of_device_id stm32_adc_of_match[] = {
{ .compatible = "st,stm32f4-adc-core" },
{},
};
MODULE_DEVICE_TABLE(of, stm32_adc_of_match);
static struct platform_driver stm32_adc_driver = {
.probe = stm32_adc_probe,
.remove = stm32_adc_remove,
.driver = {
.name = "stm32-adc-core",
.of_match_table = stm32_adc_of_match,
},
};
module_platform_driver(stm32_adc_driver);
MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier@st.com>");
MODULE_DESCRIPTION("STMicroelectronics STM32 ADC core driver");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:stm32-adc-core");

Просмотреть файл

@ -0,0 +1,52 @@
/*
* This file is part of STM32 ADC driver
*
* Copyright (C) 2016, STMicroelectronics - All Rights Reserved
* Author: Fabrice Gasnier <fabrice.gasnier@st.com>.
*
* License type: GPLv2
*
* 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 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.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __STM32_ADC_H
#define __STM32_ADC_H
/*
* STM32 - ADC global register map
* ________________________________________________________
* | Offset | Register |
* --------------------------------------------------------
* | 0x000 | Master ADC1 |
* --------------------------------------------------------
* | 0x100 | Slave ADC2 |
* --------------------------------------------------------
* | 0x200 | Slave ADC3 |
* --------------------------------------------------------
* | 0x300 | Master & Slave common regs |
* --------------------------------------------------------
*/
#define STM32_ADC_MAX_ADCS 3
#define STM32_ADCX_COMN_OFFSET 0x300
/**
* struct stm32_adc_common - stm32 ADC driver common data (for all instances)
* @base: control registers base cpu addr
* @vref_mv: vref voltage (mv)
*/
struct stm32_adc_common {
void __iomem *base;
int vref_mv;
};
#endif

518
drivers/iio/adc/stm32-adc.c Normal file
Просмотреть файл

@ -0,0 +1,518 @@
/*
* This file is part of STM32 ADC driver
*
* Copyright (C) 2016, STMicroelectronics - All Rights Reserved
* Author: Fabrice Gasnier <fabrice.gasnier@st.com>.
*
* License type: GPLv2
*
* 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 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.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/iio/iio.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include "stm32-adc-core.h"
/* STM32F4 - Registers for each ADC instance */
#define STM32F4_ADC_SR 0x00
#define STM32F4_ADC_CR1 0x04
#define STM32F4_ADC_CR2 0x08
#define STM32F4_ADC_SMPR1 0x0C
#define STM32F4_ADC_SMPR2 0x10
#define STM32F4_ADC_HTR 0x24
#define STM32F4_ADC_LTR 0x28
#define STM32F4_ADC_SQR1 0x2C
#define STM32F4_ADC_SQR2 0x30
#define STM32F4_ADC_SQR3 0x34
#define STM32F4_ADC_JSQR 0x38
#define STM32F4_ADC_JDR1 0x3C
#define STM32F4_ADC_JDR2 0x40
#define STM32F4_ADC_JDR3 0x44
#define STM32F4_ADC_JDR4 0x48
#define STM32F4_ADC_DR 0x4C
/* STM32F4_ADC_SR - bit fields */
#define STM32F4_STRT BIT(4)
#define STM32F4_EOC BIT(1)
/* STM32F4_ADC_CR1 - bit fields */
#define STM32F4_SCAN BIT(8)
#define STM32F4_EOCIE BIT(5)
/* STM32F4_ADC_CR2 - bit fields */
#define STM32F4_SWSTART BIT(30)
#define STM32F4_EXTEN_MASK GENMASK(29, 28)
#define STM32F4_EOCS BIT(10)
#define STM32F4_ADON BIT(0)
/* STM32F4_ADC_SQR1 - bit fields */
#define STM32F4_L_SHIFT 20
#define STM32F4_L_MASK GENMASK(23, 20)
/* STM32F4_ADC_SQR3 - bit fields */
#define STM32F4_SQ1_SHIFT 0
#define STM32F4_SQ1_MASK GENMASK(4, 0)
#define STM32_ADC_TIMEOUT_US 100000
#define STM32_ADC_TIMEOUT (msecs_to_jiffies(STM32_ADC_TIMEOUT_US / 1000))
/**
* struct stm32_adc - private data of each ADC IIO instance
* @common: reference to ADC block common data
* @offset: ADC instance register offset in ADC block
* @completion: end of single conversion completion
* @buffer: data buffer
* @clk: clock for this adc instance
* @irq: interrupt for this adc instance
* @lock: spinlock
*/
struct stm32_adc {
struct stm32_adc_common *common;
u32 offset;
struct completion completion;
u16 *buffer;
struct clk *clk;
int irq;
spinlock_t lock; /* interrupt lock */
};
/**
* struct stm32_adc_chan_spec - specification of stm32 adc channel
* @type: IIO channel type
* @channel: channel number (single ended)
* @name: channel name (single ended)
*/
struct stm32_adc_chan_spec {
enum iio_chan_type type;
int channel;
const char *name;
};
/* Input definitions common for all STM32F4 instances */
static const struct stm32_adc_chan_spec stm32f4_adc123_channels[] = {
{ IIO_VOLTAGE, 0, "in0" },
{ IIO_VOLTAGE, 1, "in1" },
{ IIO_VOLTAGE, 2, "in2" },
{ IIO_VOLTAGE, 3, "in3" },
{ IIO_VOLTAGE, 4, "in4" },
{ IIO_VOLTAGE, 5, "in5" },
{ IIO_VOLTAGE, 6, "in6" },
{ IIO_VOLTAGE, 7, "in7" },
{ IIO_VOLTAGE, 8, "in8" },
{ IIO_VOLTAGE, 9, "in9" },
{ IIO_VOLTAGE, 10, "in10" },
{ IIO_VOLTAGE, 11, "in11" },
{ IIO_VOLTAGE, 12, "in12" },
{ IIO_VOLTAGE, 13, "in13" },
{ IIO_VOLTAGE, 14, "in14" },
{ IIO_VOLTAGE, 15, "in15" },
};
/**
* STM32 ADC registers access routines
* @adc: stm32 adc instance
* @reg: reg offset in adc instance
*
* Note: All instances share same base, with 0x0, 0x100 or 0x200 offset resp.
* for adc1, adc2 and adc3.
*/
static u32 stm32_adc_readl(struct stm32_adc *adc, u32 reg)
{
return readl_relaxed(adc->common->base + adc->offset + reg);
}
static u16 stm32_adc_readw(struct stm32_adc *adc, u32 reg)
{
return readw_relaxed(adc->common->base + adc->offset + reg);
}
static void stm32_adc_writel(struct stm32_adc *adc, u32 reg, u32 val)
{
writel_relaxed(val, adc->common->base + adc->offset + reg);
}
static void stm32_adc_set_bits(struct stm32_adc *adc, u32 reg, u32 bits)
{
unsigned long flags;
spin_lock_irqsave(&adc->lock, flags);
stm32_adc_writel(adc, reg, stm32_adc_readl(adc, reg) | bits);
spin_unlock_irqrestore(&adc->lock, flags);
}
static void stm32_adc_clr_bits(struct stm32_adc *adc, u32 reg, u32 bits)
{
unsigned long flags;
spin_lock_irqsave(&adc->lock, flags);
stm32_adc_writel(adc, reg, stm32_adc_readl(adc, reg) & ~bits);
spin_unlock_irqrestore(&adc->lock, flags);
}
/**
* stm32_adc_conv_irq_enable() - Enable end of conversion interrupt
* @adc: stm32 adc instance
*/
static void stm32_adc_conv_irq_enable(struct stm32_adc *adc)
{
stm32_adc_set_bits(adc, STM32F4_ADC_CR1, STM32F4_EOCIE);
};
/**
* stm32_adc_conv_irq_disable() - Disable end of conversion interrupt
* @adc: stm32 adc instance
*/
static void stm32_adc_conv_irq_disable(struct stm32_adc *adc)
{
stm32_adc_clr_bits(adc, STM32F4_ADC_CR1, STM32F4_EOCIE);
}
/**
* stm32_adc_start_conv() - Start conversions for regular channels.
* @adc: stm32 adc instance
*/
static void stm32_adc_start_conv(struct stm32_adc *adc)
{
stm32_adc_set_bits(adc, STM32F4_ADC_CR1, STM32F4_SCAN);
stm32_adc_set_bits(adc, STM32F4_ADC_CR2, STM32F4_EOCS | STM32F4_ADON);
/* Wait for Power-up time (tSTAB from datasheet) */
usleep_range(2, 3);
/* Software start ? (e.g. trigger detection disabled ?) */
if (!(stm32_adc_readl(adc, STM32F4_ADC_CR2) & STM32F4_EXTEN_MASK))
stm32_adc_set_bits(adc, STM32F4_ADC_CR2, STM32F4_SWSTART);
}
static void stm32_adc_stop_conv(struct stm32_adc *adc)
{
stm32_adc_clr_bits(adc, STM32F4_ADC_CR2, STM32F4_EXTEN_MASK);
stm32_adc_clr_bits(adc, STM32F4_ADC_SR, STM32F4_STRT);
stm32_adc_clr_bits(adc, STM32F4_ADC_CR1, STM32F4_SCAN);
stm32_adc_clr_bits(adc, STM32F4_ADC_CR2, STM32F4_ADON);
}
/**
* stm32_adc_single_conv() - Performs a single conversion
* @indio_dev: IIO device
* @chan: IIO channel
* @res: conversion result
*
* The function performs a single conversion on a given channel:
* - Program sequencer with one channel (e.g. in SQ1 with len = 1)
* - Use SW trigger
* - Start conversion, then wait for interrupt completion.
*/
static int stm32_adc_single_conv(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
int *res)
{
struct stm32_adc *adc = iio_priv(indio_dev);
long timeout;
u32 val;
u16 result;
int ret;
reinit_completion(&adc->completion);
adc->buffer = &result;
/* Program chan number in regular sequence */
val = stm32_adc_readl(adc, STM32F4_ADC_SQR3);
val &= ~STM32F4_SQ1_MASK;
val |= chan->channel << STM32F4_SQ1_SHIFT;
stm32_adc_writel(adc, STM32F4_ADC_SQR3, val);
/* Set regular sequence len (0 for 1 conversion) */
stm32_adc_clr_bits(adc, STM32F4_ADC_SQR1, STM32F4_L_MASK);
/* Trigger detection disabled (conversion can be launched in SW) */
stm32_adc_clr_bits(adc, STM32F4_ADC_CR2, STM32F4_EXTEN_MASK);
stm32_adc_conv_irq_enable(adc);
stm32_adc_start_conv(adc);
timeout = wait_for_completion_interruptible_timeout(
&adc->completion, STM32_ADC_TIMEOUT);
if (timeout == 0) {
ret = -ETIMEDOUT;
} else if (timeout < 0) {
ret = timeout;
} else {
*res = result;
ret = IIO_VAL_INT;
}
stm32_adc_stop_conv(adc);
stm32_adc_conv_irq_disable(adc);
return ret;
}
static int stm32_adc_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct stm32_adc *adc = iio_priv(indio_dev);
int ret;
switch (mask) {
case IIO_CHAN_INFO_RAW:
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
if (chan->type == IIO_VOLTAGE)
ret = stm32_adc_single_conv(indio_dev, chan, val);
else
ret = -EINVAL;
iio_device_release_direct_mode(indio_dev);
return ret;
case IIO_CHAN_INFO_SCALE:
*val = adc->common->vref_mv;
*val2 = chan->scan_type.realbits;
return IIO_VAL_FRACTIONAL_LOG2;
default:
return -EINVAL;
}
}
static irqreturn_t stm32_adc_isr(int irq, void *data)
{
struct stm32_adc *adc = data;
u32 status = stm32_adc_readl(adc, STM32F4_ADC_SR);
if (status & STM32F4_EOC) {
*adc->buffer = stm32_adc_readw(adc, STM32F4_ADC_DR);
complete(&adc->completion);
return IRQ_HANDLED;
}
return IRQ_NONE;
}
static int stm32_adc_of_xlate(struct iio_dev *indio_dev,
const struct of_phandle_args *iiospec)
{
int i;
for (i = 0; i < indio_dev->num_channels; i++)
if (indio_dev->channels[i].channel == iiospec->args[0])
return i;
return -EINVAL;
}
/**
* stm32_adc_debugfs_reg_access - read or write register value
*
* To read a value from an ADC register:
* echo [ADC reg offset] > direct_reg_access
* cat direct_reg_access
*
* To write a value in a ADC register:
* echo [ADC_reg_offset] [value] > direct_reg_access
*/
static int stm32_adc_debugfs_reg_access(struct iio_dev *indio_dev,
unsigned reg, unsigned writeval,
unsigned *readval)
{
struct stm32_adc *adc = iio_priv(indio_dev);
if (!readval)
stm32_adc_writel(adc, reg, writeval);
else
*readval = stm32_adc_readl(adc, reg);
return 0;
}
static const struct iio_info stm32_adc_iio_info = {
.read_raw = stm32_adc_read_raw,
.debugfs_reg_access = stm32_adc_debugfs_reg_access,
.of_xlate = stm32_adc_of_xlate,
.driver_module = THIS_MODULE,
};
static void stm32_adc_chan_init_one(struct iio_dev *indio_dev,
struct iio_chan_spec *chan,
const struct stm32_adc_chan_spec *channel,
int scan_index)
{
chan->type = channel->type;
chan->channel = channel->channel;
chan->datasheet_name = channel->name;
chan->scan_index = scan_index;
chan->indexed = 1;
chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE);
chan->scan_type.sign = 'u';
chan->scan_type.realbits = 12;
chan->scan_type.storagebits = 16;
}
static int stm32_adc_chan_of_init(struct iio_dev *indio_dev)
{
struct device_node *node = indio_dev->dev.of_node;
struct property *prop;
const __be32 *cur;
struct iio_chan_spec *channels;
int scan_index = 0, num_channels;
u32 val;
num_channels = of_property_count_u32_elems(node, "st,adc-channels");
if (num_channels < 0 ||
num_channels >= ARRAY_SIZE(stm32f4_adc123_channels)) {
dev_err(&indio_dev->dev, "Bad st,adc-channels?\n");
return num_channels < 0 ? num_channels : -EINVAL;
}
channels = devm_kcalloc(&indio_dev->dev, num_channels,
sizeof(struct iio_chan_spec), GFP_KERNEL);
if (!channels)
return -ENOMEM;
of_property_for_each_u32(node, "st,adc-channels", prop, cur, val) {
if (val >= ARRAY_SIZE(stm32f4_adc123_channels)) {
dev_err(&indio_dev->dev, "Invalid channel %d\n", val);
return -EINVAL;
}
stm32_adc_chan_init_one(indio_dev, &channels[scan_index],
&stm32f4_adc123_channels[val],
scan_index);
scan_index++;
}
indio_dev->num_channels = scan_index;
indio_dev->channels = channels;
return 0;
}
static int stm32_adc_probe(struct platform_device *pdev)
{
struct iio_dev *indio_dev;
struct stm32_adc *adc;
int ret;
if (!pdev->dev.of_node)
return -ENODEV;
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc));
if (!indio_dev)
return -ENOMEM;
adc = iio_priv(indio_dev);
adc->common = dev_get_drvdata(pdev->dev.parent);
spin_lock_init(&adc->lock);
init_completion(&adc->completion);
indio_dev->name = dev_name(&pdev->dev);
indio_dev->dev.parent = &pdev->dev;
indio_dev->dev.of_node = pdev->dev.of_node;
indio_dev->info = &stm32_adc_iio_info;
indio_dev->modes = INDIO_DIRECT_MODE;
platform_set_drvdata(pdev, adc);
ret = of_property_read_u32(pdev->dev.of_node, "reg", &adc->offset);
if (ret != 0) {
dev_err(&pdev->dev, "missing reg property\n");
return -EINVAL;
}
adc->irq = platform_get_irq(pdev, 0);
if (adc->irq < 0) {
dev_err(&pdev->dev, "failed to get irq\n");
return adc->irq;
}
ret = devm_request_irq(&pdev->dev, adc->irq, stm32_adc_isr,
0, pdev->name, adc);
if (ret) {
dev_err(&pdev->dev, "failed to request IRQ\n");
return ret;
}
adc->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(adc->clk)) {
dev_err(&pdev->dev, "Can't get clock\n");
return PTR_ERR(adc->clk);
}
ret = clk_prepare_enable(adc->clk);
if (ret < 0) {
dev_err(&pdev->dev, "clk enable failed\n");
return ret;
}
ret = stm32_adc_chan_of_init(indio_dev);
if (ret < 0)
goto err_clk_disable;
ret = iio_device_register(indio_dev);
if (ret) {
dev_err(&pdev->dev, "iio dev register failed\n");
goto err_clk_disable;
}
return 0;
err_clk_disable:
clk_disable_unprepare(adc->clk);
return ret;
}
static int stm32_adc_remove(struct platform_device *pdev)
{
struct stm32_adc *adc = platform_get_drvdata(pdev);
struct iio_dev *indio_dev = iio_priv_to_dev(adc);
iio_device_unregister(indio_dev);
clk_disable_unprepare(adc->clk);
return 0;
}
static const struct of_device_id stm32_adc_of_match[] = {
{ .compatible = "st,stm32f4-adc" },
{},
};
MODULE_DEVICE_TABLE(of, stm32_adc_of_match);
static struct platform_driver stm32_adc_driver = {
.probe = stm32_adc_probe,
.remove = stm32_adc_remove,
.driver = {
.name = "stm32-adc",
.of_match_table = stm32_adc_of_match,
},
};
module_platform_driver(stm32_adc_driver);
MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier@st.com>");
MODULE_DESCRIPTION("STMicroelectronics STM32 ADC IIO driver");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:stm32-adc");

Просмотреть файл

@ -14,6 +14,10 @@
#include <linux/spi/spi.h>
#include <linux/iio/iio.h>
#include <linux/regulator/consumer.h>
#include <linux/iio/buffer.h>
#include <linux/iio/trigger.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/iio/trigger_consumer.h>
enum {
adc0831,
@ -38,10 +42,16 @@ struct adc0832 {
.indexed = 1, \
.channel = chan, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
.scan_index = chan, \
.scan_type = { \
.sign = 'u', \
.realbits = 8, \
.storagebits = 8, \
}, \
}
#define ADC0832_VOLTAGE_CHANNEL_DIFF(chan1, chan2) \
#define ADC0832_VOLTAGE_CHANNEL_DIFF(chan1, chan2, si) \
{ \
.type = IIO_VOLTAGE, \
.indexed = 1, \
@ -49,18 +59,26 @@ struct adc0832 {
.channel2 = (chan2), \
.differential = 1, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
.scan_index = si, \
.scan_type = { \
.sign = 'u', \
.realbits = 8, \
.storagebits = 8, \
}, \
}
static const struct iio_chan_spec adc0831_channels[] = {
ADC0832_VOLTAGE_CHANNEL_DIFF(0, 1),
ADC0832_VOLTAGE_CHANNEL_DIFF(0, 1, 0),
IIO_CHAN_SOFT_TIMESTAMP(1),
};
static const struct iio_chan_spec adc0832_channels[] = {
ADC0832_VOLTAGE_CHANNEL(0),
ADC0832_VOLTAGE_CHANNEL(1),
ADC0832_VOLTAGE_CHANNEL_DIFF(0, 1),
ADC0832_VOLTAGE_CHANNEL_DIFF(1, 0),
ADC0832_VOLTAGE_CHANNEL_DIFF(0, 1, 2),
ADC0832_VOLTAGE_CHANNEL_DIFF(1, 0, 3),
IIO_CHAN_SOFT_TIMESTAMP(4),
};
static const struct iio_chan_spec adc0834_channels[] = {
@ -68,10 +86,11 @@ static const struct iio_chan_spec adc0834_channels[] = {
ADC0832_VOLTAGE_CHANNEL(1),
ADC0832_VOLTAGE_CHANNEL(2),
ADC0832_VOLTAGE_CHANNEL(3),
ADC0832_VOLTAGE_CHANNEL_DIFF(0, 1),
ADC0832_VOLTAGE_CHANNEL_DIFF(1, 0),
ADC0832_VOLTAGE_CHANNEL_DIFF(2, 3),
ADC0832_VOLTAGE_CHANNEL_DIFF(3, 2),
ADC0832_VOLTAGE_CHANNEL_DIFF(0, 1, 4),
ADC0832_VOLTAGE_CHANNEL_DIFF(1, 0, 5),
ADC0832_VOLTAGE_CHANNEL_DIFF(2, 3, 6),
ADC0832_VOLTAGE_CHANNEL_DIFF(3, 2, 7),
IIO_CHAN_SOFT_TIMESTAMP(8),
};
static const struct iio_chan_spec adc0838_channels[] = {
@ -83,14 +102,15 @@ static const struct iio_chan_spec adc0838_channels[] = {
ADC0832_VOLTAGE_CHANNEL(5),
ADC0832_VOLTAGE_CHANNEL(6),
ADC0832_VOLTAGE_CHANNEL(7),
ADC0832_VOLTAGE_CHANNEL_DIFF(0, 1),
ADC0832_VOLTAGE_CHANNEL_DIFF(1, 0),
ADC0832_VOLTAGE_CHANNEL_DIFF(2, 3),
ADC0832_VOLTAGE_CHANNEL_DIFF(3, 2),
ADC0832_VOLTAGE_CHANNEL_DIFF(4, 5),
ADC0832_VOLTAGE_CHANNEL_DIFF(5, 4),
ADC0832_VOLTAGE_CHANNEL_DIFF(6, 7),
ADC0832_VOLTAGE_CHANNEL_DIFF(7, 6),
ADC0832_VOLTAGE_CHANNEL_DIFF(0, 1, 8),
ADC0832_VOLTAGE_CHANNEL_DIFF(1, 0, 9),
ADC0832_VOLTAGE_CHANNEL_DIFF(2, 3, 10),
ADC0832_VOLTAGE_CHANNEL_DIFF(3, 2, 11),
ADC0832_VOLTAGE_CHANNEL_DIFF(4, 5, 12),
ADC0832_VOLTAGE_CHANNEL_DIFF(5, 4, 13),
ADC0832_VOLTAGE_CHANNEL_DIFF(6, 7, 14),
ADC0832_VOLTAGE_CHANNEL_DIFF(7, 6, 15),
IIO_CHAN_SOFT_TIMESTAMP(16),
};
static int adc0831_adc_conversion(struct adc0832 *adc)
@ -178,6 +198,42 @@ static const struct iio_info adc0832_info = {
.driver_module = THIS_MODULE,
};
static irqreturn_t adc0832_trigger_handler(int irq, void *p)
{
struct iio_poll_func *pf = p;
struct iio_dev *indio_dev = pf->indio_dev;
struct adc0832 *adc = iio_priv(indio_dev);
u8 data[24] = { }; /* 16x 1 byte ADC data + 8 bytes timestamp */
int scan_index;
int i = 0;
mutex_lock(&adc->lock);
for_each_set_bit(scan_index, indio_dev->active_scan_mask,
indio_dev->masklength) {
const struct iio_chan_spec *scan_chan =
&indio_dev->channels[scan_index];
int ret = adc0832_adc_conversion(adc, scan_chan->channel,
scan_chan->differential);
if (ret < 0) {
dev_warn(&adc->spi->dev,
"failed to get conversion data\n");
goto out;
}
data[i] = ret;
i++;
}
iio_push_to_buffers_with_timestamp(indio_dev, data,
iio_get_time_ns(indio_dev));
out:
mutex_unlock(&adc->lock);
iio_trigger_notify_done(indio_dev->trig);
return IRQ_HANDLED;
}
static int adc0832_probe(struct spi_device *spi)
{
struct iio_dev *indio_dev;
@ -233,9 +289,20 @@ static int adc0832_probe(struct spi_device *spi)
spi_set_drvdata(spi, indio_dev);
ret = iio_triggered_buffer_setup(indio_dev, NULL,
adc0832_trigger_handler, NULL);
if (ret)
goto err_reg_disable;
ret = iio_device_register(indio_dev);
if (ret)
regulator_disable(adc->reg);
goto err_buffer_cleanup;
return 0;
err_buffer_cleanup:
iio_triggered_buffer_cleanup(indio_dev);
err_reg_disable:
regulator_disable(adc->reg);
return ret;
}
@ -246,6 +313,7 @@ static int adc0832_remove(struct spi_device *spi)
struct adc0832 *adc = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
iio_triggered_buffer_cleanup(indio_dev);
regulator_disable(adc->reg);
return 0;

Просмотреть файл

@ -27,6 +27,7 @@
#include <linux/iio/buffer.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/regulator/consumer.h>
#define TI_ADC_DRV_NAME "ti-adc161s626"
@ -39,7 +40,9 @@ static const struct iio_chan_spec ti_adc141s626_channels[] = {
{
.type = IIO_VOLTAGE,
.channel = 0,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_OFFSET),
.scan_index = 0,
.scan_type = {
.sign = 's',
@ -54,7 +57,9 @@ static const struct iio_chan_spec ti_adc161s626_channels[] = {
{
.type = IIO_VOLTAGE,
.channel = 0,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_OFFSET),
.scan_index = 0,
.scan_type = {
.sign = 's',
@ -68,6 +73,8 @@ static const struct iio_chan_spec ti_adc161s626_channels[] = {
struct ti_adc_data {
struct iio_dev *indio_dev;
struct spi_device *spi;
struct regulator *ref;
u8 read_size;
u8 shift;
@ -135,18 +142,32 @@ static int ti_adc_read_raw(struct iio_dev *indio_dev,
struct ti_adc_data *data = iio_priv(indio_dev);
int ret;
if (mask != IIO_CHAN_INFO_RAW)
return -EINVAL;
switch (mask) {
case IIO_CHAN_INFO_RAW:
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
ret = ti_adc_read_measurement(data, chan, val);
iio_device_release_direct_mode(indio_dev);
ret = ti_adc_read_measurement(data, chan, val);
iio_device_release_direct_mode(indio_dev);
if (ret)
return ret;
if (!ret)
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
ret = regulator_get_voltage(data->ref);
if (ret < 0)
return ret;
*val = ret / 1000;
*val2 = chan->scan_type.realbits;
return IIO_VAL_FRACTIONAL_LOG2;
case IIO_CHAN_INFO_OFFSET:
*val = 1 << (chan->scan_type.realbits - 1);
return IIO_VAL_INT;
}
return 0;
}
@ -191,10 +212,17 @@ static int ti_adc_probe(struct spi_device *spi)
break;
}
data->ref = devm_regulator_get(&spi->dev, "vdda");
if (!IS_ERR(data->ref)) {
ret = regulator_enable(data->ref);
if (ret < 0)
return ret;
}
ret = iio_triggered_buffer_setup(indio_dev, NULL,
ti_adc_trigger_handler, NULL);
if (ret)
return ret;
goto error_regulator_disable;
ret = iio_device_register(indio_dev);
if (ret)
@ -205,15 +233,20 @@ static int ti_adc_probe(struct spi_device *spi)
error_unreg_buffer:
iio_triggered_buffer_cleanup(indio_dev);
error_regulator_disable:
regulator_disable(data->ref);
return ret;
}
static int ti_adc_remove(struct spi_device *spi)
{
struct iio_dev *indio_dev = spi_get_drvdata(spi);
struct ti_adc_data *data = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
iio_triggered_buffer_cleanup(indio_dev);
regulator_disable(data->ref);
return 0;
}

Просмотреть файл

@ -30,10 +30,28 @@
#include <linux/iio/buffer.h>
#include <linux/iio/kfifo_buf.h>
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#define DMA_BUFFER_SIZE SZ_2K
struct tiadc_dma {
struct dma_slave_config conf;
struct dma_chan *chan;
dma_addr_t addr;
dma_cookie_t cookie;
u8 *buf;
int current_period;
int period_size;
u8 fifo_thresh;
};
struct tiadc_device {
struct ti_tscadc_dev *mfd_tscadc;
struct tiadc_dma dma;
struct mutex fifo1_lock; /* to protect fifo access */
int channels;
int total_ch_enabled;
u8 channel_line[8];
u8 channel_step[8];
int buffer_en_ch_steps;
@ -198,6 +216,67 @@ static irqreturn_t tiadc_worker_h(int irq, void *private)
return IRQ_HANDLED;
}
static void tiadc_dma_rx_complete(void *param)
{
struct iio_dev *indio_dev = param;
struct tiadc_device *adc_dev = iio_priv(indio_dev);
struct tiadc_dma *dma = &adc_dev->dma;
u8 *data;
int i;
data = dma->buf + dma->current_period * dma->period_size;
dma->current_period = 1 - dma->current_period; /* swap the buffer ID */
for (i = 0; i < dma->period_size; i += indio_dev->scan_bytes) {
iio_push_to_buffers(indio_dev, data);
data += indio_dev->scan_bytes;
}
}
static int tiadc_start_dma(struct iio_dev *indio_dev)
{
struct tiadc_device *adc_dev = iio_priv(indio_dev);
struct tiadc_dma *dma = &adc_dev->dma;
struct dma_async_tx_descriptor *desc;
dma->current_period = 0; /* We start to fill period 0 */
/*
* Make the fifo thresh as the multiple of total number of
* channels enabled, so make sure that cyclic DMA period
* length is also a multiple of total number of channels
* enabled. This ensures that no invalid data is reported
* to the stack via iio_push_to_buffers().
*/
dma->fifo_thresh = rounddown(FIFO1_THRESHOLD + 1,
adc_dev->total_ch_enabled) - 1;
/* Make sure that period length is multiple of fifo thresh level */
dma->period_size = rounddown(DMA_BUFFER_SIZE / 2,
(dma->fifo_thresh + 1) * sizeof(u16));
dma->conf.src_maxburst = dma->fifo_thresh + 1;
dmaengine_slave_config(dma->chan, &dma->conf);
desc = dmaengine_prep_dma_cyclic(dma->chan, dma->addr,
dma->period_size * 2,
dma->period_size, DMA_DEV_TO_MEM,
DMA_PREP_INTERRUPT);
if (!desc)
return -EBUSY;
desc->callback = tiadc_dma_rx_complete;
desc->callback_param = indio_dev;
dma->cookie = dmaengine_submit(desc);
dma_async_issue_pending(dma->chan);
tiadc_writel(adc_dev, REG_FIFO1THR, dma->fifo_thresh);
tiadc_writel(adc_dev, REG_DMA1REQ, dma->fifo_thresh);
tiadc_writel(adc_dev, REG_DMAENABLE_SET, DMA_FIFO1);
return 0;
}
static int tiadc_buffer_preenable(struct iio_dev *indio_dev)
{
struct tiadc_device *adc_dev = iio_priv(indio_dev);
@ -218,20 +297,30 @@ static int tiadc_buffer_preenable(struct iio_dev *indio_dev)
static int tiadc_buffer_postenable(struct iio_dev *indio_dev)
{
struct tiadc_device *adc_dev = iio_priv(indio_dev);
struct tiadc_dma *dma = &adc_dev->dma;
unsigned int irq_enable;
unsigned int enb = 0;
u8 bit;
tiadc_step_config(indio_dev);
for_each_set_bit(bit, indio_dev->active_scan_mask, adc_dev->channels)
for_each_set_bit(bit, indio_dev->active_scan_mask, adc_dev->channels) {
enb |= (get_adc_step_bit(adc_dev, bit) << 1);
adc_dev->total_ch_enabled++;
}
adc_dev->buffer_en_ch_steps = enb;
if (dma->chan)
tiadc_start_dma(indio_dev);
am335x_tsc_se_set_cache(adc_dev->mfd_tscadc, enb);
tiadc_writel(adc_dev, REG_IRQSTATUS, IRQENB_FIFO1THRES
| IRQENB_FIFO1OVRRUN | IRQENB_FIFO1UNDRFLW);
tiadc_writel(adc_dev, REG_IRQENABLE, IRQENB_FIFO1THRES
| IRQENB_FIFO1OVRRUN);
irq_enable = IRQENB_FIFO1OVRRUN;
if (!dma->chan)
irq_enable |= IRQENB_FIFO1THRES;
tiadc_writel(adc_dev, REG_IRQENABLE, irq_enable);
return 0;
}
@ -239,12 +328,18 @@ static int tiadc_buffer_postenable(struct iio_dev *indio_dev)
static int tiadc_buffer_predisable(struct iio_dev *indio_dev)
{
struct tiadc_device *adc_dev = iio_priv(indio_dev);
struct tiadc_dma *dma = &adc_dev->dma;
int fifo1count, i, read;
tiadc_writel(adc_dev, REG_IRQCLR, (IRQENB_FIFO1THRES |
IRQENB_FIFO1OVRRUN | IRQENB_FIFO1UNDRFLW));
am335x_tsc_se_clr(adc_dev->mfd_tscadc, adc_dev->buffer_en_ch_steps);
adc_dev->buffer_en_ch_steps = 0;
adc_dev->total_ch_enabled = 0;
if (dma->chan) {
tiadc_writel(adc_dev, REG_DMAENABLE_CLEAR, 0x2);
dmaengine_terminate_async(dma->chan);
}
/* Flush FIFO of leftover data in the time it takes to disable adc */
fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT);
@ -430,6 +525,41 @@ static const struct iio_info tiadc_info = {
.driver_module = THIS_MODULE,
};
static int tiadc_request_dma(struct platform_device *pdev,
struct tiadc_device *adc_dev)
{
struct tiadc_dma *dma = &adc_dev->dma;
dma_cap_mask_t mask;
/* Default slave configuration parameters */
dma->conf.direction = DMA_DEV_TO_MEM;
dma->conf.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
dma->conf.src_addr = adc_dev->mfd_tscadc->tscadc_phys_base + REG_FIFO1;
dma_cap_zero(mask);
dma_cap_set(DMA_CYCLIC, mask);
/* Get a channel for RX */
dma->chan = dma_request_chan(adc_dev->mfd_tscadc->dev, "fifo1");
if (IS_ERR(dma->chan)) {
int ret = PTR_ERR(dma->chan);
dma->chan = NULL;
return ret;
}
/* RX buffer */
dma->buf = dma_alloc_coherent(dma->chan->device->dev, DMA_BUFFER_SIZE,
&dma->addr, GFP_KERNEL);
if (!dma->buf)
goto err;
return 0;
err:
dma_release_channel(dma->chan);
return -ENOMEM;
}
static int tiadc_parse_dt(struct platform_device *pdev,
struct tiadc_device *adc_dev)
{
@ -512,8 +642,14 @@ static int tiadc_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, indio_dev);
err = tiadc_request_dma(pdev, adc_dev);
if (err && err == -EPROBE_DEFER)
goto err_dma;
return 0;
err_dma:
iio_device_unregister(indio_dev);
err_buffer_unregister:
tiadc_iio_buffered_hardware_remove(indio_dev);
err_free_channels:
@ -525,8 +661,14 @@ static int tiadc_remove(struct platform_device *pdev)
{
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
struct tiadc_device *adc_dev = iio_priv(indio_dev);
struct tiadc_dma *dma = &adc_dev->dma;
u32 step_en;
if (dma->chan) {
dma_free_coherent(dma->chan->device->dev, DMA_BUFFER_SIZE,
dma->buf, dma->addr);
dma_release_channel(dma->chan);
}
iio_device_unregister(indio_dev);
tiadc_iio_buffered_hardware_remove(indio_dev);
tiadc_channels_remove(indio_dev);

Просмотреть файл

@ -2,6 +2,7 @@
# IIO common modules
#
source "drivers/iio/common/cros_ec_sensors/Kconfig"
source "drivers/iio/common/hid-sensors/Kconfig"
source "drivers/iio/common/ms_sensors/Kconfig"
source "drivers/iio/common/ssp_sensors/Kconfig"

Просмотреть файл

@ -7,6 +7,7 @@
#
# When adding new entries keep the list in alphabetical order
obj-y += cros_ec_sensors/
obj-y += hid-sensors/
obj-y += ms_sensors/
obj-y += ssp_sensors/

Просмотреть файл

@ -0,0 +1,22 @@
#
# Chrome OS Embedded Controller managed sensors library
#
config IIO_CROS_EC_SENSORS_CORE
tristate "ChromeOS EC Sensors Core"
depends on SYSFS && MFD_CROS_EC
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
help
Base module for the ChromeOS EC Sensors module.
Contains core functions used by other IIO CrosEC sensor
drivers.
Define common attributes and sysfs interrupt handler.
config IIO_CROS_EC_SENSORS
tristate "ChromeOS EC Contiguous Sensors"
depends on IIO_CROS_EC_SENSORS_CORE
help
Module to handle 3d contiguous sensors like
Accelerometers, Gyroscope and Magnetometer that are
presented by the ChromeOS EC Sensor hub.
Creates an IIO device for each functions.

Просмотреть файл

@ -0,0 +1,6 @@
#
# Makefile for sensors seen through the ChromeOS EC sensor hub.
#
obj-$(CONFIG_IIO_CROS_EC_SENSORS_CORE) += cros_ec_sensors_core.o
obj-$(CONFIG_IIO_CROS_EC_SENSORS) += cros_ec_sensors.o

Просмотреть файл

@ -0,0 +1,322 @@
/*
* cros_ec_sensors - Driver for Chrome OS Embedded Controller sensors.
*
* Copyright (C) 2016 Google, Inc
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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.
*
* This driver uses the cros-ec interface to communicate with the Chrome OS
* EC about sensors data. Data access is presented through iio sysfs.
*/
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/iio/buffer.h>
#include <linux/iio/iio.h>
#include <linux/iio/kfifo_buf.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/kernel.h>
#include <linux/mfd/cros_ec.h>
#include <linux/mfd/cros_ec_commands.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include "cros_ec_sensors_core.h"
#define CROS_EC_SENSORS_MAX_CHANNELS 4
/* State data for ec_sensors iio driver. */
struct cros_ec_sensors_state {
/* Shared by all sensors */
struct cros_ec_sensors_core_state core;
struct iio_chan_spec channels[CROS_EC_SENSORS_MAX_CHANNELS];
};
static int cros_ec_sensors_read(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct cros_ec_sensors_state *st = iio_priv(indio_dev);
s16 data = 0;
s64 val64;
int i;
int ret;
int idx = chan->scan_index;
mutex_lock(&st->core.cmd_lock);
switch (mask) {
case IIO_CHAN_INFO_RAW:
ret = st->core.read_ec_sensors_data(indio_dev, 1 << idx, &data);
if (ret < 0)
break;
*val = data;
break;
case IIO_CHAN_INFO_CALIBBIAS:
st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_OFFSET;
st->core.param.sensor_offset.flags = 0;
ret = cros_ec_motion_send_host_cmd(&st->core, 0);
if (ret < 0)
break;
/* Save values */
for (i = CROS_EC_SENSOR_X; i < CROS_EC_SENSOR_MAX_AXIS; i++)
st->core.calib[i] =
st->core.resp->sensor_offset.offset[i];
*val = st->core.calib[idx];
break;
case IIO_CHAN_INFO_SCALE:
st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_RANGE;
st->core.param.sensor_range.data = EC_MOTION_SENSE_NO_VALUE;
ret = cros_ec_motion_send_host_cmd(&st->core, 0);
if (ret < 0)
break;
val64 = st->core.resp->sensor_range.ret;
switch (st->core.type) {
case MOTIONSENSE_TYPE_ACCEL:
/*
* EC returns data in g, iio exepects m/s^2.
* Do not use IIO_G_TO_M_S_2 to avoid precision loss.
*/
*val = div_s64(val64 * 980665, 10);
*val2 = 10000 << (CROS_EC_SENSOR_BITS - 1);
ret = IIO_VAL_FRACTIONAL;
break;
case MOTIONSENSE_TYPE_GYRO:
/*
* EC returns data in dps, iio expects rad/s.
* Do not use IIO_DEGREE_TO_RAD to avoid precision
* loss. Round to the nearest integer.
*/
*val = div_s64(val64 * 314159 + 9000000ULL, 1000);
*val2 = 18000 << (CROS_EC_SENSOR_BITS - 1);
ret = IIO_VAL_FRACTIONAL;
break;
case MOTIONSENSE_TYPE_MAG:
/*
* EC returns data in 16LSB / uT,
* iio expects Gauss
*/
*val = val64;
*val2 = 100 << (CROS_EC_SENSOR_BITS - 1);
ret = IIO_VAL_FRACTIONAL;
break;
default:
ret = -EINVAL;
}
break;
default:
ret = cros_ec_sensors_core_read(&st->core, chan, val, val2,
mask);
break;
}
mutex_unlock(&st->core.cmd_lock);
return ret;
}
static int cros_ec_sensors_write(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
struct cros_ec_sensors_state *st = iio_priv(indio_dev);
int i;
int ret;
int idx = chan->scan_index;
mutex_lock(&st->core.cmd_lock);
switch (mask) {
case IIO_CHAN_INFO_CALIBBIAS:
st->core.calib[idx] = val;
/* Send to EC for each axis, even if not complete */
st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_OFFSET;
st->core.param.sensor_offset.flags =
MOTION_SENSE_SET_OFFSET;
for (i = CROS_EC_SENSOR_X; i < CROS_EC_SENSOR_MAX_AXIS; i++)
st->core.param.sensor_offset.offset[i] =
st->core.calib[i];
st->core.param.sensor_offset.temp =
EC_MOTION_SENSE_INVALID_CALIB_TEMP;
ret = cros_ec_motion_send_host_cmd(&st->core, 0);
break;
case IIO_CHAN_INFO_SCALE:
if (st->core.type == MOTIONSENSE_TYPE_MAG) {
ret = -EINVAL;
break;
}
st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_RANGE;
st->core.param.sensor_range.data = val;
/* Always roundup, so caller gets at least what it asks for. */
st->core.param.sensor_range.roundup = 1;
ret = cros_ec_motion_send_host_cmd(&st->core, 0);
break;
default:
ret = cros_ec_sensors_core_write(
&st->core, chan, val, val2, mask);
break;
}
mutex_unlock(&st->core.cmd_lock);
return ret;
}
static const struct iio_info ec_sensors_info = {
.read_raw = &cros_ec_sensors_read,
.write_raw = &cros_ec_sensors_write,
.driver_module = THIS_MODULE,
};
static int cros_ec_sensors_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct cros_ec_dev *ec_dev = dev_get_drvdata(dev->parent);
struct cros_ec_device *ec_device;
struct iio_dev *indio_dev;
struct cros_ec_sensors_state *state;
struct iio_chan_spec *channel;
int ret, i;
if (!ec_dev || !ec_dev->ec_dev) {
dev_warn(&pdev->dev, "No CROS EC device found.\n");
return -EINVAL;
}
ec_device = ec_dev->ec_dev;
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*state));
if (!indio_dev)
return -ENOMEM;
ret = cros_ec_sensors_core_init(pdev, indio_dev, true);
if (ret)
return ret;
indio_dev->info = &ec_sensors_info;
state = iio_priv(indio_dev);
for (channel = state->channels, i = CROS_EC_SENSOR_X;
i < CROS_EC_SENSOR_MAX_AXIS; i++, channel++) {
/* Common part */
channel->info_mask_separate =
BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_CALIBBIAS);
channel->info_mask_shared_by_all =
BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_FREQUENCY) |
BIT(IIO_CHAN_INFO_SAMP_FREQ);
channel->scan_type.realbits = CROS_EC_SENSOR_BITS;
channel->scan_type.storagebits = CROS_EC_SENSOR_BITS;
channel->scan_index = i;
channel->ext_info = cros_ec_sensors_ext_info;
channel->modified = 1;
channel->channel2 = IIO_MOD_X + i;
channel->scan_type.sign = 's';
/* Sensor specific */
switch (state->core.type) {
case MOTIONSENSE_TYPE_ACCEL:
channel->type = IIO_ACCEL;
break;
case MOTIONSENSE_TYPE_GYRO:
channel->type = IIO_ANGL_VEL;
break;
case MOTIONSENSE_TYPE_MAG:
channel->type = IIO_MAGN;
break;
default:
dev_err(&pdev->dev, "Unknown motion sensor\n");
return -EINVAL;
}
}
/* Timestamp */
channel->type = IIO_TIMESTAMP;
channel->channel = -1;
channel->scan_index = CROS_EC_SENSOR_MAX_AXIS;
channel->scan_type.sign = 's';
channel->scan_type.realbits = 64;
channel->scan_type.storagebits = 64;
indio_dev->channels = state->channels;
indio_dev->num_channels = CROS_EC_SENSORS_MAX_CHANNELS;
/* There is only enough room for accel and gyro in the io space */
if ((state->core.ec->cmd_readmem != NULL) &&
(state->core.type != MOTIONSENSE_TYPE_MAG))
state->core.read_ec_sensors_data = cros_ec_sensors_read_lpc;
else
state->core.read_ec_sensors_data = cros_ec_sensors_read_cmd;
ret = iio_triggered_buffer_setup(indio_dev, NULL,
cros_ec_sensors_capture, NULL);
if (ret)
return ret;
ret = iio_device_register(indio_dev);
if (ret)
goto error_uninit_buffer;
return 0;
error_uninit_buffer:
iio_triggered_buffer_cleanup(indio_dev);
return ret;
}
static int cros_ec_sensors_remove(struct platform_device *pdev)
{
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
iio_device_unregister(indio_dev);
iio_triggered_buffer_cleanup(indio_dev);
return 0;
}
static const struct platform_device_id cros_ec_sensors_ids[] = {
{
.name = "cros-ec-accel",
},
{
.name = "cros-ec-gyro",
},
{
.name = "cros-ec-mag",
},
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(platform, cros_ec_sensors_ids);
static struct platform_driver cros_ec_sensors_platform_driver = {
.driver = {
.name = "cros-ec-sensors",
},
.probe = cros_ec_sensors_probe,
.remove = cros_ec_sensors_remove,
.id_table = cros_ec_sensors_ids,
};
module_platform_driver(cros_ec_sensors_platform_driver);
MODULE_DESCRIPTION("ChromeOS EC 3-axis sensors driver");
MODULE_LICENSE("GPL v2");

Просмотреть файл

@ -0,0 +1,450 @@
/*
* cros_ec_sensors_core - Common function for Chrome OS EC sensor driver.
*
* Copyright (C) 2016 Google, Inc
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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/device.h>
#include <linux/iio/buffer.h>
#include <linux/iio/iio.h>
#include <linux/iio/kfifo_buf.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/kernel.h>
#include <linux/mfd/cros_ec.h>
#include <linux/mfd/cros_ec_commands.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include <linux/platform_device.h>
#include "cros_ec_sensors_core.h"
static char *cros_ec_loc[] = {
[MOTIONSENSE_LOC_BASE] = "base",
[MOTIONSENSE_LOC_LID] = "lid",
[MOTIONSENSE_LOC_MAX] = "unknown",
};
int cros_ec_sensors_core_init(struct platform_device *pdev,
struct iio_dev *indio_dev,
bool physical_device)
{
struct device *dev = &pdev->dev;
struct cros_ec_sensors_core_state *state = iio_priv(indio_dev);
struct cros_ec_dev *ec = dev_get_drvdata(pdev->dev.parent);
struct cros_ec_sensor_platform *sensor_platform = dev_get_platdata(dev);
platform_set_drvdata(pdev, indio_dev);
state->ec = ec->ec_dev;
state->msg = devm_kzalloc(&pdev->dev,
max((u16)sizeof(struct ec_params_motion_sense),
state->ec->max_response), GFP_KERNEL);
if (!state->msg)
return -ENOMEM;
state->resp = (struct ec_response_motion_sense *)state->msg->data;
mutex_init(&state->cmd_lock);
/* Set up the host command structure. */
state->msg->version = 2;
state->msg->command = EC_CMD_MOTION_SENSE_CMD + ec->cmd_offset;
state->msg->outsize = sizeof(struct ec_params_motion_sense);
indio_dev->dev.parent = &pdev->dev;
indio_dev->name = pdev->name;
if (physical_device) {
indio_dev->modes = INDIO_DIRECT_MODE;
state->param.cmd = MOTIONSENSE_CMD_INFO;
state->param.info.sensor_num = sensor_platform->sensor_num;
if (cros_ec_motion_send_host_cmd(state, 0)) {
dev_warn(dev, "Can not access sensor info\n");
return -EIO;
}
state->type = state->resp->info.type;
state->loc = state->resp->info.location;
}
return 0;
}
EXPORT_SYMBOL_GPL(cros_ec_sensors_core_init);
int cros_ec_motion_send_host_cmd(struct cros_ec_sensors_core_state *state,
u16 opt_length)
{
int ret;
if (opt_length)
state->msg->insize = min(opt_length, state->ec->max_response);
else
state->msg->insize = state->ec->max_response;
memcpy(state->msg->data, &state->param, sizeof(state->param));
ret = cros_ec_cmd_xfer_status(state->ec, state->msg);
if (ret < 0)
return -EIO;
if (ret &&
state->resp != (struct ec_response_motion_sense *)state->msg->data)
memcpy(state->resp, state->msg->data, ret);
return 0;
}
EXPORT_SYMBOL_GPL(cros_ec_motion_send_host_cmd);
static ssize_t cros_ec_sensors_calibrate(struct iio_dev *indio_dev,
uintptr_t private, const struct iio_chan_spec *chan,
const char *buf, size_t len)
{
struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
int ret, i;
bool calibrate;
ret = strtobool(buf, &calibrate);
if (ret < 0)
return ret;
if (!calibrate)
return -EINVAL;
mutex_lock(&st->cmd_lock);
st->param.cmd = MOTIONSENSE_CMD_PERFORM_CALIB;
ret = cros_ec_motion_send_host_cmd(st, 0);
if (ret != 0) {
dev_warn(&indio_dev->dev, "Unable to calibrate sensor\n");
} else {
/* Save values */
for (i = CROS_EC_SENSOR_X; i < CROS_EC_SENSOR_MAX_AXIS; i++)
st->calib[i] = st->resp->perform_calib.offset[i];
}
mutex_unlock(&st->cmd_lock);
return ret ? ret : len;
}
static ssize_t cros_ec_sensors_loc(struct iio_dev *indio_dev,
uintptr_t private, const struct iio_chan_spec *chan,
char *buf)
{
struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
return snprintf(buf, PAGE_SIZE, "%s\n", cros_ec_loc[st->loc]);
}
const struct iio_chan_spec_ext_info cros_ec_sensors_ext_info[] = {
{
.name = "calibrate",
.shared = IIO_SHARED_BY_ALL,
.write = cros_ec_sensors_calibrate
},
{
.name = "location",
.shared = IIO_SHARED_BY_ALL,
.read = cros_ec_sensors_loc
},
{ },
};
EXPORT_SYMBOL_GPL(cros_ec_sensors_ext_info);
/**
* cros_ec_sensors_idx_to_reg - convert index into offset in shared memory
* @st: pointer to state information for device
* @idx: sensor index (should be element of enum sensor_index)
*
* Return: address to read at
*/
static unsigned int cros_ec_sensors_idx_to_reg(
struct cros_ec_sensors_core_state *st,
unsigned int idx)
{
/*
* When using LPC interface, only space for 2 Accel and one Gyro.
* First halfword of MOTIONSENSE_TYPE_ACCEL is used by angle.
*/
if (st->type == MOTIONSENSE_TYPE_ACCEL)
return EC_MEMMAP_ACC_DATA + sizeof(u16) *
(1 + idx + st->param.info.sensor_num *
CROS_EC_SENSOR_MAX_AXIS);
return EC_MEMMAP_GYRO_DATA + sizeof(u16) * idx;
}
static int cros_ec_sensors_cmd_read_u8(struct cros_ec_device *ec,
unsigned int offset, u8 *dest)
{
return ec->cmd_readmem(ec, offset, 1, dest);
}
static int cros_ec_sensors_cmd_read_u16(struct cros_ec_device *ec,
unsigned int offset, u16 *dest)
{
__le16 tmp;
int ret = ec->cmd_readmem(ec, offset, 2, &tmp);
if (ret >= 0)
*dest = le16_to_cpu(tmp);
return ret;
}
/**
* cros_ec_sensors_read_until_not_busy() - read until is not busy
*
* @st: pointer to state information for device
*
* Read from EC status byte until it reads not busy.
* Return: 8-bit status if ok, -errno on failure.
*/
static int cros_ec_sensors_read_until_not_busy(
struct cros_ec_sensors_core_state *st)
{
struct cros_ec_device *ec = st->ec;
u8 status;
int ret, attempts = 0;
ret = cros_ec_sensors_cmd_read_u8(ec, EC_MEMMAP_ACC_STATUS, &status);
if (ret < 0)
return ret;
while (status & EC_MEMMAP_ACC_STATUS_BUSY_BIT) {
/* Give up after enough attempts, return error. */
if (attempts++ >= 50)
return -EIO;
/* Small delay every so often. */
if (attempts % 5 == 0)
msleep(25);
ret = cros_ec_sensors_cmd_read_u8(ec, EC_MEMMAP_ACC_STATUS,
&status);
if (ret < 0)
return ret;
}
return status;
}
/**
* read_ec_sensors_data_unsafe() - read acceleration data from EC shared memory
* @indio_dev: pointer to IIO device
* @scan_mask: bitmap of the sensor indices to scan
* @data: location to store data
*
* This is the unsafe function for reading the EC data. It does not guarantee
* that the EC will not modify the data as it is being read in.
*
* Return: 0 on success, -errno on failure.
*/
static int cros_ec_sensors_read_data_unsafe(struct iio_dev *indio_dev,
unsigned long scan_mask, s16 *data)
{
struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
struct cros_ec_device *ec = st->ec;
unsigned int i;
int ret;
/* Read all sensors enabled in scan_mask. Each value is 2 bytes. */
for_each_set_bit(i, &scan_mask, indio_dev->masklength) {
ret = cros_ec_sensors_cmd_read_u16(ec,
cros_ec_sensors_idx_to_reg(st, i),
data);
if (ret < 0)
return ret;
data++;
}
return 0;
}
int cros_ec_sensors_read_lpc(struct iio_dev *indio_dev,
unsigned long scan_mask, s16 *data)
{
struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
struct cros_ec_device *ec = st->ec;
u8 samp_id = 0xff, status = 0;
int ret, attempts = 0;
/*
* Continually read all data from EC until the status byte after
* all reads reflects that the EC is not busy and the sample id
* matches the sample id from before all reads. This guarantees
* that data read in was not modified by the EC while reading.
*/
while ((status & (EC_MEMMAP_ACC_STATUS_BUSY_BIT |
EC_MEMMAP_ACC_STATUS_SAMPLE_ID_MASK)) != samp_id) {
/* If we have tried to read too many times, return error. */
if (attempts++ >= 5)
return -EIO;
/* Read status byte until EC is not busy. */
ret = cros_ec_sensors_read_until_not_busy(st);
if (ret < 0)
return ret;
/*
* Store the current sample id so that we can compare to the
* sample id after reading the data.
*/
samp_id = ret & EC_MEMMAP_ACC_STATUS_SAMPLE_ID_MASK;
/* Read all EC data, format it, and store it into data. */
ret = cros_ec_sensors_read_data_unsafe(indio_dev, scan_mask,
data);
if (ret < 0)
return ret;
/* Read status byte. */
ret = cros_ec_sensors_cmd_read_u8(ec, EC_MEMMAP_ACC_STATUS,
&status);
if (ret < 0)
return ret;
}
return 0;
}
EXPORT_SYMBOL_GPL(cros_ec_sensors_read_lpc);
int cros_ec_sensors_read_cmd(struct iio_dev *indio_dev,
unsigned long scan_mask, s16 *data)
{
struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
int ret;
unsigned int i;
/* Read all sensor data through a command. */
st->param.cmd = MOTIONSENSE_CMD_DATA;
ret = cros_ec_motion_send_host_cmd(st, sizeof(st->resp->data));
if (ret != 0) {
dev_warn(&indio_dev->dev, "Unable to read sensor data\n");
return ret;
}
for_each_set_bit(i, &scan_mask, indio_dev->masklength) {
*data = st->resp->data.data[i];
data++;
}
return 0;
}
EXPORT_SYMBOL_GPL(cros_ec_sensors_read_cmd);
irqreturn_t cros_ec_sensors_capture(int irq, void *p)
{
struct iio_poll_func *pf = p;
struct iio_dev *indio_dev = pf->indio_dev;
struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
int ret;
mutex_lock(&st->cmd_lock);
/* Clear capture data. */
memset(st->samples, 0, indio_dev->scan_bytes);
/* Read data based on which channels are enabled in scan mask. */
ret = st->read_ec_sensors_data(indio_dev,
*(indio_dev->active_scan_mask),
(s16 *)st->samples);
if (ret < 0)
goto done;
iio_push_to_buffers_with_timestamp(indio_dev, st->samples,
iio_get_time_ns(indio_dev));
done:
/*
* Tell the core we are done with this trigger and ready for the
* next one.
*/
iio_trigger_notify_done(indio_dev->trig);
mutex_unlock(&st->cmd_lock);
return IRQ_HANDLED;
}
EXPORT_SYMBOL_GPL(cros_ec_sensors_capture);
int cros_ec_sensors_core_read(struct cros_ec_sensors_core_state *st,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
int ret = IIO_VAL_INT;
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
st->param.cmd = MOTIONSENSE_CMD_EC_RATE;
st->param.ec_rate.data =
EC_MOTION_SENSE_NO_VALUE;
if (cros_ec_motion_send_host_cmd(st, 0))
ret = -EIO;
else
*val = st->resp->ec_rate.ret;
break;
case IIO_CHAN_INFO_FREQUENCY:
st->param.cmd = MOTIONSENSE_CMD_SENSOR_ODR;
st->param.sensor_odr.data =
EC_MOTION_SENSE_NO_VALUE;
if (cros_ec_motion_send_host_cmd(st, 0))
ret = -EIO;
else
*val = st->resp->sensor_odr.ret;
break;
default:
break;
}
return ret;
}
EXPORT_SYMBOL_GPL(cros_ec_sensors_core_read);
int cros_ec_sensors_core_write(struct cros_ec_sensors_core_state *st,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
int ret = 0;
switch (mask) {
case IIO_CHAN_INFO_FREQUENCY:
st->param.cmd = MOTIONSENSE_CMD_SENSOR_ODR;
st->param.sensor_odr.data = val;
/* Always roundup, so caller gets at least what it asks for. */
st->param.sensor_odr.roundup = 1;
if (cros_ec_motion_send_host_cmd(st, 0))
ret = -EIO;
break;
case IIO_CHAN_INFO_SAMP_FREQ:
st->param.cmd = MOTIONSENSE_CMD_EC_RATE;
st->param.ec_rate.data = val;
if (cros_ec_motion_send_host_cmd(st, 0))
ret = -EIO;
else
st->curr_sampl_freq = val;
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
EXPORT_SYMBOL_GPL(cros_ec_sensors_core_write);
MODULE_DESCRIPTION("ChromeOS EC sensor hub core functions");
MODULE_LICENSE("GPL v2");

Просмотреть файл

@ -0,0 +1,175 @@
/*
* ChromeOS EC sensor hub
*
* Copyright (C) 2016 Google, Inc
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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.
*/
#ifndef __CROS_EC_SENSORS_CORE_H
#define __CROS_EC_SENSORS_CORE_H
#include <linux/irqreturn.h>
enum {
CROS_EC_SENSOR_X,
CROS_EC_SENSOR_Y,
CROS_EC_SENSOR_Z,
CROS_EC_SENSOR_MAX_AXIS,
};
/* EC returns sensor values using signed 16 bit registers */
#define CROS_EC_SENSOR_BITS 16
/*
* 4 16 bit channels are allowed.
* Good enough for current sensors, they use up to 3 16 bit vectors.
*/
#define CROS_EC_SAMPLE_SIZE (sizeof(s64) * 2)
/* Minimum sampling period to use when device is suspending */
#define CROS_EC_MIN_SUSPEND_SAMPLING_FREQUENCY 1000 /* 1 second */
/**
* struct cros_ec_sensors_core_state - state data for EC sensors IIO driver
* @ec: cros EC device structure
* @cmd_lock: lock used to prevent simultaneous access to the
* commands.
* @msg: cros EC command structure
* @param: motion sensor parameters structure
* @resp: motion sensor response structure
* @type: type of motion sensor
* @loc: location where the motion sensor is placed
* @calib: calibration parameters. Note that trigger
* captured data will always provide the calibrated
* data
* @samples: static array to hold data from a single capture.
* For each channel we need 2 bytes, except for
* the timestamp. The timestamp is always last and
* is always 8-byte aligned.
* @read_ec_sensors_data: function used for accessing sensors values
* @cuur_sampl_freq: current sampling period
*/
struct cros_ec_sensors_core_state {
struct cros_ec_device *ec;
struct mutex cmd_lock;
struct cros_ec_command *msg;
struct ec_params_motion_sense param;
struct ec_response_motion_sense *resp;
enum motionsensor_type type;
enum motionsensor_location loc;
s16 calib[CROS_EC_SENSOR_MAX_AXIS];
u8 samples[CROS_EC_SAMPLE_SIZE];
int (*read_ec_sensors_data)(struct iio_dev *indio_dev,
unsigned long scan_mask, s16 *data);
int curr_sampl_freq;
};
/**
* cros_ec_sensors_read_lpc() - retrieve data from EC shared memory
* @indio_dev: pointer to IIO device
* @scan_mask: bitmap of the sensor indices to scan
* @data: location to store data
*
* This is the safe function for reading the EC data. It guarantees that the
* data sampled was not modified by the EC while being read.
*
* Return: 0 on success, -errno on failure.
*/
int cros_ec_sensors_read_lpc(struct iio_dev *indio_dev, unsigned long scan_mask,
s16 *data);
/**
* cros_ec_sensors_read_cmd() - retrieve data using the EC command protocol
* @indio_dev: pointer to IIO device
* @scan_mask: bitmap of the sensor indices to scan
* @data: location to store data
*
* Return: 0 on success, -errno on failure.
*/
int cros_ec_sensors_read_cmd(struct iio_dev *indio_dev, unsigned long scan_mask,
s16 *data);
/**
* cros_ec_sensors_core_init() - basic initialization of the core structure
* @pdev: platform device created for the sensors
* @indio_dev: iio device structure of the device
* @physical_device: true if the device refers to a physical device
*
* Return: 0 on success, -errno on failure.
*/
int cros_ec_sensors_core_init(struct platform_device *pdev,
struct iio_dev *indio_dev, bool physical_device);
/**
* cros_ec_sensors_capture() - the trigger handler function
* @irq: the interrupt number.
* @p: a pointer to the poll function.
*
* On a trigger event occurring, if the pollfunc is attached then this
* handler is called as a threaded interrupt (and hence may sleep). It
* is responsible for grabbing data from the device and pushing it into
* the associated buffer.
*
* Return: IRQ_HANDLED
*/
irqreturn_t cros_ec_sensors_capture(int irq, void *p);
/**
* cros_ec_motion_send_host_cmd() - send motion sense host command
* @st: pointer to state information for device
* @opt_length: optional length to reduce the response size, useful on the data
* path. Otherwise, the maximal allowed response size is used
*
* When called, the sub-command is assumed to be set in param->cmd.
*
* Return: 0 on success, -errno on failure.
*/
int cros_ec_motion_send_host_cmd(struct cros_ec_sensors_core_state *st,
u16 opt_length);
/**
* cros_ec_sensors_core_read() - function to request a value from the sensor
* @st: pointer to state information for device
* @chan: channel specification structure table
* @val: will contain one element making up the returned value
* @val2: will contain another element making up the returned value
* @mask: specifies which values to be requested
*
* Return: the type of value returned by the device
*/
int cros_ec_sensors_core_read(struct cros_ec_sensors_core_state *st,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask);
/**
* cros_ec_sensors_core_write() - function to write a value to the sensor
* @st: pointer to state information for device
* @chan: channel specification structure table
* @val: first part of value to write
* @val2: second part of value to write
* @mask: specifies which values to write
*
* Return: the type of value returned by the device
*/
int cros_ec_sensors_core_write(struct cros_ec_sensors_core_state *st,
struct iio_chan_spec const *chan,
int val, int val2, long mask);
/* List of extended channel specification for all sensors */
extern const struct iio_chan_spec_ext_info cros_ec_sensors_ext_info[];
#endif /* __CROS_EC_SENSORS_CORE_H */

Просмотреть файл

@ -201,7 +201,7 @@ int hid_sensor_write_samp_freq_value(struct hid_sensor_common *st,
int ret;
if (val1 < 0 || val2 < 0)
ret = -EINVAL;
return -EINVAL;
value = val1 * pow_10(6) + val2;
if (value) {
@ -250,6 +250,9 @@ int hid_sensor_write_raw_hyst_value(struct hid_sensor_common *st,
s32 value;
int ret;
if (val1 < 0 || val2 < 0)
return -EINVAL;
value = convert_to_vtf_format(st->sensitivity.size,
st->sensitivity.unit_expo,
val1, val2);

Просмотреть файл

@ -0,0 +1,593 @@
/*
* IIO driver for the ACCES 104-QUAD-8
* Copyright (C) 2016 William Breathitt Gray
*
* 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 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.
*
* This driver supports the ACCES 104-QUAD-8 and ACCES 104-QUAD-4.
*/
#include <linux/bitops.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/iio/iio.h>
#include <linux/iio/types.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/isa.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/types.h>
#define QUAD8_EXTENT 32
static unsigned int base[max_num_isa_dev(QUAD8_EXTENT)];
static unsigned int num_quad8;
module_param_array(base, uint, &num_quad8, 0);
MODULE_PARM_DESC(base, "ACCES 104-QUAD-8 base addresses");
#define QUAD8_NUM_COUNTERS 8
/**
* struct quad8_iio - IIO device private data structure
* @preset: array of preset values
* @count_mode: array of count mode configurations
* @quadrature_mode: array of quadrature mode configurations
* @quadrature_scale: array of quadrature mode scale configurations
* @ab_enable: array of A and B inputs enable configurations
* @preset_enable: array of set_to_preset_on_index attribute configurations
* @synchronous_mode: array of index function synchronous mode configurations
* @index_polarity: array of index function polarity configurations
* @base: base port address of the IIO device
*/
struct quad8_iio {
unsigned int preset[QUAD8_NUM_COUNTERS];
unsigned int count_mode[QUAD8_NUM_COUNTERS];
unsigned int quadrature_mode[QUAD8_NUM_COUNTERS];
unsigned int quadrature_scale[QUAD8_NUM_COUNTERS];
unsigned int ab_enable[QUAD8_NUM_COUNTERS];
unsigned int preset_enable[QUAD8_NUM_COUNTERS];
unsigned int synchronous_mode[QUAD8_NUM_COUNTERS];
unsigned int index_polarity[QUAD8_NUM_COUNTERS];
unsigned int base;
};
static int quad8_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val, int *val2, long mask)
{
struct quad8_iio *const priv = iio_priv(indio_dev);
const int base_offset = priv->base + 2 * chan->channel;
unsigned int flags;
unsigned int borrow;
unsigned int carry;
int i;
switch (mask) {
case IIO_CHAN_INFO_RAW:
if (chan->type == IIO_INDEX) {
*val = !!(inb(priv->base + 0x16) & BIT(chan->channel));
return IIO_VAL_INT;
}
flags = inb(base_offset);
borrow = flags & BIT(0);
carry = !!(flags & BIT(1));
/* Borrow XOR Carry effectively doubles count range */
*val = (borrow ^ carry) << 24;
/* Reset Byte Pointer; transfer Counter to Output Latch */
outb(0x11, base_offset + 1);
for (i = 0; i < 3; i++)
*val |= (unsigned int)inb(base_offset) << (8 * i);
return IIO_VAL_INT;
case IIO_CHAN_INFO_ENABLE:
*val = priv->ab_enable[chan->channel];
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
*val = 1;
*val2 = priv->quadrature_scale[chan->channel];
return IIO_VAL_FRACTIONAL_LOG2;
}
return -EINVAL;
}
static int quad8_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int val, int val2, long mask)
{
struct quad8_iio *const priv = iio_priv(indio_dev);
const int base_offset = priv->base + 2 * chan->channel;
int i;
unsigned int ior_cfg;
switch (mask) {
case IIO_CHAN_INFO_RAW:
if (chan->type == IIO_INDEX)
return -EINVAL;
/* Only 24-bit values are supported */
if ((unsigned int)val > 0xFFFFFF)
return -EINVAL;
/* Reset Byte Pointer */
outb(0x01, base_offset + 1);
/* Counter can only be set via Preset Register */
for (i = 0; i < 3; i++)
outb(val >> (8 * i), base_offset);
/* Transfer Preset Register to Counter */
outb(0x08, base_offset + 1);
/* Reset Byte Pointer */
outb(0x01, base_offset + 1);
/* Set Preset Register back to original value */
val = priv->preset[chan->channel];
for (i = 0; i < 3; i++)
outb(val >> (8 * i), base_offset);
/* Reset Borrow, Carry, Compare, and Sign flags */
outb(0x02, base_offset + 1);
/* Reset Error flag */
outb(0x06, base_offset + 1);
return 0;
case IIO_CHAN_INFO_ENABLE:
/* only boolean values accepted */
if (val < 0 || val > 1)
return -EINVAL;
priv->ab_enable[chan->channel] = val;
ior_cfg = val | priv->preset_enable[chan->channel] << 1;
/* Load I/O control configuration */
outb(0x40 | ior_cfg, base_offset);
return 0;
case IIO_CHAN_INFO_SCALE:
/* Quadrature scaling only available in quadrature mode */
if (!priv->quadrature_mode[chan->channel] && (val2 || val != 1))
return -EINVAL;
/* Only three gain states (1, 0.5, 0.25) */
if (val == 1 && !val2)
priv->quadrature_scale[chan->channel] = 0;
else if (!val)
switch (val2) {
case 500000:
priv->quadrature_scale[chan->channel] = 1;
break;
case 250000:
priv->quadrature_scale[chan->channel] = 2;
break;
default:
return -EINVAL;
}
else
return -EINVAL;
return 0;
}
return -EINVAL;
}
static const struct iio_info quad8_info = {
.driver_module = THIS_MODULE,
.read_raw = quad8_read_raw,
.write_raw = quad8_write_raw
};
static ssize_t quad8_read_preset(struct iio_dev *indio_dev, uintptr_t private,
const struct iio_chan_spec *chan, char *buf)
{
const struct quad8_iio *const priv = iio_priv(indio_dev);
return snprintf(buf, PAGE_SIZE, "%u\n", priv->preset[chan->channel]);
}
static ssize_t quad8_write_preset(struct iio_dev *indio_dev, uintptr_t private,
const struct iio_chan_spec *chan, const char *buf, size_t len)
{
struct quad8_iio *const priv = iio_priv(indio_dev);
const int base_offset = priv->base + 2 * chan->channel;
unsigned int preset;
int ret;
int i;
ret = kstrtouint(buf, 0, &preset);
if (ret)
return ret;
/* Only 24-bit values are supported */
if (preset > 0xFFFFFF)
return -EINVAL;
priv->preset[chan->channel] = preset;
/* Reset Byte Pointer */
outb(0x01, base_offset + 1);
/* Set Preset Register */
for (i = 0; i < 3; i++)
outb(preset >> (8 * i), base_offset);
return len;
}
static ssize_t quad8_read_set_to_preset_on_index(struct iio_dev *indio_dev,
uintptr_t private, const struct iio_chan_spec *chan, char *buf)
{
const struct quad8_iio *const priv = iio_priv(indio_dev);
return snprintf(buf, PAGE_SIZE, "%u\n",
priv->preset_enable[chan->channel]);
}
static ssize_t quad8_write_set_to_preset_on_index(struct iio_dev *indio_dev,
uintptr_t private, const struct iio_chan_spec *chan, const char *buf,
size_t len)
{
struct quad8_iio *const priv = iio_priv(indio_dev);
const int base_offset = priv->base + 2 * chan->channel;
bool preset_enable;
int ret;
unsigned int ior_cfg;
ret = kstrtobool(buf, &preset_enable);
if (ret)
return ret;
priv->preset_enable[chan->channel] = preset_enable;
ior_cfg = priv->ab_enable[chan->channel] |
(unsigned int)preset_enable << 1;
/* Load I/O control configuration to Input / Output Control Register */
outb(0x40 | ior_cfg, base_offset);
return len;
}
static const char *const quad8_noise_error_states[] = {
"No excessive noise is present at the count inputs",
"Excessive noise is present at the count inputs"
};
static int quad8_get_noise_error(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan)
{
struct quad8_iio *const priv = iio_priv(indio_dev);
const int base_offset = priv->base + 2 * chan->channel + 1;
return !!(inb(base_offset) & BIT(4));
}
static const struct iio_enum quad8_noise_error_enum = {
.items = quad8_noise_error_states,
.num_items = ARRAY_SIZE(quad8_noise_error_states),
.get = quad8_get_noise_error
};
static const char *const quad8_count_direction_states[] = {
"down",
"up"
};
static int quad8_get_count_direction(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan)
{
struct quad8_iio *const priv = iio_priv(indio_dev);
const int base_offset = priv->base + 2 * chan->channel + 1;
return !!(inb(base_offset) & BIT(5));
}
static const struct iio_enum quad8_count_direction_enum = {
.items = quad8_count_direction_states,
.num_items = ARRAY_SIZE(quad8_count_direction_states),
.get = quad8_get_count_direction
};
static const char *const quad8_count_modes[] = {
"normal",
"range limit",
"non-recycle",
"modulo-n"
};
static int quad8_set_count_mode(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan, unsigned int count_mode)
{
struct quad8_iio *const priv = iio_priv(indio_dev);
unsigned int mode_cfg = count_mode << 1;
const int base_offset = priv->base + 2 * chan->channel + 1;
priv->count_mode[chan->channel] = count_mode;
/* Add quadrature mode configuration */
if (priv->quadrature_mode[chan->channel])
mode_cfg |= (priv->quadrature_scale[chan->channel] + 1) << 3;
/* Load mode configuration to Counter Mode Register */
outb(0x20 | mode_cfg, base_offset);
return 0;
}
static int quad8_get_count_mode(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan)
{
const struct quad8_iio *const priv = iio_priv(indio_dev);
return priv->count_mode[chan->channel];
}
static const struct iio_enum quad8_count_mode_enum = {
.items = quad8_count_modes,
.num_items = ARRAY_SIZE(quad8_count_modes),
.set = quad8_set_count_mode,
.get = quad8_get_count_mode
};
static const char *const quad8_synchronous_modes[] = {
"non-synchronous",
"synchronous"
};
static int quad8_set_synchronous_mode(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan, unsigned int synchronous_mode)
{
struct quad8_iio *const priv = iio_priv(indio_dev);
const unsigned int idr_cfg = synchronous_mode |
priv->index_polarity[chan->channel] << 1;
const int base_offset = priv->base + 2 * chan->channel + 1;
/* Index function must be non-synchronous in non-quadrature mode */
if (synchronous_mode && !priv->quadrature_mode[chan->channel])
return -EINVAL;
priv->synchronous_mode[chan->channel] = synchronous_mode;
/* Load Index Control configuration to Index Control Register */
outb(0x40 | idr_cfg, base_offset);
return 0;
}
static int quad8_get_synchronous_mode(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan)
{
const struct quad8_iio *const priv = iio_priv(indio_dev);
return priv->synchronous_mode[chan->channel];
}
static const struct iio_enum quad8_synchronous_mode_enum = {
.items = quad8_synchronous_modes,
.num_items = ARRAY_SIZE(quad8_synchronous_modes),
.set = quad8_set_synchronous_mode,
.get = quad8_get_synchronous_mode
};
static const char *const quad8_quadrature_modes[] = {
"non-quadrature",
"quadrature"
};
static int quad8_set_quadrature_mode(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan, unsigned int quadrature_mode)
{
struct quad8_iio *const priv = iio_priv(indio_dev);
unsigned int mode_cfg = priv->count_mode[chan->channel] << 1;
const int base_offset = priv->base + 2 * chan->channel + 1;
if (quadrature_mode)
mode_cfg |= (priv->quadrature_scale[chan->channel] + 1) << 3;
else {
/* Quadrature scaling only available in quadrature mode */
priv->quadrature_scale[chan->channel] = 0;
/* Synchronous function not supported in non-quadrature mode */
if (priv->synchronous_mode[chan->channel])
quad8_set_synchronous_mode(indio_dev, chan, 0);
}
priv->quadrature_mode[chan->channel] = quadrature_mode;
/* Load mode configuration to Counter Mode Register */
outb(0x20 | mode_cfg, base_offset);
return 0;
}
static int quad8_get_quadrature_mode(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan)
{
const struct quad8_iio *const priv = iio_priv(indio_dev);
return priv->quadrature_mode[chan->channel];
}
static const struct iio_enum quad8_quadrature_mode_enum = {
.items = quad8_quadrature_modes,
.num_items = ARRAY_SIZE(quad8_quadrature_modes),
.set = quad8_set_quadrature_mode,
.get = quad8_get_quadrature_mode
};
static const char *const quad8_index_polarity_modes[] = {
"negative",
"positive"
};
static int quad8_set_index_polarity(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan, unsigned int index_polarity)
{
struct quad8_iio *const priv = iio_priv(indio_dev);
const unsigned int idr_cfg = priv->synchronous_mode[chan->channel] |
index_polarity << 1;
const int base_offset = priv->base + 2 * chan->channel + 1;
priv->index_polarity[chan->channel] = index_polarity;
/* Load Index Control configuration to Index Control Register */
outb(0x40 | idr_cfg, base_offset);
return 0;
}
static int quad8_get_index_polarity(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan)
{
const struct quad8_iio *const priv = iio_priv(indio_dev);
return priv->index_polarity[chan->channel];
}
static const struct iio_enum quad8_index_polarity_enum = {
.items = quad8_index_polarity_modes,
.num_items = ARRAY_SIZE(quad8_index_polarity_modes),
.set = quad8_set_index_polarity,
.get = quad8_get_index_polarity
};
static const struct iio_chan_spec_ext_info quad8_count_ext_info[] = {
{
.name = "preset",
.shared = IIO_SEPARATE,
.read = quad8_read_preset,
.write = quad8_write_preset
},
{
.name = "set_to_preset_on_index",
.shared = IIO_SEPARATE,
.read = quad8_read_set_to_preset_on_index,
.write = quad8_write_set_to_preset_on_index
},
IIO_ENUM("noise_error", IIO_SEPARATE, &quad8_noise_error_enum),
IIO_ENUM_AVAILABLE("noise_error", &quad8_noise_error_enum),
IIO_ENUM("count_direction", IIO_SEPARATE, &quad8_count_direction_enum),
IIO_ENUM_AVAILABLE("count_direction", &quad8_count_direction_enum),
IIO_ENUM("count_mode", IIO_SEPARATE, &quad8_count_mode_enum),
IIO_ENUM_AVAILABLE("count_mode", &quad8_count_mode_enum),
IIO_ENUM("quadrature_mode", IIO_SEPARATE, &quad8_quadrature_mode_enum),
IIO_ENUM_AVAILABLE("quadrature_mode", &quad8_quadrature_mode_enum),
{}
};
static const struct iio_chan_spec_ext_info quad8_index_ext_info[] = {
IIO_ENUM("synchronous_mode", IIO_SEPARATE,
&quad8_synchronous_mode_enum),
IIO_ENUM_AVAILABLE("synchronous_mode", &quad8_synchronous_mode_enum),
IIO_ENUM("index_polarity", IIO_SEPARATE, &quad8_index_polarity_enum),
IIO_ENUM_AVAILABLE("index_polarity", &quad8_index_polarity_enum),
{}
};
#define QUAD8_COUNT_CHAN(_chan) { \
.type = IIO_COUNT, \
.channel = (_chan), \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
BIT(IIO_CHAN_INFO_ENABLE) | BIT(IIO_CHAN_INFO_SCALE), \
.ext_info = quad8_count_ext_info, \
.indexed = 1 \
}
#define QUAD8_INDEX_CHAN(_chan) { \
.type = IIO_INDEX, \
.channel = (_chan), \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.ext_info = quad8_index_ext_info, \
.indexed = 1 \
}
static const struct iio_chan_spec quad8_channels[] = {
QUAD8_COUNT_CHAN(0), QUAD8_INDEX_CHAN(0),
QUAD8_COUNT_CHAN(1), QUAD8_INDEX_CHAN(1),
QUAD8_COUNT_CHAN(2), QUAD8_INDEX_CHAN(2),
QUAD8_COUNT_CHAN(3), QUAD8_INDEX_CHAN(3),
QUAD8_COUNT_CHAN(4), QUAD8_INDEX_CHAN(4),
QUAD8_COUNT_CHAN(5), QUAD8_INDEX_CHAN(5),
QUAD8_COUNT_CHAN(6), QUAD8_INDEX_CHAN(6),
QUAD8_COUNT_CHAN(7), QUAD8_INDEX_CHAN(7)
};
static int quad8_probe(struct device *dev, unsigned int id)
{
struct iio_dev *indio_dev;
struct quad8_iio *priv;
int i, j;
unsigned int base_offset;
indio_dev = devm_iio_device_alloc(dev, sizeof(*priv));
if (!indio_dev)
return -ENOMEM;
if (!devm_request_region(dev, base[id], QUAD8_EXTENT,
dev_name(dev))) {
dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n",
base[id], base[id] + QUAD8_EXTENT);
return -EBUSY;
}
indio_dev->info = &quad8_info;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->num_channels = ARRAY_SIZE(quad8_channels);
indio_dev->channels = quad8_channels;
indio_dev->name = dev_name(dev);
priv = iio_priv(indio_dev);
priv->base = base[id];
/* Reset all counters and disable interrupt function */
outb(0x01, base[id] + 0x11);
/* Set initial configuration for all counters */
for (i = 0; i < QUAD8_NUM_COUNTERS; i++) {
base_offset = base[id] + 2 * i;
/* Reset Byte Pointer */
outb(0x01, base_offset + 1);
/* Reset Preset Register */
for (j = 0; j < 3; j++)
outb(0x00, base_offset);
/* Reset Borrow, Carry, Compare, and Sign flags */
outb(0x04, base_offset + 1);
/* Reset Error flag */
outb(0x06, base_offset + 1);
/* Binary encoding; Normal count; non-quadrature mode */
outb(0x20, base_offset + 1);
/* Disable A and B inputs; preset on index; FLG1 as Carry */
outb(0x40, base_offset + 1);
/* Disable index function; negative index polarity */
outb(0x60, base_offset + 1);
}
/* Enable all counters */
outb(0x00, base[id] + 0x11);
return devm_iio_device_register(dev, indio_dev);
}
static struct isa_driver quad8_driver = {
.probe = quad8_probe,
.driver = {
.name = "104-quad-8"
}
};
module_isa_driver(quad8_driver, num_quad8);
MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
MODULE_DESCRIPTION("ACCES 104-QUAD-8 IIO driver");
MODULE_LICENSE("GPL v2");

Просмотреть файл

@ -0,0 +1,24 @@
#
# Counter devices
#
# When adding new entries keep the list in alphabetical order
menu "Counters"
config 104_QUAD_8
tristate "ACCES 104-QUAD-8 driver"
depends on X86 && ISA_BUS_API
help
Say yes here to build support for the ACCES 104-QUAD-8 quadrature
encoder counter/interface device family (104-QUAD-8, 104-QUAD-4).
Performing a write to a counter's IIO_CHAN_INFO_RAW sets the counter and
also clears the counter's respective error flag. Although the counters
have a 25-bit range, only the lower 24 bits may be set, either directly
or via a counter's preset attribute. Interrupts are not supported by
this driver.
The base port addresses for the devices may be configured via the base
array module parameter.
endmenu

Просмотреть файл

@ -0,0 +1,7 @@
#
# Makefile for IIO counter devices
#
# When adding new entries keep the list in alphabetical order
obj-$(CONFIG_104_QUAD_8) += 104-quad-8.o

Просмотреть файл

@ -200,6 +200,16 @@ config AD8801
To compile this driver as a module choose M here: the module will be called
ad8801.
config DPOT_DAC
tristate "DAC emulation using a DPOT"
depends on OF
help
Say yes here to build support for DAC emulation using a digital
potentiometer.
To compile this driver as a module, choose M here: the module will be
called dpot-dac.
config LPC18XX_DAC
tristate "NXP LPC18xx DAC driver"
depends on ARCH_LPC18XX || COMPILE_TEST

Просмотреть файл

@ -22,6 +22,7 @@ obj-$(CONFIG_AD5686) += ad5686.o
obj-$(CONFIG_AD7303) += ad7303.o
obj-$(CONFIG_AD8801) += ad8801.o
obj-$(CONFIG_CIO_DAC) += cio-dac.o
obj-$(CONFIG_DPOT_DAC) += dpot-dac.o
obj-$(CONFIG_LPC18XX_DAC) += lpc18xx_dac.o
obj-$(CONFIG_M62332) += m62332.o
obj-$(CONFIG_MAX517) += max517.o

Просмотреть файл

@ -17,7 +17,7 @@
#define AD5592R_GPIO_READBACK_EN BIT(10)
#define AD5592R_LDAC_READBACK_EN BIT(6)
static int ad5592r_spi_wnop_r16(struct ad5592r_state *st, u16 *buf)
static int ad5592r_spi_wnop_r16(struct ad5592r_state *st, __be16 *buf)
{
struct spi_device *spi = container_of(st->dev, struct spi_device, dev);
struct spi_transfer t = {

266
drivers/iio/dac/dpot-dac.c Normal file
Просмотреть файл

@ -0,0 +1,266 @@
/*
* IIO DAC emulation driver using a digital potentiometer
*
* Copyright (C) 2016 Axentia Technologies AB
*
* Author: Peter Rosin <peda@axentia.se>
*
* 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.
*/
/*
* It is assumed that the dpot is used as a voltage divider between the
* current dpot wiper setting and the maximum resistance of the dpot. The
* divided voltage is provided by a vref regulator.
*
* .------.
* .-----------. | |
* | vref |--' .---.
* | regulator |--. | |
* '-----------' | | d |
* | | p |
* | | o | wiper
* | | t |<---------+
* | | |
* | '---' dac output voltage
* | |
* '------+------------+
*/
#include <linux/err.h>
#include <linux/iio/consumer.h>
#include <linux/iio/iio.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
struct dpot_dac {
struct regulator *vref;
struct iio_channel *dpot;
u32 max_ohms;
};
static const struct iio_chan_spec dpot_dac_iio_channel = {
.type = IIO_VOLTAGE,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW)
| BIT(IIO_CHAN_INFO_SCALE),
.info_mask_separate_available = BIT(IIO_CHAN_INFO_RAW),
.output = 1,
.indexed = 1,
};
static int dpot_dac_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct dpot_dac *dac = iio_priv(indio_dev);
int ret;
unsigned long long tmp;
switch (mask) {
case IIO_CHAN_INFO_RAW:
return iio_read_channel_raw(dac->dpot, val);
case IIO_CHAN_INFO_SCALE:
ret = iio_read_channel_scale(dac->dpot, val, val2);
switch (ret) {
case IIO_VAL_FRACTIONAL_LOG2:
tmp = *val * 1000000000LL;
do_div(tmp, dac->max_ohms);
tmp *= regulator_get_voltage(dac->vref) / 1000;
do_div(tmp, 1000000000LL);
*val = tmp;
return ret;
case IIO_VAL_INT:
/*
* Convert integer scale to fractional scale by
* setting the denominator (val2) to one...
*/
*val2 = 1;
ret = IIO_VAL_FRACTIONAL;
/* ...and fall through. */
case IIO_VAL_FRACTIONAL:
*val *= regulator_get_voltage(dac->vref) / 1000;
*val2 *= dac->max_ohms;
break;
}
return ret;
}
return -EINVAL;
}
static int dpot_dac_read_avail(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
const int **vals, int *type, int *length,
long mask)
{
struct dpot_dac *dac = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_RAW:
*type = IIO_VAL_INT;
return iio_read_avail_channel_raw(dac->dpot, vals, length);
}
return -EINVAL;
}
static int dpot_dac_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
struct dpot_dac *dac = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_RAW:
return iio_write_channel_raw(dac->dpot, val);
}
return -EINVAL;
}
static const struct iio_info dpot_dac_info = {
.read_raw = dpot_dac_read_raw,
.read_avail = dpot_dac_read_avail,
.write_raw = dpot_dac_write_raw,
.driver_module = THIS_MODULE,
};
static int dpot_dac_channel_max_ohms(struct iio_dev *indio_dev)
{
struct device *dev = &indio_dev->dev;
struct dpot_dac *dac = iio_priv(indio_dev);
unsigned long long tmp;
int ret;
int val;
int val2;
int max;
ret = iio_read_max_channel_raw(dac->dpot, &max);
if (ret < 0) {
dev_err(dev, "dpot does not indicate its raw maximum value\n");
return ret;
}
switch (iio_read_channel_scale(dac->dpot, &val, &val2)) {
case IIO_VAL_INT:
return max * val;
case IIO_VAL_FRACTIONAL:
tmp = (unsigned long long)max * val;
do_div(tmp, val2);
return tmp;
case IIO_VAL_FRACTIONAL_LOG2:
tmp = val * 1000000000LL * max >> val2;
do_div(tmp, 1000000000LL);
return tmp;
default:
dev_err(dev, "dpot has a scale that is too weird\n");
}
return -EINVAL;
}
static int dpot_dac_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct iio_dev *indio_dev;
struct dpot_dac *dac;
enum iio_chan_type type;
int ret;
indio_dev = devm_iio_device_alloc(dev, sizeof(*dac));
if (!indio_dev)
return -ENOMEM;
platform_set_drvdata(pdev, indio_dev);
dac = iio_priv(indio_dev);
indio_dev->name = dev_name(dev);
indio_dev->dev.parent = dev;
indio_dev->info = &dpot_dac_info;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = &dpot_dac_iio_channel;
indio_dev->num_channels = 1;
dac->vref = devm_regulator_get(dev, "vref");
if (IS_ERR(dac->vref)) {
if (PTR_ERR(dac->vref) != -EPROBE_DEFER)
dev_err(&pdev->dev, "failed to get vref regulator\n");
return PTR_ERR(dac->vref);
}
dac->dpot = devm_iio_channel_get(dev, "dpot");
if (IS_ERR(dac->dpot)) {
if (PTR_ERR(dac->dpot) != -EPROBE_DEFER)
dev_err(dev, "failed to get dpot input channel\n");
return PTR_ERR(dac->dpot);
}
ret = iio_get_channel_type(dac->dpot, &type);
if (ret < 0)
return ret;
if (type != IIO_RESISTANCE) {
dev_err(dev, "dpot is of the wrong type\n");
return -EINVAL;
}
ret = dpot_dac_channel_max_ohms(indio_dev);
if (ret < 0)
return ret;
dac->max_ohms = ret;
ret = regulator_enable(dac->vref);
if (ret) {
dev_err(dev, "failed to enable the vref regulator\n");
return ret;
}
ret = iio_device_register(indio_dev);
if (ret) {
dev_err(dev, "failed to register iio device\n");
goto disable_reg;
}
return 0;
disable_reg:
regulator_disable(dac->vref);
return ret;
}
static int dpot_dac_remove(struct platform_device *pdev)
{
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
struct dpot_dac *dac = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
regulator_disable(dac->vref);
return 0;
}
static const struct of_device_id dpot_dac_match[] = {
{ .compatible = "dpot-dac" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, dpot_dac_match);
static struct platform_driver dpot_dac_driver = {
.probe = dpot_dac_probe,
.remove = dpot_dac_remove,
.driver = {
.name = "iio-dpot-dac",
.of_match_table = dpot_dac_match,
},
};
module_platform_driver(dpot_dac_driver);
MODULE_DESCRIPTION("DAC emulation driver using a digital potentiometer");
MODULE_AUTHOR("Peter Rosin <peda@axentia.se>");
MODULE_LICENSE("GPL v2");

Просмотреть файл

@ -18,6 +18,8 @@
#include <linux/i2c.h>
#include <linux/err.h>
#include <linux/delay.h>
#include <linux/regulator/consumer.h>
#include <linux/of.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
@ -26,12 +28,20 @@
#define MCP4725_DRV_NAME "mcp4725"
#define MCP472X_REF_VDD 0x00
#define MCP472X_REF_VREF_UNBUFFERED 0x02
#define MCP472X_REF_VREF_BUFFERED 0x03
struct mcp4725_data {
struct i2c_client *client;
u16 vref_mv;
int id;
unsigned ref_mode;
bool vref_buffered;
u16 dac_value;
bool powerdown;
unsigned powerdown_mode;
struct regulator *vdd_reg;
struct regulator *vref_reg;
};
static int mcp4725_suspend(struct device *dev)
@ -86,6 +96,7 @@ static ssize_t mcp4725_store_eeprom(struct device *dev,
return 0;
inoutbuf[0] = 0x60; /* write EEPROM */
inoutbuf[0] |= data->ref_mode << 3;
inoutbuf[1] = data->dac_value >> 4;
inoutbuf[2] = (data->dac_value & 0xf) << 4;
@ -278,18 +289,49 @@ static int mcp4725_set_value(struct iio_dev *indio_dev, int val)
return 0;
}
static int mcp4726_set_cfg(struct iio_dev *indio_dev)
{
struct mcp4725_data *data = iio_priv(indio_dev);
u8 outbuf[3];
int ret;
outbuf[0] = 0x40;
outbuf[0] |= data->ref_mode << 3;
if (data->powerdown)
outbuf[0] |= data->powerdown << 1;
outbuf[1] = data->dac_value >> 4;
outbuf[2] = (data->dac_value & 0xf) << 4;
ret = i2c_master_send(data->client, outbuf, 3);
if (ret < 0)
return ret;
else if (ret != 3)
return -EIO;
else
return 0;
}
static int mcp4725_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct mcp4725_data *data = iio_priv(indio_dev);
int ret;
switch (mask) {
case IIO_CHAN_INFO_RAW:
*val = data->dac_value;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
*val = data->vref_mv;
if (data->ref_mode == MCP472X_REF_VDD)
ret = regulator_get_voltage(data->vdd_reg);
else
ret = regulator_get_voltage(data->vref_reg);
if (ret < 0)
return ret;
*val = ret / 1000;
*val2 = 12;
return IIO_VAL_FRACTIONAL_LOG2;
}
@ -323,27 +365,98 @@ static const struct iio_info mcp4725_info = {
.driver_module = THIS_MODULE,
};
#ifdef CONFIG_OF
static int mcp4725_probe_dt(struct device *dev,
struct mcp4725_platform_data *pdata)
{
struct device_node *np = dev->of_node;
if (!np)
return -ENODEV;
/* check if is the vref-supply defined */
pdata->use_vref = of_property_read_bool(np, "vref-supply");
pdata->vref_buffered =
of_property_read_bool(np, "microchip,vref-buffered");
return 0;
}
#else
static int mcp4725_probe_dt(struct device *dev,
struct mcp4725_platform_data *platform_data)
{
return -ENODEV;
}
#endif
static int mcp4725_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct mcp4725_data *data;
struct iio_dev *indio_dev;
struct mcp4725_platform_data *platform_data = client->dev.platform_data;
u8 inbuf[3];
struct mcp4725_platform_data *pdata, pdata_dt;
u8 inbuf[4];
u8 pd;
u8 ref;
int err;
if (!platform_data || !platform_data->vref_mv) {
dev_err(&client->dev, "invalid platform data");
return -EINVAL;
}
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (indio_dev == NULL)
return -ENOMEM;
data = iio_priv(indio_dev);
i2c_set_clientdata(client, indio_dev);
data->client = client;
data->id = id->driver_data;
pdata = dev_get_platdata(&client->dev);
if (!pdata) {
err = mcp4725_probe_dt(&client->dev, &pdata_dt);
if (err) {
dev_err(&client->dev,
"invalid platform or devicetree data");
return err;
}
pdata = &pdata_dt;
}
if (data->id == MCP4725 && pdata->use_vref) {
dev_err(&client->dev,
"external reference is unavailable on MCP4725");
return -EINVAL;
}
if (!pdata->use_vref && pdata->vref_buffered) {
dev_err(&client->dev,
"buffering is unavailable on the internal reference");
return -EINVAL;
}
if (!pdata->use_vref)
data->ref_mode = MCP472X_REF_VDD;
else
data->ref_mode = pdata->vref_buffered ?
MCP472X_REF_VREF_BUFFERED :
MCP472X_REF_VREF_UNBUFFERED;
data->vdd_reg = devm_regulator_get(&client->dev, "vdd");
if (IS_ERR(data->vdd_reg))
return PTR_ERR(data->vdd_reg);
err = regulator_enable(data->vdd_reg);
if (err)
return err;
if (pdata->use_vref) {
data->vref_reg = devm_regulator_get(&client->dev, "vref");
if (IS_ERR(data->vref_reg)) {
err = PTR_ERR(data->vref_reg);
goto err_disable_vdd_reg;
}
err = regulator_enable(data->vref_reg);
if (err)
goto err_disable_vdd_reg;
}
indio_dev->dev.parent = &client->dev;
indio_dev->name = id->name;
@ -352,25 +465,56 @@ static int mcp4725_probe(struct i2c_client *client,
indio_dev->num_channels = 1;
indio_dev->modes = INDIO_DIRECT_MODE;
data->vref_mv = platform_data->vref_mv;
/* read current DAC value and settings */
err = i2c_master_recv(client, inbuf, data->id == MCP4725 ? 3 : 4);
/* read current DAC value */
err = i2c_master_recv(client, inbuf, 3);
if (err < 0) {
dev_err(&client->dev, "failed to read DAC value");
return err;
goto err_disable_vref_reg;
}
pd = (inbuf[0] >> 1) & 0x3;
data->powerdown = pd > 0 ? true : false;
data->powerdown_mode = pd ? pd - 1 : 2; /* largest register to gnd */
data->powerdown_mode = pd ? pd - 1 : 2; /* largest resistor to gnd */
data->dac_value = (inbuf[1] << 4) | (inbuf[2] >> 4);
if (data->id == MCP4726)
ref = (inbuf[3] >> 3) & 0x3;
return iio_device_register(indio_dev);
if (data->id == MCP4726 && ref != data->ref_mode) {
dev_info(&client->dev,
"voltage reference mode differs (conf: %u, eeprom: %u), setting %u",
data->ref_mode, ref, data->ref_mode);
err = mcp4726_set_cfg(indio_dev);
if (err < 0)
goto err_disable_vref_reg;
}
err = iio_device_register(indio_dev);
if (err)
goto err_disable_vref_reg;
return 0;
err_disable_vref_reg:
if (data->vref_reg)
regulator_disable(data->vref_reg);
err_disable_vdd_reg:
regulator_disable(data->vdd_reg);
return err;
}
static int mcp4725_remove(struct i2c_client *client)
{
iio_device_unregister(i2c_get_clientdata(client));
struct iio_dev *indio_dev = i2c_get_clientdata(client);
struct mcp4725_data *data = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
if (data->vref_reg)
regulator_disable(data->vref_reg);
regulator_disable(data->vdd_reg);
return 0;
}

Просмотреть файл

@ -84,6 +84,24 @@ config HID_SENSOR_GYRO_3D
Say yes here to build support for the HID SENSOR
Gyroscope 3D.
config MPU3050
tristate
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
select REGMAP
config MPU3050_I2C
tristate "Invensense MPU3050 devices on I2C"
depends on !(INPUT_MPU3050=y || INPUT_MPU3050=m)
depends on I2C
select MPU3050
select REGMAP_I2C
select I2C_MUX
help
This driver supports the Invensense MPU3050 gyroscope over I2C.
This driver can be built as a module. The module will be called
inv-mpu3050-i2c.
config IIO_ST_GYRO_3AXIS
tristate "STMicroelectronics gyroscopes 3-Axis Driver"
depends on (I2C || SPI_MASTER) && SYSFS

Просмотреть файл

@ -14,6 +14,11 @@ obj-$(CONFIG_BMG160_SPI) += bmg160_spi.o
obj-$(CONFIG_HID_SENSOR_GYRO_3D) += hid-sensor-gyro-3d.o
# Currently this is rolled into one module, split it if
# we ever create a separate SPI interface for MPU-3050
obj-$(CONFIG_MPU3050) += mpu3050.o
mpu3050-objs := mpu3050-core.o mpu3050-i2c.o
itg3200-y := itg3200_core.o
itg3200-$(CONFIG_IIO_BUFFER) += itg3200_buffer.o
obj-$(CONFIG_ITG3200) += itg3200.o

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,124 @@
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/i2c-mux.h>
#include <linux/iio/iio.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/pm_runtime.h>
#include "mpu3050.h"
static const struct regmap_config mpu3050_i2c_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
};
static int mpu3050_i2c_bypass_select(struct i2c_mux_core *mux, u32 chan_id)
{
struct mpu3050 *mpu3050 = i2c_mux_priv(mux);
/* Just power up the device, that is all that is needed */
pm_runtime_get_sync(mpu3050->dev);
return 0;
}
static int mpu3050_i2c_bypass_deselect(struct i2c_mux_core *mux, u32 chan_id)
{
struct mpu3050 *mpu3050 = i2c_mux_priv(mux);
pm_runtime_mark_last_busy(mpu3050->dev);
pm_runtime_put_autosuspend(mpu3050->dev);
return 0;
}
static int mpu3050_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct regmap *regmap;
const char *name;
struct mpu3050 *mpu3050;
int ret;
if (!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_I2C_BLOCK))
return -EOPNOTSUPP;
if (id)
name = id->name;
else
return -ENODEV;
regmap = devm_regmap_init_i2c(client, &mpu3050_i2c_regmap_config);
if (IS_ERR(regmap)) {
dev_err(&client->dev, "Failed to register i2c regmap %d\n",
(int)PTR_ERR(regmap));
return PTR_ERR(regmap);
}
ret = mpu3050_common_probe(&client->dev, regmap, client->irq, name);
if (ret)
return ret;
/* The main driver is up, now register the I2C mux */
mpu3050 = iio_priv(dev_get_drvdata(&client->dev));
mpu3050->i2cmux = i2c_mux_alloc(client->adapter, &client->dev,
1, 0, I2C_MUX_LOCKED | I2C_MUX_GATE,
mpu3050_i2c_bypass_select,
mpu3050_i2c_bypass_deselect);
/* Just fail the mux, there is no point in killing the driver */
if (!mpu3050->i2cmux)
dev_err(&client->dev, "failed to allocate I2C mux\n");
else {
mpu3050->i2cmux->priv = mpu3050;
ret = i2c_mux_add_adapter(mpu3050->i2cmux, 0, 0, 0);
if (ret)
dev_err(&client->dev, "failed to add I2C mux\n");
}
return 0;
}
static int mpu3050_i2c_remove(struct i2c_client *client)
{
struct iio_dev *indio_dev = dev_get_drvdata(&client->dev);
struct mpu3050 *mpu3050 = iio_priv(indio_dev);
if (mpu3050->i2cmux)
i2c_mux_del_adapters(mpu3050->i2cmux);
return mpu3050_common_remove(&client->dev);
}
/*
* device id table is used to identify what device can be
* supported by this driver
*/
static const struct i2c_device_id mpu3050_i2c_id[] = {
{ "mpu3050" },
{}
};
MODULE_DEVICE_TABLE(i2c, mpu3050_i2c_id);
static const struct of_device_id mpu3050_i2c_of_match[] = {
{ .compatible = "invensense,mpu3050", .data = "mpu3050" },
/* Deprecated vendor ID from the Input driver */
{ .compatible = "invn,mpu3050", .data = "mpu3050" },
{ },
};
MODULE_DEVICE_TABLE(of, mpu3050_i2c_of_match);
static struct i2c_driver mpu3050_i2c_driver = {
.probe = mpu3050_i2c_probe,
.remove = mpu3050_i2c_remove,
.id_table = mpu3050_i2c_id,
.driver = {
.of_match_table = mpu3050_i2c_of_match,
.name = "mpu3050-i2c",
.pm = &mpu3050_dev_pm_ops,
},
};
module_i2c_driver(mpu3050_i2c_driver);
MODULE_AUTHOR("Linus Walleij");
MODULE_DESCRIPTION("Invensense MPU3050 gyroscope driver");
MODULE_LICENSE("GPL");

Просмотреть файл

@ -0,0 +1,96 @@
#include <linux/iio/iio.h>
#include <linux/mutex.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/i2c.h>
/**
* enum mpu3050_fullscale - indicates the full range of the sensor in deg/sec
*/
enum mpu3050_fullscale {
FS_250_DPS = 0,
FS_500_DPS,
FS_1000_DPS,
FS_2000_DPS,
};
/**
* enum mpu3050_lpf - indicates the low pass filter width
*/
enum mpu3050_lpf {
/* This implicity sets sample frequency to 8 kHz */
LPF_256_HZ_NOLPF = 0,
/* All others sets the sample frequency to 1 kHz */
LPF_188_HZ,
LPF_98_HZ,
LPF_42_HZ,
LPF_20_HZ,
LPF_10_HZ,
LPF_5_HZ,
LPF_2100_HZ_NOLPF,
};
enum mpu3050_axis {
AXIS_X = 0,
AXIS_Y,
AXIS_Z,
AXIS_MAX,
};
/**
* struct mpu3050 - instance state container for the device
* @dev: parent device for this instance
* @orientation: mounting matrix, flipped axis etc
* @map: regmap to reach the registers
* @lock: serialization lock to marshal all requests
* @irq: the IRQ used for this device
* @regs: the regulators to power this device
* @fullscale: the current fullscale setting for the device
* @lpf: digital low pass filter setting for the device
* @divisor: base frequency divider: divides 8 or 1 kHz
* @calibration: the three signed 16-bit calibration settings that
* get written into the offset registers for each axis to compensate
* for DC offsets
* @trig: trigger for the MPU-3050 interrupt, if present
* @hw_irq_trigger: hardware interrupt trigger is in use
* @irq_actl: interrupt is active low
* @irq_latch: latched IRQ, this means that it is a level IRQ
* @irq_opendrain: the interrupt line shall be configured open drain
* @pending_fifo_footer: tells us if there is a pending footer in the FIFO
* that we have to read out first when handling the FIFO
* @hw_timestamp: latest hardware timestamp from the trigger IRQ, when in
* use
* @i2cmux: an I2C mux reflecting the fact that this sensor is a hub with
* a pass-through I2C interface coming out of it: this device needs to be
* powered up in order to reach devices on the other side of this mux
*/
struct mpu3050 {
struct device *dev;
struct iio_mount_matrix orientation;
struct regmap *map;
struct mutex lock;
int irq;
struct regulator_bulk_data regs[2];
enum mpu3050_fullscale fullscale;
enum mpu3050_lpf lpf;
u8 divisor;
s16 calibration[3];
struct iio_trigger *trig;
bool hw_irq_trigger;
bool irq_actl;
bool irq_latch;
bool irq_opendrain;
bool pending_fifo_footer;
s64 hw_timestamp;
struct i2c_mux_core *i2cmux;
};
/* Probe called from different transports */
int mpu3050_common_probe(struct device *dev,
struct regmap *map,
int irq,
const char *name);
int mpu3050_common_remove(struct device *dev);
/* PM ops */
extern const struct dev_pm_ops mpu3050_dev_pm_ops;

Просмотреть файл

@ -39,79 +39,6 @@
#define ST_GYRO_FS_AVL_500DPS 500
#define ST_GYRO_FS_AVL_2000DPS 2000
/* CUSTOM VALUES FOR SENSOR 1 */
#define ST_GYRO_1_WAI_EXP 0xd3
#define ST_GYRO_1_ODR_ADDR 0x20
#define ST_GYRO_1_ODR_MASK 0xc0
#define ST_GYRO_1_ODR_AVL_100HZ_VAL 0x00
#define ST_GYRO_1_ODR_AVL_200HZ_VAL 0x01
#define ST_GYRO_1_ODR_AVL_400HZ_VAL 0x02
#define ST_GYRO_1_ODR_AVL_800HZ_VAL 0x03
#define ST_GYRO_1_PW_ADDR 0x20
#define ST_GYRO_1_PW_MASK 0x08
#define ST_GYRO_1_FS_ADDR 0x23
#define ST_GYRO_1_FS_MASK 0x30
#define ST_GYRO_1_FS_AVL_250_VAL 0x00
#define ST_GYRO_1_FS_AVL_500_VAL 0x01
#define ST_GYRO_1_FS_AVL_2000_VAL 0x02
#define ST_GYRO_1_FS_AVL_250_GAIN IIO_DEGREE_TO_RAD(8750)
#define ST_GYRO_1_FS_AVL_500_GAIN IIO_DEGREE_TO_RAD(17500)
#define ST_GYRO_1_FS_AVL_2000_GAIN IIO_DEGREE_TO_RAD(70000)
#define ST_GYRO_1_BDU_ADDR 0x23
#define ST_GYRO_1_BDU_MASK 0x80
#define ST_GYRO_1_DRDY_IRQ_ADDR 0x22
#define ST_GYRO_1_DRDY_IRQ_INT2_MASK 0x08
#define ST_GYRO_1_MULTIREAD_BIT true
/* CUSTOM VALUES FOR SENSOR 2 */
#define ST_GYRO_2_WAI_EXP 0xd4
#define ST_GYRO_2_ODR_ADDR 0x20
#define ST_GYRO_2_ODR_MASK 0xc0
#define ST_GYRO_2_ODR_AVL_95HZ_VAL 0x00
#define ST_GYRO_2_ODR_AVL_190HZ_VAL 0x01
#define ST_GYRO_2_ODR_AVL_380HZ_VAL 0x02
#define ST_GYRO_2_ODR_AVL_760HZ_VAL 0x03
#define ST_GYRO_2_PW_ADDR 0x20
#define ST_GYRO_2_PW_MASK 0x08
#define ST_GYRO_2_FS_ADDR 0x23
#define ST_GYRO_2_FS_MASK 0x30
#define ST_GYRO_2_FS_AVL_250_VAL 0x00
#define ST_GYRO_2_FS_AVL_500_VAL 0x01
#define ST_GYRO_2_FS_AVL_2000_VAL 0x02
#define ST_GYRO_2_FS_AVL_250_GAIN IIO_DEGREE_TO_RAD(8750)
#define ST_GYRO_2_FS_AVL_500_GAIN IIO_DEGREE_TO_RAD(17500)
#define ST_GYRO_2_FS_AVL_2000_GAIN IIO_DEGREE_TO_RAD(70000)
#define ST_GYRO_2_BDU_ADDR 0x23
#define ST_GYRO_2_BDU_MASK 0x80
#define ST_GYRO_2_DRDY_IRQ_ADDR 0x22
#define ST_GYRO_2_DRDY_IRQ_INT2_MASK 0x08
#define ST_GYRO_2_MULTIREAD_BIT true
/* CUSTOM VALUES FOR SENSOR 3 */
#define ST_GYRO_3_WAI_EXP 0xd7
#define ST_GYRO_3_ODR_ADDR 0x20
#define ST_GYRO_3_ODR_MASK 0xc0
#define ST_GYRO_3_ODR_AVL_95HZ_VAL 0x00
#define ST_GYRO_3_ODR_AVL_190HZ_VAL 0x01
#define ST_GYRO_3_ODR_AVL_380HZ_VAL 0x02
#define ST_GYRO_3_ODR_AVL_760HZ_VAL 0x03
#define ST_GYRO_3_PW_ADDR 0x20
#define ST_GYRO_3_PW_MASK 0x08
#define ST_GYRO_3_FS_ADDR 0x23
#define ST_GYRO_3_FS_MASK 0x30
#define ST_GYRO_3_FS_AVL_250_VAL 0x00
#define ST_GYRO_3_FS_AVL_500_VAL 0x01
#define ST_GYRO_3_FS_AVL_2000_VAL 0x02
#define ST_GYRO_3_FS_AVL_250_GAIN IIO_DEGREE_TO_RAD(8750)
#define ST_GYRO_3_FS_AVL_500_GAIN IIO_DEGREE_TO_RAD(17500)
#define ST_GYRO_3_FS_AVL_2000_GAIN IIO_DEGREE_TO_RAD(70000)
#define ST_GYRO_3_BDU_ADDR 0x23
#define ST_GYRO_3_BDU_MASK 0x80
#define ST_GYRO_3_DRDY_IRQ_ADDR 0x22
#define ST_GYRO_3_DRDY_IRQ_INT2_MASK 0x08
#define ST_GYRO_3_MULTIREAD_BIT true
static const struct iio_chan_spec st_gyro_16bit_channels[] = {
ST_SENSORS_LSM_CHANNELS(IIO_ANGL_VEL,
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
@ -130,7 +57,7 @@ static const struct iio_chan_spec st_gyro_16bit_channels[] = {
static const struct st_sensor_settings st_gyro_sensors_settings[] = {
{
.wai = ST_GYRO_1_WAI_EXP,
.wai = 0xd3,
.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
.sensors_supported = {
[0] = L3G4200D_GYRO_DEV_NAME,
@ -138,18 +65,18 @@ static const struct st_sensor_settings st_gyro_sensors_settings[] = {
},
.ch = (struct iio_chan_spec *)st_gyro_16bit_channels,
.odr = {
.addr = ST_GYRO_1_ODR_ADDR,
.mask = ST_GYRO_1_ODR_MASK,
.addr = 0x20,
.mask = 0xc0,
.odr_avl = {
{ 100, ST_GYRO_1_ODR_AVL_100HZ_VAL, },
{ 200, ST_GYRO_1_ODR_AVL_200HZ_VAL, },
{ 400, ST_GYRO_1_ODR_AVL_400HZ_VAL, },
{ 800, ST_GYRO_1_ODR_AVL_800HZ_VAL, },
{ .hz = 100, .value = 0x00, },
{ .hz = 200, .value = 0x01, },
{ .hz = 400, .value = 0x02, },
{ .hz = 800, .value = 0x03, },
},
},
.pw = {
.addr = ST_GYRO_1_PW_ADDR,
.mask = ST_GYRO_1_PW_MASK,
.addr = 0x20,
.mask = 0x08,
.value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE,
.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
},
@ -158,33 +85,33 @@ static const struct st_sensor_settings st_gyro_sensors_settings[] = {
.mask = ST_SENSORS_DEFAULT_AXIS_MASK,
},
.fs = {
.addr = ST_GYRO_1_FS_ADDR,
.mask = ST_GYRO_1_FS_MASK,
.addr = 0x23,
.mask = 0x30,
.fs_avl = {
[0] = {
.num = ST_GYRO_FS_AVL_250DPS,
.value = ST_GYRO_1_FS_AVL_250_VAL,
.gain = ST_GYRO_1_FS_AVL_250_GAIN,
.value = 0x00,
.gain = IIO_DEGREE_TO_RAD(8750),
},
[1] = {
.num = ST_GYRO_FS_AVL_500DPS,
.value = ST_GYRO_1_FS_AVL_500_VAL,
.gain = ST_GYRO_1_FS_AVL_500_GAIN,
.value = 0x01,
.gain = IIO_DEGREE_TO_RAD(17500),
},
[2] = {
.num = ST_GYRO_FS_AVL_2000DPS,
.value = ST_GYRO_1_FS_AVL_2000_VAL,
.gain = ST_GYRO_1_FS_AVL_2000_GAIN,
.value = 0x02,
.gain = IIO_DEGREE_TO_RAD(70000),
},
},
},
.bdu = {
.addr = ST_GYRO_1_BDU_ADDR,
.mask = ST_GYRO_1_BDU_MASK,
.addr = 0x23,
.mask = 0x80,
},
.drdy_irq = {
.addr = ST_GYRO_1_DRDY_IRQ_ADDR,
.mask_int2 = ST_GYRO_1_DRDY_IRQ_INT2_MASK,
.addr = 0x22,
.mask_int2 = 0x08,
/*
* The sensor has IHL (active low) and open
* drain settings, but only for INT1 and not
@ -192,11 +119,11 @@ static const struct st_sensor_settings st_gyro_sensors_settings[] = {
*/
.addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR,
},
.multi_read_bit = ST_GYRO_1_MULTIREAD_BIT,
.multi_read_bit = true,
.bootime = 2,
},
{
.wai = ST_GYRO_2_WAI_EXP,
.wai = 0xd4,
.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
.sensors_supported = {
[0] = L3GD20_GYRO_DEV_NAME,
@ -208,18 +135,18 @@ static const struct st_sensor_settings st_gyro_sensors_settings[] = {
},
.ch = (struct iio_chan_spec *)st_gyro_16bit_channels,
.odr = {
.addr = ST_GYRO_2_ODR_ADDR,
.mask = ST_GYRO_2_ODR_MASK,
.addr = 0x20,
.mask = 0xc0,
.odr_avl = {
{ 95, ST_GYRO_2_ODR_AVL_95HZ_VAL, },
{ 190, ST_GYRO_2_ODR_AVL_190HZ_VAL, },
{ 380, ST_GYRO_2_ODR_AVL_380HZ_VAL, },
{ 760, ST_GYRO_2_ODR_AVL_760HZ_VAL, },
{ .hz = 95, .value = 0x00, },
{ .hz = 190, .value = 0x01, },
{ .hz = 380, .value = 0x02, },
{ .hz = 760, .value = 0x03, },
},
},
.pw = {
.addr = ST_GYRO_2_PW_ADDR,
.mask = ST_GYRO_2_PW_MASK,
.addr = 0x20,
.mask = 0x08,
.value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE,
.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
},
@ -228,33 +155,33 @@ static const struct st_sensor_settings st_gyro_sensors_settings[] = {
.mask = ST_SENSORS_DEFAULT_AXIS_MASK,
},
.fs = {
.addr = ST_GYRO_2_FS_ADDR,
.mask = ST_GYRO_2_FS_MASK,
.addr = 0x23,
.mask = 0x30,
.fs_avl = {
[0] = {
.num = ST_GYRO_FS_AVL_250DPS,
.value = ST_GYRO_2_FS_AVL_250_VAL,
.gain = ST_GYRO_2_FS_AVL_250_GAIN,
.value = 0x00,
.gain = IIO_DEGREE_TO_RAD(8750),
},
[1] = {
.num = ST_GYRO_FS_AVL_500DPS,
.value = ST_GYRO_2_FS_AVL_500_VAL,
.gain = ST_GYRO_2_FS_AVL_500_GAIN,
.value = 0x01,
.gain = IIO_DEGREE_TO_RAD(17500),
},
[2] = {
.num = ST_GYRO_FS_AVL_2000DPS,
.value = ST_GYRO_2_FS_AVL_2000_VAL,
.gain = ST_GYRO_2_FS_AVL_2000_GAIN,
.value = 0x02,
.gain = IIO_DEGREE_TO_RAD(70000),
},
},
},
.bdu = {
.addr = ST_GYRO_2_BDU_ADDR,
.mask = ST_GYRO_2_BDU_MASK,
.addr = 0x23,
.mask = 0x80,
},
.drdy_irq = {
.addr = ST_GYRO_2_DRDY_IRQ_ADDR,
.mask_int2 = ST_GYRO_2_DRDY_IRQ_INT2_MASK,
.addr = 0x22,
.mask_int2 = 0x08,
/*
* The sensor has IHL (active low) and open
* drain settings, but only for INT1 and not
@ -262,29 +189,29 @@ static const struct st_sensor_settings st_gyro_sensors_settings[] = {
*/
.addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR,
},
.multi_read_bit = ST_GYRO_2_MULTIREAD_BIT,
.multi_read_bit = true,
.bootime = 2,
},
{
.wai = ST_GYRO_3_WAI_EXP,
.wai = 0xd7,
.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
.sensors_supported = {
[0] = L3GD20_GYRO_DEV_NAME,
},
.ch = (struct iio_chan_spec *)st_gyro_16bit_channels,
.odr = {
.addr = ST_GYRO_3_ODR_ADDR,
.mask = ST_GYRO_3_ODR_MASK,
.addr = 0x20,
.mask = 0xc0,
.odr_avl = {
{ 95, ST_GYRO_3_ODR_AVL_95HZ_VAL, },
{ 190, ST_GYRO_3_ODR_AVL_190HZ_VAL, },
{ 380, ST_GYRO_3_ODR_AVL_380HZ_VAL, },
{ 760, ST_GYRO_3_ODR_AVL_760HZ_VAL, },
{ .hz = 95, .value = 0x00, },
{ .hz = 190, .value = 0x01, },
{ .hz = 380, .value = 0x02, },
{ .hz = 760, .value = 0x03, },
},
},
.pw = {
.addr = ST_GYRO_3_PW_ADDR,
.mask = ST_GYRO_3_PW_MASK,
.addr = 0x20,
.mask = 0x08,
.value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE,
.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
},
@ -293,33 +220,33 @@ static const struct st_sensor_settings st_gyro_sensors_settings[] = {
.mask = ST_SENSORS_DEFAULT_AXIS_MASK,
},
.fs = {
.addr = ST_GYRO_3_FS_ADDR,
.mask = ST_GYRO_3_FS_MASK,
.addr = 0x23,
.mask = 0x30,
.fs_avl = {
[0] = {
.num = ST_GYRO_FS_AVL_250DPS,
.value = ST_GYRO_3_FS_AVL_250_VAL,
.gain = ST_GYRO_3_FS_AVL_250_GAIN,
.value = 0x00,
.gain = IIO_DEGREE_TO_RAD(8750),
},
[1] = {
.num = ST_GYRO_FS_AVL_500DPS,
.value = ST_GYRO_3_FS_AVL_500_VAL,
.gain = ST_GYRO_3_FS_AVL_500_GAIN,
.value = 0x01,
.gain = IIO_DEGREE_TO_RAD(17500),
},
[2] = {
.num = ST_GYRO_FS_AVL_2000DPS,
.value = ST_GYRO_3_FS_AVL_2000_VAL,
.gain = ST_GYRO_3_FS_AVL_2000_GAIN,
.value = 0x02,
.gain = IIO_DEGREE_TO_RAD(70000),
},
},
},
.bdu = {
.addr = ST_GYRO_3_BDU_ADDR,
.mask = ST_GYRO_3_BDU_MASK,
.addr = 0x23,
.mask = 0x80,
},
.drdy_irq = {
.addr = ST_GYRO_3_DRDY_IRQ_ADDR,
.mask_int2 = ST_GYRO_3_DRDY_IRQ_INT2_MASK,
.addr = 0x22,
.mask_int2 = 0x08,
/*
* The sensor has IHL (active low) and open
* drain settings, but only for INT1 and not
@ -327,7 +254,7 @@ static const struct st_sensor_settings st_gyro_sensors_settings[] = {
*/
.addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR,
},
.multi_read_bit = ST_GYRO_3_MULTIREAD_BIT,
.multi_read_bit = true,
.bootime = 2,
},
};

Просмотреть файл

@ -27,6 +27,8 @@ config DHT11
config HDC100X
tristate "TI HDC100x relative humidity and temperature sensor"
depends on I2C
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
help
Say yes here to build support for the Texas Instruments
HDC1000 and HDC1008 relative humidity and temperature sensors.
@ -34,6 +36,28 @@ config HDC100X
To compile this driver as a module, choose M here: the module
will be called hdc100x.
config HTS221
tristate "STMicroelectronics HTS221 sensor Driver"
depends on (I2C || SPI)
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
select HTS221_I2C if (I2C)
select HTS221_SPI if (SPI_MASTER)
help
Say yes here to build support for STMicroelectronics HTS221
temperature-humidity sensor
To compile this driver as a module, choose M here: the module
will be called hts221.
config HTS221_I2C
tristate
depends on HTS221
config HTS221_SPI
tristate
depends on HTS221
config HTU21
tristate "Measurement Specialties HTU21 humidity & temperature sensor"
depends on I2C

Просмотреть файл

@ -5,6 +5,13 @@
obj-$(CONFIG_AM2315) += am2315.o
obj-$(CONFIG_DHT11) += dht11.o
obj-$(CONFIG_HDC100X) += hdc100x.o
hts221-y := hts221_core.o \
hts221_buffer.o
obj-$(CONFIG_HTS221) += hts221.o
obj-$(CONFIG_HTS221_I2C) += hts221_i2c.o
obj-$(CONFIG_HTS221_SPI) += hts221_spi.o
obj-$(CONFIG_HTU21) += htu21.o
obj-$(CONFIG_SI7005) += si7005.o
obj-$(CONFIG_SI7020) += si7020.o

Просмотреть файл

@ -22,11 +22,15 @@
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/buffer.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
#define HDC100X_REG_TEMP 0x00
#define HDC100X_REG_HUMIDITY 0x01
#define HDC100X_REG_CONFIG 0x02
#define HDC100X_REG_CONFIG_ACQ_MODE BIT(12)
#define HDC100X_REG_CONFIG_HEATER_EN BIT(13)
struct hdc100x_data {
@ -87,22 +91,40 @@ static const struct iio_chan_spec hdc100x_channels[] = {
BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_INT_TIME) |
BIT(IIO_CHAN_INFO_OFFSET),
.scan_index = 0,
.scan_type = {
.sign = 's',
.realbits = 16,
.storagebits = 16,
.endianness = IIO_BE,
},
},
{
.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)
BIT(IIO_CHAN_INFO_INT_TIME),
.scan_index = 1,
.scan_type = {
.sign = 'u',
.realbits = 16,
.storagebits = 16,
.endianness = IIO_BE,
},
},
{
.type = IIO_CURRENT,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
.extend_name = "heater",
.output = 1,
.scan_index = -1,
},
IIO_CHAN_SOFT_TIMESTAMP(2),
};
static const unsigned long hdc100x_scan_masks[] = {0x3, 0};
static int hdc100x_update_config(struct hdc100x_data *data, int mask, int val)
{
int tmp = (~mask & data->config) | val;
@ -183,7 +205,14 @@ static int hdc100x_read_raw(struct iio_dev *indio_dev,
*val = hdc100x_get_heater_status(data);
ret = IIO_VAL_INT;
} else {
ret = iio_device_claim_direct_mode(indio_dev);
if (ret) {
mutex_unlock(&data->lock);
return ret;
}
ret = hdc100x_get_measurement(data, chan);
iio_device_release_direct_mode(indio_dev);
if (ret >= 0) {
*val = ret;
ret = IIO_VAL_INT;
@ -246,6 +275,78 @@ static int hdc100x_write_raw(struct iio_dev *indio_dev,
}
}
static int hdc100x_buffer_postenable(struct iio_dev *indio_dev)
{
struct hdc100x_data *data = iio_priv(indio_dev);
int ret;
/* Buffer is enabled. First set ACQ Mode, then attach poll func */
mutex_lock(&data->lock);
ret = hdc100x_update_config(data, HDC100X_REG_CONFIG_ACQ_MODE,
HDC100X_REG_CONFIG_ACQ_MODE);
mutex_unlock(&data->lock);
if (ret)
return ret;
return iio_triggered_buffer_postenable(indio_dev);
}
static int hdc100x_buffer_predisable(struct iio_dev *indio_dev)
{
struct hdc100x_data *data = iio_priv(indio_dev);
int ret;
/* First detach poll func, then reset ACQ mode. OK to disable buffer */
ret = iio_triggered_buffer_predisable(indio_dev);
if (ret)
return ret;
mutex_lock(&data->lock);
ret = hdc100x_update_config(data, HDC100X_REG_CONFIG_ACQ_MODE, 0);
mutex_unlock(&data->lock);
return ret;
}
static const struct iio_buffer_setup_ops hdc_buffer_setup_ops = {
.postenable = hdc100x_buffer_postenable,
.predisable = hdc100x_buffer_predisable,
};
static irqreturn_t hdc100x_trigger_handler(int irq, void *p)
{
struct iio_poll_func *pf = p;
struct iio_dev *indio_dev = pf->indio_dev;
struct hdc100x_data *data = iio_priv(indio_dev);
struct i2c_client *client = data->client;
int delay = data->adc_int_us[0] + data->adc_int_us[1];
int ret;
s16 buf[8]; /* 2x s16 + padding + 8 byte timestamp */
/* dual read starts at temp register */
mutex_lock(&data->lock);
ret = i2c_smbus_write_byte(client, HDC100X_REG_TEMP);
if (ret < 0) {
dev_err(&client->dev, "cannot start measurement\n");
goto err;
}
usleep_range(delay, delay + 1000);
ret = i2c_master_recv(client, (u8 *)buf, 4);
if (ret < 0) {
dev_err(&client->dev, "cannot read sensor data\n");
goto err;
}
iio_push_to_buffers_with_timestamp(indio_dev, buf,
iio_get_time_ns(indio_dev));
err:
mutex_unlock(&data->lock);
iio_trigger_notify_done(indio_dev->trig);
return IRQ_HANDLED;
}
static const struct iio_info hdc100x_info = {
.read_raw = hdc100x_read_raw,
.write_raw = hdc100x_write_raw,
@ -258,6 +359,7 @@ static int hdc100x_probe(struct i2c_client *client,
{
struct iio_dev *indio_dev;
struct hdc100x_data *data;
int ret;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA |
I2C_FUNC_SMBUS_BYTE | I2C_FUNC_I2C))
@ -279,12 +381,35 @@ static int hdc100x_probe(struct i2c_client *client,
indio_dev->channels = hdc100x_channels;
indio_dev->num_channels = ARRAY_SIZE(hdc100x_channels);
indio_dev->available_scan_masks = hdc100x_scan_masks;
/* 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]);
hdc100x_update_config(data, HDC100X_REG_CONFIG_ACQ_MODE, 0);
return devm_iio_device_register(&client->dev, indio_dev);
ret = iio_triggered_buffer_setup(indio_dev, NULL,
hdc100x_trigger_handler,
&hdc_buffer_setup_ops);
if (ret < 0) {
dev_err(&client->dev, "iio triggered buffer setup failed\n");
return ret;
}
ret = iio_device_register(indio_dev);
if (ret < 0)
iio_triggered_buffer_cleanup(indio_dev);
return ret;
}
static int hdc100x_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 hdc100x_id[] = {
@ -298,6 +423,7 @@ static struct i2c_driver hdc100x_driver = {
.name = "hdc100x",
},
.probe = hdc100x_probe,
.remove = hdc100x_remove,
.id_table = hdc100x_id,
};
module_i2c_driver(hdc100x_driver);

Просмотреть файл

@ -0,0 +1,73 @@
/*
* STMicroelectronics hts221 sensor driver
*
* Copyright 2016 STMicroelectronics Inc.
*
* Lorenzo Bianconi <lorenzo.bianconi@st.com>
*
* Licensed under the GPL-2.
*/
#ifndef HTS221_H
#define HTS221_H
#define HTS221_DEV_NAME "hts221"
#include <linux/iio/iio.h>
#define HTS221_RX_MAX_LENGTH 8
#define HTS221_TX_MAX_LENGTH 8
#define HTS221_DATA_SIZE 2
struct hts221_transfer_buffer {
u8 rx_buf[HTS221_RX_MAX_LENGTH];
u8 tx_buf[HTS221_TX_MAX_LENGTH] ____cacheline_aligned;
};
struct hts221_transfer_function {
int (*read)(struct device *dev, u8 addr, int len, u8 *data);
int (*write)(struct device *dev, u8 addr, int len, u8 *data);
};
#define HTS221_AVG_DEPTH 8
struct hts221_avg_avl {
u16 avg;
u8 val;
};
enum hts221_sensor_type {
HTS221_SENSOR_H,
HTS221_SENSOR_T,
HTS221_SENSOR_MAX,
};
struct hts221_sensor {
u8 cur_avg_idx;
int slope, b_gen;
};
struct hts221_hw {
const char *name;
struct device *dev;
struct mutex lock;
struct iio_trigger *trig;
int irq;
struct hts221_sensor sensors[HTS221_SENSOR_MAX];
u8 odr;
const struct hts221_transfer_function *tf;
struct hts221_transfer_buffer tb;
};
int hts221_config_drdy(struct hts221_hw *hw, bool enable);
int hts221_probe(struct iio_dev *iio_dev);
int hts221_power_on(struct hts221_hw *hw);
int hts221_power_off(struct hts221_hw *hw);
int hts221_allocate_buffers(struct hts221_hw *hw);
int hts221_allocate_trigger(struct hts221_hw *hw);
#endif /* HTS221_H */

Просмотреть файл

@ -0,0 +1,168 @@
/*
* STMicroelectronics hts221 sensor driver
*
* Copyright 2016 STMicroelectronics Inc.
*
* Lorenzo Bianconi <lorenzo.bianconi@st.com>
*
* Licensed under the GPL-2.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/irqreturn.h>
#include <linux/iio/iio.h>
#include <linux/iio/trigger.h>
#include <linux/iio/events.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/iio/buffer.h>
#include "hts221.h"
#define HTS221_REG_STATUS_ADDR 0x27
#define HTS221_RH_DRDY_MASK BIT(1)
#define HTS221_TEMP_DRDY_MASK BIT(0)
static int hts221_trig_set_state(struct iio_trigger *trig, bool state)
{
struct iio_dev *iio_dev = iio_trigger_get_drvdata(trig);
struct hts221_hw *hw = iio_priv(iio_dev);
return hts221_config_drdy(hw, state);
}
static const struct iio_trigger_ops hts221_trigger_ops = {
.owner = THIS_MODULE,
.set_trigger_state = hts221_trig_set_state,
};
static irqreturn_t hts221_trigger_handler_thread(int irq, void *private)
{
struct hts221_hw *hw = (struct hts221_hw *)private;
u8 status;
int err;
err = hw->tf->read(hw->dev, HTS221_REG_STATUS_ADDR, sizeof(status),
&status);
if (err < 0)
return IRQ_HANDLED;
/*
* H_DA bit (humidity data available) is routed to DRDY line.
* Humidity sample is computed after temperature one.
* Here we can assume data channels are both available if H_DA bit
* is set in status register
*/
if (!(status & HTS221_RH_DRDY_MASK))
return IRQ_NONE;
iio_trigger_poll_chained(hw->trig);
return IRQ_HANDLED;
}
int hts221_allocate_trigger(struct hts221_hw *hw)
{
struct iio_dev *iio_dev = iio_priv_to_dev(hw);
unsigned long irq_type;
int err;
irq_type = irqd_get_trigger_type(irq_get_irq_data(hw->irq));
switch (irq_type) {
case IRQF_TRIGGER_HIGH:
case IRQF_TRIGGER_RISING:
break;
default:
dev_info(hw->dev,
"mode %lx unsupported, using IRQF_TRIGGER_RISING\n",
irq_type);
irq_type = IRQF_TRIGGER_RISING;
break;
}
err = devm_request_threaded_irq(hw->dev, hw->irq, NULL,
hts221_trigger_handler_thread,
irq_type | IRQF_ONESHOT,
hw->name, hw);
if (err) {
dev_err(hw->dev, "failed to request trigger irq %d\n",
hw->irq);
return err;
}
hw->trig = devm_iio_trigger_alloc(hw->dev, "%s-trigger",
iio_dev->name);
if (!hw->trig)
return -ENOMEM;
iio_trigger_set_drvdata(hw->trig, iio_dev);
hw->trig->ops = &hts221_trigger_ops;
hw->trig->dev.parent = hw->dev;
iio_dev->trig = iio_trigger_get(hw->trig);
return devm_iio_trigger_register(hw->dev, hw->trig);
}
static int hts221_buffer_preenable(struct iio_dev *iio_dev)
{
return hts221_power_on(iio_priv(iio_dev));
}
static int hts221_buffer_postdisable(struct iio_dev *iio_dev)
{
return hts221_power_off(iio_priv(iio_dev));
}
static const struct iio_buffer_setup_ops hts221_buffer_ops = {
.preenable = hts221_buffer_preenable,
.postenable = iio_triggered_buffer_postenable,
.predisable = iio_triggered_buffer_predisable,
.postdisable = hts221_buffer_postdisable,
};
static irqreturn_t hts221_buffer_handler_thread(int irq, void *p)
{
u8 buffer[ALIGN(2 * HTS221_DATA_SIZE, sizeof(s64)) + sizeof(s64)];
struct iio_poll_func *pf = p;
struct iio_dev *iio_dev = pf->indio_dev;
struct hts221_hw *hw = iio_priv(iio_dev);
struct iio_chan_spec const *ch;
int err;
/* humidity data */
ch = &iio_dev->channels[HTS221_SENSOR_H];
err = hw->tf->read(hw->dev, ch->address, HTS221_DATA_SIZE,
buffer);
if (err < 0)
goto out;
/* temperature data */
ch = &iio_dev->channels[HTS221_SENSOR_T];
err = hw->tf->read(hw->dev, ch->address, HTS221_DATA_SIZE,
buffer + HTS221_DATA_SIZE);
if (err < 0)
goto out;
iio_push_to_buffers_with_timestamp(iio_dev, buffer,
iio_get_time_ns(iio_dev));
out:
iio_trigger_notify_done(hw->trig);
return IRQ_HANDLED;
}
int hts221_allocate_buffers(struct hts221_hw *hw)
{
return devm_iio_triggered_buffer_setup(hw->dev, iio_priv_to_dev(hw),
NULL, hts221_buffer_handler_thread,
&hts221_buffer_ops);
}
MODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi@st.com>");
MODULE_DESCRIPTION("STMicroelectronics hts221 buffer driver");
MODULE_LICENSE("GPL v2");

Просмотреть файл

@ -0,0 +1,687 @@
/*
* STMicroelectronics hts221 sensor driver
*
* Copyright 2016 STMicroelectronics Inc.
*
* Lorenzo Bianconi <lorenzo.bianconi@st.com>
*
* Licensed under the GPL-2.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/iio/sysfs.h>
#include <linux/delay.h>
#include <asm/unaligned.h>
#include "hts221.h"
#define HTS221_REG_WHOAMI_ADDR 0x0f
#define HTS221_REG_WHOAMI_VAL 0xbc
#define HTS221_REG_CNTRL1_ADDR 0x20
#define HTS221_REG_CNTRL2_ADDR 0x21
#define HTS221_REG_CNTRL3_ADDR 0x22
#define HTS221_REG_AVG_ADDR 0x10
#define HTS221_REG_H_OUT_L 0x28
#define HTS221_REG_T_OUT_L 0x2a
#define HTS221_HUMIDITY_AVG_MASK 0x07
#define HTS221_TEMP_AVG_MASK 0x38
#define HTS221_ODR_MASK 0x87
#define HTS221_BDU_MASK BIT(2)
#define HTS221_DRDY_MASK BIT(2)
#define HTS221_ENABLE_SENSOR BIT(7)
#define HTS221_HUMIDITY_AVG_4 0x00 /* 0.4 %RH */
#define HTS221_HUMIDITY_AVG_8 0x01 /* 0.3 %RH */
#define HTS221_HUMIDITY_AVG_16 0x02 /* 0.2 %RH */
#define HTS221_HUMIDITY_AVG_32 0x03 /* 0.15 %RH */
#define HTS221_HUMIDITY_AVG_64 0x04 /* 0.1 %RH */
#define HTS221_HUMIDITY_AVG_128 0x05 /* 0.07 %RH */
#define HTS221_HUMIDITY_AVG_256 0x06 /* 0.05 %RH */
#define HTS221_HUMIDITY_AVG_512 0x07 /* 0.03 %RH */
#define HTS221_TEMP_AVG_2 0x00 /* 0.08 degC */
#define HTS221_TEMP_AVG_4 0x08 /* 0.05 degC */
#define HTS221_TEMP_AVG_8 0x10 /* 0.04 degC */
#define HTS221_TEMP_AVG_16 0x18 /* 0.03 degC */
#define HTS221_TEMP_AVG_32 0x20 /* 0.02 degC */
#define HTS221_TEMP_AVG_64 0x28 /* 0.015 degC */
#define HTS221_TEMP_AVG_128 0x30 /* 0.01 degC */
#define HTS221_TEMP_AVG_256 0x38 /* 0.007 degC */
/* calibration registers */
#define HTS221_REG_0RH_CAL_X_H 0x36
#define HTS221_REG_1RH_CAL_X_H 0x3a
#define HTS221_REG_0RH_CAL_Y_H 0x30
#define HTS221_REG_1RH_CAL_Y_H 0x31
#define HTS221_REG_0T_CAL_X_L 0x3c
#define HTS221_REG_1T_CAL_X_L 0x3e
#define HTS221_REG_0T_CAL_Y_H 0x32
#define HTS221_REG_1T_CAL_Y_H 0x33
#define HTS221_REG_T1_T0_CAL_Y_H 0x35
struct hts221_odr {
u8 hz;
u8 val;
};
struct hts221_avg {
u8 addr;
u8 mask;
struct hts221_avg_avl avg_avl[HTS221_AVG_DEPTH];
};
static const struct hts221_odr hts221_odr_table[] = {
{ 1, 0x01 }, /* 1Hz */
{ 7, 0x02 }, /* 7Hz */
{ 13, 0x03 }, /* 12.5Hz */
};
static const struct hts221_avg hts221_avg_list[] = {
{
.addr = HTS221_REG_AVG_ADDR,
.mask = HTS221_HUMIDITY_AVG_MASK,
.avg_avl = {
{ 4, HTS221_HUMIDITY_AVG_4 },
{ 8, HTS221_HUMIDITY_AVG_8 },
{ 16, HTS221_HUMIDITY_AVG_16 },
{ 32, HTS221_HUMIDITY_AVG_32 },
{ 64, HTS221_HUMIDITY_AVG_64 },
{ 128, HTS221_HUMIDITY_AVG_128 },
{ 256, HTS221_HUMIDITY_AVG_256 },
{ 512, HTS221_HUMIDITY_AVG_512 },
},
},
{
.addr = HTS221_REG_AVG_ADDR,
.mask = HTS221_TEMP_AVG_MASK,
.avg_avl = {
{ 2, HTS221_TEMP_AVG_2 },
{ 4, HTS221_TEMP_AVG_4 },
{ 8, HTS221_TEMP_AVG_8 },
{ 16, HTS221_TEMP_AVG_16 },
{ 32, HTS221_TEMP_AVG_32 },
{ 64, HTS221_TEMP_AVG_64 },
{ 128, HTS221_TEMP_AVG_128 },
{ 256, HTS221_TEMP_AVG_256 },
},
},
};
static const struct iio_chan_spec hts221_channels[] = {
{
.type = IIO_HUMIDITYRELATIVE,
.address = HTS221_REG_H_OUT_L,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_OFFSET) |
BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
.scan_index = 0,
.scan_type = {
.sign = 's',
.realbits = 16,
.storagebits = 16,
.endianness = IIO_LE,
},
},
{
.type = IIO_TEMP,
.address = HTS221_REG_T_OUT_L,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_OFFSET) |
BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
.scan_index = 1,
.scan_type = {
.sign = 's',
.realbits = 16,
.storagebits = 16,
.endianness = IIO_LE,
},
},
IIO_CHAN_SOFT_TIMESTAMP(2),
};
static int hts221_write_with_mask(struct hts221_hw *hw, u8 addr, u8 mask,
u8 val)
{
u8 data;
int err;
mutex_lock(&hw->lock);
err = hw->tf->read(hw->dev, addr, sizeof(data), &data);
if (err < 0) {
dev_err(hw->dev, "failed to read %02x register\n", addr);
goto unlock;
}
data = (data & ~mask) | (val & mask);
err = hw->tf->write(hw->dev, addr, sizeof(data), &data);
if (err < 0)
dev_err(hw->dev, "failed to write %02x register\n", addr);
unlock:
mutex_unlock(&hw->lock);
return err;
}
static int hts221_check_whoami(struct hts221_hw *hw)
{
u8 data;
int err;
err = hw->tf->read(hw->dev, HTS221_REG_WHOAMI_ADDR, sizeof(data),
&data);
if (err < 0) {
dev_err(hw->dev, "failed to read whoami register\n");
return err;
}
if (data != HTS221_REG_WHOAMI_VAL) {
dev_err(hw->dev, "wrong whoami {%02x vs %02x}\n",
data, HTS221_REG_WHOAMI_VAL);
return -ENODEV;
}
return 0;
}
int hts221_config_drdy(struct hts221_hw *hw, bool enable)
{
u8 val = enable ? BIT(2) : 0;
int err;
err = hts221_write_with_mask(hw, HTS221_REG_CNTRL3_ADDR,
HTS221_DRDY_MASK, val);
return err < 0 ? err : 0;
}
static int hts221_update_odr(struct hts221_hw *hw, u8 odr)
{
int i, err;
u8 val;
for (i = 0; i < ARRAY_SIZE(hts221_odr_table); i++)
if (hts221_odr_table[i].hz == odr)
break;
if (i == ARRAY_SIZE(hts221_odr_table))
return -EINVAL;
val = HTS221_ENABLE_SENSOR | HTS221_BDU_MASK | hts221_odr_table[i].val;
err = hts221_write_with_mask(hw, HTS221_REG_CNTRL1_ADDR,
HTS221_ODR_MASK, val);
if (err < 0)
return err;
hw->odr = odr;
return 0;
}
static int hts221_update_avg(struct hts221_hw *hw,
enum hts221_sensor_type type,
u16 val)
{
int i, err;
const struct hts221_avg *avg = &hts221_avg_list[type];
for (i = 0; i < HTS221_AVG_DEPTH; i++)
if (avg->avg_avl[i].avg == val)
break;
if (i == HTS221_AVG_DEPTH)
return -EINVAL;
err = hts221_write_with_mask(hw, avg->addr, avg->mask,
avg->avg_avl[i].val);
if (err < 0)
return err;
hw->sensors[type].cur_avg_idx = i;
return 0;
}
static ssize_t hts221_sysfs_sampling_freq(struct device *dev,
struct device_attribute *attr,
char *buf)
{
int i;
ssize_t len = 0;
for (i = 0; i < ARRAY_SIZE(hts221_odr_table); i++)
len += scnprintf(buf + len, PAGE_SIZE - len, "%d ",
hts221_odr_table[i].hz);
buf[len - 1] = '\n';
return len;
}
static ssize_t
hts221_sysfs_rh_oversampling_avail(struct device *dev,
struct device_attribute *attr,
char *buf)
{
const struct hts221_avg *avg = &hts221_avg_list[HTS221_SENSOR_H];
ssize_t len = 0;
int i;
for (i = 0; i < ARRAY_SIZE(avg->avg_avl); i++)
len += scnprintf(buf + len, PAGE_SIZE - len, "%d ",
avg->avg_avl[i].avg);
buf[len - 1] = '\n';
return len;
}
static ssize_t
hts221_sysfs_temp_oversampling_avail(struct device *dev,
struct device_attribute *attr,
char *buf)
{
const struct hts221_avg *avg = &hts221_avg_list[HTS221_SENSOR_T];
ssize_t len = 0;
int i;
for (i = 0; i < ARRAY_SIZE(avg->avg_avl); i++)
len += scnprintf(buf + len, PAGE_SIZE - len, "%d ",
avg->avg_avl[i].avg);
buf[len - 1] = '\n';
return len;
}
int hts221_power_on(struct hts221_hw *hw)
{
return hts221_update_odr(hw, hw->odr);
}
int hts221_power_off(struct hts221_hw *hw)
{
u8 data[] = {0x00, 0x00};
return hw->tf->write(hw->dev, HTS221_REG_CNTRL1_ADDR, sizeof(data),
data);
}
static int hts221_parse_temp_caldata(struct hts221_hw *hw)
{
int err, *slope, *b_gen;
s16 cal_x0, cal_x1, cal_y0, cal_y1;
u8 cal0, cal1;
err = hw->tf->read(hw->dev, HTS221_REG_0T_CAL_Y_H,
sizeof(cal0), &cal0);
if (err < 0)
return err;
err = hw->tf->read(hw->dev, HTS221_REG_T1_T0_CAL_Y_H,
sizeof(cal1), &cal1);
if (err < 0)
return err;
cal_y0 = (le16_to_cpu(cal1 & 0x3) << 8) | cal0;
err = hw->tf->read(hw->dev, HTS221_REG_1T_CAL_Y_H,
sizeof(cal0), &cal0);
if (err < 0)
return err;
cal_y1 = (((cal1 & 0xc) >> 2) << 8) | cal0;
err = hw->tf->read(hw->dev, HTS221_REG_0T_CAL_X_L, sizeof(cal_x0),
(u8 *)&cal_x0);
if (err < 0)
return err;
cal_x0 = le16_to_cpu(cal_x0);
err = hw->tf->read(hw->dev, HTS221_REG_1T_CAL_X_L, sizeof(cal_x1),
(u8 *)&cal_x1);
if (err < 0)
return err;
cal_x1 = le16_to_cpu(cal_x1);
slope = &hw->sensors[HTS221_SENSOR_T].slope;
b_gen = &hw->sensors[HTS221_SENSOR_T].b_gen;
*slope = ((cal_y1 - cal_y0) * 8000) / (cal_x1 - cal_x0);
*b_gen = (((s32)cal_x1 * cal_y0 - (s32)cal_x0 * cal_y1) * 1000) /
(cal_x1 - cal_x0);
*b_gen *= 8;
return 0;
}
static int hts221_parse_rh_caldata(struct hts221_hw *hw)
{
int err, *slope, *b_gen;
s16 cal_x0, cal_x1, cal_y0, cal_y1;
u8 data;
err = hw->tf->read(hw->dev, HTS221_REG_0RH_CAL_Y_H, sizeof(data),
&data);
if (err < 0)
return err;
cal_y0 = data;
err = hw->tf->read(hw->dev, HTS221_REG_1RH_CAL_Y_H, sizeof(data),
&data);
if (err < 0)
return err;
cal_y1 = data;
err = hw->tf->read(hw->dev, HTS221_REG_0RH_CAL_X_H, sizeof(cal_x0),
(u8 *)&cal_x0);
if (err < 0)
return err;
cal_x0 = le16_to_cpu(cal_x0);
err = hw->tf->read(hw->dev, HTS221_REG_1RH_CAL_X_H, sizeof(cal_x1),
(u8 *)&cal_x1);
if (err < 0)
return err;
cal_x1 = le16_to_cpu(cal_x1);
slope = &hw->sensors[HTS221_SENSOR_H].slope;
b_gen = &hw->sensors[HTS221_SENSOR_H].b_gen;
*slope = ((cal_y1 - cal_y0) * 8000) / (cal_x1 - cal_x0);
*b_gen = (((s32)cal_x1 * cal_y0 - (s32)cal_x0 * cal_y1) * 1000) /
(cal_x1 - cal_x0);
*b_gen *= 8;
return 0;
}
static int hts221_get_sensor_scale(struct hts221_hw *hw,
enum iio_chan_type ch_type,
int *val, int *val2)
{
s64 tmp;
s32 rem, div, data;
switch (ch_type) {
case IIO_HUMIDITYRELATIVE:
data = hw->sensors[HTS221_SENSOR_H].slope;
div = (1 << 4) * 1000;
break;
case IIO_TEMP:
data = hw->sensors[HTS221_SENSOR_T].slope;
div = (1 << 6) * 1000;
break;
default:
return -EINVAL;
}
tmp = div_s64(data * 1000000000LL, div);
tmp = div_s64_rem(tmp, 1000000000LL, &rem);
*val = tmp;
*val2 = rem;
return IIO_VAL_INT_PLUS_NANO;
}
static int hts221_get_sensor_offset(struct hts221_hw *hw,
enum iio_chan_type ch_type,
int *val, int *val2)
{
s64 tmp;
s32 rem, div, data;
switch (ch_type) {
case IIO_HUMIDITYRELATIVE:
data = hw->sensors[HTS221_SENSOR_H].b_gen;
div = hw->sensors[HTS221_SENSOR_H].slope;
break;
case IIO_TEMP:
data = hw->sensors[HTS221_SENSOR_T].b_gen;
div = hw->sensors[HTS221_SENSOR_T].slope;
break;
default:
return -EINVAL;
}
tmp = div_s64(data * 1000000000LL, div);
tmp = div_s64_rem(tmp, 1000000000LL, &rem);
*val = tmp;
*val2 = rem;
return IIO_VAL_INT_PLUS_NANO;
}
static int hts221_read_oneshot(struct hts221_hw *hw, u8 addr, int *val)
{
u8 data[HTS221_DATA_SIZE];
int err;
err = hts221_power_on(hw);
if (err < 0)
return err;
msleep(50);
err = hw->tf->read(hw->dev, addr, sizeof(data), data);
if (err < 0)
return err;
hts221_power_off(hw);
*val = (s16)get_unaligned_le16(data);
return IIO_VAL_INT;
}
static int hts221_read_raw(struct iio_dev *iio_dev,
struct iio_chan_spec const *ch,
int *val, int *val2, long mask)
{
struct hts221_hw *hw = iio_priv(iio_dev);
int ret;
ret = iio_device_claim_direct_mode(iio_dev);
if (ret)
return ret;
switch (mask) {
case IIO_CHAN_INFO_RAW:
ret = hts221_read_oneshot(hw, ch->address, val);
break;
case IIO_CHAN_INFO_SCALE:
ret = hts221_get_sensor_scale(hw, ch->type, val, val2);
break;
case IIO_CHAN_INFO_OFFSET:
ret = hts221_get_sensor_offset(hw, ch->type, val, val2);
break;
case IIO_CHAN_INFO_SAMP_FREQ:
*val = hw->odr;
ret = IIO_VAL_INT;
break;
case IIO_CHAN_INFO_OVERSAMPLING_RATIO: {
u8 idx;
const struct hts221_avg *avg;
switch (ch->type) {
case IIO_HUMIDITYRELATIVE:
avg = &hts221_avg_list[HTS221_SENSOR_H];
idx = hw->sensors[HTS221_SENSOR_H].cur_avg_idx;
*val = avg->avg_avl[idx].avg;
ret = IIO_VAL_INT;
break;
case IIO_TEMP:
avg = &hts221_avg_list[HTS221_SENSOR_T];
idx = hw->sensors[HTS221_SENSOR_T].cur_avg_idx;
*val = avg->avg_avl[idx].avg;
ret = IIO_VAL_INT;
break;
default:
ret = -EINVAL;
break;
}
break;
}
default:
ret = -EINVAL;
break;
}
iio_device_release_direct_mode(iio_dev);
return ret;
}
static int hts221_write_raw(struct iio_dev *iio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
struct hts221_hw *hw = iio_priv(iio_dev);
int ret;
ret = iio_device_claim_direct_mode(iio_dev);
if (ret)
return ret;
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
ret = hts221_update_odr(hw, val);
break;
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
switch (chan->type) {
case IIO_HUMIDITYRELATIVE:
ret = hts221_update_avg(hw, HTS221_SENSOR_H, val);
break;
case IIO_TEMP:
ret = hts221_update_avg(hw, HTS221_SENSOR_T, val);
break;
default:
ret = -EINVAL;
break;
}
break;
default:
ret = -EINVAL;
break;
}
iio_device_release_direct_mode(iio_dev);
return ret;
}
static int hts221_validate_trigger(struct iio_dev *iio_dev,
struct iio_trigger *trig)
{
struct hts221_hw *hw = iio_priv(iio_dev);
return hw->trig == trig ? 0 : -EINVAL;
}
static IIO_DEVICE_ATTR(in_humidity_oversampling_ratio_available, S_IRUGO,
hts221_sysfs_rh_oversampling_avail, NULL, 0);
static IIO_DEVICE_ATTR(in_temp_oversampling_ratio_available, S_IRUGO,
hts221_sysfs_temp_oversampling_avail, NULL, 0);
static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(hts221_sysfs_sampling_freq);
static struct attribute *hts221_attributes[] = {
&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
&iio_dev_attr_in_humidity_oversampling_ratio_available.dev_attr.attr,
&iio_dev_attr_in_temp_oversampling_ratio_available.dev_attr.attr,
NULL,
};
static const struct attribute_group hts221_attribute_group = {
.attrs = hts221_attributes,
};
static const struct iio_info hts221_info = {
.driver_module = THIS_MODULE,
.attrs = &hts221_attribute_group,
.read_raw = hts221_read_raw,
.write_raw = hts221_write_raw,
.validate_trigger = hts221_validate_trigger,
};
static const unsigned long hts221_scan_masks[] = {0x3, 0x0};
int hts221_probe(struct iio_dev *iio_dev)
{
struct hts221_hw *hw = iio_priv(iio_dev);
int err;
u8 data;
mutex_init(&hw->lock);
err = hts221_check_whoami(hw);
if (err < 0)
return err;
hw->odr = hts221_odr_table[0].hz;
iio_dev->modes = INDIO_DIRECT_MODE;
iio_dev->dev.parent = hw->dev;
iio_dev->available_scan_masks = hts221_scan_masks;
iio_dev->channels = hts221_channels;
iio_dev->num_channels = ARRAY_SIZE(hts221_channels);
iio_dev->name = HTS221_DEV_NAME;
iio_dev->info = &hts221_info;
/* configure humidity sensor */
err = hts221_parse_rh_caldata(hw);
if (err < 0) {
dev_err(hw->dev, "failed to get rh calibration data\n");
return err;
}
data = hts221_avg_list[HTS221_SENSOR_H].avg_avl[3].avg;
err = hts221_update_avg(hw, HTS221_SENSOR_H, data);
if (err < 0) {
dev_err(hw->dev, "failed to set rh oversampling ratio\n");
return err;
}
/* configure temperature sensor */
err = hts221_parse_temp_caldata(hw);
if (err < 0) {
dev_err(hw->dev,
"failed to get temperature calibration data\n");
return err;
}
data = hts221_avg_list[HTS221_SENSOR_T].avg_avl[3].avg;
err = hts221_update_avg(hw, HTS221_SENSOR_T, data);
if (err < 0) {
dev_err(hw->dev,
"failed to set temperature oversampling ratio\n");
return err;
}
if (hw->irq > 0) {
err = hts221_allocate_buffers(hw);
if (err < 0)
return err;
err = hts221_allocate_trigger(hw);
if (err)
return err;
}
return devm_iio_device_register(hw->dev, iio_dev);
}
EXPORT_SYMBOL(hts221_probe);
MODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi@st.com>");
MODULE_DESCRIPTION("STMicroelectronics hts221 sensor driver");
MODULE_LICENSE("GPL v2");

Просмотреть файл

@ -0,0 +1,110 @@
/*
* STMicroelectronics hts221 i2c driver
*
* Copyright 2016 STMicroelectronics Inc.
*
* Lorenzo Bianconi <lorenzo.bianconi@st.com>
*
* Licensed under the GPL-2.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/slab.h>
#include "hts221.h"
#define I2C_AUTO_INCREMENT 0x80
static int hts221_i2c_read(struct device *dev, u8 addr, int len, u8 *data)
{
struct i2c_msg msg[2];
struct i2c_client *client = to_i2c_client(dev);
if (len > 1)
addr |= I2C_AUTO_INCREMENT;
msg[0].addr = client->addr;
msg[0].flags = client->flags;
msg[0].len = 1;
msg[0].buf = &addr;
msg[1].addr = client->addr;
msg[1].flags = client->flags | I2C_M_RD;
msg[1].len = len;
msg[1].buf = data;
return i2c_transfer(client->adapter, msg, 2);
}
static int hts221_i2c_write(struct device *dev, u8 addr, int len, u8 *data)
{
u8 send[len + 1];
struct i2c_msg msg;
struct i2c_client *client = to_i2c_client(dev);
if (len > 1)
addr |= I2C_AUTO_INCREMENT;
send[0] = addr;
memcpy(&send[1], data, len * sizeof(u8));
msg.addr = client->addr;
msg.flags = client->flags;
msg.len = len + 1;
msg.buf = send;
return i2c_transfer(client->adapter, &msg, 1);
}
static const struct hts221_transfer_function hts221_transfer_fn = {
.read = hts221_i2c_read,
.write = hts221_i2c_write,
};
static int hts221_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct hts221_hw *hw;
struct iio_dev *iio_dev;
iio_dev = devm_iio_device_alloc(&client->dev, sizeof(*hw));
if (!iio_dev)
return -ENOMEM;
i2c_set_clientdata(client, iio_dev);
hw = iio_priv(iio_dev);
hw->name = client->name;
hw->dev = &client->dev;
hw->irq = client->irq;
hw->tf = &hts221_transfer_fn;
return hts221_probe(iio_dev);
}
static const struct of_device_id hts221_i2c_of_match[] = {
{ .compatible = "st,hts221", },
{},
};
MODULE_DEVICE_TABLE(of, hts221_i2c_of_match);
static const struct i2c_device_id hts221_i2c_id_table[] = {
{ HTS221_DEV_NAME },
{},
};
MODULE_DEVICE_TABLE(i2c, hts221_i2c_id_table);
static struct i2c_driver hts221_driver = {
.driver = {
.name = "hts221_i2c",
.of_match_table = of_match_ptr(hts221_i2c_of_match),
},
.probe = hts221_i2c_probe,
.id_table = hts221_i2c_id_table,
};
module_i2c_driver(hts221_driver);
MODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi@st.com>");
MODULE_DESCRIPTION("STMicroelectronics hts221 i2c driver");
MODULE_LICENSE("GPL v2");

Просмотреть файл

@ -0,0 +1,125 @@
/*
* STMicroelectronics hts221 spi driver
*
* Copyright 2016 STMicroelectronics Inc.
*
* Lorenzo Bianconi <lorenzo.bianconi@st.com>
*
* Licensed under the GPL-2.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/spi/spi.h>
#include <linux/slab.h>
#include "hts221.h"
#define SENSORS_SPI_READ 0x80
#define SPI_AUTO_INCREMENT 0x40
static int hts221_spi_read(struct device *dev, u8 addr, int len, u8 *data)
{
int err;
struct spi_device *spi = to_spi_device(dev);
struct iio_dev *iio_dev = spi_get_drvdata(spi);
struct hts221_hw *hw = iio_priv(iio_dev);
struct spi_transfer xfers[] = {
{
.tx_buf = hw->tb.tx_buf,
.bits_per_word = 8,
.len = 1,
},
{
.rx_buf = hw->tb.rx_buf,
.bits_per_word = 8,
.len = len,
}
};
if (len > 1)
addr |= SPI_AUTO_INCREMENT;
hw->tb.tx_buf[0] = addr | SENSORS_SPI_READ;
err = spi_sync_transfer(spi, xfers, ARRAY_SIZE(xfers));
if (err < 0)
return err;
memcpy(data, hw->tb.rx_buf, len * sizeof(u8));
return len;
}
static int hts221_spi_write(struct device *dev, u8 addr, int len, u8 *data)
{
struct spi_device *spi = to_spi_device(dev);
struct iio_dev *iio_dev = spi_get_drvdata(spi);
struct hts221_hw *hw = iio_priv(iio_dev);
struct spi_transfer xfers = {
.tx_buf = hw->tb.tx_buf,
.bits_per_word = 8,
.len = len + 1,
};
if (len >= HTS221_TX_MAX_LENGTH)
return -ENOMEM;
if (len > 1)
addr |= SPI_AUTO_INCREMENT;
hw->tb.tx_buf[0] = addr;
memcpy(&hw->tb.tx_buf[1], data, len);
return spi_sync_transfer(spi, &xfers, 1);
}
static const struct hts221_transfer_function hts221_transfer_fn = {
.read = hts221_spi_read,
.write = hts221_spi_write,
};
static int hts221_spi_probe(struct spi_device *spi)
{
struct hts221_hw *hw;
struct iio_dev *iio_dev;
iio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*hw));
if (!iio_dev)
return -ENOMEM;
spi_set_drvdata(spi, iio_dev);
hw = iio_priv(iio_dev);
hw->name = spi->modalias;
hw->dev = &spi->dev;
hw->irq = spi->irq;
hw->tf = &hts221_transfer_fn;
return hts221_probe(iio_dev);
}
static const struct of_device_id hts221_spi_of_match[] = {
{ .compatible = "st,hts221", },
{},
};
MODULE_DEVICE_TABLE(of, hts221_spi_of_match);
static const struct spi_device_id hts221_spi_id_table[] = {
{ HTS221_DEV_NAME },
{},
};
MODULE_DEVICE_TABLE(spi, hts221_spi_id_table);
static struct spi_driver hts221_driver = {
.driver = {
.name = "hts221_spi",
.of_match_table = of_match_ptr(hts221_spi_of_match),
},
.probe = hts221_spi_probe,
.id_table = hts221_spi_id_table,
};
module_spi_driver(hts221_driver);
MODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi@st.com>");
MODULE_DESCRIPTION("STMicroelectronics hts221 spi driver");
MODULE_LICENSE("GPL v2");

Просмотреть файл

@ -154,8 +154,17 @@ static const struct i2c_device_id si7020_id[] = {
};
MODULE_DEVICE_TABLE(i2c, si7020_id);
static const struct of_device_id si7020_dt_ids[] = {
{ .compatible = "silabs,si7020" },
{ }
};
MODULE_DEVICE_TABLE(of, si7020_dt_ids);
static struct i2c_driver si7020_driver = {
.driver.name = "si7020",
.driver = {
.name = "si7020",
.of_match_table = of_match_ptr(si7020_dt_ids),
},
.probe = si7020_probe,
.id_table = si7020_id,
};

Просмотреть файл

@ -398,7 +398,8 @@ static irqreturn_t bmi160_trigger_handler(int irq, void *p)
struct iio_poll_func *pf = p;
struct iio_dev *indio_dev = pf->indio_dev;
struct bmi160_data *data = iio_priv(indio_dev);
s16 buf[16]; /* 3 sens x 3 axis x s16 + 3 x s16 pad + 4 x s16 tstamp */
__le16 buf[16];
/* 3 sens x 3 axis x __le16 + 3 x __le16 pad + 4 x __le16 tstamp */
int i, ret, j = 0, base = BMI160_REG_DATA_MAGN_XOUT_L;
__le16 sample;

Просмотреть файл

@ -126,7 +126,7 @@ static int inv_mpu_probe(struct i2c_client *client,
st = iio_priv(dev_get_drvdata(&client->dev));
st->muxc = i2c_mux_alloc(client->adapter, &client->dev,
1, 0, I2C_MUX_LOCKED,
1, 0, I2C_MUX_LOCKED | I2C_MUX_GATE,
inv_mpu6050_select_bypass,
inv_mpu6050_deselect_bypass);
if (!st->muxc) {

Просмотреть файл

@ -307,10 +307,9 @@ static int iio_scan_mask_set(struct iio_dev *indio_dev,
const unsigned long *mask;
unsigned long *trialmask;
trialmask = kmalloc(sizeof(*trialmask)*
BITS_TO_LONGS(indio_dev->masklength),
GFP_KERNEL);
trialmask = kmalloc_array(BITS_TO_LONGS(indio_dev->masklength),
sizeof(*trialmask),
GFP_KERNEL);
if (trialmask == NULL)
return -ENOMEM;
if (!indio_dev->masklength) {

Просмотреть файл

@ -81,6 +81,8 @@ static const char * const iio_chan_type_name_spec[] = {
[IIO_PH] = "ph",
[IIO_UVINDEX] = "uvindex",
[IIO_ELECTRICALCONDUCTIVITY] = "electricalconductivity",
[IIO_COUNT] = "count",
[IIO_INDEX] = "index",
};
static const char * const iio_modifier_names[] = {
@ -575,9 +577,62 @@ int of_iio_read_mount_matrix(const struct device *dev,
#endif
EXPORT_SYMBOL(of_iio_read_mount_matrix);
static ssize_t __iio_format_value(char *buf, size_t len, unsigned int type,
int size, const int *vals)
{
unsigned long long tmp;
int tmp0, tmp1;
bool scale_db = false;
switch (type) {
case IIO_VAL_INT:
return snprintf(buf, len, "%d", vals[0]);
case IIO_VAL_INT_PLUS_MICRO_DB:
scale_db = true;
case IIO_VAL_INT_PLUS_MICRO:
if (vals[1] < 0)
return snprintf(buf, len, "-%d.%06u%s", abs(vals[0]),
-vals[1], scale_db ? " dB" : "");
else
return snprintf(buf, len, "%d.%06u%s", vals[0], vals[1],
scale_db ? " dB" : "");
case IIO_VAL_INT_PLUS_NANO:
if (vals[1] < 0)
return snprintf(buf, len, "-%d.%09u", abs(vals[0]),
-vals[1]);
else
return snprintf(buf, len, "%d.%09u", vals[0], vals[1]);
case IIO_VAL_FRACTIONAL:
tmp = div_s64((s64)vals[0] * 1000000000LL, vals[1]);
tmp1 = vals[1];
tmp0 = (int)div_s64_rem(tmp, 1000000000, &tmp1);
return snprintf(buf, len, "%d.%09u", tmp0, abs(tmp1));
case IIO_VAL_FRACTIONAL_LOG2:
tmp = (s64)vals[0] * 1000000000LL >> vals[1];
tmp1 = do_div(tmp, 1000000000LL);
tmp0 = tmp;
return snprintf(buf, len, "%d.%09u", tmp0, tmp1);
case IIO_VAL_INT_MULTIPLE:
{
int i;
int l = 0;
for (i = 0; i < size; ++i) {
l += snprintf(&buf[l], len - l, "%d ", vals[i]);
if (l >= len)
break;
}
return l;
}
default:
return 0;
}
}
/**
* iio_format_value() - Formats a IIO value into its string representation
* @buf: The buffer to which the formatted value gets written
* which is assumed to be big enough (i.e. PAGE_SIZE).
* @type: One of the IIO_VAL_... constants. This decides how the val
* and val2 parameters are formatted.
* @size: Number of IIO value entries contained in vals
@ -590,50 +645,13 @@ EXPORT_SYMBOL(of_iio_read_mount_matrix);
*/
ssize_t iio_format_value(char *buf, unsigned int type, int size, int *vals)
{
unsigned long long tmp;
bool scale_db = false;
ssize_t len;
switch (type) {
case IIO_VAL_INT:
return sprintf(buf, "%d\n", vals[0]);
case IIO_VAL_INT_PLUS_MICRO_DB:
scale_db = true;
case IIO_VAL_INT_PLUS_MICRO:
if (vals[1] < 0)
return sprintf(buf, "-%d.%06u%s\n", abs(vals[0]),
-vals[1], scale_db ? " dB" : "");
else
return sprintf(buf, "%d.%06u%s\n", vals[0], vals[1],
scale_db ? " dB" : "");
case IIO_VAL_INT_PLUS_NANO:
if (vals[1] < 0)
return sprintf(buf, "-%d.%09u\n", abs(vals[0]),
-vals[1]);
else
return sprintf(buf, "%d.%09u\n", vals[0], vals[1]);
case IIO_VAL_FRACTIONAL:
tmp = div_s64((s64)vals[0] * 1000000000LL, vals[1]);
vals[0] = (int)div_s64_rem(tmp, 1000000000, &vals[1]);
return sprintf(buf, "%d.%09u\n", vals[0], abs(vals[1]));
case IIO_VAL_FRACTIONAL_LOG2:
tmp = (s64)vals[0] * 1000000000LL >> vals[1];
vals[1] = do_div(tmp, 1000000000LL);
vals[0] = tmp;
return sprintf(buf, "%d.%09u\n", vals[0], vals[1]);
case IIO_VAL_INT_MULTIPLE:
{
int i;
int len = 0;
len = __iio_format_value(buf, PAGE_SIZE, type, size, vals);
if (len >= PAGE_SIZE - 1)
return -EFBIG;
for (i = 0; i < size; ++i)
len += snprintf(&buf[len], PAGE_SIZE - len, "%d ",
vals[i]);
len += snprintf(&buf[len], PAGE_SIZE - len, "\n");
return len;
}
default:
return 0;
}
return len + sprintf(buf + len, "\n");
}
EXPORT_SYMBOL_GPL(iio_format_value);
@ -662,6 +680,119 @@ static ssize_t iio_read_channel_info(struct device *dev,
return iio_format_value(buf, ret, val_len, vals);
}
static ssize_t iio_format_avail_list(char *buf, const int *vals,
int type, int length)
{
int i;
ssize_t len = 0;
switch (type) {
case IIO_VAL_INT:
for (i = 0; i < length; i++) {
len += __iio_format_value(buf + len, PAGE_SIZE - len,
type, 1, &vals[i]);
if (len >= PAGE_SIZE)
return -EFBIG;
if (i < length - 1)
len += snprintf(buf + len, PAGE_SIZE - len,
" ");
else
len += snprintf(buf + len, PAGE_SIZE - len,
"\n");
if (len >= PAGE_SIZE)
return -EFBIG;
}
break;
default:
for (i = 0; i < length / 2; i++) {
len += __iio_format_value(buf + len, PAGE_SIZE - len,
type, 2, &vals[i * 2]);
if (len >= PAGE_SIZE)
return -EFBIG;
if (i < length / 2 - 1)
len += snprintf(buf + len, PAGE_SIZE - len,
" ");
else
len += snprintf(buf + len, PAGE_SIZE - len,
"\n");
if (len >= PAGE_SIZE)
return -EFBIG;
}
}
return len;
}
static ssize_t iio_format_avail_range(char *buf, const int *vals, int type)
{
int i;
ssize_t len;
len = snprintf(buf, PAGE_SIZE, "[");
switch (type) {
case IIO_VAL_INT:
for (i = 0; i < 3; i++) {
len += __iio_format_value(buf + len, PAGE_SIZE - len,
type, 1, &vals[i]);
if (len >= PAGE_SIZE)
return -EFBIG;
if (i < 2)
len += snprintf(buf + len, PAGE_SIZE - len,
" ");
else
len += snprintf(buf + len, PAGE_SIZE - len,
"]\n");
if (len >= PAGE_SIZE)
return -EFBIG;
}
break;
default:
for (i = 0; i < 3; i++) {
len += __iio_format_value(buf + len, PAGE_SIZE - len,
type, 2, &vals[i * 2]);
if (len >= PAGE_SIZE)
return -EFBIG;
if (i < 2)
len += snprintf(buf + len, PAGE_SIZE - len,
" ");
else
len += snprintf(buf + len, PAGE_SIZE - len,
"]\n");
if (len >= PAGE_SIZE)
return -EFBIG;
}
}
return len;
}
static ssize_t iio_read_channel_info_avail(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
const int *vals;
int ret;
int length;
int type;
ret = indio_dev->info->read_avail(indio_dev, this_attr->c,
&vals, &type, &length,
this_attr->address);
if (ret < 0)
return ret;
switch (ret) {
case IIO_AVAIL_LIST:
return iio_format_avail_list(buf, vals, type, length);
case IIO_AVAIL_RANGE:
return iio_format_avail_range(buf, vals, type);
default:
return -EINVAL;
}
}
/**
* iio_str_to_fixpoint() - Parse a fixed-point number from a string
* @str: The string to parse
@ -978,6 +1109,40 @@ static int iio_device_add_info_mask_type(struct iio_dev *indio_dev,
return attrcount;
}
static int iio_device_add_info_mask_type_avail(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
enum iio_shared_by shared_by,
const long *infomask)
{
int i, ret, attrcount = 0;
char *avail_postfix;
for_each_set_bit(i, infomask, sizeof(infomask) * 8) {
avail_postfix = kasprintf(GFP_KERNEL,
"%s_available",
iio_chan_info_postfix[i]);
if (!avail_postfix)
return -ENOMEM;
ret = __iio_add_chan_devattr(avail_postfix,
chan,
&iio_read_channel_info_avail,
NULL,
i,
shared_by,
&indio_dev->dev,
&indio_dev->channel_attr_list);
kfree(avail_postfix);
if ((ret == -EBUSY) && (shared_by != IIO_SEPARATE))
continue;
else if (ret < 0)
return ret;
attrcount++;
}
return attrcount;
}
static int iio_device_add_channel_sysfs(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan)
{
@ -993,6 +1158,14 @@ static int iio_device_add_channel_sysfs(struct iio_dev *indio_dev,
return ret;
attrcount += ret;
ret = iio_device_add_info_mask_type_avail(indio_dev, chan,
IIO_SEPARATE,
&chan->
info_mask_separate_available);
if (ret < 0)
return ret;
attrcount += ret;
ret = iio_device_add_info_mask_type(indio_dev, chan,
IIO_SHARED_BY_TYPE,
&chan->info_mask_shared_by_type);
@ -1000,6 +1173,14 @@ static int iio_device_add_channel_sysfs(struct iio_dev *indio_dev,
return ret;
attrcount += ret;
ret = iio_device_add_info_mask_type_avail(indio_dev, chan,
IIO_SHARED_BY_TYPE,
&chan->
info_mask_shared_by_type_available);
if (ret < 0)
return ret;
attrcount += ret;
ret = iio_device_add_info_mask_type(indio_dev, chan,
IIO_SHARED_BY_DIR,
&chan->info_mask_shared_by_dir);
@ -1007,6 +1188,13 @@ static int iio_device_add_channel_sysfs(struct iio_dev *indio_dev,
return ret;
attrcount += ret;
ret = iio_device_add_info_mask_type_avail(indio_dev, chan,
IIO_SHARED_BY_DIR,
&chan->info_mask_shared_by_dir_available);
if (ret < 0)
return ret;
attrcount += ret;
ret = iio_device_add_info_mask_type(indio_dev, chan,
IIO_SHARED_BY_ALL,
&chan->info_mask_shared_by_all);
@ -1014,6 +1202,13 @@ static int iio_device_add_channel_sysfs(struct iio_dev *indio_dev,
return ret;
attrcount += ret;
ret = iio_device_add_info_mask_type_avail(indio_dev, chan,
IIO_SHARED_BY_ALL,
&chan->info_mask_shared_by_all_available);
if (ret < 0)
return ret;
attrcount += ret;
if (chan->ext_info) {
unsigned int i = 0;
for (ext_info = chan->ext_info; ext_info->name; ext_info++) {

Просмотреть файл

@ -717,6 +717,27 @@ bool iio_trigger_using_own(struct iio_dev *indio_dev)
}
EXPORT_SYMBOL(iio_trigger_using_own);
/**
* iio_trigger_validate_own_device - Check if a trigger and IIO device belong to
* the same device
* @trig: The IIO trigger to check
* @indio_dev: the IIO device to check
*
* This function can be used as the validate_device callback for triggers that
* can only be attached to their own device.
*
* Return: 0 if both the trigger and the IIO device belong to the same
* device, -EINVAL otherwise.
*/
int iio_trigger_validate_own_device(struct iio_trigger *trig,
struct iio_dev *indio_dev)
{
if (indio_dev->dev.parent != trig->dev.parent)
return -EINVAL;
return 0;
}
EXPORT_SYMBOL(iio_trigger_validate_own_device);
void iio_device_register_trigger_consumer(struct iio_dev *indio_dev)
{
indio_dev->groups[indio_dev->groupcounter++] =

Просмотреть файл

@ -658,6 +658,31 @@ err_unlock:
}
EXPORT_SYMBOL_GPL(iio_convert_raw_to_processed);
static int iio_read_channel_attribute(struct iio_channel *chan,
int *val, int *val2,
enum iio_chan_info_enum attribute)
{
int ret;
mutex_lock(&chan->indio_dev->info_exist_lock);
if (chan->indio_dev->info == NULL) {
ret = -ENODEV;
goto err_unlock;
}
ret = iio_channel_read(chan, val, val2, attribute);
err_unlock:
mutex_unlock(&chan->indio_dev->info_exist_lock);
return ret;
}
int iio_read_channel_offset(struct iio_channel *chan, int *val, int *val2)
{
return iio_read_channel_attribute(chan, val, val2, IIO_CHAN_INFO_OFFSET);
}
EXPORT_SYMBOL_GPL(iio_read_channel_offset);
int iio_read_channel_processed(struct iio_channel *chan, int *val)
{
int ret;
@ -686,22 +711,114 @@ err_unlock:
EXPORT_SYMBOL_GPL(iio_read_channel_processed);
int iio_read_channel_scale(struct iio_channel *chan, int *val, int *val2)
{
return iio_read_channel_attribute(chan, val, val2, IIO_CHAN_INFO_SCALE);
}
EXPORT_SYMBOL_GPL(iio_read_channel_scale);
static int iio_channel_read_avail(struct iio_channel *chan,
const int **vals, int *type, int *length,
enum iio_chan_info_enum info)
{
if (!iio_channel_has_available(chan->channel, info))
return -EINVAL;
return chan->indio_dev->info->read_avail(chan->indio_dev, chan->channel,
vals, type, length, info);
}
int iio_read_avail_channel_raw(struct iio_channel *chan,
const int **vals, int *length)
{
int ret;
int type;
mutex_lock(&chan->indio_dev->info_exist_lock);
if (chan->indio_dev->info == NULL) {
if (!chan->indio_dev->info) {
ret = -ENODEV;
goto err_unlock;
}
ret = iio_channel_read(chan, val, val2, IIO_CHAN_INFO_SCALE);
ret = iio_channel_read_avail(chan,
vals, &type, length, IIO_CHAN_INFO_RAW);
err_unlock:
mutex_unlock(&chan->indio_dev->info_exist_lock);
if (ret >= 0 && type != IIO_VAL_INT) {
/* raw values are assumed to be IIO_VAL_INT */
ret = -EINVAL;
goto err_unlock;
}
return ret;
}
EXPORT_SYMBOL_GPL(iio_read_avail_channel_raw);
static int iio_channel_read_max(struct iio_channel *chan,
int *val, int *val2, int *type,
enum iio_chan_info_enum info)
{
int unused;
const int *vals;
int length;
int ret;
if (!val2)
val2 = &unused;
ret = iio_channel_read_avail(chan, &vals, type, &length, info);
switch (ret) {
case IIO_AVAIL_RANGE:
switch (*type) {
case IIO_VAL_INT:
*val = vals[2];
break;
default:
*val = vals[4];
*val2 = vals[5];
}
return 0;
case IIO_AVAIL_LIST:
if (length <= 0)
return -EINVAL;
switch (*type) {
case IIO_VAL_INT:
*val = vals[--length];
while (length) {
if (vals[--length] > *val)
*val = vals[length];
}
break;
default:
/* FIXME: learn about max for other iio values */
return -EINVAL;
}
return 0;
default:
return ret;
}
}
int iio_read_max_channel_raw(struct iio_channel *chan, int *val)
{
int ret;
int type;
mutex_lock(&chan->indio_dev->info_exist_lock);
if (!chan->indio_dev->info) {
ret = -ENODEV;
goto err_unlock;
}
ret = iio_channel_read_max(chan, val, NULL, &type, IIO_CHAN_INFO_RAW);
err_unlock:
mutex_unlock(&chan->indio_dev->info_exist_lock);
return ret;
}
EXPORT_SYMBOL_GPL(iio_read_channel_scale);
EXPORT_SYMBOL_GPL(iio_read_max_channel_raw);
int iio_get_channel_type(struct iio_channel *chan, enum iio_chan_type *type)
{

Просмотреть файл

@ -140,6 +140,18 @@ config GP2AP020A00F
To compile this driver as a module, choose M here: the
module will be called gp2ap020a00f.
config SENSORS_ISL29018
tristate "Intersil 29018 light and proximity sensor"
depends on I2C
select REGMAP_I2C
default n
help
If you say yes here you get support for ambient light sensing and
proximity infrared sensing from Intersil ISL29018.
This driver will provide the measurements of ambient light intensity
in lux, proximity infrared sensing and normal infrared sensing.
Data from sensor is accessible via sysfs.
config ISL29125
tristate "Intersil ISL29125 digital color light sensor"
depends on I2C
@ -326,6 +338,13 @@ config SENSORS_TSL2563
This driver can also be built as a module. If so, the module
will be called tsl2563.
config TSL2583
tristate "TAOS TSL2580, TSL2581 and TSL2583 light-to-digital converters"
depends on I2C
help
Provides support for the TAOS tsl2580, tsl2581 and tsl2583 devices.
Access ALS data via iio, sysfs.
config TSL4531
tristate "TAOS TSL4531 ambient light sensors"
depends on I2C

Просмотреть файл

@ -17,6 +17,7 @@ obj-$(CONFIG_CM36651) += cm36651.o
obj-$(CONFIG_GP2AP020A00F) += gp2ap020a00f.o
obj-$(CONFIG_HID_SENSOR_ALS) += hid-sensor-als.o
obj-$(CONFIG_HID_SENSOR_PROX) += hid-sensor-prox.o
obj-$(CONFIG_SENSORS_ISL29018) += isl29018.o
obj-$(CONFIG_ISL29125) += isl29125.o
obj-$(CONFIG_JSA1212) += jsa1212.o
obj-$(CONFIG_SENSORS_LM3533) += lm3533-als.o
@ -30,6 +31,7 @@ obj-$(CONFIG_SI1145) += si1145.o
obj-$(CONFIG_STK3310) += stk3310.o
obj-$(CONFIG_TCS3414) += tcs3414.o
obj-$(CONFIG_TCS3472) += tcs3472.o
obj-$(CONFIG_TSL2583) += tsl2583.o
obj-$(CONFIG_TSL4531) += tsl4531.o
obj-$(CONFIG_US5182D) += us5182d.o
obj-$(CONFIG_VCNL4000) += vcnl4000.o

Просмотреть файл

@ -62,16 +62,6 @@
#define ISL29035_BOUT_SHIFT 0x07
#define ISL29035_BOUT_MASK (0x01 << ISL29035_BOUT_SHIFT)
#define ISL29018_INT_TIME_AVAIL "0.090000 0.005630 0.000351 0.000021"
#define ISL29023_INT_TIME_AVAIL "0.090000 0.005600 0.000352 0.000022"
#define ISL29035_INT_TIME_AVAIL "0.105000 0.006500 0.000410 0.000025"
static const char * const int_time_avail[] = {
ISL29018_INT_TIME_AVAIL,
ISL29023_INT_TIME_AVAIL,
ISL29035_INT_TIME_AVAIL,
};
enum isl29018_int_time {
ISL29018_INT_TIME_16,
ISL29018_INT_TIME_12,
@ -110,7 +100,8 @@ struct isl29018_chip {
static int isl29018_set_integration_time(struct isl29018_chip *chip,
unsigned int utime)
{
int i, ret;
unsigned int i;
int ret;
unsigned int int_time, new_int_time;
for (i = 0; i < ARRAY_SIZE(isl29018_int_utimes[chip->type]); ++i) {
@ -145,7 +136,8 @@ static int isl29018_set_integration_time(struct isl29018_chip *chip,
static int isl29018_set_scale(struct isl29018_chip *chip, int scale, int uscale)
{
int i, ret;
unsigned int i;
int ret;
struct isl29018_scale new_scale;
for (i = 0; i < ARRAY_SIZE(isl29018_scales[chip->int_time]); ++i) {
@ -276,29 +268,35 @@ static int isl29018_read_proximity_ir(struct isl29018_chip *chip, int scheme,
return 0;
}
static ssize_t isl29018_show_scale_available(struct device *dev,
struct device_attribute *attr, char *buf)
static ssize_t in_illuminance_scale_available_show
(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct isl29018_chip *chip = iio_priv(indio_dev);
int i, len = 0;
unsigned int i;
int len = 0;
mutex_lock(&chip->lock);
for (i = 0; i < ARRAY_SIZE(isl29018_scales[chip->int_time]); ++i)
len += sprintf(buf + len, "%d.%06d ",
isl29018_scales[chip->int_time][i].scale,
isl29018_scales[chip->int_time][i].uscale);
mutex_unlock(&chip->lock);
buf[len - 1] = '\n';
return len;
}
static ssize_t isl29018_show_int_time_available(struct device *dev,
struct device_attribute *attr, char *buf)
static ssize_t in_illuminance_integration_time_available_show
(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct isl29018_chip *chip = iio_priv(indio_dev);
int i, len = 0;
unsigned int i;
int len = 0;
for (i = 0; i < ARRAY_SIZE(isl29018_int_utimes[chip->type]); ++i)
len += sprintf(buf + len, "0.%06d ",
@ -309,9 +307,27 @@ static ssize_t isl29018_show_int_time_available(struct device *dev,
return len;
}
static ssize_t isl29018_show_prox_infrared_suppression(struct device *dev,
struct device_attribute *attr,
char *buf)
/*
* From ISL29018 Data Sheet (FN6619.4, Oct 8, 2012) regarding the
* infrared suppression:
*
* Proximity Sensing Scheme: Bit 7. This bit programs the function
* of the proximity detection. Logic 0 of this bit, Scheme 0, makes
* full n (4, 8, 12, 16) bits (unsigned) proximity detection. The range
* of Scheme 0 proximity count is from 0 to 2^n. Logic 1 of this bit,
* Scheme 1, makes n-1 (3, 7, 11, 15) bits (2's complementary)
* proximity_less_ambient detection. The range of Scheme 1
* proximity count is from -2^(n-1) to 2^(n-1) . The sign bit is extended
* for resolutions less than 16. While Scheme 0 has wider dynamic
* range, Scheme 1 proximity detection is less affected by the
* ambient IR noise variation.
*
* 0 Sensing IR from LED and ambient
* 1 Sensing IR from LED with ambient IR rejection
*/
static ssize_t proximity_on_chip_ambient_infrared_suppression_show
(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct isl29018_chip *chip = iio_priv(indio_dev);
@ -323,9 +339,9 @@ static ssize_t isl29018_show_prox_infrared_suppression(struct device *dev,
return sprintf(buf, "%d\n", chip->prox_scheme);
}
static ssize_t isl29018_store_prox_infrared_suppression(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
static ssize_t proximity_on_chip_ambient_infrared_suppression_store
(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct isl29018_chip *chip = iio_priv(indio_dev);
@ -357,6 +373,10 @@ static int isl29018_write_raw(struct iio_dev *indio_dev,
int ret = -EINVAL;
mutex_lock(&chip->lock);
if (chip->suspended) {
ret = -EBUSY;
goto write_done;
}
switch (mask) {
case IIO_CHAN_INFO_CALIBSCALE:
if (chan->type == IIO_LIGHT) {
@ -366,13 +386,8 @@ static int isl29018_write_raw(struct iio_dev *indio_dev,
}
break;
case IIO_CHAN_INFO_INT_TIME:
if (chan->type == IIO_LIGHT) {
if (val) {
mutex_unlock(&chip->lock);
return -EINVAL;
}
if (chan->type == IIO_LIGHT && !val)
ret = isl29018_set_integration_time(chip, val2);
}
break;
case IIO_CHAN_INFO_SCALE:
if (chan->type == IIO_LIGHT)
@ -381,6 +396,8 @@ static int isl29018_write_raw(struct iio_dev *indio_dev,
default:
break;
}
write_done:
mutex_unlock(&chip->lock);
return ret;
@ -397,8 +414,8 @@ static int isl29018_read_raw(struct iio_dev *indio_dev,
mutex_lock(&chip->lock);
if (chip->suspended) {
mutex_unlock(&chip->lock);
return -EBUSY;
ret = -EBUSY;
goto read_done;
}
switch (mask) {
case IIO_CHAN_INFO_RAW:
@ -445,7 +462,10 @@ static int isl29018_read_raw(struct iio_dev *indio_dev,
default:
break;
}
read_done:
mutex_unlock(&chip->lock);
return ret;
}
@ -482,14 +502,9 @@ static const struct iio_chan_spec isl29023_channels[] = {
ISL29018_IR_CHANNEL,
};
static IIO_DEVICE_ATTR(in_illuminance_integration_time_available, S_IRUGO,
isl29018_show_int_time_available, NULL, 0);
static IIO_DEVICE_ATTR(in_illuminance_scale_available, S_IRUGO,
isl29018_show_scale_available, NULL, 0);
static IIO_DEVICE_ATTR(proximity_on_chip_ambient_infrared_suppression,
S_IRUGO | S_IWUSR,
isl29018_show_prox_infrared_suppression,
isl29018_store_prox_infrared_suppression, 0);
static IIO_DEVICE_ATTR_RO(in_illuminance_integration_time_available, 0);
static IIO_DEVICE_ATTR_RO(in_illuminance_scale_available, 0);
static IIO_DEVICE_ATTR_RW(proximity_on_chip_ambient_infrared_suppression, 0);
#define ISL29018_DEV_ATTR(name) (&iio_dev_attr_##name.dev_attr.attr)
@ -514,30 +529,6 @@ static const struct attribute_group isl29023_group = {
.attrs = isl29023_attributes,
};
static int isl29035_detect(struct isl29018_chip *chip)
{
int status;
unsigned int id;
struct device *dev = regmap_get_device(chip->regmap);
status = regmap_read(chip->regmap, ISL29035_REG_DEVICE_ID, &id);
if (status < 0) {
dev_err(dev,
"Error reading ID register with error %d\n",
status);
return status;
}
id = (id & ISL29035_DEVICE_ID_MASK) >> ISL29035_DEVICE_ID_SHIFT;
if (id != ISL29035_DEVICE_ID)
return -ENODEV;
/* Clear brownout bit */
return regmap_update_bits(chip->regmap, ISL29035_REG_DEVICE_ID,
ISL29035_BOUT_MASK, 0);
}
enum {
isl29018,
isl29023,
@ -550,12 +541,31 @@ static int isl29018_chip_init(struct isl29018_chip *chip)
struct device *dev = regmap_get_device(chip->regmap);
if (chip->type == isl29035) {
status = isl29035_detect(chip);
unsigned int id;
status = regmap_read(chip->regmap, ISL29035_REG_DEVICE_ID, &id);
if (status < 0) {
dev_err(dev,
"Error reading ID register with error %d\n",
status);
return status;
}
id = (id & ISL29035_DEVICE_ID_MASK) >> ISL29035_DEVICE_ID_SHIFT;
if (id != ISL29035_DEVICE_ID)
return -ENODEV;
/* Clear brownout bit */
status = regmap_update_bits(chip->regmap,
ISL29035_REG_DEVICE_ID,
ISL29035_BOUT_MASK, 0);
if (status < 0)
return status;
}
/* Code added per Intersil Application Note 1534:
/*
* Code added per Intersil Application Note 1534:
* When VDD sinks to approximately 1.8V or below, some of
* the part's registers may change their state. When VDD
* recovers to 2.25V (or greater), the part may thus be in an
@ -582,7 +592,8 @@ static int isl29018_chip_init(struct isl29018_chip *chip)
return status;
}
/* See Intersil AN1534 comments above.
/*
* See Intersil AN1534 comments above.
* "Operating Mode" (COMMAND1) register is reprogrammed when
* data is read from the device.
*/
@ -605,12 +616,10 @@ static int isl29018_chip_init(struct isl29018_chip *chip)
status = isl29018_set_integration_time(chip,
isl29018_int_utimes[chip->type][chip->int_time]);
if (status < 0) {
if (status < 0)
dev_err(dev, "Init of isl29018 fails\n");
return status;
}
return 0;
return status;
}
static const struct iio_info isl29018_info = {
@ -713,6 +722,7 @@ static int isl29018_probe(struct i2c_client *client,
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip));
if (!indio_dev)
return -ENOMEM;
chip = iio_priv(indio_dev);
i2c_set_clientdata(client, indio_dev);
@ -752,6 +762,7 @@ static int isl29018_probe(struct i2c_client *client,
indio_dev->name = name;
indio_dev->dev.parent = &client->dev;
indio_dev->modes = INDIO_DIRECT_MODE;
return devm_iio_device_register(&client->dev, indio_dev);
}
@ -762,13 +773,15 @@ static int isl29018_suspend(struct device *dev)
mutex_lock(&chip->lock);
/* Since this driver uses only polling commands, we are by default in
/*
* Since this driver uses only polling commands, we are by default in
* auto shutdown (ie, power-down) mode.
* So we do not have much to do here.
*/
chip->suspended = true;
mutex_unlock(&chip->lock);
return 0;
}
@ -784,6 +797,7 @@ static int isl29018_resume(struct device *dev)
chip->suspended = false;
mutex_unlock(&chip->lock);
return err;
}
@ -807,7 +821,6 @@ static const struct i2c_device_id isl29018_id[] = {
{"isl29035", isl29035},
{}
};
MODULE_DEVICE_TABLE(i2c, isl29018_id);
static const struct of_device_id isl29018_of_match[] = {

Просмотреть файл

@ -631,14 +631,16 @@ static int ltr501_read_raw(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_PROCESSED:
if (iio_buffer_enabled(indio_dev))
return -EBUSY;
switch (chan->type) {
case IIO_LIGHT:
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
mutex_lock(&data->lock_als);
ret = ltr501_read_als(data, buf);
mutex_unlock(&data->lock_als);
iio_device_release_direct_mode(indio_dev);
if (ret < 0)
return ret;
*val = ltr501_calculate_lux(le16_to_cpu(buf[1]),
@ -648,8 +650,9 @@ static int ltr501_read_raw(struct iio_dev *indio_dev,
return -EINVAL;
}
case IIO_CHAN_INFO_RAW:
if (iio_buffer_enabled(indio_dev))
return -EBUSY;
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
switch (chan->type) {
case IIO_INTENSITY:
@ -657,21 +660,28 @@ static int ltr501_read_raw(struct iio_dev *indio_dev,
ret = ltr501_read_als(data, buf);
mutex_unlock(&data->lock_als);
if (ret < 0)
return ret;
break;
*val = le16_to_cpu(chan->address == LTR501_ALS_DATA1 ?
buf[0] : buf[1]);
return IIO_VAL_INT;
ret = IIO_VAL_INT;
break;
case IIO_PROXIMITY:
mutex_lock(&data->lock_ps);
ret = ltr501_read_ps(data);
mutex_unlock(&data->lock_ps);
if (ret < 0)
return ret;
break;
*val = ret & LTR501_PS_DATA_MASK;
return IIO_VAL_INT;
ret = IIO_VAL_INT;
break;
default:
return -EINVAL;
ret = -EINVAL;
break;
}
iio_device_release_direct_mode(indio_dev);
return ret;
case IIO_CHAN_INFO_SCALE:
switch (chan->type) {
case IIO_INTENSITY:
@ -729,8 +739,9 @@ static int ltr501_write_raw(struct iio_dev *indio_dev,
int i, ret, freq_val, freq_val2;
struct ltr501_chip_info *info = data->chip_info;
if (iio_buffer_enabled(indio_dev))
return -EBUSY;
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
switch (mask) {
case IIO_CHAN_INFO_SCALE:
@ -739,85 +750,105 @@ static int ltr501_write_raw(struct iio_dev *indio_dev,
i = ltr501_get_gain_index(info->als_gain,
info->als_gain_tbl_size,
val, val2);
if (i < 0)
return -EINVAL;
if (i < 0) {
ret = -EINVAL;
break;
}
data->als_contr &= ~info->als_gain_mask;
data->als_contr |= i << info->als_gain_shift;
return regmap_write(data->regmap, LTR501_ALS_CONTR,
data->als_contr);
ret = regmap_write(data->regmap, LTR501_ALS_CONTR,
data->als_contr);
break;
case IIO_PROXIMITY:
i = ltr501_get_gain_index(info->ps_gain,
info->ps_gain_tbl_size,
val, val2);
if (i < 0)
return -EINVAL;
if (i < 0) {
ret = -EINVAL;
break;
}
data->ps_contr &= ~LTR501_CONTR_PS_GAIN_MASK;
data->ps_contr |= i << LTR501_CONTR_PS_GAIN_SHIFT;
return regmap_write(data->regmap, LTR501_PS_CONTR,
data->ps_contr);
ret = regmap_write(data->regmap, LTR501_PS_CONTR,
data->ps_contr);
break;
default:
return -EINVAL;
ret = -EINVAL;
break;
}
break;
case IIO_CHAN_INFO_INT_TIME:
switch (chan->type) {
case IIO_INTENSITY:
if (val != 0)
return -EINVAL;
if (val != 0) {
ret = -EINVAL;
break;
}
mutex_lock(&data->lock_als);
i = ltr501_set_it_time(data, val2);
ret = ltr501_set_it_time(data, val2);
mutex_unlock(&data->lock_als);
return i;
break;
default:
return -EINVAL;
ret = -EINVAL;
break;
}
break;
case IIO_CHAN_INFO_SAMP_FREQ:
switch (chan->type) {
case IIO_INTENSITY:
ret = ltr501_als_read_samp_freq(data, &freq_val,
&freq_val2);
if (ret < 0)
return ret;
break;
ret = ltr501_als_write_samp_freq(data, val, val2);
if (ret < 0)
return ret;
break;
/* update persistence count when changing frequency */
ret = ltr501_write_intr_prst(data, chan->type,
0, data->als_period);
if (ret < 0)
return ltr501_als_write_samp_freq(data,
freq_val,
freq_val2);
return ret;
ret = ltr501_als_write_samp_freq(data, freq_val,
freq_val2);
break;
case IIO_PROXIMITY:
ret = ltr501_ps_read_samp_freq(data, &freq_val,
&freq_val2);
if (ret < 0)
return ret;
break;
ret = ltr501_ps_write_samp_freq(data, val, val2);
if (ret < 0)
return ret;
break;
/* update persistence count when changing frequency */
ret = ltr501_write_intr_prst(data, chan->type,
0, data->ps_period);
if (ret < 0)
return ltr501_ps_write_samp_freq(data,
freq_val,
freq_val2);
return ret;
ret = ltr501_ps_write_samp_freq(data, freq_val,
freq_val2);
break;
default:
return -EINVAL;
ret = -EINVAL;
break;
}
break;
default:
ret = -EINVAL;
break;
}
return -EINVAL;
iio_device_release_direct_mode(indio_dev);
return ret;
}
static int ltr501_read_thresh(struct iio_dev *indio_dev,

Просмотреть файл

@ -204,17 +204,18 @@ static int max44000_write_alspga(struct max44000_data *data, int val)
static int max44000_read_alsval(struct max44000_data *data)
{
u16 regval;
__be16 val;
int alstim, ret;
ret = regmap_bulk_read(data->regmap, MAX44000_REG_ALS_DATA_HI,
&regval, sizeof(regval));
&val, sizeof(val));
if (ret < 0)
return ret;
alstim = ret = max44000_read_alstim(data);
if (ret < 0)
return ret;
regval = be16_to_cpu(regval);
regval = be16_to_cpu(val);
/*
* Overflow is explained on datasheet page 17.

913
drivers/iio/light/tsl2583.c Normal file
Просмотреть файл

@ -0,0 +1,913 @@
/*
* Device driver for monitoring ambient light intensity (lux)
* within the TAOS tsl258x family of devices (tsl2580, tsl2581, tsl2583).
*
* Copyright (c) 2011, TAOS Corporation.
* Copyright (c) 2016 Brian Masney <masneyb@onstation.org>
*
* 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/kernel.h>
#include <linux/i2c.h>
#include <linux/errno.h>
#include <linux/delay.h>
#include <linux/string.h>
#include <linux/mutex.h>
#include <linux/unistd.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
/* Device Registers and Masks */
#define TSL2583_CNTRL 0x00
#define TSL2583_ALS_TIME 0X01
#define TSL2583_INTERRUPT 0x02
#define TSL2583_GAIN 0x07
#define TSL2583_REVID 0x11
#define TSL2583_CHIPID 0x12
#define TSL2583_ALS_CHAN0LO 0x14
#define TSL2583_ALS_CHAN0HI 0x15
#define TSL2583_ALS_CHAN1LO 0x16
#define TSL2583_ALS_CHAN1HI 0x17
#define TSL2583_TMR_LO 0x18
#define TSL2583_TMR_HI 0x19
/* tsl2583 cmd reg masks */
#define TSL2583_CMD_REG 0x80
#define TSL2583_CMD_SPL_FN 0x60
#define TSL2583_CMD_ALS_INT_CLR 0x01
/* tsl2583 cntrl reg masks */
#define TSL2583_CNTL_ADC_ENBL 0x02
#define TSL2583_CNTL_PWR_OFF 0x00
#define TSL2583_CNTL_PWR_ON 0x01
/* tsl2583 status reg masks */
#define TSL2583_STA_ADC_VALID 0x01
#define TSL2583_STA_ADC_INTR 0x10
/* Lux calculation constants */
#define TSL2583_LUX_CALC_OVER_FLOW 65535
#define TSL2583_INTERRUPT_DISABLED 0x00
#define TSL2583_CHIP_ID 0x90
#define TSL2583_CHIP_ID_MASK 0xf0
/* Per-device data */
struct tsl2583_als_info {
u16 als_ch0;
u16 als_ch1;
u16 lux;
};
struct tsl2583_lux {
unsigned int ratio;
unsigned int ch0;
unsigned int ch1;
};
static const struct tsl2583_lux tsl2583_default_lux[] = {
{ 9830, 8520, 15729 },
{ 12452, 10807, 23344 },
{ 14746, 6383, 11705 },
{ 17695, 4063, 6554 },
{ 0, 0, 0 } /* Termination segment */
};
#define TSL2583_MAX_LUX_TABLE_ENTRIES 11
struct tsl2583_settings {
int als_time;
int als_gain;
int als_gain_trim;
int als_cal_target;
/*
* This structure is intentionally large to accommodate updates via
* sysfs. Sized to 11 = max 10 segments + 1 termination segment.
* Assumption is that one and only one type of glass used.
*/
struct tsl2583_lux als_device_lux[TSL2583_MAX_LUX_TABLE_ENTRIES];
};
struct tsl2583_chip {
struct mutex als_mutex;
struct i2c_client *client;
struct tsl2583_als_info als_cur_info;
struct tsl2583_settings als_settings;
int als_time_scale;
int als_saturation;
bool suspended;
};
struct gainadj {
s16 ch0;
s16 ch1;
s16 mean;
};
/* Index = (0 - 3) Used to validate the gain selection index */
static const struct gainadj gainadj[] = {
{ 1, 1, 1 },
{ 8, 8, 8 },
{ 16, 16, 16 },
{ 107, 115, 111 }
};
/*
* Provides initial operational parameter defaults.
* These defaults may be changed through the device's sysfs files.
*/
static void tsl2583_defaults(struct tsl2583_chip *chip)
{
/*
* The integration time must be a multiple of 50ms and within the
* range [50, 600] ms.
*/
chip->als_settings.als_time = 100;
/*
* This is an index into the gainadj table. Assume clear glass as the
* default.
*/
chip->als_settings.als_gain = 0;
/* Default gain trim to account for aperture effects */
chip->als_settings.als_gain_trim = 1000;
/* Known external ALS reading used for calibration */
chip->als_settings.als_cal_target = 130;
/* Default lux table. */
memcpy(chip->als_settings.als_device_lux, tsl2583_default_lux,
sizeof(tsl2583_default_lux));
}
/*
* Reads and calculates current lux value.
* The raw ch0 and ch1 values of the ambient light sensed in the last
* integration cycle are read from the device.
* Time scale factor array values are adjusted based on the integration time.
* The raw values are multiplied by a scale factor, and device gain is obtained
* using gain index. Limit checks are done next, then the ratio of a multiple
* of ch1 value, to the ch0 value, is calculated. The array als_device_lux[]
* declared above is then scanned to find the first ratio value that is just
* above the ratio we just calculated. The ch0 and ch1 multiplier constants in
* the array are then used along with the time scale factor array values, to
* calculate the lux.
*/
static int tsl2583_get_lux(struct iio_dev *indio_dev)
{
u16 ch0, ch1; /* separated ch0/ch1 data from device */
u32 lux; /* raw lux calculated from device data */
u64 lux64;
u32 ratio;
u8 buf[5];
struct tsl2583_lux *p;
struct tsl2583_chip *chip = iio_priv(indio_dev);
int i, ret;
ret = i2c_smbus_read_byte_data(chip->client, TSL2583_CMD_REG);
if (ret < 0) {
dev_err(&chip->client->dev, "%s: failed to read CMD_REG register\n",
__func__);
goto done;
}
/* is data new & valid */
if (!(ret & TSL2583_STA_ADC_INTR)) {
dev_err(&chip->client->dev, "%s: data not valid; returning last value\n",
__func__);
ret = chip->als_cur_info.lux; /* return LAST VALUE */
goto done;
}
for (i = 0; i < 4; i++) {
int reg = TSL2583_CMD_REG | (TSL2583_ALS_CHAN0LO + i);
ret = i2c_smbus_read_byte_data(chip->client, reg);
if (ret < 0) {
dev_err(&chip->client->dev, "%s: failed to read register %x\n",
__func__, reg);
goto done;
}
buf[i] = ret;
}
/*
* Clear the pending interrupt status bit on the chip to allow the next
* integration cycle to start. This has to be done even though this
* driver currently does not support interrupts.
*/
ret = i2c_smbus_write_byte(chip->client,
(TSL2583_CMD_REG | TSL2583_CMD_SPL_FN |
TSL2583_CMD_ALS_INT_CLR));
if (ret < 0) {
dev_err(&chip->client->dev, "%s: failed to clear the interrupt bit\n",
__func__);
goto done; /* have no data, so return failure */
}
/* extract ALS/lux data */
ch0 = le16_to_cpup((const __le16 *)&buf[0]);
ch1 = le16_to_cpup((const __le16 *)&buf[2]);
chip->als_cur_info.als_ch0 = ch0;
chip->als_cur_info.als_ch1 = ch1;
if ((ch0 >= chip->als_saturation) || (ch1 >= chip->als_saturation))
goto return_max;
if (!ch0) {
/*
* The sensor appears to be in total darkness so set the
* calculated lux to 0 and return early to avoid a division by
* zero below when calculating the ratio.
*/
ret = 0;
chip->als_cur_info.lux = 0;
goto done;
}
/* calculate ratio */
ratio = (ch1 << 15) / ch0;
/* convert to unscaled lux using the pointer to the table */
for (p = (struct tsl2583_lux *)chip->als_settings.als_device_lux;
p->ratio != 0 && p->ratio < ratio; p++)
;
if (p->ratio == 0) {
lux = 0;
} else {
u32 ch0lux, ch1lux;
ch0lux = ((ch0 * p->ch0) +
(gainadj[chip->als_settings.als_gain].ch0 >> 1))
/ gainadj[chip->als_settings.als_gain].ch0;
ch1lux = ((ch1 * p->ch1) +
(gainadj[chip->als_settings.als_gain].ch1 >> 1))
/ gainadj[chip->als_settings.als_gain].ch1;
/* note: lux is 31 bit max at this point */
if (ch1lux > ch0lux) {
dev_dbg(&chip->client->dev, "%s: No Data - Returning 0\n",
__func__);
ret = 0;
chip->als_cur_info.lux = 0;
goto done;
}
lux = ch0lux - ch1lux;
}
/* adjust for active time scale */
if (chip->als_time_scale == 0)
lux = 0;
else
lux = (lux + (chip->als_time_scale >> 1)) /
chip->als_time_scale;
/*
* Adjust for active gain scale.
* The tsl2583_default_lux tables above have a factor of 8192 built in,
* so we need to shift right.
* User-specified gain provides a multiplier.
* Apply user-specified gain before shifting right to retain precision.
* Use 64 bits to avoid overflow on multiplication.
* Then go back to 32 bits before division to avoid using div_u64().
*/
lux64 = lux;
lux64 = lux64 * chip->als_settings.als_gain_trim;
lux64 >>= 13;
lux = lux64;
lux = (lux + 500) / 1000;
if (lux > TSL2583_LUX_CALC_OVER_FLOW) { /* check for overflow */
return_max:
lux = TSL2583_LUX_CALC_OVER_FLOW;
}
/* Update the structure with the latest VALID lux. */
chip->als_cur_info.lux = lux;
ret = lux;
done:
return ret;
}
/*
* Obtain single reading and calculate the als_gain_trim (later used
* to derive actual lux).
* Return updated gain_trim value.
*/
static int tsl2583_als_calibrate(struct iio_dev *indio_dev)
{
struct tsl2583_chip *chip = iio_priv(indio_dev);
unsigned int gain_trim_val;
int ret;
int lux_val;
ret = i2c_smbus_read_byte_data(chip->client,
TSL2583_CMD_REG | TSL2583_CNTRL);
if (ret < 0) {
dev_err(&chip->client->dev,
"%s: failed to read from the CNTRL register\n",
__func__);
return ret;
}
if ((ret & (TSL2583_CNTL_ADC_ENBL | TSL2583_CNTL_PWR_ON))
!= (TSL2583_CNTL_ADC_ENBL | TSL2583_CNTL_PWR_ON)) {
dev_err(&chip->client->dev,
"%s: Device is not powered on and/or ADC is not enabled\n",
__func__);
return -EINVAL;
} else if ((ret & TSL2583_STA_ADC_VALID) != TSL2583_STA_ADC_VALID) {
dev_err(&chip->client->dev,
"%s: The two ADC channels have not completed an integration cycle\n",
__func__);
return -ENODATA;
}
lux_val = tsl2583_get_lux(indio_dev);
if (lux_val < 0) {
dev_err(&chip->client->dev, "%s: failed to get lux\n",
__func__);
return lux_val;
}
gain_trim_val = (unsigned int)(((chip->als_settings.als_cal_target)
* chip->als_settings.als_gain_trim) / lux_val);
if ((gain_trim_val < 250) || (gain_trim_val > 4000)) {
dev_err(&chip->client->dev,
"%s: trim_val of %d is not within the range [250, 4000]\n",
__func__, gain_trim_val);
return -ENODATA;
}
chip->als_settings.als_gain_trim = (int)gain_trim_val;
return 0;
}
static int tsl2583_set_als_time(struct tsl2583_chip *chip)
{
int als_count, als_time, ret;
u8 val;
/* determine als integration register */
als_count = (chip->als_settings.als_time * 100 + 135) / 270;
if (!als_count)
als_count = 1; /* ensure at least one cycle */
/* convert back to time (encompasses overrides) */
als_time = (als_count * 27 + 5) / 10;
val = 256 - als_count;
ret = i2c_smbus_write_byte_data(chip->client,
TSL2583_CMD_REG | TSL2583_ALS_TIME,
val);
if (ret < 0) {
dev_err(&chip->client->dev, "%s: failed to set the als time to %d\n",
__func__, val);
return ret;
}
/* set chip struct re scaling and saturation */
chip->als_saturation = als_count * 922; /* 90% of full scale */
chip->als_time_scale = (als_time + 25) / 50;
return ret;
}
static int tsl2583_set_als_gain(struct tsl2583_chip *chip)
{
int ret;
/* Set the gain based on als_settings struct */
ret = i2c_smbus_write_byte_data(chip->client,
TSL2583_CMD_REG | TSL2583_GAIN,
chip->als_settings.als_gain);
if (ret < 0)
dev_err(&chip->client->dev,
"%s: failed to set the gain to %d\n", __func__,
chip->als_settings.als_gain);
return ret;
}
static int tsl2583_set_power_state(struct tsl2583_chip *chip, u8 state)
{
int ret;
ret = i2c_smbus_write_byte_data(chip->client,
TSL2583_CMD_REG | TSL2583_CNTRL, state);
if (ret < 0)
dev_err(&chip->client->dev,
"%s: failed to set the power state to %d\n", __func__,
state);
return ret;
}
/*
* Turn the device on.
* Configuration must be set before calling this function.
*/
static int tsl2583_chip_init_and_power_on(struct iio_dev *indio_dev)
{
struct tsl2583_chip *chip = iio_priv(indio_dev);
int ret;
/* Power on the device; ADC off. */
ret = tsl2583_set_power_state(chip, TSL2583_CNTL_PWR_ON);
if (ret < 0)
return ret;
ret = i2c_smbus_write_byte_data(chip->client,
TSL2583_CMD_REG | TSL2583_INTERRUPT,
TSL2583_INTERRUPT_DISABLED);
if (ret < 0) {
dev_err(&chip->client->dev,
"%s: failed to disable interrupts\n", __func__);
return ret;
}
ret = tsl2583_set_als_time(chip);
if (ret < 0)
return ret;
ret = tsl2583_set_als_gain(chip);
if (ret < 0)
return ret;
usleep_range(3000, 3500);
ret = tsl2583_set_power_state(chip, TSL2583_CNTL_PWR_ON |
TSL2583_CNTL_ADC_ENBL);
if (ret < 0)
return ret;
chip->suspended = false;
return ret;
}
/* Sysfs Interface Functions */
static ssize_t in_illuminance_input_target_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct tsl2583_chip *chip = iio_priv(indio_dev);
int ret;
mutex_lock(&chip->als_mutex);
ret = sprintf(buf, "%d\n", chip->als_settings.als_cal_target);
mutex_unlock(&chip->als_mutex);
return ret;
}
static ssize_t in_illuminance_input_target_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t len)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct tsl2583_chip *chip = iio_priv(indio_dev);
int value;
if (kstrtoint(buf, 0, &value) || !value)
return -EINVAL;
mutex_lock(&chip->als_mutex);
chip->als_settings.als_cal_target = value;
mutex_unlock(&chip->als_mutex);
return len;
}
static ssize_t in_illuminance_calibrate_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t len)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct tsl2583_chip *chip = iio_priv(indio_dev);
int value, ret;
if (kstrtoint(buf, 0, &value) || value != 1)
return -EINVAL;
mutex_lock(&chip->als_mutex);
if (chip->suspended) {
ret = -EBUSY;
goto done;
}
ret = tsl2583_als_calibrate(indio_dev);
if (ret < 0)
goto done;
ret = len;
done:
mutex_unlock(&chip->als_mutex);
return ret;
}
static ssize_t in_illuminance_lux_table_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct tsl2583_chip *chip = iio_priv(indio_dev);
unsigned int i;
int offset = 0;
for (i = 0; i < ARRAY_SIZE(chip->als_settings.als_device_lux); i++) {
offset += sprintf(buf + offset, "%u,%u,%u,",
chip->als_settings.als_device_lux[i].ratio,
chip->als_settings.als_device_lux[i].ch0,
chip->als_settings.als_device_lux[i].ch1);
if (chip->als_settings.als_device_lux[i].ratio == 0) {
/*
* We just printed the first "0" entry.
* Now get rid of the extra "," and break.
*/
offset--;
break;
}
}
offset += sprintf(buf + offset, "\n");
return offset;
}
static ssize_t in_illuminance_lux_table_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t len)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct tsl2583_chip *chip = iio_priv(indio_dev);
const unsigned int max_ints = TSL2583_MAX_LUX_TABLE_ENTRIES * 3;
int value[TSL2583_MAX_LUX_TABLE_ENTRIES * 3 + 1];
int ret = -EINVAL;
unsigned int n;
mutex_lock(&chip->als_mutex);
get_options(buf, ARRAY_SIZE(value), value);
/*
* We now have an array of ints starting at value[1], and
* enumerated by value[0].
* We expect each group of three ints is one table entry,
* and the last table entry is all 0.
*/
n = value[0];
if ((n % 3) || n < 6 || n > max_ints) {
dev_err(dev,
"%s: The number of entries in the lux table must be a multiple of 3 and within the range [6, %d]\n",
__func__, max_ints);
goto done;
}
if ((value[n - 2] | value[n - 1] | value[n]) != 0) {
dev_err(dev, "%s: The last 3 entries in the lux table must be zeros.\n",
__func__);
goto done;
}
memcpy(chip->als_settings.als_device_lux, &value[1],
value[0] * sizeof(value[1]));
ret = len;
done:
mutex_unlock(&chip->als_mutex);
return ret;
}
static IIO_CONST_ATTR(in_illuminance_calibscale_available, "1 8 16 111");
static IIO_CONST_ATTR(in_illuminance_integration_time_available,
"0.000050 0.000100 0.000150 0.000200 0.000250 0.000300 0.000350 0.000400 0.000450 0.000500 0.000550 0.000600 0.000650");
static IIO_DEVICE_ATTR_RW(in_illuminance_input_target, 0);
static IIO_DEVICE_ATTR_WO(in_illuminance_calibrate, 0);
static IIO_DEVICE_ATTR_RW(in_illuminance_lux_table, 0);
static struct attribute *sysfs_attrs_ctrl[] = {
&iio_const_attr_in_illuminance_calibscale_available.dev_attr.attr,
&iio_const_attr_in_illuminance_integration_time_available.dev_attr.attr,
&iio_dev_attr_in_illuminance_input_target.dev_attr.attr,
&iio_dev_attr_in_illuminance_calibrate.dev_attr.attr,
&iio_dev_attr_in_illuminance_lux_table.dev_attr.attr,
NULL
};
static const struct attribute_group tsl2583_attribute_group = {
.attrs = sysfs_attrs_ctrl,
};
static const struct iio_chan_spec tsl2583_channels[] = {
{
.type = IIO_LIGHT,
.modified = 1,
.channel2 = IIO_MOD_LIGHT_IR,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
},
{
.type = IIO_LIGHT,
.modified = 1,
.channel2 = IIO_MOD_LIGHT_BOTH,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
},
{
.type = IIO_LIGHT,
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
BIT(IIO_CHAN_INFO_CALIBBIAS) |
BIT(IIO_CHAN_INFO_CALIBSCALE) |
BIT(IIO_CHAN_INFO_INT_TIME),
},
};
static int tsl2583_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct tsl2583_chip *chip = iio_priv(indio_dev);
int ret = -EINVAL;
mutex_lock(&chip->als_mutex);
if (chip->suspended) {
ret = -EBUSY;
goto read_done;
}
switch (mask) {
case IIO_CHAN_INFO_RAW:
if (chan->type == IIO_LIGHT) {
ret = tsl2583_get_lux(indio_dev);
if (ret < 0)
goto read_done;
/*
* From page 20 of the TSL2581, TSL2583 data
* sheet (TAOS134 MARCH 2011):
*
* One of the photodiodes (channel 0) is
* sensitive to both visible and infrared light,
* while the second photodiode (channel 1) is
* sensitive primarily to infrared light.
*/
if (chan->channel2 == IIO_MOD_LIGHT_BOTH)
*val = chip->als_cur_info.als_ch0;
else
*val = chip->als_cur_info.als_ch1;
ret = IIO_VAL_INT;
}
break;
case IIO_CHAN_INFO_PROCESSED:
if (chan->type == IIO_LIGHT) {
ret = tsl2583_get_lux(indio_dev);
if (ret < 0)
goto read_done;
*val = ret;
ret = IIO_VAL_INT;
}
break;
case IIO_CHAN_INFO_CALIBBIAS:
if (chan->type == IIO_LIGHT) {
*val = chip->als_settings.als_gain_trim;
ret = IIO_VAL_INT;
}
break;
case IIO_CHAN_INFO_CALIBSCALE:
if (chan->type == IIO_LIGHT) {
*val = gainadj[chip->als_settings.als_gain].mean;
ret = IIO_VAL_INT;
}
break;
case IIO_CHAN_INFO_INT_TIME:
if (chan->type == IIO_LIGHT) {
*val = 0;
*val2 = chip->als_settings.als_time;
ret = IIO_VAL_INT_PLUS_MICRO;
}
break;
default:
break;
}
read_done:
mutex_unlock(&chip->als_mutex);
return ret;
}
static int tsl2583_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
struct tsl2583_chip *chip = iio_priv(indio_dev);
int ret = -EINVAL;
mutex_lock(&chip->als_mutex);
if (chip->suspended) {
ret = -EBUSY;
goto write_done;
}
switch (mask) {
case IIO_CHAN_INFO_CALIBBIAS:
if (chan->type == IIO_LIGHT) {
chip->als_settings.als_gain_trim = val;
ret = 0;
}
break;
case IIO_CHAN_INFO_CALIBSCALE:
if (chan->type == IIO_LIGHT) {
unsigned int i;
for (i = 0; i < ARRAY_SIZE(gainadj); i++) {
if (gainadj[i].mean == val) {
chip->als_settings.als_gain = i;
ret = tsl2583_set_als_gain(chip);
break;
}
}
}
break;
case IIO_CHAN_INFO_INT_TIME:
if (chan->type == IIO_LIGHT && !val && val2 >= 50 &&
val2 <= 650 && !(val2 % 50)) {
chip->als_settings.als_time = val2;
ret = tsl2583_set_als_time(chip);
}
break;
default:
break;
}
write_done:
mutex_unlock(&chip->als_mutex);
return ret;
}
static const struct iio_info tsl2583_info = {
.attrs = &tsl2583_attribute_group,
.driver_module = THIS_MODULE,
.read_raw = tsl2583_read_raw,
.write_raw = tsl2583_write_raw,
};
static int tsl2583_probe(struct i2c_client *clientp,
const struct i2c_device_id *idp)
{
int ret;
struct tsl2583_chip *chip;
struct iio_dev *indio_dev;
if (!i2c_check_functionality(clientp->adapter,
I2C_FUNC_SMBUS_BYTE_DATA)) {
dev_err(&clientp->dev, "%s: i2c smbus byte data functionality is unsupported\n",
__func__);
return -EOPNOTSUPP;
}
indio_dev = devm_iio_device_alloc(&clientp->dev, sizeof(*chip));
if (!indio_dev)
return -ENOMEM;
chip = iio_priv(indio_dev);
chip->client = clientp;
i2c_set_clientdata(clientp, indio_dev);
mutex_init(&chip->als_mutex);
chip->suspended = true;
ret = i2c_smbus_read_byte_data(clientp,
TSL2583_CMD_REG | TSL2583_CHIPID);
if (ret < 0) {
dev_err(&clientp->dev,
"%s: failed to read the chip ID register\n", __func__);
return ret;
}
if ((ret & TSL2583_CHIP_ID_MASK) != TSL2583_CHIP_ID) {
dev_err(&clientp->dev, "%s: received an unknown chip ID %x\n",
__func__, ret);
return -EINVAL;
}
indio_dev->info = &tsl2583_info;
indio_dev->channels = tsl2583_channels;
indio_dev->num_channels = ARRAY_SIZE(tsl2583_channels);
indio_dev->dev.parent = &clientp->dev;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->name = chip->client->name;
ret = devm_iio_device_register(indio_dev->dev.parent, indio_dev);
if (ret) {
dev_err(&clientp->dev, "%s: iio registration failed\n",
__func__);
return ret;
}
/* Load up the V2 defaults (these are hard coded defaults for now) */
tsl2583_defaults(chip);
/* Make sure the chip is on */
ret = tsl2583_chip_init_and_power_on(indio_dev);
if (ret < 0)
return ret;
dev_info(&clientp->dev, "Light sensor found.\n");
return 0;
}
static int __maybe_unused tsl2583_suspend(struct device *dev)
{
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
struct tsl2583_chip *chip = iio_priv(indio_dev);
int ret;
mutex_lock(&chip->als_mutex);
ret = tsl2583_set_power_state(chip, TSL2583_CNTL_PWR_OFF);
chip->suspended = true;
mutex_unlock(&chip->als_mutex);
return ret;
}
static int __maybe_unused tsl2583_resume(struct device *dev)
{
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
struct tsl2583_chip *chip = iio_priv(indio_dev);
int ret;
mutex_lock(&chip->als_mutex);
ret = tsl2583_chip_init_and_power_on(indio_dev);
mutex_unlock(&chip->als_mutex);
return ret;
}
static SIMPLE_DEV_PM_OPS(tsl2583_pm_ops, tsl2583_suspend, tsl2583_resume);
static struct i2c_device_id tsl2583_idtable[] = {
{ "tsl2580", 0 },
{ "tsl2581", 1 },
{ "tsl2583", 2 },
{}
};
MODULE_DEVICE_TABLE(i2c, tsl2583_idtable);
static const struct of_device_id tsl2583_of_match[] = {
{ .compatible = "amstaos,tsl2580", },
{ .compatible = "amstaos,tsl2581", },
{ .compatible = "amstaos,tsl2583", },
{ },
};
MODULE_DEVICE_TABLE(of, tsl2583_of_match);
/* Driver definition */
static struct i2c_driver tsl2583_driver = {
.driver = {
.name = "tsl2583",
.pm = &tsl2583_pm_ops,
.of_match_table = tsl2583_of_match,
},
.id_table = tsl2583_idtable,
.probe = tsl2583_probe,
};
module_i2c_driver(tsl2583_driver);
MODULE_AUTHOR("J. August Brenner <jbrenner@taosinc.com>");
MODULE_AUTHOR("Brian Masney <masneyb@onstation.org>");
MODULE_DESCRIPTION("TAOS tsl2583 ambient light sensor driver");
MODULE_LICENSE("GPL");

Просмотреть файл

@ -287,7 +287,7 @@ static int ak8974_await_drdy(struct ak8974 *ak8974)
return 0;
}
static int ak8974_getresult(struct ak8974 *ak8974, s16 *result)
static int ak8974_getresult(struct ak8974 *ak8974, __le16 *result)
{
unsigned int src;
int ret;
@ -395,7 +395,7 @@ static int ak8974_selftest(struct ak8974 *ak8974)
static int ak8974_get_u16_val(struct ak8974 *ak8974, u8 reg, u16 *val)
{
int ret;
u16 bulk;
__le16 bulk;
ret = regmap_bulk_read(ak8974->map, reg, &bulk, 2);
if (ret)
@ -453,7 +453,7 @@ static int ak8974_read_raw(struct iio_dev *indio_dev,
long mask)
{
struct ak8974 *ak8974 = iio_priv(indio_dev);
s16 hw_values[3];
__le16 hw_values[3];
int ret = -EINVAL;
pm_runtime_get_sync(&ak8974->i2c->dev);
@ -494,7 +494,7 @@ static void ak8974_fill_buffer(struct iio_dev *indio_dev)
{
struct ak8974 *ak8974 = iio_priv(indio_dev);
int ret;
s16 hw_values[8]; /* Three axes + 64bit padding */
__le16 hw_values[8]; /* Three axes + 64bit padding */
pm_runtime_get_sync(&ak8974->i2c->dev);
mutex_lock(&ak8974->lock);

Просмотреть файл

@ -690,6 +690,7 @@ static int ak8975_read_axis(struct iio_dev *indio_dev, int index, int *val)
struct ak8975_data *data = iio_priv(indio_dev);
const struct i2c_client *client = data->client;
const struct ak_def *def = data->def;
__le16 rval;
u16 buff;
int ret;
@ -703,7 +704,7 @@ static int ak8975_read_axis(struct iio_dev *indio_dev, int index, int *val)
ret = i2c_smbus_read_i2c_block_data_or_emulated(
client, def->data_regs[index],
sizeof(buff), (u8*)&buff);
sizeof(rval), (u8*)&rval);
if (ret < 0)
goto exit;
@ -713,7 +714,7 @@ static int ak8975_read_axis(struct iio_dev *indio_dev, int index, int *val)
pm_runtime_put_autosuspend(&data->client->dev);
/* Swap bytes and convert to valid range. */
buff = le16_to_cpu(buff);
buff = le16_to_cpu(rval);
*val = clamp_t(s16, buff, -def->range, def->range);
return IIO_VAL_INT;
@ -813,6 +814,7 @@ static void ak8975_fill_buffer(struct iio_dev *indio_dev)
const struct ak_def *def = data->def;
int ret;
s16 buff[8]; /* 3 x 16 bits axis values + 1 aligned 64 bits timestamp */
__le16 fval[3];
mutex_lock(&data->lock);
@ -826,17 +828,17 @@ static void ak8975_fill_buffer(struct iio_dev *indio_dev)
*/
ret = i2c_smbus_read_i2c_block_data_or_emulated(client,
def->data_regs[0],
3 * sizeof(buff[0]),
(u8 *)buff);
3 * sizeof(fval[0]),
(u8 *)fval);
if (ret < 0)
goto unlock;
mutex_unlock(&data->lock);
/* Clamp to valid range. */
buff[0] = clamp_t(s16, le16_to_cpu(buff[0]), -def->range, def->range);
buff[1] = clamp_t(s16, le16_to_cpu(buff[1]), -def->range, def->range);
buff[2] = clamp_t(s16, le16_to_cpu(buff[2]), -def->range, def->range);
buff[0] = clamp_t(s16, le16_to_cpu(fval[0]), -def->range, def->range);
buff[1] = clamp_t(s16, le16_to_cpu(fval[1]), -def->range, def->range);
buff[2] = clamp_t(s16, le16_to_cpu(fval[2]), -def->range, def->range);
iio_push_to_buffers_with_timestamp(indio_dev, buff,
iio_get_time_ns(indio_dev));

Просмотреть файл

@ -42,9 +42,17 @@ enum magn_3d_channel {
MAGN_3D_CHANNEL_MAX,
};
struct common_attributes {
int scale_pre_decml;
int scale_post_decml;
int scale_precision;
int value_offset;
};
struct magn_3d_state {
struct hid_sensor_hub_callbacks callbacks;
struct hid_sensor_common common_attributes;
struct hid_sensor_common magn_flux_attributes;
struct hid_sensor_common rot_attributes;
struct hid_sensor_hub_attribute_info magn[MAGN_3D_CHANNEL_MAX];
/* dynamically sized array to hold sensor values */
@ -52,10 +60,8 @@ struct magn_3d_state {
/* array of pointers to sensor value */
u32 *magn_val_addr[MAGN_3D_CHANNEL_MAX];
int scale_pre_decml;
int scale_post_decml;
int scale_precision;
int value_offset;
struct common_attributes magn_flux_attr;
struct common_attributes rot_attr;
};
static const u32 magn_3d_addresses[MAGN_3D_CHANNEL_MAX] = {
@ -162,41 +168,74 @@ static int magn_3d_read_raw(struct iio_dev *indio_dev,
*val2 = 0;
switch (mask) {
case 0:
hid_sensor_power_state(&magn_state->common_attributes, true);
hid_sensor_power_state(&magn_state->magn_flux_attributes, true);
report_id =
magn_state->magn[chan->address].report_id;
address = magn_3d_addresses[chan->address];
if (report_id >= 0)
*val = sensor_hub_input_attr_get_raw_value(
magn_state->common_attributes.hsdev,
magn_state->magn_flux_attributes.hsdev,
HID_USAGE_SENSOR_COMPASS_3D, address,
report_id,
SENSOR_HUB_SYNC);
else {
*val = 0;
hid_sensor_power_state(&magn_state->common_attributes,
false);
hid_sensor_power_state(
&magn_state->magn_flux_attributes,
false);
return -EINVAL;
}
hid_sensor_power_state(&magn_state->common_attributes, false);
hid_sensor_power_state(&magn_state->magn_flux_attributes,
false);
ret_type = IIO_VAL_INT;
break;
case IIO_CHAN_INFO_SCALE:
*val = magn_state->scale_pre_decml;
*val2 = magn_state->scale_post_decml;
ret_type = magn_state->scale_precision;
switch (chan->type) {
case IIO_MAGN:
*val = magn_state->magn_flux_attr.scale_pre_decml;
*val2 = magn_state->magn_flux_attr.scale_post_decml;
ret_type = magn_state->magn_flux_attr.scale_precision;
break;
case IIO_ROT:
*val = magn_state->rot_attr.scale_pre_decml;
*val2 = magn_state->rot_attr.scale_post_decml;
ret_type = magn_state->rot_attr.scale_precision;
break;
default:
ret_type = -EINVAL;
}
break;
case IIO_CHAN_INFO_OFFSET:
*val = magn_state->value_offset;
ret_type = IIO_VAL_INT;
switch (chan->type) {
case IIO_MAGN:
*val = magn_state->magn_flux_attr.value_offset;
ret_type = IIO_VAL_INT;
break;
case IIO_ROT:
*val = magn_state->rot_attr.value_offset;
ret_type = IIO_VAL_INT;
break;
default:
ret_type = -EINVAL;
}
break;
case IIO_CHAN_INFO_SAMP_FREQ:
ret_type = hid_sensor_read_samp_freq_value(
&magn_state->common_attributes, val, val2);
&magn_state->magn_flux_attributes, val, val2);
break;
case IIO_CHAN_INFO_HYSTERESIS:
ret_type = hid_sensor_read_raw_hyst_value(
&magn_state->common_attributes, val, val2);
switch (chan->type) {
case IIO_MAGN:
ret_type = hid_sensor_read_raw_hyst_value(
&magn_state->magn_flux_attributes, val, val2);
break;
case IIO_ROT:
ret_type = hid_sensor_read_raw_hyst_value(
&magn_state->rot_attributes, val, val2);
break;
default:
ret_type = -EINVAL;
}
break;
default:
ret_type = -EINVAL;
@ -219,11 +258,21 @@ static int magn_3d_write_raw(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
ret = hid_sensor_write_samp_freq_value(
&magn_state->common_attributes, val, val2);
&magn_state->magn_flux_attributes, val, val2);
break;
case IIO_CHAN_INFO_HYSTERESIS:
ret = hid_sensor_write_raw_hyst_value(
&magn_state->common_attributes, val, val2);
switch (chan->type) {
case IIO_MAGN:
ret = hid_sensor_write_raw_hyst_value(
&magn_state->magn_flux_attributes, val, val2);
break;
case IIO_ROT:
ret = hid_sensor_write_raw_hyst_value(
&magn_state->rot_attributes, val, val2);
break;
default:
ret = -EINVAL;
}
break;
default:
ret = -EINVAL;
@ -254,7 +303,7 @@ static int magn_3d_proc_event(struct hid_sensor_hub_device *hsdev,
struct magn_3d_state *magn_state = iio_priv(indio_dev);
dev_dbg(&indio_dev->dev, "magn_3d_proc_event\n");
if (atomic_read(&magn_state->common_attributes.data_ready))
if (atomic_read(&magn_state->magn_flux_attributes.data_ready))
hid_sensor_push_data(indio_dev, magn_state->iio_vals);
return 0;
@ -389,21 +438,48 @@ static int magn_3d_parse_report(struct platform_device *pdev,
dev_dbg(&pdev->dev, "magn_3d Setup %d IIO channels\n",
*chan_count);
st->scale_precision = hid_sensor_format_scale(
st->magn_flux_attr.scale_precision = hid_sensor_format_scale(
HID_USAGE_SENSOR_COMPASS_3D,
&st->magn[CHANNEL_SCAN_INDEX_X],
&st->scale_pre_decml, &st->scale_post_decml);
&st->magn_flux_attr.scale_pre_decml,
&st->magn_flux_attr.scale_post_decml);
st->rot_attr.scale_precision
= hid_sensor_format_scale(
HID_USAGE_SENSOR_ORIENT_COMP_MAGN_NORTH,
&st->magn[CHANNEL_SCAN_INDEX_NORTH_MAGN_TILT_COMP],
&st->rot_attr.scale_pre_decml,
&st->rot_attr.scale_post_decml);
/* Set Sensitivity field ids, when there is no individual modifier */
if (st->common_attributes.sensitivity.index < 0) {
if (st->magn_flux_attributes.sensitivity.index < 0) {
sensor_hub_input_get_attribute_info(hsdev,
HID_FEATURE_REPORT, usage_id,
HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS |
HID_USAGE_SENSOR_DATA_ORIENTATION,
&st->common_attributes.sensitivity);
&st->magn_flux_attributes.sensitivity);
dev_dbg(&pdev->dev, "Sensitivity index:report %d:%d\n",
st->common_attributes.sensitivity.index,
st->common_attributes.sensitivity.report_id);
st->magn_flux_attributes.sensitivity.index,
st->magn_flux_attributes.sensitivity.report_id);
}
if (st->magn_flux_attributes.sensitivity.index < 0) {
sensor_hub_input_get_attribute_info(hsdev,
HID_FEATURE_REPORT, usage_id,
HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS |
HID_USAGE_SENSOR_ORIENT_MAGN_FLUX,
&st->magn_flux_attributes.sensitivity);
dev_dbg(&pdev->dev, "Sensitivity index:report %d:%d\n",
st->magn_flux_attributes.sensitivity.index,
st->magn_flux_attributes.sensitivity.report_id);
}
if (st->rot_attributes.sensitivity.index < 0) {
sensor_hub_input_get_attribute_info(hsdev,
HID_FEATURE_REPORT, usage_id,
HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS |
HID_USAGE_SENSOR_ORIENT_COMP_MAGN_NORTH,
&st->rot_attributes.sensitivity);
dev_dbg(&pdev->dev, "Sensitivity index:report %d:%d\n",
st->rot_attributes.sensitivity.index,
st->rot_attributes.sensitivity.report_id);
}
return 0;
@ -428,16 +504,17 @@ static int hid_magn_3d_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, indio_dev);
magn_state = iio_priv(indio_dev);
magn_state->common_attributes.hsdev = hsdev;
magn_state->common_attributes.pdev = pdev;
magn_state->magn_flux_attributes.hsdev = hsdev;
magn_state->magn_flux_attributes.pdev = pdev;
ret = hid_sensor_parse_common_attributes(hsdev,
HID_USAGE_SENSOR_COMPASS_3D,
&magn_state->common_attributes);
&magn_state->magn_flux_attributes);
if (ret) {
dev_err(&pdev->dev, "failed to setup common attributes\n");
return ret;
}
magn_state->rot_attributes = magn_state->magn_flux_attributes;
ret = magn_3d_parse_report(pdev, hsdev,
&channels, &chan_count,
@ -460,9 +537,9 @@ static int hid_magn_3d_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "failed to initialize trigger buffer\n");
return ret;
}
atomic_set(&magn_state->common_attributes.data_ready, 0);
atomic_set(&magn_state->magn_flux_attributes.data_ready, 0);
ret = hid_sensor_setup_trigger(indio_dev, name,
&magn_state->common_attributes);
&magn_state->magn_flux_attributes);
if (ret < 0) {
dev_err(&pdev->dev, "trigger setup failed\n");
goto error_unreg_buffer_funcs;
@ -489,7 +566,7 @@ static int hid_magn_3d_probe(struct platform_device *pdev)
error_iio_unreg:
iio_device_unregister(indio_dev);
error_remove_trigger:
hid_sensor_remove_trigger(&magn_state->common_attributes);
hid_sensor_remove_trigger(&magn_state->magn_flux_attributes);
error_unreg_buffer_funcs:
iio_triggered_buffer_cleanup(indio_dev);
return ret;
@ -504,7 +581,7 @@ static int hid_magn_3d_remove(struct platform_device *pdev)
sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_COMPASS_3D);
iio_device_unregister(indio_dev);
hid_sensor_remove_trigger(&magn_state->common_attributes);
hid_sensor_remove_trigger(&magn_state->magn_flux_attributes);
iio_triggered_buffer_cleanup(indio_dev);
return 0;

Просмотреть файл

@ -46,139 +46,12 @@
#define ST_MAGN_FS_AVL_15000MG 15000
#define ST_MAGN_FS_AVL_16000MG 16000
/* CUSTOM VALUES FOR SENSOR 0 */
#define ST_MAGN_0_ODR_ADDR 0x00
#define ST_MAGN_0_ODR_MASK 0x1c
#define ST_MAGN_0_ODR_AVL_1HZ_VAL 0x00
#define ST_MAGN_0_ODR_AVL_2HZ_VAL 0x01
#define ST_MAGN_0_ODR_AVL_3HZ_VAL 0x02
#define ST_MAGN_0_ODR_AVL_8HZ_VAL 0x03
#define ST_MAGN_0_ODR_AVL_15HZ_VAL 0x04
#define ST_MAGN_0_ODR_AVL_30HZ_VAL 0x05
#define ST_MAGN_0_ODR_AVL_75HZ_VAL 0x06
#define ST_MAGN_0_ODR_AVL_220HZ_VAL 0x07
#define ST_MAGN_0_PW_ADDR 0x02
#define ST_MAGN_0_PW_MASK 0x03
#define ST_MAGN_0_PW_ON 0x00
#define ST_MAGN_0_PW_OFF 0x03
#define ST_MAGN_0_FS_ADDR 0x01
#define ST_MAGN_0_FS_MASK 0xe0
#define ST_MAGN_0_FS_AVL_1300_VAL 0x01
#define ST_MAGN_0_FS_AVL_1900_VAL 0x02
#define ST_MAGN_0_FS_AVL_2500_VAL 0x03
#define ST_MAGN_0_FS_AVL_4000_VAL 0x04
#define ST_MAGN_0_FS_AVL_4700_VAL 0x05
#define ST_MAGN_0_FS_AVL_5600_VAL 0x06
#define ST_MAGN_0_FS_AVL_8100_VAL 0x07
#define ST_MAGN_0_FS_AVL_1300_GAIN_XY 1100
#define ST_MAGN_0_FS_AVL_1900_GAIN_XY 855
#define ST_MAGN_0_FS_AVL_2500_GAIN_XY 670
#define ST_MAGN_0_FS_AVL_4000_GAIN_XY 450
#define ST_MAGN_0_FS_AVL_4700_GAIN_XY 400
#define ST_MAGN_0_FS_AVL_5600_GAIN_XY 330
#define ST_MAGN_0_FS_AVL_8100_GAIN_XY 230
#define ST_MAGN_0_FS_AVL_1300_GAIN_Z 980
#define ST_MAGN_0_FS_AVL_1900_GAIN_Z 760
#define ST_MAGN_0_FS_AVL_2500_GAIN_Z 600
#define ST_MAGN_0_FS_AVL_4000_GAIN_Z 400
#define ST_MAGN_0_FS_AVL_4700_GAIN_Z 355
#define ST_MAGN_0_FS_AVL_5600_GAIN_Z 295
#define ST_MAGN_0_FS_AVL_8100_GAIN_Z 205
#define ST_MAGN_0_MULTIREAD_BIT false
/* CUSTOM VALUES FOR SENSOR 1 */
#define ST_MAGN_1_WAI_EXP 0x3c
#define ST_MAGN_1_ODR_ADDR 0x00
#define ST_MAGN_1_ODR_MASK 0x1c
#define ST_MAGN_1_ODR_AVL_1HZ_VAL 0x00
#define ST_MAGN_1_ODR_AVL_2HZ_VAL 0x01
#define ST_MAGN_1_ODR_AVL_3HZ_VAL 0x02
#define ST_MAGN_1_ODR_AVL_8HZ_VAL 0x03
#define ST_MAGN_1_ODR_AVL_15HZ_VAL 0x04
#define ST_MAGN_1_ODR_AVL_30HZ_VAL 0x05
#define ST_MAGN_1_ODR_AVL_75HZ_VAL 0x06
#define ST_MAGN_1_ODR_AVL_220HZ_VAL 0x07
#define ST_MAGN_1_PW_ADDR 0x02
#define ST_MAGN_1_PW_MASK 0x03
#define ST_MAGN_1_PW_ON 0x00
#define ST_MAGN_1_PW_OFF 0x03
#define ST_MAGN_1_FS_ADDR 0x01
#define ST_MAGN_1_FS_MASK 0xe0
#define ST_MAGN_1_FS_AVL_1300_VAL 0x01
#define ST_MAGN_1_FS_AVL_1900_VAL 0x02
#define ST_MAGN_1_FS_AVL_2500_VAL 0x03
#define ST_MAGN_1_FS_AVL_4000_VAL 0x04
#define ST_MAGN_1_FS_AVL_4700_VAL 0x05
#define ST_MAGN_1_FS_AVL_5600_VAL 0x06
#define ST_MAGN_1_FS_AVL_8100_VAL 0x07
#define ST_MAGN_1_FS_AVL_1300_GAIN_XY 909
#define ST_MAGN_1_FS_AVL_1900_GAIN_XY 1169
#define ST_MAGN_1_FS_AVL_2500_GAIN_XY 1492
#define ST_MAGN_1_FS_AVL_4000_GAIN_XY 2222
#define ST_MAGN_1_FS_AVL_4700_GAIN_XY 2500
#define ST_MAGN_1_FS_AVL_5600_GAIN_XY 3030
#define ST_MAGN_1_FS_AVL_8100_GAIN_XY 4347
#define ST_MAGN_1_FS_AVL_1300_GAIN_Z 1020
#define ST_MAGN_1_FS_AVL_1900_GAIN_Z 1315
#define ST_MAGN_1_FS_AVL_2500_GAIN_Z 1666
#define ST_MAGN_1_FS_AVL_4000_GAIN_Z 2500
#define ST_MAGN_1_FS_AVL_4700_GAIN_Z 2816
#define ST_MAGN_1_FS_AVL_5600_GAIN_Z 3389
#define ST_MAGN_1_FS_AVL_8100_GAIN_Z 4878
#define ST_MAGN_1_MULTIREAD_BIT false
/* CUSTOM VALUES FOR SENSOR 2 */
#define ST_MAGN_2_WAI_EXP 0x3d
#define ST_MAGN_2_ODR_ADDR 0x20
#define ST_MAGN_2_ODR_MASK 0x1c
#define ST_MAGN_2_ODR_AVL_1HZ_VAL 0x00
#define ST_MAGN_2_ODR_AVL_2HZ_VAL 0x01
#define ST_MAGN_2_ODR_AVL_3HZ_VAL 0x02
#define ST_MAGN_2_ODR_AVL_5HZ_VAL 0x03
#define ST_MAGN_2_ODR_AVL_10HZ_VAL 0x04
#define ST_MAGN_2_ODR_AVL_20HZ_VAL 0x05
#define ST_MAGN_2_ODR_AVL_40HZ_VAL 0x06
#define ST_MAGN_2_ODR_AVL_80HZ_VAL 0x07
#define ST_MAGN_2_PW_ADDR 0x22
#define ST_MAGN_2_PW_MASK 0x03
#define ST_MAGN_2_PW_ON 0x00
#define ST_MAGN_2_PW_OFF 0x03
#define ST_MAGN_2_FS_ADDR 0x21
#define ST_MAGN_2_FS_MASK 0x60
#define ST_MAGN_2_FS_AVL_4000_VAL 0x00
#define ST_MAGN_2_FS_AVL_8000_VAL 0x01
#define ST_MAGN_2_FS_AVL_12000_VAL 0x02
#define ST_MAGN_2_FS_AVL_16000_VAL 0x03
#define ST_MAGN_2_FS_AVL_4000_GAIN 146
#define ST_MAGN_2_FS_AVL_8000_GAIN 292
#define ST_MAGN_2_FS_AVL_12000_GAIN 438
#define ST_MAGN_2_FS_AVL_16000_GAIN 584
#define ST_MAGN_2_MULTIREAD_BIT false
/* Special L addresses for Sensor 2 */
#define ST_MAGN_2_OUT_X_L_ADDR 0x28
#define ST_MAGN_2_OUT_Y_L_ADDR 0x2a
#define ST_MAGN_2_OUT_Z_L_ADDR 0x2c
/* CUSTOM VALUES FOR SENSOR 3 */
#define ST_MAGN_3_WAI_ADDR 0x4f
#define ST_MAGN_3_WAI_EXP 0x40
#define ST_MAGN_3_ODR_ADDR 0x60
#define ST_MAGN_3_ODR_MASK 0x0c
#define ST_MAGN_3_ODR_AVL_10HZ_VAL 0x00
#define ST_MAGN_3_ODR_AVL_20HZ_VAL 0x01
#define ST_MAGN_3_ODR_AVL_50HZ_VAL 0x02
#define ST_MAGN_3_ODR_AVL_100HZ_VAL 0x03
#define ST_MAGN_3_PW_ADDR 0x60
#define ST_MAGN_3_PW_MASK 0x03
#define ST_MAGN_3_PW_ON 0x00
#define ST_MAGN_3_PW_OFF 0x03
#define ST_MAGN_3_BDU_ADDR 0x62
#define ST_MAGN_3_BDU_MASK 0x10
#define ST_MAGN_3_DRDY_IRQ_ADDR 0x62
#define ST_MAGN_3_DRDY_INT_MASK 0x01
#define ST_MAGN_3_IHL_IRQ_ADDR 0x63
#define ST_MAGN_3_IHL_IRQ_MASK 0x04
#define ST_MAGN_3_FS_AVL_15000_GAIN 1500
#define ST_MAGN_3_MULTIREAD_BIT false
/* Special L addresses for sensor 3 */
#define ST_MAGN_3_OUT_X_L_ADDR 0x68
#define ST_MAGN_3_OUT_Y_L_ADDR 0x6a
#define ST_MAGN_3_OUT_Z_L_ADDR 0x6c
@ -240,77 +113,78 @@ static const struct st_sensor_settings st_magn_sensors_settings[] = {
},
.ch = (struct iio_chan_spec *)st_magn_16bit_channels,
.odr = {
.addr = ST_MAGN_0_ODR_ADDR,
.mask = ST_MAGN_0_ODR_MASK,
.addr = 0x00,
.mask = 0x1c,
.odr_avl = {
{ 1, ST_MAGN_0_ODR_AVL_1HZ_VAL, },
{ 2, ST_MAGN_0_ODR_AVL_2HZ_VAL, },
{ 3, ST_MAGN_0_ODR_AVL_3HZ_VAL, },
{ 8, ST_MAGN_0_ODR_AVL_8HZ_VAL, },
{ 15, ST_MAGN_0_ODR_AVL_15HZ_VAL, },
{ 30, ST_MAGN_0_ODR_AVL_30HZ_VAL, },
{ 75, ST_MAGN_0_ODR_AVL_75HZ_VAL, },
{ .hz = 1, .value = 0x00 },
{ .hz = 2, .value = 0x01 },
{ .hz = 3, .value = 0x02 },
{ .hz = 8, .value = 0x03 },
{ .hz = 15, .value = 0x04 },
{ .hz = 30, .value = 0x05 },
{ .hz = 75, .value = 0x06 },
/* 220 Hz, 0x07 reportedly exist */
},
},
.pw = {
.addr = ST_MAGN_0_PW_ADDR,
.mask = ST_MAGN_0_PW_MASK,
.value_on = ST_MAGN_0_PW_ON,
.value_off = ST_MAGN_0_PW_OFF,
.addr = 0x02,
.mask = 0x03,
.value_on = 0x00,
.value_off = 0x03,
},
.fs = {
.addr = ST_MAGN_0_FS_ADDR,
.mask = ST_MAGN_0_FS_MASK,
.addr = 0x01,
.mask = 0xe0,
.fs_avl = {
[0] = {
.num = ST_MAGN_FS_AVL_1300MG,
.value = ST_MAGN_0_FS_AVL_1300_VAL,
.gain = ST_MAGN_0_FS_AVL_1300_GAIN_XY,
.gain2 = ST_MAGN_0_FS_AVL_1300_GAIN_Z,
.value = 0x01,
.gain = 1100,
.gain2 = 980,
},
[1] = {
.num = ST_MAGN_FS_AVL_1900MG,
.value = ST_MAGN_0_FS_AVL_1900_VAL,
.gain = ST_MAGN_0_FS_AVL_1900_GAIN_XY,
.gain2 = ST_MAGN_0_FS_AVL_1900_GAIN_Z,
.value = 0x02,
.gain = 855,
.gain2 = 760,
},
[2] = {
.num = ST_MAGN_FS_AVL_2500MG,
.value = ST_MAGN_0_FS_AVL_2500_VAL,
.gain = ST_MAGN_0_FS_AVL_2500_GAIN_XY,
.gain2 = ST_MAGN_0_FS_AVL_2500_GAIN_Z,
.value = 0x03,
.gain = 670,
.gain2 = 600,
},
[3] = {
.num = ST_MAGN_FS_AVL_4000MG,
.value = ST_MAGN_0_FS_AVL_4000_VAL,
.gain = ST_MAGN_0_FS_AVL_4000_GAIN_XY,
.gain2 = ST_MAGN_0_FS_AVL_4000_GAIN_Z,
.value = 0x04,
.gain = 450,
.gain2 = 400,
},
[4] = {
.num = ST_MAGN_FS_AVL_4700MG,
.value = ST_MAGN_0_FS_AVL_4700_VAL,
.gain = ST_MAGN_0_FS_AVL_4700_GAIN_XY,
.gain2 = ST_MAGN_0_FS_AVL_4700_GAIN_Z,
.value = 0x05,
.gain = 400,
.gain2 = 355,
},
[5] = {
.num = ST_MAGN_FS_AVL_5600MG,
.value = ST_MAGN_0_FS_AVL_5600_VAL,
.gain = ST_MAGN_0_FS_AVL_5600_GAIN_XY,
.gain2 = ST_MAGN_0_FS_AVL_5600_GAIN_Z,
.value = 0x06,
.gain = 330,
.gain2 = 295,
},
[6] = {
.num = ST_MAGN_FS_AVL_8100MG,
.value = ST_MAGN_0_FS_AVL_8100_VAL,
.gain = ST_MAGN_0_FS_AVL_8100_GAIN_XY,
.gain2 = ST_MAGN_0_FS_AVL_8100_GAIN_Z,
.value = 0x07,
.gain = 230,
.gain2 = 205,
},
},
},
.multi_read_bit = ST_MAGN_0_MULTIREAD_BIT,
.multi_read_bit = false,
.bootime = 2,
},
{
.wai = ST_MAGN_1_WAI_EXP,
.wai = 0x3c,
.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
.sensors_supported = {
[0] = LSM303DLHC_MAGN_DEV_NAME,
@ -318,175 +192,175 @@ static const struct st_sensor_settings st_magn_sensors_settings[] = {
},
.ch = (struct iio_chan_spec *)st_magn_16bit_channels,
.odr = {
.addr = ST_MAGN_1_ODR_ADDR,
.mask = ST_MAGN_1_ODR_MASK,
.addr = 0x00,
.mask = 0x1c,
.odr_avl = {
{ 1, ST_MAGN_1_ODR_AVL_1HZ_VAL, },
{ 2, ST_MAGN_1_ODR_AVL_2HZ_VAL, },
{ 3, ST_MAGN_1_ODR_AVL_3HZ_VAL, },
{ 8, ST_MAGN_1_ODR_AVL_8HZ_VAL, },
{ 15, ST_MAGN_1_ODR_AVL_15HZ_VAL, },
{ 30, ST_MAGN_1_ODR_AVL_30HZ_VAL, },
{ 75, ST_MAGN_1_ODR_AVL_75HZ_VAL, },
{ 220, ST_MAGN_1_ODR_AVL_220HZ_VAL, },
{ .hz = 1, .value = 0x00 },
{ .hz = 2, .value = 0x01 },
{ .hz = 3, .value = 0x02 },
{ .hz = 8, .value = 0x03 },
{ .hz = 15, .value = 0x04 },
{ .hz = 30, .value = 0x05 },
{ .hz = 75, .value = 0x06 },
{ .hz = 220, .value = 0x07 },
},
},
.pw = {
.addr = ST_MAGN_1_PW_ADDR,
.mask = ST_MAGN_1_PW_MASK,
.value_on = ST_MAGN_1_PW_ON,
.value_off = ST_MAGN_1_PW_OFF,
.addr = 0x02,
.mask = 0x03,
.value_on = 0x00,
.value_off = 0x03,
},
.fs = {
.addr = ST_MAGN_1_FS_ADDR,
.mask = ST_MAGN_1_FS_MASK,
.addr = 0x01,
.mask = 0xe0,
.fs_avl = {
[0] = {
.num = ST_MAGN_FS_AVL_1300MG,
.value = ST_MAGN_1_FS_AVL_1300_VAL,
.gain = ST_MAGN_1_FS_AVL_1300_GAIN_XY,
.gain2 = ST_MAGN_1_FS_AVL_1300_GAIN_Z,
.value = 0x01,
.gain = 909,
.gain2 = 1020,
},
[1] = {
.num = ST_MAGN_FS_AVL_1900MG,
.value = ST_MAGN_1_FS_AVL_1900_VAL,
.gain = ST_MAGN_1_FS_AVL_1900_GAIN_XY,
.gain2 = ST_MAGN_1_FS_AVL_1900_GAIN_Z,
.value = 0x02,
.gain = 1169,
.gain2 = 1315,
},
[2] = {
.num = ST_MAGN_FS_AVL_2500MG,
.value = ST_MAGN_1_FS_AVL_2500_VAL,
.gain = ST_MAGN_1_FS_AVL_2500_GAIN_XY,
.gain2 = ST_MAGN_1_FS_AVL_2500_GAIN_Z,
.value = 0x03,
.gain = 1492,
.gain2 = 1666,
},
[3] = {
.num = ST_MAGN_FS_AVL_4000MG,
.value = ST_MAGN_1_FS_AVL_4000_VAL,
.gain = ST_MAGN_1_FS_AVL_4000_GAIN_XY,
.gain2 = ST_MAGN_1_FS_AVL_4000_GAIN_Z,
.value = 0x04,
.gain = 2222,
.gain2 = 2500,
},
[4] = {
.num = ST_MAGN_FS_AVL_4700MG,
.value = ST_MAGN_1_FS_AVL_4700_VAL,
.gain = ST_MAGN_1_FS_AVL_4700_GAIN_XY,
.gain2 = ST_MAGN_1_FS_AVL_4700_GAIN_Z,
.value = 0x05,
.gain = 2500,
.gain2 = 2816,
},
[5] = {
.num = ST_MAGN_FS_AVL_5600MG,
.value = ST_MAGN_1_FS_AVL_5600_VAL,
.gain = ST_MAGN_1_FS_AVL_5600_GAIN_XY,
.gain2 = ST_MAGN_1_FS_AVL_5600_GAIN_Z,
.value = 0x06,
.gain = 3030,
.gain2 = 3389,
},
[6] = {
.num = ST_MAGN_FS_AVL_8100MG,
.value = ST_MAGN_1_FS_AVL_8100_VAL,
.gain = ST_MAGN_1_FS_AVL_8100_GAIN_XY,
.gain2 = ST_MAGN_1_FS_AVL_8100_GAIN_Z,
.value = 0x07,
.gain = 4347,
.gain2 = 4878,
},
},
},
.multi_read_bit = ST_MAGN_1_MULTIREAD_BIT,
.multi_read_bit = false,
.bootime = 2,
},
{
.wai = ST_MAGN_2_WAI_EXP,
.wai = 0x3d,
.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
.sensors_supported = {
[0] = LIS3MDL_MAGN_DEV_NAME,
},
.ch = (struct iio_chan_spec *)st_magn_2_16bit_channels,
.odr = {
.addr = ST_MAGN_2_ODR_ADDR,
.mask = ST_MAGN_2_ODR_MASK,
.addr = 0x20,
.mask = 0x1c,
.odr_avl = {
{ 1, ST_MAGN_2_ODR_AVL_1HZ_VAL, },
{ 2, ST_MAGN_2_ODR_AVL_2HZ_VAL, },
{ 3, ST_MAGN_2_ODR_AVL_3HZ_VAL, },
{ 5, ST_MAGN_2_ODR_AVL_5HZ_VAL, },
{ 10, ST_MAGN_2_ODR_AVL_10HZ_VAL, },
{ 20, ST_MAGN_2_ODR_AVL_20HZ_VAL, },
{ 40, ST_MAGN_2_ODR_AVL_40HZ_VAL, },
{ 80, ST_MAGN_2_ODR_AVL_80HZ_VAL, },
{ .hz = 1, .value = 0x00 },
{ .hz = 2, .value = 0x01 },
{ .hz = 3, .value = 0x02 },
{ .hz = 5, .value = 0x03 },
{ .hz = 10, .value = 0x04 },
{ .hz = 20, .value = 0x05 },
{ .hz = 40, .value = 0x06 },
{ .hz = 80, .value = 0x07 },
},
},
.pw = {
.addr = ST_MAGN_2_PW_ADDR,
.mask = ST_MAGN_2_PW_MASK,
.value_on = ST_MAGN_2_PW_ON,
.value_off = ST_MAGN_2_PW_OFF,
.addr = 0x22,
.mask = 0x03,
.value_on = 0x00,
.value_off = 0x03,
},
.fs = {
.addr = ST_MAGN_2_FS_ADDR,
.mask = ST_MAGN_2_FS_MASK,
.addr = 0x21,
.mask = 0x60,
.fs_avl = {
[0] = {
.num = ST_MAGN_FS_AVL_4000MG,
.value = ST_MAGN_2_FS_AVL_4000_VAL,
.gain = ST_MAGN_2_FS_AVL_4000_GAIN,
.value = 0x00,
.gain = 146,
},
[1] = {
.num = ST_MAGN_FS_AVL_8000MG,
.value = ST_MAGN_2_FS_AVL_8000_VAL,
.gain = ST_MAGN_2_FS_AVL_8000_GAIN,
.value = 0x01,
.gain = 292,
},
[2] = {
.num = ST_MAGN_FS_AVL_12000MG,
.value = ST_MAGN_2_FS_AVL_12000_VAL,
.gain = ST_MAGN_2_FS_AVL_12000_GAIN,
.value = 0x02,
.gain = 438,
},
[3] = {
.num = ST_MAGN_FS_AVL_16000MG,
.value = ST_MAGN_2_FS_AVL_16000_VAL,
.gain = ST_MAGN_2_FS_AVL_16000_GAIN,
.value = 0x03,
.gain = 584,
},
},
},
.multi_read_bit = ST_MAGN_2_MULTIREAD_BIT,
.multi_read_bit = false,
.bootime = 2,
},
{
.wai = ST_MAGN_3_WAI_EXP,
.wai_addr = ST_MAGN_3_WAI_ADDR,
.wai = 0x40,
.wai_addr = 0x4f,
.sensors_supported = {
[0] = LSM303AGR_MAGN_DEV_NAME,
},
.ch = (struct iio_chan_spec *)st_magn_3_16bit_channels,
.odr = {
.addr = ST_MAGN_3_ODR_ADDR,
.mask = ST_MAGN_3_ODR_MASK,
.addr = 0x60,
.mask = 0x0c,
.odr_avl = {
{ 10, ST_MAGN_3_ODR_AVL_10HZ_VAL, },
{ 20, ST_MAGN_3_ODR_AVL_20HZ_VAL, },
{ 50, ST_MAGN_3_ODR_AVL_50HZ_VAL, },
{ 100, ST_MAGN_3_ODR_AVL_100HZ_VAL, },
{ .hz = 10, .value = 0x00 },
{ .hz = 20, .value = 0x01 },
{ .hz = 50, .value = 0x02 },
{ .hz = 100, .value = 0x03 },
},
},
.pw = {
.addr = ST_MAGN_3_PW_ADDR,
.mask = ST_MAGN_3_PW_MASK,
.value_on = ST_MAGN_3_PW_ON,
.value_off = ST_MAGN_3_PW_OFF,
.addr = 0x60,
.mask = 0x03,
.value_on = 0x00,
.value_off = 0x03,
},
.fs = {
.fs_avl = {
[0] = {
.num = ST_MAGN_FS_AVL_15000MG,
.gain = ST_MAGN_3_FS_AVL_15000_GAIN,
.gain = 1500,
},
},
},
.bdu = {
.addr = ST_MAGN_3_BDU_ADDR,
.mask = ST_MAGN_3_BDU_MASK,
.addr = 0x62,
.mask = 0x10,
},
.drdy_irq = {
.addr = ST_MAGN_3_DRDY_IRQ_ADDR,
.mask_int1 = ST_MAGN_3_DRDY_INT_MASK,
.addr_ihl = ST_MAGN_3_IHL_IRQ_ADDR,
.mask_ihl = ST_MAGN_3_IHL_IRQ_MASK,
.addr = 0x62,
.mask_int1 = 0x01,
.addr_ihl = 0x63,
.mask_ihl = 0x04,
.addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR,
},
.multi_read_bit = ST_MAGN_3_MULTIREAD_BIT,
.multi_read_bit = false,
.bootime = 2,
},
};

Просмотреть файл

@ -38,7 +38,7 @@
struct mcp4531_cfg {
int wipers;
int max_pos;
int avail[3];
int kohms;
};
@ -78,38 +78,38 @@ enum mcp4531_type {
};
static const struct mcp4531_cfg mcp4531_cfg[] = {
[MCP453x_502] = { .wipers = 1, .max_pos = 128, .kohms = 5, },
[MCP453x_103] = { .wipers = 1, .max_pos = 128, .kohms = 10, },
[MCP453x_503] = { .wipers = 1, .max_pos = 128, .kohms = 50, },
[MCP453x_104] = { .wipers = 1, .max_pos = 128, .kohms = 100, },
[MCP454x_502] = { .wipers = 1, .max_pos = 128, .kohms = 5, },
[MCP454x_103] = { .wipers = 1, .max_pos = 128, .kohms = 10, },
[MCP454x_503] = { .wipers = 1, .max_pos = 128, .kohms = 50, },
[MCP454x_104] = { .wipers = 1, .max_pos = 128, .kohms = 100, },
[MCP455x_502] = { .wipers = 1, .max_pos = 256, .kohms = 5, },
[MCP455x_103] = { .wipers = 1, .max_pos = 256, .kohms = 10, },
[MCP455x_503] = { .wipers = 1, .max_pos = 256, .kohms = 50, },
[MCP455x_104] = { .wipers = 1, .max_pos = 256, .kohms = 100, },
[MCP456x_502] = { .wipers = 1, .max_pos = 256, .kohms = 5, },
[MCP456x_103] = { .wipers = 1, .max_pos = 256, .kohms = 10, },
[MCP456x_503] = { .wipers = 1, .max_pos = 256, .kohms = 50, },
[MCP456x_104] = { .wipers = 1, .max_pos = 256, .kohms = 100, },
[MCP463x_502] = { .wipers = 2, .max_pos = 128, .kohms = 5, },
[MCP463x_103] = { .wipers = 2, .max_pos = 128, .kohms = 10, },
[MCP463x_503] = { .wipers = 2, .max_pos = 128, .kohms = 50, },
[MCP463x_104] = { .wipers = 2, .max_pos = 128, .kohms = 100, },
[MCP464x_502] = { .wipers = 2, .max_pos = 128, .kohms = 5, },
[MCP464x_103] = { .wipers = 2, .max_pos = 128, .kohms = 10, },
[MCP464x_503] = { .wipers = 2, .max_pos = 128, .kohms = 50, },
[MCP464x_104] = { .wipers = 2, .max_pos = 128, .kohms = 100, },
[MCP465x_502] = { .wipers = 2, .max_pos = 256, .kohms = 5, },
[MCP465x_103] = { .wipers = 2, .max_pos = 256, .kohms = 10, },
[MCP465x_503] = { .wipers = 2, .max_pos = 256, .kohms = 50, },
[MCP465x_104] = { .wipers = 2, .max_pos = 256, .kohms = 100, },
[MCP466x_502] = { .wipers = 2, .max_pos = 256, .kohms = 5, },
[MCP466x_103] = { .wipers = 2, .max_pos = 256, .kohms = 10, },
[MCP466x_503] = { .wipers = 2, .max_pos = 256, .kohms = 50, },
[MCP466x_104] = { .wipers = 2, .max_pos = 256, .kohms = 100, },
[MCP453x_502] = { .wipers = 1, .avail = { 0, 1, 128 }, .kohms = 5, },
[MCP453x_103] = { .wipers = 1, .avail = { 0, 1, 128 }, .kohms = 10, },
[MCP453x_503] = { .wipers = 1, .avail = { 0, 1, 128 }, .kohms = 50, },
[MCP453x_104] = { .wipers = 1, .avail = { 0, 1, 128 }, .kohms = 100, },
[MCP454x_502] = { .wipers = 1, .avail = { 0, 1, 128 }, .kohms = 5, },
[MCP454x_103] = { .wipers = 1, .avail = { 0, 1, 128 }, .kohms = 10, },
[MCP454x_503] = { .wipers = 1, .avail = { 0, 1, 128 }, .kohms = 50, },
[MCP454x_104] = { .wipers = 1, .avail = { 0, 1, 128 }, .kohms = 100, },
[MCP455x_502] = { .wipers = 1, .avail = { 0, 1, 256 }, .kohms = 5, },
[MCP455x_103] = { .wipers = 1, .avail = { 0, 1, 256 }, .kohms = 10, },
[MCP455x_503] = { .wipers = 1, .avail = { 0, 1, 256 }, .kohms = 50, },
[MCP455x_104] = { .wipers = 1, .avail = { 0, 1, 256 }, .kohms = 100, },
[MCP456x_502] = { .wipers = 1, .avail = { 0, 1, 256 }, .kohms = 5, },
[MCP456x_103] = { .wipers = 1, .avail = { 0, 1, 256 }, .kohms = 10, },
[MCP456x_503] = { .wipers = 1, .avail = { 0, 1, 256 }, .kohms = 50, },
[MCP456x_104] = { .wipers = 1, .avail = { 0, 1, 256 }, .kohms = 100, },
[MCP463x_502] = { .wipers = 2, .avail = { 0, 1, 128 }, .kohms = 5, },
[MCP463x_103] = { .wipers = 2, .avail = { 0, 1, 128 }, .kohms = 10, },
[MCP463x_503] = { .wipers = 2, .avail = { 0, 1, 128 }, .kohms = 50, },
[MCP463x_104] = { .wipers = 2, .avail = { 0, 1, 128 }, .kohms = 100, },
[MCP464x_502] = { .wipers = 2, .avail = { 0, 1, 128 }, .kohms = 5, },
[MCP464x_103] = { .wipers = 2, .avail = { 0, 1, 128 }, .kohms = 10, },
[MCP464x_503] = { .wipers = 2, .avail = { 0, 1, 128 }, .kohms = 50, },
[MCP464x_104] = { .wipers = 2, .avail = { 0, 1, 128 }, .kohms = 100, },
[MCP465x_502] = { .wipers = 2, .avail = { 0, 1, 256 }, .kohms = 5, },
[MCP465x_103] = { .wipers = 2, .avail = { 0, 1, 256 }, .kohms = 10, },
[MCP465x_503] = { .wipers = 2, .avail = { 0, 1, 256 }, .kohms = 50, },
[MCP465x_104] = { .wipers = 2, .avail = { 0, 1, 256 }, .kohms = 100, },
[MCP466x_502] = { .wipers = 2, .avail = { 0, 1, 256 }, .kohms = 5, },
[MCP466x_103] = { .wipers = 2, .avail = { 0, 1, 256 }, .kohms = 10, },
[MCP466x_503] = { .wipers = 2, .avail = { 0, 1, 256 }, .kohms = 50, },
[MCP466x_104] = { .wipers = 2, .avail = { 0, 1, 256 }, .kohms = 100, },
};
#define MCP4531_WRITE (0 << 2)
@ -124,13 +124,14 @@ struct mcp4531_data {
const struct mcp4531_cfg *cfg;
};
#define MCP4531_CHANNEL(ch) { \
.type = IIO_RESISTANCE, \
.indexed = 1, \
.output = 1, \
.channel = (ch), \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
#define MCP4531_CHANNEL(ch) { \
.type = IIO_RESISTANCE, \
.indexed = 1, \
.output = 1, \
.channel = (ch), \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
.info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_RAW), \
}
static const struct iio_chan_spec mcp4531_channels[] = {
@ -156,13 +157,31 @@ static int mcp4531_read_raw(struct iio_dev *indio_dev,
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
*val = 1000 * data->cfg->kohms;
*val2 = data->cfg->max_pos;
*val2 = data->cfg->avail[2];
return IIO_VAL_FRACTIONAL;
}
return -EINVAL;
}
static int mcp4531_read_avail(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
const int **vals, int *type, int *length,
long mask)
{
struct mcp4531_data *data = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_RAW:
*length = ARRAY_SIZE(data->cfg->avail);
*vals = data->cfg->avail;
*type = IIO_VAL_INT;
return IIO_AVAIL_RANGE;
}
return -EINVAL;
}
static int mcp4531_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
@ -172,7 +191,7 @@ static int mcp4531_write_raw(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_RAW:
if (val > data->cfg->max_pos || val < 0)
if (val > data->cfg->avail[2] || val < 0)
return -EINVAL;
break;
default:
@ -186,6 +205,7 @@ static int mcp4531_write_raw(struct iio_dev *indio_dev,
static const struct iio_info mcp4531_info = {
.read_raw = mcp4531_read_raw,
.read_avail = mcp4531_read_avail,
.write_raw = mcp4531_write_raw,
.driver_module = THIS_MODULE,
};

Просмотреть файл

@ -0,0 +1,22 @@
#
# Potentiostat drivers
#
# When adding new entries keep the list in alphabetical order
menu "Digital potentiostats"
config LMP91000
tristate "Texas Instruments LMP91000 potentiostat driver"
depends on I2C
select REGMAP_I2C
select IIO_BUFFER
select IIO_BUFFER_CB
select IIO_TRIGGERED_BUFFER
help
Say yes here to build support for the Texas Instruments
LMP91000 digital potentiostat chip.
To compile this driver as a module, choose M here: the
module will be called lmp91000
endmenu

Просмотреть файл

@ -0,0 +1,6 @@
#
# Makefile for industrial I/O potentiostat drivers
#
# When adding new entries keep the list in alphabetical order
obj-$(CONFIG_LMP91000) += lmp91000.o

Просмотреть файл

@ -0,0 +1,446 @@
/*
* lmp91000.c - Support for Texas Instruments digital potentiostats
*
* Copyright (C) 2016 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: bias voltage + polarity control, and multiple chip support
*/
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/of.h>
#include <linux/regmap.h>
#include <linux/iio/iio.h>
#include <linux/iio/buffer.h>
#include <linux/iio/consumer.h>
#include <linux/iio/trigger.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
#define LMP91000_REG_LOCK 0x01
#define LMP91000_REG_TIACN 0x10
#define LMP91000_REG_TIACN_GAIN_SHIFT 2
#define LMP91000_REG_REFCN 0x11
#define LMP91000_REG_REFCN_EXT_REF 0x20
#define LMP91000_REG_REFCN_50_ZERO 0x80
#define LMP91000_REG_MODECN 0x12
#define LMP91000_REG_MODECN_3LEAD 0x03
#define LMP91000_REG_MODECN_TEMP 0x07
#define LMP91000_DRV_NAME "lmp91000"
static const int lmp91000_tia_gain[] = { 0, 2750, 3500, 7000, 14000, 35000,
120000, 350000 };
static const int lmp91000_rload[] = { 10, 33, 50, 100 };
#define LMP91000_TEMP_BASE -40
static const u16 lmp91000_temp_lut[] = {
1875, 1867, 1860, 1852, 1844, 1836, 1828, 1821, 1813, 1805,
1797, 1789, 1782, 1774, 1766, 1758, 1750, 1742, 1734, 1727,
1719, 1711, 1703, 1695, 1687, 1679, 1671, 1663, 1656, 1648,
1640, 1632, 1624, 1616, 1608, 1600, 1592, 1584, 1576, 1568,
1560, 1552, 1544, 1536, 1528, 1520, 1512, 1504, 1496, 1488,
1480, 1472, 1464, 1456, 1448, 1440, 1432, 1424, 1415, 1407,
1399, 1391, 1383, 1375, 1367, 1359, 1351, 1342, 1334, 1326,
1318, 1310, 1302, 1293, 1285, 1277, 1269, 1261, 1253, 1244,
1236, 1228, 1220, 1212, 1203, 1195, 1187, 1179, 1170, 1162,
1154, 1146, 1137, 1129, 1121, 1112, 1104, 1096, 1087, 1079,
1071, 1063, 1054, 1046, 1038, 1029, 1021, 1012, 1004, 996,
987, 979, 971, 962, 954, 945, 937, 929, 920, 912,
903, 895, 886, 878, 870, 861 };
static const struct regmap_config lmp91000_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
};
struct lmp91000_data {
struct regmap *regmap;
struct device *dev;
struct iio_trigger *trig;
struct iio_cb_buffer *cb_buffer;
struct iio_channel *adc_chan;
struct completion completion;
u8 chan_select;
u32 buffer[4]; /* 64-bit data + 64-bit timestamp */
};
static const struct iio_chan_spec lmp91000_channels[] = {
{ /* chemical channel mV */
.type = IIO_VOLTAGE,
.channel = 0,
.address = LMP91000_REG_MODECN_3LEAD,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_OFFSET) |
BIT(IIO_CHAN_INFO_SCALE),
.scan_index = 0,
.scan_type = {
.sign = 's',
.realbits = 32,
.storagebits = 32,
},
},
IIO_CHAN_SOFT_TIMESTAMP(1),
{ /* temperature channel mV */
.type = IIO_TEMP,
.channel = 1,
.address = LMP91000_REG_MODECN_TEMP,
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
.scan_index = -1,
},
};
static int lmp91000_read(struct lmp91000_data *data, int channel, int *val)
{
int state, ret;
ret = regmap_read(data->regmap, LMP91000_REG_MODECN, &state);
if (ret)
return -EINVAL;
ret = regmap_write(data->regmap, LMP91000_REG_MODECN, channel);
if (ret)
return -EINVAL;
/* delay till first temperature reading is complete */
if ((state != channel) && (channel == LMP91000_REG_MODECN_TEMP))
usleep_range(3000, 4000);
data->chan_select = channel != LMP91000_REG_MODECN_3LEAD;
iio_trigger_poll_chained(data->trig);
ret = wait_for_completion_timeout(&data->completion, HZ);
reinit_completion(&data->completion);
if (!ret)
return -ETIMEDOUT;
*val = data->buffer[data->chan_select];
return 0;
}
static irqreturn_t lmp91000_buffer_handler(int irq, void *private)
{
struct iio_poll_func *pf = private;
struct iio_dev *indio_dev = pf->indio_dev;
struct lmp91000_data *data = iio_priv(indio_dev);
int ret, val;
memset(data->buffer, 0, sizeof(data->buffer));
ret = lmp91000_read(data, LMP91000_REG_MODECN_3LEAD, &val);
if (!ret) {
data->buffer[0] = val;
iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
iio_get_time_ns(indio_dev));
}
iio_trigger_notify_done(indio_dev->trig);
return IRQ_HANDLED;
}
static int lmp91000_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct lmp91000_data *data = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_RAW:
case IIO_CHAN_INFO_PROCESSED: {
int ret = iio_channel_start_all_cb(data->cb_buffer);
if (ret)
return ret;
ret = lmp91000_read(data, chan->address, val);
iio_channel_stop_all_cb(data->cb_buffer);
if (ret)
return ret;
if (mask == IIO_CHAN_INFO_PROCESSED) {
int tmp, i;
ret = iio_convert_raw_to_processed(data->adc_chan,
*val, &tmp, 1);
if (ret)
return ret;
for (i = 0; i < ARRAY_SIZE(lmp91000_temp_lut); i++)
if (lmp91000_temp_lut[i] < tmp)
break;
*val = (LMP91000_TEMP_BASE + i) * 1000;
}
return IIO_VAL_INT;
}
case IIO_CHAN_INFO_OFFSET:
return iio_read_channel_offset(data->adc_chan, val, val2);
case IIO_CHAN_INFO_SCALE:
return iio_read_channel_scale(data->adc_chan, val, val2);
}
return -EINVAL;
}
static const struct iio_info lmp91000_info = {
.driver_module = THIS_MODULE,
.read_raw = lmp91000_read_raw,
};
static int lmp91000_read_config(struct lmp91000_data *data)
{
struct device *dev = data->dev;
struct device_node *np = dev->of_node;
unsigned int reg, val;
int i, ret;
ret = of_property_read_u32(np, "ti,tia-gain-ohm", &val);
if (ret) {
if (of_property_read_bool(np, "ti,external-tia-resistor"))
val = 0;
else {
dev_err(dev, "no ti,tia-gain-ohm defined");
return ret;
}
}
ret = -EINVAL;
for (i = 0; i < ARRAY_SIZE(lmp91000_tia_gain); i++) {
if (lmp91000_tia_gain[i] == val) {
reg = i << LMP91000_REG_TIACN_GAIN_SHIFT;
ret = 0;
break;
}
}
if (ret) {
dev_err(dev, "invalid ti,tia-gain-ohm %d\n", val);
return ret;
}
ret = of_property_read_u32(np, "ti,rload-ohm", &val);
if (ret) {
val = 100;
dev_info(dev, "no ti,rload-ohm defined, default to %d\n", val);
}
ret = -EINVAL;
for (i = 0; i < ARRAY_SIZE(lmp91000_rload); i++) {
if (lmp91000_rload[i] == val) {
reg |= i;
ret = 0;
break;
}
}
if (ret) {
dev_err(dev, "invalid ti,rload-ohm %d\n", val);
return ret;
}
regmap_write(data->regmap, LMP91000_REG_LOCK, 0);
regmap_write(data->regmap, LMP91000_REG_TIACN, reg);
regmap_write(data->regmap, LMP91000_REG_REFCN, LMP91000_REG_REFCN_EXT_REF
| LMP91000_REG_REFCN_50_ZERO);
regmap_write(data->regmap, LMP91000_REG_LOCK, 1);
return 0;
}
static int lmp91000_buffer_cb(const void *val, void *private)
{
struct iio_dev *indio_dev = private;
struct lmp91000_data *data = iio_priv(indio_dev);
data->buffer[data->chan_select] = *((int *)val);
complete_all(&data->completion);
return 0;
}
static const struct iio_trigger_ops lmp91000_trigger_ops = {
.owner = THIS_MODULE,
};
static int lmp91000_buffer_preenable(struct iio_dev *indio_dev)
{
struct lmp91000_data *data = iio_priv(indio_dev);
return iio_channel_start_all_cb(data->cb_buffer);
}
static int lmp91000_buffer_predisable(struct iio_dev *indio_dev)
{
struct lmp91000_data *data = iio_priv(indio_dev);
iio_channel_stop_all_cb(data->cb_buffer);
return 0;
}
static const struct iio_buffer_setup_ops lmp91000_buffer_setup_ops = {
.preenable = lmp91000_buffer_preenable,
.postenable = iio_triggered_buffer_postenable,
.predisable = lmp91000_buffer_predisable,
};
static int lmp91000_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct device *dev = &client->dev;
struct lmp91000_data *data;
struct iio_dev *indio_dev;
int ret;
indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
indio_dev->info = &lmp91000_info;
indio_dev->channels = lmp91000_channels;
indio_dev->num_channels = ARRAY_SIZE(lmp91000_channels);
indio_dev->name = LMP91000_DRV_NAME;
indio_dev->modes = INDIO_DIRECT_MODE;
i2c_set_clientdata(client, indio_dev);
data = iio_priv(indio_dev);
data->dev = dev;
data->regmap = devm_regmap_init_i2c(client, &lmp91000_regmap_config);
if (IS_ERR(data->regmap)) {
dev_err(dev, "regmap initialization failed.\n");
return PTR_ERR(data->regmap);
}
data->trig = devm_iio_trigger_alloc(data->dev, "%s-mux%d",
indio_dev->name, indio_dev->id);
if (!data->trig) {
dev_err(dev, "cannot allocate iio trigger.\n");
return -ENOMEM;
}
data->trig->ops = &lmp91000_trigger_ops;
data->trig->dev.parent = dev;
init_completion(&data->completion);
ret = lmp91000_read_config(data);
if (ret)
return ret;
ret = iio_trigger_set_immutable(iio_channel_cb_get_iio_dev(data->cb_buffer),
data->trig);
if (ret) {
dev_err(dev, "cannot set immutable trigger.\n");
return ret;
}
ret = iio_trigger_register(data->trig);
if (ret) {
dev_err(dev, "cannot register iio trigger.\n");
return ret;
}
ret = iio_triggered_buffer_setup(indio_dev, NULL,
&lmp91000_buffer_handler,
&lmp91000_buffer_setup_ops);
if (ret)
goto error_unreg_trigger;
data->cb_buffer = iio_channel_get_all_cb(dev, &lmp91000_buffer_cb,
indio_dev);
if (IS_ERR(data->cb_buffer)) {
if (PTR_ERR(data->cb_buffer) == -ENODEV)
ret = -EPROBE_DEFER;
else
ret = PTR_ERR(data->cb_buffer);
goto error_unreg_buffer;
}
data->adc_chan = iio_channel_cb_get_channels(data->cb_buffer);
ret = iio_device_register(indio_dev);
if (ret)
goto error_unreg_cb_buffer;
return 0;
error_unreg_cb_buffer:
iio_channel_release_all_cb(data->cb_buffer);
error_unreg_buffer:
iio_triggered_buffer_cleanup(indio_dev);
error_unreg_trigger:
iio_trigger_unregister(data->trig);
return ret;
}
static int lmp91000_remove(struct i2c_client *client)
{
struct iio_dev *indio_dev = i2c_get_clientdata(client);
struct lmp91000_data *data = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
iio_channel_stop_all_cb(data->cb_buffer);
iio_channel_release_all_cb(data->cb_buffer);
iio_triggered_buffer_cleanup(indio_dev);
iio_trigger_unregister(data->trig);
return 0;
}
static const struct of_device_id lmp91000_of_match[] = {
{ .compatible = "ti,lmp91000", },
{ },
};
MODULE_DEVICE_TABLE(of, lmp91000_of_match);
static const struct i2c_device_id lmp91000_id[] = {
{ "lmp91000", 0 },
{}
};
MODULE_DEVICE_TABLE(i2c, lmp91000_id);
static struct i2c_driver lmp91000_driver = {
.driver = {
.name = LMP91000_DRV_NAME,
.of_match_table = of_match_ptr(lmp91000_of_match),
},
.probe = lmp91000_probe,
.remove = lmp91000_remove,
.id_table = lmp91000_id,
};
module_i2c_driver(lmp91000_driver);
MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
MODULE_DESCRIPTION("LMP91000 digital potentiostat");
MODULE_LICENSE("GPL");

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше