mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-27 22:24:11 +08:00
Staging/IIO driver patches for 5.4-rc1
Here is the big staging/iio driver update for 5.4-rc1. Lots of churn here, with a few driver/filesystems moving out of staging finally: - erofs moved out of staging - greybus core code moved out of staging Along with that, a new filesytem has been added: - extfat to provide support for those devices requiring that filesystem (i.e. transfer devices to/from windows systems or printers.) Other than that, there a number of new IIO drivers, and lots and lots and lots of staging driver cleanups and minor fixes as people continue to dig into those for easy changes. All of these have been in linux-next for a while with no reported issues. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCXYIW0g8cZ3JlZ0Brcm9h aC5jb20ACgkQMUfUDdst+ynJgwCgt22YQdsWRrOuJnZp3xpFq/ZSMWAAn0dAgFf5 SlSTI2nQhbW7jjdShrPR =Ra09 -----END PGP SIGNATURE----- Merge tag 'staging-5.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging Pull staging and IIO driver updates from Greg KH: "Here is the big staging/iio driver update for 5.4-rc1. Lots of churn here, with a few driver/filesystems moving out of staging finally: - erofs moved out of staging - greybus core code moved out of staging Along with that, a new filesytem has been added: - extfat to provide support for those devices requiring that filesystem (i.e. transfer devices to/from windows systems or printers) Other than that, there a number of new IIO drivers, and lots and lots and lots of staging driver cleanups and minor fixes as people continue to dig into those for easy changes. All of these have been in linux-next for a while with no reported issues" * tag 'staging-5.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging: (453 commits) Staging: gasket: Use temporaries to reduce line length. Staging: octeon: Avoid several usecases of strcpy staging: vhciq_core: replace snprintf with scnprintf staging: wilc1000: avoid twice IRQ handler execution for each single interrupt staging: wilc1000: remove unused interrupt status handling code staging: fbtft: make several arrays static const, makes object smaller staging: rtl8188eu: make two arrays static const, makes object smaller staging: rtl8723bs: core: Remove Macro "IS_MAC_ADDRESS_BROADCAST" dt-bindings: anybus-controller: move to staging/ tree staging: emxx_udc: remove local TRUE/FALSE definition staging: wilc1000: look for rtc_clk clock staging: dt-bindings: wilc1000: add optional rtc_clk property staging: nvec: make use of devm_platform_ioremap_resource staging: exfat: drop unused function parameter Staging: exfat: Avoid use of strcpy staging: exfat: use integer constants staging: exfat: cleanup spacing for casts staging: exfat: cleanup spacing for operators staging: rtl8723bs: hal: remove redundant variable n staging: pi433: Fix typo in documentation ...
This commit is contained in:
commit
e6874fc294
@ -13,4 +13,4 @@ Description:
|
||||
error on writing
|
||||
If DFSDM input is SPI Slave:
|
||||
Reading returns value previously set.
|
||||
Writing value before starting conversions.
|
||||
Writing value before starting conversions.
|
||||
|
@ -91,29 +91,6 @@ Description:
|
||||
When counting down the counter start from preset value
|
||||
and fire event when reach 0.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_count_quadrature_mode_available
|
||||
KernelVersion: 4.12
|
||||
Contact: benjamin.gaignard@st.com
|
||||
Description:
|
||||
Reading returns the list possible quadrature modes.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_count0_quadrature_mode
|
||||
KernelVersion: 4.12
|
||||
Contact: benjamin.gaignard@st.com
|
||||
Description:
|
||||
Configure the device counter quadrature modes:
|
||||
channel_A:
|
||||
Encoder A input servers as the count input and B as
|
||||
the UP/DOWN direction control input.
|
||||
|
||||
channel_B:
|
||||
Encoder B input serves as the count input and A as
|
||||
the UP/DOWN direction control input.
|
||||
|
||||
quadrature:
|
||||
Encoder A and B inputs are mixed to get direction
|
||||
and count with a scale of 0.25.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_count_enable_mode_available
|
||||
KernelVersion: 4.12
|
||||
Contact: benjamin.gaignard@st.com
|
||||
|
121
Documentation/devicetree/bindings/iio/adc/adi,ad7192.yaml
Normal file
121
Documentation/devicetree/bindings/iio/adc/adi,ad7192.yaml
Normal file
@ -0,0 +1,121 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
# Copyright 2019 Analog Devices Inc.
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/bindings/iio/adc/adi,ad7192.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Analog Devices AD7192 ADC device driver
|
||||
|
||||
maintainers:
|
||||
- Michael Hennerich <michael.hennerich@analog.com>
|
||||
|
||||
description: |
|
||||
Bindings for the Analog Devices AD7192 ADC device. Datasheet can be
|
||||
found here:
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/AD7192.pdf
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- adi,ad7190
|
||||
- adi,ad7192
|
||||
- adi,ad7193
|
||||
- adi,ad7195
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
spi-cpol: true
|
||||
|
||||
spi-cpha: true
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
description: phandle to the master clock (mclk)
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: mclk
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
dvdd-supply:
|
||||
description: DVdd voltage supply
|
||||
items:
|
||||
- const: dvdd
|
||||
|
||||
avdd-supply:
|
||||
description: AVdd voltage supply
|
||||
items:
|
||||
- const: avdd
|
||||
|
||||
adi,rejection-60-Hz-enable:
|
||||
description: |
|
||||
This bit enables a notch at 60 Hz when the first notch of the sinc
|
||||
filter is at 50 Hz. When REJ60 is set, a filter notch is placed at
|
||||
60 Hz when the sinc filter first notch is at 50 Hz. This allows
|
||||
simultaneous 50 Hz/ 60 Hz rejection.
|
||||
type: boolean
|
||||
|
||||
adi,refin2-pins-enable:
|
||||
description: |
|
||||
External reference applied between the P1/REFIN2(+) and P0/REFIN2(−) pins.
|
||||
type: boolean
|
||||
|
||||
adi,buffer-enable:
|
||||
description: |
|
||||
Enables the buffer on the analog inputs. If cleared, the analog inputs
|
||||
are unbuffered, lowering the power consumption of the device. If this
|
||||
bit is set, the analog inputs are buffered, allowing the user to place
|
||||
source impedances on the front end without contributing gain errors to
|
||||
the system.
|
||||
type: boolean
|
||||
|
||||
adi,burnout-currents-enable:
|
||||
description: |
|
||||
When this bit is set to 1, the 500 nA current sources in the signal
|
||||
path are enabled. When BURN = 0, the burnout currents are disabled.
|
||||
The burnout currents can be enabled only when the buffer is active
|
||||
and when chop is disabled.
|
||||
type: boolean
|
||||
|
||||
bipolar:
|
||||
description: see Documentation/devicetree/bindings/iio/adc/adc.txt
|
||||
type: boolean
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- clocks
|
||||
- clock-names
|
||||
- interrupts
|
||||
- dvdd-supply
|
||||
- avdd-supply
|
||||
- spi-cpol
|
||||
- spi-cpha
|
||||
|
||||
examples:
|
||||
- |
|
||||
spi0 {
|
||||
adc@0 {
|
||||
compatible = "adi,ad7192";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <1000000>;
|
||||
spi-cpol;
|
||||
spi-cpha;
|
||||
clocks = <&ad7192_mclk>;
|
||||
clock-names = "mclk";
|
||||
#interrupt-cells = <2>;
|
||||
interrupts = <25 0x2>;
|
||||
interrupt-parent = <&gpio>;
|
||||
dvdd-supply = <&dvdd>;
|
||||
avdd-supply = <&avdd>;
|
||||
|
||||
adi,refin2-pins-enable;
|
||||
adi,rejection-60-Hz-enable;
|
||||
adi,buffer-enable;
|
||||
adi,burnout-currents-enable;
|
||||
};
|
||||
};
|
@ -1,66 +0,0 @@
|
||||
Analog Devices AD7606 Simultaneous Sampling ADC
|
||||
|
||||
Required properties for the AD7606:
|
||||
|
||||
- compatible: Must be one of
|
||||
* "adi,ad7605-4"
|
||||
* "adi,ad7606-8"
|
||||
* "adi,ad7606-6"
|
||||
* "adi,ad7606-4"
|
||||
* "adi,ad7616"
|
||||
- reg: SPI chip select number for the device
|
||||
- spi-max-frequency: Max SPI frequency to use
|
||||
see: Documentation/devicetree/bindings/spi/spi-bus.txt
|
||||
- spi-cpha: See Documentation/devicetree/bindings/spi/spi-bus.txt
|
||||
- avcc-supply: phandle to the Avcc power supply
|
||||
- interrupts: IRQ line for the ADC
|
||||
see: Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
|
||||
- adi,conversion-start-gpios: must be the device tree identifier of the CONVST pin.
|
||||
This logic input is used to initiate conversions on the analog
|
||||
input channels. As the line is active high, it should be marked
|
||||
GPIO_ACTIVE_HIGH.
|
||||
|
||||
Optional properties:
|
||||
|
||||
- reset-gpios: must be the device tree identifier of the RESET pin. If specified,
|
||||
it will be asserted during driver probe. As the line is active high,
|
||||
it should be marked GPIO_ACTIVE_HIGH.
|
||||
- standby-gpios: must be the device tree identifier of the STBY pin. This pin is used
|
||||
to place the AD7606 into one of two power-down modes, Standby mode or
|
||||
Shutdown mode. As the line is active low, it should be marked
|
||||
GPIO_ACTIVE_LOW.
|
||||
- adi,first-data-gpios: must be the device tree identifier of the FRSTDATA pin.
|
||||
The FRSTDATA output indicates when the first channel, V1, is
|
||||
being read back on either the parallel, byte or serial interface.
|
||||
As the line is active high, it should be marked GPIO_ACTIVE_HIGH.
|
||||
- adi,range-gpios: must be the device tree identifier of the RANGE pin. The polarity on
|
||||
this pin determines the input range of the analog input channels. If
|
||||
this pin is tied to a logic high, the analog input range is ±10V for
|
||||
all channels. If this pin is tied to a logic low, the analog input range
|
||||
is ±5V for all channels. As the line is active high, it should be marked
|
||||
GPIO_ACTIVE_HIGH.
|
||||
- adi,oversampling-ratio-gpios: must be the device tree identifier of the over-sampling
|
||||
mode pins. As the line is active high, it should be marked
|
||||
GPIO_ACTIVE_HIGH.
|
||||
|
||||
Example:
|
||||
|
||||
adc@0 {
|
||||
compatible = "adi,ad7606-8";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <1000000>;
|
||||
spi-cpol;
|
||||
|
||||
avcc-supply = <&adc_vref>;
|
||||
|
||||
interrupts = <25 IRQ_TYPE_EDGE_FALLING>;
|
||||
interrupt-parent = <&gpio>;
|
||||
|
||||
adi,conversion-start-gpios = <&gpio 17 GPIO_ACTIVE_HIGH>;
|
||||
reset-gpios = <&gpio 27 GPIO_ACTIVE_HIGH>;
|
||||
adi,first-data-gpios = <&gpio 22 GPIO_ACTIVE_HIGH>;
|
||||
adi,oversampling-ratio-gpios = <&gpio 18 GPIO_ACTIVE_HIGH
|
||||
&gpio 23 GPIO_ACTIVE_HIGH
|
||||
&gpio 26 GPIO_ACTIVE_HIGH>;
|
||||
standby-gpios = <&gpio 24 GPIO_ACTIVE_LOW>;
|
||||
};
|
138
Documentation/devicetree/bindings/iio/adc/adi,ad7606.yaml
Normal file
138
Documentation/devicetree/bindings/iio/adc/adi,ad7606.yaml
Normal file
@ -0,0 +1,138 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/adc/adi,ad7606.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Analog Devices AD7606 Simultaneous Sampling ADC
|
||||
|
||||
maintainers:
|
||||
- Beniamin Bia <beniamin.bia@analog.com>
|
||||
- Stefan Popa <stefan.popa@analog.com>
|
||||
|
||||
description: |
|
||||
Analog Devices AD7606 Simultaneous Sampling ADC
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/ad7606_7606-6_7606-4.pdf
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/AD7606B.pdf
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/AD7616.pdf
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- adi,ad7605-4
|
||||
- adi,ad7606-8
|
||||
- adi,ad7606-6
|
||||
- adi,ad7606-4
|
||||
- adi,ad7606b
|
||||
- adi,ad7616
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
spi-cpha: true
|
||||
|
||||
avcc-supply:
|
||||
description:
|
||||
Phandle to the Avcc power supply
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
adi,conversion-start-gpios:
|
||||
description:
|
||||
Must be the device tree identifier of the CONVST pin.
|
||||
This logic input is used to initiate conversions on the analog
|
||||
input channels. As the line is active high, it should be marked
|
||||
GPIO_ACTIVE_HIGH.
|
||||
maxItems: 1
|
||||
|
||||
reset-gpios:
|
||||
description:
|
||||
Must be the device tree identifier of the RESET pin. If specified,
|
||||
it will be asserted during driver probe. As the line is active high,
|
||||
it should be marked GPIO_ACTIVE_HIGH.
|
||||
maxItems: 1
|
||||
|
||||
standby-gpios:
|
||||
description:
|
||||
Must be the device tree identifier of the STBY pin. This pin is used
|
||||
to place the AD7606 into one of two power-down modes, Standby mode or
|
||||
Shutdown mode. As the line is active low, it should be marked
|
||||
GPIO_ACTIVE_LOW.
|
||||
maxItems: 1
|
||||
|
||||
adi,first-data-gpios:
|
||||
description:
|
||||
Must be the device tree identifier of the FRSTDATA pin.
|
||||
The FRSTDATA output indicates when the first channel, V1, is
|
||||
being read back on either the parallel, byte or serial interface.
|
||||
As the line is active high, it should be marked GPIO_ACTIVE_HIGH.
|
||||
maxItems: 1
|
||||
|
||||
adi,range-gpios:
|
||||
description:
|
||||
Must be the device tree identifier of the RANGE pin. The polarity on
|
||||
this pin determines the input range of the analog input channels. If
|
||||
this pin is tied to a logic high, the analog input range is ±10V for
|
||||
all channels. If this pin is tied to a logic low, the analog input range
|
||||
is ±5V for all channels. As the line is active high, it should be marked
|
||||
GPIO_ACTIVE_HIGH.
|
||||
maxItems: 1
|
||||
|
||||
adi,oversampling-ratio-gpios:
|
||||
description:
|
||||
Must be the device tree identifier of the over-sampling
|
||||
mode pins. As the line is active high, it should be marked
|
||||
GPIO_ACTIVE_HIGH.
|
||||
maxItems: 1
|
||||
|
||||
adi,sw-mode:
|
||||
description:
|
||||
Software mode of operation, so far available only for ad7616 and ad7606b.
|
||||
It is enabled when all three oversampling mode pins are connected to
|
||||
high level. The device is configured by the corresponding registers. If the
|
||||
adi,oversampling-ratio-gpios property is defined, then the driver will set the
|
||||
oversampling gpios to high. Otherwise, it is assumed that the pins are hardwired
|
||||
to VDD.
|
||||
type: boolean
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- spi-cpha
|
||||
- avcc-supply
|
||||
- interrupts
|
||||
- adi,conversion-start-gpios
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
spi0 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
adc@0 {
|
||||
compatible = "adi,ad7606-8";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <1000000>;
|
||||
spi-cpol;
|
||||
spi-cpha;
|
||||
|
||||
avcc-supply = <&adc_vref>;
|
||||
|
||||
interrupts = <25 IRQ_TYPE_EDGE_FALLING>;
|
||||
interrupt-parent = <&gpio>;
|
||||
|
||||
adi,conversion-start-gpios = <&gpio 17 GPIO_ACTIVE_HIGH>;
|
||||
reset-gpios = <&gpio 27 GPIO_ACTIVE_HIGH>;
|
||||
adi,first-data-gpios = <&gpio 22 GPIO_ACTIVE_HIGH>;
|
||||
adi,oversampling-ratio-gpios = <&gpio 18 GPIO_ACTIVE_HIGH
|
||||
&gpio 23 GPIO_ACTIVE_HIGH
|
||||
&gpio 26 GPIO_ACTIVE_HIGH>;
|
||||
standby-gpios = <&gpio 24 GPIO_ACTIVE_LOW>;
|
||||
adi,sw-mode;
|
||||
};
|
||||
};
|
||||
...
|
@ -47,6 +47,12 @@ Required properties:
|
||||
Optional properties:
|
||||
- A pinctrl state named "default" for each ADC channel may be defined to set
|
||||
inX ADC pins in mode of operation for analog input on external pin.
|
||||
- booster-supply: Phandle to the embedded booster regulator that can be used
|
||||
to supply ADC analog input switches on stm32h7 and stm32mp1.
|
||||
- vdd-supply: Phandle to the vdd input voltage. It can be used to supply ADC
|
||||
analog input switches on stm32mp1.
|
||||
- st,syscfg: Phandle to system configuration controller. It can be used to
|
||||
control the analog circuitry on stm32mp1.
|
||||
|
||||
Contents of a stm32 adc child node:
|
||||
-----------------------------------
|
||||
|
@ -1,26 +0,0 @@
|
||||
* Plantower PMS7003 particulate matter sensor
|
||||
|
||||
Required properties:
|
||||
- compatible: must one of:
|
||||
"plantower,pms1003"
|
||||
"plantower,pms3003"
|
||||
"plantower,pms5003"
|
||||
"plantower,pms6003"
|
||||
"plantower,pms7003"
|
||||
"plantower,pmsa003"
|
||||
- vcc-supply: phandle to the regulator that provides power to the sensor
|
||||
|
||||
Optional properties:
|
||||
- plantower,set-gpios: phandle to the GPIO connected to the SET line
|
||||
- reset-gpios: phandle to the GPIO connected to the RESET line
|
||||
|
||||
Refer to serial/slave-device.txt for generic serial attached device bindings.
|
||||
|
||||
Example:
|
||||
|
||||
&uart0 {
|
||||
air-pollution-sensor {
|
||||
compatible = "plantower,pms7003";
|
||||
vcc-supply = <®_vcc5v0>;
|
||||
};
|
||||
};
|
@ -0,0 +1,51 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/chemical/plantower,pms7003.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Plantower PMS7003 air pollution sensor
|
||||
|
||||
maintainers:
|
||||
- Tomasz Duszynski <tduszyns@gmail.com>
|
||||
|
||||
description: |
|
||||
Air pollution sensor capable of measuring mass concentration of dust
|
||||
particles.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- plantower,pms1003
|
||||
- plantower,pms3003
|
||||
- plantower,pms5003
|
||||
- plantower,pms6003
|
||||
- plantower,pms7003
|
||||
- plantower,pmsa003
|
||||
|
||||
vcc-supply:
|
||||
description: regulator that provides power to the sensor
|
||||
maxItems: 1
|
||||
|
||||
plantower,set-gpios:
|
||||
description: GPIO connected to the SET line
|
||||
maxItems: 1
|
||||
|
||||
reset-gpios:
|
||||
description: GPIO connected to the RESET line
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- vcc-supply
|
||||
|
||||
examples:
|
||||
- |
|
||||
serial {
|
||||
air-pollution-sensor {
|
||||
compatible = "plantower,pms7003";
|
||||
vcc-supply = <®_vcc5v0>;
|
||||
};
|
||||
};
|
||||
|
||||
...
|
53
Documentation/devicetree/bindings/iio/imu/adi,adis16460.yaml
Normal file
53
Documentation/devicetree/bindings/iio/imu/adi,adis16460.yaml
Normal file
@ -0,0 +1,53 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/imu/adi,adis16460.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Analog Devices ADIS16460 and similar IMUs
|
||||
|
||||
maintainers:
|
||||
- Dragos Bogdan <dragos.bogdan@analog.com>
|
||||
|
||||
description: |
|
||||
Analog Devices ADIS16460 and similar IMUs
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/ADIS16460.pdf
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- adi,adis16460
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
spi-cpha: true
|
||||
|
||||
spi-cpol: true
|
||||
|
||||
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>;
|
||||
|
||||
imu@0 {
|
||||
compatible = "adi,adis16460";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <5000000>;
|
||||
spi-cpol;
|
||||
spi-cpha;
|
||||
interrupt-parent = <&gpio0>;
|
||||
interrupts = <0 IRQ_TYPE_LEVEL_HIGH>;
|
||||
};
|
||||
};
|
@ -11,6 +11,9 @@ Required properties:
|
||||
"st,asm330lhh"
|
||||
"st,lsm6dsox"
|
||||
"st,lsm6dsr"
|
||||
"st,lsm6ds3tr-c"
|
||||
"st,ism330dhcx"
|
||||
"st,lsm9ds1-imu"
|
||||
- reg: i2c address of the sensor / spi cs line
|
||||
|
||||
Optional properties:
|
||||
|
44
Documentation/devicetree/bindings/iio/light/noa1305.yaml
Normal file
44
Documentation/devicetree/bindings/iio/light/noa1305.yaml
Normal file
@ -0,0 +1,44 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/light/noa1305.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: ON Semiconductor NOA1305 Ambient Light Sensor
|
||||
|
||||
maintainers:
|
||||
- Martyn Welch <martyn.welch@collabora.com>
|
||||
|
||||
description: |
|
||||
Ambient sensing with an i2c interface.
|
||||
|
||||
https://www.onsemi.com/pub/Collateral/NOA1305-D.PDF
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- onnn,noa1305
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
vin-supply:
|
||||
description: Regulator that provides power to the sensor
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
examples:
|
||||
- |
|
||||
i2c {
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
light@39 {
|
||||
compatible = "onnn,noa1305";
|
||||
reg = <0x39>;
|
||||
};
|
||||
};
|
||||
...
|
49
Documentation/devicetree/bindings/iio/light/stk33xx.yaml
Normal file
49
Documentation/devicetree/bindings/iio/light/stk33xx.yaml
Normal file
@ -0,0 +1,49 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/light/stk33xx.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: |
|
||||
Sensortek STK33xx I2C Ambient Light and Proximity sensor
|
||||
|
||||
maintainers:
|
||||
- Jonathan Cameron <jic23@kernel.org>
|
||||
|
||||
description: |
|
||||
Ambient light and proximity sensor over an i2c interface.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- sensortek,stk3310
|
||||
- sensortek,stk3311
|
||||
- sensortek,stk3335
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
i2c {
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
stk3310@48 {
|
||||
compatible = "sensortek,stk3310";
|
||||
reg = <0x48>;
|
||||
interrupt-parent = <&gpio1>;
|
||||
interrupts = <5 IRQ_TYPE_LEVEL_LOW>;
|
||||
};
|
||||
};
|
||||
...
|
203
Documentation/devicetree/bindings/iio/mount-matrix.txt
Normal file
203
Documentation/devicetree/bindings/iio/mount-matrix.txt
Normal file
@ -0,0 +1,203 @@
|
||||
For discussion. Unclear are:
|
||||
* is the definition of +/- values practical or counterintuitive?
|
||||
* are the definitions unambiguous and easy to follow?
|
||||
* are the examples correct?
|
||||
* should we have HOWTO engineer a correct matrix for a new device (without comparing to a different one)?
|
||||
|
||||
====
|
||||
|
||||
|
||||
Mounting matrix
|
||||
|
||||
The mounting matrix is a device tree property used to orient any device
|
||||
that produce three-dimensional data in relation to the world where it is
|
||||
deployed.
|
||||
|
||||
The purpose of the mounting matrix is to translate the sensor frame of
|
||||
reference into the device frame of reference using a translation matrix as
|
||||
defined in linear algebra.
|
||||
|
||||
The typical usecase is that where a component has an internal representation
|
||||
of the (x,y,z) triplets, such as different registers to read these coordinates,
|
||||
and thus implying that the component should be mounted in a certain orientation
|
||||
relative to some specific device frame of reference.
|
||||
|
||||
For example a device with some kind of screen, where the user is supposed to
|
||||
interact with the environment using an accelerometer, gyroscope or magnetometer
|
||||
mounted on the same chassis as this screen, will likely take the screen as
|
||||
reference to (x,y,z) orientation, with (x,y) corresponding to these axes on the
|
||||
screen and (z) being depth, the axis perpendicular to the screen.
|
||||
|
||||
For a screen you probably want (x) coordinates to go from negative on the left
|
||||
to positive on the right, (y) from negative on the bottom to positive on top
|
||||
and (z) depth to be negative under the screen and positive in front of it,
|
||||
toward the face of the user.
|
||||
|
||||
A sensor can be mounted in any angle along the axes relative to the frame of
|
||||
reference. This means that the sensor may be flipped upside-down, left-right,
|
||||
or tilted at any angle relative to the frame of reference.
|
||||
|
||||
Another frame of reference is how the device with its sensor relates to the
|
||||
external world, the environment where the device is deployed. Usually the data
|
||||
from the sensor is used to figure out how the device is oriented with respect
|
||||
to this world. When using the mounting matrix, the sensor and device orientation
|
||||
becomes identical and we can focus on the data as it relates to the surrounding
|
||||
world.
|
||||
|
||||
Device-to-world examples for some three-dimensional sensor types:
|
||||
|
||||
- Accelerometers have their world frame of reference toward the center of
|
||||
gravity, usually to the core of the planet. A reading of the (x,y,z) values
|
||||
from the sensor will give a projection of the gravity vector through the
|
||||
device relative to the center of the planet, i.e. relative to its surface at
|
||||
this point. Up and down in the world relative to the device frame of
|
||||
reference can thus be determined. and users would likely expect a value of
|
||||
9.81 m/s^2 upwards along the (z) axis, i.e. out of the screen when the device
|
||||
is held with its screen flat on the planets surface and 0 on the other axes,
|
||||
as the gravity vector is projected 1:1 onto the sensors (z)-axis.
|
||||
|
||||
If you tilt the device, the g vector virtually coming out of the display
|
||||
is projected onto the (x,y) plane of the display panel.
|
||||
|
||||
Example:
|
||||
|
||||
^ z: +g ^ z: > 0
|
||||
! /!
|
||||
! x=y=0 / ! x: > 0
|
||||
+--------+ +--------+
|
||||
! ! ! !
|
||||
+--------+ +--------+
|
||||
! /
|
||||
! /
|
||||
v v
|
||||
center of center of
|
||||
gravity gravity
|
||||
|
||||
|
||||
If the device is tilted to the left, you get a positive x value. If you point
|
||||
its top towards surface, you get a negative y axis.
|
||||
|
||||
(---------)
|
||||
! ! y: -g
|
||||
! ! ^
|
||||
! ! !
|
||||
! !
|
||||
! ! x: +g <- z: +g -> x: -g
|
||||
! 1 2 3 !
|
||||
! 4 5 6 ! !
|
||||
! 7 8 9 ! v
|
||||
! * 0 # ! y: +g
|
||||
(---------)
|
||||
|
||||
|
||||
- Magnetometers (compasses) have their world frame of reference relative to the
|
||||
geomagnetic field. The system orientation vis-a-vis the world is defined with
|
||||
respect to the local earth geomagnetic reference frame where (y) is in the
|
||||
ground plane and positive towards magnetic North, (x) is in the ground plane,
|
||||
perpendicular to the North axis and positive towards the East and (z) is
|
||||
perpendicular to the ground plane and positive upwards.
|
||||
|
||||
|
||||
^^^ North: y > 0
|
||||
|
||||
(---------)
|
||||
! !
|
||||
! !
|
||||
! !
|
||||
! ! >
|
||||
! ! > North: x > 0
|
||||
! 1 2 3 ! >
|
||||
! 4 5 6 !
|
||||
! 7 8 9 !
|
||||
! * 0 # !
|
||||
(---------)
|
||||
|
||||
Since the geomagnetic field is not uniform this definition fails if we come
|
||||
closer to the poles.
|
||||
|
||||
Sensors and driver can not and should not take care of this because there
|
||||
are complex calculations and empirical data to be taken care of. We leave
|
||||
this up to user space.
|
||||
|
||||
The definition we take:
|
||||
|
||||
If the device is placed at the equator and the top is pointing north, the
|
||||
display is readable by a person standing upright on the earth surface, this
|
||||
defines a positive y value.
|
||||
|
||||
|
||||
- Gyroscopes detects the movement relative the device itself. The angular
|
||||
velocity is defined as orthogonal to the plane of rotation, so if you put the
|
||||
device on a flat surface and spin it around the z axis (such as rotating a
|
||||
device with a screen lying flat on a table), you should get a negative value
|
||||
along the (z) axis if rotated clockwise, and a positive value if rotated
|
||||
counter-clockwise according to the right-hand rule.
|
||||
|
||||
|
||||
(---------) y > 0
|
||||
! ! v---\
|
||||
! !
|
||||
! !
|
||||
! ! <--\
|
||||
! ! ! z > 0
|
||||
! 1 2 3 ! --/
|
||||
! 4 5 6 !
|
||||
! 7 8 9 !
|
||||
! * 0 # !
|
||||
(---------)
|
||||
|
||||
|
||||
So unless the sensor is ideally mounted, we need a means to indicate the
|
||||
relative orientation of any given sensor of this type with respect to the
|
||||
frame of reference.
|
||||
|
||||
To achieve this, use the device tree property "mount-matrix" for the sensor.
|
||||
|
||||
This supplies a 3x3 rotation matrix in the strict linear algebraic sense,
|
||||
to orient the senor axes relative to a desired point of reference. This means
|
||||
the resulting values from the sensor, after scaling to proper units, should be
|
||||
multiplied by this matrix to give the proper vectors values in three-dimensional
|
||||
space, relative to the device or world point of reference.
|
||||
|
||||
For more information, consult:
|
||||
https://en.wikipedia.org/wiki/Rotation_matrix
|
||||
|
||||
The mounting matrix has the layout:
|
||||
|
||||
(mxx, myx, mzx)
|
||||
(mxy, myy, mzy)
|
||||
(mxz, myz, mzz)
|
||||
|
||||
Values are intended to be multiplied as:
|
||||
|
||||
x' = mxx * x + myx * y + mzx * z
|
||||
y' = mxy * x + myy * y + mzy * z
|
||||
z' = mxz * x + myz * y + mzz * z
|
||||
|
||||
It is represented as an array of strings containing the real values for
|
||||
producing the transformation matrix.
|
||||
|
||||
Examples:
|
||||
|
||||
Identity matrix (nothing happens to the coordinates, which means the device was
|
||||
mechanically mounted in an ideal way and we need no transformation):
|
||||
|
||||
mount-matrix = "1", "0", "0",
|
||||
"0", "1", "0",
|
||||
"0", "0", "1";
|
||||
|
||||
The sensor is mounted 30 degrees (Pi/6 radians) tilted along the X axis, so we
|
||||
compensate by performing a -30 degrees rotation around the X axis:
|
||||
|
||||
mount-matrix = "1", "0", "0",
|
||||
"0", "0.866", "0.5",
|
||||
"0", "-0.5", "0.866";
|
||||
|
||||
The sensor is flipped 180 degrees (Pi radians) around the Z axis, i.e. mounted
|
||||
upside-down:
|
||||
|
||||
mount-matrix = "0.998", "0.054", "0",
|
||||
"-0.054", "0.998", "0",
|
||||
"0", "0", "1";
|
||||
|
||||
???: this does not match "180 degrees" - factors indicate ca. 3 degrees compensation
|
@ -0,0 +1,44 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/potentiometer/max5432.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Maxim Integrated MAX5432-MAX5435 Digital Potentiometers
|
||||
|
||||
maintainers:
|
||||
- Martin Kaiser <martin@kaiser.cx>
|
||||
|
||||
description: |
|
||||
Maxim Integrated MAX5432-MAX5435 Digital Potentiometers connected via I2C
|
||||
|
||||
Datasheet:
|
||||
https://datasheets.maximintegrated.com/en/ds/MAX5432-MAX5435.pdf
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- maxim,max5432
|
||||
- maxim,max5433
|
||||
- maxim,max5434
|
||||
- maxim,max5435
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
max5434@28 {
|
||||
compatible = "maxim,max5434";
|
||||
reg = <0x28>;
|
||||
};
|
||||
};
|
@ -827,6 +827,8 @@ patternProperties:
|
||||
description: Semtech Corporation
|
||||
"^sensirion,.*":
|
||||
description: Sensirion AG
|
||||
"^sensortek,.*":
|
||||
description: Sensortek Technology Corporation
|
||||
"^sff,.*":
|
||||
description: Small Form Factor Committee
|
||||
"^sgd,.*":
|
||||
|
@ -49,22 +49,23 @@ Bugs and patches are welcome, please kindly help us and send to the following
|
||||
linux-erofs mailing list:
|
||||
>> linux-erofs mailing list <linux-erofs@lists.ozlabs.org>
|
||||
|
||||
Note that EROFS is still working in progress as a Linux staging driver,
|
||||
Cc the staging mailing list as well is highly recommended:
|
||||
>> Linux Driver Project Developer List <devel@driverdev.osuosl.org>
|
||||
|
||||
Mount options
|
||||
=============
|
||||
|
||||
fault_injection=%d Enable fault injection in all supported types with
|
||||
specified injection rate. Supported injection type:
|
||||
Type_Name Type_Value
|
||||
FAULT_KMALLOC 0x000000001
|
||||
FAULT_READ_IO 0x000000002
|
||||
(no)user_xattr Setup Extended User Attributes. Note: xattr is enabled
|
||||
by default if CONFIG_EROFS_FS_XATTR is selected.
|
||||
(no)acl Setup POSIX Access Control List. Note: acl is enabled
|
||||
by default if CONFIG_EROFS_FS_POSIX_ACL is selected.
|
||||
cache_strategy=%s Select a strategy for cached decompression from now on:
|
||||
disabled: In-place I/O decompression only;
|
||||
readahead: Cache the last incomplete compressed physical
|
||||
cluster for further reading. It still does
|
||||
in-place I/O decompression for the rest
|
||||
compressed physical clusters;
|
||||
readaround: Cache the both ends of incomplete compressed
|
||||
physical clusters for further reading.
|
||||
It still does in-place I/O decompression
|
||||
for the rest compressed physical clusters.
|
||||
|
||||
On-disk details
|
||||
===============
|
39
MAINTAINERS
39
MAINTAINERS
@ -902,11 +902,12 @@ F: Documentation/devicetree/bindings/iio/adc/adi,ad7124.yaml
|
||||
|
||||
ANALOG DEVICES INC AD7606 DRIVER
|
||||
M: Stefan Popa <stefan.popa@analog.com>
|
||||
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/ad7606.c
|
||||
F: Documentation/devicetree/bindings/iio/adc/adi,ad7606.txt
|
||||
F: Documentation/devicetree/bindings/iio/adc/adi,ad7606.yaml
|
||||
|
||||
ANALOG DEVICES INC AD7768-1 DRIVER
|
||||
M: Stefan Popa <stefan.popa@analog.com>
|
||||
@ -944,6 +945,14 @@ L: linux-iio@vger.kernel.org
|
||||
F: include/linux/iio/imu/adis.h
|
||||
F: drivers/iio/imu/adis.c
|
||||
|
||||
ANALOG DEVICES INC ADIS16460 DRIVER
|
||||
M: Dragos Bogdan <dragos.bogdan@analog.com>
|
||||
S: Supported
|
||||
L: linux-iio@vger.kernel.org
|
||||
W: http://ez.analog.com/community/linux-device-drivers
|
||||
F: drivers/iio/imu/adis16460.c
|
||||
F: Documentation/devicetree/bindings/iio/imu/adi,adis16460.yaml
|
||||
|
||||
ANALOG DEVICES INC ADP5061 DRIVER
|
||||
M: Stefan Popa <stefan.popa@analog.com>
|
||||
L: linux-pm@vger.kernel.org
|
||||
@ -6050,6 +6059,13 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/kristoffer/linux-hpc.git
|
||||
F: drivers/video/fbdev/s1d13xxxfb.c
|
||||
F: include/video/s1d13xxxfb.h
|
||||
|
||||
EROFS FILE SYSTEM
|
||||
M: Gao Xiang <gaoxiang25@huawei.com>
|
||||
M: Chao Yu <yuchao0@huawei.com>
|
||||
L: linux-erofs@lists.ozlabs.org
|
||||
S: Maintained
|
||||
F: fs/erofs/
|
||||
|
||||
ERRSEQ ERROR TRACKING INFRASTRUCTURE
|
||||
M: Jeff Layton <jlayton@kernel.org>
|
||||
S: Maintained
|
||||
@ -6094,6 +6110,11 @@ F: include/trace/events/mdio.h
|
||||
F: include/uapi/linux/mdio.h
|
||||
F: include/uapi/linux/mii.h
|
||||
|
||||
EXFAT FILE SYSTEM
|
||||
M: Valdis Kletnieks <valdis.kletnieks@vt.edu>
|
||||
S: Maintained
|
||||
F: drivers/staging/exfat/
|
||||
|
||||
EXT2 FILE SYSTEM
|
||||
M: Jan Kara <jack@suse.com>
|
||||
L: linux-ext4@vger.kernel.org
|
||||
@ -7001,6 +7022,9 @@ M: Alex Elder <elder@kernel.org>
|
||||
M: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
||||
S: Maintained
|
||||
F: drivers/staging/greybus/
|
||||
F: drivers/greybus/
|
||||
F: include/linux/greybus.h
|
||||
F: include/linux/greybus/
|
||||
L: greybus-dev@lists.linaro.org (moderated for non-subscribers)
|
||||
|
||||
GREYBUS UART PROTOCOLS DRIVERS
|
||||
@ -12803,6 +12827,12 @@ F: drivers/i2c/busses/i2c-puv3.c
|
||||
F: drivers/video/fbdev/fb-puv3.c
|
||||
F: drivers/rtc/rtc-puv3.c
|
||||
|
||||
PLANTOWER PMS7003 AIR POLLUTION SENSOR DRIVER
|
||||
M: Tomasz Duszynski <tduszyns@gmail.com>
|
||||
S: Maintained
|
||||
F: drivers/iio/chemical/pms7003.c
|
||||
F: Documentation/devicetree/bindings/iio/chemical/plantower,pms7003.yaml
|
||||
|
||||
PMBUS HARDWARE MONITORING DRIVERS
|
||||
M: Guenter Roeck <linux@roeck-us.net>
|
||||
L: linux-hwmon@vger.kernel.org
|
||||
@ -15240,13 +15270,6 @@ M: H Hartley Sweeten <hsweeten@visionengravers.com>
|
||||
S: Odd Fixes
|
||||
F: drivers/staging/comedi/
|
||||
|
||||
STAGING - EROFS FILE SYSTEM
|
||||
M: Gao Xiang <gaoxiang25@huawei.com>
|
||||
M: Chao Yu <yuchao0@huawei.com>
|
||||
L: linux-erofs@lists.ozlabs.org
|
||||
S: Maintained
|
||||
F: drivers/staging/erofs/
|
||||
|
||||
STAGING - FIELDBUS SUBSYSTEM
|
||||
M: Sven Van Asbroeck <TheSven73@gmail.com>
|
||||
S: Maintained
|
||||
|
@ -142,6 +142,8 @@ source "drivers/hv/Kconfig"
|
||||
|
||||
source "drivers/xen/Kconfig"
|
||||
|
||||
source "drivers/greybus/Kconfig"
|
||||
|
||||
source "drivers/staging/Kconfig"
|
||||
|
||||
source "drivers/platform/Kconfig"
|
||||
|
@ -146,6 +146,7 @@ obj-$(CONFIG_BCMA) += bcma/
|
||||
obj-$(CONFIG_VHOST_RING) += vhost/
|
||||
obj-$(CONFIG_VHOST) += vhost/
|
||||
obj-$(CONFIG_VLYNQ) += vlynq/
|
||||
obj-$(CONFIG_GREYBUS) += greybus/
|
||||
obj-$(CONFIG_STAGING) += staging/
|
||||
obj-y += platform/
|
||||
|
||||
|
@ -100,16 +100,18 @@ static void ftm_quaddec_init(struct ftm_quaddec *ftm)
|
||||
ftm_set_write_protection(ftm);
|
||||
}
|
||||
|
||||
static void ftm_quaddec_disable(struct ftm_quaddec *ftm)
|
||||
static void ftm_quaddec_disable(void *ftm)
|
||||
{
|
||||
ftm_clear_write_protection(ftm);
|
||||
ftm_write(ftm, FTM_MODE, 0);
|
||||
ftm_write(ftm, FTM_QDCTRL, 0);
|
||||
struct ftm_quaddec *ftm_qua = ftm;
|
||||
|
||||
ftm_clear_write_protection(ftm_qua);
|
||||
ftm_write(ftm_qua, FTM_MODE, 0);
|
||||
ftm_write(ftm_qua, FTM_QDCTRL, 0);
|
||||
/*
|
||||
* This is enough to disable the counter. No clock has been
|
||||
* selected by writing to FTM_SC in init()
|
||||
*/
|
||||
ftm_set_write_protection(ftm);
|
||||
ftm_set_write_protection(ftm_qua);
|
||||
}
|
||||
|
||||
static int ftm_quaddec_get_prescaler(struct counter_device *counter,
|
||||
@ -317,20 +319,13 @@ static int ftm_quaddec_probe(struct platform_device *pdev)
|
||||
|
||||
ftm_quaddec_init(ftm);
|
||||
|
||||
ret = counter_register(&ftm->counter);
|
||||
ret = devm_add_action_or_reset(&pdev->dev, ftm_quaddec_disable, ftm);
|
||||
if (ret)
|
||||
ftm_quaddec_disable(ftm);
|
||||
return ret;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ftm_quaddec_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct ftm_quaddec *ftm = platform_get_drvdata(pdev);
|
||||
|
||||
counter_unregister(&ftm->counter);
|
||||
|
||||
ftm_quaddec_disable(ftm);
|
||||
ret = devm_counter_register(&pdev->dev, &ftm->counter);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -346,7 +341,6 @@ static struct platform_driver ftm_quaddec_driver = {
|
||||
.of_match_table = ftm_quaddec_match,
|
||||
},
|
||||
.probe = ftm_quaddec_probe,
|
||||
.remove = ftm_quaddec_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(ftm_quaddec_driver);
|
||||
|
32
drivers/greybus/Kconfig
Normal file
32
drivers/greybus/Kconfig
Normal file
@ -0,0 +1,32 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
menuconfig GREYBUS
|
||||
tristate "Greybus support"
|
||||
depends on SYSFS
|
||||
---help---
|
||||
This option enables the Greybus driver core. Greybus is an
|
||||
hardware protocol that was designed to provide Unipro with a
|
||||
sane application layer. It was originally designed for the
|
||||
ARA project, a module phone system, but has shown up in other
|
||||
phones, and can be tunneled over other busses in order to
|
||||
control hardware devices.
|
||||
|
||||
Say Y here to enable support for these types of drivers.
|
||||
|
||||
To compile this code as a module, chose M here: the module
|
||||
will be called greybus.ko
|
||||
|
||||
if GREYBUS
|
||||
|
||||
config GREYBUS_ES2
|
||||
tristate "Greybus ES3 USB host controller"
|
||||
depends on USB
|
||||
---help---
|
||||
Select this option if you have a Toshiba ES3 USB device that
|
||||
acts as a Greybus "host controller". This device is a bridge
|
||||
from a USB device to a Unipro network.
|
||||
|
||||
To compile this code as a module, chose M here: the module
|
||||
will be called gb-es2.ko
|
||||
|
||||
endif # GREYBUS
|
||||
|
26
drivers/greybus/Makefile
Normal file
26
drivers/greybus/Makefile
Normal file
@ -0,0 +1,26 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
# Greybus core
|
||||
greybus-y := core.o \
|
||||
debugfs.o \
|
||||
hd.o \
|
||||
manifest.o \
|
||||
module.o \
|
||||
interface.o \
|
||||
bundle.o \
|
||||
connection.o \
|
||||
control.o \
|
||||
svc.o \
|
||||
svc_watchdog.o \
|
||||
operation.o
|
||||
|
||||
obj-$(CONFIG_GREYBUS) += greybus.o
|
||||
|
||||
# needed for trace events
|
||||
ccflags-y += -I$(src)
|
||||
|
||||
# Greybus Host controller drivers
|
||||
gb-es2-y := es2.o
|
||||
|
||||
obj-$(CONFIG_GREYBUS_ES2) += gb-es2.o
|
||||
|
||||
|
63
drivers/greybus/arpc.h
Normal file
63
drivers/greybus/arpc.h
Normal file
@ -0,0 +1,63 @@
|
||||
/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
|
||||
/*
|
||||
* Copyright(c) 2016 Google Inc. All rights reserved.
|
||||
* Copyright(c) 2016 Linaro Ltd. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __ARPC_H
|
||||
#define __ARPC_H
|
||||
|
||||
/* APBridgeA RPC (ARPC) */
|
||||
|
||||
enum arpc_result {
|
||||
ARPC_SUCCESS = 0x00,
|
||||
ARPC_NO_MEMORY = 0x01,
|
||||
ARPC_INVALID = 0x02,
|
||||
ARPC_TIMEOUT = 0x03,
|
||||
ARPC_UNKNOWN_ERROR = 0xff,
|
||||
};
|
||||
|
||||
struct arpc_request_message {
|
||||
__le16 id; /* RPC unique id */
|
||||
__le16 size; /* Size in bytes of header + payload */
|
||||
__u8 type; /* RPC type */
|
||||
__u8 data[0]; /* ARPC data */
|
||||
} __packed;
|
||||
|
||||
struct arpc_response_message {
|
||||
__le16 id; /* RPC unique id */
|
||||
__u8 result; /* Result of RPC */
|
||||
} __packed;
|
||||
|
||||
/* ARPC requests */
|
||||
#define ARPC_TYPE_CPORT_CONNECTED 0x01
|
||||
#define ARPC_TYPE_CPORT_QUIESCE 0x02
|
||||
#define ARPC_TYPE_CPORT_CLEAR 0x03
|
||||
#define ARPC_TYPE_CPORT_FLUSH 0x04
|
||||
#define ARPC_TYPE_CPORT_SHUTDOWN 0x05
|
||||
|
||||
struct arpc_cport_connected_req {
|
||||
__le16 cport_id;
|
||||
} __packed;
|
||||
|
||||
struct arpc_cport_quiesce_req {
|
||||
__le16 cport_id;
|
||||
__le16 peer_space;
|
||||
__le16 timeout;
|
||||
} __packed;
|
||||
|
||||
struct arpc_cport_clear_req {
|
||||
__le16 cport_id;
|
||||
} __packed;
|
||||
|
||||
struct arpc_cport_flush_req {
|
||||
__le16 cport_id;
|
||||
} __packed;
|
||||
|
||||
struct arpc_cport_shutdown_req {
|
||||
__le16 cport_id;
|
||||
__le16 timeout;
|
||||
__u8 phase;
|
||||
} __packed;
|
||||
|
||||
#endif /* __ARPC_H */
|
@ -6,7 +6,7 @@
|
||||
* Copyright 2014-2015 Linaro Ltd.
|
||||
*/
|
||||
|
||||
#include "greybus.h"
|
||||
#include <linux/greybus.h>
|
||||
#include "greybus_trace.h"
|
||||
|
||||
static ssize_t bundle_class_show(struct device *dev,
|
@ -7,8 +7,8 @@
|
||||
*/
|
||||
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/greybus.h>
|
||||
|
||||
#include "greybus.h"
|
||||
#include "greybus_trace.h"
|
||||
|
||||
#define GB_CONNECTION_CPORT_QUIESCE_TIMEOUT 1000
|
@ -9,7 +9,7 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include "greybus.h"
|
||||
#include <linux/greybus.h>
|
||||
|
||||
/* Highest control-protocol version supported */
|
||||
#define GB_CONTROL_VERSION_MAJOR 0
|
@ -9,7 +9,7 @@
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#define CREATE_TRACE_POINTS
|
||||
#include "greybus.h"
|
||||
#include <linux/greybus.h>
|
||||
#include "greybus_trace.h"
|
||||
|
||||
#define GB_BUNDLE_AUTOSUSPEND_MS 3000
|
@ -7,8 +7,7 @@
|
||||
*/
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
|
||||
#include "greybus.h"
|
||||
#include <linux/greybus.h>
|
||||
|
||||
static struct dentry *gb_debug_root;
|
||||
|
@ -11,12 +11,11 @@
|
||||
#include <linux/kfifo.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/greybus.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#include "arpc.h"
|
||||
#include "greybus.h"
|
||||
#include "greybus_trace.h"
|
||||
#include "connection.h"
|
||||
|
||||
|
||||
/* Default timeout for USB vendor requests. */
|
@ -1,4 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Greybus driver and device API
|
||||
*
|
@ -8,8 +8,8 @@
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/greybus.h>
|
||||
|
||||
#include "greybus.h"
|
||||
#include "greybus_trace.h"
|
||||
|
||||
EXPORT_TRACEPOINT_SYMBOL_GPL(gb_hd_create);
|
||||
@ -31,7 +31,7 @@ int gb_hd_output(struct gb_host_device *hd, void *req, u16 size, u8 cmd,
|
||||
EXPORT_SYMBOL_GPL(gb_hd_output);
|
||||
|
||||
static ssize_t bus_id_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct gb_host_device *hd = to_gb_host_device(dev);
|
||||
|
||||
@ -70,7 +70,7 @@ EXPORT_SYMBOL_GPL(gb_hd_cport_release_reserved);
|
||||
|
||||
/* Locking: Caller guarantees serialisation */
|
||||
int gb_hd_cport_allocate(struct gb_host_device *hd, int cport_id,
|
||||
unsigned long flags)
|
||||
unsigned long flags)
|
||||
{
|
||||
struct ida *id_map = &hd->cport_id_map;
|
||||
int ida_start, ida_end;
|
||||
@ -122,9 +122,9 @@ struct device_type greybus_hd_type = {
|
||||
};
|
||||
|
||||
struct gb_host_device *gb_hd_create(struct gb_hd_driver *driver,
|
||||
struct device *parent,
|
||||
size_t buffer_size_max,
|
||||
size_t num_cports)
|
||||
struct device *parent,
|
||||
size_t buffer_size_max,
|
||||
size_t num_cports)
|
||||
{
|
||||
struct gb_host_device *hd;
|
||||
int ret;
|
@ -7,8 +7,8 @@
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/greybus.h>
|
||||
|
||||
#include "greybus.h"
|
||||
#include "greybus_trace.h"
|
||||
|
||||
#define GB_INTERFACE_MODE_SWITCH_TIMEOUT 2000
|
@ -6,7 +6,7 @@
|
||||
* Copyright 2014-2015 Linaro Ltd.
|
||||
*/
|
||||
|
||||
#include "greybus.h"
|
||||
#include <linux/greybus.h>
|
||||
|
||||
static const char *get_descriptor_type_string(u8 type)
|
||||
{
|
||||
@ -104,15 +104,15 @@ static int identify_descriptor(struct gb_interface *intf,
|
||||
size_t expected_size;
|
||||
|
||||
if (size < sizeof(*desc_header)) {
|
||||
dev_err(&intf->dev, "manifest too small (%zu < %zu)\n",
|
||||
size, sizeof(*desc_header));
|
||||
dev_err(&intf->dev, "manifest too small (%zu < %zu)\n", size,
|
||||
sizeof(*desc_header));
|
||||
return -EINVAL; /* Must at least have header */
|
||||
}
|
||||
|
||||
desc_size = le16_to_cpu(desc_header->size);
|
||||
if (desc_size > size) {
|
||||
dev_err(&intf->dev, "descriptor too big (%zu > %zu)\n",
|
||||
desc_size, size);
|
||||
desc_size, size);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -139,22 +139,22 @@ static int identify_descriptor(struct gb_interface *intf,
|
||||
case GREYBUS_TYPE_INVALID:
|
||||
default:
|
||||
dev_err(&intf->dev, "invalid descriptor type (%u)\n",
|
||||
desc_header->type);
|
||||
desc_header->type);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (desc_size < expected_size) {
|
||||
dev_err(&intf->dev, "%s descriptor too small (%zu < %zu)\n",
|
||||
get_descriptor_type_string(desc_header->type),
|
||||
desc_size, expected_size);
|
||||
get_descriptor_type_string(desc_header->type),
|
||||
desc_size, expected_size);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Descriptor bigger than what we expect */
|
||||
if (desc_size > expected_size) {
|
||||
dev_warn(&intf->dev, "%s descriptor size mismatch (want %zu got %zu)\n",
|
||||
get_descriptor_type_string(desc_header->type),
|
||||
expected_size, desc_size);
|
||||
get_descriptor_type_string(desc_header->type),
|
||||
expected_size, desc_size);
|
||||
}
|
||||
|
||||
descriptor = kzalloc(sizeof(*descriptor), GFP_KERNEL);
|
||||
@ -208,7 +208,7 @@ static char *gb_string_get(struct gb_interface *intf, u8 string_id)
|
||||
|
||||
/* Allocate an extra byte so we can guarantee it's NUL-terminated */
|
||||
string = kmemdup(&desc_string->string, desc_string->length + 1,
|
||||
GFP_KERNEL);
|
||||
GFP_KERNEL);
|
||||
if (!string)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
string[desc_string->length] = '\0';
|
||||
@ -264,8 +264,7 @@ static u32 gb_manifest_parse_cports(struct gb_bundle *bundle)
|
||||
desc_cport = tmp->data;
|
||||
if (cport_id == le16_to_cpu(desc_cport->id)) {
|
||||
dev_err(&bundle->dev,
|
||||
"duplicate CPort %u found\n",
|
||||
cport_id);
|
||||
"duplicate CPort %u found\n", cport_id);
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
@ -277,7 +276,7 @@ static u32 gb_manifest_parse_cports(struct gb_bundle *bundle)
|
||||
return 0;
|
||||
|
||||
bundle->cport_desc = kcalloc(count, sizeof(*bundle->cport_desc),
|
||||
GFP_KERNEL);
|
||||
GFP_KERNEL);
|
||||
if (!bundle->cport_desc)
|
||||
goto exit;
|
||||
|
||||
@ -287,7 +286,7 @@ static u32 gb_manifest_parse_cports(struct gb_bundle *bundle)
|
||||
list_for_each_entry_safe(desc, next, &list, links) {
|
||||
desc_cport = desc->data;
|
||||
memcpy(&bundle->cport_desc[i++], desc_cport,
|
||||
sizeof(*desc_cport));
|
||||
sizeof(*desc_cport));
|
||||
|
||||
/* Release the cport descriptor */
|
||||
release_manifest_descriptor(desc);
|
||||
@ -333,9 +332,9 @@ static u32 gb_manifest_parse_bundles(struct gb_interface *intf)
|
||||
/* Ignore any legacy control bundles */
|
||||
if (bundle_id == GB_CONTROL_BUNDLE_ID) {
|
||||
dev_dbg(&intf->dev, "%s - ignoring control bundle\n",
|
||||
__func__);
|
||||
__func__);
|
||||
release_cport_descriptors(&intf->manifest_descs,
|
||||
bundle_id);
|
||||
bundle_id);
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -468,7 +467,7 @@ bool gb_manifest_parse(struct gb_interface *intf, void *data, size_t size)
|
||||
/* we have to have at _least_ the manifest header */
|
||||
if (size < sizeof(*header)) {
|
||||
dev_err(&intf->dev, "short manifest (%zu < %zu)\n",
|
||||
size, sizeof(*header));
|
||||
size, sizeof(*header));
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -478,15 +477,15 @@ bool gb_manifest_parse(struct gb_interface *intf, void *data, size_t size)
|
||||
manifest_size = le16_to_cpu(header->size);
|
||||
if (manifest_size != size) {
|
||||
dev_err(&intf->dev, "manifest size mismatch (%zu != %u)\n",
|
||||
size, manifest_size);
|
||||
size, manifest_size);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Validate major/minor number */
|
||||
if (header->version_major > GREYBUS_VERSION_MAJOR) {
|
||||
dev_err(&intf->dev, "manifest version too new (%u.%u > %u.%u)\n",
|
||||
header->version_major, header->version_minor,
|
||||
GREYBUS_VERSION_MAJOR, GREYBUS_VERSION_MINOR);
|
||||
header->version_major, header->version_minor,
|
||||
GREYBUS_VERSION_MAJOR, GREYBUS_VERSION_MINOR);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -513,7 +512,7 @@ bool gb_manifest_parse(struct gb_interface *intf, void *data, size_t size)
|
||||
}
|
||||
if (found != 1) {
|
||||
dev_err(&intf->dev, "manifest must have 1 interface descriptor (%u found)\n",
|
||||
found);
|
||||
found);
|
||||
result = false;
|
||||
goto out;
|
||||
}
|
@ -6,7 +6,7 @@
|
||||
* Copyright 2016 Linaro Ltd.
|
||||
*/
|
||||
|
||||
#include "greybus.h"
|
||||
#include <linux/greybus.h>
|
||||
#include "greybus_trace.h"
|
||||
|
||||
static ssize_t eject_store(struct device *dev,
|
@ -12,8 +12,8 @@
|
||||
#include <linux/sched.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/greybus.h>
|
||||
|
||||
#include "greybus.h"
|
||||
#include "greybus_trace.h"
|
||||
|
||||
static struct kmem_cache *gb_operation_cache;
|
@ -8,8 +8,7 @@
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#include "greybus.h"
|
||||
#include <linux/greybus.h>
|
||||
|
||||
#define SVC_INTF_EJECT_TIMEOUT 9000
|
||||
#define SVC_INTF_ACTIVATE_TIMEOUT 6000
|
@ -8,7 +8,7 @@
|
||||
#include <linux/delay.h>
|
||||
#include <linux/suspend.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include "greybus.h"
|
||||
#include <linux/greybus.h>
|
||||
|
||||
#define SVC_WATCHDOG_PERIOD (2 * HZ)
|
||||
|
@ -202,9 +202,7 @@ config HID_SENSOR_ACCEL_3D
|
||||
|
||||
config IIO_CROS_EC_ACCEL_LEGACY
|
||||
tristate "ChromeOS EC Legacy Accelerometer Sensor"
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
select CROS_EC_LPC_REGISTER_DEVICE
|
||||
depends on IIO_CROS_EC_SENSORS_CORE
|
||||
help
|
||||
Say yes here to get support for accelerometers on Chromebook using
|
||||
legacy EC firmware.
|
||||
|
@ -5,13 +5,14 @@
|
||||
* Copyright 2017 Google, Inc
|
||||
*
|
||||
* This driver uses the memory mapper cros-ec interface to communicate
|
||||
* with the Chrome OS EC about accelerometer data.
|
||||
* with the Chrome OS EC about accelerometer data or older commands.
|
||||
* Accelerometer access is presented through iio sysfs.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/common/cros_ec_sensors_core.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/kfifo_buf.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
@ -25,160 +26,41 @@
|
||||
|
||||
#define DRV_NAME "cros-ec-accel-legacy"
|
||||
|
||||
#define CROS_EC_SENSOR_LEGACY_NUM 2
|
||||
/*
|
||||
* Sensor scale hard coded at 10 bits per g, computed as:
|
||||
* g / (2^10 - 1) = 0.009586168; with g = 9.80665 m.s^-2
|
||||
*/
|
||||
#define ACCEL_LEGACY_NSCALE 9586168
|
||||
|
||||
/* Indices for EC sensor values. */
|
||||
enum {
|
||||
X,
|
||||
Y,
|
||||
Z,
|
||||
MAX_AXIS,
|
||||
};
|
||||
|
||||
/* State data for cros_ec_accel_legacy iio driver. */
|
||||
struct cros_ec_accel_legacy_state {
|
||||
struct cros_ec_device *ec;
|
||||
|
||||
/*
|
||||
* Array holding data from a single capture. 2 bytes per channel
|
||||
* for the 3 channels plus the timestamp which is always last and
|
||||
* 8-bytes aligned.
|
||||
*/
|
||||
s16 capture_data[8];
|
||||
s8 sign[MAX_AXIS];
|
||||
u8 sensor_num;
|
||||
};
|
||||
|
||||
static int ec_cmd_read_u8(struct cros_ec_device *ec, unsigned int offset,
|
||||
u8 *dest)
|
||||
static int cros_ec_accel_legacy_read_cmd(struct iio_dev *indio_dev,
|
||||
unsigned long scan_mask, s16 *data)
|
||||
{
|
||||
return ec->cmd_readmem(ec, offset, 1, dest);
|
||||
}
|
||||
|
||||
static int ec_cmd_read_u16(struct cros_ec_device *ec, unsigned int offset,
|
||||
u16 *dest)
|
||||
{
|
||||
__le16 tmp;
|
||||
int ret = ec->cmd_readmem(ec, offset, 2, &tmp);
|
||||
|
||||
*dest = le16_to_cpu(tmp);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* read_ec_until_not_busy() - Read from EC status byte until it reads not busy.
|
||||
* @st: Pointer to state information for device.
|
||||
*
|
||||
* This function reads EC status until its busy bit gets cleared. It does not
|
||||
* wait indefinitely and returns -EIO if the EC status is still busy after a
|
||||
* few hundreds milliseconds.
|
||||
*
|
||||
* Return: 8-bit status if ok, -EIO on error
|
||||
*/
|
||||
static int read_ec_until_not_busy(struct cros_ec_accel_legacy_state *st)
|
||||
{
|
||||
struct cros_ec_device *ec = st->ec;
|
||||
u8 status;
|
||||
int attempts = 0;
|
||||
|
||||
ec_cmd_read_u8(ec, EC_MEMMAP_ACC_STATUS, &status);
|
||||
while (status & EC_MEMMAP_ACC_STATUS_BUSY_BIT) {
|
||||
/* Give up after enough attempts, return error. */
|
||||
if (attempts++ >= 50)
|
||||
return -EIO;
|
||||
|
||||
/* Small delay every so often. */
|
||||
if (attempts % 5 == 0)
|
||||
msleep(25);
|
||||
|
||||
ec_cmd_read_u8(ec, EC_MEMMAP_ACC_STATUS, &status);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* read_ec_accel_data_unsafe() - Read acceleration data from EC shared memory.
|
||||
* @st: Pointer to state information for device.
|
||||
* @scan_mask: Bitmap of the sensor indices to scan.
|
||||
* @data: Location to store data.
|
||||
*
|
||||
* This is the unsafe function for reading the EC data. It does not guarantee
|
||||
* that the EC will not modify the data as it is being read in.
|
||||
*/
|
||||
static void read_ec_accel_data_unsafe(struct cros_ec_accel_legacy_state *st,
|
||||
unsigned long scan_mask, s16 *data)
|
||||
{
|
||||
int i = 0;
|
||||
int num_enabled = bitmap_weight(&scan_mask, MAX_AXIS);
|
||||
|
||||
/* Read all sensors enabled in scan_mask. Each value is 2 bytes. */
|
||||
while (num_enabled--) {
|
||||
i = find_next_bit(&scan_mask, MAX_AXIS, i);
|
||||
ec_cmd_read_u16(st->ec,
|
||||
EC_MEMMAP_ACC_DATA +
|
||||
sizeof(s16) *
|
||||
(1 + i + st->sensor_num * MAX_AXIS),
|
||||
data);
|
||||
*data *= st->sign[i];
|
||||
i++;
|
||||
data++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* read_ec_accel_data() - Read acceleration data from EC shared memory.
|
||||
* @st: Pointer to state information for device.
|
||||
* @scan_mask: Bitmap of the sensor indices to scan.
|
||||
* @data: Location to store data.
|
||||
*
|
||||
* This is the safe function for reading the EC data. It guarantees that
|
||||
* the data sampled was not modified by the EC while being read.
|
||||
*
|
||||
* Return: 0 if ok, -ve on error
|
||||
*/
|
||||
static int read_ec_accel_data(struct cros_ec_accel_legacy_state *st,
|
||||
unsigned long scan_mask, s16 *data)
|
||||
{
|
||||
u8 samp_id = 0xff;
|
||||
u8 status = 0;
|
||||
struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
int attempts = 0;
|
||||
unsigned int i;
|
||||
u8 sensor_num;
|
||||
|
||||
/*
|
||||
* Continually read all data from EC until the status byte after
|
||||
* all reads reflects that the EC is not busy and the sample id
|
||||
* matches the sample id from before all reads. This guarantees
|
||||
* that data read in was not modified by the EC while reading.
|
||||
* Read all sensor data through a command.
|
||||
* Save sensor_num, it is assumed to stay.
|
||||
*/
|
||||
while ((status & (EC_MEMMAP_ACC_STATUS_BUSY_BIT |
|
||||
EC_MEMMAP_ACC_STATUS_SAMPLE_ID_MASK)) != samp_id) {
|
||||
/* If we have tried to read too many times, return error. */
|
||||
if (attempts++ >= 5)
|
||||
return -EIO;
|
||||
sensor_num = st->param.info.sensor_num;
|
||||
st->param.cmd = MOTIONSENSE_CMD_DUMP;
|
||||
st->param.dump.max_sensor_count = CROS_EC_SENSOR_LEGACY_NUM;
|
||||
ret = cros_ec_motion_send_host_cmd(st,
|
||||
sizeof(st->resp->dump) + CROS_EC_SENSOR_LEGACY_NUM *
|
||||
sizeof(struct ec_response_motion_sensor_data));
|
||||
st->param.info.sensor_num = sensor_num;
|
||||
if (ret != 0) {
|
||||
dev_warn(&indio_dev->dev, "Unable to read sensor data\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Read status byte until EC is not busy. */
|
||||
ret = read_ec_until_not_busy(st);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
status = ret;
|
||||
|
||||
/*
|
||||
* Store the current sample id so that we can compare to the
|
||||
* sample id after reading the data.
|
||||
*/
|
||||
samp_id = status & EC_MEMMAP_ACC_STATUS_SAMPLE_ID_MASK;
|
||||
|
||||
/* Read all EC data, format it, and store it into data. */
|
||||
read_ec_accel_data_unsafe(st, scan_mask, data);
|
||||
|
||||
/* Read status byte. */
|
||||
ec_cmd_read_u8(st->ec, EC_MEMMAP_ACC_STATUS, &status);
|
||||
for_each_set_bit(i, &scan_mask, indio_dev->masklength) {
|
||||
*data = st->resp->dump.sensor[sensor_num].data[i] *
|
||||
st->sign[i];
|
||||
data++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -188,28 +70,40 @@ static int cros_ec_accel_legacy_read(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct cros_ec_accel_legacy_state *st = iio_priv(indio_dev);
|
||||
struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
|
||||
s16 data = 0;
|
||||
int ret = IIO_VAL_INT;
|
||||
int ret;
|
||||
int idx = chan->scan_index;
|
||||
|
||||
mutex_lock(&st->cmd_lock);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = read_ec_accel_data(st, (1 << chan->scan_index), &data);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = st->read_ec_sensors_data(indio_dev, 1 << idx, &data);
|
||||
if (ret < 0)
|
||||
break;
|
||||
ret = IIO_VAL_INT;
|
||||
*val = data;
|
||||
return IIO_VAL_INT;
|
||||
break;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
WARN_ON(st->type != MOTIONSENSE_TYPE_ACCEL);
|
||||
*val = 0;
|
||||
*val2 = ACCEL_LEGACY_NSCALE;
|
||||
return IIO_VAL_INT_PLUS_NANO;
|
||||
ret = IIO_VAL_INT_PLUS_NANO;
|
||||
break;
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
/* Calibration not supported. */
|
||||
*val = 0;
|
||||
return IIO_VAL_INT;
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
ret = cros_ec_sensors_core_read(st, chan, val, val2,
|
||||
mask);
|
||||
break;
|
||||
}
|
||||
mutex_unlock(&st->cmd_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cros_ec_accel_legacy_write(struct iio_dev *indio_dev,
|
||||
@ -231,86 +125,14 @@ static const struct iio_info cros_ec_accel_legacy_info = {
|
||||
.write_raw = &cros_ec_accel_legacy_write,
|
||||
};
|
||||
|
||||
/**
|
||||
* cros_ec_accel_legacy_capture() - The trigger handler function
|
||||
* @irq: The interrupt number.
|
||||
* @p: Private data - always a pointer to the poll func.
|
||||
*
|
||||
* On a trigger event occurring, if the pollfunc is attached then this
|
||||
* handler is called as a threaded interrupt (and hence may sleep). It
|
||||
* is responsible for grabbing data from the device and pushing it into
|
||||
* the associated buffer.
|
||||
*
|
||||
* Return: IRQ_HANDLED
|
||||
/*
|
||||
* Present the channel using HTML5 standard:
|
||||
* need to invert X and Y and invert some lid axis.
|
||||
*/
|
||||
static irqreturn_t cros_ec_accel_legacy_capture(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct cros_ec_accel_legacy_state *st = iio_priv(indio_dev);
|
||||
|
||||
/* Clear capture data. */
|
||||
memset(st->capture_data, 0, sizeof(st->capture_data));
|
||||
|
||||
/*
|
||||
* Read data based on which channels are enabled in scan mask. Note
|
||||
* that on a capture we are always reading the calibrated data.
|
||||
*/
|
||||
read_ec_accel_data(st, *indio_dev->active_scan_mask, st->capture_data);
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, (void *)st->capture_data,
|
||||
iio_get_time_ns(indio_dev));
|
||||
|
||||
/*
|
||||
* Tell the core we are done with this trigger and ready for the
|
||||
* next one.
|
||||
*/
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static char *cros_ec_accel_legacy_loc_strings[] = {
|
||||
[MOTIONSENSE_LOC_BASE] = "base",
|
||||
[MOTIONSENSE_LOC_LID] = "lid",
|
||||
[MOTIONSENSE_LOC_MAX] = "unknown",
|
||||
};
|
||||
|
||||
static ssize_t cros_ec_accel_legacy_loc(struct iio_dev *indio_dev,
|
||||
uintptr_t private,
|
||||
const struct iio_chan_spec *chan,
|
||||
char *buf)
|
||||
{
|
||||
struct cros_ec_accel_legacy_state *st = iio_priv(indio_dev);
|
||||
|
||||
return sprintf(buf, "%s\n",
|
||||
cros_ec_accel_legacy_loc_strings[st->sensor_num +
|
||||
MOTIONSENSE_LOC_BASE]);
|
||||
}
|
||||
|
||||
static ssize_t cros_ec_accel_legacy_id(struct iio_dev *indio_dev,
|
||||
uintptr_t private,
|
||||
const struct iio_chan_spec *chan,
|
||||
char *buf)
|
||||
{
|
||||
struct cros_ec_accel_legacy_state *st = iio_priv(indio_dev);
|
||||
|
||||
return sprintf(buf, "%d\n", st->sensor_num);
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec_ext_info cros_ec_accel_legacy_ext_info[] = {
|
||||
{
|
||||
.name = "id",
|
||||
.shared = IIO_SHARED_BY_ALL,
|
||||
.read = cros_ec_accel_legacy_id,
|
||||
},
|
||||
{
|
||||
.name = "location",
|
||||
.shared = IIO_SHARED_BY_ALL,
|
||||
.read = cros_ec_accel_legacy_loc,
|
||||
},
|
||||
{ }
|
||||
};
|
||||
#define CROS_EC_ACCEL_ROTATE_AXIS(_axis) \
|
||||
((_axis) == CROS_EC_SENSOR_Z ? CROS_EC_SENSOR_Z : \
|
||||
((_axis) == CROS_EC_SENSOR_X ? CROS_EC_SENSOR_Y : \
|
||||
CROS_EC_SENSOR_X))
|
||||
|
||||
#define CROS_EC_ACCEL_LEGACY_CHAN(_axis) \
|
||||
{ \
|
||||
@ -321,28 +143,28 @@ static const struct iio_chan_spec_ext_info cros_ec_accel_legacy_ext_info[] = {
|
||||
BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_CALIBBIAS), \
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.ext_info = cros_ec_accel_legacy_ext_info, \
|
||||
.ext_info = cros_ec_sensors_ext_info, \
|
||||
.scan_type = { \
|
||||
.sign = 's', \
|
||||
.realbits = 16, \
|
||||
.storagebits = 16, \
|
||||
.realbits = CROS_EC_SENSOR_BITS, \
|
||||
.storagebits = CROS_EC_SENSOR_BITS, \
|
||||
}, \
|
||||
.scan_index = CROS_EC_ACCEL_ROTATE_AXIS(_axis), \
|
||||
} \
|
||||
|
||||
static struct iio_chan_spec ec_accel_channels[] = {
|
||||
CROS_EC_ACCEL_LEGACY_CHAN(X),
|
||||
CROS_EC_ACCEL_LEGACY_CHAN(Y),
|
||||
CROS_EC_ACCEL_LEGACY_CHAN(Z),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(MAX_AXIS)
|
||||
static const struct iio_chan_spec cros_ec_accel_legacy_channels[] = {
|
||||
CROS_EC_ACCEL_LEGACY_CHAN(CROS_EC_SENSOR_X),
|
||||
CROS_EC_ACCEL_LEGACY_CHAN(CROS_EC_SENSOR_Y),
|
||||
CROS_EC_ACCEL_LEGACY_CHAN(CROS_EC_SENSOR_Z),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(CROS_EC_SENSOR_MAX_AXIS)
|
||||
};
|
||||
|
||||
static int cros_ec_accel_legacy_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct cros_ec_dev *ec = dev_get_drvdata(dev->parent);
|
||||
struct cros_ec_sensor_platform *sensor_platform = dev_get_platdata(dev);
|
||||
struct iio_dev *indio_dev;
|
||||
struct cros_ec_accel_legacy_state *state;
|
||||
struct cros_ec_sensors_core_state *state;
|
||||
int ret;
|
||||
|
||||
if (!ec || !ec->ec_dev) {
|
||||
@ -350,46 +172,32 @@ static int cros_ec_accel_legacy_probe(struct platform_device *pdev)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!ec->ec_dev->cmd_readmem) {
|
||||
dev_warn(&pdev->dev, "EC does not support direct reads.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*state));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, indio_dev);
|
||||
state = iio_priv(indio_dev);
|
||||
state->ec = ec->ec_dev;
|
||||
state->sensor_num = sensor_platform->sensor_num;
|
||||
ret = cros_ec_sensors_core_init(pdev, indio_dev, true);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
indio_dev->dev.parent = dev;
|
||||
indio_dev->name = pdev->name;
|
||||
indio_dev->channels = ec_accel_channels;
|
||||
/*
|
||||
* Present the channel using HTML5 standard:
|
||||
* need to invert X and Y and invert some lid axis.
|
||||
*/
|
||||
ec_accel_channels[X].scan_index = Y;
|
||||
ec_accel_channels[Y].scan_index = X;
|
||||
ec_accel_channels[Z].scan_index = Z;
|
||||
|
||||
state->sign[Y] = 1;
|
||||
|
||||
if (state->sensor_num == MOTIONSENSE_LOC_LID)
|
||||
state->sign[X] = state->sign[Z] = -1;
|
||||
else
|
||||
state->sign[X] = state->sign[Z] = 1;
|
||||
|
||||
indio_dev->num_channels = ARRAY_SIZE(ec_accel_channels);
|
||||
indio_dev->dev.parent = &pdev->dev;
|
||||
indio_dev->info = &cros_ec_accel_legacy_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
state = iio_priv(indio_dev);
|
||||
|
||||
if (state->ec->cmd_readmem != NULL)
|
||||
state->read_ec_sensors_data = cros_ec_sensors_read_lpc;
|
||||
else
|
||||
state->read_ec_sensors_data = cros_ec_accel_legacy_read_cmd;
|
||||
|
||||
indio_dev->channels = cros_ec_accel_legacy_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(cros_ec_accel_legacy_channels);
|
||||
/* The lid sensor needs to be presented inverted. */
|
||||
if (state->loc == MOTIONSENSE_LOC_LID) {
|
||||
state->sign[CROS_EC_SENSOR_X] = -1;
|
||||
state->sign[CROS_EC_SENSOR_Z] = -1;
|
||||
}
|
||||
|
||||
ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
|
||||
cros_ec_accel_legacy_capture,
|
||||
NULL);
|
||||
cros_ec_sensors_capture, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -1486,8 +1486,8 @@ static const struct acpi_device_id kx_acpi_match[] = {
|
||||
{"KIOX0008", KXCJ91008},
|
||||
{"KIOX0009", KXTJ21009},
|
||||
{"KIOX000A", KXCJ91008},
|
||||
{"KIOX010A", KXCJ91008}, /* KXCJ91008 inside the display of a 2-in-1 */
|
||||
{"KIOX020A", KXCJ91008},
|
||||
{"KIOX010A", KXCJ91008}, /* KXCJ91008 in the display of a yoga 2-in-1 */
|
||||
{"KIOX020A", KXCJ91008}, /* KXCJ91008 in the base of a yoga 2-in-1 */
|
||||
{"KXTJ1009", KXTJ21009},
|
||||
{"KXJ2109", KXTJ21009},
|
||||
{"SMO8500", KXCJ91008},
|
||||
|
@ -424,7 +424,7 @@ static int mxc4005_probe(struct i2c_client *client,
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &mxc4005_info;
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev,
|
||||
ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev,
|
||||
iio_pollfunc_store_time,
|
||||
mxc4005_trigger_handler,
|
||||
NULL);
|
||||
@ -452,7 +452,7 @@ static int mxc4005_probe(struct i2c_client *client,
|
||||
if (ret) {
|
||||
dev_err(&client->dev,
|
||||
"failed to init threaded irq\n");
|
||||
goto err_buffer_cleanup;
|
||||
return ret;
|
||||
}
|
||||
|
||||
data->dready_trig->dev.parent = &client->dev;
|
||||
@ -460,43 +460,16 @@ static int mxc4005_probe(struct i2c_client *client,
|
||||
iio_trigger_set_drvdata(data->dready_trig, indio_dev);
|
||||
indio_dev->trig = data->dready_trig;
|
||||
iio_trigger_get(indio_dev->trig);
|
||||
ret = iio_trigger_register(data->dready_trig);
|
||||
ret = devm_iio_trigger_register(&client->dev,
|
||||
data->dready_trig);
|
||||
if (ret) {
|
||||
dev_err(&client->dev,
|
||||
"failed to register trigger\n");
|
||||
goto err_trigger_unregister;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev,
|
||||
"unable to register iio device %d\n", ret);
|
||||
goto err_buffer_cleanup;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_trigger_unregister:
|
||||
iio_trigger_unregister(data->dready_trig);
|
||||
err_buffer_cleanup:
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mxc4005_remove(struct i2c_client *client)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
struct mxc4005_data *data = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
if (data->dready_trig)
|
||||
iio_trigger_unregister(data->dready_trig);
|
||||
|
||||
return 0;
|
||||
return devm_iio_device_register(&client->dev, indio_dev);
|
||||
}
|
||||
|
||||
static const struct acpi_device_id mxc4005_acpi_match[] = {
|
||||
@ -517,7 +490,6 @@ static struct i2c_driver mxc4005_driver = {
|
||||
.acpi_match_table = ACPI_PTR(mxc4005_acpi_match),
|
||||
},
|
||||
.probe = mxc4005_probe,
|
||||
.remove = mxc4005_remove,
|
||||
.id_table = mxc4005_id,
|
||||
};
|
||||
|
||||
|
@ -111,7 +111,7 @@
|
||||
/* Currently unsupported */
|
||||
#define SCA3000_MD_CTRL_AND_Y BIT(3)
|
||||
#define SCA3000_MD_CTRL_AND_X BIT(4)
|
||||
#define SAC3000_MD_CTRL_AND_Z BIT(5)
|
||||
#define SCA3000_MD_CTRL_AND_Z BIT(5)
|
||||
|
||||
/*
|
||||
* Some control registers of complex access methods requiring this register to
|
||||
|
@ -68,6 +68,7 @@ static const struct st_sensors_platform_data default_accel_pdata = {
|
||||
.drdy_int_pin = 1,
|
||||
};
|
||||
|
||||
const struct st_sensor_settings *st_accel_get_settings(const char *name);
|
||||
int st_accel_common_probe(struct iio_dev *indio_dev);
|
||||
void st_accel_common_remove(struct iio_dev *indio_dev);
|
||||
|
||||
|
@ -29,65 +29,51 @@ int st_accel_trig_set_state(struct iio_trigger *trig, bool state)
|
||||
return st_sensors_set_dataready_irq(indio_dev, state);
|
||||
}
|
||||
|
||||
static int st_accel_buffer_preenable(struct iio_dev *indio_dev)
|
||||
{
|
||||
return st_sensors_set_enable(indio_dev, true);
|
||||
}
|
||||
|
||||
static int st_accel_buffer_postenable(struct iio_dev *indio_dev)
|
||||
{
|
||||
int err;
|
||||
struct st_sensor_data *adata = iio_priv(indio_dev);
|
||||
|
||||
adata->buffer_data = kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
|
||||
if (adata->buffer_data == NULL) {
|
||||
err = -ENOMEM;
|
||||
goto allocate_memory_error;
|
||||
}
|
||||
|
||||
err = iio_triggered_buffer_postenable(indio_dev);
|
||||
if (err < 0)
|
||||
goto st_accel_buffer_postenable_error;
|
||||
return err;
|
||||
|
||||
err = st_sensors_set_axis_enable(indio_dev,
|
||||
(u8)indio_dev->active_scan_mask[0]);
|
||||
(u8)indio_dev->active_scan_mask[0]);
|
||||
if (err < 0)
|
||||
goto st_sensors_set_axis_enable_error;
|
||||
goto st_accel_buffer_predisable;
|
||||
|
||||
return err;
|
||||
err = st_sensors_set_enable(indio_dev, true);
|
||||
if (err < 0)
|
||||
goto st_accel_buffer_enable_all_axis;
|
||||
|
||||
st_sensors_set_axis_enable_error:
|
||||
return 0;
|
||||
|
||||
st_accel_buffer_enable_all_axis:
|
||||
st_sensors_set_axis_enable(indio_dev, ST_SENSORS_ENABLE_ALL_AXIS);
|
||||
st_accel_buffer_predisable:
|
||||
iio_triggered_buffer_predisable(indio_dev);
|
||||
st_accel_buffer_postenable_error:
|
||||
kfree(adata->buffer_data);
|
||||
allocate_memory_error:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int st_accel_buffer_predisable(struct iio_dev *indio_dev)
|
||||
{
|
||||
int err, err2;
|
||||
struct st_sensor_data *adata = iio_priv(indio_dev);
|
||||
|
||||
err = st_sensors_set_axis_enable(indio_dev, ST_SENSORS_ENABLE_ALL_AXIS);
|
||||
if (err < 0)
|
||||
goto st_accel_buffer_predisable_error;
|
||||
|
||||
err = st_sensors_set_enable(indio_dev, false);
|
||||
if (err < 0)
|
||||
goto st_accel_buffer_predisable_error;
|
||||
goto st_accel_buffer_predisable;
|
||||
|
||||
st_accel_buffer_predisable_error:
|
||||
err = st_sensors_set_axis_enable(indio_dev, ST_SENSORS_ENABLE_ALL_AXIS);
|
||||
|
||||
st_accel_buffer_predisable:
|
||||
err2 = iio_triggered_buffer_predisable(indio_dev);
|
||||
if (!err)
|
||||
err = err2;
|
||||
|
||||
kfree(adata->buffer_data);
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct iio_buffer_setup_ops st_accel_buffer_setup_ops = {
|
||||
.preenable = &st_accel_buffer_preenable,
|
||||
.postenable = &st_accel_buffer_postenable,
|
||||
.predisable = &st_accel_buffer_predisable,
|
||||
};
|
||||
|
@ -13,7 +13,6 @@
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/gpio.h>
|
||||
@ -1147,32 +1146,45 @@ out:
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* st_accel_get_settings() - get sensor settings from device name
|
||||
* @name: device name buffer reference.
|
||||
*
|
||||
* Return: valid reference on success, NULL otherwise.
|
||||
*/
|
||||
const struct st_sensor_settings *st_accel_get_settings(const char *name)
|
||||
{
|
||||
int index = st_sensors_get_settings_index(name,
|
||||
st_accel_sensors_settings,
|
||||
ARRAY_SIZE(st_accel_sensors_settings));
|
||||
if (index < 0)
|
||||
return NULL;
|
||||
|
||||
return &st_accel_sensors_settings[index];
|
||||
}
|
||||
EXPORT_SYMBOL(st_accel_get_settings);
|
||||
|
||||
int st_accel_common_probe(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct st_sensor_data *adata = iio_priv(indio_dev);
|
||||
struct st_sensors_platform_data *pdata =
|
||||
(struct st_sensors_platform_data *)adata->dev->platform_data;
|
||||
int irq = adata->get_irq_data_ready(indio_dev);
|
||||
struct iio_chan_spec *channels;
|
||||
size_t channels_size;
|
||||
int err;
|
||||
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &accel_info;
|
||||
mutex_init(&adata->tb.buf_lock);
|
||||
|
||||
err = st_sensors_power_enable(indio_dev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = st_sensors_check_device_support(indio_dev,
|
||||
ARRAY_SIZE(st_accel_sensors_settings),
|
||||
st_accel_sensors_settings);
|
||||
err = st_sensors_verify_id(indio_dev);
|
||||
if (err < 0)
|
||||
goto st_accel_power_off;
|
||||
|
||||
adata->num_data_channels = ST_ACCEL_NUMBER_DATA_CHANNELS;
|
||||
adata->multiread_bit = adata->sensor_settings->multi_read_bit;
|
||||
indio_dev->num_channels = ST_SENSORS_NUMBER_ALL_CHANNELS;
|
||||
|
||||
channels_size = indio_dev->num_channels * sizeof(struct iio_chan_spec);
|
||||
@ -1204,7 +1216,7 @@ int st_accel_common_probe(struct iio_dev *indio_dev)
|
||||
if (err < 0)
|
||||
goto st_accel_power_off;
|
||||
|
||||
if (irq > 0) {
|
||||
if (adata->irq > 0) {
|
||||
err = st_sensors_allocate_trigger(indio_dev,
|
||||
ST_ACCEL_TRIGGER_OPS);
|
||||
if (err < 0)
|
||||
@ -1221,7 +1233,7 @@ int st_accel_common_probe(struct iio_dev *indio_dev)
|
||||
return 0;
|
||||
|
||||
st_accel_device_register_error:
|
||||
if (irq > 0)
|
||||
if (adata->irq > 0)
|
||||
st_sensors_deallocate_trigger(indio_dev);
|
||||
st_accel_probe_trigger_error:
|
||||
st_accel_deallocate_ring(indio_dev);
|
||||
@ -1239,7 +1251,7 @@ void st_accel_common_remove(struct iio_dev *indio_dev)
|
||||
st_sensors_power_disable(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
if (adata->get_irq_data_ready(indio_dev) > 0)
|
||||
if (adata->irq > 0)
|
||||
st_sensors_deallocate_trigger(indio_dev);
|
||||
|
||||
st_accel_deallocate_ring(indio_dev);
|
||||
|
@ -150,22 +150,33 @@ MODULE_DEVICE_TABLE(i2c, st_accel_id_table);
|
||||
|
||||
static int st_accel_i2c_probe(struct i2c_client *client)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
const struct st_sensor_settings *settings;
|
||||
struct st_sensor_data *adata;
|
||||
struct iio_dev *indio_dev;
|
||||
const char *match;
|
||||
int ret;
|
||||
|
||||
match = device_get_match_data(&client->dev);
|
||||
if (match)
|
||||
strlcpy(client->name, match, sizeof(client->name));
|
||||
|
||||
settings = st_accel_get_settings(client->name);
|
||||
if (!settings) {
|
||||
dev_err(&client->dev, "device name %s not recognized.\n",
|
||||
client->name);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*adata));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
adata = iio_priv(indio_dev);
|
||||
adata->sensor_settings = (struct st_sensor_settings *)settings;
|
||||
|
||||
match = device_get_match_data(&client->dev);
|
||||
if (match)
|
||||
strlcpy(client->name, match, sizeof(client->name));
|
||||
|
||||
st_sensors_i2c_configure(indio_dev, client, adata);
|
||||
ret = st_sensors_i2c_configure(indio_dev, client);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = st_accel_common_probe(indio_dev);
|
||||
if (ret < 0)
|
||||
|
@ -102,19 +102,31 @@ MODULE_DEVICE_TABLE(of, st_accel_of_match);
|
||||
|
||||
static int st_accel_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
const struct st_sensor_settings *settings;
|
||||
struct st_sensor_data *adata;
|
||||
struct iio_dev *indio_dev;
|
||||
int err;
|
||||
|
||||
st_sensors_of_name_probe(&spi->dev, st_accel_of_match,
|
||||
spi->modalias, sizeof(spi->modalias));
|
||||
|
||||
settings = st_accel_get_settings(spi->modalias);
|
||||
if (!settings) {
|
||||
dev_err(&spi->dev, "device name %s not recognized.\n",
|
||||
spi->modalias);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adata));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
adata = iio_priv(indio_dev);
|
||||
adata->sensor_settings = (struct st_sensor_settings *)settings;
|
||||
|
||||
st_sensors_of_name_probe(&spi->dev, st_accel_of_match,
|
||||
spi->modalias, sizeof(spi->modalias));
|
||||
st_sensors_spi_configure(indio_dev, spi, adata);
|
||||
err = st_sensors_spi_configure(indio_dev, spi);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = st_accel_common_probe(indio_dev);
|
||||
if (err < 0)
|
||||
|
@ -35,6 +35,11 @@ static const unsigned int ad7606_scale_avail[2] = {
|
||||
152588, 305176
|
||||
};
|
||||
|
||||
|
||||
static const unsigned int ad7616_sw_scale_avail[3] = {
|
||||
76293, 152588, 305176
|
||||
};
|
||||
|
||||
static const unsigned int ad7606_oversampling_avail[7] = {
|
||||
1, 2, 4, 8, 16, 32, 64,
|
||||
};
|
||||
@ -55,6 +60,29 @@ static int ad7606_reset(struct ad7606_state *st)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static int ad7606_reg_access(struct iio_dev *indio_dev,
|
||||
unsigned int reg,
|
||||
unsigned int writeval,
|
||||
unsigned int *readval)
|
||||
{
|
||||
struct ad7606_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&st->lock);
|
||||
if (readval) {
|
||||
ret = st->bops->reg_read(st, reg);
|
||||
if (ret < 0)
|
||||
goto err_unlock;
|
||||
*readval = ret;
|
||||
ret = 0;
|
||||
} else {
|
||||
ret = st->bops->reg_write(st, reg, writeval);
|
||||
}
|
||||
err_unlock:
|
||||
mutex_unlock(&st->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad7606_read_samples(struct ad7606_state *st)
|
||||
{
|
||||
unsigned int num = st->chip_info->num_channels;
|
||||
@ -308,29 +336,6 @@ static const struct attribute_group ad7606_attribute_group_range = {
|
||||
.attrs = ad7606_attributes_range,
|
||||
};
|
||||
|
||||
#define AD760X_CHANNEL(num, mask) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = num, \
|
||||
.address = num, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),\
|
||||
.info_mask_shared_by_all = mask, \
|
||||
.scan_index = num, \
|
||||
.scan_type = { \
|
||||
.sign = 's', \
|
||||
.realbits = 16, \
|
||||
.storagebits = 16, \
|
||||
.endianness = IIO_CPU, \
|
||||
}, \
|
||||
}
|
||||
|
||||
#define AD7605_CHANNEL(num) \
|
||||
AD760X_CHANNEL(num, 0)
|
||||
|
||||
#define AD7606_CHANNEL(num) \
|
||||
AD760X_CHANNEL(num, BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO))
|
||||
|
||||
static const struct iio_chan_spec ad7605_channels[] = {
|
||||
IIO_CHAN_SOFT_TIMESTAMP(4),
|
||||
AD7605_CHANNEL(0),
|
||||
@ -405,12 +410,19 @@ static const struct ad7606_chip_info ad7606_chip_info_tbl[] = {
|
||||
.oversampling_avail = ad7606_oversampling_avail,
|
||||
.oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail),
|
||||
},
|
||||
[ID_AD7606B] = {
|
||||
.channels = ad7606_channels,
|
||||
.num_channels = 9,
|
||||
.oversampling_avail = ad7606_oversampling_avail,
|
||||
.oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail),
|
||||
},
|
||||
[ID_AD7616] = {
|
||||
.channels = ad7616_channels,
|
||||
.num_channels = 17,
|
||||
.oversampling_avail = ad7616_oversampling_avail,
|
||||
.oversampling_num = ARRAY_SIZE(ad7616_oversampling_avail),
|
||||
.os_req_reset = true,
|
||||
.init_delay_ms = 15,
|
||||
},
|
||||
};
|
||||
|
||||
@ -519,6 +531,14 @@ static const struct iio_info ad7606_info_os_and_range = {
|
||||
.validate_trigger = &ad7606_validate_trigger,
|
||||
};
|
||||
|
||||
static const struct iio_info ad7606_info_os_range_and_debug = {
|
||||
.read_raw = &ad7606_read_raw,
|
||||
.write_raw = &ad7606_write_raw,
|
||||
.debugfs_reg_access = &ad7606_reg_access,
|
||||
.attrs = &ad7606_attribute_group_os_and_range,
|
||||
.validate_trigger = &ad7606_validate_trigger,
|
||||
};
|
||||
|
||||
static const struct iio_info ad7606_info_os = {
|
||||
.read_raw = &ad7606_read_raw,
|
||||
.write_raw = &ad7606_write_raw,
|
||||
@ -617,35 +637,29 @@ int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,
|
||||
if (ret)
|
||||
dev_warn(st->dev, "failed to RESET: no RESET GPIO specified\n");
|
||||
|
||||
/* AD7616 requires al least 15ms to reconfigure after a reset */
|
||||
if (st->chip_info->init_delay_ms) {
|
||||
if (msleep_interruptible(st->chip_info->init_delay_ms))
|
||||
return -ERESTARTSYS;
|
||||
}
|
||||
|
||||
st->write_scale = ad7606_write_scale_hw;
|
||||
st->write_os = ad7606_write_os_hw;
|
||||
|
||||
if (st->chip_info->sw_mode_config)
|
||||
if (st->bops->sw_mode_config)
|
||||
st->sw_mode_en = device_property_present(st->dev,
|
||||
"adi,sw-mode");
|
||||
|
||||
if (st->sw_mode_en) {
|
||||
/* Scale of 0.076293 is only available in sw mode */
|
||||
st->scale_avail = ad7616_sw_scale_avail;
|
||||
st->num_scales = ARRAY_SIZE(ad7616_sw_scale_avail);
|
||||
|
||||
/* After reset, in software mode, ±10 V is set by default */
|
||||
memset32(st->range, 2, ARRAY_SIZE(st->range));
|
||||
indio_dev->info = &ad7606_info_os_and_range;
|
||||
indio_dev->info = &ad7606_info_os_range_and_debug;
|
||||
|
||||
/*
|
||||
* In software mode, the range gpio has no longer its function.
|
||||
* Instead, the scale can be configured individually for each
|
||||
* channel from the range registers.
|
||||
*/
|
||||
if (st->chip_info->write_scale_sw)
|
||||
st->write_scale = st->chip_info->write_scale_sw;
|
||||
|
||||
/*
|
||||
* In software mode, the oversampling is no longer configured
|
||||
* with GPIO pins. Instead, the oversampling can be configured
|
||||
* in configuratiion register.
|
||||
*/
|
||||
if (st->chip_info->write_os_sw)
|
||||
st->write_os = st->chip_info->write_os_sw;
|
||||
|
||||
ret = st->chip_info->sw_mode_config(indio_dev);
|
||||
ret = st->bops->sw_mode_config(indio_dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
@ -8,6 +8,36 @@
|
||||
#ifndef IIO_ADC_AD7606_H_
|
||||
#define IIO_ADC_AD7606_H_
|
||||
|
||||
#define AD760X_CHANNEL(num, mask_sep, mask_type, mask_all) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = num, \
|
||||
.address = num, \
|
||||
.info_mask_separate = mask_sep, \
|
||||
.info_mask_shared_by_type = mask_type, \
|
||||
.info_mask_shared_by_all = mask_all, \
|
||||
.scan_index = num, \
|
||||
.scan_type = { \
|
||||
.sign = 's', \
|
||||
.realbits = 16, \
|
||||
.storagebits = 16, \
|
||||
.endianness = IIO_CPU, \
|
||||
}, \
|
||||
}
|
||||
|
||||
#define AD7605_CHANNEL(num) \
|
||||
AD760X_CHANNEL(num, BIT(IIO_CHAN_INFO_RAW), \
|
||||
BIT(IIO_CHAN_INFO_SCALE), 0)
|
||||
|
||||
#define AD7606_CHANNEL(num) \
|
||||
AD760X_CHANNEL(num, BIT(IIO_CHAN_INFO_RAW), \
|
||||
BIT(IIO_CHAN_INFO_SCALE), \
|
||||
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO))
|
||||
|
||||
#define AD7616_CHANNEL(num) \
|
||||
AD760X_CHANNEL(num, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),\
|
||||
0, BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO))
|
||||
|
||||
/**
|
||||
* struct ad7606_chip_info - chip specific information
|
||||
* @channels: channel specification
|
||||
@ -16,12 +46,8 @@
|
||||
* oversampling ratios.
|
||||
* @oversampling_num number of elements stored in oversampling_avail array
|
||||
* @os_req_reset some devices require a reset to update oversampling
|
||||
* @write_scale_sw pointer to the function which writes the scale via spi
|
||||
in software mode
|
||||
* @write_os_sw pointer to the function which writes the os via spi
|
||||
in software mode
|
||||
* @sw_mode_config: pointer to a function which configured the device
|
||||
* for software mode
|
||||
* @init_delay_ms required delay in miliseconds for initialization
|
||||
* after a restart
|
||||
*/
|
||||
struct ad7606_chip_info {
|
||||
const struct iio_chan_spec *channels;
|
||||
@ -29,9 +55,7 @@ struct ad7606_chip_info {
|
||||
const unsigned int *oversampling_avail;
|
||||
unsigned int oversampling_num;
|
||||
bool os_req_reset;
|
||||
int (*write_scale_sw)(struct iio_dev *indio_dev, int ch, int val);
|
||||
int (*write_os_sw)(struct iio_dev *indio_dev, int val);
|
||||
int (*sw_mode_config)(struct iio_dev *indio_dev);
|
||||
unsigned long init_delay_ms;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -63,6 +87,7 @@ struct ad7606_chip_info {
|
||||
* @complete completion to indicate end of conversion
|
||||
* @trig The IIO trigger associated with the device.
|
||||
* @data buffer for reading data from the device
|
||||
* @d16 be16 buffer for reading data from the device
|
||||
*/
|
||||
struct ad7606_state {
|
||||
struct device *dev;
|
||||
@ -96,15 +121,32 @@ struct ad7606_state {
|
||||
* 16 * 16-bit samples + 64-bit timestamp
|
||||
*/
|
||||
unsigned short data[20] ____cacheline_aligned;
|
||||
__be16 d16[2];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ad7606_bus_ops - driver bus operations
|
||||
* @read_block function pointer for reading blocks of data
|
||||
* @sw_mode_config: pointer to a function which configured the device
|
||||
* for software mode
|
||||
* @reg_read function pointer for reading spi register
|
||||
* @reg_write function pointer for writing spi register
|
||||
* @write_mask function pointer for write spi register with mask
|
||||
* @rd_wr_cmd pointer to the function which calculates the spi address
|
||||
*/
|
||||
struct ad7606_bus_ops {
|
||||
/* more methods added in future? */
|
||||
int (*read_block)(struct device *dev, int num, void *data);
|
||||
int (*sw_mode_config)(struct iio_dev *indio_dev);
|
||||
int (*reg_read)(struct ad7606_state *st, unsigned int addr);
|
||||
int (*reg_write)(struct ad7606_state *st,
|
||||
unsigned int addr,
|
||||
unsigned int val);
|
||||
int (*write_mask)(struct ad7606_state *st,
|
||||
unsigned int addr,
|
||||
unsigned long mask,
|
||||
unsigned int val);
|
||||
u16 (*rd_wr_cmd)(int addr, char isWriteOp);
|
||||
};
|
||||
|
||||
int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,
|
||||
@ -116,6 +158,7 @@ enum ad7606_supported_device_ids {
|
||||
ID_AD7606_8,
|
||||
ID_AD7606_6,
|
||||
ID_AD7606_4,
|
||||
ID_AD7606B,
|
||||
ID_AD7616,
|
||||
};
|
||||
|
||||
|
@ -53,10 +53,8 @@ static int ad7606_par_probe(struct platform_device *pdev)
|
||||
int irq;
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(&pdev->dev, "no irq: %d\n", irq);
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
addr = devm_ioremap_resource(&pdev->dev, res);
|
||||
|
@ -15,6 +15,91 @@
|
||||
|
||||
#define MAX_SPI_FREQ_HZ 23500000 /* VDRIVE above 4.75 V */
|
||||
|
||||
#define AD7616_CONFIGURATION_REGISTER 0x02
|
||||
#define AD7616_OS_MASK GENMASK(4, 2)
|
||||
#define AD7616_BURST_MODE BIT(6)
|
||||
#define AD7616_SEQEN_MODE BIT(5)
|
||||
#define AD7616_RANGE_CH_A_ADDR_OFF 0x04
|
||||
#define AD7616_RANGE_CH_B_ADDR_OFF 0x06
|
||||
/*
|
||||
* Range of channels from a group are stored in 2 registers.
|
||||
* 0, 1, 2, 3 in a register followed by 4, 5, 6, 7 in second register.
|
||||
* For channels from second group(8-15) the order is the same, only with
|
||||
* an offset of 2 for register address.
|
||||
*/
|
||||
#define AD7616_RANGE_CH_ADDR(ch) ((ch) >> 2)
|
||||
/* The range of the channel is stored in 2 bits */
|
||||
#define AD7616_RANGE_CH_MSK(ch) (0b11 << (((ch) & 0b11) * 2))
|
||||
#define AD7616_RANGE_CH_MODE(ch, mode) ((mode) << ((((ch) & 0b11)) * 2))
|
||||
|
||||
#define AD7606_CONFIGURATION_REGISTER 0x02
|
||||
#define AD7606_SINGLE_DOUT 0x00
|
||||
|
||||
/*
|
||||
* Range for AD7606B channels are stored in registers starting with address 0x3.
|
||||
* Each register stores range for 2 channels(4 bits per channel).
|
||||
*/
|
||||
#define AD7606_RANGE_CH_MSK(ch) (GENMASK(3, 0) << (4 * ((ch) & 0x1)))
|
||||
#define AD7606_RANGE_CH_MODE(ch, mode) \
|
||||
((GENMASK(3, 0) & mode) << (4 * ((ch) & 0x1)))
|
||||
#define AD7606_RANGE_CH_ADDR(ch) (0x03 + ((ch) >> 1))
|
||||
#define AD7606_OS_MODE 0x08
|
||||
|
||||
static const struct iio_chan_spec ad7616_sw_channels[] = {
|
||||
IIO_CHAN_SOFT_TIMESTAMP(16),
|
||||
AD7616_CHANNEL(0),
|
||||
AD7616_CHANNEL(1),
|
||||
AD7616_CHANNEL(2),
|
||||
AD7616_CHANNEL(3),
|
||||
AD7616_CHANNEL(4),
|
||||
AD7616_CHANNEL(5),
|
||||
AD7616_CHANNEL(6),
|
||||
AD7616_CHANNEL(7),
|
||||
AD7616_CHANNEL(8),
|
||||
AD7616_CHANNEL(9),
|
||||
AD7616_CHANNEL(10),
|
||||
AD7616_CHANNEL(11),
|
||||
AD7616_CHANNEL(12),
|
||||
AD7616_CHANNEL(13),
|
||||
AD7616_CHANNEL(14),
|
||||
AD7616_CHANNEL(15),
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec ad7606b_sw_channels[] = {
|
||||
IIO_CHAN_SOFT_TIMESTAMP(8),
|
||||
AD7616_CHANNEL(0),
|
||||
AD7616_CHANNEL(1),
|
||||
AD7616_CHANNEL(2),
|
||||
AD7616_CHANNEL(3),
|
||||
AD7616_CHANNEL(4),
|
||||
AD7616_CHANNEL(5),
|
||||
AD7616_CHANNEL(6),
|
||||
AD7616_CHANNEL(7),
|
||||
};
|
||||
|
||||
static const unsigned int ad7606B_oversampling_avail[9] = {
|
||||
1, 2, 4, 8, 16, 32, 64, 128, 256
|
||||
};
|
||||
|
||||
static u16 ad7616_spi_rd_wr_cmd(int addr, char isWriteOp)
|
||||
{
|
||||
/*
|
||||
* The address of register consist of one w/r bit
|
||||
* 6 bits of address followed by one reserved bit.
|
||||
*/
|
||||
return ((addr & 0x7F) << 1) | ((isWriteOp & 0x1) << 7);
|
||||
}
|
||||
|
||||
static u16 ad7606B_spi_rd_wr_cmd(int addr, char is_write_op)
|
||||
{
|
||||
/*
|
||||
* The address of register consists of one bit which
|
||||
* specifies a read command placed in bit 6, followed by
|
||||
* 6 bits of address.
|
||||
*/
|
||||
return (addr & 0x3F) | (((~is_write_op) & 0x1) << 6);
|
||||
}
|
||||
|
||||
static int ad7606_spi_read_block(struct device *dev,
|
||||
int count, void *buf)
|
||||
{
|
||||
@ -35,17 +120,210 @@ static int ad7606_spi_read_block(struct device *dev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ad7606_spi_reg_read(struct ad7606_state *st, unsigned int addr)
|
||||
{
|
||||
struct spi_device *spi = to_spi_device(st->dev);
|
||||
struct spi_transfer t[] = {
|
||||
{
|
||||
.tx_buf = &st->d16[0],
|
||||
.len = 2,
|
||||
.cs_change = 0,
|
||||
}, {
|
||||
.rx_buf = &st->d16[1],
|
||||
.len = 2,
|
||||
},
|
||||
};
|
||||
int ret;
|
||||
|
||||
st->d16[0] = cpu_to_be16(st->bops->rd_wr_cmd(addr, 0) << 8);
|
||||
|
||||
ret = spi_sync_transfer(spi, t, ARRAY_SIZE(t));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return be16_to_cpu(st->d16[1]);
|
||||
}
|
||||
|
||||
static int ad7606_spi_reg_write(struct ad7606_state *st,
|
||||
unsigned int addr,
|
||||
unsigned int val)
|
||||
{
|
||||
struct spi_device *spi = to_spi_device(st->dev);
|
||||
|
||||
st->d16[0] = cpu_to_be16((st->bops->rd_wr_cmd(addr, 1) << 8) |
|
||||
(val & 0x1FF));
|
||||
|
||||
return spi_write(spi, &st->d16[0], sizeof(st->d16[0]));
|
||||
}
|
||||
|
||||
static int ad7606_spi_write_mask(struct ad7606_state *st,
|
||||
unsigned int addr,
|
||||
unsigned long mask,
|
||||
unsigned int val)
|
||||
{
|
||||
int readval;
|
||||
|
||||
readval = st->bops->reg_read(st, addr);
|
||||
if (readval < 0)
|
||||
return readval;
|
||||
|
||||
readval &= ~mask;
|
||||
readval |= val;
|
||||
|
||||
return st->bops->reg_write(st, addr, readval);
|
||||
}
|
||||
|
||||
static int ad7616_write_scale_sw(struct iio_dev *indio_dev, int ch, int val)
|
||||
{
|
||||
struct ad7606_state *st = iio_priv(indio_dev);
|
||||
unsigned int ch_addr, mode, ch_index;
|
||||
|
||||
|
||||
/*
|
||||
* Ad7616 has 16 channels divided in group A and group B.
|
||||
* The range of channels from A are stored in registers with address 4
|
||||
* while channels from B are stored in register with address 6.
|
||||
* The last bit from channels determines if it is from group A or B
|
||||
* because the order of channels in iio is 0A, 0B, 1A, 1B...
|
||||
*/
|
||||
ch_index = ch >> 1;
|
||||
|
||||
ch_addr = AD7616_RANGE_CH_ADDR(ch_index);
|
||||
|
||||
if ((ch & 0x1) == 0) /* channel A */
|
||||
ch_addr += AD7616_RANGE_CH_A_ADDR_OFF;
|
||||
else /* channel B */
|
||||
ch_addr += AD7616_RANGE_CH_B_ADDR_OFF;
|
||||
|
||||
/* 0b01 for 2.5v, 0b10 for 5v and 0b11 for 10v */
|
||||
mode = AD7616_RANGE_CH_MODE(ch_index, ((val + 1) & 0b11));
|
||||
return st->bops->write_mask(st, ch_addr, AD7616_RANGE_CH_MSK(ch_index),
|
||||
mode);
|
||||
}
|
||||
|
||||
static int ad7616_write_os_sw(struct iio_dev *indio_dev, int val)
|
||||
{
|
||||
struct ad7606_state *st = iio_priv(indio_dev);
|
||||
|
||||
return st->bops->write_mask(st, AD7616_CONFIGURATION_REGISTER,
|
||||
AD7616_OS_MASK, val << 2);
|
||||
}
|
||||
|
||||
static int ad7606_write_scale_sw(struct iio_dev *indio_dev, int ch, int val)
|
||||
{
|
||||
struct ad7606_state *st = iio_priv(indio_dev);
|
||||
|
||||
return ad7606_spi_write_mask(st,
|
||||
AD7606_RANGE_CH_ADDR(ch),
|
||||
AD7606_RANGE_CH_MSK(ch),
|
||||
AD7606_RANGE_CH_MODE(ch, val));
|
||||
}
|
||||
|
||||
static int ad7606_write_os_sw(struct iio_dev *indio_dev, int val)
|
||||
{
|
||||
struct ad7606_state *st = iio_priv(indio_dev);
|
||||
|
||||
return ad7606_spi_reg_write(st, AD7606_OS_MODE, val);
|
||||
}
|
||||
|
||||
static int ad7616_sw_mode_config(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct ad7606_state *st = iio_priv(indio_dev);
|
||||
|
||||
/*
|
||||
* Scale can be configured individually for each channel
|
||||
* in software mode.
|
||||
*/
|
||||
indio_dev->channels = ad7616_sw_channels;
|
||||
|
||||
st->write_scale = ad7616_write_scale_sw;
|
||||
st->write_os = &ad7616_write_os_sw;
|
||||
|
||||
/* Activate Burst mode and SEQEN MODE */
|
||||
return st->bops->write_mask(st,
|
||||
AD7616_CONFIGURATION_REGISTER,
|
||||
AD7616_BURST_MODE | AD7616_SEQEN_MODE,
|
||||
AD7616_BURST_MODE | AD7616_SEQEN_MODE);
|
||||
}
|
||||
|
||||
static int ad7606B_sw_mode_config(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct ad7606_state *st = iio_priv(indio_dev);
|
||||
unsigned long os[3] = {1};
|
||||
|
||||
/*
|
||||
* Software mode is enabled when all three oversampling
|
||||
* pins are set to high. If oversampling gpios are defined
|
||||
* in the device tree, then they need to be set to high,
|
||||
* otherwise, they must be hardwired to VDD
|
||||
*/
|
||||
if (st->gpio_os) {
|
||||
gpiod_set_array_value(ARRAY_SIZE(os),
|
||||
st->gpio_os->desc, st->gpio_os->info, os);
|
||||
}
|
||||
/* OS of 128 and 256 are available only in software mode */
|
||||
st->oversampling_avail = ad7606B_oversampling_avail;
|
||||
st->num_os_ratios = ARRAY_SIZE(ad7606B_oversampling_avail);
|
||||
|
||||
st->write_scale = ad7606_write_scale_sw;
|
||||
st->write_os = &ad7606_write_os_sw;
|
||||
|
||||
/* Configure device spi to output on a single channel */
|
||||
st->bops->reg_write(st,
|
||||
AD7606_CONFIGURATION_REGISTER,
|
||||
AD7606_SINGLE_DOUT);
|
||||
|
||||
/*
|
||||
* Scale can be configured individually for each channel
|
||||
* in software mode.
|
||||
*/
|
||||
indio_dev->channels = ad7606b_sw_channels;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct ad7606_bus_ops ad7606_spi_bops = {
|
||||
.read_block = ad7606_spi_read_block,
|
||||
};
|
||||
|
||||
static const struct ad7606_bus_ops ad7616_spi_bops = {
|
||||
.read_block = ad7606_spi_read_block,
|
||||
.reg_read = ad7606_spi_reg_read,
|
||||
.reg_write = ad7606_spi_reg_write,
|
||||
.write_mask = ad7606_spi_write_mask,
|
||||
.rd_wr_cmd = ad7616_spi_rd_wr_cmd,
|
||||
.sw_mode_config = ad7616_sw_mode_config,
|
||||
};
|
||||
|
||||
static const struct ad7606_bus_ops ad7606B_spi_bops = {
|
||||
.read_block = ad7606_spi_read_block,
|
||||
.reg_read = ad7606_spi_reg_read,
|
||||
.reg_write = ad7606_spi_reg_write,
|
||||
.write_mask = ad7606_spi_write_mask,
|
||||
.rd_wr_cmd = ad7606B_spi_rd_wr_cmd,
|
||||
.sw_mode_config = ad7606B_sw_mode_config,
|
||||
};
|
||||
|
||||
static int ad7606_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
const struct spi_device_id *id = spi_get_device_id(spi);
|
||||
const struct ad7606_bus_ops *bops;
|
||||
|
||||
switch (id->driver_data) {
|
||||
case ID_AD7616:
|
||||
bops = &ad7616_spi_bops;
|
||||
break;
|
||||
case ID_AD7606B:
|
||||
bops = &ad7606B_spi_bops;
|
||||
break;
|
||||
default:
|
||||
bops = &ad7606_spi_bops;
|
||||
break;
|
||||
}
|
||||
|
||||
return ad7606_probe(&spi->dev, spi->irq, NULL,
|
||||
id->name, id->driver_data,
|
||||
&ad7606_spi_bops);
|
||||
bops);
|
||||
}
|
||||
|
||||
static const struct spi_device_id ad7606_id_table[] = {
|
||||
@ -53,6 +331,7 @@ static const struct spi_device_id ad7606_id_table[] = {
|
||||
{ "ad7606-4", ID_AD7606_4 },
|
||||
{ "ad7606-6", ID_AD7606_6 },
|
||||
{ "ad7606-8", ID_AD7606_8 },
|
||||
{ "ad7606b", ID_AD7606B },
|
||||
{ "ad7616", ID_AD7616 },
|
||||
{}
|
||||
};
|
||||
@ -63,6 +342,7 @@ static const struct of_device_id ad7606_of_match[] = {
|
||||
{ .compatible = "adi,ad7606-4" },
|
||||
{ .compatible = "adi,ad7606-6" },
|
||||
{ .compatible = "adi,ad7606-8" },
|
||||
{ .compatible = "adi,ad7606b" },
|
||||
{ .compatible = "adi,ad7616" },
|
||||
{ },
|
||||
};
|
||||
|
@ -1179,10 +1179,8 @@ static int at91_adc_probe(struct platform_device *pdev)
|
||||
idev->info = &at91_adc_info;
|
||||
|
||||
st->irq = platform_get_irq(pdev, 0);
|
||||
if (st->irq < 0) {
|
||||
dev_err(&pdev->dev, "No IRQ ID is designated\n");
|
||||
if (st->irq < 0)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
|
||||
|
@ -225,10 +225,8 @@ static int axp288_adc_probe(struct platform_device *pdev)
|
||||
|
||||
info = iio_priv(indio_dev);
|
||||
info->irq = platform_get_irq(pdev, 0);
|
||||
if (info->irq < 0) {
|
||||
dev_err(&pdev->dev, "no irq resource?\n");
|
||||
if (info->irq < 0)
|
||||
return info->irq;
|
||||
}
|
||||
platform_set_drvdata(pdev, indio_dev);
|
||||
info->regmap = axp20x->regmap;
|
||||
/*
|
||||
|
@ -540,11 +540,8 @@ static int iproc_adc_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
adc_priv->irqno = platform_get_irq(pdev, 0);
|
||||
if (adc_priv->irqno <= 0) {
|
||||
dev_err(&pdev->dev, "platform_get_irq failed\n");
|
||||
ret = -ENODEV;
|
||||
return ret;
|
||||
}
|
||||
if (adc_priv->irqno <= 0)
|
||||
return -ENODEV;
|
||||
|
||||
ret = regmap_update_bits(adc_priv->regmap, IPROC_REGCTL2,
|
||||
IPROC_ADC_AUXIN_SCAN_ENA, 0);
|
||||
|
@ -337,10 +337,8 @@ static int da9150_gpadc_probe(struct platform_device *pdev)
|
||||
init_completion(&gpadc->complete);
|
||||
|
||||
irq = platform_get_irq_byname(pdev, "GPADC");
|
||||
if (irq < 0) {
|
||||
dev_err(dev, "Failed to get IRQ: %d\n", irq);
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
}
|
||||
|
||||
ret = devm_request_threaded_irq(dev, irq, NULL, da9150_gpadc_irq,
|
||||
IRQF_ONESHOT, "GPADC", gpadc);
|
||||
|
@ -357,11 +357,8 @@ static int envelope_detector_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
env->comp_irq = platform_get_irq_byname(pdev, "comp");
|
||||
if (env->comp_irq < 0) {
|
||||
if (env->comp_irq != -EPROBE_DEFER)
|
||||
dev_err(dev, "failed to get compare interrupt\n");
|
||||
if (env->comp_irq < 0)
|
||||
return env->comp_irq;
|
||||
}
|
||||
|
||||
ret = devm_request_irq(dev, env->comp_irq, envelope_detector_comp_isr,
|
||||
0, "envelope-detector", env);
|
||||
|
@ -805,10 +805,8 @@ static int exynos_adc_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(&pdev->dev, "no irq resource?\n");
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
}
|
||||
info->irq = irq;
|
||||
|
||||
irq = platform_get_irq(pdev, 1);
|
||||
|
@ -340,7 +340,6 @@ static int mx25_gcq_probe(struct platform_device *pdev)
|
||||
|
||||
priv->irq = platform_get_irq(pdev, 0);
|
||||
if (priv->irq <= 0) {
|
||||
dev_err(dev, "Failed to get IRQ\n");
|
||||
ret = priv->irq;
|
||||
if (!ret)
|
||||
ret = -ENXIO;
|
||||
|
@ -456,6 +456,11 @@ err_read:
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void hi8435_triggered_event_cleanup(void *data)
|
||||
{
|
||||
iio_triggered_event_cleanup(data);
|
||||
}
|
||||
|
||||
static int hi8435_probe(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *idev;
|
||||
@ -477,7 +482,7 @@ static int hi8435_probe(struct spi_device *spi)
|
||||
hi8435_writeb(priv, HI8435_CTRL_REG, 0);
|
||||
} else {
|
||||
udelay(5);
|
||||
gpiod_set_value(reset_gpio, 1);
|
||||
gpiod_set_value_cansleep(reset_gpio, 1);
|
||||
}
|
||||
|
||||
spi_set_drvdata(spi, idev);
|
||||
@ -513,27 +518,13 @@ static int hi8435_probe(struct spi_device *spi)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = iio_device_register(idev);
|
||||
if (ret < 0) {
|
||||
dev_err(&spi->dev, "unable to register device\n");
|
||||
goto unregister_triggered_event;
|
||||
}
|
||||
ret = devm_add_action_or_reset(&spi->dev,
|
||||
hi8435_triggered_event_cleanup,
|
||||
idev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
|
||||
unregister_triggered_event:
|
||||
iio_triggered_event_cleanup(idev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hi8435_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *idev = spi_get_drvdata(spi);
|
||||
|
||||
iio_device_unregister(idev);
|
||||
iio_triggered_event_cleanup(idev);
|
||||
|
||||
return 0;
|
||||
return devm_iio_device_register(&spi->dev, idev);
|
||||
}
|
||||
|
||||
static const struct of_device_id hi8435_dt_ids[] = {
|
||||
@ -554,7 +545,6 @@ static struct spi_driver hi8435_driver = {
|
||||
.of_match_table = of_match_ptr(hi8435_dt_ids),
|
||||
},
|
||||
.probe = hi8435_probe,
|
||||
.remove = hi8435_remove,
|
||||
.id_table = hi8435_id,
|
||||
};
|
||||
module_spi_driver(hi8435_driver);
|
||||
|
@ -492,10 +492,8 @@ static int imx7d_adc_probe(struct platform_device *pdev)
|
||||
return PTR_ERR(info->regs);
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(dev, "No irq resource?\n");
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
}
|
||||
|
||||
info->clk = devm_clk_get(dev, "adc");
|
||||
if (IS_ERR(info->clk)) {
|
||||
|
@ -172,10 +172,8 @@ static int lpc32xx_adc_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq <= 0) {
|
||||
dev_err(&pdev->dev, "failed getting interrupt resource\n");
|
||||
if (irq <= 0)
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
retval = devm_request_irq(&pdev->dev, irq, lpc32xx_adc_isr, 0,
|
||||
LPC32XXAD_NAME, st);
|
||||
|
@ -427,8 +427,9 @@ static int max1027_probe(struct spi_device *spi)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
|
||||
&max1027_trigger_handler, NULL);
|
||||
ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev,
|
||||
&iio_pollfunc_store_time,
|
||||
&max1027_trigger_handler, NULL);
|
||||
if (ret < 0) {
|
||||
dev_err(&indio_dev->dev, "Failed to setup buffer\n");
|
||||
return ret;
|
||||
@ -439,7 +440,7 @@ static int max1027_probe(struct spi_device *spi)
|
||||
if (st->trig == NULL) {
|
||||
ret = -ENOMEM;
|
||||
dev_err(&indio_dev->dev, "Failed to allocate iio trigger\n");
|
||||
goto fail_trigger_alloc;
|
||||
return ret;
|
||||
}
|
||||
|
||||
st->trig->ops = &max1027_trigger_ops;
|
||||
@ -454,7 +455,7 @@ static int max1027_probe(struct spi_device *spi)
|
||||
spi->dev.driver->name, st->trig);
|
||||
if (ret < 0) {
|
||||
dev_err(&indio_dev->dev, "Failed to allocate IRQ.\n");
|
||||
goto fail_dev_register;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Disable averaging */
|
||||
@ -462,34 +463,10 @@ static int max1027_probe(struct spi_device *spi)
|
||||
ret = spi_write(st->spi, &st->reg, 1);
|
||||
if (ret < 0) {
|
||||
dev_err(&indio_dev->dev, "Failed to configure averaging register\n");
|
||||
goto fail_dev_register;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret < 0) {
|
||||
dev_err(&indio_dev->dev, "Failed to register iio device\n");
|
||||
goto fail_dev_register;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail_dev_register:
|
||||
fail_trigger_alloc:
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int max1027_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
|
||||
pr_debug("%s: remove(spi = 0x%p)\n", __func__, spi);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
|
||||
return 0;
|
||||
return devm_iio_device_register(&spi->dev, indio_dev);
|
||||
}
|
||||
|
||||
static struct spi_driver max1027_driver = {
|
||||
@ -498,7 +475,6 @@ static struct spi_driver max1027_driver = {
|
||||
.of_match_table = of_match_ptr(max1027_adc_dt_ids),
|
||||
},
|
||||
.probe = max1027_probe,
|
||||
.remove = max1027_remove,
|
||||
.id_table = max1027_id,
|
||||
};
|
||||
module_spi_driver(max1027_driver);
|
||||
|
@ -225,7 +225,6 @@ static int npcm_adc_probe(struct platform_device *pdev)
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq <= 0) {
|
||||
dev_err(dev, "failed getting interrupt resource\n");
|
||||
ret = -EINVAL;
|
||||
goto err_disable_clk;
|
||||
}
|
||||
|
@ -244,10 +244,8 @@ static int rockchip_saradc_probe(struct platform_device *pdev)
|
||||
init_completion(&info->completion);
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(&pdev->dev, "no irq resource?\n");
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
}
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, irq, rockchip_saradc_isr,
|
||||
0, dev_name(&pdev->dev), info);
|
||||
|
@ -3,7 +3,6 @@
|
||||
|
||||
#include <linux/hwspinlock.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/nvmem-consumer.h>
|
||||
#include <linux/of.h>
|
||||
@ -46,14 +45,18 @@
|
||||
/* Bits definitions for SC27XX_ADC_INT_CLR registers */
|
||||
#define SC27XX_ADC_IRQ_CLR BIT(0)
|
||||
|
||||
/* Bits definitions for SC27XX_ADC_INT_RAW registers */
|
||||
#define SC27XX_ADC_IRQ_RAW BIT(0)
|
||||
|
||||
/* Mask definition for SC27XX_ADC_DATA register */
|
||||
#define SC27XX_ADC_DATA_MASK GENMASK(11, 0)
|
||||
|
||||
/* Timeout (ms) for the trylock of hardware spinlocks */
|
||||
#define SC27XX_ADC_HWLOCK_TIMEOUT 5000
|
||||
|
||||
/* Timeout (ms) for ADC data conversion according to ADC datasheet */
|
||||
#define SC27XX_ADC_RDY_TIMEOUT 100
|
||||
/* Timeout (us) for ADC data conversion according to ADC datasheet */
|
||||
#define SC27XX_ADC_RDY_TIMEOUT 1000000
|
||||
#define SC27XX_ADC_POLL_RAW_STATUS 500
|
||||
|
||||
/* Maximum ADC channel number */
|
||||
#define SC27XX_ADC_CHANNEL_MAX 32
|
||||
@ -72,10 +75,8 @@ struct sc27xx_adc_data {
|
||||
* subsystems which will access the unique ADC controller.
|
||||
*/
|
||||
struct hwspinlock *hwlock;
|
||||
struct completion completion;
|
||||
int channel_scale[SC27XX_ADC_CHANNEL_MAX];
|
||||
u32 base;
|
||||
int value;
|
||||
int irq;
|
||||
};
|
||||
|
||||
@ -188,9 +189,7 @@ static int sc27xx_adc_read(struct sc27xx_adc_data *data, int channel,
|
||||
int scale, int *val)
|
||||
{
|
||||
int ret;
|
||||
u32 tmp;
|
||||
|
||||
reinit_completion(&data->completion);
|
||||
u32 tmp, value, status;
|
||||
|
||||
ret = hwspin_lock_timeout_raw(data->hwlock, SC27XX_ADC_HWLOCK_TIMEOUT);
|
||||
if (ret) {
|
||||
@ -203,6 +202,11 @@ static int sc27xx_adc_read(struct sc27xx_adc_data *data, int channel,
|
||||
if (ret)
|
||||
goto unlock_adc;
|
||||
|
||||
ret = regmap_update_bits(data->regmap, data->base + SC27XX_ADC_INT_CLR,
|
||||
SC27XX_ADC_IRQ_CLR, SC27XX_ADC_IRQ_CLR);
|
||||
if (ret)
|
||||
goto disable_adc;
|
||||
|
||||
/* Configure the channel id and scale */
|
||||
tmp = (scale << SC27XX_ADC_SCALE_SHIFT) & SC27XX_ADC_SCALE_MASK;
|
||||
tmp |= channel & SC27XX_ADC_CHN_ID_MASK;
|
||||
@ -226,15 +230,22 @@ static int sc27xx_adc_read(struct sc27xx_adc_data *data, int channel,
|
||||
if (ret)
|
||||
goto disable_adc;
|
||||
|
||||
ret = wait_for_completion_timeout(&data->completion,
|
||||
msecs_to_jiffies(SC27XX_ADC_RDY_TIMEOUT));
|
||||
if (!ret) {
|
||||
dev_err(data->dev, "read ADC data timeout\n");
|
||||
ret = -ETIMEDOUT;
|
||||
} else {
|
||||
ret = 0;
|
||||
ret = regmap_read_poll_timeout(data->regmap,
|
||||
data->base + SC27XX_ADC_INT_RAW,
|
||||
status, (status & SC27XX_ADC_IRQ_RAW),
|
||||
SC27XX_ADC_POLL_RAW_STATUS,
|
||||
SC27XX_ADC_RDY_TIMEOUT);
|
||||
if (ret) {
|
||||
dev_err(data->dev, "read adc timeout, status = 0x%x\n", status);
|
||||
goto disable_adc;
|
||||
}
|
||||
|
||||
ret = regmap_read(data->regmap, data->base + SC27XX_ADC_DATA, &value);
|
||||
if (ret)
|
||||
goto disable_adc;
|
||||
|
||||
value &= SC27XX_ADC_DATA_MASK;
|
||||
|
||||
disable_adc:
|
||||
regmap_update_bits(data->regmap, data->base + SC27XX_ADC_CTL,
|
||||
SC27XX_ADC_EN, 0);
|
||||
@ -242,32 +253,11 @@ unlock_adc:
|
||||
hwspin_unlock_raw(data->hwlock);
|
||||
|
||||
if (!ret)
|
||||
*val = data->value;
|
||||
*val = value;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static irqreturn_t sc27xx_adc_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct sc27xx_adc_data *data = dev_id;
|
||||
int ret;
|
||||
|
||||
ret = regmap_update_bits(data->regmap, data->base + SC27XX_ADC_INT_CLR,
|
||||
SC27XX_ADC_IRQ_CLR, SC27XX_ADC_IRQ_CLR);
|
||||
if (ret)
|
||||
return IRQ_RETVAL(ret);
|
||||
|
||||
ret = regmap_read(data->regmap, data->base + SC27XX_ADC_DATA,
|
||||
&data->value);
|
||||
if (ret)
|
||||
return IRQ_RETVAL(ret);
|
||||
|
||||
data->value &= SC27XX_ADC_DATA_MASK;
|
||||
complete(&data->completion);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void sc27xx_adc_volt_ratio(struct sc27xx_adc_data *data,
|
||||
int channel, int scale,
|
||||
u32 *div_numerator, u32 *div_denominator)
|
||||
@ -454,11 +444,6 @@ static int sc27xx_adc_enable(struct sc27xx_adc_data *data)
|
||||
if (ret)
|
||||
goto disable_adc;
|
||||
|
||||
ret = regmap_update_bits(data->regmap, data->base + SC27XX_ADC_INT_EN,
|
||||
SC27XX_ADC_IRQ_EN, SC27XX_ADC_IRQ_EN);
|
||||
if (ret)
|
||||
goto disable_clk;
|
||||
|
||||
/* ADC channel scales' calibration from nvmem device */
|
||||
ret = sc27xx_adc_scale_calibration(data, true);
|
||||
if (ret)
|
||||
@ -484,9 +469,6 @@ static void sc27xx_adc_disable(void *_data)
|
||||
{
|
||||
struct sc27xx_adc_data *data = _data;
|
||||
|
||||
regmap_update_bits(data->regmap, data->base + SC27XX_ADC_INT_EN,
|
||||
SC27XX_ADC_IRQ_EN, 0);
|
||||
|
||||
/* Disable ADC work clock and controller clock */
|
||||
regmap_update_bits(data->regmap, SC27XX_ARM_CLK_EN,
|
||||
SC27XX_CLK_ADC_EN | SC27XX_CLK_ADC_CLK_EN, 0);
|
||||
@ -504,88 +486,76 @@ static void sc27xx_adc_free_hwlock(void *_data)
|
||||
|
||||
static int sc27xx_adc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct sc27xx_adc_data *sc27xx_data;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*sc27xx_data));
|
||||
indio_dev = devm_iio_device_alloc(dev, sizeof(*sc27xx_data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
sc27xx_data = iio_priv(indio_dev);
|
||||
|
||||
sc27xx_data->regmap = dev_get_regmap(pdev->dev.parent, NULL);
|
||||
sc27xx_data->regmap = dev_get_regmap(dev->parent, NULL);
|
||||
if (!sc27xx_data->regmap) {
|
||||
dev_err(&pdev->dev, "failed to get ADC regmap\n");
|
||||
dev_err(dev, "failed to get ADC regmap\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(np, "reg", &sc27xx_data->base);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to get ADC base address\n");
|
||||
dev_err(dev, "failed to get ADC base address\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
sc27xx_data->irq = platform_get_irq(pdev, 0);
|
||||
if (sc27xx_data->irq < 0) {
|
||||
dev_err(&pdev->dev, "failed to get ADC irq number\n");
|
||||
if (sc27xx_data->irq < 0)
|
||||
return sc27xx_data->irq;
|
||||
}
|
||||
|
||||
ret = of_hwspin_lock_get_id(np, 0);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed to get hwspinlock id\n");
|
||||
dev_err(dev, "failed to get hwspinlock id\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
sc27xx_data->hwlock = hwspin_lock_request_specific(ret);
|
||||
if (!sc27xx_data->hwlock) {
|
||||
dev_err(&pdev->dev, "failed to request hwspinlock\n");
|
||||
dev_err(dev, "failed to request hwspinlock\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
ret = devm_add_action(&pdev->dev, sc27xx_adc_free_hwlock,
|
||||
ret = devm_add_action_or_reset(dev, sc27xx_adc_free_hwlock,
|
||||
sc27xx_data->hwlock);
|
||||
if (ret) {
|
||||
sc27xx_adc_free_hwlock(sc27xx_data->hwlock);
|
||||
dev_err(&pdev->dev, "failed to add hwspinlock action\n");
|
||||
dev_err(dev, "failed to add hwspinlock action\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
init_completion(&sc27xx_data->completion);
|
||||
sc27xx_data->dev = &pdev->dev;
|
||||
sc27xx_data->dev = dev;
|
||||
|
||||
ret = sc27xx_adc_enable(sc27xx_data);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to enable ADC module\n");
|
||||
dev_err(dev, "failed to enable ADC module\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = devm_add_action(&pdev->dev, sc27xx_adc_disable, sc27xx_data);
|
||||
ret = devm_add_action_or_reset(dev, sc27xx_adc_disable, sc27xx_data);
|
||||
if (ret) {
|
||||
sc27xx_adc_disable(sc27xx_data);
|
||||
dev_err(&pdev->dev, "failed to add ADC disable action\n");
|
||||
dev_err(dev, "failed to add ADC disable action\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = devm_request_threaded_irq(&pdev->dev, sc27xx_data->irq, NULL,
|
||||
sc27xx_adc_isr, IRQF_ONESHOT,
|
||||
pdev->name, sc27xx_data);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to request ADC irq\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
indio_dev->dev.parent = &pdev->dev;
|
||||
indio_dev->name = dev_name(&pdev->dev);
|
||||
indio_dev->dev.parent = dev;
|
||||
indio_dev->name = dev_name(dev);
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &sc27xx_info;
|
||||
indio_dev->channels = sc27xx_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(sc27xx_channels);
|
||||
ret = devm_iio_device_register(&pdev->dev, indio_dev);
|
||||
ret = devm_iio_device_register(dev, indio_dev);
|
||||
if (ret)
|
||||
dev_err(&pdev->dev, "could not register iio (ADC)");
|
||||
dev_err(dev, "could not register iio (ADC)");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -301,7 +301,6 @@ static int spear_adc_probe(struct platform_device *pdev)
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq <= 0) {
|
||||
dev_err(dev, "failed getting interrupt resource\n");
|
||||
ret = -EINVAL;
|
||||
goto errout2;
|
||||
}
|
||||
|
@ -14,9 +14,11 @@
|
||||
#include <linux/irqchip/chained_irq.h>
|
||||
#include <linux/irqdesc.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
@ -51,6 +53,17 @@
|
||||
|
||||
#define STM32_ADC_CORE_SLEEP_DELAY_MS 2000
|
||||
|
||||
/* SYSCFG registers */
|
||||
#define STM32MP1_SYSCFG_PMCSETR 0x04
|
||||
#define STM32MP1_SYSCFG_PMCCLRR 0x44
|
||||
|
||||
/* SYSCFG bit fields */
|
||||
#define STM32MP1_SYSCFG_ANASWVDD_MASK BIT(9)
|
||||
|
||||
/* SYSCFG capability flags */
|
||||
#define HAS_VBOOSTER BIT(0)
|
||||
#define HAS_ANASWVDD BIT(1)
|
||||
|
||||
/**
|
||||
* stm32_adc_common_regs - stm32 common registers, compatible dependent data
|
||||
* @csr: common status register offset
|
||||
@ -74,11 +87,13 @@ struct stm32_adc_priv;
|
||||
* @regs: common registers for all instances
|
||||
* @clk_sel: clock selection routine
|
||||
* @max_clk_rate_hz: maximum analog clock rate (Hz, from datasheet)
|
||||
* @has_syscfg: SYSCFG capability flags
|
||||
*/
|
||||
struct stm32_adc_priv_cfg {
|
||||
const struct stm32_adc_common_regs *regs;
|
||||
int (*clk_sel)(struct platform_device *, struct stm32_adc_priv *);
|
||||
u32 max_clk_rate_hz;
|
||||
unsigned int has_syscfg;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -87,22 +102,32 @@ struct stm32_adc_priv_cfg {
|
||||
* @domain: irq domain reference
|
||||
* @aclk: clock reference for the analog circuitry
|
||||
* @bclk: bus clock common for all ADCs, depends on part used
|
||||
* @booster: booster supply reference
|
||||
* @vdd: vdd supply reference
|
||||
* @vdda: vdda analog supply reference
|
||||
* @vref: regulator reference
|
||||
* @vdd_uv: vdd supply voltage (microvolts)
|
||||
* @vdda_uv: vdda supply voltage (microvolts)
|
||||
* @cfg: compatible configuration data
|
||||
* @common: common data for all ADC instances
|
||||
* @ccr_bak: backup CCR in low power mode
|
||||
* @syscfg: reference to syscon, system control registers
|
||||
*/
|
||||
struct stm32_adc_priv {
|
||||
int irq[STM32_ADC_MAX_ADCS];
|
||||
struct irq_domain *domain;
|
||||
struct clk *aclk;
|
||||
struct clk *bclk;
|
||||
struct regulator *booster;
|
||||
struct regulator *vdd;
|
||||
struct regulator *vdda;
|
||||
struct regulator *vref;
|
||||
int vdd_uv;
|
||||
int vdda_uv;
|
||||
const struct stm32_adc_priv_cfg *cfg;
|
||||
struct stm32_adc_common common;
|
||||
u32 ccr_bak;
|
||||
struct regmap *syscfg;
|
||||
};
|
||||
|
||||
static struct stm32_adc_priv *to_stm32_adc_priv(struct stm32_adc_common *com)
|
||||
@ -349,7 +374,6 @@ static int stm32_adc_irq_probe(struct platform_device *pdev,
|
||||
*/
|
||||
if (i && priv->irq[i] == -ENXIO)
|
||||
continue;
|
||||
dev_err(&pdev->dev, "failed to get irq\n");
|
||||
|
||||
return priv->irq[i];
|
||||
}
|
||||
@ -390,6 +414,82 @@ static void stm32_adc_irq_remove(struct platform_device *pdev,
|
||||
}
|
||||
}
|
||||
|
||||
static int stm32_adc_core_switches_supply_en(struct stm32_adc_priv *priv,
|
||||
struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* On STM32H7 and STM32MP1, the ADC inputs are multiplexed with analog
|
||||
* switches (via PCSEL) which have reduced performances when their
|
||||
* supply is below 2.7V (vdda by default):
|
||||
* - Voltage booster can be used, to get full ADC performances
|
||||
* (increases power consumption).
|
||||
* - Vdd can be used to supply them, if above 2.7V (STM32MP1 only).
|
||||
*
|
||||
* Recommended settings for ANASWVDD and EN_BOOSTER:
|
||||
* - vdda < 2.7V but vdd > 2.7V: ANASWVDD = 1, EN_BOOSTER = 0 (stm32mp1)
|
||||
* - vdda < 2.7V and vdd < 2.7V: ANASWVDD = 0, EN_BOOSTER = 1
|
||||
* - vdda >= 2.7V: ANASWVDD = 0, EN_BOOSTER = 0 (default)
|
||||
*/
|
||||
if (priv->vdda_uv < 2700000) {
|
||||
if (priv->syscfg && priv->vdd_uv > 2700000) {
|
||||
ret = regulator_enable(priv->vdd);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "vdd enable failed %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regmap_write(priv->syscfg,
|
||||
STM32MP1_SYSCFG_PMCSETR,
|
||||
STM32MP1_SYSCFG_ANASWVDD_MASK);
|
||||
if (ret < 0) {
|
||||
regulator_disable(priv->vdd);
|
||||
dev_err(dev, "vdd select failed, %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
dev_dbg(dev, "analog switches supplied by vdd\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (priv->booster) {
|
||||
/*
|
||||
* This is optional, as this is a trade-off between
|
||||
* analog performance and power consumption.
|
||||
*/
|
||||
ret = regulator_enable(priv->booster);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "booster enable failed %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
dev_dbg(dev, "analog switches supplied by booster\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Fallback using vdda (default), nothing to do */
|
||||
dev_dbg(dev, "analog switches supplied by vdda (%d uV)\n",
|
||||
priv->vdda_uv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void stm32_adc_core_switches_supply_dis(struct stm32_adc_priv *priv)
|
||||
{
|
||||
if (priv->vdda_uv < 2700000) {
|
||||
if (priv->syscfg && priv->vdd_uv > 2700000) {
|
||||
regmap_write(priv->syscfg, STM32MP1_SYSCFG_PMCCLRR,
|
||||
STM32MP1_SYSCFG_ANASWVDD_MASK);
|
||||
regulator_disable(priv->vdd);
|
||||
return;
|
||||
}
|
||||
if (priv->booster)
|
||||
regulator_disable(priv->booster);
|
||||
}
|
||||
}
|
||||
|
||||
static int stm32_adc_core_hw_start(struct device *dev)
|
||||
{
|
||||
struct stm32_adc_common *common = dev_get_drvdata(dev);
|
||||
@ -402,10 +502,21 @@ static int stm32_adc_core_hw_start(struct device *dev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regulator_get_voltage(priv->vdda);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "vdda get voltage failed, %d\n", ret);
|
||||
goto err_vdda_disable;
|
||||
}
|
||||
priv->vdda_uv = ret;
|
||||
|
||||
ret = stm32_adc_core_switches_supply_en(priv, dev);
|
||||
if (ret < 0)
|
||||
goto err_vdda_disable;
|
||||
|
||||
ret = regulator_enable(priv->vref);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "vref enable failed\n");
|
||||
goto err_vdda_disable;
|
||||
goto err_switches_dis;
|
||||
}
|
||||
|
||||
if (priv->bclk) {
|
||||
@ -433,6 +544,8 @@ err_bclk_disable:
|
||||
clk_disable_unprepare(priv->bclk);
|
||||
err_regulator_disable:
|
||||
regulator_disable(priv->vref);
|
||||
err_switches_dis:
|
||||
stm32_adc_core_switches_supply_dis(priv);
|
||||
err_vdda_disable:
|
||||
regulator_disable(priv->vdda);
|
||||
|
||||
@ -451,9 +564,80 @@ static void stm32_adc_core_hw_stop(struct device *dev)
|
||||
if (priv->bclk)
|
||||
clk_disable_unprepare(priv->bclk);
|
||||
regulator_disable(priv->vref);
|
||||
stm32_adc_core_switches_supply_dis(priv);
|
||||
regulator_disable(priv->vdda);
|
||||
}
|
||||
|
||||
static int stm32_adc_core_switches_probe(struct device *dev,
|
||||
struct stm32_adc_priv *priv)
|
||||
{
|
||||
struct device_node *np = dev->of_node;
|
||||
int ret;
|
||||
|
||||
/* Analog switches supply can be controlled by syscfg (optional) */
|
||||
priv->syscfg = syscon_regmap_lookup_by_phandle(np, "st,syscfg");
|
||||
if (IS_ERR(priv->syscfg)) {
|
||||
ret = PTR_ERR(priv->syscfg);
|
||||
if (ret != -ENODEV) {
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(dev, "Can't probe syscfg: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
priv->syscfg = NULL;
|
||||
}
|
||||
|
||||
/* Booster can be used to supply analog switches (optional) */
|
||||
if (priv->cfg->has_syscfg & HAS_VBOOSTER &&
|
||||
of_property_read_bool(np, "booster-supply")) {
|
||||
priv->booster = devm_regulator_get_optional(dev, "booster");
|
||||
if (IS_ERR(priv->booster)) {
|
||||
ret = PTR_ERR(priv->booster);
|
||||
if (ret != -ENODEV) {
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(dev, "can't get booster %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
priv->booster = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Vdd can be used to supply analog switches (optional) */
|
||||
if (priv->cfg->has_syscfg & HAS_ANASWVDD &&
|
||||
of_property_read_bool(np, "vdd-supply")) {
|
||||
priv->vdd = devm_regulator_get_optional(dev, "vdd");
|
||||
if (IS_ERR(priv->vdd)) {
|
||||
ret = PTR_ERR(priv->vdd);
|
||||
if (ret != -ENODEV) {
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(dev, "can't get vdd %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
priv->vdd = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (priv->vdd) {
|
||||
ret = regulator_enable(priv->vdd);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "vdd enable failed %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regulator_get_voltage(priv->vdd);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "vdd get voltage failed %d\n", ret);
|
||||
regulator_disable(priv->vdd);
|
||||
return ret;
|
||||
}
|
||||
priv->vdd_uv = ret;
|
||||
|
||||
regulator_disable(priv->vdd);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stm32_adc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct stm32_adc_priv *priv;
|
||||
@ -514,6 +698,10 @@ static int stm32_adc_probe(struct platform_device *pdev)
|
||||
priv->bclk = NULL;
|
||||
}
|
||||
|
||||
ret = stm32_adc_core_switches_probe(dev, priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pm_runtime_get_noresume(dev);
|
||||
pm_runtime_set_active(dev);
|
||||
pm_runtime_set_autosuspend_delay(dev, STM32_ADC_CORE_SLEEP_DELAY_MS);
|
||||
@ -611,12 +799,14 @@ static const struct stm32_adc_priv_cfg stm32h7_adc_priv_cfg = {
|
||||
.regs = &stm32h7_adc_common_regs,
|
||||
.clk_sel = stm32h7_adc_clk_sel,
|
||||
.max_clk_rate_hz = 36000000,
|
||||
.has_syscfg = HAS_VBOOSTER,
|
||||
};
|
||||
|
||||
static const struct stm32_adc_priv_cfg stm32mp1_adc_priv_cfg = {
|
||||
.regs = &stm32h7_adc_common_regs,
|
||||
.clk_sel = stm32h7_adc_clk_sel,
|
||||
.max_clk_rate_hz = 40000000,
|
||||
.has_syscfg = HAS_VBOOSTER | HAS_ANASWVDD,
|
||||
};
|
||||
|
||||
static const struct of_device_id stm32_adc_of_match[] = {
|
||||
|
@ -1919,10 +1919,8 @@ static int stm32_adc_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
adc->irq = platform_get_irq(pdev, 0);
|
||||
if (adc->irq < 0) {
|
||||
dev_err(&pdev->dev, "failed to get irq\n");
|
||||
if (adc->irq < 0)
|
||||
return adc->irq;
|
||||
}
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, adc->irq, stm32_adc_isr,
|
||||
0, pdev->name, adc);
|
||||
|
@ -1601,11 +1601,8 @@ static int stm32_dfsdm_adc_probe(struct platform_device *pdev)
|
||||
* So IRQ associated to filter instance 0 is dedicated to the Filter 0.
|
||||
*/
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
if (irq != -EPROBE_DEFER)
|
||||
dev_err(dev, "Failed to get IRQ: %d\n", irq);
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
}
|
||||
|
||||
ret = devm_request_irq(dev, irq, stm32_dfsdm_irq,
|
||||
0, pdev->name, adc);
|
||||
|
@ -460,10 +460,8 @@ static int sun4i_irq_init(struct platform_device *pdev, const char *name,
|
||||
atomic_set(atomic, 1);
|
||||
|
||||
ret = platform_get_irq_byname(pdev, name);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "no %s interrupt registered\n", name);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regmap_irq_get_virq(mfd_dev->regmap_irqc, ret);
|
||||
if (ret < 0) {
|
||||
|
@ -905,10 +905,8 @@ static int twl6030_gpadc_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(&pdev->dev, "failed to get irq\n");
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
}
|
||||
|
||||
ret = devm_request_threaded_irq(dev, irq, NULL,
|
||||
twl6030_gpadc_irq_handler,
|
||||
|
@ -821,10 +821,8 @@ static int vf610_adc_probe(struct platform_device *pdev)
|
||||
return PTR_ERR(info->regs);
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(&pdev->dev, "no irq resource?\n");
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
}
|
||||
|
||||
ret = devm_request_irq(info->dev, irq,
|
||||
vf610_adc_isr, 0,
|
||||
|
@ -63,10 +63,35 @@ static int cros_ec_sensors_read(struct iio_dev *indio_dev,
|
||||
|
||||
/* Save values */
|
||||
for (i = CROS_EC_SENSOR_X; i < CROS_EC_SENSOR_MAX_AXIS; i++)
|
||||
st->core.calib[i] =
|
||||
st->core.calib[i].offset =
|
||||
st->core.resp->sensor_offset.offset[i];
|
||||
ret = IIO_VAL_INT;
|
||||
*val = st->core.calib[idx];
|
||||
*val = st->core.calib[idx].offset;
|
||||
break;
|
||||
case IIO_CHAN_INFO_CALIBSCALE:
|
||||
st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_SCALE;
|
||||
st->core.param.sensor_offset.flags = 0;
|
||||
|
||||
ret = cros_ec_motion_send_host_cmd(&st->core, 0);
|
||||
if (ret == -EPROTO) {
|
||||
/* Reading calibscale is not supported on older EC. */
|
||||
*val = 1;
|
||||
*val2 = 0;
|
||||
ret = IIO_VAL_INT_PLUS_MICRO;
|
||||
break;
|
||||
} else if (ret) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Save values */
|
||||
for (i = CROS_EC_SENSOR_X; i < CROS_EC_SENSOR_MAX_AXIS; i++)
|
||||
st->core.calib[i].scale =
|
||||
st->core.resp->sensor_scale.scale[i];
|
||||
|
||||
*val = st->core.calib[idx].scale >> 15;
|
||||
*val2 = ((st->core.calib[idx].scale & 0x7FFF) * 1000000LL) /
|
||||
MOTION_SENSE_DEFAULT_SCALE;
|
||||
ret = IIO_VAL_INT_PLUS_MICRO;
|
||||
break;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_RANGE;
|
||||
@ -134,7 +159,7 @@ static int cros_ec_sensors_write(struct iio_dev *indio_dev,
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
st->core.calib[idx] = val;
|
||||
st->core.calib[idx].offset = val;
|
||||
|
||||
/* Send to EC for each axis, even if not complete */
|
||||
st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_OFFSET;
|
||||
@ -142,10 +167,25 @@ static int cros_ec_sensors_write(struct iio_dev *indio_dev,
|
||||
MOTION_SENSE_SET_OFFSET;
|
||||
for (i = CROS_EC_SENSOR_X; i < CROS_EC_SENSOR_MAX_AXIS; i++)
|
||||
st->core.param.sensor_offset.offset[i] =
|
||||
st->core.calib[i];
|
||||
st->core.calib[i].offset;
|
||||
st->core.param.sensor_offset.temp =
|
||||
EC_MOTION_SENSE_INVALID_CALIB_TEMP;
|
||||
|
||||
ret = cros_ec_motion_send_host_cmd(&st->core, 0);
|
||||
break;
|
||||
case IIO_CHAN_INFO_CALIBSCALE:
|
||||
st->core.calib[idx].scale = val;
|
||||
/* Send to EC for each axis, even if not complete */
|
||||
|
||||
st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_SCALE;
|
||||
st->core.param.sensor_offset.flags =
|
||||
MOTION_SENSE_SET_OFFSET;
|
||||
for (i = CROS_EC_SENSOR_X; i < CROS_EC_SENSOR_MAX_AXIS; i++)
|
||||
st->core.param.sensor_scale.scale[i] =
|
||||
st->core.calib[i].scale;
|
||||
st->core.param.sensor_scale.temp =
|
||||
EC_MOTION_SENSE_INVALID_CALIB_TEMP;
|
||||
|
||||
ret = cros_ec_motion_send_host_cmd(&st->core, 0);
|
||||
break;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
@ -175,6 +215,7 @@ static int cros_ec_sensors_write(struct iio_dev *indio_dev,
|
||||
static const struct iio_info ec_sensors_info = {
|
||||
.read_raw = &cros_ec_sensors_read,
|
||||
.write_raw = &cros_ec_sensors_write,
|
||||
.read_avail = &cros_ec_sensors_core_read_avail,
|
||||
};
|
||||
|
||||
static int cros_ec_sensors_probe(struct platform_device *pdev)
|
||||
@ -206,11 +247,14 @@ static int cros_ec_sensors_probe(struct platform_device *pdev)
|
||||
/* Common part */
|
||||
channel->info_mask_separate =
|
||||
BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_CALIBBIAS);
|
||||
BIT(IIO_CHAN_INFO_CALIBBIAS) |
|
||||
BIT(IIO_CHAN_INFO_CALIBSCALE);
|
||||
channel->info_mask_shared_by_all =
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_FREQUENCY) |
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ);
|
||||
channel->info_mask_shared_by_all_available =
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ);
|
||||
channel->scan_type.realbits = CROS_EC_SENSOR_BITS;
|
||||
channel->scan_type.storagebits = CROS_EC_SENSOR_BITS;
|
||||
channel->scan_index = i;
|
||||
|
@ -25,6 +25,62 @@ static char *cros_ec_loc[] = {
|
||||
[MOTIONSENSE_LOC_MAX] = "unknown",
|
||||
};
|
||||
|
||||
static int cros_ec_get_host_cmd_version_mask(struct cros_ec_device *ec_dev,
|
||||
u16 cmd_offset, u16 cmd, u32 *mask)
|
||||
{
|
||||
int ret;
|
||||
struct {
|
||||
struct cros_ec_command msg;
|
||||
union {
|
||||
struct ec_params_get_cmd_versions params;
|
||||
struct ec_response_get_cmd_versions resp;
|
||||
};
|
||||
} __packed buf = {
|
||||
.msg = {
|
||||
.command = EC_CMD_GET_CMD_VERSIONS + cmd_offset,
|
||||
.insize = sizeof(struct ec_response_get_cmd_versions),
|
||||
.outsize = sizeof(struct ec_params_get_cmd_versions)
|
||||
},
|
||||
.params = {.cmd = cmd}
|
||||
};
|
||||
|
||||
ret = cros_ec_cmd_xfer_status(ec_dev, &buf.msg);
|
||||
if (ret >= 0)
|
||||
*mask = buf.resp.version_mask;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void get_default_min_max_freq(enum motionsensor_type type,
|
||||
u32 *min_freq,
|
||||
u32 *max_freq)
|
||||
{
|
||||
switch (type) {
|
||||
case MOTIONSENSE_TYPE_ACCEL:
|
||||
case MOTIONSENSE_TYPE_GYRO:
|
||||
*min_freq = 12500;
|
||||
*max_freq = 100000;
|
||||
break;
|
||||
case MOTIONSENSE_TYPE_MAG:
|
||||
*min_freq = 5000;
|
||||
*max_freq = 25000;
|
||||
break;
|
||||
case MOTIONSENSE_TYPE_PROX:
|
||||
case MOTIONSENSE_TYPE_LIGHT:
|
||||
*min_freq = 100;
|
||||
*max_freq = 50000;
|
||||
break;
|
||||
case MOTIONSENSE_TYPE_BARO:
|
||||
*min_freq = 250;
|
||||
*max_freq = 20000;
|
||||
break;
|
||||
case MOTIONSENSE_TYPE_ACTIVITY:
|
||||
default:
|
||||
*min_freq = 0;
|
||||
*max_freq = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int cros_ec_sensors_core_init(struct platform_device *pdev,
|
||||
struct iio_dev *indio_dev,
|
||||
bool physical_device)
|
||||
@ -33,6 +89,8 @@ int cros_ec_sensors_core_init(struct platform_device *pdev,
|
||||
struct cros_ec_sensors_core_state *state = iio_priv(indio_dev);
|
||||
struct cros_ec_dev *ec = dev_get_drvdata(pdev->dev.parent);
|
||||
struct cros_ec_sensor_platform *sensor_platform = dev_get_platdata(dev);
|
||||
u32 ver_mask;
|
||||
int ret, i;
|
||||
|
||||
platform_set_drvdata(pdev, indio_dev);
|
||||
|
||||
@ -47,8 +105,15 @@ int cros_ec_sensors_core_init(struct platform_device *pdev,
|
||||
|
||||
mutex_init(&state->cmd_lock);
|
||||
|
||||
ret = cros_ec_get_host_cmd_version_mask(state->ec,
|
||||
ec->cmd_offset,
|
||||
EC_CMD_MOTION_SENSE_CMD,
|
||||
&ver_mask);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Set up the host command structure. */
|
||||
state->msg->version = 2;
|
||||
state->msg->version = fls(ver_mask) - 1;
|
||||
state->msg->command = EC_CMD_MOTION_SENSE_CMD + ec->cmd_offset;
|
||||
state->msg->outsize = sizeof(struct ec_params_motion_sense);
|
||||
|
||||
@ -60,12 +125,32 @@ int cros_ec_sensors_core_init(struct platform_device *pdev,
|
||||
|
||||
state->param.cmd = MOTIONSENSE_CMD_INFO;
|
||||
state->param.info.sensor_num = sensor_platform->sensor_num;
|
||||
if (cros_ec_motion_send_host_cmd(state, 0)) {
|
||||
ret = cros_ec_motion_send_host_cmd(state, 0);
|
||||
if (ret) {
|
||||
dev_warn(dev, "Can not access sensor info\n");
|
||||
return -EIO;
|
||||
return ret;
|
||||
}
|
||||
state->type = state->resp->info.type;
|
||||
state->loc = state->resp->info.location;
|
||||
|
||||
/* Set sign vector, only used for backward compatibility. */
|
||||
memset(state->sign, 1, CROS_EC_SENSOR_MAX_AXIS);
|
||||
|
||||
for (i = CROS_EC_SENSOR_X; i < CROS_EC_SENSOR_MAX_AXIS; i++)
|
||||
state->calib[i].scale = MOTION_SENSE_DEFAULT_SCALE;
|
||||
|
||||
/* 0 is a correct value used to stop the device */
|
||||
state->frequencies[0] = 0;
|
||||
if (state->msg->version < 3) {
|
||||
get_default_min_max_freq(state->resp->info.type,
|
||||
&state->frequencies[1],
|
||||
&state->frequencies[2]);
|
||||
} else {
|
||||
state->frequencies[1] =
|
||||
state->resp->info_3.min_frequency;
|
||||
state->frequencies[2] =
|
||||
state->resp->info_3.max_frequency;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -86,7 +171,7 @@ int cros_ec_motion_send_host_cmd(struct cros_ec_sensors_core_state *state,
|
||||
|
||||
ret = cros_ec_cmd_xfer_status(state->ec, state->msg);
|
||||
if (ret < 0)
|
||||
return -EIO;
|
||||
return ret;
|
||||
|
||||
if (ret &&
|
||||
state->resp != (struct ec_response_motion_sense *)state->msg->data)
|
||||
@ -118,7 +203,7 @@ static ssize_t cros_ec_sensors_calibrate(struct iio_dev *indio_dev,
|
||||
} else {
|
||||
/* Save values */
|
||||
for (i = CROS_EC_SENSOR_X; i < CROS_EC_SENSOR_MAX_AXIS; i++)
|
||||
st->calib[i] = st->resp->perform_calib.offset[i];
|
||||
st->calib[i].offset = st->resp->perform_calib.offset[i];
|
||||
}
|
||||
mutex_unlock(&st->cmd_lock);
|
||||
|
||||
@ -268,6 +353,7 @@ static int cros_ec_sensors_read_data_unsafe(struct iio_dev *indio_dev,
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*data *= st->sign[i];
|
||||
data++;
|
||||
}
|
||||
|
||||
@ -396,7 +482,7 @@ int cros_ec_sensors_core_read(struct cros_ec_sensors_core_state *st,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
int ret = IIO_VAL_INT;
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
@ -404,22 +490,27 @@ int cros_ec_sensors_core_read(struct cros_ec_sensors_core_state *st,
|
||||
st->param.ec_rate.data =
|
||||
EC_MOTION_SENSE_NO_VALUE;
|
||||
|
||||
if (cros_ec_motion_send_host_cmd(st, 0))
|
||||
ret = -EIO;
|
||||
else
|
||||
*val = st->resp->ec_rate.ret;
|
||||
ret = cros_ec_motion_send_host_cmd(st, 0);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
*val = st->resp->ec_rate.ret;
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
case IIO_CHAN_INFO_FREQUENCY:
|
||||
st->param.cmd = MOTIONSENSE_CMD_SENSOR_ODR;
|
||||
st->param.sensor_odr.data =
|
||||
EC_MOTION_SENSE_NO_VALUE;
|
||||
|
||||
if (cros_ec_motion_send_host_cmd(st, 0))
|
||||
ret = -EIO;
|
||||
else
|
||||
*val = st->resp->sensor_odr.ret;
|
||||
ret = cros_ec_motion_send_host_cmd(st, 0);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
*val = st->resp->sensor_odr.ret;
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -427,11 +518,32 @@ int cros_ec_sensors_core_read(struct cros_ec_sensors_core_state *st,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cros_ec_sensors_core_read);
|
||||
|
||||
int cros_ec_sensors_core_read_avail(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
const int **vals,
|
||||
int *type,
|
||||
int *length,
|
||||
long mask)
|
||||
{
|
||||
struct cros_ec_sensors_core_state *state = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
*length = ARRAY_SIZE(state->frequencies);
|
||||
*vals = (const int *)&state->frequencies;
|
||||
*type = IIO_VAL_INT;
|
||||
return IIO_AVAIL_LIST;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cros_ec_sensors_core_read_avail);
|
||||
|
||||
int cros_ec_sensors_core_write(struct cros_ec_sensors_core_state *st,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
int ret = 0;
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_FREQUENCY:
|
||||
@ -441,17 +553,16 @@ int cros_ec_sensors_core_write(struct cros_ec_sensors_core_state *st,
|
||||
/* Always roundup, so caller gets at least what it asks for. */
|
||||
st->param.sensor_odr.roundup = 1;
|
||||
|
||||
if (cros_ec_motion_send_host_cmd(st, 0))
|
||||
ret = -EIO;
|
||||
ret = cros_ec_motion_send_host_cmd(st, 0);
|
||||
break;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
st->param.cmd = MOTIONSENSE_CMD_EC_RATE;
|
||||
st->param.ec_rate.data = val;
|
||||
|
||||
if (cros_ec_motion_send_host_cmd(st, 0))
|
||||
ret = -EIO;
|
||||
else
|
||||
st->curr_sampl_freq = val;
|
||||
ret = cros_ec_motion_send_host_cmd(st, 0);
|
||||
if (ret)
|
||||
break;
|
||||
st->curr_sampl_freq = val;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
|
@ -8,11 +8,16 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/time.h>
|
||||
|
||||
#include <linux/hid-sensor-hub.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
#define HZ_PER_MHZ 1000000L
|
||||
|
||||
static struct {
|
||||
u32 usage_id;
|
||||
int unit; /* 0 for default others from HID sensor spec */
|
||||
@ -68,16 +73,6 @@ static struct {
|
||||
{HID_USAGE_SENSOR_HUMIDITY, 0, 1000, 0},
|
||||
};
|
||||
|
||||
static int pow_10(unsigned power)
|
||||
{
|
||||
int i;
|
||||
int ret = 1;
|
||||
for (i = 0; i < power; ++i)
|
||||
ret = ret * 10;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void simple_div(int dividend, int divisor, int *whole,
|
||||
int *micro_frac)
|
||||
{
|
||||
@ -96,14 +91,16 @@ static void simple_div(int dividend, int divisor, int *whole,
|
||||
rem *= 10;
|
||||
exp++;
|
||||
}
|
||||
*micro_frac = (rem / divisor) * pow_10(6-exp);
|
||||
*micro_frac = (rem / divisor) * int_pow(10, 6 - exp);
|
||||
}
|
||||
}
|
||||
|
||||
static void split_micro_fraction(unsigned int no, int exp, int *val1, int *val2)
|
||||
{
|
||||
*val1 = no/pow_10(exp);
|
||||
*val2 = no%pow_10(exp) * pow_10(6-exp);
|
||||
int divisor = int_pow(10, exp);
|
||||
|
||||
*val1 = no / divisor;
|
||||
*val2 = no % divisor * int_pow(10, 6 - exp);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -125,7 +122,7 @@ static void convert_from_vtf_format(u32 value, int size, int exp,
|
||||
}
|
||||
exp = hid_sensor_convert_exponent(exp);
|
||||
if (exp >= 0) {
|
||||
*val1 = sign * value * pow_10(exp);
|
||||
*val1 = sign * value * int_pow(10, exp);
|
||||
*val2 = 0;
|
||||
} else {
|
||||
split_micro_fraction(value, -exp, val1, val2);
|
||||
@ -138,6 +135,7 @@ static void convert_from_vtf_format(u32 value, int size, int exp,
|
||||
|
||||
static u32 convert_to_vtf_format(int size, int exp, int val1, int val2)
|
||||
{
|
||||
int divisor;
|
||||
u32 value;
|
||||
int sign = 1;
|
||||
|
||||
@ -145,10 +143,13 @@ static u32 convert_to_vtf_format(int size, int exp, int val1, int val2)
|
||||
sign = -1;
|
||||
exp = hid_sensor_convert_exponent(exp);
|
||||
if (exp < 0) {
|
||||
value = abs(val1) * pow_10(-exp);
|
||||
value += abs(val2) / pow_10(6+exp);
|
||||
} else
|
||||
value = abs(val1) / pow_10(exp);
|
||||
divisor = int_pow(10, 6 + exp);
|
||||
value = abs(val1) * int_pow(10, -exp);
|
||||
value += abs(val2) / divisor;
|
||||
} else {
|
||||
divisor = int_pow(10, exp);
|
||||
value = abs(val1) / divisor;
|
||||
}
|
||||
if (sign < 0)
|
||||
value = ((1LL << (size * 8)) - value);
|
||||
|
||||
@ -211,12 +212,12 @@ int hid_sensor_write_samp_freq_value(struct hid_sensor_common *st,
|
||||
if (val1 < 0 || val2 < 0)
|
||||
return -EINVAL;
|
||||
|
||||
value = val1 * pow_10(6) + val2;
|
||||
value = val1 * HZ_PER_MHZ + val2;
|
||||
if (value) {
|
||||
if (st->poll.units == HID_USAGE_SENSOR_UNITS_MILLISECOND)
|
||||
value = pow_10(9)/value;
|
||||
value = NSEC_PER_SEC / value;
|
||||
else if (st->poll.units == HID_USAGE_SENSOR_UNITS_SECOND)
|
||||
value = pow_10(6)/value;
|
||||
value = USEC_PER_SEC / value;
|
||||
else
|
||||
value = 0;
|
||||
}
|
||||
@ -305,40 +306,44 @@ EXPORT_SYMBOL(hid_sensor_write_raw_hyst_value);
|
||||
static void adjust_exponent_nano(int *val0, int *val1, int scale0,
|
||||
int scale1, int exp)
|
||||
{
|
||||
int divisor;
|
||||
int i;
|
||||
int x;
|
||||
int res;
|
||||
int rem;
|
||||
|
||||
if (exp > 0) {
|
||||
*val0 = scale0 * pow_10(exp);
|
||||
*val0 = scale0 * int_pow(10, exp);
|
||||
res = 0;
|
||||
if (exp > 9) {
|
||||
*val1 = 0;
|
||||
return;
|
||||
}
|
||||
for (i = 0; i < exp; ++i) {
|
||||
x = scale1 / pow_10(8 - i);
|
||||
res += (pow_10(exp - 1 - i) * x);
|
||||
scale1 = scale1 % pow_10(8 - i);
|
||||
divisor = int_pow(10, 8 - i);
|
||||
x = scale1 / divisor;
|
||||
res += int_pow(10, exp - 1 - i) * x;
|
||||
scale1 = scale1 % divisor;
|
||||
}
|
||||
*val0 += res;
|
||||
*val1 = scale1 * pow_10(exp);
|
||||
*val1 = scale1 * int_pow(10, exp);
|
||||
} else if (exp < 0) {
|
||||
exp = abs(exp);
|
||||
if (exp > 9) {
|
||||
*val0 = *val1 = 0;
|
||||
return;
|
||||
}
|
||||
*val0 = scale0 / pow_10(exp);
|
||||
rem = scale0 % pow_10(exp);
|
||||
divisor = int_pow(10, exp);
|
||||
*val0 = scale0 / divisor;
|
||||
rem = scale0 % divisor;
|
||||
res = 0;
|
||||
for (i = 0; i < (9 - exp); ++i) {
|
||||
x = scale1 / pow_10(8 - i);
|
||||
res += (pow_10(8 - exp - i) * x);
|
||||
scale1 = scale1 % pow_10(8 - i);
|
||||
divisor = int_pow(10, 8 - i);
|
||||
x = scale1 / divisor;
|
||||
res += int_pow(10, 8 - exp - i) * x;
|
||||
scale1 = scale1 % divisor;
|
||||
}
|
||||
*val1 = rem * pow_10(9 - exp) + res;
|
||||
*val1 = rem * int_pow(10, 9 - exp) + res;
|
||||
} else {
|
||||
*val0 = scale0;
|
||||
*val1 = scale1;
|
||||
|
@ -5,9 +5,11 @@
|
||||
|
||||
config IIO_ST_SENSORS_I2C
|
||||
tristate
|
||||
select REGMAP_I2C
|
||||
|
||||
config IIO_ST_SENSORS_SPI
|
||||
tristate
|
||||
select REGMAP_SPI
|
||||
|
||||
config IIO_ST_SENSORS_CORE
|
||||
tristate
|
||||
|
@ -17,15 +17,16 @@
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/irqreturn.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include <linux/iio/common/st_sensors.h>
|
||||
|
||||
|
||||
static int st_sensors_get_buffer_element(struct iio_dev *indio_dev, u8 *buf)
|
||||
{
|
||||
int i;
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
unsigned int num_data_channels = sdata->num_data_channels;
|
||||
int i;
|
||||
|
||||
for_each_set_bit(i, indio_dev->active_scan_mask, num_data_channels) {
|
||||
const struct iio_chan_spec *channel = &indio_dev->channels[i];
|
||||
@ -36,11 +37,8 @@ static int st_sensors_get_buffer_element(struct iio_dev *indio_dev, u8 *buf)
|
||||
channel->scan_type.storagebits >> 3;
|
||||
|
||||
buf = PTR_ALIGN(buf, storage_bytes);
|
||||
if (sdata->tf->read_multiple_byte(&sdata->tb, sdata->dev,
|
||||
channel->address,
|
||||
bytes_to_read, buf,
|
||||
sdata->multiread_bit) <
|
||||
bytes_to_read)
|
||||
if (regmap_bulk_read(sdata->regmap, channel->address,
|
||||
buf, bytes_to_read) < 0)
|
||||
return -EIO;
|
||||
|
||||
/* Advance the buffer pointer */
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <asm/unaligned.h>
|
||||
#include <linux/iio/common/st_sensors.h>
|
||||
|
||||
@ -28,19 +29,10 @@ static inline u32 st_sensors_get_unaligned_le24(const u8 *p)
|
||||
int st_sensors_write_data_with_mask(struct iio_dev *indio_dev,
|
||||
u8 reg_addr, u8 mask, u8 data)
|
||||
{
|
||||
int err;
|
||||
u8 new_data;
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
|
||||
err = sdata->tf->read_byte(&sdata->tb, sdata->dev, reg_addr, &new_data);
|
||||
if (err < 0)
|
||||
goto st_sensors_write_data_with_mask_error;
|
||||
|
||||
new_data = ((new_data & (~mask)) | ((data << __ffs(mask)) & mask));
|
||||
err = sdata->tf->write_byte(&sdata->tb, sdata->dev, reg_addr, new_data);
|
||||
|
||||
st_sensors_write_data_with_mask_error:
|
||||
return err;
|
||||
return regmap_update_bits(sdata->regmap,
|
||||
reg_addr, mask, data << __ffs(mask));
|
||||
}
|
||||
|
||||
int st_sensors_debugfs_reg_access(struct iio_dev *indio_dev,
|
||||
@ -48,19 +40,15 @@ int st_sensors_debugfs_reg_access(struct iio_dev *indio_dev,
|
||||
unsigned *readval)
|
||||
{
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
u8 readdata;
|
||||
int err;
|
||||
|
||||
if (!readval)
|
||||
return sdata->tf->write_byte(&sdata->tb, sdata->dev,
|
||||
(u8)reg, (u8)writeval);
|
||||
return regmap_write(sdata->regmap, reg, writeval);
|
||||
|
||||
err = sdata->tf->read_byte(&sdata->tb, sdata->dev, (u8)reg, &readdata);
|
||||
err = regmap_read(sdata->regmap, reg, readval);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
*readval = (unsigned)readdata;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(st_sensors_debugfs_reg_access);
|
||||
@ -545,7 +533,7 @@ st_sensors_match_scale_error:
|
||||
EXPORT_SYMBOL(st_sensors_set_fullscale_by_gain);
|
||||
|
||||
static int st_sensors_read_axis_data(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *ch, int *data)
|
||||
struct iio_chan_spec const *ch, int *data)
|
||||
{
|
||||
int err;
|
||||
u8 *outdata;
|
||||
@ -554,13 +542,12 @@ static int st_sensors_read_axis_data(struct iio_dev *indio_dev,
|
||||
|
||||
byte_for_channel = DIV_ROUND_UP(ch->scan_type.realbits +
|
||||
ch->scan_type.shift, 8);
|
||||
outdata = kmalloc(byte_for_channel, GFP_KERNEL);
|
||||
outdata = kmalloc(byte_for_channel, GFP_DMA | GFP_KERNEL);
|
||||
if (!outdata)
|
||||
return -ENOMEM;
|
||||
|
||||
err = sdata->tf->read_multiple_byte(&sdata->tb, sdata->dev,
|
||||
ch->address, byte_for_channel,
|
||||
outdata, sdata->multiread_bit);
|
||||
err = regmap_bulk_read(sdata->regmap, ch->address,
|
||||
outdata, byte_for_channel);
|
||||
if (err < 0)
|
||||
goto st_sensors_free_memory;
|
||||
|
||||
@ -608,69 +595,55 @@ out:
|
||||
}
|
||||
EXPORT_SYMBOL(st_sensors_read_info_raw);
|
||||
|
||||
static int st_sensors_init_interface_mode(struct iio_dev *indio_dev,
|
||||
const struct st_sensor_settings *sensor_settings)
|
||||
/*
|
||||
* st_sensors_get_settings_index() - get index of the sensor settings for a
|
||||
* specific device from list of settings
|
||||
* @name: device name buffer reference.
|
||||
* @list: sensor settings list.
|
||||
* @list_length: length of sensor settings list.
|
||||
*
|
||||
* Return: non negative number on success (valid index),
|
||||
* negative error code otherwise.
|
||||
*/
|
||||
int st_sensors_get_settings_index(const char *name,
|
||||
const struct st_sensor_settings *list,
|
||||
const int list_length)
|
||||
{
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
struct device_node *np = sdata->dev->of_node;
|
||||
struct st_sensors_platform_data *pdata;
|
||||
int i, n;
|
||||
|
||||
pdata = (struct st_sensors_platform_data *)sdata->dev->platform_data;
|
||||
if (((np && of_property_read_bool(np, "spi-3wire")) ||
|
||||
(pdata && pdata->spi_3wire)) && sensor_settings->sim.addr) {
|
||||
int err;
|
||||
|
||||
err = sdata->tf->write_byte(&sdata->tb, sdata->dev,
|
||||
sensor_settings->sim.addr,
|
||||
sensor_settings->sim.value);
|
||||
if (err < 0) {
|
||||
dev_err(&indio_dev->dev,
|
||||
"failed to init interface mode\n");
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int st_sensors_check_device_support(struct iio_dev *indio_dev,
|
||||
int num_sensors_list,
|
||||
const struct st_sensor_settings *sensor_settings)
|
||||
{
|
||||
int i, n, err = 0;
|
||||
u8 wai;
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
|
||||
for (i = 0; i < num_sensors_list; i++) {
|
||||
for (i = 0; i < list_length; i++) {
|
||||
for (n = 0; n < ST_SENSORS_MAX_4WAI; n++) {
|
||||
if (strcmp(indio_dev->name,
|
||||
sensor_settings[i].sensors_supported[n]) == 0) {
|
||||
break;
|
||||
}
|
||||
if (strcmp(name, list[i].sensors_supported[n]) == 0)
|
||||
return i;
|
||||
}
|
||||
if (n < ST_SENSORS_MAX_4WAI)
|
||||
break;
|
||||
}
|
||||
if (i == num_sensors_list) {
|
||||
dev_err(&indio_dev->dev, "device name %s not recognized.\n",
|
||||
indio_dev->name);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
err = st_sensors_init_interface_mode(indio_dev, &sensor_settings[i]);
|
||||
if (err < 0)
|
||||
return err;
|
||||
return -ENODEV;
|
||||
}
|
||||
EXPORT_SYMBOL(st_sensors_get_settings_index);
|
||||
|
||||
if (sensor_settings[i].wai_addr) {
|
||||
err = sdata->tf->read_byte(&sdata->tb, sdata->dev,
|
||||
sensor_settings[i].wai_addr, &wai);
|
||||
/*
|
||||
* st_sensors_verify_id() - verify sensor ID (WhoAmI) is matching with the
|
||||
* expected value
|
||||
* @indio_dev: IIO device reference.
|
||||
*
|
||||
* Return: 0 on success (valid sensor ID), else a negative error code.
|
||||
*/
|
||||
int st_sensors_verify_id(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
int wai, err;
|
||||
|
||||
if (sdata->sensor_settings->wai_addr) {
|
||||
err = regmap_read(sdata->regmap,
|
||||
sdata->sensor_settings->wai_addr, &wai);
|
||||
if (err < 0) {
|
||||
dev_err(&indio_dev->dev,
|
||||
"failed to read Who-Am-I register.\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
if (sensor_settings[i].wai != wai) {
|
||||
if (sdata->sensor_settings->wai != wai) {
|
||||
dev_err(&indio_dev->dev,
|
||||
"%s: WhoAmI mismatch (0x%x).\n",
|
||||
indio_dev->name, wai);
|
||||
@ -678,12 +651,9 @@ int st_sensors_check_device_support(struct iio_dev *indio_dev,
|
||||
}
|
||||
}
|
||||
|
||||
sdata->sensor_settings =
|
||||
(struct st_sensor_settings *)&sensor_settings[i];
|
||||
|
||||
return i;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(st_sensors_check_device_support);
|
||||
EXPORT_SYMBOL(st_sensors_verify_id);
|
||||
|
||||
ssize_t st_sensors_sysfs_sampling_frequency_avail(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
|
@ -13,68 +13,58 @@
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include <linux/iio/common/st_sensors_i2c.h>
|
||||
|
||||
|
||||
#define ST_SENSORS_I2C_MULTIREAD 0x80
|
||||
|
||||
static unsigned int st_sensors_i2c_get_irq(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
|
||||
return to_i2c_client(sdata->dev)->irq;
|
||||
}
|
||||
|
||||
static int st_sensors_i2c_read_byte(struct st_sensor_transfer_buffer *tb,
|
||||
struct device *dev, u8 reg_addr, u8 *res_byte)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = i2c_smbus_read_byte_data(to_i2c_client(dev), reg_addr);
|
||||
if (err < 0)
|
||||
goto st_accel_i2c_read_byte_error;
|
||||
|
||||
*res_byte = err & 0xff;
|
||||
|
||||
st_accel_i2c_read_byte_error:
|
||||
return err < 0 ? err : 0;
|
||||
}
|
||||
|
||||
static int st_sensors_i2c_read_multiple_byte(
|
||||
struct st_sensor_transfer_buffer *tb, struct device *dev,
|
||||
u8 reg_addr, int len, u8 *data, bool multiread_bit)
|
||||
{
|
||||
if (multiread_bit)
|
||||
reg_addr |= ST_SENSORS_I2C_MULTIREAD;
|
||||
|
||||
return i2c_smbus_read_i2c_block_data_or_emulated(to_i2c_client(dev),
|
||||
reg_addr, len, data);
|
||||
}
|
||||
|
||||
static int st_sensors_i2c_write_byte(struct st_sensor_transfer_buffer *tb,
|
||||
struct device *dev, u8 reg_addr, u8 data)
|
||||
{
|
||||
return i2c_smbus_write_byte_data(to_i2c_client(dev), reg_addr, data);
|
||||
}
|
||||
|
||||
static const struct st_sensor_transfer_function st_sensors_tf_i2c = {
|
||||
.read_byte = st_sensors_i2c_read_byte,
|
||||
.write_byte = st_sensors_i2c_write_byte,
|
||||
.read_multiple_byte = st_sensors_i2c_read_multiple_byte,
|
||||
static const struct regmap_config st_sensors_i2c_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
};
|
||||
|
||||
void st_sensors_i2c_configure(struct iio_dev *indio_dev,
|
||||
struct i2c_client *client, struct st_sensor_data *sdata)
|
||||
static const struct regmap_config st_sensors_i2c_regmap_multiread_bit_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.read_flag_mask = ST_SENSORS_I2C_MULTIREAD,
|
||||
};
|
||||
|
||||
/*
|
||||
* st_sensors_i2c_configure() - configure I2C interface
|
||||
* @indio_dev: IIO device reference.
|
||||
* @client: i2c client reference.
|
||||
*
|
||||
* Return: 0 on success, else a negative error code.
|
||||
*/
|
||||
int st_sensors_i2c_configure(struct iio_dev *indio_dev,
|
||||
struct i2c_client *client)
|
||||
{
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
const struct regmap_config *config;
|
||||
|
||||
if (sdata->sensor_settings->multi_read_bit)
|
||||
config = &st_sensors_i2c_regmap_multiread_bit_config;
|
||||
else
|
||||
config = &st_sensors_i2c_regmap_config;
|
||||
|
||||
sdata->regmap = devm_regmap_init_i2c(client, config);
|
||||
if (IS_ERR(sdata->regmap)) {
|
||||
dev_err(&client->dev, "Failed to register i2c regmap (%d)\n",
|
||||
(int)PTR_ERR(sdata->regmap));
|
||||
return PTR_ERR(sdata->regmap);
|
||||
}
|
||||
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->name = client->name;
|
||||
|
||||
sdata->dev = &client->dev;
|
||||
sdata->tf = &st_sensors_tf_i2c;
|
||||
sdata->get_irq_data_ready = st_sensors_i2c_get_irq;
|
||||
sdata->irq = client->irq;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(st_sensors_i2c_configure);
|
||||
|
||||
|
@ -11,108 +11,108 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include <linux/iio/common/st_sensors_spi.h>
|
||||
|
||||
#include "st_sensors_core.h"
|
||||
|
||||
#define ST_SENSORS_SPI_MULTIREAD 0xc0
|
||||
#define ST_SENSORS_SPI_READ 0x80
|
||||
|
||||
static unsigned int st_sensors_spi_get_irq(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
|
||||
return to_spi_device(sdata->dev)->irq;
|
||||
}
|
||||
|
||||
static int st_sensors_spi_read(struct st_sensor_transfer_buffer *tb,
|
||||
struct device *dev, u8 reg_addr, int len, u8 *data, bool multiread_bit)
|
||||
{
|
||||
int err;
|
||||
|
||||
struct spi_transfer xfers[] = {
|
||||
{
|
||||
.tx_buf = tb->tx_buf,
|
||||
.bits_per_word = 8,
|
||||
.len = 1,
|
||||
},
|
||||
{
|
||||
.rx_buf = tb->rx_buf,
|
||||
.bits_per_word = 8,
|
||||
.len = len,
|
||||
}
|
||||
};
|
||||
|
||||
mutex_lock(&tb->buf_lock);
|
||||
if ((multiread_bit) && (len > 1))
|
||||
tb->tx_buf[0] = reg_addr | ST_SENSORS_SPI_MULTIREAD;
|
||||
else
|
||||
tb->tx_buf[0] = reg_addr | ST_SENSORS_SPI_READ;
|
||||
|
||||
err = spi_sync_transfer(to_spi_device(dev), xfers, ARRAY_SIZE(xfers));
|
||||
if (err)
|
||||
goto acc_spi_read_error;
|
||||
|
||||
memcpy(data, tb->rx_buf, len);
|
||||
mutex_unlock(&tb->buf_lock);
|
||||
return len;
|
||||
|
||||
acc_spi_read_error:
|
||||
mutex_unlock(&tb->buf_lock);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int st_sensors_spi_read_byte(struct st_sensor_transfer_buffer *tb,
|
||||
struct device *dev, u8 reg_addr, u8 *res_byte)
|
||||
{
|
||||
return st_sensors_spi_read(tb, dev, reg_addr, 1, res_byte, false);
|
||||
}
|
||||
|
||||
static int st_sensors_spi_read_multiple_byte(
|
||||
struct st_sensor_transfer_buffer *tb, struct device *dev,
|
||||
u8 reg_addr, int len, u8 *data, bool multiread_bit)
|
||||
{
|
||||
return st_sensors_spi_read(tb, dev, reg_addr, len, data, multiread_bit);
|
||||
}
|
||||
|
||||
static int st_sensors_spi_write_byte(struct st_sensor_transfer_buffer *tb,
|
||||
struct device *dev, u8 reg_addr, u8 data)
|
||||
{
|
||||
int err;
|
||||
|
||||
struct spi_transfer xfers = {
|
||||
.tx_buf = tb->tx_buf,
|
||||
.bits_per_word = 8,
|
||||
.len = 2,
|
||||
};
|
||||
|
||||
mutex_lock(&tb->buf_lock);
|
||||
tb->tx_buf[0] = reg_addr;
|
||||
tb->tx_buf[1] = data;
|
||||
|
||||
err = spi_sync_transfer(to_spi_device(dev), &xfers, 1);
|
||||
mutex_unlock(&tb->buf_lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct st_sensor_transfer_function st_sensors_tf_spi = {
|
||||
.read_byte = st_sensors_spi_read_byte,
|
||||
.write_byte = st_sensors_spi_write_byte,
|
||||
.read_multiple_byte = st_sensors_spi_read_multiple_byte,
|
||||
static const struct regmap_config st_sensors_spi_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
};
|
||||
|
||||
void st_sensors_spi_configure(struct iio_dev *indio_dev,
|
||||
struct spi_device *spi, struct st_sensor_data *sdata)
|
||||
static const struct regmap_config st_sensors_spi_regmap_multiread_bit_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.read_flag_mask = ST_SENSORS_SPI_MULTIREAD,
|
||||
};
|
||||
|
||||
/*
|
||||
* st_sensors_is_spi_3_wire() - check if SPI 3-wire mode has been selected
|
||||
* @spi: spi device reference.
|
||||
*
|
||||
* Return: true if SPI 3-wire mode is selected, false otherwise.
|
||||
*/
|
||||
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;
|
||||
|
||||
pdata = (struct st_sensors_platform_data *)spi->dev.platform_data;
|
||||
if ((np && of_property_read_bool(np, "spi-3wire")) ||
|
||||
(pdata && pdata->spi_3wire)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* st_sensors_configure_spi_3_wire() - configure SPI 3-wire if needed
|
||||
* @spi: spi device reference.
|
||||
* @settings: sensor specific settings reference.
|
||||
*
|
||||
* Return: 0 on success, else a negative error code.
|
||||
*/
|
||||
static int st_sensors_configure_spi_3_wire(struct spi_device *spi,
|
||||
struct st_sensor_settings *settings)
|
||||
{
|
||||
if (settings->sim.addr) {
|
||||
u8 buffer[] = {
|
||||
settings->sim.addr,
|
||||
settings->sim.value
|
||||
};
|
||||
|
||||
return spi_write(spi, buffer, 2);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* st_sensors_spi_configure() - configure SPI interface
|
||||
* @indio_dev: IIO device reference.
|
||||
* @spi: spi device reference.
|
||||
*
|
||||
* Return: 0 on success, else a negative error code.
|
||||
*/
|
||||
int st_sensors_spi_configure(struct iio_dev *indio_dev,
|
||||
struct spi_device *spi)
|
||||
{
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
const struct regmap_config *config;
|
||||
int err;
|
||||
|
||||
if (st_sensors_is_spi_3_wire(spi)) {
|
||||
err = st_sensors_configure_spi_3_wire(spi,
|
||||
sdata->sensor_settings);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (sdata->sensor_settings->multi_read_bit)
|
||||
config = &st_sensors_spi_regmap_multiread_bit_config;
|
||||
else
|
||||
config = &st_sensors_spi_regmap_config;
|
||||
|
||||
sdata->regmap = devm_regmap_init_spi(spi, config);
|
||||
if (IS_ERR(sdata->regmap)) {
|
||||
dev_err(&spi->dev, "Failed to register spi regmap (%d)\n",
|
||||
(int)PTR_ERR(sdata->regmap));
|
||||
return PTR_ERR(sdata->regmap);
|
||||
}
|
||||
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->name = spi->modalias;
|
||||
|
||||
sdata->dev = &spi->dev;
|
||||
sdata->tf = &st_sensors_tf_spi;
|
||||
sdata->get_irq_data_ready = st_sensors_spi_get_irq;
|
||||
sdata->irq = spi->irq;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(st_sensors_spi_configure);
|
||||
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/iio/common/st_sensors.h>
|
||||
#include "st_sensors_core.h"
|
||||
|
||||
@ -26,8 +27,7 @@
|
||||
static int st_sensors_new_samples_available(struct iio_dev *indio_dev,
|
||||
struct st_sensor_data *sdata)
|
||||
{
|
||||
u8 status;
|
||||
int ret;
|
||||
int ret, status;
|
||||
|
||||
/* How would I know if I can't check it? */
|
||||
if (!sdata->sensor_settings->drdy_irq.stat_drdy.addr)
|
||||
@ -37,9 +37,9 @@ static int st_sensors_new_samples_available(struct iio_dev *indio_dev,
|
||||
if (!indio_dev->active_scan_mask)
|
||||
return 0;
|
||||
|
||||
ret = sdata->tf->read_byte(&sdata->tb, sdata->dev,
|
||||
sdata->sensor_settings->drdy_irq.stat_drdy.addr,
|
||||
&status);
|
||||
ret = regmap_read(sdata->regmap,
|
||||
sdata->sensor_settings->drdy_irq.stat_drdy.addr,
|
||||
&status);
|
||||
if (ret < 0) {
|
||||
dev_err(sdata->dev,
|
||||
"error checking samples available\n");
|
||||
@ -121,9 +121,9 @@ static irqreturn_t st_sensors_irq_thread(int irq, void *p)
|
||||
int st_sensors_allocate_trigger(struct iio_dev *indio_dev,
|
||||
const struct iio_trigger_ops *trigger_ops)
|
||||
{
|
||||
int err, irq;
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
unsigned long irq_trig;
|
||||
int err;
|
||||
|
||||
sdata->trig = iio_trigger_alloc("%s-trigger", indio_dev->name);
|
||||
if (sdata->trig == NULL) {
|
||||
@ -135,8 +135,7 @@ int st_sensors_allocate_trigger(struct iio_dev *indio_dev,
|
||||
sdata->trig->ops = trigger_ops;
|
||||
sdata->trig->dev.parent = sdata->dev;
|
||||
|
||||
irq = sdata->get_irq_data_ready(indio_dev);
|
||||
irq_trig = irqd_get_trigger_type(irq_get_irq_data(irq));
|
||||
irq_trig = irqd_get_trigger_type(irq_get_irq_data(sdata->irq));
|
||||
/*
|
||||
* If the IRQ is triggered on falling edge, we need to mark the
|
||||
* interrupt as active low, if the hardware supports this.
|
||||
@ -206,12 +205,12 @@ int st_sensors_allocate_trigger(struct iio_dev *indio_dev,
|
||||
sdata->sensor_settings->drdy_irq.stat_drdy.addr)
|
||||
irq_trig |= IRQF_SHARED;
|
||||
|
||||
err = request_threaded_irq(sdata->get_irq_data_ready(indio_dev),
|
||||
st_sensors_irq_handler,
|
||||
st_sensors_irq_thread,
|
||||
irq_trig,
|
||||
sdata->trig->name,
|
||||
sdata->trig);
|
||||
err = request_threaded_irq(sdata->irq,
|
||||
st_sensors_irq_handler,
|
||||
st_sensors_irq_thread,
|
||||
irq_trig,
|
||||
sdata->trig->name,
|
||||
sdata->trig);
|
||||
if (err) {
|
||||
dev_err(&indio_dev->dev, "failed to request trigger IRQ.\n");
|
||||
goto iio_trigger_free;
|
||||
@ -227,7 +226,7 @@ int st_sensors_allocate_trigger(struct iio_dev *indio_dev,
|
||||
return 0;
|
||||
|
||||
iio_trigger_register_error:
|
||||
free_irq(sdata->get_irq_data_ready(indio_dev), sdata->trig);
|
||||
free_irq(sdata->irq, sdata->trig);
|
||||
iio_trigger_free:
|
||||
iio_trigger_free(sdata->trig);
|
||||
return err;
|
||||
@ -239,7 +238,7 @@ void st_sensors_deallocate_trigger(struct iio_dev *indio_dev)
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
|
||||
iio_trigger_unregister(sdata->trig);
|
||||
free_irq(sdata->get_irq_data_ready(indio_dev), sdata->trig);
|
||||
free_irq(sdata->irq, sdata->trig);
|
||||
iio_trigger_free(sdata->trig);
|
||||
}
|
||||
EXPORT_SYMBOL(st_sensors_deallocate_trigger);
|
||||
|
@ -220,7 +220,7 @@ static int ad5380_read_raw(struct iio_dev *indio_dev,
|
||||
if (ret)
|
||||
return ret;
|
||||
*val >>= chan->scan_type.shift;
|
||||
val -= (1 << chan->scan_type.realbits) / 2;
|
||||
*val -= (1 << chan->scan_type.realbits) / 2;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = 2 * st->vref;
|
||||
|
@ -32,6 +32,7 @@ static const struct st_sensors_platform_data gyro_pdata = {
|
||||
.drdy_int_pin = 2,
|
||||
};
|
||||
|
||||
const struct st_sensor_settings *st_gyro_get_settings(const char *name);
|
||||
int st_gyro_common_probe(struct iio_dev *indio_dev);
|
||||
void st_gyro_common_remove(struct iio_dev *indio_dev);
|
||||
|
||||
|
@ -29,61 +29,51 @@ int st_gyro_trig_set_state(struct iio_trigger *trig, bool state)
|
||||
return st_sensors_set_dataready_irq(indio_dev, state);
|
||||
}
|
||||
|
||||
static int st_gyro_buffer_preenable(struct iio_dev *indio_dev)
|
||||
{
|
||||
return st_sensors_set_enable(indio_dev, true);
|
||||
}
|
||||
|
||||
static int st_gyro_buffer_postenable(struct iio_dev *indio_dev)
|
||||
{
|
||||
int err;
|
||||
struct st_sensor_data *gdata = iio_priv(indio_dev);
|
||||
|
||||
gdata->buffer_data = kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
|
||||
if (gdata->buffer_data == NULL) {
|
||||
err = -ENOMEM;
|
||||
goto allocate_memory_error;
|
||||
}
|
||||
|
||||
err = st_sensors_set_axis_enable(indio_dev,
|
||||
(u8)indio_dev->active_scan_mask[0]);
|
||||
if (err < 0)
|
||||
goto st_gyro_buffer_postenable_error;
|
||||
|
||||
err = iio_triggered_buffer_postenable(indio_dev);
|
||||
if (err < 0)
|
||||
goto st_gyro_buffer_postenable_error;
|
||||
return err;
|
||||
|
||||
return err;
|
||||
err = st_sensors_set_axis_enable(indio_dev,
|
||||
(u8)indio_dev->active_scan_mask[0]);
|
||||
if (err < 0)
|
||||
goto st_gyro_buffer_predisable;
|
||||
|
||||
st_gyro_buffer_postenable_error:
|
||||
kfree(gdata->buffer_data);
|
||||
allocate_memory_error:
|
||||
err = st_sensors_set_enable(indio_dev, true);
|
||||
if (err < 0)
|
||||
goto st_gyro_buffer_enable_all_axis;
|
||||
|
||||
return 0;
|
||||
|
||||
st_gyro_buffer_enable_all_axis:
|
||||
st_sensors_set_axis_enable(indio_dev, ST_SENSORS_ENABLE_ALL_AXIS);
|
||||
st_gyro_buffer_predisable:
|
||||
iio_triggered_buffer_predisable(indio_dev);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int st_gyro_buffer_predisable(struct iio_dev *indio_dev)
|
||||
{
|
||||
int err;
|
||||
struct st_sensor_data *gdata = iio_priv(indio_dev);
|
||||
|
||||
err = iio_triggered_buffer_predisable(indio_dev);
|
||||
if (err < 0)
|
||||
goto st_gyro_buffer_predisable_error;
|
||||
|
||||
err = st_sensors_set_axis_enable(indio_dev, ST_SENSORS_ENABLE_ALL_AXIS);
|
||||
if (err < 0)
|
||||
goto st_gyro_buffer_predisable_error;
|
||||
int err, err2;
|
||||
|
||||
err = st_sensors_set_enable(indio_dev, false);
|
||||
if (err < 0)
|
||||
goto st_gyro_buffer_predisable;
|
||||
|
||||
err = st_sensors_set_axis_enable(indio_dev, ST_SENSORS_ENABLE_ALL_AXIS);
|
||||
|
||||
st_gyro_buffer_predisable:
|
||||
err2 = iio_triggered_buffer_predisable(indio_dev);
|
||||
if (!err)
|
||||
err = err2;
|
||||
|
||||
st_gyro_buffer_predisable_error:
|
||||
kfree(gdata->buffer_data);
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct iio_buffer_setup_ops st_gyro_buffer_setup_ops = {
|
||||
.preenable = &st_gyro_buffer_preenable,
|
||||
.postenable = &st_gyro_buffer_postenable,
|
||||
.predisable = &st_gyro_buffer_predisable,
|
||||
};
|
||||
|
@ -12,7 +12,6 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/gpio.h>
|
||||
@ -368,28 +367,41 @@ static const struct iio_trigger_ops st_gyro_trigger_ops = {
|
||||
#define ST_GYRO_TRIGGER_OPS NULL
|
||||
#endif
|
||||
|
||||
/*
|
||||
* st_gyro_get_settings() - get sensor settings from device name
|
||||
* @name: device name buffer reference.
|
||||
*
|
||||
* Return: valid reference on success, NULL otherwise.
|
||||
*/
|
||||
const struct st_sensor_settings *st_gyro_get_settings(const char *name)
|
||||
{
|
||||
int index = st_sensors_get_settings_index(name,
|
||||
st_gyro_sensors_settings,
|
||||
ARRAY_SIZE(st_gyro_sensors_settings));
|
||||
if (index < 0)
|
||||
return NULL;
|
||||
|
||||
return &st_gyro_sensors_settings[index];
|
||||
}
|
||||
EXPORT_SYMBOL(st_gyro_get_settings);
|
||||
|
||||
int st_gyro_common_probe(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct st_sensor_data *gdata = iio_priv(indio_dev);
|
||||
int irq = gdata->get_irq_data_ready(indio_dev);
|
||||
int err;
|
||||
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &gyro_info;
|
||||
mutex_init(&gdata->tb.buf_lock);
|
||||
|
||||
err = st_sensors_power_enable(indio_dev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = st_sensors_check_device_support(indio_dev,
|
||||
ARRAY_SIZE(st_gyro_sensors_settings),
|
||||
st_gyro_sensors_settings);
|
||||
err = st_sensors_verify_id(indio_dev);
|
||||
if (err < 0)
|
||||
goto st_gyro_power_off;
|
||||
|
||||
gdata->num_data_channels = ST_GYRO_NUMBER_DATA_CHANNELS;
|
||||
gdata->multiread_bit = gdata->sensor_settings->multi_read_bit;
|
||||
indio_dev->channels = gdata->sensor_settings->ch;
|
||||
indio_dev->num_channels = ST_SENSORS_NUMBER_ALL_CHANNELS;
|
||||
|
||||
@ -406,7 +418,7 @@ int st_gyro_common_probe(struct iio_dev *indio_dev)
|
||||
if (err < 0)
|
||||
goto st_gyro_power_off;
|
||||
|
||||
if (irq > 0) {
|
||||
if (gdata->irq > 0) {
|
||||
err = st_sensors_allocate_trigger(indio_dev,
|
||||
ST_GYRO_TRIGGER_OPS);
|
||||
if (err < 0)
|
||||
@ -423,7 +435,7 @@ int st_gyro_common_probe(struct iio_dev *indio_dev)
|
||||
return 0;
|
||||
|
||||
st_gyro_device_register_error:
|
||||
if (irq > 0)
|
||||
if (gdata->irq > 0)
|
||||
st_sensors_deallocate_trigger(indio_dev);
|
||||
st_gyro_probe_trigger_error:
|
||||
st_gyro_deallocate_ring(indio_dev);
|
||||
@ -441,7 +453,7 @@ void st_gyro_common_remove(struct iio_dev *indio_dev)
|
||||
st_sensors_power_disable(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
if (gdata->get_irq_data_ready(indio_dev) > 0)
|
||||
if (gdata->irq > 0)
|
||||
st_sensors_deallocate_trigger(indio_dev);
|
||||
|
||||
st_gyro_deallocate_ring(indio_dev);
|
||||
|
@ -63,21 +63,33 @@ MODULE_DEVICE_TABLE(of, st_gyro_of_match);
|
||||
#endif
|
||||
|
||||
static int st_gyro_i2c_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
const struct st_sensor_settings *settings;
|
||||
struct st_sensor_data *gdata;
|
||||
struct iio_dev *indio_dev;
|
||||
int err;
|
||||
|
||||
st_sensors_of_name_probe(&client->dev, st_gyro_of_match,
|
||||
client->name, sizeof(client->name));
|
||||
|
||||
settings = st_gyro_get_settings(client->name);
|
||||
if (!settings) {
|
||||
dev_err(&client->dev, "device name %s not recognized.\n",
|
||||
client->name);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*gdata));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
gdata = iio_priv(indio_dev);
|
||||
st_sensors_of_name_probe(&client->dev, st_gyro_of_match,
|
||||
client->name, sizeof(client->name));
|
||||
gdata->sensor_settings = (struct st_sensor_settings *)settings;
|
||||
|
||||
st_sensors_i2c_configure(indio_dev, client, gdata);
|
||||
err = st_sensors_i2c_configure(indio_dev, client);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = st_gyro_common_probe(indio_dev);
|
||||
if (err < 0)
|
||||
|
@ -69,19 +69,31 @@ MODULE_DEVICE_TABLE(of, st_gyro_of_match);
|
||||
|
||||
static int st_gyro_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
const struct st_sensor_settings *settings;
|
||||
struct st_sensor_data *gdata;
|
||||
struct iio_dev *indio_dev;
|
||||
int err;
|
||||
|
||||
st_sensors_of_name_probe(&spi->dev, st_gyro_of_match,
|
||||
spi->modalias, sizeof(spi->modalias));
|
||||
|
||||
settings = st_gyro_get_settings(spi->modalias);
|
||||
if (!settings) {
|
||||
dev_err(&spi->dev, "device name %s not recognized.\n",
|
||||
spi->modalias);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*gdata));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
gdata = iio_priv(indio_dev);
|
||||
gdata->sensor_settings = (struct st_sensor_settings *)settings;
|
||||
|
||||
st_sensors_of_name_probe(&spi->dev, st_gyro_of_match,
|
||||
spi->modalias, sizeof(spi->modalias));
|
||||
st_sensors_spi_configure(indio_dev, spi, gdata);
|
||||
err = st_sensors_spi_configure(indio_dev, spi);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = st_gyro_common_probe(indio_dev);
|
||||
if (err < 0)
|
||||
|
@ -240,32 +240,15 @@ static int am2315_probe(struct i2c_client *client,
|
||||
indio_dev->channels = am2315_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(am2315_channels);
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, iio_pollfunc_store_time,
|
||||
ret = devm_iio_triggered_buffer_setup(&client->dev,
|
||||
indio_dev, iio_pollfunc_store_time,
|
||||
am2315_trigger_handler, NULL);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "iio triggered buffer setup failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret < 0)
|
||||
goto err_buffer_cleanup;
|
||||
|
||||
return 0;
|
||||
|
||||
err_buffer_cleanup:
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int am2315_remove(struct i2c_client *client)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
|
||||
return 0;
|
||||
return devm_iio_device_register(&client->dev, indio_dev);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id am2315_i2c_id[] = {
|
||||
@ -287,7 +270,6 @@ static struct i2c_driver am2315_driver = {
|
||||
.acpi_match_table = ACPI_PTR(am2315_acpi_id),
|
||||
},
|
||||
.probe = am2315_probe,
|
||||
.remove = am2315_remove,
|
||||
.id_table = am2315_i2c_id,
|
||||
};
|
||||
|
||||
|
@ -385,28 +385,16 @@ static int hdc100x_probe(struct i2c_client *client,
|
||||
hdc100x_set_it_time(data, 1, hdc100x_int_time[1][0]);
|
||||
hdc100x_update_config(data, HDC100X_REG_CONFIG_ACQ_MODE, 0);
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, NULL,
|
||||
ret = devm_iio_triggered_buffer_setup(&client->dev,
|
||||
indio_dev, NULL,
|
||||
hdc100x_trigger_handler,
|
||||
&hdc_buffer_setup_ops);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "iio triggered buffer setup failed\n");
|
||||
return ret;
|
||||
}
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret < 0)
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hdc100x_remove(struct i2c_client *client)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
|
||||
return 0;
|
||||
return devm_iio_device_register(&client->dev, indio_dev);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id hdc100x_id[] = {
|
||||
@ -436,7 +424,6 @@ static struct i2c_driver hdc100x_driver = {
|
||||
.of_match_table = of_match_ptr(hdc100x_dt_ids),
|
||||
},
|
||||
.probe = hdc100x_probe,
|
||||
.remove = hdc100x_remove,
|
||||
.id_table = hdc100x_id,
|
||||
};
|
||||
module_i2c_driver(hdc100x_driver);
|
||||
|
@ -17,6 +17,18 @@ config ADIS16400
|
||||
adis16365, adis16400 and adis16405 triaxial inertial sensors
|
||||
(adis16400 series also have magnetometers).
|
||||
|
||||
config ADIS16460
|
||||
tristate "Analog Devices ADIS16460 and similar IMU driver"
|
||||
depends on SPI
|
||||
select IIO_ADIS_LIB
|
||||
select IIO_ADIS_LIB_BUFFER if IIO_BUFFER
|
||||
help
|
||||
Say yes here to build support for Analog Devices ADIS16460 inertial
|
||||
sensor.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called adis16460.
|
||||
|
||||
config ADIS16480
|
||||
tristate "Analog Devices ADIS16480 and similar IMU driver"
|
||||
depends on SPI
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
# When adding new entries keep the list in alphabetical order
|
||||
obj-$(CONFIG_ADIS16400) += adis16400.o
|
||||
obj-$(CONFIG_ADIS16460) += adis16460.o
|
||||
obj-$(CONFIG_ADIS16480) += adis16480.o
|
||||
|
||||
adis_lib-y += adis.o
|
||||
|
@ -39,18 +39,24 @@ int adis_write_reg(struct adis *adis, unsigned int reg,
|
||||
.len = 2,
|
||||
.cs_change = 1,
|
||||
.delay_usecs = adis->data->write_delay,
|
||||
.cs_change_delay = adis->data->cs_change_delay,
|
||||
.cs_change_delay_unit = SPI_DELAY_UNIT_USECS,
|
||||
}, {
|
||||
.tx_buf = adis->tx + 2,
|
||||
.bits_per_word = 8,
|
||||
.len = 2,
|
||||
.cs_change = 1,
|
||||
.delay_usecs = adis->data->write_delay,
|
||||
.cs_change_delay = adis->data->cs_change_delay,
|
||||
.cs_change_delay_unit = SPI_DELAY_UNIT_USECS,
|
||||
}, {
|
||||
.tx_buf = adis->tx + 4,
|
||||
.bits_per_word = 8,
|
||||
.len = 2,
|
||||
.cs_change = 1,
|
||||
.delay_usecs = adis->data->write_delay,
|
||||
.cs_change_delay = adis->data->cs_change_delay,
|
||||
.cs_change_delay_unit = SPI_DELAY_UNIT_USECS,
|
||||
}, {
|
||||
.tx_buf = adis->tx + 6,
|
||||
.bits_per_word = 8,
|
||||
@ -133,12 +139,16 @@ int adis_read_reg(struct adis *adis, unsigned int reg,
|
||||
.len = 2,
|
||||
.cs_change = 1,
|
||||
.delay_usecs = adis->data->write_delay,
|
||||
.cs_change_delay = adis->data->cs_change_delay,
|
||||
.cs_change_delay_unit = SPI_DELAY_UNIT_USECS,
|
||||
}, {
|
||||
.tx_buf = adis->tx + 2,
|
||||
.bits_per_word = 8,
|
||||
.len = 2,
|
||||
.cs_change = 1,
|
||||
.delay_usecs = adis->data->read_delay,
|
||||
.cs_change_delay = adis->data->cs_change_delay,
|
||||
.cs_change_delay_unit = SPI_DELAY_UNIT_USECS,
|
||||
}, {
|
||||
.tx_buf = adis->tx + 4,
|
||||
.rx_buf = adis->rx,
|
||||
@ -146,6 +156,8 @@ int adis_read_reg(struct adis *adis, unsigned int reg,
|
||||
.len = 2,
|
||||
.cs_change = 1,
|
||||
.delay_usecs = adis->data->read_delay,
|
||||
.cs_change_delay = adis->data->cs_change_delay,
|
||||
.cs_change_delay_unit = SPI_DELAY_UNIT_USECS,
|
||||
}, {
|
||||
.rx_buf = adis->rx + 2,
|
||||
.bits_per_word = 8,
|
||||
|
489
drivers/iio/imu/adis16460.c
Normal file
489
drivers/iio/imu/adis16460.c
Normal file
@ -0,0 +1,489 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* ADIS16460 IMU driver
|
||||
*
|
||||
* Copyright 2019 Analog Devices Inc.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/imu/adis.h>
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
|
||||
#define ADIS16460_REG_FLASH_CNT 0x00
|
||||
#define ADIS16460_REG_DIAG_STAT 0x02
|
||||
#define ADIS16460_REG_X_GYRO_LOW 0x04
|
||||
#define ADIS16460_REG_X_GYRO_OUT 0x06
|
||||
#define ADIS16460_REG_Y_GYRO_LOW 0x08
|
||||
#define ADIS16460_REG_Y_GYRO_OUT 0x0A
|
||||
#define ADIS16460_REG_Z_GYRO_LOW 0x0C
|
||||
#define ADIS16460_REG_Z_GYRO_OUT 0x0E
|
||||
#define ADIS16460_REG_X_ACCL_LOW 0x10
|
||||
#define ADIS16460_REG_X_ACCL_OUT 0x12
|
||||
#define ADIS16460_REG_Y_ACCL_LOW 0x14
|
||||
#define ADIS16460_REG_Y_ACCL_OUT 0x16
|
||||
#define ADIS16460_REG_Z_ACCL_LOW 0x18
|
||||
#define ADIS16460_REG_Z_ACCL_OUT 0x1A
|
||||
#define ADIS16460_REG_SMPL_CNTR 0x1C
|
||||
#define ADIS16460_REG_TEMP_OUT 0x1E
|
||||
#define ADIS16460_REG_X_DELT_ANG 0x24
|
||||
#define ADIS16460_REG_Y_DELT_ANG 0x26
|
||||
#define ADIS16460_REG_Z_DELT_ANG 0x28
|
||||
#define ADIS16460_REG_X_DELT_VEL 0x2A
|
||||
#define ADIS16460_REG_Y_DELT_VEL 0x2C
|
||||
#define ADIS16460_REG_Z_DELT_VEL 0x2E
|
||||
#define ADIS16460_REG_MSC_CTRL 0x32
|
||||
#define ADIS16460_REG_SYNC_SCAL 0x34
|
||||
#define ADIS16460_REG_DEC_RATE 0x36
|
||||
#define ADIS16460_REG_FLTR_CTRL 0x38
|
||||
#define ADIS16460_REG_GLOB_CMD 0x3E
|
||||
#define ADIS16460_REG_X_GYRO_OFF 0x40
|
||||
#define ADIS16460_REG_Y_GYRO_OFF 0x42
|
||||
#define ADIS16460_REG_Z_GYRO_OFF 0x44
|
||||
#define ADIS16460_REG_X_ACCL_OFF 0x46
|
||||
#define ADIS16460_REG_Y_ACCL_OFF 0x48
|
||||
#define ADIS16460_REG_Z_ACCL_OFF 0x4A
|
||||
#define ADIS16460_REG_LOT_ID1 0x52
|
||||
#define ADIS16460_REG_LOT_ID2 0x54
|
||||
#define ADIS16460_REG_PROD_ID 0x56
|
||||
#define ADIS16460_REG_SERIAL_NUM 0x58
|
||||
#define ADIS16460_REG_CAL_SGNTR 0x60
|
||||
#define ADIS16460_REG_CAL_CRC 0x62
|
||||
#define ADIS16460_REG_CODE_SGNTR 0x64
|
||||
#define ADIS16460_REG_CODE_CRC 0x66
|
||||
|
||||
struct adis16460_chip_info {
|
||||
unsigned int num_channels;
|
||||
const struct iio_chan_spec *channels;
|
||||
unsigned int gyro_max_val;
|
||||
unsigned int gyro_max_scale;
|
||||
unsigned int accel_max_val;
|
||||
unsigned int accel_max_scale;
|
||||
};
|
||||
|
||||
struct adis16460 {
|
||||
const struct adis16460_chip_info *chip_info;
|
||||
struct adis adis;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
|
||||
static int adis16460_show_serial_number(void *arg, u64 *val)
|
||||
{
|
||||
struct adis16460 *adis16460 = arg;
|
||||
u16 serial;
|
||||
int ret;
|
||||
|
||||
ret = adis_read_reg_16(&adis16460->adis, ADIS16460_REG_SERIAL_NUM,
|
||||
&serial);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = serial;
|
||||
|
||||
return 0;
|
||||
}
|
||||
DEFINE_SIMPLE_ATTRIBUTE(adis16460_serial_number_fops,
|
||||
adis16460_show_serial_number, NULL, "0x%.4llx\n");
|
||||
|
||||
static int adis16460_show_product_id(void *arg, u64 *val)
|
||||
{
|
||||
struct adis16460 *adis16460 = arg;
|
||||
u16 prod_id;
|
||||
int ret;
|
||||
|
||||
ret = adis_read_reg_16(&adis16460->adis, ADIS16460_REG_PROD_ID,
|
||||
&prod_id);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = prod_id;
|
||||
|
||||
return 0;
|
||||
}
|
||||
DEFINE_SIMPLE_ATTRIBUTE(adis16460_product_id_fops,
|
||||
adis16460_show_product_id, NULL, "%llu\n");
|
||||
|
||||
static int adis16460_show_flash_count(void *arg, u64 *val)
|
||||
{
|
||||
struct adis16460 *adis16460 = arg;
|
||||
u32 flash_count;
|
||||
int ret;
|
||||
|
||||
ret = adis_read_reg_32(&adis16460->adis, ADIS16460_REG_FLASH_CNT,
|
||||
&flash_count);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = flash_count;
|
||||
|
||||
return 0;
|
||||
}
|
||||
DEFINE_SIMPLE_ATTRIBUTE(adis16460_flash_count_fops,
|
||||
adis16460_show_flash_count, NULL, "%lld\n");
|
||||
|
||||
static int adis16460_debugfs_init(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct adis16460 *adis16460 = iio_priv(indio_dev);
|
||||
|
||||
debugfs_create_file("serial_number", 0400, indio_dev->debugfs_dentry,
|
||||
adis16460, &adis16460_serial_number_fops);
|
||||
debugfs_create_file("product_id", 0400, indio_dev->debugfs_dentry,
|
||||
adis16460, &adis16460_product_id_fops);
|
||||
debugfs_create_file("flash_count", 0400, indio_dev->debugfs_dentry,
|
||||
adis16460, &adis16460_flash_count_fops);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static int adis16460_debugfs_init(struct iio_dev *indio_dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static int adis16460_set_freq(struct iio_dev *indio_dev, int val, int val2)
|
||||
{
|
||||
struct adis16460 *st = iio_priv(indio_dev);
|
||||
int t;
|
||||
|
||||
t = val * 1000 + val2 / 1000;
|
||||
if (t <= 0)
|
||||
return -EINVAL;
|
||||
|
||||
t = 2048000 / t;
|
||||
if (t > 2048)
|
||||
t = 2048;
|
||||
|
||||
if (t != 0)
|
||||
t--;
|
||||
|
||||
return adis_write_reg_16(&st->adis, ADIS16460_REG_DEC_RATE, t);
|
||||
}
|
||||
|
||||
static int adis16460_get_freq(struct iio_dev *indio_dev, int *val, int *val2)
|
||||
{
|
||||
struct adis16460 *st = iio_priv(indio_dev);
|
||||
uint16_t t;
|
||||
int ret;
|
||||
unsigned int freq;
|
||||
|
||||
ret = adis_read_reg_16(&st->adis, ADIS16460_REG_DEC_RATE, &t);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
freq = 2048000 / (t + 1);
|
||||
*val = freq / 1000;
|
||||
*val2 = (freq % 1000) * 1000;
|
||||
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
}
|
||||
|
||||
static int adis16460_read_raw(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, int *val, int *val2, long info)
|
||||
{
|
||||
struct adis16460 *st = iio_priv(indio_dev);
|
||||
|
||||
switch (info) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
return adis_single_conversion(indio_dev, chan, 0, val);
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
switch (chan->type) {
|
||||
case IIO_ANGL_VEL:
|
||||
*val = st->chip_info->gyro_max_scale;
|
||||
*val2 = st->chip_info->gyro_max_val;
|
||||
return IIO_VAL_FRACTIONAL;
|
||||
case IIO_ACCEL:
|
||||
*val = st->chip_info->accel_max_scale;
|
||||
*val2 = st->chip_info->accel_max_val;
|
||||
return IIO_VAL_FRACTIONAL;
|
||||
case IIO_TEMP:
|
||||
*val = 50; /* 50 milli degrees Celsius/LSB */
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
*val = 500; /* 25 degrees Celsius = 0x0000 */
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
return adis16460_get_freq(indio_dev, val, val2);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int adis16460_write_raw(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, int val, int val2, long info)
|
||||
{
|
||||
switch (info) {
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
return adis16460_set_freq(indio_dev, val, val2);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
enum {
|
||||
ADIS16460_SCAN_GYRO_X,
|
||||
ADIS16460_SCAN_GYRO_Y,
|
||||
ADIS16460_SCAN_GYRO_Z,
|
||||
ADIS16460_SCAN_ACCEL_X,
|
||||
ADIS16460_SCAN_ACCEL_Y,
|
||||
ADIS16460_SCAN_ACCEL_Z,
|
||||
ADIS16460_SCAN_TEMP,
|
||||
};
|
||||
|
||||
#define ADIS16460_MOD_CHANNEL(_type, _mod, _address, _si, _bits) \
|
||||
{ \
|
||||
.type = (_type), \
|
||||
.modified = 1, \
|
||||
.channel2 = (_mod), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
|
||||
.address = (_address), \
|
||||
.scan_index = (_si), \
|
||||
.scan_type = { \
|
||||
.sign = 's', \
|
||||
.realbits = (_bits), \
|
||||
.storagebits = (_bits), \
|
||||
.endianness = IIO_BE, \
|
||||
}, \
|
||||
}
|
||||
|
||||
#define ADIS16460_GYRO_CHANNEL(_mod) \
|
||||
ADIS16460_MOD_CHANNEL(IIO_ANGL_VEL, IIO_MOD_ ## _mod, \
|
||||
ADIS16460_REG_ ## _mod ## _GYRO_LOW, ADIS16460_SCAN_GYRO_ ## _mod, \
|
||||
32)
|
||||
|
||||
#define ADIS16460_ACCEL_CHANNEL(_mod) \
|
||||
ADIS16460_MOD_CHANNEL(IIO_ACCEL, IIO_MOD_ ## _mod, \
|
||||
ADIS16460_REG_ ## _mod ## _ACCL_LOW, ADIS16460_SCAN_ACCEL_ ## _mod, \
|
||||
32)
|
||||
|
||||
#define ADIS16460_TEMP_CHANNEL() { \
|
||||
.type = IIO_TEMP, \
|
||||
.indexed = 1, \
|
||||
.channel = 0, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_SCALE) | \
|
||||
BIT(IIO_CHAN_INFO_OFFSET), \
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
|
||||
.address = ADIS16460_REG_TEMP_OUT, \
|
||||
.scan_index = ADIS16460_SCAN_TEMP, \
|
||||
.scan_type = { \
|
||||
.sign = 's', \
|
||||
.realbits = 16, \
|
||||
.storagebits = 16, \
|
||||
.endianness = IIO_BE, \
|
||||
}, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec adis16460_channels[] = {
|
||||
ADIS16460_GYRO_CHANNEL(X),
|
||||
ADIS16460_GYRO_CHANNEL(Y),
|
||||
ADIS16460_GYRO_CHANNEL(Z),
|
||||
ADIS16460_ACCEL_CHANNEL(X),
|
||||
ADIS16460_ACCEL_CHANNEL(Y),
|
||||
ADIS16460_ACCEL_CHANNEL(Z),
|
||||
ADIS16460_TEMP_CHANNEL(),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(7)
|
||||
};
|
||||
|
||||
static const struct adis16460_chip_info adis16460_chip_info = {
|
||||
.channels = adis16460_channels,
|
||||
.num_channels = ARRAY_SIZE(adis16460_channels),
|
||||
/*
|
||||
* storing the value in rad/degree and the scale in degree
|
||||
* gives us the result in rad and better precession than
|
||||
* storing the scale directly in rad.
|
||||
*/
|
||||
.gyro_max_val = IIO_RAD_TO_DEGREE(200 << 16),
|
||||
.gyro_max_scale = 1,
|
||||
.accel_max_val = IIO_M_S_2_TO_G(20000 << 16),
|
||||
.accel_max_scale = 5,
|
||||
};
|
||||
|
||||
static const struct iio_info adis16460_info = {
|
||||
.read_raw = &adis16460_read_raw,
|
||||
.write_raw = &adis16460_write_raw,
|
||||
.update_scan_mode = adis_update_scan_mode,
|
||||
.debugfs_reg_access = adis_debugfs_reg_access,
|
||||
};
|
||||
|
||||
static int adis16460_enable_irq(struct adis *adis, bool enable)
|
||||
{
|
||||
/*
|
||||
* There is no way to gate the data-ready signal internally inside the
|
||||
* ADIS16460 :(
|
||||
*/
|
||||
if (enable)
|
||||
enable_irq(adis->spi->irq);
|
||||
else
|
||||
disable_irq(adis->spi->irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adis16460_initial_setup(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct adis16460 *st = iio_priv(indio_dev);
|
||||
uint16_t prod_id;
|
||||
unsigned int device_id;
|
||||
int ret;
|
||||
|
||||
adis_reset(&st->adis);
|
||||
msleep(222);
|
||||
|
||||
ret = adis_write_reg_16(&st->adis, ADIS16460_REG_GLOB_CMD, BIT(1));
|
||||
if (ret)
|
||||
return ret;
|
||||
msleep(75);
|
||||
|
||||
ret = adis_check_status(&st->adis);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = adis_read_reg_16(&st->adis, ADIS16460_REG_PROD_ID, &prod_id);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = sscanf(indio_dev->name, "adis%u\n", &device_id);
|
||||
if (ret != 1)
|
||||
return -EINVAL;
|
||||
|
||||
if (prod_id != device_id)
|
||||
dev_warn(&indio_dev->dev, "Device ID(%u) and product ID(%u) do not match.",
|
||||
device_id, prod_id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define ADIS16460_DIAG_STAT_IN_CLK_OOS 7
|
||||
#define ADIS16460_DIAG_STAT_FLASH_MEM 6
|
||||
#define ADIS16460_DIAG_STAT_SELF_TEST 5
|
||||
#define ADIS16460_DIAG_STAT_OVERRANGE 4
|
||||
#define ADIS16460_DIAG_STAT_SPI_COMM 3
|
||||
#define ADIS16460_DIAG_STAT_FLASH_UPT 2
|
||||
|
||||
static const char * const adis16460_status_error_msgs[] = {
|
||||
[ADIS16460_DIAG_STAT_IN_CLK_OOS] = "Input clock out of sync",
|
||||
[ADIS16460_DIAG_STAT_FLASH_MEM] = "Flash memory failure",
|
||||
[ADIS16460_DIAG_STAT_SELF_TEST] = "Self test diagnostic failure",
|
||||
[ADIS16460_DIAG_STAT_OVERRANGE] = "Sensor overrange",
|
||||
[ADIS16460_DIAG_STAT_SPI_COMM] = "SPI communication failure",
|
||||
[ADIS16460_DIAG_STAT_FLASH_UPT] = "Flash update failure",
|
||||
};
|
||||
|
||||
static const struct adis_data adis16460_data = {
|
||||
.diag_stat_reg = ADIS16460_REG_DIAG_STAT,
|
||||
.glob_cmd_reg = ADIS16460_REG_GLOB_CMD,
|
||||
.has_paging = false,
|
||||
.read_delay = 5,
|
||||
.write_delay = 5,
|
||||
.cs_change_delay = 16,
|
||||
.status_error_msgs = adis16460_status_error_msgs,
|
||||
.status_error_mask = BIT(ADIS16460_DIAG_STAT_IN_CLK_OOS) |
|
||||
BIT(ADIS16460_DIAG_STAT_FLASH_MEM) |
|
||||
BIT(ADIS16460_DIAG_STAT_SELF_TEST) |
|
||||
BIT(ADIS16460_DIAG_STAT_OVERRANGE) |
|
||||
BIT(ADIS16460_DIAG_STAT_SPI_COMM) |
|
||||
BIT(ADIS16460_DIAG_STAT_FLASH_UPT),
|
||||
.enable_irq = adis16460_enable_irq,
|
||||
};
|
||||
|
||||
static int adis16460_probe(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct adis16460 *st;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
||||
if (indio_dev == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
|
||||
st->chip_info = &adis16460_chip_info;
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->name = spi_get_device_id(spi)->name;
|
||||
indio_dev->channels = st->chip_info->channels;
|
||||
indio_dev->num_channels = st->chip_info->num_channels;
|
||||
indio_dev->info = &adis16460_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
ret = adis_init(&st->adis, indio_dev, spi, &adis16460_data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = adis_setup_buffer_and_trigger(&st->adis, indio_dev, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
adis16460_enable_irq(&st->adis, 0);
|
||||
|
||||
ret = adis16460_initial_setup(indio_dev);
|
||||
if (ret)
|
||||
goto error_cleanup_buffer;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto error_cleanup_buffer;
|
||||
|
||||
adis16460_debugfs_init(indio_dev);
|
||||
|
||||
return 0;
|
||||
|
||||
error_cleanup_buffer:
|
||||
adis_cleanup_buffer_and_trigger(&st->adis, indio_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adis16460_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct adis16460 *st = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
adis_cleanup_buffer_and_trigger(&st->adis, indio_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id adis16460_ids[] = {
|
||||
{ "adis16460", 0 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, adis16460_ids);
|
||||
|
||||
static const struct of_device_id adis16460_of_match[] = {
|
||||
{ .compatible = "adi,adis16460" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, adis16460_of_match);
|
||||
|
||||
static struct spi_driver adis16460_driver = {
|
||||
.driver = {
|
||||
.name = "adis16460",
|
||||
.of_match_table = adis16460_of_match,
|
||||
},
|
||||
.id_table = adis16460_ids,
|
||||
.probe = adis16460_probe,
|
||||
.remove = adis16460_remove,
|
||||
};
|
||||
module_spi_driver(adis16460_driver);
|
||||
|
||||
MODULE_AUTHOR("Dragos Bogdan <dragos.bogdan@analog.com>");
|
||||
MODULE_DESCRIPTION("Analog Devices ADIS16460 IMU driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -14,8 +14,9 @@ config INV_MPU6050_I2C
|
||||
select INV_MPU6050_IIO
|
||||
select REGMAP_I2C
|
||||
help
|
||||
This driver supports the Invensense MPU6050/6500/9150 and
|
||||
ICM20608/20602 motion tracking devices over I2C.
|
||||
This driver supports the Invensense MPU6000/6050/6500/6515,
|
||||
MPU9150/9250/9255 and ICM20608/20602 motion tracking devices
|
||||
over I2C.
|
||||
This driver can be built as a module. The module will be called
|
||||
inv-mpu6050-i2c.
|
||||
|
||||
@ -25,7 +26,8 @@ config INV_MPU6050_SPI
|
||||
select INV_MPU6050_IIO
|
||||
select REGMAP_SPI
|
||||
help
|
||||
This driver supports the Invensense MPU6050/6500/9150 and
|
||||
ICM20608/20602 motion tracking devices over SPI.
|
||||
This driver supports the Invensense MPU6000/6050/6500/6515,
|
||||
MPU9150/9250/9255 and ICM20608/20602 motion tracking devices
|
||||
over SPI.
|
||||
This driver can be built as a module. The module will be called
|
||||
inv-mpu6050-spi.
|
||||
|
@ -1137,10 +1137,9 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name,
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
result = devm_add_action(dev, inv_mpu_core_disable_regulator_action,
|
||||
result = devm_add_action_or_reset(dev, inv_mpu_core_disable_regulator_action,
|
||||
st);
|
||||
if (result) {
|
||||
inv_mpu_core_disable_regulator_action(st);
|
||||
dev_err(dev, "Failed to setup regulator cleanup action %d\n",
|
||||
result);
|
||||
return result;
|
||||
|
@ -2,15 +2,17 @@
|
||||
|
||||
config IIO_ST_LSM6DSX
|
||||
tristate "ST_LSM6DSx driver for STM 6-axis IMU MEMS sensors"
|
||||
depends on (I2C || SPI)
|
||||
depends on (I2C || SPI || I3C)
|
||||
select IIO_BUFFER
|
||||
select IIO_KFIFO_BUF
|
||||
select IIO_ST_LSM6DSX_I2C if (I2C)
|
||||
select IIO_ST_LSM6DSX_SPI if (SPI_MASTER)
|
||||
select IIO_ST_LSM6DSX_I3C if (I3C)
|
||||
help
|
||||
Say yes here to build support for STMicroelectronics LSM6DSx imu
|
||||
sensor. Supported devices: lsm6ds3, lsm6ds3h, lsm6dsl, lsm6dsm,
|
||||
ism330dlc, lsm6dso, lsm6dsox, asm330lhh, lsm6dsr
|
||||
ism330dlc, lsm6dso, lsm6dsox, asm330lhh, lsm6dsr, lsm6ds3tr-c,
|
||||
ism330dhcx and the accelerometer/gyroscope of lsm9ds1.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called st_lsm6dsx.
|
||||
@ -24,3 +26,8 @@ config IIO_ST_LSM6DSX_SPI
|
||||
tristate
|
||||
depends on IIO_ST_LSM6DSX
|
||||
select REGMAP_SPI
|
||||
|
||||
config IIO_ST_LSM6DSX_I3C
|
||||
tristate
|
||||
depends on IIO_ST_LSM6DSX
|
||||
select REGMAP_I3C
|
||||
|
@ -5,3 +5,4 @@ st_lsm6dsx-y := st_lsm6dsx_core.o st_lsm6dsx_buffer.o \
|
||||
obj-$(CONFIG_IIO_ST_LSM6DSX) += st_lsm6dsx.o
|
||||
obj-$(CONFIG_IIO_ST_LSM6DSX_I2C) += st_lsm6dsx_i2c.o
|
||||
obj-$(CONFIG_IIO_ST_LSM6DSX_SPI) += st_lsm6dsx_spi.o
|
||||
obj-$(CONFIG_IIO_ST_LSM6DSX_I3C) += st_lsm6dsx_i3c.o
|
||||
|
@ -22,6 +22,9 @@
|
||||
#define ST_ASM330LHH_DEV_NAME "asm330lhh"
|
||||
#define ST_LSM6DSOX_DEV_NAME "lsm6dsox"
|
||||
#define ST_LSM6DSR_DEV_NAME "lsm6dsr"
|
||||
#define ST_LSM6DS3TRC_DEV_NAME "lsm6ds3tr-c"
|
||||
#define ST_ISM330DHCX_DEV_NAME "ism330dhcx"
|
||||
#define ST_LSM9DS1_DEV_NAME "lsm9ds1-imu"
|
||||
|
||||
enum st_lsm6dsx_hw_id {
|
||||
ST_LSM6DS3_ID,
|
||||
@ -33,6 +36,9 @@ enum st_lsm6dsx_hw_id {
|
||||
ST_ASM330LHH_ID,
|
||||
ST_LSM6DSOX_ID,
|
||||
ST_LSM6DSR_ID,
|
||||
ST_LSM6DS3TRC_ID,
|
||||
ST_ISM330DHCX_ID,
|
||||
ST_LSM9DS1_ID,
|
||||
ST_LSM6DSX_MAX_ID,
|
||||
};
|
||||
|
||||
@ -54,8 +60,8 @@ enum st_lsm6dsx_hw_id {
|
||||
.address = addr, \
|
||||
.modified = 1, \
|
||||
.channel2 = mod, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
|
||||
.scan_index = scan_idx, \
|
||||
.scan_type = { \
|
||||
@ -71,6 +77,7 @@ struct st_lsm6dsx_reg {
|
||||
u8 mask;
|
||||
};
|
||||
|
||||
struct st_lsm6dsx_sensor;
|
||||
struct st_lsm6dsx_hw;
|
||||
|
||||
struct st_lsm6dsx_odr {
|
||||
@ -97,12 +104,14 @@ struct st_lsm6dsx_fs_table_entry {
|
||||
|
||||
/**
|
||||
* struct st_lsm6dsx_fifo_ops - ST IMU FIFO settings
|
||||
* @update_fifo: Update FIFO configuration callback.
|
||||
* @read_fifo: Read FIFO callback.
|
||||
* @fifo_th: FIFO threshold register info (addr + mask).
|
||||
* @fifo_diff: FIFO diff status register info (addr + mask).
|
||||
* @th_wl: FIFO threshold word length.
|
||||
*/
|
||||
struct st_lsm6dsx_fifo_ops {
|
||||
int (*update_fifo)(struct st_lsm6dsx_sensor *sensor, bool enable);
|
||||
int (*read_fifo)(struct st_lsm6dsx_hw *hw);
|
||||
struct {
|
||||
u8 addr;
|
||||
@ -196,8 +205,14 @@ struct st_lsm6dsx_ext_dev_settings {
|
||||
/**
|
||||
* struct st_lsm6dsx_settings - ST IMU sensor settings
|
||||
* @wai: Sensor WhoAmI default value.
|
||||
* @int1_addr: Control Register address for INT1
|
||||
* @int2_addr: Control Register address for INT2
|
||||
* @reset_addr: register address for reset/reboot
|
||||
* @max_fifo_size: Sensor max fifo length in FIFO words.
|
||||
* @id: List of hw id/device name supported by the driver configuration.
|
||||
* @channels: IIO channels supported by the device.
|
||||
* @odr_table: Hw sensors odr table (Hz + val).
|
||||
* @fs_table: Hw sensors gain table (gain + val).
|
||||
* @decimator: List of decimator register info (addr + mask).
|
||||
* @batch: List of FIFO batching register info (addr + mask).
|
||||
* @fifo_ops: Sensor hw FIFO parameters.
|
||||
@ -206,11 +221,20 @@ struct st_lsm6dsx_ext_dev_settings {
|
||||
*/
|
||||
struct st_lsm6dsx_settings {
|
||||
u8 wai;
|
||||
u8 int1_addr;
|
||||
u8 int2_addr;
|
||||
u8 reset_addr;
|
||||
u16 max_fifo_size;
|
||||
struct {
|
||||
enum st_lsm6dsx_hw_id hw_id;
|
||||
const char *name;
|
||||
} id[ST_LSM6DSX_MAX_ID];
|
||||
struct {
|
||||
const struct iio_chan_spec *chan;
|
||||
int len;
|
||||
} channels[2];
|
||||
struct st_lsm6dsx_odr_table_entry odr_table[2];
|
||||
struct st_lsm6dsx_fs_table_entry fs_table[2];
|
||||
struct st_lsm6dsx_reg decimator[ST_LSM6DSX_MAX_ID];
|
||||
struct st_lsm6dsx_reg batch[ST_LSM6DSX_MAX_ID];
|
||||
struct st_lsm6dsx_fifo_ops fifo_ops;
|
||||
@ -314,6 +338,7 @@ int st_lsm6dsx_fifo_setup(struct st_lsm6dsx_hw *hw);
|
||||
int st_lsm6dsx_set_watermark(struct iio_dev *iio_dev, unsigned int val);
|
||||
int st_lsm6dsx_update_watermark(struct st_lsm6dsx_sensor *sensor,
|
||||
u16 watermark);
|
||||
int st_lsm6dsx_update_fifo(struct st_lsm6dsx_sensor *sensor, bool enable);
|
||||
int st_lsm6dsx_flush_fifo(struct st_lsm6dsx_hw *hw);
|
||||
int st_lsm6dsx_set_fifo_mode(struct st_lsm6dsx_hw *hw,
|
||||
enum st_lsm6dsx_fifo_mode fifo_mode);
|
||||
|
@ -2,10 +2,10 @@
|
||||
/*
|
||||
* STMicroelectronics st_lsm6dsx FIFO buffer library driver
|
||||
*
|
||||
* LSM6DS3/LSM6DS3H/LSM6DSL/LSM6DSM/ISM330DLC: The FIFO buffer can be
|
||||
* configured to store data from gyroscope and accelerometer. Samples are
|
||||
* queued without any tag according to a specific pattern based on
|
||||
* 'FIFO data sets' (6 bytes each):
|
||||
* LSM6DS3/LSM6DS3H/LSM6DSL/LSM6DSM/ISM330DLC/LSM6DS3TR-C:
|
||||
* The FIFO buffer can be configured to store data from gyroscope and
|
||||
* accelerometer. Samples are queued without any tag according to a
|
||||
* specific pattern based on 'FIFO data sets' (6 bytes each):
|
||||
* - 1st data set is reserved for gyroscope data
|
||||
* - 2nd data set is reserved for accelerometer data
|
||||
* The FIFO pattern changes depending on the ODRs and decimation factors
|
||||
@ -14,9 +14,10 @@
|
||||
* (e.g. Gx, Gy, Gz, Ax, Ay, Az), then data are repeated depending on the
|
||||
* value of the decimation factor and ODR set for each FIFO data set.
|
||||
*
|
||||
* LSM6DSO/LSM6DSOX/ASM330LHH/LSM6DSR: The FIFO buffer can be configured to
|
||||
* store data from gyroscope and accelerometer. Each sample is queued with
|
||||
* a tag (1B) indicating data source (gyroscope, accelerometer, hw timer).
|
||||
* LSM6DSO/LSM6DSOX/ASM330LHH/LSM6DSR/ISM330DHCX: The FIFO buffer can be
|
||||
* configured to store data from gyroscope and accelerometer. Each sample
|
||||
* is queued with a tag (1B) indicating data source (gyroscope, accelerometer,
|
||||
* hw timer).
|
||||
*
|
||||
* FIFO supported modes:
|
||||
* - BYPASS: FIFO disabled
|
||||
@ -601,9 +602,8 @@ int st_lsm6dsx_flush_fifo(struct st_lsm6dsx_hw *hw)
|
||||
return err;
|
||||
}
|
||||
|
||||
static int st_lsm6dsx_update_fifo(struct iio_dev *iio_dev, bool enable)
|
||||
int st_lsm6dsx_update_fifo(struct st_lsm6dsx_sensor *sensor, bool enable)
|
||||
{
|
||||
struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev);
|
||||
struct st_lsm6dsx_hw *hw = sensor->hw;
|
||||
int err;
|
||||
|
||||
@ -670,17 +670,29 @@ static irqreturn_t st_lsm6dsx_handler_thread(int irq, void *private)
|
||||
count = hw->settings->fifo_ops.read_fifo(hw);
|
||||
mutex_unlock(&hw->fifo_lock);
|
||||
|
||||
return !count ? IRQ_NONE : IRQ_HANDLED;
|
||||
return count ? IRQ_HANDLED : IRQ_NONE;
|
||||
}
|
||||
|
||||
static int st_lsm6dsx_buffer_preenable(struct iio_dev *iio_dev)
|
||||
{
|
||||
return st_lsm6dsx_update_fifo(iio_dev, true);
|
||||
struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev);
|
||||
struct st_lsm6dsx_hw *hw = sensor->hw;
|
||||
|
||||
if (!hw->settings->fifo_ops.update_fifo)
|
||||
return -ENOTSUPP;
|
||||
|
||||
return hw->settings->fifo_ops.update_fifo(sensor, true);
|
||||
}
|
||||
|
||||
static int st_lsm6dsx_buffer_postdisable(struct iio_dev *iio_dev)
|
||||
{
|
||||
return st_lsm6dsx_update_fifo(iio_dev, false);
|
||||
struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev);
|
||||
struct st_lsm6dsx_hw *hw = sensor->hw;
|
||||
|
||||
if (!hw->settings->fifo_ops.update_fifo)
|
||||
return -ENOTSUPP;
|
||||
|
||||
return hw->settings->fifo_ops.update_fifo(sensor, false);
|
||||
}
|
||||
|
||||
static const struct iio_buffer_setup_ops st_lsm6dsx_buffer_ops = {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user