mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-11-24 07:20:50 +07:00
Staging/IIO patches for 4.12-rc1
Here is the big staging tree update for 4.12-rc1. And it's a big one, adding about 350k new lines of crap^Wcode, mostly all in a big dump of media drivers from Intel. But there's other new drivers in here as well, yet-another-wifi driver, new IIO drivers, and a new crypto accelerator. We also deleted a bunch of stuff, mostly in patch cleanups, but also the Android ION code has shrunk a lot, and the Android low memory killer driver was finally deleted, much to the celebration of the -mm developers. All of these have been in linux-next with a few build issues that will show up when you merge to your tree, I'll follow up with fixes for those after this gets merged. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCWQzzlQ8cZ3JlZ0Brcm9h aC5jb20ACgkQMUfUDdst+ylNMgCcD+GoaF/Ml7YnULRl2GG/526II78AnitZ8qjd rPqeowMIewYu9fgckLUc =7rzO -----END PGP SIGNATURE----- Merge tag 'staging-4.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging Pull staging/IIO updates from Greg KH: "Here is the big staging tree update for 4.12-rc1. It's a big one, adding about 350k new lines of crap^Wcode, mostly all in a big dump of media drivers from Intel. But there's other new drivers in here as well, yet-another-wifi driver, new IIO drivers, and a new crypto accelerator. We also deleted a bunch of stuff, mostly in patch cleanups, but also the Android ION code has shrunk a lot, and the Android low memory killer driver was finally deleted, much to the celebration of the -mm developers. All of these have been in linux-next with a few build issues that will show up when you merge to your tree" Merge conflicts in the new rtl8723bs driver (due to the wifi changes this merge window) handled as per linux-next, courtesy of Stephen Rothwell. * tag 'staging-4.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging: (1182 commits) staging: fsl-mc/dpio: add cpu <--> LE conversion for dpaa2_fd staging: ks7010: remove line continuations in quoted strings staging: vt6656: use tabs instead of spaces staging: android: ion: Fix unnecessary initialization of static variable staging: media: atomisp: fix range checking on clk_num staging: media: atomisp: fix misspelled word in comment staging: media: atomisp: kmap() can't fail staging: atomisp: remove #ifdef for runtime PM functions staging: atomisp: satm include directory is gone atomisp: remove some more unused files atomisp: remove hmm_load/store/clear indirections atomisp: kill off mmgr_free atomisp: clean up the hmm init/cleanup indirections atomisp: handle allocation calls before init in the hmm layer staging: fsl-dpaa2/eth: Add maintainer for Ethernet driver staging: fsl-dpaa2/eth: Add TODO file staging: fsl-dpaa2/eth: Add trace points staging: fsl-dpaa2/eth: Add driver specific stats staging: fsl-dpaa2/eth: Add ethtool support staging: fsl-dpaa2/eth: Add Freescale DPAA2 Ethernet driver ...
This commit is contained in:
commit
c6a677c6f3
1
.mailmap
1
.mailmap
@ -111,6 +111,7 @@ Mauro Carvalho Chehab <mchehab@kernel.org> <mchehab@osg.samsung.com>
|
||||
Mauro Carvalho Chehab <mchehab@kernel.org> <mchehab@s-opensource.com>
|
||||
Matt Ranostay <mranostay@gmail.com> Matthew Ranostay <mranostay@embeddedalley.com>
|
||||
Matt Ranostay <mranostay@gmail.com> <matt.ranostay@intel.com>
|
||||
Matt Ranostay <matt.ranostay@konsulko.com> <matt@ranostay.consulting>
|
||||
Mayuresh Janorkar <mayur@ti.com>
|
||||
Michael Buesch <m@bues.ch>
|
||||
Michel Dänzer <michel@tungstengraphics.com>
|
||||
|
@ -55,6 +55,7 @@ Description:
|
||||
then it is to be found in the base device directory.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/sampling_frequency_available
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_proximity_sampling_frequency_available
|
||||
What: /sys/.../iio:deviceX/buffer/sampling_frequency_available
|
||||
What: /sys/bus/iio/devices/triggerX/sampling_frequency_available
|
||||
KernelVersion: 2.6.35
|
||||
@ -1593,7 +1594,7 @@ Description:
|
||||
can be processed to siemens per meter.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_countY_raw
|
||||
KernelVersion: 4.9
|
||||
KernelVersion: 4.10
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Raw counter device counts from channel Y. For quadrature
|
||||
@ -1601,10 +1602,24 @@ Description:
|
||||
the counts of a single quadrature signal phase from channel Y.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_indexY_raw
|
||||
KernelVersion: 4.9
|
||||
KernelVersion: 4.10
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Raw counter device index value from channel Y. This attribute
|
||||
provides an absolute positional reference (e.g. a pulse once per
|
||||
revolution) which may be used to home positional systems as
|
||||
required.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_count_count_direction_available
|
||||
KernelVersion: 4.12
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
A list of possible counting directions which are:
|
||||
- "up" : counter device is increasing.
|
||||
- "down": counter device is decreasing.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_countY_count_direction
|
||||
KernelVersion: 4.12
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Raw counter device counters direction for channel Y.
|
||||
|
17
Documentation/ABI/testing/sysfs-bus-iio-adc-max9611
Normal file
17
Documentation/ABI/testing/sysfs-bus-iio-adc-max9611
Normal file
@ -0,0 +1,17 @@
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_power_shunt_resistor
|
||||
Date: March 2017
|
||||
KernelVersion: 4.12
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description: The value of the shunt resistor used to compute power drain on
|
||||
common input voltage pin (RS+). In Ohms.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_current_shunt_resistor
|
||||
Date: March 2017
|
||||
KernelVersion: 4.12
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description: The value of the shunt resistor used to compute current flowing
|
||||
between RS+ and RS- voltage sense inputs. In Ohms.
|
||||
|
||||
These attributes describe a single physical component, exposed as two distinct
|
||||
attributes as it is used to calculate two different values: power load and
|
||||
current flowing between RS+ and RS- inputs.
|
@ -1,24 +1,16 @@
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_count_count_direction_available
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_count_count_mode_available
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_count_noise_error_available
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_count_quadrature_mode_available
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_index_index_polarity_available
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_index_synchronous_mode_available
|
||||
KernelVersion: 4.9
|
||||
KernelVersion: 4.10
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Discrete set of available values for the respective counter
|
||||
configuration are listed in this file.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_countY_count_direction
|
||||
KernelVersion: 4.9
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Read-only attribute that indicates whether the counter for
|
||||
channel Y is counting up or down.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_countY_count_mode
|
||||
KernelVersion: 4.9
|
||||
KernelVersion: 4.10
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Count mode for channel Y. Four count modes are available:
|
||||
@ -52,7 +44,7 @@ Description:
|
||||
continuously throughout.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_countY_noise_error
|
||||
KernelVersion: 4.9
|
||||
KernelVersion: 4.10
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Read-only attribute that indicates whether excessive noise is
|
||||
@ -60,14 +52,14 @@ Description:
|
||||
irrelevant in non-quadrature clock mode.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_countY_preset
|
||||
KernelVersion: 4.9
|
||||
KernelVersion: 4.10
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
If the counter device supports preset registers, the preset
|
||||
count for channel Y is provided by this attribute.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_countY_quadrature_mode
|
||||
KernelVersion: 4.9
|
||||
KernelVersion: 4.10
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Configure channel Y counter for non-quadrature or quadrature
|
||||
@ -88,7 +80,7 @@ Description:
|
||||
decoded for UP/DN clock.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_countY_set_to_preset_on_index
|
||||
KernelVersion: 4.9
|
||||
KernelVersion: 4.10
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Whether to set channel Y counter with channel Y preset value
|
||||
@ -96,14 +88,14 @@ Description:
|
||||
Valid attribute values are boolean.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_indexY_index_polarity
|
||||
KernelVersion: 4.9
|
||||
KernelVersion: 4.10
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Active level of channel Y index input; irrelevant in
|
||||
non-synchronous load mode.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_indexY_synchronous_mode
|
||||
KernelVersion: 4.9
|
||||
KernelVersion: 4.10
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Configure channel Y counter for non-synchronous or synchronous
|
||||
|
@ -3,11 +3,15 @@ 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.
|
||||
- "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.
|
||||
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.
|
||||
@ -27,3 +31,62 @@ Description:
|
||||
Reading returns the current sampling frequency.
|
||||
Writing an value different of 0 set and start sampling.
|
||||
Writing 0 stop sampling.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_count0_preset
|
||||
KernelVersion: 4.12
|
||||
Contact: benjamin.gaignard@st.com
|
||||
Description:
|
||||
Reading returns the current preset value.
|
||||
Writing sets the preset value.
|
||||
When counting up the counter starts from 0 and fires an
|
||||
event when reach preset value.
|
||||
When counting down the counter start from preset value
|
||||
and fire event when reach 0.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_count_quadrature_mode_available
|
||||
KernelVersion: 4.12
|
||||
Contact: benjamin.gaignard@st.com
|
||||
Description:
|
||||
Reading returns the list possible quadrature modes.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_count0_quadrature_mode
|
||||
KernelVersion: 4.12
|
||||
Contact: benjamin.gaignard@st.com
|
||||
Description:
|
||||
Configure the device counter quadrature modes:
|
||||
channel_A:
|
||||
Encoder A input servers as the count input and B as
|
||||
the UP/DOWN direction control input.
|
||||
|
||||
channel_B:
|
||||
Encoder B input serves as the count input and A as
|
||||
the UP/DOWN direction control input.
|
||||
|
||||
quadrature:
|
||||
Encoder A and B inputs are mixed to get direction
|
||||
and count with a scale of 0.25.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_count_enable_mode_available
|
||||
KernelVersion: 4.12
|
||||
Contact: benjamin.gaignard@st.com
|
||||
Description:
|
||||
Reading returns the list possible enable modes.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_count0_enable_mode
|
||||
KernelVersion: 4.12
|
||||
Contact: benjamin.gaignard@st.com
|
||||
Description:
|
||||
Configure the device counter enable modes, in all case
|
||||
counting direction is set by in_count0_count_direction
|
||||
attribute and the counter is clocked by the internal clock.
|
||||
always:
|
||||
Counter is always ON.
|
||||
|
||||
gated:
|
||||
Counting is enabled when connected trigger signal
|
||||
level is high else counting is disabled.
|
||||
|
||||
triggered:
|
||||
Counting is enabled on rising edge of the connected
|
||||
trigger, and remains enabled for the duration of this
|
||||
selected mode.
|
||||
|
38
Documentation/devicetree/bindings/iio/accel/adxl345.txt
Normal file
38
Documentation/devicetree/bindings/iio/accel/adxl345.txt
Normal file
@ -0,0 +1,38 @@
|
||||
Analog Devices ADXL345 3-Axis, +/-(2g/4g/8g/16g) Digital Accelerometer
|
||||
|
||||
http://www.analog.com/en/products/mems/accelerometers/adxl345.html
|
||||
|
||||
Required properties:
|
||||
- compatible : should be "adi,adxl345"
|
||||
- reg : the I2C address or SPI chip select number of the sensor
|
||||
|
||||
Required properties for SPI bus usage:
|
||||
- spi-max-frequency : set maximum clock frequency, must be 5000000
|
||||
- spi-cpol and spi-cpha : must be defined for adxl345 to enable SPI mode 3
|
||||
|
||||
Optional properties:
|
||||
- interrupt-parent : phandle to the parent interrupt controller as documented
|
||||
in Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
|
||||
- interrupts: interrupt mapping for IRQ as documented in
|
||||
Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
|
||||
|
||||
Example for a I2C device node:
|
||||
|
||||
accelerometer@2a {
|
||||
compatible = "adi,adxl345";
|
||||
reg = <0x53>;
|
||||
interrupt-parent = <&gpio1>;
|
||||
interrupts = <0 IRQ_TYPE_LEVEL_HIGH>;
|
||||
};
|
||||
|
||||
Example for a SPI device node:
|
||||
|
||||
accelerometer@0 {
|
||||
compatible = "adi,adxl345";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <5000000>;
|
||||
spi-cpol;
|
||||
spi-cpha;
|
||||
interrupt-parent = <&gpio1>;
|
||||
interrupts = <0 IRQ_TYPE_LEVEL_HIGH>;
|
||||
};
|
@ -7,6 +7,7 @@ Required properties:
|
||||
- "amlogic,meson-gxm-saradc" for GXM
|
||||
along with the generic "amlogic,meson-saradc"
|
||||
- reg: the physical base address and length of the registers
|
||||
- interrupts: the interrupt indicating end of sampling
|
||||
- clocks: phandle and clock identifier (see clock-names)
|
||||
- clock-names: mandatory clocks:
|
||||
- "clkin" for the reference clock (typically XTAL)
|
||||
@ -23,6 +24,7 @@ Example:
|
||||
compatible = "amlogic,meson-gxl-saradc", "amlogic,meson-saradc";
|
||||
#io-channel-cells = <1>;
|
||||
reg = <0x0 0x8680 0x0 0x34>;
|
||||
interrupts = <GIC_SPI 73 IRQ_TYPE_EDGE_RISING>;
|
||||
clocks = <&xtal>,
|
||||
<&clkc CLKID_SAR_ADC>,
|
||||
<&clkc CLKID_SANA>,
|
||||
|
20
Documentation/devicetree/bindings/iio/adc/aspeed_adc.txt
Normal file
20
Documentation/devicetree/bindings/iio/adc/aspeed_adc.txt
Normal file
@ -0,0 +1,20 @@
|
||||
Aspeed ADC
|
||||
|
||||
This device is a 10-bit converter for 16 voltage channels. All inputs are
|
||||
single ended.
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "aspeed,ast2400-adc" or "aspeed,ast2500-adc"
|
||||
- reg: memory window mapping address and length
|
||||
- clocks: Input clock used to derive the sample clock. Expected to be the
|
||||
SoC's APB clock.
|
||||
- #io-channel-cells: Must be set to <1> to indicate channels are selected
|
||||
by index.
|
||||
|
||||
Example:
|
||||
adc@1e6e9000 {
|
||||
compatible = "aspeed,ast2400-adc";
|
||||
reg = <0x1e6e9000 0xb0>;
|
||||
clocks = <&clk_apb>;
|
||||
#io-channel-cells = <1>;
|
||||
};
|
18
Documentation/devicetree/bindings/iio/adc/cpcap-adc.txt
Normal file
18
Documentation/devicetree/bindings/iio/adc/cpcap-adc.txt
Normal file
@ -0,0 +1,18 @@
|
||||
Motorola CPCAP PMIC ADC binding
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "motorola,cpcap-adc" or "motorola,mapphone-cpcap-adc"
|
||||
- interrupt-parent: The interrupt controller
|
||||
- interrupts: The interrupt number for the ADC device
|
||||
- interrupt-names: Should be "adcdone"
|
||||
- #io-channel-cells: Number of cells in an IIO specifier
|
||||
|
||||
Example:
|
||||
|
||||
cpcap_adc: adc {
|
||||
compatible = "motorola,mapphone-cpcap-adc";
|
||||
interrupt-parent = <&cpcap>;
|
||||
interrupts = <8 IRQ_TYPE_NONE>;
|
||||
interrupt-names = "adcdone";
|
||||
#io-channel-cells = <1>;
|
||||
};
|
13
Documentation/devicetree/bindings/iio/adc/ltc2497.txt
Normal file
13
Documentation/devicetree/bindings/iio/adc/ltc2497.txt
Normal file
@ -0,0 +1,13 @@
|
||||
* Linear Technology / Analog Devices LTC2497 ADC
|
||||
|
||||
Required properties:
|
||||
- compatible: Must be "lltc,ltc2497"
|
||||
- reg: Must contain the ADC I2C address
|
||||
- vref-supply: The regulator supply for ADC reference voltage
|
||||
|
||||
Example:
|
||||
ltc2497: adc@76 {
|
||||
compatible = "lltc,ltc2497";
|
||||
reg = <0x76>;
|
||||
vref-supply = <<c2497_reg>;
|
||||
};
|
21
Documentation/devicetree/bindings/iio/adc/max1118.txt
Normal file
21
Documentation/devicetree/bindings/iio/adc/max1118.txt
Normal file
@ -0,0 +1,21 @@
|
||||
* MAX1117/MAX1118/MAX1119 8-bit, dual-channel ADCs
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be one of
|
||||
* "maxim,max1117"
|
||||
* "maxim,max1118"
|
||||
* "maxim,max1119"
|
||||
- reg: spi chip select number for the device
|
||||
- (max1118 only) vref-supply: The regulator supply for ADC reference voltage
|
||||
|
||||
Recommended properties:
|
||||
- spi-max-frequency: Definition as per
|
||||
Documentation/devicetree/bindings/spi/spi-bus.txt
|
||||
|
||||
Example:
|
||||
adc@0 {
|
||||
compatible = "maxim,max1118";
|
||||
reg = <0>;
|
||||
vref-supply = <&vdd_supply>;
|
||||
spi-max-frequency = <1000000>;
|
||||
};
|
27
Documentation/devicetree/bindings/iio/adc/max9611.txt
Normal file
27
Documentation/devicetree/bindings/iio/adc/max9611.txt
Normal file
@ -0,0 +1,27 @@
|
||||
* Maxim max9611/max9612 current sense amplifier with 12-bits ADC interface
|
||||
|
||||
Maxim max9611/max9612 is an high-side current sense amplifier with integrated
|
||||
12-bits ADC communicating over I2c bus.
|
||||
The device node for this driver shall be a child of a I2c controller.
|
||||
|
||||
Required properties
|
||||
- compatible: Should be "maxim,max9611" or "maxim,max9612"
|
||||
- reg: The 7-bits long I2c address of the device
|
||||
- shunt-resistor-micro-ohms: Value, in micro Ohms, of the current sense shunt
|
||||
resistor
|
||||
|
||||
Example:
|
||||
|
||||
&i2c4 {
|
||||
csa: adc@7c {
|
||||
compatible = "maxim,max9611";
|
||||
reg = <0x7c>;
|
||||
|
||||
shunt-resistor-micro-ohms = <5000>;
|
||||
};
|
||||
};
|
||||
|
||||
This device node describes a current sense amplifier sitting on I2c4 bus
|
||||
with address 0x7c (read address is 0xf9, write address is 0xf8).
|
||||
A sense resistor of 0,005 Ohm is installed between RS+ and RS- current-sensing
|
||||
inputs.
|
@ -19,32 +19,42 @@ Required properties:
|
||||
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:
|
||||
iio-bindings.txt for more details, but notice that this particular
|
||||
ADC has a special addressing scheme that require two cells for
|
||||
identifying each ADC channel:
|
||||
|
||||
- #address-cells: should be set to <1>
|
||||
- #address-cells: should be set to <2>, the first cell is the
|
||||
prescaler (on PM8058) or premux (on PM8921) with two valid bits
|
||||
so legal values are 0x00, 0x01 or 0x02. The second cell
|
||||
is the main analog mux setting (0x00..0x0f). The combination
|
||||
of prescaler/premux and analog mux uniquely addresses a hardware
|
||||
channel on all systems.
|
||||
|
||||
- #size-cells: should be set to <0>
|
||||
|
||||
- #io-channel-cells: should be set to <1>
|
||||
- #io-channel-cells: should be set to <2>, again the cells are
|
||||
precaler or premux followed by the analog muxing line.
|
||||
|
||||
- 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:
|
||||
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>;
|
||||
reg = <0x00 0x0c>;
|
||||
};
|
||||
|
||||
adc-channel@d {
|
||||
reg = <0x0d>;
|
||||
reg = <0x00 0x0d>;
|
||||
};
|
||||
|
||||
adc-channel@f {
|
||||
reg = <0x0f>;
|
||||
reg = <0x00 0x0f>;
|
||||
};
|
||||
|
||||
These three nodes are used for absolute and ratiometric calibration
|
||||
@ -52,13 +62,13 @@ 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.
|
||||
Optional subnodes: any channels other than channels [0x00 0x0c],
|
||||
[0x00 0x0d] and [0x00 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.
|
||||
0 .. 0xff (8 bits).
|
||||
|
||||
Optional channel node properties:
|
||||
|
||||
@ -94,56 +104,54 @@ Example:
|
||||
xoadc: xoadc@197 {
|
||||
compatible = "qcom,pm8058-adc";
|
||||
reg = <0x197>;
|
||||
interrupt-parent = <&pm8058>;
|
||||
interrupts = <76 1>;
|
||||
#address-cells = <1>;
|
||||
interrupts-extended = <&pm8058 76 IRQ_TYPE_EDGE_RISING>;
|
||||
#address-cells = <2>;
|
||||
#size-cells = <0>;
|
||||
#io-channel-cells = <1>;
|
||||
#io-channel-cells = <2>;
|
||||
|
||||
vcoin: adc-channel@0 {
|
||||
reg = <0x00>;
|
||||
reg = <0x00 0x00>;
|
||||
};
|
||||
vbat: adc-channel@1 {
|
||||
reg = <0x01>;
|
||||
reg = <0x00 0x01>;
|
||||
};
|
||||
dcin: adc-channel@2 {
|
||||
reg = <0x02>;
|
||||
reg = <0x00 0x02>;
|
||||
};
|
||||
ichg: adc-channel@3 {
|
||||
reg = <0x03>;
|
||||
reg = <0x00 0x03>;
|
||||
};
|
||||
vph_pwr: adc-channel@4 {
|
||||
reg = <0x04>;
|
||||
reg = <0x00 0x04>;
|
||||
};
|
||||
usb_vbus: adc-channel@a {
|
||||
reg = <0x0a>;
|
||||
reg = <0x00 0x0a>;
|
||||
};
|
||||
die_temp: adc-channel@b {
|
||||
reg = <0x0b>;
|
||||
reg = <0x00 0x0b>;
|
||||
};
|
||||
ref_625mv: adc-channel@c {
|
||||
reg = <0x0c>;
|
||||
reg = <0x00 0x0c>;
|
||||
};
|
||||
ref_1250mv: adc-channel@d {
|
||||
reg = <0x0d>;
|
||||
reg = <0x00 0x0d>;
|
||||
};
|
||||
ref_325mv: adc-channel@e {
|
||||
reg = <0x0e>;
|
||||
reg = <0x00 0x0e>;
|
||||
};
|
||||
ref_muxoff: adc-channel@f {
|
||||
reg = <0x0f>;
|
||||
reg = <0x00 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 */
|
||||
io-channels = <&xoadc 0x00 0x01>, /* Battery */
|
||||
<&xoadc 0x00 0x02>, /* DC in (charger) */
|
||||
<&xoadc 0x00 0x04>, /* VPH the main system voltage */
|
||||
<&xoadc 0x00 0x0b>, /* Die temperature */
|
||||
<&xoadc 0x00 0x0c>, /* Reference voltage 1.25V */
|
||||
<&xoadc 0x00 0x0d>, /* Reference voltage 0.625V */
|
||||
<&xoadc 0x00 0x0e>; /* Reference voltage 0.325V */
|
||||
};
|
||||
|
@ -4,6 +4,7 @@ Required properties:
|
||||
- compatible: should be "rockchip,<name>-saradc" or "rockchip,rk3066-tsadc"
|
||||
- "rockchip,saradc": for rk3188, rk3288
|
||||
- "rockchip,rk3066-tsadc": for rk3036
|
||||
- "rockchip,rk3328-saradc", "rockchip,rk3399-saradc": for rk3328
|
||||
- "rockchip,rk3399-saradc": for rk3399
|
||||
|
||||
- reg: physical base address of the controller and length of memory mapped
|
||||
|
@ -57,6 +57,9 @@ 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.
|
||||
- assigned-resolution-bits: Resolution (bits) to use for conversions. Must
|
||||
match device available resolutions (e.g. can be 6, 8, 10 or 12 on stm32f4).
|
||||
Default is maximum resolution if unset.
|
||||
|
||||
Example:
|
||||
adc: adc@40012000 {
|
||||
@ -84,6 +87,7 @@ Example:
|
||||
st,adc-channels = <8>;
|
||||
dmas = <&dma2 0 0 0x400 0x0>;
|
||||
dma-names = "rx";
|
||||
assigned-resolution-bits = <8>;
|
||||
};
|
||||
...
|
||||
other adc child nodes follow...
|
||||
|
23
Documentation/devicetree/bindings/iio/dac/ltc2632.txt
Normal file
23
Documentation/devicetree/bindings/iio/dac/ltc2632.txt
Normal file
@ -0,0 +1,23 @@
|
||||
Linear Technology LTC2632 DAC device driver
|
||||
|
||||
Required properties:
|
||||
- compatible: Has to contain one of the following:
|
||||
lltc,ltc2632-l12
|
||||
lltc,ltc2632-l10
|
||||
lltc,ltc2632-l8
|
||||
lltc,ltc2632-h12
|
||||
lltc,ltc2632-h10
|
||||
lltc,ltc2632-h8
|
||||
|
||||
Property rules described in Documentation/devicetree/bindings/spi/spi-bus.txt
|
||||
apply. In particular, "reg" and "spi-max-frequency" properties must be given.
|
||||
|
||||
Example:
|
||||
|
||||
spi_master {
|
||||
dac: ltc2632@0 {
|
||||
compatible = "lltc,ltc2632-l12";
|
||||
reg = <0>; /* CS0 */
|
||||
spi-max-frequency = <1000000>;
|
||||
};
|
||||
};
|
61
Documentation/devicetree/bindings/iio/dac/st,stm32-dac.txt
Normal file
61
Documentation/devicetree/bindings/iio/dac/st,stm32-dac.txt
Normal file
@ -0,0 +1,61 @@
|
||||
STMicroelectronics STM32 DAC
|
||||
|
||||
The STM32 DAC is a 12-bit voltage output digital-to-analog converter. The DAC
|
||||
may be configured in 8 or 12-bit mode. It has two output channels, each with
|
||||
its own converter.
|
||||
It has built-in noise and triangle waveform generator and supports external
|
||||
triggers for conversions. The DAC's output buffer allows a high drive output
|
||||
current.
|
||||
|
||||
Contents of a stm32 dac root node:
|
||||
-----------------------------------
|
||||
Required properties:
|
||||
- compatible: Must be "st,stm32h7-dac-core".
|
||||
- reg: Offset and length of the device's register set.
|
||||
- clocks: Must contain an entry for pclk (which feeds the peripheral bus
|
||||
interface)
|
||||
- clock-names: Must be "pclk".
|
||||
- vref-supply: Phandle to the vref+ input analog reference supply.
|
||||
- #address-cells = <1>;
|
||||
- #size-cells = <0>;
|
||||
|
||||
Optional properties:
|
||||
- resets: Must contain the phandle to the reset controller.
|
||||
- A pinctrl state named "default" for each DAC channel may be defined to set
|
||||
DAC_OUTx pin in mode of operation for analog output on external pin.
|
||||
|
||||
Contents of a stm32 dac child node:
|
||||
-----------------------------------
|
||||
DAC core node should contain at least one subnode, representing a
|
||||
DAC instance/channel available on the machine.
|
||||
|
||||
Required properties:
|
||||
- compatible: Must be "st,stm32-dac".
|
||||
- reg: Must be either 1 or 2, to define (single) channel in use
|
||||
- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers" in
|
||||
Documentation/devicetree/bindings/iio/iio-bindings.txt
|
||||
|
||||
Example:
|
||||
dac: dac@40007400 {
|
||||
compatible = "st,stm32h7-dac-core";
|
||||
reg = <0x40007400 0x400>;
|
||||
clocks = <&clk>;
|
||||
clock-names = "pclk";
|
||||
vref-supply = <®_vref>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&dac_out1 &dac_out2>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
dac1: dac@1 {
|
||||
compatible = "st,stm32-dac";
|
||||
#io-channels-cells = <1>;
|
||||
reg = <1>;
|
||||
};
|
||||
|
||||
dac2: dac@2 {
|
||||
compatible = "st,stm32-dac";
|
||||
#io-channels-cells = <1>;
|
||||
reg = <2>;
|
||||
};
|
||||
};
|
30
Documentation/devicetree/bindings/iio/health/max30102.txt
Normal file
30
Documentation/devicetree/bindings/iio/health/max30102.txt
Normal file
@ -0,0 +1,30 @@
|
||||
Maxim MAX30102 heart rate and pulse oximeter sensor
|
||||
|
||||
* https://datasheets.maximintegrated.com/en/ds/MAX30102.pdf
|
||||
|
||||
Required properties:
|
||||
- compatible: must be "maxim,max30102"
|
||||
- reg: the I2C address of the sensor
|
||||
- interrupt-parent: should be the phandle for the interrupt controller
|
||||
- interrupts: the sole interrupt generated by the device
|
||||
|
||||
Refer to interrupt-controller/interrupts.txt for generic
|
||||
interrupt client node bindings.
|
||||
|
||||
Optional properties:
|
||||
- maxim,red-led-current-microamp: configuration for RED LED current
|
||||
- maxim,ir-led-current-microamp: configuration for IR LED current
|
||||
|
||||
Note that each step is approximately 200 microamps, ranging from 0 uA to
|
||||
50800 uA.
|
||||
|
||||
Example:
|
||||
|
||||
max30100@57 {
|
||||
compatible = "maxim,max30102";
|
||||
reg = <0x57>;
|
||||
maxim,red-led-current-microamp = <7000>;
|
||||
maxim,ir-led-current-microamp = <7000>;
|
||||
interrupt-parent = <&gpio1>;
|
||||
interrupts = <16 2>;
|
||||
};
|
@ -3,14 +3,21 @@ InvenSense MPU-6050 Six-Axis (Gyro + Accelerometer) MEMS MotionTracking Device
|
||||
http://www.invensense.com/mems/gyro/mpu6050.html
|
||||
|
||||
Required properties:
|
||||
- compatible : should be "invensense,mpu6050"
|
||||
- compatible : should be one of
|
||||
"invensense,mpu6050"
|
||||
"invensense,mpu6500"
|
||||
"invensense,mpu9150"
|
||||
"invensense,mpu9250"
|
||||
"invensense,icm20608"
|
||||
- reg : the I2C address of the sensor
|
||||
- interrupt-parent : should be the phandle for the interrupt controller
|
||||
- interrupts : interrupt mapping for GPIO IRQ
|
||||
|
||||
Optional properties:
|
||||
- mount-matrix: an optional 3x3 mounting rotation matrix
|
||||
|
||||
- i2c-gate node. These devices also support an auxiliary i2c bus. This is
|
||||
simple enough to be described using the i2c-gate binding. See
|
||||
i2c/i2c-gate.txt for more details.
|
||||
|
||||
Example:
|
||||
mpu6050@68 {
|
||||
@ -28,3 +35,19 @@ Example:
|
||||
"0", /* y2 */
|
||||
"0.984807753012208"; /* z2 */
|
||||
};
|
||||
|
||||
|
||||
mpu9250@68 {
|
||||
compatible = "invensense,mpu9250";
|
||||
reg = <0x68>;
|
||||
interrupt-parent = <&gpio3>;
|
||||
interrupts = <21 1>;
|
||||
i2c-gate {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
ax8975@c {
|
||||
compatible = "ak,ak8975";
|
||||
reg = <0x0c>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -3,6 +3,8 @@
|
||||
Required properties:
|
||||
- compatible: must be one of:
|
||||
"st,lsm6ds3"
|
||||
"st,lsm6ds3h"
|
||||
"st,lsm6dsl"
|
||||
"st,lsm6dsm"
|
||||
- reg: i2c address of the sensor / spi cs line
|
||||
|
||||
|
15
Documentation/devicetree/bindings/iio/light/vl6180.txt
Normal file
15
Documentation/devicetree/bindings/iio/light/vl6180.txt
Normal file
@ -0,0 +1,15 @@
|
||||
STMicro VL6180 - ALS, range and proximity sensor
|
||||
|
||||
Link to datasheet: http://www.st.com/resource/en/datasheet/vl6180x.pdf
|
||||
|
||||
Required properties:
|
||||
|
||||
-compatible: should be "st,vl6180"
|
||||
-reg: the I2C address of the sensor
|
||||
|
||||
Example:
|
||||
|
||||
vl6180@29 {
|
||||
compatible = "st,vl6180";
|
||||
reg = <0x29>;
|
||||
};
|
@ -0,0 +1,28 @@
|
||||
* Devantech SRF04 ultrasonic range finder
|
||||
Bit-banging driver using two GPIOs
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "devantech,srf04"
|
||||
|
||||
- trig-gpios: Definition of the GPIO for the triggering (output)
|
||||
This GPIO is set for about 10 us by the driver to tell the
|
||||
device it should initiate the measurement cycle.
|
||||
|
||||
- echo-gpios: Definition of the GPIO for the echo (input)
|
||||
This GPIO is set by the device as soon as an ultrasonic
|
||||
burst is sent out and reset when the first echo is
|
||||
received.
|
||||
Thus this GPIO is set while the ultrasonic waves are doing
|
||||
one round trip.
|
||||
It needs to be an GPIO which is able to deliver an
|
||||
interrupt because the time between two interrupts is
|
||||
measured in the driver.
|
||||
See Documentation/devicetree/bindings/gpio/gpio.txt for
|
||||
information on how to specify a consumer gpio.
|
||||
|
||||
Example:
|
||||
srf04@0 {
|
||||
compatible = "devantech,srf04";
|
||||
trig-gpios = <&gpio1 15 GPIO_ACTIVE_HIGH>;
|
||||
echo-gpios = <&gpio2 6 GPIO_ACTIVE_HIGH>;
|
||||
};
|
20
MAINTAINERS
20
MAINTAINERS
@ -813,6 +813,7 @@ W: http://wiki.analog.com/
|
||||
W: http://ez.analog.com/community/linux-device-drivers
|
||||
S: Supported
|
||||
F: drivers/iio/*/ad*
|
||||
F: drivers/iio/adc/ltc2497*
|
||||
X: drivers/iio/*/adjd*
|
||||
F: drivers/staging/iio/*/ad*
|
||||
F: drivers/staging/iio/trigger/iio-trig-bfin-timer.c
|
||||
@ -3050,7 +3051,6 @@ CAPELLA MICROSYSTEMS LIGHT SENSOR DRIVER
|
||||
M: Kevin Tsai <ktsai@capellamicro.com>
|
||||
S: Maintained
|
||||
F: drivers/iio/light/cm*
|
||||
F: Documentation/devicetree/bindings/i2c/trivial-admin-guide/devices.rst
|
||||
|
||||
CAVIUM THUNDERX2 ARM64 SOC
|
||||
M: Jayachandran C <jnair@caviumnetworks.com>
|
||||
@ -3886,6 +3886,12 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb.git
|
||||
S: Maintained
|
||||
F: drivers/usb/dwc3/
|
||||
|
||||
DEVANTECH SRF ULTRASONIC RANGER IIO DRIVER
|
||||
M: Andreas Klinger <ak@it-klinger.de>
|
||||
L: linux-iio@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/iio/proximity/srf*.c
|
||||
|
||||
DEVICE COREDUMP (DEV_COREDUMP)
|
||||
M: Johannes Berg <johannes@sipsolutions.net>
|
||||
L: linux-kernel@vger.kernel.org
|
||||
@ -4131,6 +4137,18 @@ S: Maintained
|
||||
F: drivers/char/dtlk.c
|
||||
F: include/linux/dtlk.h
|
||||
|
||||
DPAA2 DATAPATH I/O (DPIO) DRIVER
|
||||
M: Roy Pledge <Roy.Pledge@nxp.com>
|
||||
L: linux-kernel@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/staging/fsl-mc/bus/dpio
|
||||
|
||||
DPAA2 ETHERNET DRIVER
|
||||
M: Ioana Radulescu <ruxandra.radulescu@nxp.com>
|
||||
L: linux-kernel@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/staging/fsl-dpaa2/ethernet
|
||||
|
||||
DPT_I2O SCSI RAID DRIVER
|
||||
M: Adaptec OEM Raid Solutions <aacraid@adaptec.com>
|
||||
L: linux-scsi@vger.kernel.org
|
||||
|
@ -101,7 +101,8 @@ void __init kvm_cma_reserve(void)
|
||||
(unsigned long)selected_size / SZ_1M);
|
||||
align_size = HPT_ALIGN_PAGES << PAGE_SHIFT;
|
||||
cma_declare_contiguous(0, selected_size, 0, align_size,
|
||||
KVM_CMA_CHUNK_ORDER - PAGE_SHIFT, false, &kvm_cma);
|
||||
KVM_CMA_CHUNK_ORDER - PAGE_SHIFT, false, "kvm_cma",
|
||||
&kvm_cma);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -165,7 +165,8 @@ int __init dma_contiguous_reserve_area(phys_addr_t size, phys_addr_t base,
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = cma_declare_contiguous(base, size, limit, 0, 0, fixed, res_cma);
|
||||
ret = cma_declare_contiguous(base, size, limit, 0, 0, fixed,
|
||||
"reserved", res_cma);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -258,7 +259,7 @@ static int __init rmem_cma_setup(struct reserved_mem *rmem)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = cma_init_reserved_mem(rmem->base, rmem->size, 0, &cma);
|
||||
err = cma_init_reserved_mem(rmem->base, rmem->size, 0, rmem->name, &cma);
|
||||
if (err) {
|
||||
pr_err("Reserved memory: unable to setup CMA region\n");
|
||||
return err;
|
||||
|
@ -5,6 +5,37 @@
|
||||
|
||||
menu "Accelerometers"
|
||||
|
||||
config ADXL345
|
||||
tristate
|
||||
|
||||
config ADXL345_I2C
|
||||
tristate "Analog Devices ADXL345 3-Axis Digital Accelerometer I2C Driver"
|
||||
depends on INPUT_ADXL34X=n
|
||||
depends on I2C
|
||||
select ADXL345
|
||||
select REGMAP_I2C
|
||||
help
|
||||
Say Y here if you want to build support for the Analog Devices
|
||||
ADXL345 3-axis digital accelerometer.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called adxl345_i2c and you will also get adxl345_core
|
||||
for the core module.
|
||||
|
||||
config ADXL345_SPI
|
||||
tristate "Analog Devices ADXL345 3-Axis Digital Accelerometer SPI Driver"
|
||||
depends on INPUT_ADXL34X=n
|
||||
depends on SPI
|
||||
select ADXL345
|
||||
select REGMAP_SPI
|
||||
help
|
||||
Say Y here if you want to build support for the Analog Devices
|
||||
ADXL345 3-axis digital accelerometer.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called adxl345_spi and you will also get adxl345_core
|
||||
for the core module.
|
||||
|
||||
config BMA180
|
||||
tristate "Bosch BMA180/BMA250 3-Axis Accelerometer Driver"
|
||||
depends on I2C
|
||||
|
@ -3,6 +3,9 @@
|
||||
#
|
||||
|
||||
# When adding new entries keep the list in alphabetical order
|
||||
obj-$(CONFIG_ADXL345) += adxl345_core.o
|
||||
obj-$(CONFIG_ADXL345_I2C) += adxl345_i2c.o
|
||||
obj-$(CONFIG_ADXL345_SPI) += adxl345_spi.o
|
||||
obj-$(CONFIG_BMA180) += bma180.o
|
||||
obj-$(CONFIG_BMA220) += bma220_spi.o
|
||||
obj-$(CONFIG_BMC150_ACCEL) += bmc150-accel-core.o
|
||||
|
18
drivers/iio/accel/adxl345.h
Normal file
18
drivers/iio/accel/adxl345.h
Normal file
@ -0,0 +1,18 @@
|
||||
/*
|
||||
* ADXL345 3-Axis Digital Accelerometer
|
||||
*
|
||||
* Copyright (c) 2017 Eva Rachel Retuya <eraretuya@gmail.com>
|
||||
*
|
||||
* This file is subject to the terms and conditions of version 2 of
|
||||
* the GNU General Public License. See the file COPYING in the main
|
||||
* directory of this archive for more details.
|
||||
*/
|
||||
|
||||
#ifndef _ADXL345_H_
|
||||
#define _ADXL345_H_
|
||||
|
||||
int adxl345_core_probe(struct device *dev, struct regmap *regmap,
|
||||
const char *name);
|
||||
int adxl345_core_remove(struct device *dev);
|
||||
|
||||
#endif /* _ADXL345_H_ */
|
179
drivers/iio/accel/adxl345_core.c
Normal file
179
drivers/iio/accel/adxl345_core.c
Normal file
@ -0,0 +1,179 @@
|
||||
/*
|
||||
* ADXL345 3-Axis Digital Accelerometer IIO core driver
|
||||
*
|
||||
* Copyright (c) 2017 Eva Rachel Retuya <eraretuya@gmail.com>
|
||||
*
|
||||
* This file is subject to the terms and conditions of version 2 of
|
||||
* the GNU General Public License. See the file COPYING in the main
|
||||
* directory of this archive for more details.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
|
||||
#include "adxl345.h"
|
||||
|
||||
#define ADXL345_REG_DEVID 0x00
|
||||
#define ADXL345_REG_POWER_CTL 0x2D
|
||||
#define ADXL345_REG_DATA_FORMAT 0x31
|
||||
#define ADXL345_REG_DATAX0 0x32
|
||||
#define ADXL345_REG_DATAY0 0x34
|
||||
#define ADXL345_REG_DATAZ0 0x36
|
||||
|
||||
#define ADXL345_POWER_CTL_MEASURE BIT(3)
|
||||
#define ADXL345_POWER_CTL_STANDBY 0x00
|
||||
|
||||
#define ADXL345_DATA_FORMAT_FULL_RES BIT(3) /* Up to 13-bits resolution */
|
||||
#define ADXL345_DATA_FORMAT_2G 0
|
||||
#define ADXL345_DATA_FORMAT_4G 1
|
||||
#define ADXL345_DATA_FORMAT_8G 2
|
||||
#define ADXL345_DATA_FORMAT_16G 3
|
||||
|
||||
#define ADXL345_DEVID 0xE5
|
||||
|
||||
/*
|
||||
* In full-resolution mode, scale factor is maintained at ~4 mg/LSB
|
||||
* in all g ranges.
|
||||
*
|
||||
* At +/- 16g with 13-bit resolution, scale is computed as:
|
||||
* (16 + 16) * 9.81 / (2^13 - 1) = 0.0383
|
||||
*/
|
||||
static const int adxl345_uscale = 38300;
|
||||
|
||||
struct adxl345_data {
|
||||
struct regmap *regmap;
|
||||
u8 data_range;
|
||||
};
|
||||
|
||||
#define ADXL345_CHANNEL(reg, axis) { \
|
||||
.type = IIO_ACCEL, \
|
||||
.modified = 1, \
|
||||
.channel2 = IIO_MOD_##axis, \
|
||||
.address = reg, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec adxl345_channels[] = {
|
||||
ADXL345_CHANNEL(ADXL345_REG_DATAX0, X),
|
||||
ADXL345_CHANNEL(ADXL345_REG_DATAY0, Y),
|
||||
ADXL345_CHANNEL(ADXL345_REG_DATAZ0, Z),
|
||||
};
|
||||
|
||||
static int adxl345_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct adxl345_data *data = iio_priv(indio_dev);
|
||||
__le16 regval;
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
/*
|
||||
* Data is stored in adjacent registers:
|
||||
* ADXL345_REG_DATA(X0/Y0/Z0) contain the least significant byte
|
||||
* and ADXL345_REG_DATA(X0/Y0/Z0) + 1 the most significant byte
|
||||
*/
|
||||
ret = regmap_bulk_read(data->regmap, chan->address, ®val,
|
||||
sizeof(regval));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = sign_extend32(le16_to_cpu(regval), 12);
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = 0;
|
||||
*val2 = adxl345_uscale;
|
||||
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct iio_info adxl345_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = adxl345_read_raw,
|
||||
};
|
||||
|
||||
int adxl345_core_probe(struct device *dev, struct regmap *regmap,
|
||||
const char *name)
|
||||
{
|
||||
struct adxl345_data *data;
|
||||
struct iio_dev *indio_dev;
|
||||
u32 regval;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(regmap, ADXL345_REG_DEVID, ®val);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Error reading device ID: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (regval != ADXL345_DEVID) {
|
||||
dev_err(dev, "Invalid device ID: %x, expected %x\n",
|
||||
regval, ADXL345_DEVID);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
data = iio_priv(indio_dev);
|
||||
dev_set_drvdata(dev, indio_dev);
|
||||
data->regmap = regmap;
|
||||
/* Enable full-resolution mode */
|
||||
data->data_range = ADXL345_DATA_FORMAT_FULL_RES;
|
||||
|
||||
ret = regmap_write(data->regmap, ADXL345_REG_DATA_FORMAT,
|
||||
data->data_range);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Failed to set data range: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
indio_dev->dev.parent = dev;
|
||||
indio_dev->name = name;
|
||||
indio_dev->info = &adxl345_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = adxl345_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(adxl345_channels);
|
||||
|
||||
/* Enable measurement mode */
|
||||
ret = regmap_write(data->regmap, ADXL345_REG_POWER_CTL,
|
||||
ADXL345_POWER_CTL_MEASURE);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Failed to enable measurement mode: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "iio_device_register failed: %d\n", ret);
|
||||
regmap_write(data->regmap, ADXL345_REG_POWER_CTL,
|
||||
ADXL345_POWER_CTL_STANDBY);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(adxl345_core_probe);
|
||||
|
||||
int adxl345_core_remove(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct adxl345_data *data = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
return regmap_write(data->regmap, ADXL345_REG_POWER_CTL,
|
||||
ADXL345_POWER_CTL_STANDBY);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(adxl345_core_remove);
|
||||
|
||||
MODULE_AUTHOR("Eva Rachel Retuya <eraretuya@gmail.com>");
|
||||
MODULE_DESCRIPTION("ADXL345 3-Axis Digital Accelerometer core driver");
|
||||
MODULE_LICENSE("GPL v2");
|
73
drivers/iio/accel/adxl345_i2c.c
Normal file
73
drivers/iio/accel/adxl345_i2c.c
Normal file
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* ADXL345 3-Axis Digital Accelerometer I2C driver
|
||||
*
|
||||
* Copyright (c) 2017 Eva Rachel Retuya <eraretuya@gmail.com>
|
||||
*
|
||||
* This file is subject to the terms and conditions of version 2 of
|
||||
* the GNU General Public License. See the file COPYING in the main
|
||||
* directory of this archive for more details.
|
||||
*
|
||||
* 7-bit I2C slave address: 0x1D (ALT ADDRESS pin tied to VDDIO) or
|
||||
* 0x53 (ALT ADDRESS pin grounded)
|
||||
*/
|
||||
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include "adxl345.h"
|
||||
|
||||
static const struct regmap_config adxl345_i2c_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
};
|
||||
|
||||
static int adxl345_i2c_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct regmap *regmap;
|
||||
|
||||
regmap = devm_regmap_init_i2c(client, &adxl345_i2c_regmap_config);
|
||||
if (IS_ERR(regmap)) {
|
||||
dev_err(&client->dev, "Error initializing i2c regmap: %ld\n",
|
||||
PTR_ERR(regmap));
|
||||
return PTR_ERR(regmap);
|
||||
}
|
||||
|
||||
return adxl345_core_probe(&client->dev, regmap, id ? id->name : NULL);
|
||||
}
|
||||
|
||||
static int adxl345_i2c_remove(struct i2c_client *client)
|
||||
{
|
||||
return adxl345_core_remove(&client->dev);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id adxl345_i2c_id[] = {
|
||||
{ "adxl345", 0 },
|
||||
{ }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(i2c, adxl345_i2c_id);
|
||||
|
||||
static const struct of_device_id adxl345_of_match[] = {
|
||||
{ .compatible = "adi,adxl345" },
|
||||
{ },
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, adxl345_of_match);
|
||||
|
||||
static struct i2c_driver adxl345_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "adxl345_i2c",
|
||||
.of_match_table = adxl345_of_match,
|
||||
},
|
||||
.probe = adxl345_i2c_probe,
|
||||
.remove = adxl345_i2c_remove,
|
||||
.id_table = adxl345_i2c_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(adxl345_i2c_driver);
|
||||
|
||||
MODULE_AUTHOR("Eva Rachel Retuya <eraretuya@gmail.com>");
|
||||
MODULE_DESCRIPTION("ADXL345 3-Axis Digital Accelerometer I2C driver");
|
||||
MODULE_LICENSE("GPL v2");
|
81
drivers/iio/accel/adxl345_spi.c
Normal file
81
drivers/iio/accel/adxl345_spi.c
Normal file
@ -0,0 +1,81 @@
|
||||
/*
|
||||
* ADXL345 3-Axis Digital Accelerometer SPI driver
|
||||
*
|
||||
* Copyright (c) 2017 Eva Rachel Retuya <eraretuya@gmail.com>
|
||||
*
|
||||
* This file is subject to the terms and conditions of version 2 of
|
||||
* the GNU General Public License. See the file COPYING in the main
|
||||
* directory of this archive for more details.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#include "adxl345.h"
|
||||
|
||||
#define ADXL345_MAX_SPI_FREQ_HZ 5000000
|
||||
|
||||
static const struct regmap_config adxl345_spi_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
/* Setting bits 7 and 6 enables multiple-byte read */
|
||||
.read_flag_mask = BIT(7) | BIT(6),
|
||||
};
|
||||
|
||||
static int adxl345_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
const struct spi_device_id *id = spi_get_device_id(spi);
|
||||
struct regmap *regmap;
|
||||
|
||||
/* Bail out if max_speed_hz exceeds 5 MHz */
|
||||
if (spi->max_speed_hz > ADXL345_MAX_SPI_FREQ_HZ) {
|
||||
dev_err(&spi->dev, "SPI CLK, %d Hz exceeds 5 MHz\n",
|
||||
spi->max_speed_hz);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
regmap = devm_regmap_init_spi(spi, &adxl345_spi_regmap_config);
|
||||
if (IS_ERR(regmap)) {
|
||||
dev_err(&spi->dev, "Error initializing spi regmap: %ld\n",
|
||||
PTR_ERR(regmap));
|
||||
return PTR_ERR(regmap);
|
||||
}
|
||||
|
||||
return adxl345_core_probe(&spi->dev, regmap, id->name);
|
||||
}
|
||||
|
||||
static int adxl345_spi_remove(struct spi_device *spi)
|
||||
{
|
||||
return adxl345_core_remove(&spi->dev);
|
||||
}
|
||||
|
||||
static const struct spi_device_id adxl345_spi_id[] = {
|
||||
{ "adxl345", 0 },
|
||||
{ }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(spi, adxl345_spi_id);
|
||||
|
||||
static const struct of_device_id adxl345_of_match[] = {
|
||||
{ .compatible = "adi,adxl345" },
|
||||
{ },
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, adxl345_of_match);
|
||||
|
||||
static struct spi_driver adxl345_spi_driver = {
|
||||
.driver = {
|
||||
.name = "adxl345_spi",
|
||||
.of_match_table = adxl345_of_match,
|
||||
},
|
||||
.probe = adxl345_spi_probe,
|
||||
.remove = adxl345_spi_remove,
|
||||
.id_table = adxl345_spi_id,
|
||||
};
|
||||
|
||||
module_spi_driver(adxl345_spi_driver);
|
||||
|
||||
MODULE_AUTHOR("Eva Rachel Retuya <eraretuya@gmail.com>");
|
||||
MODULE_DESCRIPTION("ADXL345 3-Axis Digital Accelerometer SPI driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -18,6 +18,7 @@
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/slab.h>
|
||||
@ -32,7 +33,7 @@
|
||||
#define BMA180_DRV_NAME "bma180"
|
||||
#define BMA180_IRQ_NAME "bma180_event"
|
||||
|
||||
enum {
|
||||
enum chip_ids {
|
||||
BMA180,
|
||||
BMA250,
|
||||
};
|
||||
@ -41,11 +42,11 @@ struct bma180_data;
|
||||
|
||||
struct bma180_part_info {
|
||||
const struct iio_chan_spec *channels;
|
||||
unsigned num_channels;
|
||||
unsigned int num_channels;
|
||||
const int *scale_table;
|
||||
unsigned num_scales;
|
||||
unsigned int num_scales;
|
||||
const int *bw_table;
|
||||
unsigned num_bw;
|
||||
unsigned int num_bw;
|
||||
|
||||
u8 int_reset_reg, int_reset_mask;
|
||||
u8 sleep_reg, sleep_mask;
|
||||
@ -408,7 +409,7 @@ static void bma250_chip_disable(struct bma180_data *data)
|
||||
dev_err(&data->client->dev, "failed to disable the chip\n");
|
||||
}
|
||||
|
||||
static ssize_t bma180_show_avail(char *buf, const int *vals, unsigned n,
|
||||
static ssize_t bma180_show_avail(char *buf, const int *vals, unsigned int n,
|
||||
bool micros)
|
||||
{
|
||||
size_t len = 0;
|
||||
@ -707,6 +708,7 @@ static int bma180_probe(struct i2c_client *client,
|
||||
{
|
||||
struct bma180_data *data;
|
||||
struct iio_dev *indio_dev;
|
||||
enum chip_ids chip;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
||||
@ -716,7 +718,11 @@ static int bma180_probe(struct i2c_client *client,
|
||||
data = iio_priv(indio_dev);
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
data->client = client;
|
||||
data->part_info = &bma180_part_info[id->driver_data];
|
||||
if (client->dev.of_node)
|
||||
chip = (enum chip_ids)of_device_get_match_data(&client->dev);
|
||||
else
|
||||
chip = id->driver_data;
|
||||
data->part_info = &bma180_part_info[chip];
|
||||
|
||||
ret = data->part_info->chip_config(data);
|
||||
if (ret < 0)
|
||||
@ -844,10 +850,24 @@ static struct i2c_device_id bma180_ids[] = {
|
||||
|
||||
MODULE_DEVICE_TABLE(i2c, bma180_ids);
|
||||
|
||||
static const struct of_device_id bma180_of_match[] = {
|
||||
{
|
||||
.compatible = "bosch,bma180",
|
||||
.data = (void *)BMA180
|
||||
},
|
||||
{
|
||||
.compatible = "bosch,bma250",
|
||||
.data = (void *)BMA250
|
||||
},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, bma180_of_match);
|
||||
|
||||
static struct i2c_driver bma180_driver = {
|
||||
.driver = {
|
||||
.name = "bma180",
|
||||
.pm = BMA180_PM_OPS,
|
||||
.of_match_table = bma180_of_match,
|
||||
},
|
||||
.probe = bma180_probe,
|
||||
.remove = bma180_remove,
|
||||
|
@ -41,12 +41,20 @@ static const struct i2c_device_id mma7455_i2c_ids[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, mma7455_i2c_ids);
|
||||
|
||||
static const struct of_device_id mma7455_of_match[] = {
|
||||
{ .compatible = "fsl,mma7455" },
|
||||
{ .compatible = "fsl,mma7456" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mma7455_of_match);
|
||||
|
||||
static struct i2c_driver mma7455_i2c_driver = {
|
||||
.probe = mma7455_i2c_probe,
|
||||
.remove = mma7455_i2c_remove,
|
||||
.id_table = mma7455_i2c_ids,
|
||||
.driver = {
|
||||
.name = "mma7455-i2c",
|
||||
.of_match_table = mma7455_of_match,
|
||||
},
|
||||
};
|
||||
module_i2c_driver(mma7455_i2c_driver);
|
||||
|
@ -253,6 +253,12 @@ static const struct i2c_device_id mma7660_i2c_id[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, mma7660_i2c_id);
|
||||
|
||||
static const struct of_device_id mma7660_of_match[] = {
|
||||
{ .compatible = "fsl,mma7660" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mma7660_of_match);
|
||||
|
||||
static const struct acpi_device_id mma7660_acpi_id[] = {
|
||||
{"MMA7660", 0},
|
||||
{}
|
||||
@ -264,6 +270,7 @@ static struct i2c_driver mma7660_driver = {
|
||||
.driver = {
|
||||
.name = "mma7660",
|
||||
.pm = MMA7660_PM_OPS,
|
||||
.of_match_table = mma7660_of_match,
|
||||
.acpi_match_table = ACPI_PTR(mma7660_acpi_id),
|
||||
},
|
||||
.probe = mma7660_probe,
|
||||
|
@ -130,6 +130,17 @@ config AD799X
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called ad799x.
|
||||
|
||||
config ASPEED_ADC
|
||||
tristate "Aspeed ADC"
|
||||
depends on ARCH_ASPEED || COMPILE_TEST
|
||||
depends on COMMON_CLK
|
||||
help
|
||||
If you say yes here you get support for the ADC included in Aspeed
|
||||
BMC SoCs.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called aspeed_adc.
|
||||
|
||||
config AT91_ADC
|
||||
tristate "Atmel AT91 ADC"
|
||||
depends on ARCH_AT91
|
||||
@ -205,6 +216,17 @@ config CC10001_ADC
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called cc10001_adc.
|
||||
|
||||
config CPCAP_ADC
|
||||
tristate "Motorola CPCAP PMIC ADC driver"
|
||||
depends on MFD_CPCAP
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
Say yes here to build support for Motorola CPCAP PMIC ADC.
|
||||
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called cpcap-adc.
|
||||
|
||||
config DA9150_GPADC
|
||||
tristate "Dialog DA9150 GPADC driver support"
|
||||
depends on MFD_DA9150
|
||||
@ -328,6 +350,18 @@ config LPC18XX_ADC
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called lpc18xx_adc.
|
||||
|
||||
config LPC32XX_ADC
|
||||
tristate "NXP LPC32XX ADC"
|
||||
depends on ARCH_LPC32XX || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
Say yes here to build support for the integrated ADC inside the
|
||||
LPC32XX SoC. Note that this feature uses the same hardware as the
|
||||
touchscreen driver, so you should either select only one of the two
|
||||
drivers (lpc32xx_adc or lpc32xx_ts) or, in the OpenFirmware case,
|
||||
activate only one via device tree selection. Provides direct access
|
||||
via sysfs.
|
||||
|
||||
config LTC2485
|
||||
tristate "Linear Technology LTC2485 ADC driver"
|
||||
depends on I2C
|
||||
@ -337,6 +371,16 @@ config LTC2485
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called ltc2485.
|
||||
|
||||
config LTC2497
|
||||
tristate "Linear Technology LTC2497 ADC driver"
|
||||
depends on I2C
|
||||
help
|
||||
Say yes here to build support for Linear Technology LTC2497
|
||||
16-Bit 8-/16-Channel Delta Sigma ADC.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called ltc2497.
|
||||
|
||||
config MAX1027
|
||||
tristate "Maxim max1027 ADC driver"
|
||||
depends on SPI
|
||||
@ -358,6 +402,18 @@ config MAX11100
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called max11100.
|
||||
|
||||
config MAX1118
|
||||
tristate "Maxim max1117/max1118/max1119 ADCs driver"
|
||||
depends on SPI
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
Say yes here to build support for Maxim max1117/max1118/max1119
|
||||
8-bit, dual-channel ADCs.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called max1118.
|
||||
|
||||
config MAX1363
|
||||
tristate "Maxim max1363 ADC driver"
|
||||
depends on I2C
|
||||
@ -377,6 +433,16 @@ config MAX1363
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called max1363.
|
||||
|
||||
config MAX9611
|
||||
tristate "Maxim max9611/max9612 ADC driver"
|
||||
depends on I2C
|
||||
help
|
||||
Say yes here to build support for Maxim max9611/max9612 current sense
|
||||
amplifier with 12-bits ADC interface.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called max9611.
|
||||
|
||||
config MCP320X
|
||||
tristate "Microchip Technology MCP3x01/02/04/08"
|
||||
depends on SPI
|
||||
@ -451,6 +517,20 @@ config PALMAS_GPADC
|
||||
is used in smartphones and tablets and supports a 16 channel
|
||||
general purpose ADC.
|
||||
|
||||
config QCOM_VADC_COMMON
|
||||
tristate
|
||||
|
||||
config QCOM_PM8XXX_XOADC
|
||||
tristate "Qualcomm SSBI PM8xxx PMIC XOADCs"
|
||||
depends on MFD_PM8XXX
|
||||
select QCOM_VADC_COMMON
|
||||
help
|
||||
ADC driver for the XOADC portions of the Qualcomm PM8xxx PMICs
|
||||
using SSBI transport: PM8018, PM8038, PM8058, PM8921.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called qcom-pm8xxx-xoadc.
|
||||
|
||||
config QCOM_SPMI_IADC
|
||||
tristate "Qualcomm SPMI PMIC current ADC"
|
||||
depends on SPMI
|
||||
@ -469,6 +549,7 @@ config QCOM_SPMI_VADC
|
||||
tristate "Qualcomm SPMI PMIC voltage ADC"
|
||||
depends on SPMI
|
||||
select REGMAP_SPMI
|
||||
select QCOM_VADC_COMMON
|
||||
help
|
||||
This is the IIO Voltage ADC driver for Qualcomm QPNP VADC Chip.
|
||||
|
||||
@ -503,6 +584,17 @@ config ROCKCHIP_SARADC
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called rockchip_saradc.
|
||||
|
||||
config SPEAR_ADC
|
||||
tristate "ST SPEAr ADC"
|
||||
depends on PLAT_SPEAR || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
Say yes here to build support for the integrated ADC inside the
|
||||
ST SPEAr SoC. Provides direct access via sysfs.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called spear_adc.
|
||||
|
||||
config STM32_ADC_CORE
|
||||
tristate "STMicroelectronics STM32 adc core"
|
||||
depends on ARCH_STM32 || COMPILE_TEST
|
||||
@ -532,7 +624,7 @@ config STM32_ADC
|
||||
|
||||
config STX104
|
||||
tristate "Apex Embedded Systems STX104 driver"
|
||||
depends on X86 && ISA_BUS_API
|
||||
depends on PC104 && X86 && ISA_BUS_API
|
||||
select GPIOLIB
|
||||
help
|
||||
Say yes here to build support for the Apex Embedded Systems STX104
|
||||
@ -545,6 +637,24 @@ config STX104
|
||||
The base port addresses for the devices may be configured via the base
|
||||
array module parameter.
|
||||
|
||||
config SUN4I_GPADC
|
||||
tristate "Support for the Allwinner SoCs GPADC"
|
||||
depends on IIO
|
||||
depends on MFD_SUN4I_GPADC || MACH_SUN8I
|
||||
depends on THERMAL || !THERMAL_OF
|
||||
help
|
||||
Say yes here to build support for Allwinner (A10, A13 and A31) SoCs
|
||||
GPADC. This ADC provides 4 channels which can be used as an ADC or as
|
||||
a touchscreen input and one channel for thermal sensor.
|
||||
|
||||
The thermal sensor slows down ADC readings and can be disabled by
|
||||
disabling CONFIG_THERMAL_OF. However, the thermal sensor should be
|
||||
enabled by default since the SoC temperature is usually more critical
|
||||
than ADC readings.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called sun4i-gpadc-iio.
|
||||
|
||||
config TI_ADC081C
|
||||
tristate "Texas Instruments ADC081C/ADC101C/ADC121C family"
|
||||
depends on I2C
|
||||
|
@ -14,6 +14,7 @@ obj-$(CONFIG_AD7791) += ad7791.o
|
||||
obj-$(CONFIG_AD7793) += ad7793.o
|
||||
obj-$(CONFIG_AD7887) += ad7887.o
|
||||
obj-$(CONFIG_AD799X) += ad799x.o
|
||||
obj-$(CONFIG_ASPEED_ADC) += aspeed_adc.o
|
||||
obj-$(CONFIG_AT91_ADC) += at91_adc.o
|
||||
obj-$(CONFIG_AT91_SAMA5D2_ADC) += at91-sama5d2_adc.o
|
||||
obj-$(CONFIG_AXP20X_ADC) += axp20x_adc.o
|
||||
@ -21,6 +22,7 @@ obj-$(CONFIG_AXP288_ADC) += axp288_adc.o
|
||||
obj-$(CONFIG_BCM_IPROC_ADC) += bcm_iproc_adc.o
|
||||
obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o
|
||||
obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o
|
||||
obj-$(CONFIG_CPCAP_ADC) += cpcap-adc.o
|
||||
obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o
|
||||
obj-$(CONFIG_ENVELOPE_DETECTOR) += envelope-detector.o
|
||||
obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
|
||||
@ -31,10 +33,14 @@ 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_LPC32XX_ADC) += lpc32xx_adc.o
|
||||
obj-$(CONFIG_LTC2485) += ltc2485.o
|
||||
obj-$(CONFIG_LTC2497) += ltc2497.o
|
||||
obj-$(CONFIG_MAX1027) += max1027.o
|
||||
obj-$(CONFIG_MAX11100) += max11100.o
|
||||
obj-$(CONFIG_MAX1118) += max1118.o
|
||||
obj-$(CONFIG_MAX1363) += max1363.o
|
||||
obj-$(CONFIG_MAX9611) += max9611.o
|
||||
obj-$(CONFIG_MCP320X) += mcp320x.o
|
||||
obj-$(CONFIG_MCP3422) += mcp3422.o
|
||||
obj-$(CONFIG_MEDIATEK_MT6577_AUXADC) += mt6577_auxadc.o
|
||||
@ -44,10 +50,14 @@ obj-$(CONFIG_MXS_LRADC_ADC) += mxs-lradc-adc.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_VADC_COMMON) += qcom-vadc-common.o
|
||||
obj-$(CONFIG_QCOM_SPMI_VADC) += qcom-spmi-vadc.o
|
||||
obj-$(CONFIG_QCOM_PM8XXX_XOADC) += qcom-pm8xxx-xoadc.o
|
||||
obj-$(CONFIG_RCAR_GYRO_ADC) += rcar-gyroadc.o
|
||||
obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
|
||||
obj-$(CONFIG_SPEAR_ADC) += spear_adc.o
|
||||
obj-$(CONFIG_STX104) += stx104.o
|
||||
obj-$(CONFIG_SUN4I_GPADC) += sun4i-gpadc-iio.o
|
||||
obj-$(CONFIG_STM32_ADC_CORE) += stm32-adc-core.o
|
||||
obj-$(CONFIG_STM32_ADC) += stm32-adc.o
|
||||
obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
|
||||
|
@ -520,7 +520,7 @@ static struct attribute *ad799x_event_attributes[] = {
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group ad799x_event_attrs_group = {
|
||||
static const struct attribute_group ad799x_event_attrs_group = {
|
||||
.attrs = ad799x_event_attributes,
|
||||
};
|
||||
|
||||
|
295
drivers/iio/adc/aspeed_adc.c
Normal file
295
drivers/iio/adc/aspeed_adc.c
Normal file
@ -0,0 +1,295 @@
|
||||
/*
|
||||
* Aspeed AST2400/2500 ADC
|
||||
*
|
||||
* Copyright (C) 2017 Google, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/driver.h>
|
||||
|
||||
#define ASPEED_RESOLUTION_BITS 10
|
||||
#define ASPEED_CLOCKS_PER_SAMPLE 12
|
||||
|
||||
#define ASPEED_REG_ENGINE_CONTROL 0x00
|
||||
#define ASPEED_REG_INTERRUPT_CONTROL 0x04
|
||||
#define ASPEED_REG_VGA_DETECT_CONTROL 0x08
|
||||
#define ASPEED_REG_CLOCK_CONTROL 0x0C
|
||||
#define ASPEED_REG_MAX 0xC0
|
||||
|
||||
#define ASPEED_OPERATION_MODE_POWER_DOWN (0x0 << 1)
|
||||
#define ASPEED_OPERATION_MODE_STANDBY (0x1 << 1)
|
||||
#define ASPEED_OPERATION_MODE_NORMAL (0x7 << 1)
|
||||
|
||||
#define ASPEED_ENGINE_ENABLE BIT(0)
|
||||
|
||||
struct aspeed_adc_model_data {
|
||||
const char *model_name;
|
||||
unsigned int min_sampling_rate; // Hz
|
||||
unsigned int max_sampling_rate; // Hz
|
||||
unsigned int vref_voltage; // mV
|
||||
};
|
||||
|
||||
struct aspeed_adc_data {
|
||||
struct device *dev;
|
||||
void __iomem *base;
|
||||
spinlock_t clk_lock;
|
||||
struct clk_hw *clk_prescaler;
|
||||
struct clk_hw *clk_scaler;
|
||||
};
|
||||
|
||||
#define ASPEED_CHAN(_idx, _data_reg_addr) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = (_idx), \
|
||||
.address = (_data_reg_addr), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ), \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec aspeed_adc_iio_channels[] = {
|
||||
ASPEED_CHAN(0, 0x10),
|
||||
ASPEED_CHAN(1, 0x12),
|
||||
ASPEED_CHAN(2, 0x14),
|
||||
ASPEED_CHAN(3, 0x16),
|
||||
ASPEED_CHAN(4, 0x18),
|
||||
ASPEED_CHAN(5, 0x1A),
|
||||
ASPEED_CHAN(6, 0x1C),
|
||||
ASPEED_CHAN(7, 0x1E),
|
||||
ASPEED_CHAN(8, 0x20),
|
||||
ASPEED_CHAN(9, 0x22),
|
||||
ASPEED_CHAN(10, 0x24),
|
||||
ASPEED_CHAN(11, 0x26),
|
||||
ASPEED_CHAN(12, 0x28),
|
||||
ASPEED_CHAN(13, 0x2A),
|
||||
ASPEED_CHAN(14, 0x2C),
|
||||
ASPEED_CHAN(15, 0x2E),
|
||||
};
|
||||
|
||||
static int aspeed_adc_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct aspeed_adc_data *data = iio_priv(indio_dev);
|
||||
const struct aspeed_adc_model_data *model_data =
|
||||
of_device_get_match_data(data->dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
*val = readw(data->base + chan->address);
|
||||
return IIO_VAL_INT;
|
||||
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = model_data->vref_voltage;
|
||||
*val2 = ASPEED_RESOLUTION_BITS;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
*val = clk_get_rate(data->clk_scaler->clk) /
|
||||
ASPEED_CLOCKS_PER_SAMPLE;
|
||||
return IIO_VAL_INT;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int aspeed_adc_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
struct aspeed_adc_data *data = iio_priv(indio_dev);
|
||||
const struct aspeed_adc_model_data *model_data =
|
||||
of_device_get_match_data(data->dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
if (val < model_data->min_sampling_rate ||
|
||||
val > model_data->max_sampling_rate)
|
||||
return -EINVAL;
|
||||
|
||||
clk_set_rate(data->clk_scaler->clk,
|
||||
val * ASPEED_CLOCKS_PER_SAMPLE);
|
||||
return 0;
|
||||
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
/*
|
||||
* Technically, these could be written but the only reasons
|
||||
* for doing so seem better handled in userspace. EPERM is
|
||||
* returned to signal this is a policy choice rather than a
|
||||
* hardware limitation.
|
||||
*/
|
||||
return -EPERM;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int aspeed_adc_reg_access(struct iio_dev *indio_dev,
|
||||
unsigned int reg, unsigned int writeval,
|
||||
unsigned int *readval)
|
||||
{
|
||||
struct aspeed_adc_data *data = iio_priv(indio_dev);
|
||||
|
||||
if (!readval || reg % 4 || reg > ASPEED_REG_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
*readval = readl(data->base + reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct iio_info aspeed_adc_iio_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = aspeed_adc_read_raw,
|
||||
.write_raw = aspeed_adc_write_raw,
|
||||
.debugfs_reg_access = aspeed_adc_reg_access,
|
||||
};
|
||||
|
||||
static int aspeed_adc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct aspeed_adc_data *data;
|
||||
const struct aspeed_adc_model_data *model_data;
|
||||
struct resource *res;
|
||||
const char *clk_parent_name;
|
||||
int ret;
|
||||
u32 adc_engine_control_reg_val;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
data = iio_priv(indio_dev);
|
||||
data->dev = &pdev->dev;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
data->base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(data->base))
|
||||
return PTR_ERR(data->base);
|
||||
|
||||
/* Register ADC clock prescaler with source specified by device tree. */
|
||||
spin_lock_init(&data->clk_lock);
|
||||
clk_parent_name = of_clk_get_parent_name(pdev->dev.of_node, 0);
|
||||
|
||||
data->clk_prescaler = clk_hw_register_divider(
|
||||
&pdev->dev, "prescaler", clk_parent_name, 0,
|
||||
data->base + ASPEED_REG_CLOCK_CONTROL,
|
||||
17, 15, 0, &data->clk_lock);
|
||||
if (IS_ERR(data->clk_prescaler))
|
||||
return PTR_ERR(data->clk_prescaler);
|
||||
|
||||
/*
|
||||
* Register ADC clock scaler downstream from the prescaler. Allow rate
|
||||
* setting to adjust the prescaler as well.
|
||||
*/
|
||||
data->clk_scaler = clk_hw_register_divider(
|
||||
&pdev->dev, "scaler", "prescaler",
|
||||
CLK_SET_RATE_PARENT,
|
||||
data->base + ASPEED_REG_CLOCK_CONTROL,
|
||||
0, 10, 0, &data->clk_lock);
|
||||
if (IS_ERR(data->clk_scaler)) {
|
||||
ret = PTR_ERR(data->clk_scaler);
|
||||
goto scaler_error;
|
||||
}
|
||||
|
||||
/* Start all channels in normal mode. */
|
||||
clk_prepare_enable(data->clk_scaler->clk);
|
||||
adc_engine_control_reg_val = GENMASK(31, 16) |
|
||||
ASPEED_OPERATION_MODE_NORMAL | ASPEED_ENGINE_ENABLE;
|
||||
writel(adc_engine_control_reg_val,
|
||||
data->base + ASPEED_REG_ENGINE_CONTROL);
|
||||
|
||||
model_data = of_device_get_match_data(&pdev->dev);
|
||||
indio_dev->name = model_data->model_name;
|
||||
indio_dev->dev.parent = &pdev->dev;
|
||||
indio_dev->info = &aspeed_adc_iio_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = aspeed_adc_iio_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(aspeed_adc_iio_channels);
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto iio_register_error;
|
||||
|
||||
return 0;
|
||||
|
||||
iio_register_error:
|
||||
writel(ASPEED_OPERATION_MODE_POWER_DOWN,
|
||||
data->base + ASPEED_REG_ENGINE_CONTROL);
|
||||
clk_disable_unprepare(data->clk_scaler->clk);
|
||||
clk_hw_unregister_divider(data->clk_scaler);
|
||||
|
||||
scaler_error:
|
||||
clk_hw_unregister_divider(data->clk_prescaler);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int aspeed_adc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
struct aspeed_adc_data *data = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
writel(ASPEED_OPERATION_MODE_POWER_DOWN,
|
||||
data->base + ASPEED_REG_ENGINE_CONTROL);
|
||||
clk_disable_unprepare(data->clk_scaler->clk);
|
||||
clk_hw_unregister_divider(data->clk_scaler);
|
||||
clk_hw_unregister_divider(data->clk_prescaler);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct aspeed_adc_model_data ast2400_model_data = {
|
||||
.model_name = "ast2400-adc",
|
||||
.vref_voltage = 2500, // mV
|
||||
.min_sampling_rate = 10000,
|
||||
.max_sampling_rate = 500000,
|
||||
};
|
||||
|
||||
static const struct aspeed_adc_model_data ast2500_model_data = {
|
||||
.model_name = "ast2500-adc",
|
||||
.vref_voltage = 1800, // mV
|
||||
.min_sampling_rate = 1,
|
||||
.max_sampling_rate = 1000000,
|
||||
};
|
||||
|
||||
static const struct of_device_id aspeed_adc_matches[] = {
|
||||
{ .compatible = "aspeed,ast2400-adc", .data = &ast2400_model_data },
|
||||
{ .compatible = "aspeed,ast2500-adc", .data = &ast2500_model_data },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, aspeed_adc_matches);
|
||||
|
||||
static struct platform_driver aspeed_adc_driver = {
|
||||
.probe = aspeed_adc_probe,
|
||||
.remove = aspeed_adc_remove,
|
||||
.driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
.of_match_table = aspeed_adc_matches,
|
||||
}
|
||||
};
|
||||
|
||||
module_platform_driver(aspeed_adc_driver);
|
||||
|
||||
MODULE_AUTHOR("Rick Altherr <raltherr@google.com>");
|
||||
MODULE_DESCRIPTION("Aspeed AST2400/2500 ADC Driver");
|
||||
MODULE_LICENSE("GPL");
|
1007
drivers/iio/adc/cpcap-adc.c
Normal file
1007
drivers/iio/adc/cpcap-adc.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -579,7 +579,7 @@ static int exynos_read_s3c64xx_ts(struct iio_dev *indio_dev, int *x, int *y)
|
||||
|
||||
static irqreturn_t exynos_adc_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct exynos_adc *info = (struct exynos_adc *)dev_id;
|
||||
struct exynos_adc *info = dev_id;
|
||||
u32 mask = info->data->mask;
|
||||
|
||||
/* Read value */
|
||||
|
@ -369,7 +369,7 @@ static struct attribute *hx711_attributes[] = {
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group hx711_attribute_group = {
|
||||
static const struct attribute_group hx711_attribute_group = {
|
||||
.attrs = hx711_attributes,
|
||||
};
|
||||
|
||||
|
@ -365,7 +365,7 @@ static int imx7d_adc_read_data(struct imx7d_adc *info)
|
||||
|
||||
static irqreturn_t imx7d_adc_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct imx7d_adc *info = (struct imx7d_adc *)dev_id;
|
||||
struct imx7d_adc *info = dev_id;
|
||||
int status;
|
||||
|
||||
status = readl(info->regs + IMX7D_REG_ADC_INT_STATUS);
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/util_macros.h>
|
||||
|
||||
@ -635,6 +636,7 @@ static int ina2xx_probe(struct i2c_client *client,
|
||||
struct iio_dev *indio_dev;
|
||||
struct iio_buffer *buffer;
|
||||
unsigned int val;
|
||||
enum ina2xx_ids type;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip));
|
||||
@ -652,7 +654,11 @@ static int ina2xx_probe(struct i2c_client *client,
|
||||
return PTR_ERR(chip->regmap);
|
||||
}
|
||||
|
||||
chip->config = &ina2xx_config[id->driver_data];
|
||||
if (client->dev.of_node)
|
||||
type = (enum ina2xx_ids)of_device_get_match_data(&client->dev);
|
||||
else
|
||||
type = id->driver_data;
|
||||
chip->config = &ina2xx_config[type];
|
||||
|
||||
mutex_init(&chip->state_lock);
|
||||
|
||||
@ -726,9 +732,35 @@ static const struct i2c_device_id ina2xx_id[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ina2xx_id);
|
||||
|
||||
static const struct of_device_id ina2xx_of_match[] = {
|
||||
{
|
||||
.compatible = "ti,ina219",
|
||||
.data = (void *)ina219
|
||||
},
|
||||
{
|
||||
.compatible = "ti,ina220",
|
||||
.data = (void *)ina219
|
||||
},
|
||||
{
|
||||
.compatible = "ti,ina226",
|
||||
.data = (void *)ina226
|
||||
},
|
||||
{
|
||||
.compatible = "ti,ina230",
|
||||
.data = (void *)ina226
|
||||
},
|
||||
{
|
||||
.compatible = "ti,ina231",
|
||||
.data = (void *)ina226
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ina2xx_of_match);
|
||||
|
||||
static struct i2c_driver ina2xx_driver = {
|
||||
.driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
.of_match_table = ina2xx_of_match,
|
||||
},
|
||||
.probe = ina2xx_probe,
|
||||
.remove = ina2xx_remove,
|
||||
|
@ -38,27 +38,30 @@
|
||||
/*
|
||||
* LPC32XX registers definitions
|
||||
*/
|
||||
#define LPC32XX_ADC_SELECT(x) ((x) + 0x04)
|
||||
#define LPC32XX_ADC_CTRL(x) ((x) + 0x08)
|
||||
#define LPC32XX_ADC_VALUE(x) ((x) + 0x48)
|
||||
#define LPC32XXAD_SELECT(x) ((x) + 0x04)
|
||||
#define LPC32XXAD_CTRL(x) ((x) + 0x08)
|
||||
#define LPC32XXAD_VALUE(x) ((x) + 0x48)
|
||||
|
||||
/* Bit definitions for LPC32XX_ADC_SELECT: */
|
||||
#define AD_REFm 0x00000200 /* constant, always write this value! */
|
||||
#define AD_REFp 0x00000080 /* constant, always write this value! */
|
||||
#define AD_IN 0x00000010 /* multiple of this is the */
|
||||
/* channel number: 0, 1, 2 */
|
||||
#define AD_INTERNAL 0x00000004 /* constant, always write this value! */
|
||||
/* Bit definitions for LPC32XXAD_SELECT: */
|
||||
/* constant, always write this value! */
|
||||
#define LPC32XXAD_REFm 0x00000200
|
||||
/* constant, always write this value! */
|
||||
#define LPC32XXAD_REFp 0x00000080
|
||||
/* multiple of this is the channel number: 0, 1, 2 */
|
||||
#define LPC32XXAD_IN 0x00000010
|
||||
/* constant, always write this value! */
|
||||
#define LPC32XXAD_INTERNAL 0x00000004
|
||||
|
||||
/* Bit definitions for LPC32XX_ADC_CTRL: */
|
||||
#define AD_STROBE 0x00000002
|
||||
#define AD_PDN_CTRL 0x00000004
|
||||
/* Bit definitions for LPC32XXAD_CTRL: */
|
||||
#define LPC32XXAD_STROBE 0x00000002
|
||||
#define LPC32XXAD_PDN_CTRL 0x00000004
|
||||
|
||||
/* Bit definitions for LPC32XX_ADC_VALUE: */
|
||||
#define ADC_VALUE_MASK 0x000003FF
|
||||
/* Bit definitions for LPC32XXAD_VALUE: */
|
||||
#define LPC32XXAD_VALUE_MASK 0x000003FF
|
||||
|
||||
#define MOD_NAME "lpc32xx-adc"
|
||||
#define LPC32XXAD_NAME "lpc32xx-adc"
|
||||
|
||||
struct lpc32xx_adc_info {
|
||||
struct lpc32xx_adc_state {
|
||||
void __iomem *adc_base;
|
||||
struct clk *clk;
|
||||
struct completion completion;
|
||||
@ -72,20 +75,21 @@ static int lpc32xx_read_raw(struct iio_dev *indio_dev,
|
||||
int *val2,
|
||||
long mask)
|
||||
{
|
||||
struct lpc32xx_adc_info *info = iio_priv(indio_dev);
|
||||
struct lpc32xx_adc_state *st = iio_priv(indio_dev);
|
||||
|
||||
if (mask == IIO_CHAN_INFO_RAW) {
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
clk_prepare_enable(info->clk);
|
||||
clk_prepare_enable(st->clk);
|
||||
/* Measurement setup */
|
||||
__raw_writel(AD_INTERNAL | (chan->address) | AD_REFp | AD_REFm,
|
||||
LPC32XX_ADC_SELECT(info->adc_base));
|
||||
__raw_writel(LPC32XXAD_INTERNAL | (chan->address) |
|
||||
LPC32XXAD_REFp | LPC32XXAD_REFm,
|
||||
LPC32XXAD_SELECT(st->adc_base));
|
||||
/* Trigger conversion */
|
||||
__raw_writel(AD_PDN_CTRL | AD_STROBE,
|
||||
LPC32XX_ADC_CTRL(info->adc_base));
|
||||
wait_for_completion(&info->completion); /* set by ISR */
|
||||
clk_disable_unprepare(info->clk);
|
||||
*val = info->value;
|
||||
__raw_writel(LPC32XXAD_PDN_CTRL | LPC32XXAD_STROBE,
|
||||
LPC32XXAD_CTRL(st->adc_base));
|
||||
wait_for_completion(&st->completion); /* set by ISR */
|
||||
clk_disable_unprepare(st->clk);
|
||||
*val = st->value;
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return IIO_VAL_INT;
|
||||
@ -104,7 +108,7 @@ static const struct iio_info lpc32xx_adc_iio_info = {
|
||||
.indexed = 1, \
|
||||
.channel = _index, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.address = AD_IN * _index, \
|
||||
.address = LPC32XXAD_IN * _index, \
|
||||
.scan_index = _index, \
|
||||
}
|
||||
|
||||
@ -116,19 +120,19 @@ static const struct iio_chan_spec lpc32xx_adc_iio_channels[] = {
|
||||
|
||||
static irqreturn_t lpc32xx_adc_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct lpc32xx_adc_info *info = dev_id;
|
||||
struct lpc32xx_adc_state *st = dev_id;
|
||||
|
||||
/* Read value and clear irq */
|
||||
info->value = __raw_readl(LPC32XX_ADC_VALUE(info->adc_base)) &
|
||||
ADC_VALUE_MASK;
|
||||
complete(&info->completion);
|
||||
st->value = __raw_readl(LPC32XXAD_VALUE(st->adc_base)) &
|
||||
LPC32XXAD_VALUE_MASK;
|
||||
complete(&st->completion);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int lpc32xx_adc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct lpc32xx_adc_info *info = NULL;
|
||||
struct lpc32xx_adc_state *st = NULL;
|
||||
struct resource *res;
|
||||
int retval = -ENODEV;
|
||||
struct iio_dev *iodev = NULL;
|
||||
@ -140,23 +144,23 @@ static int lpc32xx_adc_probe(struct platform_device *pdev)
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
iodev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));
|
||||
iodev = devm_iio_device_alloc(&pdev->dev, sizeof(*st));
|
||||
if (!iodev)
|
||||
return -ENOMEM;
|
||||
|
||||
info = iio_priv(iodev);
|
||||
st = iio_priv(iodev);
|
||||
|
||||
info->adc_base = devm_ioremap(&pdev->dev, res->start,
|
||||
resource_size(res));
|
||||
if (!info->adc_base) {
|
||||
st->adc_base = devm_ioremap(&pdev->dev, res->start,
|
||||
resource_size(res));
|
||||
if (!st->adc_base) {
|
||||
dev_err(&pdev->dev, "failed mapping memory\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
info->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(info->clk)) {
|
||||
st->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(st->clk)) {
|
||||
dev_err(&pdev->dev, "failed getting clock\n");
|
||||
return PTR_ERR(info->clk);
|
||||
return PTR_ERR(st->clk);
|
||||
}
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
@ -166,7 +170,7 @@ static int lpc32xx_adc_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
retval = devm_request_irq(&pdev->dev, irq, lpc32xx_adc_isr, 0,
|
||||
MOD_NAME, info);
|
||||
LPC32XXAD_NAME, st);
|
||||
if (retval < 0) {
|
||||
dev_err(&pdev->dev, "failed requesting interrupt\n");
|
||||
return retval;
|
||||
@ -174,9 +178,9 @@ static int lpc32xx_adc_probe(struct platform_device *pdev)
|
||||
|
||||
platform_set_drvdata(pdev, iodev);
|
||||
|
||||
init_completion(&info->completion);
|
||||
init_completion(&st->completion);
|
||||
|
||||
iodev->name = MOD_NAME;
|
||||
iodev->name = LPC32XXAD_NAME;
|
||||
iodev->dev.parent = &pdev->dev;
|
||||
iodev->info = &lpc32xx_adc_iio_info;
|
||||
iodev->modes = INDIO_DIRECT_MODE;
|
||||
@ -203,7 +207,7 @@ MODULE_DEVICE_TABLE(of, lpc32xx_adc_match);
|
||||
static struct platform_driver lpc32xx_adc_driver = {
|
||||
.probe = lpc32xx_adc_probe,
|
||||
.driver = {
|
||||
.name = MOD_NAME,
|
||||
.name = LPC32XXAD_NAME,
|
||||
.of_match_table = of_match_ptr(lpc32xx_adc_match),
|
||||
},
|
||||
};
|
279
drivers/iio/adc/ltc2497.c
Normal file
279
drivers/iio/adc/ltc2497.c
Normal file
@ -0,0 +1,279 @@
|
||||
/*
|
||||
* ltc2497.c - Driver for Analog Devices/Linear Technology LTC2497 ADC
|
||||
*
|
||||
* Copyright (C) 2017 Analog Devices Inc.
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*
|
||||
* Datasheet: http://cds.linear.com/docs/en/datasheet/2497fd.pdf
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#define LTC2497_ENABLE 0xA0
|
||||
#define LTC2497_SGL BIT(4)
|
||||
#define LTC2497_DIFF 0
|
||||
#define LTC2497_SIGN BIT(3)
|
||||
#define LTC2497_CONFIG_DEFAULT LTC2497_ENABLE
|
||||
#define LTC2497_CONVERSION_TIME_MS 150ULL
|
||||
|
||||
struct ltc2497_st {
|
||||
struct i2c_client *client;
|
||||
struct regulator *ref;
|
||||
ktime_t time_prev;
|
||||
u8 addr_prev;
|
||||
/*
|
||||
* DMA (thus cache coherency maintenance) requires the
|
||||
* transfer buffers to live in their own cache lines.
|
||||
*/
|
||||
__be32 buf ____cacheline_aligned;
|
||||
};
|
||||
|
||||
static int ltc2497_wait_conv(struct ltc2497_st *st)
|
||||
{
|
||||
s64 time_elapsed;
|
||||
|
||||
time_elapsed = ktime_ms_delta(ktime_get(), st->time_prev);
|
||||
|
||||
if (time_elapsed < LTC2497_CONVERSION_TIME_MS) {
|
||||
/* delay if conversion time not passed
|
||||
* since last read or write
|
||||
*/
|
||||
if (msleep_interruptible(
|
||||
LTC2497_CONVERSION_TIME_MS - time_elapsed))
|
||||
return -ERESTARTSYS;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (time_elapsed - LTC2497_CONVERSION_TIME_MS <= 0) {
|
||||
/* We're in automatic mode -
|
||||
* so the last reading is stil not outdated
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ltc2497_read(struct ltc2497_st *st, u8 address, int *val)
|
||||
{
|
||||
struct i2c_client *client = st->client;
|
||||
int ret;
|
||||
|
||||
ret = ltc2497_wait_conv(st);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (ret || st->addr_prev != address) {
|
||||
ret = i2c_smbus_write_byte(st->client,
|
||||
LTC2497_ENABLE | address);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
st->addr_prev = address;
|
||||
if (msleep_interruptible(LTC2497_CONVERSION_TIME_MS))
|
||||
return -ERESTARTSYS;
|
||||
}
|
||||
ret = i2c_master_recv(client, (char *)&st->buf, 3);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "i2c_master_recv failed\n");
|
||||
return ret;
|
||||
}
|
||||
st->time_prev = ktime_get();
|
||||
|
||||
/* convert and shift the result,
|
||||
* and finally convert from offset binary to signed integer
|
||||
*/
|
||||
*val = (be32_to_cpu(st->buf) >> 14) - (1 << 17);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ltc2497_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct ltc2497_st *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
ret = ltc2497_read(st, chan->address, val);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
ret = regulator_get_voltage(st->ref);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = ret / 1000;
|
||||
*val2 = 17;
|
||||
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
#define LTC2497_CHAN(_chan, _addr) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = (_chan), \
|
||||
.address = (_addr | (_chan / 2) | ((_chan & 1) ? LTC2497_SIGN : 0)), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
}
|
||||
|
||||
#define LTC2497_CHAN_DIFF(_chan, _addr) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = (_chan) * 2 + ((_addr) & LTC2497_SIGN ? 1 : 0), \
|
||||
.channel2 = (_chan) * 2 + ((_addr) & LTC2497_SIGN ? 0 : 1),\
|
||||
.address = (_addr | _chan), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.differential = 1, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec ltc2497_channel[] = {
|
||||
LTC2497_CHAN(0, LTC2497_SGL),
|
||||
LTC2497_CHAN(1, LTC2497_SGL),
|
||||
LTC2497_CHAN(2, LTC2497_SGL),
|
||||
LTC2497_CHAN(3, LTC2497_SGL),
|
||||
LTC2497_CHAN(4, LTC2497_SGL),
|
||||
LTC2497_CHAN(5, LTC2497_SGL),
|
||||
LTC2497_CHAN(6, LTC2497_SGL),
|
||||
LTC2497_CHAN(7, LTC2497_SGL),
|
||||
LTC2497_CHAN(8, LTC2497_SGL),
|
||||
LTC2497_CHAN(9, LTC2497_SGL),
|
||||
LTC2497_CHAN(10, LTC2497_SGL),
|
||||
LTC2497_CHAN(11, LTC2497_SGL),
|
||||
LTC2497_CHAN(12, LTC2497_SGL),
|
||||
LTC2497_CHAN(13, LTC2497_SGL),
|
||||
LTC2497_CHAN(14, LTC2497_SGL),
|
||||
LTC2497_CHAN(15, LTC2497_SGL),
|
||||
LTC2497_CHAN_DIFF(0, LTC2497_DIFF),
|
||||
LTC2497_CHAN_DIFF(1, LTC2497_DIFF),
|
||||
LTC2497_CHAN_DIFF(2, LTC2497_DIFF),
|
||||
LTC2497_CHAN_DIFF(3, LTC2497_DIFF),
|
||||
LTC2497_CHAN_DIFF(4, LTC2497_DIFF),
|
||||
LTC2497_CHAN_DIFF(5, LTC2497_DIFF),
|
||||
LTC2497_CHAN_DIFF(6, LTC2497_DIFF),
|
||||
LTC2497_CHAN_DIFF(7, LTC2497_DIFF),
|
||||
LTC2497_CHAN_DIFF(0, LTC2497_DIFF | LTC2497_SIGN),
|
||||
LTC2497_CHAN_DIFF(1, LTC2497_DIFF | LTC2497_SIGN),
|
||||
LTC2497_CHAN_DIFF(2, LTC2497_DIFF | LTC2497_SIGN),
|
||||
LTC2497_CHAN_DIFF(3, LTC2497_DIFF | LTC2497_SIGN),
|
||||
LTC2497_CHAN_DIFF(4, LTC2497_DIFF | LTC2497_SIGN),
|
||||
LTC2497_CHAN_DIFF(5, LTC2497_DIFF | LTC2497_SIGN),
|
||||
LTC2497_CHAN_DIFF(6, LTC2497_DIFF | LTC2497_SIGN),
|
||||
LTC2497_CHAN_DIFF(7, LTC2497_DIFF | LTC2497_SIGN),
|
||||
};
|
||||
|
||||
static const struct iio_info ltc2497_info = {
|
||||
.read_raw = ltc2497_read_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int ltc2497_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct ltc2497_st *st;
|
||||
int ret;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C |
|
||||
I2C_FUNC_SMBUS_WRITE_BYTE))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*st));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
st->client = client;
|
||||
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->name = id->name;
|
||||
indio_dev->info = <c2497_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = ltc2497_channel;
|
||||
indio_dev->num_channels = ARRAY_SIZE(ltc2497_channel);
|
||||
|
||||
st->ref = devm_regulator_get(&client->dev, "vref");
|
||||
if (IS_ERR(st->ref))
|
||||
return PTR_ERR(st->ref);
|
||||
|
||||
ret = regulator_enable(st->ref);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = i2c_smbus_write_byte(st->client, LTC2497_CONFIG_DEFAULT);
|
||||
if (ret < 0)
|
||||
goto err_regulator_disable;
|
||||
|
||||
st->addr_prev = LTC2497_CONFIG_DEFAULT;
|
||||
st->time_prev = ktime_get();
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret < 0)
|
||||
goto err_regulator_disable;
|
||||
|
||||
return 0;
|
||||
|
||||
err_regulator_disable:
|
||||
regulator_disable(st->ref);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ltc2497_remove(struct i2c_client *client)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
struct ltc2497_st *st = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
regulator_disable(st->ref);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id ltc2497_id[] = {
|
||||
{ "ltc2497", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ltc2497_id);
|
||||
|
||||
static const struct of_device_id ltc2497_of_match[] = {
|
||||
{ .compatible = "lltc,ltc2497", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ltc2497_of_match);
|
||||
|
||||
static struct i2c_driver ltc2497_driver = {
|
||||
.driver = {
|
||||
.name = "ltc2497",
|
||||
.of_match_table = of_match_ptr(ltc2497_of_match),
|
||||
},
|
||||
.probe = ltc2497_probe,
|
||||
.remove = ltc2497_remove,
|
||||
.id_table = ltc2497_id,
|
||||
};
|
||||
module_i2c_driver(ltc2497_driver);
|
||||
|
||||
MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
|
||||
MODULE_DESCRIPTION("Linear Technology LTC2497 ADC driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -364,7 +364,7 @@ static int max1027_set_trigger_state(struct iio_trigger *trig, bool state)
|
||||
|
||||
static irqreturn_t max1027_trigger_handler(int irq, void *private)
|
||||
{
|
||||
struct iio_poll_func *pf = (struct iio_poll_func *)private;
|
||||
struct iio_poll_func *pf = private;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct max1027_state *st = iio_priv(indio_dev);
|
||||
|
||||
|
@ -124,8 +124,8 @@ static int max11100_probe(struct spi_device *spi)
|
||||
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),
|
||||
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))
|
||||
@ -167,7 +167,6 @@ 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,
|
||||
|
307
drivers/iio/adc/max1118.c
Normal file
307
drivers/iio/adc/max1118.c
Normal file
@ -0,0 +1,307 @@
|
||||
/*
|
||||
* MAX1117/MAX1118/MAX1119 8-bit, dual-channel ADCs driver
|
||||
*
|
||||
* Copyright (c) 2017 Akinobu Mita <akinobu.mita@gmail.com>
|
||||
*
|
||||
* This file is subject to the terms and conditions of version 2 of
|
||||
* the GNU General Public License. See the file COPYING in the main
|
||||
* directory of this archive for more details.
|
||||
*
|
||||
* Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX1117-MAX1119.pdf
|
||||
*
|
||||
* SPI interface connections
|
||||
*
|
||||
* SPI MAXIM
|
||||
* Master Direction MAX1117/8/9
|
||||
* ------ --------- -----------
|
||||
* nCS --> CNVST
|
||||
* SCK --> SCLK
|
||||
* MISO <-- DOUT
|
||||
* ------ --------- -----------
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
enum max1118_id {
|
||||
max1117,
|
||||
max1118,
|
||||
max1119,
|
||||
};
|
||||
|
||||
struct max1118 {
|
||||
struct spi_device *spi;
|
||||
struct mutex lock;
|
||||
struct regulator *reg;
|
||||
|
||||
u8 data ____cacheline_aligned;
|
||||
};
|
||||
|
||||
#define MAX1118_CHANNEL(ch) \
|
||||
{ \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = (ch), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.scan_index = ch, \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = 8, \
|
||||
.storagebits = 8, \
|
||||
}, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec max1118_channels[] = {
|
||||
MAX1118_CHANNEL(0),
|
||||
MAX1118_CHANNEL(1),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(2),
|
||||
};
|
||||
|
||||
static int max1118_read(struct spi_device *spi, int channel)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct max1118 *adc = iio_priv(indio_dev);
|
||||
struct spi_transfer xfers[] = {
|
||||
/*
|
||||
* To select CH1 for conversion, CNVST pin must be brought high
|
||||
* and low for a second time.
|
||||
*/
|
||||
{
|
||||
.len = 0,
|
||||
.delay_usecs = 1, /* > CNVST Low Time 100 ns */
|
||||
.cs_change = 1,
|
||||
},
|
||||
/*
|
||||
* The acquisition interval begins with the falling edge of
|
||||
* CNVST. The total acquisition and conversion process takes
|
||||
* <7.5us.
|
||||
*/
|
||||
{
|
||||
.len = 0,
|
||||
.delay_usecs = 8,
|
||||
},
|
||||
{
|
||||
.rx_buf = &adc->data,
|
||||
.len = 1,
|
||||
},
|
||||
};
|
||||
int ret;
|
||||
|
||||
if (channel == 0)
|
||||
ret = spi_sync_transfer(spi, xfers + 1, 2);
|
||||
else
|
||||
ret = spi_sync_transfer(spi, xfers, 3);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return adc->data;
|
||||
}
|
||||
|
||||
static int max1118_get_vref_mV(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct max1118 *adc = iio_priv(indio_dev);
|
||||
const struct spi_device_id *id = spi_get_device_id(spi);
|
||||
int vref_uV;
|
||||
|
||||
switch (id->driver_data) {
|
||||
case max1117:
|
||||
return 2048;
|
||||
case max1119:
|
||||
return 4096;
|
||||
case max1118:
|
||||
vref_uV = regulator_get_voltage(adc->reg);
|
||||
if (vref_uV < 0)
|
||||
return vref_uV;
|
||||
return vref_uV / 1000;
|
||||
}
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static int max1118_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct max1118 *adc = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
mutex_lock(&adc->lock);
|
||||
*val = max1118_read(adc->spi, chan->channel);
|
||||
mutex_unlock(&adc->lock);
|
||||
if (*val < 0)
|
||||
return *val;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = max1118_get_vref_mV(adc->spi);
|
||||
if (*val < 0)
|
||||
return *val;
|
||||
*val2 = 8;
|
||||
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct iio_info max1118_info = {
|
||||
.read_raw = max1118_read_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static irqreturn_t max1118_trigger_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct max1118 *adc = iio_priv(indio_dev);
|
||||
u8 data[16] = { }; /* 2x 8-bit ADC data + padding + 8 bytes timestamp */
|
||||
int scan_index;
|
||||
int i = 0;
|
||||
|
||||
mutex_lock(&adc->lock);
|
||||
|
||||
for_each_set_bit(scan_index, indio_dev->active_scan_mask,
|
||||
indio_dev->masklength) {
|
||||
const struct iio_chan_spec *scan_chan =
|
||||
&indio_dev->channels[scan_index];
|
||||
int ret = max1118_read(adc->spi, scan_chan->channel);
|
||||
|
||||
if (ret < 0) {
|
||||
dev_warn(&adc->spi->dev,
|
||||
"failed to get conversion data\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
data[i] = ret;
|
||||
i++;
|
||||
}
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, data,
|
||||
iio_get_time_ns(indio_dev));
|
||||
out:
|
||||
mutex_unlock(&adc->lock);
|
||||
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int max1118_probe(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct max1118 *adc;
|
||||
const struct spi_device_id *id = spi_get_device_id(spi);
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
adc = iio_priv(indio_dev);
|
||||
adc->spi = spi;
|
||||
mutex_init(&adc->lock);
|
||||
|
||||
if (id->driver_data == max1118) {
|
||||
adc->reg = devm_regulator_get(&spi->dev, "vref");
|
||||
if (IS_ERR(adc->reg)) {
|
||||
dev_err(&spi->dev, "failed to get vref regulator\n");
|
||||
return PTR_ERR(adc->reg);
|
||||
}
|
||||
ret = regulator_enable(adc->reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
|
||||
indio_dev->name = spi_get_device_id(spi)->name;
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->info = &max1118_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = max1118_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(max1118_channels);
|
||||
|
||||
/*
|
||||
* To reinitiate a conversion on CH0, it is necessary to allow for a
|
||||
* conversion to be complete and all of the data to be read out. Once
|
||||
* a conversion has been completed, the MAX1117/MAX1118/MAX1119 will go
|
||||
* into AutoShutdown mode until the next conversion is initiated.
|
||||
*/
|
||||
max1118_read(spi, 0);
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, NULL,
|
||||
max1118_trigger_handler, NULL);
|
||||
if (ret)
|
||||
goto err_reg_disable;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto err_buffer_cleanup;
|
||||
|
||||
return 0;
|
||||
|
||||
err_buffer_cleanup:
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
err_reg_disable:
|
||||
if (id->driver_data == max1118)
|
||||
regulator_disable(adc->reg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int max1118_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct max1118 *adc = iio_priv(indio_dev);
|
||||
const struct spi_device_id *id = spi_get_device_id(spi);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
if (id->driver_data == max1118)
|
||||
return regulator_disable(adc->reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id max1118_id[] = {
|
||||
{ "max1117", max1117 },
|
||||
{ "max1118", max1118 },
|
||||
{ "max1119", max1119 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, max1118_id);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
|
||||
static const struct of_device_id max1118_dt_ids[] = {
|
||||
{ .compatible = "maxim,max1117" },
|
||||
{ .compatible = "maxim,max1118" },
|
||||
{ .compatible = "maxim,max1119" },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, max1118_dt_ids);
|
||||
|
||||
#endif
|
||||
|
||||
static struct spi_driver max1118_spi_driver = {
|
||||
.driver = {
|
||||
.name = "max1118",
|
||||
.of_match_table = of_match_ptr(max1118_dt_ids),
|
||||
},
|
||||
.probe = max1118_probe,
|
||||
.remove = max1118_remove,
|
||||
.id_table = max1118_id,
|
||||
};
|
||||
module_spi_driver(max1118_spi_driver);
|
||||
|
||||
MODULE_AUTHOR("Akinobu Mita <akinobu.mita@gmail.com>");
|
||||
MODULE_DESCRIPTION("MAXIM MAX1117/MAX1118/MAX1119 ADCs driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -1007,7 +1007,7 @@ static struct attribute *max1363_event_attributes[] = {
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group max1363_event_attribute_group = {
|
||||
static const struct attribute_group max1363_event_attribute_group = {
|
||||
.attrs = max1363_event_attributes,
|
||||
};
|
||||
|
||||
|
585
drivers/iio/adc/max9611.c
Normal file
585
drivers/iio/adc/max9611.c
Normal file
@ -0,0 +1,585 @@
|
||||
/*
|
||||
* iio/adc/max9611.c
|
||||
*
|
||||
* Maxim max9611/max9612 high side current sense amplifier with
|
||||
* 12-bit ADC interface.
|
||||
*
|
||||
* Copyright (C) 2017 Jacopo Mondi
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This driver supports input common-mode voltage, current-sense
|
||||
* amplifier with programmable gains and die temperature reading from
|
||||
* Maxim max9611/max9612.
|
||||
*
|
||||
* Op-amp, analog comparator, and watchdog functionalities are not
|
||||
* supported by this driver.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
|
||||
#define DRIVER_NAME "max9611"
|
||||
|
||||
/* max9611 register addresses */
|
||||
#define MAX9611_REG_CSA_DATA 0x00
|
||||
#define MAX9611_REG_RS_DATA 0x02
|
||||
#define MAX9611_REG_TEMP_DATA 0x08
|
||||
#define MAX9611_REG_CTRL1 0x0a
|
||||
#define MAX9611_REG_CTRL2 0x0b
|
||||
|
||||
/* max9611 REG1 mux configuration options */
|
||||
#define MAX9611_MUX_MASK GENMASK(3, 0)
|
||||
#define MAX9611_MUX_SENSE_1x 0x00
|
||||
#define MAX9611_MUX_SENSE_4x 0x01
|
||||
#define MAX9611_MUX_SENSE_8x 0x02
|
||||
#define MAX9611_INPUT_VOLT 0x03
|
||||
#define MAX9611_MUX_TEMP 0x06
|
||||
|
||||
/* max9611 voltage (both csa and input) helper macros */
|
||||
#define MAX9611_VOLTAGE_SHIFT 0x04
|
||||
#define MAX9611_VOLTAGE_RAW(_r) ((_r) >> MAX9611_VOLTAGE_SHIFT)
|
||||
|
||||
/*
|
||||
* max9611 current sense amplifier voltage output:
|
||||
* LSB and offset values depends on selected gain (1x, 4x, 8x)
|
||||
*
|
||||
* GAIN LSB (nV) OFFSET (LSB steps)
|
||||
* 1x 107500 1
|
||||
* 4x 26880 1
|
||||
* 8x 13440 3
|
||||
*
|
||||
* The complete formula to calculate current sense voltage is:
|
||||
* (((adc_read >> 4) - offset) / ((1 / LSB) * 10^-3)
|
||||
*/
|
||||
#define MAX9611_CSA_1X_LSB_nV 107500
|
||||
#define MAX9611_CSA_4X_LSB_nV 26880
|
||||
#define MAX9611_CSA_8X_LSB_nV 13440
|
||||
|
||||
#define MAX9611_CSA_1X_OFFS_RAW 1
|
||||
#define MAX9611_CSA_4X_OFFS_RAW 1
|
||||
#define MAX9611_CSA_8X_OFFS_RAW 3
|
||||
|
||||
/*
|
||||
* max9611 common input mode (CIM): LSB is 14mV, with 14mV offset at 25 C
|
||||
*
|
||||
* The complete formula to calculate input common voltage is:
|
||||
* (((adc_read >> 4) * 1000) - offset) / (1 / 14 * 1000)
|
||||
*/
|
||||
#define MAX9611_CIM_LSB_mV 14
|
||||
#define MAX9611_CIM_OFFSET_RAW 1
|
||||
|
||||
/*
|
||||
* max9611 temperature reading: LSB is 480 milli degrees Celsius
|
||||
*
|
||||
* The complete formula to calculate temperature is:
|
||||
* ((adc_read >> 7) * 1000) / (1 / 480 * 1000)
|
||||
*/
|
||||
#define MAX9611_TEMP_MAX_POS 0x7f80
|
||||
#define MAX9611_TEMP_MAX_NEG 0xff80
|
||||
#define MAX9611_TEMP_MIN_NEG 0xd980
|
||||
#define MAX9611_TEMP_MASK GENMASK(7, 15)
|
||||
#define MAX9611_TEMP_SHIFT 0x07
|
||||
#define MAX9611_TEMP_RAW(_r) ((_r) >> MAX9611_TEMP_SHIFT)
|
||||
#define MAX9611_TEMP_SCALE_NUM 1000000
|
||||
#define MAX9611_TEMP_SCALE_DIV 2083
|
||||
|
||||
struct max9611_dev {
|
||||
struct device *dev;
|
||||
struct i2c_client *i2c_client;
|
||||
struct mutex lock;
|
||||
unsigned int shunt_resistor_uohm;
|
||||
};
|
||||
|
||||
enum max9611_conf_ids {
|
||||
CONF_SENSE_1x,
|
||||
CONF_SENSE_4x,
|
||||
CONF_SENSE_8x,
|
||||
CONF_IN_VOLT,
|
||||
CONF_TEMP,
|
||||
};
|
||||
|
||||
/**
|
||||
* max9611_mux_conf - associate ADC mux configuration with register address
|
||||
* where data shall be read from
|
||||
*/
|
||||
static const unsigned int max9611_mux_conf[][2] = {
|
||||
/* CONF_SENSE_1x */
|
||||
{ MAX9611_MUX_SENSE_1x, MAX9611_REG_CSA_DATA },
|
||||
/* CONF_SENSE_4x */
|
||||
{ MAX9611_MUX_SENSE_4x, MAX9611_REG_CSA_DATA },
|
||||
/* CONF_SENSE_8x */
|
||||
{ MAX9611_MUX_SENSE_8x, MAX9611_REG_CSA_DATA },
|
||||
/* CONF_IN_VOLT */
|
||||
{ MAX9611_INPUT_VOLT, MAX9611_REG_RS_DATA },
|
||||
/* CONF_TEMP */
|
||||
{ MAX9611_MUX_TEMP, MAX9611_REG_TEMP_DATA },
|
||||
};
|
||||
|
||||
enum max9611_csa_gain {
|
||||
CSA_GAIN_1x,
|
||||
CSA_GAIN_4x,
|
||||
CSA_GAIN_8x,
|
||||
};
|
||||
|
||||
enum max9611_csa_gain_params {
|
||||
CSA_GAIN_LSB_nV,
|
||||
CSA_GAIN_OFFS_RAW,
|
||||
};
|
||||
|
||||
/**
|
||||
* max9611_csa_gain_conf - associate gain multiplier with LSB and
|
||||
* offset values.
|
||||
*
|
||||
* Group together parameters associated with configurable gain
|
||||
* on current sense amplifier path to ADC interface.
|
||||
* Current sense read routine adjusts gain until it gets a meaningful
|
||||
* value; use this structure to retrieve the correct LSB and offset values.
|
||||
*/
|
||||
static const unsigned int max9611_gain_conf[][2] = {
|
||||
{ /* [0] CSA_GAIN_1x */
|
||||
MAX9611_CSA_1X_LSB_nV,
|
||||
MAX9611_CSA_1X_OFFS_RAW,
|
||||
},
|
||||
{ /* [1] CSA_GAIN_4x */
|
||||
MAX9611_CSA_4X_LSB_nV,
|
||||
MAX9611_CSA_4X_OFFS_RAW,
|
||||
},
|
||||
{ /* [2] CSA_GAIN_8x */
|
||||
MAX9611_CSA_8X_LSB_nV,
|
||||
MAX9611_CSA_8X_OFFS_RAW,
|
||||
},
|
||||
};
|
||||
|
||||
enum max9611_chan_addrs {
|
||||
MAX9611_CHAN_VOLTAGE_INPUT,
|
||||
MAX9611_CHAN_VOLTAGE_SENSE,
|
||||
MAX9611_CHAN_TEMPERATURE,
|
||||
MAX9611_CHAN_CURRENT_LOAD,
|
||||
MAX9611_CHAN_POWER_LOAD,
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec max9611_channels[] = {
|
||||
{
|
||||
.type = IIO_TEMP,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
.address = MAX9611_CHAN_TEMPERATURE,
|
||||
},
|
||||
{
|
||||
.type = IIO_VOLTAGE,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
|
||||
.address = MAX9611_CHAN_VOLTAGE_SENSE,
|
||||
.indexed = 1,
|
||||
.channel = 0,
|
||||
},
|
||||
{
|
||||
.type = IIO_VOLTAGE,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_OFFSET),
|
||||
.address = MAX9611_CHAN_VOLTAGE_INPUT,
|
||||
.indexed = 1,
|
||||
.channel = 1,
|
||||
},
|
||||
{
|
||||
.type = IIO_CURRENT,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
|
||||
.address = MAX9611_CHAN_CURRENT_LOAD,
|
||||
},
|
||||
{
|
||||
.type = IIO_POWER,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
|
||||
.address = MAX9611_CHAN_POWER_LOAD
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* max9611_read_single() - read a single value from ADC interface
|
||||
*
|
||||
* Data registers are 16 bit long, spread between two 8 bit registers
|
||||
* with consecutive addresses.
|
||||
* Configure ADC mux first, then read register at address "reg_addr".
|
||||
* The smbus_read_word routine asks for 16 bits and the ADC is kind enough
|
||||
* to return values from "reg_addr" and "reg_addr + 1" consecutively.
|
||||
* Data are transmitted with big-endian ordering: MSB arrives first.
|
||||
*
|
||||
* @max9611: max9611 device
|
||||
* @selector: index for mux and register configuration
|
||||
* @raw_val: the value returned from ADC
|
||||
*/
|
||||
static int max9611_read_single(struct max9611_dev *max9611,
|
||||
enum max9611_conf_ids selector,
|
||||
u16 *raw_val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
u8 mux_conf = max9611_mux_conf[selector][0] & MAX9611_MUX_MASK;
|
||||
u8 reg_addr = max9611_mux_conf[selector][1];
|
||||
|
||||
/*
|
||||
* Keep mutex lock held during read-write to avoid mux register
|
||||
* (CTRL1) re-configuration.
|
||||
*/
|
||||
mutex_lock(&max9611->lock);
|
||||
ret = i2c_smbus_write_byte_data(max9611->i2c_client,
|
||||
MAX9611_REG_CTRL1, mux_conf);
|
||||
if (ret) {
|
||||
dev_err(max9611->dev, "i2c write byte failed: 0x%2x - 0x%2x\n",
|
||||
MAX9611_REG_CTRL1, mux_conf);
|
||||
mutex_unlock(&max9611->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* need a delay here to make register configuration
|
||||
* stabilize. 1 msec at least, from empirical testing.
|
||||
*/
|
||||
usleep_range(1000, 2000);
|
||||
|
||||
ret = i2c_smbus_read_word_swapped(max9611->i2c_client, reg_addr);
|
||||
if (ret < 0) {
|
||||
dev_err(max9611->dev, "i2c read word from 0x%2x failed\n",
|
||||
reg_addr);
|
||||
mutex_unlock(&max9611->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*raw_val = ret;
|
||||
mutex_unlock(&max9611->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* max9611_read_csa_voltage() - read current sense amplifier output voltage
|
||||
*
|
||||
* Current sense amplifier output voltage is read through a configurable
|
||||
* 1x, 4x or 8x gain.
|
||||
* Start with plain 1x gain, and adjust gain control properly until a
|
||||
* meaningful value is read from ADC output.
|
||||
*
|
||||
* @max9611: max9611 device
|
||||
* @adc_raw: raw value read from ADC output
|
||||
* @csa_gain: gain configuration option selector
|
||||
*/
|
||||
static int max9611_read_csa_voltage(struct max9611_dev *max9611,
|
||||
u16 *adc_raw,
|
||||
enum max9611_csa_gain *csa_gain)
|
||||
{
|
||||
enum max9611_conf_ids gain_selectors[] = {
|
||||
CONF_SENSE_1x,
|
||||
CONF_SENSE_4x,
|
||||
CONF_SENSE_8x
|
||||
};
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(gain_selectors); ++i) {
|
||||
ret = max9611_read_single(max9611, gain_selectors[i], adc_raw);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (*adc_raw > 0) {
|
||||
*csa_gain = gain_selectors[i];
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static int max9611_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct max9611_dev *dev = iio_priv(indio_dev);
|
||||
enum max9611_csa_gain gain_selector;
|
||||
const unsigned int *csa_gain;
|
||||
u16 adc_data;
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
|
||||
switch (chan->address) {
|
||||
case MAX9611_CHAN_TEMPERATURE:
|
||||
ret = max9611_read_single(dev, CONF_TEMP,
|
||||
&adc_data);
|
||||
if (ret)
|
||||
return -EINVAL;
|
||||
|
||||
*val = MAX9611_TEMP_RAW(adc_data);
|
||||
return IIO_VAL_INT;
|
||||
|
||||
case MAX9611_CHAN_VOLTAGE_INPUT:
|
||||
ret = max9611_read_single(dev, CONF_IN_VOLT,
|
||||
&adc_data);
|
||||
if (ret)
|
||||
return -EINVAL;
|
||||
|
||||
*val = MAX9611_VOLTAGE_RAW(adc_data);
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
/* MAX9611_CHAN_VOLTAGE_INPUT */
|
||||
*val = MAX9611_CIM_OFFSET_RAW;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
|
||||
switch (chan->address) {
|
||||
case MAX9611_CHAN_TEMPERATURE:
|
||||
*val = MAX9611_TEMP_SCALE_NUM;
|
||||
*val2 = MAX9611_TEMP_SCALE_DIV;
|
||||
|
||||
return IIO_VAL_FRACTIONAL;
|
||||
|
||||
case MAX9611_CHAN_VOLTAGE_INPUT:
|
||||
*val = MAX9611_CIM_LSB_mV;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case IIO_CHAN_INFO_PROCESSED:
|
||||
|
||||
switch (chan->address) {
|
||||
case MAX9611_CHAN_VOLTAGE_SENSE:
|
||||
/*
|
||||
* processed (mV): (raw - offset) * LSB (nV) / 10^6
|
||||
*
|
||||
* Even if max9611 can output raw csa voltage readings,
|
||||
* use a produced value as scale depends on gain.
|
||||
*/
|
||||
ret = max9611_read_csa_voltage(dev, &adc_data,
|
||||
&gain_selector);
|
||||
if (ret)
|
||||
return -EINVAL;
|
||||
|
||||
csa_gain = max9611_gain_conf[gain_selector];
|
||||
|
||||
adc_data -= csa_gain[CSA_GAIN_OFFS_RAW];
|
||||
*val = MAX9611_VOLTAGE_RAW(adc_data) *
|
||||
csa_gain[CSA_GAIN_LSB_nV];
|
||||
*val2 = 1000000;
|
||||
|
||||
return IIO_VAL_FRACTIONAL;
|
||||
|
||||
case MAX9611_CHAN_CURRENT_LOAD:
|
||||
/* processed (mA): Vcsa (nV) / Rshunt (uOhm) */
|
||||
ret = max9611_read_csa_voltage(dev, &adc_data,
|
||||
&gain_selector);
|
||||
if (ret)
|
||||
return -EINVAL;
|
||||
|
||||
csa_gain = max9611_gain_conf[gain_selector];
|
||||
|
||||
adc_data -= csa_gain[CSA_GAIN_OFFS_RAW];
|
||||
*val = MAX9611_VOLTAGE_RAW(adc_data) *
|
||||
csa_gain[CSA_GAIN_LSB_nV];
|
||||
*val2 = dev->shunt_resistor_uohm;
|
||||
|
||||
return IIO_VAL_FRACTIONAL;
|
||||
|
||||
case MAX9611_CHAN_POWER_LOAD:
|
||||
/*
|
||||
* processed (mW): Vin (mV) * Vcsa (uV) /
|
||||
* Rshunt (uOhm)
|
||||
*/
|
||||
ret = max9611_read_single(dev, CONF_IN_VOLT,
|
||||
&adc_data);
|
||||
if (ret)
|
||||
return -EINVAL;
|
||||
|
||||
adc_data -= MAX9611_CIM_OFFSET_RAW;
|
||||
*val = MAX9611_VOLTAGE_RAW(adc_data) *
|
||||
MAX9611_CIM_LSB_mV;
|
||||
|
||||
ret = max9611_read_csa_voltage(dev, &adc_data,
|
||||
&gain_selector);
|
||||
if (ret)
|
||||
return -EINVAL;
|
||||
|
||||
csa_gain = max9611_gain_conf[gain_selector];
|
||||
|
||||
/* divide by 10^3 here to avoid 32bit overflow */
|
||||
adc_data -= csa_gain[CSA_GAIN_OFFS_RAW];
|
||||
*val *= MAX9611_VOLTAGE_RAW(adc_data) *
|
||||
csa_gain[CSA_GAIN_LSB_nV] / 1000;
|
||||
*val2 = dev->shunt_resistor_uohm;
|
||||
|
||||
return IIO_VAL_FRACTIONAL;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static ssize_t max9611_shunt_resistor_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct max9611_dev *max9611 = iio_priv(dev_to_iio_dev(dev));
|
||||
unsigned int i, r;
|
||||
|
||||
i = max9611->shunt_resistor_uohm / 1000;
|
||||
r = max9611->shunt_resistor_uohm % 1000;
|
||||
|
||||
return sprintf(buf, "%u.%03u\n", i, r);
|
||||
}
|
||||
|
||||
static IIO_DEVICE_ATTR(in_power_shunt_resistor, 0444,
|
||||
max9611_shunt_resistor_show, NULL, 0);
|
||||
static IIO_DEVICE_ATTR(in_current_shunt_resistor, 0444,
|
||||
max9611_shunt_resistor_show, NULL, 0);
|
||||
|
||||
static struct attribute *max9611_attributes[] = {
|
||||
&iio_dev_attr_in_power_shunt_resistor.dev_attr.attr,
|
||||
&iio_dev_attr_in_current_shunt_resistor.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group max9611_attribute_group = {
|
||||
.attrs = max9611_attributes,
|
||||
};
|
||||
|
||||
static const struct iio_info indio_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = max9611_read_raw,
|
||||
.attrs = &max9611_attribute_group,
|
||||
};
|
||||
|
||||
static int max9611_init(struct max9611_dev *max9611)
|
||||
{
|
||||
struct i2c_client *client = max9611->i2c_client;
|
||||
u16 regval;
|
||||
int ret;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter,
|
||||
I2C_FUNC_SMBUS_WRITE_BYTE |
|
||||
I2C_FUNC_SMBUS_READ_WORD_DATA)) {
|
||||
dev_err(max9611->dev,
|
||||
"I2c adapter does not support smbus write_byte or read_word functionalities: aborting probe.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Make sure die temperature is in range to test communications. */
|
||||
ret = max9611_read_single(max9611, CONF_TEMP, ®val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
regval = ret & MAX9611_TEMP_MASK;
|
||||
|
||||
if ((regval > MAX9611_TEMP_MAX_POS &&
|
||||
regval < MAX9611_TEMP_MIN_NEG) ||
|
||||
regval > MAX9611_TEMP_MAX_NEG) {
|
||||
dev_err(max9611->dev,
|
||||
"Invalid value received from ADC 0x%4x: aborting\n",
|
||||
regval);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Mux shall be zeroed back before applying other configurations */
|
||||
ret = i2c_smbus_write_byte_data(max9611->i2c_client,
|
||||
MAX9611_REG_CTRL1, 0);
|
||||
if (ret) {
|
||||
dev_err(max9611->dev, "i2c write byte failed: 0x%2x - 0x%2x\n",
|
||||
MAX9611_REG_CTRL1, 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = i2c_smbus_write_byte_data(max9611->i2c_client,
|
||||
MAX9611_REG_CTRL2, 0);
|
||||
if (ret) {
|
||||
dev_err(max9611->dev, "i2c write byte failed: 0x%2x - 0x%2x\n",
|
||||
MAX9611_REG_CTRL2, 0);
|
||||
return ret;
|
||||
}
|
||||
usleep_range(1000, 2000);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id max9611_of_table[] = {
|
||||
{.compatible = "maxim,max9611", .data = "max9611"},
|
||||
{.compatible = "maxim,max9612", .data = "max9612"},
|
||||
{ },
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, max9611_of_table);
|
||||
static int max9611_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
const char * const shunt_res_prop = "shunt-resistor-micro-ohms";
|
||||
const struct device_node *of_node = client->dev.of_node;
|
||||
const struct of_device_id *of_id =
|
||||
of_match_device(max9611_of_table, &client->dev);
|
||||
struct max9611_dev *max9611;
|
||||
struct iio_dev *indio_dev;
|
||||
unsigned int of_shunt;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*max9611));
|
||||
if (IS_ERR(indio_dev))
|
||||
return PTR_ERR(indio_dev);
|
||||
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
|
||||
max9611 = iio_priv(indio_dev);
|
||||
max9611->dev = &client->dev;
|
||||
max9611->i2c_client = client;
|
||||
mutex_init(&max9611->lock);
|
||||
|
||||
ret = of_property_read_u32(of_node, shunt_res_prop, &of_shunt);
|
||||
if (ret) {
|
||||
dev_err(&client->dev,
|
||||
"Missing %s property for %s node\n",
|
||||
shunt_res_prop, of_node->full_name);
|
||||
return ret;
|
||||
}
|
||||
max9611->shunt_resistor_uohm = of_shunt;
|
||||
|
||||
ret = max9611_init(max9611);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->dev.of_node = client->dev.of_node;
|
||||
indio_dev->name = of_id->data;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &indio_info;
|
||||
indio_dev->channels = max9611_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(max9611_channels);
|
||||
|
||||
return devm_iio_device_register(&client->dev, indio_dev);
|
||||
}
|
||||
|
||||
static struct i2c_driver max9611_driver = {
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = max9611_of_table,
|
||||
},
|
||||
.probe = max9611_probe,
|
||||
};
|
||||
module_i2c_driver(max9611_driver);
|
||||
|
||||
MODULE_AUTHOR("Jacopo Mondi <jacopo+renesas@jmondi.org>");
|
||||
MODULE_DESCRIPTION("Maxim max9611/12 current sense amplifier with 12bit ADC");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -18,7 +18,9 @@
|
||||
#include <linux/io.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
@ -163,6 +165,9 @@
|
||||
#define MESON_SAR_ADC_REG13_12BIT_CALIBRATION_MASK GENMASK(13, 8)
|
||||
|
||||
#define MESON_SAR_ADC_MAX_FIFO_SIZE 32
|
||||
#define MESON_SAR_ADC_TIMEOUT 100 /* ms */
|
||||
/* for use with IIO_VAL_INT_PLUS_MICRO */
|
||||
#define MILLION 1000000
|
||||
|
||||
#define MESON_SAR_ADC_CHAN(_chan) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
@ -170,7 +175,9 @@
|
||||
.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), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
|
||||
BIT(IIO_CHAN_INFO_CALIBBIAS) | \
|
||||
BIT(IIO_CHAN_INFO_CALIBSCALE), \
|
||||
.datasheet_name = "SAR_ADC_CH"#_chan, \
|
||||
}
|
||||
|
||||
@ -229,6 +236,9 @@ struct meson_sar_adc_priv {
|
||||
struct clk_gate clk_gate;
|
||||
struct clk *adc_div_clk;
|
||||
struct clk_divider clk_div;
|
||||
struct completion done;
|
||||
int calibbias;
|
||||
int calibscale;
|
||||
};
|
||||
|
||||
static const struct regmap_config meson_sar_adc_regmap_config = {
|
||||
@ -248,6 +258,17 @@ static unsigned int meson_sar_adc_get_fifo_count(struct iio_dev *indio_dev)
|
||||
return FIELD_GET(MESON_SAR_ADC_REG0_FIFO_COUNT_MASK, regval);
|
||||
}
|
||||
|
||||
static int meson_sar_adc_calib_val(struct iio_dev *indio_dev, int val)
|
||||
{
|
||||
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
|
||||
int tmp;
|
||||
|
||||
/* use val_calib = scale * val_raw + offset calibration function */
|
||||
tmp = div_s64((s64)val * priv->calibscale, MILLION) + priv->calibbias;
|
||||
|
||||
return clamp(tmp, 0, (1 << priv->data->resolution) - 1);
|
||||
}
|
||||
|
||||
static int meson_sar_adc_wait_busy_clear(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
|
||||
@ -274,33 +295,31 @@ static int meson_sar_adc_read_raw_sample(struct iio_dev *indio_dev,
|
||||
int *val)
|
||||
{
|
||||
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
|
||||
int ret, regval, fifo_chan, fifo_val, sum = 0, count = 0;
|
||||
int regval, fifo_chan, fifo_val, count;
|
||||
|
||||
ret = meson_sar_adc_wait_busy_clear(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
if(!wait_for_completion_timeout(&priv->done,
|
||||
msecs_to_jiffies(MESON_SAR_ADC_TIMEOUT)))
|
||||
return -ETIMEDOUT;
|
||||
|
||||
while (meson_sar_adc_get_fifo_count(indio_dev) > 0 &&
|
||||
count < MESON_SAR_ADC_MAX_FIFO_SIZE) {
|
||||
regmap_read(priv->regmap, MESON_SAR_ADC_FIFO_RD, ®val);
|
||||
|
||||
fifo_chan = FIELD_GET(MESON_SAR_ADC_FIFO_RD_CHAN_ID_MASK,
|
||||
regval);
|
||||
if (fifo_chan != chan->channel)
|
||||
continue;
|
||||
|
||||
fifo_val = FIELD_GET(MESON_SAR_ADC_FIFO_RD_SAMPLE_VALUE_MASK,
|
||||
regval);
|
||||
fifo_val &= (BIT(priv->data->resolution) - 1);
|
||||
|
||||
sum += fifo_val;
|
||||
count++;
|
||||
count = meson_sar_adc_get_fifo_count(indio_dev);
|
||||
if (count != 1) {
|
||||
dev_err(&indio_dev->dev,
|
||||
"ADC FIFO has %d element(s) instead of one\n", count);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!count)
|
||||
return -ENOENT;
|
||||
regmap_read(priv->regmap, MESON_SAR_ADC_FIFO_RD, ®val);
|
||||
fifo_chan = FIELD_GET(MESON_SAR_ADC_FIFO_RD_CHAN_ID_MASK, regval);
|
||||
if (fifo_chan != chan->channel) {
|
||||
dev_err(&indio_dev->dev,
|
||||
"ADC FIFO entry belongs to channel %d instead of %d\n",
|
||||
fifo_chan, chan->channel);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*val = sum / count;
|
||||
fifo_val = FIELD_GET(MESON_SAR_ADC_FIFO_RD_SAMPLE_VALUE_MASK, regval);
|
||||
fifo_val &= GENMASK(priv->data->resolution - 1, 0);
|
||||
*val = meson_sar_adc_calib_val(indio_dev, fifo_val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -378,6 +397,12 @@ static void meson_sar_adc_start_sample_engine(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
|
||||
|
||||
reinit_completion(&priv->done);
|
||||
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0,
|
||||
MESON_SAR_ADC_REG0_FIFO_IRQ_EN,
|
||||
MESON_SAR_ADC_REG0_FIFO_IRQ_EN);
|
||||
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0,
|
||||
MESON_SAR_ADC_REG0_SAMPLE_ENGINE_ENABLE,
|
||||
MESON_SAR_ADC_REG0_SAMPLE_ENGINE_ENABLE);
|
||||
@ -391,6 +416,9 @@ 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_FIFO_IRQ_EN, 0);
|
||||
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0,
|
||||
MESON_SAR_ADC_REG0_SAMPLING_STOP,
|
||||
MESON_SAR_ADC_REG0_SAMPLING_STOP);
|
||||
@ -516,6 +544,15 @@ static int meson_sar_adc_iio_info_read_raw(struct iio_dev *indio_dev,
|
||||
*val2 = priv->data->resolution;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
*val = priv->calibbias;
|
||||
return IIO_VAL_INT;
|
||||
|
||||
case IIO_CHAN_INFO_CALIBSCALE:
|
||||
*val = priv->calibscale / MILLION;
|
||||
*val2 = priv->calibscale % MILLION;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -643,6 +680,7 @@ static int meson_sar_adc_hw_enable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
|
||||
int ret;
|
||||
u32 regval;
|
||||
|
||||
ret = meson_sar_adc_lock(indio_dev);
|
||||
if (ret)
|
||||
@ -667,6 +705,9 @@ static int meson_sar_adc_hw_enable(struct iio_dev *indio_dev)
|
||||
goto err_sana_clk;
|
||||
}
|
||||
|
||||
regval = FIELD_PREP(MESON_SAR_ADC_REG0_FIFO_CNT_IRQ_MASK, 1);
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0,
|
||||
MESON_SAR_ADC_REG0_FIFO_CNT_IRQ_MASK, regval);
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG11,
|
||||
MESON_SAR_ADC_REG11_BANDGAP_EN,
|
||||
MESON_SAR_ADC_REG11_BANDGAP_EN);
|
||||
@ -728,6 +769,66 @@ static int meson_sar_adc_hw_disable(struct iio_dev *indio_dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t meson_sar_adc_irq(int irq, void *data)
|
||||
{
|
||||
struct iio_dev *indio_dev = data;
|
||||
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
|
||||
unsigned int cnt, threshold;
|
||||
u32 regval;
|
||||
|
||||
regmap_read(priv->regmap, MESON_SAR_ADC_REG0, ®val);
|
||||
cnt = FIELD_GET(MESON_SAR_ADC_REG0_FIFO_COUNT_MASK, regval);
|
||||
threshold = FIELD_GET(MESON_SAR_ADC_REG0_FIFO_CNT_IRQ_MASK, regval);
|
||||
|
||||
if (cnt < threshold)
|
||||
return IRQ_NONE;
|
||||
|
||||
complete(&priv->done);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int meson_sar_adc_calib(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
|
||||
int ret, nominal0, nominal1, value0, value1;
|
||||
|
||||
/* use points 25% and 75% for calibration */
|
||||
nominal0 = (1 << priv->data->resolution) / 4;
|
||||
nominal1 = (1 << priv->data->resolution) * 3 / 4;
|
||||
|
||||
meson_sar_adc_set_chan7_mux(indio_dev, CHAN7_MUX_VDD_DIV4);
|
||||
usleep_range(10, 20);
|
||||
ret = meson_sar_adc_get_sample(indio_dev,
|
||||
&meson_sar_adc_iio_channels[7],
|
||||
MEAN_AVERAGING, EIGHT_SAMPLES, &value0);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
meson_sar_adc_set_chan7_mux(indio_dev, CHAN7_MUX_VDD_MUL3_DIV4);
|
||||
usleep_range(10, 20);
|
||||
ret = meson_sar_adc_get_sample(indio_dev,
|
||||
&meson_sar_adc_iio_channels[7],
|
||||
MEAN_AVERAGING, EIGHT_SAMPLES, &value1);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
if (value1 <= value0) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
priv->calibscale = div_s64((nominal1 - nominal0) * (s64)MILLION,
|
||||
value1 - value0);
|
||||
priv->calibbias = nominal0 - div_s64((s64)value0 * priv->calibscale,
|
||||
MILLION);
|
||||
ret = 0;
|
||||
out:
|
||||
meson_sar_adc_set_chan7_mux(indio_dev, CHAN7_MUX_CH7_INPUT);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct iio_info meson_sar_adc_iio_info = {
|
||||
.read_raw = meson_sar_adc_iio_info_read_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
@ -770,7 +871,7 @@ static int meson_sar_adc_probe(struct platform_device *pdev)
|
||||
struct resource *res;
|
||||
void __iomem *base;
|
||||
const struct of_device_id *match;
|
||||
int ret;
|
||||
int irq, ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*priv));
|
||||
if (!indio_dev) {
|
||||
@ -779,6 +880,7 @@ static int meson_sar_adc_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
priv = iio_priv(indio_dev);
|
||||
init_completion(&priv->done);
|
||||
|
||||
match = of_match_device(meson_sar_adc_of_match, &pdev->dev);
|
||||
priv->data = match->data;
|
||||
@ -797,6 +899,15 @@ static int meson_sar_adc_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
irq = irq_of_parse_and_map(pdev->dev.of_node, 0);
|
||||
if (!irq)
|
||||
return -EINVAL;
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, irq, meson_sar_adc_irq, IRQF_SHARED,
|
||||
dev_name(&pdev->dev), indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
priv->regmap = devm_regmap_init_mmio(&pdev->dev, base,
|
||||
&meson_sar_adc_regmap_config);
|
||||
if (IS_ERR(priv->regmap))
|
||||
@ -857,6 +968,8 @@ static int meson_sar_adc_probe(struct platform_device *pdev)
|
||||
return PTR_ERR(priv->vref);
|
||||
}
|
||||
|
||||
priv->calibscale = MILLION;
|
||||
|
||||
ret = meson_sar_adc_init(indio_dev);
|
||||
if (ret)
|
||||
goto err;
|
||||
@ -865,6 +978,10 @@ static int meson_sar_adc_probe(struct platform_device *pdev)
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
ret = meson_sar_adc_calib(indio_dev);
|
||||
if (ret)
|
||||
dev_warn(&pdev->dev, "calibration failed\n");
|
||||
|
||||
platform_set_drvdata(pdev, indio_dev);
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
|
1036
drivers/iio/adc/qcom-pm8xxx-xoadc.c
Normal file
1036
drivers/iio/adc/qcom-pm8xxx-xoadc.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -28,6 +28,8 @@
|
||||
|
||||
#include <dt-bindings/iio/qcom,spmi-vadc.h>
|
||||
|
||||
#include "qcom-vadc-common.h"
|
||||
|
||||
/* VADC register and bit definitions */
|
||||
#define VADC_REVISION2 0x1
|
||||
#define VADC_REVISION2_SUPPORTED_VADC 1
|
||||
@ -75,83 +77,9 @@
|
||||
|
||||
#define VADC_DATA 0x60 /* 16 bits */
|
||||
|
||||
#define VADC_CONV_TIME_MIN_US 2000
|
||||
#define VADC_CONV_TIME_MAX_US 2100
|
||||
|
||||
/* Min ADC code represents 0V */
|
||||
#define VADC_MIN_ADC_CODE 0x6000
|
||||
/* Max ADC code represents full-scale range of 1.8V */
|
||||
#define VADC_MAX_ADC_CODE 0xa800
|
||||
|
||||
#define VADC_ABSOLUTE_RANGE_UV 625000
|
||||
#define VADC_RATIOMETRIC_RANGE 1800
|
||||
|
||||
#define VADC_DEF_PRESCALING 0 /* 1:1 */
|
||||
#define VADC_DEF_DECIMATION 0 /* 512 */
|
||||
#define VADC_DEF_HW_SETTLE_TIME 0 /* 0 us */
|
||||
#define VADC_DEF_AVG_SAMPLES 0 /* 1 sample */
|
||||
#define VADC_DEF_CALIB_TYPE VADC_CALIB_ABSOLUTE
|
||||
|
||||
#define VADC_DECIMATION_MIN 512
|
||||
#define VADC_DECIMATION_MAX 4096
|
||||
|
||||
#define VADC_HW_SETTLE_DELAY_MAX 10000
|
||||
#define VADC_AVG_SAMPLES_MAX 512
|
||||
|
||||
#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
|
||||
* calibration.
|
||||
*/
|
||||
enum vadc_calibration {
|
||||
VADC_CALIB_ABSOLUTE = 0,
|
||||
VADC_CALIB_RATIOMETRIC
|
||||
};
|
||||
|
||||
/**
|
||||
* struct vadc_linear_graph - Represent ADC characteristics.
|
||||
* @dy: numerator slope to calculate the gain.
|
||||
* @dx: denominator slope to calculate the gain.
|
||||
* @gnd: A/D word of the ground reference used for the channel.
|
||||
*
|
||||
* Each ADC device has different offset and gain parameters which are
|
||||
* computed to calibrate the device.
|
||||
*/
|
||||
struct vadc_linear_graph {
|
||||
s32 dy;
|
||||
s32 dx;
|
||||
s32 gnd;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct vadc_prescale_ratio - Represent scaling ratio for ADC input.
|
||||
* @num: the inverse numerator of the gain applied to the input channel.
|
||||
* @den: the inverse denominator of the gain applied to the input channel.
|
||||
*/
|
||||
struct vadc_prescale_ratio {
|
||||
u32 num;
|
||||
u32 den;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct vadc_channel_prop - VADC channel property.
|
||||
* @channel: channel number, refer to the channel list.
|
||||
@ -162,9 +90,8 @@ 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
|
||||
* @scale_fn_type: 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;
|
||||
@ -173,7 +100,7 @@ struct vadc_channel_prop {
|
||||
unsigned int prescale;
|
||||
unsigned int hw_settle_time;
|
||||
unsigned int avg_samples;
|
||||
unsigned int scale_fn;
|
||||
enum vadc_scale_fn_type scale_fn_type;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -204,35 +131,6 @@ 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},
|
||||
@ -244,44 +142,6 @@ 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);
|
||||
@ -553,159 +413,6 @@ static int vadc_measure_ref_points(struct vadc_priv *vadc)
|
||||
return ret;
|
||||
}
|
||||
|
||||
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 = 0, result = 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 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)
|
||||
{
|
||||
if (!is_power_of_2(value) || value < VADC_DECIMATION_MIN ||
|
||||
value > VADC_DECIMATION_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
return __ffs64(value / VADC_DECIMATION_MIN);
|
||||
}
|
||||
|
||||
static int vadc_prescaling_from_dt(u32 num, u32 den)
|
||||
{
|
||||
unsigned int pre;
|
||||
@ -742,14 +449,6 @@ 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)
|
||||
@ -766,7 +465,13 @@ static int vadc_read_raw(struct iio_dev *indio_dev,
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
scale_fn[prop->scale_fn].scale(vadc, prop, adc_code, val);
|
||||
ret = qcom_vadc_scale(prop->scale_fn_type,
|
||||
&vadc->graph[prop->calibration],
|
||||
&vadc_prescale_ratios[prop->prescale],
|
||||
(prop->calibration == VADC_CALIB_ABSOLUTE),
|
||||
adc_code, val);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
@ -809,7 +514,7 @@ struct vadc_channels {
|
||||
unsigned int prescale_index;
|
||||
enum iio_chan_type type;
|
||||
long info_mask;
|
||||
unsigned int scale_fn;
|
||||
enum vadc_scale_fn_type scale_fn_type;
|
||||
};
|
||||
|
||||
#define VADC_CHAN(_dname, _type, _mask, _pre, _scale) \
|
||||
@ -818,7 +523,7 @@ struct vadc_channels {
|
||||
.prescale_index = _pre, \
|
||||
.type = _type, \
|
||||
.info_mask = _mask, \
|
||||
.scale_fn = _scale \
|
||||
.scale_fn_type = _scale \
|
||||
}, \
|
||||
|
||||
#define VADC_NO_CHAN(_dname, _type, _mask, _pre) \
|
||||
@ -976,7 +681,7 @@ static int vadc_get_dt_channel_data(struct device *dev,
|
||||
|
||||
ret = of_property_read_u32(node, "qcom,decimation", &value);
|
||||
if (!ret) {
|
||||
ret = vadc_decimation_from_dt(value);
|
||||
ret = qcom_vadc_decimation_from_dt(value);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "%02x invalid decimation %d\n",
|
||||
chan, value);
|
||||
@ -1068,7 +773,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;
|
||||
prop.scale_fn_type = vadc_chans[prop.channel].scale_fn_type;
|
||||
vadc->chan_props[index] = prop;
|
||||
|
||||
vadc_chan = &vadc_chans[prop.channel];
|
||||
|
230
drivers/iio/adc/qcom-vadc-common.c
Normal file
230
drivers/iio/adc/qcom-vadc-common.c
Normal file
@ -0,0 +1,230 @@
|
||||
#include <linux/bug.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/math64.h>
|
||||
#include <linux/log2.h>
|
||||
#include <linux/err.h>
|
||||
|
||||
#include "qcom-vadc-common.h"
|
||||
|
||||
/* 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 qcom_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 qcom_vadc_scale_calib(const struct vadc_linear_graph *calib_graph,
|
||||
u16 adc_code,
|
||||
bool absolute,
|
||||
s64 *scale_voltage)
|
||||
{
|
||||
*scale_voltage = (adc_code - calib_graph->gnd);
|
||||
*scale_voltage *= calib_graph->dx;
|
||||
*scale_voltage = div64_s64(*scale_voltage, calib_graph->dy);
|
||||
if (absolute)
|
||||
*scale_voltage += calib_graph->dx;
|
||||
|
||||
if (*scale_voltage < 0)
|
||||
*scale_voltage = 0;
|
||||
}
|
||||
|
||||
static int qcom_vadc_scale_volt(const struct vadc_linear_graph *calib_graph,
|
||||
const struct vadc_prescale_ratio *prescale,
|
||||
bool absolute, u16 adc_code,
|
||||
int *result_uv)
|
||||
{
|
||||
s64 voltage = 0, result = 0;
|
||||
|
||||
qcom_vadc_scale_calib(calib_graph, adc_code, absolute, &voltage);
|
||||
|
||||
voltage = voltage * prescale->den;
|
||||
result = div64_s64(voltage, prescale->num);
|
||||
*result_uv = result;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcom_vadc_scale_therm(const struct vadc_linear_graph *calib_graph,
|
||||
const struct vadc_prescale_ratio *prescale,
|
||||
bool absolute, u16 adc_code,
|
||||
int *result_mdec)
|
||||
{
|
||||
s64 voltage = 0, result = 0;
|
||||
int ret;
|
||||
|
||||
qcom_vadc_scale_calib(calib_graph, adc_code, absolute, &voltage);
|
||||
|
||||
if (absolute)
|
||||
voltage = div64_s64(voltage, 1000);
|
||||
|
||||
ret = qcom_vadc_map_voltage_temp(adcmap_100k_104ef_104fb,
|
||||
ARRAY_SIZE(adcmap_100k_104ef_104fb),
|
||||
voltage, &result);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
result *= 1000;
|
||||
*result_mdec = result;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcom_vadc_scale_die_temp(const struct vadc_linear_graph *calib_graph,
|
||||
const struct vadc_prescale_ratio *prescale,
|
||||
bool absolute,
|
||||
u16 adc_code, int *result_mdec)
|
||||
{
|
||||
s64 voltage = 0;
|
||||
u64 temp; /* Temporary variable for do_div */
|
||||
|
||||
qcom_vadc_scale_calib(calib_graph, adc_code, absolute, &voltage);
|
||||
|
||||
if (voltage > 0) {
|
||||
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 qcom_vadc_scale_chg_temp(const struct vadc_linear_graph *calib_graph,
|
||||
const struct vadc_prescale_ratio *prescale,
|
||||
bool absolute,
|
||||
u16 adc_code, int *result_mdec)
|
||||
{
|
||||
s64 voltage = 0, result = 0;
|
||||
|
||||
qcom_vadc_scale_calib(calib_graph, adc_code, absolute, &voltage);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
int qcom_vadc_scale(enum vadc_scale_fn_type scaletype,
|
||||
const struct vadc_linear_graph *calib_graph,
|
||||
const struct vadc_prescale_ratio *prescale,
|
||||
bool absolute,
|
||||
u16 adc_code, int *result)
|
||||
{
|
||||
switch (scaletype) {
|
||||
case SCALE_DEFAULT:
|
||||
return qcom_vadc_scale_volt(calib_graph, prescale,
|
||||
absolute, adc_code,
|
||||
result);
|
||||
case SCALE_THERM_100K_PULLUP:
|
||||
case SCALE_XOTHERM:
|
||||
return qcom_vadc_scale_therm(calib_graph, prescale,
|
||||
absolute, adc_code,
|
||||
result);
|
||||
case SCALE_PMIC_THERM:
|
||||
return qcom_vadc_scale_die_temp(calib_graph, prescale,
|
||||
absolute, adc_code,
|
||||
result);
|
||||
case SCALE_PMI_CHG_TEMP:
|
||||
return qcom_vadc_scale_chg_temp(calib_graph, prescale,
|
||||
absolute, adc_code,
|
||||
result);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(qcom_vadc_scale);
|
||||
|
||||
int qcom_vadc_decimation_from_dt(u32 value)
|
||||
{
|
||||
if (!is_power_of_2(value) || value < VADC_DECIMATION_MIN ||
|
||||
value > VADC_DECIMATION_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
return __ffs64(value / VADC_DECIMATION_MIN);
|
||||
}
|
||||
EXPORT_SYMBOL(qcom_vadc_decimation_from_dt);
|
108
drivers/iio/adc/qcom-vadc-common.h
Normal file
108
drivers/iio/adc/qcom-vadc-common.h
Normal file
@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Code shared between the different Qualcomm PMIC voltage ADCs
|
||||
*/
|
||||
|
||||
#ifndef QCOM_VADC_COMMON_H
|
||||
#define QCOM_VADC_COMMON_H
|
||||
|
||||
#define VADC_CONV_TIME_MIN_US 2000
|
||||
#define VADC_CONV_TIME_MAX_US 2100
|
||||
|
||||
/* Min ADC code represents 0V */
|
||||
#define VADC_MIN_ADC_CODE 0x6000
|
||||
/* Max ADC code represents full-scale range of 1.8V */
|
||||
#define VADC_MAX_ADC_CODE 0xa800
|
||||
|
||||
#define VADC_ABSOLUTE_RANGE_UV 625000
|
||||
#define VADC_RATIOMETRIC_RANGE 1800
|
||||
|
||||
#define VADC_DEF_PRESCALING 0 /* 1:1 */
|
||||
#define VADC_DEF_DECIMATION 0 /* 512 */
|
||||
#define VADC_DEF_HW_SETTLE_TIME 0 /* 0 us */
|
||||
#define VADC_DEF_AVG_SAMPLES 0 /* 1 sample */
|
||||
#define VADC_DEF_CALIB_TYPE VADC_CALIB_ABSOLUTE
|
||||
|
||||
#define VADC_DECIMATION_MIN 512
|
||||
#define VADC_DECIMATION_MAX 4096
|
||||
|
||||
#define VADC_HW_SETTLE_DELAY_MAX 10000
|
||||
#define VADC_AVG_SAMPLES_MAX 512
|
||||
|
||||
#define KELVINMIL_CELSIUSMIL 273150
|
||||
|
||||
#define PMI_CHG_SCALE_1 -138890
|
||||
#define PMI_CHG_SCALE_2 391750000000LL
|
||||
|
||||
/**
|
||||
* 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
|
||||
* calibration.
|
||||
*/
|
||||
enum vadc_calibration {
|
||||
VADC_CALIB_ABSOLUTE = 0,
|
||||
VADC_CALIB_RATIOMETRIC
|
||||
};
|
||||
|
||||
/**
|
||||
* struct vadc_linear_graph - Represent ADC characteristics.
|
||||
* @dy: numerator slope to calculate the gain.
|
||||
* @dx: denominator slope to calculate the gain.
|
||||
* @gnd: A/D word of the ground reference used for the channel.
|
||||
*
|
||||
* Each ADC device has different offset and gain parameters which are
|
||||
* computed to calibrate the device.
|
||||
*/
|
||||
struct vadc_linear_graph {
|
||||
s32 dy;
|
||||
s32 dx;
|
||||
s32 gnd;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct vadc_prescale_ratio - Represent scaling ratio for ADC input.
|
||||
* @num: the inverse numerator of the gain applied to the input channel.
|
||||
* @den: the inverse denominator of the gain applied to the input channel.
|
||||
*/
|
||||
struct vadc_prescale_ratio {
|
||||
u32 num;
|
||||
u32 den;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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,
|
||||
};
|
||||
|
||||
int qcom_vadc_scale(enum vadc_scale_fn_type scaletype,
|
||||
const struct vadc_linear_graph *calib_graph,
|
||||
const struct vadc_prescale_ratio *prescale,
|
||||
bool absolute,
|
||||
u16 adc_code, int *result_mdec);
|
||||
|
||||
int qcom_vadc_decimation_from_dt(u32 value);
|
||||
|
||||
#endif /* QCOM_VADC_COMMON_H */
|
@ -109,7 +109,7 @@ static int rockchip_saradc_read_raw(struct iio_dev *indio_dev,
|
||||
|
||||
static irqreturn_t rockchip_saradc_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct rockchip_saradc *info = (struct rockchip_saradc *)dev_id;
|
||||
struct rockchip_saradc *info = dev_id;
|
||||
|
||||
/* Read value */
|
||||
info->last_val = readl_relaxed(info->regs + SARADC_DATA);
|
||||
|
@ -60,6 +60,8 @@
|
||||
#define STM32F4_EOC BIT(1)
|
||||
|
||||
/* STM32F4_ADC_CR1 - bit fields */
|
||||
#define STM32F4_RES_SHIFT 24
|
||||
#define STM32F4_RES_MASK GENMASK(25, 24)
|
||||
#define STM32F4_SCAN BIT(8)
|
||||
#define STM32F4_EOCIE BIT(5)
|
||||
|
||||
@ -141,6 +143,7 @@ struct stm32_adc_regs {
|
||||
* @lock: spinlock
|
||||
* @bufi: data buffer index
|
||||
* @num_conv: expected number of scan conversions
|
||||
* @res: data resolution (e.g. RES bitfield value)
|
||||
* @trigger_polarity: external trigger polarity (e.g. exten)
|
||||
* @dma_chan: dma channel
|
||||
* @rx_buf: dma rx buffer cpu address
|
||||
@ -157,6 +160,7 @@ struct stm32_adc {
|
||||
spinlock_t lock; /* interrupt lock */
|
||||
unsigned int bufi;
|
||||
unsigned int num_conv;
|
||||
u32 res;
|
||||
u32 trigger_polarity;
|
||||
struct dma_chan *dma_chan;
|
||||
u8 *rx_buf;
|
||||
@ -196,6 +200,11 @@ static const struct stm32_adc_chan_spec stm32f4_adc123_channels[] = {
|
||||
{ IIO_VOLTAGE, 15, "in15" },
|
||||
};
|
||||
|
||||
static const unsigned int stm32f4_adc_resolutions[] = {
|
||||
/* sorted values so the index matches RES[1:0] in STM32F4_ADC_CR1 */
|
||||
12, 10, 8, 6,
|
||||
};
|
||||
|
||||
/**
|
||||
* stm32f4_sq - describe regular sequence registers
|
||||
* - L: sequence len (register & bit field)
|
||||
@ -302,6 +311,14 @@ static void stm32_adc_conv_irq_disable(struct stm32_adc *adc)
|
||||
stm32_adc_clr_bits(adc, STM32F4_ADC_CR1, STM32F4_EOCIE);
|
||||
}
|
||||
|
||||
static void stm32_adc_set_res(struct stm32_adc *adc)
|
||||
{
|
||||
u32 val = stm32_adc_readl(adc, STM32F4_ADC_CR1);
|
||||
|
||||
val = (val & ~STM32F4_RES_MASK) | (adc->res << STM32F4_RES_SHIFT);
|
||||
stm32_adc_writel(adc, STM32F4_ADC_CR1, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* stm32_adc_start_conv() - Start conversions for regular channels.
|
||||
* @adc: stm32 adc instance
|
||||
@ -870,11 +887,37 @@ static const struct iio_chan_spec_ext_info stm32_adc_ext_info[] = {
|
||||
{},
|
||||
};
|
||||
|
||||
static int stm32_adc_of_get_resolution(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct device_node *node = indio_dev->dev.of_node;
|
||||
struct stm32_adc *adc = iio_priv(indio_dev);
|
||||
unsigned int i;
|
||||
u32 res;
|
||||
|
||||
if (of_property_read_u32(node, "assigned-resolution-bits", &res))
|
||||
res = stm32f4_adc_resolutions[0];
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(stm32f4_adc_resolutions); i++)
|
||||
if (res == stm32f4_adc_resolutions[i])
|
||||
break;
|
||||
if (i >= ARRAY_SIZE(stm32f4_adc_resolutions)) {
|
||||
dev_err(&indio_dev->dev, "Bad resolution: %u bits\n", res);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dev_dbg(&indio_dev->dev, "Using %u bits resolution\n", res);
|
||||
adc->res = i;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void stm32_adc_chan_init_one(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec *chan,
|
||||
const struct stm32_adc_chan_spec *channel,
|
||||
int scan_index)
|
||||
{
|
||||
struct stm32_adc *adc = iio_priv(indio_dev);
|
||||
|
||||
chan->type = channel->type;
|
||||
chan->channel = channel->channel;
|
||||
chan->datasheet_name = channel->name;
|
||||
@ -883,7 +926,7 @@ static void stm32_adc_chan_init_one(struct iio_dev *indio_dev,
|
||||
chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
|
||||
chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE);
|
||||
chan->scan_type.sign = 'u';
|
||||
chan->scan_type.realbits = 12;
|
||||
chan->scan_type.realbits = stm32f4_adc_resolutions[adc->res];
|
||||
chan->scan_type.storagebits = 16;
|
||||
chan->ext_info = stm32_adc_ext_info;
|
||||
}
|
||||
@ -1022,6 +1065,11 @@ static int stm32_adc_probe(struct platform_device *pdev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = stm32_adc_of_get_resolution(indio_dev);
|
||||
if (ret < 0)
|
||||
goto err_clk_disable;
|
||||
stm32_adc_set_res(adc);
|
||||
|
||||
ret = stm32_adc_chan_of_init(indio_dev);
|
||||
if (ret < 0)
|
||||
goto err_clk_disable;
|
||||
|
@ -318,6 +318,7 @@ static int stx104_probe(struct device *dev, unsigned int id)
|
||||
}
|
||||
|
||||
indio_dev->name = dev_name(dev);
|
||||
indio_dev->dev.parent = dev;
|
||||
|
||||
priv = iio_priv(indio_dev);
|
||||
priv->base = base[id];
|
||||
|
719
drivers/iio/adc/sun4i-gpadc-iio.c
Normal file
719
drivers/iio/adc/sun4i-gpadc-iio.c
Normal file
@ -0,0 +1,719 @@
|
||||
/* ADC driver for sunxi platforms' (A10, A13 and A31) GPADC
|
||||
*
|
||||
* Copyright (c) 2016 Quentin Schulz <quentin.schulz@free-electrons.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.
|
||||
*
|
||||
* The Allwinner SoCs all have an ADC that can also act as a touchscreen
|
||||
* controller and a thermal sensor.
|
||||
* The thermal sensor works only when the ADC acts as a touchscreen controller
|
||||
* and is configured to throw an interrupt every fixed periods of time (let say
|
||||
* every X seconds).
|
||||
* One would be tempted to disable the IP on the hardware side rather than
|
||||
* disabling interrupts to save some power but that resets the internal clock of
|
||||
* the IP, resulting in having to wait X seconds every time we want to read the
|
||||
* value of the thermal sensor.
|
||||
* This is also the reason of using autosuspend in pm_runtime. If there was no
|
||||
* autosuspend, the thermal sensor would need X seconds after every
|
||||
* pm_runtime_get_sync to get a value from the ADC. The autosuspend allows the
|
||||
* thermal sensor to be requested again in a certain time span before it gets
|
||||
* shutdown for not being used.
|
||||
*/
|
||||
|
||||
#include <linux/completion.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/thermal.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/driver.h>
|
||||
#include <linux/iio/machine.h>
|
||||
#include <linux/mfd/sun4i-gpadc.h>
|
||||
|
||||
static unsigned int sun4i_gpadc_chan_select(unsigned int chan)
|
||||
{
|
||||
return SUN4I_GPADC_CTRL1_ADC_CHAN_SELECT(chan);
|
||||
}
|
||||
|
||||
static unsigned int sun6i_gpadc_chan_select(unsigned int chan)
|
||||
{
|
||||
return SUN6I_GPADC_CTRL1_ADC_CHAN_SELECT(chan);
|
||||
}
|
||||
|
||||
struct gpadc_data {
|
||||
int temp_offset;
|
||||
int temp_scale;
|
||||
unsigned int tp_mode_en;
|
||||
unsigned int tp_adc_select;
|
||||
unsigned int (*adc_chan_select)(unsigned int chan);
|
||||
unsigned int adc_chan_mask;
|
||||
};
|
||||
|
||||
static const struct gpadc_data sun4i_gpadc_data = {
|
||||
.temp_offset = -1932,
|
||||
.temp_scale = 133,
|
||||
.tp_mode_en = SUN4I_GPADC_CTRL1_TP_MODE_EN,
|
||||
.tp_adc_select = SUN4I_GPADC_CTRL1_TP_ADC_SELECT,
|
||||
.adc_chan_select = &sun4i_gpadc_chan_select,
|
||||
.adc_chan_mask = SUN4I_GPADC_CTRL1_ADC_CHAN_MASK,
|
||||
};
|
||||
|
||||
static const struct gpadc_data sun5i_gpadc_data = {
|
||||
.temp_offset = -1447,
|
||||
.temp_scale = 100,
|
||||
.tp_mode_en = SUN4I_GPADC_CTRL1_TP_MODE_EN,
|
||||
.tp_adc_select = SUN4I_GPADC_CTRL1_TP_ADC_SELECT,
|
||||
.adc_chan_select = &sun4i_gpadc_chan_select,
|
||||
.adc_chan_mask = SUN4I_GPADC_CTRL1_ADC_CHAN_MASK,
|
||||
};
|
||||
|
||||
static const struct gpadc_data sun6i_gpadc_data = {
|
||||
.temp_offset = -1623,
|
||||
.temp_scale = 167,
|
||||
.tp_mode_en = SUN6I_GPADC_CTRL1_TP_MODE_EN,
|
||||
.tp_adc_select = SUN6I_GPADC_CTRL1_TP_ADC_SELECT,
|
||||
.adc_chan_select = &sun6i_gpadc_chan_select,
|
||||
.adc_chan_mask = SUN6I_GPADC_CTRL1_ADC_CHAN_MASK,
|
||||
};
|
||||
|
||||
static const struct gpadc_data sun8i_a33_gpadc_data = {
|
||||
.temp_offset = -1662,
|
||||
.temp_scale = 162,
|
||||
.tp_mode_en = SUN8I_GPADC_CTRL1_CHOP_TEMP_EN,
|
||||
};
|
||||
|
||||
struct sun4i_gpadc_iio {
|
||||
struct iio_dev *indio_dev;
|
||||
struct completion completion;
|
||||
int temp_data;
|
||||
u32 adc_data;
|
||||
struct regmap *regmap;
|
||||
unsigned int fifo_data_irq;
|
||||
atomic_t ignore_fifo_data_irq;
|
||||
unsigned int temp_data_irq;
|
||||
atomic_t ignore_temp_data_irq;
|
||||
const struct gpadc_data *data;
|
||||
bool no_irq;
|
||||
/* prevents concurrent reads of temperature and ADC */
|
||||
struct mutex mutex;
|
||||
};
|
||||
|
||||
#define SUN4I_GPADC_ADC_CHANNEL(_channel, _name) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = _channel, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.datasheet_name = _name, \
|
||||
}
|
||||
|
||||
static struct iio_map sun4i_gpadc_hwmon_maps[] = {
|
||||
{
|
||||
.adc_channel_label = "temp_adc",
|
||||
.consumer_dev_name = "iio_hwmon.0",
|
||||
},
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec sun4i_gpadc_channels[] = {
|
||||
SUN4I_GPADC_ADC_CHANNEL(0, "adc_chan0"),
|
||||
SUN4I_GPADC_ADC_CHANNEL(1, "adc_chan1"),
|
||||
SUN4I_GPADC_ADC_CHANNEL(2, "adc_chan2"),
|
||||
SUN4I_GPADC_ADC_CHANNEL(3, "adc_chan3"),
|
||||
{
|
||||
.type = IIO_TEMP,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_OFFSET),
|
||||
.datasheet_name = "temp_adc",
|
||||
},
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec sun4i_gpadc_channels_no_temp[] = {
|
||||
SUN4I_GPADC_ADC_CHANNEL(0, "adc_chan0"),
|
||||
SUN4I_GPADC_ADC_CHANNEL(1, "adc_chan1"),
|
||||
SUN4I_GPADC_ADC_CHANNEL(2, "adc_chan2"),
|
||||
SUN4I_GPADC_ADC_CHANNEL(3, "adc_chan3"),
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec sun8i_a33_gpadc_channels[] = {
|
||||
{
|
||||
.type = IIO_TEMP,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_OFFSET),
|
||||
.datasheet_name = "temp_adc",
|
||||
},
|
||||
};
|
||||
|
||||
static const struct regmap_config sun4i_gpadc_regmap_config = {
|
||||
.reg_bits = 32,
|
||||
.val_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.fast_io = true,
|
||||
};
|
||||
|
||||
static int sun4i_prepare_for_irq(struct iio_dev *indio_dev, int channel,
|
||||
unsigned int irq)
|
||||
{
|
||||
struct sun4i_gpadc_iio *info = iio_priv(indio_dev);
|
||||
int ret;
|
||||
u32 reg;
|
||||
|
||||
pm_runtime_get_sync(indio_dev->dev.parent);
|
||||
|
||||
reinit_completion(&info->completion);
|
||||
|
||||
ret = regmap_write(info->regmap, SUN4I_GPADC_INT_FIFOC,
|
||||
SUN4I_GPADC_INT_FIFOC_TP_FIFO_TRIG_LEVEL(1) |
|
||||
SUN4I_GPADC_INT_FIFOC_TP_FIFO_FLUSH);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_read(info->regmap, SUN4I_GPADC_CTRL1, ®);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (irq == info->fifo_data_irq) {
|
||||
ret = regmap_write(info->regmap, SUN4I_GPADC_CTRL1,
|
||||
info->data->tp_mode_en |
|
||||
info->data->tp_adc_select |
|
||||
info->data->adc_chan_select(channel));
|
||||
/*
|
||||
* When the IP changes channel, it needs a bit of time to get
|
||||
* correct values.
|
||||
*/
|
||||
if ((reg & info->data->adc_chan_mask) !=
|
||||
info->data->adc_chan_select(channel))
|
||||
mdelay(10);
|
||||
|
||||
} else {
|
||||
/*
|
||||
* The temperature sensor returns valid data only when the ADC
|
||||
* operates in touchscreen mode.
|
||||
*/
|
||||
ret = regmap_write(info->regmap, SUN4I_GPADC_CTRL1,
|
||||
info->data->tp_mode_en);
|
||||
}
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* When the IP changes mode between ADC or touchscreen, it
|
||||
* needs a bit of time to get correct values.
|
||||
*/
|
||||
if ((reg & info->data->tp_adc_select) != info->data->tp_adc_select)
|
||||
mdelay(100);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sun4i_gpadc_read(struct iio_dev *indio_dev, int channel, int *val,
|
||||
unsigned int irq)
|
||||
{
|
||||
struct sun4i_gpadc_iio *info = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&info->mutex);
|
||||
|
||||
ret = sun4i_prepare_for_irq(indio_dev, channel, irq);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
enable_irq(irq);
|
||||
|
||||
/*
|
||||
* The temperature sensor throws an interruption periodically (currently
|
||||
* set at periods of ~0.6s in sun4i_gpadc_runtime_resume). A 1s delay
|
||||
* makes sure an interruption occurs in normal conditions. If it doesn't
|
||||
* occur, then there is a timeout.
|
||||
*/
|
||||
if (!wait_for_completion_timeout(&info->completion,
|
||||
msecs_to_jiffies(1000))) {
|
||||
ret = -ETIMEDOUT;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (irq == info->fifo_data_irq)
|
||||
*val = info->adc_data;
|
||||
else
|
||||
*val = info->temp_data;
|
||||
|
||||
ret = 0;
|
||||
pm_runtime_mark_last_busy(indio_dev->dev.parent);
|
||||
|
||||
err:
|
||||
pm_runtime_put_autosuspend(indio_dev->dev.parent);
|
||||
mutex_unlock(&info->mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sun4i_gpadc_adc_read(struct iio_dev *indio_dev, int channel,
|
||||
int *val)
|
||||
{
|
||||
struct sun4i_gpadc_iio *info = iio_priv(indio_dev);
|
||||
|
||||
return sun4i_gpadc_read(indio_dev, channel, val, info->fifo_data_irq);
|
||||
}
|
||||
|
||||
static int sun4i_gpadc_temp_read(struct iio_dev *indio_dev, int *val)
|
||||
{
|
||||
struct sun4i_gpadc_iio *info = iio_priv(indio_dev);
|
||||
|
||||
if (info->no_irq) {
|
||||
pm_runtime_get_sync(indio_dev->dev.parent);
|
||||
|
||||
regmap_read(info->regmap, SUN4I_GPADC_TEMP_DATA, val);
|
||||
|
||||
pm_runtime_mark_last_busy(indio_dev->dev.parent);
|
||||
pm_runtime_put_autosuspend(indio_dev->dev.parent);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return sun4i_gpadc_read(indio_dev, 0, val, info->temp_data_irq);
|
||||
}
|
||||
|
||||
static int sun4i_gpadc_temp_offset(struct iio_dev *indio_dev, int *val)
|
||||
{
|
||||
struct sun4i_gpadc_iio *info = iio_priv(indio_dev);
|
||||
|
||||
*val = info->data->temp_offset;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sun4i_gpadc_temp_scale(struct iio_dev *indio_dev, int *val)
|
||||
{
|
||||
struct sun4i_gpadc_iio *info = iio_priv(indio_dev);
|
||||
|
||||
*val = info->data->temp_scale;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sun4i_gpadc_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val,
|
||||
int *val2, long mask)
|
||||
{
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
ret = sun4i_gpadc_temp_offset(indio_dev, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (chan->type == IIO_VOLTAGE)
|
||||
ret = sun4i_gpadc_adc_read(indio_dev, chan->channel,
|
||||
val);
|
||||
else
|
||||
ret = sun4i_gpadc_temp_read(indio_dev, val);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
if (chan->type == IIO_VOLTAGE) {
|
||||
/* 3000mV / 4096 * raw */
|
||||
*val = 0;
|
||||
*val2 = 732421875;
|
||||
return IIO_VAL_INT_PLUS_NANO;
|
||||
}
|
||||
|
||||
ret = sun4i_gpadc_temp_scale(indio_dev, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct iio_info sun4i_gpadc_iio_info = {
|
||||
.read_raw = sun4i_gpadc_read_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static irqreturn_t sun4i_gpadc_temp_data_irq_handler(int irq, void *dev_id)
|
||||
{
|
||||
struct sun4i_gpadc_iio *info = dev_id;
|
||||
|
||||
if (atomic_read(&info->ignore_temp_data_irq))
|
||||
goto out;
|
||||
|
||||
if (!regmap_read(info->regmap, SUN4I_GPADC_TEMP_DATA, &info->temp_data))
|
||||
complete(&info->completion);
|
||||
|
||||
out:
|
||||
disable_irq_nosync(info->temp_data_irq);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t sun4i_gpadc_fifo_data_irq_handler(int irq, void *dev_id)
|
||||
{
|
||||
struct sun4i_gpadc_iio *info = dev_id;
|
||||
|
||||
if (atomic_read(&info->ignore_fifo_data_irq))
|
||||
goto out;
|
||||
|
||||
if (!regmap_read(info->regmap, SUN4I_GPADC_DATA, &info->adc_data))
|
||||
complete(&info->completion);
|
||||
|
||||
out:
|
||||
disable_irq_nosync(info->fifo_data_irq);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int sun4i_gpadc_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct sun4i_gpadc_iio *info = iio_priv(dev_get_drvdata(dev));
|
||||
|
||||
/* Disable the ADC on IP */
|
||||
regmap_write(info->regmap, SUN4I_GPADC_CTRL1, 0);
|
||||
/* Disable temperature sensor on IP */
|
||||
regmap_write(info->regmap, SUN4I_GPADC_TPR, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sun4i_gpadc_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct sun4i_gpadc_iio *info = iio_priv(dev_get_drvdata(dev));
|
||||
|
||||
/* clkin = 6MHz */
|
||||
regmap_write(info->regmap, SUN4I_GPADC_CTRL0,
|
||||
SUN4I_GPADC_CTRL0_ADC_CLK_DIVIDER(2) |
|
||||
SUN4I_GPADC_CTRL0_FS_DIV(7) |
|
||||
SUN4I_GPADC_CTRL0_T_ACQ(63));
|
||||
regmap_write(info->regmap, SUN4I_GPADC_CTRL1, info->data->tp_mode_en);
|
||||
regmap_write(info->regmap, SUN4I_GPADC_CTRL3,
|
||||
SUN4I_GPADC_CTRL3_FILTER_EN |
|
||||
SUN4I_GPADC_CTRL3_FILTER_TYPE(1));
|
||||
/* period = SUN4I_GPADC_TPR_TEMP_PERIOD * 256 * 16 / clkin; ~0.6s */
|
||||
regmap_write(info->regmap, SUN4I_GPADC_TPR,
|
||||
SUN4I_GPADC_TPR_TEMP_ENABLE |
|
||||
SUN4I_GPADC_TPR_TEMP_PERIOD(800));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sun4i_gpadc_get_temp(void *data, int *temp)
|
||||
{
|
||||
struct sun4i_gpadc_iio *info = data;
|
||||
int val, scale, offset;
|
||||
|
||||
if (sun4i_gpadc_temp_read(info->indio_dev, &val))
|
||||
return -ETIMEDOUT;
|
||||
|
||||
sun4i_gpadc_temp_scale(info->indio_dev, &scale);
|
||||
sun4i_gpadc_temp_offset(info->indio_dev, &offset);
|
||||
|
||||
*temp = (val + offset) * scale;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct thermal_zone_of_device_ops sun4i_ts_tz_ops = {
|
||||
.get_temp = &sun4i_gpadc_get_temp,
|
||||
};
|
||||
|
||||
static const struct dev_pm_ops sun4i_gpadc_pm_ops = {
|
||||
.runtime_suspend = &sun4i_gpadc_runtime_suspend,
|
||||
.runtime_resume = &sun4i_gpadc_runtime_resume,
|
||||
};
|
||||
|
||||
static int sun4i_irq_init(struct platform_device *pdev, const char *name,
|
||||
irq_handler_t handler, const char *devname,
|
||||
unsigned int *irq, atomic_t *atomic)
|
||||
{
|
||||
int ret;
|
||||
struct sun4i_gpadc_dev *mfd_dev = dev_get_drvdata(pdev->dev.parent);
|
||||
struct sun4i_gpadc_iio *info = iio_priv(dev_get_drvdata(&pdev->dev));
|
||||
|
||||
/*
|
||||
* Once the interrupt is activated, the IP continuously performs
|
||||
* conversions thus throws interrupts. The interrupt is activated right
|
||||
* after being requested but we want to control when these interrupts
|
||||
* occur thus we disable it right after being requested. However, an
|
||||
* interrupt might occur between these two instructions and we have to
|
||||
* make sure that does not happen, by using atomic flags. We set the
|
||||
* flag before requesting the interrupt and unset it right after
|
||||
* disabling the interrupt. When an interrupt occurs between these two
|
||||
* instructions, reading the atomic flag will tell us to ignore the
|
||||
* interrupt.
|
||||
*/
|
||||
atomic_set(atomic, 1);
|
||||
|
||||
ret = platform_get_irq_byname(pdev, name);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "no %s interrupt registered\n", name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regmap_irq_get_virq(mfd_dev->regmap_irqc, ret);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed to get virq for irq %s\n", name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*irq = ret;
|
||||
ret = devm_request_any_context_irq(&pdev->dev, *irq, handler, 0,
|
||||
devname, info);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "could not request %s interrupt: %d\n",
|
||||
name, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
disable_irq(*irq);
|
||||
atomic_set(atomic, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id sun4i_gpadc_of_id[] = {
|
||||
{
|
||||
.compatible = "allwinner,sun8i-a33-ths",
|
||||
.data = &sun8i_a33_gpadc_data,
|
||||
},
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
static int sun4i_gpadc_probe_dt(struct platform_device *pdev,
|
||||
struct iio_dev *indio_dev)
|
||||
{
|
||||
struct sun4i_gpadc_iio *info = iio_priv(indio_dev);
|
||||
const struct of_device_id *of_dev;
|
||||
struct thermal_zone_device *tzd;
|
||||
struct resource *mem;
|
||||
void __iomem *base;
|
||||
int ret;
|
||||
|
||||
of_dev = of_match_device(sun4i_gpadc_of_id, &pdev->dev);
|
||||
if (!of_dev)
|
||||
return -ENODEV;
|
||||
|
||||
info->no_irq = true;
|
||||
info->data = (struct gpadc_data *)of_dev->data;
|
||||
indio_dev->num_channels = ARRAY_SIZE(sun8i_a33_gpadc_channels);
|
||||
indio_dev->channels = sun8i_a33_gpadc_channels;
|
||||
|
||||
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
base = devm_ioremap_resource(&pdev->dev, mem);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
info->regmap = devm_regmap_init_mmio(&pdev->dev, base,
|
||||
&sun4i_gpadc_regmap_config);
|
||||
if (IS_ERR(info->regmap)) {
|
||||
ret = PTR_ERR(info->regmap);
|
||||
dev_err(&pdev->dev, "failed to init regmap: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!IS_ENABLED(CONFIG_THERMAL_OF))
|
||||
return 0;
|
||||
|
||||
tzd = devm_thermal_zone_of_sensor_register(&pdev->dev, 0, info,
|
||||
&sun4i_ts_tz_ops);
|
||||
if (IS_ERR(tzd))
|
||||
dev_err(&pdev->dev, "could not register thermal sensor: %ld\n",
|
||||
PTR_ERR(tzd));
|
||||
|
||||
return PTR_ERR_OR_ZERO(tzd);
|
||||
}
|
||||
|
||||
static int sun4i_gpadc_probe_mfd(struct platform_device *pdev,
|
||||
struct iio_dev *indio_dev)
|
||||
{
|
||||
struct sun4i_gpadc_iio *info = iio_priv(indio_dev);
|
||||
struct sun4i_gpadc_dev *sun4i_gpadc_dev =
|
||||
dev_get_drvdata(pdev->dev.parent);
|
||||
int ret;
|
||||
|
||||
info->no_irq = false;
|
||||
info->regmap = sun4i_gpadc_dev->regmap;
|
||||
|
||||
indio_dev->num_channels = ARRAY_SIZE(sun4i_gpadc_channels);
|
||||
indio_dev->channels = sun4i_gpadc_channels;
|
||||
|
||||
info->data = (struct gpadc_data *)platform_get_device_id(pdev)->driver_data;
|
||||
|
||||
/*
|
||||
* Since the controller needs to be in touchscreen mode for its thermal
|
||||
* sensor to operate properly, and that switching between the two modes
|
||||
* needs a delay, always registering in the thermal framework will
|
||||
* significantly slow down the conversion rate of the ADCs.
|
||||
*
|
||||
* Therefore, instead of depending on THERMAL_OF in Kconfig, we only
|
||||
* register the sensor if that option is enabled, eventually leaving
|
||||
* that choice to the user.
|
||||
*/
|
||||
|
||||
if (IS_ENABLED(CONFIG_THERMAL_OF)) {
|
||||
/*
|
||||
* This driver is a child of an MFD which has a node in the DT
|
||||
* but not its children, because of DT backward compatibility
|
||||
* for A10, A13 and A31 SoCs. Therefore, the resulting devices
|
||||
* of this driver do not have an of_node variable.
|
||||
* However, its parent (the MFD driver) has an of_node variable
|
||||
* and since devm_thermal_zone_of_sensor_register uses its first
|
||||
* argument to match the phandle defined in the node of the
|
||||
* thermal driver with the of_node of the device passed as first
|
||||
* argument and the third argument to call ops from
|
||||
* thermal_zone_of_device_ops, the solution is to use the parent
|
||||
* device as first argument to match the phandle with its
|
||||
* of_node, and the device from this driver as third argument to
|
||||
* return the temperature.
|
||||
*/
|
||||
struct thermal_zone_device *tzd;
|
||||
tzd = devm_thermal_zone_of_sensor_register(pdev->dev.parent, 0,
|
||||
info,
|
||||
&sun4i_ts_tz_ops);
|
||||
if (IS_ERR(tzd)) {
|
||||
dev_err(&pdev->dev,
|
||||
"could not register thermal sensor: %ld\n",
|
||||
PTR_ERR(tzd));
|
||||
return PTR_ERR(tzd);
|
||||
}
|
||||
} else {
|
||||
indio_dev->num_channels =
|
||||
ARRAY_SIZE(sun4i_gpadc_channels_no_temp);
|
||||
indio_dev->channels = sun4i_gpadc_channels_no_temp;
|
||||
}
|
||||
|
||||
if (IS_ENABLED(CONFIG_THERMAL_OF)) {
|
||||
ret = sun4i_irq_init(pdev, "TEMP_DATA_PENDING",
|
||||
sun4i_gpadc_temp_data_irq_handler,
|
||||
"temp_data", &info->temp_data_irq,
|
||||
&info->ignore_temp_data_irq);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = sun4i_irq_init(pdev, "FIFO_DATA_PENDING",
|
||||
sun4i_gpadc_fifo_data_irq_handler, "fifo_data",
|
||||
&info->fifo_data_irq, &info->ignore_fifo_data_irq);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (IS_ENABLED(CONFIG_THERMAL_OF)) {
|
||||
ret = iio_map_array_register(indio_dev, sun4i_gpadc_hwmon_maps);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev,
|
||||
"failed to register iio map array\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sun4i_gpadc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct sun4i_gpadc_iio *info;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
info = iio_priv(indio_dev);
|
||||
platform_set_drvdata(pdev, indio_dev);
|
||||
|
||||
mutex_init(&info->mutex);
|
||||
info->indio_dev = indio_dev;
|
||||
init_completion(&info->completion);
|
||||
indio_dev->name = dev_name(&pdev->dev);
|
||||
indio_dev->dev.parent = &pdev->dev;
|
||||
indio_dev->dev.of_node = pdev->dev.of_node;
|
||||
indio_dev->info = &sun4i_gpadc_iio_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
if (pdev->dev.of_node)
|
||||
ret = sun4i_gpadc_probe_dt(pdev, indio_dev);
|
||||
else
|
||||
ret = sun4i_gpadc_probe_mfd(pdev, indio_dev);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pm_runtime_set_autosuspend_delay(&pdev->dev,
|
||||
SUN4I_GPADC_AUTOSUSPEND_DELAY);
|
||||
pm_runtime_use_autosuspend(&pdev->dev);
|
||||
pm_runtime_set_suspended(&pdev->dev);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
||||
ret = devm_iio_device_register(&pdev->dev, indio_dev);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "could not register the device\n");
|
||||
goto err_map;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_map:
|
||||
if (!info->no_irq && IS_ENABLED(CONFIG_THERMAL_OF))
|
||||
iio_map_array_unregister(indio_dev);
|
||||
|
||||
pm_runtime_put(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sun4i_gpadc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
struct sun4i_gpadc_iio *info = iio_priv(indio_dev);
|
||||
|
||||
pm_runtime_put(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
if (!info->no_irq && IS_ENABLED(CONFIG_THERMAL_OF))
|
||||
iio_map_array_unregister(indio_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct platform_device_id sun4i_gpadc_id[] = {
|
||||
{ "sun4i-a10-gpadc-iio", (kernel_ulong_t)&sun4i_gpadc_data },
|
||||
{ "sun5i-a13-gpadc-iio", (kernel_ulong_t)&sun5i_gpadc_data },
|
||||
{ "sun6i-a31-gpadc-iio", (kernel_ulong_t)&sun6i_gpadc_data },
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
|
||||
static struct platform_driver sun4i_gpadc_driver = {
|
||||
.driver = {
|
||||
.name = "sun4i-gpadc-iio",
|
||||
.of_match_table = sun4i_gpadc_of_id,
|
||||
.pm = &sun4i_gpadc_pm_ops,
|
||||
},
|
||||
.id_table = sun4i_gpadc_id,
|
||||
.probe = sun4i_gpadc_probe,
|
||||
.remove = sun4i_gpadc_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(sun4i_gpadc_driver);
|
||||
|
||||
MODULE_DESCRIPTION("ADC driver for sunxi platforms");
|
||||
MODULE_AUTHOR("Quentin Schulz <quentin.schulz@free-electrons.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -15,6 +15,7 @@
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/regmap.h>
|
||||
@ -55,7 +56,7 @@
|
||||
#define ADS1015_DEFAULT_DATA_RATE 4
|
||||
#define ADS1015_DEFAULT_CHAN 0
|
||||
|
||||
enum {
|
||||
enum chip_ids {
|
||||
ADS1015,
|
||||
ADS1115,
|
||||
};
|
||||
@ -578,6 +579,7 @@ static int ads1015_probe(struct i2c_client *client,
|
||||
struct iio_dev *indio_dev;
|
||||
struct ads1015_data *data;
|
||||
int ret;
|
||||
enum chip_ids chip;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
@ -593,7 +595,11 @@ static int ads1015_probe(struct i2c_client *client,
|
||||
indio_dev->name = ADS1015_DRV_NAME;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
switch (id->driver_data) {
|
||||
if (client->dev.of_node)
|
||||
chip = (enum chip_ids)of_device_get_match_data(&client->dev);
|
||||
else
|
||||
chip = id->driver_data;
|
||||
switch (chip) {
|
||||
case ADS1015:
|
||||
indio_dev->channels = ads1015_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(ads1015_channels);
|
||||
@ -698,9 +704,23 @@ static const struct i2c_device_id ads1015_id[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ads1015_id);
|
||||
|
||||
static const struct of_device_id ads1015_of_match[] = {
|
||||
{
|
||||
.compatible = "ti,ads1015",
|
||||
.data = (void *)ADS1015
|
||||
},
|
||||
{
|
||||
.compatible = "ti,ads1115",
|
||||
.data = (void *)ADS1115
|
||||
},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ads1015_of_match);
|
||||
|
||||
static struct i2c_driver ads1015_driver = {
|
||||
.driver = {
|
||||
.name = ADS1015_DRV_NAME,
|
||||
.of_match_table = ads1015_of_match,
|
||||
.pm = &ads1015_pm_ops,
|
||||
},
|
||||
.probe = ads1015_probe,
|
||||
|
@ -584,7 +584,7 @@ static int vf610_adc_read_data(struct vf610_adc *info)
|
||||
|
||||
static irqreturn_t vf610_adc_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct iio_dev *indio_dev = (struct iio_dev *)dev_id;
|
||||
struct iio_dev *indio_dev = dev_id;
|
||||
struct vf610_adc *info = iio_priv(indio_dev);
|
||||
int coco;
|
||||
|
||||
|
@ -163,7 +163,7 @@ static int ams_iaqcore_probe(struct i2c_client *client,
|
||||
mutex_init(&data->lock);
|
||||
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->info = &ams_iaqcore_info,
|
||||
indio_dev->info = &ams_iaqcore_info;
|
||||
indio_dev->name = dev_name(&client->dev);
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
|
@ -393,7 +393,7 @@ static int vz89x_probe(struct i2c_client *client,
|
||||
mutex_init(&data->lock);
|
||||
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->info = &vz89x_info,
|
||||
indio_dev->info = &vz89x_info;
|
||||
indio_dev->name = dev_name(&client->dev);
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
|
@ -267,31 +267,12 @@ static int cros_ec_sensors_probe(struct platform_device *pdev)
|
||||
else
|
||||
state->core.read_ec_sensors_data = cros_ec_sensors_read_cmd;
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, NULL,
|
||||
cros_ec_sensors_capture, NULL);
|
||||
ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
|
||||
cros_ec_sensors_capture, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto error_uninit_buffer;
|
||||
|
||||
return 0;
|
||||
|
||||
error_uninit_buffer:
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cros_ec_sensors_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
|
||||
return 0;
|
||||
return devm_iio_device_register(dev, indio_dev);
|
||||
}
|
||||
|
||||
static const struct platform_device_id cros_ec_sensors_ids[] = {
|
||||
@ -313,7 +294,6 @@ static struct platform_driver cros_ec_sensors_platform_driver = {
|
||||
.name = "cros-ec-sensors",
|
||||
},
|
||||
.probe = cros_ec_sensors_probe,
|
||||
.remove = cros_ec_sensors_remove,
|
||||
.id_table = cros_ec_sensors_ids,
|
||||
};
|
||||
module_platform_driver(cros_ec_sensors_platform_driver);
|
||||
|
@ -38,6 +38,12 @@ static struct {
|
||||
{HID_USAGE_SENSOR_ACCEL_3D,
|
||||
HID_USAGE_SENSOR_UNITS_G, 9, 806650000},
|
||||
|
||||
{HID_USAGE_SENSOR_GRAVITY_VECTOR, 0, 9, 806650000},
|
||||
{HID_USAGE_SENSOR_GRAVITY_VECTOR,
|
||||
HID_USAGE_SENSOR_UNITS_METERS_PER_SEC_SQRD, 1, 0},
|
||||
{HID_USAGE_SENSOR_GRAVITY_VECTOR,
|
||||
HID_USAGE_SENSOR_UNITS_G, 9, 806650000},
|
||||
|
||||
{HID_USAGE_SENSOR_GYRO_3D, 0, 0, 17453293},
|
||||
{HID_USAGE_SENSOR_GYRO_3D,
|
||||
HID_USAGE_SENSOR_UNITS_RADIANS_PER_SECOND, 1, 0},
|
||||
@ -62,6 +68,11 @@ static struct {
|
||||
{HID_USAGE_SENSOR_TIME_TIMESTAMP, 0, 1000000000, 0},
|
||||
{HID_USAGE_SENSOR_TIME_TIMESTAMP, HID_USAGE_SENSOR_UNITS_MILLISECOND,
|
||||
1000000, 0},
|
||||
|
||||
{HID_USAGE_SENSOR_TEMPERATURE, 0, 1000, 0},
|
||||
{HID_USAGE_SENSOR_TEMPERATURE, HID_USAGE_SENSOR_UNITS_DEGREES, 1000, 0},
|
||||
|
||||
{HID_USAGE_SENSOR_HUMIDITY, 0, 1000, 0},
|
||||
};
|
||||
|
||||
static int pow_10(unsigned power)
|
||||
@ -221,7 +232,15 @@ int hid_sensor_write_samp_freq_value(struct hid_sensor_common *st,
|
||||
if (ret < 0 || value < 0)
|
||||
ret = -EINVAL;
|
||||
|
||||
return ret;
|
||||
ret = sensor_hub_get_feature(st->hsdev,
|
||||
st->poll.report_id,
|
||||
st->poll.index, sizeof(value), &value);
|
||||
if (ret < 0 || value < 0)
|
||||
return -EINVAL;
|
||||
|
||||
st->poll_interval = value;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(hid_sensor_write_samp_freq_value);
|
||||
|
||||
@ -266,7 +285,16 @@ int hid_sensor_write_raw_hyst_value(struct hid_sensor_common *st,
|
||||
if (ret < 0 || value < 0)
|
||||
ret = -EINVAL;
|
||||
|
||||
return ret;
|
||||
ret = sensor_hub_get_feature(st->hsdev,
|
||||
st->sensitivity.report_id,
|
||||
st->sensitivity.index, sizeof(value),
|
||||
&value);
|
||||
if (ret < 0 || value < 0)
|
||||
return -EINVAL;
|
||||
|
||||
st->raw_hystersis = value;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(hid_sensor_write_raw_hyst_value);
|
||||
|
||||
@ -369,6 +397,9 @@ int hid_sensor_get_reporting_interval(struct hid_sensor_hub_device *hsdev,
|
||||
/* Default unit of measure is milliseconds */
|
||||
if (st->poll.units == 0)
|
||||
st->poll.units = HID_USAGE_SENSOR_UNITS_MILLISECOND;
|
||||
|
||||
st->poll_interval = -1;
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
@ -399,6 +430,8 @@ int hid_sensor_parse_common_attributes(struct hid_sensor_hub_device *hsdev,
|
||||
HID_USAGE_SENSOR_PROP_SENSITIVITY_ABS,
|
||||
&st->sensitivity);
|
||||
|
||||
st->raw_hystersis = -1;
|
||||
|
||||
sensor_hub_input_get_attribute_info(hsdev,
|
||||
HID_INPUT_REPORT, usage_id,
|
||||
HID_USAGE_SENSOR_TIME_TIMESTAMP,
|
||||
|
@ -51,6 +51,8 @@ static int _hid_sensor_power_state(struct hid_sensor_common *st, bool state)
|
||||
st->report_state.report_id,
|
||||
st->report_state.index,
|
||||
HID_USAGE_SENSOR_PROP_REPORTING_STATE_ALL_EVENTS_ENUM);
|
||||
|
||||
poll_value = hid_sensor_read_poll_value(st);
|
||||
} else {
|
||||
int val;
|
||||
|
||||
@ -87,9 +89,7 @@ static int _hid_sensor_power_state(struct hid_sensor_common *st, bool state)
|
||||
sensor_hub_get_feature(st->hsdev, st->power_state.report_id,
|
||||
st->power_state.index,
|
||||
sizeof(state_val), &state_val);
|
||||
if (state)
|
||||
poll_value = hid_sensor_read_poll_value(st);
|
||||
if (poll_value > 0)
|
||||
if (state && poll_value)
|
||||
msleep_interruptible(poll_value * 2);
|
||||
|
||||
return 0;
|
||||
@ -127,6 +127,20 @@ static void hid_sensor_set_power_work(struct work_struct *work)
|
||||
struct hid_sensor_common *attrb = container_of(work,
|
||||
struct hid_sensor_common,
|
||||
work);
|
||||
|
||||
if (attrb->poll_interval >= 0)
|
||||
sensor_hub_set_feature(attrb->hsdev, attrb->poll.report_id,
|
||||
attrb->poll.index,
|
||||
sizeof(attrb->poll_interval),
|
||||
&attrb->poll_interval);
|
||||
|
||||
if (attrb->raw_hystersis >= 0)
|
||||
sensor_hub_set_feature(attrb->hsdev,
|
||||
attrb->sensitivity.report_id,
|
||||
attrb->sensitivity.index,
|
||||
sizeof(attrb->raw_hystersis),
|
||||
&attrb->raw_hystersis);
|
||||
|
||||
_hid_sensor_power_state(attrb, true);
|
||||
}
|
||||
|
||||
@ -138,6 +152,10 @@ static int hid_sensor_data_rdy_trigger_set_state(struct iio_trigger *trig,
|
||||
|
||||
void hid_sensor_remove_trigger(struct hid_sensor_common *attrb)
|
||||
{
|
||||
pm_runtime_disable(&attrb->pdev->dev);
|
||||
pm_runtime_set_suspended(&attrb->pdev->dev);
|
||||
pm_runtime_put_noidle(&attrb->pdev->dev);
|
||||
|
||||
cancel_work_sync(&attrb->work);
|
||||
iio_trigger_unregister(attrb->trigger);
|
||||
iio_trigger_free(attrb->trigger);
|
||||
|
@ -74,7 +74,7 @@ EXPORT_SYMBOL(ms_sensors_reset);
|
||||
int ms_sensors_read_prom_word(void *cli, int cmd, u16 *word)
|
||||
{
|
||||
int ret;
|
||||
struct i2c_client *client = (struct i2c_client *)cli;
|
||||
struct i2c_client *client = cli;
|
||||
|
||||
ret = i2c_smbus_read_word_swapped(client, cmd);
|
||||
if (ret < 0) {
|
||||
@ -107,7 +107,7 @@ int ms_sensors_convert_and_read(void *cli, u8 conv, u8 rd,
|
||||
{
|
||||
int ret;
|
||||
__be32 buf = 0;
|
||||
struct i2c_client *client = (struct i2c_client *)cli;
|
||||
struct i2c_client *client = cli;
|
||||
|
||||
/* Trigger conversion */
|
||||
ret = i2c_smbus_write_byte(client, conv);
|
||||
|
@ -551,6 +551,7 @@ static int quad8_probe(struct device *dev, unsigned int id)
|
||||
indio_dev->num_channels = ARRAY_SIZE(quad8_channels);
|
||||
indio_dev->channels = quad8_channels;
|
||||
indio_dev->name = dev_name(dev);
|
||||
indio_dev->dev.parent = dev;
|
||||
|
||||
priv = iio_priv(indio_dev);
|
||||
priv->base = base[id];
|
||||
|
@ -7,7 +7,7 @@ menu "Counters"
|
||||
|
||||
config 104_QUAD_8
|
||||
tristate "ACCES 104-QUAD-8 driver"
|
||||
depends on X86 && ISA_BUS_API
|
||||
depends on PC104 && X86 && ISA_BUS_API
|
||||
help
|
||||
Say yes here to build support for the ACCES 104-QUAD-8 quadrature
|
||||
encoder counter/interface device family (104-QUAD-8, 104-QUAD-4).
|
||||
|
@ -118,6 +118,16 @@ config AD5624R_SPI
|
||||
Say yes here to build support for Analog Devices AD5624R, AD5644R and
|
||||
AD5664R converters (DAC). This driver uses the common SPI interface.
|
||||
|
||||
config LTC2632
|
||||
tristate "Linear Technology LTC2632-12/10/8 DAC spi driver"
|
||||
depends on SPI
|
||||
help
|
||||
Say yes here to build support for Linear Technology
|
||||
LTC2632-12, LTC2632-10, LTC2632-8 converters (DAC).
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ltc2632.
|
||||
|
||||
config AD5686
|
||||
tristate "Analog Devices AD5686R/AD5685R/AD5684R DAC SPI driver"
|
||||
depends on SPI
|
||||
@ -274,6 +284,21 @@ config MCP4922
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called mcp4922.
|
||||
|
||||
config STM32_DAC
|
||||
tristate "STMicroelectronics STM32 DAC"
|
||||
depends on (ARCH_STM32 && OF) || COMPILE_TEST
|
||||
depends on REGULATOR
|
||||
select STM32_DAC_CORE
|
||||
help
|
||||
Say yes here to build support for STMicroelectronics STM32 Digital
|
||||
to Analog Converter (DAC).
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called stm32-dac.
|
||||
|
||||
config STM32_DAC_CORE
|
||||
tristate
|
||||
|
||||
config VF610_DAC
|
||||
tristate "Vybrid vf610 DAC driver"
|
||||
depends on OF
|
||||
|
@ -24,9 +24,12 @@ obj-$(CONFIG_AD8801) += ad8801.o
|
||||
obj-$(CONFIG_CIO_DAC) += cio-dac.o
|
||||
obj-$(CONFIG_DPOT_DAC) += dpot-dac.o
|
||||
obj-$(CONFIG_LPC18XX_DAC) += lpc18xx_dac.o
|
||||
obj-$(CONFIG_LTC2632) += ltc2632.o
|
||||
obj-$(CONFIG_M62332) += m62332.o
|
||||
obj-$(CONFIG_MAX517) += max517.o
|
||||
obj-$(CONFIG_MAX5821) += max5821.o
|
||||
obj-$(CONFIG_MCP4725) += mcp4725.o
|
||||
obj-$(CONFIG_MCP4922) += mcp4922.o
|
||||
obj-$(CONFIG_STM32_DAC_CORE) += stm32-dac-core.o
|
||||
obj-$(CONFIG_STM32_DAC) += stm32-dac.o
|
||||
obj-$(CONFIG_VF610_DAC) += vf610_dac.o
|
||||
|
@ -212,7 +212,7 @@ static struct attribute *ad5504_ev_attributes[] = {
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group ad5504_ev_attribute_group = {
|
||||
static const struct attribute_group ad5504_ev_attribute_group = {
|
||||
.attrs = ad5504_ev_attributes,
|
||||
};
|
||||
|
||||
@ -223,7 +223,7 @@ static irqreturn_t ad5504_event_handler(int irq, void *private)
|
||||
0,
|
||||
IIO_EV_TYPE_THRESH,
|
||||
IIO_EV_DIR_RISING),
|
||||
iio_get_time_ns((struct iio_dev *)private));
|
||||
iio_get_time_ns(private));
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
@ -184,9 +184,9 @@ static const struct iio_chan_spec_ext_info ad7303_ext_info[] = {
|
||||
.address = (chan), \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = '8', \
|
||||
.storagebits = '8', \
|
||||
.shift = '0', \
|
||||
.realbits = 8, \
|
||||
.storagebits = 8, \
|
||||
.shift = 0, \
|
||||
}, \
|
||||
.ext_info = ad7303_ext_info, \
|
||||
}
|
||||
|
@ -119,6 +119,7 @@ static int cio_dac_probe(struct device *dev, unsigned int id)
|
||||
indio_dev->channels = cio_dac_channels;
|
||||
indio_dev->num_channels = CIO_DAC_NUM_CHAN;
|
||||
indio_dev->name = dev_name(dev);
|
||||
indio_dev->dev.parent = dev;
|
||||
|
||||
priv = iio_priv(indio_dev);
|
||||
priv->base = base[id];
|
||||
|
314
drivers/iio/dac/ltc2632.c
Normal file
314
drivers/iio/dac/ltc2632.c
Normal file
@ -0,0 +1,314 @@
|
||||
/*
|
||||
* LTC2632 Digital to analog convertors spi driver
|
||||
*
|
||||
* Copyright 2017 Maxime Roussin-Bélanger
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/iio/iio.h>
|
||||
|
||||
#define LTC2632_DAC_CHANNELS 2
|
||||
|
||||
#define LTC2632_ADDR_DAC0 0x0
|
||||
#define LTC2632_ADDR_DAC1 0x1
|
||||
|
||||
#define LTC2632_CMD_WRITE_INPUT_N 0x0
|
||||
#define LTC2632_CMD_UPDATE_DAC_N 0x1
|
||||
#define LTC2632_CMD_WRITE_INPUT_N_UPDATE_ALL 0x2
|
||||
#define LTC2632_CMD_WRITE_INPUT_N_UPDATE_N 0x3
|
||||
#define LTC2632_CMD_POWERDOWN_DAC_N 0x4
|
||||
#define LTC2632_CMD_POWERDOWN_CHIP 0x5
|
||||
#define LTC2632_CMD_INTERNAL_REFER 0x6
|
||||
#define LTC2632_CMD_EXTERNAL_REFER 0x7
|
||||
|
||||
/**
|
||||
* struct ltc2632_chip_info - chip specific information
|
||||
* @channels: channel spec for the DAC
|
||||
* @vref_mv: reference voltage
|
||||
*/
|
||||
struct ltc2632_chip_info {
|
||||
const struct iio_chan_spec *channels;
|
||||
const int vref_mv;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ltc2632_state - driver instance specific data
|
||||
* @spi_dev: pointer to the spi_device struct
|
||||
* @powerdown_cache_mask used to show current channel powerdown state
|
||||
*/
|
||||
struct ltc2632_state {
|
||||
struct spi_device *spi_dev;
|
||||
unsigned int powerdown_cache_mask;
|
||||
};
|
||||
|
||||
enum ltc2632_supported_device_ids {
|
||||
ID_LTC2632L12,
|
||||
ID_LTC2632L10,
|
||||
ID_LTC2632L8,
|
||||
ID_LTC2632H12,
|
||||
ID_LTC2632H10,
|
||||
ID_LTC2632H8,
|
||||
};
|
||||
|
||||
static int ltc2632_spi_write(struct spi_device *spi,
|
||||
u8 cmd, u8 addr, u16 val, u8 shift)
|
||||
{
|
||||
u32 data;
|
||||
u8 msg[3];
|
||||
|
||||
/*
|
||||
* The input shift register is 24 bits wide.
|
||||
* The next four are the command bits, C3 to C0,
|
||||
* followed by the 4-bit DAC address, A3 to A0, and then the
|
||||
* 12-, 10-, 8-bit data-word. The data-word comprises the 12-,
|
||||
* 10-, 8-bit input code followed by 4, 6, or 8 don't care bits.
|
||||
*/
|
||||
data = (cmd << 20) | (addr << 16) | (val << shift);
|
||||
msg[0] = data >> 16;
|
||||
msg[1] = data >> 8;
|
||||
msg[2] = data;
|
||||
|
||||
return spi_write(spi, msg, sizeof(msg));
|
||||
}
|
||||
|
||||
static int ltc2632_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val,
|
||||
int *val2,
|
||||
long m)
|
||||
{
|
||||
struct ltc2632_chip_info *chip_info;
|
||||
|
||||
const struct ltc2632_state *st = iio_priv(indio_dev);
|
||||
const struct spi_device_id *spi_dev_id = spi_get_device_id(st->spi_dev);
|
||||
|
||||
chip_info = (struct ltc2632_chip_info *)spi_dev_id->driver_data;
|
||||
|
||||
switch (m) {
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = chip_info->vref_mv;
|
||||
*val2 = chan->scan_type.realbits;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int ltc2632_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val,
|
||||
int val2,
|
||||
long mask)
|
||||
{
|
||||
struct ltc2632_state *st = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (val >= (1 << chan->scan_type.realbits) || val < 0)
|
||||
return -EINVAL;
|
||||
|
||||
return ltc2632_spi_write(st->spi_dev,
|
||||
LTC2632_CMD_WRITE_INPUT_N_UPDATE_N,
|
||||
chan->address, val,
|
||||
chan->scan_type.shift);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t ltc2632_read_dac_powerdown(struct iio_dev *indio_dev,
|
||||
uintptr_t private,
|
||||
const struct iio_chan_spec *chan,
|
||||
char *buf)
|
||||
{
|
||||
struct ltc2632_state *st = iio_priv(indio_dev);
|
||||
|
||||
return sprintf(buf, "%d\n",
|
||||
!!(st->powerdown_cache_mask & (1 << chan->channel)));
|
||||
}
|
||||
|
||||
static ssize_t ltc2632_write_dac_powerdown(struct iio_dev *indio_dev,
|
||||
uintptr_t private,
|
||||
const struct iio_chan_spec *chan,
|
||||
const char *buf,
|
||||
size_t len)
|
||||
{
|
||||
bool pwr_down;
|
||||
int ret;
|
||||
struct ltc2632_state *st = iio_priv(indio_dev);
|
||||
|
||||
ret = strtobool(buf, &pwr_down);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (pwr_down)
|
||||
st->powerdown_cache_mask |= (1 << chan->channel);
|
||||
else
|
||||
st->powerdown_cache_mask &= ~(1 << chan->channel);
|
||||
|
||||
ret = ltc2632_spi_write(st->spi_dev,
|
||||
LTC2632_CMD_POWERDOWN_DAC_N,
|
||||
chan->channel, 0, 0);
|
||||
|
||||
return ret ? ret : len;
|
||||
}
|
||||
|
||||
static const struct iio_info ltc2632_info = {
|
||||
.write_raw = ltc2632_write_raw,
|
||||
.read_raw = ltc2632_read_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec_ext_info ltc2632_ext_info[] = {
|
||||
{
|
||||
.name = "powerdown",
|
||||
.read = ltc2632_read_dac_powerdown,
|
||||
.write = ltc2632_write_dac_powerdown,
|
||||
.shared = IIO_SEPARATE,
|
||||
},
|
||||
{ },
|
||||
};
|
||||
|
||||
#define LTC2632_CHANNEL(_chan, _bits) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.output = 1, \
|
||||
.channel = (_chan), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.address = (_chan), \
|
||||
.scan_type = { \
|
||||
.realbits = (_bits), \
|
||||
.shift = 16 - (_bits), \
|
||||
}, \
|
||||
.ext_info = ltc2632_ext_info, \
|
||||
}
|
||||
|
||||
#define DECLARE_LTC2632_CHANNELS(_name, _bits) \
|
||||
const struct iio_chan_spec _name ## _channels[] = { \
|
||||
LTC2632_CHANNEL(0, _bits), \
|
||||
LTC2632_CHANNEL(1, _bits), \
|
||||
}
|
||||
|
||||
static DECLARE_LTC2632_CHANNELS(ltc2632l12, 12);
|
||||
static DECLARE_LTC2632_CHANNELS(ltc2632l10, 10);
|
||||
static DECLARE_LTC2632_CHANNELS(ltc2632l8, 8);
|
||||
|
||||
static DECLARE_LTC2632_CHANNELS(ltc2632h12, 12);
|
||||
static DECLARE_LTC2632_CHANNELS(ltc2632h10, 10);
|
||||
static DECLARE_LTC2632_CHANNELS(ltc2632h8, 8);
|
||||
|
||||
static const struct ltc2632_chip_info ltc2632_chip_info_tbl[] = {
|
||||
[ID_LTC2632L12] = {
|
||||
.channels = ltc2632l12_channels,
|
||||
.vref_mv = 2500,
|
||||
},
|
||||
[ID_LTC2632L10] = {
|
||||
.channels = ltc2632l10_channels,
|
||||
.vref_mv = 2500,
|
||||
},
|
||||
[ID_LTC2632L8] = {
|
||||
.channels = ltc2632l8_channels,
|
||||
.vref_mv = 2500,
|
||||
},
|
||||
[ID_LTC2632H12] = {
|
||||
.channels = ltc2632h12_channels,
|
||||
.vref_mv = 4096,
|
||||
},
|
||||
[ID_LTC2632H10] = {
|
||||
.channels = ltc2632h10_channels,
|
||||
.vref_mv = 4096,
|
||||
},
|
||||
[ID_LTC2632H8] = {
|
||||
.channels = ltc2632h8_channels,
|
||||
.vref_mv = 4096,
|
||||
},
|
||||
};
|
||||
|
||||
static int ltc2632_probe(struct spi_device *spi)
|
||||
{
|
||||
struct ltc2632_state *st;
|
||||
struct iio_dev *indio_dev;
|
||||
struct ltc2632_chip_info *chip_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_dev = spi;
|
||||
|
||||
chip_info = (struct ltc2632_chip_info *)
|
||||
spi_get_device_id(spi)->driver_data;
|
||||
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->name = dev_of_node(&spi->dev) ? dev_of_node(&spi->dev)->name
|
||||
: spi_get_device_id(spi)->name;
|
||||
indio_dev->info = <c2632_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = chip_info->channels;
|
||||
indio_dev->num_channels = LTC2632_DAC_CHANNELS;
|
||||
|
||||
ret = ltc2632_spi_write(spi, LTC2632_CMD_INTERNAL_REFER, 0, 0, 0);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev,
|
||||
"Set internal reference command failed, %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return devm_iio_device_register(&spi->dev, indio_dev);
|
||||
}
|
||||
|
||||
static const struct spi_device_id ltc2632_id[] = {
|
||||
{ "ltc2632-l12", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2632L12] },
|
||||
{ "ltc2632-l10", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2632L10] },
|
||||
{ "ltc2632-l8", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2632L8] },
|
||||
{ "ltc2632-h12", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2632H12] },
|
||||
{ "ltc2632-h10", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2632H10] },
|
||||
{ "ltc2632-h8", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2632H8] },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ltc2632_id);
|
||||
|
||||
static struct spi_driver ltc2632_driver = {
|
||||
.driver = {
|
||||
.name = "ltc2632",
|
||||
},
|
||||
.probe = ltc2632_probe,
|
||||
.id_table = ltc2632_id,
|
||||
};
|
||||
module_spi_driver(ltc2632_driver);
|
||||
|
||||
static const struct of_device_id ltc2632_of_match[] = {
|
||||
{
|
||||
.compatible = "lltc,ltc2632-l12",
|
||||
.data = <c2632_chip_info_tbl[ID_LTC2632L12]
|
||||
}, {
|
||||
.compatible = "lltc,ltc2632-l10",
|
||||
.data = <c2632_chip_info_tbl[ID_LTC2632L10]
|
||||
}, {
|
||||
.compatible = "lltc,ltc2632-l8",
|
||||
.data = <c2632_chip_info_tbl[ID_LTC2632L8]
|
||||
}, {
|
||||
.compatible = "lltc,ltc2632-h12",
|
||||
.data = <c2632_chip_info_tbl[ID_LTC2632H12]
|
||||
}, {
|
||||
.compatible = "lltc,ltc2632-h10",
|
||||
.data = <c2632_chip_info_tbl[ID_LTC2632H10]
|
||||
}, {
|
||||
.compatible = "lltc,ltc2632-h8",
|
||||
.data = <c2632_chip_info_tbl[ID_LTC2632H8]
|
||||
},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ltc2632_of_match);
|
||||
|
||||
MODULE_AUTHOR("Maxime Roussin-Belanger <maxime.roussinbelanger@gmail.com>");
|
||||
MODULE_DESCRIPTION("LTC2632 DAC SPI driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -392,6 +392,7 @@ MODULE_DEVICE_TABLE(of, max5821_of_match);
|
||||
static struct i2c_driver max5821_driver = {
|
||||
.driver = {
|
||||
.name = "max5821",
|
||||
.of_match_table = max5821_of_match,
|
||||
.pm = MAX5821_PM_OPS,
|
||||
},
|
||||
.probe = max5821_probe,
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include <linux/err.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
@ -199,7 +200,7 @@ static ssize_t mcp4725_write_powerdown(struct iio_dev *indio_dev,
|
||||
return len;
|
||||
}
|
||||
|
||||
enum {
|
||||
enum chip_id {
|
||||
MCP4725,
|
||||
MCP4726,
|
||||
};
|
||||
@ -406,7 +407,10 @@ static int mcp4725_probe(struct i2c_client *client,
|
||||
data = iio_priv(indio_dev);
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
data->client = client;
|
||||
data->id = id->driver_data;
|
||||
if (client->dev.of_node)
|
||||
data->id = (enum chip_id)of_device_get_match_data(&client->dev);
|
||||
else
|
||||
data->id = id->driver_data;
|
||||
pdata = dev_get_platdata(&client->dev);
|
||||
|
||||
if (!pdata) {
|
||||
@ -525,9 +529,25 @@ static const struct i2c_device_id mcp4725_id[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, mcp4725_id);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id mcp4725_of_match[] = {
|
||||
{
|
||||
.compatible = "microchip,mcp4725",
|
||||
.data = (void *)MCP4725
|
||||
},
|
||||
{
|
||||
.compatible = "microchip,mcp4726",
|
||||
.data = (void *)MCP4726
|
||||
},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mcp4725_of_match);
|
||||
#endif
|
||||
|
||||
static struct i2c_driver mcp4725_driver = {
|
||||
.driver = {
|
||||
.name = MCP4725_DRV_NAME,
|
||||
.of_match_table = of_match_ptr(mcp4725_of_match),
|
||||
.pm = MCP4725_PM_OPS,
|
||||
},
|
||||
.probe = mcp4725_probe,
|
||||
|
180
drivers/iio/dac/stm32-dac-core.c
Normal file
180
drivers/iio/dac/stm32-dac-core.c
Normal file
@ -0,0 +1,180 @@
|
||||
/*
|
||||
* This file is part of STM32 DAC driver
|
||||
*
|
||||
* Copyright (C) 2017, STMicroelectronics - All Rights Reserved
|
||||
* Author: Fabrice Gasnier <fabrice.gasnier@st.com>.
|
||||
*
|
||||
* License type: GPLv2
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/reset.h>
|
||||
|
||||
#include "stm32-dac-core.h"
|
||||
|
||||
/**
|
||||
* struct stm32_dac_priv - stm32 DAC core private data
|
||||
* @pclk: peripheral clock common for all DACs
|
||||
* @rst: peripheral reset control
|
||||
* @vref: regulator reference
|
||||
* @common: Common data for all DAC instances
|
||||
*/
|
||||
struct stm32_dac_priv {
|
||||
struct clk *pclk;
|
||||
struct reset_control *rst;
|
||||
struct regulator *vref;
|
||||
struct stm32_dac_common common;
|
||||
};
|
||||
|
||||
static struct stm32_dac_priv *to_stm32_dac_priv(struct stm32_dac_common *com)
|
||||
{
|
||||
return container_of(com, struct stm32_dac_priv, common);
|
||||
}
|
||||
|
||||
static const struct regmap_config stm32_dac_regmap_cfg = {
|
||||
.reg_bits = 32,
|
||||
.val_bits = 32,
|
||||
.reg_stride = sizeof(u32),
|
||||
.max_register = 0x3fc,
|
||||
};
|
||||
|
||||
static int stm32_dac_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct stm32_dac_priv *priv;
|
||||
struct regmap *regmap;
|
||||
struct resource *res;
|
||||
void __iomem *mmio;
|
||||
int ret;
|
||||
|
||||
if (!dev->of_node)
|
||||
return -ENODEV;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
mmio = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(mmio))
|
||||
return PTR_ERR(mmio);
|
||||
|
||||
regmap = devm_regmap_init_mmio(dev, mmio, &stm32_dac_regmap_cfg);
|
||||
if (IS_ERR(regmap))
|
||||
return PTR_ERR(regmap);
|
||||
priv->common.regmap = regmap;
|
||||
|
||||
priv->vref = devm_regulator_get(dev, "vref");
|
||||
if (IS_ERR(priv->vref)) {
|
||||
ret = PTR_ERR(priv->vref);
|
||||
dev_err(dev, "vref get failed, %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regulator_enable(priv->vref);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "vref enable failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regulator_get_voltage(priv->vref);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "vref get voltage failed, %d\n", ret);
|
||||
goto err_vref;
|
||||
}
|
||||
priv->common.vref_mv = ret / 1000;
|
||||
dev_dbg(dev, "vref+=%dmV\n", priv->common.vref_mv);
|
||||
|
||||
priv->pclk = devm_clk_get(dev, "pclk");
|
||||
if (IS_ERR(priv->pclk)) {
|
||||
ret = PTR_ERR(priv->pclk);
|
||||
dev_err(dev, "pclk get failed\n");
|
||||
goto err_vref;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(priv->pclk);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "pclk enable failed\n");
|
||||
goto err_vref;
|
||||
}
|
||||
|
||||
priv->rst = devm_reset_control_get(dev, NULL);
|
||||
if (!IS_ERR(priv->rst)) {
|
||||
reset_control_assert(priv->rst);
|
||||
udelay(2);
|
||||
reset_control_deassert(priv->rst);
|
||||
}
|
||||
|
||||
/* When clock speed is higher than 80MHz, set HFSEL */
|
||||
priv->common.hfsel = (clk_get_rate(priv->pclk) > 80000000UL);
|
||||
ret = regmap_update_bits(regmap, STM32_DAC_CR, STM32H7_DAC_CR_HFSEL,
|
||||
priv->common.hfsel ? STM32H7_DAC_CR_HFSEL : 0);
|
||||
if (ret)
|
||||
goto err_pclk;
|
||||
|
||||
platform_set_drvdata(pdev, &priv->common);
|
||||
|
||||
ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, dev);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to populate DT children\n");
|
||||
goto err_pclk;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_pclk:
|
||||
clk_disable_unprepare(priv->pclk);
|
||||
err_vref:
|
||||
regulator_disable(priv->vref);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int stm32_dac_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct stm32_dac_common *common = platform_get_drvdata(pdev);
|
||||
struct stm32_dac_priv *priv = to_stm32_dac_priv(common);
|
||||
|
||||
of_platform_depopulate(&pdev->dev);
|
||||
clk_disable_unprepare(priv->pclk);
|
||||
regulator_disable(priv->vref);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id stm32_dac_of_match[] = {
|
||||
{ .compatible = "st,stm32h7-dac-core", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, stm32_dac_of_match);
|
||||
|
||||
static struct platform_driver stm32_dac_driver = {
|
||||
.probe = stm32_dac_probe,
|
||||
.remove = stm32_dac_remove,
|
||||
.driver = {
|
||||
.name = "stm32-dac-core",
|
||||
.of_match_table = stm32_dac_of_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(stm32_dac_driver);
|
||||
|
||||
MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier@st.com>");
|
||||
MODULE_DESCRIPTION("STMicroelectronics STM32 DAC core driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:stm32-dac-core");
|
51
drivers/iio/dac/stm32-dac-core.h
Normal file
51
drivers/iio/dac/stm32-dac-core.h
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* This file is part of STM32 DAC driver
|
||||
*
|
||||
* Copyright (C) 2017, STMicroelectronics - All Rights Reserved
|
||||
* Author: Fabrice Gasnier <fabrice.gasnier@st.com>.
|
||||
*
|
||||
* License type: GPLv2
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __STM32_DAC_CORE_H
|
||||
#define __STM32_DAC_CORE_H
|
||||
|
||||
#include <linux/regmap.h>
|
||||
|
||||
/* STM32 DAC registers */
|
||||
#define STM32_DAC_CR 0x00
|
||||
#define STM32_DAC_DHR12R1 0x08
|
||||
#define STM32_DAC_DHR12R2 0x14
|
||||
#define STM32_DAC_DOR1 0x2C
|
||||
#define STM32_DAC_DOR2 0x30
|
||||
|
||||
/* STM32_DAC_CR bit fields */
|
||||
#define STM32_DAC_CR_EN1 BIT(0)
|
||||
#define STM32H7_DAC_CR_HFSEL BIT(15)
|
||||
#define STM32_DAC_CR_EN2 BIT(16)
|
||||
|
||||
/**
|
||||
* struct stm32_dac_common - stm32 DAC driver common data (for all instances)
|
||||
* @regmap: DAC registers shared via regmap
|
||||
* @vref_mv: reference voltage (mv)
|
||||
* @hfsel: high speed bus clock selected
|
||||
*/
|
||||
struct stm32_dac_common {
|
||||
struct regmap *regmap;
|
||||
int vref_mv;
|
||||
bool hfsel;
|
||||
};
|
||||
|
||||
#endif
|
334
drivers/iio/dac/stm32-dac.c
Normal file
334
drivers/iio/dac/stm32-dac.c
Normal file
@ -0,0 +1,334 @@
|
||||
/*
|
||||
* This file is part of STM32 DAC driver
|
||||
*
|
||||
* Copyright (C) 2017, STMicroelectronics - All Rights Reserved
|
||||
* Authors: Amelie Delaunay <amelie.delaunay@st.com>
|
||||
* Fabrice Gasnier <fabrice.gasnier@st.com>
|
||||
*
|
||||
* License type: GPLv2
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include "stm32-dac-core.h"
|
||||
|
||||
#define STM32_DAC_CHANNEL_1 1
|
||||
#define STM32_DAC_CHANNEL_2 2
|
||||
#define STM32_DAC_IS_CHAN_1(ch) ((ch) & STM32_DAC_CHANNEL_1)
|
||||
|
||||
/**
|
||||
* struct stm32_dac - private data of DAC driver
|
||||
* @common: reference to DAC common data
|
||||
*/
|
||||
struct stm32_dac {
|
||||
struct stm32_dac_common *common;
|
||||
};
|
||||
|
||||
static int stm32_dac_is_enabled(struct iio_dev *indio_dev, int channel)
|
||||
{
|
||||
struct stm32_dac *dac = iio_priv(indio_dev);
|
||||
u32 en, val;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(dac->common->regmap, STM32_DAC_CR, &val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (STM32_DAC_IS_CHAN_1(channel))
|
||||
en = FIELD_GET(STM32_DAC_CR_EN1, val);
|
||||
else
|
||||
en = FIELD_GET(STM32_DAC_CR_EN2, val);
|
||||
|
||||
return !!en;
|
||||
}
|
||||
|
||||
static int stm32_dac_set_enable_state(struct iio_dev *indio_dev, int ch,
|
||||
bool enable)
|
||||
{
|
||||
struct stm32_dac *dac = iio_priv(indio_dev);
|
||||
u32 msk = STM32_DAC_IS_CHAN_1(ch) ? STM32_DAC_CR_EN1 : STM32_DAC_CR_EN2;
|
||||
u32 en = enable ? msk : 0;
|
||||
int ret;
|
||||
|
||||
ret = regmap_update_bits(dac->common->regmap, STM32_DAC_CR, msk, en);
|
||||
if (ret < 0) {
|
||||
dev_err(&indio_dev->dev, "%s failed\n", en ?
|
||||
"Enable" : "Disable");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* When HFSEL is set, it is not allowed to write the DHRx register
|
||||
* during 8 clock cycles after the ENx bit is set. It is not allowed
|
||||
* to make software/hardware trigger during this period either.
|
||||
*/
|
||||
if (en && dac->common->hfsel)
|
||||
udelay(1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stm32_dac_get_value(struct stm32_dac *dac, int channel, int *val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (STM32_DAC_IS_CHAN_1(channel))
|
||||
ret = regmap_read(dac->common->regmap, STM32_DAC_DOR1, val);
|
||||
else
|
||||
ret = regmap_read(dac->common->regmap, STM32_DAC_DOR2, val);
|
||||
|
||||
return ret ? ret : IIO_VAL_INT;
|
||||
}
|
||||
|
||||
static int stm32_dac_set_value(struct stm32_dac *dac, int channel, int val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (STM32_DAC_IS_CHAN_1(channel))
|
||||
ret = regmap_write(dac->common->regmap, STM32_DAC_DHR12R1, val);
|
||||
else
|
||||
ret = regmap_write(dac->common->regmap, STM32_DAC_DHR12R2, val);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int stm32_dac_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct stm32_dac *dac = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
return stm32_dac_get_value(dac, chan->channel, val);
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = dac->common->vref_mv;
|
||||
*val2 = chan->scan_type.realbits;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int stm32_dac_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
struct stm32_dac *dac = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
return stm32_dac_set_value(dac, chan->channel, val);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int stm32_dac_debugfs_reg_access(struct iio_dev *indio_dev,
|
||||
unsigned reg, unsigned writeval,
|
||||
unsigned *readval)
|
||||
{
|
||||
struct stm32_dac *dac = iio_priv(indio_dev);
|
||||
|
||||
if (!readval)
|
||||
return regmap_write(dac->common->regmap, reg, writeval);
|
||||
else
|
||||
return regmap_read(dac->common->regmap, reg, readval);
|
||||
}
|
||||
|
||||
static const struct iio_info stm32_dac_iio_info = {
|
||||
.read_raw = stm32_dac_read_raw,
|
||||
.write_raw = stm32_dac_write_raw,
|
||||
.debugfs_reg_access = stm32_dac_debugfs_reg_access,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static const char * const stm32_dac_powerdown_modes[] = {
|
||||
"three_state",
|
||||
};
|
||||
|
||||
static int stm32_dac_get_powerdown_mode(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stm32_dac_set_powerdown_mode(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
unsigned int type)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t stm32_dac_read_powerdown(struct iio_dev *indio_dev,
|
||||
uintptr_t private,
|
||||
const struct iio_chan_spec *chan,
|
||||
char *buf)
|
||||
{
|
||||
int ret = stm32_dac_is_enabled(indio_dev, chan->channel);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return sprintf(buf, "%d\n", ret ? 0 : 1);
|
||||
}
|
||||
|
||||
static ssize_t stm32_dac_write_powerdown(struct iio_dev *indio_dev,
|
||||
uintptr_t private,
|
||||
const struct iio_chan_spec *chan,
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
bool powerdown;
|
||||
int ret;
|
||||
|
||||
ret = strtobool(buf, &powerdown);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = stm32_dac_set_enable_state(indio_dev, chan->channel, !powerdown);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static const struct iio_enum stm32_dac_powerdown_mode_en = {
|
||||
.items = stm32_dac_powerdown_modes,
|
||||
.num_items = ARRAY_SIZE(stm32_dac_powerdown_modes),
|
||||
.get = stm32_dac_get_powerdown_mode,
|
||||
.set = stm32_dac_set_powerdown_mode,
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec_ext_info stm32_dac_ext_info[] = {
|
||||
{
|
||||
.name = "powerdown",
|
||||
.read = stm32_dac_read_powerdown,
|
||||
.write = stm32_dac_write_powerdown,
|
||||
.shared = IIO_SEPARATE,
|
||||
},
|
||||
IIO_ENUM("powerdown_mode", IIO_SEPARATE, &stm32_dac_powerdown_mode_en),
|
||||
IIO_ENUM_AVAILABLE("powerdown_mode", &stm32_dac_powerdown_mode_en),
|
||||
{},
|
||||
};
|
||||
|
||||
#define STM32_DAC_CHANNEL(chan, name) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.output = 1, \
|
||||
.channel = chan, \
|
||||
.info_mask_separate = \
|
||||
BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_SCALE), \
|
||||
/* scan_index is always 0 as num_channels is 1 */ \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = 12, \
|
||||
.storagebits = 16, \
|
||||
}, \
|
||||
.datasheet_name = name, \
|
||||
.ext_info = stm32_dac_ext_info \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec stm32_dac_channels[] = {
|
||||
STM32_DAC_CHANNEL(STM32_DAC_CHANNEL_1, "out1"),
|
||||
STM32_DAC_CHANNEL(STM32_DAC_CHANNEL_2, "out2"),
|
||||
};
|
||||
|
||||
static int stm32_dac_chan_of_init(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct device_node *np = indio_dev->dev.of_node;
|
||||
unsigned int i;
|
||||
u32 channel;
|
||||
int ret;
|
||||
|
||||
ret = of_property_read_u32(np, "reg", &channel);
|
||||
if (ret) {
|
||||
dev_err(&indio_dev->dev, "Failed to read reg property\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(stm32_dac_channels); i++) {
|
||||
if (stm32_dac_channels[i].channel == channel)
|
||||
break;
|
||||
}
|
||||
if (i >= ARRAY_SIZE(stm32_dac_channels)) {
|
||||
dev_err(&indio_dev->dev, "Invalid st,dac-channel\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
indio_dev->channels = &stm32_dac_channels[i];
|
||||
/*
|
||||
* Expose only one channel here, as they can be used independently,
|
||||
* with separate trigger. Then separate IIO devices are instantiated
|
||||
* to manage this.
|
||||
*/
|
||||
indio_dev->num_channels = 1;
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
static int stm32_dac_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct iio_dev *indio_dev;
|
||||
struct stm32_dac *dac;
|
||||
int ret;
|
||||
|
||||
if (!np)
|
||||
return -ENODEV;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*dac));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
platform_set_drvdata(pdev, indio_dev);
|
||||
|
||||
dac = iio_priv(indio_dev);
|
||||
dac->common = dev_get_drvdata(pdev->dev.parent);
|
||||
indio_dev->name = dev_name(&pdev->dev);
|
||||
indio_dev->dev.parent = &pdev->dev;
|
||||
indio_dev->dev.of_node = pdev->dev.of_node;
|
||||
indio_dev->info = &stm32_dac_iio_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
ret = stm32_dac_chan_of_init(indio_dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return devm_iio_device_register(&pdev->dev, indio_dev);
|
||||
}
|
||||
|
||||
static const struct of_device_id stm32_dac_of_match[] = {
|
||||
{ .compatible = "st,stm32-dac", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, stm32_dac_of_match);
|
||||
|
||||
static struct platform_driver stm32_dac_driver = {
|
||||
.probe = stm32_dac_probe,
|
||||
.driver = {
|
||||
.name = "stm32-dac",
|
||||
.of_match_table = stm32_dac_of_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(stm32_dac_driver);
|
||||
|
||||
MODULE_ALIAS("platform:stm32-dac");
|
||||
MODULE_AUTHOR("Amelie Delaunay <amelie.delaunay@st.com>");
|
||||
MODULE_DESCRIPTION("STMicroelectronics STM32 DAC driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -377,9 +377,16 @@ static const struct i2c_device_id itg3200_id[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, itg3200_id);
|
||||
|
||||
static const struct of_device_id itg3200_of_match[] = {
|
||||
{ .compatible = "invensense,itg3200" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, itg3200_of_match);
|
||||
|
||||
static struct i2c_driver itg3200_driver = {
|
||||
.driver = {
|
||||
.name = "itg3200",
|
||||
.of_match_table = itg3200_of_match,
|
||||
.pm = &itg3200_pm_ops,
|
||||
},
|
||||
.id_table = itg3200_id,
|
||||
|
@ -70,9 +70,8 @@ static int mpu3050_i2c_probe(struct i2c_client *client,
|
||||
dev_err(&client->dev, "failed to allocate I2C mux\n");
|
||||
else {
|
||||
mpu3050->i2cmux->priv = mpu3050;
|
||||
ret = i2c_mux_add_adapter(mpu3050->i2cmux, 0, 0, 0);
|
||||
if (ret)
|
||||
dev_err(&client->dev, "failed to add I2C mux\n");
|
||||
/* Ignore failure, not critical */
|
||||
i2c_mux_add_adapter(mpu3050->i2cmux, 0, 0, 0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -46,6 +46,19 @@ config MAX30100
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called max30100.
|
||||
|
||||
config MAX30102
|
||||
tristate "MAX30102 heart rate and pulse oximeter sensor"
|
||||
depends on I2C
|
||||
select REGMAP_I2C
|
||||
select IIO_BUFFER
|
||||
select IIO_KFIFO_BUF
|
||||
help
|
||||
Say Y here to build I2C interface support for the Maxim
|
||||
MAX30102 heart rate, and pulse oximeter sensor.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called max30102.
|
||||
|
||||
endmenu
|
||||
|
||||
endmenu
|
||||
|
@ -7,3 +7,4 @@
|
||||
obj-$(CONFIG_AFE4403) += afe4403.o
|
||||
obj-$(CONFIG_AFE4404) += afe4404.o
|
||||
obj-$(CONFIG_MAX30100) += max30100.o
|
||||
obj-$(CONFIG_MAX30102) += max30102.o
|
||||
|
@ -449,6 +449,7 @@ static int max30100_probe(struct i2c_client *client,
|
||||
indio_dev->available_scan_masks = max30100_scan_masks;
|
||||
indio_dev->modes = (INDIO_BUFFER_SOFTWARE | INDIO_DIRECT_MODE);
|
||||
indio_dev->setup_ops = &max30100_buffer_setup_ops;
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
|
||||
data = iio_priv(indio_dev);
|
||||
data->indio_dev = indio_dev;
|
||||
|
486
drivers/iio/health/max30102.c
Normal file
486
drivers/iio/health/max30102.c
Normal file
@ -0,0 +1,486 @@
|
||||
/*
|
||||
* max30102.c - Support for MAX30102 heart rate and pulse oximeter sensor
|
||||
*
|
||||
* Copyright (C) 2017 Matt Ranostay <matt@ranostay.consulting>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* TODO: proximity power saving feature
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/kfifo_buf.h>
|
||||
|
||||
#define MAX30102_REGMAP_NAME "max30102_regmap"
|
||||
#define MAX30102_DRV_NAME "max30102"
|
||||
|
||||
#define MAX30102_REG_INT_STATUS 0x00
|
||||
#define MAX30102_REG_INT_STATUS_PWR_RDY BIT(0)
|
||||
#define MAX30102_REG_INT_STATUS_PROX_INT BIT(4)
|
||||
#define MAX30102_REG_INT_STATUS_ALC_OVF BIT(5)
|
||||
#define MAX30102_REG_INT_STATUS_PPG_RDY BIT(6)
|
||||
#define MAX30102_REG_INT_STATUS_FIFO_RDY BIT(7)
|
||||
|
||||
#define MAX30102_REG_INT_ENABLE 0x02
|
||||
#define MAX30102_REG_INT_ENABLE_PROX_INT_EN BIT(4)
|
||||
#define MAX30102_REG_INT_ENABLE_ALC_OVF_EN BIT(5)
|
||||
#define MAX30102_REG_INT_ENABLE_PPG_EN BIT(6)
|
||||
#define MAX30102_REG_INT_ENABLE_FIFO_EN BIT(7)
|
||||
#define MAX30102_REG_INT_ENABLE_MASK 0xf0
|
||||
#define MAX30102_REG_INT_ENABLE_MASK_SHIFT 4
|
||||
|
||||
#define MAX30102_REG_FIFO_WR_PTR 0x04
|
||||
#define MAX30102_REG_FIFO_OVR_CTR 0x05
|
||||
#define MAX30102_REG_FIFO_RD_PTR 0x06
|
||||
#define MAX30102_REG_FIFO_DATA 0x07
|
||||
#define MAX30102_REG_FIFO_DATA_ENTRY_LEN 6
|
||||
|
||||
#define MAX30102_REG_FIFO_CONFIG 0x08
|
||||
#define MAX30102_REG_FIFO_CONFIG_AVG_4SAMPLES BIT(1)
|
||||
#define MAX30102_REG_FIFO_CONFIG_AVG_SHIFT 5
|
||||
#define MAX30102_REG_FIFO_CONFIG_AFULL BIT(0)
|
||||
|
||||
#define MAX30102_REG_MODE_CONFIG 0x09
|
||||
#define MAX30102_REG_MODE_CONFIG_MODE_SPO2_EN BIT(0)
|
||||
#define MAX30102_REG_MODE_CONFIG_MODE_HR_EN BIT(1)
|
||||
#define MAX30102_REG_MODE_CONFIG_MODE_MASK 0x03
|
||||
#define MAX30102_REG_MODE_CONFIG_PWR BIT(7)
|
||||
|
||||
#define MAX30102_REG_SPO2_CONFIG 0x0a
|
||||
#define MAX30102_REG_SPO2_CONFIG_PULSE_411_US 0x03
|
||||
#define MAX30102_REG_SPO2_CONFIG_SR_400HZ 0x03
|
||||
#define MAX30102_REG_SPO2_CONFIG_SR_MASK 0x07
|
||||
#define MAX30102_REG_SPO2_CONFIG_SR_MASK_SHIFT 2
|
||||
#define MAX30102_REG_SPO2_CONFIG_ADC_4096_STEPS BIT(0)
|
||||
#define MAX30102_REG_SPO2_CONFIG_ADC_MASK_SHIFT 5
|
||||
|
||||
#define MAX30102_REG_RED_LED_CONFIG 0x0c
|
||||
#define MAX30102_REG_IR_LED_CONFIG 0x0d
|
||||
|
||||
#define MAX30102_REG_TEMP_CONFIG 0x21
|
||||
#define MAX30102_REG_TEMP_CONFIG_TEMP_EN BIT(0)
|
||||
|
||||
#define MAX30102_REG_TEMP_INTEGER 0x1f
|
||||
#define MAX30102_REG_TEMP_FRACTION 0x20
|
||||
|
||||
struct max30102_data {
|
||||
struct i2c_client *client;
|
||||
struct iio_dev *indio_dev;
|
||||
struct mutex lock;
|
||||
struct regmap *regmap;
|
||||
|
||||
u8 buffer[8];
|
||||
__be32 processed_buffer[2]; /* 2 x 18-bit (padded to 32-bits) */
|
||||
};
|
||||
|
||||
static const struct regmap_config max30102_regmap_config = {
|
||||
.name = MAX30102_REGMAP_NAME,
|
||||
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
};
|
||||
|
||||
static const unsigned long max30102_scan_masks[] = {0x3, 0};
|
||||
|
||||
static const struct iio_chan_spec max30102_channels[] = {
|
||||
{
|
||||
.type = IIO_INTENSITY,
|
||||
.channel2 = IIO_MOD_LIGHT_RED,
|
||||
.modified = 1,
|
||||
|
||||
.scan_index = 0,
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
.shift = 8,
|
||||
.realbits = 18,
|
||||
.storagebits = 32,
|
||||
.endianness = IIO_BE,
|
||||
},
|
||||
},
|
||||
{
|
||||
.type = IIO_INTENSITY,
|
||||
.channel2 = IIO_MOD_LIGHT_IR,
|
||||
.modified = 1,
|
||||
|
||||
.scan_index = 1,
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
.shift = 8,
|
||||
.realbits = 18,
|
||||
.storagebits = 32,
|
||||
.endianness = IIO_BE,
|
||||
},
|
||||
},
|
||||
{
|
||||
.type = IIO_TEMP,
|
||||
.info_mask_separate =
|
||||
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
|
||||
.scan_index = -1,
|
||||
},
|
||||
};
|
||||
|
||||
static int max30102_set_powermode(struct max30102_data *data, bool state)
|
||||
{
|
||||
return regmap_update_bits(data->regmap, MAX30102_REG_MODE_CONFIG,
|
||||
MAX30102_REG_MODE_CONFIG_PWR,
|
||||
state ? 0 : MAX30102_REG_MODE_CONFIG_PWR);
|
||||
}
|
||||
|
||||
static int max30102_buffer_postenable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct max30102_data *data = iio_priv(indio_dev);
|
||||
|
||||
return max30102_set_powermode(data, true);
|
||||
}
|
||||
|
||||
static int max30102_buffer_predisable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct max30102_data *data = iio_priv(indio_dev);
|
||||
|
||||
return max30102_set_powermode(data, false);
|
||||
}
|
||||
|
||||
static const struct iio_buffer_setup_ops max30102_buffer_setup_ops = {
|
||||
.postenable = max30102_buffer_postenable,
|
||||
.predisable = max30102_buffer_predisable,
|
||||
};
|
||||
|
||||
static inline int max30102_fifo_count(struct max30102_data *data)
|
||||
{
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(data->regmap, MAX30102_REG_INT_STATUS, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* FIFO has one sample slot left */
|
||||
if (val & MAX30102_REG_INT_STATUS_FIFO_RDY)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max30102_read_measurement(struct max30102_data *data)
|
||||
{
|
||||
int ret;
|
||||
u8 *buffer = (u8 *) &data->buffer;
|
||||
|
||||
ret = i2c_smbus_read_i2c_block_data(data->client,
|
||||
MAX30102_REG_FIFO_DATA,
|
||||
MAX30102_REG_FIFO_DATA_ENTRY_LEN,
|
||||
buffer);
|
||||
|
||||
memcpy(&data->processed_buffer[0], &buffer[0], 3);
|
||||
memcpy(&data->processed_buffer[1], &buffer[3], 3);
|
||||
|
||||
return (ret == MAX30102_REG_FIFO_DATA_ENTRY_LEN) ? 0 : -EINVAL;
|
||||
}
|
||||
|
||||
static irqreturn_t max30102_interrupt_handler(int irq, void *private)
|
||||
{
|
||||
struct iio_dev *indio_dev = private;
|
||||
struct max30102_data *data = iio_priv(indio_dev);
|
||||
int ret, cnt = 0;
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
|
||||
while (cnt || (cnt = max30102_fifo_count(data)) > 0) {
|
||||
ret = max30102_read_measurement(data);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
iio_push_to_buffers(data->indio_dev, data->processed_buffer);
|
||||
cnt--;
|
||||
}
|
||||
|
||||
mutex_unlock(&data->lock);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int max30102_get_current_idx(unsigned int val, int *reg)
|
||||
{
|
||||
/* each step is 0.200 mA */
|
||||
*reg = val / 200;
|
||||
|
||||
return *reg > 0xff ? -EINVAL : 0;
|
||||
}
|
||||
|
||||
static int max30102_led_init(struct max30102_data *data)
|
||||
{
|
||||
struct device *dev = &data->client->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
unsigned int val;
|
||||
int reg, ret;
|
||||
|
||||
ret = of_property_read_u32(np, "maxim,red-led-current-microamp", &val);
|
||||
if (ret) {
|
||||
dev_info(dev, "no red-led-current-microamp set\n");
|
||||
|
||||
/* Default to 7 mA RED LED */
|
||||
val = 7000;
|
||||
}
|
||||
|
||||
ret = max30102_get_current_idx(val, ®);
|
||||
if (ret) {
|
||||
dev_err(dev, "invalid RED LED current setting %d\n", val);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regmap_write(data->regmap, MAX30102_REG_RED_LED_CONFIG, reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = of_property_read_u32(np, "maxim,ir-led-current-microamp", &val);
|
||||
if (ret) {
|
||||
dev_info(dev, "no ir-led-current-microamp set\n");
|
||||
|
||||
/* Default to 7 mA IR LED */
|
||||
val = 7000;
|
||||
}
|
||||
|
||||
ret = max30102_get_current_idx(val, ®);
|
||||
if (ret) {
|
||||
dev_err(dev, "invalid IR LED current setting %d", val);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return regmap_write(data->regmap, MAX30102_REG_IR_LED_CONFIG, reg);
|
||||
}
|
||||
|
||||
static int max30102_chip_init(struct max30102_data *data)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* setup LED current settings */
|
||||
ret = max30102_led_init(data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* enable 18-bit HR + SPO2 readings at 400Hz */
|
||||
ret = regmap_write(data->regmap, MAX30102_REG_SPO2_CONFIG,
|
||||
(MAX30102_REG_SPO2_CONFIG_ADC_4096_STEPS
|
||||
<< MAX30102_REG_SPO2_CONFIG_ADC_MASK_SHIFT) |
|
||||
(MAX30102_REG_SPO2_CONFIG_SR_400HZ
|
||||
<< MAX30102_REG_SPO2_CONFIG_SR_MASK_SHIFT) |
|
||||
MAX30102_REG_SPO2_CONFIG_PULSE_411_US);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* enable SPO2 mode */
|
||||
ret = regmap_update_bits(data->regmap, MAX30102_REG_MODE_CONFIG,
|
||||
MAX30102_REG_MODE_CONFIG_MODE_MASK,
|
||||
MAX30102_REG_MODE_CONFIG_MODE_HR_EN |
|
||||
MAX30102_REG_MODE_CONFIG_MODE_SPO2_EN);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* average 4 samples + generate FIFO interrupt */
|
||||
ret = regmap_write(data->regmap, MAX30102_REG_FIFO_CONFIG,
|
||||
(MAX30102_REG_FIFO_CONFIG_AVG_4SAMPLES
|
||||
<< MAX30102_REG_FIFO_CONFIG_AVG_SHIFT) |
|
||||
MAX30102_REG_FIFO_CONFIG_AFULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* enable FIFO interrupt */
|
||||
return regmap_update_bits(data->regmap, MAX30102_REG_INT_ENABLE,
|
||||
MAX30102_REG_INT_ENABLE_MASK,
|
||||
MAX30102_REG_INT_ENABLE_FIFO_EN);
|
||||
}
|
||||
|
||||
static int max30102_read_temp(struct max30102_data *data, int *val)
|
||||
{
|
||||
int ret;
|
||||
unsigned int reg;
|
||||
|
||||
ret = regmap_read(data->regmap, MAX30102_REG_TEMP_INTEGER, ®);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = reg << 4;
|
||||
|
||||
ret = regmap_read(data->regmap, MAX30102_REG_TEMP_FRACTION, ®);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val |= reg & 0xf;
|
||||
*val = sign_extend32(*val, 11);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max30102_get_temp(struct max30102_data *data, int *val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* start acquisition */
|
||||
ret = regmap_update_bits(data->regmap, MAX30102_REG_TEMP_CONFIG,
|
||||
MAX30102_REG_TEMP_CONFIG_TEMP_EN,
|
||||
MAX30102_REG_TEMP_CONFIG_TEMP_EN);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
msleep(35);
|
||||
|
||||
return max30102_read_temp(data, val);
|
||||
}
|
||||
|
||||
static int max30102_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct max30102_data *data = iio_priv(indio_dev);
|
||||
int ret = -EINVAL;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
/*
|
||||
* Temperature reading can only be acquired while engine
|
||||
* is running
|
||||
*/
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
|
||||
if (!iio_buffer_enabled(indio_dev))
|
||||
ret = -EBUSY;
|
||||
else {
|
||||
ret = max30102_get_temp(data, val);
|
||||
if (!ret)
|
||||
ret = IIO_VAL_INT;
|
||||
}
|
||||
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
break;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = 1; /* 0.0625 */
|
||||
*val2 = 16;
|
||||
ret = IIO_VAL_FRACTIONAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct iio_info max30102_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = max30102_read_raw,
|
||||
};
|
||||
|
||||
static int max30102_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct max30102_data *data;
|
||||
struct iio_buffer *buffer;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
buffer = devm_iio_kfifo_allocate(&client->dev);
|
||||
if (!buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
iio_device_attach_buffer(indio_dev, buffer);
|
||||
|
||||
indio_dev->name = MAX30102_DRV_NAME;
|
||||
indio_dev->channels = max30102_channels;
|
||||
indio_dev->info = &max30102_info;
|
||||
indio_dev->num_channels = ARRAY_SIZE(max30102_channels);
|
||||
indio_dev->available_scan_masks = max30102_scan_masks;
|
||||
indio_dev->modes = (INDIO_BUFFER_SOFTWARE | INDIO_DIRECT_MODE);
|
||||
indio_dev->setup_ops = &max30102_buffer_setup_ops;
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
|
||||
data = iio_priv(indio_dev);
|
||||
data->indio_dev = indio_dev;
|
||||
data->client = client;
|
||||
|
||||
mutex_init(&data->lock);
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
|
||||
data->regmap = devm_regmap_init_i2c(client, &max30102_regmap_config);
|
||||
if (IS_ERR(data->regmap)) {
|
||||
dev_err(&client->dev, "regmap initialization failed.\n");
|
||||
return PTR_ERR(data->regmap);
|
||||
}
|
||||
max30102_set_powermode(data, false);
|
||||
|
||||
ret = max30102_chip_init(data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (client->irq <= 0) {
|
||||
dev_err(&client->dev, "no valid irq defined\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = devm_request_threaded_irq(&client->dev, client->irq,
|
||||
NULL, max30102_interrupt_handler,
|
||||
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
||||
"max30102_irq", indio_dev);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "request irq (%d) failed\n", client->irq);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return iio_device_register(indio_dev);
|
||||
}
|
||||
|
||||
static int max30102_remove(struct i2c_client *client)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
struct max30102_data *data = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
max30102_set_powermode(data, false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id max30102_id[] = {
|
||||
{ "max30102", 0 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, max30102_id);
|
||||
|
||||
static const struct of_device_id max30102_dt_ids[] = {
|
||||
{ .compatible = "maxim,max30102" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, max30102_dt_ids);
|
||||
|
||||
static struct i2c_driver max30102_driver = {
|
||||
.driver = {
|
||||
.name = MAX30102_DRV_NAME,
|
||||
.of_match_table = of_match_ptr(max30102_dt_ids),
|
||||
},
|
||||
.probe = max30102_probe,
|
||||
.remove = max30102_remove,
|
||||
.id_table = max30102_id,
|
||||
};
|
||||
module_i2c_driver(max30102_driver);
|
||||
|
||||
MODULE_AUTHOR("Matt Ranostay <matt@ranostay.consulting>");
|
||||
MODULE_DESCRIPTION("MAX30102 heart rate and pulse oximeter sensor");
|
||||
MODULE_LICENSE("GPL");
|
@ -36,6 +36,20 @@ config HDC100X
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called hdc100x.
|
||||
|
||||
config HID_SENSOR_HUMIDITY
|
||||
tristate "HID Environmental humidity sensor"
|
||||
depends on HID_SENSOR_HUB
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
select HID_SENSOR_IIO_COMMON
|
||||
select HID_SENSOR_IIO_TRIGGER
|
||||
help
|
||||
Say yes here to build support for the HID SENSOR
|
||||
humidity driver
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called hid-sensor-humidity.
|
||||
|
||||
config HTS221
|
||||
tristate "STMicroelectronics HTS221 sensor Driver"
|
||||
depends on (I2C || SPI)
|
||||
|
@ -5,6 +5,7 @@
|
||||
obj-$(CONFIG_AM2315) += am2315.o
|
||||
obj-$(CONFIG_DHT11) += dht11.o
|
||||
obj-$(CONFIG_HDC100X) += hdc100x.o
|
||||
obj-$(CONFIG_HID_SENSOR_HUMIDITY) += hid-sensor-humidity.o
|
||||
|
||||
hts221-y := hts221_core.o \
|
||||
hts221_buffer.o
|
||||
@ -15,3 +16,5 @@ obj-$(CONFIG_HTS221_SPI) += hts221_spi.o
|
||||
obj-$(CONFIG_HTU21) += htu21.o
|
||||
obj-$(CONFIG_SI7005) += si7005.o
|
||||
obj-$(CONFIG_SI7020) += si7020.o
|
||||
|
||||
ccflags-y += -I$(srctree)/drivers/iio/common/hid-sensors
|
||||
|
@ -79,7 +79,7 @@ static struct attribute *hdc100x_attributes[] = {
|
||||
NULL
|
||||
};
|
||||
|
||||
static struct attribute_group hdc100x_attribute_group = {
|
||||
static const struct attribute_group hdc100x_attribute_group = {
|
||||
.attrs = hdc100x_attributes,
|
||||
};
|
||||
|
||||
|
315
drivers/iio/humidity/hid-sensor-humidity.c
Normal file
315
drivers/iio/humidity/hid-sensor-humidity.c
Normal file
@ -0,0 +1,315 @@
|
||||
/*
|
||||
* HID Sensors Driver
|
||||
* Copyright (c) 2017, Intel Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program.
|
||||
*/
|
||||
#include <linux/device.h>
|
||||
#include <linux/hid-sensor-hub.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include "hid-sensor-trigger.h"
|
||||
|
||||
struct hid_humidity_state {
|
||||
struct hid_sensor_common common_attributes;
|
||||
struct hid_sensor_hub_attribute_info humidity_attr;
|
||||
s32 humidity_data;
|
||||
int scale_pre_decml;
|
||||
int scale_post_decml;
|
||||
int scale_precision;
|
||||
int value_offset;
|
||||
};
|
||||
|
||||
/* Channel definitions */
|
||||
static const struct iio_chan_spec humidity_channels[] = {
|
||||
{
|
||||
.type = IIO_HUMIDITYRELATIVE,
|
||||
.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),
|
||||
},
|
||||
IIO_CHAN_SOFT_TIMESTAMP(1)
|
||||
};
|
||||
|
||||
/* Adjust channel real bits based on report descriptor */
|
||||
static void humidity_adjust_channel_bit_mask(struct iio_chan_spec *channels,
|
||||
int channel, int size)
|
||||
{
|
||||
channels[channel].scan_type.sign = 's';
|
||||
/* Real storage bits will change based on the report desc. */
|
||||
channels[channel].scan_type.realbits = size * 8;
|
||||
/* Maximum size of a sample to capture is s32 */
|
||||
channels[channel].scan_type.storagebits = sizeof(s32) * 8;
|
||||
}
|
||||
|
||||
static int humidity_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct hid_humidity_state *humid_st = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (chan->type != IIO_HUMIDITYRELATIVE)
|
||||
return -EINVAL;
|
||||
hid_sensor_power_state(&humid_st->common_attributes, true);
|
||||
*val = sensor_hub_input_attr_get_raw_value(
|
||||
humid_st->common_attributes.hsdev,
|
||||
HID_USAGE_SENSOR_HUMIDITY,
|
||||
HID_USAGE_SENSOR_ATMOSPHERIC_HUMIDITY,
|
||||
humid_st->humidity_attr.report_id,
|
||||
SENSOR_HUB_SYNC);
|
||||
hid_sensor_power_state(&humid_st->common_attributes, false);
|
||||
|
||||
return IIO_VAL_INT;
|
||||
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = humid_st->scale_pre_decml;
|
||||
*val2 = humid_st->scale_post_decml;
|
||||
|
||||
return humid_st->scale_precision;
|
||||
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
*val = humid_st->value_offset;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
return hid_sensor_read_samp_freq_value(
|
||||
&humid_st->common_attributes, val, val2);
|
||||
|
||||
case IIO_CHAN_INFO_HYSTERESIS:
|
||||
return hid_sensor_read_raw_hyst_value(
|
||||
&humid_st->common_attributes, val, val2);
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int humidity_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
struct hid_humidity_state *humid_st = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
return hid_sensor_write_samp_freq_value(
|
||||
&humid_st->common_attributes, val, val2);
|
||||
|
||||
case IIO_CHAN_INFO_HYSTERESIS:
|
||||
return hid_sensor_write_raw_hyst_value(
|
||||
&humid_st->common_attributes, val, val2);
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct iio_info humidity_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = &humidity_read_raw,
|
||||
.write_raw = &humidity_write_raw,
|
||||
};
|
||||
|
||||
/* Callback handler to send event after all samples are received and captured */
|
||||
static int humidity_proc_event(struct hid_sensor_hub_device *hsdev,
|
||||
unsigned int usage_id, void *pdev)
|
||||
{
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
struct hid_humidity_state *humid_st = iio_priv(indio_dev);
|
||||
|
||||
if (atomic_read(&humid_st->common_attributes.data_ready))
|
||||
iio_push_to_buffers_with_timestamp(indio_dev,
|
||||
&humid_st->humidity_data,
|
||||
iio_get_time_ns(indio_dev));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Capture samples in local storage */
|
||||
static int humidity_capture_sample(struct hid_sensor_hub_device *hsdev,
|
||||
unsigned int usage_id, size_t raw_len,
|
||||
char *raw_data, void *pdev)
|
||||
{
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
struct hid_humidity_state *humid_st = iio_priv(indio_dev);
|
||||
|
||||
switch (usage_id) {
|
||||
case HID_USAGE_SENSOR_ATMOSPHERIC_HUMIDITY:
|
||||
humid_st->humidity_data = *(s32 *)raw_data;
|
||||
|
||||
return 0;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Parse report which is specific to an usage id */
|
||||
static int humidity_parse_report(struct platform_device *pdev,
|
||||
struct hid_sensor_hub_device *hsdev,
|
||||
struct iio_chan_spec *channels,
|
||||
unsigned int usage_id,
|
||||
struct hid_humidity_state *st)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = sensor_hub_input_get_attribute_info(hsdev, HID_INPUT_REPORT,
|
||||
usage_id,
|
||||
HID_USAGE_SENSOR_ATMOSPHERIC_HUMIDITY,
|
||||
&st->humidity_attr);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
humidity_adjust_channel_bit_mask(channels, 0, st->humidity_attr.size);
|
||||
|
||||
st->scale_precision = hid_sensor_format_scale(
|
||||
HID_USAGE_SENSOR_HUMIDITY,
|
||||
&st->humidity_attr,
|
||||
&st->scale_pre_decml,
|
||||
&st->scale_post_decml);
|
||||
|
||||
/* Set Sensitivity field ids, when there is no individual modifier */
|
||||
if (st->common_attributes.sensitivity.index < 0)
|
||||
sensor_hub_input_get_attribute_info(hsdev,
|
||||
HID_FEATURE_REPORT, usage_id,
|
||||
HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS |
|
||||
HID_USAGE_SENSOR_ATMOSPHERIC_HUMIDITY,
|
||||
&st->common_attributes.sensitivity);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct hid_sensor_hub_callbacks humidity_callbacks = {
|
||||
.send_event = &humidity_proc_event,
|
||||
.capture_sample = &humidity_capture_sample,
|
||||
};
|
||||
|
||||
/* Function to initialize the processing for usage id */
|
||||
static int hid_humidity_probe(struct platform_device *pdev)
|
||||
{
|
||||
static const char *name = "humidity";
|
||||
struct iio_dev *indio_dev;
|
||||
struct hid_humidity_state *humid_st;
|
||||
struct iio_chan_spec *humid_chans;
|
||||
struct hid_sensor_hub_device *hsdev = dev_get_platdata(&pdev->dev);
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*humid_st));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
humid_st = iio_priv(indio_dev);
|
||||
humid_st->common_attributes.hsdev = hsdev;
|
||||
humid_st->common_attributes.pdev = pdev;
|
||||
|
||||
ret = hid_sensor_parse_common_attributes(hsdev,
|
||||
HID_USAGE_SENSOR_HUMIDITY,
|
||||
&humid_st->common_attributes);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
humid_chans = devm_kmemdup(&indio_dev->dev, humidity_channels,
|
||||
sizeof(humidity_channels), GFP_KERNEL);
|
||||
if (!humid_chans)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = humidity_parse_report(pdev, hsdev, humid_chans,
|
||||
HID_USAGE_SENSOR_HUMIDITY, humid_st);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
indio_dev->channels = humid_chans;
|
||||
indio_dev->num_channels = ARRAY_SIZE(humidity_channels);
|
||||
indio_dev->dev.parent = &pdev->dev;
|
||||
indio_dev->info = &humidity_info;
|
||||
indio_dev->name = name;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
ret = devm_iio_triggered_buffer_setup(&pdev->dev, indio_dev,
|
||||
&iio_pollfunc_store_time, NULL, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
atomic_set(&humid_st->common_attributes.data_ready, 0);
|
||||
ret = hid_sensor_setup_trigger(indio_dev, name,
|
||||
&humid_st->common_attributes);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
platform_set_drvdata(pdev, indio_dev);
|
||||
|
||||
humidity_callbacks.pdev = pdev;
|
||||
ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_HUMIDITY,
|
||||
&humidity_callbacks);
|
||||
if (ret)
|
||||
goto error_remove_trigger;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto error_remove_callback;
|
||||
|
||||
return ret;
|
||||
|
||||
error_remove_callback:
|
||||
sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_HUMIDITY);
|
||||
error_remove_trigger:
|
||||
hid_sensor_remove_trigger(&humid_st->common_attributes);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Function to deinitialize the processing for usage id */
|
||||
static int hid_humidity_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct hid_sensor_hub_device *hsdev = dev_get_platdata(&pdev->dev);
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
struct hid_humidity_state *humid_st = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_HUMIDITY);
|
||||
hid_sensor_remove_trigger(&humid_st->common_attributes);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct platform_device_id hid_humidity_ids[] = {
|
||||
{
|
||||
/* Format: HID-SENSOR-usage_id_in_hex_lowercase */
|
||||
.name = "HID-SENSOR-200032",
|
||||
},
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, hid_humidity_ids);
|
||||
|
||||
static struct platform_driver hid_humidity_platform_driver = {
|
||||
.id_table = hid_humidity_ids,
|
||||
.driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
.pm = &hid_sensor_pm_ops,
|
||||
},
|
||||
.probe = hid_humidity_probe,
|
||||
.remove = hid_humidity_remove,
|
||||
};
|
||||
module_platform_driver(hid_humidity_platform_driver);
|
||||
|
||||
MODULE_DESCRIPTION("HID Environmental humidity sensor");
|
||||
MODULE_AUTHOR("Song Hongyan <hongyan.song@intel.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -41,7 +41,7 @@ static const struct iio_trigger_ops hts221_trigger_ops = {
|
||||
|
||||
static irqreturn_t hts221_trigger_handler_thread(int irq, void *private)
|
||||
{
|
||||
struct hts221_hw *hw = (struct hts221_hw *)private;
|
||||
struct hts221_hw *hw = private;
|
||||
u8 status;
|
||||
int err;
|
||||
|
||||
|
@ -113,6 +113,12 @@ static const struct inv_mpu6050_hw hw_info[] = {
|
||||
.reg = ®_set_6050,
|
||||
.config = &chip_config_6050,
|
||||
},
|
||||
{
|
||||
.whoami = INV_MPU9250_WHOAMI_VALUE,
|
||||
.name = "MPU9250",
|
||||
.reg = ®_set_6500,
|
||||
.config = &chip_config_6050,
|
||||
},
|
||||
{
|
||||
.whoami = INV_ICM20608_WHOAMI_VALUE,
|
||||
.name = "ICM20608",
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include "inv_mpu_iio.h"
|
||||
|
||||
static const struct regmap_config inv_mpu_regmap_config = {
|
||||
@ -69,7 +70,8 @@ static int inv_mpu6050_deselect_bypass(struct i2c_mux_core *muxc, u32 chan_id)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *inv_mpu_match_acpi_device(struct device *dev, int *chip_id)
|
||||
static const char *inv_mpu_match_acpi_device(struct device *dev,
|
||||
enum inv_devices *chip_id)
|
||||
{
|
||||
const struct acpi_device_id *id;
|
||||
|
||||
@ -93,7 +95,8 @@ static int inv_mpu_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct inv_mpu6050_state *st;
|
||||
int result, chip_type;
|
||||
int result;
|
||||
enum inv_devices chip_type;
|
||||
struct regmap *regmap;
|
||||
const char *name;
|
||||
|
||||
@ -101,8 +104,13 @@ static int inv_mpu_probe(struct i2c_client *client,
|
||||
I2C_FUNC_SMBUS_I2C_BLOCK))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (id) {
|
||||
chip_type = (int)id->driver_data;
|
||||
if (client->dev.of_node) {
|
||||
chip_type = (enum inv_devices)
|
||||
of_device_get_match_data(&client->dev);
|
||||
name = client->name;
|
||||
} else if (id) {
|
||||
chip_type = (enum inv_devices)
|
||||
id->driver_data;
|
||||
name = id->name;
|
||||
} else if (ACPI_HANDLE(&client->dev)) {
|
||||
name = inv_mpu_match_acpi_device(&client->dev, &chip_type);
|
||||
@ -170,12 +178,38 @@ static const struct i2c_device_id inv_mpu_id[] = {
|
||||
{"mpu6050", INV_MPU6050},
|
||||
{"mpu6500", INV_MPU6500},
|
||||
{"mpu9150", INV_MPU9150},
|
||||
{"mpu9250", INV_MPU9250},
|
||||
{"icm20608", INV_ICM20608},
|
||||
{}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(i2c, inv_mpu_id);
|
||||
|
||||
static const struct of_device_id inv_of_match[] = {
|
||||
{
|
||||
.compatible = "invensense,mpu6050",
|
||||
.data = (void *)INV_MPU6050
|
||||
},
|
||||
{
|
||||
.compatible = "invensense,mpu6500",
|
||||
.data = (void *)INV_MPU6500
|
||||
},
|
||||
{
|
||||
.compatible = "invensense,mpu9150",
|
||||
.data = (void *)INV_MPU9150
|
||||
},
|
||||
{
|
||||
.compatible = "invensense,mpu9250",
|
||||
.data = (void *)INV_MPU9250
|
||||
},
|
||||
{
|
||||
.compatible = "invensense,icm20608",
|
||||
.data = (void *)INV_ICM20608
|
||||
},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, inv_of_match);
|
||||
|
||||
static const struct acpi_device_id inv_acpi_match[] = {
|
||||
{"INVN6500", INV_MPU6500},
|
||||
{ },
|
||||
@ -188,6 +222,7 @@ static struct i2c_driver inv_mpu_driver = {
|
||||
.remove = inv_mpu_remove,
|
||||
.id_table = inv_mpu_id,
|
||||
.driver = {
|
||||
.of_match_table = inv_of_match,
|
||||
.acpi_match_table = ACPI_PTR(inv_acpi_match),
|
||||
.name = "inv-mpu6050-i2c",
|
||||
.pm = &inv_mpu_pmops,
|
||||
|
@ -70,6 +70,7 @@ enum inv_devices {
|
||||
INV_MPU6500,
|
||||
INV_MPU6000,
|
||||
INV_MPU9150,
|
||||
INV_MPU9250,
|
||||
INV_ICM20608,
|
||||
INV_NUM_PARTS
|
||||
};
|
||||
@ -226,6 +227,7 @@ struct inv_mpu6050_state {
|
||||
#define INV_MPU6050_WHOAMI_VALUE 0x68
|
||||
#define INV_MPU6500_WHOAMI_VALUE 0x70
|
||||
#define INV_MPU9150_WHOAMI_VALUE 0x68
|
||||
#define INV_MPU9250_WHOAMI_VALUE 0x71
|
||||
#define INV_ICM20608_WHOAMI_VALUE 0xAF
|
||||
|
||||
/* scan element definition */
|
||||
|
@ -82,6 +82,7 @@ static const struct spi_device_id inv_mpu_id[] = {
|
||||
{"mpu6000", INV_MPU6000},
|
||||
{"mpu6500", INV_MPU6500},
|
||||
{"mpu9150", INV_MPU9150},
|
||||
{"mpu9250", INV_MPU9250},
|
||||
{"icm20608", INV_ICM20608},
|
||||
{}
|
||||
};
|
||||
|
@ -8,7 +8,7 @@ config IIO_ST_LSM6DSX
|
||||
select IIO_ST_LSM6DSX_SPI if (SPI_MASTER)
|
||||
help
|
||||
Say yes here to build support for STMicroelectronics LSM6DSx imu
|
||||
sensor. Supported devices: lsm6ds3, lsm6dsm
|
||||
sensor. Supported devices: lsm6ds3, lsm6ds3h, lsm6dsl, lsm6dsm
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called st_lsm6dsx.
|
||||
|
@ -15,11 +15,16 @@
|
||||
#include <linux/device.h>
|
||||
|
||||
#define ST_LSM6DS3_DEV_NAME "lsm6ds3"
|
||||
#define ST_LSM6DS3H_DEV_NAME "lsm6ds3h"
|
||||
#define ST_LSM6DSL_DEV_NAME "lsm6dsl"
|
||||
#define ST_LSM6DSM_DEV_NAME "lsm6dsm"
|
||||
|
||||
enum st_lsm6dsx_hw_id {
|
||||
ST_LSM6DS3_ID,
|
||||
ST_LSM6DS3H_ID,
|
||||
ST_LSM6DSL_ID,
|
||||
ST_LSM6DSM_ID,
|
||||
ST_LSM6DSX_MAX_ID,
|
||||
};
|
||||
|
||||
#define ST_LSM6DSX_CHAN_SIZE 2
|
||||
@ -50,7 +55,7 @@ struct st_lsm6dsx_reg {
|
||||
struct st_lsm6dsx_settings {
|
||||
u8 wai;
|
||||
u16 max_fifo_size;
|
||||
enum st_lsm6dsx_hw_id id;
|
||||
enum st_lsm6dsx_hw_id id[ST_LSM6DSX_MAX_ID];
|
||||
};
|
||||
|
||||
enum st_lsm6dsx_sensor_id {
|
||||
@ -66,6 +71,7 @@ enum st_lsm6dsx_fifo_mode {
|
||||
|
||||
/**
|
||||
* struct st_lsm6dsx_sensor - ST IMU sensor instance
|
||||
* @name: Sensor name.
|
||||
* @id: Sensor identifier.
|
||||
* @hw: Pointer to instance of struct st_lsm6dsx_hw.
|
||||
* @gain: Configured sensor sensitivity.
|
||||
@ -78,6 +84,7 @@ enum st_lsm6dsx_fifo_mode {
|
||||
* @ts: Latest timestamp from the interrupt handler.
|
||||
*/
|
||||
struct st_lsm6dsx_sensor {
|
||||
char name[32];
|
||||
enum st_lsm6dsx_sensor_id id;
|
||||
struct st_lsm6dsx_hw *hw;
|
||||
|
||||
@ -128,7 +135,7 @@ struct st_lsm6dsx_hw {
|
||||
#endif /* CONFIG_SPI_MASTER */
|
||||
};
|
||||
|
||||
int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id,
|
||||
int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, const char *name,
|
||||
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);
|
||||
|
@ -1,9 +1,10 @@
|
||||
/*
|
||||
* 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):
|
||||
* LSM6DS3/LSM6DS3H/LSM6DSL/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
|
||||
@ -206,7 +207,7 @@ int st_lsm6dsx_update_watermark(struct st_lsm6dsx_sensor *sensor, u16 watermark)
|
||||
}
|
||||
|
||||
/**
|
||||
* st_lsm6dsx_read_fifo() - LSM6DS3-LSM6DSM read FIFO routine
|
||||
* st_lsm6dsx_read_fifo() - LSM6DS3-LSM6DS3H-LSM6DSL-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.
|
||||
@ -363,7 +364,7 @@ static int st_lsm6dsx_update_fifo(struct iio_dev *iio_dev, bool enable)
|
||||
|
||||
static irqreturn_t st_lsm6dsx_handler_irq(int irq, void *private)
|
||||
{
|
||||
struct st_lsm6dsx_hw *hw = (struct st_lsm6dsx_hw *)private;
|
||||
struct st_lsm6dsx_hw *hw = private;
|
||||
struct st_lsm6dsx_sensor *sensor;
|
||||
int i;
|
||||
|
||||
@ -387,7 +388,7 @@ static irqreturn_t st_lsm6dsx_handler_irq(int irq, void *private)
|
||||
|
||||
static irqreturn_t st_lsm6dsx_handler_thread(int irq, void *private)
|
||||
{
|
||||
struct st_lsm6dsx_hw *hw = (struct st_lsm6dsx_hw *)private;
|
||||
struct st_lsm6dsx_hw *hw = private;
|
||||
int count;
|
||||
|
||||
mutex_lock(&hw->fifo_lock);
|
||||
|
@ -17,7 +17,7 @@
|
||||
* - Gyroscope supported full-scale [dps]: +-125/+-245/+-500/+-1000/+-2000
|
||||
* - FIFO size: 8KB
|
||||
*
|
||||
* - LSM6DSM:
|
||||
* - LSM6DS3H/LSM6DSL/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
|
||||
@ -74,12 +74,6 @@
|
||||
#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)
|
||||
@ -164,14 +158,26 @@ static const struct st_lsm6dsx_fs_table_entry st_lsm6dsx_fs_table[] = {
|
||||
|
||||
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 = 0x69,
|
||||
.max_fifo_size = 8192,
|
||||
.id = {
|
||||
[0] = ST_LSM6DS3_ID,
|
||||
},
|
||||
},
|
||||
{
|
||||
.wai = ST_LSM6DSM_WHOAMI,
|
||||
.max_fifo_size = ST_LSM6DSM_MAX_FIFO_SIZE,
|
||||
.id = ST_LSM6DSM_ID,
|
||||
.wai = 0x69,
|
||||
.max_fifo_size = 4096,
|
||||
.id = {
|
||||
[0] = ST_LSM6DS3H_ID,
|
||||
},
|
||||
},
|
||||
{
|
||||
.wai = 0x6a,
|
||||
.max_fifo_size = 4096,
|
||||
.id = {
|
||||
[0] = ST_LSM6DSL_ID,
|
||||
[1] = ST_LSM6DSM_ID,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@ -241,11 +247,15 @@ int st_lsm6dsx_write_with_mask(struct st_lsm6dsx_hw *hw, u8 addr, u8 mask,
|
||||
|
||||
static int st_lsm6dsx_check_whoami(struct st_lsm6dsx_hw *hw, int id)
|
||||
{
|
||||
int err, i;
|
||||
int err, i, j;
|
||||
u8 data;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(st_lsm6dsx_sensor_settings); i++) {
|
||||
if (id == st_lsm6dsx_sensor_settings[i].id)
|
||||
for (j = 0; j < ST_LSM6DSX_MAX_ID; j++) {
|
||||
if (id == st_lsm6dsx_sensor_settings[i].id[j])
|
||||
break;
|
||||
}
|
||||
if (j < ST_LSM6DSX_MAX_ID)
|
||||
break;
|
||||
}
|
||||
|
||||
@ -298,32 +308,40 @@ static int st_lsm6dsx_set_full_scale(struct st_lsm6dsx_sensor *sensor,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int st_lsm6dsx_set_odr(struct st_lsm6dsx_sensor *sensor, u16 odr)
|
||||
static int st_lsm6dsx_check_odr(struct st_lsm6dsx_sensor *sensor, u16 odr,
|
||||
u8 *val)
|
||||
{
|
||||
enum st_lsm6dsx_sensor_id id = sensor->id;
|
||||
int i, err;
|
||||
u8 val;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ST_LSM6DSX_ODR_LIST_SIZE; i++)
|
||||
if (st_lsm6dsx_odr_table[id].odr_avl[i].hz == odr)
|
||||
if (st_lsm6dsx_odr_table[sensor->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;
|
||||
|
||||
*val = st_lsm6dsx_odr_table[sensor->id].odr_avl[i].val;
|
||||
sensor->odr = odr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int st_lsm6dsx_set_odr(struct st_lsm6dsx_sensor *sensor, u16 odr)
|
||||
{
|
||||
enum st_lsm6dsx_sensor_id id = sensor->id;
|
||||
int err;
|
||||
u8 val;
|
||||
|
||||
err = st_lsm6dsx_check_odr(sensor, odr, &val);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return st_lsm6dsx_write_with_mask(sensor->hw,
|
||||
st_lsm6dsx_odr_table[id].reg.addr,
|
||||
st_lsm6dsx_odr_table[id].reg.mask,
|
||||
val);
|
||||
}
|
||||
|
||||
int st_lsm6dsx_sensor_enable(struct st_lsm6dsx_sensor *sensor)
|
||||
{
|
||||
int err;
|
||||
@ -426,9 +444,12 @@ static int st_lsm6dsx_write_raw(struct iio_dev *iio_dev,
|
||||
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);
|
||||
case IIO_CHAN_INFO_SAMP_FREQ: {
|
||||
u8 data;
|
||||
|
||||
err = st_lsm6dsx_check_odr(sensor, val, &data);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
err = -EINVAL;
|
||||
break;
|
||||
@ -538,19 +559,11 @@ 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;
|
||||
return of_property_read_u32(np, "st,drdy-int-pin", drdy_pin);
|
||||
}
|
||||
|
||||
static int st_lsm6dsx_get_drdy_reg(struct st_lsm6dsx_hw *hw, u8 *drdy_reg)
|
||||
@ -621,7 +634,8 @@ static int st_lsm6dsx_init_device(struct st_lsm6dsx_hw *hw)
|
||||
}
|
||||
|
||||
static struct iio_dev *st_lsm6dsx_alloc_iiodev(struct st_lsm6dsx_hw *hw,
|
||||
enum st_lsm6dsx_sensor_id id)
|
||||
enum st_lsm6dsx_sensor_id id,
|
||||
const char *name)
|
||||
{
|
||||
struct st_lsm6dsx_sensor *sensor;
|
||||
struct iio_dev *iio_dev;
|
||||
@ -645,27 +659,30 @@ static struct iio_dev *st_lsm6dsx_alloc_iiodev(struct st_lsm6dsx_hw *hw,
|
||||
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;
|
||||
scnprintf(sensor->name, sizeof(sensor->name), "%s_accel",
|
||||
name);
|
||||
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;
|
||||
scnprintf(sensor->name, sizeof(sensor->name), "%s_gyro",
|
||||
name);
|
||||
break;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
iio_dev->name = sensor->name;
|
||||
|
||||
return iio_dev;
|
||||
}
|
||||
|
||||
int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id,
|
||||
int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, const char *name,
|
||||
const struct st_lsm6dsx_transfer_function *tf_ops)
|
||||
{
|
||||
struct st_lsm6dsx_hw *hw;
|
||||
@ -689,7 +706,7 @@ int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id,
|
||||
return err;
|
||||
|
||||
for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
|
||||
hw->iio_devs[i] = st_lsm6dsx_alloc_iiodev(hw, i);
|
||||
hw->iio_devs[i] = st_lsm6dsx_alloc_iiodev(hw, i, name);
|
||||
if (!hw->iio_devs[i])
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user