forked from Minki/linux
Staging/IIO patches for 4.19-rc1
Here are the big staging/iio patches for 4.19-rc1. Lots of churn here, with tons of cleanups happening in staging drivers, a removal of an old crypto driver that no one was using (skein), and the addition of some new IIO drivers. Also added was a "gasket" driver from Google that needs loads of work and the erofs filesystem. Even with adding all of the new drivers and a new filesystem, we are only adding about 1000 lines overall to the kernel linecount, which shows just how much cleanup happened, and how big the unused crypto driver was. All of these have been in the linux-next tree for a while now with no reported issues. Note, you will have a merge problem with a device tree IIO file and the MAINTAINERS file, both resolutions are easy, just take all changed. There will be a skein file merge issue as well, but that file got deleted so just drop that. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCW3g+2A8cZ3JlZ0Brcm9h aC5jb20ACgkQMUfUDdst+ykwGACfQZz3Ncvc7thHkZytxxqQnbx5JpkAn0yV5SvF yVXG9SA9yCTKVjYczZjZ =6t/x -----END PGP SIGNATURE----- Merge tag 'staging-4.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging Pull staging and IIO updates from Greg KH: "Here are the big staging/iio patches for 4.19-rc1. Lots of churn here, with tons of cleanups happening in staging drivers, a removal of an old crypto driver that no one was using (skein), and the addition of some new IIO drivers. Also added was a "gasket" driver from Google that needs loads of work and the erofs filesystem. Even with adding all of the new drivers and a new filesystem, we are only adding about 1000 lines overall to the kernel linecount, which shows just how much cleanup happened, and how big the unused crypto driver was. All of these have been in the linux-next tree for a while now with no reported issues" * tag 'staging-4.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging: (903 commits) staging:rtl8192u: Remove unused macro definitions - Style staging:rtl8192u: Add spaces around '+' operator - Style staging:rtl8192u: Remove stale comment - Style staging: rtl8188eu: remove unused mp_custom_oid.h staging: fbtft: Add spaces around / - Style staging: fbtft: Erases some repetitive usage of function name - Style staging: fbtft: Adjust some empty-line problems - Style staging: fbtft: Removes one nesting level to help readability - Style staging: fbtft: Changes gamma table to define. staging: fbtft: A bit more information on dev_err. staging: fbtft: Fixes some alignment issues - Style staging: fbtft: Puts macro arguments in parenthesis to avoid precedence issues - Style staging: rtl8188eu: remove unused array dB_Invert_Table staging: rtl8188eu: remove whitespace, add missing blank line staging: rtl8188eu: use is_multicast_ether_addr in rtw_sta_mgt.c staging: rtl8188eu: remove whitespace - style staging: rtl8188eu: cleanup block comment - style staging: rtl8188eu: use is_multicast_ether_addr in rtl8188eu_xmit.c staging: rtl8188eu: use is_multicast_ether_addr in recv_linux.c staging: rtlwifi: refactor rtl_get_tcb_desc ...
This commit is contained in:
commit
2475c515d4
@ -197,6 +197,18 @@ Description:
|
||||
Angle of rotation. Units after application of scale and offset
|
||||
are radians.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_positionrelative_x_raw
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_positionrelative_y_raw
|
||||
KernelVersion: 4.18
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Relative position in direction x or y on a pad (may be
|
||||
arbitrarily assigned but should match other such assignments on
|
||||
device).
|
||||
Units after application of scale and offset are milli percents
|
||||
from the pad's size in both directions. Should be calibrated by
|
||||
the consumer.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_anglvel_x_raw
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_anglvel_y_raw
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_anglvel_z_raw
|
||||
@ -1295,13 +1307,16 @@ What: /sys/.../iio:deviceX/in_intensityY_raw
|
||||
What: /sys/.../iio:deviceX/in_intensityY_ir_raw
|
||||
What: /sys/.../iio:deviceX/in_intensityY_both_raw
|
||||
What: /sys/.../iio:deviceX/in_intensityY_uv_raw
|
||||
What: /sys/.../iio:deviceX/in_intensityY_duv_raw
|
||||
KernelVersion: 3.4
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Unit-less light intensity. Modifiers both and ir indicate
|
||||
that measurements contain visible and infrared light
|
||||
components or just infrared light, respectively. Modifier uv indicates
|
||||
that measurements contain ultraviolet light components.
|
||||
components or just infrared light, respectively. Modifier
|
||||
uv indicates that measurements contain ultraviolet light
|
||||
components. Modifier duv indicates that measurements
|
||||
contain deep ultraviolet light components.
|
||||
|
||||
What: /sys/.../iio:deviceX/in_uvindex_input
|
||||
KernelVersion: 4.6
|
||||
@ -1663,3 +1678,10 @@ KernelVersion: 4.12
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Raw counter device counters direction for channel Y.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_phaseY_raw
|
||||
KernelVersion: 4.18
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Raw (unscaled) phase difference reading from channel Y
|
||||
that can be processed to radians.
|
47
Documentation/ABI/testing/sysfs-bus-iio-isl29501
Normal file
47
Documentation/ABI/testing/sysfs-bus-iio-isl29501
Normal file
@ -0,0 +1,47 @@
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_proximity0_agc_gain
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_proximity0_agc_gain_bias
|
||||
KernelVersion: 4.18
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
This sensor has an automatic gain control (agc) loop
|
||||
which sets the analog signal levels at an optimum
|
||||
level by controlling programmable gain amplifiers. The
|
||||
criteria for optimal gain is determined by the sensor.
|
||||
|
||||
Return the actual gain value as an integer in [0; 65536]
|
||||
range when read from.
|
||||
|
||||
The agc gain read when measuring crosstalk shall be
|
||||
written into in_proximity0_agc_gain_bias.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_proximity0_calib_phase_temp_a
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_proximity0_calib_phase_temp_b
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_proximity0_calib_phase_light_a
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_proximity0_calib_phase_light_b
|
||||
KernelVersion: 4.18
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
The sensor is able to perform correction of distance
|
||||
measurements due to changing temperature and ambient
|
||||
light conditions. It can be programmed to correct for
|
||||
a second order error polynomial.
|
||||
|
||||
Phase data has to be collected when temperature and
|
||||
ambient light are modulated independently.
|
||||
|
||||
Then a least squares curve fit to a second order
|
||||
polynomial has to be generated from the data. The
|
||||
resultant curves have the form ax^2 + bx + c.
|
||||
|
||||
From those two curves, a and b coefficients shall be
|
||||
stored in in_proximity0_calib_phase_temp_a and
|
||||
in_proximity0_calib_phase_temp_b for temperature and
|
||||
in in_proximity0_calib_phase_light_a and
|
||||
in_proximity0_calib_phase_light_b for ambient light.
|
||||
|
||||
Those values must be integer in [0; 8355840] range.
|
||||
|
||||
Finally, the c constant is set by the sensor
|
||||
internally.
|
||||
|
||||
The value stored in sensor is displayed when read from.
|
22
Documentation/ABI/testing/sysfs-bus-iio-light-si1133
Normal file
22
Documentation/ABI/testing/sysfs-bus-iio-light-si1133
Normal file
@ -0,0 +1,22 @@
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_intensity_ir_small_raw
|
||||
KernelVersion: 4.18
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Unit-less infrared intensity. The intensity is measured from 1
|
||||
dark photodiode. "small" indicate the surface area capturing
|
||||
infrared.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_intensity_ir_large_raw
|
||||
KernelVersion: 4.18
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Unit-less infrared intensity. The intensity is measured from 4
|
||||
dark photodiodes. "large" indicate the surface area capturing
|
||||
infrared.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_intensity_large_raw
|
||||
KernelVersion: 4.18
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Unit-less light intensity with more diodes.
|
||||
|
@ -1,9 +1,12 @@
|
||||
Analog Devices ADXL345 3-Axis, +/-(2g/4g/8g/16g) Digital Accelerometer
|
||||
Analog Devices ADXL345/ADXL375 3-Axis Digital Accelerometers
|
||||
|
||||
http://www.analog.com/en/products/mems/accelerometers/adxl345.html
|
||||
http://www.analog.com/en/products/sensors-mems/accelerometers/adxl375.html
|
||||
|
||||
Required properties:
|
||||
- compatible : should be "adi,adxl345"
|
||||
- compatible : should be one of
|
||||
"adi,adxl345"
|
||||
"adi,adxl375"
|
||||
- reg : the I2C address or SPI chip select number of the sensor
|
||||
|
||||
Required properties for SPI bus usage:
|
||||
|
@ -4,6 +4,7 @@ Required properties:
|
||||
- compatible: depending on the SoC this should be one of:
|
||||
- "amlogic,meson8-saradc" for Meson8
|
||||
- "amlogic,meson8b-saradc" for Meson8b
|
||||
- "amlogic,meson8m2-saradc" for Meson8m2
|
||||
- "amlogic,meson-gxbb-saradc" for GXBB
|
||||
- "amlogic,meson-gxl-saradc" for GXL
|
||||
- "amlogic,meson-gxm-saradc" for GXM
|
||||
|
@ -21,6 +21,14 @@ Optional properties:
|
||||
- dmas: Phandle to dma channel for the ADC.
|
||||
- dma-names: Must be "rx" when dmas property is being used.
|
||||
See ../../dma/dma.txt for details.
|
||||
- #io-channel-cells: in case consumer drivers are attached, this must be 1.
|
||||
See <Documentation/devicetree/bindings/iio/iio-bindings.txt> for details.
|
||||
|
||||
Properties for consumer drivers:
|
||||
- Consumer drivers can be connected to this producer device, as specified
|
||||
in <Documentation/devicetree/bindings/iio/iio-bindings.txt>
|
||||
- Channels exposed are specified in:
|
||||
<dt-bindings/iio/adc/at91-sama5d2_adc.txt>
|
||||
|
||||
Example:
|
||||
|
||||
@ -38,4 +46,5 @@ adc: adc@fc030000 {
|
||||
atmel,trigger-edge-type = <IRQ_TYPE_EDGE_BOTH>;
|
||||
dmas = <&dma0 (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | AT91_XDMAC_DT_PERID(25))>;
|
||||
dma-names = "rx";
|
||||
#io-channel-cells = <1>;
|
||||
}
|
||||
|
@ -8,11 +8,17 @@ Required properties:
|
||||
See Documentation/devicetree/bindings/gpio/gpio.txt
|
||||
- avdd-supply: Definition of the regulator used as analog supply
|
||||
|
||||
Optional properties:
|
||||
- clock-frequency: Frequency of PD_SCK in Hz
|
||||
Minimum value allowed is 10 kHz because of maximum
|
||||
high time of 50 microseconds.
|
||||
|
||||
Example:
|
||||
weight@0 {
|
||||
weight {
|
||||
compatible = "avia,hx711";
|
||||
sck-gpios = <&gpio3 10 GPIO_ACTIVE_HIGH>;
|
||||
dout-gpios = <&gpio0 7 GPIO_ACTIVE_HIGH>;
|
||||
avdd-suppy = <&avdd>;
|
||||
clock-frequency = <100000>;
|
||||
};
|
||||
|
||||
|
@ -7,7 +7,7 @@ Required properties:
|
||||
|
||||
Example node:
|
||||
|
||||
ads1202: adc@0 {
|
||||
ads1202: adc {
|
||||
compatible = "sd-modulator";
|
||||
#io-channel-cells = <0>;
|
||||
};
|
||||
|
@ -0,0 +1,36 @@
|
||||
Spreadtrum SC27XX series PMICs ADC binding
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be one of the following.
|
||||
"sprd,sc2720-adc"
|
||||
"sprd,sc2721-adc"
|
||||
"sprd,sc2723-adc"
|
||||
"sprd,sc2730-adc"
|
||||
"sprd,sc2731-adc"
|
||||
- reg: The address offset of ADC controller.
|
||||
- interrupt-parent: The interrupt controller.
|
||||
- interrupts: The interrupt number for the ADC device.
|
||||
- #io-channel-cells: Number of cells in an IIO specifier.
|
||||
- hwlocks: Reference to a phandle of a hwlock provider node.
|
||||
|
||||
Example:
|
||||
|
||||
sc2731_pmic: pmic@0 {
|
||||
compatible = "sprd,sc2731";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <26000000>;
|
||||
interrupts = <GIC_SPI 31 IRQ_TYPE_LEVEL_HIGH>;
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <2>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
pmic_adc: adc@480 {
|
||||
compatible = "sprd,sc2731-adc";
|
||||
reg = <0x480>;
|
||||
interrupt-parent = <&sc2731_pmic>;
|
||||
interrupts = <0 IRQ_TYPE_LEVEL_HIGH>;
|
||||
#io-channel-cells = <1>;
|
||||
hwlocks = <&hwlock 4>;
|
||||
};
|
||||
};
|
78
Documentation/devicetree/bindings/iio/dac/ad5758.txt
Normal file
78
Documentation/devicetree/bindings/iio/dac/ad5758.txt
Normal file
@ -0,0 +1,78 @@
|
||||
Analog Devices AD5758 DAC device driver
|
||||
|
||||
Required properties for the AD5758:
|
||||
- compatible: Must be "adi,ad5758"
|
||||
- reg: SPI chip select number for the device
|
||||
- spi-max-frequency: Max SPI frequency to use (< 50000000)
|
||||
- spi-cpha: is the only mode that is supported
|
||||
|
||||
Required properties:
|
||||
|
||||
- adi,dc-dc-mode: Mode of operation of the dc-to-dc converter
|
||||
Dynamic Power Control (DPC)
|
||||
In this mode, the AD5758 circuitry senses the output
|
||||
voltage and dynamically regulates the supply voltage,
|
||||
VDPC+, to meet compliance requirements plus an optimized
|
||||
headroom voltage for the output buffer.
|
||||
|
||||
Programmable Power Control (PPC)
|
||||
In this mode, the VDPC+ voltage is user-programmable to
|
||||
a fixed level that needs to accommodate the maximum output
|
||||
load required.
|
||||
|
||||
The output of the DAC core is either converted to a
|
||||
current or voltage output at the VIOUT pin. Only one mode
|
||||
can be enabled at any one time.
|
||||
|
||||
The following values are currently supported:
|
||||
* 1: DPC current mode
|
||||
* 2: DPC voltage mode
|
||||
* 3: PPC current mode
|
||||
|
||||
Depending on the selected output mode (voltage or current) one of the
|
||||
two properties must
|
||||
be present:
|
||||
|
||||
- adi,range-microvolt: Voltage output range
|
||||
The array of voltage output ranges must contain two fields:
|
||||
* <0 5000000>: 0 V to 5 V voltage range
|
||||
* <0 10000000>: 0 V to 10 V voltage range
|
||||
* <(-5000000) 5000000>: ±5 V voltage range
|
||||
* <(-10000000) 10000000>: ±10 V voltage range
|
||||
- adi,range-microamp: Current output range
|
||||
The array of current output ranges must contain two fields:
|
||||
* <0 20000>: 0 mA to 20 mA current range
|
||||
* <0 24000>: 0 mA to 24 mA current range
|
||||
* <4 24000>: 4 mA to 20 mA current range
|
||||
* <(-20000) 20000>: ±20 mA current range
|
||||
* <(-24000) 24000>: ±24 mA current range
|
||||
* <(-1000) 22000>: −1 mA to +22 mA current range
|
||||
|
||||
Optional properties:
|
||||
|
||||
- adi,dc-dc-ilim-microamp: The dc-to-dc converter current limit
|
||||
The following values are currently supported [uA]:
|
||||
* 150000
|
||||
* 200000
|
||||
* 250000
|
||||
* 300000
|
||||
* 350000
|
||||
* 400000
|
||||
|
||||
- adi,slew-time-us: The time it takes for the output to reach the
|
||||
full scale [uS]
|
||||
The supported range is between 133us up to 1023984375us
|
||||
|
||||
AD5758 Example:
|
||||
|
||||
dac@0 {
|
||||
compatible = "adi,ad5758";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <1000000>;
|
||||
spi-cpha;
|
||||
|
||||
adi,dc-dc-mode = <2>;
|
||||
adi,range-microvolt = <0 10000000>;
|
||||
adi,dc-dc-ilim-microamp = <200000>;
|
||||
adi,slew-time-us = <125000>;
|
||||
};
|
@ -6,6 +6,7 @@ Required properties:
|
||||
- compatible : should be one of
|
||||
"invensense,mpu6050"
|
||||
"invensense,mpu6500"
|
||||
"invensense,mpu6515"
|
||||
"invensense,mpu9150"
|
||||
"invensense,mpu9250"
|
||||
"invensense,mpu9255"
|
||||
|
13
Documentation/devicetree/bindings/iio/light/isl29501.txt
Normal file
13
Documentation/devicetree/bindings/iio/light/isl29501.txt
Normal file
@ -0,0 +1,13 @@
|
||||
* ISL29501 Time-of-flight sensor.
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : should be "renesas,isl29501"
|
||||
- reg : the I2C address of the sensor
|
||||
|
||||
Example:
|
||||
|
||||
isl29501@57 {
|
||||
compatible = "renesas,isl29501";
|
||||
reg = <0x57>;
|
||||
};
|
@ -8,10 +8,6 @@ Required properties:
|
||||
"bosch,bme280"
|
||||
|
||||
Optional properties:
|
||||
- chip-id: configurable chip id for non-default chip revisions
|
||||
- temp-measurement-period: temperature measurement period (milliseconds)
|
||||
- default-oversampling: default oversampling value to be used at startup,
|
||||
value range is 0-3 with rising sensitivity.
|
||||
- interrupts: interrupt mapping for IRQ
|
||||
- reset-gpios: a GPIO line handling reset of the sensor: as the line is
|
||||
active low, it should be marked GPIO_ACTIVE_LOW (see gpio/gpio.txt)
|
||||
@ -23,9 +19,6 @@ Example:
|
||||
pressure@77 {
|
||||
compatible = "bosch,bmp085";
|
||||
reg = <0x77>;
|
||||
chip-id = <10>;
|
||||
temp-measurement-period = <100>;
|
||||
default-oversampling = <2>;
|
||||
interrupt-parent = <&gpio0>;
|
||||
interrupts = <25 IRQ_TYPE_EDGE_RISING>;
|
||||
reset-gpios = <&gpio0 26 GPIO_ACTIVE_LOW>;
|
||||
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 745 B |
@ -0,0 +1,30 @@
|
||||
Generic resistive touchscreen ADC
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: must be "resistive-adc-touch"
|
||||
The device must be connected to an ADC device that provides channels for
|
||||
position measurement and optional pressure.
|
||||
Refer to ../iio/iio-bindings.txt for details
|
||||
- iio-channels: must have at least two channels connected to an ADC device.
|
||||
These should correspond to the channels exposed by the ADC device and should
|
||||
have the right index as the ADC device registers them. These channels
|
||||
represent the relative position on the "x" and "y" axes.
|
||||
- iio-channel-names: must have all the channels' names. Mandatory channels
|
||||
are "x" and "y".
|
||||
|
||||
Optional properties:
|
||||
- iio-channels: The third channel named "pressure" is optional and can be
|
||||
used if the ADC device also measures pressure besides position.
|
||||
If this channel is missing, pressure will be ignored and the touchscreen
|
||||
will only report position.
|
||||
- iio-channel-names: optional channel named "pressure".
|
||||
|
||||
Example:
|
||||
|
||||
resistive_touch: resistive_touch {
|
||||
compatible = "resistive-adc-touch";
|
||||
touchscreen-min-pressure = <50000>;
|
||||
io-channels = <&adc 24>, <&adc 25>, <&adc 26>;
|
||||
io-channel-names = "x", "y", "pressure";
|
||||
};
|
@ -7,6 +7,9 @@ Optional properties for Touchscreens:
|
||||
(in pixels)
|
||||
- touchscreen-max-pressure : maximum reported pressure (arbitrary range
|
||||
dependent on the controller)
|
||||
- touchscreen-min-pressure : minimum pressure on the touchscreen to be
|
||||
achieved in order for the touchscreen
|
||||
driver to report a touch event.
|
||||
- touchscreen-fuzz-x : horizontal noise value of the absolute input
|
||||
device (in pixels)
|
||||
- touchscreen-fuzz-y : vertical noise value of the absolute input
|
||||
|
44
MAINTAINERS
44
MAINTAINERS
@ -822,6 +822,14 @@ S: Supported
|
||||
F: drivers/iio/dac/ad5686*
|
||||
F: drivers/iio/dac/ad5696*
|
||||
|
||||
ANALOG DEVICES INC AD5758 DRIVER
|
||||
M: Stefan Popa <stefan.popa@analog.com>
|
||||
L: linux-iio@vger.kernel.org
|
||||
W: http://ez.analog.com/community/linux-device-drivers
|
||||
S: Supported
|
||||
F: drivers/iio/dac/ad5758.c
|
||||
F: Documentation/devicetree/bindings/iio/dac/ad5758.txt
|
||||
|
||||
ANALOG DEVICES INC AD9389B DRIVER
|
||||
M: Hans Verkuil <hans.verkuil@cisco.com>
|
||||
L: linux-media@vger.kernel.org
|
||||
@ -2575,6 +2583,13 @@ S: Maintained
|
||||
F: drivers/auxdisplay/
|
||||
F: include/linux/cfag12864b.h
|
||||
|
||||
AVIA HX711 ANALOG DIGITAL CONVERTER IIO DRIVER
|
||||
M: Andreas Klinger <ak@it-klinger.de>
|
||||
L: linux-iio@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/iio/adc/avia-hx711.txt
|
||||
F: drivers/iio/adc/hx711.c
|
||||
|
||||
AX.25 NETWORK LAYER
|
||||
M: Ralf Baechle <ralf@linux-mips.org>
|
||||
L: linux-hams@vger.kernel.org
|
||||
@ -4472,7 +4487,8 @@ S: Maintained
|
||||
F: drivers/staging/fsl-dpaa2/ethernet
|
||||
|
||||
DPAA2 ETHERNET SWITCH DRIVER
|
||||
M: Razvan Stefanescu <razvan.stefanescu@nxp.com>
|
||||
M: Ioana Radulescu <ruxandra.radulescu@nxp.com>
|
||||
M: Ioana Ciornei <ioana.ciornei@nxp.com>
|
||||
L: linux-kernel@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/staging/fsl-dpaa2/ethsw
|
||||
@ -5980,6 +5996,13 @@ F: scripts/gcc-plugin.sh
|
||||
F: scripts/Makefile.gcc-plugins
|
||||
F: Documentation/gcc-plugins.txt
|
||||
|
||||
GASKET DRIVER FRAMEWORK
|
||||
M: Rob Springer <rspringer@google.com>
|
||||
M: John Joseph <jnjoseph@google.com>
|
||||
M: Ben Chan <benchan@chromium.org>
|
||||
S: Maintained
|
||||
F: drivers/staging/gasket/
|
||||
|
||||
GCOV BASED KERNEL PROFILING
|
||||
M: Peter Oberparleiter <oberpar@linux.ibm.com>
|
||||
S: Maintained
|
||||
@ -6064,6 +6087,12 @@ F: drivers/base/power/domain*.c
|
||||
F: include/linux/pm_domain.h
|
||||
F: Documentation/devicetree/bindings/power/power_domain.txt
|
||||
|
||||
GENERIC RESISTIVE TOUCHSCREEN ADC DRIVER
|
||||
M: Eugen Hristev <eugen.hristev@microchip.com>
|
||||
L: linux-input@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/input/touchscreen/resistive-adc-touch.c
|
||||
|
||||
GENERIC UIO DRIVER FOR PCI DEVICES
|
||||
M: "Michael S. Tsirkin" <mst@redhat.com>
|
||||
L: kvm@vger.kernel.org
|
||||
@ -6199,7 +6228,7 @@ F: drivers/staging/greybus/bootrom.c
|
||||
F: drivers/staging/greybus/firmware.h
|
||||
F: drivers/staging/greybus/fw-core.c
|
||||
F: drivers/staging/greybus/fw-download.c
|
||||
F: drivers/staging/greybus/fw-managament.c
|
||||
F: drivers/staging/greybus/fw-management.c
|
||||
F: drivers/staging/greybus/greybus_authentication.h
|
||||
F: drivers/staging/greybus/greybus_firmware.h
|
||||
F: drivers/staging/greybus/hid.c
|
||||
@ -6208,12 +6237,10 @@ F: drivers/staging/greybus/spi.c
|
||||
F: drivers/staging/greybus/spilib.c
|
||||
F: drivers/staging/greybus/spilib.h
|
||||
|
||||
GREYBUS LOOPBACK/TIME PROTOCOLS DRIVERS
|
||||
GREYBUS LOOPBACK DRIVER
|
||||
M: Bryan O'Donoghue <pure.logic@nexus-software.ie>
|
||||
S: Maintained
|
||||
F: drivers/staging/greybus/loopback.c
|
||||
F: drivers/staging/greybus/timesync.c
|
||||
F: drivers/staging/greybus/timesync_platform.c
|
||||
|
||||
GREYBUS PLATFORM DRIVERS
|
||||
M: Vaibhav Hiremath <hvaibhav.linux@gmail.com>
|
||||
@ -13619,6 +13646,13 @@ M: H Hartley Sweeten <hsweeten@visionengravers.com>
|
||||
S: Odd Fixes
|
||||
F: drivers/staging/comedi/
|
||||
|
||||
STAGING - EROFS FILE SYSTEM
|
||||
M: Gao Xiang <gaoxiang25@huawei.com>
|
||||
M: Chao Yu <yuchao0@huawei.com>
|
||||
L: linux-erofs@lists.ozlabs.org
|
||||
S: Maintained
|
||||
F: drivers/staging/erofs/
|
||||
|
||||
STAGING - FLARION FT1000 DRIVERS
|
||||
M: Marek Belisko <marek.belisko@gmail.com>
|
||||
S: Odd Fixes
|
||||
|
@ -40,7 +40,7 @@ config ADXL345_I2C
|
||||
select REGMAP_I2C
|
||||
help
|
||||
Say Y here if you want to build support for the Analog Devices
|
||||
ADXL345 3-axis digital accelerometer.
|
||||
ADXL345 or ADXL375 3-axis digital accelerometer.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called adxl345_i2c and you will also get adxl345_core
|
||||
@ -54,7 +54,7 @@ config ADXL345_SPI
|
||||
select REGMAP_SPI
|
||||
help
|
||||
Say Y here if you want to build support for the Analog Devices
|
||||
ADXL345 3-axis digital accelerometer.
|
||||
ADXL345 or ADXL375 3-axis digital accelerometer.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called adxl345_spi and you will also get adxl345_core
|
||||
|
@ -11,8 +11,13 @@
|
||||
#ifndef _ADXL345_H_
|
||||
#define _ADXL345_H_
|
||||
|
||||
enum adxl345_device_type {
|
||||
ADXL345,
|
||||
ADXL375,
|
||||
};
|
||||
|
||||
int adxl345_core_probe(struct device *dev, struct regmap *regmap,
|
||||
const char *name);
|
||||
enum adxl345_device_type type, const char *name);
|
||||
int adxl345_core_remove(struct device *dev);
|
||||
|
||||
#endif /* _ADXL345_H_ */
|
||||
|
@ -6,21 +6,35 @@
|
||||
* This file is subject to the terms and conditions of version 2 of
|
||||
* the GNU General Public License. See the file COPYING in the main
|
||||
* directory of this archive for more details.
|
||||
*
|
||||
* Datasheet: http://www.analog.com/media/en/technical-documentation/data-sheets/ADXL345.pdf
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
#include "adxl345.h"
|
||||
|
||||
#define ADXL345_REG_DEVID 0x00
|
||||
#define ADXL345_REG_OFSX 0x1e
|
||||
#define ADXL345_REG_OFSY 0x1f
|
||||
#define ADXL345_REG_OFSZ 0x20
|
||||
#define ADXL345_REG_OFS_AXIS(index) (ADXL345_REG_OFSX + (index))
|
||||
#define ADXL345_REG_BW_RATE 0x2C
|
||||
#define ADXL345_REG_POWER_CTL 0x2D
|
||||
#define ADXL345_REG_DATA_FORMAT 0x31
|
||||
#define ADXL345_REG_DATAX0 0x32
|
||||
#define ADXL345_REG_DATAY0 0x34
|
||||
#define ADXL345_REG_DATAZ0 0x36
|
||||
#define ADXL345_REG_DATA_AXIS(index) \
|
||||
(ADXL345_REG_DATAX0 + (index) * sizeof(__le16))
|
||||
|
||||
#define ADXL345_BW_RATE GENMASK(3, 0)
|
||||
#define ADXL345_BASE_RATE_NANO_HZ 97656250LL
|
||||
#define NHZ_PER_HZ 1000000000LL
|
||||
|
||||
#define ADXL345_POWER_CTL_MEASURE BIT(3)
|
||||
#define ADXL345_POWER_CTL_STANDBY 0x00
|
||||
@ -42,24 +56,33 @@
|
||||
*/
|
||||
static const int adxl345_uscale = 38300;
|
||||
|
||||
/*
|
||||
* The Datasheet lists a resolution of Resolution is ~49 mg per LSB. That's
|
||||
* ~480mm/s**2 per LSB.
|
||||
*/
|
||||
static const int adxl375_uscale = 480000;
|
||||
|
||||
struct adxl345_data {
|
||||
struct regmap *regmap;
|
||||
u8 data_range;
|
||||
enum adxl345_device_type type;
|
||||
};
|
||||
|
||||
#define ADXL345_CHANNEL(reg, axis) { \
|
||||
#define ADXL345_CHANNEL(index, axis) { \
|
||||
.type = IIO_ACCEL, \
|
||||
.modified = 1, \
|
||||
.channel2 = IIO_MOD_##axis, \
|
||||
.address = reg, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.address = index, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_CALIBBIAS), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ), \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec adxl345_channels[] = {
|
||||
ADXL345_CHANNEL(ADXL345_REG_DATAX0, X),
|
||||
ADXL345_CHANNEL(ADXL345_REG_DATAY0, Y),
|
||||
ADXL345_CHANNEL(ADXL345_REG_DATAZ0, Z),
|
||||
ADXL345_CHANNEL(0, X),
|
||||
ADXL345_CHANNEL(1, Y),
|
||||
ADXL345_CHANNEL(2, Z),
|
||||
};
|
||||
|
||||
static int adxl345_read_raw(struct iio_dev *indio_dev,
|
||||
@ -67,7 +90,9 @@ static int adxl345_read_raw(struct iio_dev *indio_dev,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct adxl345_data *data = iio_priv(indio_dev);
|
||||
__le16 regval;
|
||||
__le16 accel;
|
||||
long long samp_freq_nhz;
|
||||
unsigned int regval;
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
@ -77,29 +102,117 @@ static int adxl345_read_raw(struct iio_dev *indio_dev,
|
||||
* ADXL345_REG_DATA(X0/Y0/Z0) contain the least significant byte
|
||||
* and ADXL345_REG_DATA(X0/Y0/Z0) + 1 the most significant byte
|
||||
*/
|
||||
ret = regmap_bulk_read(data->regmap, chan->address, ®val,
|
||||
sizeof(regval));
|
||||
ret = regmap_bulk_read(data->regmap,
|
||||
ADXL345_REG_DATA_AXIS(chan->address),
|
||||
&accel, sizeof(accel));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = sign_extend32(le16_to_cpu(regval), 12);
|
||||
*val = sign_extend32(le16_to_cpu(accel), 12);
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = 0;
|
||||
*val2 = adxl345_uscale;
|
||||
switch (data->type) {
|
||||
case ADXL345:
|
||||
*val2 = adxl345_uscale;
|
||||
break;
|
||||
case ADXL375:
|
||||
*val2 = adxl375_uscale;
|
||||
break;
|
||||
}
|
||||
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
ret = regmap_read(data->regmap,
|
||||
ADXL345_REG_OFS_AXIS(chan->address), ®val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
/*
|
||||
* 8-bit resolution at +/- 2g, that is 4x accel data scale
|
||||
* factor
|
||||
*/
|
||||
*val = sign_extend32(regval, 7) * 4;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
ret = regmap_read(data->regmap, ADXL345_REG_BW_RATE, ®val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
samp_freq_nhz = ADXL345_BASE_RATE_NANO_HZ <<
|
||||
(regval & ADXL345_BW_RATE);
|
||||
*val = div_s64_rem(samp_freq_nhz, NHZ_PER_HZ, val2);
|
||||
|
||||
return IIO_VAL_INT_PLUS_NANO;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int adxl345_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
struct adxl345_data *data = iio_priv(indio_dev);
|
||||
s64 n;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
/*
|
||||
* 8-bit resolution at +/- 2g, that is 4x accel data scale
|
||||
* factor
|
||||
*/
|
||||
return regmap_write(data->regmap,
|
||||
ADXL345_REG_OFS_AXIS(chan->address),
|
||||
val / 4);
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
n = div_s64(val * NHZ_PER_HZ + val2, ADXL345_BASE_RATE_NANO_HZ);
|
||||
|
||||
return regmap_update_bits(data->regmap, ADXL345_REG_BW_RATE,
|
||||
ADXL345_BW_RATE,
|
||||
clamp_val(ilog2(n), 0,
|
||||
ADXL345_BW_RATE));
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int adxl345_write_raw_get_fmt(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
long mask)
|
||||
{
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
return IIO_VAL_INT_PLUS_NANO;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static IIO_CONST_ATTR_SAMP_FREQ_AVAIL(
|
||||
"0.09765625 0.1953125 0.390625 0.78125 1.5625 3.125 6.25 12.5 25 50 100 200 400 800 1600 3200"
|
||||
);
|
||||
|
||||
static struct attribute *adxl345_attrs[] = {
|
||||
&iio_const_attr_sampling_frequency_available.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group adxl345_attrs_group = {
|
||||
.attrs = adxl345_attrs,
|
||||
};
|
||||
|
||||
static const struct iio_info adxl345_info = {
|
||||
.attrs = &adxl345_attrs_group,
|
||||
.read_raw = adxl345_read_raw,
|
||||
.write_raw = adxl345_write_raw,
|
||||
.write_raw_get_fmt = adxl345_write_raw_get_fmt,
|
||||
};
|
||||
|
||||
int adxl345_core_probe(struct device *dev, struct regmap *regmap,
|
||||
const char *name)
|
||||
enum adxl345_device_type type, const char *name)
|
||||
{
|
||||
struct adxl345_data *data;
|
||||
struct iio_dev *indio_dev;
|
||||
@ -125,6 +238,7 @@ int adxl345_core_probe(struct device *dev, struct regmap *regmap,
|
||||
data = iio_priv(indio_dev);
|
||||
dev_set_drvdata(dev, indio_dev);
|
||||
data->regmap = regmap;
|
||||
data->type = type;
|
||||
/* Enable full-resolution mode */
|
||||
data->data_range = ADXL345_DATA_FORMAT_FULL_RES;
|
||||
|
||||
|
@ -34,7 +34,8 @@ static int adxl345_i2c_probe(struct i2c_client *client,
|
||||
return PTR_ERR(regmap);
|
||||
}
|
||||
|
||||
return adxl345_core_probe(&client->dev, regmap, id ? id->name : NULL);
|
||||
return adxl345_core_probe(&client->dev, regmap, id->driver_data,
|
||||
id ? id->name : NULL);
|
||||
}
|
||||
|
||||
static int adxl345_i2c_remove(struct i2c_client *client)
|
||||
@ -43,7 +44,8 @@ static int adxl345_i2c_remove(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id adxl345_i2c_id[] = {
|
||||
{ "adxl345", 0 },
|
||||
{ "adxl345", ADXL345 },
|
||||
{ "adxl375", ADXL375 },
|
||||
{ }
|
||||
};
|
||||
|
||||
@ -51,6 +53,7 @@ MODULE_DEVICE_TABLE(i2c, adxl345_i2c_id);
|
||||
|
||||
static const struct of_device_id adxl345_of_match[] = {
|
||||
{ .compatible = "adi,adxl345" },
|
||||
{ .compatible = "adi,adxl375" },
|
||||
{ },
|
||||
};
|
||||
|
||||
|
@ -42,7 +42,7 @@ static int adxl345_spi_probe(struct spi_device *spi)
|
||||
return PTR_ERR(regmap);
|
||||
}
|
||||
|
||||
return adxl345_core_probe(&spi->dev, regmap, id->name);
|
||||
return adxl345_core_probe(&spi->dev, regmap, id->driver_data, id->name);
|
||||
}
|
||||
|
||||
static int adxl345_spi_remove(struct spi_device *spi)
|
||||
@ -51,7 +51,8 @@ static int adxl345_spi_remove(struct spi_device *spi)
|
||||
}
|
||||
|
||||
static const struct spi_device_id adxl345_spi_id[] = {
|
||||
{ "adxl345", 0 },
|
||||
{ "adxl345", ADXL345 },
|
||||
{ "adxl375", ADXL375 },
|
||||
{ }
|
||||
};
|
||||
|
||||
@ -59,6 +60,7 @@ MODULE_DEVICE_TABLE(spi, adxl345_spi_id);
|
||||
|
||||
static const struct of_device_id adxl345_of_match[] = {
|
||||
{ .compatible = "adi,adxl345" },
|
||||
{ .compatible = "adi,adxl375" },
|
||||
{ },
|
||||
};
|
||||
|
||||
|
@ -1547,6 +1547,7 @@ static int mma8452_probe(struct i2c_client *client,
|
||||
case FXLS8471_DEVICE_ID:
|
||||
if (ret == data->chip_info->chip_id)
|
||||
break;
|
||||
/* else: fall through */
|
||||
default:
|
||||
return -ENODEV;
|
||||
}
|
||||
|
@ -797,6 +797,7 @@ static int sca3000_write_raw(struct iio_dev *indio_dev,
|
||||
mutex_lock(&st->lock);
|
||||
ret = sca3000_write_3db_freq(st, val);
|
||||
mutex_unlock(&st->lock);
|
||||
return ret;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -14,8 +14,8 @@
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/property.h>
|
||||
|
||||
#include <linux/iio/common/st_sensors.h>
|
||||
#include <linux/iio/common/st_sensors_i2c.h>
|
||||
#include "st_accel.h"
|
||||
|
||||
@ -107,8 +107,8 @@ MODULE_DEVICE_TABLE(of, st_accel_of_match);
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
static const struct acpi_device_id st_accel_acpi_match[] = {
|
||||
{"SMO8840", LNG2DM},
|
||||
{"SMO8A90", LNG2DM},
|
||||
{"SMO8840", (kernel_ulong_t)LNG2DM_ACCEL_DEV_NAME},
|
||||
{"SMO8A90", (kernel_ulong_t)LNG2DM_ACCEL_DEV_NAME},
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, st_accel_acpi_match);
|
||||
@ -117,33 +117,33 @@ MODULE_DEVICE_TABLE(acpi, st_accel_acpi_match);
|
||||
#endif
|
||||
|
||||
static const struct i2c_device_id st_accel_id_table[] = {
|
||||
{ LSM303DLH_ACCEL_DEV_NAME, LSM303DLH },
|
||||
{ LSM303DLHC_ACCEL_DEV_NAME, LSM303DLHC },
|
||||
{ LIS3DH_ACCEL_DEV_NAME, LIS3DH },
|
||||
{ LSM330D_ACCEL_DEV_NAME, LSM330D },
|
||||
{ LSM330DL_ACCEL_DEV_NAME, LSM330DL },
|
||||
{ LSM330DLC_ACCEL_DEV_NAME, LSM330DLC },
|
||||
{ LIS331DLH_ACCEL_DEV_NAME, LIS331DLH },
|
||||
{ LSM303DL_ACCEL_DEV_NAME, LSM303DL },
|
||||
{ LSM303DLM_ACCEL_DEV_NAME, LSM303DLM },
|
||||
{ LSM330_ACCEL_DEV_NAME, LSM330 },
|
||||
{ LSM303AGR_ACCEL_DEV_NAME, LSM303AGR },
|
||||
{ LIS2DH12_ACCEL_DEV_NAME, LIS2DH12 },
|
||||
{ LIS3L02DQ_ACCEL_DEV_NAME, LIS3L02DQ },
|
||||
{ LNG2DM_ACCEL_DEV_NAME, LNG2DM },
|
||||
{ H3LIS331DL_ACCEL_DEV_NAME, H3LIS331DL },
|
||||
{ LIS331DL_ACCEL_DEV_NAME, LIS331DL },
|
||||
{ LIS3LV02DL_ACCEL_DEV_NAME, LIS3LV02DL },
|
||||
{ LIS2DW12_ACCEL_DEV_NAME, LIS2DW12 },
|
||||
{ LSM303DLH_ACCEL_DEV_NAME },
|
||||
{ LSM303DLHC_ACCEL_DEV_NAME },
|
||||
{ LIS3DH_ACCEL_DEV_NAME },
|
||||
{ LSM330D_ACCEL_DEV_NAME },
|
||||
{ LSM330DL_ACCEL_DEV_NAME },
|
||||
{ LSM330DLC_ACCEL_DEV_NAME },
|
||||
{ LIS331DLH_ACCEL_DEV_NAME },
|
||||
{ LSM303DL_ACCEL_DEV_NAME },
|
||||
{ LSM303DLM_ACCEL_DEV_NAME },
|
||||
{ LSM330_ACCEL_DEV_NAME },
|
||||
{ LSM303AGR_ACCEL_DEV_NAME },
|
||||
{ LIS2DH12_ACCEL_DEV_NAME },
|
||||
{ LIS3L02DQ_ACCEL_DEV_NAME },
|
||||
{ LNG2DM_ACCEL_DEV_NAME },
|
||||
{ H3LIS331DL_ACCEL_DEV_NAME },
|
||||
{ LIS331DL_ACCEL_DEV_NAME },
|
||||
{ LIS3LV02DL_ACCEL_DEV_NAME },
|
||||
{ LIS2DW12_ACCEL_DEV_NAME },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, st_accel_id_table);
|
||||
|
||||
static int st_accel_i2c_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
static int st_accel_i2c_probe(struct i2c_client *client)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct st_sensor_data *adata;
|
||||
const char *match;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*adata));
|
||||
@ -152,19 +152,9 @@ static int st_accel_i2c_probe(struct i2c_client *client,
|
||||
|
||||
adata = iio_priv(indio_dev);
|
||||
|
||||
if (client->dev.of_node) {
|
||||
st_sensors_of_name_probe(&client->dev, st_accel_of_match,
|
||||
client->name, sizeof(client->name));
|
||||
} else if (ACPI_HANDLE(&client->dev)) {
|
||||
ret = st_sensors_match_acpi_device(&client->dev);
|
||||
if ((ret < 0) || (ret >= ST_ACCEL_MAX))
|
||||
return -ENODEV;
|
||||
|
||||
strlcpy(client->name, st_accel_id_table[ret].name,
|
||||
sizeof(client->name));
|
||||
} else if (!id)
|
||||
return -ENODEV;
|
||||
|
||||
match = device_get_match_data(&client->dev);
|
||||
if (match)
|
||||
strlcpy(client->name, match, sizeof(client->name));
|
||||
|
||||
st_sensors_i2c_configure(indio_dev, client, adata);
|
||||
|
||||
@ -188,7 +178,7 @@ static struct i2c_driver st_accel_driver = {
|
||||
.of_match_table = of_match_ptr(st_accel_of_match),
|
||||
.acpi_match_table = ACPI_PTR(st_accel_acpi_match),
|
||||
},
|
||||
.probe = st_accel_i2c_probe,
|
||||
.probe_new = st_accel_i2c_probe,
|
||||
.remove = st_accel_i2c_remove,
|
||||
.id_table = st_accel_id_table,
|
||||
};
|
||||
|
@ -157,7 +157,6 @@ config AT91_SAMA5D2_ADC
|
||||
tristate "Atmel AT91 SAMA5D2 ADC"
|
||||
depends on ARCH_AT91 || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
depends on HAS_DMA
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
@ -621,6 +620,16 @@ config ROCKCHIP_SARADC
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called rockchip_saradc.
|
||||
|
||||
config SC27XX_ADC
|
||||
tristate "Spreadtrum SC27xx series PMICs ADC"
|
||||
depends on MFD_SC27XX_PMIC || COMPILE_TEST
|
||||
help
|
||||
Say yes here to build support for the integrated ADC inside the
|
||||
Spreadtrum SC27xx series PMICs.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called sc27xx_adc.
|
||||
|
||||
config SPEAR_ADC
|
||||
tristate "ST SPEAr ADC"
|
||||
depends on PLAT_SPEAR || COMPILE_TEST
|
||||
@ -647,7 +656,6 @@ config SD_ADC_MODULATOR
|
||||
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
|
||||
@ -717,6 +725,7 @@ config SUN4I_GPADC
|
||||
depends on IIO
|
||||
depends on MFD_SUN4I_GPADC || MACH_SUN8I
|
||||
depends on THERMAL || !THERMAL_OF
|
||||
select REGMAP_IRQ
|
||||
help
|
||||
Say yes here to build support for Allwinner (A10, A13 and A31) SoCs
|
||||
GPADC. This ADC provides 4 channels which can be used as an ADC or as
|
||||
|
@ -59,6 +59,7 @@ obj-$(CONFIG_QCOM_SPMI_VADC) += qcom-spmi-vadc.o
|
||||
obj-$(CONFIG_QCOM_PM8XXX_XOADC) += qcom-pm8xxx-xoadc.o
|
||||
obj-$(CONFIG_RCAR_GYRO_ADC) += rcar-gyroadc.o
|
||||
obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
|
||||
obj-$(CONFIG_SC27XX_ADC) += sc27xx_adc.o
|
||||
obj-$(CONFIG_SPEAR_ADC) += spear_adc.o
|
||||
obj-$(CONFIG_STX104) += stx104.o
|
||||
obj-$(CONFIG_SUN4I_GPADC) += sun4i-gpadc-iio.o
|
||||
|
@ -209,6 +209,7 @@ static int ad_sd_calibrate(struct ad_sigma_delta *sigma_delta,
|
||||
unsigned int mode, unsigned int channel)
|
||||
{
|
||||
int ret;
|
||||
unsigned long timeout;
|
||||
|
||||
ret = ad_sigma_delta_set_channel(sigma_delta, channel);
|
||||
if (ret)
|
||||
@ -224,8 +225,8 @@ static int ad_sd_calibrate(struct ad_sigma_delta *sigma_delta,
|
||||
|
||||
sigma_delta->irq_dis = false;
|
||||
enable_irq(sigma_delta->spi->irq);
|
||||
ret = wait_for_completion_timeout(&sigma_delta->completion, 2*HZ);
|
||||
if (ret == 0) {
|
||||
timeout = wait_for_completion_timeout(&sigma_delta->completion, 2 * HZ);
|
||||
if (timeout == 0) {
|
||||
sigma_delta->irq_dis = true;
|
||||
disable_irq_nosync(sigma_delta->spi->irq);
|
||||
ret = -EIO;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -97,6 +97,14 @@ struct hx711_data {
|
||||
* 2x32-bit channel + 64-bit timestamp
|
||||
*/
|
||||
u32 buffer[4];
|
||||
/*
|
||||
* delay after a rising edge on SCK until the data is ready DOUT
|
||||
* this is dependent on the hx711 where the datasheet tells a
|
||||
* maximum value of 100 ns
|
||||
* but also on potential parasitic capacities on the wiring
|
||||
*/
|
||||
u32 data_ready_delay_ns;
|
||||
u32 clock_frequency;
|
||||
};
|
||||
|
||||
static int hx711_cycle(struct hx711_data *hx711_data)
|
||||
@ -110,6 +118,14 @@ static int hx711_cycle(struct hx711_data *hx711_data)
|
||||
*/
|
||||
preempt_disable();
|
||||
gpiod_set_value(hx711_data->gpiod_pd_sck, 1);
|
||||
|
||||
/*
|
||||
* wait until DOUT is ready
|
||||
* it turned out that parasitic capacities are extending the time
|
||||
* until DOUT has reached it's value
|
||||
*/
|
||||
ndelay(hx711_data->data_ready_delay_ns);
|
||||
|
||||
val = gpiod_get_value(hx711_data->gpiod_dout);
|
||||
/*
|
||||
* here we are not waiting for 0.2 us as suggested by the datasheet,
|
||||
@ -120,6 +136,12 @@ static int hx711_cycle(struct hx711_data *hx711_data)
|
||||
gpiod_set_value(hx711_data->gpiod_pd_sck, 0);
|
||||
preempt_enable();
|
||||
|
||||
/*
|
||||
* make it a square wave for addressing cases with capacitance on
|
||||
* PC_SCK
|
||||
*/
|
||||
ndelay(hx711_data->data_ready_delay_ns);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
@ -458,6 +480,7 @@ static const struct iio_chan_spec hx711_chan_spec[] = {
|
||||
static int hx711_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct hx711_data *hx711_data;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret;
|
||||
@ -530,6 +553,22 @@ static int hx711_probe(struct platform_device *pdev)
|
||||
hx711_data->gain_set = 128;
|
||||
hx711_data->gain_chan_a = 128;
|
||||
|
||||
hx711_data->clock_frequency = 400000;
|
||||
ret = of_property_read_u32(np, "clock-frequency",
|
||||
&hx711_data->clock_frequency);
|
||||
|
||||
/*
|
||||
* datasheet says the high level of PD_SCK has a maximum duration
|
||||
* of 50 microseconds
|
||||
*/
|
||||
if (hx711_data->clock_frequency < 20000) {
|
||||
dev_warn(dev, "clock-frequency too low - assuming 400 kHz\n");
|
||||
hx711_data->clock_frequency = 400000;
|
||||
}
|
||||
|
||||
hx711_data->data_ready_delay_ns =
|
||||
1000000000 / hx711_data->clock_frequency;
|
||||
|
||||
platform_set_drvdata(pdev, indio_dev);
|
||||
|
||||
indio_dev->name = "hx711";
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/sched/task.h>
|
||||
#include <linux/util_macros.h>
|
||||
|
||||
#include <linux/platform_data/ina2xx.h>
|
||||
@ -826,6 +827,7 @@ static int ina2xx_buffer_enable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct ina2xx_chip_info *chip = iio_priv(indio_dev);
|
||||
unsigned int sampling_us = SAMPLING_PERIOD(chip);
|
||||
struct task_struct *task;
|
||||
|
||||
dev_dbg(&indio_dev->dev, "Enabling buffer w/ scan_mask %02x, freq = %d, avg =%u\n",
|
||||
(unsigned int)(*indio_dev->active_scan_mask),
|
||||
@ -835,11 +837,17 @@ static int ina2xx_buffer_enable(struct iio_dev *indio_dev)
|
||||
dev_dbg(&indio_dev->dev, "Async readout mode: %d\n",
|
||||
chip->allow_async_readout);
|
||||
|
||||
chip->task = kthread_run(ina2xx_capture_thread, (void *)indio_dev,
|
||||
"%s:%d-%uus", indio_dev->name, indio_dev->id,
|
||||
sampling_us);
|
||||
task = kthread_create(ina2xx_capture_thread, (void *)indio_dev,
|
||||
"%s:%d-%uus", indio_dev->name, indio_dev->id,
|
||||
sampling_us);
|
||||
if (IS_ERR(task))
|
||||
return PTR_ERR(task);
|
||||
|
||||
return PTR_ERR_OR_ZERO(chip->task);
|
||||
get_task_struct(task);
|
||||
wake_up_process(task);
|
||||
chip->task = task;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ina2xx_buffer_disable(struct iio_dev *indio_dev)
|
||||
@ -848,6 +856,7 @@ static int ina2xx_buffer_disable(struct iio_dev *indio_dev)
|
||||
|
||||
if (chip->task) {
|
||||
kthread_stop(chip->task);
|
||||
put_task_struct(chip->task);
|
||||
chip->task = NULL;
|
||||
}
|
||||
|
||||
|
@ -1577,7 +1577,6 @@ static int max1363_probe(struct i2c_client *client,
|
||||
struct max1363_state *st;
|
||||
struct iio_dev *indio_dev;
|
||||
struct regulator *vref;
|
||||
const struct of_device_id *match;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev,
|
||||
sizeof(struct max1363_state));
|
||||
@ -1604,11 +1603,8 @@ static int max1363_probe(struct i2c_client *client,
|
||||
/* this is only used for device removal purposes */
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
|
||||
match = of_match_device(of_match_ptr(max1363_of_match),
|
||||
&client->dev);
|
||||
if (match)
|
||||
st->chip_info = of_device_get_match_data(&client->dev);
|
||||
else
|
||||
st->chip_info = of_device_get_match_data(&client->dev);
|
||||
if (!st->chip_info)
|
||||
st->chip_info = &max1363_chip_info_tbl[id->driver_data];
|
||||
st->client = client;
|
||||
|
||||
|
@ -922,6 +922,11 @@ static const struct meson_sar_adc_data meson_sar_adc_meson8b_data = {
|
||||
.name = "meson-meson8b-saradc",
|
||||
};
|
||||
|
||||
static const struct meson_sar_adc_data meson_sar_adc_meson8m2_data = {
|
||||
.param = &meson_sar_adc_meson8_param,
|
||||
.name = "meson-meson8m2-saradc",
|
||||
};
|
||||
|
||||
static const struct meson_sar_adc_data meson_sar_adc_gxbb_data = {
|
||||
.param = &meson_sar_adc_gxbb_param,
|
||||
.name = "meson-gxbb-saradc",
|
||||
@ -951,6 +956,10 @@ static const struct of_device_id meson_sar_adc_of_match[] = {
|
||||
.compatible = "amlogic,meson8b-saradc",
|
||||
.data = &meson_sar_adc_meson8b_data,
|
||||
},
|
||||
{
|
||||
.compatible = "amlogic,meson8m2-saradc",
|
||||
.data = &meson_sar_adc_meson8m2_data,
|
||||
},
|
||||
{
|
||||
.compatible = "amlogic,meson-gxbb-saradc",
|
||||
.data = &meson_sar_adc_gxbb_data,
|
||||
|
522
drivers/iio/adc/sc27xx_adc.c
Normal file
522
drivers/iio/adc/sc27xx_adc.c
Normal file
@ -0,0 +1,522 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (C) 2018 Spreadtrum Communications Inc.
|
||||
|
||||
#include <linux/hwspinlock.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
/* PMIC global registers definition */
|
||||
#define SC27XX_MODULE_EN 0xc08
|
||||
#define SC27XX_MODULE_ADC_EN BIT(5)
|
||||
#define SC27XX_ARM_CLK_EN 0xc10
|
||||
#define SC27XX_CLK_ADC_EN BIT(5)
|
||||
#define SC27XX_CLK_ADC_CLK_EN BIT(6)
|
||||
|
||||
/* ADC controller registers definition */
|
||||
#define SC27XX_ADC_CTL 0x0
|
||||
#define SC27XX_ADC_CH_CFG 0x4
|
||||
#define SC27XX_ADC_DATA 0x4c
|
||||
#define SC27XX_ADC_INT_EN 0x50
|
||||
#define SC27XX_ADC_INT_CLR 0x54
|
||||
#define SC27XX_ADC_INT_STS 0x58
|
||||
#define SC27XX_ADC_INT_RAW 0x5c
|
||||
|
||||
/* Bits and mask definition for SC27XX_ADC_CTL register */
|
||||
#define SC27XX_ADC_EN BIT(0)
|
||||
#define SC27XX_ADC_CHN_RUN BIT(1)
|
||||
#define SC27XX_ADC_12BIT_MODE BIT(2)
|
||||
#define SC27XX_ADC_RUN_NUM_MASK GENMASK(7, 4)
|
||||
#define SC27XX_ADC_RUN_NUM_SHIFT 4
|
||||
|
||||
/* Bits and mask definition for SC27XX_ADC_CH_CFG register */
|
||||
#define SC27XX_ADC_CHN_ID_MASK GENMASK(4, 0)
|
||||
#define SC27XX_ADC_SCALE_MASK GENMASK(10, 8)
|
||||
#define SC27XX_ADC_SCALE_SHIFT 8
|
||||
|
||||
/* Bits definitions for SC27XX_ADC_INT_EN registers */
|
||||
#define SC27XX_ADC_IRQ_EN BIT(0)
|
||||
|
||||
/* Bits definitions for SC27XX_ADC_INT_CLR registers */
|
||||
#define SC27XX_ADC_IRQ_CLR BIT(0)
|
||||
|
||||
/* Mask definition for SC27XX_ADC_DATA register */
|
||||
#define SC27XX_ADC_DATA_MASK GENMASK(11, 0)
|
||||
|
||||
/* Timeout (ms) for the trylock of hardware spinlocks */
|
||||
#define SC27XX_ADC_HWLOCK_TIMEOUT 5000
|
||||
|
||||
/* Maximum ADC channel number */
|
||||
#define SC27XX_ADC_CHANNEL_MAX 32
|
||||
|
||||
/* ADC voltage ratio definition */
|
||||
#define SC27XX_VOLT_RATIO(n, d) \
|
||||
(((n) << SC27XX_RATIO_NUMERATOR_OFFSET) | (d))
|
||||
#define SC27XX_RATIO_NUMERATOR_OFFSET 16
|
||||
#define SC27XX_RATIO_DENOMINATOR_MASK GENMASK(15, 0)
|
||||
|
||||
struct sc27xx_adc_data {
|
||||
struct device *dev;
|
||||
struct regmap *regmap;
|
||||
/*
|
||||
* One hardware spinlock to synchronize between the multiple
|
||||
* subsystems which will access the unique ADC controller.
|
||||
*/
|
||||
struct hwspinlock *hwlock;
|
||||
struct completion completion;
|
||||
int channel_scale[SC27XX_ADC_CHANNEL_MAX];
|
||||
u32 base;
|
||||
int value;
|
||||
int irq;
|
||||
};
|
||||
|
||||
struct sc27xx_adc_linear_graph {
|
||||
int volt0;
|
||||
int adc0;
|
||||
int volt1;
|
||||
int adc1;
|
||||
};
|
||||
|
||||
/*
|
||||
* According to the datasheet, we can convert one ADC value to one voltage value
|
||||
* through 2 points in the linear graph. If the voltage is less than 1.2v, we
|
||||
* should use the small-scale graph, and if more than 1.2v, we should use the
|
||||
* big-scale graph.
|
||||
*/
|
||||
static const struct sc27xx_adc_linear_graph big_scale_graph = {
|
||||
4200, 3310,
|
||||
3600, 2832,
|
||||
};
|
||||
|
||||
static const struct sc27xx_adc_linear_graph small_scale_graph = {
|
||||
1000, 3413,
|
||||
100, 341,
|
||||
};
|
||||
|
||||
static int sc27xx_adc_get_ratio(int channel, int scale)
|
||||
{
|
||||
switch (channel) {
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
return scale ? SC27XX_VOLT_RATIO(400, 1025) :
|
||||
SC27XX_VOLT_RATIO(1, 1);
|
||||
case 5:
|
||||
return SC27XX_VOLT_RATIO(7, 29);
|
||||
case 6:
|
||||
return SC27XX_VOLT_RATIO(375, 9000);
|
||||
case 7:
|
||||
case 8:
|
||||
return scale ? SC27XX_VOLT_RATIO(100, 125) :
|
||||
SC27XX_VOLT_RATIO(1, 1);
|
||||
case 19:
|
||||
return SC27XX_VOLT_RATIO(1, 3);
|
||||
default:
|
||||
return SC27XX_VOLT_RATIO(1, 1);
|
||||
}
|
||||
return SC27XX_VOLT_RATIO(1, 1);
|
||||
}
|
||||
|
||||
static int sc27xx_adc_read(struct sc27xx_adc_data *data, int channel,
|
||||
int scale, int *val)
|
||||
{
|
||||
int ret;
|
||||
u32 tmp;
|
||||
|
||||
reinit_completion(&data->completion);
|
||||
|
||||
ret = hwspin_lock_timeout_raw(data->hwlock, SC27XX_ADC_HWLOCK_TIMEOUT);
|
||||
if (ret) {
|
||||
dev_err(data->dev, "timeout to get the hwspinlock\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regmap_update_bits(data->regmap, data->base + SC27XX_ADC_CTL,
|
||||
SC27XX_ADC_EN, SC27XX_ADC_EN);
|
||||
if (ret)
|
||||
goto unlock_adc;
|
||||
|
||||
/* Configure the channel id and scale */
|
||||
tmp = (scale << SC27XX_ADC_SCALE_SHIFT) & SC27XX_ADC_SCALE_MASK;
|
||||
tmp |= channel & SC27XX_ADC_CHN_ID_MASK;
|
||||
ret = regmap_update_bits(data->regmap, data->base + SC27XX_ADC_CH_CFG,
|
||||
SC27XX_ADC_CHN_ID_MASK | SC27XX_ADC_SCALE_MASK,
|
||||
tmp);
|
||||
if (ret)
|
||||
goto disable_adc;
|
||||
|
||||
/* Select 12bit conversion mode, and only sample 1 time */
|
||||
tmp = SC27XX_ADC_12BIT_MODE;
|
||||
tmp |= (0 << SC27XX_ADC_RUN_NUM_SHIFT) & SC27XX_ADC_RUN_NUM_MASK;
|
||||
ret = regmap_update_bits(data->regmap, data->base + SC27XX_ADC_CTL,
|
||||
SC27XX_ADC_RUN_NUM_MASK | SC27XX_ADC_12BIT_MODE,
|
||||
tmp);
|
||||
if (ret)
|
||||
goto disable_adc;
|
||||
|
||||
ret = regmap_update_bits(data->regmap, data->base + SC27XX_ADC_CTL,
|
||||
SC27XX_ADC_CHN_RUN, SC27XX_ADC_CHN_RUN);
|
||||
if (ret)
|
||||
goto disable_adc;
|
||||
|
||||
wait_for_completion(&data->completion);
|
||||
|
||||
disable_adc:
|
||||
regmap_update_bits(data->regmap, data->base + SC27XX_ADC_CTL,
|
||||
SC27XX_ADC_EN, 0);
|
||||
unlock_adc:
|
||||
hwspin_unlock_raw(data->hwlock);
|
||||
|
||||
if (!ret)
|
||||
*val = data->value;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static irqreturn_t sc27xx_adc_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct sc27xx_adc_data *data = dev_id;
|
||||
int ret;
|
||||
|
||||
ret = regmap_update_bits(data->regmap, data->base + SC27XX_ADC_INT_CLR,
|
||||
SC27XX_ADC_IRQ_CLR, SC27XX_ADC_IRQ_CLR);
|
||||
if (ret)
|
||||
return IRQ_RETVAL(ret);
|
||||
|
||||
ret = regmap_read(data->regmap, data->base + SC27XX_ADC_DATA,
|
||||
&data->value);
|
||||
if (ret)
|
||||
return IRQ_RETVAL(ret);
|
||||
|
||||
data->value &= SC27XX_ADC_DATA_MASK;
|
||||
complete(&data->completion);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void sc27xx_adc_volt_ratio(struct sc27xx_adc_data *data,
|
||||
int channel, int scale,
|
||||
u32 *div_numerator, u32 *div_denominator)
|
||||
{
|
||||
u32 ratio = sc27xx_adc_get_ratio(channel, scale);
|
||||
|
||||
*div_numerator = ratio >> SC27XX_RATIO_NUMERATOR_OFFSET;
|
||||
*div_denominator = ratio & SC27XX_RATIO_DENOMINATOR_MASK;
|
||||
}
|
||||
|
||||
static int sc27xx_adc_to_volt(const struct sc27xx_adc_linear_graph *graph,
|
||||
int raw_adc)
|
||||
{
|
||||
int tmp;
|
||||
|
||||
tmp = (graph->volt0 - graph->volt1) * (raw_adc - graph->adc1);
|
||||
tmp /= (graph->adc0 - graph->adc1);
|
||||
tmp += graph->volt1;
|
||||
|
||||
return tmp < 0 ? 0 : tmp;
|
||||
}
|
||||
|
||||
static int sc27xx_adc_convert_volt(struct sc27xx_adc_data *data, int channel,
|
||||
int scale, int raw_adc)
|
||||
{
|
||||
u32 numerator, denominator;
|
||||
u32 volt;
|
||||
|
||||
/*
|
||||
* Convert ADC values to voltage values according to the linear graph,
|
||||
* and channel 5 and channel 1 has been calibrated, so we can just
|
||||
* return the voltage values calculated by the linear graph. But other
|
||||
* channels need be calculated to the real voltage values with the
|
||||
* voltage ratio.
|
||||
*/
|
||||
switch (channel) {
|
||||
case 5:
|
||||
return sc27xx_adc_to_volt(&big_scale_graph, raw_adc);
|
||||
|
||||
case 1:
|
||||
return sc27xx_adc_to_volt(&small_scale_graph, raw_adc);
|
||||
|
||||
default:
|
||||
volt = sc27xx_adc_to_volt(&small_scale_graph, raw_adc);
|
||||
break;
|
||||
}
|
||||
|
||||
sc27xx_adc_volt_ratio(data, channel, scale, &numerator, &denominator);
|
||||
|
||||
return (volt * denominator + numerator / 2) / numerator;
|
||||
}
|
||||
|
||||
static int sc27xx_adc_read_processed(struct sc27xx_adc_data *data,
|
||||
int channel, int scale, int *val)
|
||||
{
|
||||
int ret, raw_adc;
|
||||
|
||||
ret = sc27xx_adc_read(data, channel, scale, &raw_adc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*val = sc27xx_adc_convert_volt(data, channel, scale, raw_adc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sc27xx_adc_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct sc27xx_adc_data *data = iio_priv(indio_dev);
|
||||
int scale = data->channel_scale[chan->channel];
|
||||
int ret, tmp;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_PROCESSED:
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
ret = sc27xx_adc_read_processed(data, chan->channel, scale,
|
||||
&tmp);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*val = tmp;
|
||||
return IIO_VAL_INT;
|
||||
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = scale;
|
||||
return IIO_VAL_INT;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int sc27xx_adc_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
struct sc27xx_adc_data *data = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
data->channel_scale[chan->channel] = val;
|
||||
return IIO_VAL_INT;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct iio_info sc27xx_info = {
|
||||
.read_raw = &sc27xx_adc_read_raw,
|
||||
.write_raw = &sc27xx_adc_write_raw,
|
||||
};
|
||||
|
||||
#define SC27XX_ADC_CHANNEL(index) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.channel = index, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) | \
|
||||
BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.datasheet_name = "CH##index", \
|
||||
.indexed = 1, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec sc27xx_channels[] = {
|
||||
SC27XX_ADC_CHANNEL(0),
|
||||
SC27XX_ADC_CHANNEL(1),
|
||||
SC27XX_ADC_CHANNEL(2),
|
||||
SC27XX_ADC_CHANNEL(3),
|
||||
SC27XX_ADC_CHANNEL(4),
|
||||
SC27XX_ADC_CHANNEL(5),
|
||||
SC27XX_ADC_CHANNEL(6),
|
||||
SC27XX_ADC_CHANNEL(7),
|
||||
SC27XX_ADC_CHANNEL(8),
|
||||
SC27XX_ADC_CHANNEL(9),
|
||||
SC27XX_ADC_CHANNEL(10),
|
||||
SC27XX_ADC_CHANNEL(11),
|
||||
SC27XX_ADC_CHANNEL(12),
|
||||
SC27XX_ADC_CHANNEL(13),
|
||||
SC27XX_ADC_CHANNEL(14),
|
||||
SC27XX_ADC_CHANNEL(15),
|
||||
SC27XX_ADC_CHANNEL(16),
|
||||
SC27XX_ADC_CHANNEL(17),
|
||||
SC27XX_ADC_CHANNEL(18),
|
||||
SC27XX_ADC_CHANNEL(19),
|
||||
SC27XX_ADC_CHANNEL(20),
|
||||
SC27XX_ADC_CHANNEL(21),
|
||||
SC27XX_ADC_CHANNEL(22),
|
||||
SC27XX_ADC_CHANNEL(23),
|
||||
SC27XX_ADC_CHANNEL(24),
|
||||
SC27XX_ADC_CHANNEL(25),
|
||||
SC27XX_ADC_CHANNEL(26),
|
||||
SC27XX_ADC_CHANNEL(27),
|
||||
SC27XX_ADC_CHANNEL(28),
|
||||
SC27XX_ADC_CHANNEL(29),
|
||||
SC27XX_ADC_CHANNEL(30),
|
||||
SC27XX_ADC_CHANNEL(31),
|
||||
};
|
||||
|
||||
static int sc27xx_adc_enable(struct sc27xx_adc_data *data)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = regmap_update_bits(data->regmap, SC27XX_MODULE_EN,
|
||||
SC27XX_MODULE_ADC_EN, SC27XX_MODULE_ADC_EN);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Enable ADC work clock and controller clock */
|
||||
ret = regmap_update_bits(data->regmap, SC27XX_ARM_CLK_EN,
|
||||
SC27XX_CLK_ADC_EN | SC27XX_CLK_ADC_CLK_EN,
|
||||
SC27XX_CLK_ADC_EN | SC27XX_CLK_ADC_CLK_EN);
|
||||
if (ret)
|
||||
goto disable_adc;
|
||||
|
||||
ret = regmap_update_bits(data->regmap, data->base + SC27XX_ADC_INT_EN,
|
||||
SC27XX_ADC_IRQ_EN, SC27XX_ADC_IRQ_EN);
|
||||
if (ret)
|
||||
goto disable_clk;
|
||||
|
||||
return 0;
|
||||
|
||||
disable_clk:
|
||||
regmap_update_bits(data->regmap, SC27XX_ARM_CLK_EN,
|
||||
SC27XX_CLK_ADC_EN | SC27XX_CLK_ADC_CLK_EN, 0);
|
||||
disable_adc:
|
||||
regmap_update_bits(data->regmap, SC27XX_MODULE_EN,
|
||||
SC27XX_MODULE_ADC_EN, 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void sc27xx_adc_disable(void *_data)
|
||||
{
|
||||
struct sc27xx_adc_data *data = _data;
|
||||
|
||||
regmap_update_bits(data->regmap, data->base + SC27XX_ADC_INT_EN,
|
||||
SC27XX_ADC_IRQ_EN, 0);
|
||||
|
||||
/* Disable ADC work clock and controller clock */
|
||||
regmap_update_bits(data->regmap, SC27XX_ARM_CLK_EN,
|
||||
SC27XX_CLK_ADC_EN | SC27XX_CLK_ADC_CLK_EN, 0);
|
||||
|
||||
regmap_update_bits(data->regmap, SC27XX_MODULE_EN,
|
||||
SC27XX_MODULE_ADC_EN, 0);
|
||||
}
|
||||
|
||||
static void sc27xx_adc_free_hwlock(void *_data)
|
||||
{
|
||||
struct hwspinlock *hwlock = _data;
|
||||
|
||||
hwspin_lock_free(hwlock);
|
||||
}
|
||||
|
||||
static int sc27xx_adc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct sc27xx_adc_data *sc27xx_data;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*sc27xx_data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
sc27xx_data = iio_priv(indio_dev);
|
||||
|
||||
sc27xx_data->regmap = dev_get_regmap(pdev->dev.parent, NULL);
|
||||
if (!sc27xx_data->regmap) {
|
||||
dev_err(&pdev->dev, "failed to get ADC regmap\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(np, "reg", &sc27xx_data->base);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to get ADC base address\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
sc27xx_data->irq = platform_get_irq(pdev, 0);
|
||||
if (sc27xx_data->irq < 0) {
|
||||
dev_err(&pdev->dev, "failed to get ADC irq number\n");
|
||||
return sc27xx_data->irq;
|
||||
}
|
||||
|
||||
ret = of_hwspin_lock_get_id(np, 0);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed to get hwspinlock id\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
sc27xx_data->hwlock = hwspin_lock_request_specific(ret);
|
||||
if (!sc27xx_data->hwlock) {
|
||||
dev_err(&pdev->dev, "failed to request hwspinlock\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
ret = devm_add_action(&pdev->dev, sc27xx_adc_free_hwlock,
|
||||
sc27xx_data->hwlock);
|
||||
if (ret) {
|
||||
sc27xx_adc_free_hwlock(sc27xx_data->hwlock);
|
||||
dev_err(&pdev->dev, "failed to add hwspinlock action\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
init_completion(&sc27xx_data->completion);
|
||||
sc27xx_data->dev = &pdev->dev;
|
||||
|
||||
ret = sc27xx_adc_enable(sc27xx_data);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to enable ADC module\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = devm_add_action(&pdev->dev, sc27xx_adc_disable, sc27xx_data);
|
||||
if (ret) {
|
||||
sc27xx_adc_disable(sc27xx_data);
|
||||
dev_err(&pdev->dev, "failed to add ADC disable action\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = devm_request_threaded_irq(&pdev->dev, sc27xx_data->irq, NULL,
|
||||
sc27xx_adc_isr, IRQF_ONESHOT,
|
||||
pdev->name, sc27xx_data);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to request ADC irq\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
indio_dev->dev.parent = &pdev->dev;
|
||||
indio_dev->name = dev_name(&pdev->dev);
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &sc27xx_info;
|
||||
indio_dev->channels = sc27xx_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(sc27xx_channels);
|
||||
ret = devm_iio_device_register(&pdev->dev, indio_dev);
|
||||
if (ret)
|
||||
dev_err(&pdev->dev, "could not register iio (ADC)");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct of_device_id sc27xx_adc_of_match[] = {
|
||||
{ .compatible = "sprd,sc2731-adc", },
|
||||
{ }
|
||||
};
|
||||
|
||||
static struct platform_driver sc27xx_adc_driver = {
|
||||
.probe = sc27xx_adc_probe,
|
||||
.driver = {
|
||||
.name = "sc27xx-adc",
|
||||
.of_match_table = sc27xx_adc_of_match,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(sc27xx_adc_driver);
|
||||
|
||||
MODULE_AUTHOR("Freeman Liu <freeman.liu@spreadtrum.com>");
|
||||
MODULE_DESCRIPTION("Spreadtrum SC27XX ADC Driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -1,3 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Texas Instruments ADS7950 SPI ADC driver
|
||||
*
|
||||
@ -10,15 +11,6 @@
|
||||
* And also on hwmon/ads79xx.c
|
||||
* Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/
|
||||
* Nishanth Menon
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation version 2.
|
||||
*
|
||||
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
|
||||
* kind, whether express or implied; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
@ -76,6 +68,9 @@ struct ti_ads7950_state {
|
||||
__be16 rx_buf[TI_ADS7950_MAX_CHAN + TI_ADS7950_TIMESTAMP_SIZE]
|
||||
____cacheline_aligned;
|
||||
__be16 tx_buf[TI_ADS7950_MAX_CHAN];
|
||||
__be16 single_tx;
|
||||
__be16 single_rx;
|
||||
|
||||
};
|
||||
|
||||
struct ti_ads7950_chip_info {
|
||||
@ -295,18 +290,26 @@ out:
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int ti_ads7950_scan_direct(struct ti_ads7950_state *st, unsigned int ch)
|
||||
static int ti_ads7950_scan_direct(struct iio_dev *indio_dev, unsigned int ch)
|
||||
{
|
||||
struct ti_ads7950_state *st = iio_priv(indio_dev);
|
||||
int ret, cmd;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
|
||||
cmd = TI_ADS7950_CR_WRITE | TI_ADS7950_CR_CHAN(ch) | st->settings;
|
||||
st->tx_buf[0] = cpu_to_be16(cmd);
|
||||
st->single_tx = cpu_to_be16(cmd);
|
||||
|
||||
ret = spi_sync(st->spi, &st->scan_single_msg);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto out;
|
||||
|
||||
return be16_to_cpu(st->rx_buf[0]);
|
||||
ret = be16_to_cpu(st->single_rx);
|
||||
|
||||
out:
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ti_ads7950_get_range(struct ti_ads7950_state *st)
|
||||
@ -338,13 +341,7 @@ static int ti_ads7950_read_raw(struct iio_dev *indio_dev,
|
||||
|
||||
switch (m) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
|
||||
ret = iio_device_claim_direct_mode(indio_dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = ti_ads7950_scan_direct(st, chan->address);
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
ret = ti_ads7950_scan_direct(indio_dev, chan->address);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@ -410,13 +407,13 @@ static int ti_ads7950_probe(struct spi_device *spi)
|
||||
* was read at the end of the first transfer.
|
||||
*/
|
||||
|
||||
st->scan_single_xfer[0].tx_buf = &st->tx_buf[0];
|
||||
st->scan_single_xfer[0].tx_buf = &st->single_tx;
|
||||
st->scan_single_xfer[0].len = 2;
|
||||
st->scan_single_xfer[0].cs_change = 1;
|
||||
st->scan_single_xfer[1].tx_buf = &st->tx_buf[0];
|
||||
st->scan_single_xfer[1].tx_buf = &st->single_tx;
|
||||
st->scan_single_xfer[1].len = 2;
|
||||
st->scan_single_xfer[1].cs_change = 1;
|
||||
st->scan_single_xfer[2].rx_buf = &st->rx_buf[0];
|
||||
st->scan_single_xfer[2].rx_buf = &st->single_rx;
|
||||
st->scan_single_xfer[2].len = 2;
|
||||
|
||||
spi_message_init_with_transfers(&st->scan_single_msg,
|
||||
|
@ -322,6 +322,7 @@ static irqreturn_t xadc_zynq_interrupt_handler(int irq, void *devid)
|
||||
|
||||
#define XADC_ZYNQ_TCK_RATE_MAX 50000000
|
||||
#define XADC_ZYNQ_IGAP_DEFAULT 20
|
||||
#define XADC_ZYNQ_PCAP_RATE_MAX 200000000
|
||||
|
||||
static int xadc_zynq_setup(struct platform_device *pdev,
|
||||
struct iio_dev *indio_dev, int irq)
|
||||
@ -332,6 +333,7 @@ static int xadc_zynq_setup(struct platform_device *pdev,
|
||||
unsigned int div;
|
||||
unsigned int igap;
|
||||
unsigned int tck_rate;
|
||||
int ret;
|
||||
|
||||
/* TODO: Figure out how to make igap and tck_rate configurable */
|
||||
igap = XADC_ZYNQ_IGAP_DEFAULT;
|
||||
@ -340,9 +342,16 @@ static int xadc_zynq_setup(struct platform_device *pdev,
|
||||
xadc->zynq_intmask = ~0;
|
||||
|
||||
pcap_rate = clk_get_rate(xadc->clk);
|
||||
if (!pcap_rate)
|
||||
return -EINVAL;
|
||||
|
||||
if (pcap_rate > XADC_ZYNQ_PCAP_RATE_MAX) {
|
||||
ret = clk_set_rate(xadc->clk,
|
||||
(unsigned long)XADC_ZYNQ_PCAP_RATE_MAX);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (tck_rate > XADC_ZYNQ_TCK_RATE_MAX)
|
||||
tck_rate = XADC_ZYNQ_TCK_RATE_MAX;
|
||||
if (tck_rate > pcap_rate / 2) {
|
||||
div = 2;
|
||||
} else {
|
||||
@ -368,6 +377,12 @@ static int xadc_zynq_setup(struct platform_device *pdev,
|
||||
XADC_ZYNQ_CFG_REDGE | XADC_ZYNQ_CFG_WEDGE |
|
||||
tck_div | XADC_ZYNQ_CFG_IGAP(igap));
|
||||
|
||||
if (pcap_rate > XADC_ZYNQ_PCAP_RATE_MAX) {
|
||||
ret = clk_set_rate(xadc->clk, pcap_rate);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -889,6 +904,9 @@ static int xadc_write_raw(struct iio_dev *indio_dev,
|
||||
unsigned long clk_rate = xadc_get_dclk_rate(xadc);
|
||||
unsigned int div;
|
||||
|
||||
if (!clk_rate)
|
||||
return -EINVAL;
|
||||
|
||||
if (info != IIO_CHAN_INFO_SAMP_FREQ)
|
||||
return -EINVAL;
|
||||
|
||||
@ -1045,7 +1063,7 @@ static int xadc_parse_dt(struct iio_dev *indio_dev, struct device_node *np,
|
||||
unsigned int num_channels;
|
||||
const char *external_mux;
|
||||
u32 ext_mux_chan;
|
||||
int reg;
|
||||
u32 reg;
|
||||
int ret;
|
||||
|
||||
*conf = 0;
|
||||
@ -1157,6 +1175,7 @@ static int xadc_probe(struct platform_device *pdev)
|
||||
|
||||
xadc = iio_priv(indio_dev);
|
||||
xadc->ops = id->data;
|
||||
xadc->irq = irq;
|
||||
init_completion(&xadc->completion);
|
||||
mutex_init(&xadc->mutex);
|
||||
spin_lock_init(&xadc->lock);
|
||||
@ -1207,15 +1226,15 @@ static int xadc_probe(struct platform_device *pdev)
|
||||
if (ret)
|
||||
goto err_free_samplerate_trigger;
|
||||
|
||||
ret = xadc->ops->setup(pdev, indio_dev, irq);
|
||||
if (ret)
|
||||
goto err_clk_disable_unprepare;
|
||||
|
||||
ret = request_irq(irq, xadc->ops->interrupt_handler, 0,
|
||||
ret = request_irq(xadc->irq, xadc->ops->interrupt_handler, 0,
|
||||
dev_name(&pdev->dev), indio_dev);
|
||||
if (ret)
|
||||
goto err_clk_disable_unprepare;
|
||||
|
||||
ret = xadc->ops->setup(pdev, indio_dev, xadc->irq);
|
||||
if (ret)
|
||||
goto err_free_irq;
|
||||
|
||||
for (i = 0; i < 16; i++)
|
||||
xadc_read_adc_reg(xadc, XADC_REG_THRESHOLD(i),
|
||||
&xadc->threshold[i]);
|
||||
@ -1239,8 +1258,10 @@ static int xadc_probe(struct platform_device *pdev)
|
||||
goto err_free_irq;
|
||||
|
||||
/* Disable all alarms */
|
||||
xadc_update_adc_reg(xadc, XADC_REG_CONF1, XADC_CONF1_ALARM_MASK,
|
||||
XADC_CONF1_ALARM_MASK);
|
||||
ret = xadc_update_adc_reg(xadc, XADC_REG_CONF1, XADC_CONF1_ALARM_MASK,
|
||||
XADC_CONF1_ALARM_MASK);
|
||||
if (ret)
|
||||
goto err_free_irq;
|
||||
|
||||
/* Set thresholds to min/max */
|
||||
for (i = 0; i < 16; i++) {
|
||||
@ -1268,7 +1289,7 @@ static int xadc_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
|
||||
err_free_irq:
|
||||
free_irq(irq, indio_dev);
|
||||
free_irq(xadc->irq, indio_dev);
|
||||
err_clk_disable_unprepare:
|
||||
clk_disable_unprepare(xadc->clk);
|
||||
err_free_samplerate_trigger:
|
||||
@ -1290,7 +1311,6 @@ static int xadc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
struct xadc *xadc = iio_priv(indio_dev);
|
||||
int irq = platform_get_irq(pdev, 0);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
if (xadc->ops->flags & XADC_FLAGS_BUFFERED) {
|
||||
@ -1298,7 +1318,7 @@ static int xadc_remove(struct platform_device *pdev)
|
||||
iio_trigger_free(xadc->convst_trigger);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
}
|
||||
free_irq(irq, indio_dev);
|
||||
free_irq(xadc->irq, indio_dev);
|
||||
clk_disable_unprepare(xadc->clk);
|
||||
cancel_delayed_work(&xadc->zynq_unmask_work);
|
||||
kfree(xadc->data);
|
||||
|
@ -68,6 +68,7 @@ struct xadc {
|
||||
spinlock_t lock;
|
||||
|
||||
struct completion completion;
|
||||
int irq;
|
||||
};
|
||||
|
||||
struct xadc_ops {
|
||||
|
@ -21,6 +21,29 @@ config ATLAS_PH_SENSOR
|
||||
To compile this driver as module, choose M here: the
|
||||
module will be called atlas-ph-sensor.
|
||||
|
||||
config BME680
|
||||
tristate "Bosch Sensortec BME680 sensor driver"
|
||||
depends on (I2C || SPI)
|
||||
select REGMAP
|
||||
select BME680_I2C if I2C
|
||||
select BME680_SPI if SPI
|
||||
help
|
||||
Say yes here to build support for Bosch Sensortec BME680 sensor with
|
||||
temperature, pressure, humidity and gas sensing capability.
|
||||
|
||||
This driver can also be built as a module. If so, the module for I2C
|
||||
would be called bme680_i2c and bme680_spi for SPI support.
|
||||
|
||||
config BME680_I2C
|
||||
tristate
|
||||
depends on I2C && BME680
|
||||
select REGMAP_I2C
|
||||
|
||||
config BME680_SPI
|
||||
tristate
|
||||
depends on SPI && BME680
|
||||
select REGMAP_SPI
|
||||
|
||||
config CCS811
|
||||
tristate "AMS CCS811 VOC sensor"
|
||||
depends on I2C
|
||||
|
@ -4,6 +4,9 @@
|
||||
|
||||
# When adding new entries keep the list in alphabetical order
|
||||
obj-$(CONFIG_ATLAS_PH_SENSOR) += atlas-ph-sensor.o
|
||||
obj-$(CONFIG_BME680) += bme680_core.o
|
||||
obj-$(CONFIG_BME680_I2C) += bme680_i2c.o
|
||||
obj-$(CONFIG_BME680_SPI) += bme680_spi.o
|
||||
obj-$(CONFIG_CCS811) += ccs811.o
|
||||
obj-$(CONFIG_IAQCORE) += ams-iaq-core.o
|
||||
obj-$(CONFIG_VZ89X) += vz89x.o
|
||||
|
96
drivers/iio/chemical/bme680.h
Normal file
96
drivers/iio/chemical/bme680.h
Normal file
@ -0,0 +1,96 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef BME680_H_
|
||||
#define BME680_H_
|
||||
|
||||
#define BME680_REG_CHIP_I2C_ID 0xD0
|
||||
#define BME680_REG_CHIP_SPI_ID 0x50
|
||||
#define BME680_CHIP_ID_VAL 0x61
|
||||
#define BME680_REG_SOFT_RESET_I2C 0xE0
|
||||
#define BME680_REG_SOFT_RESET_SPI 0x60
|
||||
#define BME680_CMD_SOFTRESET 0xB6
|
||||
#define BME680_REG_STATUS 0x73
|
||||
#define BME680_SPI_MEM_PAGE_BIT BIT(4)
|
||||
#define BME680_SPI_MEM_PAGE_1_VAL 1
|
||||
|
||||
#define BME680_REG_TEMP_MSB 0x22
|
||||
#define BME680_REG_PRESS_MSB 0x1F
|
||||
#define BM6880_REG_HUMIDITY_MSB 0x25
|
||||
#define BME680_REG_GAS_MSB 0x2A
|
||||
#define BME680_REG_GAS_R_LSB 0x2B
|
||||
#define BME680_GAS_STAB_BIT BIT(4)
|
||||
|
||||
#define BME680_REG_CTRL_HUMIDITY 0x72
|
||||
#define BME680_OSRS_HUMIDITY_MASK GENMASK(2, 0)
|
||||
|
||||
#define BME680_REG_CTRL_MEAS 0x74
|
||||
#define BME680_OSRS_TEMP_MASK GENMASK(7, 5)
|
||||
#define BME680_OSRS_PRESS_MASK GENMASK(4, 2)
|
||||
#define BME680_MODE_MASK GENMASK(1, 0)
|
||||
|
||||
#define BME680_MODE_FORCED 1
|
||||
#define BME680_MODE_SLEEP 0
|
||||
|
||||
#define BME680_REG_CONFIG 0x75
|
||||
#define BME680_FILTER_MASK GENMASK(4, 2)
|
||||
#define BME680_FILTER_COEFF_VAL BIT(1)
|
||||
|
||||
/* TEMP/PRESS/HUMID reading skipped */
|
||||
#define BME680_MEAS_SKIPPED 0x8000
|
||||
|
||||
#define BME680_MAX_OVERFLOW_VAL 0x40000000
|
||||
#define BME680_HUM_REG_SHIFT_VAL 4
|
||||
#define BME680_BIT_H1_DATA_MSK 0x0F
|
||||
|
||||
#define BME680_REG_RES_HEAT_RANGE 0x02
|
||||
#define BME680_RHRANGE_MSK 0x30
|
||||
#define BME680_REG_RES_HEAT_VAL 0x00
|
||||
#define BME680_REG_RANGE_SW_ERR 0x04
|
||||
#define BME680_RSERROR_MSK 0xF0
|
||||
#define BME680_REG_RES_HEAT_0 0x5A
|
||||
#define BME680_REG_GAS_WAIT_0 0x64
|
||||
#define BME680_GAS_RANGE_MASK 0x0F
|
||||
#define BME680_ADC_GAS_RES_SHIFT 6
|
||||
#define BME680_AMB_TEMP 25
|
||||
|
||||
#define BME680_REG_CTRL_GAS_1 0x71
|
||||
#define BME680_RUN_GAS_MASK BIT(4)
|
||||
#define BME680_NB_CONV_MASK GENMASK(3, 0)
|
||||
#define BME680_RUN_GAS_EN_BIT BIT(4)
|
||||
#define BME680_NB_CONV_0_VAL 0
|
||||
|
||||
#define BME680_REG_MEAS_STAT_0 0x1D
|
||||
#define BME680_GAS_MEAS_BIT BIT(6)
|
||||
|
||||
/* Calibration Parameters */
|
||||
#define BME680_T2_LSB_REG 0x8A
|
||||
#define BME680_T3_REG 0x8C
|
||||
#define BME680_P1_LSB_REG 0x8E
|
||||
#define BME680_P2_LSB_REG 0x90
|
||||
#define BME680_P3_REG 0x92
|
||||
#define BME680_P4_LSB_REG 0x94
|
||||
#define BME680_P5_LSB_REG 0x96
|
||||
#define BME680_P7_REG 0x98
|
||||
#define BME680_P6_REG 0x99
|
||||
#define BME680_P8_LSB_REG 0x9C
|
||||
#define BME680_P9_LSB_REG 0x9E
|
||||
#define BME680_P10_REG 0xA0
|
||||
#define BME680_H2_LSB_REG 0xE2
|
||||
#define BME680_H2_MSB_REG 0xE1
|
||||
#define BME680_H1_MSB_REG 0xE3
|
||||
#define BME680_H1_LSB_REG 0xE2
|
||||
#define BME680_H3_REG 0xE4
|
||||
#define BME680_H4_REG 0xE5
|
||||
#define BME680_H5_REG 0xE6
|
||||
#define BME680_H6_REG 0xE7
|
||||
#define BME680_H7_REG 0xE8
|
||||
#define BME680_T1_LSB_REG 0xE9
|
||||
#define BME680_GH2_LSB_REG 0xEB
|
||||
#define BME680_GH1_REG 0xED
|
||||
#define BME680_GH3_REG 0xEE
|
||||
|
||||
extern const struct regmap_config bme680_regmap_config;
|
||||
|
||||
int bme680_core_probe(struct device *dev, struct regmap *regmap,
|
||||
const char *name);
|
||||
|
||||
#endif /* BME680_H_ */
|
959
drivers/iio/chemical/bme680_core.c
Normal file
959
drivers/iio/chemical/bme680_core.c
Normal file
@ -0,0 +1,959 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Bosch BME680 - Temperature, Pressure, Humidity & Gas Sensor
|
||||
*
|
||||
* Copyright (C) 2017 - 2018 Bosch Sensortec GmbH
|
||||
* Copyright (C) 2018 Himanshu Jha <himanshujha199640@gmail.com>
|
||||
*
|
||||
* Datasheet:
|
||||
* https://ae-bst.resource.bosch.com/media/_tech/media/datasheets/BST-BME680-DS001-00.pdf
|
||||
*/
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/log2.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
#include "bme680.h"
|
||||
|
||||
struct bme680_calib {
|
||||
u16 par_t1;
|
||||
s16 par_t2;
|
||||
s8 par_t3;
|
||||
u16 par_p1;
|
||||
s16 par_p2;
|
||||
s8 par_p3;
|
||||
s16 par_p4;
|
||||
s16 par_p5;
|
||||
s8 par_p6;
|
||||
s8 par_p7;
|
||||
s16 par_p8;
|
||||
s16 par_p9;
|
||||
u8 par_p10;
|
||||
u16 par_h1;
|
||||
u16 par_h2;
|
||||
s8 par_h3;
|
||||
s8 par_h4;
|
||||
s8 par_h5;
|
||||
s8 par_h6;
|
||||
s8 par_h7;
|
||||
s8 par_gh1;
|
||||
s16 par_gh2;
|
||||
s8 par_gh3;
|
||||
u8 res_heat_range;
|
||||
s8 res_heat_val;
|
||||
s8 range_sw_err;
|
||||
};
|
||||
|
||||
struct bme680_data {
|
||||
struct regmap *regmap;
|
||||
struct bme680_calib bme680;
|
||||
u8 oversampling_temp;
|
||||
u8 oversampling_press;
|
||||
u8 oversampling_humid;
|
||||
u16 heater_dur;
|
||||
u16 heater_temp;
|
||||
/*
|
||||
* Carryover value from temperature conversion, used in pressure
|
||||
* and humidity compensation calculations.
|
||||
*/
|
||||
s32 t_fine;
|
||||
};
|
||||
|
||||
const struct regmap_config bme680_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
};
|
||||
EXPORT_SYMBOL(bme680_regmap_config);
|
||||
|
||||
static const struct iio_chan_spec bme680_channels[] = {
|
||||
{
|
||||
.type = IIO_TEMP,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
|
||||
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
|
||||
},
|
||||
{
|
||||
.type = IIO_PRESSURE,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
|
||||
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
|
||||
},
|
||||
{
|
||||
.type = IIO_HUMIDITYRELATIVE,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
|
||||
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
|
||||
},
|
||||
{
|
||||
.type = IIO_RESISTANCE,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
|
||||
},
|
||||
};
|
||||
|
||||
static const int bme680_oversampling_avail[] = { 1, 2, 4, 8, 16 };
|
||||
|
||||
static int bme680_read_calib(struct bme680_data *data,
|
||||
struct bme680_calib *calib)
|
||||
{
|
||||
struct device *dev = regmap_get_device(data->regmap);
|
||||
unsigned int tmp, tmp_msb, tmp_lsb;
|
||||
int ret;
|
||||
__le16 buf;
|
||||
|
||||
/* Temperature related coefficients */
|
||||
ret = regmap_bulk_read(data->regmap, BME680_T1_LSB_REG,
|
||||
(u8 *) &buf, 2);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read BME680_T1_LSB_REG\n");
|
||||
return ret;
|
||||
}
|
||||
calib->par_t1 = le16_to_cpu(buf);
|
||||
|
||||
ret = regmap_bulk_read(data->regmap, BME680_T2_LSB_REG,
|
||||
(u8 *) &buf, 2);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read BME680_T2_LSB_REG\n");
|
||||
return ret;
|
||||
}
|
||||
calib->par_t2 = le16_to_cpu(buf);
|
||||
|
||||
ret = regmap_read(data->regmap, BME680_T3_REG, &tmp);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read BME680_T3_REG\n");
|
||||
return ret;
|
||||
}
|
||||
calib->par_t3 = tmp;
|
||||
|
||||
/* Pressure related coefficients */
|
||||
ret = regmap_bulk_read(data->regmap, BME680_P1_LSB_REG,
|
||||
(u8 *) &buf, 2);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read BME680_P1_LSB_REG\n");
|
||||
return ret;
|
||||
}
|
||||
calib->par_p1 = le16_to_cpu(buf);
|
||||
|
||||
ret = regmap_bulk_read(data->regmap, BME680_P2_LSB_REG,
|
||||
(u8 *) &buf, 2);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read BME680_P2_LSB_REG\n");
|
||||
return ret;
|
||||
}
|
||||
calib->par_p2 = le16_to_cpu(buf);
|
||||
|
||||
ret = regmap_read(data->regmap, BME680_P3_REG, &tmp);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read BME680_P3_REG\n");
|
||||
return ret;
|
||||
}
|
||||
calib->par_p3 = tmp;
|
||||
|
||||
ret = regmap_bulk_read(data->regmap, BME680_P4_LSB_REG,
|
||||
(u8 *) &buf, 2);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read BME680_P4_LSB_REG\n");
|
||||
return ret;
|
||||
}
|
||||
calib->par_p4 = le16_to_cpu(buf);
|
||||
|
||||
ret = regmap_bulk_read(data->regmap, BME680_P5_LSB_REG,
|
||||
(u8 *) &buf, 2);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read BME680_P5_LSB_REG\n");
|
||||
return ret;
|
||||
}
|
||||
calib->par_p5 = le16_to_cpu(buf);
|
||||
|
||||
ret = regmap_read(data->regmap, BME680_P6_REG, &tmp);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read BME680_P6_REG\n");
|
||||
return ret;
|
||||
}
|
||||
calib->par_p6 = tmp;
|
||||
|
||||
ret = regmap_read(data->regmap, BME680_P7_REG, &tmp);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read BME680_P7_REG\n");
|
||||
return ret;
|
||||
}
|
||||
calib->par_p7 = tmp;
|
||||
|
||||
ret = regmap_bulk_read(data->regmap, BME680_P8_LSB_REG,
|
||||
(u8 *) &buf, 2);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read BME680_P8_LSB_REG\n");
|
||||
return ret;
|
||||
}
|
||||
calib->par_p8 = le16_to_cpu(buf);
|
||||
|
||||
ret = regmap_bulk_read(data->regmap, BME680_P9_LSB_REG,
|
||||
(u8 *) &buf, 2);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read BME680_P9_LSB_REG\n");
|
||||
return ret;
|
||||
}
|
||||
calib->par_p9 = le16_to_cpu(buf);
|
||||
|
||||
ret = regmap_read(data->regmap, BME680_P10_REG, &tmp);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read BME680_P10_REG\n");
|
||||
return ret;
|
||||
}
|
||||
calib->par_p10 = tmp;
|
||||
|
||||
/* Humidity related coefficients */
|
||||
ret = regmap_read(data->regmap, BME680_H1_MSB_REG, &tmp_msb);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read BME680_H1_MSB_REG\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regmap_read(data->regmap, BME680_H1_LSB_REG, &tmp_lsb);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read BME680_H1_LSB_REG\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
calib->par_h1 = (tmp_msb << BME680_HUM_REG_SHIFT_VAL) |
|
||||
(tmp_lsb & BME680_BIT_H1_DATA_MSK);
|
||||
|
||||
ret = regmap_read(data->regmap, BME680_H2_MSB_REG, &tmp_msb);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read BME680_H2_MSB_REG\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regmap_read(data->regmap, BME680_H2_LSB_REG, &tmp_lsb);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read BME680_H2_LSB_REG\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
calib->par_h2 = (tmp_msb << BME680_HUM_REG_SHIFT_VAL) |
|
||||
(tmp_lsb >> BME680_HUM_REG_SHIFT_VAL);
|
||||
|
||||
ret = regmap_read(data->regmap, BME680_H3_REG, &tmp);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read BME680_H3_REG\n");
|
||||
return ret;
|
||||
}
|
||||
calib->par_h3 = tmp;
|
||||
|
||||
ret = regmap_read(data->regmap, BME680_H4_REG, &tmp);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read BME680_H4_REG\n");
|
||||
return ret;
|
||||
}
|
||||
calib->par_h4 = tmp;
|
||||
|
||||
ret = regmap_read(data->regmap, BME680_H5_REG, &tmp);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read BME680_H5_REG\n");
|
||||
return ret;
|
||||
}
|
||||
calib->par_h5 = tmp;
|
||||
|
||||
ret = regmap_read(data->regmap, BME680_H6_REG, &tmp);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read BME680_H6_REG\n");
|
||||
return ret;
|
||||
}
|
||||
calib->par_h6 = tmp;
|
||||
|
||||
ret = regmap_read(data->regmap, BME680_H7_REG, &tmp);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read BME680_H7_REG\n");
|
||||
return ret;
|
||||
}
|
||||
calib->par_h7 = tmp;
|
||||
|
||||
/* Gas heater related coefficients */
|
||||
ret = regmap_read(data->regmap, BME680_GH1_REG, &tmp);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read BME680_GH1_REG\n");
|
||||
return ret;
|
||||
}
|
||||
calib->par_gh1 = tmp;
|
||||
|
||||
ret = regmap_bulk_read(data->regmap, BME680_GH2_LSB_REG,
|
||||
(u8 *) &buf, 2);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read BME680_GH2_LSB_REG\n");
|
||||
return ret;
|
||||
}
|
||||
calib->par_gh2 = le16_to_cpu(buf);
|
||||
|
||||
ret = regmap_read(data->regmap, BME680_GH3_REG, &tmp);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read BME680_GH3_REG\n");
|
||||
return ret;
|
||||
}
|
||||
calib->par_gh3 = tmp;
|
||||
|
||||
/* Other coefficients */
|
||||
ret = regmap_read(data->regmap, BME680_REG_RES_HEAT_RANGE, &tmp);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read resistance heat range\n");
|
||||
return ret;
|
||||
}
|
||||
calib->res_heat_range = (tmp & BME680_RHRANGE_MSK) / 16;
|
||||
|
||||
ret = regmap_read(data->regmap, BME680_REG_RES_HEAT_VAL, &tmp);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read resistance heat value\n");
|
||||
return ret;
|
||||
}
|
||||
calib->res_heat_val = tmp;
|
||||
|
||||
ret = regmap_read(data->regmap, BME680_REG_RANGE_SW_ERR, &tmp);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read range software error\n");
|
||||
return ret;
|
||||
}
|
||||
calib->range_sw_err = (tmp & BME680_RSERROR_MSK) / 16;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Taken from Bosch BME680 API:
|
||||
* https://github.com/BoschSensortec/BME680_driver/blob/63bb5336/bme680.c#L876
|
||||
*
|
||||
* Returns temperature measurement in DegC, resolutions is 0.01 DegC. Therefore,
|
||||
* output value of "3233" represents 32.33 DegC.
|
||||
*/
|
||||
static s16 bme680_compensate_temp(struct bme680_data *data,
|
||||
s32 adc_temp)
|
||||
{
|
||||
struct bme680_calib *calib = &data->bme680;
|
||||
s64 var1, var2, var3;
|
||||
s16 calc_temp;
|
||||
|
||||
var1 = (adc_temp >> 3) - (calib->par_t1 << 1);
|
||||
var2 = (var1 * calib->par_t2) >> 11;
|
||||
var3 = ((var1 >> 1) * (var1 >> 1)) >> 12;
|
||||
var3 = (var3 * (calib->par_t3 << 4)) >> 14;
|
||||
data->t_fine = var2 + var3;
|
||||
calc_temp = (data->t_fine * 5 + 128) >> 8;
|
||||
|
||||
return calc_temp;
|
||||
}
|
||||
|
||||
/*
|
||||
* Taken from Bosch BME680 API:
|
||||
* https://github.com/BoschSensortec/BME680_driver/blob/63bb5336/bme680.c#L896
|
||||
*
|
||||
* Returns pressure measurement in Pa. Output value of "97356" represents
|
||||
* 97356 Pa = 973.56 hPa.
|
||||
*/
|
||||
static u32 bme680_compensate_press(struct bme680_data *data,
|
||||
u32 adc_press)
|
||||
{
|
||||
struct bme680_calib *calib = &data->bme680;
|
||||
s32 var1, var2, var3, press_comp;
|
||||
|
||||
var1 = (data->t_fine >> 1) - 64000;
|
||||
var2 = ((((var1 >> 2) * (var1 >> 2)) >> 11) * calib->par_p6) >> 2;
|
||||
var2 = var2 + (var1 * calib->par_p5 << 1);
|
||||
var2 = (var2 >> 2) + (calib->par_p4 << 16);
|
||||
var1 = (((((var1 >> 2) * (var1 >> 2)) >> 13) *
|
||||
(calib->par_p3 << 5)) >> 3) +
|
||||
((calib->par_p2 * var1) >> 1);
|
||||
var1 = var1 >> 18;
|
||||
var1 = ((32768 + var1) * calib->par_p1) >> 15;
|
||||
press_comp = 1048576 - adc_press;
|
||||
press_comp = ((press_comp - (var2 >> 12)) * 3125);
|
||||
|
||||
if (press_comp >= BME680_MAX_OVERFLOW_VAL)
|
||||
press_comp = ((press_comp / (u32)var1) << 1);
|
||||
else
|
||||
press_comp = ((press_comp << 1) / (u32)var1);
|
||||
|
||||
var1 = (calib->par_p9 * (((press_comp >> 3) *
|
||||
(press_comp >> 3)) >> 13)) >> 12;
|
||||
var2 = ((press_comp >> 2) * calib->par_p8) >> 13;
|
||||
var3 = ((press_comp >> 8) * (press_comp >> 8) *
|
||||
(press_comp >> 8) * calib->par_p10) >> 17;
|
||||
|
||||
press_comp += (var1 + var2 + var3 + (calib->par_p7 << 7)) >> 4;
|
||||
|
||||
return press_comp;
|
||||
}
|
||||
|
||||
/*
|
||||
* Taken from Bosch BME680 API:
|
||||
* https://github.com/BoschSensortec/BME680_driver/blob/63bb5336/bme680.c#L937
|
||||
*
|
||||
* Returns humidity measurement in percent, resolution is 0.001 percent. Output
|
||||
* value of "43215" represents 43.215 %rH.
|
||||
*/
|
||||
static u32 bme680_compensate_humid(struct bme680_data *data,
|
||||
u16 adc_humid)
|
||||
{
|
||||
struct bme680_calib *calib = &data->bme680;
|
||||
s32 var1, var2, var3, var4, var5, var6, temp_scaled, calc_hum;
|
||||
|
||||
temp_scaled = (data->t_fine * 5 + 128) >> 8;
|
||||
var1 = (adc_humid - ((s32) ((s32) calib->par_h1 * 16))) -
|
||||
(((temp_scaled * (s32) calib->par_h3) / 100) >> 1);
|
||||
var2 = ((s32) calib->par_h2 *
|
||||
(((temp_scaled * calib->par_h4) / 100) +
|
||||
(((temp_scaled * ((temp_scaled * calib->par_h5) / 100))
|
||||
>> 6) / 100) + (1 << 14))) >> 10;
|
||||
var3 = var1 * var2;
|
||||
var4 = calib->par_h6 << 7;
|
||||
var4 = (var4 + ((temp_scaled * calib->par_h7) / 100)) >> 4;
|
||||
var5 = ((var3 >> 14) * (var3 >> 14)) >> 10;
|
||||
var6 = (var4 * var5) >> 1;
|
||||
calc_hum = (((var3 + var6) >> 10) * 1000) >> 12;
|
||||
|
||||
if (calc_hum > 100000) /* Cap at 100%rH */
|
||||
calc_hum = 100000;
|
||||
else if (calc_hum < 0)
|
||||
calc_hum = 0;
|
||||
|
||||
return calc_hum;
|
||||
}
|
||||
|
||||
/*
|
||||
* Taken from Bosch BME680 API:
|
||||
* https://github.com/BoschSensortec/BME680_driver/blob/63bb5336/bme680.c#L973
|
||||
*
|
||||
* Returns gas measurement in Ohm. Output value of "82986" represent 82986 ohms.
|
||||
*/
|
||||
static u32 bme680_compensate_gas(struct bme680_data *data, u16 gas_res_adc,
|
||||
u8 gas_range)
|
||||
{
|
||||
struct bme680_calib *calib = &data->bme680;
|
||||
s64 var1;
|
||||
u64 var2;
|
||||
s64 var3;
|
||||
u32 calc_gas_res;
|
||||
|
||||
/* Look up table for the possible gas range values */
|
||||
const u32 lookupTable[16] = {2147483647u, 2147483647u,
|
||||
2147483647u, 2147483647u, 2147483647u,
|
||||
2126008810u, 2147483647u, 2130303777u,
|
||||
2147483647u, 2147483647u, 2143188679u,
|
||||
2136746228u, 2147483647u, 2126008810u,
|
||||
2147483647u, 2147483647u};
|
||||
|
||||
var1 = ((1340 + (5 * (s64) calib->range_sw_err)) *
|
||||
((s64) lookupTable[gas_range])) >> 16;
|
||||
var2 = ((gas_res_adc << 15) - 16777216) + var1;
|
||||
var3 = ((125000 << (15 - gas_range)) * var1) >> 9;
|
||||
var3 += (var2 >> 1);
|
||||
calc_gas_res = div64_s64(var3, (s64) var2);
|
||||
|
||||
return calc_gas_res;
|
||||
}
|
||||
|
||||
/*
|
||||
* Taken from Bosch BME680 API:
|
||||
* https://github.com/BoschSensortec/BME680_driver/blob/63bb5336/bme680.c#L1002
|
||||
*/
|
||||
static u8 bme680_calc_heater_res(struct bme680_data *data, u16 temp)
|
||||
{
|
||||
struct bme680_calib *calib = &data->bme680;
|
||||
s32 var1, var2, var3, var4, var5, heatr_res_x100;
|
||||
u8 heatr_res;
|
||||
|
||||
if (temp > 400) /* Cap temperature */
|
||||
temp = 400;
|
||||
|
||||
var1 = (((s32) BME680_AMB_TEMP * calib->par_gh3) / 1000) * 256;
|
||||
var2 = (calib->par_gh1 + 784) * (((((calib->par_gh2 + 154009) *
|
||||
temp * 5) / 100)
|
||||
+ 3276800) / 10);
|
||||
var3 = var1 + (var2 / 2);
|
||||
var4 = (var3 / (calib->res_heat_range + 4));
|
||||
var5 = 131 * calib->res_heat_val + 65536;
|
||||
heatr_res_x100 = ((var4 / var5) - 250) * 34;
|
||||
heatr_res = (heatr_res_x100 + 50) / 100;
|
||||
|
||||
return heatr_res;
|
||||
}
|
||||
|
||||
/*
|
||||
* Taken from Bosch BME680 API:
|
||||
* https://github.com/BoschSensortec/BME680_driver/blob/63bb5336/bme680.c#L1188
|
||||
*/
|
||||
static u8 bme680_calc_heater_dur(u16 dur)
|
||||
{
|
||||
u8 durval, factor = 0;
|
||||
|
||||
if (dur >= 0xfc0) {
|
||||
durval = 0xff; /* Max duration */
|
||||
} else {
|
||||
while (dur > 0x3F) {
|
||||
dur = dur / 4;
|
||||
factor += 1;
|
||||
}
|
||||
durval = dur + (factor * 64);
|
||||
}
|
||||
|
||||
return durval;
|
||||
}
|
||||
|
||||
static int bme680_set_mode(struct bme680_data *data, bool mode)
|
||||
{
|
||||
struct device *dev = regmap_get_device(data->regmap);
|
||||
int ret;
|
||||
|
||||
if (mode) {
|
||||
ret = regmap_write_bits(data->regmap, BME680_REG_CTRL_MEAS,
|
||||
BME680_MODE_MASK, BME680_MODE_FORCED);
|
||||
if (ret < 0)
|
||||
dev_err(dev, "failed to set forced mode\n");
|
||||
|
||||
} else {
|
||||
ret = regmap_write_bits(data->regmap, BME680_REG_CTRL_MEAS,
|
||||
BME680_MODE_MASK, BME680_MODE_SLEEP);
|
||||
if (ret < 0)
|
||||
dev_err(dev, "failed to set sleep mode\n");
|
||||
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bme680_chip_config(struct bme680_data *data)
|
||||
{
|
||||
struct device *dev = regmap_get_device(data->regmap);
|
||||
int ret;
|
||||
u8 osrs = FIELD_PREP(BME680_OSRS_HUMIDITY_MASK,
|
||||
data->oversampling_humid + 1);
|
||||
/*
|
||||
* Highly recommended to set oversampling of humidity before
|
||||
* temperature/pressure oversampling.
|
||||
*/
|
||||
ret = regmap_update_bits(data->regmap, BME680_REG_CTRL_HUMIDITY,
|
||||
BME680_OSRS_HUMIDITY_MASK, osrs);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to write ctrl_hum register\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* IIR filter settings */
|
||||
ret = regmap_update_bits(data->regmap, BME680_REG_CONFIG,
|
||||
BME680_FILTER_MASK,
|
||||
BME680_FILTER_COEFF_VAL);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to write config register\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
osrs = FIELD_PREP(BME680_OSRS_TEMP_MASK, data->oversampling_temp + 1) |
|
||||
FIELD_PREP(BME680_OSRS_PRESS_MASK, data->oversampling_press + 1);
|
||||
|
||||
ret = regmap_write_bits(data->regmap, BME680_REG_CTRL_MEAS,
|
||||
BME680_OSRS_TEMP_MASK |
|
||||
BME680_OSRS_PRESS_MASK,
|
||||
osrs);
|
||||
if (ret < 0)
|
||||
dev_err(dev, "failed to write ctrl_meas register\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bme680_gas_config(struct bme680_data *data)
|
||||
{
|
||||
struct device *dev = regmap_get_device(data->regmap);
|
||||
int ret;
|
||||
u8 heatr_res, heatr_dur;
|
||||
|
||||
heatr_res = bme680_calc_heater_res(data, data->heater_temp);
|
||||
|
||||
/* set target heater temperature */
|
||||
ret = regmap_write(data->regmap, BME680_REG_RES_HEAT_0, heatr_res);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to write res_heat_0 register\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
heatr_dur = bme680_calc_heater_dur(data->heater_dur);
|
||||
|
||||
/* set target heating duration */
|
||||
ret = regmap_write(data->regmap, BME680_REG_GAS_WAIT_0, heatr_dur);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failted to write gas_wait_0 register\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Selecting the runGas and NB conversion settings for the sensor */
|
||||
ret = regmap_update_bits(data->regmap, BME680_REG_CTRL_GAS_1,
|
||||
BME680_RUN_GAS_MASK | BME680_NB_CONV_MASK,
|
||||
BME680_RUN_GAS_EN_BIT | BME680_NB_CONV_0_VAL);
|
||||
if (ret < 0)
|
||||
dev_err(dev, "failed to write ctrl_gas_1 register\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bme680_read_temp(struct bme680_data *data,
|
||||
int *val, int *val2)
|
||||
{
|
||||
struct device *dev = regmap_get_device(data->regmap);
|
||||
int ret;
|
||||
__be32 tmp = 0;
|
||||
s32 adc_temp;
|
||||
s16 comp_temp;
|
||||
|
||||
/* set forced mode to trigger measurement */
|
||||
ret = bme680_set_mode(data, true);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = regmap_bulk_read(data->regmap, BME680_REG_TEMP_MSB,
|
||||
(u8 *) &tmp, 3);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read temperature\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
adc_temp = be32_to_cpu(tmp) >> 12;
|
||||
if (adc_temp == BME680_MEAS_SKIPPED) {
|
||||
/* reading was skipped */
|
||||
dev_err(dev, "reading temperature skipped\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
comp_temp = bme680_compensate_temp(data, adc_temp);
|
||||
/*
|
||||
* val might be NULL if we're called by the read_press/read_humid
|
||||
* routine which is callled to get t_fine value used in
|
||||
* compensate_press/compensate_humid to get compensated
|
||||
* pressure/humidity readings.
|
||||
*/
|
||||
if (val && val2) {
|
||||
*val = comp_temp;
|
||||
*val2 = 100;
|
||||
return IIO_VAL_FRACTIONAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bme680_read_press(struct bme680_data *data,
|
||||
int *val, int *val2)
|
||||
{
|
||||
struct device *dev = regmap_get_device(data->regmap);
|
||||
int ret;
|
||||
__be32 tmp = 0;
|
||||
s32 adc_press;
|
||||
|
||||
/* Read and compensate temperature to get a reading of t_fine */
|
||||
ret = bme680_read_temp(data, NULL, NULL);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = regmap_bulk_read(data->regmap, BME680_REG_PRESS_MSB,
|
||||
(u8 *) &tmp, 3);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read pressure\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
adc_press = be32_to_cpu(tmp) >> 12;
|
||||
if (adc_press == BME680_MEAS_SKIPPED) {
|
||||
/* reading was skipped */
|
||||
dev_err(dev, "reading pressure skipped\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*val = bme680_compensate_press(data, adc_press);
|
||||
*val2 = 100;
|
||||
return IIO_VAL_FRACTIONAL;
|
||||
}
|
||||
|
||||
static int bme680_read_humid(struct bme680_data *data,
|
||||
int *val, int *val2)
|
||||
{
|
||||
struct device *dev = regmap_get_device(data->regmap);
|
||||
int ret;
|
||||
__be16 tmp = 0;
|
||||
s32 adc_humidity;
|
||||
u32 comp_humidity;
|
||||
|
||||
/* Read and compensate temperature to get a reading of t_fine */
|
||||
ret = bme680_read_temp(data, NULL, NULL);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = regmap_bulk_read(data->regmap, BM6880_REG_HUMIDITY_MSB,
|
||||
(u8 *) &tmp, 2);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read humidity\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
adc_humidity = be16_to_cpu(tmp);
|
||||
if (adc_humidity == BME680_MEAS_SKIPPED) {
|
||||
/* reading was skipped */
|
||||
dev_err(dev, "reading humidity skipped\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
comp_humidity = bme680_compensate_humid(data, adc_humidity);
|
||||
|
||||
*val = comp_humidity;
|
||||
*val2 = 1000;
|
||||
return IIO_VAL_FRACTIONAL;
|
||||
}
|
||||
|
||||
static int bme680_read_gas(struct bme680_data *data,
|
||||
int *val)
|
||||
{
|
||||
struct device *dev = regmap_get_device(data->regmap);
|
||||
int ret;
|
||||
__be16 tmp = 0;
|
||||
unsigned int check;
|
||||
u16 adc_gas_res;
|
||||
u8 gas_range;
|
||||
|
||||
/* Set heater settings */
|
||||
ret = bme680_gas_config(data);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to set gas config\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* set forced mode to trigger measurement */
|
||||
ret = bme680_set_mode(data, true);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = regmap_read(data->regmap, BME680_REG_MEAS_STAT_0, &check);
|
||||
if (check & BME680_GAS_MEAS_BIT) {
|
||||
dev_err(dev, "gas measurement incomplete\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
ret = regmap_read(data->regmap, BME680_REG_GAS_R_LSB, &check);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read gas_r_lsb register\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* occurs if either the gas heating duration was insuffient
|
||||
* to reach the target heater temperature or the target
|
||||
* heater temperature was too high for the heater sink to
|
||||
* reach.
|
||||
*/
|
||||
if ((check & BME680_GAS_STAB_BIT) == 0) {
|
||||
dev_err(dev, "heater failed to reach the target temperature\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = regmap_bulk_read(data->regmap, BME680_REG_GAS_MSB,
|
||||
(u8 *) &tmp, 2);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read gas resistance\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
gas_range = check & BME680_GAS_RANGE_MASK;
|
||||
adc_gas_res = be16_to_cpu(tmp) >> BME680_ADC_GAS_RES_SHIFT;
|
||||
|
||||
*val = bme680_compensate_gas(data, adc_gas_res, gas_range);
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
static int bme680_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct bme680_data *data = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_PROCESSED:
|
||||
switch (chan->type) {
|
||||
case IIO_TEMP:
|
||||
return bme680_read_temp(data, val, val2);
|
||||
case IIO_PRESSURE:
|
||||
return bme680_read_press(data, val, val2);
|
||||
case IIO_HUMIDITYRELATIVE:
|
||||
return bme680_read_humid(data, val, val2);
|
||||
case IIO_RESISTANCE:
|
||||
return bme680_read_gas(data, val);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
|
||||
switch (chan->type) {
|
||||
case IIO_TEMP:
|
||||
*val = 1 << data->oversampling_temp;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_PRESSURE:
|
||||
*val = 1 << data->oversampling_press;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_HUMIDITYRELATIVE:
|
||||
*val = 1 << data->oversampling_humid;
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int bme680_write_oversampling_ratio_temp(struct bme680_data *data,
|
||||
int val)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(bme680_oversampling_avail); i++) {
|
||||
if (bme680_oversampling_avail[i] == val) {
|
||||
data->oversampling_temp = ilog2(val);
|
||||
|
||||
return bme680_chip_config(data);
|
||||
}
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int bme680_write_oversampling_ratio_press(struct bme680_data *data,
|
||||
int val)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(bme680_oversampling_avail); i++) {
|
||||
if (bme680_oversampling_avail[i] == val) {
|
||||
data->oversampling_press = ilog2(val);
|
||||
|
||||
return bme680_chip_config(data);
|
||||
}
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int bme680_write_oversampling_ratio_humid(struct bme680_data *data,
|
||||
int val)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(bme680_oversampling_avail); i++) {
|
||||
if (bme680_oversampling_avail[i] == val) {
|
||||
data->oversampling_humid = ilog2(val);
|
||||
|
||||
return bme680_chip_config(data);
|
||||
}
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int bme680_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
struct bme680_data *data = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
|
||||
switch (chan->type) {
|
||||
case IIO_TEMP:
|
||||
return bme680_write_oversampling_ratio_temp(data, val);
|
||||
case IIO_PRESSURE:
|
||||
return bme680_write_oversampling_ratio_press(data, val);
|
||||
case IIO_HUMIDITYRELATIVE:
|
||||
return bme680_write_oversampling_ratio_humid(data, val);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static const char bme680_oversampling_ratio_show[] = "1 2 4 8 16";
|
||||
|
||||
static IIO_CONST_ATTR(oversampling_ratio_available,
|
||||
bme680_oversampling_ratio_show);
|
||||
|
||||
static struct attribute *bme680_attributes[] = {
|
||||
&iio_const_attr_oversampling_ratio_available.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group bme680_attribute_group = {
|
||||
.attrs = bme680_attributes,
|
||||
};
|
||||
|
||||
static const struct iio_info bme680_info = {
|
||||
.read_raw = &bme680_read_raw,
|
||||
.write_raw = &bme680_write_raw,
|
||||
.attrs = &bme680_attribute_group,
|
||||
};
|
||||
|
||||
static const char *bme680_match_acpi_device(struct device *dev)
|
||||
{
|
||||
const struct acpi_device_id *id;
|
||||
|
||||
id = acpi_match_device(dev->driver->acpi_match_table, dev);
|
||||
if (!id)
|
||||
return NULL;
|
||||
|
||||
return dev_name(dev);
|
||||
}
|
||||
|
||||
int bme680_core_probe(struct device *dev, struct regmap *regmap,
|
||||
const char *name)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct bme680_data *data;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!name && ACPI_HANDLE(dev))
|
||||
name = bme680_match_acpi_device(dev);
|
||||
|
||||
data = iio_priv(indio_dev);
|
||||
dev_set_drvdata(dev, indio_dev);
|
||||
data->regmap = regmap;
|
||||
indio_dev->dev.parent = dev;
|
||||
indio_dev->name = name;
|
||||
indio_dev->channels = bme680_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(bme680_channels);
|
||||
indio_dev->info = &bme680_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
/* default values for the sensor */
|
||||
data->oversampling_humid = ilog2(2); /* 2X oversampling rate */
|
||||
data->oversampling_press = ilog2(4); /* 4X oversampling rate */
|
||||
data->oversampling_temp = ilog2(8); /* 8X oversampling rate */
|
||||
data->heater_temp = 320; /* degree Celsius */
|
||||
data->heater_dur = 150; /* milliseconds */
|
||||
|
||||
ret = bme680_chip_config(data);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to set chip_config data\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = bme680_gas_config(data);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to set gas config data\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = bme680_read_calib(data, &data->bme680);
|
||||
if (ret < 0) {
|
||||
dev_err(dev,
|
||||
"failed to read calibration coefficients at probe\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return devm_iio_device_register(dev, indio_dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(bme680_core_probe);
|
||||
|
||||
MODULE_AUTHOR("Himanshu Jha <himanshujha199640@gmail.com>");
|
||||
MODULE_DESCRIPTION("Bosch BME680 Driver");
|
||||
MODULE_LICENSE("GPL v2");
|
85
drivers/iio/chemical/bme680_i2c.c
Normal file
85
drivers/iio/chemical/bme680_i2c.c
Normal file
@ -0,0 +1,85 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* BME680 - I2C Driver
|
||||
*
|
||||
* Copyright (C) 2018 Himanshu Jha <himanshujha199640@gmail.com>
|
||||
*
|
||||
* 7-Bit I2C slave address is:
|
||||
* - 0x76 if SDO is pulled to GND
|
||||
* - 0x77 if SDO is pulled to VDDIO
|
||||
*
|
||||
* Note: SDO pin cannot be left floating otherwise I2C address
|
||||
* will be undefined.
|
||||
*/
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include "bme680.h"
|
||||
|
||||
static int bme680_i2c_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct regmap *regmap;
|
||||
const char *name = NULL;
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
regmap = devm_regmap_init_i2c(client, &bme680_regmap_config);
|
||||
if (IS_ERR(regmap)) {
|
||||
dev_err(&client->dev, "Failed to register i2c regmap %d\n",
|
||||
(int)PTR_ERR(regmap));
|
||||
return PTR_ERR(regmap);
|
||||
}
|
||||
|
||||
ret = regmap_write(regmap, BME680_REG_SOFT_RESET_I2C,
|
||||
BME680_CMD_SOFTRESET);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "Failed to reset chip\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regmap_read(regmap, BME680_REG_CHIP_I2C_ID, &val);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "Error reading I2C chip ID\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (val != BME680_CHIP_ID_VAL) {
|
||||
dev_err(&client->dev, "Wrong chip ID, got %x expected %x\n",
|
||||
val, BME680_CHIP_ID_VAL);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (id)
|
||||
name = id->name;
|
||||
|
||||
return bme680_core_probe(&client->dev, regmap, name);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id bme680_i2c_id[] = {
|
||||
{"bme680", 0},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, bme680_i2c_id);
|
||||
|
||||
static const struct acpi_device_id bme680_acpi_match[] = {
|
||||
{"BME0680", 0},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, bme680_acpi_match);
|
||||
|
||||
static struct i2c_driver bme680_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "bme680_i2c",
|
||||
.acpi_match_table = ACPI_PTR(bme680_acpi_match),
|
||||
},
|
||||
.probe = bme680_i2c_probe,
|
||||
.id_table = bme680_i2c_id,
|
||||
};
|
||||
module_i2c_driver(bme680_i2c_driver);
|
||||
|
||||
MODULE_AUTHOR("Himanshu Jha <himanshujha199640@gmail.com>");
|
||||
MODULE_DESCRIPTION("BME680 I2C driver");
|
||||
MODULE_LICENSE("GPL v2");
|
125
drivers/iio/chemical/bme680_spi.c
Normal file
125
drivers/iio/chemical/bme680_spi.c
Normal file
@ -0,0 +1,125 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* BME680 - SPI Driver
|
||||
*
|
||||
* Copyright (C) 2018 Himanshu Jha <himanshujha199640@gmail.com>
|
||||
*/
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#include "bme680.h"
|
||||
|
||||
static int bme680_regmap_spi_write(void *context, const void *data,
|
||||
size_t count)
|
||||
{
|
||||
struct spi_device *spi = context;
|
||||
u8 buf[2];
|
||||
|
||||
memcpy(buf, data, 2);
|
||||
/*
|
||||
* The SPI register address (= full register address without bit 7)
|
||||
* and the write command (bit7 = RW = '0')
|
||||
*/
|
||||
buf[0] &= ~0x80;
|
||||
|
||||
return spi_write_then_read(spi, buf, 2, NULL, 0);
|
||||
}
|
||||
|
||||
static int bme680_regmap_spi_read(void *context, const void *reg,
|
||||
size_t reg_size, void *val, size_t val_size)
|
||||
{
|
||||
struct spi_device *spi = context;
|
||||
|
||||
return spi_write_then_read(spi, reg, reg_size, val, val_size);
|
||||
}
|
||||
|
||||
static struct regmap_bus bme680_regmap_bus = {
|
||||
.write = bme680_regmap_spi_write,
|
||||
.read = bme680_regmap_spi_read,
|
||||
.reg_format_endian_default = REGMAP_ENDIAN_BIG,
|
||||
.val_format_endian_default = REGMAP_ENDIAN_BIG,
|
||||
};
|
||||
|
||||
static int bme680_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
const struct spi_device_id *id = spi_get_device_id(spi);
|
||||
struct regmap *regmap;
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
spi->bits_per_word = 8;
|
||||
ret = spi_setup(spi);
|
||||
if (ret < 0) {
|
||||
dev_err(&spi->dev, "spi_setup failed!\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
regmap = devm_regmap_init(&spi->dev, &bme680_regmap_bus,
|
||||
&spi->dev, &bme680_regmap_config);
|
||||
if (IS_ERR(regmap)) {
|
||||
dev_err(&spi->dev, "Failed to register spi regmap %d\n",
|
||||
(int)PTR_ERR(regmap));
|
||||
return PTR_ERR(regmap);
|
||||
}
|
||||
|
||||
ret = regmap_write(regmap, BME680_REG_SOFT_RESET_SPI,
|
||||
BME680_CMD_SOFTRESET);
|
||||
if (ret < 0) {
|
||||
dev_err(&spi->dev, "Failed to reset chip\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* after power-on reset, Page 0(0x80-0xFF) of spi_mem_page is active */
|
||||
ret = regmap_read(regmap, BME680_REG_CHIP_SPI_ID, &val);
|
||||
if (ret < 0) {
|
||||
dev_err(&spi->dev, "Error reading SPI chip ID\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (val != BME680_CHIP_ID_VAL) {
|
||||
dev_err(&spi->dev, "Wrong chip ID, got %x expected %x\n",
|
||||
val, BME680_CHIP_ID_VAL);
|
||||
return -ENODEV;
|
||||
}
|
||||
/*
|
||||
* select Page 1 of spi_mem_page to enable access to
|
||||
* to registers from address 0x00 to 0x7F.
|
||||
*/
|
||||
ret = regmap_write_bits(regmap, BME680_REG_STATUS,
|
||||
BME680_SPI_MEM_PAGE_BIT,
|
||||
BME680_SPI_MEM_PAGE_1_VAL);
|
||||
if (ret < 0) {
|
||||
dev_err(&spi->dev, "failed to set page 1 of spi_mem_page\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return bme680_core_probe(&spi->dev, regmap, id->name);
|
||||
}
|
||||
|
||||
static const struct spi_device_id bme680_spi_id[] = {
|
||||
{"bme680", 0},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, bme680_spi_id);
|
||||
|
||||
static const struct acpi_device_id bme680_acpi_match[] = {
|
||||
{"BME0680", 0},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, bme680_acpi_match);
|
||||
|
||||
static struct spi_driver bme680_spi_driver = {
|
||||
.driver = {
|
||||
.name = "bme680_spi",
|
||||
.acpi_match_table = ACPI_PTR(bme680_acpi_match),
|
||||
},
|
||||
.probe = bme680_spi_probe,
|
||||
.id_table = bme680_spi_id,
|
||||
};
|
||||
module_spi_driver(bme680_spi_driver);
|
||||
|
||||
MODULE_AUTHOR("Himanshu Jha <himanshujha199640@gmail.com>");
|
||||
MODULE_DESCRIPTION("Bosch BME680 SPI driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -380,8 +380,7 @@ void st_sensors_of_name_probe(struct device *dev,
|
||||
return;
|
||||
|
||||
/* The name from the OF match takes precedence if present */
|
||||
strncpy(name, of_id->data, len);
|
||||
name[len - 1] = '\0';
|
||||
strlcpy(name, of_id->data, len);
|
||||
}
|
||||
EXPORT_SYMBOL(st_sensors_of_name_probe);
|
||||
#else
|
||||
|
@ -59,6 +59,39 @@ struct quad8_iio {
|
||||
unsigned int base;
|
||||
};
|
||||
|
||||
#define QUAD8_REG_CHAN_OP 0x11
|
||||
#define QUAD8_REG_INDEX_INPUT_LEVELS 0x16
|
||||
/* Borrow Toggle flip-flop */
|
||||
#define QUAD8_FLAG_BT BIT(0)
|
||||
/* Carry Toggle flip-flop */
|
||||
#define QUAD8_FLAG_CT BIT(1)
|
||||
/* Error flag */
|
||||
#define QUAD8_FLAG_E BIT(4)
|
||||
/* Up/Down flag */
|
||||
#define QUAD8_FLAG_UD BIT(5)
|
||||
/* Reset and Load Signal Decoders */
|
||||
#define QUAD8_CTR_RLD 0x00
|
||||
/* Counter Mode Register */
|
||||
#define QUAD8_CTR_CMR 0x20
|
||||
/* Input / Output Control Register */
|
||||
#define QUAD8_CTR_IOR 0x40
|
||||
/* Index Control Register */
|
||||
#define QUAD8_CTR_IDR 0x60
|
||||
/* Reset Byte Pointer (three byte data pointer) */
|
||||
#define QUAD8_RLD_RESET_BP 0x01
|
||||
/* Reset Counter */
|
||||
#define QUAD8_RLD_RESET_CNTR 0x02
|
||||
/* Reset Borrow Toggle, Carry Toggle, Compare Toggle, and Sign flags */
|
||||
#define QUAD8_RLD_RESET_FLAGS 0x04
|
||||
/* Reset Error flag */
|
||||
#define QUAD8_RLD_RESET_E 0x06
|
||||
/* Preset Register to Counter */
|
||||
#define QUAD8_RLD_PRESET_CNTR 0x08
|
||||
/* Transfer Counter to Output Latch */
|
||||
#define QUAD8_RLD_CNTR_OUT 0x10
|
||||
#define QUAD8_CHAN_OP_ENABLE_COUNTERS 0x00
|
||||
#define QUAD8_CHAN_OP_RESET_COUNTERS 0x01
|
||||
|
||||
static int quad8_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val, int *val2, long mask)
|
||||
{
|
||||
@ -72,19 +105,21 @@ static int quad8_read_raw(struct iio_dev *indio_dev,
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (chan->type == IIO_INDEX) {
|
||||
*val = !!(inb(priv->base + 0x16) & BIT(chan->channel));
|
||||
*val = !!(inb(priv->base + QUAD8_REG_INDEX_INPUT_LEVELS)
|
||||
& BIT(chan->channel));
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
flags = inb(base_offset + 1);
|
||||
borrow = flags & BIT(0);
|
||||
carry = !!(flags & BIT(1));
|
||||
borrow = flags & QUAD8_FLAG_BT;
|
||||
carry = !!(flags & QUAD8_FLAG_CT);
|
||||
|
||||
/* Borrow XOR Carry effectively doubles count range */
|
||||
*val = (borrow ^ carry) << 24;
|
||||
|
||||
/* Reset Byte Pointer; transfer Counter to Output Latch */
|
||||
outb(0x11, base_offset + 1);
|
||||
outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP | QUAD8_RLD_CNTR_OUT,
|
||||
base_offset + 1);
|
||||
|
||||
for (i = 0; i < 3; i++)
|
||||
*val |= (unsigned int)inb(base_offset) << (8 * i);
|
||||
@ -120,17 +155,17 @@ static int quad8_write_raw(struct iio_dev *indio_dev,
|
||||
return -EINVAL;
|
||||
|
||||
/* Reset Byte Pointer */
|
||||
outb(0x01, base_offset + 1);
|
||||
outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
|
||||
|
||||
/* Counter can only be set via Preset Register */
|
||||
for (i = 0; i < 3; i++)
|
||||
outb(val >> (8 * i), base_offset);
|
||||
|
||||
/* Transfer Preset Register to Counter */
|
||||
outb(0x08, base_offset + 1);
|
||||
outb(QUAD8_CTR_RLD | QUAD8_RLD_PRESET_CNTR, base_offset + 1);
|
||||
|
||||
/* Reset Byte Pointer */
|
||||
outb(0x01, base_offset + 1);
|
||||
outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
|
||||
|
||||
/* Set Preset Register back to original value */
|
||||
val = priv->preset[chan->channel];
|
||||
@ -138,9 +173,9 @@ static int quad8_write_raw(struct iio_dev *indio_dev,
|
||||
outb(val >> (8 * i), base_offset);
|
||||
|
||||
/* Reset Borrow, Carry, Compare, and Sign flags */
|
||||
outb(0x02, base_offset + 1);
|
||||
outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_FLAGS, base_offset + 1);
|
||||
/* Reset Error flag */
|
||||
outb(0x06, base_offset + 1);
|
||||
outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_E, base_offset + 1);
|
||||
|
||||
return 0;
|
||||
case IIO_CHAN_INFO_ENABLE:
|
||||
@ -153,7 +188,7 @@ static int quad8_write_raw(struct iio_dev *indio_dev,
|
||||
ior_cfg = val | priv->preset_enable[chan->channel] << 1;
|
||||
|
||||
/* Load I/O control configuration */
|
||||
outb(0x40 | ior_cfg, base_offset + 1);
|
||||
outb(QUAD8_CTR_IOR | ior_cfg, base_offset + 1);
|
||||
|
||||
return 0;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
@ -217,7 +252,7 @@ static ssize_t quad8_write_preset(struct iio_dev *indio_dev, uintptr_t private,
|
||||
priv->preset[chan->channel] = preset;
|
||||
|
||||
/* Reset Byte Pointer */
|
||||
outb(0x01, base_offset + 1);
|
||||
outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
|
||||
|
||||
/* Set Preset Register */
|
||||
for (i = 0; i < 3; i++)
|
||||
@ -258,7 +293,7 @@ static ssize_t quad8_write_set_to_preset_on_index(struct iio_dev *indio_dev,
|
||||
(unsigned int)preset_enable << 1;
|
||||
|
||||
/* Load I/O control configuration to Input / Output Control Register */
|
||||
outb(0x40 | ior_cfg, base_offset);
|
||||
outb(QUAD8_CTR_IOR | ior_cfg, base_offset);
|
||||
|
||||
return len;
|
||||
}
|
||||
@ -274,7 +309,7 @@ static int quad8_get_noise_error(struct iio_dev *indio_dev,
|
||||
struct quad8_iio *const priv = iio_priv(indio_dev);
|
||||
const int base_offset = priv->base + 2 * chan->channel + 1;
|
||||
|
||||
return !!(inb(base_offset) & BIT(4));
|
||||
return !!(inb(base_offset) & QUAD8_FLAG_E);
|
||||
}
|
||||
|
||||
static const struct iio_enum quad8_noise_error_enum = {
|
||||
@ -294,7 +329,7 @@ static int quad8_get_count_direction(struct iio_dev *indio_dev,
|
||||
struct quad8_iio *const priv = iio_priv(indio_dev);
|
||||
const int base_offset = priv->base + 2 * chan->channel + 1;
|
||||
|
||||
return !!(inb(base_offset) & BIT(5));
|
||||
return !!(inb(base_offset) & QUAD8_FLAG_UD);
|
||||
}
|
||||
|
||||
static const struct iio_enum quad8_count_direction_enum = {
|
||||
@ -324,7 +359,7 @@ static int quad8_set_count_mode(struct iio_dev *indio_dev,
|
||||
mode_cfg |= (priv->quadrature_scale[chan->channel] + 1) << 3;
|
||||
|
||||
/* Load mode configuration to Counter Mode Register */
|
||||
outb(0x20 | mode_cfg, base_offset);
|
||||
outb(QUAD8_CTR_CMR | mode_cfg, base_offset);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -364,7 +399,7 @@ static int quad8_set_synchronous_mode(struct iio_dev *indio_dev,
|
||||
priv->synchronous_mode[chan->channel] = synchronous_mode;
|
||||
|
||||
/* Load Index Control configuration to Index Control Register */
|
||||
outb(0x60 | idr_cfg, base_offset);
|
||||
outb(QUAD8_CTR_IDR | idr_cfg, base_offset);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -410,7 +445,7 @@ static int quad8_set_quadrature_mode(struct iio_dev *indio_dev,
|
||||
priv->quadrature_mode[chan->channel] = quadrature_mode;
|
||||
|
||||
/* Load mode configuration to Counter Mode Register */
|
||||
outb(0x20 | mode_cfg, base_offset);
|
||||
outb(QUAD8_CTR_CMR | mode_cfg, base_offset);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -446,7 +481,7 @@ static int quad8_set_index_polarity(struct iio_dev *indio_dev,
|
||||
priv->index_polarity[chan->channel] = index_polarity;
|
||||
|
||||
/* Load Index Control configuration to Index Control Register */
|
||||
outb(0x60 | idr_cfg, base_offset);
|
||||
outb(QUAD8_CTR_IDR | idr_cfg, base_offset);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -556,28 +591,28 @@ static int quad8_probe(struct device *dev, unsigned int id)
|
||||
priv->base = base[id];
|
||||
|
||||
/* Reset all counters and disable interrupt function */
|
||||
outb(0x01, base[id] + 0x11);
|
||||
outb(QUAD8_CHAN_OP_RESET_COUNTERS, base[id] + QUAD8_REG_CHAN_OP);
|
||||
/* Set initial configuration for all counters */
|
||||
for (i = 0; i < QUAD8_NUM_COUNTERS; i++) {
|
||||
base_offset = base[id] + 2 * i;
|
||||
/* Reset Byte Pointer */
|
||||
outb(0x01, base_offset + 1);
|
||||
outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
|
||||
/* Reset Preset Register */
|
||||
for (j = 0; j < 3; j++)
|
||||
outb(0x00, base_offset);
|
||||
/* Reset Borrow, Carry, Compare, and Sign flags */
|
||||
outb(0x04, base_offset + 1);
|
||||
outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_FLAGS, base_offset + 1);
|
||||
/* Reset Error flag */
|
||||
outb(0x06, base_offset + 1);
|
||||
outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_E, base_offset + 1);
|
||||
/* Binary encoding; Normal count; non-quadrature mode */
|
||||
outb(0x20, base_offset + 1);
|
||||
outb(QUAD8_CTR_CMR, base_offset + 1);
|
||||
/* Disable A and B inputs; preset on index; FLG1 as Carry */
|
||||
outb(0x40, base_offset + 1);
|
||||
outb(QUAD8_CTR_IOR, base_offset + 1);
|
||||
/* Disable index function; negative index polarity */
|
||||
outb(0x60, base_offset + 1);
|
||||
outb(QUAD8_CTR_IDR, base_offset + 1);
|
||||
}
|
||||
/* Enable all counters */
|
||||
outb(0x00, base[id] + 0x11);
|
||||
outb(QUAD8_CHAN_OP_ENABLE_COUNTERS, base[id] + QUAD8_REG_CHAN_OP);
|
||||
|
||||
return devm_iio_device_register(dev, indio_dev);
|
||||
}
|
||||
|
@ -167,6 +167,16 @@ config AD5755
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ad5755.
|
||||
|
||||
config AD5758
|
||||
tristate "Analog Devices AD5758 DAC driver"
|
||||
depends on SPI_MASTER
|
||||
help
|
||||
Say yes here to build support for Analog Devices AD5758 single channel
|
||||
Digital to Analog Converter.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ad5758.
|
||||
|
||||
config AD5761
|
||||
tristate "Analog Devices AD5761/61R/21/21R DAC driver"
|
||||
depends on SPI_MASTER
|
||||
|
@ -16,6 +16,7 @@ obj-$(CONFIG_AD5592R_BASE) += ad5592r-base.o
|
||||
obj-$(CONFIG_AD5592R) += ad5592r.o
|
||||
obj-$(CONFIG_AD5593R) += ad5593r.o
|
||||
obj-$(CONFIG_AD5755) += ad5755.o
|
||||
obj-$(CONFIG_AD5755) += ad5758.o
|
||||
obj-$(CONFIG_AD5761) += ad5761.o
|
||||
obj-$(CONFIG_AD5764) += ad5764.o
|
||||
obj-$(CONFIG_AD5791) += ad5791.o
|
||||
|
@ -221,6 +221,7 @@ static struct iio_chan_spec name[] = { \
|
||||
AD5868_CHANNEL(7, 7, bits, _shift), \
|
||||
}
|
||||
|
||||
DECLARE_AD5693_CHANNELS(ad5311r_channels, 10, 6);
|
||||
DECLARE_AD5676_CHANNELS(ad5672_channels, 12, 4);
|
||||
DECLARE_AD5676_CHANNELS(ad5676_channels, 16, 0);
|
||||
DECLARE_AD5686_CHANNELS(ad5684_channels, 12, 4);
|
||||
@ -231,6 +232,12 @@ DECLARE_AD5693_CHANNELS(ad5692r_channels, 14, 2);
|
||||
DECLARE_AD5693_CHANNELS(ad5691r_channels, 12, 4);
|
||||
|
||||
static const struct ad5686_chip_info ad5686_chip_info_tbl[] = {
|
||||
[ID_AD5311R] = {
|
||||
.channels = ad5311r_channels,
|
||||
.int_vref_mv = 2500,
|
||||
.num_channels = 1,
|
||||
.regmap_type = AD5693_REGMAP,
|
||||
},
|
||||
[ID_AD5671R] = {
|
||||
.channels = ad5672_channels,
|
||||
.int_vref_mv = 2500,
|
||||
|
@ -45,6 +45,7 @@
|
||||
* ad5686_supported_device_ids:
|
||||
*/
|
||||
enum ad5686_supported_device_ids {
|
||||
ID_AD5311R,
|
||||
ID_AD5671R,
|
||||
ID_AD5672R,
|
||||
ID_AD5675R,
|
||||
|
@ -71,6 +71,7 @@ static int ad5686_i2c_remove(struct i2c_client *i2c)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id ad5686_i2c_id[] = {
|
||||
{"ad5311r", ID_AD5311R},
|
||||
{"ad5671r", ID_AD5671R},
|
||||
{"ad5675r", ID_AD5675R},
|
||||
{"ad5691r", ID_AD5691R},
|
||||
|
897
drivers/iio/dac/ad5758.c
Normal file
897
drivers/iio/dac/ad5758.c
Normal file
@ -0,0 +1,897 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* AD5758 Digital to analog converters driver
|
||||
*
|
||||
* Copyright 2018 Analog Devices Inc.
|
||||
*
|
||||
* TODO: Currently CRC is not supported in this driver
|
||||
*/
|
||||
#include <linux/bsearch.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
/* AD5758 registers definition */
|
||||
#define AD5758_NOP 0x00
|
||||
#define AD5758_DAC_INPUT 0x01
|
||||
#define AD5758_DAC_OUTPUT 0x02
|
||||
#define AD5758_CLEAR_CODE 0x03
|
||||
#define AD5758_USER_GAIN 0x04
|
||||
#define AD5758_USER_OFFSET 0x05
|
||||
#define AD5758_DAC_CONFIG 0x06
|
||||
#define AD5758_SW_LDAC 0x07
|
||||
#define AD5758_KEY 0x08
|
||||
#define AD5758_GP_CONFIG1 0x09
|
||||
#define AD5758_GP_CONFIG2 0x0A
|
||||
#define AD5758_DCDC_CONFIG1 0x0B
|
||||
#define AD5758_DCDC_CONFIG2 0x0C
|
||||
#define AD5758_WDT_CONFIG 0x0F
|
||||
#define AD5758_DIGITAL_DIAG_CONFIG 0x10
|
||||
#define AD5758_ADC_CONFIG 0x11
|
||||
#define AD5758_FAULT_PIN_CONFIG 0x12
|
||||
#define AD5758_TWO_STAGE_READBACK_SELECT 0x13
|
||||
#define AD5758_DIGITAL_DIAG_RESULTS 0x14
|
||||
#define AD5758_ANALOG_DIAG_RESULTS 0x15
|
||||
#define AD5758_STATUS 0x16
|
||||
#define AD5758_CHIP_ID 0x17
|
||||
#define AD5758_FREQ_MONITOR 0x18
|
||||
#define AD5758_DEVICE_ID_0 0x19
|
||||
#define AD5758_DEVICE_ID_1 0x1A
|
||||
#define AD5758_DEVICE_ID_2 0x1B
|
||||
#define AD5758_DEVICE_ID_3 0x1C
|
||||
|
||||
/* AD5758_DAC_CONFIG */
|
||||
#define AD5758_DAC_CONFIG_RANGE_MSK GENMASK(3, 0)
|
||||
#define AD5758_DAC_CONFIG_RANGE_MODE(x) (((x) & 0xF) << 0)
|
||||
#define AD5758_DAC_CONFIG_INT_EN_MSK BIT(5)
|
||||
#define AD5758_DAC_CONFIG_INT_EN_MODE(x) (((x) & 0x1) << 5)
|
||||
#define AD5758_DAC_CONFIG_OUT_EN_MSK BIT(6)
|
||||
#define AD5758_DAC_CONFIG_OUT_EN_MODE(x) (((x) & 0x1) << 6)
|
||||
#define AD5758_DAC_CONFIG_SR_EN_MSK BIT(8)
|
||||
#define AD5758_DAC_CONFIG_SR_EN_MODE(x) (((x) & 0x1) << 8)
|
||||
#define AD5758_DAC_CONFIG_SR_CLOCK_MSK GENMASK(12, 9)
|
||||
#define AD5758_DAC_CONFIG_SR_CLOCK_MODE(x) (((x) & 0xF) << 9)
|
||||
#define AD5758_DAC_CONFIG_SR_STEP_MSK GENMASK(15, 13)
|
||||
#define AD5758_DAC_CONFIG_SR_STEP_MODE(x) (((x) & 0x7) << 13)
|
||||
|
||||
/* AD5758_KEY */
|
||||
#define AD5758_KEY_CODE_RESET_1 0x15FA
|
||||
#define AD5758_KEY_CODE_RESET_2 0xAF51
|
||||
#define AD5758_KEY_CODE_SINGLE_ADC_CONV 0x1ADC
|
||||
#define AD5758_KEY_CODE_RESET_WDT 0x0D06
|
||||
#define AD5758_KEY_CODE_CALIB_MEM_REFRESH 0xFCBA
|
||||
|
||||
/* AD5758_DCDC_CONFIG1 */
|
||||
#define AD5758_DCDC_CONFIG1_DCDC_VPROG_MSK GENMASK(4, 0)
|
||||
#define AD5758_DCDC_CONFIG1_DCDC_VPROG_MODE(x) (((x) & 0x1F) << 0)
|
||||
#define AD5758_DCDC_CONFIG1_DCDC_MODE_MSK GENMASK(6, 5)
|
||||
#define AD5758_DCDC_CONFIG1_DCDC_MODE_MODE(x) (((x) & 0x3) << 5)
|
||||
#define AD5758_DCDC_CONFIG1_PROT_SW_EN_MSK BIT(7)
|
||||
#define AD5758_DCDC_CONFIG1_PROT_SW_EN_MODE(x) (((x) & 0x1) << 7)
|
||||
|
||||
/* AD5758_DCDC_CONFIG2 */
|
||||
#define AD5758_DCDC_CONFIG2_ILIMIT_MSK GENMASK(3, 1)
|
||||
#define AD5758_DCDC_CONFIG2_ILIMIT_MODE(x) (((x) & 0x7) << 1)
|
||||
#define AD5758_DCDC_CONFIG2_INTR_SAT_3WI_MSK BIT(11)
|
||||
#define AD5758_DCDC_CONFIG2_BUSY_3WI_MSK BIT(12)
|
||||
|
||||
/* AD5758_DIGITAL_DIAG_RESULTS */
|
||||
#define AD5758_CAL_MEM_UNREFRESHED_MSK BIT(15)
|
||||
|
||||
#define AD5758_WR_FLAG_MSK(x) (0x80 | ((x) & 0x1F))
|
||||
|
||||
#define AD5758_FULL_SCALE_MICRO 65535000000ULL
|
||||
|
||||
/**
|
||||
* struct ad5758_state - driver instance specific data
|
||||
* @spi: spi_device
|
||||
* @lock: mutex lock
|
||||
* @out_range: struct which stores the output range
|
||||
* @dc_dc_mode: variable which stores the mode of operation
|
||||
* @dc_dc_ilim: variable which stores the dc-to-dc converter current limit
|
||||
* @slew_time: variable which stores the target slew time
|
||||
* @pwr_down: variable which contains whether a channel is powered down or not
|
||||
* @data: spi transfer buffers
|
||||
*/
|
||||
|
||||
struct ad5758_range {
|
||||
int reg;
|
||||
int min;
|
||||
int max;
|
||||
};
|
||||
|
||||
struct ad5758_state {
|
||||
struct spi_device *spi;
|
||||
struct mutex lock;
|
||||
struct ad5758_range out_range;
|
||||
unsigned int dc_dc_mode;
|
||||
unsigned int dc_dc_ilim;
|
||||
unsigned int slew_time;
|
||||
bool pwr_down;
|
||||
__be32 d32[3];
|
||||
};
|
||||
|
||||
/**
|
||||
* Output ranges corresponding to bits [3:0] from DAC_CONFIG register
|
||||
* 0000: 0 V to 5 V voltage range
|
||||
* 0001: 0 V to 10 V voltage range
|
||||
* 0010: ±5 V voltage range
|
||||
* 0011: ±10 V voltage range
|
||||
* 1000: 0 mA to 20 mA current range
|
||||
* 1001: 0 mA to 24 mA current range
|
||||
* 1010: 4 mA to 20 mA current range
|
||||
* 1011: ±20 mA current range
|
||||
* 1100: ±24 mA current range
|
||||
* 1101: -1 mA to +22 mA current range
|
||||
*/
|
||||
enum ad5758_output_range {
|
||||
AD5758_RANGE_0V_5V,
|
||||
AD5758_RANGE_0V_10V,
|
||||
AD5758_RANGE_PLUSMINUS_5V,
|
||||
AD5758_RANGE_PLUSMINUS_10V,
|
||||
AD5758_RANGE_0mA_20mA = 8,
|
||||
AD5758_RANGE_0mA_24mA,
|
||||
AD5758_RANGE_4mA_24mA,
|
||||
AD5758_RANGE_PLUSMINUS_20mA,
|
||||
AD5758_RANGE_PLUSMINUS_24mA,
|
||||
AD5758_RANGE_MINUS_1mA_PLUS_22mA,
|
||||
};
|
||||
|
||||
enum ad5758_dc_dc_mode {
|
||||
AD5758_DCDC_MODE_POWER_OFF,
|
||||
AD5758_DCDC_MODE_DPC_CURRENT,
|
||||
AD5758_DCDC_MODE_DPC_VOLTAGE,
|
||||
AD5758_DCDC_MODE_PPC_CURRENT,
|
||||
};
|
||||
|
||||
static const struct ad5758_range ad5758_voltage_range[] = {
|
||||
{ AD5758_RANGE_0V_5V, 0, 5000000 },
|
||||
{ AD5758_RANGE_0V_10V, 0, 10000000 },
|
||||
{ AD5758_RANGE_PLUSMINUS_5V, -5000000, 5000000 },
|
||||
{ AD5758_RANGE_PLUSMINUS_10V, -10000000, 10000000 }
|
||||
};
|
||||
|
||||
static const struct ad5758_range ad5758_current_range[] = {
|
||||
{ AD5758_RANGE_0mA_20mA, 0, 20000},
|
||||
{ AD5758_RANGE_0mA_24mA, 0, 24000 },
|
||||
{ AD5758_RANGE_4mA_24mA, 4, 24000 },
|
||||
{ AD5758_RANGE_PLUSMINUS_20mA, -20000, 20000 },
|
||||
{ AD5758_RANGE_PLUSMINUS_24mA, -24000, 24000 },
|
||||
{ AD5758_RANGE_MINUS_1mA_PLUS_22mA, -1000, 22000 },
|
||||
};
|
||||
|
||||
static const int ad5758_sr_clk[16] = {
|
||||
240000, 200000, 150000, 128000, 64000, 32000, 16000, 8000, 4000, 2000,
|
||||
1000, 512, 256, 128, 64, 16
|
||||
};
|
||||
|
||||
static const int ad5758_sr_step[8] = {
|
||||
4, 12, 64, 120, 256, 500, 1820, 2048
|
||||
};
|
||||
|
||||
static const int ad5758_dc_dc_ilim[6] = {
|
||||
150000, 200000, 250000, 300000, 350000, 400000
|
||||
};
|
||||
|
||||
static int ad5758_spi_reg_read(struct ad5758_state *st, unsigned int addr)
|
||||
{
|
||||
struct spi_transfer t[] = {
|
||||
{
|
||||
.tx_buf = &st->d32[0],
|
||||
.len = 4,
|
||||
.cs_change = 1,
|
||||
}, {
|
||||
.tx_buf = &st->d32[1],
|
||||
.rx_buf = &st->d32[2],
|
||||
.len = 4,
|
||||
},
|
||||
};
|
||||
int ret;
|
||||
|
||||
st->d32[0] = cpu_to_be32(
|
||||
(AD5758_WR_FLAG_MSK(AD5758_TWO_STAGE_READBACK_SELECT) << 24) |
|
||||
(addr << 8));
|
||||
st->d32[1] = cpu_to_be32(AD5758_WR_FLAG_MSK(AD5758_NOP) << 24);
|
||||
|
||||
ret = spi_sync_transfer(st->spi, t, ARRAY_SIZE(t));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return (be32_to_cpu(st->d32[2]) >> 8) & 0xFFFF;
|
||||
}
|
||||
|
||||
static int ad5758_spi_reg_write(struct ad5758_state *st,
|
||||
unsigned int addr,
|
||||
unsigned int val)
|
||||
{
|
||||
st->d32[0] = cpu_to_be32((AD5758_WR_FLAG_MSK(addr) << 24) |
|
||||
((val & 0xFFFF) << 8));
|
||||
|
||||
return spi_write(st->spi, &st->d32[0], sizeof(st->d32[0]));
|
||||
}
|
||||
|
||||
static int ad5758_spi_write_mask(struct ad5758_state *st,
|
||||
unsigned int addr,
|
||||
unsigned long int mask,
|
||||
unsigned int val)
|
||||
{
|
||||
int regval;
|
||||
|
||||
regval = ad5758_spi_reg_read(st, addr);
|
||||
if (regval < 0)
|
||||
return regval;
|
||||
|
||||
regval &= ~mask;
|
||||
regval |= val;
|
||||
|
||||
return ad5758_spi_reg_write(st, addr, regval);
|
||||
}
|
||||
|
||||
static int cmpfunc(const void *a, const void *b)
|
||||
{
|
||||
return *(int *)a - *(int *)b;
|
||||
}
|
||||
|
||||
static int ad5758_find_closest_match(const int *array,
|
||||
unsigned int size, int val)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
if (val <= array[i])
|
||||
return i;
|
||||
}
|
||||
|
||||
return size - 1;
|
||||
}
|
||||
|
||||
static int ad5758_wait_for_task_complete(struct ad5758_state *st,
|
||||
unsigned int reg,
|
||||
unsigned int mask)
|
||||
{
|
||||
unsigned int timeout;
|
||||
int ret;
|
||||
|
||||
timeout = 10;
|
||||
do {
|
||||
ret = ad5758_spi_reg_read(st, reg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (!(ret & mask))
|
||||
return 0;
|
||||
|
||||
usleep_range(100, 1000);
|
||||
} while (--timeout);
|
||||
|
||||
dev_err(&st->spi->dev,
|
||||
"Error reading bit 0x%x in 0x%x register\n", mask, reg);
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static int ad5758_calib_mem_refresh(struct ad5758_state *st)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ad5758_spi_reg_write(st, AD5758_KEY,
|
||||
AD5758_KEY_CODE_CALIB_MEM_REFRESH);
|
||||
if (ret < 0) {
|
||||
dev_err(&st->spi->dev,
|
||||
"Failed to initiate a calibration memory refresh\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Wait to allow time for the internal calibrations to complete */
|
||||
return ad5758_wait_for_task_complete(st, AD5758_DIGITAL_DIAG_RESULTS,
|
||||
AD5758_CAL_MEM_UNREFRESHED_MSK);
|
||||
}
|
||||
|
||||
static int ad5758_soft_reset(struct ad5758_state *st)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ad5758_spi_reg_write(st, AD5758_KEY, AD5758_KEY_CODE_RESET_1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = ad5758_spi_reg_write(st, AD5758_KEY, AD5758_KEY_CODE_RESET_2);
|
||||
|
||||
/* Perform a software reset and wait at least 100us */
|
||||
usleep_range(100, 1000);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad5758_set_dc_dc_conv_mode(struct ad5758_state *st,
|
||||
enum ad5758_dc_dc_mode mode)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ad5758_spi_write_mask(st, AD5758_DCDC_CONFIG1,
|
||||
AD5758_DCDC_CONFIG1_DCDC_MODE_MSK,
|
||||
AD5758_DCDC_CONFIG1_DCDC_MODE_MODE(mode));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Poll the BUSY_3WI bit in the DCDC_CONFIG2 register until it is 0.
|
||||
* This allows the 3-wire interface communication to complete.
|
||||
*/
|
||||
ret = ad5758_wait_for_task_complete(st, AD5758_DCDC_CONFIG2,
|
||||
AD5758_DCDC_CONFIG2_BUSY_3WI_MSK);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
st->dc_dc_mode = mode;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad5758_set_dc_dc_ilim(struct ad5758_state *st, unsigned int ilim)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ad5758_spi_write_mask(st, AD5758_DCDC_CONFIG2,
|
||||
AD5758_DCDC_CONFIG2_ILIMIT_MSK,
|
||||
AD5758_DCDC_CONFIG2_ILIMIT_MODE(ilim));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
/*
|
||||
* Poll the BUSY_3WI bit in the DCDC_CONFIG2 register until it is 0.
|
||||
* This allows the 3-wire interface communication to complete.
|
||||
*/
|
||||
return ad5758_wait_for_task_complete(st, AD5758_DCDC_CONFIG2,
|
||||
AD5758_DCDC_CONFIG2_BUSY_3WI_MSK);
|
||||
}
|
||||
|
||||
static int ad5758_slew_rate_set(struct ad5758_state *st,
|
||||
unsigned int sr_clk_idx,
|
||||
unsigned int sr_step_idx)
|
||||
{
|
||||
unsigned int mode;
|
||||
unsigned long int mask;
|
||||
int ret;
|
||||
|
||||
mask = AD5758_DAC_CONFIG_SR_EN_MSK |
|
||||
AD5758_DAC_CONFIG_SR_CLOCK_MSK |
|
||||
AD5758_DAC_CONFIG_SR_STEP_MSK;
|
||||
mode = AD5758_DAC_CONFIG_SR_EN_MODE(1) |
|
||||
AD5758_DAC_CONFIG_SR_STEP_MODE(sr_step_idx) |
|
||||
AD5758_DAC_CONFIG_SR_CLOCK_MODE(sr_clk_idx);
|
||||
|
||||
ret = ad5758_spi_write_mask(st, AD5758_DAC_CONFIG, mask, mode);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Wait to allow time for the internal calibrations to complete */
|
||||
return ad5758_wait_for_task_complete(st, AD5758_DIGITAL_DIAG_RESULTS,
|
||||
AD5758_CAL_MEM_UNREFRESHED_MSK);
|
||||
}
|
||||
|
||||
static int ad5758_slew_rate_config(struct ad5758_state *st)
|
||||
{
|
||||
unsigned int sr_clk_idx, sr_step_idx;
|
||||
int i, res;
|
||||
s64 diff_new, diff_old;
|
||||
u64 sr_step, calc_slew_time;
|
||||
|
||||
sr_clk_idx = 0;
|
||||
sr_step_idx = 0;
|
||||
diff_old = S64_MAX;
|
||||
/*
|
||||
* The slew time can be determined by using the formula:
|
||||
* Slew Time = (Full Scale Out / (Step Size x Update Clk Freq))
|
||||
* where Slew time is expressed in microseconds
|
||||
* Given the desired slew time, the following algorithm determines the
|
||||
* best match for the step size and the update clock frequency.
|
||||
*/
|
||||
for (i = 0; i < ARRAY_SIZE(ad5758_sr_clk); i++) {
|
||||
/*
|
||||
* Go through each valid update clock freq and determine a raw
|
||||
* value for the step size by using the formula:
|
||||
* Step Size = Full Scale Out / (Update Clk Freq * Slew Time)
|
||||
*/
|
||||
sr_step = AD5758_FULL_SCALE_MICRO;
|
||||
do_div(sr_step, ad5758_sr_clk[i]);
|
||||
do_div(sr_step, st->slew_time);
|
||||
/*
|
||||
* After a raw value for step size was determined, find the
|
||||
* closest valid match
|
||||
*/
|
||||
res = ad5758_find_closest_match(ad5758_sr_step,
|
||||
ARRAY_SIZE(ad5758_sr_step),
|
||||
sr_step);
|
||||
/* Calculate the slew time */
|
||||
calc_slew_time = AD5758_FULL_SCALE_MICRO;
|
||||
do_div(calc_slew_time, ad5758_sr_step[res]);
|
||||
do_div(calc_slew_time, ad5758_sr_clk[i]);
|
||||
/*
|
||||
* Determine with how many microseconds the calculated slew time
|
||||
* is different from the desired slew time and store the diff
|
||||
* for the next iteration
|
||||
*/
|
||||
diff_new = abs(st->slew_time - calc_slew_time);
|
||||
if (diff_new < diff_old) {
|
||||
diff_old = diff_new;
|
||||
sr_clk_idx = i;
|
||||
sr_step_idx = res;
|
||||
}
|
||||
}
|
||||
|
||||
return ad5758_slew_rate_set(st, sr_clk_idx, sr_step_idx);
|
||||
}
|
||||
|
||||
static int ad5758_set_out_range(struct ad5758_state *st, int range)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ad5758_spi_write_mask(st, AD5758_DAC_CONFIG,
|
||||
AD5758_DAC_CONFIG_RANGE_MSK,
|
||||
AD5758_DAC_CONFIG_RANGE_MODE(range));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Wait to allow time for the internal calibrations to complete */
|
||||
return ad5758_wait_for_task_complete(st, AD5758_DIGITAL_DIAG_RESULTS,
|
||||
AD5758_CAL_MEM_UNREFRESHED_MSK);
|
||||
}
|
||||
|
||||
static int ad5758_fault_prot_switch_en(struct ad5758_state *st, bool enable)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ad5758_spi_write_mask(st, AD5758_DCDC_CONFIG1,
|
||||
AD5758_DCDC_CONFIG1_PROT_SW_EN_MSK,
|
||||
AD5758_DCDC_CONFIG1_PROT_SW_EN_MODE(enable));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
/*
|
||||
* Poll the BUSY_3WI bit in the DCDC_CONFIG2 register until it is 0.
|
||||
* This allows the 3-wire interface communication to complete.
|
||||
*/
|
||||
return ad5758_wait_for_task_complete(st, AD5758_DCDC_CONFIG2,
|
||||
AD5758_DCDC_CONFIG2_BUSY_3WI_MSK);
|
||||
}
|
||||
|
||||
static int ad5758_internal_buffers_en(struct ad5758_state *st, bool enable)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ad5758_spi_write_mask(st, AD5758_DAC_CONFIG,
|
||||
AD5758_DAC_CONFIG_INT_EN_MSK,
|
||||
AD5758_DAC_CONFIG_INT_EN_MODE(enable));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Wait to allow time for the internal calibrations to complete */
|
||||
return ad5758_wait_for_task_complete(st, AD5758_DIGITAL_DIAG_RESULTS,
|
||||
AD5758_CAL_MEM_UNREFRESHED_MSK);
|
||||
}
|
||||
|
||||
static int ad5758_reg_access(struct iio_dev *indio_dev,
|
||||
unsigned int reg,
|
||||
unsigned int writeval,
|
||||
unsigned int *readval)
|
||||
{
|
||||
struct ad5758_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&st->lock);
|
||||
if (readval) {
|
||||
ret = ad5758_spi_reg_read(st, reg);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&st->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*readval = ret;
|
||||
ret = 0;
|
||||
} else {
|
||||
ret = ad5758_spi_reg_write(st, reg, writeval);
|
||||
}
|
||||
mutex_unlock(&st->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad5758_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long info)
|
||||
{
|
||||
struct ad5758_state *st = iio_priv(indio_dev);
|
||||
int max, min, ret;
|
||||
|
||||
switch (info) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
mutex_lock(&st->lock);
|
||||
ret = ad5758_spi_reg_read(st, AD5758_DAC_INPUT);
|
||||
mutex_unlock(&st->lock);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = ret;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
min = st->out_range.min;
|
||||
max = st->out_range.max;
|
||||
*val = (max - min) / 1000;
|
||||
*val2 = 16;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
min = st->out_range.min;
|
||||
max = st->out_range.max;
|
||||
*val = ((min * (1 << 16)) / (max - min)) / 1000;
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int ad5758_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long info)
|
||||
{
|
||||
struct ad5758_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (info) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
mutex_lock(&st->lock);
|
||||
ret = ad5758_spi_reg_write(st, AD5758_DAC_INPUT, val);
|
||||
mutex_unlock(&st->lock);
|
||||
return ret;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t ad5758_read_powerdown(struct iio_dev *indio_dev,
|
||||
uintptr_t priv,
|
||||
const struct iio_chan_spec *chan,
|
||||
char *buf)
|
||||
{
|
||||
struct ad5758_state *st = iio_priv(indio_dev);
|
||||
|
||||
return sprintf(buf, "%d\n", st->pwr_down);
|
||||
}
|
||||
|
||||
static ssize_t ad5758_write_powerdown(struct iio_dev *indio_dev,
|
||||
uintptr_t priv,
|
||||
struct iio_chan_spec const *chan,
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
struct ad5758_state *st = iio_priv(indio_dev);
|
||||
bool pwr_down;
|
||||
unsigned int dcdc_config1_mode, dc_dc_mode, dac_config_mode, val;
|
||||
unsigned long int dcdc_config1_msk, dac_config_msk;
|
||||
int ret;
|
||||
|
||||
ret = kstrtobool(buf, &pwr_down);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&st->lock);
|
||||
if (pwr_down) {
|
||||
dc_dc_mode = AD5758_DCDC_MODE_POWER_OFF;
|
||||
val = 0;
|
||||
} else {
|
||||
dc_dc_mode = st->dc_dc_mode;
|
||||
val = 1;
|
||||
}
|
||||
|
||||
dcdc_config1_mode = AD5758_DCDC_CONFIG1_DCDC_MODE_MODE(dc_dc_mode) |
|
||||
AD5758_DCDC_CONFIG1_PROT_SW_EN_MODE(val);
|
||||
dcdc_config1_msk = AD5758_DCDC_CONFIG1_DCDC_MODE_MSK |
|
||||
AD5758_DCDC_CONFIG1_PROT_SW_EN_MSK;
|
||||
|
||||
ret = ad5758_spi_write_mask(st, AD5758_DCDC_CONFIG1,
|
||||
dcdc_config1_msk,
|
||||
dcdc_config1_mode);
|
||||
if (ret < 0)
|
||||
goto err_unlock;
|
||||
|
||||
dac_config_mode = AD5758_DAC_CONFIG_OUT_EN_MODE(val) |
|
||||
AD5758_DAC_CONFIG_INT_EN_MODE(val);
|
||||
dac_config_msk = AD5758_DAC_CONFIG_OUT_EN_MSK |
|
||||
AD5758_DAC_CONFIG_INT_EN_MSK;
|
||||
|
||||
ret = ad5758_spi_write_mask(st, AD5758_DAC_CONFIG,
|
||||
dac_config_msk,
|
||||
dac_config_mode);
|
||||
if (ret < 0)
|
||||
goto err_unlock;
|
||||
|
||||
st->pwr_down = pwr_down;
|
||||
|
||||
err_unlock:
|
||||
mutex_unlock(&st->lock);
|
||||
|
||||
return ret ? ret : len;
|
||||
}
|
||||
|
||||
static const struct iio_info ad5758_info = {
|
||||
.read_raw = ad5758_read_raw,
|
||||
.write_raw = ad5758_write_raw,
|
||||
.debugfs_reg_access = &ad5758_reg_access,
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec_ext_info ad5758_ext_info[] = {
|
||||
{
|
||||
.name = "powerdown",
|
||||
.read = ad5758_read_powerdown,
|
||||
.write = ad5758_write_powerdown,
|
||||
.shared = IIO_SHARED_BY_TYPE,
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
||||
#define AD5758_DAC_CHAN(_chan_type) { \
|
||||
.type = (_chan_type), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_SCALE) | \
|
||||
BIT(IIO_CHAN_INFO_OFFSET), \
|
||||
.indexed = 1, \
|
||||
.output = 1, \
|
||||
.ext_info = ad5758_ext_info, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec ad5758_voltage_ch[] = {
|
||||
AD5758_DAC_CHAN(IIO_VOLTAGE)
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec ad5758_current_ch[] = {
|
||||
AD5758_DAC_CHAN(IIO_CURRENT)
|
||||
};
|
||||
|
||||
static bool ad5758_is_valid_mode(enum ad5758_dc_dc_mode mode)
|
||||
{
|
||||
switch (mode) {
|
||||
case AD5758_DCDC_MODE_DPC_CURRENT:
|
||||
case AD5758_DCDC_MODE_DPC_VOLTAGE:
|
||||
case AD5758_DCDC_MODE_PPC_CURRENT:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static int ad5758_crc_disable(struct ad5758_state *st)
|
||||
{
|
||||
unsigned int mask;
|
||||
|
||||
mask = (AD5758_WR_FLAG_MSK(AD5758_DIGITAL_DIAG_CONFIG) << 24) | 0x5C3A;
|
||||
st->d32[0] = cpu_to_be32(mask);
|
||||
|
||||
return spi_write(st->spi, &st->d32[0], 4);
|
||||
}
|
||||
|
||||
static int ad5758_find_out_range(struct ad5758_state *st,
|
||||
const struct ad5758_range *range,
|
||||
unsigned int size,
|
||||
int min, int max)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
if ((min == range[i].min) && (max == range[i].max)) {
|
||||
st->out_range.reg = range[i].reg;
|
||||
st->out_range.min = range[i].min;
|
||||
st->out_range.max = range[i].max;
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int ad5758_parse_dt(struct ad5758_state *st)
|
||||
{
|
||||
unsigned int tmp, tmparray[2], size;
|
||||
const struct ad5758_range *range;
|
||||
int *index, ret;
|
||||
|
||||
st->dc_dc_ilim = 0;
|
||||
ret = device_property_read_u32(&st->spi->dev,
|
||||
"adi,dc-dc-ilim-microamp", &tmp);
|
||||
if (ret) {
|
||||
dev_dbg(&st->spi->dev,
|
||||
"Missing \"dc-dc-ilim-microamp\" property\n");
|
||||
} else {
|
||||
index = bsearch(&tmp, ad5758_dc_dc_ilim,
|
||||
ARRAY_SIZE(ad5758_dc_dc_ilim),
|
||||
sizeof(int), cmpfunc);
|
||||
if (!index)
|
||||
dev_dbg(&st->spi->dev, "dc-dc-ilim out of range\n");
|
||||
else
|
||||
st->dc_dc_ilim = index - ad5758_dc_dc_ilim;
|
||||
}
|
||||
|
||||
ret = device_property_read_u32(&st->spi->dev, "adi,dc-dc-mode",
|
||||
&st->dc_dc_mode);
|
||||
if (ret) {
|
||||
dev_err(&st->spi->dev, "Missing \"dc-dc-mode\" property\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!ad5758_is_valid_mode(st->dc_dc_mode))
|
||||
return -EINVAL;
|
||||
|
||||
if (st->dc_dc_mode == AD5758_DCDC_MODE_DPC_VOLTAGE) {
|
||||
ret = device_property_read_u32_array(&st->spi->dev,
|
||||
"adi,range-microvolt",
|
||||
tmparray, 2);
|
||||
if (ret) {
|
||||
dev_err(&st->spi->dev,
|
||||
"Missing \"range-microvolt\" property\n");
|
||||
return ret;
|
||||
}
|
||||
range = ad5758_voltage_range;
|
||||
size = ARRAY_SIZE(ad5758_voltage_range);
|
||||
} else {
|
||||
ret = device_property_read_u32_array(&st->spi->dev,
|
||||
"adi,range-microamp",
|
||||
tmparray, 2);
|
||||
if (ret) {
|
||||
dev_err(&st->spi->dev,
|
||||
"Missing \"range-microamp\" property\n");
|
||||
return ret;
|
||||
}
|
||||
range = ad5758_current_range;
|
||||
size = ARRAY_SIZE(ad5758_current_range);
|
||||
}
|
||||
|
||||
ret = ad5758_find_out_range(st, range, size, tmparray[0], tmparray[1]);
|
||||
if (ret) {
|
||||
dev_err(&st->spi->dev, "range invalid\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = device_property_read_u32(&st->spi->dev, "adi,slew-time-us", &tmp);
|
||||
if (ret) {
|
||||
dev_dbg(&st->spi->dev, "Missing \"slew-time-us\" property\n");
|
||||
st->slew_time = 0;
|
||||
} else {
|
||||
st->slew_time = tmp;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ad5758_init(struct ad5758_state *st)
|
||||
{
|
||||
int regval, ret;
|
||||
|
||||
/* Disable CRC checks */
|
||||
ret = ad5758_crc_disable(st);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Perform a software reset */
|
||||
ret = ad5758_soft_reset(st);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Disable CRC checks */
|
||||
ret = ad5758_crc_disable(st);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Perform a calibration memory refresh */
|
||||
ret = ad5758_calib_mem_refresh(st);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
regval = ad5758_spi_reg_read(st, AD5758_DIGITAL_DIAG_RESULTS);
|
||||
if (regval < 0)
|
||||
return regval;
|
||||
|
||||
/* Clear all the error flags */
|
||||
ret = ad5758_spi_reg_write(st, AD5758_DIGITAL_DIAG_RESULTS, regval);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Set the dc-to-dc current limit */
|
||||
ret = ad5758_set_dc_dc_ilim(st, st->dc_dc_ilim);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Configure the dc-to-dc controller mode */
|
||||
ret = ad5758_set_dc_dc_conv_mode(st, st->dc_dc_mode);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Configure the output range */
|
||||
ret = ad5758_set_out_range(st, st->out_range.reg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Enable Slew Rate Control, set the slew rate clock and step */
|
||||
if (st->slew_time) {
|
||||
ret = ad5758_slew_rate_config(st);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Enable the VIOUT fault protection switch (FPS is closed) */
|
||||
ret = ad5758_fault_prot_switch_en(st, 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Power up the DAC and internal (INT) amplifiers */
|
||||
ret = ad5758_internal_buffers_en(st, 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Enable VIOUT */
|
||||
return ad5758_spi_write_mask(st, AD5758_DAC_CONFIG,
|
||||
AD5758_DAC_CONFIG_OUT_EN_MSK,
|
||||
AD5758_DAC_CONFIG_OUT_EN_MODE(1));
|
||||
}
|
||||
|
||||
static int ad5758_probe(struct spi_device *spi)
|
||||
{
|
||||
struct ad5758_state *st;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
|
||||
st->spi = spi;
|
||||
|
||||
mutex_init(&st->lock);
|
||||
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->name = spi_get_device_id(spi)->name;
|
||||
indio_dev->info = &ad5758_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->num_channels = 1;
|
||||
|
||||
ret = ad5758_parse_dt(st);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (st->dc_dc_mode == AD5758_DCDC_MODE_DPC_VOLTAGE)
|
||||
indio_dev->channels = ad5758_voltage_ch;
|
||||
else
|
||||
indio_dev->channels = ad5758_current_ch;
|
||||
|
||||
ret = ad5758_init(st);
|
||||
if (ret < 0) {
|
||||
dev_err(&spi->dev, "AD5758 init failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return devm_iio_device_register(&st->spi->dev, indio_dev);
|
||||
}
|
||||
|
||||
static const struct spi_device_id ad5758_id[] = {
|
||||
{ "ad5758", 0 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ad5758_id);
|
||||
|
||||
static struct spi_driver ad5758_driver = {
|
||||
.driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
},
|
||||
.probe = ad5758_probe,
|
||||
.id_table = ad5758_id,
|
||||
};
|
||||
|
||||
module_spi_driver(ad5758_driver);
|
||||
|
||||
MODULE_AUTHOR("Stefan Popa <stefan.popa@analog.com>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD5758 DAC");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -87,12 +87,7 @@ static int ltc2632_read_raw(struct iio_dev *indio_dev,
|
||||
int *val2,
|
||||
long m)
|
||||
{
|
||||
struct ltc2632_chip_info *chip_info;
|
||||
|
||||
const struct ltc2632_state *st = iio_priv(indio_dev);
|
||||
const struct spi_device_id *spi_dev_id = spi_get_device_id(st->spi_dev);
|
||||
|
||||
chip_info = (struct ltc2632_chip_info *)spi_dev_id->driver_data;
|
||||
|
||||
switch (m) {
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
|
@ -97,9 +97,6 @@ static int dac5571_cmd_quad(struct dac5571_data *data, int channel, u16 val)
|
||||
|
||||
static int dac5571_pwrdwn_single(struct dac5571_data *data, int channel, u8 pwrdwn)
|
||||
{
|
||||
unsigned int shift;
|
||||
|
||||
shift = 12 - data->spec->resolution;
|
||||
data->buf[1] = 0;
|
||||
data->buf[0] = pwrdwn << DAC5571_SINGLE_PWRDWN_BITS;
|
||||
|
||||
@ -111,9 +108,6 @@ static int dac5571_pwrdwn_single(struct dac5571_data *data, int channel, u8 pwrd
|
||||
|
||||
static int dac5571_pwrdwn_quad(struct dac5571_data *data, int channel, u8 pwrdwn)
|
||||
{
|
||||
unsigned int shift;
|
||||
|
||||
shift = 16 - data->spec->resolution;
|
||||
data->buf[2] = 0;
|
||||
data->buf[1] = pwrdwn << DAC5571_QUAD_PWRDWN_BITS;
|
||||
data->buf[0] = (channel << DAC5571_CHANNEL_SELECT) |
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
@ -268,12 +269,24 @@ struct ad9523_state {
|
||||
struct regulator *reg;
|
||||
struct ad9523_platform_data *pdata;
|
||||
struct iio_chan_spec ad9523_channels[AD9523_NUM_CHAN];
|
||||
struct gpio_desc *pwrdown_gpio;
|
||||
struct gpio_desc *reset_gpio;
|
||||
struct gpio_desc *sync_gpio;
|
||||
|
||||
unsigned long vcxo_freq;
|
||||
unsigned long vco_freq;
|
||||
unsigned long vco_out_freq[AD9523_NUM_CLK_SRC];
|
||||
unsigned char vco_out_map[AD9523_NUM_CHAN_ALT_CLK_SRC];
|
||||
|
||||
/*
|
||||
* Lock for accessing device registers. Some operations require
|
||||
* multiple consecutive R/W operations, during which the device
|
||||
* shouldn't be interrupted. The buffers are also shared across
|
||||
* all operations so need to be protected on stand alone reads and
|
||||
* writes.
|
||||
*/
|
||||
struct mutex lock;
|
||||
|
||||
/*
|
||||
* DMA (thus cache coherency maintenance) requires the
|
||||
* transfer buffers to live in their own cache lines.
|
||||
@ -500,6 +513,7 @@ static ssize_t ad9523_store(struct device *dev,
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
|
||||
struct ad9523_state *st = iio_priv(indio_dev);
|
||||
bool state;
|
||||
int ret;
|
||||
|
||||
@ -508,9 +522,9 @@ static ssize_t ad9523_store(struct device *dev,
|
||||
return ret;
|
||||
|
||||
if (!state)
|
||||
return 0;
|
||||
return len;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
mutex_lock(&st->lock);
|
||||
switch ((u32)this_attr->address) {
|
||||
case AD9523_SYNC:
|
||||
ret = ad9523_sync(indio_dev);
|
||||
@ -521,7 +535,7 @@ static ssize_t ad9523_store(struct device *dev,
|
||||
default:
|
||||
ret = -ENODEV;
|
||||
}
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
mutex_unlock(&st->lock);
|
||||
|
||||
return ret ? ret : len;
|
||||
}
|
||||
@ -532,15 +546,16 @@ static ssize_t ad9523_show(struct device *dev,
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
|
||||
struct ad9523_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
mutex_lock(&st->lock);
|
||||
ret = ad9523_read(indio_dev, AD9523_READBACK_0);
|
||||
if (ret >= 0) {
|
||||
ret = sprintf(buf, "%d\n", !!(ret & (1 <<
|
||||
(u32)this_attr->address)));
|
||||
}
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
mutex_unlock(&st->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -623,9 +638,9 @@ static int ad9523_read_raw(struct iio_dev *indio_dev,
|
||||
unsigned int code;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
mutex_lock(&st->lock);
|
||||
ret = ad9523_read(indio_dev, AD9523_CHANNEL_CLOCK_DIST(chan->channel));
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
mutex_unlock(&st->lock);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
@ -642,7 +657,7 @@ static int ad9523_read_raw(struct iio_dev *indio_dev,
|
||||
code = (AD9523_CLK_DIST_DIV_PHASE_REV(ret) * 3141592) /
|
||||
AD9523_CLK_DIST_DIV_REV(ret);
|
||||
*val = code / 1000000;
|
||||
*val2 = (code % 1000000) * 10;
|
||||
*val2 = code % 1000000;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
default:
|
||||
return -EINVAL;
|
||||
@ -659,7 +674,7 @@ static int ad9523_write_raw(struct iio_dev *indio_dev,
|
||||
unsigned int reg;
|
||||
int ret, tmp, code;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
mutex_lock(&st->lock);
|
||||
ret = ad9523_read(indio_dev, AD9523_CHANNEL_CLOCK_DIST(chan->channel));
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
@ -705,7 +720,7 @@ static int ad9523_write_raw(struct iio_dev *indio_dev,
|
||||
|
||||
ad9523_io_update(indio_dev);
|
||||
out:
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
mutex_unlock(&st->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -713,9 +728,10 @@ static int ad9523_reg_access(struct iio_dev *indio_dev,
|
||||
unsigned int reg, unsigned int writeval,
|
||||
unsigned int *readval)
|
||||
{
|
||||
struct ad9523_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
mutex_lock(&st->lock);
|
||||
if (readval == NULL) {
|
||||
ret = ad9523_write(indio_dev, reg | AD9523_R1B, writeval);
|
||||
ad9523_io_update(indio_dev);
|
||||
@ -728,7 +744,7 @@ static int ad9523_reg_access(struct iio_dev *indio_dev,
|
||||
}
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
mutex_unlock(&st->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -967,6 +983,8 @@ static int ad9523_probe(struct spi_device *spi)
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
|
||||
mutex_init(&st->lock);
|
||||
|
||||
st->reg = devm_regulator_get(&spi->dev, "vcc");
|
||||
if (!IS_ERR(st->reg)) {
|
||||
ret = regulator_enable(st->reg);
|
||||
@ -974,6 +992,32 @@ static int ad9523_probe(struct spi_device *spi)
|
||||
return ret;
|
||||
}
|
||||
|
||||
st->pwrdown_gpio = devm_gpiod_get_optional(&spi->dev, "powerdown",
|
||||
GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(st->pwrdown_gpio)) {
|
||||
ret = PTR_ERR(st->pwrdown_gpio);
|
||||
goto error_disable_reg;
|
||||
}
|
||||
|
||||
st->reset_gpio = devm_gpiod_get_optional(&spi->dev, "reset",
|
||||
GPIOD_OUT_LOW);
|
||||
if (IS_ERR(st->reset_gpio)) {
|
||||
ret = PTR_ERR(st->reset_gpio);
|
||||
goto error_disable_reg;
|
||||
}
|
||||
|
||||
if (st->reset_gpio) {
|
||||
udelay(1);
|
||||
gpiod_direction_output(st->reset_gpio, 1);
|
||||
}
|
||||
|
||||
st->sync_gpio = devm_gpiod_get_optional(&spi->dev, "sync",
|
||||
GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(st->sync_gpio)) {
|
||||
ret = PTR_ERR(st->sync_gpio);
|
||||
goto error_disable_reg;
|
||||
}
|
||||
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
st->spi = spi;
|
||||
st->pdata = pdata;
|
||||
|
@ -81,9 +81,11 @@ int adis_write_reg(struct adis *adis, unsigned int reg,
|
||||
adis->tx[9] = (value >> 24) & 0xff;
|
||||
adis->tx[6] = ADIS_WRITE_REG(reg + 2);
|
||||
adis->tx[7] = (value >> 16) & 0xff;
|
||||
/* fall through */
|
||||
case 2:
|
||||
adis->tx[4] = ADIS_WRITE_REG(reg + 1);
|
||||
adis->tx[5] = (value >> 8) & 0xff;
|
||||
/* fall through */
|
||||
case 1:
|
||||
adis->tx[2] = ADIS_WRITE_REG(reg);
|
||||
adis->tx[3] = value & 0xff;
|
||||
@ -167,6 +169,7 @@ int adis_read_reg(struct adis *adis, unsigned int reg,
|
||||
adis->tx[2] = ADIS_READ_REG(reg + 2);
|
||||
adis->tx[3] = 0;
|
||||
spi_message_add_tail(&xfers[1], &msg);
|
||||
/* fall through */
|
||||
case 2:
|
||||
adis->tx[4] = ADIS_READ_REG(reg);
|
||||
adis->tx[5] = 0;
|
||||
|
@ -20,8 +20,6 @@
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kfifo.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/platform_device.h>
|
||||
@ -84,7 +82,7 @@ static const struct inv_mpu6050_reg_map reg_set_6050 = {
|
||||
static const struct inv_mpu6050_chip_config chip_config_6050 = {
|
||||
.fsr = INV_MPU6050_FSR_2000DPS,
|
||||
.lpf = INV_MPU6050_FILTER_20HZ,
|
||||
.fifo_rate = INV_MPU6050_INIT_FIFO_RATE,
|
||||
.divider = INV_MPU6050_FIFO_RATE_TO_DIVIDER(INV_MPU6050_INIT_FIFO_RATE),
|
||||
.gyro_fifo_enable = false,
|
||||
.accl_fifo_enable = false,
|
||||
.accl_fs = INV_MPU6050_FS_02G,
|
||||
@ -105,6 +103,12 @@ static const struct inv_mpu6050_hw hw_info[] = {
|
||||
.reg = ®_set_6500,
|
||||
.config = &chip_config_6050,
|
||||
},
|
||||
{
|
||||
.whoami = INV_MPU6515_WHOAMI_VALUE,
|
||||
.name = "MPU6515",
|
||||
.reg = ®_set_6500,
|
||||
.config = &chip_config_6050,
|
||||
},
|
||||
{
|
||||
.whoami = INV_MPU6000_WHOAMI_VALUE,
|
||||
.name = "MPU6000",
|
||||
@ -280,7 +284,7 @@ static int inv_mpu6050_init_config(struct iio_dev *indio_dev)
|
||||
if (result)
|
||||
goto error_power_off;
|
||||
|
||||
d = INV_MPU6050_ONE_K_HZ / INV_MPU6050_INIT_FIFO_RATE - 1;
|
||||
d = INV_MPU6050_FIFO_RATE_TO_DIVIDER(INV_MPU6050_INIT_FIFO_RATE);
|
||||
result = regmap_write(st->map, st->reg->sample_rate_div, d);
|
||||
if (result)
|
||||
goto error_power_off;
|
||||
@ -297,6 +301,13 @@ static int inv_mpu6050_init_config(struct iio_dev *indio_dev)
|
||||
memcpy(&st->chip_config, hw_info[st->chip_type].config,
|
||||
sizeof(struct inv_mpu6050_chip_config));
|
||||
|
||||
/*
|
||||
* Internal chip period is 1ms (1kHz).
|
||||
* Let's use at the beginning the theorical value before measuring
|
||||
* with interrupt timestamps.
|
||||
*/
|
||||
st->chip_period = NSEC_PER_MSEC;
|
||||
|
||||
return inv_mpu6050_set_power_itg(st, false);
|
||||
|
||||
error_power_off:
|
||||
@ -630,7 +641,7 @@ static ssize_t
|
||||
inv_mpu6050_fifo_rate_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
s32 fifo_rate;
|
||||
int fifo_rate;
|
||||
u8 d;
|
||||
int result;
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
@ -646,8 +657,13 @@ inv_mpu6050_fifo_rate_store(struct device *dev, struct device_attribute *attr,
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
/* compute the chip sample rate divider */
|
||||
d = INV_MPU6050_FIFO_RATE_TO_DIVIDER(fifo_rate);
|
||||
/* compute back the fifo rate to handle truncation cases */
|
||||
fifo_rate = INV_MPU6050_DIVIDER_TO_FIFO_RATE(d);
|
||||
|
||||
mutex_lock(&st->lock);
|
||||
if (fifo_rate == st->chip_config.fifo_rate) {
|
||||
if (d == st->chip_config.divider) {
|
||||
result = 0;
|
||||
goto fifo_rate_fail_unlock;
|
||||
}
|
||||
@ -655,11 +671,10 @@ inv_mpu6050_fifo_rate_store(struct device *dev, struct device_attribute *attr,
|
||||
if (result)
|
||||
goto fifo_rate_fail_unlock;
|
||||
|
||||
d = INV_MPU6050_ONE_K_HZ / fifo_rate - 1;
|
||||
result = regmap_write(st->map, st->reg->sample_rate_div, d);
|
||||
if (result)
|
||||
goto fifo_rate_fail_power_off;
|
||||
st->chip_config.fifo_rate = fifo_rate;
|
||||
st->chip_config.divider = d;
|
||||
|
||||
result = inv_mpu6050_set_lpf(st, fifo_rate);
|
||||
if (result)
|
||||
@ -687,7 +702,7 @@ inv_fifo_rate_show(struct device *dev, struct device_attribute *attr,
|
||||
unsigned fifo_rate;
|
||||
|
||||
mutex_lock(&st->lock);
|
||||
fifo_rate = st->chip_config.fifo_rate;
|
||||
fifo_rate = INV_MPU6050_DIVIDER_TO_FIFO_RATE(st->chip_config.divider);
|
||||
mutex_unlock(&st->lock);
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%u\n", fifo_rate);
|
||||
@ -1005,7 +1020,7 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name,
|
||||
indio_dev->modes = INDIO_BUFFER_TRIGGERED;
|
||||
|
||||
result = devm_iio_triggered_buffer_setup(dev, indio_dev,
|
||||
inv_mpu6050_irq_handler,
|
||||
iio_pollfunc_store_time,
|
||||
inv_mpu6050_read_fifo,
|
||||
NULL);
|
||||
if (result) {
|
||||
@ -1018,8 +1033,6 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name,
|
||||
return result;
|
||||
}
|
||||
|
||||
INIT_KFIFO(st->timestamps);
|
||||
spin_lock_init(&st->time_stamp_lock);
|
||||
result = devm_iio_device_register(dev, indio_dev);
|
||||
if (result) {
|
||||
dev_err(dev, "IIO register fail %d\n", result);
|
||||
|
@ -174,6 +174,7 @@ static int inv_mpu_remove(struct i2c_client *client)
|
||||
static const struct i2c_device_id inv_mpu_id[] = {
|
||||
{"mpu6050", INV_MPU6050},
|
||||
{"mpu6500", INV_MPU6500},
|
||||
{"mpu6515", INV_MPU6515},
|
||||
{"mpu9150", INV_MPU9150},
|
||||
{"mpu9250", INV_MPU9250},
|
||||
{"mpu9255", INV_MPU9255},
|
||||
@ -192,6 +193,10 @@ static const struct of_device_id inv_of_match[] = {
|
||||
.compatible = "invensense,mpu6500",
|
||||
.data = (void *)INV_MPU6500
|
||||
},
|
||||
{
|
||||
.compatible = "invensense,mpu6515",
|
||||
.data = (void *)INV_MPU6515
|
||||
},
|
||||
{
|
||||
.compatible = "invensense,mpu9150",
|
||||
.data = (void *)INV_MPU9150
|
||||
|
@ -12,8 +12,6 @@
|
||||
*/
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i2c-mux.h>
|
||||
#include <linux/kfifo.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
@ -73,6 +71,7 @@ struct inv_mpu6050_reg_map {
|
||||
enum inv_devices {
|
||||
INV_MPU6050,
|
||||
INV_MPU6500,
|
||||
INV_MPU6515,
|
||||
INV_MPU6000,
|
||||
INV_MPU9150,
|
||||
INV_MPU9250,
|
||||
@ -88,7 +87,7 @@ enum inv_devices {
|
||||
* @accl_fs: accel full scale range.
|
||||
* @accl_fifo_enable: enable accel data output
|
||||
* @gyro_fifo_enable: enable gyro data output
|
||||
* @fifo_rate: FIFO update rate.
|
||||
* @divider: chip sample rate divider (sample rate divider - 1)
|
||||
*/
|
||||
struct inv_mpu6050_chip_config {
|
||||
unsigned int fsr:2;
|
||||
@ -96,7 +95,7 @@ struct inv_mpu6050_chip_config {
|
||||
unsigned int accl_fs:2;
|
||||
unsigned int accl_fifo_enable:1;
|
||||
unsigned int gyro_fifo_enable:1;
|
||||
u16 fifo_rate;
|
||||
u8 divider;
|
||||
u8 user_ctrl;
|
||||
};
|
||||
|
||||
@ -116,40 +115,40 @@ struct inv_mpu6050_hw {
|
||||
|
||||
/*
|
||||
* struct inv_mpu6050_state - Driver state variables.
|
||||
* @TIMESTAMP_FIFO_SIZE: fifo size for timestamp.
|
||||
* @lock: Chip access lock.
|
||||
* @trig: IIO trigger.
|
||||
* @chip_config: Cached attribute information.
|
||||
* @reg: Map of important registers.
|
||||
* @hw: Other hardware-specific information.
|
||||
* @chip_type: chip type.
|
||||
* @time_stamp_lock: spin lock to time stamp.
|
||||
* @plat_data: platform data (deprecated in favor of @orientation).
|
||||
* @orientation: sensor chip orientation relative to main hardware.
|
||||
* @timestamps: kfifo queue to store time stamp.
|
||||
* @map regmap pointer.
|
||||
* @irq interrupt number.
|
||||
* @irq_mask the int_pin_cfg mask to configure interrupt type.
|
||||
* @chip_period: chip internal period estimation (~1kHz).
|
||||
* @it_timestamp: timestamp from previous interrupt.
|
||||
* @data_timestamp: timestamp for next data sample.
|
||||
*/
|
||||
struct inv_mpu6050_state {
|
||||
#define TIMESTAMP_FIFO_SIZE 16
|
||||
struct mutex lock;
|
||||
struct iio_trigger *trig;
|
||||
struct inv_mpu6050_chip_config chip_config;
|
||||
const struct inv_mpu6050_reg_map *reg;
|
||||
const struct inv_mpu6050_hw *hw;
|
||||
enum inv_devices chip_type;
|
||||
spinlock_t time_stamp_lock;
|
||||
struct i2c_mux_core *muxc;
|
||||
struct i2c_client *mux_client;
|
||||
unsigned int powerup_count;
|
||||
struct inv_mpu6050_platform_data plat_data;
|
||||
struct iio_mount_matrix orientation;
|
||||
DECLARE_KFIFO(timestamps, long long, TIMESTAMP_FIFO_SIZE);
|
||||
struct regmap *map;
|
||||
int irq;
|
||||
u8 irq_mask;
|
||||
unsigned skip_samples;
|
||||
s64 chip_period;
|
||||
s64 it_timestamp;
|
||||
s64 data_timestamp;
|
||||
};
|
||||
|
||||
/*register and associated bit definition*/
|
||||
@ -174,6 +173,7 @@ struct inv_mpu6050_state {
|
||||
#define INV_MPU6050_REG_RAW_GYRO 0x43
|
||||
|
||||
#define INV_MPU6050_REG_INT_STATUS 0x3A
|
||||
#define INV_MPU6050_BIT_FIFO_OVERFLOW_INT 0x10
|
||||
#define INV_MPU6050_BIT_RAW_DATA_RDY_INT 0x01
|
||||
|
||||
#define INV_MPU6050_REG_USER_CTRL 0x6A
|
||||
@ -198,7 +198,6 @@ struct inv_mpu6050_state {
|
||||
|
||||
#define INV_MPU6050_BYTES_PER_3AXIS_SENSOR 6
|
||||
#define INV_MPU6050_FIFO_COUNT_BYTE 2
|
||||
#define INV_MPU6050_FIFO_THRESHOLD 500
|
||||
|
||||
/* mpu6500 registers */
|
||||
#define INV_MPU6500_REG_ACCEL_CONFIG_2 0x1D
|
||||
@ -231,13 +230,24 @@ struct inv_mpu6050_state {
|
||||
#define INV_MPU6050_LATCH_INT_EN 0x20
|
||||
#define INV_MPU6050_BIT_BYPASS_EN 0x2
|
||||
|
||||
/* Allowed timestamp period jitter in percent */
|
||||
#define INV_MPU6050_TS_PERIOD_JITTER 4
|
||||
|
||||
/* init parameters */
|
||||
#define INV_MPU6050_INIT_FIFO_RATE 50
|
||||
#define INV_MPU6050_TIME_STAMP_TOR 5
|
||||
#define INV_MPU6050_MAX_FIFO_RATE 1000
|
||||
#define INV_MPU6050_MIN_FIFO_RATE 4
|
||||
#define INV_MPU6050_ONE_K_HZ 1000
|
||||
|
||||
/* chip internal frequency: 1KHz */
|
||||
#define INV_MPU6050_INTERNAL_FREQ_HZ 1000
|
||||
/* return the frequency divider (chip sample rate divider + 1) */
|
||||
#define INV_MPU6050_FREQ_DIVIDER(st) \
|
||||
((st)->chip_config.divider + 1)
|
||||
/* chip sample rate divider to fifo rate */
|
||||
#define INV_MPU6050_FIFO_RATE_TO_DIVIDER(fifo_rate) \
|
||||
((INV_MPU6050_INTERNAL_FREQ_HZ / (fifo_rate)) - 1)
|
||||
#define INV_MPU6050_DIVIDER_TO_FIFO_RATE(divider) \
|
||||
(INV_MPU6050_INTERNAL_FREQ_HZ / ((divider) + 1))
|
||||
|
||||
#define INV_MPU6050_REG_WHOAMI 117
|
||||
|
||||
@ -247,6 +257,7 @@ struct inv_mpu6050_state {
|
||||
#define INV_MPU9150_WHOAMI_VALUE 0x68
|
||||
#define INV_MPU9250_WHOAMI_VALUE 0x71
|
||||
#define INV_MPU9255_WHOAMI_VALUE 0x73
|
||||
#define INV_MPU6515_WHOAMI_VALUE 0x74
|
||||
#define INV_ICM20608_WHOAMI_VALUE 0xAF
|
||||
|
||||
/* scan element definition */
|
||||
@ -300,7 +311,6 @@ enum inv_mpu6050_clock_sel_e {
|
||||
NUM_CLK
|
||||
};
|
||||
|
||||
irqreturn_t inv_mpu6050_irq_handler(int irq, void *p);
|
||||
irqreturn_t inv_mpu6050_read_fifo(int irq, void *p);
|
||||
int inv_mpu6050_probe_trigger(struct iio_dev *indio_dev, int irq_type);
|
||||
int inv_reset_fifo(struct iio_dev *indio_dev);
|
||||
|
@ -19,18 +19,83 @@
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kfifo.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/math64.h>
|
||||
#include <asm/unaligned.h>
|
||||
#include "inv_mpu_iio.h"
|
||||
|
||||
static void inv_clear_kfifo(struct inv_mpu6050_state *st)
|
||||
/**
|
||||
* inv_mpu6050_update_period() - Update chip internal period estimation
|
||||
*
|
||||
* @st: driver state
|
||||
* @timestamp: the interrupt timestamp
|
||||
* @nb: number of data set in the fifo
|
||||
*
|
||||
* This function uses interrupt timestamps to estimate the chip period and
|
||||
* to choose the data timestamp to come.
|
||||
*/
|
||||
static void inv_mpu6050_update_period(struct inv_mpu6050_state *st,
|
||||
s64 timestamp, size_t nb)
|
||||
{
|
||||
unsigned long flags;
|
||||
/* Period boundaries for accepting timestamp */
|
||||
const s64 period_min =
|
||||
(NSEC_PER_MSEC * (100 - INV_MPU6050_TS_PERIOD_JITTER)) / 100;
|
||||
const s64 period_max =
|
||||
(NSEC_PER_MSEC * (100 + INV_MPU6050_TS_PERIOD_JITTER)) / 100;
|
||||
const s32 divider = INV_MPU6050_FREQ_DIVIDER(st);
|
||||
s64 delta, interval;
|
||||
bool use_it_timestamp = false;
|
||||
|
||||
/* take the spin lock sem to avoid interrupt kick in */
|
||||
spin_lock_irqsave(&st->time_stamp_lock, flags);
|
||||
kfifo_reset(&st->timestamps);
|
||||
spin_unlock_irqrestore(&st->time_stamp_lock, flags);
|
||||
if (st->it_timestamp == 0) {
|
||||
/* not initialized, forced to use it_timestamp */
|
||||
use_it_timestamp = true;
|
||||
} else if (nb == 1) {
|
||||
/*
|
||||
* Validate the use of it timestamp by checking if interrupt
|
||||
* has been delayed.
|
||||
* nb > 1 means interrupt was delayed for more than 1 sample,
|
||||
* so it's obviously not good.
|
||||
* Compute the chip period between 2 interrupts for validating.
|
||||
*/
|
||||
delta = div_s64(timestamp - st->it_timestamp, divider);
|
||||
if (delta > period_min && delta < period_max) {
|
||||
/* update chip period and use it timestamp */
|
||||
st->chip_period = (st->chip_period + delta) / 2;
|
||||
use_it_timestamp = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (use_it_timestamp) {
|
||||
/*
|
||||
* Manage case of multiple samples in the fifo (nb > 1):
|
||||
* compute timestamp corresponding to the first sample using
|
||||
* estimated chip period.
|
||||
*/
|
||||
interval = (nb - 1) * st->chip_period * divider;
|
||||
st->data_timestamp = timestamp - interval;
|
||||
}
|
||||
|
||||
/* save it timestamp */
|
||||
st->it_timestamp = timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* inv_mpu6050_get_timestamp() - Return the current data timestamp
|
||||
*
|
||||
* @st: driver state
|
||||
* @return: current data timestamp
|
||||
*
|
||||
* This function returns the current data timestamp and prepares for next one.
|
||||
*/
|
||||
static s64 inv_mpu6050_get_timestamp(struct inv_mpu6050_state *st)
|
||||
{
|
||||
s64 ts;
|
||||
|
||||
/* return current data timestamp and increment */
|
||||
ts = st->data_timestamp;
|
||||
st->data_timestamp += st->chip_period * INV_MPU6050_FREQ_DIVIDER(st);
|
||||
|
||||
return ts;
|
||||
}
|
||||
|
||||
int inv_reset_fifo(struct iio_dev *indio_dev)
|
||||
@ -39,6 +104,9 @@ int inv_reset_fifo(struct iio_dev *indio_dev)
|
||||
u8 d;
|
||||
struct inv_mpu6050_state *st = iio_priv(indio_dev);
|
||||
|
||||
/* reset it timestamp validation */
|
||||
st->it_timestamp = 0;
|
||||
|
||||
/* disable interrupt */
|
||||
result = regmap_write(st->map, st->reg->int_enable, 0);
|
||||
if (result) {
|
||||
@ -62,9 +130,6 @@ int inv_reset_fifo(struct iio_dev *indio_dev)
|
||||
if (result)
|
||||
goto reset_fifo_fail;
|
||||
|
||||
/* clear timestamps fifo */
|
||||
inv_clear_kfifo(st);
|
||||
|
||||
/* enable interrupt */
|
||||
if (st->chip_config.accl_fifo_enable ||
|
||||
st->chip_config.gyro_fifo_enable) {
|
||||
@ -98,23 +163,6 @@ reset_fifo_fail:
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* inv_mpu6050_irq_handler() - Cache a timestamp at each data ready interrupt.
|
||||
*/
|
||||
irqreturn_t inv_mpu6050_irq_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct inv_mpu6050_state *st = iio_priv(indio_dev);
|
||||
s64 timestamp;
|
||||
|
||||
timestamp = iio_get_time_ns(indio_dev);
|
||||
kfifo_in_spinlocked(&st->timestamps, ×tamp, 1,
|
||||
&st->time_stamp_lock);
|
||||
|
||||
return IRQ_WAKE_THREAD;
|
||||
}
|
||||
|
||||
/**
|
||||
* inv_mpu6050_read_fifo() - Transfer data from hardware FIFO to KFIFO.
|
||||
*/
|
||||
@ -129,6 +177,7 @@ irqreturn_t inv_mpu6050_read_fifo(int irq, void *p)
|
||||
u16 fifo_count;
|
||||
s64 timestamp;
|
||||
int int_status;
|
||||
size_t i, nb;
|
||||
|
||||
mutex_lock(&st->lock);
|
||||
|
||||
@ -139,6 +188,9 @@ irqreturn_t inv_mpu6050_read_fifo(int irq, void *p)
|
||||
"failed to ack interrupt\n");
|
||||
goto flush_fifo;
|
||||
}
|
||||
/* handle fifo overflow by reseting fifo */
|
||||
if (int_status & INV_MPU6050_BIT_FIFO_OVERFLOW_INT)
|
||||
goto flush_fifo;
|
||||
if (!(int_status & INV_MPU6050_BIT_RAW_DATA_RDY_INT)) {
|
||||
dev_warn(regmap_get_device(st->map),
|
||||
"spurious interrupt with status 0x%x\n", int_status);
|
||||
@ -163,38 +215,23 @@ irqreturn_t inv_mpu6050_read_fifo(int irq, void *p)
|
||||
INV_MPU6050_FIFO_COUNT_BYTE);
|
||||
if (result)
|
||||
goto end_session;
|
||||
fifo_count = be16_to_cpup((__be16 *)(&data[0]));
|
||||
if (fifo_count < bytes_per_datum)
|
||||
goto end_session;
|
||||
/* fifo count can't be an odd number. If it is odd, reset the FIFO. */
|
||||
if (fifo_count & 1)
|
||||
goto flush_fifo;
|
||||
if (fifo_count > INV_MPU6050_FIFO_THRESHOLD)
|
||||
goto flush_fifo;
|
||||
/* Timestamp mismatch. */
|
||||
if (kfifo_len(&st->timestamps) >
|
||||
fifo_count / bytes_per_datum + INV_MPU6050_TIME_STAMP_TOR)
|
||||
goto flush_fifo;
|
||||
do {
|
||||
fifo_count = get_unaligned_be16(&data[0]);
|
||||
/* compute and process all complete datum */
|
||||
nb = fifo_count / bytes_per_datum;
|
||||
inv_mpu6050_update_period(st, pf->timestamp, nb);
|
||||
for (i = 0; i < nb; ++i) {
|
||||
result = regmap_bulk_read(st->map, st->reg->fifo_r_w,
|
||||
data, bytes_per_datum);
|
||||
if (result)
|
||||
goto flush_fifo;
|
||||
|
||||
result = kfifo_out(&st->timestamps, ×tamp, 1);
|
||||
/* when there is no timestamp, put timestamp as 0 */
|
||||
if (result == 0)
|
||||
timestamp = 0;
|
||||
|
||||
/* skip first samples if needed */
|
||||
if (st->skip_samples)
|
||||
if (st->skip_samples) {
|
||||
st->skip_samples--;
|
||||
else
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, data,
|
||||
timestamp);
|
||||
|
||||
fifo_count -= bytes_per_datum;
|
||||
} while (fifo_count >= bytes_per_datum);
|
||||
continue;
|
||||
}
|
||||
timestamp = inv_mpu6050_get_timestamp(st);
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, data, timestamp);
|
||||
}
|
||||
|
||||
end_session:
|
||||
mutex_unlock(&st->lock);
|
||||
|
@ -298,8 +298,11 @@ static int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw)
|
||||
err = regmap_bulk_read(hw->regmap,
|
||||
hw->settings->fifo_ops.fifo_diff.addr,
|
||||
&fifo_status, sizeof(fifo_status));
|
||||
if (err < 0)
|
||||
if (err < 0) {
|
||||
dev_err(hw->dev, "failed to read fifo status (err=%d)\n",
|
||||
err);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (fifo_status & cpu_to_le16(ST_LSM6DSX_FIFO_EMPTY_MASK))
|
||||
return 0;
|
||||
@ -313,8 +316,12 @@ static int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw)
|
||||
|
||||
for (read_len = 0; read_len < fifo_len; read_len += pattern_len) {
|
||||
err = st_lsm6dsx_read_block(hw, hw->buff, pattern_len);
|
||||
if (err < 0)
|
||||
if (err < 0) {
|
||||
dev_err(hw->dev,
|
||||
"failed to read pattern from fifo (err=%d)\n",
|
||||
err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Data are written to the FIFO with a specific pattern
|
||||
@ -385,8 +392,11 @@ static int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw)
|
||||
|
||||
if (unlikely(reset_ts)) {
|
||||
err = st_lsm6dsx_reset_hw_ts(hw);
|
||||
if (err < 0)
|
||||
if (err < 0) {
|
||||
dev_err(hw->dev, "failed to reset hw ts (err=%d)\n",
|
||||
err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
return read_len;
|
||||
}
|
||||
|
@ -85,6 +85,8 @@ static const char * const iio_chan_type_name_spec[] = {
|
||||
[IIO_COUNT] = "count",
|
||||
[IIO_INDEX] = "index",
|
||||
[IIO_GRAVITY] = "gravity",
|
||||
[IIO_POSITIONRELATIVE] = "positionrelative",
|
||||
[IIO_PHASE] = "phase",
|
||||
};
|
||||
|
||||
static const char * const iio_modifier_names[] = {
|
||||
@ -108,6 +110,7 @@ static const char * const iio_modifier_names[] = {
|
||||
[IIO_MOD_LIGHT_GREEN] = "green",
|
||||
[IIO_MOD_LIGHT_BLUE] = "blue",
|
||||
[IIO_MOD_LIGHT_UV] = "uv",
|
||||
[IIO_MOD_LIGHT_DUV] = "duv",
|
||||
[IIO_MOD_QUATERNION] = "quaternion",
|
||||
[IIO_MOD_TEMP_AMBIENT] = "ambient",
|
||||
[IIO_MOD_TEMP_OBJECT] = "object",
|
||||
@ -207,35 +210,27 @@ static int iio_device_set_clock(struct iio_dev *indio_dev, clockid_t clock_id)
|
||||
*/
|
||||
s64 iio_get_time_ns(const struct iio_dev *indio_dev)
|
||||
{
|
||||
struct timespec tp;
|
||||
struct timespec64 tp;
|
||||
|
||||
switch (iio_device_get_clock(indio_dev)) {
|
||||
case CLOCK_REALTIME:
|
||||
ktime_get_real_ts(&tp);
|
||||
break;
|
||||
return ktime_get_real_ns();
|
||||
case CLOCK_MONOTONIC:
|
||||
ktime_get_ts(&tp);
|
||||
break;
|
||||
return ktime_get_ns();
|
||||
case CLOCK_MONOTONIC_RAW:
|
||||
getrawmonotonic(&tp);
|
||||
break;
|
||||
return ktime_get_raw_ns();
|
||||
case CLOCK_REALTIME_COARSE:
|
||||
tp = current_kernel_time();
|
||||
break;
|
||||
return ktime_to_ns(ktime_get_coarse_real());
|
||||
case CLOCK_MONOTONIC_COARSE:
|
||||
tp = get_monotonic_coarse();
|
||||
break;
|
||||
ktime_get_coarse_ts64(&tp);
|
||||
return timespec64_to_ns(&tp);
|
||||
case CLOCK_BOOTTIME:
|
||||
get_monotonic_boottime(&tp);
|
||||
break;
|
||||
return ktime_get_boot_ns();
|
||||
case CLOCK_TAI:
|
||||
timekeeping_clocktai(&tp);
|
||||
break;
|
||||
return ktime_get_tai_ns();
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
|
||||
return timespec_to_ns(&tp);
|
||||
}
|
||||
EXPORT_SYMBOL(iio_get_time_ns);
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
|
||||
#
|
||||
# Light sensors
|
||||
#
|
||||
@ -319,6 +320,17 @@ config PA12203001
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called pa12203001.
|
||||
|
||||
config SI1133
|
||||
tristate "SI1133 UV Index Sensor and Ambient Light Sensor"
|
||||
depends on I2C
|
||||
select REGMAP_I2C
|
||||
help
|
||||
Say Y here if you want to build a driver for the Silicon Labs SI1133
|
||||
UV Index Sensor and Ambient Light Sensor chip.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called si1133.
|
||||
|
||||
config SI1145
|
||||
tristate "SI1132 and SI1141/2/3/5/6/7 combined ALS, UV index and proximity sensor"
|
||||
depends on I2C
|
||||
@ -438,11 +450,12 @@ config US5182D
|
||||
will be called us5182d.
|
||||
|
||||
config VCNL4000
|
||||
tristate "VCNL4000/4010/4020 combined ALS and proximity sensor"
|
||||
tristate "VCNL4000/4010/4020/4200 combined ALS and proximity sensor"
|
||||
depends on I2C
|
||||
help
|
||||
Say Y here if you want to build a driver for the Vishay VCNL4000,
|
||||
VCNL4010, VCNL4020 combined ambient light and proximity sensor.
|
||||
VCNL4010, VCNL4020, VCNL4200 combined ambient light and proximity
|
||||
sensor.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called vcnl4000.
|
||||
|
@ -32,6 +32,7 @@ obj-$(CONFIG_OPT3001) += opt3001.o
|
||||
obj-$(CONFIG_PA12203001) += pa12203001.o
|
||||
obj-$(CONFIG_RPR0521) += rpr0521.o
|
||||
obj-$(CONFIG_SENSORS_TSL2563) += tsl2563.o
|
||||
obj-$(CONFIG_SI1133) += si1133.o
|
||||
obj-$(CONFIG_SI1145) += si1145.o
|
||||
obj-$(CONFIG_STK3310) += stk3310.o
|
||||
obj-$(CONFIG_ST_UVIS25) += st_uvis25_core.o
|
||||
|
1071
drivers/iio/light/si1133.c
Normal file
1071
drivers/iio/light/si1133.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* vcnl4000.c - Support for Vishay VCNL4000/4010/4020 combined ambient
|
||||
* vcnl4000.c - Support for Vishay VCNL4000/4010/4020/4200 combined ambient
|
||||
* light and proximity sensor
|
||||
*
|
||||
* Copyright 2012 Peter Meerwald <pmeerw@pmeerw.net>
|
||||
@ -8,13 +8,15 @@
|
||||
* the GNU General Public License. See the file COPYING in the main
|
||||
* directory of this archive for more details.
|
||||
*
|
||||
* IIO driver for VCNL4000 (7-bit I2C slave address 0x13)
|
||||
* IIO driver for:
|
||||
* VCNL4000/10/20 (7-bit I2C slave address 0x13)
|
||||
* VCNL4200 (7-bit I2C slave address 0x51)
|
||||
*
|
||||
* TODO:
|
||||
* allow to adjust IR current
|
||||
* proximity threshold and event handling
|
||||
* periodic ALS/proximity measurement (VCNL4010/20)
|
||||
* interrupts (VCNL4010/20)
|
||||
* interrupts (VCNL4010/20, VCNL4200)
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
@ -26,8 +28,9 @@
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
#define VCNL4000_DRV_NAME "vcnl4000"
|
||||
#define VCNL4000_ID 0x01
|
||||
#define VCNL4010_ID 0x02 /* for VCNL4020, VCNL4010 */
|
||||
#define VCNL4000_PROD_ID 0x01
|
||||
#define VCNL4010_PROD_ID 0x02 /* for VCNL4020, VCNL4010 */
|
||||
#define VCNL4200_PROD_ID 0x58
|
||||
|
||||
#define VCNL4000_COMMAND 0x80 /* Command register */
|
||||
#define VCNL4000_PROD_REV 0x81 /* Product ID and Revision ID */
|
||||
@ -40,23 +43,124 @@
|
||||
#define VCNL4000_PS_MEAS_FREQ 0x89 /* Proximity test signal frequency */
|
||||
#define VCNL4000_PS_MOD_ADJ 0x8a /* Proximity modulator timing adjustment */
|
||||
|
||||
#define VCNL4200_AL_CONF 0x00 /* Ambient light configuration */
|
||||
#define VCNL4200_PS_CONF1 0x03 /* Proximity configuration */
|
||||
#define VCNL4200_PS_DATA 0x08 /* Proximity data */
|
||||
#define VCNL4200_AL_DATA 0x09 /* Ambient light data */
|
||||
#define VCNL4200_DEV_ID 0x0e /* Device ID, slave address and version */
|
||||
|
||||
/* Bit masks for COMMAND register */
|
||||
#define VCNL4000_AL_RDY BIT(6) /* ALS data ready? */
|
||||
#define VCNL4000_PS_RDY BIT(5) /* proximity data ready? */
|
||||
#define VCNL4000_AL_OD BIT(4) /* start on-demand ALS measurement */
|
||||
#define VCNL4000_PS_OD BIT(3) /* start on-demand proximity measurement */
|
||||
|
||||
struct vcnl4000_data {
|
||||
struct i2c_client *client;
|
||||
enum vcnl4000_device_ids {
|
||||
VCNL4000,
|
||||
VCNL4010,
|
||||
VCNL4200,
|
||||
};
|
||||
|
||||
struct vcnl4200_channel {
|
||||
u8 reg;
|
||||
ktime_t last_measurement;
|
||||
ktime_t sampling_rate;
|
||||
struct mutex lock;
|
||||
};
|
||||
|
||||
struct vcnl4000_data {
|
||||
struct i2c_client *client;
|
||||
enum vcnl4000_device_ids id;
|
||||
int rev;
|
||||
int al_scale;
|
||||
const struct vcnl4000_chip_spec *chip_spec;
|
||||
struct mutex vcnl4000_lock;
|
||||
struct vcnl4200_channel vcnl4200_al;
|
||||
struct vcnl4200_channel vcnl4200_ps;
|
||||
};
|
||||
|
||||
struct vcnl4000_chip_spec {
|
||||
const char *prod;
|
||||
int (*init)(struct vcnl4000_data *data);
|
||||
int (*measure_light)(struct vcnl4000_data *data, int *val);
|
||||
int (*measure_proximity)(struct vcnl4000_data *data, int *val);
|
||||
};
|
||||
|
||||
static const struct i2c_device_id vcnl4000_id[] = {
|
||||
{ "vcnl4000", 0 },
|
||||
{ "vcnl4000", VCNL4000 },
|
||||
{ "vcnl4010", VCNL4010 },
|
||||
{ "vcnl4020", VCNL4010 },
|
||||
{ "vcnl4200", VCNL4200 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, vcnl4000_id);
|
||||
|
||||
static int vcnl4000_init(struct vcnl4000_data *data)
|
||||
{
|
||||
int ret, prod_id;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(data->client, VCNL4000_PROD_REV);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
prod_id = ret >> 4;
|
||||
switch (prod_id) {
|
||||
case VCNL4000_PROD_ID:
|
||||
if (data->id != VCNL4000)
|
||||
dev_warn(&data->client->dev,
|
||||
"wrong device id, use vcnl4000");
|
||||
break;
|
||||
case VCNL4010_PROD_ID:
|
||||
if (data->id != VCNL4010)
|
||||
dev_warn(&data->client->dev,
|
||||
"wrong device id, use vcnl4010/4020");
|
||||
break;
|
||||
default:
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
data->rev = ret & 0xf;
|
||||
data->al_scale = 250000;
|
||||
mutex_init(&data->vcnl4000_lock);
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
static int vcnl4200_init(struct vcnl4000_data *data)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_read_word_data(data->client, VCNL4200_DEV_ID);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if ((ret & 0xff) != VCNL4200_PROD_ID)
|
||||
return -ENODEV;
|
||||
|
||||
data->rev = (ret >> 8) & 0xf;
|
||||
|
||||
/* Set defaults and enable both channels */
|
||||
ret = i2c_smbus_write_byte_data(data->client, VCNL4200_AL_CONF, 0x00);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = i2c_smbus_write_byte_data(data->client, VCNL4200_PS_CONF1, 0x00);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
data->al_scale = 24000;
|
||||
data->vcnl4200_al.reg = VCNL4200_AL_DATA;
|
||||
data->vcnl4200_ps.reg = VCNL4200_PS_DATA;
|
||||
/* Integration time is 50ms, but the experiments show 54ms in total. */
|
||||
data->vcnl4200_al.sampling_rate = ktime_set(0, 54000 * 1000);
|
||||
data->vcnl4200_ps.sampling_rate = ktime_set(0, 4200 * 1000);
|
||||
data->vcnl4200_al.last_measurement = ktime_set(0, 0);
|
||||
data->vcnl4200_ps.last_measurement = ktime_set(0, 0);
|
||||
mutex_init(&data->vcnl4200_al.lock);
|
||||
mutex_init(&data->vcnl4200_ps.lock);
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
static int vcnl4000_measure(struct vcnl4000_data *data, u8 req_mask,
|
||||
u8 rdy_mask, u8 data_reg, int *val)
|
||||
{
|
||||
@ -64,7 +168,7 @@ static int vcnl4000_measure(struct vcnl4000_data *data, u8 req_mask,
|
||||
__be16 buf;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
mutex_lock(&data->vcnl4000_lock);
|
||||
|
||||
ret = i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND,
|
||||
req_mask);
|
||||
@ -93,16 +197,88 @@ static int vcnl4000_measure(struct vcnl4000_data *data, u8 req_mask,
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
|
||||
mutex_unlock(&data->lock);
|
||||
mutex_unlock(&data->vcnl4000_lock);
|
||||
*val = be16_to_cpu(buf);
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
mutex_unlock(&data->lock);
|
||||
mutex_unlock(&data->vcnl4000_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int vcnl4200_measure(struct vcnl4000_data *data,
|
||||
struct vcnl4200_channel *chan, int *val)
|
||||
{
|
||||
int ret;
|
||||
s64 delta;
|
||||
ktime_t next_measurement;
|
||||
|
||||
mutex_lock(&chan->lock);
|
||||
|
||||
next_measurement = ktime_add(chan->last_measurement,
|
||||
chan->sampling_rate);
|
||||
delta = ktime_us_delta(next_measurement, ktime_get());
|
||||
if (delta > 0)
|
||||
usleep_range(delta, delta + 500);
|
||||
chan->last_measurement = ktime_get();
|
||||
|
||||
mutex_unlock(&chan->lock);
|
||||
|
||||
ret = i2c_smbus_read_word_data(data->client, chan->reg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vcnl4000_measure_light(struct vcnl4000_data *data, int *val)
|
||||
{
|
||||
return vcnl4000_measure(data,
|
||||
VCNL4000_AL_OD, VCNL4000_AL_RDY,
|
||||
VCNL4000_AL_RESULT_HI, val);
|
||||
}
|
||||
|
||||
static int vcnl4200_measure_light(struct vcnl4000_data *data, int *val)
|
||||
{
|
||||
return vcnl4200_measure(data, &data->vcnl4200_al, val);
|
||||
}
|
||||
|
||||
static int vcnl4000_measure_proximity(struct vcnl4000_data *data, int *val)
|
||||
{
|
||||
return vcnl4000_measure(data,
|
||||
VCNL4000_PS_OD, VCNL4000_PS_RDY,
|
||||
VCNL4000_PS_RESULT_HI, val);
|
||||
}
|
||||
|
||||
static int vcnl4200_measure_proximity(struct vcnl4000_data *data, int *val)
|
||||
{
|
||||
return vcnl4200_measure(data, &data->vcnl4200_ps, val);
|
||||
}
|
||||
|
||||
static const struct vcnl4000_chip_spec vcnl4000_chip_spec_cfg[] = {
|
||||
[VCNL4000] = {
|
||||
.prod = "VCNL4000",
|
||||
.init = vcnl4000_init,
|
||||
.measure_light = vcnl4000_measure_light,
|
||||
.measure_proximity = vcnl4000_measure_proximity,
|
||||
},
|
||||
[VCNL4010] = {
|
||||
.prod = "VCNL4010/4020",
|
||||
.init = vcnl4000_init,
|
||||
.measure_light = vcnl4000_measure_light,
|
||||
.measure_proximity = vcnl4000_measure_proximity,
|
||||
},
|
||||
[VCNL4200] = {
|
||||
.prod = "VCNL4200",
|
||||
.init = vcnl4200_init,
|
||||
.measure_light = vcnl4200_measure_light,
|
||||
.measure_proximity = vcnl4200_measure_proximity,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec vcnl4000_channels[] = {
|
||||
{
|
||||
.type = IIO_LIGHT,
|
||||
@ -125,16 +301,12 @@ static int vcnl4000_read_raw(struct iio_dev *indio_dev,
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
switch (chan->type) {
|
||||
case IIO_LIGHT:
|
||||
ret = vcnl4000_measure(data,
|
||||
VCNL4000_AL_OD, VCNL4000_AL_RDY,
|
||||
VCNL4000_AL_RESULT_HI, val);
|
||||
ret = data->chip_spec->measure_light(data, val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_PROXIMITY:
|
||||
ret = vcnl4000_measure(data,
|
||||
VCNL4000_PS_OD, VCNL4000_PS_RDY,
|
||||
VCNL4000_PS_RESULT_HI, val);
|
||||
ret = data->chip_spec->measure_proximity(data, val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return IIO_VAL_INT;
|
||||
@ -146,7 +318,7 @@ static int vcnl4000_read_raw(struct iio_dev *indio_dev,
|
||||
return -EINVAL;
|
||||
|
||||
*val = 0;
|
||||
*val2 = 250000;
|
||||
*val2 = data->al_scale;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
default:
|
||||
return -EINVAL;
|
||||
@ -162,7 +334,7 @@ static int vcnl4000_probe(struct i2c_client *client,
|
||||
{
|
||||
struct vcnl4000_data *data;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret, prod_id;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
@ -171,19 +343,15 @@ static int vcnl4000_probe(struct i2c_client *client,
|
||||
data = iio_priv(indio_dev);
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
data->client = client;
|
||||
mutex_init(&data->lock);
|
||||
data->id = id->driver_data;
|
||||
data->chip_spec = &vcnl4000_chip_spec_cfg[data->id];
|
||||
|
||||
ret = i2c_smbus_read_byte_data(data->client, VCNL4000_PROD_REV);
|
||||
ret = data->chip_spec->init(data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
prod_id = ret >> 4;
|
||||
if (prod_id != VCNL4010_ID && prod_id != VCNL4000_ID)
|
||||
return -ENODEV;
|
||||
|
||||
dev_dbg(&client->dev, "%s Ambient light/proximity sensor, Rev: %02x\n",
|
||||
(prod_id == VCNL4010_ID) ? "VCNL4010/4020" : "VCNL4000",
|
||||
ret & 0xf);
|
||||
data->chip_spec->prod, data->rev);
|
||||
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->info = &vcnl4000_info;
|
||||
|
@ -94,9 +94,8 @@ static int st_press_i2c_probe(struct i2c_client *client,
|
||||
if ((ret < 0) || (ret >= ST_PRESS_MAX))
|
||||
return -ENODEV;
|
||||
|
||||
strncpy(client->name, st_press_id_table[ret].name,
|
||||
strlcpy(client->name, st_press_id_table[ret].name,
|
||||
sizeof(client->name));
|
||||
client->name[sizeof(client->name) - 1] = '\0';
|
||||
} else if (!id)
|
||||
return -ENODEV;
|
||||
|
||||
|
@ -20,6 +20,19 @@ endmenu
|
||||
|
||||
menu "Proximity and distance sensors"
|
||||
|
||||
config ISL29501
|
||||
tristate "Intersil ISL29501 Time Of Flight sensor"
|
||||
depends on I2C
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
select IIO_KFIFO_BUF
|
||||
help
|
||||
Say Y here if you want to build a driver for the Intersil ISL29501
|
||||
Time of Flight sensor.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called isl29501.
|
||||
|
||||
config LIDAR_LITE_V2
|
||||
tristate "PulsedLight LIDAR sensor"
|
||||
select IIO_BUFFER
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
# When adding new entries keep the list in alphabetical order
|
||||
obj-$(CONFIG_AS3935) += as3935.o
|
||||
obj-$(CONFIG_ISL29501) += isl29501.o
|
||||
obj-$(CONFIG_LIDAR_LITE_V2) += pulsedlight-lidar-lite-v2.o
|
||||
obj-$(CONFIG_RFD77402) += rfd77402.o
|
||||
obj-$(CONFIG_SRF04) += srf04.o
|
||||
|
1027
drivers/iio/proximity/isl29501.c
Normal file
1027
drivers/iio/proximity/isl29501.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -92,6 +92,19 @@ config TOUCHSCREEN_AD7879_SPI
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ad7879-spi.
|
||||
|
||||
config TOUCHSCREEN_ADC
|
||||
tristate "Generic ADC based resistive touchscreen"
|
||||
depends on IIO
|
||||
select IIO_BUFFER_CB
|
||||
help
|
||||
Say Y here if you want to use the generic ADC
|
||||
resistive touchscreen driver.
|
||||
|
||||
If unsure, say N (but it's safe to say "Y").
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called resistive-adc-touch.ko.
|
||||
|
||||
config TOUCHSCREEN_AR1021_I2C
|
||||
tristate "Microchip AR1020/1021 i2c touchscreen"
|
||||
depends on I2C && OF
|
||||
|
@ -13,6 +13,7 @@ obj-$(CONFIG_TOUCHSCREEN_AD7877) += ad7877.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_AD7879) += ad7879.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_AD7879_I2C) += ad7879-i2c.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_AD7879_SPI) += ad7879-spi.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_ADC) += resistive-adc-touch.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_ADS7846) += ads7846.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_AR1021_I2C) += ar1021_i2c.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_ATMEL_MXT) += atmel_mxt_ts.o
|
||||
|
204
drivers/input/touchscreen/resistive-adc-touch.c
Normal file
204
drivers/input/touchscreen/resistive-adc-touch.c
Normal file
@ -0,0 +1,204 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* ADC generic resistive touchscreen (GRTS)
|
||||
* This is a generic input driver that connects to an ADC
|
||||
* given the channels in device tree, and reports events to the input
|
||||
* subsystem.
|
||||
*
|
||||
* Copyright (C) 2017,2018 Microchip Technology,
|
||||
* Author: Eugen Hristev <eugen.hristev@microchip.com>
|
||||
*
|
||||
*/
|
||||
#include <linux/input.h>
|
||||
#include <linux/input/touchscreen.h>
|
||||
#include <linux/iio/consumer.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#define DRIVER_NAME "resistive-adc-touch"
|
||||
#define GRTS_DEFAULT_PRESSURE_MIN 50000
|
||||
#define GRTS_MAX_POS_MASK GENMASK(11, 0)
|
||||
|
||||
/**
|
||||
* grts_state - generic resistive touch screen information struct
|
||||
* @pressure_min: number representing the minimum for the pressure
|
||||
* @pressure: are we getting pressure info or not
|
||||
* @iio_chans: list of channels acquired
|
||||
* @iio_cb: iio_callback buffer for the data
|
||||
* @input: the input device structure that we register
|
||||
* @prop: touchscreen properties struct
|
||||
*/
|
||||
struct grts_state {
|
||||
u32 pressure_min;
|
||||
bool pressure;
|
||||
struct iio_channel *iio_chans;
|
||||
struct iio_cb_buffer *iio_cb;
|
||||
struct input_dev *input;
|
||||
struct touchscreen_properties prop;
|
||||
};
|
||||
|
||||
static int grts_cb(const void *data, void *private)
|
||||
{
|
||||
const u16 *touch_info = data;
|
||||
struct grts_state *st = private;
|
||||
unsigned int x, y, press = 0x0;
|
||||
|
||||
/* channel data coming in buffer in the order below */
|
||||
x = touch_info[0];
|
||||
y = touch_info[1];
|
||||
if (st->pressure)
|
||||
press = touch_info[2];
|
||||
|
||||
if ((!x && !y) || (st->pressure && (press < st->pressure_min))) {
|
||||
/* report end of touch */
|
||||
input_report_key(st->input, BTN_TOUCH, 0);
|
||||
input_sync(st->input);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* report proper touch to subsystem*/
|
||||
touchscreen_report_pos(st->input, &st->prop, x, y, false);
|
||||
if (st->pressure)
|
||||
input_report_abs(st->input, ABS_PRESSURE, press);
|
||||
input_report_key(st->input, BTN_TOUCH, 1);
|
||||
input_sync(st->input);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int grts_open(struct input_dev *dev)
|
||||
{
|
||||
int error;
|
||||
struct grts_state *st = input_get_drvdata(dev);
|
||||
|
||||
error = iio_channel_start_all_cb(st->iio_cb);
|
||||
if (error) {
|
||||
dev_err(dev->dev.parent, "failed to start callback buffer.\n");
|
||||
return error;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void grts_close(struct input_dev *dev)
|
||||
{
|
||||
struct grts_state *st = input_get_drvdata(dev);
|
||||
|
||||
iio_channel_stop_all_cb(st->iio_cb);
|
||||
}
|
||||
|
||||
static void grts_disable(void *data)
|
||||
{
|
||||
iio_channel_release_all_cb(data);
|
||||
}
|
||||
|
||||
static int grts_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct grts_state *st;
|
||||
struct input_dev *input;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct iio_channel *chan;
|
||||
int error;
|
||||
|
||||
st = devm_kzalloc(dev, sizeof(struct grts_state), GFP_KERNEL);
|
||||
if (!st)
|
||||
return -ENOMEM;
|
||||
|
||||
/* get the channels from IIO device */
|
||||
st->iio_chans = devm_iio_channel_get_all(dev);
|
||||
if (IS_ERR(st->iio_chans)) {
|
||||
error = PTR_ERR(st->iio_chans);
|
||||
if (error != -EPROBE_DEFER)
|
||||
dev_err(dev, "can't get iio channels.\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
chan = &st->iio_chans[0];
|
||||
st->pressure = false;
|
||||
while (chan && chan->indio_dev) {
|
||||
if (!strcmp(chan->channel->datasheet_name, "pressure"))
|
||||
st->pressure = true;
|
||||
chan++;
|
||||
}
|
||||
|
||||
if (st->pressure) {
|
||||
error = device_property_read_u32(dev,
|
||||
"touchscreen-min-pressure",
|
||||
&st->pressure_min);
|
||||
if (error) {
|
||||
dev_dbg(dev, "can't get touchscreen-min-pressure property.\n");
|
||||
st->pressure_min = GRTS_DEFAULT_PRESSURE_MIN;
|
||||
}
|
||||
}
|
||||
|
||||
input = devm_input_allocate_device(dev);
|
||||
if (!input) {
|
||||
dev_err(dev, "failed to allocate input device.\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
input->name = DRIVER_NAME;
|
||||
input->id.bustype = BUS_HOST;
|
||||
input->open = grts_open;
|
||||
input->close = grts_close;
|
||||
|
||||
input_set_abs_params(input, ABS_X, 0, GRTS_MAX_POS_MASK - 1, 0, 0);
|
||||
input_set_abs_params(input, ABS_Y, 0, GRTS_MAX_POS_MASK - 1, 0, 0);
|
||||
if (st->pressure)
|
||||
input_set_abs_params(input, ABS_PRESSURE, st->pressure_min,
|
||||
0xffff, 0, 0);
|
||||
|
||||
input_set_capability(input, EV_KEY, BTN_TOUCH);
|
||||
|
||||
/* parse optional device tree properties */
|
||||
touchscreen_parse_properties(input, false, &st->prop);
|
||||
|
||||
st->input = input;
|
||||
input_set_drvdata(input, st);
|
||||
|
||||
error = input_register_device(input);
|
||||
if (error) {
|
||||
dev_err(dev, "failed to register input device.");
|
||||
return error;
|
||||
}
|
||||
|
||||
st->iio_cb = iio_channel_get_all_cb(dev, grts_cb, st);
|
||||
if (IS_ERR(st->iio_cb)) {
|
||||
dev_err(dev, "failed to allocate callback buffer.\n");
|
||||
return PTR_ERR(st->iio_cb);
|
||||
}
|
||||
|
||||
error = devm_add_action_or_reset(dev, grts_disable, st->iio_cb);
|
||||
if (error) {
|
||||
dev_err(dev, "failed to add disable action.\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id grts_of_match[] = {
|
||||
{
|
||||
.compatible = "resistive-adc-touch",
|
||||
}, {
|
||||
/* sentinel */
|
||||
},
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, grts_of_match);
|
||||
|
||||
static struct platform_driver grts_driver = {
|
||||
.probe = grts_probe,
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.of_match_table = of_match_ptr(grts_of_match),
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(grts_driver);
|
||||
|
||||
MODULE_AUTHOR("Eugen Hristev <eugen.hristev@microchip.com>");
|
||||
MODULE_DESCRIPTION("Generic ADC Resistive Touch Driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -84,8 +84,6 @@ source "drivers/staging/dgnc/Kconfig"
|
||||
|
||||
source "drivers/staging/gs_fpgaboot/Kconfig"
|
||||
|
||||
source "drivers/staging/skein/Kconfig"
|
||||
|
||||
source "drivers/staging/unisys/Kconfig"
|
||||
|
||||
source "drivers/staging/clocking-wizard/Kconfig"
|
||||
@ -110,9 +108,9 @@ source "drivers/staging/vboxvideo/Kconfig"
|
||||
|
||||
source "drivers/staging/pi433/Kconfig"
|
||||
|
||||
source "drivers/staging/mt7621-pinctrl/Kconfig"
|
||||
source "drivers/staging/mt7621-pci/Kconfig"
|
||||
|
||||
source "drivers/staging/mt7621-gpio/Kconfig"
|
||||
source "drivers/staging/mt7621-pinctrl/Kconfig"
|
||||
|
||||
source "drivers/staging/mt7621-spi/Kconfig"
|
||||
|
||||
@ -124,4 +122,10 @@ source "drivers/staging/mt7621-eth/Kconfig"
|
||||
|
||||
source "drivers/staging/mt7621-dts/Kconfig"
|
||||
|
||||
source "drivers/staging/gasket/Kconfig"
|
||||
|
||||
source "drivers/staging/axis-fifo/Kconfig"
|
||||
|
||||
source "drivers/staging/erofs/Kconfig"
|
||||
|
||||
endif # STAGING
|
||||
|
@ -32,7 +32,6 @@ obj-$(CONFIG_GOLDFISH) += goldfish/
|
||||
obj-$(CONFIG_DGNC) += dgnc/
|
||||
obj-$(CONFIG_MTD_SPINAND_MT29F) += mt29f_spinand/
|
||||
obj-$(CONFIG_GS_FPGABOOT) += gs_fpgaboot/
|
||||
obj-$(CONFIG_CRYPTO_SKEIN) += skein/
|
||||
obj-$(CONFIG_UNISYSSPAR) += unisys/
|
||||
obj-$(CONFIG_COMMON_CLK_XLNX_CLKWZRD) += clocking-wizard/
|
||||
obj-$(CONFIG_FB_TFT) += fbtft/
|
||||
@ -47,9 +46,11 @@ obj-$(CONFIG_DRM_VBOXVIDEO) += vboxvideo/
|
||||
obj-$(CONFIG_PI433) += pi433/
|
||||
obj-$(CONFIG_SOC_MT7621) += mt7621-pci/
|
||||
obj-$(CONFIG_SOC_MT7621) += mt7621-pinctrl/
|
||||
obj-$(CONFIG_SOC_MT7621) += mt7621-gpio/
|
||||
obj-$(CONFIG_SOC_MT7621) += mt7621-spi/
|
||||
obj-$(CONFIG_SOC_MT7621) += mt7621-dma/
|
||||
obj-$(CONFIG_SOC_MT7621) += mt7621-mmc/
|
||||
obj-$(CONFIG_SOC_MT7621) += mt7621-eth/
|
||||
obj-$(CONFIG_SOC_MT7621) += mt7621-dts/
|
||||
obj-$(CONFIG_STAGING_GASKET_FRAMEWORK) += gasket/
|
||||
obj-$(CONFIG_XIL_AXIS_FIFO) += axis-fifo/
|
||||
obj-$(CONFIG_EROFS_FS) += erofs/
|
||||
|
@ -178,7 +178,7 @@ static int range_alloc(struct ashmem_area *asma,
|
||||
struct ashmem_range *range;
|
||||
|
||||
range = kmem_cache_zalloc(ashmem_range_cachep, GFP_KERNEL);
|
||||
if (unlikely(!range))
|
||||
if (!range)
|
||||
return -ENOMEM;
|
||||
|
||||
range->asma = asma;
|
||||
@ -246,11 +246,11 @@ static int ashmem_open(struct inode *inode, struct file *file)
|
||||
int ret;
|
||||
|
||||
ret = generic_file_open(inode, file);
|
||||
if (unlikely(ret))
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
asma = kmem_cache_zalloc(ashmem_area_cachep, GFP_KERNEL);
|
||||
if (unlikely(!asma))
|
||||
if (!asma)
|
||||
return -ENOMEM;
|
||||
|
||||
INIT_LIST_HEAD(&asma->unpinned_list);
|
||||
@ -361,14 +361,20 @@ static int ashmem_mmap(struct file *file, struct vm_area_struct *vma)
|
||||
mutex_lock(&ashmem_mutex);
|
||||
|
||||
/* user needs to SET_SIZE before mapping */
|
||||
if (unlikely(!asma->size)) {
|
||||
if (!asma->size) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* requested mapping size larger than object size */
|
||||
if (vma->vm_end - vma->vm_start > PAGE_ALIGN(asma->size)) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* requested protection bits must match our allowed protection mask */
|
||||
if (unlikely((vma->vm_flags & ~calc_vm_prot_bits(asma->prot_mask, 0)) &
|
||||
calc_vm_prot_bits(PROT_MASK, 0))) {
|
||||
if ((vma->vm_flags & ~calc_vm_prot_bits(asma->prot_mask, 0)) &
|
||||
calc_vm_prot_bits(PROT_MASK, 0)) {
|
||||
ret = -EPERM;
|
||||
goto out;
|
||||
}
|
||||
@ -446,9 +452,9 @@ ashmem_shrink_scan(struct shrinker *shrink, struct shrink_control *sc)
|
||||
loff_t start = range->pgstart * PAGE_SIZE;
|
||||
loff_t end = (range->pgend + 1) * PAGE_SIZE;
|
||||
|
||||
vfs_fallocate(range->asma->file,
|
||||
FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
|
||||
start, end - start);
|
||||
range->asma->file->f_op->fallocate(range->asma->file,
|
||||
FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
|
||||
start, end - start);
|
||||
range->purged = ASHMEM_WAS_PURGED;
|
||||
lru_del(range);
|
||||
|
||||
@ -488,7 +494,7 @@ static int set_prot_mask(struct ashmem_area *asma, unsigned long prot)
|
||||
mutex_lock(&ashmem_mutex);
|
||||
|
||||
/* the user can only remove, not add, protection bits */
|
||||
if (unlikely((asma->prot_mask & prot) != prot)) {
|
||||
if ((asma->prot_mask & prot) != prot) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
@ -526,7 +532,7 @@ static int set_name(struct ashmem_area *asma, void __user *name)
|
||||
local_name[ASHMEM_NAME_LEN - 1] = '\0';
|
||||
mutex_lock(&ashmem_mutex);
|
||||
/* cannot change an existing mapping's name */
|
||||
if (unlikely(asma->file))
|
||||
if (asma->file)
|
||||
ret = -EINVAL;
|
||||
else
|
||||
strcpy(asma->name + ASHMEM_NAME_PREFIX_LEN, local_name);
|
||||
@ -565,7 +571,7 @@ static int get_name(struct ashmem_area *asma, void __user *name)
|
||||
* Now we are just copying from the stack variable to userland
|
||||
* No lock held
|
||||
*/
|
||||
if (unlikely(copy_to_user(name, local_name, len)))
|
||||
if (copy_to_user(name, local_name, len))
|
||||
ret = -EFAULT;
|
||||
return ret;
|
||||
}
|
||||
@ -703,25 +709,25 @@ static int ashmem_pin_unpin(struct ashmem_area *asma, unsigned long cmd,
|
||||
size_t pgstart, pgend;
|
||||
int ret = -EINVAL;
|
||||
|
||||
if (unlikely(copy_from_user(&pin, p, sizeof(pin))))
|
||||
if (copy_from_user(&pin, p, sizeof(pin)))
|
||||
return -EFAULT;
|
||||
|
||||
mutex_lock(&ashmem_mutex);
|
||||
|
||||
if (unlikely(!asma->file))
|
||||
if (!asma->file)
|
||||
goto out_unlock;
|
||||
|
||||
/* per custom, you can pass zero for len to mean "everything onward" */
|
||||
if (!pin.len)
|
||||
pin.len = PAGE_ALIGN(asma->size) - pin.offset;
|
||||
|
||||
if (unlikely((pin.offset | pin.len) & ~PAGE_MASK))
|
||||
if ((pin.offset | pin.len) & ~PAGE_MASK)
|
||||
goto out_unlock;
|
||||
|
||||
if (unlikely(((__u32)-1) - pin.offset < pin.len))
|
||||
if (((__u32)-1) - pin.offset < pin.len)
|
||||
goto out_unlock;
|
||||
|
||||
if (unlikely(PAGE_ALIGN(asma->size) < pin.offset + pin.len))
|
||||
if (PAGE_ALIGN(asma->size) < pin.offset + pin.len)
|
||||
goto out_unlock;
|
||||
|
||||
pgstart = pin.offset / PAGE_SIZE;
|
||||
@ -858,7 +864,7 @@ static int __init ashmem_init(void)
|
||||
ashmem_area_cachep = kmem_cache_create("ashmem_area_cache",
|
||||
sizeof(struct ashmem_area),
|
||||
0, 0, NULL);
|
||||
if (unlikely(!ashmem_area_cachep)) {
|
||||
if (!ashmem_area_cachep) {
|
||||
pr_err("failed to create slab cache\n");
|
||||
goto out;
|
||||
}
|
||||
@ -866,13 +872,13 @@ static int __init ashmem_init(void)
|
||||
ashmem_range_cachep = kmem_cache_create("ashmem_range_cache",
|
||||
sizeof(struct ashmem_range),
|
||||
0, 0, NULL);
|
||||
if (unlikely(!ashmem_range_cachep)) {
|
||||
if (!ashmem_range_cachep) {
|
||||
pr_err("failed to create slab cache\n");
|
||||
goto out_free1;
|
||||
}
|
||||
|
||||
ret = misc_register(&ashmem_misc);
|
||||
if (unlikely(ret)) {
|
||||
if (ret) {
|
||||
pr_err("failed to register misc device!\n");
|
||||
goto out_free2;
|
||||
}
|
||||
|
@ -405,7 +405,7 @@ static int handle_vsoc_cond_wait(struct file *filp, struct vsoc_cond_wait *arg)
|
||||
int ret = 0;
|
||||
struct vsoc_device_region *region_p = vsoc_region_from_filep(filp);
|
||||
atomic_t *address = NULL;
|
||||
struct timespec ts;
|
||||
ktime_t wake_time;
|
||||
|
||||
/* Ensure that the offset is aligned */
|
||||
if (arg->offset & (sizeof(uint32_t) - 1))
|
||||
@ -433,14 +433,13 @@ static int handle_vsoc_cond_wait(struct file *filp, struct vsoc_cond_wait *arg)
|
||||
* We do things this way to flatten differences between 32 bit
|
||||
* and 64 bit timespecs.
|
||||
*/
|
||||
ts.tv_sec = arg->wake_time_sec;
|
||||
ts.tv_nsec = arg->wake_time_nsec;
|
||||
|
||||
if (!timespec_valid(&ts))
|
||||
if (arg->wake_time_nsec >= NSEC_PER_SEC)
|
||||
return -EINVAL;
|
||||
wake_time = ktime_set(arg->wake_time_sec, arg->wake_time_nsec);
|
||||
|
||||
hrtimer_init_on_stack(&to->timer, CLOCK_MONOTONIC,
|
||||
HRTIMER_MODE_ABS);
|
||||
hrtimer_set_expires_range_ns(&to->timer, timespec_to_ktime(ts),
|
||||
hrtimer_set_expires_range_ns(&to->timer, wake_time,
|
||||
current->timer_slack_ns);
|
||||
|
||||
hrtimer_init_sleeper(to, current);
|
||||
|
9
drivers/staging/axis-fifo/Kconfig
Normal file
9
drivers/staging/axis-fifo/Kconfig
Normal file
@ -0,0 +1,9 @@
|
||||
#
|
||||
# "Xilinx AXI-Stream FIFO IP core driver"
|
||||
#
|
||||
config XIL_AXIS_FIFO
|
||||
tristate "Xilinx AXI-Stream FIFO IP core driver"
|
||||
default n
|
||||
help
|
||||
This adds support for the Xilinx AXI-Stream
|
||||
FIFO IP core driver.
|
1
drivers/staging/axis-fifo/Makefile
Normal file
1
drivers/staging/axis-fifo/Makefile
Normal file
@ -0,0 +1 @@
|
||||
obj-$(CONFIG_XIL_AXIS_FIFO) += axis-fifo.o
|
0
drivers/staging/axis-fifo/README
Normal file
0
drivers/staging/axis-fifo/README
Normal file
1107
drivers/staging/axis-fifo/axis-fifo.c
Normal file
1107
drivers/staging/axis-fifo/axis-fifo.c
Normal file
File diff suppressed because it is too large
Load Diff
89
drivers/staging/axis-fifo/axis-fifo.txt
Normal file
89
drivers/staging/axis-fifo/axis-fifo.txt
Normal file
@ -0,0 +1,89 @@
|
||||
Xilinx AXI-Stream FIFO v4.1 IP core
|
||||
|
||||
This IP core has read and write AXI-Stream FIFOs, the contents of which can
|
||||
be accessed from the AXI4 memory-mapped interface. This is useful for
|
||||
transferring data from a processor into the FPGA fabric. The driver creates
|
||||
a character device that can be read/written to with standard
|
||||
open/read/write/close.
|
||||
|
||||
See Xilinx PG080 document for IP details.
|
||||
|
||||
Currently supports only store-forward mode with a 32-bit
|
||||
AXI4-Lite interface. DOES NOT support:
|
||||
- cut-through mode
|
||||
- AXI4 (non-lite)
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "xlnx,axi-fifo-mm-s-4.1"
|
||||
- interrupt-names: Should be "interrupt"
|
||||
- interrupt-parent: Should be <&intc>
|
||||
- interrupts: Should contain interrupts lines.
|
||||
- reg: Should contain registers location and length.
|
||||
- xlnx,axi-str-rxd-protocol: Should be "XIL_AXI_STREAM_ETH_DATA"
|
||||
- xlnx,axi-str-rxd-tdata-width: Should be <0x20>
|
||||
- xlnx,axi-str-txc-protocol: Should be "XIL_AXI_STREAM_ETH_CTRL"
|
||||
- xlnx,axi-str-txc-tdata-width: Should be <0x20>
|
||||
- xlnx,axi-str-txd-protocol: Should be "XIL_AXI_STREAM_ETH_DATA"
|
||||
- xlnx,axi-str-txd-tdata-width: Should be <0x20>
|
||||
- xlnx,axis-tdest-width: AXI-Stream TDEST width
|
||||
- xlnx,axis-tid-width: AXI-Stream TID width
|
||||
- xlnx,axis-tuser-width: AXI-Stream TUSER width
|
||||
- xlnx,data-interface-type: Should be <0x0>
|
||||
- xlnx,has-axis-tdest: Should be <0x0> (this feature isn't supported)
|
||||
- xlnx,has-axis-tid: Should be <0x0> (this feature isn't supported)
|
||||
- xlnx,has-axis-tkeep: Should be <0x0> (this feature isn't supported)
|
||||
- xlnx,has-axis-tstrb: Should be <0x0> (this feature isn't supported)
|
||||
- xlnx,has-axis-tuser: Should be <0x0> (this feature isn't supported)
|
||||
- xlnx,rx-fifo-depth: Depth of RX FIFO in words
|
||||
- xlnx,rx-fifo-pe-threshold: RX programmable empty interrupt threshold
|
||||
- xlnx,rx-fifo-pf-threshold: RX programmable full interrupt threshold
|
||||
- xlnx,s-axi-id-width: Should be <0x4>
|
||||
- xlnx,s-axi4-data-width: Should be <0x20>
|
||||
- xlnx,select-xpm: Should be <0x0>
|
||||
- xlnx,tx-fifo-depth: Depth of TX FIFO in words
|
||||
- xlnx,tx-fifo-pe-threshold: TX programmable empty interrupt threshold
|
||||
- xlnx,tx-fifo-pf-threshold: TX programmable full interrupt threshold
|
||||
- xlnx,use-rx-cut-through: Should be <0x0> (this feature isn't supported)
|
||||
- xlnx,use-rx-data: <0x1> if RX FIFO is enabled, <0x0> otherwise
|
||||
- xlnx,use-tx-ctrl: Should be <0x0> (this feature isn't supported)
|
||||
- xlnx,use-tx-cut-through: Should be <0x0> (this feature isn't supported)
|
||||
- xlnx,use-tx-data: <0x1> if TX FIFO is enabled, <0x0> otherwise
|
||||
|
||||
Example:
|
||||
|
||||
axi_fifo_mm_s_0: axi_fifo_mm_s@43c00000 {
|
||||
compatible = "xlnx,axi-fifo-mm-s-4.1";
|
||||
interrupt-names = "interrupt";
|
||||
interrupt-parent = <&intc>;
|
||||
interrupts = <0 29 4>;
|
||||
reg = <0x43c00000 0x10000>;
|
||||
xlnx,axi-str-rxd-protocol = "XIL_AXI_STREAM_ETH_DATA";
|
||||
xlnx,axi-str-rxd-tdata-width = <0x20>;
|
||||
xlnx,axi-str-txc-protocol = "XIL_AXI_STREAM_ETH_CTRL";
|
||||
xlnx,axi-str-txc-tdata-width = <0x20>;
|
||||
xlnx,axi-str-txd-protocol = "XIL_AXI_STREAM_ETH_DATA";
|
||||
xlnx,axi-str-txd-tdata-width = <0x20>;
|
||||
xlnx,axis-tdest-width = <0x4>;
|
||||
xlnx,axis-tid-width = <0x4>;
|
||||
xlnx,axis-tuser-width = <0x4>;
|
||||
xlnx,data-interface-type = <0x0>;
|
||||
xlnx,has-axis-tdest = <0x0>;
|
||||
xlnx,has-axis-tid = <0x0>;
|
||||
xlnx,has-axis-tkeep = <0x0>;
|
||||
xlnx,has-axis-tstrb = <0x0>;
|
||||
xlnx,has-axis-tuser = <0x0>;
|
||||
xlnx,rx-fifo-depth = <0x200>;
|
||||
xlnx,rx-fifo-pe-threshold = <0x2>;
|
||||
xlnx,rx-fifo-pf-threshold = <0x1fb>;
|
||||
xlnx,s-axi-id-width = <0x4>;
|
||||
xlnx,s-axi4-data-width = <0x20>;
|
||||
xlnx,select-xpm = <0x0>;
|
||||
xlnx,tx-fifo-depth = <0x8000>;
|
||||
xlnx,tx-fifo-pe-threshold = <0x200>;
|
||||
xlnx,tx-fifo-pf-threshold = <0x7ffb>;
|
||||
xlnx,use-rx-cut-through = <0x0>;
|
||||
xlnx,use-rx-data = <0x0>;
|
||||
xlnx,use-tx-ctrl = <0x0>;
|
||||
xlnx,use-tx-cut-through = <0x0>;
|
||||
xlnx,use-tx-data = <0x1>;
|
||||
};
|
@ -5,5 +5,5 @@
|
||||
config COMMON_CLK_XLNX_CLKWZRD
|
||||
tristate "Xilinx Clocking Wizard"
|
||||
depends on COMMON_CLK && OF
|
||||
---help---
|
||||
help
|
||||
Support for the Xilinx Clocking Wizard IP core clock generator.
|
||||
|
@ -75,14 +75,6 @@ config COMEDI_PARPORT
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called comedi_parport.
|
||||
|
||||
config COMEDI_SERIAL2002
|
||||
tristate "Driver for serial connected hardware"
|
||||
---help---
|
||||
Enable support for serial connected hardware
|
||||
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called serial2002.
|
||||
|
||||
config COMEDI_SSV_DNP
|
||||
tristate "SSV Embedded Systems DIL/Net-PC support"
|
||||
depends on X86_32 || COMPILE_TEST
|
||||
|
@ -1,4 +1,4 @@
|
||||
// SPDX-License-Identifier: LGPL-2.0+
|
||||
/* SPDX-License-Identifier: LGPL-2.0+ */
|
||||
/*
|
||||
* comedi.h
|
||||
* header file for COMEDI user API
|
||||
|
@ -1,4 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* comedi/comedi_compat32.h
|
||||
* 32-bit ioctl compatibility for 64-bit comedi kernel module.
|
||||
|
@ -60,7 +60,7 @@ struct comedi_file {
|
||||
struct comedi_subdevice *read_subdev;
|
||||
struct comedi_subdevice *write_subdev;
|
||||
unsigned int last_detach_count;
|
||||
bool last_attached:1;
|
||||
unsigned int last_attached:1;
|
||||
};
|
||||
|
||||
#define COMEDI_NUM_MINORS 0x100
|
||||
@ -79,8 +79,8 @@ MODULE_PARM_DESC(comedi_default_buf_size_kb,
|
||||
"default asynchronous buffer size in KiB (default "
|
||||
__MODULE_STRING(CONFIG_COMEDI_DEFAULT_BUF_SIZE_KB) ")");
|
||||
|
||||
unsigned int comedi_default_buf_maxsize_kb
|
||||
= CONFIG_COMEDI_DEFAULT_BUF_MAXSIZE_KB;
|
||||
unsigned int comedi_default_buf_maxsize_kb =
|
||||
CONFIG_COMEDI_DEFAULT_BUF_MAXSIZE_KB;
|
||||
module_param(comedi_default_buf_maxsize_kb, uint, 0644);
|
||||
MODULE_PARM_DESC(comedi_default_buf_maxsize_kb,
|
||||
"default maximum size of asynchronous buffer in KiB (default "
|
||||
|
@ -1,4 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* comedi_pci.h
|
||||
* header file for Comedi PCI drivers
|
||||
|
@ -1,4 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* comedi_pcmcia.h
|
||||
* header file for Comedi PCMCIA drivers
|
||||
|
@ -542,8 +542,8 @@ struct comedi_device {
|
||||
|
||||
const char *board_name;
|
||||
const void *board_ptr;
|
||||
bool attached:1;
|
||||
bool ioenabled:1;
|
||||
unsigned int attached:1;
|
||||
unsigned int ioenabled:1;
|
||||
spinlock_t spinlock; /* generic spin-lock for low-level driver */
|
||||
struct mutex mutex; /* generic mutex for COMEDI core */
|
||||
struct rw_semaphore attach_lock;
|
||||
|
@ -1,4 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* comedilib.h
|
||||
* Header file for kcomedilib
|
||||
|
@ -473,21 +473,21 @@ unsigned int comedi_nsamples_left(struct comedi_subdevice *s,
|
||||
{
|
||||
struct comedi_async *async = s->async;
|
||||
struct comedi_cmd *cmd = &async->cmd;
|
||||
unsigned long long scans_left;
|
||||
unsigned long long samples_left;
|
||||
|
||||
if (cmd->stop_src == TRIG_COUNT) {
|
||||
unsigned int scans_left = __comedi_nscans_left(s, cmd->stop_arg);
|
||||
unsigned int scan_pos =
|
||||
comedi_bytes_to_samples(s, async->scan_progress);
|
||||
unsigned long long samples_left = 0;
|
||||
if (cmd->stop_src != TRIG_COUNT)
|
||||
return nsamples;
|
||||
|
||||
if (scans_left) {
|
||||
samples_left = ((unsigned long long)scans_left *
|
||||
cmd->scan_end_arg) - scan_pos;
|
||||
}
|
||||
scans_left = __comedi_nscans_left(s, cmd->stop_arg);
|
||||
if (!scans_left)
|
||||
return 0;
|
||||
|
||||
if (samples_left < nsamples)
|
||||
nsamples = samples_left;
|
||||
}
|
||||
samples_left = scans_left * cmd->scan_end_arg -
|
||||
comedi_bytes_to_samples(s, async->scan_progress);
|
||||
|
||||
if (samples_left < nsamples)
|
||||
return samples_left;
|
||||
return nsamples;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(comedi_nsamples_left);
|
||||
|
@ -11,7 +11,6 @@ obj-$(CONFIG_COMEDI_ISADMA) += comedi_isadma.o
|
||||
obj-$(CONFIG_COMEDI_BOND) += comedi_bond.o
|
||||
obj-$(CONFIG_COMEDI_TEST) += comedi_test.o
|
||||
obj-$(CONFIG_COMEDI_PARPORT) += comedi_parport.o
|
||||
obj-$(CONFIG_COMEDI_SERIAL2002) += serial2002.o
|
||||
|
||||
# Comedi ISA drivers
|
||||
obj-$(CONFIG_COMEDI_AMPLC_DIO200_ISA) += amplc_dio200.o
|
||||
|
@ -32,9 +32,9 @@ struct dio200_board {
|
||||
unsigned short n_subdevs; /* number of subdevices */
|
||||
unsigned char sdtype[DIO200_MAX_SUBDEVS]; /* enum dio200_sdtype */
|
||||
unsigned char sdinfo[DIO200_MAX_SUBDEVS]; /* depends on sdtype */
|
||||
bool has_int_sce:1; /* has interrupt enable/status reg */
|
||||
bool has_clk_gat_sce:1; /* has clock/gate selection registers */
|
||||
bool is_pcie:1; /* has enhanced features */
|
||||
unsigned int has_int_sce:1; /* has interrupt enable/status reg */
|
||||
unsigned int has_clk_gat_sce:1; /* has clock/gate selection registers */
|
||||
unsigned int is_pcie:1; /* has enhanced features */
|
||||
};
|
||||
|
||||
int amplc_dio200_common_attach(struct comedi_device *dev, unsigned int irq,
|
||||
|
@ -96,7 +96,7 @@ struct dio200_subdev_intr {
|
||||
unsigned int ofs;
|
||||
unsigned int valid_isns;
|
||||
unsigned int enabled_isns;
|
||||
bool active:1;
|
||||
unsigned int active:1;
|
||||
};
|
||||
|
||||
static unsigned char dio200_read8(struct comedi_device *dev,
|
||||
|
@ -444,7 +444,7 @@ struct pci230_board {
|
||||
unsigned char ai_bits;
|
||||
unsigned char ao_bits;
|
||||
unsigned char min_hwver; /* Minimum hardware version supported. */
|
||||
bool have_dio:1;
|
||||
unsigned int have_dio:1;
|
||||
};
|
||||
|
||||
static const struct pci230_board pci230_boards[] = {
|
||||
@ -490,11 +490,11 @@ struct pci230_private {
|
||||
unsigned short adcg; /* ADCG register value */
|
||||
unsigned char ier; /* Interrupt enable bits */
|
||||
unsigned char res_owned[NUM_OWNERS]; /* Owned resources */
|
||||
bool intr_running:1; /* Flag set in interrupt routine */
|
||||
bool ai_bipolar:1; /* Flag AI range is bipolar */
|
||||
bool ao_bipolar:1; /* Flag AO range is bipolar */
|
||||
bool ai_cmd_started:1; /* Flag AI command started */
|
||||
bool ao_cmd_started:1; /* Flag AO command started */
|
||||
unsigned int intr_running:1; /* Flag set in interrupt routine */
|
||||
unsigned int ai_bipolar:1; /* Flag AI range is bipolar */
|
||||
unsigned int ao_bipolar:1; /* Flag AO range is bipolar */
|
||||
unsigned int ai_cmd_started:1; /* Flag AI command started */
|
||||
unsigned int ao_cmd_started:1; /* Flag AO command started */
|
||||
};
|
||||
|
||||
/* PCI230 clock source periods in ns */
|
||||
|
@ -188,7 +188,5 @@ static struct pci_driver cb_pcimdda_driver_pci_driver = {
|
||||
module_comedi_pci_driver(cb_pcimdda_driver, cb_pcimdda_driver_pci_driver);
|
||||
|
||||
MODULE_AUTHOR("Calin A. Culianu <calin@rtlab.org>");
|
||||
MODULE_DESCRIPTION("Comedi low-level driver for the Computerboards PCIM-DDA "
|
||||
"series. Currently only supports PCIM-DDA06-16 (which "
|
||||
"also happens to be the only board in this series. :) ) ");
|
||||
MODULE_DESCRIPTION("Comedi low-level driver for the Computerboards PCIM-DDA series. Currently only supports PCIM-DDA06-16 (which also happens to be the only board in this series. :) ) ");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -240,7 +240,7 @@ enum db2k_boardid {
|
||||
|
||||
struct db2k_boardtype {
|
||||
const char *name;
|
||||
bool has_2_ao:1; /* false: 4 AO chans; true: 2 AO chans */
|
||||
unsigned int has_2_ao:1;/* false: 4 AO chans; true: 2 AO chans */
|
||||
};
|
||||
|
||||
static const struct db2k_boardtype db2k_boardtypes[] = {
|
||||
|
@ -4294,7 +4294,7 @@ static int pack_ad8842(int addr, int val, int *bitstring)
|
||||
struct caldac_struct {
|
||||
int n_chans;
|
||||
int n_bits;
|
||||
int (*packbits)(int, int, int *);
|
||||
int (*packbits)(int address, int value, int *bitstring);
|
||||
};
|
||||
|
||||
static struct caldac_struct caldacs[] = {
|
||||
@ -5446,11 +5446,11 @@ static int ni_E_init(struct comedi_device *dev,
|
||||
/* Digital I/O (PFI) subdevice */
|
||||
s = &dev->subdevices[NI_PFI_DIO_SUBDEV];
|
||||
s->type = COMEDI_SUBD_DIO;
|
||||
s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_INTERNAL;
|
||||
s->maxdata = 1;
|
||||
if (devpriv->is_m_series) {
|
||||
s->n_chan = 16;
|
||||
s->insn_bits = ni_pfi_insn_bits;
|
||||
s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_INTERNAL;
|
||||
|
||||
ni_writew(dev, s->state, NI_M_PFI_DO_REG);
|
||||
for (i = 0; i < NUM_PFI_OUTPUT_SELECT_REGS; ++i) {
|
||||
@ -5459,6 +5459,7 @@ static int ni_E_init(struct comedi_device *dev,
|
||||
}
|
||||
} else {
|
||||
s->n_chan = 10;
|
||||
s->subdev_flags = SDF_INTERNAL;
|
||||
}
|
||||
s->insn_config = ni_pfi_insn_config;
|
||||
|
||||
|
@ -282,7 +282,7 @@ static int check_channel_list(struct comedi_device *dev,
|
||||
unsigned int chanlen)
|
||||
{
|
||||
unsigned int chansegment[16];
|
||||
unsigned int i, nowmustbechan, seglen, segpos;
|
||||
unsigned int i, nowmustbechan, seglen;
|
||||
|
||||
/* correct channel and range number check itself comedi/range.c */
|
||||
if (chanlen < 1) {
|
||||
@ -312,7 +312,7 @@ static int check_channel_list(struct comedi_device *dev,
|
||||
}
|
||||
|
||||
/* check whole chanlist */
|
||||
for (i = 0, segpos = 0; i < chanlen; i++) {
|
||||
for (i = 0; i < chanlen; i++) {
|
||||
if (chanlist[i] != chansegment[i % seglen]) {
|
||||
dev_dbg(dev->class_dev,
|
||||
"bad channel or range number! chanlist[%i]=%d,%d,%d and not %d,%d,%d!\n",
|
||||
|
@ -573,7 +573,7 @@ static int check_channel_list(struct comedi_device *dev,
|
||||
unsigned int *chanlist, unsigned int n_chan)
|
||||
{
|
||||
unsigned int chansegment[16];
|
||||
unsigned int i, nowmustbechan, seglen, segpos;
|
||||
unsigned int i, nowmustbechan, seglen;
|
||||
|
||||
/* correct channel and range number check itself comedi/range.c */
|
||||
if (n_chan < 1) {
|
||||
@ -605,7 +605,7 @@ static int check_channel_list(struct comedi_device *dev,
|
||||
}
|
||||
|
||||
/* check whole chanlist */
|
||||
for (i = 0, segpos = 0; i < n_chan; i++) {
|
||||
for (i = 0; i < n_chan; i++) {
|
||||
if (chanlist[i] != chansegment[i % seglen]) {
|
||||
dev_dbg(dev->class_dev,
|
||||
"bad channel or range number! chanlist[%i]=%d,%d,%d and not %d,%d,%d!\n",
|
||||
|
@ -1,778 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* serial2002.c
|
||||
* Comedi driver for serial connected hardware
|
||||
*
|
||||
* COMEDI - Linux Control and Measurement Device Interface
|
||||
* Copyright (C) 2002 Anders Blomdell <anders.blomdell@control.lth.se>
|
||||
*/
|
||||
|
||||
/*
|
||||
* Driver: serial2002
|
||||
* Description: Driver for serial connected hardware
|
||||
* Devices:
|
||||
* Author: Anders Blomdell
|
||||
* Updated: Fri, 7 Jun 2002 12:56:45 -0700
|
||||
* Status: in development
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include "../comedidev.h"
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/ktime.h>
|
||||
|
||||
#include <linux/termios.h>
|
||||
#include <asm/ioctls.h>
|
||||
#include <linux/serial.h>
|
||||
#include <linux/poll.h>
|
||||
|
||||
struct serial2002_range_table_t {
|
||||
/* HACK... */
|
||||
int length;
|
||||
struct comedi_krange range;
|
||||
};
|
||||
|
||||
struct serial2002_private {
|
||||
int port; /* /dev/ttyS<port> */
|
||||
int speed; /* baudrate */
|
||||
struct file *tty;
|
||||
unsigned int ao_readback[32];
|
||||
unsigned char digital_in_mapping[32];
|
||||
unsigned char digital_out_mapping[32];
|
||||
unsigned char analog_in_mapping[32];
|
||||
unsigned char analog_out_mapping[32];
|
||||
unsigned char encoder_in_mapping[32];
|
||||
struct serial2002_range_table_t in_range[32], out_range[32];
|
||||
};
|
||||
|
||||
struct serial_data {
|
||||
enum { is_invalid, is_digital, is_channel } kind;
|
||||
int index;
|
||||
unsigned long value;
|
||||
};
|
||||
|
||||
/*
|
||||
* The configuration serial_data.value read from the device is
|
||||
* a bitmask that defines specific options of a channel:
|
||||
*
|
||||
* 4:0 - the channel to configure
|
||||
* 7:5 - the kind of channel
|
||||
* 9:8 - the command used to configure the channel
|
||||
*
|
||||
* The remaining bits vary in use depending on the command:
|
||||
*
|
||||
* BITS 15:10 - the channel bits (maxdata)
|
||||
* MIN/MAX 12:10 - the units multiplier for the scale
|
||||
* 13 - the sign of the scale
|
||||
* 33:14 - the base value for the range
|
||||
*/
|
||||
#define S2002_CFG_CHAN(x) ((x) & 0x1f)
|
||||
#define S2002_CFG_KIND(x) (((x) >> 5) & 0x7)
|
||||
#define S2002_CFG_KIND_INVALID 0
|
||||
#define S2002_CFG_KIND_DIGITAL_IN 1
|
||||
#define S2002_CFG_KIND_DIGITAL_OUT 2
|
||||
#define S2002_CFG_KIND_ANALOG_IN 3
|
||||
#define S2002_CFG_KIND_ANALOG_OUT 4
|
||||
#define S2002_CFG_KIND_ENCODER_IN 5
|
||||
#define S2002_CFG_CMD(x) (((x) >> 8) & 0x3)
|
||||
#define S2002_CFG_CMD_BITS 0
|
||||
#define S2002_CFG_CMD_MIN 1
|
||||
#define S2002_CFG_CMD_MAX 2
|
||||
#define S2002_CFG_BITS(x) (((x) >> 10) & 0x3f)
|
||||
#define S2002_CFG_UNITS(x) (((x) >> 10) & 0x7)
|
||||
#define S2002_CFG_SIGN(x) (((x) >> 13) & 0x1)
|
||||
#define S2002_CFG_BASE(x) (((x) >> 14) & 0xfffff)
|
||||
|
||||
static long serial2002_tty_ioctl(struct file *f, unsigned int op,
|
||||
unsigned long param)
|
||||
{
|
||||
if (f->f_op->unlocked_ioctl)
|
||||
return f->f_op->unlocked_ioctl(f, op, param);
|
||||
|
||||
return -ENOTTY;
|
||||
}
|
||||
|
||||
static int serial2002_tty_write(struct file *f, unsigned char *buf, int count)
|
||||
{
|
||||
loff_t pos = 0;
|
||||
|
||||
return kernel_write(f, buf, count, &pos);
|
||||
}
|
||||
|
||||
static void serial2002_tty_read_poll_wait(struct file *f, int timeout)
|
||||
{
|
||||
struct poll_wqueues table;
|
||||
ktime_t start, now;
|
||||
|
||||
start = ktime_get();
|
||||
poll_initwait(&table);
|
||||
while (1) {
|
||||
long elapsed;
|
||||
__poll_t mask;
|
||||
|
||||
mask = vfs_poll(f, &table.pt);
|
||||
if (mask & (EPOLLRDNORM | EPOLLRDBAND | EPOLLIN |
|
||||
EPOLLHUP | EPOLLERR)) {
|
||||
break;
|
||||
}
|
||||
now = ktime_get();
|
||||
elapsed = ktime_us_delta(now, start);
|
||||
if (elapsed > timeout)
|
||||
break;
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
schedule_timeout(((timeout - elapsed) * HZ) / 10000);
|
||||
}
|
||||
poll_freewait(&table);
|
||||
}
|
||||
|
||||
static int serial2002_tty_read(struct file *f, int timeout)
|
||||
{
|
||||
unsigned char ch;
|
||||
int result;
|
||||
loff_t pos = 0;
|
||||
|
||||
result = -1;
|
||||
if (!IS_ERR(f)) {
|
||||
if (file_can_poll(f)) {
|
||||
serial2002_tty_read_poll_wait(f, timeout);
|
||||
|
||||
if (kernel_read(f, &ch, 1, &pos) == 1)
|
||||
result = ch;
|
||||
} else {
|
||||
/* Device does not support poll, busy wait */
|
||||
int retries = 0;
|
||||
|
||||
while (1) {
|
||||
retries++;
|
||||
if (retries >= timeout)
|
||||
break;
|
||||
|
||||
if (kernel_read(f, &ch, 1, &pos) == 1) {
|
||||
result = ch;
|
||||
break;
|
||||
}
|
||||
usleep_range(100, 1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static void serial2002_tty_setspeed(struct file *f, int speed)
|
||||
{
|
||||
struct termios termios;
|
||||
struct serial_struct serial;
|
||||
mm_segment_t oldfs;
|
||||
|
||||
oldfs = get_fs();
|
||||
set_fs(KERNEL_DS);
|
||||
|
||||
/* Set speed */
|
||||
serial2002_tty_ioctl(f, TCGETS, (unsigned long)&termios);
|
||||
termios.c_iflag = 0;
|
||||
termios.c_oflag = 0;
|
||||
termios.c_lflag = 0;
|
||||
termios.c_cflag = CLOCAL | CS8 | CREAD;
|
||||
termios.c_cc[VMIN] = 0;
|
||||
termios.c_cc[VTIME] = 0;
|
||||
switch (speed) {
|
||||
case 2400:
|
||||
termios.c_cflag |= B2400;
|
||||
break;
|
||||
case 4800:
|
||||
termios.c_cflag |= B4800;
|
||||
break;
|
||||
case 9600:
|
||||
termios.c_cflag |= B9600;
|
||||
break;
|
||||
case 19200:
|
||||
termios.c_cflag |= B19200;
|
||||
break;
|
||||
case 38400:
|
||||
termios.c_cflag |= B38400;
|
||||
break;
|
||||
case 57600:
|
||||
termios.c_cflag |= B57600;
|
||||
break;
|
||||
case 115200:
|
||||
termios.c_cflag |= B115200;
|
||||
break;
|
||||
default:
|
||||
termios.c_cflag |= B9600;
|
||||
break;
|
||||
}
|
||||
serial2002_tty_ioctl(f, TCSETS, (unsigned long)&termios);
|
||||
|
||||
/* Set low latency */
|
||||
serial2002_tty_ioctl(f, TIOCGSERIAL, (unsigned long)&serial);
|
||||
serial.flags |= ASYNC_LOW_LATENCY;
|
||||
serial2002_tty_ioctl(f, TIOCSSERIAL, (unsigned long)&serial);
|
||||
|
||||
set_fs(oldfs);
|
||||
}
|
||||
|
||||
static void serial2002_poll_digital(struct file *f, int channel)
|
||||
{
|
||||
char cmd;
|
||||
|
||||
cmd = 0x40 | (channel & 0x1f);
|
||||
serial2002_tty_write(f, &cmd, 1);
|
||||
}
|
||||
|
||||
static void serial2002_poll_channel(struct file *f, int channel)
|
||||
{
|
||||
char cmd;
|
||||
|
||||
cmd = 0x60 | (channel & 0x1f);
|
||||
serial2002_tty_write(f, &cmd, 1);
|
||||
}
|
||||
|
||||
static struct serial_data serial2002_read(struct file *f, int timeout)
|
||||
{
|
||||
struct serial_data result;
|
||||
int length;
|
||||
|
||||
result.kind = is_invalid;
|
||||
result.index = 0;
|
||||
result.value = 0;
|
||||
length = 0;
|
||||
while (1) {
|
||||
int data = serial2002_tty_read(f, timeout);
|
||||
|
||||
length++;
|
||||
if (data < 0) {
|
||||
break;
|
||||
} else if (data & 0x80) {
|
||||
result.value = (result.value << 7) | (data & 0x7f);
|
||||
} else {
|
||||
if (length == 1) {
|
||||
switch ((data >> 5) & 0x03) {
|
||||
case 0:
|
||||
result.value = 0;
|
||||
result.kind = is_digital;
|
||||
break;
|
||||
case 1:
|
||||
result.value = 1;
|
||||
result.kind = is_digital;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
result.value =
|
||||
(result.value << 2) | ((data & 0x60) >> 5);
|
||||
result.kind = is_channel;
|
||||
}
|
||||
result.index = data & 0x1f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static void serial2002_write(struct file *f, struct serial_data data)
|
||||
{
|
||||
if (data.kind == is_digital) {
|
||||
unsigned char ch =
|
||||
((data.value << 5) & 0x20) | (data.index & 0x1f);
|
||||
serial2002_tty_write(f, &ch, 1);
|
||||
} else {
|
||||
unsigned char ch[6];
|
||||
int i = 0;
|
||||
|
||||
if (data.value >= (1L << 30)) {
|
||||
ch[i] = 0x80 | ((data.value >> 30) & 0x03);
|
||||
i++;
|
||||
}
|
||||
if (data.value >= (1L << 23)) {
|
||||
ch[i] = 0x80 | ((data.value >> 23) & 0x7f);
|
||||
i++;
|
||||
}
|
||||
if (data.value >= (1L << 16)) {
|
||||
ch[i] = 0x80 | ((data.value >> 16) & 0x7f);
|
||||
i++;
|
||||
}
|
||||
if (data.value >= (1L << 9)) {
|
||||
ch[i] = 0x80 | ((data.value >> 9) & 0x7f);
|
||||
i++;
|
||||
}
|
||||
ch[i] = 0x80 | ((data.value >> 2) & 0x7f);
|
||||
i++;
|
||||
ch[i] = ((data.value << 5) & 0x60) | (data.index & 0x1f);
|
||||
i++;
|
||||
serial2002_tty_write(f, ch, i);
|
||||
}
|
||||
}
|
||||
|
||||
struct config_t {
|
||||
short int kind;
|
||||
short int bits;
|
||||
int min;
|
||||
int max;
|
||||
};
|
||||
|
||||
static int serial2002_setup_subdevice(struct comedi_subdevice *s,
|
||||
struct config_t *cfg,
|
||||
struct serial2002_range_table_t *range,
|
||||
unsigned char *mapping,
|
||||
int kind)
|
||||
{
|
||||
const struct comedi_lrange **range_table_list = NULL;
|
||||
unsigned int *maxdata_list;
|
||||
int j, chan;
|
||||
|
||||
for (chan = 0, j = 0; j < 32; j++) {
|
||||
if (cfg[j].kind == kind)
|
||||
chan++;
|
||||
}
|
||||
s->n_chan = chan;
|
||||
s->maxdata = 0;
|
||||
kfree(s->maxdata_list);
|
||||
maxdata_list = kmalloc_array(s->n_chan, sizeof(unsigned int),
|
||||
GFP_KERNEL);
|
||||
if (!maxdata_list)
|
||||
return -ENOMEM;
|
||||
s->maxdata_list = maxdata_list;
|
||||
kfree(s->range_table_list);
|
||||
s->range_table = NULL;
|
||||
s->range_table_list = NULL;
|
||||
if (kind == 1 || kind == 2) {
|
||||
s->range_table = &range_digital;
|
||||
} else if (range) {
|
||||
range_table_list = kmalloc_array(s->n_chan, sizeof(*range),
|
||||
GFP_KERNEL);
|
||||
if (!range_table_list)
|
||||
return -ENOMEM;
|
||||
s->range_table_list = range_table_list;
|
||||
}
|
||||
for (chan = 0, j = 0; j < 32; j++) {
|
||||
if (cfg[j].kind == kind) {
|
||||
if (mapping)
|
||||
mapping[chan] = j;
|
||||
if (range && range_table_list) {
|
||||
range[j].length = 1;
|
||||
range[j].range.min = cfg[j].min;
|
||||
range[j].range.max = cfg[j].max;
|
||||
range_table_list[chan] =
|
||||
(const struct comedi_lrange *)&range[j];
|
||||
}
|
||||
if (cfg[j].bits < 32)
|
||||
maxdata_list[chan] = (1u << cfg[j].bits) - 1;
|
||||
else
|
||||
maxdata_list[chan] = 0xffffffff;
|
||||
chan++;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int serial2002_setup_subdevs(struct comedi_device *dev)
|
||||
{
|
||||
struct serial2002_private *devpriv = dev->private;
|
||||
struct config_t *di_cfg;
|
||||
struct config_t *do_cfg;
|
||||
struct config_t *ai_cfg;
|
||||
struct config_t *ao_cfg;
|
||||
struct config_t *cfg;
|
||||
struct comedi_subdevice *s;
|
||||
int result = 0;
|
||||
int i;
|
||||
|
||||
/* Allocate the temporary structs to hold the configuration data */
|
||||
di_cfg = kcalloc(32, sizeof(*cfg), GFP_KERNEL);
|
||||
do_cfg = kcalloc(32, sizeof(*cfg), GFP_KERNEL);
|
||||
ai_cfg = kcalloc(32, sizeof(*cfg), GFP_KERNEL);
|
||||
ao_cfg = kcalloc(32, sizeof(*cfg), GFP_KERNEL);
|
||||
if (!di_cfg || !do_cfg || !ai_cfg || !ao_cfg) {
|
||||
result = -ENOMEM;
|
||||
goto err_alloc_configs;
|
||||
}
|
||||
|
||||
/* Read the configuration from the connected device */
|
||||
serial2002_tty_setspeed(devpriv->tty, devpriv->speed);
|
||||
serial2002_poll_channel(devpriv->tty, 31);
|
||||
while (1) {
|
||||
struct serial_data data = serial2002_read(devpriv->tty, 1000);
|
||||
int kind = S2002_CFG_KIND(data.value);
|
||||
int channel = S2002_CFG_CHAN(data.value);
|
||||
int range = S2002_CFG_BASE(data.value);
|
||||
int cmd = S2002_CFG_CMD(data.value);
|
||||
|
||||
if (data.kind != is_channel || data.index != 31 ||
|
||||
kind == S2002_CFG_KIND_INVALID)
|
||||
break;
|
||||
|
||||
switch (kind) {
|
||||
case S2002_CFG_KIND_DIGITAL_IN:
|
||||
cfg = di_cfg;
|
||||
break;
|
||||
case S2002_CFG_KIND_DIGITAL_OUT:
|
||||
cfg = do_cfg;
|
||||
break;
|
||||
case S2002_CFG_KIND_ANALOG_IN:
|
||||
cfg = ai_cfg;
|
||||
break;
|
||||
case S2002_CFG_KIND_ANALOG_OUT:
|
||||
cfg = ao_cfg;
|
||||
break;
|
||||
case S2002_CFG_KIND_ENCODER_IN:
|
||||
cfg = ai_cfg;
|
||||
break;
|
||||
default:
|
||||
cfg = NULL;
|
||||
break;
|
||||
}
|
||||
if (!cfg)
|
||||
continue; /* unknown kind, skip it */
|
||||
|
||||
cfg[channel].kind = kind;
|
||||
|
||||
switch (cmd) {
|
||||
case S2002_CFG_CMD_BITS:
|
||||
cfg[channel].bits = S2002_CFG_BITS(data.value);
|
||||
break;
|
||||
case S2002_CFG_CMD_MIN:
|
||||
case S2002_CFG_CMD_MAX:
|
||||
switch (S2002_CFG_UNITS(data.value)) {
|
||||
case 0:
|
||||
range *= 1000000;
|
||||
break;
|
||||
case 1:
|
||||
range *= 1000;
|
||||
break;
|
||||
case 2:
|
||||
range *= 1;
|
||||
break;
|
||||
}
|
||||
if (S2002_CFG_SIGN(data.value))
|
||||
range = -range;
|
||||
if (cmd == S2002_CFG_CMD_MIN)
|
||||
cfg[channel].min = range;
|
||||
else
|
||||
cfg[channel].max = range;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Fill in subdevice data */
|
||||
for (i = 0; i <= 4; i++) {
|
||||
unsigned char *mapping = NULL;
|
||||
struct serial2002_range_table_t *range = NULL;
|
||||
int kind = 0;
|
||||
|
||||
s = &dev->subdevices[i];
|
||||
|
||||
switch (i) {
|
||||
case 0:
|
||||
cfg = di_cfg;
|
||||
mapping = devpriv->digital_in_mapping;
|
||||
kind = S2002_CFG_KIND_DIGITAL_IN;
|
||||
break;
|
||||
case 1:
|
||||
cfg = do_cfg;
|
||||
mapping = devpriv->digital_out_mapping;
|
||||
kind = S2002_CFG_KIND_DIGITAL_OUT;
|
||||
break;
|
||||
case 2:
|
||||
cfg = ai_cfg;
|
||||
mapping = devpriv->analog_in_mapping;
|
||||
range = devpriv->in_range;
|
||||
kind = S2002_CFG_KIND_ANALOG_IN;
|
||||
break;
|
||||
case 3:
|
||||
cfg = ao_cfg;
|
||||
mapping = devpriv->analog_out_mapping;
|
||||
range = devpriv->out_range;
|
||||
kind = S2002_CFG_KIND_ANALOG_OUT;
|
||||
break;
|
||||
case 4:
|
||||
cfg = ai_cfg;
|
||||
mapping = devpriv->encoder_in_mapping;
|
||||
range = devpriv->in_range;
|
||||
kind = S2002_CFG_KIND_ENCODER_IN;
|
||||
break;
|
||||
}
|
||||
|
||||
if (serial2002_setup_subdevice(s, cfg, range, mapping, kind))
|
||||
break; /* err handled below */
|
||||
}
|
||||
if (i <= 4) {
|
||||
/*
|
||||
* Failed to allocate maxdata_list or range_table_list
|
||||
* for a subdevice that needed it.
|
||||
*/
|
||||
result = -ENOMEM;
|
||||
for (i = 0; i <= 4; i++) {
|
||||
s = &dev->subdevices[i];
|
||||
kfree(s->maxdata_list);
|
||||
s->maxdata_list = NULL;
|
||||
kfree(s->range_table_list);
|
||||
s->range_table_list = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
err_alloc_configs:
|
||||
kfree(di_cfg);
|
||||
kfree(do_cfg);
|
||||
kfree(ai_cfg);
|
||||
kfree(ao_cfg);
|
||||
|
||||
if (result) {
|
||||
if (devpriv->tty) {
|
||||
filp_close(devpriv->tty, NULL);
|
||||
devpriv->tty = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int serial2002_open(struct comedi_device *dev)
|
||||
{
|
||||
struct serial2002_private *devpriv = dev->private;
|
||||
int result;
|
||||
char port[20];
|
||||
|
||||
sprintf(port, "/dev/ttyS%d", devpriv->port);
|
||||
devpriv->tty = filp_open(port, O_RDWR, 0);
|
||||
if (IS_ERR(devpriv->tty)) {
|
||||
result = (int)PTR_ERR(devpriv->tty);
|
||||
dev_err(dev->class_dev, "file open error = %d\n", result);
|
||||
} else {
|
||||
result = serial2002_setup_subdevs(dev);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static void serial2002_close(struct comedi_device *dev)
|
||||
{
|
||||
struct serial2002_private *devpriv = dev->private;
|
||||
|
||||
if (!IS_ERR(devpriv->tty) && devpriv->tty)
|
||||
filp_close(devpriv->tty, NULL);
|
||||
}
|
||||
|
||||
static int serial2002_di_insn_read(struct comedi_device *dev,
|
||||
struct comedi_subdevice *s,
|
||||
struct comedi_insn *insn,
|
||||
unsigned int *data)
|
||||
{
|
||||
struct serial2002_private *devpriv = dev->private;
|
||||
int n;
|
||||
int chan;
|
||||
|
||||
chan = devpriv->digital_in_mapping[CR_CHAN(insn->chanspec)];
|
||||
for (n = 0; n < insn->n; n++) {
|
||||
struct serial_data read;
|
||||
|
||||
serial2002_poll_digital(devpriv->tty, chan);
|
||||
while (1) {
|
||||
read = serial2002_read(devpriv->tty, 1000);
|
||||
if (read.kind != is_digital || read.index == chan)
|
||||
break;
|
||||
}
|
||||
data[n] = read.value;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
static int serial2002_do_insn_write(struct comedi_device *dev,
|
||||
struct comedi_subdevice *s,
|
||||
struct comedi_insn *insn,
|
||||
unsigned int *data)
|
||||
{
|
||||
struct serial2002_private *devpriv = dev->private;
|
||||
int n;
|
||||
int chan;
|
||||
|
||||
chan = devpriv->digital_out_mapping[CR_CHAN(insn->chanspec)];
|
||||
for (n = 0; n < insn->n; n++) {
|
||||
struct serial_data write;
|
||||
|
||||
write.kind = is_digital;
|
||||
write.index = chan;
|
||||
write.value = data[n];
|
||||
serial2002_write(devpriv->tty, write);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
static int serial2002_ai_insn_read(struct comedi_device *dev,
|
||||
struct comedi_subdevice *s,
|
||||
struct comedi_insn *insn,
|
||||
unsigned int *data)
|
||||
{
|
||||
struct serial2002_private *devpriv = dev->private;
|
||||
int n;
|
||||
int chan;
|
||||
|
||||
chan = devpriv->analog_in_mapping[CR_CHAN(insn->chanspec)];
|
||||
for (n = 0; n < insn->n; n++) {
|
||||
struct serial_data read;
|
||||
|
||||
serial2002_poll_channel(devpriv->tty, chan);
|
||||
while (1) {
|
||||
read = serial2002_read(devpriv->tty, 1000);
|
||||
if (read.kind != is_channel || read.index == chan)
|
||||
break;
|
||||
}
|
||||
data[n] = read.value;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
static int serial2002_ao_insn_write(struct comedi_device *dev,
|
||||
struct comedi_subdevice *s,
|
||||
struct comedi_insn *insn,
|
||||
unsigned int *data)
|
||||
{
|
||||
struct serial2002_private *devpriv = dev->private;
|
||||
int n;
|
||||
int chan;
|
||||
|
||||
chan = devpriv->analog_out_mapping[CR_CHAN(insn->chanspec)];
|
||||
for (n = 0; n < insn->n; n++) {
|
||||
struct serial_data write;
|
||||
|
||||
write.kind = is_channel;
|
||||
write.index = chan;
|
||||
write.value = data[n];
|
||||
serial2002_write(devpriv->tty, write);
|
||||
devpriv->ao_readback[chan] = data[n];
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
static int serial2002_ao_insn_read(struct comedi_device *dev,
|
||||
struct comedi_subdevice *s,
|
||||
struct comedi_insn *insn,
|
||||
unsigned int *data)
|
||||
{
|
||||
struct serial2002_private *devpriv = dev->private;
|
||||
int n;
|
||||
int chan = CR_CHAN(insn->chanspec);
|
||||
|
||||
for (n = 0; n < insn->n; n++)
|
||||
data[n] = devpriv->ao_readback[chan];
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
static int serial2002_encoder_insn_read(struct comedi_device *dev,
|
||||
struct comedi_subdevice *s,
|
||||
struct comedi_insn *insn,
|
||||
unsigned int *data)
|
||||
{
|
||||
struct serial2002_private *devpriv = dev->private;
|
||||
int n;
|
||||
int chan;
|
||||
|
||||
chan = devpriv->encoder_in_mapping[CR_CHAN(insn->chanspec)];
|
||||
for (n = 0; n < insn->n; n++) {
|
||||
struct serial_data read;
|
||||
|
||||
serial2002_poll_channel(devpriv->tty, chan);
|
||||
while (1) {
|
||||
read = serial2002_read(devpriv->tty, 1000);
|
||||
if (read.kind != is_channel || read.index == chan)
|
||||
break;
|
||||
}
|
||||
data[n] = read.value;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
static int serial2002_attach(struct comedi_device *dev,
|
||||
struct comedi_devconfig *it)
|
||||
{
|
||||
struct serial2002_private *devpriv;
|
||||
struct comedi_subdevice *s;
|
||||
int ret;
|
||||
|
||||
devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
|
||||
if (!devpriv)
|
||||
return -ENOMEM;
|
||||
|
||||
devpriv->port = it->options[0];
|
||||
devpriv->speed = it->options[1];
|
||||
|
||||
ret = comedi_alloc_subdevices(dev, 5);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* digital input subdevice */
|
||||
s = &dev->subdevices[0];
|
||||
s->type = COMEDI_SUBD_DI;
|
||||
s->subdev_flags = SDF_READABLE;
|
||||
s->n_chan = 0;
|
||||
s->maxdata = 1;
|
||||
s->range_table = &range_digital;
|
||||
s->insn_read = serial2002_di_insn_read;
|
||||
|
||||
/* digital output subdevice */
|
||||
s = &dev->subdevices[1];
|
||||
s->type = COMEDI_SUBD_DO;
|
||||
s->subdev_flags = SDF_WRITABLE;
|
||||
s->n_chan = 0;
|
||||
s->maxdata = 1;
|
||||
s->range_table = &range_digital;
|
||||
s->insn_write = serial2002_do_insn_write;
|
||||
|
||||
/* analog input subdevice */
|
||||
s = &dev->subdevices[2];
|
||||
s->type = COMEDI_SUBD_AI;
|
||||
s->subdev_flags = SDF_READABLE | SDF_GROUND;
|
||||
s->n_chan = 0;
|
||||
s->maxdata = 1;
|
||||
s->range_table = NULL;
|
||||
s->insn_read = serial2002_ai_insn_read;
|
||||
|
||||
/* analog output subdevice */
|
||||
s = &dev->subdevices[3];
|
||||
s->type = COMEDI_SUBD_AO;
|
||||
s->subdev_flags = SDF_WRITABLE;
|
||||
s->n_chan = 0;
|
||||
s->maxdata = 1;
|
||||
s->range_table = NULL;
|
||||
s->insn_write = serial2002_ao_insn_write;
|
||||
s->insn_read = serial2002_ao_insn_read;
|
||||
|
||||
/* encoder input subdevice */
|
||||
s = &dev->subdevices[4];
|
||||
s->type = COMEDI_SUBD_COUNTER;
|
||||
s->subdev_flags = SDF_READABLE | SDF_LSAMPL;
|
||||
s->n_chan = 0;
|
||||
s->maxdata = 1;
|
||||
s->range_table = NULL;
|
||||
s->insn_read = serial2002_encoder_insn_read;
|
||||
|
||||
dev->open = serial2002_open;
|
||||
dev->close = serial2002_close;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void serial2002_detach(struct comedi_device *dev)
|
||||
{
|
||||
struct comedi_subdevice *s;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < dev->n_subdevices; i++) {
|
||||
s = &dev->subdevices[i];
|
||||
kfree(s->maxdata_list);
|
||||
kfree(s->range_table_list);
|
||||
}
|
||||
}
|
||||
|
||||
static struct comedi_driver serial2002_driver = {
|
||||
.driver_name = "serial2002",
|
||||
.module = THIS_MODULE,
|
||||
.attach = serial2002_attach,
|
||||
.detach = serial2002_detach,
|
||||
};
|
||||
module_comedi_driver(serial2002_driver);
|
||||
|
||||
MODULE_AUTHOR("Comedi http://www.comedi.org");
|
||||
MODULE_DESCRIPTION("Comedi low-level driver");
|
||||
MODULE_LICENSE("GPL");
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user