Staging/IIO patches for 4.18-rc1
Here is the big staging and IIO driver update for 4.18-rc1. It was delayed as I wanted to make sure the final driver deletions did not cause any major merge issues, and all now looks good. There are a lot of patches here, just over 1000. The diffstat summary shows the major changes here: 1007 files changed, 16828 insertions(+), 227770 deletions(-) Because of this, we might be close to shrinking the overall kernel source code size for two releases in a row. There was loads of work in this release cycle, primarily: - tons of ks7010 driver cleanups - lots of mt7621 driver fixes and cleanups - most driver cleanups - wilc1000 fixes and cleanups - lots and lots of IIO driver cleanups and new additions - debugfs cleanups for all staging drivers - lots of other staging driver cleanups and fixes, the shortlog has the full details. but the big user-visable things here are the removal of 3 chunks of code: - ncpfs and ipx were removed on schedule, no one has cared about this code since it moved to staging last year, and if it needs to come back, it can be reverted. - lustre file system is removed. I've ranted at the lustre developers about once a year for the past 5 years, with no real forward progress at all to clean things up and get the code into the "real" part of the kernel. Given that the lustre developers continue to work on an external tree and try to port those changes to the in-kernel tree every once in a while, this whole thing really really is not working out at all. So I'm deleting it so that the developers can spend the time working in their out-of-tree location and get things cleaned up properly to get merged into the tree correctly at a later date. Because of these file removals, you will have merge issues on some of these files (2 in the ipx code, 1 in the ncpfs code, and 1 in the atomisp driver). Just delete those files, it's a simple merge :) All of this has been in linux-next for a while with no reported problems. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCWxvjGQ8cZ3JlZ0Brcm9h aC5jb20ACgkQMUfUDdst+ymoEwCbBYnyUl3cwCszIJ3L3/zvUWpmqIgAn1DDsAim dM4lmKg6HX/JBSV4GAN0 =zdta -----END PGP SIGNATURE----- Merge tag 'staging-4.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging Pull staging/IIO updates from Greg KH: "Here is the big staging and IIO driver update for 4.18-rc1. It was delayed as I wanted to make sure the final driver deletions did not cause any major merge issues, and all now looks good. There are a lot of patches here, just over 1000. The diffstat summary shows the major changes here: 1007 files changed, 16828 insertions(+), 227770 deletions(-) Because of this, we might be close to shrinking the overall kernel source code size for two releases in a row. There was loads of work in this release cycle, primarily: - tons of ks7010 driver cleanups - lots of mt7621 driver fixes and cleanups - most driver cleanups - wilc1000 fixes and cleanups - lots and lots of IIO driver cleanups and new additions - debugfs cleanups for all staging drivers - lots of other staging driver cleanups and fixes, the shortlog has the full details. but the big user-visable things here are the removal of 3 chunks of code: - ncpfs and ipx were removed on schedule, no one has cared about this code since it moved to staging last year, and if it needs to come back, it can be reverted. - lustre file system is removed. I've ranted at the lustre developers about once a year for the past 5 years, with no real forward progress at all to clean things up and get the code into the "real" part of the kernel. Given that the lustre developers continue to work on an external tree and try to port those changes to the in-kernel tree every once in a while, this whole thing really really is not working out at all. So I'm deleting it so that the developers can spend the time working in their out-of-tree location and get things cleaned up properly to get merged into the tree correctly at a later date. Because of these file removals, you will have merge issues on some of these files (2 in the ipx code, 1 in the ncpfs code, and 1 in the atomisp driver). Just delete those files, it's a simple merge :) All of this has been in linux-next for a while with no reported problems" * tag 'staging-4.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging: (1011 commits) staging: ipx: delete it from the tree ncpfs: remove uapi .h files ncpfs: remove Documentation ncpfs: remove compat functionality staging: ncpfs: delete it staging: lustre: delete the filesystem from the tree. staging: vc04_services: no need to save the log debufs dentries staging: vc04_services: vchiq_debugfs_log_entry can be a void * staging: vc04_services: remove struct vchiq_debugfs_info staging: vc04_services: move client dbg directory into static variable staging: vc04_services: remove odd vchiq_debugfs_top() wrapper staging: vc04_services: no need to check debugfs return values staging: mt7621-gpio: reorder includes alphabetically staging: mt7621-gpio: change gc_map to don't use pointers staging: mt7621-gpio: use GPIOF_DIR_OUT and GPIOF_DIR_IN macros instead of custom values staging: mt7621-gpio: change 'to_mediatek_gpio' to make just a one line return staging: mt7621-gpio: dt-bindings: update documentation for #interrupt-cells property staging: mt7621-gpio: update #interrupt-cells for the gpio node staging: mt7621-gpio: dt-bindings: complete documentation for the gpio staging: mt7621-dts: add missing properties to gpio node ...
This commit is contained in:
commit
eafdca4d70
@ -190,6 +190,13 @@ Description:
|
||||
but should match other such assignments on device).
|
||||
Units after application of scale and offset are m/s^2.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_angl_raw
|
||||
KernelVersion: 4.17
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Angle of rotation. Units after application of scale and offset
|
||||
are radians.
|
||||
|
||||
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
|
||||
@ -297,6 +304,7 @@ What: /sys/bus/iio/devices/iio:deviceX/in_pressure_offset
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_humidityrelative_offset
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_magn_offset
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_rot_offset
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_angl_offset
|
||||
KernelVersion: 2.6.35
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
@ -350,6 +358,7 @@ What: /sys/bus/iio/devices/iio:deviceX/in_humidityrelative_scale
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_velocity_sqrt(x^2+y^2+z^2)_scale
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_illuminance_scale
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_countY_scale
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_angl_scale
|
||||
KernelVersion: 2.6.35
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
|
@ -7,6 +7,7 @@ Required properties:
|
||||
- "amlogic,meson-gxbb-saradc" for GXBB
|
||||
- "amlogic,meson-gxl-saradc" for GXL
|
||||
- "amlogic,meson-gxm-saradc" for GXM
|
||||
- "amlogic,meson-axg-saradc" for AXG
|
||||
along with the generic "amlogic,meson-saradc"
|
||||
- reg: the physical base address and length of the registers
|
||||
- interrupts: the interrupt indicating end of sampling
|
||||
|
@ -49,7 +49,7 @@ Required properties:
|
||||
Examples:
|
||||
spi_controller {
|
||||
mcp3x0x@0 {
|
||||
compatible = "mcp3002";
|
||||
compatible = "microchip,mcp3002";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <1000000>;
|
||||
vref-supply = <&vref_reg>;
|
||||
|
@ -24,8 +24,11 @@ Required properties:
|
||||
- compatible: Should be one of:
|
||||
"st,stm32f4-adc-core"
|
||||
"st,stm32h7-adc-core"
|
||||
"st,stm32mp1-adc-core"
|
||||
- reg: Offset and length of the ADC block register set.
|
||||
- interrupts: Must contain the interrupt for ADC block.
|
||||
- interrupts: One or more interrupts for ADC block. Some parts like stm32f4
|
||||
and stm32h7 share a common ADC interrupt line. stm32mp1 has two separate
|
||||
interrupt lines, one for each ADC within ADC block.
|
||||
- clocks: Core can use up to two clocks, depending on part used:
|
||||
- "adc" clock: for the analog circuitry, common to all ADCs.
|
||||
It's required on stm32f4.
|
||||
@ -53,6 +56,7 @@ Required properties:
|
||||
- compatible: Should be one of:
|
||||
"st,stm32f4-adc"
|
||||
"st,stm32h7-adc"
|
||||
"st,stm32mp1-adc"
|
||||
- reg: Offset of ADC instance in ADC block (e.g. may be 0x0, 0x100, 0x200).
|
||||
- clocks: Input clock private to this ADC instance. It's required only on
|
||||
stm32f4, that has per instance clock input for registers access.
|
||||
|
@ -8,14 +8,16 @@ It is mainly targeted for:
|
||||
- PDM microphones (audio digital microphone)
|
||||
|
||||
It features up to 8 serial digital interfaces (SPI or Manchester) and
|
||||
up to 4 filters on stm32h7.
|
||||
up to 4 filters on stm32h7 or 6 filters on stm32mp1.
|
||||
|
||||
Each child node match with a filter instance.
|
||||
|
||||
Contents of a STM32 DFSDM root node:
|
||||
------------------------------------
|
||||
Required properties:
|
||||
- compatible: Should be "st,stm32h7-dfsdm".
|
||||
- compatible: Should be one of:
|
||||
"st,stm32h7-dfsdm"
|
||||
"st,stm32mp1-dfsdm"
|
||||
- reg: Offset and length of the DFSDM block register set.
|
||||
- clocks: IP and serial interfaces clocking. Should be set according
|
||||
to rcc clock ID and "clock-names".
|
||||
@ -45,6 +47,7 @@ Required properties:
|
||||
"st,stm32-dfsdm-adc" for sigma delta ADCs
|
||||
"st,stm32-dfsdm-dmic" for audio digital microphone.
|
||||
- reg: Specifies the DFSDM filter instance used.
|
||||
Valid values are from 0 to 3 on stm32h7, 0 to 5 on stm32mp1.
|
||||
- interrupts: IRQ lines connected to each DFSDM filter instance.
|
||||
- st,adc-channels: List of single-ended channels muxed for this ADC.
|
||||
valid values:
|
||||
|
@ -0,0 +1,26 @@
|
||||
Current Sense Amplifier
|
||||
=======================
|
||||
|
||||
When an io-channel measures the output voltage from a current sense
|
||||
amplifier, the interesting measurement is almost always the current
|
||||
through the sense resistor, not the voltage output. This binding
|
||||
describes such a current sense circuit.
|
||||
|
||||
Required properties:
|
||||
- compatible : "current-sense-amplifier"
|
||||
- io-channels : Channel node of a voltage io-channel.
|
||||
- sense-resistor-micro-ohms : The sense resistance in microohms.
|
||||
|
||||
Optional properties:
|
||||
- sense-gain-mult: Amplifier gain multiplier. The default is <1>.
|
||||
- sense-gain-div: Amplifier gain divider. The default is <1>.
|
||||
|
||||
Example:
|
||||
|
||||
sysi {
|
||||
compatible = "current-sense-amplifier";
|
||||
io-channels = <&tiadc 0>;
|
||||
|
||||
sense-resistor-micro-ohms = <20000>;
|
||||
sense-gain-mul = <50>;
|
||||
};
|
@ -0,0 +1,41 @@
|
||||
Current Sense Shunt
|
||||
===================
|
||||
|
||||
When an io-channel measures the voltage over a current sense shunt,
|
||||
the interesting measurement is almost always the current through the
|
||||
shunt, not the voltage over it. This binding describes such a current
|
||||
sense circuit.
|
||||
|
||||
Required properties:
|
||||
- compatible : "current-sense-shunt"
|
||||
- io-channels : Channel node of a voltage io-channel.
|
||||
- shunt-resistor-micro-ohms : The shunt resistance in microohms.
|
||||
|
||||
Example:
|
||||
The system current is measured by measuring the voltage over a
|
||||
3.3 ohms shunt resistor.
|
||||
|
||||
sysi {
|
||||
compatible = "current-sense-shunt";
|
||||
io-channels = <&tiadc 0>;
|
||||
|
||||
/* Divide the voltage by 3300000/1000000 (or 3.3) for the current. */
|
||||
shunt-resistor-micro-ohms = <3300000>;
|
||||
};
|
||||
|
||||
&i2c {
|
||||
tiadc: adc@48 {
|
||||
compatible = "ti,ads1015";
|
||||
reg = <0x48>;
|
||||
#io-channel-cells = <1>;
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
channel@0 { /* IN0,IN1 differential */
|
||||
reg = <0>;
|
||||
ti,gain = <1>;
|
||||
ti,datarate = <4>;
|
||||
};
|
||||
};
|
||||
};
|
@ -0,0 +1,53 @@
|
||||
Voltage divider
|
||||
===============
|
||||
|
||||
When an io-channel measures the midpoint of a voltage divider, the
|
||||
interesting voltage is often the voltage over the full resistance
|
||||
of the divider. This binding describes the voltage divider in such
|
||||
a curcuit.
|
||||
|
||||
Vin ----.
|
||||
|
|
||||
.-----.
|
||||
| R |
|
||||
'-----'
|
||||
|
|
||||
+---- Vout
|
||||
|
|
||||
.-----.
|
||||
| Rout|
|
||||
'-----'
|
||||
|
|
||||
GND
|
||||
|
||||
Required properties:
|
||||
- compatible : "voltage-divider"
|
||||
- io-channels : Channel node of a voltage io-channel measuring Vout.
|
||||
- output-ohms : Resistance Rout over which the output voltage is measured.
|
||||
See full-ohms.
|
||||
- full-ohms : Resistance R + Rout for the full divider. The io-channel
|
||||
is scaled by the Rout / (R + Rout) quotient.
|
||||
|
||||
Example:
|
||||
The system voltage is circa 12V, but divided down with a 22/222
|
||||
voltage divider (R = 200 Ohms, Rout = 22 Ohms) and fed to an ADC.
|
||||
|
||||
sysv {
|
||||
compatible = "voltage-divider";
|
||||
io-channels = <&maxadc 1>;
|
||||
|
||||
/* Scale the system voltage by 22/222 to fit the ADC range. */
|
||||
output-ohms = <22>;
|
||||
full-ohms = <222>; /* 200 + 22 */
|
||||
};
|
||||
|
||||
&spi {
|
||||
maxadc: adc@0 {
|
||||
compatible = "maxim,max1027";
|
||||
reg = <0>;
|
||||
#io-channel-cells = <1>;
|
||||
interrupt-parent = <&gpio5>;
|
||||
interrupts = <15 IRQ_TYPE_EDGE_RISING>;
|
||||
spi-max-frequency = <1000000>;
|
||||
};
|
||||
};
|
@ -12,12 +12,26 @@ Required properties:
|
||||
Property rules described in Documentation/devicetree/bindings/spi/spi-bus.txt
|
||||
apply. In particular, "reg" and "spi-max-frequency" properties must be given.
|
||||
|
||||
Optional properties:
|
||||
- vref-supply: Phandle to the external reference voltage supply. This should
|
||||
only be set if there is an external reference voltage connected to the VREF
|
||||
pin. If the property is not set the internal reference is used.
|
||||
|
||||
Example:
|
||||
|
||||
vref: regulator-vref {
|
||||
compatible = "regulator-fixed";
|
||||
regulator-name = "vref-ltc2632";
|
||||
regulator-min-microvolt = <1250000>;
|
||||
regulator-max-microvolt = <1250000>;
|
||||
regulator-always-on;
|
||||
};
|
||||
|
||||
spi_master {
|
||||
dac: ltc2632@0 {
|
||||
compatible = "lltc,ltc2632-l12";
|
||||
reg = <0>; /* CS0 */
|
||||
spi-max-frequency = <1000000>;
|
||||
vref-supply = <&vref>; /* optional */
|
||||
};
|
||||
};
|
||||
|
24
Documentation/devicetree/bindings/iio/dac/ti,dac5571.txt
Normal file
24
Documentation/devicetree/bindings/iio/dac/ti,dac5571.txt
Normal file
@ -0,0 +1,24 @@
|
||||
* Texas Instruments DAC5571 Family
|
||||
|
||||
Required properties:
|
||||
- compatible: Should contain
|
||||
"ti,dac5571"
|
||||
"ti,dac6571"
|
||||
"ti,dac7571"
|
||||
"ti,dac5574"
|
||||
"ti,dac6574"
|
||||
"ti,dac7574"
|
||||
"ti,dac5573"
|
||||
"ti,dac6573"
|
||||
"ti,dac7573"
|
||||
- reg: Should contain the DAC I2C address
|
||||
|
||||
Optional properties:
|
||||
- vref-supply: The regulator supply for DAC reference voltage
|
||||
|
||||
Example:
|
||||
dac@0 {
|
||||
compatible = "ti,dac5571";
|
||||
reg = <0x4C>;
|
||||
vref-supply = <&vdd_supply>;
|
||||
};
|
@ -8,10 +8,16 @@ Required properties:
|
||||
"invensense,mpu6500"
|
||||
"invensense,mpu9150"
|
||||
"invensense,mpu9250"
|
||||
"invensense,mpu9255"
|
||||
"invensense,icm20608"
|
||||
- reg : the I2C address of the sensor
|
||||
- interrupt-parent : should be the phandle for the interrupt controller
|
||||
- interrupts : interrupt mapping for GPIO IRQ
|
||||
- interrupts: interrupt mapping for IRQ. It should be configured with flags
|
||||
IRQ_TYPE_LEVEL_HIGH, IRQ_TYPE_EDGE_RISING, IRQ_TYPE_LEVEL_LOW or
|
||||
IRQ_TYPE_EDGE_FALLING.
|
||||
|
||||
Refer to interrupt-controller/interrupts.txt for generic interrupt client node
|
||||
bindings.
|
||||
|
||||
Optional properties:
|
||||
- mount-matrix: an optional 3x3 mounting rotation matrix
|
||||
@ -24,7 +30,7 @@ Example:
|
||||
compatible = "invensense,mpu6050";
|
||||
reg = <0x68>;
|
||||
interrupt-parent = <&gpio1>;
|
||||
interrupts = <18 1>;
|
||||
interrupts = <18 IRQ_TYPE_EDGE_RISING>;
|
||||
mount-matrix = "-0.984807753012208", /* x0 */
|
||||
"0", /* y0 */
|
||||
"-0.173648177666930", /* z0 */
|
||||
@ -41,7 +47,7 @@ Example:
|
||||
compatible = "invensense,mpu9250";
|
||||
reg = <0x68>;
|
||||
interrupt-parent = <&gpio3>;
|
||||
interrupts = <21 1>;
|
||||
interrupts = <21 IRQ_TYPE_LEVEL_HIGH>;
|
||||
i2c-gate {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
@ -6,6 +6,7 @@ Required properties:
|
||||
"st,lsm6ds3h"
|
||||
"st,lsm6dsl"
|
||||
"st,lsm6dsm"
|
||||
"st,ism330dlc"
|
||||
- reg: i2c address of the sensor / spi cs line
|
||||
|
||||
Optional properties:
|
||||
|
@ -1,10 +1,13 @@
|
||||
* Texas Instruments LMP91000 potentiostat
|
||||
* Texas Instruments LMP91000 series of potentiostats
|
||||
|
||||
http://www.ti.com/lit/ds/symlink/lmp91000.pdf
|
||||
LMP91000: http://www.ti.com/lit/ds/symlink/lmp91000.pdf
|
||||
LMP91002: http://www.ti.com/lit/ds/symlink/lmp91002.pdf
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: should be "ti,lmp91000"
|
||||
- compatible: should be one of the following:
|
||||
"ti,lmp91000"
|
||||
"ti,lmp91002"
|
||||
- reg: the I2C address of the device
|
||||
- io-channels: the phandle of the iio provider
|
||||
|
||||
|
@ -89,8 +89,6 @@ locks.txt
|
||||
- info on file locking implementations, flock() vs. fcntl(), etc.
|
||||
mandatory-locking.txt
|
||||
- info on the Linux implementation of Sys V mandatory file locking.
|
||||
ncpfs.txt
|
||||
- info on Novell Netware(tm) filesystem using NCP protocol.
|
||||
nfs/
|
||||
- nfs-related documentation.
|
||||
nilfs2.txt
|
||||
|
@ -1,12 +0,0 @@
|
||||
The ncpfs filesystem understands the NCP protocol, designed by the
|
||||
Novell Corporation for their NetWare(tm) product. NCP is functionally
|
||||
similar to the NFS used in the TCP/IP community.
|
||||
To mount a NetWare filesystem, you need a special mount program, which
|
||||
can be found in the ncpfs package. The home site for ncpfs is
|
||||
ftp.gwdg.de/pub/linux/misc/ncpfs, but sunsite and its many mirrors
|
||||
will have it as well.
|
||||
|
||||
Related products are linware and mars_nwe, which will give Linux partial
|
||||
NetWare server functionality.
|
||||
|
||||
mars_nwe can be found on ftp.gwdg.de/pub/linux/misc/ncpfs.
|
32
MAINTAINERS
32
MAINTAINERS
@ -796,6 +796,14 @@ M: Michael Hanselmann <linux-kernel@hansmi.ch>
|
||||
S: Supported
|
||||
F: drivers/macintosh/ams/
|
||||
|
||||
ANALOG DEVICES INC AD5686 DRIVER
|
||||
M: Stefan Popa <stefan.popa@analog.com>
|
||||
L: linux-pm@vger.kernel.org
|
||||
W: http://ez.analog.com/community/linux-device-drivers
|
||||
S: Supported
|
||||
F: drivers/iio/dac/ad5686*
|
||||
F: drivers/iio/dac/ad5696*
|
||||
|
||||
ANALOG DEVICES INC AD9389B DRIVER
|
||||
M: Hans Verkuil <hans.verkuil@cisco.com>
|
||||
L: linux-media@vger.kernel.org
|
||||
@ -4421,6 +4429,12 @@ L: linux-kernel@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/staging/fsl-dpaa2/ethsw
|
||||
|
||||
DPAA2 PTP CLOCK DRIVER
|
||||
M: Yangbo Lu <yangbo.lu@nxp.com>
|
||||
L: linux-kernel@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/staging/fsl-dpaa2/rtc
|
||||
|
||||
DPT_I2O SCSI RAID DRIVER
|
||||
M: Adaptec OEM Raid Solutions <aacraid@microsemi.com>
|
||||
L: linux-scsi@vger.kernel.org
|
||||
@ -6968,6 +6982,15 @@ F: drivers/staging/iio/
|
||||
F: include/linux/iio/
|
||||
F: tools/iio/
|
||||
|
||||
IIO UNIT CONVERTER
|
||||
M: Peter Rosin <peda@axentia.se>
|
||||
L: linux-iio@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/iio/afe/current-sense-amplifier.txt
|
||||
F: Documentation/devicetree/bindings/iio/afe/current-sense-shunt.txt
|
||||
F: Documentation/devicetree/bindings/iio/afe/voltage-divider.txt
|
||||
F: drivers/iio/afe/iio-rescale.c
|
||||
|
||||
IKANOS/ADI EAGLE ADSL USB DRIVER
|
||||
M: Matthieu Castet <castet.matthieu@free.fr>
|
||||
M: Stanislaw Gruszka <stf_xl@wp.pl>
|
||||
@ -13433,15 +13456,6 @@ S: Odd Fixes
|
||||
F: Documentation/devicetree/bindings/staging/iio/
|
||||
F: drivers/staging/iio/
|
||||
|
||||
STAGING - LUSTRE PARALLEL FILESYSTEM
|
||||
M: Oleg Drokin <oleg.drokin@intel.com>
|
||||
M: Andreas Dilger <andreas.dilger@intel.com>
|
||||
M: James Simmons <jsimmons@infradead.org>
|
||||
L: lustre-devel@lists.lustre.org (moderated for non-subscribers)
|
||||
W: http://wiki.lustre.org/
|
||||
S: Maintained
|
||||
F: drivers/staging/lustre
|
||||
|
||||
STAGING - NVIDIA COMPLIANT EMBEDDED CONTROLLER INTERFACE (nvec)
|
||||
M: Marc Dietrich <marvin24@gmx.de>
|
||||
L: ac100@lists.launchpad.net (moderated for non-subscribers)
|
||||
|
@ -70,6 +70,7 @@ config IIO_TRIGGERED_EVENT
|
||||
|
||||
source "drivers/iio/accel/Kconfig"
|
||||
source "drivers/iio/adc/Kconfig"
|
||||
source "drivers/iio/afe/Kconfig"
|
||||
source "drivers/iio/amplifiers/Kconfig"
|
||||
source "drivers/iio/chemical/Kconfig"
|
||||
source "drivers/iio/common/Kconfig"
|
||||
@ -92,6 +93,7 @@ source "drivers/iio/potentiometer/Kconfig"
|
||||
source "drivers/iio/potentiostat/Kconfig"
|
||||
source "drivers/iio/pressure/Kconfig"
|
||||
source "drivers/iio/proximity/Kconfig"
|
||||
source "drivers/iio/resolver/Kconfig"
|
||||
source "drivers/iio/temperature/Kconfig"
|
||||
|
||||
endif # IIO
|
||||
|
@ -15,6 +15,7 @@ obj-$(CONFIG_IIO_TRIGGERED_EVENT) += industrialio-triggered-event.o
|
||||
|
||||
obj-y += accel/
|
||||
obj-y += adc/
|
||||
obj-y += afe/
|
||||
obj-y += amplifiers/
|
||||
obj-y += buffer/
|
||||
obj-y += chemical/
|
||||
@ -35,5 +36,6 @@ obj-y += potentiometer/
|
||||
obj-y += potentiostat/
|
||||
obj-y += pressure/
|
||||
obj-y += proximity/
|
||||
obj-y += resolver/
|
||||
obj-y += temperature/
|
||||
obj-y += trigger/
|
||||
|
@ -5,6 +5,30 @@
|
||||
|
||||
menu "Accelerometers"
|
||||
|
||||
config ADIS16201
|
||||
tristate "Analog Devices ADIS16201 Dual-Axis Digital Inclinometer and Accelerometer"
|
||||
depends on SPI
|
||||
select IIO_ADIS_LIB
|
||||
select IIO_ADIS_LIB_BUFFER if IIO_BUFFER
|
||||
help
|
||||
Say Y here to build support for Analog Devices adis16201 dual-axis
|
||||
digital inclinometer and accelerometer.
|
||||
|
||||
To compile this driver as a module, say M here: the module will
|
||||
be called adis16201.
|
||||
|
||||
config ADIS16209
|
||||
tristate "Analog Devices ADIS16209 Dual-Axis Digital Inclinometer and Accelerometer"
|
||||
depends on SPI
|
||||
select IIO_ADIS_LIB
|
||||
select IIO_ADIS_LIB_BUFFER if IIO_BUFFER
|
||||
help
|
||||
Say Y here to build support for Analog Devices adis16209 dual-axis digital inclinometer
|
||||
and accelerometer.
|
||||
|
||||
To compile this driver as a module, say M here: the module will be
|
||||
called adis16209.
|
||||
|
||||
config ADXL345
|
||||
tristate
|
||||
|
||||
|
@ -4,6 +4,8 @@
|
||||
#
|
||||
|
||||
# When adding new entries keep the list in alphabetical order
|
||||
obj-$(CONFIG_ADIS16201) += adis16201.o
|
||||
obj-$(CONFIG_ADIS16209) += adis16209.o
|
||||
obj-$(CONFIG_ADXL345) += adxl345_core.o
|
||||
obj-$(CONFIG_ADXL345_I2C) += adxl345_i2c.o
|
||||
obj-$(CONFIG_ADXL345_SPI) += adxl345_spi.o
|
||||
|
321
drivers/iio/accel/adis16201.c
Normal file
321
drivers/iio/accel/adis16201.c
Normal file
@ -0,0 +1,321 @@
|
||||
/*
|
||||
* ADIS16201 Dual-Axis Digital Inclinometer and Accelerometer
|
||||
*
|
||||
* Copyright 2010 Analog Devices Inc.
|
||||
*
|
||||
* Licensed under the GPL-2 or later.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/sysfs.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/imu/adis.h>
|
||||
|
||||
#define ADIS16201_STARTUP_DELAY_MS 220
|
||||
#define ADIS16201_FLASH_CNT 0x00
|
||||
|
||||
/* Data Output Register Information */
|
||||
#define ADIS16201_SUPPLY_OUT_REG 0x02
|
||||
#define ADIS16201_XACCL_OUT_REG 0x04
|
||||
#define ADIS16201_YACCL_OUT_REG 0x06
|
||||
#define ADIS16201_AUX_ADC_REG 0x08
|
||||
#define ADIS16201_TEMP_OUT_REG 0x0A
|
||||
#define ADIS16201_XINCL_OUT_REG 0x0C
|
||||
#define ADIS16201_YINCL_OUT_REG 0x0E
|
||||
|
||||
/* Calibration Register Definition */
|
||||
#define ADIS16201_XACCL_OFFS_REG 0x10
|
||||
#define ADIS16201_YACCL_OFFS_REG 0x12
|
||||
#define ADIS16201_XACCL_SCALE_REG 0x14
|
||||
#define ADIS16201_YACCL_SCALE_REG 0x16
|
||||
#define ADIS16201_XINCL_OFFS_REG 0x18
|
||||
#define ADIS16201_YINCL_OFFS_REG 0x1A
|
||||
#define ADIS16201_XINCL_SCALE_REG 0x1C
|
||||
#define ADIS16201_YINCL_SCALE_REG 0x1E
|
||||
|
||||
/* Alarm Register Definition */
|
||||
#define ADIS16201_ALM_MAG1_REG 0x20
|
||||
#define ADIS16201_ALM_MAG2_REG 0x22
|
||||
#define ADIS16201_ALM_SMPL1_REG 0x24
|
||||
#define ADIS16201_ALM_SMPL2_REG 0x26
|
||||
#define ADIS16201_ALM_CTRL_REG 0x28
|
||||
|
||||
#define ADIS16201_AUX_DAC_REG 0x30
|
||||
#define ADIS16201_GPIO_CTRL_REG 0x32
|
||||
#define ADIS16201_SMPL_PRD_REG 0x36
|
||||
/* Operation, filter configuration */
|
||||
#define ADIS16201_AVG_CNT_REG 0x38
|
||||
#define ADIS16201_SLP_CNT_REG 0x3A
|
||||
|
||||
/* Miscellaneous Control Register Definition */
|
||||
#define ADIS16201_MSC_CTRL_REG 0x34
|
||||
#define ADIS16201_MSC_CTRL_SELF_TEST_EN BIT(8)
|
||||
/* Data-ready enable: 1 = enabled, 0 = disabled */
|
||||
#define ADIS16201_MSC_CTRL_DATA_RDY_EN BIT(2)
|
||||
/* Data-ready polarity: 1 = active high, 0 = active low */
|
||||
#define ADIS16201_MSC_CTRL_ACTIVE_DATA_RDY_HIGH BIT(1)
|
||||
/* Data-ready line selection: 1 = DIO1, 0 = DIO0 */
|
||||
#define ADIS16201_MSC_CTRL_DATA_RDY_DIO1 BIT(0)
|
||||
|
||||
/* Diagnostics System Status Register Definition */
|
||||
#define ADIS16201_DIAG_STAT_REG 0x3C
|
||||
#define ADIS16201_DIAG_STAT_ALARM2 BIT(9)
|
||||
#define ADIS16201_DIAG_STAT_ALARM1 BIT(8)
|
||||
#define ADIS16201_DIAG_STAT_SPI_FAIL_BIT 3
|
||||
#define ADIS16201_DIAG_STAT_FLASH_UPT_FAIL_BIT 2
|
||||
/* Power supply above 3.625 V */
|
||||
#define ADIS16201_DIAG_STAT_POWER_HIGH_BIT 1
|
||||
/* Power supply below 3.15 V */
|
||||
#define ADIS16201_DIAG_STAT_POWER_LOW_BIT 0
|
||||
|
||||
/* System Command Register Definition */
|
||||
#define ADIS16201_GLOB_CMD_REG 0x3E
|
||||
#define ADIS16201_GLOB_CMD_SW_RESET BIT(7)
|
||||
#define ADIS16201_GLOB_CMD_FACTORY_RESET BIT(1)
|
||||
|
||||
#define ADIS16201_ERROR_ACTIVE BIT(14)
|
||||
|
||||
enum adis16201_scan {
|
||||
ADIS16201_SCAN_ACC_X,
|
||||
ADIS16201_SCAN_ACC_Y,
|
||||
ADIS16201_SCAN_INCLI_X,
|
||||
ADIS16201_SCAN_INCLI_Y,
|
||||
ADIS16201_SCAN_SUPPLY,
|
||||
ADIS16201_SCAN_AUX_ADC,
|
||||
ADIS16201_SCAN_TEMP,
|
||||
};
|
||||
|
||||
static const u8 adis16201_addresses[] = {
|
||||
[ADIS16201_SCAN_ACC_X] = ADIS16201_XACCL_OFFS_REG,
|
||||
[ADIS16201_SCAN_ACC_Y] = ADIS16201_YACCL_OFFS_REG,
|
||||
[ADIS16201_SCAN_INCLI_X] = ADIS16201_XINCL_OFFS_REG,
|
||||
[ADIS16201_SCAN_INCLI_Y] = ADIS16201_YINCL_OFFS_REG,
|
||||
};
|
||||
|
||||
static int adis16201_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2,
|
||||
long mask)
|
||||
{
|
||||
struct adis *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
int bits;
|
||||
u8 addr;
|
||||
s16 val16;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
return adis_single_conversion(indio_dev, chan,
|
||||
ADIS16201_ERROR_ACTIVE, val);
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
switch (chan->type) {
|
||||
case IIO_VOLTAGE:
|
||||
if (chan->channel == 0) {
|
||||
/* Voltage base units are mV hence 1.22 mV */
|
||||
*val = 1;
|
||||
*val2 = 220000;
|
||||
} else {
|
||||
/* Voltage base units are mV hence 0.61 mV */
|
||||
*val = 0;
|
||||
*val2 = 610000;
|
||||
}
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
case IIO_TEMP:
|
||||
*val = -470;
|
||||
*val2 = 0;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
case IIO_ACCEL:
|
||||
/*
|
||||
* IIO base unit for sensitivity of accelerometer
|
||||
* is milli g.
|
||||
* 1 LSB represents 0.244 mg.
|
||||
*/
|
||||
*val = 0;
|
||||
*val2 = IIO_G_TO_M_S_2(462400);
|
||||
return IIO_VAL_INT_PLUS_NANO;
|
||||
case IIO_INCLI:
|
||||
*val = 0;
|
||||
*val2 = 100000;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
/*
|
||||
* The raw ADC value is 1278 when the temperature
|
||||
* is 25 degrees and the scale factor per milli
|
||||
* degree celcius is -470.
|
||||
*/
|
||||
*val = 25000 / -470 - 1278;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
switch (chan->type) {
|
||||
case IIO_ACCEL:
|
||||
bits = 12;
|
||||
break;
|
||||
case IIO_INCLI:
|
||||
bits = 9;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
addr = adis16201_addresses[chan->scan_index];
|
||||
ret = adis_read_reg_16(st, addr, &val16);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*val = sign_extend32(val16, bits - 1);
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int adis16201_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val,
|
||||
int val2,
|
||||
long mask)
|
||||
{
|
||||
struct adis *st = iio_priv(indio_dev);
|
||||
int m;
|
||||
|
||||
if (mask != IIO_CHAN_INFO_CALIBBIAS)
|
||||
return -EINVAL;
|
||||
|
||||
switch (chan->type) {
|
||||
case IIO_ACCEL:
|
||||
m = GENMASK(11, 0);
|
||||
break;
|
||||
case IIO_INCLI:
|
||||
m = GENMASK(8, 0);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return adis_write_reg_16(st, adis16201_addresses[chan->scan_index],
|
||||
val & m);
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec adis16201_channels[] = {
|
||||
ADIS_SUPPLY_CHAN(ADIS16201_SUPPLY_OUT_REG, ADIS16201_SCAN_SUPPLY, 0,
|
||||
12),
|
||||
ADIS_TEMP_CHAN(ADIS16201_TEMP_OUT_REG, ADIS16201_SCAN_TEMP, 0, 12),
|
||||
ADIS_ACCEL_CHAN(X, ADIS16201_XACCL_OUT_REG, ADIS16201_SCAN_ACC_X,
|
||||
BIT(IIO_CHAN_INFO_CALIBBIAS), 0, 14),
|
||||
ADIS_ACCEL_CHAN(Y, ADIS16201_YACCL_OUT_REG, ADIS16201_SCAN_ACC_Y,
|
||||
BIT(IIO_CHAN_INFO_CALIBBIAS), 0, 14),
|
||||
ADIS_AUX_ADC_CHAN(ADIS16201_AUX_ADC_REG, ADIS16201_SCAN_AUX_ADC, 0, 12),
|
||||
ADIS_INCLI_CHAN(X, ADIS16201_XINCL_OUT_REG, ADIS16201_SCAN_INCLI_X,
|
||||
BIT(IIO_CHAN_INFO_CALIBBIAS), 0, 14),
|
||||
ADIS_INCLI_CHAN(X, ADIS16201_YINCL_OUT_REG, ADIS16201_SCAN_INCLI_Y,
|
||||
BIT(IIO_CHAN_INFO_CALIBBIAS), 0, 14),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(7)
|
||||
};
|
||||
|
||||
static const struct iio_info adis16201_info = {
|
||||
.read_raw = adis16201_read_raw,
|
||||
.write_raw = adis16201_write_raw,
|
||||
.update_scan_mode = adis_update_scan_mode,
|
||||
};
|
||||
|
||||
static const char * const adis16201_status_error_msgs[] = {
|
||||
[ADIS16201_DIAG_STAT_SPI_FAIL_BIT] = "SPI failure",
|
||||
[ADIS16201_DIAG_STAT_FLASH_UPT_FAIL_BIT] = "Flash update failed",
|
||||
[ADIS16201_DIAG_STAT_POWER_HIGH_BIT] = "Power supply above 3.625V",
|
||||
[ADIS16201_DIAG_STAT_POWER_LOW_BIT] = "Power supply below 3.15V",
|
||||
};
|
||||
|
||||
static const struct adis_data adis16201_data = {
|
||||
.read_delay = 20,
|
||||
.msc_ctrl_reg = ADIS16201_MSC_CTRL_REG,
|
||||
.glob_cmd_reg = ADIS16201_GLOB_CMD_REG,
|
||||
.diag_stat_reg = ADIS16201_DIAG_STAT_REG,
|
||||
|
||||
.self_test_mask = ADIS16201_MSC_CTRL_SELF_TEST_EN,
|
||||
.self_test_no_autoclear = true,
|
||||
.startup_delay = ADIS16201_STARTUP_DELAY_MS,
|
||||
|
||||
.status_error_msgs = adis16201_status_error_msgs,
|
||||
.status_error_mask = BIT(ADIS16201_DIAG_STAT_SPI_FAIL_BIT) |
|
||||
BIT(ADIS16201_DIAG_STAT_FLASH_UPT_FAIL_BIT) |
|
||||
BIT(ADIS16201_DIAG_STAT_POWER_HIGH_BIT) |
|
||||
BIT(ADIS16201_DIAG_STAT_POWER_LOW_BIT),
|
||||
};
|
||||
|
||||
static int adis16201_probe(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct adis *st;
|
||||
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);
|
||||
|
||||
indio_dev->name = spi->dev.driver->name;
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->info = &adis16201_info;
|
||||
|
||||
indio_dev->channels = adis16201_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(adis16201_channels);
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
ret = adis_init(st, indio_dev, spi, &adis16201_data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = adis_setup_buffer_and_trigger(st, indio_dev, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = adis_initial_startup(st);
|
||||
if (ret)
|
||||
goto error_cleanup_buffer_trigger;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret < 0)
|
||||
goto error_cleanup_buffer_trigger;
|
||||
|
||||
return 0;
|
||||
|
||||
error_cleanup_buffer_trigger:
|
||||
adis_cleanup_buffer_and_trigger(st, indio_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adis16201_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct adis *st = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
adis_cleanup_buffer_and_trigger(st, indio_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct spi_driver adis16201_driver = {
|
||||
.driver = {
|
||||
.name = "adis16201",
|
||||
},
|
||||
.probe = adis16201_probe,
|
||||
.remove = adis16201_remove,
|
||||
};
|
||||
module_spi_driver(adis16201_driver);
|
||||
|
||||
MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
|
||||
MODULE_DESCRIPTION("Analog Devices ADIS16201 Dual-Axis Digital Inclinometer and Accelerometer");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("spi:adis16201");
|
@ -6,7 +6,6 @@
|
||||
* Licensed under the GPL-2 or later.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/list.h>
|
||||
@ -16,8 +15,6 @@
|
||||
#include <linux/sysfs.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/imu/adis.h>
|
||||
|
||||
#define ADIS16209_STARTUP_DELAY_MS 220
|
||||
@ -71,13 +68,13 @@
|
||||
#define ADIS16209_STAT_REG 0x3C
|
||||
#define ADIS16209_STAT_ALARM2 BIT(9)
|
||||
#define ADIS16209_STAT_ALARM1 BIT(8)
|
||||
#define ADIS16209_STAT_SELFTEST_FAIL_BIT 5
|
||||
#define ADIS16209_STAT_SPI_FAIL_BIT 3
|
||||
#define ADIS16209_STAT_FLASH_UPT_FAIL_BIT 2
|
||||
#define ADIS16209_STAT_SELFTEST_FAIL_BIT 5
|
||||
#define ADIS16209_STAT_SPI_FAIL_BIT 3
|
||||
#define ADIS16209_STAT_FLASH_UPT_FAIL_BIT 2
|
||||
/* Power supply above 3.625 V */
|
||||
#define ADIS16209_STAT_POWER_HIGH_BIT 1
|
||||
#define ADIS16209_STAT_POWER_HIGH_BIT 1
|
||||
/* Power supply below 3.15 V */
|
||||
#define ADIS16209_STAT_POWER_LOW_BIT 0
|
||||
#define ADIS16209_STAT_POWER_LOW_BIT 0
|
||||
|
||||
#define ADIS16209_CMD_REG 0x3E
|
||||
#define ADIS16209_CMD_SW_RESET BIT(7)
|
||||
@ -115,25 +112,22 @@ static int adis16209_write_raw(struct iio_dev *indio_dev,
|
||||
long mask)
|
||||
{
|
||||
struct adis *st = iio_priv(indio_dev);
|
||||
int bits;
|
||||
s16 val16;
|
||||
u8 addr;
|
||||
int m;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
switch (chan->type) {
|
||||
case IIO_ACCEL:
|
||||
case IIO_INCLI:
|
||||
bits = 14;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
val16 = val & ((1 << bits) - 1);
|
||||
addr = adis16209_addresses[chan->scan_index][0];
|
||||
return adis_write_reg_16(st, addr, val16);
|
||||
if (mask != IIO_CHAN_INFO_CALIBBIAS)
|
||||
return -EINVAL;
|
||||
|
||||
switch (chan->type) {
|
||||
case IIO_ACCEL:
|
||||
case IIO_INCLI:
|
||||
m = GENMASK(13, 0);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
return -EINVAL;
|
||||
|
||||
return adis_write_reg_16(st, adis16209_addresses[chan->scan_index][0],
|
||||
val & m);
|
||||
}
|
||||
|
||||
static int adis16209_read_raw(struct iio_dev *indio_dev,
|
||||
@ -195,7 +189,7 @@ static int adis16209_read_raw(struct iio_dev *indio_dev,
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
/*
|
||||
* The raw ADC value is 0x4FE when the temperature
|
||||
* is 25 degrees and the scale factor per milli
|
||||
* is 45 degrees and the scale factor per milli
|
||||
* degree celcius is -470.
|
||||
*/
|
||||
*val = 25000 / -470 - 0x4FE;
|
||||
@ -270,13 +264,14 @@ static const struct adis_data adis16209_data = {
|
||||
|
||||
static int adis16209_probe(struct spi_device *spi)
|
||||
{
|
||||
int ret;
|
||||
struct adis *st;
|
||||
struct iio_dev *indio_dev;
|
||||
struct adis *st;
|
||||
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);
|
||||
|
||||
@ -290,6 +285,7 @@ static int adis16209_probe(struct spi_device *spi)
|
||||
ret = adis_init(st, indio_dev, spi, &adis16209_data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = adis_setup_buffer_and_trigger(st, indio_dev, NULL);
|
||||
if (ret)
|
||||
return ret;
|
@ -837,29 +837,12 @@ static int bmc150_accel_fifo_transfer(struct bmc150_accel_data *data,
|
||||
int sample_length = 3 * 2;
|
||||
int ret;
|
||||
int total_length = samples * sample_length;
|
||||
int i;
|
||||
size_t step = regmap_get_raw_read_max(data->regmap);
|
||||
|
||||
if (!step || step > total_length)
|
||||
step = total_length;
|
||||
else if (step < total_length)
|
||||
step = sample_length;
|
||||
|
||||
/*
|
||||
* Seems we have a bus with size limitation so we have to execute
|
||||
* multiple reads
|
||||
*/
|
||||
for (i = 0; i < total_length; i += step) {
|
||||
ret = regmap_raw_read(data->regmap, BMC150_ACCEL_REG_FIFO_DATA,
|
||||
&buffer[i], step);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
|
||||
ret = regmap_raw_read(data->regmap, BMC150_ACCEL_REG_FIFO_DATA,
|
||||
buffer, total_length);
|
||||
if (ret)
|
||||
dev_err(dev,
|
||||
"Error transferring data from fifo in single steps of %zu\n",
|
||||
step);
|
||||
"Error transferring data from fifo: %d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -106,6 +106,7 @@ struct mma8452_data {
|
||||
u8 ctrl_reg1;
|
||||
u8 data_cfg;
|
||||
const struct mma_chip_info *chip_info;
|
||||
int sleep_val;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -193,7 +194,11 @@ static int mma8452_drdy(struct mma8452_data *data)
|
||||
if ((ret & MMA8452_STATUS_DRDY) == MMA8452_STATUS_DRDY)
|
||||
return 0;
|
||||
|
||||
msleep(20);
|
||||
if (data->sleep_val <= 20)
|
||||
usleep_range(data->sleep_val * 250,
|
||||
data->sleep_val * 500);
|
||||
else
|
||||
msleep(20);
|
||||
}
|
||||
|
||||
dev_err(&data->client->dev, "data not ready\n");
|
||||
@ -544,6 +549,18 @@ static int mma8452_read_raw(struct iio_dev *indio_dev,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int mma8452_calculate_sleep(struct mma8452_data *data)
|
||||
{
|
||||
int ret, i = mma8452_get_odr_index(data);
|
||||
|
||||
if (mma8452_samp_freq[i][0] > 0)
|
||||
ret = 1000 / mma8452_samp_freq[i][0];
|
||||
else
|
||||
ret = 1000;
|
||||
|
||||
return ret == 0 ? 1 : ret;
|
||||
}
|
||||
|
||||
static int mma8452_standby(struct mma8452_data *data)
|
||||
{
|
||||
return i2c_smbus_write_byte_data(data->client, MMA8452_CTRL_REG1,
|
||||
@ -700,6 +717,8 @@ static int mma8452_write_raw(struct iio_dev *indio_dev,
|
||||
data->ctrl_reg1 &= ~MMA8452_CTRL_DR_MASK;
|
||||
data->ctrl_reg1 |= i << MMA8452_CTRL_DR_SHIFT;
|
||||
|
||||
data->sleep_val = mma8452_calculate_sleep(data);
|
||||
|
||||
ret = mma8452_change_config(data, MMA8452_CTRL_REG1,
|
||||
data->ctrl_reg1);
|
||||
break;
|
||||
@ -1593,6 +1612,9 @@ static int mma8452_probe(struct i2c_client *client,
|
||||
|
||||
data->ctrl_reg1 = MMA8452_CTRL_ACTIVE |
|
||||
(MMA8452_CTRL_DR_DEFAULT << MMA8452_CTRL_DR_SHIFT);
|
||||
|
||||
data->sleep_val = mma8452_calculate_sleep(data);
|
||||
|
||||
ret = i2c_smbus_write_byte_data(client, MMA8452_CTRL_REG1,
|
||||
data->ctrl_reg1);
|
||||
if (ret < 0)
|
||||
|
@ -1277,7 +1277,7 @@ static int sca3000_configure_ring(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct iio_buffer *buffer;
|
||||
|
||||
buffer = iio_kfifo_allocate();
|
||||
buffer = devm_iio_kfifo_allocate(&indio_dev->dev);
|
||||
if (!buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -1287,11 +1287,6 @@ static int sca3000_configure_ring(struct iio_dev *indio_dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sca3000_unconfigure_ring(struct iio_dev *indio_dev)
|
||||
{
|
||||
iio_kfifo_free(indio_dev->buffer);
|
||||
}
|
||||
|
||||
static inline
|
||||
int __sca3000_hw_ring_state_set(struct iio_dev *indio_dev, bool state)
|
||||
{
|
||||
@ -1486,7 +1481,9 @@ static int sca3000_probe(struct spi_device *spi)
|
||||
}
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
sca3000_configure_ring(indio_dev);
|
||||
ret = sca3000_configure_ring(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (spi->irq) {
|
||||
ret = request_threaded_irq(spi->irq,
|
||||
@ -1546,8 +1543,6 @@ static int sca3000_remove(struct spi_device *spi)
|
||||
if (spi->irq)
|
||||
free_irq(spi->irq, indio_dev);
|
||||
|
||||
sca3000_unconfigure_ring(indio_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -107,6 +107,7 @@ 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},
|
||||
{ },
|
||||
};
|
||||
|
@ -153,6 +153,17 @@ struct ad7791_state {
|
||||
const struct ad7791_chip_info *info;
|
||||
};
|
||||
|
||||
static const int ad7791_sample_freq_avail[8][2] = {
|
||||
[AD7791_FILTER_RATE_120] = { 120, 0 },
|
||||
[AD7791_FILTER_RATE_100] = { 100, 0 },
|
||||
[AD7791_FILTER_RATE_33_3] = { 33, 300000 },
|
||||
[AD7791_FILTER_RATE_20] = { 20, 0 },
|
||||
[AD7791_FILTER_RATE_16_6] = { 16, 600000 },
|
||||
[AD7791_FILTER_RATE_16_7] = { 16, 700000 },
|
||||
[AD7791_FILTER_RATE_13_3] = { 13, 300000 },
|
||||
[AD7791_FILTER_RATE_9_5] = { 9, 500000 },
|
||||
};
|
||||
|
||||
static struct ad7791_state *ad_sigma_delta_to_ad7791(struct ad_sigma_delta *sd)
|
||||
{
|
||||
return container_of(sd, struct ad7791_state, sd);
|
||||
@ -202,6 +213,7 @@ static int ad7791_read_raw(struct iio_dev *indio_dev,
|
||||
{
|
||||
struct ad7791_state *st = iio_priv(indio_dev);
|
||||
bool unipolar = !!(st->mode & AD7791_MODE_UNIPOLAR);
|
||||
unsigned int rate;
|
||||
|
||||
switch (info) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
@ -239,63 +251,56 @@ static int ad7791_read_raw(struct iio_dev *indio_dev,
|
||||
*val2 = chan->scan_type.realbits - 1;
|
||||
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
rate = st->filter & AD7791_FILTER_RATE_MASK;
|
||||
*val = ad7791_sample_freq_avail[rate][0];
|
||||
*val2 = ad7791_sample_freq_avail[rate][1];
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const char * const ad7791_sample_freq_avail[] = {
|
||||
[AD7791_FILTER_RATE_120] = "120",
|
||||
[AD7791_FILTER_RATE_100] = "100",
|
||||
[AD7791_FILTER_RATE_33_3] = "33.3",
|
||||
[AD7791_FILTER_RATE_20] = "20",
|
||||
[AD7791_FILTER_RATE_16_6] = "16.6",
|
||||
[AD7791_FILTER_RATE_16_7] = "16.7",
|
||||
[AD7791_FILTER_RATE_13_3] = "13.3",
|
||||
[AD7791_FILTER_RATE_9_5] = "9.5",
|
||||
};
|
||||
|
||||
static ssize_t ad7791_read_frequency(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
static int ad7791_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int val, int val2, long mask)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct ad7791_state *st = iio_priv(indio_dev);
|
||||
unsigned int rate = st->filter & AD7791_FILTER_RATE_MASK;
|
||||
|
||||
return sprintf(buf, "%s\n", ad7791_sample_freq_avail[rate]);
|
||||
}
|
||||
|
||||
static ssize_t ad7791_write_frequency(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t len)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct ad7791_state *st = iio_priv(indio_dev);
|
||||
int i, ret;
|
||||
|
||||
i = sysfs_match_string(ad7791_sample_freq_avail, buf);
|
||||
if (i < 0)
|
||||
return i;
|
||||
int ret, i;
|
||||
|
||||
ret = iio_device_claim_direct_mode(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
st->filter &= ~AD7791_FILTER_RATE_MASK;
|
||||
st->filter |= i;
|
||||
ad_sd_write_reg(&st->sd, AD7791_REG_FILTER, sizeof(st->filter),
|
||||
st->filter);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
for (i = 0; i < ARRAY_SIZE(ad7791_sample_freq_avail); i++) {
|
||||
if (ad7791_sample_freq_avail[i][0] == val &&
|
||||
ad7791_sample_freq_avail[i][1] == val2)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == ARRAY_SIZE(ad7791_sample_freq_avail)) {
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
st->filter &= ~AD7791_FILTER_RATE_MASK;
|
||||
st->filter |= i;
|
||||
ad_sd_write_reg(&st->sd, AD7791_REG_FILTER,
|
||||
sizeof(st->filter),
|
||||
st->filter);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
|
||||
return len;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
|
||||
ad7791_read_frequency,
|
||||
ad7791_write_frequency);
|
||||
|
||||
static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("120 100 33.3 20 16.7 16.6 13.3 9.5");
|
||||
|
||||
static struct attribute *ad7791_attributes[] = {
|
||||
&iio_dev_attr_sampling_frequency.dev_attr.attr,
|
||||
&iio_const_attr_sampling_frequency_available.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
@ -306,12 +311,14 @@ static const struct attribute_group ad7791_attribute_group = {
|
||||
|
||||
static const struct iio_info ad7791_info = {
|
||||
.read_raw = &ad7791_read_raw,
|
||||
.write_raw = &ad7791_write_raw,
|
||||
.attrs = &ad7791_attribute_group,
|
||||
.validate_trigger = ad_sd_validate_trigger,
|
||||
};
|
||||
|
||||
static const struct iio_info ad7791_no_filter_info = {
|
||||
.read_raw = &ad7791_read_raw,
|
||||
.write_raw = &ad7791_write_raw,
|
||||
.validate_trigger = ad_sd_validate_trigger,
|
||||
};
|
||||
|
||||
|
@ -604,5 +604,5 @@ static struct platform_driver imx7d_adc_driver = {
|
||||
module_platform_driver(imx7d_adc_driver);
|
||||
|
||||
MODULE_AUTHOR("Haibo Chen <haibo.chen@freescale.com>");
|
||||
MODULE_DESCRIPTION("Freeacale IMX7D ADC driver");
|
||||
MODULE_DESCRIPTION("Freescale IMX7D ADC driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
@ -219,15 +219,19 @@ enum meson_sar_adc_chan7_mux_sel {
|
||||
CHAN7_MUX_CH7_INPUT = 0x7,
|
||||
};
|
||||
|
||||
struct meson_sar_adc_data {
|
||||
struct meson_sar_adc_param {
|
||||
bool has_bl30_integration;
|
||||
unsigned long clock_rate;
|
||||
u32 bandgap_reg;
|
||||
unsigned int resolution;
|
||||
const char *name;
|
||||
const struct regmap_config *regmap_config;
|
||||
};
|
||||
|
||||
struct meson_sar_adc_data {
|
||||
const struct meson_sar_adc_param *param;
|
||||
const char *name;
|
||||
};
|
||||
|
||||
struct meson_sar_adc_priv {
|
||||
struct regmap *regmap;
|
||||
struct regulator *vref;
|
||||
@ -276,7 +280,7 @@ static int meson_sar_adc_calib_val(struct iio_dev *indio_dev, int val)
|
||||
/* use val_calib = scale * val_raw + offset calibration function */
|
||||
tmp = div_s64((s64)val * priv->calibscale, MILLION) + priv->calibbias;
|
||||
|
||||
return clamp(tmp, 0, (1 << priv->data->resolution) - 1);
|
||||
return clamp(tmp, 0, (1 << priv->data->param->resolution) - 1);
|
||||
}
|
||||
|
||||
static int meson_sar_adc_wait_busy_clear(struct iio_dev *indio_dev)
|
||||
@ -328,7 +332,7 @@ static int meson_sar_adc_read_raw_sample(struct iio_dev *indio_dev,
|
||||
}
|
||||
|
||||
fifo_val = FIELD_GET(MESON_SAR_ADC_FIFO_RD_SAMPLE_VALUE_MASK, regval);
|
||||
fifo_val &= GENMASK(priv->data->resolution - 1, 0);
|
||||
fifo_val &= GENMASK(priv->data->param->resolution - 1, 0);
|
||||
*val = meson_sar_adc_calib_val(indio_dev, fifo_val);
|
||||
|
||||
return 0;
|
||||
@ -447,7 +451,7 @@ static int meson_sar_adc_lock(struct iio_dev *indio_dev)
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
|
||||
if (priv->data->has_bl30_integration) {
|
||||
if (priv->data->param->has_bl30_integration) {
|
||||
/* prevent BL30 from using the SAR ADC while we are using it */
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELAY,
|
||||
MESON_SAR_ADC_DELAY_KERNEL_BUSY,
|
||||
@ -475,7 +479,7 @@ static void meson_sar_adc_unlock(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
|
||||
|
||||
if (priv->data->has_bl30_integration)
|
||||
if (priv->data->param->has_bl30_integration)
|
||||
/* allow BL30 to use the SAR ADC again */
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELAY,
|
||||
MESON_SAR_ADC_DELAY_KERNEL_BUSY, 0);
|
||||
@ -559,7 +563,7 @@ static int meson_sar_adc_iio_info_read_raw(struct iio_dev *indio_dev,
|
||||
}
|
||||
|
||||
*val = ret / 1000;
|
||||
*val2 = priv->data->resolution;
|
||||
*val2 = priv->data->param->resolution;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
@ -632,7 +636,7 @@ static int meson_sar_adc_init(struct iio_dev *indio_dev)
|
||||
*/
|
||||
meson_sar_adc_set_chan7_mux(indio_dev, CHAN7_MUX_CH7_INPUT);
|
||||
|
||||
if (priv->data->has_bl30_integration) {
|
||||
if (priv->data->param->has_bl30_integration) {
|
||||
/*
|
||||
* leave sampling delay and the input clocks as configured by
|
||||
* BL30 to make sure BL30 gets the values it expects when
|
||||
@ -712,7 +716,7 @@ static int meson_sar_adc_init(struct iio_dev *indio_dev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = clk_set_rate(priv->adc_clk, priv->data->clock_rate);
|
||||
ret = clk_set_rate(priv->adc_clk, priv->data->param->clock_rate);
|
||||
if (ret) {
|
||||
dev_err(indio_dev->dev.parent,
|
||||
"failed to set adc clock rate\n");
|
||||
@ -725,14 +729,15 @@ static int meson_sar_adc_init(struct iio_dev *indio_dev)
|
||||
static void meson_sar_adc_set_bandgap(struct iio_dev *indio_dev, bool on_off)
|
||||
{
|
||||
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
|
||||
const struct meson_sar_adc_param *param = priv->data->param;
|
||||
u32 enable_mask;
|
||||
|
||||
if (priv->data->bandgap_reg == MESON_SAR_ADC_REG11)
|
||||
if (param->bandgap_reg == MESON_SAR_ADC_REG11)
|
||||
enable_mask = MESON_SAR_ADC_REG11_BANDGAP_EN;
|
||||
else
|
||||
enable_mask = MESON_SAR_ADC_DELTA_10_TS_VBG_EN;
|
||||
|
||||
regmap_update_bits(priv->regmap, priv->data->bandgap_reg, enable_mask,
|
||||
regmap_update_bits(priv->regmap, param->bandgap_reg, enable_mask,
|
||||
on_off ? enable_mask : 0);
|
||||
}
|
||||
|
||||
@ -844,8 +849,8 @@ static int meson_sar_adc_calib(struct iio_dev *indio_dev)
|
||||
int ret, nominal0, nominal1, value0, value1;
|
||||
|
||||
/* use points 25% and 75% for calibration */
|
||||
nominal0 = (1 << priv->data->resolution) / 4;
|
||||
nominal1 = (1 << priv->data->resolution) * 3 / 4;
|
||||
nominal0 = (1 << priv->data->param->resolution) / 4;
|
||||
nominal1 = (1 << priv->data->param->resolution) * 3 / 4;
|
||||
|
||||
meson_sar_adc_set_chan7_mux(indio_dev, CHAN7_MUX_VDD_DIV4);
|
||||
usleep_range(10, 20);
|
||||
@ -883,51 +888,60 @@ static const struct iio_info meson_sar_adc_iio_info = {
|
||||
.read_raw = meson_sar_adc_iio_info_read_raw,
|
||||
};
|
||||
|
||||
static const struct meson_sar_adc_data meson_sar_adc_meson8_data = {
|
||||
static const struct meson_sar_adc_param meson_sar_adc_meson8_param = {
|
||||
.has_bl30_integration = false,
|
||||
.clock_rate = 1150000,
|
||||
.bandgap_reg = MESON_SAR_ADC_DELTA_10,
|
||||
.regmap_config = &meson_sar_adc_regmap_config_meson8,
|
||||
.resolution = 10,
|
||||
};
|
||||
|
||||
static const struct meson_sar_adc_param meson_sar_adc_gxbb_param = {
|
||||
.has_bl30_integration = true,
|
||||
.clock_rate = 1200000,
|
||||
.bandgap_reg = MESON_SAR_ADC_REG11,
|
||||
.regmap_config = &meson_sar_adc_regmap_config_gxbb,
|
||||
.resolution = 10,
|
||||
};
|
||||
|
||||
static const struct meson_sar_adc_param meson_sar_adc_gxl_param = {
|
||||
.has_bl30_integration = true,
|
||||
.clock_rate = 1200000,
|
||||
.bandgap_reg = MESON_SAR_ADC_REG11,
|
||||
.regmap_config = &meson_sar_adc_regmap_config_gxbb,
|
||||
.resolution = 12,
|
||||
};
|
||||
|
||||
static const struct meson_sar_adc_data meson_sar_adc_meson8_data = {
|
||||
.param = &meson_sar_adc_meson8_param,
|
||||
.name = "meson-meson8-saradc",
|
||||
};
|
||||
|
||||
static const struct meson_sar_adc_data meson_sar_adc_meson8b_data = {
|
||||
.has_bl30_integration = false,
|
||||
.clock_rate = 1150000,
|
||||
.bandgap_reg = MESON_SAR_ADC_DELTA_10,
|
||||
.regmap_config = &meson_sar_adc_regmap_config_meson8,
|
||||
.resolution = 10,
|
||||
.param = &meson_sar_adc_meson8_param,
|
||||
.name = "meson-meson8b-saradc",
|
||||
};
|
||||
|
||||
static const struct meson_sar_adc_data meson_sar_adc_gxbb_data = {
|
||||
.has_bl30_integration = true,
|
||||
.clock_rate = 1200000,
|
||||
.bandgap_reg = MESON_SAR_ADC_REG11,
|
||||
.regmap_config = &meson_sar_adc_regmap_config_gxbb,
|
||||
.resolution = 10,
|
||||
.param = &meson_sar_adc_gxbb_param,
|
||||
.name = "meson-gxbb-saradc",
|
||||
};
|
||||
|
||||
static const struct meson_sar_adc_data meson_sar_adc_gxl_data = {
|
||||
.has_bl30_integration = true,
|
||||
.clock_rate = 1200000,
|
||||
.bandgap_reg = MESON_SAR_ADC_REG11,
|
||||
.regmap_config = &meson_sar_adc_regmap_config_gxbb,
|
||||
.resolution = 12,
|
||||
.param = &meson_sar_adc_gxl_param,
|
||||
.name = "meson-gxl-saradc",
|
||||
};
|
||||
|
||||
static const struct meson_sar_adc_data meson_sar_adc_gxm_data = {
|
||||
.has_bl30_integration = true,
|
||||
.clock_rate = 1200000,
|
||||
.bandgap_reg = MESON_SAR_ADC_REG11,
|
||||
.regmap_config = &meson_sar_adc_regmap_config_gxbb,
|
||||
.resolution = 12,
|
||||
.param = &meson_sar_adc_gxl_param,
|
||||
.name = "meson-gxm-saradc",
|
||||
};
|
||||
|
||||
static const struct meson_sar_adc_data meson_sar_adc_axg_data = {
|
||||
.param = &meson_sar_adc_gxl_param,
|
||||
.name = "meson-axg-saradc",
|
||||
};
|
||||
|
||||
static const struct of_device_id meson_sar_adc_of_match[] = {
|
||||
{
|
||||
.compatible = "amlogic,meson8-saradc",
|
||||
@ -946,6 +960,9 @@ static const struct of_device_id meson_sar_adc_of_match[] = {
|
||||
}, {
|
||||
.compatible = "amlogic,meson-gxm-saradc",
|
||||
.data = &meson_sar_adc_gxm_data,
|
||||
}, {
|
||||
.compatible = "amlogic,meson-axg-saradc",
|
||||
.data = &meson_sar_adc_axg_data,
|
||||
},
|
||||
{},
|
||||
};
|
||||
@ -1001,7 +1018,7 @@ static int meson_sar_adc_probe(struct platform_device *pdev)
|
||||
return ret;
|
||||
|
||||
priv->regmap = devm_regmap_init_mmio(&pdev->dev, base,
|
||||
priv->data->regmap_config);
|
||||
priv->data->param->regmap_config);
|
||||
if (IS_ERR(priv->regmap))
|
||||
return PTR_ERR(priv->regmap);
|
||||
|
||||
|
@ -34,9 +34,6 @@
|
||||
#define STM32F4_ADC_ADCPRE_SHIFT 16
|
||||
#define STM32F4_ADC_ADCPRE_MASK GENMASK(17, 16)
|
||||
|
||||
/* STM32 F4 maximum analog clock rate (from datasheet) */
|
||||
#define STM32F4_ADC_MAX_CLK_RATE 36000000
|
||||
|
||||
/* STM32H7 - common registers for all ADC instances */
|
||||
#define STM32H7_ADC_CSR (STM32_ADCX_COMN_OFFSET + 0x00)
|
||||
#define STM32H7_ADC_CCR (STM32_ADCX_COMN_OFFSET + 0x08)
|
||||
@ -51,9 +48,6 @@
|
||||
#define STM32H7_CKMODE_SHIFT 16
|
||||
#define STM32H7_CKMODE_MASK GENMASK(17, 16)
|
||||
|
||||
/* STM32 H7 maximum analog clock rate (from datasheet) */
|
||||
#define STM32H7_ADC_MAX_CLK_RATE 36000000
|
||||
|
||||
/**
|
||||
* stm32_adc_common_regs - stm32 common registers, compatible dependent data
|
||||
* @csr: common status register offset
|
||||
@ -74,15 +68,17 @@ struct stm32_adc_priv;
|
||||
* stm32_adc_priv_cfg - stm32 core compatible configuration data
|
||||
* @regs: common registers for all instances
|
||||
* @clk_sel: clock selection routine
|
||||
* @max_clk_rate_hz: maximum analog clock rate (Hz, from datasheet)
|
||||
*/
|
||||
struct stm32_adc_priv_cfg {
|
||||
const struct stm32_adc_common_regs *regs;
|
||||
int (*clk_sel)(struct platform_device *, struct stm32_adc_priv *);
|
||||
u32 max_clk_rate_hz;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct stm32_adc_priv - stm32 ADC core private data
|
||||
* @irq: irq for ADC block
|
||||
* @irq: irq(s) for ADC block
|
||||
* @domain: irq domain reference
|
||||
* @aclk: clock reference for the analog circuitry
|
||||
* @bclk: bus clock common for all ADCs, depends on part used
|
||||
@ -91,7 +87,7 @@ struct stm32_adc_priv_cfg {
|
||||
* @common: common data for all ADC instances
|
||||
*/
|
||||
struct stm32_adc_priv {
|
||||
int irq;
|
||||
int irq[STM32_ADC_MAX_ADCS];
|
||||
struct irq_domain *domain;
|
||||
struct clk *aclk;
|
||||
struct clk *bclk;
|
||||
@ -133,7 +129,7 @@ static int stm32f4_adc_clk_sel(struct platform_device *pdev,
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(stm32f4_pclk_div); i++) {
|
||||
if ((rate / stm32f4_pclk_div[i]) <= STM32F4_ADC_MAX_CLK_RATE)
|
||||
if ((rate / stm32f4_pclk_div[i]) <= priv->cfg->max_clk_rate_hz)
|
||||
break;
|
||||
}
|
||||
if (i >= ARRAY_SIZE(stm32f4_pclk_div)) {
|
||||
@ -222,7 +218,7 @@ static int stm32h7_adc_clk_sel(struct platform_device *pdev,
|
||||
if (ckmode)
|
||||
continue;
|
||||
|
||||
if ((rate / div) <= STM32H7_ADC_MAX_CLK_RATE)
|
||||
if ((rate / div) <= priv->cfg->max_clk_rate_hz)
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
@ -242,7 +238,7 @@ static int stm32h7_adc_clk_sel(struct platform_device *pdev,
|
||||
if (!ckmode)
|
||||
continue;
|
||||
|
||||
if ((rate / div) <= STM32H7_ADC_MAX_CLK_RATE)
|
||||
if ((rate / div) <= priv->cfg->max_clk_rate_hz)
|
||||
goto out;
|
||||
}
|
||||
|
||||
@ -328,11 +324,24 @@ static int stm32_adc_irq_probe(struct platform_device *pdev,
|
||||
struct stm32_adc_priv *priv)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
unsigned int i;
|
||||
|
||||
priv->irq = platform_get_irq(pdev, 0);
|
||||
if (priv->irq < 0) {
|
||||
dev_err(&pdev->dev, "failed to get irq\n");
|
||||
return priv->irq;
|
||||
for (i = 0; i < STM32_ADC_MAX_ADCS; i++) {
|
||||
priv->irq[i] = platform_get_irq(pdev, i);
|
||||
if (priv->irq[i] < 0) {
|
||||
/*
|
||||
* At least one interrupt must be provided, make others
|
||||
* optional:
|
||||
* - stm32f4/h7 shares a common interrupt.
|
||||
* - stm32mp1, has one line per ADC (either for ADC1,
|
||||
* ADC2 or both).
|
||||
*/
|
||||
if (i && priv->irq[i] == -ENXIO)
|
||||
continue;
|
||||
dev_err(&pdev->dev, "failed to get irq\n");
|
||||
|
||||
return priv->irq[i];
|
||||
}
|
||||
}
|
||||
|
||||
priv->domain = irq_domain_add_simple(np, STM32_ADC_MAX_ADCS, 0,
|
||||
@ -343,8 +352,12 @@ static int stm32_adc_irq_probe(struct platform_device *pdev,
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
irq_set_chained_handler(priv->irq, stm32_adc_irq_handler);
|
||||
irq_set_handler_data(priv->irq, priv);
|
||||
for (i = 0; i < STM32_ADC_MAX_ADCS; i++) {
|
||||
if (priv->irq[i] < 0)
|
||||
continue;
|
||||
irq_set_chained_handler(priv->irq[i], stm32_adc_irq_handler);
|
||||
irq_set_handler_data(priv->irq[i], priv);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -353,11 +366,17 @@ static void stm32_adc_irq_remove(struct platform_device *pdev,
|
||||
struct stm32_adc_priv *priv)
|
||||
{
|
||||
int hwirq;
|
||||
unsigned int i;
|
||||
|
||||
for (hwirq = 0; hwirq < STM32_ADC_MAX_ADCS; hwirq++)
|
||||
irq_dispose_mapping(irq_find_mapping(priv->domain, hwirq));
|
||||
irq_domain_remove(priv->domain);
|
||||
irq_set_chained_handler(priv->irq, NULL);
|
||||
|
||||
for (i = 0; i < STM32_ADC_MAX_ADCS; i++) {
|
||||
if (priv->irq[i] < 0)
|
||||
continue;
|
||||
irq_set_chained_handler(priv->irq[i], NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static int stm32_adc_probe(struct platform_device *pdev)
|
||||
@ -497,11 +516,19 @@ static int stm32_adc_remove(struct platform_device *pdev)
|
||||
static const struct stm32_adc_priv_cfg stm32f4_adc_priv_cfg = {
|
||||
.regs = &stm32f4_adc_common_regs,
|
||||
.clk_sel = stm32f4_adc_clk_sel,
|
||||
.max_clk_rate_hz = 36000000,
|
||||
};
|
||||
|
||||
static const struct stm32_adc_priv_cfg stm32h7_adc_priv_cfg = {
|
||||
.regs = &stm32h7_adc_common_regs,
|
||||
.clk_sel = stm32h7_adc_clk_sel,
|
||||
.max_clk_rate_hz = 36000000,
|
||||
};
|
||||
|
||||
static const struct stm32_adc_priv_cfg stm32mp1_adc_priv_cfg = {
|
||||
.regs = &stm32h7_adc_common_regs,
|
||||
.clk_sel = stm32h7_adc_clk_sel,
|
||||
.max_clk_rate_hz = 40000000,
|
||||
};
|
||||
|
||||
static const struct of_device_id stm32_adc_of_match[] = {
|
||||
@ -511,6 +538,9 @@ static const struct of_device_id stm32_adc_of_match[] = {
|
||||
}, {
|
||||
.compatible = "st,stm32h7-adc-core",
|
||||
.data = (void *)&stm32h7_adc_priv_cfg
|
||||
}, {
|
||||
.compatible = "st,stm32mp1-adc-core",
|
||||
.data = (void *)&stm32mp1_adc_priv_cfg
|
||||
}, {
|
||||
},
|
||||
};
|
||||
|
@ -84,6 +84,7 @@
|
||||
#define STM32H7_ADC_CALFACT2 0xC8
|
||||
|
||||
/* STM32H7_ADC_ISR - bit fields */
|
||||
#define STM32MP1_VREGREADY BIT(12)
|
||||
#define STM32H7_EOC BIT(2)
|
||||
#define STM32H7_ADRDY BIT(0)
|
||||
|
||||
@ -249,6 +250,7 @@ struct stm32_adc;
|
||||
* @adc_info: per instance input channels definitions
|
||||
* @trigs: external trigger sources
|
||||
* @clk_required: clock is required
|
||||
* @has_vregready: vregready status flag presence
|
||||
* @selfcalib: optional routine for self-calibration
|
||||
* @prepare: optional prepare routine (power-up, enable)
|
||||
* @start_conv: routine to start conversions
|
||||
@ -261,6 +263,7 @@ struct stm32_adc_cfg {
|
||||
const struct stm32_adc_info *adc_info;
|
||||
struct stm32_adc_trig_info *trigs;
|
||||
bool clk_required;
|
||||
bool has_vregready;
|
||||
int (*selfcalib)(struct stm32_adc *);
|
||||
int (*prepare)(struct stm32_adc *);
|
||||
void (*start_conv)(struct stm32_adc *, bool dma);
|
||||
@ -695,8 +698,12 @@ static void stm32h7_adc_stop_conv(struct stm32_adc *adc)
|
||||
stm32_adc_clr_bits(adc, STM32H7_ADC_CFGR, STM32H7_DMNGT_MASK);
|
||||
}
|
||||
|
||||
static void stm32h7_adc_exit_pwr_down(struct stm32_adc *adc)
|
||||
static int stm32h7_adc_exit_pwr_down(struct stm32_adc *adc)
|
||||
{
|
||||
struct iio_dev *indio_dev = iio_priv_to_dev(adc);
|
||||
int ret;
|
||||
u32 val;
|
||||
|
||||
/* Exit deep power down, then enable ADC voltage regulator */
|
||||
stm32_adc_clr_bits(adc, STM32H7_ADC_CR, STM32H7_DEEPPWD);
|
||||
stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADVREGEN);
|
||||
@ -705,7 +712,20 @@ static void stm32h7_adc_exit_pwr_down(struct stm32_adc *adc)
|
||||
stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_BOOST);
|
||||
|
||||
/* Wait for startup time */
|
||||
usleep_range(10, 20);
|
||||
if (!adc->cfg->has_vregready) {
|
||||
usleep_range(10, 20);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = stm32_adc_readl_poll_timeout(STM32H7_ADC_ISR, val,
|
||||
val & STM32MP1_VREGREADY, 100,
|
||||
STM32_ADC_TIMEOUT_US);
|
||||
if (ret) {
|
||||
stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_DEEPPWD);
|
||||
dev_err(&indio_dev->dev, "Failed to exit power down\n");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void stm32h7_adc_enter_pwr_down(struct stm32_adc *adc)
|
||||
@ -888,7 +908,9 @@ static int stm32h7_adc_selfcalib(struct stm32_adc *adc)
|
||||
int ret;
|
||||
u32 val;
|
||||
|
||||
stm32h7_adc_exit_pwr_down(adc);
|
||||
ret = stm32h7_adc_exit_pwr_down(adc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Select calibration mode:
|
||||
@ -952,7 +974,10 @@ static int stm32h7_adc_prepare(struct stm32_adc *adc)
|
||||
{
|
||||
int ret;
|
||||
|
||||
stm32h7_adc_exit_pwr_down(adc);
|
||||
ret = stm32h7_adc_exit_pwr_down(adc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
stm32_adc_writel(adc, STM32H7_ADC_DIFSEL, adc->difsel);
|
||||
|
||||
ret = stm32h7_adc_enable(adc);
|
||||
@ -1944,9 +1969,23 @@ static const struct stm32_adc_cfg stm32h7_adc_cfg = {
|
||||
.smp_cycles = stm32h7_adc_smp_cycles,
|
||||
};
|
||||
|
||||
static const struct stm32_adc_cfg stm32mp1_adc_cfg = {
|
||||
.regs = &stm32h7_adc_regspec,
|
||||
.adc_info = &stm32h7_adc_info,
|
||||
.trigs = stm32h7_adc_trigs,
|
||||
.has_vregready = true,
|
||||
.selfcalib = stm32h7_adc_selfcalib,
|
||||
.start_conv = stm32h7_adc_start_conv,
|
||||
.stop_conv = stm32h7_adc_stop_conv,
|
||||
.prepare = stm32h7_adc_prepare,
|
||||
.unprepare = stm32h7_adc_unprepare,
|
||||
.smp_cycles = stm32h7_adc_smp_cycles,
|
||||
};
|
||||
|
||||
static const struct of_device_id stm32_adc_of_match[] = {
|
||||
{ .compatible = "st,stm32f4-adc", .data = (void *)&stm32f4_adc_cfg },
|
||||
{ .compatible = "st,stm32h7-adc", .data = (void *)&stm32h7_adc_cfg },
|
||||
{ .compatible = "st,stm32mp1-adc", .data = (void *)&stm32mp1_adc_cfg },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, stm32_adc_of_match);
|
||||
|
@ -8,11 +8,11 @@
|
||||
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/iio/adc/stm32-dfsdm-adc.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/hw-consumer.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
@ -254,7 +254,8 @@ static int stm32_dfsdm_start_filter(struct stm32_dfsdm *dfsdm,
|
||||
DFSDM_CR1_RSWSTART(1));
|
||||
}
|
||||
|
||||
static void stm32_dfsdm_stop_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id)
|
||||
static void stm32_dfsdm_stop_filter(struct stm32_dfsdm *dfsdm,
|
||||
unsigned int fl_id)
|
||||
{
|
||||
/* Disable conversion */
|
||||
regmap_update_bits(dfsdm->regmap, DFSDM_CR1(fl_id),
|
||||
@ -338,7 +339,7 @@ static int stm32_dfsdm_channel_parse_of(struct stm32_dfsdm *dfsdm,
|
||||
"st,adc-channel-types", chan_idx,
|
||||
&of_str);
|
||||
if (!ret) {
|
||||
val = stm32_dfsdm_str2val(of_str, stm32_dfsdm_chan_type);
|
||||
val = stm32_dfsdm_str2val(of_str, stm32_dfsdm_chan_type);
|
||||
if (val < 0)
|
||||
return val;
|
||||
} else {
|
||||
@ -350,7 +351,7 @@ static int stm32_dfsdm_channel_parse_of(struct stm32_dfsdm *dfsdm,
|
||||
"st,adc-channel-clk-src", chan_idx,
|
||||
&of_str);
|
||||
if (!ret) {
|
||||
val = stm32_dfsdm_str2val(of_str, stm32_dfsdm_chan_src);
|
||||
val = stm32_dfsdm_str2val(of_str, stm32_dfsdm_chan_src);
|
||||
if (val < 0)
|
||||
return val;
|
||||
} else {
|
||||
@ -1104,7 +1105,6 @@ static int stm32_dfsdm_adc_probe(struct platform_device *pdev)
|
||||
char *name;
|
||||
int ret, irq, val;
|
||||
|
||||
|
||||
dev_data = of_device_get_match_data(dev);
|
||||
iio = devm_iio_device_alloc(dev, sizeof(*adc));
|
||||
if (!iio) {
|
||||
@ -1122,8 +1122,8 @@ static int stm32_dfsdm_adc_probe(struct platform_device *pdev)
|
||||
platform_set_drvdata(pdev, adc);
|
||||
|
||||
ret = of_property_read_u32(dev->of_node, "reg", &adc->fl_id);
|
||||
if (ret != 0) {
|
||||
dev_err(dev, "Missing reg property\n");
|
||||
if (ret != 0 || adc->fl_id >= adc->dfsdm->num_fls) {
|
||||
dev_err(dev, "Missing or bad reg property\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -1172,7 +1172,6 @@ static int stm32_dfsdm_adc_probe(struct platform_device *pdev)
|
||||
if (ret < 0)
|
||||
goto err_cleanup;
|
||||
|
||||
dev_err(dev, "of_platform_populate\n");
|
||||
if (dev_data->type == DFSDM_AUDIO) {
|
||||
ret = of_platform_populate(np, NULL, NULL, dev);
|
||||
if (ret < 0) {
|
||||
|
@ -25,6 +25,8 @@ struct stm32_dfsdm_dev_data {
|
||||
|
||||
#define STM32H7_DFSDM_NUM_FILTERS 4
|
||||
#define STM32H7_DFSDM_NUM_CHANNELS 8
|
||||
#define STM32MP1_DFSDM_NUM_FILTERS 6
|
||||
#define STM32MP1_DFSDM_NUM_CHANNELS 8
|
||||
|
||||
static bool stm32_dfsdm_volatile_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
@ -61,6 +63,21 @@ static const struct stm32_dfsdm_dev_data stm32h7_dfsdm_data = {
|
||||
.regmap_cfg = &stm32h7_dfsdm_regmap_cfg,
|
||||
};
|
||||
|
||||
static const struct regmap_config stm32mp1_dfsdm_regmap_cfg = {
|
||||
.reg_bits = 32,
|
||||
.val_bits = 32,
|
||||
.reg_stride = sizeof(u32),
|
||||
.max_register = 0x7fc,
|
||||
.volatile_reg = stm32_dfsdm_volatile_reg,
|
||||
.fast_io = true,
|
||||
};
|
||||
|
||||
static const struct stm32_dfsdm_dev_data stm32mp1_dfsdm_data = {
|
||||
.num_filters = STM32MP1_DFSDM_NUM_FILTERS,
|
||||
.num_channels = STM32MP1_DFSDM_NUM_CHANNELS,
|
||||
.regmap_cfg = &stm32mp1_dfsdm_regmap_cfg,
|
||||
};
|
||||
|
||||
struct dfsdm_priv {
|
||||
struct platform_device *pdev; /* platform device */
|
||||
|
||||
@ -227,6 +244,11 @@ static int stm32_dfsdm_parse_of(struct platform_device *pdev,
|
||||
}
|
||||
|
||||
priv->spi_clk_out_div = div_u64_rem(clk_freq, spi_freq, &rem) - 1;
|
||||
if (!priv->spi_clk_out_div) {
|
||||
/* spi_clk_out_div == 0 means ckout is OFF */
|
||||
dev_err(&pdev->dev, "spi-max-frequency not achievable\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
priv->dfsdm.spi_master_freq = spi_freq;
|
||||
|
||||
if (rem) {
|
||||
@ -243,6 +265,10 @@ static const struct of_device_id stm32_dfsdm_of_match[] = {
|
||||
.compatible = "st,stm32h7-dfsdm",
|
||||
.data = &stm32h7_dfsdm_data,
|
||||
},
|
||||
{
|
||||
.compatible = "st,stm32mp1-dfsdm",
|
||||
.data = &stm32mp1_dfsdm_data,
|
||||
},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, stm32_dfsdm_of_match);
|
||||
|
@ -233,6 +233,16 @@ static int stx104_gpio_get(struct gpio_chip *chip, unsigned int offset)
|
||||
return !!(inb(stx104gpio->base) & BIT(offset));
|
||||
}
|
||||
|
||||
static int stx104_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask,
|
||||
unsigned long *bits)
|
||||
{
|
||||
struct stx104_gpio *const stx104gpio = gpiochip_get_data(chip);
|
||||
|
||||
*bits = inb(stx104gpio->base);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void stx104_gpio_set(struct gpio_chip *chip, unsigned int offset,
|
||||
int value)
|
||||
{
|
||||
@ -342,6 +352,7 @@ static int stx104_probe(struct device *dev, unsigned int id)
|
||||
stx104gpio->chip.direction_input = stx104_gpio_direction_input;
|
||||
stx104gpio->chip.direction_output = stx104_gpio_direction_output;
|
||||
stx104gpio->chip.get = stx104_gpio_get;
|
||||
stx104gpio->chip.get_multiple = stx104_gpio_get_multiple;
|
||||
stx104gpio->chip.set = stx104_gpio_set;
|
||||
stx104gpio->chip.set_multiple = stx104_gpio_set_multiple;
|
||||
stx104gpio->base = base[id] + 3;
|
||||
|
@ -17,6 +17,9 @@
|
||||
#include <linux/of.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
#define ADS8688_CMD_REG(x) (x << 8)
|
||||
@ -155,6 +158,13 @@ static const struct attribute_group ads8688_attribute_group = {
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) \
|
||||
| BIT(IIO_CHAN_INFO_SCALE) \
|
||||
| BIT(IIO_CHAN_INFO_OFFSET), \
|
||||
.scan_index = index, \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = 16, \
|
||||
.storagebits = 16, \
|
||||
.endianness = IIO_BE, \
|
||||
}, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec ads8684_channels[] = {
|
||||
@ -371,6 +381,28 @@ static const struct iio_info ads8688_info = {
|
||||
.attrs = &ads8688_attribute_group,
|
||||
};
|
||||
|
||||
static irqreturn_t ads8688_trigger_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
u16 buffer[8];
|
||||
int i, j = 0;
|
||||
|
||||
for (i = 0; i < indio_dev->masklength; i++) {
|
||||
if (!test_bit(i, indio_dev->active_scan_mask))
|
||||
continue;
|
||||
buffer[j] = ads8688_read(indio_dev, i);
|
||||
j++;
|
||||
}
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, buffer,
|
||||
pf->timestamp);
|
||||
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static const struct ads8688_chip_info ads8688_chip_info_tbl[] = {
|
||||
[ID_ADS8684] = {
|
||||
.channels = ads8684_channels,
|
||||
@ -402,7 +434,7 @@ static int ads8688_probe(struct spi_device *spi)
|
||||
|
||||
ret = regulator_get_voltage(st->reg);
|
||||
if (ret < 0)
|
||||
goto error_out;
|
||||
goto err_regulator_disable;
|
||||
|
||||
st->vref_mv = ret / 1000;
|
||||
} else {
|
||||
@ -430,13 +462,22 @@ static int ads8688_probe(struct spi_device *spi)
|
||||
|
||||
mutex_init(&st->lock);
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, NULL, ads8688_trigger_handler, NULL);
|
||||
if (ret < 0) {
|
||||
dev_err(&spi->dev, "iio triggered buffer setup failed\n");
|
||||
goto err_regulator_disable;
|
||||
}
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto error_out;
|
||||
goto err_buffer_cleanup;
|
||||
|
||||
return 0;
|
||||
|
||||
error_out:
|
||||
err_buffer_cleanup:
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
|
||||
err_regulator_disable:
|
||||
if (!IS_ERR(st->reg))
|
||||
regulator_disable(st->reg);
|
||||
|
||||
@ -449,6 +490,7 @@ static int ads8688_remove(struct spi_device *spi)
|
||||
struct ads8688_state *st = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
|
||||
if (!IS_ERR(st->reg))
|
||||
regulator_disable(st->reg);
|
||||
|
19
drivers/iio/afe/Kconfig
Normal file
19
drivers/iio/afe/Kconfig
Normal file
@ -0,0 +1,19 @@
|
||||
#
|
||||
# Analog Front End drivers
|
||||
#
|
||||
# When adding new entries keep the list in alphabetical order
|
||||
|
||||
menu "Analog Front Ends"
|
||||
|
||||
config IIO_RESCALE
|
||||
tristate "IIO rescale"
|
||||
depends on OF || COMPILE_TEST
|
||||
help
|
||||
Say yes here to build support for the IIO rescaling
|
||||
that handles voltage dividers, current sense shunts and
|
||||
current sense amplifiers.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called iio-rescale.
|
||||
|
||||
endmenu
|
6
drivers/iio/afe/Makefile
Normal file
6
drivers/iio/afe/Makefile
Normal file
@ -0,0 +1,6 @@
|
||||
#
|
||||
# Makefile for industrial I/O Analog Front Ends (AFE)
|
||||
#
|
||||
|
||||
# When adding new entries keep the list in alphabetical order
|
||||
obj-$(CONFIG_IIO_RESCALE) += iio-rescale.o
|
359
drivers/iio/afe/iio-rescale.c
Normal file
359
drivers/iio/afe/iio-rescale.c
Normal file
@ -0,0 +1,359 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* IIO rescale driver
|
||||
*
|
||||
* Copyright (C) 2018 Axentia Technologies AB
|
||||
*
|
||||
* Author: Peter Rosin <peda@axentia.se>
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/gcd.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>
|
||||
#include <linux/property.h>
|
||||
|
||||
struct rescale;
|
||||
|
||||
struct rescale_cfg {
|
||||
enum iio_chan_type type;
|
||||
int (*props)(struct device *dev, struct rescale *rescale);
|
||||
};
|
||||
|
||||
struct rescale {
|
||||
const struct rescale_cfg *cfg;
|
||||
struct iio_channel *source;
|
||||
struct iio_chan_spec chan;
|
||||
struct iio_chan_spec_ext_info *ext_info;
|
||||
s32 numerator;
|
||||
s32 denominator;
|
||||
};
|
||||
|
||||
static int rescale_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct rescale *rescale = iio_priv(indio_dev);
|
||||
unsigned long long tmp;
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
return iio_read_channel_raw(rescale->source, val);
|
||||
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
ret = iio_read_channel_scale(rescale->source, val, val2);
|
||||
switch (ret) {
|
||||
case IIO_VAL_FRACTIONAL:
|
||||
*val *= rescale->numerator;
|
||||
*val2 *= rescale->denominator;
|
||||
return ret;
|
||||
case IIO_VAL_INT:
|
||||
*val *= rescale->numerator;
|
||||
if (rescale->denominator == 1)
|
||||
return ret;
|
||||
*val2 = rescale->denominator;
|
||||
return IIO_VAL_FRACTIONAL;
|
||||
case IIO_VAL_FRACTIONAL_LOG2:
|
||||
tmp = *val * 1000000000LL;
|
||||
do_div(tmp, rescale->denominator);
|
||||
tmp *= rescale->numerator;
|
||||
do_div(tmp, 1000000000LL);
|
||||
*val = tmp;
|
||||
return ret;
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int rescale_read_avail(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
const int **vals, int *type, int *length,
|
||||
long mask)
|
||||
{
|
||||
struct rescale *rescale = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
*type = IIO_VAL_INT;
|
||||
return iio_read_avail_channel_raw(rescale->source,
|
||||
vals, length);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct iio_info rescale_info = {
|
||||
.read_raw = rescale_read_raw,
|
||||
.read_avail = rescale_read_avail,
|
||||
};
|
||||
|
||||
static ssize_t rescale_read_ext_info(struct iio_dev *indio_dev,
|
||||
uintptr_t private,
|
||||
struct iio_chan_spec const *chan,
|
||||
char *buf)
|
||||
{
|
||||
struct rescale *rescale = iio_priv(indio_dev);
|
||||
|
||||
return iio_read_channel_ext_info(rescale->source,
|
||||
rescale->ext_info[private].name,
|
||||
buf);
|
||||
}
|
||||
|
||||
static ssize_t rescale_write_ext_info(struct iio_dev *indio_dev,
|
||||
uintptr_t private,
|
||||
struct iio_chan_spec const *chan,
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
struct rescale *rescale = iio_priv(indio_dev);
|
||||
|
||||
return iio_write_channel_ext_info(rescale->source,
|
||||
rescale->ext_info[private].name,
|
||||
buf, len);
|
||||
}
|
||||
|
||||
static int rescale_configure_channel(struct device *dev,
|
||||
struct rescale *rescale)
|
||||
{
|
||||
struct iio_chan_spec *chan = &rescale->chan;
|
||||
struct iio_chan_spec const *schan = rescale->source->channel;
|
||||
|
||||
chan->indexed = 1;
|
||||
chan->output = schan->output;
|
||||
chan->ext_info = rescale->ext_info;
|
||||
chan->type = rescale->cfg->type;
|
||||
|
||||
if (!iio_channel_has_info(schan, IIO_CHAN_INFO_RAW) ||
|
||||
!iio_channel_has_info(schan, IIO_CHAN_INFO_SCALE)) {
|
||||
dev_err(dev, "source channel does not support raw/scale\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE);
|
||||
|
||||
if (iio_channel_has_available(schan, IIO_CHAN_INFO_RAW))
|
||||
chan->info_mask_separate_available |= BIT(IIO_CHAN_INFO_RAW);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rescale_current_sense_amplifier_props(struct device *dev,
|
||||
struct rescale *rescale)
|
||||
{
|
||||
u32 sense;
|
||||
u32 gain_mult = 1;
|
||||
u32 gain_div = 1;
|
||||
u32 factor;
|
||||
int ret;
|
||||
|
||||
ret = device_property_read_u32(dev, "sense-resistor-micro-ohms",
|
||||
&sense);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to read the sense resistance: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
device_property_read_u32(dev, "sense-gain-mult", &gain_mult);
|
||||
device_property_read_u32(dev, "sense-gain-div", &gain_div);
|
||||
|
||||
/*
|
||||
* Calculate the scaling factor, 1 / (gain * sense), or
|
||||
* gain_div / (gain_mult * sense), while trying to keep the
|
||||
* numerator/denominator from overflowing.
|
||||
*/
|
||||
factor = gcd(sense, 1000000);
|
||||
rescale->numerator = 1000000 / factor;
|
||||
rescale->denominator = sense / factor;
|
||||
|
||||
factor = gcd(rescale->numerator, gain_mult);
|
||||
rescale->numerator /= factor;
|
||||
rescale->denominator *= gain_mult / factor;
|
||||
|
||||
factor = gcd(rescale->denominator, gain_div);
|
||||
rescale->numerator *= gain_div / factor;
|
||||
rescale->denominator /= factor;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rescale_current_sense_shunt_props(struct device *dev,
|
||||
struct rescale *rescale)
|
||||
{
|
||||
u32 shunt;
|
||||
u32 factor;
|
||||
int ret;
|
||||
|
||||
ret = device_property_read_u32(dev, "shunt-resistor-micro-ohms",
|
||||
&shunt);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to read the shunt resistance: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
factor = gcd(shunt, 1000000);
|
||||
rescale->numerator = 1000000 / factor;
|
||||
rescale->denominator = shunt / factor;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rescale_voltage_divider_props(struct device *dev,
|
||||
struct rescale *rescale)
|
||||
{
|
||||
int ret;
|
||||
u32 factor;
|
||||
|
||||
ret = device_property_read_u32(dev, "output-ohms",
|
||||
&rescale->denominator);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to read output-ohms: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = device_property_read_u32(dev, "full-ohms",
|
||||
&rescale->numerator);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to read full-ohms: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
factor = gcd(rescale->numerator, rescale->denominator);
|
||||
rescale->numerator /= factor;
|
||||
rescale->denominator /= factor;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
enum rescale_variant {
|
||||
CURRENT_SENSE_AMPLIFIER,
|
||||
CURRENT_SENSE_SHUNT,
|
||||
VOLTAGE_DIVIDER,
|
||||
};
|
||||
|
||||
static const struct rescale_cfg rescale_cfg[] = {
|
||||
[CURRENT_SENSE_AMPLIFIER] = {
|
||||
.type = IIO_CURRENT,
|
||||
.props = rescale_current_sense_amplifier_props,
|
||||
},
|
||||
[CURRENT_SENSE_SHUNT] = {
|
||||
.type = IIO_CURRENT,
|
||||
.props = rescale_current_sense_shunt_props,
|
||||
},
|
||||
[VOLTAGE_DIVIDER] = {
|
||||
.type = IIO_VOLTAGE,
|
||||
.props = rescale_voltage_divider_props,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct of_device_id rescale_match[] = {
|
||||
{ .compatible = "current-sense-amplifier",
|
||||
.data = &rescale_cfg[CURRENT_SENSE_AMPLIFIER], },
|
||||
{ .compatible = "current-sense-shunt",
|
||||
.data = &rescale_cfg[CURRENT_SENSE_SHUNT], },
|
||||
{ .compatible = "voltage-divider",
|
||||
.data = &rescale_cfg[VOLTAGE_DIVIDER], },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rescale_match);
|
||||
|
||||
static int rescale_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct iio_dev *indio_dev;
|
||||
struct iio_channel *source;
|
||||
struct rescale *rescale;
|
||||
int sizeof_ext_info;
|
||||
int sizeof_priv;
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
source = devm_iio_channel_get(dev, NULL);
|
||||
if (IS_ERR(source)) {
|
||||
if (PTR_ERR(source) != -EPROBE_DEFER)
|
||||
dev_err(dev, "failed to get source channel\n");
|
||||
return PTR_ERR(source);
|
||||
}
|
||||
|
||||
sizeof_ext_info = iio_get_channel_ext_info_count(source);
|
||||
if (sizeof_ext_info) {
|
||||
sizeof_ext_info += 1; /* one extra entry for the sentinel */
|
||||
sizeof_ext_info *= sizeof(*rescale->ext_info);
|
||||
}
|
||||
|
||||
sizeof_priv = sizeof(*rescale) + sizeof_ext_info;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(dev, sizeof_priv);
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
rescale = iio_priv(indio_dev);
|
||||
|
||||
rescale->cfg = of_device_get_match_data(dev);
|
||||
rescale->numerator = 1;
|
||||
rescale->denominator = 1;
|
||||
|
||||
ret = rescale->cfg->props(dev, rescale);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!rescale->numerator || !rescale->denominator) {
|
||||
dev_err(dev, "invalid scaling factor.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, indio_dev);
|
||||
|
||||
rescale->source = source;
|
||||
|
||||
indio_dev->name = dev_name(dev);
|
||||
indio_dev->dev.parent = dev;
|
||||
indio_dev->info = &rescale_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = &rescale->chan;
|
||||
indio_dev->num_channels = 1;
|
||||
if (sizeof_ext_info) {
|
||||
rescale->ext_info = devm_kmemdup(dev,
|
||||
source->channel->ext_info,
|
||||
sizeof_ext_info, GFP_KERNEL);
|
||||
if (!rescale->ext_info)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; rescale->ext_info[i].name; ++i) {
|
||||
struct iio_chan_spec_ext_info *ext_info =
|
||||
&rescale->ext_info[i];
|
||||
|
||||
if (source->channel->ext_info[i].read)
|
||||
ext_info->read = rescale_read_ext_info;
|
||||
if (source->channel->ext_info[i].write)
|
||||
ext_info->write = rescale_write_ext_info;
|
||||
ext_info->private = i;
|
||||
}
|
||||
}
|
||||
|
||||
ret = rescale_configure_channel(dev, rescale);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return devm_iio_device_register(dev, indio_dev);
|
||||
}
|
||||
|
||||
static struct platform_driver rescale_driver = {
|
||||
.probe = rescale_probe,
|
||||
.driver = {
|
||||
.name = "iio-rescale",
|
||||
.of_match_table = rescale_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(rescale_driver);
|
||||
|
||||
MODULE_DESCRIPTION("IIO rescale driver");
|
||||
MODULE_AUTHOR("Peter Rosin <peda@axentia.se>");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -161,12 +161,14 @@ static int ad8366_probe(struct spi_device *spi)
|
||||
indio_dev->channels = ad8366_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(ad8366_channels);
|
||||
|
||||
ret = ad8366_write(indio_dev, 0 , 0);
|
||||
if (ret < 0)
|
||||
goto error_disable_reg;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto error_disable_reg;
|
||||
|
||||
ad8366_write(indio_dev, 0, 0);
|
||||
|
||||
return 0;
|
||||
|
||||
error_disable_reg:
|
||||
|
@ -61,9 +61,9 @@
|
||||
#define ATLAS_REG_ORP_CALIB_STATUS 0x0d
|
||||
#define ATLAS_REG_ORP_DATA 0x0e
|
||||
|
||||
#define ATLAS_PH_INT_TIME_IN_US 450000
|
||||
#define ATLAS_EC_INT_TIME_IN_US 650000
|
||||
#define ATLAS_ORP_INT_TIME_IN_US 450000
|
||||
#define ATLAS_PH_INT_TIME_IN_MS 450
|
||||
#define ATLAS_EC_INT_TIME_IN_MS 650
|
||||
#define ATLAS_ORP_INT_TIME_IN_MS 450
|
||||
|
||||
enum {
|
||||
ATLAS_PH_SM,
|
||||
@ -270,21 +270,21 @@ static struct atlas_device atlas_devices[] = {
|
||||
.num_channels = 3,
|
||||
.data_reg = ATLAS_REG_PH_DATA,
|
||||
.calibration = &atlas_check_ph_calibration,
|
||||
.delay = ATLAS_PH_INT_TIME_IN_US,
|
||||
.delay = ATLAS_PH_INT_TIME_IN_MS,
|
||||
},
|
||||
[ATLAS_EC_SM] = {
|
||||
.channels = atlas_ec_channels,
|
||||
.num_channels = 5,
|
||||
.data_reg = ATLAS_REG_EC_DATA,
|
||||
.calibration = &atlas_check_ec_calibration,
|
||||
.delay = ATLAS_EC_INT_TIME_IN_US,
|
||||
.delay = ATLAS_EC_INT_TIME_IN_MS,
|
||||
},
|
||||
[ATLAS_ORP_SM] = {
|
||||
.channels = atlas_orp_channels,
|
||||
.num_channels = 2,
|
||||
.data_reg = ATLAS_REG_ORP_DATA,
|
||||
.calibration = &atlas_check_orp_calibration,
|
||||
.delay = ATLAS_ORP_INT_TIME_IN_US,
|
||||
.delay = ATLAS_ORP_INT_TIME_IN_MS,
|
||||
},
|
||||
};
|
||||
|
||||
@ -393,7 +393,7 @@ static int atlas_read_measurement(struct atlas_data *data, int reg, __be32 *val)
|
||||
}
|
||||
|
||||
if (suspended)
|
||||
usleep_range(data->chip->delay, data->chip->delay + 100000);
|
||||
msleep(data->chip->delay);
|
||||
|
||||
ret = regmap_bulk_read(data->regmap, reg, (u8 *) val, sizeof(*val));
|
||||
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/common/cros_ec_sensors_core.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/kfifo_buf.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
@ -31,8 +32,6 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfs.h>
|
||||
|
||||
#include "cros_ec_sensors_core.h"
|
||||
|
||||
#define CROS_EC_SENSORS_MAX_CHANNELS 4
|
||||
|
||||
/* State data for ec_sensors iio driver. */
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/common/cros_ec_sensors_core.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/kfifo_buf.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
@ -27,8 +28,6 @@
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include "cros_ec_sensors_core.h"
|
||||
|
||||
static char *cros_ec_loc[] = {
|
||||
[MOTIONSENSE_LOC_BASE] = "base",
|
||||
[MOTIONSENSE_LOC_LID] = "lid",
|
||||
@ -448,8 +447,7 @@ EXPORT_SYMBOL_GPL(cros_ec_sensors_core_write);
|
||||
|
||||
static int __maybe_unused cros_ec_sensors_prepare(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
|
||||
|
||||
if (st->curr_sampl_freq == 0)
|
||||
@ -471,8 +469,7 @@ static int __maybe_unused cros_ec_sensors_prepare(struct device *dev)
|
||||
|
||||
static void __maybe_unused cros_ec_sensors_complete(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
|
||||
|
||||
if (st->curr_sampl_freq == 0)
|
||||
|
@ -304,8 +304,7 @@ EXPORT_SYMBOL(hid_sensor_setup_trigger);
|
||||
|
||||
static int __maybe_unused hid_sensor_suspend(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct hid_sensor_common *attrb = iio_device_get_drvdata(indio_dev);
|
||||
|
||||
return _hid_sensor_power_state(attrb, false);
|
||||
@ -313,8 +312,7 @@ static int __maybe_unused hid_sensor_suspend(struct device *dev)
|
||||
|
||||
static int __maybe_unused hid_sensor_resume(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct hid_sensor_common *attrb = iio_device_get_drvdata(indio_dev);
|
||||
schedule_work(&attrb->work);
|
||||
return 0;
|
||||
@ -322,8 +320,7 @@ static int __maybe_unused hid_sensor_resume(struct device *dev)
|
||||
|
||||
static int __maybe_unused hid_sensor_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct hid_sensor_common *attrb = iio_device_get_drvdata(indio_dev);
|
||||
return _hid_sensor_power_state(attrb, true);
|
||||
}
|
||||
|
@ -131,16 +131,31 @@ config LTC2632
|
||||
module will be called ltc2632.
|
||||
|
||||
config AD5686
|
||||
tristate "Analog Devices AD5686R/AD5685R/AD5684R DAC SPI driver"
|
||||
tristate
|
||||
|
||||
config AD5686_SPI
|
||||
tristate "Analog Devices AD5686 and similar multi-channel DACs (SPI)"
|
||||
depends on SPI
|
||||
select AD5686
|
||||
help
|
||||
Say yes here to build support for Analog Devices AD5686R, AD5685R,
|
||||
AD5684R, AD5791 Voltage Output Digital to
|
||||
Analog Converter.
|
||||
Say yes here to build support for Analog Devices AD5672R, AD5676,
|
||||
AD5676R, AD5684, AD5684R, AD5684R, AD5685R, AD5686, AD5686R.
|
||||
Voltage Output Digital to Analog Converter.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ad5686.
|
||||
|
||||
config AD5696_I2C
|
||||
tristate "Analog Devices AD5696 and similar multi-channel DACs (I2C)"
|
||||
depends on I2C
|
||||
select AD5686
|
||||
help
|
||||
Say yes here to build support for Analog Devices AD5671R, AD5675R,
|
||||
AD5694, AD5694R, AD5695R, AD5696, AD5696R Voltage Output Digital to
|
||||
Analog Converter.
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called ad5696.
|
||||
|
||||
config AD5755
|
||||
tristate "Analog Devices AD5755/AD5755-1/AD5757/AD5735/AD5737 DAC driver"
|
||||
depends on SPI_MASTER
|
||||
@ -321,6 +336,16 @@ config TI_DAC082S085
|
||||
|
||||
If compiled as a module, it will be called ti-dac082s085.
|
||||
|
||||
config TI_DAC5571
|
||||
tristate "Texas Instruments 8/10/12/16-bit 1/2/4-channel DAC driver"
|
||||
depends on I2C
|
||||
help
|
||||
Driver for the Texas Instruments
|
||||
DAC5571, DAC6571, DAC7571, DAC5574, DAC6574, DAC7574, DAC5573,
|
||||
DAC6573, DAC7573, DAC8571, DAC8574.
|
||||
|
||||
If compiled as a module, it will be called ti-dac5571.
|
||||
|
||||
config VF610_DAC
|
||||
tristate "Vybrid vf610 DAC driver"
|
||||
depends on OF
|
||||
|
@ -20,6 +20,8 @@ obj-$(CONFIG_AD5761) += ad5761.o
|
||||
obj-$(CONFIG_AD5764) += ad5764.o
|
||||
obj-$(CONFIG_AD5791) += ad5791.o
|
||||
obj-$(CONFIG_AD5686) += ad5686.o
|
||||
obj-$(CONFIG_AD5686_SPI) += ad5686-spi.o
|
||||
obj-$(CONFIG_AD5696_I2C) += ad5696-i2c.o
|
||||
obj-$(CONFIG_AD7303) += ad7303.o
|
||||
obj-$(CONFIG_AD8801) += ad8801.o
|
||||
obj-$(CONFIG_CIO_DAC) += cio-dac.o
|
||||
@ -35,4 +37,5 @@ obj-$(CONFIG_MCP4922) += mcp4922.o
|
||||
obj-$(CONFIG_STM32_DAC_CORE) += stm32-dac-core.o
|
||||
obj-$(CONFIG_STM32_DAC) += stm32-dac.o
|
||||
obj-$(CONFIG_TI_DAC082S085) += ti-dac082s085.o
|
||||
obj-$(CONFIG_TI_DAC5571) += ti-dac5571.o
|
||||
obj-$(CONFIG_VF610_DAC) += vf610_dac.o
|
||||
|
119
drivers/iio/dac/ad5686-spi.c
Normal file
119
drivers/iio/dac/ad5686-spi.c
Normal file
@ -0,0 +1,119 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* AD5672R, AD5676, AD5676R, AD5681R, AD5682R, AD5683, AD5683R,
|
||||
* AD5684, AD5684R, AD5685R, AD5686, AD5686R
|
||||
* Digital to analog converters driver
|
||||
*
|
||||
* Copyright 2018 Analog Devices Inc.
|
||||
*/
|
||||
|
||||
#include "ad5686.h"
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
static int ad5686_spi_write(struct ad5686_state *st,
|
||||
u8 cmd, u8 addr, u16 val)
|
||||
{
|
||||
struct spi_device *spi = to_spi_device(st->dev);
|
||||
u8 tx_len, *buf;
|
||||
|
||||
switch (st->chip_info->regmap_type) {
|
||||
case AD5683_REGMAP:
|
||||
st->data[0].d32 = cpu_to_be32(AD5686_CMD(cmd) |
|
||||
AD5683_DATA(val));
|
||||
buf = &st->data[0].d8[1];
|
||||
tx_len = 3;
|
||||
break;
|
||||
case AD5686_REGMAP:
|
||||
st->data[0].d32 = cpu_to_be32(AD5686_CMD(cmd) |
|
||||
AD5686_ADDR(addr) |
|
||||
val);
|
||||
buf = &st->data[0].d8[1];
|
||||
tx_len = 3;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return spi_write(spi, buf, tx_len);
|
||||
}
|
||||
|
||||
static int ad5686_spi_read(struct ad5686_state *st, u8 addr)
|
||||
{
|
||||
struct spi_transfer t[] = {
|
||||
{
|
||||
.tx_buf = &st->data[0].d8[1],
|
||||
.len = 3,
|
||||
.cs_change = 1,
|
||||
}, {
|
||||
.tx_buf = &st->data[1].d8[1],
|
||||
.rx_buf = &st->data[2].d8[1],
|
||||
.len = 3,
|
||||
},
|
||||
};
|
||||
struct spi_device *spi = to_spi_device(st->dev);
|
||||
u8 cmd = 0;
|
||||
int ret;
|
||||
|
||||
if (st->chip_info->regmap_type == AD5686_REGMAP)
|
||||
cmd = AD5686_CMD_READBACK_ENABLE;
|
||||
else if (st->chip_info->regmap_type == AD5683_REGMAP)
|
||||
cmd = AD5686_CMD_READBACK_ENABLE_V2;
|
||||
|
||||
st->data[0].d32 = cpu_to_be32(AD5686_CMD(cmd) |
|
||||
AD5686_ADDR(addr));
|
||||
st->data[1].d32 = cpu_to_be32(AD5686_CMD(AD5686_CMD_NOOP));
|
||||
|
||||
ret = spi_sync_transfer(spi, t, ARRAY_SIZE(t));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return be32_to_cpu(st->data[2].d32);
|
||||
}
|
||||
|
||||
static int ad5686_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
const struct spi_device_id *id = spi_get_device_id(spi);
|
||||
|
||||
return ad5686_probe(&spi->dev, id->driver_data, id->name,
|
||||
ad5686_spi_write, ad5686_spi_read);
|
||||
}
|
||||
|
||||
static int ad5686_spi_remove(struct spi_device *spi)
|
||||
{
|
||||
return ad5686_remove(&spi->dev);
|
||||
}
|
||||
|
||||
static const struct spi_device_id ad5686_spi_id[] = {
|
||||
{"ad5672r", ID_AD5672R},
|
||||
{"ad5676", ID_AD5676},
|
||||
{"ad5676r", ID_AD5676R},
|
||||
{"ad5681r", ID_AD5681R},
|
||||
{"ad5682r", ID_AD5682R},
|
||||
{"ad5683", ID_AD5683},
|
||||
{"ad5683r", ID_AD5683R},
|
||||
{"ad5684", ID_AD5684},
|
||||
{"ad5684r", ID_AD5684R},
|
||||
{"ad5685", ID_AD5685R}, /* Does not exist */
|
||||
{"ad5685r", ID_AD5685R},
|
||||
{"ad5686", ID_AD5686},
|
||||
{"ad5686r", ID_AD5686R},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ad5686_spi_id);
|
||||
|
||||
static struct spi_driver ad5686_spi_driver = {
|
||||
.driver = {
|
||||
.name = "ad5686",
|
||||
},
|
||||
.probe = ad5686_spi_probe,
|
||||
.remove = ad5686_spi_remove,
|
||||
.id_table = ad5686_spi_id,
|
||||
};
|
||||
|
||||
module_spi_driver(ad5686_spi_driver);
|
||||
|
||||
MODULE_AUTHOR("Stefan Popa <stefan.popa@analog.com>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD5686 and similar multi-channel DACs");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -1,9 +1,8 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* AD5686R, AD5685R, AD5684R Digital to analog converters driver
|
||||
*
|
||||
* Copyright 2011 Analog Devices Inc.
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
@ -11,7 +10,6 @@
|
||||
#include <linux/device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
@ -19,116 +17,7 @@
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
#define AD5686_DAC_CHANNELS 4
|
||||
|
||||
#define AD5686_ADDR(x) ((x) << 16)
|
||||
#define AD5686_CMD(x) ((x) << 20)
|
||||
|
||||
#define AD5686_ADDR_DAC(chan) (0x1 << (chan))
|
||||
#define AD5686_ADDR_ALL_DAC 0xF
|
||||
|
||||
#define AD5686_CMD_NOOP 0x0
|
||||
#define AD5686_CMD_WRITE_INPUT_N 0x1
|
||||
#define AD5686_CMD_UPDATE_DAC_N 0x2
|
||||
#define AD5686_CMD_WRITE_INPUT_N_UPDATE_N 0x3
|
||||
#define AD5686_CMD_POWERDOWN_DAC 0x4
|
||||
#define AD5686_CMD_LDAC_MASK 0x5
|
||||
#define AD5686_CMD_RESET 0x6
|
||||
#define AD5686_CMD_INTERNAL_REFER_SETUP 0x7
|
||||
#define AD5686_CMD_DAISY_CHAIN_ENABLE 0x8
|
||||
#define AD5686_CMD_READBACK_ENABLE 0x9
|
||||
|
||||
#define AD5686_LDAC_PWRDN_NONE 0x0
|
||||
#define AD5686_LDAC_PWRDN_1K 0x1
|
||||
#define AD5686_LDAC_PWRDN_100K 0x2
|
||||
#define AD5686_LDAC_PWRDN_3STATE 0x3
|
||||
|
||||
/**
|
||||
* struct ad5686_chip_info - chip specific information
|
||||
* @int_vref_mv: AD5620/40/60: the internal reference voltage
|
||||
* @channel: channel specification
|
||||
*/
|
||||
|
||||
struct ad5686_chip_info {
|
||||
u16 int_vref_mv;
|
||||
struct iio_chan_spec channel[AD5686_DAC_CHANNELS];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ad5446_state - driver instance specific data
|
||||
* @spi: spi_device
|
||||
* @chip_info: chip model specific constants, available modes etc
|
||||
* @reg: supply regulator
|
||||
* @vref_mv: actual reference voltage used
|
||||
* @pwr_down_mask: power down mask
|
||||
* @pwr_down_mode: current power down mode
|
||||
* @data: spi transfer buffers
|
||||
*/
|
||||
|
||||
struct ad5686_state {
|
||||
struct spi_device *spi;
|
||||
const struct ad5686_chip_info *chip_info;
|
||||
struct regulator *reg;
|
||||
unsigned short vref_mv;
|
||||
unsigned pwr_down_mask;
|
||||
unsigned pwr_down_mode;
|
||||
/*
|
||||
* DMA (thus cache coherency maintenance) requires the
|
||||
* transfer buffers to live in their own cache lines.
|
||||
*/
|
||||
|
||||
union {
|
||||
__be32 d32;
|
||||
u8 d8[4];
|
||||
} data[3] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
/**
|
||||
* ad5686_supported_device_ids:
|
||||
*/
|
||||
|
||||
enum ad5686_supported_device_ids {
|
||||
ID_AD5684,
|
||||
ID_AD5685,
|
||||
ID_AD5686,
|
||||
};
|
||||
static int ad5686_spi_write(struct ad5686_state *st,
|
||||
u8 cmd, u8 addr, u16 val, u8 shift)
|
||||
{
|
||||
val <<= shift;
|
||||
|
||||
st->data[0].d32 = cpu_to_be32(AD5686_CMD(cmd) |
|
||||
AD5686_ADDR(addr) |
|
||||
val);
|
||||
|
||||
return spi_write(st->spi, &st->data[0].d8[1], 3);
|
||||
}
|
||||
|
||||
static int ad5686_spi_read(struct ad5686_state *st, u8 addr)
|
||||
{
|
||||
struct spi_transfer t[] = {
|
||||
{
|
||||
.tx_buf = &st->data[0].d8[1],
|
||||
.len = 3,
|
||||
.cs_change = 1,
|
||||
}, {
|
||||
.tx_buf = &st->data[1].d8[1],
|
||||
.rx_buf = &st->data[2].d8[1],
|
||||
.len = 3,
|
||||
},
|
||||
};
|
||||
int ret;
|
||||
|
||||
st->data[0].d32 = cpu_to_be32(AD5686_CMD(AD5686_CMD_READBACK_ENABLE) |
|
||||
AD5686_ADDR(addr));
|
||||
st->data[1].d32 = cpu_to_be32(AD5686_CMD(AD5686_CMD_NOOP));
|
||||
|
||||
ret = spi_sync_transfer(st->spi, t, ARRAY_SIZE(t));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return be32_to_cpu(st->data[2].d32);
|
||||
}
|
||||
#include "ad5686.h"
|
||||
|
||||
static const char * const ad5686_powerdown_modes[] = {
|
||||
"1kohm_to_gnd",
|
||||
@ -137,7 +26,7 @@ static const char * const ad5686_powerdown_modes[] = {
|
||||
};
|
||||
|
||||
static int ad5686_get_powerdown_mode(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan)
|
||||
const struct iio_chan_spec *chan)
|
||||
{
|
||||
struct ad5686_state *st = iio_priv(indio_dev);
|
||||
|
||||
@ -145,7 +34,8 @@ static int ad5686_get_powerdown_mode(struct iio_dev *indio_dev,
|
||||
}
|
||||
|
||||
static int ad5686_set_powerdown_mode(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, unsigned int mode)
|
||||
const struct iio_chan_spec *chan,
|
||||
unsigned int mode)
|
||||
{
|
||||
struct ad5686_state *st = iio_priv(indio_dev);
|
||||
|
||||
@ -163,21 +53,25 @@ static const struct iio_enum ad5686_powerdown_mode_enum = {
|
||||
};
|
||||
|
||||
static ssize_t ad5686_read_dac_powerdown(struct iio_dev *indio_dev,
|
||||
uintptr_t private, const struct iio_chan_spec *chan, char *buf)
|
||||
uintptr_t private, const struct iio_chan_spec *chan, char *buf)
|
||||
{
|
||||
struct ad5686_state *st = iio_priv(indio_dev);
|
||||
|
||||
return sprintf(buf, "%d\n", !!(st->pwr_down_mask &
|
||||
(0x3 << (chan->channel * 2))));
|
||||
(0x3 << (chan->channel * 2))));
|
||||
}
|
||||
|
||||
static ssize_t ad5686_write_dac_powerdown(struct iio_dev *indio_dev,
|
||||
uintptr_t private, const struct iio_chan_spec *chan, const char *buf,
|
||||
size_t len)
|
||||
uintptr_t private,
|
||||
const struct iio_chan_spec *chan,
|
||||
const char *buf,
|
||||
size_t len)
|
||||
{
|
||||
bool readin;
|
||||
int ret;
|
||||
struct ad5686_state *st = iio_priv(indio_dev);
|
||||
unsigned int val, ref_bit_msk;
|
||||
u8 shift;
|
||||
|
||||
ret = strtobool(buf, &readin);
|
||||
if (ret)
|
||||
@ -188,8 +82,28 @@ static ssize_t ad5686_write_dac_powerdown(struct iio_dev *indio_dev,
|
||||
else
|
||||
st->pwr_down_mask &= ~(0x3 << (chan->channel * 2));
|
||||
|
||||
ret = ad5686_spi_write(st, AD5686_CMD_POWERDOWN_DAC, 0,
|
||||
st->pwr_down_mask & st->pwr_down_mode, 0);
|
||||
switch (st->chip_info->regmap_type) {
|
||||
case AD5683_REGMAP:
|
||||
shift = 13;
|
||||
ref_bit_msk = AD5683_REF_BIT_MSK;
|
||||
break;
|
||||
case AD5686_REGMAP:
|
||||
shift = 0;
|
||||
ref_bit_msk = 0;
|
||||
break;
|
||||
case AD5693_REGMAP:
|
||||
shift = 13;
|
||||
ref_bit_msk = AD5693_REF_BIT_MSK;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
val = ((st->pwr_down_mask & st->pwr_down_mode) << shift);
|
||||
if (!st->use_internal_vref)
|
||||
val |= ref_bit_msk;
|
||||
|
||||
ret = st->write(st, AD5686_CMD_POWERDOWN_DAC, 0, val);
|
||||
|
||||
return ret ? ret : len;
|
||||
}
|
||||
@ -206,7 +120,7 @@ static int ad5686_read_raw(struct iio_dev *indio_dev,
|
||||
switch (m) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
ret = ad5686_spi_read(st, chan->address);
|
||||
ret = st->read(st, chan->address);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
@ -221,10 +135,10 @@ static int ad5686_read_raw(struct iio_dev *indio_dev,
|
||||
}
|
||||
|
||||
static int ad5686_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val,
|
||||
int val2,
|
||||
long mask)
|
||||
struct iio_chan_spec const *chan,
|
||||
int val,
|
||||
int val2,
|
||||
long mask)
|
||||
{
|
||||
struct ad5686_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
@ -235,11 +149,10 @@ static int ad5686_write_raw(struct iio_dev *indio_dev,
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
ret = ad5686_spi_write(st,
|
||||
AD5686_CMD_WRITE_INPUT_N_UPDATE_N,
|
||||
chan->address,
|
||||
val,
|
||||
chan->scan_type.shift);
|
||||
ret = st->write(st,
|
||||
AD5686_CMD_WRITE_INPUT_N_UPDATE_N,
|
||||
chan->address,
|
||||
val << chan->scan_type.shift);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
break;
|
||||
default:
|
||||
@ -266,14 +179,14 @@ static const struct iio_chan_spec_ext_info ad5686_ext_info[] = {
|
||||
{ },
|
||||
};
|
||||
|
||||
#define AD5868_CHANNEL(chan, bits, _shift) { \
|
||||
#define AD5868_CHANNEL(chan, addr, bits, _shift) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.output = 1, \
|
||||
.channel = chan, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),\
|
||||
.address = AD5686_ADDR_DAC(chan), \
|
||||
.address = addr, \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = (bits), \
|
||||
@ -283,45 +196,191 @@ static const struct iio_chan_spec_ext_info ad5686_ext_info[] = {
|
||||
.ext_info = ad5686_ext_info, \
|
||||
}
|
||||
|
||||
#define DECLARE_AD5693_CHANNELS(name, bits, _shift) \
|
||||
static struct iio_chan_spec name[] = { \
|
||||
AD5868_CHANNEL(0, 0, bits, _shift), \
|
||||
}
|
||||
|
||||
#define DECLARE_AD5686_CHANNELS(name, bits, _shift) \
|
||||
static struct iio_chan_spec name[] = { \
|
||||
AD5868_CHANNEL(0, 1, bits, _shift), \
|
||||
AD5868_CHANNEL(1, 2, bits, _shift), \
|
||||
AD5868_CHANNEL(2, 4, bits, _shift), \
|
||||
AD5868_CHANNEL(3, 8, bits, _shift), \
|
||||
}
|
||||
|
||||
#define DECLARE_AD5676_CHANNELS(name, bits, _shift) \
|
||||
static struct iio_chan_spec name[] = { \
|
||||
AD5868_CHANNEL(0, 0, bits, _shift), \
|
||||
AD5868_CHANNEL(1, 1, bits, _shift), \
|
||||
AD5868_CHANNEL(2, 2, bits, _shift), \
|
||||
AD5868_CHANNEL(3, 3, bits, _shift), \
|
||||
AD5868_CHANNEL(4, 4, bits, _shift), \
|
||||
AD5868_CHANNEL(5, 5, bits, _shift), \
|
||||
AD5868_CHANNEL(6, 6, bits, _shift), \
|
||||
AD5868_CHANNEL(7, 7, bits, _shift), \
|
||||
}
|
||||
|
||||
DECLARE_AD5676_CHANNELS(ad5672_channels, 12, 4);
|
||||
DECLARE_AD5676_CHANNELS(ad5676_channels, 16, 0);
|
||||
DECLARE_AD5686_CHANNELS(ad5684_channels, 12, 4);
|
||||
DECLARE_AD5686_CHANNELS(ad5685r_channels, 14, 2);
|
||||
DECLARE_AD5686_CHANNELS(ad5686_channels, 16, 0);
|
||||
DECLARE_AD5693_CHANNELS(ad5693_channels, 16, 0);
|
||||
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_AD5684] = {
|
||||
.channel[0] = AD5868_CHANNEL(0, 12, 4),
|
||||
.channel[1] = AD5868_CHANNEL(1, 12, 4),
|
||||
.channel[2] = AD5868_CHANNEL(2, 12, 4),
|
||||
.channel[3] = AD5868_CHANNEL(3, 12, 4),
|
||||
[ID_AD5671R] = {
|
||||
.channels = ad5672_channels,
|
||||
.int_vref_mv = 2500,
|
||||
.num_channels = 8,
|
||||
.regmap_type = AD5686_REGMAP,
|
||||
},
|
||||
[ID_AD5685] = {
|
||||
.channel[0] = AD5868_CHANNEL(0, 14, 2),
|
||||
.channel[1] = AD5868_CHANNEL(1, 14, 2),
|
||||
.channel[2] = AD5868_CHANNEL(2, 14, 2),
|
||||
.channel[3] = AD5868_CHANNEL(3, 14, 2),
|
||||
[ID_AD5672R] = {
|
||||
.channels = ad5672_channels,
|
||||
.int_vref_mv = 2500,
|
||||
.num_channels = 8,
|
||||
.regmap_type = AD5686_REGMAP,
|
||||
},
|
||||
[ID_AD5675R] = {
|
||||
.channels = ad5676_channels,
|
||||
.int_vref_mv = 2500,
|
||||
.num_channels = 8,
|
||||
.regmap_type = AD5686_REGMAP,
|
||||
},
|
||||
[ID_AD5676] = {
|
||||
.channels = ad5676_channels,
|
||||
.num_channels = 8,
|
||||
.regmap_type = AD5686_REGMAP,
|
||||
},
|
||||
[ID_AD5676R] = {
|
||||
.channels = ad5676_channels,
|
||||
.int_vref_mv = 2500,
|
||||
.num_channels = 8,
|
||||
.regmap_type = AD5686_REGMAP,
|
||||
},
|
||||
[ID_AD5681R] = {
|
||||
.channels = ad5691r_channels,
|
||||
.int_vref_mv = 2500,
|
||||
.num_channels = 1,
|
||||
.regmap_type = AD5683_REGMAP,
|
||||
},
|
||||
[ID_AD5682R] = {
|
||||
.channels = ad5692r_channels,
|
||||
.int_vref_mv = 2500,
|
||||
.num_channels = 1,
|
||||
.regmap_type = AD5683_REGMAP,
|
||||
},
|
||||
[ID_AD5683] = {
|
||||
.channels = ad5693_channels,
|
||||
.num_channels = 1,
|
||||
.regmap_type = AD5683_REGMAP,
|
||||
},
|
||||
[ID_AD5683R] = {
|
||||
.channels = ad5693_channels,
|
||||
.int_vref_mv = 2500,
|
||||
.num_channels = 1,
|
||||
.regmap_type = AD5683_REGMAP,
|
||||
},
|
||||
[ID_AD5684] = {
|
||||
.channels = ad5684_channels,
|
||||
.num_channels = 4,
|
||||
.regmap_type = AD5686_REGMAP,
|
||||
},
|
||||
[ID_AD5684R] = {
|
||||
.channels = ad5684_channels,
|
||||
.int_vref_mv = 2500,
|
||||
.num_channels = 4,
|
||||
.regmap_type = AD5686_REGMAP,
|
||||
},
|
||||
[ID_AD5685R] = {
|
||||
.channels = ad5685r_channels,
|
||||
.int_vref_mv = 2500,
|
||||
.num_channels = 4,
|
||||
.regmap_type = AD5686_REGMAP,
|
||||
},
|
||||
[ID_AD5686] = {
|
||||
.channel[0] = AD5868_CHANNEL(0, 16, 0),
|
||||
.channel[1] = AD5868_CHANNEL(1, 16, 0),
|
||||
.channel[2] = AD5868_CHANNEL(2, 16, 0),
|
||||
.channel[3] = AD5868_CHANNEL(3, 16, 0),
|
||||
.channels = ad5686_channels,
|
||||
.num_channels = 4,
|
||||
.regmap_type = AD5686_REGMAP,
|
||||
},
|
||||
[ID_AD5686R] = {
|
||||
.channels = ad5686_channels,
|
||||
.int_vref_mv = 2500,
|
||||
.num_channels = 4,
|
||||
.regmap_type = AD5686_REGMAP,
|
||||
},
|
||||
[ID_AD5691R] = {
|
||||
.channels = ad5691r_channels,
|
||||
.int_vref_mv = 2500,
|
||||
.num_channels = 1,
|
||||
.regmap_type = AD5693_REGMAP,
|
||||
},
|
||||
[ID_AD5692R] = {
|
||||
.channels = ad5692r_channels,
|
||||
.int_vref_mv = 2500,
|
||||
.num_channels = 1,
|
||||
.regmap_type = AD5693_REGMAP,
|
||||
},
|
||||
[ID_AD5693] = {
|
||||
.channels = ad5693_channels,
|
||||
.num_channels = 1,
|
||||
.regmap_type = AD5693_REGMAP,
|
||||
},
|
||||
[ID_AD5693R] = {
|
||||
.channels = ad5693_channels,
|
||||
.int_vref_mv = 2500,
|
||||
.num_channels = 1,
|
||||
.regmap_type = AD5693_REGMAP,
|
||||
},
|
||||
[ID_AD5694] = {
|
||||
.channels = ad5684_channels,
|
||||
.num_channels = 4,
|
||||
.regmap_type = AD5686_REGMAP,
|
||||
},
|
||||
[ID_AD5694R] = {
|
||||
.channels = ad5684_channels,
|
||||
.int_vref_mv = 2500,
|
||||
.num_channels = 4,
|
||||
.regmap_type = AD5686_REGMAP,
|
||||
},
|
||||
[ID_AD5696] = {
|
||||
.channels = ad5686_channels,
|
||||
.num_channels = 4,
|
||||
.regmap_type = AD5686_REGMAP,
|
||||
},
|
||||
[ID_AD5696R] = {
|
||||
.channels = ad5686_channels,
|
||||
.int_vref_mv = 2500,
|
||||
.num_channels = 4,
|
||||
.regmap_type = AD5686_REGMAP,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
static int ad5686_probe(struct spi_device *spi)
|
||||
int ad5686_probe(struct device *dev,
|
||||
enum ad5686_supported_device_ids chip_type,
|
||||
const char *name, ad5686_write_func write,
|
||||
ad5686_read_func read)
|
||||
{
|
||||
struct ad5686_state *st;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret, voltage_uv = 0;
|
||||
unsigned int val, ref_bit_msk;
|
||||
u8 cmd;
|
||||
int ret, i, voltage_uv = 0;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
||||
indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
|
||||
if (indio_dev == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
dev_set_drvdata(dev, indio_dev);
|
||||
|
||||
st->reg = devm_regulator_get_optional(&spi->dev, "vcc");
|
||||
st->dev = dev;
|
||||
st->write = write;
|
||||
st->read = read;
|
||||
|
||||
st->reg = devm_regulator_get_optional(dev, "vcc");
|
||||
if (!IS_ERR(st->reg)) {
|
||||
ret = regulator_enable(st->reg);
|
||||
if (ret)
|
||||
@ -334,28 +393,47 @@ static int ad5686_probe(struct spi_device *spi)
|
||||
voltage_uv = ret;
|
||||
}
|
||||
|
||||
st->chip_info =
|
||||
&ad5686_chip_info_tbl[spi_get_device_id(spi)->driver_data];
|
||||
st->chip_info = &ad5686_chip_info_tbl[chip_type];
|
||||
|
||||
if (voltage_uv)
|
||||
st->vref_mv = voltage_uv / 1000;
|
||||
else
|
||||
st->vref_mv = st->chip_info->int_vref_mv;
|
||||
|
||||
st->spi = spi;
|
||||
|
||||
/* Set all the power down mode for all channels to 1K pulldown */
|
||||
st->pwr_down_mode = 0x55;
|
||||
for (i = 0; i < st->chip_info->num_channels; i++)
|
||||
st->pwr_down_mode |= (0x01 << (i * 2));
|
||||
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->name = spi_get_device_id(spi)->name;
|
||||
indio_dev->dev.parent = dev;
|
||||
indio_dev->name = name;
|
||||
indio_dev->info = &ad5686_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = st->chip_info->channel;
|
||||
indio_dev->num_channels = AD5686_DAC_CHANNELS;
|
||||
indio_dev->channels = st->chip_info->channels;
|
||||
indio_dev->num_channels = st->chip_info->num_channels;
|
||||
|
||||
ret = ad5686_spi_write(st, AD5686_CMD_INTERNAL_REFER_SETUP, 0,
|
||||
!!voltage_uv, 0);
|
||||
switch (st->chip_info->regmap_type) {
|
||||
case AD5683_REGMAP:
|
||||
cmd = AD5686_CMD_CONTROL_REG;
|
||||
ref_bit_msk = AD5683_REF_BIT_MSK;
|
||||
st->use_internal_vref = !voltage_uv;
|
||||
break;
|
||||
case AD5686_REGMAP:
|
||||
cmd = AD5686_CMD_INTERNAL_REFER_SETUP;
|
||||
ref_bit_msk = 0;
|
||||
break;
|
||||
case AD5693_REGMAP:
|
||||
cmd = AD5686_CMD_CONTROL_REG;
|
||||
ref_bit_msk = AD5693_REF_BIT_MSK;
|
||||
st->use_internal_vref = !voltage_uv;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
goto error_disable_reg;
|
||||
}
|
||||
|
||||
val = (voltage_uv | ref_bit_msk);
|
||||
|
||||
ret = st->write(st, cmd, 0, !!val);
|
||||
if (ret)
|
||||
goto error_disable_reg;
|
||||
|
||||
@ -370,10 +448,11 @@ error_disable_reg:
|
||||
regulator_disable(st->reg);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ad5686_probe);
|
||||
|
||||
static int ad5686_remove(struct spi_device *spi)
|
||||
int ad5686_remove(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct ad5686_state *st = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
@ -382,24 +461,7 @@ static int ad5686_remove(struct spi_device *spi)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id ad5686_id[] = {
|
||||
{"ad5684", ID_AD5684},
|
||||
{"ad5685", ID_AD5685},
|
||||
{"ad5686", ID_AD5686},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ad5686_id);
|
||||
|
||||
static struct spi_driver ad5686_driver = {
|
||||
.driver = {
|
||||
.name = "ad5686",
|
||||
},
|
||||
.probe = ad5686_probe,
|
||||
.remove = ad5686_remove,
|
||||
.id_table = ad5686_id,
|
||||
};
|
||||
module_spi_driver(ad5686_driver);
|
||||
EXPORT_SYMBOL_GPL(ad5686_remove);
|
||||
|
||||
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD5686/85/84 DAC");
|
||||
|
145
drivers/iio/dac/ad5686.h
Normal file
145
drivers/iio/dac/ad5686.h
Normal file
@ -0,0 +1,145 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* This file is part of AD5686 DAC driver
|
||||
*
|
||||
* Copyright 2018 Analog Devices Inc.
|
||||
*/
|
||||
|
||||
#ifndef __DRIVERS_IIO_DAC_AD5686_H__
|
||||
#define __DRIVERS_IIO_DAC_AD5686_H__
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/cache.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/kernel.h>
|
||||
|
||||
#define AD5683_DATA(x) ((x) << 4)
|
||||
#define AD5686_ADDR(x) ((x) << 16)
|
||||
#define AD5686_CMD(x) ((x) << 20)
|
||||
|
||||
#define AD5686_ADDR_DAC(chan) (0x1 << (chan))
|
||||
#define AD5686_ADDR_ALL_DAC 0xF
|
||||
|
||||
#define AD5686_CMD_NOOP 0x0
|
||||
#define AD5686_CMD_WRITE_INPUT_N 0x1
|
||||
#define AD5686_CMD_UPDATE_DAC_N 0x2
|
||||
#define AD5686_CMD_WRITE_INPUT_N_UPDATE_N 0x3
|
||||
#define AD5686_CMD_POWERDOWN_DAC 0x4
|
||||
#define AD5686_CMD_LDAC_MASK 0x5
|
||||
#define AD5686_CMD_RESET 0x6
|
||||
#define AD5686_CMD_INTERNAL_REFER_SETUP 0x7
|
||||
#define AD5686_CMD_DAISY_CHAIN_ENABLE 0x8
|
||||
#define AD5686_CMD_READBACK_ENABLE 0x9
|
||||
|
||||
#define AD5686_LDAC_PWRDN_NONE 0x0
|
||||
#define AD5686_LDAC_PWRDN_1K 0x1
|
||||
#define AD5686_LDAC_PWRDN_100K 0x2
|
||||
#define AD5686_LDAC_PWRDN_3STATE 0x3
|
||||
|
||||
#define AD5686_CMD_CONTROL_REG 0x4
|
||||
#define AD5686_CMD_READBACK_ENABLE_V2 0x5
|
||||
#define AD5683_REF_BIT_MSK BIT(12)
|
||||
#define AD5693_REF_BIT_MSK BIT(12)
|
||||
|
||||
/**
|
||||
* ad5686_supported_device_ids:
|
||||
*/
|
||||
enum ad5686_supported_device_ids {
|
||||
ID_AD5671R,
|
||||
ID_AD5672R,
|
||||
ID_AD5675R,
|
||||
ID_AD5676,
|
||||
ID_AD5676R,
|
||||
ID_AD5681R,
|
||||
ID_AD5682R,
|
||||
ID_AD5683,
|
||||
ID_AD5683R,
|
||||
ID_AD5684,
|
||||
ID_AD5684R,
|
||||
ID_AD5685R,
|
||||
ID_AD5686,
|
||||
ID_AD5686R,
|
||||
ID_AD5691R,
|
||||
ID_AD5692R,
|
||||
ID_AD5693,
|
||||
ID_AD5693R,
|
||||
ID_AD5694,
|
||||
ID_AD5694R,
|
||||
ID_AD5695R,
|
||||
ID_AD5696,
|
||||
ID_AD5696R,
|
||||
};
|
||||
|
||||
enum ad5686_regmap_type {
|
||||
AD5683_REGMAP,
|
||||
AD5686_REGMAP,
|
||||
AD5693_REGMAP
|
||||
};
|
||||
|
||||
struct ad5686_state;
|
||||
|
||||
typedef int (*ad5686_write_func)(struct ad5686_state *st,
|
||||
u8 cmd, u8 addr, u16 val);
|
||||
|
||||
typedef int (*ad5686_read_func)(struct ad5686_state *st, u8 addr);
|
||||
|
||||
/**
|
||||
* struct ad5686_chip_info - chip specific information
|
||||
* @int_vref_mv: AD5620/40/60: the internal reference voltage
|
||||
* @num_channels: number of channels
|
||||
* @channel: channel specification
|
||||
* @regmap_type: register map layout variant
|
||||
*/
|
||||
|
||||
struct ad5686_chip_info {
|
||||
u16 int_vref_mv;
|
||||
unsigned int num_channels;
|
||||
struct iio_chan_spec *channels;
|
||||
enum ad5686_regmap_type regmap_type;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ad5446_state - driver instance specific data
|
||||
* @spi: spi_device
|
||||
* @chip_info: chip model specific constants, available modes etc
|
||||
* @reg: supply regulator
|
||||
* @vref_mv: actual reference voltage used
|
||||
* @pwr_down_mask: power down mask
|
||||
* @pwr_down_mode: current power down mode
|
||||
* @use_internal_vref: set to true if the internal reference voltage is used
|
||||
* @data: spi transfer buffers
|
||||
*/
|
||||
|
||||
struct ad5686_state {
|
||||
struct device *dev;
|
||||
const struct ad5686_chip_info *chip_info;
|
||||
struct regulator *reg;
|
||||
unsigned short vref_mv;
|
||||
unsigned int pwr_down_mask;
|
||||
unsigned int pwr_down_mode;
|
||||
ad5686_write_func write;
|
||||
ad5686_read_func read;
|
||||
bool use_internal_vref;
|
||||
|
||||
/*
|
||||
* DMA (thus cache coherency maintenance) requires the
|
||||
* transfer buffers to live in their own cache lines.
|
||||
*/
|
||||
|
||||
union {
|
||||
__be32 d32;
|
||||
__be16 d16;
|
||||
u8 d8[4];
|
||||
} data[3] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
|
||||
int ad5686_probe(struct device *dev,
|
||||
enum ad5686_supported_device_ids chip_type,
|
||||
const char *name, ad5686_write_func write,
|
||||
ad5686_read_func read);
|
||||
|
||||
int ad5686_remove(struct device *dev);
|
||||
|
||||
|
||||
#endif /* __DRIVERS_IIO_DAC_AD5686_H__ */
|
102
drivers/iio/dac/ad5696-i2c.c
Normal file
102
drivers/iio/dac/ad5696-i2c.c
Normal file
@ -0,0 +1,102 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* AD5671R, AD5675R, AD5691R, AD5692R, AD5693, AD5693R,
|
||||
* AD5694, AD5694R, AD5695R, AD5696, AD5696R
|
||||
* Digital to analog converters driver
|
||||
*
|
||||
* Copyright 2018 Analog Devices Inc.
|
||||
*/
|
||||
|
||||
#include "ad5686.h"
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
|
||||
static int ad5686_i2c_read(struct ad5686_state *st, u8 addr)
|
||||
{
|
||||
struct i2c_client *i2c = to_i2c_client(st->dev);
|
||||
struct i2c_msg msg[2] = {
|
||||
{
|
||||
.addr = i2c->addr,
|
||||
.flags = i2c->flags,
|
||||
.len = 3,
|
||||
.buf = &st->data[0].d8[1],
|
||||
},
|
||||
{
|
||||
.addr = i2c->addr,
|
||||
.flags = i2c->flags | I2C_M_RD,
|
||||
.len = 2,
|
||||
.buf = (char *)&st->data[0].d16,
|
||||
},
|
||||
};
|
||||
int ret;
|
||||
|
||||
st->data[0].d32 = cpu_to_be32(AD5686_CMD(AD5686_CMD_NOOP) |
|
||||
AD5686_ADDR(addr) |
|
||||
0x00);
|
||||
|
||||
ret = i2c_transfer(i2c->adapter, msg, 2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return be16_to_cpu(st->data[0].d16);
|
||||
}
|
||||
|
||||
static int ad5686_i2c_write(struct ad5686_state *st,
|
||||
u8 cmd, u8 addr, u16 val)
|
||||
{
|
||||
struct i2c_client *i2c = to_i2c_client(st->dev);
|
||||
int ret;
|
||||
|
||||
st->data[0].d32 = cpu_to_be32(AD5686_CMD(cmd) | AD5686_ADDR(addr)
|
||||
| val);
|
||||
|
||||
ret = i2c_master_send(i2c, &st->data[0].d8[1], 3);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return (ret != 3) ? -EIO : 0;
|
||||
}
|
||||
|
||||
static int ad5686_i2c_probe(struct i2c_client *i2c,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
return ad5686_probe(&i2c->dev, id->driver_data, id->name,
|
||||
ad5686_i2c_write, ad5686_i2c_read);
|
||||
}
|
||||
|
||||
static int ad5686_i2c_remove(struct i2c_client *i2c)
|
||||
{
|
||||
return ad5686_remove(&i2c->dev);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id ad5686_i2c_id[] = {
|
||||
{"ad5671r", ID_AD5671R},
|
||||
{"ad5675r", ID_AD5675R},
|
||||
{"ad5691r", ID_AD5691R},
|
||||
{"ad5692r", ID_AD5692R},
|
||||
{"ad5693", ID_AD5693},
|
||||
{"ad5693r", ID_AD5693R},
|
||||
{"ad5694", ID_AD5694},
|
||||
{"ad5694r", ID_AD5694R},
|
||||
{"ad5695r", ID_AD5695R},
|
||||
{"ad5696", ID_AD5696},
|
||||
{"ad5696r", ID_AD5696R},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ad5686_i2c_id);
|
||||
|
||||
static struct i2c_driver ad5686_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "ad5696",
|
||||
},
|
||||
.probe = ad5686_i2c_probe,
|
||||
.remove = ad5686_i2c_remove,
|
||||
.id_table = ad5686_i2c_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(ad5686_i2c_driver);
|
||||
|
||||
MODULE_AUTHOR("Stefan Popa <stefan.popa@analog.com>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD5686 and similar multi-channel DACs");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -2,6 +2,7 @@
|
||||
* LTC2632 Digital to analog convertors spi driver
|
||||
*
|
||||
* Copyright 2017 Maxime Roussin-Bélanger
|
||||
* expanded by Silvan Murer <silvan.murer@gmail.com>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
@ -10,6 +11,7 @@
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#define LTC2632_DAC_CHANNELS 2
|
||||
|
||||
@ -28,7 +30,7 @@
|
||||
/**
|
||||
* struct ltc2632_chip_info - chip specific information
|
||||
* @channels: channel spec for the DAC
|
||||
* @vref_mv: reference voltage
|
||||
* @vref_mv: internal reference voltage
|
||||
*/
|
||||
struct ltc2632_chip_info {
|
||||
const struct iio_chan_spec *channels;
|
||||
@ -39,10 +41,14 @@ struct ltc2632_chip_info {
|
||||
* struct ltc2632_state - driver instance specific data
|
||||
* @spi_dev: pointer to the spi_device struct
|
||||
* @powerdown_cache_mask used to show current channel powerdown state
|
||||
* @vref_mv used reference voltage (internal or external)
|
||||
* @vref_reg regulator for the reference voltage
|
||||
*/
|
||||
struct ltc2632_state {
|
||||
struct spi_device *spi_dev;
|
||||
unsigned int powerdown_cache_mask;
|
||||
int vref_mv;
|
||||
struct regulator *vref_reg;
|
||||
};
|
||||
|
||||
enum ltc2632_supported_device_ids {
|
||||
@ -90,7 +96,7 @@ static int ltc2632_read_raw(struct iio_dev *indio_dev,
|
||||
|
||||
switch (m) {
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = chip_info->vref_mv;
|
||||
*val = st->vref_mv;
|
||||
*val2 = chan->scan_type.realbits;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
}
|
||||
@ -246,6 +252,45 @@ static int ltc2632_probe(struct spi_device *spi)
|
||||
chip_info = (struct ltc2632_chip_info *)
|
||||
spi_get_device_id(spi)->driver_data;
|
||||
|
||||
st->vref_reg = devm_regulator_get_optional(&spi->dev, "vref");
|
||||
if (PTR_ERR(st->vref_reg) == -ENODEV) {
|
||||
/* use internal reference voltage */
|
||||
st->vref_reg = NULL;
|
||||
st->vref_mv = chip_info->vref_mv;
|
||||
|
||||
ret = ltc2632_spi_write(spi, LTC2632_CMD_INTERNAL_REFER,
|
||||
0, 0, 0);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev,
|
||||
"Set internal reference command failed, %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
} else if (IS_ERR(st->vref_reg)) {
|
||||
dev_err(&spi->dev,
|
||||
"Error getting voltage reference regulator\n");
|
||||
return PTR_ERR(st->vref_reg);
|
||||
} else {
|
||||
/* use external reference voltage */
|
||||
ret = regulator_enable(st->vref_reg);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev,
|
||||
"enable reference regulator failed, %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
st->vref_mv = regulator_get_voltage(st->vref_reg) / 1000;
|
||||
|
||||
ret = ltc2632_spi_write(spi, LTC2632_CMD_EXTERNAL_REFER,
|
||||
0, 0, 0);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev,
|
||||
"Set external reference command failed, %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->name = dev_of_node(&spi->dev) ? dev_of_node(&spi->dev)->name
|
||||
: spi_get_device_id(spi)->name;
|
||||
@ -254,14 +299,20 @@ static int ltc2632_probe(struct spi_device *spi)
|
||||
indio_dev->channels = chip_info->channels;
|
||||
indio_dev->num_channels = LTC2632_DAC_CHANNELS;
|
||||
|
||||
ret = ltc2632_spi_write(spi, LTC2632_CMD_INTERNAL_REFER, 0, 0, 0);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev,
|
||||
"Set internal reference command failed, %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
return iio_device_register(indio_dev);
|
||||
}
|
||||
|
||||
return devm_iio_device_register(&spi->dev, indio_dev);
|
||||
static int ltc2632_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct ltc2632_state *st = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
if (st->vref_reg)
|
||||
regulator_disable(st->vref_reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id ltc2632_id[] = {
|
||||
@ -275,15 +326,6 @@ static const struct spi_device_id ltc2632_id[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ltc2632_id);
|
||||
|
||||
static struct spi_driver ltc2632_driver = {
|
||||
.driver = {
|
||||
.name = "ltc2632",
|
||||
},
|
||||
.probe = ltc2632_probe,
|
||||
.id_table = ltc2632_id,
|
||||
};
|
||||
module_spi_driver(ltc2632_driver);
|
||||
|
||||
static const struct of_device_id ltc2632_of_match[] = {
|
||||
{
|
||||
.compatible = "lltc,ltc2632-l12",
|
||||
@ -308,6 +350,17 @@ static const struct of_device_id ltc2632_of_match[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ltc2632_of_match);
|
||||
|
||||
static struct spi_driver ltc2632_driver = {
|
||||
.driver = {
|
||||
.name = "ltc2632",
|
||||
.of_match_table = of_match_ptr(ltc2632_of_match),
|
||||
},
|
||||
.probe = ltc2632_probe,
|
||||
.remove = ltc2632_remove,
|
||||
.id_table = ltc2632_id,
|
||||
};
|
||||
module_spi_driver(ltc2632_driver);
|
||||
|
||||
MODULE_AUTHOR("Maxime Roussin-Belanger <maxime.roussinbelanger@gmail.com>");
|
||||
MODULE_DESCRIPTION("LTC2632 DAC SPI driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
439
drivers/iio/dac/ti-dac5571.c
Normal file
439
drivers/iio/dac/ti-dac5571.c
Normal file
@ -0,0 +1,439 @@
|
||||
/*
|
||||
* ti-dac5571.c - Texas Instruments 8/10/12-bit 1/4-channel DAC driver
|
||||
*
|
||||
* Copyright (C) 2018 Prevas A/S
|
||||
*
|
||||
* http://www.ti.com/lit/ds/symlink/dac5571.pdf
|
||||
* http://www.ti.com/lit/ds/symlink/dac6571.pdf
|
||||
* http://www.ti.com/lit/ds/symlink/dac7571.pdf
|
||||
* http://www.ti.com/lit/ds/symlink/dac5574.pdf
|
||||
* http://www.ti.com/lit/ds/symlink/dac6574.pdf
|
||||
* http://www.ti.com/lit/ds/symlink/dac7574.pdf
|
||||
* http://www.ti.com/lit/ds/symlink/dac5573.pdf
|
||||
* http://www.ti.com/lit/ds/symlink/dac6573.pdf
|
||||
* http://www.ti.com/lit/ds/symlink/dac7573.pdf
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License (version 2) as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
enum chip_id {
|
||||
single_8bit, single_10bit, single_12bit,
|
||||
quad_8bit, quad_10bit, quad_12bit
|
||||
};
|
||||
|
||||
struct dac5571_spec {
|
||||
u8 num_channels;
|
||||
u8 resolution;
|
||||
};
|
||||
|
||||
static const struct dac5571_spec dac5571_spec[] = {
|
||||
[single_8bit] = {.num_channels = 1, .resolution = 8},
|
||||
[single_10bit] = {.num_channels = 1, .resolution = 10},
|
||||
[single_12bit] = {.num_channels = 1, .resolution = 12},
|
||||
[quad_8bit] = {.num_channels = 4, .resolution = 8},
|
||||
[quad_10bit] = {.num_channels = 4, .resolution = 10},
|
||||
[quad_12bit] = {.num_channels = 4, .resolution = 12},
|
||||
};
|
||||
|
||||
struct dac5571_data {
|
||||
struct i2c_client *client;
|
||||
int id;
|
||||
struct mutex lock;
|
||||
struct regulator *vref;
|
||||
u16 val[4];
|
||||
bool powerdown;
|
||||
u8 powerdown_mode;
|
||||
struct dac5571_spec const *spec;
|
||||
int (*dac5571_cmd)(struct dac5571_data *data, int channel, u16 val);
|
||||
int (*dac5571_pwrdwn)(struct dac5571_data *data, int channel, u8 pwrdwn);
|
||||
u8 buf[3] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
#define DAC5571_POWERDOWN(mode) ((mode) + 1)
|
||||
#define DAC5571_POWERDOWN_FLAG BIT(0)
|
||||
#define DAC5571_CHANNEL_SELECT 1
|
||||
#define DAC5571_LOADMODE_DIRECT BIT(4)
|
||||
#define DAC5571_SINGLE_PWRDWN_BITS 4
|
||||
#define DAC5571_QUAD_PWRDWN_BITS 6
|
||||
|
||||
static int dac5571_cmd_single(struct dac5571_data *data, int channel, u16 val)
|
||||
{
|
||||
unsigned int shift;
|
||||
|
||||
shift = 12 - data->spec->resolution;
|
||||
data->buf[1] = val << shift;
|
||||
data->buf[0] = val >> (8 - shift);
|
||||
|
||||
if (i2c_master_send(data->client, data->buf, 2) != 2)
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dac5571_cmd_quad(struct dac5571_data *data, int channel, u16 val)
|
||||
{
|
||||
unsigned int shift;
|
||||
|
||||
shift = 16 - data->spec->resolution;
|
||||
data->buf[2] = val << shift;
|
||||
data->buf[1] = (val >> (8 - shift));
|
||||
data->buf[0] = (channel << DAC5571_CHANNEL_SELECT) |
|
||||
DAC5571_LOADMODE_DIRECT;
|
||||
|
||||
if (i2c_master_send(data->client, data->buf, 3) != 3)
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if (i2c_master_send(data->client, data->buf, 2) != 2)
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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) |
|
||||
DAC5571_LOADMODE_DIRECT | DAC5571_POWERDOWN_FLAG;
|
||||
|
||||
if (i2c_master_send(data->client, data->buf, 3) != 3)
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *const dac5571_powerdown_modes[] = {
|
||||
"1kohm_to_gnd", "100kohm_to_gnd", "three_state",
|
||||
};
|
||||
|
||||
static int dac5571_get_powerdown_mode(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan)
|
||||
{
|
||||
struct dac5571_data *data = iio_priv(indio_dev);
|
||||
|
||||
return data->powerdown_mode;
|
||||
}
|
||||
|
||||
static int dac5571_set_powerdown_mode(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
unsigned int mode)
|
||||
{
|
||||
struct dac5571_data *data = iio_priv(indio_dev);
|
||||
int ret = 0;
|
||||
|
||||
if (data->powerdown_mode == mode)
|
||||
return 0;
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
if (data->powerdown) {
|
||||
ret = data->dac5571_pwrdwn(data, chan->channel,
|
||||
DAC5571_POWERDOWN(mode));
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
data->powerdown_mode = mode;
|
||||
|
||||
out:
|
||||
mutex_unlock(&data->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct iio_enum dac5571_powerdown_mode = {
|
||||
.items = dac5571_powerdown_modes,
|
||||
.num_items = ARRAY_SIZE(dac5571_powerdown_modes),
|
||||
.get = dac5571_get_powerdown_mode,
|
||||
.set = dac5571_set_powerdown_mode,
|
||||
};
|
||||
|
||||
static ssize_t dac5571_read_powerdown(struct iio_dev *indio_dev,
|
||||
uintptr_t private,
|
||||
const struct iio_chan_spec *chan,
|
||||
char *buf)
|
||||
{
|
||||
struct dac5571_data *data = iio_priv(indio_dev);
|
||||
|
||||
return sprintf(buf, "%d\n", data->powerdown);
|
||||
}
|
||||
|
||||
static ssize_t dac5571_write_powerdown(struct iio_dev *indio_dev,
|
||||
uintptr_t private,
|
||||
const struct iio_chan_spec *chan,
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
struct dac5571_data *data = iio_priv(indio_dev);
|
||||
bool powerdown;
|
||||
int ret;
|
||||
|
||||
ret = strtobool(buf, &powerdown);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (data->powerdown == powerdown)
|
||||
return len;
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
if (powerdown)
|
||||
ret = data->dac5571_pwrdwn(data, chan->channel,
|
||||
DAC5571_POWERDOWN(data->powerdown_mode));
|
||||
else
|
||||
ret = data->dac5571_cmd(data, chan->channel, data->val[0]);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
data->powerdown = powerdown;
|
||||
|
||||
out:
|
||||
mutex_unlock(&data->lock);
|
||||
|
||||
return ret ? ret : len;
|
||||
}
|
||||
|
||||
|
||||
static const struct iio_chan_spec_ext_info dac5571_ext_info[] = {
|
||||
{
|
||||
.name = "powerdown",
|
||||
.read = dac5571_read_powerdown,
|
||||
.write = dac5571_write_powerdown,
|
||||
.shared = IIO_SHARED_BY_TYPE,
|
||||
},
|
||||
IIO_ENUM("powerdown_mode", IIO_SHARED_BY_TYPE, &dac5571_powerdown_mode),
|
||||
IIO_ENUM_AVAILABLE("powerdown_mode", &dac5571_powerdown_mode),
|
||||
{},
|
||||
};
|
||||
|
||||
#define dac5571_CHANNEL(chan, name) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.channel = (chan), \
|
||||
.address = (chan), \
|
||||
.indexed = true, \
|
||||
.output = true, \
|
||||
.datasheet_name = name, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.ext_info = dac5571_ext_info, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec dac5571_channels[] = {
|
||||
dac5571_CHANNEL(0, "A"),
|
||||
dac5571_CHANNEL(1, "B"),
|
||||
dac5571_CHANNEL(2, "C"),
|
||||
dac5571_CHANNEL(3, "D"),
|
||||
};
|
||||
|
||||
static int dac5571_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct dac5571_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
*val = data->val[chan->channel];
|
||||
return IIO_VAL_INT;
|
||||
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
ret = regulator_get_voltage(data->vref);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = ret / 1000;
|
||||
*val2 = data->spec->resolution;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int dac5571_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
struct dac5571_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (data->val[chan->channel] == val)
|
||||
return 0;
|
||||
|
||||
if (val >= (1 << data->spec->resolution) || val < 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (data->powerdown)
|
||||
return -EBUSY;
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
ret = data->dac5571_cmd(data, chan->channel, val);
|
||||
if (ret == 0)
|
||||
data->val[chan->channel] = val;
|
||||
mutex_unlock(&data->lock);
|
||||
return ret;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int dac5571_write_raw_get_fmt(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
long mask)
|
||||
{
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
static const struct iio_info dac5571_info = {
|
||||
.read_raw = dac5571_read_raw,
|
||||
.write_raw = dac5571_write_raw,
|
||||
.write_raw_get_fmt = dac5571_write_raw_get_fmt,
|
||||
};
|
||||
|
||||
static int dac5571_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
const struct dac5571_spec *spec;
|
||||
struct dac5571_data *data;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret, i;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
data = iio_priv(indio_dev);
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
data->client = client;
|
||||
|
||||
indio_dev->dev.parent = dev;
|
||||
indio_dev->dev.of_node = client->dev.of_node;
|
||||
indio_dev->info = &dac5571_info;
|
||||
indio_dev->name = id->name;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = dac5571_channels;
|
||||
|
||||
spec = &dac5571_spec[id->driver_data];
|
||||
indio_dev->num_channels = spec->num_channels;
|
||||
data->spec = spec;
|
||||
|
||||
data->vref = devm_regulator_get(dev, "vref");
|
||||
if (IS_ERR(data->vref))
|
||||
return PTR_ERR(data->vref);
|
||||
|
||||
ret = regulator_enable(data->vref);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
mutex_init(&data->lock);
|
||||
|
||||
switch (spec->num_channels) {
|
||||
case 1:
|
||||
data->dac5571_cmd = dac5571_cmd_single;
|
||||
data->dac5571_pwrdwn = dac5571_pwrdwn_single;
|
||||
break;
|
||||
case 4:
|
||||
data->dac5571_cmd = dac5571_cmd_quad;
|
||||
data->dac5571_pwrdwn = dac5571_pwrdwn_quad;
|
||||
break;
|
||||
default:
|
||||
goto err;
|
||||
}
|
||||
|
||||
for (i = 0; i < spec->num_channels; i++) {
|
||||
ret = data->dac5571_cmd(data, i, 0);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to initialize channel %d to 0\n", i);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
regulator_disable(data->vref);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dac5571_remove(struct i2c_client *i2c)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(i2c);
|
||||
struct dac5571_data *data = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
regulator_disable(data->vref);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id dac5571_of_id[] = {
|
||||
{.compatible = "ti,dac5571"},
|
||||
{.compatible = "ti,dac6571"},
|
||||
{.compatible = "ti,dac7571"},
|
||||
{.compatible = "ti,dac5574"},
|
||||
{.compatible = "ti,dac6574"},
|
||||
{.compatible = "ti,dac7574"},
|
||||
{.compatible = "ti,dac5573"},
|
||||
{.compatible = "ti,dac6573"},
|
||||
{.compatible = "ti,dac7573"},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, dac5571_of_id);
|
||||
#endif
|
||||
|
||||
static const struct i2c_device_id dac5571_id[] = {
|
||||
{"dac5571", single_8bit},
|
||||
{"dac6571", single_10bit},
|
||||
{"dac7571", single_12bit},
|
||||
{"dac5574", quad_8bit},
|
||||
{"dac6574", quad_10bit},
|
||||
{"dac7574", quad_12bit},
|
||||
{"dac5573", quad_8bit},
|
||||
{"dac6573", quad_10bit},
|
||||
{"dac7573", quad_12bit},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, dac5571_id);
|
||||
|
||||
static struct i2c_driver dac5571_driver = {
|
||||
.driver = {
|
||||
.name = "ti-dac5571",
|
||||
},
|
||||
.probe = dac5571_probe,
|
||||
.remove = dac5571_remove,
|
||||
.id_table = dac5571_id,
|
||||
};
|
||||
module_i2c_driver(dac5571_driver);
|
||||
|
||||
MODULE_AUTHOR("Sean Nyekjaer <sean.nyekjaer@prevas.dk>");
|
||||
MODULE_DESCRIPTION("Texas Instruments 8/10/12-bit 1/4-channel DAC driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -24,6 +24,7 @@
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include "inv_mpu_iio.h"
|
||||
|
||||
/*
|
||||
@ -52,6 +53,7 @@ static const struct inv_mpu6050_reg_map reg_set_6500 = {
|
||||
.raw_accl = INV_MPU6050_REG_RAW_ACCEL,
|
||||
.temperature = INV_MPU6050_REG_TEMPERATURE,
|
||||
.int_enable = INV_MPU6050_REG_INT_ENABLE,
|
||||
.int_status = INV_MPU6050_REG_INT_STATUS,
|
||||
.pwr_mgmt_1 = INV_MPU6050_REG_PWR_MGMT_1,
|
||||
.pwr_mgmt_2 = INV_MPU6050_REG_PWR_MGMT_2,
|
||||
.int_pin_cfg = INV_MPU6050_REG_INT_PIN_CFG,
|
||||
@ -86,6 +88,7 @@ static const struct inv_mpu6050_chip_config chip_config_6050 = {
|
||||
.gyro_fifo_enable = false,
|
||||
.accl_fifo_enable = false,
|
||||
.accl_fs = INV_MPU6050_FS_02G,
|
||||
.user_ctrl = 0,
|
||||
};
|
||||
|
||||
/* Indexed by enum inv_devices */
|
||||
@ -120,6 +123,12 @@ static const struct inv_mpu6050_hw hw_info[] = {
|
||||
.reg = ®_set_6500,
|
||||
.config = &chip_config_6050,
|
||||
},
|
||||
{
|
||||
.whoami = INV_MPU9255_WHOAMI_VALUE,
|
||||
.name = "MPU9255",
|
||||
.reg = ®_set_6500,
|
||||
.config = &chip_config_6050,
|
||||
},
|
||||
{
|
||||
.whoami = INV_ICM20608_WHOAMI_VALUE,
|
||||
.name = "ICM20608",
|
||||
@ -168,7 +177,7 @@ int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en, u32 mask)
|
||||
return result;
|
||||
|
||||
if (en) {
|
||||
/* Wait for output stabilize */
|
||||
/* Wait for output to stabilize */
|
||||
msleep(INV_MPU6050_TEMP_UP_TIME);
|
||||
if (mask == INV_MPU6050_BIT_PWR_GYRO_STBY) {
|
||||
/* switch internal clock to PLL */
|
||||
@ -185,26 +194,29 @@ int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en, u32 mask)
|
||||
|
||||
int inv_mpu6050_set_power_itg(struct inv_mpu6050_state *st, bool power_on)
|
||||
{
|
||||
int result = 0;
|
||||
int result;
|
||||
|
||||
if (power_on) {
|
||||
if (!st->powerup_count)
|
||||
if (!st->powerup_count) {
|
||||
result = regmap_write(st->map, st->reg->pwr_mgmt_1, 0);
|
||||
if (!result)
|
||||
st->powerup_count++;
|
||||
if (result)
|
||||
return result;
|
||||
usleep_range(INV_MPU6050_REG_UP_TIME_MIN,
|
||||
INV_MPU6050_REG_UP_TIME_MAX);
|
||||
}
|
||||
st->powerup_count++;
|
||||
} else {
|
||||
st->powerup_count--;
|
||||
if (!st->powerup_count)
|
||||
if (st->powerup_count == 1) {
|
||||
result = regmap_write(st->map, st->reg->pwr_mgmt_1,
|
||||
INV_MPU6050_BIT_SLEEP);
|
||||
if (result)
|
||||
return result;
|
||||
}
|
||||
st->powerup_count--;
|
||||
}
|
||||
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
if (power_on)
|
||||
usleep_range(INV_MPU6050_REG_UP_TIME_MIN,
|
||||
INV_MPU6050_REG_UP_TIME_MAX);
|
||||
dev_dbg(regmap_get_device(st->map), "set power %d, count=%u\n",
|
||||
power_on, st->powerup_count);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -262,26 +274,33 @@ static int inv_mpu6050_init_config(struct iio_dev *indio_dev)
|
||||
d = (INV_MPU6050_FSR_2000DPS << INV_MPU6050_GYRO_CONFIG_FSR_SHIFT);
|
||||
result = regmap_write(st->map, st->reg->gyro_config, d);
|
||||
if (result)
|
||||
return result;
|
||||
goto error_power_off;
|
||||
|
||||
result = inv_mpu6050_set_lpf_regs(st, INV_MPU6050_FILTER_20HZ);
|
||||
if (result)
|
||||
return result;
|
||||
goto error_power_off;
|
||||
|
||||
d = INV_MPU6050_ONE_K_HZ / INV_MPU6050_INIT_FIFO_RATE - 1;
|
||||
result = regmap_write(st->map, st->reg->sample_rate_div, d);
|
||||
if (result)
|
||||
return result;
|
||||
goto error_power_off;
|
||||
|
||||
d = (INV_MPU6050_FS_02G << INV_MPU6050_ACCL_CONFIG_FSR_SHIFT);
|
||||
result = regmap_write(st->map, st->reg->accl_config, d);
|
||||
if (result)
|
||||
goto error_power_off;
|
||||
|
||||
result = regmap_write(st->map, st->reg->int_pin_cfg, st->irq_mask);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
memcpy(&st->chip_config, hw_info[st->chip_type].config,
|
||||
sizeof(struct inv_mpu6050_chip_config));
|
||||
result = inv_mpu6050_set_power_itg(st, false);
|
||||
|
||||
return inv_mpu6050_set_power_itg(st, false);
|
||||
|
||||
error_power_off:
|
||||
inv_mpu6050_set_power_itg(st, false);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -314,6 +333,65 @@ static int inv_mpu6050_sensor_show(struct inv_mpu6050_state *st, int reg,
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
static int inv_mpu6050_read_channel_data(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val)
|
||||
{
|
||||
struct inv_mpu6050_state *st = iio_priv(indio_dev);
|
||||
int result;
|
||||
int ret;
|
||||
|
||||
result = inv_mpu6050_set_power_itg(st, true);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
switch (chan->type) {
|
||||
case IIO_ANGL_VEL:
|
||||
result = inv_mpu6050_switch_engine(st, true,
|
||||
INV_MPU6050_BIT_PWR_GYRO_STBY);
|
||||
if (result)
|
||||
goto error_power_off;
|
||||
ret = inv_mpu6050_sensor_show(st, st->reg->raw_gyro,
|
||||
chan->channel2, val);
|
||||
result = inv_mpu6050_switch_engine(st, false,
|
||||
INV_MPU6050_BIT_PWR_GYRO_STBY);
|
||||
if (result)
|
||||
goto error_power_off;
|
||||
break;
|
||||
case IIO_ACCEL:
|
||||
result = inv_mpu6050_switch_engine(st, true,
|
||||
INV_MPU6050_BIT_PWR_ACCL_STBY);
|
||||
if (result)
|
||||
goto error_power_off;
|
||||
ret = inv_mpu6050_sensor_show(st, st->reg->raw_accl,
|
||||
chan->channel2, val);
|
||||
result = inv_mpu6050_switch_engine(st, false,
|
||||
INV_MPU6050_BIT_PWR_ACCL_STBY);
|
||||
if (result)
|
||||
goto error_power_off;
|
||||
break;
|
||||
case IIO_TEMP:
|
||||
/* wait for stablization */
|
||||
msleep(INV_MPU6050_SENSOR_UP_TIME);
|
||||
ret = inv_mpu6050_sensor_show(st, st->reg->temperature,
|
||||
IIO_MOD_X, val);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
result = inv_mpu6050_set_power_itg(st, false);
|
||||
if (result)
|
||||
goto error_power_off;
|
||||
|
||||
return ret;
|
||||
|
||||
error_power_off:
|
||||
inv_mpu6050_set_power_itg(st, false);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int
|
||||
inv_mpu6050_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
@ -324,63 +402,14 @@ inv_mpu6050_read_raw(struct iio_dev *indio_dev,
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
{
|
||||
int result;
|
||||
|
||||
ret = IIO_VAL_INT;
|
||||
ret = iio_device_claim_direct_mode(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
mutex_lock(&st->lock);
|
||||
result = iio_device_claim_direct_mode(indio_dev);
|
||||
if (result)
|
||||
goto error_read_raw_unlock;
|
||||
result = inv_mpu6050_set_power_itg(st, true);
|
||||
if (result)
|
||||
goto error_read_raw_release;
|
||||
switch (chan->type) {
|
||||
case IIO_ANGL_VEL:
|
||||
result = inv_mpu6050_switch_engine(st, true,
|
||||
INV_MPU6050_BIT_PWR_GYRO_STBY);
|
||||
if (result)
|
||||
goto error_read_raw_power_off;
|
||||
ret = inv_mpu6050_sensor_show(st, st->reg->raw_gyro,
|
||||
chan->channel2, val);
|
||||
result = inv_mpu6050_switch_engine(st, false,
|
||||
INV_MPU6050_BIT_PWR_GYRO_STBY);
|
||||
if (result)
|
||||
goto error_read_raw_power_off;
|
||||
break;
|
||||
case IIO_ACCEL:
|
||||
result = inv_mpu6050_switch_engine(st, true,
|
||||
INV_MPU6050_BIT_PWR_ACCL_STBY);
|
||||
if (result)
|
||||
goto error_read_raw_power_off;
|
||||
ret = inv_mpu6050_sensor_show(st, st->reg->raw_accl,
|
||||
chan->channel2, val);
|
||||
result = inv_mpu6050_switch_engine(st, false,
|
||||
INV_MPU6050_BIT_PWR_ACCL_STBY);
|
||||
if (result)
|
||||
goto error_read_raw_power_off;
|
||||
break;
|
||||
case IIO_TEMP:
|
||||
/* wait for stablization */
|
||||
msleep(INV_MPU6050_SENSOR_UP_TIME);
|
||||
ret = inv_mpu6050_sensor_show(st, st->reg->temperature,
|
||||
IIO_MOD_X, val);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
error_read_raw_power_off:
|
||||
result |= inv_mpu6050_set_power_itg(st, false);
|
||||
error_read_raw_release:
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
error_read_raw_unlock:
|
||||
ret = inv_mpu6050_read_channel_data(indio_dev, chan, val);
|
||||
mutex_unlock(&st->lock);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
return ret;
|
||||
}
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
switch (chan->type) {
|
||||
case IIO_ANGL_VEL:
|
||||
@ -502,17 +531,18 @@ static int inv_mpu6050_write_raw(struct iio_dev *indio_dev,
|
||||
struct inv_mpu6050_state *st = iio_priv(indio_dev);
|
||||
int result;
|
||||
|
||||
mutex_lock(&st->lock);
|
||||
/*
|
||||
* we should only update scale when the chip is disabled, i.e.
|
||||
* not running
|
||||
*/
|
||||
result = iio_device_claim_direct_mode(indio_dev);
|
||||
if (result)
|
||||
goto error_write_raw_unlock;
|
||||
return result;
|
||||
|
||||
mutex_lock(&st->lock);
|
||||
result = inv_mpu6050_set_power_itg(st, true);
|
||||
if (result)
|
||||
goto error_write_raw_release;
|
||||
goto error_write_raw_unlock;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
@ -551,10 +581,9 @@ static int inv_mpu6050_write_raw(struct iio_dev *indio_dev,
|
||||
}
|
||||
|
||||
result |= inv_mpu6050_set_power_itg(st, false);
|
||||
error_write_raw_release:
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
error_write_raw_unlock:
|
||||
mutex_unlock(&st->lock);
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
|
||||
return result;
|
||||
}
|
||||
@ -613,17 +642,18 @@ inv_mpu6050_fifo_rate_store(struct device *dev, struct device_attribute *attr,
|
||||
fifo_rate > INV_MPU6050_MAX_FIFO_RATE)
|
||||
return -EINVAL;
|
||||
|
||||
result = iio_device_claim_direct_mode(indio_dev);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
mutex_lock(&st->lock);
|
||||
if (fifo_rate == st->chip_config.fifo_rate) {
|
||||
result = 0;
|
||||
goto fifo_rate_fail_unlock;
|
||||
}
|
||||
result = iio_device_claim_direct_mode(indio_dev);
|
||||
if (result)
|
||||
goto fifo_rate_fail_unlock;
|
||||
result = inv_mpu6050_set_power_itg(st, true);
|
||||
if (result)
|
||||
goto fifo_rate_fail_release;
|
||||
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);
|
||||
@ -637,10 +667,9 @@ inv_mpu6050_fifo_rate_store(struct device *dev, struct device_attribute *attr,
|
||||
|
||||
fifo_rate_fail_power_off:
|
||||
result |= inv_mpu6050_set_power_itg(st, false);
|
||||
fifo_rate_fail_release:
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
fifo_rate_fail_unlock:
|
||||
mutex_unlock(&st->lock);
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
@ -769,7 +798,14 @@ static const struct iio_chan_spec inv_mpu_channels[] = {
|
||||
INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_Z, INV_MPU6050_SCAN_ACCL_Z),
|
||||
};
|
||||
|
||||
/* constant IIO attribute */
|
||||
/*
|
||||
* The user can choose any frequency between INV_MPU6050_MIN_FIFO_RATE and
|
||||
* INV_MPU6050_MAX_FIFO_RATE, but only these frequencies are matched by the
|
||||
* low-pass filter. Specifically, each of these sampling rates are about twice
|
||||
* the bandwidth of a corresponding low-pass filter, which should eliminate
|
||||
* aliasing following the Nyquist principle. By picking a frequency different
|
||||
* from these, the user risks aliasing effects.
|
||||
*/
|
||||
static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("10 20 50 100 200 500");
|
||||
static IIO_CONST_ATTR(in_anglvel_scale_available,
|
||||
"0.000133090 0.000266181 0.000532362 0.001064724");
|
||||
@ -850,14 +886,11 @@ static int inv_check_and_setup_chip(struct inv_mpu6050_state *st)
|
||||
msleep(INV_MPU6050_POWER_UP_TIME);
|
||||
|
||||
/*
|
||||
* toggle power state. After reset, the sleep bit could be on
|
||||
* or off depending on the OTP settings. Toggling power would
|
||||
* Turn power on. After reset, the sleep bit could be on
|
||||
* or off depending on the OTP settings. Turning power on
|
||||
* make it in a definite state as well as making the hardware
|
||||
* state align with the software state
|
||||
*/
|
||||
result = inv_mpu6050_set_power_itg(st, false);
|
||||
if (result)
|
||||
return result;
|
||||
result = inv_mpu6050_set_power_itg(st, true);
|
||||
if (result)
|
||||
return result;
|
||||
@ -865,13 +898,17 @@ static int inv_check_and_setup_chip(struct inv_mpu6050_state *st)
|
||||
result = inv_mpu6050_switch_engine(st, false,
|
||||
INV_MPU6050_BIT_PWR_ACCL_STBY);
|
||||
if (result)
|
||||
return result;
|
||||
goto error_power_off;
|
||||
result = inv_mpu6050_switch_engine(st, false,
|
||||
INV_MPU6050_BIT_PWR_GYRO_STBY);
|
||||
if (result)
|
||||
return result;
|
||||
goto error_power_off;
|
||||
|
||||
return 0;
|
||||
return inv_mpu6050_set_power_itg(st, false);
|
||||
|
||||
error_power_off:
|
||||
inv_mpu6050_set_power_itg(st, false);
|
||||
return result;
|
||||
}
|
||||
|
||||
int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name,
|
||||
@ -882,6 +919,8 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name,
|
||||
struct inv_mpu6050_platform_data *pdata;
|
||||
struct device *dev = regmap_get_device(regmap);
|
||||
int result;
|
||||
struct irq_data *desc;
|
||||
int irq_type;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
|
||||
if (!indio_dev)
|
||||
@ -913,20 +952,43 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name,
|
||||
st->plat_data = *pdata;
|
||||
}
|
||||
|
||||
desc = irq_get_irq_data(irq);
|
||||
if (!desc) {
|
||||
dev_err(dev, "Could not find IRQ %d\n", irq);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
irq_type = irqd_get_trigger_type(desc);
|
||||
if (irq_type == IRQF_TRIGGER_RISING)
|
||||
st->irq_mask = INV_MPU6050_ACTIVE_HIGH;
|
||||
else if (irq_type == IRQF_TRIGGER_FALLING)
|
||||
st->irq_mask = INV_MPU6050_ACTIVE_LOW;
|
||||
else if (irq_type == IRQF_TRIGGER_HIGH)
|
||||
st->irq_mask = INV_MPU6050_ACTIVE_HIGH |
|
||||
INV_MPU6050_LATCH_INT_EN;
|
||||
else if (irq_type == IRQF_TRIGGER_LOW)
|
||||
st->irq_mask = INV_MPU6050_ACTIVE_LOW |
|
||||
INV_MPU6050_LATCH_INT_EN;
|
||||
else {
|
||||
dev_err(dev, "Invalid interrupt type 0x%x specified\n",
|
||||
irq_type);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* power is turned on inside check chip type*/
|
||||
result = inv_check_and_setup_chip(st);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
if (inv_mpu_bus_setup)
|
||||
inv_mpu_bus_setup(indio_dev);
|
||||
|
||||
result = inv_mpu6050_init_config(indio_dev);
|
||||
if (result) {
|
||||
dev_err(dev, "Could not initialize device.\n");
|
||||
return result;
|
||||
}
|
||||
|
||||
if (inv_mpu_bus_setup)
|
||||
inv_mpu_bus_setup(indio_dev);
|
||||
|
||||
dev_set_drvdata(dev, indio_dev);
|
||||
indio_dev->dev.parent = dev;
|
||||
/* name will be NULL when enumerated via ACPI */
|
||||
@ -940,50 +1002,32 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name,
|
||||
indio_dev->info = &mpu_info;
|
||||
indio_dev->modes = INDIO_BUFFER_TRIGGERED;
|
||||
|
||||
result = iio_triggered_buffer_setup(indio_dev,
|
||||
inv_mpu6050_irq_handler,
|
||||
inv_mpu6050_read_fifo,
|
||||
NULL);
|
||||
result = devm_iio_triggered_buffer_setup(dev, indio_dev,
|
||||
inv_mpu6050_irq_handler,
|
||||
inv_mpu6050_read_fifo,
|
||||
NULL);
|
||||
if (result) {
|
||||
dev_err(dev, "configure buffer fail %d\n", result);
|
||||
return result;
|
||||
}
|
||||
result = inv_mpu6050_probe_trigger(indio_dev);
|
||||
result = inv_mpu6050_probe_trigger(indio_dev, irq_type);
|
||||
if (result) {
|
||||
dev_err(dev, "trigger probe fail %d\n", result);
|
||||
goto out_unreg_ring;
|
||||
return result;
|
||||
}
|
||||
|
||||
INIT_KFIFO(st->timestamps);
|
||||
spin_lock_init(&st->time_stamp_lock);
|
||||
result = iio_device_register(indio_dev);
|
||||
result = devm_iio_device_register(dev, indio_dev);
|
||||
if (result) {
|
||||
dev_err(dev, "IIO register fail %d\n", result);
|
||||
goto out_remove_trigger;
|
||||
return result;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out_remove_trigger:
|
||||
inv_mpu6050_remove_trigger(st);
|
||||
out_unreg_ring:
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
return result;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(inv_mpu_core_probe);
|
||||
|
||||
int inv_mpu_core_remove(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
inv_mpu6050_remove_trigger(iio_priv(indio_dev));
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(inv_mpu_core_remove);
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
|
||||
static int inv_mpu_resume(struct device *dev)
|
||||
|
@ -29,25 +29,18 @@ static int inv_mpu6050_select_bypass(struct i2c_mux_core *muxc, u32 chan_id)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_mux_priv(muxc);
|
||||
struct inv_mpu6050_state *st = iio_priv(indio_dev);
|
||||
int ret = 0;
|
||||
int ret;
|
||||
|
||||
/* Use the same mutex which was used everywhere to protect power-op */
|
||||
mutex_lock(&st->lock);
|
||||
if (!st->powerup_count) {
|
||||
ret = regmap_write(st->map, st->reg->pwr_mgmt_1, 0);
|
||||
if (ret)
|
||||
goto write_error;
|
||||
|
||||
usleep_range(INV_MPU6050_REG_UP_TIME_MIN,
|
||||
INV_MPU6050_REG_UP_TIME_MAX);
|
||||
}
|
||||
if (!ret) {
|
||||
st->powerup_count++;
|
||||
ret = regmap_write(st->map, st->reg->int_pin_cfg,
|
||||
INV_MPU6050_INT_PIN_CFG |
|
||||
INV_MPU6050_BIT_BYPASS_EN);
|
||||
}
|
||||
write_error:
|
||||
ret = inv_mpu6050_set_power_itg(st, true);
|
||||
if (ret)
|
||||
goto error_unlock;
|
||||
|
||||
ret = regmap_write(st->map, st->reg->int_pin_cfg,
|
||||
st->irq_mask | INV_MPU6050_BIT_BYPASS_EN);
|
||||
|
||||
error_unlock:
|
||||
mutex_unlock(&st->lock);
|
||||
|
||||
return ret;
|
||||
@ -59,12 +52,11 @@ static int inv_mpu6050_deselect_bypass(struct i2c_mux_core *muxc, u32 chan_id)
|
||||
struct inv_mpu6050_state *st = iio_priv(indio_dev);
|
||||
|
||||
mutex_lock(&st->lock);
|
||||
/* It doesn't really mattter, if any of the calls fails */
|
||||
regmap_write(st->map, st->reg->int_pin_cfg, INV_MPU6050_INT_PIN_CFG);
|
||||
st->powerup_count--;
|
||||
if (!st->powerup_count)
|
||||
regmap_write(st->map, st->reg->pwr_mgmt_1,
|
||||
INV_MPU6050_BIT_SLEEP);
|
||||
|
||||
/* It doesn't really matter if any of the calls fail */
|
||||
regmap_write(st->map, st->reg->int_pin_cfg, st->irq_mask);
|
||||
inv_mpu6050_set_power_itg(st, false);
|
||||
|
||||
mutex_unlock(&st->lock);
|
||||
|
||||
return 0;
|
||||
@ -133,29 +125,32 @@ static int inv_mpu_probe(struct i2c_client *client,
|
||||
return result;
|
||||
|
||||
st = iio_priv(dev_get_drvdata(&client->dev));
|
||||
st->muxc = i2c_mux_alloc(client->adapter, &client->dev,
|
||||
1, 0, I2C_MUX_LOCKED | I2C_MUX_GATE,
|
||||
inv_mpu6050_select_bypass,
|
||||
inv_mpu6050_deselect_bypass);
|
||||
if (!st->muxc) {
|
||||
result = -ENOMEM;
|
||||
goto out_unreg_device;
|
||||
switch (st->chip_type) {
|
||||
case INV_ICM20608:
|
||||
/* no i2c auxiliary bus on the chip */
|
||||
break;
|
||||
default:
|
||||
/* declare i2c auxiliary bus */
|
||||
st->muxc = i2c_mux_alloc(client->adapter, &client->dev,
|
||||
1, 0, I2C_MUX_LOCKED | I2C_MUX_GATE,
|
||||
inv_mpu6050_select_bypass,
|
||||
inv_mpu6050_deselect_bypass);
|
||||
if (!st->muxc)
|
||||
return -ENOMEM;
|
||||
st->muxc->priv = dev_get_drvdata(&client->dev);
|
||||
result = i2c_mux_add_adapter(st->muxc, 0, 0, 0);
|
||||
if (result)
|
||||
return result;
|
||||
result = inv_mpu_acpi_create_mux_client(client);
|
||||
if (result)
|
||||
goto out_del_mux;
|
||||
break;
|
||||
}
|
||||
st->muxc->priv = dev_get_drvdata(&client->dev);
|
||||
result = i2c_mux_add_adapter(st->muxc, 0, 0, 0);
|
||||
if (result)
|
||||
goto out_unreg_device;
|
||||
|
||||
result = inv_mpu_acpi_create_mux_client(client);
|
||||
if (result)
|
||||
goto out_del_mux;
|
||||
|
||||
return 0;
|
||||
|
||||
out_del_mux:
|
||||
i2c_mux_del_adapters(st->muxc);
|
||||
out_unreg_device:
|
||||
inv_mpu_core_remove(&client->dev);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -164,10 +159,12 @@ static int inv_mpu_remove(struct i2c_client *client)
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
struct inv_mpu6050_state *st = iio_priv(indio_dev);
|
||||
|
||||
inv_mpu_acpi_delete_mux_client(client);
|
||||
i2c_mux_del_adapters(st->muxc);
|
||||
if (st->muxc) {
|
||||
inv_mpu_acpi_delete_mux_client(client);
|
||||
i2c_mux_del_adapters(st->muxc);
|
||||
}
|
||||
|
||||
return inv_mpu_core_remove(&client->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -179,6 +176,7 @@ static const struct i2c_device_id inv_mpu_id[] = {
|
||||
{"mpu6500", INV_MPU6500},
|
||||
{"mpu9150", INV_MPU9150},
|
||||
{"mpu9250", INV_MPU9250},
|
||||
{"mpu9255", INV_MPU9255},
|
||||
{"icm20608", INV_ICM20608},
|
||||
{}
|
||||
};
|
||||
@ -202,6 +200,10 @@ static const struct of_device_id inv_of_match[] = {
|
||||
.compatible = "invensense,mpu9250",
|
||||
.data = (void *)INV_MPU9250
|
||||
},
|
||||
{
|
||||
.compatible = "invensense,mpu9255",
|
||||
.data = (void *)INV_MPU9255
|
||||
},
|
||||
{
|
||||
.compatible = "invensense,icm20608",
|
||||
.data = (void *)INV_ICM20608
|
||||
|
@ -40,6 +40,7 @@
|
||||
* @raw_accl: Address of first accel register.
|
||||
* @temperature: temperature register
|
||||
* @int_enable: Interrupt enable register.
|
||||
* @int_status: Interrupt status register.
|
||||
* @pwr_mgmt_1: Controls chip's power state and clock source.
|
||||
* @pwr_mgmt_2: Controls power state of individual sensors.
|
||||
* @int_pin_cfg; Controls interrupt pin configuration.
|
||||
@ -60,6 +61,7 @@ struct inv_mpu6050_reg_map {
|
||||
u8 raw_accl;
|
||||
u8 temperature;
|
||||
u8 int_enable;
|
||||
u8 int_status;
|
||||
u8 pwr_mgmt_1;
|
||||
u8 pwr_mgmt_2;
|
||||
u8 int_pin_cfg;
|
||||
@ -74,6 +76,7 @@ enum inv_devices {
|
||||
INV_MPU6000,
|
||||
INV_MPU9150,
|
||||
INV_MPU9250,
|
||||
INV_MPU9255,
|
||||
INV_ICM20608,
|
||||
INV_NUM_PARTS
|
||||
};
|
||||
@ -94,6 +97,7 @@ struct inv_mpu6050_chip_config {
|
||||
unsigned int accl_fifo_enable:1;
|
||||
unsigned int gyro_fifo_enable:1;
|
||||
u16 fifo_rate;
|
||||
u8 user_ctrl;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -125,6 +129,7 @@ struct inv_mpu6050_hw {
|
||||
* @timestamps: kfifo queue to store time stamp.
|
||||
* @map regmap pointer.
|
||||
* @irq interrupt number.
|
||||
* @irq_mask the int_pin_cfg mask to configure interrupt type.
|
||||
*/
|
||||
struct inv_mpu6050_state {
|
||||
#define TIMESTAMP_FIFO_SIZE 16
|
||||
@ -143,6 +148,8 @@ struct inv_mpu6050_state {
|
||||
DECLARE_KFIFO(timestamps, long long, TIMESTAMP_FIFO_SIZE);
|
||||
struct regmap *map;
|
||||
int irq;
|
||||
u8 irq_mask;
|
||||
unsigned skip_samples;
|
||||
};
|
||||
|
||||
/*register and associated bit definition*/
|
||||
@ -166,6 +173,9 @@ struct inv_mpu6050_state {
|
||||
#define INV_MPU6050_REG_TEMPERATURE 0x41
|
||||
#define INV_MPU6050_REG_RAW_GYRO 0x43
|
||||
|
||||
#define INV_MPU6050_REG_INT_STATUS 0x3A
|
||||
#define INV_MPU6050_BIT_RAW_DATA_RDY_INT 0x01
|
||||
|
||||
#define INV_MPU6050_REG_USER_CTRL 0x6A
|
||||
#define INV_MPU6050_BIT_FIFO_RST 0x04
|
||||
#define INV_MPU6050_BIT_DMP_RST 0x08
|
||||
@ -215,8 +225,12 @@ struct inv_mpu6050_state {
|
||||
#define INV_MPU6050_OUTPUT_DATA_SIZE 24
|
||||
|
||||
#define INV_MPU6050_REG_INT_PIN_CFG 0x37
|
||||
#define INV_MPU6050_ACTIVE_HIGH 0x00
|
||||
#define INV_MPU6050_ACTIVE_LOW 0x80
|
||||
/* enable level triggering */
|
||||
#define INV_MPU6050_LATCH_INT_EN 0x20
|
||||
#define INV_MPU6050_BIT_BYPASS_EN 0x2
|
||||
#define INV_MPU6050_INT_PIN_CFG 0
|
||||
|
||||
|
||||
/* init parameters */
|
||||
#define INV_MPU6050_INIT_FIFO_RATE 50
|
||||
@ -232,6 +246,7 @@ struct inv_mpu6050_state {
|
||||
#define INV_MPU6500_WHOAMI_VALUE 0x70
|
||||
#define INV_MPU9150_WHOAMI_VALUE 0x68
|
||||
#define INV_MPU9250_WHOAMI_VALUE 0x71
|
||||
#define INV_MPU9255_WHOAMI_VALUE 0x73
|
||||
#define INV_ICM20608_WHOAMI_VALUE 0xAF
|
||||
|
||||
/* scan element definition */
|
||||
@ -287,8 +302,7 @@ enum inv_mpu6050_clock_sel_e {
|
||||
|
||||
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);
|
||||
void inv_mpu6050_remove_trigger(struct inv_mpu6050_state *st);
|
||||
int inv_mpu6050_probe_trigger(struct iio_dev *indio_dev, int irq_type);
|
||||
int inv_reset_fifo(struct iio_dev *indio_dev);
|
||||
int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en, u32 mask);
|
||||
int inv_mpu6050_write_reg(struct inv_mpu6050_state *st, int reg, u8 val);
|
||||
@ -297,6 +311,4 @@ int inv_mpu_acpi_create_mux_client(struct i2c_client *client);
|
||||
void inv_mpu_acpi_delete_mux_client(struct i2c_client *client);
|
||||
int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name,
|
||||
int (*inv_mpu_bus_setup)(struct iio_dev *), int chip_type);
|
||||
int inv_mpu_core_remove(struct device *dev);
|
||||
int inv_mpu6050_set_power_itg(struct inv_mpu6050_state *st, bool power_on);
|
||||
extern const struct dev_pm_ops inv_mpu_pmops;
|
||||
|
@ -51,13 +51,14 @@ int inv_reset_fifo(struct iio_dev *indio_dev)
|
||||
if (result)
|
||||
goto reset_fifo_fail;
|
||||
/* disable fifo reading */
|
||||
result = regmap_write(st->map, st->reg->user_ctrl, 0);
|
||||
result = regmap_write(st->map, st->reg->user_ctrl,
|
||||
st->chip_config.user_ctrl);
|
||||
if (result)
|
||||
goto reset_fifo_fail;
|
||||
|
||||
/* reset FIFO*/
|
||||
result = regmap_write(st->map, st->reg->user_ctrl,
|
||||
INV_MPU6050_BIT_FIFO_RST);
|
||||
d = st->chip_config.user_ctrl | INV_MPU6050_BIT_FIFO_RST;
|
||||
result = regmap_write(st->map, st->reg->user_ctrl, d);
|
||||
if (result)
|
||||
goto reset_fifo_fail;
|
||||
|
||||
@ -72,9 +73,9 @@ int inv_reset_fifo(struct iio_dev *indio_dev)
|
||||
if (result)
|
||||
return result;
|
||||
}
|
||||
/* enable FIFO reading and I2C master interface*/
|
||||
result = regmap_write(st->map, st->reg->user_ctrl,
|
||||
INV_MPU6050_BIT_FIFO_EN);
|
||||
/* enable FIFO reading */
|
||||
d = st->chip_config.user_ctrl | INV_MPU6050_BIT_FIFO_EN;
|
||||
result = regmap_write(st->map, st->reg->user_ctrl, d);
|
||||
if (result)
|
||||
goto reset_fifo_fail;
|
||||
/* enable sensor output to FIFO */
|
||||
@ -127,8 +128,23 @@ irqreturn_t inv_mpu6050_read_fifo(int irq, void *p)
|
||||
u8 data[INV_MPU6050_OUTPUT_DATA_SIZE];
|
||||
u16 fifo_count;
|
||||
s64 timestamp;
|
||||
int int_status;
|
||||
|
||||
mutex_lock(&st->lock);
|
||||
|
||||
/* ack interrupt and check status */
|
||||
result = regmap_read(st->map, st->reg->int_status, &int_status);
|
||||
if (result) {
|
||||
dev_err(regmap_get_device(st->map),
|
||||
"failed to ack interrupt\n");
|
||||
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);
|
||||
goto end_session;
|
||||
}
|
||||
|
||||
if (!(st->chip_config.accl_fifo_enable |
|
||||
st->chip_config.gyro_fifo_enable))
|
||||
goto end_session;
|
||||
@ -140,7 +156,7 @@ irqreturn_t inv_mpu6050_read_fifo(int irq, void *p)
|
||||
bytes_per_datum += INV_MPU6050_BYTES_PER_3AXIS_SENSOR;
|
||||
|
||||
/*
|
||||
* read fifo_count register to know how many bytes inside FIFO
|
||||
* read fifo_count register to know how many bytes are inside the FIFO
|
||||
* right now
|
||||
*/
|
||||
result = regmap_bulk_read(st->map, st->reg->fifo_count_h, data,
|
||||
@ -150,7 +166,7 @@ irqreturn_t inv_mpu6050_read_fifo(int irq, void *p)
|
||||
fifo_count = be16_to_cpup((__be16 *)(&data[0]));
|
||||
if (fifo_count < bytes_per_datum)
|
||||
goto end_session;
|
||||
/* fifo count can't be odd number, if it is odd, reset fifo*/
|
||||
/* 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)
|
||||
@ -159,7 +175,7 @@ irqreturn_t inv_mpu6050_read_fifo(int irq, void *p)
|
||||
if (kfifo_len(&st->timestamps) >
|
||||
fifo_count / bytes_per_datum + INV_MPU6050_TIME_STAMP_TOR)
|
||||
goto flush_fifo;
|
||||
while (fifo_count >= bytes_per_datum) {
|
||||
do {
|
||||
result = regmap_bulk_read(st->map, st->reg->fifo_r_w,
|
||||
data, bytes_per_datum);
|
||||
if (result)
|
||||
@ -170,12 +186,15 @@ irqreturn_t inv_mpu6050_read_fifo(int irq, void *p)
|
||||
if (result == 0)
|
||||
timestamp = 0;
|
||||
|
||||
result = iio_push_to_buffers_with_timestamp(indio_dev, data,
|
||||
timestamp);
|
||||
if (result)
|
||||
goto flush_fifo;
|
||||
/* skip first samples if needed */
|
||||
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);
|
||||
|
||||
end_session:
|
||||
mutex_unlock(&st->lock);
|
||||
|
@ -31,8 +31,9 @@ static int inv_mpu_i2c_disable(struct iio_dev *indio_dev)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_write(st->map, INV_MPU6050_REG_USER_CTRL,
|
||||
INV_MPU6050_BIT_I2C_IF_DIS);
|
||||
st->chip_config.user_ctrl |= INV_MPU6050_BIT_I2C_IF_DIS;
|
||||
ret = regmap_write(st->map, st->reg->user_ctrl,
|
||||
st->chip_config.user_ctrl);
|
||||
if (ret) {
|
||||
inv_mpu6050_set_power_itg(st, false);
|
||||
return ret;
|
||||
@ -69,11 +70,6 @@ static int inv_mpu_probe(struct spi_device *spi)
|
||||
inv_mpu_i2c_disable, chip_type);
|
||||
}
|
||||
|
||||
static int inv_mpu_remove(struct spi_device *spi)
|
||||
{
|
||||
return inv_mpu_core_remove(&spi->dev);
|
||||
}
|
||||
|
||||
/*
|
||||
* device id table is used to identify what device can be
|
||||
* supported by this driver
|
||||
@ -83,6 +79,7 @@ static const struct spi_device_id inv_mpu_id[] = {
|
||||
{"mpu6500", INV_MPU6500},
|
||||
{"mpu9150", INV_MPU9150},
|
||||
{"mpu9250", INV_MPU9250},
|
||||
{"mpu9255", INV_MPU9255},
|
||||
{"icm20608", INV_ICM20608},
|
||||
{}
|
||||
};
|
||||
@ -97,7 +94,6 @@ MODULE_DEVICE_TABLE(acpi, inv_acpi_match);
|
||||
|
||||
static struct spi_driver inv_mpu_driver = {
|
||||
.probe = inv_mpu_probe,
|
||||
.remove = inv_mpu_remove,
|
||||
.id_table = inv_mpu_id,
|
||||
.driver = {
|
||||
.acpi_match_table = ACPI_PTR(inv_acpi_match),
|
||||
|
@ -49,49 +49,66 @@ static int inv_mpu6050_set_enable(struct iio_dev *indio_dev, bool enable)
|
||||
if (result)
|
||||
return result;
|
||||
inv_scan_query(indio_dev);
|
||||
st->skip_samples = 0;
|
||||
if (st->chip_config.gyro_fifo_enable) {
|
||||
result = inv_mpu6050_switch_engine(st, true,
|
||||
INV_MPU6050_BIT_PWR_GYRO_STBY);
|
||||
if (result)
|
||||
return result;
|
||||
goto error_power_off;
|
||||
/* gyro first sample is out of specs, skip it */
|
||||
st->skip_samples = 1;
|
||||
}
|
||||
if (st->chip_config.accl_fifo_enable) {
|
||||
result = inv_mpu6050_switch_engine(st, true,
|
||||
INV_MPU6050_BIT_PWR_ACCL_STBY);
|
||||
if (result)
|
||||
return result;
|
||||
goto error_gyro_off;
|
||||
}
|
||||
result = inv_reset_fifo(indio_dev);
|
||||
if (result)
|
||||
return result;
|
||||
goto error_accl_off;
|
||||
} else {
|
||||
result = regmap_write(st->map, st->reg->fifo_en, 0);
|
||||
if (result)
|
||||
return result;
|
||||
goto error_accl_off;
|
||||
|
||||
result = regmap_write(st->map, st->reg->int_enable, 0);
|
||||
if (result)
|
||||
return result;
|
||||
goto error_accl_off;
|
||||
|
||||
result = regmap_write(st->map, st->reg->user_ctrl, 0);
|
||||
result = regmap_write(st->map, st->reg->user_ctrl,
|
||||
st->chip_config.user_ctrl);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
result = inv_mpu6050_switch_engine(st, false,
|
||||
INV_MPU6050_BIT_PWR_GYRO_STBY);
|
||||
if (result)
|
||||
return result;
|
||||
goto error_accl_off;
|
||||
|
||||
result = inv_mpu6050_switch_engine(st, false,
|
||||
INV_MPU6050_BIT_PWR_ACCL_STBY);
|
||||
if (result)
|
||||
return result;
|
||||
goto error_accl_off;
|
||||
|
||||
result = inv_mpu6050_switch_engine(st, false,
|
||||
INV_MPU6050_BIT_PWR_GYRO_STBY);
|
||||
if (result)
|
||||
goto error_gyro_off;
|
||||
|
||||
result = inv_mpu6050_set_power_itg(st, false);
|
||||
if (result)
|
||||
return result;
|
||||
goto error_power_off;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error_accl_off:
|
||||
if (st->chip_config.accl_fifo_enable)
|
||||
inv_mpu6050_switch_engine(st, false,
|
||||
INV_MPU6050_BIT_PWR_ACCL_STBY);
|
||||
error_gyro_off:
|
||||
if (st->chip_config.gyro_fifo_enable)
|
||||
inv_mpu6050_switch_engine(st, false,
|
||||
INV_MPU6050_BIT_PWR_GYRO_STBY);
|
||||
error_power_off:
|
||||
inv_mpu6050_set_power_itg(st, false);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -117,7 +134,7 @@ static const struct iio_trigger_ops inv_mpu_trigger_ops = {
|
||||
.set_trigger_state = &inv_mpu_data_rdy_trigger_set_state,
|
||||
};
|
||||
|
||||
int inv_mpu6050_probe_trigger(struct iio_dev *indio_dev)
|
||||
int inv_mpu6050_probe_trigger(struct iio_dev *indio_dev, int irq_type)
|
||||
{
|
||||
int ret;
|
||||
struct inv_mpu6050_state *st = iio_priv(indio_dev);
|
||||
@ -131,7 +148,7 @@ int inv_mpu6050_probe_trigger(struct iio_dev *indio_dev)
|
||||
|
||||
ret = devm_request_irq(&indio_dev->dev, st->irq,
|
||||
&iio_trigger_generic_data_rdy_poll,
|
||||
IRQF_TRIGGER_RISING,
|
||||
irq_type,
|
||||
"inv_mpu",
|
||||
st->trig);
|
||||
if (ret)
|
||||
@ -141,7 +158,7 @@ int inv_mpu6050_probe_trigger(struct iio_dev *indio_dev)
|
||||
st->trig->ops = &inv_mpu_trigger_ops;
|
||||
iio_trigger_set_drvdata(st->trig, indio_dev);
|
||||
|
||||
ret = iio_trigger_register(st->trig);
|
||||
ret = devm_iio_trigger_register(&indio_dev->dev, st->trig);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -149,8 +166,3 @@ int inv_mpu6050_probe_trigger(struct iio_dev *indio_dev)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void inv_mpu6050_remove_trigger(struct inv_mpu6050_state *st)
|
||||
{
|
||||
iio_trigger_unregister(st->trig);
|
||||
}
|
||||
|
@ -8,7 +8,8 @@ config IIO_ST_LSM6DSX
|
||||
select IIO_ST_LSM6DSX_SPI if (SPI_MASTER)
|
||||
help
|
||||
Say yes here to build support for STMicroelectronics LSM6DSx imu
|
||||
sensor. Supported devices: lsm6ds3, lsm6ds3h, lsm6dsl, lsm6dsm
|
||||
sensor. Supported devices: lsm6ds3, lsm6ds3h, lsm6dsl, lsm6dsm,
|
||||
ism330dlc
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called st_lsm6dsx.
|
||||
|
@ -18,12 +18,14 @@
|
||||
#define ST_LSM6DS3H_DEV_NAME "lsm6ds3h"
|
||||
#define ST_LSM6DSL_DEV_NAME "lsm6dsl"
|
||||
#define ST_LSM6DSM_DEV_NAME "lsm6dsm"
|
||||
#define ST_ISM330DLC_DEV_NAME "ism330dlc"
|
||||
|
||||
enum st_lsm6dsx_hw_id {
|
||||
ST_LSM6DS3_ID,
|
||||
ST_LSM6DS3H_ID,
|
||||
ST_LSM6DSL_ID,
|
||||
ST_LSM6DSM_ID,
|
||||
ST_ISM330DLC_ID,
|
||||
ST_LSM6DSX_MAX_ID,
|
||||
};
|
||||
|
||||
|
@ -1,10 +1,10 @@
|
||||
/*
|
||||
* STMicroelectronics st_lsm6dsx FIFO buffer library driver
|
||||
*
|
||||
* LSM6DS3/LSM6DS3H/LSM6DSL/LSM6DSM: The FIFO buffer can be configured
|
||||
* to store data from gyroscope and accelerometer. Samples are queued
|
||||
* without any tag according to a specific pattern based on 'FIFO data sets'
|
||||
* (6 bytes each):
|
||||
* LSM6DS3/LSM6DS3H/LSM6DSL/LSM6DSM/ISM330DLC: The FIFO buffer can be
|
||||
* configured to store data from gyroscope and accelerometer. Samples are
|
||||
* queued without any tag according to a specific pattern based on
|
||||
* 'FIFO data sets' (6 bytes each):
|
||||
* - 1st data set is reserved for gyroscope data
|
||||
* - 2nd data set is reserved for accelerometer data
|
||||
* The FIFO pattern changes depending on the ODRs and decimation factors
|
||||
@ -276,7 +276,7 @@ static inline int st_lsm6dsx_read_block(struct st_lsm6dsx_hw *hw, u8 *data,
|
||||
#define ST_LSM6DSX_IIO_BUFF_SIZE (ALIGN(ST_LSM6DSX_SAMPLE_SIZE, \
|
||||
sizeof(s64)) + sizeof(s64))
|
||||
/**
|
||||
* st_lsm6dsx_read_fifo() - LSM6DS3-LSM6DS3H-LSM6DSL-LSM6DSM read FIFO routine
|
||||
* st_lsm6dsx_read_fifo() - hw FIFO read routine
|
||||
* @hw: Pointer to instance of struct st_lsm6dsx_hw.
|
||||
*
|
||||
* Read samples from the hw FIFO and push them to IIO buffers.
|
||||
|
@ -17,7 +17,7 @@
|
||||
* - Gyroscope supported full-scale [dps]: +-125/+-245/+-500/+-1000/+-2000
|
||||
* - FIFO size: 8KB
|
||||
*
|
||||
* - LSM6DS3H/LSM6DSL/LSM6DSM:
|
||||
* - LSM6DS3H/LSM6DSL/LSM6DSM/ISM330DLC:
|
||||
* - Accelerometer/Gyroscope supported ODR [Hz]: 13, 26, 52, 104, 208, 416
|
||||
* - Accelerometer supported full-scale [g]: +-2/+-4/+-8/+-16
|
||||
* - Gyroscope supported full-scale [dps]: +-125/+-245/+-500/+-1000/+-2000
|
||||
@ -252,6 +252,7 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
|
||||
.id = {
|
||||
[0] = ST_LSM6DSL_ID,
|
||||
[1] = ST_LSM6DSM_ID,
|
||||
[2] = ST_ISM330DLC_ID,
|
||||
},
|
||||
.decimator = {
|
||||
[ST_LSM6DSX_ID_ACC] = {
|
||||
@ -266,11 +267,11 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
|
||||
.fifo_ops = {
|
||||
.fifo_th = {
|
||||
.addr = 0x06,
|
||||
.mask = GENMASK(11, 0),
|
||||
.mask = GENMASK(10, 0),
|
||||
},
|
||||
.fifo_diff = {
|
||||
.addr = 0x3a,
|
||||
.mask = GENMASK(11, 0),
|
||||
.mask = GENMASK(10, 0),
|
||||
},
|
||||
.th_wl = 3, /* 1LSB = 2B */
|
||||
},
|
||||
|
@ -57,6 +57,10 @@ static const struct of_device_id st_lsm6dsx_i2c_of_match[] = {
|
||||
.compatible = "st,lsm6dsm",
|
||||
.data = (void *)ST_LSM6DSM_ID,
|
||||
},
|
||||
{
|
||||
.compatible = "st,ism330dlc",
|
||||
.data = (void *)ST_ISM330DLC_ID,
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, st_lsm6dsx_i2c_of_match);
|
||||
@ -66,6 +70,7 @@ static const struct i2c_device_id st_lsm6dsx_i2c_id_table[] = {
|
||||
{ ST_LSM6DS3H_DEV_NAME, ST_LSM6DS3H_ID },
|
||||
{ ST_LSM6DSL_DEV_NAME, ST_LSM6DSL_ID },
|
||||
{ ST_LSM6DSM_DEV_NAME, ST_LSM6DSM_ID },
|
||||
{ ST_ISM330DLC_DEV_NAME, ST_ISM330DLC_ID },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, st_lsm6dsx_i2c_id_table);
|
||||
|
@ -57,6 +57,10 @@ static const struct of_device_id st_lsm6dsx_spi_of_match[] = {
|
||||
.compatible = "st,lsm6dsm",
|
||||
.data = (void *)ST_LSM6DSM_ID,
|
||||
},
|
||||
{
|
||||
.compatible = "st,ism330dlc",
|
||||
.data = (void *)ST_ISM330DLC_ID,
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, st_lsm6dsx_spi_of_match);
|
||||
@ -66,6 +70,7 @@ static const struct spi_device_id st_lsm6dsx_spi_id_table[] = {
|
||||
{ ST_LSM6DS3H_DEV_NAME, ST_LSM6DS3H_ID },
|
||||
{ ST_LSM6DSL_DEV_NAME, ST_LSM6DSL_ID },
|
||||
{ ST_LSM6DSM_DEV_NAME, ST_LSM6DSM_ID },
|
||||
{ ST_ISM330DLC_DEV_NAME, ST_ISM330DLC_ID },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, st_lsm6dsx_spi_id_table);
|
||||
|
@ -409,6 +409,14 @@ config TSL2583
|
||||
Provides support for the TAOS tsl2580, tsl2581 and tsl2583 devices.
|
||||
Access ALS data via iio, sysfs.
|
||||
|
||||
config TSL2772
|
||||
tristate "TAOS TSL/TMD2x71 and TSL/TMD2x72 Family of light and proximity sensors"
|
||||
depends on I2C
|
||||
help
|
||||
Support for: tsl2571, tsl2671, tmd2671, tsl2771, tmd2771, tsl2572, tsl2672,
|
||||
tmd2672, tsl2772, tmd2772 devices.
|
||||
Provides iio_events and direct access via sysfs.
|
||||
|
||||
config TSL4531
|
||||
tristate "TAOS TSL4531 ambient light sensors"
|
||||
depends on I2C
|
||||
|
@ -40,6 +40,7 @@ obj-$(CONFIG_ST_UVIS25_SPI) += st_uvis25_spi.o
|
||||
obj-$(CONFIG_TCS3414) += tcs3414.o
|
||||
obj-$(CONFIG_TCS3472) += tcs3472.o
|
||||
obj-$(CONFIG_TSL2583) += tsl2583.o
|
||||
obj-$(CONFIG_TSL2772) += tsl2772.o
|
||||
obj-$(CONFIG_TSL4531) += tsl4531.o
|
||||
obj-$(CONFIG_US5182D) += us5182d.o
|
||||
obj-$(CONFIG_VCNL4000) += vcnl4000.o
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/common/cros_ec_sensors_core.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/kfifo_buf.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
@ -29,8 +30,6 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfs.h>
|
||||
|
||||
#include "../common/cros_ec_sensors/cros_ec_sensors_core.h"
|
||||
|
||||
/*
|
||||
* We only represent one entry for light or proximity. EC is merging different
|
||||
* light sensors to return the what the eye would see. For proximity, we
|
||||
|
@ -600,7 +600,7 @@ done:
|
||||
|
||||
static IIO_CONST_ATTR(in_illuminance_calibscale_available, "1 8 16 111");
|
||||
static IIO_CONST_ATTR(in_illuminance_integration_time_available,
|
||||
"0.000050 0.000100 0.000150 0.000200 0.000250 0.000300 0.000350 0.000400 0.000450 0.000500 0.000550 0.000600 0.000650");
|
||||
"0.050 0.100 0.150 0.200 0.250 0.300 0.350 0.400 0.450 0.500 0.550 0.600 0.650");
|
||||
static IIO_DEVICE_ATTR_RW(in_illuminance_input_target, 0);
|
||||
static IIO_DEVICE_ATTR_WO(in_illuminance_calibrate, 0);
|
||||
static IIO_DEVICE_ATTR_RW(in_illuminance_lux_table, 0);
|
||||
|
1800
drivers/iio/light/tsl2772.c
Normal file
1800
drivers/iio/light/tsl2772.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -26,6 +26,7 @@
|
||||
#define MAG3110_OUT_Y 0x03
|
||||
#define MAG3110_OUT_Z 0x05
|
||||
#define MAG3110_WHO_AM_I 0x07
|
||||
#define MAG3110_SYSMOD 0x08
|
||||
#define MAG3110_OFF_X 0x09 /* MSB first */
|
||||
#define MAG3110_OFF_Y 0x0b
|
||||
#define MAG3110_OFF_Z 0x0d
|
||||
@ -39,6 +40,8 @@
|
||||
#define MAG3110_CTRL_DR_SHIFT 5
|
||||
#define MAG3110_CTRL_DR_DEFAULT 0
|
||||
|
||||
#define MAG3110_SYSMOD_MODE_MASK GENMASK(1, 0)
|
||||
|
||||
#define MAG3110_CTRL_TM BIT(1) /* trigger single measurement */
|
||||
#define MAG3110_CTRL_AC BIT(0) /* continuous measurements */
|
||||
|
||||
@ -52,17 +55,20 @@ struct mag3110_data {
|
||||
struct i2c_client *client;
|
||||
struct mutex lock;
|
||||
u8 ctrl_reg1;
|
||||
int sleep_val;
|
||||
};
|
||||
|
||||
static int mag3110_request(struct mag3110_data *data)
|
||||
{
|
||||
int ret, tries = 150;
|
||||
|
||||
/* trigger measurement */
|
||||
ret = i2c_smbus_write_byte_data(data->client, MAG3110_CTRL_REG1,
|
||||
data->ctrl_reg1 | MAG3110_CTRL_TM);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if ((data->ctrl_reg1 & MAG3110_CTRL_AC) == 0) {
|
||||
/* trigger measurement */
|
||||
ret = i2c_smbus_write_byte_data(data->client, MAG3110_CTRL_REG1,
|
||||
data->ctrl_reg1 | MAG3110_CTRL_TM);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
while (tries-- > 0) {
|
||||
ret = i2c_smbus_read_byte_data(data->client, MAG3110_STATUS);
|
||||
@ -71,7 +77,11 @@ static int mag3110_request(struct mag3110_data *data)
|
||||
/* wait for data ready */
|
||||
if ((ret & MAG3110_STATUS_DRDY) == MAG3110_STATUS_DRDY)
|
||||
break;
|
||||
msleep(20);
|
||||
|
||||
if (data->sleep_val <= 20)
|
||||
usleep_range(data->sleep_val * 250, data->sleep_val * 500);
|
||||
else
|
||||
msleep(20);
|
||||
}
|
||||
|
||||
if (tries < 0) {
|
||||
@ -144,6 +154,117 @@ static int mag3110_get_samp_freq_index(struct mag3110_data *data,
|
||||
val2);
|
||||
}
|
||||
|
||||
static int mag3110_calculate_sleep(struct mag3110_data *data)
|
||||
{
|
||||
int ret, i = data->ctrl_reg1 >> MAG3110_CTRL_DR_SHIFT;
|
||||
|
||||
if (mag3110_samp_freq[i][0] > 0)
|
||||
ret = 1000 / mag3110_samp_freq[i][0];
|
||||
else
|
||||
ret = 1000;
|
||||
|
||||
return ret == 0 ? 1 : ret;
|
||||
}
|
||||
|
||||
static int mag3110_standby(struct mag3110_data *data)
|
||||
{
|
||||
return i2c_smbus_write_byte_data(data->client, MAG3110_CTRL_REG1,
|
||||
data->ctrl_reg1 & ~MAG3110_CTRL_AC);
|
||||
}
|
||||
|
||||
static int mag3110_wait_standby(struct mag3110_data *data)
|
||||
{
|
||||
int ret, tries = 30;
|
||||
|
||||
/*
|
||||
* Takes up to 1/ODR to come out of active mode into stby
|
||||
* Longest expected period is 12.5seconds.
|
||||
* We'll sleep for 500ms between checks
|
||||
*/
|
||||
while (tries-- > 0) {
|
||||
ret = i2c_smbus_read_byte_data(data->client, MAG3110_SYSMOD);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev, "i2c error\n");
|
||||
return ret;
|
||||
}
|
||||
/* wait for standby */
|
||||
if ((ret & MAG3110_SYSMOD_MODE_MASK) == 0)
|
||||
break;
|
||||
|
||||
msleep_interruptible(500);
|
||||
}
|
||||
|
||||
if (tries < 0) {
|
||||
dev_err(&data->client->dev, "device not entering standby mode\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mag3110_active(struct mag3110_data *data)
|
||||
{
|
||||
return i2c_smbus_write_byte_data(data->client, MAG3110_CTRL_REG1,
|
||||
data->ctrl_reg1);
|
||||
}
|
||||
|
||||
/* returns >0 if active, 0 if in standby and <0 on error */
|
||||
static int mag3110_is_active(struct mag3110_data *data)
|
||||
{
|
||||
int reg;
|
||||
|
||||
reg = i2c_smbus_read_byte_data(data->client, MAG3110_CTRL_REG1);
|
||||
if (reg < 0)
|
||||
return reg;
|
||||
|
||||
return reg & MAG3110_CTRL_AC;
|
||||
}
|
||||
|
||||
static int mag3110_change_config(struct mag3110_data *data, u8 reg, u8 val)
|
||||
{
|
||||
int ret;
|
||||
int is_active;
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
|
||||
is_active = mag3110_is_active(data);
|
||||
if (is_active < 0) {
|
||||
ret = is_active;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* config can only be changed when in standby */
|
||||
if (is_active > 0) {
|
||||
ret = mag3110_standby(data);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/*
|
||||
* After coming out of active we must wait for the part
|
||||
* to transition to STBY. This can take up to 1 /ODR to occur
|
||||
*/
|
||||
ret = mag3110_wait_standby(data);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(data->client, reg, val);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
|
||||
if (is_active > 0) {
|
||||
ret = mag3110_active(data);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
fail:
|
||||
mutex_unlock(&data->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mag3110_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
@ -235,11 +356,15 @@ static int mag3110_write_raw(struct iio_dev *indio_dev,
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
data->ctrl_reg1 &= ~MAG3110_CTRL_DR_MASK;
|
||||
data->ctrl_reg1 &= 0xff & ~MAG3110_CTRL_DR_MASK
|
||||
& ~MAG3110_CTRL_AC;
|
||||
data->ctrl_reg1 |= rate << MAG3110_CTRL_DR_SHIFT;
|
||||
ret = i2c_smbus_write_byte_data(data->client,
|
||||
MAG3110_CTRL_REG1, data->ctrl_reg1);
|
||||
data->sleep_val = mag3110_calculate_sleep(data);
|
||||
if (data->sleep_val < 40)
|
||||
data->ctrl_reg1 |= MAG3110_CTRL_AC;
|
||||
|
||||
ret = mag3110_change_config(data, MAG3110_CTRL_REG1,
|
||||
data->ctrl_reg1);
|
||||
break;
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
if (val < -10000 || val > 10000) {
|
||||
@ -337,12 +462,6 @@ static const struct iio_info mag3110_info = {
|
||||
|
||||
static const unsigned long mag3110_scan_masks[] = {0x7, 0xf, 0};
|
||||
|
||||
static int mag3110_standby(struct mag3110_data *data)
|
||||
{
|
||||
return i2c_smbus_write_byte_data(data->client, MAG3110_CTRL_REG1,
|
||||
data->ctrl_reg1 & ~MAG3110_CTRL_AC);
|
||||
}
|
||||
|
||||
static int mag3110_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
@ -374,8 +493,11 @@ static int mag3110_probe(struct i2c_client *client,
|
||||
indio_dev->available_scan_masks = mag3110_scan_masks;
|
||||
|
||||
data->ctrl_reg1 = MAG3110_CTRL_DR_DEFAULT << MAG3110_CTRL_DR_SHIFT;
|
||||
ret = i2c_smbus_write_byte_data(client, MAG3110_CTRL_REG1,
|
||||
data->ctrl_reg1);
|
||||
data->sleep_val = mag3110_calculate_sleep(data);
|
||||
if (data->sleep_val < 40)
|
||||
data->ctrl_reg1 |= MAG3110_CTRL_AC;
|
||||
|
||||
ret = mag3110_change_config(data, MAG3110_CTRL_REG1, data->ctrl_reg1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
@ -99,6 +99,23 @@ static const struct iio_info mcp4018_info = {
|
||||
.write_raw = mcp4018_write_raw,
|
||||
};
|
||||
|
||||
static const struct i2c_device_id mcp4018_id[] = {
|
||||
{ "mcp4017-502", MCP4018_502 },
|
||||
{ "mcp4017-103", MCP4018_103 },
|
||||
{ "mcp4017-503", MCP4018_503 },
|
||||
{ "mcp4017-104", MCP4018_104 },
|
||||
{ "mcp4018-502", MCP4018_502 },
|
||||
{ "mcp4018-103", MCP4018_103 },
|
||||
{ "mcp4018-503", MCP4018_503 },
|
||||
{ "mcp4018-104", MCP4018_104 },
|
||||
{ "mcp4019-502", MCP4018_502 },
|
||||
{ "mcp4019-103", MCP4018_103 },
|
||||
{ "mcp4019-503", MCP4018_503 },
|
||||
{ "mcp4019-104", MCP4018_104 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, mcp4018_id);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
|
||||
#define MCP4018_COMPATIBLE(of_compatible, cfg) { \
|
||||
@ -125,8 +142,7 @@ MODULE_DEVICE_TABLE(of, mcp4018_of_match);
|
||||
|
||||
#endif
|
||||
|
||||
static int mcp4018_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
static int mcp4018_probe(struct i2c_client *client)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
struct mcp4018_data *data;
|
||||
@ -150,7 +166,7 @@ static int mcp4018_probe(struct i2c_client *client,
|
||||
if (match)
|
||||
data->cfg = of_device_get_match_data(dev);
|
||||
else
|
||||
data->cfg = &mcp4018_cfg[id->driver_data];
|
||||
data->cfg = &mcp4018_cfg[i2c_match_id(mcp4018_id, client)->driver_data];
|
||||
|
||||
indio_dev->dev.parent = dev;
|
||||
indio_dev->info = &mcp4018_info;
|
||||
@ -161,29 +177,12 @@ static int mcp4018_probe(struct i2c_client *client,
|
||||
return devm_iio_device_register(dev, indio_dev);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id mcp4018_id[] = {
|
||||
{ "mcp4017-502", MCP4018_502 },
|
||||
{ "mcp4017-103", MCP4018_103 },
|
||||
{ "mcp4017-503", MCP4018_503 },
|
||||
{ "mcp4017-104", MCP4018_104 },
|
||||
{ "mcp4018-502", MCP4018_502 },
|
||||
{ "mcp4018-103", MCP4018_103 },
|
||||
{ "mcp4018-503", MCP4018_503 },
|
||||
{ "mcp4018-104", MCP4018_104 },
|
||||
{ "mcp4019-502", MCP4018_502 },
|
||||
{ "mcp4019-103", MCP4018_103 },
|
||||
{ "mcp4019-503", MCP4018_503 },
|
||||
{ "mcp4019-104", MCP4018_104 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, mcp4018_id);
|
||||
|
||||
static struct i2c_driver mcp4018_driver = {
|
||||
.driver = {
|
||||
.name = "mcp4018",
|
||||
.of_match_table = of_match_ptr(mcp4018_of_match),
|
||||
},
|
||||
.probe = mcp4018_probe,
|
||||
.probe_new = mcp4018_probe,
|
||||
.id_table = mcp4018_id,
|
||||
};
|
||||
|
||||
|
@ -209,6 +209,75 @@ static const struct iio_info mcp4531_info = {
|
||||
.write_raw = mcp4531_write_raw,
|
||||
};
|
||||
|
||||
static const struct i2c_device_id mcp4531_id[] = {
|
||||
{ "mcp4531-502", MCP453x_502 },
|
||||
{ "mcp4531-103", MCP453x_103 },
|
||||
{ "mcp4531-503", MCP453x_503 },
|
||||
{ "mcp4531-104", MCP453x_104 },
|
||||
{ "mcp4532-502", MCP453x_502 },
|
||||
{ "mcp4532-103", MCP453x_103 },
|
||||
{ "mcp4532-503", MCP453x_503 },
|
||||
{ "mcp4532-104", MCP453x_104 },
|
||||
{ "mcp4541-502", MCP454x_502 },
|
||||
{ "mcp4541-103", MCP454x_103 },
|
||||
{ "mcp4541-503", MCP454x_503 },
|
||||
{ "mcp4541-104", MCP454x_104 },
|
||||
{ "mcp4542-502", MCP454x_502 },
|
||||
{ "mcp4542-103", MCP454x_103 },
|
||||
{ "mcp4542-503", MCP454x_503 },
|
||||
{ "mcp4542-104", MCP454x_104 },
|
||||
{ "mcp4551-502", MCP455x_502 },
|
||||
{ "mcp4551-103", MCP455x_103 },
|
||||
{ "mcp4551-503", MCP455x_503 },
|
||||
{ "mcp4551-104", MCP455x_104 },
|
||||
{ "mcp4552-502", MCP455x_502 },
|
||||
{ "mcp4552-103", MCP455x_103 },
|
||||
{ "mcp4552-503", MCP455x_503 },
|
||||
{ "mcp4552-104", MCP455x_104 },
|
||||
{ "mcp4561-502", MCP456x_502 },
|
||||
{ "mcp4561-103", MCP456x_103 },
|
||||
{ "mcp4561-503", MCP456x_503 },
|
||||
{ "mcp4561-104", MCP456x_104 },
|
||||
{ "mcp4562-502", MCP456x_502 },
|
||||
{ "mcp4562-103", MCP456x_103 },
|
||||
{ "mcp4562-503", MCP456x_503 },
|
||||
{ "mcp4562-104", MCP456x_104 },
|
||||
{ "mcp4631-502", MCP463x_502 },
|
||||
{ "mcp4631-103", MCP463x_103 },
|
||||
{ "mcp4631-503", MCP463x_503 },
|
||||
{ "mcp4631-104", MCP463x_104 },
|
||||
{ "mcp4632-502", MCP463x_502 },
|
||||
{ "mcp4632-103", MCP463x_103 },
|
||||
{ "mcp4632-503", MCP463x_503 },
|
||||
{ "mcp4632-104", MCP463x_104 },
|
||||
{ "mcp4641-502", MCP464x_502 },
|
||||
{ "mcp4641-103", MCP464x_103 },
|
||||
{ "mcp4641-503", MCP464x_503 },
|
||||
{ "mcp4641-104", MCP464x_104 },
|
||||
{ "mcp4642-502", MCP464x_502 },
|
||||
{ "mcp4642-103", MCP464x_103 },
|
||||
{ "mcp4642-503", MCP464x_503 },
|
||||
{ "mcp4642-104", MCP464x_104 },
|
||||
{ "mcp4651-502", MCP465x_502 },
|
||||
{ "mcp4651-103", MCP465x_103 },
|
||||
{ "mcp4651-503", MCP465x_503 },
|
||||
{ "mcp4651-104", MCP465x_104 },
|
||||
{ "mcp4652-502", MCP465x_502 },
|
||||
{ "mcp4652-103", MCP465x_103 },
|
||||
{ "mcp4652-503", MCP465x_503 },
|
||||
{ "mcp4652-104", MCP465x_104 },
|
||||
{ "mcp4661-502", MCP466x_502 },
|
||||
{ "mcp4661-103", MCP466x_103 },
|
||||
{ "mcp4661-503", MCP466x_503 },
|
||||
{ "mcp4661-104", MCP466x_104 },
|
||||
{ "mcp4662-502", MCP466x_502 },
|
||||
{ "mcp4662-103", MCP466x_103 },
|
||||
{ "mcp4662-503", MCP466x_503 },
|
||||
{ "mcp4662-104", MCP466x_104 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, mcp4531_id);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
|
||||
#define MCP4531_COMPATIBLE(of_compatible, cfg) { \
|
||||
@ -286,8 +355,7 @@ static const struct of_device_id mcp4531_of_match[] = {
|
||||
MODULE_DEVICE_TABLE(of, mcp4531_of_match);
|
||||
#endif
|
||||
|
||||
static int mcp4531_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
static int mcp4531_probe(struct i2c_client *client)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
struct mcp4531_data *data;
|
||||
@ -311,7 +379,7 @@ static int mcp4531_probe(struct i2c_client *client,
|
||||
if (match)
|
||||
data->cfg = of_device_get_match_data(dev);
|
||||
else
|
||||
data->cfg = &mcp4531_cfg[id->driver_data];
|
||||
data->cfg = &mcp4531_cfg[i2c_match_id(mcp4531_id, client)->driver_data];
|
||||
|
||||
indio_dev->dev.parent = dev;
|
||||
indio_dev->info = &mcp4531_info;
|
||||
@ -322,81 +390,12 @@ static int mcp4531_probe(struct i2c_client *client,
|
||||
return devm_iio_device_register(dev, indio_dev);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id mcp4531_id[] = {
|
||||
{ "mcp4531-502", MCP453x_502 },
|
||||
{ "mcp4531-103", MCP453x_103 },
|
||||
{ "mcp4531-503", MCP453x_503 },
|
||||
{ "mcp4531-104", MCP453x_104 },
|
||||
{ "mcp4532-502", MCP453x_502 },
|
||||
{ "mcp4532-103", MCP453x_103 },
|
||||
{ "mcp4532-503", MCP453x_503 },
|
||||
{ "mcp4532-104", MCP453x_104 },
|
||||
{ "mcp4541-502", MCP454x_502 },
|
||||
{ "mcp4541-103", MCP454x_103 },
|
||||
{ "mcp4541-503", MCP454x_503 },
|
||||
{ "mcp4541-104", MCP454x_104 },
|
||||
{ "mcp4542-502", MCP454x_502 },
|
||||
{ "mcp4542-103", MCP454x_103 },
|
||||
{ "mcp4542-503", MCP454x_503 },
|
||||
{ "mcp4542-104", MCP454x_104 },
|
||||
{ "mcp4551-502", MCP455x_502 },
|
||||
{ "mcp4551-103", MCP455x_103 },
|
||||
{ "mcp4551-503", MCP455x_503 },
|
||||
{ "mcp4551-104", MCP455x_104 },
|
||||
{ "mcp4552-502", MCP455x_502 },
|
||||
{ "mcp4552-103", MCP455x_103 },
|
||||
{ "mcp4552-503", MCP455x_503 },
|
||||
{ "mcp4552-104", MCP455x_104 },
|
||||
{ "mcp4561-502", MCP456x_502 },
|
||||
{ "mcp4561-103", MCP456x_103 },
|
||||
{ "mcp4561-503", MCP456x_503 },
|
||||
{ "mcp4561-104", MCP456x_104 },
|
||||
{ "mcp4562-502", MCP456x_502 },
|
||||
{ "mcp4562-103", MCP456x_103 },
|
||||
{ "mcp4562-503", MCP456x_503 },
|
||||
{ "mcp4562-104", MCP456x_104 },
|
||||
{ "mcp4631-502", MCP463x_502 },
|
||||
{ "mcp4631-103", MCP463x_103 },
|
||||
{ "mcp4631-503", MCP463x_503 },
|
||||
{ "mcp4631-104", MCP463x_104 },
|
||||
{ "mcp4632-502", MCP463x_502 },
|
||||
{ "mcp4632-103", MCP463x_103 },
|
||||
{ "mcp4632-503", MCP463x_503 },
|
||||
{ "mcp4632-104", MCP463x_104 },
|
||||
{ "mcp4641-502", MCP464x_502 },
|
||||
{ "mcp4641-103", MCP464x_103 },
|
||||
{ "mcp4641-503", MCP464x_503 },
|
||||
{ "mcp4641-104", MCP464x_104 },
|
||||
{ "mcp4642-502", MCP464x_502 },
|
||||
{ "mcp4642-103", MCP464x_103 },
|
||||
{ "mcp4642-503", MCP464x_503 },
|
||||
{ "mcp4642-104", MCP464x_104 },
|
||||
{ "mcp4651-502", MCP465x_502 },
|
||||
{ "mcp4651-103", MCP465x_103 },
|
||||
{ "mcp4651-503", MCP465x_503 },
|
||||
{ "mcp4651-104", MCP465x_104 },
|
||||
{ "mcp4652-502", MCP465x_502 },
|
||||
{ "mcp4652-103", MCP465x_103 },
|
||||
{ "mcp4652-503", MCP465x_503 },
|
||||
{ "mcp4652-104", MCP465x_104 },
|
||||
{ "mcp4661-502", MCP466x_502 },
|
||||
{ "mcp4661-103", MCP466x_103 },
|
||||
{ "mcp4661-503", MCP466x_503 },
|
||||
{ "mcp4661-104", MCP466x_104 },
|
||||
{ "mcp4662-502", MCP466x_502 },
|
||||
{ "mcp4662-103", MCP466x_103 },
|
||||
{ "mcp4662-503", MCP466x_503 },
|
||||
{ "mcp4662-104", MCP466x_104 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, mcp4531_id);
|
||||
|
||||
static struct i2c_driver mcp4531_driver = {
|
||||
.driver = {
|
||||
.name = "mcp4531",
|
||||
.of_match_table = of_match_ptr(mcp4531_of_match),
|
||||
},
|
||||
.probe = mcp4531_probe,
|
||||
.probe_new = mcp4531_probe,
|
||||
.id_table = mcp4531_id,
|
||||
};
|
||||
|
||||
|
@ -411,12 +411,14 @@ static int lmp91000_remove(struct i2c_client *client)
|
||||
|
||||
static const struct of_device_id lmp91000_of_match[] = {
|
||||
{ .compatible = "ti,lmp91000", },
|
||||
{ .compatible = "ti,lmp91002", },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, lmp91000_of_match);
|
||||
|
||||
static const struct i2c_device_id lmp91000_id[] = {
|
||||
{ "lmp91000", 0 },
|
||||
{ "lmp91002", 0 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, lmp91000_id);
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/common/cros_ec_sensors_core.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/kfifo_buf.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
@ -28,8 +29,6 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include "../common/cros_ec_sensors/cros_ec_sensors_core.h"
|
||||
|
||||
/*
|
||||
* One channel for pressure, the other for timestamp.
|
||||
*/
|
||||
|
17
drivers/iio/resolver/Kconfig
Normal file
17
drivers/iio/resolver/Kconfig
Normal file
@ -0,0 +1,17 @@
|
||||
#
|
||||
# Resolver/Synchro drivers
|
||||
#
|
||||
menu "Resolver to digital converters"
|
||||
|
||||
config AD2S1200
|
||||
tristate "Analog Devices ad2s1200/ad2s1205 driver"
|
||||
depends on SPI
|
||||
depends on GPIOLIB || COMPILE_TEST
|
||||
help
|
||||
Say yes here to build support for Analog Devices spi resolver
|
||||
to digital converters, ad2s1200 and ad2s1205, provides direct access
|
||||
via sysfs.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ad2s1200.
|
||||
endmenu
|
5
drivers/iio/resolver/Makefile
Normal file
5
drivers/iio/resolver/Makefile
Normal file
@ -0,0 +1,5 @@
|
||||
#
|
||||
# Makefile for Resolver/Synchro drivers
|
||||
#
|
||||
|
||||
obj-$(CONFIG_AD2S1200) += ad2s1200.o
|
@ -2,43 +2,49 @@
|
||||
* ad2s1200.c simple support for the ADI Resolver to Digital Converters:
|
||||
* AD2S1200/1205
|
||||
*
|
||||
* Copyright (c) 2018-2018 David Veenstra <davidjulianveenstra@gmail.com>
|
||||
* Copyright (c) 2010-2010 Analog Devices Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
*/
|
||||
#include <linux/types.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
#define DRV_NAME "ad2s1200"
|
||||
|
||||
/* input pin sample and rdvel is controlled by driver */
|
||||
#define AD2S1200_PN 2
|
||||
|
||||
/* input clock on serial interface */
|
||||
#define AD2S1200_HZ 8192000
|
||||
/* clock period in nano second */
|
||||
#define AD2S1200_TSCLK (1000000000 / AD2S1200_HZ)
|
||||
|
||||
/**
|
||||
* struct ad2s1200_state - driver instance specific data.
|
||||
* @lock: protects both the GPIO pins and the rx buffer.
|
||||
* @sdev: spi device.
|
||||
* @sample: GPIO pin SAMPLE.
|
||||
* @rdvel: GPIO pin RDVEL.
|
||||
* @rx: buffer for spi transfers.
|
||||
*/
|
||||
struct ad2s1200_state {
|
||||
struct mutex lock;
|
||||
struct spi_device *sdev;
|
||||
int sample;
|
||||
int rdvel;
|
||||
u8 rx[2] ____cacheline_aligned;
|
||||
struct gpio_desc *sample;
|
||||
struct gpio_desc *rdvel;
|
||||
__be16 rx ____cacheline_aligned;
|
||||
};
|
||||
|
||||
static int ad2s1200_read_raw(struct iio_dev *indio_dev,
|
||||
@ -47,39 +53,63 @@ static int ad2s1200_read_raw(struct iio_dev *indio_dev,
|
||||
int *val2,
|
||||
long m)
|
||||
{
|
||||
int ret = 0;
|
||||
s16 vel;
|
||||
struct ad2s1200_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&st->lock);
|
||||
gpio_set_value(st->sample, 0);
|
||||
/* delay (6 * AD2S1200_TSCLK + 20) nano seconds */
|
||||
udelay(1);
|
||||
gpio_set_value(st->sample, 1);
|
||||
gpio_set_value(st->rdvel, !!(chan->type == IIO_ANGL));
|
||||
ret = spi_read(st->sdev, st->rx, 2);
|
||||
if (ret < 0) {
|
||||
switch (m) {
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
switch (chan->type) {
|
||||
case IIO_ANGL:
|
||||
/* 2 * Pi / (2^12 - 1) ~= 0.001534355 */
|
||||
*val = 0;
|
||||
*val2 = 1534355;
|
||||
return IIO_VAL_INT_PLUS_NANO;
|
||||
case IIO_ANGL_VEL:
|
||||
/* 2 * Pi ~= 6.283185 */
|
||||
*val = 6;
|
||||
*val2 = 283185;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
mutex_lock(&st->lock);
|
||||
gpiod_set_value(st->sample, 0);
|
||||
|
||||
/* delay (6 * AD2S1200_TSCLK + 20) nano seconds */
|
||||
udelay(1);
|
||||
gpiod_set_value(st->sample, 1);
|
||||
gpiod_set_value(st->rdvel, !!(chan->type == IIO_ANGL));
|
||||
|
||||
ret = spi_read(st->sdev, &st->rx, 2);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&st->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
switch (chan->type) {
|
||||
case IIO_ANGL:
|
||||
*val = be16_to_cpup(&st->rx) >> 4;
|
||||
break;
|
||||
case IIO_ANGL_VEL:
|
||||
*val = sign_extend32(be16_to_cpup(&st->rx) >> 4, 11);
|
||||
break;
|
||||
default:
|
||||
mutex_unlock(&st->lock);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* delay (2 * AD2S1200_TSCLK + 20) ns for sample pulse */
|
||||
udelay(1);
|
||||
mutex_unlock(&st->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
switch (chan->type) {
|
||||
case IIO_ANGL:
|
||||
*val = (((u16)(st->rx[0])) << 4) | ((st->rx[1] & 0xF0) >> 4);
|
||||
break;
|
||||
case IIO_ANGL_VEL:
|
||||
vel = (((s16)(st->rx[0])) << 4) | ((st->rx[1] & 0xF0) >> 4);
|
||||
vel = sign_extend32(vel, 11);
|
||||
*val = vel;
|
||||
break;
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
mutex_unlock(&st->lock);
|
||||
return -EINVAL;
|
||||
break;
|
||||
}
|
||||
/* delay (2 * AD2S1200_TSCLK + 20) ns for sample pulse */
|
||||
udelay(1);
|
||||
mutex_unlock(&st->lock);
|
||||
return IIO_VAL_INT;
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec ad2s1200_channels[] = {
|
||||
@ -88,11 +118,13 @@ static const struct iio_chan_spec ad2s1200_channels[] = {
|
||||
.indexed = 1,
|
||||
.channel = 0,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
|
||||
}, {
|
||||
.type = IIO_ANGL_VEL,
|
||||
.indexed = 1,
|
||||
.channel = 0,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
|
||||
}
|
||||
};
|
||||
|
||||
@ -104,27 +136,30 @@ static int ad2s1200_probe(struct spi_device *spi)
|
||||
{
|
||||
struct ad2s1200_state *st;
|
||||
struct iio_dev *indio_dev;
|
||||
int pn, ret = 0;
|
||||
unsigned short *pins = spi->dev.platform_data;
|
||||
int ret;
|
||||
|
||||
for (pn = 0; pn < AD2S1200_PN; pn++) {
|
||||
ret = devm_gpio_request_one(&spi->dev, pins[pn], GPIOF_DIR_OUT,
|
||||
DRV_NAME);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev, "request gpio pin %d failed\n",
|
||||
pins[pn]);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
st = iio_priv(indio_dev);
|
||||
mutex_init(&st->lock);
|
||||
st->sdev = spi;
|
||||
st->sample = pins[0];
|
||||
st->rdvel = pins[1];
|
||||
|
||||
st->sample = devm_gpiod_get(&spi->dev, "adi,sample", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(st->sample)) {
|
||||
dev_err(&spi->dev, "Failed to claim SAMPLE gpio: err=%ld\n",
|
||||
PTR_ERR(st->sample));
|
||||
return PTR_ERR(st->sample);
|
||||
}
|
||||
|
||||
st->rdvel = devm_gpiod_get(&spi->dev, "adi,rdvel", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(st->rdvel)) {
|
||||
dev_err(&spi->dev, "Failed to claim RDVEL gpio: err=%ld\n",
|
||||
PTR_ERR(st->rdvel));
|
||||
return PTR_ERR(st->rdvel);
|
||||
}
|
||||
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->info = &ad2s1200_info;
|
||||
@ -133,17 +168,25 @@ static int ad2s1200_probe(struct spi_device *spi)
|
||||
indio_dev->num_channels = ARRAY_SIZE(ad2s1200_channels);
|
||||
indio_dev->name = spi_get_device_id(spi)->name;
|
||||
|
||||
ret = devm_iio_device_register(&spi->dev, indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
spi->max_speed_hz = AD2S1200_HZ;
|
||||
spi->mode = SPI_MODE_3;
|
||||
spi_setup(spi);
|
||||
ret = spi_setup(spi);
|
||||
|
||||
return 0;
|
||||
if (ret < 0) {
|
||||
dev_err(&spi->dev, "spi_setup failed!\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return devm_iio_device_register(&spi->dev, indio_dev);
|
||||
}
|
||||
|
||||
static const struct of_device_id ad2s1200_of_match[] = {
|
||||
{ .compatible = "adi,ad2s1200", },
|
||||
{ .compatible = "adi,ad2s1205", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ad2s1200_of_match);
|
||||
|
||||
static const struct spi_device_id ad2s1200_id[] = {
|
||||
{ "ad2s1200" },
|
||||
{ "ad2s1205" },
|
||||
@ -154,12 +197,14 @@ MODULE_DEVICE_TABLE(spi, ad2s1200_id);
|
||||
static struct spi_driver ad2s1200_driver = {
|
||||
.driver = {
|
||||
.name = DRV_NAME,
|
||||
.of_match_table = of_match_ptr(ad2s1200_of_match),
|
||||
},
|
||||
.probe = ad2s1200_probe,
|
||||
.id_table = ad2s1200_id,
|
||||
};
|
||||
module_spi_driver(ad2s1200_driver);
|
||||
|
||||
MODULE_AUTHOR("David Veenstra <davidjulianveenstra@gmail.com>");
|
||||
MODULE_AUTHOR("Graff Yang <graff.yang@gmail.com>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD2S1200/1205 Resolver to Digital SPI driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -24,10 +24,6 @@ menuconfig STAGING
|
||||
|
||||
if STAGING
|
||||
|
||||
source "drivers/staging/ipx/Kconfig"
|
||||
|
||||
source "drivers/staging/ncpfs/Kconfig"
|
||||
|
||||
source "drivers/staging/wlan-ng/Kconfig"
|
||||
|
||||
source "drivers/staging/comedi/Kconfig"
|
||||
@ -84,8 +80,6 @@ source "drivers/staging/netlogic/Kconfig"
|
||||
|
||||
source "drivers/staging/mt29f_spinand/Kconfig"
|
||||
|
||||
source "drivers/staging/lustre/Kconfig"
|
||||
|
||||
source "drivers/staging/dgnc/Kconfig"
|
||||
|
||||
source "drivers/staging/gs_fpgaboot/Kconfig"
|
||||
|
@ -3,8 +3,6 @@
|
||||
|
||||
obj-y += media/
|
||||
obj-y += typec/
|
||||
obj-$(CONFIG_IPX) += ipx/
|
||||
obj-$(CONFIG_NCP_FS) += ncpfs/
|
||||
obj-$(CONFIG_PRISM2_USB) += wlan-ng/
|
||||
obj-$(CONFIG_COMEDI) += comedi/
|
||||
obj-$(CONFIG_FB_OLPC_DCON) += olpc_dcon/
|
||||
@ -32,7 +30,6 @@ obj-$(CONFIG_STAGING_BOARD) += board/
|
||||
obj-$(CONFIG_LTE_GDM724X) += gdm724x/
|
||||
obj-$(CONFIG_FIREWIRE_SERIAL) += fwserial/
|
||||
obj-$(CONFIG_GOLDFISH) += goldfish/
|
||||
obj-$(CONFIG_LNET) += lustre/
|
||||
obj-$(CONFIG_DGNC) += dgnc/
|
||||
obj-$(CONFIG_MTD_SPINAND_MT29F) += mt29f_spinand/
|
||||
obj-$(CONFIG_GS_FPGABOOT) += gs_fpgaboot/
|
||||
|
@ -6,7 +6,7 @@ config ASHMEM
|
||||
bool "Enable the Anonymous Shared Memory Subsystem"
|
||||
default n
|
||||
depends on SHMEM
|
||||
---help---
|
||||
help
|
||||
The ashmem subsystem is a new shared memory allocator, similar to
|
||||
POSIX SHM but with different behavior and sporting a simpler
|
||||
file-based API.
|
||||
@ -14,6 +14,15 @@ config ASHMEM
|
||||
It is, in theory, a good memory allocator for low-memory devices,
|
||||
because it can discard shared memory units when under memory pressure.
|
||||
|
||||
config ANDROID_VSOC
|
||||
tristate "Android Virtual SoC support"
|
||||
default n
|
||||
depends on PCI_MSI
|
||||
help
|
||||
This option adds support for the Virtual SoC driver needed to boot
|
||||
a 'cuttlefish' Android image inside QEmu. The driver interacts with
|
||||
a QEmu ivshmem device. If built as a module, it will be called vsoc.
|
||||
|
||||
source "drivers/staging/android/ion/Kconfig"
|
||||
|
||||
endif # if ANDROID
|
||||
|
@ -3,3 +3,4 @@ ccflags-y += -I$(src) # needed for trace events
|
||||
obj-y += ion/
|
||||
|
||||
obj-$(CONFIG_ASHMEM) += ashmem.o
|
||||
obj-$(CONFIG_ANDROID_VSOC) += vsoc.o
|
||||
|
@ -11,5 +11,14 @@ ion/
|
||||
- Split /dev/ion up into multiple nodes (e.g. /dev/ion/heap0)
|
||||
- Better test framework (integration with VGEM was suggested)
|
||||
|
||||
vsoc.c, uapi/vsoc_shm.h
|
||||
- The current driver uses the same wait queue for all of the futexes in a
|
||||
region. This will cause false wakeups in regions with a large number of
|
||||
waiting threads. We should eventually use multiple queues and select the
|
||||
queue based on the region.
|
||||
- Add debugfs support for examining the permissions of regions.
|
||||
- Remove VSOC_WAIT_FOR_INCOMING_INTERRUPT ioctl. This functionality has been
|
||||
superseded by the futex and is there for legacy reasons.
|
||||
|
||||
Please send patches to Greg Kroah-Hartman <greg@kroah.com> and Cc:
|
||||
Arve Hjønnevåg <arve@android.com> and Riley Andrews <riandrews@android.com>
|
||||
|
@ -1,13 +1,9 @@
|
||||
// SPDX-License-Identifier: (GPL-2.0 OR Apache-2.0)
|
||||
/* SPDX-License-Identifier: GPL-2.0 OR Apache-2.0 */
|
||||
/*
|
||||
* include/linux/ashmem.h
|
||||
*
|
||||
* Copyright 2008 Google Inc.
|
||||
* Author: Robert Love
|
||||
*
|
||||
* This file is dual licensed. It may be redistributed and/or modified
|
||||
* under the terms of the Apache 2.0 License OR version 2 of the GNU
|
||||
* General Public License.
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_ASHMEM_H
|
||||
|
@ -3,7 +3,7 @@ menuconfig ION
|
||||
depends on HAVE_MEMBLOCK && HAS_DMA && MMU
|
||||
select GENERIC_ALLOCATOR
|
||||
select DMA_SHARED_BUFFER
|
||||
---help---
|
||||
help
|
||||
Choose this option to enable the ION Memory Manager,
|
||||
used by Android to efficiently allocate buffers
|
||||
from userspace that can be shared between drivers.
|
||||
|
@ -74,6 +74,8 @@ static struct ion_buffer *ion_buffer_create(struct ion_heap *heap,
|
||||
|
||||
buffer->heap = heap;
|
||||
buffer->flags = flags;
|
||||
buffer->dev = dev;
|
||||
buffer->size = len;
|
||||
|
||||
ret = heap->ops->allocate(heap, buffer, len, flags);
|
||||
|
||||
@ -93,11 +95,6 @@ static struct ion_buffer *ion_buffer_create(struct ion_heap *heap,
|
||||
goto err1;
|
||||
}
|
||||
|
||||
buffer->dev = dev;
|
||||
buffer->size = len;
|
||||
|
||||
buffer->dev = dev;
|
||||
buffer->size = len;
|
||||
INIT_LIST_HEAD(&buffer->attachments);
|
||||
mutex_init(&buffer->lock);
|
||||
mutex_lock(&dev->buffer_lock);
|
||||
@ -114,8 +111,11 @@ err2:
|
||||
|
||||
void ion_buffer_destroy(struct ion_buffer *buffer)
|
||||
{
|
||||
if (WARN_ON(buffer->kmap_cnt > 0))
|
||||
if (buffer->kmap_cnt > 0) {
|
||||
pr_warn_once("%s: buffer still mapped in the kernel\n",
|
||||
__func__);
|
||||
buffer->heap->ops->unmap_kernel(buffer->heap, buffer);
|
||||
}
|
||||
buffer->heap->ops->free(buffer);
|
||||
kfree(buffer);
|
||||
}
|
||||
@ -318,6 +318,7 @@ static int ion_dma_buf_begin_cpu_access(struct dma_buf *dmabuf,
|
||||
struct ion_buffer *buffer = dmabuf->priv;
|
||||
void *vaddr;
|
||||
struct ion_dma_buf_attachment *a;
|
||||
int ret = 0;
|
||||
|
||||
/*
|
||||
* TODO: Move this elsewhere because we don't always need a vaddr
|
||||
@ -325,6 +326,10 @@ static int ion_dma_buf_begin_cpu_access(struct dma_buf *dmabuf,
|
||||
if (buffer->heap->ops->map_kernel) {
|
||||
mutex_lock(&buffer->lock);
|
||||
vaddr = ion_buffer_kmap_get(buffer);
|
||||
if (IS_ERR(vaddr)) {
|
||||
ret = PTR_ERR(vaddr);
|
||||
goto unlock;
|
||||
}
|
||||
mutex_unlock(&buffer->lock);
|
||||
}
|
||||
|
||||
@ -333,9 +338,10 @@ static int ion_dma_buf_begin_cpu_access(struct dma_buf *dmabuf,
|
||||
dma_sync_sg_for_cpu(a->dev, a->table->sgl, a->table->nents,
|
||||
direction);
|
||||
}
|
||||
mutex_unlock(&buffer->lock);
|
||||
|
||||
return 0;
|
||||
unlock:
|
||||
mutex_unlock(&buffer->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ion_dma_buf_end_cpu_access(struct dma_buf *dmabuf,
|
||||
|
@ -1,4 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* drivers/staging/android/ion/ion.h
|
||||
*
|
||||
|
@ -32,6 +32,9 @@ static void ion_page_pool_add(struct ion_page_pool *pool, struct page *page)
|
||||
list_add_tail(&page->lru, &pool->low_items);
|
||||
pool->low_count++;
|
||||
}
|
||||
|
||||
mod_node_page_state(page_pgdat(page), NR_INDIRECTLY_RECLAIMABLE_BYTES,
|
||||
(1 << (PAGE_SHIFT + pool->order)));
|
||||
mutex_unlock(&pool->mutex);
|
||||
}
|
||||
|
||||
@ -50,6 +53,8 @@ static struct page *ion_page_pool_remove(struct ion_page_pool *pool, bool high)
|
||||
}
|
||||
|
||||
list_del(&page->lru);
|
||||
mod_node_page_state(page_pgdat(page), NR_INDIRECTLY_RECLAIMABLE_BYTES,
|
||||
-(1 << (PAGE_SHIFT + pool->order)));
|
||||
return page;
|
||||
}
|
||||
|
||||
|
@ -1,13 +1,9 @@
|
||||
// SPDX-License-Identifier: (GPL-2.0 OR Apache-2.0)
|
||||
/* SPDX-License-Identifier: GPL-2.0 OR Apache-2.0 */
|
||||
/*
|
||||
* drivers/staging/android/uapi/ashmem.h
|
||||
*
|
||||
* Copyright 2008 Google Inc.
|
||||
* Author: Robert Love
|
||||
*
|
||||
* This file is dual licensed. It may be redistributed and/or modified
|
||||
* under the terms of the Apache 2.0 License OR version 2 of the GNU
|
||||
* General Public License.
|
||||
*/
|
||||
|
||||
#ifndef _UAPI_LINUX_ASHMEM_H
|
||||
|
@ -1,4 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* drivers/staging/android/uapi/ion.h
|
||||
*
|
||||
|
295
drivers/staging/android/uapi/vsoc_shm.h
Normal file
295
drivers/staging/android/uapi/vsoc_shm.h
Normal file
@ -0,0 +1,295 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (C) 2017 Google, Inc.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _UAPI_LINUX_VSOC_SHM_H
|
||||
#define _UAPI_LINUX_VSOC_SHM_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
/**
|
||||
* A permission is a token that permits a receiver to read and/or write an area
|
||||
* of memory within a Vsoc region.
|
||||
*
|
||||
* An fd_scoped permission grants both read and write access, and can be
|
||||
* attached to a file description (see open(2)).
|
||||
* Ownership of the area can then be shared by passing a file descriptor
|
||||
* among processes.
|
||||
*
|
||||
* begin_offset and end_offset define the area of memory that is controlled by
|
||||
* the permission. owner_offset points to a word, also in shared memory, that
|
||||
* controls ownership of the area.
|
||||
*
|
||||
* ownership of the region expires when the associated file description is
|
||||
* released.
|
||||
*
|
||||
* At most one permission can be attached to each file description.
|
||||
*
|
||||
* This is useful when implementing HALs like gralloc that scope and pass
|
||||
* ownership of shared resources via file descriptors.
|
||||
*
|
||||
* The caller is responsibe for doing any fencing.
|
||||
*
|
||||
* The calling process will normally identify a currently free area of
|
||||
* memory. It will construct a proposed fd_scoped_permission_arg structure:
|
||||
*
|
||||
* begin_offset and end_offset describe the area being claimed
|
||||
*
|
||||
* owner_offset points to the location in shared memory that indicates the
|
||||
* owner of the area.
|
||||
*
|
||||
* owned_value is the value that will be stored in owner_offset iff the
|
||||
* permission can be granted. It must be different than VSOC_REGION_FREE.
|
||||
*
|
||||
* Two fd_scoped_permission structures are compatible if they vary only by
|
||||
* their owned_value fields.
|
||||
*
|
||||
* The driver ensures that, for any group of simultaneous callers proposing
|
||||
* compatible fd_scoped_permissions, it will accept exactly one of the
|
||||
* propopsals. The other callers will get a failure with errno of EAGAIN.
|
||||
*
|
||||
* A process receiving a file descriptor can identify the region being
|
||||
* granted using the VSOC_GET_FD_SCOPED_PERMISSION ioctl.
|
||||
*/
|
||||
struct fd_scoped_permission {
|
||||
__u32 begin_offset;
|
||||
__u32 end_offset;
|
||||
__u32 owner_offset;
|
||||
__u32 owned_value;
|
||||
};
|
||||
|
||||
/*
|
||||
* This value represents a free area of memory. The driver expects to see this
|
||||
* value at owner_offset when creating a permission otherwise it will not do it,
|
||||
* and will write this value back once the permission is no longer needed.
|
||||
*/
|
||||
#define VSOC_REGION_FREE ((__u32)0)
|
||||
|
||||
/**
|
||||
* ioctl argument for VSOC_CREATE_FD_SCOPE_PERMISSION
|
||||
*/
|
||||
struct fd_scoped_permission_arg {
|
||||
struct fd_scoped_permission perm;
|
||||
__s32 managed_region_fd;
|
||||
};
|
||||
|
||||
#define VSOC_NODE_FREE ((__u32)0)
|
||||
|
||||
/*
|
||||
* Describes a signal table in shared memory. Each non-zero entry in the
|
||||
* table indicates that the receiver should signal the futex at the given
|
||||
* offset. Offsets are relative to the region, not the shared memory window.
|
||||
*
|
||||
* interrupt_signalled_offset is used to reliably signal interrupts across the
|
||||
* vmm boundary. There are two roles: transmitter and receiver. For example,
|
||||
* in the host_to_guest_signal_table the host is the transmitter and the
|
||||
* guest is the receiver. The protocol is as follows:
|
||||
*
|
||||
* 1. The transmitter should convert the offset of the futex to an offset
|
||||
* in the signal table [0, (1 << num_nodes_lg2))
|
||||
* The transmitter can choose any appropriate hashing algorithm, including
|
||||
* hash = futex_offset & ((1 << num_nodes_lg2) - 1)
|
||||
*
|
||||
* 3. The transmitter should atomically compare and swap futex_offset with 0
|
||||
* at hash. There are 3 possible outcomes
|
||||
* a. The swap fails because the futex_offset is already in the table.
|
||||
* The transmitter should stop.
|
||||
* b. Some other offset is in the table. This is a hash collision. The
|
||||
* transmitter should move to another table slot and try again. One
|
||||
* possible algorithm:
|
||||
* hash = (hash + 1) & ((1 << num_nodes_lg2) - 1)
|
||||
* c. The swap worked. Continue below.
|
||||
*
|
||||
* 3. The transmitter atomically swaps 1 with the value at the
|
||||
* interrupt_signalled_offset. There are two outcomes:
|
||||
* a. The prior value was 1. In this case an interrupt has already been
|
||||
* posted. The transmitter is done.
|
||||
* b. The prior value was 0, indicating that the receiver may be sleeping.
|
||||
* The transmitter will issue an interrupt.
|
||||
*
|
||||
* 4. On waking the receiver immediately exchanges a 0 with the
|
||||
* interrupt_signalled_offset. If it receives a 0 then this a spurious
|
||||
* interrupt. That may occasionally happen in the current protocol, but
|
||||
* should be rare.
|
||||
*
|
||||
* 5. The receiver scans the signal table by atomicaly exchanging 0 at each
|
||||
* location. If a non-zero offset is returned from the exchange the
|
||||
* receiver wakes all sleepers at the given offset:
|
||||
* futex((int*)(region_base + old_value), FUTEX_WAKE, MAX_INT);
|
||||
*
|
||||
* 6. The receiver thread then does a conditional wait, waking immediately
|
||||
* if the value at interrupt_signalled_offset is non-zero. This catches cases
|
||||
* here additional signals were posted while the table was being scanned.
|
||||
* On the guest the wait is handled via the VSOC_WAIT_FOR_INCOMING_INTERRUPT
|
||||
* ioctl.
|
||||
*/
|
||||
struct vsoc_signal_table_layout {
|
||||
/* log_2(Number of signal table entries) */
|
||||
__u32 num_nodes_lg2;
|
||||
/*
|
||||
* Offset to the first signal table entry relative to the start of the
|
||||
* region
|
||||
*/
|
||||
__u32 futex_uaddr_table_offset;
|
||||
/*
|
||||
* Offset to an atomic_t / atomic uint32_t. A non-zero value indicates
|
||||
* that one or more offsets are currently posted in the table.
|
||||
* semi-unique access to an entry in the table
|
||||
*/
|
||||
__u32 interrupt_signalled_offset;
|
||||
};
|
||||
|
||||
#define VSOC_REGION_WHOLE ((__s32)0)
|
||||
#define VSOC_DEVICE_NAME_SZ 16
|
||||
|
||||
/**
|
||||
* Each HAL would (usually) talk to a single device region
|
||||
* Mulitple entities care about these regions:
|
||||
* - The ivshmem_server will populate the regions in shared memory
|
||||
* - The guest kernel will read the region, create minor device nodes, and
|
||||
* allow interested parties to register for FUTEX_WAKE events in the region
|
||||
* - HALs will access via the minor device nodes published by the guest kernel
|
||||
* - Host side processes will access the region via the ivshmem_server:
|
||||
* 1. Pass name to ivshmem_server at a UNIX socket
|
||||
* 2. ivshmemserver will reply with 2 fds:
|
||||
* - host->guest doorbell fd
|
||||
* - guest->host doorbell fd
|
||||
* - fd for the shared memory region
|
||||
* - region offset
|
||||
* 3. Start a futex receiver thread on the doorbell fd pointed at the
|
||||
* signal_nodes
|
||||
*/
|
||||
struct vsoc_device_region {
|
||||
__u16 current_version;
|
||||
__u16 min_compatible_version;
|
||||
__u32 region_begin_offset;
|
||||
__u32 region_end_offset;
|
||||
__u32 offset_of_region_data;
|
||||
struct vsoc_signal_table_layout guest_to_host_signal_table;
|
||||
struct vsoc_signal_table_layout host_to_guest_signal_table;
|
||||
/* Name of the device. Must always be terminated with a '\0', so
|
||||
* the longest supported device name is 15 characters.
|
||||
*/
|
||||
char device_name[VSOC_DEVICE_NAME_SZ];
|
||||
/* There are two ways that permissions to access regions are handled:
|
||||
* - When subdivided_by is VSOC_REGION_WHOLE, any process that can
|
||||
* open the device node for the region gains complete access to it.
|
||||
* - When subdivided is set processes that open the region cannot
|
||||
* access it. Access to a sub-region must be established by invoking
|
||||
* the VSOC_CREATE_FD_SCOPE_PERMISSION ioctl on the region
|
||||
* referenced in subdivided_by, providing a fileinstance
|
||||
* (represented by a fd) opened on this region.
|
||||
*/
|
||||
__u32 managed_by;
|
||||
};
|
||||
|
||||
/*
|
||||
* The vsoc layout descriptor.
|
||||
* The first 4K should be reserved for the shm header and region descriptors.
|
||||
* The regions should be page aligned.
|
||||
*/
|
||||
|
||||
struct vsoc_shm_layout_descriptor {
|
||||
__u16 major_version;
|
||||
__u16 minor_version;
|
||||
|
||||
/* size of the shm. This may be redundant but nice to have */
|
||||
__u32 size;
|
||||
|
||||
/* number of shared memory regions */
|
||||
__u32 region_count;
|
||||
|
||||
/* The offset to the start of region descriptors */
|
||||
__u32 vsoc_region_desc_offset;
|
||||
};
|
||||
|
||||
/*
|
||||
* This specifies the current version that should be stored in
|
||||
* vsoc_shm_layout_descriptor.major_version and
|
||||
* vsoc_shm_layout_descriptor.minor_version.
|
||||
* It should be updated only if the vsoc_device_region and
|
||||
* vsoc_shm_layout_descriptor structures have changed.
|
||||
* Versioning within each region is transferred
|
||||
* via the min_compatible_version and current_version fields in
|
||||
* vsoc_device_region. The driver does not consult these fields: they are left
|
||||
* for the HALs and host processes and will change independently of the layout
|
||||
* version.
|
||||
*/
|
||||
#define CURRENT_VSOC_LAYOUT_MAJOR_VERSION 2
|
||||
#define CURRENT_VSOC_LAYOUT_MINOR_VERSION 0
|
||||
|
||||
#define VSOC_CREATE_FD_SCOPED_PERMISSION \
|
||||
_IOW(0xF5, 0, struct fd_scoped_permission)
|
||||
#define VSOC_GET_FD_SCOPED_PERMISSION _IOR(0xF5, 1, struct fd_scoped_permission)
|
||||
|
||||
/*
|
||||
* This is used to signal the host to scan the guest_to_host_signal_table
|
||||
* for new futexes to wake. This sends an interrupt if one is not already
|
||||
* in flight.
|
||||
*/
|
||||
#define VSOC_MAYBE_SEND_INTERRUPT_TO_HOST _IO(0xF5, 2)
|
||||
|
||||
/*
|
||||
* When this returns the guest will scan host_to_guest_signal_table to
|
||||
* check for new futexes to wake.
|
||||
*/
|
||||
/* TODO(ghartman): Consider moving this to the bottom half */
|
||||
#define VSOC_WAIT_FOR_INCOMING_INTERRUPT _IO(0xF5, 3)
|
||||
|
||||
/*
|
||||
* Guest HALs will use this to retrieve the region description after
|
||||
* opening their device node.
|
||||
*/
|
||||
#define VSOC_DESCRIBE_REGION _IOR(0xF5, 4, struct vsoc_device_region)
|
||||
|
||||
/*
|
||||
* Wake any threads that may be waiting for a host interrupt on this region.
|
||||
* This is mostly used during shutdown.
|
||||
*/
|
||||
#define VSOC_SELF_INTERRUPT _IO(0xF5, 5)
|
||||
|
||||
/*
|
||||
* This is used to signal the host to scan the guest_to_host_signal_table
|
||||
* for new futexes to wake. This sends an interrupt unconditionally.
|
||||
*/
|
||||
#define VSOC_SEND_INTERRUPT_TO_HOST _IO(0xF5, 6)
|
||||
|
||||
enum wait_types {
|
||||
VSOC_WAIT_UNDEFINED = 0,
|
||||
VSOC_WAIT_IF_EQUAL = 1,
|
||||
VSOC_WAIT_IF_EQUAL_TIMEOUT = 2
|
||||
};
|
||||
|
||||
/*
|
||||
* Wait for a condition to be true
|
||||
*
|
||||
* Note, this is sized and aligned so the 32 bit and 64 bit layouts are
|
||||
* identical.
|
||||
*/
|
||||
struct vsoc_cond_wait {
|
||||
/* Input: Offset of the 32 bit word to check */
|
||||
__u32 offset;
|
||||
/* Input: Value that will be compared with the offset */
|
||||
__u32 value;
|
||||
/* Monotonic time to wake at in seconds */
|
||||
__u64 wake_time_sec;
|
||||
/* Input: Monotonic time to wait in nanoseconds */
|
||||
__u32 wake_time_nsec;
|
||||
/* Input: Type of wait */
|
||||
__u32 wait_type;
|
||||
/* Output: Number of times the thread woke before returning. */
|
||||
__u32 wakes;
|
||||
/* Ensure that we're 8-byte aligned and 8 byte length for 32/64 bit
|
||||
* compatibility.
|
||||
*/
|
||||
__u32 reserved_1;
|
||||
};
|
||||
|
||||
#define VSOC_COND_WAIT _IOWR(0xF5, 7, struct vsoc_cond_wait)
|
||||
|
||||
/* Wake any local threads waiting at the offset given in arg */
|
||||
#define VSOC_COND_WAKE _IO(0xF5, 8)
|
||||
|
||||
#endif /* _UAPI_LINUX_VSOC_SHM_H */
|
1152
drivers/staging/android/vsoc.c
Normal file
1152
drivers/staging/android/vsoc.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,3 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Staging board support for Armadillo 800 eva.
|
||||
* Enable not-yet-DT-capable devices here.
|
||||
@ -6,15 +7,6 @@
|
||||
*
|
||||
* Copyright (C) 2012 Renesas Solutions Corp.
|
||||
* Copyright (C) 2012 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/dma-mapping.h>
|
||||
|
@ -1,10 +1,7 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2014 Magnus Damm
|
||||
* Copyright (C) 2015 Glider bvba
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU General Public
|
||||
* License. See the file "COPYING" in the main directory of this archive
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "board_staging: " fmt
|
||||
|
@ -34,7 +34,7 @@
|
||||
struct comedi32_chaninfo_struct {
|
||||
unsigned int subdev;
|
||||
compat_uptr_t maxdata_list; /* 32-bit 'unsigned int *' */
|
||||
compat_uptr_t flaglist; /* 32-bit 'unsigned int *' */
|
||||
compat_uptr_t flaglist; /* 32-bit 'unsigned int *' */
|
||||
compat_uptr_t rangelist; /* 32-bit 'unsigned int *' */
|
||||
unsigned int unused[4];
|
||||
};
|
||||
@ -57,16 +57,16 @@ struct comedi32_cmd_struct {
|
||||
unsigned int scan_end_arg;
|
||||
unsigned int stop_src;
|
||||
unsigned int stop_arg;
|
||||
compat_uptr_t chanlist; /* 32-bit 'unsigned int *' */
|
||||
compat_uptr_t chanlist; /* 32-bit 'unsigned int *' */
|
||||
unsigned int chanlist_len;
|
||||
compat_uptr_t data; /* 32-bit 'short *' */
|
||||
compat_uptr_t data; /* 32-bit 'short *' */
|
||||
unsigned int data_len;
|
||||
};
|
||||
|
||||
struct comedi32_insn_struct {
|
||||
unsigned int insn;
|
||||
unsigned int n;
|
||||
compat_uptr_t data; /* 32-bit 'unsigned int *' */
|
||||
compat_uptr_t data; /* 32-bit 'unsigned int *' */
|
||||
unsigned int subdev;
|
||||
unsigned int chanspec;
|
||||
unsigned int unused[3];
|
||||
@ -74,7 +74,7 @@ struct comedi32_insn_struct {
|
||||
|
||||
struct comedi32_insnlist_struct {
|
||||
unsigned int n_insns;
|
||||
compat_uptr_t insns; /* 32-bit 'struct comedi_insn *' */
|
||||
compat_uptr_t insns; /* 32-bit 'struct comedi_insn *' */
|
||||
};
|
||||
|
||||
/* Handle translated ioctl. */
|
||||
@ -194,7 +194,7 @@ static int get_compat_cmd(struct comedi_cmd __user *cmd,
|
||||
err |= __put_user(temp.uint, &cmd->stop_arg);
|
||||
err |= __get_user(temp.uptr, &cmd32->chanlist);
|
||||
err |= __put_user((unsigned int __force *)compat_ptr(temp.uptr),
|
||||
&cmd->chanlist);
|
||||
&cmd->chanlist);
|
||||
err |= __get_user(temp.uint, &cmd32->chanlist_len);
|
||||
err |= __put_user(temp.uint, &cmd->chanlist_len);
|
||||
err |= __get_user(temp.uptr, &cmd32->data);
|
||||
|
@ -1,6 +1,5 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* comedi_usb.h
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/* comedi_usb.h
|
||||
* header file for USB Comedi drivers
|
||||
*
|
||||
* COMEDI - Linux Control and Measurement Device Interface
|
||||
|
@ -1,4 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* comedidev.h
|
||||
* header file for kernel-only structures, variables, and constants
|
||||
|
@ -2268,14 +2268,14 @@ static inline unsigned int dma_transfer_size(struct comedi_device *dev)
|
||||
}
|
||||
|
||||
static u32 ai_convert_counter_6xxx(const struct comedi_device *dev,
|
||||
const struct comedi_cmd *cmd)
|
||||
const struct comedi_cmd *cmd)
|
||||
{
|
||||
/* supposed to load counter with desired divisor minus 3 */
|
||||
return cmd->convert_arg / TIMER_BASE - 3;
|
||||
}
|
||||
|
||||
static u32 ai_scan_counter_6xxx(struct comedi_device *dev,
|
||||
struct comedi_cmd *cmd)
|
||||
struct comedi_cmd *cmd)
|
||||
{
|
||||
u32 count;
|
||||
|
||||
@ -2296,7 +2296,7 @@ static u32 ai_scan_counter_6xxx(struct comedi_device *dev,
|
||||
}
|
||||
|
||||
static u32 ai_convert_counter_4020(struct comedi_device *dev,
|
||||
struct comedi_cmd *cmd)
|
||||
struct comedi_cmd *cmd)
|
||||
{
|
||||
struct pcidas64_private *devpriv = dev->private;
|
||||
unsigned int divisor;
|
||||
|
@ -1,6 +1,6 @@
|
||||
config USB_EMXX
|
||||
tristate "EMXX USB Function Device Controller"
|
||||
depends on USB_GADGET && (ARCH_SHMOBILE || (ARM && COMPILE_TEST))
|
||||
depends on USB_GADGET && (ARCH_RENESAS || (ARM && COMPILE_TEST))
|
||||
help
|
||||
The Emma Mobile series of SoCs from Renesas Electronics and
|
||||
former NEC Electronics include USB Function hardware.
|
||||
|
@ -1,4 +1,6 @@
|
||||
* add clock framework support (platform device with CCF needs special care)
|
||||
* break out board-specific VBUS GPIO to work with multiplatform
|
||||
* convert VBUS GPIO to use GPIO descriptors from <linux/gpio/consumer.h>
|
||||
and stop using the old GPIO API
|
||||
* DT bindings
|
||||
* move driver into drivers/usb/gadget/
|
||||
|
4
drivers/staging/fbtft/TODO
Normal file
4
drivers/staging/fbtft/TODO
Normal file
@ -0,0 +1,4 @@
|
||||
* convert all uses of the old GPIO API from <linux/gpio.h> to the
|
||||
GPIO descriptor API in <linux/gpio/consumer.h> and look up GPIO
|
||||
lines from device tree, ACPI or board files, board files should
|
||||
use <linux/gpio/machine.h>
|
@ -22,10 +22,13 @@ void func(struct fbtft_par *par, int len, ...) \
|
||||
if (unlikely(par->debug & DEBUG_WRITE_REGISTER)) { \
|
||||
va_start(args, len); \
|
||||
for (i = 0; i < len; i++) { \
|
||||
buf[i] = modifier((data_type)va_arg(args, unsigned int)); \
|
||||
buf[i] = modifier((data_type)va_arg(args, \
|
||||
unsigned int)); \
|
||||
} \
|
||||
va_end(args); \
|
||||
fbtft_par_dbg_hex(DEBUG_WRITE_REGISTER, par, par->info->device, buffer_type, buf, len, "%s: ", __func__); \
|
||||
fbtft_par_dbg_hex(DEBUG_WRITE_REGISTER, par, \
|
||||
par->info->device, buffer_type, buf, len, \
|
||||
"%s: ", __func__); \
|
||||
} \
|
||||
\
|
||||
va_start(args, len); \
|
||||
@ -37,7 +40,8 @@ void func(struct fbtft_par *par, int len, ...) \
|
||||
} \
|
||||
\
|
||||
*buf = modifier((data_type)va_arg(args, unsigned int)); \
|
||||
ret = fbtft_write_buf_dc(par, par->buf, sizeof(data_type) + offset, 0); \
|
||||
ret = fbtft_write_buf_dc(par, par->buf, sizeof(data_type) + offset, \
|
||||
0); \
|
||||
if (ret < 0) \
|
||||
goto out; \
|
||||
len--; \
|
||||
@ -48,7 +52,8 @@ void func(struct fbtft_par *par, int len, ...) \
|
||||
if (len) { \
|
||||
i = len; \
|
||||
while (i--) \
|
||||
*buf++ = modifier((data_type)va_arg(args, unsigned int)); \
|
||||
*buf++ = modifier((data_type)va_arg(args, \
|
||||
unsigned int)); \
|
||||
fbtft_write_buf_dc(par, par->buf, \
|
||||
len * (sizeof(data_type) + offset), 1); \
|
||||
} \
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user