staging/iio driver patches for 4.11-rc1
Here is the big staging and iio driver patchsets for 4.11-rc1. We almost broke even this time around, with only a few thousand lines added overall, as we removed the old and obsolete i4l code, but added some new drivers for the RPi platform, as well as adding some new IIO drivers. All of these have been in linux-next for a while with no reported issues. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCWK2j/w8cZ3JlZ0Brcm9h aC5jb20ACgkQMUfUDdst+ymZ1ACdFR4o6xYrWEizmao4a/u+lUZE1aIAnRmcGcIc J+leO1n9bE5iadQvKYUW =sKVA -----END PGP SIGNATURE----- Merge tag 'staging-4.11-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging Pull staging/iio driver updates from Greg KH: "Here is the big staging and iio driver patchsets for 4.11-rc1. We almost broke even this time around, with only a few thousand lines added overall, as we removed the old and obsolete i4l code, but added some new drivers for the RPi platform, as well as adding some new IIO drivers. All of these have been in linux-next for a while with no reported issues" * tag 'staging-4.11-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging: (669 commits) Staging: vc04_services: Fix the "space prohibited" code style errors Staging: vc04_services: Fix the "wrong indent" code style errors staging: octeon: Use net_device_stats from struct net_device Staging: rtl8192u: ieee80211: ieee80211.h - style fix Staging: rtl8192u: ieee80211: ieee80211_tx.c - style fix Staging: rtl8192u: ieee80211: rtl819x_BAProc.c - style fix Staging: rtl8192u: ieee80211: ieee80211_module.c - style fix Staging: rtl8192u: ieee80211: rtl819x_TSProc.c - style fix Staging: rtl8192u: r8192U.h - style fix Staging: rtl8192u: r8192U_core.c - style fix Staging: rtl8192u: r819xU_cmdpkt.c - style fix staging: rtl8192u: blank lines aren't necessary before a close brace '}' staging: rtl8192u: Adding space after enum and struct definition staging: rtl8192u: Adding space after struct definition Staging: ks7010: Add required and preferred spaces around operators Staging: ks7010: ks*: Remove redundant blank lines Staging: ks7010: ks*: Add missing blank lines after declarations staging: visorbus, replace init_timer with setup_timer staging: vt6656: rxtx.c Removed multiple dereferencing staging: vt6656: Alignment match open parenthesis ...
This commit is contained in:
commit
caa5942897
|
@ -170,6 +170,16 @@ Description:
|
|||
Has all of the equivalent parameters as per voltageY. Units
|
||||
after application of scale and offset are m/s^2.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_gravity_x_raw
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_gravity_y_raw
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_gravity_z_raw
|
||||
KernelVersion: 4.11
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Gravity in direction x, y or z (may be arbitrarily assigned
|
||||
but should match other such assignments on device).
|
||||
Units after application of scale and offset are m/s^2.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_anglvel_x_raw
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_anglvel_y_raw
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_anglvel_z_raw
|
||||
|
@ -805,7 +815,7 @@ Description:
|
|||
attribute. E.g. if in_voltage0_raw_thresh_rising_value is set to 1200
|
||||
and in_voltage0_raw_thresh_rising_hysteresis is set to 50. The event
|
||||
will get activated once in_voltage0_raw goes above 1200 and will become
|
||||
deactived again once the value falls below 1150.
|
||||
deactivated again once the value falls below 1150.
|
||||
|
||||
What: /sys/.../events/in_accel_x_raw_roc_rising_value
|
||||
What: /sys/.../events/in_accel_x_raw_roc_falling_value
|
||||
|
@ -1245,7 +1255,8 @@ Description:
|
|||
reflectivity of infrared or ultrasound emitted.
|
||||
Often these sensors are unit less and as such conversion
|
||||
to SI units is not possible. Higher proximity measurements
|
||||
indicate closer objects, and vice versa.
|
||||
indicate closer objects, and vice versa. Units after
|
||||
application of scale and offset are meters.
|
||||
|
||||
What: /sys/.../iio:deviceX/in_illuminance_input
|
||||
What: /sys/.../iio:deviceX/in_illuminance_raw
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
What: /sys/bus/iio/devices/triggerX/trigger_polarity
|
||||
KernelVersion: 4.11
|
||||
Contact: fabrice.gasnier@st.com
|
||||
Description:
|
||||
The STM32 ADC can be configured to use external trigger sources
|
||||
(e.g. timers, pwm or exti gpio). Then, it can be tuned to start
|
||||
conversions on external trigger by either:
|
||||
- "rising-edge"
|
||||
- "falling-edge"
|
||||
- "both-edges".
|
||||
Reading returns current trigger polarity.
|
||||
Writing value before enabling conversions sets trigger polarity.
|
||||
|
||||
What: /sys/bus/iio/devices/triggerX/trigger_polarity_available
|
||||
KernelVersion: 4.11
|
||||
Contact: fabrice.gasnier@st.com
|
||||
Description:
|
||||
List all available trigger_polarity settings.
|
|
@ -0,0 +1,22 @@
|
|||
What /sys/bus/iio/devices/iio:deviceX/sensor_sensitivity
|
||||
Date: January 2017
|
||||
KernelVersion: 4.11
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Show or set the gain boost of the amp, from 0-31 range.
|
||||
default 31
|
||||
|
||||
What /sys/bus/iio/devices/iio:deviceX/sensor_max_range
|
||||
Date: January 2017
|
||||
KernelVersion: 4.11
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Show or set the maximum range between the sensor and the
|
||||
first object echoed in meters. Default value is 6.020.
|
||||
This setting limits the time the driver is waiting for a
|
||||
echo.
|
||||
Showing the range of available values is represented as the
|
||||
minimum value, the step and the maximum value, all enclosed
|
||||
in square brackets.
|
||||
Example:
|
||||
[0.043 0.043 11.008]
|
|
@ -0,0 +1,29 @@
|
|||
What: /sys/bus/iio/devices/triggerX/master_mode_available
|
||||
KernelVersion: 4.11
|
||||
Contact: benjamin.gaignard@st.com
|
||||
Description:
|
||||
Reading returns the list possible master modes which are:
|
||||
- "reset" : The UG bit from the TIMx_EGR register is used as trigger output (TRGO).
|
||||
- "enable" : The Counter Enable signal CNT_EN is used as trigger output.
|
||||
- "update" : The update event is selected as trigger output.
|
||||
For instance a master timer can then be used as a prescaler for a slave timer.
|
||||
- "compare_pulse" : The trigger output send a positive pulse when the CC1IF flag is to be set.
|
||||
- "OC1REF" : OC1REF signal is used as trigger output.
|
||||
- "OC2REF" : OC2REF signal is used as trigger output.
|
||||
- "OC3REF" : OC3REF signal is used as trigger output.
|
||||
- "OC4REF" : OC4REF signal is used as trigger output.
|
||||
|
||||
What: /sys/bus/iio/devices/triggerX/master_mode
|
||||
KernelVersion: 4.11
|
||||
Contact: benjamin.gaignard@st.com
|
||||
Description:
|
||||
Reading returns the current master modes.
|
||||
Writing set the master mode
|
||||
|
||||
What: /sys/bus/iio/devices/triggerX/sampling_frequency
|
||||
KernelVersion: 4.11
|
||||
Contact: benjamin.gaignard@st.com
|
||||
Description:
|
||||
Reading returns the current sampling frequency.
|
||||
Writing an value different of 0 set and start sampling.
|
||||
Writing 0 stop sampling.
|
|
@ -36,6 +36,7 @@ dallas,ds1775 Tiny Digital Thermometer and Thermostat
|
|||
dallas,ds3232 Extremely Accurate I²C RTC with Integrated Crystal and SRAM
|
||||
dallas,ds4510 CPU Supervisor with Nonvolatile Memory and Programmable I/O
|
||||
dallas,ds75 Digital Thermometer and Thermostat
|
||||
devantech,srf08 Devantech SRF08 ultrasonic ranger
|
||||
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
|
||||
|
|
|
@ -5,7 +5,7 @@ that apply in on the generic device (independent from the bus).
|
|||
|
||||
|
||||
Required properties for the SPI bindings:
|
||||
- compatible: should be set to "st,lis3lv02d_spi"
|
||||
- compatible: should be set to "st,lis3lv02d-spi"
|
||||
- reg: the chipselect index
|
||||
- spi-max-frequency: maximal bus speed, should be set to 1000000 unless
|
||||
constrained by external circuitry
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
* Amlogic Meson SAR (Successive Approximation Register) A/D converter
|
||||
|
||||
Required properties:
|
||||
- compatible: depending on the SoC this should be one of:
|
||||
- "amlogic,meson-gxbb-saradc" for GXBB
|
||||
- "amlogic,meson-gxl-saradc" for GXL
|
||||
- "amlogic,meson-gxm-saradc" for GXM
|
||||
along with the generic "amlogic,meson-saradc"
|
||||
- reg: the physical base address and length of the registers
|
||||
- clocks: phandle and clock identifier (see clock-names)
|
||||
- clock-names: mandatory clocks:
|
||||
- "clkin" for the reference clock (typically XTAL)
|
||||
- "core" for the SAR ADC core clock
|
||||
optional clocks:
|
||||
- "sana" for the analog clock
|
||||
- "adc_clk" for the ADC (sampling) clock
|
||||
- "adc_sel" for the ADC (sampling) clock mux
|
||||
- vref-supply: the regulator supply for the ADC reference voltage
|
||||
- #io-channel-cells: must be 1, see ../iio-bindings.txt
|
||||
|
||||
Example:
|
||||
saradc: adc@8680 {
|
||||
compatible = "amlogic,meson-gxl-saradc", "amlogic,meson-saradc";
|
||||
#io-channel-cells = <1>;
|
||||
reg = <0x0 0x8680 0x0 0x34>;
|
||||
clocks = <&xtal>,
|
||||
<&clkc CLKID_SAR_ADC>,
|
||||
<&clkc CLKID_SANA>,
|
||||
<&clkc CLKID_SAR_ADC_CLK>,
|
||||
<&clkc CLKID_SAR_ADC_SEL>;
|
||||
clock-names = "clkin", "core", "sana", "adc_clk", "adc_sel";
|
||||
};
|
|
@ -0,0 +1,18 @@
|
|||
* AVIA HX711 ADC chip for weight cells
|
||||
Bit-banging driver
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "avia,hx711"
|
||||
- sck-gpios: Definition of the GPIO for the clock
|
||||
- dout-gpios: Definition of the GPIO for data-out
|
||||
See Documentation/devicetree/bindings/gpio/gpio.txt
|
||||
- avdd-supply: Definition of the regulator used as analog supply
|
||||
|
||||
Example:
|
||||
weight@0 {
|
||||
compatible = "avia,hx711";
|
||||
sck-gpios = <&gpio3 10 GPIO_ACTIVE_HIGH>;
|
||||
dout-gpios = <&gpio0 7 GPIO_ACTIVE_HIGH>;
|
||||
avdd-suppy = <&avdd>;
|
||||
};
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
* Maxim max11100 Analog to Digital Converter (ADC)
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "maxim,max11100"
|
||||
- reg: the adc unit address
|
||||
- vref-supply: phandle to the regulator that provides reference voltage
|
||||
|
||||
Optional properties:
|
||||
- spi-max-frequency: SPI maximum frequency
|
||||
|
||||
Example:
|
||||
|
||||
max11100: adc@0 {
|
||||
compatible = "maxim,max11100";
|
||||
reg = <0>;
|
||||
vref-supply = <&adc0_vref>;
|
||||
spi-max-frequency = <240000>;
|
||||
};
|
|
@ -0,0 +1,149 @@
|
|||
Qualcomm's PM8xxx voltage XOADC
|
||||
|
||||
The Qualcomm PM8xxx PMICs contain a HK/XO ADC (Housekeeping/Crystal
|
||||
oscillator ADC) encompassing PM8018, PM8038, PM8058 and PM8921.
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: should be one of:
|
||||
"qcom,pm8018-adc"
|
||||
"qcom,pm8038-adc"
|
||||
"qcom,pm8058-adc"
|
||||
"qcom,pm8921-adc"
|
||||
|
||||
- reg: should contain the ADC base address in the PMIC, typically
|
||||
0x197.
|
||||
|
||||
- xoadc-ref-supply: should reference a regulator that can supply
|
||||
a reference voltage on demand. The reference voltage may vary
|
||||
with PMIC variant but is typically something like 2.2 or 1.8V.
|
||||
|
||||
The following required properties are standard for IO channels, see
|
||||
iio-bindings.txt for more details:
|
||||
|
||||
- #address-cells: should be set to <1>
|
||||
|
||||
- #size-cells: should be set to <0>
|
||||
|
||||
- #io-channel-cells: should be set to <1>
|
||||
|
||||
- interrupts: should refer to the parent PMIC interrupt controller
|
||||
and reference the proper ADC interrupt.
|
||||
|
||||
Required subnodes:
|
||||
|
||||
The ADC channels are configured as subnodes of the ADC. Since some of
|
||||
them are used for calibrating the ADC, these nodes are compulsory:
|
||||
|
||||
adc-channel@c {
|
||||
reg = <0x0c>;
|
||||
};
|
||||
|
||||
adc-channel@d {
|
||||
reg = <0x0d>;
|
||||
};
|
||||
|
||||
adc-channel@f {
|
||||
reg = <0x0f>;
|
||||
};
|
||||
|
||||
These three nodes are used for absolute and ratiometric calibration
|
||||
and only need to have these reg values: they are by hardware definition
|
||||
1:1 ratio converters that sample 625, 1250 and 0 milliV and create
|
||||
an interpolation calibration for all other ADCs.
|
||||
|
||||
Optional subnodes: any channels other than channel 0x0c, 0x0d and
|
||||
0x0f are optional.
|
||||
|
||||
Required channel node properties:
|
||||
|
||||
- reg: should contain the hardware channel number in the range
|
||||
0 .. 0x0f (4 bits). The hardware only supports 16 channels.
|
||||
|
||||
Optional channel node properties:
|
||||
|
||||
- qcom,decimation:
|
||||
Value type: <u32>
|
||||
Definition: This parameter is used to decrease the ADC sampling rate.
|
||||
Quicker measurements can be made by reducing the decimation ratio.
|
||||
Valid values are 512, 1024, 2048, 4096.
|
||||
If the property is not found, a default value of 512 will be used.
|
||||
|
||||
- qcom,ratiometric:
|
||||
Value type: <u32>
|
||||
Definition: Channel calibration type. If this property is specified
|
||||
VADC will use a special voltage references for channel
|
||||
calibration. The available references are specified in the
|
||||
as a u32 value setting (see below) and it is compulsory
|
||||
to also specify this reference if ratiometric calibration
|
||||
is selected.
|
||||
|
||||
If the property is not found, the channel will be
|
||||
calibrated with the 0.625V and 1.25V reference channels, also
|
||||
known as an absolute calibration.
|
||||
The reference voltage pairs when using ratiometric calibration:
|
||||
0 = XO_IN/XOADC_GND
|
||||
1 = PMIC_IN/XOADC_GND
|
||||
2 = PMIC_IN/BMS_CSP
|
||||
3 (invalid)
|
||||
4 = XOADC_GND/XOADC_GND
|
||||
5 = XOADC_VREF/XOADC_GND
|
||||
|
||||
Example:
|
||||
|
||||
xoadc: xoadc@197 {
|
||||
compatible = "qcom,pm8058-adc";
|
||||
reg = <0x197>;
|
||||
interrupt-parent = <&pm8058>;
|
||||
interrupts = <76 1>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
#io-channel-cells = <1>;
|
||||
|
||||
vcoin: adc-channel@0 {
|
||||
reg = <0x00>;
|
||||
};
|
||||
vbat: adc-channel@1 {
|
||||
reg = <0x01>;
|
||||
};
|
||||
dcin: adc-channel@2 {
|
||||
reg = <0x02>;
|
||||
};
|
||||
ichg: adc-channel@3 {
|
||||
reg = <0x03>;
|
||||
};
|
||||
vph_pwr: adc-channel@4 {
|
||||
reg = <0x04>;
|
||||
};
|
||||
usb_vbus: adc-channel@a {
|
||||
reg = <0x0a>;
|
||||
};
|
||||
die_temp: adc-channel@b {
|
||||
reg = <0x0b>;
|
||||
};
|
||||
ref_625mv: adc-channel@c {
|
||||
reg = <0x0c>;
|
||||
};
|
||||
ref_1250mv: adc-channel@d {
|
||||
reg = <0x0d>;
|
||||
};
|
||||
ref_325mv: adc-channel@e {
|
||||
reg = <0x0e>;
|
||||
};
|
||||
ref_muxoff: adc-channel@f {
|
||||
reg = <0x0f>;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
/* IIO client node */
|
||||
iio-hwmon {
|
||||
compatible = "iio-hwmon";
|
||||
io-channels = <&xoadc 0x01>, /* Battery */
|
||||
<&xoadc 0x02>, /* DC in (charger) */
|
||||
<&xoadc 0x04>, /* VPH the main system voltage */
|
||||
<&xoadc 0x0b>, /* Die temperature */
|
||||
<&xoadc 0x0c>, /* Reference voltage 1.25V */
|
||||
<&xoadc 0x0d>, /* Reference voltage 0.625V */
|
||||
<&xoadc 0x0e>; /* Reference voltage 0.325V */
|
||||
};
|
|
@ -0,0 +1,99 @@
|
|||
* Renesas RCar GyroADC device driver
|
||||
|
||||
The GyroADC block is a reduced SPI block with up to 8 chipselect lines,
|
||||
which supports the SPI protocol of a selected few SPI ADCs. The SPI ADCs
|
||||
are sampled by the GyroADC block in a round-robin fashion and the result
|
||||
presented in the GyroADC registers.
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "<soc-specific>", "renesas,rcar-gyroadc".
|
||||
The <soc-specific> should be one of:
|
||||
renesas,r8a7791-gyroadc - for the GyroADC block present
|
||||
in r8a7791 SoC
|
||||
renesas,r8a7792-gyroadc - for the GyroADC with interrupt
|
||||
block present in r8a7792 SoC
|
||||
- reg: Address and length of the register set for the device
|
||||
- clocks: References to all the clocks specified in the clock-names
|
||||
property as specified in
|
||||
Documentation/devicetree/bindings/clock/clock-bindings.txt.
|
||||
- clock-names: Shall contain "fck" and "if". The "fck" is the GyroADC block
|
||||
clock, the "if" is the interface clock.
|
||||
- power-domains: Must contain a reference to the PM domain, if available.
|
||||
- #address-cells: Should be <1> (setting for the subnodes) for all ADCs
|
||||
except for "fujitsu,mb88101a". Should be <0> (setting for
|
||||
only subnode) for "fujitsu,mb88101a".
|
||||
- #size-cells: Should be <0> (setting for the subnodes)
|
||||
|
||||
Sub-nodes:
|
||||
You must define subnode(s) which select the connected ADC type and reference
|
||||
voltage for the GyroADC channels.
|
||||
|
||||
Required properties for subnodes:
|
||||
- compatible: Should be either of:
|
||||
"fujitsu,mb88101a"
|
||||
- Fujitsu MB88101A compatible mode,
|
||||
12bit sampling, up to 4 channels can be sampled in
|
||||
round-robin fashion. One Fujitsu chip supplies four
|
||||
GyroADC channels with data as it contains four ADCs
|
||||
on the chip and thus for 4-channel operation, single
|
||||
MB88101A is required. The Cx chipselect lines of the
|
||||
MB88101A connect directly to two CHS lines of the
|
||||
GyroADC, no demuxer is required. The data out line
|
||||
of each MB88101A connects to a shared input pin of
|
||||
the GyroADC.
|
||||
"ti,adcs7476" or "ti,adc121" or "adi,ad7476"
|
||||
- TI ADCS7476 / TI ADC121 / ADI AD7476 compatible mode,
|
||||
15bit sampling, up to 8 channels can be sampled in
|
||||
round-robin fashion. One TI/ADI chip supplies single
|
||||
ADC channel with data, thus for 8-channel operation,
|
||||
8 chips are required. A 3:8 chipselect demuxer is
|
||||
required to connect the nCS line of the TI/ADI chips
|
||||
to the GyroADC, while MISO line of each TI/ADI ADC
|
||||
connects to a shared input pin of the GyroADC.
|
||||
"maxim,max1162" or "maxim,max11100"
|
||||
- Maxim MAX1162 / Maxim MAX11100 compatible mode,
|
||||
16bit sampling, up to 8 channels can be sampled in
|
||||
round-robin fashion. One Maxim chip supplies single
|
||||
ADC channel with data, thus for 8-channel operation,
|
||||
8 chips are required. A 3:8 chipselect demuxer is
|
||||
required to connect the nCS line of the MAX chips
|
||||
to the GyroADC, while MISO line of each Maxim ADC
|
||||
connects to a shared input pin of the GyroADC.
|
||||
- reg: Should be the number of the analog input. Should be present
|
||||
for all ADCs except "fujitsu,mb88101a".
|
||||
- vref-supply: Reference to the channel reference voltage regulator.
|
||||
|
||||
Example:
|
||||
vref_max1162: regulator-vref-max1162 {
|
||||
compatible = "regulator-fixed";
|
||||
|
||||
regulator-name = "MAX1162 Vref";
|
||||
regulator-min-microvolt = <4096000>;
|
||||
regulator-max-microvolt = <4096000>;
|
||||
};
|
||||
|
||||
adc@e6e54000 {
|
||||
compatible = "renesas,r8a7791-gyroadc", "renesas,rcar-gyroadc";
|
||||
reg = <0 0xe6e54000 0 64>;
|
||||
clocks = <&mstp9_clks R8A7791_CLK_GYROADC>, <&clk_65m>;
|
||||
clock-names = "fck", "if";
|
||||
power-domains = <&sysc R8A7791_PD_ALWAYS_ON>;
|
||||
|
||||
pinctrl-0 = <&adc_pins>;
|
||||
pinctrl-names = "default";
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
adc@0 {
|
||||
reg = <0>;
|
||||
compatible = "maxim,max1162";
|
||||
vref-supply = <&vref_max1162>;
|
||||
};
|
||||
|
||||
adc@1 {
|
||||
reg = <1>;
|
||||
compatible = "maxim,max1162";
|
||||
vref-supply = <&vref_max1162>;
|
||||
};
|
||||
};
|
|
@ -53,6 +53,11 @@ Required properties:
|
|||
- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers" in
|
||||
Documentation/devicetree/bindings/iio/iio-bindings.txt
|
||||
|
||||
Optional properties:
|
||||
- dmas: Phandle to dma channel for this ADC instance.
|
||||
See ../../dma/dma.txt for details.
|
||||
- dma-names: Must be "rx" when dmas property is being used.
|
||||
|
||||
Example:
|
||||
adc: adc@40012000 {
|
||||
compatible = "st,stm32f4-adc-core";
|
||||
|
@ -77,6 +82,8 @@ Example:
|
|||
interrupt-parent = <&adc>;
|
||||
interrupts = <0>;
|
||||
st,adc-channels = <8>;
|
||||
dmas = <&dma2 0 0 0x400 0x0>;
|
||||
dma-names = "rx";
|
||||
};
|
||||
...
|
||||
other adc child nodes follow...
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
* Texas Instruments ADS7950 family of A/DC chips
|
||||
|
||||
Required properties:
|
||||
- compatible: Must be one of "ti,ads7950", "ti,ads7951", "ti,ads7952",
|
||||
"ti,ads7953", "ti,ads7954", "ti,ads7955", "ti,ads7956", "ti,ads7957",
|
||||
"ti,ads7958", "ti,ads7959", "ti,ads7960", or "ti,ads7961"
|
||||
- reg: SPI chip select number for the device
|
||||
- #io-channel-cells: Must be 1 as per ../iio-bindings.txt
|
||||
- vref-supply: phandle to a regulator node that supplies the 2.5V or 5V
|
||||
reference voltage
|
||||
|
||||
Recommended properties:
|
||||
- spi-max-frequency: Definition as per
|
||||
Documentation/devicetree/bindings/spi/spi-bus.txt
|
||||
|
||||
Example:
|
||||
adc@0 {
|
||||
compatible = "ti,ads7957";
|
||||
reg = <0>;
|
||||
#io-channel-cells = <1>;
|
||||
vref-supply = <&refin_supply>;
|
||||
spi-max-frequency = <10000000>;
|
||||
};
|
|
@ -0,0 +1,36 @@
|
|||
Bosch BMI160 - Inertial Measurement Unit with Accelerometer, Gyroscope
|
||||
and externally connectable Magnetometer
|
||||
|
||||
https://www.bosch-sensortec.com/bst/products/all_products/bmi160
|
||||
|
||||
Required properties:
|
||||
- compatible : should be "bosch,bmi160"
|
||||
- reg : the I2C address or SPI chip select number of the sensor
|
||||
- spi-max-frequency : set maximum clock frequency (only for SPI)
|
||||
|
||||
Optional properties:
|
||||
- interrupt-parent : should be the phandle of the interrupt controller
|
||||
- interrupts : interrupt mapping for IRQ, must be IRQ_TYPE_LEVEL_LOW
|
||||
- interrupt-names : set to "INT1" if INT1 pin should be used as interrupt
|
||||
input, set to "INT2" if INT2 pin should be used instead
|
||||
|
||||
Examples:
|
||||
|
||||
bmi160@68 {
|
||||
compatible = "bosch,bmi160";
|
||||
reg = <0x68>;
|
||||
|
||||
interrupt-parent = <&gpio4>;
|
||||
interrupts = <12 IRQ_TYPE_LEVEL_LOW>;
|
||||
interrupt-names = "INT1";
|
||||
};
|
||||
|
||||
bmi160@0 {
|
||||
compatible = "bosch,bmi160";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <10000000>;
|
||||
|
||||
interrupt-parent = <&gpio2>;
|
||||
interrupts = <12 IRQ_TYPE_LEVEL_LOW>;
|
||||
interrupt-names = "INT2";
|
||||
};
|
|
@ -0,0 +1,26 @@
|
|||
* ST_LSM6DSx driver for STM 6-axis (acc + gyro) imu Mems sensors
|
||||
|
||||
Required properties:
|
||||
- compatible: must be one of:
|
||||
"st,lsm6ds3"
|
||||
"st,lsm6dsm"
|
||||
- reg: i2c address of the sensor / spi cs line
|
||||
|
||||
Optional properties:
|
||||
- st,drdy-int-pin: the pin on the package that will be used to signal
|
||||
"data ready" (valid values: 1 or 2).
|
||||
- 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:
|
||||
|
||||
lsm6dsm@6b {
|
||||
compatible = "st,lsm6dsm";
|
||||
reg = <0x6b>;
|
||||
interrupt-parent = <&gpio0>;
|
||||
interrupts = <0 IRQ_TYPE_EDGE_RISING>;
|
||||
};
|
|
@ -0,0 +1,41 @@
|
|||
Capella Microsystems CM3605
|
||||
Ambient Light and Short Distance Proximity Sensor
|
||||
|
||||
The CM3605 is an entirely analog part which however require quite a bit of
|
||||
software logic to interface a host operating system.
|
||||
|
||||
This ALS and proximity sensor was one of the very first deployed in mobile
|
||||
handsets, notably it is used in the very first Nexus One Android phone from
|
||||
2010.
|
||||
|
||||
Required properties:
|
||||
- compatible: must be: "capella,cm3605"
|
||||
- aset-gpios: GPIO line controlling the ASET line (drive low
|
||||
to activate the ALS, should be flagged GPIO_ACTIVE_LOW)
|
||||
- interrupts: the IRQ line (such as a GPIO) that is connected to
|
||||
the POUT (proximity sensor out) line. The edge detection must
|
||||
be set to IRQ_TYPE_EDGE_BOTH so as to detect movements toward
|
||||
and away from the proximity sensor.
|
||||
- io-channels: the ADC channel used for converting the voltage from
|
||||
AOUT to a digital representation.
|
||||
- io-channel-names: must be "aout"
|
||||
|
||||
Optional properties:
|
||||
- vdd-supply: regulator supplying VDD power to the component.
|
||||
- capella,aset-resistance-ohms: the sensitivity calibration resistance,
|
||||
in Ohms. Valid values are: 50000, 100000, 300000 and 600000,
|
||||
as these are the resistance values that we are supplied with
|
||||
calibration curves for. If not supplied, 100 kOhm will be assumed
|
||||
but it is strongly recommended to supply this.
|
||||
|
||||
Example:
|
||||
|
||||
cm3605 {
|
||||
compatible = "capella,cm3605";
|
||||
vdd-supply = <&foo_reg>;
|
||||
aset-gpios = <&foo_gpio 1 GPIO_ACTIVE_LOW>;
|
||||
capella,aset-resistance-ohms = <100000>;
|
||||
interrupts = <1 IRQ_TYPE_EDGE_BOTH>;
|
||||
io-channels = <&adc 0x01>;
|
||||
io-channel-names = "aout";
|
||||
};
|
|
@ -0,0 +1,23 @@
|
|||
* Maxim Linear-Taper Digital Potentiometer MAX5481-MAX5484
|
||||
|
||||
The node for this driver must be a child node of a SPI controller, hence
|
||||
all mandatory properties described in
|
||||
|
||||
Documentation/devicetree/bindings/spi/spi-bus.txt
|
||||
|
||||
must be specified.
|
||||
|
||||
Required properties:
|
||||
- compatible: Must be one of the following, depending on the
|
||||
model:
|
||||
"maxim,max5481"
|
||||
"maxim,max5482"
|
||||
"maxim,max5483"
|
||||
"maxim,max5484"
|
||||
|
||||
Example:
|
||||
max548x: max548x@0 {
|
||||
compatible = "maxim,max5482";
|
||||
spi-max-frequency = <7000000>;
|
||||
reg = <0>; /* chip-select */
|
||||
};
|
|
@ -27,6 +27,8 @@ standard bindings from pinctrl/pinctrl-bindings.txt.
|
|||
Valid compatible strings:
|
||||
|
||||
Accelerometers:
|
||||
- st,lis3lv02d (deprecated, use st,lis3lv02dl-accel)
|
||||
- st,lis302dl-spi (deprecated, use st,lis3lv02dl-accel)
|
||||
- st,lis3lv02dl-accel
|
||||
- st,lsm303dlh-accel
|
||||
- st,lsm303dlhc-accel
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
* TI TMP007 - IR thermopile sensor with integrated math engine
|
||||
|
||||
Link to datasheet: http://www.ti.com/lit/ds/symlink/tmp007.pdf
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: should be "ti,tmp007"
|
||||
- reg: the I2C address of the sensor (changeable via ADR pins)
|
||||
------------------------------
|
||||
|ADR1 | ADR0 | Device Address|
|
||||
------------------------------
|
||||
0 0 0x40
|
||||
0 1 0x41
|
||||
0 SDA 0x42
|
||||
0 SCL 0x43
|
||||
1 0 0x44
|
||||
1 1 0x45
|
||||
1 SDA 0x46
|
||||
1 SCL 0x47
|
||||
|
||||
Optional properties:
|
||||
|
||||
- interrupt-parent: should be the phandle for the interrupt controller
|
||||
|
||||
- interrupts: interrupt mapping for GPIO IRQ (level active low)
|
||||
|
||||
Example:
|
||||
|
||||
tmp007@40 {
|
||||
compatible = "ti,tmp007";
|
||||
reg = <0x40>;
|
||||
interrupt-parent = <&gpio0>;
|
||||
interrupts = <5 0x08>;
|
||||
};
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
STMicroelectronics STM32 Timers IIO timer bindings
|
||||
|
||||
Must be a sub-node of an STM32 Timers device tree node.
|
||||
See ../mfd/stm32-timers.txt for details about the parent node.
|
||||
|
||||
Required parameters:
|
||||
- compatible: Must be "st,stm32-timer-trigger".
|
||||
- reg: Identify trigger hardware block.
|
||||
|
||||
Example:
|
||||
timers@40010000 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "st,stm32-timers";
|
||||
reg = <0x40010000 0x400>;
|
||||
clocks = <&rcc 0 160>;
|
||||
clock-names = "clk_int";
|
||||
|
||||
timer@0 {
|
||||
compatible = "st,stm32-timer-trigger";
|
||||
reg = <0>;
|
||||
};
|
||||
};
|
|
@ -0,0 +1,46 @@
|
|||
STM32 Timers driver bindings
|
||||
|
||||
This IP provides 3 types of timer along with PWM functionality:
|
||||
- advanced-control timers consist of a 16-bit auto-reload counter driven by a programmable
|
||||
prescaler, break input feature, PWM outputs and complementary PWM ouputs channels.
|
||||
- general-purpose timers consist of a 16-bit or 32-bit auto-reload counter driven by a
|
||||
programmable prescaler and PWM outputs.
|
||||
- basic timers consist of a 16-bit auto-reload counter driven by a programmable prescaler.
|
||||
|
||||
Required parameters:
|
||||
- compatible: must be "st,stm32-timers"
|
||||
|
||||
- reg: Physical base address and length of the controller's
|
||||
registers.
|
||||
- clock-names: Set to "int".
|
||||
- clocks: Phandle to the clock used by the timer module.
|
||||
For Clk properties, please refer to ../clock/clock-bindings.txt
|
||||
|
||||
Optional parameters:
|
||||
- resets: Phandle to the parent reset controller.
|
||||
See ../reset/st,stm32-rcc.txt
|
||||
|
||||
Optional subnodes:
|
||||
- pwm: See ../pwm/pwm-stm32.txt
|
||||
- timer: See ../iio/timer/stm32-timer-trigger.txt
|
||||
|
||||
Example:
|
||||
timers@40010000 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "st,stm32-timers";
|
||||
reg = <0x40010000 0x400>;
|
||||
clocks = <&rcc 0 160>;
|
||||
clock-names = "clk_int";
|
||||
|
||||
pwm {
|
||||
compatible = "st,stm32-pwm";
|
||||
pinctrl-0 = <&pwm1_pins>;
|
||||
pinctrl-names = "default";
|
||||
};
|
||||
|
||||
timer@0 {
|
||||
compatible = "st,stm32-timer-trigger";
|
||||
reg = <0>;
|
||||
};
|
||||
};
|
|
@ -0,0 +1,35 @@
|
|||
STMicroelectronics STM32 Timers PWM bindings
|
||||
|
||||
Must be a sub-node of an STM32 Timers device tree node.
|
||||
See ../mfd/stm32-timers.txt for details about the parent node.
|
||||
|
||||
Required parameters:
|
||||
- compatible: Must be "st,stm32-pwm".
|
||||
- pinctrl-names: Set to "default".
|
||||
- pinctrl-0: List of phandles pointing to pin configuration nodes for PWM module.
|
||||
For Pinctrl properties see ../pinctrl/pinctrl-bindings.txt
|
||||
|
||||
Optional parameters:
|
||||
- st,breakinput: One or two <index level filter> to describe break input configurations.
|
||||
"index" indicates on which break input (0 or 1) the configuration
|
||||
should be applied.
|
||||
"level" gives the active level (0=low or 1=high) of the input signal
|
||||
for this configuration.
|
||||
"filter" gives the filtering value to be applied.
|
||||
|
||||
Example:
|
||||
timers@40010000 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "st,stm32-timers";
|
||||
reg = <0x40010000 0x400>;
|
||||
clocks = <&rcc 0 160>;
|
||||
clock-names = "clk_int";
|
||||
|
||||
pwm {
|
||||
compatible = "st,stm32-pwm";
|
||||
pinctrl-0 = <&pwm1_pins>;
|
||||
pinctrl-names = "default";
|
||||
st,breakinput = <0 1 5>;
|
||||
};
|
||||
};
|
|
@ -40,6 +40,7 @@ atmel Atmel Corporation
|
|||
auo AU Optronics Corporation
|
||||
auvidea Auvidea GmbH
|
||||
avago Avago Technologies
|
||||
avia avia semiconductor
|
||||
avic Shanghai AVIC Optoelectronics Co., Ltd.
|
||||
axentia Axentia Technologies AB
|
||||
axis Axis Communications AB
|
||||
|
@ -75,6 +76,7 @@ dallas Maxim Integrated Products (formerly Dallas Semiconductor)
|
|||
davicom DAVICOM Semiconductor, Inc.
|
||||
delta Delta Electronics, Inc.
|
||||
denx Denx Software Engineering
|
||||
devantech Devantech, Ltd.
|
||||
digi Digi International Inc.
|
||||
digilent Diglent, Inc.
|
||||
dlg Dialog Semiconductor
|
||||
|
@ -118,6 +120,7 @@ gmt Global Mixed-mode Technology, Inc.
|
|||
goodix Shenzhen Huiding Technology Co., Ltd.
|
||||
google Google, Inc.
|
||||
grinn Grinn
|
||||
grmn Garmin Limited
|
||||
gumstix Gumstix, Inc.
|
||||
gw Gateworks Corporation
|
||||
hannstar HannStar Display Corporation
|
||||
|
|
|
@ -10251,7 +10251,8 @@ 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>
|
||||
M: Stuart Yoder <stuyoder@gmail.com>
|
||||
M: Laurentiu Tudor <laurentiu.tudor@nxp.com>
|
||||
L: linux-kernel@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/staging/fsl-mc/
|
||||
|
@ -10528,6 +10529,12 @@ L: linux-renesas-soc@vger.kernel.org
|
|||
F: drivers/net/ethernet/renesas/
|
||||
F: include/linux/sh_eth.h
|
||||
|
||||
RENESAS R-CAR GYROADC DRIVER
|
||||
M: Marek Vasut <marek.vasut@gmail.com>
|
||||
L: linux-iio@vger.kernel.org
|
||||
S: Supported
|
||||
F: drivers/iio/adc/rcar_gyro_adc.c
|
||||
|
||||
RENESAS USB2 PHY DRIVER
|
||||
M: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
|
||||
L: linux-renesas-soc@vger.kernel.org
|
||||
|
|
|
@ -120,6 +120,8 @@ config HID_SENSOR_ACCEL_3D
|
|||
config IIO_ST_ACCEL_3AXIS
|
||||
tristate "STMicroelectronics accelerometers 3-Axis Driver"
|
||||
depends on (I2C || SPI_MASTER) && SYSFS
|
||||
depends on !SENSORS_LIS3_I2C
|
||||
depends on !SENSORS_LIS3_SPI
|
||||
select IIO_ST_SENSORS_CORE
|
||||
select IIO_ST_ACCEL_I2C_3AXIS if (I2C)
|
||||
select IIO_ST_ACCEL_SPI_3AXIS if (SPI_MASTER)
|
||||
|
|
|
@ -1638,7 +1638,8 @@ int bmc150_accel_core_probe(struct device *dev, struct regmap *regmap, int irq,
|
|||
if (block_supported) {
|
||||
indio_dev->modes |= INDIO_BUFFER_SOFTWARE;
|
||||
indio_dev->info = &bmc150_accel_info_fifo;
|
||||
indio_dev->buffer->attrs = bmc150_accel_fifo_attributes;
|
||||
iio_buffer_set_attrs(indio_dev->buffer,
|
||||
bmc150_accel_fifo_attributes);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -42,11 +42,13 @@ struct accel_3d_state {
|
|||
struct hid_sensor_hub_callbacks callbacks;
|
||||
struct hid_sensor_common common_attributes;
|
||||
struct hid_sensor_hub_attribute_info accel[ACCEL_3D_CHANNEL_MAX];
|
||||
u32 accel_val[ACCEL_3D_CHANNEL_MAX];
|
||||
/* Reserve for 3 channels + padding + timestamp */
|
||||
u32 accel_val[ACCEL_3D_CHANNEL_MAX + 3];
|
||||
int scale_pre_decml;
|
||||
int scale_post_decml;
|
||||
int scale_precision;
|
||||
int value_offset;
|
||||
int64_t timestamp;
|
||||
};
|
||||
|
||||
static const u32 accel_3d_addresses[ACCEL_3D_CHANNEL_MAX] = {
|
||||
|
@ -87,6 +89,42 @@ static const struct iio_chan_spec accel_3d_channels[] = {
|
|||
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
|
||||
BIT(IIO_CHAN_INFO_HYSTERESIS),
|
||||
.scan_index = CHANNEL_SCAN_INDEX_Z,
|
||||
},
|
||||
IIO_CHAN_SOFT_TIMESTAMP(3)
|
||||
};
|
||||
|
||||
/* Channel definitions */
|
||||
static const struct iio_chan_spec gravity_channels[] = {
|
||||
{
|
||||
.type = IIO_GRAVITY,
|
||||
.modified = 1,
|
||||
.channel2 = IIO_MOD_X,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
|
||||
BIT(IIO_CHAN_INFO_HYSTERESIS),
|
||||
.scan_index = CHANNEL_SCAN_INDEX_X,
|
||||
}, {
|
||||
.type = IIO_GRAVITY,
|
||||
.modified = 1,
|
||||
.channel2 = IIO_MOD_Y,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
|
||||
BIT(IIO_CHAN_INFO_HYSTERESIS),
|
||||
.scan_index = CHANNEL_SCAN_INDEX_Y,
|
||||
}, {
|
||||
.type = IIO_GRAVITY,
|
||||
.modified = 1,
|
||||
.channel2 = IIO_MOD_Z,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
|
||||
BIT(IIO_CHAN_INFO_HYSTERESIS),
|
||||
.scan_index = CHANNEL_SCAN_INDEX_Z,
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -111,6 +149,8 @@ static int accel_3d_read_raw(struct iio_dev *indio_dev,
|
|||
int report_id = -1;
|
||||
u32 address;
|
||||
int ret_type;
|
||||
struct hid_sensor_hub_device *hsdev =
|
||||
accel_state->common_attributes.hsdev;
|
||||
|
||||
*val = 0;
|
||||
*val2 = 0;
|
||||
|
@ -122,8 +162,7 @@ static int accel_3d_read_raw(struct iio_dev *indio_dev,
|
|||
if (report_id >= 0)
|
||||
*val = sensor_hub_input_attr_get_raw_value(
|
||||
accel_state->common_attributes.hsdev,
|
||||
HID_USAGE_SENSOR_ACCEL_3D, address,
|
||||
report_id,
|
||||
hsdev->usage, address, report_id,
|
||||
SENSOR_HUB_SYNC);
|
||||
else {
|
||||
*val = 0;
|
||||
|
@ -192,11 +231,11 @@ static const struct iio_info accel_3d_info = {
|
|||
};
|
||||
|
||||
/* Function to push data to buffer */
|
||||
static void hid_sensor_push_data(struct iio_dev *indio_dev, const void *data,
|
||||
int len)
|
||||
static void hid_sensor_push_data(struct iio_dev *indio_dev, void *data,
|
||||
int len, int64_t timestamp)
|
||||
{
|
||||
dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n");
|
||||
iio_push_to_buffers(indio_dev, data);
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, data, timestamp);
|
||||
}
|
||||
|
||||
/* Callback handler to send event after all samples are received and captured */
|
||||
|
@ -208,10 +247,17 @@ static int accel_3d_proc_event(struct hid_sensor_hub_device *hsdev,
|
|||
struct accel_3d_state *accel_state = iio_priv(indio_dev);
|
||||
|
||||
dev_dbg(&indio_dev->dev, "accel_3d_proc_event\n");
|
||||
if (atomic_read(&accel_state->common_attributes.data_ready))
|
||||
if (atomic_read(&accel_state->common_attributes.data_ready)) {
|
||||
if (!accel_state->timestamp)
|
||||
accel_state->timestamp = iio_get_time_ns(indio_dev);
|
||||
|
||||
hid_sensor_push_data(indio_dev,
|
||||
accel_state->accel_val,
|
||||
sizeof(accel_state->accel_val));
|
||||
accel_state->accel_val,
|
||||
sizeof(accel_state->accel_val),
|
||||
accel_state->timestamp);
|
||||
|
||||
accel_state->timestamp = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -236,6 +282,12 @@ static int accel_3d_capture_sample(struct hid_sensor_hub_device *hsdev,
|
|||
*(u32 *)raw_data;
|
||||
ret = 0;
|
||||
break;
|
||||
case HID_USAGE_SENSOR_TIME_TIMESTAMP:
|
||||
accel_state->timestamp =
|
||||
hid_sensor_convert_timestamp(
|
||||
&accel_state->common_attributes,
|
||||
*(int64_t *)raw_data);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -272,7 +324,7 @@ static int accel_3d_parse_report(struct platform_device *pdev,
|
|||
st->accel[2].index, st->accel[2].report_id);
|
||||
|
||||
st->scale_precision = hid_sensor_format_scale(
|
||||
HID_USAGE_SENSOR_ACCEL_3D,
|
||||
hsdev->usage,
|
||||
&st->accel[CHANNEL_SCAN_INDEX_X],
|
||||
&st->scale_pre_decml, &st->scale_post_decml);
|
||||
|
||||
|
@ -295,9 +347,12 @@ static int accel_3d_parse_report(struct platform_device *pdev,
|
|||
static int hid_accel_3d_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret = 0;
|
||||
static const char *name = "accel_3d";
|
||||
static const char *name;
|
||||
struct iio_dev *indio_dev;
|
||||
struct accel_3d_state *accel_state;
|
||||
const struct iio_chan_spec *channel_spec;
|
||||
int channel_size;
|
||||
|
||||
struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev,
|
||||
|
@ -311,24 +366,30 @@ static int hid_accel_3d_probe(struct platform_device *pdev)
|
|||
accel_state->common_attributes.hsdev = hsdev;
|
||||
accel_state->common_attributes.pdev = pdev;
|
||||
|
||||
ret = hid_sensor_parse_common_attributes(hsdev,
|
||||
HID_USAGE_SENSOR_ACCEL_3D,
|
||||
if (hsdev->usage == HID_USAGE_SENSOR_ACCEL_3D) {
|
||||
name = "accel_3d";
|
||||
channel_spec = accel_3d_channels;
|
||||
channel_size = sizeof(accel_3d_channels);
|
||||
} else {
|
||||
name = "gravity";
|
||||
channel_spec = gravity_channels;
|
||||
channel_size = sizeof(gravity_channels);
|
||||
}
|
||||
ret = hid_sensor_parse_common_attributes(hsdev, hsdev->usage,
|
||||
&accel_state->common_attributes);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to setup common attributes\n");
|
||||
return ret;
|
||||
}
|
||||
indio_dev->channels = kmemdup(channel_spec, channel_size, GFP_KERNEL);
|
||||
|
||||
indio_dev->channels = kmemdup(accel_3d_channels,
|
||||
sizeof(accel_3d_channels), GFP_KERNEL);
|
||||
if (!indio_dev->channels) {
|
||||
dev_err(&pdev->dev, "failed to duplicate channels\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = accel_3d_parse_report(pdev, hsdev,
|
||||
(struct iio_chan_spec *)indio_dev->channels,
|
||||
HID_USAGE_SENSOR_ACCEL_3D, accel_state);
|
||||
(struct iio_chan_spec *)indio_dev->channels,
|
||||
hsdev->usage, accel_state);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to setup attributes\n");
|
||||
goto error_free_dev_mem;
|
||||
|
@ -363,7 +424,7 @@ static int hid_accel_3d_probe(struct platform_device *pdev)
|
|||
accel_state->callbacks.send_event = accel_3d_proc_event;
|
||||
accel_state->callbacks.capture_sample = accel_3d_capture_sample;
|
||||
accel_state->callbacks.pdev = pdev;
|
||||
ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_ACCEL_3D,
|
||||
ret = sensor_hub_register_callback(hsdev, hsdev->usage,
|
||||
&accel_state->callbacks);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "callback reg failed\n");
|
||||
|
@ -390,7 +451,7 @@ static int hid_accel_3d_remove(struct platform_device *pdev)
|
|||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
struct accel_3d_state *accel_state = iio_priv(indio_dev);
|
||||
|
||||
sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_ACCEL_3D);
|
||||
sensor_hub_remove_callback(hsdev, hsdev->usage);
|
||||
iio_device_unregister(indio_dev);
|
||||
hid_sensor_remove_trigger(&accel_state->common_attributes);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
|
@ -404,6 +465,9 @@ static const struct platform_device_id hid_accel_3d_ids[] = {
|
|||
/* Format: HID-SENSOR-usage_id_in_hex_lowercase */
|
||||
.name = "HID-SENSOR-200073",
|
||||
},
|
||||
{ /* gravity sensor */
|
||||
.name = "HID-SENSOR-20007b",
|
||||
},
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, hid_accel_3d_ids);
|
||||
|
|
|
@ -248,7 +248,7 @@ static int mma8452_get_int_plus_micros_index(const int (*vals)[2], int n,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int mma8452_get_odr_index(struct mma8452_data *data)
|
||||
static unsigned int mma8452_get_odr_index(struct mma8452_data *data)
|
||||
{
|
||||
return (data->ctrl_reg1 & MMA8452_CTRL_DR_MASK) >>
|
||||
MMA8452_CTRL_DR_SHIFT;
|
||||
|
@ -260,7 +260,7 @@ static const int mma8452_samp_freq[8][2] = {
|
|||
};
|
||||
|
||||
/* Datasheet table: step time "Relationship with the ODR" (sample frequency) */
|
||||
static const int mma8452_transient_time_step_us[4][8] = {
|
||||
static const unsigned int mma8452_transient_time_step_us[4][8] = {
|
||||
{ 1250, 2500, 5000, 10000, 20000, 20000, 20000, 20000 }, /* normal */
|
||||
{ 1250, 2500, 5000, 10000, 20000, 80000, 80000, 80000 }, /* l p l n */
|
||||
{ 1250, 2500, 2500, 2500, 2500, 2500, 2500, 2500 }, /* high res*/
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
#include <linux/iio/common/ssp_sensors.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/kfifo_buf.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
@ -135,7 +136,7 @@ static int ssp_accel_probe(struct platform_device *pdev)
|
|||
|
||||
platform_set_drvdata(pdev, indio_dev);
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
ret = devm_iio_device_register(&pdev->dev, indio_dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
@ -145,21 +146,11 @@ static int ssp_accel_probe(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int ssp_accel_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver ssp_accel_driver = {
|
||||
.driver = {
|
||||
.name = SSP_ACCEL_NAME,
|
||||
},
|
||||
.probe = ssp_accel_probe,
|
||||
.remove = ssp_accel_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(ssp_accel_driver);
|
||||
|
|
|
@ -14,6 +14,24 @@
|
|||
#include <linux/types.h>
|
||||
#include <linux/iio/common/st_sensors.h>
|
||||
|
||||
enum st_accel_type {
|
||||
LSM303DLH,
|
||||
LSM303DLHC,
|
||||
LIS3DH,
|
||||
LSM330D,
|
||||
LSM330DL,
|
||||
LSM330DLC,
|
||||
LIS331DLH,
|
||||
LSM303DL,
|
||||
LSM303DLM,
|
||||
LSM330,
|
||||
LSM303AGR,
|
||||
LIS2DH12,
|
||||
LIS3L02DQ,
|
||||
LNG2DM,
|
||||
ST_ACCEL_MAX,
|
||||
};
|
||||
|
||||
#define H3LIS331DL_DRIVER_NAME "h3lis331dl_accel"
|
||||
#define LIS3LV02DL_ACCEL_DEV_NAME "lis3lv02dl_accel"
|
||||
#define LSM303DLHC_ACCEL_DEV_NAME "lsm303dlhc_accel"
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/iio/iio.h>
|
||||
|
||||
|
@ -20,6 +21,11 @@
|
|||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id st_accel_of_match[] = {
|
||||
{
|
||||
/* An older compatible */
|
||||
.compatible = "st,lis3lv02d",
|
||||
.data = LIS3LV02DL_ACCEL_DEV_NAME,
|
||||
},
|
||||
{
|
||||
.compatible = "st,lis3lv02dl-accel",
|
||||
.data = LIS3LV02DL_ACCEL_DEV_NAME,
|
||||
|
@ -95,25 +101,67 @@ MODULE_DEVICE_TABLE(of, st_accel_of_match);
|
|||
#define st_accel_of_match NULL
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
static const struct acpi_device_id st_accel_acpi_match[] = {
|
||||
{"SMO8A90", LNG2DM},
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, st_accel_acpi_match);
|
||||
#else
|
||||
#define st_accel_acpi_match NULL
|
||||
#endif
|
||||
|
||||
static const struct i2c_device_id st_accel_id_table[] = {
|
||||
{ LSM303DLH_ACCEL_DEV_NAME, LSM303DLH },
|
||||
{ LSM303DLHC_ACCEL_DEV_NAME, LSM303DLHC },
|
||||
{ LIS3DH_ACCEL_DEV_NAME, LIS3DH },
|
||||
{ LSM330D_ACCEL_DEV_NAME, LSM330D },
|
||||
{ LSM330DL_ACCEL_DEV_NAME, LSM330DL },
|
||||
{ LSM330DLC_ACCEL_DEV_NAME, LSM330DLC },
|
||||
{ LIS331DLH_ACCEL_DEV_NAME, LIS331DLH },
|
||||
{ LSM303DL_ACCEL_DEV_NAME, LSM303DL },
|
||||
{ LSM303DLM_ACCEL_DEV_NAME, LSM303DLM },
|
||||
{ LSM330_ACCEL_DEV_NAME, LSM330 },
|
||||
{ LSM303AGR_ACCEL_DEV_NAME, LSM303AGR },
|
||||
{ LIS2DH12_ACCEL_DEV_NAME, LIS2DH12 },
|
||||
{ LIS3L02DQ_ACCEL_DEV_NAME, LIS3L02DQ },
|
||||
{ LNG2DM_ACCEL_DEV_NAME, LNG2DM },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, st_accel_id_table);
|
||||
|
||||
static int st_accel_i2c_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct st_sensor_data *adata;
|
||||
int err;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*adata));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
adata = iio_priv(indio_dev);
|
||||
st_sensors_of_i2c_probe(client, st_accel_of_match);
|
||||
|
||||
if (client->dev.of_node) {
|
||||
st_sensors_of_i2c_probe(client, st_accel_of_match);
|
||||
} else if (ACPI_HANDLE(&client->dev)) {
|
||||
ret = st_sensors_match_acpi_device(&client->dev);
|
||||
if ((ret < 0) || (ret >= ST_ACCEL_MAX))
|
||||
return -ENODEV;
|
||||
|
||||
strncpy(client->name, st_accel_id_table[ret].name,
|
||||
sizeof(client->name));
|
||||
client->name[sizeof(client->name) - 1] = '\0';
|
||||
} else if (!id)
|
||||
return -ENODEV;
|
||||
|
||||
|
||||
st_sensors_i2c_configure(indio_dev, client, adata);
|
||||
|
||||
err = st_accel_common_probe(indio_dev);
|
||||
if (err < 0)
|
||||
return err;
|
||||
ret = st_accel_common_probe(indio_dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -125,29 +173,11 @@ static int st_accel_i2c_remove(struct i2c_client *client)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id st_accel_id_table[] = {
|
||||
{ LSM303DLH_ACCEL_DEV_NAME },
|
||||
{ LSM303DLHC_ACCEL_DEV_NAME },
|
||||
{ LIS3DH_ACCEL_DEV_NAME },
|
||||
{ LSM330D_ACCEL_DEV_NAME },
|
||||
{ LSM330DL_ACCEL_DEV_NAME },
|
||||
{ LSM330DLC_ACCEL_DEV_NAME },
|
||||
{ LIS331DLH_ACCEL_DEV_NAME },
|
||||
{ LSM303DL_ACCEL_DEV_NAME },
|
||||
{ LSM303DLM_ACCEL_DEV_NAME },
|
||||
{ LSM330_ACCEL_DEV_NAME },
|
||||
{ LSM303AGR_ACCEL_DEV_NAME },
|
||||
{ LIS2DH12_ACCEL_DEV_NAME },
|
||||
{ LIS3L02DQ_ACCEL_DEV_NAME },
|
||||
{ LNG2DM_ACCEL_DEV_NAME },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, st_accel_id_table);
|
||||
|
||||
static struct i2c_driver st_accel_driver = {
|
||||
.driver = {
|
||||
.name = "st-accel-i2c",
|
||||
.of_match_table = of_match_ptr(st_accel_of_match),
|
||||
.acpi_match_table = ACPI_PTR(st_accel_acpi_match),
|
||||
},
|
||||
.probe = st_accel_i2c_probe,
|
||||
.remove = st_accel_i2c_remove,
|
||||
|
|
|
@ -65,9 +65,18 @@ static const struct spi_device_id st_accel_id_table[] = {
|
|||
};
|
||||
MODULE_DEVICE_TABLE(spi, st_accel_id_table);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id lis302dl_spi_dt_ids[] = {
|
||||
{ .compatible = "st,lis302dl-spi" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, lis302dl_spi_dt_ids);
|
||||
#endif
|
||||
|
||||
static struct spi_driver st_accel_driver = {
|
||||
.driver = {
|
||||
.name = "st-accel-spi",
|
||||
.of_match_table = of_match_ptr(lis302dl_spi_dt_ids),
|
||||
},
|
||||
.probe = st_accel_spi_probe,
|
||||
.remove = st_accel_spi_remove,
|
||||
|
|
|
@ -247,6 +247,25 @@ config HI8435
|
|||
This driver can also be built as a module. If so, the module will be
|
||||
called hi8435.
|
||||
|
||||
config HX711
|
||||
tristate "AVIA HX711 ADC for weight cells"
|
||||
depends on GPIOLIB
|
||||
help
|
||||
If you say yes here you get support for AVIA HX711 ADC which is used
|
||||
for weigh cells
|
||||
|
||||
This driver uses two GPIOs, one acts as the clock and controls the
|
||||
channel selection and gain, the other one is used for the measurement
|
||||
data
|
||||
|
||||
Currently the raw value is read from the chip and delivered.
|
||||
To get an actual weight one needs to subtract the
|
||||
zero offset and multiply by a scale factor.
|
||||
This should be done in userspace.
|
||||
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called hx711.
|
||||
|
||||
config INA2XX_ADC
|
||||
tristate "Texas Instruments INA2xx Power Monitors IIO driver"
|
||||
depends on I2C && !SENSORS_INA2XX
|
||||
|
@ -307,6 +326,15 @@ config MAX1027
|
|||
To compile this driver as a module, choose M here: the module will be
|
||||
called max1027.
|
||||
|
||||
config MAX11100
|
||||
tristate "Maxim max11100 ADC driver"
|
||||
depends on SPI_MASTER
|
||||
help
|
||||
Say yes here to build support for Maxim max11100 SPI ADC
|
||||
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called max11100.
|
||||
|
||||
config MAX1363
|
||||
tristate "Maxim max1363 ADC driver"
|
||||
depends on I2C
|
||||
|
@ -371,6 +399,18 @@ config MEN_Z188_ADC
|
|||
This driver can also be built as a module. If so, the module will be
|
||||
called men_z188_adc.
|
||||
|
||||
config MESON_SARADC
|
||||
tristate "Amlogic Meson SAR ADC driver"
|
||||
default ARCH_MESON
|
||||
depends on OF && COMMON_CLK && (ARCH_MESON || COMPILE_TEST)
|
||||
select REGMAP_MMIO
|
||||
help
|
||||
Say yes here to build support for the SAR ADC found in Amlogic Meson
|
||||
SoCs.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called meson_saradc.
|
||||
|
||||
config MXS_LRADC
|
||||
tristate "Freescale i.MX23/i.MX28 LRADC"
|
||||
depends on (ARCH_MXS || COMPILE_TEST) && HAS_IOMEM
|
||||
|
@ -430,6 +470,19 @@ config QCOM_SPMI_VADC
|
|||
To compile this driver as a module, choose M here: the module will
|
||||
be called qcom-spmi-vadc.
|
||||
|
||||
config RCAR_GYRO_ADC
|
||||
tristate "Renesas R-Car GyroADC driver"
|
||||
depends on ARCH_RCAR_GEN2 || (ARM && COMPILE_TEST)
|
||||
help
|
||||
Say yes here to build support for the GyroADC found in Renesas
|
||||
R-Car Gen2 SoCs. This block is a simple SPI offload engine for
|
||||
reading data out of attached compatible ADCs in a round-robin
|
||||
fashion. Up to 4 or 8 ADC channels are supported by this block,
|
||||
depending on which ADCs are attached.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called rcar-gyroadc.
|
||||
|
||||
config ROCKCHIP_SARADC
|
||||
tristate "Rockchip SARADC driver"
|
||||
depends on ARCH_ROCKCHIP || (ARM && COMPILE_TEST)
|
||||
|
@ -444,8 +497,13 @@ config ROCKCHIP_SARADC
|
|||
config STM32_ADC_CORE
|
||||
tristate "STMicroelectronics STM32 adc core"
|
||||
depends on ARCH_STM32 || COMPILE_TEST
|
||||
depends on HAS_DMA
|
||||
depends on OF
|
||||
depends on REGULATOR
|
||||
select IIO_BUFFER
|
||||
select MFD_STM32_TIMERS
|
||||
select IIO_STM32_TIMER_TRIGGER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
Select this option to enable the core driver for STMicroelectronics
|
||||
STM32 analog-to-digital converter (ADC).
|
||||
|
@ -549,6 +607,19 @@ config TI_ADS1015
|
|||
This driver can also be built as a module. If so, the module will be
|
||||
called ti-ads1015.
|
||||
|
||||
config TI_ADS7950
|
||||
tristate "Texas Instruments ADS7950 ADC driver"
|
||||
depends on SPI
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
Say yes here to build support for Texas Instruments ADS7950, ADS7951,
|
||||
ADS7952, ADS7953, ADS7954, ADS7955, ADS7956, ADS7957, ADS7958, ADS7959.
|
||||
ADS7960, ADS7961.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ti-ads7950.
|
||||
|
||||
config TI_ADS8688
|
||||
tristate "Texas Instruments ADS8688"
|
||||
depends on SPI && OF
|
||||
|
@ -571,6 +642,18 @@ config TI_AM335X_ADC
|
|||
To compile this driver as a module, choose M here: the module will be
|
||||
called ti_am335x_adc.
|
||||
|
||||
config TI_TLC4541
|
||||
tristate "Texas Instruments TLC4541 ADC driver"
|
||||
depends on SPI
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
Say yes here to build support for Texas Instruments TLC4541 / TLC3541
|
||||
ADC chips.
|
||||
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called ti-tlc4541.
|
||||
|
||||
config TWL4030_MADC
|
||||
tristate "TWL4030 MADC (Monitoring A/D Converter)"
|
||||
depends on TWL4030_CORE
|
||||
|
|
|
@ -25,22 +25,26 @@ 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
|
||||
obj-$(CONFIG_HX711) += hx711.o
|
||||
obj-$(CONFIG_IMX7D_ADC) += imx7d_adc.o
|
||||
obj-$(CONFIG_INA2XX_ADC) += ina2xx-adc.o
|
||||
obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
|
||||
obj-$(CONFIG_LPC18XX_ADC) += lpc18xx_adc.o
|
||||
obj-$(CONFIG_LTC2485) += ltc2485.o
|
||||
obj-$(CONFIG_MAX1027) += max1027.o
|
||||
obj-$(CONFIG_MAX11100) += max11100.o
|
||||
obj-$(CONFIG_MAX1363) += max1363.o
|
||||
obj-$(CONFIG_MCP320X) += mcp320x.o
|
||||
obj-$(CONFIG_MCP3422) += mcp3422.o
|
||||
obj-$(CONFIG_MEDIATEK_MT6577_AUXADC) += mt6577_auxadc.o
|
||||
obj-$(CONFIG_MEN_Z188_ADC) += men_z188_adc.o
|
||||
obj-$(CONFIG_MESON_SARADC) += meson_saradc.o
|
||||
obj-$(CONFIG_MXS_LRADC) += mxs-lradc.o
|
||||
obj-$(CONFIG_NAU7802) += nau7802.o
|
||||
obj-$(CONFIG_PALMAS_GPADC) += palmas_gpadc.o
|
||||
obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o
|
||||
obj-$(CONFIG_QCOM_SPMI_VADC) += qcom-spmi-vadc.o
|
||||
obj-$(CONFIG_RCAR_GYRO_ADC) += rcar-gyroadc.o
|
||||
obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
|
||||
obj-$(CONFIG_STX104) += stx104.o
|
||||
obj-$(CONFIG_STM32_ADC_CORE) += stm32-adc-core.o
|
||||
|
@ -51,8 +55,10 @@ obj-$(CONFIG_TI_ADC12138) += ti-adc12138.o
|
|||
obj-$(CONFIG_TI_ADC128S052) += ti-adc128s052.o
|
||||
obj-$(CONFIG_TI_ADC161S626) += ti-adc161s626.o
|
||||
obj-$(CONFIG_TI_ADS1015) += ti-ads1015.o
|
||||
obj-$(CONFIG_TI_ADS7950) += ti-ads7950.o
|
||||
obj-$(CONFIG_TI_ADS8688) += ti-ads8688.o
|
||||
obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o
|
||||
obj-$(CONFIG_TI_TLC4541) += ti-tlc4541.o
|
||||
obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o
|
||||
obj-$(CONFIG_TWL6030_GPADC) += twl6030-gpadc.o
|
||||
obj-$(CONFIG_VF610_ADC) += vf610_adc.o
|
||||
|
|
|
@ -28,8 +28,6 @@
|
|||
#include <linux/iio/driver.h>
|
||||
|
||||
#define AXP288_ADC_EN_MASK 0xF1
|
||||
#define AXP288_ADC_TS_PIN_GPADC 0xF2
|
||||
#define AXP288_ADC_TS_PIN_ON 0xF3
|
||||
|
||||
enum axp288_adc_id {
|
||||
AXP288_ADC_TS,
|
||||
|
@ -123,16 +121,6 @@ static int axp288_adc_read_channel(int *val, unsigned long address,
|
|||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
static int axp288_adc_set_ts(struct regmap *regmap, unsigned int mode,
|
||||
unsigned long address)
|
||||
{
|
||||
/* channels other than GPADC do not need to switch TS pin */
|
||||
if (address != AXP288_GP_ADC_H)
|
||||
return 0;
|
||||
|
||||
return regmap_write(regmap, AXP288_ADC_TS_PIN_CTRL, mode);
|
||||
}
|
||||
|
||||
static int axp288_adc_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
|
@ -143,16 +131,7 @@ static int axp288_adc_read_raw(struct iio_dev *indio_dev,
|
|||
mutex_lock(&indio_dev->mlock);
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (axp288_adc_set_ts(info->regmap, AXP288_ADC_TS_PIN_GPADC,
|
||||
chan->address)) {
|
||||
dev_err(&indio_dev->dev, "GPADC mode\n");
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
ret = axp288_adc_read_channel(val, chan->address, info->regmap);
|
||||
if (axp288_adc_set_ts(info->regmap, AXP288_ADC_TS_PIN_ON,
|
||||
chan->address))
|
||||
dev_err(&indio_dev->dev, "TS pin restore\n");
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
|
@ -162,15 +141,6 @@ static int axp288_adc_read_raw(struct iio_dev *indio_dev,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int axp288_adc_set_state(struct regmap *regmap)
|
||||
{
|
||||
/* ADC should be always enabled for internal FG to function */
|
||||
if (regmap_write(regmap, AXP288_ADC_TS_PIN_CTRL, AXP288_ADC_TS_PIN_ON))
|
||||
return -EIO;
|
||||
|
||||
return regmap_write(regmap, AXP20X_ADC_EN1, AXP288_ADC_EN_MASK);
|
||||
}
|
||||
|
||||
static const struct iio_info axp288_adc_iio_info = {
|
||||
.read_raw = &axp288_adc_read_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
|
@ -199,7 +169,7 @@ static int axp288_adc_probe(struct platform_device *pdev)
|
|||
* Set ADC to enabled state at all time, including system suspend.
|
||||
* otherwise internal fuel gauge functionality may be affected.
|
||||
*/
|
||||
ret = axp288_adc_set_state(axp20x->regmap);
|
||||
ret = regmap_write(info->regmap, AXP20X_ADC_EN1, AXP288_ADC_EN_MASK);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "unable to enable ADC device\n");
|
||||
return ret;
|
||||
|
|
|
@ -632,7 +632,7 @@ static irqreturn_t exynos_ts_isr(int irq, void *dev_id)
|
|||
input_report_key(info->input, BTN_TOUCH, 1);
|
||||
input_sync(info->input);
|
||||
|
||||
msleep(1);
|
||||
usleep_range(1000, 1100);
|
||||
};
|
||||
|
||||
writel(0, ADC_V1_CLRINTPNDNUP(info->regs));
|
||||
|
|
|
@ -401,6 +401,7 @@ static const struct of_device_id mx25_gcq_ids[] = {
|
|||
{ .compatible = "fsl,imx25-gcq", },
|
||||
{ /* Sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mx25_gcq_ids);
|
||||
|
||||
static struct platform_driver mx25_gcq_driver = {
|
||||
.driver = {
|
||||
|
|
|
@ -0,0 +1,532 @@
|
|||
/*
|
||||
* HX711: analog to digital converter for weight sensor module
|
||||
*
|
||||
* Copyright (c) 2016 Andreas Klinger <ak@it-klinger.de>
|
||||
*
|
||||
* 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/err.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
/* gain to pulse and scale conversion */
|
||||
#define HX711_GAIN_MAX 3
|
||||
|
||||
struct hx711_gain_to_scale {
|
||||
int gain;
|
||||
int gain_pulse;
|
||||
int scale;
|
||||
int channel;
|
||||
};
|
||||
|
||||
/*
|
||||
* .scale depends on AVDD which in turn is known as soon as the regulator
|
||||
* is available
|
||||
* therefore we set .scale in hx711_probe()
|
||||
*
|
||||
* channel A in documentation is channel 0 in source code
|
||||
* channel B in documentation is channel 1 in source code
|
||||
*/
|
||||
static struct hx711_gain_to_scale hx711_gain_to_scale[HX711_GAIN_MAX] = {
|
||||
{ 128, 1, 0, 0 },
|
||||
{ 32, 2, 0, 1 },
|
||||
{ 64, 3, 0, 0 }
|
||||
};
|
||||
|
||||
static int hx711_get_gain_to_pulse(int gain)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < HX711_GAIN_MAX; i++)
|
||||
if (hx711_gain_to_scale[i].gain == gain)
|
||||
return hx711_gain_to_scale[i].gain_pulse;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int hx711_get_gain_to_scale(int gain)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < HX711_GAIN_MAX; i++)
|
||||
if (hx711_gain_to_scale[i].gain == gain)
|
||||
return hx711_gain_to_scale[i].scale;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hx711_get_scale_to_gain(int scale)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < HX711_GAIN_MAX; i++)
|
||||
if (hx711_gain_to_scale[i].scale == scale)
|
||||
return hx711_gain_to_scale[i].gain;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
struct hx711_data {
|
||||
struct device *dev;
|
||||
struct gpio_desc *gpiod_pd_sck;
|
||||
struct gpio_desc *gpiod_dout;
|
||||
struct regulator *reg_avdd;
|
||||
int gain_set; /* gain set on device */
|
||||
int gain_chan_a; /* gain for channel A */
|
||||
struct mutex lock;
|
||||
};
|
||||
|
||||
static int hx711_cycle(struct hx711_data *hx711_data)
|
||||
{
|
||||
int val;
|
||||
|
||||
/*
|
||||
* if preempted for more then 60us while PD_SCK is high:
|
||||
* hx711 is going in reset
|
||||
* ==> measuring is false
|
||||
*/
|
||||
preempt_disable();
|
||||
gpiod_set_value(hx711_data->gpiod_pd_sck, 1);
|
||||
val = gpiod_get_value(hx711_data->gpiod_dout);
|
||||
/*
|
||||
* here we are not waiting for 0.2 us as suggested by the datasheet,
|
||||
* because the oscilloscope showed in a test scenario
|
||||
* at least 1.15 us for PD_SCK high (T3 in datasheet)
|
||||
* and 0.56 us for PD_SCK low on TI Sitara with 800 MHz
|
||||
*/
|
||||
gpiod_set_value(hx711_data->gpiod_pd_sck, 0);
|
||||
preempt_enable();
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static int hx711_read(struct hx711_data *hx711_data)
|
||||
{
|
||||
int i, ret;
|
||||
int value = 0;
|
||||
int val = gpiod_get_value(hx711_data->gpiod_dout);
|
||||
|
||||
/* we double check if it's really down */
|
||||
if (val)
|
||||
return -EIO;
|
||||
|
||||
for (i = 0; i < 24; i++) {
|
||||
value <<= 1;
|
||||
ret = hx711_cycle(hx711_data);
|
||||
if (ret)
|
||||
value++;
|
||||
}
|
||||
|
||||
value ^= 0x800000;
|
||||
|
||||
for (i = 0; i < hx711_get_gain_to_pulse(hx711_data->gain_set); i++)
|
||||
hx711_cycle(hx711_data);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static int hx711_wait_for_ready(struct hx711_data *hx711_data)
|
||||
{
|
||||
int i, val;
|
||||
|
||||
/*
|
||||
* a maximum reset cycle time of 56 ms was measured.
|
||||
* we round it up to 100 ms
|
||||
*/
|
||||
for (i = 0; i < 100; i++) {
|
||||
val = gpiod_get_value(hx711_data->gpiod_dout);
|
||||
if (!val)
|
||||
break;
|
||||
/* sleep at least 1 ms */
|
||||
msleep(1);
|
||||
}
|
||||
if (val)
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hx711_reset(struct hx711_data *hx711_data)
|
||||
{
|
||||
int ret;
|
||||
int val = gpiod_get_value(hx711_data->gpiod_dout);
|
||||
|
||||
if (val) {
|
||||
/*
|
||||
* an examination with the oszilloscope indicated
|
||||
* that the first value read after the reset is not stable
|
||||
* if we reset too short;
|
||||
* the shorter the reset cycle
|
||||
* the less reliable the first value after reset is;
|
||||
* there were no problems encountered with a value
|
||||
* of 10 ms or higher
|
||||
*/
|
||||
gpiod_set_value(hx711_data->gpiod_pd_sck, 1);
|
||||
msleep(10);
|
||||
gpiod_set_value(hx711_data->gpiod_pd_sck, 0);
|
||||
|
||||
ret = hx711_wait_for_ready(hx711_data);
|
||||
if (ret)
|
||||
return ret;
|
||||
/*
|
||||
* after a reset the gain is 128 so we do a dummy read
|
||||
* to set the gain for the next read
|
||||
*/
|
||||
ret = hx711_read(hx711_data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* after a dummy read we need to wait vor readiness
|
||||
* for not mixing gain pulses with the clock
|
||||
*/
|
||||
ret = hx711_wait_for_ready(hx711_data);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static int hx711_set_gain_for_channel(struct hx711_data *hx711_data, int chan)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (chan == 0) {
|
||||
if (hx711_data->gain_set == 32) {
|
||||
hx711_data->gain_set = hx711_data->gain_chan_a;
|
||||
|
||||
ret = hx711_read(hx711_data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = hx711_wait_for_ready(hx711_data);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
if (hx711_data->gain_set != 32) {
|
||||
hx711_data->gain_set = 32;
|
||||
|
||||
ret = hx711_read(hx711_data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = hx711_wait_for_ready(hx711_data);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hx711_read_raw(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct hx711_data *hx711_data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
mutex_lock(&hx711_data->lock);
|
||||
|
||||
/*
|
||||
* hx711_reset() must be called from here
|
||||
* because it could be calling hx711_read() by itself
|
||||
*/
|
||||
if (hx711_reset(hx711_data)) {
|
||||
mutex_unlock(&hx711_data->lock);
|
||||
dev_err(hx711_data->dev, "reset failed!");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
ret = hx711_set_gain_for_channel(hx711_data, chan->channel);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&hx711_data->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*val = hx711_read(hx711_data);
|
||||
|
||||
mutex_unlock(&hx711_data->lock);
|
||||
|
||||
if (*val < 0)
|
||||
return *val;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = 0;
|
||||
mutex_lock(&hx711_data->lock);
|
||||
|
||||
*val2 = hx711_get_gain_to_scale(hx711_data->gain_set);
|
||||
|
||||
mutex_unlock(&hx711_data->lock);
|
||||
|
||||
return IIO_VAL_INT_PLUS_NANO;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int hx711_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val,
|
||||
int val2,
|
||||
long mask)
|
||||
{
|
||||
struct hx711_data *hx711_data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
int gain;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
/*
|
||||
* a scale greater than 1 mV per LSB is not possible
|
||||
* with the HX711, therefore val must be 0
|
||||
*/
|
||||
if (val != 0)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&hx711_data->lock);
|
||||
|
||||
gain = hx711_get_scale_to_gain(val2);
|
||||
if (gain < 0) {
|
||||
mutex_unlock(&hx711_data->lock);
|
||||
return gain;
|
||||
}
|
||||
|
||||
if (gain != hx711_data->gain_set) {
|
||||
hx711_data->gain_set = gain;
|
||||
if (gain != 32)
|
||||
hx711_data->gain_chan_a = gain;
|
||||
|
||||
ret = hx711_read(hx711_data);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&hx711_data->lock);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&hx711_data->lock);
|
||||
return 0;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hx711_write_raw_get_fmt(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
long mask)
|
||||
{
|
||||
return IIO_VAL_INT_PLUS_NANO;
|
||||
}
|
||||
|
||||
static ssize_t hx711_scale_available_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct iio_dev_attr *iio_attr = to_iio_dev_attr(attr);
|
||||
int channel = iio_attr->address;
|
||||
int i, len = 0;
|
||||
|
||||
for (i = 0; i < HX711_GAIN_MAX; i++)
|
||||
if (hx711_gain_to_scale[i].channel == channel)
|
||||
len += sprintf(buf + len, "0.%09d ",
|
||||
hx711_gain_to_scale[i].scale);
|
||||
|
||||
len += sprintf(buf + len, "\n");
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static IIO_DEVICE_ATTR(in_voltage0_scale_available, S_IRUGO,
|
||||
hx711_scale_available_show, NULL, 0);
|
||||
|
||||
static IIO_DEVICE_ATTR(in_voltage1_scale_available, S_IRUGO,
|
||||
hx711_scale_available_show, NULL, 1);
|
||||
|
||||
static struct attribute *hx711_attributes[] = {
|
||||
&iio_dev_attr_in_voltage0_scale_available.dev_attr.attr,
|
||||
&iio_dev_attr_in_voltage1_scale_available.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group hx711_attribute_group = {
|
||||
.attrs = hx711_attributes,
|
||||
};
|
||||
|
||||
static const struct iio_info hx711_iio_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = hx711_read_raw,
|
||||
.write_raw = hx711_write_raw,
|
||||
.write_raw_get_fmt = hx711_write_raw_get_fmt,
|
||||
.attrs = &hx711_attribute_group,
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec hx711_chan_spec[] = {
|
||||
{
|
||||
.type = IIO_VOLTAGE,
|
||||
.channel = 0,
|
||||
.indexed = 1,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
|
||||
},
|
||||
{
|
||||
.type = IIO_VOLTAGE,
|
||||
.channel = 1,
|
||||
.indexed = 1,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
|
||||
},
|
||||
};
|
||||
|
||||
static int hx711_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct hx711_data *hx711_data;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(dev, sizeof(struct hx711_data));
|
||||
if (!indio_dev) {
|
||||
dev_err(dev, "failed to allocate IIO device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
hx711_data = iio_priv(indio_dev);
|
||||
hx711_data->dev = dev;
|
||||
|
||||
mutex_init(&hx711_data->lock);
|
||||
|
||||
/*
|
||||
* PD_SCK stands for power down and serial clock input of HX711
|
||||
* in the driver it is an output
|
||||
*/
|
||||
hx711_data->gpiod_pd_sck = devm_gpiod_get(dev, "sck", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(hx711_data->gpiod_pd_sck)) {
|
||||
dev_err(dev, "failed to get sck-gpiod: err=%ld\n",
|
||||
PTR_ERR(hx711_data->gpiod_pd_sck));
|
||||
return PTR_ERR(hx711_data->gpiod_pd_sck);
|
||||
}
|
||||
|
||||
/*
|
||||
* DOUT stands for serial data output of HX711
|
||||
* for the driver it is an input
|
||||
*/
|
||||
hx711_data->gpiod_dout = devm_gpiod_get(dev, "dout", GPIOD_IN);
|
||||
if (IS_ERR(hx711_data->gpiod_dout)) {
|
||||
dev_err(dev, "failed to get dout-gpiod: err=%ld\n",
|
||||
PTR_ERR(hx711_data->gpiod_dout));
|
||||
return PTR_ERR(hx711_data->gpiod_dout);
|
||||
}
|
||||
|
||||
hx711_data->reg_avdd = devm_regulator_get(dev, "avdd");
|
||||
if (IS_ERR(hx711_data->reg_avdd))
|
||||
return PTR_ERR(hx711_data->reg_avdd);
|
||||
|
||||
ret = regulator_enable(hx711_data->reg_avdd);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* with
|
||||
* full scale differential input range: AVDD / GAIN
|
||||
* full scale output data: 2^24
|
||||
* we can say:
|
||||
* AVDD / GAIN = 2^24
|
||||
* therefore:
|
||||
* 1 LSB = AVDD / GAIN / 2^24
|
||||
* AVDD is in uV, but we need 10^-9 mV
|
||||
* approximately to fit into a 32 bit number:
|
||||
* 1 LSB = (AVDD * 100) / GAIN / 1678 [10^-9 mV]
|
||||
*/
|
||||
ret = regulator_get_voltage(hx711_data->reg_avdd);
|
||||
if (ret < 0) {
|
||||
regulator_disable(hx711_data->reg_avdd);
|
||||
return ret;
|
||||
}
|
||||
/* we need 10^-9 mV */
|
||||
ret *= 100;
|
||||
|
||||
for (i = 0; i < HX711_GAIN_MAX; i++)
|
||||
hx711_gain_to_scale[i].scale =
|
||||
ret / hx711_gain_to_scale[i].gain / 1678;
|
||||
|
||||
hx711_data->gain_set = 128;
|
||||
hx711_data->gain_chan_a = 128;
|
||||
|
||||
platform_set_drvdata(pdev, indio_dev);
|
||||
|
||||
indio_dev->name = "hx711";
|
||||
indio_dev->dev.parent = &pdev->dev;
|
||||
indio_dev->info = &hx711_iio_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = hx711_chan_spec;
|
||||
indio_dev->num_channels = ARRAY_SIZE(hx711_chan_spec);
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Couldn't register the device\n");
|
||||
regulator_disable(hx711_data->reg_avdd);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hx711_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct hx711_data *hx711_data;
|
||||
struct iio_dev *indio_dev;
|
||||
|
||||
indio_dev = platform_get_drvdata(pdev);
|
||||
hx711_data = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
regulator_disable(hx711_data->reg_avdd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id of_hx711_match[] = {
|
||||
{ .compatible = "avia,hx711", },
|
||||
{},
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, of_hx711_match);
|
||||
|
||||
static struct platform_driver hx711_driver = {
|
||||
.probe = hx711_probe,
|
||||
.remove = hx711_remove,
|
||||
.driver = {
|
||||
.name = "hx711-gpio",
|
||||
.of_match_table = of_hx711_match,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(hx711_driver);
|
||||
|
||||
MODULE_AUTHOR("Andreas Klinger <ak@it-klinger.de>");
|
||||
MODULE_DESCRIPTION("HX711 bitbanging driver - ADC for weight cells");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:hx711-gpio");
|
||||
|
|
@ -22,6 +22,8 @@
|
|||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/kfifo_buf.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/kthread.h>
|
||||
|
|
|
@ -0,0 +1,181 @@
|
|||
/*
|
||||
* iio/adc/max11100.c
|
||||
* Maxim max11100 ADC Driver with IIO interface
|
||||
*
|
||||
* Copyright (C) 2016-17 Renesas Electronics Corporation
|
||||
* Copyright (C) 2016-17 Jacopo Mondi
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include <linux/delay.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/driver.h>
|
||||
|
||||
/*
|
||||
* LSB is the ADC single digital step
|
||||
* 1 LSB = (vref_mv / 2 ^ 16)
|
||||
*
|
||||
* LSB is used to calculate analog voltage value
|
||||
* from the number of ADC steps count
|
||||
*
|
||||
* Ain = (count * LSB)
|
||||
*/
|
||||
#define MAX11100_LSB_DIV (1 << 16)
|
||||
|
||||
struct max11100_state {
|
||||
struct regulator *vref_reg;
|
||||
struct spi_device *spi;
|
||||
|
||||
/*
|
||||
* DMA (thus cache coherency maintenance) requires the
|
||||
* transfer buffers to live in their own cache lines.
|
||||
*/
|
||||
u8 buffer[3] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
static struct iio_chan_spec max11100_channels[] = {
|
||||
{ /* [0] */
|
||||
.type = IIO_VOLTAGE,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
},
|
||||
};
|
||||
|
||||
static int max11100_read_single(struct iio_dev *indio_dev, int *val)
|
||||
{
|
||||
int ret;
|
||||
struct max11100_state *state = iio_priv(indio_dev);
|
||||
|
||||
ret = spi_read(state->spi, state->buffer, sizeof(state->buffer));
|
||||
if (ret) {
|
||||
dev_err(&indio_dev->dev, "SPI transfer failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* the first 8 bits sent out from ADC must be 0s */
|
||||
if (state->buffer[0]) {
|
||||
dev_err(&indio_dev->dev, "Invalid value: buffer[0] != 0\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*val = (state->buffer[1] << 8) | state->buffer[2];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max11100_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long info)
|
||||
{
|
||||
int ret, vref_uv;
|
||||
struct max11100_state *state = iio_priv(indio_dev);
|
||||
|
||||
switch (info) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = max11100_read_single(indio_dev, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
vref_uv = regulator_get_voltage(state->vref_reg);
|
||||
if (vref_uv < 0)
|
||||
/* dummy regulator "get_voltage" returns -EINVAL */
|
||||
return -EINVAL;
|
||||
|
||||
*val = vref_uv / 1000;
|
||||
*val2 = MAX11100_LSB_DIV;
|
||||
return IIO_VAL_FRACTIONAL;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct iio_info max11100_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = max11100_read_raw,
|
||||
};
|
||||
|
||||
static int max11100_probe(struct spi_device *spi)
|
||||
{
|
||||
int ret;
|
||||
struct iio_dev *indio_dev;
|
||||
struct max11100_state *state;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*state));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
|
||||
state = iio_priv(indio_dev);
|
||||
state->spi = spi;
|
||||
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->dev.of_node = spi->dev.of_node;
|
||||
indio_dev->name = "max11100";
|
||||
indio_dev->info = &max11100_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = max11100_channels,
|
||||
indio_dev->num_channels = ARRAY_SIZE(max11100_channels),
|
||||
|
||||
state->vref_reg = devm_regulator_get(&spi->dev, "vref");
|
||||
if (IS_ERR(state->vref_reg))
|
||||
return PTR_ERR(state->vref_reg);
|
||||
|
||||
ret = regulator_enable(state->vref_reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto disable_regulator;
|
||||
|
||||
return 0;
|
||||
|
||||
disable_regulator:
|
||||
regulator_disable(state->vref_reg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int max11100_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct max11100_state *state = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
regulator_disable(state->vref_reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id max11100_ids[] = {
|
||||
{.compatible = "maxim,max11100"},
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, max11100_ids);
|
||||
|
||||
static struct spi_driver max11100_driver = {
|
||||
.driver = {
|
||||
.name = "max11100",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_match_ptr(max11100_ids),
|
||||
},
|
||||
.probe = max11100_probe,
|
||||
.remove = max11100_remove,
|
||||
};
|
||||
|
||||
module_spi_driver(max11100_driver);
|
||||
|
||||
MODULE_AUTHOR("Jacopo Mondi <jacopo@jmondi.org>");
|
||||
MODULE_DESCRIPTION("Maxim max11100 ADC Driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -1567,6 +1567,7 @@ static const struct of_device_id max1363_of_match[] = {
|
|||
MAX1363_COMPATIBLE("maxim,max11647", max11647),
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, max1363_of_match);
|
||||
#endif
|
||||
|
||||
static int max1363_probe(struct i2c_client *client,
|
||||
|
|
|
@ -0,0 +1,922 @@
|
|||
/*
|
||||
* Amlogic Meson Successive Approximation Register (SAR) A/D Converter
|
||||
*
|
||||
* Copyright (C) 2017 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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/bitfield.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#define MESON_SAR_ADC_REG0 0x00
|
||||
#define MESON_SAR_ADC_REG0_PANEL_DETECT BIT(31)
|
||||
#define MESON_SAR_ADC_REG0_BUSY_MASK GENMASK(30, 28)
|
||||
#define MESON_SAR_ADC_REG0_DELTA_BUSY BIT(30)
|
||||
#define MESON_SAR_ADC_REG0_AVG_BUSY BIT(29)
|
||||
#define MESON_SAR_ADC_REG0_SAMPLE_BUSY BIT(28)
|
||||
#define MESON_SAR_ADC_REG0_FIFO_FULL BIT(27)
|
||||
#define MESON_SAR_ADC_REG0_FIFO_EMPTY BIT(26)
|
||||
#define MESON_SAR_ADC_REG0_FIFO_COUNT_MASK GENMASK(25, 21)
|
||||
#define MESON_SAR_ADC_REG0_ADC_BIAS_CTRL_MASK GENMASK(20, 19)
|
||||
#define MESON_SAR_ADC_REG0_CURR_CHAN_ID_MASK GENMASK(18, 16)
|
||||
#define MESON_SAR_ADC_REG0_ADC_TEMP_SEN_SEL BIT(15)
|
||||
#define MESON_SAR_ADC_REG0_SAMPLING_STOP BIT(14)
|
||||
#define MESON_SAR_ADC_REG0_CHAN_DELTA_EN_MASK GENMASK(13, 12)
|
||||
#define MESON_SAR_ADC_REG0_DETECT_IRQ_POL BIT(10)
|
||||
#define MESON_SAR_ADC_REG0_DETECT_IRQ_EN BIT(9)
|
||||
#define MESON_SAR_ADC_REG0_FIFO_CNT_IRQ_MASK GENMASK(8, 4)
|
||||
#define MESON_SAR_ADC_REG0_FIFO_IRQ_EN BIT(3)
|
||||
#define MESON_SAR_ADC_REG0_SAMPLING_START BIT(2)
|
||||
#define MESON_SAR_ADC_REG0_CONTINUOUS_EN BIT(1)
|
||||
#define MESON_SAR_ADC_REG0_SAMPLE_ENGINE_ENABLE BIT(0)
|
||||
|
||||
#define MESON_SAR_ADC_CHAN_LIST 0x04
|
||||
#define MESON_SAR_ADC_CHAN_LIST_MAX_INDEX_MASK GENMASK(26, 24)
|
||||
#define MESON_SAR_ADC_CHAN_LIST_ENTRY_MASK(_chan) \
|
||||
(GENMASK(2, 0) << ((_chan) * 3))
|
||||
|
||||
#define MESON_SAR_ADC_AVG_CNTL 0x08
|
||||
#define MESON_SAR_ADC_AVG_CNTL_AVG_MODE_SHIFT(_chan) \
|
||||
(16 + ((_chan) * 2))
|
||||
#define MESON_SAR_ADC_AVG_CNTL_AVG_MODE_MASK(_chan) \
|
||||
(GENMASK(17, 16) << ((_chan) * 2))
|
||||
#define MESON_SAR_ADC_AVG_CNTL_NUM_SAMPLES_SHIFT(_chan) \
|
||||
(0 + ((_chan) * 2))
|
||||
#define MESON_SAR_ADC_AVG_CNTL_NUM_SAMPLES_MASK(_chan) \
|
||||
(GENMASK(1, 0) << ((_chan) * 2))
|
||||
|
||||
#define MESON_SAR_ADC_REG3 0x0c
|
||||
#define MESON_SAR_ADC_REG3_CNTL_USE_SC_DLY BIT(31)
|
||||
#define MESON_SAR_ADC_REG3_CLK_EN BIT(30)
|
||||
#define MESON_SAR_ADC_REG3_BL30_INITIALIZED BIT(28)
|
||||
#define MESON_SAR_ADC_REG3_CTRL_CONT_RING_COUNTER_EN BIT(27)
|
||||
#define MESON_SAR_ADC_REG3_CTRL_SAMPLING_CLOCK_PHASE BIT(26)
|
||||
#define MESON_SAR_ADC_REG3_CTRL_CHAN7_MUX_SEL_MASK GENMASK(25, 23)
|
||||
#define MESON_SAR_ADC_REG3_DETECT_EN BIT(22)
|
||||
#define MESON_SAR_ADC_REG3_ADC_EN BIT(21)
|
||||
#define MESON_SAR_ADC_REG3_PANEL_DETECT_COUNT_MASK GENMASK(20, 18)
|
||||
#define MESON_SAR_ADC_REG3_PANEL_DETECT_FILTER_TB_MASK GENMASK(17, 16)
|
||||
#define MESON_SAR_ADC_REG3_ADC_CLK_DIV_SHIFT 10
|
||||
#define MESON_SAR_ADC_REG3_ADC_CLK_DIV_WIDTH 5
|
||||
#define MESON_SAR_ADC_REG3_BLOCK_DLY_SEL_MASK GENMASK(9, 8)
|
||||
#define MESON_SAR_ADC_REG3_BLOCK_DLY_MASK GENMASK(7, 0)
|
||||
|
||||
#define MESON_SAR_ADC_DELAY 0x10
|
||||
#define MESON_SAR_ADC_DELAY_INPUT_DLY_SEL_MASK GENMASK(25, 24)
|
||||
#define MESON_SAR_ADC_DELAY_BL30_BUSY BIT(15)
|
||||
#define MESON_SAR_ADC_DELAY_KERNEL_BUSY BIT(14)
|
||||
#define MESON_SAR_ADC_DELAY_INPUT_DLY_CNT_MASK GENMASK(23, 16)
|
||||
#define MESON_SAR_ADC_DELAY_SAMPLE_DLY_SEL_MASK GENMASK(9, 8)
|
||||
#define MESON_SAR_ADC_DELAY_SAMPLE_DLY_CNT_MASK GENMASK(7, 0)
|
||||
|
||||
#define MESON_SAR_ADC_LAST_RD 0x14
|
||||
#define MESON_SAR_ADC_LAST_RD_LAST_CHANNEL1_MASK GENMASK(23, 16)
|
||||
#define MESON_SAR_ADC_LAST_RD_LAST_CHANNEL0_MASK GENMASK(9, 0)
|
||||
|
||||
#define MESON_SAR_ADC_FIFO_RD 0x18
|
||||
#define MESON_SAR_ADC_FIFO_RD_CHAN_ID_MASK GENMASK(14, 12)
|
||||
#define MESON_SAR_ADC_FIFO_RD_SAMPLE_VALUE_MASK GENMASK(11, 0)
|
||||
|
||||
#define MESON_SAR_ADC_AUX_SW 0x1c
|
||||
#define MESON_SAR_ADC_AUX_SW_MUX_SEL_CHAN_MASK(_chan) \
|
||||
(GENMASK(10, 8) << (((_chan) - 2) * 2))
|
||||
#define MESON_SAR_ADC_AUX_SW_VREF_P_MUX BIT(6)
|
||||
#define MESON_SAR_ADC_AUX_SW_VREF_N_MUX BIT(5)
|
||||
#define MESON_SAR_ADC_AUX_SW_MODE_SEL BIT(4)
|
||||
#define MESON_SAR_ADC_AUX_SW_YP_DRIVE_SW BIT(3)
|
||||
#define MESON_SAR_ADC_AUX_SW_XP_DRIVE_SW BIT(2)
|
||||
#define MESON_SAR_ADC_AUX_SW_YM_DRIVE_SW BIT(1)
|
||||
#define MESON_SAR_ADC_AUX_SW_XM_DRIVE_SW BIT(0)
|
||||
|
||||
#define MESON_SAR_ADC_CHAN_10_SW 0x20
|
||||
#define MESON_SAR_ADC_CHAN_10_SW_CHAN1_MUX_SEL_MASK GENMASK(25, 23)
|
||||
#define MESON_SAR_ADC_CHAN_10_SW_CHAN1_VREF_P_MUX BIT(22)
|
||||
#define MESON_SAR_ADC_CHAN_10_SW_CHAN1_VREF_N_MUX BIT(21)
|
||||
#define MESON_SAR_ADC_CHAN_10_SW_CHAN1_MODE_SEL BIT(20)
|
||||
#define MESON_SAR_ADC_CHAN_10_SW_CHAN1_YP_DRIVE_SW BIT(19)
|
||||
#define MESON_SAR_ADC_CHAN_10_SW_CHAN1_XP_DRIVE_SW BIT(18)
|
||||
#define MESON_SAR_ADC_CHAN_10_SW_CHAN1_YM_DRIVE_SW BIT(17)
|
||||
#define MESON_SAR_ADC_CHAN_10_SW_CHAN1_XM_DRIVE_SW BIT(16)
|
||||
#define MESON_SAR_ADC_CHAN_10_SW_CHAN0_MUX_SEL_MASK GENMASK(9, 7)
|
||||
#define MESON_SAR_ADC_CHAN_10_SW_CHAN0_VREF_P_MUX BIT(6)
|
||||
#define MESON_SAR_ADC_CHAN_10_SW_CHAN0_VREF_N_MUX BIT(5)
|
||||
#define MESON_SAR_ADC_CHAN_10_SW_CHAN0_MODE_SEL BIT(4)
|
||||
#define MESON_SAR_ADC_CHAN_10_SW_CHAN0_YP_DRIVE_SW BIT(3)
|
||||
#define MESON_SAR_ADC_CHAN_10_SW_CHAN0_XP_DRIVE_SW BIT(2)
|
||||
#define MESON_SAR_ADC_CHAN_10_SW_CHAN0_YM_DRIVE_SW BIT(1)
|
||||
#define MESON_SAR_ADC_CHAN_10_SW_CHAN0_XM_DRIVE_SW BIT(0)
|
||||
|
||||
#define MESON_SAR_ADC_DETECT_IDLE_SW 0x24
|
||||
#define MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_SW_EN BIT(26)
|
||||
#define MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_MUX_MASK GENMASK(25, 23)
|
||||
#define MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_VREF_P_MUX BIT(22)
|
||||
#define MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_VREF_N_MUX BIT(21)
|
||||
#define MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_MODE_SEL BIT(20)
|
||||
#define MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_YP_DRIVE_SW BIT(19)
|
||||
#define MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_XP_DRIVE_SW BIT(18)
|
||||
#define MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_YM_DRIVE_SW BIT(17)
|
||||
#define MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_XM_DRIVE_SW BIT(16)
|
||||
#define MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_MUX_SEL_MASK GENMASK(9, 7)
|
||||
#define MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_VREF_P_MUX BIT(6)
|
||||
#define MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_VREF_N_MUX BIT(5)
|
||||
#define MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_MODE_SEL BIT(4)
|
||||
#define MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_YP_DRIVE_SW BIT(3)
|
||||
#define MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_XP_DRIVE_SW BIT(2)
|
||||
#define MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_YM_DRIVE_SW BIT(1)
|
||||
#define MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_XM_DRIVE_SW BIT(0)
|
||||
|
||||
#define MESON_SAR_ADC_DELTA_10 0x28
|
||||
#define MESON_SAR_ADC_DELTA_10_TEMP_SEL BIT(27)
|
||||
#define MESON_SAR_ADC_DELTA_10_TS_REVE1 BIT(26)
|
||||
#define MESON_SAR_ADC_DELTA_10_CHAN1_DELTA_VALUE_MASK GENMASK(25, 16)
|
||||
#define MESON_SAR_ADC_DELTA_10_TS_REVE0 BIT(15)
|
||||
#define MESON_SAR_ADC_DELTA_10_TS_C_SHIFT 11
|
||||
#define MESON_SAR_ADC_DELTA_10_TS_C_MASK GENMASK(14, 11)
|
||||
#define MESON_SAR_ADC_DELTA_10_TS_VBG_EN BIT(10)
|
||||
#define MESON_SAR_ADC_DELTA_10_CHAN0_DELTA_VALUE_MASK GENMASK(9, 0)
|
||||
|
||||
/*
|
||||
* NOTE: registers from here are undocumented (the vendor Linux kernel driver
|
||||
* and u-boot source served as reference). These only seem to be relevant on
|
||||
* GXBB and newer.
|
||||
*/
|
||||
#define MESON_SAR_ADC_REG11 0x2c
|
||||
#define MESON_SAR_ADC_REG11_BANDGAP_EN BIT(13)
|
||||
|
||||
#define MESON_SAR_ADC_REG13 0x34
|
||||
#define MESON_SAR_ADC_REG13_12BIT_CALIBRATION_MASK GENMASK(13, 8)
|
||||
|
||||
#define MESON_SAR_ADC_MAX_FIFO_SIZE 32
|
||||
|
||||
#define MESON_SAR_ADC_CHAN(_chan) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = _chan, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_AVERAGE_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.datasheet_name = "SAR_ADC_CH"#_chan, \
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: the hardware supports IIO_TEMP for channel 6 as well which is
|
||||
* currently not supported by this driver.
|
||||
*/
|
||||
static const struct iio_chan_spec meson_sar_adc_iio_channels[] = {
|
||||
MESON_SAR_ADC_CHAN(0),
|
||||
MESON_SAR_ADC_CHAN(1),
|
||||
MESON_SAR_ADC_CHAN(2),
|
||||
MESON_SAR_ADC_CHAN(3),
|
||||
MESON_SAR_ADC_CHAN(4),
|
||||
MESON_SAR_ADC_CHAN(5),
|
||||
MESON_SAR_ADC_CHAN(6),
|
||||
MESON_SAR_ADC_CHAN(7),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(8),
|
||||
};
|
||||
|
||||
enum meson_sar_adc_avg_mode {
|
||||
NO_AVERAGING = 0x0,
|
||||
MEAN_AVERAGING = 0x1,
|
||||
MEDIAN_AVERAGING = 0x2,
|
||||
};
|
||||
|
||||
enum meson_sar_adc_num_samples {
|
||||
ONE_SAMPLE = 0x0,
|
||||
TWO_SAMPLES = 0x1,
|
||||
FOUR_SAMPLES = 0x2,
|
||||
EIGHT_SAMPLES = 0x3,
|
||||
};
|
||||
|
||||
enum meson_sar_adc_chan7_mux_sel {
|
||||
CHAN7_MUX_VSS = 0x0,
|
||||
CHAN7_MUX_VDD_DIV4 = 0x1,
|
||||
CHAN7_MUX_VDD_DIV2 = 0x2,
|
||||
CHAN7_MUX_VDD_MUL3_DIV4 = 0x3,
|
||||
CHAN7_MUX_VDD = 0x4,
|
||||
CHAN7_MUX_CH7_INPUT = 0x7,
|
||||
};
|
||||
|
||||
struct meson_sar_adc_data {
|
||||
unsigned int resolution;
|
||||
const char *name;
|
||||
};
|
||||
|
||||
struct meson_sar_adc_priv {
|
||||
struct regmap *regmap;
|
||||
struct regulator *vref;
|
||||
const struct meson_sar_adc_data *data;
|
||||
struct clk *clkin;
|
||||
struct clk *core_clk;
|
||||
struct clk *sana_clk;
|
||||
struct clk *adc_sel_clk;
|
||||
struct clk *adc_clk;
|
||||
struct clk_gate clk_gate;
|
||||
struct clk *adc_div_clk;
|
||||
struct clk_divider clk_div;
|
||||
};
|
||||
|
||||
static const struct regmap_config meson_sar_adc_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.max_register = MESON_SAR_ADC_REG13,
|
||||
};
|
||||
|
||||
static unsigned int meson_sar_adc_get_fifo_count(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
|
||||
u32 regval;
|
||||
|
||||
regmap_read(priv->regmap, MESON_SAR_ADC_REG0, ®val);
|
||||
|
||||
return FIELD_GET(MESON_SAR_ADC_REG0_FIFO_COUNT_MASK, regval);
|
||||
}
|
||||
|
||||
static int meson_sar_adc_wait_busy_clear(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
|
||||
int regval, timeout = 10000;
|
||||
|
||||
/*
|
||||
* NOTE: we need a small delay before reading the status, otherwise
|
||||
* the sample engine may not have started internally (which would
|
||||
* seem to us that sampling is already finished).
|
||||
*/
|
||||
do {
|
||||
udelay(1);
|
||||
regmap_read(priv->regmap, MESON_SAR_ADC_REG0, ®val);
|
||||
} while (FIELD_GET(MESON_SAR_ADC_REG0_BUSY_MASK, regval) && timeout--);
|
||||
|
||||
if (timeout < 0)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int meson_sar_adc_read_raw_sample(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
int *val)
|
||||
{
|
||||
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
|
||||
int ret, regval, fifo_chan, fifo_val, sum = 0, count = 0;
|
||||
|
||||
ret = meson_sar_adc_wait_busy_clear(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
while (meson_sar_adc_get_fifo_count(indio_dev) > 0 &&
|
||||
count < MESON_SAR_ADC_MAX_FIFO_SIZE) {
|
||||
regmap_read(priv->regmap, MESON_SAR_ADC_FIFO_RD, ®val);
|
||||
|
||||
fifo_chan = FIELD_GET(MESON_SAR_ADC_FIFO_RD_CHAN_ID_MASK,
|
||||
regval);
|
||||
if (fifo_chan != chan->channel)
|
||||
continue;
|
||||
|
||||
fifo_val = FIELD_GET(MESON_SAR_ADC_FIFO_RD_SAMPLE_VALUE_MASK,
|
||||
regval);
|
||||
fifo_val &= (BIT(priv->data->resolution) - 1);
|
||||
|
||||
sum += fifo_val;
|
||||
count++;
|
||||
}
|
||||
|
||||
if (!count)
|
||||
return -ENOENT;
|
||||
|
||||
*val = sum / count;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void meson_sar_adc_set_averaging(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
enum meson_sar_adc_avg_mode mode,
|
||||
enum meson_sar_adc_num_samples samples)
|
||||
{
|
||||
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
|
||||
int val, channel = chan->channel;
|
||||
|
||||
val = samples << MESON_SAR_ADC_AVG_CNTL_NUM_SAMPLES_SHIFT(channel);
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_AVG_CNTL,
|
||||
MESON_SAR_ADC_AVG_CNTL_NUM_SAMPLES_MASK(channel),
|
||||
val);
|
||||
|
||||
val = mode << MESON_SAR_ADC_AVG_CNTL_AVG_MODE_SHIFT(channel);
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_AVG_CNTL,
|
||||
MESON_SAR_ADC_AVG_CNTL_AVG_MODE_MASK(channel), val);
|
||||
}
|
||||
|
||||
static void meson_sar_adc_enable_channel(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan)
|
||||
{
|
||||
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
|
||||
u32 regval;
|
||||
|
||||
/*
|
||||
* the SAR ADC engine allows sampling multiple channels at the same
|
||||
* time. to keep it simple we're only working with one *internal*
|
||||
* channel, which starts counting at index 0 (which means: count = 1).
|
||||
*/
|
||||
regval = FIELD_PREP(MESON_SAR_ADC_CHAN_LIST_MAX_INDEX_MASK, 0);
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_CHAN_LIST,
|
||||
MESON_SAR_ADC_CHAN_LIST_MAX_INDEX_MASK, regval);
|
||||
|
||||
/* map channel index 0 to the channel which we want to read */
|
||||
regval = FIELD_PREP(MESON_SAR_ADC_CHAN_LIST_ENTRY_MASK(0),
|
||||
chan->channel);
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_CHAN_LIST,
|
||||
MESON_SAR_ADC_CHAN_LIST_ENTRY_MASK(0), regval);
|
||||
|
||||
regval = FIELD_PREP(MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_MUX_MASK,
|
||||
chan->channel);
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_DETECT_IDLE_SW,
|
||||
MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_MUX_MASK,
|
||||
regval);
|
||||
|
||||
regval = FIELD_PREP(MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_MUX_SEL_MASK,
|
||||
chan->channel);
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_DETECT_IDLE_SW,
|
||||
MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_MUX_SEL_MASK,
|
||||
regval);
|
||||
|
||||
if (chan->channel == 6)
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELTA_10,
|
||||
MESON_SAR_ADC_DELTA_10_TEMP_SEL, 0);
|
||||
}
|
||||
|
||||
static void meson_sar_adc_set_chan7_mux(struct iio_dev *indio_dev,
|
||||
enum meson_sar_adc_chan7_mux_sel sel)
|
||||
{
|
||||
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
|
||||
u32 regval;
|
||||
|
||||
regval = FIELD_PREP(MESON_SAR_ADC_REG3_CTRL_CHAN7_MUX_SEL_MASK, sel);
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG3,
|
||||
MESON_SAR_ADC_REG3_CTRL_CHAN7_MUX_SEL_MASK, regval);
|
||||
|
||||
usleep_range(10, 20);
|
||||
}
|
||||
|
||||
static void meson_sar_adc_start_sample_engine(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
|
||||
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0,
|
||||
MESON_SAR_ADC_REG0_SAMPLE_ENGINE_ENABLE,
|
||||
MESON_SAR_ADC_REG0_SAMPLE_ENGINE_ENABLE);
|
||||
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0,
|
||||
MESON_SAR_ADC_REG0_SAMPLING_START,
|
||||
MESON_SAR_ADC_REG0_SAMPLING_START);
|
||||
}
|
||||
|
||||
static void meson_sar_adc_stop_sample_engine(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
|
||||
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0,
|
||||
MESON_SAR_ADC_REG0_SAMPLING_STOP,
|
||||
MESON_SAR_ADC_REG0_SAMPLING_STOP);
|
||||
|
||||
/* wait until all modules are stopped */
|
||||
meson_sar_adc_wait_busy_clear(indio_dev);
|
||||
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0,
|
||||
MESON_SAR_ADC_REG0_SAMPLE_ENGINE_ENABLE, 0);
|
||||
}
|
||||
|
||||
static int meson_sar_adc_lock(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
|
||||
int val, timeout = 10000;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
|
||||
/* prevent BL30 from using the SAR ADC while we are using it */
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELAY,
|
||||
MESON_SAR_ADC_DELAY_KERNEL_BUSY,
|
||||
MESON_SAR_ADC_DELAY_KERNEL_BUSY);
|
||||
|
||||
/* wait until BL30 releases it's lock (so we can use the SAR ADC) */
|
||||
do {
|
||||
udelay(1);
|
||||
regmap_read(priv->regmap, MESON_SAR_ADC_DELAY, &val);
|
||||
} while (val & MESON_SAR_ADC_DELAY_BL30_BUSY && timeout--);
|
||||
|
||||
if (timeout < 0)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void meson_sar_adc_unlock(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
|
||||
|
||||
/* allow BL30 to use the SAR ADC again */
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELAY,
|
||||
MESON_SAR_ADC_DELAY_KERNEL_BUSY, 0);
|
||||
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
}
|
||||
|
||||
static void meson_sar_adc_clear_fifo(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
|
||||
int count;
|
||||
|
||||
for (count = 0; count < MESON_SAR_ADC_MAX_FIFO_SIZE; count++) {
|
||||
if (!meson_sar_adc_get_fifo_count(indio_dev))
|
||||
break;
|
||||
|
||||
regmap_read(priv->regmap, MESON_SAR_ADC_FIFO_RD, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static int meson_sar_adc_get_sample(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
enum meson_sar_adc_avg_mode avg_mode,
|
||||
enum meson_sar_adc_num_samples avg_samples,
|
||||
int *val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = meson_sar_adc_lock(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* clear the FIFO to make sure we're not reading old values */
|
||||
meson_sar_adc_clear_fifo(indio_dev);
|
||||
|
||||
meson_sar_adc_set_averaging(indio_dev, chan, avg_mode, avg_samples);
|
||||
|
||||
meson_sar_adc_enable_channel(indio_dev, chan);
|
||||
|
||||
meson_sar_adc_start_sample_engine(indio_dev);
|
||||
ret = meson_sar_adc_read_raw_sample(indio_dev, chan, val);
|
||||
meson_sar_adc_stop_sample_engine(indio_dev);
|
||||
|
||||
meson_sar_adc_unlock(indio_dev);
|
||||
|
||||
if (ret) {
|
||||
dev_warn(indio_dev->dev.parent,
|
||||
"failed to read sample for channel %d: %d\n",
|
||||
chan->channel, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
static int meson_sar_adc_iio_info_read_raw(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
return meson_sar_adc_get_sample(indio_dev, chan, NO_AVERAGING,
|
||||
ONE_SAMPLE, val);
|
||||
break;
|
||||
|
||||
case IIO_CHAN_INFO_AVERAGE_RAW:
|
||||
return meson_sar_adc_get_sample(indio_dev, chan,
|
||||
MEAN_AVERAGING, EIGHT_SAMPLES,
|
||||
val);
|
||||
break;
|
||||
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
ret = regulator_get_voltage(priv->vref);
|
||||
if (ret < 0) {
|
||||
dev_err(indio_dev->dev.parent,
|
||||
"failed to get vref voltage: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*val = ret / 1000;
|
||||
*val2 = priv->data->resolution;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int meson_sar_adc_clk_init(struct iio_dev *indio_dev,
|
||||
void __iomem *base)
|
||||
{
|
||||
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
|
||||
struct clk_init_data init;
|
||||
const char *clk_parents[1];
|
||||
|
||||
init.name = devm_kasprintf(&indio_dev->dev, GFP_KERNEL, "%s#adc_div",
|
||||
of_node_full_name(indio_dev->dev.of_node));
|
||||
init.flags = 0;
|
||||
init.ops = &clk_divider_ops;
|
||||
clk_parents[0] = __clk_get_name(priv->clkin);
|
||||
init.parent_names = clk_parents;
|
||||
init.num_parents = 1;
|
||||
|
||||
priv->clk_div.reg = base + MESON_SAR_ADC_REG3;
|
||||
priv->clk_div.shift = MESON_SAR_ADC_REG3_ADC_CLK_DIV_SHIFT;
|
||||
priv->clk_div.width = MESON_SAR_ADC_REG3_ADC_CLK_DIV_WIDTH;
|
||||
priv->clk_div.hw.init = &init;
|
||||
priv->clk_div.flags = 0;
|
||||
|
||||
priv->adc_div_clk = devm_clk_register(&indio_dev->dev,
|
||||
&priv->clk_div.hw);
|
||||
if (WARN_ON(IS_ERR(priv->adc_div_clk)))
|
||||
return PTR_ERR(priv->adc_div_clk);
|
||||
|
||||
init.name = devm_kasprintf(&indio_dev->dev, GFP_KERNEL, "%s#adc_en",
|
||||
of_node_full_name(indio_dev->dev.of_node));
|
||||
init.flags = CLK_SET_RATE_PARENT;
|
||||
init.ops = &clk_gate_ops;
|
||||
clk_parents[0] = __clk_get_name(priv->adc_div_clk);
|
||||
init.parent_names = clk_parents;
|
||||
init.num_parents = 1;
|
||||
|
||||
priv->clk_gate.reg = base + MESON_SAR_ADC_REG3;
|
||||
priv->clk_gate.bit_idx = fls(MESON_SAR_ADC_REG3_CLK_EN);
|
||||
priv->clk_gate.hw.init = &init;
|
||||
|
||||
priv->adc_clk = devm_clk_register(&indio_dev->dev, &priv->clk_gate.hw);
|
||||
if (WARN_ON(IS_ERR(priv->adc_clk)))
|
||||
return PTR_ERR(priv->adc_clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int meson_sar_adc_init(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
|
||||
int regval, ret;
|
||||
|
||||
/*
|
||||
* make sure we start at CH7 input since the other muxes are only used
|
||||
* for internal calibration.
|
||||
*/
|
||||
meson_sar_adc_set_chan7_mux(indio_dev, CHAN7_MUX_CH7_INPUT);
|
||||
|
||||
/*
|
||||
* leave sampling delay and the input clocks as configured by BL30 to
|
||||
* make sure BL30 gets the values it expects when reading the
|
||||
* temperature sensor.
|
||||
*/
|
||||
regmap_read(priv->regmap, MESON_SAR_ADC_REG3, ®val);
|
||||
if (regval & MESON_SAR_ADC_REG3_BL30_INITIALIZED)
|
||||
return 0;
|
||||
|
||||
meson_sar_adc_stop_sample_engine(indio_dev);
|
||||
|
||||
/* update the channel 6 MUX to select the temperature sensor */
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0,
|
||||
MESON_SAR_ADC_REG0_ADC_TEMP_SEN_SEL,
|
||||
MESON_SAR_ADC_REG0_ADC_TEMP_SEN_SEL);
|
||||
|
||||
/* disable all channels by default */
|
||||
regmap_write(priv->regmap, MESON_SAR_ADC_CHAN_LIST, 0x0);
|
||||
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG3,
|
||||
MESON_SAR_ADC_REG3_CTRL_SAMPLING_CLOCK_PHASE, 0);
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG3,
|
||||
MESON_SAR_ADC_REG3_CNTL_USE_SC_DLY,
|
||||
MESON_SAR_ADC_REG3_CNTL_USE_SC_DLY);
|
||||
|
||||
/* delay between two samples = (10+1) * 1uS */
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELAY,
|
||||
MESON_SAR_ADC_DELAY_INPUT_DLY_CNT_MASK,
|
||||
FIELD_PREP(MESON_SAR_ADC_DELAY_SAMPLE_DLY_CNT_MASK,
|
||||
10));
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELAY,
|
||||
MESON_SAR_ADC_DELAY_SAMPLE_DLY_SEL_MASK,
|
||||
FIELD_PREP(MESON_SAR_ADC_DELAY_SAMPLE_DLY_SEL_MASK,
|
||||
0));
|
||||
|
||||
/* delay between two samples = (10+1) * 1uS */
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELAY,
|
||||
MESON_SAR_ADC_DELAY_INPUT_DLY_CNT_MASK,
|
||||
FIELD_PREP(MESON_SAR_ADC_DELAY_INPUT_DLY_CNT_MASK,
|
||||
10));
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELAY,
|
||||
MESON_SAR_ADC_DELAY_INPUT_DLY_SEL_MASK,
|
||||
FIELD_PREP(MESON_SAR_ADC_DELAY_INPUT_DLY_SEL_MASK,
|
||||
1));
|
||||
|
||||
ret = clk_set_parent(priv->adc_sel_clk, priv->clkin);
|
||||
if (ret) {
|
||||
dev_err(indio_dev->dev.parent,
|
||||
"failed to set adc parent to clkin\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = clk_set_rate(priv->adc_clk, 1200000);
|
||||
if (ret) {
|
||||
dev_err(indio_dev->dev.parent,
|
||||
"failed to set adc clock rate\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int meson_sar_adc_hw_enable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = meson_sar_adc_lock(indio_dev);
|
||||
if (ret)
|
||||
goto err_lock;
|
||||
|
||||
ret = regulator_enable(priv->vref);
|
||||
if (ret < 0) {
|
||||
dev_err(indio_dev->dev.parent,
|
||||
"failed to enable vref regulator\n");
|
||||
goto err_vref;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(priv->core_clk);
|
||||
if (ret) {
|
||||
dev_err(indio_dev->dev.parent, "failed to enable core clk\n");
|
||||
goto err_core_clk;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(priv->sana_clk);
|
||||
if (ret) {
|
||||
dev_err(indio_dev->dev.parent, "failed to enable sana clk\n");
|
||||
goto err_sana_clk;
|
||||
}
|
||||
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG11,
|
||||
MESON_SAR_ADC_REG11_BANDGAP_EN,
|
||||
MESON_SAR_ADC_REG11_BANDGAP_EN);
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG3,
|
||||
MESON_SAR_ADC_REG3_ADC_EN,
|
||||
MESON_SAR_ADC_REG3_ADC_EN);
|
||||
|
||||
udelay(5);
|
||||
|
||||
ret = clk_prepare_enable(priv->adc_clk);
|
||||
if (ret) {
|
||||
dev_err(indio_dev->dev.parent, "failed to enable adc clk\n");
|
||||
goto err_adc_clk;
|
||||
}
|
||||
|
||||
meson_sar_adc_unlock(indio_dev);
|
||||
|
||||
return 0;
|
||||
|
||||
err_adc_clk:
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG3,
|
||||
MESON_SAR_ADC_REG3_ADC_EN, 0);
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG11,
|
||||
MESON_SAR_ADC_REG11_BANDGAP_EN, 0);
|
||||
clk_disable_unprepare(priv->sana_clk);
|
||||
err_sana_clk:
|
||||
clk_disable_unprepare(priv->core_clk);
|
||||
err_core_clk:
|
||||
regulator_disable(priv->vref);
|
||||
err_vref:
|
||||
meson_sar_adc_unlock(indio_dev);
|
||||
err_lock:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int meson_sar_adc_hw_disable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = meson_sar_adc_lock(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
clk_disable_unprepare(priv->adc_clk);
|
||||
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG3,
|
||||
MESON_SAR_ADC_REG3_ADC_EN, 0);
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG11,
|
||||
MESON_SAR_ADC_REG11_BANDGAP_EN, 0);
|
||||
|
||||
clk_disable_unprepare(priv->sana_clk);
|
||||
clk_disable_unprepare(priv->core_clk);
|
||||
|
||||
regulator_disable(priv->vref);
|
||||
|
||||
meson_sar_adc_unlock(indio_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct iio_info meson_sar_adc_iio_info = {
|
||||
.read_raw = meson_sar_adc_iio_info_read_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
struct meson_sar_adc_data meson_sar_adc_gxbb_data = {
|
||||
.resolution = 10,
|
||||
.name = "meson-gxbb-saradc",
|
||||
};
|
||||
|
||||
struct meson_sar_adc_data meson_sar_adc_gxl_data = {
|
||||
.resolution = 12,
|
||||
.name = "meson-gxl-saradc",
|
||||
};
|
||||
|
||||
struct meson_sar_adc_data meson_sar_adc_gxm_data = {
|
||||
.resolution = 12,
|
||||
.name = "meson-gxm-saradc",
|
||||
};
|
||||
|
||||
static const struct of_device_id meson_sar_adc_of_match[] = {
|
||||
{
|
||||
.compatible = "amlogic,meson-gxbb-saradc",
|
||||
.data = &meson_sar_adc_gxbb_data,
|
||||
}, {
|
||||
.compatible = "amlogic,meson-gxl-saradc",
|
||||
.data = &meson_sar_adc_gxl_data,
|
||||
}, {
|
||||
.compatible = "amlogic,meson-gxm-saradc",
|
||||
.data = &meson_sar_adc_gxm_data,
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, meson_sar_adc_of_match);
|
||||
|
||||
static int meson_sar_adc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct meson_sar_adc_priv *priv;
|
||||
struct iio_dev *indio_dev;
|
||||
struct resource *res;
|
||||
void __iomem *base;
|
||||
const struct of_device_id *match;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*priv));
|
||||
if (!indio_dev) {
|
||||
dev_err(&pdev->dev, "failed allocating iio device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
priv = iio_priv(indio_dev);
|
||||
|
||||
match = of_match_device(meson_sar_adc_of_match, &pdev->dev);
|
||||
priv->data = match->data;
|
||||
|
||||
indio_dev->name = priv->data->name;
|
||||
indio_dev->dev.parent = &pdev->dev;
|
||||
indio_dev->dev.of_node = pdev->dev.of_node;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &meson_sar_adc_iio_info;
|
||||
|
||||
indio_dev->channels = meson_sar_adc_iio_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(meson_sar_adc_iio_channels);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
priv->regmap = devm_regmap_init_mmio(&pdev->dev, base,
|
||||
&meson_sar_adc_regmap_config);
|
||||
if (IS_ERR(priv->regmap))
|
||||
return PTR_ERR(priv->regmap);
|
||||
|
||||
priv->clkin = devm_clk_get(&pdev->dev, "clkin");
|
||||
if (IS_ERR(priv->clkin)) {
|
||||
dev_err(&pdev->dev, "failed to get clkin\n");
|
||||
return PTR_ERR(priv->clkin);
|
||||
}
|
||||
|
||||
priv->core_clk = devm_clk_get(&pdev->dev, "core");
|
||||
if (IS_ERR(priv->core_clk)) {
|
||||
dev_err(&pdev->dev, "failed to get core clk\n");
|
||||
return PTR_ERR(priv->core_clk);
|
||||
}
|
||||
|
||||
priv->sana_clk = devm_clk_get(&pdev->dev, "sana");
|
||||
if (IS_ERR(priv->sana_clk)) {
|
||||
if (PTR_ERR(priv->sana_clk) == -ENOENT) {
|
||||
priv->sana_clk = NULL;
|
||||
} else {
|
||||
dev_err(&pdev->dev, "failed to get sana clk\n");
|
||||
return PTR_ERR(priv->sana_clk);
|
||||
}
|
||||
}
|
||||
|
||||
priv->adc_clk = devm_clk_get(&pdev->dev, "adc_clk");
|
||||
if (IS_ERR(priv->adc_clk)) {
|
||||
if (PTR_ERR(priv->adc_clk) == -ENOENT) {
|
||||
priv->adc_clk = NULL;
|
||||
} else {
|
||||
dev_err(&pdev->dev, "failed to get adc clk\n");
|
||||
return PTR_ERR(priv->adc_clk);
|
||||
}
|
||||
}
|
||||
|
||||
priv->adc_sel_clk = devm_clk_get(&pdev->dev, "adc_sel");
|
||||
if (IS_ERR(priv->adc_sel_clk)) {
|
||||
if (PTR_ERR(priv->adc_sel_clk) == -ENOENT) {
|
||||
priv->adc_sel_clk = NULL;
|
||||
} else {
|
||||
dev_err(&pdev->dev, "failed to get adc_sel clk\n");
|
||||
return PTR_ERR(priv->adc_sel_clk);
|
||||
}
|
||||
}
|
||||
|
||||
/* on pre-GXBB SoCs the SAR ADC itself provides the ADC clock: */
|
||||
if (!priv->adc_clk) {
|
||||
ret = meson_sar_adc_clk_init(indio_dev, base);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
priv->vref = devm_regulator_get(&pdev->dev, "vref");
|
||||
if (IS_ERR(priv->vref)) {
|
||||
dev_err(&pdev->dev, "failed to get vref regulator\n");
|
||||
return PTR_ERR(priv->vref);
|
||||
}
|
||||
|
||||
ret = meson_sar_adc_init(indio_dev);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
ret = meson_sar_adc_hw_enable(indio_dev);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
platform_set_drvdata(pdev, indio_dev);
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto err_hw;
|
||||
|
||||
return 0;
|
||||
|
||||
err_hw:
|
||||
meson_sar_adc_hw_disable(indio_dev);
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int meson_sar_adc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
return meson_sar_adc_hw_disable(indio_dev);
|
||||
}
|
||||
|
||||
static int __maybe_unused meson_sar_adc_suspend(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
|
||||
return meson_sar_adc_hw_disable(indio_dev);
|
||||
}
|
||||
|
||||
static int __maybe_unused meson_sar_adc_resume(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
|
||||
return meson_sar_adc_hw_enable(indio_dev);
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(meson_sar_adc_pm_ops,
|
||||
meson_sar_adc_suspend, meson_sar_adc_resume);
|
||||
|
||||
static struct platform_driver meson_sar_adc_driver = {
|
||||
.probe = meson_sar_adc_probe,
|
||||
.remove = meson_sar_adc_remove,
|
||||
.driver = {
|
||||
.name = "meson-saradc",
|
||||
.of_match_table = meson_sar_adc_of_match,
|
||||
.pm = &meson_sar_adc_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(meson_sar_adc_driver);
|
||||
|
||||
MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl@googlemail.com>");
|
||||
MODULE_DESCRIPTION("Amlogic Meson SAR ADC driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
|
@ -84,7 +84,7 @@
|
|||
#define VADC_MAX_ADC_CODE 0xa800
|
||||
|
||||
#define VADC_ABSOLUTE_RANGE_UV 625000
|
||||
#define VADC_RATIOMETRIC_RANGE_UV 1800000
|
||||
#define VADC_RATIOMETRIC_RANGE 1800
|
||||
|
||||
#define VADC_DEF_PRESCALING 0 /* 1:1 */
|
||||
#define VADC_DEF_DECIMATION 0 /* 512 */
|
||||
|
@ -100,9 +100,23 @@
|
|||
|
||||
#define KELVINMIL_CELSIUSMIL 273150
|
||||
|
||||
#define PMI_CHG_SCALE_1 -138890
|
||||
#define PMI_CHG_SCALE_2 391750000000LL
|
||||
|
||||
#define VADC_CHAN_MIN VADC_USBIN
|
||||
#define VADC_CHAN_MAX VADC_LR_MUX3_BUF_PU1_PU2_XO_THERM
|
||||
|
||||
/**
|
||||
* struct vadc_map_pt - Map the graph representation for ADC channel
|
||||
* @x: Represent the ADC digitized code.
|
||||
* @y: Represent the physical data which can be temperature, voltage,
|
||||
* resistance.
|
||||
*/
|
||||
struct vadc_map_pt {
|
||||
s32 x;
|
||||
s32 y;
|
||||
};
|
||||
|
||||
/*
|
||||
* VADC_CALIB_ABSOLUTE: uses the 625mV and 1.25V as reference channels.
|
||||
* VADC_CALIB_RATIOMETRIC: uses the reference voltage (1.8V) and GND for
|
||||
|
@ -148,6 +162,9 @@ struct vadc_prescale_ratio {
|
|||
* start of conversion.
|
||||
* @avg_samples: ability to provide single result from the ADC
|
||||
* that is an average of multiple measurements.
|
||||
* @scale_fn: Represents the scaling function to convert voltage
|
||||
* physical units desired by the client for the channel.
|
||||
* Referenced from enum vadc_scale_fn_type.
|
||||
*/
|
||||
struct vadc_channel_prop {
|
||||
unsigned int channel;
|
||||
|
@ -156,6 +173,7 @@ struct vadc_channel_prop {
|
|||
unsigned int prescale;
|
||||
unsigned int hw_settle_time;
|
||||
unsigned int avg_samples;
|
||||
unsigned int scale_fn;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -186,6 +204,35 @@ struct vadc_priv {
|
|||
struct mutex lock;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct vadc_scale_fn - Scaling function prototype
|
||||
* @scale: Function pointer to one of the scaling functions
|
||||
* which takes the adc properties, channel properties,
|
||||
* and returns the physical result.
|
||||
*/
|
||||
struct vadc_scale_fn {
|
||||
int (*scale)(struct vadc_priv *, const struct vadc_channel_prop *,
|
||||
u16, int *);
|
||||
};
|
||||
|
||||
/**
|
||||
* enum vadc_scale_fn_type - Scaling function to convert ADC code to
|
||||
* physical scaled units for the channel.
|
||||
* SCALE_DEFAULT: Default scaling to convert raw adc code to voltage (uV).
|
||||
* SCALE_THERM_100K_PULLUP: Returns temperature in millidegC.
|
||||
* Uses a mapping table with 100K pullup.
|
||||
* SCALE_PMIC_THERM: Returns result in milli degree's Centigrade.
|
||||
* SCALE_XOTHERM: Returns XO thermistor voltage in millidegC.
|
||||
* SCALE_PMI_CHG_TEMP: Conversion for PMI CHG temp
|
||||
*/
|
||||
enum vadc_scale_fn_type {
|
||||
SCALE_DEFAULT = 0,
|
||||
SCALE_THERM_100K_PULLUP,
|
||||
SCALE_PMIC_THERM,
|
||||
SCALE_XOTHERM,
|
||||
SCALE_PMI_CHG_TEMP,
|
||||
};
|
||||
|
||||
static const struct vadc_prescale_ratio vadc_prescale_ratios[] = {
|
||||
{.num = 1, .den = 1},
|
||||
{.num = 1, .den = 3},
|
||||
|
@ -197,6 +244,44 @@ static const struct vadc_prescale_ratio vadc_prescale_ratios[] = {
|
|||
{.num = 1, .den = 10}
|
||||
};
|
||||
|
||||
/* Voltage to temperature */
|
||||
static const struct vadc_map_pt adcmap_100k_104ef_104fb[] = {
|
||||
{1758, -40},
|
||||
{1742, -35},
|
||||
{1719, -30},
|
||||
{1691, -25},
|
||||
{1654, -20},
|
||||
{1608, -15},
|
||||
{1551, -10},
|
||||
{1483, -5},
|
||||
{1404, 0},
|
||||
{1315, 5},
|
||||
{1218, 10},
|
||||
{1114, 15},
|
||||
{1007, 20},
|
||||
{900, 25},
|
||||
{795, 30},
|
||||
{696, 35},
|
||||
{605, 40},
|
||||
{522, 45},
|
||||
{448, 50},
|
||||
{383, 55},
|
||||
{327, 60},
|
||||
{278, 65},
|
||||
{237, 70},
|
||||
{202, 75},
|
||||
{172, 80},
|
||||
{146, 85},
|
||||
{125, 90},
|
||||
{107, 95},
|
||||
{92, 100},
|
||||
{79, 105},
|
||||
{68, 110},
|
||||
{59, 115},
|
||||
{51, 120},
|
||||
{44, 125}
|
||||
};
|
||||
|
||||
static int vadc_read(struct vadc_priv *vadc, u16 offset, u8 *data)
|
||||
{
|
||||
return regmap_bulk_read(vadc->regmap, vadc->base + offset, data, 1);
|
||||
|
@ -418,7 +503,7 @@ static int vadc_measure_ref_points(struct vadc_priv *vadc)
|
|||
u16 read_1, read_2;
|
||||
int ret;
|
||||
|
||||
vadc->graph[VADC_CALIB_RATIOMETRIC].dx = VADC_RATIOMETRIC_RANGE_UV;
|
||||
vadc->graph[VADC_CALIB_RATIOMETRIC].dx = VADC_RATIOMETRIC_RANGE;
|
||||
vadc->graph[VADC_CALIB_ABSOLUTE].dx = VADC_ABSOLUTE_RANGE_UV;
|
||||
|
||||
prop = vadc_get_channel(vadc, VADC_REF_1250MV);
|
||||
|
@ -468,27 +553,148 @@ err:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static s32 vadc_calibrate(struct vadc_priv *vadc,
|
||||
const struct vadc_channel_prop *prop, u16 adc_code)
|
||||
static int vadc_map_voltage_temp(const struct vadc_map_pt *pts,
|
||||
u32 tablesize, s32 input, s64 *output)
|
||||
{
|
||||
bool descending = 1;
|
||||
u32 i = 0;
|
||||
|
||||
if (!pts)
|
||||
return -EINVAL;
|
||||
|
||||
/* Check if table is descending or ascending */
|
||||
if (tablesize > 1) {
|
||||
if (pts[0].x < pts[1].x)
|
||||
descending = 0;
|
||||
}
|
||||
|
||||
while (i < tablesize) {
|
||||
if ((descending) && (pts[i].x < input)) {
|
||||
/* table entry is less than measured*/
|
||||
/* value and table is descending, stop */
|
||||
break;
|
||||
} else if ((!descending) &&
|
||||
(pts[i].x > input)) {
|
||||
/* table entry is greater than measured*/
|
||||
/*value and table is ascending, stop */
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
if (i == 0) {
|
||||
*output = pts[0].y;
|
||||
} else if (i == tablesize) {
|
||||
*output = pts[tablesize - 1].y;
|
||||
} else {
|
||||
/* result is between search_index and search_index-1 */
|
||||
/* interpolate linearly */
|
||||
*output = (((s32)((pts[i].y - pts[i - 1].y) *
|
||||
(input - pts[i - 1].x)) /
|
||||
(pts[i].x - pts[i - 1].x)) +
|
||||
pts[i - 1].y);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void vadc_scale_calib(struct vadc_priv *vadc, u16 adc_code,
|
||||
const struct vadc_channel_prop *prop,
|
||||
s64 *scale_voltage)
|
||||
{
|
||||
*scale_voltage = (adc_code -
|
||||
vadc->graph[prop->calibration].gnd);
|
||||
*scale_voltage *= vadc->graph[prop->calibration].dx;
|
||||
*scale_voltage = div64_s64(*scale_voltage,
|
||||
vadc->graph[prop->calibration].dy);
|
||||
if (prop->calibration == VADC_CALIB_ABSOLUTE)
|
||||
*scale_voltage +=
|
||||
vadc->graph[prop->calibration].dx;
|
||||
|
||||
if (*scale_voltage < 0)
|
||||
*scale_voltage = 0;
|
||||
}
|
||||
|
||||
static int vadc_scale_volt(struct vadc_priv *vadc,
|
||||
const struct vadc_channel_prop *prop, u16 adc_code,
|
||||
int *result_uv)
|
||||
{
|
||||
const struct vadc_prescale_ratio *prescale;
|
||||
s64 voltage;
|
||||
s64 voltage = 0, result = 0;
|
||||
|
||||
voltage = adc_code - vadc->graph[prop->calibration].gnd;
|
||||
voltage *= vadc->graph[prop->calibration].dx;
|
||||
voltage = div64_s64(voltage, vadc->graph[prop->calibration].dy);
|
||||
|
||||
if (prop->calibration == VADC_CALIB_ABSOLUTE)
|
||||
voltage += vadc->graph[prop->calibration].dx;
|
||||
|
||||
if (voltage < 0)
|
||||
voltage = 0;
|
||||
vadc_scale_calib(vadc, adc_code, prop, &voltage);
|
||||
|
||||
prescale = &vadc_prescale_ratios[prop->prescale];
|
||||
|
||||
voltage = voltage * prescale->den;
|
||||
result = div64_s64(voltage, prescale->num);
|
||||
*result_uv = result;
|
||||
|
||||
return div64_s64(voltage, prescale->num);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vadc_scale_therm(struct vadc_priv *vadc,
|
||||
const struct vadc_channel_prop *prop, u16 adc_code,
|
||||
int *result_mdec)
|
||||
{
|
||||
s64 voltage = 0, result = 0;
|
||||
|
||||
vadc_scale_calib(vadc, adc_code, prop, &voltage);
|
||||
|
||||
if (prop->calibration == VADC_CALIB_ABSOLUTE)
|
||||
voltage = div64_s64(voltage, 1000);
|
||||
|
||||
vadc_map_voltage_temp(adcmap_100k_104ef_104fb,
|
||||
ARRAY_SIZE(adcmap_100k_104ef_104fb),
|
||||
voltage, &result);
|
||||
result *= 1000;
|
||||
*result_mdec = result;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vadc_scale_die_temp(struct vadc_priv *vadc,
|
||||
const struct vadc_channel_prop *prop,
|
||||
u16 adc_code, int *result_mdec)
|
||||
{
|
||||
const struct vadc_prescale_ratio *prescale;
|
||||
s64 voltage = 0;
|
||||
u64 temp; /* Temporary variable for do_div */
|
||||
|
||||
vadc_scale_calib(vadc, adc_code, prop, &voltage);
|
||||
|
||||
if (voltage > 0) {
|
||||
prescale = &vadc_prescale_ratios[prop->prescale];
|
||||
temp = voltage * prescale->den;
|
||||
do_div(temp, prescale->num * 2);
|
||||
voltage = temp;
|
||||
} else {
|
||||
voltage = 0;
|
||||
}
|
||||
|
||||
voltage -= KELVINMIL_CELSIUSMIL;
|
||||
*result_mdec = voltage;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vadc_scale_chg_temp(struct vadc_priv *vadc,
|
||||
const struct vadc_channel_prop *prop,
|
||||
u16 adc_code, int *result_mdec)
|
||||
{
|
||||
const struct vadc_prescale_ratio *prescale;
|
||||
s64 voltage = 0, result = 0;
|
||||
|
||||
vadc_scale_calib(vadc, adc_code, prop, &voltage);
|
||||
|
||||
prescale = &vadc_prescale_ratios[prop->prescale];
|
||||
voltage = voltage * prescale->den;
|
||||
voltage = div64_s64(voltage, prescale->num);
|
||||
voltage = ((PMI_CHG_SCALE_1) * (voltage * 2));
|
||||
voltage = (voltage + PMI_CHG_SCALE_2);
|
||||
result = div64_s64(voltage, 1000000);
|
||||
*result_mdec = result;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vadc_decimation_from_dt(u32 value)
|
||||
|
@ -536,6 +742,14 @@ static int vadc_avg_samples_from_dt(u32 value)
|
|||
return __ffs64(value);
|
||||
}
|
||||
|
||||
static struct vadc_scale_fn scale_fn[] = {
|
||||
[SCALE_DEFAULT] = {vadc_scale_volt},
|
||||
[SCALE_THERM_100K_PULLUP] = {vadc_scale_therm},
|
||||
[SCALE_PMIC_THERM] = {vadc_scale_die_temp},
|
||||
[SCALE_XOTHERM] = {vadc_scale_therm},
|
||||
[SCALE_PMI_CHG_TEMP] = {vadc_scale_chg_temp},
|
||||
};
|
||||
|
||||
static int vadc_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val, int *val2,
|
||||
long mask)
|
||||
|
@ -552,11 +766,8 @@ static int vadc_read_raw(struct iio_dev *indio_dev,
|
|||
if (ret)
|
||||
break;
|
||||
|
||||
*val = vadc_calibrate(vadc, prop, adc_code);
|
||||
scale_fn[prop->scale_fn].scale(vadc, prop, adc_code, val);
|
||||
|
||||
/* 2mV/K, return milli Celsius */
|
||||
*val /= 2;
|
||||
*val -= KELVINMIL_CELSIUSMIL;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
prop = &vadc->chan_props[chan->address];
|
||||
|
@ -564,12 +775,8 @@ static int vadc_read_raw(struct iio_dev *indio_dev,
|
|||
if (ret)
|
||||
break;
|
||||
|
||||
*val = vadc_calibrate(vadc, prop, adc_code);
|
||||
*val = (int)adc_code;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = 0;
|
||||
*val2 = 1000;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
|
@ -602,22 +809,39 @@ struct vadc_channels {
|
|||
unsigned int prescale_index;
|
||||
enum iio_chan_type type;
|
||||
long info_mask;
|
||||
unsigned int scale_fn;
|
||||
};
|
||||
|
||||
#define VADC_CHAN(_dname, _type, _mask, _pre) \
|
||||
#define VADC_CHAN(_dname, _type, _mask, _pre, _scale) \
|
||||
[VADC_##_dname] = { \
|
||||
.datasheet_name = __stringify(_dname), \
|
||||
.prescale_index = _pre, \
|
||||
.type = _type, \
|
||||
.info_mask = _mask, \
|
||||
.scale_fn = _scale \
|
||||
}, \
|
||||
|
||||
#define VADC_NO_CHAN(_dname, _type, _mask, _pre) \
|
||||
[VADC_##_dname] = { \
|
||||
.datasheet_name = __stringify(_dname), \
|
||||
.prescale_index = _pre, \
|
||||
.type = _type, \
|
||||
.info_mask = _mask \
|
||||
}, \
|
||||
},
|
||||
|
||||
#define VADC_CHAN_TEMP(_dname, _pre) \
|
||||
VADC_CHAN(_dname, IIO_TEMP, BIT(IIO_CHAN_INFO_PROCESSED), _pre) \
|
||||
#define VADC_CHAN_TEMP(_dname, _pre, _scale) \
|
||||
VADC_CHAN(_dname, IIO_TEMP, \
|
||||
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED), \
|
||||
_pre, _scale) \
|
||||
|
||||
#define VADC_CHAN_VOLT(_dname, _pre) \
|
||||
#define VADC_CHAN_VOLT(_dname, _pre, _scale) \
|
||||
VADC_CHAN(_dname, IIO_VOLTAGE, \
|
||||
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), \
|
||||
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED),\
|
||||
_pre, _scale) \
|
||||
|
||||
#define VADC_CHAN_NO_SCALE(_dname, _pre) \
|
||||
VADC_NO_CHAN(_dname, IIO_VOLTAGE, \
|
||||
BIT(IIO_CHAN_INFO_RAW), \
|
||||
_pre) \
|
||||
|
||||
/*
|
||||
|
@ -626,106 +850,106 @@ struct vadc_channels {
|
|||
* gaps in the array should be treated as reserved channels.
|
||||
*/
|
||||
static const struct vadc_channels vadc_chans[] = {
|
||||
VADC_CHAN_VOLT(USBIN, 4)
|
||||
VADC_CHAN_VOLT(DCIN, 4)
|
||||
VADC_CHAN_VOLT(VCHG_SNS, 3)
|
||||
VADC_CHAN_VOLT(SPARE1_03, 1)
|
||||
VADC_CHAN_VOLT(USB_ID_MV, 1)
|
||||
VADC_CHAN_VOLT(VCOIN, 1)
|
||||
VADC_CHAN_VOLT(VBAT_SNS, 1)
|
||||
VADC_CHAN_VOLT(VSYS, 1)
|
||||
VADC_CHAN_TEMP(DIE_TEMP, 0)
|
||||
VADC_CHAN_VOLT(REF_625MV, 0)
|
||||
VADC_CHAN_VOLT(REF_1250MV, 0)
|
||||
VADC_CHAN_VOLT(CHG_TEMP, 0)
|
||||
VADC_CHAN_VOLT(SPARE1, 0)
|
||||
VADC_CHAN_VOLT(SPARE2, 0)
|
||||
VADC_CHAN_VOLT(GND_REF, 0)
|
||||
VADC_CHAN_VOLT(VDD_VADC, 0)
|
||||
VADC_CHAN_VOLT(USBIN, 4, SCALE_DEFAULT)
|
||||
VADC_CHAN_VOLT(DCIN, 4, SCALE_DEFAULT)
|
||||
VADC_CHAN_NO_SCALE(VCHG_SNS, 3)
|
||||
VADC_CHAN_NO_SCALE(SPARE1_03, 1)
|
||||
VADC_CHAN_NO_SCALE(USB_ID_MV, 1)
|
||||
VADC_CHAN_VOLT(VCOIN, 1, SCALE_DEFAULT)
|
||||
VADC_CHAN_NO_SCALE(VBAT_SNS, 1)
|
||||
VADC_CHAN_VOLT(VSYS, 1, SCALE_DEFAULT)
|
||||
VADC_CHAN_TEMP(DIE_TEMP, 0, SCALE_PMIC_THERM)
|
||||
VADC_CHAN_VOLT(REF_625MV, 0, SCALE_DEFAULT)
|
||||
VADC_CHAN_VOLT(REF_1250MV, 0, SCALE_DEFAULT)
|
||||
VADC_CHAN_NO_SCALE(CHG_TEMP, 0)
|
||||
VADC_CHAN_NO_SCALE(SPARE1, 0)
|
||||
VADC_CHAN_TEMP(SPARE2, 0, SCALE_PMI_CHG_TEMP)
|
||||
VADC_CHAN_VOLT(GND_REF, 0, SCALE_DEFAULT)
|
||||
VADC_CHAN_VOLT(VDD_VADC, 0, SCALE_DEFAULT)
|
||||
|
||||
VADC_CHAN_VOLT(P_MUX1_1_1, 0)
|
||||
VADC_CHAN_VOLT(P_MUX2_1_1, 0)
|
||||
VADC_CHAN_VOLT(P_MUX3_1_1, 0)
|
||||
VADC_CHAN_VOLT(P_MUX4_1_1, 0)
|
||||
VADC_CHAN_VOLT(P_MUX5_1_1, 0)
|
||||
VADC_CHAN_VOLT(P_MUX6_1_1, 0)
|
||||
VADC_CHAN_VOLT(P_MUX7_1_1, 0)
|
||||
VADC_CHAN_VOLT(P_MUX8_1_1, 0)
|
||||
VADC_CHAN_VOLT(P_MUX9_1_1, 0)
|
||||
VADC_CHAN_VOLT(P_MUX10_1_1, 0)
|
||||
VADC_CHAN_VOLT(P_MUX11_1_1, 0)
|
||||
VADC_CHAN_VOLT(P_MUX12_1_1, 0)
|
||||
VADC_CHAN_VOLT(P_MUX13_1_1, 0)
|
||||
VADC_CHAN_VOLT(P_MUX14_1_1, 0)
|
||||
VADC_CHAN_VOLT(P_MUX15_1_1, 0)
|
||||
VADC_CHAN_VOLT(P_MUX16_1_1, 0)
|
||||
VADC_CHAN_NO_SCALE(P_MUX1_1_1, 0)
|
||||
VADC_CHAN_NO_SCALE(P_MUX2_1_1, 0)
|
||||
VADC_CHAN_NO_SCALE(P_MUX3_1_1, 0)
|
||||
VADC_CHAN_NO_SCALE(P_MUX4_1_1, 0)
|
||||
VADC_CHAN_NO_SCALE(P_MUX5_1_1, 0)
|
||||
VADC_CHAN_NO_SCALE(P_MUX6_1_1, 0)
|
||||
VADC_CHAN_NO_SCALE(P_MUX7_1_1, 0)
|
||||
VADC_CHAN_NO_SCALE(P_MUX8_1_1, 0)
|
||||
VADC_CHAN_NO_SCALE(P_MUX9_1_1, 0)
|
||||
VADC_CHAN_NO_SCALE(P_MUX10_1_1, 0)
|
||||
VADC_CHAN_NO_SCALE(P_MUX11_1_1, 0)
|
||||
VADC_CHAN_NO_SCALE(P_MUX12_1_1, 0)
|
||||
VADC_CHAN_NO_SCALE(P_MUX13_1_1, 0)
|
||||
VADC_CHAN_NO_SCALE(P_MUX14_1_1, 0)
|
||||
VADC_CHAN_NO_SCALE(P_MUX15_1_1, 0)
|
||||
VADC_CHAN_NO_SCALE(P_MUX16_1_1, 0)
|
||||
|
||||
VADC_CHAN_VOLT(P_MUX1_1_3, 1)
|
||||
VADC_CHAN_VOLT(P_MUX2_1_3, 1)
|
||||
VADC_CHAN_VOLT(P_MUX3_1_3, 1)
|
||||
VADC_CHAN_VOLT(P_MUX4_1_3, 1)
|
||||
VADC_CHAN_VOLT(P_MUX5_1_3, 1)
|
||||
VADC_CHAN_VOLT(P_MUX6_1_3, 1)
|
||||
VADC_CHAN_VOLT(P_MUX7_1_3, 1)
|
||||
VADC_CHAN_VOLT(P_MUX8_1_3, 1)
|
||||
VADC_CHAN_VOLT(P_MUX9_1_3, 1)
|
||||
VADC_CHAN_VOLT(P_MUX10_1_3, 1)
|
||||
VADC_CHAN_VOLT(P_MUX11_1_3, 1)
|
||||
VADC_CHAN_VOLT(P_MUX12_1_3, 1)
|
||||
VADC_CHAN_VOLT(P_MUX13_1_3, 1)
|
||||
VADC_CHAN_VOLT(P_MUX14_1_3, 1)
|
||||
VADC_CHAN_VOLT(P_MUX15_1_3, 1)
|
||||
VADC_CHAN_VOLT(P_MUX16_1_3, 1)
|
||||
VADC_CHAN_NO_SCALE(P_MUX1_1_3, 1)
|
||||
VADC_CHAN_NO_SCALE(P_MUX2_1_3, 1)
|
||||
VADC_CHAN_NO_SCALE(P_MUX3_1_3, 1)
|
||||
VADC_CHAN_NO_SCALE(P_MUX4_1_3, 1)
|
||||
VADC_CHAN_NO_SCALE(P_MUX5_1_3, 1)
|
||||
VADC_CHAN_NO_SCALE(P_MUX6_1_3, 1)
|
||||
VADC_CHAN_NO_SCALE(P_MUX7_1_3, 1)
|
||||
VADC_CHAN_NO_SCALE(P_MUX8_1_3, 1)
|
||||
VADC_CHAN_NO_SCALE(P_MUX9_1_3, 1)
|
||||
VADC_CHAN_NO_SCALE(P_MUX10_1_3, 1)
|
||||
VADC_CHAN_NO_SCALE(P_MUX11_1_3, 1)
|
||||
VADC_CHAN_NO_SCALE(P_MUX12_1_3, 1)
|
||||
VADC_CHAN_NO_SCALE(P_MUX13_1_3, 1)
|
||||
VADC_CHAN_NO_SCALE(P_MUX14_1_3, 1)
|
||||
VADC_CHAN_NO_SCALE(P_MUX15_1_3, 1)
|
||||
VADC_CHAN_NO_SCALE(P_MUX16_1_3, 1)
|
||||
|
||||
VADC_CHAN_VOLT(LR_MUX1_BAT_THERM, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX2_BAT_ID, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX3_XO_THERM, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX4_AMUX_THM1, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX5_AMUX_THM2, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX6_AMUX_THM3, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX7_HW_ID, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX8_AMUX_THM4, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX9_AMUX_THM5, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX10_USB_ID, 0)
|
||||
VADC_CHAN_VOLT(AMUX_PU1, 0)
|
||||
VADC_CHAN_VOLT(AMUX_PU2, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX3_BUF_XO_THERM, 0)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX1_BAT_THERM, 0)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX2_BAT_ID, 0)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX3_XO_THERM, 0)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX4_AMUX_THM1, 0)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX5_AMUX_THM2, 0)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX6_AMUX_THM3, 0)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX7_HW_ID, 0)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX8_AMUX_THM4, 0)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX9_AMUX_THM5, 0)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX10_USB_ID, 0)
|
||||
VADC_CHAN_NO_SCALE(AMUX_PU1, 0)
|
||||
VADC_CHAN_NO_SCALE(AMUX_PU2, 0)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX3_BUF_XO_THERM, 0)
|
||||
|
||||
VADC_CHAN_VOLT(LR_MUX1_PU1_BAT_THERM, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX2_PU1_BAT_ID, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX3_PU1_XO_THERM, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX4_PU1_AMUX_THM1, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX5_PU1_AMUX_THM2, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX6_PU1_AMUX_THM3, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX7_PU1_AMUX_HW_ID, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX8_PU1_AMUX_THM4, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX9_PU1_AMUX_THM5, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX10_PU1_AMUX_USB_ID, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX3_BUF_PU1_XO_THERM, 0)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX1_PU1_BAT_THERM, 0)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX2_PU1_BAT_ID, 0)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX3_PU1_XO_THERM, 0)
|
||||
VADC_CHAN_TEMP(LR_MUX4_PU1_AMUX_THM1, 0, SCALE_THERM_100K_PULLUP)
|
||||
VADC_CHAN_TEMP(LR_MUX5_PU1_AMUX_THM2, 0, SCALE_THERM_100K_PULLUP)
|
||||
VADC_CHAN_TEMP(LR_MUX6_PU1_AMUX_THM3, 0, SCALE_THERM_100K_PULLUP)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX7_PU1_AMUX_HW_ID, 0)
|
||||
VADC_CHAN_TEMP(LR_MUX8_PU1_AMUX_THM4, 0, SCALE_THERM_100K_PULLUP)
|
||||
VADC_CHAN_TEMP(LR_MUX9_PU1_AMUX_THM5, 0, SCALE_THERM_100K_PULLUP)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX10_PU1_AMUX_USB_ID, 0)
|
||||
VADC_CHAN_TEMP(LR_MUX3_BUF_PU1_XO_THERM, 0, SCALE_XOTHERM)
|
||||
|
||||
VADC_CHAN_VOLT(LR_MUX1_PU2_BAT_THERM, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX2_PU2_BAT_ID, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX3_PU2_XO_THERM, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX4_PU2_AMUX_THM1, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX5_PU2_AMUX_THM2, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX6_PU2_AMUX_THM3, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX7_PU2_AMUX_HW_ID, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX8_PU2_AMUX_THM4, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX9_PU2_AMUX_THM5, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX10_PU2_AMUX_USB_ID, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX3_BUF_PU2_XO_THERM, 0)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX1_PU2_BAT_THERM, 0)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX2_PU2_BAT_ID, 0)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX3_PU2_XO_THERM, 0)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX4_PU2_AMUX_THM1, 0)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX5_PU2_AMUX_THM2, 0)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX6_PU2_AMUX_THM3, 0)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX7_PU2_AMUX_HW_ID, 0)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX8_PU2_AMUX_THM4, 0)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX9_PU2_AMUX_THM5, 0)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX10_PU2_AMUX_USB_ID, 0)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX3_BUF_PU2_XO_THERM, 0)
|
||||
|
||||
VADC_CHAN_VOLT(LR_MUX1_PU1_PU2_BAT_THERM, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX2_PU1_PU2_BAT_ID, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX3_PU1_PU2_XO_THERM, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX4_PU1_PU2_AMUX_THM1, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX5_PU1_PU2_AMUX_THM2, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX6_PU1_PU2_AMUX_THM3, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX7_PU1_PU2_AMUX_HW_ID, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX8_PU1_PU2_AMUX_THM4, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX9_PU1_PU2_AMUX_THM5, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX10_PU1_PU2_AMUX_USB_ID, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX3_BUF_PU1_PU2_XO_THERM, 0)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX1_PU1_PU2_BAT_THERM, 0)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX2_PU1_PU2_BAT_ID, 0)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX3_PU1_PU2_XO_THERM, 0)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX4_PU1_PU2_AMUX_THM1, 0)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX5_PU1_PU2_AMUX_THM2, 0)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX6_PU1_PU2_AMUX_THM3, 0)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX7_PU1_PU2_AMUX_HW_ID, 0)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX8_PU1_PU2_AMUX_THM4, 0)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX9_PU1_PU2_AMUX_THM5, 0)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX10_PU1_PU2_AMUX_USB_ID, 0)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX3_BUF_PU1_PU2_XO_THERM, 0)
|
||||
};
|
||||
|
||||
static int vadc_get_dt_channel_data(struct device *dev,
|
||||
|
@ -844,6 +1068,7 @@ static int vadc_get_dt_data(struct vadc_priv *vadc, struct device_node *node)
|
|||
return ret;
|
||||
}
|
||||
|
||||
prop.scale_fn = vadc_chans[prop.channel].scale_fn;
|
||||
vadc->chan_props[index] = prop;
|
||||
|
||||
vadc_chan = &vadc_chans[prop.channel];
|
||||
|
|
|
@ -0,0 +1,631 @@
|
|||
/*
|
||||
* Renesas R-Car GyroADC driver
|
||||
*
|
||||
* Copyright 2016 Marek Vasut <marek.vasut@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
|
||||
#define DRIVER_NAME "rcar-gyroadc"
|
||||
|
||||
/* GyroADC registers. */
|
||||
#define RCAR_GYROADC_MODE_SELECT 0x00
|
||||
#define RCAR_GYROADC_MODE_SELECT_1_MB88101A 0x0
|
||||
#define RCAR_GYROADC_MODE_SELECT_2_ADCS7476 0x1
|
||||
#define RCAR_GYROADC_MODE_SELECT_3_MAX1162 0x3
|
||||
|
||||
#define RCAR_GYROADC_START_STOP 0x04
|
||||
#define RCAR_GYROADC_START_STOP_START BIT(0)
|
||||
|
||||
#define RCAR_GYROADC_CLOCK_LENGTH 0x08
|
||||
#define RCAR_GYROADC_1_25MS_LENGTH 0x0c
|
||||
|
||||
#define RCAR_GYROADC_REALTIME_DATA(ch) (0x10 + ((ch) * 4))
|
||||
#define RCAR_GYROADC_100MS_ADDED_DATA(ch) (0x30 + ((ch) * 4))
|
||||
#define RCAR_GYROADC_10MS_AVG_DATA(ch) (0x50 + ((ch) * 4))
|
||||
|
||||
#define RCAR_GYROADC_FIFO_STATUS 0x70
|
||||
#define RCAR_GYROADC_FIFO_STATUS_EMPTY(ch) BIT(0 + (4 * (ch)))
|
||||
#define RCAR_GYROADC_FIFO_STATUS_FULL(ch) BIT(1 + (4 * (ch)))
|
||||
#define RCAR_GYROADC_FIFO_STATUS_ERROR(ch) BIT(2 + (4 * (ch)))
|
||||
|
||||
#define RCAR_GYROADC_INTR 0x74
|
||||
#define RCAR_GYROADC_INTR_INT BIT(0)
|
||||
|
||||
#define RCAR_GYROADC_INTENR 0x78
|
||||
#define RCAR_GYROADC_INTENR_INTEN BIT(0)
|
||||
|
||||
#define RCAR_GYROADC_SAMPLE_RATE 800 /* Hz */
|
||||
|
||||
#define RCAR_GYROADC_RUNTIME_PM_DELAY_MS 2000
|
||||
|
||||
enum rcar_gyroadc_model {
|
||||
RCAR_GYROADC_MODEL_DEFAULT,
|
||||
RCAR_GYROADC_MODEL_R8A7792,
|
||||
};
|
||||
|
||||
struct rcar_gyroadc {
|
||||
struct device *dev;
|
||||
void __iomem *regs;
|
||||
struct clk *iclk;
|
||||
struct regulator *vref[8];
|
||||
unsigned int num_channels;
|
||||
enum rcar_gyroadc_model model;
|
||||
unsigned int mode;
|
||||
unsigned int sample_width;
|
||||
};
|
||||
|
||||
static void rcar_gyroadc_hw_init(struct rcar_gyroadc *priv)
|
||||
{
|
||||
const unsigned long clk_mhz = clk_get_rate(priv->iclk) / 1000000;
|
||||
const unsigned long clk_mul =
|
||||
(priv->mode == RCAR_GYROADC_MODE_SELECT_1_MB88101A) ? 10 : 5;
|
||||
unsigned long clk_len = clk_mhz * clk_mul;
|
||||
|
||||
/*
|
||||
* According to the R-Car Gen2 datasheet Rev. 1.01, Sept 08 2014,
|
||||
* page 77-7, clock length must be even number. If it's odd number,
|
||||
* add one.
|
||||
*/
|
||||
if (clk_len & 1)
|
||||
clk_len++;
|
||||
|
||||
/* Stop the GyroADC. */
|
||||
writel(0, priv->regs + RCAR_GYROADC_START_STOP);
|
||||
|
||||
/* Disable IRQ on V2H. */
|
||||
if (priv->model == RCAR_GYROADC_MODEL_R8A7792)
|
||||
writel(0, priv->regs + RCAR_GYROADC_INTENR);
|
||||
|
||||
/* Set mode and timing. */
|
||||
writel(priv->mode, priv->regs + RCAR_GYROADC_MODE_SELECT);
|
||||
writel(clk_len, priv->regs + RCAR_GYROADC_CLOCK_LENGTH);
|
||||
writel(clk_mhz * 1250, priv->regs + RCAR_GYROADC_1_25MS_LENGTH);
|
||||
}
|
||||
|
||||
static void rcar_gyroadc_hw_start(struct rcar_gyroadc *priv)
|
||||
{
|
||||
/* Start sampling. */
|
||||
writel(RCAR_GYROADC_START_STOP_START,
|
||||
priv->regs + RCAR_GYROADC_START_STOP);
|
||||
|
||||
/*
|
||||
* Wait for the first conversion to complete. This is longer than
|
||||
* the 1.25 mS in the datasheet because 1.25 mS is not enough for
|
||||
* the hardware to deliver the first sample and the hardware does
|
||||
* then return zeroes instead of valid data.
|
||||
*/
|
||||
mdelay(3);
|
||||
}
|
||||
|
||||
static void rcar_gyroadc_hw_stop(struct rcar_gyroadc *priv)
|
||||
{
|
||||
/* Stop the GyroADC. */
|
||||
writel(0, priv->regs + RCAR_GYROADC_START_STOP);
|
||||
}
|
||||
|
||||
#define RCAR_GYROADC_CHAN(_idx) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = (_idx), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec rcar_gyroadc_iio_channels_1[] = {
|
||||
RCAR_GYROADC_CHAN(0),
|
||||
RCAR_GYROADC_CHAN(1),
|
||||
RCAR_GYROADC_CHAN(2),
|
||||
RCAR_GYROADC_CHAN(3),
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec rcar_gyroadc_iio_channels_2[] = {
|
||||
RCAR_GYROADC_CHAN(0),
|
||||
RCAR_GYROADC_CHAN(1),
|
||||
RCAR_GYROADC_CHAN(2),
|
||||
RCAR_GYROADC_CHAN(3),
|
||||
RCAR_GYROADC_CHAN(4),
|
||||
RCAR_GYROADC_CHAN(5),
|
||||
RCAR_GYROADC_CHAN(6),
|
||||
RCAR_GYROADC_CHAN(7),
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec rcar_gyroadc_iio_channels_3[] = {
|
||||
RCAR_GYROADC_CHAN(0),
|
||||
RCAR_GYROADC_CHAN(1),
|
||||
RCAR_GYROADC_CHAN(2),
|
||||
RCAR_GYROADC_CHAN(3),
|
||||
RCAR_GYROADC_CHAN(4),
|
||||
RCAR_GYROADC_CHAN(5),
|
||||
RCAR_GYROADC_CHAN(6),
|
||||
RCAR_GYROADC_CHAN(7),
|
||||
};
|
||||
|
||||
static int rcar_gyroadc_set_power(struct rcar_gyroadc *priv, bool on)
|
||||
{
|
||||
struct device *dev = priv->dev;
|
||||
int ret;
|
||||
|
||||
if (on) {
|
||||
ret = pm_runtime_get_sync(dev);
|
||||
if (ret < 0)
|
||||
pm_runtime_put_noidle(dev);
|
||||
} else {
|
||||
pm_runtime_mark_last_busy(dev);
|
||||
ret = pm_runtime_put_autosuspend(dev);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rcar_gyroadc_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct rcar_gyroadc *priv = iio_priv(indio_dev);
|
||||
struct regulator *consumer;
|
||||
unsigned int datareg = RCAR_GYROADC_REALTIME_DATA(chan->channel);
|
||||
unsigned int vref;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* MB88101 is special in that it has only single regulator for
|
||||
* all four channels.
|
||||
*/
|
||||
if (priv->mode == RCAR_GYROADC_MODE_SELECT_1_MB88101A)
|
||||
consumer = priv->vref[0];
|
||||
else
|
||||
consumer = priv->vref[chan->channel];
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (chan->type != IIO_VOLTAGE)
|
||||
return -EINVAL;
|
||||
|
||||
/* Channel not connected. */
|
||||
if (!consumer)
|
||||
return -EINVAL;
|
||||
|
||||
ret = iio_device_claim_direct_mode(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = rcar_gyroadc_set_power(priv, true);
|
||||
if (ret < 0) {
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*val = readl(priv->regs + datareg);
|
||||
*val &= BIT(priv->sample_width) - 1;
|
||||
|
||||
ret = rcar_gyroadc_set_power(priv, false);
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
/* Channel not connected. */
|
||||
if (!consumer)
|
||||
return -EINVAL;
|
||||
|
||||
vref = regulator_get_voltage(consumer);
|
||||
*val = vref / 1000;
|
||||
*val2 = 1 << priv->sample_width;
|
||||
|
||||
return IIO_VAL_FRACTIONAL;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
*val = RCAR_GYROADC_SAMPLE_RATE;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int rcar_gyroadc_reg_access(struct iio_dev *indio_dev,
|
||||
unsigned int reg, unsigned int writeval,
|
||||
unsigned int *readval)
|
||||
{
|
||||
struct rcar_gyroadc *priv = iio_priv(indio_dev);
|
||||
unsigned int maxreg = RCAR_GYROADC_FIFO_STATUS;
|
||||
|
||||
if (readval == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
if (reg % 4)
|
||||
return -EINVAL;
|
||||
|
||||
/* Handle the V2H case with extra interrupt block. */
|
||||
if (priv->model == RCAR_GYROADC_MODEL_R8A7792)
|
||||
maxreg = RCAR_GYROADC_INTENR;
|
||||
|
||||
if (reg > maxreg)
|
||||
return -EINVAL;
|
||||
|
||||
*readval = readl(priv->regs + reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct iio_info rcar_gyroadc_iio_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = rcar_gyroadc_read_raw,
|
||||
.debugfs_reg_access = rcar_gyroadc_reg_access,
|
||||
};
|
||||
|
||||
static const struct of_device_id rcar_gyroadc_match[] = {
|
||||
{
|
||||
/* R-Car compatible GyroADC */
|
||||
.compatible = "renesas,rcar-gyroadc",
|
||||
.data = (void *)RCAR_GYROADC_MODEL_DEFAULT,
|
||||
}, {
|
||||
/* R-Car V2H specialty with interrupt registers. */
|
||||
.compatible = "renesas,r8a7792-gyroadc",
|
||||
.data = (void *)RCAR_GYROADC_MODEL_R8A7792,
|
||||
}, {
|
||||
/* sentinel */
|
||||
}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, rcar_gyroadc_match);
|
||||
|
||||
static const struct of_device_id rcar_gyroadc_child_match[] = {
|
||||
/* Mode 1 ADCs */
|
||||
{
|
||||
.compatible = "fujitsu,mb88101a",
|
||||
.data = (void *)RCAR_GYROADC_MODE_SELECT_1_MB88101A,
|
||||
},
|
||||
/* Mode 2 ADCs */
|
||||
{
|
||||
.compatible = "ti,adcs7476",
|
||||
.data = (void *)RCAR_GYROADC_MODE_SELECT_2_ADCS7476,
|
||||
}, {
|
||||
.compatible = "ti,adc121",
|
||||
.data = (void *)RCAR_GYROADC_MODE_SELECT_2_ADCS7476,
|
||||
}, {
|
||||
.compatible = "adi,ad7476",
|
||||
.data = (void *)RCAR_GYROADC_MODE_SELECT_2_ADCS7476,
|
||||
},
|
||||
/* Mode 3 ADCs */
|
||||
{
|
||||
.compatible = "maxim,max1162",
|
||||
.data = (void *)RCAR_GYROADC_MODE_SELECT_3_MAX1162,
|
||||
}, {
|
||||
.compatible = "maxim,max11100",
|
||||
.data = (void *)RCAR_GYROADC_MODE_SELECT_3_MAX1162,
|
||||
},
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
static int rcar_gyroadc_parse_subdevs(struct iio_dev *indio_dev)
|
||||
{
|
||||
const struct of_device_id *of_id;
|
||||
const struct iio_chan_spec *channels;
|
||||
struct rcar_gyroadc *priv = iio_priv(indio_dev);
|
||||
struct device *dev = priv->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct device_node *child;
|
||||
struct regulator *vref;
|
||||
unsigned int reg;
|
||||
unsigned int adcmode, childmode;
|
||||
unsigned int sample_width;
|
||||
unsigned int num_channels;
|
||||
int ret, first = 1;
|
||||
|
||||
for_each_child_of_node(np, child) {
|
||||
of_id = of_match_node(rcar_gyroadc_child_match, child);
|
||||
if (!of_id) {
|
||||
dev_err(dev, "Ignoring unsupported ADC \"%s\".",
|
||||
child->name);
|
||||
continue;
|
||||
}
|
||||
|
||||
childmode = (unsigned int)of_id->data;
|
||||
switch (childmode) {
|
||||
case RCAR_GYROADC_MODE_SELECT_1_MB88101A:
|
||||
sample_width = 12;
|
||||
channels = rcar_gyroadc_iio_channels_1;
|
||||
num_channels = ARRAY_SIZE(rcar_gyroadc_iio_channels_1);
|
||||
break;
|
||||
case RCAR_GYROADC_MODE_SELECT_2_ADCS7476:
|
||||
sample_width = 15;
|
||||
channels = rcar_gyroadc_iio_channels_2;
|
||||
num_channels = ARRAY_SIZE(rcar_gyroadc_iio_channels_2);
|
||||
break;
|
||||
case RCAR_GYROADC_MODE_SELECT_3_MAX1162:
|
||||
sample_width = 16;
|
||||
channels = rcar_gyroadc_iio_channels_3;
|
||||
num_channels = ARRAY_SIZE(rcar_gyroadc_iio_channels_3);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* MB88101 is special in that it's only a single chip taking
|
||||
* up all the CHS lines. Thus, the DT binding is also special
|
||||
* and has no reg property. If we run into such ADC, handle
|
||||
* it here.
|
||||
*/
|
||||
if (childmode == RCAR_GYROADC_MODE_SELECT_1_MB88101A) {
|
||||
reg = 0;
|
||||
} else {
|
||||
ret = of_property_read_u32(child, "reg", ®);
|
||||
if (ret) {
|
||||
dev_err(dev,
|
||||
"Failed to get child reg property of ADC \"%s\".\n",
|
||||
child->name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Channel number is too high. */
|
||||
if (reg >= num_channels) {
|
||||
dev_err(dev,
|
||||
"Only %i channels supported with %s, but reg = <%i>.\n",
|
||||
num_channels, child->name, reg);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/* Child node selected different mode than the rest. */
|
||||
if (!first && (adcmode != childmode)) {
|
||||
dev_err(dev,
|
||||
"Channel %i uses different ADC mode than the rest.\n",
|
||||
reg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Channel is valid, grab the regulator. */
|
||||
dev->of_node = child;
|
||||
vref = devm_regulator_get(dev, "vref");
|
||||
dev->of_node = np;
|
||||
if (IS_ERR(vref)) {
|
||||
dev_dbg(dev, "Channel %i 'vref' supply not connected.\n",
|
||||
reg);
|
||||
return PTR_ERR(vref);
|
||||
}
|
||||
|
||||
priv->vref[reg] = vref;
|
||||
|
||||
if (!first)
|
||||
continue;
|
||||
|
||||
/* First child node which passed sanity tests. */
|
||||
adcmode = childmode;
|
||||
first = 0;
|
||||
|
||||
priv->num_channels = num_channels;
|
||||
priv->mode = childmode;
|
||||
priv->sample_width = sample_width;
|
||||
|
||||
indio_dev->channels = channels;
|
||||
indio_dev->num_channels = num_channels;
|
||||
|
||||
/*
|
||||
* MB88101 is special and we only have one such device
|
||||
* attached to the GyroADC at a time, so if we found it,
|
||||
* we can stop parsing here.
|
||||
*/
|
||||
if (childmode == RCAR_GYROADC_MODE_SELECT_1_MB88101A)
|
||||
break;
|
||||
}
|
||||
|
||||
if (first) {
|
||||
dev_err(dev, "No valid ADC channels found, aborting.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rcar_gyroadc_deinit_supplies(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct rcar_gyroadc *priv = iio_priv(indio_dev);
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < priv->num_channels; i++) {
|
||||
if (!priv->vref[i])
|
||||
continue;
|
||||
|
||||
regulator_disable(priv->vref[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static int rcar_gyroadc_init_supplies(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct rcar_gyroadc *priv = iio_priv(indio_dev);
|
||||
struct device *dev = priv->dev;
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
for (i = 0; i < priv->num_channels; i++) {
|
||||
if (!priv->vref[i])
|
||||
continue;
|
||||
|
||||
ret = regulator_enable(priv->vref[i]);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to enable regulator %i (ret=%i)\n",
|
||||
i, ret);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
rcar_gyroadc_deinit_supplies(indio_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rcar_gyroadc_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct of_device_id *of_id =
|
||||
of_match_device(rcar_gyroadc_match, &pdev->dev);
|
||||
struct device *dev = &pdev->dev;
|
||||
struct rcar_gyroadc *priv;
|
||||
struct iio_dev *indio_dev;
|
||||
struct resource *mem;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(dev, sizeof(*priv));
|
||||
if (!indio_dev) {
|
||||
dev_err(dev, "Failed to allocate IIO device.\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
priv = iio_priv(indio_dev);
|
||||
priv->dev = dev;
|
||||
|
||||
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
priv->regs = devm_ioremap_resource(dev, mem);
|
||||
if (IS_ERR(priv->regs))
|
||||
return PTR_ERR(priv->regs);
|
||||
|
||||
priv->iclk = devm_clk_get(dev, "if");
|
||||
if (IS_ERR(priv->iclk)) {
|
||||
ret = PTR_ERR(priv->iclk);
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(dev, "Failed to get IF clock (ret=%i)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = rcar_gyroadc_parse_subdevs(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = rcar_gyroadc_init_supplies(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
priv->model = (enum rcar_gyroadc_model)of_id->data;
|
||||
|
||||
platform_set_drvdata(pdev, indio_dev);
|
||||
|
||||
indio_dev->name = DRIVER_NAME;
|
||||
indio_dev->dev.parent = dev;
|
||||
indio_dev->dev.of_node = pdev->dev.of_node;
|
||||
indio_dev->info = &rcar_gyroadc_iio_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
ret = clk_prepare_enable(priv->iclk);
|
||||
if (ret) {
|
||||
dev_err(dev, "Could not prepare or enable the IF clock.\n");
|
||||
goto err_clk_if_enable;
|
||||
}
|
||||
|
||||
pm_runtime_set_autosuspend_delay(dev, RCAR_GYROADC_RUNTIME_PM_DELAY_MS);
|
||||
pm_runtime_use_autosuspend(dev);
|
||||
pm_runtime_enable(dev);
|
||||
|
||||
pm_runtime_get_sync(dev);
|
||||
rcar_gyroadc_hw_init(priv);
|
||||
rcar_gyroadc_hw_start(priv);
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret) {
|
||||
dev_err(dev, "Couldn't register IIO device.\n");
|
||||
goto err_iio_device_register;
|
||||
}
|
||||
|
||||
pm_runtime_put_sync(dev);
|
||||
|
||||
return 0;
|
||||
|
||||
err_iio_device_register:
|
||||
rcar_gyroadc_hw_stop(priv);
|
||||
pm_runtime_put_sync(dev);
|
||||
pm_runtime_disable(dev);
|
||||
pm_runtime_set_suspended(dev);
|
||||
clk_disable_unprepare(priv->iclk);
|
||||
err_clk_if_enable:
|
||||
rcar_gyroadc_deinit_supplies(indio_dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rcar_gyroadc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
struct rcar_gyroadc *priv = iio_priv(indio_dev);
|
||||
struct device *dev = priv->dev;
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
pm_runtime_get_sync(dev);
|
||||
rcar_gyroadc_hw_stop(priv);
|
||||
pm_runtime_put_sync(dev);
|
||||
pm_runtime_disable(dev);
|
||||
pm_runtime_set_suspended(dev);
|
||||
clk_disable_unprepare(priv->iclk);
|
||||
rcar_gyroadc_deinit_supplies(indio_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_PM)
|
||||
static int rcar_gyroadc_suspend(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct rcar_gyroadc *priv = iio_priv(indio_dev);
|
||||
|
||||
rcar_gyroadc_hw_stop(priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rcar_gyroadc_resume(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct rcar_gyroadc *priv = iio_priv(indio_dev);
|
||||
|
||||
rcar_gyroadc_hw_start(priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops rcar_gyroadc_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(rcar_gyroadc_suspend, rcar_gyroadc_resume, NULL)
|
||||
};
|
||||
|
||||
static struct platform_driver rcar_gyroadc_driver = {
|
||||
.probe = rcar_gyroadc_probe,
|
||||
.remove = rcar_gyroadc_remove,
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.of_match_table = rcar_gyroadc_match,
|
||||
.pm = &rcar_gyroadc_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(rcar_gyroadc_driver);
|
||||
|
||||
MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>");
|
||||
MODULE_DESCRIPTION("Renesas R-Car GyroADC driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -201,6 +201,7 @@ static int stm32_adc_probe(struct platform_device *pdev)
|
|||
priv->common.base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(priv->common.base))
|
||||
return PTR_ERR(priv->common.base);
|
||||
priv->common.phys_base = res->start;
|
||||
|
||||
priv->vref = devm_regulator_get(&pdev->dev, "vref");
|
||||
if (IS_ERR(priv->vref)) {
|
||||
|
|
|
@ -42,10 +42,12 @@
|
|||
/**
|
||||
* struct stm32_adc_common - stm32 ADC driver common data (for all instances)
|
||||
* @base: control registers base cpu addr
|
||||
* @phys_base: control registers base physical addr
|
||||
* @vref_mv: vref voltage (mv)
|
||||
*/
|
||||
struct stm32_adc_common {
|
||||
void __iomem *base;
|
||||
phys_addr_t phys_base;
|
||||
int vref_mv;
|
||||
};
|
||||
|
||||
|
|
|
@ -21,7 +21,14 @@
|
|||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/timer/stm32-timer-trigger.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
|
@ -58,21 +65,71 @@
|
|||
|
||||
/* STM32F4_ADC_CR2 - bit fields */
|
||||
#define STM32F4_SWSTART BIT(30)
|
||||
#define STM32F4_EXTEN_SHIFT 28
|
||||
#define STM32F4_EXTEN_MASK GENMASK(29, 28)
|
||||
#define STM32F4_EXTSEL_SHIFT 24
|
||||
#define STM32F4_EXTSEL_MASK GENMASK(27, 24)
|
||||
#define STM32F4_EOCS BIT(10)
|
||||
#define STM32F4_DDS BIT(9)
|
||||
#define STM32F4_DMA BIT(8)
|
||||
#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_MAX_SQ 16 /* SQ1..SQ16 */
|
||||
#define STM32_ADC_TIMEOUT_US 100000
|
||||
#define STM32_ADC_TIMEOUT (msecs_to_jiffies(STM32_ADC_TIMEOUT_US / 1000))
|
||||
|
||||
#define STM32_DMA_BUFFER_SIZE PAGE_SIZE
|
||||
|
||||
/* External trigger enable */
|
||||
enum stm32_adc_exten {
|
||||
STM32_EXTEN_SWTRIG,
|
||||
STM32_EXTEN_HWTRIG_RISING_EDGE,
|
||||
STM32_EXTEN_HWTRIG_FALLING_EDGE,
|
||||
STM32_EXTEN_HWTRIG_BOTH_EDGES,
|
||||
};
|
||||
|
||||
/* extsel - trigger mux selection value */
|
||||
enum stm32_adc_extsel {
|
||||
STM32_EXT0,
|
||||
STM32_EXT1,
|
||||
STM32_EXT2,
|
||||
STM32_EXT3,
|
||||
STM32_EXT4,
|
||||
STM32_EXT5,
|
||||
STM32_EXT6,
|
||||
STM32_EXT7,
|
||||
STM32_EXT8,
|
||||
STM32_EXT9,
|
||||
STM32_EXT10,
|
||||
STM32_EXT11,
|
||||
STM32_EXT12,
|
||||
STM32_EXT13,
|
||||
STM32_EXT14,
|
||||
STM32_EXT15,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct stm32_adc_trig_info - ADC trigger info
|
||||
* @name: name of the trigger, corresponding to its source
|
||||
* @extsel: trigger selection
|
||||
*/
|
||||
struct stm32_adc_trig_info {
|
||||
const char *name;
|
||||
enum stm32_adc_extsel extsel;
|
||||
};
|
||||
|
||||
/**
|
||||
* stm32_adc_regs - stm32 ADC misc registers & bitfield desc
|
||||
* @reg: register offset
|
||||
* @mask: bitfield mask
|
||||
* @shift: left shift
|
||||
*/
|
||||
struct stm32_adc_regs {
|
||||
int reg;
|
||||
int mask;
|
||||
int shift;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct stm32_adc - private data of each ADC IIO instance
|
||||
* @common: reference to ADC block common data
|
||||
|
@ -82,15 +139,29 @@
|
|||
* @clk: clock for this adc instance
|
||||
* @irq: interrupt for this adc instance
|
||||
* @lock: spinlock
|
||||
* @bufi: data buffer index
|
||||
* @num_conv: expected number of scan conversions
|
||||
* @trigger_polarity: external trigger polarity (e.g. exten)
|
||||
* @dma_chan: dma channel
|
||||
* @rx_buf: dma rx buffer cpu address
|
||||
* @rx_dma_buf: dma rx buffer bus address
|
||||
* @rx_buf_sz: dma rx buffer size
|
||||
*/
|
||||
struct stm32_adc {
|
||||
struct stm32_adc_common *common;
|
||||
u32 offset;
|
||||
struct completion completion;
|
||||
u16 *buffer;
|
||||
u16 buffer[STM32_ADC_MAX_SQ];
|
||||
struct clk *clk;
|
||||
int irq;
|
||||
spinlock_t lock; /* interrupt lock */
|
||||
unsigned int bufi;
|
||||
unsigned int num_conv;
|
||||
u32 trigger_polarity;
|
||||
struct dma_chan *dma_chan;
|
||||
u8 *rx_buf;
|
||||
dma_addr_t rx_dma_buf;
|
||||
unsigned int rx_buf_sz;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -125,6 +196,53 @@ static const struct stm32_adc_chan_spec stm32f4_adc123_channels[] = {
|
|||
{ IIO_VOLTAGE, 15, "in15" },
|
||||
};
|
||||
|
||||
/**
|
||||
* stm32f4_sq - describe regular sequence registers
|
||||
* - L: sequence len (register & bit field)
|
||||
* - SQ1..SQ16: sequence entries (register & bit field)
|
||||
*/
|
||||
static const struct stm32_adc_regs stm32f4_sq[STM32_ADC_MAX_SQ + 1] = {
|
||||
/* L: len bit field description to be kept as first element */
|
||||
{ STM32F4_ADC_SQR1, GENMASK(23, 20), 20 },
|
||||
/* SQ1..SQ16 registers & bit fields (reg, mask, shift) */
|
||||
{ STM32F4_ADC_SQR3, GENMASK(4, 0), 0 },
|
||||
{ STM32F4_ADC_SQR3, GENMASK(9, 5), 5 },
|
||||
{ STM32F4_ADC_SQR3, GENMASK(14, 10), 10 },
|
||||
{ STM32F4_ADC_SQR3, GENMASK(19, 15), 15 },
|
||||
{ STM32F4_ADC_SQR3, GENMASK(24, 20), 20 },
|
||||
{ STM32F4_ADC_SQR3, GENMASK(29, 25), 25 },
|
||||
{ STM32F4_ADC_SQR2, GENMASK(4, 0), 0 },
|
||||
{ STM32F4_ADC_SQR2, GENMASK(9, 5), 5 },
|
||||
{ STM32F4_ADC_SQR2, GENMASK(14, 10), 10 },
|
||||
{ STM32F4_ADC_SQR2, GENMASK(19, 15), 15 },
|
||||
{ STM32F4_ADC_SQR2, GENMASK(24, 20), 20 },
|
||||
{ STM32F4_ADC_SQR2, GENMASK(29, 25), 25 },
|
||||
{ STM32F4_ADC_SQR1, GENMASK(4, 0), 0 },
|
||||
{ STM32F4_ADC_SQR1, GENMASK(9, 5), 5 },
|
||||
{ STM32F4_ADC_SQR1, GENMASK(14, 10), 10 },
|
||||
{ STM32F4_ADC_SQR1, GENMASK(19, 15), 15 },
|
||||
};
|
||||
|
||||
/* STM32F4 external trigger sources for all instances */
|
||||
static struct stm32_adc_trig_info stm32f4_adc_trigs[] = {
|
||||
{ TIM1_CH1, STM32_EXT0 },
|
||||
{ TIM1_CH2, STM32_EXT1 },
|
||||
{ TIM1_CH3, STM32_EXT2 },
|
||||
{ TIM2_CH2, STM32_EXT3 },
|
||||
{ TIM2_CH3, STM32_EXT4 },
|
||||
{ TIM2_CH4, STM32_EXT5 },
|
||||
{ TIM2_TRGO, STM32_EXT6 },
|
||||
{ TIM3_CH1, STM32_EXT7 },
|
||||
{ TIM3_TRGO, STM32_EXT8 },
|
||||
{ TIM4_CH4, STM32_EXT9 },
|
||||
{ TIM5_CH1, STM32_EXT10 },
|
||||
{ TIM5_CH2, STM32_EXT11 },
|
||||
{ TIM5_CH3, STM32_EXT12 },
|
||||
{ TIM8_CH1, STM32_EXT13 },
|
||||
{ TIM8_TRGO, STM32_EXT14 },
|
||||
{}, /* sentinel */
|
||||
};
|
||||
|
||||
/**
|
||||
* STM32 ADC registers access routines
|
||||
* @adc: stm32 adc instance
|
||||
|
@ -187,10 +305,21 @@ static void stm32_adc_conv_irq_disable(struct stm32_adc *adc)
|
|||
/**
|
||||
* stm32_adc_start_conv() - Start conversions for regular channels.
|
||||
* @adc: stm32 adc instance
|
||||
* @dma: use dma to transfer conversion result
|
||||
*
|
||||
* Start conversions for regular channels.
|
||||
* Also take care of normal or DMA mode. Circular DMA may be used for regular
|
||||
* conversions, in IIO buffer modes. Otherwise, use ADC interrupt with direct
|
||||
* DR read instead (e.g. read_raw, or triggered buffer mode without DMA).
|
||||
*/
|
||||
static void stm32_adc_start_conv(struct stm32_adc *adc)
|
||||
static void stm32_adc_start_conv(struct stm32_adc *adc, bool dma)
|
||||
{
|
||||
stm32_adc_set_bits(adc, STM32F4_ADC_CR1, STM32F4_SCAN);
|
||||
|
||||
if (dma)
|
||||
stm32_adc_set_bits(adc, STM32F4_ADC_CR2,
|
||||
STM32F4_DMA | STM32F4_DDS);
|
||||
|
||||
stm32_adc_set_bits(adc, STM32F4_ADC_CR2, STM32F4_EOCS | STM32F4_ADON);
|
||||
|
||||
/* Wait for Power-up time (tSTAB from datasheet) */
|
||||
|
@ -207,9 +336,152 @@ static void stm32_adc_stop_conv(struct stm32_adc *adc)
|
|||
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_clr_bits(adc, STM32F4_ADC_CR2,
|
||||
STM32F4_ADON | STM32F4_DMA | STM32F4_DDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* stm32_adc_conf_scan_seq() - Build regular channels scan sequence
|
||||
* @indio_dev: IIO device
|
||||
* @scan_mask: channels to be converted
|
||||
*
|
||||
* Conversion sequence :
|
||||
* Configure ADC scan sequence based on selected channels in scan_mask.
|
||||
* Add channels to SQR registers, from scan_mask LSB to MSB, then
|
||||
* program sequence len.
|
||||
*/
|
||||
static int stm32_adc_conf_scan_seq(struct iio_dev *indio_dev,
|
||||
const unsigned long *scan_mask)
|
||||
{
|
||||
struct stm32_adc *adc = iio_priv(indio_dev);
|
||||
const struct iio_chan_spec *chan;
|
||||
u32 val, bit;
|
||||
int i = 0;
|
||||
|
||||
for_each_set_bit(bit, scan_mask, indio_dev->masklength) {
|
||||
chan = indio_dev->channels + bit;
|
||||
/*
|
||||
* Assign one channel per SQ entry in regular
|
||||
* sequence, starting with SQ1.
|
||||
*/
|
||||
i++;
|
||||
if (i > STM32_ADC_MAX_SQ)
|
||||
return -EINVAL;
|
||||
|
||||
dev_dbg(&indio_dev->dev, "%s chan %d to SQ%d\n",
|
||||
__func__, chan->channel, i);
|
||||
|
||||
val = stm32_adc_readl(adc, stm32f4_sq[i].reg);
|
||||
val &= ~stm32f4_sq[i].mask;
|
||||
val |= chan->channel << stm32f4_sq[i].shift;
|
||||
stm32_adc_writel(adc, stm32f4_sq[i].reg, val);
|
||||
}
|
||||
|
||||
if (!i)
|
||||
return -EINVAL;
|
||||
|
||||
/* Sequence len */
|
||||
val = stm32_adc_readl(adc, stm32f4_sq[0].reg);
|
||||
val &= ~stm32f4_sq[0].mask;
|
||||
val |= ((i - 1) << stm32f4_sq[0].shift);
|
||||
stm32_adc_writel(adc, stm32f4_sq[0].reg, val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* stm32_adc_get_trig_extsel() - Get external trigger selection
|
||||
* @trig: trigger
|
||||
*
|
||||
* Returns trigger extsel value, if trig matches, -EINVAL otherwise.
|
||||
*/
|
||||
static int stm32_adc_get_trig_extsel(struct iio_trigger *trig)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* lookup triggers registered by stm32 timer trigger driver */
|
||||
for (i = 0; stm32f4_adc_trigs[i].name; i++) {
|
||||
/**
|
||||
* Checking both stm32 timer trigger type and trig name
|
||||
* should be safe against arbitrary trigger names.
|
||||
*/
|
||||
if (is_stm32_timer_trigger(trig) &&
|
||||
!strcmp(stm32f4_adc_trigs[i].name, trig->name)) {
|
||||
return stm32f4_adc_trigs[i].extsel;
|
||||
}
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* stm32_adc_set_trig() - Set a regular trigger
|
||||
* @indio_dev: IIO device
|
||||
* @trig: IIO trigger
|
||||
*
|
||||
* Set trigger source/polarity (e.g. SW, or HW with polarity) :
|
||||
* - if HW trigger disabled (e.g. trig == NULL, conversion launched by sw)
|
||||
* - if HW trigger enabled, set source & polarity
|
||||
*/
|
||||
static int stm32_adc_set_trig(struct iio_dev *indio_dev,
|
||||
struct iio_trigger *trig)
|
||||
{
|
||||
struct stm32_adc *adc = iio_priv(indio_dev);
|
||||
u32 val, extsel = 0, exten = STM32_EXTEN_SWTRIG;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
if (trig) {
|
||||
ret = stm32_adc_get_trig_extsel(trig);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* set trigger source and polarity (default to rising edge) */
|
||||
extsel = ret;
|
||||
exten = adc->trigger_polarity + STM32_EXTEN_HWTRIG_RISING_EDGE;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&adc->lock, flags);
|
||||
val = stm32_adc_readl(adc, STM32F4_ADC_CR2);
|
||||
val &= ~(STM32F4_EXTEN_MASK | STM32F4_EXTSEL_MASK);
|
||||
val |= exten << STM32F4_EXTEN_SHIFT;
|
||||
val |= extsel << STM32F4_EXTSEL_SHIFT;
|
||||
stm32_adc_writel(adc, STM32F4_ADC_CR2, val);
|
||||
spin_unlock_irqrestore(&adc->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stm32_adc_set_trig_pol(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
unsigned int type)
|
||||
{
|
||||
struct stm32_adc *adc = iio_priv(indio_dev);
|
||||
|
||||
adc->trigger_polarity = type;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stm32_adc_get_trig_pol(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan)
|
||||
{
|
||||
struct stm32_adc *adc = iio_priv(indio_dev);
|
||||
|
||||
return adc->trigger_polarity;
|
||||
}
|
||||
|
||||
static const char * const stm32_trig_pol_items[] = {
|
||||
"rising-edge", "falling-edge", "both-edges",
|
||||
};
|
||||
|
||||
static const struct iio_enum stm32_adc_trig_pol = {
|
||||
.items = stm32_trig_pol_items,
|
||||
.num_items = ARRAY_SIZE(stm32_trig_pol_items),
|
||||
.get = stm32_adc_get_trig_pol,
|
||||
.set = stm32_adc_set_trig_pol,
|
||||
};
|
||||
|
||||
/**
|
||||
* stm32_adc_single_conv() - Performs a single conversion
|
||||
* @indio_dev: IIO device
|
||||
|
@ -228,28 +500,27 @@ static int stm32_adc_single_conv(struct iio_dev *indio_dev,
|
|||
struct stm32_adc *adc = iio_priv(indio_dev);
|
||||
long timeout;
|
||||
u32 val;
|
||||
u16 result;
|
||||
int ret;
|
||||
|
||||
reinit_completion(&adc->completion);
|
||||
|
||||
adc->buffer = &result;
|
||||
adc->bufi = 0;
|
||||
|
||||
/* 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);
|
||||
/* Program chan number in regular sequence (SQ1) */
|
||||
val = stm32_adc_readl(adc, stm32f4_sq[1].reg);
|
||||
val &= ~stm32f4_sq[1].mask;
|
||||
val |= chan->channel << stm32f4_sq[1].shift;
|
||||
stm32_adc_writel(adc, stm32f4_sq[1].reg, val);
|
||||
|
||||
/* Set regular sequence len (0 for 1 conversion) */
|
||||
stm32_adc_clr_bits(adc, STM32F4_ADC_SQR1, STM32F4_L_MASK);
|
||||
stm32_adc_clr_bits(adc, stm32f4_sq[0].reg, stm32f4_sq[0].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);
|
||||
stm32_adc_start_conv(adc, false);
|
||||
|
||||
timeout = wait_for_completion_interruptible_timeout(
|
||||
&adc->completion, STM32_ADC_TIMEOUT);
|
||||
|
@ -258,7 +529,7 @@ static int stm32_adc_single_conv(struct iio_dev *indio_dev,
|
|||
} else if (timeout < 0) {
|
||||
ret = timeout;
|
||||
} else {
|
||||
*res = result;
|
||||
*res = adc->buffer[0];
|
||||
ret = IIO_VAL_INT;
|
||||
}
|
||||
|
||||
|
@ -301,17 +572,73 @@ static int stm32_adc_read_raw(struct iio_dev *indio_dev,
|
|||
static irqreturn_t stm32_adc_isr(int irq, void *data)
|
||||
{
|
||||
struct stm32_adc *adc = data;
|
||||
struct iio_dev *indio_dev = iio_priv_to_dev(adc);
|
||||
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);
|
||||
/* Reading DR also clears EOC status flag */
|
||||
adc->buffer[adc->bufi] = stm32_adc_readw(adc, STM32F4_ADC_DR);
|
||||
if (iio_buffer_enabled(indio_dev)) {
|
||||
adc->bufi++;
|
||||
if (adc->bufi >= adc->num_conv) {
|
||||
stm32_adc_conv_irq_disable(adc);
|
||||
iio_trigger_poll(indio_dev->trig);
|
||||
}
|
||||
} else {
|
||||
complete(&adc->completion);
|
||||
}
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* stm32_adc_validate_trigger() - validate trigger for stm32 adc
|
||||
* @indio_dev: IIO device
|
||||
* @trig: new trigger
|
||||
*
|
||||
* Returns: 0 if trig matches one of the triggers registered by stm32 adc
|
||||
* driver, -EINVAL otherwise.
|
||||
*/
|
||||
static int stm32_adc_validate_trigger(struct iio_dev *indio_dev,
|
||||
struct iio_trigger *trig)
|
||||
{
|
||||
return stm32_adc_get_trig_extsel(trig) < 0 ? -EINVAL : 0;
|
||||
}
|
||||
|
||||
static int stm32_adc_set_watermark(struct iio_dev *indio_dev, unsigned int val)
|
||||
{
|
||||
struct stm32_adc *adc = iio_priv(indio_dev);
|
||||
unsigned int watermark = STM32_DMA_BUFFER_SIZE / 2;
|
||||
|
||||
/*
|
||||
* dma cyclic transfers are used, buffer is split into two periods.
|
||||
* There should be :
|
||||
* - always one buffer (period) dma is working on
|
||||
* - one buffer (period) driver can push with iio_trigger_poll().
|
||||
*/
|
||||
watermark = min(watermark, val * (unsigned)(sizeof(u16)));
|
||||
adc->rx_buf_sz = watermark * 2;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stm32_adc_update_scan_mode(struct iio_dev *indio_dev,
|
||||
const unsigned long *scan_mask)
|
||||
{
|
||||
struct stm32_adc *adc = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
adc->num_conv = bitmap_weight(scan_mask, indio_dev->masklength);
|
||||
|
||||
ret = stm32_adc_conf_scan_seq(indio_dev, scan_mask);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stm32_adc_of_xlate(struct iio_dev *indio_dev,
|
||||
const struct of_phandle_args *iiospec)
|
||||
{
|
||||
|
@ -350,11 +677,199 @@ static int stm32_adc_debugfs_reg_access(struct iio_dev *indio_dev,
|
|||
|
||||
static const struct iio_info stm32_adc_iio_info = {
|
||||
.read_raw = stm32_adc_read_raw,
|
||||
.validate_trigger = stm32_adc_validate_trigger,
|
||||
.hwfifo_set_watermark = stm32_adc_set_watermark,
|
||||
.update_scan_mode = stm32_adc_update_scan_mode,
|
||||
.debugfs_reg_access = stm32_adc_debugfs_reg_access,
|
||||
.of_xlate = stm32_adc_of_xlate,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static unsigned int stm32_adc_dma_residue(struct stm32_adc *adc)
|
||||
{
|
||||
struct dma_tx_state state;
|
||||
enum dma_status status;
|
||||
|
||||
status = dmaengine_tx_status(adc->dma_chan,
|
||||
adc->dma_chan->cookie,
|
||||
&state);
|
||||
if (status == DMA_IN_PROGRESS) {
|
||||
/* Residue is size in bytes from end of buffer */
|
||||
unsigned int i = adc->rx_buf_sz - state.residue;
|
||||
unsigned int size;
|
||||
|
||||
/* Return available bytes */
|
||||
if (i >= adc->bufi)
|
||||
size = i - adc->bufi;
|
||||
else
|
||||
size = adc->rx_buf_sz + i - adc->bufi;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void stm32_adc_dma_buffer_done(void *data)
|
||||
{
|
||||
struct iio_dev *indio_dev = data;
|
||||
|
||||
iio_trigger_poll_chained(indio_dev->trig);
|
||||
}
|
||||
|
||||
static int stm32_adc_dma_start(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct stm32_adc *adc = iio_priv(indio_dev);
|
||||
struct dma_async_tx_descriptor *desc;
|
||||
dma_cookie_t cookie;
|
||||
int ret;
|
||||
|
||||
if (!adc->dma_chan)
|
||||
return 0;
|
||||
|
||||
dev_dbg(&indio_dev->dev, "%s size=%d watermark=%d\n", __func__,
|
||||
adc->rx_buf_sz, adc->rx_buf_sz / 2);
|
||||
|
||||
/* Prepare a DMA cyclic transaction */
|
||||
desc = dmaengine_prep_dma_cyclic(adc->dma_chan,
|
||||
adc->rx_dma_buf,
|
||||
adc->rx_buf_sz, adc->rx_buf_sz / 2,
|
||||
DMA_DEV_TO_MEM,
|
||||
DMA_PREP_INTERRUPT);
|
||||
if (!desc)
|
||||
return -EBUSY;
|
||||
|
||||
desc->callback = stm32_adc_dma_buffer_done;
|
||||
desc->callback_param = indio_dev;
|
||||
|
||||
cookie = dmaengine_submit(desc);
|
||||
ret = dma_submit_error(cookie);
|
||||
if (ret) {
|
||||
dmaengine_terminate_all(adc->dma_chan);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Issue pending DMA requests */
|
||||
dma_async_issue_pending(adc->dma_chan);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stm32_adc_buffer_postenable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct stm32_adc *adc = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = stm32_adc_set_trig(indio_dev, indio_dev->trig);
|
||||
if (ret) {
|
||||
dev_err(&indio_dev->dev, "Can't set trigger\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = stm32_adc_dma_start(indio_dev);
|
||||
if (ret) {
|
||||
dev_err(&indio_dev->dev, "Can't start dma\n");
|
||||
goto err_clr_trig;
|
||||
}
|
||||
|
||||
ret = iio_triggered_buffer_postenable(indio_dev);
|
||||
if (ret < 0)
|
||||
goto err_stop_dma;
|
||||
|
||||
/* Reset adc buffer index */
|
||||
adc->bufi = 0;
|
||||
|
||||
if (!adc->dma_chan)
|
||||
stm32_adc_conv_irq_enable(adc);
|
||||
|
||||
stm32_adc_start_conv(adc, !!adc->dma_chan);
|
||||
|
||||
return 0;
|
||||
|
||||
err_stop_dma:
|
||||
if (adc->dma_chan)
|
||||
dmaengine_terminate_all(adc->dma_chan);
|
||||
err_clr_trig:
|
||||
stm32_adc_set_trig(indio_dev, NULL);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int stm32_adc_buffer_predisable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct stm32_adc *adc = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
stm32_adc_stop_conv(adc);
|
||||
if (!adc->dma_chan)
|
||||
stm32_adc_conv_irq_disable(adc);
|
||||
|
||||
ret = iio_triggered_buffer_predisable(indio_dev);
|
||||
if (ret < 0)
|
||||
dev_err(&indio_dev->dev, "predisable failed\n");
|
||||
|
||||
if (adc->dma_chan)
|
||||
dmaengine_terminate_all(adc->dma_chan);
|
||||
|
||||
if (stm32_adc_set_trig(indio_dev, NULL))
|
||||
dev_err(&indio_dev->dev, "Can't clear trigger\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct iio_buffer_setup_ops stm32_adc_buffer_setup_ops = {
|
||||
.postenable = &stm32_adc_buffer_postenable,
|
||||
.predisable = &stm32_adc_buffer_predisable,
|
||||
};
|
||||
|
||||
static irqreturn_t stm32_adc_trigger_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct stm32_adc *adc = iio_priv(indio_dev);
|
||||
|
||||
dev_dbg(&indio_dev->dev, "%s bufi=%d\n", __func__, adc->bufi);
|
||||
|
||||
if (!adc->dma_chan) {
|
||||
/* reset buffer index */
|
||||
adc->bufi = 0;
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, adc->buffer,
|
||||
pf->timestamp);
|
||||
} else {
|
||||
int residue = stm32_adc_dma_residue(adc);
|
||||
|
||||
while (residue >= indio_dev->scan_bytes) {
|
||||
u16 *buffer = (u16 *)&adc->rx_buf[adc->bufi];
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, buffer,
|
||||
pf->timestamp);
|
||||
residue -= indio_dev->scan_bytes;
|
||||
adc->bufi += indio_dev->scan_bytes;
|
||||
if (adc->bufi >= adc->rx_buf_sz)
|
||||
adc->bufi = 0;
|
||||
}
|
||||
}
|
||||
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
/* re-enable eoc irq */
|
||||
if (!adc->dma_chan)
|
||||
stm32_adc_conv_irq_enable(adc);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec_ext_info stm32_adc_ext_info[] = {
|
||||
IIO_ENUM("trigger_polarity", IIO_SHARED_BY_ALL, &stm32_adc_trig_pol),
|
||||
{
|
||||
.name = "trigger_polarity_available",
|
||||
.shared = IIO_SHARED_BY_ALL,
|
||||
.read = iio_enum_available_read,
|
||||
.private = (uintptr_t)&stm32_adc_trig_pol,
|
||||
},
|
||||
{},
|
||||
};
|
||||
|
||||
static void stm32_adc_chan_init_one(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec *chan,
|
||||
const struct stm32_adc_chan_spec *channel,
|
||||
|
@ -370,6 +885,7 @@ static void stm32_adc_chan_init_one(struct iio_dev *indio_dev,
|
|||
chan->scan_type.sign = 'u';
|
||||
chan->scan_type.realbits = 12;
|
||||
chan->scan_type.storagebits = 16;
|
||||
chan->ext_info = stm32_adc_ext_info;
|
||||
}
|
||||
|
||||
static int stm32_adc_chan_of_init(struct iio_dev *indio_dev)
|
||||
|
@ -410,6 +926,45 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int stm32_adc_dma_request(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct stm32_adc *adc = iio_priv(indio_dev);
|
||||
struct dma_slave_config config;
|
||||
int ret;
|
||||
|
||||
adc->dma_chan = dma_request_slave_channel(&indio_dev->dev, "rx");
|
||||
if (!adc->dma_chan)
|
||||
return 0;
|
||||
|
||||
adc->rx_buf = dma_alloc_coherent(adc->dma_chan->device->dev,
|
||||
STM32_DMA_BUFFER_SIZE,
|
||||
&adc->rx_dma_buf, GFP_KERNEL);
|
||||
if (!adc->rx_buf) {
|
||||
ret = -ENOMEM;
|
||||
goto err_release;
|
||||
}
|
||||
|
||||
/* Configure DMA channel to read data register */
|
||||
memset(&config, 0, sizeof(config));
|
||||
config.src_addr = (dma_addr_t)adc->common->phys_base;
|
||||
config.src_addr += adc->offset + STM32F4_ADC_DR;
|
||||
config.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
|
||||
|
||||
ret = dmaengine_slave_config(adc->dma_chan, &config);
|
||||
if (ret)
|
||||
goto err_free;
|
||||
|
||||
return 0;
|
||||
|
||||
err_free:
|
||||
dma_free_coherent(adc->dma_chan->device->dev, STM32_DMA_BUFFER_SIZE,
|
||||
adc->rx_buf, adc->rx_dma_buf);
|
||||
err_release:
|
||||
dma_release_channel(adc->dma_chan);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int stm32_adc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
|
@ -471,14 +1026,37 @@ static int stm32_adc_probe(struct platform_device *pdev)
|
|||
if (ret < 0)
|
||||
goto err_clk_disable;
|
||||
|
||||
ret = stm32_adc_dma_request(indio_dev);
|
||||
if (ret < 0)
|
||||
goto err_clk_disable;
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev,
|
||||
&iio_pollfunc_store_time,
|
||||
&stm32_adc_trigger_handler,
|
||||
&stm32_adc_buffer_setup_ops);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "buffer setup failed\n");
|
||||
goto err_dma_disable;
|
||||
}
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "iio dev register failed\n");
|
||||
goto err_clk_disable;
|
||||
goto err_buffer_cleanup;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_buffer_cleanup:
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
|
||||
err_dma_disable:
|
||||
if (adc->dma_chan) {
|
||||
dma_free_coherent(adc->dma_chan->device->dev,
|
||||
STM32_DMA_BUFFER_SIZE,
|
||||
adc->rx_buf, adc->rx_dma_buf);
|
||||
dma_release_channel(adc->dma_chan);
|
||||
}
|
||||
err_clk_disable:
|
||||
clk_disable_unprepare(adc->clk);
|
||||
|
||||
|
@ -491,6 +1069,13 @@ static int stm32_adc_remove(struct platform_device *pdev)
|
|||
struct iio_dev *indio_dev = iio_priv_to_dev(adc);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
if (adc->dma_chan) {
|
||||
dma_free_coherent(adc->dma_chan->device->dev,
|
||||
STM32_DMA_BUFFER_SIZE,
|
||||
adc->rx_buf, adc->rx_dma_buf);
|
||||
dma_release_channel(adc->dma_chan);
|
||||
}
|
||||
clk_disable_unprepare(adc->clk);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -76,16 +76,6 @@ struct stx104_gpio {
|
|||
unsigned int out_state;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct stx104_dev - STX104 device private data structure
|
||||
* @indio_dev: IIO device
|
||||
* @chip: instance of the gpio_chip
|
||||
*/
|
||||
struct stx104_dev {
|
||||
struct iio_dev *indio_dev;
|
||||
struct gpio_chip *chip;
|
||||
};
|
||||
|
||||
static int stx104_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val, int *val2, long mask)
|
||||
{
|
||||
|
@ -266,12 +256,38 @@ static void stx104_gpio_set(struct gpio_chip *chip, unsigned int offset,
|
|||
spin_unlock_irqrestore(&stx104gpio->lock, flags);
|
||||
}
|
||||
|
||||
#define STX104_NGPIO 8
|
||||
static const char *stx104_names[STX104_NGPIO] = {
|
||||
"DIN0", "DIN1", "DIN2", "DIN3", "DOUT0", "DOUT1", "DOUT2", "DOUT3"
|
||||
};
|
||||
|
||||
static void stx104_gpio_set_multiple(struct gpio_chip *chip,
|
||||
unsigned long *mask, unsigned long *bits)
|
||||
{
|
||||
struct stx104_gpio *const stx104gpio = gpiochip_get_data(chip);
|
||||
unsigned long flags;
|
||||
|
||||
/* verify masked GPIO are output */
|
||||
if (!(*mask & 0xF0))
|
||||
return;
|
||||
|
||||
*mask >>= 4;
|
||||
*bits >>= 4;
|
||||
|
||||
spin_lock_irqsave(&stx104gpio->lock, flags);
|
||||
|
||||
stx104gpio->out_state &= ~*mask;
|
||||
stx104gpio->out_state |= *mask & *bits;
|
||||
outb(stx104gpio->out_state, stx104gpio->base);
|
||||
|
||||
spin_unlock_irqrestore(&stx104gpio->lock, flags);
|
||||
}
|
||||
|
||||
static int stx104_probe(struct device *dev, unsigned int id)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct stx104_iio *priv;
|
||||
struct stx104_gpio *stx104gpio;
|
||||
struct stx104_dev *stx104dev;
|
||||
int err;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(dev, sizeof(*priv));
|
||||
|
@ -282,10 +298,6 @@ static int stx104_probe(struct device *dev, unsigned int id)
|
|||
if (!stx104gpio)
|
||||
return -ENOMEM;
|
||||
|
||||
stx104dev = devm_kzalloc(dev, sizeof(*stx104dev), GFP_KERNEL);
|
||||
if (!stx104dev)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!devm_request_region(dev, base[id], STX104_EXTENT,
|
||||
dev_name(dev))) {
|
||||
dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n",
|
||||
|
@ -324,45 +336,26 @@ static int stx104_probe(struct device *dev, unsigned int id)
|
|||
stx104gpio->chip.parent = dev;
|
||||
stx104gpio->chip.owner = THIS_MODULE;
|
||||
stx104gpio->chip.base = -1;
|
||||
stx104gpio->chip.ngpio = 8;
|
||||
stx104gpio->chip.ngpio = STX104_NGPIO;
|
||||
stx104gpio->chip.names = stx104_names;
|
||||
stx104gpio->chip.get_direction = stx104_gpio_get_direction;
|
||||
stx104gpio->chip.direction_input = stx104_gpio_direction_input;
|
||||
stx104gpio->chip.direction_output = stx104_gpio_direction_output;
|
||||
stx104gpio->chip.get = stx104_gpio_get;
|
||||
stx104gpio->chip.set = stx104_gpio_set;
|
||||
stx104gpio->chip.set_multiple = stx104_gpio_set_multiple;
|
||||
stx104gpio->base = base[id] + 3;
|
||||
stx104gpio->out_state = 0x0;
|
||||
|
||||
spin_lock_init(&stx104gpio->lock);
|
||||
|
||||
stx104dev->indio_dev = indio_dev;
|
||||
stx104dev->chip = &stx104gpio->chip;
|
||||
dev_set_drvdata(dev, stx104dev);
|
||||
|
||||
err = gpiochip_add_data(&stx104gpio->chip, stx104gpio);
|
||||
err = devm_gpiochip_add_data(dev, &stx104gpio->chip, stx104gpio);
|
||||
if (err) {
|
||||
dev_err(dev, "GPIO registering failed (%d)\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = iio_device_register(indio_dev);
|
||||
if (err) {
|
||||
dev_err(dev, "IIO device registering failed (%d)\n", err);
|
||||
gpiochip_remove(&stx104gpio->chip);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stx104_remove(struct device *dev, unsigned int id)
|
||||
{
|
||||
struct stx104_dev *const stx104dev = dev_get_drvdata(dev);
|
||||
|
||||
iio_device_unregister(stx104dev->indio_dev);
|
||||
gpiochip_remove(stx104dev->chip);
|
||||
|
||||
return 0;
|
||||
return devm_iio_device_register(dev, indio_dev);
|
||||
}
|
||||
|
||||
static struct isa_driver stx104_driver = {
|
||||
|
@ -370,7 +363,6 @@ static struct isa_driver stx104_driver = {
|
|||
.driver = {
|
||||
.name = "stx104"
|
||||
},
|
||||
.remove = stx104_remove
|
||||
};
|
||||
|
||||
module_isa_driver(stx104_driver, num_stx104);
|
||||
|
|
|
@ -472,14 +472,14 @@ static const struct attribute_group ads1115_attribute_group = {
|
|||
.attrs = ads1115_attributes,
|
||||
};
|
||||
|
||||
static struct iio_info ads1015_info = {
|
||||
static const struct iio_info ads1015_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = ads1015_read_raw,
|
||||
.write_raw = ads1015_write_raw,
|
||||
.attrs = &ads1015_attribute_group,
|
||||
};
|
||||
|
||||
static struct iio_info ads1115_info = {
|
||||
static const struct iio_info ads1115_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = ads1015_read_raw,
|
||||
.write_raw = ads1015_write_raw,
|
||||
|
|
|
@ -0,0 +1,490 @@
|
|||
/*
|
||||
* Texas Instruments ADS7950 SPI ADC driver
|
||||
*
|
||||
* Copyright 2016 David Lechner <david@lechnology.com>
|
||||
*
|
||||
* Based on iio/ad7923.c:
|
||||
* Copyright 2011 Analog Devices Inc
|
||||
* Copyright 2012 CS Systemes d'Information
|
||||
*
|
||||
* And also on hwmon/ads79xx.c
|
||||
* Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/
|
||||
* Nishanth Menon
|
||||
*
|
||||
* 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 version 2.
|
||||
*
|
||||
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
|
||||
* kind, whether express or implied; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
|
||||
#define TI_ADS7950_CR_MANUAL BIT(12)
|
||||
#define TI_ADS7950_CR_WRITE BIT(11)
|
||||
#define TI_ADS7950_CR_CHAN(ch) ((ch) << 7)
|
||||
#define TI_ADS7950_CR_RANGE_5V BIT(6)
|
||||
|
||||
#define TI_ADS7950_MAX_CHAN 16
|
||||
|
||||
#define TI_ADS7950_TIMESTAMP_SIZE (sizeof(int64_t) / sizeof(__be16))
|
||||
|
||||
/* val = value, dec = left shift, bits = number of bits of the mask */
|
||||
#define TI_ADS7950_EXTRACT(val, dec, bits) \
|
||||
(((val) >> (dec)) & ((1 << (bits)) - 1))
|
||||
|
||||
struct ti_ads7950_state {
|
||||
struct spi_device *spi;
|
||||
struct spi_transfer ring_xfer[TI_ADS7950_MAX_CHAN + 2];
|
||||
struct spi_transfer scan_single_xfer[3];
|
||||
struct spi_message ring_msg;
|
||||
struct spi_message scan_single_msg;
|
||||
|
||||
struct regulator *reg;
|
||||
|
||||
unsigned int settings;
|
||||
|
||||
/*
|
||||
* DMA (thus cache coherency maintenance) requires the
|
||||
* transfer buffers to live in their own cache lines.
|
||||
*/
|
||||
__be16 rx_buf[TI_ADS7950_MAX_CHAN + TI_ADS7950_TIMESTAMP_SIZE]
|
||||
____cacheline_aligned;
|
||||
__be16 tx_buf[TI_ADS7950_MAX_CHAN];
|
||||
};
|
||||
|
||||
struct ti_ads7950_chip_info {
|
||||
const struct iio_chan_spec *channels;
|
||||
unsigned int num_channels;
|
||||
};
|
||||
|
||||
enum ti_ads7950_id {
|
||||
TI_ADS7950,
|
||||
TI_ADS7951,
|
||||
TI_ADS7952,
|
||||
TI_ADS7953,
|
||||
TI_ADS7954,
|
||||
TI_ADS7955,
|
||||
TI_ADS7956,
|
||||
TI_ADS7957,
|
||||
TI_ADS7958,
|
||||
TI_ADS7959,
|
||||
TI_ADS7960,
|
||||
TI_ADS7961,
|
||||
};
|
||||
|
||||
#define TI_ADS7950_V_CHAN(index, bits) \
|
||||
{ \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = index, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.address = index, \
|
||||
.datasheet_name = "CH##index", \
|
||||
.scan_index = index, \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = bits, \
|
||||
.storagebits = 16, \
|
||||
.shift = 12 - (bits), \
|
||||
.endianness = IIO_BE, \
|
||||
}, \
|
||||
}
|
||||
|
||||
#define DECLARE_TI_ADS7950_4_CHANNELS(name, bits) \
|
||||
const struct iio_chan_spec name ## _channels[] = { \
|
||||
TI_ADS7950_V_CHAN(0, bits), \
|
||||
TI_ADS7950_V_CHAN(1, bits), \
|
||||
TI_ADS7950_V_CHAN(2, bits), \
|
||||
TI_ADS7950_V_CHAN(3, bits), \
|
||||
IIO_CHAN_SOFT_TIMESTAMP(4), \
|
||||
}
|
||||
|
||||
#define DECLARE_TI_ADS7950_8_CHANNELS(name, bits) \
|
||||
const struct iio_chan_spec name ## _channels[] = { \
|
||||
TI_ADS7950_V_CHAN(0, bits), \
|
||||
TI_ADS7950_V_CHAN(1, bits), \
|
||||
TI_ADS7950_V_CHAN(2, bits), \
|
||||
TI_ADS7950_V_CHAN(3, bits), \
|
||||
TI_ADS7950_V_CHAN(4, bits), \
|
||||
TI_ADS7950_V_CHAN(5, bits), \
|
||||
TI_ADS7950_V_CHAN(6, bits), \
|
||||
TI_ADS7950_V_CHAN(7, bits), \
|
||||
IIO_CHAN_SOFT_TIMESTAMP(8), \
|
||||
}
|
||||
|
||||
#define DECLARE_TI_ADS7950_12_CHANNELS(name, bits) \
|
||||
const struct iio_chan_spec name ## _channels[] = { \
|
||||
TI_ADS7950_V_CHAN(0, bits), \
|
||||
TI_ADS7950_V_CHAN(1, bits), \
|
||||
TI_ADS7950_V_CHAN(2, bits), \
|
||||
TI_ADS7950_V_CHAN(3, bits), \
|
||||
TI_ADS7950_V_CHAN(4, bits), \
|
||||
TI_ADS7950_V_CHAN(5, bits), \
|
||||
TI_ADS7950_V_CHAN(6, bits), \
|
||||
TI_ADS7950_V_CHAN(7, bits), \
|
||||
TI_ADS7950_V_CHAN(8, bits), \
|
||||
TI_ADS7950_V_CHAN(9, bits), \
|
||||
TI_ADS7950_V_CHAN(10, bits), \
|
||||
TI_ADS7950_V_CHAN(11, bits), \
|
||||
IIO_CHAN_SOFT_TIMESTAMP(12), \
|
||||
}
|
||||
|
||||
#define DECLARE_TI_ADS7950_16_CHANNELS(name, bits) \
|
||||
const struct iio_chan_spec name ## _channels[] = { \
|
||||
TI_ADS7950_V_CHAN(0, bits), \
|
||||
TI_ADS7950_V_CHAN(1, bits), \
|
||||
TI_ADS7950_V_CHAN(2, bits), \
|
||||
TI_ADS7950_V_CHAN(3, bits), \
|
||||
TI_ADS7950_V_CHAN(4, bits), \
|
||||
TI_ADS7950_V_CHAN(5, bits), \
|
||||
TI_ADS7950_V_CHAN(6, bits), \
|
||||
TI_ADS7950_V_CHAN(7, bits), \
|
||||
TI_ADS7950_V_CHAN(8, bits), \
|
||||
TI_ADS7950_V_CHAN(9, bits), \
|
||||
TI_ADS7950_V_CHAN(10, bits), \
|
||||
TI_ADS7950_V_CHAN(11, bits), \
|
||||
TI_ADS7950_V_CHAN(12, bits), \
|
||||
TI_ADS7950_V_CHAN(13, bits), \
|
||||
TI_ADS7950_V_CHAN(14, bits), \
|
||||
TI_ADS7950_V_CHAN(15, bits), \
|
||||
IIO_CHAN_SOFT_TIMESTAMP(16), \
|
||||
}
|
||||
|
||||
static DECLARE_TI_ADS7950_4_CHANNELS(ti_ads7950, 12);
|
||||
static DECLARE_TI_ADS7950_8_CHANNELS(ti_ads7951, 12);
|
||||
static DECLARE_TI_ADS7950_12_CHANNELS(ti_ads7952, 12);
|
||||
static DECLARE_TI_ADS7950_16_CHANNELS(ti_ads7953, 12);
|
||||
static DECLARE_TI_ADS7950_4_CHANNELS(ti_ads7954, 10);
|
||||
static DECLARE_TI_ADS7950_8_CHANNELS(ti_ads7955, 10);
|
||||
static DECLARE_TI_ADS7950_12_CHANNELS(ti_ads7956, 10);
|
||||
static DECLARE_TI_ADS7950_16_CHANNELS(ti_ads7957, 10);
|
||||
static DECLARE_TI_ADS7950_4_CHANNELS(ti_ads7958, 8);
|
||||
static DECLARE_TI_ADS7950_8_CHANNELS(ti_ads7959, 8);
|
||||
static DECLARE_TI_ADS7950_12_CHANNELS(ti_ads7960, 8);
|
||||
static DECLARE_TI_ADS7950_16_CHANNELS(ti_ads7961, 8);
|
||||
|
||||
static const struct ti_ads7950_chip_info ti_ads7950_chip_info[] = {
|
||||
[TI_ADS7950] = {
|
||||
.channels = ti_ads7950_channels,
|
||||
.num_channels = ARRAY_SIZE(ti_ads7950_channels),
|
||||
},
|
||||
[TI_ADS7951] = {
|
||||
.channels = ti_ads7951_channels,
|
||||
.num_channels = ARRAY_SIZE(ti_ads7951_channels),
|
||||
},
|
||||
[TI_ADS7952] = {
|
||||
.channels = ti_ads7952_channels,
|
||||
.num_channels = ARRAY_SIZE(ti_ads7952_channels),
|
||||
},
|
||||
[TI_ADS7953] = {
|
||||
.channels = ti_ads7953_channels,
|
||||
.num_channels = ARRAY_SIZE(ti_ads7953_channels),
|
||||
},
|
||||
[TI_ADS7954] = {
|
||||
.channels = ti_ads7954_channels,
|
||||
.num_channels = ARRAY_SIZE(ti_ads7954_channels),
|
||||
},
|
||||
[TI_ADS7955] = {
|
||||
.channels = ti_ads7955_channels,
|
||||
.num_channels = ARRAY_SIZE(ti_ads7955_channels),
|
||||
},
|
||||
[TI_ADS7956] = {
|
||||
.channels = ti_ads7956_channels,
|
||||
.num_channels = ARRAY_SIZE(ti_ads7956_channels),
|
||||
},
|
||||
[TI_ADS7957] = {
|
||||
.channels = ti_ads7957_channels,
|
||||
.num_channels = ARRAY_SIZE(ti_ads7957_channels),
|
||||
},
|
||||
[TI_ADS7958] = {
|
||||
.channels = ti_ads7958_channels,
|
||||
.num_channels = ARRAY_SIZE(ti_ads7958_channels),
|
||||
},
|
||||
[TI_ADS7959] = {
|
||||
.channels = ti_ads7959_channels,
|
||||
.num_channels = ARRAY_SIZE(ti_ads7959_channels),
|
||||
},
|
||||
[TI_ADS7960] = {
|
||||
.channels = ti_ads7960_channels,
|
||||
.num_channels = ARRAY_SIZE(ti_ads7960_channels),
|
||||
},
|
||||
[TI_ADS7961] = {
|
||||
.channels = ti_ads7961_channels,
|
||||
.num_channels = ARRAY_SIZE(ti_ads7961_channels),
|
||||
},
|
||||
};
|
||||
|
||||
/*
|
||||
* ti_ads7950_update_scan_mode() setup the spi transfer buffer for the new
|
||||
* scan mask
|
||||
*/
|
||||
static int ti_ads7950_update_scan_mode(struct iio_dev *indio_dev,
|
||||
const unsigned long *active_scan_mask)
|
||||
{
|
||||
struct ti_ads7950_state *st = iio_priv(indio_dev);
|
||||
int i, cmd, len;
|
||||
|
||||
len = 0;
|
||||
for_each_set_bit(i, active_scan_mask, indio_dev->num_channels) {
|
||||
cmd = TI_ADS7950_CR_WRITE | TI_ADS7950_CR_CHAN(i) | st->settings;
|
||||
st->tx_buf[len++] = cpu_to_be16(cmd);
|
||||
}
|
||||
|
||||
/* Data for the 1st channel is not returned until the 3rd transfer */
|
||||
len += 2;
|
||||
for (i = 0; i < len; i++) {
|
||||
if ((i + 2) < len)
|
||||
st->ring_xfer[i].tx_buf = &st->tx_buf[i];
|
||||
if (i >= 2)
|
||||
st->ring_xfer[i].rx_buf = &st->rx_buf[i - 2];
|
||||
st->ring_xfer[i].len = 2;
|
||||
st->ring_xfer[i].cs_change = 1;
|
||||
}
|
||||
/* make sure last transfer's cs_change is not set */
|
||||
st->ring_xfer[len - 1].cs_change = 0;
|
||||
|
||||
spi_message_init_with_transfers(&st->ring_msg, st->ring_xfer, len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t ti_ads7950_trigger_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct ti_ads7950_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = spi_sync(st->spi, &st->ring_msg);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, st->rx_buf,
|
||||
iio_get_time_ns(indio_dev));
|
||||
|
||||
out:
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int ti_ads7950_scan_direct(struct ti_ads7950_state *st, unsigned int ch)
|
||||
{
|
||||
int ret, cmd;
|
||||
|
||||
cmd = TI_ADS7950_CR_WRITE | TI_ADS7950_CR_CHAN(ch) | st->settings;
|
||||
st->tx_buf[0] = cpu_to_be16(cmd);
|
||||
|
||||
ret = spi_sync(st->spi, &st->scan_single_msg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return be16_to_cpu(st->rx_buf[0]);
|
||||
}
|
||||
|
||||
static int ti_ads7950_get_range(struct ti_ads7950_state *st)
|
||||
{
|
||||
int vref;
|
||||
|
||||
vref = regulator_get_voltage(st->reg);
|
||||
if (vref < 0)
|
||||
return vref;
|
||||
|
||||
vref /= 1000;
|
||||
|
||||
if (st->settings & TI_ADS7950_CR_RANGE_5V)
|
||||
vref *= 2;
|
||||
|
||||
return vref;
|
||||
}
|
||||
|
||||
static int ti_ads7950_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long m)
|
||||
{
|
||||
struct ti_ads7950_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (m) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
|
||||
ret = iio_device_claim_direct_mode(indio_dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = ti_ads7950_scan_direct(st, chan->address);
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (chan->address != TI_ADS7950_EXTRACT(ret, 12, 4))
|
||||
return -EIO;
|
||||
|
||||
*val = TI_ADS7950_EXTRACT(ret, chan->scan_type.shift,
|
||||
chan->scan_type.realbits);
|
||||
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
ret = ti_ads7950_get_range(st);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = ret;
|
||||
*val2 = (1 << chan->scan_type.realbits) - 1;
|
||||
|
||||
return IIO_VAL_FRACTIONAL;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct iio_info ti_ads7950_info = {
|
||||
.read_raw = &ti_ads7950_read_raw,
|
||||
.update_scan_mode = ti_ads7950_update_scan_mode,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int ti_ads7950_probe(struct spi_device *spi)
|
||||
{
|
||||
struct ti_ads7950_state *st;
|
||||
struct iio_dev *indio_dev;
|
||||
const struct ti_ads7950_chip_info *info;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
|
||||
st->spi = spi;
|
||||
st->settings = TI_ADS7950_CR_MANUAL | TI_ADS7950_CR_RANGE_5V;
|
||||
|
||||
info = &ti_ads7950_chip_info[spi_get_device_id(spi)->driver_data];
|
||||
|
||||
indio_dev->name = spi_get_device_id(spi)->name;
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = info->channels;
|
||||
indio_dev->num_channels = info->num_channels;
|
||||
indio_dev->info = &ti_ads7950_info;
|
||||
|
||||
/*
|
||||
* Setup default message. The sample is read at the end of the first
|
||||
* transfer, then it takes one full cycle to convert the sample and one
|
||||
* more cycle to send the value. The conversion process is driven by
|
||||
* the SPI clock, which is why we have 3 transfers. The middle one is
|
||||
* just dummy data sent while the chip is converting the sample that
|
||||
* was read at the end of the first transfer.
|
||||
*/
|
||||
|
||||
st->scan_single_xfer[0].tx_buf = &st->tx_buf[0];
|
||||
st->scan_single_xfer[0].len = 2;
|
||||
st->scan_single_xfer[0].cs_change = 1;
|
||||
st->scan_single_xfer[1].tx_buf = &st->tx_buf[0];
|
||||
st->scan_single_xfer[1].len = 2;
|
||||
st->scan_single_xfer[1].cs_change = 1;
|
||||
st->scan_single_xfer[2].rx_buf = &st->rx_buf[0];
|
||||
st->scan_single_xfer[2].len = 2;
|
||||
|
||||
spi_message_init_with_transfers(&st->scan_single_msg,
|
||||
st->scan_single_xfer, 3);
|
||||
|
||||
st->reg = devm_regulator_get(&spi->dev, "vref");
|
||||
if (IS_ERR(st->reg)) {
|
||||
dev_err(&spi->dev, "Failed get get regulator \"vref\"\n");
|
||||
return PTR_ERR(st->reg);
|
||||
}
|
||||
|
||||
ret = regulator_enable(st->reg);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev, "Failed to enable regulator \"vref\"\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, NULL,
|
||||
&ti_ads7950_trigger_handler, NULL);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev, "Failed to setup triggered buffer\n");
|
||||
goto error_disable_reg;
|
||||
}
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev, "Failed to register iio device\n");
|
||||
goto error_cleanup_ring;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error_cleanup_ring:
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
error_disable_reg:
|
||||
regulator_disable(st->reg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ti_ads7950_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct ti_ads7950_state *st = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
regulator_disable(st->reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id ti_ads7950_id[] = {
|
||||
{ "ads7950", TI_ADS7950 },
|
||||
{ "ads7951", TI_ADS7951 },
|
||||
{ "ads7952", TI_ADS7952 },
|
||||
{ "ads7953", TI_ADS7953 },
|
||||
{ "ads7954", TI_ADS7954 },
|
||||
{ "ads7955", TI_ADS7955 },
|
||||
{ "ads7956", TI_ADS7956 },
|
||||
{ "ads7957", TI_ADS7957 },
|
||||
{ "ads7958", TI_ADS7958 },
|
||||
{ "ads7959", TI_ADS7959 },
|
||||
{ "ads7960", TI_ADS7960 },
|
||||
{ "ads7961", TI_ADS7961 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ti_ads7950_id);
|
||||
|
||||
static struct spi_driver ti_ads7950_driver = {
|
||||
.driver = {
|
||||
.name = "ads7950",
|
||||
},
|
||||
.probe = ti_ads7950_probe,
|
||||
.remove = ti_ads7950_remove,
|
||||
.id_table = ti_ads7950_id,
|
||||
};
|
||||
module_spi_driver(ti_ads7950_driver);
|
||||
|
||||
MODULE_AUTHOR("David Lechner <david@lechnology.com>");
|
||||
MODULE_DESCRIPTION("TI TI_ADS7950 ADC");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -0,0 +1,271 @@
|
|||
/*
|
||||
* TI tlc4541 ADC Driver
|
||||
*
|
||||
* Copyright (C) 2017 Phil Reid
|
||||
*
|
||||
* Datasheets can be found here:
|
||||
* http://www.ti.com/lit/gpn/tlc3541
|
||||
* http://www.ti.com/lit/gpn/tlc4541
|
||||
*
|
||||
* 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 tlc4541 requires 24 clock cycles to start a transfer.
|
||||
* Conversion then takes 2.94us to complete before data is ready
|
||||
* Data is returned MSB first.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/interrupt.h>
|
||||
#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>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/sysfs.h>
|
||||
|
||||
struct tlc4541_state {
|
||||
struct spi_device *spi;
|
||||
struct regulator *reg;
|
||||
struct spi_transfer scan_single_xfer[3];
|
||||
struct spi_message scan_single_msg;
|
||||
|
||||
/*
|
||||
* DMA (thus cache coherency maintenance) requires the
|
||||
* transfer buffers to live in their own cache lines.
|
||||
* 2 bytes data + 6 bytes padding + 8 bytes timestamp when
|
||||
* call iio_push_to_buffers_with_timestamp.
|
||||
*/
|
||||
__be16 rx_buf[8] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
struct tlc4541_chip_info {
|
||||
const struct iio_chan_spec *channels;
|
||||
unsigned int num_channels;
|
||||
};
|
||||
|
||||
enum tlc4541_id {
|
||||
TLC3541,
|
||||
TLC4541,
|
||||
};
|
||||
|
||||
#define TLC4541_V_CHAN(bits, bitshift) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = (bits), \
|
||||
.storagebits = 16, \
|
||||
.shift = (bitshift), \
|
||||
.endianness = IIO_BE, \
|
||||
}, \
|
||||
}
|
||||
|
||||
#define DECLARE_TLC4541_CHANNELS(name, bits, bitshift) \
|
||||
const struct iio_chan_spec name ## _channels[] = { \
|
||||
TLC4541_V_CHAN(bits, bitshift), \
|
||||
IIO_CHAN_SOFT_TIMESTAMP(1), \
|
||||
}
|
||||
|
||||
static DECLARE_TLC4541_CHANNELS(tlc3541, 14, 2);
|
||||
static DECLARE_TLC4541_CHANNELS(tlc4541, 16, 0);
|
||||
|
||||
static const struct tlc4541_chip_info tlc4541_chip_info[] = {
|
||||
[TLC3541] = {
|
||||
.channels = tlc3541_channels,
|
||||
.num_channels = ARRAY_SIZE(tlc3541_channels),
|
||||
},
|
||||
[TLC4541] = {
|
||||
.channels = tlc4541_channels,
|
||||
.num_channels = ARRAY_SIZE(tlc4541_channels),
|
||||
},
|
||||
};
|
||||
|
||||
static irqreturn_t tlc4541_trigger_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct tlc4541_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = spi_sync(st->spi, &st->scan_single_msg);
|
||||
if (ret < 0)
|
||||
goto done;
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, st->rx_buf,
|
||||
iio_get_time_ns(indio_dev));
|
||||
|
||||
done:
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int tlc4541_get_range(struct tlc4541_state *st)
|
||||
{
|
||||
int vref;
|
||||
|
||||
vref = regulator_get_voltage(st->reg);
|
||||
if (vref < 0)
|
||||
return vref;
|
||||
|
||||
vref /= 1000;
|
||||
|
||||
return vref;
|
||||
}
|
||||
|
||||
static int tlc4541_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val,
|
||||
int *val2,
|
||||
long m)
|
||||
{
|
||||
int ret = 0;
|
||||
struct tlc4541_state *st = iio_priv(indio_dev);
|
||||
|
||||
switch (m) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = iio_device_claim_direct_mode(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = spi_sync(st->spi, &st->scan_single_msg);
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = be16_to_cpu(st->rx_buf[0]);
|
||||
*val = *val >> chan->scan_type.shift;
|
||||
*val &= GENMASK(chan->scan_type.realbits - 1, 0);
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
ret = tlc4541_get_range(st);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = ret;
|
||||
*val2 = chan->scan_type.realbits;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct iio_info tlc4541_info = {
|
||||
.read_raw = &tlc4541_read_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int tlc4541_probe(struct spi_device *spi)
|
||||
{
|
||||
struct tlc4541_state *st;
|
||||
struct iio_dev *indio_dev;
|
||||
const struct tlc4541_chip_info *info;
|
||||
int ret;
|
||||
int8_t device_init = 0;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
||||
if (indio_dev == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
|
||||
st->spi = spi;
|
||||
|
||||
info = &tlc4541_chip_info[spi_get_device_id(spi)->driver_data];
|
||||
|
||||
indio_dev->name = spi_get_device_id(spi)->name;
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = info->channels;
|
||||
indio_dev->num_channels = info->num_channels;
|
||||
indio_dev->info = &tlc4541_info;
|
||||
|
||||
/* perform reset */
|
||||
spi_write(spi, &device_init, 1);
|
||||
|
||||
/* Setup default message */
|
||||
st->scan_single_xfer[0].rx_buf = &st->rx_buf[0];
|
||||
st->scan_single_xfer[0].len = 3;
|
||||
st->scan_single_xfer[1].delay_usecs = 3;
|
||||
st->scan_single_xfer[2].rx_buf = &st->rx_buf[0];
|
||||
st->scan_single_xfer[2].len = 2;
|
||||
|
||||
spi_message_init_with_transfers(&st->scan_single_msg,
|
||||
st->scan_single_xfer, 3);
|
||||
|
||||
st->reg = devm_regulator_get(&spi->dev, "vref");
|
||||
if (IS_ERR(st->reg))
|
||||
return PTR_ERR(st->reg);
|
||||
|
||||
ret = regulator_enable(st->reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, NULL,
|
||||
&tlc4541_trigger_handler, NULL);
|
||||
if (ret)
|
||||
goto error_disable_reg;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto error_cleanup_buffer;
|
||||
|
||||
return 0;
|
||||
|
||||
error_cleanup_buffer:
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
error_disable_reg:
|
||||
regulator_disable(st->reg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tlc4541_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct tlc4541_state *st = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
regulator_disable(st->reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id tlc4541_dt_ids[] = {
|
||||
{ .compatible = "ti,tlc3541", },
|
||||
{ .compatible = "ti,tlc4541", },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, tlc4541_dt_ids);
|
||||
#endif
|
||||
|
||||
static const struct spi_device_id tlc4541_id[] = {
|
||||
{"tlc3541", TLC3541},
|
||||
{"tlc4541", TLC4541},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, tlc4541_id);
|
||||
|
||||
static struct spi_driver tlc4541_driver = {
|
||||
.driver = {
|
||||
.name = "tlc4541",
|
||||
.of_match_table = of_match_ptr(tlc4541_dt_ids),
|
||||
},
|
||||
.probe = tlc4541_probe,
|
||||
.remove = tlc4541_remove,
|
||||
.id_table = tlc4541_id,
|
||||
};
|
||||
module_spi_driver(tlc4541_driver);
|
||||
|
||||
MODULE_AUTHOR("Phil Reid <preid@electromag.com.au>");
|
||||
MODULE_DESCRIPTION("Texas Instruments TLC4541 ADC");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -10,7 +10,8 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/buffer_impl.h>
|
||||
#include <linux/iio/consumer.h>
|
||||
|
||||
struct iio_cb_buffer {
|
||||
|
|
|
@ -5,7 +5,10 @@
|
|||
#include <linux/workqueue.h>
|
||||
#include <linux/kfifo.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/kfifo_buf.h>
|
||||
#include <linux/iio/buffer_impl.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/poll.h>
|
||||
|
||||
|
|
|
@ -58,6 +58,10 @@ static struct {
|
|||
|
||||
{HID_USAGE_SENSOR_PRESSURE, 0, 100, 0},
|
||||
{HID_USAGE_SENSOR_PRESSURE, HID_USAGE_SENSOR_UNITS_PASCAL, 0, 1000000},
|
||||
|
||||
{HID_USAGE_SENSOR_TIME_TIMESTAMP, 0, 1000000000, 0},
|
||||
{HID_USAGE_SENSOR_TIME_TIMESTAMP, HID_USAGE_SENSOR_UNITS_MILLISECOND,
|
||||
1000000, 0},
|
||||
};
|
||||
|
||||
static int pow_10(unsigned power)
|
||||
|
@ -346,6 +350,13 @@ int hid_sensor_format_scale(u32 usage_id,
|
|||
}
|
||||
EXPORT_SYMBOL(hid_sensor_format_scale);
|
||||
|
||||
int64_t hid_sensor_convert_timestamp(struct hid_sensor_common *st,
|
||||
int64_t raw_value)
|
||||
{
|
||||
return st->timestamp_ns_scale * raw_value;
|
||||
}
|
||||
EXPORT_SYMBOL(hid_sensor_convert_timestamp);
|
||||
|
||||
static
|
||||
int hid_sensor_get_reporting_interval(struct hid_sensor_hub_device *hsdev,
|
||||
u32 usage_id,
|
||||
|
@ -367,6 +378,7 @@ int hid_sensor_parse_common_attributes(struct hid_sensor_hub_device *hsdev,
|
|||
struct hid_sensor_common *st)
|
||||
{
|
||||
|
||||
struct hid_sensor_hub_attribute_info timestamp;
|
||||
|
||||
hid_sensor_get_reporting_interval(hsdev, usage_id, st);
|
||||
|
||||
|
@ -385,11 +397,25 @@ int hid_sensor_parse_common_attributes(struct hid_sensor_hub_device *hsdev,
|
|||
HID_USAGE_SENSOR_PROP_SENSITIVITY_ABS,
|
||||
&st->sensitivity);
|
||||
|
||||
hid_dbg(hsdev->hdev, "common attributes: %x:%x, %x:%x, %x:%x %x:%x\n",
|
||||
st->poll.index, st->poll.report_id,
|
||||
st->report_state.index, st->report_state.report_id,
|
||||
st->power_state.index, st->power_state.report_id,
|
||||
st->sensitivity.index, st->sensitivity.report_id);
|
||||
sensor_hub_input_get_attribute_info(hsdev,
|
||||
HID_INPUT_REPORT, usage_id,
|
||||
HID_USAGE_SENSOR_TIME_TIMESTAMP,
|
||||
×tamp);
|
||||
if (timestamp.index >= 0 && timestamp.report_id) {
|
||||
int val0, val1;
|
||||
|
||||
hid_sensor_format_scale(HID_USAGE_SENSOR_TIME_TIMESTAMP,
|
||||
×tamp, &val0, &val1);
|
||||
st->timestamp_ns_scale = val0;
|
||||
} else
|
||||
st->timestamp_ns_scale = 1000000000;
|
||||
|
||||
hid_dbg(hsdev->hdev, "common attributes: %x:%x, %x:%x, %x:%x %x:%x %x:%x\n",
|
||||
st->poll.index, st->poll.report_id,
|
||||
st->report_state.index, st->report_state.report_id,
|
||||
st->power_state.index, st->power_state.report_id,
|
||||
st->sensitivity.index, st->sensitivity.report_id,
|
||||
timestamp.index, timestamp.report_id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
*/
|
||||
|
||||
#include <linux/iio/common/ssp_sensors.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/kfifo_buf.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/acpi.h>
|
||||
|
||||
#include <linux/iio/common/st_sensors_i2c.h>
|
||||
|
||||
|
@ -107,6 +108,25 @@ void st_sensors_of_i2c_probe(struct i2c_client *client,
|
|||
EXPORT_SYMBOL(st_sensors_of_i2c_probe);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
int st_sensors_match_acpi_device(struct device *dev)
|
||||
{
|
||||
const struct acpi_device_id *acpi_id;
|
||||
kernel_ulong_t driver_data = 0;
|
||||
|
||||
if (ACPI_HANDLE(dev)) {
|
||||
acpi_id = acpi_match_device(dev->driver->acpi_match_table, dev);
|
||||
if (!acpi_id) {
|
||||
dev_err(dev, "No driver data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
driver_data = acpi_id->driver_data;
|
||||
}
|
||||
return driver_data;
|
||||
}
|
||||
EXPORT_SYMBOL(st_sensors_match_acpi_device);
|
||||
#endif
|
||||
|
||||
MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
|
||||
MODULE_DESCRIPTION("STMicroelectronics ST-sensors i2c driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/acpi.h>
|
||||
|
||||
#define AD5592R_GPIO_READBACK_EN BIT(10)
|
||||
#define AD5592R_LDAC_READBACK_EN BIT(6)
|
||||
|
@ -148,10 +149,17 @@ static const struct of_device_id ad5592r_of_match[] = {
|
|||
};
|
||||
MODULE_DEVICE_TABLE(of, ad5592r_of_match);
|
||||
|
||||
static const struct acpi_device_id ad5592r_acpi_match[] = {
|
||||
{"ADS5592", },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, ad5592r_acpi_match);
|
||||
|
||||
static struct spi_driver ad5592r_spi_driver = {
|
||||
.driver = {
|
||||
.name = "ad5592r",
|
||||
.of_match_table = of_match_ptr(ad5592r_of_match),
|
||||
.acpi_match_table = ACPI_PTR(ad5592r_acpi_match),
|
||||
},
|
||||
.probe = ad5592r_spi_probe,
|
||||
.remove = ad5592r_spi_remove,
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/acpi.h>
|
||||
|
||||
#define AD5593R_MODE_CONF (0 << 4)
|
||||
#define AD5593R_MODE_DAC_WRITE (1 << 4)
|
||||
|
@ -115,10 +116,17 @@ static const struct of_device_id ad5593r_of_match[] = {
|
|||
};
|
||||
MODULE_DEVICE_TABLE(of, ad5593r_of_match);
|
||||
|
||||
static const struct acpi_device_id ad5593r_acpi_match[] = {
|
||||
{"ADS5593", },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, ad5593r_acpi_match);
|
||||
|
||||
static struct i2c_driver ad5593r_driver = {
|
||||
.driver = {
|
||||
.name = "ad5593r",
|
||||
.of_match_table = of_match_ptr(ad5593r_of_match),
|
||||
.acpi_match_table = ACPI_PTR(ad5593r_acpi_match),
|
||||
},
|
||||
.probe = ad5593r_i2c_probe,
|
||||
.remove = ad5593r_i2c_remove,
|
||||
|
|
|
@ -88,11 +88,11 @@ static inline int
|
|||
iio_simple_dummy_events_register(struct iio_dev *indio_dev)
|
||||
{
|
||||
return 0;
|
||||
};
|
||||
}
|
||||
|
||||
static inline void
|
||||
iio_simple_dummy_events_unregister(struct iio_dev *indio_dev)
|
||||
{ };
|
||||
{}
|
||||
|
||||
#endif /* CONFIG_IIO_SIMPLE_DUMMY_EVENTS*/
|
||||
|
||||
|
@ -119,11 +119,11 @@ void iio_simple_dummy_unconfigure_buffer(struct iio_dev *indio_dev);
|
|||
static inline int iio_simple_dummy_configure_buffer(struct iio_dev *indio_dev)
|
||||
{
|
||||
return 0;
|
||||
};
|
||||
}
|
||||
|
||||
static inline
|
||||
void iio_simple_dummy_unconfigure_buffer(struct iio_dev *indio_dev)
|
||||
{};
|
||||
{}
|
||||
|
||||
#endif /* CONFIG_IIO_SIMPLE_DUMMY_BUFFER */
|
||||
#endif /* _IIO_SIMPLE_DUMMY_H_ */
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/kfifo_buf.h>
|
||||
|
||||
#include "iio_simple_dummy.h"
|
||||
|
@ -131,9 +132,6 @@ int iio_simple_dummy_configure_buffer(struct iio_dev *indio_dev)
|
|||
|
||||
iio_device_attach_buffer(indio_dev, buffer);
|
||||
|
||||
/* Enable timestamps by default */
|
||||
buffer->scan_timestamp = true;
|
||||
|
||||
/*
|
||||
* Tell the core what device type specific functions should
|
||||
* be run on either side of buffer capture enable / disable.
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
#include <linux/iio/common/ssp_sensors.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/kfifo_buf.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
@ -134,7 +135,7 @@ static int ssp_gyro_probe(struct platform_device *pdev)
|
|||
|
||||
platform_set_drvdata(pdev, indio_dev);
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
ret = devm_iio_device_register(&pdev->dev, indio_dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
@ -144,21 +145,11 @@ static int ssp_gyro_probe(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int ssp_gyro_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver ssp_gyro_driver = {
|
||||
.driver = {
|
||||
.name = SSP_GYROSCOPE_NAME,
|
||||
},
|
||||
.probe = ssp_gyro_probe,
|
||||
.remove = ssp_gyro_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(ssp_gyro_driver);
|
||||
|
|
|
@ -378,7 +378,7 @@ static int max30100_get_temp(struct max30100_data *data, int *val)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
usleep_range(35000, 50000);
|
||||
msleep(35);
|
||||
|
||||
return max30100_read_temp(data, val);
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/slab.h>
|
||||
#include "hts221.h"
|
||||
|
@ -83,6 +84,12 @@ static int hts221_i2c_probe(struct i2c_client *client,
|
|||
return hts221_probe(iio_dev);
|
||||
}
|
||||
|
||||
static const struct acpi_device_id hts221_acpi_match[] = {
|
||||
{"SMO9100", 0},
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, hts221_acpi_match);
|
||||
|
||||
static const struct of_device_id hts221_i2c_of_match[] = {
|
||||
{ .compatible = "st,hts221", },
|
||||
{},
|
||||
|
@ -99,6 +106,7 @@ static struct i2c_driver hts221_driver = {
|
|||
.driver = {
|
||||
.name = "hts221_i2c",
|
||||
.of_match_table = of_match_ptr(hts221_i2c_of_match),
|
||||
.acpi_match_table = ACPI_PTR(hts221_acpi_match),
|
||||
},
|
||||
.probe = hts221_i2c_probe,
|
||||
.id_table = hts221_i2c_id_table,
|
||||
|
|
|
@ -39,6 +39,7 @@ config KMX61
|
|||
be called kmx61.
|
||||
|
||||
source "drivers/iio/imu/inv_mpu6050/Kconfig"
|
||||
source "drivers/iio/imu/st_lsm6dsx/Kconfig"
|
||||
|
||||
endmenu
|
||||
|
||||
|
|
|
@ -17,3 +17,5 @@ obj-y += bmi160/
|
|||
obj-y += inv_mpu6050/
|
||||
|
||||
obj-$(CONFIG_KMX61) += kmx61.o
|
||||
|
||||
obj-y += st_lsm6dsx/
|
||||
|
|
|
@ -325,9 +325,9 @@ static int bmi160_get_data(struct bmi160_data *data, int chan_type,
|
|||
__le16 sample;
|
||||
enum bmi160_sensor_type t = bmi160_to_sensor(chan_type);
|
||||
|
||||
reg = bmi160_regs[t].data + (axis - IIO_MOD_X) * sizeof(__le16);
|
||||
reg = bmi160_regs[t].data + (axis - IIO_MOD_X) * sizeof(sample);
|
||||
|
||||
ret = regmap_bulk_read(data->regmap, reg, &sample, sizeof(__le16));
|
||||
ret = regmap_bulk_read(data->regmap, reg, &sample, sizeof(sample));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
@ -392,8 +392,8 @@ static irqreturn_t bmi160_trigger_handler(int irq, void *p)
|
|||
|
||||
for_each_set_bit(i, indio_dev->active_scan_mask,
|
||||
indio_dev->masklength) {
|
||||
ret = regmap_bulk_read(data->regmap, base + i * sizeof(__le16),
|
||||
&sample, sizeof(__le16));
|
||||
ret = regmap_bulk_read(data->regmap, base + i * sizeof(sample),
|
||||
&sample, sizeof(sample));
|
||||
if (ret < 0)
|
||||
goto done;
|
||||
buf[j++] = sample;
|
||||
|
|
|
@ -11,10 +11,11 @@
|
|||
* - 0x68 if SDO is pulled to GND
|
||||
* - 0x69 if SDO is pulled to VDDIO
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include "bmi160.h"
|
||||
|
||||
|
@ -56,10 +57,19 @@ static const struct acpi_device_id bmi160_acpi_match[] = {
|
|||
};
|
||||
MODULE_DEVICE_TABLE(acpi, bmi160_acpi_match);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id bmi160_of_match[] = {
|
||||
{ .compatible = "bosch,bmi160" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, bmi160_of_match);
|
||||
#endif
|
||||
|
||||
static struct i2c_driver bmi160_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "bmi160_i2c",
|
||||
.acpi_match_table = ACPI_PTR(bmi160_acpi_match),
|
||||
.of_match_table = of_match_ptr(bmi160_of_match),
|
||||
},
|
||||
.probe = bmi160_i2c_probe,
|
||||
.remove = bmi160_i2c_remove,
|
||||
|
|
|
@ -7,10 +7,11 @@
|
|||
* the GNU General Public License. See the file COPYING in the main
|
||||
* directory of this archive for more details.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#include "bmi160.h"
|
||||
|
||||
|
@ -47,13 +48,22 @@ static const struct acpi_device_id bmi160_acpi_match[] = {
|
|||
};
|
||||
MODULE_DEVICE_TABLE(acpi, bmi160_acpi_match);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id bmi160_of_match[] = {
|
||||
{ .compatible = "bosch,bmi160" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, bmi160_of_match);
|
||||
#endif
|
||||
|
||||
static struct spi_driver bmi160_spi_driver = {
|
||||
.probe = bmi160_spi_probe,
|
||||
.remove = bmi160_spi_remove,
|
||||
.id_table = bmi160_spi_id,
|
||||
.driver = {
|
||||
.acpi_match_table = ACPI_PTR(bmi160_acpi_match),
|
||||
.name = "bmi160_spi",
|
||||
.acpi_match_table = ACPI_PTR(bmi160_acpi_match),
|
||||
.of_match_table = of_match_ptr(bmi160_of_match),
|
||||
.name = "bmi160_spi",
|
||||
},
|
||||
};
|
||||
module_spi_driver(bmi160_spi_driver);
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
|
||||
config IIO_ST_LSM6DSX
|
||||
tristate "ST_LSM6DSx driver for STM 6-axis IMU MEMS sensors"
|
||||
depends on (I2C || SPI)
|
||||
select IIO_BUFFER
|
||||
select IIO_KFIFO_BUF
|
||||
select IIO_ST_LSM6DSX_I2C if (I2C)
|
||||
select IIO_ST_LSM6DSX_SPI if (SPI_MASTER)
|
||||
help
|
||||
Say yes here to build support for STMicroelectronics LSM6DSx imu
|
||||
sensor. Supported devices: lsm6ds3, lsm6dsm
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called st_lsm6dsx.
|
||||
|
||||
config IIO_ST_LSM6DSX_I2C
|
||||
tristate
|
||||
depends on IIO_ST_LSM6DSX
|
||||
|
||||
config IIO_ST_LSM6DSX_SPI
|
||||
tristate
|
||||
depends on IIO_ST_LSM6DSX
|
|
@ -0,0 +1,5 @@
|
|||
st_lsm6dsx-y := st_lsm6dsx_core.o st_lsm6dsx_buffer.o
|
||||
|
||||
obj-$(CONFIG_IIO_ST_LSM6DSX) += st_lsm6dsx.o
|
||||
obj-$(CONFIG_IIO_ST_LSM6DSX_I2C) += st_lsm6dsx_i2c.o
|
||||
obj-$(CONFIG_IIO_ST_LSM6DSX_SPI) += st_lsm6dsx_spi.o
|
|
@ -0,0 +1,141 @@
|
|||
/*
|
||||
* STMicroelectronics st_lsm6dsx sensor driver
|
||||
*
|
||||
* Copyright 2016 STMicroelectronics Inc.
|
||||
*
|
||||
* Lorenzo Bianconi <lorenzo.bianconi@st.com>
|
||||
* Denis Ciocca <denis.ciocca@st.com>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#ifndef ST_LSM6DSX_H
|
||||
#define ST_LSM6DSX_H
|
||||
|
||||
#include <linux/device.h>
|
||||
|
||||
#define ST_LSM6DS3_DEV_NAME "lsm6ds3"
|
||||
#define ST_LSM6DSM_DEV_NAME "lsm6dsm"
|
||||
|
||||
enum st_lsm6dsx_hw_id {
|
||||
ST_LSM6DS3_ID,
|
||||
ST_LSM6DSM_ID,
|
||||
};
|
||||
|
||||
#define ST_LSM6DSX_CHAN_SIZE 2
|
||||
#define ST_LSM6DSX_SAMPLE_SIZE 6
|
||||
#define ST_LSM6DSX_SAMPLE_DEPTH (ST_LSM6DSX_SAMPLE_SIZE / \
|
||||
ST_LSM6DSX_CHAN_SIZE)
|
||||
|
||||
#if defined(CONFIG_SPI_MASTER)
|
||||
#define ST_LSM6DSX_RX_MAX_LENGTH 256
|
||||
#define ST_LSM6DSX_TX_MAX_LENGTH 8
|
||||
|
||||
struct st_lsm6dsx_transfer_buffer {
|
||||
u8 rx_buf[ST_LSM6DSX_RX_MAX_LENGTH];
|
||||
u8 tx_buf[ST_LSM6DSX_TX_MAX_LENGTH] ____cacheline_aligned;
|
||||
};
|
||||
#endif /* CONFIG_SPI_MASTER */
|
||||
|
||||
struct st_lsm6dsx_transfer_function {
|
||||
int (*read)(struct device *dev, u8 addr, int len, u8 *data);
|
||||
int (*write)(struct device *dev, u8 addr, int len, u8 *data);
|
||||
};
|
||||
|
||||
struct st_lsm6dsx_reg {
|
||||
u8 addr;
|
||||
u8 mask;
|
||||
};
|
||||
|
||||
struct st_lsm6dsx_settings {
|
||||
u8 wai;
|
||||
u16 max_fifo_size;
|
||||
enum st_lsm6dsx_hw_id id;
|
||||
};
|
||||
|
||||
enum st_lsm6dsx_sensor_id {
|
||||
ST_LSM6DSX_ID_ACC,
|
||||
ST_LSM6DSX_ID_GYRO,
|
||||
ST_LSM6DSX_ID_MAX,
|
||||
};
|
||||
|
||||
enum st_lsm6dsx_fifo_mode {
|
||||
ST_LSM6DSX_FIFO_BYPASS = 0x0,
|
||||
ST_LSM6DSX_FIFO_CONT = 0x6,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct st_lsm6dsx_sensor - ST IMU sensor instance
|
||||
* @id: Sensor identifier.
|
||||
* @hw: Pointer to instance of struct st_lsm6dsx_hw.
|
||||
* @gain: Configured sensor sensitivity.
|
||||
* @odr: Output data rate of the sensor [Hz].
|
||||
* @watermark: Sensor watermark level.
|
||||
* @sip: Number of samples in a given pattern.
|
||||
* @decimator: FIFO decimation factor.
|
||||
* @decimator_mask: Sensor mask for decimation register.
|
||||
* @delta_ts: Delta time between two consecutive interrupts.
|
||||
* @ts: Latest timestamp from the interrupt handler.
|
||||
*/
|
||||
struct st_lsm6dsx_sensor {
|
||||
enum st_lsm6dsx_sensor_id id;
|
||||
struct st_lsm6dsx_hw *hw;
|
||||
|
||||
u32 gain;
|
||||
u16 odr;
|
||||
|
||||
u16 watermark;
|
||||
u8 sip;
|
||||
u8 decimator;
|
||||
u8 decimator_mask;
|
||||
|
||||
s64 delta_ts;
|
||||
s64 ts;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct st_lsm6dsx_hw - ST IMU MEMS hw instance
|
||||
* @dev: Pointer to instance of struct device (I2C or SPI).
|
||||
* @irq: Device interrupt line (I2C or SPI).
|
||||
* @lock: Mutex to protect read and write operations.
|
||||
* @fifo_lock: Mutex to prevent concurrent access to the hw FIFO.
|
||||
* @fifo_mode: FIFO operating mode supported by the device.
|
||||
* @enable_mask: Enabled sensor bitmask.
|
||||
* @sip: Total number of samples (acc/gyro) in a given pattern.
|
||||
* @iio_devs: Pointers to acc/gyro iio_dev instances.
|
||||
* @settings: Pointer to the specific sensor settings in use.
|
||||
* @tf: Transfer function structure used by I/O operations.
|
||||
* @tb: Transfer buffers used by SPI I/O operations.
|
||||
*/
|
||||
struct st_lsm6dsx_hw {
|
||||
struct device *dev;
|
||||
int irq;
|
||||
|
||||
struct mutex lock;
|
||||
struct mutex fifo_lock;
|
||||
|
||||
enum st_lsm6dsx_fifo_mode fifo_mode;
|
||||
u8 enable_mask;
|
||||
u8 sip;
|
||||
|
||||
struct iio_dev *iio_devs[ST_LSM6DSX_ID_MAX];
|
||||
|
||||
const struct st_lsm6dsx_settings *settings;
|
||||
|
||||
const struct st_lsm6dsx_transfer_function *tf;
|
||||
#if defined(CONFIG_SPI_MASTER)
|
||||
struct st_lsm6dsx_transfer_buffer tb;
|
||||
#endif /* CONFIG_SPI_MASTER */
|
||||
};
|
||||
|
||||
int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id,
|
||||
const struct st_lsm6dsx_transfer_function *tf_ops);
|
||||
int st_lsm6dsx_sensor_enable(struct st_lsm6dsx_sensor *sensor);
|
||||
int st_lsm6dsx_sensor_disable(struct st_lsm6dsx_sensor *sensor);
|
||||
int st_lsm6dsx_fifo_setup(struct st_lsm6dsx_hw *hw);
|
||||
int st_lsm6dsx_write_with_mask(struct st_lsm6dsx_hw *hw, u8 addr, u8 mask,
|
||||
u8 val);
|
||||
int st_lsm6dsx_update_watermark(struct st_lsm6dsx_sensor *sensor,
|
||||
u16 watermark);
|
||||
|
||||
#endif /* ST_LSM6DSX_H */
|
|
@ -0,0 +1,454 @@
|
|||
/*
|
||||
* STMicroelectronics st_lsm6dsx FIFO buffer library driver
|
||||
*
|
||||
* LSM6DS3/LSM6DSM: The FIFO buffer can be configured to store data
|
||||
* from gyroscope and accelerometer. Samples are queued without any tag
|
||||
* according to a specific pattern based on 'FIFO data sets' (6 bytes each):
|
||||
* - 1st data set is reserved for gyroscope data
|
||||
* - 2nd data set is reserved for accelerometer data
|
||||
* The FIFO pattern changes depending on the ODRs and decimation factors
|
||||
* assigned to the FIFO data sets. The first sequence of data stored in FIFO
|
||||
* buffer contains the data of all the enabled FIFO data sets
|
||||
* (e.g. Gx, Gy, Gz, Ax, Ay, Az), then data are repeated depending on the
|
||||
* value of the decimation factor and ODR set for each FIFO data set.
|
||||
* FIFO supported modes:
|
||||
* - BYPASS: FIFO disabled
|
||||
* - CONTINUOUS: FIFO enabled. When the buffer is full, the FIFO index
|
||||
* restarts from the beginning and the oldest sample is overwritten
|
||||
*
|
||||
* Copyright 2016 STMicroelectronics Inc.
|
||||
*
|
||||
* Lorenzo Bianconi <lorenzo.bianconi@st.com>
|
||||
* Denis Ciocca <denis.ciocca@st.com>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/iio/kfifo_buf.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
|
||||
#include "st_lsm6dsx.h"
|
||||
|
||||
#define ST_LSM6DSX_REG_FIFO_THL_ADDR 0x06
|
||||
#define ST_LSM6DSX_REG_FIFO_THH_ADDR 0x07
|
||||
#define ST_LSM6DSX_FIFO_TH_MASK GENMASK(11, 0)
|
||||
#define ST_LSM6DSX_REG_FIFO_DEC_GXL_ADDR 0x08
|
||||
#define ST_LSM6DSX_REG_FIFO_MODE_ADDR 0x0a
|
||||
#define ST_LSM6DSX_FIFO_MODE_MASK GENMASK(2, 0)
|
||||
#define ST_LSM6DSX_FIFO_ODR_MASK GENMASK(6, 3)
|
||||
#define ST_LSM6DSX_REG_FIFO_DIFFL_ADDR 0x3a
|
||||
#define ST_LSM6DSX_FIFO_DIFF_MASK GENMASK(11, 0)
|
||||
#define ST_LSM6DSX_FIFO_EMPTY_MASK BIT(12)
|
||||
#define ST_LSM6DSX_REG_FIFO_OUTL_ADDR 0x3e
|
||||
|
||||
#define ST_LSM6DSX_MAX_FIFO_ODR_VAL 0x08
|
||||
|
||||
struct st_lsm6dsx_decimator_entry {
|
||||
u8 decimator;
|
||||
u8 val;
|
||||
};
|
||||
|
||||
static const
|
||||
struct st_lsm6dsx_decimator_entry st_lsm6dsx_decimator_table[] = {
|
||||
{ 0, 0x0 },
|
||||
{ 1, 0x1 },
|
||||
{ 2, 0x2 },
|
||||
{ 3, 0x3 },
|
||||
{ 4, 0x4 },
|
||||
{ 8, 0x5 },
|
||||
{ 16, 0x6 },
|
||||
{ 32, 0x7 },
|
||||
};
|
||||
|
||||
static int st_lsm6dsx_get_decimator_val(u8 val)
|
||||
{
|
||||
const int max_size = ARRAY_SIZE(st_lsm6dsx_decimator_table);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < max_size; i++)
|
||||
if (st_lsm6dsx_decimator_table[i].decimator == val)
|
||||
break;
|
||||
|
||||
return i == max_size ? 0 : st_lsm6dsx_decimator_table[i].val;
|
||||
}
|
||||
|
||||
static void st_lsm6dsx_get_max_min_odr(struct st_lsm6dsx_hw *hw,
|
||||
u16 *max_odr, u16 *min_odr)
|
||||
{
|
||||
struct st_lsm6dsx_sensor *sensor;
|
||||
int i;
|
||||
|
||||
*max_odr = 0, *min_odr = ~0;
|
||||
for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
|
||||
sensor = iio_priv(hw->iio_devs[i]);
|
||||
|
||||
if (!(hw->enable_mask & BIT(sensor->id)))
|
||||
continue;
|
||||
|
||||
*max_odr = max_t(u16, *max_odr, sensor->odr);
|
||||
*min_odr = min_t(u16, *min_odr, sensor->odr);
|
||||
}
|
||||
}
|
||||
|
||||
static int st_lsm6dsx_update_decimators(struct st_lsm6dsx_hw *hw)
|
||||
{
|
||||
struct st_lsm6dsx_sensor *sensor;
|
||||
u16 max_odr, min_odr, sip = 0;
|
||||
int err, i;
|
||||
u8 data;
|
||||
|
||||
st_lsm6dsx_get_max_min_odr(hw, &max_odr, &min_odr);
|
||||
|
||||
for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
|
||||
sensor = iio_priv(hw->iio_devs[i]);
|
||||
|
||||
/* update fifo decimators and sample in pattern */
|
||||
if (hw->enable_mask & BIT(sensor->id)) {
|
||||
sensor->sip = sensor->odr / min_odr;
|
||||
sensor->decimator = max_odr / sensor->odr;
|
||||
data = st_lsm6dsx_get_decimator_val(sensor->decimator);
|
||||
} else {
|
||||
sensor->sip = 0;
|
||||
sensor->decimator = 0;
|
||||
data = 0;
|
||||
}
|
||||
|
||||
err = st_lsm6dsx_write_with_mask(hw,
|
||||
ST_LSM6DSX_REG_FIFO_DEC_GXL_ADDR,
|
||||
sensor->decimator_mask, data);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
sip += sensor->sip;
|
||||
}
|
||||
hw->sip = sip;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int st_lsm6dsx_set_fifo_mode(struct st_lsm6dsx_hw *hw,
|
||||
enum st_lsm6dsx_fifo_mode fifo_mode)
|
||||
{
|
||||
u8 data;
|
||||
int err;
|
||||
|
||||
switch (fifo_mode) {
|
||||
case ST_LSM6DSX_FIFO_BYPASS:
|
||||
data = fifo_mode;
|
||||
break;
|
||||
case ST_LSM6DSX_FIFO_CONT:
|
||||
data = (ST_LSM6DSX_MAX_FIFO_ODR_VAL <<
|
||||
__ffs(ST_LSM6DSX_FIFO_ODR_MASK)) | fifo_mode;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = hw->tf->write(hw->dev, ST_LSM6DSX_REG_FIFO_MODE_ADDR,
|
||||
sizeof(data), &data);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
hw->fifo_mode = fifo_mode;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int st_lsm6dsx_update_watermark(struct st_lsm6dsx_sensor *sensor, u16 watermark)
|
||||
{
|
||||
u16 fifo_watermark = ~0, cur_watermark, sip = 0;
|
||||
struct st_lsm6dsx_hw *hw = sensor->hw;
|
||||
struct st_lsm6dsx_sensor *cur_sensor;
|
||||
__le16 wdata;
|
||||
int i, err;
|
||||
u8 data;
|
||||
|
||||
for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
|
||||
cur_sensor = iio_priv(hw->iio_devs[i]);
|
||||
|
||||
if (!(hw->enable_mask & BIT(cur_sensor->id)))
|
||||
continue;
|
||||
|
||||
cur_watermark = (cur_sensor == sensor) ? watermark
|
||||
: cur_sensor->watermark;
|
||||
|
||||
fifo_watermark = min_t(u16, fifo_watermark, cur_watermark);
|
||||
sip += cur_sensor->sip;
|
||||
}
|
||||
|
||||
if (!sip)
|
||||
return 0;
|
||||
|
||||
fifo_watermark = max_t(u16, fifo_watermark, sip);
|
||||
fifo_watermark = (fifo_watermark / sip) * sip;
|
||||
fifo_watermark = fifo_watermark * ST_LSM6DSX_SAMPLE_DEPTH;
|
||||
|
||||
mutex_lock(&hw->lock);
|
||||
|
||||
err = hw->tf->read(hw->dev, ST_LSM6DSX_REG_FIFO_THH_ADDR,
|
||||
sizeof(data), &data);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
fifo_watermark = ((data & ~ST_LSM6DSX_FIFO_TH_MASK) << 8) |
|
||||
(fifo_watermark & ST_LSM6DSX_FIFO_TH_MASK);
|
||||
|
||||
wdata = cpu_to_le16(fifo_watermark);
|
||||
err = hw->tf->write(hw->dev, ST_LSM6DSX_REG_FIFO_THL_ADDR,
|
||||
sizeof(wdata), (u8 *)&wdata);
|
||||
out:
|
||||
mutex_unlock(&hw->lock);
|
||||
|
||||
return err < 0 ? err : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* st_lsm6dsx_read_fifo() - LSM6DS3-LSM6DSM read FIFO routine
|
||||
* @hw: Pointer to instance of struct st_lsm6dsx_hw.
|
||||
*
|
||||
* Read samples from the hw FIFO and push them to IIO buffers.
|
||||
*
|
||||
* Return: Number of bytes read from the FIFO
|
||||
*/
|
||||
static int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw)
|
||||
{
|
||||
u16 fifo_len, pattern_len = hw->sip * ST_LSM6DSX_SAMPLE_SIZE;
|
||||
int err, acc_sip, gyro_sip, read_len, samples, offset;
|
||||
struct st_lsm6dsx_sensor *acc_sensor, *gyro_sensor;
|
||||
s64 acc_ts, acc_delta_ts, gyro_ts, gyro_delta_ts;
|
||||
u8 iio_buff[ALIGN(ST_LSM6DSX_SAMPLE_SIZE, sizeof(s64)) + sizeof(s64)];
|
||||
u8 buff[pattern_len];
|
||||
__le16 fifo_status;
|
||||
|
||||
err = hw->tf->read(hw->dev, ST_LSM6DSX_REG_FIFO_DIFFL_ADDR,
|
||||
sizeof(fifo_status), (u8 *)&fifo_status);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (fifo_status & cpu_to_le16(ST_LSM6DSX_FIFO_EMPTY_MASK))
|
||||
return 0;
|
||||
|
||||
fifo_len = (le16_to_cpu(fifo_status) & ST_LSM6DSX_FIFO_DIFF_MASK) *
|
||||
ST_LSM6DSX_CHAN_SIZE;
|
||||
samples = fifo_len / ST_LSM6DSX_SAMPLE_SIZE;
|
||||
fifo_len = (fifo_len / pattern_len) * pattern_len;
|
||||
|
||||
/*
|
||||
* compute delta timestamp between two consecutive samples
|
||||
* in order to estimate queueing time of data generated
|
||||
* by the sensor
|
||||
*/
|
||||
acc_sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_ACC]);
|
||||
acc_ts = acc_sensor->ts - acc_sensor->delta_ts;
|
||||
acc_delta_ts = div_s64(acc_sensor->delta_ts * acc_sensor->decimator,
|
||||
samples);
|
||||
|
||||
gyro_sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_GYRO]);
|
||||
gyro_ts = gyro_sensor->ts - gyro_sensor->delta_ts;
|
||||
gyro_delta_ts = div_s64(gyro_sensor->delta_ts * gyro_sensor->decimator,
|
||||
samples);
|
||||
|
||||
for (read_len = 0; read_len < fifo_len; read_len += pattern_len) {
|
||||
err = hw->tf->read(hw->dev, ST_LSM6DSX_REG_FIFO_OUTL_ADDR,
|
||||
sizeof(buff), buff);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/*
|
||||
* Data are written to the FIFO with a specific pattern
|
||||
* depending on the configured ODRs. The first sequence of data
|
||||
* stored in FIFO contains the data of all enabled sensors
|
||||
* (e.g. Gx, Gy, Gz, Ax, Ay, Az), then data are repeated
|
||||
* depending on the value of the decimation factor set for each
|
||||
* sensor.
|
||||
*
|
||||
* Supposing the FIFO is storing data from gyroscope and
|
||||
* accelerometer at different ODRs:
|
||||
* - gyroscope ODR = 208Hz, accelerometer ODR = 104Hz
|
||||
* Since the gyroscope ODR is twice the accelerometer one, the
|
||||
* following pattern is repeated every 9 samples:
|
||||
* - Gx, Gy, Gz, Ax, Ay, Az, Gx, Gy, Gz
|
||||
*/
|
||||
gyro_sip = gyro_sensor->sip;
|
||||
acc_sip = acc_sensor->sip;
|
||||
offset = 0;
|
||||
|
||||
while (acc_sip > 0 || gyro_sip > 0) {
|
||||
if (gyro_sip-- > 0) {
|
||||
memcpy(iio_buff, &buff[offset],
|
||||
ST_LSM6DSX_SAMPLE_SIZE);
|
||||
iio_push_to_buffers_with_timestamp(
|
||||
hw->iio_devs[ST_LSM6DSX_ID_GYRO],
|
||||
iio_buff, gyro_ts);
|
||||
offset += ST_LSM6DSX_SAMPLE_SIZE;
|
||||
gyro_ts += gyro_delta_ts;
|
||||
}
|
||||
|
||||
if (acc_sip-- > 0) {
|
||||
memcpy(iio_buff, &buff[offset],
|
||||
ST_LSM6DSX_SAMPLE_SIZE);
|
||||
iio_push_to_buffers_with_timestamp(
|
||||
hw->iio_devs[ST_LSM6DSX_ID_ACC],
|
||||
iio_buff, acc_ts);
|
||||
offset += ST_LSM6DSX_SAMPLE_SIZE;
|
||||
acc_ts += acc_delta_ts;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return read_len;
|
||||
}
|
||||
|
||||
static int st_lsm6dsx_flush_fifo(struct st_lsm6dsx_hw *hw)
|
||||
{
|
||||
int err;
|
||||
|
||||
mutex_lock(&hw->fifo_lock);
|
||||
|
||||
st_lsm6dsx_read_fifo(hw);
|
||||
err = st_lsm6dsx_set_fifo_mode(hw, ST_LSM6DSX_FIFO_BYPASS);
|
||||
|
||||
mutex_unlock(&hw->fifo_lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int st_lsm6dsx_update_fifo(struct iio_dev *iio_dev, bool enable)
|
||||
{
|
||||
struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev);
|
||||
struct st_lsm6dsx_hw *hw = sensor->hw;
|
||||
int err;
|
||||
|
||||
if (hw->fifo_mode != ST_LSM6DSX_FIFO_BYPASS) {
|
||||
err = st_lsm6dsx_flush_fifo(hw);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (enable) {
|
||||
err = st_lsm6dsx_sensor_enable(sensor);
|
||||
if (err < 0)
|
||||
return err;
|
||||
} else {
|
||||
err = st_lsm6dsx_sensor_disable(sensor);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
err = st_lsm6dsx_update_decimators(hw);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = st_lsm6dsx_update_watermark(sensor, sensor->watermark);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (hw->enable_mask) {
|
||||
err = st_lsm6dsx_set_fifo_mode(hw, ST_LSM6DSX_FIFO_CONT);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/*
|
||||
* store enable buffer timestamp as reference to compute
|
||||
* first delta timestamp
|
||||
*/
|
||||
sensor->ts = iio_get_time_ns(iio_dev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t st_lsm6dsx_handler_irq(int irq, void *private)
|
||||
{
|
||||
struct st_lsm6dsx_hw *hw = (struct st_lsm6dsx_hw *)private;
|
||||
struct st_lsm6dsx_sensor *sensor;
|
||||
int i;
|
||||
|
||||
if (!hw->sip)
|
||||
return IRQ_NONE;
|
||||
|
||||
for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
|
||||
sensor = iio_priv(hw->iio_devs[i]);
|
||||
|
||||
if (sensor->sip > 0) {
|
||||
s64 timestamp;
|
||||
|
||||
timestamp = iio_get_time_ns(hw->iio_devs[i]);
|
||||
sensor->delta_ts = timestamp - sensor->ts;
|
||||
sensor->ts = timestamp;
|
||||
}
|
||||
}
|
||||
|
||||
return IRQ_WAKE_THREAD;
|
||||
}
|
||||
|
||||
static irqreturn_t st_lsm6dsx_handler_thread(int irq, void *private)
|
||||
{
|
||||
struct st_lsm6dsx_hw *hw = (struct st_lsm6dsx_hw *)private;
|
||||
int count;
|
||||
|
||||
mutex_lock(&hw->fifo_lock);
|
||||
count = st_lsm6dsx_read_fifo(hw);
|
||||
mutex_unlock(&hw->fifo_lock);
|
||||
|
||||
return !count ? IRQ_NONE : IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int st_lsm6dsx_buffer_preenable(struct iio_dev *iio_dev)
|
||||
{
|
||||
return st_lsm6dsx_update_fifo(iio_dev, true);
|
||||
}
|
||||
|
||||
static int st_lsm6dsx_buffer_postdisable(struct iio_dev *iio_dev)
|
||||
{
|
||||
return st_lsm6dsx_update_fifo(iio_dev, false);
|
||||
}
|
||||
|
||||
static const struct iio_buffer_setup_ops st_lsm6dsx_buffer_ops = {
|
||||
.preenable = st_lsm6dsx_buffer_preenable,
|
||||
.postdisable = st_lsm6dsx_buffer_postdisable,
|
||||
};
|
||||
|
||||
int st_lsm6dsx_fifo_setup(struct st_lsm6dsx_hw *hw)
|
||||
{
|
||||
struct iio_buffer *buffer;
|
||||
unsigned long irq_type;
|
||||
int i, 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\n", irq_type);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = devm_request_threaded_irq(hw->dev, hw->irq,
|
||||
st_lsm6dsx_handler_irq,
|
||||
st_lsm6dsx_handler_thread,
|
||||
irq_type | IRQF_ONESHOT,
|
||||
"lsm6dsx", hw);
|
||||
if (err) {
|
||||
dev_err(hw->dev, "failed to request trigger irq %d\n",
|
||||
hw->irq);
|
||||
return err;
|
||||
}
|
||||
|
||||
for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
|
||||
buffer = devm_iio_kfifo_allocate(hw->dev);
|
||||
if (!buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
iio_device_attach_buffer(hw->iio_devs[i], buffer);
|
||||
hw->iio_devs[i]->modes |= INDIO_BUFFER_SOFTWARE;
|
||||
hw->iio_devs[i]->setup_ops = &st_lsm6dsx_buffer_ops;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,720 @@
|
|||
/*
|
||||
* STMicroelectronics st_lsm6dsx sensor driver
|
||||
*
|
||||
* The ST LSM6DSx IMU MEMS series consists of 3D digital accelerometer
|
||||
* and 3D digital gyroscope system-in-package with a digital I2C/SPI serial
|
||||
* interface standard output.
|
||||
* LSM6DSx IMU MEMS series has a dynamic user-selectable full-scale
|
||||
* acceleration range of +-2/+-4/+-8/+-16 g and an angular rate range of
|
||||
* +-125/+-245/+-500/+-1000/+-2000 dps
|
||||
* LSM6DSx series has an integrated First-In-First-Out (FIFO) buffer
|
||||
* allowing dynamic batching of sensor data.
|
||||
*
|
||||
* Supported sensors:
|
||||
* - LSM6DS3:
|
||||
* - Accelerometer/Gyroscope supported ODR [Hz]: 13, 26, 52, 104, 208, 416
|
||||
* - Accelerometer supported full-scale [g]: +-2/+-4/+-8/+-16
|
||||
* - Gyroscope supported full-scale [dps]: +-125/+-245/+-500/+-1000/+-2000
|
||||
* - FIFO size: 8KB
|
||||
*
|
||||
* - LSM6DSM:
|
||||
* - Accelerometer/Gyroscope supported ODR [Hz]: 13, 26, 52, 104, 208, 416
|
||||
* - Accelerometer supported full-scale [g]: +-2/+-4/+-8/+-16
|
||||
* - Gyroscope supported full-scale [dps]: +-125/+-245/+-500/+-1000/+-2000
|
||||
* - FIFO size: 4KB
|
||||
*
|
||||
* Copyright 2016 STMicroelectronics Inc.
|
||||
*
|
||||
* Lorenzo Bianconi <lorenzo.bianconi@st.com>
|
||||
* Denis Ciocca <denis.ciocca@st.com>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
#include <linux/platform_data/st_sensors_pdata.h>
|
||||
|
||||
#include "st_lsm6dsx.h"
|
||||
|
||||
#define ST_LSM6DSX_REG_ACC_DEC_MASK GENMASK(2, 0)
|
||||
#define ST_LSM6DSX_REG_GYRO_DEC_MASK GENMASK(5, 3)
|
||||
#define ST_LSM6DSX_REG_INT1_ADDR 0x0d
|
||||
#define ST_LSM6DSX_REG_INT2_ADDR 0x0e
|
||||
#define ST_LSM6DSX_REG_FIFO_FTH_IRQ_MASK BIT(3)
|
||||
#define ST_LSM6DSX_REG_WHOAMI_ADDR 0x0f
|
||||
#define ST_LSM6DSX_REG_RESET_ADDR 0x12
|
||||
#define ST_LSM6DSX_REG_RESET_MASK BIT(0)
|
||||
#define ST_LSM6DSX_REG_BDU_ADDR 0x12
|
||||
#define ST_LSM6DSX_REG_BDU_MASK BIT(6)
|
||||
#define ST_LSM6DSX_REG_INT2_ON_INT1_ADDR 0x13
|
||||
#define ST_LSM6DSX_REG_INT2_ON_INT1_MASK BIT(5)
|
||||
#define ST_LSM6DSX_REG_ROUNDING_ADDR 0x16
|
||||
#define ST_LSM6DSX_REG_ROUNDING_MASK BIT(2)
|
||||
#define ST_LSM6DSX_REG_LIR_ADDR 0x58
|
||||
#define ST_LSM6DSX_REG_LIR_MASK BIT(0)
|
||||
|
||||
#define ST_LSM6DSX_REG_ACC_ODR_ADDR 0x10
|
||||
#define ST_LSM6DSX_REG_ACC_ODR_MASK GENMASK(7, 4)
|
||||
#define ST_LSM6DSX_REG_ACC_FS_ADDR 0x10
|
||||
#define ST_LSM6DSX_REG_ACC_FS_MASK GENMASK(3, 2)
|
||||
#define ST_LSM6DSX_REG_ACC_OUT_X_L_ADDR 0x28
|
||||
#define ST_LSM6DSX_REG_ACC_OUT_Y_L_ADDR 0x2a
|
||||
#define ST_LSM6DSX_REG_ACC_OUT_Z_L_ADDR 0x2c
|
||||
|
||||
#define ST_LSM6DSX_REG_GYRO_ODR_ADDR 0x11
|
||||
#define ST_LSM6DSX_REG_GYRO_ODR_MASK GENMASK(7, 4)
|
||||
#define ST_LSM6DSX_REG_GYRO_FS_ADDR 0x11
|
||||
#define ST_LSM6DSX_REG_GYRO_FS_MASK GENMASK(3, 2)
|
||||
#define ST_LSM6DSX_REG_GYRO_OUT_X_L_ADDR 0x22
|
||||
#define ST_LSM6DSX_REG_GYRO_OUT_Y_L_ADDR 0x24
|
||||
#define ST_LSM6DSX_REG_GYRO_OUT_Z_L_ADDR 0x26
|
||||
|
||||
#define ST_LSM6DS3_WHOAMI 0x69
|
||||
#define ST_LSM6DSM_WHOAMI 0x6a
|
||||
|
||||
#define ST_LSM6DS3_MAX_FIFO_SIZE 8192
|
||||
#define ST_LSM6DSM_MAX_FIFO_SIZE 4096
|
||||
|
||||
#define ST_LSM6DSX_ACC_FS_2G_GAIN IIO_G_TO_M_S_2(61)
|
||||
#define ST_LSM6DSX_ACC_FS_4G_GAIN IIO_G_TO_M_S_2(122)
|
||||
#define ST_LSM6DSX_ACC_FS_8G_GAIN IIO_G_TO_M_S_2(244)
|
||||
#define ST_LSM6DSX_ACC_FS_16G_GAIN IIO_G_TO_M_S_2(488)
|
||||
|
||||
#define ST_LSM6DSX_GYRO_FS_245_GAIN IIO_DEGREE_TO_RAD(8750)
|
||||
#define ST_LSM6DSX_GYRO_FS_500_GAIN IIO_DEGREE_TO_RAD(17500)
|
||||
#define ST_LSM6DSX_GYRO_FS_1000_GAIN IIO_DEGREE_TO_RAD(35000)
|
||||
#define ST_LSM6DSX_GYRO_FS_2000_GAIN IIO_DEGREE_TO_RAD(70000)
|
||||
|
||||
struct st_lsm6dsx_odr {
|
||||
u16 hz;
|
||||
u8 val;
|
||||
};
|
||||
|
||||
#define ST_LSM6DSX_ODR_LIST_SIZE 6
|
||||
struct st_lsm6dsx_odr_table_entry {
|
||||
struct st_lsm6dsx_reg reg;
|
||||
struct st_lsm6dsx_odr odr_avl[ST_LSM6DSX_ODR_LIST_SIZE];
|
||||
};
|
||||
|
||||
static const struct st_lsm6dsx_odr_table_entry st_lsm6dsx_odr_table[] = {
|
||||
[ST_LSM6DSX_ID_ACC] = {
|
||||
.reg = {
|
||||
.addr = ST_LSM6DSX_REG_ACC_ODR_ADDR,
|
||||
.mask = ST_LSM6DSX_REG_ACC_ODR_MASK,
|
||||
},
|
||||
.odr_avl[0] = { 13, 0x01 },
|
||||
.odr_avl[1] = { 26, 0x02 },
|
||||
.odr_avl[2] = { 52, 0x03 },
|
||||
.odr_avl[3] = { 104, 0x04 },
|
||||
.odr_avl[4] = { 208, 0x05 },
|
||||
.odr_avl[5] = { 416, 0x06 },
|
||||
},
|
||||
[ST_LSM6DSX_ID_GYRO] = {
|
||||
.reg = {
|
||||
.addr = ST_LSM6DSX_REG_GYRO_ODR_ADDR,
|
||||
.mask = ST_LSM6DSX_REG_GYRO_ODR_MASK,
|
||||
},
|
||||
.odr_avl[0] = { 13, 0x01 },
|
||||
.odr_avl[1] = { 26, 0x02 },
|
||||
.odr_avl[2] = { 52, 0x03 },
|
||||
.odr_avl[3] = { 104, 0x04 },
|
||||
.odr_avl[4] = { 208, 0x05 },
|
||||
.odr_avl[5] = { 416, 0x06 },
|
||||
}
|
||||
};
|
||||
|
||||
struct st_lsm6dsx_fs {
|
||||
u32 gain;
|
||||
u8 val;
|
||||
};
|
||||
|
||||
#define ST_LSM6DSX_FS_LIST_SIZE 4
|
||||
struct st_lsm6dsx_fs_table_entry {
|
||||
struct st_lsm6dsx_reg reg;
|
||||
struct st_lsm6dsx_fs fs_avl[ST_LSM6DSX_FS_LIST_SIZE];
|
||||
};
|
||||
|
||||
static const struct st_lsm6dsx_fs_table_entry st_lsm6dsx_fs_table[] = {
|
||||
[ST_LSM6DSX_ID_ACC] = {
|
||||
.reg = {
|
||||
.addr = ST_LSM6DSX_REG_ACC_FS_ADDR,
|
||||
.mask = ST_LSM6DSX_REG_ACC_FS_MASK,
|
||||
},
|
||||
.fs_avl[0] = { ST_LSM6DSX_ACC_FS_2G_GAIN, 0x0 },
|
||||
.fs_avl[1] = { ST_LSM6DSX_ACC_FS_4G_GAIN, 0x2 },
|
||||
.fs_avl[2] = { ST_LSM6DSX_ACC_FS_8G_GAIN, 0x3 },
|
||||
.fs_avl[3] = { ST_LSM6DSX_ACC_FS_16G_GAIN, 0x1 },
|
||||
},
|
||||
[ST_LSM6DSX_ID_GYRO] = {
|
||||
.reg = {
|
||||
.addr = ST_LSM6DSX_REG_GYRO_FS_ADDR,
|
||||
.mask = ST_LSM6DSX_REG_GYRO_FS_MASK,
|
||||
},
|
||||
.fs_avl[0] = { ST_LSM6DSX_GYRO_FS_245_GAIN, 0x0 },
|
||||
.fs_avl[1] = { ST_LSM6DSX_GYRO_FS_500_GAIN, 0x1 },
|
||||
.fs_avl[2] = { ST_LSM6DSX_GYRO_FS_1000_GAIN, 0x2 },
|
||||
.fs_avl[3] = { ST_LSM6DSX_GYRO_FS_2000_GAIN, 0x3 },
|
||||
}
|
||||
};
|
||||
|
||||
static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
|
||||
{
|
||||
.wai = ST_LSM6DS3_WHOAMI,
|
||||
.max_fifo_size = ST_LSM6DS3_MAX_FIFO_SIZE,
|
||||
.id = ST_LSM6DS3_ID,
|
||||
},
|
||||
{
|
||||
.wai = ST_LSM6DSM_WHOAMI,
|
||||
.max_fifo_size = ST_LSM6DSM_MAX_FIFO_SIZE,
|
||||
.id = ST_LSM6DSM_ID,
|
||||
},
|
||||
};
|
||||
|
||||
#define ST_LSM6DSX_CHANNEL(chan_type, addr, mod, scan_idx) \
|
||||
{ \
|
||||
.type = chan_type, \
|
||||
.address = addr, \
|
||||
.modified = 1, \
|
||||
.channel2 = mod, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
|
||||
.scan_index = scan_idx, \
|
||||
.scan_type = { \
|
||||
.sign = 's', \
|
||||
.realbits = 16, \
|
||||
.storagebits = 16, \
|
||||
.endianness = IIO_LE, \
|
||||
}, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec st_lsm6dsx_acc_channels[] = {
|
||||
ST_LSM6DSX_CHANNEL(IIO_ACCEL, ST_LSM6DSX_REG_ACC_OUT_X_L_ADDR,
|
||||
IIO_MOD_X, 0),
|
||||
ST_LSM6DSX_CHANNEL(IIO_ACCEL, ST_LSM6DSX_REG_ACC_OUT_Y_L_ADDR,
|
||||
IIO_MOD_Y, 1),
|
||||
ST_LSM6DSX_CHANNEL(IIO_ACCEL, ST_LSM6DSX_REG_ACC_OUT_Z_L_ADDR,
|
||||
IIO_MOD_Z, 2),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(3),
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec st_lsm6dsx_gyro_channels[] = {
|
||||
ST_LSM6DSX_CHANNEL(IIO_ANGL_VEL, ST_LSM6DSX_REG_GYRO_OUT_X_L_ADDR,
|
||||
IIO_MOD_X, 0),
|
||||
ST_LSM6DSX_CHANNEL(IIO_ANGL_VEL, ST_LSM6DSX_REG_GYRO_OUT_Y_L_ADDR,
|
||||
IIO_MOD_Y, 1),
|
||||
ST_LSM6DSX_CHANNEL(IIO_ANGL_VEL, ST_LSM6DSX_REG_GYRO_OUT_Z_L_ADDR,
|
||||
IIO_MOD_Z, 2),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(3),
|
||||
};
|
||||
|
||||
int st_lsm6dsx_write_with_mask(struct st_lsm6dsx_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 out;
|
||||
}
|
||||
|
||||
data = (data & ~mask) | ((val << __ffs(mask)) & 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);
|
||||
|
||||
out:
|
||||
mutex_unlock(&hw->lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int st_lsm6dsx_check_whoami(struct st_lsm6dsx_hw *hw, int id)
|
||||
{
|
||||
int err, i;
|
||||
u8 data;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(st_lsm6dsx_sensor_settings); i++) {
|
||||
if (id == st_lsm6dsx_sensor_settings[i].id)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == ARRAY_SIZE(st_lsm6dsx_sensor_settings)) {
|
||||
dev_err(hw->dev, "unsupported hw id [%02x]\n", id);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
err = hw->tf->read(hw->dev, ST_LSM6DSX_REG_WHOAMI_ADDR, sizeof(data),
|
||||
&data);
|
||||
if (err < 0) {
|
||||
dev_err(hw->dev, "failed to read whoami register\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
if (data != st_lsm6dsx_sensor_settings[i].wai) {
|
||||
dev_err(hw->dev, "unsupported whoami [%02x]\n", data);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
hw->settings = &st_lsm6dsx_sensor_settings[i];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int st_lsm6dsx_set_full_scale(struct st_lsm6dsx_sensor *sensor,
|
||||
u32 gain)
|
||||
{
|
||||
enum st_lsm6dsx_sensor_id id = sensor->id;
|
||||
int i, err;
|
||||
u8 val;
|
||||
|
||||
for (i = 0; i < ST_LSM6DSX_FS_LIST_SIZE; i++)
|
||||
if (st_lsm6dsx_fs_table[id].fs_avl[i].gain == gain)
|
||||
break;
|
||||
|
||||
if (i == ST_LSM6DSX_FS_LIST_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
val = st_lsm6dsx_fs_table[id].fs_avl[i].val;
|
||||
err = st_lsm6dsx_write_with_mask(sensor->hw,
|
||||
st_lsm6dsx_fs_table[id].reg.addr,
|
||||
st_lsm6dsx_fs_table[id].reg.mask,
|
||||
val);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
sensor->gain = gain;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int st_lsm6dsx_set_odr(struct st_lsm6dsx_sensor *sensor, u16 odr)
|
||||
{
|
||||
enum st_lsm6dsx_sensor_id id = sensor->id;
|
||||
int i, err;
|
||||
u8 val;
|
||||
|
||||
for (i = 0; i < ST_LSM6DSX_ODR_LIST_SIZE; i++)
|
||||
if (st_lsm6dsx_odr_table[id].odr_avl[i].hz == odr)
|
||||
break;
|
||||
|
||||
if (i == ST_LSM6DSX_ODR_LIST_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
val = st_lsm6dsx_odr_table[id].odr_avl[i].val;
|
||||
err = st_lsm6dsx_write_with_mask(sensor->hw,
|
||||
st_lsm6dsx_odr_table[id].reg.addr,
|
||||
st_lsm6dsx_odr_table[id].reg.mask,
|
||||
val);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
sensor->odr = odr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int st_lsm6dsx_sensor_enable(struct st_lsm6dsx_sensor *sensor)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = st_lsm6dsx_set_odr(sensor, sensor->odr);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
sensor->hw->enable_mask |= BIT(sensor->id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int st_lsm6dsx_sensor_disable(struct st_lsm6dsx_sensor *sensor)
|
||||
{
|
||||
enum st_lsm6dsx_sensor_id id = sensor->id;
|
||||
int err;
|
||||
|
||||
err = st_lsm6dsx_write_with_mask(sensor->hw,
|
||||
st_lsm6dsx_odr_table[id].reg.addr,
|
||||
st_lsm6dsx_odr_table[id].reg.mask, 0);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
sensor->hw->enable_mask &= ~BIT(id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int st_lsm6dsx_read_oneshot(struct st_lsm6dsx_sensor *sensor,
|
||||
u8 addr, int *val)
|
||||
{
|
||||
int err, delay;
|
||||
__le16 data;
|
||||
|
||||
err = st_lsm6dsx_sensor_enable(sensor);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
delay = 1000000 / sensor->odr;
|
||||
usleep_range(delay, 2 * delay);
|
||||
|
||||
err = sensor->hw->tf->read(sensor->hw->dev, addr, sizeof(data),
|
||||
(u8 *)&data);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
st_lsm6dsx_sensor_disable(sensor);
|
||||
|
||||
*val = (s16)data;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
static int st_lsm6dsx_read_raw(struct iio_dev *iio_dev,
|
||||
struct iio_chan_spec const *ch,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = iio_device_claim_direct_mode(iio_dev);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
ret = st_lsm6dsx_read_oneshot(sensor, ch->address, val);
|
||||
iio_device_release_direct_mode(iio_dev);
|
||||
break;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
*val = sensor->odr;
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = 0;
|
||||
*val2 = sensor->gain;
|
||||
ret = IIO_VAL_INT_PLUS_MICRO;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int st_lsm6dsx_write_raw(struct iio_dev *iio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev);
|
||||
int err;
|
||||
|
||||
err = iio_device_claim_direct_mode(iio_dev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
err = st_lsm6dsx_set_full_scale(sensor, val2);
|
||||
break;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
err = st_lsm6dsx_set_odr(sensor, val);
|
||||
break;
|
||||
default:
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
iio_device_release_direct_mode(iio_dev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int st_lsm6dsx_set_watermark(struct iio_dev *iio_dev, unsigned int val)
|
||||
{
|
||||
struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev);
|
||||
struct st_lsm6dsx_hw *hw = sensor->hw;
|
||||
int err, max_fifo_len;
|
||||
|
||||
max_fifo_len = hw->settings->max_fifo_size / ST_LSM6DSX_SAMPLE_SIZE;
|
||||
if (val < 1 || val > max_fifo_len)
|
||||
return -EINVAL;
|
||||
|
||||
err = st_lsm6dsx_update_watermark(sensor, val);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
sensor->watermark = val;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
st_lsm6dsx_sysfs_sampling_frequency_avail(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct st_lsm6dsx_sensor *sensor = iio_priv(dev_get_drvdata(dev));
|
||||
enum st_lsm6dsx_sensor_id id = sensor->id;
|
||||
int i, len = 0;
|
||||
|
||||
for (i = 0; i < ST_LSM6DSX_ODR_LIST_SIZE; i++)
|
||||
len += scnprintf(buf + len, PAGE_SIZE - len, "%d ",
|
||||
st_lsm6dsx_odr_table[id].odr_avl[i].hz);
|
||||
buf[len - 1] = '\n';
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t st_lsm6dsx_sysfs_scale_avail(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct st_lsm6dsx_sensor *sensor = iio_priv(dev_get_drvdata(dev));
|
||||
enum st_lsm6dsx_sensor_id id = sensor->id;
|
||||
int i, len = 0;
|
||||
|
||||
for (i = 0; i < ST_LSM6DSX_FS_LIST_SIZE; i++)
|
||||
len += scnprintf(buf + len, PAGE_SIZE - len, "0.%06u ",
|
||||
st_lsm6dsx_fs_table[id].fs_avl[i].gain);
|
||||
buf[len - 1] = '\n';
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(st_lsm6dsx_sysfs_sampling_frequency_avail);
|
||||
static IIO_DEVICE_ATTR(in_accel_scale_available, 0444,
|
||||
st_lsm6dsx_sysfs_scale_avail, NULL, 0);
|
||||
static IIO_DEVICE_ATTR(in_anglvel_scale_available, 0444,
|
||||
st_lsm6dsx_sysfs_scale_avail, NULL, 0);
|
||||
|
||||
static struct attribute *st_lsm6dsx_acc_attributes[] = {
|
||||
&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
|
||||
&iio_dev_attr_in_accel_scale_available.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group st_lsm6dsx_acc_attribute_group = {
|
||||
.attrs = st_lsm6dsx_acc_attributes,
|
||||
};
|
||||
|
||||
static const struct iio_info st_lsm6dsx_acc_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.attrs = &st_lsm6dsx_acc_attribute_group,
|
||||
.read_raw = st_lsm6dsx_read_raw,
|
||||
.write_raw = st_lsm6dsx_write_raw,
|
||||
.hwfifo_set_watermark = st_lsm6dsx_set_watermark,
|
||||
};
|
||||
|
||||
static struct attribute *st_lsm6dsx_gyro_attributes[] = {
|
||||
&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
|
||||
&iio_dev_attr_in_anglvel_scale_available.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group st_lsm6dsx_gyro_attribute_group = {
|
||||
.attrs = st_lsm6dsx_gyro_attributes,
|
||||
};
|
||||
|
||||
static const struct iio_info st_lsm6dsx_gyro_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.attrs = &st_lsm6dsx_gyro_attribute_group,
|
||||
.read_raw = st_lsm6dsx_read_raw,
|
||||
.write_raw = st_lsm6dsx_write_raw,
|
||||
.hwfifo_set_watermark = st_lsm6dsx_set_watermark,
|
||||
};
|
||||
|
||||
static const unsigned long st_lsm6dsx_available_scan_masks[] = {0x7, 0x0};
|
||||
|
||||
static int st_lsm6dsx_of_get_drdy_pin(struct st_lsm6dsx_hw *hw, int *drdy_pin)
|
||||
{
|
||||
struct device_node *np = hw->dev->of_node;
|
||||
int err;
|
||||
|
||||
if (!np)
|
||||
return -EINVAL;
|
||||
|
||||
err = of_property_read_u32(np, "st,drdy-int-pin", drdy_pin);
|
||||
if (err == -ENODATA) {
|
||||
/* if the property has not been specified use default value */
|
||||
*drdy_pin = 1;
|
||||
err = 0;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int st_lsm6dsx_get_drdy_reg(struct st_lsm6dsx_hw *hw, u8 *drdy_reg)
|
||||
{
|
||||
int err = 0, drdy_pin;
|
||||
|
||||
if (st_lsm6dsx_of_get_drdy_pin(hw, &drdy_pin) < 0) {
|
||||
struct st_sensors_platform_data *pdata;
|
||||
struct device *dev = hw->dev;
|
||||
|
||||
pdata = (struct st_sensors_platform_data *)dev->platform_data;
|
||||
drdy_pin = pdata ? pdata->drdy_int_pin : 1;
|
||||
}
|
||||
|
||||
switch (drdy_pin) {
|
||||
case 1:
|
||||
*drdy_reg = ST_LSM6DSX_REG_INT1_ADDR;
|
||||
break;
|
||||
case 2:
|
||||
*drdy_reg = ST_LSM6DSX_REG_INT2_ADDR;
|
||||
break;
|
||||
default:
|
||||
dev_err(hw->dev, "unsupported data ready pin\n");
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int st_lsm6dsx_init_device(struct st_lsm6dsx_hw *hw)
|
||||
{
|
||||
u8 data, drdy_int_reg;
|
||||
int err;
|
||||
|
||||
data = ST_LSM6DSX_REG_RESET_MASK;
|
||||
err = hw->tf->write(hw->dev, ST_LSM6DSX_REG_RESET_ADDR, sizeof(data),
|
||||
&data);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
msleep(200);
|
||||
|
||||
/* latch interrupts */
|
||||
err = st_lsm6dsx_write_with_mask(hw, ST_LSM6DSX_REG_LIR_ADDR,
|
||||
ST_LSM6DSX_REG_LIR_MASK, 1);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* enable Block Data Update */
|
||||
err = st_lsm6dsx_write_with_mask(hw, ST_LSM6DSX_REG_BDU_ADDR,
|
||||
ST_LSM6DSX_REG_BDU_MASK, 1);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = st_lsm6dsx_write_with_mask(hw, ST_LSM6DSX_REG_ROUNDING_ADDR,
|
||||
ST_LSM6DSX_REG_ROUNDING_MASK, 1);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* enable FIFO watermak interrupt */
|
||||
err = st_lsm6dsx_get_drdy_reg(hw, &drdy_int_reg);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return st_lsm6dsx_write_with_mask(hw, drdy_int_reg,
|
||||
ST_LSM6DSX_REG_FIFO_FTH_IRQ_MASK, 1);
|
||||
}
|
||||
|
||||
static struct iio_dev *st_lsm6dsx_alloc_iiodev(struct st_lsm6dsx_hw *hw,
|
||||
enum st_lsm6dsx_sensor_id id)
|
||||
{
|
||||
struct st_lsm6dsx_sensor *sensor;
|
||||
struct iio_dev *iio_dev;
|
||||
|
||||
iio_dev = devm_iio_device_alloc(hw->dev, sizeof(*sensor));
|
||||
if (!iio_dev)
|
||||
return NULL;
|
||||
|
||||
iio_dev->modes = INDIO_DIRECT_MODE;
|
||||
iio_dev->dev.parent = hw->dev;
|
||||
iio_dev->available_scan_masks = st_lsm6dsx_available_scan_masks;
|
||||
|
||||
sensor = iio_priv(iio_dev);
|
||||
sensor->id = id;
|
||||
sensor->hw = hw;
|
||||
sensor->odr = st_lsm6dsx_odr_table[id].odr_avl[0].hz;
|
||||
sensor->gain = st_lsm6dsx_fs_table[id].fs_avl[0].gain;
|
||||
sensor->watermark = 1;
|
||||
|
||||
switch (id) {
|
||||
case ST_LSM6DSX_ID_ACC:
|
||||
iio_dev->channels = st_lsm6dsx_acc_channels;
|
||||
iio_dev->num_channels = ARRAY_SIZE(st_lsm6dsx_acc_channels);
|
||||
iio_dev->name = "lsm6dsx_accel";
|
||||
iio_dev->info = &st_lsm6dsx_acc_info;
|
||||
|
||||
sensor->decimator_mask = ST_LSM6DSX_REG_ACC_DEC_MASK;
|
||||
break;
|
||||
case ST_LSM6DSX_ID_GYRO:
|
||||
iio_dev->channels = st_lsm6dsx_gyro_channels;
|
||||
iio_dev->num_channels = ARRAY_SIZE(st_lsm6dsx_gyro_channels);
|
||||
iio_dev->name = "lsm6dsx_gyro";
|
||||
iio_dev->info = &st_lsm6dsx_gyro_info;
|
||||
|
||||
sensor->decimator_mask = ST_LSM6DSX_REG_GYRO_DEC_MASK;
|
||||
break;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return iio_dev;
|
||||
}
|
||||
|
||||
int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id,
|
||||
const struct st_lsm6dsx_transfer_function *tf_ops)
|
||||
{
|
||||
struct st_lsm6dsx_hw *hw;
|
||||
int i, err;
|
||||
|
||||
hw = devm_kzalloc(dev, sizeof(*hw), GFP_KERNEL);
|
||||
if (!hw)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_set_drvdata(dev, (void *)hw);
|
||||
|
||||
mutex_init(&hw->lock);
|
||||
mutex_init(&hw->fifo_lock);
|
||||
|
||||
hw->dev = dev;
|
||||
hw->irq = irq;
|
||||
hw->tf = tf_ops;
|
||||
|
||||
err = st_lsm6dsx_check_whoami(hw, hw_id);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
|
||||
hw->iio_devs[i] = st_lsm6dsx_alloc_iiodev(hw, i);
|
||||
if (!hw->iio_devs[i])
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
err = st_lsm6dsx_init_device(hw);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (hw->irq > 0) {
|
||||
err = st_lsm6dsx_fifo_setup(hw);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
|
||||
err = devm_iio_device_register(hw->dev, hw->iio_devs[i]);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(st_lsm6dsx_probe);
|
||||
|
||||
MODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi@st.com>");
|
||||
MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
|
||||
MODULE_DESCRIPTION("STMicroelectronics st_lsm6dsx driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
* STMicroelectronics st_lsm6dsx i2c driver
|
||||
*
|
||||
* Copyright 2016 STMicroelectronics Inc.
|
||||
*
|
||||
* Lorenzo Bianconi <lorenzo.bianconi@st.com>
|
||||
* Denis Ciocca <denis.ciocca@st.com>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include "st_lsm6dsx.h"
|
||||
|
||||
static int st_lsm6dsx_i2c_read(struct device *dev, u8 addr, int len, u8 *data)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct i2c_msg msg[2];
|
||||
|
||||
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 st_lsm6dsx_i2c_write(struct device *dev, u8 addr, int len, u8 *data)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct i2c_msg msg;
|
||||
u8 send[len + 1];
|
||||
|
||||
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 st_lsm6dsx_transfer_function st_lsm6dsx_transfer_fn = {
|
||||
.read = st_lsm6dsx_i2c_read,
|
||||
.write = st_lsm6dsx_i2c_write,
|
||||
};
|
||||
|
||||
static int st_lsm6dsx_i2c_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
return st_lsm6dsx_probe(&client->dev, client->irq,
|
||||
(int)id->driver_data,
|
||||
&st_lsm6dsx_transfer_fn);
|
||||
}
|
||||
|
||||
static const struct of_device_id st_lsm6dsx_i2c_of_match[] = {
|
||||
{
|
||||
.compatible = "st,lsm6ds3",
|
||||
.data = (void *)ST_LSM6DS3_ID,
|
||||
},
|
||||
{
|
||||
.compatible = "st,lsm6dsm",
|
||||
.data = (void *)ST_LSM6DSM_ID,
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, st_lsm6dsx_i2c_of_match);
|
||||
|
||||
static const struct i2c_device_id st_lsm6dsx_i2c_id_table[] = {
|
||||
{ ST_LSM6DS3_DEV_NAME, ST_LSM6DS3_ID },
|
||||
{ ST_LSM6DSM_DEV_NAME, ST_LSM6DSM_ID },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, st_lsm6dsx_i2c_id_table);
|
||||
|
||||
static struct i2c_driver st_lsm6dsx_driver = {
|
||||
.driver = {
|
||||
.name = "st_lsm6dsx_i2c",
|
||||
.of_match_table = of_match_ptr(st_lsm6dsx_i2c_of_match),
|
||||
},
|
||||
.probe = st_lsm6dsx_i2c_probe,
|
||||
.id_table = st_lsm6dsx_i2c_id_table,
|
||||
};
|
||||
module_i2c_driver(st_lsm6dsx_driver);
|
||||
|
||||
MODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi@st.com>");
|
||||
MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
|
||||
MODULE_DESCRIPTION("STMicroelectronics st_lsm6dsx i2c driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
* STMicroelectronics st_lsm6dsx spi driver
|
||||
*
|
||||
* Copyright 2016 STMicroelectronics Inc.
|
||||
*
|
||||
* Lorenzo Bianconi <lorenzo.bianconi@st.com>
|
||||
* Denis Ciocca <denis.ciocca@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 <linux/of.h>
|
||||
|
||||
#include "st_lsm6dsx.h"
|
||||
|
||||
#define SENSORS_SPI_READ BIT(7)
|
||||
|
||||
static int st_lsm6dsx_spi_read(struct device *dev, u8 addr, int len,
|
||||
u8 *data)
|
||||
{
|
||||
struct spi_device *spi = to_spi_device(dev);
|
||||
struct st_lsm6dsx_hw *hw = spi_get_drvdata(spi);
|
||||
int err;
|
||||
|
||||
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,
|
||||
}
|
||||
};
|
||||
|
||||
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 st_lsm6dsx_spi_write(struct device *dev, u8 addr, int len,
|
||||
u8 *data)
|
||||
{
|
||||
struct st_lsm6dsx_hw *hw;
|
||||
struct spi_device *spi;
|
||||
|
||||
if (len >= ST_LSM6DSX_TX_MAX_LENGTH)
|
||||
return -ENOMEM;
|
||||
|
||||
spi = to_spi_device(dev);
|
||||
hw = spi_get_drvdata(spi);
|
||||
|
||||
hw->tb.tx_buf[0] = addr;
|
||||
memcpy(&hw->tb.tx_buf[1], data, len);
|
||||
|
||||
return spi_write(spi, hw->tb.tx_buf, len + 1);
|
||||
}
|
||||
|
||||
static const struct st_lsm6dsx_transfer_function st_lsm6dsx_transfer_fn = {
|
||||
.read = st_lsm6dsx_spi_read,
|
||||
.write = st_lsm6dsx_spi_write,
|
||||
};
|
||||
|
||||
static int st_lsm6dsx_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
const struct spi_device_id *id = spi_get_device_id(spi);
|
||||
|
||||
return st_lsm6dsx_probe(&spi->dev, spi->irq,
|
||||
(int)id->driver_data,
|
||||
&st_lsm6dsx_transfer_fn);
|
||||
}
|
||||
|
||||
static const struct of_device_id st_lsm6dsx_spi_of_match[] = {
|
||||
{
|
||||
.compatible = "st,lsm6ds3",
|
||||
.data = (void *)ST_LSM6DS3_ID,
|
||||
},
|
||||
{
|
||||
.compatible = "st,lsm6dsm",
|
||||
.data = (void *)ST_LSM6DSM_ID,
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, st_lsm6dsx_spi_of_match);
|
||||
|
||||
static const struct spi_device_id st_lsm6dsx_spi_id_table[] = {
|
||||
{ ST_LSM6DS3_DEV_NAME, ST_LSM6DS3_ID },
|
||||
{ ST_LSM6DSM_DEV_NAME, ST_LSM6DSM_ID },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, st_lsm6dsx_spi_id_table);
|
||||
|
||||
static struct spi_driver st_lsm6dsx_driver = {
|
||||
.driver = {
|
||||
.name = "st_lsm6dsx_spi",
|
||||
.of_match_table = of_match_ptr(st_lsm6dsx_spi_of_match),
|
||||
},
|
||||
.probe = st_lsm6dsx_spi_probe,
|
||||
.id_table = st_lsm6dsx_spi_id_table,
|
||||
};
|
||||
module_spi_driver(st_lsm6dsx_driver);
|
||||
|
||||
MODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi@st.com>");
|
||||
MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
|
||||
MODULE_DESCRIPTION("STMicroelectronics st_lsm6dsx spi driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -26,6 +26,7 @@
|
|||
#include "iio_core.h"
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/buffer_impl.h>
|
||||
|
||||
static const char * const iio_endian_prefix[] = {
|
||||
[IIO_BE] = "be",
|
||||
|
@ -209,6 +210,18 @@ void iio_buffer_init(struct iio_buffer *buffer)
|
|||
}
|
||||
EXPORT_SYMBOL(iio_buffer_init);
|
||||
|
||||
/**
|
||||
* iio_buffer_set_attrs - Set buffer specific attributes
|
||||
* @buffer: The buffer for which we are setting attributes
|
||||
* @attrs: Pointer to a null terminated list of pointers to attributes
|
||||
*/
|
||||
void iio_buffer_set_attrs(struct iio_buffer *buffer,
|
||||
const struct attribute **attrs)
|
||||
{
|
||||
buffer->attrs = attrs;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iio_buffer_set_attrs);
|
||||
|
||||
static ssize_t iio_show_scan_index(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
|
@ -346,6 +359,19 @@ static int iio_scan_mask_clear(struct iio_buffer *buffer, int bit)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int iio_scan_mask_query(struct iio_dev *indio_dev,
|
||||
struct iio_buffer *buffer, int bit)
|
||||
{
|
||||
if (bit > indio_dev->masklength)
|
||||
return -EINVAL;
|
||||
|
||||
if (!buffer->scan_mask)
|
||||
return 0;
|
||||
|
||||
/* Ensure return value is 0 or 1. */
|
||||
return !!test_bit(bit, buffer->scan_mask);
|
||||
};
|
||||
|
||||
static ssize_t iio_scan_el_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf,
|
||||
|
@ -751,6 +777,135 @@ static int iio_verify_update(struct iio_dev *indio_dev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* struct iio_demux_table - table describing demux memcpy ops
|
||||
* @from: index to copy from
|
||||
* @to: index to copy to
|
||||
* @length: how many bytes to copy
|
||||
* @l: list head used for management
|
||||
*/
|
||||
struct iio_demux_table {
|
||||
unsigned from;
|
||||
unsigned to;
|
||||
unsigned length;
|
||||
struct list_head l;
|
||||
};
|
||||
|
||||
static void iio_buffer_demux_free(struct iio_buffer *buffer)
|
||||
{
|
||||
struct iio_demux_table *p, *q;
|
||||
list_for_each_entry_safe(p, q, &buffer->demux_list, l) {
|
||||
list_del(&p->l);
|
||||
kfree(p);
|
||||
}
|
||||
}
|
||||
|
||||
static int iio_buffer_add_demux(struct iio_buffer *buffer,
|
||||
struct iio_demux_table **p, unsigned int in_loc, unsigned int out_loc,
|
||||
unsigned int length)
|
||||
{
|
||||
|
||||
if (*p && (*p)->from + (*p)->length == in_loc &&
|
||||
(*p)->to + (*p)->length == out_loc) {
|
||||
(*p)->length += length;
|
||||
} else {
|
||||
*p = kmalloc(sizeof(**p), GFP_KERNEL);
|
||||
if (*p == NULL)
|
||||
return -ENOMEM;
|
||||
(*p)->from = in_loc;
|
||||
(*p)->to = out_loc;
|
||||
(*p)->length = length;
|
||||
list_add_tail(&(*p)->l, &buffer->demux_list);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iio_buffer_update_demux(struct iio_dev *indio_dev,
|
||||
struct iio_buffer *buffer)
|
||||
{
|
||||
int ret, in_ind = -1, out_ind, length;
|
||||
unsigned in_loc = 0, out_loc = 0;
|
||||
struct iio_demux_table *p = NULL;
|
||||
|
||||
/* Clear out any old demux */
|
||||
iio_buffer_demux_free(buffer);
|
||||
kfree(buffer->demux_bounce);
|
||||
buffer->demux_bounce = NULL;
|
||||
|
||||
/* First work out which scan mode we will actually have */
|
||||
if (bitmap_equal(indio_dev->active_scan_mask,
|
||||
buffer->scan_mask,
|
||||
indio_dev->masklength))
|
||||
return 0;
|
||||
|
||||
/* Now we have the two masks, work from least sig and build up sizes */
|
||||
for_each_set_bit(out_ind,
|
||||
buffer->scan_mask,
|
||||
indio_dev->masklength) {
|
||||
in_ind = find_next_bit(indio_dev->active_scan_mask,
|
||||
indio_dev->masklength,
|
||||
in_ind + 1);
|
||||
while (in_ind != out_ind) {
|
||||
in_ind = find_next_bit(indio_dev->active_scan_mask,
|
||||
indio_dev->masklength,
|
||||
in_ind + 1);
|
||||
length = iio_storage_bytes_for_si(indio_dev, in_ind);
|
||||
/* Make sure we are aligned */
|
||||
in_loc = roundup(in_loc, length) + length;
|
||||
}
|
||||
length = iio_storage_bytes_for_si(indio_dev, in_ind);
|
||||
out_loc = roundup(out_loc, length);
|
||||
in_loc = roundup(in_loc, length);
|
||||
ret = iio_buffer_add_demux(buffer, &p, in_loc, out_loc, length);
|
||||
if (ret)
|
||||
goto error_clear_mux_table;
|
||||
out_loc += length;
|
||||
in_loc += length;
|
||||
}
|
||||
/* Relies on scan_timestamp being last */
|
||||
if (buffer->scan_timestamp) {
|
||||
length = iio_storage_bytes_for_timestamp(indio_dev);
|
||||
out_loc = roundup(out_loc, length);
|
||||
in_loc = roundup(in_loc, length);
|
||||
ret = iio_buffer_add_demux(buffer, &p, in_loc, out_loc, length);
|
||||
if (ret)
|
||||
goto error_clear_mux_table;
|
||||
out_loc += length;
|
||||
in_loc += length;
|
||||
}
|
||||
buffer->demux_bounce = kzalloc(out_loc, GFP_KERNEL);
|
||||
if (buffer->demux_bounce == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto error_clear_mux_table;
|
||||
}
|
||||
return 0;
|
||||
|
||||
error_clear_mux_table:
|
||||
iio_buffer_demux_free(buffer);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int iio_update_demux(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct iio_buffer *buffer;
|
||||
int ret;
|
||||
|
||||
list_for_each_entry(buffer, &indio_dev->buffer_list, buffer_list) {
|
||||
ret = iio_buffer_update_demux(indio_dev, buffer);
|
||||
if (ret < 0)
|
||||
goto error_clear_mux_table;
|
||||
}
|
||||
return 0;
|
||||
|
||||
error_clear_mux_table:
|
||||
list_for_each_entry(buffer, &indio_dev->buffer_list, buffer_list)
|
||||
iio_buffer_demux_free(buffer);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int iio_enable_buffers(struct iio_dev *indio_dev,
|
||||
struct iio_device_config *config)
|
||||
{
|
||||
|
@ -1199,34 +1354,6 @@ bool iio_validate_scan_mask_onehot(struct iio_dev *indio_dev,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(iio_validate_scan_mask_onehot);
|
||||
|
||||
int iio_scan_mask_query(struct iio_dev *indio_dev,
|
||||
struct iio_buffer *buffer, int bit)
|
||||
{
|
||||
if (bit > indio_dev->masklength)
|
||||
return -EINVAL;
|
||||
|
||||
if (!buffer->scan_mask)
|
||||
return 0;
|
||||
|
||||
/* Ensure return value is 0 or 1. */
|
||||
return !!test_bit(bit, buffer->scan_mask);
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(iio_scan_mask_query);
|
||||
|
||||
/**
|
||||
* struct iio_demux_table - table describing demux memcpy ops
|
||||
* @from: index to copy from
|
||||
* @to: index to copy to
|
||||
* @length: how many bytes to copy
|
||||
* @l: list head used for management
|
||||
*/
|
||||
struct iio_demux_table {
|
||||
unsigned from;
|
||||
unsigned to;
|
||||
unsigned length;
|
||||
struct list_head l;
|
||||
};
|
||||
|
||||
static const void *iio_demux(struct iio_buffer *buffer,
|
||||
const void *datain)
|
||||
{
|
||||
|
@ -1258,16 +1385,11 @@ static int iio_push_to_buffer(struct iio_buffer *buffer, const void *data)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void iio_buffer_demux_free(struct iio_buffer *buffer)
|
||||
{
|
||||
struct iio_demux_table *p, *q;
|
||||
list_for_each_entry_safe(p, q, &buffer->demux_list, l) {
|
||||
list_del(&p->l);
|
||||
kfree(p);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* iio_push_to_buffers() - push to a registered buffer.
|
||||
* @indio_dev: iio_dev structure for device.
|
||||
* @data: Full scan.
|
||||
*/
|
||||
int iio_push_to_buffers(struct iio_dev *indio_dev, const void *data)
|
||||
{
|
||||
int ret;
|
||||
|
@ -1283,113 +1405,6 @@ int iio_push_to_buffers(struct iio_dev *indio_dev, const void *data)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(iio_push_to_buffers);
|
||||
|
||||
static int iio_buffer_add_demux(struct iio_buffer *buffer,
|
||||
struct iio_demux_table **p, unsigned int in_loc, unsigned int out_loc,
|
||||
unsigned int length)
|
||||
{
|
||||
|
||||
if (*p && (*p)->from + (*p)->length == in_loc &&
|
||||
(*p)->to + (*p)->length == out_loc) {
|
||||
(*p)->length += length;
|
||||
} else {
|
||||
*p = kmalloc(sizeof(**p), GFP_KERNEL);
|
||||
if (*p == NULL)
|
||||
return -ENOMEM;
|
||||
(*p)->from = in_loc;
|
||||
(*p)->to = out_loc;
|
||||
(*p)->length = length;
|
||||
list_add_tail(&(*p)->l, &buffer->demux_list);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iio_buffer_update_demux(struct iio_dev *indio_dev,
|
||||
struct iio_buffer *buffer)
|
||||
{
|
||||
int ret, in_ind = -1, out_ind, length;
|
||||
unsigned in_loc = 0, out_loc = 0;
|
||||
struct iio_demux_table *p = NULL;
|
||||
|
||||
/* Clear out any old demux */
|
||||
iio_buffer_demux_free(buffer);
|
||||
kfree(buffer->demux_bounce);
|
||||
buffer->demux_bounce = NULL;
|
||||
|
||||
/* First work out which scan mode we will actually have */
|
||||
if (bitmap_equal(indio_dev->active_scan_mask,
|
||||
buffer->scan_mask,
|
||||
indio_dev->masklength))
|
||||
return 0;
|
||||
|
||||
/* Now we have the two masks, work from least sig and build up sizes */
|
||||
for_each_set_bit(out_ind,
|
||||
buffer->scan_mask,
|
||||
indio_dev->masklength) {
|
||||
in_ind = find_next_bit(indio_dev->active_scan_mask,
|
||||
indio_dev->masklength,
|
||||
in_ind + 1);
|
||||
while (in_ind != out_ind) {
|
||||
in_ind = find_next_bit(indio_dev->active_scan_mask,
|
||||
indio_dev->masklength,
|
||||
in_ind + 1);
|
||||
length = iio_storage_bytes_for_si(indio_dev, in_ind);
|
||||
/* Make sure we are aligned */
|
||||
in_loc = roundup(in_loc, length) + length;
|
||||
}
|
||||
length = iio_storage_bytes_for_si(indio_dev, in_ind);
|
||||
out_loc = roundup(out_loc, length);
|
||||
in_loc = roundup(in_loc, length);
|
||||
ret = iio_buffer_add_demux(buffer, &p, in_loc, out_loc, length);
|
||||
if (ret)
|
||||
goto error_clear_mux_table;
|
||||
out_loc += length;
|
||||
in_loc += length;
|
||||
}
|
||||
/* Relies on scan_timestamp being last */
|
||||
if (buffer->scan_timestamp) {
|
||||
length = iio_storage_bytes_for_timestamp(indio_dev);
|
||||
out_loc = roundup(out_loc, length);
|
||||
in_loc = roundup(in_loc, length);
|
||||
ret = iio_buffer_add_demux(buffer, &p, in_loc, out_loc, length);
|
||||
if (ret)
|
||||
goto error_clear_mux_table;
|
||||
out_loc += length;
|
||||
in_loc += length;
|
||||
}
|
||||
buffer->demux_bounce = kzalloc(out_loc, GFP_KERNEL);
|
||||
if (buffer->demux_bounce == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto error_clear_mux_table;
|
||||
}
|
||||
return 0;
|
||||
|
||||
error_clear_mux_table:
|
||||
iio_buffer_demux_free(buffer);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int iio_update_demux(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct iio_buffer *buffer;
|
||||
int ret;
|
||||
|
||||
list_for_each_entry(buffer, &indio_dev->buffer_list, buffer_list) {
|
||||
ret = iio_buffer_update_demux(indio_dev, buffer);
|
||||
if (ret < 0)
|
||||
goto error_clear_mux_table;
|
||||
}
|
||||
return 0;
|
||||
|
||||
error_clear_mux_table:
|
||||
list_for_each_entry(buffer, &indio_dev->buffer_list, buffer_list)
|
||||
iio_buffer_demux_free(buffer);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iio_update_demux);
|
||||
|
||||
/**
|
||||
* iio_buffer_release() - Free a buffer's resources
|
||||
* @ref: Pointer to the kref embedded in the iio_buffer struct
|
||||
|
@ -1431,3 +1446,19 @@ void iio_buffer_put(struct iio_buffer *buffer)
|
|||
kref_put(&buffer->ref, iio_buffer_release);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iio_buffer_put);
|
||||
|
||||
/**
|
||||
* iio_device_attach_buffer - Attach a buffer to a IIO device
|
||||
* @indio_dev: The device the buffer should be attached to
|
||||
* @buffer: The buffer to attach to the device
|
||||
*
|
||||
* This function attaches a buffer to a IIO device. The buffer stays attached to
|
||||
* the device until the device is freed. The function should only be called at
|
||||
* most once per device.
|
||||
*/
|
||||
void iio_device_attach_buffer(struct iio_dev *indio_dev,
|
||||
struct iio_buffer *buffer)
|
||||
{
|
||||
indio_dev->buffer = iio_buffer_get(buffer);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iio_device_attach_buffer);
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/events.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/buffer_impl.h>
|
||||
|
||||
/* IDA to assign each registered device a unique id */
|
||||
static DEFINE_IDA(iio_ida);
|
||||
|
@ -83,6 +84,7 @@ static const char * const iio_chan_type_name_spec[] = {
|
|||
[IIO_ELECTRICALCONDUCTIVITY] = "electricalconductivity",
|
||||
[IIO_COUNT] = "count",
|
||||
[IIO_INDEX] = "index",
|
||||
[IIO_GRAVITY] = "gravity",
|
||||
};
|
||||
|
||||
static const char * const iio_modifier_names[] = {
|
||||
|
|
|
@ -147,8 +147,7 @@ static struct iio_trigger *__iio_trigger_find_by_name(const char *name)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static struct iio_trigger *iio_trigger_find_by_name(const char *name,
|
||||
size_t len)
|
||||
static struct iio_trigger *iio_trigger_acquire_by_name(const char *name)
|
||||
{
|
||||
struct iio_trigger *trig = NULL, *iter;
|
||||
|
||||
|
@ -156,6 +155,7 @@ static struct iio_trigger *iio_trigger_find_by_name(const char *name,
|
|||
list_for_each_entry(iter, &iio_trigger_list, list)
|
||||
if (sysfs_streq(iter->name, name)) {
|
||||
trig = iter;
|
||||
iio_trigger_get(trig);
|
||||
break;
|
||||
}
|
||||
mutex_unlock(&iio_trigger_list_lock);
|
||||
|
@ -416,20 +416,22 @@ static ssize_t iio_trigger_write_current(struct device *dev,
|
|||
}
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
trig = iio_trigger_find_by_name(buf, len);
|
||||
if (oldtrig == trig)
|
||||
return len;
|
||||
trig = iio_trigger_acquire_by_name(buf);
|
||||
if (oldtrig == trig) {
|
||||
ret = len;
|
||||
goto out_trigger_put;
|
||||
}
|
||||
|
||||
if (trig && indio_dev->info->validate_trigger) {
|
||||
ret = indio_dev->info->validate_trigger(indio_dev, trig);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto out_trigger_put;
|
||||
}
|
||||
|
||||
if (trig && trig->ops->validate_device) {
|
||||
ret = trig->ops->validate_device(trig, indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto out_trigger_put;
|
||||
}
|
||||
|
||||
indio_dev->trig = trig;
|
||||
|
@ -441,13 +443,16 @@ static ssize_t iio_trigger_write_current(struct device *dev,
|
|||
iio_trigger_put(oldtrig);
|
||||
}
|
||||
if (indio_dev->trig) {
|
||||
iio_trigger_get(indio_dev->trig);
|
||||
if (indio_dev->modes & INDIO_EVENT_TRIGGERED)
|
||||
iio_trigger_attach_poll_func(indio_dev->trig,
|
||||
indio_dev->pollfunc_event);
|
||||
}
|
||||
|
||||
return len;
|
||||
|
||||
out_trigger_put:
|
||||
iio_trigger_put(trig);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(current_trigger, S_IRUGO | S_IWUSR,
|
||||
|
@ -487,7 +492,7 @@ static void iio_trig_release(struct device *device)
|
|||
kfree(trig);
|
||||
}
|
||||
|
||||
static struct device_type iio_trig_type = {
|
||||
static const struct device_type iio_trig_type = {
|
||||
.release = iio_trig_release,
|
||||
.groups = iio_trig_dev_groups,
|
||||
};
|
||||
|
@ -513,46 +518,45 @@ static void iio_trig_subirqunmask(struct irq_data *d)
|
|||
static struct iio_trigger *viio_trigger_alloc(const char *fmt, va_list vargs)
|
||||
{
|
||||
struct iio_trigger *trig;
|
||||
int i;
|
||||
|
||||
trig = kzalloc(sizeof *trig, GFP_KERNEL);
|
||||
if (trig) {
|
||||
int i;
|
||||
trig->dev.type = &iio_trig_type;
|
||||
trig->dev.bus = &iio_bus_type;
|
||||
device_initialize(&trig->dev);
|
||||
if (!trig)
|
||||
return NULL;
|
||||
|
||||
mutex_init(&trig->pool_lock);
|
||||
trig->subirq_base
|
||||
= irq_alloc_descs(-1, 0,
|
||||
CONFIG_IIO_CONSUMERS_PER_TRIGGER,
|
||||
0);
|
||||
if (trig->subirq_base < 0) {
|
||||
kfree(trig);
|
||||
return NULL;
|
||||
}
|
||||
trig->dev.type = &iio_trig_type;
|
||||
trig->dev.bus = &iio_bus_type;
|
||||
device_initialize(&trig->dev);
|
||||
|
||||
trig->name = kvasprintf(GFP_KERNEL, fmt, vargs);
|
||||
if (trig->name == NULL) {
|
||||
irq_free_descs(trig->subirq_base,
|
||||
CONFIG_IIO_CONSUMERS_PER_TRIGGER);
|
||||
kfree(trig);
|
||||
return NULL;
|
||||
}
|
||||
trig->subirq_chip.name = trig->name;
|
||||
trig->subirq_chip.irq_mask = &iio_trig_subirqmask;
|
||||
trig->subirq_chip.irq_unmask = &iio_trig_subirqunmask;
|
||||
for (i = 0; i < CONFIG_IIO_CONSUMERS_PER_TRIGGER; i++) {
|
||||
irq_set_chip(trig->subirq_base + i,
|
||||
&trig->subirq_chip);
|
||||
irq_set_handler(trig->subirq_base + i,
|
||||
&handle_simple_irq);
|
||||
irq_modify_status(trig->subirq_base + i,
|
||||
IRQ_NOREQUEST | IRQ_NOAUTOEN,
|
||||
IRQ_NOPROBE);
|
||||
}
|
||||
get_device(&trig->dev);
|
||||
mutex_init(&trig->pool_lock);
|
||||
trig->subirq_base = irq_alloc_descs(-1, 0,
|
||||
CONFIG_IIO_CONSUMERS_PER_TRIGGER,
|
||||
0);
|
||||
if (trig->subirq_base < 0)
|
||||
goto free_trig;
|
||||
|
||||
trig->name = kvasprintf(GFP_KERNEL, fmt, vargs);
|
||||
if (trig->name == NULL)
|
||||
goto free_descs;
|
||||
|
||||
trig->subirq_chip.name = trig->name;
|
||||
trig->subirq_chip.irq_mask = &iio_trig_subirqmask;
|
||||
trig->subirq_chip.irq_unmask = &iio_trig_subirqunmask;
|
||||
for (i = 0; i < CONFIG_IIO_CONSUMERS_PER_TRIGGER; i++) {
|
||||
irq_set_chip(trig->subirq_base + i, &trig->subirq_chip);
|
||||
irq_set_handler(trig->subirq_base + i, &handle_simple_irq);
|
||||
irq_modify_status(trig->subirq_base + i,
|
||||
IRQ_NOREQUEST | IRQ_NOAUTOEN, IRQ_NOPROBE);
|
||||
}
|
||||
get_device(&trig->dev);
|
||||
|
||||
return trig;
|
||||
|
||||
free_descs:
|
||||
irq_free_descs(trig->subirq_base, CONFIG_IIO_CONSUMERS_PER_TRIGGER);
|
||||
free_trig:
|
||||
kfree(trig);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct iio_trigger *iio_trigger_alloc(const char *fmt, ...)
|
||||
|
|
|
@ -601,8 +601,14 @@ static int iio_convert_raw_to_processed_unlocked(struct iio_channel *chan,
|
|||
|
||||
scale_type = iio_channel_read(chan, &scale_val, &scale_val2,
|
||||
IIO_CHAN_INFO_SCALE);
|
||||
if (scale_type < 0)
|
||||
return scale_type;
|
||||
if (scale_type < 0) {
|
||||
/*
|
||||
* Just pass raw values as processed if no scaling is
|
||||
* available.
|
||||
*/
|
||||
*processed = raw;
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (scale_type) {
|
||||
case IIO_VAL_INT:
|
||||
|
|
|
@ -115,6 +115,16 @@ config CM3323
|
|||
To compile this driver as a module, choose M here: the module will
|
||||
be called cm3323.
|
||||
|
||||
config CM3605
|
||||
tristate "Capella CM3605 ambient light and proximity sensor"
|
||||
depends on OF
|
||||
help
|
||||
Say Y here if you want to build a driver for Capella CM3605
|
||||
ambient light and short range proximity sensor.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will
|
||||
be called cm3605.
|
||||
|
||||
config CM36651
|
||||
depends on I2C
|
||||
tristate "CM36651 driver"
|
||||
|
|
|
@ -13,6 +13,7 @@ obj-$(CONFIG_BH1780) += bh1780.o
|
|||
obj-$(CONFIG_CM32181) += cm32181.o
|
||||
obj-$(CONFIG_CM3232) += cm3232.o
|
||||
obj-$(CONFIG_CM3323) += cm3323.o
|
||||
obj-$(CONFIG_CM3605) += cm3605.o
|
||||
obj-$(CONFIG_CM36651) += cm36651.o
|
||||
obj-$(CONFIG_GP2AP020A00F) += gp2ap020a00f.o
|
||||
obj-$(CONFIG_HID_SENSOR_ALS) += hid-sensor-als.o
|
||||
|
|
|
@ -119,7 +119,7 @@ static int cm3232_reg_init(struct cm3232_chip *chip)
|
|||
if (ret < 0)
|
||||
dev_err(&chip->client->dev, "Error writing reg_cmd\n");
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,330 @@
|
|||
/*
|
||||
* CM3605 Ambient Light and Proximity Sensor
|
||||
*
|
||||
* Copyright (C) 2016 Linaro Ltd.
|
||||
* Author: Linus Walleij <linus.walleij@linaro.org>
|
||||
*
|
||||
* This hardware was found in the very first Nexus One handset from Google/HTC
|
||||
* and an early endavour into mobile light and proximity sensors.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/events.h>
|
||||
#include <linux/iio/consumer.h> /* To get our ADC channel */
|
||||
#include <linux/iio/types.h> /* To deal with our ADC channel */
|
||||
#include <linux/init.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/math64.h>
|
||||
#include <linux/pm.h>
|
||||
|
||||
#define CM3605_PROX_CHANNEL 0
|
||||
#define CM3605_ALS_CHANNEL 1
|
||||
#define CM3605_AOUT_TYP_MAX_MV 1550
|
||||
/* It should not go above 1.650V according to the data sheet */
|
||||
#define CM3605_AOUT_MAX_MV 1650
|
||||
|
||||
/**
|
||||
* struct cm3605 - CM3605 state
|
||||
* @dev: pointer to parent device
|
||||
* @vdd: regulator controlling VDD
|
||||
* @aset: sleep enable GPIO, high = sleep
|
||||
* @aout: IIO ADC channel to convert the AOUT signal
|
||||
* @als_max: maximum LUX detection (depends on RSET)
|
||||
* @dir: proximity direction: start as FALLING
|
||||
* @led: trigger for the infrared LED used by the proximity sensor
|
||||
*/
|
||||
struct cm3605 {
|
||||
struct device *dev;
|
||||
struct regulator *vdd;
|
||||
struct gpio_desc *aset;
|
||||
struct iio_channel *aout;
|
||||
s32 als_max;
|
||||
enum iio_event_direction dir;
|
||||
struct led_trigger *led;
|
||||
};
|
||||
|
||||
static irqreturn_t cm3605_prox_irq(int irq, void *d)
|
||||
{
|
||||
struct iio_dev *indio_dev = d;
|
||||
struct cm3605 *cm3605 = iio_priv(indio_dev);
|
||||
u64 ev;
|
||||
|
||||
ev = IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, CM3605_PROX_CHANNEL,
|
||||
IIO_EV_TYPE_THRESH, cm3605->dir);
|
||||
iio_push_event(indio_dev, ev, iio_get_time_ns(indio_dev));
|
||||
|
||||
/* Invert the edge for each event */
|
||||
if (cm3605->dir == IIO_EV_DIR_RISING)
|
||||
cm3605->dir = IIO_EV_DIR_FALLING;
|
||||
else
|
||||
cm3605->dir = IIO_EV_DIR_RISING;
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int cm3605_get_lux(struct cm3605 *cm3605)
|
||||
{
|
||||
int ret, res;
|
||||
s64 lux;
|
||||
|
||||
ret = iio_read_channel_processed(cm3605->aout, &res);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
dev_dbg(cm3605->dev, "read %d mV from ADC\n", res);
|
||||
|
||||
/*
|
||||
* AOUT has an offset of ~30mV then linear at dark
|
||||
* then goes from 2.54 up to 650 LUX yielding 1.55V
|
||||
* (1550 mV) so scale the returned value to this interval
|
||||
* using simple linear interpolation.
|
||||
*/
|
||||
if (res < 30)
|
||||
return 0;
|
||||
if (res > CM3605_AOUT_MAX_MV)
|
||||
dev_err(cm3605->dev, "device out of range\n");
|
||||
|
||||
/* Remove bias */
|
||||
lux = res - 30;
|
||||
|
||||
/* Linear interpolation between 0 and ALS typ max */
|
||||
lux *= cm3605->als_max;
|
||||
lux = div64_s64(lux, CM3605_AOUT_TYP_MAX_MV);
|
||||
|
||||
return lux;
|
||||
}
|
||||
|
||||
static int cm3605_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct cm3605 *cm3605 = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
switch (chan->type) {
|
||||
case IIO_LIGHT:
|
||||
ret = cm3605_get_lux(cm3605);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = ret;
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct iio_info cm3605_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = cm3605_read_raw,
|
||||
};
|
||||
|
||||
static const struct iio_event_spec cm3605_events[] = {
|
||||
{
|
||||
.type = IIO_EV_TYPE_THRESH,
|
||||
.dir = IIO_EV_DIR_EITHER,
|
||||
.mask_separate = BIT(IIO_EV_INFO_ENABLE),
|
||||
},
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec cm3605_channels[] = {
|
||||
{
|
||||
.type = IIO_PROXIMITY,
|
||||
.event_spec = cm3605_events,
|
||||
.num_event_specs = ARRAY_SIZE(cm3605_events),
|
||||
},
|
||||
{
|
||||
.type = IIO_LIGHT,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.channel = CM3605_ALS_CHANNEL,
|
||||
},
|
||||
};
|
||||
|
||||
static int cm3605_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct cm3605 *cm3605;
|
||||
struct iio_dev *indio_dev;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
enum iio_chan_type ch_type;
|
||||
u32 rset;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(dev, sizeof(*cm3605));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
platform_set_drvdata(pdev, indio_dev);
|
||||
|
||||
cm3605 = iio_priv(indio_dev);
|
||||
cm3605->dev = dev;
|
||||
cm3605->dir = IIO_EV_DIR_FALLING;
|
||||
|
||||
ret = of_property_read_u32(np, "capella,aset-resistance-ohms", &rset);
|
||||
if (ret) {
|
||||
dev_info(dev, "no RSET specified, assuming 100K\n");
|
||||
rset = 100000;
|
||||
}
|
||||
switch (rset) {
|
||||
case 50000:
|
||||
cm3605->als_max = 650;
|
||||
break;
|
||||
case 100000:
|
||||
cm3605->als_max = 300;
|
||||
break;
|
||||
case 300000:
|
||||
cm3605->als_max = 100;
|
||||
break;
|
||||
case 600000:
|
||||
cm3605->als_max = 50;
|
||||
break;
|
||||
default:
|
||||
dev_info(dev, "non-standard resistance\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
cm3605->aout = devm_iio_channel_get(dev, "aout");
|
||||
if (IS_ERR(cm3605->aout)) {
|
||||
if (PTR_ERR(cm3605->aout) == -ENODEV) {
|
||||
dev_err(dev, "no ADC, deferring...\n");
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
dev_err(dev, "failed to get AOUT ADC channel\n");
|
||||
return PTR_ERR(cm3605->aout);
|
||||
}
|
||||
ret = iio_get_channel_type(cm3605->aout, &ch_type);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ch_type != IIO_VOLTAGE) {
|
||||
dev_err(dev, "wrong type of IIO channel specified for AOUT\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
cm3605->vdd = devm_regulator_get(dev, "vdd");
|
||||
if (IS_ERR(cm3605->vdd)) {
|
||||
dev_err(dev, "failed to get VDD regulator\n");
|
||||
return PTR_ERR(cm3605->vdd);
|
||||
}
|
||||
ret = regulator_enable(cm3605->vdd);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to enable VDD regulator\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
cm3605->aset = devm_gpiod_get(dev, "aset", GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(cm3605->aset)) {
|
||||
dev_err(dev, "no ASET GPIO\n");
|
||||
ret = PTR_ERR(cm3605->aset);
|
||||
goto out_disable_vdd;
|
||||
}
|
||||
|
||||
ret = devm_request_threaded_irq(dev, platform_get_irq(pdev, 0),
|
||||
cm3605_prox_irq, NULL, 0, "cm3605", indio_dev);
|
||||
if (ret) {
|
||||
dev_err(dev, "unable to request IRQ\n");
|
||||
goto out_disable_aset;
|
||||
}
|
||||
|
||||
/* Just name the trigger the same as the driver */
|
||||
led_trigger_register_simple("cm3605", &cm3605->led);
|
||||
led_trigger_event(cm3605->led, LED_FULL);
|
||||
|
||||
indio_dev->dev.parent = dev;
|
||||
indio_dev->info = &cm3605_info;
|
||||
indio_dev->name = "cm3605";
|
||||
indio_dev->channels = cm3605_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(cm3605_channels);
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto out_remove_trigger;
|
||||
dev_info(dev, "Capella Microsystems CM3605 enabled range 0..%d LUX\n",
|
||||
cm3605->als_max);
|
||||
|
||||
return 0;
|
||||
|
||||
out_remove_trigger:
|
||||
led_trigger_event(cm3605->led, LED_OFF);
|
||||
led_trigger_unregister_simple(cm3605->led);
|
||||
out_disable_aset:
|
||||
gpiod_set_value_cansleep(cm3605->aset, 0);
|
||||
out_disable_vdd:
|
||||
regulator_disable(cm3605->vdd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cm3605_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
struct cm3605 *cm3605 = iio_priv(indio_dev);
|
||||
|
||||
led_trigger_event(cm3605->led, LED_OFF);
|
||||
led_trigger_unregister_simple(cm3605->led);
|
||||
gpiod_set_value_cansleep(cm3605->aset, 0);
|
||||
iio_device_unregister(indio_dev);
|
||||
regulator_disable(cm3605->vdd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused cm3605_pm_suspend(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct cm3605 *cm3605 = iio_priv(indio_dev);
|
||||
|
||||
led_trigger_event(cm3605->led, LED_OFF);
|
||||
regulator_disable(cm3605->vdd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused cm3605_pm_resume(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct cm3605 *cm3605 = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = regulator_enable(cm3605->vdd);
|
||||
if (ret)
|
||||
dev_err(dev, "failed to enable regulator in resume path\n");
|
||||
led_trigger_event(cm3605->led, LED_FULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops cm3605_dev_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(cm3605_pm_suspend,
|
||||
cm3605_pm_resume)
|
||||
};
|
||||
|
||||
static const struct of_device_id cm3605_of_match[] = {
|
||||
{.compatible = "capella,cm3605"},
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, cm3605_of_match);
|
||||
|
||||
static struct platform_driver cm3605_driver = {
|
||||
.driver = {
|
||||
.name = "cm3605",
|
||||
.of_match_table = cm3605_of_match,
|
||||
.pm = &cm3605_dev_pm_ops,
|
||||
},
|
||||
.probe = cm3605_probe,
|
||||
.remove = cm3605_remove,
|
||||
};
|
||||
module_platform_driver(cm3605_driver);
|
||||
|
||||
MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");
|
||||
MODULE_DESCRIPTION("CM3605 ambient light and proximity sensor driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -31,13 +31,17 @@
|
|||
#include <linux/iio/triggered_buffer.h>
|
||||
#include "../common/hid-sensors/hid-sensor-trigger.h"
|
||||
|
||||
#define CHANNEL_SCAN_INDEX_ILLUM 0
|
||||
enum {
|
||||
CHANNEL_SCAN_INDEX_INTENSITY = 0,
|
||||
CHANNEL_SCAN_INDEX_ILLUM = 1,
|
||||
CHANNEL_SCAN_INDEX_MAX
|
||||
};
|
||||
|
||||
struct als_state {
|
||||
struct hid_sensor_hub_callbacks callbacks;
|
||||
struct hid_sensor_common common_attributes;
|
||||
struct hid_sensor_hub_attribute_info als_illum;
|
||||
u32 illum;
|
||||
u32 illum[CHANNEL_SCAN_INDEX_MAX];
|
||||
int scale_pre_decml;
|
||||
int scale_post_decml;
|
||||
int scale_precision;
|
||||
|
@ -55,6 +59,15 @@ static const struct iio_chan_spec als_channels[] = {
|
|||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
|
||||
BIT(IIO_CHAN_INFO_HYSTERESIS),
|
||||
.scan_index = CHANNEL_SCAN_INDEX_INTENSITY,
|
||||
},
|
||||
{
|
||||
.type = IIO_LIGHT,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
|
||||
BIT(IIO_CHAN_INFO_HYSTERESIS),
|
||||
.scan_index = CHANNEL_SCAN_INDEX_ILLUM,
|
||||
}
|
||||
};
|
||||
|
@ -86,6 +99,7 @@ static int als_read_raw(struct iio_dev *indio_dev,
|
|||
switch (mask) {
|
||||
case 0:
|
||||
switch (chan->scan_index) {
|
||||
case CHANNEL_SCAN_INDEX_INTENSITY:
|
||||
case CHANNEL_SCAN_INDEX_ILLUM:
|
||||
report_id = als_state->als_illum.report_id;
|
||||
address =
|
||||
|
@ -202,10 +216,12 @@ static int als_capture_sample(struct hid_sensor_hub_device *hsdev,
|
|||
struct iio_dev *indio_dev = platform_get_drvdata(priv);
|
||||
struct als_state *als_state = iio_priv(indio_dev);
|
||||
int ret = -EINVAL;
|
||||
u32 sample_data = *(u32 *)raw_data;
|
||||
|
||||
switch (usage_id) {
|
||||
case HID_USAGE_SENSOR_LIGHT_ILLUM:
|
||||
als_state->illum = *(u32 *)raw_data;
|
||||
als_state->illum[CHANNEL_SCAN_INDEX_INTENSITY] = sample_data;
|
||||
als_state->illum[CHANNEL_SCAN_INDEX_ILLUM] = sample_data;
|
||||
ret = 0;
|
||||
break;
|
||||
default:
|
||||
|
@ -230,6 +246,8 @@ static int als_parse_report(struct platform_device *pdev,
|
|||
&st->als_illum);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
als_adjust_channel_bit_mask(channels, CHANNEL_SCAN_INDEX_INTENSITY,
|
||||
st->als_illum.size);
|
||||
als_adjust_channel_bit_mask(channels, CHANNEL_SCAN_INDEX_ILLUM,
|
||||
st->als_illum.size);
|
||||
|
||||
|
|
|
@ -840,6 +840,7 @@ static const struct of_device_id opt3001_of_match[] = {
|
|||
{ .compatible = "ti,opt3001" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, opt3001_of_match);
|
||||
|
||||
static struct i2c_driver opt3001_driver = {
|
||||
.probe = opt3001_probe,
|
||||
|
|
|
@ -278,13 +278,9 @@ static int ak8974_await_drdy(struct ak8974 *ak8974)
|
|||
if (val & AK8974_STATUS_DRDY)
|
||||
return 0;
|
||||
} while (--timeout);
|
||||
if (!timeout) {
|
||||
dev_err(&ak8974->i2c->dev,
|
||||
"timeout waiting for DRDY\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
dev_err(&ak8974->i2c->dev, "timeout waiting for DRDY\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static int ak8974_getresult(struct ak8974 *ak8974, __le16 *result)
|
||||
|
|
|
@ -222,29 +222,39 @@ static int mag3110_write_raw(struct iio_dev *indio_dev,
|
|||
int val, int val2, long mask)
|
||||
{
|
||||
struct mag3110_data *data = iio_priv(indio_dev);
|
||||
int rate;
|
||||
int rate, 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:
|
||||
rate = mag3110_get_samp_freq_index(data, val, val2);
|
||||
if (rate < 0)
|
||||
return -EINVAL;
|
||||
if (rate < 0) {
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
data->ctrl_reg1 &= ~MAG3110_CTRL_DR_MASK;
|
||||
data->ctrl_reg1 |= rate << MAG3110_CTRL_DR_SHIFT;
|
||||
return i2c_smbus_write_byte_data(data->client,
|
||||
ret = i2c_smbus_write_byte_data(data->client,
|
||||
MAG3110_CTRL_REG1, data->ctrl_reg1);
|
||||
break;
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
if (val < -10000 || val > 10000)
|
||||
return -EINVAL;
|
||||
return i2c_smbus_write_word_swapped(data->client,
|
||||
if (val < -10000 || val > 10000) {
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
ret = i2c_smbus_write_word_swapped(data->client,
|
||||
MAG3110_OFF_X + 2 * chan->scan_index, val << 1);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static irqreturn_t mag3110_trigger_handler(int irq, void *p)
|
||||
|
|
|
@ -15,6 +15,17 @@ config DS1803
|
|||
To compile this driver as a module, choose M here: the
|
||||
module will be called ds1803.
|
||||
|
||||
config MAX5481
|
||||
tristate "Maxim MAX5481-MAX5484 Digital Potentiometer driver"
|
||||
depends on SPI
|
||||
help
|
||||
Say yes here to build support for the Maxim
|
||||
MAX5481, MAX5482, MAX5483, MAX5484 digital potentiometer
|
||||
chips.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called max5481.
|
||||
|
||||
config MAX5487
|
||||
tristate "Maxim MAX5487/MAX5488/MAX5489 Digital Potentiometer driver"
|
||||
depends on SPI
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
# When adding new entries keep the list in alphabetical order
|
||||
obj-$(CONFIG_DS1803) += ds1803.o
|
||||
obj-$(CONFIG_MAX5481) += max5481.o
|
||||
obj-$(CONFIG_MAX5487) += max5487.o
|
||||
obj-$(CONFIG_MCP4131) += mcp4131.o
|
||||
obj-$(CONFIG_MCP4531) += mcp4531.o
|
||||
|
|
|
@ -0,0 +1,223 @@
|
|||
/*
|
||||
* Maxim Integrated MAX5481-MAX5484 digital potentiometer driver
|
||||
* Copyright 2016 Rockwell Collins
|
||||
*
|
||||
* Datasheet:
|
||||
* http://datasheets.maximintegrated.com/en/ds/MAX5481-MAX5484.pdf
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the gnu general public license version 2 as
|
||||
* published by the free software foundation.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
/* write wiper reg */
|
||||
#define MAX5481_WRITE_WIPER (0 << 4)
|
||||
/* copy wiper reg to NV reg */
|
||||
#define MAX5481_COPY_AB_TO_NV (2 << 4)
|
||||
/* copy NV reg to wiper reg */
|
||||
#define MAX5481_COPY_NV_TO_AB (3 << 4)
|
||||
|
||||
#define MAX5481_MAX_POS 1023
|
||||
|
||||
enum max5481_variant {
|
||||
max5481,
|
||||
max5482,
|
||||
max5483,
|
||||
max5484,
|
||||
};
|
||||
|
||||
struct max5481_cfg {
|
||||
int kohms;
|
||||
};
|
||||
|
||||
static const struct max5481_cfg max5481_cfg[] = {
|
||||
[max5481] = { .kohms = 10, },
|
||||
[max5482] = { .kohms = 50, },
|
||||
[max5483] = { .kohms = 10, },
|
||||
[max5484] = { .kohms = 50, },
|
||||
};
|
||||
|
||||
struct max5481_data {
|
||||
struct spi_device *spi;
|
||||
const struct max5481_cfg *cfg;
|
||||
u8 msg[3] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
#define MAX5481_CHANNEL { \
|
||||
.type = IIO_RESISTANCE, \
|
||||
.indexed = 1, \
|
||||
.output = 1, \
|
||||
.channel = 0, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec max5481_channels[] = {
|
||||
MAX5481_CHANNEL,
|
||||
};
|
||||
|
||||
static int max5481_write_cmd(struct max5481_data *data, u8 cmd, u16 val)
|
||||
{
|
||||
struct spi_device *spi = data->spi;
|
||||
|
||||
data->msg[0] = cmd;
|
||||
|
||||
switch (cmd) {
|
||||
case MAX5481_WRITE_WIPER:
|
||||
data->msg[1] = val >> 2;
|
||||
data->msg[2] = (val & 0x3) << 6;
|
||||
return spi_write(spi, data->msg, 3);
|
||||
|
||||
case MAX5481_COPY_AB_TO_NV:
|
||||
case MAX5481_COPY_NV_TO_AB:
|
||||
return spi_write(spi, data->msg, 1);
|
||||
|
||||
default:
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
static int max5481_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct max5481_data *data = iio_priv(indio_dev);
|
||||
|
||||
if (mask != IIO_CHAN_INFO_SCALE)
|
||||
return -EINVAL;
|
||||
|
||||
*val = 1000 * data->cfg->kohms;
|
||||
*val2 = MAX5481_MAX_POS;
|
||||
|
||||
return IIO_VAL_FRACTIONAL;
|
||||
}
|
||||
|
||||
static int max5481_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
struct max5481_data *data = iio_priv(indio_dev);
|
||||
|
||||
if (mask != IIO_CHAN_INFO_RAW)
|
||||
return -EINVAL;
|
||||
|
||||
if (val < 0 || val > MAX5481_MAX_POS)
|
||||
return -EINVAL;
|
||||
|
||||
return max5481_write_cmd(data, MAX5481_WRITE_WIPER, val);
|
||||
}
|
||||
|
||||
static const struct iio_info max5481_info = {
|
||||
.read_raw = max5481_read_raw,
|
||||
.write_raw = max5481_write_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
#if defined(CONFIG_OF)
|
||||
static const struct of_device_id max5481_match[] = {
|
||||
{ .compatible = "maxim,max5481", .data = &max5481_cfg[max5481] },
|
||||
{ .compatible = "maxim,max5482", .data = &max5481_cfg[max5482] },
|
||||
{ .compatible = "maxim,max5483", .data = &max5481_cfg[max5483] },
|
||||
{ .compatible = "maxim,max5484", .data = &max5481_cfg[max5484] },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, max5481_match);
|
||||
#endif
|
||||
|
||||
static int max5481_probe(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct max5481_data *data;
|
||||
const struct spi_device_id *id = spi_get_device_id(spi);
|
||||
const struct of_device_id *match;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_set_drvdata(&spi->dev, indio_dev);
|
||||
data = iio_priv(indio_dev);
|
||||
|
||||
data->spi = spi;
|
||||
|
||||
match = of_match_device(of_match_ptr(max5481_match), &spi->dev);
|
||||
if (match)
|
||||
data->cfg = of_device_get_match_data(&spi->dev);
|
||||
else
|
||||
data->cfg = &max5481_cfg[id->driver_data];
|
||||
|
||||
indio_dev->name = id->name;
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
/* variant specific configuration */
|
||||
indio_dev->info = &max5481_info;
|
||||
indio_dev->channels = max5481_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(max5481_channels);
|
||||
|
||||
/* restore wiper from NV */
|
||||
ret = max5481_write_cmd(data, MAX5481_COPY_NV_TO_AB, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return iio_device_register(indio_dev);
|
||||
}
|
||||
|
||||
static int max5481_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(&spi->dev);
|
||||
struct max5481_data *data = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
/* save wiper reg to NV reg */
|
||||
return max5481_write_cmd(data, MAX5481_COPY_AB_TO_NV, 0);
|
||||
}
|
||||
|
||||
static const struct spi_device_id max5481_id_table[] = {
|
||||
{ "max5481", max5481 },
|
||||
{ "max5482", max5482 },
|
||||
{ "max5483", max5483 },
|
||||
{ "max5484", max5484 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, max5481_id_table);
|
||||
|
||||
#if defined(CONFIG_ACPI)
|
||||
static const struct acpi_device_id max5481_acpi_match[] = {
|
||||
{ "max5481", max5481 },
|
||||
{ "max5482", max5482 },
|
||||
{ "max5483", max5483 },
|
||||
{ "max5484", max5484 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, max5481_acpi_match);
|
||||
#endif
|
||||
|
||||
static struct spi_driver max5481_driver = {
|
||||
.driver = {
|
||||
.name = "max5481",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_match_ptr(max5481_match),
|
||||
.acpi_match_table = ACPI_PTR(max5481_acpi_match),
|
||||
},
|
||||
.probe = max5481_probe,
|
||||
.remove = max5481_remove,
|
||||
.id_table = max5481_id_table,
|
||||
};
|
||||
|
||||
module_spi_driver(max5481_driver);
|
||||
|
||||
MODULE_AUTHOR("Maury Anderson <maury.anderson@rockwellcollins.com>");
|
||||
MODULE_DESCRIPTION("max5481 SPI driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -284,6 +284,7 @@ static const struct of_device_id mcp4531_of_match[] = {
|
|||
MCP4531_COMPATIBLE("microchip,mcp4662-104", MCP466x_104),
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mcp4531_of_match);
|
||||
#endif
|
||||
|
||||
static int mcp4531_probe(struct i2c_client *client,
|
||||
|
|
|
@ -42,6 +42,16 @@ config BMP280_SPI
|
|||
depends on SPI_MASTER
|
||||
select REGMAP
|
||||
|
||||
config IIO_CROS_EC_BARO
|
||||
tristate "ChromeOS EC Barometer Sensor"
|
||||
depends on IIO_CROS_EC_SENSORS_CORE
|
||||
help
|
||||
Say yes here to build support for the Barometer sensor when
|
||||
presented by the ChromeOS EC Sensor hub.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called cros_ec_baro.
|
||||
|
||||
config HID_SENSOR_PRESS
|
||||
depends on HID_SENSOR_HUB
|
||||
select IIO_BUFFER
|
||||
|
|
|
@ -8,6 +8,7 @@ obj-$(CONFIG_BMP280) += bmp280.o
|
|||
bmp280-objs := bmp280-core.o bmp280-regmap.o
|
||||
obj-$(CONFIG_BMP280_I2C) += bmp280-i2c.o
|
||||
obj-$(CONFIG_BMP280_SPI) += bmp280-spi.o
|
||||
obj-$(CONFIG_IIO_CROS_EC_BARO) += cros_ec_baro.o
|
||||
obj-$(CONFIG_HID_SENSOR_PRESS) += hid-sensor-press.o
|
||||
obj-$(CONFIG_HP03) += hp03.o
|
||||
obj-$(CONFIG_MPL115) += mpl115.o
|
||||
|
|
|
@ -65,7 +65,7 @@ struct bmp280_data {
|
|||
struct bmp180_calib calib;
|
||||
struct regulator *vddd;
|
||||
struct regulator *vdda;
|
||||
unsigned int start_up_time; /* in milliseconds */
|
||||
unsigned int start_up_time; /* in microseconds */
|
||||
|
||||
/* log of base 2 of oversampling rate */
|
||||
u8 oversampling_press;
|
||||
|
@ -935,14 +935,14 @@ int bmp280_common_probe(struct device *dev,
|
|||
data->chip_info = &bmp180_chip_info;
|
||||
data->oversampling_press = ilog2(8);
|
||||
data->oversampling_temp = ilog2(1);
|
||||
data->start_up_time = 10;
|
||||
data->start_up_time = 10000;
|
||||
break;
|
||||
case BMP280_CHIP_ID:
|
||||
indio_dev->num_channels = 2;
|
||||
data->chip_info = &bmp280_chip_info;
|
||||
data->oversampling_press = ilog2(16);
|
||||
data->oversampling_temp = ilog2(2);
|
||||
data->start_up_time = 2;
|
||||
data->start_up_time = 2000;
|
||||
break;
|
||||
case BME280_CHIP_ID:
|
||||
indio_dev->num_channels = 3;
|
||||
|
@ -950,7 +950,7 @@ int bmp280_common_probe(struct device *dev,
|
|||
data->oversampling_press = ilog2(16);
|
||||
data->oversampling_humid = ilog2(16);
|
||||
data->oversampling_temp = ilog2(2);
|
||||
data->start_up_time = 2;
|
||||
data->start_up_time = 2000;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
|
@ -979,7 +979,7 @@ int bmp280_common_probe(struct device *dev,
|
|||
goto out_disable_vddd;
|
||||
}
|
||||
/* Wait to make sure we started up properly */
|
||||
mdelay(data->start_up_time);
|
||||
usleep_range(data->start_up_time, data->start_up_time + 100);
|
||||
|
||||
/* Bring chip out of reset if there is an assigned GPIO line */
|
||||
gpiod = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
|
||||
|
@ -1038,7 +1038,7 @@ int bmp280_common_probe(struct device *dev,
|
|||
* Set autosuspend to two orders of magnitude larger than the
|
||||
* start-up time.
|
||||
*/
|
||||
pm_runtime_set_autosuspend_delay(dev, data->start_up_time *100);
|
||||
pm_runtime_set_autosuspend_delay(dev, data->start_up_time / 10);
|
||||
pm_runtime_use_autosuspend(dev);
|
||||
pm_runtime_put(dev);
|
||||
|
||||
|
@ -1101,7 +1101,7 @@ static int bmp280_runtime_resume(struct device *dev)
|
|||
ret = regulator_enable(data->vdda);
|
||||
if (ret)
|
||||
return ret;
|
||||
msleep(data->start_up_time);
|
||||
usleep_range(data->start_up_time, data->start_up_time + 100);
|
||||
return data->chip_info->chip_config(data);
|
||||
}
|
||||
#endif /* CONFIG_PM */
|
||||
|
|
|
@ -0,0 +1,220 @@
|
|||
/*
|
||||
* cros_ec_baro - Driver for barometer sensor behind CrosEC.
|
||||
*
|
||||
* Copyright (C) 2017 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.h>
|
||||
#include <linux/iio/triggered_buffer.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/platform_device.h>
|
||||
|
||||
#include "../common/cros_ec_sensors/cros_ec_sensors_core.h"
|
||||
|
||||
/*
|
||||
* One channel for pressure, the other for timestamp.
|
||||
*/
|
||||
#define CROS_EC_BARO_MAX_CHANNELS (1 + 1)
|
||||
|
||||
/* State data for ec_sensors iio driver. */
|
||||
struct cros_ec_baro_state {
|
||||
/* Shared by all sensors */
|
||||
struct cros_ec_sensors_core_state core;
|
||||
|
||||
struct iio_chan_spec channels[CROS_EC_BARO_MAX_CHANNELS];
|
||||
};
|
||||
|
||||
static int cros_ec_baro_read(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct cros_ec_baro_state *st = iio_priv(indio_dev);
|
||||
u16 data = 0;
|
||||
int ret = IIO_VAL_INT;
|
||||
int idx = chan->scan_index;
|
||||
|
||||
mutex_lock(&st->core.cmd_lock);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (cros_ec_sensors_read_cmd(indio_dev, 1 << idx,
|
||||
(s16 *)&data) < 0)
|
||||
ret = -EIO;
|
||||
*val = data;
|
||||
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;
|
||||
|
||||
if (cros_ec_motion_send_host_cmd(&st->core, 0)) {
|
||||
ret = -EIO;
|
||||
break;
|
||||
}
|
||||
*val = st->core.resp->sensor_range.ret;
|
||||
|
||||
/* scale * in_pressure_raw --> kPa */
|
||||
*val2 = 10 << CROS_EC_SENSOR_BITS;
|
||||
ret = IIO_VAL_FRACTIONAL;
|
||||
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_baro_write(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
struct cros_ec_baro_state *st = iio_priv(indio_dev);
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&st->core.cmd_lock);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
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;
|
||||
|
||||
if (cros_ec_motion_send_host_cmd(&st->core, 0))
|
||||
ret = -EIO;
|
||||
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 cros_ec_baro_info = {
|
||||
.read_raw = &cros_ec_baro_read,
|
||||
.write_raw = &cros_ec_baro_write,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int cros_ec_baro_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_baro_state *state;
|
||||
struct iio_chan_spec *channel;
|
||||
int ret;
|
||||
|
||||
if (!ec_dev || !ec_dev->ec_dev) {
|
||||
dev_warn(dev, "No CROS EC device found.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
ec_device = ec_dev->ec_dev;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(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 = &cros_ec_baro_info;
|
||||
state = iio_priv(indio_dev);
|
||||
state->core.type = state->core.resp->info.type;
|
||||
state->core.loc = state->core.resp->info.location;
|
||||
channel = state->channels;
|
||||
/* Common part */
|
||||
channel->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
|
||||
channel->info_mask_shared_by_all =
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
|
||||
BIT(IIO_CHAN_INFO_FREQUENCY);
|
||||
channel->scan_type.realbits = CROS_EC_SENSOR_BITS;
|
||||
channel->scan_type.storagebits = CROS_EC_SENSOR_BITS;
|
||||
channel->scan_type.shift = 0;
|
||||
channel->scan_index = 0;
|
||||
channel->ext_info = cros_ec_sensors_ext_info;
|
||||
channel->scan_type.sign = 'u';
|
||||
|
||||
state->core.calib[0] = 0;
|
||||
|
||||
/* Sensor specific */
|
||||
switch (state->core.type) {
|
||||
case MOTIONSENSE_TYPE_BARO:
|
||||
channel->type = IIO_PRESSURE;
|
||||
break;
|
||||
default:
|
||||
dev_warn(dev, "Unknown motion sensor\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Timestamp */
|
||||
channel++;
|
||||
channel->type = IIO_TIMESTAMP;
|
||||
channel->channel = -1;
|
||||
channel->scan_index = 1;
|
||||
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_BARO_MAX_CHANNELS;
|
||||
|
||||
state->core.read_ec_sensors_data = cros_ec_sensors_read_cmd;
|
||||
|
||||
ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
|
||||
cros_ec_sensors_capture, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return devm_iio_device_register(dev, indio_dev);
|
||||
}
|
||||
|
||||
static const struct platform_device_id cros_ec_baro_ids[] = {
|
||||
{
|
||||
.name = "cros-ec-baro",
|
||||
},
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, cros_ec_baro_ids);
|
||||
|
||||
static struct platform_driver cros_ec_baro_platform_driver = {
|
||||
.driver = {
|
||||
.name = "cros-ec-baro",
|
||||
},
|
||||
.probe = cros_ec_baro_probe,
|
||||
.id_table = cros_ec_baro_ids,
|
||||
};
|
||||
module_platform_driver(cros_ec_baro_platform_driver);
|
||||
|
||||
MODULE_DESCRIPTION("ChromeOS EC barometer sensor driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -137,6 +137,7 @@ static const struct iio_chan_spec mpl115_channels[] = {
|
|||
{
|
||||
.type = IIO_TEMP,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.info_mask_shared_by_type =
|
||||
BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_SCALE),
|
||||
},
|
||||
};
|
||||
|
|
|
@ -190,7 +190,7 @@ static const struct iio_chan_spec mpl3115_channels[] = {
|
|||
{
|
||||
.type = IIO_PRESSURE,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
|
||||
.scan_index = 0,
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
|
@ -203,7 +203,7 @@ static const struct iio_chan_spec mpl3115_channels[] = {
|
|||
{
|
||||
.type = IIO_TEMP,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
|
||||
.scan_index = 1,
|
||||
.scan_type = {
|
||||
.sign = 's',
|
||||
|
|
|
@ -308,6 +308,7 @@ static int ms5611_write_raw(struct iio_dev *indio_dev,
|
|||
{
|
||||
struct ms5611_state *st = iio_priv(indio_dev);
|
||||
const struct ms5611_osr *osr = NULL;
|
||||
int ret;
|
||||
|
||||
if (mask != IIO_CHAN_INFO_OVERSAMPLING_RATIO)
|
||||
return -EINVAL;
|
||||
|
@ -321,12 +322,11 @@ static int ms5611_write_raw(struct iio_dev *indio_dev,
|
|||
if (!osr)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&st->lock);
|
||||
ret = iio_device_claim_direct_mode(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (iio_buffer_enabled(indio_dev)) {
|
||||
mutex_unlock(&st->lock);
|
||||
return -EBUSY;
|
||||
}
|
||||
mutex_lock(&st->lock);
|
||||
|
||||
if (chan->type == IIO_TEMP)
|
||||
st->temp_osr = osr;
|
||||
|
@ -334,6 +334,8 @@ static int ms5611_write_raw(struct iio_dev *indio_dev,
|
|||
st->pressure_osr = osr;
|
||||
|
||||
mutex_unlock(&st->lock);
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,14 @@
|
|||
#include <linux/types.h>
|
||||
#include <linux/iio/common/st_sensors.h>
|
||||
|
||||
enum st_press_type {
|
||||
LPS001WP,
|
||||
LPS25H,
|
||||
LPS331AP,
|
||||
LPS22HB,
|
||||
ST_PRESS_MAX,
|
||||
};
|
||||
|
||||
#define LPS001WP_PRESS_DEV_NAME "lps001wp"
|
||||
#define LPS25H_PRESS_DEV_NAME "lps25h"
|
||||
#define LPS331AP_PRESS_DEV_NAME "lps331ap"
|
||||
|
|
|
@ -136,20 +136,21 @@ static const struct iio_chan_spec st_press_1_channels[] = {
|
|||
.address = ST_PRESS_1_OUT_XL_ADDR,
|
||||
.scan_index = 0,
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
.sign = 's',
|
||||
.realbits = 24,
|
||||
.storagebits = 32,
|
||||
.endianness = IIO_LE,
|
||||
},
|
||||
.info_mask_separate =
|
||||
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
|
||||
},
|
||||
{
|
||||
.type = IIO_TEMP,
|
||||
.address = ST_TEMP_1_OUT_L_ADDR,
|
||||
.scan_index = 1,
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
.sign = 's',
|
||||
.realbits = 16,
|
||||
.storagebits = 16,
|
||||
.endianness = IIO_LE,
|
||||
|
@ -158,6 +159,7 @@ static const struct iio_chan_spec st_press_1_channels[] = {
|
|||
BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_OFFSET),
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
|
||||
},
|
||||
IIO_CHAN_SOFT_TIMESTAMP(2)
|
||||
};
|
||||
|
@ -168,7 +170,7 @@ static const struct iio_chan_spec st_press_lps001wp_channels[] = {
|
|||
.address = ST_PRESS_LPS001WP_OUT_L_ADDR,
|
||||
.scan_index = 0,
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
.sign = 's',
|
||||
.realbits = 16,
|
||||
.storagebits = 16,
|
||||
.endianness = IIO_LE,
|
||||
|
@ -182,7 +184,7 @@ static const struct iio_chan_spec st_press_lps001wp_channels[] = {
|
|||
.address = ST_TEMP_LPS001WP_OUT_L_ADDR,
|
||||
.scan_index = 1,
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
.sign = 's',
|
||||
.realbits = 16,
|
||||
.storagebits = 16,
|
||||
.endianness = IIO_LE,
|
||||
|
@ -200,7 +202,7 @@ static const struct iio_chan_spec st_press_lps22hb_channels[] = {
|
|||
.address = ST_PRESS_1_OUT_XL_ADDR,
|
||||
.scan_index = 0,
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
.sign = 's',
|
||||
.realbits = 24,
|
||||
.storagebits = 32,
|
||||
.endianness = IIO_LE,
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue