1
0
Fork 0

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
  ...
zero-colors
Linus Torvalds 2017-02-22 12:14:01 -08:00
commit caa5942897
669 changed files with 25683 additions and 20835 deletions

View File

@ -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

View File

@ -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.

View File

@ -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]

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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";
};

View File

@ -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>;
};

View File

@ -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>;
};

View File

@ -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 */
};

View File

@ -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>;
};
};

View File

@ -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...

View File

@ -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>;
};

View File

@ -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";
};

View File

@ -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>;
};

View File

@ -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";
};

View File

@ -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 */
};

View File

@ -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

View File

@ -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>;
};

View File

@ -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>;
};
};

View File

@ -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>;
};
};

View File

@ -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>;
};
};

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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*/

View File

@ -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);

View File

@ -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"

View File

@ -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,

View File

@ -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,

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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));

View File

@ -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 = {

View File

@ -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");

View File

@ -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>

View File

@ -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");

View File

@ -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,

View File

@ -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, &regval);
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, &regval);
} 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, &regval);
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, &regval);
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");

View File

@ -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];

View File

@ -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", &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");

View File

@ -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)) {

View File

@ -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;
};

View File

@ -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;

View File

@ -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);

View File

@ -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,

View File

@ -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");

View File

@ -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");

View File

@ -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 {

View File

@ -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>

View File

@ -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,
&timestamp);
if (timestamp.index >= 0 && timestamp.report_id) {
int val0, val1;
hid_sensor_format_scale(HID_USAGE_SENSOR_TIME_TIMESTAMP,
&timestamp, &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;
}

View File

@ -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>

View File

@ -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");

View File

@ -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,

View File

@ -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,

View File

@ -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_ */

View File

@ -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.

View File

@ -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);

View File

@ -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);
}

View File

@ -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,

View File

@ -39,6 +39,7 @@ config KMX61
be called kmx61.
source "drivers/iio/imu/inv_mpu6050/Kconfig"
source "drivers/iio/imu/st_lsm6dsx/Kconfig"
endmenu

View File

@ -17,3 +17,5 @@ obj-y += bmi160/
obj-y += inv_mpu6050/
obj-$(CONFIG_KMX61) += kmx61.o
obj-y += st_lsm6dsx/

View File

@ -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;

View File

@ -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,

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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 */

View File

@ -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;
}

View File

@ -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");

View File

@ -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");

View File

@ -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");

View File

@ -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);

View File

@ -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[] = {

View File

@ -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, ...)

View File

@ -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:

View File

@ -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"

View File

@ -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

View File

@ -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;
}
/**

View File

@ -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");

View File

@ -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);

View File

@ -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,

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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");

View File

@ -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,

View File

@ -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

View File

@ -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

View File

@ -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 */

View File

@ -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");

View File

@ -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),
},
};

View File

@ -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',

View File

@ -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;
}

View File

@ -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"

View File

@ -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