forked from Minki/linux
Staging / IIO patches for 5.5-rc1
Here is the big staging and iio set of patches for the 5.5-rc1 release. It's the usual huge collection of cleanup patches all over the drivers/staging/ area, along with a new staging driver, and a bunch of new IIO drivers as well. Full details are in the shortlog, but all of these have been in linux-next for a long time with no reported issues. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCXd6lVQ8cZ3JlZ0Brcm9h aC5jb20ACgkQMUfUDdst+ylFnwCgyvZ62uUyQTRey0zvNTe3I4fY9L4AnAnrz3ZC U6ZA2+Uj3O6qhAr5frRu =uv8S -----END PGP SIGNATURE----- Merge tag 'staging-5.5-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging Pull staging / iio updates from Greg KH: "Here is the big staging and iio set of patches for the 5.5-rc1 release. It's the usual huge collection of cleanup patches all over the drivers/staging/ area, along with a new staging driver, and a bunch of new IIO drivers as well. Full details are in the shortlog, but all of these have been in linux-next for a long time with no reported issues" * tag 'staging-5.5-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging: (548 commits) staging: vchiq: Have vchiq_dump_* functions return an error code staging: vchiq: Refactor indentation in vchiq_dump_* functions staging: fwserial: Fix Kconfig indentation (seven spaces) staging: vchiq_dump: Replace min with min_t staging: vchiq: Fix block comment format in vchiq_dump() staging: octeon: indent with tabs instead of spaces staging: comedi: usbduxfast: usbduxfast_ai_cmdtest rounding error staging: most: core: remove sysfs attr remove_link staging: vc04: Fix Kconfig indentation staging: pi433: Fix Kconfig indentation staging: nvec: Fix Kconfig indentation staging: most: Fix Kconfig indentation staging: fwserial: Fix Kconfig indentation staging: fbtft: Fix Kconfig indentation fbtft: Drop OF dependency fbtft: Make use of device property API fbtft: Drop useless #ifdef CONFIG_OF and dead code fbtft: Describe function parameters in kernel-doc fbtft: Make sure string is NULL terminated staging: rtl8723bs: remove set but not used variable 'change', 'pos' ...
This commit is contained in:
commit
0dd09bc02c
@ -753,6 +753,8 @@ What: /sys/.../events/in_illuminance0_thresh_falling_value
|
||||
what: /sys/.../events/in_illuminance0_thresh_rising_value
|
||||
what: /sys/.../events/in_proximity0_thresh_falling_value
|
||||
what: /sys/.../events/in_proximity0_thresh_rising_value
|
||||
What: /sys/.../events/in_illuminance_thresh_rising_value
|
||||
What: /sys/.../events/in_illuminance_thresh_falling_value
|
||||
KernelVersion: 2.6.37
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
@ -972,6 +974,7 @@ What: /sys/.../events/in_activity_jogging_thresh_rising_period
|
||||
What: /sys/.../events/in_activity_jogging_thresh_falling_period
|
||||
What: /sys/.../events/in_activity_running_thresh_rising_period
|
||||
What: /sys/.../events/in_activity_running_thresh_falling_period
|
||||
What: /sys/.../events/in_illuminance_thresh_either_period
|
||||
KernelVersion: 2.6.37
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
@ -1715,3 +1718,11 @@ Description:
|
||||
Mass concentration reading of particulate matter in ug / m3.
|
||||
pmX consists of particles with aerodynamic diameter less or
|
||||
equal to X micrometers.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/events/in_illuminance_period_available
|
||||
Date: November 2019
|
||||
KernelVersion: 5.4
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
List of valid periods (in seconds) for which the light intensity
|
||||
must be above the threshold level before interrupt is asserted.
|
||||
|
39
Documentation/ABI/testing/sysfs-bus-iio-adc-ad7192
Normal file
39
Documentation/ABI/testing/sysfs-bus-iio-adc-ad7192
Normal file
@ -0,0 +1,39 @@
|
||||
What: /sys/bus/iio/devices/iio:deviceX/ac_excitation_en
|
||||
KernelVersion:
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Reading gives the state of AC excitation.
|
||||
Writing '1' enables AC excitation.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/bridge_switch_en
|
||||
KernelVersion:
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
This bridge switch is used to disconnect it when there is a
|
||||
need to minimize the system current consumption.
|
||||
Reading gives the state of the bridge switch.
|
||||
Writing '1' enables the bridge switch.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_voltagex_sys_calibration
|
||||
KernelVersion:
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Initiates the system calibration procedure. This is done on a
|
||||
single channel at a time. Write '1' to start the calibration.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_voltagex_sys_calibration_mode_available
|
||||
KernelVersion:
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Reading returns a list with the possible calibration modes.
|
||||
There are two available options:
|
||||
"zero_scale" - calibrate to zero scale
|
||||
"full_scale" - calibrate to full scale
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_voltagex_sys_calibration_mode
|
||||
KernelVersion:
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Sets up the calibration mode used in the system calibration
|
||||
procedure. Reading returns the current calibration mode.
|
||||
Writing sets the system calibration mode.
|
50
Documentation/devicetree/bindings/counter/ti-eqep.yaml
Normal file
50
Documentation/devicetree/bindings/counter/ti-eqep.yaml
Normal file
@ -0,0 +1,50 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/counter/ti-eqep.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Texas Instruments Enhanced Quadrature Encoder Pulse (eQEP) Module
|
||||
|
||||
maintainers:
|
||||
- David Lechner <david@lechnology.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: ti,am3352-eqep
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
description: The eQEP event interrupt
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
description: The clock that determines the SYSCLKOUT rate for the eQEP
|
||||
peripheral.
|
||||
maxItems: 1
|
||||
|
||||
clock-names:
|
||||
const: sysclkout
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
- clock-names
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
eqep0: counter@180 {
|
||||
compatible = "ti,am3352-eqep";
|
||||
reg = <0x180 0x80>;
|
||||
clocks = <&l4ls_gclk>;
|
||||
clock-names = "sysclkout";
|
||||
interrupts = <79>;
|
||||
};
|
||||
|
||||
...
|
104
Documentation/devicetree/bindings/iio/adc/adi,ad7292.yaml
Normal file
104
Documentation/devicetree/bindings/iio/adc/adi,ad7292.yaml
Normal file
@ -0,0 +1,104 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/adc/adi,ad7292.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Analog Devices AD7292 10-Bit Monitor and Control System
|
||||
|
||||
maintainers:
|
||||
- Marcelo Schmitt <marcelo.schmitt1@gmail.com>
|
||||
|
||||
description: |
|
||||
Analog Devices AD7292 10-Bit Monitor and Control System with ADC, DACs,
|
||||
Temperature Sensor, and GPIOs
|
||||
|
||||
Specifications about the part can be found at:
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/ad7292.pdf
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- adi,ad7292
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
vref-supply:
|
||||
description: |
|
||||
The regulator supply for ADC and DAC reference voltage.
|
||||
|
||||
spi-cpha: true
|
||||
|
||||
'#address-cells':
|
||||
const: 1
|
||||
|
||||
'#size-cells':
|
||||
const: 0
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- spi-cpha
|
||||
|
||||
patternProperties:
|
||||
"^channel@[0-7]$":
|
||||
type: object
|
||||
description: |
|
||||
Represents the external channels which are connected to the ADC.
|
||||
See Documentation/devicetree/bindings/iio/adc/adc.txt.
|
||||
|
||||
properties:
|
||||
reg:
|
||||
description: |
|
||||
The channel number. It can have up to 8 channels numbered from 0 to 7.
|
||||
items:
|
||||
maximum: 7
|
||||
|
||||
diff-channels:
|
||||
description: see Documentation/devicetree/bindings/iio/adc/adc.txt
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- reg
|
||||
|
||||
examples:
|
||||
- |
|
||||
spi {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
ad7292: adc@0 {
|
||||
compatible = "adi,ad7292";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <25000000>;
|
||||
vref-supply = <&adc_vref>;
|
||||
spi-cpha;
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
channel@0 {
|
||||
reg = <0>;
|
||||
diff-channels = <0 1>;
|
||||
};
|
||||
channel@2 {
|
||||
reg = <2>;
|
||||
};
|
||||
channel@3 {
|
||||
reg = <3>;
|
||||
};
|
||||
channel@4 {
|
||||
reg = <4>;
|
||||
};
|
||||
channel@5 {
|
||||
reg = <5>;
|
||||
};
|
||||
channel@6 {
|
||||
reg = <6>;
|
||||
};
|
||||
channel@7 {
|
||||
reg = <7>;
|
||||
};
|
||||
};
|
||||
};
|
@ -5,6 +5,7 @@ Required properties:
|
||||
- compatible: Should be one of:
|
||||
* ingenic,jz4725b-adc
|
||||
* ingenic,jz4740-adc
|
||||
* ingenic,jz4770-adc
|
||||
- reg: ADC controller registers location and length.
|
||||
- clocks: phandle to the SoC's ADC clock.
|
||||
- clock-names: Must be set to "adc".
|
||||
|
@ -1,20 +0,0 @@
|
||||
* Maxim 1027/1029/1031 Analog to Digital Converter (ADC)
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "maxim,max1027" or "maxim,max1029" or "maxim,max1031"
|
||||
- reg: SPI chip select number for the device
|
||||
- interrupts: IRQ line for the ADC
|
||||
see: Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
|
||||
|
||||
Recommended properties:
|
||||
- spi-max-frequency: Definition as per
|
||||
Documentation/devicetree/bindings/spi/spi-bus.txt
|
||||
|
||||
Example:
|
||||
adc@0 {
|
||||
compatible = "maxim,max1027";
|
||||
reg = <0>;
|
||||
interrupt-parent = <&gpio5>;
|
||||
interrupts = <15 IRQ_TYPE_EDGE_RISING>;
|
||||
spi-max-frequency = <1000000>;
|
||||
};
|
@ -1,30 +0,0 @@
|
||||
* Microchip MCP3911 Dual channel analog front end (ADC)
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "microchip,mcp3911"
|
||||
- reg: SPI chip select number for the device
|
||||
|
||||
Recommended properties:
|
||||
- spi-max-frequency: Definition as per
|
||||
Documentation/devicetree/bindings/spi/spi-bus.txt.
|
||||
Max frequency for this chip is 20MHz.
|
||||
|
||||
Optional properties:
|
||||
- clocks: Phandle and clock identifier for sampling clock
|
||||
- interrupt-parent: Phandle to the parent interrupt controller
|
||||
- interrupts: IRQ line for the ADC
|
||||
- microchip,device-addr: Device address when multiple MCP3911 chips are present on the
|
||||
same SPI bus. Valid values are 0-3. Defaults to 0.
|
||||
- vref-supply: Phandle to the external reference voltage supply.
|
||||
|
||||
Example:
|
||||
adc@0 {
|
||||
compatible = "microchip,mcp3911";
|
||||
reg = <0>;
|
||||
interrupt-parent = <&gpio5>;
|
||||
interrupts = <15 IRQ_TYPE_EDGE_RISING>;
|
||||
spi-max-frequency = <20000000>;
|
||||
microchip,device-addr = <0>;
|
||||
vref-supply = <&vref_reg>;
|
||||
clocks = <&xtal>;
|
||||
};
|
@ -0,0 +1,71 @@
|
||||
# SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
|
||||
# Copyright 2019 Marcus Folkesson <marcus.folkesson@gmail.com>
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: "http://devicetree.org/schemas/bindings/iio/adc/microchip,mcp3911.yaml#"
|
||||
$schema: "http://devicetree.org/meta-schemas/core.yaml#"
|
||||
|
||||
title: Microchip MCP3911 Dual channel analog front end (ADC)
|
||||
|
||||
maintainers:
|
||||
- Marcus Folkesson <marcus.folkesson@gmail.com>
|
||||
- Kent Gustavsson <nedo80@gmail.com>
|
||||
|
||||
description: |
|
||||
Bindings for the Microchip MCP3911 Dual channel ADC device. Datasheet can be
|
||||
found here: https://ww1.microchip.com/downloads/en/DeviceDoc/20002286C.pdf
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- microchip,mcp3911
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
spi-max-frequency:
|
||||
maximum: 20000000
|
||||
|
||||
clocks:
|
||||
description: |
|
||||
Phandle and clock identifier for external sampling clock.
|
||||
If not specified, the internal crystal oscillator will be used.
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
description: IRQ line of the ADC
|
||||
maxItems: 1
|
||||
|
||||
microchip,device-addr:
|
||||
description: Device address when multiple MCP3911 chips are present on the same SPI bus.
|
||||
allOf:
|
||||
- $ref: /schemas/types.yaml#/definitions/uint32
|
||||
- enum: [0, 1, 2, 3]
|
||||
- default: 0
|
||||
|
||||
vref-supply:
|
||||
description: |
|
||||
Phandle to the external reference voltage supply.
|
||||
If not specified, the internal voltage reference (1.2V) will be used.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
examples:
|
||||
- |
|
||||
spi {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
adc@0 {
|
||||
compatible = "microchip,mcp3911";
|
||||
reg = <0>;
|
||||
interrupt-parent = <&gpio5>;
|
||||
interrupts = <15 2>;
|
||||
spi-max-frequency = <20000000>;
|
||||
microchip,device-addr = <0>;
|
||||
vref-supply = <&vref_reg>;
|
||||
clocks = <&xtal>;
|
||||
};
|
||||
};
|
@ -53,6 +53,8 @@ Optional properties:
|
||||
analog input switches on stm32mp1.
|
||||
- st,syscfg: Phandle to system configuration controller. It can be used to
|
||||
control the analog circuitry on stm32mp1.
|
||||
- st,max-clk-rate-hz: Allow to specify desired max clock rate used by analog
|
||||
circuitry.
|
||||
|
||||
Contents of a stm32 adc child node:
|
||||
-----------------------------------
|
||||
|
49
Documentation/devicetree/bindings/iio/dac/lltc,ltc1660.yaml
Normal file
49
Documentation/devicetree/bindings/iio/dac/lltc,ltc1660.yaml
Normal file
@ -0,0 +1,49 @@
|
||||
# SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
|
||||
# Copyright 2019 Marcus Folkesson <marcus.folkesson@gmail.com>
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: "http://devicetree.org/schemas/bindings/iio/dac/lltc,ltc1660.yaml#"
|
||||
$schema: "http://devicetree.org/meta-schemas/core.yaml#"
|
||||
|
||||
title: Linear Technology Micropower octal 8-Bit and 10-Bit DACs
|
||||
|
||||
maintainers:
|
||||
- Marcus Folkesson <marcus.folkesson@gmail.com>
|
||||
|
||||
description: |
|
||||
Bindings for the Linear Technology Micropower octal 8-Bit and 10-Bit DAC.
|
||||
Datasheet can be found here: https://www.analog.com/media/en/technical-documentation/data-sheets/166560fa.pdf
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- lltc,ltc1660
|
||||
- lltc,ltc1665
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
spi-max-frequency:
|
||||
maximum: 5000000
|
||||
|
||||
vref-supply:
|
||||
description: Phandle to the external reference voltage supply.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- vref-supply
|
||||
|
||||
examples:
|
||||
- |
|
||||
spi {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
dac@0 {
|
||||
compatible = "lltc,ltc1660";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <5000000>;
|
||||
vref-supply = <&vref_reg>;
|
||||
};
|
||||
};
|
@ -1,21 +0,0 @@
|
||||
* Linear Technology Micropower octal 8-Bit and 10-Bit DACs
|
||||
|
||||
Required properties:
|
||||
- compatible: Must be one of the following:
|
||||
"lltc,ltc1660"
|
||||
"lltc,ltc1665"
|
||||
- reg: SPI chip select number for the device
|
||||
- vref-supply: Phandle to the voltage reference supply
|
||||
|
||||
Recommended properties:
|
||||
- spi-max-frequency: Definition as per
|
||||
Documentation/devicetree/bindings/spi/spi-bus.txt.
|
||||
Max frequency for this chip is 5 MHz.
|
||||
|
||||
Example:
|
||||
dac@0 {
|
||||
compatible = "lltc,ltc1660";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <5000000>;
|
||||
vref-supply = <&vref_reg>;
|
||||
};
|
@ -18,12 +18,17 @@ Required properties:
|
||||
with a single IIO output and 1 for nodes with multiple
|
||||
IIO outputs.
|
||||
|
||||
Optional properties:
|
||||
label: A symbolic name for the device.
|
||||
|
||||
|
||||
Example for a simple configuration with no trigger:
|
||||
|
||||
adc: voltage-sensor@35 {
|
||||
compatible = "maxim,max1139";
|
||||
reg = <0x35>;
|
||||
#io-channel-cells = <1>;
|
||||
label = "voltage_feedback_group1";
|
||||
};
|
||||
|
||||
Example for a configuration with trigger:
|
||||
|
@ -21,6 +21,7 @@ Required properties:
|
||||
bindings.
|
||||
|
||||
Optional properties:
|
||||
- vdd-supply: regulator phandle for VDD supply
|
||||
- vddio-supply: regulator phandle for VDDIO supply
|
||||
- mount-matrix: an optional 3x3 mounting rotation matrix
|
||||
- i2c-gate node. These devices also support an auxiliary i2c bus. This is
|
||||
|
76
Documentation/devicetree/bindings/iio/imu/nxp,fxos8700.yaml
Normal file
76
Documentation/devicetree/bindings/iio/imu/nxp,fxos8700.yaml
Normal file
@ -0,0 +1,76 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/imu/nxp,fxos8700.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Freescale FXOS8700 Inertial Measurement Unit
|
||||
|
||||
maintainers:
|
||||
- Robert Jones <rjones@gateworks.com>
|
||||
|
||||
description: |
|
||||
Accelerometer and magnetometer combo device with an i2c and SPI interface.
|
||||
https://www.nxp.com/products/sensors/motion-sensors/6-axis/digital-motion-sensor-3d-accelerometer-2g-4g-8g-plus-3d-magnetometer:FXOS8700CQ
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- nxp,fxos8700
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
interrupt-names:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
items:
|
||||
enum:
|
||||
- INT1
|
||||
- INT2
|
||||
|
||||
drive-open-drain:
|
||||
type: boolean
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
i2c0 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
fxos8700@1e {
|
||||
compatible = "nxp,fxos8700";
|
||||
reg = <0x1e>;
|
||||
|
||||
interrupt-parent = <&gpio2>;
|
||||
interrupts = <7 IRQ_TYPE_EDGE_RISING>;
|
||||
interrupt-names = "INT1";
|
||||
};
|
||||
};
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
spi0 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
fxos8700@0 {
|
||||
compatible = "nxp,fxos8700";
|
||||
reg = <0>;
|
||||
|
||||
spi-max-frequency = <1000000>;
|
||||
interrupt-parent = <&gpio1>;
|
||||
interrupts = <7 IRQ_TYPE_EDGE_RISING>;
|
||||
interrupt-names = "INT2";
|
||||
};
|
||||
};
|
@ -14,6 +14,8 @@ Required properties:
|
||||
"st,lsm6ds3tr-c"
|
||||
"st,ism330dhcx"
|
||||
"st,lsm9ds1-imu"
|
||||
"st,lsm6ds0"
|
||||
"st,lsm6dsrx"
|
||||
- reg: i2c address of the sensor / spi cs line
|
||||
|
||||
Optional properties:
|
||||
@ -31,6 +33,7 @@ Optional properties:
|
||||
- interrupts: interrupt mapping for IRQ. It should be configured with
|
||||
flags IRQ_TYPE_LEVEL_HIGH, IRQ_TYPE_EDGE_RISING, IRQ_TYPE_LEVEL_LOW or
|
||||
IRQ_TYPE_EDGE_FALLING.
|
||||
- wakeup-source: Enables wake up of host system on event.
|
||||
|
||||
Refer to interrupt-controller/interrupts.txt for generic interrupt
|
||||
client node bindings.
|
||||
|
47
Documentation/devicetree/bindings/iio/light/adux1020.yaml
Normal file
47
Documentation/devicetree/bindings/iio/light/adux1020.yaml
Normal file
@ -0,0 +1,47 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/light/adux1020.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Analog Devices ADUX1020 Photometric sensor
|
||||
|
||||
maintainers:
|
||||
- Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
|
||||
|
||||
description: |
|
||||
Photometric sensor over an i2c interface.
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/ADUX1020.pdf
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- adi,adux1020
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
i2c {
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
adux1020@64 {
|
||||
compatible = "adi,adux1020";
|
||||
reg = <0x64>;
|
||||
interrupt-parent = <&msmgpio>;
|
||||
interrupts = <24 IRQ_TYPE_LEVEL_HIGH>;
|
||||
};
|
||||
};
|
||||
...
|
@ -1,18 +0,0 @@
|
||||
ROHM BH1750 - ALS, Ambient light sensor
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: Must be one of:
|
||||
"rohm,bh1710"
|
||||
"rohm,bh1715"
|
||||
"rohm,bh1721"
|
||||
"rohm,bh1750"
|
||||
"rohm,bh1751"
|
||||
- reg: the I2C address of the sensor
|
||||
|
||||
Example:
|
||||
|
||||
light-sensor@23 {
|
||||
compatible = "rohm,bh1750";
|
||||
reg = <0x23>;
|
||||
};
|
43
Documentation/devicetree/bindings/iio/light/bh1750.yaml
Normal file
43
Documentation/devicetree/bindings/iio/light/bh1750.yaml
Normal file
@ -0,0 +1,43 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/light/bh1750.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: ROHM BH1750 ambient light sensor
|
||||
|
||||
maintainers:
|
||||
- Tomasz Duszynski <tduszyns@gmail.com>
|
||||
|
||||
description: |
|
||||
Ambient light sensor with an i2c interface.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- rohm,bh1710
|
||||
- rohm,bh1715
|
||||
- rohm,bh1721
|
||||
- rohm,bh1750
|
||||
- rohm,bh1751
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
examples:
|
||||
- |
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
light-sensor@23 {
|
||||
compatible = "rohm,bh1750";
|
||||
reg = <0x23>;
|
||||
};
|
||||
};
|
||||
|
||||
...
|
62
Documentation/devicetree/bindings/iio/light/veml6030.yaml
Normal file
62
Documentation/devicetree/bindings/iio/light/veml6030.yaml
Normal file
@ -0,0 +1,62 @@
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/light/veml6030.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: VEML6030 Ambient Light Sensor (ALS)
|
||||
|
||||
maintainers:
|
||||
- Rishi Gupta <gupt21@gmail.com>
|
||||
|
||||
description: |
|
||||
Bindings for the ambient light sensor veml6030 from Vishay
|
||||
Semiconductors over an i2c interface.
|
||||
|
||||
Irrespective of whether interrupt is used or not, application
|
||||
can get the ALS and White channel reading from IIO raw interface.
|
||||
|
||||
If the interrupts are used, application will receive an IIO event
|
||||
whenever configured threshold is crossed.
|
||||
|
||||
Specifications about the sensor can be found at:
|
||||
https://www.vishay.com/docs/84366/veml6030.pdf
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- vishay,veml6030
|
||||
|
||||
reg:
|
||||
description:
|
||||
I2C address of the device.
|
||||
enum:
|
||||
- 0x10 # ADDR pin pulled down
|
||||
- 0x48 # ADDR pin pulled up
|
||||
|
||||
interrupts:
|
||||
description:
|
||||
interrupt mapping for IRQ. Configure with IRQ_TYPE_LEVEL_LOW.
|
||||
Refer to interrupt-controller/interrupts.txt for generic
|
||||
interrupt client node bindings.
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
light-sensor@10 {
|
||||
compatible = "vishay,veml6030";
|
||||
reg = <0x10>;
|
||||
interrupts = <12 IRQ_TYPE_LEVEL_LOW>;
|
||||
};
|
||||
};
|
||||
...
|
@ -1,29 +0,0 @@
|
||||
* MaxBotix I2CXL-MaxSonar ultrasonic distance sensor of type mb1202,
|
||||
mb1212, mb1222, mb1232, mb1242, mb7040 or mb7137 using the i2c interface
|
||||
for ranging
|
||||
|
||||
Required properties:
|
||||
- compatible: "maxbotix,mb1202",
|
||||
"maxbotix,mb1212",
|
||||
"maxbotix,mb1222",
|
||||
"maxbotix,mb1232",
|
||||
"maxbotix,mb1242",
|
||||
"maxbotix,mb7040" or
|
||||
"maxbotix,mb7137"
|
||||
|
||||
- reg: i2c address of the device, see also i2c/i2c.txt
|
||||
|
||||
Optional properties:
|
||||
- interrupts: Interrupt used to announce the preceding reading
|
||||
request has finished and that data is available.
|
||||
If no interrupt is specified the device driver
|
||||
falls back to wait a fixed amount of time until
|
||||
data can be retrieved.
|
||||
|
||||
Example:
|
||||
proximity@70 {
|
||||
compatible = "maxbotix,mb1232";
|
||||
reg = <0x70>;
|
||||
interrupt-parent = <&gpio2>;
|
||||
interrupts = <2 IRQ_TYPE_EDGE_FALLING>;
|
||||
};
|
@ -0,0 +1,60 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/proximity/maxbotix,mb1232.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: MaxBotix I2CXL-MaxSonar ultrasonic distance sensor
|
||||
|
||||
maintainers:
|
||||
- Andreas Klinger <ak@it-klinger.de>
|
||||
|
||||
description: |
|
||||
MaxBotix I2CXL-MaxSonar ultrasonic distance sensor of type mb1202,
|
||||
mb1212, mb1222, mb1232, mb1242, mb7040 or mb7137 using the i2c interface
|
||||
for ranging
|
||||
|
||||
Specifications about the devices can be found at:
|
||||
https://www.maxbotix.com/documents/I2CXL-MaxSonar-EZ_Datasheet.pdf
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- maxbotix,mb1202
|
||||
- maxbotix,mb1212
|
||||
- maxbotix,mb1222
|
||||
- maxbotix,mb1232
|
||||
- maxbotix,mb1242
|
||||
- maxbotix,mb7040
|
||||
- maxbotix,mb7137
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
description:
|
||||
Interrupt used to announce the preceding reading request has finished
|
||||
and that data is available. If no interrupt is specified the device
|
||||
driver falls back to wait a fixed amount of time until data can be
|
||||
retrieved.
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
proximity@70 {
|
||||
compatible = "maxbotix,mb1232";
|
||||
reg = <0x70>;
|
||||
interrupt-parent = <&gpio2>;
|
||||
interrupts = <2 IRQ_TYPE_EDGE_FALLING>;
|
||||
};
|
||||
};
|
@ -0,0 +1,480 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/temperature/adi,ltc2983.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Analog Devices LTC2983 Multi-sensor Temperature system
|
||||
|
||||
maintainers:
|
||||
- Nuno Sá <nuno.sa@analog.com>
|
||||
|
||||
description: |
|
||||
Analog Devices LTC2983 Multi-Sensor Digital Temperature Measurement System
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/2983fc.pdf
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- adi,ltc2983
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
adi,mux-delay-config-us:
|
||||
description:
|
||||
The LTC2983 performs 2 or 3 internal conversion cycles per temperature
|
||||
result. Each conversion cycle is performed with different excitation and
|
||||
input multiplexer configurations. Prior to each conversion, these
|
||||
excitation circuits and input switch configurations are changed and an
|
||||
internal 1ms delay ensures settling prior to the conversion cycle in most
|
||||
cases. An extra delay can be configured using this property. The value is
|
||||
rounded to nearest 100us.
|
||||
maximum: 255
|
||||
|
||||
adi,filter-notch-freq:
|
||||
description:
|
||||
Set's the default setting of the digital filter. The default is
|
||||
simultaneous 50/60Hz rejection.
|
||||
0 - 50/60Hz rejection
|
||||
1 - 60Hz rejection
|
||||
2 - 50Hz rejection
|
||||
allOf:
|
||||
- $ref: /schemas/types.yaml#/definitions/uint32
|
||||
- minimum: 0
|
||||
maximum: 2
|
||||
|
||||
'#address-cells':
|
||||
const: 1
|
||||
|
||||
'#size-cells':
|
||||
const: 0
|
||||
|
||||
patternProperties:
|
||||
"@([1-9]|1[0-9]|20)$":
|
||||
type: object
|
||||
|
||||
properties:
|
||||
reg:
|
||||
description:
|
||||
The channel number. It can be connected to one of the 20 channels of
|
||||
the device.
|
||||
minimum: 1
|
||||
maximum: 20
|
||||
|
||||
adi,sensor-type:
|
||||
description: Identifies the type of sensor connected to the device.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
|
||||
required:
|
||||
- reg
|
||||
- adi,sensor-type
|
||||
|
||||
"^thermocouple@":
|
||||
type: object
|
||||
description:
|
||||
Represents a thermocouple sensor which is connected to one of the device
|
||||
channels.
|
||||
|
||||
properties:
|
||||
adi,sensor-type:
|
||||
description: |
|
||||
1 - Type J Thermocouple
|
||||
2 - Type K Thermocouple
|
||||
3 - Type E Thermocouple
|
||||
4 - Type N Thermocouple
|
||||
5 - Type R Thermocouple
|
||||
6 - Type S Thermocouple
|
||||
7 - Type T Thermocouple
|
||||
8 - Type B Thermocouple
|
||||
9 - Custom Thermocouple
|
||||
allOf:
|
||||
- $ref: /schemas/types.yaml#/definitions/uint32
|
||||
minimum: 1
|
||||
maximum: 9
|
||||
|
||||
adi,single-ended:
|
||||
description:
|
||||
Boolean property which set's the thermocouple as single-ended.
|
||||
type: boolean
|
||||
|
||||
adi,sensor-oc-current-microamp:
|
||||
description:
|
||||
This property set's the pulsed current value applied during
|
||||
open-circuit detect.
|
||||
enum: [10, 100, 500, 1000]
|
||||
|
||||
adi,cold-junction-handle:
|
||||
description:
|
||||
Phandle which points to a sensor object responsible for measuring
|
||||
the thermocouple cold junction temperature.
|
||||
$ref: "/schemas/types.yaml#/definitions/phandle"
|
||||
|
||||
adi,custom-thermocouple:
|
||||
description:
|
||||
This is a table, where each entry should be a pair of
|
||||
voltage(mv)-temperature(K). The entries must be given in nv and uK
|
||||
so that, the original values must be multiplied by 1000000. For
|
||||
more details look at table 69 and 70.
|
||||
Note should be signed, but dtc doesn't currently maintain the
|
||||
sign.
|
||||
allOf:
|
||||
- $ref: /schemas/types.yaml#/definitions/uint64-matrix
|
||||
items:
|
||||
minItems: 3
|
||||
maxItems: 64
|
||||
items:
|
||||
minItems: 2
|
||||
maxItems: 2
|
||||
|
||||
"^diode@":
|
||||
type: object
|
||||
description:
|
||||
Represents a diode sensor which is connected to one of the device
|
||||
channels.
|
||||
|
||||
properties:
|
||||
adi,sensor-type:
|
||||
description: Identifies the sensor as a diode.
|
||||
allOf:
|
||||
- $ref: /schemas/types.yaml#/definitions/uint32
|
||||
const: 28
|
||||
|
||||
adi,single-ended:
|
||||
description: Boolean property which set's the diode as single-ended.
|
||||
type: boolean
|
||||
|
||||
adi,three-conversion-cycles:
|
||||
description:
|
||||
Boolean property which set's three conversion cycles removing
|
||||
parasitic resistance effects between the LTC2983 and the diode.
|
||||
type: boolean
|
||||
|
||||
adi,average-on:
|
||||
description:
|
||||
Boolean property which enables a running average of the diode
|
||||
temperature reading. This reduces the noise when the diode is used
|
||||
as a cold junction temperature element on an isothermal block
|
||||
where temperatures change slowly.
|
||||
type: boolean
|
||||
|
||||
adi,excitation-current-microamp:
|
||||
description:
|
||||
This property controls the magnitude of the excitation current
|
||||
applied to the diode. Depending on the number of conversions
|
||||
cycles, this property will assume different predefined values on
|
||||
each cycle. Just set the value of the first cycle (1l).
|
||||
enum: [10, 20, 40, 80]
|
||||
|
||||
adi,ideal-factor-value:
|
||||
description:
|
||||
This property sets the diode ideality factor. The real value must
|
||||
be multiplied by 1000000 to remove the fractional part. For more
|
||||
information look at table 20 of the datasheet.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
|
||||
"^rtd@":
|
||||
type: object
|
||||
description:
|
||||
Represents a rtd sensor which is connected to one of the device channels.
|
||||
|
||||
properties:
|
||||
reg:
|
||||
minimum: 2
|
||||
maximum: 20
|
||||
|
||||
adi,sensor-type:
|
||||
description: |
|
||||
10 - RTD PT-10
|
||||
11 - RTD PT-50
|
||||
12 - RTD PT-100
|
||||
13 - RTD PT-200
|
||||
14 - RTD PT-500
|
||||
15 - RTD PT-1000
|
||||
16 - RTD PT-1000 (0.00375)
|
||||
17 - RTD NI-120
|
||||
18 - RTD Custom
|
||||
allOf:
|
||||
- $ref: /schemas/types.yaml#/definitions/uint32
|
||||
minimum: 10
|
||||
maximum: 18
|
||||
|
||||
adi,rsense-handle:
|
||||
description:
|
||||
Phandle pointing to a rsense object associated with this RTD.
|
||||
$ref: "/schemas/types.yaml#/definitions/phandle"
|
||||
|
||||
adi,number-of-wires:
|
||||
description:
|
||||
Identifies the number of wires used by the RTD. Setting this
|
||||
property to 5 means 4 wires with Kelvin Rsense.
|
||||
allOf:
|
||||
- $ref: /schemas/types.yaml#/definitions/uint32
|
||||
- enum: [2, 3, 4, 5]
|
||||
|
||||
adi,rsense-share:
|
||||
description:
|
||||
Boolean property which enables Rsense sharing, where one sense
|
||||
resistor is used for multiple 2-, 3-, and/or 4-wire RTDs.
|
||||
type: boolean
|
||||
|
||||
adi,current-rotate:
|
||||
description:
|
||||
Boolean property which enables excitation current rotation to
|
||||
automatically remove parasitic thermocouple effects. Note that
|
||||
this property is not allowed for 2- and 3-wire RTDs.
|
||||
type: boolean
|
||||
|
||||
adi,excitation-current-microamp:
|
||||
description:
|
||||
This property controls the magnitude of the excitation current
|
||||
applied to the RTD.
|
||||
enum: [5, 10, 25, 50, 100, 250, 500, 1000]
|
||||
|
||||
adi,rtd-curve:
|
||||
description:
|
||||
This property set the RTD curve used and the corresponding
|
||||
Callendar-VanDusen constants. Look at table 30 of the datasheet.
|
||||
allOf:
|
||||
- $ref: /schemas/types.yaml#/definitions/uint32
|
||||
- minimum: 0
|
||||
maximum: 3
|
||||
|
||||
adi,custom-rtd:
|
||||
description:
|
||||
This is a table, where each entry should be a pair of
|
||||
resistance(ohm)-temperature(K). The entries added here are in uohm
|
||||
and uK. For more details values look at table 74 and 75.
|
||||
allOf:
|
||||
- $ref: /schemas/types.yaml#/definitions/uint64-matrix
|
||||
items:
|
||||
minItems: 3
|
||||
maxItems: 64
|
||||
items:
|
||||
minItems: 2
|
||||
maxItems: 2
|
||||
|
||||
required:
|
||||
- adi,rsense-handle
|
||||
|
||||
dependencies:
|
||||
adi,current-rotate: [ adi,rsense-share ]
|
||||
|
||||
"^thermistor@":
|
||||
type: object
|
||||
description:
|
||||
Represents a thermistor sensor which is connected to one of the device
|
||||
channels.
|
||||
|
||||
properties:
|
||||
adi,sensor-type:
|
||||
description:
|
||||
19 - Thermistor 44004/44033 2.252kohm at 25°C
|
||||
20 - Thermistor 44005/44030 3kohm at 25°C
|
||||
21 - Thermistor 44007/44034 5kohm at 25°C
|
||||
22 - Thermistor 44006/44031 10kohm at 25°C
|
||||
23 - Thermistor 44008/44032 30kohm at 25°C
|
||||
24 - Thermistor YSI 400 2.252kohm at 25°C
|
||||
25 - Thermistor Spectrum 1003k 1kohm
|
||||
26 - Thermistor Custom Steinhart-Hart
|
||||
27 - Custom Thermistor
|
||||
allOf:
|
||||
- $ref: /schemas/types.yaml#/definitions/uint32
|
||||
minimum: 19
|
||||
maximum: 27
|
||||
|
||||
adi,rsense-handle:
|
||||
description:
|
||||
Phandle pointing to a rsense object associated with this
|
||||
thermistor.
|
||||
$ref: "/schemas/types.yaml#/definitions/phandle"
|
||||
|
||||
adi,single-ended:
|
||||
description:
|
||||
Boolean property which set's the thermistor as single-ended.
|
||||
type: boolean
|
||||
|
||||
adi,rsense-share:
|
||||
description:
|
||||
Boolean property which enables Rsense sharing, where one sense
|
||||
resistor is used for multiple thermistors. Note that this property
|
||||
is ignored if adi,single-ended is set.
|
||||
type: boolean
|
||||
|
||||
adi,current-rotate:
|
||||
description:
|
||||
Boolean property which enables excitation current rotation to
|
||||
automatically remove parasitic thermocouple effects.
|
||||
type: boolean
|
||||
|
||||
adi,excitation-current-nanoamp:
|
||||
description:
|
||||
This property controls the magnitude of the excitation current
|
||||
applied to the thermistor. Value 0 set's the sensor in auto-range
|
||||
mode.
|
||||
allOf:
|
||||
- $ref: /schemas/types.yaml#/definitions/uint32
|
||||
- enum: [0, 250, 500, 1000, 5000, 10000, 25000, 50000, 100000,
|
||||
250000, 500000, 1000000]
|
||||
|
||||
adi,custom-thermistor:
|
||||
description:
|
||||
This is a table, where each entry should be a pair of
|
||||
resistance(ohm)-temperature(K). The entries added here are in uohm
|
||||
and uK only for custom thermistors. For more details look at table
|
||||
78 and 79.
|
||||
allOf:
|
||||
- $ref: /schemas/types.yaml#/definitions/uint64-matrix
|
||||
items:
|
||||
minItems: 3
|
||||
maxItems: 64
|
||||
items:
|
||||
minItems: 2
|
||||
maxItems: 2
|
||||
|
||||
adi,custom-steinhart:
|
||||
description:
|
||||
Steinhart-Hart coefficients are also supported and can
|
||||
be programmed into the device memory using this property. For
|
||||
Steinhart sensors the coefficients are given in the raw
|
||||
format. Look at table 82 for more information.
|
||||
allOf:
|
||||
- $ref: /schemas/types.yaml#/definitions/uint32-array
|
||||
items:
|
||||
minItems: 6
|
||||
maxItems: 6
|
||||
|
||||
required:
|
||||
- adi,rsense-handle
|
||||
|
||||
dependencies:
|
||||
adi,current-rotate: [ adi,rsense-share ]
|
||||
|
||||
"^adc@":
|
||||
type: object
|
||||
description: Represents a channel which is being used as a direct adc.
|
||||
|
||||
properties:
|
||||
adi,sensor-type:
|
||||
description: Identifies the sensor as a direct adc.
|
||||
allOf:
|
||||
- $ref: /schemas/types.yaml#/definitions/uint32
|
||||
const: 30
|
||||
|
||||
adi,single-ended:
|
||||
description: Boolean property which set's the adc as single-ended.
|
||||
type: boolean
|
||||
|
||||
"^rsense@":
|
||||
type: object
|
||||
description:
|
||||
Represents a rsense which is connected to one of the device channels.
|
||||
Rsense are used by thermistors and RTD's.
|
||||
|
||||
properties:
|
||||
reg:
|
||||
minimum: 2
|
||||
maximum: 20
|
||||
|
||||
adi,sensor-type:
|
||||
description: Identifies the sensor as a rsense.
|
||||
allOf:
|
||||
- $ref: /schemas/types.yaml#/definitions/uint32
|
||||
const: 29
|
||||
|
||||
adi,rsense-val-milli-ohms:
|
||||
description:
|
||||
Sets the value of the sense resistor. Look at table 20 of the
|
||||
datasheet for information.
|
||||
|
||||
required:
|
||||
- adi,rsense-val-milli-ohms
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
spi {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
sensor_ltc2983: ltc2983@0 {
|
||||
compatible = "adi,ltc2983";
|
||||
reg = <0>;
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
interrupts = <20 IRQ_TYPE_EDGE_RISING>;
|
||||
interrupt-parent = <&gpio>;
|
||||
|
||||
thermocouple@18 {
|
||||
reg = <18>;
|
||||
adi,sensor-type = <8>; //Type B
|
||||
adi,sensor-oc-current-microamp = <10>;
|
||||
adi,cold-junction-handle = <&diode5>;
|
||||
};
|
||||
|
||||
diode5: diode@5 {
|
||||
reg = <5>;
|
||||
adi,sensor-type = <28>;
|
||||
};
|
||||
|
||||
rsense2: rsense@2 {
|
||||
reg = <2>;
|
||||
adi,sensor-type = <29>;
|
||||
adi,rsense-val-milli-ohms = <1200000>; //1.2Kohms
|
||||
};
|
||||
|
||||
rtd@14 {
|
||||
reg = <14>;
|
||||
adi,sensor-type = <15>; //PT1000
|
||||
/*2-wire, internal gnd, no current rotation*/
|
||||
adi,number-of-wires = <2>;
|
||||
adi,rsense-share;
|
||||
adi,excitation-current-microamp = <500>;
|
||||
adi,rsense-handle = <&rsense2>;
|
||||
};
|
||||
|
||||
adc@10 {
|
||||
reg = <10>;
|
||||
adi,sensor-type = <30>;
|
||||
adi,single-ended;
|
||||
};
|
||||
|
||||
thermistor@12 {
|
||||
reg = <12>;
|
||||
adi,sensor-type = <26>; //Steinhart
|
||||
adi,rsense-handle = <&rsense2>;
|
||||
adi,custom-steinhart = <0x00F371EC 0x12345678
|
||||
0x2C0F8733 0x10018C66 0xA0FEACCD
|
||||
0x90021D99>; //6 entries
|
||||
};
|
||||
|
||||
thermocouple@20 {
|
||||
reg = <20>;
|
||||
adi,sensor-type = <9>; //custom thermocouple
|
||||
adi,single-ended;
|
||||
adi,custom-thermocouple = /bits/ 64
|
||||
<(-50220000) 0
|
||||
(-30200000) 99100000
|
||||
(-5300000) 135400000
|
||||
0 273150000
|
||||
40200000 361200000
|
||||
55300000 522100000
|
||||
88300000 720300000
|
||||
132200000 811200000
|
||||
188700000 922500000
|
||||
460400000 1000000000>; //10 pairs
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
...
|
@ -114,6 +114,18 @@ properties:
|
||||
- isil,isl68137
|
||||
# 5 Bit Programmable, Pulse-Width Modulator
|
||||
- maxim,ds1050
|
||||
# 10-bit 8 channels 300ks/s SPI ADC with temperature sensor
|
||||
- maxim,max1027
|
||||
# 10-bit 12 channels 300ks/s SPI ADC with temperature sensor
|
||||
- maxim,max1029
|
||||
# 10-bit 16 channels 300ks/s SPI ADC with temperature sensor
|
||||
- maxim,max1031
|
||||
# 12-bit 8 channels 300ks/s SPI ADC with temperature sensor
|
||||
- maxim,max1227
|
||||
# 12-bit 12 channels 300ks/s SPI ADC with temperature sensor
|
||||
- maxim,max1229
|
||||
# 12-bit 16 channels 300ks/s SPI ADC with temperature sensor
|
||||
- maxim,max1231
|
||||
# Low-Power, 4-/12-Channel, 2-Wire Serial, 12-Bit ADCs
|
||||
- maxim,max1237
|
||||
# PECI-to-I2C translator for PECI-to-SMBus/I2C protocol conversion
|
||||
|
@ -7,7 +7,7 @@ Generic Counter Interface
|
||||
Introduction
|
||||
============
|
||||
|
||||
Counter devices are prevalent within a diverse spectrum of industries.
|
||||
Counter devices are prevalent among a diverse spectrum of industries.
|
||||
The ubiquitous presence of these devices necessitates a common interface
|
||||
and standard of interaction and exposure. This driver API attempts to
|
||||
resolve the issue of duplicate code found among existing counter device
|
||||
@ -26,23 +26,72 @@ the Generic Counter interface.
|
||||
|
||||
There are three core components to a counter:
|
||||
|
||||
* Count:
|
||||
Count data for a set of Signals.
|
||||
|
||||
* Signal:
|
||||
Input data that is evaluated by the counter to determine the count
|
||||
data.
|
||||
Stream of data to be evaluated by the counter.
|
||||
|
||||
* Synapse:
|
||||
The association of a Signal with a respective Count.
|
||||
Association of a Signal, and evaluation trigger, with a Count.
|
||||
|
||||
* Count:
|
||||
Accumulation of the effects of connected Synapses.
|
||||
|
||||
SIGNAL
|
||||
------
|
||||
A Signal represents a stream of data. This is the input data that is
|
||||
evaluated by the counter to determine the count data; e.g. a quadrature
|
||||
signal output line of a rotary encoder. Not all counter devices provide
|
||||
user access to the Signal data, so exposure is optional for drivers.
|
||||
|
||||
When the Signal data is available for user access, the Generic Counter
|
||||
interface provides the following available signal values:
|
||||
|
||||
* SIGNAL_LOW:
|
||||
Signal line is in a low state.
|
||||
|
||||
* SIGNAL_HIGH:
|
||||
Signal line is in a high state.
|
||||
|
||||
A Signal may be associated with one or more Counts.
|
||||
|
||||
SYNAPSE
|
||||
-------
|
||||
A Synapse represents the association of a Signal with a Count. Signal
|
||||
data affects respective Count data, and the Synapse represents this
|
||||
relationship.
|
||||
|
||||
The Synapse action mode specifies the Signal data condition that
|
||||
triggers the respective Count's count function evaluation to update the
|
||||
count data. The Generic Counter interface provides the following
|
||||
available action modes:
|
||||
|
||||
* None:
|
||||
Signal does not trigger the count function. In Pulse-Direction count
|
||||
function mode, this Signal is evaluated as Direction.
|
||||
|
||||
* Rising Edge:
|
||||
Low state transitions to high state.
|
||||
|
||||
* Falling Edge:
|
||||
High state transitions to low state.
|
||||
|
||||
* Both Edges:
|
||||
Any state transition.
|
||||
|
||||
A counter is defined as a set of input signals associated with count
|
||||
data that are generated by the evaluation of the state of the associated
|
||||
input signals as defined by the respective count functions. Within the
|
||||
context of the Generic Counter interface, a counter consists of Counts
|
||||
each associated with a set of Signals, whose respective Synapse
|
||||
instances represent the count function update conditions for the
|
||||
associated Counts.
|
||||
|
||||
A Synapse associates one Signal with one Count.
|
||||
|
||||
COUNT
|
||||
-----
|
||||
A Count represents the count data for a set of Signals. The Generic
|
||||
Counter interface provides the following available count data types:
|
||||
|
||||
* COUNT_POSITION:
|
||||
Unsigned integer value representing position.
|
||||
A Count represents the accumulation of the effects of connected
|
||||
Synapses; i.e. the count data for a set of Signals. The Generic
|
||||
Counter interface represents the count data as a natural number.
|
||||
|
||||
A Count has a count function mode which represents the update behavior
|
||||
for the count data. The Generic Counter interface provides the following
|
||||
@ -86,60 +135,7 @@ available count function modes:
|
||||
Any state transition on either quadrature pair signals updates the
|
||||
respective count. Quadrature encoding determines the direction.
|
||||
|
||||
A Count has a set of one or more associated Signals.
|
||||
|
||||
SIGNAL
|
||||
------
|
||||
A Signal represents a counter input data; this is the input data that is
|
||||
evaluated by the counter to determine the count data; e.g. a quadrature
|
||||
signal output line of a rotary encoder. Not all counter devices provide
|
||||
user access to the Signal data.
|
||||
|
||||
The Generic Counter interface provides the following available signal
|
||||
data types for when the Signal data is available for user access:
|
||||
|
||||
* SIGNAL_LEVEL:
|
||||
Signal line state level. The following states are possible:
|
||||
|
||||
- SIGNAL_LEVEL_LOW:
|
||||
Signal line is in a low state.
|
||||
|
||||
- SIGNAL_LEVEL_HIGH:
|
||||
Signal line is in a high state.
|
||||
|
||||
A Signal may be associated with one or more Counts.
|
||||
|
||||
SYNAPSE
|
||||
-------
|
||||
A Synapse represents the association of a Signal with a respective
|
||||
Count. Signal data affects respective Count data, and the Synapse
|
||||
represents this relationship.
|
||||
|
||||
The Synapse action mode specifies the Signal data condition which
|
||||
triggers the respective Count's count function evaluation to update the
|
||||
count data. The Generic Counter interface provides the following
|
||||
available action modes:
|
||||
|
||||
* None:
|
||||
Signal does not trigger the count function. In Pulse-Direction count
|
||||
function mode, this Signal is evaluated as Direction.
|
||||
|
||||
* Rising Edge:
|
||||
Low state transitions to high state.
|
||||
|
||||
* Falling Edge:
|
||||
High state transitions to low state.
|
||||
|
||||
* Both Edges:
|
||||
Any state transition.
|
||||
|
||||
A counter is defined as a set of input signals associated with count
|
||||
data that are generated by the evaluation of the state of the associated
|
||||
input signals as defined by the respective count functions. Within the
|
||||
context of the Generic Counter interface, a counter consists of Counts
|
||||
each associated with a set of Signals, whose respective Synapse
|
||||
instances represent the count function update conditions for the
|
||||
associated Counts.
|
||||
A Count has a set of one or more associated Synapses.
|
||||
|
||||
Paradigm
|
||||
========
|
||||
@ -286,10 +282,36 @@ if device memory-managed registration is desired.
|
||||
Extension sysfs attributes can be created for auxiliary functionality
|
||||
and data by passing in defined counter_device_ext, counter_count_ext,
|
||||
and counter_signal_ext structures. In these cases, the
|
||||
counter_device_ext structure is used for global configuration of the
|
||||
respective Counter device, while the counter_count_ext and
|
||||
counter_signal_ext structures allow for auxiliary exposure and
|
||||
configuration of a specific Count or Signal respectively.
|
||||
counter_device_ext structure is used for global/miscellaneous exposure
|
||||
and configuration of the respective Counter device, while the
|
||||
counter_count_ext and counter_signal_ext structures allow for auxiliary
|
||||
exposure and configuration of a specific Count or Signal respectively.
|
||||
|
||||
Determining the type of extension to create is a matter of scope.
|
||||
|
||||
* Signal extensions are attributes that expose information/control
|
||||
specific to a Signal. These types of attributes will exist under a
|
||||
Signal's directory in sysfs.
|
||||
|
||||
For example, if you have an invert feature for a Signal, you can have
|
||||
a Signal extension called "invert" that toggles that feature:
|
||||
/sys/bus/counter/devices/counterX/signalY/invert
|
||||
|
||||
* Count extensions are attributes that expose information/control
|
||||
specific to a Count. These type of attributes will exist under a
|
||||
Count's directory in sysfs.
|
||||
|
||||
For example, if you want to pause/unpause a Count from updating, you
|
||||
can have a Count extension called "enable" that toggles such:
|
||||
/sys/bus/counter/devices/counterX/countY/enable
|
||||
|
||||
* Device extensions are attributes that expose information/control
|
||||
non-specific to a particular Count or Signal. This is where you would
|
||||
put your global features or other miscellanous functionality.
|
||||
|
||||
For example, if your device has an overtemp sensor, you can report the
|
||||
chip overheated via a device extension called "error_overtemp":
|
||||
/sys/bus/counter/devices/counterX/error_overtemp
|
||||
|
||||
Architecture
|
||||
============
|
||||
|
46
MAINTAINERS
46
MAINTAINERS
@ -901,6 +901,14 @@ S: Supported
|
||||
F: drivers/iio/adc/ad7124.c
|
||||
F: Documentation/devicetree/bindings/iio/adc/adi,ad7124.yaml
|
||||
|
||||
ANALOG DEVICES INC AD7292 DRIVER
|
||||
M: Marcelo Schmitt <marcelo.schmitt1@gmail.com>
|
||||
L: linux-iio@vger.kernel.org
|
||||
W: http://ez.analog.com/community/linux-device-drivers
|
||||
S: Supported
|
||||
F: drivers/iio/adc/ad7292.c
|
||||
F: Documentation/devicetree/bindings/iio/adc/adi,ad7292.yaml
|
||||
|
||||
ANALOG DEVICES INC AD7606 DRIVER
|
||||
M: Stefan Popa <stefan.popa@analog.com>
|
||||
M: Beniamin Bia <beniamin.bia@analog.com>
|
||||
@ -6182,6 +6190,7 @@ F: include/uapi/linux/mii.h
|
||||
|
||||
EXFAT FILE SYSTEM
|
||||
M: Valdis Kletnieks <valdis.kletnieks@vt.edu>
|
||||
L: linux-fsdevel@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/staging/exfat/
|
||||
|
||||
@ -9701,9 +9710,17 @@ LTC1660 DAC DRIVER
|
||||
M: Marcus Folkesson <marcus.folkesson@gmail.com>
|
||||
L: linux-iio@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/iio/dac/ltc1660.txt
|
||||
F: Documentation/devicetree/bindings/iio/dac/lltc,ltc1660.yaml
|
||||
F: drivers/iio/dac/ltc1660.c
|
||||
|
||||
LTC2983 IIO TEMPERATURE DRIVER
|
||||
M: Nuno Sá <nuno.sa@analog.com>
|
||||
W: http://ez.analog.com/community/linux-device-drivers
|
||||
L: linux-iio@vger.kernel.org
|
||||
S: Supported
|
||||
F: drivers/iio/temperature/ltc2983.c
|
||||
F: Documentation/devicetree/bindings/iio/temperature/adi,ltc2983.yaml
|
||||
|
||||
LTC4261 HARDWARE MONITOR DRIVER
|
||||
M: Guenter Roeck <linux@roeck-us.net>
|
||||
L: linux-hwmon@vger.kernel.org
|
||||
@ -10819,7 +10836,7 @@ M: Kent Gustavsson <kent@minoris.se>
|
||||
L: linux-iio@vger.kernel.org
|
||||
S: Supported
|
||||
F: drivers/iio/adc/mcp3911.c
|
||||
F: Documentation/devicetree/bindings/iio/adc/mcp3911.txt
|
||||
F: Documentation/devicetree/bindings/iio/adc/microchip,mcp3911.yaml
|
||||
|
||||
MICROCHIP NAND DRIVER
|
||||
M: Tudor Ambarus <tudor.ambarus@microchip.com>
|
||||
@ -14072,6 +14089,12 @@ L: linux-serial@vger.kernel.org
|
||||
S: Odd Fixes
|
||||
F: drivers/tty/serial/rp2.*
|
||||
|
||||
ROHM BH1750 AMBIENT LIGHT SENSOR DRIVER
|
||||
M: Tomasz Duszynski <tduszyns@gmail.com>
|
||||
S: Maintained
|
||||
F: drivers/iio/light/bh1750.c
|
||||
F: Documentation/devicetree/bindings/iio/light/bh1750.yaml
|
||||
|
||||
ROHM MULTIFUNCTION BD9571MWV-M PMIC DEVICE DRIVERS
|
||||
M: Marek Vasut <marek.vasut+renesas@gmail.com>
|
||||
L: linux-kernel@vger.kernel.org
|
||||
@ -14923,6 +14946,11 @@ S: Maintained
|
||||
F: drivers/input/touchscreen/silead.c
|
||||
F: drivers/platform/x86/touchscreen_dmi.c
|
||||
|
||||
SILICON LABS WIRELESS DRIVERS (for WFxxx series)
|
||||
M: Jérôme Pouiller <jerome.pouiller@silabs.com>
|
||||
S: Supported
|
||||
F: drivers/staging/wfx/
|
||||
|
||||
SILICON MOTION SM712 FRAME BUFFER DRIVER
|
||||
M: Sudip Mukherjee <sudipm.mukherjee@gmail.com>
|
||||
M: Teddy Wang <teddy.wang@siliconmotion.com>
|
||||
@ -15588,6 +15616,14 @@ L: linux-wireless@vger.kernel.org
|
||||
S: Supported
|
||||
F: drivers/staging/wilc1000/
|
||||
|
||||
STAGING - SEPS525 LCD CONTROLLER DRIVERS
|
||||
M: Michael Hennerich <michael.hennerich@analog.com>
|
||||
M: Beniamin Bia <beniamin.bia@analog.com>
|
||||
L: linux-fbdev@vger.kernel.org
|
||||
S: Supported
|
||||
F: drivers/staging/fbtft/fb_seps525.c
|
||||
F: Documentation/devicetree/bindings/iio/adc/adi,ad7606.yaml
|
||||
|
||||
STAGING SUBSYSTEM
|
||||
M: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging.git
|
||||
@ -16330,6 +16366,12 @@ S: Maintained
|
||||
F: drivers/media/platform/davinci/
|
||||
F: include/media/davinci/
|
||||
|
||||
TI ENHANCED QUADRATURE ENCODER PULSE (eQEP) DRIVER
|
||||
R: David Lechner <david@lechnology.com>
|
||||
L: linux-iio@vger.kernel.org
|
||||
F: Documentation/devicetree/bindings/counter/ti-eqep.yaml
|
||||
F: drivers/counter/ti-eqep.c
|
||||
|
||||
TI ETHERNET SWITCH DRIVER (CPSW)
|
||||
R: Grygorii Strashko <grygorii.strashko@ti.com>
|
||||
L: linux-omap@vger.kernel.org
|
||||
|
@ -51,6 +51,7 @@ choice
|
||||
select MIPS_GIC
|
||||
select COMMON_CLK
|
||||
select CLKSRC_MIPS_GIC
|
||||
select HAVE_PCI if PCI_MT7621
|
||||
endchoice
|
||||
|
||||
choice
|
||||
|
@ -150,6 +150,15 @@ config TEGRA_GMI
|
||||
Driver for the Tegra Generic Memory Interface bus which can be used
|
||||
to attach devices such as NOR, UART, FPGA and more.
|
||||
|
||||
config TI_PWMSS
|
||||
bool
|
||||
default y if (ARCH_OMAP2PLUS) && (PWM_TIECAP || PWM_TIEHRPWM || TI_EQEP)
|
||||
help
|
||||
PWM Subsystem driver support for AM33xx SOC.
|
||||
|
||||
PWM submodules require PWM config space access from submodule
|
||||
drivers and require common parent driver support.
|
||||
|
||||
config TI_SYSC
|
||||
bool "TI sysc interconnect target module driver"
|
||||
depends on ARCH_OMAP2PLUS
|
||||
|
@ -27,6 +27,7 @@ obj-$(CONFIG_SUNXI_RSB) += sunxi-rsb.o
|
||||
obj-$(CONFIG_SIMPLE_PM_BUS) += simple-pm-bus.o
|
||||
obj-$(CONFIG_TEGRA_ACONNECT) += tegra-aconnect.o
|
||||
obj-$(CONFIG_TEGRA_GMI) += tegra-gmi.o
|
||||
obj-$(CONFIG_TI_PWMSS) += ti-pwmss.o
|
||||
obj-$(CONFIG_TI_SYSC) += ti-sysc.o
|
||||
obj-$(CONFIG_TS_NBUS) += ts-nbus.o
|
||||
obj-$(CONFIG_UNIPHIER_SYSTEM_BUS) += uniphier-system-bus.o
|
||||
|
@ -562,11 +562,10 @@ static const struct iio_chan_spec quad8_channels[] = {
|
||||
};
|
||||
|
||||
static int quad8_signal_read(struct counter_device *counter,
|
||||
struct counter_signal *signal, struct counter_signal_read_value *val)
|
||||
struct counter_signal *signal, enum counter_signal_value *val)
|
||||
{
|
||||
const struct quad8_iio *const priv = counter->priv;
|
||||
unsigned int state;
|
||||
enum counter_signal_level level;
|
||||
|
||||
/* Only Index signal levels can be read */
|
||||
if (signal->id < 16)
|
||||
@ -575,22 +574,19 @@ static int quad8_signal_read(struct counter_device *counter,
|
||||
state = inb(priv->base + QUAD8_REG_INDEX_INPUT_LEVELS)
|
||||
& BIT(signal->id - 16);
|
||||
|
||||
level = (state) ? COUNTER_SIGNAL_LEVEL_HIGH : COUNTER_SIGNAL_LEVEL_LOW;
|
||||
|
||||
counter_signal_read_value_set(val, COUNTER_SIGNAL_LEVEL, &level);
|
||||
*val = (state) ? COUNTER_SIGNAL_HIGH : COUNTER_SIGNAL_LOW;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int quad8_count_read(struct counter_device *counter,
|
||||
struct counter_count *count, struct counter_count_read_value *val)
|
||||
struct counter_count *count, unsigned long *val)
|
||||
{
|
||||
const struct quad8_iio *const priv = counter->priv;
|
||||
const int base_offset = priv->base + 2 * count->id;
|
||||
unsigned int flags;
|
||||
unsigned int borrow;
|
||||
unsigned int carry;
|
||||
unsigned long position;
|
||||
int i;
|
||||
|
||||
flags = inb(base_offset + 1);
|
||||
@ -598,36 +594,27 @@ static int quad8_count_read(struct counter_device *counter,
|
||||
carry = !!(flags & QUAD8_FLAG_CT);
|
||||
|
||||
/* Borrow XOR Carry effectively doubles count range */
|
||||
position = (unsigned long)(borrow ^ carry) << 24;
|
||||
*val = (unsigned long)(borrow ^ carry) << 24;
|
||||
|
||||
/* Reset Byte Pointer; transfer Counter to Output Latch */
|
||||
outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP | QUAD8_RLD_CNTR_OUT,
|
||||
base_offset + 1);
|
||||
|
||||
for (i = 0; i < 3; i++)
|
||||
position |= (unsigned long)inb(base_offset) << (8 * i);
|
||||
|
||||
counter_count_read_value_set(val, COUNTER_COUNT_POSITION, &position);
|
||||
*val |= (unsigned long)inb(base_offset) << (8 * i);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int quad8_count_write(struct counter_device *counter,
|
||||
struct counter_count *count, struct counter_count_write_value *val)
|
||||
struct counter_count *count, unsigned long val)
|
||||
{
|
||||
const struct quad8_iio *const priv = counter->priv;
|
||||
const int base_offset = priv->base + 2 * count->id;
|
||||
int err;
|
||||
unsigned long position;
|
||||
int i;
|
||||
|
||||
err = counter_count_write_value_get(&position, COUNTER_COUNT_POSITION,
|
||||
val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Only 24-bit values are supported */
|
||||
if (position > 0xFFFFFF)
|
||||
if (val > 0xFFFFFF)
|
||||
return -EINVAL;
|
||||
|
||||
/* Reset Byte Pointer */
|
||||
@ -635,7 +622,7 @@ static int quad8_count_write(struct counter_device *counter,
|
||||
|
||||
/* Counter can only be set via Preset Register */
|
||||
for (i = 0; i < 3; i++)
|
||||
outb(position >> (8 * i), base_offset);
|
||||
outb(val >> (8 * i), base_offset);
|
||||
|
||||
/* Transfer Preset Register to Counter */
|
||||
outb(QUAD8_CTR_RLD | QUAD8_RLD_PRESET_CNTR, base_offset + 1);
|
||||
@ -644,9 +631,9 @@ static int quad8_count_write(struct counter_device *counter,
|
||||
outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
|
||||
|
||||
/* Set Preset Register back to original value */
|
||||
position = priv->preset[count->id];
|
||||
val = priv->preset[count->id];
|
||||
for (i = 0; i < 3; i++)
|
||||
outb(position >> (8 * i), base_offset);
|
||||
outb(val >> (8 * i), base_offset);
|
||||
|
||||
/* Reset Borrow, Carry, Compare, and Sign flags */
|
||||
outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_FLAGS, base_offset + 1);
|
||||
|
@ -49,6 +49,17 @@ config STM32_LPTIMER_CNT
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called stm32-lptimer-cnt.
|
||||
|
||||
config TI_EQEP
|
||||
tristate "TI eQEP counter driver"
|
||||
depends on (SOC_AM33XX || COMPILE_TEST)
|
||||
select REGMAP_MMIO
|
||||
help
|
||||
Select this option to enable the Texas Instruments Enhanced Quadrature
|
||||
Encoder Pulse (eQEP) counter driver.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called ti-eqep.
|
||||
|
||||
config FTM_QUADDEC
|
||||
tristate "Flex Timer Module Quadrature decoder driver"
|
||||
depends on HAS_IOMEM && OF
|
||||
|
@ -8,4 +8,5 @@ obj-$(CONFIG_COUNTER) += counter.o
|
||||
obj-$(CONFIG_104_QUAD_8) += 104-quad-8.o
|
||||
obj-$(CONFIG_STM32_TIMER_CNT) += stm32-timer-cnt.o
|
||||
obj-$(CONFIG_STM32_LPTIMER_CNT) += stm32-lptimer-cnt.o
|
||||
obj-$(CONFIG_TI_EQEP) += ti-eqep.o
|
||||
obj-$(CONFIG_FTM_QUADDEC) += ftm-quaddec.o
|
||||
|
@ -220,86 +220,6 @@ ssize_t counter_device_enum_available_read(struct counter_device *counter,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(counter_device_enum_available_read);
|
||||
|
||||
static const char *const counter_signal_level_str[] = {
|
||||
[COUNTER_SIGNAL_LEVEL_LOW] = "low",
|
||||
[COUNTER_SIGNAL_LEVEL_HIGH] = "high"
|
||||
};
|
||||
|
||||
/**
|
||||
* counter_signal_read_value_set - set counter_signal_read_value data
|
||||
* @val: counter_signal_read_value structure to set
|
||||
* @type: property Signal data represents
|
||||
* @data: Signal data
|
||||
*
|
||||
* This function sets an opaque counter_signal_read_value structure with the
|
||||
* provided Signal data.
|
||||
*/
|
||||
void counter_signal_read_value_set(struct counter_signal_read_value *const val,
|
||||
const enum counter_signal_value_type type,
|
||||
void *const data)
|
||||
{
|
||||
if (type == COUNTER_SIGNAL_LEVEL)
|
||||
val->len = sprintf(val->buf, "%s\n",
|
||||
counter_signal_level_str[*(enum counter_signal_level *)data]);
|
||||
else
|
||||
val->len = 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(counter_signal_read_value_set);
|
||||
|
||||
/**
|
||||
* counter_count_read_value_set - set counter_count_read_value data
|
||||
* @val: counter_count_read_value structure to set
|
||||
* @type: property Count data represents
|
||||
* @data: Count data
|
||||
*
|
||||
* This function sets an opaque counter_count_read_value structure with the
|
||||
* provided Count data.
|
||||
*/
|
||||
void counter_count_read_value_set(struct counter_count_read_value *const val,
|
||||
const enum counter_count_value_type type,
|
||||
void *const data)
|
||||
{
|
||||
switch (type) {
|
||||
case COUNTER_COUNT_POSITION:
|
||||
val->len = sprintf(val->buf, "%lu\n", *(unsigned long *)data);
|
||||
break;
|
||||
default:
|
||||
val->len = 0;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(counter_count_read_value_set);
|
||||
|
||||
/**
|
||||
* counter_count_write_value_get - get counter_count_write_value data
|
||||
* @data: Count data
|
||||
* @type: property Count data represents
|
||||
* @val: counter_count_write_value structure containing data
|
||||
*
|
||||
* This function extracts Count data from the provided opaque
|
||||
* counter_count_write_value structure and stores it at the address provided by
|
||||
* @data.
|
||||
*
|
||||
* RETURNS:
|
||||
* 0 on success, negative error number on failure.
|
||||
*/
|
||||
int counter_count_write_value_get(void *const data,
|
||||
const enum counter_count_value_type type,
|
||||
const struct counter_count_write_value *const val)
|
||||
{
|
||||
int err;
|
||||
|
||||
switch (type) {
|
||||
case COUNTER_COUNT_POSITION:
|
||||
err = kstrtoul(val->buf, 0, data);
|
||||
if (err)
|
||||
return err;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(counter_count_write_value_get);
|
||||
|
||||
struct counter_attr_parm {
|
||||
struct counter_device_attr_group *group;
|
||||
const char *prefix;
|
||||
@ -369,6 +289,11 @@ struct counter_signal_unit {
|
||||
struct counter_signal *signal;
|
||||
};
|
||||
|
||||
static const char *const counter_signal_value_str[] = {
|
||||
[COUNTER_SIGNAL_LOW] = "low",
|
||||
[COUNTER_SIGNAL_HIGH] = "high"
|
||||
};
|
||||
|
||||
static ssize_t counter_signal_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
@ -377,13 +302,13 @@ static ssize_t counter_signal_show(struct device *dev,
|
||||
const struct counter_signal_unit *const component = devattr->component;
|
||||
struct counter_signal *const signal = component->signal;
|
||||
int err;
|
||||
struct counter_signal_read_value val = { .buf = buf };
|
||||
enum counter_signal_value val;
|
||||
|
||||
err = counter->ops->signal_read(counter, signal, &val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return val.len;
|
||||
return sprintf(buf, "%s\n", counter_signal_value_str[val]);
|
||||
}
|
||||
|
||||
struct counter_name_unit {
|
||||
@ -788,13 +713,13 @@ static ssize_t counter_count_show(struct device *dev,
|
||||
const struct counter_count_unit *const component = devattr->component;
|
||||
struct counter_count *const count = component->count;
|
||||
int err;
|
||||
struct counter_count_read_value val = { .buf = buf };
|
||||
unsigned long val;
|
||||
|
||||
err = counter->ops->count_read(counter, count, &val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return val.len;
|
||||
return sprintf(buf, "%lu\n", val);
|
||||
}
|
||||
|
||||
static ssize_t counter_count_store(struct device *dev,
|
||||
@ -806,9 +731,13 @@ static ssize_t counter_count_store(struct device *dev,
|
||||
const struct counter_count_unit *const component = devattr->component;
|
||||
struct counter_count *const count = component->count;
|
||||
int err;
|
||||
struct counter_count_write_value val = { .buf = buf };
|
||||
unsigned long val;
|
||||
|
||||
err = counter->ops->count_write(counter, count, &val);
|
||||
err = kstrtoul(buf, 0, &val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = counter->ops->count_write(counter, count, val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
|
@ -178,31 +178,25 @@ static const enum counter_count_function ftm_quaddec_count_functions[] = {
|
||||
|
||||
static int ftm_quaddec_count_read(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
struct counter_count_read_value *val)
|
||||
unsigned long *val)
|
||||
{
|
||||
struct ftm_quaddec *const ftm = counter->priv;
|
||||
uint32_t cntval;
|
||||
|
||||
ftm_read(ftm, FTM_CNT, &cntval);
|
||||
|
||||
counter_count_read_value_set(val, COUNTER_COUNT_POSITION, &cntval);
|
||||
*val = cntval;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ftm_quaddec_count_write(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
struct counter_count_write_value *val)
|
||||
const unsigned long val)
|
||||
{
|
||||
struct ftm_quaddec *const ftm = counter->priv;
|
||||
u32 cnt;
|
||||
int err;
|
||||
|
||||
err = counter_count_write_value_get(&cnt, COUNTER_COUNT_POSITION, val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (cnt != 0) {
|
||||
if (val != 0) {
|
||||
dev_warn(&ftm->pdev->dev, "Can only accept '0' as new counter value\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -347,7 +347,7 @@ static const struct iio_chan_spec stm32_lptim_cnt_channels = {
|
||||
};
|
||||
|
||||
/**
|
||||
* stm32_lptim_cnt_function - enumerates stm32 LPTimer counter & encoder modes
|
||||
* enum stm32_lptim_cnt_function - enumerates LPTimer counter & encoder modes
|
||||
* @STM32_LPTIM_COUNTER_INCREASE: up count on IN1 rising, falling or both edges
|
||||
* @STM32_LPTIM_ENCODER_BOTH_EDGE: count on both edges (IN1 & IN2 quadrature)
|
||||
*/
|
||||
@ -377,8 +377,7 @@ static enum counter_synapse_action stm32_lptim_cnt_synapse_actions[] = {
|
||||
};
|
||||
|
||||
static int stm32_lptim_cnt_read(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
struct counter_count_read_value *val)
|
||||
struct counter_count *count, unsigned long *val)
|
||||
{
|
||||
struct stm32_lptim_cnt *const priv = counter->priv;
|
||||
u32 cnt;
|
||||
@ -388,7 +387,7 @@ static int stm32_lptim_cnt_read(struct counter_device *counter,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
counter_count_read_value_set(val, COUNTER_COUNT_POSITION, &cnt);
|
||||
*val = cnt;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ struct stm32_timer_cnt {
|
||||
};
|
||||
|
||||
/**
|
||||
* stm32_count_function - enumerates stm32 timer counter encoder modes
|
||||
* enum stm32_count_function - enumerates stm32 timer counter encoder modes
|
||||
* @STM32_COUNT_SLAVE_MODE_DISABLED: counts on internal clock when CEN=1
|
||||
* @STM32_COUNT_ENCODER_MODE_1: counts TI1FP1 edges, depending on TI2FP2 level
|
||||
* @STM32_COUNT_ENCODER_MODE_2: counts TI2FP2 edges, depending on TI1FP1 level
|
||||
@ -48,34 +48,27 @@ static enum counter_count_function stm32_count_functions[] = {
|
||||
};
|
||||
|
||||
static int stm32_count_read(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
struct counter_count_read_value *val)
|
||||
struct counter_count *count, unsigned long *val)
|
||||
{
|
||||
struct stm32_timer_cnt *const priv = counter->priv;
|
||||
u32 cnt;
|
||||
|
||||
regmap_read(priv->regmap, TIM_CNT, &cnt);
|
||||
counter_count_read_value_set(val, COUNTER_COUNT_POSITION, &cnt);
|
||||
*val = cnt;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stm32_count_write(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
struct counter_count_write_value *val)
|
||||
const unsigned long val)
|
||||
{
|
||||
struct stm32_timer_cnt *const priv = counter->priv;
|
||||
u32 cnt;
|
||||
int err;
|
||||
|
||||
err = counter_count_write_value_get(&cnt, COUNTER_COUNT_POSITION, val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (cnt > priv->ceiling)
|
||||
if (val > priv->ceiling)
|
||||
return -EINVAL;
|
||||
|
||||
return regmap_write(priv->regmap, TIM_CNT, cnt);
|
||||
return regmap_write(priv->regmap, TIM_CNT, val);
|
||||
}
|
||||
|
||||
static int stm32_count_function_get(struct counter_device *counter,
|
||||
@ -219,8 +212,8 @@ static ssize_t stm32_count_enable_write(struct counter_device *counter,
|
||||
|
||||
if (enable) {
|
||||
regmap_read(priv->regmap, TIM_CR1, &cr1);
|
||||
if (!(cr1 & TIM_CR1_CEN))
|
||||
clk_enable(priv->clk);
|
||||
if (!(cr1 & TIM_CR1_CEN))
|
||||
clk_enable(priv->clk);
|
||||
|
||||
regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN,
|
||||
TIM_CR1_CEN);
|
||||
|
466
drivers/counter/ti-eqep.c
Normal file
466
drivers/counter/ti-eqep.c
Normal file
@ -0,0 +1,466 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 2019 David Lechner <david@lechnology.com>
|
||||
*
|
||||
* Counter driver for Texas Instruments Enhanced Quadrature Encoder Pulse (eQEP)
|
||||
*/
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/counter.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
/* 32-bit registers */
|
||||
#define QPOSCNT 0x0
|
||||
#define QPOSINIT 0x4
|
||||
#define QPOSMAX 0x8
|
||||
#define QPOSCMP 0xc
|
||||
#define QPOSILAT 0x10
|
||||
#define QPOSSLAT 0x14
|
||||
#define QPOSLAT 0x18
|
||||
#define QUTMR 0x1c
|
||||
#define QUPRD 0x20
|
||||
|
||||
/* 16-bit registers */
|
||||
#define QWDTMR 0x0 /* 0x24 */
|
||||
#define QWDPRD 0x2 /* 0x26 */
|
||||
#define QDECCTL 0x4 /* 0x28 */
|
||||
#define QEPCTL 0x6 /* 0x2a */
|
||||
#define QCAPCTL 0x8 /* 0x2c */
|
||||
#define QPOSCTL 0xa /* 0x2e */
|
||||
#define QEINT 0xc /* 0x30 */
|
||||
#define QFLG 0xe /* 0x32 */
|
||||
#define QCLR 0x10 /* 0x34 */
|
||||
#define QFRC 0x12 /* 0x36 */
|
||||
#define QEPSTS 0x14 /* 0x38 */
|
||||
#define QCTMR 0x16 /* 0x3a */
|
||||
#define QCPRD 0x18 /* 0x3c */
|
||||
#define QCTMRLAT 0x1a /* 0x3e */
|
||||
#define QCPRDLAT 0x1c /* 0x40 */
|
||||
|
||||
#define QDECCTL_QSRC_SHIFT 14
|
||||
#define QDECCTL_QSRC GENMASK(15, 14)
|
||||
#define QDECCTL_SOEN BIT(13)
|
||||
#define QDECCTL_SPSEL BIT(12)
|
||||
#define QDECCTL_XCR BIT(11)
|
||||
#define QDECCTL_SWAP BIT(10)
|
||||
#define QDECCTL_IGATE BIT(9)
|
||||
#define QDECCTL_QAP BIT(8)
|
||||
#define QDECCTL_QBP BIT(7)
|
||||
#define QDECCTL_QIP BIT(6)
|
||||
#define QDECCTL_QSP BIT(5)
|
||||
|
||||
#define QEPCTL_FREE_SOFT GENMASK(15, 14)
|
||||
#define QEPCTL_PCRM GENMASK(13, 12)
|
||||
#define QEPCTL_SEI GENMASK(11, 10)
|
||||
#define QEPCTL_IEI GENMASK(9, 8)
|
||||
#define QEPCTL_SWI BIT(7)
|
||||
#define QEPCTL_SEL BIT(6)
|
||||
#define QEPCTL_IEL GENMASK(5, 4)
|
||||
#define QEPCTL_PHEN BIT(3)
|
||||
#define QEPCTL_QCLM BIT(2)
|
||||
#define QEPCTL_UTE BIT(1)
|
||||
#define QEPCTL_WDE BIT(0)
|
||||
|
||||
/* EQEP Inputs */
|
||||
enum {
|
||||
TI_EQEP_SIGNAL_QEPA, /* QEPA/XCLK */
|
||||
TI_EQEP_SIGNAL_QEPB, /* QEPB/XDIR */
|
||||
};
|
||||
|
||||
/* Position Counter Input Modes */
|
||||
enum {
|
||||
TI_EQEP_COUNT_FUNC_QUAD_COUNT,
|
||||
TI_EQEP_COUNT_FUNC_DIR_COUNT,
|
||||
TI_EQEP_COUNT_FUNC_UP_COUNT,
|
||||
TI_EQEP_COUNT_FUNC_DOWN_COUNT,
|
||||
};
|
||||
|
||||
enum {
|
||||
TI_EQEP_SYNAPSE_ACTION_BOTH_EDGES,
|
||||
TI_EQEP_SYNAPSE_ACTION_RISING_EDGE,
|
||||
TI_EQEP_SYNAPSE_ACTION_NONE,
|
||||
};
|
||||
|
||||
struct ti_eqep_cnt {
|
||||
struct counter_device counter;
|
||||
struct regmap *regmap32;
|
||||
struct regmap *regmap16;
|
||||
};
|
||||
|
||||
static int ti_eqep_count_read(struct counter_device *counter,
|
||||
struct counter_count *count, unsigned long *val)
|
||||
{
|
||||
struct ti_eqep_cnt *priv = counter->priv;
|
||||
u32 cnt;
|
||||
|
||||
regmap_read(priv->regmap32, QPOSCNT, &cnt);
|
||||
*val = cnt;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ti_eqep_count_write(struct counter_device *counter,
|
||||
struct counter_count *count, unsigned long val)
|
||||
{
|
||||
struct ti_eqep_cnt *priv = counter->priv;
|
||||
u32 max;
|
||||
|
||||
regmap_read(priv->regmap32, QPOSMAX, &max);
|
||||
if (val > max)
|
||||
return -EINVAL;
|
||||
|
||||
return regmap_write(priv->regmap32, QPOSCNT, val);
|
||||
}
|
||||
|
||||
static int ti_eqep_function_get(struct counter_device *counter,
|
||||
struct counter_count *count, size_t *function)
|
||||
{
|
||||
struct ti_eqep_cnt *priv = counter->priv;
|
||||
u32 qdecctl;
|
||||
|
||||
regmap_read(priv->regmap16, QDECCTL, &qdecctl);
|
||||
*function = (qdecctl & QDECCTL_QSRC) >> QDECCTL_QSRC_SHIFT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ti_eqep_function_set(struct counter_device *counter,
|
||||
struct counter_count *count, size_t function)
|
||||
{
|
||||
struct ti_eqep_cnt *priv = counter->priv;
|
||||
|
||||
return regmap_write_bits(priv->regmap16, QDECCTL, QDECCTL_QSRC,
|
||||
function << QDECCTL_QSRC_SHIFT);
|
||||
}
|
||||
|
||||
static int ti_eqep_action_get(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
struct counter_synapse *synapse, size_t *action)
|
||||
{
|
||||
struct ti_eqep_cnt *priv = counter->priv;
|
||||
size_t function;
|
||||
u32 qdecctl;
|
||||
int err;
|
||||
|
||||
err = ti_eqep_function_get(counter, count, &function);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
switch (function) {
|
||||
case TI_EQEP_COUNT_FUNC_QUAD_COUNT:
|
||||
/* In quadrature mode, the rising and falling edge of both
|
||||
* QEPA and QEPB trigger QCLK.
|
||||
*/
|
||||
*action = TI_EQEP_SYNAPSE_ACTION_BOTH_EDGES;
|
||||
break;
|
||||
case TI_EQEP_COUNT_FUNC_DIR_COUNT:
|
||||
/* In direction-count mode only rising edge of QEPA is counted
|
||||
* and QEPB gives direction.
|
||||
*/
|
||||
switch (synapse->signal->id) {
|
||||
case TI_EQEP_SIGNAL_QEPA:
|
||||
*action = TI_EQEP_SYNAPSE_ACTION_RISING_EDGE;
|
||||
break;
|
||||
default:
|
||||
*action = TI_EQEP_SYNAPSE_ACTION_NONE;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case TI_EQEP_COUNT_FUNC_UP_COUNT:
|
||||
case TI_EQEP_COUNT_FUNC_DOWN_COUNT:
|
||||
/* In up/down-count modes only QEPA is counted and QEPB is not
|
||||
* used.
|
||||
*/
|
||||
switch (synapse->signal->id) {
|
||||
case TI_EQEP_SIGNAL_QEPA:
|
||||
err = regmap_read(priv->regmap16, QDECCTL, &qdecctl);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (qdecctl & QDECCTL_XCR)
|
||||
*action = TI_EQEP_SYNAPSE_ACTION_BOTH_EDGES;
|
||||
else
|
||||
*action = TI_EQEP_SYNAPSE_ACTION_RISING_EDGE;
|
||||
break;
|
||||
default:
|
||||
*action = TI_EQEP_SYNAPSE_ACTION_NONE;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct counter_ops ti_eqep_counter_ops = {
|
||||
.count_read = ti_eqep_count_read,
|
||||
.count_write = ti_eqep_count_write,
|
||||
.function_get = ti_eqep_function_get,
|
||||
.function_set = ti_eqep_function_set,
|
||||
.action_get = ti_eqep_action_get,
|
||||
};
|
||||
|
||||
static ssize_t ti_eqep_position_ceiling_read(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
void *ext_priv, char *buf)
|
||||
{
|
||||
struct ti_eqep_cnt *priv = counter->priv;
|
||||
u32 qposmax;
|
||||
|
||||
regmap_read(priv->regmap32, QPOSMAX, &qposmax);
|
||||
|
||||
return sprintf(buf, "%u\n", qposmax);
|
||||
}
|
||||
|
||||
static ssize_t ti_eqep_position_ceiling_write(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
void *ext_priv, const char *buf,
|
||||
size_t len)
|
||||
{
|
||||
struct ti_eqep_cnt *priv = counter->priv;
|
||||
int err;
|
||||
u32 res;
|
||||
|
||||
err = kstrtouint(buf, 0, &res);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
regmap_write(priv->regmap32, QPOSMAX, res);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t ti_eqep_position_floor_read(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
void *ext_priv, char *buf)
|
||||
{
|
||||
struct ti_eqep_cnt *priv = counter->priv;
|
||||
u32 qposinit;
|
||||
|
||||
regmap_read(priv->regmap32, QPOSINIT, &qposinit);
|
||||
|
||||
return sprintf(buf, "%u\n", qposinit);
|
||||
}
|
||||
|
||||
static ssize_t ti_eqep_position_floor_write(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
void *ext_priv, const char *buf,
|
||||
size_t len)
|
||||
{
|
||||
struct ti_eqep_cnt *priv = counter->priv;
|
||||
int err;
|
||||
u32 res;
|
||||
|
||||
err = kstrtouint(buf, 0, &res);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
regmap_write(priv->regmap32, QPOSINIT, res);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t ti_eqep_position_enable_read(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
void *ext_priv, char *buf)
|
||||
{
|
||||
struct ti_eqep_cnt *priv = counter->priv;
|
||||
u32 qepctl;
|
||||
|
||||
regmap_read(priv->regmap16, QEPCTL, &qepctl);
|
||||
|
||||
return sprintf(buf, "%u\n", !!(qepctl & QEPCTL_PHEN));
|
||||
}
|
||||
|
||||
static ssize_t ti_eqep_position_enable_write(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
void *ext_priv, const char *buf,
|
||||
size_t len)
|
||||
{
|
||||
struct ti_eqep_cnt *priv = counter->priv;
|
||||
int err;
|
||||
bool res;
|
||||
|
||||
err = kstrtobool(buf, &res);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
regmap_write_bits(priv->regmap16, QEPCTL, QEPCTL_PHEN, res ? -1 : 0);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static struct counter_count_ext ti_eqep_position_ext[] = {
|
||||
{
|
||||
.name = "ceiling",
|
||||
.read = ti_eqep_position_ceiling_read,
|
||||
.write = ti_eqep_position_ceiling_write,
|
||||
},
|
||||
{
|
||||
.name = "floor",
|
||||
.read = ti_eqep_position_floor_read,
|
||||
.write = ti_eqep_position_floor_write,
|
||||
},
|
||||
{
|
||||
.name = "enable",
|
||||
.read = ti_eqep_position_enable_read,
|
||||
.write = ti_eqep_position_enable_write,
|
||||
},
|
||||
};
|
||||
|
||||
static struct counter_signal ti_eqep_signals[] = {
|
||||
[TI_EQEP_SIGNAL_QEPA] = {
|
||||
.id = TI_EQEP_SIGNAL_QEPA,
|
||||
.name = "QEPA"
|
||||
},
|
||||
[TI_EQEP_SIGNAL_QEPB] = {
|
||||
.id = TI_EQEP_SIGNAL_QEPB,
|
||||
.name = "QEPB"
|
||||
},
|
||||
};
|
||||
|
||||
static const enum counter_count_function ti_eqep_position_functions[] = {
|
||||
[TI_EQEP_COUNT_FUNC_QUAD_COUNT] = COUNTER_COUNT_FUNCTION_QUADRATURE_X4,
|
||||
[TI_EQEP_COUNT_FUNC_DIR_COUNT] = COUNTER_COUNT_FUNCTION_PULSE_DIRECTION,
|
||||
[TI_EQEP_COUNT_FUNC_UP_COUNT] = COUNTER_COUNT_FUNCTION_INCREASE,
|
||||
[TI_EQEP_COUNT_FUNC_DOWN_COUNT] = COUNTER_COUNT_FUNCTION_DECREASE,
|
||||
};
|
||||
|
||||
static const enum counter_synapse_action ti_eqep_position_synapse_actions[] = {
|
||||
[TI_EQEP_SYNAPSE_ACTION_BOTH_EDGES] = COUNTER_SYNAPSE_ACTION_BOTH_EDGES,
|
||||
[TI_EQEP_SYNAPSE_ACTION_RISING_EDGE] = COUNTER_SYNAPSE_ACTION_RISING_EDGE,
|
||||
[TI_EQEP_SYNAPSE_ACTION_NONE] = COUNTER_SYNAPSE_ACTION_NONE,
|
||||
};
|
||||
|
||||
static struct counter_synapse ti_eqep_position_synapses[] = {
|
||||
{
|
||||
.actions_list = ti_eqep_position_synapse_actions,
|
||||
.num_actions = ARRAY_SIZE(ti_eqep_position_synapse_actions),
|
||||
.signal = &ti_eqep_signals[TI_EQEP_SIGNAL_QEPA],
|
||||
},
|
||||
{
|
||||
.actions_list = ti_eqep_position_synapse_actions,
|
||||
.num_actions = ARRAY_SIZE(ti_eqep_position_synapse_actions),
|
||||
.signal = &ti_eqep_signals[TI_EQEP_SIGNAL_QEPB],
|
||||
},
|
||||
};
|
||||
|
||||
static struct counter_count ti_eqep_counts[] = {
|
||||
{
|
||||
.id = 0,
|
||||
.name = "QPOSCNT",
|
||||
.functions_list = ti_eqep_position_functions,
|
||||
.num_functions = ARRAY_SIZE(ti_eqep_position_functions),
|
||||
.synapses = ti_eqep_position_synapses,
|
||||
.num_synapses = ARRAY_SIZE(ti_eqep_position_synapses),
|
||||
.ext = ti_eqep_position_ext,
|
||||
.num_ext = ARRAY_SIZE(ti_eqep_position_ext),
|
||||
},
|
||||
};
|
||||
|
||||
static const struct regmap_config ti_eqep_regmap32_config = {
|
||||
.name = "32-bit",
|
||||
.reg_bits = 32,
|
||||
.val_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.max_register = 0x24,
|
||||
};
|
||||
|
||||
static const struct regmap_config ti_eqep_regmap16_config = {
|
||||
.name = "16-bit",
|
||||
.reg_bits = 16,
|
||||
.val_bits = 16,
|
||||
.reg_stride = 2,
|
||||
.max_register = 0x1e,
|
||||
};
|
||||
|
||||
static int ti_eqep_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct ti_eqep_cnt *priv;
|
||||
void __iomem *base;
|
||||
int err;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
priv->regmap32 = devm_regmap_init_mmio(dev, base,
|
||||
&ti_eqep_regmap32_config);
|
||||
if (IS_ERR(priv->regmap32))
|
||||
return PTR_ERR(priv->regmap32);
|
||||
|
||||
priv->regmap16 = devm_regmap_init_mmio(dev, base + 0x24,
|
||||
&ti_eqep_regmap16_config);
|
||||
if (IS_ERR(priv->regmap16))
|
||||
return PTR_ERR(priv->regmap16);
|
||||
|
||||
priv->counter.name = dev_name(dev);
|
||||
priv->counter.parent = dev;
|
||||
priv->counter.ops = &ti_eqep_counter_ops;
|
||||
priv->counter.counts = ti_eqep_counts;
|
||||
priv->counter.num_counts = ARRAY_SIZE(ti_eqep_counts);
|
||||
priv->counter.signals = ti_eqep_signals;
|
||||
priv->counter.num_signals = ARRAY_SIZE(ti_eqep_signals);
|
||||
priv->counter.priv = priv;
|
||||
|
||||
platform_set_drvdata(pdev, priv);
|
||||
|
||||
/*
|
||||
* Need to make sure power is turned on. On AM33xx, this comes from the
|
||||
* parent PWMSS bus driver. On AM17xx, this comes from the PSC power
|
||||
* domain.
|
||||
*/
|
||||
pm_runtime_enable(dev);
|
||||
pm_runtime_get_sync(dev);
|
||||
|
||||
err = counter_register(&priv->counter);
|
||||
if (err < 0) {
|
||||
pm_runtime_put_sync(dev);
|
||||
pm_runtime_disable(dev);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ti_eqep_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct ti_eqep_cnt *priv = platform_get_drvdata(pdev);
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
counter_unregister(&priv->counter);
|
||||
pm_runtime_put_sync(dev),
|
||||
pm_runtime_disable(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id ti_eqep_of_match[] = {
|
||||
{ .compatible = "ti,am3352-eqep", },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ti_eqep_of_match);
|
||||
|
||||
static struct platform_driver ti_eqep_driver = {
|
||||
.probe = ti_eqep_probe,
|
||||
.remove = ti_eqep_remove,
|
||||
.driver = {
|
||||
.name = "ti-eqep-cnt",
|
||||
.of_match_table = ti_eqep_of_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(ti_eqep_driver);
|
||||
|
||||
MODULE_AUTHOR("David Lechner <david@lechnology.com>");
|
||||
MODULE_DESCRIPTION("TI eQEP counter driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -15,7 +15,6 @@
|
||||
#include <linux/types.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
@ -55,6 +55,16 @@ config AD7291
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ad7291.
|
||||
|
||||
config AD7292
|
||||
tristate "Analog Devices AD7292 ADC driver"
|
||||
depends on SPI
|
||||
help
|
||||
Say yes here to build support for Analog Devices AD7292
|
||||
8 Channel ADC with temperature sensor.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ad7292.
|
||||
|
||||
config AD7298
|
||||
tristate "Analog Devices AD7298 ADC driver"
|
||||
depends on SPI
|
||||
@ -442,6 +452,17 @@ config INGENIC_ADC
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called ingenic_adc.
|
||||
|
||||
config INTEL_MRFLD_ADC
|
||||
tristate "Intel Merrifield Basin Cove ADC driver"
|
||||
depends on INTEL_SOC_PMIC_MRFLD
|
||||
help
|
||||
Say yes here to have support for Basin Cove power management IC (PMIC) ADC
|
||||
device. Depending on platform configuration, this general purpose ADC can
|
||||
be used for sampling sensors such as thermal resistors.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called intel_mrfld_adc.
|
||||
|
||||
config IMX7D_ADC
|
||||
tristate "Freescale IMX7D ADC driver"
|
||||
depends on ARCH_MXC || COMPILE_TEST
|
||||
@ -518,8 +539,8 @@ config MAX1027
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
Say yes here to build support for Maxim SPI ADC models
|
||||
max1027, max1029 and max1031.
|
||||
Say yes here to build support for Maxim SPI {10,12}-bit ADC models:
|
||||
max1027, max1029, max1031, max1227, max1229 and max1231.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called max1027.
|
||||
|
@ -9,6 +9,7 @@ obj-$(CONFIG_AD_SIGMA_DELTA) += ad_sigma_delta.o
|
||||
obj-$(CONFIG_AD7124) += ad7124.o
|
||||
obj-$(CONFIG_AD7266) += ad7266.o
|
||||
obj-$(CONFIG_AD7291) += ad7291.o
|
||||
obj-$(CONFIG_AD7292) += ad7292.o
|
||||
obj-$(CONFIG_AD7298) += ad7298.o
|
||||
obj-$(CONFIG_AD7923) += ad7923.o
|
||||
obj-$(CONFIG_AD7476) += ad7476.o
|
||||
@ -43,6 +44,7 @@ obj-$(CONFIG_HX711) += hx711.o
|
||||
obj-$(CONFIG_IMX7D_ADC) += imx7d_adc.o
|
||||
obj-$(CONFIG_INA2XX_ADC) += ina2xx-adc.o
|
||||
obj-$(CONFIG_INGENIC_ADC) += ingenic-adc.o
|
||||
obj-$(CONFIG_INTEL_MRFLD_ADC) += intel_mrfld_adc.o
|
||||
obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
|
||||
obj-$(CONFIG_LPC18XX_ADC) += lpc18xx_adc.o
|
||||
obj-$(CONFIG_LPC32XX_ADC) += lpc32xx_adc.o
|
||||
|
350
drivers/iio/adc/ad7292.c
Normal file
350
drivers/iio/adc/ad7292.c
Normal file
@ -0,0 +1,350 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Analog Devices AD7292 SPI ADC driver
|
||||
*
|
||||
* Copyright 2019 Analog Devices Inc.
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
|
||||
#define ADI_VENDOR_ID 0x0018
|
||||
|
||||
/* AD7292 registers definition */
|
||||
#define AD7292_REG_VENDOR_ID 0x00
|
||||
#define AD7292_REG_CONF_BANK 0x05
|
||||
#define AD7292_REG_CONV_COMM 0x0E
|
||||
#define AD7292_REG_ADC_CH(x) (0x10 + (x))
|
||||
|
||||
/* AD7292 configuration bank subregisters definition */
|
||||
#define AD7292_BANK_REG_VIN_RNG0 0x10
|
||||
#define AD7292_BANK_REG_VIN_RNG1 0x11
|
||||
#define AD7292_BANK_REG_SAMP_MODE 0x12
|
||||
|
||||
#define AD7292_RD_FLAG_MSK(x) (BIT(7) | ((x) & 0x3F))
|
||||
|
||||
/* AD7292_REG_ADC_CONVERSION */
|
||||
#define AD7292_ADC_DATA_MASK GENMASK(15, 6)
|
||||
#define AD7292_ADC_DATA(x) FIELD_GET(AD7292_ADC_DATA_MASK, x)
|
||||
|
||||
/* AD7292_CHANNEL_SAMPLING_MODE */
|
||||
#define AD7292_CH_SAMP_MODE(reg, ch) (((reg) >> 8) & BIT(ch))
|
||||
|
||||
/* AD7292_CHANNEL_VIN_RANGE */
|
||||
#define AD7292_CH_VIN_RANGE(reg, ch) ((reg) & BIT(ch))
|
||||
|
||||
#define AD7292_VOLTAGE_CHAN(_chan) \
|
||||
{ \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.indexed = 1, \
|
||||
.channel = _chan, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec ad7292_channels[] = {
|
||||
AD7292_VOLTAGE_CHAN(0),
|
||||
AD7292_VOLTAGE_CHAN(1),
|
||||
AD7292_VOLTAGE_CHAN(2),
|
||||
AD7292_VOLTAGE_CHAN(3),
|
||||
AD7292_VOLTAGE_CHAN(4),
|
||||
AD7292_VOLTAGE_CHAN(5),
|
||||
AD7292_VOLTAGE_CHAN(6),
|
||||
AD7292_VOLTAGE_CHAN(7)
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec ad7292_channels_diff[] = {
|
||||
{
|
||||
.type = IIO_VOLTAGE,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.indexed = 1,
|
||||
.differential = 1,
|
||||
.channel = 0,
|
||||
.channel2 = 1,
|
||||
},
|
||||
AD7292_VOLTAGE_CHAN(2),
|
||||
AD7292_VOLTAGE_CHAN(3),
|
||||
AD7292_VOLTAGE_CHAN(4),
|
||||
AD7292_VOLTAGE_CHAN(5),
|
||||
AD7292_VOLTAGE_CHAN(6),
|
||||
AD7292_VOLTAGE_CHAN(7)
|
||||
};
|
||||
|
||||
struct ad7292_state {
|
||||
struct spi_device *spi;
|
||||
struct regulator *reg;
|
||||
unsigned short vref_mv;
|
||||
|
||||
__be16 d16 ____cacheline_aligned;
|
||||
u8 d8[2];
|
||||
};
|
||||
|
||||
static int ad7292_spi_reg_read(struct ad7292_state *st, unsigned int addr)
|
||||
{
|
||||
int ret;
|
||||
|
||||
st->d8[0] = AD7292_RD_FLAG_MSK(addr);
|
||||
|
||||
ret = spi_write_then_read(st->spi, st->d8, 1, &st->d16, 2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return be16_to_cpu(st->d16);
|
||||
}
|
||||
|
||||
static int ad7292_spi_subreg_read(struct ad7292_state *st, unsigned int addr,
|
||||
unsigned int sub_addr, unsigned int len)
|
||||
{
|
||||
unsigned int shift = 16 - (8 * len);
|
||||
int ret;
|
||||
|
||||
st->d8[0] = AD7292_RD_FLAG_MSK(addr);
|
||||
st->d8[1] = sub_addr;
|
||||
|
||||
ret = spi_write_then_read(st->spi, st->d8, 2, &st->d16, len);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return (be16_to_cpu(st->d16) >> shift);
|
||||
}
|
||||
|
||||
static int ad7292_single_conversion(struct ad7292_state *st,
|
||||
unsigned int chan_addr)
|
||||
{
|
||||
int ret;
|
||||
|
||||
struct spi_transfer t[] = {
|
||||
{
|
||||
.tx_buf = &st->d8,
|
||||
.len = 4,
|
||||
.delay_usecs = 6,
|
||||
}, {
|
||||
.rx_buf = &st->d16,
|
||||
.len = 2,
|
||||
},
|
||||
};
|
||||
|
||||
st->d8[0] = chan_addr;
|
||||
st->d8[1] = AD7292_RD_FLAG_MSK(AD7292_REG_CONV_COMM);
|
||||
|
||||
ret = spi_sync_transfer(st->spi, t, ARRAY_SIZE(t));
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return be16_to_cpu(st->d16);
|
||||
}
|
||||
|
||||
static int ad7292_vin_range_multiplier(struct ad7292_state *st, int channel)
|
||||
{
|
||||
int samp_mode, range0, range1, factor = 1;
|
||||
|
||||
/*
|
||||
* Every AD7292 ADC channel may have its input range adjusted according
|
||||
* to the settings at the ADC sampling mode and VIN range subregisters.
|
||||
* For a given channel, the minimum input range is equal to Vref, and it
|
||||
* may be increased by a multiplier factor of 2 or 4 according to the
|
||||
* following rule:
|
||||
* If channel is being sampled with respect to AGND:
|
||||
* factor = 4 if VIN range0 and VIN range1 equal 0
|
||||
* factor = 2 if only one of VIN ranges equal 1
|
||||
* factor = 1 if both VIN range0 and VIN range1 equal 1
|
||||
* If channel is being sampled with respect to AVDD:
|
||||
* factor = 4 if VIN range0 and VIN range1 equal 0
|
||||
* Behavior is undefined if any of VIN range doesn't equal 0
|
||||
*/
|
||||
|
||||
samp_mode = ad7292_spi_subreg_read(st, AD7292_REG_CONF_BANK,
|
||||
AD7292_BANK_REG_SAMP_MODE, 2);
|
||||
|
||||
if (samp_mode < 0)
|
||||
return samp_mode;
|
||||
|
||||
range0 = ad7292_spi_subreg_read(st, AD7292_REG_CONF_BANK,
|
||||
AD7292_BANK_REG_VIN_RNG0, 2);
|
||||
|
||||
if (range0 < 0)
|
||||
return range0;
|
||||
|
||||
range1 = ad7292_spi_subreg_read(st, AD7292_REG_CONF_BANK,
|
||||
AD7292_BANK_REG_VIN_RNG1, 2);
|
||||
|
||||
if (range1 < 0)
|
||||
return range1;
|
||||
|
||||
if (AD7292_CH_SAMP_MODE(samp_mode, channel)) {
|
||||
/* Sampling with respect to AGND */
|
||||
if (!AD7292_CH_VIN_RANGE(range0, channel))
|
||||
factor *= 2;
|
||||
|
||||
if (!AD7292_CH_VIN_RANGE(range1, channel))
|
||||
factor *= 2;
|
||||
|
||||
} else {
|
||||
/* Sampling with respect to AVDD */
|
||||
if (AD7292_CH_VIN_RANGE(range0, channel) ||
|
||||
AD7292_CH_VIN_RANGE(range1, channel))
|
||||
return -EPERM;
|
||||
|
||||
factor = 4;
|
||||
}
|
||||
|
||||
return factor;
|
||||
}
|
||||
|
||||
static int ad7292_read_raw(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
int *val, int *val2, long info)
|
||||
{
|
||||
struct ad7292_state *st = iio_priv(indio_dev);
|
||||
unsigned int ch_addr;
|
||||
int ret;
|
||||
|
||||
switch (info) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ch_addr = AD7292_REG_ADC_CH(chan->channel);
|
||||
ret = ad7292_single_conversion(st, ch_addr);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = AD7292_ADC_DATA(ret);
|
||||
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
/*
|
||||
* To convert a raw value to standard units, the IIO defines
|
||||
* this formula: Scaled value = (raw + offset) * scale.
|
||||
* For the scale to be a correct multiplier for (raw + offset),
|
||||
* it must be calculated as the input range divided by the
|
||||
* number of possible distinct input values. Given the ADC data
|
||||
* is 10 bit long, it may assume 2^10 distinct values.
|
||||
* Hence, scale = range / 2^10. The IIO_VAL_FRACTIONAL_LOG2
|
||||
* return type indicates to the IIO API to divide *val by 2 to
|
||||
* the power of *val2 when returning from read_raw.
|
||||
*/
|
||||
|
||||
ret = ad7292_vin_range_multiplier(st, chan->channel);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = st->vref_mv * ret;
|
||||
*val2 = 10;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct iio_info ad7292_info = {
|
||||
.read_raw = ad7292_read_raw,
|
||||
};
|
||||
|
||||
static void ad7292_regulator_disable(void *data)
|
||||
{
|
||||
struct ad7292_state *st = data;
|
||||
|
||||
regulator_disable(st->reg);
|
||||
}
|
||||
|
||||
static int ad7292_probe(struct spi_device *spi)
|
||||
{
|
||||
struct ad7292_state *st;
|
||||
struct iio_dev *indio_dev;
|
||||
struct device_node *child;
|
||||
bool diff_channels = 0;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
st->spi = spi;
|
||||
|
||||
ret = ad7292_spi_reg_read(st, AD7292_REG_VENDOR_ID);
|
||||
if (ret != ADI_VENDOR_ID) {
|
||||
dev_err(&spi->dev, "Wrong vendor id 0x%x\n", ret);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
|
||||
st->reg = devm_regulator_get_optional(&spi->dev, "vref");
|
||||
if (!IS_ERR(st->reg)) {
|
||||
ret = regulator_enable(st->reg);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev,
|
||||
"Failed to enable external vref supply\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = devm_add_action_or_reset(&spi->dev,
|
||||
ad7292_regulator_disable, st);
|
||||
if (ret) {
|
||||
regulator_disable(st->reg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regulator_get_voltage(st->reg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
st->vref_mv = ret / 1000;
|
||||
} else {
|
||||
/* Use the internal voltage reference. */
|
||||
st->vref_mv = 1250;
|
||||
}
|
||||
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->name = spi_get_device_id(spi)->name;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &ad7292_info;
|
||||
|
||||
for_each_available_child_of_node(spi->dev.of_node, child) {
|
||||
diff_channels = of_property_read_bool(child, "diff-channels");
|
||||
if (diff_channels)
|
||||
break;
|
||||
}
|
||||
|
||||
if (diff_channels) {
|
||||
indio_dev->num_channels = ARRAY_SIZE(ad7292_channels_diff);
|
||||
indio_dev->channels = ad7292_channels_diff;
|
||||
} else {
|
||||
indio_dev->num_channels = ARRAY_SIZE(ad7292_channels);
|
||||
indio_dev->channels = ad7292_channels;
|
||||
}
|
||||
|
||||
return devm_iio_device_register(&spi->dev, indio_dev);
|
||||
}
|
||||
|
||||
static const struct spi_device_id ad7292_id_table[] = {
|
||||
{ "ad7292", 0 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ad7292_id_table);
|
||||
|
||||
static const struct of_device_id ad7292_of_match[] = {
|
||||
{ .compatible = "adi,ad7292" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ad7292_of_match);
|
||||
|
||||
static struct spi_driver ad7292_driver = {
|
||||
.driver = {
|
||||
.name = "ad7292",
|
||||
.of_match_table = ad7292_of_match,
|
||||
},
|
||||
.probe = ad7292_probe,
|
||||
.id_table = ad7292_id_table,
|
||||
};
|
||||
module_spi_driver(ad7292_driver);
|
||||
|
||||
MODULE_AUTHOR("Marcelo Schmitt <marcelo.schmitt1@gmail.com>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD7292 ADC driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -54,38 +54,20 @@ struct ad7949_adc_chip {
|
||||
u8 resolution;
|
||||
u16 cfg;
|
||||
unsigned int current_channel;
|
||||
u32 buffer ____cacheline_aligned;
|
||||
u16 buffer ____cacheline_aligned;
|
||||
};
|
||||
|
||||
static bool ad7949_spi_cfg_is_read_back(struct ad7949_adc_chip *ad7949_adc)
|
||||
{
|
||||
if (!(ad7949_adc->cfg & AD7949_CFG_READ_BACK))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int ad7949_spi_bits_per_word(struct ad7949_adc_chip *ad7949_adc)
|
||||
{
|
||||
int ret = ad7949_adc->resolution;
|
||||
|
||||
if (ad7949_spi_cfg_is_read_back(ad7949_adc))
|
||||
ret += AD7949_CFG_REG_SIZE_BITS;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad7949_spi_write_cfg(struct ad7949_adc_chip *ad7949_adc, u16 val,
|
||||
u16 mask)
|
||||
{
|
||||
int ret;
|
||||
int bits_per_word = ad7949_spi_bits_per_word(ad7949_adc);
|
||||
int bits_per_word = ad7949_adc->resolution;
|
||||
int shift = bits_per_word - AD7949_CFG_REG_SIZE_BITS;
|
||||
struct spi_message msg;
|
||||
struct spi_transfer tx[] = {
|
||||
{
|
||||
.tx_buf = &ad7949_adc->buffer,
|
||||
.len = 4,
|
||||
.len = 2,
|
||||
.bits_per_word = bits_per_word,
|
||||
},
|
||||
};
|
||||
@ -107,13 +89,13 @@ static int ad7949_spi_read_channel(struct ad7949_adc_chip *ad7949_adc, int *val,
|
||||
unsigned int channel)
|
||||
{
|
||||
int ret;
|
||||
int bits_per_word = ad7949_spi_bits_per_word(ad7949_adc);
|
||||
int bits_per_word = ad7949_adc->resolution;
|
||||
int mask = GENMASK(ad7949_adc->resolution, 0);
|
||||
struct spi_message msg;
|
||||
struct spi_transfer tx[] = {
|
||||
{
|
||||
.rx_buf = &ad7949_adc->buffer,
|
||||
.len = 4,
|
||||
.len = 2,
|
||||
.bits_per_word = bits_per_word,
|
||||
},
|
||||
};
|
||||
@ -138,10 +120,7 @@ static int ad7949_spi_read_channel(struct ad7949_adc_chip *ad7949_adc, int *val,
|
||||
|
||||
ad7949_adc->current_channel = channel;
|
||||
|
||||
if (ad7949_spi_cfg_is_read_back(ad7949_adc))
|
||||
*val = (ad7949_adc->buffer >> AD7949_CFG_REG_SIZE_BITS) & mask;
|
||||
else
|
||||
*val = ad7949_adc->buffer & mask;
|
||||
*val = ad7949_adc->buffer & mask;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -205,7 +205,7 @@ int ad_sd_reset(struct ad_sigma_delta *sigma_delta,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ad_sd_reset);
|
||||
|
||||
static int ad_sd_calibrate(struct ad_sigma_delta *sigma_delta,
|
||||
int ad_sd_calibrate(struct ad_sigma_delta *sigma_delta,
|
||||
unsigned int mode, unsigned int channel)
|
||||
{
|
||||
int ret;
|
||||
@ -242,6 +242,7 @@ out:
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ad_sd_calibrate);
|
||||
|
||||
/**
|
||||
* ad_sd_calibrate_all() - Performs channel calibration
|
||||
|
@ -173,7 +173,6 @@ static int aspeed_adc_probe(struct platform_device *pdev)
|
||||
struct iio_dev *indio_dev;
|
||||
struct aspeed_adc_data *data;
|
||||
const struct aspeed_adc_model_data *model_data;
|
||||
struct resource *res;
|
||||
const char *clk_parent_name;
|
||||
int ret;
|
||||
u32 adc_engine_control_reg_val;
|
||||
@ -185,8 +184,7 @@ static int aspeed_adc_probe(struct platform_device *pdev)
|
||||
data = iio_priv(indio_dev);
|
||||
data->dev = &pdev->dev;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
data->base = devm_ioremap_resource(&pdev->dev, res);
|
||||
data->base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(data->base))
|
||||
return PTR_ERR(data->base);
|
||||
|
||||
|
@ -1483,7 +1483,7 @@ dma_free_area:
|
||||
st->dma_st.rx_buf, st->dma_st.rx_dma_buf);
|
||||
dma_chan_disable:
|
||||
dma_release_channel(st->dma_st.dma_chan);
|
||||
st->dma_st.dma_chan = 0;
|
||||
st->dma_st.dma_chan = NULL;
|
||||
dma_exit:
|
||||
dev_info(&pdev->dev, "continuing without DMA support\n");
|
||||
}
|
||||
@ -1506,7 +1506,7 @@ static void at91_adc_dma_disable(struct platform_device *pdev)
|
||||
dma_free_coherent(st->dma_st.dma_chan->device->dev, pages * PAGE_SIZE,
|
||||
st->dma_st.rx_buf, st->dma_st.rx_dma_buf);
|
||||
dma_release_channel(st->dma_st.dma_chan);
|
||||
st->dma_st.dma_chan = 0;
|
||||
st->dma_st.dma_chan = NULL;
|
||||
|
||||
dev_info(&pdev->dev, "continuing without DMA support\n");
|
||||
}
|
||||
|
@ -308,7 +308,7 @@ static int iproc_adc_do_read(struct iio_dev *indio_dev,
|
||||
"IntMask set failed. Read will likely fail.");
|
||||
read_len = -EIO;
|
||||
goto adc_err;
|
||||
};
|
||||
}
|
||||
}
|
||||
regmap_read(adc_priv->regmap, IPROC_INTERRUPT_MASK, &val_check);
|
||||
|
||||
|
@ -310,7 +310,6 @@ static int cc10001_adc_probe(struct platform_device *pdev)
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
struct cc10001_adc_device *adc_dev;
|
||||
unsigned long adc_clk_rate;
|
||||
struct resource *res;
|
||||
struct iio_dev *indio_dev;
|
||||
unsigned long channel_map;
|
||||
int ret;
|
||||
@ -340,8 +339,7 @@ static int cc10001_adc_probe(struct platform_device *pdev)
|
||||
indio_dev->info = &cc10001_adc_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
adc_dev->reg_base = devm_ioremap_resource(&pdev->dev, res);
|
||||
adc_dev->reg_base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(adc_dev->reg_base)) {
|
||||
ret = PTR_ERR(adc_dev->reg_base);
|
||||
goto err_disable_reg;
|
||||
|
@ -1008,7 +1008,7 @@ static int cpcap_adc_probe(struct platform_device *pdev)
|
||||
|
||||
error = devm_request_threaded_irq(&pdev->dev, ddata->irq, NULL,
|
||||
cpcap_adc_irq_thread,
|
||||
IRQF_TRIGGER_NONE,
|
||||
IRQF_TRIGGER_NONE | IRQF_ONESHOT,
|
||||
"cpcap-adc", indio_dev);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev, "could not get irq: %i\n",
|
||||
|
@ -524,6 +524,10 @@ static int dln2_adc_triggered_buffer_postenable(struct iio_dev *indio_dev)
|
||||
u16 conflict;
|
||||
unsigned int trigger_chan;
|
||||
|
||||
ret = iio_triggered_buffer_postenable(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&dln2->mutex);
|
||||
|
||||
/* Enable ADC */
|
||||
@ -537,6 +541,7 @@ static int dln2_adc_triggered_buffer_postenable(struct iio_dev *indio_dev)
|
||||
(int)conflict);
|
||||
ret = -EBUSY;
|
||||
}
|
||||
iio_triggered_buffer_predisable(indio_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -550,6 +555,7 @@ static int dln2_adc_triggered_buffer_postenable(struct iio_dev *indio_dev)
|
||||
mutex_unlock(&dln2->mutex);
|
||||
if (ret < 0) {
|
||||
dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
|
||||
iio_triggered_buffer_predisable(indio_dev);
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
@ -557,12 +563,12 @@ static int dln2_adc_triggered_buffer_postenable(struct iio_dev *indio_dev)
|
||||
mutex_unlock(&dln2->mutex);
|
||||
}
|
||||
|
||||
return iio_triggered_buffer_postenable(indio_dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dln2_adc_triggered_buffer_predisable(struct iio_dev *indio_dev)
|
||||
{
|
||||
int ret;
|
||||
int ret, ret2;
|
||||
struct dln2_adc *dln2 = iio_priv(indio_dev);
|
||||
|
||||
mutex_lock(&dln2->mutex);
|
||||
@ -577,12 +583,14 @@ static int dln2_adc_triggered_buffer_predisable(struct iio_dev *indio_dev)
|
||||
ret = dln2_adc_set_port_enabled(dln2, false, NULL);
|
||||
|
||||
mutex_unlock(&dln2->mutex);
|
||||
if (ret < 0) {
|
||||
if (ret < 0)
|
||||
dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return iio_triggered_buffer_predisable(indio_dev);
|
||||
ret2 = iio_triggered_buffer_predisable(indio_dev);
|
||||
if (ret == 0)
|
||||
ret = ret2;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct iio_buffer_setup_ops dln2_adc_buffer_setup_ops = {
|
||||
|
@ -651,7 +651,7 @@ static irqreturn_t exynos_ts_isr(int irq, void *dev_id)
|
||||
input_sync(info->input);
|
||||
|
||||
usleep_range(1000, 1100);
|
||||
};
|
||||
}
|
||||
|
||||
writel(0, ADC_V1_CLRINTPNDNUP(info->regs));
|
||||
|
||||
@ -769,7 +769,6 @@ static int exynos_adc_probe(struct platform_device *pdev)
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct s3c2410_ts_mach_info *pdata = dev_get_platdata(&pdev->dev);
|
||||
struct iio_dev *indio_dev = NULL;
|
||||
struct resource *mem;
|
||||
bool has_ts = false;
|
||||
int ret = -ENODEV;
|
||||
int irq;
|
||||
@ -788,8 +787,7 @@ static int exynos_adc_probe(struct platform_device *pdev)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
info->regs = devm_ioremap_resource(&pdev->dev, mem);
|
||||
info->regs = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(info->regs))
|
||||
return PTR_ERR(info->regs);
|
||||
|
||||
|
@ -23,6 +23,7 @@
|
||||
|
||||
/* gain to pulse and scale conversion */
|
||||
#define HX711_GAIN_MAX 3
|
||||
#define HX711_RESET_GAIN 128
|
||||
|
||||
struct hx711_gain_to_scale {
|
||||
int gain;
|
||||
@ -185,8 +186,7 @@ static int hx711_wait_for_ready(struct hx711_data *hx711_data)
|
||||
|
||||
static int hx711_reset(struct hx711_data *hx711_data)
|
||||
{
|
||||
int ret;
|
||||
int val = gpiod_get_value(hx711_data->gpiod_dout);
|
||||
int val = hx711_wait_for_ready(hx711_data);
|
||||
|
||||
if (val) {
|
||||
/*
|
||||
@ -202,22 +202,10 @@ static int hx711_reset(struct hx711_data *hx711_data)
|
||||
msleep(10);
|
||||
gpiod_set_value(hx711_data->gpiod_pd_sck, 0);
|
||||
|
||||
ret = hx711_wait_for_ready(hx711_data);
|
||||
if (ret)
|
||||
return ret;
|
||||
/*
|
||||
* after a reset the gain is 128 so we do a dummy read
|
||||
* to set the gain for the next read
|
||||
*/
|
||||
ret = hx711_read(hx711_data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* after a dummy read we need to wait vor readiness
|
||||
* for not mixing gain pulses with the clock
|
||||
*/
|
||||
val = hx711_wait_for_ready(hx711_data);
|
||||
|
||||
/* after a reset the gain is 128 */
|
||||
hx711_data->gain_set = HX711_RESET_GAIN;
|
||||
}
|
||||
|
||||
return val;
|
||||
|
@ -25,9 +25,13 @@
|
||||
#define JZ_ADC_REG_ADSDAT 0x20
|
||||
#define JZ_ADC_REG_ADCLK 0x28
|
||||
|
||||
#define JZ_ADC_REG_ENABLE_PD BIT(7)
|
||||
#define JZ_ADC_REG_CFG_AUX_MD (BIT(0) | BIT(1))
|
||||
#define JZ_ADC_REG_CFG_BAT_MD BIT(4)
|
||||
#define JZ_ADC_REG_ADCLK_CLKDIV_LSB 0
|
||||
#define JZ_ADC_REG_ADCLK_CLKDIV10US_LSB 16
|
||||
#define JZ4725B_ADC_REG_ADCLK_CLKDIV10US_LSB 16
|
||||
#define JZ4770_ADC_REG_ADCLK_CLKDIV10US_LSB 8
|
||||
#define JZ4770_ADC_REG_ADCLK_CLKDIVMS_LSB 16
|
||||
|
||||
#define JZ_ADC_AUX_VREF 3300
|
||||
#define JZ_ADC_AUX_VREF_BITS 12
|
||||
@ -37,6 +41,8 @@
|
||||
#define JZ4725B_ADC_BATTERY_HIGH_VREF_BITS 10
|
||||
#define JZ4740_ADC_BATTERY_HIGH_VREF (7500 * 0.986)
|
||||
#define JZ4740_ADC_BATTERY_HIGH_VREF_BITS 12
|
||||
#define JZ4770_ADC_BATTERY_VREF 6600
|
||||
#define JZ4770_ADC_BATTERY_VREF_BITS 12
|
||||
|
||||
struct ingenic_adc;
|
||||
|
||||
@ -47,6 +53,8 @@ struct ingenic_adc_soc_data {
|
||||
size_t battery_raw_avail_size;
|
||||
const int *battery_scale_avail;
|
||||
size_t battery_scale_avail_size;
|
||||
unsigned int battery_vref_mode: 1;
|
||||
unsigned int has_aux2: 1;
|
||||
int (*init_clk_div)(struct device *dev, struct ingenic_adc *adc);
|
||||
};
|
||||
|
||||
@ -54,6 +62,7 @@ struct ingenic_adc {
|
||||
void __iomem *base;
|
||||
struct clk *clk;
|
||||
struct mutex lock;
|
||||
struct mutex aux_lock;
|
||||
const struct ingenic_adc_soc_data *soc_data;
|
||||
bool low_vref_mode;
|
||||
};
|
||||
@ -120,6 +129,8 @@ static int ingenic_adc_write_raw(struct iio_dev *iio_dev,
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
switch (chan->channel) {
|
||||
case INGENIC_ADC_BATTERY:
|
||||
if (!adc->soc_data->battery_vref_mode)
|
||||
return -EINVAL;
|
||||
if (val > JZ_ADC_BATTERY_LOW_VREF) {
|
||||
ingenic_adc_set_config(adc,
|
||||
JZ_ADC_REG_CFG_BAT_MD,
|
||||
@ -158,6 +169,14 @@ static const int jz4740_adc_battery_scale_avail[] = {
|
||||
JZ_ADC_BATTERY_LOW_VREF, JZ_ADC_BATTERY_LOW_VREF_BITS,
|
||||
};
|
||||
|
||||
static const int jz4770_adc_battery_raw_avail[] = {
|
||||
0, 1, (1 << JZ4770_ADC_BATTERY_VREF_BITS) - 1,
|
||||
};
|
||||
|
||||
static const int jz4770_adc_battery_scale_avail[] = {
|
||||
JZ4770_ADC_BATTERY_VREF, JZ4770_ADC_BATTERY_VREF_BITS,
|
||||
};
|
||||
|
||||
static int jz4725b_adc_init_clk_div(struct device *dev, struct ingenic_adc *adc)
|
||||
{
|
||||
struct clk *parent_clk;
|
||||
@ -187,7 +206,45 @@ static int jz4725b_adc_init_clk_div(struct device *dev, struct ingenic_adc *adc)
|
||||
/* We also need a divider that produces a 10us clock. */
|
||||
div_10us = DIV_ROUND_UP(rate, 100000);
|
||||
|
||||
writel(((div_10us - 1) << JZ_ADC_REG_ADCLK_CLKDIV10US_LSB) |
|
||||
writel(((div_10us - 1) << JZ4725B_ADC_REG_ADCLK_CLKDIV10US_LSB) |
|
||||
(div_main - 1) << JZ_ADC_REG_ADCLK_CLKDIV_LSB,
|
||||
adc->base + JZ_ADC_REG_ADCLK);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int jz4770_adc_init_clk_div(struct device *dev, struct ingenic_adc *adc)
|
||||
{
|
||||
struct clk *parent_clk;
|
||||
unsigned long parent_rate, rate;
|
||||
unsigned int div_main, div_ms, div_10us;
|
||||
|
||||
parent_clk = clk_get_parent(adc->clk);
|
||||
if (!parent_clk) {
|
||||
dev_err(dev, "ADC clock has no parent\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
parent_rate = clk_get_rate(parent_clk);
|
||||
|
||||
/*
|
||||
* The JZ4770 ADC works at 20 kHz to 200 kHz.
|
||||
* We pick the highest rate possible.
|
||||
*/
|
||||
div_main = DIV_ROUND_UP(parent_rate, 200000);
|
||||
div_main = clamp(div_main, 1u, 256u);
|
||||
rate = parent_rate / div_main;
|
||||
if (rate < 20000 || rate > 200000) {
|
||||
dev_err(dev, "No valid divider for ADC main clock\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* We also need a divider that produces a 10us clock. */
|
||||
div_10us = DIV_ROUND_UP(rate, 10000);
|
||||
/* And another, which produces a 1ms clock. */
|
||||
div_ms = DIV_ROUND_UP(rate, 1000);
|
||||
|
||||
writel(((div_ms - 1) << JZ4770_ADC_REG_ADCLK_CLKDIVMS_LSB) |
|
||||
((div_10us - 1) << JZ4770_ADC_REG_ADCLK_CLKDIV10US_LSB) |
|
||||
(div_main - 1) << JZ_ADC_REG_ADCLK_CLKDIV_LSB,
|
||||
adc->base + JZ_ADC_REG_ADCLK);
|
||||
|
||||
@ -201,6 +258,8 @@ static const struct ingenic_adc_soc_data jz4725b_adc_soc_data = {
|
||||
.battery_raw_avail_size = ARRAY_SIZE(jz4725b_adc_battery_raw_avail),
|
||||
.battery_scale_avail = jz4725b_adc_battery_scale_avail,
|
||||
.battery_scale_avail_size = ARRAY_SIZE(jz4725b_adc_battery_scale_avail),
|
||||
.battery_vref_mode = true,
|
||||
.has_aux2 = false,
|
||||
.init_clk_div = jz4725b_adc_init_clk_div,
|
||||
};
|
||||
|
||||
@ -211,9 +270,23 @@ static const struct ingenic_adc_soc_data jz4740_adc_soc_data = {
|
||||
.battery_raw_avail_size = ARRAY_SIZE(jz4740_adc_battery_raw_avail),
|
||||
.battery_scale_avail = jz4740_adc_battery_scale_avail,
|
||||
.battery_scale_avail_size = ARRAY_SIZE(jz4740_adc_battery_scale_avail),
|
||||
.battery_vref_mode = true,
|
||||
.has_aux2 = false,
|
||||
.init_clk_div = NULL, /* no ADCLK register on JZ4740 */
|
||||
};
|
||||
|
||||
static const struct ingenic_adc_soc_data jz4770_adc_soc_data = {
|
||||
.battery_high_vref = JZ4770_ADC_BATTERY_VREF,
|
||||
.battery_high_vref_bits = JZ4770_ADC_BATTERY_VREF_BITS,
|
||||
.battery_raw_avail = jz4770_adc_battery_raw_avail,
|
||||
.battery_raw_avail_size = ARRAY_SIZE(jz4770_adc_battery_raw_avail),
|
||||
.battery_scale_avail = jz4770_adc_battery_scale_avail,
|
||||
.battery_scale_avail_size = ARRAY_SIZE(jz4770_adc_battery_scale_avail),
|
||||
.battery_vref_mode = false,
|
||||
.has_aux2 = true,
|
||||
.init_clk_div = jz4770_adc_init_clk_div,
|
||||
};
|
||||
|
||||
static int ingenic_adc_read_avail(struct iio_dev *iio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
const int **vals,
|
||||
@ -239,6 +312,42 @@ static int ingenic_adc_read_avail(struct iio_dev *iio_dev,
|
||||
};
|
||||
}
|
||||
|
||||
static int ingenic_adc_read_chan_info_raw(struct ingenic_adc *adc,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val)
|
||||
{
|
||||
int bit, ret, engine = (chan->channel == INGENIC_ADC_BATTERY);
|
||||
|
||||
/* We cannot sample AUX/AUX2 in parallel. */
|
||||
mutex_lock(&adc->aux_lock);
|
||||
if (adc->soc_data->has_aux2 && engine == 0) {
|
||||
bit = BIT(chan->channel == INGENIC_ADC_AUX2);
|
||||
ingenic_adc_set_config(adc, JZ_ADC_REG_CFG_AUX_MD, bit);
|
||||
}
|
||||
|
||||
clk_enable(adc->clk);
|
||||
ret = ingenic_adc_capture(adc, engine);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
switch (chan->channel) {
|
||||
case INGENIC_ADC_AUX:
|
||||
case INGENIC_ADC_AUX2:
|
||||
*val = readw(adc->base + JZ_ADC_REG_ADSDAT);
|
||||
break;
|
||||
case INGENIC_ADC_BATTERY:
|
||||
*val = readw(adc->base + JZ_ADC_REG_ADBDAT);
|
||||
break;
|
||||
}
|
||||
|
||||
ret = IIO_VAL_INT;
|
||||
out:
|
||||
clk_disable(adc->clk);
|
||||
mutex_unlock(&adc->aux_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ingenic_adc_read_raw(struct iio_dev *iio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val,
|
||||
@ -246,32 +355,14 @@ static int ingenic_adc_read_raw(struct iio_dev *iio_dev,
|
||||
long m)
|
||||
{
|
||||
struct ingenic_adc *adc = iio_priv(iio_dev);
|
||||
int ret;
|
||||
|
||||
switch (m) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
clk_enable(adc->clk);
|
||||
ret = ingenic_adc_capture(adc, chan->channel);
|
||||
if (ret) {
|
||||
clk_disable(adc->clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
switch (chan->channel) {
|
||||
case INGENIC_ADC_AUX:
|
||||
*val = readw(adc->base + JZ_ADC_REG_ADSDAT);
|
||||
break;
|
||||
case INGENIC_ADC_BATTERY:
|
||||
*val = readw(adc->base + JZ_ADC_REG_ADBDAT);
|
||||
break;
|
||||
}
|
||||
|
||||
clk_disable(adc->clk);
|
||||
|
||||
return IIO_VAL_INT;
|
||||
return ingenic_adc_read_chan_info_raw(adc, chan, val);
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
switch (chan->channel) {
|
||||
case INGENIC_ADC_AUX:
|
||||
case INGENIC_ADC_AUX2:
|
||||
*val = JZ_ADC_AUX_VREF;
|
||||
*val2 = JZ_ADC_AUX_VREF_BITS;
|
||||
break;
|
||||
@ -322,6 +413,14 @@ static const struct iio_chan_spec ingenic_channels[] = {
|
||||
.indexed = 1,
|
||||
.channel = INGENIC_ADC_BATTERY,
|
||||
},
|
||||
{ /* Must always be last in the array. */
|
||||
.extend_name = "aux2",
|
||||
.type = IIO_VOLTAGE,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
.indexed = 1,
|
||||
.channel = INGENIC_ADC_AUX2,
|
||||
},
|
||||
};
|
||||
|
||||
static int ingenic_adc_probe(struct platform_device *pdev)
|
||||
@ -329,7 +428,6 @@ static int ingenic_adc_probe(struct platform_device *pdev)
|
||||
struct device *dev = &pdev->dev;
|
||||
struct iio_dev *iio_dev;
|
||||
struct ingenic_adc *adc;
|
||||
struct resource *mem_base;
|
||||
const struct ingenic_adc_soc_data *soc_data;
|
||||
int ret;
|
||||
|
||||
@ -343,10 +441,10 @@ static int ingenic_adc_probe(struct platform_device *pdev)
|
||||
|
||||
adc = iio_priv(iio_dev);
|
||||
mutex_init(&adc->lock);
|
||||
mutex_init(&adc->aux_lock);
|
||||
adc->soc_data = soc_data;
|
||||
|
||||
mem_base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
adc->base = devm_ioremap_resource(dev, mem_base);
|
||||
adc->base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(adc->base))
|
||||
return PTR_ERR(adc->base);
|
||||
|
||||
@ -374,6 +472,7 @@ static int ingenic_adc_probe(struct platform_device *pdev)
|
||||
/* Put hardware in a known passive state. */
|
||||
writeb(0x00, adc->base + JZ_ADC_REG_ENABLE);
|
||||
writeb(0xff, adc->base + JZ_ADC_REG_CTRL);
|
||||
usleep_range(2000, 3000); /* Must wait at least 2ms. */
|
||||
clk_disable(adc->clk);
|
||||
|
||||
ret = devm_add_action_or_reset(dev, ingenic_adc_clk_cleanup, adc->clk);
|
||||
@ -387,6 +486,9 @@ static int ingenic_adc_probe(struct platform_device *pdev)
|
||||
iio_dev->modes = INDIO_DIRECT_MODE;
|
||||
iio_dev->channels = ingenic_channels;
|
||||
iio_dev->num_channels = ARRAY_SIZE(ingenic_channels);
|
||||
/* Remove AUX2 from the list of supported channels. */
|
||||
if (!adc->soc_data->has_aux2)
|
||||
iio_dev->num_channels -= 1;
|
||||
iio_dev->info = &ingenic_adc_info;
|
||||
|
||||
ret = devm_iio_device_register(dev, iio_dev);
|
||||
@ -400,6 +502,7 @@ static int ingenic_adc_probe(struct platform_device *pdev)
|
||||
static const struct of_device_id ingenic_adc_of_match[] = {
|
||||
{ .compatible = "ingenic,jz4725b-adc", .data = &jz4725b_adc_soc_data, },
|
||||
{ .compatible = "ingenic,jz4740-adc", .data = &jz4740_adc_soc_data, },
|
||||
{ .compatible = "ingenic,jz4770-adc", .data = &jz4770_adc_soc_data, },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ingenic_adc_of_match);
|
||||
|
262
drivers/iio/adc/intel_mrfld_adc.c
Normal file
262
drivers/iio/adc/intel_mrfld_adc.c
Normal file
@ -0,0 +1,262 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* ADC driver for Basin Cove PMIC
|
||||
*
|
||||
* Copyright (C) 2012 Intel Corporation
|
||||
* Author: Bin Yang <bin.yang@intel.com>
|
||||
*
|
||||
* Rewritten for upstream by:
|
||||
* Vincent Pelletier <plr.vincent@gmail.com>
|
||||
* Andy Shevchenko <andriy.shevchenko@linux.intel.com>
|
||||
*/
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mfd/intel_soc_pmic.h>
|
||||
#include <linux/mfd/intel_soc_pmic_mrfld.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include <linux/iio/driver.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/machine.h>
|
||||
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#define BCOVE_GPADCREQ 0xDC
|
||||
#define BCOVE_GPADCREQ_BUSY BIT(0)
|
||||
#define BCOVE_GPADCREQ_IRQEN BIT(1)
|
||||
|
||||
#define BCOVE_ADCIRQ_ALL ( \
|
||||
BCOVE_ADCIRQ_BATTEMP | \
|
||||
BCOVE_ADCIRQ_SYSTEMP | \
|
||||
BCOVE_ADCIRQ_BATTID | \
|
||||
BCOVE_ADCIRQ_VIBATT | \
|
||||
BCOVE_ADCIRQ_CCTICK)
|
||||
|
||||
#define BCOVE_ADC_TIMEOUT msecs_to_jiffies(1000)
|
||||
|
||||
static const u8 mrfld_adc_requests[] = {
|
||||
BCOVE_ADCIRQ_VIBATT,
|
||||
BCOVE_ADCIRQ_BATTID,
|
||||
BCOVE_ADCIRQ_VIBATT,
|
||||
BCOVE_ADCIRQ_SYSTEMP,
|
||||
BCOVE_ADCIRQ_BATTEMP,
|
||||
BCOVE_ADCIRQ_BATTEMP,
|
||||
BCOVE_ADCIRQ_SYSTEMP,
|
||||
BCOVE_ADCIRQ_SYSTEMP,
|
||||
BCOVE_ADCIRQ_SYSTEMP,
|
||||
};
|
||||
|
||||
struct mrfld_adc {
|
||||
struct regmap *regmap;
|
||||
struct completion completion;
|
||||
/* Lock to protect the IPC transfers */
|
||||
struct mutex lock;
|
||||
};
|
||||
|
||||
static irqreturn_t mrfld_adc_thread_isr(int irq, void *data)
|
||||
{
|
||||
struct iio_dev *indio_dev = data;
|
||||
struct mrfld_adc *adc = iio_priv(indio_dev);
|
||||
|
||||
complete(&adc->completion);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int mrfld_adc_single_conv(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *result)
|
||||
{
|
||||
struct mrfld_adc *adc = iio_priv(indio_dev);
|
||||
struct regmap *regmap = adc->regmap;
|
||||
unsigned int req;
|
||||
long timeout;
|
||||
u8 buf[2];
|
||||
int ret;
|
||||
|
||||
reinit_completion(&adc->completion);
|
||||
|
||||
regmap_update_bits(regmap, BCOVE_MADCIRQ, BCOVE_ADCIRQ_ALL, 0);
|
||||
regmap_update_bits(regmap, BCOVE_MIRQLVL1, BCOVE_LVL1_ADC, 0);
|
||||
|
||||
ret = regmap_read_poll_timeout(regmap, BCOVE_GPADCREQ, req,
|
||||
!(req & BCOVE_GPADCREQ_BUSY),
|
||||
2000, 1000000);
|
||||
if (ret)
|
||||
goto done;
|
||||
|
||||
req = mrfld_adc_requests[chan->channel];
|
||||
ret = regmap_write(regmap, BCOVE_GPADCREQ, BCOVE_GPADCREQ_IRQEN | req);
|
||||
if (ret)
|
||||
goto done;
|
||||
|
||||
timeout = wait_for_completion_interruptible_timeout(&adc->completion,
|
||||
BCOVE_ADC_TIMEOUT);
|
||||
if (timeout < 0) {
|
||||
ret = timeout;
|
||||
goto done;
|
||||
}
|
||||
if (timeout == 0) {
|
||||
ret = -ETIMEDOUT;
|
||||
goto done;
|
||||
}
|
||||
|
||||
ret = regmap_bulk_read(regmap, chan->address, buf, 2);
|
||||
if (ret)
|
||||
goto done;
|
||||
|
||||
*result = get_unaligned_be16(buf);
|
||||
ret = IIO_VAL_INT;
|
||||
|
||||
done:
|
||||
regmap_update_bits(regmap, BCOVE_MIRQLVL1, BCOVE_LVL1_ADC, 0xff);
|
||||
regmap_update_bits(regmap, BCOVE_MADCIRQ, BCOVE_ADCIRQ_ALL, 0xff);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mrfld_adc_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct mrfld_adc *adc = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
mutex_lock(&adc->lock);
|
||||
ret = mrfld_adc_single_conv(indio_dev, chan, val);
|
||||
mutex_unlock(&adc->lock);
|
||||
return ret;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct iio_info mrfld_adc_iio_info = {
|
||||
.read_raw = &mrfld_adc_read_raw,
|
||||
};
|
||||
|
||||
#define BCOVE_ADC_CHANNEL(_type, _channel, _datasheet_name, _address) \
|
||||
{ \
|
||||
.indexed = 1, \
|
||||
.type = _type, \
|
||||
.channel = _channel, \
|
||||
.address = _address, \
|
||||
.datasheet_name = _datasheet_name, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec mrfld_adc_channels[] = {
|
||||
BCOVE_ADC_CHANNEL(IIO_VOLTAGE, 0, "CH0", 0xE9),
|
||||
BCOVE_ADC_CHANNEL(IIO_RESISTANCE, 1, "CH1", 0xEB),
|
||||
BCOVE_ADC_CHANNEL(IIO_CURRENT, 2, "CH2", 0xED),
|
||||
BCOVE_ADC_CHANNEL(IIO_TEMP, 3, "CH3", 0xCC),
|
||||
BCOVE_ADC_CHANNEL(IIO_TEMP, 4, "CH4", 0xC8),
|
||||
BCOVE_ADC_CHANNEL(IIO_TEMP, 5, "CH5", 0xCA),
|
||||
BCOVE_ADC_CHANNEL(IIO_TEMP, 6, "CH6", 0xC2),
|
||||
BCOVE_ADC_CHANNEL(IIO_TEMP, 7, "CH7", 0xC4),
|
||||
BCOVE_ADC_CHANNEL(IIO_TEMP, 8, "CH8", 0xC6),
|
||||
};
|
||||
|
||||
static struct iio_map iio_maps[] = {
|
||||
IIO_MAP("CH0", "bcove-battery", "VBATRSLT"),
|
||||
IIO_MAP("CH1", "bcove-battery", "BATTID"),
|
||||
IIO_MAP("CH2", "bcove-battery", "IBATRSLT"),
|
||||
IIO_MAP("CH3", "bcove-temp", "PMICTEMP"),
|
||||
IIO_MAP("CH4", "bcove-temp", "BATTEMP0"),
|
||||
IIO_MAP("CH5", "bcove-temp", "BATTEMP1"),
|
||||
IIO_MAP("CH6", "bcove-temp", "SYSTEMP0"),
|
||||
IIO_MAP("CH7", "bcove-temp", "SYSTEMP1"),
|
||||
IIO_MAP("CH8", "bcove-temp", "SYSTEMP2"),
|
||||
{}
|
||||
};
|
||||
|
||||
static int mrfld_adc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct intel_soc_pmic *pmic = dev_get_drvdata(dev->parent);
|
||||
struct iio_dev *indio_dev;
|
||||
struct mrfld_adc *adc;
|
||||
int irq;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(dev, sizeof(*indio_dev));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
adc = iio_priv(indio_dev);
|
||||
|
||||
mutex_init(&adc->lock);
|
||||
init_completion(&adc->completion);
|
||||
adc->regmap = pmic->regmap;
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
ret = devm_request_threaded_irq(dev, irq, NULL, mrfld_adc_thread_isr,
|
||||
IRQF_ONESHOT | IRQF_SHARED, pdev->name,
|
||||
indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
platform_set_drvdata(pdev, indio_dev);
|
||||
|
||||
indio_dev->dev.parent = dev;
|
||||
indio_dev->name = pdev->name;
|
||||
|
||||
indio_dev->channels = mrfld_adc_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(mrfld_adc_channels);
|
||||
indio_dev->info = &mrfld_adc_iio_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
ret = iio_map_array_register(indio_dev, iio_maps);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = devm_iio_device_register(dev, indio_dev);
|
||||
if (ret < 0)
|
||||
goto err_array_unregister;
|
||||
|
||||
return 0;
|
||||
|
||||
err_array_unregister:
|
||||
iio_map_array_unregister(indio_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mrfld_adc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
|
||||
iio_map_array_unregister(indio_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct platform_device_id mrfld_adc_id_table[] = {
|
||||
{ .name = "mrfld_bcove_adc" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, mrfld_adc_id_table);
|
||||
|
||||
static struct platform_driver mrfld_adc_driver = {
|
||||
.driver = {
|
||||
.name = "mrfld_bcove_adc",
|
||||
},
|
||||
.probe = mrfld_adc_probe,
|
||||
.remove = mrfld_adc_remove,
|
||||
.id_table = mrfld_adc_id_table,
|
||||
};
|
||||
module_platform_driver(mrfld_adc_driver);
|
||||
|
||||
MODULE_AUTHOR("Bin Yang <bin.yang@intel.com>");
|
||||
MODULE_AUTHOR("Vincent Pelletier <plr.vincent@gmail.com>");
|
||||
MODULE_AUTHOR("Andy Shevchenko <andriy.shevchenko@linux.intel.com>");
|
||||
MODULE_DESCRIPTION("ADC driver for Basin Cove PMIC");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -119,7 +119,6 @@ static int lpc18xx_adc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct lpc18xx_adc *adc;
|
||||
struct resource *res;
|
||||
unsigned int clkdiv;
|
||||
unsigned long rate;
|
||||
int ret;
|
||||
@ -133,8 +132,7 @@ static int lpc18xx_adc_probe(struct platform_device *pdev)
|
||||
adc->dev = &pdev->dev;
|
||||
mutex_init(&adc->lock);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
adc->base = devm_ioremap_resource(&pdev->dev, res);
|
||||
adc->base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(adc->base))
|
||||
return PTR_ERR(adc->base);
|
||||
|
||||
|
@ -63,12 +63,18 @@ enum max1027_id {
|
||||
max1027,
|
||||
max1029,
|
||||
max1031,
|
||||
max1227,
|
||||
max1229,
|
||||
max1231,
|
||||
};
|
||||
|
||||
static const struct spi_device_id max1027_id[] = {
|
||||
{"max1027", max1027},
|
||||
{"max1029", max1029},
|
||||
{"max1031", max1031},
|
||||
{"max1227", max1227},
|
||||
{"max1229", max1229},
|
||||
{"max1231", max1231},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, max1027_id);
|
||||
@ -78,12 +84,15 @@ static const struct of_device_id max1027_adc_dt_ids[] = {
|
||||
{ .compatible = "maxim,max1027" },
|
||||
{ .compatible = "maxim,max1029" },
|
||||
{ .compatible = "maxim,max1031" },
|
||||
{ .compatible = "maxim,max1227" },
|
||||
{ .compatible = "maxim,max1229" },
|
||||
{ .compatible = "maxim,max1231" },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, max1027_adc_dt_ids);
|
||||
#endif
|
||||
|
||||
#define MAX1027_V_CHAN(index) \
|
||||
#define MAX1027_V_CHAN(index, depth) \
|
||||
{ \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
@ -93,7 +102,7 @@ MODULE_DEVICE_TABLE(of, max1027_adc_dt_ids);
|
||||
.scan_index = index + 1, \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = 10, \
|
||||
.realbits = depth, \
|
||||
.storagebits = 16, \
|
||||
.shift = 2, \
|
||||
.endianness = IIO_BE, \
|
||||
@ -115,52 +124,54 @@ MODULE_DEVICE_TABLE(of, max1027_adc_dt_ids);
|
||||
}, \
|
||||
}
|
||||
|
||||
#define MAX1X27_CHANNELS(depth) \
|
||||
MAX1027_T_CHAN, \
|
||||
MAX1027_V_CHAN(0, depth), \
|
||||
MAX1027_V_CHAN(1, depth), \
|
||||
MAX1027_V_CHAN(2, depth), \
|
||||
MAX1027_V_CHAN(3, depth), \
|
||||
MAX1027_V_CHAN(4, depth), \
|
||||
MAX1027_V_CHAN(5, depth), \
|
||||
MAX1027_V_CHAN(6, depth), \
|
||||
MAX1027_V_CHAN(7, depth)
|
||||
|
||||
#define MAX1X29_CHANNELS(depth) \
|
||||
MAX1X27_CHANNELS(depth), \
|
||||
MAX1027_V_CHAN(8, depth), \
|
||||
MAX1027_V_CHAN(9, depth), \
|
||||
MAX1027_V_CHAN(10, depth), \
|
||||
MAX1027_V_CHAN(11, depth)
|
||||
|
||||
#define MAX1X31_CHANNELS(depth) \
|
||||
MAX1X27_CHANNELS(depth), \
|
||||
MAX1X29_CHANNELS(depth), \
|
||||
MAX1027_V_CHAN(12, depth), \
|
||||
MAX1027_V_CHAN(13, depth), \
|
||||
MAX1027_V_CHAN(14, depth), \
|
||||
MAX1027_V_CHAN(15, depth)
|
||||
|
||||
static const struct iio_chan_spec max1027_channels[] = {
|
||||
MAX1027_T_CHAN,
|
||||
MAX1027_V_CHAN(0),
|
||||
MAX1027_V_CHAN(1),
|
||||
MAX1027_V_CHAN(2),
|
||||
MAX1027_V_CHAN(3),
|
||||
MAX1027_V_CHAN(4),
|
||||
MAX1027_V_CHAN(5),
|
||||
MAX1027_V_CHAN(6),
|
||||
MAX1027_V_CHAN(7)
|
||||
MAX1X27_CHANNELS(10),
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec max1029_channels[] = {
|
||||
MAX1027_T_CHAN,
|
||||
MAX1027_V_CHAN(0),
|
||||
MAX1027_V_CHAN(1),
|
||||
MAX1027_V_CHAN(2),
|
||||
MAX1027_V_CHAN(3),
|
||||
MAX1027_V_CHAN(4),
|
||||
MAX1027_V_CHAN(5),
|
||||
MAX1027_V_CHAN(6),
|
||||
MAX1027_V_CHAN(7),
|
||||
MAX1027_V_CHAN(8),
|
||||
MAX1027_V_CHAN(9),
|
||||
MAX1027_V_CHAN(10),
|
||||
MAX1027_V_CHAN(11)
|
||||
MAX1X29_CHANNELS(10),
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec max1031_channels[] = {
|
||||
MAX1027_T_CHAN,
|
||||
MAX1027_V_CHAN(0),
|
||||
MAX1027_V_CHAN(1),
|
||||
MAX1027_V_CHAN(2),
|
||||
MAX1027_V_CHAN(3),
|
||||
MAX1027_V_CHAN(4),
|
||||
MAX1027_V_CHAN(5),
|
||||
MAX1027_V_CHAN(6),
|
||||
MAX1027_V_CHAN(7),
|
||||
MAX1027_V_CHAN(8),
|
||||
MAX1027_V_CHAN(9),
|
||||
MAX1027_V_CHAN(10),
|
||||
MAX1027_V_CHAN(11),
|
||||
MAX1027_V_CHAN(12),
|
||||
MAX1027_V_CHAN(13),
|
||||
MAX1027_V_CHAN(14),
|
||||
MAX1027_V_CHAN(15)
|
||||
MAX1X31_CHANNELS(10),
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec max1227_channels[] = {
|
||||
MAX1X27_CHANNELS(12),
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec max1229_channels[] = {
|
||||
MAX1X29_CHANNELS(12),
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec max1231_channels[] = {
|
||||
MAX1X31_CHANNELS(12),
|
||||
};
|
||||
|
||||
static const unsigned long max1027_available_scan_masks[] = {
|
||||
@ -200,6 +211,21 @@ static const struct max1027_chip_info max1027_chip_info_tbl[] = {
|
||||
.num_channels = ARRAY_SIZE(max1031_channels),
|
||||
.available_scan_masks = max1031_available_scan_masks,
|
||||
},
|
||||
[max1227] = {
|
||||
.channels = max1227_channels,
|
||||
.num_channels = ARRAY_SIZE(max1227_channels),
|
||||
.available_scan_masks = max1027_available_scan_masks,
|
||||
},
|
||||
[max1229] = {
|
||||
.channels = max1229_channels,
|
||||
.num_channels = ARRAY_SIZE(max1229_channels),
|
||||
.available_scan_masks = max1029_available_scan_masks,
|
||||
},
|
||||
[max1231] = {
|
||||
.channels = max1231_channels,
|
||||
.num_channels = ARRAY_SIZE(max1231_channels),
|
||||
.available_scan_masks = max1031_available_scan_masks,
|
||||
},
|
||||
};
|
||||
|
||||
struct max1027_state {
|
||||
@ -284,7 +310,7 @@ static int max1027_read_raw(struct iio_dev *indio_dev,
|
||||
break;
|
||||
case IIO_VOLTAGE:
|
||||
*val = 2500;
|
||||
*val2 = 10;
|
||||
*val2 = chan->scan_type.realbits;
|
||||
ret = IIO_VAL_FRACTIONAL_LOG2;
|
||||
break;
|
||||
default:
|
||||
@ -309,8 +335,11 @@ static int max1027_debugfs_reg_access(struct iio_dev *indio_dev,
|
||||
struct max1027_state *st = iio_priv(indio_dev);
|
||||
u8 *val = (u8 *)st->buffer;
|
||||
|
||||
if (readval != NULL)
|
||||
return -EINVAL;
|
||||
if (readval) {
|
||||
int ret = spi_read(st->spi, val, 2);
|
||||
*readval = be16_to_cpu(st->buffer[0]);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*val = (u8)writeval;
|
||||
return spi_write(st->spi, val, 1);
|
||||
@ -427,34 +456,47 @@ static int max1027_probe(struct spi_device *spi)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
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;
|
||||
if (spi->irq) {
|
||||
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;
|
||||
}
|
||||
|
||||
st->trig = devm_iio_trigger_alloc(&spi->dev, "%s-trigger",
|
||||
indio_dev->name);
|
||||
if (st->trig == NULL) {
|
||||
ret = -ENOMEM;
|
||||
dev_err(&indio_dev->dev,
|
||||
"Failed to allocate iio trigger\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
st->trig->ops = &max1027_trigger_ops;
|
||||
st->trig->dev.parent = &spi->dev;
|
||||
iio_trigger_set_drvdata(st->trig, indio_dev);
|
||||
iio_trigger_register(st->trig);
|
||||
|
||||
ret = devm_request_threaded_irq(&spi->dev, spi->irq,
|
||||
iio_trigger_generic_data_rdy_poll,
|
||||
NULL,
|
||||
IRQF_TRIGGER_FALLING,
|
||||
spi->dev.driver->name,
|
||||
st->trig);
|
||||
if (ret < 0) {
|
||||
dev_err(&indio_dev->dev, "Failed to allocate IRQ.\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
st->trig = devm_iio_trigger_alloc(&spi->dev, "%s-trigger",
|
||||
indio_dev->name);
|
||||
if (st->trig == NULL) {
|
||||
ret = -ENOMEM;
|
||||
dev_err(&indio_dev->dev, "Failed to allocate iio trigger\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
st->trig->ops = &max1027_trigger_ops;
|
||||
st->trig->dev.parent = &spi->dev;
|
||||
iio_trigger_set_drvdata(st->trig, indio_dev);
|
||||
iio_trigger_register(st->trig);
|
||||
|
||||
ret = devm_request_threaded_irq(&spi->dev, spi->irq,
|
||||
iio_trigger_generic_data_rdy_poll,
|
||||
NULL,
|
||||
IRQF_TRIGGER_FALLING,
|
||||
spi->dev.driver->name, st->trig);
|
||||
/* Internal reset */
|
||||
st->reg = MAX1027_RST_REG;
|
||||
ret = spi_write(st->spi, &st->reg, 1);
|
||||
if (ret < 0) {
|
||||
dev_err(&indio_dev->dev, "Failed to allocate IRQ.\n");
|
||||
dev_err(&indio_dev->dev, "Failed to reset the ADC\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -480,5 +522,5 @@ static struct spi_driver max1027_driver = {
|
||||
module_spi_driver(max1027_driver);
|
||||
|
||||
MODULE_AUTHOR("Philippe Reynes <tremyfr@yahoo.fr>");
|
||||
MODULE_DESCRIPTION("MAX1027/MAX1029/MAX1031 ADC");
|
||||
MODULE_DESCRIPTION("MAX1X27/MAX1X29/MAX1X31 ADC");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
@ -164,7 +164,7 @@ static int mcp320x_adc_conversion(struct mcp320x *adc, u8 channel,
|
||||
case mcp3550_60:
|
||||
case mcp3551:
|
||||
case mcp3553: {
|
||||
u32 raw = be32_to_cpup((u32 *)adc->rx_buf);
|
||||
u32 raw = be32_to_cpup((__be32 *)adc->rx_buf);
|
||||
|
||||
if (!(adc->spi->mode & SPI_CPOL))
|
||||
raw <<= 1; /* strip Data Ready bit in SPI mode 0,0 */
|
||||
|
@ -1187,7 +1187,6 @@ static int meson_sar_adc_probe(struct platform_device *pdev)
|
||||
const struct meson_sar_adc_data *match_data;
|
||||
struct meson_sar_adc_priv *priv;
|
||||
struct iio_dev *indio_dev;
|
||||
struct resource *res;
|
||||
void __iomem *base;
|
||||
int irq, ret;
|
||||
|
||||
@ -1214,8 +1213,7 @@ static int meson_sar_adc_probe(struct platform_device *pdev)
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &meson_sar_adc_iio_info;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
base = devm_ioremap_resource(&pdev->dev, res);
|
||||
base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
|
@ -237,7 +237,6 @@ static int mt6577_auxadc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct mt6577_auxadc_device *adc_dev;
|
||||
unsigned long adc_clk_rate;
|
||||
struct resource *res;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret;
|
||||
|
||||
@ -253,8 +252,7 @@ static int mt6577_auxadc_probe(struct platform_device *pdev)
|
||||
indio_dev->channels = mt6577_auxadc_iio_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(mt6577_auxadc_iio_channels);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
adc_dev->reg_base = devm_ioremap_resource(&pdev->dev, res);
|
||||
adc_dev->reg_base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(adc_dev->reg_base)) {
|
||||
dev_err(&pdev->dev, "failed to get auxadc base address\n");
|
||||
return PTR_ERR(adc_dev->reg_base);
|
||||
|
@ -183,7 +183,6 @@ static int npcm_adc_probe(struct platform_device *pdev)
|
||||
int irq;
|
||||
u32 div;
|
||||
u32 reg_con;
|
||||
struct resource *res;
|
||||
struct npcm_adc *info;
|
||||
struct iio_dev *indio_dev;
|
||||
struct device *dev = &pdev->dev;
|
||||
@ -196,8 +195,7 @@ static int npcm_adc_probe(struct platform_device *pdev)
|
||||
|
||||
info->dev = &pdev->dev;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
info->regs = devm_ioremap_resource(&pdev->dev, res);
|
||||
info->regs = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(info->regs))
|
||||
return PTR_ERR(info->regs);
|
||||
|
||||
|
@ -481,7 +481,6 @@ static int rcar_gyroadc_probe(struct platform_device *pdev)
|
||||
struct device *dev = &pdev->dev;
|
||||
struct rcar_gyroadc *priv;
|
||||
struct iio_dev *indio_dev;
|
||||
struct resource *mem;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(dev, sizeof(*priv));
|
||||
@ -491,8 +490,7 @@ static int rcar_gyroadc_probe(struct platform_device *pdev)
|
||||
priv = iio_priv(indio_dev);
|
||||
priv->dev = dev;
|
||||
|
||||
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
priv->regs = devm_ioremap_resource(dev, mem);
|
||||
priv->regs = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(priv->regs))
|
||||
return PTR_ERR(priv->regs);
|
||||
|
||||
|
@ -477,13 +477,6 @@ static void sc27xx_adc_disable(void *_data)
|
||||
SC27XX_MODULE_ADC_EN, 0);
|
||||
}
|
||||
|
||||
static void sc27xx_adc_free_hwlock(void *_data)
|
||||
{
|
||||
struct hwspinlock *hwlock = _data;
|
||||
|
||||
hwspin_lock_free(hwlock);
|
||||
}
|
||||
|
||||
static int sc27xx_adc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
@ -520,19 +513,12 @@ static int sc27xx_adc_probe(struct platform_device *pdev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
sc27xx_data->hwlock = hwspin_lock_request_specific(ret);
|
||||
sc27xx_data->hwlock = devm_hwspin_lock_request_specific(dev, ret);
|
||||
if (!sc27xx_data->hwlock) {
|
||||
dev_err(dev, "failed to request hwspinlock\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
ret = devm_add_action_or_reset(dev, sc27xx_adc_free_hwlock,
|
||||
sc27xx_data->hwlock);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to add hwspinlock action\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
sc27xx_data->dev = dev;
|
||||
|
||||
ret = sc27xx_adc_enable(sc27xx_data);
|
||||
|
@ -260,7 +260,6 @@ static int spear_adc_probe(struct platform_device *pdev)
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct spear_adc_state *st;
|
||||
struct resource *res;
|
||||
struct iio_dev *indio_dev = NULL;
|
||||
int ret = -ENODEV;
|
||||
int irq;
|
||||
@ -279,8 +278,7 @@ static int spear_adc_probe(struct platform_device *pdev)
|
||||
* (e.g. SPEAr3xx). Let's provide two register base addresses
|
||||
* to support multi-arch kernels.
|
||||
*/
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
st->adc_base_spear6xx = devm_ioremap_resource(&pdev->dev, res);
|
||||
st->adc_base_spear6xx = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(st->adc_base_spear6xx))
|
||||
return PTR_ERR(st->adc_base_spear6xx);
|
||||
|
||||
|
@ -38,12 +38,12 @@
|
||||
#define HAS_ANASWVDD BIT(1)
|
||||
|
||||
/**
|
||||
* stm32_adc_common_regs - stm32 common registers, compatible dependent data
|
||||
* struct stm32_adc_common_regs - stm32 common registers
|
||||
* @csr: common status register offset
|
||||
* @ccr: common control register offset
|
||||
* @eoc1: adc1 end of conversion flag in @csr
|
||||
* @eoc2: adc2 end of conversion flag in @csr
|
||||
* @eoc3: adc3 end of conversion flag in @csr
|
||||
* @eoc1_msk: adc1 end of conversion flag in @csr
|
||||
* @eoc2_msk: adc2 end of conversion flag in @csr
|
||||
* @eoc3_msk: adc3 end of conversion flag in @csr
|
||||
* @ier: interrupt enable register offset for each adc
|
||||
* @eocie_msk: end of conversion interrupt enable mask in @ier
|
||||
*/
|
||||
@ -60,7 +60,7 @@ struct stm32_adc_common_regs {
|
||||
struct stm32_adc_priv;
|
||||
|
||||
/**
|
||||
* stm32_adc_priv_cfg - stm32 core compatible configuration data
|
||||
* struct stm32_adc_priv_cfg - stm32 core compatible configuration data
|
||||
* @regs: common registers for all instances
|
||||
* @clk_sel: clock selection routine
|
||||
* @max_clk_rate_hz: maximum analog clock rate (Hz, from datasheet)
|
||||
@ -79,6 +79,7 @@ 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
|
||||
* @max_clk_rate: desired maximum clock rate
|
||||
* @booster: booster supply reference
|
||||
* @vdd: vdd supply reference
|
||||
* @vdda: vdda analog supply reference
|
||||
@ -95,6 +96,7 @@ struct stm32_adc_priv {
|
||||
struct irq_domain *domain;
|
||||
struct clk *aclk;
|
||||
struct clk *bclk;
|
||||
u32 max_clk_rate;
|
||||
struct regulator *booster;
|
||||
struct regulator *vdd;
|
||||
struct regulator *vdda;
|
||||
@ -117,6 +119,7 @@ static int stm32f4_pclk_div[] = {2, 4, 6, 8};
|
||||
|
||||
/**
|
||||
* stm32f4_adc_clk_sel() - Select stm32f4 ADC common clock prescaler
|
||||
* @pdev: platform device
|
||||
* @priv: stm32 ADC core private data
|
||||
* Select clock prescaler used for analog conversions, before using ADC.
|
||||
*/
|
||||
@ -140,7 +143,7 @@ static int stm32f4_adc_clk_sel(struct platform_device *pdev,
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(stm32f4_pclk_div); i++) {
|
||||
if ((rate / stm32f4_pclk_div[i]) <= priv->cfg->max_clk_rate_hz)
|
||||
if ((rate / stm32f4_pclk_div[i]) <= priv->max_clk_rate)
|
||||
break;
|
||||
}
|
||||
if (i >= ARRAY_SIZE(stm32f4_pclk_div)) {
|
||||
@ -229,7 +232,7 @@ static int stm32h7_adc_clk_sel(struct platform_device *pdev,
|
||||
if (ckmode)
|
||||
continue;
|
||||
|
||||
if ((rate / div) <= priv->cfg->max_clk_rate_hz)
|
||||
if ((rate / div) <= priv->max_clk_rate)
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
@ -249,7 +252,7 @@ static int stm32h7_adc_clk_sel(struct platform_device *pdev,
|
||||
if (!ckmode)
|
||||
continue;
|
||||
|
||||
if ((rate / div) <= priv->cfg->max_clk_rate_hz)
|
||||
if ((rate / div) <= priv->max_clk_rate)
|
||||
goto out;
|
||||
}
|
||||
|
||||
@ -654,6 +657,7 @@ static int stm32_adc_probe(struct platform_device *pdev)
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct resource *res;
|
||||
u32 max_rate;
|
||||
int ret;
|
||||
|
||||
if (!pdev->dev.of_node)
|
||||
@ -730,6 +734,13 @@ static int stm32_adc_probe(struct platform_device *pdev)
|
||||
priv->common.vref_mv = ret / 1000;
|
||||
dev_dbg(&pdev->dev, "vref+=%dmV\n", priv->common.vref_mv);
|
||||
|
||||
ret = of_property_read_u32(pdev->dev.of_node, "st,max-clk-rate-hz",
|
||||
&max_rate);
|
||||
if (!ret)
|
||||
priv->max_clk_rate = min(max_rate, priv->cfg->max_clk_rate_hz);
|
||||
else
|
||||
priv->max_clk_rate = priv->cfg->max_clk_rate_hz;
|
||||
|
||||
ret = priv->cfg->clk_sel(pdev, priv);
|
||||
if (ret < 0)
|
||||
goto err_hw_stop;
|
||||
|
@ -102,7 +102,7 @@ struct stm32_adc_calib {
|
||||
};
|
||||
|
||||
/**
|
||||
* stm32_adc_regs - stm32 ADC misc registers & bitfield desc
|
||||
* struct stm32_adc_regs - stm32 ADC misc registers & bitfield desc
|
||||
* @reg: register offset
|
||||
* @mask: bitfield mask
|
||||
* @shift: left shift
|
||||
@ -114,7 +114,7 @@ struct stm32_adc_regs {
|
||||
};
|
||||
|
||||
/**
|
||||
* stm32_adc_regspec - stm32 registers definition, compatible dependent data
|
||||
* struct stm32_adc_regspec - stm32 registers definition
|
||||
* @dr: data register offset
|
||||
* @ier_eoc: interrupt enable register & eocie bitfield
|
||||
* @isr_eoc: interrupt status register & eoc bitfield
|
||||
@ -140,7 +140,7 @@ struct stm32_adc_regspec {
|
||||
struct stm32_adc;
|
||||
|
||||
/**
|
||||
* stm32_adc_cfg - stm32 compatible configuration data
|
||||
* struct stm32_adc_cfg - stm32 compatible configuration data
|
||||
* @regs: registers descriptions
|
||||
* @adc_info: per instance input channels definitions
|
||||
* @trigs: external trigger sources
|
||||
@ -183,8 +183,8 @@ struct stm32_adc_cfg {
|
||||
* @rx_buf: dma rx buffer cpu address
|
||||
* @rx_dma_buf: dma rx buffer bus address
|
||||
* @rx_buf_sz: dma rx buffer size
|
||||
* @difsel bitmask to set single-ended/differential channel
|
||||
* @pcsel bitmask to preselect channels on some devices
|
||||
* @difsel: bitmask to set single-ended/differential channel
|
||||
* @pcsel: bitmask to preselect channels on some devices
|
||||
* @smpr_val: sampling time settings (e.g. smpr1 / smpr2)
|
||||
* @cal: optional calibration data on some devices
|
||||
* @chan_name: channel name array
|
||||
@ -254,7 +254,7 @@ static const struct stm32_adc_info stm32h7_adc_info = {
|
||||
.num_res = ARRAY_SIZE(stm32h7_adc_resolutions),
|
||||
};
|
||||
|
||||
/**
|
||||
/*
|
||||
* stm32f4_sq - describe regular sequence registers
|
||||
* - L: sequence len (register & bit field)
|
||||
* - SQ1..SQ16: sequence entries (register & bit field)
|
||||
@ -301,7 +301,7 @@ static struct stm32_adc_trig_info stm32f4_adc_trigs[] = {
|
||||
{}, /* sentinel */
|
||||
};
|
||||
|
||||
/**
|
||||
/*
|
||||
* stm32f4_smp_bits[] - describe sampling time register index & bit fields
|
||||
* Sorted so it can be indexed by channel number.
|
||||
*/
|
||||
@ -392,7 +392,7 @@ static struct stm32_adc_trig_info stm32h7_adc_trigs[] = {
|
||||
{},
|
||||
};
|
||||
|
||||
/**
|
||||
/*
|
||||
* stm32h7_smp_bits - describe sampling time register index & bit fields
|
||||
* Sorted so it can be indexed by channel number.
|
||||
*/
|
||||
@ -994,6 +994,7 @@ static int stm32_adc_conf_scan_seq(struct iio_dev *indio_dev,
|
||||
|
||||
/**
|
||||
* stm32_adc_get_trig_extsel() - Get external trigger selection
|
||||
* @indio_dev: IIO device structure
|
||||
* @trig: trigger
|
||||
*
|
||||
* Returns trigger extsel value, if trig matches, -EINVAL otherwise.
|
||||
@ -1297,6 +1298,10 @@ static int stm32_adc_of_xlate(struct iio_dev *indio_dev,
|
||||
|
||||
/**
|
||||
* stm32_adc_debugfs_reg_access - read or write register value
|
||||
* @indio_dev: IIO device structure
|
||||
* @reg: register offset
|
||||
* @writeval: value to write
|
||||
* @readval: value to read
|
||||
*
|
||||
* To read a value from an ADC register:
|
||||
* echo [ADC reg offset] > direct_reg_access
|
||||
|
@ -175,7 +175,7 @@ static int stmpe_read_raw(struct iio_dev *indio_dev,
|
||||
static irqreturn_t stmpe_adc_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct stmpe_adc *info = (struct stmpe_adc *)dev_id;
|
||||
u16 data;
|
||||
__be16 data;
|
||||
|
||||
if (info->channel <= STMPE_ADC_LAST_NR) {
|
||||
int int_sta;
|
||||
|
@ -495,7 +495,7 @@ static irqreturn_t twl4030_madc_threaded_irq_handler(int irq, void *_madc)
|
||||
ret = twl4030_madc_disable_irq(madc, i);
|
||||
if (ret < 0)
|
||||
dev_dbg(madc->dev, "Disable interrupt failed %d\n", i);
|
||||
madc->requests[i].result_pending = 1;
|
||||
madc->requests[i].result_pending = true;
|
||||
}
|
||||
for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) {
|
||||
r = &madc->requests[i];
|
||||
@ -507,8 +507,8 @@ static irqreturn_t twl4030_madc_threaded_irq_handler(int irq, void *_madc)
|
||||
len = twl4030_madc_read_channels(madc, method->rbase,
|
||||
r->channels, r->rbuf, r->raw);
|
||||
/* Free request */
|
||||
r->result_pending = 0;
|
||||
r->active = 0;
|
||||
r->result_pending = false;
|
||||
r->active = false;
|
||||
}
|
||||
mutex_unlock(&madc->lock);
|
||||
|
||||
@ -521,15 +521,15 @@ err_i2c:
|
||||
*/
|
||||
for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) {
|
||||
r = &madc->requests[i];
|
||||
if (r->active == 0)
|
||||
if (!r->active)
|
||||
continue;
|
||||
method = &twl4030_conversion_methods[r->method];
|
||||
/* Read results */
|
||||
len = twl4030_madc_read_channels(madc, method->rbase,
|
||||
r->channels, r->rbuf, r->raw);
|
||||
/* Free request */
|
||||
r->result_pending = 0;
|
||||
r->active = 0;
|
||||
r->result_pending = false;
|
||||
r->active = false;
|
||||
}
|
||||
mutex_unlock(&madc->lock);
|
||||
|
||||
@ -652,16 +652,16 @@ static int twl4030_madc_conversion(struct twl4030_madc_request *req)
|
||||
ret = twl4030_madc_start_conversion(twl4030_madc, req->method);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
twl4030_madc->requests[req->method].active = 1;
|
||||
twl4030_madc->requests[req->method].active = true;
|
||||
/* Wait until conversion is ready (ctrl register returns EOC) */
|
||||
ret = twl4030_madc_wait_conversion_ready(twl4030_madc, 5, method->ctrl);
|
||||
if (ret) {
|
||||
twl4030_madc->requests[req->method].active = 0;
|
||||
twl4030_madc->requests[req->method].active = false;
|
||||
goto out;
|
||||
}
|
||||
ret = twl4030_madc_read_channels(twl4030_madc, method->rbase,
|
||||
req->channels, req->rbuf, req->raw);
|
||||
twl4030_madc->requests[req->method].active = 0;
|
||||
twl4030_madc->requests[req->method].active = false;
|
||||
|
||||
out:
|
||||
mutex_unlock(&twl4030_madc->lock);
|
||||
|
@ -802,7 +802,6 @@ static int vf610_adc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct vf610_adc *info;
|
||||
struct iio_dev *indio_dev;
|
||||
struct resource *mem;
|
||||
int irq;
|
||||
int ret;
|
||||
|
||||
@ -815,8 +814,7 @@ static int vf610_adc_probe(struct platform_device *pdev)
|
||||
info = iio_priv(indio_dev);
|
||||
info->dev = &pdev->dev;
|
||||
|
||||
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
info->regs = devm_ioremap_resource(&pdev->dev, mem);
|
||||
info->regs = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(info->regs))
|
||||
return PTR_ERR(info->regs);
|
||||
|
||||
|
@ -1150,7 +1150,6 @@ static int xadc_probe(struct platform_device *pdev)
|
||||
const struct of_device_id *id;
|
||||
struct iio_dev *indio_dev;
|
||||
unsigned int bipolar_mask;
|
||||
struct resource *mem;
|
||||
unsigned int conf0;
|
||||
struct xadc *xadc;
|
||||
int ret;
|
||||
@ -1180,8 +1179,7 @@ static int xadc_probe(struct platform_device *pdev)
|
||||
spin_lock_init(&xadc->lock);
|
||||
INIT_DELAYED_WORK(&xadc->zynq_unmask_work, xadc_zynq_unmask_worker);
|
||||
|
||||
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
xadc->base = devm_ioremap_resource(&pdev->dev, mem);
|
||||
xadc->base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(xadc->base))
|
||||
return PTR_ERR(xadc->base);
|
||||
|
||||
|
@ -323,16 +323,16 @@ static int atlas_buffer_predisable(struct iio_dev *indio_dev)
|
||||
struct atlas_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = iio_triggered_buffer_predisable(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = atlas_set_interrupt(data, false);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pm_runtime_mark_last_busy(&data->client->dev);
|
||||
return pm_runtime_put_autosuspend(&data->client->dev);
|
||||
ret = pm_runtime_put_autosuspend(&data->client->dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return iio_triggered_buffer_predisable(indio_dev);
|
||||
}
|
||||
|
||||
static const struct iio_trigger_ops atlas_interrupt_trigger_ops = {
|
||||
|
@ -483,7 +483,7 @@ static void sgp_init(struct sgp_data *data)
|
||||
data->iaq_defval_skip_jiffies =
|
||||
43 * data->measure_interval_jiffies;
|
||||
break;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
static const struct iio_info sgp_info = {
|
||||
|
@ -117,7 +117,7 @@ static int sps30_do_cmd(struct sps30_state *state, u16 cmd, u8 *data, int size)
|
||||
break;
|
||||
case SPS30_READ_AUTO_CLEANING_PERIOD:
|
||||
buf[0] = SPS30_AUTO_CLEANING_PERIOD >> 8;
|
||||
buf[1] = (u8)SPS30_AUTO_CLEANING_PERIOD;
|
||||
buf[1] = (u8)(SPS30_AUTO_CLEANING_PERIOD & 0xff);
|
||||
/* fall through */
|
||||
case SPS30_READ_DATA_READY_FLAG:
|
||||
case SPS30_READ_DATA:
|
||||
|
@ -60,8 +60,8 @@ config AD5446
|
||||
help
|
||||
Say yes here to build support for Analog Devices AD5300, AD5301, AD5310,
|
||||
AD5311, AD5320, AD5321, AD5444, AD5446, AD5450, AD5451, AD5452, AD5453,
|
||||
AD5512A, AD5541A, AD5542A, AD5543, AD5553, AD5601, AD5602, AD5611, AD5612,
|
||||
AD5620, AD5621, AD5622, AD5640, AD5641, AD5660, AD5662 DACs
|
||||
AD5512A, AD5541A, AD5542A, AD5543, AD5553, AD5600, AD5601, AD5602, AD5611,
|
||||
AD5612, AD5620, AD5621, AD5622, AD5640, AD5641, AD5660, AD5662 DACs
|
||||
as well as Texas Instruments DAC081S101, DAC101S101, DAC121S101.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
|
@ -327,6 +327,7 @@ enum ad5446_supported_spi_device_ids {
|
||||
ID_AD5541A,
|
||||
ID_AD5512A,
|
||||
ID_AD5553,
|
||||
ID_AD5600,
|
||||
ID_AD5601,
|
||||
ID_AD5611,
|
||||
ID_AD5621,
|
||||
@ -381,6 +382,10 @@ static const struct ad5446_chip_info ad5446_spi_chip_info[] = {
|
||||
.channel = AD5446_CHANNEL(14, 16, 0),
|
||||
.write = ad5446_write,
|
||||
},
|
||||
[ID_AD5600] = {
|
||||
.channel = AD5446_CHANNEL(16, 16, 0),
|
||||
.write = ad5446_write,
|
||||
},
|
||||
[ID_AD5601] = {
|
||||
.channel = AD5446_CHANNEL_POWERDOWN(8, 16, 6),
|
||||
.write = ad5446_write,
|
||||
@ -448,6 +453,7 @@ static const struct spi_device_id ad5446_spi_ids[] = {
|
||||
{"ad5542a", ID_AD5541A}, /* ad5541a and ad5542a are compatible */
|
||||
{"ad5543", ID_AD5541A}, /* ad5541a and ad5543 are compatible */
|
||||
{"ad5553", ID_AD5553},
|
||||
{"ad5600", ID_AD5600},
|
||||
{"ad5601", ID_AD5601},
|
||||
{"ad5611", ID_AD5611},
|
||||
{"ad5621", ID_AD5621},
|
||||
|
@ -41,6 +41,7 @@ struct ad7303_state {
|
||||
struct regulator *vdd_reg;
|
||||
struct regulator *vref_reg;
|
||||
|
||||
struct mutex lock;
|
||||
/*
|
||||
* DMA (thus cache coherency maintenance) requires the
|
||||
* transfer buffers to live in their own cache lines.
|
||||
@ -79,7 +80,7 @@ static ssize_t ad7303_write_dac_powerdown(struct iio_dev *indio_dev,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
mutex_lock(&st->lock);
|
||||
|
||||
if (pwr_down)
|
||||
st->config |= AD7303_CFG_POWER_DOWN(chan->channel);
|
||||
@ -90,7 +91,7 @@ static ssize_t ad7303_write_dac_powerdown(struct iio_dev *indio_dev,
|
||||
* mode, so just write one of the DAC channels again */
|
||||
ad7303_write(st, chan->channel, st->dac_cache[chan->channel]);
|
||||
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
mutex_unlock(&st->lock);
|
||||
return len;
|
||||
}
|
||||
|
||||
@ -116,7 +117,9 @@ static int ad7303_read_raw(struct iio_dev *indio_dev,
|
||||
|
||||
switch (info) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
mutex_lock(&st->lock);
|
||||
*val = st->dac_cache[chan->channel];
|
||||
mutex_unlock(&st->lock);
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
vref_uv = ad7303_get_vref(st, chan);
|
||||
@ -144,11 +147,11 @@ static int ad7303_write_raw(struct iio_dev *indio_dev,
|
||||
if (val >= (1 << chan->scan_type.realbits) || val < 0)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
mutex_lock(&st->lock);
|
||||
ret = ad7303_write(st, chan->address, val);
|
||||
if (ret == 0)
|
||||
st->dac_cache[chan->channel] = val;
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
mutex_unlock(&st->lock);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
@ -211,6 +214,8 @@ static int ad7303_probe(struct spi_device *spi)
|
||||
|
||||
st->spi = spi;
|
||||
|
||||
mutex_init(&st->lock);
|
||||
|
||||
st->vdd_reg = devm_regulator_get(&spi->dev, "Vdd");
|
||||
if (IS_ERR(st->vdd_reg))
|
||||
return PTR_ERR(st->vdd_reg);
|
||||
|
@ -106,7 +106,6 @@ static int lpc18xx_dac_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct lpc18xx_dac *dac;
|
||||
struct resource *res;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*dac));
|
||||
@ -117,8 +116,7 @@ static int lpc18xx_dac_probe(struct platform_device *pdev)
|
||||
dac = iio_priv(indio_dev);
|
||||
mutex_init(&dac->lock);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
dac->base = devm_ioremap_resource(&pdev->dev, res);
|
||||
dac->base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(dac->base))
|
||||
return PTR_ERR(dac->base);
|
||||
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include <linux/delay.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/reset.h>
|
||||
|
||||
@ -50,6 +51,41 @@ static const struct regmap_config stm32_dac_regmap_cfg = {
|
||||
.max_register = 0x3fc,
|
||||
};
|
||||
|
||||
static int stm32_dac_core_hw_start(struct device *dev)
|
||||
{
|
||||
struct stm32_dac_common *common = dev_get_drvdata(dev);
|
||||
struct stm32_dac_priv *priv = to_stm32_dac_priv(common);
|
||||
int ret;
|
||||
|
||||
ret = regulator_enable(priv->vref);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "vref enable failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(priv->pclk);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "pclk enable failed: %d\n", ret);
|
||||
goto err_regulator_disable;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_regulator_disable:
|
||||
regulator_disable(priv->vref);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void stm32_dac_core_hw_stop(struct device *dev)
|
||||
{
|
||||
struct stm32_dac_common *common = dev_get_drvdata(dev);
|
||||
struct stm32_dac_priv *priv = to_stm32_dac_priv(common);
|
||||
|
||||
clk_disable_unprepare(priv->pclk);
|
||||
regulator_disable(priv->vref);
|
||||
}
|
||||
|
||||
static int stm32_dac_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
@ -66,6 +102,8 @@ static int stm32_dac_probe(struct platform_device *pdev)
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
platform_set_drvdata(pdev, &priv->common);
|
||||
|
||||
cfg = (const struct stm32_dac_cfg *)
|
||||
of_match_device(dev->driver->of_match_table, dev)->data;
|
||||
|
||||
@ -74,11 +112,19 @@ static int stm32_dac_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(mmio))
|
||||
return PTR_ERR(mmio);
|
||||
|
||||
regmap = devm_regmap_init_mmio(dev, mmio, &stm32_dac_regmap_cfg);
|
||||
regmap = devm_regmap_init_mmio_clk(dev, "pclk", mmio,
|
||||
&stm32_dac_regmap_cfg);
|
||||
if (IS_ERR(regmap))
|
||||
return PTR_ERR(regmap);
|
||||
priv->common.regmap = regmap;
|
||||
|
||||
priv->pclk = devm_clk_get(dev, "pclk");
|
||||
if (IS_ERR(priv->pclk)) {
|
||||
ret = PTR_ERR(priv->pclk);
|
||||
dev_err(dev, "pclk get failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
priv->vref = devm_regulator_get(dev, "vref");
|
||||
if (IS_ERR(priv->vref)) {
|
||||
ret = PTR_ERR(priv->vref);
|
||||
@ -86,33 +132,22 @@ static int stm32_dac_probe(struct platform_device *pdev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regulator_enable(priv->vref);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "vref enable failed\n");
|
||||
return ret;
|
||||
}
|
||||
pm_runtime_get_noresume(dev);
|
||||
pm_runtime_set_active(dev);
|
||||
pm_runtime_enable(dev);
|
||||
|
||||
ret = stm32_dac_core_hw_start(dev);
|
||||
if (ret)
|
||||
goto err_pm_stop;
|
||||
|
||||
ret = regulator_get_voltage(priv->vref);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "vref get voltage failed, %d\n", ret);
|
||||
goto err_vref;
|
||||
goto err_hw_stop;
|
||||
}
|
||||
priv->common.vref_mv = ret / 1000;
|
||||
dev_dbg(dev, "vref+=%dmV\n", priv->common.vref_mv);
|
||||
|
||||
priv->pclk = devm_clk_get(dev, "pclk");
|
||||
if (IS_ERR(priv->pclk)) {
|
||||
ret = PTR_ERR(priv->pclk);
|
||||
dev_err(dev, "pclk get failed\n");
|
||||
goto err_vref;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(priv->pclk);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "pclk enable failed\n");
|
||||
goto err_vref;
|
||||
}
|
||||
|
||||
priv->rst = devm_reset_control_get_exclusive(dev, NULL);
|
||||
if (!IS_ERR(priv->rst)) {
|
||||
reset_control_assert(priv->rst);
|
||||
@ -128,39 +163,79 @@ static int stm32_dac_probe(struct platform_device *pdev)
|
||||
priv->common.hfsel ?
|
||||
STM32H7_DAC_CR_HFSEL : 0);
|
||||
if (ret)
|
||||
goto err_pclk;
|
||||
goto err_hw_stop;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, &priv->common);
|
||||
|
||||
ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, dev);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to populate DT children\n");
|
||||
goto err_pclk;
|
||||
goto err_hw_stop;
|
||||
}
|
||||
|
||||
pm_runtime_put(dev);
|
||||
|
||||
return 0;
|
||||
|
||||
err_pclk:
|
||||
clk_disable_unprepare(priv->pclk);
|
||||
err_vref:
|
||||
regulator_disable(priv->vref);
|
||||
err_hw_stop:
|
||||
stm32_dac_core_hw_stop(dev);
|
||||
err_pm_stop:
|
||||
pm_runtime_disable(dev);
|
||||
pm_runtime_set_suspended(dev);
|
||||
pm_runtime_put_noidle(dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int stm32_dac_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct stm32_dac_common *common = platform_get_drvdata(pdev);
|
||||
struct stm32_dac_priv *priv = to_stm32_dac_priv(common);
|
||||
|
||||
pm_runtime_get_sync(&pdev->dev);
|
||||
of_platform_depopulate(&pdev->dev);
|
||||
clk_disable_unprepare(priv->pclk);
|
||||
regulator_disable(priv->vref);
|
||||
stm32_dac_core_hw_stop(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
pm_runtime_set_suspended(&pdev->dev);
|
||||
pm_runtime_put_noidle(&pdev->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused stm32_dac_core_resume(struct device *dev)
|
||||
{
|
||||
struct stm32_dac_common *common = dev_get_drvdata(dev);
|
||||
struct stm32_dac_priv *priv = to_stm32_dac_priv(common);
|
||||
int ret;
|
||||
|
||||
if (priv->common.hfsel) {
|
||||
/* restore hfsel (maybe lost under low power state) */
|
||||
ret = regmap_update_bits(priv->common.regmap, STM32_DAC_CR,
|
||||
STM32H7_DAC_CR_HFSEL,
|
||||
STM32H7_DAC_CR_HFSEL);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return pm_runtime_force_resume(dev);
|
||||
}
|
||||
|
||||
static int __maybe_unused stm32_dac_core_runtime_suspend(struct device *dev)
|
||||
{
|
||||
stm32_dac_core_hw_stop(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused stm32_dac_core_runtime_resume(struct device *dev)
|
||||
{
|
||||
return stm32_dac_core_hw_start(dev);
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops stm32_dac_core_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, stm32_dac_core_resume)
|
||||
SET_RUNTIME_PM_OPS(stm32_dac_core_runtime_suspend,
|
||||
stm32_dac_core_runtime_resume,
|
||||
NULL)
|
||||
};
|
||||
|
||||
static const struct stm32_dac_cfg stm32h7_dac_cfg = {
|
||||
.has_hfsel = true,
|
||||
};
|
||||
@ -182,6 +257,7 @@ static struct platform_driver stm32_dac_driver = {
|
||||
.driver = {
|
||||
.name = "stm32-dac-core",
|
||||
.of_match_table = stm32_dac_of_match,
|
||||
.pm = &stm32_dac_core_pm_ops,
|
||||
},
|
||||
};
|
||||
module_platform_driver(stm32_dac_driver);
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include "stm32-dac-core.h"
|
||||
|
||||
@ -20,6 +21,8 @@
|
||||
#define STM32_DAC_CHANNEL_2 2
|
||||
#define STM32_DAC_IS_CHAN_1(ch) ((ch) & STM32_DAC_CHANNEL_1)
|
||||
|
||||
#define STM32_DAC_AUTO_SUSPEND_DELAY_MS 2000
|
||||
|
||||
/**
|
||||
* struct stm32_dac - private data of DAC driver
|
||||
* @common: reference to DAC common data
|
||||
@ -49,15 +52,34 @@ static int stm32_dac_set_enable_state(struct iio_dev *indio_dev, int ch,
|
||||
bool enable)
|
||||
{
|
||||
struct stm32_dac *dac = iio_priv(indio_dev);
|
||||
struct device *dev = indio_dev->dev.parent;
|
||||
u32 msk = STM32_DAC_IS_CHAN_1(ch) ? STM32_DAC_CR_EN1 : STM32_DAC_CR_EN2;
|
||||
u32 en = enable ? msk : 0;
|
||||
int ret;
|
||||
|
||||
/* already enabled / disabled ? */
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
ret = stm32_dac_is_enabled(indio_dev, ch);
|
||||
if (ret < 0 || enable == !!ret) {
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return ret < 0 ? ret : 0;
|
||||
}
|
||||
|
||||
if (enable) {
|
||||
ret = pm_runtime_get_sync(dev);
|
||||
if (ret < 0) {
|
||||
pm_runtime_put_noidle(dev);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = regmap_update_bits(dac->common->regmap, STM32_DAC_CR, msk, en);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
if (ret < 0) {
|
||||
dev_err(&indio_dev->dev, "%s failed\n", en ?
|
||||
"Enable" : "Disable");
|
||||
return ret;
|
||||
goto err_put_pm;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -68,7 +90,20 @@ static int stm32_dac_set_enable_state(struct iio_dev *indio_dev, int ch,
|
||||
if (en && dac->common->hfsel)
|
||||
udelay(1);
|
||||
|
||||
if (!enable) {
|
||||
pm_runtime_mark_last_busy(dev);
|
||||
pm_runtime_put_autosuspend(dev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_put_pm:
|
||||
if (enable) {
|
||||
pm_runtime_mark_last_busy(dev);
|
||||
pm_runtime_put_autosuspend(dev);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int stm32_dac_get_value(struct stm32_dac *dac, int channel, int *val)
|
||||
@ -272,6 +307,7 @@ static int stm32_dac_chan_of_init(struct iio_dev *indio_dev)
|
||||
static int stm32_dac_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct iio_dev *indio_dev;
|
||||
struct stm32_dac *dac;
|
||||
int ret;
|
||||
@ -296,9 +332,61 @@ static int stm32_dac_probe(struct platform_device *pdev)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return devm_iio_device_register(&pdev->dev, indio_dev);
|
||||
/* Get stm32-dac-core PM online */
|
||||
pm_runtime_get_noresume(dev);
|
||||
pm_runtime_set_active(dev);
|
||||
pm_runtime_set_autosuspend_delay(dev, STM32_DAC_AUTO_SUSPEND_DELAY_MS);
|
||||
pm_runtime_use_autosuspend(dev);
|
||||
pm_runtime_enable(dev);
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto err_pm_put;
|
||||
|
||||
pm_runtime_mark_last_busy(dev);
|
||||
pm_runtime_put_autosuspend(dev);
|
||||
|
||||
return 0;
|
||||
|
||||
err_pm_put:
|
||||
pm_runtime_disable(dev);
|
||||
pm_runtime_set_suspended(dev);
|
||||
pm_runtime_put_noidle(dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int stm32_dac_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
|
||||
pm_runtime_get_sync(&pdev->dev);
|
||||
iio_device_unregister(indio_dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
pm_runtime_set_suspended(&pdev->dev);
|
||||
pm_runtime_put_noidle(&pdev->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused stm32_dac_suspend(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
int channel = indio_dev->channels[0].channel;
|
||||
int ret;
|
||||
|
||||
/* Ensure DAC is disabled before suspend */
|
||||
ret = stm32_dac_is_enabled(indio_dev, channel);
|
||||
if (ret)
|
||||
return ret < 0 ? ret : -EBUSY;
|
||||
|
||||
return pm_runtime_force_suspend(dev);
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops stm32_dac_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(stm32_dac_suspend, pm_runtime_force_resume)
|
||||
};
|
||||
|
||||
static const struct of_device_id stm32_dac_of_match[] = {
|
||||
{ .compatible = "st,stm32-dac", },
|
||||
{},
|
||||
@ -307,9 +395,11 @@ MODULE_DEVICE_TABLE(of, stm32_dac_of_match);
|
||||
|
||||
static struct platform_driver stm32_dac_driver = {
|
||||
.probe = stm32_dac_probe,
|
||||
.remove = stm32_dac_remove,
|
||||
.driver = {
|
||||
.name = "stm32-dac",
|
||||
.of_match_table = stm32_dac_of_match,
|
||||
.pm = &stm32_dac_pm_ops,
|
||||
},
|
||||
};
|
||||
module_platform_driver(stm32_dac_driver);
|
||||
|
@ -172,7 +172,6 @@ static int vf610_dac_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct vf610_dac *info;
|
||||
struct resource *mem;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev,
|
||||
@ -185,8 +184,7 @@ static int vf610_dac_probe(struct platform_device *pdev)
|
||||
info = iio_priv(indio_dev);
|
||||
info->dev = &pdev->dev;
|
||||
|
||||
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
info->regs = devm_ioremap_resource(&pdev->dev, mem);
|
||||
info->regs = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(info->regs))
|
||||
return PTR_ERR(info->regs);
|
||||
|
||||
|
@ -38,10 +38,12 @@ struct adis16080_chip_info {
|
||||
* @us: actual spi_device to write data
|
||||
* @info: chip specific parameters
|
||||
* @buf: transmit or receive buffer
|
||||
* @lock lock to protect buffer during reads
|
||||
**/
|
||||
struct adis16080_state {
|
||||
struct spi_device *us;
|
||||
const struct adis16080_chip_info *info;
|
||||
struct mutex lock;
|
||||
|
||||
__be16 buf ____cacheline_aligned;
|
||||
};
|
||||
@ -82,9 +84,9 @@ static int adis16080_read_raw(struct iio_dev *indio_dev,
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
mutex_lock(&st->lock);
|
||||
ret = adis16080_read_sample(indio_dev, chan->address, val);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
mutex_unlock(&st->lock);
|
||||
return ret ? ret : IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
switch (chan->type) {
|
||||
@ -196,6 +198,8 @@ static int adis16080_probe(struct spi_device *spi)
|
||||
/* this is only used for removal purposes */
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
|
||||
mutex_init(&st->lock);
|
||||
|
||||
/* Allocate the comms buffers */
|
||||
st->us = spi;
|
||||
st->info = &adis16080_chip_info[id->driver_data];
|
||||
|
@ -76,9 +76,7 @@ static int adis16130_read_raw(struct iio_dev *indio_dev,
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
/* Take the iio_dev status lock */
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
ret = adis16130_spi_read(indio_dev, chan->address, &temp);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
if (ret)
|
||||
return ret;
|
||||
*val = temp;
|
||||
|
@ -80,19 +80,19 @@ static ssize_t adis16136_show_serial(struct file *file,
|
||||
|
||||
ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_SERIAL_NUM,
|
||||
&serial);
|
||||
if (ret < 0)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_LOT1, &lot1);
|
||||
if (ret < 0)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_LOT2, &lot2);
|
||||
if (ret < 0)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_LOT3, &lot3);
|
||||
if (ret < 0)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
len = snprintf(buf, sizeof(buf), "%.4x%.4x%.4x-%.4x\n", lot1, lot2,
|
||||
@ -116,7 +116,7 @@ static int adis16136_show_product_id(void *arg, u64 *val)
|
||||
|
||||
ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_PROD_ID,
|
||||
&prod_id);
|
||||
if (ret < 0)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*val = prod_id;
|
||||
@ -134,7 +134,7 @@ static int adis16136_show_flash_count(void *arg, u64 *val)
|
||||
|
||||
ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_FLASH_CNT,
|
||||
&flash_count);
|
||||
if (ret < 0)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*val = flash_count;
|
||||
@ -191,7 +191,7 @@ static int adis16136_get_freq(struct adis16136 *adis16136, unsigned int *freq)
|
||||
int ret;
|
||||
|
||||
ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_SMPL_PRD, &t);
|
||||
if (ret < 0)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*freq = 32768 / (t + 1);
|
||||
@ -228,7 +228,7 @@ static ssize_t adis16136_read_frequency(struct device *dev,
|
||||
int ret;
|
||||
|
||||
ret = adis16136_get_freq(adis16136, &freq);
|
||||
if (ret < 0)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return sprintf(buf, "%d\n", freq);
|
||||
@ -256,7 +256,7 @@ static int adis16136_set_filter(struct iio_dev *indio_dev, int val)
|
||||
int i, ret;
|
||||
|
||||
ret = adis16136_get_freq(adis16136, &freq);
|
||||
if (ret < 0)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (i = ARRAY_SIZE(adis16136_3db_divisors) - 1; i >= 1; i--) {
|
||||
@ -277,11 +277,11 @@ static int adis16136_get_filter(struct iio_dev *indio_dev, int *val)
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
|
||||
ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_AVG_CNT, &val16);
|
||||
if (ret < 0)
|
||||
if (ret)
|
||||
goto err_unlock;
|
||||
|
||||
ret = adis16136_get_freq(adis16136, &freq);
|
||||
if (ret < 0)
|
||||
if (ret)
|
||||
goto err_unlock;
|
||||
|
||||
*val = freq / adis16136_3db_divisors[val16 & 0x07];
|
||||
@ -318,7 +318,7 @@ static int adis16136_read_raw(struct iio_dev *indio_dev,
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
ret = adis_read_reg_32(&adis16136->adis,
|
||||
ADIS16136_REG_GYRO_OFF2, &val32);
|
||||
if (ret < 0)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*val = sign_extend32(val32, 31);
|
||||
|
@ -154,7 +154,7 @@ static int itg3200_write_raw(struct iio_dev *indio_dev,
|
||||
t);
|
||||
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return ret;
|
||||
return ret;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
|
@ -543,7 +543,7 @@ static irqreturn_t mpu3050_trigger_handler(int irq, void *p)
|
||||
toread = bytes_per_datum;
|
||||
offset = 1;
|
||||
/* Put in some dummy value */
|
||||
fifo_values[0] = 0xAAAA;
|
||||
fifo_values[0] = cpu_to_be16(0xAAAA);
|
||||
}
|
||||
|
||||
ret = regmap_bulk_read(mpu3050->map,
|
||||
|
@ -14,7 +14,6 @@
|
||||
#include <linux/types.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/iio/iio.h>
|
||||
|
@ -278,31 +278,34 @@ static int hdc100x_buffer_postenable(struct iio_dev *indio_dev)
|
||||
struct hdc100x_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = iio_triggered_buffer_postenable(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Buffer is enabled. First set ACQ Mode, then attach poll func */
|
||||
mutex_lock(&data->lock);
|
||||
ret = hdc100x_update_config(data, HDC100X_REG_CONFIG_ACQ_MODE,
|
||||
HDC100X_REG_CONFIG_ACQ_MODE);
|
||||
mutex_unlock(&data->lock);
|
||||
if (ret)
|
||||
return ret;
|
||||
iio_triggered_buffer_predisable(indio_dev);
|
||||
|
||||
return iio_triggered_buffer_postenable(indio_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hdc100x_buffer_predisable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct hdc100x_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
/* First detach poll func, then reset ACQ mode. OK to disable buffer */
|
||||
ret = iio_triggered_buffer_predisable(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
int ret, ret2;
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
ret = hdc100x_update_config(data, HDC100X_REG_CONFIG_ACQ_MODE, 0);
|
||||
mutex_unlock(&data->lock);
|
||||
|
||||
ret2 = iio_triggered_buffer_predisable(indio_dev);
|
||||
if (ret == 0)
|
||||
ret = ret2;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -40,6 +40,33 @@ config ADIS16480
|
||||
|
||||
source "drivers/iio/imu/bmi160/Kconfig"
|
||||
|
||||
config FXOS8700
|
||||
tristate
|
||||
|
||||
config FXOS8700_I2C
|
||||
tristate "NXP FXOS8700 I2C driver"
|
||||
depends on I2C
|
||||
select FXOS8700
|
||||
select REGMAP_I2C
|
||||
help
|
||||
Say yes here to build support for the NXP FXOS8700 m+g combo
|
||||
sensor on I2C.
|
||||
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called fxos8700_i2c.
|
||||
|
||||
config FXOS8700_SPI
|
||||
tristate "NXP FXOS8700 SPI driver"
|
||||
depends on SPI
|
||||
select FXOS8700
|
||||
select REGMAP_SPI
|
||||
help
|
||||
Say yes here to build support for the NXP FXOS8700 m+g combo
|
||||
sensor on SPI.
|
||||
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called fxos8700_spi.
|
||||
|
||||
config KMX61
|
||||
tristate "Kionix KMX61 6-axis accelerometer and magnetometer"
|
||||
depends on I2C
|
||||
|
@ -14,6 +14,11 @@ adis_lib-$(CONFIG_IIO_ADIS_LIB_BUFFER) += adis_buffer.o
|
||||
obj-$(CONFIG_IIO_ADIS_LIB) += adis_lib.o
|
||||
|
||||
obj-y += bmi160/
|
||||
|
||||
obj-$(CONFIG_FXOS8700) += fxos8700_core.o
|
||||
obj-$(CONFIG_FXOS8700_I2C) += fxos8700_i2c.o
|
||||
obj-$(CONFIG_FXOS8700_SPI) += fxos8700_spi.o
|
||||
|
||||
obj-y += inv_mpu6050/
|
||||
|
||||
obj-$(CONFIG_KMX61) += kmx61.o
|
||||
|
@ -229,7 +229,8 @@ int adis_debugfs_reg_access(struct iio_dev *indio_dev,
|
||||
int ret;
|
||||
|
||||
ret = adis_read_reg_16(adis, reg, &val16);
|
||||
*readval = val16;
|
||||
if (ret == 0)
|
||||
*readval = val16;
|
||||
|
||||
return ret;
|
||||
} else {
|
||||
@ -286,7 +287,7 @@ int adis_check_status(struct adis *adis)
|
||||
int i;
|
||||
|
||||
ret = adis_read_reg_16(adis, adis->data->diag_stat_reg, &status);
|
||||
if (ret < 0)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
status &= adis->data->status_error_mask;
|
||||
|
@ -217,16 +217,16 @@ static ssize_t adis16400_show_serial_number(struct file *file,
|
||||
int ret;
|
||||
|
||||
ret = adis_read_reg_16(&st->adis, ADIS16334_LOT_ID1, &lot1);
|
||||
if (ret < 0)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = adis_read_reg_16(&st->adis, ADIS16334_LOT_ID2, &lot2);
|
||||
if (ret < 0)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = adis_read_reg_16(&st->adis, ADIS16334_SERIAL_NUMBER,
|
||||
&serial_number);
|
||||
if (ret < 0)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
len = snprintf(buf, sizeof(buf), "%.4x-%.4x-%.4x\n", lot1, lot2,
|
||||
@ -249,7 +249,7 @@ static int adis16400_show_product_id(void *arg, u64 *val)
|
||||
int ret;
|
||||
|
||||
ret = adis_read_reg_16(&st->adis, ADIS16400_PRODUCT_ID, &prod_id);
|
||||
if (ret < 0)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*val = prod_id;
|
||||
@ -266,7 +266,7 @@ static int adis16400_show_flash_count(void *arg, u64 *val)
|
||||
int ret;
|
||||
|
||||
ret = adis_read_reg_16(&st->adis, ADIS16400_FLASH_CNT, &flash_count);
|
||||
if (ret < 0)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*val = flash_count;
|
||||
@ -327,7 +327,7 @@ static int adis16334_get_freq(struct adis16400_state *st)
|
||||
uint16_t t;
|
||||
|
||||
ret = adis_read_reg_16(&st->adis, ADIS16400_SMPL_PRD, &t);
|
||||
if (ret < 0)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
t >>= ADIS16334_RATE_DIV_SHIFT;
|
||||
@ -359,7 +359,7 @@ static int adis16400_get_freq(struct adis16400_state *st)
|
||||
uint16_t t;
|
||||
|
||||
ret = adis_read_reg_16(&st->adis, ADIS16400_SMPL_PRD, &t);
|
||||
if (ret < 0)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
sps = (t & ADIS16400_SMPL_PRD_TIME_BASE) ? 52851 : 1638404;
|
||||
@ -416,7 +416,7 @@ static int adis16400_set_filter(struct iio_dev *indio_dev, int sps, int val)
|
||||
}
|
||||
|
||||
ret = adis_read_reg_16(&st->adis, ADIS16400_SENS_AVG, &val16);
|
||||
if (ret < 0)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = adis_write_reg_16(&st->adis, ADIS16400_SENS_AVG,
|
||||
@ -615,7 +615,7 @@ static int adis16400_read_raw(struct iio_dev *indio_dev,
|
||||
ret = adis_read_reg_16(&st->adis,
|
||||
ADIS16400_SENS_AVG,
|
||||
&val16);
|
||||
if (ret < 0) {
|
||||
if (ret) {
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return ret;
|
||||
}
|
||||
@ -626,12 +626,12 @@ static int adis16400_read_raw(struct iio_dev *indio_dev,
|
||||
*val2 = (ret % 1000) * 1000;
|
||||
}
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
if (ret < 0)
|
||||
if (ret)
|
||||
return ret;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
ret = st->variant->get_freq(st);
|
||||
if (ret < 0)
|
||||
if (ret)
|
||||
return ret;
|
||||
*val = ret / 1000;
|
||||
*val2 = (ret % 1000) * 1000;
|
||||
|
@ -80,7 +80,7 @@ static int adis16460_show_serial_number(void *arg, u64 *val)
|
||||
|
||||
ret = adis_read_reg_16(&adis16460->adis, ADIS16460_REG_SERIAL_NUM,
|
||||
&serial);
|
||||
if (ret < 0)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*val = serial;
|
||||
@ -98,7 +98,7 @@ static int adis16460_show_product_id(void *arg, u64 *val)
|
||||
|
||||
ret = adis_read_reg_16(&adis16460->adis, ADIS16460_REG_PROD_ID,
|
||||
&prod_id);
|
||||
if (ret < 0)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*val = prod_id;
|
||||
@ -116,7 +116,7 @@ static int adis16460_show_flash_count(void *arg, u64 *val)
|
||||
|
||||
ret = adis_read_reg_32(&adis16460->adis, ADIS16460_REG_FLASH_CNT,
|
||||
&flash_count);
|
||||
if (ret < 0)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*val = flash_count;
|
||||
@ -176,7 +176,7 @@ static int adis16460_get_freq(struct iio_dev *indio_dev, int *val, int *val2)
|
||||
unsigned int freq;
|
||||
|
||||
ret = adis_read_reg_16(&st->adis, ADIS16460_REG_DEC_RATE, &t);
|
||||
if (ret < 0)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
freq = 2048000 / (t + 1);
|
||||
|
@ -181,7 +181,7 @@ static ssize_t adis16480_show_firmware_revision(struct file *file,
|
||||
int ret;
|
||||
|
||||
ret = adis_read_reg_16(&adis16480->adis, ADIS16480_REG_FIRM_REV, &rev);
|
||||
if (ret < 0)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
len = scnprintf(buf, sizeof(buf), "%x.%x\n", rev >> 8, rev & 0xff);
|
||||
@ -206,11 +206,11 @@ static ssize_t adis16480_show_firmware_date(struct file *file,
|
||||
int ret;
|
||||
|
||||
ret = adis_read_reg_16(&adis16480->adis, ADIS16480_REG_FIRM_Y, &year);
|
||||
if (ret < 0)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = adis_read_reg_16(&adis16480->adis, ADIS16480_REG_FIRM_DM, &md);
|
||||
if (ret < 0)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
len = snprintf(buf, sizeof(buf), "%.2x-%.2x-%.4x\n",
|
||||
@ -234,7 +234,7 @@ static int adis16480_show_serial_number(void *arg, u64 *val)
|
||||
|
||||
ret = adis_read_reg_16(&adis16480->adis, ADIS16480_REG_SERIAL_NUM,
|
||||
&serial);
|
||||
if (ret < 0)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*val = serial;
|
||||
@ -252,7 +252,7 @@ static int adis16480_show_product_id(void *arg, u64 *val)
|
||||
|
||||
ret = adis_read_reg_16(&adis16480->adis, ADIS16480_REG_PROD_ID,
|
||||
&prod_id);
|
||||
if (ret < 0)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*val = prod_id;
|
||||
@ -270,7 +270,7 @@ static int adis16480_show_flash_count(void *arg, u64 *val)
|
||||
|
||||
ret = adis_read_reg_32(&adis16480->adis, ADIS16480_REG_FLASH_CNT,
|
||||
&flash_count);
|
||||
if (ret < 0)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*val = flash_count;
|
||||
@ -353,7 +353,7 @@ static int adis16480_get_freq(struct iio_dev *indio_dev, int *val, int *val2)
|
||||
struct adis16480 *st = iio_priv(indio_dev);
|
||||
uint16_t t;
|
||||
int ret;
|
||||
unsigned freq;
|
||||
unsigned int freq;
|
||||
unsigned int reg;
|
||||
|
||||
if (st->clk_mode == ADIS16480_CLK_PPS)
|
||||
@ -362,7 +362,7 @@ static int adis16480_get_freq(struct iio_dev *indio_dev, int *val, int *val2)
|
||||
reg = ADIS16480_REG_DEC_RATE;
|
||||
|
||||
ret = adis_read_reg_16(&st->adis, reg, &t);
|
||||
if (ret < 0)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
@ -454,18 +454,20 @@ static int adis16480_get_calibbias(struct iio_dev *indio_dev,
|
||||
case IIO_MAGN:
|
||||
case IIO_PRESSURE:
|
||||
ret = adis_read_reg_16(&st->adis, reg, &val16);
|
||||
*bias = sign_extend32(val16, 15);
|
||||
if (ret == 0)
|
||||
*bias = sign_extend32(val16, 15);
|
||||
break;
|
||||
case IIO_ANGL_VEL:
|
||||
case IIO_ACCEL:
|
||||
ret = adis_read_reg_32(&st->adis, reg, &val32);
|
||||
*bias = sign_extend32(val32, 31);
|
||||
if (ret == 0)
|
||||
*bias = sign_extend32(val32, 31);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
if (ret < 0)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
@ -492,7 +494,7 @@ static int adis16480_get_calibscale(struct iio_dev *indio_dev,
|
||||
int ret;
|
||||
|
||||
ret = adis_read_reg_16(&st->adis, reg, &val16);
|
||||
if (ret < 0)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*scale = sign_extend32(val16, 15);
|
||||
@ -538,7 +540,7 @@ static int adis16480_get_filter_freq(struct iio_dev *indio_dev,
|
||||
enable_mask = BIT(offset + 2);
|
||||
|
||||
ret = adis_read_reg_16(&st->adis, reg, &val);
|
||||
if (ret < 0)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!(val & enable_mask))
|
||||
@ -564,7 +566,7 @@ static int adis16480_set_filter_freq(struct iio_dev *indio_dev,
|
||||
enable_mask = BIT(offset + 2);
|
||||
|
||||
ret = adis_read_reg_16(&st->adis, reg, &val);
|
||||
if (ret < 0)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (freq == 0) {
|
||||
@ -623,9 +625,13 @@ static int adis16480_read_raw(struct iio_dev *indio_dev,
|
||||
*val2 = (st->chip_info->temp_scale % 1000) * 1000;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
case IIO_PRESSURE:
|
||||
*val = 0;
|
||||
*val2 = 4000; /* 40ubar = 0.004 kPa */
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
/*
|
||||
* max scale is 1310 mbar
|
||||
* max raw value is 32767 shifted for 32bits
|
||||
*/
|
||||
*val = 131; /* 1310mbar = 131 kPa */
|
||||
*val2 = 32767 << 16;
|
||||
return IIO_VAL_FRACTIONAL;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -786,13 +792,14 @@ static const struct adis16480_chip_info adis16480_chip_info[] = {
|
||||
.channels = adis16485_channels,
|
||||
.num_channels = ARRAY_SIZE(adis16485_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.
|
||||
* Typically we do IIO_RAD_TO_DEGREE in the denominator, which
|
||||
* is exactly the same as IIO_DEGREE_TO_RAD in numerator, since
|
||||
* it gives better approximation. However, in this case we
|
||||
* cannot do it since it would not fit in a 32bit variable.
|
||||
*/
|
||||
.gyro_max_val = IIO_RAD_TO_DEGREE(22887),
|
||||
.gyro_max_scale = 300,
|
||||
.accel_max_val = IIO_M_S_2_TO_G(21973),
|
||||
.gyro_max_val = 22887 << 16,
|
||||
.gyro_max_scale = IIO_DEGREE_TO_RAD(300),
|
||||
.accel_max_val = IIO_M_S_2_TO_G(21973 << 16),
|
||||
.accel_max_scale = 18,
|
||||
.temp_scale = 5650, /* 5.65 milli degree Celsius */
|
||||
.int_clk = 2460000,
|
||||
@ -802,9 +809,9 @@ static const struct adis16480_chip_info adis16480_chip_info[] = {
|
||||
[ADIS16480] = {
|
||||
.channels = adis16480_channels,
|
||||
.num_channels = ARRAY_SIZE(adis16480_channels),
|
||||
.gyro_max_val = IIO_RAD_TO_DEGREE(22500),
|
||||
.gyro_max_scale = 450,
|
||||
.accel_max_val = IIO_M_S_2_TO_G(12500),
|
||||
.gyro_max_val = 22500 << 16,
|
||||
.gyro_max_scale = IIO_DEGREE_TO_RAD(450),
|
||||
.accel_max_val = IIO_M_S_2_TO_G(12500 << 16),
|
||||
.accel_max_scale = 10,
|
||||
.temp_scale = 5650, /* 5.65 milli degree Celsius */
|
||||
.int_clk = 2460000,
|
||||
@ -814,9 +821,9 @@ static const struct adis16480_chip_info adis16480_chip_info[] = {
|
||||
[ADIS16485] = {
|
||||
.channels = adis16485_channels,
|
||||
.num_channels = ARRAY_SIZE(adis16485_channels),
|
||||
.gyro_max_val = IIO_RAD_TO_DEGREE(22500),
|
||||
.gyro_max_scale = 450,
|
||||
.accel_max_val = IIO_M_S_2_TO_G(20000),
|
||||
.gyro_max_val = 22500 << 16,
|
||||
.gyro_max_scale = IIO_DEGREE_TO_RAD(450),
|
||||
.accel_max_val = IIO_M_S_2_TO_G(20000 << 16),
|
||||
.accel_max_scale = 5,
|
||||
.temp_scale = 5650, /* 5.65 milli degree Celsius */
|
||||
.int_clk = 2460000,
|
||||
@ -826,9 +833,9 @@ static const struct adis16480_chip_info adis16480_chip_info[] = {
|
||||
[ADIS16488] = {
|
||||
.channels = adis16480_channels,
|
||||
.num_channels = ARRAY_SIZE(adis16480_channels),
|
||||
.gyro_max_val = IIO_RAD_TO_DEGREE(22500),
|
||||
.gyro_max_scale = 450,
|
||||
.accel_max_val = IIO_M_S_2_TO_G(22500),
|
||||
.gyro_max_val = 22500 << 16,
|
||||
.gyro_max_scale = IIO_DEGREE_TO_RAD(450),
|
||||
.accel_max_val = IIO_M_S_2_TO_G(22500 << 16),
|
||||
.accel_max_scale = 18,
|
||||
.temp_scale = 5650, /* 5.65 milli degree Celsius */
|
||||
.int_clk = 2460000,
|
||||
@ -838,9 +845,9 @@ static const struct adis16480_chip_info adis16480_chip_info[] = {
|
||||
[ADIS16495_1] = {
|
||||
.channels = adis16485_channels,
|
||||
.num_channels = ARRAY_SIZE(adis16485_channels),
|
||||
.gyro_max_val = IIO_RAD_TO_DEGREE(20000),
|
||||
.gyro_max_scale = 125,
|
||||
.accel_max_val = IIO_M_S_2_TO_G(32000),
|
||||
.gyro_max_val = 20000 << 16,
|
||||
.gyro_max_scale = IIO_DEGREE_TO_RAD(125),
|
||||
.accel_max_val = IIO_M_S_2_TO_G(32000 << 16),
|
||||
.accel_max_scale = 8,
|
||||
.temp_scale = 12500, /* 12.5 milli degree Celsius */
|
||||
.int_clk = 4250000,
|
||||
@ -851,9 +858,9 @@ static const struct adis16480_chip_info adis16480_chip_info[] = {
|
||||
[ADIS16495_2] = {
|
||||
.channels = adis16485_channels,
|
||||
.num_channels = ARRAY_SIZE(adis16485_channels),
|
||||
.gyro_max_val = IIO_RAD_TO_DEGREE(18000),
|
||||
.gyro_max_scale = 450,
|
||||
.accel_max_val = IIO_M_S_2_TO_G(32000),
|
||||
.gyro_max_val = 18000 << 16,
|
||||
.gyro_max_scale = IIO_DEGREE_TO_RAD(450),
|
||||
.accel_max_val = IIO_M_S_2_TO_G(32000 << 16),
|
||||
.accel_max_scale = 8,
|
||||
.temp_scale = 12500, /* 12.5 milli degree Celsius */
|
||||
.int_clk = 4250000,
|
||||
@ -864,9 +871,9 @@ static const struct adis16480_chip_info adis16480_chip_info[] = {
|
||||
[ADIS16495_3] = {
|
||||
.channels = adis16485_channels,
|
||||
.num_channels = ARRAY_SIZE(adis16485_channels),
|
||||
.gyro_max_val = IIO_RAD_TO_DEGREE(20000),
|
||||
.gyro_max_scale = 2000,
|
||||
.accel_max_val = IIO_M_S_2_TO_G(32000),
|
||||
.gyro_max_val = 20000 << 16,
|
||||
.gyro_max_scale = IIO_DEGREE_TO_RAD(2000),
|
||||
.accel_max_val = IIO_M_S_2_TO_G(32000 << 16),
|
||||
.accel_max_scale = 8,
|
||||
.temp_scale = 12500, /* 12.5 milli degree Celsius */
|
||||
.int_clk = 4250000,
|
||||
@ -877,9 +884,9 @@ static const struct adis16480_chip_info adis16480_chip_info[] = {
|
||||
[ADIS16497_1] = {
|
||||
.channels = adis16485_channels,
|
||||
.num_channels = ARRAY_SIZE(adis16485_channels),
|
||||
.gyro_max_val = IIO_RAD_TO_DEGREE(20000),
|
||||
.gyro_max_scale = 125,
|
||||
.accel_max_val = IIO_M_S_2_TO_G(32000),
|
||||
.gyro_max_val = 20000 << 16,
|
||||
.gyro_max_scale = IIO_DEGREE_TO_RAD(125),
|
||||
.accel_max_val = IIO_M_S_2_TO_G(32000 << 16),
|
||||
.accel_max_scale = 40,
|
||||
.temp_scale = 12500, /* 12.5 milli degree Celsius */
|
||||
.int_clk = 4250000,
|
||||
@ -890,9 +897,9 @@ static const struct adis16480_chip_info adis16480_chip_info[] = {
|
||||
[ADIS16497_2] = {
|
||||
.channels = adis16485_channels,
|
||||
.num_channels = ARRAY_SIZE(adis16485_channels),
|
||||
.gyro_max_val = IIO_RAD_TO_DEGREE(18000),
|
||||
.gyro_max_scale = 450,
|
||||
.accel_max_val = IIO_M_S_2_TO_G(32000),
|
||||
.gyro_max_val = 18000 << 16,
|
||||
.gyro_max_scale = IIO_DEGREE_TO_RAD(450),
|
||||
.accel_max_val = IIO_M_S_2_TO_G(32000 << 16),
|
||||
.accel_max_scale = 40,
|
||||
.temp_scale = 12500, /* 12.5 milli degree Celsius */
|
||||
.int_clk = 4250000,
|
||||
@ -903,9 +910,9 @@ static const struct adis16480_chip_info adis16480_chip_info[] = {
|
||||
[ADIS16497_3] = {
|
||||
.channels = adis16485_channels,
|
||||
.num_channels = ARRAY_SIZE(adis16485_channels),
|
||||
.gyro_max_val = IIO_RAD_TO_DEGREE(20000),
|
||||
.gyro_max_scale = 2000,
|
||||
.accel_max_val = IIO_M_S_2_TO_G(32000),
|
||||
.gyro_max_val = 20000 << 16,
|
||||
.gyro_max_scale = IIO_DEGREE_TO_RAD(2000),
|
||||
.accel_max_val = IIO_M_S_2_TO_G(32000 << 16),
|
||||
.accel_max_scale = 40,
|
||||
.temp_scale = 12500, /* 12.5 milli degree Celsius */
|
||||
.int_clk = 4250000,
|
||||
@ -919,6 +926,7 @@ static const struct iio_info adis16480_info = {
|
||||
.read_raw = &adis16480_read_raw,
|
||||
.write_raw = &adis16480_write_raw,
|
||||
.update_scan_mode = adis_update_scan_mode,
|
||||
.debugfs_reg_access = adis_debugfs_reg_access,
|
||||
};
|
||||
|
||||
static int adis16480_stop_device(struct iio_dev *indio_dev)
|
||||
@ -940,7 +948,7 @@ static int adis16480_enable_irq(struct adis *adis, bool enable)
|
||||
int ret;
|
||||
|
||||
ret = adis_read_reg_16(adis, ADIS16480_REG_FNCTIO_CTRL, &val);
|
||||
if (ret < 0)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
val &= ~ADIS16480_DRDY_EN_MSK;
|
||||
@ -1118,7 +1126,7 @@ static int adis16480_ext_clk_config(struct adis16480 *st,
|
||||
int ret;
|
||||
|
||||
ret = adis_read_reg_16(&st->adis, ADIS16480_REG_FNCTIO_CTRL, &val);
|
||||
if (ret < 0)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pin = adis16480_of_get_ext_clk_pin(st, of_node);
|
||||
@ -1144,7 +1152,7 @@ static int adis16480_ext_clk_config(struct adis16480 *st,
|
||||
val |= mode;
|
||||
|
||||
ret = adis_write_reg_16(&st->adis, ADIS16480_REG_FNCTIO_CTRL, val);
|
||||
if (ret < 0)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return clk_prepare_enable(st->ext_clk);
|
||||
|
10
drivers/iio/imu/fxos8700.h
Normal file
10
drivers/iio/imu/fxos8700.h
Normal file
@ -0,0 +1,10 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef FXOS8700_H_
|
||||
#define FXOS8700_H_
|
||||
|
||||
extern const struct regmap_config fxos8700_regmap_config;
|
||||
|
||||
int fxos8700_core_probe(struct device *dev, struct regmap *regmap,
|
||||
const char *name, bool use_spi);
|
||||
|
||||
#endif /* FXOS8700_H_ */
|
649
drivers/iio/imu/fxos8700_core.c
Normal file
649
drivers/iio/imu/fxos8700_core.c
Normal file
@ -0,0 +1,649 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* FXOS8700 - NXP IMU (accelerometer plus magnetometer)
|
||||
*
|
||||
* IIO core driver for FXOS8700, with support for I2C/SPI busses
|
||||
*
|
||||
* TODO: Buffer, trigger, and IRQ support
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/bitops.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
#include "fxos8700.h"
|
||||
|
||||
/* Register Definitions */
|
||||
#define FXOS8700_STATUS 0x00
|
||||
#define FXOS8700_OUT_X_MSB 0x01
|
||||
#define FXOS8700_OUT_X_LSB 0x02
|
||||
#define FXOS8700_OUT_Y_MSB 0x03
|
||||
#define FXOS8700_OUT_Y_LSB 0x04
|
||||
#define FXOS8700_OUT_Z_MSB 0x05
|
||||
#define FXOS8700_OUT_Z_LSB 0x06
|
||||
#define FXOS8700_F_SETUP 0x09
|
||||
#define FXOS8700_TRIG_CFG 0x0a
|
||||
#define FXOS8700_SYSMOD 0x0b
|
||||
#define FXOS8700_INT_SOURCE 0x0c
|
||||
#define FXOS8700_WHO_AM_I 0x0d
|
||||
#define FXOS8700_XYZ_DATA_CFG 0x0e
|
||||
#define FXOS8700_HP_FILTER_CUTOFF 0x0f
|
||||
#define FXOS8700_PL_STATUS 0x10
|
||||
#define FXOS8700_PL_CFG 0x11
|
||||
#define FXOS8700_PL_COUNT 0x12
|
||||
#define FXOS8700_PL_BF_ZCOMP 0x13
|
||||
#define FXOS8700_PL_THS_REG 0x14
|
||||
#define FXOS8700_A_FFMT_CFG 0x15
|
||||
#define FXOS8700_A_FFMT_SRC 0x16
|
||||
#define FXOS8700_A_FFMT_THS 0x17
|
||||
#define FXOS8700_A_FFMT_COUNT 0x18
|
||||
#define FXOS8700_TRANSIENT_CFG 0x1d
|
||||
#define FXOS8700_TRANSIENT_SRC 0x1e
|
||||
#define FXOS8700_TRANSIENT_THS 0x1f
|
||||
#define FXOS8700_TRANSIENT_COUNT 0x20
|
||||
#define FXOS8700_PULSE_CFG 0x21
|
||||
#define FXOS8700_PULSE_SRC 0x22
|
||||
#define FXOS8700_PULSE_THSX 0x23
|
||||
#define FXOS8700_PULSE_THSY 0x24
|
||||
#define FXOS8700_PULSE_THSZ 0x25
|
||||
#define FXOS8700_PULSE_TMLT 0x26
|
||||
#define FXOS8700_PULSE_LTCY 0x27
|
||||
#define FXOS8700_PULSE_WIND 0x28
|
||||
#define FXOS8700_ASLP_COUNT 0x29
|
||||
#define FXOS8700_CTRL_REG1 0x2a
|
||||
#define FXOS8700_CTRL_REG2 0x2b
|
||||
#define FXOS8700_CTRL_REG3 0x2c
|
||||
#define FXOS8700_CTRL_REG4 0x2d
|
||||
#define FXOS8700_CTRL_REG5 0x2e
|
||||
#define FXOS8700_OFF_X 0x2f
|
||||
#define FXOS8700_OFF_Y 0x30
|
||||
#define FXOS8700_OFF_Z 0x31
|
||||
#define FXOS8700_M_DR_STATUS 0x32
|
||||
#define FXOS8700_M_OUT_X_MSB 0x33
|
||||
#define FXOS8700_M_OUT_X_LSB 0x34
|
||||
#define FXOS8700_M_OUT_Y_MSB 0x35
|
||||
#define FXOS8700_M_OUT_Y_LSB 0x36
|
||||
#define FXOS8700_M_OUT_Z_MSB 0x37
|
||||
#define FXOS8700_M_OUT_Z_LSB 0x38
|
||||
#define FXOS8700_CMP_X_MSB 0x39
|
||||
#define FXOS8700_CMP_X_LSB 0x3a
|
||||
#define FXOS8700_CMP_Y_MSB 0x3b
|
||||
#define FXOS8700_CMP_Y_LSB 0x3c
|
||||
#define FXOS8700_CMP_Z_MSB 0x3d
|
||||
#define FXOS8700_CMP_Z_LSB 0x3e
|
||||
#define FXOS8700_M_OFF_X_MSB 0x3f
|
||||
#define FXOS8700_M_OFF_X_LSB 0x40
|
||||
#define FXOS8700_M_OFF_Y_MSB 0x41
|
||||
#define FXOS8700_M_OFF_Y_LSB 0x42
|
||||
#define FXOS8700_M_OFF_Z_MSB 0x43
|
||||
#define FXOS8700_M_OFF_Z_LSB 0x44
|
||||
#define FXOS8700_MAX_X_MSB 0x45
|
||||
#define FXOS8700_MAX_X_LSB 0x46
|
||||
#define FXOS8700_MAX_Y_MSB 0x47
|
||||
#define FXOS8700_MAX_Y_LSB 0x48
|
||||
#define FXOS8700_MAX_Z_MSB 0x49
|
||||
#define FXOS8700_MAX_Z_LSB 0x4a
|
||||
#define FXOS8700_MIN_X_MSB 0x4b
|
||||
#define FXOS8700_MIN_X_LSB 0x4c
|
||||
#define FXOS8700_MIN_Y_MSB 0x4d
|
||||
#define FXOS8700_MIN_Y_LSB 0x4e
|
||||
#define FXOS8700_MIN_Z_MSB 0x4f
|
||||
#define FXOS8700_MIN_Z_LSB 0x50
|
||||
#define FXOS8700_TEMP 0x51
|
||||
#define FXOS8700_M_THS_CFG 0x52
|
||||
#define FXOS8700_M_THS_SRC 0x53
|
||||
#define FXOS8700_M_THS_X_MSB 0x54
|
||||
#define FXOS8700_M_THS_X_LSB 0x55
|
||||
#define FXOS8700_M_THS_Y_MSB 0x56
|
||||
#define FXOS8700_M_THS_Y_LSB 0x57
|
||||
#define FXOS8700_M_THS_Z_MSB 0x58
|
||||
#define FXOS8700_M_THS_Z_LSB 0x59
|
||||
#define FXOS8700_M_THS_COUNT 0x5a
|
||||
#define FXOS8700_M_CTRL_REG1 0x5b
|
||||
#define FXOS8700_M_CTRL_REG2 0x5c
|
||||
#define FXOS8700_M_CTRL_REG3 0x5d
|
||||
#define FXOS8700_M_INT_SRC 0x5e
|
||||
#define FXOS8700_A_VECM_CFG 0x5f
|
||||
#define FXOS8700_A_VECM_THS_MSB 0x60
|
||||
#define FXOS8700_A_VECM_THS_LSB 0x61
|
||||
#define FXOS8700_A_VECM_CNT 0x62
|
||||
#define FXOS8700_A_VECM_INITX_MSB 0x63
|
||||
#define FXOS8700_A_VECM_INITX_LSB 0x64
|
||||
#define FXOS8700_A_VECM_INITY_MSB 0x65
|
||||
#define FXOS8700_A_VECM_INITY_LSB 0x66
|
||||
#define FXOS8700_A_VECM_INITZ_MSB 0x67
|
||||
#define FXOS8700_A_VECM_INITZ_LSB 0x68
|
||||
#define FXOS8700_M_VECM_CFG 0x69
|
||||
#define FXOS8700_M_VECM_THS_MSB 0x6a
|
||||
#define FXOS8700_M_VECM_THS_LSB 0x6b
|
||||
#define FXOS8700_M_VECM_CNT 0x6c
|
||||
#define FXOS8700_M_VECM_INITX_MSB 0x6d
|
||||
#define FXOS8700_M_VECM_INITX_LSB 0x6e
|
||||
#define FXOS8700_M_VECM_INITY_MSB 0x6f
|
||||
#define FXOS8700_M_VECM_INITY_LSB 0x70
|
||||
#define FXOS8700_M_VECM_INITZ_MSB 0x71
|
||||
#define FXOS8700_M_VECM_INITZ_LSB 0x72
|
||||
#define FXOS8700_A_FFMT_THS_X_MSB 0x73
|
||||
#define FXOS8700_A_FFMT_THS_X_LSB 0x74
|
||||
#define FXOS8700_A_FFMT_THS_Y_MSB 0x75
|
||||
#define FXOS8700_A_FFMT_THS_Y_LSB 0x76
|
||||
#define FXOS8700_A_FFMT_THS_Z_MSB 0x77
|
||||
#define FXOS8700_A_FFMT_THS_Z_LSB 0x78
|
||||
#define FXOS8700_A_TRAN_INIT_MSB 0x79
|
||||
#define FXOS8700_A_TRAN_INIT_LSB_X 0x7a
|
||||
#define FXOS8700_A_TRAN_INIT_LSB_Y 0x7b
|
||||
#define FXOS8700_A_TRAN_INIT_LSB_Z 0x7d
|
||||
#define FXOS8700_TM_NVM_LOCK 0x7e
|
||||
#define FXOS8700_NVM_DATA0_35 0x80
|
||||
#define FXOS8700_NVM_DATA_BNK3 0xa4
|
||||
#define FXOS8700_NVM_DATA_BNK2 0xa5
|
||||
#define FXOS8700_NVM_DATA_BNK1 0xa6
|
||||
#define FXOS8700_NVM_DATA_BNK0 0xa7
|
||||
|
||||
/* Bit definitions for FXOS8700_CTRL_REG1 */
|
||||
#define FXOS8700_CTRL_ODR_MSK 0x38
|
||||
#define FXOS8700_CTRL_ODR_MAX 0x00
|
||||
#define FXOS8700_CTRL_ODR_MIN GENMASK(4, 3)
|
||||
|
||||
/* Bit definitions for FXOS8700_M_CTRL_REG1 */
|
||||
#define FXOS8700_HMS_MASK GENMASK(1, 0)
|
||||
#define FXOS8700_OS_MASK GENMASK(4, 2)
|
||||
|
||||
/* Bit definitions for FXOS8700_M_CTRL_REG2 */
|
||||
#define FXOS8700_MAXMIN_RST BIT(2)
|
||||
#define FXOS8700_MAXMIN_DIS_THS BIT(3)
|
||||
#define FXOS8700_MAXMIN_DIS BIT(4)
|
||||
|
||||
#define FXOS8700_ACTIVE 0x01
|
||||
#define FXOS8700_ACTIVE_MIN_USLEEP 4000 /* from table 6 in datasheet */
|
||||
|
||||
#define FXOS8700_DEVICE_ID 0xC7
|
||||
#define FXOS8700_PRE_DEVICE_ID 0xC4
|
||||
#define FXOS8700_DATA_BUF_SIZE 3
|
||||
|
||||
struct fxos8700_data {
|
||||
struct regmap *regmap;
|
||||
struct iio_trigger *trig;
|
||||
__be16 buf[FXOS8700_DATA_BUF_SIZE] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
/* Regmap info */
|
||||
static const struct regmap_range read_range[] = {
|
||||
{
|
||||
.range_min = FXOS8700_STATUS,
|
||||
.range_max = FXOS8700_A_FFMT_COUNT,
|
||||
}, {
|
||||
.range_min = FXOS8700_TRANSIENT_CFG,
|
||||
.range_max = FXOS8700_A_FFMT_THS_Z_LSB,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct regmap_range write_range[] = {
|
||||
{
|
||||
.range_min = FXOS8700_F_SETUP,
|
||||
.range_max = FXOS8700_TRIG_CFG,
|
||||
}, {
|
||||
.range_min = FXOS8700_XYZ_DATA_CFG,
|
||||
.range_max = FXOS8700_HP_FILTER_CUTOFF,
|
||||
}, {
|
||||
.range_min = FXOS8700_PL_CFG,
|
||||
.range_max = FXOS8700_A_FFMT_CFG,
|
||||
}, {
|
||||
.range_min = FXOS8700_A_FFMT_THS,
|
||||
.range_max = FXOS8700_TRANSIENT_CFG,
|
||||
}, {
|
||||
.range_min = FXOS8700_TRANSIENT_THS,
|
||||
.range_max = FXOS8700_PULSE_CFG,
|
||||
}, {
|
||||
.range_min = FXOS8700_PULSE_THSX,
|
||||
.range_max = FXOS8700_OFF_Z,
|
||||
}, {
|
||||
.range_min = FXOS8700_M_OFF_X_MSB,
|
||||
.range_max = FXOS8700_M_OFF_Z_LSB,
|
||||
}, {
|
||||
.range_min = FXOS8700_M_THS_CFG,
|
||||
.range_max = FXOS8700_M_THS_CFG,
|
||||
}, {
|
||||
.range_min = FXOS8700_M_THS_X_MSB,
|
||||
.range_max = FXOS8700_M_CTRL_REG3,
|
||||
}, {
|
||||
.range_min = FXOS8700_A_VECM_CFG,
|
||||
.range_max = FXOS8700_A_FFMT_THS_Z_LSB,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct regmap_access_table driver_read_table = {
|
||||
.yes_ranges = read_range,
|
||||
.n_yes_ranges = ARRAY_SIZE(read_range),
|
||||
};
|
||||
|
||||
static const struct regmap_access_table driver_write_table = {
|
||||
.yes_ranges = write_range,
|
||||
.n_yes_ranges = ARRAY_SIZE(write_range),
|
||||
};
|
||||
|
||||
const struct regmap_config fxos8700_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.max_register = FXOS8700_NVM_DATA_BNK0,
|
||||
.rd_table = &driver_read_table,
|
||||
.wr_table = &driver_write_table,
|
||||
};
|
||||
EXPORT_SYMBOL(fxos8700_regmap_config);
|
||||
|
||||
#define FXOS8700_CHANNEL(_type, _axis) { \
|
||||
.type = _type, \
|
||||
.modified = 1, \
|
||||
.channel2 = IIO_MOD_##_axis, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ), \
|
||||
}
|
||||
|
||||
enum fxos8700_accel_scale_bits {
|
||||
MODE_2G = 0,
|
||||
MODE_4G,
|
||||
MODE_8G,
|
||||
};
|
||||
|
||||
/* scan indexes follow DATA register order */
|
||||
enum fxos8700_scan_axis {
|
||||
FXOS8700_SCAN_ACCEL_X = 0,
|
||||
FXOS8700_SCAN_ACCEL_Y,
|
||||
FXOS8700_SCAN_ACCEL_Z,
|
||||
FXOS8700_SCAN_MAGN_X,
|
||||
FXOS8700_SCAN_MAGN_Y,
|
||||
FXOS8700_SCAN_MAGN_Z,
|
||||
FXOS8700_SCAN_RHALL,
|
||||
FXOS8700_SCAN_TIMESTAMP,
|
||||
};
|
||||
|
||||
enum fxos8700_sensor {
|
||||
FXOS8700_ACCEL = 0,
|
||||
FXOS8700_MAGN,
|
||||
FXOS8700_NUM_SENSORS /* must be last */
|
||||
};
|
||||
|
||||
enum fxos8700_int_pin {
|
||||
FXOS8700_PIN_INT1,
|
||||
FXOS8700_PIN_INT2
|
||||
};
|
||||
|
||||
struct fxos8700_scale {
|
||||
u8 bits;
|
||||
int uscale;
|
||||
};
|
||||
|
||||
struct fxos8700_odr {
|
||||
u8 bits;
|
||||
int odr;
|
||||
int uodr;
|
||||
};
|
||||
|
||||
static const struct fxos8700_scale fxos8700_accel_scale[] = {
|
||||
{ MODE_2G, 244},
|
||||
{ MODE_4G, 488},
|
||||
{ MODE_8G, 976},
|
||||
};
|
||||
|
||||
/*
|
||||
* Accellerometer and magnetometer have the same ODR options, set in the
|
||||
* CTRL_REG1 register. ODR is halved when using both sensors at once in
|
||||
* hybrid mode.
|
||||
*/
|
||||
static const struct fxos8700_odr fxos8700_odr[] = {
|
||||
{0x00, 800, 0},
|
||||
{0x01, 400, 0},
|
||||
{0x02, 200, 0},
|
||||
{0x03, 100, 0},
|
||||
{0x04, 50, 0},
|
||||
{0x05, 12, 500000},
|
||||
{0x06, 6, 250000},
|
||||
{0x07, 1, 562500},
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec fxos8700_channels[] = {
|
||||
FXOS8700_CHANNEL(IIO_ACCEL, X),
|
||||
FXOS8700_CHANNEL(IIO_ACCEL, Y),
|
||||
FXOS8700_CHANNEL(IIO_ACCEL, Z),
|
||||
FXOS8700_CHANNEL(IIO_MAGN, X),
|
||||
FXOS8700_CHANNEL(IIO_MAGN, Y),
|
||||
FXOS8700_CHANNEL(IIO_MAGN, Z),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(FXOS8700_SCAN_TIMESTAMP),
|
||||
};
|
||||
|
||||
static enum fxos8700_sensor fxos8700_to_sensor(enum iio_chan_type iio_type)
|
||||
{
|
||||
switch (iio_type) {
|
||||
case IIO_ACCEL:
|
||||
return FXOS8700_ACCEL;
|
||||
case IIO_ANGL_VEL:
|
||||
return FXOS8700_MAGN;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int fxos8700_set_active_mode(struct fxos8700_data *data,
|
||||
enum fxos8700_sensor t, bool mode)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = regmap_write(data->regmap, FXOS8700_CTRL_REG1, mode);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
usleep_range(FXOS8700_ACTIVE_MIN_USLEEP,
|
||||
FXOS8700_ACTIVE_MIN_USLEEP + 1000);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fxos8700_set_scale(struct fxos8700_data *data,
|
||||
enum fxos8700_sensor t, int uscale)
|
||||
{
|
||||
int i;
|
||||
static const int scale_num = ARRAY_SIZE(fxos8700_accel_scale);
|
||||
struct device *dev = regmap_get_device(data->regmap);
|
||||
|
||||
if (t == FXOS8700_MAGN) {
|
||||
dev_err(dev, "Magnetometer scale is locked at 1200uT\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < scale_num; i++)
|
||||
if (fxos8700_accel_scale[i].uscale == uscale)
|
||||
break;
|
||||
|
||||
if (i == scale_num)
|
||||
return -EINVAL;
|
||||
|
||||
return regmap_write(data->regmap, FXOS8700_XYZ_DATA_CFG,
|
||||
fxos8700_accel_scale[i].bits);
|
||||
}
|
||||
|
||||
static int fxos8700_get_scale(struct fxos8700_data *data,
|
||||
enum fxos8700_sensor t, int *uscale)
|
||||
{
|
||||
int i, ret, val;
|
||||
static const int scale_num = ARRAY_SIZE(fxos8700_accel_scale);
|
||||
|
||||
if (t == FXOS8700_MAGN) {
|
||||
*uscale = 1200; /* Magnetometer is locked at 1200uT */
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = regmap_read(data->regmap, FXOS8700_XYZ_DATA_CFG, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < scale_num; i++) {
|
||||
if (fxos8700_accel_scale[i].bits == (val & 0x3)) {
|
||||
*uscale = fxos8700_accel_scale[i].uscale;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int fxos8700_get_data(struct fxos8700_data *data, int chan_type,
|
||||
int axis, int *val)
|
||||
{
|
||||
u8 base, reg;
|
||||
int ret;
|
||||
enum fxos8700_sensor type = fxos8700_to_sensor(chan_type);
|
||||
|
||||
base = type ? FXOS8700_OUT_X_MSB : FXOS8700_M_OUT_X_MSB;
|
||||
|
||||
/* Block read 6 bytes of device output registers to avoid data loss */
|
||||
ret = regmap_bulk_read(data->regmap, base, data->buf,
|
||||
FXOS8700_DATA_BUF_SIZE);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Convert axis to buffer index */
|
||||
reg = axis - IIO_MOD_X;
|
||||
|
||||
/* Convert to native endianness */
|
||||
*val = sign_extend32(be16_to_cpu(data->buf[reg]), 15);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fxos8700_set_odr(struct fxos8700_data *data, enum fxos8700_sensor t,
|
||||
int odr, int uodr)
|
||||
{
|
||||
int i, ret, val;
|
||||
bool active_mode;
|
||||
static const int odr_num = ARRAY_SIZE(fxos8700_odr);
|
||||
|
||||
ret = regmap_read(data->regmap, FXOS8700_CTRL_REG1, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
active_mode = val & FXOS8700_ACTIVE;
|
||||
|
||||
if (active_mode) {
|
||||
/*
|
||||
* The device must be in standby mode to change any of the
|
||||
* other fields within CTRL_REG1
|
||||
*/
|
||||
ret = regmap_write(data->regmap, FXOS8700_CTRL_REG1,
|
||||
val & ~FXOS8700_ACTIVE);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (i = 0; i < odr_num; i++)
|
||||
if (fxos8700_odr[i].odr == odr && fxos8700_odr[i].uodr == uodr)
|
||||
break;
|
||||
|
||||
if (i >= odr_num)
|
||||
return -EINVAL;
|
||||
|
||||
return regmap_update_bits(data->regmap,
|
||||
FXOS8700_CTRL_REG1,
|
||||
FXOS8700_CTRL_ODR_MSK + FXOS8700_ACTIVE,
|
||||
fxos8700_odr[i].bits << 3 | active_mode);
|
||||
}
|
||||
|
||||
static int fxos8700_get_odr(struct fxos8700_data *data, enum fxos8700_sensor t,
|
||||
int *odr, int *uodr)
|
||||
{
|
||||
int i, val, ret;
|
||||
static const int odr_num = ARRAY_SIZE(fxos8700_odr);
|
||||
|
||||
ret = regmap_read(data->regmap, FXOS8700_CTRL_REG1, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
val &= FXOS8700_CTRL_ODR_MSK;
|
||||
|
||||
for (i = 0; i < odr_num; i++)
|
||||
if (val == fxos8700_odr[i].bits)
|
||||
break;
|
||||
|
||||
if (i >= odr_num)
|
||||
return -EINVAL;
|
||||
|
||||
*odr = fxos8700_odr[i].odr;
|
||||
*uodr = fxos8700_odr[i].uodr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fxos8700_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
int ret;
|
||||
struct fxos8700_data *data = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = fxos8700_get_data(data, chan->type, chan->channel2, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = 0;
|
||||
ret = fxos8700_get_scale(data, fxos8700_to_sensor(chan->type),
|
||||
val2);
|
||||
return ret ? ret : IIO_VAL_INT_PLUS_MICRO;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
ret = fxos8700_get_odr(data, fxos8700_to_sensor(chan->type),
|
||||
val, val2);
|
||||
return ret ? ret : IIO_VAL_INT_PLUS_MICRO;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int fxos8700_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
struct fxos8700_data *data = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
return fxos8700_set_scale(data, fxos8700_to_sensor(chan->type),
|
||||
val2);
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
return fxos8700_set_odr(data, fxos8700_to_sensor(chan->type),
|
||||
val, val2);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static IIO_CONST_ATTR(in_accel_sampling_frequency_available,
|
||||
"1.5625 6.25 12.5 50 100 200 400 800");
|
||||
static IIO_CONST_ATTR(in_magn_sampling_frequency_available,
|
||||
"1.5625 6.25 12.5 50 100 200 400 800");
|
||||
static IIO_CONST_ATTR(in_accel_scale_available, "0.000244 0.000488 0.000976");
|
||||
static IIO_CONST_ATTR(in_magn_scale_available, "0.000001200");
|
||||
|
||||
static struct attribute *fxos8700_attrs[] = {
|
||||
&iio_const_attr_in_accel_sampling_frequency_available.dev_attr.attr,
|
||||
&iio_const_attr_in_magn_sampling_frequency_available.dev_attr.attr,
|
||||
&iio_const_attr_in_accel_scale_available.dev_attr.attr,
|
||||
&iio_const_attr_in_magn_scale_available.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group fxos8700_attrs_group = {
|
||||
.attrs = fxos8700_attrs,
|
||||
};
|
||||
|
||||
static const struct iio_info fxos8700_info = {
|
||||
.read_raw = fxos8700_read_raw,
|
||||
.write_raw = fxos8700_write_raw,
|
||||
.attrs = &fxos8700_attrs_group,
|
||||
};
|
||||
|
||||
static int fxos8700_chip_init(struct fxos8700_data *data, bool use_spi)
|
||||
{
|
||||
int ret;
|
||||
unsigned int val;
|
||||
struct device *dev = regmap_get_device(data->regmap);
|
||||
|
||||
ret = regmap_read(data->regmap, FXOS8700_WHO_AM_I, &val);
|
||||
if (ret) {
|
||||
dev_err(dev, "Error reading chip id\n");
|
||||
return ret;
|
||||
}
|
||||
if (val != FXOS8700_DEVICE_ID && val != FXOS8700_PRE_DEVICE_ID) {
|
||||
dev_err(dev, "Wrong chip id, got %x expected %x or %x\n",
|
||||
val, FXOS8700_DEVICE_ID, FXOS8700_PRE_DEVICE_ID);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ret = fxos8700_set_active_mode(data, FXOS8700_ACCEL, true);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = fxos8700_set_active_mode(data, FXOS8700_MAGN, true);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* The device must be in standby mode to change any of the other fields
|
||||
* within CTRL_REG1
|
||||
*/
|
||||
ret = regmap_write(data->regmap, FXOS8700_CTRL_REG1, 0x00);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Set max oversample ratio (OSR) and both devices active */
|
||||
ret = regmap_write(data->regmap, FXOS8700_M_CTRL_REG1,
|
||||
FXOS8700_HMS_MASK | FXOS8700_OS_MASK);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Disable and rst min/max measurements & threshold */
|
||||
ret = regmap_write(data->regmap, FXOS8700_M_CTRL_REG2,
|
||||
FXOS8700_MAXMIN_RST | FXOS8700_MAXMIN_DIS_THS |
|
||||
FXOS8700_MAXMIN_DIS);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Max ODR (800Hz individual or 400Hz hybrid), active mode */
|
||||
ret = regmap_write(data->regmap, FXOS8700_CTRL_REG1,
|
||||
FXOS8700_CTRL_ODR_MAX | FXOS8700_ACTIVE);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Set for max full-scale range (+/-8G) */
|
||||
return regmap_write(data->regmap, FXOS8700_XYZ_DATA_CFG, MODE_8G);
|
||||
}
|
||||
|
||||
static void fxos8700_chip_uninit(void *data)
|
||||
{
|
||||
struct fxos8700_data *fxos8700_data = data;
|
||||
|
||||
fxos8700_set_active_mode(fxos8700_data, FXOS8700_ACCEL, false);
|
||||
fxos8700_set_active_mode(fxos8700_data, FXOS8700_MAGN, false);
|
||||
}
|
||||
|
||||
int fxos8700_core_probe(struct device *dev, struct regmap *regmap,
|
||||
const char *name, bool use_spi)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct fxos8700_data *data;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
data = iio_priv(indio_dev);
|
||||
dev_set_drvdata(dev, indio_dev);
|
||||
data->regmap = regmap;
|
||||
|
||||
ret = fxos8700_chip_init(data, use_spi);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = devm_add_action_or_reset(dev, fxos8700_chip_uninit, data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
indio_dev->dev.parent = dev;
|
||||
indio_dev->channels = fxos8700_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(fxos8700_channels);
|
||||
indio_dev->name = name ? name : "fxos8700";
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &fxos8700_info;
|
||||
|
||||
return devm_iio_device_register(dev, indio_dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fxos8700_core_probe);
|
||||
|
||||
MODULE_AUTHOR("Robert Jones <rjones@gateworks.com>");
|
||||
MODULE_DESCRIPTION("FXOS8700 6-Axis Acc and Mag Combo Sensor driver");
|
||||
MODULE_LICENSE("GPL v2");
|
71
drivers/iio/imu/fxos8700_i2c.c
Normal file
71
drivers/iio/imu/fxos8700_i2c.c
Normal file
@ -0,0 +1,71 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* FXOS8700 - NXP IMU, I2C bits
|
||||
*
|
||||
* 7-bit I2C slave address determined by SA1 and SA0 logic level
|
||||
* inputs represented in the following table:
|
||||
* SA1 | SA0 | Slave Address
|
||||
* 0 | 0 | 0x1E
|
||||
* 0 | 1 | 0x1D
|
||||
* 1 | 0 | 0x1C
|
||||
* 1 | 1 | 0x1F
|
||||
*/
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include "fxos8700.h"
|
||||
|
||||
static int fxos8700_i2c_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct regmap *regmap;
|
||||
const char *name = NULL;
|
||||
|
||||
regmap = devm_regmap_init_i2c(client, &fxos8700_regmap_config);
|
||||
if (IS_ERR(regmap)) {
|
||||
dev_err(&client->dev, "Failed to register i2c regmap %d\n",
|
||||
(int)PTR_ERR(regmap));
|
||||
return PTR_ERR(regmap);
|
||||
}
|
||||
|
||||
if (id)
|
||||
name = id->name;
|
||||
|
||||
return fxos8700_core_probe(&client->dev, regmap, name, false);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id fxos8700_i2c_id[] = {
|
||||
{"fxos8700", 0},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, fxos8700_i2c_id);
|
||||
|
||||
static const struct acpi_device_id fxos8700_acpi_match[] = {
|
||||
{"FXOS8700", 0},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, fxos8700_acpi_match);
|
||||
|
||||
static const struct of_device_id fxos8700_of_match[] = {
|
||||
{ .compatible = "nxp,fxos8700" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, fxos8700_of_match);
|
||||
|
||||
static struct i2c_driver fxos8700_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "fxos8700_i2c",
|
||||
.acpi_match_table = ACPI_PTR(fxos8700_acpi_match),
|
||||
.of_match_table = fxos8700_of_match,
|
||||
},
|
||||
.probe = fxos8700_i2c_probe,
|
||||
.id_table = fxos8700_i2c_id,
|
||||
};
|
||||
module_i2c_driver(fxos8700_i2c_driver);
|
||||
|
||||
MODULE_AUTHOR("Robert Jones <rjones@gateworks.com>");
|
||||
MODULE_DESCRIPTION("FXOS8700 I2C driver");
|
||||
MODULE_LICENSE("GPL v2");
|
59
drivers/iio/imu/fxos8700_spi.c
Normal file
59
drivers/iio/imu/fxos8700_spi.c
Normal file
@ -0,0 +1,59 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* FXOS8700 - NXP IMU, SPI bits
|
||||
*/
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#include "fxos8700.h"
|
||||
|
||||
static int fxos8700_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
struct regmap *regmap;
|
||||
const struct spi_device_id *id = spi_get_device_id(spi);
|
||||
|
||||
regmap = devm_regmap_init_spi(spi, &fxos8700_regmap_config);
|
||||
if (IS_ERR(regmap)) {
|
||||
dev_err(&spi->dev, "Failed to register spi regmap %d\n",
|
||||
(int)PTR_ERR(regmap));
|
||||
return PTR_ERR(regmap);
|
||||
}
|
||||
|
||||
return fxos8700_core_probe(&spi->dev, regmap, id->name, true);
|
||||
}
|
||||
|
||||
static const struct spi_device_id fxos8700_spi_id[] = {
|
||||
{"fxos8700", 0},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, fxos8700_spi_id);
|
||||
|
||||
static const struct acpi_device_id fxos8700_acpi_match[] = {
|
||||
{"FXOS8700", 0},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, fxos8700_acpi_match);
|
||||
|
||||
static const struct of_device_id fxos8700_of_match[] = {
|
||||
{ .compatible = "nxp,fxos8700" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, fxos8700_of_match);
|
||||
|
||||
static struct spi_driver fxos8700_spi_driver = {
|
||||
.probe = fxos8700_spi_probe,
|
||||
.id_table = fxos8700_spi_id,
|
||||
.driver = {
|
||||
.acpi_match_table = ACPI_PTR(fxos8700_acpi_match),
|
||||
.of_match_table = fxos8700_of_match,
|
||||
.name = "fxos8700_spi",
|
||||
},
|
||||
};
|
||||
module_spi_driver(fxos8700_spi_driver);
|
||||
|
||||
MODULE_AUTHOR("Robert Jones <rjones@gateworks.com>");
|
||||
MODULE_DESCRIPTION("FXOS8700 SPI driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -4,10 +4,11 @@
|
||||
#
|
||||
|
||||
obj-$(CONFIG_INV_MPU6050_IIO) += inv-mpu6050.o
|
||||
inv-mpu6050-objs := inv_mpu_core.o inv_mpu_ring.o inv_mpu_trigger.o
|
||||
inv-mpu6050-y := inv_mpu_core.o inv_mpu_ring.o inv_mpu_trigger.o \
|
||||
inv_mpu_aux.o inv_mpu_magn.o
|
||||
|
||||
obj-$(CONFIG_INV_MPU6050_I2C) += inv-mpu6050-i2c.o
|
||||
inv-mpu6050-i2c-objs := inv_mpu_i2c.o inv_mpu_acpi.o
|
||||
inv-mpu6050-i2c-y := inv_mpu_i2c.o inv_mpu_acpi.o
|
||||
|
||||
obj-$(CONFIG_INV_MPU6050_SPI) += inv-mpu6050-spi.o
|
||||
inv-mpu6050-spi-objs := inv_mpu_spi.o
|
||||
inv-mpu6050-spi-y := inv_mpu_spi.o
|
||||
|
204
drivers/iio/imu/inv_mpu6050/inv_mpu_aux.c
Normal file
204
drivers/iio/imu/inv_mpu6050/inv_mpu_aux.c
Normal file
@ -0,0 +1,204 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2019 TDK-InvenSense, Inc.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include "inv_mpu_aux.h"
|
||||
#include "inv_mpu_iio.h"
|
||||
|
||||
/*
|
||||
* i2c master auxiliary bus transfer function.
|
||||
* Requires the i2c operations to be correctly setup before.
|
||||
*/
|
||||
static int inv_mpu_i2c_master_xfer(const struct inv_mpu6050_state *st)
|
||||
{
|
||||
/* use 50hz frequency for xfer */
|
||||
const unsigned int freq = 50;
|
||||
const unsigned int period_ms = 1000 / freq;
|
||||
uint8_t d;
|
||||
unsigned int user_ctrl;
|
||||
int ret;
|
||||
|
||||
/* set sample rate */
|
||||
d = INV_MPU6050_FIFO_RATE_TO_DIVIDER(freq);
|
||||
ret = regmap_write(st->map, st->reg->sample_rate_div, d);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* start i2c master */
|
||||
user_ctrl = st->chip_config.user_ctrl | INV_MPU6050_BIT_I2C_MST_EN;
|
||||
ret = regmap_write(st->map, st->reg->user_ctrl, user_ctrl);
|
||||
if (ret)
|
||||
goto error_restore_rate;
|
||||
|
||||
/* wait for xfer: 1 period + half-period margin */
|
||||
msleep(period_ms + period_ms / 2);
|
||||
|
||||
/* stop i2c master */
|
||||
user_ctrl = st->chip_config.user_ctrl;
|
||||
ret = regmap_write(st->map, st->reg->user_ctrl, user_ctrl);
|
||||
if (ret)
|
||||
goto error_stop_i2c;
|
||||
|
||||
/* restore sample rate */
|
||||
d = st->chip_config.divider;
|
||||
ret = regmap_write(st->map, st->reg->sample_rate_div, d);
|
||||
if (ret)
|
||||
goto error_restore_rate;
|
||||
|
||||
return 0;
|
||||
|
||||
error_stop_i2c:
|
||||
regmap_write(st->map, st->reg->user_ctrl, st->chip_config.user_ctrl);
|
||||
error_restore_rate:
|
||||
regmap_write(st->map, st->reg->sample_rate_div, st->chip_config.divider);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* inv_mpu_aux_init() - init i2c auxiliary bus
|
||||
* @st: driver internal state
|
||||
*
|
||||
* Returns 0 on success, a negative error code otherwise.
|
||||
*/
|
||||
int inv_mpu_aux_init(const struct inv_mpu6050_state *st)
|
||||
{
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
/* configure i2c master */
|
||||
val = INV_MPU6050_BITS_I2C_MST_CLK_400KHZ |
|
||||
INV_MPU6050_BIT_WAIT_FOR_ES;
|
||||
ret = regmap_write(st->map, INV_MPU6050_REG_I2C_MST_CTRL, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* configure i2c master delay */
|
||||
ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV4_CTRL, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
val = INV_MPU6050_BIT_I2C_SLV0_DLY_EN |
|
||||
INV_MPU6050_BIT_I2C_SLV1_DLY_EN |
|
||||
INV_MPU6050_BIT_I2C_SLV2_DLY_EN |
|
||||
INV_MPU6050_BIT_I2C_SLV3_DLY_EN |
|
||||
INV_MPU6050_BIT_DELAY_ES_SHADOW;
|
||||
return regmap_write(st->map, INV_MPU6050_REG_I2C_MST_DELAY_CTRL, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* inv_mpu_aux_read() - read register function for i2c auxiliary bus
|
||||
* @st: driver internal state.
|
||||
* @addr: chip i2c Address
|
||||
* @reg: chip register address
|
||||
* @val: buffer for storing read bytes
|
||||
* @size: number of bytes to read
|
||||
*
|
||||
* Returns 0 on success, a negative error code otherwise.
|
||||
*/
|
||||
int inv_mpu_aux_read(const struct inv_mpu6050_state *st, uint8_t addr,
|
||||
uint8_t reg, uint8_t *val, size_t size)
|
||||
{
|
||||
unsigned int status;
|
||||
int ret;
|
||||
|
||||
if (size > 0x0F)
|
||||
return -EINVAL;
|
||||
|
||||
/* setup i2c SLV0 control: i2c addr, register, enable + size */
|
||||
ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_ADDR(0),
|
||||
INV_MPU6050_BIT_I2C_SLV_RNW | addr);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_REG(0), reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_CTRL(0),
|
||||
INV_MPU6050_BIT_SLV_EN | size);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* do i2c xfer */
|
||||
ret = inv_mpu_i2c_master_xfer(st);
|
||||
if (ret)
|
||||
goto error_disable_i2c;
|
||||
|
||||
/* disable i2c slave */
|
||||
ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_CTRL(0), 0);
|
||||
if (ret)
|
||||
goto error_disable_i2c;
|
||||
|
||||
/* check i2c status */
|
||||
ret = regmap_read(st->map, INV_MPU6050_REG_I2C_MST_STATUS, &status);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (status & INV_MPU6050_BIT_I2C_SLV0_NACK)
|
||||
return -EIO;
|
||||
|
||||
/* read data in registers */
|
||||
return regmap_bulk_read(st->map, INV_MPU6050_REG_EXT_SENS_DATA,
|
||||
val, size);
|
||||
|
||||
error_disable_i2c:
|
||||
regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_CTRL(0), 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* inv_mpu_aux_write() - write register function for i2c auxiliary bus
|
||||
* @st: driver internal state.
|
||||
* @addr: chip i2c Address
|
||||
* @reg: chip register address
|
||||
* @val: 1 byte value to write
|
||||
*
|
||||
* Returns 0 on success, a negative error code otherwise.
|
||||
*/
|
||||
int inv_mpu_aux_write(const struct inv_mpu6050_state *st, uint8_t addr,
|
||||
uint8_t reg, uint8_t val)
|
||||
{
|
||||
unsigned int status;
|
||||
int ret;
|
||||
|
||||
/* setup i2c SLV0 control: i2c addr, register, value, enable + size */
|
||||
ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_ADDR(0), addr);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_REG(0), reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_DO(0), val);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_CTRL(0),
|
||||
INV_MPU6050_BIT_SLV_EN | 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* do i2c xfer */
|
||||
ret = inv_mpu_i2c_master_xfer(st);
|
||||
if (ret)
|
||||
goto error_disable_i2c;
|
||||
|
||||
/* disable i2c slave */
|
||||
ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_CTRL(0), 0);
|
||||
if (ret)
|
||||
goto error_disable_i2c;
|
||||
|
||||
/* check i2c status */
|
||||
ret = regmap_read(st->map, INV_MPU6050_REG_I2C_MST_STATUS, &status);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (status & INV_MPU6050_BIT_I2C_SLV0_NACK)
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
|
||||
error_disable_i2c:
|
||||
regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_CTRL(0), 0);
|
||||
return ret;
|
||||
}
|
19
drivers/iio/imu/inv_mpu6050/inv_mpu_aux.h
Normal file
19
drivers/iio/imu/inv_mpu6050/inv_mpu_aux.h
Normal file
@ -0,0 +1,19 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (C) 2019 TDK-InvenSense, Inc.
|
||||
*/
|
||||
|
||||
#ifndef INV_MPU_AUX_H_
|
||||
#define INV_MPU_AUX_H_
|
||||
|
||||
#include "inv_mpu_iio.h"
|
||||
|
||||
int inv_mpu_aux_init(const struct inv_mpu6050_state *st);
|
||||
|
||||
int inv_mpu_aux_read(const struct inv_mpu6050_state *st, uint8_t addr,
|
||||
uint8_t reg, uint8_t *val, size_t size);
|
||||
|
||||
int inv_mpu_aux_write(const struct inv_mpu6050_state *st, uint8_t addr,
|
||||
uint8_t reg, uint8_t val);
|
||||
|
||||
#endif /* INV_MPU_AUX_H_ */
|
@ -17,6 +17,7 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include "inv_mpu_iio.h"
|
||||
#include "inv_mpu_magn.h"
|
||||
|
||||
/*
|
||||
* this is the gyro scale translated from dynamic range plus/minus
|
||||
@ -103,6 +104,7 @@ static const struct inv_mpu6050_chip_config chip_config_6050 = {
|
||||
.divider = INV_MPU6050_FIFO_RATE_TO_DIVIDER(INV_MPU6050_INIT_FIFO_RATE),
|
||||
.gyro_fifo_enable = false,
|
||||
.accl_fifo_enable = false,
|
||||
.magn_fifo_enable = false,
|
||||
.accl_fs = INV_MPU6050_FS_02G,
|
||||
.user_ctrl = 0,
|
||||
};
|
||||
@ -341,6 +343,11 @@ static int inv_mpu6050_init_config(struct iio_dev *indio_dev)
|
||||
*/
|
||||
st->chip_period = NSEC_PER_MSEC;
|
||||
|
||||
/* magn chip init, noop if not present in the chip */
|
||||
result = inv_mpu_magn_probe(st);
|
||||
if (result)
|
||||
goto error_power_off;
|
||||
|
||||
return inv_mpu6050_set_power_itg(st, false);
|
||||
|
||||
error_power_off:
|
||||
@ -420,6 +427,9 @@ static int inv_mpu6050_read_channel_data(struct iio_dev *indio_dev,
|
||||
ret = inv_mpu6050_sensor_show(st, st->reg->temperature,
|
||||
IIO_MOD_X, val);
|
||||
break;
|
||||
case IIO_MAGN:
|
||||
ret = inv_mpu_magn_read(st, chan->channel2, val);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
@ -478,6 +488,8 @@ inv_mpu6050_read_raw(struct iio_dev *indio_dev,
|
||||
*val2 = INV_MPU6050_TEMP_SCALE;
|
||||
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
case IIO_MAGN:
|
||||
return inv_mpu_magn_get_scale(st, chan, val, val2);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -719,6 +731,11 @@ inv_mpu6050_fifo_rate_store(struct device *dev, struct device_attribute *attr,
|
||||
if (result)
|
||||
goto fifo_rate_fail_power_off;
|
||||
|
||||
/* update rate for magn, noop if not present in chip */
|
||||
result = inv_mpu_magn_set_rate(st, fifo_rate);
|
||||
if (result)
|
||||
goto fifo_rate_fail_power_off;
|
||||
|
||||
fifo_rate_fail_power_off:
|
||||
result |= inv_mpu6050_set_power_itg(st, false);
|
||||
fifo_rate_fail_unlock:
|
||||
@ -804,8 +821,14 @@ inv_get_mount_matrix(const struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan)
|
||||
{
|
||||
struct inv_mpu6050_state *data = iio_priv(indio_dev);
|
||||
const struct iio_mount_matrix *matrix;
|
||||
|
||||
return &data->orientation;
|
||||
if (chan->type == IIO_MAGN)
|
||||
matrix = &data->magn_orient;
|
||||
else
|
||||
matrix = &data->orientation;
|
||||
|
||||
return matrix;
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec_ext_info inv_ext_info[] = {
|
||||
@ -873,6 +896,98 @@ static const unsigned long inv_mpu_scan_masks[] = {
|
||||
0,
|
||||
};
|
||||
|
||||
#define INV_MPU9X50_MAGN_CHAN(_chan2, _bits, _index) \
|
||||
{ \
|
||||
.type = IIO_MAGN, \
|
||||
.modified = 1, \
|
||||
.channel2 = _chan2, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_SCALE) | \
|
||||
BIT(IIO_CHAN_INFO_RAW), \
|
||||
.scan_index = _index, \
|
||||
.scan_type = { \
|
||||
.sign = 's', \
|
||||
.realbits = _bits, \
|
||||
.storagebits = 16, \
|
||||
.shift = 0, \
|
||||
.endianness = IIO_BE, \
|
||||
}, \
|
||||
.ext_info = inv_ext_info, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec inv_mpu9250_channels[] = {
|
||||
IIO_CHAN_SOFT_TIMESTAMP(INV_MPU9X50_SCAN_TIMESTAMP),
|
||||
/*
|
||||
* Note that temperature should only be via polled reading only,
|
||||
* not the final scan elements output.
|
||||
*/
|
||||
{
|
||||
.type = IIO_TEMP,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW)
|
||||
| BIT(IIO_CHAN_INFO_OFFSET)
|
||||
| BIT(IIO_CHAN_INFO_SCALE),
|
||||
.scan_index = -1,
|
||||
},
|
||||
INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_X, INV_MPU6050_SCAN_GYRO_X),
|
||||
INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_Y, INV_MPU6050_SCAN_GYRO_Y),
|
||||
INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_Z, INV_MPU6050_SCAN_GYRO_Z),
|
||||
|
||||
INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_X, INV_MPU6050_SCAN_ACCL_X),
|
||||
INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_Y, INV_MPU6050_SCAN_ACCL_Y),
|
||||
INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_Z, INV_MPU6050_SCAN_ACCL_Z),
|
||||
|
||||
/* Magnetometer resolution is 16 bits */
|
||||
INV_MPU9X50_MAGN_CHAN(IIO_MOD_X, 16, INV_MPU9X50_SCAN_MAGN_X),
|
||||
INV_MPU9X50_MAGN_CHAN(IIO_MOD_Y, 16, INV_MPU9X50_SCAN_MAGN_Y),
|
||||
INV_MPU9X50_MAGN_CHAN(IIO_MOD_Z, 16, INV_MPU9X50_SCAN_MAGN_Z),
|
||||
};
|
||||
|
||||
static const unsigned long inv_mpu9x50_scan_masks[] = {
|
||||
/* 3-axis accel */
|
||||
BIT(INV_MPU6050_SCAN_ACCL_X)
|
||||
| BIT(INV_MPU6050_SCAN_ACCL_Y)
|
||||
| BIT(INV_MPU6050_SCAN_ACCL_Z),
|
||||
/* 3-axis gyro */
|
||||
BIT(INV_MPU6050_SCAN_GYRO_X)
|
||||
| BIT(INV_MPU6050_SCAN_GYRO_Y)
|
||||
| BIT(INV_MPU6050_SCAN_GYRO_Z),
|
||||
/* 3-axis magn */
|
||||
BIT(INV_MPU9X50_SCAN_MAGN_X)
|
||||
| BIT(INV_MPU9X50_SCAN_MAGN_Y)
|
||||
| BIT(INV_MPU9X50_SCAN_MAGN_Z),
|
||||
/* 6-axis accel + gyro */
|
||||
BIT(INV_MPU6050_SCAN_ACCL_X)
|
||||
| BIT(INV_MPU6050_SCAN_ACCL_Y)
|
||||
| BIT(INV_MPU6050_SCAN_ACCL_Z)
|
||||
| BIT(INV_MPU6050_SCAN_GYRO_X)
|
||||
| BIT(INV_MPU6050_SCAN_GYRO_Y)
|
||||
| BIT(INV_MPU6050_SCAN_GYRO_Z),
|
||||
/* 6-axis accel + magn */
|
||||
BIT(INV_MPU6050_SCAN_ACCL_X)
|
||||
| BIT(INV_MPU6050_SCAN_ACCL_Y)
|
||||
| BIT(INV_MPU6050_SCAN_ACCL_Z)
|
||||
| BIT(INV_MPU9X50_SCAN_MAGN_X)
|
||||
| BIT(INV_MPU9X50_SCAN_MAGN_Y)
|
||||
| BIT(INV_MPU9X50_SCAN_MAGN_Z),
|
||||
/* 6-axis gyro + magn */
|
||||
BIT(INV_MPU6050_SCAN_GYRO_X)
|
||||
| BIT(INV_MPU6050_SCAN_GYRO_Y)
|
||||
| BIT(INV_MPU6050_SCAN_GYRO_Z)
|
||||
| BIT(INV_MPU9X50_SCAN_MAGN_X)
|
||||
| BIT(INV_MPU9X50_SCAN_MAGN_Y)
|
||||
| BIT(INV_MPU9X50_SCAN_MAGN_Z),
|
||||
/* 9-axis accel + gyro + magn */
|
||||
BIT(INV_MPU6050_SCAN_ACCL_X)
|
||||
| BIT(INV_MPU6050_SCAN_ACCL_Y)
|
||||
| BIT(INV_MPU6050_SCAN_ACCL_Z)
|
||||
| BIT(INV_MPU6050_SCAN_GYRO_X)
|
||||
| BIT(INV_MPU6050_SCAN_GYRO_Y)
|
||||
| BIT(INV_MPU6050_SCAN_GYRO_Z)
|
||||
| BIT(INV_MPU9X50_SCAN_MAGN_X)
|
||||
| BIT(INV_MPU9X50_SCAN_MAGN_Y)
|
||||
| BIT(INV_MPU9X50_SCAN_MAGN_Z),
|
||||
0,
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec inv_icm20602_channels[] = {
|
||||
IIO_CHAN_SOFT_TIMESTAMP(INV_ICM20602_SCAN_TIMESTAMP),
|
||||
{
|
||||
@ -1034,14 +1149,14 @@ error_power_off:
|
||||
return result;
|
||||
}
|
||||
|
||||
static int inv_mpu_core_enable_regulator(struct inv_mpu6050_state *st)
|
||||
static int inv_mpu_core_enable_regulator_vddio(struct inv_mpu6050_state *st)
|
||||
{
|
||||
int result;
|
||||
|
||||
result = regulator_enable(st->vddio_supply);
|
||||
if (result) {
|
||||
dev_err(regmap_get_device(st->map),
|
||||
"Failed to enable regulator: %d\n", result);
|
||||
"Failed to enable vddio regulator: %d\n", result);
|
||||
} else {
|
||||
/* Give the device a little bit of time to start up. */
|
||||
usleep_range(35000, 70000);
|
||||
@ -1050,21 +1165,29 @@ static int inv_mpu_core_enable_regulator(struct inv_mpu6050_state *st)
|
||||
return result;
|
||||
}
|
||||
|
||||
static int inv_mpu_core_disable_regulator(struct inv_mpu6050_state *st)
|
||||
static int inv_mpu_core_disable_regulator_vddio(struct inv_mpu6050_state *st)
|
||||
{
|
||||
int result;
|
||||
|
||||
result = regulator_disable(st->vddio_supply);
|
||||
if (result)
|
||||
dev_err(regmap_get_device(st->map),
|
||||
"Failed to disable regulator: %d\n", result);
|
||||
"Failed to disable vddio regulator: %d\n", result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void inv_mpu_core_disable_regulator_action(void *_data)
|
||||
{
|
||||
inv_mpu_core_disable_regulator(_data);
|
||||
struct inv_mpu6050_state *st = _data;
|
||||
int result;
|
||||
|
||||
result = regulator_disable(st->vdd_supply);
|
||||
if (result)
|
||||
dev_err(regmap_get_device(st->map),
|
||||
"Failed to disable vdd regulator: %d\n", result);
|
||||
|
||||
inv_mpu_core_disable_regulator_vddio(st);
|
||||
}
|
||||
|
||||
int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name,
|
||||
@ -1133,6 +1256,15 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
st->vdd_supply = devm_regulator_get(dev, "vdd");
|
||||
if (IS_ERR(st->vdd_supply)) {
|
||||
if (PTR_ERR(st->vdd_supply) != -EPROBE_DEFER)
|
||||
dev_err(dev, "Failed to get vdd regulator %d\n",
|
||||
(int)PTR_ERR(st->vdd_supply));
|
||||
|
||||
return PTR_ERR(st->vdd_supply);
|
||||
}
|
||||
|
||||
st->vddio_supply = devm_regulator_get(dev, "vddio");
|
||||
if (IS_ERR(st->vddio_supply)) {
|
||||
if (PTR_ERR(st->vddio_supply) != -EPROBE_DEFER)
|
||||
@ -1142,9 +1274,17 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name,
|
||||
return PTR_ERR(st->vddio_supply);
|
||||
}
|
||||
|
||||
result = inv_mpu_core_enable_regulator(st);
|
||||
if (result)
|
||||
result = regulator_enable(st->vdd_supply);
|
||||
if (result) {
|
||||
dev_err(dev, "Failed to enable vdd regulator: %d\n", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
result = inv_mpu_core_enable_regulator_vddio(st);
|
||||
if (result) {
|
||||
regulator_disable(st->vdd_supply);
|
||||
return result;
|
||||
}
|
||||
|
||||
result = devm_add_action_or_reset(dev, inv_mpu_core_disable_regulator_action,
|
||||
st);
|
||||
@ -1154,6 +1294,11 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name,
|
||||
return result;
|
||||
}
|
||||
|
||||
/* fill magnetometer orientation */
|
||||
result = inv_mpu_magn_set_orient(st);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
/* power is turned on inside check chip type*/
|
||||
result = inv_check_and_setup_chip(st);
|
||||
if (result)
|
||||
@ -1165,9 +1310,6 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name,
|
||||
return result;
|
||||
}
|
||||
|
||||
if (inv_mpu_bus_setup)
|
||||
inv_mpu_bus_setup(indio_dev);
|
||||
|
||||
dev_set_drvdata(dev, indio_dev);
|
||||
indio_dev->dev.parent = dev;
|
||||
/* name will be NULL when enumerated via ACPI */
|
||||
@ -1176,14 +1318,37 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name,
|
||||
else
|
||||
indio_dev->name = dev_name(dev);
|
||||
|
||||
if (chip_type == INV_ICM20602) {
|
||||
/* requires parent device set in indio_dev */
|
||||
if (inv_mpu_bus_setup)
|
||||
inv_mpu_bus_setup(indio_dev);
|
||||
|
||||
switch (chip_type) {
|
||||
case INV_MPU9250:
|
||||
case INV_MPU9255:
|
||||
/*
|
||||
* Use magnetometer inside the chip only if there is no i2c
|
||||
* auxiliary device in use.
|
||||
*/
|
||||
if (!st->magn_disabled) {
|
||||
indio_dev->channels = inv_mpu9250_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(inv_mpu9250_channels);
|
||||
indio_dev->available_scan_masks = inv_mpu9x50_scan_masks;
|
||||
} else {
|
||||
indio_dev->channels = inv_mpu_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(inv_mpu_channels);
|
||||
indio_dev->available_scan_masks = inv_mpu_scan_masks;
|
||||
}
|
||||
break;
|
||||
case INV_ICM20602:
|
||||
indio_dev->channels = inv_icm20602_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(inv_icm20602_channels);
|
||||
indio_dev->available_scan_masks = inv_icm20602_scan_masks;
|
||||
} else {
|
||||
break;
|
||||
default:
|
||||
indio_dev->channels = inv_mpu_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(inv_mpu_channels);
|
||||
indio_dev->available_scan_masks = inv_mpu_scan_masks;
|
||||
break;
|
||||
}
|
||||
|
||||
indio_dev->info = &mpu_info;
|
||||
@ -1221,7 +1386,7 @@ static int inv_mpu_resume(struct device *dev)
|
||||
int result;
|
||||
|
||||
mutex_lock(&st->lock);
|
||||
result = inv_mpu_core_enable_regulator(st);
|
||||
result = inv_mpu_core_enable_regulator_vddio(st);
|
||||
if (result)
|
||||
goto out_unlock;
|
||||
|
||||
@ -1239,7 +1404,7 @@ static int inv_mpu_suspend(struct device *dev)
|
||||
|
||||
mutex_lock(&st->lock);
|
||||
result = inv_mpu6050_set_power_itg(st, false);
|
||||
inv_mpu_core_disable_regulator(st);
|
||||
inv_mpu_core_disable_regulator_vddio(st);
|
||||
mutex_unlock(&st->lock);
|
||||
|
||||
return result;
|
||||
|
@ -68,6 +68,56 @@ static const char *inv_mpu_match_acpi_device(struct device *dev,
|
||||
return dev_name(dev);
|
||||
}
|
||||
|
||||
static bool inv_mpu_i2c_aux_bus(struct device *dev)
|
||||
{
|
||||
struct inv_mpu6050_state *st = iio_priv(dev_get_drvdata(dev));
|
||||
|
||||
switch (st->chip_type) {
|
||||
case INV_ICM20608:
|
||||
case INV_ICM20602:
|
||||
/* no i2c auxiliary bus on the chip */
|
||||
return false;
|
||||
case INV_MPU9250:
|
||||
case INV_MPU9255:
|
||||
if (st->magn_disabled)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* MPU9xxx magnetometer support requires to disable i2c auxiliary bus support.
|
||||
* To ensure backward compatibility with existing setups, do not disable
|
||||
* i2c auxiliary bus if it used.
|
||||
* Check for i2c-gate node in devicetree and set magnetometer disabled.
|
||||
* Only MPU6500 is supported by ACPI, no need to check.
|
||||
*/
|
||||
static int inv_mpu_magn_disable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct inv_mpu6050_state *st = iio_priv(indio_dev);
|
||||
struct device *dev = indio_dev->dev.parent;
|
||||
struct device_node *mux_node;
|
||||
|
||||
switch (st->chip_type) {
|
||||
case INV_MPU9250:
|
||||
case INV_MPU9255:
|
||||
mux_node = of_get_child_by_name(dev->of_node, "i2c-gate");
|
||||
if (mux_node != NULL) {
|
||||
st->magn_disabled = true;
|
||||
dev_warn(dev, "disable internal use of magnetometer\n");
|
||||
}
|
||||
of_node_put(mux_node);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* inv_mpu_probe() - probe function.
|
||||
* @client: i2c client.
|
||||
@ -112,17 +162,12 @@ static int inv_mpu_probe(struct i2c_client *client,
|
||||
}
|
||||
|
||||
result = inv_mpu_core_probe(regmap, client->irq, name,
|
||||
NULL, chip_type);
|
||||
inv_mpu_magn_disable, chip_type);
|
||||
if (result < 0)
|
||||
return result;
|
||||
|
||||
st = iio_priv(dev_get_drvdata(&client->dev));
|
||||
switch (st->chip_type) {
|
||||
case INV_ICM20608:
|
||||
case INV_ICM20602:
|
||||
/* no i2c auxiliary bus on the chip */
|
||||
break;
|
||||
default:
|
||||
if (inv_mpu_i2c_aux_bus(&client->dev)) {
|
||||
/* declare i2c auxiliary bus */
|
||||
st->muxc = i2c_mux_alloc(client->adapter, &client->dev,
|
||||
1, 0, I2C_MUX_LOCKED | I2C_MUX_GATE,
|
||||
@ -137,7 +182,6 @@ static int inv_mpu_probe(struct i2c_client *client,
|
||||
result = inv_mpu_acpi_create_mux_client(client);
|
||||
if (result)
|
||||
goto out_del_mux;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user