Fourth set of IIO new device support, features and cleanups for the 4.12 cycle
New device support * max1117, 1118 and 1119 - new ADC driver * max9611 - new ADC driver * pm8xxx hk/xoadc - new driver with some shared features broken out from the SPMI vadc. * sun4i-gpadc - A33 thermal sensor support (with associated rework) * stm32-dac - new driver and bindings * stm32 trigger - enable support of quadrature encoder device and counter modes Features * apds9960 - use the runtime pm for normal suspend * stm32-adc - add opition to sest resolution via devicetree * xoadc - augment DT bindings to deal with some weird mux cases Cleanups * ad5933 - protect direct mode using claim and release helpers * ade7759 - S_IRUGO and friends to octal in two goes * adis16203 - drop unnecessary brackets * hid-sensor - fix unbalanced pm_runtieme_enable error when probing after remove * lsm6dsx - use actual part numbers for device name when known - simplify data read pin parsing * mpu3050 - avoid double reporting errors -----BEGIN PGP SIGNATURE----- iQJFBAABCAAvFiEEbilms4eEBlKRJoGxVIU0mcT0FogFAljxPf4RHGppYzIzQGtl cm5lbC5vcmcACgkQVIU0mcT0FojZfA//UUaFcaEiMJjoUwBQlOM49nueGcAlOOOz blMYe6JIIm3DYjEJnNhlA1Wn83JVitGQopQ4Tt9yrsxc3urt6GiAiRCK1Lwuyauw l1IrdsV9tzbPrauL8UxZYygIYHZx6ltKnpbSHCrXaA2BOTIYzgIBDGmRv+MPutwW iaW+dKJVW5G/5VV0aIaJc2w5wXujFcWkZoiTJgZBHhGJJ5AY6zyA3tBEOIY7K3KO z4EIY6V9pvVaSgvuWGVWQjtYwjqSSqp5J5Ih7ATpp4hPwi+XIudWqaiq1y2rHQKM Y/wqjz+W/NyAJGSFEnub7gE4wGauQtxMiOLTenrfDWkFxF0YRkunylY8Kwcgvmhh +TdHhazlfIHca5z/MJ0dg/4UPj75o2CtB+N1NyNMjx/K7Juu2MmcKNShLa/l68jT rYEuNQUyhTvNuhlYPTcYmGbL7VbyXJzgbVbk9/Dd1hcS+p/oqn18ulGsWxcj5kEI ME+vHOakq02GTAQEG2FLvE604fTt/dHyXdYFsDY0RCm9ZSURvRqFLYpkxyrQFe+P yhC9b6ouW+dPGez0RmwPZTWnH0E6eN5b13yR3ILewj/NUVA6cjlm0inhnck1IwFe NWVQiMz1a2DsXfk5VjBQVlsipHxW9rs8SArdWnndDpbSErQJT/KrCxantmkNciYw oXf1eFCo/MQ= =zHqs -----END PGP SIGNATURE----- Merge tag 'iio-for-4.12d' of git://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio into staging-next Jonathan writes: Fourth set of IIO new device support, features and cleanups for the 4.12 cycle New device support * max1117, 1118 and 1119 - new ADC driver * max9611 - new ADC driver * pm8xxx hk/xoadc - new driver with some shared features broken out from the SPMI vadc. * sun4i-gpadc - A33 thermal sensor support (with associated rework) * stm32-dac - new driver and bindings * stm32 trigger - enable support of quadrature encoder device and counter modes Features * apds9960 - use the runtime pm for normal suspend * stm32-adc - add opition to sest resolution via devicetree * xoadc - augment DT bindings to deal with some weird mux cases Cleanups * ad5933 - protect direct mode using claim and release helpers * ade7759 - S_IRUGO and friends to octal in two goes * adis16203 - drop unnecessary brackets * hid-sensor - fix unbalanced pm_runtieme_enable error when probing after remove * lsm6dsx - use actual part numbers for device name when known - simplify data read pin parsing * mpu3050 - avoid double reporting errors
This commit is contained in:
Коммит
d47e538235
|
@ -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.
|
|
@ -3,11 +3,15 @@ KernelVersion: 4.11
|
||||||
Contact: benjamin.gaignard@st.com
|
Contact: benjamin.gaignard@st.com
|
||||||
Description:
|
Description:
|
||||||
Reading returns the list possible master modes which are:
|
Reading returns the list possible master modes which are:
|
||||||
- "reset" : The UG bit from the TIMx_EGR register is used as trigger output (TRGO).
|
- "reset" : The UG bit from the TIMx_EGR register is
|
||||||
- "enable" : The Counter Enable signal CNT_EN is used as trigger output.
|
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.
|
- "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.
|
For instance a master timer can then be used
|
||||||
- "compare_pulse" : The trigger output send a positive pulse when the CC1IF flag is to be set.
|
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.
|
- "OC1REF" : OC1REF signal is used as trigger output.
|
||||||
- "OC2REF" : OC2REF signal is used as trigger output.
|
- "OC2REF" : OC2REF signal is used as trigger output.
|
||||||
- "OC3REF" : OC3REF 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.
|
Reading returns the current sampling frequency.
|
||||||
Writing an value different of 0 set and start sampling.
|
Writing an value different of 0 set and start sampling.
|
||||||
Writing 0 stop 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.
|
||||||
|
|
|
@ -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>;
|
||||||
|
};
|
|
@ -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.
|
with PMIC variant but is typically something like 2.2 or 1.8V.
|
||||||
|
|
||||||
The following required properties are standard for IO channels, see
|
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>
|
- #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
|
- interrupts: should refer to the parent PMIC interrupt controller
|
||||||
and reference the proper ADC interrupt.
|
and reference the proper ADC interrupt.
|
||||||
|
|
||||||
Required subnodes:
|
Required subnodes:
|
||||||
|
|
||||||
The ADC channels are configured as subnodes of the ADC. Since some of
|
The ADC channels are configured as subnodes of the ADC.
|
||||||
them are used for calibrating the ADC, these nodes are compulsory:
|
|
||||||
|
Since some of them are used for calibrating the ADC, these nodes are
|
||||||
|
compulsory:
|
||||||
|
|
||||||
adc-channel@c {
|
adc-channel@c {
|
||||||
reg = <0x0c>;
|
reg = <0x00 0x0c>;
|
||||||
};
|
};
|
||||||
|
|
||||||
adc-channel@d {
|
adc-channel@d {
|
||||||
reg = <0x0d>;
|
reg = <0x00 0x0d>;
|
||||||
};
|
};
|
||||||
|
|
||||||
adc-channel@f {
|
adc-channel@f {
|
||||||
reg = <0x0f>;
|
reg = <0x00 0x0f>;
|
||||||
};
|
};
|
||||||
|
|
||||||
These three nodes are used for absolute and ratiometric calibration
|
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
|
1:1 ratio converters that sample 625, 1250 and 0 milliV and create
|
||||||
an interpolation calibration for all other ADCs.
|
an interpolation calibration for all other ADCs.
|
||||||
|
|
||||||
Optional subnodes: any channels other than channel 0x0c, 0x0d and
|
Optional subnodes: any channels other than channels [0x00 0x0c],
|
||||||
0x0f are optional.
|
[0x00 0x0d] and [0x00 0x0f] are optional.
|
||||||
|
|
||||||
Required channel node properties:
|
Required channel node properties:
|
||||||
|
|
||||||
- reg: should contain the hardware channel number in the range
|
- 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:
|
Optional channel node properties:
|
||||||
|
|
||||||
|
@ -94,56 +104,54 @@ Example:
|
||||||
xoadc: xoadc@197 {
|
xoadc: xoadc@197 {
|
||||||
compatible = "qcom,pm8058-adc";
|
compatible = "qcom,pm8058-adc";
|
||||||
reg = <0x197>;
|
reg = <0x197>;
|
||||||
interrupt-parent = <&pm8058>;
|
interrupts-extended = <&pm8058 76 IRQ_TYPE_EDGE_RISING>;
|
||||||
interrupts = <76 1>;
|
#address-cells = <2>;
|
||||||
#address-cells = <1>;
|
|
||||||
#size-cells = <0>;
|
#size-cells = <0>;
|
||||||
#io-channel-cells = <1>;
|
#io-channel-cells = <2>;
|
||||||
|
|
||||||
vcoin: adc-channel@0 {
|
vcoin: adc-channel@0 {
|
||||||
reg = <0x00>;
|
reg = <0x00 0x00>;
|
||||||
};
|
};
|
||||||
vbat: adc-channel@1 {
|
vbat: adc-channel@1 {
|
||||||
reg = <0x01>;
|
reg = <0x00 0x01>;
|
||||||
};
|
};
|
||||||
dcin: adc-channel@2 {
|
dcin: adc-channel@2 {
|
||||||
reg = <0x02>;
|
reg = <0x00 0x02>;
|
||||||
};
|
};
|
||||||
ichg: adc-channel@3 {
|
ichg: adc-channel@3 {
|
||||||
reg = <0x03>;
|
reg = <0x00 0x03>;
|
||||||
};
|
};
|
||||||
vph_pwr: adc-channel@4 {
|
vph_pwr: adc-channel@4 {
|
||||||
reg = <0x04>;
|
reg = <0x00 0x04>;
|
||||||
};
|
};
|
||||||
usb_vbus: adc-channel@a {
|
usb_vbus: adc-channel@a {
|
||||||
reg = <0x0a>;
|
reg = <0x00 0x0a>;
|
||||||
};
|
};
|
||||||
die_temp: adc-channel@b {
|
die_temp: adc-channel@b {
|
||||||
reg = <0x0b>;
|
reg = <0x00 0x0b>;
|
||||||
};
|
};
|
||||||
ref_625mv: adc-channel@c {
|
ref_625mv: adc-channel@c {
|
||||||
reg = <0x0c>;
|
reg = <0x00 0x0c>;
|
||||||
};
|
};
|
||||||
ref_1250mv: adc-channel@d {
|
ref_1250mv: adc-channel@d {
|
||||||
reg = <0x0d>;
|
reg = <0x00 0x0d>;
|
||||||
};
|
};
|
||||||
ref_325mv: adc-channel@e {
|
ref_325mv: adc-channel@e {
|
||||||
reg = <0x0e>;
|
reg = <0x00 0x0e>;
|
||||||
};
|
};
|
||||||
ref_muxoff: adc-channel@f {
|
ref_muxoff: adc-channel@f {
|
||||||
reg = <0x0f>;
|
reg = <0x00 0x0f>;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/* IIO client node */
|
/* IIO client node */
|
||||||
iio-hwmon {
|
iio-hwmon {
|
||||||
compatible = "iio-hwmon";
|
compatible = "iio-hwmon";
|
||||||
io-channels = <&xoadc 0x01>, /* Battery */
|
io-channels = <&xoadc 0x00 0x01>, /* Battery */
|
||||||
<&xoadc 0x02>, /* DC in (charger) */
|
<&xoadc 0x00 0x02>, /* DC in (charger) */
|
||||||
<&xoadc 0x04>, /* VPH the main system voltage */
|
<&xoadc 0x00 0x04>, /* VPH the main system voltage */
|
||||||
<&xoadc 0x0b>, /* Die temperature */
|
<&xoadc 0x00 0x0b>, /* Die temperature */
|
||||||
<&xoadc 0x0c>, /* Reference voltage 1.25V */
|
<&xoadc 0x00 0x0c>, /* Reference voltage 1.25V */
|
||||||
<&xoadc 0x0d>, /* Reference voltage 0.625V */
|
<&xoadc 0x00 0x0d>, /* Reference voltage 0.625V */
|
||||||
<&xoadc 0x0e>; /* Reference voltage 0.325V */
|
<&xoadc 0x00 0x0e>; /* Reference voltage 0.325V */
|
||||||
};
|
};
|
||||||
|
|
|
@ -57,6 +57,9 @@ Optional properties:
|
||||||
- dmas: Phandle to dma channel for this ADC instance.
|
- dmas: Phandle to dma channel for this ADC instance.
|
||||||
See ../../dma/dma.txt for details.
|
See ../../dma/dma.txt for details.
|
||||||
- dma-names: Must be "rx" when dmas property is being used.
|
- 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:
|
Example:
|
||||||
adc: adc@40012000 {
|
adc: adc@40012000 {
|
||||||
|
@ -84,6 +87,7 @@ Example:
|
||||||
st,adc-channels = <8>;
|
st,adc-channels = <8>;
|
||||||
dmas = <&dma2 0 0 0x400 0x0>;
|
dmas = <&dma2 0 0 0x400 0x0>;
|
||||||
dma-names = "rx";
|
dma-names = "rx";
|
||||||
|
assigned-resolution-bits = <8>;
|
||||||
};
|
};
|
||||||
...
|
...
|
||||||
other adc child nodes follow...
|
other adc child nodes follow...
|
||||||
|
|
|
@ -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>;
|
||||||
|
};
|
||||||
|
};
|
|
@ -379,6 +379,18 @@ config MAX11100
|
||||||
To compile this driver as a module, choose M here: the module will be
|
To compile this driver as a module, choose M here: the module will be
|
||||||
called max11100.
|
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
|
config MAX1363
|
||||||
tristate "Maxim max1363 ADC driver"
|
tristate "Maxim max1363 ADC driver"
|
||||||
depends on I2C
|
depends on I2C
|
||||||
|
@ -398,6 +410,16 @@ config MAX1363
|
||||||
To compile this driver as a module, choose M here: the module will be
|
To compile this driver as a module, choose M here: the module will be
|
||||||
called max1363.
|
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
|
config MCP320X
|
||||||
tristate "Microchip Technology MCP3x01/02/04/08"
|
tristate "Microchip Technology MCP3x01/02/04/08"
|
||||||
depends on SPI
|
depends on SPI
|
||||||
|
@ -486,6 +508,20 @@ config PALMAS_GPADC
|
||||||
is used in smartphones and tablets and supports a 16 channel
|
is used in smartphones and tablets and supports a 16 channel
|
||||||
general purpose ADC.
|
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
|
config QCOM_SPMI_IADC
|
||||||
tristate "Qualcomm SPMI PMIC current ADC"
|
tristate "Qualcomm SPMI PMIC current ADC"
|
||||||
depends on SPMI
|
depends on SPMI
|
||||||
|
@ -504,6 +540,7 @@ config QCOM_SPMI_VADC
|
||||||
tristate "Qualcomm SPMI PMIC voltage ADC"
|
tristate "Qualcomm SPMI PMIC voltage ADC"
|
||||||
depends on SPMI
|
depends on SPMI
|
||||||
select REGMAP_SPMI
|
select REGMAP_SPMI
|
||||||
|
select QCOM_VADC_COMMON
|
||||||
help
|
help
|
||||||
This is the IIO Voltage ADC driver for Qualcomm QPNP VADC Chip.
|
This is the IIO Voltage ADC driver for Qualcomm QPNP VADC Chip.
|
||||||
|
|
||||||
|
@ -594,7 +631,7 @@ config STX104
|
||||||
config SUN4I_GPADC
|
config SUN4I_GPADC
|
||||||
tristate "Support for the Allwinner SoCs GPADC"
|
tristate "Support for the Allwinner SoCs GPADC"
|
||||||
depends on IIO
|
depends on IIO
|
||||||
depends on MFD_SUN4I_GPADC
|
depends on MFD_SUN4I_GPADC || MACH_SUN8I
|
||||||
depends on THERMAL || !THERMAL_OF
|
depends on THERMAL || !THERMAL_OF
|
||||||
help
|
help
|
||||||
Say yes here to build support for Allwinner (A10, A13 and A31) SoCs
|
Say yes here to build support for Allwinner (A10, A13 and A31) SoCs
|
||||||
|
|
|
@ -37,7 +37,9 @@ obj-$(CONFIG_LTC2485) += ltc2485.o
|
||||||
obj-$(CONFIG_LTC2497) += ltc2497.o
|
obj-$(CONFIG_LTC2497) += ltc2497.o
|
||||||
obj-$(CONFIG_MAX1027) += max1027.o
|
obj-$(CONFIG_MAX1027) += max1027.o
|
||||||
obj-$(CONFIG_MAX11100) += max11100.o
|
obj-$(CONFIG_MAX11100) += max11100.o
|
||||||
|
obj-$(CONFIG_MAX1118) += max1118.o
|
||||||
obj-$(CONFIG_MAX1363) += max1363.o
|
obj-$(CONFIG_MAX1363) += max1363.o
|
||||||
|
obj-$(CONFIG_MAX9611) += max9611.o
|
||||||
obj-$(CONFIG_MCP320X) += mcp320x.o
|
obj-$(CONFIG_MCP320X) += mcp320x.o
|
||||||
obj-$(CONFIG_MCP3422) += mcp3422.o
|
obj-$(CONFIG_MCP3422) += mcp3422.o
|
||||||
obj-$(CONFIG_MEDIATEK_MT6577_AUXADC) += mt6577_auxadc.o
|
obj-$(CONFIG_MEDIATEK_MT6577_AUXADC) += mt6577_auxadc.o
|
||||||
|
@ -47,7 +49,9 @@ obj-$(CONFIG_MXS_LRADC) += mxs-lradc.o
|
||||||
obj-$(CONFIG_NAU7802) += nau7802.o
|
obj-$(CONFIG_NAU7802) += nau7802.o
|
||||||
obj-$(CONFIG_PALMAS_GPADC) += palmas_gpadc.o
|
obj-$(CONFIG_PALMAS_GPADC) += palmas_gpadc.o
|
||||||
obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.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_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_RCAR_GYRO_ADC) += rcar-gyroadc.o
|
||||||
obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
|
obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
|
||||||
obj-$(CONFIG_SPEAR_ADC) += spear_adc.o
|
obj-$(CONFIG_SPEAR_ADC) += spear_adc.o
|
||||||
|
|
|
@ -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");
|
|
@ -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");
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -28,6 +28,8 @@
|
||||||
|
|
||||||
#include <dt-bindings/iio/qcom,spmi-vadc.h>
|
#include <dt-bindings/iio/qcom,spmi-vadc.h>
|
||||||
|
|
||||||
|
#include "qcom-vadc-common.h"
|
||||||
|
|
||||||
/* VADC register and bit definitions */
|
/* VADC register and bit definitions */
|
||||||
#define VADC_REVISION2 0x1
|
#define VADC_REVISION2 0x1
|
||||||
#define VADC_REVISION2_SUPPORTED_VADC 1
|
#define VADC_REVISION2_SUPPORTED_VADC 1
|
||||||
|
@ -75,83 +77,9 @@
|
||||||
|
|
||||||
#define VADC_DATA 0x60 /* 16 bits */
|
#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_MIN VADC_USBIN
|
||||||
#define VADC_CHAN_MAX VADC_LR_MUX3_BUF_PU1_PU2_XO_THERM
|
#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.
|
* struct vadc_channel_prop - VADC channel property.
|
||||||
* @channel: channel number, refer to the channel list.
|
* @channel: channel number, refer to the channel list.
|
||||||
|
@ -162,9 +90,8 @@ struct vadc_prescale_ratio {
|
||||||
* start of conversion.
|
* start of conversion.
|
||||||
* @avg_samples: ability to provide single result from the ADC
|
* @avg_samples: ability to provide single result from the ADC
|
||||||
* that is an average of multiple measurements.
|
* 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.
|
* physical units desired by the client for the channel.
|
||||||
* Referenced from enum vadc_scale_fn_type.
|
|
||||||
*/
|
*/
|
||||||
struct vadc_channel_prop {
|
struct vadc_channel_prop {
|
||||||
unsigned int channel;
|
unsigned int channel;
|
||||||
|
@ -173,7 +100,7 @@ struct vadc_channel_prop {
|
||||||
unsigned int prescale;
|
unsigned int prescale;
|
||||||
unsigned int hw_settle_time;
|
unsigned int hw_settle_time;
|
||||||
unsigned int avg_samples;
|
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 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[] = {
|
static const struct vadc_prescale_ratio vadc_prescale_ratios[] = {
|
||||||
{.num = 1, .den = 1},
|
{.num = 1, .den = 1},
|
||||||
{.num = 1, .den = 3},
|
{.num = 1, .den = 3},
|
||||||
|
@ -244,44 +142,6 @@ static const struct vadc_prescale_ratio vadc_prescale_ratios[] = {
|
||||||
{.num = 1, .den = 10}
|
{.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)
|
static int vadc_read(struct vadc_priv *vadc, u16 offset, u8 *data)
|
||||||
{
|
{
|
||||||
return regmap_bulk_read(vadc->regmap, vadc->base + offset, data, 1);
|
return regmap_bulk_read(vadc->regmap, vadc->base + offset, data, 1);
|
||||||
|
@ -553,159 +413,6 @@ err:
|
||||||
return ret;
|
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)
|
static int vadc_prescaling_from_dt(u32 num, u32 den)
|
||||||
{
|
{
|
||||||
unsigned int pre;
|
unsigned int pre;
|
||||||
|
@ -742,14 +449,6 @@ static int vadc_avg_samples_from_dt(u32 value)
|
||||||
return __ffs64(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,
|
static int vadc_read_raw(struct iio_dev *indio_dev,
|
||||||
struct iio_chan_spec const *chan, int *val, int *val2,
|
struct iio_chan_spec const *chan, int *val, int *val2,
|
||||||
long mask)
|
long mask)
|
||||||
|
@ -766,7 +465,13 @@ static int vadc_read_raw(struct iio_dev *indio_dev,
|
||||||
if (ret)
|
if (ret)
|
||||||
break;
|
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;
|
return IIO_VAL_INT;
|
||||||
case IIO_CHAN_INFO_RAW:
|
case IIO_CHAN_INFO_RAW:
|
||||||
|
@ -809,7 +514,7 @@ struct vadc_channels {
|
||||||
unsigned int prescale_index;
|
unsigned int prescale_index;
|
||||||
enum iio_chan_type type;
|
enum iio_chan_type type;
|
||||||
long info_mask;
|
long info_mask;
|
||||||
unsigned int scale_fn;
|
enum vadc_scale_fn_type scale_fn_type;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define VADC_CHAN(_dname, _type, _mask, _pre, _scale) \
|
#define VADC_CHAN(_dname, _type, _mask, _pre, _scale) \
|
||||||
|
@ -818,7 +523,7 @@ struct vadc_channels {
|
||||||
.prescale_index = _pre, \
|
.prescale_index = _pre, \
|
||||||
.type = _type, \
|
.type = _type, \
|
||||||
.info_mask = _mask, \
|
.info_mask = _mask, \
|
||||||
.scale_fn = _scale \
|
.scale_fn_type = _scale \
|
||||||
}, \
|
}, \
|
||||||
|
|
||||||
#define VADC_NO_CHAN(_dname, _type, _mask, _pre) \
|
#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);
|
ret = of_property_read_u32(node, "qcom,decimation", &value);
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
ret = vadc_decimation_from_dt(value);
|
ret = qcom_vadc_decimation_from_dt(value);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(dev, "%02x invalid decimation %d\n",
|
dev_err(dev, "%02x invalid decimation %d\n",
|
||||||
chan, value);
|
chan, value);
|
||||||
|
@ -1068,7 +773,7 @@ static int vadc_get_dt_data(struct vadc_priv *vadc, struct device_node *node)
|
||||||
return ret;
|
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_props[index] = prop;
|
||||||
|
|
||||||
vadc_chan = &vadc_chans[prop.channel];
|
vadc_chan = &vadc_chans[prop.channel];
|
||||||
|
|
|
@ -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);
|
|
@ -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 */
|
|
@ -60,6 +60,8 @@
|
||||||
#define STM32F4_EOC BIT(1)
|
#define STM32F4_EOC BIT(1)
|
||||||
|
|
||||||
/* STM32F4_ADC_CR1 - bit fields */
|
/* STM32F4_ADC_CR1 - bit fields */
|
||||||
|
#define STM32F4_RES_SHIFT 24
|
||||||
|
#define STM32F4_RES_MASK GENMASK(25, 24)
|
||||||
#define STM32F4_SCAN BIT(8)
|
#define STM32F4_SCAN BIT(8)
|
||||||
#define STM32F4_EOCIE BIT(5)
|
#define STM32F4_EOCIE BIT(5)
|
||||||
|
|
||||||
|
@ -141,6 +143,7 @@ struct stm32_adc_regs {
|
||||||
* @lock: spinlock
|
* @lock: spinlock
|
||||||
* @bufi: data buffer index
|
* @bufi: data buffer index
|
||||||
* @num_conv: expected number of scan conversions
|
* @num_conv: expected number of scan conversions
|
||||||
|
* @res: data resolution (e.g. RES bitfield value)
|
||||||
* @trigger_polarity: external trigger polarity (e.g. exten)
|
* @trigger_polarity: external trigger polarity (e.g. exten)
|
||||||
* @dma_chan: dma channel
|
* @dma_chan: dma channel
|
||||||
* @rx_buf: dma rx buffer cpu address
|
* @rx_buf: dma rx buffer cpu address
|
||||||
|
@ -157,6 +160,7 @@ struct stm32_adc {
|
||||||
spinlock_t lock; /* interrupt lock */
|
spinlock_t lock; /* interrupt lock */
|
||||||
unsigned int bufi;
|
unsigned int bufi;
|
||||||
unsigned int num_conv;
|
unsigned int num_conv;
|
||||||
|
u32 res;
|
||||||
u32 trigger_polarity;
|
u32 trigger_polarity;
|
||||||
struct dma_chan *dma_chan;
|
struct dma_chan *dma_chan;
|
||||||
u8 *rx_buf;
|
u8 *rx_buf;
|
||||||
|
@ -196,6 +200,11 @@ static const struct stm32_adc_chan_spec stm32f4_adc123_channels[] = {
|
||||||
{ IIO_VOLTAGE, 15, "in15" },
|
{ 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
|
* stm32f4_sq - describe regular sequence registers
|
||||||
* - L: sequence len (register & bit field)
|
* - 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);
|
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.
|
* stm32_adc_start_conv() - Start conversions for regular channels.
|
||||||
* @adc: stm32 adc instance
|
* @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,
|
static void stm32_adc_chan_init_one(struct iio_dev *indio_dev,
|
||||||
struct iio_chan_spec *chan,
|
struct iio_chan_spec *chan,
|
||||||
const struct stm32_adc_chan_spec *channel,
|
const struct stm32_adc_chan_spec *channel,
|
||||||
int scan_index)
|
int scan_index)
|
||||||
{
|
{
|
||||||
|
struct stm32_adc *adc = iio_priv(indio_dev);
|
||||||
|
|
||||||
chan->type = channel->type;
|
chan->type = channel->type;
|
||||||
chan->channel = channel->channel;
|
chan->channel = channel->channel;
|
||||||
chan->datasheet_name = channel->name;
|
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_separate = BIT(IIO_CHAN_INFO_RAW);
|
||||||
chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE);
|
chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE);
|
||||||
chan->scan_type.sign = 'u';
|
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->scan_type.storagebits = 16;
|
||||||
chan->ext_info = stm32_adc_ext_info;
|
chan->ext_info = stm32_adc_ext_info;
|
||||||
}
|
}
|
||||||
|
@ -1022,6 +1065,11 @@ static int stm32_adc_probe(struct platform_device *pdev)
|
||||||
return ret;
|
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);
|
ret = stm32_adc_chan_of_init(indio_dev);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto err_clk_disable;
|
goto err_clk_disable;
|
||||||
|
|
|
@ -85,6 +85,12 @@ static const struct gpadc_data sun6i_gpadc_data = {
|
||||||
.adc_chan_mask = SUN6I_GPADC_CTRL1_ADC_CHAN_MASK,
|
.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 sun4i_gpadc_iio {
|
||||||
struct iio_dev *indio_dev;
|
struct iio_dev *indio_dev;
|
||||||
struct completion completion;
|
struct completion completion;
|
||||||
|
@ -96,6 +102,7 @@ struct sun4i_gpadc_iio {
|
||||||
unsigned int temp_data_irq;
|
unsigned int temp_data_irq;
|
||||||
atomic_t ignore_temp_data_irq;
|
atomic_t ignore_temp_data_irq;
|
||||||
const struct gpadc_data *data;
|
const struct gpadc_data *data;
|
||||||
|
bool no_irq;
|
||||||
/* prevents concurrent reads of temperature and ADC */
|
/* prevents concurrent reads of temperature and ADC */
|
||||||
struct mutex mutex;
|
struct mutex mutex;
|
||||||
};
|
};
|
||||||
|
@ -138,6 +145,23 @@ static const struct iio_chan_spec sun4i_gpadc_channels_no_temp[] = {
|
||||||
SUN4I_GPADC_ADC_CHANNEL(3, "adc_chan3"),
|
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,
|
static int sun4i_prepare_for_irq(struct iio_dev *indio_dev, int channel,
|
||||||
unsigned int irq)
|
unsigned int irq)
|
||||||
{
|
{
|
||||||
|
@ -247,6 +271,17 @@ static int sun4i_gpadc_temp_read(struct iio_dev *indio_dev, int *val)
|
||||||
{
|
{
|
||||||
struct sun4i_gpadc_iio *info = iio_priv(indio_dev);
|
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);
|
return sun4i_gpadc_read(indio_dev, 0, val, info->temp_data_irq);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -454,31 +489,69 @@ static int sun4i_irq_init(struct platform_device *pdev, const char *name,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sun4i_gpadc_probe(struct platform_device *pdev)
|
static const struct of_device_id sun4i_gpadc_of_id[] = {
|
||||||
{
|
{
|
||||||
struct sun4i_gpadc_iio *info;
|
.compatible = "allwinner,sun8i-a33-ths",
|
||||||
struct iio_dev *indio_dev;
|
.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;
|
int ret;
|
||||||
struct sun4i_gpadc_dev *sun4i_gpadc_dev;
|
|
||||||
|
|
||||||
sun4i_gpadc_dev = dev_get_drvdata(pdev->dev.parent);
|
of_dev = of_match_device(sun4i_gpadc_of_id, &pdev->dev);
|
||||||
|
if (!of_dev)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));
|
info->no_irq = true;
|
||||||
if (!indio_dev)
|
info->data = (struct gpadc_data *)of_dev->data;
|
||||||
return -ENOMEM;
|
indio_dev->num_channels = ARRAY_SIZE(sun8i_a33_gpadc_channels);
|
||||||
|
indio_dev->channels = sun8i_a33_gpadc_channels;
|
||||||
|
|
||||||
info = iio_priv(indio_dev);
|
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
platform_set_drvdata(pdev, indio_dev);
|
base = devm_ioremap_resource(&pdev->dev, mem);
|
||||||
|
if (IS_ERR(base))
|
||||||
|
return PTR_ERR(base);
|
||||||
|
|
||||||
mutex_init(&info->mutex);
|
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;
|
info->regmap = sun4i_gpadc_dev->regmap;
|
||||||
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;
|
|
||||||
indio_dev->num_channels = ARRAY_SIZE(sun4i_gpadc_channels);
|
indio_dev->num_channels = ARRAY_SIZE(sun4i_gpadc_channels);
|
||||||
indio_dev->channels = sun4i_gpadc_channels;
|
indio_dev->channels = sun4i_gpadc_channels;
|
||||||
|
|
||||||
|
@ -519,8 +592,7 @@ static int sun4i_gpadc_probe(struct platform_device *pdev)
|
||||||
dev_err(&pdev->dev,
|
dev_err(&pdev->dev,
|
||||||
"could not register thermal sensor: %ld\n",
|
"could not register thermal sensor: %ld\n",
|
||||||
PTR_ERR(tzd));
|
PTR_ERR(tzd));
|
||||||
ret = PTR_ERR(tzd);
|
return PTR_ERR(tzd);
|
||||||
goto err;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
indio_dev->num_channels =
|
indio_dev->num_channels =
|
||||||
|
@ -528,36 +600,69 @@ static int sun4i_gpadc_probe(struct platform_device *pdev)
|
||||||
indio_dev->channels = sun4i_gpadc_channels_no_temp;
|
indio_dev->channels = sun4i_gpadc_channels_no_temp;
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
if (IS_ENABLED(CONFIG_THERMAL_OF)) {
|
if (IS_ENABLED(CONFIG_THERMAL_OF)) {
|
||||||
ret = sun4i_irq_init(pdev, "TEMP_DATA_PENDING",
|
ret = sun4i_irq_init(pdev, "TEMP_DATA_PENDING",
|
||||||
sun4i_gpadc_temp_data_irq_handler,
|
sun4i_gpadc_temp_data_irq_handler,
|
||||||
"temp_data", &info->temp_data_irq,
|
"temp_data", &info->temp_data_irq,
|
||||||
&info->ignore_temp_data_irq);
|
&info->ignore_temp_data_irq);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto err;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = sun4i_irq_init(pdev, "FIFO_DATA_PENDING",
|
ret = sun4i_irq_init(pdev, "FIFO_DATA_PENDING",
|
||||||
sun4i_gpadc_fifo_data_irq_handler, "fifo_data",
|
sun4i_gpadc_fifo_data_irq_handler, "fifo_data",
|
||||||
&info->fifo_data_irq, &info->ignore_fifo_data_irq);
|
&info->fifo_data_irq, &info->ignore_fifo_data_irq);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto err;
|
return ret;
|
||||||
|
|
||||||
if (IS_ENABLED(CONFIG_THERMAL_OF)) {
|
if (IS_ENABLED(CONFIG_THERMAL_OF)) {
|
||||||
ret = iio_map_array_register(indio_dev, sun4i_gpadc_hwmon_maps);
|
ret = iio_map_array_register(indio_dev, sun4i_gpadc_hwmon_maps);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(&pdev->dev,
|
dev_err(&pdev->dev,
|
||||||
"failed to register iio map array\n");
|
"failed to register iio map array\n");
|
||||||
goto err;
|
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);
|
ret = devm_iio_device_register(&pdev->dev, indio_dev);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(&pdev->dev, "could not register the device\n");
|
dev_err(&pdev->dev, "could not register the device\n");
|
||||||
|
@ -567,10 +672,9 @@ static int sun4i_gpadc_probe(struct platform_device *pdev)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_map:
|
err_map:
|
||||||
if (IS_ENABLED(CONFIG_THERMAL_OF))
|
if (!info->no_irq && IS_ENABLED(CONFIG_THERMAL_OF))
|
||||||
iio_map_array_unregister(indio_dev);
|
iio_map_array_unregister(indio_dev);
|
||||||
|
|
||||||
err:
|
|
||||||
pm_runtime_put(&pdev->dev);
|
pm_runtime_put(&pdev->dev);
|
||||||
pm_runtime_disable(&pdev->dev);
|
pm_runtime_disable(&pdev->dev);
|
||||||
|
|
||||||
|
@ -580,10 +684,11 @@ err:
|
||||||
static int sun4i_gpadc_remove(struct platform_device *pdev)
|
static int sun4i_gpadc_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct iio_dev *indio_dev = platform_get_drvdata(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_put(&pdev->dev);
|
||||||
pm_runtime_disable(&pdev->dev);
|
pm_runtime_disable(&pdev->dev);
|
||||||
if (IS_ENABLED(CONFIG_THERMAL_OF))
|
if (!info->no_irq && IS_ENABLED(CONFIG_THERMAL_OF))
|
||||||
iio_map_array_unregister(indio_dev);
|
iio_map_array_unregister(indio_dev);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -599,6 +704,7 @@ static const struct platform_device_id sun4i_gpadc_id[] = {
|
||||||
static struct platform_driver sun4i_gpadc_driver = {
|
static struct platform_driver sun4i_gpadc_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "sun4i-gpadc-iio",
|
.name = "sun4i-gpadc-iio",
|
||||||
|
.of_match_table = sun4i_gpadc_of_id,
|
||||||
.pm = &sun4i_gpadc_pm_ops,
|
.pm = &sun4i_gpadc_pm_ops,
|
||||||
},
|
},
|
||||||
.id_table = sun4i_gpadc_id,
|
.id_table = sun4i_gpadc_id,
|
||||||
|
|
|
@ -138,6 +138,10 @@ static int hid_sensor_data_rdy_trigger_set_state(struct iio_trigger *trig,
|
||||||
|
|
||||||
void hid_sensor_remove_trigger(struct hid_sensor_common *attrb)
|
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);
|
cancel_work_sync(&attrb->work);
|
||||||
iio_trigger_unregister(attrb->trigger);
|
iio_trigger_unregister(attrb->trigger);
|
||||||
iio_trigger_free(attrb->trigger);
|
iio_trigger_free(attrb->trigger);
|
||||||
|
|
|
@ -284,6 +284,21 @@ config MCP4922
|
||||||
To compile this driver as a module, choose M here: the module
|
To compile this driver as a module, choose M here: the module
|
||||||
will be called mcp4922.
|
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
|
config VF610_DAC
|
||||||
tristate "Vybrid vf610 DAC driver"
|
tristate "Vybrid vf610 DAC driver"
|
||||||
depends on OF
|
depends on OF
|
||||||
|
|
|
@ -30,4 +30,6 @@ obj-$(CONFIG_MAX517) += max517.o
|
||||||
obj-$(CONFIG_MAX5821) += max5821.o
|
obj-$(CONFIG_MAX5821) += max5821.o
|
||||||
obj-$(CONFIG_MCP4725) += mcp4725.o
|
obj-$(CONFIG_MCP4725) += mcp4725.o
|
||||||
obj-$(CONFIG_MCP4922) += mcp4922.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
|
obj-$(CONFIG_VF610_DAC) += vf610_dac.o
|
||||||
|
|
|
@ -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");
|
|
@ -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
|
|
@ -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");
|
|
@ -70,9 +70,8 @@ static int mpu3050_i2c_probe(struct i2c_client *client,
|
||||||
dev_err(&client->dev, "failed to allocate I2C mux\n");
|
dev_err(&client->dev, "failed to allocate I2C mux\n");
|
||||||
else {
|
else {
|
||||||
mpu3050->i2cmux->priv = mpu3050;
|
mpu3050->i2cmux->priv = mpu3050;
|
||||||
ret = i2c_mux_add_adapter(mpu3050->i2cmux, 0, 0, 0);
|
/* Ignore failure, not critical */
|
||||||
if (ret)
|
i2c_mux_add_adapter(mpu3050->i2cmux, 0, 0, 0);
|
||||||
dev_err(&client->dev, "failed to add I2C mux\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -71,6 +71,7 @@ enum st_lsm6dsx_fifo_mode {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct st_lsm6dsx_sensor - ST IMU sensor instance
|
* struct st_lsm6dsx_sensor - ST IMU sensor instance
|
||||||
|
* @name: Sensor name.
|
||||||
* @id: Sensor identifier.
|
* @id: Sensor identifier.
|
||||||
* @hw: Pointer to instance of struct st_lsm6dsx_hw.
|
* @hw: Pointer to instance of struct st_lsm6dsx_hw.
|
||||||
* @gain: Configured sensor sensitivity.
|
* @gain: Configured sensor sensitivity.
|
||||||
|
@ -83,6 +84,7 @@ enum st_lsm6dsx_fifo_mode {
|
||||||
* @ts: Latest timestamp from the interrupt handler.
|
* @ts: Latest timestamp from the interrupt handler.
|
||||||
*/
|
*/
|
||||||
struct st_lsm6dsx_sensor {
|
struct st_lsm6dsx_sensor {
|
||||||
|
char name[32];
|
||||||
enum st_lsm6dsx_sensor_id id;
|
enum st_lsm6dsx_sensor_id id;
|
||||||
struct st_lsm6dsx_hw *hw;
|
struct st_lsm6dsx_hw *hw;
|
||||||
|
|
||||||
|
@ -133,7 +135,7 @@ struct st_lsm6dsx_hw {
|
||||||
#endif /* CONFIG_SPI_MASTER */
|
#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);
|
const struct st_lsm6dsx_transfer_function *tf_ops);
|
||||||
int st_lsm6dsx_sensor_enable(struct st_lsm6dsx_sensor *sensor);
|
int st_lsm6dsx_sensor_enable(struct st_lsm6dsx_sensor *sensor);
|
||||||
int st_lsm6dsx_sensor_disable(struct st_lsm6dsx_sensor *sensor);
|
int st_lsm6dsx_sensor_disable(struct st_lsm6dsx_sensor *sensor);
|
||||||
|
|
|
@ -559,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)
|
static int st_lsm6dsx_of_get_drdy_pin(struct st_lsm6dsx_hw *hw, int *drdy_pin)
|
||||||
{
|
{
|
||||||
struct device_node *np = hw->dev->of_node;
|
struct device_node *np = hw->dev->of_node;
|
||||||
int err;
|
|
||||||
|
|
||||||
if (!np)
|
if (!np)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
err = of_property_read_u32(np, "st,drdy-int-pin", drdy_pin);
|
return of_property_read_u32(np, "st,drdy-int-pin", drdy_pin);
|
||||||
if (err == -ENODATA) {
|
|
||||||
/* if the property has not been specified use default value */
|
|
||||||
*drdy_pin = 1;
|
|
||||||
err = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int st_lsm6dsx_get_drdy_reg(struct st_lsm6dsx_hw *hw, u8 *drdy_reg)
|
static int st_lsm6dsx_get_drdy_reg(struct st_lsm6dsx_hw *hw, u8 *drdy_reg)
|
||||||
|
@ -642,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,
|
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 st_lsm6dsx_sensor *sensor;
|
||||||
struct iio_dev *iio_dev;
|
struct iio_dev *iio_dev;
|
||||||
|
@ -666,27 +659,30 @@ static struct iio_dev *st_lsm6dsx_alloc_iiodev(struct st_lsm6dsx_hw *hw,
|
||||||
case ST_LSM6DSX_ID_ACC:
|
case ST_LSM6DSX_ID_ACC:
|
||||||
iio_dev->channels = st_lsm6dsx_acc_channels;
|
iio_dev->channels = st_lsm6dsx_acc_channels;
|
||||||
iio_dev->num_channels = ARRAY_SIZE(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;
|
iio_dev->info = &st_lsm6dsx_acc_info;
|
||||||
|
|
||||||
sensor->decimator_mask = ST_LSM6DSX_REG_ACC_DEC_MASK;
|
sensor->decimator_mask = ST_LSM6DSX_REG_ACC_DEC_MASK;
|
||||||
|
scnprintf(sensor->name, sizeof(sensor->name), "%s_accel",
|
||||||
|
name);
|
||||||
break;
|
break;
|
||||||
case ST_LSM6DSX_ID_GYRO:
|
case ST_LSM6DSX_ID_GYRO:
|
||||||
iio_dev->channels = st_lsm6dsx_gyro_channels;
|
iio_dev->channels = st_lsm6dsx_gyro_channels;
|
||||||
iio_dev->num_channels = ARRAY_SIZE(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;
|
iio_dev->info = &st_lsm6dsx_gyro_info;
|
||||||
|
|
||||||
sensor->decimator_mask = ST_LSM6DSX_REG_GYRO_DEC_MASK;
|
sensor->decimator_mask = ST_LSM6DSX_REG_GYRO_DEC_MASK;
|
||||||
|
scnprintf(sensor->name, sizeof(sensor->name), "%s_gyro",
|
||||||
|
name);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
iio_dev->name = sensor->name;
|
||||||
|
|
||||||
return iio_dev;
|
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)
|
const struct st_lsm6dsx_transfer_function *tf_ops)
|
||||||
{
|
{
|
||||||
struct st_lsm6dsx_hw *hw;
|
struct st_lsm6dsx_hw *hw;
|
||||||
|
@ -710,7 +706,7 @@ int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id,
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
|
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])
|
if (!hw->iio_devs[i])
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,7 +61,7 @@ static int st_lsm6dsx_i2c_probe(struct i2c_client *client,
|
||||||
const struct i2c_device_id *id)
|
const struct i2c_device_id *id)
|
||||||
{
|
{
|
||||||
return st_lsm6dsx_probe(&client->dev, client->irq,
|
return st_lsm6dsx_probe(&client->dev, client->irq,
|
||||||
(int)id->driver_data,
|
(int)id->driver_data, id->name,
|
||||||
&st_lsm6dsx_transfer_fn);
|
&st_lsm6dsx_transfer_fn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -78,7 +78,7 @@ static int st_lsm6dsx_spi_probe(struct spi_device *spi)
|
||||||
const struct spi_device_id *id = spi_get_device_id(spi);
|
const struct spi_device_id *id = spi_get_device_id(spi);
|
||||||
|
|
||||||
return st_lsm6dsx_probe(&spi->dev, spi->irq,
|
return st_lsm6dsx_probe(&spi->dev, spi->irq,
|
||||||
(int)id->driver_data,
|
(int)id->driver_data, id->name,
|
||||||
&st_lsm6dsx_transfer_fn);
|
&st_lsm6dsx_transfer_fn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1112,6 +1112,8 @@ static int apds9960_runtime_resume(struct device *dev)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static const struct dev_pm_ops apds9960_pm_ops = {
|
static const struct dev_pm_ops apds9960_pm_ops = {
|
||||||
|
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
|
||||||
|
pm_runtime_force_resume)
|
||||||
SET_RUNTIME_PM_OPS(apds9960_runtime_suspend,
|
SET_RUNTIME_PM_OPS(apds9960_runtime_suspend,
|
||||||
apds9960_runtime_resume, NULL)
|
apds9960_runtime_resume, NULL)
|
||||||
};
|
};
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
|
|
||||||
#define MAX_TRIGGERS 6
|
#define MAX_TRIGGERS 6
|
||||||
|
#define MAX_VALIDS 5
|
||||||
|
|
||||||
/* List the triggers created by each timer */
|
/* List the triggers created by each timer */
|
||||||
static const void *triggers_table[][MAX_TRIGGERS] = {
|
static const void *triggers_table[][MAX_TRIGGERS] = {
|
||||||
|
@ -32,12 +33,29 @@ static const void *triggers_table[][MAX_TRIGGERS] = {
|
||||||
{ TIM12_TRGO, TIM12_CH1, TIM12_CH2,},
|
{ TIM12_TRGO, TIM12_CH1, TIM12_CH2,},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* List the triggers accepted by each timer */
|
||||||
|
static const void *valids_table[][MAX_VALIDS] = {
|
||||||
|
{ TIM5_TRGO, TIM2_TRGO, TIM3_TRGO, TIM4_TRGO,},
|
||||||
|
{ TIM1_TRGO, TIM8_TRGO, TIM3_TRGO, TIM4_TRGO,},
|
||||||
|
{ TIM1_TRGO, TIM2_TRGO, TIM5_TRGO, TIM4_TRGO,},
|
||||||
|
{ TIM1_TRGO, TIM2_TRGO, TIM3_TRGO, TIM8_TRGO,},
|
||||||
|
{ TIM2_TRGO, TIM3_TRGO, TIM4_TRGO, TIM8_TRGO,},
|
||||||
|
{ }, /* timer 6 */
|
||||||
|
{ }, /* timer 7 */
|
||||||
|
{ TIM1_TRGO, TIM2_TRGO, TIM4_TRGO, TIM5_TRGO,},
|
||||||
|
{ TIM2_TRGO, TIM3_TRGO,},
|
||||||
|
{ }, /* timer 10 */
|
||||||
|
{ }, /* timer 11 */
|
||||||
|
{ TIM4_TRGO, TIM5_TRGO,},
|
||||||
|
};
|
||||||
|
|
||||||
struct stm32_timer_trigger {
|
struct stm32_timer_trigger {
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
struct regmap *regmap;
|
struct regmap *regmap;
|
||||||
struct clk *clk;
|
struct clk *clk;
|
||||||
u32 max_arr;
|
u32 max_arr;
|
||||||
const void *triggers;
|
const void *triggers;
|
||||||
|
const void *valids;
|
||||||
};
|
};
|
||||||
|
|
||||||
static int stm32_timer_start(struct stm32_timer_trigger *priv,
|
static int stm32_timer_start(struct stm32_timer_trigger *priv,
|
||||||
|
@ -180,8 +198,7 @@ static ssize_t stm32_tt_show_master_mode(struct device *dev,
|
||||||
struct device_attribute *attr,
|
struct device_attribute *attr,
|
||||||
char *buf)
|
char *buf)
|
||||||
{
|
{
|
||||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
struct stm32_timer_trigger *priv = dev_get_drvdata(dev);
|
||||||
struct stm32_timer_trigger *priv = iio_priv(indio_dev);
|
|
||||||
u32 cr2;
|
u32 cr2;
|
||||||
|
|
||||||
regmap_read(priv->regmap, TIM_CR2, &cr2);
|
regmap_read(priv->regmap, TIM_CR2, &cr2);
|
||||||
|
@ -194,8 +211,7 @@ static ssize_t stm32_tt_store_master_mode(struct device *dev,
|
||||||
struct device_attribute *attr,
|
struct device_attribute *attr,
|
||||||
const char *buf, size_t len)
|
const char *buf, size_t len)
|
||||||
{
|
{
|
||||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
struct stm32_timer_trigger *priv = dev_get_drvdata(dev);
|
||||||
struct stm32_timer_trigger *priv = iio_priv(indio_dev);
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(master_mode_table); i++) {
|
for (i = 0; i < ARRAY_SIZE(master_mode_table); i++) {
|
||||||
|
@ -275,6 +291,286 @@ static int stm32_setup_iio_triggers(struct stm32_timer_trigger *priv)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int stm32_counter_read_raw(struct iio_dev *indio_dev,
|
||||||
|
struct iio_chan_spec const *chan,
|
||||||
|
int *val, int *val2, long mask)
|
||||||
|
{
|
||||||
|
struct stm32_timer_trigger *priv = iio_priv(indio_dev);
|
||||||
|
|
||||||
|
switch (mask) {
|
||||||
|
case IIO_CHAN_INFO_RAW:
|
||||||
|
{
|
||||||
|
u32 cnt;
|
||||||
|
|
||||||
|
regmap_read(priv->regmap, TIM_CNT, &cnt);
|
||||||
|
*val = cnt;
|
||||||
|
|
||||||
|
return IIO_VAL_INT;
|
||||||
|
}
|
||||||
|
case IIO_CHAN_INFO_SCALE:
|
||||||
|
{
|
||||||
|
u32 smcr;
|
||||||
|
|
||||||
|
regmap_read(priv->regmap, TIM_SMCR, &smcr);
|
||||||
|
smcr &= TIM_SMCR_SMS;
|
||||||
|
|
||||||
|
*val = 1;
|
||||||
|
*val2 = 0;
|
||||||
|
|
||||||
|
/* in quadrature case scale = 0.25 */
|
||||||
|
if (smcr == 3)
|
||||||
|
*val2 = 2;
|
||||||
|
|
||||||
|
return IIO_VAL_FRACTIONAL_LOG2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int stm32_counter_write_raw(struct iio_dev *indio_dev,
|
||||||
|
struct iio_chan_spec const *chan,
|
||||||
|
int val, int val2, long mask)
|
||||||
|
{
|
||||||
|
struct stm32_timer_trigger *priv = iio_priv(indio_dev);
|
||||||
|
|
||||||
|
switch (mask) {
|
||||||
|
case IIO_CHAN_INFO_RAW:
|
||||||
|
regmap_write(priv->regmap, TIM_CNT, val);
|
||||||
|
|
||||||
|
return IIO_VAL_INT;
|
||||||
|
case IIO_CHAN_INFO_SCALE:
|
||||||
|
/* fixed scale */
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct iio_info stm32_trigger_info = {
|
||||||
|
.driver_module = THIS_MODULE,
|
||||||
|
.read_raw = stm32_counter_read_raw,
|
||||||
|
.write_raw = stm32_counter_write_raw
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char *const stm32_enable_modes[] = {
|
||||||
|
"always",
|
||||||
|
"gated",
|
||||||
|
"triggered",
|
||||||
|
};
|
||||||
|
|
||||||
|
static int stm32_enable_mode2sms(int mode)
|
||||||
|
{
|
||||||
|
switch (mode) {
|
||||||
|
case 0:
|
||||||
|
return 0;
|
||||||
|
case 1:
|
||||||
|
return 5;
|
||||||
|
case 2:
|
||||||
|
return 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int stm32_set_enable_mode(struct iio_dev *indio_dev,
|
||||||
|
const struct iio_chan_spec *chan,
|
||||||
|
unsigned int mode)
|
||||||
|
{
|
||||||
|
struct stm32_timer_trigger *priv = iio_priv(indio_dev);
|
||||||
|
int sms = stm32_enable_mode2sms(mode);
|
||||||
|
|
||||||
|
if (sms < 0)
|
||||||
|
return sms;
|
||||||
|
|
||||||
|
regmap_update_bits(priv->regmap, TIM_SMCR, TIM_SMCR_SMS, sms);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int stm32_sms2enable_mode(int mode)
|
||||||
|
{
|
||||||
|
switch (mode) {
|
||||||
|
case 0:
|
||||||
|
return 0;
|
||||||
|
case 5:
|
||||||
|
return 1;
|
||||||
|
case 6:
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int stm32_get_enable_mode(struct iio_dev *indio_dev,
|
||||||
|
const struct iio_chan_spec *chan)
|
||||||
|
{
|
||||||
|
struct stm32_timer_trigger *priv = iio_priv(indio_dev);
|
||||||
|
u32 smcr;
|
||||||
|
|
||||||
|
regmap_read(priv->regmap, TIM_SMCR, &smcr);
|
||||||
|
smcr &= TIM_SMCR_SMS;
|
||||||
|
|
||||||
|
return stm32_sms2enable_mode(smcr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct iio_enum stm32_enable_mode_enum = {
|
||||||
|
.items = stm32_enable_modes,
|
||||||
|
.num_items = ARRAY_SIZE(stm32_enable_modes),
|
||||||
|
.set = stm32_set_enable_mode,
|
||||||
|
.get = stm32_get_enable_mode
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char *const stm32_quadrature_modes[] = {
|
||||||
|
"channel_A",
|
||||||
|
"channel_B",
|
||||||
|
"quadrature",
|
||||||
|
};
|
||||||
|
|
||||||
|
static int stm32_set_quadrature_mode(struct iio_dev *indio_dev,
|
||||||
|
const struct iio_chan_spec *chan,
|
||||||
|
unsigned int mode)
|
||||||
|
{
|
||||||
|
struct stm32_timer_trigger *priv = iio_priv(indio_dev);
|
||||||
|
|
||||||
|
regmap_update_bits(priv->regmap, TIM_SMCR, TIM_SMCR_SMS, mode + 1);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int stm32_get_quadrature_mode(struct iio_dev *indio_dev,
|
||||||
|
const struct iio_chan_spec *chan)
|
||||||
|
{
|
||||||
|
struct stm32_timer_trigger *priv = iio_priv(indio_dev);
|
||||||
|
u32 smcr;
|
||||||
|
|
||||||
|
regmap_read(priv->regmap, TIM_SMCR, &smcr);
|
||||||
|
smcr &= TIM_SMCR_SMS;
|
||||||
|
|
||||||
|
return smcr - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct iio_enum stm32_quadrature_mode_enum = {
|
||||||
|
.items = stm32_quadrature_modes,
|
||||||
|
.num_items = ARRAY_SIZE(stm32_quadrature_modes),
|
||||||
|
.set = stm32_set_quadrature_mode,
|
||||||
|
.get = stm32_get_quadrature_mode
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char *const stm32_count_direction_states[] = {
|
||||||
|
"up",
|
||||||
|
"down"
|
||||||
|
};
|
||||||
|
|
||||||
|
static int stm32_set_count_direction(struct iio_dev *indio_dev,
|
||||||
|
const struct iio_chan_spec *chan,
|
||||||
|
unsigned int mode)
|
||||||
|
{
|
||||||
|
struct stm32_timer_trigger *priv = iio_priv(indio_dev);
|
||||||
|
|
||||||
|
regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_DIR, mode);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int stm32_get_count_direction(struct iio_dev *indio_dev,
|
||||||
|
const struct iio_chan_spec *chan)
|
||||||
|
{
|
||||||
|
struct stm32_timer_trigger *priv = iio_priv(indio_dev);
|
||||||
|
u32 cr1;
|
||||||
|
|
||||||
|
regmap_read(priv->regmap, TIM_CR1, &cr1);
|
||||||
|
|
||||||
|
return (cr1 & TIM_CR1_DIR);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct iio_enum stm32_count_direction_enum = {
|
||||||
|
.items = stm32_count_direction_states,
|
||||||
|
.num_items = ARRAY_SIZE(stm32_count_direction_states),
|
||||||
|
.set = stm32_set_count_direction,
|
||||||
|
.get = stm32_get_count_direction
|
||||||
|
};
|
||||||
|
|
||||||
|
static ssize_t stm32_count_get_preset(struct iio_dev *indio_dev,
|
||||||
|
uintptr_t private,
|
||||||
|
const struct iio_chan_spec *chan,
|
||||||
|
char *buf)
|
||||||
|
{
|
||||||
|
struct stm32_timer_trigger *priv = iio_priv(indio_dev);
|
||||||
|
u32 arr;
|
||||||
|
|
||||||
|
regmap_read(priv->regmap, TIM_ARR, &arr);
|
||||||
|
|
||||||
|
return snprintf(buf, PAGE_SIZE, "%u\n", arr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t stm32_count_set_preset(struct iio_dev *indio_dev,
|
||||||
|
uintptr_t private,
|
||||||
|
const struct iio_chan_spec *chan,
|
||||||
|
const char *buf, size_t len)
|
||||||
|
{
|
||||||
|
struct stm32_timer_trigger *priv = iio_priv(indio_dev);
|
||||||
|
unsigned int preset;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = kstrtouint(buf, 0, &preset);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
regmap_write(priv->regmap, TIM_ARR, preset);
|
||||||
|
regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_ARPE, TIM_CR1_ARPE);
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct iio_chan_spec_ext_info stm32_trigger_count_info[] = {
|
||||||
|
{
|
||||||
|
.name = "preset",
|
||||||
|
.shared = IIO_SEPARATE,
|
||||||
|
.read = stm32_count_get_preset,
|
||||||
|
.write = stm32_count_set_preset
|
||||||
|
},
|
||||||
|
IIO_ENUM("count_direction", IIO_SEPARATE, &stm32_count_direction_enum),
|
||||||
|
IIO_ENUM_AVAILABLE("count_direction", &stm32_count_direction_enum),
|
||||||
|
IIO_ENUM("quadrature_mode", IIO_SEPARATE, &stm32_quadrature_mode_enum),
|
||||||
|
IIO_ENUM_AVAILABLE("quadrature_mode", &stm32_quadrature_mode_enum),
|
||||||
|
IIO_ENUM("enable_mode", IIO_SEPARATE, &stm32_enable_mode_enum),
|
||||||
|
IIO_ENUM_AVAILABLE("enable_mode", &stm32_enable_mode_enum),
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct iio_chan_spec stm32_trigger_channel = {
|
||||||
|
.type = IIO_COUNT,
|
||||||
|
.channel = 0,
|
||||||
|
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
|
||||||
|
.ext_info = stm32_trigger_count_info,
|
||||||
|
.indexed = 1
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct stm32_timer_trigger *stm32_setup_counter_device(struct device *dev)
|
||||||
|
{
|
||||||
|
struct iio_dev *indio_dev;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
indio_dev = devm_iio_device_alloc(dev,
|
||||||
|
sizeof(struct stm32_timer_trigger));
|
||||||
|
if (!indio_dev)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
indio_dev->name = dev_name(dev);
|
||||||
|
indio_dev->dev.parent = dev;
|
||||||
|
indio_dev->info = &stm32_trigger_info;
|
||||||
|
indio_dev->num_channels = 1;
|
||||||
|
indio_dev->channels = &stm32_trigger_channel;
|
||||||
|
indio_dev->dev.of_node = dev->of_node;
|
||||||
|
|
||||||
|
ret = devm_iio_device_register(dev, indio_dev);
|
||||||
|
if (ret)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return iio_priv(indio_dev);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* is_stm32_timer_trigger
|
* is_stm32_timer_trigger
|
||||||
* @trig: trigger to be checked
|
* @trig: trigger to be checked
|
||||||
|
@ -299,9 +595,14 @@ static int stm32_timer_trigger_probe(struct platform_device *pdev)
|
||||||
if (of_property_read_u32(dev->of_node, "reg", &index))
|
if (of_property_read_u32(dev->of_node, "reg", &index))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (index >= ARRAY_SIZE(triggers_table))
|
if (index >= ARRAY_SIZE(triggers_table) ||
|
||||||
|
index >= ARRAY_SIZE(valids_table))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
|
/* Create an IIO device only if we have triggers to be validated */
|
||||||
|
if (*valids_table[index])
|
||||||
|
priv = stm32_setup_counter_device(dev);
|
||||||
|
else
|
||||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||||
|
|
||||||
if (!priv)
|
if (!priv)
|
||||||
|
@ -312,6 +613,7 @@ static int stm32_timer_trigger_probe(struct platform_device *pdev)
|
||||||
priv->clk = ddata->clk;
|
priv->clk = ddata->clk;
|
||||||
priv->max_arr = ddata->max_arr;
|
priv->max_arr = ddata->max_arr;
|
||||||
priv->triggers = triggers_table[index];
|
priv->triggers = triggers_table[index];
|
||||||
|
priv->valids = valids_table[index];
|
||||||
|
|
||||||
ret = stm32_setup_iio_triggers(priv);
|
ret = stm32_setup_iio_triggers(priv);
|
||||||
if (ret)
|
if (ret)
|
||||||
|
|
|
@ -205,9 +205,8 @@ static int adis16203_read_raw(struct iio_dev *indio_dev,
|
||||||
bits = 14;
|
bits = 14;
|
||||||
addr = adis16203_addresses[chan->scan_index];
|
addr = adis16203_addresses[chan->scan_index];
|
||||||
ret = adis_read_reg_16(st, addr, &val16);
|
ret = adis_read_reg_16(st, addr, &val16);
|
||||||
if (ret) {
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
|
||||||
val16 &= (1 << bits) - 1;
|
val16 &= (1 << bits) - 1;
|
||||||
val16 = (s16)(val16 << (16 - bits)) >> (16 - bits);
|
val16 = (s16)(val16 << (16 - bits)) >> (16 - bits);
|
||||||
*val = val16;
|
*val = val16;
|
||||||
|
|
|
@ -98,6 +98,7 @@ struct ad5933_state {
|
||||||
struct i2c_client *client;
|
struct i2c_client *client;
|
||||||
struct regulator *reg;
|
struct regulator *reg;
|
||||||
struct delayed_work work;
|
struct delayed_work work;
|
||||||
|
struct mutex lock; /* Protect sensor state */
|
||||||
unsigned long mclk_hz;
|
unsigned long mclk_hz;
|
||||||
unsigned char ctrl_hb;
|
unsigned char ctrl_hb;
|
||||||
unsigned char ctrl_lb;
|
unsigned char ctrl_lb;
|
||||||
|
@ -306,9 +307,11 @@ static ssize_t ad5933_show_frequency(struct device *dev,
|
||||||
u8 d8[4];
|
u8 d8[4];
|
||||||
} dat;
|
} dat;
|
||||||
|
|
||||||
mutex_lock(&indio_dev->mlock);
|
ret = iio_device_claim_direct_mode(indio_dev);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
ret = ad5933_i2c_read(st->client, this_attr->address, 3, &dat.d8[1]);
|
ret = ad5933_i2c_read(st->client, this_attr->address, 3, &dat.d8[1]);
|
||||||
mutex_unlock(&indio_dev->mlock);
|
iio_device_release_direct_mode(indio_dev);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
@ -338,9 +341,11 @@ static ssize_t ad5933_store_frequency(struct device *dev,
|
||||||
if (val > AD5933_MAX_OUTPUT_FREQ_Hz)
|
if (val > AD5933_MAX_OUTPUT_FREQ_Hz)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
mutex_lock(&indio_dev->mlock);
|
ret = iio_device_claim_direct_mode(indio_dev);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
ret = ad5933_set_freq(st, this_attr->address, val);
|
ret = ad5933_set_freq(st, this_attr->address, val);
|
||||||
mutex_unlock(&indio_dev->mlock);
|
iio_device_release_direct_mode(indio_dev);
|
||||||
|
|
||||||
return ret ? ret : len;
|
return ret ? ret : len;
|
||||||
}
|
}
|
||||||
|
@ -364,7 +369,7 @@ static ssize_t ad5933_show(struct device *dev,
|
||||||
struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
|
struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
|
||||||
int ret = 0, len = 0;
|
int ret = 0, len = 0;
|
||||||
|
|
||||||
mutex_lock(&indio_dev->mlock);
|
mutex_lock(&st->lock);
|
||||||
switch ((u32)this_attr->address) {
|
switch ((u32)this_attr->address) {
|
||||||
case AD5933_OUT_RANGE:
|
case AD5933_OUT_RANGE:
|
||||||
len = sprintf(buf, "%u\n",
|
len = sprintf(buf, "%u\n",
|
||||||
|
@ -393,7 +398,7 @@ static ssize_t ad5933_show(struct device *dev,
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
mutex_unlock(&indio_dev->mlock);
|
mutex_unlock(&st->lock);
|
||||||
return ret ? ret : len;
|
return ret ? ret : len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -415,7 +420,10 @@ static ssize_t ad5933_store(struct device *dev,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
mutex_lock(&indio_dev->mlock);
|
ret = iio_device_claim_direct_mode(indio_dev);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
mutex_lock(&st->lock);
|
||||||
switch ((u32)this_attr->address) {
|
switch ((u32)this_attr->address) {
|
||||||
case AD5933_OUT_RANGE:
|
case AD5933_OUT_RANGE:
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
|
@ -465,7 +473,8 @@ static ssize_t ad5933_store(struct device *dev,
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
mutex_unlock(&indio_dev->mlock);
|
mutex_unlock(&st->lock);
|
||||||
|
iio_device_release_direct_mode(indio_dev);
|
||||||
return ret ? ret : len;
|
return ret ? ret : len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -532,11 +541,9 @@ static int ad5933_read_raw(struct iio_dev *indio_dev,
|
||||||
|
|
||||||
switch (m) {
|
switch (m) {
|
||||||
case IIO_CHAN_INFO_RAW:
|
case IIO_CHAN_INFO_RAW:
|
||||||
mutex_lock(&indio_dev->mlock);
|
ret = iio_device_claim_direct_mode(indio_dev);
|
||||||
if (iio_buffer_enabled(indio_dev)) {
|
if (ret)
|
||||||
ret = -EBUSY;
|
return ret;
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
ret = ad5933_cmd(st, AD5933_CTRL_MEASURE_TEMP);
|
ret = ad5933_cmd(st, AD5933_CTRL_MEASURE_TEMP);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto out;
|
goto out;
|
||||||
|
@ -549,7 +556,7 @@ static int ad5933_read_raw(struct iio_dev *indio_dev,
|
||||||
2, (u8 *)&dat);
|
2, (u8 *)&dat);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto out;
|
goto out;
|
||||||
mutex_unlock(&indio_dev->mlock);
|
iio_device_release_direct_mode(indio_dev);
|
||||||
*val = sign_extend32(be16_to_cpu(dat), 13);
|
*val = sign_extend32(be16_to_cpu(dat), 13);
|
||||||
|
|
||||||
return IIO_VAL_INT;
|
return IIO_VAL_INT;
|
||||||
|
@ -561,7 +568,7 @@ static int ad5933_read_raw(struct iio_dev *indio_dev,
|
||||||
|
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
out:
|
out:
|
||||||
mutex_unlock(&indio_dev->mlock);
|
iio_device_release_direct_mode(indio_dev);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -657,18 +664,17 @@ static void ad5933_work(struct work_struct *work)
|
||||||
unsigned char status;
|
unsigned char status;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
mutex_lock(&indio_dev->mlock);
|
|
||||||
if (st->state == AD5933_CTRL_INIT_START_FREQ) {
|
if (st->state == AD5933_CTRL_INIT_START_FREQ) {
|
||||||
/* start sweep */
|
/* start sweep */
|
||||||
ad5933_cmd(st, AD5933_CTRL_START_SWEEP);
|
ad5933_cmd(st, AD5933_CTRL_START_SWEEP);
|
||||||
st->state = AD5933_CTRL_START_SWEEP;
|
st->state = AD5933_CTRL_START_SWEEP;
|
||||||
schedule_delayed_work(&st->work, st->poll_time_jiffies);
|
schedule_delayed_work(&st->work, st->poll_time_jiffies);
|
||||||
goto out;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = ad5933_i2c_read(st->client, AD5933_REG_STATUS, 1, &status);
|
ret = ad5933_i2c_read(st->client, AD5933_REG_STATUS, 1, &status);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out;
|
return;
|
||||||
|
|
||||||
if (status & AD5933_STAT_DATA_VALID) {
|
if (status & AD5933_STAT_DATA_VALID) {
|
||||||
int scan_count = bitmap_weight(indio_dev->active_scan_mask,
|
int scan_count = bitmap_weight(indio_dev->active_scan_mask,
|
||||||
|
@ -678,7 +684,7 @@ static void ad5933_work(struct work_struct *work)
|
||||||
AD5933_REG_REAL_DATA : AD5933_REG_IMAG_DATA,
|
AD5933_REG_REAL_DATA : AD5933_REG_IMAG_DATA,
|
||||||
scan_count * 2, (u8 *)buf);
|
scan_count * 2, (u8 *)buf);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out;
|
return;
|
||||||
|
|
||||||
if (scan_count == 2) {
|
if (scan_count == 2) {
|
||||||
val[0] = be16_to_cpu(buf[0]);
|
val[0] = be16_to_cpu(buf[0]);
|
||||||
|
@ -690,7 +696,7 @@ static void ad5933_work(struct work_struct *work)
|
||||||
} else {
|
} else {
|
||||||
/* no data available - try again later */
|
/* no data available - try again later */
|
||||||
schedule_delayed_work(&st->work, st->poll_time_jiffies);
|
schedule_delayed_work(&st->work, st->poll_time_jiffies);
|
||||||
goto out;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status & AD5933_STAT_SWEEP_DONE) {
|
if (status & AD5933_STAT_SWEEP_DONE) {
|
||||||
|
@ -703,8 +709,6 @@ static void ad5933_work(struct work_struct *work)
|
||||||
ad5933_cmd(st, AD5933_CTRL_INC_FREQ);
|
ad5933_cmd(st, AD5933_CTRL_INC_FREQ);
|
||||||
schedule_delayed_work(&st->work, st->poll_time_jiffies);
|
schedule_delayed_work(&st->work, st->poll_time_jiffies);
|
||||||
}
|
}
|
||||||
out:
|
|
||||||
mutex_unlock(&indio_dev->mlock);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ad5933_probe(struct i2c_client *client,
|
static int ad5933_probe(struct i2c_client *client,
|
||||||
|
@ -723,6 +727,8 @@ static int ad5933_probe(struct i2c_client *client,
|
||||||
i2c_set_clientdata(client, indio_dev);
|
i2c_set_clientdata(client, indio_dev);
|
||||||
st->client = client;
|
st->client = client;
|
||||||
|
|
||||||
|
mutex_init(&st->lock);
|
||||||
|
|
||||||
if (!pdata)
|
if (!pdata)
|
||||||
pdata = &ad5933_default_pdata;
|
pdata = &ad5933_default_pdata;
|
||||||
|
|
||||||
|
|
|
@ -279,49 +279,49 @@ static int ade7759_reset(struct device *dev)
|
||||||
}
|
}
|
||||||
|
|
||||||
static IIO_DEV_ATTR_AENERGY(ade7759_read_40bit, ADE7759_AENERGY);
|
static IIO_DEV_ATTR_AENERGY(ade7759_read_40bit, ADE7759_AENERGY);
|
||||||
static IIO_DEV_ATTR_CFDEN(S_IWUSR | S_IRUGO,
|
static IIO_DEV_ATTR_CFDEN(0644,
|
||||||
ade7759_read_16bit,
|
ade7759_read_16bit,
|
||||||
ade7759_write_16bit,
|
ade7759_write_16bit,
|
||||||
ADE7759_CFDEN);
|
ADE7759_CFDEN);
|
||||||
static IIO_DEV_ATTR_CFNUM(S_IWUSR | S_IRUGO,
|
static IIO_DEV_ATTR_CFNUM(0644,
|
||||||
ade7759_read_8bit,
|
ade7759_read_8bit,
|
||||||
ade7759_write_8bit,
|
ade7759_write_8bit,
|
||||||
ADE7759_CFNUM);
|
ADE7759_CFNUM);
|
||||||
static IIO_DEV_ATTR_CHKSUM(ade7759_read_8bit, ADE7759_CHKSUM);
|
static IIO_DEV_ATTR_CHKSUM(ade7759_read_8bit, ADE7759_CHKSUM);
|
||||||
static IIO_DEV_ATTR_PHCAL(S_IWUSR | S_IRUGO,
|
static IIO_DEV_ATTR_PHCAL(0644,
|
||||||
ade7759_read_16bit,
|
ade7759_read_16bit,
|
||||||
ade7759_write_16bit,
|
ade7759_write_16bit,
|
||||||
ADE7759_PHCAL);
|
ADE7759_PHCAL);
|
||||||
static IIO_DEV_ATTR_APOS(S_IWUSR | S_IRUGO,
|
static IIO_DEV_ATTR_APOS(0644,
|
||||||
ade7759_read_16bit,
|
ade7759_read_16bit,
|
||||||
ade7759_write_16bit,
|
ade7759_write_16bit,
|
||||||
ADE7759_APOS);
|
ADE7759_APOS);
|
||||||
static IIO_DEV_ATTR_SAGCYC(S_IWUSR | S_IRUGO,
|
static IIO_DEV_ATTR_SAGCYC(0644,
|
||||||
ade7759_read_8bit,
|
ade7759_read_8bit,
|
||||||
ade7759_write_8bit,
|
ade7759_write_8bit,
|
||||||
ADE7759_SAGCYC);
|
ADE7759_SAGCYC);
|
||||||
static IIO_DEV_ATTR_SAGLVL(S_IWUSR | S_IRUGO,
|
static IIO_DEV_ATTR_SAGLVL(0644,
|
||||||
ade7759_read_8bit,
|
ade7759_read_8bit,
|
||||||
ade7759_write_8bit,
|
ade7759_write_8bit,
|
||||||
ADE7759_SAGLVL);
|
ADE7759_SAGLVL);
|
||||||
static IIO_DEV_ATTR_LINECYC(S_IWUSR | S_IRUGO,
|
static IIO_DEV_ATTR_LINECYC(0644,
|
||||||
ade7759_read_8bit,
|
ade7759_read_8bit,
|
||||||
ade7759_write_8bit,
|
ade7759_write_8bit,
|
||||||
ADE7759_LINECYC);
|
ADE7759_LINECYC);
|
||||||
static IIO_DEV_ATTR_LENERGY(ade7759_read_40bit, ADE7759_LENERGY);
|
static IIO_DEV_ATTR_LENERGY(ade7759_read_40bit, ADE7759_LENERGY);
|
||||||
static IIO_DEV_ATTR_PGA_GAIN(S_IWUSR | S_IRUGO,
|
static IIO_DEV_ATTR_PGA_GAIN(0644,
|
||||||
ade7759_read_8bit,
|
ade7759_read_8bit,
|
||||||
ade7759_write_8bit,
|
ade7759_write_8bit,
|
||||||
ADE7759_GAIN);
|
ADE7759_GAIN);
|
||||||
static IIO_DEV_ATTR_ACTIVE_POWER_GAIN(S_IWUSR | S_IRUGO,
|
static IIO_DEV_ATTR_ACTIVE_POWER_GAIN(0644,
|
||||||
ade7759_read_16bit,
|
ade7759_read_16bit,
|
||||||
ade7759_write_16bit,
|
ade7759_write_16bit,
|
||||||
ADE7759_APGAIN);
|
ADE7759_APGAIN);
|
||||||
static IIO_DEV_ATTR_CH_OFF(1, S_IWUSR | S_IRUGO,
|
static IIO_DEV_ATTR_CH_OFF(1, 0644,
|
||||||
ade7759_read_8bit,
|
ade7759_read_8bit,
|
||||||
ade7759_write_8bit,
|
ade7759_write_8bit,
|
||||||
ADE7759_CH1OS);
|
ADE7759_CH1OS);
|
||||||
static IIO_DEV_ATTR_CH_OFF(2, S_IWUSR | S_IRUGO,
|
static IIO_DEV_ATTR_CH_OFF(2, 0644,
|
||||||
ade7759_read_8bit,
|
ade7759_read_8bit,
|
||||||
ade7759_write_8bit,
|
ade7759_write_8bit,
|
||||||
ADE7759_CH2OS);
|
ADE7759_CH2OS);
|
||||||
|
@ -458,7 +458,7 @@ static IIO_DEV_ATTR_TEMP_RAW(ade7759_read_8bit);
|
||||||
static IIO_CONST_ATTR(in_temp_offset, "70 C");
|
static IIO_CONST_ATTR(in_temp_offset, "70 C");
|
||||||
static IIO_CONST_ATTR(in_temp_scale, "1 C");
|
static IIO_CONST_ATTR(in_temp_scale, "1 C");
|
||||||
|
|
||||||
static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
|
static IIO_DEV_ATTR_SAMP_FREQ(0644,
|
||||||
ade7759_read_frequency,
|
ade7759_read_frequency,
|
||||||
ade7759_write_frequency);
|
ade7759_write_frequency);
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#define TIM_CCMR1 0x18 /* Capt/Comp 1 Mode Reg */
|
#define TIM_CCMR1 0x18 /* Capt/Comp 1 Mode Reg */
|
||||||
#define TIM_CCMR2 0x1C /* Capt/Comp 2 Mode Reg */
|
#define TIM_CCMR2 0x1C /* Capt/Comp 2 Mode Reg */
|
||||||
#define TIM_CCER 0x20 /* Capt/Comp Enable Reg */
|
#define TIM_CCER 0x20 /* Capt/Comp Enable Reg */
|
||||||
|
#define TIM_CNT 0x24 /* Counter */
|
||||||
#define TIM_PSC 0x28 /* Prescaler */
|
#define TIM_PSC 0x28 /* Prescaler */
|
||||||
#define TIM_ARR 0x2c /* Auto-Reload Register */
|
#define TIM_ARR 0x2c /* Auto-Reload Register */
|
||||||
#define TIM_CCR1 0x34 /* Capt/Comp Register 1 */
|
#define TIM_CCR1 0x34 /* Capt/Comp Register 1 */
|
||||||
|
@ -30,6 +31,7 @@
|
||||||
#define TIM_BDTR 0x44 /* Break and Dead-Time Reg */
|
#define TIM_BDTR 0x44 /* Break and Dead-Time Reg */
|
||||||
|
|
||||||
#define TIM_CR1_CEN BIT(0) /* Counter Enable */
|
#define TIM_CR1_CEN BIT(0) /* Counter Enable */
|
||||||
|
#define TIM_CR1_DIR BIT(4) /* Counter Direction */
|
||||||
#define TIM_CR1_ARPE BIT(7) /* Auto-reload Preload Ena */
|
#define TIM_CR1_ARPE BIT(7) /* Auto-reload Preload Ena */
|
||||||
#define TIM_CR2_MMS (BIT(4) | BIT(5) | BIT(6)) /* Master mode selection */
|
#define TIM_CR2_MMS (BIT(4) | BIT(5) | BIT(6)) /* Master mode selection */
|
||||||
#define TIM_SMCR_SMS (BIT(0) | BIT(1) | BIT(2)) /* Slave mode selection */
|
#define TIM_SMCR_SMS (BIT(0) | BIT(1) | BIT(2)) /* Slave mode selection */
|
||||||
|
|
|
@ -38,6 +38,10 @@
|
||||||
#define SUN6I_GPADC_CTRL1_ADC_CHAN_SELECT(x) (GENMASK(3, 0) & BIT(x))
|
#define SUN6I_GPADC_CTRL1_ADC_CHAN_SELECT(x) (GENMASK(3, 0) & BIT(x))
|
||||||
#define SUN6I_GPADC_CTRL1_ADC_CHAN_MASK GENMASK(3, 0)
|
#define SUN6I_GPADC_CTRL1_ADC_CHAN_MASK GENMASK(3, 0)
|
||||||
|
|
||||||
|
/* TP_CTRL1 bits for sun8i SoCs */
|
||||||
|
#define SUN8I_GPADC_CTRL1_CHOP_TEMP_EN BIT(8)
|
||||||
|
#define SUN8I_GPADC_CTRL1_GPADC_CALI_EN BIT(7)
|
||||||
|
|
||||||
#define SUN4I_GPADC_CTRL2 0x08
|
#define SUN4I_GPADC_CTRL2 0x08
|
||||||
|
|
||||||
#define SUN4I_GPADC_CTRL2_TP_SENSITIVE_ADJUST(x) ((GENMASK(3, 0) & (x)) << 28)
|
#define SUN4I_GPADC_CTRL2_TP_SENSITIVE_ADJUST(x) ((GENMASK(3, 0) & (x)) << 28)
|
||||||
|
|
Загрузка…
Ссылка в новой задаче