1
0
Fork 0

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
  ...
hifive-unleashed-5.1
Linus Torvalds 2018-10-29 10:38:10 -07:00
commit 738b04fba1
485 changed files with 44123 additions and 16372 deletions

View File

@ -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

View 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>;
};

View 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>;
};

View File

@ -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>;

View File

@ -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";
};
};

View File

@ -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>;

View 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>;
};

View File

@ -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

View File

@ -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:

View 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>;
};

View 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>;
};

View 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>;
};

View File

@ -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

View File

@ -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.

View File

@ -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).

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View 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, &regval, 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, &regval);
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");

View 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_ */

View 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, &regval);
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");

View 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");

View File

@ -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

View File

@ -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

View File

@ -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");

View File

@ -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");

View File

@ -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");

View File

@ -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");

View File

@ -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");

View File

@ -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");

View File

@ -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);
}

View File

@ -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.
*/
/*

View File

@ -209,12 +209,14 @@ static int mx25_gcq_setup_cfgs(struct platform_device *pdev,
ret = of_property_read_u32(child, "reg", &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;
}

View File

@ -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;
}
}

View 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, &reg, 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");

View File

@ -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, &regval);
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);

View File

@ -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;
}

View 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", &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");

View File

@ -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 ||

View File

@ -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 */

View File

@ -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", &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;
}
}

View File

@ -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:

View File

@ -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

View File

@ -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");

View File

@ -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)

View File

@ -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 */

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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");

View File

@ -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");

View File

@ -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");

View File

@ -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;

View File

@ -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");

View File

@ -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.
*/
/*

View 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 = &ltc1660_read_raw,
.write_raw = &ltc1660_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, &ltc1660_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 = &ltc1660_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 = &ltc1660_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");

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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;
}

View File

@ -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,

View File

@ -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");

View File

@ -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);

View File

@ -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:

View File

@ -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 @@ error_power_off:
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;

View File

@ -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*/

View File

@ -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.

View File

@ -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 */

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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,

View File

@ -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: */

View File

@ -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 @@ prox_poll_err:
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);

View File

@ -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

View File

@ -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>

View File

@ -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;

View File

@ -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");

View File

@ -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");

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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);

View 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");

View File

@ -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");

View File

@ -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"

View File

@ -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/

View File

@ -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);
};
/**

View File

@ -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:

View File

@ -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);
}

View File

@ -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;

View File

@ -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

View File

@ -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)

View File

@ -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