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:
|
Description:
|
||||||
List of valid periods (in seconds) for which the light intensity
|
List of valid periods (in seconds) for which the light intensity
|
||||||
must be above the threshold level before interrupt is asserted.
|
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://omapworld.com/BMA180_111_1002839.pdf
|
||||||
http://ae-bst.resource.bosch.com/media/products/dokumente/bma250/bst-bma250-ds002-05.pdf
|
http://ae-bst.resource.bosch.com/media/products/dokumente/bma250/bst-bma250-ds002-05.pdf
|
||||||
|
|
||||||
Required properties:
|
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
|
- reg : the I2C address of the sensor
|
||||||
|
|
||||||
Optional properties:
|
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"
|
"kionix,kxtf9"
|
||||||
- reg: i2c slave address
|
- reg: i2c slave address
|
||||||
|
|
||||||
|
Optional properties:
|
||||||
|
|
||||||
|
- mount-matrix: an optional 3x3 mounting rotation matrix
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
kxtf9@f {
|
kxtf9@f {
|
||||||
compatible = "kionix,kxtf9";
|
compatible = "kionix,kxtf9";
|
||||||
reg = <0x0F>;
|
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,adis16480"
|
||||||
* "adi,adis16485"
|
* "adi,adis16485"
|
||||||
* "adi,adis16488"
|
* "adi,adis16488"
|
||||||
|
* "adi,adis16490"
|
||||||
* "adi,adis16495-1"
|
* "adi,adis16495-1"
|
||||||
* "adi,adis16495-2"
|
* "adi,adis16495-2"
|
||||||
* "adi,adis16495-3"
|
* "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:
|
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
|
- reg: SPI chip select number for the device
|
||||||
- spi-max-frequency: must be 4300000
|
- spi-max-frequency: must be 4300000
|
||||||
- spi-cpha: must be defined for max6675 to enable SPI mode 1
|
- spi-cpha: must be defined for max6675 to enable SPI mode 1
|
||||||
@ -15,7 +18,7 @@ Required properties:
|
|||||||
Example:
|
Example:
|
||||||
|
|
||||||
max31855@0 {
|
max31855@0 {
|
||||||
compatible = "maxim,max31855";
|
compatible = "maxim,max31855k";
|
||||||
reg = <0>;
|
reg = <0>;
|
||||||
spi-max-frequency = <4300000>;
|
spi-max-frequency = <4300000>;
|
||||||
};
|
};
|
||||||
|
@ -109,6 +109,8 @@ patternProperties:
|
|||||||
description: Artesyn Embedded Technologies Inc.
|
description: Artesyn Embedded Technologies Inc.
|
||||||
"^asahi-kasei,.*":
|
"^asahi-kasei,.*":
|
||||||
description: Asahi Kasei Corp.
|
description: Asahi Kasei Corp.
|
||||||
|
"^asc,.*":
|
||||||
|
description: All Sensors Corporation
|
||||||
"^aspeed,.*":
|
"^aspeed,.*":
|
||||||
description: ASPEED Technology Inc.
|
description: ASPEED Technology Inc.
|
||||||
"^asus,.*":
|
"^asus,.*":
|
||||||
@ -717,6 +719,8 @@ patternProperties:
|
|||||||
description: Panasonic Corporation
|
description: Panasonic Corporation
|
||||||
"^parade,.*":
|
"^parade,.*":
|
||||||
description: Parade Technologies Inc.
|
description: Parade Technologies Inc.
|
||||||
|
"^parallax,.*":
|
||||||
|
description: Parallax Inc.
|
||||||
"^pda,.*":
|
"^pda,.*":
|
||||||
description: Precision Design Associates, Inc.
|
description: Precision Design Associates, Inc.
|
||||||
"^pericom,.*":
|
"^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
|
interface_capi
|
||||||
|
|
||||||
avmb1
|
|
||||||
gigaset
|
|
||||||
hysdn
|
|
||||||
m_isdn
|
m_isdn
|
||||||
|
|
||||||
credits
|
credits
|
||||||
|
@ -26,13 +26,6 @@ This standard is freely available from https://www.capi.org.
|
|||||||
2. Driver and Device Registration
|
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 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
|
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
|
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
|
the name of the driver, as a zero-terminated ASCII string
|
||||||
``char revision[32]``
|
``char revision[32]``
|
||||||
the revision number of the driver, as a zero-terminated ASCII string
|
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
|
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
|
pointer to a callback function returning the entry for the device in
|
||||||
the CAPI controller info table, /proc/capi/controller
|
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:
|
Note:
|
||||||
Callback functions except send_message() are never called in interrupt
|
Callback functions except send_message() are never called in interrupt
|
||||||
context.
|
context.
|
||||||
@ -267,25 +251,10 @@ _cmstruct alternative representation for CAPI parameters of type 'struct'
|
|||||||
_cmsg structure members.
|
_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
|
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)
|
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
|
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,
|
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
|
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
|
Macros to extract/set element values from/in a CAPI message header
|
||||||
(from <linux/isdn/capiutil.h>):
|
(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
|
Library functions for working with _cmsg structures
|
||||||
(from <linux/isdn/capiutil.h>):
|
(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)``
|
``char *capi_cmd2str(u8 Command, u8 Subcommand)``
|
||||||
Returns the CAPI 2.0 message name corresponding to the given command
|
Returns the CAPI 2.0 message name corresponding to the given command
|
||||||
and subcommand values, as a static ASCII string. The return value may
|
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' 80-8F linux/arcfb.h conflict!
|
||||||
'F' DD video/sstfb.h conflict!
|
'F' DD video/sstfb.h conflict!
|
||||||
'G' 00-3F drivers/misc/sgi-gru/grulib.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-7F linux/hiddev.h conflict!
|
||||||
'H' 00-0F linux/hidraw.h conflict!
|
'H' 00-0F linux/hidraw.h conflict!
|
||||||
'H' 01 linux/mei.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: Documentation/i2c/busses/i2c-ali1563.rst
|
||||||
F: drivers/i2c/busses/i2c-ali1563.c
|
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
|
ALLEGRO DVT VIDEO IP CORE DRIVER
|
||||||
M: Michael Tretter <m.tretter@pengutronix.de>
|
M: Michael Tretter <m.tretter@pengutronix.de>
|
||||||
R: Pengutronix Kernel Team <kernel@pengutronix.de>
|
R: Pengutronix Kernel Team <kernel@pengutronix.de>
|
||||||
@ -907,6 +915,14 @@ S: Supported
|
|||||||
F: drivers/iio/dac/ad5758.c
|
F: drivers/iio/dac/ad5758.c
|
||||||
F: Documentation/devicetree/bindings/iio/dac/ad5758.txt
|
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
|
ANALOG DEVICES INC AD7124 DRIVER
|
||||||
M: Stefan Popa <stefan.popa@analog.com>
|
M: Stefan Popa <stefan.popa@analog.com>
|
||||||
L: linux-iio@vger.kernel.org
|
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-ad9523
|
||||||
F: Documentation/ABI/testing/sysfs-bus-iio-frequency-adf4350
|
F: Documentation/ABI/testing/sysfs-bus-iio-frequency-adf4350
|
||||||
F: drivers/iio/*/ad*
|
F: drivers/iio/*/ad*
|
||||||
F: drivers/iio/adc/ltc2497*
|
F: drivers/iio/adc/ltc249*
|
||||||
X: drivers/iio/*/adjd*
|
X: drivers/iio/*/adjd*
|
||||||
F: drivers/staging/iio/*/ad*
|
F: drivers/staging/iio/*/ad*
|
||||||
|
|
||||||
@ -3116,6 +3132,13 @@ S: Supported
|
|||||||
F: drivers/net/bonding/
|
F: drivers/net/bonding/
|
||||||
F: include/uapi/linux/if_bonding.h
|
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)
|
BPF (Safe dynamic programs and tools)
|
||||||
M: Alexei Starovoitov <ast@kernel.org>
|
M: Alexei Starovoitov <ast@kernel.org>
|
||||||
M: Daniel Borkmann <daniel@iogearbox.net>
|
M: Daniel Borkmann <daniel@iogearbox.net>
|
||||||
@ -8862,7 +8885,7 @@ S: Maintained
|
|||||||
F: drivers/isdn/mISDN
|
F: drivers/isdn/mISDN
|
||||||
F: drivers/isdn/hardware
|
F: drivers/isdn/hardware
|
||||||
|
|
||||||
ISDN/CAPI SUBSYSTEM
|
ISDN/CMTP OVER BLUETOOTH
|
||||||
M: Karsten Keil <isdn@linux-pingi.de>
|
M: Karsten Keil <isdn@linux-pingi.de>
|
||||||
L: isdn4linux@listserv.isdn4linux.de (subscribers-only)
|
L: isdn4linux@listserv.isdn4linux.de (subscribers-only)
|
||||||
L: netdev@vger.kernel.org
|
L: netdev@vger.kernel.org
|
||||||
@ -8870,7 +8893,6 @@ W: http://www.isdn4linux.de
|
|||||||
S: Odd Fixes
|
S: Odd Fixes
|
||||||
F: Documentation/isdn/
|
F: Documentation/isdn/
|
||||||
F: drivers/isdn/capi/
|
F: drivers/isdn/capi/
|
||||||
F: drivers/staging/isdn/
|
|
||||||
F: net/bluetooth/cmtp/
|
F: net/bluetooth/cmtp/
|
||||||
F: include/linux/isdn/
|
F: include/linux/isdn/
|
||||||
F: include/uapi/linux/isdn/
|
F: include/uapi/linux/isdn/
|
||||||
@ -12528,6 +12550,13 @@ L: platform-driver-x86@vger.kernel.org
|
|||||||
S: Maintained
|
S: Maintained
|
||||||
F: drivers/platform/x86/panasonic-laptop.c
|
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
|
PARALLEL LCD/KEYPAD PANEL DRIVER
|
||||||
M: Willy Tarreau <willy@haproxy.com>
|
M: Willy Tarreau <willy@haproxy.com>
|
||||||
M: Ksenija Stanojevic <ksenija.stanojevic@gmail.com>
|
M: Ksenija Stanojevic <ksenija.stanojevic@gmail.com>
|
||||||
|
@ -89,13 +89,13 @@ config ADXL372_I2C
|
|||||||
module will be called adxl372_i2c.
|
module will be called adxl372_i2c.
|
||||||
|
|
||||||
config BMA180
|
config BMA180
|
||||||
tristate "Bosch BMA180/BMA250 3-Axis Accelerometer Driver"
|
tristate "Bosch BMA180/BMA25x 3-Axis Accelerometer Driver"
|
||||||
depends on I2C
|
depends on I2C
|
||||||
select IIO_BUFFER
|
select IIO_BUFFER
|
||||||
select IIO_TRIGGERED_BUFFER
|
select IIO_TRIGGERED_BUFFER
|
||||||
help
|
help
|
||||||
Say Y here if you want to build a driver for the Bosch BMA180 or
|
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
|
To compile this driver as a module, choose M here: the
|
||||||
module will be called bma180.
|
module will be called bma180.
|
||||||
@ -112,6 +112,22 @@ config BMA220
|
|||||||
To compile this driver as a module, choose M here: the
|
To compile this driver as a module, choose M here: the
|
||||||
module will be called bma220_spi.
|
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
|
config BMC150_ACCEL
|
||||||
tristate "Bosch BMC150 Accelerometer Driver"
|
tristate "Bosch BMC150 Accelerometer Driver"
|
||||||
select IIO_BUFFER
|
select IIO_BUFFER
|
||||||
|
@ -14,6 +14,8 @@ obj-$(CONFIG_ADXL372_I2C) += adxl372_i2c.o
|
|||||||
obj-$(CONFIG_ADXL372_SPI) += adxl372_spi.o
|
obj-$(CONFIG_ADXL372_SPI) += adxl372_spi.o
|
||||||
obj-$(CONFIG_BMA180) += bma180.o
|
obj-$(CONFIG_BMA180) += bma180.o
|
||||||
obj-$(CONFIG_BMA220) += bma220_spi.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) += bmc150-accel-core.o
|
||||||
obj-$(CONFIG_BMC150_ACCEL_I2C) += bmc150-accel-i2c.o
|
obj-$(CONFIG_BMC150_ACCEL_I2C) += bmc150-accel-i2c.o
|
||||||
obj-$(CONFIG_BMC150_ACCEL_SPI) += bmc150-accel-spi.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",
|
[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 = {
|
static const struct adis_data adis16201_data = {
|
||||||
.read_delay = 20,
|
.read_delay = 20,
|
||||||
.msc_ctrl_reg = ADIS16201_MSC_CTRL_REG,
|
.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_mask = ADIS16201_MSC_CTRL_SELF_TEST_EN,
|
||||||
.self_test_no_autoclear = true,
|
.self_test_no_autoclear = true,
|
||||||
.startup_delay = ADIS16201_STARTUP_DELAY_MS,
|
.timeouts = &adis16201_timeouts,
|
||||||
|
|
||||||
.status_error_msgs = adis16201_status_error_msgs,
|
.status_error_msgs = adis16201_status_error_msgs,
|
||||||
.status_error_mask = BIT(ADIS16201_DIAG_STAT_SPI_FAIL_BIT) |
|
.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",
|
[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 = {
|
static const struct adis_data adis16209_data = {
|
||||||
.read_delay = 30,
|
.read_delay = 30,
|
||||||
.msc_ctrl_reg = ADIS16209_MSC_CTRL_REG,
|
.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_mask = ADIS16209_MSC_CTRL_SELF_TEST_EN,
|
||||||
.self_test_no_autoclear = true,
|
.self_test_no_autoclear = true,
|
||||||
.startup_delay = ADIS16209_STARTUP_DELAY_MS,
|
.timeouts = &adis16209_timeouts,
|
||||||
|
|
||||||
.status_error_msgs = adis16209_status_error_msgs,
|
.status_error_msgs = adis16209_status_error_msgs,
|
||||||
.status_error_mask = BIT(ADIS16209_STAT_SELFTEST_FAIL_BIT) |
|
.status_error_mask = BIT(ADIS16209_STAT_SELFTEST_FAIL_BIT) |
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
* SPI is not supported by driver
|
* SPI is not supported by driver
|
||||||
* BMA180: 7-bit I2C slave address 0x40 or 0x41
|
* BMA180: 7-bit I2C slave address 0x40 or 0x41
|
||||||
* BMA250: 7-bit I2C slave address 0x18 or 0x19
|
* BMA250: 7-bit I2C slave address 0x18 or 0x19
|
||||||
|
* BMA254: 7-bit I2C slave address 0x18 or 0x19
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
@ -18,6 +19,7 @@
|
|||||||
#include <linux/of_device.h>
|
#include <linux/of_device.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/bitops.h>
|
#include <linux/bitops.h>
|
||||||
|
#include <linux/regulator/consumer.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/string.h>
|
#include <linux/string.h>
|
||||||
#include <linux/iio/iio.h>
|
#include <linux/iio/iio.h>
|
||||||
@ -33,17 +35,20 @@
|
|||||||
enum chip_ids {
|
enum chip_ids {
|
||||||
BMA180,
|
BMA180,
|
||||||
BMA250,
|
BMA250,
|
||||||
|
BMA254,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct bma180_data;
|
struct bma180_data;
|
||||||
|
|
||||||
struct bma180_part_info {
|
struct bma180_part_info {
|
||||||
|
u8 chip_id;
|
||||||
const struct iio_chan_spec *channels;
|
const struct iio_chan_spec *channels;
|
||||||
unsigned int num_channels;
|
unsigned int num_channels;
|
||||||
const int *scale_table;
|
const int *scale_table;
|
||||||
unsigned int num_scales;
|
unsigned int num_scales;
|
||||||
const int *bw_table;
|
const int *bw_table;
|
||||||
unsigned int num_bw;
|
unsigned int num_bw;
|
||||||
|
int center_temp;
|
||||||
|
|
||||||
u8 int_reset_reg, int_reset_mask;
|
u8 int_reset_reg, int_reset_mask;
|
||||||
u8 sleep_reg, sleep_mask;
|
u8 sleep_reg, sleep_mask;
|
||||||
@ -51,6 +56,7 @@ struct bma180_part_info {
|
|||||||
u8 scale_reg, scale_mask;
|
u8 scale_reg, scale_mask;
|
||||||
u8 power_reg, power_mask, lowpower_val;
|
u8 power_reg, power_mask, lowpower_val;
|
||||||
u8 int_enable_reg, int_enable_mask;
|
u8 int_enable_reg, int_enable_mask;
|
||||||
|
u8 int_map_reg, int_enable_dataready_int1_mask;
|
||||||
u8 softreset_reg;
|
u8 softreset_reg;
|
||||||
|
|
||||||
int (*chip_config)(struct bma180_data *data);
|
int (*chip_config)(struct bma180_data *data);
|
||||||
@ -89,6 +95,8 @@ struct bma180_part_info {
|
|||||||
#define BMA180_RESET_VAL 0xb6
|
#define BMA180_RESET_VAL 0xb6
|
||||||
|
|
||||||
#define BMA180_ID_REG_VAL 0x03
|
#define BMA180_ID_REG_VAL 0x03
|
||||||
|
#define BMA250_ID_REG_VAL 0x03
|
||||||
|
#define BMA254_ID_REG_VAL 0xfa /* 250 decimal */
|
||||||
|
|
||||||
/* Chip power modes */
|
/* Chip power modes */
|
||||||
#define BMA180_LOW_POWER 0x03
|
#define BMA180_LOW_POWER 0x03
|
||||||
@ -109,7 +117,26 @@ struct bma180_part_info {
|
|||||||
#define BMA250_INT1_DATA_MASK BIT(0)
|
#define BMA250_INT1_DATA_MASK BIT(0)
|
||||||
#define BMA250_INT_RESET_MASK BIT(7) /* Reset pending interrupts */
|
#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 bma180_data {
|
||||||
|
struct regulator *vdd_supply;
|
||||||
|
struct regulator *vddio_supply;
|
||||||
struct i2c_client *client;
|
struct i2c_client *client;
|
||||||
struct iio_trigger *trig;
|
struct iio_trigger *trig;
|
||||||
const struct bma180_part_info *part_info;
|
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_bw_table[] = { 10, 20, 40, 75, 150, 300 }; /* Hz */
|
||||||
static int bma180_scale_table[] = { 1275, 1863, 2452, 3727, 4903, 9709, 19417 };
|
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 bma25x_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_scale_table[] = { 0, 0, 0, 38344, 0, 76590, 0, 0, 153180, 0,
|
||||||
0, 0, 306458 };
|
0, 0, 306458 };
|
||||||
|
|
||||||
static int bma180_get_data_reg(struct bma180_data *data, enum bma180_chan chan)
|
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)
|
if (ret < 0)
|
||||||
return ret;
|
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;
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
ret = bma180_soft_reset(data);
|
ret = bma180_soft_reset(data);
|
||||||
if (ret)
|
if (ret)
|
||||||
@ -355,7 +385,7 @@ err:
|
|||||||
return ret;
|
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);
|
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 */
|
ret = bma180_set_scale(data, 38344); /* 2 G */
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err;
|
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)
|
if (ret)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
@ -394,7 +428,7 @@ err:
|
|||||||
dev_err(&data->client->dev, "failed to disable the chip\n");
|
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))
|
if (bma180_set_new_data_intr_state(data, false))
|
||||||
goto err;
|
goto err;
|
||||||
@ -497,7 +531,7 @@ static int bma180_read_raw(struct iio_dev *indio_dev,
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
case IIO_CHAN_INFO_OFFSET:
|
case IIO_CHAN_INFO_OFFSET:
|
||||||
*val = 48; /* 0 LSB @ 24 degree C */
|
*val = data->part_info->center_temp;
|
||||||
return IIO_VAL_INT;
|
return IIO_VAL_INT;
|
||||||
default:
|
default:
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
@ -627,34 +661,96 @@ static const struct iio_chan_spec bma250_channels[] = {
|
|||||||
IIO_CHAN_SOFT_TIMESTAMP(4),
|
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[] = {
|
static const struct bma180_part_info bma180_part_info[] = {
|
||||||
[BMA180] = {
|
[BMA180] = {
|
||||||
bma180_channels, ARRAY_SIZE(bma180_channels),
|
.chip_id = BMA180_ID_REG_VAL,
|
||||||
bma180_scale_table, ARRAY_SIZE(bma180_scale_table),
|
.channels = bma180_channels,
|
||||||
bma180_bw_table, ARRAY_SIZE(bma180_bw_table),
|
.num_channels = ARRAY_SIZE(bma180_channels),
|
||||||
BMA180_CTRL_REG0, BMA180_RESET_INT,
|
.scale_table = bma180_scale_table,
|
||||||
BMA180_CTRL_REG0, BMA180_SLEEP,
|
.num_scales = ARRAY_SIZE(bma180_scale_table),
|
||||||
BMA180_BW_TCS, BMA180_BW,
|
.bw_table = bma180_bw_table,
|
||||||
BMA180_OFFSET_LSB1, BMA180_RANGE,
|
.num_bw = ARRAY_SIZE(bma180_bw_table),
|
||||||
BMA180_TCO_Z, BMA180_MODE_CONFIG, BMA180_LOW_POWER,
|
.center_temp = 48, /* 0 LSB @ 24 degree C */
|
||||||
BMA180_CTRL_REG3, BMA180_NEW_DATA_INT,
|
.int_reset_reg = BMA180_CTRL_REG0,
|
||||||
BMA180_RESET,
|
.int_reset_mask = BMA180_RESET_INT,
|
||||||
bma180_chip_config,
|
.sleep_reg = BMA180_CTRL_REG0,
|
||||||
bma180_chip_disable,
|
.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] = {
|
||||||
bma250_channels, ARRAY_SIZE(bma250_channels),
|
.chip_id = BMA250_ID_REG_VAL,
|
||||||
bma250_scale_table, ARRAY_SIZE(bma250_scale_table),
|
.channels = bma250_channels,
|
||||||
bma250_bw_table, ARRAY_SIZE(bma250_bw_table),
|
.num_channels = ARRAY_SIZE(bma250_channels),
|
||||||
BMA250_INT_RESET_REG, BMA250_INT_RESET_MASK,
|
.scale_table = bma25x_scale_table,
|
||||||
BMA250_POWER_REG, BMA250_SUSPEND_MASK,
|
.num_scales = ARRAY_SIZE(bma25x_scale_table),
|
||||||
BMA250_BW_REG, BMA250_BW_MASK,
|
.bw_table = bma25x_bw_table,
|
||||||
BMA250_RANGE_REG, BMA250_RANGE_MASK,
|
.num_bw = ARRAY_SIZE(bma25x_bw_table),
|
||||||
BMA250_POWER_REG, BMA250_LOWPOWER_MASK, 1,
|
.center_temp = 48, /* 0 LSB @ 24 degree C */
|
||||||
BMA250_INT_ENABLE_REG, BMA250_DATA_INTEN_MASK,
|
.int_reset_reg = BMA250_INT_RESET_REG,
|
||||||
BMA250_RESET_REG,
|
.int_reset_mask = BMA250_INT_RESET_MASK,
|
||||||
bma250_chip_config,
|
.sleep_reg = BMA250_POWER_REG,
|
||||||
bma250_chip_disable,
|
.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,
|
static int bma180_probe(struct i2c_client *client,
|
||||||
const struct i2c_device_id *id)
|
const struct i2c_device_id *id)
|
||||||
{
|
{
|
||||||
|
struct device *dev = &client->dev;
|
||||||
struct bma180_data *data;
|
struct bma180_data *data;
|
||||||
struct iio_dev *indio_dev;
|
struct iio_dev *indio_dev;
|
||||||
enum chip_ids chip;
|
enum chip_ids chip;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
|
||||||
if (!indio_dev)
|
if (!indio_dev)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
@ -725,22 +822,56 @@ static int bma180_probe(struct i2c_client *client,
|
|||||||
i2c_set_clientdata(client, indio_dev);
|
i2c_set_clientdata(client, indio_dev);
|
||||||
data->client = client;
|
data->client = client;
|
||||||
if (client->dev.of_node)
|
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
|
else
|
||||||
chip = id->driver_data;
|
chip = id->driver_data;
|
||||||
data->part_info = &bma180_part_info[chip];
|
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);
|
&data->orientation);
|
||||||
if (ret)
|
if (ret)
|
||||||
return 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);
|
ret = data->part_info->chip_config(data);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto err_chip_disable;
|
goto err_chip_disable;
|
||||||
|
|
||||||
mutex_init(&data->mutex);
|
mutex_init(&data->mutex);
|
||||||
indio_dev->dev.parent = &client->dev;
|
indio_dev->dev.parent = dev;
|
||||||
indio_dev->channels = data->part_info->channels;
|
indio_dev->channels = data->part_info->channels;
|
||||||
indio_dev->num_channels = data->part_info->num_channels;
|
indio_dev->num_channels = data->part_info->num_channels;
|
||||||
indio_dev->name = id->name;
|
indio_dev->name = id->name;
|
||||||
@ -755,15 +886,15 @@ static int bma180_probe(struct i2c_client *client,
|
|||||||
goto err_chip_disable;
|
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,
|
iio_trigger_generic_data_rdy_poll, IRQF_TRIGGER_RISING,
|
||||||
"bma180_event", data->trig);
|
"bma180_event", data->trig);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(&client->dev, "unable to request IRQ\n");
|
dev_err(dev, "unable to request IRQ\n");
|
||||||
goto err_trigger_free;
|
goto err_trigger_free;
|
||||||
}
|
}
|
||||||
|
|
||||||
data->trig->dev.parent = &client->dev;
|
data->trig->dev.parent = dev;
|
||||||
data->trig->ops = &bma180_trigger_ops;
|
data->trig->ops = &bma180_trigger_ops;
|
||||||
iio_trigger_set_drvdata(data->trig, indio_dev);
|
iio_trigger_set_drvdata(data->trig, indio_dev);
|
||||||
indio_dev->trig = iio_trigger_get(data->trig);
|
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,
|
ret = iio_triggered_buffer_setup(indio_dev, NULL,
|
||||||
bma180_trigger_handler, NULL);
|
bma180_trigger_handler, NULL);
|
||||||
if (ret < 0) {
|
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;
|
goto err_trigger_unregister;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = iio_device_register(indio_dev);
|
ret = iio_device_register(indio_dev);
|
||||||
if (ret < 0) {
|
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;
|
goto err_buffer_cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -797,6 +928,9 @@ err_trigger_free:
|
|||||||
iio_trigger_free(data->trig);
|
iio_trigger_free(data->trig);
|
||||||
err_chip_disable:
|
err_chip_disable:
|
||||||
data->part_info->chip_disable(data);
|
data->part_info->chip_disable(data);
|
||||||
|
regulator_disable(data->vddio_supply);
|
||||||
|
err_disable_vdd:
|
||||||
|
regulator_disable(data->vdd_supply);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -816,6 +950,8 @@ static int bma180_remove(struct i2c_client *client)
|
|||||||
mutex_lock(&data->mutex);
|
mutex_lock(&data->mutex);
|
||||||
data->part_info->chip_disable(data);
|
data->part_info->chip_disable(data);
|
||||||
mutex_unlock(&data->mutex);
|
mutex_unlock(&data->mutex);
|
||||||
|
regulator_disable(data->vddio_supply);
|
||||||
|
regulator_disable(data->vdd_supply);
|
||||||
|
|
||||||
return 0;
|
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[] = {
|
static const struct i2c_device_id bma180_ids[] = {
|
||||||
{ "bma180", BMA180 },
|
{ "bma180", BMA180 },
|
||||||
{ "bma250", BMA250 },
|
{ "bma250", BMA250 },
|
||||||
|
{ "bma254", BMA254 },
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -870,6 +1007,10 @@ static const struct of_device_id bma180_of_match[] = {
|
|||||||
.compatible = "bosch,bma250",
|
.compatible = "bosch,bma250",
|
||||||
.data = (void *)BMA250
|
.data = (void *)BMA250
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.compatible = "bosch,bma254",
|
||||||
|
.data = (void *)BMA254
|
||||||
|
},
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(of, bma180_of_match);
|
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("Kravchenko Oleksandr <x0199363@ti.com>");
|
||||||
MODULE_AUTHOR("Texas Instruments, Inc.");
|
MODULE_AUTHOR("Texas Instruments, Inc.");
|
||||||
MODULE_DESCRIPTION("Bosch BMA180/BMA250 triaxial acceleration sensor");
|
MODULE_DESCRIPTION("Bosch BMA180/BMA25x triaxial acceleration sensor");
|
||||||
MODULE_LICENSE("GPL");
|
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 i2c_client *client;
|
||||||
struct iio_trigger *dready_trig;
|
struct iio_trigger *dready_trig;
|
||||||
struct iio_trigger *motion_trig;
|
struct iio_trigger *motion_trig;
|
||||||
|
struct iio_mount_matrix orientation;
|
||||||
struct mutex mutex;
|
struct mutex mutex;
|
||||||
s16 buffer[8];
|
s16 buffer[8];
|
||||||
u8 odr_bits;
|
u8 odr_bits;
|
||||||
@ -983,6 +984,20 @@ static const struct iio_event_spec kxcjk1013_event = {
|
|||||||
BIT(IIO_EV_INFO_PERIOD)
|
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) { \
|
#define KXCJK1013_CHANNEL(_axis) { \
|
||||||
.type = IIO_ACCEL, \
|
.type = IIO_ACCEL, \
|
||||||
.modified = 1, \
|
.modified = 1, \
|
||||||
@ -999,6 +1014,7 @@ static const struct iio_event_spec kxcjk1013_event = {
|
|||||||
.endianness = IIO_LE, \
|
.endianness = IIO_LE, \
|
||||||
}, \
|
}, \
|
||||||
.event_spec = &kxcjk1013_event, \
|
.event_spec = &kxcjk1013_event, \
|
||||||
|
.ext_info = kxcjk1013_ext_info, \
|
||||||
.num_event_specs = 1 \
|
.num_event_specs = 1 \
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1267,11 +1283,18 @@ static int kxcjk1013_probe(struct i2c_client *client,
|
|||||||
data->client = client;
|
data->client = client;
|
||||||
|
|
||||||
pdata = dev_get_platdata(&client->dev);
|
pdata = dev_get_platdata(&client->dev);
|
||||||
if (pdata)
|
if (pdata) {
|
||||||
data->active_high_intr = pdata->active_high_intr;
|
data->active_high_intr = pdata->active_high_intr;
|
||||||
else
|
data->orientation = pdata->orientation;
|
||||||
|
} else {
|
||||||
data->active_high_intr = true; /* default polarity */
|
data->active_high_intr = true; /* default polarity */
|
||||||
|
|
||||||
|
ret = iio_read_mount_matrix(&client->dev, "mount-matrix",
|
||||||
|
&data->orientation);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
if (id) {
|
if (id) {
|
||||||
data->chipset = (enum kx_chipset)(id->driver_data);
|
data->chipset = (enum kx_chipset)(id->driver_data);
|
||||||
name = id->name;
|
name = id->name;
|
||||||
|
@ -64,7 +64,7 @@ enum st_accel_type {
|
|||||||
* struct st_sensors_platform_data - default accel platform data
|
* struct st_sensors_platform_data - default accel platform data
|
||||||
* @drdy_int_pin: default accel DRDY is available on INT1 pin.
|
* @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,
|
.drdy_int_pin = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -18,7 +18,6 @@
|
|||||||
#include <linux/iio/common/st_sensors_i2c.h>
|
#include <linux/iio/common/st_sensors_i2c.h>
|
||||||
#include "st_accel.h"
|
#include "st_accel.h"
|
||||||
|
|
||||||
#ifdef CONFIG_OF
|
|
||||||
static const struct of_device_id st_accel_of_match[] = {
|
static const struct of_device_id st_accel_of_match[] = {
|
||||||
{
|
{
|
||||||
/* An older compatible */
|
/* 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);
|
MODULE_DEVICE_TABLE(of, st_accel_of_match);
|
||||||
#else
|
|
||||||
#define st_accel_of_match NULL
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef CONFIG_ACPI
|
#ifdef CONFIG_ACPI
|
||||||
static const struct acpi_device_id st_accel_acpi_match[] = {
|
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);
|
MODULE_DEVICE_TABLE(acpi, st_accel_acpi_match);
|
||||||
#else
|
|
||||||
#define st_accel_acpi_match NULL
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static const struct i2c_device_id st_accel_id_table[] = {
|
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 = {
|
static struct i2c_driver st_accel_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "st-accel-i2c",
|
.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),
|
.acpi_match_table = ACPI_PTR(st_accel_acpi_match),
|
||||||
},
|
},
|
||||||
.probe_new = st_accel_i2c_probe,
|
.probe_new = st_accel_i2c_probe,
|
||||||
|
@ -17,7 +17,6 @@
|
|||||||
#include <linux/iio/common/st_sensors_spi.h>
|
#include <linux/iio/common/st_sensors_spi.h>
|
||||||
#include "st_accel.h"
|
#include "st_accel.h"
|
||||||
|
|
||||||
#ifdef CONFIG_OF
|
|
||||||
/*
|
/*
|
||||||
* For new single-chip sensors use <device_name> as compatible string.
|
* For new single-chip sensors use <device_name> as compatible string.
|
||||||
* For old single-chip devices keep <device_name>-accel to maintain
|
* 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);
|
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)
|
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;
|
struct iio_dev *indio_dev;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
st_sensors_of_name_probe(&spi->dev, st_accel_of_match,
|
st_sensors_dev_name_probe(&spi->dev, spi->modalias, sizeof(spi->modalias));
|
||||||
spi->modalias, sizeof(spi->modalias));
|
|
||||||
|
|
||||||
settings = st_accel_get_settings(spi->modalias);
|
settings = st_accel_get_settings(spi->modalias);
|
||||||
if (!settings) {
|
if (!settings) {
|
||||||
@ -166,7 +161,7 @@ MODULE_DEVICE_TABLE(spi, st_accel_id_table);
|
|||||||
static struct spi_driver st_accel_driver = {
|
static struct spi_driver st_accel_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "st-accel-spi",
|
.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,
|
.probe = st_accel_spi_probe,
|
||||||
.remove = st_accel_spi_remove,
|
.remove = st_accel_spi_remove,
|
||||||
|
@ -21,6 +21,13 @@ config AD_SIGMA_DELTA
|
|||||||
select IIO_BUFFER
|
select IIO_BUFFER
|
||||||
select IIO_TRIGGERED_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
|
config AD7124
|
||||||
tristate "Analog Devices AD7124 and similar sigma-delta ADCs driver"
|
tristate "Analog Devices AD7124 and similar sigma-delta ADCs driver"
|
||||||
depends on SPI_MASTER
|
depends on SPI_MASTER
|
||||||
@ -523,6 +530,16 @@ config LTC2485
|
|||||||
To compile this driver as a module, choose M here: the module will be
|
To compile this driver as a module, choose M here: the module will be
|
||||||
called ltc2485.
|
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
|
config LTC2497
|
||||||
tristate "Linear Technology LTC2497 ADC driver"
|
tristate "Linear Technology LTC2497 ADC driver"
|
||||||
depends on I2C
|
depends on I2C
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
# When adding new entries keep the list in alphabetical order
|
# When adding new entries keep the list in alphabetical order
|
||||||
obj-$(CONFIG_AB8500_GPADC) += ab8500-gpadc.o
|
obj-$(CONFIG_AB8500_GPADC) += ab8500-gpadc.o
|
||||||
obj-$(CONFIG_AD_SIGMA_DELTA) += ad_sigma_delta.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_AD7124) += ad7124.o
|
||||||
obj-$(CONFIG_AD7266) += ad7266.o
|
obj-$(CONFIG_AD7266) += ad7266.o
|
||||||
obj-$(CONFIG_AD7291) += ad7291.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_LPC32XX_ADC) += lpc32xx_adc.o
|
||||||
obj-$(CONFIG_LTC2471) += ltc2471.o
|
obj-$(CONFIG_LTC2471) += ltc2471.o
|
||||||
obj-$(CONFIG_LTC2485) += ltc2485.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_MAX1027) += max1027.o
|
||||||
obj-$(CONFIG_MAX11100) += max11100.o
|
obj-$(CONFIG_MAX11100) += max11100.o
|
||||||
obj-$(CONFIG_MAX1118) += max1118.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/delay.h>
|
||||||
#include <linux/device.h>
|
#include <linux/device.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/regulator/consumer.h>
|
#include <linux/regulator/consumer.h>
|
||||||
@ -224,6 +225,7 @@ static const struct ad_sigma_delta_info ad7124_sigma_delta_info = {
|
|||||||
.addr_shift = 0,
|
.addr_shift = 0,
|
||||||
.read_mask = BIT(6),
|
.read_mask = BIT(6),
|
||||||
.data_reg = AD7124_DATA,
|
.data_reg = AD7124_DATA,
|
||||||
|
.irq_flags = IRQF_TRIGGER_FALLING,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int ad7124_set_channel_odr(struct ad7124_state *st,
|
static int ad7124_set_channel_odr(struct ad7124_state *st,
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
#include <linux/spi/spi.h>
|
#include <linux/spi/spi.h>
|
||||||
#include <linux/regulator/consumer.h>
|
#include <linux/regulator/consumer.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/gpio.h>
|
#include <linux/gpio/consumer.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
@ -34,7 +34,7 @@ struct ad7266_state {
|
|||||||
enum ad7266_range range;
|
enum ad7266_range range;
|
||||||
enum ad7266_mode mode;
|
enum ad7266_mode mode;
|
||||||
bool fixed_addr;
|
bool fixed_addr;
|
||||||
struct gpio gpios[3];
|
struct gpio_desc *gpios[3];
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* DMA (thus cache coherency maintenance) requires the
|
* 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)
|
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,
|
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[] = {
|
static const char * const ad7266_gpio_labels[] = {
|
||||||
"AD0", "AD1", "AD2",
|
"ad0", "ad1", "ad2",
|
||||||
};
|
};
|
||||||
|
|
||||||
static int ad7266_probe(struct spi_device *spi)
|
static int ad7266_probe(struct spi_device *spi)
|
||||||
@ -419,14 +419,14 @@ static int ad7266_probe(struct spi_device *spi)
|
|||||||
|
|
||||||
if (!st->fixed_addr) {
|
if (!st->fixed_addr) {
|
||||||
for (i = 0; i < ARRAY_SIZE(st->gpios); ++i) {
|
for (i = 0; i < ARRAY_SIZE(st->gpios); ++i) {
|
||||||
st->gpios[i].gpio = pdata->addr_gpios[i];
|
st->gpios[i] = devm_gpiod_get(&spi->dev,
|
||||||
st->gpios[i].flags = GPIOF_OUT_INIT_LOW;
|
ad7266_gpio_labels[i],
|
||||||
st->gpios[i].label = 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 {
|
} else {
|
||||||
st->fixed_addr = true;
|
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,
|
ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
|
||||||
&ad7266_trigger_handler, &iio_triggered_buffer_setup_ops);
|
&ad7266_trigger_handler, &iio_triggered_buffer_setup_ops);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto error_free_gpios;
|
goto error_disable_reg;
|
||||||
|
|
||||||
ret = iio_device_register(indio_dev);
|
ret = iio_device_register(indio_dev);
|
||||||
if (ret)
|
if (ret)
|
||||||
@ -475,9 +475,6 @@ static int ad7266_probe(struct spi_device *spi)
|
|||||||
|
|
||||||
error_buffer_cleanup:
|
error_buffer_cleanup:
|
||||||
iio_triggered_buffer_cleanup(indio_dev);
|
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:
|
error_disable_reg:
|
||||||
if (!IS_ERR(st->reg))
|
if (!IS_ERR(st->reg))
|
||||||
regulator_disable(st->reg);
|
regulator_disable(st->reg);
|
||||||
@ -492,8 +489,6 @@ static int ad7266_remove(struct spi_device *spi)
|
|||||||
|
|
||||||
iio_device_unregister(indio_dev);
|
iio_device_unregister(indio_dev);
|
||||||
iio_triggered_buffer_cleanup(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))
|
if (!IS_ERR(st->reg))
|
||||||
regulator_disable(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,
|
.set_mode = ad7780_set_mode,
|
||||||
.postprocess_sample = ad7780_postprocess_sample,
|
.postprocess_sample = ad7780_postprocess_sample,
|
||||||
.has_registers = false,
|
.has_registers = false,
|
||||||
|
.irq_flags = IRQF_TRIGGER_LOW,
|
||||||
};
|
};
|
||||||
|
|
||||||
#define AD7780_CHANNEL(bits, wordsize) \
|
#define AD7780_CHANNEL(bits, wordsize) \
|
||||||
|
@ -205,6 +205,7 @@ static const struct ad_sigma_delta_info ad7791_sigma_delta_info = {
|
|||||||
.has_registers = true,
|
.has_registers = true,
|
||||||
.addr_shift = 4,
|
.addr_shift = 4,
|
||||||
.read_mask = BIT(3),
|
.read_mask = BIT(3),
|
||||||
|
.irq_flags = IRQF_TRIGGER_LOW,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int ad7791_read_raw(struct iio_dev *indio_dev,
|
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,
|
.has_registers = true,
|
||||||
.addr_shift = 3,
|
.addr_shift = 3,
|
||||||
.read_mask = BIT(6),
|
.read_mask = BIT(6),
|
||||||
|
.irq_flags = IRQF_TRIGGER_LOW,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct ad_sd_calib_data ad7793_calib_arr[6] = {
|
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
|
* struct ad7887_chip_info - chip specifc information
|
||||||
* @int_vref_mv: the internal reference voltage
|
* @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 {
|
struct ad7887_chip_info {
|
||||||
u16 int_vref_mv;
|
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 {
|
struct ad7887_state {
|
||||||
@ -183,45 +189,43 @@ static int ad7887_read_raw(struct iio_dev *indio_dev,
|
|||||||
return -EINVAL;
|
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[] = {
|
static const struct ad7887_chip_info ad7887_chip_info_tbl[] = {
|
||||||
/*
|
/*
|
||||||
* More devices added in future
|
* More devices added in future
|
||||||
*/
|
*/
|
||||||
[ID_AD7887] = {
|
[ID_AD7887] = {
|
||||||
.channel[0] = {
|
.channels = ad7887_channels,
|
||||||
.type = IIO_VOLTAGE,
|
.num_channels = ARRAY_SIZE(ad7887_channels),
|
||||||
.indexed = 1,
|
.dual_channels = ad7887_dual_channels,
|
||||||
.channel = 1,
|
.num_dual_channels = ARRAY_SIZE(ad7887_dual_channels),
|
||||||
.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),
|
|
||||||
.int_vref_mv = 2500,
|
.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_init(&st->msg[AD7887_CH1]);
|
||||||
spi_message_add_tail(&st->xfer[3], &st->msg[AD7887_CH1]);
|
spi_message_add_tail(&st->xfer[3], &st->msg[AD7887_CH1]);
|
||||||
|
|
||||||
indio_dev->channels = st->chip_info->channel;
|
indio_dev->channels = st->chip_info->dual_channels;
|
||||||
indio_dev->num_channels = 3;
|
indio_dev->num_channels = st->chip_info->num_dual_channels;
|
||||||
} else {
|
} else {
|
||||||
indio_dev->channels = &st->chip_info->channel[1];
|
indio_dev->channels = st->chip_info->channels;
|
||||||
indio_dev->num_channels = 2;
|
indio_dev->num_channels = st->chip_info->num_channels;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
|
ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-only
|
// 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 2011 Analog Devices Inc (from AD7923 Driver)
|
||||||
* Copyright 2012 CS Systemes d'Information
|
* Copyright 2012 CS Systemes d'Information
|
||||||
@ -29,15 +29,10 @@
|
|||||||
#define AD7923_PM_MODE_AS (1) /* auto shutdown */
|
#define AD7923_PM_MODE_AS (1) /* auto shutdown */
|
||||||
#define AD7923_PM_MODE_FS (2) /* full shutdown */
|
#define AD7923_PM_MODE_FS (2) /* full shutdown */
|
||||||
#define AD7923_PM_MODE_OPS (3) /* normal operation */
|
#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_OFF (0) /* no sequence fonction */
|
||||||
#define AD7923_SEQUENCE_PROTECT (2) /* no interrupt write cycle */
|
#define AD7923_SEQUENCE_PROTECT (2) /* no interrupt write cycle */
|
||||||
#define AD7923_SEQUENCE_ON (3) /* continuous sequence */
|
#define AD7923_SEQUENCE_ON (3) /* continuous sequence */
|
||||||
|
|
||||||
#define AD7923_MAX_CHAN 4
|
|
||||||
|
|
||||||
#define AD7923_PM_MODE_WRITE(mode) ((mode) << 4) /* write mode */
|
#define AD7923_PM_MODE_WRITE(mode) ((mode) << 4) /* write mode */
|
||||||
#define AD7923_CHANNEL_WRITE(channel) ((channel) << 6) /* write channel */
|
#define AD7923_CHANNEL_WRITE(channel) ((channel) << 6) /* write channel */
|
||||||
@ -78,6 +73,9 @@ enum ad7923_id {
|
|||||||
AD7904,
|
AD7904,
|
||||||
AD7914,
|
AD7914,
|
||||||
AD7924,
|
AD7924,
|
||||||
|
AD7908,
|
||||||
|
AD7918,
|
||||||
|
AD7928
|
||||||
};
|
};
|
||||||
|
|
||||||
#define AD7923_V_CHAN(index, bits) \
|
#define AD7923_V_CHAN(index, bits) \
|
||||||
@ -106,9 +104,25 @@ const struct iio_chan_spec name ## _channels[] = { \
|
|||||||
IIO_CHAN_SOFT_TIMESTAMP(4), \
|
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(ad7904, 8);
|
||||||
static DECLARE_AD7923_CHANNELS(ad7914, 10);
|
static DECLARE_AD7923_CHANNELS(ad7914, 10);
|
||||||
static DECLARE_AD7923_CHANNELS(ad7924, 12);
|
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[] = {
|
static const struct ad7923_chip_info ad7923_chip_info[] = {
|
||||||
[AD7904] = {
|
[AD7904] = {
|
||||||
@ -123,6 +137,18 @@ static const struct ad7923_chip_info ad7923_chip_info[] = {
|
|||||||
.channels = ad7924_channels,
|
.channels = ad7924_channels,
|
||||||
.num_channels = ARRAY_SIZE(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;
|
int i, cmd, len;
|
||||||
|
|
||||||
len = 0;
|
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) |
|
cmd = AD7923_WRITE_CR | AD7923_CHANNEL_WRITE(i) |
|
||||||
AD7923_SEQUENCE_WRITE(AD7923_SEQUENCE_OFF) |
|
AD7923_SEQUENCE_WRITE(AD7923_SEQUENCE_OFF) |
|
||||||
st->settings;
|
st->settings;
|
||||||
@ -188,7 +218,7 @@ done:
|
|||||||
return IRQ_HANDLED;
|
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;
|
int ret, cmd;
|
||||||
|
|
||||||
@ -348,13 +378,29 @@ static const struct spi_device_id ad7923_id[] = {
|
|||||||
{"ad7914", AD7914},
|
{"ad7914", AD7914},
|
||||||
{"ad7923", AD7924},
|
{"ad7923", AD7924},
|
||||||
{"ad7924", AD7924},
|
{"ad7924", AD7924},
|
||||||
|
{"ad7908", AD7908},
|
||||||
|
{"ad7918", AD7918},
|
||||||
|
{"ad7928", AD7928},
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(spi, ad7923_id);
|
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 = {
|
static struct spi_driver ad7923_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "ad7923",
|
.name = "ad7923",
|
||||||
|
.of_match_table = ad7923_of_match,
|
||||||
},
|
},
|
||||||
.probe = ad7923_probe,
|
.probe = ad7923_probe,
|
||||||
.remove = ad7923_remove,
|
.remove = ad7923_remove,
|
||||||
@ -364,5 +410,5 @@ module_spi_driver(ad7923_driver);
|
|||||||
|
|
||||||
MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
|
MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
|
||||||
MODULE_AUTHOR("Patrick Vasseur <patrick.vasseur@c-s.fr>");
|
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");
|
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
|
* 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->channels = st->chip_config->channel;
|
||||||
indio_dev->num_channels = chip_info->num_channels;
|
indio_dev->num_channels = chip_info->num_channels;
|
||||||
|
|
||||||
ret = ad799x_write_config(st, st->chip_config->default_config);
|
ret = ad799x_update_config(st, st->chip_config->default_config);
|
||||||
if (ret < 0)
|
if (ret)
|
||||||
goto error_disable_vref;
|
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,
|
ret = iio_triggered_buffer_setup(indio_dev, NULL,
|
||||||
&ad799x_trigger_handler, NULL);
|
&ad799x_trigger_handler, NULL);
|
||||||
@ -864,6 +875,48 @@ static int ad799x_remove(struct i2c_client *client)
|
|||||||
return 0;
|
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[] = {
|
static const struct i2c_device_id ad799x_id[] = {
|
||||||
{ "ad7991", ad7991 },
|
{ "ad7991", ad7991 },
|
||||||
{ "ad7995", ad7995 },
|
{ "ad7995", ad7995 },
|
||||||
@ -881,6 +934,7 @@ MODULE_DEVICE_TABLE(i2c, ad799x_id);
|
|||||||
static struct i2c_driver ad799x_driver = {
|
static struct i2c_driver ad799x_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "ad799x",
|
.name = "ad799x",
|
||||||
|
.pm = &ad799x_pm_ops,
|
||||||
},
|
},
|
||||||
.probe = ad799x_probe,
|
.probe = ad799x_probe,
|
||||||
.remove = ad799x_remove,
|
.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,
|
ret = request_irq(sigma_delta->spi->irq,
|
||||||
ad_sd_data_rdy_trig_poll,
|
ad_sd_data_rdy_trig_poll,
|
||||||
IRQF_TRIGGER_LOW,
|
sigma_delta->info->irq_flags,
|
||||||
indio_dev->name,
|
indio_dev->name,
|
||||||
sigma_delta);
|
sigma_delta);
|
||||||
if (ret)
|
if (ret)
|
||||||
|
@ -1444,10 +1444,10 @@ static void at91_adc_dma_init(struct platform_device *pdev)
|
|||||||
if (st->dma_st.dma_chan)
|
if (st->dma_st.dma_chan)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
st->dma_st.dma_chan = dma_request_slave_channel(&pdev->dev, "rx");
|
st->dma_st.dma_chan = dma_request_chan(&pdev->dev, "rx");
|
||||||
|
if (IS_ERR(st->dma_st.dma_chan)) {
|
||||||
if (!st->dma_st.dma_chan) {
|
|
||||||
dev_info(&pdev->dev, "can't get DMA channel\n");
|
dev_info(&pdev->dev, "can't get DMA channel\n");
|
||||||
|
st->dma_st.dma_chan = NULL;
|
||||||
goto dma_exit;
|
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
|
* Datasheet: http://cds.linear.com/docs/en/datasheet/2497fd.pdf
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/delay.h>
|
|
||||||
#include <linux/i2c.h>
|
#include <linux/i2c.h>
|
||||||
#include <linux/iio/iio.h>
|
#include <linux/iio/iio.h>
|
||||||
#include <linux/iio/driver.h>
|
#include <linux/iio/driver.h>
|
||||||
#include <linux/iio/sysfs.h>
|
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/regulator/consumer.h>
|
|
||||||
|
|
||||||
#define LTC2497_ENABLE 0xA0
|
#include "ltc2497.h"
|
||||||
#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
|
|
||||||
|
|
||||||
struct ltc2497_st {
|
struct ltc2497_driverdata {
|
||||||
|
/* this must be the first member */
|
||||||
|
struct ltc2497core_driverdata common_ddata;
|
||||||
struct i2c_client *client;
|
struct i2c_client *client;
|
||||||
struct regulator *ref;
|
|
||||||
ktime_t time_prev;
|
|
||||||
u8 addr_prev;
|
|
||||||
/*
|
/*
|
||||||
* DMA (thus cache coherency maintenance) requires the
|
* DMA (thus cache coherency maintenance) requires the
|
||||||
* transfer buffers to live in their own cache lines.
|
* transfer buffers to live in their own cache lines.
|
||||||
@ -35,232 +26,59 @@ struct ltc2497_st {
|
|||||||
__be32 buf ____cacheline_aligned;
|
__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;
|
struct ltc2497_driverdata *st =
|
||||||
|
container_of(ddata, struct ltc2497_driverdata, common_ddata);
|
||||||
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;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = ltc2497_wait_conv(st);
|
if (val) {
|
||||||
if (ret < 0)
|
ret = i2c_master_recv(st->client, (char *)&st->buf, 3);
|
||||||
return ret;
|
if (ret < 0) {
|
||||||
|
dev_err(&st->client->dev, "i2c_master_recv failed\n");
|
||||||
if (ret || st->addr_prev != address) {
|
|
||||||
ret = i2c_smbus_write_byte(st->client,
|
|
||||||
LTC2497_ENABLE | address);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
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,
|
*val = (be32_to_cpu(st->buf) >> 14) - (1 << 17);
|
||||||
* and finally convert from offset binary to signed integer
|
}
|
||||||
*/
|
|
||||||
*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;
|
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,
|
static int ltc2497_probe(struct i2c_client *client,
|
||||||
const struct i2c_device_id *id)
|
const struct i2c_device_id *id)
|
||||||
{
|
{
|
||||||
struct iio_dev *indio_dev;
|
struct iio_dev *indio_dev;
|
||||||
struct ltc2497_st *st;
|
struct ltc2497_driverdata *st;
|
||||||
struct iio_map *plat_data;
|
struct device *dev = &client->dev;
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C |
|
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C |
|
||||||
I2C_FUNC_SMBUS_WRITE_BYTE))
|
I2C_FUNC_SMBUS_WRITE_BYTE))
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*st));
|
indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
|
||||||
if (!indio_dev)
|
if (!indio_dev)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
st = iio_priv(indio_dev);
|
st = iio_priv(indio_dev);
|
||||||
i2c_set_clientdata(client, indio_dev);
|
i2c_set_clientdata(client, indio_dev);
|
||||||
st->client = client;
|
st->client = client;
|
||||||
|
st->common_ddata.result_and_measure = ltc2497_result_and_measure;
|
||||||
|
|
||||||
indio_dev->dev.parent = &client->dev;
|
return ltc2497core_probe(dev, indio_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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ltc2497_remove(struct i2c_client *client)
|
static int ltc2497_remove(struct i2c_client *client)
|
||||||
{
|
{
|
||||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||||
struct ltc2497_st *st = iio_priv(indio_dev);
|
|
||||||
|
|
||||||
iio_map_array_unregister(indio_dev);
|
ltc2497core_remove(indio_dev);
|
||||||
iio_device_unregister(indio_dev);
|
|
||||||
regulator_disable(st->ref);
|
|
||||||
|
|
||||||
return 0;
|
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
|
* where data shall be read from
|
||||||
*/
|
*/
|
||||||
static const unsigned int max9611_mux_conf[][2] = {
|
static const unsigned int max9611_mux_conf[][2] = {
|
||||||
/* CONF_SENSE_1x */
|
[CONF_SENSE_1x] = { MAX9611_MUX_SENSE_1x, MAX9611_REG_CSA_DATA },
|
||||||
{ MAX9611_MUX_SENSE_1x, MAX9611_REG_CSA_DATA },
|
[CONF_SENSE_4x] = { MAX9611_MUX_SENSE_4x, MAX9611_REG_CSA_DATA },
|
||||||
/* CONF_SENSE_4x */
|
[CONF_SENSE_8x] = { MAX9611_MUX_SENSE_8x, MAX9611_REG_CSA_DATA },
|
||||||
{ MAX9611_MUX_SENSE_4x, MAX9611_REG_CSA_DATA },
|
[CONF_IN_VOLT] = { MAX9611_INPUT_VOLT, MAX9611_REG_RS_DATA },
|
||||||
/* CONF_SENSE_8x */
|
[CONF_TEMP] = { MAX9611_MUX_TEMP, MAX9611_REG_TEMP_DATA },
|
||||||
{ 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 {
|
enum max9611_csa_gain {
|
||||||
CSA_GAIN_1x,
|
CSA_GAIN_1x = CONF_SENSE_1x,
|
||||||
CSA_GAIN_4x,
|
CSA_GAIN_4x = CONF_SENSE_4x,
|
||||||
CSA_GAIN_8x,
|
CSA_GAIN_8x = CONF_SENSE_8x,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum max9611_csa_gain_params {
|
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.
|
* value; use this structure to retrieve the correct LSB and offset values.
|
||||||
*/
|
*/
|
||||||
static const unsigned int max9611_gain_conf[][2] = {
|
static const unsigned int max9611_gain_conf[][2] = {
|
||||||
{ /* [0] CSA_GAIN_1x */
|
[CSA_GAIN_1x] = { MAX9611_CSA_1X_LSB_nV, MAX9611_CSA_1X_OFFS_RAW, },
|
||||||
MAX9611_CSA_1X_LSB_nV,
|
[CSA_GAIN_4x] = { MAX9611_CSA_4X_LSB_nV, MAX9611_CSA_4X_OFFS_RAW, },
|
||||||
MAX9611_CSA_1X_OFFS_RAW,
|
[CSA_GAIN_8x] = { MAX9611_CSA_8X_LSB_nV, MAX9611_CSA_8X_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,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
enum max9611_chan_addrs {
|
enum max9611_chan_addrs {
|
||||||
|
@ -280,21 +280,21 @@ out:
|
|||||||
static const struct stm32_adc_common_regs stm32f4_adc_common_regs = {
|
static const struct stm32_adc_common_regs stm32f4_adc_common_regs = {
|
||||||
.csr = STM32F4_ADC_CSR,
|
.csr = STM32F4_ADC_CSR,
|
||||||
.ccr = STM32F4_ADC_CCR,
|
.ccr = STM32F4_ADC_CCR,
|
||||||
.eoc1_msk = STM32F4_EOC1,
|
.eoc1_msk = STM32F4_EOC1 | STM32F4_OVR1,
|
||||||
.eoc2_msk = STM32F4_EOC2,
|
.eoc2_msk = STM32F4_EOC2 | STM32F4_OVR2,
|
||||||
.eoc3_msk = STM32F4_EOC3,
|
.eoc3_msk = STM32F4_EOC3 | STM32F4_OVR3,
|
||||||
.ier = STM32F4_ADC_CR1,
|
.ier = STM32F4_ADC_CR1,
|
||||||
.eocie_msk = STM32F4_EOCIE,
|
.eocie_msk = STM32F4_EOCIE | STM32F4_OVRIE,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* STM32H7 common registers definitions */
|
/* STM32H7 common registers definitions */
|
||||||
static const struct stm32_adc_common_regs stm32h7_adc_common_regs = {
|
static const struct stm32_adc_common_regs stm32h7_adc_common_regs = {
|
||||||
.csr = STM32H7_ADC_CSR,
|
.csr = STM32H7_ADC_CSR,
|
||||||
.ccr = STM32H7_ADC_CCR,
|
.ccr = STM32H7_ADC_CCR,
|
||||||
.eoc1_msk = STM32H7_EOC_MST,
|
.eoc1_msk = STM32H7_EOC_MST | STM32H7_OVR_MST,
|
||||||
.eoc2_msk = STM32H7_EOC_SLV,
|
.eoc2_msk = STM32H7_EOC_SLV | STM32H7_OVR_SLV,
|
||||||
.ier = STM32H7_ADC_IER,
|
.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] = {
|
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");
|
priv->vref = devm_regulator_get(&pdev->dev, "vref");
|
||||||
if (IS_ERR(priv->vref)) {
|
if (IS_ERR(priv->vref)) {
|
||||||
ret = PTR_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;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -696,7 +697,8 @@ static int stm32_adc_probe(struct platform_device *pdev)
|
|||||||
if (IS_ERR(priv->aclk)) {
|
if (IS_ERR(priv->aclk)) {
|
||||||
ret = PTR_ERR(priv->aclk);
|
ret = PTR_ERR(priv->aclk);
|
||||||
if (ret != -ENOENT) {
|
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;
|
return ret;
|
||||||
}
|
}
|
||||||
priv->aclk = NULL;
|
priv->aclk = NULL;
|
||||||
@ -706,7 +708,8 @@ static int stm32_adc_probe(struct platform_device *pdev)
|
|||||||
if (IS_ERR(priv->bclk)) {
|
if (IS_ERR(priv->bclk)) {
|
||||||
ret = PTR_ERR(priv->bclk);
|
ret = PTR_ERR(priv->bclk);
|
||||||
if (ret != -ENOENT) {
|
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;
|
return ret;
|
||||||
}
|
}
|
||||||
priv->bclk = NULL;
|
priv->bclk = NULL;
|
||||||
|
@ -51,10 +51,12 @@
|
|||||||
#define STM32F4_ADC_CCR (STM32_ADCX_COMN_OFFSET + 0x04)
|
#define STM32F4_ADC_CCR (STM32_ADCX_COMN_OFFSET + 0x04)
|
||||||
|
|
||||||
/* STM32F4_ADC_SR - bit fields */
|
/* STM32F4_ADC_SR - bit fields */
|
||||||
|
#define STM32F4_OVR BIT(5)
|
||||||
#define STM32F4_STRT BIT(4)
|
#define STM32F4_STRT BIT(4)
|
||||||
#define STM32F4_EOC BIT(1)
|
#define STM32F4_EOC BIT(1)
|
||||||
|
|
||||||
/* STM32F4_ADC_CR1 - bit fields */
|
/* STM32F4_ADC_CR1 - bit fields */
|
||||||
|
#define STM32F4_OVRIE BIT(26)
|
||||||
#define STM32F4_RES_SHIFT 24
|
#define STM32F4_RES_SHIFT 24
|
||||||
#define STM32F4_RES_MASK GENMASK(25, 24)
|
#define STM32F4_RES_MASK GENMASK(25, 24)
|
||||||
#define STM32F4_SCAN BIT(8)
|
#define STM32F4_SCAN BIT(8)
|
||||||
@ -72,8 +74,11 @@
|
|||||||
#define STM32F4_ADON BIT(0)
|
#define STM32F4_ADON BIT(0)
|
||||||
|
|
||||||
/* STM32F4_ADC_CSR - bit fields */
|
/* STM32F4_ADC_CSR - bit fields */
|
||||||
|
#define STM32F4_OVR3 BIT(21)
|
||||||
#define STM32F4_EOC3 BIT(17)
|
#define STM32F4_EOC3 BIT(17)
|
||||||
|
#define STM32F4_OVR2 BIT(13)
|
||||||
#define STM32F4_EOC2 BIT(9)
|
#define STM32F4_EOC2 BIT(9)
|
||||||
|
#define STM32F4_OVR1 BIT(5)
|
||||||
#define STM32F4_EOC1 BIT(1)
|
#define STM32F4_EOC1 BIT(1)
|
||||||
|
|
||||||
/* STM32F4_ADC_CCR - bit fields */
|
/* STM32F4_ADC_CCR - bit fields */
|
||||||
@ -103,10 +108,12 @@
|
|||||||
|
|
||||||
/* STM32H7_ADC_ISR - bit fields */
|
/* STM32H7_ADC_ISR - bit fields */
|
||||||
#define STM32MP1_VREGREADY BIT(12)
|
#define STM32MP1_VREGREADY BIT(12)
|
||||||
|
#define STM32H7_OVR BIT(4)
|
||||||
#define STM32H7_EOC BIT(2)
|
#define STM32H7_EOC BIT(2)
|
||||||
#define STM32H7_ADRDY BIT(0)
|
#define STM32H7_ADRDY BIT(0)
|
||||||
|
|
||||||
/* STM32H7_ADC_IER - bit fields */
|
/* STM32H7_ADC_IER - bit fields */
|
||||||
|
#define STM32H7_OVRIE STM32H7_OVR
|
||||||
#define STM32H7_EOCIE STM32H7_EOC
|
#define STM32H7_EOCIE STM32H7_EOC
|
||||||
|
|
||||||
/* STM32H7_ADC_CR - bit fields */
|
/* STM32H7_ADC_CR - bit fields */
|
||||||
@ -155,7 +162,9 @@ enum stm32h7_adc_dmngt {
|
|||||||
#define STM32H7_LINCALFACT_MASK GENMASK(29, 0)
|
#define STM32H7_LINCALFACT_MASK GENMASK(29, 0)
|
||||||
|
|
||||||
/* STM32H7_ADC_CSR - bit fields */
|
/* STM32H7_ADC_CSR - bit fields */
|
||||||
|
#define STM32H7_OVR_SLV BIT(20)
|
||||||
#define STM32H7_EOC_SLV BIT(18)
|
#define STM32H7_EOC_SLV BIT(18)
|
||||||
|
#define STM32H7_OVR_MST BIT(4)
|
||||||
#define STM32H7_EOC_MST BIT(2)
|
#define STM32H7_EOC_MST BIT(2)
|
||||||
|
|
||||||
/* STM32H7_ADC_CCR - bit fields */
|
/* STM32H7_ADC_CCR - bit fields */
|
||||||
|
@ -117,7 +117,9 @@ struct stm32_adc_regs {
|
|||||||
* struct stm32_adc_regspec - stm32 registers definition
|
* struct stm32_adc_regspec - stm32 registers definition
|
||||||
* @dr: data register offset
|
* @dr: data register offset
|
||||||
* @ier_eoc: interrupt enable register & eocie bitfield
|
* @ier_eoc: interrupt enable register & eocie bitfield
|
||||||
|
* @ier_ovr: interrupt enable register & overrun bitfield
|
||||||
* @isr_eoc: interrupt status register & eoc bitfield
|
* @isr_eoc: interrupt status register & eoc bitfield
|
||||||
|
* @isr_ovr: interrupt status register & overrun bitfield
|
||||||
* @sqr: reference to sequence registers array
|
* @sqr: reference to sequence registers array
|
||||||
* @exten: trigger control register & bitfield
|
* @exten: trigger control register & bitfield
|
||||||
* @extsel: trigger selection register & bitfield
|
* @extsel: trigger selection register & bitfield
|
||||||
@ -128,7 +130,9 @@ struct stm32_adc_regs {
|
|||||||
struct stm32_adc_regspec {
|
struct stm32_adc_regspec {
|
||||||
const u32 dr;
|
const u32 dr;
|
||||||
const struct stm32_adc_regs ier_eoc;
|
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_eoc;
|
||||||
|
const struct stm32_adc_regs isr_ovr;
|
||||||
const struct stm32_adc_regs *sqr;
|
const struct stm32_adc_regs *sqr;
|
||||||
const struct stm32_adc_regs exten;
|
const struct stm32_adc_regs exten;
|
||||||
const struct stm32_adc_regs extsel;
|
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 = {
|
static const struct stm32_adc_regspec stm32f4_adc_regspec = {
|
||||||
.dr = STM32F4_ADC_DR,
|
.dr = STM32F4_ADC_DR,
|
||||||
.ier_eoc = { STM32F4_ADC_CR1, STM32F4_EOCIE },
|
.ier_eoc = { STM32F4_ADC_CR1, STM32F4_EOCIE },
|
||||||
|
.ier_ovr = { STM32F4_ADC_CR1, STM32F4_OVRIE },
|
||||||
.isr_eoc = { STM32F4_ADC_SR, STM32F4_EOC },
|
.isr_eoc = { STM32F4_ADC_SR, STM32F4_EOC },
|
||||||
|
.isr_ovr = { STM32F4_ADC_SR, STM32F4_OVR },
|
||||||
.sqr = stm32f4_sq,
|
.sqr = stm32f4_sq,
|
||||||
.exten = { STM32F4_ADC_CR2, STM32F4_EXTEN_MASK, STM32F4_EXTEN_SHIFT },
|
.exten = { STM32F4_ADC_CR2, STM32F4_EXTEN_MASK, STM32F4_EXTEN_SHIFT },
|
||||||
.extsel = { STM32F4_ADC_CR2, STM32F4_EXTSEL_MASK,
|
.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 = {
|
static const struct stm32_adc_regspec stm32h7_adc_regspec = {
|
||||||
.dr = STM32H7_ADC_DR,
|
.dr = STM32H7_ADC_DR,
|
||||||
.ier_eoc = { STM32H7_ADC_IER, STM32H7_EOCIE },
|
.ier_eoc = { STM32H7_ADC_IER, STM32H7_EOCIE },
|
||||||
|
.ier_ovr = { STM32H7_ADC_IER, STM32H7_OVRIE },
|
||||||
.isr_eoc = { STM32H7_ADC_ISR, STM32H7_EOC },
|
.isr_eoc = { STM32H7_ADC_ISR, STM32H7_EOC },
|
||||||
|
.isr_ovr = { STM32H7_ADC_ISR, STM32H7_OVR },
|
||||||
.sqr = stm32h7_sq,
|
.sqr = stm32h7_sq,
|
||||||
.exten = { STM32H7_ADC_CFGR, STM32H7_EXTEN_MASK, STM32H7_EXTEN_SHIFT },
|
.exten = { STM32H7_ADC_CFGR, STM32H7_EXTEN_MASK, STM32H7_EXTEN_SHIFT },
|
||||||
.extsel = { STM32H7_ADC_CFGR, STM32H7_EXTSEL_MASK,
|
.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);
|
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)
|
static void stm32_adc_set_res(struct stm32_adc *adc)
|
||||||
{
|
{
|
||||||
const struct stm32_adc_regs *res = &adc->cfg->regs->res;
|
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)
|
static irqreturn_t stm32_adc_isr(int irq, void *data)
|
||||||
{
|
{
|
||||||
struct stm32_adc *adc = 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;
|
const struct stm32_adc_regspec *regs = adc->cfg->regs;
|
||||||
u32 status = stm32_adc_readl(adc, regs->isr_eoc.reg);
|
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) {
|
if (status & regs->isr_eoc.mask) {
|
||||||
/* Reading DR also clears EOC status flag */
|
/* Reading DR also clears EOC status flag */
|
||||||
adc->buffer[adc->bufi] = stm32_adc_readw(adc, regs->dr);
|
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 */
|
/* Reset adc buffer index */
|
||||||
adc->bufi = 0;
|
adc->bufi = 0;
|
||||||
|
|
||||||
|
stm32_adc_ovr_irq_enable(adc);
|
||||||
|
|
||||||
if (!adc->dma_chan)
|
if (!adc->dma_chan)
|
||||||
stm32_adc_conv_irq_enable(adc);
|
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)
|
if (!adc->dma_chan)
|
||||||
stm32_adc_conv_irq_disable(adc);
|
stm32_adc_conv_irq_disable(adc);
|
||||||
|
|
||||||
|
stm32_adc_ovr_irq_disable(adc);
|
||||||
|
|
||||||
if (adc->dma_chan)
|
if (adc->dma_chan)
|
||||||
dmaengine_terminate_sync(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;
|
struct dma_slave_config config;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
adc->dma_chan = dma_request_slave_channel(&indio_dev->dev, "rx");
|
adc->dma_chan = dma_request_chan(&indio_dev->dev, "rx");
|
||||||
if (!adc->dma_chan)
|
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;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
adc->rx_buf = dma_alloc_coherent(adc->dma_chan->device->dev,
|
adc->rx_buf = dma_alloc_coherent(adc->dma_chan->device->dev,
|
||||||
STM32_DMA_BUFFER_SIZE,
|
STM32_DMA_BUFFER_SIZE,
|
||||||
@ -1818,8 +1880,9 @@ static int stm32_adc_probe(struct platform_device *pdev)
|
|||||||
if (adc->irq < 0)
|
if (adc->irq < 0)
|
||||||
return adc->irq;
|
return adc->irq;
|
||||||
|
|
||||||
ret = devm_request_irq(&pdev->dev, adc->irq, stm32_adc_isr,
|
ret = devm_request_threaded_irq(&pdev->dev, adc->irq, stm32_adc_isr,
|
||||||
0, pdev->name, adc);
|
stm32_adc_threaded_isr,
|
||||||
|
0, pdev->name, adc);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(&pdev->dev, "failed to request IRQ\n");
|
dev_err(&pdev->dev, "failed to request IRQ\n");
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -1204,6 +1204,8 @@ static int stm32_dfsdm_single_conv(struct iio_dev *indio_dev,
|
|||||||
|
|
||||||
stm32_dfsdm_stop_conv(adc);
|
stm32_dfsdm_stop_conv(adc);
|
||||||
|
|
||||||
|
stm32_dfsdm_process_data(adc, res);
|
||||||
|
|
||||||
stop_dfsdm:
|
stop_dfsdm:
|
||||||
stm32_dfsdm_stop_dfsdm(adc->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;
|
unsigned int spi_freq;
|
||||||
int ret = -EINVAL;
|
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) {
|
switch (mask) {
|
||||||
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
|
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
|
||||||
ret = iio_device_claim_direct_mode(indio_dev);
|
ret = iio_device_claim_direct_mode(indio_dev);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
ret = stm32_dfsdm_compute_all_osrs(indio_dev, val);
|
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->oversamp = val;
|
||||||
|
adc->sample_freq = spi_freq / val;
|
||||||
|
}
|
||||||
iio_device_release_direct_mode(indio_dev);
|
iio_device_release_direct_mode(indio_dev);
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
@ -1238,18 +1258,6 @@ static int stm32_dfsdm_write_raw(struct iio_dev *indio_dev,
|
|||||||
if (ret)
|
if (ret)
|
||||||
return 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);
|
ret = dfsdm_adc_set_samp_freq(indio_dev, val, spi_freq);
|
||||||
iio_device_release_direct_mode(indio_dev);
|
iio_device_release_direct_mode(indio_dev);
|
||||||
return ret;
|
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);
|
struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
|
||||||
|
|
||||||
adc->dma_chan = dma_request_slave_channel(&indio_dev->dev, "rx");
|
adc->dma_chan = dma_request_chan(&indio_dev->dev, "rx");
|
||||||
if (!adc->dma_chan)
|
if (IS_ERR(adc->dma_chan)) {
|
||||||
return -EINVAL;
|
int ret = PTR_ERR(adc->dma_chan);
|
||||||
|
|
||||||
|
adc->dma_chan = NULL;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
adc->rx_buf = dma_alloc_coherent(adc->dma_chan->device->dev,
|
adc->rx_buf = dma_alloc_coherent(adc->dma_chan->device->dev,
|
||||||
DFSDM_DMA_BUFFER_SIZE,
|
DFSDM_DMA_BUFFER_SIZE,
|
||||||
@ -1509,7 +1521,16 @@ static int stm32_dfsdm_adc_init(struct iio_dev *indio_dev)
|
|||||||
init_completion(&adc->completion);
|
init_completion(&adc->completion);
|
||||||
|
|
||||||
/* Optionally request DMA */
|
/* 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");
|
dev_dbg(&indio_dev->dev, "No DMA support\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -12,17 +12,15 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/of_device.h>
|
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/irq.h>
|
#include <linux/irq.h>
|
||||||
#include <linux/i2c.h>
|
#include <linux/i2c.h>
|
||||||
|
#include <linux/property.h>
|
||||||
#include <linux/regmap.h>
|
#include <linux/regmap.h>
|
||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
#include <linux/mutex.h>
|
#include <linux/mutex.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
|
|
||||||
#include <linux/platform_data/ads1015.h>
|
|
||||||
|
|
||||||
#include <linux/iio/iio.h>
|
#include <linux/iio/iio.h>
|
||||||
#include <linux/iio/types.h>
|
#include <linux/iio/types.h>
|
||||||
#include <linux/iio/sysfs.h>
|
#include <linux/iio/sysfs.h>
|
||||||
@ -33,6 +31,8 @@
|
|||||||
|
|
||||||
#define ADS1015_DRV_NAME "ads1015"
|
#define ADS1015_DRV_NAME "ads1015"
|
||||||
|
|
||||||
|
#define ADS1015_CHANNELS 8
|
||||||
|
|
||||||
#define ADS1015_CONV_REG 0x00
|
#define ADS1015_CONV_REG 0x00
|
||||||
#define ADS1015_CFG_REG 0x01
|
#define ADS1015_CFG_REG 0x01
|
||||||
#define ADS1015_LO_THRESH_REG 0x02
|
#define ADS1015_LO_THRESH_REG 0x02
|
||||||
@ -77,6 +77,7 @@
|
|||||||
#define ADS1015_DEFAULT_CHAN 0
|
#define ADS1015_DEFAULT_CHAN 0
|
||||||
|
|
||||||
enum chip_ids {
|
enum chip_ids {
|
||||||
|
ADSXXXX = 0,
|
||||||
ADS1015,
|
ADS1015,
|
||||||
ADS1115,
|
ADS1115,
|
||||||
};
|
};
|
||||||
@ -219,6 +220,12 @@ static const struct iio_event_spec ads1015_events[] = {
|
|||||||
.datasheet_name = "AIN"#_chan"-AIN"#_chan2, \
|
.datasheet_name = "AIN"#_chan"-AIN"#_chan2, \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct ads1015_channel_data {
|
||||||
|
bool enabled;
|
||||||
|
unsigned int pga;
|
||||||
|
unsigned int data_rate;
|
||||||
|
};
|
||||||
|
|
||||||
struct ads1015_thresh_data {
|
struct ads1015_thresh_data {
|
||||||
unsigned int comp_queue;
|
unsigned int comp_queue;
|
||||||
int high_thresh;
|
int high_thresh;
|
||||||
@ -837,65 +844,58 @@ static const struct iio_info ads1115_info = {
|
|||||||
.attrs = &ads1115_attribute_group,
|
.attrs = &ads1115_attribute_group,
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef CONFIG_OF
|
static int ads1015_client_get_channels_config(struct i2c_client *client)
|
||||||
static int ads1015_get_channels_config_of(struct i2c_client *client)
|
|
||||||
{
|
{
|
||||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||||
struct ads1015_data *data = iio_priv(indio_dev);
|
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 ||
|
device_for_each_child_node(dev, node) {
|
||||||
!of_get_next_child(client->dev.of_node, NULL))
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
for_each_child_of_node(client->dev.of_node, node) {
|
|
||||||
u32 pval;
|
u32 pval;
|
||||||
unsigned int channel;
|
unsigned int channel;
|
||||||
unsigned int pga = ADS1015_DEFAULT_PGA;
|
unsigned int pga = ADS1015_DEFAULT_PGA;
|
||||||
unsigned int data_rate = ADS1015_DEFAULT_DATA_RATE;
|
unsigned int data_rate = ADS1015_DEFAULT_DATA_RATE;
|
||||||
|
|
||||||
if (of_property_read_u32(node, "reg", &pval)) {
|
if (fwnode_property_read_u32(node, "reg", &pval)) {
|
||||||
dev_err(&client->dev, "invalid reg on %pOF\n",
|
dev_err(dev, "invalid reg on %pfw\n", node);
|
||||||
node);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
channel = pval;
|
channel = pval;
|
||||||
if (channel >= ADS1015_CHANNELS) {
|
if (channel >= ADS1015_CHANNELS) {
|
||||||
dev_err(&client->dev,
|
dev_err(dev, "invalid channel index %d on %pfw\n",
|
||||||
"invalid channel index %d on %pOF\n",
|
|
||||||
channel, node);
|
channel, node);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!of_property_read_u32(node, "ti,gain", &pval)) {
|
if (!fwnode_property_read_u32(node, "ti,gain", &pval)) {
|
||||||
pga = pval;
|
pga = pval;
|
||||||
if (pga > 6) {
|
if (pga > 6) {
|
||||||
dev_err(&client->dev, "invalid gain on %pOF\n",
|
dev_err(dev, "invalid gain on %pfw\n", node);
|
||||||
node);
|
fwnode_handle_put(node);
|
||||||
of_node_put(node);
|
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!of_property_read_u32(node, "ti,datarate", &pval)) {
|
if (!fwnode_property_read_u32(node, "ti,datarate", &pval)) {
|
||||||
data_rate = pval;
|
data_rate = pval;
|
||||||
if (data_rate > 7) {
|
if (data_rate > 7) {
|
||||||
dev_err(&client->dev,
|
dev_err(dev, "invalid data_rate on %pfw\n", node);
|
||||||
"invalid data_rate on %pOF\n",
|
fwnode_handle_put(node);
|
||||||
node);
|
|
||||||
of_node_put(node);
|
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data->channel_data[channel].pga = pga;
|
data->channel_data[channel].pga = pga;
|
||||||
data->channel_data[channel].data_rate = data_rate;
|
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)
|
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 iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||||
struct ads1015_data *data = iio_priv(indio_dev);
|
struct ads1015_data *data = iio_priv(indio_dev);
|
||||||
struct ads1015_platform_data *pdata = dev_get_platdata(&client->dev);
|
|
||||||
|
|
||||||
/* prefer platform data */
|
if (!ads1015_client_get_channels_config(client))
|
||||||
if (pdata) {
|
|
||||||
memcpy(data->channel_data, pdata->channel_data,
|
|
||||||
sizeof(data->channel_data));
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef CONFIG_OF
|
|
||||||
if (!ads1015_get_channels_config_of(client))
|
|
||||||
return;
|
|
||||||
#endif
|
|
||||||
/* fallback on default configuration */
|
/* fallback on default configuration */
|
||||||
for (k = 0; k < ADS1015_CHANNELS; ++k) {
|
for (k = 0; k < ADS1015_CHANNELS; ++k) {
|
||||||
data->channel_data[k].pga = ADS1015_DEFAULT_PGA;
|
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->name = ADS1015_DRV_NAME;
|
||||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||||
|
|
||||||
if (client->dev.of_node)
|
chip = (enum chip_ids)device_get_match_data(&client->dev);
|
||||||
chip = (enum chip_ids)of_device_get_match_data(&client->dev);
|
if (chip == ADSXXXX)
|
||||||
else
|
|
||||||
chip = id->driver_data;
|
chip = id->driver_data;
|
||||||
switch (chip) {
|
switch (chip) {
|
||||||
case ADS1015:
|
case ADS1015:
|
||||||
@ -970,6 +960,9 @@ static int ads1015_probe(struct i2c_client *client,
|
|||||||
indio_dev->info = &ads1115_info;
|
indio_dev->info = &ads1115_info;
|
||||||
data->data_rate = (unsigned int *) &ads1115_data_rate;
|
data->data_rate = (unsigned int *) &ads1115_data_rate;
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
dev_err(&client->dev, "Unknown chip %d\n", chip);
|
||||||
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
data->event_channel = ADS1015_CHANNELS;
|
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");
|
st->reg = devm_regulator_get(&spi->dev, "vref");
|
||||||
if (IS_ERR(st->reg)) {
|
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);
|
ret = PTR_ERR(st->reg);
|
||||||
goto error_destroy_mutex;
|
goto error_destroy_mutex;
|
||||||
}
|
}
|
||||||
|
@ -476,7 +476,7 @@ static struct iio_dma_buffer_block *iio_dma_buffer_dequeue(
|
|||||||
* @n: Number of bytes to read
|
* @n: Number of bytes to read
|
||||||
* @user_buffer: Userspace buffer to copy the data to
|
* @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.
|
* struct for DMA buffers.
|
||||||
*/
|
*/
|
||||||
int iio_dma_buffer_read(struct iio_buffer *buffer, size_t n,
|
int iio_dma_buffer_read(struct iio_buffer *buffer, size_t n,
|
||||||
|
@ -10,8 +10,10 @@
|
|||||||
#include <linux/dma-mapping.h>
|
#include <linux/dma-mapping.h>
|
||||||
#include <linux/spinlock.h>
|
#include <linux/spinlock.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
|
||||||
#include <linux/iio/iio.h>
|
#include <linux/iio/iio.h>
|
||||||
|
#include <linux/iio/sysfs.h>
|
||||||
#include <linux/iio/buffer.h>
|
#include <linux/iio/buffer.h>
|
||||||
#include <linux/iio/buffer_impl.h>
|
#include <linux/iio/buffer_impl.h>
|
||||||
#include <linux/iio/buffer-dma.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 = {
|
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_bytes_per_datum = iio_dma_buffer_set_bytes_per_datum,
|
||||||
.set_length = iio_dma_buffer_set_length,
|
.set_length = iio_dma_buffer_set_length,
|
||||||
.request_update = iio_dma_buffer_request_update,
|
.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,
|
.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
|
* iio_dmaengine_buffer_alloc() - Allocate new buffer which uses DMAengine
|
||||||
* @dev: Parent device for the buffer
|
* @dev: Parent device for the buffer
|
||||||
@ -150,7 +170,7 @@ struct iio_buffer *iio_dmaengine_buffer_alloc(struct device *dev,
|
|||||||
if (!dmaengine_buffer)
|
if (!dmaengine_buffer)
|
||||||
return ERR_PTR(-ENOMEM);
|
return ERR_PTR(-ENOMEM);
|
||||||
|
|
||||||
chan = dma_request_slave_channel_reason(dev, channel);
|
chan = dma_request_chan(dev, channel);
|
||||||
if (IS_ERR(chan)) {
|
if (IS_ERR(chan)) {
|
||||||
ret = PTR_ERR(chan);
|
ret = PTR_ERR(chan);
|
||||||
goto err_free;
|
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_dma_buffer_init(&dmaengine_buffer->queue, chan->device->dev,
|
||||||
&iio_dmaengine_default_ops);
|
&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;
|
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);
|
iio_buffer_put(buffer);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(iio_dmaengine_buffer_free);
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int iio_read_first_n_kfifo(struct iio_buffer *r,
|
static int iio_read_kfifo(struct iio_buffer *r, size_t n, char __user *buf)
|
||||||
size_t n, char __user *buf)
|
|
||||||
{
|
{
|
||||||
int ret, copied;
|
int ret, copied;
|
||||||
struct iio_kfifo *kf = iio_to_kfifo(r);
|
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 = {
|
static const struct iio_buffer_access_funcs kfifo_access_funcs = {
|
||||||
.store_to = &iio_store_to_kfifo,
|
.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,
|
.data_available = iio_kfifo_buf_data_available,
|
||||||
.request_update = &iio_request_update_kfifo,
|
.request_update = &iio_request_update_kfifo,
|
||||||
.set_bytes_per_datum = &iio_set_bytes_per_datum_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
|
# 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) += bme680_core.o
|
||||||
obj-$(CONFIG_BME680_I2C) += bme680_i2c.o
|
obj-$(CONFIG_BME680_I2C) += bme680_i2c.o
|
||||||
obj-$(CONFIG_BME680_SPI) += bme680_spi.o
|
obj-$(CONFIG_BME680_SPI) += bme680_spi.o
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0+
|
// 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>
|
* Author: Matt Ranostay <matt.ranostay@konsulko.com>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -14,7 +14,6 @@
|
|||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/irq.h>
|
#include <linux/irq.h>
|
||||||
#include <linux/irq_work.h>
|
#include <linux/irq_work.h>
|
||||||
#include <linux/gpio.h>
|
|
||||||
#include <linux/i2c.h>
|
#include <linux/i2c.h>
|
||||||
#include <linux/of_device.h>
|
#include <linux/of_device.h>
|
||||||
#include <linux/regmap.h>
|
#include <linux/regmap.h>
|
||||||
@ -25,8 +24,8 @@
|
|||||||
#include <linux/iio/triggered_buffer.h>
|
#include <linux/iio/triggered_buffer.h>
|
||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
|
|
||||||
#define ATLAS_REGMAP_NAME "atlas_ph_regmap"
|
#define ATLAS_REGMAP_NAME "atlas_regmap"
|
||||||
#define ATLAS_DRV_NAME "atlas_ph"
|
#define ATLAS_DRV_NAME "atlas"
|
||||||
|
|
||||||
#define ATLAS_REG_DEV_TYPE 0x00
|
#define ATLAS_REG_DEV_TYPE 0x00
|
||||||
#define ATLAS_REG_DEV_VERSION 0x01
|
#define ATLAS_REG_DEV_VERSION 0x01
|
||||||
@ -87,6 +86,16 @@ static const struct regmap_config atlas_regmap_config = {
|
|||||||
.val_bits = 8,
|
.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[] = {
|
static const struct iio_chan_spec atlas_ph_channels[] = {
|
||||||
{
|
{
|
||||||
.type = IIO_PH,
|
.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_poll_func *pf = private;
|
||||||
struct iio_dev *indio_dev = pf->indio_dev;
|
struct iio_dev *indio_dev = pf->indio_dev;
|
||||||
struct atlas_data *data = iio_priv(indio_dev);
|
struct atlas_data *data = iio_priv(indio_dev);
|
||||||
|
int channels = atlas_buffer_num_channels(data->chip->channels);
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = regmap_bulk_read(data->regmap, data->chip->data_reg,
|
ret = regmap_bulk_read(data->regmap, data->chip->data_reg,
|
||||||
(u8 *) &data->buffer,
|
(u8 *) &data->buffer,
|
||||||
sizeof(__be32) * (data->chip->num_channels - 2));
|
sizeof(__be32) * channels);
|
||||||
|
|
||||||
if (!ret)
|
if (!ret)
|
||||||
iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
|
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_i2c_driver(atlas_driver);
|
||||||
|
|
||||||
MODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>");
|
MODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>");
|
||||||
MODULE_DESCRIPTION("Atlas Scientific pH-SM sensor");
|
MODULE_DESCRIPTION("Atlas Scientific SM sensors");
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
@ -7,7 +7,7 @@
|
|||||||
#define __SSP_SENSORHUB_H__
|
#define __SSP_SENSORHUB_H__
|
||||||
|
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/gpio.h>
|
#include <linux/gpio/consumer.h>
|
||||||
#include <linux/iio/common/ssp_sensors.h>
|
#include <linux/iio/common/ssp_sensors.h>
|
||||||
#include <linux/iio/iio.h>
|
#include <linux/iio/iio.h>
|
||||||
#include <linux/spi/spi.h>
|
#include <linux/spi/spi.h>
|
||||||
@ -168,9 +168,9 @@ struct ssp_sensorhub_info {
|
|||||||
* @fw_dl_state: firmware download state
|
* @fw_dl_state: firmware download state
|
||||||
* @comm_lock: lock protecting the handshake
|
* @comm_lock: lock protecting the handshake
|
||||||
* @pending_lock: lock protecting pending list and completion
|
* @pending_lock: lock protecting pending list and completion
|
||||||
* @mcu_reset_gpio: mcu reset line
|
* @mcu_reset_gpiod: mcu reset line
|
||||||
* @ap_mcu_gpio: ap to mcu gpio line
|
* @ap_mcu_gpiod: ap to mcu gpio line
|
||||||
* @mcu_ap_gpio: mcu to ap gpio line
|
* @mcu_ap_gpiod: mcu to ap gpio line
|
||||||
* @pending_list: pending list for messages queued to be sent/read
|
* @pending_list: pending list for messages queued to be sent/read
|
||||||
* @sensor_devs: registered IIO devices table
|
* @sensor_devs: registered IIO devices table
|
||||||
* @enable_refcount: enable reference count for wdt (watchdog timer)
|
* @enable_refcount: enable reference count for wdt (watchdog timer)
|
||||||
@ -212,9 +212,9 @@ struct ssp_data {
|
|||||||
struct mutex comm_lock;
|
struct mutex comm_lock;
|
||||||
struct mutex pending_lock;
|
struct mutex pending_lock;
|
||||||
|
|
||||||
int mcu_reset_gpio;
|
struct gpio_desc *mcu_reset_gpiod;
|
||||||
int ap_mcu_gpio;
|
struct gpio_desc *ap_mcu_gpiod;
|
||||||
int mcu_ap_gpio;
|
struct gpio_desc *mcu_ap_gpiod;
|
||||||
|
|
||||||
struct list_head pending_list;
|
struct list_head pending_list;
|
||||||
|
|
||||||
|
@ -9,7 +9,6 @@
|
|||||||
#include <linux/mfd/core.h>
|
#include <linux/mfd/core.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/of_gpio.h>
|
|
||||||
#include <linux/of_platform.h>
|
#include <linux/of_platform.h>
|
||||||
#include "ssp.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)
|
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);
|
usleep_range(1000, 1200);
|
||||||
gpio_set_value(data->mcu_reset_gpio, 1);
|
gpiod_set_value(data->mcu_reset_gpiod, 1);
|
||||||
msleep(50);
|
msleep(50);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -441,7 +440,6 @@ MODULE_DEVICE_TABLE(of, ssp_of_match);
|
|||||||
|
|
||||||
static struct ssp_data *ssp_parse_dt(struct device *dev)
|
static struct ssp_data *ssp_parse_dt(struct device *dev)
|
||||||
{
|
{
|
||||||
int ret;
|
|
||||||
struct ssp_data *data;
|
struct ssp_data *data;
|
||||||
struct device_node *node = dev->of_node;
|
struct device_node *node = dev->of_node;
|
||||||
const struct of_device_id *match;
|
const struct of_device_id *match;
|
||||||
@ -450,26 +448,17 @@ static struct ssp_data *ssp_parse_dt(struct device *dev)
|
|||||||
if (!data)
|
if (!data)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
data->mcu_ap_gpio = of_get_named_gpio(node, "mcu-ap-gpios", 0);
|
data->mcu_ap_gpiod = devm_gpiod_get(dev, "mcu-ap", GPIOD_IN);
|
||||||
if (data->mcu_ap_gpio < 0)
|
if (IS_ERR(data->mcu_ap_gpiod))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
data->ap_mcu_gpio = of_get_named_gpio(node, "ap-mcu-gpios", 0);
|
data->ap_mcu_gpiod = devm_gpiod_get(dev, "ap-mcu", GPIOD_OUT_HIGH);
|
||||||
if (data->ap_mcu_gpio < 0)
|
if (IS_ERR(data->ap_mcu_gpiod))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
data->mcu_reset_gpio = of_get_named_gpio(node, "mcu-reset-gpios", 0);
|
data->mcu_reset_gpiod = devm_gpiod_get(dev, "mcu-reset",
|
||||||
if (data->mcu_reset_gpio < 0)
|
GPIOD_OUT_HIGH);
|
||||||
return NULL;
|
if (IS_ERR(data->mcu_reset_gpiod))
|
||||||
|
|
||||||
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)
|
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
match = of_match_node(ssp_of_match, node);
|
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;
|
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);
|
usleep_range(3000, 3500);
|
||||||
|
|
||||||
if (data->shut_down || delay_cnt++ > 500) {
|
if (data->shut_down || delay_cnt++ > 500) {
|
||||||
@ -165,7 +165,7 @@ static int ssp_check_lines(struct ssp_data *data, bool state)
|
|||||||
__func__, state);
|
__func__, state);
|
||||||
|
|
||||||
if (!state)
|
if (!state)
|
||||||
gpio_set_value_cansleep(data->ap_mcu_gpio, 1);
|
gpiod_set_value_cansleep(data->ap_mcu_gpiod, 1);
|
||||||
|
|
||||||
return -ETIMEDOUT;
|
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);
|
status = spi_write(data->spi, msg->buffer, SSP_HEADER_SIZE);
|
||||||
if (status < 0) {
|
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__);
|
dev_err(SSP_DEV, "%s spi_write fail\n", __func__);
|
||||||
goto _error_locked;
|
goto _error_locked;
|
||||||
}
|
}
|
||||||
|
@ -12,9 +12,8 @@
|
|||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/iio/iio.h>
|
#include <linux/iio/iio.h>
|
||||||
|
#include <linux/property.h>
|
||||||
#include <linux/regulator/consumer.h>
|
#include <linux/regulator/consumer.h>
|
||||||
#include <linux/of.h>
|
|
||||||
#include <linux/of_device.h>
|
|
||||||
#include <linux/regmap.h>
|
#include <linux/regmap.h>
|
||||||
#include <asm/unaligned.h>
|
#include <asm/unaligned.h>
|
||||||
#include <linux/iio/common/st_sensors.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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_OF
|
static struct st_sensors_platform_data *st_sensors_dev_probe(struct device *dev,
|
||||||
static struct st_sensors_platform_data *st_sensors_of_probe(struct device *dev,
|
|
||||||
struct st_sensors_platform_data *defdata)
|
struct st_sensors_platform_data *defdata)
|
||||||
{
|
{
|
||||||
struct st_sensors_platform_data *pdata;
|
struct st_sensors_platform_data *pdata;
|
||||||
struct device_node *np = dev->of_node;
|
|
||||||
u32 val;
|
u32 val;
|
||||||
|
|
||||||
if (!np)
|
if (!dev_fwnode(dev))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
|
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;
|
pdata->drdy_int_pin = (u8) val;
|
||||||
else
|
else
|
||||||
pdata->drdy_int_pin = defdata ? defdata->drdy_int_pin : 0;
|
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;
|
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.
|
* @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.
|
* @name: device name buffer reference.
|
||||||
* @len: device name buffer length.
|
* @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
|
* 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
|
* rely on that name from this point on. I2C/SPI devices will be renamed
|
||||||
* to match the internal kernel convention.
|
* to match the internal kernel convention.
|
||||||
*/
|
*/
|
||||||
void st_sensors_of_name_probe(struct device *dev,
|
void st_sensors_dev_name_probe(struct device *dev, char *name, int len)
|
||||||
const struct of_device_id *match,
|
|
||||||
char *name, int len)
|
|
||||||
{
|
{
|
||||||
const struct of_device_id *of_id;
|
const void *match;
|
||||||
|
|
||||||
of_id = of_match_device(match, dev);
|
match = device_get_match_data(dev);
|
||||||
if (!of_id || !of_id->data)
|
if (!match)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* The name from the OF match takes precedence if present */
|
/* The name from the match takes precedence if present */
|
||||||
strlcpy(name, of_id->data, len);
|
strlcpy(name, match, len);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(st_sensors_of_name_probe);
|
EXPORT_SYMBOL(st_sensors_dev_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
|
|
||||||
|
|
||||||
int st_sensors_init_sensor(struct iio_dev *indio_dev,
|
int st_sensors_init_sensor(struct iio_dev *indio_dev,
|
||||||
struct st_sensors_platform_data *pdata)
|
struct st_sensors_platform_data *pdata)
|
||||||
@ -385,7 +370,7 @@ int st_sensors_init_sensor(struct iio_dev *indio_dev,
|
|||||||
int err = 0;
|
int err = 0;
|
||||||
|
|
||||||
/* If OF/DT pdata exists, it will take precedence of anything else */
|
/* 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)
|
if (of_pdata)
|
||||||
pdata = of_pdata;
|
pdata = of_pdata;
|
||||||
|
|
||||||
|
@ -11,8 +11,6 @@
|
|||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/iio/iio.h>
|
#include <linux/iio/iio.h>
|
||||||
#include <linux/of_device.h>
|
|
||||||
#include <linux/acpi.h>
|
|
||||||
#include <linux/regmap.h>
|
#include <linux/regmap.h>
|
||||||
|
|
||||||
#include <linux/iio/common/st_sensors_i2c.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);
|
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_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
|
||||||
MODULE_DESCRIPTION("STMicroelectronics ST-sensors i2c driver");
|
MODULE_DESCRIPTION("STMicroelectronics ST-sensors i2c driver");
|
||||||
MODULE_LICENSE("GPL v2");
|
MODULE_LICENSE("GPL v2");
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/iio/iio.h>
|
#include <linux/iio/iio.h>
|
||||||
|
#include <linux/property.h>
|
||||||
#include <linux/regmap.h>
|
#include <linux/regmap.h>
|
||||||
|
|
||||||
#include <linux/iio/common/st_sensors_spi.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)
|
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 st_sensors_platform_data *pdata;
|
||||||
|
struct device *dev = &spi->dev;
|
||||||
|
|
||||||
pdata = (struct st_sensors_platform_data *)spi->dev.platform_data;
|
if (device_property_read_bool(dev, "spi-3wire"))
|
||||||
if ((np && of_property_read_bool(np, "spi-3wire")) ||
|
return true;
|
||||||
(pdata && pdata->spi_3wire)) {
|
|
||||||
|
pdata = (struct st_sensors_platform_data *)dev->platform_data;
|
||||||
|
if (pdata && pdata->spi_3wire)
|
||||||
return true;
|
return true;
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,9 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* st_sensors_new_samples_available() - check if more samples came in
|
* st_sensors_new_samples_available() - check if more samples came in
|
||||||
|
* @indio_dev: IIO device reference.
|
||||||
|
* @sdata: Sensor data.
|
||||||
|
*
|
||||||
* returns:
|
* returns:
|
||||||
* 0 - no new samples available
|
* 0 - no new samples available
|
||||||
* 1 - new samples available
|
* 1 - new samples available
|
||||||
|
@ -15,7 +15,6 @@
|
|||||||
#include <linux/regulator/consumer.h>
|
#include <linux/regulator/consumer.h>
|
||||||
#include <linux/gpio/consumer.h>
|
#include <linux/gpio/consumer.h>
|
||||||
#include <linux/gpio/driver.h>
|
#include <linux/gpio/driver.h>
|
||||||
#include <linux/gpio.h>
|
|
||||||
#include <linux/property.h>
|
#include <linux/property.h>
|
||||||
|
|
||||||
#include <dt-bindings/iio/adi,ad5592r.h>
|
#include <dt-bindings/iio/adi,ad5592r.h>
|
||||||
|
@ -12,7 +12,6 @@
|
|||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/sysfs.h>
|
#include <linux/sysfs.h>
|
||||||
#include <linux/regulator/consumer.h>
|
#include <linux/regulator/consumer.h>
|
||||||
#include <linux/of.h>
|
|
||||||
|
|
||||||
#include <linux/iio/iio.h>
|
#include <linux/iio/iio.h>
|
||||||
#include <linux/iio/sysfs.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);
|
const struct spi_device_id *id = spi_get_device_id(spi);
|
||||||
struct iio_dev *indio_dev;
|
struct iio_dev *indio_dev;
|
||||||
struct ad7303_state *st;
|
struct ad7303_state *st;
|
||||||
bool ext_ref;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
||||||
@ -224,24 +222,15 @@ static int ad7303_probe(struct spi_device *spi)
|
|||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
if (spi->dev.of_node) {
|
st->vref_reg = devm_regulator_get_optional(&spi->dev, "REF");
|
||||||
ext_ref = of_property_read_bool(spi->dev.of_node,
|
if (IS_ERR(st->vref_reg)) {
|
||||||
"REF-supply");
|
ret = PTR_ERR(st->vref_reg);
|
||||||
} else {
|
if (ret != -ENODEV)
|
||||||
struct ad7303_platform_data *pdata = spi->dev.platform_data;
|
goto err_disable_vdd_reg;
|
||||||
if (pdata && pdata->use_external_ref)
|
st->vref_reg = NULL;
|
||||||
ext_ref = true;
|
|
||||||
else
|
|
||||||
ext_ref = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ext_ref) {
|
if (st->vref_reg) {
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = regulator_enable(st->vref_reg);
|
ret = regulator_enable(st->vref_reg);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_disable_vdd_reg;
|
goto err_disable_vdd_reg;
|
||||||
|
@ -20,13 +20,11 @@
|
|||||||
/**
|
/**
|
||||||
* struct stm32_dac_priv - stm32 DAC core private data
|
* struct stm32_dac_priv - stm32 DAC core private data
|
||||||
* @pclk: peripheral clock common for all DACs
|
* @pclk: peripheral clock common for all DACs
|
||||||
* @rst: peripheral reset control
|
|
||||||
* @vref: regulator reference
|
* @vref: regulator reference
|
||||||
* @common: Common data for all DAC instances
|
* @common: Common data for all DAC instances
|
||||||
*/
|
*/
|
||||||
struct stm32_dac_priv {
|
struct stm32_dac_priv {
|
||||||
struct clk *pclk;
|
struct clk *pclk;
|
||||||
struct reset_control *rst;
|
|
||||||
struct regulator *vref;
|
struct regulator *vref;
|
||||||
struct stm32_dac_common common;
|
struct stm32_dac_common common;
|
||||||
};
|
};
|
||||||
@ -94,6 +92,7 @@ static int stm32_dac_probe(struct platform_device *pdev)
|
|||||||
struct regmap *regmap;
|
struct regmap *regmap;
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
void __iomem *mmio;
|
void __iomem *mmio;
|
||||||
|
struct reset_control *rst;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (!dev->of_node)
|
if (!dev->of_node)
|
||||||
@ -148,11 +147,19 @@ static int stm32_dac_probe(struct platform_device *pdev)
|
|||||||
priv->common.vref_mv = ret / 1000;
|
priv->common.vref_mv = ret / 1000;
|
||||||
dev_dbg(dev, "vref+=%dmV\n", priv->common.vref_mv);
|
dev_dbg(dev, "vref+=%dmV\n", priv->common.vref_mv);
|
||||||
|
|
||||||
priv->rst = devm_reset_control_get_exclusive(dev, NULL);
|
rst = devm_reset_control_get_optional_exclusive(dev, NULL);
|
||||||
if (!IS_ERR(priv->rst)) {
|
if (rst) {
|
||||||
reset_control_assert(priv->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);
|
udelay(2);
|
||||||
reset_control_deassert(priv->rst);
|
reset_control_deassert(rst);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cfg && cfg->has_hfsel) {
|
if (cfg && cfg->has_hfsel) {
|
||||||
|
@ -14,11 +14,10 @@
|
|||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/gcd.h>
|
#include <linux/gcd.h>
|
||||||
#include <linux/gpio.h>
|
#include <linux/gpio/consumer.h>
|
||||||
#include <asm/div64.h>
|
#include <asm/div64.h>
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/of_gpio.h>
|
|
||||||
|
|
||||||
#include <linux/iio/iio.h>
|
#include <linux/iio/iio.h>
|
||||||
#include <linux/iio/sysfs.h>
|
#include <linux/iio/sysfs.h>
|
||||||
@ -34,6 +33,7 @@ enum {
|
|||||||
struct adf4350_state {
|
struct adf4350_state {
|
||||||
struct spi_device *spi;
|
struct spi_device *spi;
|
||||||
struct regulator *reg;
|
struct regulator *reg;
|
||||||
|
struct gpio_desc *lock_detect_gpiod;
|
||||||
struct adf4350_platform_data *pdata;
|
struct adf4350_platform_data *pdata;
|
||||||
struct clk *clk;
|
struct clk *clk;
|
||||||
unsigned long clkin;
|
unsigned long clkin;
|
||||||
@ -61,7 +61,6 @@ static struct adf4350_platform_data default_pdata = {
|
|||||||
.r3_user_settings = ADF4350_REG3_12BIT_CLKDIV_MODE(0),
|
.r3_user_settings = ADF4350_REG3_12BIT_CLKDIV_MODE(0),
|
||||||
.r4_user_settings = ADF4350_REG4_OUTPUT_PWR(3) |
|
.r4_user_settings = ADF4350_REG4_OUTPUT_PWR(3) |
|
||||||
ADF4350_REG4_MUTE_TILL_LOCK_EN,
|
ADF4350_REG4_MUTE_TILL_LOCK_EN,
|
||||||
.gpio_lock_detect = -1,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static int adf4350_sync_config(struct adf4350_state *st)
|
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;
|
(u64)st->fpfd;
|
||||||
do_div(val, st->r1_mod * (1 << st->r4_rf_div_sel));
|
do_div(val, st->r1_mod * (1 << st->r4_rf_div_sel));
|
||||||
/* PLL unlocked? return error */
|
/* PLL unlocked? return error */
|
||||||
if (gpio_is_valid(st->pdata->gpio_lock_detect))
|
if (st->lock_detect_gpiod)
|
||||||
if (!gpio_get_value(st->pdata->gpio_lock_detect)) {
|
if (!gpiod_get_value(st->lock_detect_gpiod)) {
|
||||||
dev_dbg(&st->spi->dev, "PLL un-locked\n");
|
dev_dbg(&st->spi->dev, "PLL un-locked\n");
|
||||||
ret = -EBUSY;
|
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 device_node *np = dev->of_node;
|
||||||
struct adf4350_platform_data *pdata;
|
struct adf4350_platform_data *pdata;
|
||||||
unsigned int tmp;
|
unsigned int tmp;
|
||||||
int ret;
|
|
||||||
|
|
||||||
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
|
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
|
||||||
if (!pdata)
|
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);
|
of_property_read_u32(np, "adi,reference-div-factor", &tmp);
|
||||||
pdata->ref_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,
|
pdata->ref_doubler_en = of_property_read_bool(np,
|
||||||
"adi,reference-doubler-enable");
|
"adi,reference-doubler-enable");
|
||||||
pdata->ref_div2_en = of_property_read_bool(np,
|
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));
|
memset(st->regs_hw, 0xFF, sizeof(st->regs_hw));
|
||||||
|
|
||||||
if (gpio_is_valid(pdata->gpio_lock_detect)) {
|
st->lock_detect_gpiod = devm_gpiod_get_optional(&spi->dev, NULL,
|
||||||
ret = devm_gpio_request(&spi->dev, pdata->gpio_lock_detect,
|
GPIOD_IN);
|
||||||
indio_dev->name);
|
if (IS_ERR(st->lock_detect_gpiod))
|
||||||
if (ret) {
|
return PTR_ERR(st->lock_detect_gpiod);
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pdata->power_up_frequency) {
|
if (pdata->power_up_frequency) {
|
||||||
ret = adf4350_set_freq(st, pdata->power_up_frequency);
|
ret = adf4350_set_freq(st, pdata->power_up_frequency);
|
||||||
|
@ -75,26 +75,26 @@ config BMG160_SPI
|
|||||||
select REGMAP_SPI
|
select REGMAP_SPI
|
||||||
|
|
||||||
config FXAS21002C
|
config FXAS21002C
|
||||||
tristate "NXP FXAS21002C Gyro Sensor"
|
tristate "NXP FXAS21002C Gyro Sensor"
|
||||||
select IIO_BUFFER
|
select IIO_BUFFER
|
||||||
select IIO_TRIGGERED_BUFFER
|
select IIO_TRIGGERED_BUFFER
|
||||||
select FXAS21002C_I2C if (I2C)
|
select FXAS21002C_I2C if (I2C)
|
||||||
select FXAS21002C_SPI if (SPI)
|
select FXAS21002C_SPI if (SPI)
|
||||||
depends on (I2C || SPI_MASTER)
|
depends on (I2C || SPI_MASTER)
|
||||||
help
|
help
|
||||||
Say yes here to build support for NXP FXAS21002C Tri-axis Gyro
|
Say yes here to build support for NXP FXAS21002C Tri-axis Gyro
|
||||||
Sensor driver connected via I2C or SPI.
|
Sensor driver connected via I2C or SPI.
|
||||||
|
|
||||||
This driver can also be built as a module. If so, the module
|
This driver can also be built as a module. If so, the module
|
||||||
will be called fxas21002c_i2c or fxas21002c_spi.
|
will be called fxas21002c_i2c or fxas21002c_spi.
|
||||||
|
|
||||||
config FXAS21002C_I2C
|
config FXAS21002C_I2C
|
||||||
tristate
|
tristate
|
||||||
select REGMAP_I2C
|
select REGMAP_I2C
|
||||||
|
|
||||||
config FXAS21002C_SPI
|
config FXAS21002C_SPI
|
||||||
tristate
|
tristate
|
||||||
select REGMAP_SPI
|
select REGMAP_SPI
|
||||||
|
|
||||||
config HID_SENSOR_GYRO_3D
|
config HID_SENSOR_GYRO_3D
|
||||||
depends on HID_SENSOR_HUB
|
depends on HID_SENSOR_HUB
|
||||||
|
@ -59,6 +59,7 @@
|
|||||||
struct adis16136_chip_info {
|
struct adis16136_chip_info {
|
||||||
unsigned int precision;
|
unsigned int precision;
|
||||||
unsigned int fullscale;
|
unsigned int fullscale;
|
||||||
|
const struct adis_timeout *timeouts;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct adis16136 {
|
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);
|
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;
|
uint16_t t;
|
||||||
int ret;
|
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)
|
if (ret)
|
||||||
return 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 iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||||
struct adis16136 *adis16136 = iio_priv(indio_dev);
|
struct adis16136 *adis16136 = iio_priv(indio_dev);
|
||||||
|
struct mutex *slock = &adis16136->adis.state_lock;
|
||||||
unsigned int freq;
|
unsigned int freq;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = adis16136_get_freq(adis16136, &freq);
|
mutex_lock(slock);
|
||||||
|
ret = __adis16136_get_freq(adis16136, &freq);
|
||||||
|
mutex_unlock(slock);
|
||||||
if (ret)
|
if (ret)
|
||||||
return 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)
|
static int adis16136_set_filter(struct iio_dev *indio_dev, int val)
|
||||||
{
|
{
|
||||||
struct adis16136 *adis16136 = iio_priv(indio_dev);
|
struct adis16136 *adis16136 = iio_priv(indio_dev);
|
||||||
|
struct mutex *slock = &adis16136->adis.state_lock;
|
||||||
unsigned int freq;
|
unsigned int freq;
|
||||||
int i, ret;
|
int i, ret;
|
||||||
|
|
||||||
ret = adis16136_get_freq(adis16136, &freq);
|
mutex_lock(slock);
|
||||||
|
ret = __adis16136_get_freq(adis16136, &freq);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
goto out_unlock;
|
||||||
|
|
||||||
for (i = ARRAY_SIZE(adis16136_3db_divisors) - 1; i >= 1; i--) {
|
for (i = ARRAY_SIZE(adis16136_3db_divisors) - 1; i >= 1; i--) {
|
||||||
if (freq / adis16136_3db_divisors[i] >= val)
|
if (freq / adis16136_3db_divisors[i] >= val)
|
||||||
break;
|
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)
|
static int adis16136_get_filter(struct iio_dev *indio_dev, int *val)
|
||||||
{
|
{
|
||||||
struct adis16136 *adis16136 = iio_priv(indio_dev);
|
struct adis16136 *adis16136 = iio_priv(indio_dev);
|
||||||
|
struct mutex *slock = &adis16136->adis.state_lock;
|
||||||
unsigned int freq;
|
unsigned int freq;
|
||||||
uint16_t val16;
|
uint16_t val16;
|
||||||
int ret;
|
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)
|
if (ret)
|
||||||
goto err_unlock;
|
goto err_unlock;
|
||||||
|
|
||||||
ret = adis16136_get_freq(adis16136, &freq);
|
ret = __adis16136_get_freq(adis16136, &freq);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_unlock;
|
goto err_unlock;
|
||||||
|
|
||||||
*val = freq / adis16136_3db_divisors[val16 & 0x07];
|
*val = freq / adis16136_3db_divisors[val16 & 0x07];
|
||||||
|
|
||||||
err_unlock:
|
err_unlock:
|
||||||
mutex_unlock(&indio_dev->mlock);
|
mutex_unlock(slock);
|
||||||
|
|
||||||
return ret ? ret : IIO_VAL_INT;
|
return ret ? ret : IIO_VAL_INT;
|
||||||
}
|
}
|
||||||
@ -460,7 +472,6 @@ static const struct adis_data adis16136_data = {
|
|||||||
.msc_ctrl_reg = ADIS16136_REG_MSC_CTRL,
|
.msc_ctrl_reg = ADIS16136_REG_MSC_CTRL,
|
||||||
|
|
||||||
.self_test_mask = ADIS16136_MSC_CTRL_SELF_TEST,
|
.self_test_mask = ADIS16136_MSC_CTRL_SELF_TEST,
|
||||||
.startup_delay = 80,
|
|
||||||
|
|
||||||
.read_delay = 10,
|
.read_delay = 10,
|
||||||
.write_delay = 10,
|
.write_delay = 10,
|
||||||
@ -479,30 +490,63 @@ enum adis16136_id {
|
|||||||
ID_ADIS16137,
|
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[] = {
|
static const struct adis16136_chip_info adis16136_chip_info[] = {
|
||||||
[ID_ADIS16133] = {
|
[ID_ADIS16133] = {
|
||||||
.precision = IIO_DEGREE_TO_RAD(1200),
|
.precision = IIO_DEGREE_TO_RAD(1200),
|
||||||
.fullscale = 24000,
|
.fullscale = 24000,
|
||||||
|
.timeouts = &adis16133_timeouts,
|
||||||
},
|
},
|
||||||
[ID_ADIS16135] = {
|
[ID_ADIS16135] = {
|
||||||
.precision = IIO_DEGREE_TO_RAD(300),
|
.precision = IIO_DEGREE_TO_RAD(300),
|
||||||
.fullscale = 24000,
|
.fullscale = 24000,
|
||||||
|
.timeouts = &adis16133_timeouts,
|
||||||
},
|
},
|
||||||
[ID_ADIS16136] = {
|
[ID_ADIS16136] = {
|
||||||
.precision = IIO_DEGREE_TO_RAD(450),
|
.precision = IIO_DEGREE_TO_RAD(450),
|
||||||
.fullscale = 24623,
|
.fullscale = 24623,
|
||||||
|
.timeouts = &adis16136_timeouts,
|
||||||
},
|
},
|
||||||
[ID_ADIS16137] = {
|
[ID_ADIS16137] = {
|
||||||
.precision = IIO_DEGREE_TO_RAD(1000),
|
.precision = IIO_DEGREE_TO_RAD(1000),
|
||||||
.fullscale = 24609,
|
.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)
|
static int adis16136_probe(struct spi_device *spi)
|
||||||
{
|
{
|
||||||
const struct spi_device_id *id = spi_get_device_id(spi);
|
const struct spi_device_id *id = spi_get_device_id(spi);
|
||||||
struct adis16136 *adis16136;
|
struct adis16136 *adis16136;
|
||||||
struct iio_dev *indio_dev;
|
struct iio_dev *indio_dev;
|
||||||
|
const struct adis_data *adis16136_data;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adis16136));
|
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->info = &adis16136_info;
|
||||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
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)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
@ -293,7 +293,7 @@ static int adis16260_write_raw(struct iio_dev *indio_dev,
|
|||||||
addr = adis16260_addresses[chan->scan_index][1];
|
addr = adis16260_addresses[chan->scan_index][1];
|
||||||
return adis_write_reg_16(adis, addr, val);
|
return adis_write_reg_16(adis, addr, val);
|
||||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
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)
|
if (spi_get_device_id(adis->spi)->driver_data)
|
||||||
t = 256 / val;
|
t = 256 / val;
|
||||||
else
|
else
|
||||||
@ -308,9 +308,9 @@ static int adis16260_write_raw(struct iio_dev *indio_dev,
|
|||||||
adis->spi->max_speed_hz = ADIS16260_SPI_SLOW;
|
adis->spi->max_speed_hz = ADIS16260_SPI_SLOW;
|
||||||
else
|
else
|
||||||
adis->spi->max_speed_hz = ADIS16260_SPI_FAST;
|
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 ret;
|
||||||
}
|
}
|
||||||
return -EINVAL;
|
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",
|
[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 = {
|
static const struct adis_data adis16260_data = {
|
||||||
.write_delay = 30,
|
.write_delay = 30,
|
||||||
.read_delay = 30,
|
.read_delay = 30,
|
||||||
@ -340,7 +346,7 @@ static const struct adis_data adis16260_data = {
|
|||||||
.diag_stat_reg = ADIS16260_DIAG_STAT,
|
.diag_stat_reg = ADIS16260_DIAG_STAT,
|
||||||
|
|
||||||
.self_test_mask = ADIS16260_MSC_CTRL_MEM_TEST,
|
.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_msgs = adis1620_status_error_msgs,
|
||||||
.status_error_mask = BIT(ADIS16260_DIAG_STAT_FLASH_CHK_BIT) |
|
.status_error_mask = BIT(ADIS16260_DIAG_STAT_FLASH_CHK_BIT) |
|
||||||
|
@ -15,7 +15,6 @@
|
|||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/irq.h>
|
#include <linux/irq.h>
|
||||||
#include <linux/i2c.h>
|
#include <linux/i2c.h>
|
||||||
#include <linux/gpio.h>
|
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/stat.h>
|
#include <linux/stat.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
* struct st_sensors_platform_data - gyro platform data
|
* struct st_sensors_platform_data - gyro platform data
|
||||||
* @drdy_int_pin: DRDY on gyros is available only on INT2 pin.
|
* @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,
|
.drdy_int_pin = 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -138,7 +138,6 @@ static const struct st_sensor_settings st_gyro_sensors_settings[] = {
|
|||||||
[2] = LSM330DLC_GYRO_DEV_NAME,
|
[2] = LSM330DLC_GYRO_DEV_NAME,
|
||||||
[3] = L3G4IS_GYRO_DEV_NAME,
|
[3] = L3G4IS_GYRO_DEV_NAME,
|
||||||
[4] = LSM330_GYRO_DEV_NAME,
|
[4] = LSM330_GYRO_DEV_NAME,
|
||||||
[5] = LSM9DS0_GYRO_DEV_NAME,
|
|
||||||
},
|
},
|
||||||
.ch = (struct iio_chan_spec *)st_gyro_16bit_channels,
|
.ch = (struct iio_chan_spec *)st_gyro_16bit_channels,
|
||||||
.odr = {
|
.odr = {
|
||||||
@ -208,6 +207,80 @@ static const struct st_sensor_settings st_gyro_sensors_settings[] = {
|
|||||||
.multi_read_bit = true,
|
.multi_read_bit = true,
|
||||||
.bootime = 2,
|
.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 = 0xd7,
|
||||||
.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
|
.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
|
||||||
|
@ -17,7 +17,6 @@
|
|||||||
#include <linux/iio/common/st_sensors_i2c.h>
|
#include <linux/iio/common/st_sensors_i2c.h>
|
||||||
#include "st_gyro.h"
|
#include "st_gyro.h"
|
||||||
|
|
||||||
#ifdef CONFIG_OF
|
|
||||||
static const struct of_device_id st_gyro_of_match[] = {
|
static const struct of_device_id st_gyro_of_match[] = {
|
||||||
{
|
{
|
||||||
.compatible = "st,l3g4200d-gyro",
|
.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);
|
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,
|
static int st_gyro_i2c_probe(struct i2c_client *client,
|
||||||
const struct i2c_device_id *id)
|
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;
|
struct iio_dev *indio_dev;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
st_sensors_of_name_probe(&client->dev, st_gyro_of_match,
|
st_sensors_dev_name_probe(&client->dev, client->name, sizeof(client->name));
|
||||||
client->name, sizeof(client->name));
|
|
||||||
|
|
||||||
settings = st_gyro_get_settings(client->name);
|
settings = st_gyro_get_settings(client->name);
|
||||||
if (!settings) {
|
if (!settings) {
|
||||||
@ -122,7 +117,7 @@ MODULE_DEVICE_TABLE(i2c, st_gyro_id_table);
|
|||||||
static struct i2c_driver st_gyro_driver = {
|
static struct i2c_driver st_gyro_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "st-gyro-i2c",
|
.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,
|
.probe = st_gyro_i2c_probe,
|
||||||
.remove = st_gyro_i2c_remove,
|
.remove = st_gyro_i2c_remove,
|
||||||
|
@ -17,7 +17,6 @@
|
|||||||
#include <linux/iio/common/st_sensors_spi.h>
|
#include <linux/iio/common/st_sensors_spi.h>
|
||||||
#include "st_gyro.h"
|
#include "st_gyro.h"
|
||||||
|
|
||||||
#ifdef CONFIG_OF
|
|
||||||
/*
|
/*
|
||||||
* For new single-chip sensors use <device_name> as compatible string.
|
* For new single-chip sensors use <device_name> as compatible string.
|
||||||
* For old single-chip devices keep <device_name>-gyro to maintain
|
* 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);
|
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)
|
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;
|
struct iio_dev *indio_dev;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
st_sensors_of_name_probe(&spi->dev, st_gyro_of_match,
|
st_sensors_dev_name_probe(&spi->dev, spi->modalias, sizeof(spi->modalias));
|
||||||
spi->modalias, sizeof(spi->modalias));
|
|
||||||
|
|
||||||
settings = st_gyro_get_settings(spi->modalias);
|
settings = st_gyro_get_settings(spi->modalias);
|
||||||
if (!settings) {
|
if (!settings) {
|
||||||
@ -126,7 +121,7 @@ MODULE_DEVICE_TABLE(spi, st_gyro_id_table);
|
|||||||
static struct spi_driver st_gyro_driver = {
|
static struct spi_driver st_gyro_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "st-gyro-spi",
|
.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,
|
.probe = st_gyro_spi_probe,
|
||||||
.remove = st_gyro_spi_remove,
|
.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 iio_dev *iio = data;
|
||||||
struct dht11 *dht11 = iio_priv(iio);
|
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) {
|
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].ts = ktime_get_boottime_ns();
|
||||||
dht11->edges[dht11->num_edges++].value =
|
dht11->edges[dht11->num_edges++].value =
|
||||||
|
@ -24,13 +24,6 @@
|
|||||||
#define HTS221_REG_CNTRL1_ADDR 0x20
|
#define HTS221_REG_CNTRL1_ADDR 0x20
|
||||||
#define HTS221_REG_CNTRL2_ADDR 0x21
|
#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_ODR_MASK 0x03
|
||||||
#define HTS221_BDU_MASK BIT(2)
|
#define HTS221_BDU_MASK BIT(2)
|
||||||
#define HTS221_ENABLE_MASK BIT(7)
|
#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[] = {
|
static const struct hts221_avg hts221_avg_list[] = {
|
||||||
{
|
{
|
||||||
.addr = HTS221_REG_AVG_ADDR,
|
.addr = 0x10,
|
||||||
.mask = HTS221_HUMIDITY_AVG_MASK,
|
.mask = 0x07,
|
||||||
.avg_avl = {
|
.avg_avl = {
|
||||||
4, /* 0.4 %RH */
|
4, /* 0.4 %RH */
|
||||||
8, /* 0.3 %RH */
|
8, /* 0.3 %RH */
|
||||||
@ -80,8 +73,8 @@ static const struct hts221_avg hts221_avg_list[] = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.addr = HTS221_REG_AVG_ADDR,
|
.addr = 0x10,
|
||||||
.mask = HTS221_TEMP_AVG_MASK,
|
.mask = 0x38,
|
||||||
.avg_avl = {
|
.avg_avl = {
|
||||||
2, /* 0.08 degC */
|
2, /* 0.08 degC */
|
||||||
4, /* 0.05 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[] = {
|
static const struct iio_chan_spec hts221_channels[] = {
|
||||||
{
|
{
|
||||||
.type = IIO_HUMIDITYRELATIVE,
|
.type = IIO_HUMIDITYRELATIVE,
|
||||||
.address = HTS221_REG_H_OUT_L,
|
.address = 0x28,
|
||||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||||
BIT(IIO_CHAN_INFO_OFFSET) |
|
BIT(IIO_CHAN_INFO_OFFSET) |
|
||||||
BIT(IIO_CHAN_INFO_SCALE) |
|
BIT(IIO_CHAN_INFO_SCALE) |
|
||||||
@ -114,7 +107,7 @@ static const struct iio_chan_spec hts221_channels[] = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
.type = IIO_TEMP,
|
.type = IIO_TEMP,
|
||||||
.address = HTS221_REG_T_OUT_L,
|
.address = 0x2a,
|
||||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||||
BIT(IIO_CHAN_INFO_OFFSET) |
|
BIT(IIO_CHAN_INFO_OFFSET) |
|
||||||
BIT(IIO_CHAN_INFO_SCALE) |
|
BIT(IIO_CHAN_INFO_SCALE) |
|
||||||
|
@ -42,14 +42,14 @@ struct poll_table_struct;
|
|||||||
|
|
||||||
__poll_t iio_buffer_poll(struct file *filp,
|
__poll_t iio_buffer_poll(struct file *filp,
|
||||||
struct poll_table_struct *wait);
|
struct poll_table_struct *wait);
|
||||||
ssize_t iio_buffer_read_first_n_outer(struct file *filp, char __user *buf,
|
ssize_t iio_buffer_read_outer(struct file *filp, char __user *buf,
|
||||||
size_t n, loff_t *f_ps);
|
size_t n, loff_t *f_ps);
|
||||||
|
|
||||||
int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev);
|
int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev);
|
||||||
void iio_buffer_free_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_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_disable_all_buffers(struct iio_dev *indio_dev);
|
||||||
void iio_buffer_wakeup_poll(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
|
#else
|
||||||
|
|
||||||
#define iio_buffer_poll_addr NULL
|
#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)
|
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_MSC_CTRL_DATA_RDY_DIO2 BIT(0)
|
||||||
#define ADIS_GLOB_CMD_SW_RESET BIT(7)
|
#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 value, unsigned int size)
|
||||||
{
|
{
|
||||||
unsigned int page = reg / ADIS_PAGE_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,
|
.bits_per_word = 8,
|
||||||
.len = 2,
|
.len = 2,
|
||||||
.cs_change = 1,
|
.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.value = adis->data->cs_change_delay,
|
||||||
.cs_change_delay.unit = SPI_DELAY_UNIT_USECS,
|
.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,
|
.bits_per_word = 8,
|
||||||
.len = 2,
|
.len = 2,
|
||||||
.cs_change = 1,
|
.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.value = adis->data->cs_change_delay,
|
||||||
.cs_change_delay.unit = SPI_DELAY_UNIT_USECS,
|
.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,
|
.bits_per_word = 8,
|
||||||
.len = 2,
|
.len = 2,
|
||||||
.cs_change = 1,
|
.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.value = adis->data->cs_change_delay,
|
||||||
.cs_change_delay.unit = SPI_DELAY_UNIT_USECS,
|
.cs_change_delay.unit = SPI_DELAY_UNIT_USECS,
|
||||||
}, {
|
}, {
|
||||||
.tx_buf = adis->tx + 6,
|
.tx_buf = adis->tx + 6,
|
||||||
.bits_per_word = 8,
|
.bits_per_word = 8,
|
||||||
.len = 2,
|
.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,
|
.tx_buf = adis->tx + 8,
|
||||||
.bits_per_word = 8,
|
.bits_per_word = 8,
|
||||||
.len = 2,
|
.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);
|
spi_message_init(&msg);
|
||||||
|
|
||||||
if (adis->current_page != page) {
|
if (adis->current_page != page) {
|
||||||
@ -96,8 +106,7 @@ int adis_write_reg(struct adis *adis, unsigned int reg,
|
|||||||
adis->tx[3] = value & 0xff;
|
adis->tx[3] = value & 0xff;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
ret = -EINVAL;
|
return -EINVAL;
|
||||||
goto out_unlock;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
xfers[size].cs_change = 0;
|
xfers[size].cs_change = 0;
|
||||||
@ -113,20 +122,18 @@ int adis_write_reg(struct adis *adis, unsigned int reg,
|
|||||||
adis->current_page = page;
|
adis->current_page = page;
|
||||||
}
|
}
|
||||||
|
|
||||||
out_unlock:
|
|
||||||
mutex_unlock(&adis->txrx_lock);
|
|
||||||
|
|
||||||
return ret;
|
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
|
* @adis: The adis device
|
||||||
* @reg: The address of the lower of the two registers
|
* @reg: The address of the lower of the two registers
|
||||||
* @val: The value read back from the device
|
* @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 *val, unsigned int size)
|
||||||
{
|
{
|
||||||
unsigned int page = reg / ADIS_PAGE_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,
|
.bits_per_word = 8,
|
||||||
.len = 2,
|
.len = 2,
|
||||||
.cs_change = 1,
|
.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.value = adis->data->cs_change_delay,
|
||||||
.cs_change_delay.unit = SPI_DELAY_UNIT_USECS,
|
.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,
|
.bits_per_word = 8,
|
||||||
.len = 2,
|
.len = 2,
|
||||||
.cs_change = 1,
|
.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.value = adis->data->cs_change_delay,
|
||||||
.cs_change_delay.unit = SPI_DELAY_UNIT_USECS,
|
.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,
|
.bits_per_word = 8,
|
||||||
.len = 2,
|
.len = 2,
|
||||||
.cs_change = 1,
|
.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.value = adis->data->cs_change_delay,
|
||||||
.cs_change_delay.unit = SPI_DELAY_UNIT_USECS,
|
.cs_change_delay.unit = SPI_DELAY_UNIT_USECS,
|
||||||
}, {
|
}, {
|
||||||
.rx_buf = adis->rx + 2,
|
.rx_buf = adis->rx + 2,
|
||||||
.bits_per_word = 8,
|
.bits_per_word = 8,
|
||||||
.len = 2,
|
.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);
|
spi_message_init(&msg);
|
||||||
|
|
||||||
if (adis->current_page != page) {
|
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);
|
spi_message_add_tail(&xfers[3], &msg);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
ret = -EINVAL;
|
return -EINVAL;
|
||||||
goto out_unlock;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = spi_sync(adis->spi, &msg);
|
ret = spi_sync(adis->spi, &msg);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(&adis->spi->dev, "Failed to read register 0x%02X: %d\n",
|
dev_err(&adis->spi->dev, "Failed to read register 0x%02X: %d\n",
|
||||||
reg, ret);
|
reg, ret);
|
||||||
goto out_unlock;
|
return ret;
|
||||||
} else {
|
} else {
|
||||||
adis->current_page = page;
|
adis->current_page = page;
|
||||||
}
|
}
|
||||||
@ -210,12 +219,9 @@ int adis_read_reg(struct adis *adis, unsigned int reg,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
out_unlock:
|
|
||||||
mutex_unlock(&adis->txrx_lock);
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(adis_read_reg);
|
EXPORT_SYMBOL_GPL(__adis_read_reg);
|
||||||
|
|
||||||
#ifdef CONFIG_DEBUG_FS
|
#ifdef CONFIG_DEBUG_FS
|
||||||
|
|
||||||
@ -253,12 +259,16 @@ int adis_enable_irq(struct adis *adis, bool enable)
|
|||||||
int ret = 0;
|
int ret = 0;
|
||||||
uint16_t msc;
|
uint16_t msc;
|
||||||
|
|
||||||
if (adis->data->enable_irq)
|
mutex_lock(&adis->state_lock);
|
||||||
return adis->data->enable_irq(adis, enable);
|
|
||||||
|
|
||||||
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)
|
if (ret)
|
||||||
goto error_ret;
|
goto out_unlock;
|
||||||
|
|
||||||
msc |= ADIS_MSC_CTRL_DATA_RDY_POL_HIGH;
|
msc |= ADIS_MSC_CTRL_DATA_RDY_POL_HIGH;
|
||||||
msc &= ~ADIS_MSC_CTRL_DATA_RDY_DIO2;
|
msc &= ~ADIS_MSC_CTRL_DATA_RDY_DIO2;
|
||||||
@ -267,26 +277,27 @@ int adis_enable_irq(struct adis *adis, bool enable)
|
|||||||
else
|
else
|
||||||
msc &= ~ADIS_MSC_CTRL_DATA_RDY_EN;
|
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;
|
return ret;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(adis_enable_irq);
|
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
|
* @adis: The adis device
|
||||||
*
|
*
|
||||||
* Returns 0 on success, a negative error code otherwise
|
* 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;
|
uint16_t status;
|
||||||
int ret;
|
int ret;
|
||||||
int i;
|
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)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
@ -304,32 +315,38 @@ int adis_check_status(struct adis *adis)
|
|||||||
|
|
||||||
return -EIO;
|
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
|
* @adis: The adis device
|
||||||
*
|
*
|
||||||
* Returns 0 on success, a negative error code otherwise
|
* Returns 0 on success, a negative error code otherwise
|
||||||
*/
|
*/
|
||||||
int adis_reset(struct adis *adis)
|
int __adis_reset(struct adis *adis)
|
||||||
{
|
{
|
||||||
int ret;
|
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);
|
ADIS_GLOB_CMD_SW_RESET);
|
||||||
if (ret)
|
if (ret) {
|
||||||
dev_err(&adis->spi->dev, "Failed to reset device: %d\n", 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)
|
static int adis_self_test(struct adis *adis)
|
||||||
{
|
{
|
||||||
int ret;
|
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);
|
adis->data->self_test_mask);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(&adis->spi->dev, "Failed to initiate self test: %d\n",
|
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;
|
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)
|
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;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -360,19 +377,22 @@ int adis_initial_startup(struct adis *adis)
|
|||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
mutex_lock(&adis->state_lock);
|
||||||
|
|
||||||
ret = adis_self_test(adis);
|
ret = adis_self_test(adis);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(&adis->spi->dev, "Self-test failed, trying reset.\n");
|
dev_err(&adis->spi->dev, "Self-test failed, trying reset.\n");
|
||||||
adis_reset(adis);
|
__adis_reset(adis);
|
||||||
msleep(adis->data->startup_delay);
|
|
||||||
ret = adis_self_test(adis);
|
ret = adis_self_test(adis);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(&adis->spi->dev, "Second self-test failed, giving up.\n");
|
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);
|
EXPORT_SYMBOL_GPL(adis_initial_startup);
|
||||||
|
|
||||||
@ -398,15 +418,15 @@ int adis_single_conversion(struct iio_dev *indio_dev,
|
|||||||
unsigned int uval;
|
unsigned int uval;
|
||||||
int ret;
|
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);
|
chan->scan_type.storagebits / 8);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_unlock;
|
goto err_unlock;
|
||||||
|
|
||||||
if (uval & error_mask) {
|
if (uval & error_mask) {
|
||||||
ret = adis_check_status(adis);
|
ret = __adis_check_status(adis);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_unlock;
|
goto err_unlock;
|
||||||
}
|
}
|
||||||
@ -418,7 +438,7 @@ int adis_single_conversion(struct iio_dev *indio_dev,
|
|||||||
|
|
||||||
ret = IIO_VAL_INT;
|
ret = IIO_VAL_INT;
|
||||||
err_unlock:
|
err_unlock:
|
||||||
mutex_unlock(&indio_dev->mlock);
|
mutex_unlock(&adis->state_lock);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(adis_single_conversion);
|
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,
|
int adis_init(struct adis *adis, struct iio_dev *indio_dev,
|
||||||
struct spi_device *spi, const struct adis_data *data)
|
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->spi = spi;
|
||||||
adis->data = data;
|
adis->data = data;
|
||||||
iio_device_set_drvdata(indio_dev, adis);
|
iio_device_set_drvdata(indio_dev, adis);
|
||||||
|
@ -156,12 +156,14 @@ struct adis16400_state;
|
|||||||
|
|
||||||
struct adis16400_chip_info {
|
struct adis16400_chip_info {
|
||||||
const struct iio_chan_spec *channels;
|
const struct iio_chan_spec *channels;
|
||||||
|
const struct adis_timeout *timeouts;
|
||||||
const int num_channels;
|
const int num_channels;
|
||||||
const long flags;
|
const long flags;
|
||||||
unsigned int gyro_scale_micro;
|
unsigned int gyro_scale_micro;
|
||||||
unsigned int accel_scale_micro;
|
unsigned int accel_scale_micro;
|
||||||
int temp_scale_nano;
|
int temp_scale_nano;
|
||||||
int temp_offset;
|
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 (*set_freq)(struct adis16400_state *st, unsigned int freq);
|
||||||
int (*get_freq)(struct adis16400_state *st);
|
int (*get_freq)(struct adis16400_state *st);
|
||||||
};
|
};
|
||||||
@ -326,7 +328,7 @@ static int adis16334_get_freq(struct adis16400_state *st)
|
|||||||
int ret;
|
int ret;
|
||||||
uint16_t t;
|
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)
|
if (ret)
|
||||||
return 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_DIV_SHIFT;
|
||||||
t |= ADIS16334_RATE_INT_CLK;
|
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)
|
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;
|
int sps, ret;
|
||||||
uint16_t t;
|
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)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
@ -390,7 +392,7 @@ static int adis16400_set_freq(struct adis16400_state *st, unsigned int freq)
|
|||||||
else
|
else
|
||||||
st->adis.spi->max_speed_hz = ADIS16400_SPI_FAST;
|
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[] = {
|
static const unsigned int adis16400_3db_divisors[] = {
|
||||||
@ -404,7 +406,7 @@ static const unsigned int adis16400_3db_divisors[] = {
|
|||||||
[7] = 200, /* Not a valid setting */
|
[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);
|
struct adis16400_state *st = iio_priv(indio_dev);
|
||||||
uint16_t val16;
|
uint16_t val16;
|
||||||
@ -415,11 +417,11 @@ static int adis16400_set_filter(struct iio_dev *indio_dev, int sps, int val)
|
|||||||
break;
|
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)
|
if (ret)
|
||||||
return 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);
|
(val16 & ~0x07) | i);
|
||||||
return ret;
|
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 iio_chan_spec const *chan, int val, int val2, long info)
|
||||||
{
|
{
|
||||||
struct adis16400_state *st = iio_priv(indio_dev);
|
struct adis16400_state *st = iio_priv(indio_dev);
|
||||||
|
struct mutex *slock = &st->adis.state_lock;
|
||||||
int ret, sps;
|
int ret, sps;
|
||||||
|
|
||||||
switch (info) {
|
switch (info) {
|
||||||
case IIO_CHAN_INFO_CALIBBIAS:
|
case IIO_CHAN_INFO_CALIBBIAS:
|
||||||
mutex_lock(&indio_dev->mlock);
|
|
||||||
ret = adis_write_reg_16(&st->adis,
|
ret = adis_write_reg_16(&st->adis,
|
||||||
adis16400_addresses[chan->scan_index], val);
|
adis16400_addresses[chan->scan_index], val);
|
||||||
mutex_unlock(&indio_dev->mlock);
|
|
||||||
return ret;
|
return ret;
|
||||||
case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
|
case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
|
||||||
/*
|
/*
|
||||||
* Need to cache values so we can update if the frequency
|
* Need to cache values so we can update if the frequency
|
||||||
* changes.
|
* changes.
|
||||||
*/
|
*/
|
||||||
mutex_lock(&indio_dev->mlock);
|
mutex_lock(slock);
|
||||||
st->filt_int = val;
|
st->filt_int = val;
|
||||||
/* Work out update to current value */
|
/* Work out update to current value */
|
||||||
sps = st->variant->get_freq(st);
|
sps = st->variant->get_freq(st);
|
||||||
if (sps < 0) {
|
if (sps < 0) {
|
||||||
mutex_unlock(&indio_dev->mlock);
|
mutex_unlock(slock);
|
||||||
return sps;
|
return sps;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = adis16400_set_filter(indio_dev, sps,
|
ret = __adis16400_set_filter(indio_dev, sps,
|
||||||
val * 1000 + val2 / 1000);
|
val * 1000 + val2 / 1000);
|
||||||
mutex_unlock(&indio_dev->mlock);
|
mutex_unlock(slock);
|
||||||
return ret;
|
return ret;
|
||||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||||
sps = val * 1000 + val2 / 1000;
|
sps = val * 1000 + val2 / 1000;
|
||||||
@ -540,9 +541,9 @@ static int adis16400_write_raw(struct iio_dev *indio_dev,
|
|||||||
if (sps <= 0)
|
if (sps <= 0)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
mutex_lock(&indio_dev->mlock);
|
mutex_lock(slock);
|
||||||
ret = st->variant->set_freq(st, sps);
|
ret = st->variant->set_freq(st, sps);
|
||||||
mutex_unlock(&indio_dev->mlock);
|
mutex_unlock(slock);
|
||||||
return ret;
|
return ret;
|
||||||
default:
|
default:
|
||||||
return -EINVAL;
|
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 iio_chan_spec const *chan, int *val, int *val2, long info)
|
||||||
{
|
{
|
||||||
struct adis16400_state *st = iio_priv(indio_dev);
|
struct adis16400_state *st = iio_priv(indio_dev);
|
||||||
|
struct mutex *slock = &st->adis.state_lock;
|
||||||
int16_t val16;
|
int16_t val16;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
@ -596,10 +598,8 @@ static int adis16400_read_raw(struct iio_dev *indio_dev,
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
case IIO_CHAN_INFO_CALIBBIAS:
|
case IIO_CHAN_INFO_CALIBBIAS:
|
||||||
mutex_lock(&indio_dev->mlock);
|
|
||||||
ret = adis_read_reg_16(&st->adis,
|
ret = adis_read_reg_16(&st->adis,
|
||||||
adis16400_addresses[chan->scan_index], &val16);
|
adis16400_addresses[chan->scan_index], &val16);
|
||||||
mutex_unlock(&indio_dev->mlock);
|
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
val16 = sign_extend32(val16, 11);
|
val16 = sign_extend32(val16, 11);
|
||||||
@ -610,27 +610,27 @@ static int adis16400_read_raw(struct iio_dev *indio_dev,
|
|||||||
*val = st->variant->temp_offset;
|
*val = st->variant->temp_offset;
|
||||||
return IIO_VAL_INT;
|
return IIO_VAL_INT;
|
||||||
case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
|
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 */
|
/* 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,
|
ADIS16400_SENS_AVG,
|
||||||
&val16);
|
&val16);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
mutex_unlock(&indio_dev->mlock);
|
mutex_unlock(slock);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
ret = st->variant->get_freq(st);
|
ret = st->variant->get_freq(st);
|
||||||
if (ret >= 0) {
|
mutex_unlock(slock);
|
||||||
ret /= adis16400_3db_divisors[val16 & 0x07];
|
|
||||||
*val = ret / 1000;
|
|
||||||
*val2 = (ret % 1000) * 1000;
|
|
||||||
}
|
|
||||||
mutex_unlock(&indio_dev->mlock);
|
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
ret /= adis16400_3db_divisors[val16 & 0x07];
|
||||||
|
*val = ret / 1000;
|
||||||
|
*val2 = (ret % 1000) * 1000;
|
||||||
return IIO_VAL_INT_PLUS_MICRO;
|
return IIO_VAL_INT_PLUS_MICRO;
|
||||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||||
|
mutex_lock(slock);
|
||||||
ret = st->variant->get_freq(st);
|
ret = st->variant->get_freq(st);
|
||||||
|
mutex_unlock(slock);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
*val = ret / 1000;
|
*val = ret / 1000;
|
||||||
@ -930,6 +930,36 @@ static const struct iio_chan_spec adis16334_channels[] = {
|
|||||||
IIO_CHAN_SOFT_TIMESTAMP(ADIS16400_SCAN_TIMESTAMP),
|
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[] = {
|
static struct adis16400_chip_info adis16400_chips[] = {
|
||||||
[ADIS16300] = {
|
[ADIS16300] = {
|
||||||
.channels = adis16300_channels,
|
.channels = adis16300_channels,
|
||||||
@ -942,6 +972,7 @@ static struct adis16400_chip_info adis16400_chips[] = {
|
|||||||
.temp_offset = 25000000 / 140000, /* 25 C = 0x00 */
|
.temp_offset = 25000000 / 140000, /* 25 C = 0x00 */
|
||||||
.set_freq = adis16400_set_freq,
|
.set_freq = adis16400_set_freq,
|
||||||
.get_freq = adis16400_get_freq,
|
.get_freq = adis16400_get_freq,
|
||||||
|
.timeouts = &adis16300_timeouts,
|
||||||
},
|
},
|
||||||
[ADIS16334] = {
|
[ADIS16334] = {
|
||||||
.channels = adis16334_channels,
|
.channels = adis16334_channels,
|
||||||
@ -965,6 +996,7 @@ static struct adis16400_chip_info adis16400_chips[] = {
|
|||||||
.flags = ADIS16400_NO_BURST | ADIS16400_HAS_SLOW_MODE,
|
.flags = ADIS16400_NO_BURST | ADIS16400_HAS_SLOW_MODE,
|
||||||
.set_freq = adis16400_set_freq,
|
.set_freq = adis16400_set_freq,
|
||||||
.get_freq = adis16400_get_freq,
|
.get_freq = adis16400_get_freq,
|
||||||
|
.timeouts = &adis16300_timeouts,
|
||||||
},
|
},
|
||||||
[ADIS16360] = {
|
[ADIS16360] = {
|
||||||
.channels = adis16350_channels,
|
.channels = adis16350_channels,
|
||||||
@ -977,6 +1009,7 @@ static struct adis16400_chip_info adis16400_chips[] = {
|
|||||||
.temp_offset = 25000000 / 136000, /* 25 C = 0x00 */
|
.temp_offset = 25000000 / 136000, /* 25 C = 0x00 */
|
||||||
.set_freq = adis16400_set_freq,
|
.set_freq = adis16400_set_freq,
|
||||||
.get_freq = adis16400_get_freq,
|
.get_freq = adis16400_get_freq,
|
||||||
|
.timeouts = &adis16300_timeouts,
|
||||||
},
|
},
|
||||||
[ADIS16362] = {
|
[ADIS16362] = {
|
||||||
.channels = adis16350_channels,
|
.channels = adis16350_channels,
|
||||||
@ -989,6 +1022,7 @@ static struct adis16400_chip_info adis16400_chips[] = {
|
|||||||
.temp_offset = 25000000 / 136000, /* 25 C = 0x00 */
|
.temp_offset = 25000000 / 136000, /* 25 C = 0x00 */
|
||||||
.set_freq = adis16400_set_freq,
|
.set_freq = adis16400_set_freq,
|
||||||
.get_freq = adis16400_get_freq,
|
.get_freq = adis16400_get_freq,
|
||||||
|
.timeouts = &adis16362_timeouts,
|
||||||
},
|
},
|
||||||
[ADIS16364] = {
|
[ADIS16364] = {
|
||||||
.channels = adis16350_channels,
|
.channels = adis16350_channels,
|
||||||
@ -1001,6 +1035,7 @@ static struct adis16400_chip_info adis16400_chips[] = {
|
|||||||
.temp_offset = 25000000 / 136000, /* 25 C = 0x00 */
|
.temp_offset = 25000000 / 136000, /* 25 C = 0x00 */
|
||||||
.set_freq = adis16400_set_freq,
|
.set_freq = adis16400_set_freq,
|
||||||
.get_freq = adis16400_get_freq,
|
.get_freq = adis16400_get_freq,
|
||||||
|
.timeouts = &adis16362_timeouts,
|
||||||
},
|
},
|
||||||
[ADIS16367] = {
|
[ADIS16367] = {
|
||||||
.channels = adis16350_channels,
|
.channels = adis16350_channels,
|
||||||
@ -1013,6 +1048,7 @@ static struct adis16400_chip_info adis16400_chips[] = {
|
|||||||
.temp_offset = 25000000 / 136000, /* 25 C = 0x00 */
|
.temp_offset = 25000000 / 136000, /* 25 C = 0x00 */
|
||||||
.set_freq = adis16400_set_freq,
|
.set_freq = adis16400_set_freq,
|
||||||
.get_freq = adis16400_get_freq,
|
.get_freq = adis16400_get_freq,
|
||||||
|
.timeouts = &adis16300_timeouts,
|
||||||
},
|
},
|
||||||
[ADIS16400] = {
|
[ADIS16400] = {
|
||||||
.channels = adis16400_channels,
|
.channels = adis16400_channels,
|
||||||
@ -1024,6 +1060,7 @@ static struct adis16400_chip_info adis16400_chips[] = {
|
|||||||
.temp_offset = 25000000 / 140000, /* 25 C = 0x00 */
|
.temp_offset = 25000000 / 140000, /* 25 C = 0x00 */
|
||||||
.set_freq = adis16400_set_freq,
|
.set_freq = adis16400_set_freq,
|
||||||
.get_freq = adis16400_get_freq,
|
.get_freq = adis16400_get_freq,
|
||||||
|
.timeouts = &adis16400_timeouts,
|
||||||
},
|
},
|
||||||
[ADIS16445] = {
|
[ADIS16445] = {
|
||||||
.channels = adis16445_channels,
|
.channels = adis16445_channels,
|
||||||
@ -1037,6 +1074,7 @@ static struct adis16400_chip_info adis16400_chips[] = {
|
|||||||
.temp_offset = 31000000 / 73860, /* 31 C = 0x00 */
|
.temp_offset = 31000000 / 73860, /* 31 C = 0x00 */
|
||||||
.set_freq = adis16334_set_freq,
|
.set_freq = adis16334_set_freq,
|
||||||
.get_freq = adis16334_get_freq,
|
.get_freq = adis16334_get_freq,
|
||||||
|
.timeouts = &adis16445_timeouts,
|
||||||
},
|
},
|
||||||
[ADIS16448] = {
|
[ADIS16448] = {
|
||||||
.channels = adis16448_channels,
|
.channels = adis16448_channels,
|
||||||
@ -1050,6 +1088,7 @@ static struct adis16400_chip_info adis16400_chips[] = {
|
|||||||
.temp_offset = 31000000 / 73860, /* 31 C = 0x00 */
|
.temp_offset = 31000000 / 73860, /* 31 C = 0x00 */
|
||||||
.set_freq = adis16334_set_freq,
|
.set_freq = adis16334_set_freq,
|
||||||
.get_freq = adis16334_get_freq,
|
.get_freq = adis16334_get_freq,
|
||||||
|
.timeouts = &adis16448_timeouts,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1087,7 +1126,6 @@ static const struct adis_data adis16400_data = {
|
|||||||
.write_delay = 50,
|
.write_delay = 50,
|
||||||
|
|
||||||
.self_test_mask = ADIS16400_MSC_CTRL_MEM_TEST,
|
.self_test_mask = ADIS16400_MSC_CTRL_MEM_TEST,
|
||||||
.startup_delay = ADIS16400_STARTUP_DELAY,
|
|
||||||
|
|
||||||
.status_error_msgs = adis16400_status_error_msgs,
|
.status_error_msgs = adis16400_status_error_msgs,
|
||||||
.status_error_mask = BIT(ADIS16400_DIAG_STAT_ZACCL_FAIL) |
|
.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)
|
static int adis16400_probe(struct spi_device *spi)
|
||||||
{
|
{
|
||||||
struct adis16400_state *st;
|
struct adis16400_state *st;
|
||||||
struct iio_dev *indio_dev;
|
struct iio_dev *indio_dev;
|
||||||
int ret;
|
int ret;
|
||||||
|
const struct adis_data *adis16400_data;
|
||||||
|
|
||||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
||||||
if (indio_dev == NULL)
|
if (indio_dev == NULL)
|
||||||
@ -1152,7 +1207,11 @@ static int adis16400_probe(struct spi_device *spi)
|
|||||||
st->adis.burst->extra_len = sizeof(u16);
|
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)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
@ -383,6 +383,12 @@ static const char * const adis16460_status_error_msgs[] = {
|
|||||||
[ADIS16460_DIAG_STAT_FLASH_UPT] = "Flash update failure",
|
[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 = {
|
static const struct adis_data adis16460_data = {
|
||||||
.diag_stat_reg = ADIS16460_REG_DIAG_STAT,
|
.diag_stat_reg = ADIS16460_REG_DIAG_STAT,
|
||||||
.glob_cmd_reg = ADIS16460_REG_GLOB_CMD,
|
.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_SPI_COMM) |
|
||||||
BIT(ADIS16460_DIAG_STAT_FLASH_UPT),
|
BIT(ADIS16460_DIAG_STAT_FLASH_UPT),
|
||||||
.enable_irq = adis16460_enable_irq,
|
.enable_irq = adis16460_enable_irq,
|
||||||
|
.timeouts = &adis16460_timeouts,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int adis16460_probe(struct spi_device *spi)
|
static int adis16460_probe(struct spi_device *spi)
|
||||||
|
@ -138,6 +138,7 @@ struct adis16480_chip_info {
|
|||||||
unsigned int max_dec_rate;
|
unsigned int max_dec_rate;
|
||||||
const unsigned int *filter_freqs;
|
const unsigned int *filter_freqs;
|
||||||
bool has_pps_clk_mode;
|
bool has_pps_clk_mode;
|
||||||
|
const struct adis_timeout *timeouts;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum adis16480_int_pin {
|
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)
|
const struct iio_chan_spec *chan, unsigned int freq)
|
||||||
{
|
{
|
||||||
struct adis16480 *st = iio_priv(indio_dev);
|
struct adis16480 *st = iio_priv(indio_dev);
|
||||||
|
struct mutex *slock = &st->adis.state_lock;
|
||||||
unsigned int enable_mask, offset, reg;
|
unsigned int enable_mask, offset, reg;
|
||||||
unsigned int diff, best_diff;
|
unsigned int diff, best_diff;
|
||||||
unsigned int i, best_freq;
|
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];
|
offset = ad16480_filter_data[chan->scan_index][1];
|
||||||
enable_mask = BIT(offset + 2);
|
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)
|
if (ret)
|
||||||
return ret;
|
goto out_unlock;
|
||||||
|
|
||||||
if (freq == 0) {
|
if (freq == 0) {
|
||||||
val &= ~enable_mask;
|
val &= ~enable_mask;
|
||||||
@ -589,7 +593,11 @@ static int adis16480_set_filter_freq(struct iio_dev *indio_dev,
|
|||||||
val |= enable_mask;
|
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,
|
static int adis16480_read_raw(struct iio_dev *indio_dev,
|
||||||
@ -779,6 +787,7 @@ enum adis16480_variant {
|
|||||||
ADIS16480,
|
ADIS16480,
|
||||||
ADIS16485,
|
ADIS16485,
|
||||||
ADIS16488,
|
ADIS16488,
|
||||||
|
ADIS16490,
|
||||||
ADIS16495_1,
|
ADIS16495_1,
|
||||||
ADIS16495_2,
|
ADIS16495_2,
|
||||||
ADIS16495_3,
|
ADIS16495_3,
|
||||||
@ -787,6 +796,30 @@ enum adis16480_variant {
|
|||||||
ADIS16497_3,
|
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[] = {
|
static const struct adis16480_chip_info adis16480_chip_info[] = {
|
||||||
[ADIS16375] = {
|
[ADIS16375] = {
|
||||||
.channels = adis16485_channels,
|
.channels = adis16485_channels,
|
||||||
@ -805,6 +838,7 @@ static const struct adis16480_chip_info adis16480_chip_info[] = {
|
|||||||
.int_clk = 2460000,
|
.int_clk = 2460000,
|
||||||
.max_dec_rate = 2048,
|
.max_dec_rate = 2048,
|
||||||
.filter_freqs = adis16480_def_filter_freqs,
|
.filter_freqs = adis16480_def_filter_freqs,
|
||||||
|
.timeouts = &adis16485_timeouts,
|
||||||
},
|
},
|
||||||
[ADIS16480] = {
|
[ADIS16480] = {
|
||||||
.channels = adis16480_channels,
|
.channels = adis16480_channels,
|
||||||
@ -817,6 +851,7 @@ static const struct adis16480_chip_info adis16480_chip_info[] = {
|
|||||||
.int_clk = 2460000,
|
.int_clk = 2460000,
|
||||||
.max_dec_rate = 2048,
|
.max_dec_rate = 2048,
|
||||||
.filter_freqs = adis16480_def_filter_freqs,
|
.filter_freqs = adis16480_def_filter_freqs,
|
||||||
|
.timeouts = &adis16480_timeouts,
|
||||||
},
|
},
|
||||||
[ADIS16485] = {
|
[ADIS16485] = {
|
||||||
.channels = adis16485_channels,
|
.channels = adis16485_channels,
|
||||||
@ -829,6 +864,7 @@ static const struct adis16480_chip_info adis16480_chip_info[] = {
|
|||||||
.int_clk = 2460000,
|
.int_clk = 2460000,
|
||||||
.max_dec_rate = 2048,
|
.max_dec_rate = 2048,
|
||||||
.filter_freqs = adis16480_def_filter_freqs,
|
.filter_freqs = adis16480_def_filter_freqs,
|
||||||
|
.timeouts = &adis16485_timeouts,
|
||||||
},
|
},
|
||||||
[ADIS16488] = {
|
[ADIS16488] = {
|
||||||
.channels = adis16480_channels,
|
.channels = adis16480_channels,
|
||||||
@ -841,6 +877,21 @@ static const struct adis16480_chip_info adis16480_chip_info[] = {
|
|||||||
.int_clk = 2460000,
|
.int_clk = 2460000,
|
||||||
.max_dec_rate = 2048,
|
.max_dec_rate = 2048,
|
||||||
.filter_freqs = adis16480_def_filter_freqs,
|
.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] = {
|
[ADIS16495_1] = {
|
||||||
.channels = adis16485_channels,
|
.channels = adis16485_channels,
|
||||||
@ -854,6 +905,7 @@ static const struct adis16480_chip_info adis16480_chip_info[] = {
|
|||||||
.max_dec_rate = 4250,
|
.max_dec_rate = 4250,
|
||||||
.filter_freqs = adis16495_def_filter_freqs,
|
.filter_freqs = adis16495_def_filter_freqs,
|
||||||
.has_pps_clk_mode = true,
|
.has_pps_clk_mode = true,
|
||||||
|
.timeouts = &adis16495_1_timeouts,
|
||||||
},
|
},
|
||||||
[ADIS16495_2] = {
|
[ADIS16495_2] = {
|
||||||
.channels = adis16485_channels,
|
.channels = adis16485_channels,
|
||||||
@ -867,6 +919,7 @@ static const struct adis16480_chip_info adis16480_chip_info[] = {
|
|||||||
.max_dec_rate = 4250,
|
.max_dec_rate = 4250,
|
||||||
.filter_freqs = adis16495_def_filter_freqs,
|
.filter_freqs = adis16495_def_filter_freqs,
|
||||||
.has_pps_clk_mode = true,
|
.has_pps_clk_mode = true,
|
||||||
|
.timeouts = &adis16495_1_timeouts,
|
||||||
},
|
},
|
||||||
[ADIS16495_3] = {
|
[ADIS16495_3] = {
|
||||||
.channels = adis16485_channels,
|
.channels = adis16485_channels,
|
||||||
@ -880,6 +933,7 @@ static const struct adis16480_chip_info adis16480_chip_info[] = {
|
|||||||
.max_dec_rate = 4250,
|
.max_dec_rate = 4250,
|
||||||
.filter_freqs = adis16495_def_filter_freqs,
|
.filter_freqs = adis16495_def_filter_freqs,
|
||||||
.has_pps_clk_mode = true,
|
.has_pps_clk_mode = true,
|
||||||
|
.timeouts = &adis16495_1_timeouts,
|
||||||
},
|
},
|
||||||
[ADIS16497_1] = {
|
[ADIS16497_1] = {
|
||||||
.channels = adis16485_channels,
|
.channels = adis16485_channels,
|
||||||
@ -893,6 +947,7 @@ static const struct adis16480_chip_info adis16480_chip_info[] = {
|
|||||||
.max_dec_rate = 4250,
|
.max_dec_rate = 4250,
|
||||||
.filter_freqs = adis16495_def_filter_freqs,
|
.filter_freqs = adis16495_def_filter_freqs,
|
||||||
.has_pps_clk_mode = true,
|
.has_pps_clk_mode = true,
|
||||||
|
.timeouts = &adis16495_1_timeouts,
|
||||||
},
|
},
|
||||||
[ADIS16497_2] = {
|
[ADIS16497_2] = {
|
||||||
.channels = adis16485_channels,
|
.channels = adis16485_channels,
|
||||||
@ -906,6 +961,7 @@ static const struct adis16480_chip_info adis16480_chip_info[] = {
|
|||||||
.max_dec_rate = 4250,
|
.max_dec_rate = 4250,
|
||||||
.filter_freqs = adis16495_def_filter_freqs,
|
.filter_freqs = adis16495_def_filter_freqs,
|
||||||
.has_pps_clk_mode = true,
|
.has_pps_clk_mode = true,
|
||||||
|
.timeouts = &adis16495_1_timeouts,
|
||||||
},
|
},
|
||||||
[ADIS16497_3] = {
|
[ADIS16497_3] = {
|
||||||
.channels = adis16485_channels,
|
.channels = adis16485_channels,
|
||||||
@ -919,6 +975,7 @@ static const struct adis16480_chip_info adis16480_chip_info[] = {
|
|||||||
.max_dec_rate = 4250,
|
.max_dec_rate = 4250,
|
||||||
.filter_freqs = adis16495_def_filter_freqs,
|
.filter_freqs = adis16495_def_filter_freqs,
|
||||||
.has_pps_clk_mode = true,
|
.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;
|
uint16_t val;
|
||||||
int ret;
|
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)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
val &= ~ADIS16480_DRDY_EN_MSK;
|
val &= ~ADIS16480_DRDY_EN_MSK;
|
||||||
val |= ADIS16480_DRDY_EN(enable);
|
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)
|
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;
|
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)
|
static int adis16480_probe(struct spi_device *spi)
|
||||||
{
|
{
|
||||||
const struct spi_device_id *id = spi_get_device_id(spi);
|
const struct spi_device_id *id = spi_get_device_id(spi);
|
||||||
|
const struct adis_data *adis16480_data;
|
||||||
struct iio_dev *indio_dev;
|
struct iio_dev *indio_dev;
|
||||||
struct adis16480 *st;
|
struct adis16480 *st;
|
||||||
int ret;
|
int ret;
|
||||||
@ -1211,7 +1285,11 @@ static int adis16480_probe(struct spi_device *spi)
|
|||||||
indio_dev->info = &adis16480_info;
|
indio_dev->info = &adis16480_info;
|
||||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
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)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
@ -1278,6 +1356,7 @@ static const struct spi_device_id adis16480_ids[] = {
|
|||||||
{ "adis16480", ADIS16480 },
|
{ "adis16480", ADIS16480 },
|
||||||
{ "adis16485", ADIS16485 },
|
{ "adis16485", ADIS16485 },
|
||||||
{ "adis16488", ADIS16488 },
|
{ "adis16488", ADIS16488 },
|
||||||
|
{ "adis16490", ADIS16490 },
|
||||||
{ "adis16495-1", ADIS16495_1 },
|
{ "adis16495-1", ADIS16495_1 },
|
||||||
{ "adis16495-2", ADIS16495_2 },
|
{ "adis16495-2", ADIS16495_2 },
|
||||||
{ "adis16495-3", ADIS16495_3 },
|
{ "adis16495-3", ADIS16495_3 },
|
||||||
@ -1293,6 +1372,7 @@ static const struct of_device_id adis16480_of_match[] = {
|
|||||||
{ .compatible = "adi,adis16480" },
|
{ .compatible = "adi,adis16480" },
|
||||||
{ .compatible = "adi,adis16485" },
|
{ .compatible = "adi,adis16485" },
|
||||||
{ .compatible = "adi,adis16488" },
|
{ .compatible = "adi,adis16488" },
|
||||||
|
{ .compatible = "adi,adis16490" },
|
||||||
{ .compatible = "adi,adis16495-1" },
|
{ .compatible = "adi,adis16495-1" },
|
||||||
{ .compatible = "adi,adis16495-2" },
|
{ .compatible = "adi,adis16495-2" },
|
||||||
{ .compatible = "adi,adis16495-3" },
|
{ .compatible = "adi,adis16495-3" },
|
||||||
|
@ -129,7 +129,7 @@ static irqreturn_t adis_trigger_handler(int irq, void *p)
|
|||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
if (adis->data->has_paging) {
|
if (adis->data->has_paging) {
|
||||||
mutex_lock(&adis->txrx_lock);
|
mutex_lock(&adis->state_lock);
|
||||||
if (adis->current_page != 0) {
|
if (adis->current_page != 0) {
|
||||||
adis->tx[0] = ADIS_WRITE_REG(ADIS_REG_PAGE_ID);
|
adis->tx[0] = ADIS_WRITE_REG(ADIS_REG_PAGE_ID);
|
||||||
adis->tx[1] = 0;
|
adis->tx[1] = 0;
|
||||||
@ -144,7 +144,7 @@ static irqreturn_t adis_trigger_handler(int irq, void *p)
|
|||||||
|
|
||||||
if (adis->data->has_paging) {
|
if (adis->data->has_paging) {
|
||||||
adis->current_page = 0;
|
adis->current_page = 0;
|
||||||
mutex_unlock(&adis->txrx_lock);
|
mutex_unlock(&adis->state_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
iio_push_to_buffers_with_timestamp(indio_dev, adis->buffer,
|
iio_push_to_buffers_with_timestamp(indio_dev, adis->buffer,
|
||||||
|
@ -10,11 +10,12 @@ config INV_MPU6050_IIO
|
|||||||
|
|
||||||
config INV_MPU6050_I2C
|
config INV_MPU6050_I2C
|
||||||
tristate "Invensense MPU6050 devices (I2C)"
|
tristate "Invensense MPU6050 devices (I2C)"
|
||||||
depends on I2C_MUX
|
depends on I2C
|
||||||
|
select I2C_MUX
|
||||||
select INV_MPU6050_IIO
|
select INV_MPU6050_IIO
|
||||||
select REGMAP_I2C
|
select REGMAP_I2C
|
||||||
help
|
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
|
MPU9150/9250/9255 and ICM20608/20602 motion tracking devices
|
||||||
over I2C.
|
over I2C.
|
||||||
This driver can be built as a module. The module will be called
|
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 INV_MPU6050_IIO
|
||||||
select REGMAP_SPI
|
select REGMAP_SPI
|
||||||
help
|
help
|
||||||
This driver supports the Invensense MPU6000/6050/6500/6515,
|
This driver supports the Invensense MPU6000/6500/6515,
|
||||||
MPU9150/9250/9255 and ICM20608/20602 motion tracking devices
|
MPU9250/9255 and ICM20608/20602 motion tracking devices
|
||||||
over SPI.
|
over SPI.
|
||||||
This driver can be built as a module. The module will be called
|
This driver can be built as a module. The module will be called
|
||||||
inv-mpu6050-spi.
|
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),
|
.divider = INV_MPU6050_FIFO_RATE_TO_DIVIDER(INV_MPU6050_INIT_FIFO_RATE),
|
||||||
.gyro_fifo_enable = false,
|
.gyro_fifo_enable = false,
|
||||||
.accl_fifo_enable = false,
|
.accl_fifo_enable = false,
|
||||||
|
.temp_fifo_enable = false,
|
||||||
.magn_fifo_enable = false,
|
.magn_fifo_enable = false,
|
||||||
.accl_fs = INV_MPU6050_FS_02G,
|
.accl_fs = INV_MPU6050_FS_02G,
|
||||||
.user_ctrl = 0,
|
.user_ctrl = 0,
|
||||||
@ -856,19 +857,27 @@ static const struct iio_chan_spec_ext_info inv_ext_info[] = {
|
|||||||
.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[] = {
|
static const struct iio_chan_spec inv_mpu_channels[] = {
|
||||||
IIO_CHAN_SOFT_TIMESTAMP(INV_MPU6050_SCAN_TIMESTAMP),
|
IIO_CHAN_SOFT_TIMESTAMP(INV_MPU6050_SCAN_TIMESTAMP),
|
||||||
/*
|
|
||||||
* Note that temperature should only be via polled reading only,
|
INV_MPU6050_TEMP_CHAN(INV_MPU6050_SCAN_TEMP),
|
||||||
* 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_CHAN(IIO_ANGL_VEL, IIO_MOD_X, INV_MPU6050_SCAN_GYRO_X),
|
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_Y, INV_MPU6050_SCAN_GYRO_Y),
|
||||||
INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_Z, INV_MPU6050_SCAN_GYRO_Z),
|
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),
|
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[] = {
|
static const unsigned long inv_mpu_scan_masks[] = {
|
||||||
/* 3-axis accel */
|
/* 3-axis accel */
|
||||||
BIT(INV_MPU6050_SCAN_ACCL_X)
|
INV_MPU6050_SCAN_MASK_3AXIS_ACCEL,
|
||||||
| BIT(INV_MPU6050_SCAN_ACCL_Y)
|
INV_MPU6050_SCAN_MASK_3AXIS_ACCEL | INV_MPU6050_SCAN_MASK_TEMP,
|
||||||
| BIT(INV_MPU6050_SCAN_ACCL_Z),
|
|
||||||
/* 3-axis gyro */
|
/* 3-axis gyro */
|
||||||
BIT(INV_MPU6050_SCAN_GYRO_X)
|
INV_MPU6050_SCAN_MASK_3AXIS_GYRO,
|
||||||
| BIT(INV_MPU6050_SCAN_GYRO_Y)
|
INV_MPU6050_SCAN_MASK_3AXIS_GYRO | INV_MPU6050_SCAN_MASK_TEMP,
|
||||||
| BIT(INV_MPU6050_SCAN_GYRO_Z),
|
|
||||||
/* 6-axis accel + gyro */
|
/* 6-axis accel + gyro */
|
||||||
BIT(INV_MPU6050_SCAN_ACCL_X)
|
INV_MPU6050_SCAN_MASK_3AXIS_ACCEL | INV_MPU6050_SCAN_MASK_3AXIS_GYRO,
|
||||||
| BIT(INV_MPU6050_SCAN_ACCL_Y)
|
INV_MPU6050_SCAN_MASK_3AXIS_ACCEL | INV_MPU6050_SCAN_MASK_3AXIS_GYRO
|
||||||
| BIT(INV_MPU6050_SCAN_ACCL_Z)
|
| INV_MPU6050_SCAN_MASK_TEMP,
|
||||||
| BIT(INV_MPU6050_SCAN_GYRO_X)
|
|
||||||
| BIT(INV_MPU6050_SCAN_GYRO_Y)
|
|
||||||
| BIT(INV_MPU6050_SCAN_GYRO_Z),
|
|
||||||
0,
|
0,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -915,19 +931,30 @@ static const unsigned long inv_mpu_scan_masks[] = {
|
|||||||
.ext_info = inv_ext_info, \
|
.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[] = {
|
static const struct iio_chan_spec inv_mpu9250_channels[] = {
|
||||||
IIO_CHAN_SOFT_TIMESTAMP(INV_MPU9X50_SCAN_TIMESTAMP),
|
IIO_CHAN_SOFT_TIMESTAMP(INV_MPU9X50_SCAN_TIMESTAMP),
|
||||||
/*
|
|
||||||
* Note that temperature should only be via polled reading only,
|
INV_MPU6050_TEMP_CHAN(INV_MPU6050_SCAN_TEMP),
|
||||||
* 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_CHAN(IIO_ANGL_VEL, IIO_MOD_X, INV_MPU6050_SCAN_GYRO_X),
|
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_Y, INV_MPU6050_SCAN_GYRO_Y),
|
||||||
INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_Z, INV_MPU6050_SCAN_GYRO_Z),
|
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),
|
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[] = {
|
static const unsigned long inv_mpu9x50_scan_masks[] = {
|
||||||
/* 3-axis accel */
|
/* 3-axis accel */
|
||||||
BIT(INV_MPU6050_SCAN_ACCL_X)
|
INV_MPU6050_SCAN_MASK_3AXIS_ACCEL,
|
||||||
| BIT(INV_MPU6050_SCAN_ACCL_Y)
|
INV_MPU6050_SCAN_MASK_3AXIS_ACCEL | INV_MPU6050_SCAN_MASK_TEMP,
|
||||||
| BIT(INV_MPU6050_SCAN_ACCL_Z),
|
|
||||||
/* 3-axis gyro */
|
/* 3-axis gyro */
|
||||||
BIT(INV_MPU6050_SCAN_GYRO_X)
|
INV_MPU6050_SCAN_MASK_3AXIS_GYRO,
|
||||||
| BIT(INV_MPU6050_SCAN_GYRO_Y)
|
INV_MPU6050_SCAN_MASK_3AXIS_GYRO | INV_MPU6050_SCAN_MASK_TEMP,
|
||||||
| BIT(INV_MPU6050_SCAN_GYRO_Z),
|
|
||||||
/* 3-axis magn */
|
/* 3-axis magn */
|
||||||
BIT(INV_MPU9X50_SCAN_MAGN_X)
|
INV_MPU9X50_SCAN_MASK_3AXIS_MAGN,
|
||||||
| BIT(INV_MPU9X50_SCAN_MAGN_Y)
|
INV_MPU9X50_SCAN_MASK_3AXIS_MAGN | INV_MPU6050_SCAN_MASK_TEMP,
|
||||||
| BIT(INV_MPU9X50_SCAN_MAGN_Z),
|
|
||||||
/* 6-axis accel + gyro */
|
/* 6-axis accel + gyro */
|
||||||
BIT(INV_MPU6050_SCAN_ACCL_X)
|
INV_MPU6050_SCAN_MASK_3AXIS_ACCEL | INV_MPU6050_SCAN_MASK_3AXIS_GYRO,
|
||||||
| BIT(INV_MPU6050_SCAN_ACCL_Y)
|
INV_MPU6050_SCAN_MASK_3AXIS_ACCEL | INV_MPU6050_SCAN_MASK_3AXIS_GYRO
|
||||||
| BIT(INV_MPU6050_SCAN_ACCL_Z)
|
| INV_MPU6050_SCAN_MASK_TEMP,
|
||||||
| BIT(INV_MPU6050_SCAN_GYRO_X)
|
|
||||||
| BIT(INV_MPU6050_SCAN_GYRO_Y)
|
|
||||||
| BIT(INV_MPU6050_SCAN_GYRO_Z),
|
|
||||||
/* 6-axis accel + magn */
|
/* 6-axis accel + magn */
|
||||||
BIT(INV_MPU6050_SCAN_ACCL_X)
|
INV_MPU6050_SCAN_MASK_3AXIS_ACCEL | INV_MPU9X50_SCAN_MASK_3AXIS_MAGN,
|
||||||
| BIT(INV_MPU6050_SCAN_ACCL_Y)
|
INV_MPU6050_SCAN_MASK_3AXIS_ACCEL | INV_MPU9X50_SCAN_MASK_3AXIS_MAGN
|
||||||
| BIT(INV_MPU6050_SCAN_ACCL_Z)
|
| INV_MPU6050_SCAN_MASK_TEMP,
|
||||||
| BIT(INV_MPU9X50_SCAN_MAGN_X)
|
|
||||||
| BIT(INV_MPU9X50_SCAN_MAGN_Y)
|
|
||||||
| BIT(INV_MPU9X50_SCAN_MAGN_Z),
|
|
||||||
/* 6-axis gyro + magn */
|
/* 6-axis gyro + magn */
|
||||||
BIT(INV_MPU6050_SCAN_GYRO_X)
|
INV_MPU6050_SCAN_MASK_3AXIS_GYRO | INV_MPU9X50_SCAN_MASK_3AXIS_MAGN,
|
||||||
| BIT(INV_MPU6050_SCAN_GYRO_Y)
|
INV_MPU6050_SCAN_MASK_3AXIS_GYRO | INV_MPU9X50_SCAN_MASK_3AXIS_MAGN
|
||||||
| BIT(INV_MPU6050_SCAN_GYRO_Z)
|
| INV_MPU6050_SCAN_MASK_TEMP,
|
||||||
| BIT(INV_MPU9X50_SCAN_MAGN_X)
|
|
||||||
| BIT(INV_MPU9X50_SCAN_MAGN_Y)
|
|
||||||
| BIT(INV_MPU9X50_SCAN_MAGN_Z),
|
|
||||||
/* 9-axis accel + gyro + magn */
|
/* 9-axis accel + gyro + magn */
|
||||||
BIT(INV_MPU6050_SCAN_ACCL_X)
|
INV_MPU6050_SCAN_MASK_3AXIS_ACCEL | INV_MPU6050_SCAN_MASK_3AXIS_GYRO
|
||||||
| BIT(INV_MPU6050_SCAN_ACCL_Y)
|
| INV_MPU9X50_SCAN_MASK_3AXIS_MAGN,
|
||||||
| BIT(INV_MPU6050_SCAN_ACCL_Z)
|
INV_MPU6050_SCAN_MASK_3AXIS_ACCEL | INV_MPU6050_SCAN_MASK_3AXIS_GYRO
|
||||||
| BIT(INV_MPU6050_SCAN_GYRO_X)
|
| INV_MPU9X50_SCAN_MASK_3AXIS_MAGN
|
||||||
| BIT(INV_MPU6050_SCAN_GYRO_Y)
|
| INV_MPU6050_SCAN_MASK_TEMP,
|
||||||
| BIT(INV_MPU6050_SCAN_GYRO_Z)
|
|
||||||
| BIT(INV_MPU9X50_SCAN_MAGN_X)
|
|
||||||
| BIT(INV_MPU9X50_SCAN_MAGN_Y)
|
|
||||||
| BIT(INV_MPU9X50_SCAN_MAGN_Z),
|
|
||||||
0,
|
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[] = {
|
static const unsigned long inv_icm20602_scan_masks[] = {
|
||||||
/* 3-axis accel + temp (mandatory) */
|
/* 3-axis accel + temp (mandatory) */
|
||||||
BIT(INV_ICM20602_SCAN_ACCL_X)
|
INV_MPU6050_SCAN_MASK_3AXIS_ACCEL | INV_MPU6050_SCAN_MASK_TEMP,
|
||||||
| BIT(INV_ICM20602_SCAN_ACCL_Y)
|
|
||||||
| BIT(INV_ICM20602_SCAN_ACCL_Z)
|
|
||||||
| BIT(INV_ICM20602_SCAN_TEMP),
|
|
||||||
/* 3-axis gyro + temp (mandatory) */
|
/* 3-axis gyro + temp (mandatory) */
|
||||||
BIT(INV_ICM20602_SCAN_GYRO_X)
|
INV_MPU6050_SCAN_MASK_3AXIS_GYRO | INV_MPU6050_SCAN_MASK_TEMP,
|
||||||
| BIT(INV_ICM20602_SCAN_GYRO_Y)
|
|
||||||
| BIT(INV_ICM20602_SCAN_GYRO_Z)
|
|
||||||
| BIT(INV_ICM20602_SCAN_TEMP),
|
|
||||||
/* 6-axis accel + gyro + temp (mandatory) */
|
/* 6-axis accel + gyro + temp (mandatory) */
|
||||||
BIT(INV_ICM20602_SCAN_ACCL_X)
|
INV_MPU6050_SCAN_MASK_3AXIS_ACCEL | INV_MPU6050_SCAN_MASK_3AXIS_GYRO
|
||||||
| BIT(INV_ICM20602_SCAN_ACCL_Y)
|
| INV_MPU6050_SCAN_MASK_TEMP,
|
||||||
| 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),
|
|
||||||
0,
|
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);
|
irq_type = irqd_get_trigger_type(desc);
|
||||||
if (!irq_type)
|
if (!irq_type)
|
||||||
irq_type = IRQF_TRIGGER_RISING;
|
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;
|
st->irq_mask = INV_MPU6050_ACTIVE_HIGH;
|
||||||
else if (irq_type == IRQF_TRIGGER_FALLING)
|
else if (irq_type == IRQF_TRIGGER_FALLING)
|
||||||
st->irq_mask = INV_MPU6050_ACTIVE_LOW;
|
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);
|
inv_mpu_bus_setup(indio_dev);
|
||||||
|
|
||||||
switch (chip_type) {
|
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_MPU9250:
|
||||||
case INV_MPU9255:
|
case INV_MPU9255:
|
||||||
/*
|
indio_dev->channels = inv_mpu9250_channels;
|
||||||
* Use magnetometer inside the chip only if there is no i2c
|
indio_dev->num_channels = ARRAY_SIZE(inv_mpu9250_channels);
|
||||||
* auxiliary device in use.
|
indio_dev->available_scan_masks = inv_mpu9x50_scan_masks;
|
||||||
*/
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case INV_ICM20602:
|
case INV_ICM20602:
|
||||||
indio_dev->channels = inv_icm20602_channels;
|
indio_dev->channels = inv_mpu_channels;
|
||||||
indio_dev->num_channels = ARRAY_SIZE(inv_icm20602_channels);
|
indio_dev->num_channels = ARRAY_SIZE(inv_mpu_channels);
|
||||||
indio_dev->available_scan_masks = inv_icm20602_scan_masks;
|
indio_dev->available_scan_masks = inv_icm20602_scan_masks;
|
||||||
break;
|
break;
|
||||||
default:
|
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;
|
indio_dev->available_scan_masks = inv_mpu_scan_masks;
|
||||||
break;
|
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->info = &mpu_info;
|
||||||
indio_dev->modes = INDIO_BUFFER_TRIGGERED;
|
indio_dev->modes = INDIO_BUFFER_TRIGGERED;
|
||||||
|
@ -77,6 +77,7 @@ static bool inv_mpu_i2c_aux_bus(struct device *dev)
|
|||||||
case INV_ICM20602:
|
case INV_ICM20602:
|
||||||
/* no i2c auxiliary bus on the chip */
|
/* no i2c auxiliary bus on the chip */
|
||||||
return false;
|
return false;
|
||||||
|
case INV_MPU9150:
|
||||||
case INV_MPU9250:
|
case INV_MPU9250:
|
||||||
case INV_MPU9255:
|
case INV_MPU9255:
|
||||||
if (st->magn_disabled)
|
if (st->magn_disabled)
|
||||||
@ -102,6 +103,7 @@ static int inv_mpu_magn_disable(struct iio_dev *indio_dev)
|
|||||||
struct device_node *mux_node;
|
struct device_node *mux_node;
|
||||||
|
|
||||||
switch (st->chip_type) {
|
switch (st->chip_type) {
|
||||||
|
case INV_MPU9150:
|
||||||
case INV_MPU9250:
|
case INV_MPU9250:
|
||||||
case INV_MPU9255:
|
case INV_MPU9255:
|
||||||
mux_node = of_get_child_by_name(dev->of_node, "i2c-gate");
|
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_fs: accel full scale range.
|
||||||
* @accl_fifo_enable: enable accel data output
|
* @accl_fifo_enable: enable accel data output
|
||||||
* @gyro_fifo_enable: enable gyro data output
|
* @gyro_fifo_enable: enable gyro data output
|
||||||
|
* @temp_fifo_enable: enable temp data output
|
||||||
* @magn_fifo_enable: enable magn data output
|
* @magn_fifo_enable: enable magn data output
|
||||||
* @divider: chip sample rate divider (sample rate divider - 1)
|
* @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_fs:2;
|
||||||
unsigned int accl_fifo_enable:1;
|
unsigned int accl_fifo_enable:1;
|
||||||
unsigned int gyro_fifo_enable:1;
|
unsigned int gyro_fifo_enable:1;
|
||||||
|
unsigned int temp_fifo_enable:1;
|
||||||
unsigned int magn_fifo_enable:1;
|
unsigned int magn_fifo_enable:1;
|
||||||
u8 divider;
|
u8 divider;
|
||||||
u8 user_ctrl;
|
u8 user_ctrl;
|
||||||
@ -184,6 +186,7 @@ struct inv_mpu6050_state {
|
|||||||
#define INV_MPU6050_BIT_SLAVE_2 0x04
|
#define INV_MPU6050_BIT_SLAVE_2 0x04
|
||||||
#define INV_MPU6050_BIT_ACCEL_OUT 0x08
|
#define INV_MPU6050_BIT_ACCEL_OUT 0x08
|
||||||
#define INV_MPU6050_BITS_GYRO_OUT 0x70
|
#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_REG_I2C_MST_CTRL 0x24
|
||||||
#define INV_MPU6050_BITS_I2C_MST_CLK_400KHZ 0x0D
|
#define INV_MPU6050_BITS_I2C_MST_CLK_400KHZ 0x0D
|
||||||
@ -268,8 +271,8 @@ struct inv_mpu6050_state {
|
|||||||
/* MPU9X50 9-axis magnetometer */
|
/* MPU9X50 9-axis magnetometer */
|
||||||
#define INV_MPU9X50_BYTES_MAGN 7
|
#define INV_MPU9X50_BYTES_MAGN 7
|
||||||
|
|
||||||
/* ICM20602 FIFO samples include temperature readings */
|
/* FIFO temperature sample size */
|
||||||
#define INV_ICM20602_BYTES_PER_TEMP_SENSOR 2
|
#define INV_MPU6050_BYTES_PER_TEMP_SENSOR 2
|
||||||
|
|
||||||
/* mpu6500 registers */
|
/* mpu6500 registers */
|
||||||
#define INV_MPU6500_REG_ACCEL_CONFIG_2 0x1D
|
#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_OFFSET 8170
|
||||||
#define INV_ICM20608_TEMP_SCALE 3059976
|
#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_OUTPUT_DATA_SIZE 32
|
||||||
|
|
||||||
#define INV_MPU6050_REG_INT_PIN_CFG 0x37
|
#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_X,
|
||||||
INV_MPU6050_SCAN_ACCL_Y,
|
INV_MPU6050_SCAN_ACCL_Y,
|
||||||
INV_MPU6050_SCAN_ACCL_Z,
|
INV_MPU6050_SCAN_ACCL_Z,
|
||||||
|
INV_MPU6050_SCAN_TEMP,
|
||||||
INV_MPU6050_SCAN_GYRO_X,
|
INV_MPU6050_SCAN_GYRO_X,
|
||||||
INV_MPU6050_SCAN_GYRO_Y,
|
INV_MPU6050_SCAN_GYRO_Y,
|
||||||
INV_MPU6050_SCAN_GYRO_Z,
|
INV_MPU6050_SCAN_GYRO_Z,
|
||||||
@ -355,18 +359,6 @@ enum inv_mpu6050_scan {
|
|||||||
INV_MPU9X50_SCAN_TIMESTAMP,
|
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 {
|
enum inv_mpu6050_filter_e {
|
||||||
INV_MPU6050_FILTER_256HZ_NOLPF2 = 0,
|
INV_MPU6050_FILTER_256HZ_NOLPF2 = 0,
|
||||||
INV_MPU6050_FILTER_188HZ,
|
INV_MPU6050_FILTER_188HZ,
|
||||||
|
@ -12,7 +12,9 @@
|
|||||||
#include "inv_mpu_magn.h"
|
#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
|
#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_PWDN 0x00
|
||||||
#define INV_MPU_MAGN_BITS_MODE_SINGLE 0x01
|
#define INV_MPU_MAGN_BITS_MODE_SINGLE 0x01
|
||||||
#define INV_MPU_MAGN_BITS_MODE_FUSE 0x0F
|
#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_MPU9250_MAGN_REG_CNTL2 0x0B
|
||||||
#define INV_MPU_MAGN_BIT_SRST 0x01
|
#define INV_MPU9250_MAGN_BIT_SRST 0x01
|
||||||
|
|
||||||
#define INV_MPU_MAGN_REG_ASAX 0x10
|
#define INV_MPU_MAGN_REG_ASAX 0x10
|
||||||
#define INV_MPU_MAGN_REG_ASAY 0x11
|
#define INV_MPU_MAGN_REG_ASAY 0x11
|
||||||
@ -48,6 +50,7 @@
|
|||||||
static bool inv_magn_supported(const struct inv_mpu6050_state *st)
|
static bool inv_magn_supported(const struct inv_mpu6050_state *st)
|
||||||
{
|
{
|
||||||
switch (st->chip_type) {
|
switch (st->chip_type) {
|
||||||
|
case INV_MPU9150:
|
||||||
case INV_MPU9250:
|
case INV_MPU9250:
|
||||||
case INV_MPU9255:
|
case INV_MPU9255:
|
||||||
return true;
|
return true;
|
||||||
@ -61,6 +64,7 @@ static int inv_magn_init(struct inv_mpu6050_state *st)
|
|||||||
{
|
{
|
||||||
uint8_t val;
|
uint8_t val;
|
||||||
uint8_t asa[3];
|
uint8_t asa[3];
|
||||||
|
int32_t sensitivity;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
/* check whoami */
|
/* check whoami */
|
||||||
@ -71,12 +75,19 @@ static int inv_magn_init(struct inv_mpu6050_state *st)
|
|||||||
if (val != INV_MPU_MAGN_BITS_WIA)
|
if (val != INV_MPU_MAGN_BITS_WIA)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
/* reset chip */
|
/* software reset for MPU925x only */
|
||||||
ret = inv_mpu_aux_write(st, INV_MPU_MAGN_I2C_ADDR,
|
switch (st->chip_type) {
|
||||||
INV_MPU_MAGN_REG_CNTL2,
|
case INV_MPU9250:
|
||||||
INV_MPU_MAGN_BIT_SRST);
|
case INV_MPU9255:
|
||||||
if (ret)
|
ret = inv_mpu_aux_write(st, INV_MPU_MAGN_I2C_ADDR,
|
||||||
return ret;
|
INV_MPU9250_MAGN_REG_CNTL2,
|
||||||
|
INV_MPU9250_MAGN_BIT_SRST);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
/* read fuse ROM data */
|
/* read fuse ROM data */
|
||||||
ret = inv_mpu_aux_write(st, INV_MPU_MAGN_I2C_ADDR,
|
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)
|
if (ret)
|
||||||
return 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
|
* Sensitivity adjustement and scale to Gauss
|
||||||
*
|
*
|
||||||
@ -104,16 +134,11 @@ static int inv_magn_init(struct inv_mpu6050_state *st)
|
|||||||
* Factor simplification:
|
* Factor simplification:
|
||||||
* Hadj = H * ((ASA + 128) / 256)
|
* Hadj = H * ((ASA + 128) / 256)
|
||||||
*
|
*
|
||||||
* Sensor sentivity
|
* raw_to_gauss = Hadj * sensitivity
|
||||||
* 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
|
|
||||||
*/
|
*/
|
||||||
st->magn_raw_to_gauss[0] = (((int32_t)asa[0] + 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) * 1500) / 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) * 1500) / 256;
|
st->magn_raw_to_gauss[2] = (((int32_t)asa[2] + 128) * sensitivity) / 256;
|
||||||
|
|
||||||
return 0;
|
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)
|
int inv_mpu_magn_probe(struct inv_mpu6050_state *st)
|
||||||
{
|
{
|
||||||
|
uint8_t val;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
/* quit if chip is not supported */
|
/* quit if chip is not supported */
|
||||||
@ -179,10 +205,17 @@ int inv_mpu_magn_probe(struct inv_mpu6050_state *st)
|
|||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
/* add 16 bits mode */
|
/* add 16 bits mode for MPU925x */
|
||||||
ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_DO(1),
|
val = INV_MPU_MAGN_BITS_MODE_SINGLE;
|
||||||
INV_MPU_MAGN_BITS_MODE_SINGLE |
|
switch (st->chip_type) {
|
||||||
INV_MPU_MAGN_BIT_OUTPUT_BIT);
|
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)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
@ -237,6 +270,7 @@ int inv_mpu_magn_set_orient(struct inv_mpu6050_state *st)
|
|||||||
|
|
||||||
/* fill magnetometer orientation */
|
/* fill magnetometer orientation */
|
||||||
switch (st->chip_type) {
|
switch (st->chip_type) {
|
||||||
|
case INV_MPU9150:
|
||||||
case INV_MPU9250:
|
case INV_MPU9250:
|
||||||
case INV_MPU9255:
|
case INV_MPU9255:
|
||||||
/* x <- y */
|
/* x <- y */
|
||||||
|
@ -142,6 +142,8 @@ int inv_reset_fifo(struct iio_dev *indio_dev)
|
|||||||
d |= INV_MPU6050_BITS_GYRO_OUT;
|
d |= INV_MPU6050_BITS_GYRO_OUT;
|
||||||
if (st->chip_config.accl_fifo_enable)
|
if (st->chip_config.accl_fifo_enable)
|
||||||
d |= INV_MPU6050_BIT_ACCEL_OUT;
|
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)
|
if (st->chip_config.magn_fifo_enable)
|
||||||
d |= INV_MPU6050_BIT_SLAVE_0;
|
d |= INV_MPU6050_BIT_SLAVE_0;
|
||||||
result = regmap_write(st->map, st->reg->fifo_en, d);
|
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");
|
"failed to ack interrupt\n");
|
||||||
goto flush_fifo;
|
goto flush_fifo;
|
||||||
}
|
}
|
||||||
if (!(int_status & INV_MPU6050_BIT_RAW_DATA_RDY_INT)) {
|
if (!(int_status & INV_MPU6050_BIT_RAW_DATA_RDY_INT))
|
||||||
dev_warn(regmap_get_device(st->map),
|
|
||||||
"spurious interrupt with status 0x%x\n", int_status);
|
|
||||||
goto end_session;
|
goto end_session;
|
||||||
}
|
|
||||||
|
|
||||||
if (!(st->chip_config.accl_fifo_enable |
|
if (!(st->chip_config.accl_fifo_enable |
|
||||||
st->chip_config.gyro_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)
|
if (st->chip_config.gyro_fifo_enable)
|
||||||
bytes_per_datum += INV_MPU6050_BYTES_PER_3AXIS_SENSOR;
|
bytes_per_datum += INV_MPU6050_BYTES_PER_3AXIS_SENSOR;
|
||||||
|
|
||||||
if (st->chip_type == INV_ICM20602)
|
if (st->chip_config.temp_fifo_enable)
|
||||||
bytes_per_datum += INV_ICM20602_BYTES_PER_TEMP_SENSOR;
|
bytes_per_datum += INV_MPU6050_BYTES_PER_TEMP_SENSOR;
|
||||||
|
|
||||||
if (st->chip_config.magn_fifo_enable)
|
if (st->chip_config.magn_fifo_enable)
|
||||||
bytes_per_datum += INV_MPU9X50_BYTES_MAGN;
|
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[] = {
|
static const struct spi_device_id inv_mpu_id[] = {
|
||||||
{"mpu6000", INV_MPU6000},
|
{"mpu6000", INV_MPU6000},
|
||||||
{"mpu6500", INV_MPU6500},
|
{"mpu6500", INV_MPU6500},
|
||||||
{"mpu9150", INV_MPU9150},
|
|
||||||
{"mpu9250", INV_MPU9250},
|
{"mpu9250", INV_MPU9250},
|
||||||
{"mpu9255", INV_MPU9255},
|
{"mpu9255", INV_MPU9255},
|
||||||
{"icm20608", INV_ICM20608},
|
{"icm20608", INV_ICM20608},
|
||||||
|
@ -24,6 +24,9 @@ static void inv_scan_query_mpu6050(struct iio_dev *indio_dev)
|
|||||||
indio_dev->active_scan_mask) ||
|
indio_dev->active_scan_mask) ||
|
||||||
test_bit(INV_MPU6050_SCAN_ACCL_Z,
|
test_bit(INV_MPU6050_SCAN_ACCL_Z,
|
||||||
indio_dev->active_scan_mask);
|
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)
|
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);
|
struct inv_mpu6050_state *st = iio_priv(indio_dev);
|
||||||
|
|
||||||
switch (st->chip_type) {
|
switch (st->chip_type) {
|
||||||
|
case INV_MPU9150:
|
||||||
case INV_MPU9250:
|
case INV_MPU9250:
|
||||||
case INV_MPU9255:
|
case INV_MPU9255:
|
||||||
return inv_scan_query_mpu9x50(indio_dev);
|
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