Third set of new device support, features and cleanups for IIO in the 4.11 cycle
This also involves a merge of the ib-mid-iio-pwm-4.11 branch from mfd to bring in support for the stm32 timer triggers needed for the buffered features in the stm32 adc driver. New device support: * Amligic Meson SAR ADC - new driver and bindings * cros_ec barometer - new driver * max5481 digital potentiometers - new driver for 5481, 5482, 5483 and 5484 * Renesas GyroADC - a very specific spi offload engine for ADCs - new driver and bindings. * srf08 ultrasonic ranger - new driver, bindings and ABI docs, New features * Qualcomm PM8xxx ADC bindings. - due to a trivial build issues the driver will be following shortly. * stm32 ADC - Triggered buffer mode - Allow use of stm32 timer triggers - Add trigger polarity control - Optional DMA support with bindings update * stx104 - add support for gpio names - support set_multiple callback * tmp007 - optional interrupt support Cleanups * ad7150 - alignment fix. * ad7816 - octal rather than symbolic permissions. * lsm6dsx - allow selection of data ready pin via device tree bindings. * ssp_sensors - use devm_iio_device_register to handle unregister automatically. * stx104 - use devm functions in probe allowing removal or the remove function. - drop unneeded struct stx104_dev * tmp007 - fix the name attribute to be a meaninful description of the part. -----BEGIN PGP SIGNATURE----- iQJFBAABCAAvFiEEbilms4eEBlKRJoGxVIU0mcT0FogFAliSM5IRHGppYzIzQGtl cm5lbC5vcmcACgkQVIU0mcT0FogLaBAApj79bVoAcO+aznbW1ipg1sZEl8617610 60Wq23pXqQRVi8osklnsUC+PBEdQhpGq8ijDMppjJD7ta3UEUBEFDUTAEWnkfugb B65H0XVO2m0ZCyEh7mqb9R9PXK/et1YU1Wj0Q7VTLDqX3oUCg4tPNDI+VBsA5ZFa OWFtDEiSY9GpLiotUi9cGGAx+qt1pOzimukrZkIFwt+UXju4rCySLymjESv++G7+ 3Kqumvjz+BQqWqqEcj2X0SYORDadpTNkS/3z477Kc4b4ykYsuRIHYM+zrDAtziKu Yxd2aggnFkM3BSvk1Uld0WM9xpwBuCMOZVvN8S0QkkNGIvrM/faWsH4UmANkmDeG dtldakTp7rHrWi8bGvzVXswXELANyDL7+SB/MWqJhf3LqLnt5KM0DoFxxGfUWuXG bg3yGwgxxOaUrZJIqxVXGHOiYK+TA3fU/dz0+qp/rbtLV0bbF9TMST0UH5o2+cXy NkisXiAQSWXRV6YMNiMwEoqt5d3SuUJb0bcryMeIPl3bbjc2YBQPo5n/2Q7UWsWt GdFwuw9Y6IvRw61RQRceGjEs5sVIUXm2JnRdOFluJA1YDb3WUfxrA8lL4M08ZCa5 bt+tGtf1SCRcTeourYzn7+xMVvCurRA62p5DOkFKeJ8Ck+tr6eTyP3gE5shdzVrv skwSqrkRUPg= =smLx -----END PGP SIGNATURE----- Merge tag 'iio-for-4.11c' of git://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio into staging-next Jonathan writes: Third set of new device support, features and cleanups for IIO in the 4.11 cycle This also involves a merge of the ib-mid-iio-pwm-4.11 branch from mfd to bring in support for the stm32 timer triggers needed for the buffered features in the stm32 adc driver. New device support: * Amligic Meson SAR ADC - new driver and bindings * cros_ec barometer - new driver * max5481 digital potentiometers - new driver for 5481, 5482, 5483 and 5484 * Renesas GyroADC - a very specific spi offload engine for ADCs - new driver and bindings. * srf08 ultrasonic ranger - new driver, bindings and ABI docs, New features * Qualcomm PM8xxx ADC bindings. - due to a trivial build issues the driver will be following shortly. * stm32 ADC - Triggered buffer mode - Allow use of stm32 timer triggers - Add trigger polarity control - Optional DMA support with bindings update * stx104 - add support for gpio names - support set_multiple callback * tmp007 - optional interrupt support Cleanups * ad7150 - alignment fix. * ad7816 - octal rather than symbolic permissions. * lsm6dsx - allow selection of data ready pin via device tree bindings. * ssp_sensors - use devm_iio_device_register to handle unregister automatically. * stx104 - use devm functions in probe allowing removal or the remove function. - drop unneeded struct stx104_dev * tmp007 - fix the name attribute to be a meaninful description of the part.
This commit is contained in:
commit
fa6a8adf3a
18
Documentation/ABI/testing/sysfs-bus-iio-adc-stm32
Normal file
18
Documentation/ABI/testing/sysfs-bus-iio-adc-stm32
Normal file
@ -0,0 +1,18 @@
|
||||
What: /sys/bus/iio/devices/triggerX/trigger_polarity
|
||||
KernelVersion: 4.11
|
||||
Contact: fabrice.gasnier@st.com
|
||||
Description:
|
||||
The STM32 ADC can be configured to use external trigger sources
|
||||
(e.g. timers, pwm or exti gpio). Then, it can be tuned to start
|
||||
conversions on external trigger by either:
|
||||
- "rising-edge"
|
||||
- "falling-edge"
|
||||
- "both-edges".
|
||||
Reading returns current trigger polarity.
|
||||
Writing value before enabling conversions sets trigger polarity.
|
||||
|
||||
What: /sys/bus/iio/devices/triggerX/trigger_polarity_available
|
||||
KernelVersion: 4.11
|
||||
Contact: fabrice.gasnier@st.com
|
||||
Description:
|
||||
List all available trigger_polarity settings.
|
22
Documentation/ABI/testing/sysfs-bus-iio-distance-srf08
Normal file
22
Documentation/ABI/testing/sysfs-bus-iio-distance-srf08
Normal file
@ -0,0 +1,22 @@
|
||||
What /sys/bus/iio/devices/iio:deviceX/sensor_sensitivity
|
||||
Date: January 2017
|
||||
KernelVersion: 4.11
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Show or set the gain boost of the amp, from 0-31 range.
|
||||
default 31
|
||||
|
||||
What /sys/bus/iio/devices/iio:deviceX/sensor_max_range
|
||||
Date: January 2017
|
||||
KernelVersion: 4.11
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Show or set the maximum range between the sensor and the
|
||||
first object echoed in meters. Default value is 6.020.
|
||||
This setting limits the time the driver is waiting for a
|
||||
echo.
|
||||
Showing the range of available values is represented as the
|
||||
minimum value, the step and the maximum value, all enclosed
|
||||
in square brackets.
|
||||
Example:
|
||||
[0.043 0.043 11.008]
|
29
Documentation/ABI/testing/sysfs-bus-iio-timer-stm32
Normal file
29
Documentation/ABI/testing/sysfs-bus-iio-timer-stm32
Normal file
@ -0,0 +1,29 @@
|
||||
What: /sys/bus/iio/devices/triggerX/master_mode_available
|
||||
KernelVersion: 4.11
|
||||
Contact: benjamin.gaignard@st.com
|
||||
Description:
|
||||
Reading returns the list possible master modes which are:
|
||||
- "reset" : The UG bit from the TIMx_EGR register is used as trigger output (TRGO).
|
||||
- "enable" : The Counter Enable signal CNT_EN is used as trigger output.
|
||||
- "update" : The update event is selected as trigger output.
|
||||
For instance a master timer can then be used as a prescaler for a slave timer.
|
||||
- "compare_pulse" : The trigger output send a positive pulse when the CC1IF flag is to be set.
|
||||
- "OC1REF" : OC1REF signal is used as trigger output.
|
||||
- "OC2REF" : OC2REF signal is used as trigger output.
|
||||
- "OC3REF" : OC3REF signal is used as trigger output.
|
||||
- "OC4REF" : OC4REF signal is used as trigger output.
|
||||
|
||||
What: /sys/bus/iio/devices/triggerX/master_mode
|
||||
KernelVersion: 4.11
|
||||
Contact: benjamin.gaignard@st.com
|
||||
Description:
|
||||
Reading returns the current master modes.
|
||||
Writing set the master mode
|
||||
|
||||
What: /sys/bus/iio/devices/triggerX/sampling_frequency
|
||||
KernelVersion: 4.11
|
||||
Contact: benjamin.gaignard@st.com
|
||||
Description:
|
||||
Reading returns the current sampling frequency.
|
||||
Writing an value different of 0 set and start sampling.
|
||||
Writing 0 stop sampling.
|
@ -36,6 +36,7 @@ dallas,ds1775 Tiny Digital Thermometer and Thermostat
|
||||
dallas,ds3232 Extremely Accurate I²C RTC with Integrated Crystal and SRAM
|
||||
dallas,ds4510 CPU Supervisor with Nonvolatile Memory and Programmable I/O
|
||||
dallas,ds75 Digital Thermometer and Thermostat
|
||||
devantech,srf08 Devantech SRF08 ultrasonic ranger
|
||||
dlg,da9053 DA9053: flexible system level PMIC with multicore support
|
||||
dlg,da9063 DA9063: system PMIC for quad-core application processors
|
||||
domintech,dmard09 DMARD09: 3-axis Accelerometer
|
||||
|
@ -0,0 +1,32 @@
|
||||
* Amlogic Meson SAR (Successive Approximation Register) A/D converter
|
||||
|
||||
Required properties:
|
||||
- compatible: depending on the SoC this should be one of:
|
||||
- "amlogic,meson-gxbb-saradc" for GXBB
|
||||
- "amlogic,meson-gxl-saradc" for GXL
|
||||
- "amlogic,meson-gxm-saradc" for GXM
|
||||
along with the generic "amlogic,meson-saradc"
|
||||
- reg: the physical base address and length of the registers
|
||||
- clocks: phandle and clock identifier (see clock-names)
|
||||
- clock-names: mandatory clocks:
|
||||
- "clkin" for the reference clock (typically XTAL)
|
||||
- "core" for the SAR ADC core clock
|
||||
optional clocks:
|
||||
- "sana" for the analog clock
|
||||
- "adc_clk" for the ADC (sampling) clock
|
||||
- "adc_sel" for the ADC (sampling) clock mux
|
||||
- vref-supply: the regulator supply for the ADC reference voltage
|
||||
- #io-channel-cells: must be 1, see ../iio-bindings.txt
|
||||
|
||||
Example:
|
||||
saradc: adc@8680 {
|
||||
compatible = "amlogic,meson-gxl-saradc", "amlogic,meson-saradc";
|
||||
#io-channel-cells = <1>;
|
||||
reg = <0x0 0x8680 0x0 0x34>;
|
||||
clocks = <&xtal>,
|
||||
<&clkc CLKID_SAR_ADC>,
|
||||
<&clkc CLKID_SANA>,
|
||||
<&clkc CLKID_SAR_ADC_CLK>,
|
||||
<&clkc CLKID_SAR_ADC_SEL>;
|
||||
clock-names = "clkin", "core", "sana", "adc_clk", "adc_sel";
|
||||
};
|
149
Documentation/devicetree/bindings/iio/adc/qcom,pm8xxx-xoadc.txt
Normal file
149
Documentation/devicetree/bindings/iio/adc/qcom,pm8xxx-xoadc.txt
Normal file
@ -0,0 +1,149 @@
|
||||
Qualcomm's PM8xxx voltage XOADC
|
||||
|
||||
The Qualcomm PM8xxx PMICs contain a HK/XO ADC (Housekeeping/Crystal
|
||||
oscillator ADC) encompassing PM8018, PM8038, PM8058 and PM8921.
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: should be one of:
|
||||
"qcom,pm8018-adc"
|
||||
"qcom,pm8038-adc"
|
||||
"qcom,pm8058-adc"
|
||||
"qcom,pm8921-adc"
|
||||
|
||||
- reg: should contain the ADC base address in the PMIC, typically
|
||||
0x197.
|
||||
|
||||
- xoadc-ref-supply: should reference a regulator that can supply
|
||||
a reference voltage on demand. The reference voltage may vary
|
||||
with PMIC variant but is typically something like 2.2 or 1.8V.
|
||||
|
||||
The following required properties are standard for IO channels, see
|
||||
iio-bindings.txt for more details:
|
||||
|
||||
- #address-cells: should be set to <1>
|
||||
|
||||
- #size-cells: should be set to <0>
|
||||
|
||||
- #io-channel-cells: should be set to <1>
|
||||
|
||||
- interrupts: should refer to the parent PMIC interrupt controller
|
||||
and reference the proper ADC interrupt.
|
||||
|
||||
Required subnodes:
|
||||
|
||||
The ADC channels are configured as subnodes of the ADC. Since some of
|
||||
them are used for calibrating the ADC, these nodes are compulsory:
|
||||
|
||||
adc-channel@c {
|
||||
reg = <0x0c>;
|
||||
};
|
||||
|
||||
adc-channel@d {
|
||||
reg = <0x0d>;
|
||||
};
|
||||
|
||||
adc-channel@f {
|
||||
reg = <0x0f>;
|
||||
};
|
||||
|
||||
These three nodes are used for absolute and ratiometric calibration
|
||||
and only need to have these reg values: they are by hardware definition
|
||||
1:1 ratio converters that sample 625, 1250 and 0 milliV and create
|
||||
an interpolation calibration for all other ADCs.
|
||||
|
||||
Optional subnodes: any channels other than channel 0x0c, 0x0d and
|
||||
0x0f are optional.
|
||||
|
||||
Required channel node properties:
|
||||
|
||||
- reg: should contain the hardware channel number in the range
|
||||
0 .. 0x0f (4 bits). The hardware only supports 16 channels.
|
||||
|
||||
Optional channel node properties:
|
||||
|
||||
- qcom,decimation:
|
||||
Value type: <u32>
|
||||
Definition: This parameter is used to decrease the ADC sampling rate.
|
||||
Quicker measurements can be made by reducing the decimation ratio.
|
||||
Valid values are 512, 1024, 2048, 4096.
|
||||
If the property is not found, a default value of 512 will be used.
|
||||
|
||||
- qcom,ratiometric:
|
||||
Value type: <u32>
|
||||
Definition: Channel calibration type. If this property is specified
|
||||
VADC will use a special voltage references for channel
|
||||
calibration. The available references are specified in the
|
||||
as a u32 value setting (see below) and it is compulsory
|
||||
to also specify this reference if ratiometric calibration
|
||||
is selected.
|
||||
|
||||
If the property is not found, the channel will be
|
||||
calibrated with the 0.625V and 1.25V reference channels, also
|
||||
known as an absolute calibration.
|
||||
The reference voltage pairs when using ratiometric calibration:
|
||||
0 = XO_IN/XOADC_GND
|
||||
1 = PMIC_IN/XOADC_GND
|
||||
2 = PMIC_IN/BMS_CSP
|
||||
3 (invalid)
|
||||
4 = XOADC_GND/XOADC_GND
|
||||
5 = XOADC_VREF/XOADC_GND
|
||||
|
||||
Example:
|
||||
|
||||
xoadc: xoadc@197 {
|
||||
compatible = "qcom,pm8058-adc";
|
||||
reg = <0x197>;
|
||||
interrupt-parent = <&pm8058>;
|
||||
interrupts = <76 1>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
#io-channel-cells = <1>;
|
||||
|
||||
vcoin: adc-channel@0 {
|
||||
reg = <0x00>;
|
||||
};
|
||||
vbat: adc-channel@1 {
|
||||
reg = <0x01>;
|
||||
};
|
||||
dcin: adc-channel@2 {
|
||||
reg = <0x02>;
|
||||
};
|
||||
ichg: adc-channel@3 {
|
||||
reg = <0x03>;
|
||||
};
|
||||
vph_pwr: adc-channel@4 {
|
||||
reg = <0x04>;
|
||||
};
|
||||
usb_vbus: adc-channel@a {
|
||||
reg = <0x0a>;
|
||||
};
|
||||
die_temp: adc-channel@b {
|
||||
reg = <0x0b>;
|
||||
};
|
||||
ref_625mv: adc-channel@c {
|
||||
reg = <0x0c>;
|
||||
};
|
||||
ref_1250mv: adc-channel@d {
|
||||
reg = <0x0d>;
|
||||
};
|
||||
ref_325mv: adc-channel@e {
|
||||
reg = <0x0e>;
|
||||
};
|
||||
ref_muxoff: adc-channel@f {
|
||||
reg = <0x0f>;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
/* IIO client node */
|
||||
iio-hwmon {
|
||||
compatible = "iio-hwmon";
|
||||
io-channels = <&xoadc 0x01>, /* Battery */
|
||||
<&xoadc 0x02>, /* DC in (charger) */
|
||||
<&xoadc 0x04>, /* VPH the main system voltage */
|
||||
<&xoadc 0x0b>, /* Die temperature */
|
||||
<&xoadc 0x0c>, /* Reference voltage 1.25V */
|
||||
<&xoadc 0x0d>, /* Reference voltage 0.625V */
|
||||
<&xoadc 0x0e>; /* Reference voltage 0.325V */
|
||||
};
|
@ -0,0 +1,99 @@
|
||||
* Renesas RCar GyroADC device driver
|
||||
|
||||
The GyroADC block is a reduced SPI block with up to 8 chipselect lines,
|
||||
which supports the SPI protocol of a selected few SPI ADCs. The SPI ADCs
|
||||
are sampled by the GyroADC block in a round-robin fashion and the result
|
||||
presented in the GyroADC registers.
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "<soc-specific>", "renesas,rcar-gyroadc".
|
||||
The <soc-specific> should be one of:
|
||||
renesas,r8a7791-gyroadc - for the GyroADC block present
|
||||
in r8a7791 SoC
|
||||
renesas,r8a7792-gyroadc - for the GyroADC with interrupt
|
||||
block present in r8a7792 SoC
|
||||
- reg: Address and length of the register set for the device
|
||||
- clocks: References to all the clocks specified in the clock-names
|
||||
property as specified in
|
||||
Documentation/devicetree/bindings/clock/clock-bindings.txt.
|
||||
- clock-names: Shall contain "fck" and "if". The "fck" is the GyroADC block
|
||||
clock, the "if" is the interface clock.
|
||||
- power-domains: Must contain a reference to the PM domain, if available.
|
||||
- #address-cells: Should be <1> (setting for the subnodes) for all ADCs
|
||||
except for "fujitsu,mb88101a". Should be <0> (setting for
|
||||
only subnode) for "fujitsu,mb88101a".
|
||||
- #size-cells: Should be <0> (setting for the subnodes)
|
||||
|
||||
Sub-nodes:
|
||||
You must define subnode(s) which select the connected ADC type and reference
|
||||
voltage for the GyroADC channels.
|
||||
|
||||
Required properties for subnodes:
|
||||
- compatible: Should be either of:
|
||||
"fujitsu,mb88101a"
|
||||
- Fujitsu MB88101A compatible mode,
|
||||
12bit sampling, up to 4 channels can be sampled in
|
||||
round-robin fashion. One Fujitsu chip supplies four
|
||||
GyroADC channels with data as it contains four ADCs
|
||||
on the chip and thus for 4-channel operation, single
|
||||
MB88101A is required. The Cx chipselect lines of the
|
||||
MB88101A connect directly to two CHS lines of the
|
||||
GyroADC, no demuxer is required. The data out line
|
||||
of each MB88101A connects to a shared input pin of
|
||||
the GyroADC.
|
||||
"ti,adcs7476" or "ti,adc121" or "adi,ad7476"
|
||||
- TI ADCS7476 / TI ADC121 / ADI AD7476 compatible mode,
|
||||
15bit sampling, up to 8 channels can be sampled in
|
||||
round-robin fashion. One TI/ADI chip supplies single
|
||||
ADC channel with data, thus for 8-channel operation,
|
||||
8 chips are required. A 3:8 chipselect demuxer is
|
||||
required to connect the nCS line of the TI/ADI chips
|
||||
to the GyroADC, while MISO line of each TI/ADI ADC
|
||||
connects to a shared input pin of the GyroADC.
|
||||
"maxim,max1162" or "maxim,max11100"
|
||||
- Maxim MAX1162 / Maxim MAX11100 compatible mode,
|
||||
16bit sampling, up to 8 channels can be sampled in
|
||||
round-robin fashion. One Maxim chip supplies single
|
||||
ADC channel with data, thus for 8-channel operation,
|
||||
8 chips are required. A 3:8 chipselect demuxer is
|
||||
required to connect the nCS line of the MAX chips
|
||||
to the GyroADC, while MISO line of each Maxim ADC
|
||||
connects to a shared input pin of the GyroADC.
|
||||
- reg: Should be the number of the analog input. Should be present
|
||||
for all ADCs except "fujitsu,mb88101a".
|
||||
- vref-supply: Reference to the channel reference voltage regulator.
|
||||
|
||||
Example:
|
||||
vref_max1162: regulator-vref-max1162 {
|
||||
compatible = "regulator-fixed";
|
||||
|
||||
regulator-name = "MAX1162 Vref";
|
||||
regulator-min-microvolt = <4096000>;
|
||||
regulator-max-microvolt = <4096000>;
|
||||
};
|
||||
|
||||
adc@e6e54000 {
|
||||
compatible = "renesas,r8a7791-gyroadc", "renesas,rcar-gyroadc";
|
||||
reg = <0 0xe6e54000 0 64>;
|
||||
clocks = <&mstp9_clks R8A7791_CLK_GYROADC>, <&clk_65m>;
|
||||
clock-names = "fck", "if";
|
||||
power-domains = <&sysc R8A7791_PD_ALWAYS_ON>;
|
||||
|
||||
pinctrl-0 = <&adc_pins>;
|
||||
pinctrl-names = "default";
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
adc@0 {
|
||||
reg = <0>;
|
||||
compatible = "maxim,max1162";
|
||||
vref-supply = <&vref_max1162>;
|
||||
};
|
||||
|
||||
adc@1 {
|
||||
reg = <1>;
|
||||
compatible = "maxim,max1162";
|
||||
vref-supply = <&vref_max1162>;
|
||||
};
|
||||
};
|
@ -53,6 +53,11 @@ Required properties:
|
||||
- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers" in
|
||||
Documentation/devicetree/bindings/iio/iio-bindings.txt
|
||||
|
||||
Optional properties:
|
||||
- dmas: Phandle to dma channel for this ADC instance.
|
||||
See ../../dma/dma.txt for details.
|
||||
- dma-names: Must be "rx" when dmas property is being used.
|
||||
|
||||
Example:
|
||||
adc: adc@40012000 {
|
||||
compatible = "st,stm32f4-adc-core";
|
||||
@ -77,6 +82,8 @@ Example:
|
||||
interrupt-parent = <&adc>;
|
||||
interrupts = <0>;
|
||||
st,adc-channels = <8>;
|
||||
dmas = <&dma2 0 0 0x400 0x0>;
|
||||
dma-names = "rx";
|
||||
};
|
||||
...
|
||||
other adc child nodes follow...
|
||||
|
@ -7,6 +7,8 @@ Required properties:
|
||||
- reg: i2c address of the sensor / spi cs line
|
||||
|
||||
Optional properties:
|
||||
- st,drdy-int-pin: the pin on the package that will be used to signal
|
||||
"data ready" (valid values: 1 or 2).
|
||||
- interrupt-parent: should be the phandle for the interrupt controller
|
||||
- interrupts: interrupt mapping for IRQ. It should be configured with
|
||||
flags IRQ_TYPE_LEVEL_HIGH or IRQ_TYPE_EDGE_RISING.
|
||||
|
@ -0,0 +1,23 @@
|
||||
* Maxim Linear-Taper Digital Potentiometer MAX5481-MAX5484
|
||||
|
||||
The node for this driver must be a child node of a SPI controller, hence
|
||||
all mandatory properties described in
|
||||
|
||||
Documentation/devicetree/bindings/spi/spi-bus.txt
|
||||
|
||||
must be specified.
|
||||
|
||||
Required properties:
|
||||
- compatible: Must be one of the following, depending on the
|
||||
model:
|
||||
"maxim,max5481"
|
||||
"maxim,max5482"
|
||||
"maxim,max5483"
|
||||
"maxim,max5484"
|
||||
|
||||
Example:
|
||||
max548x: max548x@0 {
|
||||
compatible = "maxim,max5482";
|
||||
spi-max-frequency = <7000000>;
|
||||
reg = <0>; /* chip-select */
|
||||
};
|
@ -18,10 +18,18 @@ Required properties:
|
||||
1 SDA 0x46
|
||||
1 SCL 0x47
|
||||
|
||||
Optional properties:
|
||||
|
||||
- interrupt-parent: should be the phandle for the interrupt controller
|
||||
|
||||
- interrupts: interrupt mapping for GPIO IRQ (level active low)
|
||||
|
||||
Example:
|
||||
|
||||
tmp007@40 {
|
||||
compatible = "ti,tmp007";
|
||||
reg = <0x40>;
|
||||
interrupt-parent = <&gpio0>;
|
||||
interrupts = <5 0x08>;
|
||||
};
|
||||
|
||||
|
@ -0,0 +1,23 @@
|
||||
STMicroelectronics STM32 Timers IIO timer bindings
|
||||
|
||||
Must be a sub-node of an STM32 Timers device tree node.
|
||||
See ../mfd/stm32-timers.txt for details about the parent node.
|
||||
|
||||
Required parameters:
|
||||
- compatible: Must be "st,stm32-timer-trigger".
|
||||
- reg: Identify trigger hardware block.
|
||||
|
||||
Example:
|
||||
timers@40010000 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "st,stm32-timers";
|
||||
reg = <0x40010000 0x400>;
|
||||
clocks = <&rcc 0 160>;
|
||||
clock-names = "clk_int";
|
||||
|
||||
timer@0 {
|
||||
compatible = "st,stm32-timer-trigger";
|
||||
reg = <0>;
|
||||
};
|
||||
};
|
46
Documentation/devicetree/bindings/mfd/stm32-timers.txt
Normal file
46
Documentation/devicetree/bindings/mfd/stm32-timers.txt
Normal file
@ -0,0 +1,46 @@
|
||||
STM32 Timers driver bindings
|
||||
|
||||
This IP provides 3 types of timer along with PWM functionality:
|
||||
- advanced-control timers consist of a 16-bit auto-reload counter driven by a programmable
|
||||
prescaler, break input feature, PWM outputs and complementary PWM ouputs channels.
|
||||
- general-purpose timers consist of a 16-bit or 32-bit auto-reload counter driven by a
|
||||
programmable prescaler and PWM outputs.
|
||||
- basic timers consist of a 16-bit auto-reload counter driven by a programmable prescaler.
|
||||
|
||||
Required parameters:
|
||||
- compatible: must be "st,stm32-timers"
|
||||
|
||||
- reg: Physical base address and length of the controller's
|
||||
registers.
|
||||
- clock-names: Set to "int".
|
||||
- clocks: Phandle to the clock used by the timer module.
|
||||
For Clk properties, please refer to ../clock/clock-bindings.txt
|
||||
|
||||
Optional parameters:
|
||||
- resets: Phandle to the parent reset controller.
|
||||
See ../reset/st,stm32-rcc.txt
|
||||
|
||||
Optional subnodes:
|
||||
- pwm: See ../pwm/pwm-stm32.txt
|
||||
- timer: See ../iio/timer/stm32-timer-trigger.txt
|
||||
|
||||
Example:
|
||||
timers@40010000 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "st,stm32-timers";
|
||||
reg = <0x40010000 0x400>;
|
||||
clocks = <&rcc 0 160>;
|
||||
clock-names = "clk_int";
|
||||
|
||||
pwm {
|
||||
compatible = "st,stm32-pwm";
|
||||
pinctrl-0 = <&pwm1_pins>;
|
||||
pinctrl-names = "default";
|
||||
};
|
||||
|
||||
timer@0 {
|
||||
compatible = "st,stm32-timer-trigger";
|
||||
reg = <0>;
|
||||
};
|
||||
};
|
35
Documentation/devicetree/bindings/pwm/pwm-stm32.txt
Normal file
35
Documentation/devicetree/bindings/pwm/pwm-stm32.txt
Normal file
@ -0,0 +1,35 @@
|
||||
STMicroelectronics STM32 Timers PWM bindings
|
||||
|
||||
Must be a sub-node of an STM32 Timers device tree node.
|
||||
See ../mfd/stm32-timers.txt for details about the parent node.
|
||||
|
||||
Required parameters:
|
||||
- compatible: Must be "st,stm32-pwm".
|
||||
- pinctrl-names: Set to "default".
|
||||
- pinctrl-0: List of phandles pointing to pin configuration nodes for PWM module.
|
||||
For Pinctrl properties see ../pinctrl/pinctrl-bindings.txt
|
||||
|
||||
Optional parameters:
|
||||
- st,breakinput: One or two <index level filter> to describe break input configurations.
|
||||
"index" indicates on which break input (0 or 1) the configuration
|
||||
should be applied.
|
||||
"level" gives the active level (0=low or 1=high) of the input signal
|
||||
for this configuration.
|
||||
"filter" gives the filtering value to be applied.
|
||||
|
||||
Example:
|
||||
timers@40010000 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "st,stm32-timers";
|
||||
reg = <0x40010000 0x400>;
|
||||
clocks = <&rcc 0 160>;
|
||||
clock-names = "clk_int";
|
||||
|
||||
pwm {
|
||||
compatible = "st,stm32-pwm";
|
||||
pinctrl-0 = <&pwm1_pins>;
|
||||
pinctrl-names = "default";
|
||||
st,breakinput = <0 1 5>;
|
||||
};
|
||||
};
|
@ -76,6 +76,7 @@ dallas Maxim Integrated Products (formerly Dallas Semiconductor)
|
||||
davicom DAVICOM Semiconductor, Inc.
|
||||
delta Delta Electronics, Inc.
|
||||
denx Denx Software Engineering
|
||||
devantech Devantech, Ltd.
|
||||
digi Digi International Inc.
|
||||
digilent Diglent, Inc.
|
||||
dlg Dialog Semiconductor
|
||||
|
@ -10439,6 +10439,12 @@ L: linux-renesas-soc@vger.kernel.org
|
||||
F: drivers/net/ethernet/renesas/
|
||||
F: include/linux/sh_eth.h
|
||||
|
||||
RENESAS R-CAR GYROADC DRIVER
|
||||
M: Marek Vasut <marek.vasut@gmail.com>
|
||||
L: linux-iio@vger.kernel.org
|
||||
S: Supported
|
||||
F: drivers/iio/adc/rcar_gyro_adc.c
|
||||
|
||||
RENESAS USB2 PHY DRIVER
|
||||
M: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
|
||||
L: linux-renesas-soc@vger.kernel.org
|
||||
|
@ -136,7 +136,7 @@ static int ssp_accel_probe(struct platform_device *pdev)
|
||||
|
||||
platform_set_drvdata(pdev, indio_dev);
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
ret = devm_iio_device_register(&pdev->dev, indio_dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@ -146,21 +146,11 @@ static int ssp_accel_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ssp_accel_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver ssp_accel_driver = {
|
||||
.driver = {
|
||||
.name = SSP_ACCEL_NAME,
|
||||
},
|
||||
.probe = ssp_accel_probe,
|
||||
.remove = ssp_accel_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(ssp_accel_driver);
|
||||
|
@ -399,6 +399,18 @@ config MEN_Z188_ADC
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called men_z188_adc.
|
||||
|
||||
config MESON_SARADC
|
||||
tristate "Amlogic Meson SAR ADC driver"
|
||||
default ARCH_MESON
|
||||
depends on OF && COMMON_CLK && (ARCH_MESON || COMPILE_TEST)
|
||||
select REGMAP_MMIO
|
||||
help
|
||||
Say yes here to build support for the SAR ADC found in Amlogic Meson
|
||||
SoCs.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called meson_saradc.
|
||||
|
||||
config MXS_LRADC
|
||||
tristate "Freescale i.MX23/i.MX28 LRADC"
|
||||
depends on (ARCH_MXS || COMPILE_TEST) && HAS_IOMEM
|
||||
@ -458,6 +470,19 @@ config QCOM_SPMI_VADC
|
||||
To compile this driver as a module, choose M here: the module will
|
||||
be called qcom-spmi-vadc.
|
||||
|
||||
config RCAR_GYRO_ADC
|
||||
tristate "Renesas R-Car GyroADC driver"
|
||||
depends on ARCH_RCAR_GEN2 || (ARM && COMPILE_TEST)
|
||||
help
|
||||
Say yes here to build support for the GyroADC found in Renesas
|
||||
R-Car Gen2 SoCs. This block is a simple SPI offload engine for
|
||||
reading data out of attached compatible ADCs in a round-robin
|
||||
fashion. Up to 4 or 8 ADC channels are supported by this block,
|
||||
depending on which ADCs are attached.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called rcar-gyroadc.
|
||||
|
||||
config ROCKCHIP_SARADC
|
||||
tristate "Rockchip SARADC driver"
|
||||
depends on ARCH_ROCKCHIP || (ARM && COMPILE_TEST)
|
||||
@ -472,8 +497,13 @@ config ROCKCHIP_SARADC
|
||||
config STM32_ADC_CORE
|
||||
tristate "STMicroelectronics STM32 adc core"
|
||||
depends on ARCH_STM32 || COMPILE_TEST
|
||||
depends on HAS_DMA
|
||||
depends on OF
|
||||
depends on REGULATOR
|
||||
select IIO_BUFFER
|
||||
select MFD_STM32_TIMERS
|
||||
select IIO_STM32_TIMER_TRIGGER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
Select this option to enable the core driver for STMicroelectronics
|
||||
STM32 analog-to-digital converter (ADC).
|
||||
|
@ -38,11 +38,13 @@ obj-$(CONFIG_MCP320X) += mcp320x.o
|
||||
obj-$(CONFIG_MCP3422) += mcp3422.o
|
||||
obj-$(CONFIG_MEDIATEK_MT6577_AUXADC) += mt6577_auxadc.o
|
||||
obj-$(CONFIG_MEN_Z188_ADC) += men_z188_adc.o
|
||||
obj-$(CONFIG_MESON_SARADC) += meson_saradc.o
|
||||
obj-$(CONFIG_MXS_LRADC) += mxs-lradc.o
|
||||
obj-$(CONFIG_NAU7802) += nau7802.o
|
||||
obj-$(CONFIG_PALMAS_GPADC) += palmas_gpadc.o
|
||||
obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o
|
||||
obj-$(CONFIG_QCOM_SPMI_VADC) += qcom-spmi-vadc.o
|
||||
obj-$(CONFIG_RCAR_GYRO_ADC) += rcar-gyroadc.o
|
||||
obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
|
||||
obj-$(CONFIG_STX104) += stx104.o
|
||||
obj-$(CONFIG_STM32_ADC_CORE) += stm32-adc-core.o
|
||||
|
922
drivers/iio/adc/meson_saradc.c
Normal file
922
drivers/iio/adc/meson_saradc.c
Normal file
@ -0,0 +1,922 @@
|
||||
/*
|
||||
* Amlogic Meson Successive Approximation Register (SAR) A/D Converter
|
||||
*
|
||||
* Copyright (C) 2017 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#define MESON_SAR_ADC_REG0 0x00
|
||||
#define MESON_SAR_ADC_REG0_PANEL_DETECT BIT(31)
|
||||
#define MESON_SAR_ADC_REG0_BUSY_MASK GENMASK(30, 28)
|
||||
#define MESON_SAR_ADC_REG0_DELTA_BUSY BIT(30)
|
||||
#define MESON_SAR_ADC_REG0_AVG_BUSY BIT(29)
|
||||
#define MESON_SAR_ADC_REG0_SAMPLE_BUSY BIT(28)
|
||||
#define MESON_SAR_ADC_REG0_FIFO_FULL BIT(27)
|
||||
#define MESON_SAR_ADC_REG0_FIFO_EMPTY BIT(26)
|
||||
#define MESON_SAR_ADC_REG0_FIFO_COUNT_MASK GENMASK(25, 21)
|
||||
#define MESON_SAR_ADC_REG0_ADC_BIAS_CTRL_MASK GENMASK(20, 19)
|
||||
#define MESON_SAR_ADC_REG0_CURR_CHAN_ID_MASK GENMASK(18, 16)
|
||||
#define MESON_SAR_ADC_REG0_ADC_TEMP_SEN_SEL BIT(15)
|
||||
#define MESON_SAR_ADC_REG0_SAMPLING_STOP BIT(14)
|
||||
#define MESON_SAR_ADC_REG0_CHAN_DELTA_EN_MASK GENMASK(13, 12)
|
||||
#define MESON_SAR_ADC_REG0_DETECT_IRQ_POL BIT(10)
|
||||
#define MESON_SAR_ADC_REG0_DETECT_IRQ_EN BIT(9)
|
||||
#define MESON_SAR_ADC_REG0_FIFO_CNT_IRQ_MASK GENMASK(8, 4)
|
||||
#define MESON_SAR_ADC_REG0_FIFO_IRQ_EN BIT(3)
|
||||
#define MESON_SAR_ADC_REG0_SAMPLING_START BIT(2)
|
||||
#define MESON_SAR_ADC_REG0_CONTINUOUS_EN BIT(1)
|
||||
#define MESON_SAR_ADC_REG0_SAMPLE_ENGINE_ENABLE BIT(0)
|
||||
|
||||
#define MESON_SAR_ADC_CHAN_LIST 0x04
|
||||
#define MESON_SAR_ADC_CHAN_LIST_MAX_INDEX_MASK GENMASK(26, 24)
|
||||
#define MESON_SAR_ADC_CHAN_LIST_ENTRY_MASK(_chan) \
|
||||
(GENMASK(2, 0) << ((_chan) * 3))
|
||||
|
||||
#define MESON_SAR_ADC_AVG_CNTL 0x08
|
||||
#define MESON_SAR_ADC_AVG_CNTL_AVG_MODE_SHIFT(_chan) \
|
||||
(16 + ((_chan) * 2))
|
||||
#define MESON_SAR_ADC_AVG_CNTL_AVG_MODE_MASK(_chan) \
|
||||
(GENMASK(17, 16) << ((_chan) * 2))
|
||||
#define MESON_SAR_ADC_AVG_CNTL_NUM_SAMPLES_SHIFT(_chan) \
|
||||
(0 + ((_chan) * 2))
|
||||
#define MESON_SAR_ADC_AVG_CNTL_NUM_SAMPLES_MASK(_chan) \
|
||||
(GENMASK(1, 0) << ((_chan) * 2))
|
||||
|
||||
#define MESON_SAR_ADC_REG3 0x0c
|
||||
#define MESON_SAR_ADC_REG3_CNTL_USE_SC_DLY BIT(31)
|
||||
#define MESON_SAR_ADC_REG3_CLK_EN BIT(30)
|
||||
#define MESON_SAR_ADC_REG3_BL30_INITIALIZED BIT(28)
|
||||
#define MESON_SAR_ADC_REG3_CTRL_CONT_RING_COUNTER_EN BIT(27)
|
||||
#define MESON_SAR_ADC_REG3_CTRL_SAMPLING_CLOCK_PHASE BIT(26)
|
||||
#define MESON_SAR_ADC_REG3_CTRL_CHAN7_MUX_SEL_MASK GENMASK(25, 23)
|
||||
#define MESON_SAR_ADC_REG3_DETECT_EN BIT(22)
|
||||
#define MESON_SAR_ADC_REG3_ADC_EN BIT(21)
|
||||
#define MESON_SAR_ADC_REG3_PANEL_DETECT_COUNT_MASK GENMASK(20, 18)
|
||||
#define MESON_SAR_ADC_REG3_PANEL_DETECT_FILTER_TB_MASK GENMASK(17, 16)
|
||||
#define MESON_SAR_ADC_REG3_ADC_CLK_DIV_SHIFT 10
|
||||
#define MESON_SAR_ADC_REG3_ADC_CLK_DIV_WIDTH 5
|
||||
#define MESON_SAR_ADC_REG3_BLOCK_DLY_SEL_MASK GENMASK(9, 8)
|
||||
#define MESON_SAR_ADC_REG3_BLOCK_DLY_MASK GENMASK(7, 0)
|
||||
|
||||
#define MESON_SAR_ADC_DELAY 0x10
|
||||
#define MESON_SAR_ADC_DELAY_INPUT_DLY_SEL_MASK GENMASK(25, 24)
|
||||
#define MESON_SAR_ADC_DELAY_BL30_BUSY BIT(15)
|
||||
#define MESON_SAR_ADC_DELAY_KERNEL_BUSY BIT(14)
|
||||
#define MESON_SAR_ADC_DELAY_INPUT_DLY_CNT_MASK GENMASK(23, 16)
|
||||
#define MESON_SAR_ADC_DELAY_SAMPLE_DLY_SEL_MASK GENMASK(9, 8)
|
||||
#define MESON_SAR_ADC_DELAY_SAMPLE_DLY_CNT_MASK GENMASK(7, 0)
|
||||
|
||||
#define MESON_SAR_ADC_LAST_RD 0x14
|
||||
#define MESON_SAR_ADC_LAST_RD_LAST_CHANNEL1_MASK GENMASK(23, 16)
|
||||
#define MESON_SAR_ADC_LAST_RD_LAST_CHANNEL0_MASK GENMASK(9, 0)
|
||||
|
||||
#define MESON_SAR_ADC_FIFO_RD 0x18
|
||||
#define MESON_SAR_ADC_FIFO_RD_CHAN_ID_MASK GENMASK(14, 12)
|
||||
#define MESON_SAR_ADC_FIFO_RD_SAMPLE_VALUE_MASK GENMASK(11, 0)
|
||||
|
||||
#define MESON_SAR_ADC_AUX_SW 0x1c
|
||||
#define MESON_SAR_ADC_AUX_SW_MUX_SEL_CHAN_MASK(_chan) \
|
||||
(GENMASK(10, 8) << (((_chan) - 2) * 2))
|
||||
#define MESON_SAR_ADC_AUX_SW_VREF_P_MUX BIT(6)
|
||||
#define MESON_SAR_ADC_AUX_SW_VREF_N_MUX BIT(5)
|
||||
#define MESON_SAR_ADC_AUX_SW_MODE_SEL BIT(4)
|
||||
#define MESON_SAR_ADC_AUX_SW_YP_DRIVE_SW BIT(3)
|
||||
#define MESON_SAR_ADC_AUX_SW_XP_DRIVE_SW BIT(2)
|
||||
#define MESON_SAR_ADC_AUX_SW_YM_DRIVE_SW BIT(1)
|
||||
#define MESON_SAR_ADC_AUX_SW_XM_DRIVE_SW BIT(0)
|
||||
|
||||
#define MESON_SAR_ADC_CHAN_10_SW 0x20
|
||||
#define MESON_SAR_ADC_CHAN_10_SW_CHAN1_MUX_SEL_MASK GENMASK(25, 23)
|
||||
#define MESON_SAR_ADC_CHAN_10_SW_CHAN1_VREF_P_MUX BIT(22)
|
||||
#define MESON_SAR_ADC_CHAN_10_SW_CHAN1_VREF_N_MUX BIT(21)
|
||||
#define MESON_SAR_ADC_CHAN_10_SW_CHAN1_MODE_SEL BIT(20)
|
||||
#define MESON_SAR_ADC_CHAN_10_SW_CHAN1_YP_DRIVE_SW BIT(19)
|
||||
#define MESON_SAR_ADC_CHAN_10_SW_CHAN1_XP_DRIVE_SW BIT(18)
|
||||
#define MESON_SAR_ADC_CHAN_10_SW_CHAN1_YM_DRIVE_SW BIT(17)
|
||||
#define MESON_SAR_ADC_CHAN_10_SW_CHAN1_XM_DRIVE_SW BIT(16)
|
||||
#define MESON_SAR_ADC_CHAN_10_SW_CHAN0_MUX_SEL_MASK GENMASK(9, 7)
|
||||
#define MESON_SAR_ADC_CHAN_10_SW_CHAN0_VREF_P_MUX BIT(6)
|
||||
#define MESON_SAR_ADC_CHAN_10_SW_CHAN0_VREF_N_MUX BIT(5)
|
||||
#define MESON_SAR_ADC_CHAN_10_SW_CHAN0_MODE_SEL BIT(4)
|
||||
#define MESON_SAR_ADC_CHAN_10_SW_CHAN0_YP_DRIVE_SW BIT(3)
|
||||
#define MESON_SAR_ADC_CHAN_10_SW_CHAN0_XP_DRIVE_SW BIT(2)
|
||||
#define MESON_SAR_ADC_CHAN_10_SW_CHAN0_YM_DRIVE_SW BIT(1)
|
||||
#define MESON_SAR_ADC_CHAN_10_SW_CHAN0_XM_DRIVE_SW BIT(0)
|
||||
|
||||
#define MESON_SAR_ADC_DETECT_IDLE_SW 0x24
|
||||
#define MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_SW_EN BIT(26)
|
||||
#define MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_MUX_MASK GENMASK(25, 23)
|
||||
#define MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_VREF_P_MUX BIT(22)
|
||||
#define MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_VREF_N_MUX BIT(21)
|
||||
#define MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_MODE_SEL BIT(20)
|
||||
#define MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_YP_DRIVE_SW BIT(19)
|
||||
#define MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_XP_DRIVE_SW BIT(18)
|
||||
#define MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_YM_DRIVE_SW BIT(17)
|
||||
#define MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_XM_DRIVE_SW BIT(16)
|
||||
#define MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_MUX_SEL_MASK GENMASK(9, 7)
|
||||
#define MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_VREF_P_MUX BIT(6)
|
||||
#define MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_VREF_N_MUX BIT(5)
|
||||
#define MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_MODE_SEL BIT(4)
|
||||
#define MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_YP_DRIVE_SW BIT(3)
|
||||
#define MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_XP_DRIVE_SW BIT(2)
|
||||
#define MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_YM_DRIVE_SW BIT(1)
|
||||
#define MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_XM_DRIVE_SW BIT(0)
|
||||
|
||||
#define MESON_SAR_ADC_DELTA_10 0x28
|
||||
#define MESON_SAR_ADC_DELTA_10_TEMP_SEL BIT(27)
|
||||
#define MESON_SAR_ADC_DELTA_10_TS_REVE1 BIT(26)
|
||||
#define MESON_SAR_ADC_DELTA_10_CHAN1_DELTA_VALUE_MASK GENMASK(25, 16)
|
||||
#define MESON_SAR_ADC_DELTA_10_TS_REVE0 BIT(15)
|
||||
#define MESON_SAR_ADC_DELTA_10_TS_C_SHIFT 11
|
||||
#define MESON_SAR_ADC_DELTA_10_TS_C_MASK GENMASK(14, 11)
|
||||
#define MESON_SAR_ADC_DELTA_10_TS_VBG_EN BIT(10)
|
||||
#define MESON_SAR_ADC_DELTA_10_CHAN0_DELTA_VALUE_MASK GENMASK(9, 0)
|
||||
|
||||
/*
|
||||
* NOTE: registers from here are undocumented (the vendor Linux kernel driver
|
||||
* and u-boot source served as reference). These only seem to be relevant on
|
||||
* GXBB and newer.
|
||||
*/
|
||||
#define MESON_SAR_ADC_REG11 0x2c
|
||||
#define MESON_SAR_ADC_REG11_BANDGAP_EN BIT(13)
|
||||
|
||||
#define MESON_SAR_ADC_REG13 0x34
|
||||
#define MESON_SAR_ADC_REG13_12BIT_CALIBRATION_MASK GENMASK(13, 8)
|
||||
|
||||
#define MESON_SAR_ADC_MAX_FIFO_SIZE 32
|
||||
|
||||
#define MESON_SAR_ADC_CHAN(_chan) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = _chan, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_AVERAGE_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.datasheet_name = "SAR_ADC_CH"#_chan, \
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: the hardware supports IIO_TEMP for channel 6 as well which is
|
||||
* currently not supported by this driver.
|
||||
*/
|
||||
static const struct iio_chan_spec meson_sar_adc_iio_channels[] = {
|
||||
MESON_SAR_ADC_CHAN(0),
|
||||
MESON_SAR_ADC_CHAN(1),
|
||||
MESON_SAR_ADC_CHAN(2),
|
||||
MESON_SAR_ADC_CHAN(3),
|
||||
MESON_SAR_ADC_CHAN(4),
|
||||
MESON_SAR_ADC_CHAN(5),
|
||||
MESON_SAR_ADC_CHAN(6),
|
||||
MESON_SAR_ADC_CHAN(7),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(8),
|
||||
};
|
||||
|
||||
enum meson_sar_adc_avg_mode {
|
||||
NO_AVERAGING = 0x0,
|
||||
MEAN_AVERAGING = 0x1,
|
||||
MEDIAN_AVERAGING = 0x2,
|
||||
};
|
||||
|
||||
enum meson_sar_adc_num_samples {
|
||||
ONE_SAMPLE = 0x0,
|
||||
TWO_SAMPLES = 0x1,
|
||||
FOUR_SAMPLES = 0x2,
|
||||
EIGHT_SAMPLES = 0x3,
|
||||
};
|
||||
|
||||
enum meson_sar_adc_chan7_mux_sel {
|
||||
CHAN7_MUX_VSS = 0x0,
|
||||
CHAN7_MUX_VDD_DIV4 = 0x1,
|
||||
CHAN7_MUX_VDD_DIV2 = 0x2,
|
||||
CHAN7_MUX_VDD_MUL3_DIV4 = 0x3,
|
||||
CHAN7_MUX_VDD = 0x4,
|
||||
CHAN7_MUX_CH7_INPUT = 0x7,
|
||||
};
|
||||
|
||||
struct meson_sar_adc_data {
|
||||
unsigned int resolution;
|
||||
const char *name;
|
||||
};
|
||||
|
||||
struct meson_sar_adc_priv {
|
||||
struct regmap *regmap;
|
||||
struct regulator *vref;
|
||||
const struct meson_sar_adc_data *data;
|
||||
struct clk *clkin;
|
||||
struct clk *core_clk;
|
||||
struct clk *sana_clk;
|
||||
struct clk *adc_sel_clk;
|
||||
struct clk *adc_clk;
|
||||
struct clk_gate clk_gate;
|
||||
struct clk *adc_div_clk;
|
||||
struct clk_divider clk_div;
|
||||
};
|
||||
|
||||
static const struct regmap_config meson_sar_adc_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.max_register = MESON_SAR_ADC_REG13,
|
||||
};
|
||||
|
||||
static unsigned int meson_sar_adc_get_fifo_count(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
|
||||
u32 regval;
|
||||
|
||||
regmap_read(priv->regmap, MESON_SAR_ADC_REG0, ®val);
|
||||
|
||||
return FIELD_GET(MESON_SAR_ADC_REG0_FIFO_COUNT_MASK, regval);
|
||||
}
|
||||
|
||||
static int meson_sar_adc_wait_busy_clear(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
|
||||
int regval, timeout = 10000;
|
||||
|
||||
/*
|
||||
* NOTE: we need a small delay before reading the status, otherwise
|
||||
* the sample engine may not have started internally (which would
|
||||
* seem to us that sampling is already finished).
|
||||
*/
|
||||
do {
|
||||
udelay(1);
|
||||
regmap_read(priv->regmap, MESON_SAR_ADC_REG0, ®val);
|
||||
} while (FIELD_GET(MESON_SAR_ADC_REG0_BUSY_MASK, regval) && timeout--);
|
||||
|
||||
if (timeout < 0)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int meson_sar_adc_read_raw_sample(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
int *val)
|
||||
{
|
||||
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
|
||||
int ret, regval, fifo_chan, fifo_val, sum = 0, count = 0;
|
||||
|
||||
ret = meson_sar_adc_wait_busy_clear(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
while (meson_sar_adc_get_fifo_count(indio_dev) > 0 &&
|
||||
count < MESON_SAR_ADC_MAX_FIFO_SIZE) {
|
||||
regmap_read(priv->regmap, MESON_SAR_ADC_FIFO_RD, ®val);
|
||||
|
||||
fifo_chan = FIELD_GET(MESON_SAR_ADC_FIFO_RD_CHAN_ID_MASK,
|
||||
regval);
|
||||
if (fifo_chan != chan->channel)
|
||||
continue;
|
||||
|
||||
fifo_val = FIELD_GET(MESON_SAR_ADC_FIFO_RD_SAMPLE_VALUE_MASK,
|
||||
regval);
|
||||
fifo_val &= (BIT(priv->data->resolution) - 1);
|
||||
|
||||
sum += fifo_val;
|
||||
count++;
|
||||
}
|
||||
|
||||
if (!count)
|
||||
return -ENOENT;
|
||||
|
||||
*val = sum / count;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void meson_sar_adc_set_averaging(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
enum meson_sar_adc_avg_mode mode,
|
||||
enum meson_sar_adc_num_samples samples)
|
||||
{
|
||||
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
|
||||
int val, channel = chan->channel;
|
||||
|
||||
val = samples << MESON_SAR_ADC_AVG_CNTL_NUM_SAMPLES_SHIFT(channel);
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_AVG_CNTL,
|
||||
MESON_SAR_ADC_AVG_CNTL_NUM_SAMPLES_MASK(channel),
|
||||
val);
|
||||
|
||||
val = mode << MESON_SAR_ADC_AVG_CNTL_AVG_MODE_SHIFT(channel);
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_AVG_CNTL,
|
||||
MESON_SAR_ADC_AVG_CNTL_AVG_MODE_MASK(channel), val);
|
||||
}
|
||||
|
||||
static void meson_sar_adc_enable_channel(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan)
|
||||
{
|
||||
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
|
||||
u32 regval;
|
||||
|
||||
/*
|
||||
* the SAR ADC engine allows sampling multiple channels at the same
|
||||
* time. to keep it simple we're only working with one *internal*
|
||||
* channel, which starts counting at index 0 (which means: count = 1).
|
||||
*/
|
||||
regval = FIELD_PREP(MESON_SAR_ADC_CHAN_LIST_MAX_INDEX_MASK, 0);
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_CHAN_LIST,
|
||||
MESON_SAR_ADC_CHAN_LIST_MAX_INDEX_MASK, regval);
|
||||
|
||||
/* map channel index 0 to the channel which we want to read */
|
||||
regval = FIELD_PREP(MESON_SAR_ADC_CHAN_LIST_ENTRY_MASK(0),
|
||||
chan->channel);
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_CHAN_LIST,
|
||||
MESON_SAR_ADC_CHAN_LIST_ENTRY_MASK(0), regval);
|
||||
|
||||
regval = FIELD_PREP(MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_MUX_MASK,
|
||||
chan->channel);
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_DETECT_IDLE_SW,
|
||||
MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_MUX_MASK,
|
||||
regval);
|
||||
|
||||
regval = FIELD_PREP(MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_MUX_SEL_MASK,
|
||||
chan->channel);
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_DETECT_IDLE_SW,
|
||||
MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_MUX_SEL_MASK,
|
||||
regval);
|
||||
|
||||
if (chan->channel == 6)
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELTA_10,
|
||||
MESON_SAR_ADC_DELTA_10_TEMP_SEL, 0);
|
||||
}
|
||||
|
||||
static void meson_sar_adc_set_chan7_mux(struct iio_dev *indio_dev,
|
||||
enum meson_sar_adc_chan7_mux_sel sel)
|
||||
{
|
||||
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
|
||||
u32 regval;
|
||||
|
||||
regval = FIELD_PREP(MESON_SAR_ADC_REG3_CTRL_CHAN7_MUX_SEL_MASK, sel);
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG3,
|
||||
MESON_SAR_ADC_REG3_CTRL_CHAN7_MUX_SEL_MASK, regval);
|
||||
|
||||
usleep_range(10, 20);
|
||||
}
|
||||
|
||||
static void meson_sar_adc_start_sample_engine(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
|
||||
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0,
|
||||
MESON_SAR_ADC_REG0_SAMPLE_ENGINE_ENABLE,
|
||||
MESON_SAR_ADC_REG0_SAMPLE_ENGINE_ENABLE);
|
||||
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0,
|
||||
MESON_SAR_ADC_REG0_SAMPLING_START,
|
||||
MESON_SAR_ADC_REG0_SAMPLING_START);
|
||||
}
|
||||
|
||||
static void meson_sar_adc_stop_sample_engine(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
|
||||
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0,
|
||||
MESON_SAR_ADC_REG0_SAMPLING_STOP,
|
||||
MESON_SAR_ADC_REG0_SAMPLING_STOP);
|
||||
|
||||
/* wait until all modules are stopped */
|
||||
meson_sar_adc_wait_busy_clear(indio_dev);
|
||||
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0,
|
||||
MESON_SAR_ADC_REG0_SAMPLE_ENGINE_ENABLE, 0);
|
||||
}
|
||||
|
||||
static int meson_sar_adc_lock(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
|
||||
int val, timeout = 10000;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
|
||||
/* prevent BL30 from using the SAR ADC while we are using it */
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELAY,
|
||||
MESON_SAR_ADC_DELAY_KERNEL_BUSY,
|
||||
MESON_SAR_ADC_DELAY_KERNEL_BUSY);
|
||||
|
||||
/* wait until BL30 releases it's lock (so we can use the SAR ADC) */
|
||||
do {
|
||||
udelay(1);
|
||||
regmap_read(priv->regmap, MESON_SAR_ADC_DELAY, &val);
|
||||
} while (val & MESON_SAR_ADC_DELAY_BL30_BUSY && timeout--);
|
||||
|
||||
if (timeout < 0)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void meson_sar_adc_unlock(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
|
||||
|
||||
/* allow BL30 to use the SAR ADC again */
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELAY,
|
||||
MESON_SAR_ADC_DELAY_KERNEL_BUSY, 0);
|
||||
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
}
|
||||
|
||||
static void meson_sar_adc_clear_fifo(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
|
||||
int count;
|
||||
|
||||
for (count = 0; count < MESON_SAR_ADC_MAX_FIFO_SIZE; count++) {
|
||||
if (!meson_sar_adc_get_fifo_count(indio_dev))
|
||||
break;
|
||||
|
||||
regmap_read(priv->regmap, MESON_SAR_ADC_FIFO_RD, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static int meson_sar_adc_get_sample(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
enum meson_sar_adc_avg_mode avg_mode,
|
||||
enum meson_sar_adc_num_samples avg_samples,
|
||||
int *val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = meson_sar_adc_lock(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* clear the FIFO to make sure we're not reading old values */
|
||||
meson_sar_adc_clear_fifo(indio_dev);
|
||||
|
||||
meson_sar_adc_set_averaging(indio_dev, chan, avg_mode, avg_samples);
|
||||
|
||||
meson_sar_adc_enable_channel(indio_dev, chan);
|
||||
|
||||
meson_sar_adc_start_sample_engine(indio_dev);
|
||||
ret = meson_sar_adc_read_raw_sample(indio_dev, chan, val);
|
||||
meson_sar_adc_stop_sample_engine(indio_dev);
|
||||
|
||||
meson_sar_adc_unlock(indio_dev);
|
||||
|
||||
if (ret) {
|
||||
dev_warn(indio_dev->dev.parent,
|
||||
"failed to read sample for channel %d: %d\n",
|
||||
chan->channel, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
static int meson_sar_adc_iio_info_read_raw(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
return meson_sar_adc_get_sample(indio_dev, chan, NO_AVERAGING,
|
||||
ONE_SAMPLE, val);
|
||||
break;
|
||||
|
||||
case IIO_CHAN_INFO_AVERAGE_RAW:
|
||||
return meson_sar_adc_get_sample(indio_dev, chan,
|
||||
MEAN_AVERAGING, EIGHT_SAMPLES,
|
||||
val);
|
||||
break;
|
||||
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
ret = regulator_get_voltage(priv->vref);
|
||||
if (ret < 0) {
|
||||
dev_err(indio_dev->dev.parent,
|
||||
"failed to get vref voltage: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*val = ret / 1000;
|
||||
*val2 = priv->data->resolution;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int meson_sar_adc_clk_init(struct iio_dev *indio_dev,
|
||||
void __iomem *base)
|
||||
{
|
||||
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
|
||||
struct clk_init_data init;
|
||||
const char *clk_parents[1];
|
||||
|
||||
init.name = devm_kasprintf(&indio_dev->dev, GFP_KERNEL, "%s#adc_div",
|
||||
of_node_full_name(indio_dev->dev.of_node));
|
||||
init.flags = 0;
|
||||
init.ops = &clk_divider_ops;
|
||||
clk_parents[0] = __clk_get_name(priv->clkin);
|
||||
init.parent_names = clk_parents;
|
||||
init.num_parents = 1;
|
||||
|
||||
priv->clk_div.reg = base + MESON_SAR_ADC_REG3;
|
||||
priv->clk_div.shift = MESON_SAR_ADC_REG3_ADC_CLK_DIV_SHIFT;
|
||||
priv->clk_div.width = MESON_SAR_ADC_REG3_ADC_CLK_DIV_WIDTH;
|
||||
priv->clk_div.hw.init = &init;
|
||||
priv->clk_div.flags = 0;
|
||||
|
||||
priv->adc_div_clk = devm_clk_register(&indio_dev->dev,
|
||||
&priv->clk_div.hw);
|
||||
if (WARN_ON(IS_ERR(priv->adc_div_clk)))
|
||||
return PTR_ERR(priv->adc_div_clk);
|
||||
|
||||
init.name = devm_kasprintf(&indio_dev->dev, GFP_KERNEL, "%s#adc_en",
|
||||
of_node_full_name(indio_dev->dev.of_node));
|
||||
init.flags = CLK_SET_RATE_PARENT;
|
||||
init.ops = &clk_gate_ops;
|
||||
clk_parents[0] = __clk_get_name(priv->adc_div_clk);
|
||||
init.parent_names = clk_parents;
|
||||
init.num_parents = 1;
|
||||
|
||||
priv->clk_gate.reg = base + MESON_SAR_ADC_REG3;
|
||||
priv->clk_gate.bit_idx = fls(MESON_SAR_ADC_REG3_CLK_EN);
|
||||
priv->clk_gate.hw.init = &init;
|
||||
|
||||
priv->adc_clk = devm_clk_register(&indio_dev->dev, &priv->clk_gate.hw);
|
||||
if (WARN_ON(IS_ERR(priv->adc_clk)))
|
||||
return PTR_ERR(priv->adc_clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int meson_sar_adc_init(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
|
||||
int regval, ret;
|
||||
|
||||
/*
|
||||
* make sure we start at CH7 input since the other muxes are only used
|
||||
* for internal calibration.
|
||||
*/
|
||||
meson_sar_adc_set_chan7_mux(indio_dev, CHAN7_MUX_CH7_INPUT);
|
||||
|
||||
/*
|
||||
* leave sampling delay and the input clocks as configured by BL30 to
|
||||
* make sure BL30 gets the values it expects when reading the
|
||||
* temperature sensor.
|
||||
*/
|
||||
regmap_read(priv->regmap, MESON_SAR_ADC_REG3, ®val);
|
||||
if (regval & MESON_SAR_ADC_REG3_BL30_INITIALIZED)
|
||||
return 0;
|
||||
|
||||
meson_sar_adc_stop_sample_engine(indio_dev);
|
||||
|
||||
/* update the channel 6 MUX to select the temperature sensor */
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0,
|
||||
MESON_SAR_ADC_REG0_ADC_TEMP_SEN_SEL,
|
||||
MESON_SAR_ADC_REG0_ADC_TEMP_SEN_SEL);
|
||||
|
||||
/* disable all channels by default */
|
||||
regmap_write(priv->regmap, MESON_SAR_ADC_CHAN_LIST, 0x0);
|
||||
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG3,
|
||||
MESON_SAR_ADC_REG3_CTRL_SAMPLING_CLOCK_PHASE, 0);
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG3,
|
||||
MESON_SAR_ADC_REG3_CNTL_USE_SC_DLY,
|
||||
MESON_SAR_ADC_REG3_CNTL_USE_SC_DLY);
|
||||
|
||||
/* delay between two samples = (10+1) * 1uS */
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELAY,
|
||||
MESON_SAR_ADC_DELAY_INPUT_DLY_CNT_MASK,
|
||||
FIELD_PREP(MESON_SAR_ADC_DELAY_SAMPLE_DLY_CNT_MASK,
|
||||
10));
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELAY,
|
||||
MESON_SAR_ADC_DELAY_SAMPLE_DLY_SEL_MASK,
|
||||
FIELD_PREP(MESON_SAR_ADC_DELAY_SAMPLE_DLY_SEL_MASK,
|
||||
0));
|
||||
|
||||
/* delay between two samples = (10+1) * 1uS */
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELAY,
|
||||
MESON_SAR_ADC_DELAY_INPUT_DLY_CNT_MASK,
|
||||
FIELD_PREP(MESON_SAR_ADC_DELAY_INPUT_DLY_CNT_MASK,
|
||||
10));
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELAY,
|
||||
MESON_SAR_ADC_DELAY_INPUT_DLY_SEL_MASK,
|
||||
FIELD_PREP(MESON_SAR_ADC_DELAY_INPUT_DLY_SEL_MASK,
|
||||
1));
|
||||
|
||||
ret = clk_set_parent(priv->adc_sel_clk, priv->clkin);
|
||||
if (ret) {
|
||||
dev_err(indio_dev->dev.parent,
|
||||
"failed to set adc parent to clkin\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = clk_set_rate(priv->adc_clk, 1200000);
|
||||
if (ret) {
|
||||
dev_err(indio_dev->dev.parent,
|
||||
"failed to set adc clock rate\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int meson_sar_adc_hw_enable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = meson_sar_adc_lock(indio_dev);
|
||||
if (ret)
|
||||
goto err_lock;
|
||||
|
||||
ret = regulator_enable(priv->vref);
|
||||
if (ret < 0) {
|
||||
dev_err(indio_dev->dev.parent,
|
||||
"failed to enable vref regulator\n");
|
||||
goto err_vref;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(priv->core_clk);
|
||||
if (ret) {
|
||||
dev_err(indio_dev->dev.parent, "failed to enable core clk\n");
|
||||
goto err_core_clk;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(priv->sana_clk);
|
||||
if (ret) {
|
||||
dev_err(indio_dev->dev.parent, "failed to enable sana clk\n");
|
||||
goto err_sana_clk;
|
||||
}
|
||||
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG11,
|
||||
MESON_SAR_ADC_REG11_BANDGAP_EN,
|
||||
MESON_SAR_ADC_REG11_BANDGAP_EN);
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG3,
|
||||
MESON_SAR_ADC_REG3_ADC_EN,
|
||||
MESON_SAR_ADC_REG3_ADC_EN);
|
||||
|
||||
udelay(5);
|
||||
|
||||
ret = clk_prepare_enable(priv->adc_clk);
|
||||
if (ret) {
|
||||
dev_err(indio_dev->dev.parent, "failed to enable adc clk\n");
|
||||
goto err_adc_clk;
|
||||
}
|
||||
|
||||
meson_sar_adc_unlock(indio_dev);
|
||||
|
||||
return 0;
|
||||
|
||||
err_adc_clk:
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG3,
|
||||
MESON_SAR_ADC_REG3_ADC_EN, 0);
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG11,
|
||||
MESON_SAR_ADC_REG11_BANDGAP_EN, 0);
|
||||
clk_disable_unprepare(priv->sana_clk);
|
||||
err_sana_clk:
|
||||
clk_disable_unprepare(priv->core_clk);
|
||||
err_core_clk:
|
||||
regulator_disable(priv->vref);
|
||||
err_vref:
|
||||
meson_sar_adc_unlock(indio_dev);
|
||||
err_lock:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int meson_sar_adc_hw_disable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = meson_sar_adc_lock(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
clk_disable_unprepare(priv->adc_clk);
|
||||
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG3,
|
||||
MESON_SAR_ADC_REG3_ADC_EN, 0);
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG11,
|
||||
MESON_SAR_ADC_REG11_BANDGAP_EN, 0);
|
||||
|
||||
clk_disable_unprepare(priv->sana_clk);
|
||||
clk_disable_unprepare(priv->core_clk);
|
||||
|
||||
regulator_disable(priv->vref);
|
||||
|
||||
meson_sar_adc_unlock(indio_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct iio_info meson_sar_adc_iio_info = {
|
||||
.read_raw = meson_sar_adc_iio_info_read_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
struct meson_sar_adc_data meson_sar_adc_gxbb_data = {
|
||||
.resolution = 10,
|
||||
.name = "meson-gxbb-saradc",
|
||||
};
|
||||
|
||||
struct meson_sar_adc_data meson_sar_adc_gxl_data = {
|
||||
.resolution = 12,
|
||||
.name = "meson-gxl-saradc",
|
||||
};
|
||||
|
||||
struct meson_sar_adc_data meson_sar_adc_gxm_data = {
|
||||
.resolution = 12,
|
||||
.name = "meson-gxm-saradc",
|
||||
};
|
||||
|
||||
static const struct of_device_id meson_sar_adc_of_match[] = {
|
||||
{
|
||||
.compatible = "amlogic,meson-gxbb-saradc",
|
||||
.data = &meson_sar_adc_gxbb_data,
|
||||
}, {
|
||||
.compatible = "amlogic,meson-gxl-saradc",
|
||||
.data = &meson_sar_adc_gxl_data,
|
||||
}, {
|
||||
.compatible = "amlogic,meson-gxm-saradc",
|
||||
.data = &meson_sar_adc_gxm_data,
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, meson_sar_adc_of_match);
|
||||
|
||||
static int meson_sar_adc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct meson_sar_adc_priv *priv;
|
||||
struct iio_dev *indio_dev;
|
||||
struct resource *res;
|
||||
void __iomem *base;
|
||||
const struct of_device_id *match;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*priv));
|
||||
if (!indio_dev) {
|
||||
dev_err(&pdev->dev, "failed allocating iio device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
priv = iio_priv(indio_dev);
|
||||
|
||||
match = of_match_device(meson_sar_adc_of_match, &pdev->dev);
|
||||
priv->data = match->data;
|
||||
|
||||
indio_dev->name = priv->data->name;
|
||||
indio_dev->dev.parent = &pdev->dev;
|
||||
indio_dev->dev.of_node = pdev->dev.of_node;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &meson_sar_adc_iio_info;
|
||||
|
||||
indio_dev->channels = meson_sar_adc_iio_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(meson_sar_adc_iio_channels);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
priv->regmap = devm_regmap_init_mmio(&pdev->dev, base,
|
||||
&meson_sar_adc_regmap_config);
|
||||
if (IS_ERR(priv->regmap))
|
||||
return PTR_ERR(priv->regmap);
|
||||
|
||||
priv->clkin = devm_clk_get(&pdev->dev, "clkin");
|
||||
if (IS_ERR(priv->clkin)) {
|
||||
dev_err(&pdev->dev, "failed to get clkin\n");
|
||||
return PTR_ERR(priv->clkin);
|
||||
}
|
||||
|
||||
priv->core_clk = devm_clk_get(&pdev->dev, "core");
|
||||
if (IS_ERR(priv->core_clk)) {
|
||||
dev_err(&pdev->dev, "failed to get core clk\n");
|
||||
return PTR_ERR(priv->core_clk);
|
||||
}
|
||||
|
||||
priv->sana_clk = devm_clk_get(&pdev->dev, "sana");
|
||||
if (IS_ERR(priv->sana_clk)) {
|
||||
if (PTR_ERR(priv->sana_clk) == -ENOENT) {
|
||||
priv->sana_clk = NULL;
|
||||
} else {
|
||||
dev_err(&pdev->dev, "failed to get sana clk\n");
|
||||
return PTR_ERR(priv->sana_clk);
|
||||
}
|
||||
}
|
||||
|
||||
priv->adc_clk = devm_clk_get(&pdev->dev, "adc_clk");
|
||||
if (IS_ERR(priv->adc_clk)) {
|
||||
if (PTR_ERR(priv->adc_clk) == -ENOENT) {
|
||||
priv->adc_clk = NULL;
|
||||
} else {
|
||||
dev_err(&pdev->dev, "failed to get adc clk\n");
|
||||
return PTR_ERR(priv->adc_clk);
|
||||
}
|
||||
}
|
||||
|
||||
priv->adc_sel_clk = devm_clk_get(&pdev->dev, "adc_sel");
|
||||
if (IS_ERR(priv->adc_sel_clk)) {
|
||||
if (PTR_ERR(priv->adc_sel_clk) == -ENOENT) {
|
||||
priv->adc_sel_clk = NULL;
|
||||
} else {
|
||||
dev_err(&pdev->dev, "failed to get adc_sel clk\n");
|
||||
return PTR_ERR(priv->adc_sel_clk);
|
||||
}
|
||||
}
|
||||
|
||||
/* on pre-GXBB SoCs the SAR ADC itself provides the ADC clock: */
|
||||
if (!priv->adc_clk) {
|
||||
ret = meson_sar_adc_clk_init(indio_dev, base);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
priv->vref = devm_regulator_get(&pdev->dev, "vref");
|
||||
if (IS_ERR(priv->vref)) {
|
||||
dev_err(&pdev->dev, "failed to get vref regulator\n");
|
||||
return PTR_ERR(priv->vref);
|
||||
}
|
||||
|
||||
ret = meson_sar_adc_init(indio_dev);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
ret = meson_sar_adc_hw_enable(indio_dev);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
platform_set_drvdata(pdev, indio_dev);
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto err_hw;
|
||||
|
||||
return 0;
|
||||
|
||||
err_hw:
|
||||
meson_sar_adc_hw_disable(indio_dev);
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int meson_sar_adc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
return meson_sar_adc_hw_disable(indio_dev);
|
||||
}
|
||||
|
||||
static int __maybe_unused meson_sar_adc_suspend(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
|
||||
return meson_sar_adc_hw_disable(indio_dev);
|
||||
}
|
||||
|
||||
static int __maybe_unused meson_sar_adc_resume(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
|
||||
return meson_sar_adc_hw_enable(indio_dev);
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(meson_sar_adc_pm_ops,
|
||||
meson_sar_adc_suspend, meson_sar_adc_resume);
|
||||
|
||||
static struct platform_driver meson_sar_adc_driver = {
|
||||
.probe = meson_sar_adc_probe,
|
||||
.remove = meson_sar_adc_remove,
|
||||
.driver = {
|
||||
.name = "meson-saradc",
|
||||
.of_match_table = meson_sar_adc_of_match,
|
||||
.pm = &meson_sar_adc_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(meson_sar_adc_driver);
|
||||
|
||||
MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl@googlemail.com>");
|
||||
MODULE_DESCRIPTION("Amlogic Meson SAR ADC driver");
|
||||
MODULE_LICENSE("GPL v2");
|
631
drivers/iio/adc/rcar-gyroadc.c
Normal file
631
drivers/iio/adc/rcar-gyroadc.c
Normal file
@ -0,0 +1,631 @@
|
||||
/*
|
||||
* Renesas R-Car GyroADC driver
|
||||
*
|
||||
* Copyright 2016 Marek Vasut <marek.vasut@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
|
||||
#define DRIVER_NAME "rcar-gyroadc"
|
||||
|
||||
/* GyroADC registers. */
|
||||
#define RCAR_GYROADC_MODE_SELECT 0x00
|
||||
#define RCAR_GYROADC_MODE_SELECT_1_MB88101A 0x0
|
||||
#define RCAR_GYROADC_MODE_SELECT_2_ADCS7476 0x1
|
||||
#define RCAR_GYROADC_MODE_SELECT_3_MAX1162 0x3
|
||||
|
||||
#define RCAR_GYROADC_START_STOP 0x04
|
||||
#define RCAR_GYROADC_START_STOP_START BIT(0)
|
||||
|
||||
#define RCAR_GYROADC_CLOCK_LENGTH 0x08
|
||||
#define RCAR_GYROADC_1_25MS_LENGTH 0x0c
|
||||
|
||||
#define RCAR_GYROADC_REALTIME_DATA(ch) (0x10 + ((ch) * 4))
|
||||
#define RCAR_GYROADC_100MS_ADDED_DATA(ch) (0x30 + ((ch) * 4))
|
||||
#define RCAR_GYROADC_10MS_AVG_DATA(ch) (0x50 + ((ch) * 4))
|
||||
|
||||
#define RCAR_GYROADC_FIFO_STATUS 0x70
|
||||
#define RCAR_GYROADC_FIFO_STATUS_EMPTY(ch) BIT(0 + (4 * (ch)))
|
||||
#define RCAR_GYROADC_FIFO_STATUS_FULL(ch) BIT(1 + (4 * (ch)))
|
||||
#define RCAR_GYROADC_FIFO_STATUS_ERROR(ch) BIT(2 + (4 * (ch)))
|
||||
|
||||
#define RCAR_GYROADC_INTR 0x74
|
||||
#define RCAR_GYROADC_INTR_INT BIT(0)
|
||||
|
||||
#define RCAR_GYROADC_INTENR 0x78
|
||||
#define RCAR_GYROADC_INTENR_INTEN BIT(0)
|
||||
|
||||
#define RCAR_GYROADC_SAMPLE_RATE 800 /* Hz */
|
||||
|
||||
#define RCAR_GYROADC_RUNTIME_PM_DELAY_MS 2000
|
||||
|
||||
enum rcar_gyroadc_model {
|
||||
RCAR_GYROADC_MODEL_DEFAULT,
|
||||
RCAR_GYROADC_MODEL_R8A7792,
|
||||
};
|
||||
|
||||
struct rcar_gyroadc {
|
||||
struct device *dev;
|
||||
void __iomem *regs;
|
||||
struct clk *iclk;
|
||||
struct regulator *vref[8];
|
||||
unsigned int num_channels;
|
||||
enum rcar_gyroadc_model model;
|
||||
unsigned int mode;
|
||||
unsigned int sample_width;
|
||||
};
|
||||
|
||||
static void rcar_gyroadc_hw_init(struct rcar_gyroadc *priv)
|
||||
{
|
||||
const unsigned long clk_mhz = clk_get_rate(priv->iclk) / 1000000;
|
||||
const unsigned long clk_mul =
|
||||
(priv->mode == RCAR_GYROADC_MODE_SELECT_1_MB88101A) ? 10 : 5;
|
||||
unsigned long clk_len = clk_mhz * clk_mul;
|
||||
|
||||
/*
|
||||
* According to the R-Car Gen2 datasheet Rev. 1.01, Sept 08 2014,
|
||||
* page 77-7, clock length must be even number. If it's odd number,
|
||||
* add one.
|
||||
*/
|
||||
if (clk_len & 1)
|
||||
clk_len++;
|
||||
|
||||
/* Stop the GyroADC. */
|
||||
writel(0, priv->regs + RCAR_GYROADC_START_STOP);
|
||||
|
||||
/* Disable IRQ on V2H. */
|
||||
if (priv->model == RCAR_GYROADC_MODEL_R8A7792)
|
||||
writel(0, priv->regs + RCAR_GYROADC_INTENR);
|
||||
|
||||
/* Set mode and timing. */
|
||||
writel(priv->mode, priv->regs + RCAR_GYROADC_MODE_SELECT);
|
||||
writel(clk_len, priv->regs + RCAR_GYROADC_CLOCK_LENGTH);
|
||||
writel(clk_mhz * 1250, priv->regs + RCAR_GYROADC_1_25MS_LENGTH);
|
||||
}
|
||||
|
||||
static void rcar_gyroadc_hw_start(struct rcar_gyroadc *priv)
|
||||
{
|
||||
/* Start sampling. */
|
||||
writel(RCAR_GYROADC_START_STOP_START,
|
||||
priv->regs + RCAR_GYROADC_START_STOP);
|
||||
|
||||
/*
|
||||
* Wait for the first conversion to complete. This is longer than
|
||||
* the 1.25 mS in the datasheet because 1.25 mS is not enough for
|
||||
* the hardware to deliver the first sample and the hardware does
|
||||
* then return zeroes instead of valid data.
|
||||
*/
|
||||
mdelay(3);
|
||||
}
|
||||
|
||||
static void rcar_gyroadc_hw_stop(struct rcar_gyroadc *priv)
|
||||
{
|
||||
/* Stop the GyroADC. */
|
||||
writel(0, priv->regs + RCAR_GYROADC_START_STOP);
|
||||
}
|
||||
|
||||
#define RCAR_GYROADC_CHAN(_idx) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = (_idx), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec rcar_gyroadc_iio_channels_1[] = {
|
||||
RCAR_GYROADC_CHAN(0),
|
||||
RCAR_GYROADC_CHAN(1),
|
||||
RCAR_GYROADC_CHAN(2),
|
||||
RCAR_GYROADC_CHAN(3),
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec rcar_gyroadc_iio_channels_2[] = {
|
||||
RCAR_GYROADC_CHAN(0),
|
||||
RCAR_GYROADC_CHAN(1),
|
||||
RCAR_GYROADC_CHAN(2),
|
||||
RCAR_GYROADC_CHAN(3),
|
||||
RCAR_GYROADC_CHAN(4),
|
||||
RCAR_GYROADC_CHAN(5),
|
||||
RCAR_GYROADC_CHAN(6),
|
||||
RCAR_GYROADC_CHAN(7),
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec rcar_gyroadc_iio_channels_3[] = {
|
||||
RCAR_GYROADC_CHAN(0),
|
||||
RCAR_GYROADC_CHAN(1),
|
||||
RCAR_GYROADC_CHAN(2),
|
||||
RCAR_GYROADC_CHAN(3),
|
||||
RCAR_GYROADC_CHAN(4),
|
||||
RCAR_GYROADC_CHAN(5),
|
||||
RCAR_GYROADC_CHAN(6),
|
||||
RCAR_GYROADC_CHAN(7),
|
||||
};
|
||||
|
||||
static int rcar_gyroadc_set_power(struct rcar_gyroadc *priv, bool on)
|
||||
{
|
||||
struct device *dev = priv->dev;
|
||||
int ret;
|
||||
|
||||
if (on) {
|
||||
ret = pm_runtime_get_sync(dev);
|
||||
if (ret < 0)
|
||||
pm_runtime_put_noidle(dev);
|
||||
} else {
|
||||
pm_runtime_mark_last_busy(dev);
|
||||
ret = pm_runtime_put_autosuspend(dev);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rcar_gyroadc_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct rcar_gyroadc *priv = iio_priv(indio_dev);
|
||||
struct regulator *consumer;
|
||||
unsigned int datareg = RCAR_GYROADC_REALTIME_DATA(chan->channel);
|
||||
unsigned int vref;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* MB88101 is special in that it has only single regulator for
|
||||
* all four channels.
|
||||
*/
|
||||
if (priv->mode == RCAR_GYROADC_MODE_SELECT_1_MB88101A)
|
||||
consumer = priv->vref[0];
|
||||
else
|
||||
consumer = priv->vref[chan->channel];
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (chan->type != IIO_VOLTAGE)
|
||||
return -EINVAL;
|
||||
|
||||
/* Channel not connected. */
|
||||
if (!consumer)
|
||||
return -EINVAL;
|
||||
|
||||
ret = iio_device_claim_direct_mode(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = rcar_gyroadc_set_power(priv, true);
|
||||
if (ret < 0) {
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*val = readl(priv->regs + datareg);
|
||||
*val &= BIT(priv->sample_width) - 1;
|
||||
|
||||
ret = rcar_gyroadc_set_power(priv, false);
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
/* Channel not connected. */
|
||||
if (!consumer)
|
||||
return -EINVAL;
|
||||
|
||||
vref = regulator_get_voltage(consumer);
|
||||
*val = vref / 1000;
|
||||
*val2 = 1 << priv->sample_width;
|
||||
|
||||
return IIO_VAL_FRACTIONAL;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
*val = RCAR_GYROADC_SAMPLE_RATE;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int rcar_gyroadc_reg_access(struct iio_dev *indio_dev,
|
||||
unsigned int reg, unsigned int writeval,
|
||||
unsigned int *readval)
|
||||
{
|
||||
struct rcar_gyroadc *priv = iio_priv(indio_dev);
|
||||
unsigned int maxreg = RCAR_GYROADC_FIFO_STATUS;
|
||||
|
||||
if (readval == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
if (reg % 4)
|
||||
return -EINVAL;
|
||||
|
||||
/* Handle the V2H case with extra interrupt block. */
|
||||
if (priv->model == RCAR_GYROADC_MODEL_R8A7792)
|
||||
maxreg = RCAR_GYROADC_INTENR;
|
||||
|
||||
if (reg > maxreg)
|
||||
return -EINVAL;
|
||||
|
||||
*readval = readl(priv->regs + reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct iio_info rcar_gyroadc_iio_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = rcar_gyroadc_read_raw,
|
||||
.debugfs_reg_access = rcar_gyroadc_reg_access,
|
||||
};
|
||||
|
||||
static const struct of_device_id rcar_gyroadc_match[] = {
|
||||
{
|
||||
/* R-Car compatible GyroADC */
|
||||
.compatible = "renesas,rcar-gyroadc",
|
||||
.data = (void *)RCAR_GYROADC_MODEL_DEFAULT,
|
||||
}, {
|
||||
/* R-Car V2H specialty with interrupt registers. */
|
||||
.compatible = "renesas,r8a7792-gyroadc",
|
||||
.data = (void *)RCAR_GYROADC_MODEL_R8A7792,
|
||||
}, {
|
||||
/* sentinel */
|
||||
}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, rcar_gyroadc_match);
|
||||
|
||||
static const struct of_device_id rcar_gyroadc_child_match[] = {
|
||||
/* Mode 1 ADCs */
|
||||
{
|
||||
.compatible = "fujitsu,mb88101a",
|
||||
.data = (void *)RCAR_GYROADC_MODE_SELECT_1_MB88101A,
|
||||
},
|
||||
/* Mode 2 ADCs */
|
||||
{
|
||||
.compatible = "ti,adcs7476",
|
||||
.data = (void *)RCAR_GYROADC_MODE_SELECT_2_ADCS7476,
|
||||
}, {
|
||||
.compatible = "ti,adc121",
|
||||
.data = (void *)RCAR_GYROADC_MODE_SELECT_2_ADCS7476,
|
||||
}, {
|
||||
.compatible = "adi,ad7476",
|
||||
.data = (void *)RCAR_GYROADC_MODE_SELECT_2_ADCS7476,
|
||||
},
|
||||
/* Mode 3 ADCs */
|
||||
{
|
||||
.compatible = "maxim,max1162",
|
||||
.data = (void *)RCAR_GYROADC_MODE_SELECT_3_MAX1162,
|
||||
}, {
|
||||
.compatible = "maxim,max11100",
|
||||
.data = (void *)RCAR_GYROADC_MODE_SELECT_3_MAX1162,
|
||||
},
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
static int rcar_gyroadc_parse_subdevs(struct iio_dev *indio_dev)
|
||||
{
|
||||
const struct of_device_id *of_id;
|
||||
const struct iio_chan_spec *channels;
|
||||
struct rcar_gyroadc *priv = iio_priv(indio_dev);
|
||||
struct device *dev = priv->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct device_node *child;
|
||||
struct regulator *vref;
|
||||
unsigned int reg;
|
||||
unsigned int adcmode, childmode;
|
||||
unsigned int sample_width;
|
||||
unsigned int num_channels;
|
||||
int ret, first = 1;
|
||||
|
||||
for_each_child_of_node(np, child) {
|
||||
of_id = of_match_node(rcar_gyroadc_child_match, child);
|
||||
if (!of_id) {
|
||||
dev_err(dev, "Ignoring unsupported ADC \"%s\".",
|
||||
child->name);
|
||||
continue;
|
||||
}
|
||||
|
||||
childmode = (unsigned int)of_id->data;
|
||||
switch (childmode) {
|
||||
case RCAR_GYROADC_MODE_SELECT_1_MB88101A:
|
||||
sample_width = 12;
|
||||
channels = rcar_gyroadc_iio_channels_1;
|
||||
num_channels = ARRAY_SIZE(rcar_gyroadc_iio_channels_1);
|
||||
break;
|
||||
case RCAR_GYROADC_MODE_SELECT_2_ADCS7476:
|
||||
sample_width = 15;
|
||||
channels = rcar_gyroadc_iio_channels_2;
|
||||
num_channels = ARRAY_SIZE(rcar_gyroadc_iio_channels_2);
|
||||
break;
|
||||
case RCAR_GYROADC_MODE_SELECT_3_MAX1162:
|
||||
sample_width = 16;
|
||||
channels = rcar_gyroadc_iio_channels_3;
|
||||
num_channels = ARRAY_SIZE(rcar_gyroadc_iio_channels_3);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* MB88101 is special in that it's only a single chip taking
|
||||
* up all the CHS lines. Thus, the DT binding is also special
|
||||
* and has no reg property. If we run into such ADC, handle
|
||||
* it here.
|
||||
*/
|
||||
if (childmode == RCAR_GYROADC_MODE_SELECT_1_MB88101A) {
|
||||
reg = 0;
|
||||
} else {
|
||||
ret = of_property_read_u32(child, "reg", ®);
|
||||
if (ret) {
|
||||
dev_err(dev,
|
||||
"Failed to get child reg property of ADC \"%s\".\n",
|
||||
child->name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Channel number is too high. */
|
||||
if (reg >= num_channels) {
|
||||
dev_err(dev,
|
||||
"Only %i channels supported with %s, but reg = <%i>.\n",
|
||||
num_channels, child->name, reg);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/* Child node selected different mode than the rest. */
|
||||
if (!first && (adcmode != childmode)) {
|
||||
dev_err(dev,
|
||||
"Channel %i uses different ADC mode than the rest.\n",
|
||||
reg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Channel is valid, grab the regulator. */
|
||||
dev->of_node = child;
|
||||
vref = devm_regulator_get(dev, "vref");
|
||||
dev->of_node = np;
|
||||
if (IS_ERR(vref)) {
|
||||
dev_dbg(dev, "Channel %i 'vref' supply not connected.\n",
|
||||
reg);
|
||||
return PTR_ERR(vref);
|
||||
}
|
||||
|
||||
priv->vref[reg] = vref;
|
||||
|
||||
if (!first)
|
||||
continue;
|
||||
|
||||
/* First child node which passed sanity tests. */
|
||||
adcmode = childmode;
|
||||
first = 0;
|
||||
|
||||
priv->num_channels = num_channels;
|
||||
priv->mode = childmode;
|
||||
priv->sample_width = sample_width;
|
||||
|
||||
indio_dev->channels = channels;
|
||||
indio_dev->num_channels = num_channels;
|
||||
|
||||
/*
|
||||
* MB88101 is special and we only have one such device
|
||||
* attached to the GyroADC at a time, so if we found it,
|
||||
* we can stop parsing here.
|
||||
*/
|
||||
if (childmode == RCAR_GYROADC_MODE_SELECT_1_MB88101A)
|
||||
break;
|
||||
}
|
||||
|
||||
if (first) {
|
||||
dev_err(dev, "No valid ADC channels found, aborting.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rcar_gyroadc_deinit_supplies(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct rcar_gyroadc *priv = iio_priv(indio_dev);
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < priv->num_channels; i++) {
|
||||
if (!priv->vref[i])
|
||||
continue;
|
||||
|
||||
regulator_disable(priv->vref[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static int rcar_gyroadc_init_supplies(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct rcar_gyroadc *priv = iio_priv(indio_dev);
|
||||
struct device *dev = priv->dev;
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
for (i = 0; i < priv->num_channels; i++) {
|
||||
if (!priv->vref[i])
|
||||
continue;
|
||||
|
||||
ret = regulator_enable(priv->vref[i]);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to enable regulator %i (ret=%i)\n",
|
||||
i, ret);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
rcar_gyroadc_deinit_supplies(indio_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rcar_gyroadc_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct of_device_id *of_id =
|
||||
of_match_device(rcar_gyroadc_match, &pdev->dev);
|
||||
struct device *dev = &pdev->dev;
|
||||
struct rcar_gyroadc *priv;
|
||||
struct iio_dev *indio_dev;
|
||||
struct resource *mem;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(dev, sizeof(*priv));
|
||||
if (!indio_dev) {
|
||||
dev_err(dev, "Failed to allocate IIO device.\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
priv = iio_priv(indio_dev);
|
||||
priv->dev = dev;
|
||||
|
||||
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
priv->regs = devm_ioremap_resource(dev, mem);
|
||||
if (IS_ERR(priv->regs))
|
||||
return PTR_ERR(priv->regs);
|
||||
|
||||
priv->iclk = devm_clk_get(dev, "if");
|
||||
if (IS_ERR(priv->iclk)) {
|
||||
ret = PTR_ERR(priv->iclk);
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(dev, "Failed to get IF clock (ret=%i)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = rcar_gyroadc_parse_subdevs(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = rcar_gyroadc_init_supplies(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
priv->model = (enum rcar_gyroadc_model)of_id->data;
|
||||
|
||||
platform_set_drvdata(pdev, indio_dev);
|
||||
|
||||
indio_dev->name = DRIVER_NAME;
|
||||
indio_dev->dev.parent = dev;
|
||||
indio_dev->dev.of_node = pdev->dev.of_node;
|
||||
indio_dev->info = &rcar_gyroadc_iio_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
ret = clk_prepare_enable(priv->iclk);
|
||||
if (ret) {
|
||||
dev_err(dev, "Could not prepare or enable the IF clock.\n");
|
||||
goto err_clk_if_enable;
|
||||
}
|
||||
|
||||
pm_runtime_set_autosuspend_delay(dev, RCAR_GYROADC_RUNTIME_PM_DELAY_MS);
|
||||
pm_runtime_use_autosuspend(dev);
|
||||
pm_runtime_enable(dev);
|
||||
|
||||
pm_runtime_get_sync(dev);
|
||||
rcar_gyroadc_hw_init(priv);
|
||||
rcar_gyroadc_hw_start(priv);
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret) {
|
||||
dev_err(dev, "Couldn't register IIO device.\n");
|
||||
goto err_iio_device_register;
|
||||
}
|
||||
|
||||
pm_runtime_put_sync(dev);
|
||||
|
||||
return 0;
|
||||
|
||||
err_iio_device_register:
|
||||
rcar_gyroadc_hw_stop(priv);
|
||||
pm_runtime_put_sync(dev);
|
||||
pm_runtime_disable(dev);
|
||||
pm_runtime_set_suspended(dev);
|
||||
clk_disable_unprepare(priv->iclk);
|
||||
err_clk_if_enable:
|
||||
rcar_gyroadc_deinit_supplies(indio_dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rcar_gyroadc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
struct rcar_gyroadc *priv = iio_priv(indio_dev);
|
||||
struct device *dev = priv->dev;
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
pm_runtime_get_sync(dev);
|
||||
rcar_gyroadc_hw_stop(priv);
|
||||
pm_runtime_put_sync(dev);
|
||||
pm_runtime_disable(dev);
|
||||
pm_runtime_set_suspended(dev);
|
||||
clk_disable_unprepare(priv->iclk);
|
||||
rcar_gyroadc_deinit_supplies(indio_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_PM)
|
||||
static int rcar_gyroadc_suspend(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct rcar_gyroadc *priv = iio_priv(indio_dev);
|
||||
|
||||
rcar_gyroadc_hw_stop(priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rcar_gyroadc_resume(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct rcar_gyroadc *priv = iio_priv(indio_dev);
|
||||
|
||||
rcar_gyroadc_hw_start(priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops rcar_gyroadc_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(rcar_gyroadc_suspend, rcar_gyroadc_resume, NULL)
|
||||
};
|
||||
|
||||
static struct platform_driver rcar_gyroadc_driver = {
|
||||
.probe = rcar_gyroadc_probe,
|
||||
.remove = rcar_gyroadc_remove,
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.of_match_table = rcar_gyroadc_match,
|
||||
.pm = &rcar_gyroadc_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(rcar_gyroadc_driver);
|
||||
|
||||
MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>");
|
||||
MODULE_DESCRIPTION("Renesas R-Car GyroADC driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -201,6 +201,7 @@ static int stm32_adc_probe(struct platform_device *pdev)
|
||||
priv->common.base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(priv->common.base))
|
||||
return PTR_ERR(priv->common.base);
|
||||
priv->common.phys_base = res->start;
|
||||
|
||||
priv->vref = devm_regulator_get(&pdev->dev, "vref");
|
||||
if (IS_ERR(priv->vref)) {
|
||||
|
@ -42,10 +42,12 @@
|
||||
/**
|
||||
* struct stm32_adc_common - stm32 ADC driver common data (for all instances)
|
||||
* @base: control registers base cpu addr
|
||||
* @phys_base: control registers base physical addr
|
||||
* @vref_mv: vref voltage (mv)
|
||||
*/
|
||||
struct stm32_adc_common {
|
||||
void __iomem *base;
|
||||
phys_addr_t phys_base;
|
||||
int vref_mv;
|
||||
};
|
||||
|
||||
|
@ -21,7 +21,14 @@
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/timer/stm32-timer-trigger.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
@ -58,21 +65,71 @@
|
||||
|
||||
/* STM32F4_ADC_CR2 - bit fields */
|
||||
#define STM32F4_SWSTART BIT(30)
|
||||
#define STM32F4_EXTEN_SHIFT 28
|
||||
#define STM32F4_EXTEN_MASK GENMASK(29, 28)
|
||||
#define STM32F4_EXTSEL_SHIFT 24
|
||||
#define STM32F4_EXTSEL_MASK GENMASK(27, 24)
|
||||
#define STM32F4_EOCS BIT(10)
|
||||
#define STM32F4_DDS BIT(9)
|
||||
#define STM32F4_DMA BIT(8)
|
||||
#define STM32F4_ADON BIT(0)
|
||||
|
||||
/* STM32F4_ADC_SQR1 - bit fields */
|
||||
#define STM32F4_L_SHIFT 20
|
||||
#define STM32F4_L_MASK GENMASK(23, 20)
|
||||
|
||||
/* STM32F4_ADC_SQR3 - bit fields */
|
||||
#define STM32F4_SQ1_SHIFT 0
|
||||
#define STM32F4_SQ1_MASK GENMASK(4, 0)
|
||||
|
||||
#define STM32_ADC_MAX_SQ 16 /* SQ1..SQ16 */
|
||||
#define STM32_ADC_TIMEOUT_US 100000
|
||||
#define STM32_ADC_TIMEOUT (msecs_to_jiffies(STM32_ADC_TIMEOUT_US / 1000))
|
||||
|
||||
#define STM32_DMA_BUFFER_SIZE PAGE_SIZE
|
||||
|
||||
/* External trigger enable */
|
||||
enum stm32_adc_exten {
|
||||
STM32_EXTEN_SWTRIG,
|
||||
STM32_EXTEN_HWTRIG_RISING_EDGE,
|
||||
STM32_EXTEN_HWTRIG_FALLING_EDGE,
|
||||
STM32_EXTEN_HWTRIG_BOTH_EDGES,
|
||||
};
|
||||
|
||||
/* extsel - trigger mux selection value */
|
||||
enum stm32_adc_extsel {
|
||||
STM32_EXT0,
|
||||
STM32_EXT1,
|
||||
STM32_EXT2,
|
||||
STM32_EXT3,
|
||||
STM32_EXT4,
|
||||
STM32_EXT5,
|
||||
STM32_EXT6,
|
||||
STM32_EXT7,
|
||||
STM32_EXT8,
|
||||
STM32_EXT9,
|
||||
STM32_EXT10,
|
||||
STM32_EXT11,
|
||||
STM32_EXT12,
|
||||
STM32_EXT13,
|
||||
STM32_EXT14,
|
||||
STM32_EXT15,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct stm32_adc_trig_info - ADC trigger info
|
||||
* @name: name of the trigger, corresponding to its source
|
||||
* @extsel: trigger selection
|
||||
*/
|
||||
struct stm32_adc_trig_info {
|
||||
const char *name;
|
||||
enum stm32_adc_extsel extsel;
|
||||
};
|
||||
|
||||
/**
|
||||
* stm32_adc_regs - stm32 ADC misc registers & bitfield desc
|
||||
* @reg: register offset
|
||||
* @mask: bitfield mask
|
||||
* @shift: left shift
|
||||
*/
|
||||
struct stm32_adc_regs {
|
||||
int reg;
|
||||
int mask;
|
||||
int shift;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct stm32_adc - private data of each ADC IIO instance
|
||||
* @common: reference to ADC block common data
|
||||
@ -82,15 +139,29 @@
|
||||
* @clk: clock for this adc instance
|
||||
* @irq: interrupt for this adc instance
|
||||
* @lock: spinlock
|
||||
* @bufi: data buffer index
|
||||
* @num_conv: expected number of scan conversions
|
||||
* @trigger_polarity: external trigger polarity (e.g. exten)
|
||||
* @dma_chan: dma channel
|
||||
* @rx_buf: dma rx buffer cpu address
|
||||
* @rx_dma_buf: dma rx buffer bus address
|
||||
* @rx_buf_sz: dma rx buffer size
|
||||
*/
|
||||
struct stm32_adc {
|
||||
struct stm32_adc_common *common;
|
||||
u32 offset;
|
||||
struct completion completion;
|
||||
u16 *buffer;
|
||||
u16 buffer[STM32_ADC_MAX_SQ];
|
||||
struct clk *clk;
|
||||
int irq;
|
||||
spinlock_t lock; /* interrupt lock */
|
||||
unsigned int bufi;
|
||||
unsigned int num_conv;
|
||||
u32 trigger_polarity;
|
||||
struct dma_chan *dma_chan;
|
||||
u8 *rx_buf;
|
||||
dma_addr_t rx_dma_buf;
|
||||
unsigned int rx_buf_sz;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -125,6 +196,53 @@ static const struct stm32_adc_chan_spec stm32f4_adc123_channels[] = {
|
||||
{ IIO_VOLTAGE, 15, "in15" },
|
||||
};
|
||||
|
||||
/**
|
||||
* stm32f4_sq - describe regular sequence registers
|
||||
* - L: sequence len (register & bit field)
|
||||
* - SQ1..SQ16: sequence entries (register & bit field)
|
||||
*/
|
||||
static const struct stm32_adc_regs stm32f4_sq[STM32_ADC_MAX_SQ + 1] = {
|
||||
/* L: len bit field description to be kept as first element */
|
||||
{ STM32F4_ADC_SQR1, GENMASK(23, 20), 20 },
|
||||
/* SQ1..SQ16 registers & bit fields (reg, mask, shift) */
|
||||
{ STM32F4_ADC_SQR3, GENMASK(4, 0), 0 },
|
||||
{ STM32F4_ADC_SQR3, GENMASK(9, 5), 5 },
|
||||
{ STM32F4_ADC_SQR3, GENMASK(14, 10), 10 },
|
||||
{ STM32F4_ADC_SQR3, GENMASK(19, 15), 15 },
|
||||
{ STM32F4_ADC_SQR3, GENMASK(24, 20), 20 },
|
||||
{ STM32F4_ADC_SQR3, GENMASK(29, 25), 25 },
|
||||
{ STM32F4_ADC_SQR2, GENMASK(4, 0), 0 },
|
||||
{ STM32F4_ADC_SQR2, GENMASK(9, 5), 5 },
|
||||
{ STM32F4_ADC_SQR2, GENMASK(14, 10), 10 },
|
||||
{ STM32F4_ADC_SQR2, GENMASK(19, 15), 15 },
|
||||
{ STM32F4_ADC_SQR2, GENMASK(24, 20), 20 },
|
||||
{ STM32F4_ADC_SQR2, GENMASK(29, 25), 25 },
|
||||
{ STM32F4_ADC_SQR1, GENMASK(4, 0), 0 },
|
||||
{ STM32F4_ADC_SQR1, GENMASK(9, 5), 5 },
|
||||
{ STM32F4_ADC_SQR1, GENMASK(14, 10), 10 },
|
||||
{ STM32F4_ADC_SQR1, GENMASK(19, 15), 15 },
|
||||
};
|
||||
|
||||
/* STM32F4 external trigger sources for all instances */
|
||||
static struct stm32_adc_trig_info stm32f4_adc_trigs[] = {
|
||||
{ TIM1_CH1, STM32_EXT0 },
|
||||
{ TIM1_CH2, STM32_EXT1 },
|
||||
{ TIM1_CH3, STM32_EXT2 },
|
||||
{ TIM2_CH2, STM32_EXT3 },
|
||||
{ TIM2_CH3, STM32_EXT4 },
|
||||
{ TIM2_CH4, STM32_EXT5 },
|
||||
{ TIM2_TRGO, STM32_EXT6 },
|
||||
{ TIM3_CH1, STM32_EXT7 },
|
||||
{ TIM3_TRGO, STM32_EXT8 },
|
||||
{ TIM4_CH4, STM32_EXT9 },
|
||||
{ TIM5_CH1, STM32_EXT10 },
|
||||
{ TIM5_CH2, STM32_EXT11 },
|
||||
{ TIM5_CH3, STM32_EXT12 },
|
||||
{ TIM8_CH1, STM32_EXT13 },
|
||||
{ TIM8_TRGO, STM32_EXT14 },
|
||||
{}, /* sentinel */
|
||||
};
|
||||
|
||||
/**
|
||||
* STM32 ADC registers access routines
|
||||
* @adc: stm32 adc instance
|
||||
@ -187,10 +305,21 @@ static void stm32_adc_conv_irq_disable(struct stm32_adc *adc)
|
||||
/**
|
||||
* stm32_adc_start_conv() - Start conversions for regular channels.
|
||||
* @adc: stm32 adc instance
|
||||
* @dma: use dma to transfer conversion result
|
||||
*
|
||||
* Start conversions for regular channels.
|
||||
* Also take care of normal or DMA mode. Circular DMA may be used for regular
|
||||
* conversions, in IIO buffer modes. Otherwise, use ADC interrupt with direct
|
||||
* DR read instead (e.g. read_raw, or triggered buffer mode without DMA).
|
||||
*/
|
||||
static void stm32_adc_start_conv(struct stm32_adc *adc)
|
||||
static void stm32_adc_start_conv(struct stm32_adc *adc, bool dma)
|
||||
{
|
||||
stm32_adc_set_bits(adc, STM32F4_ADC_CR1, STM32F4_SCAN);
|
||||
|
||||
if (dma)
|
||||
stm32_adc_set_bits(adc, STM32F4_ADC_CR2,
|
||||
STM32F4_DMA | STM32F4_DDS);
|
||||
|
||||
stm32_adc_set_bits(adc, STM32F4_ADC_CR2, STM32F4_EOCS | STM32F4_ADON);
|
||||
|
||||
/* Wait for Power-up time (tSTAB from datasheet) */
|
||||
@ -207,9 +336,152 @@ static void stm32_adc_stop_conv(struct stm32_adc *adc)
|
||||
stm32_adc_clr_bits(adc, STM32F4_ADC_SR, STM32F4_STRT);
|
||||
|
||||
stm32_adc_clr_bits(adc, STM32F4_ADC_CR1, STM32F4_SCAN);
|
||||
stm32_adc_clr_bits(adc, STM32F4_ADC_CR2, STM32F4_ADON);
|
||||
stm32_adc_clr_bits(adc, STM32F4_ADC_CR2,
|
||||
STM32F4_ADON | STM32F4_DMA | STM32F4_DDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* stm32_adc_conf_scan_seq() - Build regular channels scan sequence
|
||||
* @indio_dev: IIO device
|
||||
* @scan_mask: channels to be converted
|
||||
*
|
||||
* Conversion sequence :
|
||||
* Configure ADC scan sequence based on selected channels in scan_mask.
|
||||
* Add channels to SQR registers, from scan_mask LSB to MSB, then
|
||||
* program sequence len.
|
||||
*/
|
||||
static int stm32_adc_conf_scan_seq(struct iio_dev *indio_dev,
|
||||
const unsigned long *scan_mask)
|
||||
{
|
||||
struct stm32_adc *adc = iio_priv(indio_dev);
|
||||
const struct iio_chan_spec *chan;
|
||||
u32 val, bit;
|
||||
int i = 0;
|
||||
|
||||
for_each_set_bit(bit, scan_mask, indio_dev->masklength) {
|
||||
chan = indio_dev->channels + bit;
|
||||
/*
|
||||
* Assign one channel per SQ entry in regular
|
||||
* sequence, starting with SQ1.
|
||||
*/
|
||||
i++;
|
||||
if (i > STM32_ADC_MAX_SQ)
|
||||
return -EINVAL;
|
||||
|
||||
dev_dbg(&indio_dev->dev, "%s chan %d to SQ%d\n",
|
||||
__func__, chan->channel, i);
|
||||
|
||||
val = stm32_adc_readl(adc, stm32f4_sq[i].reg);
|
||||
val &= ~stm32f4_sq[i].mask;
|
||||
val |= chan->channel << stm32f4_sq[i].shift;
|
||||
stm32_adc_writel(adc, stm32f4_sq[i].reg, val);
|
||||
}
|
||||
|
||||
if (!i)
|
||||
return -EINVAL;
|
||||
|
||||
/* Sequence len */
|
||||
val = stm32_adc_readl(adc, stm32f4_sq[0].reg);
|
||||
val &= ~stm32f4_sq[0].mask;
|
||||
val |= ((i - 1) << stm32f4_sq[0].shift);
|
||||
stm32_adc_writel(adc, stm32f4_sq[0].reg, val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* stm32_adc_get_trig_extsel() - Get external trigger selection
|
||||
* @trig: trigger
|
||||
*
|
||||
* Returns trigger extsel value, if trig matches, -EINVAL otherwise.
|
||||
*/
|
||||
static int stm32_adc_get_trig_extsel(struct iio_trigger *trig)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* lookup triggers registered by stm32 timer trigger driver */
|
||||
for (i = 0; stm32f4_adc_trigs[i].name; i++) {
|
||||
/**
|
||||
* Checking both stm32 timer trigger type and trig name
|
||||
* should be safe against arbitrary trigger names.
|
||||
*/
|
||||
if (is_stm32_timer_trigger(trig) &&
|
||||
!strcmp(stm32f4_adc_trigs[i].name, trig->name)) {
|
||||
return stm32f4_adc_trigs[i].extsel;
|
||||
}
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* stm32_adc_set_trig() - Set a regular trigger
|
||||
* @indio_dev: IIO device
|
||||
* @trig: IIO trigger
|
||||
*
|
||||
* Set trigger source/polarity (e.g. SW, or HW with polarity) :
|
||||
* - if HW trigger disabled (e.g. trig == NULL, conversion launched by sw)
|
||||
* - if HW trigger enabled, set source & polarity
|
||||
*/
|
||||
static int stm32_adc_set_trig(struct iio_dev *indio_dev,
|
||||
struct iio_trigger *trig)
|
||||
{
|
||||
struct stm32_adc *adc = iio_priv(indio_dev);
|
||||
u32 val, extsel = 0, exten = STM32_EXTEN_SWTRIG;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
if (trig) {
|
||||
ret = stm32_adc_get_trig_extsel(trig);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* set trigger source and polarity (default to rising edge) */
|
||||
extsel = ret;
|
||||
exten = adc->trigger_polarity + STM32_EXTEN_HWTRIG_RISING_EDGE;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&adc->lock, flags);
|
||||
val = stm32_adc_readl(adc, STM32F4_ADC_CR2);
|
||||
val &= ~(STM32F4_EXTEN_MASK | STM32F4_EXTSEL_MASK);
|
||||
val |= exten << STM32F4_EXTEN_SHIFT;
|
||||
val |= extsel << STM32F4_EXTSEL_SHIFT;
|
||||
stm32_adc_writel(adc, STM32F4_ADC_CR2, val);
|
||||
spin_unlock_irqrestore(&adc->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stm32_adc_set_trig_pol(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
unsigned int type)
|
||||
{
|
||||
struct stm32_adc *adc = iio_priv(indio_dev);
|
||||
|
||||
adc->trigger_polarity = type;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stm32_adc_get_trig_pol(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan)
|
||||
{
|
||||
struct stm32_adc *adc = iio_priv(indio_dev);
|
||||
|
||||
return adc->trigger_polarity;
|
||||
}
|
||||
|
||||
static const char * const stm32_trig_pol_items[] = {
|
||||
"rising-edge", "falling-edge", "both-edges",
|
||||
};
|
||||
|
||||
static const struct iio_enum stm32_adc_trig_pol = {
|
||||
.items = stm32_trig_pol_items,
|
||||
.num_items = ARRAY_SIZE(stm32_trig_pol_items),
|
||||
.get = stm32_adc_get_trig_pol,
|
||||
.set = stm32_adc_set_trig_pol,
|
||||
};
|
||||
|
||||
/**
|
||||
* stm32_adc_single_conv() - Performs a single conversion
|
||||
* @indio_dev: IIO device
|
||||
@ -228,28 +500,27 @@ static int stm32_adc_single_conv(struct iio_dev *indio_dev,
|
||||
struct stm32_adc *adc = iio_priv(indio_dev);
|
||||
long timeout;
|
||||
u32 val;
|
||||
u16 result;
|
||||
int ret;
|
||||
|
||||
reinit_completion(&adc->completion);
|
||||
|
||||
adc->buffer = &result;
|
||||
adc->bufi = 0;
|
||||
|
||||
/* Program chan number in regular sequence */
|
||||
val = stm32_adc_readl(adc, STM32F4_ADC_SQR3);
|
||||
val &= ~STM32F4_SQ1_MASK;
|
||||
val |= chan->channel << STM32F4_SQ1_SHIFT;
|
||||
stm32_adc_writel(adc, STM32F4_ADC_SQR3, val);
|
||||
/* Program chan number in regular sequence (SQ1) */
|
||||
val = stm32_adc_readl(adc, stm32f4_sq[1].reg);
|
||||
val &= ~stm32f4_sq[1].mask;
|
||||
val |= chan->channel << stm32f4_sq[1].shift;
|
||||
stm32_adc_writel(adc, stm32f4_sq[1].reg, val);
|
||||
|
||||
/* Set regular sequence len (0 for 1 conversion) */
|
||||
stm32_adc_clr_bits(adc, STM32F4_ADC_SQR1, STM32F4_L_MASK);
|
||||
stm32_adc_clr_bits(adc, stm32f4_sq[0].reg, stm32f4_sq[0].mask);
|
||||
|
||||
/* Trigger detection disabled (conversion can be launched in SW) */
|
||||
stm32_adc_clr_bits(adc, STM32F4_ADC_CR2, STM32F4_EXTEN_MASK);
|
||||
|
||||
stm32_adc_conv_irq_enable(adc);
|
||||
|
||||
stm32_adc_start_conv(adc);
|
||||
stm32_adc_start_conv(adc, false);
|
||||
|
||||
timeout = wait_for_completion_interruptible_timeout(
|
||||
&adc->completion, STM32_ADC_TIMEOUT);
|
||||
@ -258,7 +529,7 @@ static int stm32_adc_single_conv(struct iio_dev *indio_dev,
|
||||
} else if (timeout < 0) {
|
||||
ret = timeout;
|
||||
} else {
|
||||
*res = result;
|
||||
*res = adc->buffer[0];
|
||||
ret = IIO_VAL_INT;
|
||||
}
|
||||
|
||||
@ -301,17 +572,73 @@ static int stm32_adc_read_raw(struct iio_dev *indio_dev,
|
||||
static irqreturn_t stm32_adc_isr(int irq, void *data)
|
||||
{
|
||||
struct stm32_adc *adc = data;
|
||||
struct iio_dev *indio_dev = iio_priv_to_dev(adc);
|
||||
u32 status = stm32_adc_readl(adc, STM32F4_ADC_SR);
|
||||
|
||||
if (status & STM32F4_EOC) {
|
||||
*adc->buffer = stm32_adc_readw(adc, STM32F4_ADC_DR);
|
||||
complete(&adc->completion);
|
||||
/* Reading DR also clears EOC status flag */
|
||||
adc->buffer[adc->bufi] = stm32_adc_readw(adc, STM32F4_ADC_DR);
|
||||
if (iio_buffer_enabled(indio_dev)) {
|
||||
adc->bufi++;
|
||||
if (adc->bufi >= adc->num_conv) {
|
||||
stm32_adc_conv_irq_disable(adc);
|
||||
iio_trigger_poll(indio_dev->trig);
|
||||
}
|
||||
} else {
|
||||
complete(&adc->completion);
|
||||
}
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* stm32_adc_validate_trigger() - validate trigger for stm32 adc
|
||||
* @indio_dev: IIO device
|
||||
* @trig: new trigger
|
||||
*
|
||||
* Returns: 0 if trig matches one of the triggers registered by stm32 adc
|
||||
* driver, -EINVAL otherwise.
|
||||
*/
|
||||
static int stm32_adc_validate_trigger(struct iio_dev *indio_dev,
|
||||
struct iio_trigger *trig)
|
||||
{
|
||||
return stm32_adc_get_trig_extsel(trig) < 0 ? -EINVAL : 0;
|
||||
}
|
||||
|
||||
static int stm32_adc_set_watermark(struct iio_dev *indio_dev, unsigned int val)
|
||||
{
|
||||
struct stm32_adc *adc = iio_priv(indio_dev);
|
||||
unsigned int watermark = STM32_DMA_BUFFER_SIZE / 2;
|
||||
|
||||
/*
|
||||
* dma cyclic transfers are used, buffer is split into two periods.
|
||||
* There should be :
|
||||
* - always one buffer (period) dma is working on
|
||||
* - one buffer (period) driver can push with iio_trigger_poll().
|
||||
*/
|
||||
watermark = min(watermark, val * (unsigned)(sizeof(u16)));
|
||||
adc->rx_buf_sz = watermark * 2;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stm32_adc_update_scan_mode(struct iio_dev *indio_dev,
|
||||
const unsigned long *scan_mask)
|
||||
{
|
||||
struct stm32_adc *adc = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
adc->num_conv = bitmap_weight(scan_mask, indio_dev->masklength);
|
||||
|
||||
ret = stm32_adc_conf_scan_seq(indio_dev, scan_mask);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stm32_adc_of_xlate(struct iio_dev *indio_dev,
|
||||
const struct of_phandle_args *iiospec)
|
||||
{
|
||||
@ -350,11 +677,199 @@ static int stm32_adc_debugfs_reg_access(struct iio_dev *indio_dev,
|
||||
|
||||
static const struct iio_info stm32_adc_iio_info = {
|
||||
.read_raw = stm32_adc_read_raw,
|
||||
.validate_trigger = stm32_adc_validate_trigger,
|
||||
.hwfifo_set_watermark = stm32_adc_set_watermark,
|
||||
.update_scan_mode = stm32_adc_update_scan_mode,
|
||||
.debugfs_reg_access = stm32_adc_debugfs_reg_access,
|
||||
.of_xlate = stm32_adc_of_xlate,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static unsigned int stm32_adc_dma_residue(struct stm32_adc *adc)
|
||||
{
|
||||
struct dma_tx_state state;
|
||||
enum dma_status status;
|
||||
|
||||
status = dmaengine_tx_status(adc->dma_chan,
|
||||
adc->dma_chan->cookie,
|
||||
&state);
|
||||
if (status == DMA_IN_PROGRESS) {
|
||||
/* Residue is size in bytes from end of buffer */
|
||||
unsigned int i = adc->rx_buf_sz - state.residue;
|
||||
unsigned int size;
|
||||
|
||||
/* Return available bytes */
|
||||
if (i >= adc->bufi)
|
||||
size = i - adc->bufi;
|
||||
else
|
||||
size = adc->rx_buf_sz + i - adc->bufi;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void stm32_adc_dma_buffer_done(void *data)
|
||||
{
|
||||
struct iio_dev *indio_dev = data;
|
||||
|
||||
iio_trigger_poll_chained(indio_dev->trig);
|
||||
}
|
||||
|
||||
static int stm32_adc_dma_start(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct stm32_adc *adc = iio_priv(indio_dev);
|
||||
struct dma_async_tx_descriptor *desc;
|
||||
dma_cookie_t cookie;
|
||||
int ret;
|
||||
|
||||
if (!adc->dma_chan)
|
||||
return 0;
|
||||
|
||||
dev_dbg(&indio_dev->dev, "%s size=%d watermark=%d\n", __func__,
|
||||
adc->rx_buf_sz, adc->rx_buf_sz / 2);
|
||||
|
||||
/* Prepare a DMA cyclic transaction */
|
||||
desc = dmaengine_prep_dma_cyclic(adc->dma_chan,
|
||||
adc->rx_dma_buf,
|
||||
adc->rx_buf_sz, adc->rx_buf_sz / 2,
|
||||
DMA_DEV_TO_MEM,
|
||||
DMA_PREP_INTERRUPT);
|
||||
if (!desc)
|
||||
return -EBUSY;
|
||||
|
||||
desc->callback = stm32_adc_dma_buffer_done;
|
||||
desc->callback_param = indio_dev;
|
||||
|
||||
cookie = dmaengine_submit(desc);
|
||||
ret = dma_submit_error(cookie);
|
||||
if (ret) {
|
||||
dmaengine_terminate_all(adc->dma_chan);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Issue pending DMA requests */
|
||||
dma_async_issue_pending(adc->dma_chan);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stm32_adc_buffer_postenable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct stm32_adc *adc = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = stm32_adc_set_trig(indio_dev, indio_dev->trig);
|
||||
if (ret) {
|
||||
dev_err(&indio_dev->dev, "Can't set trigger\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = stm32_adc_dma_start(indio_dev);
|
||||
if (ret) {
|
||||
dev_err(&indio_dev->dev, "Can't start dma\n");
|
||||
goto err_clr_trig;
|
||||
}
|
||||
|
||||
ret = iio_triggered_buffer_postenable(indio_dev);
|
||||
if (ret < 0)
|
||||
goto err_stop_dma;
|
||||
|
||||
/* Reset adc buffer index */
|
||||
adc->bufi = 0;
|
||||
|
||||
if (!adc->dma_chan)
|
||||
stm32_adc_conv_irq_enable(adc);
|
||||
|
||||
stm32_adc_start_conv(adc, !!adc->dma_chan);
|
||||
|
||||
return 0;
|
||||
|
||||
err_stop_dma:
|
||||
if (adc->dma_chan)
|
||||
dmaengine_terminate_all(adc->dma_chan);
|
||||
err_clr_trig:
|
||||
stm32_adc_set_trig(indio_dev, NULL);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int stm32_adc_buffer_predisable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct stm32_adc *adc = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
stm32_adc_stop_conv(adc);
|
||||
if (!adc->dma_chan)
|
||||
stm32_adc_conv_irq_disable(adc);
|
||||
|
||||
ret = iio_triggered_buffer_predisable(indio_dev);
|
||||
if (ret < 0)
|
||||
dev_err(&indio_dev->dev, "predisable failed\n");
|
||||
|
||||
if (adc->dma_chan)
|
||||
dmaengine_terminate_all(adc->dma_chan);
|
||||
|
||||
if (stm32_adc_set_trig(indio_dev, NULL))
|
||||
dev_err(&indio_dev->dev, "Can't clear trigger\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct iio_buffer_setup_ops stm32_adc_buffer_setup_ops = {
|
||||
.postenable = &stm32_adc_buffer_postenable,
|
||||
.predisable = &stm32_adc_buffer_predisable,
|
||||
};
|
||||
|
||||
static irqreturn_t stm32_adc_trigger_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct stm32_adc *adc = iio_priv(indio_dev);
|
||||
|
||||
dev_dbg(&indio_dev->dev, "%s bufi=%d\n", __func__, adc->bufi);
|
||||
|
||||
if (!adc->dma_chan) {
|
||||
/* reset buffer index */
|
||||
adc->bufi = 0;
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, adc->buffer,
|
||||
pf->timestamp);
|
||||
} else {
|
||||
int residue = stm32_adc_dma_residue(adc);
|
||||
|
||||
while (residue >= indio_dev->scan_bytes) {
|
||||
u16 *buffer = (u16 *)&adc->rx_buf[adc->bufi];
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, buffer,
|
||||
pf->timestamp);
|
||||
residue -= indio_dev->scan_bytes;
|
||||
adc->bufi += indio_dev->scan_bytes;
|
||||
if (adc->bufi >= adc->rx_buf_sz)
|
||||
adc->bufi = 0;
|
||||
}
|
||||
}
|
||||
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
/* re-enable eoc irq */
|
||||
if (!adc->dma_chan)
|
||||
stm32_adc_conv_irq_enable(adc);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec_ext_info stm32_adc_ext_info[] = {
|
||||
IIO_ENUM("trigger_polarity", IIO_SHARED_BY_ALL, &stm32_adc_trig_pol),
|
||||
{
|
||||
.name = "trigger_polarity_available",
|
||||
.shared = IIO_SHARED_BY_ALL,
|
||||
.read = iio_enum_available_read,
|
||||
.private = (uintptr_t)&stm32_adc_trig_pol,
|
||||
},
|
||||
{},
|
||||
};
|
||||
|
||||
static void stm32_adc_chan_init_one(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec *chan,
|
||||
const struct stm32_adc_chan_spec *channel,
|
||||
@ -370,6 +885,7 @@ static void stm32_adc_chan_init_one(struct iio_dev *indio_dev,
|
||||
chan->scan_type.sign = 'u';
|
||||
chan->scan_type.realbits = 12;
|
||||
chan->scan_type.storagebits = 16;
|
||||
chan->ext_info = stm32_adc_ext_info;
|
||||
}
|
||||
|
||||
static int stm32_adc_chan_of_init(struct iio_dev *indio_dev)
|
||||
@ -410,6 +926,45 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stm32_adc_dma_request(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct stm32_adc *adc = iio_priv(indio_dev);
|
||||
struct dma_slave_config config;
|
||||
int ret;
|
||||
|
||||
adc->dma_chan = dma_request_slave_channel(&indio_dev->dev, "rx");
|
||||
if (!adc->dma_chan)
|
||||
return 0;
|
||||
|
||||
adc->rx_buf = dma_alloc_coherent(adc->dma_chan->device->dev,
|
||||
STM32_DMA_BUFFER_SIZE,
|
||||
&adc->rx_dma_buf, GFP_KERNEL);
|
||||
if (!adc->rx_buf) {
|
||||
ret = -ENOMEM;
|
||||
goto err_release;
|
||||
}
|
||||
|
||||
/* Configure DMA channel to read data register */
|
||||
memset(&config, 0, sizeof(config));
|
||||
config.src_addr = (dma_addr_t)adc->common->phys_base;
|
||||
config.src_addr += adc->offset + STM32F4_ADC_DR;
|
||||
config.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
|
||||
|
||||
ret = dmaengine_slave_config(adc->dma_chan, &config);
|
||||
if (ret)
|
||||
goto err_free;
|
||||
|
||||
return 0;
|
||||
|
||||
err_free:
|
||||
dma_free_coherent(adc->dma_chan->device->dev, STM32_DMA_BUFFER_SIZE,
|
||||
adc->rx_buf, adc->rx_dma_buf);
|
||||
err_release:
|
||||
dma_release_channel(adc->dma_chan);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int stm32_adc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
@ -471,14 +1026,37 @@ static int stm32_adc_probe(struct platform_device *pdev)
|
||||
if (ret < 0)
|
||||
goto err_clk_disable;
|
||||
|
||||
ret = stm32_adc_dma_request(indio_dev);
|
||||
if (ret < 0)
|
||||
goto err_clk_disable;
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev,
|
||||
&iio_pollfunc_store_time,
|
||||
&stm32_adc_trigger_handler,
|
||||
&stm32_adc_buffer_setup_ops);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "buffer setup failed\n");
|
||||
goto err_dma_disable;
|
||||
}
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "iio dev register failed\n");
|
||||
goto err_clk_disable;
|
||||
goto err_buffer_cleanup;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_buffer_cleanup:
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
|
||||
err_dma_disable:
|
||||
if (adc->dma_chan) {
|
||||
dma_free_coherent(adc->dma_chan->device->dev,
|
||||
STM32_DMA_BUFFER_SIZE,
|
||||
adc->rx_buf, adc->rx_dma_buf);
|
||||
dma_release_channel(adc->dma_chan);
|
||||
}
|
||||
err_clk_disable:
|
||||
clk_disable_unprepare(adc->clk);
|
||||
|
||||
@ -491,6 +1069,13 @@ static int stm32_adc_remove(struct platform_device *pdev)
|
||||
struct iio_dev *indio_dev = iio_priv_to_dev(adc);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
if (adc->dma_chan) {
|
||||
dma_free_coherent(adc->dma_chan->device->dev,
|
||||
STM32_DMA_BUFFER_SIZE,
|
||||
adc->rx_buf, adc->rx_dma_buf);
|
||||
dma_release_channel(adc->dma_chan);
|
||||
}
|
||||
clk_disable_unprepare(adc->clk);
|
||||
|
||||
return 0;
|
||||
|
@ -76,16 +76,6 @@ struct stx104_gpio {
|
||||
unsigned int out_state;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct stx104_dev - STX104 device private data structure
|
||||
* @indio_dev: IIO device
|
||||
* @chip: instance of the gpio_chip
|
||||
*/
|
||||
struct stx104_dev {
|
||||
struct iio_dev *indio_dev;
|
||||
struct gpio_chip *chip;
|
||||
};
|
||||
|
||||
static int stx104_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val, int *val2, long mask)
|
||||
{
|
||||
@ -266,12 +256,38 @@ static void stx104_gpio_set(struct gpio_chip *chip, unsigned int offset,
|
||||
spin_unlock_irqrestore(&stx104gpio->lock, flags);
|
||||
}
|
||||
|
||||
#define STX104_NGPIO 8
|
||||
static const char *stx104_names[STX104_NGPIO] = {
|
||||
"DIN0", "DIN1", "DIN2", "DIN3", "DOUT0", "DOUT1", "DOUT2", "DOUT3"
|
||||
};
|
||||
|
||||
static void stx104_gpio_set_multiple(struct gpio_chip *chip,
|
||||
unsigned long *mask, unsigned long *bits)
|
||||
{
|
||||
struct stx104_gpio *const stx104gpio = gpiochip_get_data(chip);
|
||||
unsigned long flags;
|
||||
|
||||
/* verify masked GPIO are output */
|
||||
if (!(*mask & 0xF0))
|
||||
return;
|
||||
|
||||
*mask >>= 4;
|
||||
*bits >>= 4;
|
||||
|
||||
spin_lock_irqsave(&stx104gpio->lock, flags);
|
||||
|
||||
stx104gpio->out_state &= ~*mask;
|
||||
stx104gpio->out_state |= *mask & *bits;
|
||||
outb(stx104gpio->out_state, stx104gpio->base);
|
||||
|
||||
spin_unlock_irqrestore(&stx104gpio->lock, flags);
|
||||
}
|
||||
|
||||
static int stx104_probe(struct device *dev, unsigned int id)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct stx104_iio *priv;
|
||||
struct stx104_gpio *stx104gpio;
|
||||
struct stx104_dev *stx104dev;
|
||||
int err;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(dev, sizeof(*priv));
|
||||
@ -282,10 +298,6 @@ static int stx104_probe(struct device *dev, unsigned int id)
|
||||
if (!stx104gpio)
|
||||
return -ENOMEM;
|
||||
|
||||
stx104dev = devm_kzalloc(dev, sizeof(*stx104dev), GFP_KERNEL);
|
||||
if (!stx104dev)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!devm_request_region(dev, base[id], STX104_EXTENT,
|
||||
dev_name(dev))) {
|
||||
dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n",
|
||||
@ -324,45 +336,26 @@ static int stx104_probe(struct device *dev, unsigned int id)
|
||||
stx104gpio->chip.parent = dev;
|
||||
stx104gpio->chip.owner = THIS_MODULE;
|
||||
stx104gpio->chip.base = -1;
|
||||
stx104gpio->chip.ngpio = 8;
|
||||
stx104gpio->chip.ngpio = STX104_NGPIO;
|
||||
stx104gpio->chip.names = stx104_names;
|
||||
stx104gpio->chip.get_direction = stx104_gpio_get_direction;
|
||||
stx104gpio->chip.direction_input = stx104_gpio_direction_input;
|
||||
stx104gpio->chip.direction_output = stx104_gpio_direction_output;
|
||||
stx104gpio->chip.get = stx104_gpio_get;
|
||||
stx104gpio->chip.set = stx104_gpio_set;
|
||||
stx104gpio->chip.set_multiple = stx104_gpio_set_multiple;
|
||||
stx104gpio->base = base[id] + 3;
|
||||
stx104gpio->out_state = 0x0;
|
||||
|
||||
spin_lock_init(&stx104gpio->lock);
|
||||
|
||||
stx104dev->indio_dev = indio_dev;
|
||||
stx104dev->chip = &stx104gpio->chip;
|
||||
dev_set_drvdata(dev, stx104dev);
|
||||
|
||||
err = gpiochip_add_data(&stx104gpio->chip, stx104gpio);
|
||||
err = devm_gpiochip_add_data(dev, &stx104gpio->chip, stx104gpio);
|
||||
if (err) {
|
||||
dev_err(dev, "GPIO registering failed (%d)\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = iio_device_register(indio_dev);
|
||||
if (err) {
|
||||
dev_err(dev, "IIO device registering failed (%d)\n", err);
|
||||
gpiochip_remove(&stx104gpio->chip);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stx104_remove(struct device *dev, unsigned int id)
|
||||
{
|
||||
struct stx104_dev *const stx104dev = dev_get_drvdata(dev);
|
||||
|
||||
iio_device_unregister(stx104dev->indio_dev);
|
||||
gpiochip_remove(stx104dev->chip);
|
||||
|
||||
return 0;
|
||||
return devm_iio_device_register(dev, indio_dev);
|
||||
}
|
||||
|
||||
static struct isa_driver stx104_driver = {
|
||||
@ -370,7 +363,6 @@ static struct isa_driver stx104_driver = {
|
||||
.driver = {
|
||||
.name = "stx104"
|
||||
},
|
||||
.remove = stx104_remove
|
||||
};
|
||||
|
||||
module_isa_driver(stx104_driver, num_stx104);
|
||||
|
@ -135,7 +135,7 @@ static int ssp_gyro_probe(struct platform_device *pdev)
|
||||
|
||||
platform_set_drvdata(pdev, indio_dev);
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
ret = devm_iio_device_register(&pdev->dev, indio_dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@ -145,21 +145,11 @@ static int ssp_gyro_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ssp_gyro_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver ssp_gyro_driver = {
|
||||
.driver = {
|
||||
.name = SSP_GYROSCOPE_NAME,
|
||||
},
|
||||
.probe = ssp_gyro_probe,
|
||||
.remove = ssp_gyro_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(ssp_gyro_driver);
|
||||
|
@ -37,11 +37,14 @@
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
#include <linux/platform_data/st_sensors_pdata.h>
|
||||
|
||||
#include "st_lsm6dsx.h"
|
||||
|
||||
#define ST_LSM6DSX_REG_ACC_DEC_MASK GENMASK(2, 0)
|
||||
#define ST_LSM6DSX_REG_GYRO_DEC_MASK GENMASK(5, 3)
|
||||
#define ST_LSM6DSX_REG_INT1_ADDR 0x0d
|
||||
#define ST_LSM6DSX_REG_INT2_ADDR 0x0e
|
||||
#define ST_LSM6DSX_REG_FIFO_FTH_IRQ_MASK BIT(3)
|
||||
#define ST_LSM6DSX_REG_WHOAMI_ADDR 0x0f
|
||||
#define ST_LSM6DSX_REG_RESET_ADDR 0x12
|
||||
@ -532,10 +535,56 @@ static const struct iio_info st_lsm6dsx_gyro_info = {
|
||||
|
||||
static const unsigned long st_lsm6dsx_available_scan_masks[] = {0x7, 0x0};
|
||||
|
||||
static int st_lsm6dsx_of_get_drdy_pin(struct st_lsm6dsx_hw *hw, int *drdy_pin)
|
||||
{
|
||||
struct device_node *np = hw->dev->of_node;
|
||||
int err;
|
||||
|
||||
if (!np)
|
||||
return -EINVAL;
|
||||
|
||||
err = of_property_read_u32(np, "st,drdy-int-pin", drdy_pin);
|
||||
if (err == -ENODATA) {
|
||||
/* if the property has not been specified use default value */
|
||||
*drdy_pin = 1;
|
||||
err = 0;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int st_lsm6dsx_get_drdy_reg(struct st_lsm6dsx_hw *hw, u8 *drdy_reg)
|
||||
{
|
||||
int err = 0, drdy_pin;
|
||||
|
||||
if (st_lsm6dsx_of_get_drdy_pin(hw, &drdy_pin) < 0) {
|
||||
struct st_sensors_platform_data *pdata;
|
||||
struct device *dev = hw->dev;
|
||||
|
||||
pdata = (struct st_sensors_platform_data *)dev->platform_data;
|
||||
drdy_pin = pdata ? pdata->drdy_int_pin : 1;
|
||||
}
|
||||
|
||||
switch (drdy_pin) {
|
||||
case 1:
|
||||
*drdy_reg = ST_LSM6DSX_REG_INT1_ADDR;
|
||||
break;
|
||||
case 2:
|
||||
*drdy_reg = ST_LSM6DSX_REG_INT2_ADDR;
|
||||
break;
|
||||
default:
|
||||
dev_err(hw->dev, "unsupported data ready pin\n");
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int st_lsm6dsx_init_device(struct st_lsm6dsx_hw *hw)
|
||||
{
|
||||
u8 data, drdy_int_reg;
|
||||
int err;
|
||||
u8 data;
|
||||
|
||||
data = ST_LSM6DSX_REG_RESET_MASK;
|
||||
err = hw->tf->write(hw->dev, ST_LSM6DSX_REG_RESET_ADDR, sizeof(data),
|
||||
@ -563,14 +612,12 @@ static int st_lsm6dsx_init_device(struct st_lsm6dsx_hw *hw)
|
||||
return err;
|
||||
|
||||
/* enable FIFO watermak interrupt */
|
||||
err = st_lsm6dsx_write_with_mask(hw, ST_LSM6DSX_REG_INT1_ADDR,
|
||||
ST_LSM6DSX_REG_FIFO_FTH_IRQ_MASK, 1);
|
||||
err = st_lsm6dsx_get_drdy_reg(hw, &drdy_int_reg);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* redirect INT2 on INT1 */
|
||||
return st_lsm6dsx_write_with_mask(hw, ST_LSM6DSX_REG_INT2_ON_INT1_ADDR,
|
||||
ST_LSM6DSX_REG_INT2_ON_INT1_MASK, 1);
|
||||
return st_lsm6dsx_write_with_mask(hw, drdy_int_reg,
|
||||
ST_LSM6DSX_REG_FIFO_FTH_IRQ_MASK, 1);
|
||||
}
|
||||
|
||||
static struct iio_dev *st_lsm6dsx_alloc_iiodev(struct st_lsm6dsx_hw *hw,
|
||||
|
@ -15,6 +15,17 @@ config DS1803
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ds1803.
|
||||
|
||||
config MAX5481
|
||||
tristate "Maxim MAX5481-MAX5484 Digital Potentiometer driver"
|
||||
depends on SPI
|
||||
help
|
||||
Say yes here to build support for the Maxim
|
||||
MAX5481, MAX5482, MAX5483, MAX5484 digital potentiometer
|
||||
chips.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called max5481.
|
||||
|
||||
config MAX5487
|
||||
tristate "Maxim MAX5487/MAX5488/MAX5489 Digital Potentiometer driver"
|
||||
depends on SPI
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
# When adding new entries keep the list in alphabetical order
|
||||
obj-$(CONFIG_DS1803) += ds1803.o
|
||||
obj-$(CONFIG_MAX5481) += max5481.o
|
||||
obj-$(CONFIG_MAX5487) += max5487.o
|
||||
obj-$(CONFIG_MCP4131) += mcp4131.o
|
||||
obj-$(CONFIG_MCP4531) += mcp4531.o
|
||||
|
223
drivers/iio/potentiometer/max5481.c
Normal file
223
drivers/iio/potentiometer/max5481.c
Normal file
@ -0,0 +1,223 @@
|
||||
/*
|
||||
* Maxim Integrated MAX5481-MAX5484 digital potentiometer driver
|
||||
* Copyright 2016 Rockwell Collins
|
||||
*
|
||||
* Datasheet:
|
||||
* http://datasheets.maximintegrated.com/en/ds/MAX5481-MAX5484.pdf
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the gnu general public license version 2 as
|
||||
* published by the free software foundation.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
/* write wiper reg */
|
||||
#define MAX5481_WRITE_WIPER (0 << 4)
|
||||
/* copy wiper reg to NV reg */
|
||||
#define MAX5481_COPY_AB_TO_NV (2 << 4)
|
||||
/* copy NV reg to wiper reg */
|
||||
#define MAX5481_COPY_NV_TO_AB (3 << 4)
|
||||
|
||||
#define MAX5481_MAX_POS 1023
|
||||
|
||||
enum max5481_variant {
|
||||
max5481,
|
||||
max5482,
|
||||
max5483,
|
||||
max5484,
|
||||
};
|
||||
|
||||
struct max5481_cfg {
|
||||
int kohms;
|
||||
};
|
||||
|
||||
static const struct max5481_cfg max5481_cfg[] = {
|
||||
[max5481] = { .kohms = 10, },
|
||||
[max5482] = { .kohms = 50, },
|
||||
[max5483] = { .kohms = 10, },
|
||||
[max5484] = { .kohms = 50, },
|
||||
};
|
||||
|
||||
struct max5481_data {
|
||||
struct spi_device *spi;
|
||||
const struct max5481_cfg *cfg;
|
||||
u8 msg[3] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
#define MAX5481_CHANNEL { \
|
||||
.type = IIO_RESISTANCE, \
|
||||
.indexed = 1, \
|
||||
.output = 1, \
|
||||
.channel = 0, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec max5481_channels[] = {
|
||||
MAX5481_CHANNEL,
|
||||
};
|
||||
|
||||
static int max5481_write_cmd(struct max5481_data *data, u8 cmd, u16 val)
|
||||
{
|
||||
struct spi_device *spi = data->spi;
|
||||
|
||||
data->msg[0] = cmd;
|
||||
|
||||
switch (cmd) {
|
||||
case MAX5481_WRITE_WIPER:
|
||||
data->msg[1] = val >> 2;
|
||||
data->msg[2] = (val & 0x3) << 6;
|
||||
return spi_write(spi, data->msg, 3);
|
||||
|
||||
case MAX5481_COPY_AB_TO_NV:
|
||||
case MAX5481_COPY_NV_TO_AB:
|
||||
return spi_write(spi, data->msg, 1);
|
||||
|
||||
default:
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
static int max5481_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct max5481_data *data = iio_priv(indio_dev);
|
||||
|
||||
if (mask != IIO_CHAN_INFO_SCALE)
|
||||
return -EINVAL;
|
||||
|
||||
*val = 1000 * data->cfg->kohms;
|
||||
*val2 = MAX5481_MAX_POS;
|
||||
|
||||
return IIO_VAL_FRACTIONAL;
|
||||
}
|
||||
|
||||
static int max5481_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
struct max5481_data *data = iio_priv(indio_dev);
|
||||
|
||||
if (mask != IIO_CHAN_INFO_RAW)
|
||||
return -EINVAL;
|
||||
|
||||
if (val < 0 || val > MAX5481_MAX_POS)
|
||||
return -EINVAL;
|
||||
|
||||
return max5481_write_cmd(data, MAX5481_WRITE_WIPER, val);
|
||||
}
|
||||
|
||||
static const struct iio_info max5481_info = {
|
||||
.read_raw = max5481_read_raw,
|
||||
.write_raw = max5481_write_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
#if defined(CONFIG_OF)
|
||||
static const struct of_device_id max5481_match[] = {
|
||||
{ .compatible = "maxim,max5481", .data = &max5481_cfg[max5481] },
|
||||
{ .compatible = "maxim,max5482", .data = &max5481_cfg[max5482] },
|
||||
{ .compatible = "maxim,max5483", .data = &max5481_cfg[max5483] },
|
||||
{ .compatible = "maxim,max5484", .data = &max5481_cfg[max5484] },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, max5481_match);
|
||||
#endif
|
||||
|
||||
static int max5481_probe(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct max5481_data *data;
|
||||
const struct spi_device_id *id = spi_get_device_id(spi);
|
||||
const struct of_device_id *match;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_set_drvdata(&spi->dev, indio_dev);
|
||||
data = iio_priv(indio_dev);
|
||||
|
||||
data->spi = spi;
|
||||
|
||||
match = of_match_device(of_match_ptr(max5481_match), &spi->dev);
|
||||
if (match)
|
||||
data->cfg = of_device_get_match_data(&spi->dev);
|
||||
else
|
||||
data->cfg = &max5481_cfg[id->driver_data];
|
||||
|
||||
indio_dev->name = id->name;
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
/* variant specific configuration */
|
||||
indio_dev->info = &max5481_info;
|
||||
indio_dev->channels = max5481_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(max5481_channels);
|
||||
|
||||
/* restore wiper from NV */
|
||||
ret = max5481_write_cmd(data, MAX5481_COPY_NV_TO_AB, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return iio_device_register(indio_dev);
|
||||
}
|
||||
|
||||
static int max5481_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(&spi->dev);
|
||||
struct max5481_data *data = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
/* save wiper reg to NV reg */
|
||||
return max5481_write_cmd(data, MAX5481_COPY_AB_TO_NV, 0);
|
||||
}
|
||||
|
||||
static const struct spi_device_id max5481_id_table[] = {
|
||||
{ "max5481", max5481 },
|
||||
{ "max5482", max5482 },
|
||||
{ "max5483", max5483 },
|
||||
{ "max5484", max5484 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, max5481_id_table);
|
||||
|
||||
#if defined(CONFIG_ACPI)
|
||||
static const struct acpi_device_id max5481_acpi_match[] = {
|
||||
{ "max5481", max5481 },
|
||||
{ "max5482", max5482 },
|
||||
{ "max5483", max5483 },
|
||||
{ "max5484", max5484 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, max5481_acpi_match);
|
||||
#endif
|
||||
|
||||
static struct spi_driver max5481_driver = {
|
||||
.driver = {
|
||||
.name = "max5481",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_match_ptr(max5481_match),
|
||||
.acpi_match_table = ACPI_PTR(max5481_acpi_match),
|
||||
},
|
||||
.probe = max5481_probe,
|
||||
.remove = max5481_remove,
|
||||
.id_table = max5481_id_table,
|
||||
};
|
||||
|
||||
module_spi_driver(max5481_driver);
|
||||
|
||||
MODULE_AUTHOR("Maury Anderson <maury.anderson@rockwellcollins.com>");
|
||||
MODULE_DESCRIPTION("max5481 SPI driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -42,6 +42,16 @@ config BMP280_SPI
|
||||
depends on SPI_MASTER
|
||||
select REGMAP
|
||||
|
||||
config IIO_CROS_EC_BARO
|
||||
tristate "ChromeOS EC Barometer Sensor"
|
||||
depends on IIO_CROS_EC_SENSORS_CORE
|
||||
help
|
||||
Say yes here to build support for the Barometer sensor when
|
||||
presented by the ChromeOS EC Sensor hub.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called cros_ec_baro.
|
||||
|
||||
config HID_SENSOR_PRESS
|
||||
depends on HID_SENSOR_HUB
|
||||
select IIO_BUFFER
|
||||
|
@ -8,6 +8,7 @@ obj-$(CONFIG_BMP280) += bmp280.o
|
||||
bmp280-objs := bmp280-core.o bmp280-regmap.o
|
||||
obj-$(CONFIG_BMP280_I2C) += bmp280-i2c.o
|
||||
obj-$(CONFIG_BMP280_SPI) += bmp280-spi.o
|
||||
obj-$(CONFIG_IIO_CROS_EC_BARO) += cros_ec_baro.o
|
||||
obj-$(CONFIG_HID_SENSOR_PRESS) += hid-sensor-press.o
|
||||
obj-$(CONFIG_HP03) += hp03.o
|
||||
obj-$(CONFIG_MPL115) += mpl115.o
|
||||
|
220
drivers/iio/pressure/cros_ec_baro.c
Normal file
220
drivers/iio/pressure/cros_ec_baro.c
Normal file
@ -0,0 +1,220 @@
|
||||
/*
|
||||
* cros_ec_baro - Driver for barometer sensor behind CrosEC.
|
||||
*
|
||||
* Copyright (C) 2017 Google, Inc
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/kfifo_buf.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mfd/cros_ec.h>
|
||||
#include <linux/mfd/cros_ec_commands.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include "../common/cros_ec_sensors/cros_ec_sensors_core.h"
|
||||
|
||||
/*
|
||||
* One channel for pressure, the other for timestamp.
|
||||
*/
|
||||
#define CROS_EC_BARO_MAX_CHANNELS (1 + 1)
|
||||
|
||||
/* State data for ec_sensors iio driver. */
|
||||
struct cros_ec_baro_state {
|
||||
/* Shared by all sensors */
|
||||
struct cros_ec_sensors_core_state core;
|
||||
|
||||
struct iio_chan_spec channels[CROS_EC_BARO_MAX_CHANNELS];
|
||||
};
|
||||
|
||||
static int cros_ec_baro_read(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct cros_ec_baro_state *st = iio_priv(indio_dev);
|
||||
u16 data = 0;
|
||||
int ret = IIO_VAL_INT;
|
||||
int idx = chan->scan_index;
|
||||
|
||||
mutex_lock(&st->core.cmd_lock);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (cros_ec_sensors_read_cmd(indio_dev, 1 << idx,
|
||||
(s16 *)&data) < 0)
|
||||
ret = -EIO;
|
||||
*val = data;
|
||||
break;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_RANGE;
|
||||
st->core.param.sensor_range.data = EC_MOTION_SENSE_NO_VALUE;
|
||||
|
||||
if (cros_ec_motion_send_host_cmd(&st->core, 0)) {
|
||||
ret = -EIO;
|
||||
break;
|
||||
}
|
||||
*val = st->core.resp->sensor_range.ret;
|
||||
|
||||
/* scale * in_pressure_raw --> kPa */
|
||||
*val2 = 10 << CROS_EC_SENSOR_BITS;
|
||||
ret = IIO_VAL_FRACTIONAL;
|
||||
break;
|
||||
default:
|
||||
ret = cros_ec_sensors_core_read(&st->core, chan, val, val2,
|
||||
mask);
|
||||
break;
|
||||
}
|
||||
|
||||
mutex_unlock(&st->core.cmd_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cros_ec_baro_write(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
struct cros_ec_baro_state *st = iio_priv(indio_dev);
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&st->core.cmd_lock);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_RANGE;
|
||||
st->core.param.sensor_range.data = val;
|
||||
|
||||
/* Always roundup, so caller gets at least what it asks for. */
|
||||
st->core.param.sensor_range.roundup = 1;
|
||||
|
||||
if (cros_ec_motion_send_host_cmd(&st->core, 0))
|
||||
ret = -EIO;
|
||||
break;
|
||||
default:
|
||||
ret = cros_ec_sensors_core_write(&st->core, chan, val, val2,
|
||||
mask);
|
||||
break;
|
||||
}
|
||||
|
||||
mutex_unlock(&st->core.cmd_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct iio_info cros_ec_baro_info = {
|
||||
.read_raw = &cros_ec_baro_read,
|
||||
.write_raw = &cros_ec_baro_write,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int cros_ec_baro_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct cros_ec_dev *ec_dev = dev_get_drvdata(dev->parent);
|
||||
struct cros_ec_device *ec_device;
|
||||
struct iio_dev *indio_dev;
|
||||
struct cros_ec_baro_state *state;
|
||||
struct iio_chan_spec *channel;
|
||||
int ret;
|
||||
|
||||
if (!ec_dev || !ec_dev->ec_dev) {
|
||||
dev_warn(dev, "No CROS EC device found.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
ec_device = ec_dev->ec_dev;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(dev, sizeof(*state));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = cros_ec_sensors_core_init(pdev, indio_dev, true);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
indio_dev->info = &cros_ec_baro_info;
|
||||
state = iio_priv(indio_dev);
|
||||
state->core.type = state->core.resp->info.type;
|
||||
state->core.loc = state->core.resp->info.location;
|
||||
channel = state->channels;
|
||||
/* Common part */
|
||||
channel->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
|
||||
channel->info_mask_shared_by_all =
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
|
||||
BIT(IIO_CHAN_INFO_FREQUENCY);
|
||||
channel->scan_type.realbits = CROS_EC_SENSOR_BITS;
|
||||
channel->scan_type.storagebits = CROS_EC_SENSOR_BITS;
|
||||
channel->scan_type.shift = 0;
|
||||
channel->scan_index = 0;
|
||||
channel->ext_info = cros_ec_sensors_ext_info;
|
||||
channel->scan_type.sign = 'u';
|
||||
|
||||
state->core.calib[0] = 0;
|
||||
|
||||
/* Sensor specific */
|
||||
switch (state->core.type) {
|
||||
case MOTIONSENSE_TYPE_BARO:
|
||||
channel->type = IIO_PRESSURE;
|
||||
break;
|
||||
default:
|
||||
dev_warn(dev, "Unknown motion sensor\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Timestamp */
|
||||
channel++;
|
||||
channel->type = IIO_TIMESTAMP;
|
||||
channel->channel = -1;
|
||||
channel->scan_index = 1;
|
||||
channel->scan_type.sign = 's';
|
||||
channel->scan_type.realbits = 64;
|
||||
channel->scan_type.storagebits = 64;
|
||||
|
||||
indio_dev->channels = state->channels;
|
||||
indio_dev->num_channels = CROS_EC_BARO_MAX_CHANNELS;
|
||||
|
||||
state->core.read_ec_sensors_data = cros_ec_sensors_read_cmd;
|
||||
|
||||
ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
|
||||
cros_ec_sensors_capture, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return devm_iio_device_register(dev, indio_dev);
|
||||
}
|
||||
|
||||
static const struct platform_device_id cros_ec_baro_ids[] = {
|
||||
{
|
||||
.name = "cros-ec-baro",
|
||||
},
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, cros_ec_baro_ids);
|
||||
|
||||
static struct platform_driver cros_ec_baro_platform_driver = {
|
||||
.driver = {
|
||||
.name = "cros-ec-baro",
|
||||
},
|
||||
.probe = cros_ec_baro_probe,
|
||||
.id_table = cros_ec_baro_ids,
|
||||
};
|
||||
module_platform_driver(cros_ec_baro_platform_driver);
|
||||
|
||||
MODULE_DESCRIPTION("ChromeOS EC barometer sensor driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -18,7 +18,7 @@ config AS3935
|
||||
|
||||
endmenu
|
||||
|
||||
menu "Proximity sensors"
|
||||
menu "Proximity and distance sensors"
|
||||
|
||||
config LIDAR_LITE_V2
|
||||
tristate "PulsedLight LIDAR sensor"
|
||||
@ -45,4 +45,15 @@ config SX9500
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called sx9500.
|
||||
|
||||
config SRF08
|
||||
tristate "Devantech SRF08 ultrasonic ranger sensor"
|
||||
depends on I2C
|
||||
help
|
||||
Say Y here to build a driver for Devantech SRF08 ultrasonic
|
||||
ranger sensor. This driver can be used to measure the distance
|
||||
of objects.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called srf08.
|
||||
|
||||
endmenu
|
||||
|
@ -5,4 +5,5 @@
|
||||
# When adding new entries keep the list in alphabetical order
|
||||
obj-$(CONFIG_AS3935) += as3935.o
|
||||
obj-$(CONFIG_LIDAR_LITE_V2) += pulsedlight-lidar-lite-v2.o
|
||||
obj-$(CONFIG_SRF08) += srf08.o
|
||||
obj-$(CONFIG_SX9500) += sx9500.o
|
||||
|
398
drivers/iio/proximity/srf08.c
Normal file
398
drivers/iio/proximity/srf08.c
Normal file
@ -0,0 +1,398 @@
|
||||
/*
|
||||
* srf08.c - Support for Devantech SRF08 ultrasonic ranger
|
||||
*
|
||||
* Copyright (c) 2016 Andreas Klinger <ak@it-klinger.de>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* For details about the device see:
|
||||
* http://www.robot-electronics.co.uk/htm/srf08tech.html
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
/* registers of SRF08 device */
|
||||
#define SRF08_WRITE_COMMAND 0x00 /* Command Register */
|
||||
#define SRF08_WRITE_MAX_GAIN 0x01 /* Max Gain Register: 0 .. 31 */
|
||||
#define SRF08_WRITE_RANGE 0x02 /* Range Register: 0 .. 255 */
|
||||
#define SRF08_READ_SW_REVISION 0x00 /* Software Revision */
|
||||
#define SRF08_READ_LIGHT 0x01 /* Light Sensor during last echo */
|
||||
#define SRF08_READ_ECHO_1_HIGH 0x02 /* Range of first echo received */
|
||||
#define SRF08_READ_ECHO_1_LOW 0x03 /* Range of first echo received */
|
||||
|
||||
#define SRF08_CMD_RANGING_CM 0x51 /* Ranging Mode - Result in cm */
|
||||
|
||||
#define SRF08_DEFAULT_GAIN 1025 /* default analogue value of Gain */
|
||||
#define SRF08_DEFAULT_RANGE 6020 /* default value of Range in mm */
|
||||
|
||||
struct srf08_data {
|
||||
struct i2c_client *client;
|
||||
int sensitivity; /* Gain */
|
||||
int range_mm; /* max. Range in mm */
|
||||
struct mutex lock;
|
||||
};
|
||||
|
||||
/*
|
||||
* in the documentation one can read about the "Gain" of the device
|
||||
* which is used here for amplifying the signal and filtering out unwanted
|
||||
* ones.
|
||||
* But with ADC's this term is already used differently and that's why it
|
||||
* is called "Sensitivity" here.
|
||||
*/
|
||||
static const int srf08_sensitivity[] = {
|
||||
94, 97, 100, 103, 107, 110, 114, 118,
|
||||
123, 128, 133, 139, 145, 152, 159, 168,
|
||||
177, 187, 199, 212, 227, 245, 265, 288,
|
||||
317, 352, 395, 450, 524, 626, 777, 1025 };
|
||||
|
||||
static int srf08_read_ranging(struct srf08_data *data)
|
||||
{
|
||||
struct i2c_client *client = data->client;
|
||||
int ret, i;
|
||||
int waittime;
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
|
||||
ret = i2c_smbus_write_byte_data(data->client,
|
||||
SRF08_WRITE_COMMAND, SRF08_CMD_RANGING_CM);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "write command - err: %d\n", ret);
|
||||
mutex_unlock(&data->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* we read here until a correct version number shows up as
|
||||
* suggested by the documentation
|
||||
*
|
||||
* with an ultrasonic speed of 343 m/s and a roundtrip of it
|
||||
* sleep the expected duration and try to read from the device
|
||||
* if nothing useful is read try it in a shorter grid
|
||||
*
|
||||
* polling for not more than 20 ms should be enough
|
||||
*/
|
||||
waittime = 1 + data->range_mm / 172;
|
||||
msleep(waittime);
|
||||
for (i = 0; i < 4; i++) {
|
||||
ret = i2c_smbus_read_byte_data(data->client,
|
||||
SRF08_READ_SW_REVISION);
|
||||
|
||||
/* check if a valid version number is read */
|
||||
if (ret < 255 && ret > 0)
|
||||
break;
|
||||
msleep(5);
|
||||
}
|
||||
|
||||
if (ret >= 255 || ret <= 0) {
|
||||
dev_err(&client->dev, "device not ready\n");
|
||||
mutex_unlock(&data->lock);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
ret = i2c_smbus_read_word_swapped(data->client,
|
||||
SRF08_READ_ECHO_1_HIGH);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "cannot read distance: ret=%d\n", ret);
|
||||
mutex_unlock(&data->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
mutex_unlock(&data->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int srf08_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *channel, int *val,
|
||||
int *val2, long mask)
|
||||
{
|
||||
struct srf08_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
if (channel->type != IIO_DISTANCE)
|
||||
return -EINVAL;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = srf08_read_ranging(data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = ret;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
/* 1 LSB is 1 cm */
|
||||
*val = 0;
|
||||
*val2 = 10000;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t srf08_show_range_mm_available(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return sprintf(buf, "[0.043 0.043 11.008]\n");
|
||||
}
|
||||
|
||||
static IIO_DEVICE_ATTR(sensor_max_range_available, S_IRUGO,
|
||||
srf08_show_range_mm_available, NULL, 0);
|
||||
|
||||
static ssize_t srf08_show_range_mm(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct srf08_data *data = iio_priv(indio_dev);
|
||||
|
||||
return sprintf(buf, "%d.%03d\n", data->range_mm / 1000,
|
||||
data->range_mm % 1000);
|
||||
}
|
||||
|
||||
/*
|
||||
* set the range of the sensor to an even multiple of 43 mm
|
||||
* which corresponds to 1 LSB in the register
|
||||
*
|
||||
* register value corresponding range
|
||||
* 0x00 43 mm
|
||||
* 0x01 86 mm
|
||||
* 0x02 129 mm
|
||||
* ...
|
||||
* 0xFF 11008 mm
|
||||
*/
|
||||
static ssize_t srf08_write_range_mm(struct srf08_data *data, unsigned int val)
|
||||
{
|
||||
int ret;
|
||||
struct i2c_client *client = data->client;
|
||||
unsigned int mod;
|
||||
u8 regval;
|
||||
|
||||
ret = val / 43 - 1;
|
||||
mod = val % 43;
|
||||
|
||||
if (mod || (ret < 0) || (ret > 255))
|
||||
return -EINVAL;
|
||||
|
||||
regval = ret;
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
|
||||
ret = i2c_smbus_write_byte_data(client, SRF08_WRITE_RANGE, regval);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "write_range - err: %d\n", ret);
|
||||
mutex_unlock(&data->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
data->range_mm = val;
|
||||
|
||||
mutex_unlock(&data->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t srf08_store_range_mm(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct srf08_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
int integer, fract;
|
||||
|
||||
ret = iio_str_to_fixpoint(buf, 100, &integer, &fract);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = srf08_write_range_mm(data, integer * 1000 + fract);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static IIO_DEVICE_ATTR(sensor_max_range, S_IRUGO | S_IWUSR,
|
||||
srf08_show_range_mm, srf08_store_range_mm, 0);
|
||||
|
||||
static ssize_t srf08_show_sensitivity_available(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
int i, len = 0;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(srf08_sensitivity); i++)
|
||||
len += sprintf(buf + len, "%d ", srf08_sensitivity[i]);
|
||||
|
||||
len += sprintf(buf + len, "\n");
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static IIO_DEVICE_ATTR(sensor_sensitivity_available, S_IRUGO,
|
||||
srf08_show_sensitivity_available, NULL, 0);
|
||||
|
||||
static ssize_t srf08_show_sensitivity(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct srf08_data *data = iio_priv(indio_dev);
|
||||
int len;
|
||||
|
||||
len = sprintf(buf, "%d\n", data->sensitivity);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t srf08_write_sensitivity(struct srf08_data *data,
|
||||
unsigned int val)
|
||||
{
|
||||
struct i2c_client *client = data->client;
|
||||
int ret, i;
|
||||
u8 regval;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(srf08_sensitivity); i++)
|
||||
if (val == srf08_sensitivity[i]) {
|
||||
regval = i;
|
||||
break;
|
||||
}
|
||||
|
||||
if (i >= ARRAY_SIZE(srf08_sensitivity))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
|
||||
ret = i2c_smbus_write_byte_data(client,
|
||||
SRF08_WRITE_MAX_GAIN, regval);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "write_sensitivity - err: %d\n", ret);
|
||||
mutex_unlock(&data->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
data->sensitivity = val;
|
||||
|
||||
mutex_unlock(&data->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t srf08_store_sensitivity(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct srf08_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
unsigned int val;
|
||||
|
||||
ret = kstrtouint(buf, 10, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = srf08_write_sensitivity(data, val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static IIO_DEVICE_ATTR(sensor_sensitivity, S_IRUGO | S_IWUSR,
|
||||
srf08_show_sensitivity, srf08_store_sensitivity, 0);
|
||||
|
||||
static struct attribute *srf08_attributes[] = {
|
||||
&iio_dev_attr_sensor_max_range.dev_attr.attr,
|
||||
&iio_dev_attr_sensor_max_range_available.dev_attr.attr,
|
||||
&iio_dev_attr_sensor_sensitivity.dev_attr.attr,
|
||||
&iio_dev_attr_sensor_sensitivity_available.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group srf08_attribute_group = {
|
||||
.attrs = srf08_attributes,
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec srf08_channels[] = {
|
||||
{
|
||||
.type = IIO_DISTANCE,
|
||||
.info_mask_separate =
|
||||
BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
},
|
||||
};
|
||||
|
||||
static const struct iio_info srf08_info = {
|
||||
.read_raw = srf08_read_raw,
|
||||
.attrs = &srf08_attribute_group,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int srf08_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct srf08_data *data;
|
||||
int ret;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter,
|
||||
I2C_FUNC_SMBUS_READ_BYTE_DATA |
|
||||
I2C_FUNC_SMBUS_WRITE_BYTE_DATA |
|
||||
I2C_FUNC_SMBUS_READ_WORD_DATA))
|
||||
return -ENODEV;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
data = iio_priv(indio_dev);
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
data->client = client;
|
||||
|
||||
indio_dev->name = "srf08";
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &srf08_info;
|
||||
indio_dev->channels = srf08_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(srf08_channels);
|
||||
|
||||
mutex_init(&data->lock);
|
||||
|
||||
/*
|
||||
* set default values of device here
|
||||
* these register values cannot be read from the hardware
|
||||
* therefore set driver specific default values
|
||||
*/
|
||||
ret = srf08_write_range_mm(data, SRF08_DEFAULT_RANGE);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = srf08_write_sensitivity(data, SRF08_DEFAULT_GAIN);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return devm_iio_device_register(&client->dev, indio_dev);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id srf08_id[] = {
|
||||
{ "srf08", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, srf08_id);
|
||||
|
||||
static struct i2c_driver srf08_driver = {
|
||||
.driver = {
|
||||
.name = "srf08",
|
||||
},
|
||||
.probe = srf08_probe,
|
||||
.id_table = srf08_id,
|
||||
};
|
||||
module_i2c_driver(srf08_driver);
|
||||
|
||||
MODULE_AUTHOR("Andreas Klinger <ak@it-klinger.de>");
|
||||
MODULE_DESCRIPTION("Devantech SRF08 ultrasonic ranger driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -233,7 +233,7 @@ static int tmp007_probe(struct i2c_client *client,
|
||||
data->client = client;
|
||||
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->name = dev_name(&client->dev);
|
||||
indio_dev->name = "tmp007";
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &tmp007_info;
|
||||
|
||||
|
@ -24,6 +24,15 @@ config IIO_INTERRUPT_TRIGGER
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called iio-trig-interrupt.
|
||||
|
||||
config IIO_STM32_TIMER_TRIGGER
|
||||
tristate "STM32 Timer Trigger"
|
||||
depends on (ARCH_STM32 && OF && MFD_STM32_TIMERS) || COMPILE_TEST
|
||||
help
|
||||
Select this option to enable STM32 Timer Trigger
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called stm32-timer-trigger.
|
||||
|
||||
config IIO_TIGHTLOOP_TRIGGER
|
||||
tristate "A kthread based hammering loop trigger"
|
||||
depends on IIO_SW_TRIGGER
|
||||
|
@ -6,5 +6,6 @@
|
||||
|
||||
obj-$(CONFIG_IIO_HRTIMER_TRIGGER) += iio-trig-hrtimer.o
|
||||
obj-$(CONFIG_IIO_INTERRUPT_TRIGGER) += iio-trig-interrupt.o
|
||||
obj-$(CONFIG_IIO_STM32_TIMER_TRIGGER) += stm32-timer-trigger.o
|
||||
obj-$(CONFIG_IIO_SYSFS_TRIGGER) += iio-trig-sysfs.o
|
||||
obj-$(CONFIG_IIO_TIGHTLOOP_TRIGGER) += iio-trig-loop.o
|
||||
|
342
drivers/iio/trigger/stm32-timer-trigger.c
Normal file
342
drivers/iio/trigger/stm32-timer-trigger.c
Normal file
@ -0,0 +1,342 @@
|
||||
/*
|
||||
* Copyright (C) STMicroelectronics 2016
|
||||
*
|
||||
* Author: Benjamin Gaignard <benjamin.gaignard@st.com>
|
||||
*
|
||||
* License terms: GNU General Public License (GPL), version 2
|
||||
*/
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/timer/stm32-timer-trigger.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include <linux/mfd/stm32-timers.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#define MAX_TRIGGERS 6
|
||||
|
||||
/* List the triggers created by each timer */
|
||||
static const void *triggers_table[][MAX_TRIGGERS] = {
|
||||
{ TIM1_TRGO, TIM1_CH1, TIM1_CH2, TIM1_CH3, TIM1_CH4,},
|
||||
{ TIM2_TRGO, TIM2_CH1, TIM2_CH2, TIM2_CH3, TIM2_CH4,},
|
||||
{ TIM3_TRGO, TIM3_CH1, TIM3_CH2, TIM3_CH3, TIM3_CH4,},
|
||||
{ TIM4_TRGO, TIM4_CH1, TIM4_CH2, TIM4_CH3, TIM4_CH4,},
|
||||
{ TIM5_TRGO, TIM5_CH1, TIM5_CH2, TIM5_CH3, TIM5_CH4,},
|
||||
{ TIM6_TRGO,},
|
||||
{ TIM7_TRGO,},
|
||||
{ TIM8_TRGO, TIM8_CH1, TIM8_CH2, TIM8_CH3, TIM8_CH4,},
|
||||
{ TIM9_TRGO, TIM9_CH1, TIM9_CH2,},
|
||||
{ }, /* timer 10 */
|
||||
{ }, /* timer 11 */
|
||||
{ TIM12_TRGO, TIM12_CH1, TIM12_CH2,},
|
||||
};
|
||||
|
||||
struct stm32_timer_trigger {
|
||||
struct device *dev;
|
||||
struct regmap *regmap;
|
||||
struct clk *clk;
|
||||
u32 max_arr;
|
||||
const void *triggers;
|
||||
};
|
||||
|
||||
static int stm32_timer_start(struct stm32_timer_trigger *priv,
|
||||
unsigned int frequency)
|
||||
{
|
||||
unsigned long long prd, div;
|
||||
int prescaler = 0;
|
||||
u32 ccer, cr1;
|
||||
|
||||
/* Period and prescaler values depends of clock rate */
|
||||
div = (unsigned long long)clk_get_rate(priv->clk);
|
||||
|
||||
do_div(div, frequency);
|
||||
|
||||
prd = div;
|
||||
|
||||
/*
|
||||
* Increase prescaler value until we get a result that fit
|
||||
* with auto reload register maximum value.
|
||||
*/
|
||||
while (div > priv->max_arr) {
|
||||
prescaler++;
|
||||
div = prd;
|
||||
do_div(div, (prescaler + 1));
|
||||
}
|
||||
prd = div;
|
||||
|
||||
if (prescaler > MAX_TIM_PSC) {
|
||||
dev_err(priv->dev, "prescaler exceeds the maximum value\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Check if nobody else use the timer */
|
||||
regmap_read(priv->regmap, TIM_CCER, &ccer);
|
||||
if (ccer & TIM_CCER_CCXE)
|
||||
return -EBUSY;
|
||||
|
||||
regmap_read(priv->regmap, TIM_CR1, &cr1);
|
||||
if (!(cr1 & TIM_CR1_CEN))
|
||||
clk_enable(priv->clk);
|
||||
|
||||
regmap_write(priv->regmap, TIM_PSC, prescaler);
|
||||
regmap_write(priv->regmap, TIM_ARR, prd - 1);
|
||||
regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_ARPE, TIM_CR1_ARPE);
|
||||
|
||||
/* Force master mode to update mode */
|
||||
regmap_update_bits(priv->regmap, TIM_CR2, TIM_CR2_MMS, 0x20);
|
||||
|
||||
/* Make sure that registers are updated */
|
||||
regmap_update_bits(priv->regmap, TIM_EGR, TIM_EGR_UG, TIM_EGR_UG);
|
||||
|
||||
/* Enable controller */
|
||||
regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN, TIM_CR1_CEN);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void stm32_timer_stop(struct stm32_timer_trigger *priv)
|
||||
{
|
||||
u32 ccer, cr1;
|
||||
|
||||
regmap_read(priv->regmap, TIM_CCER, &ccer);
|
||||
if (ccer & TIM_CCER_CCXE)
|
||||
return;
|
||||
|
||||
regmap_read(priv->regmap, TIM_CR1, &cr1);
|
||||
if (cr1 & TIM_CR1_CEN)
|
||||
clk_disable(priv->clk);
|
||||
|
||||
/* Stop timer */
|
||||
regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN, 0);
|
||||
regmap_write(priv->regmap, TIM_PSC, 0);
|
||||
regmap_write(priv->regmap, TIM_ARR, 0);
|
||||
|
||||
/* Make sure that registers are updated */
|
||||
regmap_update_bits(priv->regmap, TIM_EGR, TIM_EGR_UG, TIM_EGR_UG);
|
||||
}
|
||||
|
||||
static ssize_t stm32_tt_store_frequency(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
struct iio_trigger *trig = to_iio_trigger(dev);
|
||||
struct stm32_timer_trigger *priv = iio_trigger_get_drvdata(trig);
|
||||
unsigned int freq;
|
||||
int ret;
|
||||
|
||||
ret = kstrtouint(buf, 10, &freq);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (freq == 0) {
|
||||
stm32_timer_stop(priv);
|
||||
} else {
|
||||
ret = stm32_timer_start(priv, freq);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t stm32_tt_read_frequency(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct iio_trigger *trig = to_iio_trigger(dev);
|
||||
struct stm32_timer_trigger *priv = iio_trigger_get_drvdata(trig);
|
||||
u32 psc, arr, cr1;
|
||||
unsigned long long freq = 0;
|
||||
|
||||
regmap_read(priv->regmap, TIM_CR1, &cr1);
|
||||
regmap_read(priv->regmap, TIM_PSC, &psc);
|
||||
regmap_read(priv->regmap, TIM_ARR, &arr);
|
||||
|
||||
if (psc && arr && (cr1 & TIM_CR1_CEN)) {
|
||||
freq = (unsigned long long)clk_get_rate(priv->clk);
|
||||
do_div(freq, psc);
|
||||
do_div(freq, arr);
|
||||
}
|
||||
|
||||
return sprintf(buf, "%d\n", (unsigned int)freq);
|
||||
}
|
||||
|
||||
static IIO_DEV_ATTR_SAMP_FREQ(0660,
|
||||
stm32_tt_read_frequency,
|
||||
stm32_tt_store_frequency);
|
||||
|
||||
static char *master_mode_table[] = {
|
||||
"reset",
|
||||
"enable",
|
||||
"update",
|
||||
"compare_pulse",
|
||||
"OC1REF",
|
||||
"OC2REF",
|
||||
"OC3REF",
|
||||
"OC4REF"
|
||||
};
|
||||
|
||||
static ssize_t stm32_tt_show_master_mode(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct stm32_timer_trigger *priv = iio_priv(indio_dev);
|
||||
u32 cr2;
|
||||
|
||||
regmap_read(priv->regmap, TIM_CR2, &cr2);
|
||||
cr2 = (cr2 & TIM_CR2_MMS) >> TIM_CR2_MMS_SHIFT;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n", master_mode_table[cr2]);
|
||||
}
|
||||
|
||||
static ssize_t stm32_tt_store_master_mode(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct stm32_timer_trigger *priv = iio_priv(indio_dev);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(master_mode_table); i++) {
|
||||
if (!strncmp(master_mode_table[i], buf,
|
||||
strlen(master_mode_table[i]))) {
|
||||
regmap_update_bits(priv->regmap, TIM_CR2,
|
||||
TIM_CR2_MMS, i << TIM_CR2_MMS_SHIFT);
|
||||
/* Make sure that registers are updated */
|
||||
regmap_update_bits(priv->regmap, TIM_EGR,
|
||||
TIM_EGR_UG, TIM_EGR_UG);
|
||||
return len;
|
||||
}
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static IIO_CONST_ATTR(master_mode_available,
|
||||
"reset enable update compare_pulse OC1REF OC2REF OC3REF OC4REF");
|
||||
|
||||
static IIO_DEVICE_ATTR(master_mode, 0660,
|
||||
stm32_tt_show_master_mode,
|
||||
stm32_tt_store_master_mode,
|
||||
0);
|
||||
|
||||
static struct attribute *stm32_trigger_attrs[] = {
|
||||
&iio_dev_attr_sampling_frequency.dev_attr.attr,
|
||||
&iio_dev_attr_master_mode.dev_attr.attr,
|
||||
&iio_const_attr_master_mode_available.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group stm32_trigger_attr_group = {
|
||||
.attrs = stm32_trigger_attrs,
|
||||
};
|
||||
|
||||
static const struct attribute_group *stm32_trigger_attr_groups[] = {
|
||||
&stm32_trigger_attr_group,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct iio_trigger_ops timer_trigger_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int stm32_setup_iio_triggers(struct stm32_timer_trigger *priv)
|
||||
{
|
||||
int ret;
|
||||
const char * const *cur = priv->triggers;
|
||||
|
||||
while (cur && *cur) {
|
||||
struct iio_trigger *trig;
|
||||
|
||||
trig = devm_iio_trigger_alloc(priv->dev, "%s", *cur);
|
||||
if (!trig)
|
||||
return -ENOMEM;
|
||||
|
||||
trig->dev.parent = priv->dev->parent;
|
||||
trig->ops = &timer_trigger_ops;
|
||||
|
||||
/*
|
||||
* sampling frequency and master mode attributes
|
||||
* should only be available on trgo trigger which
|
||||
* is always the first in the list.
|
||||
*/
|
||||
if (cur == priv->triggers)
|
||||
trig->dev.groups = stm32_trigger_attr_groups;
|
||||
|
||||
iio_trigger_set_drvdata(trig, priv);
|
||||
|
||||
ret = devm_iio_trigger_register(priv->dev, trig);
|
||||
if (ret)
|
||||
return ret;
|
||||
cur++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* is_stm32_timer_trigger
|
||||
* @trig: trigger to be checked
|
||||
*
|
||||
* return true if the trigger is a valid stm32 iio timer trigger
|
||||
* either return false
|
||||
*/
|
||||
bool is_stm32_timer_trigger(struct iio_trigger *trig)
|
||||
{
|
||||
return (trig->ops == &timer_trigger_ops);
|
||||
}
|
||||
EXPORT_SYMBOL(is_stm32_timer_trigger);
|
||||
|
||||
static int stm32_timer_trigger_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct stm32_timer_trigger *priv;
|
||||
struct stm32_timers *ddata = dev_get_drvdata(pdev->dev.parent);
|
||||
unsigned int index;
|
||||
int ret;
|
||||
|
||||
if (of_property_read_u32(dev->of_node, "reg", &index))
|
||||
return -EINVAL;
|
||||
|
||||
if (index >= ARRAY_SIZE(triggers_table))
|
||||
return -EINVAL;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->dev = dev;
|
||||
priv->regmap = ddata->regmap;
|
||||
priv->clk = ddata->clk;
|
||||
priv->max_arr = ddata->max_arr;
|
||||
priv->triggers = triggers_table[index];
|
||||
|
||||
ret = stm32_setup_iio_triggers(priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
platform_set_drvdata(pdev, priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id stm32_trig_of_match[] = {
|
||||
{ .compatible = "st,stm32-timer-trigger", },
|
||||
{ /* end node */ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, stm32_trig_of_match);
|
||||
|
||||
static struct platform_driver stm32_timer_trigger_driver = {
|
||||
.probe = stm32_timer_trigger_probe,
|
||||
.driver = {
|
||||
.name = "stm32-timer-trigger",
|
||||
.of_match_table = stm32_trig_of_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(stm32_timer_trigger_driver);
|
||||
|
||||
MODULE_ALIAS("platform: stm32-timer-trigger");
|
||||
MODULE_DESCRIPTION("STMicroelectronics STM32 Timer Trigger driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -1621,6 +1621,17 @@ config MFD_STW481X
|
||||
in various ST Microelectronics and ST-Ericsson embedded
|
||||
Nomadik series.
|
||||
|
||||
config MFD_STM32_TIMERS
|
||||
tristate "Support for STM32 Timers"
|
||||
depends on (ARCH_STM32 && OF) || COMPILE_TEST
|
||||
select MFD_CORE
|
||||
select REGMAP
|
||||
select REGMAP_MMIO
|
||||
help
|
||||
Select this option to enable STM32 timers driver used
|
||||
for PWM and IIO Timer. This driver allow to share the
|
||||
registers between the others drivers.
|
||||
|
||||
menu "Multimedia Capabilities Port drivers"
|
||||
depends on ARCH_SA1100
|
||||
|
||||
|
@ -212,3 +212,5 @@ obj-$(CONFIG_MFD_MT6397) += mt6397-core.o
|
||||
|
||||
obj-$(CONFIG_MFD_ALTERA_A10SR) += altera-a10sr.o
|
||||
obj-$(CONFIG_MFD_SUN4I_GPADC) += sun4i-gpadc.o
|
||||
|
||||
obj-$(CONFIG_MFD_STM32_TIMERS) += stm32-timers.o
|
||||
|
80
drivers/mfd/stm32-timers.c
Normal file
80
drivers/mfd/stm32-timers.c
Normal file
@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright (C) STMicroelectronics 2016
|
||||
*
|
||||
* Author: Benjamin Gaignard <benjamin.gaignard@st.com>
|
||||
*
|
||||
* License terms: GNU General Public License (GPL), version 2
|
||||
*/
|
||||
|
||||
#include <linux/mfd/stm32-timers.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/reset.h>
|
||||
|
||||
static const struct regmap_config stm32_timers_regmap_cfg = {
|
||||
.reg_bits = 32,
|
||||
.val_bits = 32,
|
||||
.reg_stride = sizeof(u32),
|
||||
.max_register = 0x400,
|
||||
};
|
||||
|
||||
static void stm32_timers_get_arr_size(struct stm32_timers *ddata)
|
||||
{
|
||||
/*
|
||||
* Only the available bits will be written so when readback
|
||||
* we get the maximum value of auto reload register
|
||||
*/
|
||||
regmap_write(ddata->regmap, TIM_ARR, ~0L);
|
||||
regmap_read(ddata->regmap, TIM_ARR, &ddata->max_arr);
|
||||
regmap_write(ddata->regmap, TIM_ARR, 0x0);
|
||||
}
|
||||
|
||||
static int stm32_timers_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct stm32_timers *ddata;
|
||||
struct resource *res;
|
||||
void __iomem *mmio;
|
||||
|
||||
ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL);
|
||||
if (!ddata)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
mmio = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(mmio))
|
||||
return PTR_ERR(mmio);
|
||||
|
||||
ddata->regmap = devm_regmap_init_mmio_clk(dev, "int", mmio,
|
||||
&stm32_timers_regmap_cfg);
|
||||
if (IS_ERR(ddata->regmap))
|
||||
return PTR_ERR(ddata->regmap);
|
||||
|
||||
ddata->clk = devm_clk_get(dev, NULL);
|
||||
if (IS_ERR(ddata->clk))
|
||||
return PTR_ERR(ddata->clk);
|
||||
|
||||
stm32_timers_get_arr_size(ddata);
|
||||
|
||||
platform_set_drvdata(pdev, ddata);
|
||||
|
||||
return of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
|
||||
}
|
||||
|
||||
static const struct of_device_id stm32_timers_of_match[] = {
|
||||
{ .compatible = "st,stm32-timers", },
|
||||
{ /* end node */ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, stm32_timers_of_match);
|
||||
|
||||
static struct platform_driver stm32_timers_driver = {
|
||||
.probe = stm32_timers_probe,
|
||||
.driver = {
|
||||
.name = "stm32-timers",
|
||||
.of_match_table = stm32_timers_of_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(stm32_timers_driver);
|
||||
|
||||
MODULE_DESCRIPTION("STMicroelectronics STM32 Timers");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -328,6 +328,9 @@ static void cros_ec_sensors_register(struct cros_ec_dev *ec)
|
||||
case MOTIONSENSE_TYPE_ACCEL:
|
||||
sensor_cells[id].name = "cros-ec-accel";
|
||||
break;
|
||||
case MOTIONSENSE_TYPE_BARO:
|
||||
sensor_cells[id].name = "cros-ec-baro";
|
||||
break;
|
||||
case MOTIONSENSE_TYPE_GYRO:
|
||||
sensor_cells[id].name = "cros-ec-gyro";
|
||||
break;
|
||||
|
@ -397,6 +397,15 @@ config PWM_STI
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called pwm-sti.
|
||||
|
||||
config PWM_STM32
|
||||
tristate "STMicroelectronics STM32 PWM"
|
||||
depends on MFD_STM32_TIMERS || COMPILE_TEST
|
||||
help
|
||||
Generic PWM framework driver for STM32 SoCs.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called pwm-stm32.
|
||||
|
||||
config PWM_STMPE
|
||||
bool "STMPE expander PWM export"
|
||||
depends on MFD_STMPE
|
||||
|
@ -38,6 +38,7 @@ obj-$(CONFIG_PWM_ROCKCHIP) += pwm-rockchip.o
|
||||
obj-$(CONFIG_PWM_SAMSUNG) += pwm-samsung.o
|
||||
obj-$(CONFIG_PWM_SPEAR) += pwm-spear.o
|
||||
obj-$(CONFIG_PWM_STI) += pwm-sti.o
|
||||
obj-$(CONFIG_PWM_STM32) += pwm-stm32.o
|
||||
obj-$(CONFIG_PWM_STMPE) += pwm-stmpe.o
|
||||
obj-$(CONFIG_PWM_SUN4I) += pwm-sun4i.o
|
||||
obj-$(CONFIG_PWM_TEGRA) += pwm-tegra.o
|
||||
|
397
drivers/pwm/pwm-stm32.c
Normal file
397
drivers/pwm/pwm-stm32.c
Normal file
@ -0,0 +1,397 @@
|
||||
/*
|
||||
* Copyright (C) STMicroelectronics 2016
|
||||
*
|
||||
* Author: Gerald Baeza <gerald.baeza@st.com>
|
||||
*
|
||||
* License terms: GNU General Public License (GPL), version 2
|
||||
*
|
||||
* Inspired by timer-stm32.c from Maxime Coquelin
|
||||
* pwm-atmel.c from Bo Shen
|
||||
*/
|
||||
|
||||
#include <linux/mfd/stm32-timers.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pwm.h>
|
||||
|
||||
#define CCMR_CHANNEL_SHIFT 8
|
||||
#define CCMR_CHANNEL_MASK 0xFF
|
||||
#define MAX_BREAKINPUT 2
|
||||
|
||||
struct stm32_pwm {
|
||||
struct pwm_chip chip;
|
||||
struct device *dev;
|
||||
struct clk *clk;
|
||||
struct regmap *regmap;
|
||||
u32 max_arr;
|
||||
bool have_complementary_output;
|
||||
};
|
||||
|
||||
struct stm32_breakinput {
|
||||
u32 index;
|
||||
u32 level;
|
||||
u32 filter;
|
||||
};
|
||||
|
||||
static inline struct stm32_pwm *to_stm32_pwm_dev(struct pwm_chip *chip)
|
||||
{
|
||||
return container_of(chip, struct stm32_pwm, chip);
|
||||
}
|
||||
|
||||
static u32 active_channels(struct stm32_pwm *dev)
|
||||
{
|
||||
u32 ccer;
|
||||
|
||||
regmap_read(dev->regmap, TIM_CCER, &ccer);
|
||||
|
||||
return ccer & TIM_CCER_CCXE;
|
||||
}
|
||||
|
||||
static int write_ccrx(struct stm32_pwm *dev, int ch, u32 value)
|
||||
{
|
||||
switch (ch) {
|
||||
case 0:
|
||||
return regmap_write(dev->regmap, TIM_CCR1, value);
|
||||
case 1:
|
||||
return regmap_write(dev->regmap, TIM_CCR2, value);
|
||||
case 2:
|
||||
return regmap_write(dev->regmap, TIM_CCR3, value);
|
||||
case 3:
|
||||
return regmap_write(dev->regmap, TIM_CCR4, value);
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int stm32_pwm_config(struct stm32_pwm *priv, int ch,
|
||||
int duty_ns, int period_ns)
|
||||
{
|
||||
unsigned long long prd, div, dty;
|
||||
unsigned int prescaler = 0;
|
||||
u32 ccmr, mask, shift;
|
||||
|
||||
/* Period and prescaler values depends on clock rate */
|
||||
div = (unsigned long long)clk_get_rate(priv->clk) * period_ns;
|
||||
|
||||
do_div(div, NSEC_PER_SEC);
|
||||
prd = div;
|
||||
|
||||
while (div > priv->max_arr) {
|
||||
prescaler++;
|
||||
div = prd;
|
||||
do_div(div, prescaler + 1);
|
||||
}
|
||||
|
||||
prd = div;
|
||||
|
||||
if (prescaler > MAX_TIM_PSC)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* All channels share the same prescaler and counter so when two
|
||||
* channels are active at the same time we can't change them
|
||||
*/
|
||||
if (active_channels(priv) & ~(1 << ch * 4)) {
|
||||
u32 psc, arr;
|
||||
|
||||
regmap_read(priv->regmap, TIM_PSC, &psc);
|
||||
regmap_read(priv->regmap, TIM_ARR, &arr);
|
||||
|
||||
if ((psc != prescaler) || (arr != prd - 1))
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
regmap_write(priv->regmap, TIM_PSC, prescaler);
|
||||
regmap_write(priv->regmap, TIM_ARR, prd - 1);
|
||||
regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_ARPE, TIM_CR1_ARPE);
|
||||
|
||||
/* Calculate the duty cycles */
|
||||
dty = prd * duty_ns;
|
||||
do_div(dty, period_ns);
|
||||
|
||||
write_ccrx(priv, ch, dty);
|
||||
|
||||
/* Configure output mode */
|
||||
shift = (ch & 0x1) * CCMR_CHANNEL_SHIFT;
|
||||
ccmr = (TIM_CCMR_PE | TIM_CCMR_M1) << shift;
|
||||
mask = CCMR_CHANNEL_MASK << shift;
|
||||
|
||||
if (ch < 2)
|
||||
regmap_update_bits(priv->regmap, TIM_CCMR1, mask, ccmr);
|
||||
else
|
||||
regmap_update_bits(priv->regmap, TIM_CCMR2, mask, ccmr);
|
||||
|
||||
regmap_update_bits(priv->regmap, TIM_BDTR,
|
||||
TIM_BDTR_MOE | TIM_BDTR_AOE,
|
||||
TIM_BDTR_MOE | TIM_BDTR_AOE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stm32_pwm_set_polarity(struct stm32_pwm *priv, int ch,
|
||||
enum pwm_polarity polarity)
|
||||
{
|
||||
u32 mask;
|
||||
|
||||
mask = TIM_CCER_CC1P << (ch * 4);
|
||||
if (priv->have_complementary_output)
|
||||
mask |= TIM_CCER_CC1NP << (ch * 4);
|
||||
|
||||
regmap_update_bits(priv->regmap, TIM_CCER, mask,
|
||||
polarity == PWM_POLARITY_NORMAL ? 0 : mask);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stm32_pwm_enable(struct stm32_pwm *priv, int ch)
|
||||
{
|
||||
u32 mask;
|
||||
int ret;
|
||||
|
||||
ret = clk_enable(priv->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Enable channel */
|
||||
mask = TIM_CCER_CC1E << (ch * 4);
|
||||
if (priv->have_complementary_output)
|
||||
mask |= TIM_CCER_CC1NE << (ch * 4);
|
||||
|
||||
regmap_update_bits(priv->regmap, TIM_CCER, mask, mask);
|
||||
|
||||
/* Make sure that registers are updated */
|
||||
regmap_update_bits(priv->regmap, TIM_EGR, TIM_EGR_UG, TIM_EGR_UG);
|
||||
|
||||
/* Enable controller */
|
||||
regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN, TIM_CR1_CEN);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void stm32_pwm_disable(struct stm32_pwm *priv, int ch)
|
||||
{
|
||||
u32 mask;
|
||||
|
||||
/* Disable channel */
|
||||
mask = TIM_CCER_CC1E << (ch * 4);
|
||||
if (priv->have_complementary_output)
|
||||
mask |= TIM_CCER_CC1NE << (ch * 4);
|
||||
|
||||
regmap_update_bits(priv->regmap, TIM_CCER, mask, 0);
|
||||
|
||||
/* When all channels are disabled, we can disable the controller */
|
||||
if (!active_channels(priv))
|
||||
regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN, 0);
|
||||
|
||||
clk_disable(priv->clk);
|
||||
}
|
||||
|
||||
static int stm32_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||
struct pwm_state *state)
|
||||
{
|
||||
bool enabled;
|
||||
struct stm32_pwm *priv = to_stm32_pwm_dev(chip);
|
||||
int ret;
|
||||
|
||||
enabled = pwm->state.enabled;
|
||||
|
||||
if (enabled && !state->enabled) {
|
||||
stm32_pwm_disable(priv, pwm->hwpwm);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (state->polarity != pwm->state.polarity)
|
||||
stm32_pwm_set_polarity(priv, pwm->hwpwm, state->polarity);
|
||||
|
||||
ret = stm32_pwm_config(priv, pwm->hwpwm,
|
||||
state->duty_cycle, state->period);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!enabled && state->enabled)
|
||||
ret = stm32_pwm_enable(priv, pwm->hwpwm);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct pwm_ops stm32pwm_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.apply = stm32_pwm_apply,
|
||||
};
|
||||
|
||||
static int stm32_pwm_set_breakinput(struct stm32_pwm *priv,
|
||||
int index, int level, int filter)
|
||||
{
|
||||
u32 bke = (index == 0) ? TIM_BDTR_BKE : TIM_BDTR_BK2E;
|
||||
int shift = (index == 0) ? TIM_BDTR_BKF_SHIFT : TIM_BDTR_BK2F_SHIFT;
|
||||
u32 mask = (index == 0) ? TIM_BDTR_BKE | TIM_BDTR_BKP | TIM_BDTR_BKF
|
||||
: TIM_BDTR_BK2E | TIM_BDTR_BK2P | TIM_BDTR_BK2F;
|
||||
u32 bdtr = bke;
|
||||
|
||||
/*
|
||||
* The both bits could be set since only one will be wrote
|
||||
* due to mask value.
|
||||
*/
|
||||
if (level)
|
||||
bdtr |= TIM_BDTR_BKP | TIM_BDTR_BK2P;
|
||||
|
||||
bdtr |= (filter & TIM_BDTR_BKF_MASK) << shift;
|
||||
|
||||
regmap_update_bits(priv->regmap, TIM_BDTR, mask, bdtr);
|
||||
|
||||
regmap_read(priv->regmap, TIM_BDTR, &bdtr);
|
||||
|
||||
return (bdtr & bke) ? 0 : -EINVAL;
|
||||
}
|
||||
|
||||
static int stm32_pwm_apply_breakinputs(struct stm32_pwm *priv,
|
||||
struct device_node *np)
|
||||
{
|
||||
struct stm32_breakinput breakinput[MAX_BREAKINPUT];
|
||||
int nb, ret, i, array_size;
|
||||
|
||||
nb = of_property_count_elems_of_size(np, "st,breakinput",
|
||||
sizeof(struct stm32_breakinput));
|
||||
|
||||
/*
|
||||
* Because "st,breakinput" parameter is optional do not make probe
|
||||
* failed if it doesn't exist.
|
||||
*/
|
||||
if (nb <= 0)
|
||||
return 0;
|
||||
|
||||
if (nb > MAX_BREAKINPUT)
|
||||
return -EINVAL;
|
||||
|
||||
array_size = nb * sizeof(struct stm32_breakinput) / sizeof(u32);
|
||||
ret = of_property_read_u32_array(np, "st,breakinput",
|
||||
(u32 *)breakinput, array_size);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < nb && !ret; i++) {
|
||||
ret = stm32_pwm_set_breakinput(priv,
|
||||
breakinput[i].index,
|
||||
breakinput[i].level,
|
||||
breakinput[i].filter);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void stm32_pwm_detect_complementary(struct stm32_pwm *priv)
|
||||
{
|
||||
u32 ccer;
|
||||
|
||||
/*
|
||||
* If complementary bit doesn't exist writing 1 will have no
|
||||
* effect so we can detect it.
|
||||
*/
|
||||
regmap_update_bits(priv->regmap,
|
||||
TIM_CCER, TIM_CCER_CC1NE, TIM_CCER_CC1NE);
|
||||
regmap_read(priv->regmap, TIM_CCER, &ccer);
|
||||
regmap_update_bits(priv->regmap, TIM_CCER, TIM_CCER_CC1NE, 0);
|
||||
|
||||
priv->have_complementary_output = (ccer != 0);
|
||||
}
|
||||
|
||||
static int stm32_pwm_detect_channels(struct stm32_pwm *priv)
|
||||
{
|
||||
u32 ccer;
|
||||
int npwm = 0;
|
||||
|
||||
/*
|
||||
* If channels enable bits don't exist writing 1 will have no
|
||||
* effect so we can detect and count them.
|
||||
*/
|
||||
regmap_update_bits(priv->regmap,
|
||||
TIM_CCER, TIM_CCER_CCXE, TIM_CCER_CCXE);
|
||||
regmap_read(priv->regmap, TIM_CCER, &ccer);
|
||||
regmap_update_bits(priv->regmap, TIM_CCER, TIM_CCER_CCXE, 0);
|
||||
|
||||
if (ccer & TIM_CCER_CC1E)
|
||||
npwm++;
|
||||
|
||||
if (ccer & TIM_CCER_CC2E)
|
||||
npwm++;
|
||||
|
||||
if (ccer & TIM_CCER_CC3E)
|
||||
npwm++;
|
||||
|
||||
if (ccer & TIM_CCER_CC4E)
|
||||
npwm++;
|
||||
|
||||
return npwm;
|
||||
}
|
||||
|
||||
static int stm32_pwm_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct stm32_timers *ddata = dev_get_drvdata(pdev->dev.parent);
|
||||
struct stm32_pwm *priv;
|
||||
int ret;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->regmap = ddata->regmap;
|
||||
priv->clk = ddata->clk;
|
||||
priv->max_arr = ddata->max_arr;
|
||||
|
||||
if (!priv->regmap || !priv->clk)
|
||||
return -EINVAL;
|
||||
|
||||
ret = stm32_pwm_apply_breakinputs(priv, np);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
stm32_pwm_detect_complementary(priv);
|
||||
|
||||
priv->chip.base = -1;
|
||||
priv->chip.dev = dev;
|
||||
priv->chip.ops = &stm32pwm_ops;
|
||||
priv->chip.npwm = stm32_pwm_detect_channels(priv);
|
||||
|
||||
ret = pwmchip_add(&priv->chip);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
platform_set_drvdata(pdev, priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stm32_pwm_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct stm32_pwm *priv = platform_get_drvdata(pdev);
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < priv->chip.npwm; i++)
|
||||
pwm_disable(&priv->chip.pwms[i]);
|
||||
|
||||
pwmchip_remove(&priv->chip);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id stm32_pwm_of_match[] = {
|
||||
{ .compatible = "st,stm32-pwm", },
|
||||
{ /* end node */ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, stm32_pwm_of_match);
|
||||
|
||||
static struct platform_driver stm32_pwm_driver = {
|
||||
.probe = stm32_pwm_probe,
|
||||
.remove = stm32_pwm_remove,
|
||||
.driver = {
|
||||
.name = "stm32-pwm",
|
||||
.of_match_table = stm32_pwm_of_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(stm32_pwm_driver);
|
||||
|
||||
MODULE_ALIAS("platform:stm32-pwm");
|
||||
MODULE_DESCRIPTION("STMicroelectronics STM32 PWM driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -139,7 +139,7 @@ static ssize_t ad7816_store_mode(struct device *dev,
|
||||
return len;
|
||||
}
|
||||
|
||||
static IIO_DEVICE_ATTR(mode, S_IRUGO | S_IWUSR,
|
||||
static IIO_DEVICE_ATTR(mode, 0644,
|
||||
ad7816_show_mode,
|
||||
ad7816_store_mode,
|
||||
0);
|
||||
@ -151,7 +151,7 @@ static ssize_t ad7816_show_available_modes(struct device *dev,
|
||||
return sprintf(buf, "full\npower-save\n");
|
||||
}
|
||||
|
||||
static IIO_DEVICE_ATTR(available_modes, S_IRUGO, ad7816_show_available_modes,
|
||||
static IIO_DEVICE_ATTR(available_modes, 0444, ad7816_show_available_modes,
|
||||
NULL, 0);
|
||||
|
||||
static ssize_t ad7816_show_channel(struct device *dev,
|
||||
@ -197,7 +197,7 @@ static ssize_t ad7816_store_channel(struct device *dev,
|
||||
return len;
|
||||
}
|
||||
|
||||
static IIO_DEVICE_ATTR(channel, S_IRUGO | S_IWUSR,
|
||||
static IIO_DEVICE_ATTR(channel, 0644,
|
||||
ad7816_show_channel,
|
||||
ad7816_store_channel,
|
||||
0);
|
||||
@ -228,7 +228,7 @@ static ssize_t ad7816_show_value(struct device *dev,
|
||||
return sprintf(buf, "%u\n", data);
|
||||
}
|
||||
|
||||
static IIO_DEVICE_ATTR(value, S_IRUGO, ad7816_show_value, NULL, 0);
|
||||
static IIO_DEVICE_ATTR(value, 0444, ad7816_show_value, NULL, 0);
|
||||
|
||||
static struct attribute *ad7816_attributes[] = {
|
||||
&iio_dev_attr_available_modes.dev_attr.attr,
|
||||
@ -319,7 +319,7 @@ static inline ssize_t ad7816_set_oti(struct device *dev,
|
||||
return len;
|
||||
}
|
||||
|
||||
static IIO_DEVICE_ATTR(oti, S_IRUGO | S_IWUSR,
|
||||
static IIO_DEVICE_ATTR(oti, 0644,
|
||||
ad7816_show_oti, ad7816_set_oti, 0);
|
||||
|
||||
static struct attribute *ad7816_event_attributes[] = {
|
||||
|
@ -610,27 +610,27 @@ static int ad7150_probe(struct i2c_client *client,
|
||||
|
||||
if (client->irq) {
|
||||
ret = devm_request_threaded_irq(&client->dev, client->irq,
|
||||
NULL,
|
||||
&ad7150_event_handler,
|
||||
IRQF_TRIGGER_RISING |
|
||||
IRQF_TRIGGER_FALLING |
|
||||
IRQF_ONESHOT,
|
||||
"ad7150_irq1",
|
||||
indio_dev);
|
||||
NULL,
|
||||
&ad7150_event_handler,
|
||||
IRQF_TRIGGER_RISING |
|
||||
IRQF_TRIGGER_FALLING |
|
||||
IRQF_ONESHOT,
|
||||
"ad7150_irq1",
|
||||
indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (client->dev.platform_data) {
|
||||
ret = devm_request_threaded_irq(&client->dev, *(unsigned int *)
|
||||
client->dev.platform_data,
|
||||
NULL,
|
||||
&ad7150_event_handler,
|
||||
IRQF_TRIGGER_RISING |
|
||||
IRQF_TRIGGER_FALLING |
|
||||
IRQF_ONESHOT,
|
||||
"ad7150_irq2",
|
||||
indio_dev);
|
||||
client->dev.platform_data,
|
||||
NULL,
|
||||
&ad7150_event_handler,
|
||||
IRQF_TRIGGER_RISING |
|
||||
IRQF_TRIGGER_FALLING |
|
||||
IRQF_ONESHOT,
|
||||
"ad7150_irq2",
|
||||
indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
62
include/linux/iio/timer/stm32-timer-trigger.h
Normal file
62
include/linux/iio/timer/stm32-timer-trigger.h
Normal file
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (C) STMicroelectronics 2016
|
||||
*
|
||||
* Author: Benjamin Gaignard <benjamin.gaignard@st.com>
|
||||
*
|
||||
* License terms: GNU General Public License (GPL), version 2
|
||||
*/
|
||||
|
||||
#ifndef _STM32_TIMER_TRIGGER_H_
|
||||
#define _STM32_TIMER_TRIGGER_H_
|
||||
|
||||
#define TIM1_TRGO "tim1_trgo"
|
||||
#define TIM1_CH1 "tim1_ch1"
|
||||
#define TIM1_CH2 "tim1_ch2"
|
||||
#define TIM1_CH3 "tim1_ch3"
|
||||
#define TIM1_CH4 "tim1_ch4"
|
||||
|
||||
#define TIM2_TRGO "tim2_trgo"
|
||||
#define TIM2_CH1 "tim2_ch1"
|
||||
#define TIM2_CH2 "tim2_ch2"
|
||||
#define TIM2_CH3 "tim2_ch3"
|
||||
#define TIM2_CH4 "tim2_ch4"
|
||||
|
||||
#define TIM3_TRGO "tim3_trgo"
|
||||
#define TIM3_CH1 "tim3_ch1"
|
||||
#define TIM3_CH2 "tim3_ch2"
|
||||
#define TIM3_CH3 "tim3_ch3"
|
||||
#define TIM3_CH4 "tim3_ch4"
|
||||
|
||||
#define TIM4_TRGO "tim4_trgo"
|
||||
#define TIM4_CH1 "tim4_ch1"
|
||||
#define TIM4_CH2 "tim4_ch2"
|
||||
#define TIM4_CH3 "tim4_ch3"
|
||||
#define TIM4_CH4 "tim4_ch4"
|
||||
|
||||
#define TIM5_TRGO "tim5_trgo"
|
||||
#define TIM5_CH1 "tim5_ch1"
|
||||
#define TIM5_CH2 "tim5_ch2"
|
||||
#define TIM5_CH3 "tim5_ch3"
|
||||
#define TIM5_CH4 "tim5_ch4"
|
||||
|
||||
#define TIM6_TRGO "tim6_trgo"
|
||||
|
||||
#define TIM7_TRGO "tim7_trgo"
|
||||
|
||||
#define TIM8_TRGO "tim8_trgo"
|
||||
#define TIM8_CH1 "tim8_ch1"
|
||||
#define TIM8_CH2 "tim8_ch2"
|
||||
#define TIM8_CH3 "tim8_ch3"
|
||||
#define TIM8_CH4 "tim8_ch4"
|
||||
|
||||
#define TIM9_TRGO "tim9_trgo"
|
||||
#define TIM9_CH1 "tim9_ch1"
|
||||
#define TIM9_CH2 "tim9_ch2"
|
||||
|
||||
#define TIM12_TRGO "tim12_trgo"
|
||||
#define TIM12_CH1 "tim12_ch1"
|
||||
#define TIM12_CH2 "tim12_ch2"
|
||||
|
||||
bool is_stm32_timer_trigger(struct iio_trigger *trig);
|
||||
|
||||
#endif
|
@ -1441,7 +1441,8 @@ enum motionsensor_type {
|
||||
MOTIONSENSE_TYPE_PROX = 3,
|
||||
MOTIONSENSE_TYPE_LIGHT = 4,
|
||||
MOTIONSENSE_TYPE_ACTIVITY = 5,
|
||||
MOTIONSENSE_TYPE_MAX
|
||||
MOTIONSENSE_TYPE_BARO = 6,
|
||||
MOTIONSENSE_TYPE_MAX,
|
||||
};
|
||||
|
||||
/* List of motion sensor locations. */
|
||||
|
71
include/linux/mfd/stm32-timers.h
Normal file
71
include/linux/mfd/stm32-timers.h
Normal file
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (C) STMicroelectronics 2016
|
||||
*
|
||||
* Author: Benjamin Gaignard <benjamin.gaignard@st.com>
|
||||
*
|
||||
* License terms: GNU General Public License (GPL), version 2
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_STM32_GPTIMER_H_
|
||||
#define _LINUX_STM32_GPTIMER_H_
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#define TIM_CR1 0x00 /* Control Register 1 */
|
||||
#define TIM_CR2 0x04 /* Control Register 2 */
|
||||
#define TIM_SMCR 0x08 /* Slave mode control reg */
|
||||
#define TIM_DIER 0x0C /* DMA/interrupt register */
|
||||
#define TIM_SR 0x10 /* Status register */
|
||||
#define TIM_EGR 0x14 /* Event Generation Reg */
|
||||
#define TIM_CCMR1 0x18 /* Capt/Comp 1 Mode Reg */
|
||||
#define TIM_CCMR2 0x1C /* Capt/Comp 2 Mode Reg */
|
||||
#define TIM_CCER 0x20 /* Capt/Comp Enable Reg */
|
||||
#define TIM_PSC 0x28 /* Prescaler */
|
||||
#define TIM_ARR 0x2c /* Auto-Reload Register */
|
||||
#define TIM_CCR1 0x34 /* Capt/Comp Register 1 */
|
||||
#define TIM_CCR2 0x38 /* Capt/Comp Register 2 */
|
||||
#define TIM_CCR3 0x3C /* Capt/Comp Register 3 */
|
||||
#define TIM_CCR4 0x40 /* Capt/Comp Register 4 */
|
||||
#define TIM_BDTR 0x44 /* Break and Dead-Time Reg */
|
||||
|
||||
#define TIM_CR1_CEN BIT(0) /* Counter Enable */
|
||||
#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_SMCR_SMS (BIT(0) | BIT(1) | BIT(2)) /* Slave mode selection */
|
||||
#define TIM_SMCR_TS (BIT(4) | BIT(5) | BIT(6)) /* Trigger selection */
|
||||
#define TIM_DIER_UIE BIT(0) /* Update interrupt */
|
||||
#define TIM_SR_UIF BIT(0) /* Update interrupt flag */
|
||||
#define TIM_EGR_UG BIT(0) /* Update Generation */
|
||||
#define TIM_CCMR_PE BIT(3) /* Channel Preload Enable */
|
||||
#define TIM_CCMR_M1 (BIT(6) | BIT(5)) /* Channel PWM Mode 1 */
|
||||
#define TIM_CCER_CC1E BIT(0) /* Capt/Comp 1 out Ena */
|
||||
#define TIM_CCER_CC1P BIT(1) /* Capt/Comp 1 Polarity */
|
||||
#define TIM_CCER_CC1NE BIT(2) /* Capt/Comp 1N out Ena */
|
||||
#define TIM_CCER_CC1NP BIT(3) /* Capt/Comp 1N Polarity */
|
||||
#define TIM_CCER_CC2E BIT(4) /* Capt/Comp 2 out Ena */
|
||||
#define TIM_CCER_CC3E BIT(8) /* Capt/Comp 3 out Ena */
|
||||
#define TIM_CCER_CC4E BIT(12) /* Capt/Comp 4 out Ena */
|
||||
#define TIM_CCER_CCXE (BIT(0) | BIT(4) | BIT(8) | BIT(12))
|
||||
#define TIM_BDTR_BKE BIT(12) /* Break input enable */
|
||||
#define TIM_BDTR_BKP BIT(13) /* Break input polarity */
|
||||
#define TIM_BDTR_AOE BIT(14) /* Automatic Output Enable */
|
||||
#define TIM_BDTR_MOE BIT(15) /* Main Output Enable */
|
||||
#define TIM_BDTR_BKF (BIT(16) | BIT(17) | BIT(18) | BIT(19))
|
||||
#define TIM_BDTR_BK2F (BIT(20) | BIT(21) | BIT(22) | BIT(23))
|
||||
#define TIM_BDTR_BK2E BIT(24) /* Break 2 input enable */
|
||||
#define TIM_BDTR_BK2P BIT(25) /* Break 2 input polarity */
|
||||
|
||||
#define MAX_TIM_PSC 0xFFFF
|
||||
#define TIM_CR2_MMS_SHIFT 4
|
||||
#define TIM_SMCR_TS_SHIFT 4
|
||||
#define TIM_BDTR_BKF_MASK 0xF
|
||||
#define TIM_BDTR_BKF_SHIFT 16
|
||||
#define TIM_BDTR_BK2F_SHIFT 20
|
||||
|
||||
struct stm32_timers {
|
||||
struct clk *clk;
|
||||
struct regmap *regmap;
|
||||
u32 max_arr;
|
||||
};
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user