mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2025-01-19 07:26:45 +07:00
Staging/IIO patches for 4.20-rc1
Here is the big staging and IIO driver pull request for 4.20-rc1. There are lots of things here, we ended up adding more lines than removing, thanks to a large influx of Comedi National Instrument device support. Someday soon we need to get comedi out of staging... Other than the comedi drivers, the "big" things here are: - new iio drivers - delete dgnc driver (no one used it and no one had the hardware anymore) - vbox driver updates and fixes - erofs fixes - tons and tons of tiny checkpatch fixes for almost all staging drivers All of these have been in linux-next, with the last few happening a bit "late" due to them getting stuck on my laptop during travel to the Mantainers summit. When merging with your tree, there will be 2 merge conflicts, both files will be simple to resolve, just delete them :) Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCW9bSGA8cZ3JlZ0Brcm9h aC5jb20ACgkQMUfUDdst+yk5eACfYp73m9tLO22rnBcXJ73bWAYSTOMAn2GEL4Nc LZBXs8QvvJIwfqmi7ofn =UWJn -----END PGP SIGNATURE----- Merge tag 'staging-4.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging Pull staging/IIO driver updates from Greg KH: "Here is the big staging and IIO driver pull request for 4.20-rc1. There are lots of things here, we ended up adding more lines than removing, thanks to a large influx of Comedi National Instrument device support. Someday soon we need to get comedi out of staging... Other than the comedi drivers, the "big" things here are: - new iio drivers - delete dgnc driver (no one used it and no one had the hardware anymore) - vbox driver updates and fixes - erofs fixes - tons and tons of tiny checkpatch fixes for almost all staging drivers All of these have been in linux-next, with the last few happening a bit "late" due to them getting stuck on my laptop during travel to the Mantainers summit" * tag 'staging-4.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging: (690 commits) staging: gasket: Fix sparse "incorrect type in assignment" warnings. staging: gasket: remove debug logs for callback invocation staging: gasket: remove debug logs in page table mapping calls staging: rtl8188eu: core: Use sizeof(*p) instead of sizeof(struct P) for memory allocation staging: ks7010: Remove extra blank line staging: gasket: Remove extra blank line staging: media: davinci_vpfe: Fix spelling mistake in enum staging: speakup: Add a pair of braces staging: wlan-ng: Replace long int with long staging: MAINTAINERS: remove obsolete IPX staging directory staging: MAINTAINERS: remove NCP filesystem entry staging: rtl8188eu: cleanup comparsions to false staging: gasket: Update device virtual address comment staging: gasket: sysfs: fix attribute release comment staging: gasket: apex: fix sysfs_show staging: gasket: page_table: simplify gasket_components_to_dev_address staging: gasket: page_table: fix comment in components_to_dev_address staging: gasket: page table: fixup error path allocating coherent mem staging: gasket: page_table: rearrange gasket_page_table_entry staging: gasket: page_table: remove unnecessary PTE status set to free ...
This commit is contained in:
commit
738b04fba1
@ -199,7 +199,7 @@ Description:
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_positionrelative_x_raw
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_positionrelative_y_raw
|
||||
KernelVersion: 4.18
|
||||
KernelVersion: 4.19
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Relative position in direction x or y on a pad (may be
|
||||
|
33
Documentation/devicetree/bindings/iio/accel/adxl372.txt
Normal file
33
Documentation/devicetree/bindings/iio/accel/adxl372.txt
Normal file
@ -0,0 +1,33 @@
|
||||
Analog Devices ADXL372 3-Axis, +/-(200g) Digital Accelerometer
|
||||
|
||||
http://www.analog.com/media/en/technical-documentation/data-sheets/adxl372.pdf
|
||||
|
||||
Required properties:
|
||||
- compatible : should be "adi,adxl372"
|
||||
- reg: the I2C address or SPI chip select number for the device
|
||||
|
||||
Required properties for SPI bus usage:
|
||||
- spi-max-frequency: Max SPI frequency to use
|
||||
|
||||
Optional properties:
|
||||
- interrupts: interrupt mapping for IRQ as documented in
|
||||
Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
|
||||
|
||||
Example for a I2C device node:
|
||||
|
||||
accelerometer@53 {
|
||||
compatible = "adi,adxl372";
|
||||
reg = <0x53>;
|
||||
interrupt-parent = <&gpio>;
|
||||
interrupts = <25 IRQ_TYPE_EDGE_FALLING>;
|
||||
};
|
||||
|
||||
Example for a SPI device node:
|
||||
|
||||
accelerometer@0 {
|
||||
compatible = "adi,adxl372";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <1000000>;
|
||||
interrupt-parent = <&gpio>;
|
||||
interrupts = <25 IRQ_TYPE_EDGE_FALLING>;
|
||||
};
|
30
Documentation/devicetree/bindings/iio/adc/mcp3911.txt
Normal file
30
Documentation/devicetree/bindings/iio/adc/mcp3911.txt
Normal file
@ -0,0 +1,30 @@
|
||||
* Microchip MCP3911 Dual channel analog front end (ADC)
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "microchip,mcp3911"
|
||||
- reg: SPI chip select number for the device
|
||||
|
||||
Recommended properties:
|
||||
- spi-max-frequency: Definition as per
|
||||
Documentation/devicetree/bindings/spi/spi-bus.txt.
|
||||
Max frequency for this chip is 20MHz.
|
||||
|
||||
Optional properties:
|
||||
- clocks: Phandle and clock identifier for sampling clock
|
||||
- interrupt-parent: Phandle to the parent interrupt controller
|
||||
- interrupts: IRQ line for the ADC
|
||||
- microchip,device-addr: Device address when multiple MCP3911 chips are present on the
|
||||
same SPI bus. Valid values are 0-3. Defaults to 0.
|
||||
- vref-supply: Phandle to the external reference voltage supply.
|
||||
|
||||
Example:
|
||||
adc@0 {
|
||||
compatible = "microchip,mcp3911";
|
||||
reg = <0>;
|
||||
interrupt-parent = <&gpio5>;
|
||||
interrupts = <15 IRQ_TYPE_EDGE_RISING>;
|
||||
spi-max-frequency = <20000000>;
|
||||
microchip,device-addr = <0>;
|
||||
vref-supply = <&vref_reg>;
|
||||
clocks = <&xtal>;
|
||||
};
|
@ -1,7 +1,9 @@
|
||||
Qualcomm's SPMI PMIC voltage ADC
|
||||
Qualcomm's SPMI PMIC ADC
|
||||
|
||||
SPMI PMIC voltage ADC (VADC) provides interface to clients to read
|
||||
voltage. The VADC is a 15-bit sigma-delta ADC.
|
||||
- SPMI PMIC voltage ADC (VADC) provides interface to clients to read
|
||||
voltage. The VADC is a 15-bit sigma-delta ADC.
|
||||
- SPMI PMIC5 voltage ADC (ADC) provides interface to clients to read
|
||||
voltage. The VADC is a 16-bit sigma-delta ADC.
|
||||
|
||||
VADC node:
|
||||
|
||||
@ -9,11 +11,13 @@ VADC node:
|
||||
Usage: required
|
||||
Value type: <string>
|
||||
Definition: Should contain "qcom,spmi-vadc".
|
||||
Should contain "qcom,spmi-adc5" for PMIC5 ADC driver.
|
||||
Should contain "qcom,spmi-adc-rev2" for PMIC rev2 ADC driver.
|
||||
|
||||
- reg:
|
||||
Usage: required
|
||||
Value type: <prop-encoded-array>
|
||||
Definition: VADC base address and length in the SPMI PMIC register map.
|
||||
Definition: VADC base address in the SPMI PMIC register map.
|
||||
|
||||
- #address-cells:
|
||||
Usage: required
|
||||
@ -45,13 +49,26 @@ Channel node properties:
|
||||
Definition: ADC channel number.
|
||||
See include/dt-bindings/iio/qcom,spmi-vadc.h
|
||||
|
||||
- label:
|
||||
Usage: required for "qcom,spmi-adc5" and "qcom,spmi-adc-rev2"
|
||||
Value type: <empty>
|
||||
Definition: ADC input of the platform as seen in the schematics.
|
||||
For thermistor inputs connected to generic AMUX or GPIO inputs
|
||||
these can vary across platform for the same pins. Hence select
|
||||
the platform schematics name for this channel.
|
||||
|
||||
- qcom,decimation:
|
||||
Usage: optional
|
||||
Value type: <u32>
|
||||
Definition: This parameter is used to decrease ADC sampling rate.
|
||||
Quicker measurements can be made by reducing decimation ratio.
|
||||
Valid values are 512, 1024, 2048, 4096.
|
||||
If property is not found, default value of 512 will be used.
|
||||
- For compatible property "qcom,spmi-vadc", valid values are
|
||||
512, 1024, 2048, 4096. If property is not found, default value
|
||||
of 512 will be used.
|
||||
- For compatible property "qcom,spmi-adc5", valid values are 250, 420
|
||||
and 840. If property is not found, default value of 840 is used.
|
||||
- For compatible property "qcom,spmi-adc-rev2", valid values are 256,
|
||||
512 and 1024. If property is not present, default value is 1024.
|
||||
|
||||
- qcom,pre-scaling:
|
||||
Usage: optional
|
||||
@ -66,21 +83,38 @@ Channel node properties:
|
||||
- qcom,ratiometric:
|
||||
Usage: optional
|
||||
Value type: <empty>
|
||||
Definition: Channel calibration type. If this property is specified
|
||||
VADC will use the VDD reference (1.8V) and GND for channel
|
||||
calibration. If property is not found, channel will be
|
||||
calibrated with 0.625V and 1.25V reference channels, also
|
||||
known as absolute calibration.
|
||||
Definition: Channel calibration type.
|
||||
- For compatible property "qcom,spmi-vadc", if this property is
|
||||
specified VADC will use the VDD reference (1.8V) and GND for
|
||||
channel calibration. If property is not found, channel will be
|
||||
calibrated with 0.625V and 1.25V reference channels, also
|
||||
known as absolute calibration.
|
||||
- For compatible property "qcom,spmi-adc5" and "qcom,spmi-adc-rev2",
|
||||
if this property is specified VADC will use the VDD reference
|
||||
(1.875V) and GND for channel calibration. If property is not found,
|
||||
channel will be calibrated with 0V and 1.25V reference channels,
|
||||
also known as absolute calibration.
|
||||
|
||||
- qcom,hw-settle-time:
|
||||
Usage: optional
|
||||
Value type: <u32>
|
||||
Definition: Time between AMUX getting configured and the ADC starting
|
||||
conversion. Delay = 100us * (value) for value < 11, and
|
||||
2ms * (value - 10) otherwise.
|
||||
Valid values are: 0, 100, 200, 300, 400, 500, 600, 700, 800,
|
||||
900 us and 1, 2, 4, 6, 8, 10 ms
|
||||
If property is not found, channel will use 0us.
|
||||
conversion. The 'hw_settle_time' is an index used from valid values
|
||||
and programmed in hardware to achieve the hardware settling delay.
|
||||
- For compatible property "qcom,spmi-vadc" and "qcom,spmi-adc-rev2",
|
||||
Delay = 100us * (hw_settle_time) for hw_settle_time < 11,
|
||||
and 2ms * (hw_settle_time - 10) otherwise.
|
||||
Valid values are: 0, 100, 200, 300, 400, 500, 600, 700, 800,
|
||||
900 us and 1, 2, 4, 6, 8, 10 ms.
|
||||
If property is not found, channel will use 0us.
|
||||
- For compatible property "qcom,spmi-adc5", delay = 15us for
|
||||
value 0, 100us * (value) for values < 11,
|
||||
and 2ms * (value - 10) otherwise.
|
||||
Valid values are: 15, 100, 200, 300, 400, 500, 600, 700, 800,
|
||||
900 us and 1, 2, 4, 6, 8, 10 ms
|
||||
Certain controller digital versions have valid values of
|
||||
15, 100, 200, 300, 400, 500, 600, 700, 1, 2, 4, 8, 16, 32, 64, 128 ms
|
||||
If property is not found, channel will use 15us.
|
||||
|
||||
- qcom,avg-samples:
|
||||
Usage: optional
|
||||
@ -89,13 +123,18 @@ Channel node properties:
|
||||
Averaging provides the option to obtain a single measurement
|
||||
from the ADC that is an average of multiple samples. The value
|
||||
selected is 2^(value).
|
||||
Valid values are: 1, 2, 4, 8, 16, 32, 64, 128, 256, 512
|
||||
If property is not found, 1 sample will be used.
|
||||
- For compatible property "qcom,spmi-vadc", valid values
|
||||
are: 1, 2, 4, 8, 16, 32, 64, 128, 256, 512
|
||||
If property is not found, 1 sample will be used.
|
||||
- For compatible property "qcom,spmi-adc5" and "qcom,spmi-adc-rev2",
|
||||
valid values are: 1, 2, 4, 8, 16
|
||||
If property is not found, 1 sample will be used.
|
||||
|
||||
NOTE:
|
||||
|
||||
Following channels, also known as reference point channels, are used for
|
||||
result calibration and their channel configuration nodes should be defined:
|
||||
For compatible property "qcom,spmi-vadc" following channels, also known as
|
||||
reference point channels, are used for result calibration and their channel
|
||||
configuration nodes should be defined:
|
||||
VADC_REF_625MV and/or VADC_SPARE1(based on PMIC version) VADC_REF_1250MV,
|
||||
VADC_GND_REF and VADC_VDD_VADC.
|
||||
|
||||
@ -104,7 +143,7 @@ Example:
|
||||
/* VADC node */
|
||||
pmic_vadc: vadc@3100 {
|
||||
compatible = "qcom,spmi-vadc";
|
||||
reg = <0x3100 0x100>;
|
||||
reg = <0x3100>;
|
||||
interrupts = <0x0 0x31 0x0 IRQ_TYPE_EDGE_RISING>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
@ -12,6 +12,8 @@ Required properties:
|
||||
- interrupts: The interrupt number for the ADC device.
|
||||
- #io-channel-cells: Number of cells in an IIO specifier.
|
||||
- hwlocks: Reference to a phandle of a hwlock provider node.
|
||||
- nvmem-cells: A phandle to the calibration cells provided by eFuse device.
|
||||
- nvmem-cell-names: Should be "big_scale_calib", "small_scale_calib".
|
||||
|
||||
Example:
|
||||
|
||||
@ -32,5 +34,7 @@ Example:
|
||||
interrupts = <0 IRQ_TYPE_LEVEL_HIGH>;
|
||||
#io-channel-cells = <1>;
|
||||
hwlocks = <&hwlock 4>;
|
||||
nvmem-cells = <&adc_big_scale>, <&adc_small_scale>;
|
||||
nvmem-cell-names = "big_scale_calib", "small_scale_calib";
|
||||
};
|
||||
};
|
||||
|
@ -50,6 +50,9 @@ Required properties:
|
||||
|
||||
Optional properties:
|
||||
|
||||
- reset-gpios : GPIO spec for the RESET pin. If specified, it will be
|
||||
asserted during driver probe.
|
||||
|
||||
- adi,dc-dc-ilim-microamp: The dc-to-dc converter current limit
|
||||
The following values are currently supported [uA]:
|
||||
* 150000
|
||||
@ -71,6 +74,8 @@ AD5758 Example:
|
||||
spi-max-frequency = <1000000>;
|
||||
spi-cpha;
|
||||
|
||||
reset-gpios = <&gpio 22 0>;
|
||||
|
||||
adi,dc-dc-mode = <2>;
|
||||
adi,range-microvolt = <0 10000000>;
|
||||
adi,dc-dc-ilim-microamp = <200000>;
|
||||
|
21
Documentation/devicetree/bindings/iio/dac/ltc1660.txt
Normal file
21
Documentation/devicetree/bindings/iio/dac/ltc1660.txt
Normal file
@ -0,0 +1,21 @@
|
||||
* Linear Technology Micropower octal 8-Bit and 10-Bit DACs
|
||||
|
||||
Required properties:
|
||||
- compatible: Must be one of the following:
|
||||
"lltc,ltc1660"
|
||||
"lltc,ltc1665"
|
||||
- reg: SPI chip select number for the device
|
||||
- vref-supply: Phandle to the voltage reference supply
|
||||
|
||||
Recommended properties:
|
||||
- spi-max-frequency: Definition as per
|
||||
Documentation/devicetree/bindings/spi/spi-bus.txt.
|
||||
Max frequency for this chip is 5 MHz.
|
||||
|
||||
Example:
|
||||
dac@0 {
|
||||
compatible = "lltc,ltc1660";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <5000000>;
|
||||
vref-supply = <&vref_reg>;
|
||||
};
|
@ -20,6 +20,7 @@ Required properties:
|
||||
bindings.
|
||||
|
||||
Optional properties:
|
||||
- vddio-supply: regulator phandle for VDDIO supply
|
||||
- mount-matrix: an optional 3x3 mounting rotation matrix
|
||||
- i2c-gate node. These devices also support an auxiliary i2c bus. This is
|
||||
simple enough to be described using the i2c-gate binding. See
|
||||
|
@ -7,6 +7,7 @@ Required properties:
|
||||
"st,lsm6dsl"
|
||||
"st,lsm6dsm"
|
||||
"st,ism330dlc"
|
||||
"st,lsm6dso"
|
||||
- reg: i2c address of the sensor / spi cs line
|
||||
|
||||
Optional properties:
|
||||
|
18
Documentation/devicetree/bindings/iio/light/bh1750.txt
Normal file
18
Documentation/devicetree/bindings/iio/light/bh1750.txt
Normal file
@ -0,0 +1,18 @@
|
||||
ROHM BH1750 - ALS, Ambient light sensor
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: Must be one of:
|
||||
"rohm,bh1710"
|
||||
"rohm,bh1715"
|
||||
"rohm,bh1721"
|
||||
"rohm,bh1750"
|
||||
"rohm,bh1751"
|
||||
- reg: the I2C address of the sensor
|
||||
|
||||
Example:
|
||||
|
||||
light-sensor@23 {
|
||||
compatible = "rohm,bh1750";
|
||||
reg = <0x23>;
|
||||
};
|
42
Documentation/devicetree/bindings/iio/light/tsl2772.txt
Normal file
42
Documentation/devicetree/bindings/iio/light/tsl2772.txt
Normal file
@ -0,0 +1,42 @@
|
||||
* AMS/TAOS ALS and proximity sensor
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: Should be one of
|
||||
"amstaos,tsl2571"
|
||||
"amstaos,tsl2671"
|
||||
"amstaos,tmd2671"
|
||||
"amstaos,tsl2771"
|
||||
"amstaos,tmd2771"
|
||||
"amstaos,tsl2572"
|
||||
"amstaos,tsl2672"
|
||||
"amstaos,tmd2672"
|
||||
"amstaos,tsl2772"
|
||||
"amstaos,tmd2772"
|
||||
"avago,apds9930"
|
||||
- reg: the I2C address of the device
|
||||
|
||||
Optional properties:
|
||||
|
||||
- amstaos,proximity-diodes - proximity diodes to enable. <0>, <1>, or <0 1>
|
||||
are the only valid values.
|
||||
- led-max-microamp - current for the proximity LED. Must be 100000, 50000,
|
||||
25000, or 13000.
|
||||
- vdd-supply: phandle to the regulator that provides power to the sensor.
|
||||
- vddio-supply: phandle to the regulator that provides power to the bus.
|
||||
- interrupts: the sole interrupt generated by the device
|
||||
|
||||
Refer to interrupt-controller/interrupts.txt for generic interrupt client
|
||||
node bindings.
|
||||
|
||||
Example:
|
||||
|
||||
tsl2772@39 {
|
||||
compatible = "amstaos,tsl2772";
|
||||
reg = <0x39>;
|
||||
interrupts-extended = <&msmgpio 61 IRQ_TYPE_EDGE_FALLING>;
|
||||
vdd-supply = <&pm8941_l17>;
|
||||
vddio-supply = <&pm8941_lvs1>;
|
||||
amstaos,proximity-diodes = <0>;
|
||||
led-max-microamp = <100000>;
|
||||
};
|
12
Documentation/devicetree/bindings/iio/proximity/vl53l0x.txt
Normal file
12
Documentation/devicetree/bindings/iio/proximity/vl53l0x.txt
Normal file
@ -0,0 +1,12 @@
|
||||
ST VL53L0X ToF ranging sensor
|
||||
|
||||
Required properties:
|
||||
- compatible: must be "st,vl53l0x"
|
||||
- reg: i2c address where to find the device
|
||||
|
||||
Example:
|
||||
|
||||
vl53l0x@29 {
|
||||
compatible = "st,vl53l0x";
|
||||
reg = <0x29>;
|
||||
};
|
@ -21,16 +21,6 @@ adi,adt7490 +/-1C TDM Extended Temp Range I.C
|
||||
adi,adxl345 Three-Axis Digital Accelerometer
|
||||
adi,adxl346 Three-Axis Digital Accelerometer (backward-compatibility value "adi,adxl345" must be listed too)
|
||||
ams,iaq-core AMS iAQ-Core VOC Sensor
|
||||
amstaos,tsl2571 AMS/TAOS ALS and proximity sensor
|
||||
amstaos,tsl2671 AMS/TAOS ALS and proximity sensor
|
||||
amstaos,tmd2671 AMS/TAOS ALS and proximity sensor
|
||||
amstaos,tsl2771 AMS/TAOS ALS and proximity sensor
|
||||
amstaos,tmd2771 AMS/TAOS ALS and proximity sensor
|
||||
amstaos,tsl2572 AMS/TAOS ALS and proximity sensor
|
||||
amstaos,tsl2672 AMS/TAOS ALS and proximity sensor
|
||||
amstaos,tmd2672 AMS/TAOS ALS and proximity sensor
|
||||
amstaos,tsl2772 AMS/TAOS ALS and proximity sensor
|
||||
amstaos,tmd2772 AMS/TAOS ALS and proximity sensor
|
||||
at,24c08 i2c serial eeprom (24cxx)
|
||||
atmel,at97sc3204t i2c trusted platform module (TPM)
|
||||
capella,cm32181 CM32181: Ambient Light Sensor
|
||||
|
@ -1,72 +0,0 @@
|
||||
POHMELFS: Parallel Optimized Host Message Exchange Layered File System.
|
||||
|
||||
Evgeniy Polyakov <zbr@ioremap.net>
|
||||
|
||||
Homepage: http://www.ioremap.net/projects/pohmelfs
|
||||
|
||||
POHMELFS first began as a network filesystem with coherent local data and
|
||||
metadata caches but is now evolving into a parallel distributed filesystem.
|
||||
|
||||
Main features of this FS include:
|
||||
* Locally coherent cache for data and metadata with (potentially) byte-range locks.
|
||||
Since all Linux filesystems lock the whole inode during writing, algorithm
|
||||
is very simple and does not use byte-ranges, although they are sent in
|
||||
locking messages.
|
||||
* Completely async processing of all events except creation of hard and symbolic
|
||||
links, and rename events.
|
||||
Object creation and data reading and writing are processed asynchronously.
|
||||
* Flexible object architecture optimized for network processing.
|
||||
Ability to create long paths to objects and remove arbitrarily huge
|
||||
directories with a single network command.
|
||||
(like removing the whole kernel tree via a single network command).
|
||||
* Very high performance.
|
||||
* Fast and scalable multithreaded userspace server. Being in userspace it works
|
||||
with any underlying filesystem and still is much faster than async in-kernel NFS one.
|
||||
* Client is able to switch between different servers (if one goes down, client
|
||||
automatically reconnects to second and so on).
|
||||
* Transactions support. Full failover for all operations.
|
||||
Resending transactions to different servers on timeout or error.
|
||||
* Read request (data read, directory listing, lookup requests) balancing between multiple servers.
|
||||
* Write requests are replicated to multiple servers and completed only when all of them are acked.
|
||||
* Ability to add and/or remove servers from the working set at run-time.
|
||||
* Strong authentication and possible data encryption in network channel.
|
||||
* Extended attributes support.
|
||||
|
||||
POHMELFS is based on transactions, which are potentially long-standing objects that live
|
||||
in the client's memory. Each transaction contains all the information needed to process a given
|
||||
command (or set of commands, which is frequently used during data writing: single transactions
|
||||
can contain creation and data writing commands). Transactions are committed by all the servers
|
||||
to which they are sent and, in case of failures, are eventually resent or dropped with an error.
|
||||
For example, reading will return an error if no servers are available.
|
||||
|
||||
POHMELFS uses a asynchronous approach to data processing. Courtesy of transactions, it is
|
||||
possible to detach replies from requests and, if the command requires data to be received, the
|
||||
caller sleeps waiting for it. Thus, it is possible to issue multiple read commands to different
|
||||
servers and async threads will pick up replies in parallel, find appropriate transactions in the
|
||||
system and put the data where it belongs (like the page or inode cache).
|
||||
|
||||
The main feature of POHMELFS is writeback data and the metadata cache.
|
||||
Only a few non-performance critical operations use the write-through cache and
|
||||
are synchronous: hard and symbolic link creation, and object rename. Creation,
|
||||
removal of objects and data writing are asynchronous and are sent to
|
||||
the server during system writeback. Only one writer at a time is allowed for any
|
||||
given inode, which is guarded by an appropriate locking protocol.
|
||||
Because of this feature, POHMELFS is extremely fast at metadata intensive
|
||||
workloads and can fully utilize the bandwidth to the servers when doing bulk
|
||||
data transfers.
|
||||
|
||||
POHMELFS clients operate with a working set of servers and are capable of balancing read-only
|
||||
operations (like lookups or directory listings) between them according to IO priorities.
|
||||
Administrators can add or remove servers from the set at run-time via special commands (described
|
||||
in Documentation/filesystems/pohmelfs/info.txt file). Writes are replicated to all servers, which
|
||||
are connected with write permission turned on. IO priority and permissions can be changed in
|
||||
run-time.
|
||||
|
||||
POHMELFS is capable of full data channel encryption and/or strong crypto hashing.
|
||||
One can select any kernel supported cipher, encryption mode, hash type and operation mode
|
||||
(hmac or digest). It is also possible to use both or neither (default). Crypto configuration
|
||||
is checked during mount time and, if the server does not support it, appropriate capabilities
|
||||
will be disabled or mount will fail (if 'crypto_fail_unsupported' mount option is specified).
|
||||
Crypto performance heavily depends on the number of crypto threads, which asynchronously perform
|
||||
crypto operations and send the resulting data to server or submit it up the stack. This number
|
||||
can be controlled via a mount option.
|
@ -1,99 +0,0 @@
|
||||
POHMELFS usage information.
|
||||
|
||||
Mount options.
|
||||
All but index, number of crypto threads and maximum IO size can changed via remount.
|
||||
|
||||
idx=%u
|
||||
Each mountpoint is associated with a special index via this option.
|
||||
Administrator can add or remove servers from the given index, so all mounts,
|
||||
which were attached to it, are updated.
|
||||
Default it is 0.
|
||||
|
||||
trans_scan_timeout=%u
|
||||
This timeout, expressed in milliseconds, specifies time to scan transaction
|
||||
trees looking for stale requests, which have to be resent, or if number of
|
||||
retries exceed specified limit, dropped with error.
|
||||
Default is 5 seconds.
|
||||
|
||||
drop_scan_timeout=%u
|
||||
Internal timeout, expressed in milliseconds, which specifies how frequently
|
||||
inodes marked to be dropped are freed. It also specifies how frequently
|
||||
the system checks that servers have to be added or removed from current working set.
|
||||
Default is 1 second.
|
||||
|
||||
wait_on_page_timeout=%u
|
||||
Number of milliseconds to wait for reply from remote server for data reading command.
|
||||
If this timeout is exceeded, reading returns an error.
|
||||
Default is 5 seconds.
|
||||
|
||||
trans_retries=%u
|
||||
This is the number of times that a transaction will be resent to a server that did
|
||||
not answer for the last @trans_scan_timeout milliseconds.
|
||||
When the number of resends exceeds this limit, the transaction is completed with error.
|
||||
Default is 5 resends.
|
||||
|
||||
crypto_thread_num=%u
|
||||
Number of crypto processing threads. Threads are used both for RX and TX traffic.
|
||||
Default is 2, or no threads if crypto operations are not supported.
|
||||
|
||||
trans_max_pages=%u
|
||||
Maximum number of pages in a single transaction. This parameter also controls
|
||||
the number of pages, allocated for crypto processing (each crypto thread has
|
||||
pool of pages, the number of which is equal to 'trans_max_pages'.
|
||||
Default is 100 pages.
|
||||
|
||||
crypto_fail_unsupported
|
||||
If specified, mount will fail if the server does not support requested crypto operations.
|
||||
By default mount will disable non-matching crypto operations.
|
||||
|
||||
mcache_timeout=%u
|
||||
Maximum number of milliseconds to wait for the mcache objects to be processed.
|
||||
Mcache includes locks (given lock should be granted by server), attributes (they should be
|
||||
fully received in the given timeframe).
|
||||
Default is 5 seconds.
|
||||
|
||||
Usage examples.
|
||||
|
||||
Add server server1.net:1025 into the working set with index $idx
|
||||
with appropriate hash algorithm and key file and cipher algorithm, mode and key file:
|
||||
$cfg A add -a server1.net -p 1025 -i $idx -K $hash_key -k $cipher_key
|
||||
|
||||
Mount filesystem with given index $idx to /mnt mountpoint.
|
||||
Client will connect to all servers specified in the working set via previous command:
|
||||
mount -t pohmel -o idx=$idx q /mnt
|
||||
|
||||
Change permissions to read-only (-I 1 option, '-I 2' - write-only, 3 - rw):
|
||||
$cfg A modify -a server1.net -p 1025 -i $idx -I 1
|
||||
|
||||
Change IO priority to 123 (node with the highest priority gets read requests).
|
||||
$cfg A modify -a server1.net -p 1025 -i $idx -P 123
|
||||
|
||||
One can check currect status of all connections in the mountstats file:
|
||||
# cat /proc/$PID/mountstats
|
||||
...
|
||||
device none mounted on /mnt with fstype pohmel
|
||||
idx addr(:port) socket_type protocol active priority permissions
|
||||
0 server1.net:1026 1 6 1 250 1
|
||||
0 server2.net:1025 1 6 1 123 3
|
||||
|
||||
Server installation.
|
||||
|
||||
Creating a server, which listens at port 1025 and 0.0.0.0 address.
|
||||
Working root directory (note, that server chroots there, so you have to have appropriate permissions)
|
||||
is set to /mnt, server will negotiate hash/cipher with client, in case client requested it, there
|
||||
are appropriate key files.
|
||||
Number of working threads is set to 10.
|
||||
|
||||
# ./fserver -a 0.0.0.0 -p 1025 -r /mnt -w 10 -K hash_key -k cipher_key
|
||||
|
||||
-A 6 - listen on ipv6 address. Default: Disabled.
|
||||
-r root - path to root directory. Default: /tmp.
|
||||
-a addr - listen address. Default: 0.0.0.0.
|
||||
-p port - listen port. Default: 1025.
|
||||
-w workers - number of workers per connected client. Default: 1.
|
||||
-K file - hash key size. Default: none.
|
||||
-k file - cipher key size. Default: none.
|
||||
-h - this help.
|
||||
|
||||
Number of worker threads specifies how many workers will be created for each client.
|
||||
Bulk single-client transafers usually are better handled with smaller number (like 1-3).
|
@ -1,227 +0,0 @@
|
||||
POHMELFS network protocol.
|
||||
|
||||
Basic structure used in network communication is following command:
|
||||
|
||||
struct netfs_cmd
|
||||
{
|
||||
__u16 cmd; /* Command number */
|
||||
__u16 csize; /* Attached crypto information size */
|
||||
__u16 cpad; /* Attached padding size */
|
||||
__u16 ext; /* External flags */
|
||||
__u32 size; /* Size of the attached data */
|
||||
__u32 trans; /* Transaction id */
|
||||
__u64 id; /* Object ID to operate on. Used for feedback.*/
|
||||
__u64 start; /* Start of the object. */
|
||||
__u64 iv; /* IV sequence */
|
||||
__u8 data[0];
|
||||
};
|
||||
|
||||
Commands can be embedded into transaction command (which in turn has own command),
|
||||
so one can extend protocol as needed without breaking backward compatibility as long
|
||||
as old commands are supported. All string lengths include tail 0 byte.
|
||||
|
||||
All commands are transferred over the network in big-endian. CPU endianness is used at the end peers.
|
||||
|
||||
@cmd - command number, which specifies command to be processed. Following
|
||||
commands are used currently:
|
||||
|
||||
NETFS_READDIR = 1, /* Read directory for given inode number */
|
||||
NETFS_READ_PAGE, /* Read data page from the server */
|
||||
NETFS_WRITE_PAGE, /* Write data page to the server */
|
||||
NETFS_CREATE, /* Create directory entry */
|
||||
NETFS_REMOVE, /* Remove directory entry */
|
||||
NETFS_LOOKUP, /* Lookup single object */
|
||||
NETFS_LINK, /* Create a link */
|
||||
NETFS_TRANS, /* Transaction */
|
||||
NETFS_OPEN, /* Open intent */
|
||||
NETFS_INODE_INFO, /* Metadata cache coherency synchronization message */
|
||||
NETFS_PAGE_CACHE, /* Page cache invalidation message */
|
||||
NETFS_READ_PAGES, /* Read multiple contiguous pages in one go */
|
||||
NETFS_RENAME, /* Rename object */
|
||||
NETFS_CAPABILITIES, /* Capabilities of the client, for example supported crypto */
|
||||
NETFS_LOCK, /* Distributed lock message */
|
||||
NETFS_XATTR_SET, /* Set extended attribute */
|
||||
NETFS_XATTR_GET, /* Get extended attribute */
|
||||
|
||||
@ext - external flags. Used by different commands to specify some extra arguments
|
||||
like partial size of the embedded objects or creation flags.
|
||||
|
||||
@size - size of the attached data. For NETFS_READ_PAGE and NETFS_READ_PAGES no data is attached,
|
||||
but size of the requested data is incorporated here. It does not include size of the command
|
||||
header (struct netfs_cmd) itself.
|
||||
|
||||
@id - id of the object this command operates on. Each command can use it for own purpose.
|
||||
|
||||
@start - start of the object this command operates on. Each command can use it for own purpose.
|
||||
|
||||
@csize, @cpad - size and padding size of the (attached if needed) crypto information.
|
||||
|
||||
Command specifications.
|
||||
|
||||
@NETFS_READDIR
|
||||
This command is used to sync content of the remote dir to the client.
|
||||
|
||||
@ext - length of the path to object.
|
||||
@size - the same.
|
||||
@id - local inode number of the directory to read.
|
||||
@start - zero.
|
||||
|
||||
|
||||
@NETFS_READ_PAGE
|
||||
This command is used to read data from remote server.
|
||||
Data size does not exceed local page cache size.
|
||||
|
||||
@id - inode number.
|
||||
@start - first byte offset.
|
||||
@size - number of bytes to read plus length of the path to object.
|
||||
@ext - object path length.
|
||||
|
||||
|
||||
@NETFS_CREATE
|
||||
Used to create object.
|
||||
It does not require that all directories on top of the object were
|
||||
already created, it will create them automatically. Each object has
|
||||
associated @netfs_path_entry data structure, which contains creation
|
||||
mode (permissions and type) and length of the name as long as name itself.
|
||||
|
||||
@start - 0
|
||||
@size - size of the all data structures needed to create a path
|
||||
@id - local inode number
|
||||
@ext - 0
|
||||
|
||||
|
||||
@NETFS_REMOVE
|
||||
Used to remove object.
|
||||
|
||||
@ext - length of the path to object.
|
||||
@size - the same.
|
||||
@id - local inode number.
|
||||
@start - zero.
|
||||
|
||||
|
||||
@NETFS_LOOKUP
|
||||
Lookup information about object on server.
|
||||
|
||||
@ext - length of the path to object.
|
||||
@size - the same.
|
||||
@id - local inode number of the directory to look object in.
|
||||
@start - local inode number of the object to look at.
|
||||
|
||||
|
||||
@NETFS_LINK
|
||||
Create hard of symlink.
|
||||
Command is sent as "object_path|target_path".
|
||||
|
||||
@size - size of the above string.
|
||||
@id - parent local inode number.
|
||||
@start - 1 for symlink, 0 for hardlink.
|
||||
@ext - size of the "object_path" above.
|
||||
|
||||
|
||||
@NETFS_TRANS
|
||||
Transaction header.
|
||||
|
||||
@size - incorporates all embedded command sizes including theirs header sizes.
|
||||
@start - transaction generation number - unique id used to find transaction.
|
||||
@ext - transaction flags. Unused at the moment.
|
||||
@id - 0.
|
||||
|
||||
|
||||
@NETFS_OPEN
|
||||
Open intent for given transaction.
|
||||
|
||||
@id - local inode number.
|
||||
@start - 0.
|
||||
@size - path length to the object.
|
||||
@ext - open flags (O_RDWR and so on).
|
||||
|
||||
|
||||
@NETFS_INODE_INFO
|
||||
Metadata update command.
|
||||
It is sent to servers when attributes of the object are changed and received
|
||||
when data or metadata were updated. It operates with the following structure:
|
||||
|
||||
struct netfs_inode_info
|
||||
{
|
||||
unsigned int mode;
|
||||
unsigned int nlink;
|
||||
unsigned int uid;
|
||||
unsigned int gid;
|
||||
unsigned int blocksize;
|
||||
unsigned int padding;
|
||||
__u64 ino;
|
||||
__u64 blocks;
|
||||
__u64 rdev;
|
||||
__u64 size;
|
||||
__u64 version;
|
||||
};
|
||||
|
||||
It effectively mirrors stat(2) returned data.
|
||||
|
||||
|
||||
@ext - path length to the object.
|
||||
@size - the same plus size of the netfs_inode_info structure.
|
||||
@id - local inode number.
|
||||
@start - 0.
|
||||
|
||||
|
||||
@NETFS_PAGE_CACHE
|
||||
Command is only received by clients. It contains information about
|
||||
page to be marked as not up-to-date.
|
||||
|
||||
@id - client's inode number.
|
||||
@start - last byte of the page to be invalidated. If it is not equal to
|
||||
current inode size, it will be vmtruncated().
|
||||
@size - 0
|
||||
@ext - 0
|
||||
|
||||
|
||||
@NETFS_READ_PAGES
|
||||
Used to read multiple contiguous pages in one go.
|
||||
|
||||
@start - first byte of the contiguous region to read.
|
||||
@size - contains of two fields: lower 8 bits are used to represent page cache shift
|
||||
used by client, another 3 bytes are used to get number of pages.
|
||||
@id - local inode number.
|
||||
@ext - path length to the object.
|
||||
|
||||
|
||||
@NETFS_RENAME
|
||||
Used to rename object.
|
||||
Attached data is formed into following string: "old_path|new_path".
|
||||
|
||||
@id - local inode number.
|
||||
@start - parent inode number.
|
||||
@size - length of the above string.
|
||||
@ext - length of the old path part.
|
||||
|
||||
|
||||
@NETFS_CAPABILITIES
|
||||
Used to exchange crypto capabilities with server.
|
||||
If crypto capabilities are not supported by server, then client will disable it
|
||||
or fail (if 'crypto_fail_unsupported' mount options was specified).
|
||||
|
||||
@id - superblock index. Used to specify crypto information for group of servers.
|
||||
@size - size of the attached capabilities structure.
|
||||
@start - 0.
|
||||
@size - 0.
|
||||
@scsize - 0.
|
||||
|
||||
@NETFS_LOCK
|
||||
Used to send lock request/release messages. Although it sends byte range request
|
||||
and is capable of flushing pages based on that, it is not used, since all Linux
|
||||
filesystems lock the whole inode.
|
||||
|
||||
@id - lock generation number.
|
||||
@start - start of the locked range.
|
||||
@size - size of the locked range.
|
||||
@ext - lock type: read/write. Not used actually. 15'th bit is used to determine,
|
||||
if it is lock request (1) or release (0).
|
||||
|
||||
@NETFS_XATTR_SET
|
||||
@NETFS_XATTR_GET
|
||||
Used to set/get extended attributes for given inode.
|
||||
@id - attribute generation number or xattr setting type
|
||||
@start - size of the attribute (request or attached)
|
||||
@size - name length, path len and data size for given attribute
|
||||
@ext - path length for given object
|
52
MAINTAINERS
52
MAINTAINERS
@ -549,6 +549,15 @@ W: http://ez.analog.com/community/linux-device-drivers
|
||||
S: Supported
|
||||
F: drivers/input/misc/adxl34x.c
|
||||
|
||||
ADXL372 THREE-AXIS DIGITAL ACCELEROMETER DRIVER
|
||||
M: Stefan Popa <stefan.popa@analog.com>
|
||||
W: http://ez.analog.com/community/linux-device-drivers
|
||||
S: Supported
|
||||
F: drivers/iio/accel/adxl372.c
|
||||
F: drivers/iio/accel/adxl372_spi.c
|
||||
F: drivers/iio/accel/adxl372_i2c.c
|
||||
F: Documentation/devicetree/bindings/iio/accel/adxl372.txt
|
||||
|
||||
AF9013 MEDIA DRIVER
|
||||
M: Antti Palosaari <crope@iki.fi>
|
||||
L: linux-media@vger.kernel.org
|
||||
@ -4388,13 +4397,6 @@ L: linux-gpio@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/gpio/gpio-gpio-mm.c
|
||||
|
||||
DIGI NEO AND CLASSIC PCI PRODUCTS
|
||||
M: Lidza Louina <lidza.louina@gmail.com>
|
||||
M: Mark Hounschell <markh@compro.net>
|
||||
L: driverdev-devel@linuxdriverproject.org
|
||||
S: Maintained
|
||||
F: drivers/staging/dgnc/
|
||||
|
||||
DIOLAN U2C-12 I2C DRIVER
|
||||
M: Guenter Roeck <linux@roeck-us.net>
|
||||
L: linux-i2c@vger.kernel.org
|
||||
@ -5675,10 +5677,9 @@ F: Documentation/fault-injection/
|
||||
F: lib/fault-inject.c
|
||||
|
||||
FBTFT Framebuffer drivers
|
||||
M: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
|
||||
S: Orphan
|
||||
L: dri-devel@lists.freedesktop.org
|
||||
L: linux-fbdev@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/staging/fbtft/
|
||||
|
||||
FC0011 TUNER DRIVER
|
||||
@ -7737,7 +7738,6 @@ IPX NETWORK LAYER
|
||||
L: netdev@vger.kernel.org
|
||||
S: Obsolete
|
||||
F: include/uapi/linux/ipx.h
|
||||
F: drivers/staging/ipx/
|
||||
|
||||
IRQ DOMAINS (IRQ NUMBER MAPPING LIBRARY)
|
||||
M: Marc Zyngier <marc.zyngier@arm.com>
|
||||
@ -8716,6 +8716,13 @@ L: linux-scsi@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/scsi/sym53c8xx_2/
|
||||
|
||||
LTC1660 DAC DRIVER
|
||||
M: Marcus Folkesson <marcus.folkesson@gmail.com>
|
||||
L: linux-iio@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/iio/dac/ltc1660.txt
|
||||
F: drivers/iio/dac/ltc1660.c
|
||||
|
||||
LTC4261 HARDWARE MONITOR DRIVER
|
||||
M: Guenter Roeck <linux@roeck-us.net>
|
||||
L: linux-hwmon@vger.kernel.org
|
||||
@ -9670,6 +9677,14 @@ L: netdev@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/net/ethernet/microchip/lan743x_*
|
||||
|
||||
MICROCHIP / ATMEL MCP3911 ADC DRIVER
|
||||
M: Marcus Folkesson <marcus.folkesson@gmail.com>
|
||||
M: Kent Gustavsson <kent@minoris.se>
|
||||
L: linux-iio@vger.kernel.org
|
||||
S: Supported
|
||||
F: drivers/iio/adc/mcp3911.c
|
||||
F: Documentation/devicetree/bindings/iio/adc/mcp3911.txt
|
||||
|
||||
MICROCHIP USB251XB DRIVER
|
||||
M: Richard Leitner <richard.leitner@skidata.com>
|
||||
L: linux-usb@vger.kernel.org
|
||||
@ -10060,11 +10075,6 @@ NATSEMI ETHERNET DRIVER (DP8381x)
|
||||
S: Orphan
|
||||
F: drivers/net/ethernet/natsemi/natsemi.c
|
||||
|
||||
NCP FILESYSTEM
|
||||
M: Petr Vandrovec <petr@vandrovec.name>
|
||||
S: Obsolete
|
||||
F: drivers/staging/ncpfs/
|
||||
|
||||
NCR 5380 SCSI DRIVERS
|
||||
M: Finn Thain <fthain@telegraphics.com.au>
|
||||
M: Michael Schmitz <schmitzmic@gmail.com>
|
||||
@ -13911,6 +13921,13 @@ L: linux-i2c@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/i2c/busses/i2c-stm32*
|
||||
|
||||
ST VL53L0X ToF RANGER(I2C) IIO DRIVER
|
||||
M: Song Qiang <songqiang1304521@gmail.com>
|
||||
L: linux-iio@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/iio/proximity/vl53l0x-i2c.c
|
||||
F: Documentation/devicetree/bindings/iio/proximity/vl53l0x.txt
|
||||
|
||||
STABLE BRANCH
|
||||
M: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
||||
L: stable@vger.kernel.org
|
||||
@ -13930,11 +13947,6 @@ L: linux-erofs@lists.ozlabs.org
|
||||
S: Maintained
|
||||
F: drivers/staging/erofs/
|
||||
|
||||
STAGING - FLARION FT1000 DRIVERS
|
||||
M: Marek Belisko <marek.belisko@gmail.com>
|
||||
S: Odd Fixes
|
||||
F: drivers/staging/ft1000/
|
||||
|
||||
STAGING - INDUSTRIAL IO
|
||||
M: Jonathan Cameron <jic23@kernel.org>
|
||||
L: linux-iio@vger.kernel.org
|
||||
|
@ -60,6 +60,33 @@ config ADXL345_SPI
|
||||
will be called adxl345_spi and you will also get adxl345_core
|
||||
for the core module.
|
||||
|
||||
config ADXL372
|
||||
tristate
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
|
||||
config ADXL372_SPI
|
||||
tristate "Analog Devices ADXL372 3-Axis Accelerometer SPI Driver"
|
||||
depends on SPI
|
||||
select ADXL372
|
||||
select REGMAP_SPI
|
||||
help
|
||||
Say yes here to add support for the Analog Devices ADXL372 triaxial
|
||||
acceleration sensor.
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called adxl372_spi.
|
||||
|
||||
config ADXL372_I2C
|
||||
tristate "Analog Devices ADXL372 3-Axis Accelerometer I2C Driver"
|
||||
depends on I2C
|
||||
select ADXL372
|
||||
select REGMAP_I2C
|
||||
help
|
||||
Say yes here to add support for the Analog Devices ADXL372 triaxial
|
||||
acceleration sensor.
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called adxl372_i2c.
|
||||
|
||||
config BMA180
|
||||
tristate "Bosch BMA180/BMA250 3-Axis Accelerometer Driver"
|
||||
depends on I2C
|
||||
|
@ -9,6 +9,9 @@ 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
|
||||
obj-$(CONFIG_ADXL372) += adxl372.o
|
||||
obj-$(CONFIG_ADXL372_I2C) += adxl372_i2c.o
|
||||
obj-$(CONFIG_ADXL372_SPI) += adxl372_spi.o
|
||||
obj-$(CONFIG_BMA180) += bma180.o
|
||||
obj-$(CONFIG_BMA220) += bma220_spi.o
|
||||
obj-$(CONFIG_BMC150_ACCEL) += bmc150-accel-core.o
|
||||
|
@ -27,6 +27,9 @@ static int adxl345_i2c_probe(struct i2c_client *client,
|
||||
{
|
||||
struct regmap *regmap;
|
||||
|
||||
if (!id)
|
||||
return -ENODEV;
|
||||
|
||||
regmap = devm_regmap_init_i2c(client, &adxl345_i2c_regmap_config);
|
||||
if (IS_ERR(regmap)) {
|
||||
dev_err(&client->dev, "Error initializing i2c regmap: %ld\n",
|
||||
@ -35,7 +38,7 @@ static int adxl345_i2c_probe(struct i2c_client *client,
|
||||
}
|
||||
|
||||
return adxl345_core_probe(&client->dev, regmap, id->driver_data,
|
||||
id ? id->name : NULL);
|
||||
id->name);
|
||||
}
|
||||
|
||||
static int adxl345_i2c_remove(struct i2c_client *client)
|
||||
|
975
drivers/iio/accel/adxl372.c
Normal file
975
drivers/iio/accel/adxl372.c
Normal file
@ -0,0 +1,975 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* ADXL372 3-Axis Digital Accelerometer core driver
|
||||
*
|
||||
* Copyright 2018 Analog Devices Inc.
|
||||
*/
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/events.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
|
||||
#include "adxl372.h"
|
||||
|
||||
/* ADXL372 registers definition */
|
||||
#define ADXL372_DEVID 0x00
|
||||
#define ADXL372_DEVID_MST 0x01
|
||||
#define ADXL372_PARTID 0x02
|
||||
#define ADXL372_STATUS_1 0x04
|
||||
#define ADXL372_STATUS_2 0x05
|
||||
#define ADXL372_FIFO_ENTRIES_2 0x06
|
||||
#define ADXL372_FIFO_ENTRIES_1 0x07
|
||||
#define ADXL372_X_DATA_H 0x08
|
||||
#define ADXL372_X_DATA_L 0x09
|
||||
#define ADXL372_Y_DATA_H 0x0A
|
||||
#define ADXL372_Y_DATA_L 0x0B
|
||||
#define ADXL372_Z_DATA_H 0x0C
|
||||
#define ADXL372_Z_DATA_L 0x0D
|
||||
#define ADXL372_X_MAXPEAK_H 0x15
|
||||
#define ADXL372_X_MAXPEAK_L 0x16
|
||||
#define ADXL372_Y_MAXPEAK_H 0x17
|
||||
#define ADXL372_Y_MAXPEAK_L 0x18
|
||||
#define ADXL372_Z_MAXPEAK_H 0x19
|
||||
#define ADXL372_Z_MAXPEAK_L 0x1A
|
||||
#define ADXL372_OFFSET_X 0x20
|
||||
#define ADXL372_OFFSET_Y 0x21
|
||||
#define ADXL372_OFFSET_Z 0x22
|
||||
#define ADXL372_X_THRESH_ACT_H 0x23
|
||||
#define ADXL372_X_THRESH_ACT_L 0x24
|
||||
#define ADXL372_Y_THRESH_ACT_H 0x25
|
||||
#define ADXL372_Y_THRESH_ACT_L 0x26
|
||||
#define ADXL372_Z_THRESH_ACT_H 0x27
|
||||
#define ADXL372_Z_THRESH_ACT_L 0x28
|
||||
#define ADXL372_TIME_ACT 0x29
|
||||
#define ADXL372_X_THRESH_INACT_H 0x2A
|
||||
#define ADXL372_X_THRESH_INACT_L 0x2B
|
||||
#define ADXL372_Y_THRESH_INACT_H 0x2C
|
||||
#define ADXL372_Y_THRESH_INACT_L 0x2D
|
||||
#define ADXL372_Z_THRESH_INACT_H 0x2E
|
||||
#define ADXL372_Z_THRESH_INACT_L 0x2F
|
||||
#define ADXL372_TIME_INACT_H 0x30
|
||||
#define ADXL372_TIME_INACT_L 0x31
|
||||
#define ADXL372_X_THRESH_ACT2_H 0x32
|
||||
#define ADXL372_X_THRESH_ACT2_L 0x33
|
||||
#define ADXL372_Y_THRESH_ACT2_H 0x34
|
||||
#define ADXL372_Y_THRESH_ACT2_L 0x35
|
||||
#define ADXL372_Z_THRESH_ACT2_H 0x36
|
||||
#define ADXL372_Z_THRESH_ACT2_L 0x37
|
||||
#define ADXL372_HPF 0x38
|
||||
#define ADXL372_FIFO_SAMPLES 0x39
|
||||
#define ADXL372_FIFO_CTL 0x3A
|
||||
#define ADXL372_INT1_MAP 0x3B
|
||||
#define ADXL372_INT2_MAP 0x3C
|
||||
#define ADXL372_TIMING 0x3D
|
||||
#define ADXL372_MEASURE 0x3E
|
||||
#define ADXL372_POWER_CTL 0x3F
|
||||
#define ADXL372_SELF_TEST 0x40
|
||||
#define ADXL372_RESET 0x41
|
||||
#define ADXL372_FIFO_DATA 0x42
|
||||
|
||||
#define ADXL372_DEVID_VAL 0xAD
|
||||
#define ADXL372_PARTID_VAL 0xFA
|
||||
#define ADXL372_RESET_CODE 0x52
|
||||
|
||||
/* ADXL372_POWER_CTL */
|
||||
#define ADXL372_POWER_CTL_MODE_MSK GENMASK_ULL(1, 0)
|
||||
#define ADXL372_POWER_CTL_MODE(x) (((x) & 0x3) << 0)
|
||||
|
||||
/* ADXL372_MEASURE */
|
||||
#define ADXL372_MEASURE_LINKLOOP_MSK GENMASK_ULL(5, 4)
|
||||
#define ADXL372_MEASURE_LINKLOOP_MODE(x) (((x) & 0x3) << 4)
|
||||
#define ADXL372_MEASURE_BANDWIDTH_MSK GENMASK_ULL(2, 0)
|
||||
#define ADXL372_MEASURE_BANDWIDTH_MODE(x) (((x) & 0x7) << 0)
|
||||
|
||||
/* ADXL372_TIMING */
|
||||
#define ADXL372_TIMING_ODR_MSK GENMASK_ULL(7, 5)
|
||||
#define ADXL372_TIMING_ODR_MODE(x) (((x) & 0x7) << 5)
|
||||
|
||||
/* ADXL372_FIFO_CTL */
|
||||
#define ADXL372_FIFO_CTL_FORMAT_MSK GENMASK(5, 3)
|
||||
#define ADXL372_FIFO_CTL_FORMAT_MODE(x) (((x) & 0x7) << 3)
|
||||
#define ADXL372_FIFO_CTL_MODE_MSK GENMASK(2, 1)
|
||||
#define ADXL372_FIFO_CTL_MODE_MODE(x) (((x) & 0x3) << 1)
|
||||
#define ADXL372_FIFO_CTL_SAMPLES_MSK BIT(1)
|
||||
#define ADXL372_FIFO_CTL_SAMPLES_MODE(x) (((x) > 0xFF) ? 1 : 0)
|
||||
|
||||
/* ADXL372_STATUS_1 */
|
||||
#define ADXL372_STATUS_1_DATA_RDY(x) (((x) >> 0) & 0x1)
|
||||
#define ADXL372_STATUS_1_FIFO_RDY(x) (((x) >> 1) & 0x1)
|
||||
#define ADXL372_STATUS_1_FIFO_FULL(x) (((x) >> 2) & 0x1)
|
||||
#define ADXL372_STATUS_1_FIFO_OVR(x) (((x) >> 3) & 0x1)
|
||||
#define ADXL372_STATUS_1_USR_NVM_BUSY(x) (((x) >> 5) & 0x1)
|
||||
#define ADXL372_STATUS_1_AWAKE(x) (((x) >> 6) & 0x1)
|
||||
#define ADXL372_STATUS_1_ERR_USR_REGS(x) (((x) >> 7) & 0x1)
|
||||
|
||||
/* ADXL372_INT1_MAP */
|
||||
#define ADXL372_INT1_MAP_DATA_RDY_MSK BIT(0)
|
||||
#define ADXL372_INT1_MAP_DATA_RDY_MODE(x) (((x) & 0x1) << 0)
|
||||
#define ADXL372_INT1_MAP_FIFO_RDY_MSK BIT(1)
|
||||
#define ADXL372_INT1_MAP_FIFO_RDY_MODE(x) (((x) & 0x1) << 1)
|
||||
#define ADXL372_INT1_MAP_FIFO_FULL_MSK BIT(2)
|
||||
#define ADXL372_INT1_MAP_FIFO_FULL_MODE(x) (((x) & 0x1) << 2)
|
||||
#define ADXL372_INT1_MAP_FIFO_OVR_MSK BIT(3)
|
||||
#define ADXL372_INT1_MAP_FIFO_OVR_MODE(x) (((x) & 0x1) << 3)
|
||||
#define ADXL372_INT1_MAP_INACT_MSK BIT(4)
|
||||
#define ADXL372_INT1_MAP_INACT_MODE(x) (((x) & 0x1) << 4)
|
||||
#define ADXL372_INT1_MAP_ACT_MSK BIT(5)
|
||||
#define ADXL372_INT1_MAP_ACT_MODE(x) (((x) & 0x1) << 5)
|
||||
#define ADXL372_INT1_MAP_AWAKE_MSK BIT(6)
|
||||
#define ADXL372_INT1_MAP_AWAKE_MODE(x) (((x) & 0x1) << 6)
|
||||
#define ADXL372_INT1_MAP_LOW_MSK BIT(7)
|
||||
#define ADXL372_INT1_MAP_LOW_MODE(x) (((x) & 0x1) << 7)
|
||||
|
||||
/* The ADXL372 includes a deep, 512 sample FIFO buffer */
|
||||
#define ADXL372_FIFO_SIZE 512
|
||||
|
||||
/*
|
||||
* At +/- 200g with 12-bit resolution, scale is computed as:
|
||||
* (200 + 200) * 9.81 / (2^12 - 1) = 0.958241
|
||||
*/
|
||||
#define ADXL372_USCALE 958241
|
||||
|
||||
enum adxl372_op_mode {
|
||||
ADXL372_STANDBY,
|
||||
ADXL372_WAKE_UP,
|
||||
ADXL372_INSTANT_ON,
|
||||
ADXL372_FULL_BW_MEASUREMENT,
|
||||
};
|
||||
|
||||
enum adxl372_act_proc_mode {
|
||||
ADXL372_DEFAULT,
|
||||
ADXL372_LINKED,
|
||||
ADXL372_LOOPED,
|
||||
};
|
||||
|
||||
enum adxl372_th_activity {
|
||||
ADXL372_ACTIVITY,
|
||||
ADXL372_ACTIVITY2,
|
||||
ADXL372_INACTIVITY,
|
||||
};
|
||||
|
||||
enum adxl372_odr {
|
||||
ADXL372_ODR_400HZ,
|
||||
ADXL372_ODR_800HZ,
|
||||
ADXL372_ODR_1600HZ,
|
||||
ADXL372_ODR_3200HZ,
|
||||
ADXL372_ODR_6400HZ,
|
||||
};
|
||||
|
||||
enum adxl372_bandwidth {
|
||||
ADXL372_BW_200HZ,
|
||||
ADXL372_BW_400HZ,
|
||||
ADXL372_BW_800HZ,
|
||||
ADXL372_BW_1600HZ,
|
||||
ADXL372_BW_3200HZ,
|
||||
};
|
||||
|
||||
static const unsigned int adxl372_th_reg_high_addr[3] = {
|
||||
[ADXL372_ACTIVITY] = ADXL372_X_THRESH_ACT_H,
|
||||
[ADXL372_ACTIVITY2] = ADXL372_X_THRESH_ACT2_H,
|
||||
[ADXL372_INACTIVITY] = ADXL372_X_THRESH_INACT_H,
|
||||
};
|
||||
|
||||
enum adxl372_fifo_format {
|
||||
ADXL372_XYZ_FIFO,
|
||||
ADXL372_X_FIFO,
|
||||
ADXL372_Y_FIFO,
|
||||
ADXL372_XY_FIFO,
|
||||
ADXL372_Z_FIFO,
|
||||
ADXL372_XZ_FIFO,
|
||||
ADXL372_YZ_FIFO,
|
||||
ADXL372_XYZ_PEAK_FIFO,
|
||||
};
|
||||
|
||||
enum adxl372_fifo_mode {
|
||||
ADXL372_FIFO_BYPASSED,
|
||||
ADXL372_FIFO_STREAMED,
|
||||
ADXL372_FIFO_TRIGGERED,
|
||||
ADXL372_FIFO_OLD_SAVED
|
||||
};
|
||||
|
||||
static const int adxl372_samp_freq_tbl[5] = {
|
||||
400, 800, 1600, 3200, 6400,
|
||||
};
|
||||
|
||||
static const int adxl372_bw_freq_tbl[5] = {
|
||||
200, 400, 800, 1600, 3200,
|
||||
};
|
||||
|
||||
struct adxl372_axis_lookup {
|
||||
unsigned int bits;
|
||||
enum adxl372_fifo_format fifo_format;
|
||||
};
|
||||
|
||||
static const struct adxl372_axis_lookup adxl372_axis_lookup_table[] = {
|
||||
{ BIT(0), ADXL372_X_FIFO },
|
||||
{ BIT(1), ADXL372_Y_FIFO },
|
||||
{ BIT(2), ADXL372_Z_FIFO },
|
||||
{ BIT(0) | BIT(1), ADXL372_XY_FIFO },
|
||||
{ BIT(0) | BIT(2), ADXL372_XZ_FIFO },
|
||||
{ BIT(1) | BIT(2), ADXL372_YZ_FIFO },
|
||||
{ BIT(0) | BIT(1) | BIT(2), ADXL372_XYZ_FIFO },
|
||||
};
|
||||
|
||||
#define ADXL372_ACCEL_CHANNEL(index, reg, axis) { \
|
||||
.type = IIO_ACCEL, \
|
||||
.address = reg, \
|
||||
.modified = 1, \
|
||||
.channel2 = IIO_MOD_##axis, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
|
||||
BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), \
|
||||
.scan_index = index, \
|
||||
.scan_type = { \
|
||||
.sign = 's', \
|
||||
.realbits = 12, \
|
||||
.storagebits = 16, \
|
||||
.shift = 4, \
|
||||
}, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec adxl372_channels[] = {
|
||||
ADXL372_ACCEL_CHANNEL(0, ADXL372_X_DATA_H, X),
|
||||
ADXL372_ACCEL_CHANNEL(1, ADXL372_Y_DATA_H, Y),
|
||||
ADXL372_ACCEL_CHANNEL(2, ADXL372_Z_DATA_H, Z),
|
||||
};
|
||||
|
||||
struct adxl372_state {
|
||||
int irq;
|
||||
struct device *dev;
|
||||
struct regmap *regmap;
|
||||
struct iio_trigger *dready_trig;
|
||||
enum adxl372_fifo_mode fifo_mode;
|
||||
enum adxl372_fifo_format fifo_format;
|
||||
enum adxl372_op_mode op_mode;
|
||||
enum adxl372_act_proc_mode act_proc_mode;
|
||||
enum adxl372_odr odr;
|
||||
enum adxl372_bandwidth bw;
|
||||
u32 act_time_ms;
|
||||
u32 inact_time_ms;
|
||||
u8 fifo_set_size;
|
||||
u8 int1_bitmask;
|
||||
u8 int2_bitmask;
|
||||
u16 watermark;
|
||||
__be16 fifo_buf[ADXL372_FIFO_SIZE];
|
||||
};
|
||||
|
||||
static const unsigned long adxl372_channel_masks[] = {
|
||||
BIT(0), BIT(1), BIT(2),
|
||||
BIT(0) | BIT(1),
|
||||
BIT(0) | BIT(2),
|
||||
BIT(1) | BIT(2),
|
||||
BIT(0) | BIT(1) | BIT(2),
|
||||
0
|
||||
};
|
||||
|
||||
static int adxl372_read_axis(struct adxl372_state *st, u8 addr)
|
||||
{
|
||||
__be16 regval;
|
||||
int ret;
|
||||
|
||||
ret = regmap_bulk_read(st->regmap, addr, ®val, sizeof(regval));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return be16_to_cpu(regval);
|
||||
}
|
||||
|
||||
static int adxl372_set_op_mode(struct adxl372_state *st,
|
||||
enum adxl372_op_mode op_mode)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = regmap_update_bits(st->regmap, ADXL372_POWER_CTL,
|
||||
ADXL372_POWER_CTL_MODE_MSK,
|
||||
ADXL372_POWER_CTL_MODE(op_mode));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
st->op_mode = op_mode;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adxl372_set_odr(struct adxl372_state *st,
|
||||
enum adxl372_odr odr)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = regmap_update_bits(st->regmap, ADXL372_TIMING,
|
||||
ADXL372_TIMING_ODR_MSK,
|
||||
ADXL372_TIMING_ODR_MODE(odr));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
st->odr = odr;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adxl372_find_closest_match(const int *array,
|
||||
unsigned int size, int val)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
if (val <= array[i])
|
||||
return i;
|
||||
}
|
||||
|
||||
return size - 1;
|
||||
}
|
||||
|
||||
static int adxl372_set_bandwidth(struct adxl372_state *st,
|
||||
enum adxl372_bandwidth bw)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = regmap_update_bits(st->regmap, ADXL372_MEASURE,
|
||||
ADXL372_MEASURE_BANDWIDTH_MSK,
|
||||
ADXL372_MEASURE_BANDWIDTH_MODE(bw));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
st->bw = bw;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adxl372_set_act_proc_mode(struct adxl372_state *st,
|
||||
enum adxl372_act_proc_mode mode)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = regmap_update_bits(st->regmap,
|
||||
ADXL372_MEASURE,
|
||||
ADXL372_MEASURE_LINKLOOP_MSK,
|
||||
ADXL372_MEASURE_LINKLOOP_MODE(mode));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
st->act_proc_mode = mode;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adxl372_set_activity_threshold(struct adxl372_state *st,
|
||||
enum adxl372_th_activity act,
|
||||
bool ref_en, bool enable,
|
||||
unsigned int threshold)
|
||||
{
|
||||
unsigned char buf[6];
|
||||
unsigned char th_reg_high_val, th_reg_low_val, th_reg_high_addr;
|
||||
|
||||
/* scale factor is 100 mg/code */
|
||||
th_reg_high_val = (threshold / 100) >> 3;
|
||||
th_reg_low_val = ((threshold / 100) << 5) | (ref_en << 1) | enable;
|
||||
th_reg_high_addr = adxl372_th_reg_high_addr[act];
|
||||
|
||||
buf[0] = th_reg_high_val;
|
||||
buf[1] = th_reg_low_val;
|
||||
buf[2] = th_reg_high_val;
|
||||
buf[3] = th_reg_low_val;
|
||||
buf[4] = th_reg_high_val;
|
||||
buf[5] = th_reg_low_val;
|
||||
|
||||
return regmap_bulk_write(st->regmap, th_reg_high_addr,
|
||||
buf, ARRAY_SIZE(buf));
|
||||
}
|
||||
|
||||
static int adxl372_set_activity_time_ms(struct adxl372_state *st,
|
||||
unsigned int act_time_ms)
|
||||
{
|
||||
unsigned int reg_val, scale_factor;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* 3.3 ms per code is the scale factor of the TIME_ACT register for
|
||||
* ODR = 6400 Hz. It is 6.6 ms per code for ODR = 3200 Hz and below.
|
||||
*/
|
||||
if (st->odr == ADXL372_ODR_6400HZ)
|
||||
scale_factor = 3300;
|
||||
else
|
||||
scale_factor = 6600;
|
||||
|
||||
reg_val = DIV_ROUND_CLOSEST(act_time_ms * 1000, scale_factor);
|
||||
|
||||
/* TIME_ACT register is 8 bits wide */
|
||||
if (reg_val > 0xFF)
|
||||
reg_val = 0xFF;
|
||||
|
||||
ret = regmap_write(st->regmap, ADXL372_TIME_ACT, reg_val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
st->act_time_ms = act_time_ms;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adxl372_set_inactivity_time_ms(struct adxl372_state *st,
|
||||
unsigned int inact_time_ms)
|
||||
{
|
||||
unsigned int reg_val_h, reg_val_l, res, scale_factor;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* 13 ms per code is the scale factor of the TIME_INACT register for
|
||||
* ODR = 6400 Hz. It is 26 ms per code for ODR = 3200 Hz and below.
|
||||
*/
|
||||
if (st->odr == ADXL372_ODR_6400HZ)
|
||||
scale_factor = 13;
|
||||
else
|
||||
scale_factor = 26;
|
||||
|
||||
res = DIV_ROUND_CLOSEST(inact_time_ms, scale_factor);
|
||||
reg_val_h = (res >> 8) & 0xFF;
|
||||
reg_val_l = res & 0xFF;
|
||||
|
||||
ret = regmap_write(st->regmap, ADXL372_TIME_INACT_H, reg_val_h);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = regmap_write(st->regmap, ADXL372_TIME_INACT_L, reg_val_l);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
st->inact_time_ms = inact_time_ms;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adxl372_set_interrupts(struct adxl372_state *st,
|
||||
unsigned char int1_bitmask,
|
||||
unsigned char int2_bitmask)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = regmap_write(st->regmap, ADXL372_INT1_MAP, int1_bitmask);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return regmap_write(st->regmap, ADXL372_INT2_MAP, int2_bitmask);
|
||||
}
|
||||
|
||||
static int adxl372_configure_fifo(struct adxl372_state *st)
|
||||
{
|
||||
unsigned int fifo_samples, fifo_ctl;
|
||||
int ret;
|
||||
|
||||
/* FIFO must be configured while in standby mode */
|
||||
ret = adxl372_set_op_mode(st, ADXL372_STANDBY);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
fifo_samples = st->watermark & 0xFF;
|
||||
fifo_ctl = ADXL372_FIFO_CTL_FORMAT_MODE(st->fifo_format) |
|
||||
ADXL372_FIFO_CTL_MODE_MODE(st->fifo_mode) |
|
||||
ADXL372_FIFO_CTL_SAMPLES_MODE(st->watermark);
|
||||
|
||||
ret = regmap_write(st->regmap, ADXL372_FIFO_SAMPLES, fifo_samples);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = regmap_write(st->regmap, ADXL372_FIFO_CTL, fifo_ctl);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return adxl372_set_op_mode(st, ADXL372_FULL_BW_MEASUREMENT);
|
||||
}
|
||||
|
||||
static int adxl372_get_status(struct adxl372_state *st,
|
||||
u8 *status1, u8 *status2,
|
||||
u16 *fifo_entries)
|
||||
{
|
||||
__be32 buf;
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
/* STATUS1, STATUS2, FIFO_ENTRIES2 and FIFO_ENTRIES are adjacent regs */
|
||||
ret = regmap_bulk_read(st->regmap, ADXL372_STATUS_1,
|
||||
&buf, sizeof(buf));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
val = be32_to_cpu(buf);
|
||||
|
||||
*status1 = (val >> 24) & 0x0F;
|
||||
*status2 = (val >> 16) & 0x0F;
|
||||
/*
|
||||
* FIFO_ENTRIES contains the least significant byte, and FIFO_ENTRIES2
|
||||
* contains the two most significant bits
|
||||
*/
|
||||
*fifo_entries = val & 0x3FF;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static irqreturn_t adxl372_trigger_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct adxl372_state *st = iio_priv(indio_dev);
|
||||
u8 status1, status2;
|
||||
u16 fifo_entries;
|
||||
int i, ret;
|
||||
|
||||
ret = adxl372_get_status(st, &status1, &status2, &fifo_entries);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
if (st->fifo_mode != ADXL372_FIFO_BYPASSED &&
|
||||
ADXL372_STATUS_1_FIFO_FULL(status1)) {
|
||||
/*
|
||||
* When reading data from multiple axes from the FIFO,
|
||||
* to ensure that data is not overwritten and stored out
|
||||
* of order at least one sample set must be left in the
|
||||
* FIFO after every read.
|
||||
*/
|
||||
fifo_entries -= st->fifo_set_size;
|
||||
|
||||
/* Read data from the FIFO */
|
||||
ret = regmap_noinc_read(st->regmap, ADXL372_FIFO_DATA,
|
||||
st->fifo_buf,
|
||||
fifo_entries * sizeof(u16));
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
/* Each sample is 2 bytes */
|
||||
for (i = 0; i < fifo_entries * sizeof(u16);
|
||||
i += st->fifo_set_size * sizeof(u16))
|
||||
iio_push_to_buffers(indio_dev, &st->fifo_buf[i]);
|
||||
}
|
||||
err:
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int adxl372_setup(struct adxl372_state *st)
|
||||
{
|
||||
unsigned int regval;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(st->regmap, ADXL372_DEVID, ®val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (regval != ADXL372_DEVID_VAL) {
|
||||
dev_err(st->dev, "Invalid chip id %x\n", regval);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ret = adxl372_set_op_mode(st, ADXL372_STANDBY);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Set threshold for activity detection to 1g */
|
||||
ret = adxl372_set_activity_threshold(st, ADXL372_ACTIVITY,
|
||||
true, true, 1000);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Set threshold for inactivity detection to 100mg */
|
||||
ret = adxl372_set_activity_threshold(st, ADXL372_INACTIVITY,
|
||||
true, true, 100);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Set activity processing in Looped mode */
|
||||
ret = adxl372_set_act_proc_mode(st, ADXL372_LOOPED);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = adxl372_set_odr(st, ADXL372_ODR_6400HZ);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = adxl372_set_bandwidth(st, ADXL372_BW_3200HZ);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Set activity timer to 1ms */
|
||||
ret = adxl372_set_activity_time_ms(st, 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Set inactivity timer to 10s */
|
||||
ret = adxl372_set_inactivity_time_ms(st, 10000);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Set the mode of operation to full bandwidth measurement mode */
|
||||
return adxl372_set_op_mode(st, ADXL372_FULL_BW_MEASUREMENT);
|
||||
}
|
||||
|
||||
static int adxl372_reg_access(struct iio_dev *indio_dev,
|
||||
unsigned int reg,
|
||||
unsigned int writeval,
|
||||
unsigned int *readval)
|
||||
{
|
||||
struct adxl372_state *st = iio_priv(indio_dev);
|
||||
|
||||
if (readval)
|
||||
return regmap_read(st->regmap, reg, readval);
|
||||
else
|
||||
return regmap_write(st->regmap, reg, writeval);
|
||||
}
|
||||
|
||||
static int adxl372_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long info)
|
||||
{
|
||||
struct adxl372_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (info) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = iio_device_claim_direct_mode(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = adxl372_read_axis(st, chan->address);
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = sign_extend32(ret >> chan->scan_type.shift,
|
||||
chan->scan_type.realbits - 1);
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = 0;
|
||||
*val2 = ADXL372_USCALE;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
*val = adxl372_samp_freq_tbl[st->odr];
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
|
||||
*val = adxl372_bw_freq_tbl[st->bw];
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int adxl372_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long info)
|
||||
{
|
||||
struct adxl372_state *st = iio_priv(indio_dev);
|
||||
int odr_index, bw_index, ret;
|
||||
|
||||
switch (info) {
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
odr_index = adxl372_find_closest_match(adxl372_samp_freq_tbl,
|
||||
ARRAY_SIZE(adxl372_samp_freq_tbl),
|
||||
val);
|
||||
ret = adxl372_set_odr(st, odr_index);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
/*
|
||||
* The timer period depends on the ODR selected.
|
||||
* At 3200 Hz and below, it is 6.6 ms; at 6400 Hz, it is 3.3 ms
|
||||
*/
|
||||
ret = adxl372_set_activity_time_ms(st, st->act_time_ms);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
/*
|
||||
* The timer period depends on the ODR selected.
|
||||
* At 3200 Hz and below, it is 26 ms; at 6400 Hz, it is 13 ms
|
||||
*/
|
||||
ret = adxl372_set_inactivity_time_ms(st, st->inact_time_ms);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
/*
|
||||
* The maximum bandwidth is constrained to at most half of
|
||||
* the ODR to ensure that the Nyquist criteria is not violated
|
||||
*/
|
||||
if (st->bw > odr_index)
|
||||
ret = adxl372_set_bandwidth(st, odr_index);
|
||||
|
||||
return ret;
|
||||
case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
|
||||
bw_index = adxl372_find_closest_match(adxl372_bw_freq_tbl,
|
||||
ARRAY_SIZE(adxl372_bw_freq_tbl),
|
||||
val);
|
||||
return adxl372_set_bandwidth(st, bw_index);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t adxl372_show_filter_freq_avail(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct adxl372_state *st = iio_priv(indio_dev);
|
||||
int i;
|
||||
size_t len = 0;
|
||||
|
||||
for (i = 0; i <= st->odr; i++)
|
||||
len += scnprintf(buf + len, PAGE_SIZE - len,
|
||||
"%d ", adxl372_bw_freq_tbl[i]);
|
||||
|
||||
buf[len - 1] = '\n';
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t adxl372_get_fifo_enabled(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct adxl372_state *st = iio_priv(indio_dev);
|
||||
|
||||
return sprintf(buf, "%d\n", st->fifo_mode);
|
||||
}
|
||||
|
||||
static ssize_t adxl372_get_fifo_watermark(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct adxl372_state *st = iio_priv(indio_dev);
|
||||
|
||||
return sprintf(buf, "%d\n", st->watermark);
|
||||
}
|
||||
|
||||
static IIO_CONST_ATTR(hwfifo_watermark_min, "1");
|
||||
static IIO_CONST_ATTR(hwfifo_watermark_max,
|
||||
__stringify(ADXL372_FIFO_SIZE));
|
||||
static IIO_DEVICE_ATTR(hwfifo_watermark, 0444,
|
||||
adxl372_get_fifo_watermark, NULL, 0);
|
||||
static IIO_DEVICE_ATTR(hwfifo_enabled, 0444,
|
||||
adxl372_get_fifo_enabled, NULL, 0);
|
||||
|
||||
static const struct attribute *adxl372_fifo_attributes[] = {
|
||||
&iio_const_attr_hwfifo_watermark_min.dev_attr.attr,
|
||||
&iio_const_attr_hwfifo_watermark_max.dev_attr.attr,
|
||||
&iio_dev_attr_hwfifo_watermark.dev_attr.attr,
|
||||
&iio_dev_attr_hwfifo_enabled.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static int adxl372_set_watermark(struct iio_dev *indio_dev, unsigned int val)
|
||||
{
|
||||
struct adxl372_state *st = iio_priv(indio_dev);
|
||||
|
||||
if (val > ADXL372_FIFO_SIZE)
|
||||
val = ADXL372_FIFO_SIZE;
|
||||
|
||||
st->watermark = val;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adxl372_buffer_postenable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct adxl372_state *st = iio_priv(indio_dev);
|
||||
unsigned int mask;
|
||||
int i, ret;
|
||||
|
||||
ret = adxl372_set_interrupts(st, ADXL372_INT1_MAP_FIFO_FULL_MSK, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
mask = *indio_dev->active_scan_mask;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(adxl372_axis_lookup_table); i++) {
|
||||
if (mask == adxl372_axis_lookup_table[i].bits)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == ARRAY_SIZE(adxl372_axis_lookup_table))
|
||||
return -EINVAL;
|
||||
|
||||
st->fifo_format = adxl372_axis_lookup_table[i].fifo_format;
|
||||
st->fifo_set_size = bitmap_weight(indio_dev->active_scan_mask,
|
||||
indio_dev->masklength);
|
||||
/*
|
||||
* The 512 FIFO samples can be allotted in several ways, such as:
|
||||
* 170 sample sets of concurrent 3-axis data
|
||||
* 256 sample sets of concurrent 2-axis data (user selectable)
|
||||
* 512 sample sets of single-axis data
|
||||
*/
|
||||
if ((st->watermark * st->fifo_set_size) > ADXL372_FIFO_SIZE)
|
||||
st->watermark = (ADXL372_FIFO_SIZE / st->fifo_set_size);
|
||||
|
||||
st->fifo_mode = ADXL372_FIFO_STREAMED;
|
||||
|
||||
ret = adxl372_configure_fifo(st);
|
||||
if (ret < 0) {
|
||||
st->fifo_mode = ADXL372_FIFO_BYPASSED;
|
||||
adxl372_set_interrupts(st, 0, 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return iio_triggered_buffer_postenable(indio_dev);
|
||||
}
|
||||
|
||||
static int adxl372_buffer_predisable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct adxl372_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = iio_triggered_buffer_predisable(indio_dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
adxl372_set_interrupts(st, 0, 0);
|
||||
st->fifo_mode = ADXL372_FIFO_BYPASSED;
|
||||
adxl372_configure_fifo(st);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct iio_buffer_setup_ops adxl372_buffer_ops = {
|
||||
.postenable = adxl372_buffer_postenable,
|
||||
.predisable = adxl372_buffer_predisable,
|
||||
};
|
||||
|
||||
static int adxl372_dready_trig_set_state(struct iio_trigger *trig,
|
||||
bool state)
|
||||
{
|
||||
struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
|
||||
struct adxl372_state *st = iio_priv(indio_dev);
|
||||
unsigned long int mask = 0;
|
||||
|
||||
if (state)
|
||||
mask = ADXL372_INT1_MAP_FIFO_FULL_MSK;
|
||||
|
||||
return adxl372_set_interrupts(st, mask, 0);
|
||||
}
|
||||
|
||||
static int adxl372_validate_trigger(struct iio_dev *indio_dev,
|
||||
struct iio_trigger *trig)
|
||||
{
|
||||
struct adxl372_state *st = iio_priv(indio_dev);
|
||||
|
||||
if (st->dready_trig != trig)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct iio_trigger_ops adxl372_trigger_ops = {
|
||||
.validate_device = &iio_trigger_validate_own_device,
|
||||
.set_trigger_state = adxl372_dready_trig_set_state,
|
||||
};
|
||||
|
||||
static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("400 800 1600 3200 6400");
|
||||
static IIO_DEVICE_ATTR(in_accel_filter_low_pass_3db_frequency_available,
|
||||
0444, adxl372_show_filter_freq_avail, NULL, 0);
|
||||
|
||||
static struct attribute *adxl372_attributes[] = {
|
||||
&iio_const_attr_sampling_frequency_available.dev_attr.attr,
|
||||
&iio_dev_attr_in_accel_filter_low_pass_3db_frequency_available.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group adxl372_attrs_group = {
|
||||
.attrs = adxl372_attributes,
|
||||
};
|
||||
|
||||
static const struct iio_info adxl372_info = {
|
||||
.validate_trigger = &adxl372_validate_trigger,
|
||||
.attrs = &adxl372_attrs_group,
|
||||
.read_raw = adxl372_read_raw,
|
||||
.write_raw = adxl372_write_raw,
|
||||
.debugfs_reg_access = &adxl372_reg_access,
|
||||
.hwfifo_set_watermark = adxl372_set_watermark,
|
||||
};
|
||||
|
||||
bool adxl372_readable_noinc_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
return (reg == ADXL372_FIFO_DATA);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(adxl372_readable_noinc_reg);
|
||||
|
||||
int adxl372_probe(struct device *dev, struct regmap *regmap,
|
||||
int irq, const char *name)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct adxl372_state *st;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
dev_set_drvdata(dev, indio_dev);
|
||||
|
||||
st->dev = dev;
|
||||
st->regmap = regmap;
|
||||
st->irq = irq;
|
||||
|
||||
indio_dev->channels = adxl372_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(adxl372_channels);
|
||||
indio_dev->available_scan_masks = adxl372_channel_masks;
|
||||
indio_dev->dev.parent = dev;
|
||||
indio_dev->name = name;
|
||||
indio_dev->info = &adxl372_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE;
|
||||
|
||||
ret = adxl372_setup(st);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "ADXL372 setup failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = devm_iio_triggered_buffer_setup(dev,
|
||||
indio_dev, NULL,
|
||||
adxl372_trigger_handler,
|
||||
&adxl372_buffer_ops);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
iio_buffer_set_attrs(indio_dev->buffer, adxl372_fifo_attributes);
|
||||
|
||||
if (st->irq) {
|
||||
st->dready_trig = devm_iio_trigger_alloc(dev,
|
||||
"%s-dev%d",
|
||||
indio_dev->name,
|
||||
indio_dev->id);
|
||||
if (st->dready_trig == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
st->dready_trig->ops = &adxl372_trigger_ops;
|
||||
st->dready_trig->dev.parent = dev;
|
||||
iio_trigger_set_drvdata(st->dready_trig, indio_dev);
|
||||
ret = devm_iio_trigger_register(dev, st->dready_trig);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
indio_dev->trig = iio_trigger_get(st->dready_trig);
|
||||
|
||||
ret = devm_request_threaded_irq(dev, st->irq,
|
||||
iio_trigger_generic_data_rdy_poll,
|
||||
NULL,
|
||||
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
|
||||
indio_dev->name, st->dready_trig);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return devm_iio_device_register(dev, indio_dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(adxl372_probe);
|
||||
|
||||
MODULE_AUTHOR("Stefan Popa <stefan.popa@analog.com>");
|
||||
MODULE_DESCRIPTION("Analog Devices ADXL372 3-axis accelerometer driver");
|
||||
MODULE_LICENSE("GPL");
|
17
drivers/iio/accel/adxl372.h
Normal file
17
drivers/iio/accel/adxl372.h
Normal file
@ -0,0 +1,17 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* ADXL372 3-Axis Digital Accelerometer
|
||||
*
|
||||
* Copyright 2018 Analog Devices Inc.
|
||||
*/
|
||||
|
||||
#ifndef _ADXL372_H_
|
||||
#define _ADXL372_H_
|
||||
|
||||
#define ADXL372_REVID 0x03
|
||||
|
||||
int adxl372_probe(struct device *dev, struct regmap *regmap,
|
||||
int irq, const char *name);
|
||||
bool adxl372_readable_noinc_reg(struct device *dev, unsigned int reg);
|
||||
|
||||
#endif /* _ADXL372_H_ */
|
61
drivers/iio/accel/adxl372_i2c.c
Normal file
61
drivers/iio/accel/adxl372_i2c.c
Normal file
@ -0,0 +1,61 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* ADXL372 3-Axis Digital Accelerometer I2C driver
|
||||
*
|
||||
* Copyright 2018 Analog Devices Inc.
|
||||
*/
|
||||
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include "adxl372.h"
|
||||
|
||||
static const struct regmap_config adxl372_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.readable_noinc_reg = adxl372_readable_noinc_reg,
|
||||
};
|
||||
|
||||
static int adxl372_i2c_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct regmap *regmap;
|
||||
unsigned int regval;
|
||||
int ret;
|
||||
|
||||
regmap = devm_regmap_init_i2c(client, &adxl372_regmap_config);
|
||||
if (IS_ERR(regmap))
|
||||
return PTR_ERR(regmap);
|
||||
|
||||
ret = regmap_read(regmap, ADXL372_REVID, ®val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Starting with the 3rd revision an I2C chip bug was fixed */
|
||||
if (regval < 3)
|
||||
dev_warn(&client->dev,
|
||||
"I2C might not work properly with other devices on the bus");
|
||||
|
||||
return adxl372_probe(&client->dev, regmap, client->irq, id->name);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id adxl372_i2c_id[] = {
|
||||
{ "adxl372", 0 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, adxl372_i2c_id);
|
||||
|
||||
static struct i2c_driver adxl372_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "adxl372_i2c",
|
||||
},
|
||||
.probe = adxl372_i2c_probe,
|
||||
.id_table = adxl372_i2c_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(adxl372_i2c_driver);
|
||||
|
||||
MODULE_AUTHOR("Stefan Popa <stefan.popa@analog.com>");
|
||||
MODULE_DESCRIPTION("Analog Devices ADXL372 3-axis accelerometer I2C driver");
|
||||
MODULE_LICENSE("GPL");
|
52
drivers/iio/accel/adxl372_spi.c
Normal file
52
drivers/iio/accel/adxl372_spi.c
Normal file
@ -0,0 +1,52 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* ADXL372 3-Axis Digital Accelerometer SPI driver
|
||||
*
|
||||
* Copyright 2018 Analog Devices Inc.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#include "adxl372.h"
|
||||
|
||||
static const struct regmap_config adxl372_spi_regmap_config = {
|
||||
.reg_bits = 7,
|
||||
.pad_bits = 1,
|
||||
.val_bits = 8,
|
||||
.read_flag_mask = BIT(0),
|
||||
.readable_noinc_reg = adxl372_readable_noinc_reg,
|
||||
};
|
||||
|
||||
static int adxl372_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
const struct spi_device_id *id = spi_get_device_id(spi);
|
||||
struct regmap *regmap;
|
||||
|
||||
regmap = devm_regmap_init_spi(spi, &adxl372_spi_regmap_config);
|
||||
if (IS_ERR(regmap))
|
||||
return PTR_ERR(regmap);
|
||||
|
||||
return adxl372_probe(&spi->dev, regmap, spi->irq, id->name);
|
||||
}
|
||||
|
||||
static const struct spi_device_id adxl372_spi_id[] = {
|
||||
{ "adxl372", 0 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, adxl372_spi_id);
|
||||
|
||||
static struct spi_driver adxl372_spi_driver = {
|
||||
.driver = {
|
||||
.name = "adxl372_spi",
|
||||
},
|
||||
.probe = adxl372_spi_probe,
|
||||
.id_table = adxl372_spi_id,
|
||||
};
|
||||
|
||||
module_spi_driver(adxl372_spi_driver);
|
||||
|
||||
MODULE_AUTHOR("Stefan Popa <stefan.popa@analog.com>");
|
||||
MODULE_DESCRIPTION("Analog Devices ADXL372 3-axis accelerometer SPI driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -501,6 +501,16 @@ config MCP3422
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called mcp3422.
|
||||
|
||||
config MCP3911
|
||||
tristate "Microchip Technology MCP3911 driver"
|
||||
depends on SPI
|
||||
help
|
||||
Say yes here to build support for Microchip Technology's MCP3911
|
||||
analog to digital converter.
|
||||
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called mcp3911.
|
||||
|
||||
config MEDIATEK_MT6577_AUXADC
|
||||
tristate "MediaTek AUXADC driver"
|
||||
depends on ARCH_MEDIATEK || COMPILE_TEST
|
||||
@ -596,6 +606,26 @@ config QCOM_SPMI_VADC
|
||||
To compile this driver as a module, choose M here: the module will
|
||||
be called qcom-spmi-vadc.
|
||||
|
||||
config QCOM_SPMI_ADC5
|
||||
tristate "Qualcomm Technologies Inc. SPMI PMIC5 ADC"
|
||||
depends on SPMI
|
||||
select REGMAP_SPMI
|
||||
select QCOM_VADC_COMMON
|
||||
help
|
||||
This is the IIO Voltage PMIC5 ADC driver for Qualcomm Technologies Inc.
|
||||
|
||||
The driver supports multiple channels read. The ADC is a 16-bit
|
||||
sigma-delta ADC. The hardware supports calibrated results for
|
||||
conversion requests and clients include reading voltage phone
|
||||
power, on board system thermistors connected to the PMIC ADC,
|
||||
PMIC die temperature, charger temperature, battery current, USB voltage
|
||||
input, voltage signals connected to supported PMIC GPIO inputs. The
|
||||
hardware supports internal pull-up for thermistors and can choose between
|
||||
a 100k, 30k and 400k pull up using the ADC channels.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will
|
||||
be called qcom-spmi-adc5.
|
||||
|
||||
config RCAR_GYRO_ADC
|
||||
tristate "Renesas R-Car GyroADC driver"
|
||||
depends on ARCH_RCAR_GEN2 || COMPILE_TEST
|
||||
|
@ -47,12 +47,14 @@ obj-$(CONFIG_MAX1363) += max1363.o
|
||||
obj-$(CONFIG_MAX9611) += max9611.o
|
||||
obj-$(CONFIG_MCP320X) += mcp320x.o
|
||||
obj-$(CONFIG_MCP3422) += mcp3422.o
|
||||
obj-$(CONFIG_MCP3911) += mcp3911.o
|
||||
obj-$(CONFIG_MEDIATEK_MT6577_AUXADC) += mt6577_auxadc.o
|
||||
obj-$(CONFIG_MEN_Z188_ADC) += men_z188_adc.o
|
||||
obj-$(CONFIG_MESON_SARADC) += meson_saradc.o
|
||||
obj-$(CONFIG_MXS_LRADC_ADC) += mxs-lradc-adc.o
|
||||
obj-$(CONFIG_NAU7802) += nau7802.o
|
||||
obj-$(CONFIG_PALMAS_GPADC) += palmas_gpadc.o
|
||||
obj-$(CONFIG_QCOM_SPMI_ADC5) += qcom-spmi-adc5.o
|
||||
obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o
|
||||
obj-$(CONFIG_QCOM_VADC_COMMON) += qcom-vadc-common.o
|
||||
obj-$(CONFIG_QCOM_SPMI_VADC) += qcom-spmi-vadc.o
|
||||
|
@ -385,6 +385,6 @@ static struct spi_driver ad7298_driver = {
|
||||
};
|
||||
module_spi_driver(ad7298_driver);
|
||||
|
||||
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
|
||||
MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD7298 ADC");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
@ -328,6 +328,6 @@ static struct spi_driver ad7476_driver = {
|
||||
};
|
||||
module_spi_driver(ad7476_driver);
|
||||
|
||||
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
|
||||
MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD7476 and similar 1-channel ADCs");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
@ -822,6 +822,6 @@ static struct spi_driver ad7793_driver = {
|
||||
};
|
||||
module_spi_driver(ad7793_driver);
|
||||
|
||||
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
|
||||
MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD7793 and similar ADCs");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
@ -362,6 +362,6 @@ static struct spi_driver ad7887_driver = {
|
||||
};
|
||||
module_spi_driver(ad7887_driver);
|
||||
|
||||
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
|
||||
MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD7887 ADC");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
@ -363,7 +363,7 @@ static struct spi_driver ad7923_driver = {
|
||||
};
|
||||
module_spi_driver(ad7923_driver);
|
||||
|
||||
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
|
||||
MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
|
||||
MODULE_AUTHOR("Patrick Vasseur <patrick.vasseur@c-s.fr>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD7904/AD7914/AD7923/AD7924 ADC");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
@ -892,6 +892,6 @@ static struct i2c_driver ad799x_driver = {
|
||||
};
|
||||
module_i2c_driver(ad799x_driver);
|
||||
|
||||
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
|
||||
MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD799x ADC");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
@ -248,12 +248,14 @@ static irqreturn_t at91_adc_trigger_handler(int irq, void *p)
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *idev = pf->indio_dev;
|
||||
struct at91_adc_state *st = iio_priv(idev);
|
||||
struct iio_chan_spec const *chan;
|
||||
int i, j = 0;
|
||||
|
||||
for (i = 0; i < idev->masklength; i++) {
|
||||
if (!test_bit(i, idev->active_scan_mask))
|
||||
continue;
|
||||
st->buffer[j] = at91_adc_readl(st, AT91_ADC_CHAN(st, i));
|
||||
chan = idev->channels + i;
|
||||
st->buffer[j] = at91_adc_readl(st, AT91_ADC_CHAN(st, chan->channel));
|
||||
j++;
|
||||
}
|
||||
|
||||
@ -279,6 +281,8 @@ static void handle_adc_eoc_trigger(int irq, struct iio_dev *idev)
|
||||
iio_trigger_poll(idev->trig);
|
||||
} else {
|
||||
st->last_value = at91_adc_readl(st, AT91_ADC_CHAN(st, st->chnb));
|
||||
/* Needed to ACK the DRDY interruption */
|
||||
at91_adc_readl(st, AT91_ADC_LCDR);
|
||||
st->done = true;
|
||||
wake_up_interruptible(&st->wq_data_avail);
|
||||
}
|
||||
|
@ -1,13 +1,10 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Driver for an envelope detector using a DAC and a comparator
|
||||
*
|
||||
* Copyright (C) 2016 Axentia Technologies AB
|
||||
*
|
||||
* Author: Peter Rosin <peda@axentia.se>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
|
@ -209,12 +209,14 @@ static int mx25_gcq_setup_cfgs(struct platform_device *pdev,
|
||||
ret = of_property_read_u32(child, "reg", ®);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to get reg property\n");
|
||||
of_node_put(child);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (reg >= MX25_NUM_CFGS) {
|
||||
dev_err(dev,
|
||||
"reg value is greater than the number of available configuration registers\n");
|
||||
of_node_put(child);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -228,6 +230,7 @@ static int mx25_gcq_setup_cfgs(struct platform_device *pdev,
|
||||
if (IS_ERR(priv->vref[refp])) {
|
||||
dev_err(dev, "Error, trying to use external voltage reference without a vref-%s regulator.",
|
||||
mx25_gcq_refp_names[refp]);
|
||||
of_node_put(child);
|
||||
return PTR_ERR(priv->vref[refp]);
|
||||
}
|
||||
priv->channel_vref_mv[reg] =
|
||||
@ -240,6 +243,7 @@ static int mx25_gcq_setup_cfgs(struct platform_device *pdev,
|
||||
break;
|
||||
default:
|
||||
dev_err(dev, "Invalid positive reference %d\n", refp);
|
||||
of_node_put(child);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -254,10 +258,12 @@ static int mx25_gcq_setup_cfgs(struct platform_device *pdev,
|
||||
|
||||
if ((refp & MX25_ADCQ_CFG_REFP_MASK) != refp) {
|
||||
dev_err(dev, "Invalid fsl,adc-refp property value\n");
|
||||
of_node_put(child);
|
||||
return -EINVAL;
|
||||
}
|
||||
if ((refn & MX25_ADCQ_CFG_REFN_MASK) != refn) {
|
||||
dev_err(dev, "Invalid fsl,adc-refn property value\n");
|
||||
of_node_put(child);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
@ -289,7 +289,7 @@ static int max9611_read_csa_voltage(struct max9611_dev *max9611,
|
||||
return ret;
|
||||
|
||||
if (*adc_raw > 0) {
|
||||
*csa_gain = gain_selectors[i];
|
||||
*csa_gain = (enum max9611_csa_gain)gain_selectors[i];
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
363
drivers/iio/adc/mcp3911.c
Normal file
363
drivers/iio/adc/mcp3911.c
Normal file
@ -0,0 +1,363 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Driver for Microchip MCP3911, Two-channel Analog Front End
|
||||
*
|
||||
* Copyright (C) 2018 Marcus Folkesson <marcus.folkesson@gmail.com>
|
||||
* Copyright (C) 2018 Kent Gustavsson <kent@minoris.se>
|
||||
*/
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#define MCP3911_REG_CHANNEL0 0x00
|
||||
#define MCP3911_REG_CHANNEL1 0x03
|
||||
#define MCP3911_REG_MOD 0x06
|
||||
#define MCP3911_REG_PHASE 0x07
|
||||
#define MCP3911_REG_GAIN 0x09
|
||||
|
||||
#define MCP3911_REG_STATUSCOM 0x0a
|
||||
#define MCP3911_STATUSCOM_CH1_24WIDTH BIT(4)
|
||||
#define MCP3911_STATUSCOM_CH0_24WIDTH BIT(3)
|
||||
#define MCP3911_STATUSCOM_EN_OFFCAL BIT(2)
|
||||
#define MCP3911_STATUSCOM_EN_GAINCAL BIT(1)
|
||||
|
||||
#define MCP3911_REG_CONFIG 0x0c
|
||||
#define MCP3911_CONFIG_CLKEXT BIT(1)
|
||||
#define MCP3911_CONFIG_VREFEXT BIT(2)
|
||||
|
||||
#define MCP3911_REG_OFFCAL_CH0 0x0e
|
||||
#define MCP3911_REG_GAINCAL_CH0 0x11
|
||||
#define MCP3911_REG_OFFCAL_CH1 0x14
|
||||
#define MCP3911_REG_GAINCAL_CH1 0x17
|
||||
#define MCP3911_REG_VREFCAL 0x1a
|
||||
|
||||
#define MCP3911_CHANNEL(x) (MCP3911_REG_CHANNEL0 + x * 3)
|
||||
#define MCP3911_OFFCAL(x) (MCP3911_REG_OFFCAL_CH0 + x * 6)
|
||||
|
||||
/* Internal voltage reference in uV */
|
||||
#define MCP3911_INT_VREF_UV 1200000
|
||||
|
||||
#define MCP3911_REG_READ(reg, id) ((((reg) << 1) | ((id) << 5) | (1 << 0)) & 0xff)
|
||||
#define MCP3911_REG_WRITE(reg, id) ((((reg) << 1) | ((id) << 5) | (0 << 0)) & 0xff)
|
||||
|
||||
#define MCP3911_NUM_CHANNELS 2
|
||||
|
||||
struct mcp3911 {
|
||||
struct spi_device *spi;
|
||||
struct mutex lock;
|
||||
struct regulator *vref;
|
||||
struct clk *clki;
|
||||
u32 dev_addr;
|
||||
};
|
||||
|
||||
static int mcp3911_read(struct mcp3911 *adc, u8 reg, u32 *val, u8 len)
|
||||
{
|
||||
int ret;
|
||||
|
||||
reg = MCP3911_REG_READ(reg, adc->dev_addr);
|
||||
ret = spi_write_then_read(adc->spi, ®, 1, val, len);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
be32_to_cpus(val);
|
||||
*val >>= ((4 - len) * 8);
|
||||
dev_dbg(&adc->spi->dev, "reading 0x%x from register 0x%x\n", *val,
|
||||
reg >> 1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mcp3911_write(struct mcp3911 *adc, u8 reg, u32 val, u8 len)
|
||||
{
|
||||
dev_dbg(&adc->spi->dev, "writing 0x%x to register 0x%x\n", val, reg);
|
||||
|
||||
val <<= (3 - len) * 8;
|
||||
cpu_to_be32s(&val);
|
||||
val |= MCP3911_REG_WRITE(reg, adc->dev_addr);
|
||||
|
||||
return spi_write(adc->spi, &val, len + 1);
|
||||
}
|
||||
|
||||
static int mcp3911_update(struct mcp3911 *adc, u8 reg, u32 mask,
|
||||
u32 val, u8 len)
|
||||
{
|
||||
u32 tmp;
|
||||
int ret;
|
||||
|
||||
ret = mcp3911_read(adc, reg, &tmp, len);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
val &= mask;
|
||||
val |= tmp & ~mask;
|
||||
return mcp3911_write(adc, reg, val, len);
|
||||
}
|
||||
|
||||
static int mcp3911_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *channel, int *val,
|
||||
int *val2, long mask)
|
||||
{
|
||||
struct mcp3911 *adc = iio_priv(indio_dev);
|
||||
int ret = -EINVAL;
|
||||
|
||||
mutex_lock(&adc->lock);
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = mcp3911_read(adc,
|
||||
MCP3911_CHANNEL(channel->channel), val, 3);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
ret = mcp3911_read(adc,
|
||||
MCP3911_OFFCAL(channel->channel), val, 3);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
if (adc->vref) {
|
||||
ret = regulator_get_voltage(adc->vref);
|
||||
if (ret < 0) {
|
||||
dev_err(indio_dev->dev.parent,
|
||||
"failed to get vref voltage: %d\n",
|
||||
ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
*val = ret / 1000;
|
||||
} else {
|
||||
*val = MCP3911_INT_VREF_UV;
|
||||
}
|
||||
|
||||
*val2 = 24;
|
||||
ret = IIO_VAL_FRACTIONAL_LOG2;
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
mutex_unlock(&adc->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mcp3911_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *channel, int val,
|
||||
int val2, long mask)
|
||||
{
|
||||
struct mcp3911 *adc = iio_priv(indio_dev);
|
||||
int ret = -EINVAL;
|
||||
|
||||
mutex_lock(&adc->lock);
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
if (val2 != 0) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Write offset */
|
||||
ret = mcp3911_write(adc, MCP3911_OFFCAL(channel->channel), val,
|
||||
3);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
/* Enable offset*/
|
||||
ret = mcp3911_update(adc, MCP3911_REG_STATUSCOM,
|
||||
MCP3911_STATUSCOM_EN_OFFCAL,
|
||||
MCP3911_STATUSCOM_EN_OFFCAL, 2);
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
mutex_unlock(&adc->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define MCP3911_CHAN(idx) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = idx, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_OFFSET) | \
|
||||
BIT(IIO_CHAN_INFO_SCALE), \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec mcp3911_channels[] = {
|
||||
MCP3911_CHAN(0),
|
||||
MCP3911_CHAN(1),
|
||||
};
|
||||
|
||||
static const struct iio_info mcp3911_info = {
|
||||
.read_raw = mcp3911_read_raw,
|
||||
.write_raw = mcp3911_write_raw,
|
||||
};
|
||||
|
||||
static int mcp3911_config(struct mcp3911 *adc, struct device_node *of_node)
|
||||
{
|
||||
u32 configreg;
|
||||
int ret;
|
||||
|
||||
of_property_read_u32(of_node, "device-addr", &adc->dev_addr);
|
||||
if (adc->dev_addr > 3) {
|
||||
dev_err(&adc->spi->dev,
|
||||
"invalid device address (%i). Must be in range 0-3.\n",
|
||||
adc->dev_addr);
|
||||
return -EINVAL;
|
||||
}
|
||||
dev_dbg(&adc->spi->dev, "use device address %i\n", adc->dev_addr);
|
||||
|
||||
ret = mcp3911_read(adc, MCP3911_REG_CONFIG, &configreg, 2);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (adc->vref) {
|
||||
dev_dbg(&adc->spi->dev, "use external voltage reference\n");
|
||||
configreg |= MCP3911_CONFIG_VREFEXT;
|
||||
} else {
|
||||
dev_dbg(&adc->spi->dev,
|
||||
"use internal voltage reference (1.2V)\n");
|
||||
configreg &= ~MCP3911_CONFIG_VREFEXT;
|
||||
}
|
||||
|
||||
if (adc->clki) {
|
||||
dev_dbg(&adc->spi->dev, "use external clock as clocksource\n");
|
||||
configreg |= MCP3911_CONFIG_CLKEXT;
|
||||
} else {
|
||||
dev_dbg(&adc->spi->dev,
|
||||
"use crystal oscillator as clocksource\n");
|
||||
configreg &= ~MCP3911_CONFIG_CLKEXT;
|
||||
}
|
||||
|
||||
return mcp3911_write(adc, MCP3911_REG_CONFIG, configreg, 2);
|
||||
}
|
||||
|
||||
static int mcp3911_probe(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct mcp3911 *adc;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
adc = iio_priv(indio_dev);
|
||||
adc->spi = spi;
|
||||
|
||||
adc->vref = devm_regulator_get_optional(&adc->spi->dev, "vref");
|
||||
if (IS_ERR(adc->vref)) {
|
||||
if (PTR_ERR(adc->vref) == -ENODEV) {
|
||||
adc->vref = NULL;
|
||||
} else {
|
||||
dev_err(&adc->spi->dev,
|
||||
"failed to get regulator (%ld)\n",
|
||||
PTR_ERR(adc->vref));
|
||||
return PTR_ERR(adc->vref);
|
||||
}
|
||||
|
||||
} else {
|
||||
ret = regulator_enable(adc->vref);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
adc->clki = devm_clk_get(&adc->spi->dev, NULL);
|
||||
if (IS_ERR(adc->clki)) {
|
||||
if (PTR_ERR(adc->clki) == -ENOENT) {
|
||||
adc->clki = NULL;
|
||||
} else {
|
||||
dev_err(&adc->spi->dev,
|
||||
"failed to get adc clk (%ld)\n",
|
||||
PTR_ERR(adc->clki));
|
||||
ret = PTR_ERR(adc->clki);
|
||||
goto reg_disable;
|
||||
}
|
||||
} else {
|
||||
ret = clk_prepare_enable(adc->clki);
|
||||
if (ret < 0) {
|
||||
dev_err(&adc->spi->dev,
|
||||
"Failed to enable clki: %d\n", ret);
|
||||
goto reg_disable;
|
||||
}
|
||||
}
|
||||
|
||||
ret = mcp3911_config(adc, spi->dev.of_node);
|
||||
if (ret)
|
||||
goto clk_disable;
|
||||
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->dev.of_node = spi->dev.of_node;
|
||||
indio_dev->name = spi_get_device_id(spi)->name;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &mcp3911_info;
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
|
||||
indio_dev->channels = mcp3911_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(mcp3911_channels);
|
||||
|
||||
mutex_init(&adc->lock);
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto clk_disable;
|
||||
|
||||
return ret;
|
||||
|
||||
clk_disable:
|
||||
clk_disable_unprepare(adc->clki);
|
||||
reg_disable:
|
||||
if (adc->vref)
|
||||
regulator_disable(adc->vref);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mcp3911_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct mcp3911 *adc = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
clk_disable_unprepare(adc->clki);
|
||||
if (adc->vref)
|
||||
regulator_disable(adc->vref);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id mcp3911_dt_ids[] = {
|
||||
{ .compatible = "microchip,mcp3911" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mcp3911_dt_ids);
|
||||
|
||||
static const struct spi_device_id mcp3911_id[] = {
|
||||
{ "mcp3911", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, mcp3911_id);
|
||||
|
||||
static struct spi_driver mcp3911_driver = {
|
||||
.driver = {
|
||||
.name = "mcp3911",
|
||||
.of_match_table = mcp3911_dt_ids,
|
||||
},
|
||||
.probe = mcp3911_probe,
|
||||
.remove = mcp3911_remove,
|
||||
.id_table = mcp3911_id,
|
||||
};
|
||||
module_spi_driver(mcp3911_driver);
|
||||
|
||||
MODULE_AUTHOR("Marcus Folkesson <marcus.folkesson@gmail.com>");
|
||||
MODULE_AUTHOR("Kent Gustavsson <kent@minoris.se>");
|
||||
MODULE_DESCRIPTION("Microchip Technology MCP3911");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -148,7 +148,6 @@
|
||||
#define MESON_SAR_ADC_DELTA_10_TS_REVE1 BIT(26)
|
||||
#define MESON_SAR_ADC_DELTA_10_CHAN1_DELTA_VALUE_MASK GENMASK(25, 16)
|
||||
#define MESON_SAR_ADC_DELTA_10_TS_REVE0 BIT(15)
|
||||
#define MESON_SAR_ADC_DELTA_10_TS_C_SHIFT 11
|
||||
#define MESON_SAR_ADC_DELTA_10_TS_C_MASK GENMASK(14, 11)
|
||||
#define MESON_SAR_ADC_DELTA_10_TS_VBG_EN BIT(10)
|
||||
#define MESON_SAR_ADC_DELTA_10_CHAN0_DELTA_VALUE_MASK GENMASK(9, 0)
|
||||
@ -173,6 +172,7 @@
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = _chan, \
|
||||
.address = _chan, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_AVERAGE_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
|
||||
@ -235,7 +235,7 @@ struct meson_sar_adc_data {
|
||||
struct meson_sar_adc_priv {
|
||||
struct regmap *regmap;
|
||||
struct regulator *vref;
|
||||
const struct meson_sar_adc_data *data;
|
||||
const struct meson_sar_adc_param *param;
|
||||
struct clk *clkin;
|
||||
struct clk *core_clk;
|
||||
struct clk *adc_sel_clk;
|
||||
@ -280,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->param->resolution) - 1);
|
||||
return clamp(tmp, 0, (1 << priv->param->resolution) - 1);
|
||||
}
|
||||
|
||||
static int meson_sar_adc_wait_busy_clear(struct iio_dev *indio_dev)
|
||||
@ -324,15 +324,15 @@ static int meson_sar_adc_read_raw_sample(struct iio_dev *indio_dev,
|
||||
|
||||
regmap_read(priv->regmap, MESON_SAR_ADC_FIFO_RD, ®val);
|
||||
fifo_chan = FIELD_GET(MESON_SAR_ADC_FIFO_RD_CHAN_ID_MASK, regval);
|
||||
if (fifo_chan != chan->channel) {
|
||||
if (fifo_chan != chan->address) {
|
||||
dev_err(&indio_dev->dev,
|
||||
"ADC FIFO entry belongs to channel %d instead of %d\n",
|
||||
fifo_chan, chan->channel);
|
||||
"ADC FIFO entry belongs to channel %d instead of %lu\n",
|
||||
fifo_chan, chan->address);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
fifo_val = FIELD_GET(MESON_SAR_ADC_FIFO_RD_SAMPLE_VALUE_MASK, regval);
|
||||
fifo_val &= GENMASK(priv->data->param->resolution - 1, 0);
|
||||
fifo_val &= GENMASK(priv->param->resolution - 1, 0);
|
||||
*val = meson_sar_adc_calib_val(indio_dev, fifo_val);
|
||||
|
||||
return 0;
|
||||
@ -344,16 +344,16 @@ static void meson_sar_adc_set_averaging(struct iio_dev *indio_dev,
|
||||
enum meson_sar_adc_num_samples samples)
|
||||
{
|
||||
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
|
||||
int val, channel = chan->channel;
|
||||
int val, address = chan->address;
|
||||
|
||||
val = samples << MESON_SAR_ADC_AVG_CNTL_NUM_SAMPLES_SHIFT(channel);
|
||||
val = samples << MESON_SAR_ADC_AVG_CNTL_NUM_SAMPLES_SHIFT(address);
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_AVG_CNTL,
|
||||
MESON_SAR_ADC_AVG_CNTL_NUM_SAMPLES_MASK(channel),
|
||||
MESON_SAR_ADC_AVG_CNTL_NUM_SAMPLES_MASK(address),
|
||||
val);
|
||||
|
||||
val = mode << MESON_SAR_ADC_AVG_CNTL_AVG_MODE_SHIFT(channel);
|
||||
val = mode << MESON_SAR_ADC_AVG_CNTL_AVG_MODE_SHIFT(address);
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_AVG_CNTL,
|
||||
MESON_SAR_ADC_AVG_CNTL_AVG_MODE_MASK(channel), val);
|
||||
MESON_SAR_ADC_AVG_CNTL_AVG_MODE_MASK(address), val);
|
||||
}
|
||||
|
||||
static void meson_sar_adc_enable_channel(struct iio_dev *indio_dev,
|
||||
@ -373,23 +373,23 @@ static void meson_sar_adc_enable_channel(struct iio_dev *indio_dev,
|
||||
|
||||
/* map channel index 0 to the channel which we want to read */
|
||||
regval = FIELD_PREP(MESON_SAR_ADC_CHAN_LIST_ENTRY_MASK(0),
|
||||
chan->channel);
|
||||
chan->address);
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_CHAN_LIST,
|
||||
MESON_SAR_ADC_CHAN_LIST_ENTRY_MASK(0), regval);
|
||||
|
||||
regval = FIELD_PREP(MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_MUX_MASK,
|
||||
chan->channel);
|
||||
chan->address);
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_DETECT_IDLE_SW,
|
||||
MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_MUX_MASK,
|
||||
regval);
|
||||
|
||||
regval = FIELD_PREP(MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_MUX_SEL_MASK,
|
||||
chan->channel);
|
||||
chan->address);
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_DETECT_IDLE_SW,
|
||||
MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_MUX_SEL_MASK,
|
||||
regval);
|
||||
|
||||
if (chan->channel == 6)
|
||||
if (chan->address == 6)
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELTA_10,
|
||||
MESON_SAR_ADC_DELTA_10_TEMP_SEL, 0);
|
||||
}
|
||||
@ -451,7 +451,7 @@ static int meson_sar_adc_lock(struct iio_dev *indio_dev)
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
|
||||
if (priv->data->param->has_bl30_integration) {
|
||||
if (priv->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,
|
||||
@ -479,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->param->has_bl30_integration)
|
||||
if (priv->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);
|
||||
@ -527,8 +527,8 @@ static int meson_sar_adc_get_sample(struct iio_dev *indio_dev,
|
||||
|
||||
if (ret) {
|
||||
dev_warn(indio_dev->dev.parent,
|
||||
"failed to read sample for channel %d: %d\n",
|
||||
chan->channel, ret);
|
||||
"failed to read sample for channel %lu: %d\n",
|
||||
chan->address, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -563,7 +563,7 @@ static int meson_sar_adc_iio_info_read_raw(struct iio_dev *indio_dev,
|
||||
}
|
||||
|
||||
*val = ret / 1000;
|
||||
*val2 = priv->data->param->resolution;
|
||||
*val2 = priv->param->resolution;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
@ -636,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->param->has_bl30_integration) {
|
||||
if (priv->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
|
||||
@ -716,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->param->clock_rate);
|
||||
ret = clk_set_rate(priv->adc_clk, priv->param->clock_rate);
|
||||
if (ret) {
|
||||
dev_err(indio_dev->dev.parent,
|
||||
"failed to set adc clock rate\n");
|
||||
@ -729,7 +729,7 @@ 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;
|
||||
const struct meson_sar_adc_param *param = priv->param;
|
||||
u32 enable_mask;
|
||||
|
||||
if (param->bandgap_reg == MESON_SAR_ADC_REG11)
|
||||
@ -849,13 +849,13 @@ 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->param->resolution) / 4;
|
||||
nominal1 = (1 << priv->data->param->resolution) * 3 / 4;
|
||||
nominal0 = (1 << priv->param->resolution) / 4;
|
||||
nominal1 = (1 << priv->param->resolution) * 3 / 4;
|
||||
|
||||
meson_sar_adc_set_chan7_mux(indio_dev, CHAN7_MUX_VDD_DIV4);
|
||||
usleep_range(10, 20);
|
||||
ret = meson_sar_adc_get_sample(indio_dev,
|
||||
&meson_sar_adc_iio_channels[7],
|
||||
&indio_dev->channels[7],
|
||||
MEAN_AVERAGING, EIGHT_SAMPLES, &value0);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
@ -863,7 +863,7 @@ static int meson_sar_adc_calib(struct iio_dev *indio_dev)
|
||||
meson_sar_adc_set_chan7_mux(indio_dev, CHAN7_MUX_VDD_MUL3_DIV4);
|
||||
usleep_range(10, 20);
|
||||
ret = meson_sar_adc_get_sample(indio_dev,
|
||||
&meson_sar_adc_iio_channels[7],
|
||||
&indio_dev->channels[7],
|
||||
MEAN_AVERAGING, EIGHT_SAMPLES, &value1);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
@ -979,11 +979,11 @@ MODULE_DEVICE_TABLE(of, meson_sar_adc_of_match);
|
||||
|
||||
static int meson_sar_adc_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct meson_sar_adc_data *match_data;
|
||||
struct meson_sar_adc_priv *priv;
|
||||
struct iio_dev *indio_dev;
|
||||
struct resource *res;
|
||||
void __iomem *base;
|
||||
const struct of_device_id *match;
|
||||
int irq, ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*priv));
|
||||
@ -995,15 +995,15 @@ static int meson_sar_adc_probe(struct platform_device *pdev)
|
||||
priv = iio_priv(indio_dev);
|
||||
init_completion(&priv->done);
|
||||
|
||||
match = of_match_device(meson_sar_adc_of_match, &pdev->dev);
|
||||
if (!match) {
|
||||
dev_err(&pdev->dev, "failed to match device\n");
|
||||
match_data = of_device_get_match_data(&pdev->dev);
|
||||
if (!match_data) {
|
||||
dev_err(&pdev->dev, "failed to get match data\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
priv->data = match->data;
|
||||
priv->param = match_data->param;
|
||||
|
||||
indio_dev->name = priv->data->name;
|
||||
indio_dev->name = match_data->name;
|
||||
indio_dev->dev.parent = &pdev->dev;
|
||||
indio_dev->dev.of_node = pdev->dev.of_node;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
@ -1027,7 +1027,7 @@ static int meson_sar_adc_probe(struct platform_device *pdev)
|
||||
return ret;
|
||||
|
||||
priv->regmap = devm_regmap_init_mmio(&pdev->dev, base,
|
||||
priv->data->param->regmap_config);
|
||||
priv->param->regmap_config);
|
||||
if (IS_ERR(priv->regmap))
|
||||
return PTR_ERR(priv->regmap);
|
||||
|
||||
|
@ -708,8 +708,8 @@ static int pm8xxx_of_xlate(struct iio_dev *indio_dev,
|
||||
* mux.
|
||||
*/
|
||||
if (iiospec->args_count != 2) {
|
||||
dev_err(&indio_dev->dev, "wrong number of arguments for %s need 2 got %d\n",
|
||||
iiospec->np->name,
|
||||
dev_err(&indio_dev->dev, "wrong number of arguments for %pOFn need 2 got %d\n",
|
||||
iiospec->np,
|
||||
iiospec->args_count);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
793
drivers/iio/adc/qcom-spmi-adc5.c
Normal file
793
drivers/iio/adc/qcom-spmi-adc5.c
Normal file
@ -0,0 +1,793 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2018, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/log2.h>
|
||||
#include <linux/math64.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <dt-bindings/iio/qcom,spmi-vadc.h>
|
||||
#include "qcom-vadc-common.h"
|
||||
|
||||
#define ADC5_USR_REVISION1 0x0
|
||||
#define ADC5_USR_STATUS1 0x8
|
||||
#define ADC5_USR_STATUS1_REQ_STS BIT(1)
|
||||
#define ADC5_USR_STATUS1_EOC BIT(0)
|
||||
#define ADC5_USR_STATUS1_REQ_STS_EOC_MASK 0x3
|
||||
|
||||
#define ADC5_USR_STATUS2 0x9
|
||||
#define ADC5_USR_STATUS2_CONV_SEQ_MASK 0x70
|
||||
#define ADC5_USR_STATUS2_CONV_SEQ_MASK_SHIFT 0x5
|
||||
|
||||
#define ADC5_USR_IBAT_MEAS 0xf
|
||||
#define ADC5_USR_IBAT_MEAS_SUPPORTED BIT(0)
|
||||
|
||||
#define ADC5_USR_DIG_PARAM 0x42
|
||||
#define ADC5_USR_DIG_PARAM_CAL_VAL BIT(6)
|
||||
#define ADC5_USR_DIG_PARAM_CAL_VAL_SHIFT 6
|
||||
#define ADC5_USR_DIG_PARAM_CAL_SEL 0x30
|
||||
#define ADC5_USR_DIG_PARAM_CAL_SEL_SHIFT 4
|
||||
#define ADC5_USR_DIG_PARAM_DEC_RATIO_SEL 0xc
|
||||
#define ADC5_USR_DIG_PARAM_DEC_RATIO_SEL_SHIFT 2
|
||||
|
||||
#define ADC5_USR_FAST_AVG_CTL 0x43
|
||||
#define ADC5_USR_FAST_AVG_CTL_EN BIT(7)
|
||||
#define ADC5_USR_FAST_AVG_CTL_SAMPLES_MASK 0x7
|
||||
|
||||
#define ADC5_USR_CH_SEL_CTL 0x44
|
||||
|
||||
#define ADC5_USR_DELAY_CTL 0x45
|
||||
#define ADC5_USR_HW_SETTLE_DELAY_MASK 0xf
|
||||
|
||||
#define ADC5_USR_EN_CTL1 0x46
|
||||
#define ADC5_USR_EN_CTL1_ADC_EN BIT(7)
|
||||
|
||||
#define ADC5_USR_CONV_REQ 0x47
|
||||
#define ADC5_USR_CONV_REQ_REQ BIT(7)
|
||||
|
||||
#define ADC5_USR_DATA0 0x50
|
||||
|
||||
#define ADC5_USR_DATA1 0x51
|
||||
|
||||
#define ADC5_USR_IBAT_DATA0 0x52
|
||||
|
||||
#define ADC5_USR_IBAT_DATA1 0x53
|
||||
|
||||
/*
|
||||
* Conversion time varies based on the decimation, clock rate, fast average
|
||||
* samples and measurements queued across different VADC peripherals.
|
||||
* Set the timeout to a max of 100ms.
|
||||
*/
|
||||
#define ADC5_CONV_TIME_MIN_US 263
|
||||
#define ADC5_CONV_TIME_MAX_US 264
|
||||
#define ADC5_CONV_TIME_RETRY 400
|
||||
#define ADC5_CONV_TIMEOUT msecs_to_jiffies(100)
|
||||
|
||||
/* Digital version >= 5.3 supports hw_settle_2 */
|
||||
#define ADC5_HW_SETTLE_DIFF_MINOR 3
|
||||
#define ADC5_HW_SETTLE_DIFF_MAJOR 5
|
||||
|
||||
enum adc5_cal_method {
|
||||
ADC5_NO_CAL = 0,
|
||||
ADC5_RATIOMETRIC_CAL,
|
||||
ADC5_ABSOLUTE_CAL
|
||||
};
|
||||
|
||||
enum adc5_cal_val {
|
||||
ADC5_TIMER_CAL = 0,
|
||||
ADC5_NEW_CAL
|
||||
};
|
||||
|
||||
/**
|
||||
* struct adc5_channel_prop - ADC channel property.
|
||||
* @channel: channel number, refer to the channel list.
|
||||
* @cal_method: calibration method.
|
||||
* @cal_val: calibration value
|
||||
* @decimation: sampling rate supported for the channel.
|
||||
* @prescale: channel scaling performed on the input signal.
|
||||
* @hw_settle_time: the time between AMUX being configured and the
|
||||
* start of conversion.
|
||||
* @avg_samples: ability to provide single result from the ADC
|
||||
* that is an average of multiple measurements.
|
||||
* @scale_fn_type: Represents the scaling function to convert voltage
|
||||
* physical units desired by the client for the channel.
|
||||
* @datasheet_name: Channel name used in device tree.
|
||||
*/
|
||||
struct adc5_channel_prop {
|
||||
unsigned int channel;
|
||||
enum adc5_cal_method cal_method;
|
||||
enum adc5_cal_val cal_val;
|
||||
unsigned int decimation;
|
||||
unsigned int prescale;
|
||||
unsigned int hw_settle_time;
|
||||
unsigned int avg_samples;
|
||||
enum vadc_scale_fn_type scale_fn_type;
|
||||
const char *datasheet_name;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct adc5_chip - ADC private structure.
|
||||
* @regmap: SPMI ADC5 peripheral register map field.
|
||||
* @dev: SPMI ADC5 device.
|
||||
* @base: base address for the ADC peripheral.
|
||||
* @nchannels: number of ADC channels.
|
||||
* @chan_props: array of ADC channel properties.
|
||||
* @iio_chans: array of IIO channels specification.
|
||||
* @poll_eoc: use polling instead of interrupt.
|
||||
* @complete: ADC result notification after interrupt is received.
|
||||
* @lock: ADC lock for access to the peripheral.
|
||||
* @data: software configuration data.
|
||||
*/
|
||||
struct adc5_chip {
|
||||
struct regmap *regmap;
|
||||
struct device *dev;
|
||||
u16 base;
|
||||
unsigned int nchannels;
|
||||
struct adc5_channel_prop *chan_props;
|
||||
struct iio_chan_spec *iio_chans;
|
||||
bool poll_eoc;
|
||||
struct completion complete;
|
||||
struct mutex lock;
|
||||
const struct adc5_data *data;
|
||||
};
|
||||
|
||||
static const struct vadc_prescale_ratio adc5_prescale_ratios[] = {
|
||||
{.num = 1, .den = 1},
|
||||
{.num = 1, .den = 3},
|
||||
{.num = 1, .den = 4},
|
||||
{.num = 1, .den = 6},
|
||||
{.num = 1, .den = 20},
|
||||
{.num = 1, .den = 8},
|
||||
{.num = 10, .den = 81},
|
||||
{.num = 1, .den = 10},
|
||||
{.num = 1, .den = 16}
|
||||
};
|
||||
|
||||
static int adc5_read(struct adc5_chip *adc, u16 offset, u8 *data, int len)
|
||||
{
|
||||
return regmap_bulk_read(adc->regmap, adc->base + offset, data, len);
|
||||
}
|
||||
|
||||
static int adc5_write(struct adc5_chip *adc, u16 offset, u8 *data, int len)
|
||||
{
|
||||
return regmap_bulk_write(adc->regmap, adc->base + offset, data, len);
|
||||
}
|
||||
|
||||
static int adc5_prescaling_from_dt(u32 num, u32 den)
|
||||
{
|
||||
unsigned int pre;
|
||||
|
||||
for (pre = 0; pre < ARRAY_SIZE(adc5_prescale_ratios); pre++)
|
||||
if (adc5_prescale_ratios[pre].num == num &&
|
||||
adc5_prescale_ratios[pre].den == den)
|
||||
break;
|
||||
|
||||
if (pre == ARRAY_SIZE(adc5_prescale_ratios))
|
||||
return -EINVAL;
|
||||
|
||||
return pre;
|
||||
}
|
||||
|
||||
static int adc5_hw_settle_time_from_dt(u32 value,
|
||||
const unsigned int *hw_settle)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < VADC_HW_SETTLE_SAMPLES_MAX; i++) {
|
||||
if (value == hw_settle[i])
|
||||
return i;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int adc5_avg_samples_from_dt(u32 value)
|
||||
{
|
||||
if (!is_power_of_2(value) || value > ADC5_AVG_SAMPLES_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
return __ffs(value);
|
||||
}
|
||||
|
||||
static int adc5_decimation_from_dt(u32 value,
|
||||
const unsigned int *decimation)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ADC5_DECIMATION_SAMPLES_MAX; i++) {
|
||||
if (value == decimation[i])
|
||||
return i;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int adc5_read_voltage_data(struct adc5_chip *adc, u16 *data)
|
||||
{
|
||||
int ret;
|
||||
u8 rslt_lsb, rslt_msb;
|
||||
|
||||
ret = adc5_read(adc, ADC5_USR_DATA0, &rslt_lsb, sizeof(rslt_lsb));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = adc5_read(adc, ADC5_USR_DATA1, &rslt_msb, sizeof(rslt_lsb));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*data = (rslt_msb << 8) | rslt_lsb;
|
||||
|
||||
if (*data == ADC5_USR_DATA_CHECK) {
|
||||
pr_err("Invalid data:0x%x\n", *data);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pr_debug("voltage raw code:0x%x\n", *data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adc5_poll_wait_eoc(struct adc5_chip *adc)
|
||||
{
|
||||
unsigned int count, retry = ADC5_CONV_TIME_RETRY;
|
||||
u8 status1;
|
||||
int ret;
|
||||
|
||||
for (count = 0; count < retry; count++) {
|
||||
ret = adc5_read(adc, ADC5_USR_STATUS1, &status1,
|
||||
sizeof(status1));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
status1 &= ADC5_USR_STATUS1_REQ_STS_EOC_MASK;
|
||||
if (status1 == ADC5_USR_STATUS1_EOC)
|
||||
return 0;
|
||||
|
||||
usleep_range(ADC5_CONV_TIME_MIN_US, ADC5_CONV_TIME_MAX_US);
|
||||
}
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static void adc5_update_dig_param(struct adc5_chip *adc,
|
||||
struct adc5_channel_prop *prop, u8 *data)
|
||||
{
|
||||
/* Update calibration value */
|
||||
*data &= ~ADC5_USR_DIG_PARAM_CAL_VAL;
|
||||
*data |= (prop->cal_val << ADC5_USR_DIG_PARAM_CAL_VAL_SHIFT);
|
||||
|
||||
/* Update calibration select */
|
||||
*data &= ~ADC5_USR_DIG_PARAM_CAL_SEL;
|
||||
*data |= (prop->cal_method << ADC5_USR_DIG_PARAM_CAL_SEL_SHIFT);
|
||||
|
||||
/* Update decimation ratio select */
|
||||
*data &= ~ADC5_USR_DIG_PARAM_DEC_RATIO_SEL;
|
||||
*data |= (prop->decimation << ADC5_USR_DIG_PARAM_DEC_RATIO_SEL_SHIFT);
|
||||
}
|
||||
|
||||
static int adc5_configure(struct adc5_chip *adc,
|
||||
struct adc5_channel_prop *prop)
|
||||
{
|
||||
int ret;
|
||||
u8 buf[6];
|
||||
|
||||
/* Read registers 0x42 through 0x46 */
|
||||
ret = adc5_read(adc, ADC5_USR_DIG_PARAM, buf, sizeof(buf));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Digital param selection */
|
||||
adc5_update_dig_param(adc, prop, &buf[0]);
|
||||
|
||||
/* Update fast average sample value */
|
||||
buf[1] &= (u8) ~ADC5_USR_FAST_AVG_CTL_SAMPLES_MASK;
|
||||
buf[1] |= prop->avg_samples;
|
||||
|
||||
/* Select ADC channel */
|
||||
buf[2] = prop->channel;
|
||||
|
||||
/* Select HW settle delay for channel */
|
||||
buf[3] &= (u8) ~ADC5_USR_HW_SETTLE_DELAY_MASK;
|
||||
buf[3] |= prop->hw_settle_time;
|
||||
|
||||
/* Select ADC enable */
|
||||
buf[4] |= ADC5_USR_EN_CTL1_ADC_EN;
|
||||
|
||||
/* Select CONV request */
|
||||
buf[5] |= ADC5_USR_CONV_REQ_REQ;
|
||||
|
||||
if (!adc->poll_eoc)
|
||||
reinit_completion(&adc->complete);
|
||||
|
||||
return adc5_write(adc, ADC5_USR_DIG_PARAM, buf, sizeof(buf));
|
||||
}
|
||||
|
||||
static int adc5_do_conversion(struct adc5_chip *adc,
|
||||
struct adc5_channel_prop *prop,
|
||||
struct iio_chan_spec const *chan,
|
||||
u16 *data_volt, u16 *data_cur)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&adc->lock);
|
||||
|
||||
ret = adc5_configure(adc, prop);
|
||||
if (ret) {
|
||||
pr_err("ADC configure failed with %d\n", ret);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
if (adc->poll_eoc) {
|
||||
ret = adc5_poll_wait_eoc(adc);
|
||||
if (ret < 0) {
|
||||
pr_err("EOC bit not set\n");
|
||||
goto unlock;
|
||||
}
|
||||
} else {
|
||||
ret = wait_for_completion_timeout(&adc->complete,
|
||||
ADC5_CONV_TIMEOUT);
|
||||
if (!ret) {
|
||||
pr_debug("Did not get completion timeout.\n");
|
||||
ret = adc5_poll_wait_eoc(adc);
|
||||
if (ret < 0) {
|
||||
pr_err("EOC bit not set\n");
|
||||
goto unlock;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ret = adc5_read_voltage_data(adc, data_volt);
|
||||
unlock:
|
||||
mutex_unlock(&adc->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static irqreturn_t adc5_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct adc5_chip *adc = dev_id;
|
||||
|
||||
complete(&adc->complete);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int adc5_of_xlate(struct iio_dev *indio_dev,
|
||||
const struct of_phandle_args *iiospec)
|
||||
{
|
||||
struct adc5_chip *adc = iio_priv(indio_dev);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < adc->nchannels; i++)
|
||||
if (adc->chan_props[i].channel == iiospec->args[0])
|
||||
return i;
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int adc5_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val, int *val2,
|
||||
long mask)
|
||||
{
|
||||
struct adc5_chip *adc = iio_priv(indio_dev);
|
||||
struct adc5_channel_prop *prop;
|
||||
u16 adc_code_volt, adc_code_cur;
|
||||
int ret;
|
||||
|
||||
prop = &adc->chan_props[chan->address];
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_PROCESSED:
|
||||
ret = adc5_do_conversion(adc, prop, chan,
|
||||
&adc_code_volt, &adc_code_cur);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = qcom_adc5_hw_scale(prop->scale_fn_type,
|
||||
&adc5_prescale_ratios[prop->prescale],
|
||||
adc->data,
|
||||
adc_code_volt, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct iio_info adc5_info = {
|
||||
.read_raw = adc5_read_raw,
|
||||
.of_xlate = adc5_of_xlate,
|
||||
};
|
||||
|
||||
struct adc5_channels {
|
||||
const char *datasheet_name;
|
||||
unsigned int prescale_index;
|
||||
enum iio_chan_type type;
|
||||
long info_mask;
|
||||
enum vadc_scale_fn_type scale_fn_type;
|
||||
};
|
||||
|
||||
#define ADC5_CHAN(_dname, _type, _mask, _pre, _scale) \
|
||||
{ \
|
||||
.datasheet_name = _dname, \
|
||||
.prescale_index = _pre, \
|
||||
.type = _type, \
|
||||
.info_mask = _mask, \
|
||||
.scale_fn_type = _scale, \
|
||||
}, \
|
||||
|
||||
#define ADC5_CHAN_TEMP(_dname, _pre, _scale) \
|
||||
ADC5_CHAN(_dname, IIO_TEMP, \
|
||||
BIT(IIO_CHAN_INFO_PROCESSED), \
|
||||
_pre, _scale) \
|
||||
|
||||
#define ADC5_CHAN_VOLT(_dname, _pre, _scale) \
|
||||
ADC5_CHAN(_dname, IIO_VOLTAGE, \
|
||||
BIT(IIO_CHAN_INFO_PROCESSED), \
|
||||
_pre, _scale) \
|
||||
|
||||
static const struct adc5_channels adc5_chans_pmic[ADC5_MAX_CHANNEL] = {
|
||||
[ADC5_REF_GND] = ADC5_CHAN_VOLT("ref_gnd", 1,
|
||||
SCALE_HW_CALIB_DEFAULT)
|
||||
[ADC5_1P25VREF] = ADC5_CHAN_VOLT("vref_1p25", 1,
|
||||
SCALE_HW_CALIB_DEFAULT)
|
||||
[ADC5_VPH_PWR] = ADC5_CHAN_VOLT("vph_pwr", 3,
|
||||
SCALE_HW_CALIB_DEFAULT)
|
||||
[ADC5_VBAT_SNS] = ADC5_CHAN_VOLT("vbat_sns", 3,
|
||||
SCALE_HW_CALIB_DEFAULT)
|
||||
[ADC5_DIE_TEMP] = ADC5_CHAN_TEMP("die_temp", 1,
|
||||
SCALE_HW_CALIB_PMIC_THERM)
|
||||
[ADC5_USB_IN_I] = ADC5_CHAN_VOLT("usb_in_i_uv", 1,
|
||||
SCALE_HW_CALIB_DEFAULT)
|
||||
[ADC5_USB_IN_V_16] = ADC5_CHAN_VOLT("usb_in_v_div_16", 16,
|
||||
SCALE_HW_CALIB_DEFAULT)
|
||||
[ADC5_CHG_TEMP] = ADC5_CHAN_TEMP("chg_temp", 1,
|
||||
SCALE_HW_CALIB_PM5_CHG_TEMP)
|
||||
/* Charger prescales SBUx and MID_CHG to fit within 1.8V upper unit */
|
||||
[ADC5_SBUx] = ADC5_CHAN_VOLT("chg_sbux", 3,
|
||||
SCALE_HW_CALIB_DEFAULT)
|
||||
[ADC5_MID_CHG_DIV6] = ADC5_CHAN_VOLT("chg_mid_chg", 6,
|
||||
SCALE_HW_CALIB_DEFAULT)
|
||||
[ADC5_XO_THERM_100K_PU] = ADC5_CHAN_TEMP("xo_therm", 1,
|
||||
SCALE_HW_CALIB_XOTHERM)
|
||||
[ADC5_AMUX_THM1_100K_PU] = ADC5_CHAN_TEMP("amux_thm1_100k_pu", 1,
|
||||
SCALE_HW_CALIB_THERM_100K_PULLUP)
|
||||
[ADC5_AMUX_THM2_100K_PU] = ADC5_CHAN_TEMP("amux_thm2_100k_pu", 1,
|
||||
SCALE_HW_CALIB_THERM_100K_PULLUP)
|
||||
[ADC5_AMUX_THM3_100K_PU] = ADC5_CHAN_TEMP("amux_thm3_100k_pu", 1,
|
||||
SCALE_HW_CALIB_THERM_100K_PULLUP)
|
||||
[ADC5_AMUX_THM2] = ADC5_CHAN_TEMP("amux_thm2", 1,
|
||||
SCALE_HW_CALIB_PM5_SMB_TEMP)
|
||||
};
|
||||
|
||||
static const struct adc5_channels adc5_chans_rev2[ADC5_MAX_CHANNEL] = {
|
||||
[ADC5_REF_GND] = ADC5_CHAN_VOLT("ref_gnd", 1,
|
||||
SCALE_HW_CALIB_DEFAULT)
|
||||
[ADC5_1P25VREF] = ADC5_CHAN_VOLT("vref_1p25", 1,
|
||||
SCALE_HW_CALIB_DEFAULT)
|
||||
[ADC5_VPH_PWR] = ADC5_CHAN_VOLT("vph_pwr", 3,
|
||||
SCALE_HW_CALIB_DEFAULT)
|
||||
[ADC5_VBAT_SNS] = ADC5_CHAN_VOLT("vbat_sns", 3,
|
||||
SCALE_HW_CALIB_DEFAULT)
|
||||
[ADC5_VCOIN] = ADC5_CHAN_VOLT("vcoin", 3,
|
||||
SCALE_HW_CALIB_DEFAULT)
|
||||
[ADC5_DIE_TEMP] = ADC5_CHAN_TEMP("die_temp", 1,
|
||||
SCALE_HW_CALIB_PMIC_THERM)
|
||||
[ADC5_AMUX_THM1_100K_PU] = ADC5_CHAN_TEMP("amux_thm1_100k_pu", 1,
|
||||
SCALE_HW_CALIB_THERM_100K_PULLUP)
|
||||
[ADC5_AMUX_THM2_100K_PU] = ADC5_CHAN_TEMP("amux_thm2_100k_pu", 1,
|
||||
SCALE_HW_CALIB_THERM_100K_PULLUP)
|
||||
[ADC5_AMUX_THM3_100K_PU] = ADC5_CHAN_TEMP("amux_thm3_100k_pu", 1,
|
||||
SCALE_HW_CALIB_THERM_100K_PULLUP)
|
||||
[ADC5_AMUX_THM4_100K_PU] = ADC5_CHAN_TEMP("amux_thm4_100k_pu", 1,
|
||||
SCALE_HW_CALIB_THERM_100K_PULLUP)
|
||||
[ADC5_AMUX_THM5_100K_PU] = ADC5_CHAN_TEMP("amux_thm5_100k_pu", 1,
|
||||
SCALE_HW_CALIB_THERM_100K_PULLUP)
|
||||
[ADC5_XO_THERM_100K_PU] = ADC5_CHAN_TEMP("xo_therm_100k_pu", 1,
|
||||
SCALE_HW_CALIB_THERM_100K_PULLUP)
|
||||
};
|
||||
|
||||
static int adc5_get_dt_channel_data(struct adc5_chip *adc,
|
||||
struct adc5_channel_prop *prop,
|
||||
struct device_node *node,
|
||||
const struct adc5_data *data)
|
||||
{
|
||||
const char *name = node->name, *channel_name;
|
||||
u32 chan, value, varr[2];
|
||||
int ret;
|
||||
struct device *dev = adc->dev;
|
||||
|
||||
ret = of_property_read_u32(node, "reg", &chan);
|
||||
if (ret) {
|
||||
dev_err(dev, "invalid channel number %s\n", name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (chan > ADC5_PARALLEL_ISENSE_VBAT_IDATA ||
|
||||
!data->adc_chans[chan].datasheet_name) {
|
||||
dev_err(dev, "%s invalid channel number %d\n", name, chan);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* the channel has DT description */
|
||||
prop->channel = chan;
|
||||
|
||||
channel_name = of_get_property(node,
|
||||
"label", NULL) ? : node->name;
|
||||
if (!channel_name) {
|
||||
pr_err("Invalid channel name\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
prop->datasheet_name = channel_name;
|
||||
|
||||
ret = of_property_read_u32(node, "qcom,decimation", &value);
|
||||
if (!ret) {
|
||||
ret = adc5_decimation_from_dt(value, data->decimation);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "%02x invalid decimation %d\n",
|
||||
chan, value);
|
||||
return ret;
|
||||
}
|
||||
prop->decimation = ret;
|
||||
} else {
|
||||
prop->decimation = ADC5_DECIMATION_DEFAULT;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32_array(node, "qcom,pre-scaling", varr, 2);
|
||||
if (!ret) {
|
||||
ret = adc5_prescaling_from_dt(varr[0], varr[1]);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "%02x invalid pre-scaling <%d %d>\n",
|
||||
chan, varr[0], varr[1]);
|
||||
return ret;
|
||||
}
|
||||
prop->prescale = ret;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(node, "qcom,hw-settle-time", &value);
|
||||
if (!ret) {
|
||||
u8 dig_version[2];
|
||||
|
||||
ret = adc5_read(adc, ADC5_USR_REVISION1, dig_version,
|
||||
sizeof(dig_version));
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Invalid dig version read %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
pr_debug("dig_ver:minor:%d, major:%d\n", dig_version[0],
|
||||
dig_version[1]);
|
||||
/* Digital controller >= 5.3 have hw_settle_2 option */
|
||||
if (dig_version[0] >= ADC5_HW_SETTLE_DIFF_MINOR &&
|
||||
dig_version[1] >= ADC5_HW_SETTLE_DIFF_MAJOR)
|
||||
ret = adc5_hw_settle_time_from_dt(value,
|
||||
data->hw_settle_2);
|
||||
else
|
||||
ret = adc5_hw_settle_time_from_dt(value,
|
||||
data->hw_settle_1);
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "%02x invalid hw-settle-time %d us\n",
|
||||
chan, value);
|
||||
return ret;
|
||||
}
|
||||
prop->hw_settle_time = ret;
|
||||
} else {
|
||||
prop->hw_settle_time = VADC_DEF_HW_SETTLE_TIME;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(node, "qcom,avg-samples", &value);
|
||||
if (!ret) {
|
||||
ret = adc5_avg_samples_from_dt(value);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "%02x invalid avg-samples %d\n",
|
||||
chan, value);
|
||||
return ret;
|
||||
}
|
||||
prop->avg_samples = ret;
|
||||
} else {
|
||||
prop->avg_samples = VADC_DEF_AVG_SAMPLES;
|
||||
}
|
||||
|
||||
if (of_property_read_bool(node, "qcom,ratiometric"))
|
||||
prop->cal_method = ADC5_RATIOMETRIC_CAL;
|
||||
else
|
||||
prop->cal_method = ADC5_ABSOLUTE_CAL;
|
||||
|
||||
/*
|
||||
* Default to using timer calibration. Using a fresh calibration value
|
||||
* for every conversion will increase the overall time for a request.
|
||||
*/
|
||||
prop->cal_val = ADC5_TIMER_CAL;
|
||||
|
||||
dev_dbg(dev, "%02x name %s\n", chan, name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct adc5_data adc5_data_pmic = {
|
||||
.full_scale_code_volt = 0x70e4,
|
||||
.full_scale_code_cur = 0x2710,
|
||||
.adc_chans = adc5_chans_pmic,
|
||||
.decimation = (unsigned int [ADC5_DECIMATION_SAMPLES_MAX])
|
||||
{250, 420, 840},
|
||||
.hw_settle_1 = (unsigned int [VADC_HW_SETTLE_SAMPLES_MAX])
|
||||
{15, 100, 200, 300, 400, 500, 600, 700,
|
||||
800, 900, 1, 2, 4, 6, 8, 10},
|
||||
.hw_settle_2 = (unsigned int [VADC_HW_SETTLE_SAMPLES_MAX])
|
||||
{15, 100, 200, 300, 400, 500, 600, 700,
|
||||
1, 2, 4, 8, 16, 32, 64, 128},
|
||||
};
|
||||
|
||||
static const struct adc5_data adc5_data_pmic_rev2 = {
|
||||
.full_scale_code_volt = 0x4000,
|
||||
.full_scale_code_cur = 0x1800,
|
||||
.adc_chans = adc5_chans_rev2,
|
||||
.decimation = (unsigned int [ADC5_DECIMATION_SAMPLES_MAX])
|
||||
{256, 512, 1024},
|
||||
.hw_settle_1 = (unsigned int [VADC_HW_SETTLE_SAMPLES_MAX])
|
||||
{0, 100, 200, 300, 400, 500, 600, 700,
|
||||
800, 900, 1, 2, 4, 6, 8, 10},
|
||||
.hw_settle_2 = (unsigned int [VADC_HW_SETTLE_SAMPLES_MAX])
|
||||
{15, 100, 200, 300, 400, 500, 600, 700,
|
||||
1, 2, 4, 8, 16, 32, 64, 128},
|
||||
};
|
||||
|
||||
static const struct of_device_id adc5_match_table[] = {
|
||||
{
|
||||
.compatible = "qcom,spmi-adc5",
|
||||
.data = &adc5_data_pmic,
|
||||
},
|
||||
{
|
||||
.compatible = "qcom,spmi-adc-rev2",
|
||||
.data = &adc5_data_pmic_rev2,
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
||||
static int adc5_get_dt_data(struct adc5_chip *adc, struct device_node *node)
|
||||
{
|
||||
const struct adc5_channels *adc_chan;
|
||||
struct iio_chan_spec *iio_chan;
|
||||
struct adc5_channel_prop prop, *chan_props;
|
||||
struct device_node *child;
|
||||
unsigned int index = 0;
|
||||
const struct of_device_id *id;
|
||||
const struct adc5_data *data;
|
||||
int ret;
|
||||
|
||||
adc->nchannels = of_get_available_child_count(node);
|
||||
if (!adc->nchannels)
|
||||
return -EINVAL;
|
||||
|
||||
adc->iio_chans = devm_kcalloc(adc->dev, adc->nchannels,
|
||||
sizeof(*adc->iio_chans), GFP_KERNEL);
|
||||
if (!adc->iio_chans)
|
||||
return -ENOMEM;
|
||||
|
||||
adc->chan_props = devm_kcalloc(adc->dev, adc->nchannels,
|
||||
sizeof(*adc->chan_props), GFP_KERNEL);
|
||||
if (!adc->chan_props)
|
||||
return -ENOMEM;
|
||||
|
||||
chan_props = adc->chan_props;
|
||||
iio_chan = adc->iio_chans;
|
||||
id = of_match_node(adc5_match_table, node);
|
||||
if (id)
|
||||
data = id->data;
|
||||
else
|
||||
data = &adc5_data_pmic;
|
||||
adc->data = data;
|
||||
|
||||
for_each_available_child_of_node(node, child) {
|
||||
ret = adc5_get_dt_channel_data(adc, &prop, child, data);
|
||||
if (ret) {
|
||||
of_node_put(child);
|
||||
return ret;
|
||||
}
|
||||
|
||||
prop.scale_fn_type =
|
||||
data->adc_chans[prop.channel].scale_fn_type;
|
||||
*chan_props = prop;
|
||||
adc_chan = &data->adc_chans[prop.channel];
|
||||
|
||||
iio_chan->channel = prop.channel;
|
||||
iio_chan->datasheet_name = prop.datasheet_name;
|
||||
iio_chan->extend_name = prop.datasheet_name;
|
||||
iio_chan->info_mask_separate = adc_chan->info_mask;
|
||||
iio_chan->type = adc_chan->type;
|
||||
iio_chan->address = index;
|
||||
iio_chan++;
|
||||
chan_props++;
|
||||
index++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adc5_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct iio_dev *indio_dev;
|
||||
struct adc5_chip *adc;
|
||||
struct regmap *regmap;
|
||||
int ret, irq_eoc;
|
||||
u32 reg;
|
||||
|
||||
regmap = dev_get_regmap(dev->parent, NULL);
|
||||
if (!regmap)
|
||||
return -ENODEV;
|
||||
|
||||
ret = of_property_read_u32(node, "reg", ®);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(dev, sizeof(*adc));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
adc = iio_priv(indio_dev);
|
||||
adc->regmap = regmap;
|
||||
adc->dev = dev;
|
||||
adc->base = reg;
|
||||
init_completion(&adc->complete);
|
||||
mutex_init(&adc->lock);
|
||||
|
||||
ret = adc5_get_dt_data(adc, node);
|
||||
if (ret) {
|
||||
pr_err("adc get dt data failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
irq_eoc = platform_get_irq(pdev, 0);
|
||||
if (irq_eoc < 0) {
|
||||
if (irq_eoc == -EPROBE_DEFER || irq_eoc == -EINVAL)
|
||||
return irq_eoc;
|
||||
adc->poll_eoc = true;
|
||||
} else {
|
||||
ret = devm_request_irq(dev, irq_eoc, adc5_isr, 0,
|
||||
"pm-adc5", adc);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
indio_dev->dev.parent = dev;
|
||||
indio_dev->dev.of_node = node;
|
||||
indio_dev->name = pdev->name;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &adc5_info;
|
||||
indio_dev->channels = adc->iio_chans;
|
||||
indio_dev->num_channels = adc->nchannels;
|
||||
|
||||
return devm_iio_device_register(dev, indio_dev);
|
||||
}
|
||||
|
||||
static struct platform_driver adc5_driver = {
|
||||
.driver = {
|
||||
.name = "qcom-spmi-adc5.c",
|
||||
.of_match_table = adc5_match_table,
|
||||
},
|
||||
.probe = adc5_probe,
|
||||
};
|
||||
module_platform_driver(adc5_driver);
|
||||
|
||||
MODULE_ALIAS("platform:qcom-spmi-adc5");
|
||||
MODULE_DESCRIPTION("Qualcomm Technologies Inc. PMIC5 ADC driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -47,8 +47,79 @@ static const struct vadc_map_pt adcmap_100k_104ef_104fb[] = {
|
||||
{44, 125}
|
||||
};
|
||||
|
||||
/*
|
||||
* Voltage to temperature table for 100k pull up for NTCG104EF104 with
|
||||
* 1.875V reference.
|
||||
*/
|
||||
static const struct vadc_map_pt adcmap_100k_104ef_104fb_1875_vref[] = {
|
||||
{ 1831, -40000 },
|
||||
{ 1814, -35000 },
|
||||
{ 1791, -30000 },
|
||||
{ 1761, -25000 },
|
||||
{ 1723, -20000 },
|
||||
{ 1675, -15000 },
|
||||
{ 1616, -10000 },
|
||||
{ 1545, -5000 },
|
||||
{ 1463, 0 },
|
||||
{ 1370, 5000 },
|
||||
{ 1268, 10000 },
|
||||
{ 1160, 15000 },
|
||||
{ 1049, 20000 },
|
||||
{ 937, 25000 },
|
||||
{ 828, 30000 },
|
||||
{ 726, 35000 },
|
||||
{ 630, 40000 },
|
||||
{ 544, 45000 },
|
||||
{ 467, 50000 },
|
||||
{ 399, 55000 },
|
||||
{ 340, 60000 },
|
||||
{ 290, 65000 },
|
||||
{ 247, 70000 },
|
||||
{ 209, 75000 },
|
||||
{ 179, 80000 },
|
||||
{ 153, 85000 },
|
||||
{ 130, 90000 },
|
||||
{ 112, 95000 },
|
||||
{ 96, 100000 },
|
||||
{ 82, 105000 },
|
||||
{ 71, 110000 },
|
||||
{ 62, 115000 },
|
||||
{ 53, 120000 },
|
||||
{ 46, 125000 },
|
||||
};
|
||||
|
||||
static int qcom_vadc_scale_hw_calib_volt(
|
||||
const struct vadc_prescale_ratio *prescale,
|
||||
const struct adc5_data *data,
|
||||
u16 adc_code, int *result_uv);
|
||||
static int qcom_vadc_scale_hw_calib_therm(
|
||||
const struct vadc_prescale_ratio *prescale,
|
||||
const struct adc5_data *data,
|
||||
u16 adc_code, int *result_mdec);
|
||||
static int qcom_vadc_scale_hw_smb_temp(
|
||||
const struct vadc_prescale_ratio *prescale,
|
||||
const struct adc5_data *data,
|
||||
u16 adc_code, int *result_mdec);
|
||||
static int qcom_vadc_scale_hw_chg5_temp(
|
||||
const struct vadc_prescale_ratio *prescale,
|
||||
const struct adc5_data *data,
|
||||
u16 adc_code, int *result_mdec);
|
||||
static int qcom_vadc_scale_hw_calib_die_temp(
|
||||
const struct vadc_prescale_ratio *prescale,
|
||||
const struct adc5_data *data,
|
||||
u16 adc_code, int *result_mdec);
|
||||
|
||||
static struct qcom_adc5_scale_type scale_adc5_fn[] = {
|
||||
[SCALE_HW_CALIB_DEFAULT] = {qcom_vadc_scale_hw_calib_volt},
|
||||
[SCALE_HW_CALIB_THERM_100K_PULLUP] = {qcom_vadc_scale_hw_calib_therm},
|
||||
[SCALE_HW_CALIB_XOTHERM] = {qcom_vadc_scale_hw_calib_therm},
|
||||
[SCALE_HW_CALIB_PMIC_THERM] = {qcom_vadc_scale_hw_calib_die_temp},
|
||||
[SCALE_HW_CALIB_PM5_CHG_TEMP] = {qcom_vadc_scale_hw_chg5_temp},
|
||||
[SCALE_HW_CALIB_PM5_SMB_TEMP] = {qcom_vadc_scale_hw_smb_temp},
|
||||
};
|
||||
|
||||
static int qcom_vadc_map_voltage_temp(const struct vadc_map_pt *pts,
|
||||
u32 tablesize, s32 input, s64 *output)
|
||||
u32 tablesize, s32 input, int *output)
|
||||
{
|
||||
bool descending = 1;
|
||||
u32 i = 0;
|
||||
@ -128,7 +199,7 @@ static int qcom_vadc_scale_therm(const struct vadc_linear_graph *calib_graph,
|
||||
bool absolute, u16 adc_code,
|
||||
int *result_mdec)
|
||||
{
|
||||
s64 voltage = 0, result = 0;
|
||||
s64 voltage = 0;
|
||||
int ret;
|
||||
|
||||
qcom_vadc_scale_calib(calib_graph, adc_code, absolute, &voltage);
|
||||
@ -138,12 +209,11 @@ static int qcom_vadc_scale_therm(const struct vadc_linear_graph *calib_graph,
|
||||
|
||||
ret = qcom_vadc_map_voltage_temp(adcmap_100k_104ef_104fb,
|
||||
ARRAY_SIZE(adcmap_100k_104ef_104fb),
|
||||
voltage, &result);
|
||||
voltage, result_mdec);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
result *= 1000;
|
||||
*result_mdec = result;
|
||||
*result_mdec *= 1000;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -191,6 +261,99 @@ static int qcom_vadc_scale_chg_temp(const struct vadc_linear_graph *calib_graph,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcom_vadc_scale_code_voltage_factor(u16 adc_code,
|
||||
const struct vadc_prescale_ratio *prescale,
|
||||
const struct adc5_data *data,
|
||||
unsigned int factor)
|
||||
{
|
||||
s64 voltage, temp, adc_vdd_ref_mv = 1875;
|
||||
|
||||
/*
|
||||
* The normal data range is between 0V to 1.875V. On cases where
|
||||
* we read low voltage values, the ADC code can go beyond the
|
||||
* range and the scale result is incorrect so we clamp the values
|
||||
* for the cases where the code represents a value below 0V
|
||||
*/
|
||||
if (adc_code > VADC5_MAX_CODE)
|
||||
adc_code = 0;
|
||||
|
||||
/* (ADC code * vref_vadc (1.875V)) / full_scale_code */
|
||||
voltage = (s64) adc_code * adc_vdd_ref_mv * 1000;
|
||||
voltage = div64_s64(voltage, data->full_scale_code_volt);
|
||||
if (voltage > 0) {
|
||||
voltage *= prescale->den;
|
||||
temp = prescale->num * factor;
|
||||
voltage = div64_s64(voltage, temp);
|
||||
} else {
|
||||
voltage = 0;
|
||||
}
|
||||
|
||||
return (int) voltage;
|
||||
}
|
||||
|
||||
static int qcom_vadc_scale_hw_calib_volt(
|
||||
const struct vadc_prescale_ratio *prescale,
|
||||
const struct adc5_data *data,
|
||||
u16 adc_code, int *result_uv)
|
||||
{
|
||||
*result_uv = qcom_vadc_scale_code_voltage_factor(adc_code,
|
||||
prescale, data, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcom_vadc_scale_hw_calib_therm(
|
||||
const struct vadc_prescale_ratio *prescale,
|
||||
const struct adc5_data *data,
|
||||
u16 adc_code, int *result_mdec)
|
||||
{
|
||||
int voltage;
|
||||
|
||||
voltage = qcom_vadc_scale_code_voltage_factor(adc_code,
|
||||
prescale, data, 1000);
|
||||
|
||||
/* Map voltage to temperature from look-up table */
|
||||
return qcom_vadc_map_voltage_temp(adcmap_100k_104ef_104fb_1875_vref,
|
||||
ARRAY_SIZE(adcmap_100k_104ef_104fb_1875_vref),
|
||||
voltage, result_mdec);
|
||||
}
|
||||
|
||||
static int qcom_vadc_scale_hw_calib_die_temp(
|
||||
const struct vadc_prescale_ratio *prescale,
|
||||
const struct adc5_data *data,
|
||||
u16 adc_code, int *result_mdec)
|
||||
{
|
||||
*result_mdec = qcom_vadc_scale_code_voltage_factor(adc_code,
|
||||
prescale, data, 2);
|
||||
*result_mdec -= KELVINMIL_CELSIUSMIL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcom_vadc_scale_hw_smb_temp(
|
||||
const struct vadc_prescale_ratio *prescale,
|
||||
const struct adc5_data *data,
|
||||
u16 adc_code, int *result_mdec)
|
||||
{
|
||||
*result_mdec = qcom_vadc_scale_code_voltage_factor(adc_code * 100,
|
||||
prescale, data, PMIC5_SMB_TEMP_SCALE_FACTOR);
|
||||
*result_mdec = PMIC5_SMB_TEMP_CONSTANT - *result_mdec;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcom_vadc_scale_hw_chg5_temp(
|
||||
const struct vadc_prescale_ratio *prescale,
|
||||
const struct adc5_data *data,
|
||||
u16 adc_code, int *result_mdec)
|
||||
{
|
||||
*result_mdec = qcom_vadc_scale_code_voltage_factor(adc_code,
|
||||
prescale, data, 4);
|
||||
*result_mdec = PMIC5_CHG_TEMP_SCALE_FACTOR - *result_mdec;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int qcom_vadc_scale(enum vadc_scale_fn_type scaletype,
|
||||
const struct vadc_linear_graph *calib_graph,
|
||||
const struct vadc_prescale_ratio *prescale,
|
||||
@ -221,6 +384,22 @@ int qcom_vadc_scale(enum vadc_scale_fn_type scaletype,
|
||||
}
|
||||
EXPORT_SYMBOL(qcom_vadc_scale);
|
||||
|
||||
int qcom_adc5_hw_scale(enum vadc_scale_fn_type scaletype,
|
||||
const struct vadc_prescale_ratio *prescale,
|
||||
const struct adc5_data *data,
|
||||
u16 adc_code, int *result)
|
||||
{
|
||||
if (!(scaletype >= SCALE_HW_CALIB_DEFAULT &&
|
||||
scaletype < SCALE_HW_CALIB_INVALID)) {
|
||||
pr_err("Invalid scale type %d\n", scaletype);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return scale_adc5_fn[scaletype].scale_fn(prescale, data,
|
||||
adc_code, result);
|
||||
}
|
||||
EXPORT_SYMBOL(qcom_adc5_hw_scale);
|
||||
|
||||
int qcom_vadc_decimation_from_dt(u32 value)
|
||||
{
|
||||
if (!is_power_of_2(value) || value < VADC_DECIMATION_MIN ||
|
||||
|
@ -25,15 +25,31 @@
|
||||
|
||||
#define VADC_DECIMATION_MIN 512
|
||||
#define VADC_DECIMATION_MAX 4096
|
||||
#define ADC5_DEF_VBAT_PRESCALING 1 /* 1:3 */
|
||||
#define ADC5_DECIMATION_SHORT 250
|
||||
#define ADC5_DECIMATION_MEDIUM 420
|
||||
#define ADC5_DECIMATION_LONG 840
|
||||
/* Default decimation - 1024 for rev2, 840 for pmic5 */
|
||||
#define ADC5_DECIMATION_DEFAULT 2
|
||||
#define ADC5_DECIMATION_SAMPLES_MAX 3
|
||||
|
||||
#define VADC_HW_SETTLE_DELAY_MAX 10000
|
||||
#define VADC_HW_SETTLE_SAMPLES_MAX 16
|
||||
#define VADC_AVG_SAMPLES_MAX 512
|
||||
#define ADC5_AVG_SAMPLES_MAX 16
|
||||
|
||||
#define KELVINMIL_CELSIUSMIL 273150
|
||||
#define PMIC5_CHG_TEMP_SCALE_FACTOR 377500
|
||||
#define PMIC5_SMB_TEMP_CONSTANT 419400
|
||||
#define PMIC5_SMB_TEMP_SCALE_FACTOR 356
|
||||
|
||||
#define PMI_CHG_SCALE_1 -138890
|
||||
#define PMI_CHG_SCALE_2 391750000000LL
|
||||
|
||||
#define VADC5_MAX_CODE 0x7fff
|
||||
#define ADC5_FULL_SCALE_CODE 0x70e4
|
||||
#define ADC5_USR_DATA_CHECK 0x8000
|
||||
|
||||
/**
|
||||
* struct vadc_map_pt - Map the graph representation for ADC channel
|
||||
* @x: Represent the ADC digitized code.
|
||||
@ -89,6 +105,18 @@ struct vadc_prescale_ratio {
|
||||
* SCALE_PMIC_THERM: Returns result in milli degree's Centigrade.
|
||||
* SCALE_XOTHERM: Returns XO thermistor voltage in millidegC.
|
||||
* SCALE_PMI_CHG_TEMP: Conversion for PMI CHG temp
|
||||
* SCALE_HW_CALIB_DEFAULT: Default scaling to convert raw adc code to
|
||||
* voltage (uV) with hardware applied offset/slope values to adc code.
|
||||
* SCALE_HW_CALIB_THERM_100K_PULLUP: Returns temperature in millidegC using
|
||||
* lookup table. The hardware applies offset/slope to adc code.
|
||||
* SCALE_HW_CALIB_XOTHERM: Returns XO thermistor voltage in millidegC using
|
||||
* 100k pullup. The hardware applies offset/slope to adc code.
|
||||
* SCALE_HW_CALIB_PMIC_THERM: Returns result in milli degree's Centigrade.
|
||||
* The hardware applies offset/slope to adc code.
|
||||
* SCALE_HW_CALIB_PM5_CHG_TEMP: Returns result in millidegrees for PMIC5
|
||||
* charger temperature.
|
||||
* SCALE_HW_CALIB_PM5_SMB_TEMP: Returns result in millidegrees for PMIC5
|
||||
* SMB1390 temperature.
|
||||
*/
|
||||
enum vadc_scale_fn_type {
|
||||
SCALE_DEFAULT = 0,
|
||||
@ -96,6 +124,22 @@ enum vadc_scale_fn_type {
|
||||
SCALE_PMIC_THERM,
|
||||
SCALE_XOTHERM,
|
||||
SCALE_PMI_CHG_TEMP,
|
||||
SCALE_HW_CALIB_DEFAULT,
|
||||
SCALE_HW_CALIB_THERM_100K_PULLUP,
|
||||
SCALE_HW_CALIB_XOTHERM,
|
||||
SCALE_HW_CALIB_PMIC_THERM,
|
||||
SCALE_HW_CALIB_PM5_CHG_TEMP,
|
||||
SCALE_HW_CALIB_PM5_SMB_TEMP,
|
||||
SCALE_HW_CALIB_INVALID,
|
||||
};
|
||||
|
||||
struct adc5_data {
|
||||
const u32 full_scale_code_volt;
|
||||
const u32 full_scale_code_cur;
|
||||
const struct adc5_channels *adc_chans;
|
||||
unsigned int *decimation;
|
||||
unsigned int *hw_settle_1;
|
||||
unsigned int *hw_settle_2;
|
||||
};
|
||||
|
||||
int qcom_vadc_scale(enum vadc_scale_fn_type scaletype,
|
||||
@ -104,6 +148,16 @@ int qcom_vadc_scale(enum vadc_scale_fn_type scaletype,
|
||||
bool absolute,
|
||||
u16 adc_code, int *result_mdec);
|
||||
|
||||
struct qcom_adc5_scale_type {
|
||||
int (*scale_fn)(const struct vadc_prescale_ratio *prescale,
|
||||
const struct adc5_data *data, u16 adc_code, int *result);
|
||||
};
|
||||
|
||||
int qcom_adc5_hw_scale(enum vadc_scale_fn_type scaletype,
|
||||
const struct vadc_prescale_ratio *prescale,
|
||||
const struct adc5_data *data,
|
||||
u16 adc_code, int *result_mdec);
|
||||
|
||||
int qcom_vadc_decimation_from_dt(u32 value);
|
||||
|
||||
#endif /* QCOM_VADC_COMMON_H */
|
||||
|
@ -343,8 +343,8 @@ static int rcar_gyroadc_parse_subdevs(struct iio_dev *indio_dev)
|
||||
for_each_child_of_node(np, child) {
|
||||
of_id = of_match_node(rcar_gyroadc_child_match, child);
|
||||
if (!of_id) {
|
||||
dev_err(dev, "Ignoring unsupported ADC \"%s\".",
|
||||
child->name);
|
||||
dev_err(dev, "Ignoring unsupported ADC \"%pOFn\".",
|
||||
child);
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -381,16 +381,16 @@ static int rcar_gyroadc_parse_subdevs(struct iio_dev *indio_dev)
|
||||
ret = of_property_read_u32(child, "reg", ®);
|
||||
if (ret) {
|
||||
dev_err(dev,
|
||||
"Failed to get child reg property of ADC \"%s\".\n",
|
||||
child->name);
|
||||
"Failed to get child reg property of ADC \"%pOFn\".\n",
|
||||
child);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Channel number is too high. */
|
||||
if (reg >= num_channels) {
|
||||
dev_err(dev,
|
||||
"Only %i channels supported with %s, but reg = <%i>.\n",
|
||||
num_channels, child->name, reg);
|
||||
"Only %i channels supported with %pOFn, but reg = <%i>.\n",
|
||||
num_channels, child, reg);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
@ -5,10 +5,12 @@
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/nvmem-consumer.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
/* PMIC global registers definition */
|
||||
#define SC27XX_MODULE_EN 0xc08
|
||||
@ -87,16 +89,73 @@ struct sc27xx_adc_linear_graph {
|
||||
* should use the small-scale graph, and if more than 1.2v, we should use the
|
||||
* big-scale graph.
|
||||
*/
|
||||
static const struct sc27xx_adc_linear_graph big_scale_graph = {
|
||||
static struct sc27xx_adc_linear_graph big_scale_graph = {
|
||||
4200, 3310,
|
||||
3600, 2832,
|
||||
};
|
||||
|
||||
static const struct sc27xx_adc_linear_graph small_scale_graph = {
|
||||
static struct sc27xx_adc_linear_graph small_scale_graph = {
|
||||
1000, 3413,
|
||||
100, 341,
|
||||
};
|
||||
|
||||
static const struct sc27xx_adc_linear_graph big_scale_graph_calib = {
|
||||
4200, 856,
|
||||
3600, 733,
|
||||
};
|
||||
|
||||
static const struct sc27xx_adc_linear_graph small_scale_graph_calib = {
|
||||
1000, 833,
|
||||
100, 80,
|
||||
};
|
||||
|
||||
static int sc27xx_adc_get_calib_data(u32 calib_data, int calib_adc)
|
||||
{
|
||||
return ((calib_data & 0xff) + calib_adc - 128) * 4;
|
||||
}
|
||||
|
||||
static int sc27xx_adc_scale_calibration(struct sc27xx_adc_data *data,
|
||||
bool big_scale)
|
||||
{
|
||||
const struct sc27xx_adc_linear_graph *calib_graph;
|
||||
struct sc27xx_adc_linear_graph *graph;
|
||||
struct nvmem_cell *cell;
|
||||
const char *cell_name;
|
||||
u32 calib_data = 0;
|
||||
void *buf;
|
||||
size_t len;
|
||||
|
||||
if (big_scale) {
|
||||
calib_graph = &big_scale_graph_calib;
|
||||
graph = &big_scale_graph;
|
||||
cell_name = "big_scale_calib";
|
||||
} else {
|
||||
calib_graph = &small_scale_graph_calib;
|
||||
graph = &small_scale_graph;
|
||||
cell_name = "small_scale_calib";
|
||||
}
|
||||
|
||||
cell = nvmem_cell_get(data->dev, cell_name);
|
||||
if (IS_ERR(cell))
|
||||
return PTR_ERR(cell);
|
||||
|
||||
buf = nvmem_cell_read(cell, &len);
|
||||
nvmem_cell_put(cell);
|
||||
|
||||
if (IS_ERR(buf))
|
||||
return PTR_ERR(buf);
|
||||
|
||||
memcpy(&calib_data, buf, min(len, sizeof(u32)));
|
||||
|
||||
/* Only need to calibrate the adc values in the linear graph. */
|
||||
graph->adc0 = sc27xx_adc_get_calib_data(calib_data, calib_graph->adc0);
|
||||
graph->adc1 = sc27xx_adc_get_calib_data(calib_data >> 8,
|
||||
calib_graph->adc1);
|
||||
|
||||
kfree(buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sc27xx_adc_get_ratio(int channel, int scale)
|
||||
{
|
||||
switch (channel) {
|
||||
@ -209,7 +268,7 @@ static void sc27xx_adc_volt_ratio(struct sc27xx_adc_data *data,
|
||||
*div_denominator = ratio & SC27XX_RATIO_DENOMINATOR_MASK;
|
||||
}
|
||||
|
||||
static int sc27xx_adc_to_volt(const struct sc27xx_adc_linear_graph *graph,
|
||||
static int sc27xx_adc_to_volt(struct sc27xx_adc_linear_graph *graph,
|
||||
int raw_adc)
|
||||
{
|
||||
int tmp;
|
||||
@ -273,6 +332,17 @@ static int sc27xx_adc_read_raw(struct iio_dev *indio_dev,
|
||||
int ret, tmp;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
ret = sc27xx_adc_read(data, chan->channel, scale, &tmp);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*val = tmp;
|
||||
return IIO_VAL_INT;
|
||||
|
||||
case IIO_CHAN_INFO_PROCESSED:
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
ret = sc27xx_adc_read_processed(data, chan->channel, scale,
|
||||
@ -315,48 +385,47 @@ static const struct iio_info sc27xx_info = {
|
||||
.write_raw = &sc27xx_adc_write_raw,
|
||||
};
|
||||
|
||||
#define SC27XX_ADC_CHANNEL(index) { \
|
||||
#define SC27XX_ADC_CHANNEL(index, mask) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.channel = index, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) | \
|
||||
BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.info_mask_separate = mask | BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.datasheet_name = "CH##index", \
|
||||
.indexed = 1, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec sc27xx_channels[] = {
|
||||
SC27XX_ADC_CHANNEL(0),
|
||||
SC27XX_ADC_CHANNEL(1),
|
||||
SC27XX_ADC_CHANNEL(2),
|
||||
SC27XX_ADC_CHANNEL(3),
|
||||
SC27XX_ADC_CHANNEL(4),
|
||||
SC27XX_ADC_CHANNEL(5),
|
||||
SC27XX_ADC_CHANNEL(6),
|
||||
SC27XX_ADC_CHANNEL(7),
|
||||
SC27XX_ADC_CHANNEL(8),
|
||||
SC27XX_ADC_CHANNEL(9),
|
||||
SC27XX_ADC_CHANNEL(10),
|
||||
SC27XX_ADC_CHANNEL(11),
|
||||
SC27XX_ADC_CHANNEL(12),
|
||||
SC27XX_ADC_CHANNEL(13),
|
||||
SC27XX_ADC_CHANNEL(14),
|
||||
SC27XX_ADC_CHANNEL(15),
|
||||
SC27XX_ADC_CHANNEL(16),
|
||||
SC27XX_ADC_CHANNEL(17),
|
||||
SC27XX_ADC_CHANNEL(18),
|
||||
SC27XX_ADC_CHANNEL(19),
|
||||
SC27XX_ADC_CHANNEL(20),
|
||||
SC27XX_ADC_CHANNEL(21),
|
||||
SC27XX_ADC_CHANNEL(22),
|
||||
SC27XX_ADC_CHANNEL(23),
|
||||
SC27XX_ADC_CHANNEL(24),
|
||||
SC27XX_ADC_CHANNEL(25),
|
||||
SC27XX_ADC_CHANNEL(26),
|
||||
SC27XX_ADC_CHANNEL(27),
|
||||
SC27XX_ADC_CHANNEL(28),
|
||||
SC27XX_ADC_CHANNEL(29),
|
||||
SC27XX_ADC_CHANNEL(30),
|
||||
SC27XX_ADC_CHANNEL(31),
|
||||
SC27XX_ADC_CHANNEL(0, BIT(IIO_CHAN_INFO_PROCESSED)),
|
||||
SC27XX_ADC_CHANNEL(1, BIT(IIO_CHAN_INFO_PROCESSED)),
|
||||
SC27XX_ADC_CHANNEL(2, BIT(IIO_CHAN_INFO_PROCESSED)),
|
||||
SC27XX_ADC_CHANNEL(3, BIT(IIO_CHAN_INFO_PROCESSED)),
|
||||
SC27XX_ADC_CHANNEL(4, BIT(IIO_CHAN_INFO_PROCESSED)),
|
||||
SC27XX_ADC_CHANNEL(5, BIT(IIO_CHAN_INFO_PROCESSED)),
|
||||
SC27XX_ADC_CHANNEL(6, BIT(IIO_CHAN_INFO_PROCESSED)),
|
||||
SC27XX_ADC_CHANNEL(7, BIT(IIO_CHAN_INFO_PROCESSED)),
|
||||
SC27XX_ADC_CHANNEL(8, BIT(IIO_CHAN_INFO_PROCESSED)),
|
||||
SC27XX_ADC_CHANNEL(9, BIT(IIO_CHAN_INFO_PROCESSED)),
|
||||
SC27XX_ADC_CHANNEL(10, BIT(IIO_CHAN_INFO_PROCESSED)),
|
||||
SC27XX_ADC_CHANNEL(11, BIT(IIO_CHAN_INFO_PROCESSED)),
|
||||
SC27XX_ADC_CHANNEL(12, BIT(IIO_CHAN_INFO_PROCESSED)),
|
||||
SC27XX_ADC_CHANNEL(13, BIT(IIO_CHAN_INFO_PROCESSED)),
|
||||
SC27XX_ADC_CHANNEL(14, BIT(IIO_CHAN_INFO_PROCESSED)),
|
||||
SC27XX_ADC_CHANNEL(15, BIT(IIO_CHAN_INFO_PROCESSED)),
|
||||
SC27XX_ADC_CHANNEL(16, BIT(IIO_CHAN_INFO_PROCESSED)),
|
||||
SC27XX_ADC_CHANNEL(17, BIT(IIO_CHAN_INFO_PROCESSED)),
|
||||
SC27XX_ADC_CHANNEL(18, BIT(IIO_CHAN_INFO_PROCESSED)),
|
||||
SC27XX_ADC_CHANNEL(19, BIT(IIO_CHAN_INFO_PROCESSED)),
|
||||
SC27XX_ADC_CHANNEL(20, BIT(IIO_CHAN_INFO_RAW)),
|
||||
SC27XX_ADC_CHANNEL(21, BIT(IIO_CHAN_INFO_PROCESSED)),
|
||||
SC27XX_ADC_CHANNEL(22, BIT(IIO_CHAN_INFO_PROCESSED)),
|
||||
SC27XX_ADC_CHANNEL(23, BIT(IIO_CHAN_INFO_PROCESSED)),
|
||||
SC27XX_ADC_CHANNEL(24, BIT(IIO_CHAN_INFO_PROCESSED)),
|
||||
SC27XX_ADC_CHANNEL(25, BIT(IIO_CHAN_INFO_PROCESSED)),
|
||||
SC27XX_ADC_CHANNEL(26, BIT(IIO_CHAN_INFO_PROCESSED)),
|
||||
SC27XX_ADC_CHANNEL(27, BIT(IIO_CHAN_INFO_PROCESSED)),
|
||||
SC27XX_ADC_CHANNEL(28, BIT(IIO_CHAN_INFO_PROCESSED)),
|
||||
SC27XX_ADC_CHANNEL(29, BIT(IIO_CHAN_INFO_PROCESSED)),
|
||||
SC27XX_ADC_CHANNEL(30, BIT(IIO_CHAN_INFO_PROCESSED)),
|
||||
SC27XX_ADC_CHANNEL(31, BIT(IIO_CHAN_INFO_PROCESSED)),
|
||||
};
|
||||
|
||||
static int sc27xx_adc_enable(struct sc27xx_adc_data *data)
|
||||
@ -380,6 +449,15 @@ static int sc27xx_adc_enable(struct sc27xx_adc_data *data)
|
||||
if (ret)
|
||||
goto disable_clk;
|
||||
|
||||
/* ADC channel scales' calibration from nvmem device */
|
||||
ret = sc27xx_adc_scale_calibration(data, true);
|
||||
if (ret)
|
||||
goto disable_clk;
|
||||
|
||||
ret = sc27xx_adc_scale_calibration(data, false);
|
||||
if (ret)
|
||||
goto disable_clk;
|
||||
|
||||
return 0;
|
||||
|
||||
disable_clk:
|
||||
|
@ -51,7 +51,7 @@
|
||||
|
||||
struct ti_ads7950_state {
|
||||
struct spi_device *spi;
|
||||
struct spi_transfer ring_xfer[TI_ADS7950_MAX_CHAN + 2];
|
||||
struct spi_transfer ring_xfer;
|
||||
struct spi_transfer scan_single_xfer[3];
|
||||
struct spi_message ring_msg;
|
||||
struct spi_message scan_single_msg;
|
||||
@ -65,11 +65,11 @@ struct ti_ads7950_state {
|
||||
* DMA (thus cache coherency maintenance) requires the
|
||||
* transfer buffers to live in their own cache lines.
|
||||
*/
|
||||
__be16 rx_buf[TI_ADS7950_MAX_CHAN + TI_ADS7950_TIMESTAMP_SIZE]
|
||||
u16 rx_buf[TI_ADS7950_MAX_CHAN + 2 + TI_ADS7950_TIMESTAMP_SIZE]
|
||||
____cacheline_aligned;
|
||||
__be16 tx_buf[TI_ADS7950_MAX_CHAN];
|
||||
__be16 single_tx;
|
||||
__be16 single_rx;
|
||||
u16 tx_buf[TI_ADS7950_MAX_CHAN + 2];
|
||||
u16 single_tx;
|
||||
u16 single_rx;
|
||||
|
||||
};
|
||||
|
||||
@ -108,7 +108,7 @@ enum ti_ads7950_id {
|
||||
.realbits = bits, \
|
||||
.storagebits = 16, \
|
||||
.shift = 12 - (bits), \
|
||||
.endianness = IIO_BE, \
|
||||
.endianness = IIO_CPU, \
|
||||
}, \
|
||||
}
|
||||
|
||||
@ -249,23 +249,14 @@ static int ti_ads7950_update_scan_mode(struct iio_dev *indio_dev,
|
||||
len = 0;
|
||||
for_each_set_bit(i, active_scan_mask, indio_dev->num_channels) {
|
||||
cmd = TI_ADS7950_CR_WRITE | TI_ADS7950_CR_CHAN(i) | st->settings;
|
||||
st->tx_buf[len++] = cpu_to_be16(cmd);
|
||||
st->tx_buf[len++] = cmd;
|
||||
}
|
||||
|
||||
/* Data for the 1st channel is not returned until the 3rd transfer */
|
||||
len += 2;
|
||||
for (i = 0; i < len; i++) {
|
||||
if ((i + 2) < len)
|
||||
st->ring_xfer[i].tx_buf = &st->tx_buf[i];
|
||||
if (i >= 2)
|
||||
st->ring_xfer[i].rx_buf = &st->rx_buf[i - 2];
|
||||
st->ring_xfer[i].len = 2;
|
||||
st->ring_xfer[i].cs_change = 1;
|
||||
}
|
||||
/* make sure last transfer's cs_change is not set */
|
||||
st->ring_xfer[len - 1].cs_change = 0;
|
||||
st->tx_buf[len++] = 0;
|
||||
st->tx_buf[len++] = 0;
|
||||
|
||||
spi_message_init_with_transfers(&st->ring_msg, st->ring_xfer, len);
|
||||
st->ring_xfer.len = len * 2;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -281,7 +272,7 @@ static irqreturn_t ti_ads7950_trigger_handler(int irq, void *p)
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, st->rx_buf,
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, &st->rx_buf[2],
|
||||
iio_get_time_ns(indio_dev));
|
||||
|
||||
out:
|
||||
@ -298,13 +289,13 @@ static int ti_ads7950_scan_direct(struct iio_dev *indio_dev, unsigned int ch)
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
|
||||
cmd = TI_ADS7950_CR_WRITE | TI_ADS7950_CR_CHAN(ch) | st->settings;
|
||||
st->single_tx = cpu_to_be16(cmd);
|
||||
st->single_tx = cmd;
|
||||
|
||||
ret = spi_sync(st->spi, &st->scan_single_msg);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = be16_to_cpu(st->single_rx);
|
||||
ret = st->single_rx;
|
||||
|
||||
out:
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
@ -378,6 +369,14 @@ static int ti_ads7950_probe(struct spi_device *spi)
|
||||
const struct ti_ads7950_chip_info *info;
|
||||
int ret;
|
||||
|
||||
spi->bits_per_word = 16;
|
||||
spi->mode |= SPI_CS_WORD;
|
||||
ret = spi_setup(spi);
|
||||
if (ret < 0) {
|
||||
dev_err(&spi->dev, "Error in spi setup\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
@ -398,6 +397,16 @@ static int ti_ads7950_probe(struct spi_device *spi)
|
||||
indio_dev->num_channels = info->num_channels;
|
||||
indio_dev->info = &ti_ads7950_info;
|
||||
|
||||
/* build spi ring message */
|
||||
spi_message_init(&st->ring_msg);
|
||||
|
||||
st->ring_xfer.tx_buf = &st->tx_buf[0];
|
||||
st->ring_xfer.rx_buf = &st->rx_buf[0];
|
||||
/* len will be set later */
|
||||
st->ring_xfer.cs_change = true;
|
||||
|
||||
spi_message_add_tail(&st->ring_xfer, &st->ring_msg);
|
||||
|
||||
/*
|
||||
* Setup default message. The sample is read at the end of the first
|
||||
* transfer, then it takes one full cycle to convert the sample and one
|
||||
|
@ -209,6 +209,6 @@ static struct spi_driver ad8366_driver = {
|
||||
|
||||
module_spi_driver(ad8366_driver);
|
||||
|
||||
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
|
||||
MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD8366 VGA");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
@ -4,10 +4,10 @@
|
||||
|
||||
#define BME680_REG_CHIP_I2C_ID 0xD0
|
||||
#define BME680_REG_CHIP_SPI_ID 0x50
|
||||
#define BME680_CHIP_ID_VAL 0x61
|
||||
#define BME680_CHIP_ID_VAL 0x61
|
||||
#define BME680_REG_SOFT_RESET_I2C 0xE0
|
||||
#define BME680_REG_SOFT_RESET_SPI 0x60
|
||||
#define BME680_CMD_SOFTRESET 0xB6
|
||||
#define BME680_CMD_SOFTRESET 0xB6
|
||||
#define BME680_REG_STATUS 0x73
|
||||
#define BME680_SPI_MEM_PAGE_BIT BIT(4)
|
||||
#define BME680_SPI_MEM_PAGE_1_VAL 1
|
||||
@ -18,6 +18,7 @@
|
||||
#define BME680_REG_GAS_MSB 0x2A
|
||||
#define BME680_REG_GAS_R_LSB 0x2B
|
||||
#define BME680_GAS_STAB_BIT BIT(4)
|
||||
#define BME680_GAS_RANGE_MASK GENMASK(3, 0)
|
||||
|
||||
#define BME680_REG_CTRL_HUMIDITY 0x72
|
||||
#define BME680_OSRS_HUMIDITY_MASK GENMASK(2, 0)
|
||||
@ -26,9 +27,8 @@
|
||||
#define BME680_OSRS_TEMP_MASK GENMASK(7, 5)
|
||||
#define BME680_OSRS_PRESS_MASK GENMASK(4, 2)
|
||||
#define BME680_MODE_MASK GENMASK(1, 0)
|
||||
|
||||
#define BME680_MODE_FORCED 1
|
||||
#define BME680_MODE_SLEEP 0
|
||||
#define BME680_MODE_FORCED 1
|
||||
#define BME680_MODE_SLEEP 0
|
||||
|
||||
#define BME680_REG_CONFIG 0x75
|
||||
#define BME680_FILTER_MASK GENMASK(4, 2)
|
||||
@ -39,24 +39,21 @@
|
||||
|
||||
#define BME680_MAX_OVERFLOW_VAL 0x40000000
|
||||
#define BME680_HUM_REG_SHIFT_VAL 4
|
||||
#define BME680_BIT_H1_DATA_MSK 0x0F
|
||||
#define BME680_BIT_H1_DATA_MASK GENMASK(3, 0)
|
||||
|
||||
#define BME680_REG_RES_HEAT_RANGE 0x02
|
||||
#define BME680_RHRANGE_MSK 0x30
|
||||
#define BME680_RHRANGE_MASK GENMASK(5, 4)
|
||||
#define BME680_REG_RES_HEAT_VAL 0x00
|
||||
#define BME680_REG_RANGE_SW_ERR 0x04
|
||||
#define BME680_RSERROR_MSK 0xF0
|
||||
#define BME680_RSERROR_MASK GENMASK(7, 4)
|
||||
#define BME680_REG_RES_HEAT_0 0x5A
|
||||
#define BME680_REG_GAS_WAIT_0 0x64
|
||||
#define BME680_GAS_RANGE_MASK 0x0F
|
||||
#define BME680_ADC_GAS_RES_SHIFT 6
|
||||
#define BME680_AMB_TEMP 25
|
||||
|
||||
#define BME680_REG_CTRL_GAS_1 0x71
|
||||
#define BME680_RUN_GAS_MASK BIT(4)
|
||||
#define BME680_NB_CONV_MASK GENMASK(3, 0)
|
||||
#define BME680_RUN_GAS_EN_BIT BIT(4)
|
||||
#define BME680_NB_CONV_0_VAL 0
|
||||
|
||||
#define BME680_REG_MEAS_STAT_0 0x1D
|
||||
#define BME680_GAS_MEAS_BIT BIT(6)
|
||||
|
@ -91,8 +91,6 @@ static const struct iio_chan_spec bme680_channels[] = {
|
||||
},
|
||||
};
|
||||
|
||||
static const int bme680_oversampling_avail[] = { 1, 2, 4, 8, 16 };
|
||||
|
||||
static int bme680_read_calib(struct bme680_data *data,
|
||||
struct bme680_calib *calib)
|
||||
{
|
||||
@ -102,16 +100,14 @@ static int bme680_read_calib(struct bme680_data *data,
|
||||
__le16 buf;
|
||||
|
||||
/* Temperature related coefficients */
|
||||
ret = regmap_bulk_read(data->regmap, BME680_T1_LSB_REG,
|
||||
(u8 *) &buf, 2);
|
||||
ret = regmap_bulk_read(data->regmap, BME680_T1_LSB_REG, (u8 *) &buf, 2);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read BME680_T1_LSB_REG\n");
|
||||
return ret;
|
||||
}
|
||||
calib->par_t1 = le16_to_cpu(buf);
|
||||
|
||||
ret = regmap_bulk_read(data->regmap, BME680_T2_LSB_REG,
|
||||
(u8 *) &buf, 2);
|
||||
ret = regmap_bulk_read(data->regmap, BME680_T2_LSB_REG, (u8 *) &buf, 2);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read BME680_T2_LSB_REG\n");
|
||||
return ret;
|
||||
@ -126,16 +122,14 @@ static int bme680_read_calib(struct bme680_data *data,
|
||||
calib->par_t3 = tmp;
|
||||
|
||||
/* Pressure related coefficients */
|
||||
ret = regmap_bulk_read(data->regmap, BME680_P1_LSB_REG,
|
||||
(u8 *) &buf, 2);
|
||||
ret = regmap_bulk_read(data->regmap, BME680_P1_LSB_REG, (u8 *) &buf, 2);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read BME680_P1_LSB_REG\n");
|
||||
return ret;
|
||||
}
|
||||
calib->par_p1 = le16_to_cpu(buf);
|
||||
|
||||
ret = regmap_bulk_read(data->regmap, BME680_P2_LSB_REG,
|
||||
(u8 *) &buf, 2);
|
||||
ret = regmap_bulk_read(data->regmap, BME680_P2_LSB_REG, (u8 *) &buf, 2);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read BME680_P2_LSB_REG\n");
|
||||
return ret;
|
||||
@ -149,16 +143,14 @@ static int bme680_read_calib(struct bme680_data *data,
|
||||
}
|
||||
calib->par_p3 = tmp;
|
||||
|
||||
ret = regmap_bulk_read(data->regmap, BME680_P4_LSB_REG,
|
||||
(u8 *) &buf, 2);
|
||||
ret = regmap_bulk_read(data->regmap, BME680_P4_LSB_REG, (u8 *) &buf, 2);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read BME680_P4_LSB_REG\n");
|
||||
return ret;
|
||||
}
|
||||
calib->par_p4 = le16_to_cpu(buf);
|
||||
|
||||
ret = regmap_bulk_read(data->regmap, BME680_P5_LSB_REG,
|
||||
(u8 *) &buf, 2);
|
||||
ret = regmap_bulk_read(data->regmap, BME680_P5_LSB_REG, (u8 *) &buf, 2);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read BME680_P5_LSB_REG\n");
|
||||
return ret;
|
||||
@ -179,16 +171,14 @@ static int bme680_read_calib(struct bme680_data *data,
|
||||
}
|
||||
calib->par_p7 = tmp;
|
||||
|
||||
ret = regmap_bulk_read(data->regmap, BME680_P8_LSB_REG,
|
||||
(u8 *) &buf, 2);
|
||||
ret = regmap_bulk_read(data->regmap, BME680_P8_LSB_REG, (u8 *) &buf, 2);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read BME680_P8_LSB_REG\n");
|
||||
return ret;
|
||||
}
|
||||
calib->par_p8 = le16_to_cpu(buf);
|
||||
|
||||
ret = regmap_bulk_read(data->regmap, BME680_P9_LSB_REG,
|
||||
(u8 *) &buf, 2);
|
||||
ret = regmap_bulk_read(data->regmap, BME680_P9_LSB_REG, (u8 *) &buf, 2);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read BME680_P9_LSB_REG\n");
|
||||
return ret;
|
||||
@ -208,30 +198,26 @@ static int bme680_read_calib(struct bme680_data *data,
|
||||
dev_err(dev, "failed to read BME680_H1_MSB_REG\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regmap_read(data->regmap, BME680_H1_LSB_REG, &tmp_lsb);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read BME680_H1_LSB_REG\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
calib->par_h1 = (tmp_msb << BME680_HUM_REG_SHIFT_VAL) |
|
||||
(tmp_lsb & BME680_BIT_H1_DATA_MSK);
|
||||
(tmp_lsb & BME680_BIT_H1_DATA_MASK);
|
||||
|
||||
ret = regmap_read(data->regmap, BME680_H2_MSB_REG, &tmp_msb);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read BME680_H2_MSB_REG\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regmap_read(data->regmap, BME680_H2_LSB_REG, &tmp_lsb);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read BME680_H2_LSB_REG\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
calib->par_h2 = (tmp_msb << BME680_HUM_REG_SHIFT_VAL) |
|
||||
(tmp_lsb >> BME680_HUM_REG_SHIFT_VAL);
|
||||
(tmp_lsb >> BME680_HUM_REG_SHIFT_VAL);
|
||||
|
||||
ret = regmap_read(data->regmap, BME680_H3_REG, &tmp);
|
||||
if (ret < 0) {
|
||||
@ -276,8 +262,8 @@ static int bme680_read_calib(struct bme680_data *data,
|
||||
}
|
||||
calib->par_gh1 = tmp;
|
||||
|
||||
ret = regmap_bulk_read(data->regmap, BME680_GH2_LSB_REG,
|
||||
(u8 *) &buf, 2);
|
||||
ret = regmap_bulk_read(data->regmap, BME680_GH2_LSB_REG, (u8 *) &buf,
|
||||
2);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read BME680_GH2_LSB_REG\n");
|
||||
return ret;
|
||||
@ -297,7 +283,7 @@ static int bme680_read_calib(struct bme680_data *data,
|
||||
dev_err(dev, "failed to read resistance heat range\n");
|
||||
return ret;
|
||||
}
|
||||
calib->res_heat_range = (tmp & BME680_RHRANGE_MSK) / 16;
|
||||
calib->res_heat_range = FIELD_GET(BME680_RHRANGE_MASK, tmp);
|
||||
|
||||
ret = regmap_read(data->regmap, BME680_REG_RES_HEAT_VAL, &tmp);
|
||||
if (ret < 0) {
|
||||
@ -311,7 +297,7 @@ static int bme680_read_calib(struct bme680_data *data,
|
||||
dev_err(dev, "failed to read range software error\n");
|
||||
return ret;
|
||||
}
|
||||
calib->range_sw_err = (tmp & BME680_RSERROR_MSK) / 16;
|
||||
calib->range_sw_err = FIELD_GET(BME680_RSERROR_MASK, tmp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -408,10 +394,7 @@ static u32 bme680_compensate_humid(struct bme680_data *data,
|
||||
var6 = (var4 * var5) >> 1;
|
||||
calc_hum = (((var3 + var6) >> 10) * 1000) >> 12;
|
||||
|
||||
if (calc_hum > 100000) /* Cap at 100%rH */
|
||||
calc_hum = 100000;
|
||||
else if (calc_hum < 0)
|
||||
calc_hum = 0;
|
||||
calc_hum = clamp(calc_hum, 0, 100000); /* clamp between 0-100 %rH */
|
||||
|
||||
return calc_hum;
|
||||
}
|
||||
@ -518,12 +501,20 @@ static int bme680_set_mode(struct bme680_data *data, bool mode)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static u8 bme680_oversampling_to_reg(u8 val)
|
||||
{
|
||||
return ilog2(val) + 1;
|
||||
}
|
||||
|
||||
static int bme680_chip_config(struct bme680_data *data)
|
||||
{
|
||||
struct device *dev = regmap_get_device(data->regmap);
|
||||
int ret;
|
||||
u8 osrs = FIELD_PREP(BME680_OSRS_HUMIDITY_MASK,
|
||||
data->oversampling_humid + 1);
|
||||
u8 osrs;
|
||||
|
||||
osrs = FIELD_PREP(
|
||||
BME680_OSRS_HUMIDITY_MASK,
|
||||
bme680_oversampling_to_reg(data->oversampling_humid));
|
||||
/*
|
||||
* Highly recommended to set oversampling of humidity before
|
||||
* temperature/pressure oversampling.
|
||||
@ -544,12 +535,12 @@ static int bme680_chip_config(struct bme680_data *data)
|
||||
return ret;
|
||||
}
|
||||
|
||||
osrs = FIELD_PREP(BME680_OSRS_TEMP_MASK, data->oversampling_temp + 1) |
|
||||
FIELD_PREP(BME680_OSRS_PRESS_MASK, data->oversampling_press + 1);
|
||||
|
||||
osrs = FIELD_PREP(BME680_OSRS_TEMP_MASK,
|
||||
bme680_oversampling_to_reg(data->oversampling_temp)) |
|
||||
FIELD_PREP(BME680_OSRS_PRESS_MASK,
|
||||
bme680_oversampling_to_reg(data->oversampling_press));
|
||||
ret = regmap_write_bits(data->regmap, BME680_REG_CTRL_MEAS,
|
||||
BME680_OSRS_TEMP_MASK |
|
||||
BME680_OSRS_PRESS_MASK,
|
||||
BME680_OSRS_TEMP_MASK | BME680_OSRS_PRESS_MASK,
|
||||
osrs);
|
||||
if (ret < 0)
|
||||
dev_err(dev, "failed to write ctrl_meas register\n");
|
||||
@ -577,14 +568,15 @@ static int bme680_gas_config(struct bme680_data *data)
|
||||
/* set target heating duration */
|
||||
ret = regmap_write(data->regmap, BME680_REG_GAS_WAIT_0, heatr_dur);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failted to write gas_wait_0 register\n");
|
||||
dev_err(dev, "failed to write gas_wait_0 register\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Selecting the runGas and NB conversion settings for the sensor */
|
||||
/* Enable the gas sensor and select heater profile set-point 0 */
|
||||
ret = regmap_update_bits(data->regmap, BME680_REG_CTRL_GAS_1,
|
||||
BME680_RUN_GAS_MASK | BME680_NB_CONV_MASK,
|
||||
BME680_RUN_GAS_EN_BIT | BME680_NB_CONV_0_VAL);
|
||||
FIELD_PREP(BME680_RUN_GAS_MASK, 1) |
|
||||
FIELD_PREP(BME680_NB_CONV_MASK, 0));
|
||||
if (ret < 0)
|
||||
dev_err(dev, "failed to write ctrl_gas_1 register\n");
|
||||
|
||||
@ -782,13 +774,13 @@ static int bme680_read_raw(struct iio_dev *indio_dev,
|
||||
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
|
||||
switch (chan->type) {
|
||||
case IIO_TEMP:
|
||||
*val = 1 << data->oversampling_temp;
|
||||
*val = data->oversampling_temp;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_PRESSURE:
|
||||
*val = 1 << data->oversampling_press;
|
||||
*val = data->oversampling_press;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_HUMIDITYRELATIVE:
|
||||
*val = 1 << data->oversampling_humid;
|
||||
*val = data->oversampling_humid;
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
@ -798,52 +790,9 @@ static int bme680_read_raw(struct iio_dev *indio_dev,
|
||||
}
|
||||
}
|
||||
|
||||
static int bme680_write_oversampling_ratio_temp(struct bme680_data *data,
|
||||
int val)
|
||||
static bool bme680_is_valid_oversampling(int rate)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(bme680_oversampling_avail); i++) {
|
||||
if (bme680_oversampling_avail[i] == val) {
|
||||
data->oversampling_temp = ilog2(val);
|
||||
|
||||
return bme680_chip_config(data);
|
||||
}
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int bme680_write_oversampling_ratio_press(struct bme680_data *data,
|
||||
int val)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(bme680_oversampling_avail); i++) {
|
||||
if (bme680_oversampling_avail[i] == val) {
|
||||
data->oversampling_press = ilog2(val);
|
||||
|
||||
return bme680_chip_config(data);
|
||||
}
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int bme680_write_oversampling_ratio_humid(struct bme680_data *data,
|
||||
int val)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(bme680_oversampling_avail); i++) {
|
||||
if (bme680_oversampling_avail[i] == val) {
|
||||
data->oversampling_humid = ilog2(val);
|
||||
|
||||
return bme680_chip_config(data);
|
||||
}
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
return (rate > 0 && rate <= 16 && is_power_of_2(rate));
|
||||
}
|
||||
|
||||
static int bme680_write_raw(struct iio_dev *indio_dev,
|
||||
@ -852,18 +801,31 @@ static int bme680_write_raw(struct iio_dev *indio_dev,
|
||||
{
|
||||
struct bme680_data *data = iio_priv(indio_dev);
|
||||
|
||||
if (val2 != 0)
|
||||
return -EINVAL;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
|
||||
{
|
||||
if (!bme680_is_valid_oversampling(val))
|
||||
return -EINVAL;
|
||||
|
||||
switch (chan->type) {
|
||||
case IIO_TEMP:
|
||||
return bme680_write_oversampling_ratio_temp(data, val);
|
||||
data->oversampling_temp = val;
|
||||
break;
|
||||
case IIO_PRESSURE:
|
||||
return bme680_write_oversampling_ratio_press(data, val);
|
||||
data->oversampling_press = val;
|
||||
break;
|
||||
case IIO_HUMIDITYRELATIVE:
|
||||
return bme680_write_oversampling_ratio_humid(data, val);
|
||||
data->oversampling_humid = val;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return bme680_chip_config(data);
|
||||
}
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -925,9 +887,9 @@ int bme680_core_probe(struct device *dev, struct regmap *regmap,
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
/* default values for the sensor */
|
||||
data->oversampling_humid = ilog2(2); /* 2X oversampling rate */
|
||||
data->oversampling_press = ilog2(4); /* 4X oversampling rate */
|
||||
data->oversampling_temp = ilog2(8); /* 8X oversampling rate */
|
||||
data->oversampling_humid = 2; /* 2X oversampling rate */
|
||||
data->oversampling_press = 4; /* 4X oversampling rate */
|
||||
data->oversampling_temp = 8; /* 8X oversampling rate */
|
||||
data->heater_temp = 320; /* degree Celsius */
|
||||
data->heater_dur = 150; /* milliseconds */
|
||||
|
||||
|
@ -120,6 +120,16 @@ config AD5624R_SPI
|
||||
Say yes here to build support for Analog Devices AD5624R, AD5644R and
|
||||
AD5664R converters (DAC). This driver uses the common SPI interface.
|
||||
|
||||
config LTC1660
|
||||
tristate "Linear Technology LTC1660/LTC1665 DAC SPI driver"
|
||||
depends on SPI
|
||||
help
|
||||
Say yes here to build support for Linear Technology
|
||||
LTC1660 and LTC1665 Digital to Analog Converters.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ltc1660.
|
||||
|
||||
config LTC2632
|
||||
tristate "Linear Technology LTC2632-12/10/8 DAC spi driver"
|
||||
depends on SPI
|
||||
|
@ -29,6 +29,7 @@ obj-$(CONFIG_CIO_DAC) += cio-dac.o
|
||||
obj-$(CONFIG_DPOT_DAC) += dpot-dac.o
|
||||
obj-$(CONFIG_DS4424) += ds4424.o
|
||||
obj-$(CONFIG_LPC18XX_DAC) += lpc18xx_dac.o
|
||||
obj-$(CONFIG_LTC1660) += ltc1660.o
|
||||
obj-$(CONFIG_LTC2632) += ltc2632.o
|
||||
obj-$(CONFIG_M62332) += m62332.o
|
||||
obj-$(CONFIG_MAX517) += max517.o
|
||||
|
@ -808,6 +808,40 @@ static int ad5064_set_config(struct ad5064_state *st, unsigned int val)
|
||||
return ad5064_write(st, cmd, 0, val, 0);
|
||||
}
|
||||
|
||||
static int ad5064_request_vref(struct ad5064_state *st, struct device *dev)
|
||||
{
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
for (i = 0; i < ad5064_num_vref(st); ++i)
|
||||
st->vref_reg[i].supply = ad5064_vref_name(st, i);
|
||||
|
||||
if (!st->chip_info->internal_vref)
|
||||
return devm_regulator_bulk_get(dev, ad5064_num_vref(st),
|
||||
st->vref_reg);
|
||||
|
||||
/*
|
||||
* This assumes that when the regulator has an internal VREF
|
||||
* there is only one external VREF connection, which is
|
||||
* currently the case for all supported devices.
|
||||
*/
|
||||
st->vref_reg[0].consumer = devm_regulator_get_optional(dev, "vref");
|
||||
if (!IS_ERR(st->vref_reg[0].consumer))
|
||||
return 0;
|
||||
|
||||
ret = PTR_ERR(st->vref_reg[0].consumer);
|
||||
if (ret != -ENODEV)
|
||||
return ret;
|
||||
|
||||
/* If no external regulator was supplied use the internal VREF */
|
||||
st->use_internal_vref = true;
|
||||
ret = ad5064_set_config(st, AD5064_CONFIG_INT_VREF_ENABLE);
|
||||
if (ret)
|
||||
dev_err(dev, "Failed to enable internal vref: %d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad5064_probe(struct device *dev, enum ad5064_type type,
|
||||
const char *name, ad5064_write_func write)
|
||||
{
|
||||
@ -828,22 +862,11 @@ static int ad5064_probe(struct device *dev, enum ad5064_type type,
|
||||
st->dev = dev;
|
||||
st->write = write;
|
||||
|
||||
for (i = 0; i < ad5064_num_vref(st); ++i)
|
||||
st->vref_reg[i].supply = ad5064_vref_name(st, i);
|
||||
ret = ad5064_request_vref(st, dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = devm_regulator_bulk_get(dev, ad5064_num_vref(st),
|
||||
st->vref_reg);
|
||||
if (ret) {
|
||||
if (!st->chip_info->internal_vref)
|
||||
return ret;
|
||||
st->use_internal_vref = true;
|
||||
ret = ad5064_set_config(st, AD5064_CONFIG_INT_VREF_ENABLE);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to enable internal vref: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
if (!st->use_internal_vref) {
|
||||
ret = regulator_bulk_enable(ad5064_num_vref(st), st->vref_reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -628,6 +628,6 @@ static void __exit ad5446_exit(void)
|
||||
}
|
||||
module_exit(ad5446_exit);
|
||||
|
||||
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
|
||||
MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD5444/AD5446 DAC");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
@ -369,6 +369,6 @@ static struct spi_driver ad5504_driver = {
|
||||
};
|
||||
module_spi_driver(ad5504_driver);
|
||||
|
||||
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
|
||||
MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD5501/AD5501 DAC");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
@ -470,6 +470,6 @@ int ad5686_remove(struct device *dev)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ad5686_remove);
|
||||
|
||||
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
|
||||
MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD5686/85/84 DAC");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
@ -108,6 +109,7 @@ struct ad5758_range {
|
||||
struct ad5758_state {
|
||||
struct spi_device *spi;
|
||||
struct mutex lock;
|
||||
struct gpio_desc *gpio_reset;
|
||||
struct ad5758_range out_range;
|
||||
unsigned int dc_dc_mode;
|
||||
unsigned int dc_dc_ilim;
|
||||
@ -474,6 +476,21 @@ static int ad5758_internal_buffers_en(struct ad5758_state *st, bool enable)
|
||||
AD5758_CAL_MEM_UNREFRESHED_MSK);
|
||||
}
|
||||
|
||||
static int ad5758_reset(struct ad5758_state *st)
|
||||
{
|
||||
if (st->gpio_reset) {
|
||||
gpiod_set_value(st->gpio_reset, 0);
|
||||
usleep_range(100, 1000);
|
||||
gpiod_set_value(st->gpio_reset, 1);
|
||||
usleep_range(100, 1000);
|
||||
|
||||
return 0;
|
||||
} else {
|
||||
/* Perform a software reset */
|
||||
return ad5758_soft_reset(st);
|
||||
}
|
||||
}
|
||||
|
||||
static int ad5758_reg_access(struct iio_dev *indio_dev,
|
||||
unsigned int reg,
|
||||
unsigned int writeval,
|
||||
@ -768,13 +785,18 @@ static int ad5758_init(struct ad5758_state *st)
|
||||
{
|
||||
int regval, ret;
|
||||
|
||||
st->gpio_reset = devm_gpiod_get_optional(&st->spi->dev, "reset",
|
||||
GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(st->gpio_reset))
|
||||
return PTR_ERR(st->gpio_reset);
|
||||
|
||||
/* Disable CRC checks */
|
||||
ret = ad5758_crc_disable(st);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Perform a software reset */
|
||||
ret = ad5758_soft_reset(st);
|
||||
/* Perform a reset */
|
||||
ret = ad5758_reset(st);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
@ -467,6 +467,6 @@ static struct spi_driver ad5791_driver = {
|
||||
};
|
||||
module_spi_driver(ad5791_driver);
|
||||
|
||||
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
|
||||
MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD5760/AD5780/AD5781/AD5790/AD5791 DAC");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
@ -1,13 +1,10 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* IIO DAC emulation driver using a digital potentiometer
|
||||
*
|
||||
* Copyright (C) 2016 Axentia Technologies AB
|
||||
*
|
||||
* Author: Peter Rosin <peda@axentia.se>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
|
250
drivers/iio/dac/ltc1660.c
Normal file
250
drivers/iio/dac/ltc1660.c
Normal file
@ -0,0 +1,250 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Driver for Linear Technology LTC1665/LTC1660, 8 channels DAC
|
||||
*
|
||||
* Copyright (C) 2018 Marcus Folkesson <marcus.folkesson@gmail.com>
|
||||
*/
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#define LTC1660_REG_WAKE 0x0
|
||||
#define LTC1660_REG_DAC_A 0x1
|
||||
#define LTC1660_REG_DAC_B 0x2
|
||||
#define LTC1660_REG_DAC_C 0x3
|
||||
#define LTC1660_REG_DAC_D 0x4
|
||||
#define LTC1660_REG_DAC_E 0x5
|
||||
#define LTC1660_REG_DAC_F 0x6
|
||||
#define LTC1660_REG_DAC_G 0x7
|
||||
#define LTC1660_REG_DAC_H 0x8
|
||||
#define LTC1660_REG_SLEEP 0xe
|
||||
|
||||
#define LTC1660_NUM_CHANNELS 8
|
||||
|
||||
static const struct regmap_config ltc1660_regmap_config = {
|
||||
.reg_bits = 4,
|
||||
.val_bits = 12,
|
||||
};
|
||||
|
||||
enum ltc1660_supported_device_ids {
|
||||
ID_LTC1660,
|
||||
ID_LTC1665,
|
||||
};
|
||||
|
||||
struct ltc1660_priv {
|
||||
struct spi_device *spi;
|
||||
struct regmap *regmap;
|
||||
struct regulator *vref_reg;
|
||||
unsigned int value[LTC1660_NUM_CHANNELS];
|
||||
unsigned int vref_mv;
|
||||
};
|
||||
|
||||
static int ltc1660_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val,
|
||||
int *val2,
|
||||
long mask)
|
||||
{
|
||||
struct ltc1660_priv *priv = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
*val = priv->value[chan->channel];
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = regulator_get_voltage(priv->vref_reg);
|
||||
if (*val < 0) {
|
||||
dev_err(&priv->spi->dev, "failed to read vref regulator: %d\n",
|
||||
*val);
|
||||
return *val;
|
||||
}
|
||||
|
||||
/* Convert to mV */
|
||||
*val /= 1000;
|
||||
*val2 = chan->scan_type.realbits;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int ltc1660_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val,
|
||||
int val2,
|
||||
long mask)
|
||||
{
|
||||
struct ltc1660_priv *priv = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (val2 != 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (val < 0 || val > GENMASK(chan->scan_type.realbits - 1, 0))
|
||||
return -EINVAL;
|
||||
|
||||
ret = regmap_write(priv->regmap, chan->channel,
|
||||
(val << chan->scan_type.shift));
|
||||
if (!ret)
|
||||
priv->value[chan->channel] = val;
|
||||
|
||||
return ret;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
#define LTC1660_CHAN(chan, bits) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.output = 1, \
|
||||
.channel = chan, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = (bits), \
|
||||
.storagebits = 16, \
|
||||
.shift = 12 - (bits), \
|
||||
}, \
|
||||
}
|
||||
|
||||
#define LTC1660_OCTAL_CHANNELS(bits) { \
|
||||
LTC1660_CHAN(LTC1660_REG_DAC_A, bits), \
|
||||
LTC1660_CHAN(LTC1660_REG_DAC_B, bits), \
|
||||
LTC1660_CHAN(LTC1660_REG_DAC_C, bits), \
|
||||
LTC1660_CHAN(LTC1660_REG_DAC_D, bits), \
|
||||
LTC1660_CHAN(LTC1660_REG_DAC_E, bits), \
|
||||
LTC1660_CHAN(LTC1660_REG_DAC_F, bits), \
|
||||
LTC1660_CHAN(LTC1660_REG_DAC_G, bits), \
|
||||
LTC1660_CHAN(LTC1660_REG_DAC_H, bits), \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec ltc1660_channels[][LTC1660_NUM_CHANNELS] = {
|
||||
[ID_LTC1660] = LTC1660_OCTAL_CHANNELS(10),
|
||||
[ID_LTC1665] = LTC1660_OCTAL_CHANNELS(8),
|
||||
};
|
||||
|
||||
static const struct iio_info ltc1660_info = {
|
||||
.read_raw = <c1660_read_raw,
|
||||
.write_raw = <c1660_write_raw,
|
||||
};
|
||||
|
||||
static int __maybe_unused ltc1660_suspend(struct device *dev)
|
||||
{
|
||||
struct ltc1660_priv *priv = iio_priv(spi_get_drvdata(
|
||||
to_spi_device(dev)));
|
||||
return regmap_write(priv->regmap, LTC1660_REG_SLEEP, 0x00);
|
||||
}
|
||||
|
||||
static int __maybe_unused ltc1660_resume(struct device *dev)
|
||||
{
|
||||
struct ltc1660_priv *priv = iio_priv(spi_get_drvdata(
|
||||
to_spi_device(dev)));
|
||||
return regmap_write(priv->regmap, LTC1660_REG_WAKE, 0x00);
|
||||
}
|
||||
static SIMPLE_DEV_PM_OPS(ltc1660_pm_ops, ltc1660_suspend, ltc1660_resume);
|
||||
|
||||
static int ltc1660_probe(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct ltc1660_priv *priv;
|
||||
const struct spi_device_id *id = spi_get_device_id(spi);
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*priv));
|
||||
if (indio_dev == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
priv = iio_priv(indio_dev);
|
||||
priv->regmap = devm_regmap_init_spi(spi, <c1660_regmap_config);
|
||||
if (IS_ERR(priv->regmap)) {
|
||||
dev_err(&spi->dev, "failed to register spi regmap %ld\n",
|
||||
PTR_ERR(priv->regmap));
|
||||
return PTR_ERR(priv->regmap);
|
||||
}
|
||||
|
||||
priv->vref_reg = devm_regulator_get(&spi->dev, "vref");
|
||||
if (IS_ERR(priv->vref_reg)) {
|
||||
dev_err(&spi->dev, "vref regulator not specified\n");
|
||||
return PTR_ERR(priv->vref_reg);
|
||||
}
|
||||
|
||||
ret = regulator_enable(priv->vref_reg);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev, "failed to enable vref regulator: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
priv->spi = spi;
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->info = <c1660_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = ltc1660_channels[id->driver_data];
|
||||
indio_dev->num_channels = LTC1660_NUM_CHANNELS;
|
||||
indio_dev->name = id->name;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev, "failed to register iio device: %d\n",
|
||||
ret);
|
||||
goto error_disable_reg;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error_disable_reg:
|
||||
regulator_disable(priv->vref_reg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ltc1660_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct ltc1660_priv *priv = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
regulator_disable(priv->vref_reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id ltc1660_dt_ids[] = {
|
||||
{ .compatible = "lltc,ltc1660", .data = (void *)ID_LTC1660 },
|
||||
{ .compatible = "lltc,ltc1665", .data = (void *)ID_LTC1665 },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ltc1660_dt_ids);
|
||||
|
||||
static const struct spi_device_id ltc1660_id[] = {
|
||||
{"ltc1660", ID_LTC1660},
|
||||
{"ltc1665", ID_LTC1665},
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ltc1660_id);
|
||||
|
||||
static struct spi_driver ltc1660_driver = {
|
||||
.driver = {
|
||||
.name = "ltc1660",
|
||||
.of_match_table = ltc1660_dt_ids,
|
||||
.pm = <c1660_pm_ops,
|
||||
},
|
||||
.probe = ltc1660_probe,
|
||||
.remove = ltc1660_remove,
|
||||
.id_table = ltc1660_id,
|
||||
};
|
||||
module_spi_driver(ltc1660_driver);
|
||||
|
||||
MODULE_AUTHOR("Marcus Folkesson <marcus.folkesson@gmail.com>");
|
||||
MODULE_DESCRIPTION("Linear Technology LTC1660/LTC1665 DAC");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -113,15 +113,14 @@ static int max517_write_raw(struct iio_dev *indio_dev,
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int max517_suspend(struct device *dev)
|
||||
static int __maybe_unused max517_suspend(struct device *dev)
|
||||
{
|
||||
u8 outbuf = COMMAND_PD;
|
||||
|
||||
return i2c_master_send(to_i2c_client(dev), &outbuf, 1);
|
||||
}
|
||||
|
||||
static int max517_resume(struct device *dev)
|
||||
static int __maybe_unused max517_resume(struct device *dev)
|
||||
{
|
||||
u8 outbuf = 0;
|
||||
|
||||
@ -129,10 +128,6 @@ static int max517_resume(struct device *dev)
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(max517_pm_ops, max517_suspend, max517_resume);
|
||||
#define MAX517_PM_OPS (&max517_pm_ops)
|
||||
#else
|
||||
#define MAX517_PM_OPS NULL
|
||||
#endif
|
||||
|
||||
static const struct iio_info max517_info = {
|
||||
.read_raw = max517_read_raw,
|
||||
@ -229,7 +224,7 @@ MODULE_DEVICE_TABLE(i2c, max517_id);
|
||||
static struct i2c_driver max517_driver = {
|
||||
.driver = {
|
||||
.name = MAX517_DRV_NAME,
|
||||
.pm = MAX517_PM_OPS,
|
||||
.pm = &max517_pm_ops,
|
||||
},
|
||||
.probe = max517_probe,
|
||||
.remove = max517_remove,
|
||||
|
@ -270,8 +270,7 @@ static int max5821_write_raw(struct iio_dev *indio_dev,
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int max5821_suspend(struct device *dev)
|
||||
static int __maybe_unused max5821_suspend(struct device *dev)
|
||||
{
|
||||
u8 outbuf[2] = { MAX5821_EXTENDED_COMMAND_MODE,
|
||||
MAX5821_EXTENDED_DAC_A |
|
||||
@ -281,7 +280,7 @@ static int max5821_suspend(struct device *dev)
|
||||
return i2c_master_send(to_i2c_client(dev), outbuf, 2);
|
||||
}
|
||||
|
||||
static int max5821_resume(struct device *dev)
|
||||
static int __maybe_unused max5821_resume(struct device *dev)
|
||||
{
|
||||
u8 outbuf[2] = { MAX5821_EXTENDED_COMMAND_MODE,
|
||||
MAX5821_EXTENDED_DAC_A |
|
||||
@ -292,10 +291,6 @@ static int max5821_resume(struct device *dev)
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(max5821_pm_ops, max5821_suspend, max5821_resume);
|
||||
#define MAX5821_PM_OPS (&max5821_pm_ops)
|
||||
#else
|
||||
#define MAX5821_PM_OPS NULL
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
static const struct iio_info max5821_info = {
|
||||
.read_raw = max5821_read_raw,
|
||||
@ -392,7 +387,7 @@ static struct i2c_driver max5821_driver = {
|
||||
.driver = {
|
||||
.name = "max5821",
|
||||
.of_match_table = max5821_of_match,
|
||||
.pm = MAX5821_PM_OPS,
|
||||
.pm = &max5821_pm_ops,
|
||||
},
|
||||
.probe = max5821_probe,
|
||||
.remove = max5821_remove,
|
||||
|
@ -45,7 +45,7 @@ struct mcp4725_data {
|
||||
struct regulator *vref_reg;
|
||||
};
|
||||
|
||||
static int mcp4725_suspend(struct device *dev)
|
||||
static int __maybe_unused mcp4725_suspend(struct device *dev)
|
||||
{
|
||||
struct mcp4725_data *data = iio_priv(i2c_get_clientdata(
|
||||
to_i2c_client(dev)));
|
||||
@ -58,7 +58,7 @@ static int mcp4725_suspend(struct device *dev)
|
||||
return i2c_master_send(data->client, outbuf, 2);
|
||||
}
|
||||
|
||||
static int mcp4725_resume(struct device *dev)
|
||||
static int __maybe_unused mcp4725_resume(struct device *dev)
|
||||
{
|
||||
struct mcp4725_data *data = iio_priv(i2c_get_clientdata(
|
||||
to_i2c_client(dev)));
|
||||
@ -71,13 +71,7 @@ static int mcp4725_resume(struct device *dev)
|
||||
|
||||
return i2c_master_send(data->client, outbuf, 2);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static SIMPLE_DEV_PM_OPS(mcp4725_pm_ops, mcp4725_suspend, mcp4725_resume);
|
||||
#define MCP4725_PM_OPS (&mcp4725_pm_ops)
|
||||
#else
|
||||
#define MCP4725_PM_OPS NULL
|
||||
#endif
|
||||
|
||||
static ssize_t mcp4725_store_eeprom(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t len)
|
||||
@ -547,7 +541,7 @@ static struct i2c_driver mcp4725_driver = {
|
||||
.driver = {
|
||||
.name = MCP4725_DRV_NAME,
|
||||
.of_match_table = of_match_ptr(mcp4725_of_match),
|
||||
.pm = MCP4725_PM_OPS,
|
||||
.pm = &mcp4725_pm_ops,
|
||||
},
|
||||
.probe = mcp4725_probe,
|
||||
.remove = mcp4725_remove,
|
||||
|
@ -94,17 +94,22 @@ static int mcp4922_write_raw(struct iio_dev *indio_dev,
|
||||
long mask)
|
||||
{
|
||||
struct mcp4922_state *state = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
if (val2 != 0)
|
||||
return -EINVAL;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (val > GENMASK(chan->scan_type.realbits-1, 0))
|
||||
if (val < 0 || val > GENMASK(chan->scan_type.realbits - 1, 0))
|
||||
return -EINVAL;
|
||||
val <<= chan->scan_type.shift;
|
||||
state->value[chan->channel] = val;
|
||||
return mcp4922_spi_write(state, chan->channel, val);
|
||||
|
||||
ret = mcp4922_spi_write(state, chan->channel, val);
|
||||
if (!ret)
|
||||
state->value[chan->channel] = val;
|
||||
return ret;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -421,6 +421,7 @@ MODULE_DEVICE_TABLE(i2c, dac5571_id);
|
||||
static struct i2c_driver dac5571_driver = {
|
||||
.driver = {
|
||||
.name = "ti-dac5571",
|
||||
.of_match_table = of_match_ptr(dac5571_of_id),
|
||||
},
|
||||
.probe = dac5571_probe,
|
||||
.remove = dac5571_remove,
|
||||
|
@ -1078,6 +1078,6 @@ static struct spi_driver ad9523_driver = {
|
||||
};
|
||||
module_spi_driver(ad9523_driver);
|
||||
|
||||
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
|
||||
MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD9523 CLOCKDIST/PLL");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
@ -388,7 +388,7 @@ static struct adf4350_platform_data *adf4350_parse_dt(struct device *dev)
|
||||
if (!pdata)
|
||||
return NULL;
|
||||
|
||||
strncpy(&pdata->name[0], np->name, SPI_NAME_SIZE - 1);
|
||||
snprintf(&pdata->name[0], SPI_NAME_SIZE - 1, "%pOFn", np);
|
||||
|
||||
tmp = 10000;
|
||||
of_property_read_u32(np, "adi,channel-spacing", &tmp);
|
||||
|
@ -282,9 +282,11 @@ static int max30102_read_measurement(struct max30102_data *data,
|
||||
switch (measurements) {
|
||||
case 3:
|
||||
MAX30102_COPY_DATA(2);
|
||||
case 2: /* fall-through */
|
||||
/* fall through */
|
||||
case 2:
|
||||
MAX30102_COPY_DATA(1);
|
||||
case 1: /* fall-through */
|
||||
/* fall through */
|
||||
case 1:
|
||||
MAX30102_COPY_DATA(0);
|
||||
break;
|
||||
default:
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include "inv_mpu_iio.h"
|
||||
|
||||
/*
|
||||
@ -926,6 +927,39 @@ static int inv_check_and_setup_chip(struct inv_mpu6050_state *st)
|
||||
return result;
|
||||
}
|
||||
|
||||
static int inv_mpu_core_enable_regulator(struct inv_mpu6050_state *st)
|
||||
{
|
||||
int result;
|
||||
|
||||
result = regulator_enable(st->vddio_supply);
|
||||
if (result) {
|
||||
dev_err(regmap_get_device(st->map),
|
||||
"Failed to enable regulator: %d\n", result);
|
||||
} else {
|
||||
/* Give the device a little bit of time to start up. */
|
||||
usleep_range(35000, 70000);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int inv_mpu_core_disable_regulator(struct inv_mpu6050_state *st)
|
||||
{
|
||||
int result;
|
||||
|
||||
result = regulator_disable(st->vddio_supply);
|
||||
if (result)
|
||||
dev_err(regmap_get_device(st->map),
|
||||
"Failed to disable regulator: %d\n", result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void inv_mpu_core_disable_regulator_action(void *_data)
|
||||
{
|
||||
inv_mpu_core_disable_regulator(_data);
|
||||
}
|
||||
|
||||
int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name,
|
||||
int (*inv_mpu_bus_setup)(struct iio_dev *), int chip_type)
|
||||
{
|
||||
@ -992,6 +1026,28 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
st->vddio_supply = devm_regulator_get(dev, "vddio");
|
||||
if (IS_ERR(st->vddio_supply)) {
|
||||
if (PTR_ERR(st->vddio_supply) != -EPROBE_DEFER)
|
||||
dev_err(dev, "Failed to get vddio regulator %d\n",
|
||||
(int)PTR_ERR(st->vddio_supply));
|
||||
|
||||
return PTR_ERR(st->vddio_supply);
|
||||
}
|
||||
|
||||
result = inv_mpu_core_enable_regulator(st);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
result = devm_add_action(dev, inv_mpu_core_disable_regulator_action,
|
||||
st);
|
||||
if (result) {
|
||||
inv_mpu_core_disable_regulator_action(st);
|
||||
dev_err(dev, "Failed to setup regulator cleanup action %d\n",
|
||||
result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* power is turned on inside check chip type*/
|
||||
result = inv_check_and_setup_chip(st);
|
||||
if (result)
|
||||
@ -1051,7 +1107,12 @@ static int inv_mpu_resume(struct device *dev)
|
||||
int result;
|
||||
|
||||
mutex_lock(&st->lock);
|
||||
result = inv_mpu_core_enable_regulator(st);
|
||||
if (result)
|
||||
goto out_unlock;
|
||||
|
||||
result = inv_mpu6050_set_power_itg(st, true);
|
||||
out_unlock:
|
||||
mutex_unlock(&st->lock);
|
||||
|
||||
return result;
|
||||
@ -1064,6 +1125,7 @@ static int inv_mpu_suspend(struct device *dev)
|
||||
|
||||
mutex_lock(&st->lock);
|
||||
result = inv_mpu6050_set_power_itg(st, false);
|
||||
inv_mpu_core_disable_regulator(st);
|
||||
mutex_unlock(&st->lock);
|
||||
|
||||
return result;
|
||||
|
@ -129,6 +129,7 @@ struct inv_mpu6050_hw {
|
||||
* @chip_period: chip internal period estimation (~1kHz).
|
||||
* @it_timestamp: timestamp from previous interrupt.
|
||||
* @data_timestamp: timestamp for next data sample.
|
||||
* @vddio_supply voltage regulator for the chip.
|
||||
*/
|
||||
struct inv_mpu6050_state {
|
||||
struct mutex lock;
|
||||
@ -149,6 +150,7 @@ struct inv_mpu6050_state {
|
||||
s64 chip_period;
|
||||
s64 it_timestamp;
|
||||
s64 data_timestamp;
|
||||
struct regulator *vddio_supply;
|
||||
};
|
||||
|
||||
/*register and associated bit definition*/
|
||||
|
@ -9,7 +9,7 @@ config IIO_ST_LSM6DSX
|
||||
help
|
||||
Say yes here to build support for STMicroelectronics LSM6DSx imu
|
||||
sensor. Supported devices: lsm6ds3, lsm6ds3h, lsm6dsl, lsm6dsm,
|
||||
ism330dlc
|
||||
ism330dlc, lsm6dso
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called st_lsm6dsx.
|
||||
|
@ -19,6 +19,7 @@
|
||||
#define ST_LSM6DSL_DEV_NAME "lsm6dsl"
|
||||
#define ST_LSM6DSM_DEV_NAME "lsm6dsm"
|
||||
#define ST_ISM330DLC_DEV_NAME "ism330dlc"
|
||||
#define ST_LSM6DSO_DEV_NAME "lsm6dso"
|
||||
|
||||
enum st_lsm6dsx_hw_id {
|
||||
ST_LSM6DS3_ID,
|
||||
@ -26,14 +27,20 @@ enum st_lsm6dsx_hw_id {
|
||||
ST_LSM6DSL_ID,
|
||||
ST_LSM6DSM_ID,
|
||||
ST_ISM330DLC_ID,
|
||||
ST_LSM6DSO_ID,
|
||||
ST_LSM6DSX_MAX_ID,
|
||||
};
|
||||
|
||||
#define ST_LSM6DSX_BUFF_SIZE 400
|
||||
#define ST_LSM6DSX_BUFF_SIZE 512
|
||||
#define ST_LSM6DSX_CHAN_SIZE 2
|
||||
#define ST_LSM6DSX_SAMPLE_SIZE 6
|
||||
#define ST_LSM6DSX_TAG_SIZE 1
|
||||
#define ST_LSM6DSX_TAGGED_SAMPLE_SIZE (ST_LSM6DSX_SAMPLE_SIZE + \
|
||||
ST_LSM6DSX_TAG_SIZE)
|
||||
#define ST_LSM6DSX_MAX_WORD_LEN ((32 / ST_LSM6DSX_SAMPLE_SIZE) * \
|
||||
ST_LSM6DSX_SAMPLE_SIZE)
|
||||
#define ST_LSM6DSX_MAX_TAGGED_WORD_LEN ((32 / ST_LSM6DSX_TAGGED_SAMPLE_SIZE) \
|
||||
* ST_LSM6DSX_TAGGED_SAMPLE_SIZE)
|
||||
#define ST_LSM6DSX_SHIFT_VAL(val, mask) (((val) << __ffs(mask)) & (mask))
|
||||
|
||||
struct st_lsm6dsx_reg {
|
||||
@ -41,13 +48,17 @@ struct st_lsm6dsx_reg {
|
||||
u8 mask;
|
||||
};
|
||||
|
||||
struct st_lsm6dsx_hw;
|
||||
|
||||
/**
|
||||
* struct st_lsm6dsx_fifo_ops - ST IMU FIFO settings
|
||||
* @read_fifo: Read FIFO callback.
|
||||
* @fifo_th: FIFO threshold register info (addr + mask).
|
||||
* @fifo_diff: FIFO diff status register info (addr + mask).
|
||||
* @th_wl: FIFO threshold word length.
|
||||
*/
|
||||
struct st_lsm6dsx_fifo_ops {
|
||||
int (*read_fifo)(struct st_lsm6dsx_hw *hw);
|
||||
struct {
|
||||
u8 addr;
|
||||
u16 mask;
|
||||
@ -79,6 +90,7 @@ struct st_lsm6dsx_hw_ts_settings {
|
||||
* @max_fifo_size: Sensor max fifo length in FIFO words.
|
||||
* @id: List of hw id supported by the driver configuration.
|
||||
* @decimator: List of decimator register info (addr + mask).
|
||||
* @batch: List of FIFO batching register info (addr + mask).
|
||||
* @fifo_ops: Sensor hw FIFO parameters.
|
||||
* @ts_settings: Hw timer related settings.
|
||||
*/
|
||||
@ -87,6 +99,7 @@ struct st_lsm6dsx_settings {
|
||||
u16 max_fifo_size;
|
||||
enum st_lsm6dsx_hw_id id[ST_LSM6DSX_MAX_ID];
|
||||
struct st_lsm6dsx_reg decimator[ST_LSM6DSX_MAX_ID];
|
||||
struct st_lsm6dsx_reg batch[ST_LSM6DSX_MAX_ID];
|
||||
struct st_lsm6dsx_fifo_ops fifo_ops;
|
||||
struct st_lsm6dsx_hw_ts_settings ts_settings;
|
||||
};
|
||||
@ -175,5 +188,8 @@ int st_lsm6dsx_update_watermark(struct st_lsm6dsx_sensor *sensor,
|
||||
int st_lsm6dsx_flush_fifo(struct st_lsm6dsx_hw *hw);
|
||||
int st_lsm6dsx_set_fifo_mode(struct st_lsm6dsx_hw *hw,
|
||||
enum st_lsm6dsx_fifo_mode fifo_mode);
|
||||
int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw);
|
||||
int st_lsm6dsx_read_tagged_fifo(struct st_lsm6dsx_hw *hw);
|
||||
int st_lsm6dsx_check_odr(struct st_lsm6dsx_sensor *sensor, u16 odr, u8 *val);
|
||||
|
||||
#endif /* ST_LSM6DSX_H */
|
||||
|
@ -12,6 +12,11 @@
|
||||
* buffer contains the data of all the enabled FIFO data sets
|
||||
* (e.g. Gx, Gy, Gz, Ax, Ay, Az), then data are repeated depending on the
|
||||
* value of the decimation factor and ODR set for each FIFO data set.
|
||||
*
|
||||
* LSM6DSO: The FIFO buffer can be configured to store data from gyroscope and
|
||||
* accelerometer. Each sample is queued with a tag (1B) indicating data source
|
||||
* (gyroscope, accelerometer, hw timer).
|
||||
*
|
||||
* FIFO supported modes:
|
||||
* - BYPASS: FIFO disabled
|
||||
* - CONTINUOUS: FIFO enabled. When the buffer is full, the FIFO index
|
||||
@ -46,6 +51,7 @@
|
||||
#define ST_LSM6DSX_FIFO_ODR_MASK GENMASK(6, 3)
|
||||
#define ST_LSM6DSX_FIFO_EMPTY_MASK BIT(12)
|
||||
#define ST_LSM6DSX_REG_FIFO_OUTL_ADDR 0x3e
|
||||
#define ST_LSM6DSX_REG_FIFO_OUT_TAG_ADDR 0x78
|
||||
#define ST_LSM6DSX_REG_TS_RESET_ADDR 0x42
|
||||
|
||||
#define ST_LSM6DSX_MAX_FIFO_ODR_VAL 0x08
|
||||
@ -58,6 +64,12 @@ struct st_lsm6dsx_decimator_entry {
|
||||
u8 val;
|
||||
};
|
||||
|
||||
enum st_lsm6dsx_fifo_tag {
|
||||
ST_LSM6DSX_GYRO_TAG = 0x01,
|
||||
ST_LSM6DSX_ACC_TAG = 0x02,
|
||||
ST_LSM6DSX_TS_TAG = 0x04,
|
||||
};
|
||||
|
||||
static const
|
||||
struct st_lsm6dsx_decimator_entry st_lsm6dsx_decimator_table[] = {
|
||||
{ 0, 0x0 },
|
||||
@ -177,12 +189,34 @@ static int st_lsm6dsx_set_fifo_odr(struct st_lsm6dsx_sensor *sensor,
|
||||
bool enable)
|
||||
{
|
||||
struct st_lsm6dsx_hw *hw = sensor->hw;
|
||||
const struct st_lsm6dsx_reg *batch_reg;
|
||||
u8 data;
|
||||
|
||||
data = hw->enable_mask ? ST_LSM6DSX_MAX_FIFO_ODR_VAL : 0;
|
||||
return regmap_update_bits(hw->regmap, ST_LSM6DSX_REG_FIFO_MODE_ADDR,
|
||||
ST_LSM6DSX_FIFO_ODR_MASK,
|
||||
FIELD_PREP(ST_LSM6DSX_FIFO_ODR_MASK, data));
|
||||
batch_reg = &hw->settings->batch[sensor->id];
|
||||
if (batch_reg->addr) {
|
||||
int val;
|
||||
|
||||
if (enable) {
|
||||
int err;
|
||||
|
||||
err = st_lsm6dsx_check_odr(sensor, sensor->odr,
|
||||
&data);
|
||||
if (err < 0)
|
||||
return err;
|
||||
} else {
|
||||
data = 0;
|
||||
}
|
||||
val = ST_LSM6DSX_SHIFT_VAL(data, batch_reg->mask);
|
||||
return regmap_update_bits(hw->regmap, batch_reg->addr,
|
||||
batch_reg->mask, val);
|
||||
} else {
|
||||
data = hw->enable_mask ? ST_LSM6DSX_MAX_FIFO_ODR_VAL : 0;
|
||||
return regmap_update_bits(hw->regmap,
|
||||
ST_LSM6DSX_REG_FIFO_MODE_ADDR,
|
||||
ST_LSM6DSX_FIFO_ODR_MASK,
|
||||
FIELD_PREP(ST_LSM6DSX_FIFO_ODR_MASK,
|
||||
data));
|
||||
}
|
||||
}
|
||||
|
||||
int st_lsm6dsx_update_watermark(struct st_lsm6dsx_sensor *sensor, u16 watermark)
|
||||
@ -250,21 +284,21 @@ static int st_lsm6dsx_reset_hw_ts(struct st_lsm6dsx_hw *hw)
|
||||
}
|
||||
|
||||
/*
|
||||
* Set max bulk read to ST_LSM6DSX_MAX_WORD_LEN in order to avoid
|
||||
* a kmalloc for each bus access
|
||||
* Set max bulk read to ST_LSM6DSX_MAX_WORD_LEN/ST_LSM6DSX_MAX_TAGGED_WORD_LEN
|
||||
* in order to avoid a kmalloc for each bus access
|
||||
*/
|
||||
static inline int st_lsm6dsx_read_block(struct st_lsm6dsx_hw *hw, u8 *data,
|
||||
unsigned int data_len)
|
||||
static inline int st_lsm6dsx_read_block(struct st_lsm6dsx_hw *hw, u8 addr,
|
||||
u8 *data, unsigned int data_len,
|
||||
unsigned int max_word_len)
|
||||
{
|
||||
unsigned int word_len, read_len = 0;
|
||||
int err;
|
||||
|
||||
while (read_len < data_len) {
|
||||
word_len = min_t(unsigned int, data_len - read_len,
|
||||
ST_LSM6DSX_MAX_WORD_LEN);
|
||||
err = regmap_bulk_read(hw->regmap,
|
||||
ST_LSM6DSX_REG_FIFO_OUTL_ADDR,
|
||||
data + read_len, word_len);
|
||||
max_word_len);
|
||||
err = regmap_bulk_read(hw->regmap, addr, data + read_len,
|
||||
word_len);
|
||||
if (err < 0)
|
||||
return err;
|
||||
read_len += word_len;
|
||||
@ -282,7 +316,7 @@ static inline int st_lsm6dsx_read_block(struct st_lsm6dsx_hw *hw, u8 *data,
|
||||
*
|
||||
* Return: Number of bytes read from the FIFO
|
||||
*/
|
||||
static int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw)
|
||||
int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw)
|
||||
{
|
||||
u16 fifo_len, pattern_len = hw->sip * ST_LSM6DSX_SAMPLE_SIZE;
|
||||
u16 fifo_diff_mask = hw->settings->fifo_ops.fifo_diff.mask;
|
||||
@ -314,7 +348,9 @@ static int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw)
|
||||
gyro_sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_GYRO]);
|
||||
|
||||
for (read_len = 0; read_len < fifo_len; read_len += pattern_len) {
|
||||
err = st_lsm6dsx_read_block(hw, hw->buff, pattern_len);
|
||||
err = st_lsm6dsx_read_block(hw, ST_LSM6DSX_REG_FIFO_OUTL_ADDR,
|
||||
hw->buff, pattern_len,
|
||||
ST_LSM6DSX_MAX_WORD_LEN);
|
||||
if (err < 0) {
|
||||
dev_err(hw->dev,
|
||||
"failed to read pattern from fifo (err=%d)\n",
|
||||
@ -400,13 +436,111 @@ static int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw)
|
||||
return read_len;
|
||||
}
|
||||
|
||||
/**
|
||||
* st_lsm6dsx_read_tagged_fifo() - LSM6DSO read FIFO routine
|
||||
* @hw: Pointer to instance of struct st_lsm6dsx_hw.
|
||||
*
|
||||
* Read samples from the hw FIFO and push them to IIO buffers.
|
||||
*
|
||||
* Return: Number of bytes read from the FIFO
|
||||
*/
|
||||
int st_lsm6dsx_read_tagged_fifo(struct st_lsm6dsx_hw *hw)
|
||||
{
|
||||
u16 pattern_len = hw->sip * ST_LSM6DSX_TAGGED_SAMPLE_SIZE;
|
||||
u16 fifo_len, fifo_diff_mask;
|
||||
struct st_lsm6dsx_sensor *acc_sensor, *gyro_sensor;
|
||||
u8 iio_buff[ST_LSM6DSX_IIO_BUFF_SIZE], tag;
|
||||
bool reset_ts = false;
|
||||
int i, err, read_len;
|
||||
__le16 fifo_status;
|
||||
s64 ts = 0;
|
||||
|
||||
err = regmap_bulk_read(hw->regmap,
|
||||
hw->settings->fifo_ops.fifo_diff.addr,
|
||||
&fifo_status, sizeof(fifo_status));
|
||||
if (err < 0) {
|
||||
dev_err(hw->dev, "failed to read fifo status (err=%d)\n",
|
||||
err);
|
||||
return err;
|
||||
}
|
||||
|
||||
fifo_diff_mask = hw->settings->fifo_ops.fifo_diff.mask;
|
||||
fifo_len = (le16_to_cpu(fifo_status) & fifo_diff_mask) *
|
||||
ST_LSM6DSX_TAGGED_SAMPLE_SIZE;
|
||||
if (!fifo_len)
|
||||
return 0;
|
||||
|
||||
acc_sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_ACC]);
|
||||
gyro_sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_GYRO]);
|
||||
|
||||
for (read_len = 0; read_len < fifo_len; read_len += pattern_len) {
|
||||
err = st_lsm6dsx_read_block(hw,
|
||||
ST_LSM6DSX_REG_FIFO_OUT_TAG_ADDR,
|
||||
hw->buff, pattern_len,
|
||||
ST_LSM6DSX_MAX_TAGGED_WORD_LEN);
|
||||
if (err < 0) {
|
||||
dev_err(hw->dev,
|
||||
"failed to read pattern from fifo (err=%d)\n",
|
||||
err);
|
||||
return err;
|
||||
}
|
||||
|
||||
for (i = 0; i < pattern_len;
|
||||
i += ST_LSM6DSX_TAGGED_SAMPLE_SIZE) {
|
||||
memcpy(iio_buff, &hw->buff[i + ST_LSM6DSX_TAG_SIZE],
|
||||
ST_LSM6DSX_SAMPLE_SIZE);
|
||||
|
||||
tag = hw->buff[i] >> 3;
|
||||
switch (tag) {
|
||||
case ST_LSM6DSX_TS_TAG:
|
||||
/*
|
||||
* hw timestamp is 4B long and it is stored
|
||||
* in FIFO according to this schema:
|
||||
* B0 = ts[7:0], B1 = ts[15:8], B2 = ts[23:16],
|
||||
* B3 = ts[31:24]
|
||||
*/
|
||||
ts = le32_to_cpu(*((__le32 *)iio_buff));
|
||||
/*
|
||||
* check if hw timestamp engine is going to
|
||||
* reset (the sensor generates an interrupt
|
||||
* to signal the hw timestamp will reset in
|
||||
* 1.638s)
|
||||
*/
|
||||
if (!reset_ts && ts >= 0xffff0000)
|
||||
reset_ts = true;
|
||||
ts *= ST_LSM6DSX_TS_SENSITIVITY;
|
||||
break;
|
||||
case ST_LSM6DSX_GYRO_TAG:
|
||||
iio_push_to_buffers_with_timestamp(
|
||||
hw->iio_devs[ST_LSM6DSX_ID_GYRO],
|
||||
iio_buff, gyro_sensor->ts_ref + ts);
|
||||
break;
|
||||
case ST_LSM6DSX_ACC_TAG:
|
||||
iio_push_to_buffers_with_timestamp(
|
||||
hw->iio_devs[ST_LSM6DSX_ID_ACC],
|
||||
iio_buff, acc_sensor->ts_ref + ts);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (unlikely(reset_ts)) {
|
||||
err = st_lsm6dsx_reset_hw_ts(hw);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
return read_len;
|
||||
}
|
||||
|
||||
int st_lsm6dsx_flush_fifo(struct st_lsm6dsx_hw *hw)
|
||||
{
|
||||
int err;
|
||||
|
||||
mutex_lock(&hw->fifo_lock);
|
||||
|
||||
st_lsm6dsx_read_fifo(hw);
|
||||
hw->settings->fifo_ops.read_fifo(hw);
|
||||
err = st_lsm6dsx_set_fifo_mode(hw, ST_LSM6DSX_FIFO_BYPASS);
|
||||
|
||||
mutex_unlock(&hw->fifo_lock);
|
||||
@ -478,7 +612,7 @@ static irqreturn_t st_lsm6dsx_handler_thread(int irq, void *private)
|
||||
int count;
|
||||
|
||||
mutex_lock(&hw->fifo_lock);
|
||||
count = st_lsm6dsx_read_fifo(hw);
|
||||
count = hw->settings->fifo_ops.read_fifo(hw);
|
||||
mutex_unlock(&hw->fifo_lock);
|
||||
|
||||
return !count ? IRQ_NONE : IRQ_HANDLED;
|
||||
|
@ -23,6 +23,12 @@
|
||||
* - Gyroscope supported full-scale [dps]: +-125/+-245/+-500/+-1000/+-2000
|
||||
* - FIFO size: 4KB
|
||||
*
|
||||
* - LSM6DSO
|
||||
* - 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
|
||||
* - FIFO size: 3KB
|
||||
*
|
||||
* Copyright 2016 STMicroelectronics Inc.
|
||||
*
|
||||
* Lorenzo Bianconi <lorenzo.bianconi@st.com>
|
||||
@ -171,6 +177,7 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
|
||||
},
|
||||
},
|
||||
.fifo_ops = {
|
||||
.read_fifo = st_lsm6dsx_read_fifo,
|
||||
.fifo_th = {
|
||||
.addr = 0x06,
|
||||
.mask = GENMASK(11, 0),
|
||||
@ -217,6 +224,7 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
|
||||
},
|
||||
},
|
||||
.fifo_ops = {
|
||||
.read_fifo = st_lsm6dsx_read_fifo,
|
||||
.fifo_th = {
|
||||
.addr = 0x06,
|
||||
.mask = GENMASK(11, 0),
|
||||
@ -265,6 +273,7 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
|
||||
},
|
||||
},
|
||||
.fifo_ops = {
|
||||
.read_fifo = st_lsm6dsx_read_fifo,
|
||||
.fifo_th = {
|
||||
.addr = 0x06,
|
||||
.mask = GENMASK(10, 0),
|
||||
@ -294,6 +303,45 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
.wai = 0x6c,
|
||||
.max_fifo_size = 512,
|
||||
.id = {
|
||||
[0] = ST_LSM6DSO_ID,
|
||||
},
|
||||
.batch = {
|
||||
[ST_LSM6DSX_ID_ACC] = {
|
||||
.addr = 0x09,
|
||||
.mask = GENMASK(3, 0),
|
||||
},
|
||||
[ST_LSM6DSX_ID_GYRO] = {
|
||||
.addr = 0x09,
|
||||
.mask = GENMASK(7, 4),
|
||||
},
|
||||
},
|
||||
.fifo_ops = {
|
||||
.read_fifo = st_lsm6dsx_read_tagged_fifo,
|
||||
.fifo_th = {
|
||||
.addr = 0x07,
|
||||
.mask = GENMASK(8, 0),
|
||||
},
|
||||
.fifo_diff = {
|
||||
.addr = 0x3a,
|
||||
.mask = GENMASK(8, 0),
|
||||
},
|
||||
.th_wl = 1,
|
||||
},
|
||||
.ts_settings = {
|
||||
.timer_en = {
|
||||
.addr = 0x19,
|
||||
.mask = BIT(5),
|
||||
},
|
||||
.decimator = {
|
||||
.addr = 0x0a,
|
||||
.mask = GENMASK(7, 6),
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
#define ST_LSM6DSX_CHANNEL(chan_type, addr, mod, scan_idx) \
|
||||
@ -395,8 +443,7 @@ static int st_lsm6dsx_set_full_scale(struct st_lsm6dsx_sensor *sensor,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int st_lsm6dsx_check_odr(struct st_lsm6dsx_sensor *sensor, u16 odr,
|
||||
u8 *val)
|
||||
int st_lsm6dsx_check_odr(struct st_lsm6dsx_sensor *sensor, u16 odr, u8 *val)
|
||||
{
|
||||
int i;
|
||||
|
||||
|
@ -61,6 +61,10 @@ static const struct of_device_id st_lsm6dsx_i2c_of_match[] = {
|
||||
.compatible = "st,ism330dlc",
|
||||
.data = (void *)ST_ISM330DLC_ID,
|
||||
},
|
||||
{
|
||||
.compatible = "st,lsm6dso",
|
||||
.data = (void *)ST_LSM6DSO_ID,
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, st_lsm6dsx_i2c_of_match);
|
||||
@ -71,6 +75,7 @@ static const struct i2c_device_id st_lsm6dsx_i2c_id_table[] = {
|
||||
{ ST_LSM6DSL_DEV_NAME, ST_LSM6DSL_ID },
|
||||
{ ST_LSM6DSM_DEV_NAME, ST_LSM6DSM_ID },
|
||||
{ ST_ISM330DLC_DEV_NAME, ST_ISM330DLC_ID },
|
||||
{ ST_LSM6DSO_DEV_NAME, ST_LSM6DSO_ID },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, st_lsm6dsx_i2c_id_table);
|
||||
|
@ -61,6 +61,10 @@ static const struct of_device_id st_lsm6dsx_spi_of_match[] = {
|
||||
.compatible = "st,ism330dlc",
|
||||
.data = (void *)ST_ISM330DLC_ID,
|
||||
},
|
||||
{
|
||||
.compatible = "st,lsm6dso",
|
||||
.data = (void *)ST_LSM6DSO_ID,
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, st_lsm6dsx_spi_of_match);
|
||||
@ -71,6 +75,7 @@ static const struct spi_device_id st_lsm6dsx_spi_id_table[] = {
|
||||
{ ST_LSM6DSL_DEV_NAME, ST_LSM6DSL_ID },
|
||||
{ ST_LSM6DSM_DEV_NAME, ST_LSM6DSM_ID },
|
||||
{ ST_ISM330DLC_DEV_NAME, ST_ISM330DLC_ID },
|
||||
{ ST_LSM6DSO_DEV_NAME, ST_LSM6DSO_ID },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, st_lsm6dsx_spi_id_table);
|
||||
|
@ -1,12 +1,9 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* ROHM BH1710/BH1715/BH1721/BH1750/BH1751 ambient light sensor driver
|
||||
*
|
||||
* Copyright (c) Tomasz Duszynski <tduszyns@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Data sheets:
|
||||
* http://rohmfs.rohm.com/en/products/databook/datasheet/ic/sensor/light/bh1710fvc-e.pdf
|
||||
* http://rohmfs.rohm.com/en/products/databook/datasheet/ic/sensor/light/bh1715fvc-e.pdf
|
||||
@ -281,8 +278,7 @@ static int bh1750_remove(struct i2c_client *client)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int bh1750_suspend(struct device *dev)
|
||||
static int __maybe_unused bh1750_suspend(struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
struct bh1750_data *data =
|
||||
@ -300,10 +296,6 @@ static int bh1750_suspend(struct device *dev)
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(bh1750_pm_ops, bh1750_suspend, NULL);
|
||||
#define BH1750_PM_OPS (&bh1750_pm_ops)
|
||||
#else
|
||||
#define BH1750_PM_OPS NULL
|
||||
#endif
|
||||
|
||||
static const struct i2c_device_id bh1750_id[] = {
|
||||
{ "bh1710", BH1710 },
|
||||
@ -315,10 +307,21 @@ static const struct i2c_device_id bh1750_id[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, bh1750_id);
|
||||
|
||||
static const struct of_device_id bh1750_of_match[] = {
|
||||
{ .compatible = "rohm,bh1710", },
|
||||
{ .compatible = "rohm,bh1715", },
|
||||
{ .compatible = "rohm,bh1721", },
|
||||
{ .compatible = "rohm,bh1750", },
|
||||
{ .compatible = "rohm,bh1751", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, bh1750_of_match);
|
||||
|
||||
static struct i2c_driver bh1750_driver = {
|
||||
.driver = {
|
||||
.name = "bh1750",
|
||||
.pm = BH1750_PM_OPS,
|
||||
.of_match_table = bh1750_of_match,
|
||||
.pm = &bh1750_pm_ops,
|
||||
},
|
||||
.probe = bh1750_probe,
|
||||
.remove = bh1750_remove,
|
||||
|
@ -99,7 +99,6 @@ static const int max44000_alspga_shift[] = {0, 2, 4, 7};
|
||||
* Handling this internally is also required for buffer support because the
|
||||
* channel's scan_type can't be modified dynamically.
|
||||
*/
|
||||
static const int max44000_alstim_shift[] = {0, 2, 4, 6};
|
||||
#define MAX44000_ALSTIM_SHIFT(alstim) (2 * (alstim))
|
||||
|
||||
/* Available integration times with pretty manual alignment: */
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/platform_data/tsl2772.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
/* Cal defs */
|
||||
#define PROX_STAT_CAL 0
|
||||
@ -107,6 +108,11 @@
|
||||
#define TSL2772_ALS_GAIN_TRIM_MIN 250
|
||||
#define TSL2772_ALS_GAIN_TRIM_MAX 4000
|
||||
|
||||
#define TSL2772_MAX_PROX_LEDS 2
|
||||
|
||||
#define TSL2772_BOOT_MIN_SLEEP_TIME 10000
|
||||
#define TSL2772_BOOT_MAX_SLEEP_TIME 28000
|
||||
|
||||
/* Device family members */
|
||||
enum {
|
||||
tsl2571,
|
||||
@ -118,7 +124,8 @@ enum {
|
||||
tsl2672,
|
||||
tmd2672,
|
||||
tsl2772,
|
||||
tmd2772
|
||||
tmd2772,
|
||||
apds9930,
|
||||
};
|
||||
|
||||
enum {
|
||||
@ -141,11 +148,21 @@ struct tsl2772_chip_info {
|
||||
const struct iio_info *info;
|
||||
};
|
||||
|
||||
static const int tsl2772_led_currents[][2] = {
|
||||
{ 100000, TSL2772_100_mA },
|
||||
{ 50000, TSL2772_50_mA },
|
||||
{ 25000, TSL2772_25_mA },
|
||||
{ 13000, TSL2772_13_mA },
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
struct tsl2772_chip {
|
||||
kernel_ulong_t id;
|
||||
struct mutex prox_mutex;
|
||||
struct mutex als_mutex;
|
||||
struct i2c_client *client;
|
||||
struct regulator *vdd_supply;
|
||||
struct regulator *vddio_supply;
|
||||
u16 prox_data;
|
||||
struct tsl2772_als_info als_cur_info;
|
||||
struct tsl2772_settings settings;
|
||||
@ -197,6 +214,12 @@ static const struct tsl2772_lux tmd2x72_lux_table[TSL2772_DEF_LUX_TABLE_SZ] = {
|
||||
{ 0, 0 },
|
||||
};
|
||||
|
||||
static const struct tsl2772_lux apds9930_lux_table[TSL2772_DEF_LUX_TABLE_SZ] = {
|
||||
{ 52000, 96824 },
|
||||
{ 38792, 67132 },
|
||||
{ 0, 0 },
|
||||
};
|
||||
|
||||
static const struct tsl2772_lux *tsl2772_default_lux_table_group[] = {
|
||||
[tsl2571] = tsl2x71_lux_table,
|
||||
[tsl2671] = tsl2x71_lux_table,
|
||||
@ -208,6 +231,7 @@ static const struct tsl2772_lux *tsl2772_default_lux_table_group[] = {
|
||||
[tmd2672] = tmd2x72_lux_table,
|
||||
[tsl2772] = tsl2x72_lux_table,
|
||||
[tmd2772] = tmd2x72_lux_table,
|
||||
[apds9930] = apds9930_lux_table,
|
||||
};
|
||||
|
||||
static const struct tsl2772_settings tsl2772_default_settings = {
|
||||
@ -258,6 +282,7 @@ static const int tsl2772_int_time_avail[][6] = {
|
||||
[tmd2672] = { 0, 2730, 0, 2730, 0, 699000 },
|
||||
[tsl2772] = { 0, 2730, 0, 2730, 0, 699000 },
|
||||
[tmd2772] = { 0, 2730, 0, 2730, 0, 699000 },
|
||||
[apds9930] = { 0, 2730, 0, 2730, 0, 699000 },
|
||||
};
|
||||
|
||||
static int tsl2772_int_calibscale_avail[] = { 1, 8, 16, 120 };
|
||||
@ -283,7 +308,8 @@ static const u8 device_channel_config[] = {
|
||||
[tsl2672] = PRX2,
|
||||
[tmd2672] = PRX2,
|
||||
[tsl2772] = ALSPRX2,
|
||||
[tmd2772] = ALSPRX2
|
||||
[tmd2772] = ALSPRX2,
|
||||
[apds9930] = ALSPRX2,
|
||||
};
|
||||
|
||||
static int tsl2772_read_status(struct tsl2772_chip *chip)
|
||||
@ -497,6 +523,7 @@ static int tsl2772_get_prox(struct iio_dev *indio_dev)
|
||||
case tmd2672:
|
||||
case tsl2772:
|
||||
case tmd2772:
|
||||
case apds9930:
|
||||
if (!(ret & TSL2772_STA_PRX_VALID)) {
|
||||
ret = -EINVAL;
|
||||
goto prox_poll_err;
|
||||
@ -515,6 +542,75 @@ static int tsl2772_get_prox(struct iio_dev *indio_dev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tsl2772_read_prox_led_current(struct tsl2772_chip *chip)
|
||||
{
|
||||
struct device_node *of_node = chip->client->dev.of_node;
|
||||
int ret, tmp, i;
|
||||
|
||||
ret = of_property_read_u32(of_node, "led-max-microamp", &tmp);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
for (i = 0; tsl2772_led_currents[i][0] != 0; i++) {
|
||||
if (tmp == tsl2772_led_currents[i][0]) {
|
||||
chip->settings.prox_power = tsl2772_led_currents[i][1];
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
dev_err(&chip->client->dev, "Invalid value %d for led-max-microamp\n",
|
||||
tmp);
|
||||
|
||||
return -EINVAL;
|
||||
|
||||
}
|
||||
|
||||
static int tsl2772_read_prox_diodes(struct tsl2772_chip *chip)
|
||||
{
|
||||
struct device_node *of_node = chip->client->dev.of_node;
|
||||
int i, ret, num_leds, prox_diode_mask;
|
||||
u32 leds[TSL2772_MAX_PROX_LEDS];
|
||||
|
||||
ret = of_property_count_u32_elems(of_node, "amstaos,proximity-diodes");
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
num_leds = ret;
|
||||
if (num_leds > TSL2772_MAX_PROX_LEDS)
|
||||
num_leds = TSL2772_MAX_PROX_LEDS;
|
||||
|
||||
ret = of_property_read_u32_array(of_node, "amstaos,proximity-diodes",
|
||||
leds, num_leds);
|
||||
if (ret < 0) {
|
||||
dev_err(&chip->client->dev,
|
||||
"Invalid value for amstaos,proximity-diodes: %d.\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
prox_diode_mask = 0;
|
||||
for (i = 0; i < num_leds; i++) {
|
||||
if (leds[i] == 0)
|
||||
prox_diode_mask |= TSL2772_DIODE0;
|
||||
else if (leds[i] == 1)
|
||||
prox_diode_mask |= TSL2772_DIODE1;
|
||||
else {
|
||||
dev_err(&chip->client->dev,
|
||||
"Invalid value %d in amstaos,proximity-diodes.\n",
|
||||
leds[i]);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tsl2772_parse_dt(struct tsl2772_chip *chip)
|
||||
{
|
||||
tsl2772_read_prox_led_current(chip);
|
||||
tsl2772_read_prox_diodes(chip);
|
||||
}
|
||||
|
||||
/**
|
||||
* tsl2772_defaults() - Populates the device nominal operating parameters
|
||||
* with those provided by a 'platform' data struct or
|
||||
@ -541,6 +637,8 @@ static void tsl2772_defaults(struct tsl2772_chip *chip)
|
||||
memcpy(chip->tsl2772_device_lux,
|
||||
tsl2772_default_lux_table_group[chip->id],
|
||||
TSL2772_DEFAULT_TABLE_BYTES);
|
||||
|
||||
tsl2772_parse_dt(chip);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -595,6 +693,52 @@ static int tsl2772_als_calibrate(struct iio_dev *indio_dev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void tsl2772_disable_regulators_action(void *_data)
|
||||
{
|
||||
struct tsl2772_chip *chip = _data;
|
||||
|
||||
regulator_disable(chip->vdd_supply);
|
||||
regulator_disable(chip->vddio_supply);
|
||||
}
|
||||
|
||||
static int tsl2772_enable_regulator(struct tsl2772_chip *chip,
|
||||
struct regulator *regulator)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = regulator_enable(regulator);
|
||||
if (ret < 0) {
|
||||
dev_err(&chip->client->dev, "Failed to enable regulator: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct regulator *tsl2772_get_regulator(struct tsl2772_chip *chip,
|
||||
char *name)
|
||||
{
|
||||
struct regulator *regulator;
|
||||
int ret;
|
||||
|
||||
regulator = devm_regulator_get(&chip->client->dev, name);
|
||||
if (IS_ERR(regulator)) {
|
||||
if (PTR_ERR(regulator) != -EPROBE_DEFER)
|
||||
dev_err(&chip->client->dev,
|
||||
"Failed to get %s regulator %d\n",
|
||||
name, (int)PTR_ERR(regulator));
|
||||
|
||||
return regulator;
|
||||
}
|
||||
|
||||
ret = tsl2772_enable_regulator(chip, regulator);
|
||||
if (ret < 0)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
return regulator;
|
||||
}
|
||||
|
||||
static int tsl2772_chip_on(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct tsl2772_chip *chip = iio_priv(indio_dev);
|
||||
@ -1260,6 +1404,7 @@ static int tsl2772_device_id_verif(int id, int target)
|
||||
case tmd2672:
|
||||
case tsl2772:
|
||||
case tmd2772:
|
||||
case apds9930:
|
||||
return (id & 0xf0) == SWORDFISH_ID;
|
||||
}
|
||||
|
||||
@ -1652,6 +1797,27 @@ static int tsl2772_probe(struct i2c_client *clientp,
|
||||
chip->client = clientp;
|
||||
i2c_set_clientdata(clientp, indio_dev);
|
||||
|
||||
chip->vddio_supply = tsl2772_get_regulator(chip, "vddio");
|
||||
if (IS_ERR(chip->vddio_supply))
|
||||
return PTR_ERR(chip->vddio_supply);
|
||||
|
||||
chip->vdd_supply = tsl2772_get_regulator(chip, "vdd");
|
||||
if (IS_ERR(chip->vdd_supply)) {
|
||||
regulator_disable(chip->vddio_supply);
|
||||
return PTR_ERR(chip->vdd_supply);
|
||||
}
|
||||
|
||||
ret = devm_add_action(&clientp->dev, tsl2772_disable_regulators_action,
|
||||
chip);
|
||||
if (ret < 0) {
|
||||
tsl2772_disable_regulators_action(chip);
|
||||
dev_err(&clientp->dev, "Failed to setup regulator cleanup action %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
usleep_range(TSL2772_BOOT_MIN_SLEEP_TIME, TSL2772_BOOT_MAX_SLEEP_TIME);
|
||||
|
||||
ret = i2c_smbus_read_byte_data(chip->client,
|
||||
TSL2772_CMD_REG | TSL2772_CHIPID);
|
||||
if (ret < 0)
|
||||
@ -1725,13 +1891,33 @@ static int tsl2772_probe(struct i2c_client *clientp,
|
||||
static int tsl2772_suspend(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct tsl2772_chip *chip = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
return tsl2772_chip_off(indio_dev);
|
||||
ret = tsl2772_chip_off(indio_dev);
|
||||
regulator_disable(chip->vdd_supply);
|
||||
regulator_disable(chip->vddio_supply);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tsl2772_resume(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct tsl2772_chip *chip = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = tsl2772_enable_regulator(chip, chip->vddio_supply);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = tsl2772_enable_regulator(chip, chip->vdd_supply);
|
||||
if (ret < 0) {
|
||||
regulator_disable(chip->vddio_supply);
|
||||
return ret;
|
||||
}
|
||||
|
||||
usleep_range(TSL2772_BOOT_MIN_SLEEP_TIME, TSL2772_BOOT_MAX_SLEEP_TIME);
|
||||
|
||||
return tsl2772_chip_on(indio_dev);
|
||||
}
|
||||
@ -1758,6 +1944,7 @@ static const struct i2c_device_id tsl2772_idtable[] = {
|
||||
{ "tmd2672", tmd2672 },
|
||||
{ "tsl2772", tsl2772 },
|
||||
{ "tmd2772", tmd2772 },
|
||||
{ "apds9930", apds9930},
|
||||
{}
|
||||
};
|
||||
|
||||
@ -1774,6 +1961,7 @@ static const struct of_device_id tsl2772_of_match[] = {
|
||||
{ .compatible = "amstaos,tmd2672" },
|
||||
{ .compatible = "amstaos,tsl2772" },
|
||||
{ .compatible = "amstaos,tmd2772" },
|
||||
{ .compatible = "avago,apds9930" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, tsl2772_of_match);
|
||||
|
@ -31,7 +31,7 @@ enum hmc5843_ids {
|
||||
};
|
||||
|
||||
/**
|
||||
* struct hcm5843_data - device specific data
|
||||
* struct hmc5843_data - device specific data
|
||||
* @dev: actual device
|
||||
* @lock: update and read regmap data
|
||||
* @regmap: hardware access register maps
|
||||
|
@ -1,13 +1,10 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* IIO multiplexer driver
|
||||
*
|
||||
* Copyright (C) 2017 Axentia Technologies AB
|
||||
*
|
||||
* Author: Peter Rosin <peda@axentia.se>
|
||||
*
|
||||
* 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/err.h>
|
||||
|
@ -137,7 +137,6 @@ static int max5481_probe(struct spi_device *spi)
|
||||
struct iio_dev *indio_dev;
|
||||
struct max5481_data *data;
|
||||
const struct spi_device_id *id = spi_get_device_id(spi);
|
||||
const struct of_device_id *match;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*data));
|
||||
@ -149,10 +148,8 @@ static int max5481_probe(struct spi_device *spi)
|
||||
|
||||
data->spi = spi;
|
||||
|
||||
match = of_match_device(of_match_ptr(max5481_match), &spi->dev);
|
||||
if (match)
|
||||
data->cfg = of_device_get_match_data(&spi->dev);
|
||||
else
|
||||
data->cfg = of_device_get_match_data(&spi->dev);
|
||||
if (!data->cfg)
|
||||
data->cfg = &max5481_cfg[id->driver_data];
|
||||
|
||||
indio_dev->name = id->name;
|
||||
|
@ -147,7 +147,6 @@ static int mcp4018_probe(struct i2c_client *client)
|
||||
struct device *dev = &client->dev;
|
||||
struct mcp4018_data *data;
|
||||
struct iio_dev *indio_dev;
|
||||
const struct of_device_id *match;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter,
|
||||
I2C_FUNC_SMBUS_BYTE)) {
|
||||
@ -162,10 +161,8 @@ static int mcp4018_probe(struct i2c_client *client)
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
data->client = client;
|
||||
|
||||
match = of_match_device(of_match_ptr(mcp4018_of_match), dev);
|
||||
if (match)
|
||||
data->cfg = of_device_get_match_data(dev);
|
||||
else
|
||||
data->cfg = of_device_get_match_data(dev);
|
||||
if (!data->cfg)
|
||||
data->cfg = &mcp4018_cfg[i2c_match_id(mcp4018_id, client)->driver_data];
|
||||
|
||||
indio_dev->dev.parent = dev;
|
||||
@ -190,4 +187,4 @@ module_i2c_driver(mcp4018_driver);
|
||||
|
||||
MODULE_AUTHOR("Peter Rosin <peda@axentia.se>");
|
||||
MODULE_DESCRIPTION("MCP4018 digital potentiometer");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
@ -1,3 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Industrial I/O driver for Microchip digital potentiometers
|
||||
* Copyright (c) 2015 Axentia Technologies AB
|
||||
@ -22,10 +23,6 @@
|
||||
* mcp4652 2 257 5, 10, 50, 100 01011xx
|
||||
* mcp4661 2 257 5, 10, 50, 100 0101xxx
|
||||
* mcp4662 2 257 5, 10, 50, 100 01011xx
|
||||
*
|
||||
* 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/module.h>
|
||||
@ -360,7 +357,6 @@ static int mcp4531_probe(struct i2c_client *client)
|
||||
struct device *dev = &client->dev;
|
||||
struct mcp4531_data *data;
|
||||
struct iio_dev *indio_dev;
|
||||
const struct of_device_id *match;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter,
|
||||
I2C_FUNC_SMBUS_WORD_DATA)) {
|
||||
@ -375,10 +371,8 @@ static int mcp4531_probe(struct i2c_client *client)
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
data->client = client;
|
||||
|
||||
match = of_match_device(of_match_ptr(mcp4531_of_match), dev);
|
||||
if (match)
|
||||
data->cfg = of_device_get_match_data(dev);
|
||||
else
|
||||
data->cfg = of_device_get_match_data(dev);
|
||||
if (!data->cfg)
|
||||
data->cfg = &mcp4531_cfg[i2c_match_id(mcp4531_id, client)->driver_data];
|
||||
|
||||
indio_dev->dev.parent = dev;
|
||||
@ -403,4 +397,4 @@ module_i2c_driver(mcp4531_driver);
|
||||
|
||||
MODULE_AUTHOR("Peter Rosin <peda@axentia.se>");
|
||||
MODULE_DESCRIPTION("MCP4531 digital potentiometer");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
@ -1,12 +1,9 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* MS5611 pressure and temperature sensor driver
|
||||
*
|
||||
* Copyright (c) Tomasz Duszynski <tduszyns@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _MS5611_H
|
||||
|
@ -1,12 +1,9 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* MS5611 pressure and temperature sensor driver
|
||||
*
|
||||
* Copyright (c) Tomasz Duszynski <tduszyns@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Data sheet:
|
||||
* http://www.meas-spec.com/downloads/MS5611-01BA03.pdf
|
||||
* http://www.meas-spec.com/downloads/MS5607-02BA03.pdf
|
||||
|
@ -1,12 +1,9 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* MS5611 pressure and temperature sensor driver (I2C bus)
|
||||
*
|
||||
* Copyright (c) Tomasz Duszynski <tduszyns@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 7-bit I2C slave addresses:
|
||||
*
|
||||
* 0x77 (CSB pin low)
|
||||
@ -117,9 +114,7 @@ static int ms5611_i2c_remove(struct i2c_client *client)
|
||||
#if defined(CONFIG_OF)
|
||||
static const struct of_device_id ms5611_i2c_matches[] = {
|
||||
{ .compatible = "meas,ms5611" },
|
||||
{ .compatible = "ms5611" },
|
||||
{ .compatible = "meas,ms5607" },
|
||||
{ .compatible = "ms5607" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ms5611_i2c_matches);
|
||||
|
@ -1,12 +1,9 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* MS5611 pressure and temperature sensor driver (SPI bus)
|
||||
*
|
||||
* Copyright (c) Tomasz Duszynski <tduszyns@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
@ -119,9 +116,7 @@ static int ms5611_spi_remove(struct spi_device *spi)
|
||||
#if defined(CONFIG_OF)
|
||||
static const struct of_device_id ms5611_spi_matches[] = {
|
||||
{ .compatible = "meas,ms5611" },
|
||||
{ .compatible = "ms5611" },
|
||||
{ .compatible = "meas,ms5607" },
|
||||
{ .compatible = "ms5607" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ms5611_spi_matches);
|
||||
|
@ -92,4 +92,15 @@ config SRF08
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called srf08.
|
||||
|
||||
config VL53L0X_I2C
|
||||
tristate "STMicroelectronics VL53L0X ToF ranger sensor (I2C)"
|
||||
depends on I2C
|
||||
help
|
||||
Say Y here to build a driver for STMicroelectronics VL53L0X
|
||||
ToF ranger sensors with i2c interface.
|
||||
This driver can be used to measure the distance of objects.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called vl53l0x-i2c.
|
||||
|
||||
endmenu
|
||||
|
@ -11,3 +11,5 @@ obj-$(CONFIG_RFD77402) += rfd77402.o
|
||||
obj-$(CONFIG_SRF04) += srf04.o
|
||||
obj-$(CONFIG_SRF08) += srf08.o
|
||||
obj-$(CONFIG_SX9500) += sx9500.o
|
||||
obj-$(CONFIG_VL53L0X_I2C) += vl53l0x-i2c.o
|
||||
|
||||
|
@ -232,7 +232,6 @@ static u32 isl29501_register_write(struct isl29501_private *isl29501,
|
||||
u32 value)
|
||||
{
|
||||
const struct isl29501_register_desc *reg = &isl29501_registers[name];
|
||||
u8 msb, lsb;
|
||||
int ret;
|
||||
|
||||
if (!reg->msb && value > U8_MAX)
|
||||
@ -241,22 +240,15 @@ static u32 isl29501_register_write(struct isl29501_private *isl29501,
|
||||
if (value > U16_MAX)
|
||||
return -ERANGE;
|
||||
|
||||
if (!reg->msb) {
|
||||
lsb = value & 0xFF;
|
||||
} else {
|
||||
msb = (value >> 8) & 0xFF;
|
||||
lsb = value & 0xFF;
|
||||
}
|
||||
|
||||
mutex_lock(&isl29501->lock);
|
||||
if (reg->msb) {
|
||||
ret = i2c_smbus_write_byte_data(isl29501->client,
|
||||
reg->msb, msb);
|
||||
reg->msb, value >> 8);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = i2c_smbus_write_byte_data(isl29501->client, reg->lsb, lsb);
|
||||
ret = i2c_smbus_write_byte_data(isl29501->client, reg->lsb, value);
|
||||
|
||||
err:
|
||||
mutex_unlock(&isl29501->lock);
|
||||
|
164
drivers/iio/proximity/vl53l0x-i2c.c
Normal file
164
drivers/iio/proximity/vl53l0x-i2c.c
Normal file
@ -0,0 +1,164 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Support for ST VL53L0X FlightSense ToF Ranging Sensor on a i2c bus.
|
||||
*
|
||||
* Copyright (C) 2016 STMicroelectronics Imaging Division.
|
||||
* Copyright (C) 2018 Song Qiang <songqiang1304521@gmail.com>
|
||||
*
|
||||
* Datasheet available at
|
||||
* <https://www.st.com/resource/en/datasheet/vl53l0x.pdf>
|
||||
*
|
||||
* Default 7-bit i2c slave address 0x29.
|
||||
*
|
||||
* TODO: FIFO buffer, continuous mode, interrupts, range selection,
|
||||
* sensor ID check.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
|
||||
#define VL_REG_SYSRANGE_START 0x00
|
||||
|
||||
#define VL_REG_SYSRANGE_MODE_MASK GENMASK(3, 0)
|
||||
#define VL_REG_SYSRANGE_MODE_SINGLESHOT 0x00
|
||||
#define VL_REG_SYSRANGE_MODE_START_STOP BIT(0)
|
||||
#define VL_REG_SYSRANGE_MODE_BACKTOBACK BIT(1)
|
||||
#define VL_REG_SYSRANGE_MODE_TIMED BIT(2)
|
||||
#define VL_REG_SYSRANGE_MODE_HISTOGRAM BIT(3)
|
||||
|
||||
#define VL_REG_RESULT_INT_STATUS 0x13
|
||||
#define VL_REG_RESULT_RANGE_STATUS 0x14
|
||||
#define VL_REG_RESULT_RANGE_STATUS_COMPLETE BIT(0)
|
||||
|
||||
struct vl53l0x_data {
|
||||
struct i2c_client *client;
|
||||
};
|
||||
|
||||
static int vl53l0x_read_proximity(struct vl53l0x_data *data,
|
||||
const struct iio_chan_spec *chan,
|
||||
int *val)
|
||||
{
|
||||
struct i2c_client *client = data->client;
|
||||
u16 tries = 20;
|
||||
u8 buffer[12];
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(client, VL_REG_SYSRANGE_START, 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
do {
|
||||
ret = i2c_smbus_read_byte_data(client,
|
||||
VL_REG_RESULT_RANGE_STATUS);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (ret & VL_REG_RESULT_RANGE_STATUS_COMPLETE)
|
||||
break;
|
||||
|
||||
usleep_range(1000, 5000);
|
||||
} while (--tries);
|
||||
if (!tries)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
ret = i2c_smbus_read_i2c_block_data(client, VL_REG_RESULT_RANGE_STATUS,
|
||||
12, buffer);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
else if (ret != 12)
|
||||
return -EREMOTEIO;
|
||||
|
||||
/* Values should be between 30~1200 in millimeters. */
|
||||
*val = (buffer[10] << 8) + buffer[11];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec vl53l0x_channels[] = {
|
||||
{
|
||||
.type = IIO_DISTANCE,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
},
|
||||
};
|
||||
|
||||
static int vl53l0x_read_raw(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct vl53l0x_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
if (chan->type != IIO_DISTANCE)
|
||||
return -EINVAL;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = vl53l0x_read_proximity(data, chan, val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = 0;
|
||||
*val2 = 1000;
|
||||
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct iio_info vl53l0x_info = {
|
||||
.read_raw = vl53l0x_read_raw,
|
||||
};
|
||||
|
||||
static int vl53l0x_probe(struct i2c_client *client)
|
||||
{
|
||||
struct vl53l0x_data *data;
|
||||
struct iio_dev *indio_dev;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
data = iio_priv(indio_dev);
|
||||
data->client = client;
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
|
||||
if (!i2c_check_functionality(client->adapter,
|
||||
I2C_FUNC_SMBUS_READ_I2C_BLOCK |
|
||||
I2C_FUNC_SMBUS_BYTE_DATA))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->name = "vl53l0x";
|
||||
indio_dev->info = &vl53l0x_info;
|
||||
indio_dev->channels = vl53l0x_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(vl53l0x_channels);
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
return devm_iio_device_register(&client->dev, indio_dev);
|
||||
}
|
||||
|
||||
static const struct of_device_id st_vl53l0x_dt_match[] = {
|
||||
{ .compatible = "st,vl53l0x", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, st_vl53l0x_dt_match);
|
||||
|
||||
static struct i2c_driver vl53l0x_driver = {
|
||||
.driver = {
|
||||
.name = "vl53l0x-i2c",
|
||||
.of_match_table = st_vl53l0x_dt_match,
|
||||
},
|
||||
.probe_new = vl53l0x_probe,
|
||||
};
|
||||
module_i2c_driver(vl53l0x_driver);
|
||||
|
||||
MODULE_AUTHOR("Song Qiang <songqiang1304521@gmail.com>");
|
||||
MODULE_DESCRIPTION("ST vl53l0x ToF ranging sensor driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -222,7 +222,7 @@ static void __exit iio_sysfs_trig_exit(void)
|
||||
}
|
||||
module_exit(iio_sysfs_trig_exit);
|
||||
|
||||
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
|
||||
MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
|
||||
MODULE_DESCRIPTION("Sysfs based trigger for the iio subsystem");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:iio-trig-sysfs");
|
||||
|
@ -80,8 +80,6 @@ source "drivers/staging/netlogic/Kconfig"
|
||||
|
||||
source "drivers/staging/mt29f_spinand/Kconfig"
|
||||
|
||||
source "drivers/staging/dgnc/Kconfig"
|
||||
|
||||
source "drivers/staging/gs_fpgaboot/Kconfig"
|
||||
|
||||
source "drivers/staging/unisys/Kconfig"
|
||||
|
@ -29,7 +29,6 @@ obj-$(CONFIG_STAGING_BOARD) += board/
|
||||
obj-$(CONFIG_LTE_GDM724X) += gdm724x/
|
||||
obj-$(CONFIG_FIREWIRE_SERIAL) += fwserial/
|
||||
obj-$(CONFIG_GOLDFISH) += goldfish/
|
||||
obj-$(CONFIG_DGNC) += dgnc/
|
||||
obj-$(CONFIG_MTD_SPINAND_MT29F) += mt29f_spinand/
|
||||
obj-$(CONFIG_GS_FPGABOOT) += gs_fpgaboot/
|
||||
obj-$(CONFIG_UNISYSSPAR) += unisys/
|
||||
|
@ -157,8 +157,6 @@ struct ion_heap_ops {
|
||||
* @lock: protects the free list
|
||||
* @waitqueue: queue to wait on from deferred free thread
|
||||
* @task: task struct of deferred free thread
|
||||
* @debug_show: called when heap debug file is read to add any
|
||||
* heap specific debug info to output
|
||||
*
|
||||
* Represents a pool of memory from which buffers can be made. In some
|
||||
* systems the only heap is regular system memory allocated via vmalloc.
|
||||
@ -179,9 +177,6 @@ struct ion_heap {
|
||||
spinlock_t free_lock;
|
||||
wait_queue_head_t waitqueue;
|
||||
struct task_struct *task;
|
||||
|
||||
int (*debug_show)(struct ion_heap *heap, struct seq_file *s,
|
||||
void *unused);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -212,29 +212,6 @@ static struct ion_heap_ops system_heap_ops = {
|
||||
.shrink = ion_system_heap_shrink,
|
||||
};
|
||||
|
||||
static int ion_system_heap_debug_show(struct ion_heap *heap, struct seq_file *s,
|
||||
void *unused)
|
||||
{
|
||||
struct ion_system_heap *sys_heap = container_of(heap,
|
||||
struct ion_system_heap,
|
||||
heap);
|
||||
int i;
|
||||
struct ion_page_pool *pool;
|
||||
|
||||
for (i = 0; i < NUM_ORDERS; i++) {
|
||||
pool = sys_heap->pools[i];
|
||||
|
||||
seq_printf(s, "%d order %u highmem pages %lu total\n",
|
||||
pool->high_count, pool->order,
|
||||
(PAGE_SIZE << pool->order) * pool->high_count);
|
||||
seq_printf(s, "%d order %u lowmem pages %lu total\n",
|
||||
pool->low_count, pool->order,
|
||||
(PAGE_SIZE << pool->order) * pool->low_count);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ion_system_heap_destroy_pools(struct ion_page_pool **pools)
|
||||
{
|
||||
int i;
|
||||
@ -281,7 +258,6 @@ static struct ion_heap *__ion_system_heap_create(void)
|
||||
if (ion_system_heap_create_pools(heap->pools))
|
||||
goto free_heap;
|
||||
|
||||
heap->heap.debug_show = ion_system_heap_debug_show;
|
||||
return &heap->heap;
|
||||
|
||||
free_heap:
|
||||
|
@ -27,8 +27,6 @@
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/param.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/jiffies.h>
|
||||
@ -364,11 +362,11 @@ static ssize_t axis_fifo_read(struct file *f, char __user *buf,
|
||||
* if nothing is currently available
|
||||
*/
|
||||
spin_lock_irq(&fifo->read_queue_lock);
|
||||
ret = wait_event_interruptible_lock_irq_timeout(
|
||||
fifo->read_queue,
|
||||
ioread32(fifo->base_addr + XLLF_RDFO_OFFSET),
|
||||
fifo->read_queue_lock,
|
||||
(read_timeout >= 0) ? msecs_to_jiffies(read_timeout) :
|
||||
ret = wait_event_interruptible_lock_irq_timeout
|
||||
(fifo->read_queue,
|
||||
ioread32(fifo->base_addr + XLLF_RDFO_OFFSET),
|
||||
fifo->read_queue_lock,
|
||||
(read_timeout >= 0) ? msecs_to_jiffies(read_timeout) :
|
||||
MAX_SCHEDULE_TIMEOUT);
|
||||
spin_unlock_irq(&fifo->read_queue_lock);
|
||||
|
||||
@ -482,12 +480,12 @@ static ssize_t axis_fifo_write(struct file *f, const char __user *buf,
|
||||
* currently enough room in the fifo
|
||||
*/
|
||||
spin_lock_irq(&fifo->write_queue_lock);
|
||||
ret = wait_event_interruptible_lock_irq_timeout(
|
||||
fifo->write_queue,
|
||||
ioread32(fifo->base_addr + XLLF_TDFV_OFFSET)
|
||||
ret = wait_event_interruptible_lock_irq_timeout
|
||||
(fifo->write_queue,
|
||||
ioread32(fifo->base_addr + XLLF_TDFV_OFFSET)
|
||||
>= words_to_write,
|
||||
fifo->write_queue_lock,
|
||||
(write_timeout >= 0) ? msecs_to_jiffies(write_timeout) :
|
||||
fifo->write_queue_lock,
|
||||
(write_timeout >= 0) ? msecs_to_jiffies(write_timeout) :
|
||||
MAX_SCHEDULE_TIMEOUT);
|
||||
spin_unlock_irq(&fifo->write_queue_lock);
|
||||
|
||||
@ -1089,6 +1087,8 @@ static int __init axis_fifo_init(void)
|
||||
pr_info("axis-fifo driver loaded with parameters read_timeout = %i, write_timeout = %i\n",
|
||||
read_timeout, write_timeout);
|
||||
axis_fifo_driver_class = class_create(THIS_MODULE, DRIVER_NAME);
|
||||
if (IS_ERR(axis_fifo_driver_class))
|
||||
return PTR_ERR(axis_fifo_driver_class);
|
||||
return platform_driver_register(&axis_fifo_driver);
|
||||
}
|
||||
|
||||
|
@ -199,10 +199,10 @@ static int clk_wzrd_probe(struct platform_device *pdev)
|
||||
ret = -ENOMEM;
|
||||
goto err_disable_clk;
|
||||
}
|
||||
clk_wzrd->clks_internal[wzrd_clk_mul] = clk_register_fixed_factor(
|
||||
&pdev->dev, clk_name,
|
||||
__clk_get_name(clk_wzrd->clk_in1),
|
||||
0, reg, 1);
|
||||
clk_wzrd->clks_internal[wzrd_clk_mul] = clk_register_fixed_factor
|
||||
(&pdev->dev, clk_name,
|
||||
__clk_get_name(clk_wzrd->clk_in1),
|
||||
0, reg, 1);
|
||||
kfree(clk_name);
|
||||
if (IS_ERR(clk_wzrd->clks_internal[wzrd_clk_mul])) {
|
||||
dev_err(&pdev->dev, "unable to register fixed-factor clock\n");
|
||||
@ -219,10 +219,10 @@ static int clk_wzrd_probe(struct platform_device *pdev)
|
||||
goto err_rm_int_clk;
|
||||
}
|
||||
|
||||
clk_wzrd->clks_internal[wzrd_clk_mul_div] = clk_register_fixed_factor(
|
||||
&pdev->dev, clk_name,
|
||||
__clk_get_name(clk_wzrd->clks_internal[wzrd_clk_mul]),
|
||||
0, 1, reg);
|
||||
clk_wzrd->clks_internal[wzrd_clk_mul_div] = clk_register_fixed_factor
|
||||
(&pdev->dev, clk_name,
|
||||
__clk_get_name(clk_wzrd->clks_internal[wzrd_clk_mul]),
|
||||
0, 1, reg);
|
||||
if (IS_ERR(clk_wzrd->clks_internal[wzrd_clk_mul_div])) {
|
||||
dev_err(&pdev->dev, "unable to register divider clock\n");
|
||||
ret = PTR_ERR(clk_wzrd->clks_internal[wzrd_clk_mul_div]);
|
||||
@ -243,8 +243,8 @@ static int clk_wzrd_probe(struct platform_device *pdev)
|
||||
reg = readl(clk_wzrd->base + WZRD_CLK_CFG_REG(2) + i * 12);
|
||||
reg &= WZRD_CLKOUT_DIVIDE_MASK;
|
||||
reg >>= WZRD_CLKOUT_DIVIDE_SHIFT;
|
||||
clk_wzrd->clkout[i] = clk_register_fixed_factor(&pdev->dev,
|
||||
clkout_name, clk_name, 0, 1, reg);
|
||||
clk_wzrd->clkout[i] = clk_register_fixed_factor
|
||||
(&pdev->dev, clkout_name, clk_name, 0, 1, reg);
|
||||
if (IS_ERR(clk_wzrd->clkout[i])) {
|
||||
int j;
|
||||
|
||||
|
@ -1313,5 +1313,9 @@ config COMEDI_NI_LABPC_ISADMA
|
||||
|
||||
config COMEDI_NI_TIO
|
||||
tristate
|
||||
select COMEDI_NI_ROUTING
|
||||
|
||||
config COMEDI_NI_ROUTING
|
||||
tristate
|
||||
|
||||
endif # COMEDI
|
||||
|
@ -107,6 +107,7 @@
|
||||
#define INSN_WRITE (1 | INSN_MASK_WRITE)
|
||||
#define INSN_BITS (2 | INSN_MASK_READ | INSN_MASK_WRITE)
|
||||
#define INSN_CONFIG (3 | INSN_MASK_READ | INSN_MASK_WRITE)
|
||||
#define INSN_DEVICE_CONFIG (INSN_CONFIG | INSN_MASK_SPECIAL)
|
||||
#define INSN_GTOD (4 | INSN_MASK_READ | INSN_MASK_SPECIAL)
|
||||
#define INSN_WAIT (5 | INSN_MASK_WRITE | INSN_MASK_SPECIAL)
|
||||
#define INSN_INTTRIG (6 | INSN_MASK_WRITE | INSN_MASK_SPECIAL)
|
||||
@ -301,6 +302,8 @@ enum comedi_io_direction {
|
||||
* @INSN_CONFIG_PWM_SET_H_BRIDGE: Set PWM H bridge duty cycle and polarity for
|
||||
* a relay simultaneously.
|
||||
* @INSN_CONFIG_PWM_GET_H_BRIDGE: Get PWM H bridge duty cycle and polarity.
|
||||
* @INSN_CONFIG_GET_CMD_TIMING_CONSTRAINTS: Get the hardware timing restraints,
|
||||
* regardless of trigger sources.
|
||||
*/
|
||||
enum configuration_ids {
|
||||
INSN_CONFIG_DIO_INPUT = COMEDI_INPUT,
|
||||
@ -344,7 +347,25 @@ enum configuration_ids {
|
||||
INSN_CONFIG_PWM_GET_PERIOD = 5001,
|
||||
INSN_CONFIG_GET_PWM_STATUS = 5002,
|
||||
INSN_CONFIG_PWM_SET_H_BRIDGE = 5003,
|
||||
INSN_CONFIG_PWM_GET_H_BRIDGE = 5004
|
||||
INSN_CONFIG_PWM_GET_H_BRIDGE = 5004,
|
||||
INSN_CONFIG_GET_CMD_TIMING_CONSTRAINTS = 5005,
|
||||
};
|
||||
|
||||
/**
|
||||
* enum device_configuration_ids - COMEDI configuration instruction codes global
|
||||
* to an entire device.
|
||||
* @INSN_DEVICE_CONFIG_TEST_ROUTE: Validate the possibility of a
|
||||
* globally-named route
|
||||
* @INSN_DEVICE_CONFIG_CONNECT_ROUTE: Connect a globally-named route
|
||||
* @INSN_DEVICE_CONFIG_DISCONNECT_ROUTE:Disconnect a globally-named route
|
||||
* @INSN_DEVICE_CONFIG_GET_ROUTES: Get a list of all globally-named routes
|
||||
* that are valid for a particular device.
|
||||
*/
|
||||
enum device_config_route_ids {
|
||||
INSN_DEVICE_CONFIG_TEST_ROUTE = 0,
|
||||
INSN_DEVICE_CONFIG_CONNECT_ROUTE = 1,
|
||||
INSN_DEVICE_CONFIG_DISCONNECT_ROUTE = 2,
|
||||
INSN_DEVICE_CONFIG_GET_ROUTES = 3,
|
||||
};
|
||||
|
||||
/**
|
||||
@ -928,6 +949,157 @@ enum i8254_mode {
|
||||
I8254_BINARY = 0
|
||||
};
|
||||
|
||||
/* *** BEGIN GLOBALLY-NAMED NI TERMINALS/SIGNALS *** */
|
||||
|
||||
/*
|
||||
* Common National Instruments Terminal/Signal names.
|
||||
* Some of these have no NI_ prefix as they are useful for non-NI hardware, such
|
||||
* as those that utilize the PXI/RTSI trigger lines.
|
||||
*
|
||||
* NOTE ABOUT THE CHOICE OF NAMES HERE AND THE CAMELSCRIPT:
|
||||
* The choice to use CamelScript and the exact names below is for
|
||||
* maintainability, clarity, similarity to manufacturer's documentation,
|
||||
* _and_ a mitigation for confusion that has plagued the use of these drivers
|
||||
* for years!
|
||||
*
|
||||
* More detail:
|
||||
* There have been significant confusions over the past many years for users
|
||||
* when trying to understand how to connect to/from signals and terminals on
|
||||
* NI hardware using comedi. The major reason for this is that the actual
|
||||
* register values were exposed and required to be used by users. Several
|
||||
* major reasons exist why this caused major confusion for users:
|
||||
* 1) The register values are _NOT_ in user documentation, but rather in
|
||||
* arcane locations, such as a few register programming manuals that are
|
||||
* increasingly hard to find and the NI MHDDK (comments in in example code).
|
||||
* There is no one place to find the various valid values of the registers.
|
||||
* 2) The register values are _NOT_ completely consistent. There is no way to
|
||||
* gain any sense of intuition of which values, or even enums one should use
|
||||
* for various registers. There was some attempt in prior use of comedi to
|
||||
* name enums such that a user might know which enums should be used for
|
||||
* varying purposes, but the end-user had to gain a knowledge of register
|
||||
* values to correctly wield this approach.
|
||||
* 3) The names for signals and registers found in the various register level
|
||||
* programming manuals and vendor-provided documentation are _not_ even
|
||||
* close to the same names that are in the end-user documentation.
|
||||
*
|
||||
* Similar, albeit less, confusion plagued NI's previous version of their own
|
||||
* drivers. Earlier than 2003, NI greatly simplified the situation for users
|
||||
* by releasing a new API that abstracted the names of signals/terminals to a
|
||||
* common and intuitive set of names.
|
||||
*
|
||||
* The names below mirror the names chosen and well documented by NI. These
|
||||
* names are exposed to the user via the comedilib user library. By keeping
|
||||
* the names below, in spite of the use of CamelScript, maintenance will be
|
||||
* greatly eased and confusion for users _and_ comedi developers will be
|
||||
* greatly reduced.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Base of abstracted NI names.
|
||||
* The first 16 bits of *_arg are reserved for channel selection.
|
||||
* Since we only actually need the first 4 or 5 bits for all register values on
|
||||
* NI select registers anyways, we'll identify all values >= (1<<15) as being an
|
||||
* abstracted NI signal/terminal name.
|
||||
* These values are also used/returned by INSN_DEVICE_CONFIG_TEST_ROUTE,
|
||||
* INSN_DEVICE_CONFIG_CONNECT_ROUTE, INSN_DEVICE_CONFIG_DISCONNECT_ROUTE,
|
||||
* and INSN_DEVICE_CONFIG_GET_ROUTES.
|
||||
*/
|
||||
#define NI_NAMES_BASE 0x8000u
|
||||
/*
|
||||
* not necessarily all allowed 64 PFIs are valid--certainly not for all devices
|
||||
*/
|
||||
#define NI_PFI(x) (NI_NAMES_BASE + ((x) & 0x3f))
|
||||
/* 8 trigger lines by standard, Some devices cannot talk to all eight. */
|
||||
#define TRIGGER_LINE(x) (NI_PFI(-1) + 1 + ((x) & 0x7))
|
||||
/* 4 RTSI shared MUXes to route signals to/from TRIGGER_LINES on NI hardware */
|
||||
#define NI_RTSI_BRD(x) (TRIGGER_LINE(-1) + 1 + ((x) & 0x3))
|
||||
|
||||
/* *** Counter/timer names : 8 counters max *** */
|
||||
#define NI_COUNTER_NAMES_BASE (NI_RTSI_BRD(-1) + 1)
|
||||
#define NI_MAX_COUNTERS 7
|
||||
#define NI_CtrSource(x) (NI_COUNTER_NAMES_BASE + ((x) & NI_MAX_COUNTERS))
|
||||
/* Gate, Aux, A,B,Z are all treated, at times as gates */
|
||||
#define NI_GATES_NAMES_BASE (NI_CtrSource(-1) + 1)
|
||||
#define NI_CtrGate(x) (NI_GATES_NAMES_BASE + ((x) & NI_MAX_COUNTERS))
|
||||
#define NI_CtrAux(x) (NI_CtrGate(-1) + 1 + ((x) & NI_MAX_COUNTERS))
|
||||
#define NI_CtrA(x) (NI_CtrAux(-1) + 1 + ((x) & NI_MAX_COUNTERS))
|
||||
#define NI_CtrB(x) (NI_CtrA(-1) + 1 + ((x) & NI_MAX_COUNTERS))
|
||||
#define NI_CtrZ(x) (NI_CtrB(-1) + 1 + ((x) & NI_MAX_COUNTERS))
|
||||
#define NI_GATES_NAMES_MAX NI_CtrZ(-1)
|
||||
#define NI_CtrArmStartTrigger(x) (NI_CtrZ(-1) + 1 + ((x) & NI_MAX_COUNTERS))
|
||||
#define NI_CtrInternalOutput(x) \
|
||||
(NI_CtrArmStartTrigger(-1) + 1 + ((x) & NI_MAX_COUNTERS))
|
||||
/** external pin(s) labeled conveniently as Ctr<i>Out. */
|
||||
#define NI_CtrOut(x) (NI_CtrInternalOutput(-1) + 1 + ((x) & NI_MAX_COUNTERS))
|
||||
/** For Buffered sampling of ctr -- x series capability. */
|
||||
#define NI_CtrSampleClock(x) (NI_CtrOut(-1) + 1 + ((x) & NI_MAX_COUNTERS))
|
||||
#define NI_COUNTER_NAMES_MAX NI_CtrSampleClock(-1)
|
||||
|
||||
enum ni_common_signal_names {
|
||||
/* PXI_Star: this is a non-NI-specific signal */
|
||||
PXI_Star = NI_COUNTER_NAMES_MAX + 1,
|
||||
PXI_Clk10,
|
||||
PXIe_Clk100,
|
||||
NI_AI_SampleClock,
|
||||
NI_AI_SampleClockTimebase,
|
||||
NI_AI_StartTrigger,
|
||||
NI_AI_ReferenceTrigger,
|
||||
NI_AI_ConvertClock,
|
||||
NI_AI_ConvertClockTimebase,
|
||||
NI_AI_PauseTrigger,
|
||||
NI_AI_HoldCompleteEvent,
|
||||
NI_AI_HoldComplete,
|
||||
NI_AI_ExternalMUXClock,
|
||||
NI_AI_STOP, /* pulse signal that occurs when a update is finished(?) */
|
||||
NI_AO_SampleClock,
|
||||
NI_AO_SampleClockTimebase,
|
||||
NI_AO_StartTrigger,
|
||||
NI_AO_PauseTrigger,
|
||||
NI_DI_SampleClock,
|
||||
NI_DI_SampleClockTimebase,
|
||||
NI_DI_StartTrigger,
|
||||
NI_DI_ReferenceTrigger,
|
||||
NI_DI_PauseTrigger,
|
||||
NI_DI_InputBufferFull,
|
||||
NI_DI_ReadyForStartEvent,
|
||||
NI_DI_ReadyForTransferEventBurst,
|
||||
NI_DI_ReadyForTransferEventPipelined,
|
||||
NI_DO_SampleClock,
|
||||
NI_DO_SampleClockTimebase,
|
||||
NI_DO_StartTrigger,
|
||||
NI_DO_PauseTrigger,
|
||||
NI_DO_OutputBufferFull,
|
||||
NI_DO_DataActiveEvent,
|
||||
NI_DO_ReadyForStartEvent,
|
||||
NI_DO_ReadyForTransferEvent,
|
||||
NI_MasterTimebase,
|
||||
NI_20MHzTimebase,
|
||||
NI_80MHzTimebase,
|
||||
NI_100MHzTimebase,
|
||||
NI_200MHzTimebase,
|
||||
NI_100kHzTimebase,
|
||||
NI_10MHzRefClock,
|
||||
NI_FrequencyOutput,
|
||||
NI_ChangeDetectionEvent,
|
||||
NI_AnalogComparisonEvent,
|
||||
NI_WatchdogExpiredEvent,
|
||||
NI_WatchdogExpirationTrigger,
|
||||
NI_SCXI_Trig1,
|
||||
NI_LogicLow,
|
||||
NI_LogicHigh,
|
||||
NI_ExternalStrobe,
|
||||
NI_PFI_DO,
|
||||
NI_CaseGround,
|
||||
/* special internal signal used as variable source for RTSI bus: */
|
||||
NI_RGOUT0,
|
||||
|
||||
/* just a name to make the next more convenient, regardless of above */
|
||||
_NI_NAMES_MAX_PLUS_1,
|
||||
NI_NUM_NAMES = _NI_NAMES_MAX_PLUS_1 - NI_NAMES_BASE,
|
||||
};
|
||||
|
||||
/* *** END GLOBALLY-NAMED NI TERMINALS/SIGNALS *** */
|
||||
|
||||
#define NI_USUAL_PFI_SELECT(x) (((x) < 10) ? (0x1 + (x)) : (0xb + (x)))
|
||||
#define NI_USUAL_RTSI_SELECT(x) (((x) < 7) ? (0xb + (x)) : 0x1b)
|
||||
|
||||
|
@ -1216,6 +1216,10 @@ static int check_insn_config_length(struct comedi_insn *insn,
|
||||
if (insn->n == 6)
|
||||
return 0;
|
||||
break;
|
||||
case INSN_CONFIG_GET_CMD_TIMING_CONSTRAINTS:
|
||||
if (insn->n >= 4)
|
||||
return 0;
|
||||
break;
|
||||
/*
|
||||
* by default we allow the insn since we don't have checks for
|
||||
* all possible cases yet
|
||||
@ -1230,6 +1234,57 @@ static int check_insn_config_length(struct comedi_insn *insn,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int check_insn_device_config_length(struct comedi_insn *insn,
|
||||
unsigned int *data)
|
||||
{
|
||||
if (insn->n < 1)
|
||||
return -EINVAL;
|
||||
|
||||
switch (data[0]) {
|
||||
case INSN_DEVICE_CONFIG_TEST_ROUTE:
|
||||
case INSN_DEVICE_CONFIG_CONNECT_ROUTE:
|
||||
case INSN_DEVICE_CONFIG_DISCONNECT_ROUTE:
|
||||
if (insn->n == 3)
|
||||
return 0;
|
||||
break;
|
||||
case INSN_DEVICE_CONFIG_GET_ROUTES:
|
||||
/*
|
||||
* Big enough for config_id and the length of the userland
|
||||
* memory buffer. Additional length should be in factors of 2
|
||||
* to communicate any returned route pairs (source,destination).
|
||||
*/
|
||||
if (insn->n >= 2)
|
||||
return 0;
|
||||
break;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* get_valid_routes() - Calls low-level driver get_valid_routes function to
|
||||
* either return a count of valid routes to user, or copy
|
||||
* of list of all valid device routes to buffer in
|
||||
* userspace.
|
||||
* @dev: comedi device pointer
|
||||
* @data: data from user insn call. The length of the data must be >= 2.
|
||||
* data[0] must contain the INSN_DEVICE_CONFIG config_id.
|
||||
* data[1](input) contains the number of _pairs_ for which memory is
|
||||
* allotted from the user. If the user specifies '0', then only
|
||||
* the number of pairs available is returned.
|
||||
* data[1](output) returns either the number of pairs available (if none
|
||||
* where requested) or the number of _pairs_ that are copied back
|
||||
* to the user.
|
||||
* data[2::2] returns each (source, destination) pair.
|
||||
*
|
||||
* Return: -EINVAL if low-level driver does not allocate and return routes as
|
||||
* expected. Returns 0 otherwise.
|
||||
*/
|
||||
static int get_valid_routes(struct comedi_device *dev, unsigned int *data)
|
||||
{
|
||||
data[1] = dev->get_valid_routes(dev, data[1], data + 2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_insn(struct comedi_device *dev, struct comedi_insn *insn,
|
||||
unsigned int *data, void *file)
|
||||
{
|
||||
@ -1293,6 +1348,24 @@ static int parse_insn(struct comedi_device *dev, struct comedi_insn *insn,
|
||||
if (ret >= 0)
|
||||
ret = 1;
|
||||
break;
|
||||
case INSN_DEVICE_CONFIG:
|
||||
ret = check_insn_device_config_length(insn, data);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
if (data[0] == INSN_DEVICE_CONFIG_GET_ROUTES) {
|
||||
/*
|
||||
* data[1] should be the number of _pairs_ that
|
||||
* the memory can hold.
|
||||
*/
|
||||
data[1] = (insn->n - 2) / 2;
|
||||
ret = get_valid_routes(dev, data);
|
||||
break;
|
||||
}
|
||||
|
||||
/* other global device config instructions. */
|
||||
ret = dev->insn_device_config(dev, insn, data);
|
||||
break;
|
||||
default:
|
||||
dev_dbg(dev->class_dev, "invalid insn\n");
|
||||
ret = -EINVAL;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user