mirror of
https://github.com/torvalds/linux.git
synced 2024-11-10 22:21:40 +00:00
Staging/IIO patches for 5.6-rc1
Here is the big staging/iio driver patches for 5.6-rc1 Included in here are: - lots of new IIO drivers and updates for that subsystem - the usual huge quantity of minor cleanups for staging drivers - removal of the following staging drivers: - isdn/avm - isdn/gigaset - isdn/hysdn - octeon-usb - octeon ethernet Overall we deleted far more lines than we added, removing over 40k of old and obsolete driver code. All of these changes have been in linux-next for a while with no reported issues. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCXjFOKw8cZ3JlZ0Brcm9h aC5jb20ACgkQMUfUDdst+yly3wCfac6fbfrpwZ2VeUFyT5EJFr9JnKEAn1VMQTIJ QCgCqbQemnXfbOXiA5pZ =rP6a -----END PGP SIGNATURE----- Merge tag 'staging-5.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging Pull staging and IIO updates from Greg KH: "Here is the big staging/iio driver patches for 5.6-rc1 Included in here are: - lots of new IIO drivers and updates for that subsystem - the usual huge quantity of minor cleanups for staging drivers - removal of the following staging drivers: - isdn/avm - isdn/gigaset - isdn/hysdn - octeon-usb - octeon ethernet Overall we deleted far more lines than we added, removing over 40k of old and obsolete driver code. All of these changes have been in linux-next for a while with no reported issues" * tag 'staging-5.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging: (353 commits) staging: most: usb: check for NULL device staging: next: configfs: fix release link staging: most: core: fix logging messages staging: most: core: remove container struct staging: most: remove struct device core driver staging: most: core: drop device reference staging: most: remove device from interface structure staging: comedi: drivers: fix spelling mistake "to" -> "too" staging: exfat: remove fs_func struct. staging: wilc1000: avoid mutex unlock without lock in wilc_wlan_handle_txq() staging: wilc1000: return zero on success and non-zero on function failure staging: axis-fifo: replace spinlock with mutex staging: wilc1000: remove unused code prior to throughput enhancement in SPI staging: wilc1000: added 'wilc_' prefix for 'struct assoc_resp' name staging: wilc1000: move firmware API struct's to separate header file staging: wilc1000: remove use of infinite loop conditions staging: kpc2000: rename variables with kpc namespace staging: vt6656: Remove memory buffer from vnt_download_firmware. staging: vt6656: Just check NEWRSR_DECRYPTOK for RX_FLAG_DECRYPTED. staging: vt6656: Use vnt_rx_tail struct for tail variables. ...
This commit is contained in:
commit
7ba31c3f2f
@ -1726,3 +1726,16 @@ Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
List of valid periods (in seconds) for which the light intensity
|
||||
must be above the threshold level before interrupt is asserted.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_filter_notch_center_frequency
|
||||
KernelVersion: 5.5
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Center frequency in Hz for a notch filter. Used i.e. for line
|
||||
noise suppression.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_temp_thermocouple_type
|
||||
KernelVersion: 5.5
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
One of the following thermocouple types: B, E, J, K, N, R, S, T.
|
||||
|
19
Documentation/ABI/testing/sysfs-bus-iio-dma-buffer
Normal file
19
Documentation/ABI/testing/sysfs-bus-iio-dma-buffer
Normal file
@ -0,0 +1,19 @@
|
||||
What: /sys/bus/iio/devices/iio:deviceX/buffer/length_align_bytes
|
||||
KernelVersion: 5.4
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
DMA buffers tend to have a alignment requirement for the
|
||||
buffers. If this alignment requirement is not met samples might
|
||||
be dropped from the buffer.
|
||||
|
||||
This property reports the alignment requirements in bytes.
|
||||
This means that the buffer size in bytes needs to be a integer
|
||||
multiple of the number reported by this file.
|
||||
|
||||
The alignment requirements in number of sample sets will depend
|
||||
on the enabled channels and the bytes per channel. This means
|
||||
that the alignment requirement in samples sets might change
|
||||
depending on which and how many channels are enabled. Whereas
|
||||
the alignment requirement reported in bytes by this property
|
||||
will remain static and does not depend on which channels are
|
||||
enabled.
|
@ -0,0 +1,49 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/accel/adi,adis16240.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: ADIS16240 Programmable Impact Sensor and Recorder driver
|
||||
|
||||
maintainers:
|
||||
- Alexandru Ardelean <alexandru.ardelean@analog.com>
|
||||
|
||||
description: |
|
||||
ADIS16240 Programmable Impact Sensor and Recorder driver that supports
|
||||
SPI interface.
|
||||
https://www.analog.com/en/products/adis16240.html
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- adi,adis16240
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
spi0 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
/* Example for a SPI device node */
|
||||
accelerometer@0 {
|
||||
compatible = "adi,adis16240";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <2500000>;
|
||||
interrupt-parent = <&gpio0>;
|
||||
interrupts = <0 IRQ_TYPE_LEVEL_HIGH>;
|
||||
};
|
||||
};
|
@ -1,11 +1,14 @@
|
||||
* Bosch BMA180 / BMA250 triaxial acceleration sensor
|
||||
* Bosch BMA180 / BMA25x triaxial acceleration sensor
|
||||
|
||||
http://omapworld.com/BMA180_111_1002839.pdf
|
||||
http://ae-bst.resource.bosch.com/media/products/dokumente/bma250/bst-bma250-ds002-05.pdf
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : should be "bosch,bma180" or "bosch,bma250"
|
||||
- compatible : should be one of:
|
||||
"bosch,bma180"
|
||||
"bosch,bma250"
|
||||
"bosch,bma254"
|
||||
- reg : the I2C address of the sensor
|
||||
|
||||
Optional properties:
|
||||
|
@ -0,0 +1,54 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/accel/bosch,bma400.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Bosch BMA400 triaxial acceleration sensor
|
||||
|
||||
maintainers:
|
||||
- Dan Robertson <dan@dlrobertson.com>
|
||||
|
||||
description: |
|
||||
Acceleration and temperature iio sensors with an i2c interface
|
||||
|
||||
Specifications about the sensor can be found at:
|
||||
https://ae-bst.resource.bosch.com/media/_tech/media/datasheets/BST-BMA400-DS000.pdf
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- bosch,bma400
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
vdd-supply:
|
||||
description: phandle to the regulator that provides power to the accelerometer
|
||||
|
||||
vddio-supply:
|
||||
description: phandle to the regulator that provides power to the sensor's IO
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
accelerometer@14 {
|
||||
compatible = "bosch,bma400";
|
||||
reg = <0x14>;
|
||||
vdd-supply = <&vdd>;
|
||||
vddio-supply = <&vddio>;
|
||||
interrupt-parent = <&gpio0>;
|
||||
interrupts = <0 IRQ_TYPE_LEVEL_HIGH>;
|
||||
};
|
||||
};
|
@ -9,9 +9,16 @@ Required properties:
|
||||
"kionix,kxtf9"
|
||||
- reg: i2c slave address
|
||||
|
||||
Optional properties:
|
||||
|
||||
- mount-matrix: an optional 3x3 mounting rotation matrix
|
||||
|
||||
Example:
|
||||
|
||||
kxtf9@f {
|
||||
compatible = "kionix,kxtf9";
|
||||
reg = <0x0F>;
|
||||
mount-matrix = "0", "1", "0",
|
||||
"1", "0", "0",
|
||||
"0", "0", "1";
|
||||
};
|
||||
|
54
Documentation/devicetree/bindings/iio/adc/adi,ad7091r5.yaml
Normal file
54
Documentation/devicetree/bindings/iio/adc/adi,ad7091r5.yaml
Normal file
@ -0,0 +1,54 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/adc/adi,ad7091r5.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Analog Devices AD7091R5 4-Channel 12-Bit ADC
|
||||
|
||||
maintainers:
|
||||
- Beniamin Bia <beniamin.bia@analog.com>
|
||||
|
||||
description: |
|
||||
Analog Devices AD7091R5 4-Channel 12-Bit ADC
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/ad7091r-5.pdf
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- adi,ad7091r5
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
vref-supply:
|
||||
description:
|
||||
Phandle to the vref power supply
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
adc@2f {
|
||||
compatible = "adi,ad7091r5";
|
||||
reg = <0x2f>;
|
||||
|
||||
interrupts = <25 IRQ_TYPE_EDGE_FALLING>;
|
||||
interrupt-parent = <&gpio>;
|
||||
};
|
||||
};
|
||||
...
|
47
Documentation/devicetree/bindings/iio/adc/lltc,ltc2496.yaml
Normal file
47
Documentation/devicetree/bindings/iio/adc/lltc,ltc2496.yaml
Normal file
@ -0,0 +1,47 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/adc/lltc,ltc2496.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Linear Technology / Analog Devices LTC2496 ADC
|
||||
|
||||
maintainers:
|
||||
- Lars-Peter Clausen <lars@metafoo.de>
|
||||
- Michael Hennerich <Michael.Hennerich@analog.com>
|
||||
- Stefan Popa <stefan.popa@analog.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- lltc,ltc2496
|
||||
|
||||
vref-supply:
|
||||
description: phandle to an external regulator providing the reference voltage
|
||||
allOf:
|
||||
- $ref: /schemas/types.yaml#/definitions/phandle
|
||||
|
||||
reg:
|
||||
description: spi chipselect number according to the usual spi bindings
|
||||
|
||||
spi-max-frequency:
|
||||
description: maximal spi bus frequency supported
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- vref-supply
|
||||
- reg
|
||||
|
||||
examples:
|
||||
- |
|
||||
spi {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
adc@0 {
|
||||
compatible = "lltc,ltc2496";
|
||||
reg = <0>;
|
||||
vref-supply = <<c2496_reg>;
|
||||
spi-max-frequency = <2000000>;
|
||||
};
|
||||
};
|
@ -1,13 +0,0 @@
|
||||
Device-Tree bindings for sigma delta modulator
|
||||
|
||||
Required properties:
|
||||
- compatible: should be "ads1201", "sd-modulator". "sd-modulator" can be use
|
||||
as a generic SD modulator if modulator not specified in compatible list.
|
||||
- #io-channel-cells = <0>: See the IIO bindings section "IIO consumers".
|
||||
|
||||
Example node:
|
||||
|
||||
ads1202: adc {
|
||||
compatible = "sd-modulator";
|
||||
#io-channel-cells = <0>;
|
||||
};
|
@ -0,0 +1,37 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/adc/sigma-delta-modulator.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Device-Tree bindings for sigma delta modulator
|
||||
|
||||
maintainers:
|
||||
- Arnaud Pouliquen <arnaud.pouliquen@st.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
description: |
|
||||
"sd-modulator" can be used as a generic SD modulator,
|
||||
if the modulator is not specified in the compatible list.
|
||||
enum:
|
||||
- sd-modulator
|
||||
- ads1201
|
||||
|
||||
'#io-channel-cells':
|
||||
const: 0
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- '#io-channel-cells'
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
ads1202: adc {
|
||||
compatible = "sd-modulator";
|
||||
#io-channel-cells = <0>;
|
||||
};
|
||||
|
||||
...
|
@ -8,6 +8,7 @@ Required properties for the ADIS16480:
|
||||
* "adi,adis16480"
|
||||
* "adi,adis16485"
|
||||
* "adi,adis16488"
|
||||
* "adi,adis16490"
|
||||
* "adi,adis16495-1"
|
||||
* "adi,adis16495-2"
|
||||
* "adi,adis16495-3"
|
||||
|
@ -0,0 +1,51 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/pressure/asc,dlhl60d.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: All Sensors DLH series low voltage digital pressure sensors
|
||||
|
||||
maintainers:
|
||||
- Tomislav Denis <tomislav.denis@avl.com>
|
||||
|
||||
description: |
|
||||
Bindings for the All Sensors DLH series pressure sensors.
|
||||
|
||||
Specifications about the sensors can be found at:
|
||||
http://www.allsensors.com/cad/DS-0355_Rev_B.PDF
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- asc,dlhl60d
|
||||
- asc,dlhl60g
|
||||
|
||||
reg:
|
||||
description: I2C device address
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
description: interrupt mapping for EOC(data ready) pin
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
i2c0 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
pressure@29 {
|
||||
compatible = "asc,dlhl60d";
|
||||
reg = <0x29>;
|
||||
interrupt-parent = <&gpio0>;
|
||||
interrupts = <10 IRQ_TYPE_EDGE_RISING>;
|
||||
};
|
||||
};
|
||||
...
|
@ -0,0 +1,51 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/proximity/parallax-ping.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Parallax PING))) and LaserPING range finder
|
||||
|
||||
maintainers:
|
||||
- Andreas Klinger <ak@it-klinger.de>
|
||||
|
||||
description: |
|
||||
Bit-banging driver using one GPIO:
|
||||
- ping-gpios is raised by the driver to start measurement
|
||||
- direction of ping-gpio is then switched into input with an interrupt
|
||||
for receiving distance value as PWM signal
|
||||
|
||||
Specifications about the devices can be found at:
|
||||
http://parallax.com/sites/default/files/downloads/28041-LaserPING-2m-Rangefinder-Guide.pdf
|
||||
http://parallax.com/sites/default/files/downloads/28015-PING-Documentation-v1.6.pdf
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- parallax,ping
|
||||
- parallax,laserping
|
||||
|
||||
ping-gpios:
|
||||
description:
|
||||
Definition of the GPIO for the triggering and echo (output and input)
|
||||
This GPIO is set for about 5 us by the driver to tell the device it
|
||||
should initiate the measurement cycle. Afterwards the GPIO is switched
|
||||
to input direction with an interrupt. The device sets it and the
|
||||
length of the input signal corresponds to the measured distance.
|
||||
It needs to be an GPIO which is able to deliver an interrupt because
|
||||
the time between two interrupts is measured in the driver.
|
||||
See Documentation/devicetree/bindings/gpio/gpio.txt for information
|
||||
on how to specify a consumer gpio.
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- ping-gpios
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
proximity {
|
||||
compatible = "parallax,laserping";
|
||||
ping-gpios = <&gpio0 26 GPIO_ACTIVE_HIGH>;
|
||||
};
|
@ -5,7 +5,10 @@ Maxim thermocouple support
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: must be "maxim,max31855" or "maxim,max6675"
|
||||
- compatible: must be "maxim,max6675" or one of the following:
|
||||
"maxim,max31855k", "maxim,max31855j", "maxim,max31855n",
|
||||
"maxim,max31855s", "maxim,max31855t", "maxim,max31855e",
|
||||
"maxim,max31855r"; the generic "max,max31855" is deprecated.
|
||||
- reg: SPI chip select number for the device
|
||||
- spi-max-frequency: must be 4300000
|
||||
- spi-cpha: must be defined for max6675 to enable SPI mode 1
|
||||
@ -15,7 +18,7 @@ Required properties:
|
||||
Example:
|
||||
|
||||
max31855@0 {
|
||||
compatible = "maxim,max31855";
|
||||
compatible = "maxim,max31855k";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <4300000>;
|
||||
};
|
||||
|
@ -109,6 +109,8 @@ patternProperties:
|
||||
description: Artesyn Embedded Technologies Inc.
|
||||
"^asahi-kasei,.*":
|
||||
description: Asahi Kasei Corp.
|
||||
"^asc,.*":
|
||||
description: All Sensors Corporation
|
||||
"^aspeed,.*":
|
||||
description: ASPEED Technology Inc.
|
||||
"^asus,.*":
|
||||
@ -717,6 +719,8 @@ patternProperties:
|
||||
description: Panasonic Corporation
|
||||
"^parade,.*":
|
||||
description: Parade Technologies Inc.
|
||||
"^parallax,.*":
|
||||
description: Parallax Inc.
|
||||
"^pda,.*":
|
||||
description: Precision Design Associates, Inc.
|
||||
"^pericom,.*":
|
||||
|
@ -1,246 +0,0 @@
|
||||
================================
|
||||
Driver for active AVM Controller
|
||||
================================
|
||||
|
||||
The driver provides a kernel capi2.0 Interface (kernelcapi) and
|
||||
on top of this a User-Level-CAPI2.0-interface (capi)
|
||||
and a driver to connect isdn4linux with CAPI2.0 (capidrv).
|
||||
The lowlevel interface can be used to implement a CAPI2.0
|
||||
also for passive cards since July 1999.
|
||||
|
||||
The author can be reached at calle@calle.in-berlin.de.
|
||||
The command avmcapictrl is part of the isdn4k-utils.
|
||||
t4-files can be found at ftp://ftp.avm.de/cardware/b1/linux/firmware
|
||||
|
||||
Currently supported cards:
|
||||
|
||||
- B1 ISA (all versions)
|
||||
- B1 PCI
|
||||
- T1/T1B (HEMA card)
|
||||
- M1
|
||||
- M2
|
||||
- B1 PCMCIA
|
||||
|
||||
Installing
|
||||
----------
|
||||
|
||||
You need at least /dev/capi20 to load the firmware.
|
||||
|
||||
::
|
||||
|
||||
mknod /dev/capi20 c 68 0
|
||||
mknod /dev/capi20.00 c 68 1
|
||||
mknod /dev/capi20.01 c 68 2
|
||||
.
|
||||
.
|
||||
.
|
||||
mknod /dev/capi20.19 c 68 20
|
||||
|
||||
Running
|
||||
-------
|
||||
|
||||
To use the card you need the t4-files to download the firmware.
|
||||
AVM GmbH provides several t4-files for the different D-channel
|
||||
protocols (b1.t4 for Euro-ISDN). Install these file in /lib/isdn.
|
||||
|
||||
if you configure as modules load the modules this way::
|
||||
|
||||
insmod /lib/modules/current/misc/capiutil.o
|
||||
insmod /lib/modules/current/misc/b1.o
|
||||
insmod /lib/modules/current/misc/kernelcapi.o
|
||||
insmod /lib/modules/current/misc/capidrv.o
|
||||
insmod /lib/modules/current/misc/capi.o
|
||||
|
||||
if you have an B1-PCI card load the module b1pci.o::
|
||||
|
||||
insmod /lib/modules/current/misc/b1pci.o
|
||||
|
||||
and load the firmware with::
|
||||
|
||||
avmcapictrl load /lib/isdn/b1.t4 1
|
||||
|
||||
if you have an B1-ISA card load the module b1isa.o
|
||||
and add the card by calling::
|
||||
|
||||
avmcapictrl add 0x150 15
|
||||
|
||||
and load the firmware by calling::
|
||||
|
||||
avmcapictrl load /lib/isdn/b1.t4 1
|
||||
|
||||
if you have an T1-ISA card load the module t1isa.o
|
||||
and add the card by calling::
|
||||
|
||||
avmcapictrl add 0x450 15 T1 0
|
||||
|
||||
and load the firmware by calling::
|
||||
|
||||
avmcapictrl load /lib/isdn/t1.t4 1
|
||||
|
||||
if you have an PCMCIA card (B1/M1/M2) load the module b1pcmcia.o
|
||||
before you insert the card.
|
||||
|
||||
Leased Lines with B1
|
||||
--------------------
|
||||
|
||||
Init card and load firmware.
|
||||
|
||||
For an D64S use "FV: 1" as phone number
|
||||
|
||||
For an D64S2 use "FV: 1" and "FV: 2" for multilink
|
||||
or "FV: 1,2" to use CAPI channel bundling.
|
||||
|
||||
/proc-Interface
|
||||
-----------------
|
||||
|
||||
/proc/capi::
|
||||
|
||||
dr-xr-xr-x 2 root root 0 Jul 1 14:03 .
|
||||
dr-xr-xr-x 82 root root 0 Jun 30 19:08 ..
|
||||
-r--r--r-- 1 root root 0 Jul 1 14:03 applications
|
||||
-r--r--r-- 1 root root 0 Jul 1 14:03 applstats
|
||||
-r--r--r-- 1 root root 0 Jul 1 14:03 capi20
|
||||
-r--r--r-- 1 root root 0 Jul 1 14:03 capidrv
|
||||
-r--r--r-- 1 root root 0 Jul 1 14:03 controller
|
||||
-r--r--r-- 1 root root 0 Jul 1 14:03 contrstats
|
||||
-r--r--r-- 1 root root 0 Jul 1 14:03 driver
|
||||
-r--r--r-- 1 root root 0 Jul 1 14:03 ncci
|
||||
-r--r--r-- 1 root root 0 Jul 1 14:03 users
|
||||
|
||||
/proc/capi/applications:
|
||||
applid level3cnt datablkcnt datablklen ncci-cnt recvqueuelen
|
||||
level3cnt:
|
||||
capi_register parameter
|
||||
datablkcnt:
|
||||
capi_register parameter
|
||||
ncci-cnt:
|
||||
current number of nccis (connections)
|
||||
recvqueuelen:
|
||||
number of messages on receive queue
|
||||
|
||||
for example::
|
||||
|
||||
1 -2 16 2048 1 0
|
||||
2 2 7 2048 1 0
|
||||
|
||||
/proc/capi/applstats:
|
||||
applid recvctlmsg nrecvdatamsg nsentctlmsg nsentdatamsg
|
||||
recvctlmsg:
|
||||
capi messages received without DATA_B3_IND
|
||||
recvdatamsg:
|
||||
capi DATA_B3_IND received
|
||||
sentctlmsg:
|
||||
capi messages sent without DATA_B3_REQ
|
||||
sentdatamsg:
|
||||
capi DATA_B3_REQ sent
|
||||
|
||||
for example::
|
||||
|
||||
1 2057 1699 1721 1699
|
||||
|
||||
/proc/capi/capi20: statistics of capi.o (/dev/capi20)
|
||||
minor nopen nrecvdropmsg nrecvctlmsg nrecvdatamsg sentctlmsg sentdatamsg
|
||||
minor:
|
||||
minor device number of capi device
|
||||
nopen:
|
||||
number of calls to devices open
|
||||
nrecvdropmsg:
|
||||
capi messages dropped (messages in recvqueue in close)
|
||||
nrecvctlmsg:
|
||||
capi messages received without DATA_B3_IND
|
||||
nrecvdatamsg:
|
||||
capi DATA_B3_IND received
|
||||
nsentctlmsg:
|
||||
capi messages sent without DATA_B3_REQ
|
||||
nsentdatamsg:
|
||||
capi DATA_B3_REQ sent
|
||||
|
||||
for example::
|
||||
|
||||
1 2 18 0 16 2
|
||||
|
||||
/proc/capi/capidrv: statistics of capidrv.o (capi messages)
|
||||
nrecvctlmsg nrecvdatamsg sentctlmsg sentdatamsg
|
||||
nrecvctlmsg:
|
||||
capi messages received without DATA_B3_IND
|
||||
nrecvdatamsg:
|
||||
capi DATA_B3_IND received
|
||||
nsentctlmsg:
|
||||
capi messages sent without DATA_B3_REQ
|
||||
nsentdatamsg:
|
||||
capi DATA_B3_REQ sent
|
||||
|
||||
for example:
|
||||
2780 2226 2256 2226
|
||||
|
||||
/proc/capi/controller:
|
||||
controller drivername state cardname controllerinfo
|
||||
|
||||
for example::
|
||||
|
||||
1 b1pci running b1pci-e000 B1 3.07-01 0xe000 19
|
||||
2 t1isa running t1isa-450 B1 3.07-01 0x450 11 0
|
||||
3 b1pcmcia running m2-150 B1 3.07-01 0x150 5
|
||||
|
||||
/proc/capi/contrstats:
|
||||
controller nrecvctlmsg nrecvdatamsg sentctlmsg sentdatamsg
|
||||
nrecvctlmsg:
|
||||
capi messages received without DATA_B3_IND
|
||||
nrecvdatamsg:
|
||||
capi DATA_B3_IND received
|
||||
nsentctlmsg:
|
||||
capi messages sent without DATA_B3_REQ
|
||||
nsentdatamsg:
|
||||
capi DATA_B3_REQ sent
|
||||
|
||||
for example::
|
||||
|
||||
1 2845 2272 2310 2274
|
||||
2 2 0 2 0
|
||||
3 2 0 2 0
|
||||
|
||||
/proc/capi/driver:
|
||||
drivername ncontroller
|
||||
|
||||
for example::
|
||||
|
||||
b1pci 1
|
||||
t1isa 1
|
||||
b1pcmcia 1
|
||||
b1isa 0
|
||||
|
||||
/proc/capi/ncci:
|
||||
apllid ncci winsize sendwindow
|
||||
|
||||
for example::
|
||||
|
||||
1 0x10101 8 0
|
||||
|
||||
/proc/capi/users: kernelmodules that use the kernelcapi.
|
||||
name
|
||||
|
||||
for example::
|
||||
|
||||
capidrv
|
||||
capi20
|
||||
|
||||
Questions
|
||||
---------
|
||||
|
||||
Check out the FAQ (ftp.isdn4linux.de) or subscribe to the
|
||||
linux-avmb1@calle.in-berlin.de mailing list by sending
|
||||
a mail to majordomo@calle.in-berlin.de with
|
||||
subscribe linux-avmb1
|
||||
in the body.
|
||||
|
||||
German documentation and several scripts can be found at
|
||||
ftp://ftp.avm.de/cardware/b1/linux/
|
||||
|
||||
Bugs
|
||||
----
|
||||
|
||||
If you find any please let me know.
|
||||
|
||||
Enjoy,
|
||||
|
||||
Carsten Paeth (calle@calle.in-berlin.de)
|
@ -1,465 +0,0 @@
|
||||
==========================
|
||||
GigaSet 307x Device Driver
|
||||
==========================
|
||||
|
||||
1. Requirements
|
||||
=================
|
||||
|
||||
1.1. Hardware
|
||||
-------------
|
||||
|
||||
This driver supports the connection of the Gigaset 307x/417x family of
|
||||
ISDN DECT bases via Gigaset M101 Data, Gigaset M105 Data or direct USB
|
||||
connection. The following devices are reported to be compatible:
|
||||
|
||||
Bases:
|
||||
- Siemens Gigaset 3070/3075 isdn
|
||||
- Siemens Gigaset 4170/4175 isdn
|
||||
- Siemens Gigaset SX205/255
|
||||
- Siemens Gigaset SX353
|
||||
- T-Com Sinus 45 [AB] isdn
|
||||
- T-Com Sinus 721X[A] [SE]
|
||||
- Vox Chicago 390 ISDN (KPN Telecom)
|
||||
|
||||
RS232 data boxes:
|
||||
- Siemens Gigaset M101 Data
|
||||
- T-Com Sinus 45 Data 1
|
||||
|
||||
USB data boxes:
|
||||
- Siemens Gigaset M105 Data
|
||||
- Siemens Gigaset USB Adapter DECT
|
||||
- T-Com Sinus 45 Data 2
|
||||
- T-Com Sinus 721 data
|
||||
- Chicago 390 USB (KPN)
|
||||
|
||||
See also http://www.erbze.info/sinus_gigaset.htm
|
||||
(archived at https://web.archive.org/web/20100717020421/http://www.erbze.info:80/sinus_gigaset.htm ) and
|
||||
http://gigaset307x.sourceforge.net/
|
||||
|
||||
We had also reports from users of Gigaset M105 who could use the drivers
|
||||
with SX 100 and CX 100 ISDN bases (only in unimodem mode, see section 2.5.)
|
||||
If you have another device that works with our driver, please let us know.
|
||||
|
||||
Chances of getting an USB device to work are good if the output of::
|
||||
|
||||
lsusb
|
||||
|
||||
at the command line contains one of the following::
|
||||
|
||||
ID 0681:0001
|
||||
ID 0681:0002
|
||||
ID 0681:0009
|
||||
ID 0681:0021
|
||||
ID 0681:0022
|
||||
|
||||
1.2. Software
|
||||
-------------
|
||||
|
||||
The driver works with the Kernel CAPI subsystem and can be used with any
|
||||
software which is able to use CAPI 2.0 for ISDN connections (voice or data).
|
||||
|
||||
There are some user space tools available at
|
||||
https://sourceforge.net/projects/gigaset307x/
|
||||
which provide access to additional device specific functions like SMS,
|
||||
phonebook or call journal.
|
||||
|
||||
|
||||
2. How to use the driver
|
||||
==========================
|
||||
|
||||
2.1. Modules
|
||||
------------
|
||||
|
||||
For the devices to work, the proper kernel modules have to be loaded.
|
||||
This normally happens automatically when the system detects the USB
|
||||
device (base, M105) or when the line discipline is attached (M101). It
|
||||
can also be triggered manually using the modprobe(8) command, for example
|
||||
for troubleshooting or to pass module parameters.
|
||||
|
||||
The module ser_gigaset provides a serial line discipline N_GIGASET_M101
|
||||
which uses the regular serial port driver to access the device, and must
|
||||
therefore be attached to the serial device to which the M101 is connected.
|
||||
The ldattach(8) command (included in util-linux-ng release 2.14 or later)
|
||||
can be used for that purpose, for example::
|
||||
|
||||
ldattach GIGASET_M101 /dev/ttyS1
|
||||
|
||||
This will open the device file, attach the line discipline to it, and
|
||||
then sleep in the background, keeping the device open so that the line
|
||||
discipline remains active. To deactivate it, kill the daemon, for example
|
||||
with::
|
||||
|
||||
killall ldattach
|
||||
|
||||
before disconnecting the device. To have this happen automatically at
|
||||
system startup/shutdown on an LSB compatible system, create and activate
|
||||
an appropriate LSB startup script /etc/init.d/gigaset. (The init name
|
||||
'gigaset' is officially assigned to this project by LANANA.)
|
||||
Alternatively, just add the 'ldattach' command line to /etc/rc.local.
|
||||
|
||||
The modules accept the following parameters:
|
||||
|
||||
=============== ========== ==========================================
|
||||
Module Parameter Meaning
|
||||
|
||||
gigaset debug debug level (see section 3.2.)
|
||||
|
||||
startmode initial operation mode (see section 2.5.):
|
||||
bas_gigaset ) 1=CAPI (default), 0=Unimodem
|
||||
ser_gigaset )
|
||||
usb_gigaset ) cidmode initial Call-ID mode setting (see section
|
||||
2.5.): 1=on (default), 0=off
|
||||
|
||||
=============== ========== ==========================================
|
||||
|
||||
Depending on your distribution you may want to create a separate module
|
||||
configuration file like /etc/modprobe.d/gigaset.conf for these.
|
||||
|
||||
2.2. Device nodes for user space programs
|
||||
-----------------------------------------
|
||||
|
||||
The device can be accessed from user space (eg. by the user space tools
|
||||
mentioned in 1.2.) through the device nodes:
|
||||
|
||||
- /dev/ttyGS0 for M101 (RS232 data boxes)
|
||||
- /dev/ttyGU0 for M105 (USB data boxes)
|
||||
- /dev/ttyGB0 for the base driver (direct USB connection)
|
||||
|
||||
If you connect more than one device of a type, they will get consecutive
|
||||
device nodes, eg. /dev/ttyGU1 for a second M105.
|
||||
|
||||
You can also set a "default device" for the user space tools to use when
|
||||
no device node is given as parameter, by creating a symlink /dev/ttyG to
|
||||
one of them, eg.::
|
||||
|
||||
ln -s /dev/ttyGB0 /dev/ttyG
|
||||
|
||||
The devices accept the following device specific ioctl calls
|
||||
(defined in gigaset_dev.h):
|
||||
|
||||
``ioctl(int fd, GIGASET_REDIR, int *cmd);``
|
||||
|
||||
If cmd==1, the device is set to be controlled exclusively through the
|
||||
character device node; access from the ISDN subsystem is blocked.
|
||||
|
||||
If cmd==0, the device is set to be used from the ISDN subsystem and does
|
||||
not communicate through the character device node.
|
||||
|
||||
``ioctl(int fd, GIGASET_CONFIG, int *cmd);``
|
||||
|
||||
(ser_gigaset and usb_gigaset only)
|
||||
|
||||
If cmd==1, the device is set to adapter configuration mode where commands
|
||||
are interpreted by the M10x DECT adapter itself instead of being
|
||||
forwarded to the base station. In this mode, the device accepts the
|
||||
commands described in Siemens document "AT-Kommando Alignment M10x Data"
|
||||
for setting the operation mode, associating with a base station and
|
||||
querying parameters like field strengh and signal quality.
|
||||
|
||||
Note that there is no ioctl command for leaving adapter configuration
|
||||
mode and returning to regular operation. In order to leave adapter
|
||||
configuration mode, write the command ATO to the device.
|
||||
|
||||
``ioctl(int fd, GIGASET_BRKCHARS, unsigned char brkchars[6]);``
|
||||
|
||||
(usb_gigaset only)
|
||||
|
||||
Set the break characters on an M105's internal serial adapter to the six
|
||||
bytes stored in brkchars[]. Unused bytes should be set to zero.
|
||||
|
||||
ioctl(int fd, GIGASET_VERSION, unsigned version[4]);
|
||||
Retrieve version information from the driver. version[0] must be set to
|
||||
one of:
|
||||
|
||||
- GIGVER_DRIVER: retrieve driver version
|
||||
- GIGVER_COMPAT: retrieve interface compatibility version
|
||||
- GIGVER_FWBASE: retrieve the firmware version of the base
|
||||
|
||||
Upon return, version[] is filled with the requested version information.
|
||||
|
||||
2.3. CAPI
|
||||
---------
|
||||
|
||||
The devices will show up as CAPI controllers as soon as the
|
||||
corresponding driver module is loaded, and can then be used with
|
||||
CAPI 2.0 kernel and user space applications. For user space access,
|
||||
the module capi.ko must be loaded.
|
||||
|
||||
Most distributions handle loading and unloading of the various CAPI
|
||||
modules automatically via the command capiinit(1) from the capi4k-utils
|
||||
package or a similar mechanism. Note that capiinit(1) cannot unload the
|
||||
Gigaset drivers because it doesn't support more than one module per
|
||||
driver.
|
||||
|
||||
2.5. Unimodem mode
|
||||
------------------
|
||||
|
||||
In this mode the device works like a modem connected to a serial port
|
||||
(the /dev/ttyGU0, ... mentioned above) which understands the commands::
|
||||
|
||||
ATZ init, reset
|
||||
=> OK or ERROR
|
||||
ATD
|
||||
ATDT dial
|
||||
=> OK, CONNECT,
|
||||
BUSY,
|
||||
NO DIAL TONE,
|
||||
NO CARRIER,
|
||||
NO ANSWER
|
||||
<pause>+++<pause> change to command mode when connected
|
||||
ATH hangup
|
||||
|
||||
You can use some configuration tool of your distribution to configure this
|
||||
"modem" or configure pppd/wvdial manually. There are some example ppp
|
||||
configuration files and chat scripts in the gigaset-VERSION/ppp directory
|
||||
in the driver packages from https://sourceforge.net/projects/gigaset307x/.
|
||||
Please note that the USB drivers are not able to change the state of the
|
||||
control lines. This means you must use "Stupid Mode" if you are using
|
||||
wvdial or you should use the nocrtscts option of pppd.
|
||||
You must also assure that the ppp_async module is loaded with the parameter
|
||||
flag_time=0. You can do this e.g. by adding a line like::
|
||||
|
||||
options ppp_async flag_time=0
|
||||
|
||||
to an appropriate module configuration file, like::
|
||||
|
||||
/etc/modprobe.d/gigaset.conf.
|
||||
|
||||
Unimodem mode is needed for making some devices [e.g. SX100] work which
|
||||
do not support the regular Gigaset command set. If debug output (see
|
||||
section 3.2.) shows something like this when dialing::
|
||||
|
||||
CMD Received: ERROR
|
||||
Available Params: 0
|
||||
Connection State: 0, Response: -1
|
||||
gigaset_process_response: resp_code -1 in ConState 0 !
|
||||
Timeout occurred
|
||||
|
||||
then switching to unimodem mode may help.
|
||||
|
||||
If you have installed the command line tool gigacontr, you can enter
|
||||
unimodem mode using::
|
||||
|
||||
gigacontr --mode unimodem
|
||||
|
||||
You can switch back using::
|
||||
|
||||
gigacontr --mode isdn
|
||||
|
||||
You can also put the driver directly into Unimodem mode when it's loaded,
|
||||
by passing the module parameter startmode=0 to the hardware specific
|
||||
module, e.g.::
|
||||
|
||||
modprobe usb_gigaset startmode=0
|
||||
|
||||
or by adding a line like::
|
||||
|
||||
options usb_gigaset startmode=0
|
||||
|
||||
to an appropriate module configuration file, like::
|
||||
|
||||
/etc/modprobe.d/gigaset.conf
|
||||
|
||||
2.6. Call-ID (CID) mode
|
||||
-----------------------
|
||||
|
||||
Call-IDs are numbers used to tag commands to, and responses from, the
|
||||
Gigaset base in order to support the simultaneous handling of multiple
|
||||
ISDN calls. Their use can be enabled ("CID mode") or disabled ("Unimodem
|
||||
mode"). Without Call-IDs (in Unimodem mode), only a very limited set of
|
||||
functions is available. It allows outgoing data connections only, but
|
||||
does not signal incoming calls or other base events.
|
||||
|
||||
DECT cordless data devices (M10x) permanently occupy the cordless
|
||||
connection to the base while Call-IDs are activated. As the Gigaset
|
||||
bases only support one DECT data connection at a time, this prevents
|
||||
other DECT cordless data devices from accessing the base.
|
||||
|
||||
During active operation, the driver switches to the necessary mode
|
||||
automatically. However, for the reasons above, the mode chosen when
|
||||
the device is not in use (idle) can be selected by the user.
|
||||
|
||||
- If you want to receive incoming calls, you can use the default
|
||||
settings (CID mode).
|
||||
- If you have several DECT data devices (M10x) which you want to use
|
||||
in turn, select Unimodem mode by passing the parameter "cidmode=0" to
|
||||
the appropriate driver module (ser_gigaset or usb_gigaset).
|
||||
|
||||
If you want both of these at once, you are out of luck.
|
||||
|
||||
You can also use the tty class parameter "cidmode" of the device to
|
||||
change its CID mode while the driver is loaded, eg.::
|
||||
|
||||
echo 0 > /sys/class/tty/ttyGU0/cidmode
|
||||
|
||||
2.7. Dialing Numbers
|
||||
--------------------
|
||||
provided by an application for dialing out must
|
||||
be a public network number according to the local dialing plan, without
|
||||
any dial prefix for getting an outside line.
|
||||
|
||||
Internal calls can be made by providing an internal extension number
|
||||
prefixed with ``**`` (two asterisks) as the called party number. So to dial
|
||||
eg. the first registered DECT handset, give ``**11`` as the called party
|
||||
number. Dialing ``***`` (three asterisks) calls all extensions
|
||||
simultaneously (global call).
|
||||
|
||||
Unimodem mode does not support internal calls.
|
||||
|
||||
2.8. Unregistered Wireless Devices (M101/M105)
|
||||
----------------------------------------------
|
||||
|
||||
The main purpose of the ser_gigaset and usb_gigaset drivers is to allow
|
||||
the M101 and M105 wireless devices to be used as ISDN devices for ISDN
|
||||
connections through a Gigaset base. Therefore they assume that the device
|
||||
is registered to a DECT base.
|
||||
|
||||
If the M101/M105 device is not registered to a base, initialization of
|
||||
the device fails, and a corresponding error message is logged by the
|
||||
driver. In that situation, a restricted set of functions is available
|
||||
which includes, in particular, those necessary for registering the device
|
||||
to a base or for switching it between Fixed Part and Portable Part
|
||||
modes. See the gigacontr(8) manpage for details.
|
||||
|
||||
3. Troubleshooting
|
||||
====================
|
||||
|
||||
3.1. Solutions to frequently reported problems
|
||||
----------------------------------------------
|
||||
|
||||
Problem:
|
||||
You have a slow provider and isdn4linux gives up dialing too early.
|
||||
Solution:
|
||||
Load the isdn module using the dialtimeout option. You can do this e.g.
|
||||
by adding a line like::
|
||||
|
||||
options isdn dialtimeout=15
|
||||
|
||||
to /etc/modprobe.d/gigaset.conf or a similar file.
|
||||
|
||||
Problem:
|
||||
The isdnlog program emits error messages or just doesn't work.
|
||||
Solution:
|
||||
Isdnlog supports only the HiSax driver. Do not attempt to use it with
|
||||
other drivers such as Gigaset.
|
||||
|
||||
Problem:
|
||||
You have two or more DECT data adapters (M101/M105) and only the
|
||||
first one you turn on works.
|
||||
Solution:
|
||||
Select Unimodem mode for all DECT data adapters. (see section 2.5.)
|
||||
|
||||
Problem:
|
||||
Messages like this::
|
||||
|
||||
usb_gigaset 3-2:1.0: Could not initialize the device.
|
||||
|
||||
appear in your syslog.
|
||||
Solution:
|
||||
Check whether your M10x wireless device is correctly registered to the
|
||||
Gigaset base. (see section 2.7.)
|
||||
|
||||
3.2. Telling the driver to provide more information
|
||||
---------------------------------------------------
|
||||
Building the driver with the "Gigaset debugging" kernel configuration
|
||||
option (CONFIG_GIGASET_DEBUG) gives it the ability to produce additional
|
||||
information useful for debugging.
|
||||
|
||||
You can control the amount of debugging information the driver produces by
|
||||
writing an appropriate value to /sys/module/gigaset/parameters/debug,
|
||||
e.g.::
|
||||
|
||||
echo 0 > /sys/module/gigaset/parameters/debug
|
||||
|
||||
switches off debugging output completely,
|
||||
|
||||
::
|
||||
|
||||
echo 0x302020 > /sys/module/gigaset/parameters/debug
|
||||
|
||||
enables a reasonable set of debugging output messages. These values are
|
||||
bit patterns where every bit controls a certain type of debugging output.
|
||||
See the constants DEBUG_* in the source file gigaset.h for details.
|
||||
|
||||
The initial value can be set using the debug parameter when loading the
|
||||
module "gigaset", e.g. by adding a line::
|
||||
|
||||
options gigaset debug=0
|
||||
|
||||
to your module configuration file, eg. /etc/modprobe.d/gigaset.conf
|
||||
|
||||
Generated debugging information can be found
|
||||
- as output of the command::
|
||||
|
||||
dmesg
|
||||
|
||||
- in system log files written by your syslog daemon, usually
|
||||
in /var/log/, e.g. /var/log/messages.
|
||||
|
||||
3.3. Reporting problems and bugs
|
||||
--------------------------------
|
||||
If you can't solve problems with the driver on your own, feel free to
|
||||
use one of the forums, bug trackers, or mailing lists on
|
||||
|
||||
https://sourceforge.net/projects/gigaset307x
|
||||
|
||||
or write an electronic mail to the maintainers.
|
||||
|
||||
Try to provide as much information as possible, such as
|
||||
|
||||
- distribution
|
||||
- kernel version (uname -r)
|
||||
- gcc version (gcc --version)
|
||||
- hardware architecture (uname -m, ...)
|
||||
- type and firmware version of your device (base and wireless module,
|
||||
if any)
|
||||
- output of "lsusb -v" (if using an USB device)
|
||||
- error messages
|
||||
- relevant system log messages (it would help if you activate debug
|
||||
output as described in 3.2.)
|
||||
|
||||
For help with general configuration problems not specific to our driver,
|
||||
such as isdn4linux and network configuration issues, please refer to the
|
||||
appropriate forums and newsgroups.
|
||||
|
||||
3.4. Reporting problem solutions
|
||||
--------------------------------
|
||||
If you solved a problem with our drivers, wrote startup scripts for your
|
||||
distribution, ... feel free to contact us (using one of the places
|
||||
mentioned in 3.3.). We'd like to add scripts, hints, documentation
|
||||
to the driver and/or the project web page.
|
||||
|
||||
|
||||
4. Links, other software
|
||||
==========================
|
||||
|
||||
- Sourceforge project developing this driver and associated tools
|
||||
https://sourceforge.net/projects/gigaset307x
|
||||
- Yahoo! Group on the Siemens Gigaset family of devices
|
||||
https://de.groups.yahoo.com/group/Siemens-Gigaset
|
||||
- Siemens Gigaset/T-Sinus compatibility table
|
||||
http://www.erbze.info/sinus_gigaset.htm
|
||||
(archived at https://web.archive.org/web/20100717020421/http://www.erbze.info:80/sinus_gigaset.htm )
|
||||
|
||||
|
||||
5. Credits
|
||||
============
|
||||
|
||||
Thanks to
|
||||
|
||||
Karsten Keil
|
||||
for his help with isdn4linux
|
||||
Deti Fliegl
|
||||
for his base driver code
|
||||
Dennis Dietrich
|
||||
for his kernel 2.6 patches
|
||||
Andreas Rummel
|
||||
for his work and logs to get unimodem mode working
|
||||
Andreas Degert
|
||||
for his logs and patches to get cx 100 working
|
||||
Dietrich Feist
|
||||
for his generous donation of one M105 and two M101 cordless adapters
|
||||
Christoph Schweers
|
||||
for his generous donation of a M34 device
|
||||
|
||||
and all the other people who sent logs and other information.
|
@ -1,196 +0,0 @@
|
||||
============
|
||||
Hysdn Driver
|
||||
============
|
||||
|
||||
The hysdn driver has been written by
|
||||
Werner Cornelius (werner@isdn4linux.de or werner@titro.de)
|
||||
for Hypercope GmbH Aachen Germany. Hypercope agreed to publish this driver
|
||||
under the GNU General Public License.
|
||||
|
||||
The CAPI 2.0-support was added by Ulrich Albrecht (ualbrecht@hypercope.de)
|
||||
for Hypercope GmbH Aachen, Germany.
|
||||
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
.. Table of contents
|
||||
|
||||
1. About the driver
|
||||
|
||||
2. Loading/Unloading the driver
|
||||
|
||||
3. Entries in the /proc filesystem
|
||||
|
||||
4. The /proc/net/hysdn/cardconfX file
|
||||
|
||||
5. The /proc/net/hysdn/cardlogX file
|
||||
|
||||
6. Where to get additional info and help
|
||||
|
||||
|
||||
1. About the driver
|
||||
===================
|
||||
|
||||
The drivers/isdn/hysdn subdir contains a driver for HYPERCOPEs active
|
||||
PCI isdn cards Champ, Ergo and Metro. To enable support for this cards
|
||||
enable ISDN support in the kernel config and support for HYSDN cards in
|
||||
the active cards submenu. The driver may only be compiled and used if
|
||||
support for loadable modules and the process filesystem have been enabled.
|
||||
|
||||
These cards provide two different interfaces to the kernel. Without the
|
||||
optional CAPI 2.0 support, they register as ethernet card. IP-routing
|
||||
to a ISDN-destination is performed on the card itself. All necessary
|
||||
handlers for various protocols like ppp and others as well as config info
|
||||
and firmware may be fetched from Hypercopes WWW-Site www.hypercope.de.
|
||||
|
||||
With CAPI 2.0 support enabled, the card can also be used as a CAPI 2.0
|
||||
compliant devices with either CAPI 2.0 applications
|
||||
(check isdn4k-utils) or -using the capidrv module- as a regular
|
||||
isdn4linux device. This is done via the same mechanism as with the
|
||||
active AVM cards and in fact uses the same module.
|
||||
|
||||
|
||||
2. Loading/Unloading the driver
|
||||
===============================
|
||||
|
||||
The module has no command line parameters and auto detects up to 10 cards
|
||||
in the id-range 0-9.
|
||||
If a loaded driver shall be unloaded all open files in the /proc/net/hysdn
|
||||
subdir need to be closed and all ethernet interfaces allocated by this
|
||||
driver must be shut down. Otherwise the module counter will avoid a module
|
||||
unload.
|
||||
|
||||
If you are using the CAPI 2.0-interface, make sure to load/modprobe the
|
||||
kernelcapi-module first.
|
||||
|
||||
If you plan to use the capidrv-link to isdn4linux, make sure to load
|
||||
capidrv.o after all modules using this driver (i.e. after hysdn and
|
||||
any avm-specific modules).
|
||||
|
||||
3. Entries in the /proc filesystem
|
||||
==================================
|
||||
|
||||
When the module has been loaded it adds the directory hysdn in the
|
||||
/proc/net tree. This directory contains exactly 2 file entries for each
|
||||
card. One is called cardconfX and the other cardlogX, where X is the
|
||||
card id number from 0 to 9.
|
||||
The cards are numbered in the order found in the PCI config data.
|
||||
|
||||
4. The /proc/net/hysdn/cardconfX file
|
||||
=====================================
|
||||
|
||||
This file may be read to get by everyone to get info about the cards type,
|
||||
actual state, available features and used resources.
|
||||
The first 3 entries (id, bus and slot) are PCI info fields, the following
|
||||
type field gives the information about the cards type:
|
||||
|
||||
- 4 -> Ergo card (server card with 2 b-chans)
|
||||
- 5 -> Metro card (server card with 4 or 8 b-chans)
|
||||
- 6 -> Champ card (client card with 2 b-chans)
|
||||
|
||||
The following 3 fields show the hardware assignments for irq, iobase and the
|
||||
dual ported memory (dp-mem).
|
||||
|
||||
The fields b-chans and fax-chans announce the available card resources of
|
||||
this types for the user.
|
||||
|
||||
The state variable indicates the actual drivers state for this card with the
|
||||
following assignments.
|
||||
|
||||
- 0 -> card has not been booted since driver load
|
||||
- 1 -> card booting is actually in progess
|
||||
- 2 -> card is in an error state due to a previous boot failure
|
||||
- 3 -> card is booted and active
|
||||
|
||||
And the last field (device) shows the name of the ethernet device assigned
|
||||
to this card. Up to the first successful boot this field only shows a -
|
||||
to tell that no net device has been allocated up to now. Once a net device
|
||||
has been allocated it remains assigned to this card, even if a card is
|
||||
rebooted and an boot error occurs.
|
||||
|
||||
Writing to the cardconfX file boots the card or transfers config lines to
|
||||
the cards firmware. The type of data is automatically detected when the
|
||||
first data is written. Only root has write access to this file.
|
||||
The firmware boot files are normally called hyclient.pof for client cards
|
||||
and hyserver.pof for server cards.
|
||||
After successfully writing the boot file, complete config files or single
|
||||
config lines may be copied to this file.
|
||||
If an error occurs the return value given to the writing process has the
|
||||
following additional codes (decimal):
|
||||
|
||||
==== ============================================
|
||||
1000 Another process is currently bootng the card
|
||||
1001 Invalid firmware header
|
||||
1002 Boards dual-port RAM test failed
|
||||
1003 Internal firmware handler error
|
||||
1004 Boot image size invalid
|
||||
1005 First boot stage (bootstrap loader) failed
|
||||
1006 Second boot stage failure
|
||||
1007 Timeout waiting for card ready during boot
|
||||
1008 Operation only allowed in booted state
|
||||
1009 Config line too long
|
||||
1010 Invalid channel number
|
||||
1011 Timeout sending config data
|
||||
==== ============================================
|
||||
|
||||
Additional info about error reasons may be fetched from the log output.
|
||||
|
||||
5. The /proc/net/hysdn/cardlogX file
|
||||
====================================
|
||||
|
||||
The cardlogX file entry may be opened multiple for reading by everyone to
|
||||
get the cards and drivers log data. Card messages always start with the
|
||||
keyword LOG. All other lines are output from the driver.
|
||||
The driver log data may be redirected to the syslog by selecting the
|
||||
appropriate bitmask. The cards log messages will always be send to this
|
||||
interface but never to the syslog.
|
||||
|
||||
A root user may write a decimal or hex (with 0x) value t this file to select
|
||||
desired output options. As mentioned above the cards log dat is always
|
||||
written to the cardlog file independent of the following options only used
|
||||
to check and debug the driver itself:
|
||||
|
||||
For example::
|
||||
|
||||
echo "0x34560078" > /proc/net/hysdn/cardlog0
|
||||
|
||||
to output the hex log mask 34560078 for card 0.
|
||||
|
||||
The written value is regarded as an unsigned 32-Bit value, bit ored for
|
||||
desired output. The following bits are already assigned:
|
||||
|
||||
========== ============================================================
|
||||
0x80000000 All driver log data is alternatively via syslog
|
||||
0x00000001 Log memory allocation errors
|
||||
0x00000010 Firmware load start and close are logged
|
||||
0x00000020 Log firmware record parser
|
||||
0x00000040 Log every firmware write actions
|
||||
0x00000080 Log all card related boot messages
|
||||
0x00000100 Output all config data sent for debugging purposes
|
||||
0x00000200 Only non comment config lines are shown wth channel
|
||||
0x00000400 Additional conf log output
|
||||
0x00001000 Log the asynchronous scheduler actions (config and log)
|
||||
0x00100000 Log all open and close actions to /proc/net/hysdn/card files
|
||||
0x00200000 Log all actions from /proc file entries
|
||||
0x00010000 Log network interface init and deinit
|
||||
========== ============================================================
|
||||
|
||||
6. Where to get additional info and help
|
||||
========================================
|
||||
|
||||
If you have any problems concerning the driver or configuration contact
|
||||
the Hypercope support team (support@hypercope.de) and or the authors
|
||||
Werner Cornelius (werner@isdn4linux or cornelius@titro.de) or
|
||||
Ulrich Albrecht (ualbrecht@hypercope.de).
|
@ -9,9 +9,6 @@ ISDN
|
||||
|
||||
interface_capi
|
||||
|
||||
avmb1
|
||||
gigaset
|
||||
hysdn
|
||||
m_isdn
|
||||
|
||||
credits
|
||||
|
@ -26,13 +26,6 @@ This standard is freely available from https://www.capi.org.
|
||||
2. Driver and Device Registration
|
||||
=================================
|
||||
|
||||
CAPI drivers optionally register themselves with Kernel CAPI by calling the
|
||||
Kernel CAPI function register_capi_driver() with a pointer to a struct
|
||||
capi_driver. This structure must be filled with the name and revision of the
|
||||
driver, and optionally a pointer to a callback function, add_card(). The
|
||||
registration can be revoked by calling the function unregister_capi_driver()
|
||||
with a pointer to the same struct capi_driver.
|
||||
|
||||
CAPI drivers must register each of the ISDN devices they control with Kernel
|
||||
CAPI by calling the Kernel CAPI function attach_capi_ctr() with a pointer to a
|
||||
struct capi_ctr before they can be used. This structure must be filled with
|
||||
@ -89,9 +82,6 @@ register_capi_driver():
|
||||
the name of the driver, as a zero-terminated ASCII string
|
||||
``char revision[32]``
|
||||
the revision number of the driver, as a zero-terminated ASCII string
|
||||
``int (*add_card)(struct capi_driver *driver, capicardparams *data)``
|
||||
a callback function pointer (may be NULL)
|
||||
|
||||
|
||||
4.2 struct capi_ctr
|
||||
-------------------
|
||||
@ -178,12 +168,6 @@ to be set by the driver before calling attach_capi_ctr():
|
||||
pointer to a callback function returning the entry for the device in
|
||||
the CAPI controller info table, /proc/capi/controller
|
||||
|
||||
``const struct file_operations *proc_fops``
|
||||
pointers to callback functions for the device's proc file
|
||||
system entry, /proc/capi/controllers/<n>; pointer to the device's
|
||||
capi_ctr structure is available from struct proc_dir_entry::data
|
||||
which is available from struct inode.
|
||||
|
||||
Note:
|
||||
Callback functions except send_message() are never called in interrupt
|
||||
context.
|
||||
@ -267,25 +251,10 @@ _cmstruct alternative representation for CAPI parameters of type 'struct'
|
||||
_cmsg structure members.
|
||||
=========== =================================================================
|
||||
|
||||
Functions capi_cmsg2message() and capi_message2cmsg() are provided to convert
|
||||
messages between their transport encoding described in the CAPI 2.0 standard
|
||||
and their _cmsg structure representation. Note that capi_cmsg2message() does
|
||||
not know or check the size of its destination buffer. The caller must make
|
||||
sure it is big enough to accommodate the resulting CAPI message.
|
||||
|
||||
|
||||
5. Lower Layer Interface Functions
|
||||
==================================
|
||||
|
||||
(declared in <linux/isdn/capilli.h>)
|
||||
|
||||
::
|
||||
|
||||
void register_capi_driver(struct capi_driver *drvr)
|
||||
void unregister_capi_driver(struct capi_driver *drvr)
|
||||
|
||||
register/unregister a driver with Kernel CAPI
|
||||
|
||||
::
|
||||
|
||||
int attach_capi_ctr(struct capi_ctr *ctrlr)
|
||||
@ -300,13 +269,6 @@ register/unregister a device (controller) with Kernel CAPI
|
||||
|
||||
signal controller ready/not ready
|
||||
|
||||
::
|
||||
|
||||
void capi_ctr_suspend_output(struct capi_ctr *ctrlr)
|
||||
void capi_ctr_resume_output(struct capi_ctr *ctrlr)
|
||||
|
||||
signal suspend/resume
|
||||
|
||||
::
|
||||
|
||||
void capi_ctr_handle_message(struct capi_ctr * ctrlr, u16 applid,
|
||||
@ -319,21 +281,6 @@ for forwarding to the specified application
|
||||
6. Helper Functions and Macros
|
||||
==============================
|
||||
|
||||
Library functions (from <linux/isdn/capilli.h>):
|
||||
|
||||
::
|
||||
|
||||
void capilib_new_ncci(struct list_head *head, u16 applid,
|
||||
u32 ncci, u32 winsize)
|
||||
void capilib_free_ncci(struct list_head *head, u16 applid, u32 ncci)
|
||||
void capilib_release_appl(struct list_head *head, u16 applid)
|
||||
void capilib_release(struct list_head *head)
|
||||
void capilib_data_b3_conf(struct list_head *head, u16 applid,
|
||||
u32 ncci, u16 msgid)
|
||||
u16 capilib_data_b3_req(struct list_head *head, u16 applid,
|
||||
u32 ncci, u16 msgid)
|
||||
|
||||
|
||||
Macros to extract/set element values from/in a CAPI message header
|
||||
(from <linux/isdn/capiutil.h>):
|
||||
|
||||
@ -357,24 +304,6 @@ CAPIMSG_DATALEN(m) CAPIMSG_SETDATALEN(m, len) Data Length (u16)
|
||||
Library functions for working with _cmsg structures
|
||||
(from <linux/isdn/capiutil.h>):
|
||||
|
||||
``unsigned capi_cmsg2message(_cmsg *cmsg, u8 *msg)``
|
||||
Assembles a CAPI 2.0 message from the parameters in ``*cmsg``,
|
||||
storing the result in ``*msg``.
|
||||
|
||||
``unsigned capi_message2cmsg(_cmsg *cmsg, u8 *msg)``
|
||||
Disassembles the CAPI 2.0 message in ``*msg``, storing the parameters
|
||||
in ``*cmsg``.
|
||||
|
||||
``unsigned capi_cmsg_header(_cmsg *cmsg, u16 ApplId, u8 Command, u8 Subcommand, u16 Messagenumber, u32 Controller)``
|
||||
Fills the header part and address field of the _cmsg structure ``*cmsg``
|
||||
with the given values, zeroing the remainder of the structure so only
|
||||
parameters with non-default values need to be changed before sending
|
||||
the message.
|
||||
|
||||
``void capi_cmsg_answer(_cmsg *cmsg)``
|
||||
Sets the low bit of the Subcommand field in ``*cmsg``, thereby
|
||||
converting ``_REQ`` to ``_CONF`` and ``_IND`` to ``_RESP``.
|
||||
|
||||
``char *capi_cmd2str(u8 Command, u8 Subcommand)``
|
||||
Returns the CAPI 2.0 message name corresponding to the given command
|
||||
and subcommand values, as a static ASCII string. The return value may
|
||||
|
@ -132,7 +132,6 @@ Code Seq# Include File Comments
|
||||
'F' 80-8F linux/arcfb.h conflict!
|
||||
'F' DD video/sstfb.h conflict!
|
||||
'G' 00-3F drivers/misc/sgi-gru/grulib.h conflict!
|
||||
'G' 00-0F linux/gigaset_dev.h conflict!
|
||||
'H' 00-7F linux/hiddev.h conflict!
|
||||
'H' 00-0F linux/hidraw.h conflict!
|
||||
'H' 01 linux/mei.h conflict!
|
||||
|
35
MAINTAINERS
35
MAINTAINERS
@ -674,6 +674,14 @@ S: Maintained
|
||||
F: Documentation/i2c/busses/i2c-ali1563.rst
|
||||
F: drivers/i2c/busses/i2c-ali1563.c
|
||||
|
||||
ALL SENSORS DLH SERIES PRESSURE SENSORS DRIVER
|
||||
M: Tomislav Denis <tomislav.denis@avl.com>
|
||||
W: http://www.allsensors.com/
|
||||
S: Maintained
|
||||
L: linux-iio@vger.kernel.org
|
||||
F: drivers/iio/pressure/dlhl60d.c
|
||||
F: Documentation/devicetree/bindings/iio/pressure/asc,dlhl60d.yaml
|
||||
|
||||
ALLEGRO DVT VIDEO IP CORE DRIVER
|
||||
M: Michael Tretter <m.tretter@pengutronix.de>
|
||||
R: Pengutronix Kernel Team <kernel@pengutronix.de>
|
||||
@ -907,6 +915,14 @@ S: Supported
|
||||
F: drivers/iio/dac/ad5758.c
|
||||
F: Documentation/devicetree/bindings/iio/dac/ad5758.txt
|
||||
|
||||
ANALOG DEVICES INC AD7091R5 DRIVER
|
||||
M: Beniamin Bia <beniamin.bia@analog.com>
|
||||
L: linux-iio@vger.kernel.org
|
||||
W: http://ez.analog.com/community/linux-device-drivers
|
||||
S: Supported
|
||||
F: drivers/iio/adc/ad7091r5.c
|
||||
F: Documentation/devicetree/bindings/iio/adc/adi,ad7091r5.yaml
|
||||
|
||||
ANALOG DEVICES INC AD7124 DRIVER
|
||||
M: Stefan Popa <stefan.popa@analog.com>
|
||||
L: linux-iio@vger.kernel.org
|
||||
@ -1061,7 +1077,7 @@ S: Supported
|
||||
F: Documentation/ABI/testing/sysfs-bus-iio-frequency-ad9523
|
||||
F: Documentation/ABI/testing/sysfs-bus-iio-frequency-adf4350
|
||||
F: drivers/iio/*/ad*
|
||||
F: drivers/iio/adc/ltc2497*
|
||||
F: drivers/iio/adc/ltc249*
|
||||
X: drivers/iio/*/adjd*
|
||||
F: drivers/staging/iio/*/ad*
|
||||
|
||||
@ -3116,6 +3132,13 @@ S: Supported
|
||||
F: drivers/net/bonding/
|
||||
F: include/uapi/linux/if_bonding.h
|
||||
|
||||
BOSCH SENSORTEC BMA400 ACCELEROMETER IIO DRIVER
|
||||
M: Dan Robertson <dan@dlrobertson.com>
|
||||
L: linux-iio@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/iio/accel/bma400*
|
||||
F: Documentation/devicetree/bindings/iio/accel/bosch,bma400.yaml
|
||||
|
||||
BPF (Safe dynamic programs and tools)
|
||||
M: Alexei Starovoitov <ast@kernel.org>
|
||||
M: Daniel Borkmann <daniel@iogearbox.net>
|
||||
@ -8862,7 +8885,7 @@ S: Maintained
|
||||
F: drivers/isdn/mISDN
|
||||
F: drivers/isdn/hardware
|
||||
|
||||
ISDN/CAPI SUBSYSTEM
|
||||
ISDN/CMTP OVER BLUETOOTH
|
||||
M: Karsten Keil <isdn@linux-pingi.de>
|
||||
L: isdn4linux@listserv.isdn4linux.de (subscribers-only)
|
||||
L: netdev@vger.kernel.org
|
||||
@ -8870,7 +8893,6 @@ W: http://www.isdn4linux.de
|
||||
S: Odd Fixes
|
||||
F: Documentation/isdn/
|
||||
F: drivers/isdn/capi/
|
||||
F: drivers/staging/isdn/
|
||||
F: net/bluetooth/cmtp/
|
||||
F: include/linux/isdn/
|
||||
F: include/uapi/linux/isdn/
|
||||
@ -12528,6 +12550,13 @@ L: platform-driver-x86@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/platform/x86/panasonic-laptop.c
|
||||
|
||||
PARALLAX PING IIO SENSOR DRIVER
|
||||
M: Andreas Klinger <ak@it-klinger.de>
|
||||
L: linux-iio@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/iio/proximity/parallax-ping.yaml
|
||||
F: drivers/iio/proximity/ping.c
|
||||
|
||||
PARALLEL LCD/KEYPAD PANEL DRIVER
|
||||
M: Willy Tarreau <willy@haproxy.com>
|
||||
M: Ksenija Stanojevic <ksenija.stanojevic@gmail.com>
|
||||
|
@ -89,13 +89,13 @@ config ADXL372_I2C
|
||||
module will be called adxl372_i2c.
|
||||
|
||||
config BMA180
|
||||
tristate "Bosch BMA180/BMA250 3-Axis Accelerometer Driver"
|
||||
tristate "Bosch BMA180/BMA25x 3-Axis Accelerometer Driver"
|
||||
depends on I2C
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
Say Y here if you want to build a driver for the Bosch BMA180 or
|
||||
BMA250 triaxial acceleration sensor.
|
||||
BMA25x triaxial acceleration sensor.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called bma180.
|
||||
@ -112,6 +112,22 @@ config BMA220
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called bma220_spi.
|
||||
|
||||
config BMA400
|
||||
tristate "Bosch BMA400 3-Axis Accelerometer Driver"
|
||||
select REGMAP
|
||||
select BMA400_I2C if I2C
|
||||
help
|
||||
Say Y here if you want to build a driver for the Bosch BMA400
|
||||
triaxial acceleration sensor.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called bma400_core and you will also get
|
||||
bma400_i2c if I2C is enabled.
|
||||
|
||||
config BMA400_I2C
|
||||
tristate
|
||||
depends on BMA400
|
||||
|
||||
config BMC150_ACCEL
|
||||
tristate "Bosch BMC150 Accelerometer Driver"
|
||||
select IIO_BUFFER
|
||||
|
@ -14,6 +14,8 @@ 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_BMA400) += bma400_core.o
|
||||
obj-$(CONFIG_BMA400_I2C) += bma400_i2c.o
|
||||
obj-$(CONFIG_BMC150_ACCEL) += bmc150-accel-core.o
|
||||
obj-$(CONFIG_BMC150_ACCEL_I2C) += bmc150-accel-i2c.o
|
||||
obj-$(CONFIG_BMC150_ACCEL_SPI) += bmc150-accel-spi.o
|
||||
|
@ -233,6 +233,12 @@ static const char * const adis16201_status_error_msgs[] = {
|
||||
[ADIS16201_DIAG_STAT_POWER_LOW_BIT] = "Power supply below 2.975V",
|
||||
};
|
||||
|
||||
static const struct adis_timeout adis16201_timeouts = {
|
||||
.reset_ms = ADIS16201_STARTUP_DELAY_MS,
|
||||
.sw_reset_ms = ADIS16201_STARTUP_DELAY_MS,
|
||||
.self_test_ms = ADIS16201_STARTUP_DELAY_MS,
|
||||
};
|
||||
|
||||
static const struct adis_data adis16201_data = {
|
||||
.read_delay = 20,
|
||||
.msc_ctrl_reg = ADIS16201_MSC_CTRL_REG,
|
||||
@ -241,7 +247,7 @@ static const struct adis_data adis16201_data = {
|
||||
|
||||
.self_test_mask = ADIS16201_MSC_CTRL_SELF_TEST_EN,
|
||||
.self_test_no_autoclear = true,
|
||||
.startup_delay = ADIS16201_STARTUP_DELAY_MS,
|
||||
.timeouts = &adis16201_timeouts,
|
||||
|
||||
.status_error_msgs = adis16201_status_error_msgs,
|
||||
.status_error_mask = BIT(ADIS16201_DIAG_STAT_SPI_FAIL_BIT) |
|
||||
|
@ -243,6 +243,12 @@ static const char * const adis16209_status_error_msgs[] = {
|
||||
[ADIS16209_STAT_POWER_LOW_BIT] = "Power supply below 2.975V",
|
||||
};
|
||||
|
||||
static const struct adis_timeout adis16209_timeouts = {
|
||||
.reset_ms = ADIS16209_STARTUP_DELAY_MS,
|
||||
.self_test_ms = ADIS16209_STARTUP_DELAY_MS,
|
||||
.sw_reset_ms = ADIS16209_STARTUP_DELAY_MS,
|
||||
};
|
||||
|
||||
static const struct adis_data adis16209_data = {
|
||||
.read_delay = 30,
|
||||
.msc_ctrl_reg = ADIS16209_MSC_CTRL_REG,
|
||||
@ -251,7 +257,7 @@ static const struct adis_data adis16209_data = {
|
||||
|
||||
.self_test_mask = ADIS16209_MSC_CTRL_SELF_TEST_EN,
|
||||
.self_test_no_autoclear = true,
|
||||
.startup_delay = ADIS16209_STARTUP_DELAY_MS,
|
||||
.timeouts = &adis16209_timeouts,
|
||||
|
||||
.status_error_msgs = adis16209_status_error_msgs,
|
||||
.status_error_mask = BIT(ADIS16209_STAT_SELFTEST_FAIL_BIT) |
|
||||
|
@ -9,6 +9,7 @@
|
||||
* SPI is not supported by driver
|
||||
* BMA180: 7-bit I2C slave address 0x40 or 0x41
|
||||
* BMA250: 7-bit I2C slave address 0x18 or 0x19
|
||||
* BMA254: 7-bit I2C slave address 0x18 or 0x19
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
@ -18,6 +19,7 @@
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/iio/iio.h>
|
||||
@ -33,17 +35,20 @@
|
||||
enum chip_ids {
|
||||
BMA180,
|
||||
BMA250,
|
||||
BMA254,
|
||||
};
|
||||
|
||||
struct bma180_data;
|
||||
|
||||
struct bma180_part_info {
|
||||
u8 chip_id;
|
||||
const struct iio_chan_spec *channels;
|
||||
unsigned int num_channels;
|
||||
const int *scale_table;
|
||||
unsigned int num_scales;
|
||||
const int *bw_table;
|
||||
unsigned int num_bw;
|
||||
int center_temp;
|
||||
|
||||
u8 int_reset_reg, int_reset_mask;
|
||||
u8 sleep_reg, sleep_mask;
|
||||
@ -51,6 +56,7 @@ struct bma180_part_info {
|
||||
u8 scale_reg, scale_mask;
|
||||
u8 power_reg, power_mask, lowpower_val;
|
||||
u8 int_enable_reg, int_enable_mask;
|
||||
u8 int_map_reg, int_enable_dataready_int1_mask;
|
||||
u8 softreset_reg;
|
||||
|
||||
int (*chip_config)(struct bma180_data *data);
|
||||
@ -89,6 +95,8 @@ struct bma180_part_info {
|
||||
#define BMA180_RESET_VAL 0xb6
|
||||
|
||||
#define BMA180_ID_REG_VAL 0x03
|
||||
#define BMA250_ID_REG_VAL 0x03
|
||||
#define BMA254_ID_REG_VAL 0xfa /* 250 decimal */
|
||||
|
||||
/* Chip power modes */
|
||||
#define BMA180_LOW_POWER 0x03
|
||||
@ -109,7 +117,26 @@ struct bma180_part_info {
|
||||
#define BMA250_INT1_DATA_MASK BIT(0)
|
||||
#define BMA250_INT_RESET_MASK BIT(7) /* Reset pending interrupts */
|
||||
|
||||
#define BMA254_RANGE_REG 0x0f
|
||||
#define BMA254_BW_REG 0x10
|
||||
#define BMA254_POWER_REG 0x11
|
||||
#define BMA254_RESET_REG 0x14
|
||||
#define BMA254_INT_ENABLE_REG 0x17
|
||||
#define BMA254_INT_MAP_REG 0x1a
|
||||
#define BMA254_INT_RESET_REG 0x21
|
||||
|
||||
#define BMA254_RANGE_MASK GENMASK(3, 0) /* Range of accel values */
|
||||
#define BMA254_BW_MASK GENMASK(4, 0) /* Accel bandwidth */
|
||||
#define BMA254_SUSPEND_MASK BIT(7) /* chip will sleep */
|
||||
#define BMA254_LOWPOWER_MASK BIT(6)
|
||||
#define BMA254_DATA_INTEN_MASK BIT(4)
|
||||
#define BMA254_INT2_DATA_MASK BIT(7)
|
||||
#define BMA254_INT1_DATA_MASK BIT(0)
|
||||
#define BMA254_INT_RESET_MASK BIT(7) /* Reset pending interrupts */
|
||||
|
||||
struct bma180_data {
|
||||
struct regulator *vdd_supply;
|
||||
struct regulator *vddio_supply;
|
||||
struct i2c_client *client;
|
||||
struct iio_trigger *trig;
|
||||
const struct bma180_part_info *part_info;
|
||||
@ -132,8 +159,8 @@ enum bma180_chan {
|
||||
static int bma180_bw_table[] = { 10, 20, 40, 75, 150, 300 }; /* Hz */
|
||||
static int bma180_scale_table[] = { 1275, 1863, 2452, 3727, 4903, 9709, 19417 };
|
||||
|
||||
static int bma250_bw_table[] = { 8, 16, 31, 63, 125, 250 }; /* Hz */
|
||||
static int bma250_scale_table[] = { 0, 0, 0, 38344, 0, 76590, 0, 0, 153180, 0,
|
||||
static int bma25x_bw_table[] = { 8, 16, 31, 63, 125, 250 }; /* Hz */
|
||||
static int bma25x_scale_table[] = { 0, 0, 0, 38344, 0, 76590, 0, 0, 153180, 0,
|
||||
0, 0, 306458 };
|
||||
|
||||
static int bma180_get_data_reg(struct bma180_data *data, enum bma180_chan chan)
|
||||
@ -307,8 +334,11 @@ static int bma180_chip_init(struct bma180_data *data)
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret != BMA180_ID_REG_VAL)
|
||||
if (ret != data->part_info->chip_id) {
|
||||
dev_err(&data->client->dev, "wrong chip ID %d expected %d\n",
|
||||
ret, data->part_info->chip_id);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ret = bma180_soft_reset(data);
|
||||
if (ret)
|
||||
@ -355,7 +385,7 @@ err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bma250_chip_config(struct bma180_data *data)
|
||||
static int bma25x_chip_config(struct bma180_data *data)
|
||||
{
|
||||
int ret = bma180_chip_init(data);
|
||||
|
||||
@ -367,8 +397,12 @@ static int bma250_chip_config(struct bma180_data *data)
|
||||
ret = bma180_set_scale(data, 38344); /* 2 G */
|
||||
if (ret)
|
||||
goto err;
|
||||
ret = bma180_set_bits(data, BMA250_INT_MAP_REG,
|
||||
BMA250_INT1_DATA_MASK, 1);
|
||||
/*
|
||||
* This enables dataready interrupt on the INT1 pin
|
||||
* FIXME: support using the INT2 pin
|
||||
*/
|
||||
ret = bma180_set_bits(data, data->part_info->int_map_reg,
|
||||
data->part_info->int_enable_dataready_int1_mask, 1);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
@ -394,7 +428,7 @@ err:
|
||||
dev_err(&data->client->dev, "failed to disable the chip\n");
|
||||
}
|
||||
|
||||
static void bma250_chip_disable(struct bma180_data *data)
|
||||
static void bma25x_chip_disable(struct bma180_data *data)
|
||||
{
|
||||
if (bma180_set_new_data_intr_state(data, false))
|
||||
goto err;
|
||||
@ -497,7 +531,7 @@ static int bma180_read_raw(struct iio_dev *indio_dev,
|
||||
return -EINVAL;
|
||||
}
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
*val = 48; /* 0 LSB @ 24 degree C */
|
||||
*val = data->part_info->center_temp;
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
@ -627,34 +661,96 @@ static const struct iio_chan_spec bma250_channels[] = {
|
||||
IIO_CHAN_SOFT_TIMESTAMP(4),
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec bma254_channels[] = {
|
||||
BMA180_ACC_CHANNEL(X, 12),
|
||||
BMA180_ACC_CHANNEL(Y, 12),
|
||||
BMA180_ACC_CHANNEL(Z, 12),
|
||||
BMA180_TEMP_CHANNEL,
|
||||
IIO_CHAN_SOFT_TIMESTAMP(4),
|
||||
};
|
||||
|
||||
static const struct bma180_part_info bma180_part_info[] = {
|
||||
[BMA180] = {
|
||||
bma180_channels, ARRAY_SIZE(bma180_channels),
|
||||
bma180_scale_table, ARRAY_SIZE(bma180_scale_table),
|
||||
bma180_bw_table, ARRAY_SIZE(bma180_bw_table),
|
||||
BMA180_CTRL_REG0, BMA180_RESET_INT,
|
||||
BMA180_CTRL_REG0, BMA180_SLEEP,
|
||||
BMA180_BW_TCS, BMA180_BW,
|
||||
BMA180_OFFSET_LSB1, BMA180_RANGE,
|
||||
BMA180_TCO_Z, BMA180_MODE_CONFIG, BMA180_LOW_POWER,
|
||||
BMA180_CTRL_REG3, BMA180_NEW_DATA_INT,
|
||||
BMA180_RESET,
|
||||
bma180_chip_config,
|
||||
bma180_chip_disable,
|
||||
.chip_id = BMA180_ID_REG_VAL,
|
||||
.channels = bma180_channels,
|
||||
.num_channels = ARRAY_SIZE(bma180_channels),
|
||||
.scale_table = bma180_scale_table,
|
||||
.num_scales = ARRAY_SIZE(bma180_scale_table),
|
||||
.bw_table = bma180_bw_table,
|
||||
.num_bw = ARRAY_SIZE(bma180_bw_table),
|
||||
.center_temp = 48, /* 0 LSB @ 24 degree C */
|
||||
.int_reset_reg = BMA180_CTRL_REG0,
|
||||
.int_reset_mask = BMA180_RESET_INT,
|
||||
.sleep_reg = BMA180_CTRL_REG0,
|
||||
.sleep_mask = BMA180_SLEEP,
|
||||
.bw_reg = BMA180_BW_TCS,
|
||||
.bw_mask = BMA180_BW,
|
||||
.scale_reg = BMA180_OFFSET_LSB1,
|
||||
.scale_mask = BMA180_RANGE,
|
||||
.power_reg = BMA180_TCO_Z,
|
||||
.power_mask = BMA180_MODE_CONFIG,
|
||||
.lowpower_val = BMA180_LOW_POWER,
|
||||
.int_enable_reg = BMA180_CTRL_REG3,
|
||||
.int_enable_mask = BMA180_NEW_DATA_INT,
|
||||
.softreset_reg = BMA180_RESET,
|
||||
.chip_config = bma180_chip_config,
|
||||
.chip_disable = bma180_chip_disable,
|
||||
},
|
||||
[BMA250] = {
|
||||
bma250_channels, ARRAY_SIZE(bma250_channels),
|
||||
bma250_scale_table, ARRAY_SIZE(bma250_scale_table),
|
||||
bma250_bw_table, ARRAY_SIZE(bma250_bw_table),
|
||||
BMA250_INT_RESET_REG, BMA250_INT_RESET_MASK,
|
||||
BMA250_POWER_REG, BMA250_SUSPEND_MASK,
|
||||
BMA250_BW_REG, BMA250_BW_MASK,
|
||||
BMA250_RANGE_REG, BMA250_RANGE_MASK,
|
||||
BMA250_POWER_REG, BMA250_LOWPOWER_MASK, 1,
|
||||
BMA250_INT_ENABLE_REG, BMA250_DATA_INTEN_MASK,
|
||||
BMA250_RESET_REG,
|
||||
bma250_chip_config,
|
||||
bma250_chip_disable,
|
||||
.chip_id = BMA250_ID_REG_VAL,
|
||||
.channels = bma250_channels,
|
||||
.num_channels = ARRAY_SIZE(bma250_channels),
|
||||
.scale_table = bma25x_scale_table,
|
||||
.num_scales = ARRAY_SIZE(bma25x_scale_table),
|
||||
.bw_table = bma25x_bw_table,
|
||||
.num_bw = ARRAY_SIZE(bma25x_bw_table),
|
||||
.center_temp = 48, /* 0 LSB @ 24 degree C */
|
||||
.int_reset_reg = BMA250_INT_RESET_REG,
|
||||
.int_reset_mask = BMA250_INT_RESET_MASK,
|
||||
.sleep_reg = BMA250_POWER_REG,
|
||||
.sleep_mask = BMA250_SUSPEND_MASK,
|
||||
.bw_reg = BMA250_BW_REG,
|
||||
.bw_mask = BMA250_BW_MASK,
|
||||
.scale_reg = BMA250_RANGE_REG,
|
||||
.scale_mask = BMA250_RANGE_MASK,
|
||||
.power_reg = BMA250_POWER_REG,
|
||||
.power_mask = BMA250_LOWPOWER_MASK,
|
||||
.lowpower_val = 1,
|
||||
.int_enable_reg = BMA250_INT_ENABLE_REG,
|
||||
.int_enable_mask = BMA250_DATA_INTEN_MASK,
|
||||
.int_map_reg = BMA250_INT_MAP_REG,
|
||||
.int_enable_dataready_int1_mask = BMA250_INT1_DATA_MASK,
|
||||
.softreset_reg = BMA250_RESET_REG,
|
||||
.chip_config = bma25x_chip_config,
|
||||
.chip_disable = bma25x_chip_disable,
|
||||
},
|
||||
[BMA254] = {
|
||||
.chip_id = BMA254_ID_REG_VAL,
|
||||
.channels = bma254_channels,
|
||||
.num_channels = ARRAY_SIZE(bma254_channels),
|
||||
.scale_table = bma25x_scale_table,
|
||||
.num_scales = ARRAY_SIZE(bma25x_scale_table),
|
||||
.bw_table = bma25x_bw_table,
|
||||
.num_bw = ARRAY_SIZE(bma25x_bw_table),
|
||||
.center_temp = 46, /* 0 LSB @ 23 degree C */
|
||||
.int_reset_reg = BMA254_INT_RESET_REG,
|
||||
.int_reset_mask = BMA254_INT_RESET_MASK,
|
||||
.sleep_reg = BMA254_POWER_REG,
|
||||
.sleep_mask = BMA254_SUSPEND_MASK,
|
||||
.bw_reg = BMA254_BW_REG,
|
||||
.bw_mask = BMA254_BW_MASK,
|
||||
.scale_reg = BMA254_RANGE_REG,
|
||||
.scale_mask = BMA254_RANGE_MASK,
|
||||
.power_reg = BMA254_POWER_REG,
|
||||
.power_mask = BMA254_LOWPOWER_MASK,
|
||||
.lowpower_val = 1,
|
||||
.int_enable_reg = BMA254_INT_ENABLE_REG,
|
||||
.int_enable_mask = BMA254_DATA_INTEN_MASK,
|
||||
.int_map_reg = BMA254_INT_MAP_REG,
|
||||
.int_enable_dataready_int1_mask = BMA254_INT1_DATA_MASK,
|
||||
.softreset_reg = BMA254_RESET_REG,
|
||||
.chip_config = bma25x_chip_config,
|
||||
.chip_disable = bma25x_chip_disable,
|
||||
},
|
||||
};
|
||||
|
||||
@ -712,12 +808,13 @@ static const struct iio_trigger_ops bma180_trigger_ops = {
|
||||
static int bma180_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
struct bma180_data *data;
|
||||
struct iio_dev *indio_dev;
|
||||
enum chip_ids chip;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
||||
indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -725,22 +822,56 @@ static int bma180_probe(struct i2c_client *client,
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
data->client = client;
|
||||
if (client->dev.of_node)
|
||||
chip = (enum chip_ids)of_device_get_match_data(&client->dev);
|
||||
chip = (enum chip_ids)of_device_get_match_data(dev);
|
||||
else
|
||||
chip = id->driver_data;
|
||||
data->part_info = &bma180_part_info[chip];
|
||||
|
||||
ret = iio_read_mount_matrix(&client->dev, "mount-matrix",
|
||||
ret = iio_read_mount_matrix(dev, "mount-matrix",
|
||||
&data->orientation);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
data->vdd_supply = devm_regulator_get(dev, "vdd");
|
||||
if (IS_ERR(data->vdd_supply)) {
|
||||
if (PTR_ERR(data->vdd_supply) != -EPROBE_DEFER)
|
||||
dev_err(dev, "Failed to get vdd regulator %d\n",
|
||||
(int)PTR_ERR(data->vdd_supply));
|
||||
return PTR_ERR(data->vdd_supply);
|
||||
}
|
||||
data->vddio_supply = devm_regulator_get(dev, "vddio");
|
||||
if (IS_ERR(data->vddio_supply)) {
|
||||
if (PTR_ERR(data->vddio_supply) != -EPROBE_DEFER)
|
||||
dev_err(dev, "Failed to get vddio regulator %d\n",
|
||||
(int)PTR_ERR(data->vddio_supply));
|
||||
return PTR_ERR(data->vddio_supply);
|
||||
}
|
||||
/* Typical voltage 2.4V these are min and max */
|
||||
ret = regulator_set_voltage(data->vdd_supply, 1620000, 3600000);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = regulator_set_voltage(data->vddio_supply, 1200000, 3600000);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = regulator_enable(data->vdd_supply);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to enable vdd regulator: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
ret = regulator_enable(data->vddio_supply);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to enable vddio regulator: %d\n", ret);
|
||||
goto err_disable_vdd;
|
||||
}
|
||||
/* Wait to make sure we started up properly (3 ms at least) */
|
||||
usleep_range(3000, 5000);
|
||||
|
||||
ret = data->part_info->chip_config(data);
|
||||
if (ret < 0)
|
||||
goto err_chip_disable;
|
||||
|
||||
mutex_init(&data->mutex);
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->dev.parent = dev;
|
||||
indio_dev->channels = data->part_info->channels;
|
||||
indio_dev->num_channels = data->part_info->num_channels;
|
||||
indio_dev->name = id->name;
|
||||
@ -755,15 +886,15 @@ static int bma180_probe(struct i2c_client *client,
|
||||
goto err_chip_disable;
|
||||
}
|
||||
|
||||
ret = devm_request_irq(&client->dev, client->irq,
|
||||
ret = devm_request_irq(dev, client->irq,
|
||||
iio_trigger_generic_data_rdy_poll, IRQF_TRIGGER_RISING,
|
||||
"bma180_event", data->trig);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "unable to request IRQ\n");
|
||||
dev_err(dev, "unable to request IRQ\n");
|
||||
goto err_trigger_free;
|
||||
}
|
||||
|
||||
data->trig->dev.parent = &client->dev;
|
||||
data->trig->dev.parent = dev;
|
||||
data->trig->ops = &bma180_trigger_ops;
|
||||
iio_trigger_set_drvdata(data->trig, indio_dev);
|
||||
indio_dev->trig = iio_trigger_get(data->trig);
|
||||
@ -776,13 +907,13 @@ static int bma180_probe(struct i2c_client *client,
|
||||
ret = iio_triggered_buffer_setup(indio_dev, NULL,
|
||||
bma180_trigger_handler, NULL);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "unable to setup iio triggered buffer\n");
|
||||
dev_err(dev, "unable to setup iio triggered buffer\n");
|
||||
goto err_trigger_unregister;
|
||||
}
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "unable to register iio device\n");
|
||||
dev_err(dev, "unable to register iio device\n");
|
||||
goto err_buffer_cleanup;
|
||||
}
|
||||
|
||||
@ -797,6 +928,9 @@ err_trigger_free:
|
||||
iio_trigger_free(data->trig);
|
||||
err_chip_disable:
|
||||
data->part_info->chip_disable(data);
|
||||
regulator_disable(data->vddio_supply);
|
||||
err_disable_vdd:
|
||||
regulator_disable(data->vdd_supply);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -816,6 +950,8 @@ static int bma180_remove(struct i2c_client *client)
|
||||
mutex_lock(&data->mutex);
|
||||
data->part_info->chip_disable(data);
|
||||
mutex_unlock(&data->mutex);
|
||||
regulator_disable(data->vddio_supply);
|
||||
regulator_disable(data->vdd_supply);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -856,6 +992,7 @@ static SIMPLE_DEV_PM_OPS(bma180_pm_ops, bma180_suspend, bma180_resume);
|
||||
static const struct i2c_device_id bma180_ids[] = {
|
||||
{ "bma180", BMA180 },
|
||||
{ "bma250", BMA250 },
|
||||
{ "bma254", BMA254 },
|
||||
{ }
|
||||
};
|
||||
|
||||
@ -870,6 +1007,10 @@ static const struct of_device_id bma180_of_match[] = {
|
||||
.compatible = "bosch,bma250",
|
||||
.data = (void *)BMA250
|
||||
},
|
||||
{
|
||||
.compatible = "bosch,bma254",
|
||||
.data = (void *)BMA254
|
||||
},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, bma180_of_match);
|
||||
@ -889,5 +1030,5 @@ module_i2c_driver(bma180_driver);
|
||||
|
||||
MODULE_AUTHOR("Kravchenko Oleksandr <x0199363@ti.com>");
|
||||
MODULE_AUTHOR("Texas Instruments, Inc.");
|
||||
MODULE_DESCRIPTION("Bosch BMA180/BMA250 triaxial acceleration sensor");
|
||||
MODULE_DESCRIPTION("Bosch BMA180/BMA25x triaxial acceleration sensor");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
99
drivers/iio/accel/bma400.h
Normal file
99
drivers/iio/accel/bma400.h
Normal file
@ -0,0 +1,99 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Register constants and other forward declarations needed by the bma400
|
||||
* sources.
|
||||
*
|
||||
* Copyright 2019 Dan Robertson <dan@dlrobertson.com>
|
||||
*/
|
||||
|
||||
#ifndef _BMA400_H_
|
||||
#define _BMA400_H_
|
||||
|
||||
#include <linux/bits.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
/*
|
||||
* Read-Only Registers
|
||||
*/
|
||||
|
||||
/* Status and ID registers */
|
||||
#define BMA400_CHIP_ID_REG 0x00
|
||||
#define BMA400_ERR_REG 0x02
|
||||
#define BMA400_STATUS_REG 0x03
|
||||
|
||||
/* Acceleration registers */
|
||||
#define BMA400_X_AXIS_LSB_REG 0x04
|
||||
#define BMA400_X_AXIS_MSB_REG 0x05
|
||||
#define BMA400_Y_AXIS_LSB_REG 0x06
|
||||
#define BMA400_Y_AXIS_MSB_REG 0x07
|
||||
#define BMA400_Z_AXIS_LSB_REG 0x08
|
||||
#define BMA400_Z_AXIS_MSB_REG 0x09
|
||||
|
||||
/* Sensor time registers */
|
||||
#define BMA400_SENSOR_TIME0 0x0a
|
||||
#define BMA400_SENSOR_TIME1 0x0b
|
||||
#define BMA400_SENSOR_TIME2 0x0c
|
||||
|
||||
/* Event and interrupt registers */
|
||||
#define BMA400_EVENT_REG 0x0d
|
||||
#define BMA400_INT_STAT0_REG 0x0e
|
||||
#define BMA400_INT_STAT1_REG 0x0f
|
||||
#define BMA400_INT_STAT2_REG 0x10
|
||||
|
||||
/* Temperature register */
|
||||
#define BMA400_TEMP_DATA_REG 0x11
|
||||
|
||||
/* FIFO length and data registers */
|
||||
#define BMA400_FIFO_LENGTH0_REG 0x12
|
||||
#define BMA400_FIFO_LENGTH1_REG 0x13
|
||||
#define BMA400_FIFO_DATA_REG 0x14
|
||||
|
||||
/* Step count registers */
|
||||
#define BMA400_STEP_CNT0_REG 0x15
|
||||
#define BMA400_STEP_CNT1_REG 0x16
|
||||
#define BMA400_STEP_CNT3_REG 0x17
|
||||
#define BMA400_STEP_STAT_REG 0x18
|
||||
|
||||
/*
|
||||
* Read-write configuration registers
|
||||
*/
|
||||
#define BMA400_ACC_CONFIG0_REG 0x19
|
||||
#define BMA400_ACC_CONFIG1_REG 0x1a
|
||||
#define BMA400_ACC_CONFIG2_REG 0x1b
|
||||
#define BMA400_CMD_REG 0x7e
|
||||
|
||||
/* Chip ID of BMA 400 devices found in the chip ID register. */
|
||||
#define BMA400_ID_REG_VAL 0x90
|
||||
|
||||
#define BMA400_LP_OSR_SHIFT 5
|
||||
#define BMA400_NP_OSR_SHIFT 4
|
||||
#define BMA400_SCALE_SHIFT 6
|
||||
|
||||
#define BMA400_TWO_BITS_MASK GENMASK(1, 0)
|
||||
#define BMA400_LP_OSR_MASK GENMASK(6, 5)
|
||||
#define BMA400_NP_OSR_MASK GENMASK(5, 4)
|
||||
#define BMA400_ACC_ODR_MASK GENMASK(3, 0)
|
||||
#define BMA400_ACC_SCALE_MASK GENMASK(7, 6)
|
||||
|
||||
#define BMA400_ACC_ODR_MIN_RAW 0x05
|
||||
#define BMA400_ACC_ODR_LP_RAW 0x06
|
||||
#define BMA400_ACC_ODR_MAX_RAW 0x0b
|
||||
|
||||
#define BMA400_ACC_ODR_MAX_HZ 800
|
||||
#define BMA400_ACC_ODR_MIN_WHOLE_HZ 25
|
||||
#define BMA400_ACC_ODR_MIN_HZ 12
|
||||
|
||||
#define BMA400_SCALE_MIN 38357
|
||||
#define BMA400_SCALE_MAX 306864
|
||||
|
||||
#define BMA400_NUM_REGULATORS 2
|
||||
#define BMA400_VDD_REGULATOR 0
|
||||
#define BMA400_VDDIO_REGULATOR 1
|
||||
|
||||
extern const struct regmap_config bma400_regmap_config;
|
||||
|
||||
int bma400_probe(struct device *dev, struct regmap *regmap, const char *name);
|
||||
|
||||
int bma400_remove(struct device *dev);
|
||||
|
||||
#endif
|
853
drivers/iio/accel/bma400_core.c
Normal file
853
drivers/iio/accel/bma400_core.c
Normal file
@ -0,0 +1,853 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Core IIO driver for Bosch BMA400 triaxial acceleration sensor.
|
||||
*
|
||||
* Copyright 2019 Dan Robertson <dan@dlrobertson.com>
|
||||
*
|
||||
* TODO:
|
||||
* - Support for power management
|
||||
* - Support events and interrupts
|
||||
* - Create channel for step count
|
||||
* - Create channel for sensor time
|
||||
*/
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#include "bma400.h"
|
||||
|
||||
/*
|
||||
* The G-range selection may be one of 2g, 4g, 8, or 16g. The scale may
|
||||
* be selected with the acc_range bits of the ACC_CONFIG1 register.
|
||||
* NB: This buffer is populated in the device init.
|
||||
*/
|
||||
static int bma400_scales[8];
|
||||
|
||||
/*
|
||||
* See the ACC_CONFIG1 section of the datasheet.
|
||||
* NB: This buffer is populated in the device init.
|
||||
*/
|
||||
static int bma400_sample_freqs[14];
|
||||
|
||||
static const int bma400_osr_range[] = { 0, 1, 3 };
|
||||
|
||||
/* See the ACC_CONFIG0 section of the datasheet */
|
||||
enum bma400_power_mode {
|
||||
POWER_MODE_SLEEP = 0x00,
|
||||
POWER_MODE_LOW = 0x01,
|
||||
POWER_MODE_NORMAL = 0x02,
|
||||
POWER_MODE_INVALID = 0x03,
|
||||
};
|
||||
|
||||
struct bma400_sample_freq {
|
||||
int hz;
|
||||
int uhz;
|
||||
};
|
||||
|
||||
struct bma400_data {
|
||||
struct device *dev;
|
||||
struct regmap *regmap;
|
||||
struct regulator_bulk_data regulators[BMA400_NUM_REGULATORS];
|
||||
struct mutex mutex; /* data register lock */
|
||||
struct iio_mount_matrix orientation;
|
||||
enum bma400_power_mode power_mode;
|
||||
struct bma400_sample_freq sample_freq;
|
||||
int oversampling_ratio;
|
||||
int scale;
|
||||
};
|
||||
|
||||
static bool bma400_is_writable_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case BMA400_CHIP_ID_REG:
|
||||
case BMA400_ERR_REG:
|
||||
case BMA400_STATUS_REG:
|
||||
case BMA400_X_AXIS_LSB_REG:
|
||||
case BMA400_X_AXIS_MSB_REG:
|
||||
case BMA400_Y_AXIS_LSB_REG:
|
||||
case BMA400_Y_AXIS_MSB_REG:
|
||||
case BMA400_Z_AXIS_LSB_REG:
|
||||
case BMA400_Z_AXIS_MSB_REG:
|
||||
case BMA400_SENSOR_TIME0:
|
||||
case BMA400_SENSOR_TIME1:
|
||||
case BMA400_SENSOR_TIME2:
|
||||
case BMA400_EVENT_REG:
|
||||
case BMA400_INT_STAT0_REG:
|
||||
case BMA400_INT_STAT1_REG:
|
||||
case BMA400_INT_STAT2_REG:
|
||||
case BMA400_TEMP_DATA_REG:
|
||||
case BMA400_FIFO_LENGTH0_REG:
|
||||
case BMA400_FIFO_LENGTH1_REG:
|
||||
case BMA400_FIFO_DATA_REG:
|
||||
case BMA400_STEP_CNT0_REG:
|
||||
case BMA400_STEP_CNT1_REG:
|
||||
case BMA400_STEP_CNT3_REG:
|
||||
case BMA400_STEP_STAT_REG:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static bool bma400_is_volatile_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case BMA400_ERR_REG:
|
||||
case BMA400_STATUS_REG:
|
||||
case BMA400_X_AXIS_LSB_REG:
|
||||
case BMA400_X_AXIS_MSB_REG:
|
||||
case BMA400_Y_AXIS_LSB_REG:
|
||||
case BMA400_Y_AXIS_MSB_REG:
|
||||
case BMA400_Z_AXIS_LSB_REG:
|
||||
case BMA400_Z_AXIS_MSB_REG:
|
||||
case BMA400_SENSOR_TIME0:
|
||||
case BMA400_SENSOR_TIME1:
|
||||
case BMA400_SENSOR_TIME2:
|
||||
case BMA400_EVENT_REG:
|
||||
case BMA400_INT_STAT0_REG:
|
||||
case BMA400_INT_STAT1_REG:
|
||||
case BMA400_INT_STAT2_REG:
|
||||
case BMA400_TEMP_DATA_REG:
|
||||
case BMA400_FIFO_LENGTH0_REG:
|
||||
case BMA400_FIFO_LENGTH1_REG:
|
||||
case BMA400_FIFO_DATA_REG:
|
||||
case BMA400_STEP_CNT0_REG:
|
||||
case BMA400_STEP_CNT1_REG:
|
||||
case BMA400_STEP_CNT3_REG:
|
||||
case BMA400_STEP_STAT_REG:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const struct regmap_config bma400_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.max_register = BMA400_CMD_REG,
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
.writeable_reg = bma400_is_writable_reg,
|
||||
.volatile_reg = bma400_is_volatile_reg,
|
||||
};
|
||||
EXPORT_SYMBOL(bma400_regmap_config);
|
||||
|
||||
static const struct iio_mount_matrix *
|
||||
bma400_accel_get_mount_matrix(const struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan)
|
||||
{
|
||||
struct bma400_data *data = iio_priv(indio_dev);
|
||||
|
||||
return &data->orientation;
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec_ext_info bma400_ext_info[] = {
|
||||
IIO_MOUNT_MATRIX(IIO_SHARED_BY_DIR, bma400_accel_get_mount_matrix),
|
||||
{ }
|
||||
};
|
||||
|
||||
#define BMA400_ACC_CHANNEL(_axis) { \
|
||||
.type = IIO_ACCEL, \
|
||||
.modified = 1, \
|
||||
.channel2 = IIO_MOD_##_axis, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
|
||||
BIT(IIO_CHAN_INFO_SCALE) | \
|
||||
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
|
||||
.info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
|
||||
BIT(IIO_CHAN_INFO_SCALE) | \
|
||||
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
|
||||
.ext_info = bma400_ext_info, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec bma400_channels[] = {
|
||||
BMA400_ACC_CHANNEL(X),
|
||||
BMA400_ACC_CHANNEL(Y),
|
||||
BMA400_ACC_CHANNEL(Z),
|
||||
{
|
||||
.type = IIO_TEMP,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ),
|
||||
},
|
||||
};
|
||||
|
||||
static int bma400_get_temp_reg(struct bma400_data *data, int *val, int *val2)
|
||||
{
|
||||
unsigned int raw_temp;
|
||||
int host_temp;
|
||||
int ret;
|
||||
|
||||
if (data->power_mode == POWER_MODE_SLEEP)
|
||||
return -EBUSY;
|
||||
|
||||
ret = regmap_read(data->regmap, BMA400_TEMP_DATA_REG, &raw_temp);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
host_temp = sign_extend32(raw_temp, 7);
|
||||
/*
|
||||
* The formula for the TEMP_DATA register in the datasheet
|
||||
* is: x * 0.5 + 23
|
||||
*/
|
||||
*val = (host_temp >> 1) + 23;
|
||||
*val2 = (host_temp & 0x1) * 500000;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
}
|
||||
|
||||
static int bma400_get_accel_reg(struct bma400_data *data,
|
||||
const struct iio_chan_spec *chan,
|
||||
int *val)
|
||||
{
|
||||
__le16 raw_accel;
|
||||
int lsb_reg;
|
||||
int ret;
|
||||
|
||||
if (data->power_mode == POWER_MODE_SLEEP)
|
||||
return -EBUSY;
|
||||
|
||||
switch (chan->channel2) {
|
||||
case IIO_MOD_X:
|
||||
lsb_reg = BMA400_X_AXIS_LSB_REG;
|
||||
break;
|
||||
case IIO_MOD_Y:
|
||||
lsb_reg = BMA400_Y_AXIS_LSB_REG;
|
||||
break;
|
||||
case IIO_MOD_Z:
|
||||
lsb_reg = BMA400_Z_AXIS_LSB_REG;
|
||||
break;
|
||||
default:
|
||||
dev_err(data->dev, "invalid axis channel modifier\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* bulk read two registers, with the base being the LSB register */
|
||||
ret = regmap_bulk_read(data->regmap, lsb_reg, &raw_accel,
|
||||
sizeof(raw_accel));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*val = sign_extend32(le16_to_cpu(raw_accel), 11);
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
static void bma400_output_data_rate_from_raw(int raw, unsigned int *val,
|
||||
unsigned int *val2)
|
||||
{
|
||||
*val = BMA400_ACC_ODR_MAX_HZ >> (BMA400_ACC_ODR_MAX_RAW - raw);
|
||||
if (raw > BMA400_ACC_ODR_MIN_RAW)
|
||||
*val2 = 0;
|
||||
else
|
||||
*val2 = 500000;
|
||||
}
|
||||
|
||||
static int bma400_get_accel_output_data_rate(struct bma400_data *data)
|
||||
{
|
||||
unsigned int val;
|
||||
unsigned int odr;
|
||||
int ret;
|
||||
|
||||
switch (data->power_mode) {
|
||||
case POWER_MODE_LOW:
|
||||
/*
|
||||
* Runs at a fixed rate in low-power mode. See section 4.3
|
||||
* in the datasheet.
|
||||
*/
|
||||
bma400_output_data_rate_from_raw(BMA400_ACC_ODR_LP_RAW,
|
||||
&data->sample_freq.hz,
|
||||
&data->sample_freq.uhz);
|
||||
return 0;
|
||||
case POWER_MODE_NORMAL:
|
||||
/*
|
||||
* In normal mode the ODR can be found in the ACC_CONFIG1
|
||||
* register.
|
||||
*/
|
||||
ret = regmap_read(data->regmap, BMA400_ACC_CONFIG1_REG, &val);
|
||||
if (ret)
|
||||
goto error;
|
||||
|
||||
odr = val & BMA400_ACC_ODR_MASK;
|
||||
if (odr < BMA400_ACC_ODR_MIN_RAW ||
|
||||
odr > BMA400_ACC_ODR_MAX_RAW) {
|
||||
ret = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
bma400_output_data_rate_from_raw(odr, &data->sample_freq.hz,
|
||||
&data->sample_freq.uhz);
|
||||
return 0;
|
||||
case POWER_MODE_SLEEP:
|
||||
data->sample_freq.hz = 0;
|
||||
data->sample_freq.uhz = 0;
|
||||
return 0;
|
||||
default:
|
||||
ret = 0;
|
||||
goto error;
|
||||
}
|
||||
error:
|
||||
data->sample_freq.hz = -1;
|
||||
data->sample_freq.uhz = -1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bma400_set_accel_output_data_rate(struct bma400_data *data,
|
||||
int hz, int uhz)
|
||||
{
|
||||
unsigned int idx;
|
||||
unsigned int odr;
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
if (hz >= BMA400_ACC_ODR_MIN_WHOLE_HZ) {
|
||||
if (uhz || hz > BMA400_ACC_ODR_MAX_HZ)
|
||||
return -EINVAL;
|
||||
|
||||
/* Note this works because MIN_WHOLE_HZ is odd */
|
||||
idx = __ffs(hz);
|
||||
|
||||
if (hz >> idx != BMA400_ACC_ODR_MIN_WHOLE_HZ)
|
||||
return -EINVAL;
|
||||
|
||||
idx += BMA400_ACC_ODR_MIN_RAW + 1;
|
||||
} else if (hz == BMA400_ACC_ODR_MIN_HZ && uhz == 500000) {
|
||||
idx = BMA400_ACC_ODR_MIN_RAW;
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = regmap_read(data->regmap, BMA400_ACC_CONFIG1_REG, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* preserve the range and normal mode osr */
|
||||
odr = (~BMA400_ACC_ODR_MASK & val) | idx;
|
||||
|
||||
ret = regmap_write(data->regmap, BMA400_ACC_CONFIG1_REG, odr);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
bma400_output_data_rate_from_raw(idx, &data->sample_freq.hz,
|
||||
&data->sample_freq.uhz);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bma400_get_accel_oversampling_ratio(struct bma400_data *data)
|
||||
{
|
||||
unsigned int val;
|
||||
unsigned int osr;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* The oversampling ratio is stored in a different register
|
||||
* based on the power-mode. In normal mode the OSR is stored
|
||||
* in ACC_CONFIG1. In low-power mode it is stored in
|
||||
* ACC_CONFIG0.
|
||||
*/
|
||||
switch (data->power_mode) {
|
||||
case POWER_MODE_LOW:
|
||||
ret = regmap_read(data->regmap, BMA400_ACC_CONFIG0_REG, &val);
|
||||
if (ret) {
|
||||
data->oversampling_ratio = -1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
osr = (val & BMA400_LP_OSR_MASK) >> BMA400_LP_OSR_SHIFT;
|
||||
|
||||
data->oversampling_ratio = osr;
|
||||
return 0;
|
||||
case POWER_MODE_NORMAL:
|
||||
ret = regmap_read(data->regmap, BMA400_ACC_CONFIG1_REG, &val);
|
||||
if (ret) {
|
||||
data->oversampling_ratio = -1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
osr = (val & BMA400_NP_OSR_MASK) >> BMA400_NP_OSR_SHIFT;
|
||||
|
||||
data->oversampling_ratio = osr;
|
||||
return 0;
|
||||
case POWER_MODE_SLEEP:
|
||||
data->oversampling_ratio = 0;
|
||||
return 0;
|
||||
default:
|
||||
data->oversampling_ratio = -1;
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int bma400_set_accel_oversampling_ratio(struct bma400_data *data,
|
||||
int val)
|
||||
{
|
||||
unsigned int acc_config;
|
||||
int ret;
|
||||
|
||||
if (val & ~BMA400_TWO_BITS_MASK)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* The oversampling ratio is stored in a different register
|
||||
* based on the power-mode.
|
||||
*/
|
||||
switch (data->power_mode) {
|
||||
case POWER_MODE_LOW:
|
||||
ret = regmap_read(data->regmap, BMA400_ACC_CONFIG0_REG,
|
||||
&acc_config);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_write(data->regmap, BMA400_ACC_CONFIG0_REG,
|
||||
(acc_config & ~BMA400_LP_OSR_MASK) |
|
||||
(val << BMA400_LP_OSR_SHIFT));
|
||||
if (ret) {
|
||||
dev_err(data->dev, "Failed to write out OSR\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
data->oversampling_ratio = val;
|
||||
return 0;
|
||||
case POWER_MODE_NORMAL:
|
||||
ret = regmap_read(data->regmap, BMA400_ACC_CONFIG1_REG,
|
||||
&acc_config);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_write(data->regmap, BMA400_ACC_CONFIG1_REG,
|
||||
(acc_config & ~BMA400_NP_OSR_MASK) |
|
||||
(val << BMA400_NP_OSR_SHIFT));
|
||||
if (ret) {
|
||||
dev_err(data->dev, "Failed to write out OSR\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
data->oversampling_ratio = val;
|
||||
return 0;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bma400_accel_scale_to_raw(struct bma400_data *data,
|
||||
unsigned int val)
|
||||
{
|
||||
int raw;
|
||||
|
||||
if (val == 0)
|
||||
return -EINVAL;
|
||||
|
||||
/* Note this works because BMA400_SCALE_MIN is odd */
|
||||
raw = __ffs(val);
|
||||
|
||||
if (val >> raw != BMA400_SCALE_MIN)
|
||||
return -EINVAL;
|
||||
|
||||
return raw;
|
||||
}
|
||||
|
||||
static int bma400_get_accel_scale(struct bma400_data *data)
|
||||
{
|
||||
unsigned int raw_scale;
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(data->regmap, BMA400_ACC_CONFIG1_REG, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
raw_scale = (val & BMA400_ACC_SCALE_MASK) >> BMA400_SCALE_SHIFT;
|
||||
if (raw_scale > BMA400_TWO_BITS_MASK)
|
||||
return -EINVAL;
|
||||
|
||||
data->scale = BMA400_SCALE_MIN << raw_scale;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bma400_set_accel_scale(struct bma400_data *data, unsigned int val)
|
||||
{
|
||||
unsigned int acc_config;
|
||||
int raw;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(data->regmap, BMA400_ACC_CONFIG1_REG, &acc_config);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
raw = bma400_accel_scale_to_raw(data, val);
|
||||
if (raw < 0)
|
||||
return raw;
|
||||
|
||||
ret = regmap_write(data->regmap, BMA400_ACC_CONFIG1_REG,
|
||||
(acc_config & ~BMA400_ACC_SCALE_MASK) |
|
||||
(raw << BMA400_SCALE_SHIFT));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
data->scale = val;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bma400_get_power_mode(struct bma400_data *data)
|
||||
{
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(data->regmap, BMA400_STATUS_REG, &val);
|
||||
if (ret) {
|
||||
dev_err(data->dev, "Failed to read status register\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
data->power_mode = (val >> 1) & BMA400_TWO_BITS_MASK;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bma400_set_power_mode(struct bma400_data *data,
|
||||
enum bma400_power_mode mode)
|
||||
{
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(data->regmap, BMA400_ACC_CONFIG0_REG, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (data->power_mode == mode)
|
||||
return 0;
|
||||
|
||||
if (mode == POWER_MODE_INVALID)
|
||||
return -EINVAL;
|
||||
|
||||
/* Preserve the low-power oversample ratio etc */
|
||||
ret = regmap_write(data->regmap, BMA400_ACC_CONFIG0_REG,
|
||||
mode | (val & ~BMA400_TWO_BITS_MASK));
|
||||
if (ret) {
|
||||
dev_err(data->dev, "Failed to write to power-mode\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
data->power_mode = mode;
|
||||
|
||||
/*
|
||||
* Update our cached osr and odr based on the new
|
||||
* power-mode.
|
||||
*/
|
||||
bma400_get_accel_output_data_rate(data);
|
||||
bma400_get_accel_oversampling_ratio(data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void bma400_init_tables(void)
|
||||
{
|
||||
int raw;
|
||||
int i;
|
||||
|
||||
for (i = 0; i + 1 < ARRAY_SIZE(bma400_sample_freqs); i += 2) {
|
||||
raw = (i / 2) + 5;
|
||||
bma400_output_data_rate_from_raw(raw, &bma400_sample_freqs[i],
|
||||
&bma400_sample_freqs[i + 1]);
|
||||
}
|
||||
|
||||
for (i = 0; i + 1 < ARRAY_SIZE(bma400_scales); i += 2) {
|
||||
raw = i / 2;
|
||||
bma400_scales[i] = 0;
|
||||
bma400_scales[i + 1] = BMA400_SCALE_MIN << raw;
|
||||
}
|
||||
}
|
||||
|
||||
static int bma400_init(struct bma400_data *data)
|
||||
{
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
/* Try to read chip_id register. It must return 0x90. */
|
||||
ret = regmap_read(data->regmap, BMA400_CHIP_ID_REG, &val);
|
||||
if (ret) {
|
||||
dev_err(data->dev, "Failed to read chip id register\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (val != BMA400_ID_REG_VAL) {
|
||||
dev_err(data->dev, "Chip ID mismatch\n");
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
data->regulators[BMA400_VDD_REGULATOR].supply = "vdd";
|
||||
data->regulators[BMA400_VDDIO_REGULATOR].supply = "vddio";
|
||||
ret = devm_regulator_bulk_get(data->dev,
|
||||
ARRAY_SIZE(data->regulators),
|
||||
data->regulators);
|
||||
if (ret) {
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(data->dev,
|
||||
"Failed to get regulators: %d\n",
|
||||
ret);
|
||||
|
||||
goto out;
|
||||
}
|
||||
ret = regulator_bulk_enable(ARRAY_SIZE(data->regulators),
|
||||
data->regulators);
|
||||
if (ret) {
|
||||
dev_err(data->dev, "Failed to enable regulators: %d\n",
|
||||
ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = bma400_get_power_mode(data);
|
||||
if (ret) {
|
||||
dev_err(data->dev, "Failed to get the initial power-mode\n");
|
||||
goto err_reg_disable;
|
||||
}
|
||||
|
||||
if (data->power_mode != POWER_MODE_NORMAL) {
|
||||
ret = bma400_set_power_mode(data, POWER_MODE_NORMAL);
|
||||
if (ret) {
|
||||
dev_err(data->dev, "Failed to wake up the device\n");
|
||||
goto err_reg_disable;
|
||||
}
|
||||
/*
|
||||
* TODO: The datasheet waits 1500us here in the example, but
|
||||
* lists 2/ODR as the wakeup time.
|
||||
*/
|
||||
usleep_range(1500, 2000);
|
||||
}
|
||||
|
||||
bma400_init_tables();
|
||||
|
||||
ret = bma400_get_accel_output_data_rate(data);
|
||||
if (ret)
|
||||
goto err_reg_disable;
|
||||
|
||||
ret = bma400_get_accel_oversampling_ratio(data);
|
||||
if (ret)
|
||||
goto err_reg_disable;
|
||||
|
||||
ret = bma400_get_accel_scale(data);
|
||||
if (ret)
|
||||
goto err_reg_disable;
|
||||
|
||||
/*
|
||||
* Once the interrupt engine is supported we might use the
|
||||
* data_src_reg, but for now ensure this is set to the
|
||||
* variable ODR filter selectable by the sample frequency
|
||||
* channel.
|
||||
*/
|
||||
return regmap_write(data->regmap, BMA400_ACC_CONFIG2_REG, 0x00);
|
||||
|
||||
err_reg_disable:
|
||||
regulator_bulk_disable(ARRAY_SIZE(data->regulators),
|
||||
data->regulators);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bma400_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val,
|
||||
int *val2, long mask)
|
||||
{
|
||||
struct bma400_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_PROCESSED:
|
||||
mutex_lock(&data->mutex);
|
||||
ret = bma400_get_temp_reg(data, val, val2);
|
||||
mutex_unlock(&data->mutex);
|
||||
return ret;
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
mutex_lock(&data->mutex);
|
||||
ret = bma400_get_accel_reg(data, chan, val);
|
||||
mutex_unlock(&data->mutex);
|
||||
return ret;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
switch (chan->type) {
|
||||
case IIO_ACCEL:
|
||||
if (data->sample_freq.hz < 0)
|
||||
return -EINVAL;
|
||||
|
||||
*val = data->sample_freq.hz;
|
||||
*val2 = data->sample_freq.uhz;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
case IIO_TEMP:
|
||||
/*
|
||||
* Runs at a fixed sampling frequency. See Section 4.4
|
||||
* of the datasheet.
|
||||
*/
|
||||
*val = 6;
|
||||
*val2 = 250000;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = 0;
|
||||
*val2 = data->scale;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
|
||||
/*
|
||||
* TODO: We could avoid this logic and returning -EINVAL here if
|
||||
* we set both the low-power and normal mode OSR registers when
|
||||
* we configure the device.
|
||||
*/
|
||||
if (data->oversampling_ratio < 0)
|
||||
return -EINVAL;
|
||||
|
||||
*val = data->oversampling_ratio;
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int bma400_read_avail(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
const int **vals, int *type, int *length,
|
||||
long mask)
|
||||
{
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*type = IIO_VAL_INT_PLUS_MICRO;
|
||||
*vals = bma400_scales;
|
||||
*length = ARRAY_SIZE(bma400_scales);
|
||||
return IIO_AVAIL_LIST;
|
||||
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
|
||||
*type = IIO_VAL_INT;
|
||||
*vals = bma400_osr_range;
|
||||
*length = ARRAY_SIZE(bma400_osr_range);
|
||||
return IIO_AVAIL_RANGE;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
*type = IIO_VAL_INT_PLUS_MICRO;
|
||||
*vals = bma400_sample_freqs;
|
||||
*length = ARRAY_SIZE(bma400_sample_freqs);
|
||||
return IIO_AVAIL_LIST;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int bma400_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int val, int val2,
|
||||
long mask)
|
||||
{
|
||||
struct bma400_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
/*
|
||||
* The sample frequency is readonly for the temperature
|
||||
* register and a fixed value in low-power mode.
|
||||
*/
|
||||
if (chan->type != IIO_ACCEL)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&data->mutex);
|
||||
ret = bma400_set_accel_output_data_rate(data, val, val2);
|
||||
mutex_unlock(&data->mutex);
|
||||
return ret;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
if (val != 0 ||
|
||||
val2 < BMA400_SCALE_MIN || val2 > BMA400_SCALE_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&data->mutex);
|
||||
ret = bma400_set_accel_scale(data, val2);
|
||||
mutex_unlock(&data->mutex);
|
||||
return ret;
|
||||
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
|
||||
mutex_lock(&data->mutex);
|
||||
ret = bma400_set_accel_oversampling_ratio(data, val);
|
||||
mutex_unlock(&data->mutex);
|
||||
return ret;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int bma400_write_raw_get_fmt(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
long mask)
|
||||
{
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct iio_info bma400_info = {
|
||||
.read_raw = bma400_read_raw,
|
||||
.read_avail = bma400_read_avail,
|
||||
.write_raw = bma400_write_raw,
|
||||
.write_raw_get_fmt = bma400_write_raw_get_fmt,
|
||||
};
|
||||
|
||||
int bma400_probe(struct device *dev, struct regmap *regmap, const char *name)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct bma400_data *data;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
data = iio_priv(indio_dev);
|
||||
data->regmap = regmap;
|
||||
data->dev = dev;
|
||||
|
||||
ret = bma400_init(data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = iio_read_mount_matrix(dev, "mount-matrix", &data->orientation);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mutex_init(&data->mutex);
|
||||
indio_dev->dev.parent = dev;
|
||||
indio_dev->name = name;
|
||||
indio_dev->info = &bma400_info;
|
||||
indio_dev->channels = bma400_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(bma400_channels);
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
dev_set_drvdata(dev, indio_dev);
|
||||
|
||||
return iio_device_register(indio_dev);
|
||||
}
|
||||
EXPORT_SYMBOL(bma400_probe);
|
||||
|
||||
int bma400_remove(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct bma400_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&data->mutex);
|
||||
ret = bma400_set_power_mode(data, POWER_MODE_SLEEP);
|
||||
mutex_unlock(&data->mutex);
|
||||
|
||||
regulator_bulk_disable(ARRAY_SIZE(data->regulators),
|
||||
data->regulators);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(bma400_remove);
|
||||
|
||||
MODULE_AUTHOR("Dan Robertson <dan@dlrobertson.com>");
|
||||
MODULE_DESCRIPTION("Bosch BMA400 triaxial acceleration sensor core");
|
||||
MODULE_LICENSE("GPL");
|
61
drivers/iio/accel/bma400_i2c.c
Normal file
61
drivers/iio/accel/bma400_i2c.c
Normal file
@ -0,0 +1,61 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* I2C IIO driver for Bosch BMA400 triaxial acceleration sensor.
|
||||
*
|
||||
* Copyright 2019 Dan Robertson <dan@dlrobertson.com>
|
||||
*
|
||||
* I2C address is either 0x14 or 0x15 depending on SDO
|
||||
*/
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include "bma400.h"
|
||||
|
||||
static int bma400_i2c_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct regmap *regmap;
|
||||
|
||||
regmap = devm_regmap_init_i2c(client, &bma400_regmap_config);
|
||||
if (IS_ERR(regmap)) {
|
||||
dev_err(&client->dev, "failed to create regmap\n");
|
||||
return PTR_ERR(regmap);
|
||||
}
|
||||
|
||||
return bma400_probe(&client->dev, regmap, id->name);
|
||||
}
|
||||
|
||||
static int bma400_i2c_remove(struct i2c_client *client)
|
||||
{
|
||||
return bma400_remove(&client->dev);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id bma400_i2c_ids[] = {
|
||||
{ "bma400", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, bma400_i2c_ids);
|
||||
|
||||
static const struct of_device_id bma400_of_i2c_match[] = {
|
||||
{ .compatible = "bosch,bma400" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, bma400_of_i2c_match);
|
||||
|
||||
static struct i2c_driver bma400_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "bma400",
|
||||
.of_match_table = bma400_of_i2c_match,
|
||||
},
|
||||
.probe = bma400_i2c_probe,
|
||||
.remove = bma400_i2c_remove,
|
||||
.id_table = bma400_i2c_ids,
|
||||
};
|
||||
|
||||
module_i2c_driver(bma400_i2c_driver);
|
||||
|
||||
MODULE_AUTHOR("Dan Robertson <dan@dlrobertson.com>");
|
||||
MODULE_DESCRIPTION("Bosch BMA400 triaxial acceleration sensor (I2C)");
|
||||
MODULE_LICENSE("GPL");
|
@ -130,6 +130,7 @@ struct kxcjk1013_data {
|
||||
struct i2c_client *client;
|
||||
struct iio_trigger *dready_trig;
|
||||
struct iio_trigger *motion_trig;
|
||||
struct iio_mount_matrix orientation;
|
||||
struct mutex mutex;
|
||||
s16 buffer[8];
|
||||
u8 odr_bits;
|
||||
@ -983,6 +984,20 @@ static const struct iio_event_spec kxcjk1013_event = {
|
||||
BIT(IIO_EV_INFO_PERIOD)
|
||||
};
|
||||
|
||||
static const struct iio_mount_matrix *
|
||||
kxcjk1013_get_mount_matrix(const struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan)
|
||||
{
|
||||
struct kxcjk1013_data *data = iio_priv(indio_dev);
|
||||
|
||||
return &data->orientation;
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec_ext_info kxcjk1013_ext_info[] = {
|
||||
IIO_MOUNT_MATRIX(IIO_SHARED_BY_TYPE, kxcjk1013_get_mount_matrix),
|
||||
{ }
|
||||
};
|
||||
|
||||
#define KXCJK1013_CHANNEL(_axis) { \
|
||||
.type = IIO_ACCEL, \
|
||||
.modified = 1, \
|
||||
@ -999,6 +1014,7 @@ static const struct iio_event_spec kxcjk1013_event = {
|
||||
.endianness = IIO_LE, \
|
||||
}, \
|
||||
.event_spec = &kxcjk1013_event, \
|
||||
.ext_info = kxcjk1013_ext_info, \
|
||||
.num_event_specs = 1 \
|
||||
}
|
||||
|
||||
@ -1267,11 +1283,18 @@ static int kxcjk1013_probe(struct i2c_client *client,
|
||||
data->client = client;
|
||||
|
||||
pdata = dev_get_platdata(&client->dev);
|
||||
if (pdata)
|
||||
if (pdata) {
|
||||
data->active_high_intr = pdata->active_high_intr;
|
||||
else
|
||||
data->orientation = pdata->orientation;
|
||||
} else {
|
||||
data->active_high_intr = true; /* default polarity */
|
||||
|
||||
ret = iio_read_mount_matrix(&client->dev, "mount-matrix",
|
||||
&data->orientation);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (id) {
|
||||
data->chipset = (enum kx_chipset)(id->driver_data);
|
||||
name = id->name;
|
||||
|
@ -64,7 +64,7 @@ enum st_accel_type {
|
||||
* struct st_sensors_platform_data - default accel platform data
|
||||
* @drdy_int_pin: default accel DRDY is available on INT1 pin.
|
||||
*/
|
||||
static const struct st_sensors_platform_data default_accel_pdata = {
|
||||
static __maybe_unused const struct st_sensors_platform_data default_accel_pdata = {
|
||||
.drdy_int_pin = 1,
|
||||
};
|
||||
|
||||
|
@ -18,7 +18,6 @@
|
||||
#include <linux/iio/common/st_sensors_i2c.h>
|
||||
#include "st_accel.h"
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id st_accel_of_match[] = {
|
||||
{
|
||||
/* An older compatible */
|
||||
@ -108,9 +107,6 @@ static const struct of_device_id st_accel_of_match[] = {
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, st_accel_of_match);
|
||||
#else
|
||||
#define st_accel_of_match NULL
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
static const struct acpi_device_id st_accel_acpi_match[] = {
|
||||
@ -119,8 +115,6 @@ static const struct acpi_device_id st_accel_acpi_match[] = {
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, st_accel_acpi_match);
|
||||
#else
|
||||
#define st_accel_acpi_match NULL
|
||||
#endif
|
||||
|
||||
static const struct i2c_device_id st_accel_id_table[] = {
|
||||
@ -195,7 +189,7 @@ static int st_accel_i2c_remove(struct i2c_client *client)
|
||||
static struct i2c_driver st_accel_driver = {
|
||||
.driver = {
|
||||
.name = "st-accel-i2c",
|
||||
.of_match_table = of_match_ptr(st_accel_of_match),
|
||||
.of_match_table = st_accel_of_match,
|
||||
.acpi_match_table = ACPI_PTR(st_accel_acpi_match),
|
||||
},
|
||||
.probe_new = st_accel_i2c_probe,
|
||||
|
@ -17,7 +17,6 @@
|
||||
#include <linux/iio/common/st_sensors_spi.h>
|
||||
#include "st_accel.h"
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
/*
|
||||
* For new single-chip sensors use <device_name> as compatible string.
|
||||
* For old single-chip devices keep <device_name>-accel to maintain
|
||||
@ -96,9 +95,6 @@ static const struct of_device_id st_accel_of_match[] = {
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, st_accel_of_match);
|
||||
#else
|
||||
#define st_accel_of_match NULL
|
||||
#endif
|
||||
|
||||
static int st_accel_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
@ -107,8 +103,7 @@ static int st_accel_spi_probe(struct spi_device *spi)
|
||||
struct iio_dev *indio_dev;
|
||||
int err;
|
||||
|
||||
st_sensors_of_name_probe(&spi->dev, st_accel_of_match,
|
||||
spi->modalias, sizeof(spi->modalias));
|
||||
st_sensors_dev_name_probe(&spi->dev, spi->modalias, sizeof(spi->modalias));
|
||||
|
||||
settings = st_accel_get_settings(spi->modalias);
|
||||
if (!settings) {
|
||||
@ -166,7 +161,7 @@ MODULE_DEVICE_TABLE(spi, st_accel_id_table);
|
||||
static struct spi_driver st_accel_driver = {
|
||||
.driver = {
|
||||
.name = "st-accel-spi",
|
||||
.of_match_table = of_match_ptr(st_accel_of_match),
|
||||
.of_match_table = st_accel_of_match,
|
||||
},
|
||||
.probe = st_accel_spi_probe,
|
||||
.remove = st_accel_spi_remove,
|
||||
|
@ -21,6 +21,13 @@ config AD_SIGMA_DELTA
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
|
||||
config AD7091R5
|
||||
tristate "Analog Devices AD7091R5 ADC Driver"
|
||||
depends on I2C
|
||||
select REGMAP_I2C
|
||||
help
|
||||
Say yes here to build support for Analog Devices AD7091R-5 ADC.
|
||||
|
||||
config AD7124
|
||||
tristate "Analog Devices AD7124 and similar sigma-delta ADCs driver"
|
||||
depends on SPI_MASTER
|
||||
@ -523,6 +530,16 @@ config LTC2485
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called ltc2485.
|
||||
|
||||
config LTC2496
|
||||
tristate "Linear Technology LTC2496 ADC driver"
|
||||
depends on SPI
|
||||
help
|
||||
Say yes here to build support for Linear Technology LTC2496
|
||||
16-Bit 8-/16-Channel Delta Sigma ADC.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called ltc2496.
|
||||
|
||||
config LTC2497
|
||||
tristate "Linear Technology LTC2497 ADC driver"
|
||||
depends on I2C
|
||||
|
@ -6,6 +6,7 @@
|
||||
# When adding new entries keep the list in alphabetical order
|
||||
obj-$(CONFIG_AB8500_GPADC) += ab8500-gpadc.o
|
||||
obj-$(CONFIG_AD_SIGMA_DELTA) += ad_sigma_delta.o
|
||||
obj-$(CONFIG_AD7091R5) += ad7091r5.o ad7091r-base.o
|
||||
obj-$(CONFIG_AD7124) += ad7124.o
|
||||
obj-$(CONFIG_AD7266) += ad7266.o
|
||||
obj-$(CONFIG_AD7291) += ad7291.o
|
||||
@ -50,7 +51,8 @@ obj-$(CONFIG_LPC18XX_ADC) += lpc18xx_adc.o
|
||||
obj-$(CONFIG_LPC32XX_ADC) += lpc32xx_adc.o
|
||||
obj-$(CONFIG_LTC2471) += ltc2471.o
|
||||
obj-$(CONFIG_LTC2485) += ltc2485.o
|
||||
obj-$(CONFIG_LTC2497) += ltc2497.o
|
||||
obj-$(CONFIG_LTC2496) += ltc2496.o ltc2497-core.o
|
||||
obj-$(CONFIG_LTC2497) += ltc2497.o ltc2497-core.o
|
||||
obj-$(CONFIG_MAX1027) += max1027.o
|
||||
obj-$(CONFIG_MAX11100) += max11100.o
|
||||
obj-$(CONFIG_MAX1118) += max1118.o
|
||||
|
298
drivers/iio/adc/ad7091r-base.c
Normal file
298
drivers/iio/adc/ad7091r-base.c
Normal file
@ -0,0 +1,298 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* AD7091RX Analog to Digital converter driver
|
||||
*
|
||||
* Copyright 2014-2019 Analog Devices Inc.
|
||||
*/
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/iio/events.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#include "ad7091r-base.h"
|
||||
|
||||
#define AD7091R_REG_RESULT 0
|
||||
#define AD7091R_REG_CHANNEL 1
|
||||
#define AD7091R_REG_CONF 2
|
||||
#define AD7091R_REG_ALERT 3
|
||||
#define AD7091R_REG_CH_LOW_LIMIT(ch) ((ch) * 3 + 4)
|
||||
#define AD7091R_REG_CH_HIGH_LIMIT(ch) ((ch) * 3 + 5)
|
||||
#define AD7091R_REG_CH_HYSTERESIS(ch) ((ch) * 3 + 6)
|
||||
|
||||
/* AD7091R_REG_RESULT */
|
||||
#define AD7091R_REG_RESULT_CH_ID(x) (((x) >> 13) & 0x3)
|
||||
#define AD7091R_REG_RESULT_CONV_RESULT(x) ((x) & 0xfff)
|
||||
|
||||
/* AD7091R_REG_CONF */
|
||||
#define AD7091R_REG_CONF_AUTO BIT(8)
|
||||
#define AD7091R_REG_CONF_CMD BIT(10)
|
||||
|
||||
#define AD7091R_REG_CONF_MODE_MASK \
|
||||
(AD7091R_REG_CONF_AUTO | AD7091R_REG_CONF_CMD)
|
||||
|
||||
enum ad7091r_mode {
|
||||
AD7091R_MODE_SAMPLE,
|
||||
AD7091R_MODE_COMMAND,
|
||||
AD7091R_MODE_AUTOCYCLE,
|
||||
};
|
||||
|
||||
struct ad7091r_state {
|
||||
struct device *dev;
|
||||
struct regmap *map;
|
||||
struct regulator *vref;
|
||||
const struct ad7091r_chip_info *chip_info;
|
||||
enum ad7091r_mode mode;
|
||||
struct mutex lock; /*lock to prevent concurent reads */
|
||||
};
|
||||
|
||||
static int ad7091r_set_mode(struct ad7091r_state *st, enum ad7091r_mode mode)
|
||||
{
|
||||
int ret, conf;
|
||||
|
||||
switch (mode) {
|
||||
case AD7091R_MODE_SAMPLE:
|
||||
conf = 0;
|
||||
break;
|
||||
case AD7091R_MODE_COMMAND:
|
||||
conf = AD7091R_REG_CONF_CMD;
|
||||
break;
|
||||
case AD7091R_MODE_AUTOCYCLE:
|
||||
conf = AD7091R_REG_CONF_AUTO;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = regmap_update_bits(st->map, AD7091R_REG_CONF,
|
||||
AD7091R_REG_CONF_MODE_MASK, conf);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
st->mode = mode;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ad7091r_set_channel(struct ad7091r_state *st, unsigned int channel)
|
||||
{
|
||||
unsigned int dummy;
|
||||
int ret;
|
||||
|
||||
/* AD7091R_REG_CHANNEL specified which channels to be converted */
|
||||
ret = regmap_write(st->map, AD7091R_REG_CHANNEL,
|
||||
BIT(channel) | (BIT(channel) << 8));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* There is a latency of one conversion before the channel conversion
|
||||
* sequence is updated
|
||||
*/
|
||||
return regmap_read(st->map, AD7091R_REG_RESULT, &dummy);
|
||||
}
|
||||
|
||||
static int ad7091r_read_one(struct iio_dev *iio_dev,
|
||||
unsigned int channel, unsigned int *read_val)
|
||||
{
|
||||
struct ad7091r_state *st = iio_priv(iio_dev);
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
ret = ad7091r_set_channel(st, channel);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_read(st->map, AD7091R_REG_RESULT, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (AD7091R_REG_RESULT_CH_ID(val) != channel)
|
||||
return -EIO;
|
||||
|
||||
*read_val = AD7091R_REG_RESULT_CONV_RESULT(val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ad7091r_read_raw(struct iio_dev *iio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long m)
|
||||
{
|
||||
struct ad7091r_state *st = iio_priv(iio_dev);
|
||||
unsigned int read_val;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&st->lock);
|
||||
|
||||
switch (m) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (st->mode != AD7091R_MODE_COMMAND) {
|
||||
ret = -EBUSY;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
ret = ad7091r_read_one(iio_dev, chan->channel, &read_val);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
|
||||
*val = read_val;
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
if (st->vref) {
|
||||
ret = regulator_get_voltage(st->vref);
|
||||
if (ret < 0)
|
||||
goto unlock;
|
||||
|
||||
*val = ret / 1000;
|
||||
} else {
|
||||
*val = st->chip_info->vref_mV;
|
||||
}
|
||||
|
||||
*val2 = chan->scan_type.realbits;
|
||||
ret = IIO_VAL_FRACTIONAL_LOG2;
|
||||
break;
|
||||
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&st->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct iio_info ad7091r_info = {
|
||||
.read_raw = ad7091r_read_raw,
|
||||
};
|
||||
|
||||
static irqreturn_t ad7091r_event_handler(int irq, void *private)
|
||||
{
|
||||
struct ad7091r_state *st = (struct ad7091r_state *) private;
|
||||
struct iio_dev *iio_dev = dev_get_drvdata(st->dev);
|
||||
unsigned int i, read_val;
|
||||
int ret;
|
||||
s64 timestamp = iio_get_time_ns(iio_dev);
|
||||
|
||||
ret = regmap_read(st->map, AD7091R_REG_ALERT, &read_val);
|
||||
if (ret)
|
||||
return IRQ_HANDLED;
|
||||
|
||||
for (i = 0; i < st->chip_info->num_channels; i++) {
|
||||
if (read_val & BIT(i * 2))
|
||||
iio_push_event(iio_dev,
|
||||
IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, i,
|
||||
IIO_EV_TYPE_THRESH,
|
||||
IIO_EV_DIR_RISING), timestamp);
|
||||
if (read_val & BIT(i * 2 + 1))
|
||||
iio_push_event(iio_dev,
|
||||
IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, i,
|
||||
IIO_EV_TYPE_THRESH,
|
||||
IIO_EV_DIR_FALLING), timestamp);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void ad7091r_remove(void *data)
|
||||
{
|
||||
struct ad7091r_state *st = data;
|
||||
|
||||
regulator_disable(st->vref);
|
||||
}
|
||||
|
||||
int ad7091r_probe(struct device *dev, const char *name,
|
||||
const struct ad7091r_chip_info *chip_info,
|
||||
struct regmap *map, int irq)
|
||||
{
|
||||
struct iio_dev *iio_dev;
|
||||
struct ad7091r_state *st;
|
||||
int ret;
|
||||
|
||||
iio_dev = devm_iio_device_alloc(dev, sizeof(*st));
|
||||
if (!iio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
st = iio_priv(iio_dev);
|
||||
st->dev = dev;
|
||||
st->chip_info = chip_info;
|
||||
st->map = map;
|
||||
|
||||
iio_dev->dev.parent = dev;
|
||||
iio_dev->name = name;
|
||||
iio_dev->info = &ad7091r_info;
|
||||
iio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
iio_dev->num_channels = chip_info->num_channels;
|
||||
iio_dev->channels = chip_info->channels;
|
||||
|
||||
if (irq) {
|
||||
ret = devm_request_threaded_irq(dev, irq, NULL,
|
||||
ad7091r_event_handler,
|
||||
IRQF_TRIGGER_FALLING | IRQF_ONESHOT, name, st);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
st->vref = devm_regulator_get_optional(dev, "vref");
|
||||
if (IS_ERR(st->vref)) {
|
||||
if (PTR_ERR(st->vref) == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
st->vref = NULL;
|
||||
} else {
|
||||
ret = regulator_enable(st->vref);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = devm_add_action_or_reset(dev, ad7091r_remove, st);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Use command mode by default to convert only desired channels*/
|
||||
ret = ad7091r_set_mode(st, AD7091R_MODE_COMMAND);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return devm_iio_device_register(dev, iio_dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ad7091r_probe);
|
||||
|
||||
static bool ad7091r_writeable_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case AD7091R_REG_RESULT:
|
||||
case AD7091R_REG_ALERT:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static bool ad7091r_volatile_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case AD7091R_REG_RESULT:
|
||||
case AD7091R_REG_ALERT:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const struct regmap_config ad7091r_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 16,
|
||||
.writeable_reg = ad7091r_writeable_reg,
|
||||
.volatile_reg = ad7091r_volatile_reg,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(ad7091r_regmap_config);
|
||||
|
||||
MODULE_AUTHOR("Beniamin Bia <beniamin.bia@analog.com>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD7091Rx multi-channel converters");
|
||||
MODULE_LICENSE("GPL v2");
|
26
drivers/iio/adc/ad7091r-base.h
Normal file
26
drivers/iio/adc/ad7091r-base.h
Normal file
@ -0,0 +1,26 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* AD7091RX Analog to Digital converter driver
|
||||
*
|
||||
* Copyright 2014-2019 Analog Devices Inc.
|
||||
*/
|
||||
|
||||
#ifndef __DRIVERS_IIO_ADC_AD7091R_BASE_H__
|
||||
#define __DRIVERS_IIO_ADC_AD7091R_BASE_H__
|
||||
|
||||
struct device;
|
||||
struct ad7091r_state;
|
||||
|
||||
struct ad7091r_chip_info {
|
||||
unsigned int num_channels;
|
||||
const struct iio_chan_spec *channels;
|
||||
unsigned int vref_mV;
|
||||
};
|
||||
|
||||
extern const struct regmap_config ad7091r_regmap_config;
|
||||
|
||||
int ad7091r_probe(struct device *dev, const char *name,
|
||||
const struct ad7091r_chip_info *chip_info,
|
||||
struct regmap *map, int irq);
|
||||
|
||||
#endif /* __DRIVERS_IIO_ADC_AD7091R_BASE_H__ */
|
113
drivers/iio/adc/ad7091r5.c
Normal file
113
drivers/iio/adc/ad7091r5.c
Normal file
@ -0,0 +1,113 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* AD7091R5 Analog to Digital converter driver
|
||||
*
|
||||
* Copyright 2014-2019 Analog Devices Inc.
|
||||
*/
|
||||
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include "ad7091r-base.h"
|
||||
|
||||
static const struct iio_event_spec ad7091r5_events[] = {
|
||||
{
|
||||
.type = IIO_EV_TYPE_THRESH,
|
||||
.dir = IIO_EV_DIR_RISING,
|
||||
.mask_separate = BIT(IIO_EV_INFO_VALUE) |
|
||||
BIT(IIO_EV_INFO_ENABLE),
|
||||
},
|
||||
{
|
||||
.type = IIO_EV_TYPE_THRESH,
|
||||
.dir = IIO_EV_DIR_FALLING,
|
||||
.mask_separate = BIT(IIO_EV_INFO_VALUE) |
|
||||
BIT(IIO_EV_INFO_ENABLE),
|
||||
},
|
||||
{
|
||||
.type = IIO_EV_TYPE_THRESH,
|
||||
.dir = IIO_EV_DIR_EITHER,
|
||||
.mask_separate = BIT(IIO_EV_INFO_HYSTERESIS),
|
||||
},
|
||||
};
|
||||
|
||||
#define AD7091R_CHANNEL(idx, bits, ev, num_ev) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.indexed = 1, \
|
||||
.channel = idx, \
|
||||
.event_spec = ev, \
|
||||
.num_event_specs = num_ev, \
|
||||
.scan_type.storagebits = 16, \
|
||||
.scan_type.realbits = bits, \
|
||||
}
|
||||
static const struct iio_chan_spec ad7091r5_channels_irq[] = {
|
||||
AD7091R_CHANNEL(0, 12, ad7091r5_events, ARRAY_SIZE(ad7091r5_events)),
|
||||
AD7091R_CHANNEL(1, 12, ad7091r5_events, ARRAY_SIZE(ad7091r5_events)),
|
||||
AD7091R_CHANNEL(2, 12, ad7091r5_events, ARRAY_SIZE(ad7091r5_events)),
|
||||
AD7091R_CHANNEL(3, 12, ad7091r5_events, ARRAY_SIZE(ad7091r5_events)),
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec ad7091r5_channels_noirq[] = {
|
||||
AD7091R_CHANNEL(0, 12, NULL, 0),
|
||||
AD7091R_CHANNEL(1, 12, NULL, 0),
|
||||
AD7091R_CHANNEL(2, 12, NULL, 0),
|
||||
AD7091R_CHANNEL(3, 12, NULL, 0),
|
||||
};
|
||||
|
||||
static const struct ad7091r_chip_info ad7091r5_chip_info_irq = {
|
||||
.channels = ad7091r5_channels_irq,
|
||||
.num_channels = ARRAY_SIZE(ad7091r5_channels_irq),
|
||||
.vref_mV = 2500,
|
||||
};
|
||||
|
||||
static const struct ad7091r_chip_info ad7091r5_chip_info_noirq = {
|
||||
.channels = ad7091r5_channels_noirq,
|
||||
.num_channels = ARRAY_SIZE(ad7091r5_channels_noirq),
|
||||
.vref_mV = 2500,
|
||||
};
|
||||
|
||||
static int ad7091r5_i2c_probe(struct i2c_client *i2c,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
const struct ad7091r_chip_info *chip_info;
|
||||
struct regmap *map = devm_regmap_init_i2c(i2c, &ad7091r_regmap_config);
|
||||
|
||||
if (IS_ERR(map))
|
||||
return PTR_ERR(map);
|
||||
|
||||
if (i2c->irq)
|
||||
chip_info = &ad7091r5_chip_info_irq;
|
||||
else
|
||||
chip_info = &ad7091r5_chip_info_noirq;
|
||||
|
||||
return ad7091r_probe(&i2c->dev, id->name, chip_info, map, i2c->irq);
|
||||
}
|
||||
|
||||
static const struct of_device_id ad7091r5_dt_ids[] = {
|
||||
{ .compatible = "adi,ad7091r5" },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ad7091r5_dt_ids);
|
||||
|
||||
static const struct i2c_device_id ad7091r5_i2c_ids[] = {
|
||||
{"ad7091r5", 0},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ad7091r5_i2c_ids);
|
||||
|
||||
static struct i2c_driver ad7091r5_driver = {
|
||||
.driver = {
|
||||
.name = "ad7091r5",
|
||||
.of_match_table = ad7091r5_dt_ids,
|
||||
},
|
||||
.probe = ad7091r5_i2c_probe,
|
||||
.id_table = ad7091r5_i2c_ids,
|
||||
};
|
||||
module_i2c_driver(ad7091r5_driver);
|
||||
|
||||
MODULE_AUTHOR("Beniamin Bia <beniamin.bia@analog.com>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD7091R5 multi-channel ADC driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -9,6 +9,7 @@
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
@ -224,6 +225,7 @@ static const struct ad_sigma_delta_info ad7124_sigma_delta_info = {
|
||||
.addr_shift = 0,
|
||||
.read_mask = BIT(6),
|
||||
.data_reg = AD7124_DATA,
|
||||
.irq_flags = IRQF_TRIGGER_FALLING,
|
||||
};
|
||||
|
||||
static int ad7124_set_channel_odr(struct ad7124_state *st,
|
||||
|
@ -11,7 +11,7 @@
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
@ -34,7 +34,7 @@ struct ad7266_state {
|
||||
enum ad7266_range range;
|
||||
enum ad7266_mode mode;
|
||||
bool fixed_addr;
|
||||
struct gpio gpios[3];
|
||||
struct gpio_desc *gpios[3];
|
||||
|
||||
/*
|
||||
* DMA (thus cache coherency maintenance) requires the
|
||||
@ -117,7 +117,7 @@ static void ad7266_select_input(struct ad7266_state *st, unsigned int nr)
|
||||
}
|
||||
|
||||
for (i = 0; i < 3; ++i)
|
||||
gpio_set_value(st->gpios[i].gpio, (bool)(nr & BIT(i)));
|
||||
gpiod_set_value(st->gpios[i], (bool)(nr & BIT(i)));
|
||||
}
|
||||
|
||||
static int ad7266_update_scan_mode(struct iio_dev *indio_dev,
|
||||
@ -376,7 +376,7 @@ static void ad7266_init_channels(struct iio_dev *indio_dev)
|
||||
}
|
||||
|
||||
static const char * const ad7266_gpio_labels[] = {
|
||||
"AD0", "AD1", "AD2",
|
||||
"ad0", "ad1", "ad2",
|
||||
};
|
||||
|
||||
static int ad7266_probe(struct spi_device *spi)
|
||||
@ -419,14 +419,14 @@ static int ad7266_probe(struct spi_device *spi)
|
||||
|
||||
if (!st->fixed_addr) {
|
||||
for (i = 0; i < ARRAY_SIZE(st->gpios); ++i) {
|
||||
st->gpios[i].gpio = pdata->addr_gpios[i];
|
||||
st->gpios[i].flags = GPIOF_OUT_INIT_LOW;
|
||||
st->gpios[i].label = ad7266_gpio_labels[i];
|
||||
st->gpios[i] = devm_gpiod_get(&spi->dev,
|
||||
ad7266_gpio_labels[i],
|
||||
GPIOD_OUT_LOW);
|
||||
if (IS_ERR(st->gpios[i])) {
|
||||
ret = PTR_ERR(st->gpios[i]);
|
||||
goto error_disable_reg;
|
||||
}
|
||||
}
|
||||
ret = gpio_request_array(st->gpios,
|
||||
ARRAY_SIZE(st->gpios));
|
||||
if (ret)
|
||||
goto error_disable_reg;
|
||||
}
|
||||
} else {
|
||||
st->fixed_addr = true;
|
||||
@ -465,7 +465,7 @@ static int ad7266_probe(struct spi_device *spi)
|
||||
ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
|
||||
&ad7266_trigger_handler, &iio_triggered_buffer_setup_ops);
|
||||
if (ret)
|
||||
goto error_free_gpios;
|
||||
goto error_disable_reg;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
@ -475,9 +475,6 @@ static int ad7266_probe(struct spi_device *spi)
|
||||
|
||||
error_buffer_cleanup:
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
error_free_gpios:
|
||||
if (!st->fixed_addr)
|
||||
gpio_free_array(st->gpios, ARRAY_SIZE(st->gpios));
|
||||
error_disable_reg:
|
||||
if (!IS_ERR(st->reg))
|
||||
regulator_disable(st->reg);
|
||||
@ -492,8 +489,6 @@ static int ad7266_remove(struct spi_device *spi)
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
if (!st->fixed_addr)
|
||||
gpio_free_array(st->gpios, ARRAY_SIZE(st->gpios));
|
||||
if (!IS_ERR(st->reg))
|
||||
regulator_disable(st->reg);
|
||||
|
||||
|
@ -203,6 +203,7 @@ static const struct ad_sigma_delta_info ad7780_sigma_delta_info = {
|
||||
.set_mode = ad7780_set_mode,
|
||||
.postprocess_sample = ad7780_postprocess_sample,
|
||||
.has_registers = false,
|
||||
.irq_flags = IRQF_TRIGGER_LOW,
|
||||
};
|
||||
|
||||
#define AD7780_CHANNEL(bits, wordsize) \
|
||||
|
@ -205,6 +205,7 @@ static const struct ad_sigma_delta_info ad7791_sigma_delta_info = {
|
||||
.has_registers = true,
|
||||
.addr_shift = 4,
|
||||
.read_mask = BIT(3),
|
||||
.irq_flags = IRQF_TRIGGER_LOW,
|
||||
};
|
||||
|
||||
static int ad7791_read_raw(struct iio_dev *indio_dev,
|
||||
|
@ -206,6 +206,7 @@ static const struct ad_sigma_delta_info ad7793_sigma_delta_info = {
|
||||
.has_registers = true,
|
||||
.addr_shift = 3,
|
||||
.read_mask = BIT(6),
|
||||
.irq_flags = IRQF_TRIGGER_LOW,
|
||||
};
|
||||
|
||||
static const struct ad_sd_calib_data ad7793_calib_arr[6] = {
|
||||
|
@ -43,11 +43,17 @@ enum ad7887_channels {
|
||||
/**
|
||||
* struct ad7887_chip_info - chip specifc information
|
||||
* @int_vref_mv: the internal reference voltage
|
||||
* @channel: channel specification
|
||||
* @channels: channels specification
|
||||
* @num_channels: number of channels
|
||||
* @dual_channels: channels specification in dual mode
|
||||
* @num_dual_channels: number of channels in dual mode
|
||||
*/
|
||||
struct ad7887_chip_info {
|
||||
u16 int_vref_mv;
|
||||
struct iio_chan_spec channel[3];
|
||||
const struct iio_chan_spec *channels;
|
||||
unsigned int num_channels;
|
||||
const struct iio_chan_spec *dual_channels;
|
||||
unsigned int num_dual_channels;
|
||||
};
|
||||
|
||||
struct ad7887_state {
|
||||
@ -183,45 +189,43 @@ static int ad7887_read_raw(struct iio_dev *indio_dev,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
#define AD7887_CHANNEL(x) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = (x), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.address = (x), \
|
||||
.scan_index = (x), \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = 12, \
|
||||
.storagebits = 16, \
|
||||
.shift = 0, \
|
||||
.endianness = IIO_BE, \
|
||||
}, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec ad7887_channels[] = {
|
||||
AD7887_CHANNEL(0),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec ad7887_dual_channels[] = {
|
||||
AD7887_CHANNEL(0),
|
||||
AD7887_CHANNEL(1),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(2),
|
||||
};
|
||||
|
||||
static const struct ad7887_chip_info ad7887_chip_info_tbl[] = {
|
||||
/*
|
||||
* More devices added in future
|
||||
*/
|
||||
[ID_AD7887] = {
|
||||
.channel[0] = {
|
||||
.type = IIO_VOLTAGE,
|
||||
.indexed = 1,
|
||||
.channel = 1,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
|
||||
.address = 1,
|
||||
.scan_index = 1,
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
.realbits = 12,
|
||||
.storagebits = 16,
|
||||
.shift = 0,
|
||||
.endianness = IIO_BE,
|
||||
},
|
||||
},
|
||||
.channel[1] = {
|
||||
.type = IIO_VOLTAGE,
|
||||
.indexed = 1,
|
||||
.channel = 0,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
|
||||
.address = 0,
|
||||
.scan_index = 0,
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
.realbits = 12,
|
||||
.storagebits = 16,
|
||||
.shift = 0,
|
||||
.endianness = IIO_BE,
|
||||
},
|
||||
},
|
||||
.channel[2] = IIO_CHAN_SOFT_TIMESTAMP(2),
|
||||
.channels = ad7887_channels,
|
||||
.num_channels = ARRAY_SIZE(ad7887_channels),
|
||||
.dual_channels = ad7887_dual_channels,
|
||||
.num_dual_channels = ARRAY_SIZE(ad7887_dual_channels),
|
||||
.int_vref_mv = 2500,
|
||||
},
|
||||
};
|
||||
@ -306,11 +310,11 @@ static int ad7887_probe(struct spi_device *spi)
|
||||
spi_message_init(&st->msg[AD7887_CH1]);
|
||||
spi_message_add_tail(&st->xfer[3], &st->msg[AD7887_CH1]);
|
||||
|
||||
indio_dev->channels = st->chip_info->channel;
|
||||
indio_dev->num_channels = 3;
|
||||
indio_dev->channels = st->chip_info->dual_channels;
|
||||
indio_dev->num_channels = st->chip_info->num_dual_channels;
|
||||
} else {
|
||||
indio_dev->channels = &st->chip_info->channel[1];
|
||||
indio_dev->num_channels = 2;
|
||||
indio_dev->channels = st->chip_info->channels;
|
||||
indio_dev->num_channels = st->chip_info->num_channels;
|
||||
}
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
|
||||
|
@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* AD7904/AD7914/AD7923/AD7924 SPI ADC driver
|
||||
* AD7904/AD7914/AD7923/AD7924/AD7908/AD7918/AD7928 SPI ADC driver
|
||||
*
|
||||
* Copyright 2011 Analog Devices Inc (from AD7923 Driver)
|
||||
* Copyright 2012 CS Systemes d'Information
|
||||
@ -29,15 +29,10 @@
|
||||
#define AD7923_PM_MODE_AS (1) /* auto shutdown */
|
||||
#define AD7923_PM_MODE_FS (2) /* full shutdown */
|
||||
#define AD7923_PM_MODE_OPS (3) /* normal operation */
|
||||
#define AD7923_CHANNEL_0 (0) /* analog input 0 */
|
||||
#define AD7923_CHANNEL_1 (1) /* analog input 1 */
|
||||
#define AD7923_CHANNEL_2 (2) /* analog input 2 */
|
||||
#define AD7923_CHANNEL_3 (3) /* analog input 3 */
|
||||
#define AD7923_SEQUENCE_OFF (0) /* no sequence fonction */
|
||||
#define AD7923_SEQUENCE_PROTECT (2) /* no interrupt write cycle */
|
||||
#define AD7923_SEQUENCE_ON (3) /* continuous sequence */
|
||||
|
||||
#define AD7923_MAX_CHAN 4
|
||||
|
||||
#define AD7923_PM_MODE_WRITE(mode) ((mode) << 4) /* write mode */
|
||||
#define AD7923_CHANNEL_WRITE(channel) ((channel) << 6) /* write channel */
|
||||
@ -78,6 +73,9 @@ enum ad7923_id {
|
||||
AD7904,
|
||||
AD7914,
|
||||
AD7924,
|
||||
AD7908,
|
||||
AD7918,
|
||||
AD7928
|
||||
};
|
||||
|
||||
#define AD7923_V_CHAN(index, bits) \
|
||||
@ -106,9 +104,25 @@ const struct iio_chan_spec name ## _channels[] = { \
|
||||
IIO_CHAN_SOFT_TIMESTAMP(4), \
|
||||
}
|
||||
|
||||
#define DECLARE_AD7908_CHANNELS(name, bits) \
|
||||
const struct iio_chan_spec name ## _channels[] = { \
|
||||
AD7923_V_CHAN(0, bits), \
|
||||
AD7923_V_CHAN(1, bits), \
|
||||
AD7923_V_CHAN(2, bits), \
|
||||
AD7923_V_CHAN(3, bits), \
|
||||
AD7923_V_CHAN(4, bits), \
|
||||
AD7923_V_CHAN(5, bits), \
|
||||
AD7923_V_CHAN(6, bits), \
|
||||
AD7923_V_CHAN(7, bits), \
|
||||
IIO_CHAN_SOFT_TIMESTAMP(8), \
|
||||
}
|
||||
|
||||
static DECLARE_AD7923_CHANNELS(ad7904, 8);
|
||||
static DECLARE_AD7923_CHANNELS(ad7914, 10);
|
||||
static DECLARE_AD7923_CHANNELS(ad7924, 12);
|
||||
static DECLARE_AD7908_CHANNELS(ad7908, 8);
|
||||
static DECLARE_AD7908_CHANNELS(ad7918, 10);
|
||||
static DECLARE_AD7908_CHANNELS(ad7928, 12);
|
||||
|
||||
static const struct ad7923_chip_info ad7923_chip_info[] = {
|
||||
[AD7904] = {
|
||||
@ -123,6 +137,18 @@ static const struct ad7923_chip_info ad7923_chip_info[] = {
|
||||
.channels = ad7924_channels,
|
||||
.num_channels = ARRAY_SIZE(ad7924_channels),
|
||||
},
|
||||
[AD7908] = {
|
||||
.channels = ad7908_channels,
|
||||
.num_channels = ARRAY_SIZE(ad7908_channels),
|
||||
},
|
||||
[AD7918] = {
|
||||
.channels = ad7918_channels,
|
||||
.num_channels = ARRAY_SIZE(ad7918_channels),
|
||||
},
|
||||
[AD7928] = {
|
||||
.channels = ad7928_channels,
|
||||
.num_channels = ARRAY_SIZE(ad7928_channels),
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
@ -135,7 +161,11 @@ static int ad7923_update_scan_mode(struct iio_dev *indio_dev,
|
||||
int i, cmd, len;
|
||||
|
||||
len = 0;
|
||||
for_each_set_bit(i, active_scan_mask, AD7923_MAX_CHAN) {
|
||||
/*
|
||||
* For this driver the last channel is always the software timestamp so
|
||||
* skip that one.
|
||||
*/
|
||||
for_each_set_bit(i, active_scan_mask, indio_dev->num_channels - 1) {
|
||||
cmd = AD7923_WRITE_CR | AD7923_CHANNEL_WRITE(i) |
|
||||
AD7923_SEQUENCE_WRITE(AD7923_SEQUENCE_OFF) |
|
||||
st->settings;
|
||||
@ -188,7 +218,7 @@ done:
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int ad7923_scan_direct(struct ad7923_state *st, unsigned ch)
|
||||
static int ad7923_scan_direct(struct ad7923_state *st, unsigned int ch)
|
||||
{
|
||||
int ret, cmd;
|
||||
|
||||
@ -348,13 +378,29 @@ static const struct spi_device_id ad7923_id[] = {
|
||||
{"ad7914", AD7914},
|
||||
{"ad7923", AD7924},
|
||||
{"ad7924", AD7924},
|
||||
{"ad7908", AD7908},
|
||||
{"ad7918", AD7918},
|
||||
{"ad7928", AD7928},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ad7923_id);
|
||||
|
||||
static const struct of_device_id ad7923_of_match[] = {
|
||||
{ .compatible = "adi,ad7904", },
|
||||
{ .compatible = "adi,ad7914", },
|
||||
{ .compatible = "adi,ad7923", },
|
||||
{ .compatible = "adi,ad7924", },
|
||||
{ .compatible = "adi,ad7908", },
|
||||
{ .compatible = "adi,ad7918", },
|
||||
{ .compatible = "adi,ad7928", },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ad7923_of_match);
|
||||
|
||||
static struct spi_driver ad7923_driver = {
|
||||
.driver = {
|
||||
.name = "ad7923",
|
||||
.of_match_table = ad7923_of_match,
|
||||
},
|
||||
.probe = ad7923_probe,
|
||||
.remove = ad7923_remove,
|
||||
@ -364,5 +410,5 @@ module_spi_driver(ad7923_driver);
|
||||
|
||||
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_DESCRIPTION("Analog Devices AD7923 and similar ADC");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
@ -167,6 +167,21 @@ static int ad799x_read_config(struct ad799x_state *st)
|
||||
}
|
||||
}
|
||||
|
||||
static int ad799x_update_config(struct ad799x_state *st, u16 config)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ad799x_write_config(st, config);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = ad799x_read_config(st);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
st->config = ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ad799x_trigger_handler() bh of trigger launched polling to ring buffer
|
||||
*
|
||||
@ -808,13 +823,9 @@ static int ad799x_probe(struct i2c_client *client,
|
||||
indio_dev->channels = st->chip_config->channel;
|
||||
indio_dev->num_channels = chip_info->num_channels;
|
||||
|
||||
ret = ad799x_write_config(st, st->chip_config->default_config);
|
||||
if (ret < 0)
|
||||
ret = ad799x_update_config(st, st->chip_config->default_config);
|
||||
if (ret)
|
||||
goto error_disable_vref;
|
||||
ret = ad799x_read_config(st);
|
||||
if (ret < 0)
|
||||
goto error_disable_vref;
|
||||
st->config = ret;
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, NULL,
|
||||
&ad799x_trigger_handler, NULL);
|
||||
@ -864,6 +875,48 @@ static int ad799x_remove(struct i2c_client *client)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused ad799x_suspend(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
|
||||
struct ad799x_state *st = iio_priv(indio_dev);
|
||||
|
||||
regulator_disable(st->vref);
|
||||
regulator_disable(st->reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused ad799x_resume(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
|
||||
struct ad799x_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = regulator_enable(st->reg);
|
||||
if (ret) {
|
||||
dev_err(dev, "Unable to enable vcc regulator\n");
|
||||
return ret;
|
||||
}
|
||||
ret = regulator_enable(st->vref);
|
||||
if (ret) {
|
||||
regulator_disable(st->reg);
|
||||
dev_err(dev, "Unable to enable vref regulator\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* resync config */
|
||||
ret = ad799x_update_config(st, st->config);
|
||||
if (ret) {
|
||||
regulator_disable(st->vref);
|
||||
regulator_disable(st->reg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(ad799x_pm_ops, ad799x_suspend, ad799x_resume);
|
||||
|
||||
static const struct i2c_device_id ad799x_id[] = {
|
||||
{ "ad7991", ad7991 },
|
||||
{ "ad7995", ad7995 },
|
||||
@ -881,6 +934,7 @@ MODULE_DEVICE_TABLE(i2c, ad799x_id);
|
||||
static struct i2c_driver ad799x_driver = {
|
||||
.driver = {
|
||||
.name = "ad799x",
|
||||
.pm = &ad799x_pm_ops,
|
||||
},
|
||||
.probe = ad799x_probe,
|
||||
.remove = ad799x_remove,
|
||||
|
@ -500,7 +500,7 @@ static int ad_sd_probe_trigger(struct iio_dev *indio_dev)
|
||||
|
||||
ret = request_irq(sigma_delta->spi->irq,
|
||||
ad_sd_data_rdy_trig_poll,
|
||||
IRQF_TRIGGER_LOW,
|
||||
sigma_delta->info->irq_flags,
|
||||
indio_dev->name,
|
||||
sigma_delta);
|
||||
if (ret)
|
||||
|
@ -1444,10 +1444,10 @@ static void at91_adc_dma_init(struct platform_device *pdev)
|
||||
if (st->dma_st.dma_chan)
|
||||
return;
|
||||
|
||||
st->dma_st.dma_chan = dma_request_slave_channel(&pdev->dev, "rx");
|
||||
|
||||
if (!st->dma_st.dma_chan) {
|
||||
st->dma_st.dma_chan = dma_request_chan(&pdev->dev, "rx");
|
||||
if (IS_ERR(st->dma_st.dma_chan)) {
|
||||
dev_info(&pdev->dev, "can't get DMA channel\n");
|
||||
st->dma_st.dma_chan = NULL;
|
||||
goto dma_exit;
|
||||
}
|
||||
|
||||
|
108
drivers/iio/adc/ltc2496.c
Normal file
108
drivers/iio/adc/ltc2496.c
Normal file
@ -0,0 +1,108 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* ltc2496.c - Driver for Analog Devices/Linear Technology LTC2496 ADC
|
||||
*
|
||||
* Based on ltc2497.c which has
|
||||
* Copyright (C) 2017 Analog Devices Inc.
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*
|
||||
* Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/2496fc.pdf
|
||||
*/
|
||||
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/driver.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include "ltc2497.h"
|
||||
|
||||
struct ltc2496_driverdata {
|
||||
/* this must be the first member */
|
||||
struct ltc2497core_driverdata common_ddata;
|
||||
struct spi_device *spi;
|
||||
|
||||
/*
|
||||
* DMA (thus cache coherency maintenance) requires the
|
||||
* transfer buffers to live in their own cache lines.
|
||||
*/
|
||||
unsigned char rxbuf[3] ____cacheline_aligned;
|
||||
unsigned char txbuf[3];
|
||||
};
|
||||
|
||||
static int ltc2496_result_and_measure(struct ltc2497core_driverdata *ddata,
|
||||
u8 address, int *val)
|
||||
{
|
||||
struct ltc2496_driverdata *st =
|
||||
container_of(ddata, struct ltc2496_driverdata, common_ddata);
|
||||
struct spi_transfer t = {
|
||||
.tx_buf = st->txbuf,
|
||||
.rx_buf = st->rxbuf,
|
||||
.len = sizeof(st->txbuf),
|
||||
};
|
||||
int ret;
|
||||
|
||||
st->txbuf[0] = LTC2497_ENABLE | address;
|
||||
|
||||
ret = spi_sync_transfer(st->spi, &t, 1);
|
||||
if (ret < 0) {
|
||||
dev_err(&st->spi->dev, "spi_sync_transfer failed: %pe\n",
|
||||
ERR_PTR(ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (val)
|
||||
*val = ((st->rxbuf[0] & 0x3f) << 12 |
|
||||
st->rxbuf[1] << 4 | st->rxbuf[2] >> 4) -
|
||||
(1 << 17);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ltc2496_probe(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct ltc2496_driverdata *st;
|
||||
struct device *dev = &spi->dev;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
st->spi = spi;
|
||||
st->common_ddata.result_and_measure = ltc2496_result_and_measure;
|
||||
|
||||
return ltc2497core_probe(dev, indio_dev);
|
||||
}
|
||||
|
||||
static int ltc2496_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
|
||||
ltc2497core_remove(indio_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id ltc2496_of_match[] = {
|
||||
{ .compatible = "lltc,ltc2496", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ltc2496_of_match);
|
||||
|
||||
static struct spi_driver ltc2496_driver = {
|
||||
.driver = {
|
||||
.name = "ltc2496",
|
||||
.of_match_table = of_match_ptr(ltc2496_of_match),
|
||||
},
|
||||
.probe = ltc2496_probe,
|
||||
.remove = ltc2496_remove,
|
||||
};
|
||||
module_spi_driver(ltc2496_driver);
|
||||
|
||||
MODULE_AUTHOR("Uwe Kleine-König <u.kleine-könig@pengutronix.de>");
|
||||
MODULE_DESCRIPTION("Linear Technology LTC2496 ADC driver");
|
||||
MODULE_LICENSE("GPL v2");
|
243
drivers/iio/adc/ltc2497-core.c
Normal file
243
drivers/iio/adc/ltc2497-core.c
Normal file
@ -0,0 +1,243 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* ltc2497-core.c - Common code for Analog Devices/Linear Technology
|
||||
* LTC2496 and LTC2497 ADCs
|
||||
*
|
||||
* Copyright (C) 2017 Analog Devices Inc.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/driver.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#include "ltc2497.h"
|
||||
|
||||
#define LTC2497_SGL BIT(4)
|
||||
#define LTC2497_DIFF 0
|
||||
#define LTC2497_SIGN BIT(3)
|
||||
|
||||
static int ltc2497core_wait_conv(struct ltc2497core_driverdata *ddata)
|
||||
{
|
||||
s64 time_elapsed;
|
||||
|
||||
time_elapsed = ktime_ms_delta(ktime_get(), ddata->time_prev);
|
||||
|
||||
if (time_elapsed < LTC2497_CONVERSION_TIME_MS) {
|
||||
/* delay if conversion time not passed
|
||||
* since last read or write
|
||||
*/
|
||||
if (msleep_interruptible(
|
||||
LTC2497_CONVERSION_TIME_MS - time_elapsed))
|
||||
return -ERESTARTSYS;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (time_elapsed - LTC2497_CONVERSION_TIME_MS <= 0) {
|
||||
/* We're in automatic mode -
|
||||
* so the last reading is still not outdated
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ltc2497core_read(struct ltc2497core_driverdata *ddata, u8 address, int *val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ltc2497core_wait_conv(ddata);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (ret || ddata->addr_prev != address) {
|
||||
ret = ddata->result_and_measure(ddata, address, NULL);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ddata->addr_prev = address;
|
||||
|
||||
if (msleep_interruptible(LTC2497_CONVERSION_TIME_MS))
|
||||
return -ERESTARTSYS;
|
||||
}
|
||||
|
||||
ret = ddata->result_and_measure(ddata, address, val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ddata->time_prev = ktime_get();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ltc2497core_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct ltc2497core_driverdata *ddata = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
ret = ltc2497core_read(ddata, chan->address, val);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
ret = regulator_get_voltage(ddata->ref);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = ret / 1000;
|
||||
*val2 = 17;
|
||||
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
#define LTC2497_CHAN(_chan, _addr, _ds_name) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = (_chan), \
|
||||
.address = (_addr | (_chan / 2) | ((_chan & 1) ? LTC2497_SIGN : 0)), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.datasheet_name = (_ds_name), \
|
||||
}
|
||||
|
||||
#define LTC2497_CHAN_DIFF(_chan, _addr) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = (_chan) * 2 + ((_addr) & LTC2497_SIGN ? 1 : 0), \
|
||||
.channel2 = (_chan) * 2 + ((_addr) & LTC2497_SIGN ? 0 : 1),\
|
||||
.address = (_addr | _chan), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.differential = 1, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec ltc2497core_channel[] = {
|
||||
LTC2497_CHAN(0, LTC2497_SGL, "CH0"),
|
||||
LTC2497_CHAN(1, LTC2497_SGL, "CH1"),
|
||||
LTC2497_CHAN(2, LTC2497_SGL, "CH2"),
|
||||
LTC2497_CHAN(3, LTC2497_SGL, "CH3"),
|
||||
LTC2497_CHAN(4, LTC2497_SGL, "CH4"),
|
||||
LTC2497_CHAN(5, LTC2497_SGL, "CH5"),
|
||||
LTC2497_CHAN(6, LTC2497_SGL, "CH6"),
|
||||
LTC2497_CHAN(7, LTC2497_SGL, "CH7"),
|
||||
LTC2497_CHAN(8, LTC2497_SGL, "CH8"),
|
||||
LTC2497_CHAN(9, LTC2497_SGL, "CH9"),
|
||||
LTC2497_CHAN(10, LTC2497_SGL, "CH10"),
|
||||
LTC2497_CHAN(11, LTC2497_SGL, "CH11"),
|
||||
LTC2497_CHAN(12, LTC2497_SGL, "CH12"),
|
||||
LTC2497_CHAN(13, LTC2497_SGL, "CH13"),
|
||||
LTC2497_CHAN(14, LTC2497_SGL, "CH14"),
|
||||
LTC2497_CHAN(15, LTC2497_SGL, "CH15"),
|
||||
LTC2497_CHAN_DIFF(0, LTC2497_DIFF),
|
||||
LTC2497_CHAN_DIFF(1, LTC2497_DIFF),
|
||||
LTC2497_CHAN_DIFF(2, LTC2497_DIFF),
|
||||
LTC2497_CHAN_DIFF(3, LTC2497_DIFF),
|
||||
LTC2497_CHAN_DIFF(4, LTC2497_DIFF),
|
||||
LTC2497_CHAN_DIFF(5, LTC2497_DIFF),
|
||||
LTC2497_CHAN_DIFF(6, LTC2497_DIFF),
|
||||
LTC2497_CHAN_DIFF(7, LTC2497_DIFF),
|
||||
LTC2497_CHAN_DIFF(0, LTC2497_DIFF | LTC2497_SIGN),
|
||||
LTC2497_CHAN_DIFF(1, LTC2497_DIFF | LTC2497_SIGN),
|
||||
LTC2497_CHAN_DIFF(2, LTC2497_DIFF | LTC2497_SIGN),
|
||||
LTC2497_CHAN_DIFF(3, LTC2497_DIFF | LTC2497_SIGN),
|
||||
LTC2497_CHAN_DIFF(4, LTC2497_DIFF | LTC2497_SIGN),
|
||||
LTC2497_CHAN_DIFF(5, LTC2497_DIFF | LTC2497_SIGN),
|
||||
LTC2497_CHAN_DIFF(6, LTC2497_DIFF | LTC2497_SIGN),
|
||||
LTC2497_CHAN_DIFF(7, LTC2497_DIFF | LTC2497_SIGN),
|
||||
};
|
||||
|
||||
static const struct iio_info ltc2497core_info = {
|
||||
.read_raw = ltc2497core_read_raw,
|
||||
};
|
||||
|
||||
int ltc2497core_probe(struct device *dev, struct iio_dev *indio_dev)
|
||||
{
|
||||
struct ltc2497core_driverdata *ddata = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
indio_dev->dev.parent = dev;
|
||||
indio_dev->name = dev_name(dev);
|
||||
indio_dev->info = <c2497core_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = ltc2497core_channel;
|
||||
indio_dev->num_channels = ARRAY_SIZE(ltc2497core_channel);
|
||||
|
||||
ret = ddata->result_and_measure(ddata, LTC2497_CONFIG_DEFAULT, NULL);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ddata->ref = devm_regulator_get(dev, "vref");
|
||||
if (IS_ERR(ddata->ref)) {
|
||||
if (PTR_ERR(ddata->ref) != -EPROBE_DEFER)
|
||||
dev_err(dev, "Failed to get vref regulator: %pe\n",
|
||||
ddata->ref);
|
||||
|
||||
return PTR_ERR(ddata->ref);
|
||||
}
|
||||
|
||||
ret = regulator_enable(ddata->ref);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Failed to enable vref regulator: %pe\n",
|
||||
ERR_PTR(ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (dev->platform_data) {
|
||||
struct iio_map *plat_data;
|
||||
|
||||
plat_data = (struct iio_map *)dev->platform_data;
|
||||
|
||||
ret = iio_map_array_register(indio_dev, plat_data);
|
||||
if (ret) {
|
||||
dev_err(&indio_dev->dev, "iio map err: %d\n", ret);
|
||||
goto err_regulator_disable;
|
||||
}
|
||||
}
|
||||
|
||||
ddata->addr_prev = LTC2497_CONFIG_DEFAULT;
|
||||
ddata->time_prev = ktime_get();
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret < 0)
|
||||
goto err_array_unregister;
|
||||
|
||||
return 0;
|
||||
|
||||
err_array_unregister:
|
||||
iio_map_array_unregister(indio_dev);
|
||||
|
||||
err_regulator_disable:
|
||||
regulator_disable(ddata->ref);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_NS(ltc2497core_probe, LTC2497);
|
||||
|
||||
void ltc2497core_remove(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct ltc2497core_driverdata *ddata = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
iio_map_array_unregister(indio_dev);
|
||||
|
||||
regulator_disable(ddata->ref);
|
||||
}
|
||||
EXPORT_SYMBOL_NS(ltc2497core_remove, LTC2497);
|
||||
|
||||
MODULE_DESCRIPTION("common code for LTC2496/LTC2497 drivers");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -7,27 +7,18 @@
|
||||
* Datasheet: http://cds.linear.com/docs/en/datasheet/2497fd.pdf
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/driver.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#define LTC2497_ENABLE 0xA0
|
||||
#define LTC2497_SGL BIT(4)
|
||||
#define LTC2497_DIFF 0
|
||||
#define LTC2497_SIGN BIT(3)
|
||||
#define LTC2497_CONFIG_DEFAULT LTC2497_ENABLE
|
||||
#define LTC2497_CONVERSION_TIME_MS 150ULL
|
||||
#include "ltc2497.h"
|
||||
|
||||
struct ltc2497_st {
|
||||
struct ltc2497_driverdata {
|
||||
/* this must be the first member */
|
||||
struct ltc2497core_driverdata common_ddata;
|
||||
struct i2c_client *client;
|
||||
struct regulator *ref;
|
||||
ktime_t time_prev;
|
||||
u8 addr_prev;
|
||||
/*
|
||||
* DMA (thus cache coherency maintenance) requires the
|
||||
* transfer buffers to live in their own cache lines.
|
||||
@ -35,232 +26,59 @@ struct ltc2497_st {
|
||||
__be32 buf ____cacheline_aligned;
|
||||
};
|
||||
|
||||
static int ltc2497_wait_conv(struct ltc2497_st *st)
|
||||
static int ltc2497_result_and_measure(struct ltc2497core_driverdata *ddata,
|
||||
u8 address, int *val)
|
||||
{
|
||||
s64 time_elapsed;
|
||||
|
||||
time_elapsed = ktime_ms_delta(ktime_get(), st->time_prev);
|
||||
|
||||
if (time_elapsed < LTC2497_CONVERSION_TIME_MS) {
|
||||
/* delay if conversion time not passed
|
||||
* since last read or write
|
||||
*/
|
||||
if (msleep_interruptible(
|
||||
LTC2497_CONVERSION_TIME_MS - time_elapsed))
|
||||
return -ERESTARTSYS;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (time_elapsed - LTC2497_CONVERSION_TIME_MS <= 0) {
|
||||
/* We're in automatic mode -
|
||||
* so the last reading is stil not outdated
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ltc2497_read(struct ltc2497_st *st, u8 address, int *val)
|
||||
{
|
||||
struct i2c_client *client = st->client;
|
||||
struct ltc2497_driverdata *st =
|
||||
container_of(ddata, struct ltc2497_driverdata, common_ddata);
|
||||
int ret;
|
||||
|
||||
ret = ltc2497_wait_conv(st);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (ret || st->addr_prev != address) {
|
||||
ret = i2c_smbus_write_byte(st->client,
|
||||
LTC2497_ENABLE | address);
|
||||
if (ret < 0)
|
||||
if (val) {
|
||||
ret = i2c_master_recv(st->client, (char *)&st->buf, 3);
|
||||
if (ret < 0) {
|
||||
dev_err(&st->client->dev, "i2c_master_recv failed\n");
|
||||
return ret;
|
||||
st->addr_prev = address;
|
||||
if (msleep_interruptible(LTC2497_CONVERSION_TIME_MS))
|
||||
return -ERESTARTSYS;
|
||||
}
|
||||
ret = i2c_master_recv(client, (char *)&st->buf, 3);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "i2c_master_recv failed\n");
|
||||
return ret;
|
||||
}
|
||||
st->time_prev = ktime_get();
|
||||
}
|
||||
|
||||
/* convert and shift the result,
|
||||
* and finally convert from offset binary to signed integer
|
||||
*/
|
||||
*val = (be32_to_cpu(st->buf) >> 14) - (1 << 17);
|
||||
*val = (be32_to_cpu(st->buf) >> 14) - (1 << 17);
|
||||
}
|
||||
|
||||
ret = i2c_smbus_write_byte(st->client,
|
||||
LTC2497_ENABLE | address);
|
||||
if (ret)
|
||||
dev_err(&st->client->dev, "i2c transfer failed: %pe\n",
|
||||
ERR_PTR(ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ltc2497_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct ltc2497_st *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
ret = ltc2497_read(st, chan->address, val);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
ret = regulator_get_voltage(st->ref);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = ret / 1000;
|
||||
*val2 = 17;
|
||||
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
#define LTC2497_CHAN(_chan, _addr, _ds_name) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = (_chan), \
|
||||
.address = (_addr | (_chan / 2) | ((_chan & 1) ? LTC2497_SIGN : 0)), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.datasheet_name = (_ds_name), \
|
||||
}
|
||||
|
||||
#define LTC2497_CHAN_DIFF(_chan, _addr) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = (_chan) * 2 + ((_addr) & LTC2497_SIGN ? 1 : 0), \
|
||||
.channel2 = (_chan) * 2 + ((_addr) & LTC2497_SIGN ? 0 : 1),\
|
||||
.address = (_addr | _chan), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.differential = 1, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec ltc2497_channel[] = {
|
||||
LTC2497_CHAN(0, LTC2497_SGL, "CH0"),
|
||||
LTC2497_CHAN(1, LTC2497_SGL, "CH1"),
|
||||
LTC2497_CHAN(2, LTC2497_SGL, "CH2"),
|
||||
LTC2497_CHAN(3, LTC2497_SGL, "CH3"),
|
||||
LTC2497_CHAN(4, LTC2497_SGL, "CH4"),
|
||||
LTC2497_CHAN(5, LTC2497_SGL, "CH5"),
|
||||
LTC2497_CHAN(6, LTC2497_SGL, "CH6"),
|
||||
LTC2497_CHAN(7, LTC2497_SGL, "CH7"),
|
||||
LTC2497_CHAN(8, LTC2497_SGL, "CH8"),
|
||||
LTC2497_CHAN(9, LTC2497_SGL, "CH9"),
|
||||
LTC2497_CHAN(10, LTC2497_SGL, "CH10"),
|
||||
LTC2497_CHAN(11, LTC2497_SGL, "CH11"),
|
||||
LTC2497_CHAN(12, LTC2497_SGL, "CH12"),
|
||||
LTC2497_CHAN(13, LTC2497_SGL, "CH13"),
|
||||
LTC2497_CHAN(14, LTC2497_SGL, "CH14"),
|
||||
LTC2497_CHAN(15, LTC2497_SGL, "CH15"),
|
||||
LTC2497_CHAN_DIFF(0, LTC2497_DIFF),
|
||||
LTC2497_CHAN_DIFF(1, LTC2497_DIFF),
|
||||
LTC2497_CHAN_DIFF(2, LTC2497_DIFF),
|
||||
LTC2497_CHAN_DIFF(3, LTC2497_DIFF),
|
||||
LTC2497_CHAN_DIFF(4, LTC2497_DIFF),
|
||||
LTC2497_CHAN_DIFF(5, LTC2497_DIFF),
|
||||
LTC2497_CHAN_DIFF(6, LTC2497_DIFF),
|
||||
LTC2497_CHAN_DIFF(7, LTC2497_DIFF),
|
||||
LTC2497_CHAN_DIFF(0, LTC2497_DIFF | LTC2497_SIGN),
|
||||
LTC2497_CHAN_DIFF(1, LTC2497_DIFF | LTC2497_SIGN),
|
||||
LTC2497_CHAN_DIFF(2, LTC2497_DIFF | LTC2497_SIGN),
|
||||
LTC2497_CHAN_DIFF(3, LTC2497_DIFF | LTC2497_SIGN),
|
||||
LTC2497_CHAN_DIFF(4, LTC2497_DIFF | LTC2497_SIGN),
|
||||
LTC2497_CHAN_DIFF(5, LTC2497_DIFF | LTC2497_SIGN),
|
||||
LTC2497_CHAN_DIFF(6, LTC2497_DIFF | LTC2497_SIGN),
|
||||
LTC2497_CHAN_DIFF(7, LTC2497_DIFF | LTC2497_SIGN),
|
||||
};
|
||||
|
||||
static const struct iio_info ltc2497_info = {
|
||||
.read_raw = ltc2497_read_raw,
|
||||
};
|
||||
|
||||
static int ltc2497_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct ltc2497_st *st;
|
||||
struct iio_map *plat_data;
|
||||
int ret;
|
||||
struct ltc2497_driverdata *st;
|
||||
struct device *dev = &client->dev;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C |
|
||||
I2C_FUNC_SMBUS_WRITE_BYTE))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*st));
|
||||
indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
st->client = client;
|
||||
st->common_ddata.result_and_measure = ltc2497_result_and_measure;
|
||||
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->name = id->name;
|
||||
indio_dev->info = <c2497_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = ltc2497_channel;
|
||||
indio_dev->num_channels = ARRAY_SIZE(ltc2497_channel);
|
||||
|
||||
st->ref = devm_regulator_get(&client->dev, "vref");
|
||||
if (IS_ERR(st->ref))
|
||||
return PTR_ERR(st->ref);
|
||||
|
||||
ret = regulator_enable(st->ref);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (client->dev.platform_data) {
|
||||
plat_data = ((struct iio_map *)client->dev.platform_data);
|
||||
ret = iio_map_array_register(indio_dev, plat_data);
|
||||
if (ret) {
|
||||
dev_err(&indio_dev->dev, "iio map err: %d\n", ret);
|
||||
goto err_regulator_disable;
|
||||
}
|
||||
}
|
||||
|
||||
ret = i2c_smbus_write_byte(st->client, LTC2497_CONFIG_DEFAULT);
|
||||
if (ret < 0)
|
||||
goto err_array_unregister;
|
||||
|
||||
st->addr_prev = LTC2497_CONFIG_DEFAULT;
|
||||
st->time_prev = ktime_get();
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret < 0)
|
||||
goto err_array_unregister;
|
||||
|
||||
return 0;
|
||||
|
||||
err_array_unregister:
|
||||
iio_map_array_unregister(indio_dev);
|
||||
|
||||
err_regulator_disable:
|
||||
regulator_disable(st->ref);
|
||||
|
||||
return ret;
|
||||
return ltc2497core_probe(dev, indio_dev);
|
||||
}
|
||||
|
||||
static int ltc2497_remove(struct i2c_client *client)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
struct ltc2497_st *st = iio_priv(indio_dev);
|
||||
|
||||
iio_map_array_unregister(indio_dev);
|
||||
iio_device_unregister(indio_dev);
|
||||
regulator_disable(st->ref);
|
||||
ltc2497core_remove(indio_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
18
drivers/iio/adc/ltc2497.h
Normal file
18
drivers/iio/adc/ltc2497.h
Normal file
@ -0,0 +1,18 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
|
||||
#define LTC2497_ENABLE 0xA0
|
||||
#define LTC2497_CONFIG_DEFAULT LTC2497_ENABLE
|
||||
#define LTC2497_CONVERSION_TIME_MS 150ULL
|
||||
|
||||
struct ltc2497core_driverdata {
|
||||
struct regulator *ref;
|
||||
ktime_t time_prev;
|
||||
u8 addr_prev;
|
||||
int (*result_and_measure)(struct ltc2497core_driverdata *ddata,
|
||||
u8 address, int *val);
|
||||
};
|
||||
|
||||
int ltc2497core_probe(struct device *dev, struct iio_dev *indio_dev);
|
||||
void ltc2497core_remove(struct iio_dev *indio_dev);
|
||||
|
||||
MODULE_IMPORT_NS(LTC2497);
|
@ -115,22 +115,17 @@ enum max9611_conf_ids {
|
||||
* where data shall be read from
|
||||
*/
|
||||
static const unsigned int max9611_mux_conf[][2] = {
|
||||
/* CONF_SENSE_1x */
|
||||
{ MAX9611_MUX_SENSE_1x, MAX9611_REG_CSA_DATA },
|
||||
/* CONF_SENSE_4x */
|
||||
{ MAX9611_MUX_SENSE_4x, MAX9611_REG_CSA_DATA },
|
||||
/* CONF_SENSE_8x */
|
||||
{ MAX9611_MUX_SENSE_8x, MAX9611_REG_CSA_DATA },
|
||||
/* CONF_IN_VOLT */
|
||||
{ MAX9611_INPUT_VOLT, MAX9611_REG_RS_DATA },
|
||||
/* CONF_TEMP */
|
||||
{ MAX9611_MUX_TEMP, MAX9611_REG_TEMP_DATA },
|
||||
[CONF_SENSE_1x] = { MAX9611_MUX_SENSE_1x, MAX9611_REG_CSA_DATA },
|
||||
[CONF_SENSE_4x] = { MAX9611_MUX_SENSE_4x, MAX9611_REG_CSA_DATA },
|
||||
[CONF_SENSE_8x] = { MAX9611_MUX_SENSE_8x, MAX9611_REG_CSA_DATA },
|
||||
[CONF_IN_VOLT] = { MAX9611_INPUT_VOLT, MAX9611_REG_RS_DATA },
|
||||
[CONF_TEMP] = { MAX9611_MUX_TEMP, MAX9611_REG_TEMP_DATA },
|
||||
};
|
||||
|
||||
enum max9611_csa_gain {
|
||||
CSA_GAIN_1x,
|
||||
CSA_GAIN_4x,
|
||||
CSA_GAIN_8x,
|
||||
CSA_GAIN_1x = CONF_SENSE_1x,
|
||||
CSA_GAIN_4x = CONF_SENSE_4x,
|
||||
CSA_GAIN_8x = CONF_SENSE_8x,
|
||||
};
|
||||
|
||||
enum max9611_csa_gain_params {
|
||||
@ -148,18 +143,9 @@ enum max9611_csa_gain_params {
|
||||
* value; use this structure to retrieve the correct LSB and offset values.
|
||||
*/
|
||||
static const unsigned int max9611_gain_conf[][2] = {
|
||||
{ /* [0] CSA_GAIN_1x */
|
||||
MAX9611_CSA_1X_LSB_nV,
|
||||
MAX9611_CSA_1X_OFFS_RAW,
|
||||
},
|
||||
{ /* [1] CSA_GAIN_4x */
|
||||
MAX9611_CSA_4X_LSB_nV,
|
||||
MAX9611_CSA_4X_OFFS_RAW,
|
||||
},
|
||||
{ /* [2] CSA_GAIN_8x */
|
||||
MAX9611_CSA_8X_LSB_nV,
|
||||
MAX9611_CSA_8X_OFFS_RAW,
|
||||
},
|
||||
[CSA_GAIN_1x] = { MAX9611_CSA_1X_LSB_nV, MAX9611_CSA_1X_OFFS_RAW, },
|
||||
[CSA_GAIN_4x] = { MAX9611_CSA_4X_LSB_nV, MAX9611_CSA_4X_OFFS_RAW, },
|
||||
[CSA_GAIN_8x] = { MAX9611_CSA_8X_LSB_nV, MAX9611_CSA_8X_OFFS_RAW, },
|
||||
};
|
||||
|
||||
enum max9611_chan_addrs {
|
||||
|
@ -280,21 +280,21 @@ out:
|
||||
static const struct stm32_adc_common_regs stm32f4_adc_common_regs = {
|
||||
.csr = STM32F4_ADC_CSR,
|
||||
.ccr = STM32F4_ADC_CCR,
|
||||
.eoc1_msk = STM32F4_EOC1,
|
||||
.eoc2_msk = STM32F4_EOC2,
|
||||
.eoc3_msk = STM32F4_EOC3,
|
||||
.eoc1_msk = STM32F4_EOC1 | STM32F4_OVR1,
|
||||
.eoc2_msk = STM32F4_EOC2 | STM32F4_OVR2,
|
||||
.eoc3_msk = STM32F4_EOC3 | STM32F4_OVR3,
|
||||
.ier = STM32F4_ADC_CR1,
|
||||
.eocie_msk = STM32F4_EOCIE,
|
||||
.eocie_msk = STM32F4_EOCIE | STM32F4_OVRIE,
|
||||
};
|
||||
|
||||
/* STM32H7 common registers definitions */
|
||||
static const struct stm32_adc_common_regs stm32h7_adc_common_regs = {
|
||||
.csr = STM32H7_ADC_CSR,
|
||||
.ccr = STM32H7_ADC_CCR,
|
||||
.eoc1_msk = STM32H7_EOC_MST,
|
||||
.eoc2_msk = STM32H7_EOC_SLV,
|
||||
.eoc1_msk = STM32H7_EOC_MST | STM32H7_OVR_MST,
|
||||
.eoc2_msk = STM32H7_EOC_SLV | STM32H7_OVR_SLV,
|
||||
.ier = STM32H7_ADC_IER,
|
||||
.eocie_msk = STM32H7_EOCIE,
|
||||
.eocie_msk = STM32H7_EOCIE | STM32H7_OVRIE,
|
||||
};
|
||||
|
||||
static const unsigned int stm32_adc_offset[STM32_ADC_MAX_ADCS] = {
|
||||
@ -688,7 +688,8 @@ static int stm32_adc_probe(struct platform_device *pdev)
|
||||
priv->vref = devm_regulator_get(&pdev->dev, "vref");
|
||||
if (IS_ERR(priv->vref)) {
|
||||
ret = PTR_ERR(priv->vref);
|
||||
dev_err(&pdev->dev, "vref get failed, %d\n", ret);
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(&pdev->dev, "vref get failed, %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -696,7 +697,8 @@ static int stm32_adc_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(priv->aclk)) {
|
||||
ret = PTR_ERR(priv->aclk);
|
||||
if (ret != -ENOENT) {
|
||||
dev_err(&pdev->dev, "Can't get 'adc' clock\n");
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(&pdev->dev, "Can't get 'adc' clock\n");
|
||||
return ret;
|
||||
}
|
||||
priv->aclk = NULL;
|
||||
@ -706,7 +708,8 @@ static int stm32_adc_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(priv->bclk)) {
|
||||
ret = PTR_ERR(priv->bclk);
|
||||
if (ret != -ENOENT) {
|
||||
dev_err(&pdev->dev, "Can't get 'bus' clock\n");
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(&pdev->dev, "Can't get 'bus' clock\n");
|
||||
return ret;
|
||||
}
|
||||
priv->bclk = NULL;
|
||||
|
@ -51,10 +51,12 @@
|
||||
#define STM32F4_ADC_CCR (STM32_ADCX_COMN_OFFSET + 0x04)
|
||||
|
||||
/* STM32F4_ADC_SR - bit fields */
|
||||
#define STM32F4_OVR BIT(5)
|
||||
#define STM32F4_STRT BIT(4)
|
||||
#define STM32F4_EOC BIT(1)
|
||||
|
||||
/* STM32F4_ADC_CR1 - bit fields */
|
||||
#define STM32F4_OVRIE BIT(26)
|
||||
#define STM32F4_RES_SHIFT 24
|
||||
#define STM32F4_RES_MASK GENMASK(25, 24)
|
||||
#define STM32F4_SCAN BIT(8)
|
||||
@ -72,8 +74,11 @@
|
||||
#define STM32F4_ADON BIT(0)
|
||||
|
||||
/* STM32F4_ADC_CSR - bit fields */
|
||||
#define STM32F4_OVR3 BIT(21)
|
||||
#define STM32F4_EOC3 BIT(17)
|
||||
#define STM32F4_OVR2 BIT(13)
|
||||
#define STM32F4_EOC2 BIT(9)
|
||||
#define STM32F4_OVR1 BIT(5)
|
||||
#define STM32F4_EOC1 BIT(1)
|
||||
|
||||
/* STM32F4_ADC_CCR - bit fields */
|
||||
@ -103,10 +108,12 @@
|
||||
|
||||
/* STM32H7_ADC_ISR - bit fields */
|
||||
#define STM32MP1_VREGREADY BIT(12)
|
||||
#define STM32H7_OVR BIT(4)
|
||||
#define STM32H7_EOC BIT(2)
|
||||
#define STM32H7_ADRDY BIT(0)
|
||||
|
||||
/* STM32H7_ADC_IER - bit fields */
|
||||
#define STM32H7_OVRIE STM32H7_OVR
|
||||
#define STM32H7_EOCIE STM32H7_EOC
|
||||
|
||||
/* STM32H7_ADC_CR - bit fields */
|
||||
@ -155,7 +162,9 @@ enum stm32h7_adc_dmngt {
|
||||
#define STM32H7_LINCALFACT_MASK GENMASK(29, 0)
|
||||
|
||||
/* STM32H7_ADC_CSR - bit fields */
|
||||
#define STM32H7_OVR_SLV BIT(20)
|
||||
#define STM32H7_EOC_SLV BIT(18)
|
||||
#define STM32H7_OVR_MST BIT(4)
|
||||
#define STM32H7_EOC_MST BIT(2)
|
||||
|
||||
/* STM32H7_ADC_CCR - bit fields */
|
||||
|
@ -117,7 +117,9 @@ struct stm32_adc_regs {
|
||||
* struct stm32_adc_regspec - stm32 registers definition
|
||||
* @dr: data register offset
|
||||
* @ier_eoc: interrupt enable register & eocie bitfield
|
||||
* @ier_ovr: interrupt enable register & overrun bitfield
|
||||
* @isr_eoc: interrupt status register & eoc bitfield
|
||||
* @isr_ovr: interrupt status register & overrun bitfield
|
||||
* @sqr: reference to sequence registers array
|
||||
* @exten: trigger control register & bitfield
|
||||
* @extsel: trigger selection register & bitfield
|
||||
@ -128,7 +130,9 @@ struct stm32_adc_regs {
|
||||
struct stm32_adc_regspec {
|
||||
const u32 dr;
|
||||
const struct stm32_adc_regs ier_eoc;
|
||||
const struct stm32_adc_regs ier_ovr;
|
||||
const struct stm32_adc_regs isr_eoc;
|
||||
const struct stm32_adc_regs isr_ovr;
|
||||
const struct stm32_adc_regs *sqr;
|
||||
const struct stm32_adc_regs exten;
|
||||
const struct stm32_adc_regs extsel;
|
||||
@ -337,7 +341,9 @@ static const unsigned int stm32f4_adc_smp_cycles[STM32_ADC_MAX_SMP + 1] = {
|
||||
static const struct stm32_adc_regspec stm32f4_adc_regspec = {
|
||||
.dr = STM32F4_ADC_DR,
|
||||
.ier_eoc = { STM32F4_ADC_CR1, STM32F4_EOCIE },
|
||||
.ier_ovr = { STM32F4_ADC_CR1, STM32F4_OVRIE },
|
||||
.isr_eoc = { STM32F4_ADC_SR, STM32F4_EOC },
|
||||
.isr_ovr = { STM32F4_ADC_SR, STM32F4_OVR },
|
||||
.sqr = stm32f4_sq,
|
||||
.exten = { STM32F4_ADC_CR2, STM32F4_EXTEN_MASK, STM32F4_EXTEN_SHIFT },
|
||||
.extsel = { STM32F4_ADC_CR2, STM32F4_EXTSEL_MASK,
|
||||
@ -429,7 +435,9 @@ static const unsigned int stm32h7_adc_smp_cycles[STM32_ADC_MAX_SMP + 1] = {
|
||||
static const struct stm32_adc_regspec stm32h7_adc_regspec = {
|
||||
.dr = STM32H7_ADC_DR,
|
||||
.ier_eoc = { STM32H7_ADC_IER, STM32H7_EOCIE },
|
||||
.ier_ovr = { STM32H7_ADC_IER, STM32H7_OVRIE },
|
||||
.isr_eoc = { STM32H7_ADC_ISR, STM32H7_EOC },
|
||||
.isr_ovr = { STM32H7_ADC_ISR, STM32H7_OVR },
|
||||
.sqr = stm32h7_sq,
|
||||
.exten = { STM32H7_ADC_CFGR, STM32H7_EXTEN_MASK, STM32H7_EXTEN_SHIFT },
|
||||
.extsel = { STM32H7_ADC_CFGR, STM32H7_EXTSEL_MASK,
|
||||
@ -506,6 +514,18 @@ static void stm32_adc_conv_irq_disable(struct stm32_adc *adc)
|
||||
adc->cfg->regs->ier_eoc.mask);
|
||||
}
|
||||
|
||||
static void stm32_adc_ovr_irq_enable(struct stm32_adc *adc)
|
||||
{
|
||||
stm32_adc_set_bits(adc, adc->cfg->regs->ier_ovr.reg,
|
||||
adc->cfg->regs->ier_ovr.mask);
|
||||
}
|
||||
|
||||
static void stm32_adc_ovr_irq_disable(struct stm32_adc *adc)
|
||||
{
|
||||
stm32_adc_clr_bits(adc, adc->cfg->regs->ier_ovr.reg,
|
||||
adc->cfg->regs->ier_ovr.mask);
|
||||
}
|
||||
|
||||
static void stm32_adc_set_res(struct stm32_adc *adc)
|
||||
{
|
||||
const struct stm32_adc_regs *res = &adc->cfg->regs->res;
|
||||
@ -1205,6 +1225,19 @@ static int stm32_adc_read_raw(struct iio_dev *indio_dev,
|
||||
}
|
||||
}
|
||||
|
||||
static irqreturn_t stm32_adc_threaded_isr(int irq, void *data)
|
||||
{
|
||||
struct stm32_adc *adc = data;
|
||||
struct iio_dev *indio_dev = iio_priv_to_dev(adc);
|
||||
const struct stm32_adc_regspec *regs = adc->cfg->regs;
|
||||
u32 status = stm32_adc_readl(adc, regs->isr_eoc.reg);
|
||||
|
||||
if (status & regs->isr_ovr.mask)
|
||||
dev_err(&indio_dev->dev, "Overrun, stopping: restart needed\n");
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t stm32_adc_isr(int irq, void *data)
|
||||
{
|
||||
struct stm32_adc *adc = data;
|
||||
@ -1212,6 +1245,19 @@ static irqreturn_t stm32_adc_isr(int irq, void *data)
|
||||
const struct stm32_adc_regspec *regs = adc->cfg->regs;
|
||||
u32 status = stm32_adc_readl(adc, regs->isr_eoc.reg);
|
||||
|
||||
if (status & regs->isr_ovr.mask) {
|
||||
/*
|
||||
* Overrun occurred on regular conversions: data for wrong
|
||||
* channel may be read. Unconditionally disable interrupts
|
||||
* to stop processing data and print error message.
|
||||
* Restarting the capture can be done by disabling, then
|
||||
* re-enabling it (e.g. write 0, then 1 to buffer/enable).
|
||||
*/
|
||||
stm32_adc_ovr_irq_disable(adc);
|
||||
stm32_adc_conv_irq_disable(adc);
|
||||
return IRQ_WAKE_THREAD;
|
||||
}
|
||||
|
||||
if (status & regs->isr_eoc.mask) {
|
||||
/* Reading DR also clears EOC status flag */
|
||||
adc->buffer[adc->bufi] = stm32_adc_readw(adc, regs->dr);
|
||||
@ -1441,6 +1487,8 @@ static int __stm32_adc_buffer_postenable(struct iio_dev *indio_dev)
|
||||
/* Reset adc buffer index */
|
||||
adc->bufi = 0;
|
||||
|
||||
stm32_adc_ovr_irq_enable(adc);
|
||||
|
||||
if (!adc->dma_chan)
|
||||
stm32_adc_conv_irq_enable(adc);
|
||||
|
||||
@ -1481,6 +1529,8 @@ static void __stm32_adc_buffer_predisable(struct iio_dev *indio_dev)
|
||||
if (!adc->dma_chan)
|
||||
stm32_adc_conv_irq_disable(adc);
|
||||
|
||||
stm32_adc_ovr_irq_disable(adc);
|
||||
|
||||
if (adc->dma_chan)
|
||||
dmaengine_terminate_sync(adc->dma_chan);
|
||||
|
||||
@ -1746,9 +1796,21 @@ static int stm32_adc_dma_request(struct iio_dev *indio_dev)
|
||||
struct dma_slave_config config;
|
||||
int ret;
|
||||
|
||||
adc->dma_chan = dma_request_slave_channel(&indio_dev->dev, "rx");
|
||||
if (!adc->dma_chan)
|
||||
adc->dma_chan = dma_request_chan(&indio_dev->dev, "rx");
|
||||
if (IS_ERR(adc->dma_chan)) {
|
||||
ret = PTR_ERR(adc->dma_chan);
|
||||
if (ret != -ENODEV) {
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(&indio_dev->dev,
|
||||
"DMA channel request failed with %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* DMA is optional: fall back to IRQ mode */
|
||||
adc->dma_chan = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
adc->rx_buf = dma_alloc_coherent(adc->dma_chan->device->dev,
|
||||
STM32_DMA_BUFFER_SIZE,
|
||||
@ -1818,8 +1880,9 @@ static int stm32_adc_probe(struct platform_device *pdev)
|
||||
if (adc->irq < 0)
|
||||
return adc->irq;
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, adc->irq, stm32_adc_isr,
|
||||
0, pdev->name, adc);
|
||||
ret = devm_request_threaded_irq(&pdev->dev, adc->irq, stm32_adc_isr,
|
||||
stm32_adc_threaded_isr,
|
||||
0, pdev->name, adc);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to request IRQ\n");
|
||||
return ret;
|
||||
|
@ -1204,6 +1204,8 @@ static int stm32_dfsdm_single_conv(struct iio_dev *indio_dev,
|
||||
|
||||
stm32_dfsdm_stop_conv(adc);
|
||||
|
||||
stm32_dfsdm_process_data(adc, res);
|
||||
|
||||
stop_dfsdm:
|
||||
stm32_dfsdm_stop_dfsdm(adc->dfsdm);
|
||||
|
||||
@ -1219,14 +1221,32 @@ static int stm32_dfsdm_write_raw(struct iio_dev *indio_dev,
|
||||
unsigned int spi_freq;
|
||||
int ret = -EINVAL;
|
||||
|
||||
switch (ch->src) {
|
||||
case DFSDM_CHANNEL_SPI_CLOCK_INTERNAL:
|
||||
spi_freq = adc->dfsdm->spi_master_freq;
|
||||
break;
|
||||
case DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_FALLING:
|
||||
case DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_RISING:
|
||||
spi_freq = adc->dfsdm->spi_master_freq / 2;
|
||||
break;
|
||||
default:
|
||||
spi_freq = adc->spi_freq;
|
||||
}
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
|
||||
ret = iio_device_claim_direct_mode(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = stm32_dfsdm_compute_all_osrs(indio_dev, val);
|
||||
if (!ret)
|
||||
if (!ret) {
|
||||
dev_dbg(&indio_dev->dev,
|
||||
"Sampling rate changed from (%u) to (%u)\n",
|
||||
adc->sample_freq, spi_freq / val);
|
||||
adc->oversamp = val;
|
||||
adc->sample_freq = spi_freq / val;
|
||||
}
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
return ret;
|
||||
|
||||
@ -1238,18 +1258,6 @@ static int stm32_dfsdm_write_raw(struct iio_dev *indio_dev,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
switch (ch->src) {
|
||||
case DFSDM_CHANNEL_SPI_CLOCK_INTERNAL:
|
||||
spi_freq = adc->dfsdm->spi_master_freq;
|
||||
break;
|
||||
case DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_FALLING:
|
||||
case DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_RISING:
|
||||
spi_freq = adc->dfsdm->spi_master_freq / 2;
|
||||
break;
|
||||
default:
|
||||
spi_freq = adc->spi_freq;
|
||||
}
|
||||
|
||||
ret = dfsdm_adc_set_samp_freq(indio_dev, val, spi_freq);
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
return ret;
|
||||
@ -1383,9 +1391,13 @@ static int stm32_dfsdm_dma_request(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
|
||||
|
||||
adc->dma_chan = dma_request_slave_channel(&indio_dev->dev, "rx");
|
||||
if (!adc->dma_chan)
|
||||
return -EINVAL;
|
||||
adc->dma_chan = dma_request_chan(&indio_dev->dev, "rx");
|
||||
if (IS_ERR(adc->dma_chan)) {
|
||||
int ret = PTR_ERR(adc->dma_chan);
|
||||
|
||||
adc->dma_chan = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
adc->rx_buf = dma_alloc_coherent(adc->dma_chan->device->dev,
|
||||
DFSDM_DMA_BUFFER_SIZE,
|
||||
@ -1509,7 +1521,16 @@ static int stm32_dfsdm_adc_init(struct iio_dev *indio_dev)
|
||||
init_completion(&adc->completion);
|
||||
|
||||
/* Optionally request DMA */
|
||||
if (stm32_dfsdm_dma_request(indio_dev)) {
|
||||
ret = stm32_dfsdm_dma_request(indio_dev);
|
||||
if (ret) {
|
||||
if (ret != -ENODEV) {
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(&indio_dev->dev,
|
||||
"DMA channel request failed with %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_dbg(&indio_dev->dev, "No DMA support\n");
|
||||
return 0;
|
||||
}
|
||||
|
@ -12,17 +12,15 @@
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include <linux/platform_data/ads1015.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/types.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
@ -33,6 +31,8 @@
|
||||
|
||||
#define ADS1015_DRV_NAME "ads1015"
|
||||
|
||||
#define ADS1015_CHANNELS 8
|
||||
|
||||
#define ADS1015_CONV_REG 0x00
|
||||
#define ADS1015_CFG_REG 0x01
|
||||
#define ADS1015_LO_THRESH_REG 0x02
|
||||
@ -77,6 +77,7 @@
|
||||
#define ADS1015_DEFAULT_CHAN 0
|
||||
|
||||
enum chip_ids {
|
||||
ADSXXXX = 0,
|
||||
ADS1015,
|
||||
ADS1115,
|
||||
};
|
||||
@ -219,6 +220,12 @@ static const struct iio_event_spec ads1015_events[] = {
|
||||
.datasheet_name = "AIN"#_chan"-AIN"#_chan2, \
|
||||
}
|
||||
|
||||
struct ads1015_channel_data {
|
||||
bool enabled;
|
||||
unsigned int pga;
|
||||
unsigned int data_rate;
|
||||
};
|
||||
|
||||
struct ads1015_thresh_data {
|
||||
unsigned int comp_queue;
|
||||
int high_thresh;
|
||||
@ -837,65 +844,58 @@ static const struct iio_info ads1115_info = {
|
||||
.attrs = &ads1115_attribute_group,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static int ads1015_get_channels_config_of(struct i2c_client *client)
|
||||
static int ads1015_client_get_channels_config(struct i2c_client *client)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
struct ads1015_data *data = iio_priv(indio_dev);
|
||||
struct device_node *node;
|
||||
struct device *dev = &client->dev;
|
||||
struct fwnode_handle *node;
|
||||
int i = -1;
|
||||
|
||||
if (!client->dev.of_node ||
|
||||
!of_get_next_child(client->dev.of_node, NULL))
|
||||
return -EINVAL;
|
||||
|
||||
for_each_child_of_node(client->dev.of_node, node) {
|
||||
device_for_each_child_node(dev, node) {
|
||||
u32 pval;
|
||||
unsigned int channel;
|
||||
unsigned int pga = ADS1015_DEFAULT_PGA;
|
||||
unsigned int data_rate = ADS1015_DEFAULT_DATA_RATE;
|
||||
|
||||
if (of_property_read_u32(node, "reg", &pval)) {
|
||||
dev_err(&client->dev, "invalid reg on %pOF\n",
|
||||
node);
|
||||
if (fwnode_property_read_u32(node, "reg", &pval)) {
|
||||
dev_err(dev, "invalid reg on %pfw\n", node);
|
||||
continue;
|
||||
}
|
||||
|
||||
channel = pval;
|
||||
if (channel >= ADS1015_CHANNELS) {
|
||||
dev_err(&client->dev,
|
||||
"invalid channel index %d on %pOF\n",
|
||||
dev_err(dev, "invalid channel index %d on %pfw\n",
|
||||
channel, node);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!of_property_read_u32(node, "ti,gain", &pval)) {
|
||||
if (!fwnode_property_read_u32(node, "ti,gain", &pval)) {
|
||||
pga = pval;
|
||||
if (pga > 6) {
|
||||
dev_err(&client->dev, "invalid gain on %pOF\n",
|
||||
node);
|
||||
of_node_put(node);
|
||||
dev_err(dev, "invalid gain on %pfw\n", node);
|
||||
fwnode_handle_put(node);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!of_property_read_u32(node, "ti,datarate", &pval)) {
|
||||
if (!fwnode_property_read_u32(node, "ti,datarate", &pval)) {
|
||||
data_rate = pval;
|
||||
if (data_rate > 7) {
|
||||
dev_err(&client->dev,
|
||||
"invalid data_rate on %pOF\n",
|
||||
node);
|
||||
of_node_put(node);
|
||||
dev_err(dev, "invalid data_rate on %pfw\n", node);
|
||||
fwnode_handle_put(node);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
data->channel_data[channel].pga = pga;
|
||||
data->channel_data[channel].data_rate = data_rate;
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return i < 0 ? -EINVAL : 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void ads1015_get_channels_config(struct i2c_client *client)
|
||||
{
|
||||
@ -903,19 +903,10 @@ static void ads1015_get_channels_config(struct i2c_client *client)
|
||||
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
struct ads1015_data *data = iio_priv(indio_dev);
|
||||
struct ads1015_platform_data *pdata = dev_get_platdata(&client->dev);
|
||||
|
||||
/* prefer platform data */
|
||||
if (pdata) {
|
||||
memcpy(data->channel_data, pdata->channel_data,
|
||||
sizeof(data->channel_data));
|
||||
if (!ads1015_client_get_channels_config(client))
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
if (!ads1015_get_channels_config_of(client))
|
||||
return;
|
||||
#endif
|
||||
/* fallback on default configuration */
|
||||
for (k = 0; k < ADS1015_CHANNELS; ++k) {
|
||||
data->channel_data[k].pga = ADS1015_DEFAULT_PGA;
|
||||
@ -953,9 +944,8 @@ static int ads1015_probe(struct i2c_client *client,
|
||||
indio_dev->name = ADS1015_DRV_NAME;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
if (client->dev.of_node)
|
||||
chip = (enum chip_ids)of_device_get_match_data(&client->dev);
|
||||
else
|
||||
chip = (enum chip_ids)device_get_match_data(&client->dev);
|
||||
if (chip == ADSXXXX)
|
||||
chip = id->driver_data;
|
||||
switch (chip) {
|
||||
case ADS1015:
|
||||
@ -970,6 +960,9 @@ static int ads1015_probe(struct i2c_client *client,
|
||||
indio_dev->info = &ads1115_info;
|
||||
data->data_rate = (unsigned int *) &ads1115_data_rate;
|
||||
break;
|
||||
default:
|
||||
dev_err(&client->dev, "Unknown chip %d\n", chip);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
data->event_channel = ADS1015_CHANNELS;
|
||||
|
@ -602,7 +602,7 @@ static int ti_ads7950_probe(struct spi_device *spi)
|
||||
|
||||
st->reg = devm_regulator_get(&spi->dev, "vref");
|
||||
if (IS_ERR(st->reg)) {
|
||||
dev_err(&spi->dev, "Failed get get regulator \"vref\"\n");
|
||||
dev_err(&spi->dev, "Failed to get regulator \"vref\"\n");
|
||||
ret = PTR_ERR(st->reg);
|
||||
goto error_destroy_mutex;
|
||||
}
|
||||
|
@ -476,7 +476,7 @@ static struct iio_dma_buffer_block *iio_dma_buffer_dequeue(
|
||||
* @n: Number of bytes to read
|
||||
* @user_buffer: Userspace buffer to copy the data to
|
||||
*
|
||||
* Should be used as the read_first_n callback for iio_buffer_access_ops
|
||||
* Should be used as the read callback for iio_buffer_access_ops
|
||||
* struct for DMA buffers.
|
||||
*/
|
||||
int iio_dma_buffer_read(struct iio_buffer *buffer, size_t n,
|
||||
|
@ -10,8 +10,10 @@
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/buffer_impl.h>
|
||||
#include <linux/iio/buffer-dma.h>
|
||||
@ -107,7 +109,7 @@ static void iio_dmaengine_buffer_release(struct iio_buffer *buf)
|
||||
}
|
||||
|
||||
static const struct iio_buffer_access_funcs iio_dmaengine_buffer_ops = {
|
||||
.read_first_n = iio_dma_buffer_read,
|
||||
.read = iio_dma_buffer_read,
|
||||
.set_bytes_per_datum = iio_dma_buffer_set_bytes_per_datum,
|
||||
.set_length = iio_dma_buffer_set_length,
|
||||
.request_update = iio_dma_buffer_request_update,
|
||||
@ -125,6 +127,24 @@ static const struct iio_dma_buffer_ops iio_dmaengine_default_ops = {
|
||||
.abort = iio_dmaengine_buffer_abort,
|
||||
};
|
||||
|
||||
static ssize_t iio_dmaengine_buffer_get_length_align(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct dmaengine_buffer *dmaengine_buffer =
|
||||
iio_buffer_to_dmaengine_buffer(indio_dev->buffer);
|
||||
|
||||
return sprintf(buf, "%u\n", dmaengine_buffer->align);
|
||||
}
|
||||
|
||||
static IIO_DEVICE_ATTR(length_align_bytes, 0444,
|
||||
iio_dmaengine_buffer_get_length_align, NULL, 0);
|
||||
|
||||
static const struct attribute *iio_dmaengine_buffer_attrs[] = {
|
||||
&iio_dev_attr_length_align_bytes.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
/**
|
||||
* iio_dmaengine_buffer_alloc() - Allocate new buffer which uses DMAengine
|
||||
* @dev: Parent device for the buffer
|
||||
@ -150,7 +170,7 @@ struct iio_buffer *iio_dmaengine_buffer_alloc(struct device *dev,
|
||||
if (!dmaengine_buffer)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
chan = dma_request_slave_channel_reason(dev, channel);
|
||||
chan = dma_request_chan(dev, channel);
|
||||
if (IS_ERR(chan)) {
|
||||
ret = PTR_ERR(chan);
|
||||
goto err_free;
|
||||
@ -178,6 +198,8 @@ struct iio_buffer *iio_dmaengine_buffer_alloc(struct device *dev,
|
||||
|
||||
iio_dma_buffer_init(&dmaengine_buffer->queue, chan->device->dev,
|
||||
&iio_dmaengine_default_ops);
|
||||
iio_buffer_set_attrs(&dmaengine_buffer->queue.buffer,
|
||||
iio_dmaengine_buffer_attrs);
|
||||
|
||||
dmaengine_buffer->queue.buffer.access = &iio_dmaengine_buffer_ops;
|
||||
|
||||
@ -206,3 +228,7 @@ void iio_dmaengine_buffer_free(struct iio_buffer *buffer)
|
||||
iio_buffer_put(buffer);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iio_dmaengine_buffer_free);
|
||||
|
||||
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
|
||||
MODULE_DESCRIPTION("DMA buffer for the IIO framework");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -98,8 +98,7 @@ static int iio_store_to_kfifo(struct iio_buffer *r,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iio_read_first_n_kfifo(struct iio_buffer *r,
|
||||
size_t n, char __user *buf)
|
||||
static int iio_read_kfifo(struct iio_buffer *r, size_t n, char __user *buf)
|
||||
{
|
||||
int ret, copied;
|
||||
struct iio_kfifo *kf = iio_to_kfifo(r);
|
||||
@ -141,7 +140,7 @@ static void iio_kfifo_buffer_release(struct iio_buffer *buffer)
|
||||
|
||||
static const struct iio_buffer_access_funcs kfifo_access_funcs = {
|
||||
.store_to = &iio_store_to_kfifo,
|
||||
.read_first_n = &iio_read_first_n_kfifo,
|
||||
.read = &iio_read_kfifo,
|
||||
.data_available = iio_kfifo_buf_data_available,
|
||||
.request_update = &iio_request_update_kfifo,
|
||||
.set_bytes_per_datum = &iio_set_bytes_per_datum_kfifo,
|
||||
|
@ -4,7 +4,7 @@
|
||||
#
|
||||
|
||||
# When adding new entries keep the list in alphabetical order
|
||||
obj-$(CONFIG_ATLAS_PH_SENSOR) += atlas-ph-sensor.o
|
||||
obj-$(CONFIG_ATLAS_PH_SENSOR) += atlas-sensor.o
|
||||
obj-$(CONFIG_BME680) += bme680_core.o
|
||||
obj-$(CONFIG_BME680_I2C) += bme680_i2c.o
|
||||
obj-$(CONFIG_BME680_SPI) += bme680_spi.o
|
||||
|
@ -1,8 +1,8 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* atlas-ph-sensor.c - Support for Atlas Scientific OEM pH-SM sensor
|
||||
* atlas-sensor.c - Support for Atlas Scientific OEM SM sensors
|
||||
*
|
||||
* Copyright (C) 2015-2018 Matt Ranostay
|
||||
* Copyright (C) 2015-2019 Konsulko Group
|
||||
* Author: Matt Ranostay <matt.ranostay@konsulko.com>
|
||||
*/
|
||||
|
||||
@ -14,7 +14,6 @@
|
||||
#include <linux/err.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/irq_work.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/regmap.h>
|
||||
@ -25,8 +24,8 @@
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#define ATLAS_REGMAP_NAME "atlas_ph_regmap"
|
||||
#define ATLAS_DRV_NAME "atlas_ph"
|
||||
#define ATLAS_REGMAP_NAME "atlas_regmap"
|
||||
#define ATLAS_DRV_NAME "atlas"
|
||||
|
||||
#define ATLAS_REG_DEV_TYPE 0x00
|
||||
#define ATLAS_REG_DEV_VERSION 0x01
|
||||
@ -87,6 +86,16 @@ static const struct regmap_config atlas_regmap_config = {
|
||||
.val_bits = 8,
|
||||
};
|
||||
|
||||
static int atlas_buffer_num_channels(const struct iio_chan_spec *spec)
|
||||
{
|
||||
int idx = 0;
|
||||
|
||||
for (; spec->type != IIO_TIMESTAMP; spec++)
|
||||
idx++;
|
||||
|
||||
return idx;
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec atlas_ph_channels[] = {
|
||||
{
|
||||
.type = IIO_PH,
|
||||
@ -355,11 +364,12 @@ static irqreturn_t atlas_trigger_handler(int irq, void *private)
|
||||
struct iio_poll_func *pf = private;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct atlas_data *data = iio_priv(indio_dev);
|
||||
int channels = atlas_buffer_num_channels(data->chip->channels);
|
||||
int ret;
|
||||
|
||||
ret = regmap_bulk_read(data->regmap, data->chip->data_reg,
|
||||
(u8 *) &data->buffer,
|
||||
sizeof(__be32) * (data->chip->num_channels - 2));
|
||||
sizeof(__be32) * channels);
|
||||
|
||||
if (!ret)
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
|
||||
@ -681,5 +691,5 @@ static struct i2c_driver atlas_driver = {
|
||||
module_i2c_driver(atlas_driver);
|
||||
|
||||
MODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>");
|
||||
MODULE_DESCRIPTION("Atlas Scientific pH-SM sensor");
|
||||
MODULE_DESCRIPTION("Atlas Scientific SM sensors");
|
||||
MODULE_LICENSE("GPL");
|
@ -7,7 +7,7 @@
|
||||
#define __SSP_SENSORHUB_H__
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/iio/common/ssp_sensors.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/spi/spi.h>
|
||||
@ -168,9 +168,9 @@ struct ssp_sensorhub_info {
|
||||
* @fw_dl_state: firmware download state
|
||||
* @comm_lock: lock protecting the handshake
|
||||
* @pending_lock: lock protecting pending list and completion
|
||||
* @mcu_reset_gpio: mcu reset line
|
||||
* @ap_mcu_gpio: ap to mcu gpio line
|
||||
* @mcu_ap_gpio: mcu to ap gpio line
|
||||
* @mcu_reset_gpiod: mcu reset line
|
||||
* @ap_mcu_gpiod: ap to mcu gpio line
|
||||
* @mcu_ap_gpiod: mcu to ap gpio line
|
||||
* @pending_list: pending list for messages queued to be sent/read
|
||||
* @sensor_devs: registered IIO devices table
|
||||
* @enable_refcount: enable reference count for wdt (watchdog timer)
|
||||
@ -212,9 +212,9 @@ struct ssp_data {
|
||||
struct mutex comm_lock;
|
||||
struct mutex pending_lock;
|
||||
|
||||
int mcu_reset_gpio;
|
||||
int ap_mcu_gpio;
|
||||
int mcu_ap_gpio;
|
||||
struct gpio_desc *mcu_reset_gpiod;
|
||||
struct gpio_desc *ap_mcu_gpiod;
|
||||
struct gpio_desc *mcu_ap_gpiod;
|
||||
|
||||
struct list_head pending_list;
|
||||
|
||||
|
@ -9,7 +9,6 @@
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include "ssp.h"
|
||||
|
||||
@ -61,9 +60,9 @@ static const struct mfd_cell sensorhub_sensor_devs[] = {
|
||||
|
||||
static void ssp_toggle_mcu_reset_gpio(struct ssp_data *data)
|
||||
{
|
||||
gpio_set_value(data->mcu_reset_gpio, 0);
|
||||
gpiod_set_value(data->mcu_reset_gpiod, 0);
|
||||
usleep_range(1000, 1200);
|
||||
gpio_set_value(data->mcu_reset_gpio, 1);
|
||||
gpiod_set_value(data->mcu_reset_gpiod, 1);
|
||||
msleep(50);
|
||||
}
|
||||
|
||||
@ -441,7 +440,6 @@ MODULE_DEVICE_TABLE(of, ssp_of_match);
|
||||
|
||||
static struct ssp_data *ssp_parse_dt(struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
struct ssp_data *data;
|
||||
struct device_node *node = dev->of_node;
|
||||
const struct of_device_id *match;
|
||||
@ -450,26 +448,17 @@ static struct ssp_data *ssp_parse_dt(struct device *dev)
|
||||
if (!data)
|
||||
return NULL;
|
||||
|
||||
data->mcu_ap_gpio = of_get_named_gpio(node, "mcu-ap-gpios", 0);
|
||||
if (data->mcu_ap_gpio < 0)
|
||||
data->mcu_ap_gpiod = devm_gpiod_get(dev, "mcu-ap", GPIOD_IN);
|
||||
if (IS_ERR(data->mcu_ap_gpiod))
|
||||
return NULL;
|
||||
|
||||
data->ap_mcu_gpio = of_get_named_gpio(node, "ap-mcu-gpios", 0);
|
||||
if (data->ap_mcu_gpio < 0)
|
||||
data->ap_mcu_gpiod = devm_gpiod_get(dev, "ap-mcu", GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(data->ap_mcu_gpiod))
|
||||
return NULL;
|
||||
|
||||
data->mcu_reset_gpio = of_get_named_gpio(node, "mcu-reset-gpios", 0);
|
||||
if (data->mcu_reset_gpio < 0)
|
||||
return NULL;
|
||||
|
||||
ret = devm_gpio_request_one(dev, data->ap_mcu_gpio, GPIOF_OUT_INIT_HIGH,
|
||||
"ap-mcu-gpios");
|
||||
if (ret)
|
||||
return NULL;
|
||||
|
||||
ret = devm_gpio_request_one(dev, data->mcu_reset_gpio,
|
||||
GPIOF_OUT_INIT_HIGH, "mcu-reset-gpios");
|
||||
if (ret)
|
||||
data->mcu_reset_gpiod = devm_gpiod_get(dev, "mcu-reset",
|
||||
GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(data->mcu_reset_gpiod))
|
||||
return NULL;
|
||||
|
||||
match = of_match_node(ssp_of_match, node);
|
||||
|
@ -155,9 +155,9 @@ static int ssp_check_lines(struct ssp_data *data, bool state)
|
||||
{
|
||||
int delay_cnt = 0;
|
||||
|
||||
gpio_set_value_cansleep(data->ap_mcu_gpio, state);
|
||||
gpiod_set_value_cansleep(data->ap_mcu_gpiod, state);
|
||||
|
||||
while (gpio_get_value_cansleep(data->mcu_ap_gpio) != state) {
|
||||
while (gpiod_get_value_cansleep(data->mcu_ap_gpiod) != state) {
|
||||
usleep_range(3000, 3500);
|
||||
|
||||
if (data->shut_down || delay_cnt++ > 500) {
|
||||
@ -165,7 +165,7 @@ static int ssp_check_lines(struct ssp_data *data, bool state)
|
||||
__func__, state);
|
||||
|
||||
if (!state)
|
||||
gpio_set_value_cansleep(data->ap_mcu_gpio, 1);
|
||||
gpiod_set_value_cansleep(data->ap_mcu_gpiod, 1);
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
@ -197,7 +197,7 @@ static int ssp_do_transfer(struct ssp_data *data, struct ssp_msg *msg,
|
||||
|
||||
status = spi_write(data->spi, msg->buffer, SSP_HEADER_SIZE);
|
||||
if (status < 0) {
|
||||
gpio_set_value_cansleep(data->ap_mcu_gpio, 1);
|
||||
gpiod_set_value_cansleep(data->ap_mcu_gpiod, 1);
|
||||
dev_err(SSP_DEV, "%s spi_write fail\n", __func__);
|
||||
goto _error_locked;
|
||||
}
|
||||
|
@ -12,9 +12,8 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <asm/unaligned.h>
|
||||
#include <linux/iio/common/st_sensors.h>
|
||||
@ -319,63 +318,49 @@ static int st_sensors_set_drdy_int_pin(struct iio_dev *indio_dev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static struct st_sensors_platform_data *st_sensors_of_probe(struct device *dev,
|
||||
static struct st_sensors_platform_data *st_sensors_dev_probe(struct device *dev,
|
||||
struct st_sensors_platform_data *defdata)
|
||||
{
|
||||
struct st_sensors_platform_data *pdata;
|
||||
struct device_node *np = dev->of_node;
|
||||
u32 val;
|
||||
|
||||
if (!np)
|
||||
if (!dev_fwnode(dev))
|
||||
return NULL;
|
||||
|
||||
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
|
||||
if (!of_property_read_u32(np, "st,drdy-int-pin", &val) && (val <= 2))
|
||||
if (!device_property_read_u32(dev, "st,drdy-int-pin", &val) && (val <= 2))
|
||||
pdata->drdy_int_pin = (u8) val;
|
||||
else
|
||||
pdata->drdy_int_pin = defdata ? defdata->drdy_int_pin : 0;
|
||||
|
||||
pdata->open_drain = of_property_read_bool(np, "drive-open-drain");
|
||||
pdata->open_drain = device_property_read_bool(dev, "drive-open-drain");
|
||||
|
||||
return pdata;
|
||||
}
|
||||
|
||||
/**
|
||||
* st_sensors_of_name_probe() - device tree probe for ST sensor name
|
||||
* st_sensors_dev_name_probe() - device probe for ST sensor name
|
||||
* @dev: driver model representation of the device.
|
||||
* @match: the OF match table for the device, containing compatible strings
|
||||
* but also a .data field with the corresponding internal kernel name
|
||||
* used by this sensor.
|
||||
* @name: device name buffer reference.
|
||||
* @len: device name buffer length.
|
||||
*
|
||||
* In effect this function matches a compatible string to an internal kernel
|
||||
* In effect this function matches an ID to an internal kernel
|
||||
* name for a certain sensor device, so that the rest of the autodetection can
|
||||
* rely on that name from this point on. I2C/SPI devices will be renamed
|
||||
* to match the internal kernel convention.
|
||||
*/
|
||||
void st_sensors_of_name_probe(struct device *dev,
|
||||
const struct of_device_id *match,
|
||||
char *name, int len)
|
||||
void st_sensors_dev_name_probe(struct device *dev, char *name, int len)
|
||||
{
|
||||
const struct of_device_id *of_id;
|
||||
const void *match;
|
||||
|
||||
of_id = of_match_device(match, dev);
|
||||
if (!of_id || !of_id->data)
|
||||
match = device_get_match_data(dev);
|
||||
if (!match)
|
||||
return;
|
||||
|
||||
/* The name from the OF match takes precedence if present */
|
||||
strlcpy(name, of_id->data, len);
|
||||
/* The name from the match takes precedence if present */
|
||||
strlcpy(name, match, len);
|
||||
}
|
||||
EXPORT_SYMBOL(st_sensors_of_name_probe);
|
||||
#else
|
||||
static struct st_sensors_platform_data *st_sensors_of_probe(struct device *dev,
|
||||
struct st_sensors_platform_data *defdata)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
EXPORT_SYMBOL(st_sensors_dev_name_probe);
|
||||
|
||||
int st_sensors_init_sensor(struct iio_dev *indio_dev,
|
||||
struct st_sensors_platform_data *pdata)
|
||||
@ -385,7 +370,7 @@ int st_sensors_init_sensor(struct iio_dev *indio_dev,
|
||||
int err = 0;
|
||||
|
||||
/* If OF/DT pdata exists, it will take precedence of anything else */
|
||||
of_pdata = st_sensors_of_probe(indio_dev->dev.parent, pdata);
|
||||
of_pdata = st_sensors_dev_probe(indio_dev->dev.parent, pdata);
|
||||
if (of_pdata)
|
||||
pdata = of_pdata;
|
||||
|
||||
|
@ -11,8 +11,6 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include <linux/iio/common/st_sensors_i2c.h>
|
||||
@ -68,25 +66,6 @@ int st_sensors_i2c_configure(struct iio_dev *indio_dev,
|
||||
}
|
||||
EXPORT_SYMBOL(st_sensors_i2c_configure);
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
int st_sensors_match_acpi_device(struct device *dev)
|
||||
{
|
||||
const struct acpi_device_id *acpi_id;
|
||||
kernel_ulong_t driver_data = 0;
|
||||
|
||||
if (ACPI_HANDLE(dev)) {
|
||||
acpi_id = acpi_match_device(dev->driver->acpi_match_table, dev);
|
||||
if (!acpi_id) {
|
||||
dev_err(dev, "No driver data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
driver_data = acpi_id->driver_data;
|
||||
}
|
||||
return driver_data;
|
||||
}
|
||||
EXPORT_SYMBOL(st_sensors_match_acpi_device);
|
||||
#endif
|
||||
|
||||
MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
|
||||
MODULE_DESCRIPTION("STMicroelectronics ST-sensors i2c driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include <linux/iio/common/st_sensors_spi.h>
|
||||
@ -37,14 +38,15 @@ static const struct regmap_config st_sensors_spi_regmap_multiread_bit_config = {
|
||||
*/
|
||||
static bool st_sensors_is_spi_3_wire(struct spi_device *spi)
|
||||
{
|
||||
struct device_node *np = spi->dev.of_node;
|
||||
struct st_sensors_platform_data *pdata;
|
||||
struct device *dev = &spi->dev;
|
||||
|
||||
pdata = (struct st_sensors_platform_data *)spi->dev.platform_data;
|
||||
if ((np && of_property_read_bool(np, "spi-3wire")) ||
|
||||
(pdata && pdata->spi_3wire)) {
|
||||
if (device_property_read_bool(dev, "spi-3wire"))
|
||||
return true;
|
||||
|
||||
pdata = (struct st_sensors_platform_data *)dev->platform_data;
|
||||
if (pdata && pdata->spi_3wire)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -19,6 +19,9 @@
|
||||
|
||||
/**
|
||||
* st_sensors_new_samples_available() - check if more samples came in
|
||||
* @indio_dev: IIO device reference.
|
||||
* @sdata: Sensor data.
|
||||
*
|
||||
* returns:
|
||||
* 0 - no new samples available
|
||||
* 1 - new samples available
|
||||
|
@ -15,7 +15,6 @@
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/property.h>
|
||||
|
||||
#include <dt-bindings/iio/adi,ad5592r.h>
|
||||
|
@ -12,7 +12,6 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
@ -202,7 +201,6 @@ static int ad7303_probe(struct spi_device *spi)
|
||||
const struct spi_device_id *id = spi_get_device_id(spi);
|
||||
struct iio_dev *indio_dev;
|
||||
struct ad7303_state *st;
|
||||
bool ext_ref;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
||||
@ -224,24 +222,15 @@ static int ad7303_probe(struct spi_device *spi)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (spi->dev.of_node) {
|
||||
ext_ref = of_property_read_bool(spi->dev.of_node,
|
||||
"REF-supply");
|
||||
} else {
|
||||
struct ad7303_platform_data *pdata = spi->dev.platform_data;
|
||||
if (pdata && pdata->use_external_ref)
|
||||
ext_ref = true;
|
||||
else
|
||||
ext_ref = false;
|
||||
st->vref_reg = devm_regulator_get_optional(&spi->dev, "REF");
|
||||
if (IS_ERR(st->vref_reg)) {
|
||||
ret = PTR_ERR(st->vref_reg);
|
||||
if (ret != -ENODEV)
|
||||
goto err_disable_vdd_reg;
|
||||
st->vref_reg = NULL;
|
||||
}
|
||||
|
||||
if (ext_ref) {
|
||||
st->vref_reg = devm_regulator_get(&spi->dev, "REF");
|
||||
if (IS_ERR(st->vref_reg)) {
|
||||
ret = PTR_ERR(st->vref_reg);
|
||||
goto err_disable_vdd_reg;
|
||||
}
|
||||
|
||||
if (st->vref_reg) {
|
||||
ret = regulator_enable(st->vref_reg);
|
||||
if (ret)
|
||||
goto err_disable_vdd_reg;
|
||||
|
@ -20,13 +20,11 @@
|
||||
/**
|
||||
* struct stm32_dac_priv - stm32 DAC core private data
|
||||
* @pclk: peripheral clock common for all DACs
|
||||
* @rst: peripheral reset control
|
||||
* @vref: regulator reference
|
||||
* @common: Common data for all DAC instances
|
||||
*/
|
||||
struct stm32_dac_priv {
|
||||
struct clk *pclk;
|
||||
struct reset_control *rst;
|
||||
struct regulator *vref;
|
||||
struct stm32_dac_common common;
|
||||
};
|
||||
@ -94,6 +92,7 @@ static int stm32_dac_probe(struct platform_device *pdev)
|
||||
struct regmap *regmap;
|
||||
struct resource *res;
|
||||
void __iomem *mmio;
|
||||
struct reset_control *rst;
|
||||
int ret;
|
||||
|
||||
if (!dev->of_node)
|
||||
@ -148,11 +147,19 @@ static int stm32_dac_probe(struct platform_device *pdev)
|
||||
priv->common.vref_mv = ret / 1000;
|
||||
dev_dbg(dev, "vref+=%dmV\n", priv->common.vref_mv);
|
||||
|
||||
priv->rst = devm_reset_control_get_exclusive(dev, NULL);
|
||||
if (!IS_ERR(priv->rst)) {
|
||||
reset_control_assert(priv->rst);
|
||||
rst = devm_reset_control_get_optional_exclusive(dev, NULL);
|
||||
if (rst) {
|
||||
if (IS_ERR(rst)) {
|
||||
ret = PTR_ERR(rst);
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(dev, "reset get failed, %d\n", ret);
|
||||
|
||||
goto err_hw_stop;
|
||||
}
|
||||
|
||||
reset_control_assert(rst);
|
||||
udelay(2);
|
||||
reset_control_deassert(priv->rst);
|
||||
reset_control_deassert(rst);
|
||||
}
|
||||
|
||||
if (cfg && cfg->has_hfsel) {
|
||||
|
@ -14,11 +14,10 @@
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/gcd.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <asm/div64.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
@ -34,6 +33,7 @@ enum {
|
||||
struct adf4350_state {
|
||||
struct spi_device *spi;
|
||||
struct regulator *reg;
|
||||
struct gpio_desc *lock_detect_gpiod;
|
||||
struct adf4350_platform_data *pdata;
|
||||
struct clk *clk;
|
||||
unsigned long clkin;
|
||||
@ -61,7 +61,6 @@ static struct adf4350_platform_data default_pdata = {
|
||||
.r3_user_settings = ADF4350_REG3_12BIT_CLKDIV_MODE(0),
|
||||
.r4_user_settings = ADF4350_REG4_OUTPUT_PWR(3) |
|
||||
ADF4350_REG4_MUTE_TILL_LOCK_EN,
|
||||
.gpio_lock_detect = -1,
|
||||
};
|
||||
|
||||
static int adf4350_sync_config(struct adf4350_state *st)
|
||||
@ -317,8 +316,8 @@ static ssize_t adf4350_read(struct iio_dev *indio_dev,
|
||||
(u64)st->fpfd;
|
||||
do_div(val, st->r1_mod * (1 << st->r4_rf_div_sel));
|
||||
/* PLL unlocked? return error */
|
||||
if (gpio_is_valid(st->pdata->gpio_lock_detect))
|
||||
if (!gpio_get_value(st->pdata->gpio_lock_detect)) {
|
||||
if (st->lock_detect_gpiod)
|
||||
if (!gpiod_get_value(st->lock_detect_gpiod)) {
|
||||
dev_dbg(&st->spi->dev, "PLL un-locked\n");
|
||||
ret = -EBUSY;
|
||||
}
|
||||
@ -381,7 +380,6 @@ static struct adf4350_platform_data *adf4350_parse_dt(struct device *dev)
|
||||
struct device_node *np = dev->of_node;
|
||||
struct adf4350_platform_data *pdata;
|
||||
unsigned int tmp;
|
||||
int ret;
|
||||
|
||||
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
|
||||
if (!pdata)
|
||||
@ -401,12 +399,6 @@ static struct adf4350_platform_data *adf4350_parse_dt(struct device *dev)
|
||||
of_property_read_u32(np, "adi,reference-div-factor", &tmp);
|
||||
pdata->ref_div_factor = tmp;
|
||||
|
||||
ret = of_get_gpio(np, 0);
|
||||
if (ret < 0)
|
||||
pdata->gpio_lock_detect = -1;
|
||||
else
|
||||
pdata->gpio_lock_detect = ret;
|
||||
|
||||
pdata->ref_doubler_en = of_property_read_bool(np,
|
||||
"adi,reference-doubler-enable");
|
||||
pdata->ref_div2_en = of_property_read_bool(np,
|
||||
@ -561,16 +553,10 @@ static int adf4350_probe(struct spi_device *spi)
|
||||
|
||||
memset(st->regs_hw, 0xFF, sizeof(st->regs_hw));
|
||||
|
||||
if (gpio_is_valid(pdata->gpio_lock_detect)) {
|
||||
ret = devm_gpio_request(&spi->dev, pdata->gpio_lock_detect,
|
||||
indio_dev->name);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev, "fail to request lock detect GPIO-%d",
|
||||
pdata->gpio_lock_detect);
|
||||
goto error_disable_reg;
|
||||
}
|
||||
gpio_direction_input(pdata->gpio_lock_detect);
|
||||
}
|
||||
st->lock_detect_gpiod = devm_gpiod_get_optional(&spi->dev, NULL,
|
||||
GPIOD_IN);
|
||||
if (IS_ERR(st->lock_detect_gpiod))
|
||||
return PTR_ERR(st->lock_detect_gpiod);
|
||||
|
||||
if (pdata->power_up_frequency) {
|
||||
ret = adf4350_set_freq(st, pdata->power_up_frequency);
|
||||
|
@ -75,26 +75,26 @@ config BMG160_SPI
|
||||
select REGMAP_SPI
|
||||
|
||||
config FXAS21002C
|
||||
tristate "NXP FXAS21002C Gyro Sensor"
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
select FXAS21002C_I2C if (I2C)
|
||||
select FXAS21002C_SPI if (SPI)
|
||||
depends on (I2C || SPI_MASTER)
|
||||
help
|
||||
Say yes here to build support for NXP FXAS21002C Tri-axis Gyro
|
||||
Sensor driver connected via I2C or SPI.
|
||||
tristate "NXP FXAS21002C Gyro Sensor"
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
select FXAS21002C_I2C if (I2C)
|
||||
select FXAS21002C_SPI if (SPI)
|
||||
depends on (I2C || SPI_MASTER)
|
||||
help
|
||||
Say yes here to build support for NXP FXAS21002C Tri-axis Gyro
|
||||
Sensor driver connected via I2C or SPI.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called fxas21002c_i2c or fxas21002c_spi.
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called fxas21002c_i2c or fxas21002c_spi.
|
||||
|
||||
config FXAS21002C_I2C
|
||||
tristate
|
||||
select REGMAP_I2C
|
||||
tristate
|
||||
select REGMAP_I2C
|
||||
|
||||
config FXAS21002C_SPI
|
||||
tristate
|
||||
select REGMAP_SPI
|
||||
tristate
|
||||
select REGMAP_SPI
|
||||
|
||||
config HID_SENSOR_GYRO_3D
|
||||
depends on HID_SENSOR_HUB
|
||||
|
@ -59,6 +59,7 @@
|
||||
struct adis16136_chip_info {
|
||||
unsigned int precision;
|
||||
unsigned int fullscale;
|
||||
const struct adis_timeout *timeouts;
|
||||
};
|
||||
|
||||
struct adis16136 {
|
||||
@ -185,12 +186,12 @@ static int adis16136_set_freq(struct adis16136 *adis16136, unsigned int freq)
|
||||
return adis_write_reg_16(&adis16136->adis, ADIS16136_REG_SMPL_PRD, t);
|
||||
}
|
||||
|
||||
static int adis16136_get_freq(struct adis16136 *adis16136, unsigned int *freq)
|
||||
static int __adis16136_get_freq(struct adis16136 *adis16136, unsigned int *freq)
|
||||
{
|
||||
uint16_t t;
|
||||
int ret;
|
||||
|
||||
ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_SMPL_PRD, &t);
|
||||
ret = __adis_read_reg_16(&adis16136->adis, ADIS16136_REG_SMPL_PRD, &t);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -224,10 +225,13 @@ static ssize_t adis16136_read_frequency(struct device *dev,
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct adis16136 *adis16136 = iio_priv(indio_dev);
|
||||
struct mutex *slock = &adis16136->adis.state_lock;
|
||||
unsigned int freq;
|
||||
int ret;
|
||||
|
||||
ret = adis16136_get_freq(adis16136, &freq);
|
||||
mutex_lock(slock);
|
||||
ret = __adis16136_get_freq(adis16136, &freq);
|
||||
mutex_unlock(slock);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -252,42 +256,50 @@ static const unsigned adis16136_3db_divisors[] = {
|
||||
static int adis16136_set_filter(struct iio_dev *indio_dev, int val)
|
||||
{
|
||||
struct adis16136 *adis16136 = iio_priv(indio_dev);
|
||||
struct mutex *slock = &adis16136->adis.state_lock;
|
||||
unsigned int freq;
|
||||
int i, ret;
|
||||
|
||||
ret = adis16136_get_freq(adis16136, &freq);
|
||||
mutex_lock(slock);
|
||||
ret = __adis16136_get_freq(adis16136, &freq);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto out_unlock;
|
||||
|
||||
for (i = ARRAY_SIZE(adis16136_3db_divisors) - 1; i >= 1; i--) {
|
||||
if (freq / adis16136_3db_divisors[i] >= val)
|
||||
break;
|
||||
}
|
||||
|
||||
return adis_write_reg_16(&adis16136->adis, ADIS16136_REG_AVG_CNT, i);
|
||||
ret = __adis_write_reg_16(&adis16136->adis, ADIS16136_REG_AVG_CNT, i);
|
||||
out_unlock:
|
||||
mutex_unlock(slock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adis16136_get_filter(struct iio_dev *indio_dev, int *val)
|
||||
{
|
||||
struct adis16136 *adis16136 = iio_priv(indio_dev);
|
||||
struct mutex *slock = &adis16136->adis.state_lock;
|
||||
unsigned int freq;
|
||||
uint16_t val16;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
mutex_lock(slock);
|
||||
|
||||
ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_AVG_CNT, &val16);
|
||||
ret = __adis_read_reg_16(&adis16136->adis, ADIS16136_REG_AVG_CNT,
|
||||
&val16);
|
||||
if (ret)
|
||||
goto err_unlock;
|
||||
|
||||
ret = adis16136_get_freq(adis16136, &freq);
|
||||
ret = __adis16136_get_freq(adis16136, &freq);
|
||||
if (ret)
|
||||
goto err_unlock;
|
||||
|
||||
*val = freq / adis16136_3db_divisors[val16 & 0x07];
|
||||
|
||||
err_unlock:
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
mutex_unlock(slock);
|
||||
|
||||
return ret ? ret : IIO_VAL_INT;
|
||||
}
|
||||
@ -460,7 +472,6 @@ static const struct adis_data adis16136_data = {
|
||||
.msc_ctrl_reg = ADIS16136_REG_MSC_CTRL,
|
||||
|
||||
.self_test_mask = ADIS16136_MSC_CTRL_SELF_TEST,
|
||||
.startup_delay = 80,
|
||||
|
||||
.read_delay = 10,
|
||||
.write_delay = 10,
|
||||
@ -479,30 +490,63 @@ enum adis16136_id {
|
||||
ID_ADIS16137,
|
||||
};
|
||||
|
||||
static const struct adis_timeout adis16133_timeouts = {
|
||||
.reset_ms = 75,
|
||||
.sw_reset_ms = 75,
|
||||
.self_test_ms = 50,
|
||||
};
|
||||
|
||||
static const struct adis_timeout adis16136_timeouts = {
|
||||
.reset_ms = 128,
|
||||
.sw_reset_ms = 75,
|
||||
.self_test_ms = 245,
|
||||
};
|
||||
|
||||
static const struct adis16136_chip_info adis16136_chip_info[] = {
|
||||
[ID_ADIS16133] = {
|
||||
.precision = IIO_DEGREE_TO_RAD(1200),
|
||||
.fullscale = 24000,
|
||||
.timeouts = &adis16133_timeouts,
|
||||
},
|
||||
[ID_ADIS16135] = {
|
||||
.precision = IIO_DEGREE_TO_RAD(300),
|
||||
.fullscale = 24000,
|
||||
.timeouts = &adis16133_timeouts,
|
||||
},
|
||||
[ID_ADIS16136] = {
|
||||
.precision = IIO_DEGREE_TO_RAD(450),
|
||||
.fullscale = 24623,
|
||||
.timeouts = &adis16136_timeouts,
|
||||
},
|
||||
[ID_ADIS16137] = {
|
||||
.precision = IIO_DEGREE_TO_RAD(1000),
|
||||
.fullscale = 24609,
|
||||
.timeouts = &adis16136_timeouts,
|
||||
},
|
||||
};
|
||||
|
||||
static struct adis_data *adis16136_adis_data_alloc(struct adis16136 *st,
|
||||
struct device *dev)
|
||||
{
|
||||
struct adis_data *data;
|
||||
|
||||
data = devm_kmalloc(dev, sizeof(struct adis_data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
memcpy(data, &adis16136_data, sizeof(*data));
|
||||
|
||||
data->timeouts = st->chip_info->timeouts;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static int adis16136_probe(struct spi_device *spi)
|
||||
{
|
||||
const struct spi_device_id *id = spi_get_device_id(spi);
|
||||
struct adis16136 *adis16136;
|
||||
struct iio_dev *indio_dev;
|
||||
const struct adis_data *adis16136_data;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adis16136));
|
||||
@ -521,7 +565,11 @@ static int adis16136_probe(struct spi_device *spi)
|
||||
indio_dev->info = &adis16136_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
ret = adis_init(&adis16136->adis, indio_dev, spi, &adis16136_data);
|
||||
adis16136_data = adis16136_adis_data_alloc(adis16136, &spi->dev);
|
||||
if (IS_ERR(adis16136_data))
|
||||
return PTR_ERR(adis16136_data);
|
||||
|
||||
ret = adis_init(&adis16136->adis, indio_dev, spi, adis16136_data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -293,7 +293,7 @@ static int adis16260_write_raw(struct iio_dev *indio_dev,
|
||||
addr = adis16260_addresses[chan->scan_index][1];
|
||||
return adis_write_reg_16(adis, addr, val);
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
mutex_lock(&adis->state_lock);
|
||||
if (spi_get_device_id(adis->spi)->driver_data)
|
||||
t = 256 / val;
|
||||
else
|
||||
@ -308,9 +308,9 @@ static int adis16260_write_raw(struct iio_dev *indio_dev,
|
||||
adis->spi->max_speed_hz = ADIS16260_SPI_SLOW;
|
||||
else
|
||||
adis->spi->max_speed_hz = ADIS16260_SPI_FAST;
|
||||
ret = adis_write_reg_8(adis, ADIS16260_SMPL_PRD, t);
|
||||
ret = __adis_write_reg_8(adis, ADIS16260_SMPL_PRD, t);
|
||||
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
mutex_unlock(&adis->state_lock);
|
||||
return ret;
|
||||
}
|
||||
return -EINVAL;
|
||||
@ -332,6 +332,12 @@ static const char * const adis1620_status_error_msgs[] = {
|
||||
[ADIS16260_DIAG_STAT_POWER_LOW_BIT] = "Power supply below 4.75",
|
||||
};
|
||||
|
||||
static const struct adis_timeout adis16260_timeouts = {
|
||||
.reset_ms = ADIS16260_STARTUP_DELAY,
|
||||
.sw_reset_ms = ADIS16260_STARTUP_DELAY,
|
||||
.self_test_ms = ADIS16260_STARTUP_DELAY,
|
||||
};
|
||||
|
||||
static const struct adis_data adis16260_data = {
|
||||
.write_delay = 30,
|
||||
.read_delay = 30,
|
||||
@ -340,7 +346,7 @@ static const struct adis_data adis16260_data = {
|
||||
.diag_stat_reg = ADIS16260_DIAG_STAT,
|
||||
|
||||
.self_test_mask = ADIS16260_MSC_CTRL_MEM_TEST,
|
||||
.startup_delay = ADIS16260_STARTUP_DELAY,
|
||||
.timeouts = &adis16260_timeouts,
|
||||
|
||||
.status_error_msgs = adis1620_status_error_msgs,
|
||||
.status_error_mask = BIT(ADIS16260_DIAG_STAT_FLASH_CHK_BIT) |
|
||||
|
@ -15,7 +15,6 @@
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/module.h>
|
||||
|
@ -28,7 +28,7 @@
|
||||
* struct st_sensors_platform_data - gyro platform data
|
||||
* @drdy_int_pin: DRDY on gyros is available only on INT2 pin.
|
||||
*/
|
||||
static const struct st_sensors_platform_data gyro_pdata = {
|
||||
static __maybe_unused const struct st_sensors_platform_data gyro_pdata = {
|
||||
.drdy_int_pin = 2,
|
||||
};
|
||||
|
||||
|
@ -138,7 +138,6 @@ static const struct st_sensor_settings st_gyro_sensors_settings[] = {
|
||||
[2] = LSM330DLC_GYRO_DEV_NAME,
|
||||
[3] = L3G4IS_GYRO_DEV_NAME,
|
||||
[4] = LSM330_GYRO_DEV_NAME,
|
||||
[5] = LSM9DS0_GYRO_DEV_NAME,
|
||||
},
|
||||
.ch = (struct iio_chan_spec *)st_gyro_16bit_channels,
|
||||
.odr = {
|
||||
@ -208,6 +207,80 @@ static const struct st_sensor_settings st_gyro_sensors_settings[] = {
|
||||
.multi_read_bit = true,
|
||||
.bootime = 2,
|
||||
},
|
||||
{
|
||||
.wai = 0xd4,
|
||||
.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
|
||||
.sensors_supported = {
|
||||
[0] = LSM9DS0_GYRO_DEV_NAME,
|
||||
},
|
||||
.ch = (struct iio_chan_spec *)st_gyro_16bit_channels,
|
||||
.odr = {
|
||||
.addr = 0x20,
|
||||
.mask = GENMASK(7, 6),
|
||||
.odr_avl = {
|
||||
{ .hz = 95, .value = 0x00, },
|
||||
{ .hz = 190, .value = 0x01, },
|
||||
{ .hz = 380, .value = 0x02, },
|
||||
{ .hz = 760, .value = 0x03, },
|
||||
},
|
||||
},
|
||||
.pw = {
|
||||
.addr = 0x20,
|
||||
.mask = BIT(3),
|
||||
.value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE,
|
||||
.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
|
||||
},
|
||||
.enable_axis = {
|
||||
.addr = ST_SENSORS_DEFAULT_AXIS_ADDR,
|
||||
.mask = ST_SENSORS_DEFAULT_AXIS_MASK,
|
||||
},
|
||||
.fs = {
|
||||
.addr = 0x23,
|
||||
.mask = GENMASK(5, 4),
|
||||
.fs_avl = {
|
||||
[0] = {
|
||||
.num = ST_GYRO_FS_AVL_245DPS,
|
||||
.value = 0x00,
|
||||
.gain = IIO_DEGREE_TO_RAD(8750),
|
||||
},
|
||||
[1] = {
|
||||
.num = ST_GYRO_FS_AVL_500DPS,
|
||||
.value = 0x01,
|
||||
.gain = IIO_DEGREE_TO_RAD(17500),
|
||||
},
|
||||
[2] = {
|
||||
.num = ST_GYRO_FS_AVL_2000DPS,
|
||||
.value = 0x02,
|
||||
.gain = IIO_DEGREE_TO_RAD(70000),
|
||||
},
|
||||
},
|
||||
},
|
||||
.bdu = {
|
||||
.addr = 0x23,
|
||||
.mask = BIT(7),
|
||||
},
|
||||
.drdy_irq = {
|
||||
.int2 = {
|
||||
.addr = 0x22,
|
||||
.mask = BIT(3),
|
||||
},
|
||||
/*
|
||||
* The sensor has IHL (active low) and open
|
||||
* drain settings, but only for INT1 and not
|
||||
* for the DRDY line on INT2.
|
||||
*/
|
||||
.stat_drdy = {
|
||||
.addr = ST_SENSORS_DEFAULT_STAT_ADDR,
|
||||
.mask = GENMASK(2, 0),
|
||||
},
|
||||
},
|
||||
.sim = {
|
||||
.addr = 0x23,
|
||||
.value = BIT(0),
|
||||
},
|
||||
.multi_read_bit = true,
|
||||
.bootime = 2,
|
||||
},
|
||||
{
|
||||
.wai = 0xd7,
|
||||
.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
|
||||
|
@ -17,7 +17,6 @@
|
||||
#include <linux/iio/common/st_sensors_i2c.h>
|
||||
#include "st_gyro.h"
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id st_gyro_of_match[] = {
|
||||
{
|
||||
.compatible = "st,l3g4200d-gyro",
|
||||
@ -58,9 +57,6 @@ static const struct of_device_id st_gyro_of_match[] = {
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, st_gyro_of_match);
|
||||
#else
|
||||
#define st_gyro_of_match NULL
|
||||
#endif
|
||||
|
||||
static int st_gyro_i2c_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
@ -70,8 +66,7 @@ static int st_gyro_i2c_probe(struct i2c_client *client,
|
||||
struct iio_dev *indio_dev;
|
||||
int err;
|
||||
|
||||
st_sensors_of_name_probe(&client->dev, st_gyro_of_match,
|
||||
client->name, sizeof(client->name));
|
||||
st_sensors_dev_name_probe(&client->dev, client->name, sizeof(client->name));
|
||||
|
||||
settings = st_gyro_get_settings(client->name);
|
||||
if (!settings) {
|
||||
@ -122,7 +117,7 @@ MODULE_DEVICE_TABLE(i2c, st_gyro_id_table);
|
||||
static struct i2c_driver st_gyro_driver = {
|
||||
.driver = {
|
||||
.name = "st-gyro-i2c",
|
||||
.of_match_table = of_match_ptr(st_gyro_of_match),
|
||||
.of_match_table = st_gyro_of_match,
|
||||
},
|
||||
.probe = st_gyro_i2c_probe,
|
||||
.remove = st_gyro_i2c_remove,
|
||||
|
@ -17,7 +17,6 @@
|
||||
#include <linux/iio/common/st_sensors_spi.h>
|
||||
#include "st_gyro.h"
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
/*
|
||||
* For new single-chip sensors use <device_name> as compatible string.
|
||||
* For old single-chip devices keep <device_name>-gyro to maintain
|
||||
@ -63,9 +62,6 @@ static const struct of_device_id st_gyro_of_match[] = {
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, st_gyro_of_match);
|
||||
#else
|
||||
#define st_gyro_of_match NULL
|
||||
#endif
|
||||
|
||||
static int st_gyro_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
@ -74,8 +70,7 @@ static int st_gyro_spi_probe(struct spi_device *spi)
|
||||
struct iio_dev *indio_dev;
|
||||
int err;
|
||||
|
||||
st_sensors_of_name_probe(&spi->dev, st_gyro_of_match,
|
||||
spi->modalias, sizeof(spi->modalias));
|
||||
st_sensors_dev_name_probe(&spi->dev, spi->modalias, sizeof(spi->modalias));
|
||||
|
||||
settings = st_gyro_get_settings(spi->modalias);
|
||||
if (!settings) {
|
||||
@ -126,7 +121,7 @@ MODULE_DEVICE_TABLE(spi, st_gyro_id_table);
|
||||
static struct spi_driver st_gyro_driver = {
|
||||
.driver = {
|
||||
.name = "st-gyro-spi",
|
||||
.of_match_table = of_match_ptr(st_gyro_of_match),
|
||||
.of_match_table = st_gyro_of_match,
|
||||
},
|
||||
.probe = st_gyro_spi_probe,
|
||||
.remove = st_gyro_spi_remove,
|
||||
|
@ -174,7 +174,6 @@ static irqreturn_t dht11_handle_irq(int irq, void *data)
|
||||
struct iio_dev *iio = data;
|
||||
struct dht11 *dht11 = iio_priv(iio);
|
||||
|
||||
/* TODO: Consider making the handler safe for IRQ sharing */
|
||||
if (dht11->num_edges < DHT11_EDGES_PER_READ && dht11->num_edges >= 0) {
|
||||
dht11->edges[dht11->num_edges].ts = ktime_get_boottime_ns();
|
||||
dht11->edges[dht11->num_edges++].value =
|
||||
|
@ -24,13 +24,6 @@
|
||||
#define HTS221_REG_CNTRL1_ADDR 0x20
|
||||
#define HTS221_REG_CNTRL2_ADDR 0x21
|
||||
|
||||
#define HTS221_REG_AVG_ADDR 0x10
|
||||
#define HTS221_REG_H_OUT_L 0x28
|
||||
#define HTS221_REG_T_OUT_L 0x2a
|
||||
|
||||
#define HTS221_HUMIDITY_AVG_MASK 0x07
|
||||
#define HTS221_TEMP_AVG_MASK 0x38
|
||||
|
||||
#define HTS221_ODR_MASK 0x03
|
||||
#define HTS221_BDU_MASK BIT(2)
|
||||
#define HTS221_ENABLE_MASK BIT(7)
|
||||
@ -66,8 +59,8 @@ static const struct hts221_odr hts221_odr_table[] = {
|
||||
|
||||
static const struct hts221_avg hts221_avg_list[] = {
|
||||
{
|
||||
.addr = HTS221_REG_AVG_ADDR,
|
||||
.mask = HTS221_HUMIDITY_AVG_MASK,
|
||||
.addr = 0x10,
|
||||
.mask = 0x07,
|
||||
.avg_avl = {
|
||||
4, /* 0.4 %RH */
|
||||
8, /* 0.3 %RH */
|
||||
@ -80,8 +73,8 @@ static const struct hts221_avg hts221_avg_list[] = {
|
||||
},
|
||||
},
|
||||
{
|
||||
.addr = HTS221_REG_AVG_ADDR,
|
||||
.mask = HTS221_TEMP_AVG_MASK,
|
||||
.addr = 0x10,
|
||||
.mask = 0x38,
|
||||
.avg_avl = {
|
||||
2, /* 0.08 degC */
|
||||
4, /* 0.05 degC */
|
||||
@ -98,7 +91,7 @@ static const struct hts221_avg hts221_avg_list[] = {
|
||||
static const struct iio_chan_spec hts221_channels[] = {
|
||||
{
|
||||
.type = IIO_HUMIDITYRELATIVE,
|
||||
.address = HTS221_REG_H_OUT_L,
|
||||
.address = 0x28,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_OFFSET) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
@ -114,7 +107,7 @@ static const struct iio_chan_spec hts221_channels[] = {
|
||||
},
|
||||
{
|
||||
.type = IIO_TEMP,
|
||||
.address = HTS221_REG_T_OUT_L,
|
||||
.address = 0x2a,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_OFFSET) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
|
@ -42,14 +42,14 @@ struct poll_table_struct;
|
||||
|
||||
__poll_t iio_buffer_poll(struct file *filp,
|
||||
struct poll_table_struct *wait);
|
||||
ssize_t iio_buffer_read_first_n_outer(struct file *filp, char __user *buf,
|
||||
size_t n, loff_t *f_ps);
|
||||
ssize_t iio_buffer_read_outer(struct file *filp, char __user *buf,
|
||||
size_t n, loff_t *f_ps);
|
||||
|
||||
int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev);
|
||||
void iio_buffer_free_sysfs_and_mask(struct iio_dev *indio_dev);
|
||||
|
||||
#define iio_buffer_poll_addr (&iio_buffer_poll)
|
||||
#define iio_buffer_read_first_n_outer_addr (&iio_buffer_read_first_n_outer)
|
||||
#define iio_buffer_read_outer_addr (&iio_buffer_read_outer)
|
||||
|
||||
void iio_disable_all_buffers(struct iio_dev *indio_dev);
|
||||
void iio_buffer_wakeup_poll(struct iio_dev *indio_dev);
|
||||
@ -57,7 +57,7 @@ void iio_buffer_wakeup_poll(struct iio_dev *indio_dev);
|
||||
#else
|
||||
|
||||
#define iio_buffer_poll_addr NULL
|
||||
#define iio_buffer_read_first_n_outer_addr NULL
|
||||
#define iio_buffer_read_outer_addr NULL
|
||||
|
||||
static inline int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev)
|
||||
{
|
||||
|
@ -26,7 +26,14 @@
|
||||
#define ADIS_MSC_CTRL_DATA_RDY_DIO2 BIT(0)
|
||||
#define ADIS_GLOB_CMD_SW_RESET BIT(7)
|
||||
|
||||
int adis_write_reg(struct adis *adis, unsigned int reg,
|
||||
/**
|
||||
* __adis_write_reg() - write N bytes to register (unlocked version)
|
||||
* @adis: The adis device
|
||||
* @reg: The address of the lower of the two registers
|
||||
* @value: The value to write to device (up to 4 bytes)
|
||||
* @size: The size of the @value (in bytes)
|
||||
*/
|
||||
int __adis_write_reg(struct adis *adis, unsigned int reg,
|
||||
unsigned int value, unsigned int size)
|
||||
{
|
||||
unsigned int page = reg / ADIS_PAGE_SIZE;
|
||||
@ -38,7 +45,8 @@ int adis_write_reg(struct adis *adis, unsigned int reg,
|
||||
.bits_per_word = 8,
|
||||
.len = 2,
|
||||
.cs_change = 1,
|
||||
.delay_usecs = adis->data->write_delay,
|
||||
.delay.value = adis->data->write_delay,
|
||||
.delay.unit = SPI_DELAY_UNIT_USECS,
|
||||
.cs_change_delay.value = adis->data->cs_change_delay,
|
||||
.cs_change_delay.unit = SPI_DELAY_UNIT_USECS,
|
||||
}, {
|
||||
@ -46,7 +54,8 @@ int adis_write_reg(struct adis *adis, unsigned int reg,
|
||||
.bits_per_word = 8,
|
||||
.len = 2,
|
||||
.cs_change = 1,
|
||||
.delay_usecs = adis->data->write_delay,
|
||||
.delay.value = adis->data->write_delay,
|
||||
.delay.unit = SPI_DELAY_UNIT_USECS,
|
||||
.cs_change_delay.value = adis->data->cs_change_delay,
|
||||
.cs_change_delay.unit = SPI_DELAY_UNIT_USECS,
|
||||
}, {
|
||||
@ -54,24 +63,25 @@ int adis_write_reg(struct adis *adis, unsigned int reg,
|
||||
.bits_per_word = 8,
|
||||
.len = 2,
|
||||
.cs_change = 1,
|
||||
.delay_usecs = adis->data->write_delay,
|
||||
.delay.value = adis->data->write_delay,
|
||||
.delay.unit = SPI_DELAY_UNIT_USECS,
|
||||
.cs_change_delay.value = adis->data->cs_change_delay,
|
||||
.cs_change_delay.unit = SPI_DELAY_UNIT_USECS,
|
||||
}, {
|
||||
.tx_buf = adis->tx + 6,
|
||||
.bits_per_word = 8,
|
||||
.len = 2,
|
||||
.delay_usecs = adis->data->write_delay,
|
||||
.delay.value = adis->data->write_delay,
|
||||
.delay.unit = SPI_DELAY_UNIT_USECS,
|
||||
}, {
|
||||
.tx_buf = adis->tx + 8,
|
||||
.bits_per_word = 8,
|
||||
.len = 2,
|
||||
.delay_usecs = adis->data->write_delay,
|
||||
.delay.value = adis->data->write_delay,
|
||||
.delay.unit = SPI_DELAY_UNIT_USECS,
|
||||
},
|
||||
};
|
||||
|
||||
mutex_lock(&adis->txrx_lock);
|
||||
|
||||
spi_message_init(&msg);
|
||||
|
||||
if (adis->current_page != page) {
|
||||
@ -96,8 +106,7 @@ int adis_write_reg(struct adis *adis, unsigned int reg,
|
||||
adis->tx[3] = value & 0xff;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
goto out_unlock;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
xfers[size].cs_change = 0;
|
||||
@ -113,20 +122,18 @@ int adis_write_reg(struct adis *adis, unsigned int reg,
|
||||
adis->current_page = page;
|
||||
}
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&adis->txrx_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(adis_write_reg);
|
||||
EXPORT_SYMBOL_GPL(__adis_write_reg);
|
||||
|
||||
/**
|
||||
* adis_read_reg() - read 2 bytes from a 16-bit register
|
||||
* __adis_read_reg() - read N bytes from register (unlocked version)
|
||||
* @adis: The adis device
|
||||
* @reg: The address of the lower of the two registers
|
||||
* @val: The value read back from the device
|
||||
* @size: The size of the @val buffer
|
||||
*/
|
||||
int adis_read_reg(struct adis *adis, unsigned int reg,
|
||||
int __adis_read_reg(struct adis *adis, unsigned int reg,
|
||||
unsigned int *val, unsigned int size)
|
||||
{
|
||||
unsigned int page = reg / ADIS_PAGE_SIZE;
|
||||
@ -138,7 +145,8 @@ int adis_read_reg(struct adis *adis, unsigned int reg,
|
||||
.bits_per_word = 8,
|
||||
.len = 2,
|
||||
.cs_change = 1,
|
||||
.delay_usecs = adis->data->write_delay,
|
||||
.delay.value = adis->data->write_delay,
|
||||
.delay.unit = SPI_DELAY_UNIT_USECS,
|
||||
.cs_change_delay.value = adis->data->cs_change_delay,
|
||||
.cs_change_delay.unit = SPI_DELAY_UNIT_USECS,
|
||||
}, {
|
||||
@ -146,7 +154,8 @@ int adis_read_reg(struct adis *adis, unsigned int reg,
|
||||
.bits_per_word = 8,
|
||||
.len = 2,
|
||||
.cs_change = 1,
|
||||
.delay_usecs = adis->data->read_delay,
|
||||
.delay.value = adis->data->read_delay,
|
||||
.delay.unit = SPI_DELAY_UNIT_USECS,
|
||||
.cs_change_delay.value = adis->data->cs_change_delay,
|
||||
.cs_change_delay.unit = SPI_DELAY_UNIT_USECS,
|
||||
}, {
|
||||
@ -155,18 +164,19 @@ int adis_read_reg(struct adis *adis, unsigned int reg,
|
||||
.bits_per_word = 8,
|
||||
.len = 2,
|
||||
.cs_change = 1,
|
||||
.delay_usecs = adis->data->read_delay,
|
||||
.delay.value = adis->data->read_delay,
|
||||
.delay.unit = SPI_DELAY_UNIT_USECS,
|
||||
.cs_change_delay.value = adis->data->cs_change_delay,
|
||||
.cs_change_delay.unit = SPI_DELAY_UNIT_USECS,
|
||||
}, {
|
||||
.rx_buf = adis->rx + 2,
|
||||
.bits_per_word = 8,
|
||||
.len = 2,
|
||||
.delay_usecs = adis->data->read_delay,
|
||||
.delay.value = adis->data->read_delay,
|
||||
.delay.unit = SPI_DELAY_UNIT_USECS,
|
||||
},
|
||||
};
|
||||
|
||||
mutex_lock(&adis->txrx_lock);
|
||||
spi_message_init(&msg);
|
||||
|
||||
if (adis->current_page != page) {
|
||||
@ -188,15 +198,14 @@ int adis_read_reg(struct adis *adis, unsigned int reg,
|
||||
spi_message_add_tail(&xfers[3], &msg);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
goto out_unlock;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = spi_sync(adis->spi, &msg);
|
||||
if (ret) {
|
||||
dev_err(&adis->spi->dev, "Failed to read register 0x%02X: %d\n",
|
||||
reg, ret);
|
||||
goto out_unlock;
|
||||
return ret;
|
||||
} else {
|
||||
adis->current_page = page;
|
||||
}
|
||||
@ -210,12 +219,9 @@ int adis_read_reg(struct adis *adis, unsigned int reg,
|
||||
break;
|
||||
}
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&adis->txrx_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(adis_read_reg);
|
||||
EXPORT_SYMBOL_GPL(__adis_read_reg);
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
|
||||
@ -253,12 +259,16 @@ int adis_enable_irq(struct adis *adis, bool enable)
|
||||
int ret = 0;
|
||||
uint16_t msc;
|
||||
|
||||
if (adis->data->enable_irq)
|
||||
return adis->data->enable_irq(adis, enable);
|
||||
mutex_lock(&adis->state_lock);
|
||||
|
||||
ret = adis_read_reg_16(adis, adis->data->msc_ctrl_reg, &msc);
|
||||
if (adis->data->enable_irq) {
|
||||
ret = adis->data->enable_irq(adis, enable);
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
ret = __adis_read_reg_16(adis, adis->data->msc_ctrl_reg, &msc);
|
||||
if (ret)
|
||||
goto error_ret;
|
||||
goto out_unlock;
|
||||
|
||||
msc |= ADIS_MSC_CTRL_DATA_RDY_POL_HIGH;
|
||||
msc &= ~ADIS_MSC_CTRL_DATA_RDY_DIO2;
|
||||
@ -267,26 +277,27 @@ int adis_enable_irq(struct adis *adis, bool enable)
|
||||
else
|
||||
msc &= ~ADIS_MSC_CTRL_DATA_RDY_EN;
|
||||
|
||||
ret = adis_write_reg_16(adis, adis->data->msc_ctrl_reg, msc);
|
||||
ret = __adis_write_reg_16(adis, adis->data->msc_ctrl_reg, msc);
|
||||
|
||||
error_ret:
|
||||
out_unlock:
|
||||
mutex_unlock(&adis->state_lock);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(adis_enable_irq);
|
||||
|
||||
/**
|
||||
* adis_check_status() - Check the device for error conditions
|
||||
* __adis_check_status() - Check the device for error conditions (unlocked)
|
||||
* @adis: The adis device
|
||||
*
|
||||
* Returns 0 on success, a negative error code otherwise
|
||||
*/
|
||||
int adis_check_status(struct adis *adis)
|
||||
int __adis_check_status(struct adis *adis)
|
||||
{
|
||||
uint16_t status;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
ret = adis_read_reg_16(adis, adis->data->diag_stat_reg, &status);
|
||||
ret = __adis_read_reg_16(adis, adis->data->diag_stat_reg, &status);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -304,32 +315,38 @@ int adis_check_status(struct adis *adis)
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(adis_check_status);
|
||||
EXPORT_SYMBOL_GPL(__adis_check_status);
|
||||
|
||||
/**
|
||||
* adis_reset() - Reset the device
|
||||
* __adis_reset() - Reset the device (unlocked version)
|
||||
* @adis: The adis device
|
||||
*
|
||||
* Returns 0 on success, a negative error code otherwise
|
||||
*/
|
||||
int adis_reset(struct adis *adis)
|
||||
int __adis_reset(struct adis *adis)
|
||||
{
|
||||
int ret;
|
||||
const struct adis_timeout *timeouts = adis->data->timeouts;
|
||||
|
||||
ret = adis_write_reg_8(adis, adis->data->glob_cmd_reg,
|
||||
ret = __adis_write_reg_8(adis, adis->data->glob_cmd_reg,
|
||||
ADIS_GLOB_CMD_SW_RESET);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
dev_err(&adis->spi->dev, "Failed to reset device: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
msleep(timeouts->sw_reset_ms);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(adis_reset);
|
||||
EXPORT_SYMBOL_GPL(__adis_reset);
|
||||
|
||||
static int adis_self_test(struct adis *adis)
|
||||
{
|
||||
int ret;
|
||||
const struct adis_timeout *timeouts = adis->data->timeouts;
|
||||
|
||||
ret = adis_write_reg_16(adis, adis->data->msc_ctrl_reg,
|
||||
ret = __adis_write_reg_16(adis, adis->data->msc_ctrl_reg,
|
||||
adis->data->self_test_mask);
|
||||
if (ret) {
|
||||
dev_err(&adis->spi->dev, "Failed to initiate self test: %d\n",
|
||||
@ -337,12 +354,12 @@ static int adis_self_test(struct adis *adis)
|
||||
return ret;
|
||||
}
|
||||
|
||||
msleep(adis->data->startup_delay);
|
||||
msleep(timeouts->self_test_ms);
|
||||
|
||||
ret = adis_check_status(adis);
|
||||
ret = __adis_check_status(adis);
|
||||
|
||||
if (adis->data->self_test_no_autoclear)
|
||||
adis_write_reg_16(adis, adis->data->msc_ctrl_reg, 0x00);
|
||||
__adis_write_reg_16(adis, adis->data->msc_ctrl_reg, 0x00);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -360,19 +377,22 @@ int adis_initial_startup(struct adis *adis)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&adis->state_lock);
|
||||
|
||||
ret = adis_self_test(adis);
|
||||
if (ret) {
|
||||
dev_err(&adis->spi->dev, "Self-test failed, trying reset.\n");
|
||||
adis_reset(adis);
|
||||
msleep(adis->data->startup_delay);
|
||||
__adis_reset(adis);
|
||||
ret = adis_self_test(adis);
|
||||
if (ret) {
|
||||
dev_err(&adis->spi->dev, "Second self-test failed, giving up.\n");
|
||||
return ret;
|
||||
goto out_unlock;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
out_unlock:
|
||||
mutex_unlock(&adis->state_lock);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(adis_initial_startup);
|
||||
|
||||
@ -398,15 +418,15 @@ int adis_single_conversion(struct iio_dev *indio_dev,
|
||||
unsigned int uval;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
mutex_lock(&adis->state_lock);
|
||||
|
||||
ret = adis_read_reg(adis, chan->address, &uval,
|
||||
ret = __adis_read_reg(adis, chan->address, &uval,
|
||||
chan->scan_type.storagebits / 8);
|
||||
if (ret)
|
||||
goto err_unlock;
|
||||
|
||||
if (uval & error_mask) {
|
||||
ret = adis_check_status(adis);
|
||||
ret = __adis_check_status(adis);
|
||||
if (ret)
|
||||
goto err_unlock;
|
||||
}
|
||||
@ -418,7 +438,7 @@ int adis_single_conversion(struct iio_dev *indio_dev,
|
||||
|
||||
ret = IIO_VAL_INT;
|
||||
err_unlock:
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
mutex_unlock(&adis->state_lock);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(adis_single_conversion);
|
||||
@ -438,7 +458,12 @@ EXPORT_SYMBOL_GPL(adis_single_conversion);
|
||||
int adis_init(struct adis *adis, struct iio_dev *indio_dev,
|
||||
struct spi_device *spi, const struct adis_data *data)
|
||||
{
|
||||
mutex_init(&adis->txrx_lock);
|
||||
if (!data || !data->timeouts) {
|
||||
dev_err(&spi->dev, "No config data or timeouts not defined!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mutex_init(&adis->state_lock);
|
||||
adis->spi = spi;
|
||||
adis->data = data;
|
||||
iio_device_set_drvdata(indio_dev, adis);
|
||||
|
@ -156,12 +156,14 @@ struct adis16400_state;
|
||||
|
||||
struct adis16400_chip_info {
|
||||
const struct iio_chan_spec *channels;
|
||||
const struct adis_timeout *timeouts;
|
||||
const int num_channels;
|
||||
const long flags;
|
||||
unsigned int gyro_scale_micro;
|
||||
unsigned int accel_scale_micro;
|
||||
int temp_scale_nano;
|
||||
int temp_offset;
|
||||
/* set_freq() & get_freq() need to avoid using ADIS lib's state lock */
|
||||
int (*set_freq)(struct adis16400_state *st, unsigned int freq);
|
||||
int (*get_freq)(struct adis16400_state *st);
|
||||
};
|
||||
@ -326,7 +328,7 @@ static int adis16334_get_freq(struct adis16400_state *st)
|
||||
int ret;
|
||||
uint16_t t;
|
||||
|
||||
ret = adis_read_reg_16(&st->adis, ADIS16400_SMPL_PRD, &t);
|
||||
ret = __adis_read_reg_16(&st->adis, ADIS16400_SMPL_PRD, &t);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -350,7 +352,7 @@ static int adis16334_set_freq(struct adis16400_state *st, unsigned int freq)
|
||||
t <<= ADIS16334_RATE_DIV_SHIFT;
|
||||
t |= ADIS16334_RATE_INT_CLK;
|
||||
|
||||
return adis_write_reg_16(&st->adis, ADIS16400_SMPL_PRD, t);
|
||||
return __adis_write_reg_16(&st->adis, ADIS16400_SMPL_PRD, t);
|
||||
}
|
||||
|
||||
static int adis16400_get_freq(struct adis16400_state *st)
|
||||
@ -358,7 +360,7 @@ static int adis16400_get_freq(struct adis16400_state *st)
|
||||
int sps, ret;
|
||||
uint16_t t;
|
||||
|
||||
ret = adis_read_reg_16(&st->adis, ADIS16400_SMPL_PRD, &t);
|
||||
ret = __adis_read_reg_16(&st->adis, ADIS16400_SMPL_PRD, &t);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -390,7 +392,7 @@ static int adis16400_set_freq(struct adis16400_state *st, unsigned int freq)
|
||||
else
|
||||
st->adis.spi->max_speed_hz = ADIS16400_SPI_FAST;
|
||||
|
||||
return adis_write_reg_8(&st->adis, ADIS16400_SMPL_PRD, val);
|
||||
return __adis_write_reg_8(&st->adis, ADIS16400_SMPL_PRD, val);
|
||||
}
|
||||
|
||||
static const unsigned int adis16400_3db_divisors[] = {
|
||||
@ -404,7 +406,7 @@ static const unsigned int adis16400_3db_divisors[] = {
|
||||
[7] = 200, /* Not a valid setting */
|
||||
};
|
||||
|
||||
static int adis16400_set_filter(struct iio_dev *indio_dev, int sps, int val)
|
||||
static int __adis16400_set_filter(struct iio_dev *indio_dev, int sps, int val)
|
||||
{
|
||||
struct adis16400_state *st = iio_priv(indio_dev);
|
||||
uint16_t val16;
|
||||
@ -415,11 +417,11 @@ static int adis16400_set_filter(struct iio_dev *indio_dev, int sps, int val)
|
||||
break;
|
||||
}
|
||||
|
||||
ret = adis_read_reg_16(&st->adis, ADIS16400_SENS_AVG, &val16);
|
||||
ret = __adis_read_reg_16(&st->adis, ADIS16400_SENS_AVG, &val16);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = adis_write_reg_16(&st->adis, ADIS16400_SENS_AVG,
|
||||
ret = __adis_write_reg_16(&st->adis, ADIS16400_SENS_AVG,
|
||||
(val16 & ~0x07) | i);
|
||||
return ret;
|
||||
}
|
||||
@ -507,32 +509,31 @@ static int adis16400_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int val, int val2, long info)
|
||||
{
|
||||
struct adis16400_state *st = iio_priv(indio_dev);
|
||||
struct mutex *slock = &st->adis.state_lock;
|
||||
int ret, sps;
|
||||
|
||||
switch (info) {
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
ret = adis_write_reg_16(&st->adis,
|
||||
adis16400_addresses[chan->scan_index], val);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return ret;
|
||||
case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
|
||||
/*
|
||||
* Need to cache values so we can update if the frequency
|
||||
* changes.
|
||||
*/
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
mutex_lock(slock);
|
||||
st->filt_int = val;
|
||||
/* Work out update to current value */
|
||||
sps = st->variant->get_freq(st);
|
||||
if (sps < 0) {
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
mutex_unlock(slock);
|
||||
return sps;
|
||||
}
|
||||
|
||||
ret = adis16400_set_filter(indio_dev, sps,
|
||||
ret = __adis16400_set_filter(indio_dev, sps,
|
||||
val * 1000 + val2 / 1000);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
mutex_unlock(slock);
|
||||
return ret;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
sps = val * 1000 + val2 / 1000;
|
||||
@ -540,9 +541,9 @@ static int adis16400_write_raw(struct iio_dev *indio_dev,
|
||||
if (sps <= 0)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
mutex_lock(slock);
|
||||
ret = st->variant->set_freq(st, sps);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
mutex_unlock(slock);
|
||||
return ret;
|
||||
default:
|
||||
return -EINVAL;
|
||||
@ -553,6 +554,7 @@ static int adis16400_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val, int *val2, long info)
|
||||
{
|
||||
struct adis16400_state *st = iio_priv(indio_dev);
|
||||
struct mutex *slock = &st->adis.state_lock;
|
||||
int16_t val16;
|
||||
int ret;
|
||||
|
||||
@ -596,10 +598,8 @@ static int adis16400_read_raw(struct iio_dev *indio_dev,
|
||||
return -EINVAL;
|
||||
}
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
ret = adis_read_reg_16(&st->adis,
|
||||
adis16400_addresses[chan->scan_index], &val16);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
if (ret)
|
||||
return ret;
|
||||
val16 = sign_extend32(val16, 11);
|
||||
@ -610,27 +610,27 @@ static int adis16400_read_raw(struct iio_dev *indio_dev,
|
||||
*val = st->variant->temp_offset;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
mutex_lock(slock);
|
||||
/* Need both the number of taps and the sampling frequency */
|
||||
ret = adis_read_reg_16(&st->adis,
|
||||
ret = __adis_read_reg_16(&st->adis,
|
||||
ADIS16400_SENS_AVG,
|
||||
&val16);
|
||||
if (ret) {
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
mutex_unlock(slock);
|
||||
return ret;
|
||||
}
|
||||
ret = st->variant->get_freq(st);
|
||||
if (ret >= 0) {
|
||||
ret /= adis16400_3db_divisors[val16 & 0x07];
|
||||
*val = ret / 1000;
|
||||
*val2 = (ret % 1000) * 1000;
|
||||
}
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
mutex_unlock(slock);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret /= adis16400_3db_divisors[val16 & 0x07];
|
||||
*val = ret / 1000;
|
||||
*val2 = (ret % 1000) * 1000;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
mutex_lock(slock);
|
||||
ret = st->variant->get_freq(st);
|
||||
mutex_unlock(slock);
|
||||
if (ret)
|
||||
return ret;
|
||||
*val = ret / 1000;
|
||||
@ -930,6 +930,36 @@ static const struct iio_chan_spec adis16334_channels[] = {
|
||||
IIO_CHAN_SOFT_TIMESTAMP(ADIS16400_SCAN_TIMESTAMP),
|
||||
};
|
||||
|
||||
static const struct adis_timeout adis16300_timeouts = {
|
||||
.reset_ms = ADIS16400_STARTUP_DELAY,
|
||||
.sw_reset_ms = ADIS16400_STARTUP_DELAY,
|
||||
.self_test_ms = ADIS16400_STARTUP_DELAY,
|
||||
};
|
||||
|
||||
static const struct adis_timeout adis16362_timeouts = {
|
||||
.reset_ms = 130,
|
||||
.sw_reset_ms = 130,
|
||||
.self_test_ms = 12,
|
||||
};
|
||||
|
||||
static const struct adis_timeout adis16400_timeouts = {
|
||||
.reset_ms = 170,
|
||||
.sw_reset_ms = 170,
|
||||
.self_test_ms = 12,
|
||||
};
|
||||
|
||||
static const struct adis_timeout adis16445_timeouts = {
|
||||
.reset_ms = 55,
|
||||
.sw_reset_ms = 55,
|
||||
.self_test_ms = 16,
|
||||
};
|
||||
|
||||
static const struct adis_timeout adis16448_timeouts = {
|
||||
.reset_ms = 90,
|
||||
.sw_reset_ms = 90,
|
||||
.self_test_ms = 45,
|
||||
};
|
||||
|
||||
static struct adis16400_chip_info adis16400_chips[] = {
|
||||
[ADIS16300] = {
|
||||
.channels = adis16300_channels,
|
||||
@ -942,6 +972,7 @@ static struct adis16400_chip_info adis16400_chips[] = {
|
||||
.temp_offset = 25000000 / 140000, /* 25 C = 0x00 */
|
||||
.set_freq = adis16400_set_freq,
|
||||
.get_freq = adis16400_get_freq,
|
||||
.timeouts = &adis16300_timeouts,
|
||||
},
|
||||
[ADIS16334] = {
|
||||
.channels = adis16334_channels,
|
||||
@ -965,6 +996,7 @@ static struct adis16400_chip_info adis16400_chips[] = {
|
||||
.flags = ADIS16400_NO_BURST | ADIS16400_HAS_SLOW_MODE,
|
||||
.set_freq = adis16400_set_freq,
|
||||
.get_freq = adis16400_get_freq,
|
||||
.timeouts = &adis16300_timeouts,
|
||||
},
|
||||
[ADIS16360] = {
|
||||
.channels = adis16350_channels,
|
||||
@ -977,6 +1009,7 @@ static struct adis16400_chip_info adis16400_chips[] = {
|
||||
.temp_offset = 25000000 / 136000, /* 25 C = 0x00 */
|
||||
.set_freq = adis16400_set_freq,
|
||||
.get_freq = adis16400_get_freq,
|
||||
.timeouts = &adis16300_timeouts,
|
||||
},
|
||||
[ADIS16362] = {
|
||||
.channels = adis16350_channels,
|
||||
@ -989,6 +1022,7 @@ static struct adis16400_chip_info adis16400_chips[] = {
|
||||
.temp_offset = 25000000 / 136000, /* 25 C = 0x00 */
|
||||
.set_freq = adis16400_set_freq,
|
||||
.get_freq = adis16400_get_freq,
|
||||
.timeouts = &adis16362_timeouts,
|
||||
},
|
||||
[ADIS16364] = {
|
||||
.channels = adis16350_channels,
|
||||
@ -1001,6 +1035,7 @@ static struct adis16400_chip_info adis16400_chips[] = {
|
||||
.temp_offset = 25000000 / 136000, /* 25 C = 0x00 */
|
||||
.set_freq = adis16400_set_freq,
|
||||
.get_freq = adis16400_get_freq,
|
||||
.timeouts = &adis16362_timeouts,
|
||||
},
|
||||
[ADIS16367] = {
|
||||
.channels = adis16350_channels,
|
||||
@ -1013,6 +1048,7 @@ static struct adis16400_chip_info adis16400_chips[] = {
|
||||
.temp_offset = 25000000 / 136000, /* 25 C = 0x00 */
|
||||
.set_freq = adis16400_set_freq,
|
||||
.get_freq = adis16400_get_freq,
|
||||
.timeouts = &adis16300_timeouts,
|
||||
},
|
||||
[ADIS16400] = {
|
||||
.channels = adis16400_channels,
|
||||
@ -1024,6 +1060,7 @@ static struct adis16400_chip_info adis16400_chips[] = {
|
||||
.temp_offset = 25000000 / 140000, /* 25 C = 0x00 */
|
||||
.set_freq = adis16400_set_freq,
|
||||
.get_freq = adis16400_get_freq,
|
||||
.timeouts = &adis16400_timeouts,
|
||||
},
|
||||
[ADIS16445] = {
|
||||
.channels = adis16445_channels,
|
||||
@ -1037,6 +1074,7 @@ static struct adis16400_chip_info adis16400_chips[] = {
|
||||
.temp_offset = 31000000 / 73860, /* 31 C = 0x00 */
|
||||
.set_freq = adis16334_set_freq,
|
||||
.get_freq = adis16334_get_freq,
|
||||
.timeouts = &adis16445_timeouts,
|
||||
},
|
||||
[ADIS16448] = {
|
||||
.channels = adis16448_channels,
|
||||
@ -1050,6 +1088,7 @@ static struct adis16400_chip_info adis16400_chips[] = {
|
||||
.temp_offset = 31000000 / 73860, /* 31 C = 0x00 */
|
||||
.set_freq = adis16334_set_freq,
|
||||
.get_freq = adis16334_get_freq,
|
||||
.timeouts = &adis16448_timeouts,
|
||||
}
|
||||
};
|
||||
|
||||
@ -1087,7 +1126,6 @@ static const struct adis_data adis16400_data = {
|
||||
.write_delay = 50,
|
||||
|
||||
.self_test_mask = ADIS16400_MSC_CTRL_MEM_TEST,
|
||||
.startup_delay = ADIS16400_STARTUP_DELAY,
|
||||
|
||||
.status_error_msgs = adis16400_status_error_msgs,
|
||||
.status_error_mask = BIT(ADIS16400_DIAG_STAT_ZACCL_FAIL) |
|
||||
@ -1121,11 +1159,28 @@ static void adis16400_setup_chan_mask(struct adis16400_state *st)
|
||||
}
|
||||
}
|
||||
|
||||
static struct adis_data *adis16400_adis_data_alloc(struct adis16400_state *st,
|
||||
struct device *dev)
|
||||
{
|
||||
struct adis_data *data;
|
||||
|
||||
data = devm_kmalloc(dev, sizeof(struct adis_data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
memcpy(data, &adis16400_data, sizeof(*data));
|
||||
|
||||
data->timeouts = st->variant->timeouts;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static int adis16400_probe(struct spi_device *spi)
|
||||
{
|
||||
struct adis16400_state *st;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret;
|
||||
const struct adis_data *adis16400_data;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
||||
if (indio_dev == NULL)
|
||||
@ -1152,7 +1207,11 @@ static int adis16400_probe(struct spi_device *spi)
|
||||
st->adis.burst->extra_len = sizeof(u16);
|
||||
}
|
||||
|
||||
ret = adis_init(&st->adis, indio_dev, spi, &adis16400_data);
|
||||
adis16400_data = adis16400_adis_data_alloc(st, &spi->dev);
|
||||
if (IS_ERR(adis16400_data))
|
||||
return PTR_ERR(adis16400_data);
|
||||
|
||||
ret = adis_init(&st->adis, indio_dev, spi, adis16400_data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -383,6 +383,12 @@ static const char * const adis16460_status_error_msgs[] = {
|
||||
[ADIS16460_DIAG_STAT_FLASH_UPT] = "Flash update failure",
|
||||
};
|
||||
|
||||
static const struct adis_timeout adis16460_timeouts = {
|
||||
.reset_ms = 225,
|
||||
.sw_reset_ms = 225,
|
||||
.self_test_ms = 10,
|
||||
};
|
||||
|
||||
static const struct adis_data adis16460_data = {
|
||||
.diag_stat_reg = ADIS16460_REG_DIAG_STAT,
|
||||
.glob_cmd_reg = ADIS16460_REG_GLOB_CMD,
|
||||
@ -398,6 +404,7 @@ static const struct adis_data adis16460_data = {
|
||||
BIT(ADIS16460_DIAG_STAT_SPI_COMM) |
|
||||
BIT(ADIS16460_DIAG_STAT_FLASH_UPT),
|
||||
.enable_irq = adis16460_enable_irq,
|
||||
.timeouts = &adis16460_timeouts,
|
||||
};
|
||||
|
||||
static int adis16460_probe(struct spi_device *spi)
|
||||
|
@ -138,6 +138,7 @@ struct adis16480_chip_info {
|
||||
unsigned int max_dec_rate;
|
||||
const unsigned int *filter_freqs;
|
||||
bool has_pps_clk_mode;
|
||||
const struct adis_timeout *timeouts;
|
||||
};
|
||||
|
||||
enum adis16480_int_pin {
|
||||
@ -555,6 +556,7 @@ static int adis16480_set_filter_freq(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, unsigned int freq)
|
||||
{
|
||||
struct adis16480 *st = iio_priv(indio_dev);
|
||||
struct mutex *slock = &st->adis.state_lock;
|
||||
unsigned int enable_mask, offset, reg;
|
||||
unsigned int diff, best_diff;
|
||||
unsigned int i, best_freq;
|
||||
@ -565,9 +567,11 @@ static int adis16480_set_filter_freq(struct iio_dev *indio_dev,
|
||||
offset = ad16480_filter_data[chan->scan_index][1];
|
||||
enable_mask = BIT(offset + 2);
|
||||
|
||||
ret = adis_read_reg_16(&st->adis, reg, &val);
|
||||
mutex_lock(slock);
|
||||
|
||||
ret = __adis_read_reg_16(&st->adis, reg, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto out_unlock;
|
||||
|
||||
if (freq == 0) {
|
||||
val &= ~enable_mask;
|
||||
@ -589,7 +593,11 @@ static int adis16480_set_filter_freq(struct iio_dev *indio_dev,
|
||||
val |= enable_mask;
|
||||
}
|
||||
|
||||
return adis_write_reg_16(&st->adis, reg, val);
|
||||
ret = __adis_write_reg_16(&st->adis, reg, val);
|
||||
out_unlock:
|
||||
mutex_unlock(slock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adis16480_read_raw(struct iio_dev *indio_dev,
|
||||
@ -779,6 +787,7 @@ enum adis16480_variant {
|
||||
ADIS16480,
|
||||
ADIS16485,
|
||||
ADIS16488,
|
||||
ADIS16490,
|
||||
ADIS16495_1,
|
||||
ADIS16495_2,
|
||||
ADIS16495_3,
|
||||
@ -787,6 +796,30 @@ enum adis16480_variant {
|
||||
ADIS16497_3,
|
||||
};
|
||||
|
||||
static const struct adis_timeout adis16485_timeouts = {
|
||||
.reset_ms = 560,
|
||||
.sw_reset_ms = 120,
|
||||
.self_test_ms = 12,
|
||||
};
|
||||
|
||||
static const struct adis_timeout adis16480_timeouts = {
|
||||
.reset_ms = 560,
|
||||
.sw_reset_ms = 560,
|
||||
.self_test_ms = 12,
|
||||
};
|
||||
|
||||
static const struct adis_timeout adis16495_timeouts = {
|
||||
.reset_ms = 170,
|
||||
.sw_reset_ms = 130,
|
||||
.self_test_ms = 40,
|
||||
};
|
||||
|
||||
static const struct adis_timeout adis16495_1_timeouts = {
|
||||
.reset_ms = 250,
|
||||
.sw_reset_ms = 210,
|
||||
.self_test_ms = 20,
|
||||
};
|
||||
|
||||
static const struct adis16480_chip_info adis16480_chip_info[] = {
|
||||
[ADIS16375] = {
|
||||
.channels = adis16485_channels,
|
||||
@ -805,6 +838,7 @@ static const struct adis16480_chip_info adis16480_chip_info[] = {
|
||||
.int_clk = 2460000,
|
||||
.max_dec_rate = 2048,
|
||||
.filter_freqs = adis16480_def_filter_freqs,
|
||||
.timeouts = &adis16485_timeouts,
|
||||
},
|
||||
[ADIS16480] = {
|
||||
.channels = adis16480_channels,
|
||||
@ -817,6 +851,7 @@ static const struct adis16480_chip_info adis16480_chip_info[] = {
|
||||
.int_clk = 2460000,
|
||||
.max_dec_rate = 2048,
|
||||
.filter_freqs = adis16480_def_filter_freqs,
|
||||
.timeouts = &adis16480_timeouts,
|
||||
},
|
||||
[ADIS16485] = {
|
||||
.channels = adis16485_channels,
|
||||
@ -829,6 +864,7 @@ static const struct adis16480_chip_info adis16480_chip_info[] = {
|
||||
.int_clk = 2460000,
|
||||
.max_dec_rate = 2048,
|
||||
.filter_freqs = adis16480_def_filter_freqs,
|
||||
.timeouts = &adis16485_timeouts,
|
||||
},
|
||||
[ADIS16488] = {
|
||||
.channels = adis16480_channels,
|
||||
@ -841,6 +877,21 @@ static const struct adis16480_chip_info adis16480_chip_info[] = {
|
||||
.int_clk = 2460000,
|
||||
.max_dec_rate = 2048,
|
||||
.filter_freqs = adis16480_def_filter_freqs,
|
||||
.timeouts = &adis16485_timeouts,
|
||||
},
|
||||
[ADIS16490] = {
|
||||
.channels = adis16485_channels,
|
||||
.num_channels = ARRAY_SIZE(adis16485_channels),
|
||||
.gyro_max_val = 20000 << 16,
|
||||
.gyro_max_scale = IIO_DEGREE_TO_RAD(100),
|
||||
.accel_max_val = IIO_M_S_2_TO_G(16000 << 16),
|
||||
.accel_max_scale = 8,
|
||||
.temp_scale = 14285, /* 14.285 milli degree Celsius */
|
||||
.int_clk = 4250000,
|
||||
.max_dec_rate = 4250,
|
||||
.filter_freqs = adis16495_def_filter_freqs,
|
||||
.has_pps_clk_mode = true,
|
||||
.timeouts = &adis16495_timeouts,
|
||||
},
|
||||
[ADIS16495_1] = {
|
||||
.channels = adis16485_channels,
|
||||
@ -854,6 +905,7 @@ static const struct adis16480_chip_info adis16480_chip_info[] = {
|
||||
.max_dec_rate = 4250,
|
||||
.filter_freqs = adis16495_def_filter_freqs,
|
||||
.has_pps_clk_mode = true,
|
||||
.timeouts = &adis16495_1_timeouts,
|
||||
},
|
||||
[ADIS16495_2] = {
|
||||
.channels = adis16485_channels,
|
||||
@ -867,6 +919,7 @@ static const struct adis16480_chip_info adis16480_chip_info[] = {
|
||||
.max_dec_rate = 4250,
|
||||
.filter_freqs = adis16495_def_filter_freqs,
|
||||
.has_pps_clk_mode = true,
|
||||
.timeouts = &adis16495_1_timeouts,
|
||||
},
|
||||
[ADIS16495_3] = {
|
||||
.channels = adis16485_channels,
|
||||
@ -880,6 +933,7 @@ static const struct adis16480_chip_info adis16480_chip_info[] = {
|
||||
.max_dec_rate = 4250,
|
||||
.filter_freqs = adis16495_def_filter_freqs,
|
||||
.has_pps_clk_mode = true,
|
||||
.timeouts = &adis16495_1_timeouts,
|
||||
},
|
||||
[ADIS16497_1] = {
|
||||
.channels = adis16485_channels,
|
||||
@ -893,6 +947,7 @@ static const struct adis16480_chip_info adis16480_chip_info[] = {
|
||||
.max_dec_rate = 4250,
|
||||
.filter_freqs = adis16495_def_filter_freqs,
|
||||
.has_pps_clk_mode = true,
|
||||
.timeouts = &adis16495_1_timeouts,
|
||||
},
|
||||
[ADIS16497_2] = {
|
||||
.channels = adis16485_channels,
|
||||
@ -906,6 +961,7 @@ static const struct adis16480_chip_info adis16480_chip_info[] = {
|
||||
.max_dec_rate = 4250,
|
||||
.filter_freqs = adis16495_def_filter_freqs,
|
||||
.has_pps_clk_mode = true,
|
||||
.timeouts = &adis16495_1_timeouts,
|
||||
},
|
||||
[ADIS16497_3] = {
|
||||
.channels = adis16485_channels,
|
||||
@ -919,6 +975,7 @@ static const struct adis16480_chip_info adis16480_chip_info[] = {
|
||||
.max_dec_rate = 4250,
|
||||
.filter_freqs = adis16495_def_filter_freqs,
|
||||
.has_pps_clk_mode = true,
|
||||
.timeouts = &adis16495_1_timeouts,
|
||||
},
|
||||
};
|
||||
|
||||
@ -947,14 +1004,14 @@ static int adis16480_enable_irq(struct adis *adis, bool enable)
|
||||
uint16_t val;
|
||||
int ret;
|
||||
|
||||
ret = adis_read_reg_16(adis, ADIS16480_REG_FNCTIO_CTRL, &val);
|
||||
ret = __adis_read_reg_16(adis, ADIS16480_REG_FNCTIO_CTRL, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
val &= ~ADIS16480_DRDY_EN_MSK;
|
||||
val |= ADIS16480_DRDY_EN(enable);
|
||||
|
||||
return adis_write_reg_16(adis, ADIS16480_REG_FNCTIO_CTRL, val);
|
||||
return __adis_write_reg_16(adis, ADIS16480_REG_FNCTIO_CTRL, val);
|
||||
}
|
||||
|
||||
static int adis16480_initial_setup(struct iio_dev *indio_dev)
|
||||
@ -1188,9 +1245,26 @@ static int adis16480_get_ext_clocks(struct adis16480 *st)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct adis_data *adis16480_adis_data_alloc(struct adis16480 *st,
|
||||
struct device *dev)
|
||||
{
|
||||
struct adis_data *data;
|
||||
|
||||
data = devm_kmalloc(dev, sizeof(struct adis_data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
memcpy(data, &adis16480_data, sizeof(*data));
|
||||
|
||||
data->timeouts = st->chip_info->timeouts;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static int adis16480_probe(struct spi_device *spi)
|
||||
{
|
||||
const struct spi_device_id *id = spi_get_device_id(spi);
|
||||
const struct adis_data *adis16480_data;
|
||||
struct iio_dev *indio_dev;
|
||||
struct adis16480 *st;
|
||||
int ret;
|
||||
@ -1211,7 +1285,11 @@ static int adis16480_probe(struct spi_device *spi)
|
||||
indio_dev->info = &adis16480_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
ret = adis_init(&st->adis, indio_dev, spi, &adis16480_data);
|
||||
adis16480_data = adis16480_adis_data_alloc(st, &spi->dev);
|
||||
if (IS_ERR(adis16480_data))
|
||||
return PTR_ERR(adis16480_data);
|
||||
|
||||
ret = adis_init(&st->adis, indio_dev, spi, adis16480_data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -1278,6 +1356,7 @@ static const struct spi_device_id adis16480_ids[] = {
|
||||
{ "adis16480", ADIS16480 },
|
||||
{ "adis16485", ADIS16485 },
|
||||
{ "adis16488", ADIS16488 },
|
||||
{ "adis16490", ADIS16490 },
|
||||
{ "adis16495-1", ADIS16495_1 },
|
||||
{ "adis16495-2", ADIS16495_2 },
|
||||
{ "adis16495-3", ADIS16495_3 },
|
||||
@ -1293,6 +1372,7 @@ static const struct of_device_id adis16480_of_match[] = {
|
||||
{ .compatible = "adi,adis16480" },
|
||||
{ .compatible = "adi,adis16485" },
|
||||
{ .compatible = "adi,adis16488" },
|
||||
{ .compatible = "adi,adis16490" },
|
||||
{ .compatible = "adi,adis16495-1" },
|
||||
{ .compatible = "adi,adis16495-2" },
|
||||
{ .compatible = "adi,adis16495-3" },
|
||||
|
@ -129,7 +129,7 @@ static irqreturn_t adis_trigger_handler(int irq, void *p)
|
||||
return -ENOMEM;
|
||||
|
||||
if (adis->data->has_paging) {
|
||||
mutex_lock(&adis->txrx_lock);
|
||||
mutex_lock(&adis->state_lock);
|
||||
if (adis->current_page != 0) {
|
||||
adis->tx[0] = ADIS_WRITE_REG(ADIS_REG_PAGE_ID);
|
||||
adis->tx[1] = 0;
|
||||
@ -144,7 +144,7 @@ static irqreturn_t adis_trigger_handler(int irq, void *p)
|
||||
|
||||
if (adis->data->has_paging) {
|
||||
adis->current_page = 0;
|
||||
mutex_unlock(&adis->txrx_lock);
|
||||
mutex_unlock(&adis->state_lock);
|
||||
}
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, adis->buffer,
|
||||
|
@ -10,11 +10,12 @@ config INV_MPU6050_IIO
|
||||
|
||||
config INV_MPU6050_I2C
|
||||
tristate "Invensense MPU6050 devices (I2C)"
|
||||
depends on I2C_MUX
|
||||
depends on I2C
|
||||
select I2C_MUX
|
||||
select INV_MPU6050_IIO
|
||||
select REGMAP_I2C
|
||||
help
|
||||
This driver supports the Invensense MPU6000/6050/6500/6515,
|
||||
This driver supports the Invensense MPU6050/6500/6515,
|
||||
MPU9150/9250/9255 and ICM20608/20602 motion tracking devices
|
||||
over I2C.
|
||||
This driver can be built as a module. The module will be called
|
||||
@ -26,8 +27,8 @@ config INV_MPU6050_SPI
|
||||
select INV_MPU6050_IIO
|
||||
select REGMAP_SPI
|
||||
help
|
||||
This driver supports the Invensense MPU6000/6050/6500/6515,
|
||||
MPU9150/9250/9255 and ICM20608/20602 motion tracking devices
|
||||
This driver supports the Invensense MPU6000/6500/6515,
|
||||
MPU9250/9255 and ICM20608/20602 motion tracking devices
|
||||
over SPI.
|
||||
This driver can be built as a module. The module will be called
|
||||
inv-mpu6050-spi.
|
||||
|
@ -104,6 +104,7 @@ static const struct inv_mpu6050_chip_config chip_config_6050 = {
|
||||
.divider = INV_MPU6050_FIFO_RATE_TO_DIVIDER(INV_MPU6050_INIT_FIFO_RATE),
|
||||
.gyro_fifo_enable = false,
|
||||
.accl_fifo_enable = false,
|
||||
.temp_fifo_enable = false,
|
||||
.magn_fifo_enable = false,
|
||||
.accl_fs = INV_MPU6050_FS_02G,
|
||||
.user_ctrl = 0,
|
||||
@ -856,19 +857,27 @@ static const struct iio_chan_spec_ext_info inv_ext_info[] = {
|
||||
.ext_info = inv_ext_info, \
|
||||
}
|
||||
|
||||
#define INV_MPU6050_TEMP_CHAN(_index) \
|
||||
{ \
|
||||
.type = IIO_TEMP, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) \
|
||||
| BIT(IIO_CHAN_INFO_OFFSET) \
|
||||
| BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.scan_index = _index, \
|
||||
.scan_type = { \
|
||||
.sign = 's', \
|
||||
.realbits = 16, \
|
||||
.storagebits = 16, \
|
||||
.shift = 0, \
|
||||
.endianness = IIO_BE, \
|
||||
}, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec inv_mpu_channels[] = {
|
||||
IIO_CHAN_SOFT_TIMESTAMP(INV_MPU6050_SCAN_TIMESTAMP),
|
||||
/*
|
||||
* Note that temperature should only be via polled reading only,
|
||||
* not the final scan elements output.
|
||||
*/
|
||||
{
|
||||
.type = IIO_TEMP,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW)
|
||||
| BIT(IIO_CHAN_INFO_OFFSET)
|
||||
| BIT(IIO_CHAN_INFO_SCALE),
|
||||
.scan_index = -1,
|
||||
},
|
||||
|
||||
INV_MPU6050_TEMP_CHAN(INV_MPU6050_SCAN_TEMP),
|
||||
|
||||
INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_X, INV_MPU6050_SCAN_GYRO_X),
|
||||
INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_Y, INV_MPU6050_SCAN_GYRO_Y),
|
||||
INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_Z, INV_MPU6050_SCAN_GYRO_Z),
|
||||
@ -878,22 +887,29 @@ static const struct iio_chan_spec inv_mpu_channels[] = {
|
||||
INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_Z, INV_MPU6050_SCAN_ACCL_Z),
|
||||
};
|
||||
|
||||
#define INV_MPU6050_SCAN_MASK_3AXIS_ACCEL \
|
||||
(BIT(INV_MPU6050_SCAN_ACCL_X) \
|
||||
| BIT(INV_MPU6050_SCAN_ACCL_Y) \
|
||||
| BIT(INV_MPU6050_SCAN_ACCL_Z))
|
||||
|
||||
#define INV_MPU6050_SCAN_MASK_3AXIS_GYRO \
|
||||
(BIT(INV_MPU6050_SCAN_GYRO_X) \
|
||||
| BIT(INV_MPU6050_SCAN_GYRO_Y) \
|
||||
| BIT(INV_MPU6050_SCAN_GYRO_Z))
|
||||
|
||||
#define INV_MPU6050_SCAN_MASK_TEMP (BIT(INV_MPU6050_SCAN_TEMP))
|
||||
|
||||
static const unsigned long inv_mpu_scan_masks[] = {
|
||||
/* 3-axis accel */
|
||||
BIT(INV_MPU6050_SCAN_ACCL_X)
|
||||
| BIT(INV_MPU6050_SCAN_ACCL_Y)
|
||||
| BIT(INV_MPU6050_SCAN_ACCL_Z),
|
||||
INV_MPU6050_SCAN_MASK_3AXIS_ACCEL,
|
||||
INV_MPU6050_SCAN_MASK_3AXIS_ACCEL | INV_MPU6050_SCAN_MASK_TEMP,
|
||||
/* 3-axis gyro */
|
||||
BIT(INV_MPU6050_SCAN_GYRO_X)
|
||||
| BIT(INV_MPU6050_SCAN_GYRO_Y)
|
||||
| BIT(INV_MPU6050_SCAN_GYRO_Z),
|
||||
INV_MPU6050_SCAN_MASK_3AXIS_GYRO,
|
||||
INV_MPU6050_SCAN_MASK_3AXIS_GYRO | INV_MPU6050_SCAN_MASK_TEMP,
|
||||
/* 6-axis accel + gyro */
|
||||
BIT(INV_MPU6050_SCAN_ACCL_X)
|
||||
| BIT(INV_MPU6050_SCAN_ACCL_Y)
|
||||
| BIT(INV_MPU6050_SCAN_ACCL_Z)
|
||||
| BIT(INV_MPU6050_SCAN_GYRO_X)
|
||||
| BIT(INV_MPU6050_SCAN_GYRO_Y)
|
||||
| BIT(INV_MPU6050_SCAN_GYRO_Z),
|
||||
INV_MPU6050_SCAN_MASK_3AXIS_ACCEL | INV_MPU6050_SCAN_MASK_3AXIS_GYRO,
|
||||
INV_MPU6050_SCAN_MASK_3AXIS_ACCEL | INV_MPU6050_SCAN_MASK_3AXIS_GYRO
|
||||
| INV_MPU6050_SCAN_MASK_TEMP,
|
||||
0,
|
||||
};
|
||||
|
||||
@ -915,19 +931,30 @@ static const unsigned long inv_mpu_scan_masks[] = {
|
||||
.ext_info = inv_ext_info, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec inv_mpu9150_channels[] = {
|
||||
IIO_CHAN_SOFT_TIMESTAMP(INV_MPU9X50_SCAN_TIMESTAMP),
|
||||
|
||||
INV_MPU6050_TEMP_CHAN(INV_MPU6050_SCAN_TEMP),
|
||||
|
||||
INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_X, INV_MPU6050_SCAN_GYRO_X),
|
||||
INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_Y, INV_MPU6050_SCAN_GYRO_Y),
|
||||
INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_Z, INV_MPU6050_SCAN_GYRO_Z),
|
||||
|
||||
INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_X, INV_MPU6050_SCAN_ACCL_X),
|
||||
INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_Y, INV_MPU6050_SCAN_ACCL_Y),
|
||||
INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_Z, INV_MPU6050_SCAN_ACCL_Z),
|
||||
|
||||
/* Magnetometer resolution is 13 bits */
|
||||
INV_MPU9X50_MAGN_CHAN(IIO_MOD_X, 13, INV_MPU9X50_SCAN_MAGN_X),
|
||||
INV_MPU9X50_MAGN_CHAN(IIO_MOD_Y, 13, INV_MPU9X50_SCAN_MAGN_Y),
|
||||
INV_MPU9X50_MAGN_CHAN(IIO_MOD_Z, 13, INV_MPU9X50_SCAN_MAGN_Z),
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec inv_mpu9250_channels[] = {
|
||||
IIO_CHAN_SOFT_TIMESTAMP(INV_MPU9X50_SCAN_TIMESTAMP),
|
||||
/*
|
||||
* Note that temperature should only be via polled reading only,
|
||||
* not the final scan elements output.
|
||||
*/
|
||||
{
|
||||
.type = IIO_TEMP,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW)
|
||||
| BIT(IIO_CHAN_INFO_OFFSET)
|
||||
| BIT(IIO_CHAN_INFO_SCALE),
|
||||
.scan_index = -1,
|
||||
},
|
||||
|
||||
INV_MPU6050_TEMP_CHAN(INV_MPU6050_SCAN_TEMP),
|
||||
|
||||
INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_X, INV_MPU6050_SCAN_GYRO_X),
|
||||
INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_Y, INV_MPU6050_SCAN_GYRO_Y),
|
||||
INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_Z, INV_MPU6050_SCAN_GYRO_Z),
|
||||
@ -942,98 +969,50 @@ static const struct iio_chan_spec inv_mpu9250_channels[] = {
|
||||
INV_MPU9X50_MAGN_CHAN(IIO_MOD_Z, 16, INV_MPU9X50_SCAN_MAGN_Z),
|
||||
};
|
||||
|
||||
#define INV_MPU9X50_SCAN_MASK_3AXIS_MAGN \
|
||||
(BIT(INV_MPU9X50_SCAN_MAGN_X) \
|
||||
| BIT(INV_MPU9X50_SCAN_MAGN_Y) \
|
||||
| BIT(INV_MPU9X50_SCAN_MAGN_Z))
|
||||
|
||||
static const unsigned long inv_mpu9x50_scan_masks[] = {
|
||||
/* 3-axis accel */
|
||||
BIT(INV_MPU6050_SCAN_ACCL_X)
|
||||
| BIT(INV_MPU6050_SCAN_ACCL_Y)
|
||||
| BIT(INV_MPU6050_SCAN_ACCL_Z),
|
||||
INV_MPU6050_SCAN_MASK_3AXIS_ACCEL,
|
||||
INV_MPU6050_SCAN_MASK_3AXIS_ACCEL | INV_MPU6050_SCAN_MASK_TEMP,
|
||||
/* 3-axis gyro */
|
||||
BIT(INV_MPU6050_SCAN_GYRO_X)
|
||||
| BIT(INV_MPU6050_SCAN_GYRO_Y)
|
||||
| BIT(INV_MPU6050_SCAN_GYRO_Z),
|
||||
INV_MPU6050_SCAN_MASK_3AXIS_GYRO,
|
||||
INV_MPU6050_SCAN_MASK_3AXIS_GYRO | INV_MPU6050_SCAN_MASK_TEMP,
|
||||
/* 3-axis magn */
|
||||
BIT(INV_MPU9X50_SCAN_MAGN_X)
|
||||
| BIT(INV_MPU9X50_SCAN_MAGN_Y)
|
||||
| BIT(INV_MPU9X50_SCAN_MAGN_Z),
|
||||
INV_MPU9X50_SCAN_MASK_3AXIS_MAGN,
|
||||
INV_MPU9X50_SCAN_MASK_3AXIS_MAGN | INV_MPU6050_SCAN_MASK_TEMP,
|
||||
/* 6-axis accel + gyro */
|
||||
BIT(INV_MPU6050_SCAN_ACCL_X)
|
||||
| BIT(INV_MPU6050_SCAN_ACCL_Y)
|
||||
| BIT(INV_MPU6050_SCAN_ACCL_Z)
|
||||
| BIT(INV_MPU6050_SCAN_GYRO_X)
|
||||
| BIT(INV_MPU6050_SCAN_GYRO_Y)
|
||||
| BIT(INV_MPU6050_SCAN_GYRO_Z),
|
||||
INV_MPU6050_SCAN_MASK_3AXIS_ACCEL | INV_MPU6050_SCAN_MASK_3AXIS_GYRO,
|
||||
INV_MPU6050_SCAN_MASK_3AXIS_ACCEL | INV_MPU6050_SCAN_MASK_3AXIS_GYRO
|
||||
| INV_MPU6050_SCAN_MASK_TEMP,
|
||||
/* 6-axis accel + magn */
|
||||
BIT(INV_MPU6050_SCAN_ACCL_X)
|
||||
| BIT(INV_MPU6050_SCAN_ACCL_Y)
|
||||
| BIT(INV_MPU6050_SCAN_ACCL_Z)
|
||||
| BIT(INV_MPU9X50_SCAN_MAGN_X)
|
||||
| BIT(INV_MPU9X50_SCAN_MAGN_Y)
|
||||
| BIT(INV_MPU9X50_SCAN_MAGN_Z),
|
||||
INV_MPU6050_SCAN_MASK_3AXIS_ACCEL | INV_MPU9X50_SCAN_MASK_3AXIS_MAGN,
|
||||
INV_MPU6050_SCAN_MASK_3AXIS_ACCEL | INV_MPU9X50_SCAN_MASK_3AXIS_MAGN
|
||||
| INV_MPU6050_SCAN_MASK_TEMP,
|
||||
/* 6-axis gyro + magn */
|
||||
BIT(INV_MPU6050_SCAN_GYRO_X)
|
||||
| BIT(INV_MPU6050_SCAN_GYRO_Y)
|
||||
| BIT(INV_MPU6050_SCAN_GYRO_Z)
|
||||
| BIT(INV_MPU9X50_SCAN_MAGN_X)
|
||||
| BIT(INV_MPU9X50_SCAN_MAGN_Y)
|
||||
| BIT(INV_MPU9X50_SCAN_MAGN_Z),
|
||||
INV_MPU6050_SCAN_MASK_3AXIS_GYRO | INV_MPU9X50_SCAN_MASK_3AXIS_MAGN,
|
||||
INV_MPU6050_SCAN_MASK_3AXIS_GYRO | INV_MPU9X50_SCAN_MASK_3AXIS_MAGN
|
||||
| INV_MPU6050_SCAN_MASK_TEMP,
|
||||
/* 9-axis accel + gyro + magn */
|
||||
BIT(INV_MPU6050_SCAN_ACCL_X)
|
||||
| BIT(INV_MPU6050_SCAN_ACCL_Y)
|
||||
| BIT(INV_MPU6050_SCAN_ACCL_Z)
|
||||
| BIT(INV_MPU6050_SCAN_GYRO_X)
|
||||
| BIT(INV_MPU6050_SCAN_GYRO_Y)
|
||||
| BIT(INV_MPU6050_SCAN_GYRO_Z)
|
||||
| BIT(INV_MPU9X50_SCAN_MAGN_X)
|
||||
| BIT(INV_MPU9X50_SCAN_MAGN_Y)
|
||||
| BIT(INV_MPU9X50_SCAN_MAGN_Z),
|
||||
INV_MPU6050_SCAN_MASK_3AXIS_ACCEL | INV_MPU6050_SCAN_MASK_3AXIS_GYRO
|
||||
| INV_MPU9X50_SCAN_MASK_3AXIS_MAGN,
|
||||
INV_MPU6050_SCAN_MASK_3AXIS_ACCEL | INV_MPU6050_SCAN_MASK_3AXIS_GYRO
|
||||
| INV_MPU9X50_SCAN_MASK_3AXIS_MAGN
|
||||
| INV_MPU6050_SCAN_MASK_TEMP,
|
||||
0,
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec inv_icm20602_channels[] = {
|
||||
IIO_CHAN_SOFT_TIMESTAMP(INV_ICM20602_SCAN_TIMESTAMP),
|
||||
{
|
||||
.type = IIO_TEMP,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW)
|
||||
| BIT(IIO_CHAN_INFO_OFFSET)
|
||||
| BIT(IIO_CHAN_INFO_SCALE),
|
||||
.scan_index = INV_ICM20602_SCAN_TEMP,
|
||||
.scan_type = {
|
||||
.sign = 's',
|
||||
.realbits = 16,
|
||||
.storagebits = 16,
|
||||
.shift = 0,
|
||||
.endianness = IIO_BE,
|
||||
},
|
||||
},
|
||||
|
||||
INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_X, INV_ICM20602_SCAN_GYRO_X),
|
||||
INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_Y, INV_ICM20602_SCAN_GYRO_Y),
|
||||
INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_Z, INV_ICM20602_SCAN_GYRO_Z),
|
||||
|
||||
INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_Y, INV_ICM20602_SCAN_ACCL_Y),
|
||||
INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_X, INV_ICM20602_SCAN_ACCL_X),
|
||||
INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_Z, INV_ICM20602_SCAN_ACCL_Z),
|
||||
};
|
||||
|
||||
static const unsigned long inv_icm20602_scan_masks[] = {
|
||||
/* 3-axis accel + temp (mandatory) */
|
||||
BIT(INV_ICM20602_SCAN_ACCL_X)
|
||||
| BIT(INV_ICM20602_SCAN_ACCL_Y)
|
||||
| BIT(INV_ICM20602_SCAN_ACCL_Z)
|
||||
| BIT(INV_ICM20602_SCAN_TEMP),
|
||||
INV_MPU6050_SCAN_MASK_3AXIS_ACCEL | INV_MPU6050_SCAN_MASK_TEMP,
|
||||
/* 3-axis gyro + temp (mandatory) */
|
||||
BIT(INV_ICM20602_SCAN_GYRO_X)
|
||||
| BIT(INV_ICM20602_SCAN_GYRO_Y)
|
||||
| BIT(INV_ICM20602_SCAN_GYRO_Z)
|
||||
| BIT(INV_ICM20602_SCAN_TEMP),
|
||||
INV_MPU6050_SCAN_MASK_3AXIS_GYRO | INV_MPU6050_SCAN_MASK_TEMP,
|
||||
/* 6-axis accel + gyro + temp (mandatory) */
|
||||
BIT(INV_ICM20602_SCAN_ACCL_X)
|
||||
| BIT(INV_ICM20602_SCAN_ACCL_Y)
|
||||
| BIT(INV_ICM20602_SCAN_ACCL_Z)
|
||||
| BIT(INV_ICM20602_SCAN_GYRO_X)
|
||||
| BIT(INV_ICM20602_SCAN_GYRO_Y)
|
||||
| BIT(INV_ICM20602_SCAN_GYRO_Z)
|
||||
| BIT(INV_ICM20602_SCAN_TEMP),
|
||||
INV_MPU6050_SCAN_MASK_3AXIS_ACCEL | INV_MPU6050_SCAN_MASK_3AXIS_GYRO
|
||||
| INV_MPU6050_SCAN_MASK_TEMP,
|
||||
0,
|
||||
};
|
||||
|
||||
@ -1241,7 +1220,7 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name,
|
||||
irq_type = irqd_get_trigger_type(desc);
|
||||
if (!irq_type)
|
||||
irq_type = IRQF_TRIGGER_RISING;
|
||||
if (irq_type == IRQF_TRIGGER_RISING)
|
||||
if (irq_type & IRQF_TRIGGER_RISING) // rising or both-edge
|
||||
st->irq_mask = INV_MPU6050_ACTIVE_HIGH;
|
||||
else if (irq_type == IRQF_TRIGGER_FALLING)
|
||||
st->irq_mask = INV_MPU6050_ACTIVE_LOW;
|
||||
@ -1324,25 +1303,20 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name,
|
||||
inv_mpu_bus_setup(indio_dev);
|
||||
|
||||
switch (chip_type) {
|
||||
case INV_MPU9150:
|
||||
indio_dev->channels = inv_mpu9150_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(inv_mpu9150_channels);
|
||||
indio_dev->available_scan_masks = inv_mpu9x50_scan_masks;
|
||||
break;
|
||||
case INV_MPU9250:
|
||||
case INV_MPU9255:
|
||||
/*
|
||||
* Use magnetometer inside the chip only if there is no i2c
|
||||
* auxiliary device in use.
|
||||
*/
|
||||
if (!st->magn_disabled) {
|
||||
indio_dev->channels = inv_mpu9250_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(inv_mpu9250_channels);
|
||||
indio_dev->available_scan_masks = inv_mpu9x50_scan_masks;
|
||||
} else {
|
||||
indio_dev->channels = inv_mpu_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(inv_mpu_channels);
|
||||
indio_dev->available_scan_masks = inv_mpu_scan_masks;
|
||||
}
|
||||
indio_dev->channels = inv_mpu9250_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(inv_mpu9250_channels);
|
||||
indio_dev->available_scan_masks = inv_mpu9x50_scan_masks;
|
||||
break;
|
||||
case INV_ICM20602:
|
||||
indio_dev->channels = inv_icm20602_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(inv_icm20602_channels);
|
||||
indio_dev->channels = inv_mpu_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(inv_mpu_channels);
|
||||
indio_dev->available_scan_masks = inv_icm20602_scan_masks;
|
||||
break;
|
||||
default:
|
||||
@ -1351,6 +1325,15 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name,
|
||||
indio_dev->available_scan_masks = inv_mpu_scan_masks;
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* Use magnetometer inside the chip only if there is no i2c
|
||||
* auxiliary device in use. Otherwise Going back to 6-axis only.
|
||||
*/
|
||||
if (st->magn_disabled) {
|
||||
indio_dev->channels = inv_mpu_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(inv_mpu_channels);
|
||||
indio_dev->available_scan_masks = inv_mpu_scan_masks;
|
||||
}
|
||||
|
||||
indio_dev->info = &mpu_info;
|
||||
indio_dev->modes = INDIO_BUFFER_TRIGGERED;
|
||||
|
@ -77,6 +77,7 @@ static bool inv_mpu_i2c_aux_bus(struct device *dev)
|
||||
case INV_ICM20602:
|
||||
/* no i2c auxiliary bus on the chip */
|
||||
return false;
|
||||
case INV_MPU9150:
|
||||
case INV_MPU9250:
|
||||
case INV_MPU9255:
|
||||
if (st->magn_disabled)
|
||||
@ -102,6 +103,7 @@ static int inv_mpu_magn_disable(struct iio_dev *indio_dev)
|
||||
struct device_node *mux_node;
|
||||
|
||||
switch (st->chip_type) {
|
||||
case INV_MPU9150:
|
||||
case INV_MPU9250:
|
||||
case INV_MPU9255:
|
||||
mux_node = of_get_child_by_name(dev->of_node, "i2c-gate");
|
||||
|
@ -86,6 +86,7 @@ enum inv_devices {
|
||||
* @accl_fs: accel full scale range.
|
||||
* @accl_fifo_enable: enable accel data output
|
||||
* @gyro_fifo_enable: enable gyro data output
|
||||
* @temp_fifo_enable: enable temp data output
|
||||
* @magn_fifo_enable: enable magn data output
|
||||
* @divider: chip sample rate divider (sample rate divider - 1)
|
||||
*/
|
||||
@ -95,6 +96,7 @@ struct inv_mpu6050_chip_config {
|
||||
unsigned int accl_fs:2;
|
||||
unsigned int accl_fifo_enable:1;
|
||||
unsigned int gyro_fifo_enable:1;
|
||||
unsigned int temp_fifo_enable:1;
|
||||
unsigned int magn_fifo_enable:1;
|
||||
u8 divider;
|
||||
u8 user_ctrl;
|
||||
@ -184,6 +186,7 @@ struct inv_mpu6050_state {
|
||||
#define INV_MPU6050_BIT_SLAVE_2 0x04
|
||||
#define INV_MPU6050_BIT_ACCEL_OUT 0x08
|
||||
#define INV_MPU6050_BITS_GYRO_OUT 0x70
|
||||
#define INV_MPU6050_BIT_TEMP_OUT 0x80
|
||||
|
||||
#define INV_MPU6050_REG_I2C_MST_CTRL 0x24
|
||||
#define INV_MPU6050_BITS_I2C_MST_CLK_400KHZ 0x0D
|
||||
@ -268,8 +271,8 @@ struct inv_mpu6050_state {
|
||||
/* MPU9X50 9-axis magnetometer */
|
||||
#define INV_MPU9X50_BYTES_MAGN 7
|
||||
|
||||
/* ICM20602 FIFO samples include temperature readings */
|
||||
#define INV_ICM20602_BYTES_PER_TEMP_SENSOR 2
|
||||
/* FIFO temperature sample size */
|
||||
#define INV_MPU6050_BYTES_PER_TEMP_SENSOR 2
|
||||
|
||||
/* mpu6500 registers */
|
||||
#define INV_MPU6500_REG_ACCEL_CONFIG_2 0x1D
|
||||
@ -298,7 +301,7 @@ struct inv_mpu6050_state {
|
||||
#define INV_ICM20608_TEMP_OFFSET 8170
|
||||
#define INV_ICM20608_TEMP_SCALE 3059976
|
||||
|
||||
/* 6 + 6 + 7 (for MPU9x50) = 19 round up to 24 and plus 8 */
|
||||
/* 6 + 6 + 2 + 7 (for MPU9x50) = 21 round up to 24 and plus 8 */
|
||||
#define INV_MPU6050_OUTPUT_DATA_SIZE 32
|
||||
|
||||
#define INV_MPU6050_REG_INT_PIN_CFG 0x37
|
||||
@ -344,6 +347,7 @@ enum inv_mpu6050_scan {
|
||||
INV_MPU6050_SCAN_ACCL_X,
|
||||
INV_MPU6050_SCAN_ACCL_Y,
|
||||
INV_MPU6050_SCAN_ACCL_Z,
|
||||
INV_MPU6050_SCAN_TEMP,
|
||||
INV_MPU6050_SCAN_GYRO_X,
|
||||
INV_MPU6050_SCAN_GYRO_Y,
|
||||
INV_MPU6050_SCAN_GYRO_Z,
|
||||
@ -355,18 +359,6 @@ enum inv_mpu6050_scan {
|
||||
INV_MPU9X50_SCAN_TIMESTAMP,
|
||||
};
|
||||
|
||||
/* scan element definition for ICM20602, which includes temperature */
|
||||
enum inv_icm20602_scan {
|
||||
INV_ICM20602_SCAN_ACCL_X,
|
||||
INV_ICM20602_SCAN_ACCL_Y,
|
||||
INV_ICM20602_SCAN_ACCL_Z,
|
||||
INV_ICM20602_SCAN_TEMP,
|
||||
INV_ICM20602_SCAN_GYRO_X,
|
||||
INV_ICM20602_SCAN_GYRO_Y,
|
||||
INV_ICM20602_SCAN_GYRO_Z,
|
||||
INV_ICM20602_SCAN_TIMESTAMP,
|
||||
};
|
||||
|
||||
enum inv_mpu6050_filter_e {
|
||||
INV_MPU6050_FILTER_256HZ_NOLPF2 = 0,
|
||||
INV_MPU6050_FILTER_188HZ,
|
||||
|
@ -12,7 +12,9 @@
|
||||
#include "inv_mpu_magn.h"
|
||||
|
||||
/*
|
||||
* MPU9250 magnetometer is an AKM AK8963 chip on I2C aux bus
|
||||
* MPU9xxx magnetometer are AKM chips on I2C aux bus
|
||||
* MPU9150 is AK8975
|
||||
* MPU9250 is AK8963
|
||||
*/
|
||||
#define INV_MPU_MAGN_I2C_ADDR 0x0C
|
||||
|
||||
@ -33,10 +35,10 @@
|
||||
#define INV_MPU_MAGN_BITS_MODE_PWDN 0x00
|
||||
#define INV_MPU_MAGN_BITS_MODE_SINGLE 0x01
|
||||
#define INV_MPU_MAGN_BITS_MODE_FUSE 0x0F
|
||||
#define INV_MPU_MAGN_BIT_OUTPUT_BIT 0x10
|
||||
#define INV_MPU9250_MAGN_BIT_OUTPUT_BIT 0x10
|
||||
|
||||
#define INV_MPU_MAGN_REG_CNTL2 0x0B
|
||||
#define INV_MPU_MAGN_BIT_SRST 0x01
|
||||
#define INV_MPU9250_MAGN_REG_CNTL2 0x0B
|
||||
#define INV_MPU9250_MAGN_BIT_SRST 0x01
|
||||
|
||||
#define INV_MPU_MAGN_REG_ASAX 0x10
|
||||
#define INV_MPU_MAGN_REG_ASAY 0x11
|
||||
@ -48,6 +50,7 @@
|
||||
static bool inv_magn_supported(const struct inv_mpu6050_state *st)
|
||||
{
|
||||
switch (st->chip_type) {
|
||||
case INV_MPU9150:
|
||||
case INV_MPU9250:
|
||||
case INV_MPU9255:
|
||||
return true;
|
||||
@ -61,6 +64,7 @@ static int inv_magn_init(struct inv_mpu6050_state *st)
|
||||
{
|
||||
uint8_t val;
|
||||
uint8_t asa[3];
|
||||
int32_t sensitivity;
|
||||
int ret;
|
||||
|
||||
/* check whoami */
|
||||
@ -71,12 +75,19 @@ static int inv_magn_init(struct inv_mpu6050_state *st)
|
||||
if (val != INV_MPU_MAGN_BITS_WIA)
|
||||
return -ENODEV;
|
||||
|
||||
/* reset chip */
|
||||
ret = inv_mpu_aux_write(st, INV_MPU_MAGN_I2C_ADDR,
|
||||
INV_MPU_MAGN_REG_CNTL2,
|
||||
INV_MPU_MAGN_BIT_SRST);
|
||||
if (ret)
|
||||
return ret;
|
||||
/* software reset for MPU925x only */
|
||||
switch (st->chip_type) {
|
||||
case INV_MPU9250:
|
||||
case INV_MPU9255:
|
||||
ret = inv_mpu_aux_write(st, INV_MPU_MAGN_I2C_ADDR,
|
||||
INV_MPU9250_MAGN_REG_CNTL2,
|
||||
INV_MPU9250_MAGN_BIT_SRST);
|
||||
if (ret)
|
||||
return ret;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* read fuse ROM data */
|
||||
ret = inv_mpu_aux_write(st, INV_MPU_MAGN_I2C_ADDR,
|
||||
@ -97,6 +108,25 @@ static int inv_magn_init(struct inv_mpu6050_state *st)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Sensor sentivity
|
||||
* 1 uT = 0.01 G and value is in micron (1e6)
|
||||
* sensitvity = x uT * 0.01 * 1e6
|
||||
*/
|
||||
switch (st->chip_type) {
|
||||
case INV_MPU9150:
|
||||
/* sensor sensitivity is 0.3 uT */
|
||||
sensitivity = 3000;
|
||||
break;
|
||||
case INV_MPU9250:
|
||||
case INV_MPU9255:
|
||||
/* sensor sensitivity in 16 bits mode: 0.15 uT */
|
||||
sensitivity = 1500;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sensitivity adjustement and scale to Gauss
|
||||
*
|
||||
@ -104,16 +134,11 @@ static int inv_magn_init(struct inv_mpu6050_state *st)
|
||||
* Factor simplification:
|
||||
* Hadj = H * ((ASA + 128) / 256)
|
||||
*
|
||||
* Sensor sentivity
|
||||
* 0.15 uT in 16 bits mode
|
||||
* 1 uT = 0.01 G and value is in micron (1e6)
|
||||
* sensitvity = 0.15 uT * 0.01 * 1e6
|
||||
*
|
||||
* raw_to_gauss = Hadj * 1500
|
||||
* raw_to_gauss = Hadj * sensitivity
|
||||
*/
|
||||
st->magn_raw_to_gauss[0] = (((int32_t)asa[0] + 128) * 1500) / 256;
|
||||
st->magn_raw_to_gauss[1] = (((int32_t)asa[1] + 128) * 1500) / 256;
|
||||
st->magn_raw_to_gauss[2] = (((int32_t)asa[2] + 128) * 1500) / 256;
|
||||
st->magn_raw_to_gauss[0] = (((int32_t)asa[0] + 128) * sensitivity) / 256;
|
||||
st->magn_raw_to_gauss[1] = (((int32_t)asa[1] + 128) * sensitivity) / 256;
|
||||
st->magn_raw_to_gauss[2] = (((int32_t)asa[2] + 128) * sensitivity) / 256;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -129,6 +154,7 @@ static int inv_magn_init(struct inv_mpu6050_state *st)
|
||||
*/
|
||||
int inv_mpu_magn_probe(struct inv_mpu6050_state *st)
|
||||
{
|
||||
uint8_t val;
|
||||
int ret;
|
||||
|
||||
/* quit if chip is not supported */
|
||||
@ -179,10 +205,17 @@ int inv_mpu_magn_probe(struct inv_mpu6050_state *st)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* add 16 bits mode */
|
||||
ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_DO(1),
|
||||
INV_MPU_MAGN_BITS_MODE_SINGLE |
|
||||
INV_MPU_MAGN_BIT_OUTPUT_BIT);
|
||||
/* add 16 bits mode for MPU925x */
|
||||
val = INV_MPU_MAGN_BITS_MODE_SINGLE;
|
||||
switch (st->chip_type) {
|
||||
case INV_MPU9250:
|
||||
case INV_MPU9255:
|
||||
val |= INV_MPU9250_MAGN_BIT_OUTPUT_BIT;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_DO(1), val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -237,6 +270,7 @@ int inv_mpu_magn_set_orient(struct inv_mpu6050_state *st)
|
||||
|
||||
/* fill magnetometer orientation */
|
||||
switch (st->chip_type) {
|
||||
case INV_MPU9150:
|
||||
case INV_MPU9250:
|
||||
case INV_MPU9255:
|
||||
/* x <- y */
|
||||
|
@ -142,6 +142,8 @@ int inv_reset_fifo(struct iio_dev *indio_dev)
|
||||
d |= INV_MPU6050_BITS_GYRO_OUT;
|
||||
if (st->chip_config.accl_fifo_enable)
|
||||
d |= INV_MPU6050_BIT_ACCEL_OUT;
|
||||
if (st->chip_config.temp_fifo_enable)
|
||||
d |= INV_MPU6050_BIT_TEMP_OUT;
|
||||
if (st->chip_config.magn_fifo_enable)
|
||||
d |= INV_MPU6050_BIT_SLAVE_0;
|
||||
result = regmap_write(st->map, st->reg->fifo_en, d);
|
||||
@ -183,11 +185,8 @@ irqreturn_t inv_mpu6050_read_fifo(int irq, void *p)
|
||||
"failed to ack interrupt\n");
|
||||
goto flush_fifo;
|
||||
}
|
||||
if (!(int_status & INV_MPU6050_BIT_RAW_DATA_RDY_INT)) {
|
||||
dev_warn(regmap_get_device(st->map),
|
||||
"spurious interrupt with status 0x%x\n", int_status);
|
||||
if (!(int_status & INV_MPU6050_BIT_RAW_DATA_RDY_INT))
|
||||
goto end_session;
|
||||
}
|
||||
|
||||
if (!(st->chip_config.accl_fifo_enable |
|
||||
st->chip_config.gyro_fifo_enable |
|
||||
@ -200,8 +199,8 @@ irqreturn_t inv_mpu6050_read_fifo(int irq, void *p)
|
||||
if (st->chip_config.gyro_fifo_enable)
|
||||
bytes_per_datum += INV_MPU6050_BYTES_PER_3AXIS_SENSOR;
|
||||
|
||||
if (st->chip_type == INV_ICM20602)
|
||||
bytes_per_datum += INV_ICM20602_BYTES_PER_TEMP_SENSOR;
|
||||
if (st->chip_config.temp_fifo_enable)
|
||||
bytes_per_datum += INV_MPU6050_BYTES_PER_TEMP_SENSOR;
|
||||
|
||||
if (st->chip_config.magn_fifo_enable)
|
||||
bytes_per_datum += INV_MPU9X50_BYTES_MAGN;
|
||||
|
@ -74,7 +74,6 @@ static int inv_mpu_probe(struct spi_device *spi)
|
||||
static const struct spi_device_id inv_mpu_id[] = {
|
||||
{"mpu6000", INV_MPU6000},
|
||||
{"mpu6500", INV_MPU6500},
|
||||
{"mpu9150", INV_MPU9150},
|
||||
{"mpu9250", INV_MPU9250},
|
||||
{"mpu9255", INV_MPU9255},
|
||||
{"icm20608", INV_ICM20608},
|
||||
|
@ -24,6 +24,9 @@ static void inv_scan_query_mpu6050(struct iio_dev *indio_dev)
|
||||
indio_dev->active_scan_mask) ||
|
||||
test_bit(INV_MPU6050_SCAN_ACCL_Z,
|
||||
indio_dev->active_scan_mask);
|
||||
|
||||
st->chip_config.temp_fifo_enable =
|
||||
test_bit(INV_MPU6050_SCAN_TEMP, indio_dev->active_scan_mask);
|
||||
}
|
||||
|
||||
static void inv_scan_query_mpu9x50(struct iio_dev *indio_dev)
|
||||
@ -50,6 +53,7 @@ static void inv_scan_query(struct iio_dev *indio_dev)
|
||||
struct inv_mpu6050_state *st = iio_priv(indio_dev);
|
||||
|
||||
switch (st->chip_type) {
|
||||
case INV_MPU9150:
|
||||
case INV_MPU9250:
|
||||
case INV_MPU9255:
|
||||
return inv_scan_query_mpu9x50(indio_dev);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user