First set of IIO new device support, cleanups etc for 5.12
Includes one immutable branch, to support some qcom-vadc patches going through IIO and thermal. Late rebase to drop a patch that should go through the hid tree. New device support: * adi,ad5766 - New driver supporting AD5766 and AD5767 16 channel DACs. * adi,ad7476 - Support for LTC2314-14 14 bit ADC (trivial to add) * hid-sensors-hinge - New driver including HID custom sensor support. * invensense,mpu6050 - Add support for the MPU-6880 (chip info all that is needed) * memsic,ms5637 - Add support for ms5803 device after a bunch of rework. * xilinx-xadc - Add support for Ultrascale System Monitor. * yamaha,yas530 - New driver for this magnetometer supporting YAS530, YAS532 adn YAS 533. Dt-binding conversions to yaml * invensense,mpu3050 * invensense,mpu6050 Cleanups and minor features * core - Copy iio_info.attrs->is_visible along with the attrs themselves. - Handle enumerate properties with gaps (i.e. reserved values in the middle of otherwise used values). - Add an of_iio_channel_get_by_name() function. * adi,adf4350 - Drop an unnecessary NULL check. * amstaos,tsl2583 - Use DIV_ROUND_CLOSEST in place of open coding. * avago,apds9960 - Add MSHW0184 ACPI id seen in the Microsoft Surface Book 3 and Surface Pro 7. * bosch,bmc150_magn - Basic regulator support. * bosch,bme680 - Use DIV_ROUND_CLOSEST in place of opencoding. * bosch,bmg160 - Basic regulator support. * hid-sensors - Add timestamp channels to all sensors types. * kionix,kxcjk1013 - Basic regulator support. * memsic - Fix ordering in trivial-device.yaml * microchip,mcp4725 - More flexible restrictions in DT binding. * plantower,pms7003 - Fix comma that should be semicolon. * qcom-vadc - Refactors to support addition of ADC-TM5 driver - Addition of a fixp_linear_interpolate function to support this common operation. * sprd,sc27xx_adc - Use DIV_ROUND_CLOSEST in place of opencoding. * st,ab8500-adc - Enable non-hw-conversion as AB505 doesn't support it. * st,stm32-adc - Drop unneeded NULL check. * st,stm32-dfsdm - Drop unneeded NULL check. * st,vl6180 - Use DIV_ROUND_CLOSEST in place of opencoding. * xilinx-xadc - Local var for &pdev->dev to avoid excessive repetition. - devm_ throughout and drop remove() -----BEGIN PGP SIGNATURE----- iQJFBAABCAAvFiEEbilms4eEBlKRJoGxVIU0mcT0FogFAmAKlDkRHGppYzIzQGtl cm5lbC5vcmcACgkQVIU0mcT0FohMrw/+POQpOgJ1ZEEZpIXFxtYnOC8QAPQk6SFf syt+m+THauWQ6tcL2GRaE4QDvcBqWvGV+YKK0S9TaQ+L+4R2Lp/6XJ9Gp+EeB2z+ tnggshWbox4expvnSmKEFysLoiYQ5xmCf+EsGNs8UdVVCYpK4jdut914ev4ZqaGY zy1aopGh0XHHtrrGzqhE6q+p5FG11Iewi3iuvq11jK5DrmwddyhempUqkGPGow/1 JIgVDgGOHXka85L7z8uJ/6JQtVRUTyUehb8fXLEDH7htSHCt8B62OwN/rwPIRXRY JElbwetptpM753UUS0qoMGyqcEEoOI2Cr5BEJVfOfpWH2O/lVcF+XlbCiD3wQT48 1DjHfts0jO8X/O+C4ZbglfGM/AZ2fRmZLFy7mObK+CixFwH5wXv0zhVsHNzK4Vzu e0fHbMtGgK8W0Gp9kQL0Ki+SJkpnI6Ilujl0jOrmGenmW4t1i6k51okGDpVJ8azd wUHh0gvLEK0T1d2Cq8GdU+pUGyxNqu3bOSXLrMjJ+f+UFYHDcFWxWLD1p8YG6h0f cS40NtDfCQOWTy476Kg/jXAG4yQrEKz0dm6m4PoqB1WKZPLrMzgCapPLJVLdU71c Y+kH2MHHbwHrRGqcn0FE9kTJO3RaWax10QcMgn3ZWTMqOIgpW4zRpr1rSREdLiND buScx7gBKPY= =GLx1 -----END PGP SIGNATURE----- Merge tag 'iio-for-5.12a' of https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio into staging-next Jonathan writes: First set of IIO new device support, cleanups etc for 5.12 Includes one immutable branch, to support some qcom-vadc patches going through IIO and thermal. Late rebase to drop a patch that should go through the hid tree. New device support: * adi,ad5766 - New driver supporting AD5766 and AD5767 16 channel DACs. * adi,ad7476 - Support for LTC2314-14 14 bit ADC (trivial to add) * hid-sensors-hinge - New driver including HID custom sensor support. * invensense,mpu6050 - Add support for the MPU-6880 (chip info all that is needed) * memsic,ms5637 - Add support for ms5803 device after a bunch of rework. * xilinx-xadc - Add support for Ultrascale System Monitor. * yamaha,yas530 - New driver for this magnetometer supporting YAS530, YAS532 adn YAS 533. Dt-binding conversions to yaml * invensense,mpu3050 * invensense,mpu6050 Cleanups and minor features * core - Copy iio_info.attrs->is_visible along with the attrs themselves. - Handle enumerate properties with gaps (i.e. reserved values in the middle of otherwise used values). - Add an of_iio_channel_get_by_name() function. * adi,adf4350 - Drop an unnecessary NULL check. * amstaos,tsl2583 - Use DIV_ROUND_CLOSEST in place of open coding. * avago,apds9960 - Add MSHW0184 ACPI id seen in the Microsoft Surface Book 3 and Surface Pro 7. * bosch,bmc150_magn - Basic regulator support. * bosch,bme680 - Use DIV_ROUND_CLOSEST in place of opencoding. * bosch,bmg160 - Basic regulator support. * hid-sensors - Add timestamp channels to all sensors types. * kionix,kxcjk1013 - Basic regulator support. * memsic - Fix ordering in trivial-device.yaml * microchip,mcp4725 - More flexible restrictions in DT binding. * plantower,pms7003 - Fix comma that should be semicolon. * qcom-vadc - Refactors to support addition of ADC-TM5 driver - Addition of a fixp_linear_interpolate function to support this common operation. * sprd,sc27xx_adc - Use DIV_ROUND_CLOSEST in place of opencoding. * st,ab8500-adc - Enable non-hw-conversion as AB505 doesn't support it. * st,stm32-adc - Drop unneeded NULL check. * st,stm32-dfsdm - Drop unneeded NULL check. * st,vl6180 - Use DIV_ROUND_CLOSEST in place of opencoding. * xilinx-xadc - Local var for &pdev->dev to avoid excessive repetition. - devm_ throughout and drop remove() * tag 'iio-for-5.12a' of https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio: (59 commits) iio: adc: stm32-dfsdm: Remove redundant null check before clk_disable_unprepare iio:pressure:ms5637: add ms5803 support iio:common:ms_sensors:ms_sensors_i2c: add support for alternative PROM layout iio:common:ms_sensors:ms_sensors_i2c: rework CRC calculation helper iio:pressure:ms5637: limit available sample frequencies iio:pressure:ms5637: introduce hardware differentiation dt-bindings: trivial-devices: reorder memsic devices iio: dac: ad5766: add driver support for AD5766 Documentation/ABI/testing: Add documentation for AD5766 new ABI dt-bindings: iio: dac: AD5766 yaml documentation iio: hid-sensor-rotation: Add timestamp channel iio: hid-sensor-incl-3d: Add timestamp channel iio: hid-sensor-magn-3d: Add timestamp channel iio: hid-sensor-als: Add timestamp channel iio: hid-sensor-gyro-3d: Add timestamp channel iio: hid-sensor-accel-3d: Add timestamp channel for gravity sensor iio: magnetometer: bmc150: Add rudimentary regulator support dt-bindings: iio: magnetometer: bmc150: Document regulator supplies iio: Handle enumerated properties with gaps iio:Documentation: Add documentation for hinge sensor channels ...
This commit is contained in:
Коммит
8598bb4c87
|
@ -198,6 +198,7 @@ Description:
|
|||
Units after application of scale and offset are m/s^2.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_angl_raw
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_anglY_raw
|
||||
KernelVersion: 4.17
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
|
@ -1812,3 +1813,13 @@ Contact: linux-iio@vger.kernel.org
|
|||
Description:
|
||||
Unscaled light intensity according to CIE 1931/DIN 5033 color space.
|
||||
Units after application of scale are nano nanowatts per square meter.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_anglY_label
|
||||
KernelVersion: 5.12
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Optional symbolic label for channel Y.
|
||||
For Intel hid hinge sensor, the label values are:
|
||||
hinge, keyboard, screen. It means the three channels
|
||||
each correspond respectively to hinge angle, keyboard angle,
|
||||
and screen angle.
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_dither_enable
|
||||
KernelVersion: 5.12
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Dither enable. Write 1 to enable dither or 0 to disable it.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_dither_invert
|
||||
KernelVersion: 5.12
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Inverts the dither applied to the selected DAC channel. Dither is not
|
||||
inverted by default. Write "1" to invert dither.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_dither_scale_available
|
||||
KernelVersion: 5.12
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Returns possible scalings available for the current channel.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_dither_scale
|
||||
KernelVersion: 5.12
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Scales the dither before it is applied to the selected channel.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_dither_source
|
||||
KernelVersion: 5.12
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Selects dither source applied to the selected channel. Write "0" to
|
||||
select N0 source, write "1" to select N1 source.
|
|
@ -20,6 +20,9 @@ properties:
|
|||
reg:
|
||||
maxItems: 1
|
||||
|
||||
vdd-supply: true
|
||||
vddio-supply: true
|
||||
|
||||
mount-matrix:
|
||||
description: an optional 3x3 mounting rotation matrix.
|
||||
|
||||
|
|
|
@ -1,13 +1,22 @@
|
|||
Xilinx XADC device driver
|
||||
|
||||
This binding document describes the bindings for both of them since the
|
||||
bindings are very similar. The Xilinx XADC is a ADC that can be found in the
|
||||
series 7 FPGAs from Xilinx. The XADC has a DRP interface for communication.
|
||||
Currently two different frontends for the DRP interface exist. One that is only
|
||||
available on the ZYNQ family as a hardmacro in the SoC portion of the ZYNQ. The
|
||||
other one is available on all series 7 platforms and is a softmacro with a AXI
|
||||
interface. This binding document describes the bindings for both of them since
|
||||
the bindings are very similar.
|
||||
This binding document describes the bindings for the Xilinx 7 Series XADC as well
|
||||
as the UltraScale/UltraScale+ System Monitor.
|
||||
|
||||
The Xilinx XADC is an ADC that can be found in the Series 7 FPGAs from Xilinx.
|
||||
The XADC has a DRP interface for communication. Currently two different
|
||||
frontends for the DRP interface exist. One that is only available on the ZYNQ
|
||||
family as a hardmacro in the SoC portion of the ZYNQ. The other one is available
|
||||
on all series 7 platforms and is a softmacro with a AXI interface. This binding
|
||||
document describes the bindings for both of them since the bindings are very
|
||||
similar.
|
||||
|
||||
The Xilinx System Monitor is an ADC that is found in the UltraScale and
|
||||
UltraScale+ FPGAs from Xilinx. The System Monitor provides a DRP interface for
|
||||
communication. Xilinx provides a standard IP core that can be used to access the
|
||||
System Monitor through an AXI interface in the FPGA fabric. This IP core is
|
||||
called the Xilinx System Management Wizard. This document describes the bindings
|
||||
for this IP.
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be one of
|
||||
|
@ -15,11 +24,14 @@ Required properties:
|
|||
configuration interface to interface to the XADC hardmacro.
|
||||
* "xlnx,axi-xadc-1.00.a": When using the axi-xadc pcore to
|
||||
interface to the XADC hardmacro.
|
||||
* "xlnx,system-management-wiz-1.3": When using the
|
||||
Xilinx System Management Wizard fabric IP core to access the
|
||||
UltraScale and UltraScale+ System Monitor.
|
||||
- reg: Address and length of the register set for the device
|
||||
- interrupts: Interrupt for the XADC control interface.
|
||||
- clocks: When using the ZYNQ this must be the ZYNQ PCAP clock,
|
||||
when using the AXI-XADC pcore this must be the clock that provides the
|
||||
clock to the AXI bus interface of the core.
|
||||
when using the axi-xadc or the axi-system-management-wizard this must be
|
||||
the clock that provides the clock to the AXI bus interface of the core.
|
||||
|
||||
Optional properties:
|
||||
- xlnx,external-mux:
|
||||
|
@ -110,3 +122,20 @@ Examples:
|
|||
};
|
||||
};
|
||||
};
|
||||
|
||||
adc@80000000 {
|
||||
compatible = "xlnx,system-management-wiz-1.3";
|
||||
reg = <0x80000000 0x1000>;
|
||||
interrupts = <0 81 4>;
|
||||
interrupt-parent = <&gic>;
|
||||
clocks = <&fpga1_clk>;
|
||||
|
||||
xlnx,channels {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
channel@0 {
|
||||
reg = <0>;
|
||||
xlnx,bipolar;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
# Copyright 2020 Analog Devices Inc.
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/dac/adi,ad5766.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Analog Devices AD5766 DAC device driver
|
||||
|
||||
maintainers:
|
||||
- Cristian Pop <cristian.pop@analog.com>
|
||||
|
||||
description: |
|
||||
Bindings for the Analog Devices AD5766 current DAC device. Datasheet can be
|
||||
found here:
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/ad5766-5767.pdf
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- adi,ad5766
|
||||
- adi,ad5767
|
||||
|
||||
output-range-microvolts:
|
||||
description: Select converter output range.
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
spi-max-frequency:
|
||||
maximum: 1000000
|
||||
|
||||
spi-cpol: true
|
||||
|
||||
reset-gpios:
|
||||
description: GPIO spec for the RESET pin. As the line is active low, it
|
||||
should be marked GPIO_ACTIVE_LOW.
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- output-range-microvolts
|
||||
- reg
|
||||
- spi-max-frequency
|
||||
- spi-cpol
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
spi {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
ad5766@0 {
|
||||
compatible = "adi,ad5766";
|
||||
output-range-microvolts = <(-5000) 5000>;
|
||||
reg = <0>;
|
||||
spi-cpol;
|
||||
spi-max-frequency = <1000000>;
|
||||
reset-gpios = <&gpio 22 0>;
|
||||
};
|
||||
};
|
|
@ -39,20 +39,39 @@ properties:
|
|||
|
||||
allOf:
|
||||
- if:
|
||||
not:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: microchip,mcp4726
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: microchip,mcp4725
|
||||
then:
|
||||
properties:
|
||||
vref-supply: false
|
||||
required:
|
||||
- vdd-supply
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: microchip,mcp4726
|
||||
then:
|
||||
anyOf:
|
||||
- required:
|
||||
- vdd-supply
|
||||
- required:
|
||||
- vref-supply
|
||||
|
||||
- if:
|
||||
not:
|
||||
required:
|
||||
- vref-supply
|
||||
then:
|
||||
properties:
|
||||
microchip,vref-buffered: false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- vdd-supply
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
|
|
|
@ -19,6 +19,9 @@ properties:
|
|||
reg:
|
||||
maxItems: 1
|
||||
|
||||
vdd-supply: true
|
||||
vddio-supply: true
|
||||
|
||||
interrupts:
|
||||
minItems: 1
|
||||
description:
|
||||
|
|
|
@ -1,45 +0,0 @@
|
|||
Invensense MPU-3050 Gyroscope device tree bindings
|
||||
|
||||
Required properties:
|
||||
- compatible : should be "invensense,mpu3050"
|
||||
- reg : the I2C address of the sensor
|
||||
|
||||
Optional properties:
|
||||
- interrupts : interrupt mapping for the trigger interrupt from the
|
||||
internal oscillator. The following IRQ modes are supported:
|
||||
IRQ_TYPE_EDGE_RISING, IRQ_TYPE_EDGE_FALLING, IRQ_TYPE_LEVEL_HIGH and
|
||||
IRQ_TYPE_LEVEL_LOW. The driver should detect and configure the hardware
|
||||
for the desired interrupt type.
|
||||
- vdd-supply : supply regulator for the main power voltage.
|
||||
- vlogic-supply : supply regulator for the signal voltage.
|
||||
- mount-matrix : see iio/mount-matrix.txt
|
||||
|
||||
Optional subnodes:
|
||||
- The MPU-3050 will pass through and forward the I2C signals from the
|
||||
incoming I2C bus, alternatively drive traffic to a slave device (usually
|
||||
an accelerometer) on its own initiative. Therefore is supports a subnode
|
||||
i2c gate node. For details see: i2c/i2c-gate.txt
|
||||
|
||||
Example:
|
||||
|
||||
mpu3050@68 {
|
||||
compatible = "invensense,mpu3050";
|
||||
reg = <0x68>;
|
||||
interrupt-parent = <&foo>;
|
||||
interrupts = <12 IRQ_TYPE_EDGE_FALLING>;
|
||||
vdd-supply = <&bar>;
|
||||
vlogic-supply = <&baz>;
|
||||
|
||||
/* External I2C interface */
|
||||
i2c-gate {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
fnord@18 {
|
||||
compatible = "fnord";
|
||||
reg = <0x18>;
|
||||
interrupt-parent = <&foo>;
|
||||
interrupts = <13 IRQ_TYPE_EDGE_FALLING>;
|
||||
};
|
||||
};
|
||||
};
|
|
@ -0,0 +1,70 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/gyroscope/invensense,mpu3050.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Invensense MPU-3050 Gyroscope
|
||||
|
||||
maintainers:
|
||||
- Linus Walleij <linus.walleij@linaro.org>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: invensense,mpu3050
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
vdd-supply: true
|
||||
|
||||
vlogic-supply: true
|
||||
|
||||
interrupts:
|
||||
minItems: 1
|
||||
description:
|
||||
Interrupt mapping for the trigger interrupt from the internal oscillator.
|
||||
|
||||
mount-matrix: true
|
||||
|
||||
i2c-gate:
|
||||
$ref: /schemas/i2c/i2c-controller.yaml
|
||||
unevaluatedProperties: false
|
||||
description: |
|
||||
The MPU-3050 will pass through and forward the I2C signals from the
|
||||
incoming I2C bus, alternatively drive traffic to a slave device (usually
|
||||
an accelerometer) on its own initiative. Therefore is supports an
|
||||
i2c-gate subnode.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
gyroscope@68 {
|
||||
compatible = "invensense,mpu3050";
|
||||
reg = <0x68>;
|
||||
interrupt-parent = <&foo>;
|
||||
interrupts = <12 IRQ_TYPE_EDGE_FALLING>;
|
||||
vdd-supply = <&bar>;
|
||||
vlogic-supply = <&baz>;
|
||||
|
||||
i2c-gate {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
magnetometer@c {
|
||||
compatible = "ak,ak8975";
|
||||
reg = <0x0c>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
...
|
|
@ -1,67 +0,0 @@
|
|||
InvenSense MPU-6050 Six-Axis (Gyro + Accelerometer) MEMS MotionTracking Device
|
||||
|
||||
http://www.invensense.com/mems/gyro/mpu6050.html
|
||||
|
||||
Required properties:
|
||||
- compatible : should be one of
|
||||
"invensense,mpu6000"
|
||||
"invensense,mpu6050"
|
||||
"invensense,mpu6500"
|
||||
"invensense,mpu6515"
|
||||
"invensense,mpu9150"
|
||||
"invensense,mpu9250"
|
||||
"invensense,mpu9255"
|
||||
"invensense,icm20608"
|
||||
"invensense,icm20609"
|
||||
"invensense,icm20689"
|
||||
"invensense,icm20602"
|
||||
"invensense,icm20690"
|
||||
"invensense,iam20680"
|
||||
- reg : the I2C address of the sensor
|
||||
- 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.
|
||||
|
||||
Refer to interrupt-controller/interrupts.txt for generic interrupt client node
|
||||
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
|
||||
simple enough to be described using the i2c-gate binding. See
|
||||
i2c/i2c-gate.txt for more details.
|
||||
|
||||
Example:
|
||||
mpu6050@68 {
|
||||
compatible = "invensense,mpu6050";
|
||||
reg = <0x68>;
|
||||
interrupt-parent = <&gpio1>;
|
||||
interrupts = <18 IRQ_TYPE_EDGE_RISING>;
|
||||
mount-matrix = "-0.984807753012208", /* x0 */
|
||||
"0", /* y0 */
|
||||
"-0.173648177666930", /* z0 */
|
||||
"0", /* x1 */
|
||||
"-1", /* y1 */
|
||||
"0", /* z1 */
|
||||
"-0.173648177666930", /* x2 */
|
||||
"0", /* y2 */
|
||||
"0.984807753012208"; /* z2 */
|
||||
};
|
||||
|
||||
|
||||
mpu9250@68 {
|
||||
compatible = "invensense,mpu9250";
|
||||
reg = <0x68>;
|
||||
interrupt-parent = <&gpio3>;
|
||||
interrupts = <21 IRQ_TYPE_LEVEL_HIGH>;
|
||||
i2c-gate {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
ax8975@c {
|
||||
compatible = "ak,ak8975";
|
||||
reg = <0x0c>;
|
||||
};
|
||||
};
|
||||
};
|
|
@ -0,0 +1,104 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/imu/invensense,mpu6050.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: InvenSense MPU-6050 Six-Axis (Gyro + Accelerometer) MEMS MotionTracking Device
|
||||
|
||||
maintainers:
|
||||
- Jean-Baptiste Maneyrol <jmaneyrol@invensense.com>
|
||||
|
||||
description: |
|
||||
These devices support both I2C and SPI bus interfaces.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- invensense,iam20680
|
||||
- invensense,icm20608
|
||||
- invensense,icm20609
|
||||
- invensense,icm20689
|
||||
- invensense,icm20602
|
||||
- invensense,icm20690
|
||||
- invensense,mpu6000
|
||||
- invensense,mpu6050
|
||||
- invensense,mpu6500
|
||||
- invensense,mpu6515
|
||||
- invensense,mpu6880
|
||||
- invensense,mpu9150
|
||||
- invensense,mpu9250
|
||||
- invensense,mpu9255
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
spi-max-frequency: true
|
||||
|
||||
vdd-supply: true
|
||||
vddio-supply: true
|
||||
|
||||
mount-matrix: true
|
||||
|
||||
i2c-gate:
|
||||
$ref: /schemas/i2c/i2c-controller.yaml
|
||||
unevaluatedProperties: false
|
||||
description: |
|
||||
These devices also support an auxiliary i2c bus via an i2c-gate.
|
||||
|
||||
allOf:
|
||||
- if:
|
||||
not:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- invensense,mpu9150
|
||||
- invensense,mpu9250
|
||||
- invensense,mpu9255
|
||||
then:
|
||||
properties:
|
||||
i2c-gate: false
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
imu@68 {
|
||||
compatible = "invensense,mpu9250";
|
||||
reg = <0x68>;
|
||||
interrupt-parent = <&gpio3>;
|
||||
interrupts = <21 IRQ_TYPE_LEVEL_HIGH>;
|
||||
mount-matrix = "-0.984807753012208", /* x0 */
|
||||
"0", /* y0 */
|
||||
"-0.173648177666930", /* z0 */
|
||||
"0", /* x1 */
|
||||
"-1", /* y1 */
|
||||
"0", /* z1 */
|
||||
"-0.173648177666930", /* x2 */
|
||||
"0", /* y2 */
|
||||
"0.984807753012208"; /* z2 */
|
||||
i2c-gate {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
magnetometer@c {
|
||||
compatible = "ak,ak8975";
|
||||
reg = <0x0c>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
...
|
|
@ -30,6 +30,9 @@ properties:
|
|||
reg:
|
||||
maxItems: 1
|
||||
|
||||
vdd-supply: true
|
||||
vddio-supply: true
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
|
|
|
@ -0,0 +1,112 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/magnetometer/yamaha,yas530.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Yamaha YAS530 family of magnetometer sensors
|
||||
|
||||
maintainers:
|
||||
- Linus Walleij <linus.walleij@linaro.org>
|
||||
|
||||
description:
|
||||
The Yamaha YAS530 magnetometers is a line of 3-axis magnetometers
|
||||
first introduced by Yamaha in 2009 with the YAS530. They are successors
|
||||
of Yamaha's first magnetometer YAS529. Over the years this magnetometer
|
||||
has been miniaturized and appeared in a number of different variants.
|
||||
|
||||
properties:
|
||||
$nodename:
|
||||
pattern: '^magnetometer@[0-9a-f]+$'
|
||||
|
||||
compatible:
|
||||
items:
|
||||
- enum:
|
||||
- yamaha,yas530
|
||||
- yamaha,yas532
|
||||
- yamaha,yas533
|
||||
- yamaha,yas535
|
||||
- yamaha,yas536
|
||||
- yamaha,yas537
|
||||
- yamaha,yas539
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
reset-gpios:
|
||||
maxItems: 1
|
||||
description: The YAS530 sensor has a RSTN pin used to reset
|
||||
the logic inside the sensor. This GPIO line should connect
|
||||
to that pin and be marked as GPIO_ACTIVE_LOW.
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
description: Interrupt for INT pin for interrupt generation.
|
||||
The polarity, whether the interrupt is active on the rising
|
||||
or the falling edge, is software-configurable in the hardware.
|
||||
|
||||
vdd-supply:
|
||||
description: An optional regulator providing core power supply
|
||||
on the VDD pin, typically 1.8 V or 3.0 V.
|
||||
|
||||
iovdd-supply:
|
||||
description: An optional regulator providing I/O power supply
|
||||
for the I2C interface on the IOVDD pin, typically 1.8 V.
|
||||
|
||||
mount-matrix:
|
||||
description: An optional 3x3 mounting rotation matrix.
|
||||
|
||||
allOf:
|
||||
- if:
|
||||
not:
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
const: yamaha,yas530
|
||||
then:
|
||||
properties:
|
||||
reset-gpios: false
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
const: yamaha,yas539
|
||||
then:
|
||||
properties:
|
||||
interrupts: false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
i2c-0 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
magnetometer@2e {
|
||||
compatible = "yamaha,yas530";
|
||||
reg = <0x2e>;
|
||||
vdd-supply = <&ldo1_reg>;
|
||||
iovdd-supply = <&ldo2_reg>;
|
||||
reset-gpios = <&gpio6 12 GPIO_ACTIVE_LOW>;
|
||||
interrupts = <&gpio6 13 IRQ_TYPE_EDGE_RISING>;
|
||||
};
|
||||
};
|
||||
|
||||
i2c-1 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
magnetometer@2e {
|
||||
compatible = "yamaha,yas539";
|
||||
reg = <0x2e>;
|
||||
vdd-supply = <&ldo1_reg>;
|
||||
};
|
||||
};
|
|
@ -148,15 +148,13 @@ properties:
|
|||
- maxim,max31730
|
||||
# mCube 3-axis 8-bit digital accelerometer
|
||||
- mcube,mc3230
|
||||
# MEMSIC magnetometer
|
||||
- memsic,mmc35240
|
||||
# MEMSIC 2-axis 8-bit digital accelerometer
|
||||
- memsic,mxc6225
|
||||
# Measurement Specialities I2C temperature and humidity sensor
|
||||
- meas,htu21
|
||||
# Measurement Specialities I2C pressure and temperature sensor
|
||||
- meas,ms5637
|
||||
# Measurement Specialities I2C pressure and temperature sensor
|
||||
- meas,ms5803
|
||||
# Measurement Specialities I2C pressure and temperature sensor
|
||||
- meas,ms5805
|
||||
# Measurement Specialities I2C pressure and temperature sensor
|
||||
- meas,ms5837
|
||||
|
@ -166,6 +164,10 @@ properties:
|
|||
- meas,ms8607-temppressure
|
||||
# Measurement Specialties temperature sensor
|
||||
- meas,tsys01
|
||||
# MEMSIC magnetometer
|
||||
- memsic,mmc35240
|
||||
# MEMSIC 2-axis 8-bit digital accelerometer
|
||||
- memsic,mxc6225
|
||||
# Microchip differential I2C ADC, 1 Channel, 18 bit
|
||||
- microchip,mcp3421
|
||||
# Microchip differential I2C ADC, 2 Channel, 18 bit
|
||||
|
|
|
@ -1252,6 +1252,8 @@ patternProperties:
|
|||
description: Shenzhen Xunlong Software CO.,Limited
|
||||
"^xylon,.*":
|
||||
description: Xylon
|
||||
"^yamaha,.*":
|
||||
description: Yamaha Corporation
|
||||
"^yes-optoelectronics,.*":
|
||||
description: Yes Optoelectronics Co.,Ltd.
|
||||
"^ylm,.*":
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
* Copyright (c) 2015, Intel Corporation.
|
||||
*/
|
||||
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
|
@ -21,6 +22,7 @@
|
|||
#define HID_CUSTOM_TOTAL_ATTRS (HID_CUSTOM_MAX_CORE_ATTRS + 1)
|
||||
#define HID_CUSTOM_FIFO_SIZE 4096
|
||||
#define HID_CUSTOM_MAX_FEATURE_BYTES 64
|
||||
#define HID_SENSOR_USAGE_LENGTH (4 + 1)
|
||||
|
||||
struct hid_sensor_custom_field {
|
||||
int report_id;
|
||||
|
@ -50,6 +52,7 @@ struct hid_sensor_custom {
|
|||
struct kfifo data_fifo;
|
||||
unsigned long misc_opened;
|
||||
wait_queue_head_t wait;
|
||||
struct platform_device *custom_pdev;
|
||||
};
|
||||
|
||||
/* Header for each sample to user space via dev interface */
|
||||
|
@ -746,11 +749,130 @@ static void hid_sensor_custom_dev_if_remove(struct hid_sensor_custom
|
|||
|
||||
}
|
||||
|
||||
/* luid defined in FW (e.g. ISH). Maybe used to identify sensor. */
|
||||
static const char *const known_sensor_luid[] = { "020B000000000000" };
|
||||
|
||||
static int get_luid_table_index(unsigned char *usage_str)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(known_sensor_luid); i++) {
|
||||
if (!strncmp(usage_str, known_sensor_luid[i],
|
||||
strlen(known_sensor_luid[i])))
|
||||
return i;
|
||||
}
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static int get_known_custom_sensor_index(struct hid_sensor_hub_device *hsdev)
|
||||
{
|
||||
struct hid_sensor_hub_attribute_info sensor_manufacturer = { 0 };
|
||||
struct hid_sensor_hub_attribute_info sensor_luid_info = { 0 };
|
||||
int report_size;
|
||||
int ret;
|
||||
static u16 w_buf[HID_CUSTOM_MAX_FEATURE_BYTES];
|
||||
static char buf[HID_CUSTOM_MAX_FEATURE_BYTES];
|
||||
int i;
|
||||
|
||||
memset(w_buf, 0, sizeof(w_buf));
|
||||
memset(buf, 0, sizeof(buf));
|
||||
|
||||
/* get manufacturer info */
|
||||
ret = sensor_hub_input_get_attribute_info(hsdev,
|
||||
HID_FEATURE_REPORT, hsdev->usage,
|
||||
HID_USAGE_SENSOR_PROP_MANUFACTURER, &sensor_manufacturer);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
report_size =
|
||||
sensor_hub_get_feature(hsdev, sensor_manufacturer.report_id,
|
||||
sensor_manufacturer.index, sizeof(w_buf),
|
||||
w_buf);
|
||||
if (report_size <= 0) {
|
||||
hid_err(hsdev->hdev,
|
||||
"Failed to get sensor manufacturer info %d\n",
|
||||
report_size);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* convert from wide char to char */
|
||||
for (i = 0; i < ARRAY_SIZE(buf) - 1 && w_buf[i]; i++)
|
||||
buf[i] = (char)w_buf[i];
|
||||
|
||||
/* ensure it's ISH sensor */
|
||||
if (strncmp(buf, "INTEL", strlen("INTEL")))
|
||||
return -ENODEV;
|
||||
|
||||
memset(w_buf, 0, sizeof(w_buf));
|
||||
memset(buf, 0, sizeof(buf));
|
||||
|
||||
/* get real usage id */
|
||||
ret = sensor_hub_input_get_attribute_info(hsdev,
|
||||
HID_FEATURE_REPORT, hsdev->usage,
|
||||
HID_USAGE_SENSOR_PROP_SERIAL_NUM, &sensor_luid_info);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
report_size = sensor_hub_get_feature(hsdev, sensor_luid_info.report_id,
|
||||
sensor_luid_info.index, sizeof(w_buf),
|
||||
w_buf);
|
||||
if (report_size <= 0) {
|
||||
hid_err(hsdev->hdev, "Failed to get real usage info %d\n",
|
||||
report_size);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* convert from wide char to char */
|
||||
for (i = 0; i < ARRAY_SIZE(buf) - 1 && w_buf[i]; i++)
|
||||
buf[i] = (char)w_buf[i];
|
||||
|
||||
if (strlen(buf) != strlen(known_sensor_luid[0]) + 5) {
|
||||
hid_err(hsdev->hdev,
|
||||
"%s luid length not match %zu != (%zu + 5)\n", __func__,
|
||||
strlen(buf), strlen(known_sensor_luid[0]));
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* get table index with luid (not matching 'LUID: ' in luid) */
|
||||
return get_luid_table_index(&buf[5]);
|
||||
}
|
||||
|
||||
static struct platform_device *
|
||||
hid_sensor_register_platform_device(struct platform_device *pdev,
|
||||
struct hid_sensor_hub_device *hsdev,
|
||||
int index)
|
||||
{
|
||||
char real_usage[HID_SENSOR_USAGE_LENGTH] = { 0 };
|
||||
struct platform_device *custom_pdev;
|
||||
const char *dev_name;
|
||||
char *c;
|
||||
|
||||
/* copy real usage id */
|
||||
memcpy(real_usage, known_sensor_luid[index], 4);
|
||||
|
||||
/* usage id are all lowcase */
|
||||
for (c = real_usage; *c != '\0'; c++)
|
||||
*c = tolower(*c);
|
||||
|
||||
/* HID-SENSOR-INT-REAL_USAGE_ID */
|
||||
dev_name = kasprintf(GFP_KERNEL, "HID-SENSOR-INT-%s", real_usage);
|
||||
if (!dev_name)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
custom_pdev = platform_device_register_data(pdev->dev.parent, dev_name,
|
||||
PLATFORM_DEVID_NONE, hsdev,
|
||||
sizeof(*hsdev));
|
||||
kfree(dev_name);
|
||||
return custom_pdev;
|
||||
}
|
||||
|
||||
static int hid_sensor_custom_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct hid_sensor_custom *sensor_inst;
|
||||
struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
|
||||
int ret;
|
||||
int index;
|
||||
|
||||
sensor_inst = devm_kzalloc(&pdev->dev, sizeof(*sensor_inst),
|
||||
GFP_KERNEL);
|
||||
|
@ -764,6 +886,22 @@ static int hid_sensor_custom_probe(struct platform_device *pdev)
|
|||
sensor_inst->pdev = pdev;
|
||||
mutex_init(&sensor_inst->mutex);
|
||||
platform_set_drvdata(pdev, sensor_inst);
|
||||
|
||||
index = get_known_custom_sensor_index(hsdev);
|
||||
if (index >= 0 && index < ARRAY_SIZE(known_sensor_luid)) {
|
||||
sensor_inst->custom_pdev =
|
||||
hid_sensor_register_platform_device(pdev, hsdev, index);
|
||||
|
||||
ret = PTR_ERR_OR_ZERO(sensor_inst->custom_pdev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev,
|
||||
"register_platform_device failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = sensor_hub_register_callback(hsdev, hsdev->usage,
|
||||
&sensor_inst->callbacks);
|
||||
if (ret < 0) {
|
||||
|
@ -802,6 +940,11 @@ static int hid_sensor_custom_remove(struct platform_device *pdev)
|
|||
struct hid_sensor_custom *sensor_inst = platform_get_drvdata(pdev);
|
||||
struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
|
||||
|
||||
if (sensor_inst->custom_pdev) {
|
||||
platform_device_unregister(sensor_inst->custom_pdev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
hid_sensor_custom_dev_if_remove(sensor_inst);
|
||||
hid_sensor_custom_remove_attributes(sensor_inst);
|
||||
sysfs_remove_group(&sensor_inst->pdev->dev.kobj,
|
||||
|
|
|
@ -23,6 +23,7 @@ enum accel_3d_channel {
|
|||
ACCEL_3D_CHANNEL_MAX,
|
||||
};
|
||||
|
||||
#define CHANNEL_SCAN_INDEX_TIMESTAMP ACCEL_3D_CHANNEL_MAX
|
||||
struct accel_3d_state {
|
||||
struct hid_sensor_hub_callbacks callbacks;
|
||||
struct hid_sensor_common common_attributes;
|
||||
|
@ -75,7 +76,7 @@ static const struct iio_chan_spec accel_3d_channels[] = {
|
|||
BIT(IIO_CHAN_INFO_HYSTERESIS),
|
||||
.scan_index = CHANNEL_SCAN_INDEX_Z,
|
||||
},
|
||||
IIO_CHAN_SOFT_TIMESTAMP(3)
|
||||
IIO_CHAN_SOFT_TIMESTAMP(CHANNEL_SCAN_INDEX_TIMESTAMP)
|
||||
};
|
||||
|
||||
/* Channel definitions */
|
||||
|
@ -110,7 +111,8 @@ static const struct iio_chan_spec gravity_channels[] = {
|
|||
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
|
||||
BIT(IIO_CHAN_INFO_HYSTERESIS),
|
||||
.scan_index = CHANNEL_SCAN_INDEX_Z,
|
||||
}
|
||||
},
|
||||
IIO_CHAN_SOFT_TIMESTAMP(CHANNEL_SCAN_INDEX_TIMESTAMP),
|
||||
};
|
||||
|
||||
/* Adjust channel real bits based on report descriptor */
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <linux/acpi.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
|
@ -133,6 +134,7 @@ enum kx_acpi_type {
|
|||
};
|
||||
|
||||
struct kxcjk1013_data {
|
||||
struct regulator_bulk_data regulators[2];
|
||||
struct i2c_client *client;
|
||||
struct iio_trigger *dready_trig;
|
||||
struct iio_trigger *motion_trig;
|
||||
|
@ -1300,6 +1302,13 @@ static const char *kxcjk1013_match_acpi_device(struct device *dev,
|
|||
return dev_name(dev);
|
||||
}
|
||||
|
||||
static void kxcjk1013_disable_regulators(void *d)
|
||||
{
|
||||
struct kxcjk1013_data *data = d;
|
||||
|
||||
regulator_bulk_disable(ARRAY_SIZE(data->regulators), data->regulators);
|
||||
}
|
||||
|
||||
static int kxcjk1013_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
|
@ -1330,6 +1339,29 @@ static int kxcjk1013_probe(struct i2c_client *client,
|
|||
return ret;
|
||||
}
|
||||
|
||||
data->regulators[0].supply = "vdd";
|
||||
data->regulators[1].supply = "vddio";
|
||||
ret = devm_regulator_bulk_get(&client->dev, ARRAY_SIZE(data->regulators),
|
||||
data->regulators);
|
||||
if (ret)
|
||||
return dev_err_probe(&client->dev, ret, "Failed to get regulators\n");
|
||||
|
||||
ret = regulator_bulk_enable(ARRAY_SIZE(data->regulators),
|
||||
data->regulators);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = devm_add_action_or_reset(&client->dev, kxcjk1013_disable_regulators, data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* A typical delay of 10ms is required for powering up
|
||||
* according to the data sheets of supported chips.
|
||||
* Hence double that to play safe.
|
||||
*/
|
||||
msleep(20);
|
||||
|
||||
if (id) {
|
||||
data->chipset = (enum kx_chipset)(id->driver_data);
|
||||
name = id->name;
|
||||
|
|
|
@ -1228,8 +1228,15 @@ config XILINX_XADC
|
|||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
Say yes here to have support for the Xilinx XADC. The driver does support
|
||||
both the ZYNQ interface to the XADC as well as the AXI-XADC interface.
|
||||
Say yes here to have support for the Xilinx 7 Series XADC or
|
||||
UltraScale/UltraScale+ System Management Wizard.
|
||||
|
||||
For the 7 Series the driver does support both the ZYNQ interface
|
||||
to the XADC as well as the AXI-XADC interface.
|
||||
|
||||
The driver also support the Xilinx System Management Wizard IP core
|
||||
that can be used to access the System Monitor ADC on the Xilinx
|
||||
UltraScale and UltraScale+ FPGAs.
|
||||
|
||||
The driver can also be build as a module. If so, the module will be called
|
||||
xilinx-xadc.
|
||||
|
|
|
@ -1108,10 +1108,14 @@ static int ab8500_gpadc_probe(struct platform_device *pdev)
|
|||
return gpadc->irq_sw;
|
||||
}
|
||||
|
||||
gpadc->irq_hw = platform_get_irq_byname(pdev, "HW_CONV_END");
|
||||
if (gpadc->irq_hw < 0) {
|
||||
dev_err(dev, "failed to get platform hw_conv_end irq\n");
|
||||
return gpadc->irq_hw;
|
||||
if (is_ab8500(gpadc->ab8500)) {
|
||||
gpadc->irq_hw = platform_get_irq_byname(pdev, "HW_CONV_END");
|
||||
if (gpadc->irq_hw < 0) {
|
||||
dev_err(dev, "failed to get platform hw_conv_end irq\n");
|
||||
return gpadc->irq_hw;
|
||||
}
|
||||
} else {
|
||||
gpadc->irq_hw = 0;
|
||||
}
|
||||
|
||||
/* Initialize completion used to notify completion of conversion */
|
||||
|
@ -1128,14 +1132,16 @@ static int ab8500_gpadc_probe(struct platform_device *pdev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
ret = devm_request_threaded_irq(dev, gpadc->irq_hw, NULL,
|
||||
ab8500_bm_gpadcconvend_handler, IRQF_NO_SUSPEND | IRQF_ONESHOT,
|
||||
"ab8500-gpadc-hw", gpadc);
|
||||
if (ret < 0) {
|
||||
dev_err(dev,
|
||||
"Failed to request hw conversion irq: %d\n",
|
||||
gpadc->irq_hw);
|
||||
return ret;
|
||||
if (gpadc->irq_hw) {
|
||||
ret = devm_request_threaded_irq(dev, gpadc->irq_hw, NULL,
|
||||
ab8500_bm_gpadcconvend_handler, IRQF_NO_SUSPEND | IRQF_ONESHOT,
|
||||
"ab8500-gpadc-hw", gpadc);
|
||||
if (ret < 0) {
|
||||
dev_err(dev,
|
||||
"Failed to request hw conversion irq: %d\n",
|
||||
gpadc->irq_hw);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/* The VTVout LDO used to power the AB8500 GPADC */
|
||||
|
|
|
@ -67,6 +67,7 @@ enum ad7476_supported_device_ids {
|
|||
ID_ADS7866,
|
||||
ID_ADS7867,
|
||||
ID_ADS7868,
|
||||
ID_LTC2314_14,
|
||||
};
|
||||
|
||||
static void ad7091_convst(struct ad7476_state *st)
|
||||
|
@ -250,6 +251,10 @@ static const struct ad7476_chip_info ad7476_chip_info_tbl[] = {
|
|||
.channel[0] = ADS786X_CHAN(8),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
},
|
||||
[ID_LTC2314_14] = {
|
||||
.channel[0] = AD7940_CHAN(14),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
},
|
||||
};
|
||||
|
||||
static const struct iio_info ad7476_info = {
|
||||
|
@ -365,6 +370,7 @@ static const struct spi_device_id ad7476_id[] = {
|
|||
{"ads7866", ID_ADS7866},
|
||||
{"ads7867", ID_ADS7867},
|
||||
{"ads7868", ID_ADS7868},
|
||||
{"ltc2314-14", ID_LTC2314_14},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ad7476_id);
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
* Author: Linus Walleij <linus.walleij@linaro.org>
|
||||
*/
|
||||
|
||||
#include <linux/iio/adc/qcom-vadc-common.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/module.h>
|
||||
|
@ -21,8 +22,6 @@
|
|||
#include <linux/interrupt.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#include "qcom-vadc-common.h"
|
||||
|
||||
/*
|
||||
* Definitions for the "user processor" registers lifted from the v3.4
|
||||
* Qualcomm tree. Their kernel has two out-of-tree drivers for the ADC:
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include <linux/completion.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/iio/adc/qcom-vadc-common.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
|
@ -14,12 +15,12 @@
|
|||
#include <linux/math64.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <dt-bindings/iio/qcom,spmi-vadc.h>
|
||||
#include "qcom-vadc-common.h"
|
||||
|
||||
#define ADC5_USR_REVISION1 0x0
|
||||
#define ADC5_USR_STATUS1 0x8
|
||||
|
@ -154,18 +155,6 @@ struct adc5_chip {
|
|||
const struct adc5_data *data;
|
||||
};
|
||||
|
||||
static const struct vadc_prescale_ratio adc5_prescale_ratios[] = {
|
||||
{.num = 1, .den = 1},
|
||||
{.num = 1, .den = 3},
|
||||
{.num = 1, .den = 4},
|
||||
{.num = 1, .den = 6},
|
||||
{.num = 1, .den = 20},
|
||||
{.num = 1, .den = 8},
|
||||
{.num = 10, .den = 81},
|
||||
{.num = 1, .den = 10},
|
||||
{.num = 1, .den = 16}
|
||||
};
|
||||
|
||||
static int adc5_read(struct adc5_chip *adc, u16 offset, u8 *data, int len)
|
||||
{
|
||||
return regmap_bulk_read(adc->regmap, adc->base + offset, data, len);
|
||||
|
@ -181,55 +170,6 @@ static int adc5_masked_write(struct adc5_chip *adc, u16 offset, u8 mask, u8 val)
|
|||
return regmap_update_bits(adc->regmap, adc->base + offset, mask, val);
|
||||
}
|
||||
|
||||
static int adc5_prescaling_from_dt(u32 num, u32 den)
|
||||
{
|
||||
unsigned int pre;
|
||||
|
||||
for (pre = 0; pre < ARRAY_SIZE(adc5_prescale_ratios); pre++)
|
||||
if (adc5_prescale_ratios[pre].num == num &&
|
||||
adc5_prescale_ratios[pre].den == den)
|
||||
break;
|
||||
|
||||
if (pre == ARRAY_SIZE(adc5_prescale_ratios))
|
||||
return -EINVAL;
|
||||
|
||||
return pre;
|
||||
}
|
||||
|
||||
static int adc5_hw_settle_time_from_dt(u32 value,
|
||||
const unsigned int *hw_settle)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < VADC_HW_SETTLE_SAMPLES_MAX; i++) {
|
||||
if (value == hw_settle[i])
|
||||
return i;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int adc5_avg_samples_from_dt(u32 value)
|
||||
{
|
||||
if (!is_power_of_2(value) || value > ADC5_AVG_SAMPLES_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
return __ffs(value);
|
||||
}
|
||||
|
||||
static int adc5_decimation_from_dt(u32 value,
|
||||
const unsigned int *decimation)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ADC5_DECIMATION_SAMPLES_MAX; i++) {
|
||||
if (value == decimation[i])
|
||||
return i;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int adc5_read_voltage_data(struct adc5_chip *adc, u16 *data)
|
||||
{
|
||||
int ret;
|
||||
|
@ -511,7 +451,7 @@ static int adc_read_raw_common(struct iio_dev *indio_dev,
|
|||
return ret;
|
||||
|
||||
ret = qcom_adc5_hw_scale(prop->scale_fn_type,
|
||||
&adc5_prescale_ratios[prop->prescale],
|
||||
prop->prescale,
|
||||
adc->data,
|
||||
adc_code_volt, val);
|
||||
if (ret)
|
||||
|
@ -717,7 +657,7 @@ static int adc5_get_dt_channel_data(struct adc5_chip *adc,
|
|||
|
||||
ret = of_property_read_u32(node, "qcom,decimation", &value);
|
||||
if (!ret) {
|
||||
ret = adc5_decimation_from_dt(value, data->decimation);
|
||||
ret = qcom_adc5_decimation_from_dt(value, data->decimation);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "%02x invalid decimation %d\n",
|
||||
chan, value);
|
||||
|
@ -730,7 +670,7 @@ static int adc5_get_dt_channel_data(struct adc5_chip *adc,
|
|||
|
||||
ret = of_property_read_u32_array(node, "qcom,pre-scaling", varr, 2);
|
||||
if (!ret) {
|
||||
ret = adc5_prescaling_from_dt(varr[0], varr[1]);
|
||||
ret = qcom_adc5_prescaling_from_dt(varr[0], varr[1]);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "%02x invalid pre-scaling <%d %d>\n",
|
||||
chan, varr[0], varr[1]);
|
||||
|
@ -759,11 +699,9 @@ static int adc5_get_dt_channel_data(struct adc5_chip *adc,
|
|||
if ((dig_version[0] >= ADC5_HW_SETTLE_DIFF_MINOR &&
|
||||
dig_version[1] >= ADC5_HW_SETTLE_DIFF_MAJOR) ||
|
||||
adc->data->info == &adc7_info)
|
||||
ret = adc5_hw_settle_time_from_dt(value,
|
||||
data->hw_settle_2);
|
||||
ret = qcom_adc5_hw_settle_time_from_dt(value, data->hw_settle_2);
|
||||
else
|
||||
ret = adc5_hw_settle_time_from_dt(value,
|
||||
data->hw_settle_1);
|
||||
ret = qcom_adc5_hw_settle_time_from_dt(value, data->hw_settle_1);
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "%02x invalid hw-settle-time %d us\n",
|
||||
|
@ -777,7 +715,7 @@ static int adc5_get_dt_channel_data(struct adc5_chip *adc,
|
|||
|
||||
ret = of_property_read_u32(node, "qcom,avg-samples", &value);
|
||||
if (!ret) {
|
||||
ret = adc5_avg_samples_from_dt(value);
|
||||
ret = qcom_adc5_avg_samples_from_dt(value);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "%02x invalid avg-samples %d\n",
|
||||
chan, value);
|
||||
|
@ -870,8 +808,6 @@ static int adc5_get_dt_data(struct adc5_chip *adc, struct device_node *node)
|
|||
struct adc5_channel_prop prop, *chan_props;
|
||||
struct device_node *child;
|
||||
unsigned int index = 0;
|
||||
const struct of_device_id *id;
|
||||
const struct adc5_data *data;
|
||||
int ret;
|
||||
|
||||
adc->nchannels = of_get_available_child_count(node);
|
||||
|
@ -890,24 +826,21 @@ static int adc5_get_dt_data(struct adc5_chip *adc, struct device_node *node)
|
|||
|
||||
chan_props = adc->chan_props;
|
||||
iio_chan = adc->iio_chans;
|
||||
id = of_match_node(adc5_match_table, node);
|
||||
if (id)
|
||||
data = id->data;
|
||||
else
|
||||
data = &adc5_data_pmic;
|
||||
adc->data = data;
|
||||
adc->data = of_device_get_match_data(adc->dev);
|
||||
if (!adc->data)
|
||||
adc->data = &adc5_data_pmic;
|
||||
|
||||
for_each_available_child_of_node(node, child) {
|
||||
ret = adc5_get_dt_channel_data(adc, &prop, child, data);
|
||||
ret = adc5_get_dt_channel_data(adc, &prop, child, adc->data);
|
||||
if (ret) {
|
||||
of_node_put(child);
|
||||
return ret;
|
||||
}
|
||||
|
||||
prop.scale_fn_type =
|
||||
data->adc_chans[prop.channel].scale_fn_type;
|
||||
adc->data->adc_chans[prop.channel].scale_fn_type;
|
||||
*chan_props = prop;
|
||||
adc_chan = &data->adc_chans[prop.channel];
|
||||
adc_chan = &adc->data->adc_chans[prop.channel];
|
||||
|
||||
iio_chan->channel = prop.channel;
|
||||
iio_chan->datasheet_name = prop.datasheet_name;
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include <linux/completion.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/iio/adc/qcom-vadc-common.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
|
@ -20,8 +21,6 @@
|
|||
|
||||
#include <dt-bindings/iio/qcom,spmi-vadc.h>
|
||||
|
||||
#include "qcom-vadc-common.h"
|
||||
|
||||
/* VADC register and bit definitions */
|
||||
#define VADC_REVISION2 0x1
|
||||
#define VADC_REVISION2_SUPPORTED_VADC 1
|
||||
|
|
|
@ -2,50 +2,61 @@
|
|||
#include <linux/bug.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/fixp-arith.h>
|
||||
#include <linux/iio/adc/qcom-vadc-common.h>
|
||||
#include <linux/math64.h>
|
||||
#include <linux/log2.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/units.h>
|
||||
|
||||
#include "qcom-vadc-common.h"
|
||||
/**
|
||||
* struct vadc_map_pt - Map the graph representation for ADC channel
|
||||
* @x: Represent the ADC digitized code.
|
||||
* @y: Represent the physical data which can be temperature, voltage,
|
||||
* resistance.
|
||||
*/
|
||||
struct vadc_map_pt {
|
||||
s32 x;
|
||||
s32 y;
|
||||
};
|
||||
|
||||
/* Voltage to temperature */
|
||||
static const struct vadc_map_pt adcmap_100k_104ef_104fb[] = {
|
||||
{1758, -40},
|
||||
{1742, -35},
|
||||
{1719, -30},
|
||||
{1691, -25},
|
||||
{1654, -20},
|
||||
{1608, -15},
|
||||
{1551, -10},
|
||||
{1483, -5},
|
||||
{1404, 0},
|
||||
{1315, 5},
|
||||
{1218, 10},
|
||||
{1114, 15},
|
||||
{1007, 20},
|
||||
{900, 25},
|
||||
{795, 30},
|
||||
{696, 35},
|
||||
{605, 40},
|
||||
{522, 45},
|
||||
{448, 50},
|
||||
{383, 55},
|
||||
{327, 60},
|
||||
{278, 65},
|
||||
{237, 70},
|
||||
{202, 75},
|
||||
{172, 80},
|
||||
{146, 85},
|
||||
{125, 90},
|
||||
{107, 95},
|
||||
{92, 100},
|
||||
{79, 105},
|
||||
{68, 110},
|
||||
{59, 115},
|
||||
{51, 120},
|
||||
{44, 125}
|
||||
{1758, -40000 },
|
||||
{1742, -35000 },
|
||||
{1719, -30000 },
|
||||
{1691, -25000 },
|
||||
{1654, -20000 },
|
||||
{1608, -15000 },
|
||||
{1551, -10000 },
|
||||
{1483, -5000 },
|
||||
{1404, 0 },
|
||||
{1315, 5000 },
|
||||
{1218, 10000 },
|
||||
{1114, 15000 },
|
||||
{1007, 20000 },
|
||||
{900, 25000 },
|
||||
{795, 30000 },
|
||||
{696, 35000 },
|
||||
{605, 40000 },
|
||||
{522, 45000 },
|
||||
{448, 50000 },
|
||||
{383, 55000 },
|
||||
{327, 60000 },
|
||||
{278, 65000 },
|
||||
{237, 70000 },
|
||||
{202, 75000 },
|
||||
{172, 80000 },
|
||||
{146, 85000 },
|
||||
{125, 90000 },
|
||||
{107, 95000 },
|
||||
{92, 100000 },
|
||||
{79, 105000 },
|
||||
{68, 110000 },
|
||||
{59, 115000 },
|
||||
{51, 120000 },
|
||||
{44, 125000 }
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -90,18 +101,18 @@ static const struct vadc_map_pt adcmap_100k_104ef_104fb_1875_vref[] = {
|
|||
};
|
||||
|
||||
static const struct vadc_map_pt adcmap7_die_temp[] = {
|
||||
{ 433700, 1967},
|
||||
{ 473100, 1964},
|
||||
{ 512400, 1957},
|
||||
{ 551500, 1949},
|
||||
{ 590500, 1940},
|
||||
{ 629300, 1930},
|
||||
{ 667900, 1921},
|
||||
{ 706400, 1910},
|
||||
{ 744600, 1896},
|
||||
{ 782500, 1878},
|
||||
{ 820100, 1859},
|
||||
{ 857300, 0},
|
||||
{ 857300, 160000 },
|
||||
{ 820100, 140000 },
|
||||
{ 782500, 120000 },
|
||||
{ 744600, 100000 },
|
||||
{ 706400, 80000 },
|
||||
{ 667900, 60000 },
|
||||
{ 629300, 40000 },
|
||||
{ 590500, 20000 },
|
||||
{ 551500, 0 },
|
||||
{ 512400, -20000 },
|
||||
{ 473100, -40000 },
|
||||
{ 433700, -60000 },
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -278,6 +289,18 @@ static const struct vadc_map_pt adcmap7_100k[] = {
|
|||
{ 2420, 130048 }
|
||||
};
|
||||
|
||||
static const struct vadc_prescale_ratio adc5_prescale_ratios[] = {
|
||||
{.num = 1, .den = 1},
|
||||
{.num = 1, .den = 3},
|
||||
{.num = 1, .den = 4},
|
||||
{.num = 1, .den = 6},
|
||||
{.num = 1, .den = 20},
|
||||
{.num = 1, .den = 8},
|
||||
{.num = 10, .den = 81},
|
||||
{.num = 1, .den = 10},
|
||||
{.num = 1, .den = 16}
|
||||
};
|
||||
|
||||
static int qcom_vadc_scale_hw_calib_volt(
|
||||
const struct vadc_prescale_ratio *prescale,
|
||||
const struct adc5_data *data,
|
||||
|
@ -323,43 +346,23 @@ static struct qcom_adc5_scale_type scale_adc5_fn[] = {
|
|||
static int qcom_vadc_map_voltage_temp(const struct vadc_map_pt *pts,
|
||||
u32 tablesize, s32 input, int *output)
|
||||
{
|
||||
bool descending = 1;
|
||||
u32 i = 0;
|
||||
|
||||
if (!pts)
|
||||
return -EINVAL;
|
||||
|
||||
/* Check if table is descending or ascending */
|
||||
if (tablesize > 1) {
|
||||
if (pts[0].x < pts[1].x)
|
||||
descending = 0;
|
||||
}
|
||||
|
||||
while (i < tablesize) {
|
||||
if ((descending) && (pts[i].x < input)) {
|
||||
/* table entry is less than measured*/
|
||||
/* value and table is descending, stop */
|
||||
break;
|
||||
} else if ((!descending) &&
|
||||
(pts[i].x > input)) {
|
||||
/* table entry is greater than measured*/
|
||||
/*value and table is ascending, stop */
|
||||
break;
|
||||
}
|
||||
while (i < tablesize && pts[i].x > input)
|
||||
i++;
|
||||
}
|
||||
|
||||
if (i == 0) {
|
||||
*output = pts[0].y;
|
||||
} else if (i == tablesize) {
|
||||
*output = pts[tablesize - 1].y;
|
||||
} else {
|
||||
/* result is between search_index and search_index-1 */
|
||||
/* interpolate linearly */
|
||||
*output = (((s32)((pts[i].y - pts[i - 1].y) *
|
||||
(input - pts[i - 1].x)) /
|
||||
(pts[i].x - pts[i - 1].x)) +
|
||||
pts[i - 1].y);
|
||||
*output = fixp_linear_interpolate(pts[i - 1].x, pts[i - 1].y,
|
||||
pts[i].x, pts[i].y,
|
||||
input);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -415,8 +418,6 @@ static int qcom_vadc_scale_therm(const struct vadc_linear_graph *calib_graph,
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
*result_mdec *= 1000;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -563,33 +564,13 @@ static int qcom_vadc7_scale_hw_calib_die_temp(
|
|||
u16 adc_code, int *result_mdec)
|
||||
{
|
||||
|
||||
int voltage, vtemp0, temp, i;
|
||||
int voltage;
|
||||
|
||||
voltage = qcom_vadc_scale_code_voltage_factor(adc_code,
|
||||
prescale, data, 1);
|
||||
|
||||
if (adcmap7_die_temp[0].x > voltage) {
|
||||
*result_mdec = DIE_TEMP_ADC7_SCALE_1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (adcmap7_die_temp[ARRAY_SIZE(adcmap7_die_temp) - 1].x <= voltage) {
|
||||
*result_mdec = DIE_TEMP_ADC7_MAX;
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(adcmap7_die_temp); i++)
|
||||
if (adcmap7_die_temp[i].x > voltage)
|
||||
break;
|
||||
|
||||
vtemp0 = adcmap7_die_temp[i - 1].x;
|
||||
voltage = voltage - vtemp0;
|
||||
temp = div64_s64(voltage * DIE_TEMP_ADC7_SCALE_FACTOR,
|
||||
adcmap7_die_temp[i - 1].y);
|
||||
temp += DIE_TEMP_ADC7_SCALE_1 + (DIE_TEMP_ADC7_SCALE_2 * (i - 1));
|
||||
*result_mdec = temp;
|
||||
|
||||
return 0;
|
||||
return qcom_vadc_map_voltage_temp(adcmap7_die_temp, ARRAY_SIZE(adcmap7_die_temp),
|
||||
voltage, result_mdec);
|
||||
}
|
||||
|
||||
static int qcom_vadc_scale_hw_smb_temp(
|
||||
|
@ -647,10 +628,12 @@ int qcom_vadc_scale(enum vadc_scale_fn_type scaletype,
|
|||
EXPORT_SYMBOL(qcom_vadc_scale);
|
||||
|
||||
int qcom_adc5_hw_scale(enum vadc_scale_fn_type scaletype,
|
||||
const struct vadc_prescale_ratio *prescale,
|
||||
unsigned int prescale_ratio,
|
||||
const struct adc5_data *data,
|
||||
u16 adc_code, int *result)
|
||||
{
|
||||
const struct vadc_prescale_ratio *prescale = &adc5_prescale_ratios[prescale_ratio];
|
||||
|
||||
if (!(scaletype >= SCALE_HW_CALIB_DEFAULT &&
|
||||
scaletype < SCALE_HW_CALIB_INVALID)) {
|
||||
pr_err("Invalid scale type %d\n", scaletype);
|
||||
|
@ -662,6 +645,58 @@ int qcom_adc5_hw_scale(enum vadc_scale_fn_type scaletype,
|
|||
}
|
||||
EXPORT_SYMBOL(qcom_adc5_hw_scale);
|
||||
|
||||
int qcom_adc5_prescaling_from_dt(u32 num, u32 den)
|
||||
{
|
||||
unsigned int pre;
|
||||
|
||||
for (pre = 0; pre < ARRAY_SIZE(adc5_prescale_ratios); pre++)
|
||||
if (adc5_prescale_ratios[pre].num == num &&
|
||||
adc5_prescale_ratios[pre].den == den)
|
||||
break;
|
||||
|
||||
if (pre == ARRAY_SIZE(adc5_prescale_ratios))
|
||||
return -EINVAL;
|
||||
|
||||
return pre;
|
||||
}
|
||||
EXPORT_SYMBOL(qcom_adc5_prescaling_from_dt);
|
||||
|
||||
int qcom_adc5_hw_settle_time_from_dt(u32 value,
|
||||
const unsigned int *hw_settle)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < VADC_HW_SETTLE_SAMPLES_MAX; i++) {
|
||||
if (value == hw_settle[i])
|
||||
return i;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
EXPORT_SYMBOL(qcom_adc5_hw_settle_time_from_dt);
|
||||
|
||||
int qcom_adc5_avg_samples_from_dt(u32 value)
|
||||
{
|
||||
if (!is_power_of_2(value) || value > ADC5_AVG_SAMPLES_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
return __ffs(value);
|
||||
}
|
||||
EXPORT_SYMBOL(qcom_adc5_avg_samples_from_dt);
|
||||
|
||||
int qcom_adc5_decimation_from_dt(u32 value, const unsigned int *decimation)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ADC5_DECIMATION_SAMPLES_MAX; i++) {
|
||||
if (value == decimation[i])
|
||||
return i;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
EXPORT_SYMBOL(qcom_adc5_decimation_from_dt);
|
||||
|
||||
int qcom_vadc_decimation_from_dt(u32 value)
|
||||
{
|
||||
if (!is_power_of_2(value) || value < VADC_DECIMATION_MIN ||
|
||||
|
|
|
@ -307,7 +307,7 @@ static int sc27xx_adc_convert_volt(struct sc27xx_adc_data *data, int channel,
|
|||
|
||||
sc27xx_adc_volt_ratio(data, channel, scale, &numerator, &denominator);
|
||||
|
||||
return (volt * denominator + numerator / 2) / numerator;
|
||||
return DIV_ROUND_CLOSEST(volt * denominator, numerator);
|
||||
}
|
||||
|
||||
static int sc27xx_adc_read_processed(struct sc27xx_adc_data *data,
|
||||
|
|
|
@ -535,20 +535,16 @@ static int stm32_adc_core_hw_start(struct device *dev)
|
|||
goto err_switches_dis;
|
||||
}
|
||||
|
||||
if (priv->bclk) {
|
||||
ret = clk_prepare_enable(priv->bclk);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "bus clk enable failed\n");
|
||||
goto err_regulator_disable;
|
||||
}
|
||||
ret = clk_prepare_enable(priv->bclk);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "bus clk enable failed\n");
|
||||
goto err_regulator_disable;
|
||||
}
|
||||
|
||||
if (priv->aclk) {
|
||||
ret = clk_prepare_enable(priv->aclk);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "adc clk enable failed\n");
|
||||
goto err_bclk_disable;
|
||||
}
|
||||
ret = clk_prepare_enable(priv->aclk);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "adc clk enable failed\n");
|
||||
goto err_bclk_disable;
|
||||
}
|
||||
|
||||
writel_relaxed(priv->ccr_bak, priv->common.base + priv->cfg->regs->ccr);
|
||||
|
@ -556,8 +552,7 @@ static int stm32_adc_core_hw_start(struct device *dev)
|
|||
return 0;
|
||||
|
||||
err_bclk_disable:
|
||||
if (priv->bclk)
|
||||
clk_disable_unprepare(priv->bclk);
|
||||
clk_disable_unprepare(priv->bclk);
|
||||
err_regulator_disable:
|
||||
regulator_disable(priv->vref);
|
||||
err_switches_dis:
|
||||
|
@ -575,10 +570,8 @@ static void stm32_adc_core_hw_stop(struct device *dev)
|
|||
|
||||
/* Backup CCR that may be lost (depends on power state to achieve) */
|
||||
priv->ccr_bak = readl_relaxed(priv->common.base + priv->cfg->regs->ccr);
|
||||
if (priv->aclk)
|
||||
clk_disable_unprepare(priv->aclk);
|
||||
if (priv->bclk)
|
||||
clk_disable_unprepare(priv->bclk);
|
||||
clk_disable_unprepare(priv->aclk);
|
||||
clk_disable_unprepare(priv->bclk);
|
||||
regulator_disable(priv->vref);
|
||||
stm32_adc_core_switches_supply_dis(priv);
|
||||
regulator_disable(priv->vdda);
|
||||
|
|
|
@ -546,8 +546,7 @@ static int stm32_adc_hw_stop(struct device *dev)
|
|||
if (adc->cfg->unprepare)
|
||||
adc->cfg->unprepare(indio_dev);
|
||||
|
||||
if (adc->clk)
|
||||
clk_disable_unprepare(adc->clk);
|
||||
clk_disable_unprepare(adc->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -558,11 +557,9 @@ static int stm32_adc_hw_start(struct device *dev)
|
|||
struct stm32_adc *adc = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
if (adc->clk) {
|
||||
ret = clk_prepare_enable(adc->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
ret = clk_prepare_enable(adc->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
stm32_adc_set_res(adc);
|
||||
|
||||
|
@ -575,8 +572,7 @@ static int stm32_adc_hw_start(struct device *dev)
|
|||
return 0;
|
||||
|
||||
err_clk_dis:
|
||||
if (adc->clk)
|
||||
clk_disable_unprepare(adc->clk);
|
||||
clk_disable_unprepare(adc->clk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -117,8 +117,7 @@ static void stm32_dfsdm_clk_disable_unprepare(struct stm32_dfsdm *dfsdm)
|
|||
{
|
||||
struct dfsdm_priv *priv = to_stm32_dfsdm_priv(dfsdm);
|
||||
|
||||
if (priv->aclk)
|
||||
clk_disable_unprepare(priv->aclk);
|
||||
clk_disable_unprepare(priv->aclk);
|
||||
clk_disable_unprepare(priv->clk);
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/overflow.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfs.h>
|
||||
|
@ -92,7 +93,12 @@ static const unsigned int XADC_ZYNQ_UNMASK_TIMEOUT = 500;
|
|||
#define XADC_AXI_REG_GIER 0x5c
|
||||
#define XADC_AXI_REG_IPISR 0x60
|
||||
#define XADC_AXI_REG_IPIER 0x68
|
||||
#define XADC_AXI_ADC_REG_OFFSET 0x200
|
||||
|
||||
/* 7 Series */
|
||||
#define XADC_7S_AXI_ADC_REG_OFFSET 0x200
|
||||
|
||||
/* UltraScale */
|
||||
#define XADC_US_AXI_ADC_REG_OFFSET 0x400
|
||||
|
||||
#define XADC_AXI_RESET_MAGIC 0xa
|
||||
#define XADC_AXI_GIER_ENABLE BIT(31)
|
||||
|
@ -447,6 +453,12 @@ static const struct xadc_ops xadc_zynq_ops = {
|
|||
.get_dclk_rate = xadc_zynq_get_dclk_rate,
|
||||
.interrupt_handler = xadc_zynq_interrupt_handler,
|
||||
.update_alarm = xadc_zynq_update_alarm,
|
||||
.type = XADC_TYPE_S7,
|
||||
};
|
||||
|
||||
static const unsigned int xadc_axi_reg_offsets[] = {
|
||||
[XADC_TYPE_S7] = XADC_7S_AXI_ADC_REG_OFFSET,
|
||||
[XADC_TYPE_US] = XADC_US_AXI_ADC_REG_OFFSET,
|
||||
};
|
||||
|
||||
static int xadc_axi_read_adc_reg(struct xadc *xadc, unsigned int reg,
|
||||
|
@ -454,7 +466,8 @@ static int xadc_axi_read_adc_reg(struct xadc *xadc, unsigned int reg,
|
|||
{
|
||||
uint32_t val32;
|
||||
|
||||
xadc_read_reg(xadc, XADC_AXI_ADC_REG_OFFSET + reg * 4, &val32);
|
||||
xadc_read_reg(xadc, xadc_axi_reg_offsets[xadc->ops->type] + reg * 4,
|
||||
&val32);
|
||||
*val = val32 & 0xffff;
|
||||
|
||||
return 0;
|
||||
|
@ -463,7 +476,8 @@ static int xadc_axi_read_adc_reg(struct xadc *xadc, unsigned int reg,
|
|||
static int xadc_axi_write_adc_reg(struct xadc *xadc, unsigned int reg,
|
||||
uint16_t val)
|
||||
{
|
||||
xadc_write_reg(xadc, XADC_AXI_ADC_REG_OFFSET + reg * 4, val);
|
||||
xadc_write_reg(xadc, xadc_axi_reg_offsets[xadc->ops->type] + reg * 4,
|
||||
val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -541,7 +555,7 @@ static unsigned long xadc_axi_get_dclk(struct xadc *xadc)
|
|||
return clk_get_rate(xadc->clk);
|
||||
}
|
||||
|
||||
static const struct xadc_ops xadc_axi_ops = {
|
||||
static const struct xadc_ops xadc_7s_axi_ops = {
|
||||
.read = xadc_axi_read_adc_reg,
|
||||
.write = xadc_axi_write_adc_reg,
|
||||
.setup = xadc_axi_setup,
|
||||
|
@ -549,6 +563,18 @@ static const struct xadc_ops xadc_axi_ops = {
|
|||
.update_alarm = xadc_axi_update_alarm,
|
||||
.interrupt_handler = xadc_axi_interrupt_handler,
|
||||
.flags = XADC_FLAGS_BUFFERED,
|
||||
.type = XADC_TYPE_S7,
|
||||
};
|
||||
|
||||
static const struct xadc_ops xadc_us_axi_ops = {
|
||||
.read = xadc_axi_read_adc_reg,
|
||||
.write = xadc_axi_write_adc_reg,
|
||||
.setup = xadc_axi_setup,
|
||||
.get_dclk_rate = xadc_axi_get_dclk,
|
||||
.update_alarm = xadc_axi_update_alarm,
|
||||
.interrupt_handler = xadc_axi_interrupt_handler,
|
||||
.flags = XADC_FLAGS_BUFFERED,
|
||||
.type = XADC_TYPE_US,
|
||||
};
|
||||
|
||||
static int _xadc_update_adc_reg(struct xadc *xadc, unsigned int reg,
|
||||
|
@ -585,15 +611,22 @@ static int xadc_update_scan_mode(struct iio_dev *indio_dev,
|
|||
const unsigned long *mask)
|
||||
{
|
||||
struct xadc *xadc = iio_priv(indio_dev);
|
||||
unsigned int n;
|
||||
size_t new_size, n;
|
||||
void *data;
|
||||
|
||||
n = bitmap_weight(mask, indio_dev->masklength);
|
||||
|
||||
kfree(xadc->data);
|
||||
xadc->data = kcalloc(n, sizeof(*xadc->data), GFP_KERNEL);
|
||||
if (!xadc->data)
|
||||
if (check_mul_overflow(n, sizeof(*xadc->data), &new_size))
|
||||
return -ENOMEM;
|
||||
|
||||
data = devm_krealloc(indio_dev->dev.parent, xadc->data,
|
||||
new_size, GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
memset(data, 0, new_size);
|
||||
xadc->data = data;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -705,11 +738,12 @@ static const struct iio_trigger_ops xadc_trigger_ops = {
|
|||
static struct iio_trigger *xadc_alloc_trigger(struct iio_dev *indio_dev,
|
||||
const char *name)
|
||||
{
|
||||
struct device *dev = indio_dev->dev.parent;
|
||||
struct iio_trigger *trig;
|
||||
int ret;
|
||||
|
||||
trig = iio_trigger_alloc("%s%d-%s", indio_dev->name,
|
||||
indio_dev->id, name);
|
||||
trig = devm_iio_trigger_alloc(dev, "%s%d-%s", indio_dev->name,
|
||||
indio_dev->id, name);
|
||||
if (trig == NULL)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
|
@ -717,21 +751,26 @@ static struct iio_trigger *xadc_alloc_trigger(struct iio_dev *indio_dev,
|
|||
trig->ops = &xadc_trigger_ops;
|
||||
iio_trigger_set_drvdata(trig, iio_priv(indio_dev));
|
||||
|
||||
ret = iio_trigger_register(trig);
|
||||
ret = devm_iio_trigger_register(dev, trig);
|
||||
if (ret)
|
||||
goto error_free_trig;
|
||||
return ERR_PTR(ret);
|
||||
|
||||
return trig;
|
||||
|
||||
error_free_trig:
|
||||
iio_trigger_free(trig);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
static int xadc_power_adc_b(struct xadc *xadc, unsigned int seq_mode)
|
||||
{
|
||||
uint16_t val;
|
||||
|
||||
/*
|
||||
* As per datasheet the power-down bits are don't care in the
|
||||
* UltraScale, but as per reality setting the power-down bit for the
|
||||
* non-existing ADC-B powers down the main ADC, so just return and don't
|
||||
* do anything.
|
||||
*/
|
||||
if (xadc->ops->type == XADC_TYPE_US)
|
||||
return 0;
|
||||
|
||||
/* Powerdown the ADC-B when it is not needed. */
|
||||
switch (seq_mode) {
|
||||
case XADC_CONF1_SEQ_SIMULTANEOUS:
|
||||
|
@ -751,6 +790,10 @@ static int xadc_get_seq_mode(struct xadc *xadc, unsigned long scan_mode)
|
|||
{
|
||||
unsigned int aux_scan_mode = scan_mode >> 16;
|
||||
|
||||
/* UltraScale has only one ADC and supports only continuous mode */
|
||||
if (xadc->ops->type == XADC_TYPE_US)
|
||||
return XADC_CONF1_SEQ_CONTINUOUS;
|
||||
|
||||
if (xadc->external_mux_mode == XADC_EXTERNAL_MUX_DUAL)
|
||||
return XADC_CONF1_SEQ_SIMULTANEOUS;
|
||||
|
||||
|
@ -863,6 +906,7 @@ static int xadc_read_raw(struct iio_dev *indio_dev,
|
|||
struct iio_chan_spec const *chan, int *val, int *val2, long info)
|
||||
{
|
||||
struct xadc *xadc = iio_priv(indio_dev);
|
||||
unsigned int bits = chan->scan_type.realbits;
|
||||
uint16_t val16;
|
||||
int ret;
|
||||
|
||||
|
@ -874,17 +918,17 @@ static int xadc_read_raw(struct iio_dev *indio_dev,
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
val16 >>= 4;
|
||||
val16 >>= chan->scan_type.shift;
|
||||
if (chan->scan_type.sign == 'u')
|
||||
*val = val16;
|
||||
else
|
||||
*val = sign_extend32(val16, 11);
|
||||
*val = sign_extend32(val16, bits - 1);
|
||||
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
switch (chan->type) {
|
||||
case IIO_VOLTAGE:
|
||||
/* V = (val * 3.0) / 4096 */
|
||||
/* V = (val * 3.0) / 2**bits */
|
||||
switch (chan->address) {
|
||||
case XADC_REG_VCCINT:
|
||||
case XADC_REG_VCCAUX:
|
||||
|
@ -900,19 +944,19 @@ static int xadc_read_raw(struct iio_dev *indio_dev,
|
|||
*val = 1000;
|
||||
break;
|
||||
}
|
||||
*val2 = 12;
|
||||
*val2 = chan->scan_type.realbits;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
case IIO_TEMP:
|
||||
/* Temp in C = (val * 503.975) / 4096 - 273.15 */
|
||||
/* Temp in C = (val * 503.975) / 2**bits - 273.15 */
|
||||
*val = 503975;
|
||||
*val2 = 12;
|
||||
*val2 = bits;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
/* Only the temperature channel has an offset */
|
||||
*val = -((273150 << 12) / 503975);
|
||||
*val = -((273150 << bits) / 503975);
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
ret = xadc_read_samplerate(xadc);
|
||||
|
@ -1001,7 +1045,7 @@ static const struct iio_event_spec xadc_voltage_events[] = {
|
|||
},
|
||||
};
|
||||
|
||||
#define XADC_CHAN_TEMP(_chan, _scan_index, _addr) { \
|
||||
#define XADC_CHAN_TEMP(_chan, _scan_index, _addr, _bits) { \
|
||||
.type = IIO_TEMP, \
|
||||
.indexed = 1, \
|
||||
.channel = (_chan), \
|
||||
|
@ -1015,14 +1059,14 @@ static const struct iio_event_spec xadc_voltage_events[] = {
|
|||
.scan_index = (_scan_index), \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = 12, \
|
||||
.realbits = (_bits), \
|
||||
.storagebits = 16, \
|
||||
.shift = 4, \
|
||||
.shift = 16 - (_bits), \
|
||||
.endianness = IIO_CPU, \
|
||||
}, \
|
||||
}
|
||||
|
||||
#define XADC_CHAN_VOLTAGE(_chan, _scan_index, _addr, _ext, _alarm) { \
|
||||
#define XADC_CHAN_VOLTAGE(_chan, _scan_index, _addr, _bits, _ext, _alarm) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = (_chan), \
|
||||
|
@ -1035,41 +1079,82 @@ static const struct iio_event_spec xadc_voltage_events[] = {
|
|||
.scan_index = (_scan_index), \
|
||||
.scan_type = { \
|
||||
.sign = ((_addr) == XADC_REG_VREFN) ? 's' : 'u', \
|
||||
.realbits = 12, \
|
||||
.realbits = (_bits), \
|
||||
.storagebits = 16, \
|
||||
.shift = 4, \
|
||||
.shift = 16 - (_bits), \
|
||||
.endianness = IIO_CPU, \
|
||||
}, \
|
||||
.extend_name = _ext, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec xadc_channels[] = {
|
||||
XADC_CHAN_TEMP(0, 8, XADC_REG_TEMP),
|
||||
XADC_CHAN_VOLTAGE(0, 9, XADC_REG_VCCINT, "vccint", true),
|
||||
XADC_CHAN_VOLTAGE(1, 10, XADC_REG_VCCAUX, "vccaux", true),
|
||||
XADC_CHAN_VOLTAGE(2, 14, XADC_REG_VCCBRAM, "vccbram", true),
|
||||
XADC_CHAN_VOLTAGE(3, 5, XADC_REG_VCCPINT, "vccpint", true),
|
||||
XADC_CHAN_VOLTAGE(4, 6, XADC_REG_VCCPAUX, "vccpaux", true),
|
||||
XADC_CHAN_VOLTAGE(5, 7, XADC_REG_VCCO_DDR, "vccoddr", true),
|
||||
XADC_CHAN_VOLTAGE(6, 12, XADC_REG_VREFP, "vrefp", false),
|
||||
XADC_CHAN_VOLTAGE(7, 13, XADC_REG_VREFN, "vrefn", false),
|
||||
XADC_CHAN_VOLTAGE(8, 11, XADC_REG_VPVN, NULL, false),
|
||||
XADC_CHAN_VOLTAGE(9, 16, XADC_REG_VAUX(0), NULL, false),
|
||||
XADC_CHAN_VOLTAGE(10, 17, XADC_REG_VAUX(1), NULL, false),
|
||||
XADC_CHAN_VOLTAGE(11, 18, XADC_REG_VAUX(2), NULL, false),
|
||||
XADC_CHAN_VOLTAGE(12, 19, XADC_REG_VAUX(3), NULL, false),
|
||||
XADC_CHAN_VOLTAGE(13, 20, XADC_REG_VAUX(4), NULL, false),
|
||||
XADC_CHAN_VOLTAGE(14, 21, XADC_REG_VAUX(5), NULL, false),
|
||||
XADC_CHAN_VOLTAGE(15, 22, XADC_REG_VAUX(6), NULL, false),
|
||||
XADC_CHAN_VOLTAGE(16, 23, XADC_REG_VAUX(7), NULL, false),
|
||||
XADC_CHAN_VOLTAGE(17, 24, XADC_REG_VAUX(8), NULL, false),
|
||||
XADC_CHAN_VOLTAGE(18, 25, XADC_REG_VAUX(9), NULL, false),
|
||||
XADC_CHAN_VOLTAGE(19, 26, XADC_REG_VAUX(10), NULL, false),
|
||||
XADC_CHAN_VOLTAGE(20, 27, XADC_REG_VAUX(11), NULL, false),
|
||||
XADC_CHAN_VOLTAGE(21, 28, XADC_REG_VAUX(12), NULL, false),
|
||||
XADC_CHAN_VOLTAGE(22, 29, XADC_REG_VAUX(13), NULL, false),
|
||||
XADC_CHAN_VOLTAGE(23, 30, XADC_REG_VAUX(14), NULL, false),
|
||||
XADC_CHAN_VOLTAGE(24, 31, XADC_REG_VAUX(15), NULL, false),
|
||||
/* 7 Series */
|
||||
#define XADC_7S_CHAN_TEMP(_chan, _scan_index, _addr) \
|
||||
XADC_CHAN_TEMP(_chan, _scan_index, _addr, 12)
|
||||
#define XADC_7S_CHAN_VOLTAGE(_chan, _scan_index, _addr, _ext, _alarm) \
|
||||
XADC_CHAN_VOLTAGE(_chan, _scan_index, _addr, 12, _ext, _alarm)
|
||||
|
||||
static const struct iio_chan_spec xadc_7s_channels[] = {
|
||||
XADC_7S_CHAN_TEMP(0, 8, XADC_REG_TEMP),
|
||||
XADC_7S_CHAN_VOLTAGE(0, 9, XADC_REG_VCCINT, "vccint", true),
|
||||
XADC_7S_CHAN_VOLTAGE(1, 10, XADC_REG_VCCAUX, "vccaux", true),
|
||||
XADC_7S_CHAN_VOLTAGE(2, 14, XADC_REG_VCCBRAM, "vccbram", true),
|
||||
XADC_7S_CHAN_VOLTAGE(3, 5, XADC_REG_VCCPINT, "vccpint", true),
|
||||
XADC_7S_CHAN_VOLTAGE(4, 6, XADC_REG_VCCPAUX, "vccpaux", true),
|
||||
XADC_7S_CHAN_VOLTAGE(5, 7, XADC_REG_VCCO_DDR, "vccoddr", true),
|
||||
XADC_7S_CHAN_VOLTAGE(6, 12, XADC_REG_VREFP, "vrefp", false),
|
||||
XADC_7S_CHAN_VOLTAGE(7, 13, XADC_REG_VREFN, "vrefn", false),
|
||||
XADC_7S_CHAN_VOLTAGE(8, 11, XADC_REG_VPVN, NULL, false),
|
||||
XADC_7S_CHAN_VOLTAGE(9, 16, XADC_REG_VAUX(0), NULL, false),
|
||||
XADC_7S_CHAN_VOLTAGE(10, 17, XADC_REG_VAUX(1), NULL, false),
|
||||
XADC_7S_CHAN_VOLTAGE(11, 18, XADC_REG_VAUX(2), NULL, false),
|
||||
XADC_7S_CHAN_VOLTAGE(12, 19, XADC_REG_VAUX(3), NULL, false),
|
||||
XADC_7S_CHAN_VOLTAGE(13, 20, XADC_REG_VAUX(4), NULL, false),
|
||||
XADC_7S_CHAN_VOLTAGE(14, 21, XADC_REG_VAUX(5), NULL, false),
|
||||
XADC_7S_CHAN_VOLTAGE(15, 22, XADC_REG_VAUX(6), NULL, false),
|
||||
XADC_7S_CHAN_VOLTAGE(16, 23, XADC_REG_VAUX(7), NULL, false),
|
||||
XADC_7S_CHAN_VOLTAGE(17, 24, XADC_REG_VAUX(8), NULL, false),
|
||||
XADC_7S_CHAN_VOLTAGE(18, 25, XADC_REG_VAUX(9), NULL, false),
|
||||
XADC_7S_CHAN_VOLTAGE(19, 26, XADC_REG_VAUX(10), NULL, false),
|
||||
XADC_7S_CHAN_VOLTAGE(20, 27, XADC_REG_VAUX(11), NULL, false),
|
||||
XADC_7S_CHAN_VOLTAGE(21, 28, XADC_REG_VAUX(12), NULL, false),
|
||||
XADC_7S_CHAN_VOLTAGE(22, 29, XADC_REG_VAUX(13), NULL, false),
|
||||
XADC_7S_CHAN_VOLTAGE(23, 30, XADC_REG_VAUX(14), NULL, false),
|
||||
XADC_7S_CHAN_VOLTAGE(24, 31, XADC_REG_VAUX(15), NULL, false),
|
||||
};
|
||||
|
||||
/* UltraScale */
|
||||
#define XADC_US_CHAN_TEMP(_chan, _scan_index, _addr) \
|
||||
XADC_CHAN_TEMP(_chan, _scan_index, _addr, 10)
|
||||
#define XADC_US_CHAN_VOLTAGE(_chan, _scan_index, _addr, _ext, _alarm) \
|
||||
XADC_CHAN_VOLTAGE(_chan, _scan_index, _addr, 10, _ext, _alarm)
|
||||
|
||||
static const struct iio_chan_spec xadc_us_channels[] = {
|
||||
XADC_US_CHAN_TEMP(0, 8, XADC_REG_TEMP),
|
||||
XADC_US_CHAN_VOLTAGE(0, 9, XADC_REG_VCCINT, "vccint", true),
|
||||
XADC_US_CHAN_VOLTAGE(1, 10, XADC_REG_VCCAUX, "vccaux", true),
|
||||
XADC_US_CHAN_VOLTAGE(2, 14, XADC_REG_VCCBRAM, "vccbram", true),
|
||||
XADC_US_CHAN_VOLTAGE(3, 5, XADC_REG_VCCPINT, "vccpsintlp", true),
|
||||
XADC_US_CHAN_VOLTAGE(4, 6, XADC_REG_VCCPAUX, "vccpsintfp", true),
|
||||
XADC_US_CHAN_VOLTAGE(5, 7, XADC_REG_VCCO_DDR, "vccpsaux", true),
|
||||
XADC_US_CHAN_VOLTAGE(6, 12, XADC_REG_VREFP, "vrefp", false),
|
||||
XADC_US_CHAN_VOLTAGE(7, 13, XADC_REG_VREFN, "vrefn", false),
|
||||
XADC_US_CHAN_VOLTAGE(8, 11, XADC_REG_VPVN, NULL, false),
|
||||
XADC_US_CHAN_VOLTAGE(9, 16, XADC_REG_VAUX(0), NULL, false),
|
||||
XADC_US_CHAN_VOLTAGE(10, 17, XADC_REG_VAUX(1), NULL, false),
|
||||
XADC_US_CHAN_VOLTAGE(11, 18, XADC_REG_VAUX(2), NULL, false),
|
||||
XADC_US_CHAN_VOLTAGE(12, 19, XADC_REG_VAUX(3), NULL, false),
|
||||
XADC_US_CHAN_VOLTAGE(13, 20, XADC_REG_VAUX(4), NULL, false),
|
||||
XADC_US_CHAN_VOLTAGE(14, 21, XADC_REG_VAUX(5), NULL, false),
|
||||
XADC_US_CHAN_VOLTAGE(15, 22, XADC_REG_VAUX(6), NULL, false),
|
||||
XADC_US_CHAN_VOLTAGE(16, 23, XADC_REG_VAUX(7), NULL, false),
|
||||
XADC_US_CHAN_VOLTAGE(17, 24, XADC_REG_VAUX(8), NULL, false),
|
||||
XADC_US_CHAN_VOLTAGE(18, 25, XADC_REG_VAUX(9), NULL, false),
|
||||
XADC_US_CHAN_VOLTAGE(19, 26, XADC_REG_VAUX(10), NULL, false),
|
||||
XADC_US_CHAN_VOLTAGE(20, 27, XADC_REG_VAUX(11), NULL, false),
|
||||
XADC_US_CHAN_VOLTAGE(21, 28, XADC_REG_VAUX(12), NULL, false),
|
||||
XADC_US_CHAN_VOLTAGE(22, 29, XADC_REG_VAUX(13), NULL, false),
|
||||
XADC_US_CHAN_VOLTAGE(23, 30, XADC_REG_VAUX(14), NULL, false),
|
||||
XADC_US_CHAN_VOLTAGE(24, 31, XADC_REG_VAUX(15), NULL, false),
|
||||
};
|
||||
|
||||
static const struct iio_info xadc_info = {
|
||||
|
@ -1083,8 +1168,16 @@ static const struct iio_info xadc_info = {
|
|||
};
|
||||
|
||||
static const struct of_device_id xadc_of_match_table[] = {
|
||||
{ .compatible = "xlnx,zynq-xadc-1.00.a", (void *)&xadc_zynq_ops },
|
||||
{ .compatible = "xlnx,axi-xadc-1.00.a", (void *)&xadc_axi_ops },
|
||||
{
|
||||
.compatible = "xlnx,zynq-xadc-1.00.a",
|
||||
.data = &xadc_zynq_ops
|
||||
}, {
|
||||
.compatible = "xlnx,axi-xadc-1.00.a",
|
||||
.data = &xadc_7s_axi_ops
|
||||
}, {
|
||||
.compatible = "xlnx,system-management-wiz-1.3",
|
||||
.data = &xadc_us_axi_ops
|
||||
},
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, xadc_of_match_table);
|
||||
|
@ -1094,8 +1187,10 @@ static int xadc_parse_dt(struct iio_dev *indio_dev, struct device_node *np,
|
|||
{
|
||||
struct device *dev = indio_dev->dev.parent;
|
||||
struct xadc *xadc = iio_priv(indio_dev);
|
||||
const struct iio_chan_spec *channel_templates;
|
||||
struct iio_chan_spec *channels, *chan;
|
||||
struct device_node *chan_node, *child;
|
||||
unsigned int max_channels;
|
||||
unsigned int num_channels;
|
||||
const char *external_mux;
|
||||
u32 ext_mux_chan;
|
||||
|
@ -1136,9 +1231,15 @@ static int xadc_parse_dt(struct iio_dev *indio_dev, struct device_node *np,
|
|||
|
||||
*conf |= XADC_CONF0_MUX | XADC_CONF0_CHAN(ext_mux_chan);
|
||||
}
|
||||
|
||||
channels = devm_kmemdup(dev, xadc_channels,
|
||||
sizeof(xadc_channels), GFP_KERNEL);
|
||||
if (xadc->ops->type == XADC_TYPE_S7) {
|
||||
channel_templates = xadc_7s_channels;
|
||||
max_channels = ARRAY_SIZE(xadc_7s_channels);
|
||||
} else {
|
||||
channel_templates = xadc_us_channels;
|
||||
max_channels = ARRAY_SIZE(xadc_us_channels);
|
||||
}
|
||||
channels = devm_kmemdup(dev, channel_templates,
|
||||
sizeof(channels[0]) * max_channels, GFP_KERNEL);
|
||||
if (!channels)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -1148,7 +1249,7 @@ static int xadc_parse_dt(struct iio_dev *indio_dev, struct device_node *np,
|
|||
chan_node = of_get_child_by_name(np, "xlnx,channels");
|
||||
if (chan_node) {
|
||||
for_each_child_of_node(chan_node, child) {
|
||||
if (num_channels >= ARRAY_SIZE(xadc_channels)) {
|
||||
if (num_channels >= max_channels) {
|
||||
of_node_put(child);
|
||||
break;
|
||||
}
|
||||
|
@ -1184,8 +1285,28 @@ static int xadc_parse_dt(struct iio_dev *indio_dev, struct device_node *np,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const char * const xadc_type_names[] = {
|
||||
[XADC_TYPE_S7] = "xadc",
|
||||
[XADC_TYPE_US] = "xilinx-system-monitor",
|
||||
};
|
||||
|
||||
static void xadc_clk_disable_unprepare(void *data)
|
||||
{
|
||||
struct clk *clk = data;
|
||||
|
||||
clk_disable_unprepare(clk);
|
||||
}
|
||||
|
||||
static void xadc_cancel_delayed_work(void *data)
|
||||
{
|
||||
struct delayed_work *work = data;
|
||||
|
||||
cancel_delayed_work_sync(work);
|
||||
}
|
||||
|
||||
static int xadc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
const struct of_device_id *id;
|
||||
struct iio_dev *indio_dev;
|
||||
unsigned int bipolar_mask;
|
||||
|
@ -1195,10 +1316,10 @@ static int xadc_probe(struct platform_device *pdev)
|
|||
int irq;
|
||||
int i;
|
||||
|
||||
if (!pdev->dev.of_node)
|
||||
if (!dev->of_node)
|
||||
return -ENODEV;
|
||||
|
||||
id = of_match_node(xadc_of_match_table, pdev->dev.of_node);
|
||||
id = of_match_node(xadc_of_match_table, dev->of_node);
|
||||
if (!id)
|
||||
return -EINVAL;
|
||||
|
||||
|
@ -1206,7 +1327,7 @@ static int xadc_probe(struct platform_device *pdev)
|
|||
if (irq <= 0)
|
||||
return -ENXIO;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*xadc));
|
||||
indio_dev = devm_iio_device_alloc(dev, sizeof(*xadc));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -1222,43 +1343,44 @@ static int xadc_probe(struct platform_device *pdev)
|
|||
if (IS_ERR(xadc->base))
|
||||
return PTR_ERR(xadc->base);
|
||||
|
||||
indio_dev->name = "xadc";
|
||||
indio_dev->name = xadc_type_names[xadc->ops->type];
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &xadc_info;
|
||||
|
||||
ret = xadc_parse_dt(indio_dev, pdev->dev.of_node, &conf0);
|
||||
ret = xadc_parse_dt(indio_dev, dev->of_node, &conf0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (xadc->ops->flags & XADC_FLAGS_BUFFERED) {
|
||||
ret = iio_triggered_buffer_setup(indio_dev,
|
||||
&iio_pollfunc_store_time, &xadc_trigger_handler,
|
||||
&xadc_buffer_ops);
|
||||
ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
|
||||
&iio_pollfunc_store_time,
|
||||
&xadc_trigger_handler,
|
||||
&xadc_buffer_ops);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
xadc->convst_trigger = xadc_alloc_trigger(indio_dev, "convst");
|
||||
if (IS_ERR(xadc->convst_trigger)) {
|
||||
ret = PTR_ERR(xadc->convst_trigger);
|
||||
goto err_triggered_buffer_cleanup;
|
||||
}
|
||||
if (IS_ERR(xadc->convst_trigger))
|
||||
return PTR_ERR(xadc->convst_trigger);
|
||||
|
||||
xadc->samplerate_trigger = xadc_alloc_trigger(indio_dev,
|
||||
"samplerate");
|
||||
if (IS_ERR(xadc->samplerate_trigger)) {
|
||||
ret = PTR_ERR(xadc->samplerate_trigger);
|
||||
goto err_free_convst_trigger;
|
||||
}
|
||||
if (IS_ERR(xadc->samplerate_trigger))
|
||||
return PTR_ERR(xadc->samplerate_trigger);
|
||||
}
|
||||
|
||||
xadc->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(xadc->clk)) {
|
||||
ret = PTR_ERR(xadc->clk);
|
||||
goto err_free_samplerate_trigger;
|
||||
}
|
||||
xadc->clk = devm_clk_get(dev, NULL);
|
||||
if (IS_ERR(xadc->clk))
|
||||
return PTR_ERR(xadc->clk);
|
||||
|
||||
ret = clk_prepare_enable(xadc->clk);
|
||||
if (ret)
|
||||
goto err_free_samplerate_trigger;
|
||||
return ret;
|
||||
|
||||
ret = devm_add_action_or_reset(dev,
|
||||
xadc_clk_disable_unprepare, xadc->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Make sure not to exceed the maximum samplerate since otherwise the
|
||||
|
@ -1267,22 +1389,28 @@ static int xadc_probe(struct platform_device *pdev)
|
|||
if (xadc->ops->flags & XADC_FLAGS_BUFFERED) {
|
||||
ret = xadc_read_samplerate(xadc);
|
||||
if (ret < 0)
|
||||
goto err_free_samplerate_trigger;
|
||||
return ret;
|
||||
|
||||
if (ret > XADC_MAX_SAMPLERATE) {
|
||||
ret = xadc_write_samplerate(xadc, XADC_MAX_SAMPLERATE);
|
||||
if (ret < 0)
|
||||
goto err_free_samplerate_trigger;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = request_irq(xadc->irq, xadc->ops->interrupt_handler, 0,
|
||||
dev_name(&pdev->dev), indio_dev);
|
||||
ret = devm_request_irq(dev, xadc->irq, xadc->ops->interrupt_handler, 0,
|
||||
dev_name(dev), indio_dev);
|
||||
if (ret)
|
||||
goto err_clk_disable_unprepare;
|
||||
return ret;
|
||||
|
||||
ret = devm_add_action_or_reset(dev, xadc_cancel_delayed_work,
|
||||
&xadc->zynq_unmask_work);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = xadc->ops->setup(pdev, indio_dev, xadc->irq);
|
||||
if (ret)
|
||||
goto err_free_irq;
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < 16; i++)
|
||||
xadc_read_adc_reg(xadc, XADC_REG_THRESHOLD(i),
|
||||
|
@ -1290,7 +1418,7 @@ static int xadc_probe(struct platform_device *pdev)
|
|||
|
||||
ret = xadc_write_adc_reg(xadc, XADC_REG_CONF0, conf0);
|
||||
if (ret)
|
||||
goto err_free_irq;
|
||||
return ret;
|
||||
|
||||
bipolar_mask = 0;
|
||||
for (i = 0; i < indio_dev->num_channels; i++) {
|
||||
|
@ -1300,17 +1428,18 @@ static int xadc_probe(struct platform_device *pdev)
|
|||
|
||||
ret = xadc_write_adc_reg(xadc, XADC_REG_INPUT_MODE(0), bipolar_mask);
|
||||
if (ret)
|
||||
goto err_free_irq;
|
||||
return ret;
|
||||
|
||||
ret = xadc_write_adc_reg(xadc, XADC_REG_INPUT_MODE(1),
|
||||
bipolar_mask >> 16);
|
||||
if (ret)
|
||||
goto err_free_irq;
|
||||
return ret;
|
||||
|
||||
/* Disable all alarms */
|
||||
ret = xadc_update_adc_reg(xadc, XADC_REG_CONF1, XADC_CONF1_ALARM_MASK,
|
||||
XADC_CONF1_ALARM_MASK);
|
||||
if (ret)
|
||||
goto err_free_irq;
|
||||
return ret;
|
||||
|
||||
/* Set thresholds to min/max */
|
||||
for (i = 0; i < 16; i++) {
|
||||
|
@ -1325,60 +1454,17 @@ static int xadc_probe(struct platform_device *pdev)
|
|||
ret = xadc_write_adc_reg(xadc, XADC_REG_THRESHOLD(i),
|
||||
xadc->threshold[i]);
|
||||
if (ret)
|
||||
goto err_free_irq;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Go to non-buffered mode */
|
||||
xadc_postdisable(indio_dev);
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto err_free_irq;
|
||||
|
||||
platform_set_drvdata(pdev, indio_dev);
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_irq:
|
||||
free_irq(xadc->irq, indio_dev);
|
||||
cancel_delayed_work_sync(&xadc->zynq_unmask_work);
|
||||
err_clk_disable_unprepare:
|
||||
clk_disable_unprepare(xadc->clk);
|
||||
err_free_samplerate_trigger:
|
||||
if (xadc->ops->flags & XADC_FLAGS_BUFFERED)
|
||||
iio_trigger_free(xadc->samplerate_trigger);
|
||||
err_free_convst_trigger:
|
||||
if (xadc->ops->flags & XADC_FLAGS_BUFFERED)
|
||||
iio_trigger_free(xadc->convst_trigger);
|
||||
err_triggered_buffer_cleanup:
|
||||
if (xadc->ops->flags & XADC_FLAGS_BUFFERED)
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int xadc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
struct xadc *xadc = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
if (xadc->ops->flags & XADC_FLAGS_BUFFERED) {
|
||||
iio_trigger_free(xadc->samplerate_trigger);
|
||||
iio_trigger_free(xadc->convst_trigger);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
}
|
||||
free_irq(xadc->irq, indio_dev);
|
||||
cancel_delayed_work_sync(&xadc->zynq_unmask_work);
|
||||
clk_disable_unprepare(xadc->clk);
|
||||
kfree(xadc->data);
|
||||
|
||||
return 0;
|
||||
return devm_iio_device_register(dev, indio_dev);
|
||||
}
|
||||
|
||||
static struct platform_driver xadc_driver = {
|
||||
.probe = xadc_probe,
|
||||
.remove = xadc_remove,
|
||||
.driver = {
|
||||
.name = "xadc",
|
||||
.of_match_table = xadc_of_match_table,
|
||||
|
|
|
@ -155,9 +155,6 @@ err_out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* Register value is msb aligned, the lower 4 bits are ignored */
|
||||
#define XADC_THRESHOLD_VALUE_SHIFT 4
|
||||
|
||||
int xadc_read_event_value(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, enum iio_event_type type,
|
||||
enum iio_event_direction dir, enum iio_event_info info,
|
||||
|
@ -177,7 +174,8 @@ int xadc_read_event_value(struct iio_dev *indio_dev,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
*val >>= XADC_THRESHOLD_VALUE_SHIFT;
|
||||
/* MSB aligned */
|
||||
*val >>= 16 - chan->scan_type.realbits;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
@ -191,7 +189,8 @@ int xadc_write_event_value(struct iio_dev *indio_dev,
|
|||
struct xadc *xadc = iio_priv(indio_dev);
|
||||
int ret = 0;
|
||||
|
||||
val <<= XADC_THRESHOLD_VALUE_SHIFT;
|
||||
/* MSB aligned */
|
||||
val <<= 16 - chan->scan_type.realbits;
|
||||
|
||||
if (val < 0 || val > 0xffff)
|
||||
return -EINVAL;
|
||||
|
|
|
@ -70,6 +70,11 @@ struct xadc {
|
|||
int irq;
|
||||
};
|
||||
|
||||
enum xadc_type {
|
||||
XADC_TYPE_S7, /* Series 7 */
|
||||
XADC_TYPE_US, /* UltraScale and UltraScale+ */
|
||||
};
|
||||
|
||||
struct xadc_ops {
|
||||
int (*read)(struct xadc *xadc, unsigned int reg, uint16_t *val);
|
||||
int (*write)(struct xadc *xadc, unsigned int reg, uint16_t val);
|
||||
|
@ -80,6 +85,7 @@ struct xadc_ops {
|
|||
irqreturn_t (*interrupt_handler)(int irq, void *devid);
|
||||
|
||||
unsigned int flags;
|
||||
enum xadc_type type;
|
||||
};
|
||||
|
||||
static inline int _xadc_read_adc_reg(struct xadc *xadc, unsigned int reg,
|
||||
|
|
|
@ -479,7 +479,7 @@ static u8 bme680_calc_heater_res(struct bme680_data *data, u16 temp)
|
|||
var4 = (var3 / (calib->res_heat_range + 4));
|
||||
var5 = 131 * calib->res_heat_val + 65536;
|
||||
heatr_res_x100 = ((var4 / var5) - 250) * 34;
|
||||
heatr_res = (heatr_res_x100 + 50) / 100;
|
||||
heatr_res = DIV_ROUND_CLOSEST(heatr_res_x100, 100);
|
||||
|
||||
return heatr_res;
|
||||
}
|
||||
|
|
|
@ -282,7 +282,7 @@ static int pms7003_probe(struct serdev_device *serdev)
|
|||
state->serdev = serdev;
|
||||
indio_dev->info = &pms7003_info;
|
||||
indio_dev->name = PMS7003_DRIVER_NAME;
|
||||
indio_dev->channels = pms7003_channels,
|
||||
indio_dev->channels = pms7003_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(pms7003_channels);
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->available_scan_masks = pms7003_scan_masks;
|
||||
|
|
|
@ -71,6 +71,8 @@ static struct {
|
|||
{HID_USAGE_SENSOR_TEMPERATURE, HID_USAGE_SENSOR_UNITS_DEGREES, 1000, 0},
|
||||
|
||||
{HID_USAGE_SENSOR_HUMIDITY, 0, 1000, 0},
|
||||
{HID_USAGE_SENSOR_HINGE, 0, 0, 17453293},
|
||||
{HID_USAGE_SENSOR_HINGE, HID_USAGE_SENSOR_UNITS_DEGREES, 0, 17453293},
|
||||
};
|
||||
|
||||
static void simple_div(int dividend, int divisor, int *whole,
|
||||
|
|
|
@ -488,24 +488,20 @@ int ms_sensors_ht_read_humidity(struct ms_ht_dev *dev_data,
|
|||
EXPORT_SYMBOL(ms_sensors_ht_read_humidity);
|
||||
|
||||
/**
|
||||
* ms_sensors_tp_crc_valid() - CRC check function for
|
||||
* ms_sensors_tp_crc4() - Calculate PROM CRC for
|
||||
* Temperature and pressure devices.
|
||||
* This function is only used when reading PROM coefficients
|
||||
*
|
||||
* @prom: pointer to PROM coefficients array
|
||||
* @len: length of PROM coefficients array
|
||||
*
|
||||
* Return: True if CRC is ok.
|
||||
* Return: CRC.
|
||||
*/
|
||||
static bool ms_sensors_tp_crc_valid(u16 *prom, u8 len)
|
||||
static u8 ms_sensors_tp_crc4(u16 *prom)
|
||||
{
|
||||
unsigned int cnt, n_bit;
|
||||
u16 n_rem = 0x0000, crc_read = prom[0], crc = (*prom & 0xF000) >> 12;
|
||||
u16 n_rem = 0x0000;
|
||||
|
||||
prom[len - 1] = 0;
|
||||
prom[0] &= 0x0FFF; /* Clear the CRC computation part */
|
||||
|
||||
for (cnt = 0; cnt < len * 2; cnt++) {
|
||||
for (cnt = 0; cnt < MS_SENSORS_TP_PROM_WORDS_NB * 2; cnt++) {
|
||||
if (cnt % 2 == 1)
|
||||
n_rem ^= prom[cnt >> 1] & 0x00FF;
|
||||
else
|
||||
|
@ -518,10 +514,55 @@ static bool ms_sensors_tp_crc_valid(u16 *prom, u8 len)
|
|||
n_rem <<= 1;
|
||||
}
|
||||
}
|
||||
n_rem >>= 12;
|
||||
prom[0] = crc_read;
|
||||
|
||||
return n_rem == crc;
|
||||
return n_rem >> 12;
|
||||
}
|
||||
|
||||
/**
|
||||
* ms_sensors_tp_crc_valid_112() - CRC check function for
|
||||
* Temperature and pressure devices for 112bit PROM.
|
||||
* This function is only used when reading PROM coefficients
|
||||
*
|
||||
* @prom: pointer to PROM coefficients array
|
||||
*
|
||||
* Return: True if CRC is ok.
|
||||
*/
|
||||
static bool ms_sensors_tp_crc_valid_112(u16 *prom)
|
||||
{
|
||||
u16 w0 = prom[0], crc_read = (w0 & 0xF000) >> 12;
|
||||
u8 crc;
|
||||
|
||||
prom[0] &= 0x0FFF; /* Clear the CRC computation part */
|
||||
prom[MS_SENSORS_TP_PROM_WORDS_NB - 1] = 0;
|
||||
|
||||
crc = ms_sensors_tp_crc4(prom);
|
||||
|
||||
prom[0] = w0;
|
||||
|
||||
return crc == crc_read;
|
||||
}
|
||||
|
||||
/**
|
||||
* ms_sensors_tp_crc_valid_128() - CRC check function for
|
||||
* Temperature and pressure devices for 128bit PROM.
|
||||
* This function is only used when reading PROM coefficients
|
||||
*
|
||||
* @prom: pointer to PROM coefficients array
|
||||
*
|
||||
* Return: True if CRC is ok.
|
||||
*/
|
||||
static bool ms_sensors_tp_crc_valid_128(u16 *prom)
|
||||
{
|
||||
u16 w7 = prom[7], crc_read = w7 & 0x000F;
|
||||
u8 crc;
|
||||
|
||||
prom[7] &= 0xFF00; /* Clear the CRC and LSB part */
|
||||
|
||||
crc = ms_sensors_tp_crc4(prom);
|
||||
|
||||
prom[7] = w7;
|
||||
|
||||
return crc == crc_read;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -536,8 +577,9 @@ static bool ms_sensors_tp_crc_valid(u16 *prom, u8 len)
|
|||
int ms_sensors_tp_read_prom(struct ms_tp_dev *dev_data)
|
||||
{
|
||||
int i, ret;
|
||||
bool valid;
|
||||
|
||||
for (i = 0; i < MS_SENSORS_TP_PROM_WORDS_NB; i++) {
|
||||
for (i = 0; i < dev_data->hw->prom_len; i++) {
|
||||
ret = ms_sensors_read_prom_word(
|
||||
dev_data->client,
|
||||
MS_SENSORS_TP_PROM_READ + (i << 1),
|
||||
|
@ -547,8 +589,12 @@ int ms_sensors_tp_read_prom(struct ms_tp_dev *dev_data)
|
|||
return ret;
|
||||
}
|
||||
|
||||
if (!ms_sensors_tp_crc_valid(dev_data->prom,
|
||||
MS_SENSORS_TP_PROM_WORDS_NB + 1)) {
|
||||
if (dev_data->hw->prom_len == 8)
|
||||
valid = ms_sensors_tp_crc_valid_128(dev_data->prom);
|
||||
else
|
||||
valid = ms_sensors_tp_crc_valid_112(dev_data->prom);
|
||||
|
||||
if (!valid) {
|
||||
dev_err(&dev_data->client->dev,
|
||||
"Calibration coefficients crc check error\n");
|
||||
return -ENODEV;
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
#include <linux/i2c.h>
|
||||
#include <linux/mutex.h>
|
||||
|
||||
#define MS_SENSORS_TP_PROM_WORDS_NB 7
|
||||
#define MS_SENSORS_TP_PROM_WORDS_NB 8
|
||||
|
||||
/**
|
||||
* struct ms_ht_dev - Humidity/Temperature sensor device structure
|
||||
|
@ -25,6 +25,16 @@ struct ms_ht_dev {
|
|||
u8 res_index;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ms_hw_data - Temperature/Pressure sensor hardware data
|
||||
* @prom_len: number of words in the PROM
|
||||
* @max_res_index: maximum sensor resolution index
|
||||
*/
|
||||
struct ms_tp_hw_data {
|
||||
u8 prom_len;
|
||||
u8 max_res_index;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ms_tp_dev - Temperature/Pressure sensor device structure
|
||||
* @client: i2c client
|
||||
|
@ -36,7 +46,8 @@ struct ms_ht_dev {
|
|||
struct ms_tp_dev {
|
||||
struct i2c_client *client;
|
||||
struct mutex lock;
|
||||
u16 prom[MS_SENSORS_TP_PROM_WORDS_NB + 1];
|
||||
const struct ms_tp_hw_data *hw;
|
||||
u16 prom[MS_SENSORS_TP_PROM_WORDS_NB];
|
||||
u8 res_index;
|
||||
};
|
||||
|
||||
|
|
|
@ -189,6 +189,16 @@ config AD5764
|
|||
To compile this driver as a module, choose M here: the
|
||||
module will be called ad5764.
|
||||
|
||||
config AD5766
|
||||
tristate "Analog Devices AD5766/AD5767 DAC driver"
|
||||
depends on SPI_MASTER
|
||||
help
|
||||
Say yes here to build support for Analog Devices AD5766, AD5767
|
||||
Digital to Analog Converter.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ad5766.
|
||||
|
||||
config AD5770R
|
||||
tristate "Analog Devices AD5770R IDAC driver"
|
||||
depends on SPI_MASTER
|
||||
|
|
|
@ -19,6 +19,7 @@ obj-$(CONFIG_AD5755) += ad5755.o
|
|||
obj-$(CONFIG_AD5755) += ad5758.o
|
||||
obj-$(CONFIG_AD5761) += ad5761.o
|
||||
obj-$(CONFIG_AD5764) += ad5764.o
|
||||
obj-$(CONFIG_AD5766) += ad5766.o
|
||||
obj-$(CONFIG_AD5770R) += ad5770r.o
|
||||
obj-$(CONFIG_AD5791) += ad5791.o
|
||||
obj-$(CONFIG_AD5686) += ad5686.o
|
||||
|
|
|
@ -0,0 +1,643 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Analog Devices AD5766, AD5767
|
||||
* Digital to Analog Converters driver
|
||||
* Copyright 2019-2020 Analog Devices Inc.
|
||||
*/
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#define AD5766_UPPER_WORD_SPI_MASK GENMASK(31, 16)
|
||||
#define AD5766_LOWER_WORD_SPI_MASK GENMASK(15, 0)
|
||||
#define AD5766_DITHER_SOURCE_MASK(ch) GENMASK(((2 * ch) + 1), (2 * ch))
|
||||
#define AD5766_DITHER_SOURCE(ch, source) BIT((ch * 2) + source)
|
||||
#define AD5766_DITHER_SCALE_MASK(x) AD5766_DITHER_SOURCE_MASK(x)
|
||||
#define AD5766_DITHER_SCALE(ch, scale) (scale << (ch * 2))
|
||||
#define AD5766_DITHER_ENABLE_MASK(ch) BIT(ch)
|
||||
#define AD5766_DITHER_ENABLE(ch, state) ((!state) << ch)
|
||||
#define AD5766_DITHER_INVERT_MASK(ch) BIT(ch)
|
||||
#define AD5766_DITHER_INVERT(ch, state) (state << ch)
|
||||
|
||||
#define AD5766_CMD_NOP_MUX_OUT 0x00
|
||||
#define AD5766_CMD_SDO_CNTRL 0x01
|
||||
#define AD5766_CMD_WR_IN_REG(x) (0x10 | ((x) & GENMASK(3, 0)))
|
||||
#define AD5766_CMD_WR_DAC_REG(x) (0x20 | ((x) & GENMASK(3, 0)))
|
||||
#define AD5766_CMD_SW_LDAC 0x30
|
||||
#define AD5766_CMD_SPAN_REG 0x40
|
||||
#define AD5766_CMD_WR_PWR_DITHER 0x51
|
||||
#define AD5766_CMD_WR_DAC_REG_ALL 0x60
|
||||
#define AD5766_CMD_SW_FULL_RESET 0x70
|
||||
#define AD5766_CMD_READBACK_REG(x) (0x80 | ((x) & GENMASK(3, 0)))
|
||||
#define AD5766_CMD_DITHER_SIG_1 0x90
|
||||
#define AD5766_CMD_DITHER_SIG_2 0xA0
|
||||
#define AD5766_CMD_INV_DITHER 0xB0
|
||||
#define AD5766_CMD_DITHER_SCALE_1 0xC0
|
||||
#define AD5766_CMD_DITHER_SCALE_2 0xD0
|
||||
|
||||
#define AD5766_FULL_RESET_CODE 0x1234
|
||||
|
||||
enum ad5766_type {
|
||||
ID_AD5766,
|
||||
ID_AD5767,
|
||||
};
|
||||
|
||||
enum ad5766_voltage_range {
|
||||
AD5766_VOLTAGE_RANGE_M20V_0V,
|
||||
AD5766_VOLTAGE_RANGE_M16V_to_0V,
|
||||
AD5766_VOLTAGE_RANGE_M10V_to_0V,
|
||||
AD5766_VOLTAGE_RANGE_M12V_to_14V,
|
||||
AD5766_VOLTAGE_RANGE_M16V_to_10V,
|
||||
AD5766_VOLTAGE_RANGE_M10V_to_6V,
|
||||
AD5766_VOLTAGE_RANGE_M5V_to_5V,
|
||||
AD5766_VOLTAGE_RANGE_M10V_to_10V,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ad5766_chip_info - chip specific information
|
||||
* @num_channels: number of channels
|
||||
* @channels: channel specification
|
||||
*/
|
||||
struct ad5766_chip_info {
|
||||
unsigned int num_channels;
|
||||
const struct iio_chan_spec *channels;
|
||||
};
|
||||
|
||||
enum {
|
||||
AD5766_DITHER_ENABLE,
|
||||
AD5766_DITHER_INVERT,
|
||||
AD5766_DITHER_SOURCE,
|
||||
};
|
||||
|
||||
/*
|
||||
* Dither signal can also be scaled.
|
||||
* Available dither scale strings corresponding to "dither_scale" field in
|
||||
* "struct ad5766_state".
|
||||
*/
|
||||
static const char * const ad5766_dither_scales[] = {
|
||||
"1",
|
||||
"0.75",
|
||||
"0.5",
|
||||
"0.25",
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ad5766_state - driver instance specific data
|
||||
* @spi: SPI device
|
||||
* @lock: Lock used to restrict concurent access to SPI device
|
||||
* @chip_info: Chip model specific constants
|
||||
* @gpio_reset: Reset GPIO, used to reset the device
|
||||
* @crt_range: Current selected output range
|
||||
* @dither_enable: Power enable bit for each channel dither block (for
|
||||
* example, D15 = DAC 15,D8 = DAC 8, and D0 = DAC 0)
|
||||
* 0 - Normal operation, 1 - Power down
|
||||
* @dither_invert: Inverts the dither signal applied to the selected DAC
|
||||
* outputs
|
||||
* @dither_source: Selects between 2 possible sources:
|
||||
* 1: N0, 2: N1
|
||||
* Two bits are used for each channel
|
||||
* @dither_scale: Two bits are used for each of the 16 channels:
|
||||
* 0: 1 SCALING, 1: 0.75 SCALING, 2: 0.5 SCALING,
|
||||
* 3: 0.25 SCALING.
|
||||
* @data: SPI transfer buffers
|
||||
*/
|
||||
struct ad5766_state {
|
||||
struct spi_device *spi;
|
||||
struct mutex lock;
|
||||
const struct ad5766_chip_info *chip_info;
|
||||
struct gpio_desc *gpio_reset;
|
||||
enum ad5766_voltage_range crt_range;
|
||||
u16 dither_enable;
|
||||
u16 dither_invert;
|
||||
u32 dither_source;
|
||||
u32 dither_scale;
|
||||
union {
|
||||
u32 d32;
|
||||
u16 w16[2];
|
||||
u8 b8[4];
|
||||
} data[3] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
struct ad5766_span_tbl {
|
||||
int min;
|
||||
int max;
|
||||
};
|
||||
|
||||
static const struct ad5766_span_tbl ad5766_span_tbl[] = {
|
||||
[AD5766_VOLTAGE_RANGE_M20V_0V] = {-20, 0},
|
||||
[AD5766_VOLTAGE_RANGE_M16V_to_0V] = {-16, 0},
|
||||
[AD5766_VOLTAGE_RANGE_M10V_to_0V] = {-10, 0},
|
||||
[AD5766_VOLTAGE_RANGE_M12V_to_14V] = {-12, 14},
|
||||
[AD5766_VOLTAGE_RANGE_M16V_to_10V] = {-16, 10},
|
||||
[AD5766_VOLTAGE_RANGE_M10V_to_6V] = {-10, 6},
|
||||
[AD5766_VOLTAGE_RANGE_M5V_to_5V] = {-5, 5},
|
||||
[AD5766_VOLTAGE_RANGE_M10V_to_10V] = {-10, 10},
|
||||
};
|
||||
|
||||
static int __ad5766_spi_read(struct ad5766_state *st, u8 dac, int *val)
|
||||
{
|
||||
int ret;
|
||||
struct spi_transfer xfers[] = {
|
||||
{
|
||||
.tx_buf = &st->data[0].d32,
|
||||
.bits_per_word = 8,
|
||||
.len = 3,
|
||||
.cs_change = 1,
|
||||
}, {
|
||||
.tx_buf = &st->data[1].d32,
|
||||
.rx_buf = &st->data[2].d32,
|
||||
.bits_per_word = 8,
|
||||
.len = 3,
|
||||
},
|
||||
};
|
||||
|
||||
st->data[0].d32 = AD5766_CMD_READBACK_REG(dac);
|
||||
st->data[1].d32 = AD5766_CMD_NOP_MUX_OUT;
|
||||
|
||||
ret = spi_sync_transfer(st->spi, xfers, ARRAY_SIZE(xfers));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*val = st->data[2].w16[1];
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __ad5766_spi_write(struct ad5766_state *st, u8 command, u16 data)
|
||||
{
|
||||
st->data[0].b8[0] = command;
|
||||
put_unaligned_be16(data, &st->data[0].b8[1]);
|
||||
|
||||
return spi_write(st->spi, &st->data[0].b8[0], 3);
|
||||
}
|
||||
|
||||
static int ad5766_read(struct iio_dev *indio_dev, u8 dac, int *val)
|
||||
{
|
||||
struct ad5766_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&st->lock);
|
||||
ret = __ad5766_spi_read(st, dac, val);
|
||||
mutex_unlock(&st->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad5766_write(struct iio_dev *indio_dev, u8 dac, u16 data)
|
||||
{
|
||||
struct ad5766_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&st->lock);
|
||||
ret = __ad5766_spi_write(st, AD5766_CMD_WR_DAC_REG(dac), data);
|
||||
mutex_unlock(&st->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad5766_reset(struct ad5766_state *st)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (st->gpio_reset) {
|
||||
gpiod_set_value_cansleep(st->gpio_reset, 1);
|
||||
ndelay(100); /* t_reset >= 100ns */
|
||||
gpiod_set_value_cansleep(st->gpio_reset, 0);
|
||||
} else {
|
||||
ret = __ad5766_spi_write(st, AD5766_CMD_SW_FULL_RESET,
|
||||
AD5766_FULL_RESET_CODE);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Minimum time between a reset and the subsequent successful write is
|
||||
* typically 25 ns
|
||||
*/
|
||||
ndelay(25);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ad5766_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val,
|
||||
int *val2,
|
||||
long m)
|
||||
{
|
||||
struct ad5766_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (m) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = ad5766_read(indio_dev, chan->address, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
*val = ad5766_span_tbl[st->crt_range].min;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = ad5766_span_tbl[st->crt_range].max -
|
||||
ad5766_span_tbl[st->crt_range].min;
|
||||
*val2 = st->chip_info->channels[0].scan_type.realbits;
|
||||
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int ad5766_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val,
|
||||
int val2,
|
||||
long info)
|
||||
{
|
||||
switch (info) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
{
|
||||
const int max_val = GENMASK(chan->scan_type.realbits - 1, 0);
|
||||
|
||||
if (val > max_val || val < 0)
|
||||
return -EINVAL;
|
||||
val <<= chan->scan_type.shift;
|
||||
return ad5766_write(indio_dev, chan->address, val);
|
||||
}
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct iio_info ad5766_info = {
|
||||
.read_raw = ad5766_read_raw,
|
||||
.write_raw = ad5766_write_raw,
|
||||
};
|
||||
|
||||
static int ad5766_get_dither_source(struct iio_dev *dev,
|
||||
const struct iio_chan_spec *chan)
|
||||
{
|
||||
struct ad5766_state *st = iio_priv(dev);
|
||||
u32 source;
|
||||
|
||||
source = st->dither_source & AD5766_DITHER_SOURCE_MASK(chan->channel);
|
||||
source = source >> (chan->channel * 2);
|
||||
source -= 1;
|
||||
|
||||
return source;
|
||||
}
|
||||
|
||||
static int ad5766_set_dither_source(struct iio_dev *dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
unsigned int source)
|
||||
{
|
||||
struct ad5766_state *st = iio_priv(dev);
|
||||
uint16_t val;
|
||||
int ret;
|
||||
|
||||
st->dither_source &= ~AD5766_DITHER_SOURCE_MASK(chan->channel);
|
||||
st->dither_source |= AD5766_DITHER_SOURCE(chan->channel, source);
|
||||
|
||||
val = FIELD_GET(AD5766_LOWER_WORD_SPI_MASK, st->dither_source);
|
||||
ret = ad5766_write(dev, AD5766_CMD_DITHER_SIG_1, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
val = FIELD_GET(AD5766_UPPER_WORD_SPI_MASK, st->dither_source);
|
||||
|
||||
return ad5766_write(dev, AD5766_CMD_DITHER_SIG_2, val);
|
||||
}
|
||||
|
||||
static int ad5766_get_dither_scale(struct iio_dev *dev,
|
||||
const struct iio_chan_spec *chan)
|
||||
{
|
||||
struct ad5766_state *st = iio_priv(dev);
|
||||
u32 scale;
|
||||
|
||||
scale = st->dither_scale & AD5766_DITHER_SCALE_MASK(chan->channel);
|
||||
|
||||
return (scale >> (chan->channel * 2));
|
||||
}
|
||||
|
||||
static int ad5766_set_dither_scale(struct iio_dev *dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
unsigned int scale)
|
||||
{
|
||||
int ret;
|
||||
struct ad5766_state *st = iio_priv(dev);
|
||||
uint16_t val;
|
||||
|
||||
st->dither_scale &= ~AD5766_DITHER_SCALE_MASK(chan->channel);
|
||||
st->dither_scale |= AD5766_DITHER_SCALE(chan->channel, scale);
|
||||
|
||||
val = FIELD_GET(AD5766_LOWER_WORD_SPI_MASK, st->dither_scale);
|
||||
ret = ad5766_write(dev, AD5766_CMD_DITHER_SCALE_1, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
val = FIELD_GET(AD5766_UPPER_WORD_SPI_MASK, st->dither_scale);
|
||||
|
||||
return ad5766_write(dev, AD5766_CMD_DITHER_SCALE_2, val);
|
||||
}
|
||||
|
||||
static const struct iio_enum ad5766_dither_scale_enum = {
|
||||
.items = ad5766_dither_scales,
|
||||
.num_items = ARRAY_SIZE(ad5766_dither_scales),
|
||||
.set = ad5766_set_dither_scale,
|
||||
.get = ad5766_get_dither_scale,
|
||||
};
|
||||
|
||||
static ssize_t ad5766_read_ext(struct iio_dev *indio_dev,
|
||||
uintptr_t private,
|
||||
const struct iio_chan_spec *chan,
|
||||
char *buf)
|
||||
{
|
||||
struct ad5766_state *st = iio_priv(indio_dev);
|
||||
|
||||
switch (private) {
|
||||
case AD5766_DITHER_ENABLE:
|
||||
return sprintf(buf, "%u\n",
|
||||
!(st->dither_enable & BIT(chan->channel)));
|
||||
break;
|
||||
case AD5766_DITHER_INVERT:
|
||||
return sprintf(buf, "%u\n",
|
||||
!!(st->dither_invert & BIT(chan->channel)));
|
||||
break;
|
||||
case AD5766_DITHER_SOURCE:
|
||||
return sprintf(buf, "%d\n",
|
||||
ad5766_get_dither_source(indio_dev, chan));
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t ad5766_write_ext(struct iio_dev *indio_dev,
|
||||
uintptr_t private,
|
||||
const struct iio_chan_spec *chan,
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
struct ad5766_state *st = iio_priv(indio_dev);
|
||||
bool readin;
|
||||
int ret;
|
||||
|
||||
ret = kstrtobool(buf, &readin);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
switch (private) {
|
||||
case AD5766_DITHER_ENABLE:
|
||||
st->dither_enable &= ~AD5766_DITHER_ENABLE_MASK(chan->channel);
|
||||
st->dither_enable |= AD5766_DITHER_ENABLE(chan->channel,
|
||||
readin);
|
||||
ret = ad5766_write(indio_dev, AD5766_CMD_WR_PWR_DITHER,
|
||||
st->dither_enable);
|
||||
break;
|
||||
case AD5766_DITHER_INVERT:
|
||||
st->dither_invert &= ~AD5766_DITHER_INVERT_MASK(chan->channel);
|
||||
st->dither_invert |= AD5766_DITHER_INVERT(chan->channel,
|
||||
readin);
|
||||
ret = ad5766_write(indio_dev, AD5766_CMD_INV_DITHER,
|
||||
st->dither_invert);
|
||||
break;
|
||||
case AD5766_DITHER_SOURCE:
|
||||
ret = ad5766_set_dither_source(indio_dev, chan, readin);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return ret ? ret : len;
|
||||
}
|
||||
|
||||
#define _AD5766_CHAN_EXT_INFO(_name, _what, _shared) { \
|
||||
.name = _name, \
|
||||
.read = ad5766_read_ext, \
|
||||
.write = ad5766_write_ext, \
|
||||
.private = _what, \
|
||||
.shared = _shared, \
|
||||
}
|
||||
|
||||
#define IIO_ENUM_AVAILABLE_SHARED(_name, _shared, _e) \
|
||||
{ \
|
||||
.name = (_name "_available"), \
|
||||
.shared = _shared, \
|
||||
.read = iio_enum_available_read, \
|
||||
.private = (uintptr_t)(_e), \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec_ext_info ad5766_ext_info[] = {
|
||||
|
||||
_AD5766_CHAN_EXT_INFO("dither_enable", AD5766_DITHER_ENABLE,
|
||||
IIO_SEPARATE),
|
||||
_AD5766_CHAN_EXT_INFO("dither_invert", AD5766_DITHER_INVERT,
|
||||
IIO_SEPARATE),
|
||||
_AD5766_CHAN_EXT_INFO("dither_source", AD5766_DITHER_SOURCE,
|
||||
IIO_SEPARATE),
|
||||
IIO_ENUM("dither_scale", IIO_SEPARATE, &ad5766_dither_scale_enum),
|
||||
IIO_ENUM_AVAILABLE_SHARED("dither_scale",
|
||||
IIO_SEPARATE,
|
||||
&ad5766_dither_scale_enum),
|
||||
{}
|
||||
};
|
||||
|
||||
#define AD576x_CHANNEL(_chan, _bits) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.output = 1, \
|
||||
.channel = (_chan), \
|
||||
.address = (_chan), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) | \
|
||||
BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = (_bits), \
|
||||
.storagebits = 16, \
|
||||
.shift = 16 - (_bits), \
|
||||
}, \
|
||||
.ext_info = ad5766_ext_info, \
|
||||
}
|
||||
|
||||
#define DECLARE_AD576x_CHANNELS(_name, _bits) \
|
||||
const struct iio_chan_spec _name[] = { \
|
||||
AD576x_CHANNEL(0, (_bits)), \
|
||||
AD576x_CHANNEL(1, (_bits)), \
|
||||
AD576x_CHANNEL(2, (_bits)), \
|
||||
AD576x_CHANNEL(3, (_bits)), \
|
||||
AD576x_CHANNEL(4, (_bits)), \
|
||||
AD576x_CHANNEL(5, (_bits)), \
|
||||
AD576x_CHANNEL(6, (_bits)), \
|
||||
AD576x_CHANNEL(7, (_bits)), \
|
||||
AD576x_CHANNEL(8, (_bits)), \
|
||||
AD576x_CHANNEL(9, (_bits)), \
|
||||
AD576x_CHANNEL(10, (_bits)), \
|
||||
AD576x_CHANNEL(11, (_bits)), \
|
||||
AD576x_CHANNEL(12, (_bits)), \
|
||||
AD576x_CHANNEL(13, (_bits)), \
|
||||
AD576x_CHANNEL(14, (_bits)), \
|
||||
AD576x_CHANNEL(15, (_bits)), \
|
||||
}
|
||||
|
||||
static DECLARE_AD576x_CHANNELS(ad5766_channels, 16);
|
||||
static DECLARE_AD576x_CHANNELS(ad5767_channels, 12);
|
||||
|
||||
static const struct ad5766_chip_info ad5766_chip_infos[] = {
|
||||
[ID_AD5766] = {
|
||||
.num_channels = ARRAY_SIZE(ad5766_channels),
|
||||
.channels = ad5766_channels,
|
||||
},
|
||||
[ID_AD5767] = {
|
||||
.num_channels = ARRAY_SIZE(ad5767_channels),
|
||||
.channels = ad5767_channels,
|
||||
},
|
||||
};
|
||||
|
||||
static int ad5766_get_output_range(struct ad5766_state *st)
|
||||
{
|
||||
int i, ret, min, max, tmp[2];
|
||||
|
||||
ret = device_property_read_u32_array(&st->spi->dev,
|
||||
"output-range-voltage",
|
||||
tmp, 2);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
min = tmp[0] / 1000;
|
||||
max = tmp[1] / 1000;
|
||||
for (i = 0; i < ARRAY_SIZE(ad5766_span_tbl); i++) {
|
||||
if (ad5766_span_tbl[i].min != min ||
|
||||
ad5766_span_tbl[i].max != max)
|
||||
continue;
|
||||
|
||||
st->crt_range = i;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int ad5766_default_setup(struct ad5766_state *st)
|
||||
{
|
||||
uint16_t val;
|
||||
int ret, i;
|
||||
|
||||
/* Always issue a reset before writing to the span register. */
|
||||
ret = ad5766_reset(st);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ad5766_get_output_range(st);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Dither power down */
|
||||
st->dither_enable = GENMASK(15, 0);
|
||||
ret = __ad5766_spi_write(st, AD5766_CMD_WR_PWR_DITHER,
|
||||
st->dither_enable);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
st->dither_source = 0;
|
||||
for (i = 0; i < ARRAY_SIZE(ad5766_channels); i++)
|
||||
st->dither_source |= AD5766_DITHER_SOURCE(i, 0);
|
||||
val = FIELD_GET(AD5766_LOWER_WORD_SPI_MASK, st->dither_source);
|
||||
ret = __ad5766_spi_write(st, AD5766_CMD_DITHER_SIG_1, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
val = FIELD_GET(AD5766_UPPER_WORD_SPI_MASK, st->dither_source);
|
||||
ret = __ad5766_spi_write(st, AD5766_CMD_DITHER_SIG_2, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
st->dither_scale = 0;
|
||||
val = FIELD_GET(AD5766_LOWER_WORD_SPI_MASK, st->dither_scale);
|
||||
ret = __ad5766_spi_write(st, AD5766_CMD_DITHER_SCALE_1, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
val = FIELD_GET(AD5766_UPPER_WORD_SPI_MASK, st->dither_scale);
|
||||
ret = __ad5766_spi_write(st, AD5766_CMD_DITHER_SCALE_2, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
st->dither_invert = 0;
|
||||
ret = __ad5766_spi_write(st, AD5766_CMD_INV_DITHER, st->dither_invert);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return __ad5766_spi_write(st, AD5766_CMD_SPAN_REG, st->crt_range);
|
||||
}
|
||||
|
||||
static int ad5766_probe(struct spi_device *spi)
|
||||
{
|
||||
enum ad5766_type type;
|
||||
struct iio_dev *indio_dev;
|
||||
struct ad5766_state *st;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
mutex_init(&st->lock);
|
||||
|
||||
st->spi = spi;
|
||||
type = spi_get_device_id(spi)->driver_data;
|
||||
st->chip_info = &ad5766_chip_infos[type];
|
||||
|
||||
indio_dev->channels = st->chip_info->channels;
|
||||
indio_dev->num_channels = st->chip_info->num_channels;
|
||||
indio_dev->info = &ad5766_info;
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->dev.of_node = spi->dev.of_node;
|
||||
indio_dev->name = spi_get_device_id(spi)->name;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
st->gpio_reset = devm_gpiod_get_optional(&st->spi->dev, "reset",
|
||||
GPIOD_OUT_LOW);
|
||||
if (IS_ERR(st->gpio_reset))
|
||||
return PTR_ERR(st->gpio_reset);
|
||||
|
||||
ret = ad5766_default_setup(st);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return devm_iio_device_register(&spi->dev, indio_dev);
|
||||
}
|
||||
|
||||
static const struct of_device_id ad5766_dt_match[] = {
|
||||
{ .compatible = "adi,ad5766" },
|
||||
{ .compatible = "adi,ad5767" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ad5766_dt_match);
|
||||
|
||||
static const struct spi_device_id ad5766_spi_ids[] = {
|
||||
{ "ad5766", ID_AD5766 },
|
||||
{ "ad5767", ID_AD5767 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ad5766_spi_ids);
|
||||
|
||||
static struct spi_driver ad5766_driver = {
|
||||
.driver = {
|
||||
.name = "ad5766",
|
||||
.of_match_table = ad5766_dt_match,
|
||||
},
|
||||
.probe = ad5766_probe,
|
||||
.id_table = ad5766_spi_ids,
|
||||
};
|
||||
module_spi_driver(ad5766_driver);
|
||||
|
||||
MODULE_AUTHOR("Denis-Gabriel Gheorghescu <denis.gheorghescu@analog.com>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD5766/AD5767 DACs");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -582,8 +582,7 @@ error_disable_reg:
|
|||
if (!IS_ERR(st->reg))
|
||||
regulator_disable(st->reg);
|
||||
error_disable_clk:
|
||||
if (clk)
|
||||
clk_disable_unprepare(clk);
|
||||
clk_disable_unprepare(clk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -599,8 +598,7 @@ static int adf4350_remove(struct spi_device *spi)
|
|||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
if (st->clk)
|
||||
clk_disable_unprepare(st->clk);
|
||||
clk_disable_unprepare(st->clk);
|
||||
|
||||
if (!IS_ERR(reg))
|
||||
regulator_disable(reg);
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include "bmg160.h"
|
||||
|
||||
#define BMG160_IRQ_NAME "bmg160_event"
|
||||
|
@ -92,6 +93,7 @@
|
|||
|
||||
struct bmg160_data {
|
||||
struct regmap *regmap;
|
||||
struct regulator_bulk_data regulators[2];
|
||||
struct iio_trigger *dready_trig;
|
||||
struct iio_trigger *motion_trig;
|
||||
struct iio_mount_matrix orientation;
|
||||
|
@ -1061,6 +1063,13 @@ static const char *bmg160_match_acpi_device(struct device *dev)
|
|||
return dev_name(dev);
|
||||
}
|
||||
|
||||
static void bmg160_disable_regulators(void *d)
|
||||
{
|
||||
struct bmg160_data *data = d;
|
||||
|
||||
regulator_bulk_disable(ARRAY_SIZE(data->regulators), data->regulators);
|
||||
}
|
||||
|
||||
int bmg160_core_probe(struct device *dev, struct regmap *regmap, int irq,
|
||||
const char *name)
|
||||
{
|
||||
|
@ -1077,6 +1086,22 @@ int bmg160_core_probe(struct device *dev, struct regmap *regmap, int irq,
|
|||
data->irq = irq;
|
||||
data->regmap = regmap;
|
||||
|
||||
data->regulators[0].supply = "vdd";
|
||||
data->regulators[1].supply = "vddio";
|
||||
ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(data->regulators),
|
||||
data->regulators);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Failed to get regulators\n");
|
||||
|
||||
ret = regulator_bulk_enable(ARRAY_SIZE(data->regulators),
|
||||
data->regulators);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = devm_add_action_or_reset(dev, bmg160_disable_regulators, data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = iio_read_mount_matrix(dev, "mount-matrix",
|
||||
&data->orientation);
|
||||
if (ret)
|
||||
|
|
|
@ -23,15 +23,20 @@ enum gyro_3d_channel {
|
|||
GYRO_3D_CHANNEL_MAX,
|
||||
};
|
||||
|
||||
#define CHANNEL_SCAN_INDEX_TIMESTAMP GYRO_3D_CHANNEL_MAX
|
||||
struct gyro_3d_state {
|
||||
struct hid_sensor_hub_callbacks callbacks;
|
||||
struct hid_sensor_common common_attributes;
|
||||
struct hid_sensor_hub_attribute_info gyro[GYRO_3D_CHANNEL_MAX];
|
||||
u32 gyro_val[GYRO_3D_CHANNEL_MAX];
|
||||
struct {
|
||||
u32 gyro_val[GYRO_3D_CHANNEL_MAX];
|
||||
u64 timestamp __aligned(8);
|
||||
} scan;
|
||||
int scale_pre_decml;
|
||||
int scale_post_decml;
|
||||
int scale_precision;
|
||||
int value_offset;
|
||||
s64 timestamp;
|
||||
};
|
||||
|
||||
static const u32 gyro_3d_addresses[GYRO_3D_CHANNEL_MAX] = {
|
||||
|
@ -72,7 +77,8 @@ static const struct iio_chan_spec gyro_3d_channels[] = {
|
|||
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
|
||||
BIT(IIO_CHAN_INFO_HYSTERESIS),
|
||||
.scan_index = CHANNEL_SCAN_INDEX_Z,
|
||||
}
|
||||
},
|
||||
IIO_CHAN_SOFT_TIMESTAMP(CHANNEL_SCAN_INDEX_TIMESTAMP)
|
||||
};
|
||||
|
||||
/* Adjust channel real bits based on report descriptor */
|
||||
|
@ -178,14 +184,6 @@ static const struct iio_info gyro_3d_info = {
|
|||
.write_raw = &gyro_3d_write_raw,
|
||||
};
|
||||
|
||||
/* Function to push data to buffer */
|
||||
static void hid_sensor_push_data(struct iio_dev *indio_dev, const void *data,
|
||||
int len)
|
||||
{
|
||||
dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n");
|
||||
iio_push_to_buffers(indio_dev, data);
|
||||
}
|
||||
|
||||
/* Callback handler to send event after all samples are received and captured */
|
||||
static int gyro_3d_proc_event(struct hid_sensor_hub_device *hsdev,
|
||||
unsigned usage_id,
|
||||
|
@ -195,10 +193,15 @@ static int gyro_3d_proc_event(struct hid_sensor_hub_device *hsdev,
|
|||
struct gyro_3d_state *gyro_state = iio_priv(indio_dev);
|
||||
|
||||
dev_dbg(&indio_dev->dev, "gyro_3d_proc_event\n");
|
||||
if (atomic_read(&gyro_state->common_attributes.data_ready))
|
||||
hid_sensor_push_data(indio_dev,
|
||||
gyro_state->gyro_val,
|
||||
sizeof(gyro_state->gyro_val));
|
||||
if (atomic_read(&gyro_state->common_attributes.data_ready)) {
|
||||
if (!gyro_state->timestamp)
|
||||
gyro_state->timestamp = iio_get_time_ns(indio_dev);
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, &gyro_state->scan,
|
||||
gyro_state->timestamp);
|
||||
|
||||
gyro_state->timestamp = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -219,10 +222,15 @@ static int gyro_3d_capture_sample(struct hid_sensor_hub_device *hsdev,
|
|||
case HID_USAGE_SENSOR_ANGL_VELOCITY_Y_AXIS:
|
||||
case HID_USAGE_SENSOR_ANGL_VELOCITY_Z_AXIS:
|
||||
offset = usage_id - HID_USAGE_SENSOR_ANGL_VELOCITY_X_AXIS;
|
||||
gyro_state->gyro_val[CHANNEL_SCAN_INDEX_X + offset] =
|
||||
*(u32 *)raw_data;
|
||||
gyro_state->scan.gyro_val[CHANNEL_SCAN_INDEX_X + offset] =
|
||||
*(u32 *)raw_data;
|
||||
ret = 0;
|
||||
break;
|
||||
case HID_USAGE_SENSOR_TIME_TIMESTAMP:
|
||||
gyro_state->timestamp =
|
||||
hid_sensor_convert_timestamp(&gyro_state->common_attributes,
|
||||
*(s64 *)raw_data);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -16,8 +16,8 @@ config INV_MPU6050_I2C
|
|||
select REGMAP_I2C
|
||||
help
|
||||
This driver supports the Invensense MPU6050/9150,
|
||||
MPU6500/6515/9250/9255, ICM20608/20609/20689, ICM20602/ICM20690 and
|
||||
IAM20680 motion tracking devices over I2C.
|
||||
MPU6500/6515/6880/9250/9255, ICM20608/20609/20689, ICM20602/ICM20690
|
||||
and IAM20680 motion tracking devices over I2C.
|
||||
This driver can be built as a module. The module will be called
|
||||
inv-mpu6050-i2c.
|
||||
|
||||
|
@ -28,7 +28,7 @@ config INV_MPU6050_SPI
|
|||
select REGMAP_SPI
|
||||
help
|
||||
This driver supports the Invensense MPU6000,
|
||||
MPU6500/6515/9250/9255, ICM20608/20609/20689, ICM20602/ICM20690 and
|
||||
IAM20680 motion tracking devices over SPI.
|
||||
MPU6500/6515/6880/9250/9255, ICM20608/20609/20689, ICM20602/ICM20690
|
||||
and IAM20680 motion tracking devices over SPI.
|
||||
This driver can be built as a module. The module will be called
|
||||
inv-mpu6050-spi.
|
||||
|
|
|
@ -160,6 +160,14 @@ static const struct inv_mpu6050_hw hw_info[] = {
|
|||
.fifo_size = 512,
|
||||
.temp = {INV_MPU6500_TEMP_OFFSET, INV_MPU6500_TEMP_SCALE},
|
||||
},
|
||||
{
|
||||
.whoami = INV_MPU6880_WHOAMI_VALUE,
|
||||
.name = "MPU6880",
|
||||
.reg = ®_set_6500,
|
||||
.config = &chip_config_6500,
|
||||
.fifo_size = 4096,
|
||||
.temp = {INV_MPU6500_TEMP_OFFSET, INV_MPU6500_TEMP_SCALE},
|
||||
},
|
||||
{
|
||||
.whoami = INV_MPU6000_WHOAMI_VALUE,
|
||||
.name = "MPU6000",
|
||||
|
@ -1323,6 +1331,7 @@ static int inv_check_and_setup_chip(struct inv_mpu6050_state *st)
|
|||
case INV_MPU6000:
|
||||
case INV_MPU6500:
|
||||
case INV_MPU6515:
|
||||
case INV_MPU6880:
|
||||
case INV_MPU9250:
|
||||
case INV_MPU9255:
|
||||
/* reset signal path (required for spi connection) */
|
||||
|
|
|
@ -177,6 +177,7 @@ static const struct i2c_device_id inv_mpu_id[] = {
|
|||
{"mpu6050", INV_MPU6050},
|
||||
{"mpu6500", INV_MPU6500},
|
||||
{"mpu6515", INV_MPU6515},
|
||||
{"mpu6880", INV_MPU6880},
|
||||
{"mpu9150", INV_MPU9150},
|
||||
{"mpu9250", INV_MPU9250},
|
||||
{"mpu9255", INV_MPU9255},
|
||||
|
@ -204,6 +205,10 @@ static const struct of_device_id inv_of_match[] = {
|
|||
.compatible = "invensense,mpu6515",
|
||||
.data = (void *)INV_MPU6515
|
||||
},
|
||||
{
|
||||
.compatible = "invensense,mpu6880",
|
||||
.data = (void *)INV_MPU6880
|
||||
},
|
||||
{
|
||||
.compatible = "invensense,mpu9150",
|
||||
.data = (void *)INV_MPU9150
|
||||
|
|
|
@ -70,6 +70,7 @@ enum inv_devices {
|
|||
INV_MPU6050,
|
||||
INV_MPU6500,
|
||||
INV_MPU6515,
|
||||
INV_MPU6880,
|
||||
INV_MPU6000,
|
||||
INV_MPU9150,
|
||||
INV_MPU9250,
|
||||
|
@ -373,6 +374,7 @@ struct inv_mpu6050_state {
|
|||
#define INV_MPU6000_WHOAMI_VALUE 0x68
|
||||
#define INV_MPU6050_WHOAMI_VALUE 0x68
|
||||
#define INV_MPU6500_WHOAMI_VALUE 0x70
|
||||
#define INV_MPU6880_WHOAMI_VALUE 0x78
|
||||
#define INV_MPU9150_WHOAMI_VALUE 0x68
|
||||
#define INV_MPU9250_WHOAMI_VALUE 0x71
|
||||
#define INV_MPU9255_WHOAMI_VALUE 0x73
|
||||
|
|
|
@ -70,6 +70,7 @@ static const struct spi_device_id inv_mpu_id[] = {
|
|||
{"mpu6000", INV_MPU6000},
|
||||
{"mpu6500", INV_MPU6500},
|
||||
{"mpu6515", INV_MPU6515},
|
||||
{"mpu6880", INV_MPU6880},
|
||||
{"mpu9250", INV_MPU9250},
|
||||
{"mpu9255", INV_MPU9255},
|
||||
{"icm20608", INV_ICM20608},
|
||||
|
@ -96,6 +97,10 @@ static const struct of_device_id inv_of_match[] = {
|
|||
.compatible = "invensense,mpu6515",
|
||||
.data = (void *)INV_MPU6515
|
||||
},
|
||||
{
|
||||
.compatible = "invensense,mpu6880",
|
||||
.data = (void *)INV_MPU6880
|
||||
},
|
||||
{
|
||||
.compatible = "invensense,mpu9250",
|
||||
.data = (void *)INV_MPU9250
|
||||
|
|
|
@ -169,6 +169,36 @@ static const char * const iio_chan_info_postfix[] = {
|
|||
[IIO_CHAN_INFO_CALIBAMBIENT] = "calibambient",
|
||||
};
|
||||
|
||||
/**
|
||||
* iio_sysfs_match_string_with_gaps - matches given string in an array with gaps
|
||||
* @array: array of strings
|
||||
* @n: number of strings in the array
|
||||
* @str: string to match with
|
||||
*
|
||||
* Returns index of @str in the @array or -EINVAL, similar to match_string().
|
||||
* Uses sysfs_streq instead of strcmp for matching.
|
||||
*
|
||||
* This routine will look for a string in an array of strings.
|
||||
* The search will continue until the element is found or the n-th element
|
||||
* is reached, regardless of any NULL elements in the array.
|
||||
*/
|
||||
static int iio_sysfs_match_string_with_gaps(const char * const *array, size_t n,
|
||||
const char *str)
|
||||
{
|
||||
const char *item;
|
||||
int index;
|
||||
|
||||
for (index = 0; index < n; index++) {
|
||||
item = array[index];
|
||||
if (!item)
|
||||
continue;
|
||||
if (sysfs_streq(item, str))
|
||||
return index;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_DEBUG_FS)
|
||||
/*
|
||||
* There's also a CONFIG_DEBUG_FS guard in include/linux/iio/iio.h for
|
||||
|
@ -470,8 +500,11 @@ ssize_t iio_enum_available_read(struct iio_dev *indio_dev,
|
|||
if (!e->num_items)
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < e->num_items; ++i)
|
||||
for (i = 0; i < e->num_items; ++i) {
|
||||
if (!e->items[i])
|
||||
continue;
|
||||
len += scnprintf(buf + len, PAGE_SIZE - len, "%s ", e->items[i]);
|
||||
}
|
||||
|
||||
/* replace last space with a newline */
|
||||
buf[len - 1] = '\n';
|
||||
|
@ -492,7 +525,7 @@ ssize_t iio_enum_read(struct iio_dev *indio_dev,
|
|||
i = e->get(indio_dev, chan);
|
||||
if (i < 0)
|
||||
return i;
|
||||
else if (i >= e->num_items)
|
||||
else if (i >= e->num_items || !e->items[i])
|
||||
return -EINVAL;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n", e->items[i]);
|
||||
|
@ -509,7 +542,7 @@ ssize_t iio_enum_write(struct iio_dev *indio_dev,
|
|||
if (!e->set)
|
||||
return -EINVAL;
|
||||
|
||||
ret = __sysfs_match_string(e->items, e->num_items, buf);
|
||||
ret = iio_sysfs_match_string_with_gaps(e->items, e->num_items, buf);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
@ -1473,11 +1506,14 @@ static int iio_device_register_sysfs(struct iio_dev *indio_dev)
|
|||
goto error_clear_attrs;
|
||||
}
|
||||
/* Copy across original attributes */
|
||||
if (indio_dev->info->attrs)
|
||||
if (indio_dev->info->attrs) {
|
||||
memcpy(iio_dev_opaque->chan_attr_group.attrs,
|
||||
indio_dev->info->attrs->attrs,
|
||||
sizeof(iio_dev_opaque->chan_attr_group.attrs[0])
|
||||
*attrcount_orig);
|
||||
iio_dev_opaque->chan_attr_group.is_visible =
|
||||
indio_dev->info->attrs->is_visible;
|
||||
}
|
||||
attrn = attrcount_orig;
|
||||
/* Add all elements from the list. */
|
||||
list_for_each_entry(p, &iio_dev_opaque->channel_attr_list, l)
|
||||
|
|
|
@ -191,8 +191,8 @@ err_free_channel:
|
|||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
static struct iio_channel *of_iio_channel_get_by_name(struct device_node *np,
|
||||
const char *name)
|
||||
struct iio_channel *of_iio_channel_get_by_name(struct device_node *np,
|
||||
const char *name)
|
||||
{
|
||||
struct iio_channel *chan = NULL;
|
||||
|
||||
|
@ -230,6 +230,7 @@ static struct iio_channel *of_iio_channel_get_by_name(struct device_node *np,
|
|||
|
||||
return chan;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(of_iio_channel_get_by_name);
|
||||
|
||||
static struct iio_channel *of_iio_channel_get_all(struct device *dev)
|
||||
{
|
||||
|
@ -272,12 +273,6 @@ error_free_chans:
|
|||
|
||||
#else /* CONFIG_OF */
|
||||
|
||||
static inline struct iio_channel *
|
||||
of_iio_channel_get_by_name(struct device_node *np, const char *name)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline struct iio_channel *of_iio_channel_get_all(struct device *dev)
|
||||
{
|
||||
return NULL;
|
||||
|
@ -393,6 +388,29 @@ struct iio_channel *devm_iio_channel_get(struct device *dev,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(devm_iio_channel_get);
|
||||
|
||||
struct iio_channel *devm_of_iio_channel_get_by_name(struct device *dev,
|
||||
struct device_node *np,
|
||||
const char *channel_name)
|
||||
{
|
||||
struct iio_channel **ptr, *channel;
|
||||
|
||||
ptr = devres_alloc(devm_iio_channel_free, sizeof(*ptr), GFP_KERNEL);
|
||||
if (!ptr)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
channel = of_iio_channel_get_by_name(np, channel_name);
|
||||
if (IS_ERR(channel)) {
|
||||
devres_free(ptr);
|
||||
return channel;
|
||||
}
|
||||
|
||||
*ptr = channel;
|
||||
devres_add(dev, ptr);
|
||||
|
||||
return channel;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_of_iio_channel_get_by_name);
|
||||
|
||||
struct iio_channel *iio_channel_get_all(struct device *dev)
|
||||
{
|
||||
const char *name;
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
* TODO: gesture + proximity calib offsets
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
@ -1113,6 +1114,12 @@ static const struct i2c_device_id apds9960_id[] = {
|
|||
};
|
||||
MODULE_DEVICE_TABLE(i2c, apds9960_id);
|
||||
|
||||
static const struct acpi_device_id apds9960_acpi_match[] = {
|
||||
{ "MSHW0184" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, apds9960_acpi_match);
|
||||
|
||||
static const struct of_device_id apds9960_of_match[] = {
|
||||
{ .compatible = "avago,apds9960" },
|
||||
{ }
|
||||
|
@ -1124,6 +1131,7 @@ static struct i2c_driver apds9960_driver = {
|
|||
.name = APDS9960_DRV_NAME,
|
||||
.of_match_table = apds9960_of_match,
|
||||
.pm = &apds9960_pm_ops,
|
||||
.acpi_match_table = apds9960_acpi_match,
|
||||
},
|
||||
.probe = apds9960_probe,
|
||||
.remove = apds9960_remove,
|
||||
|
|
|
@ -22,15 +22,21 @@ enum {
|
|||
CHANNEL_SCAN_INDEX_MAX
|
||||
};
|
||||
|
||||
#define CHANNEL_SCAN_INDEX_TIMESTAMP CHANNEL_SCAN_INDEX_MAX
|
||||
|
||||
struct als_state {
|
||||
struct hid_sensor_hub_callbacks callbacks;
|
||||
struct hid_sensor_common common_attributes;
|
||||
struct hid_sensor_hub_attribute_info als_illum;
|
||||
u32 illum[CHANNEL_SCAN_INDEX_MAX];
|
||||
struct {
|
||||
u32 illum[CHANNEL_SCAN_INDEX_MAX];
|
||||
u64 timestamp __aligned(8);
|
||||
} scan;
|
||||
int scale_pre_decml;
|
||||
int scale_post_decml;
|
||||
int scale_precision;
|
||||
int value_offset;
|
||||
s64 timestamp;
|
||||
};
|
||||
|
||||
/* Channel definitions */
|
||||
|
@ -54,7 +60,8 @@ static const struct iio_chan_spec als_channels[] = {
|
|||
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
|
||||
BIT(IIO_CHAN_INFO_HYSTERESIS),
|
||||
.scan_index = CHANNEL_SCAN_INDEX_ILLUM,
|
||||
}
|
||||
},
|
||||
IIO_CHAN_SOFT_TIMESTAMP(CHANNEL_SCAN_INDEX_TIMESTAMP)
|
||||
};
|
||||
|
||||
/* Adjust channel real bits based on report descriptor */
|
||||
|
@ -168,14 +175,6 @@ static const struct iio_info als_info = {
|
|||
.write_raw = &als_write_raw,
|
||||
};
|
||||
|
||||
/* Function to push data to buffer */
|
||||
static void hid_sensor_push_data(struct iio_dev *indio_dev, const void *data,
|
||||
int len)
|
||||
{
|
||||
dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n");
|
||||
iio_push_to_buffers(indio_dev, data);
|
||||
}
|
||||
|
||||
/* Callback handler to send event after all samples are received and captured */
|
||||
static int als_proc_event(struct hid_sensor_hub_device *hsdev,
|
||||
unsigned usage_id,
|
||||
|
@ -185,10 +184,14 @@ static int als_proc_event(struct hid_sensor_hub_device *hsdev,
|
|||
struct als_state *als_state = iio_priv(indio_dev);
|
||||
|
||||
dev_dbg(&indio_dev->dev, "als_proc_event\n");
|
||||
if (atomic_read(&als_state->common_attributes.data_ready))
|
||||
hid_sensor_push_data(indio_dev,
|
||||
&als_state->illum,
|
||||
sizeof(als_state->illum));
|
||||
if (atomic_read(&als_state->common_attributes.data_ready)) {
|
||||
if (!als_state->timestamp)
|
||||
als_state->timestamp = iio_get_time_ns(indio_dev);
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, &als_state->scan,
|
||||
als_state->timestamp);
|
||||
als_state->timestamp = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -206,10 +209,14 @@ static int als_capture_sample(struct hid_sensor_hub_device *hsdev,
|
|||
|
||||
switch (usage_id) {
|
||||
case HID_USAGE_SENSOR_LIGHT_ILLUM:
|
||||
als_state->illum[CHANNEL_SCAN_INDEX_INTENSITY] = sample_data;
|
||||
als_state->illum[CHANNEL_SCAN_INDEX_ILLUM] = sample_data;
|
||||
als_state->scan.illum[CHANNEL_SCAN_INDEX_INTENSITY] = sample_data;
|
||||
als_state->scan.illum[CHANNEL_SCAN_INDEX_ILLUM] = sample_data;
|
||||
ret = 0;
|
||||
break;
|
||||
case HID_USAGE_SENSOR_TIME_TIMESTAMP:
|
||||
als_state->timestamp = hid_sensor_convert_timestamp(&als_state->common_attributes,
|
||||
*(s64 *)raw_data);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -285,7 +285,7 @@ static int tsl2583_get_lux(struct iio_dev *indio_dev)
|
|||
lux64 = lux64 * chip->als_settings.als_gain_trim;
|
||||
lux64 >>= 13;
|
||||
lux = lux64;
|
||||
lux = (lux + 500) / 1000;
|
||||
lux = DIV_ROUND_CLOSEST(lux, 1000);
|
||||
|
||||
if (lux > TSL2583_LUX_CALC_OVER_FLOW) { /* check for overflow */
|
||||
return_max:
|
||||
|
@ -361,12 +361,12 @@ static int tsl2583_set_als_time(struct tsl2583_chip *chip)
|
|||
u8 val;
|
||||
|
||||
/* determine als integration register */
|
||||
als_count = (chip->als_settings.als_time * 100 + 135) / 270;
|
||||
als_count = DIV_ROUND_CLOSEST(chip->als_settings.als_time * 100, 270);
|
||||
if (!als_count)
|
||||
als_count = 1; /* ensure at least one cycle */
|
||||
|
||||
/* convert back to time (encompasses overrides) */
|
||||
als_time = (als_count * 27 + 5) / 10;
|
||||
als_time = DIV_ROUND_CLOSEST(als_count * 27, 10);
|
||||
|
||||
val = 256 - als_count;
|
||||
ret = i2c_smbus_write_byte_data(chip->client,
|
||||
|
@ -380,7 +380,7 @@ static int tsl2583_set_als_time(struct tsl2583_chip *chip)
|
|||
|
||||
/* set chip struct re scaling and saturation */
|
||||
chip->als_saturation = als_count * 922; /* 90% of full scale */
|
||||
chip->als_time_scale = (als_time + 25) / 50;
|
||||
chip->als_time_scale = DIV_ROUND_CLOSEST(als_time, 50);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -392,7 +392,7 @@ static int vl6180_set_it(struct vl6180_data *data, int val, int val2)
|
|||
{
|
||||
int ret, it_ms;
|
||||
|
||||
it_ms = (val2 + 500) / 1000; /* round to ms */
|
||||
it_ms = DIV_ROUND_CLOSEST(val2, 1000); /* round to ms */
|
||||
if (val != 0 || it_ms < 1 || it_ms > 512)
|
||||
return -EINVAL;
|
||||
|
||||
|
|
|
@ -205,4 +205,19 @@ config SENSORS_RM3100_SPI
|
|||
To compile this driver as a module, choose M here: the module
|
||||
will be called rm3100-spi.
|
||||
|
||||
config YAMAHA_YAS530
|
||||
tristate "Yamaha YAS530 family of 3-Axis Magnetometers (I2C)"
|
||||
depends on I2C
|
||||
select REGMAP_I2C
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
Say Y here to add support for the Yamaha YAS530 series of
|
||||
3-Axis Magnetometers. Right now YAS530, YAS532 and YAS533 are
|
||||
fully supported.
|
||||
|
||||
This driver can also be compiled as a module.
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called yamaha-yas.
|
||||
|
||||
endmenu
|
||||
|
|
|
@ -28,3 +28,5 @@ obj-$(CONFIG_SENSORS_HMC5843_SPI) += hmc5843_spi.o
|
|||
obj-$(CONFIG_SENSORS_RM3100) += rm3100-core.o
|
||||
obj-$(CONFIG_SENSORS_RM3100_I2C) += rm3100-i2c.o
|
||||
obj-$(CONFIG_SENSORS_RM3100_SPI) += rm3100-spi.o
|
||||
|
||||
obj-$(CONFIG_YAMAHA_YAS530) += yamaha-yas530.o
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#include "bmc150_magn.h"
|
||||
|
||||
|
@ -135,6 +136,7 @@ struct bmc150_magn_data {
|
|||
*/
|
||||
struct mutex mutex;
|
||||
struct regmap *regmap;
|
||||
struct regulator_bulk_data regulators[2];
|
||||
struct iio_mount_matrix orientation;
|
||||
/* 4 x 32 bits for x, y z, 4 bytes align, 64 bits timestamp */
|
||||
s32 buffer[6];
|
||||
|
@ -692,12 +694,24 @@ static int bmc150_magn_init(struct bmc150_magn_data *data)
|
|||
int ret, chip_id;
|
||||
struct bmc150_magn_preset preset;
|
||||
|
||||
ret = regulator_bulk_enable(ARRAY_SIZE(data->regulators),
|
||||
data->regulators);
|
||||
if (ret < 0) {
|
||||
dev_err(data->dev, "Failed to enable regulators: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
/*
|
||||
* 3ms power-on time according to datasheet, let's better
|
||||
* be safe than sorry and set this delay to 5ms.
|
||||
*/
|
||||
msleep(5);
|
||||
|
||||
ret = bmc150_magn_set_power_mode(data, BMC150_MAGN_POWER_MODE_SUSPEND,
|
||||
false);
|
||||
if (ret < 0) {
|
||||
dev_err(data->dev,
|
||||
"Failed to bring up device from suspend mode\n");
|
||||
return ret;
|
||||
goto err_regulator_disable;
|
||||
}
|
||||
|
||||
ret = regmap_read(data->regmap, BMC150_MAGN_REG_CHIP_ID, &chip_id);
|
||||
|
@ -752,6 +766,8 @@ static int bmc150_magn_init(struct bmc150_magn_data *data)
|
|||
|
||||
err_poweroff:
|
||||
bmc150_magn_set_power_mode(data, BMC150_MAGN_POWER_MODE_SUSPEND, true);
|
||||
err_regulator_disable:
|
||||
regulator_bulk_disable(ARRAY_SIZE(data->regulators), data->regulators);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -867,6 +883,13 @@ int bmc150_magn_probe(struct device *dev, struct regmap *regmap,
|
|||
data->irq = irq;
|
||||
data->dev = dev;
|
||||
|
||||
data->regulators[0].supply = "vdd";
|
||||
data->regulators[1].supply = "vddio";
|
||||
ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(data->regulators),
|
||||
data->regulators);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "failed to get regulators\n");
|
||||
|
||||
ret = iio_read_mount_matrix(dev, "mount-matrix",
|
||||
&data->orientation);
|
||||
if (ret)
|
||||
|
@ -984,6 +1007,7 @@ int bmc150_magn_remove(struct device *dev)
|
|||
bmc150_magn_set_power_mode(data, BMC150_MAGN_POWER_MODE_SUSPEND, true);
|
||||
mutex_unlock(&data->mutex);
|
||||
|
||||
regulator_bulk_disable(ARRAY_SIZE(data->regulators), data->regulators);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(bmc150_magn_remove);
|
||||
|
|
|
@ -24,6 +24,7 @@ enum magn_3d_channel {
|
|||
CHANNEL_SCAN_INDEX_NORTH_TRUE_TILT_COMP,
|
||||
CHANNEL_SCAN_INDEX_NORTH_MAGN,
|
||||
CHANNEL_SCAN_INDEX_NORTH_TRUE,
|
||||
CHANNEL_SCAN_INDEX_TIMESTAMP,
|
||||
MAGN_3D_CHANNEL_MAX,
|
||||
};
|
||||
|
||||
|
@ -47,6 +48,7 @@ struct magn_3d_state {
|
|||
|
||||
struct common_attributes magn_flux_attr;
|
||||
struct common_attributes rot_attr;
|
||||
s64 timestamp;
|
||||
};
|
||||
|
||||
static const u32 magn_3d_addresses[MAGN_3D_CHANNEL_MAX] = {
|
||||
|
@ -57,6 +59,7 @@ static const u32 magn_3d_addresses[MAGN_3D_CHANNEL_MAX] = {
|
|||
HID_USAGE_SENSOR_ORIENT_COMP_TRUE_NORTH,
|
||||
HID_USAGE_SENSOR_ORIENT_MAGN_NORTH,
|
||||
HID_USAGE_SENSOR_ORIENT_TRUE_NORTH,
|
||||
HID_USAGE_SENSOR_TIME_TIMESTAMP,
|
||||
};
|
||||
|
||||
/* Channel definitions */
|
||||
|
@ -124,7 +127,8 @@ static const struct iio_chan_spec magn_3d_channels[] = {
|
|||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
|
||||
BIT(IIO_CHAN_INFO_HYSTERESIS),
|
||||
}
|
||||
},
|
||||
IIO_CHAN_SOFT_TIMESTAMP(7)
|
||||
};
|
||||
|
||||
/* Adjust channel real bits based on report descriptor */
|
||||
|
@ -273,13 +277,6 @@ static const struct iio_info magn_3d_info = {
|
|||
.write_raw = &magn_3d_write_raw,
|
||||
};
|
||||
|
||||
/* Function to push data to buffer */
|
||||
static void hid_sensor_push_data(struct iio_dev *indio_dev, const void *data)
|
||||
{
|
||||
dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n");
|
||||
iio_push_to_buffers(indio_dev, data);
|
||||
}
|
||||
|
||||
/* Callback handler to send event after all samples are received and captured */
|
||||
static int magn_3d_proc_event(struct hid_sensor_hub_device *hsdev,
|
||||
unsigned usage_id,
|
||||
|
@ -289,8 +286,15 @@ static int magn_3d_proc_event(struct hid_sensor_hub_device *hsdev,
|
|||
struct magn_3d_state *magn_state = iio_priv(indio_dev);
|
||||
|
||||
dev_dbg(&indio_dev->dev, "magn_3d_proc_event\n");
|
||||
if (atomic_read(&magn_state->magn_flux_attributes.data_ready))
|
||||
hid_sensor_push_data(indio_dev, magn_state->iio_vals);
|
||||
if (atomic_read(&magn_state->magn_flux_attributes.data_ready)) {
|
||||
if (!magn_state->timestamp)
|
||||
magn_state->timestamp = iio_get_time_ns(indio_dev);
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev,
|
||||
magn_state->iio_vals,
|
||||
magn_state->timestamp);
|
||||
magn_state->timestamp = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -321,6 +325,11 @@ static int magn_3d_capture_sample(struct hid_sensor_hub_device *hsdev,
|
|||
offset = (usage_id - HID_USAGE_SENSOR_ORIENT_COMP_MAGN_NORTH)
|
||||
+ CHANNEL_SCAN_INDEX_NORTH_MAGN_TILT_COMP;
|
||||
break;
|
||||
case HID_USAGE_SENSOR_TIME_TIMESTAMP:
|
||||
magn_state->timestamp =
|
||||
hid_sensor_convert_timestamp(&magn_state->magn_flux_attributes,
|
||||
*(s64 *)raw_data);
|
||||
return ret;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -386,9 +395,10 @@ static int magn_3d_parse_report(struct platform_device *pdev,
|
|||
return -ENOMEM;
|
||||
}
|
||||
|
||||
st->iio_vals = devm_kcalloc(&pdev->dev, attr_count,
|
||||
sizeof(u32),
|
||||
GFP_KERNEL);
|
||||
/* attr_count include timestamp channel, and the iio_vals should be aligned to 8byte */
|
||||
st->iio_vals = devm_kcalloc(&pdev->dev,
|
||||
((attr_count + 1) % 2 + (attr_count + 1) / 2) * 2,
|
||||
sizeof(u32), GFP_KERNEL);
|
||||
if (!st->iio_vals) {
|
||||
dev_err(&pdev->dev,
|
||||
"failed to allocate space for iio values array\n");
|
||||
|
@ -404,11 +414,13 @@ static int magn_3d_parse_report(struct platform_device *pdev,
|
|||
(_channels[*chan_count]).scan_index = *chan_count;
|
||||
(_channels[*chan_count]).address = i;
|
||||
|
||||
/* Set magn_val_addr to iio value address */
|
||||
st->magn_val_addr[i] = &(st->iio_vals[*chan_count]);
|
||||
magn_3d_adjust_channel_bit_mask(_channels,
|
||||
*chan_count,
|
||||
st->magn[i].size);
|
||||
if (i != CHANNEL_SCAN_INDEX_TIMESTAMP) {
|
||||
/* Set magn_val_addr to iio value address */
|
||||
st->magn_val_addr[i] = &st->iio_vals[*chan_count];
|
||||
magn_3d_adjust_channel_bit_mask(_channels,
|
||||
*chan_count,
|
||||
st->magn[i].size);
|
||||
}
|
||||
(*chan_count)++;
|
||||
}
|
||||
}
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -24,15 +24,21 @@ enum incl_3d_channel {
|
|||
INCLI_3D_CHANNEL_MAX,
|
||||
};
|
||||
|
||||
#define CHANNEL_SCAN_INDEX_TIMESTAMP INCLI_3D_CHANNEL_MAX
|
||||
|
||||
struct incl_3d_state {
|
||||
struct hid_sensor_hub_callbacks callbacks;
|
||||
struct hid_sensor_common common_attributes;
|
||||
struct hid_sensor_hub_attribute_info incl[INCLI_3D_CHANNEL_MAX];
|
||||
u32 incl_val[INCLI_3D_CHANNEL_MAX];
|
||||
struct {
|
||||
u32 incl_val[INCLI_3D_CHANNEL_MAX];
|
||||
u64 timestamp __aligned(8);
|
||||
} scan;
|
||||
int scale_pre_decml;
|
||||
int scale_post_decml;
|
||||
int scale_precision;
|
||||
int value_offset;
|
||||
s64 timestamp;
|
||||
};
|
||||
|
||||
static const u32 incl_3d_addresses[INCLI_3D_CHANNEL_MAX] = {
|
||||
|
@ -73,7 +79,8 @@ static const struct iio_chan_spec incl_3d_channels[] = {
|
|||
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
|
||||
BIT(IIO_CHAN_INFO_HYSTERESIS),
|
||||
.scan_index = CHANNEL_SCAN_INDEX_Z,
|
||||
}
|
||||
},
|
||||
IIO_CHAN_SOFT_TIMESTAMP(CHANNEL_SCAN_INDEX_TIMESTAMP),
|
||||
};
|
||||
|
||||
/* Adjust channel real bits based on report descriptor */
|
||||
|
@ -178,13 +185,6 @@ static const struct iio_info incl_3d_info = {
|
|||
.write_raw = &incl_3d_write_raw,
|
||||
};
|
||||
|
||||
/* Function to push data to buffer */
|
||||
static void hid_sensor_push_data(struct iio_dev *indio_dev, u8 *data, int len)
|
||||
{
|
||||
dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n");
|
||||
iio_push_to_buffers(indio_dev, (u8 *)data);
|
||||
}
|
||||
|
||||
/* Callback handler to send event after all samples are received and captured */
|
||||
static int incl_3d_proc_event(struct hid_sensor_hub_device *hsdev,
|
||||
unsigned usage_id,
|
||||
|
@ -194,10 +194,16 @@ static int incl_3d_proc_event(struct hid_sensor_hub_device *hsdev,
|
|||
struct incl_3d_state *incl_state = iio_priv(indio_dev);
|
||||
|
||||
dev_dbg(&indio_dev->dev, "incl_3d_proc_event\n");
|
||||
if (atomic_read(&incl_state->common_attributes.data_ready))
|
||||
hid_sensor_push_data(indio_dev,
|
||||
(u8 *)incl_state->incl_val,
|
||||
sizeof(incl_state->incl_val));
|
||||
if (atomic_read(&incl_state->common_attributes.data_ready)) {
|
||||
if (!incl_state->timestamp)
|
||||
incl_state->timestamp = iio_get_time_ns(indio_dev);
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev,
|
||||
&incl_state->scan,
|
||||
incl_state->timestamp);
|
||||
|
||||
incl_state->timestamp = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -214,13 +220,18 @@ static int incl_3d_capture_sample(struct hid_sensor_hub_device *hsdev,
|
|||
|
||||
switch (usage_id) {
|
||||
case HID_USAGE_SENSOR_ORIENT_TILT_X:
|
||||
incl_state->incl_val[CHANNEL_SCAN_INDEX_X] = *(u32 *)raw_data;
|
||||
incl_state->scan.incl_val[CHANNEL_SCAN_INDEX_X] = *(u32 *)raw_data;
|
||||
break;
|
||||
case HID_USAGE_SENSOR_ORIENT_TILT_Y:
|
||||
incl_state->incl_val[CHANNEL_SCAN_INDEX_Y] = *(u32 *)raw_data;
|
||||
incl_state->scan.incl_val[CHANNEL_SCAN_INDEX_Y] = *(u32 *)raw_data;
|
||||
break;
|
||||
case HID_USAGE_SENSOR_ORIENT_TILT_Z:
|
||||
incl_state->incl_val[CHANNEL_SCAN_INDEX_Z] = *(u32 *)raw_data;
|
||||
incl_state->scan.incl_val[CHANNEL_SCAN_INDEX_Z] = *(u32 *)raw_data;
|
||||
break;
|
||||
case HID_USAGE_SENSOR_TIME_TIMESTAMP:
|
||||
incl_state->timestamp =
|
||||
hid_sensor_convert_timestamp(&incl_state->common_attributes,
|
||||
*(s64 *)raw_data);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
|
|
|
@ -20,11 +20,15 @@ struct dev_rot_state {
|
|||
struct hid_sensor_hub_callbacks callbacks;
|
||||
struct hid_sensor_common common_attributes;
|
||||
struct hid_sensor_hub_attribute_info quaternion;
|
||||
u32 sampled_vals[4];
|
||||
struct {
|
||||
u32 sampled_vals[4] __aligned(16);
|
||||
u64 timestamp __aligned(8);
|
||||
} scan;
|
||||
int scale_pre_decml;
|
||||
int scale_post_decml;
|
||||
int scale_precision;
|
||||
int value_offset;
|
||||
s64 timestamp;
|
||||
};
|
||||
|
||||
/* Channel definitions */
|
||||
|
@ -37,8 +41,10 @@ static const struct iio_chan_spec dev_rot_channels[] = {
|
|||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) |
|
||||
BIT(IIO_CHAN_INFO_OFFSET) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_HYSTERESIS)
|
||||
}
|
||||
BIT(IIO_CHAN_INFO_HYSTERESIS),
|
||||
.scan_index = 0
|
||||
},
|
||||
IIO_CHAN_SOFT_TIMESTAMP(1)
|
||||
};
|
||||
|
||||
/* Adjust channel real bits based on report descriptor */
|
||||
|
@ -70,7 +76,7 @@ static int dev_rot_read_raw(struct iio_dev *indio_dev,
|
|||
case IIO_CHAN_INFO_RAW:
|
||||
if (size >= 4) {
|
||||
for (i = 0; i < 4; ++i)
|
||||
vals[i] = rot_state->sampled_vals[i];
|
||||
vals[i] = rot_state->scan.sampled_vals[i];
|
||||
ret_type = IIO_VAL_INT_MULTIPLE;
|
||||
*val_len = 4;
|
||||
} else
|
||||
|
@ -132,15 +138,6 @@ static const struct iio_info dev_rot_info = {
|
|||
.write_raw = &dev_rot_write_raw,
|
||||
};
|
||||
|
||||
/* Function to push data to buffer */
|
||||
static void hid_sensor_push_data(struct iio_dev *indio_dev, u8 *data, int len)
|
||||
{
|
||||
dev_dbg(&indio_dev->dev, "hid_sensor_push_data >>\n");
|
||||
iio_push_to_buffers(indio_dev, (u8 *)data);
|
||||
dev_dbg(&indio_dev->dev, "hid_sensor_push_data <<\n");
|
||||
|
||||
}
|
||||
|
||||
/* Callback handler to send event after all samples are received and captured */
|
||||
static int dev_rot_proc_event(struct hid_sensor_hub_device *hsdev,
|
||||
unsigned usage_id,
|
||||
|
@ -150,10 +147,15 @@ static int dev_rot_proc_event(struct hid_sensor_hub_device *hsdev,
|
|||
struct dev_rot_state *rot_state = iio_priv(indio_dev);
|
||||
|
||||
dev_dbg(&indio_dev->dev, "dev_rot_proc_event\n");
|
||||
if (atomic_read(&rot_state->common_attributes.data_ready))
|
||||
hid_sensor_push_data(indio_dev,
|
||||
(u8 *)rot_state->sampled_vals,
|
||||
sizeof(rot_state->sampled_vals));
|
||||
if (atomic_read(&rot_state->common_attributes.data_ready)) {
|
||||
if (!rot_state->timestamp)
|
||||
rot_state->timestamp = iio_get_time_ns(indio_dev);
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, &rot_state->scan,
|
||||
rot_state->timestamp);
|
||||
|
||||
rot_state->timestamp = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -168,10 +170,14 @@ static int dev_rot_capture_sample(struct hid_sensor_hub_device *hsdev,
|
|||
struct dev_rot_state *rot_state = iio_priv(indio_dev);
|
||||
|
||||
if (usage_id == HID_USAGE_SENSOR_ORIENT_QUATERNION) {
|
||||
memcpy(rot_state->sampled_vals, raw_data,
|
||||
sizeof(rot_state->sampled_vals));
|
||||
memcpy(&rot_state->scan.sampled_vals, raw_data,
|
||||
sizeof(rot_state->scan.sampled_vals));
|
||||
|
||||
dev_dbg(&indio_dev->dev, "Recd Quat len:%zu::%zu\n", raw_len,
|
||||
sizeof(rot_state->sampled_vals));
|
||||
sizeof(rot_state->scan.sampled_vals));
|
||||
} else if (usage_id == HID_USAGE_SENSOR_TIME_TIMESTAMP) {
|
||||
rot_state->timestamp = hid_sensor_convert_timestamp(&rot_state->common_attributes,
|
||||
*(s64 *)raw_data);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -16,4 +16,20 @@ config IQS624_POS
|
|||
To compile this driver as a module, choose M here: the module
|
||||
will be called iqs624-pos.
|
||||
|
||||
config HID_SENSOR_CUSTOM_INTEL_HINGE
|
||||
depends on HID_SENSOR_HUB
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
select HID_SENSOR_IIO_COMMON
|
||||
select HID_SENSOR_IIO_TRIGGER
|
||||
tristate "HID Hinge"
|
||||
help
|
||||
This sensor present three angles, hinge angel, screen angles
|
||||
and keyboard angle respect to horizon (ground).
|
||||
Say yes here to build support for the HID custom
|
||||
intel hinge sensor.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called hid-sensor-custom-hinge.
|
||||
|
||||
endmenu
|
||||
|
|
|
@ -4,4 +4,5 @@
|
|||
|
||||
# When adding new entries keep the list in alphabetical order
|
||||
|
||||
obj-$(CONFIG_HID_SENSOR_CUSTOM_INTEL_HINGE) += hid-sensor-custom-intel-hinge.o
|
||||
obj-$(CONFIG_IQS624_POS) += iqs624-pos.o
|
||||
|
|
|
@ -0,0 +1,385 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* HID Sensors Driver
|
||||
* Copyright (c) 2020, Intel Corporation.
|
||||
*/
|
||||
#include <linux/hid-sensor-hub.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include "../common/hid-sensors/hid-sensor-trigger.h"
|
||||
|
||||
enum hinge_channel {
|
||||
CHANNEL_SCAN_INDEX_HINGE_ANGLE,
|
||||
CHANNEL_SCAN_INDEX_SCREEN_ANGLE,
|
||||
CHANNEL_SCAN_INDEX_KEYBOARD_ANGLE,
|
||||
CHANNEL_SCAN_INDEX_MAX,
|
||||
};
|
||||
|
||||
#define CHANNEL_SCAN_INDEX_TIMESTAMP CHANNEL_SCAN_INDEX_MAX
|
||||
|
||||
static const u32 hinge_addresses[CHANNEL_SCAN_INDEX_MAX] = {
|
||||
HID_USAGE_SENSOR_DATA_FIELD_CUSTOM_VALUE(1),
|
||||
HID_USAGE_SENSOR_DATA_FIELD_CUSTOM_VALUE(2),
|
||||
HID_USAGE_SENSOR_DATA_FIELD_CUSTOM_VALUE(3)
|
||||
};
|
||||
|
||||
static const char *const hinge_labels[CHANNEL_SCAN_INDEX_MAX] = { "hinge",
|
||||
"screen",
|
||||
"keyboard" };
|
||||
|
||||
struct hinge_state {
|
||||
struct iio_dev *indio_dev;
|
||||
struct hid_sensor_hub_attribute_info hinge[CHANNEL_SCAN_INDEX_MAX];
|
||||
struct hid_sensor_hub_callbacks callbacks;
|
||||
struct hid_sensor_common common_attributes;
|
||||
const char *labels[CHANNEL_SCAN_INDEX_MAX];
|
||||
struct {
|
||||
u32 hinge_val[3];
|
||||
u64 timestamp __aligned(8);
|
||||
} scan;
|
||||
|
||||
int scale_pre_decml;
|
||||
int scale_post_decml;
|
||||
int scale_precision;
|
||||
int value_offset;
|
||||
u64 timestamp;
|
||||
};
|
||||
|
||||
/* Channel definitions */
|
||||
static const struct iio_chan_spec hinge_channels[] = {
|
||||
{
|
||||
.type = IIO_ANGL,
|
||||
.indexed = 1,
|
||||
.channel = 0,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.info_mask_shared_by_type =
|
||||
BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ) | BIT(IIO_CHAN_INFO_HYSTERESIS),
|
||||
.scan_index = CHANNEL_SCAN_INDEX_HINGE_ANGLE,
|
||||
.scan_type = {
|
||||
.sign = 's',
|
||||
.storagebits = 32,
|
||||
},
|
||||
}, {
|
||||
.type = IIO_ANGL,
|
||||
.indexed = 1,
|
||||
.channel = 1,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.info_mask_shared_by_type =
|
||||
BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ) | BIT(IIO_CHAN_INFO_HYSTERESIS),
|
||||
.scan_index = CHANNEL_SCAN_INDEX_SCREEN_ANGLE,
|
||||
.scan_type = {
|
||||
.sign = 's',
|
||||
.storagebits = 32,
|
||||
},
|
||||
}, {
|
||||
.type = IIO_ANGL,
|
||||
.indexed = 1,
|
||||
.channel = 2,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.info_mask_shared_by_type =
|
||||
BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ) | BIT(IIO_CHAN_INFO_HYSTERESIS),
|
||||
.scan_index = CHANNEL_SCAN_INDEX_KEYBOARD_ANGLE,
|
||||
.scan_type = {
|
||||
.sign = 's',
|
||||
.storagebits = 32,
|
||||
},
|
||||
},
|
||||
IIO_CHAN_SOFT_TIMESTAMP(CHANNEL_SCAN_INDEX_TIMESTAMP)
|
||||
};
|
||||
|
||||
/* Adjust channel real bits based on report descriptor */
|
||||
static void hinge_adjust_channel_realbits(struct iio_chan_spec *channels,
|
||||
int channel, int size)
|
||||
{
|
||||
channels[channel].scan_type.realbits = size * 8;
|
||||
}
|
||||
|
||||
/* Channel read_raw handler */
|
||||
static int hinge_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val, int *val2,
|
||||
long mask)
|
||||
{
|
||||
struct hinge_state *st = iio_priv(indio_dev);
|
||||
struct hid_sensor_hub_device *hsdev;
|
||||
int report_id;
|
||||
s32 min;
|
||||
|
||||
hsdev = st->common_attributes.hsdev;
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
hid_sensor_power_state(&st->common_attributes, true);
|
||||
report_id = st->hinge[chan->scan_index].report_id;
|
||||
min = st->hinge[chan->scan_index].logical_minimum;
|
||||
if (report_id < 0) {
|
||||
hid_sensor_power_state(&st->common_attributes, false);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*val = sensor_hub_input_attr_get_raw_value(st->common_attributes.hsdev,
|
||||
hsdev->usage,
|
||||
hinge_addresses[chan->scan_index],
|
||||
report_id,
|
||||
SENSOR_HUB_SYNC, min < 0);
|
||||
|
||||
hid_sensor_power_state(&st->common_attributes, false);
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = st->scale_pre_decml;
|
||||
*val2 = st->scale_post_decml;
|
||||
return st->scale_precision;
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
*val = st->value_offset;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
return hid_sensor_read_samp_freq_value(&st->common_attributes,
|
||||
val, val2);
|
||||
case IIO_CHAN_INFO_HYSTERESIS:
|
||||
return hid_sensor_read_raw_hyst_value(&st->common_attributes,
|
||||
val, val2);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Channel write_raw handler */
|
||||
static int hinge_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int val, int val2,
|
||||
long mask)
|
||||
{
|
||||
struct hinge_state *st = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
return hid_sensor_write_samp_freq_value(&st->common_attributes,
|
||||
val, val2);
|
||||
case IIO_CHAN_INFO_HYSTERESIS:
|
||||
return hid_sensor_write_raw_hyst_value(&st->common_attributes,
|
||||
val, val2);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int hinge_read_label(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, char *label)
|
||||
{
|
||||
struct hinge_state *st = iio_priv(indio_dev);
|
||||
|
||||
return sprintf(label, "%s\n", st->labels[chan->channel]);
|
||||
}
|
||||
|
||||
static const struct iio_info hinge_info = {
|
||||
.read_raw = hinge_read_raw,
|
||||
.write_raw = hinge_write_raw,
|
||||
.read_label = hinge_read_label,
|
||||
};
|
||||
|
||||
/*
|
||||
* Callback handler to send event after all samples are received
|
||||
* and captured.
|
||||
*/
|
||||
static int hinge_proc_event(struct hid_sensor_hub_device *hsdev,
|
||||
unsigned int usage_id, void *priv)
|
||||
{
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(priv);
|
||||
struct hinge_state *st = iio_priv(indio_dev);
|
||||
|
||||
if (atomic_read(&st->common_attributes.data_ready)) {
|
||||
if (!st->timestamp)
|
||||
st->timestamp = iio_get_time_ns(indio_dev);
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, &st->scan,
|
||||
st->timestamp);
|
||||
|
||||
st->timestamp = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Capture samples in local storage */
|
||||
static int hinge_capture_sample(struct hid_sensor_hub_device *hsdev,
|
||||
unsigned int usage_id, size_t raw_len,
|
||||
char *raw_data, void *priv)
|
||||
{
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(priv);
|
||||
struct hinge_state *st = iio_priv(indio_dev);
|
||||
int offset;
|
||||
|
||||
switch (usage_id) {
|
||||
case HID_USAGE_SENSOR_DATA_FIELD_CUSTOM_VALUE(1):
|
||||
case HID_USAGE_SENSOR_DATA_FIELD_CUSTOM_VALUE(2):
|
||||
case HID_USAGE_SENSOR_DATA_FIELD_CUSTOM_VALUE(3):
|
||||
offset = usage_id - HID_USAGE_SENSOR_DATA_FIELD_CUSTOM_VALUE(1);
|
||||
st->scan.hinge_val[offset] = *(u32 *)raw_data;
|
||||
return 0;
|
||||
case HID_USAGE_SENSOR_TIME_TIMESTAMP:
|
||||
st->timestamp = hid_sensor_convert_timestamp(&st->common_attributes,
|
||||
*(int64_t *)raw_data);
|
||||
return 0;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Parse report which is specific to an usage id */
|
||||
static int hinge_parse_report(struct platform_device *pdev,
|
||||
struct hid_sensor_hub_device *hsdev,
|
||||
struct iio_chan_spec *channels,
|
||||
unsigned int usage_id, struct hinge_state *st)
|
||||
{
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < CHANNEL_SCAN_INDEX_MAX; ++i) {
|
||||
ret = sensor_hub_input_get_attribute_info(hsdev,
|
||||
HID_INPUT_REPORT,
|
||||
usage_id,
|
||||
hinge_addresses[i],
|
||||
&st->hinge[i]);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
hinge_adjust_channel_realbits(channels, i, st->hinge[i].size);
|
||||
}
|
||||
|
||||
st->scale_precision = hid_sensor_format_scale(HID_USAGE_SENSOR_HINGE,
|
||||
&st->hinge[CHANNEL_SCAN_INDEX_HINGE_ANGLE],
|
||||
&st->scale_pre_decml, &st->scale_post_decml);
|
||||
|
||||
/* Set Sensitivity field ids, when there is no individual modifier */
|
||||
if (st->common_attributes.sensitivity.index < 0) {
|
||||
sensor_hub_input_get_attribute_info(hsdev,
|
||||
HID_FEATURE_REPORT, usage_id,
|
||||
HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS |
|
||||
HID_USAGE_SENSOR_DATA_FIELD_CUSTOM_VALUE(1),
|
||||
&st->common_attributes.sensitivity);
|
||||
dev_dbg(&pdev->dev, "Sensitivity index:report %d:%d\n",
|
||||
st->common_attributes.sensitivity.index,
|
||||
st->common_attributes.sensitivity.report_id);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Function to initialize the processing for usage id */
|
||||
static int hid_hinge_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct hinge_state *st;
|
||||
struct iio_dev *indio_dev;
|
||||
struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*st));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, indio_dev);
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
st->common_attributes.hsdev = hsdev;
|
||||
st->common_attributes.pdev = pdev;
|
||||
st->indio_dev = indio_dev;
|
||||
for (i = 0; i < CHANNEL_SCAN_INDEX_MAX; i++)
|
||||
st->labels[i] = hinge_labels[i];
|
||||
|
||||
ret = hid_sensor_parse_common_attributes(hsdev, hsdev->usage,
|
||||
&st->common_attributes);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to setup common attributes\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
indio_dev->num_channels = ARRAY_SIZE(hinge_channels);
|
||||
indio_dev->channels = devm_kmemdup(&indio_dev->dev, hinge_channels,
|
||||
sizeof(hinge_channels), GFP_KERNEL);
|
||||
if (!indio_dev->channels)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = hinge_parse_report(pdev, hsdev,
|
||||
(struct iio_chan_spec *)indio_dev->channels,
|
||||
hsdev->usage, st);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to setup attributes\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
indio_dev->dev.parent = &pdev->dev;
|
||||
indio_dev->info = &hinge_info;
|
||||
indio_dev->name = "hinge";
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
atomic_set(&st->common_attributes.data_ready, 0);
|
||||
ret = hid_sensor_setup_trigger(indio_dev, indio_dev->name,
|
||||
&st->common_attributes);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "trigger setup failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
st->callbacks.send_event = hinge_proc_event;
|
||||
st->callbacks.capture_sample = hinge_capture_sample;
|
||||
st->callbacks.pdev = pdev;
|
||||
ret = sensor_hub_register_callback(hsdev, hsdev->usage, &st->callbacks);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "callback reg failed\n");
|
||||
goto error_remove_trigger;
|
||||
}
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "device register failed\n");
|
||||
goto error_remove_callback;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
error_remove_callback:
|
||||
sensor_hub_remove_callback(hsdev, hsdev->usage);
|
||||
error_remove_trigger:
|
||||
hid_sensor_remove_trigger(indio_dev, &st->common_attributes);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Function to deinitialize the processing for usage id */
|
||||
static int hid_hinge_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
struct hinge_state *st = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
sensor_hub_remove_callback(hsdev, hsdev->usage);
|
||||
hid_sensor_remove_trigger(indio_dev, &st->common_attributes);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct platform_device_id hid_hinge_ids[] = {
|
||||
{
|
||||
/* Format: HID-SENSOR-INT-usage_id_in_hex_lowercase */
|
||||
.name = "HID-SENSOR-INT-020b",
|
||||
},
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, hid_hinge_ids);
|
||||
|
||||
static struct platform_driver hid_hinge_platform_driver = {
|
||||
.id_table = hid_hinge_ids,
|
||||
.driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
.pm = &hid_sensor_pm_ops,
|
||||
},
|
||||
.probe = hid_hinge_probe,
|
||||
.remove = hid_hinge_remove,
|
||||
};
|
||||
module_platform_driver(hid_hinge_platform_driver);
|
||||
|
||||
MODULE_DESCRIPTION("HID Sensor INTEL Hinge");
|
||||
MODULE_AUTHOR("Ye Xiang <xiang.ye@intel.com>");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -30,9 +30,25 @@
|
|||
|
||||
#include "../common/ms_sensors/ms_sensors_i2c.h"
|
||||
|
||||
struct ms_tp_data {
|
||||
const char *name;
|
||||
const struct ms_tp_hw_data *hw;
|
||||
};
|
||||
|
||||
static const int ms5637_samp_freq[6] = { 960, 480, 240, 120, 60, 30 };
|
||||
/* String copy of the above const for readability purpose */
|
||||
static const char ms5637_show_samp_freq[] = "960 480 240 120 60 30";
|
||||
|
||||
static ssize_t ms5637_show_samp_freq(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct ms_tp_dev *dev_data = iio_priv(indio_dev);
|
||||
int i, len = 0;
|
||||
|
||||
for (i = 0; i <= dev_data->hw->max_res_index; i++)
|
||||
len += sysfs_emit_at(buf, len, "%u ", ms5637_samp_freq[i]);
|
||||
sysfs_emit_at(buf, len - 1, "\n");
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int ms5637_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *channel, int *val,
|
||||
|
@ -109,10 +125,10 @@ static const struct iio_chan_spec ms5637_channels[] = {
|
|||
}
|
||||
};
|
||||
|
||||
static IIO_CONST_ATTR_SAMP_FREQ_AVAIL(ms5637_show_samp_freq);
|
||||
static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(ms5637_show_samp_freq);
|
||||
|
||||
static struct attribute *ms5637_attributes[] = {
|
||||
&iio_const_attr_sampling_frequency_available.dev_attr.attr,
|
||||
&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
@ -129,6 +145,7 @@ static const struct iio_info ms5637_info = {
|
|||
static int ms5637_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
const struct ms_tp_data *data;
|
||||
struct ms_tp_dev *dev_data;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret;
|
||||
|
@ -142,17 +159,25 @@ static int ms5637_probe(struct i2c_client *client,
|
|||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
if (id)
|
||||
data = (const struct ms_tp_data *)id->driver_data;
|
||||
else
|
||||
data = device_get_match_data(&client->dev);
|
||||
if (!data)
|
||||
return -EINVAL;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*dev_data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_data = iio_priv(indio_dev);
|
||||
dev_data->client = client;
|
||||
dev_data->res_index = 5;
|
||||
dev_data->res_index = data->hw->max_res_index;
|
||||
dev_data->hw = data->hw;
|
||||
mutex_init(&dev_data->lock);
|
||||
|
||||
indio_dev->info = &ms5637_info;
|
||||
indio_dev->name = id->name;
|
||||
indio_dev->name = data->name;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = ms5637_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(ms5637_channels);
|
||||
|
@ -170,20 +195,44 @@ static int ms5637_probe(struct i2c_client *client,
|
|||
return devm_iio_device_register(&client->dev, indio_dev);
|
||||
}
|
||||
|
||||
static const struct ms_tp_hw_data ms5637_hw_data = {
|
||||
.prom_len = 7,
|
||||
.max_res_index = 5
|
||||
};
|
||||
|
||||
static const struct ms_tp_hw_data ms5803_hw_data = {
|
||||
.prom_len = 8,
|
||||
.max_res_index = 4
|
||||
};
|
||||
|
||||
static const struct ms_tp_data ms5637_data = { .name = "ms5637", .hw = &ms5637_hw_data };
|
||||
|
||||
static const struct ms_tp_data ms5803_data = { .name = "ms5803", .hw = &ms5803_hw_data };
|
||||
|
||||
static const struct ms_tp_data ms5805_data = { .name = "ms5805", .hw = &ms5637_hw_data };
|
||||
|
||||
static const struct ms_tp_data ms5837_data = { .name = "ms5837", .hw = &ms5637_hw_data };
|
||||
|
||||
static const struct ms_tp_data ms8607_data = {
|
||||
.name = "ms8607-temppressure",
|
||||
.hw = &ms5637_hw_data,
|
||||
};
|
||||
|
||||
static const struct i2c_device_id ms5637_id[] = {
|
||||
{"ms5637", 0},
|
||||
{"ms5805", 0},
|
||||
{"ms5837", 0},
|
||||
{"ms8607-temppressure", 0},
|
||||
{"ms5637", (kernel_ulong_t)&ms5637_data },
|
||||
{"ms5805", (kernel_ulong_t)&ms5805_data },
|
||||
{"ms5837", (kernel_ulong_t)&ms5837_data },
|
||||
{"ms8607-temppressure", (kernel_ulong_t)&ms8607_data },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ms5637_id);
|
||||
|
||||
static const struct of_device_id ms5637_of_match[] = {
|
||||
{ .compatible = "meas,ms5637", },
|
||||
{ .compatible = "meas,ms5805", },
|
||||
{ .compatible = "meas,ms5837", },
|
||||
{ .compatible = "meas,ms8607-temppressure", },
|
||||
{ .compatible = "meas,ms5637", .data = &ms5637_data },
|
||||
{ .compatible = "meas,ms5803", .data = &ms5803_data },
|
||||
{ .compatible = "meas,ms5805", .data = &ms5805_data },
|
||||
{ .compatible = "meas,ms5837", .data = &ms5837_data },
|
||||
{ .compatible = "meas,ms8607-temppressure", .data = &ms8607_data },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ms5637_of_match);
|
||||
|
|
|
@ -141,4 +141,23 @@ static inline s32 fixp_sin32_rad(u32 radians, u32 twopi)
|
|||
#define fixp_cos32_rad(rad, twopi) \
|
||||
fixp_sin32_rad(rad + twopi / 4, twopi)
|
||||
|
||||
/**
|
||||
* fixp_linear_interpolate() - interpolates a value from two known points
|
||||
*
|
||||
* @x0: x value of point 0
|
||||
* @y0: y value of point 0
|
||||
* @x1: x value of point 1
|
||||
* @y1: y value of point 1
|
||||
* @x: the linear interpolant
|
||||
*/
|
||||
static inline int fixp_linear_interpolate(int x0, int y0, int x1, int y1, int x)
|
||||
{
|
||||
if (y0 == y1 || x == x0)
|
||||
return y0;
|
||||
if (x1 == x0 || x == x1)
|
||||
return y1;
|
||||
|
||||
return y0 + ((y1 - y0) * (x - x0) / (x1 - x0));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -128,6 +128,10 @@
|
|||
#define HID_USAGE_SENSOR_UNITS_DEGREES_PER_SECOND 0x15
|
||||
|
||||
/* Common selectors */
|
||||
#define HID_USAGE_SENSOR_PROP_DESC 0x200300
|
||||
#define HID_USAGE_SENSOR_PROP_FRIENDLY_NAME 0x200301
|
||||
#define HID_USAGE_SENSOR_PROP_SERIAL_NUM 0x200307
|
||||
#define HID_USAGE_SENSOR_PROP_MANUFACTURER 0x200305
|
||||
#define HID_USAGE_SENSOR_PROP_REPORT_INTERVAL 0x20030E
|
||||
#define HID_USAGE_SENSOR_PROP_SENSITIVITY_ABS 0x20030F
|
||||
#define HID_USAGE_SENSOR_PROP_SENSITIVITY_RANGE_PCT 0x200310
|
||||
|
@ -158,4 +162,14 @@
|
|||
#define HID_USAGE_SENSOR_PROP_REPORTING_STATE_NO_EVENTS_ENUM 0x200840
|
||||
#define HID_USAGE_SENSOR_PROP_REPORTING_STATE_ALL_EVENTS_ENUM 0x200841
|
||||
|
||||
/* Custom Sensor (2000e1) */
|
||||
#define HID_USAGE_SENSOR_HINGE 0x20020B
|
||||
#define HID_USAGE_SENSOR_DATA_FIELD_LOCATION 0x200400
|
||||
#define HID_USAGE_SENSOR_DATA_FIELE_TIME_SINCE_SYS_BOOT 0x20052B
|
||||
#define HID_USAGE_SENSOR_DATA_FIELD_CUSTOM_USAGE 0x200541
|
||||
#define HID_USAGE_SENSOR_DATA_FIELD_CUSTOM_VALUE_BASE 0x200543
|
||||
/* Custom Sensor data 28=>x>=0 */
|
||||
#define HID_USAGE_SENSOR_DATA_FIELD_CUSTOM_VALUE(x) \
|
||||
(HID_USAGE_SENSOR_DATA_FIELD_CUSTOM_VALUE_BASE + (x))
|
||||
|
||||
#endif
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
#ifndef QCOM_VADC_COMMON_H
|
||||
#define QCOM_VADC_COMMON_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
#define VADC_CONV_TIME_MIN_US 2000
|
||||
#define VADC_CONV_TIME_MAX_US 2100
|
||||
|
||||
|
@ -52,22 +54,6 @@
|
|||
#define R_PU_100K 100000
|
||||
#define RATIO_MAX_ADC7 BIT(14)
|
||||
|
||||
#define DIE_TEMP_ADC7_SCALE_1 -60000
|
||||
#define DIE_TEMP_ADC7_SCALE_2 20000
|
||||
#define DIE_TEMP_ADC7_SCALE_FACTOR 1000
|
||||
#define DIE_TEMP_ADC7_MAX 160000
|
||||
|
||||
/**
|
||||
* struct vadc_map_pt - Map the graph representation for ADC channel
|
||||
* @x: Represent the ADC digitized code.
|
||||
* @y: Represent the physical data which can be temperature, voltage,
|
||||
* resistance.
|
||||
*/
|
||||
struct vadc_map_pt {
|
||||
s32 x;
|
||||
s32 y;
|
||||
};
|
||||
|
||||
/*
|
||||
* VADC_CALIB_ABSOLUTE: uses the 625mV and 1.25V as reference channels.
|
||||
* VADC_CALIB_RATIOMETRIC: uses the reference voltage (1.8V) and GND for
|
||||
|
@ -168,10 +154,18 @@ struct qcom_adc5_scale_type {
|
|||
};
|
||||
|
||||
int qcom_adc5_hw_scale(enum vadc_scale_fn_type scaletype,
|
||||
const struct vadc_prescale_ratio *prescale,
|
||||
unsigned int prescale_ratio,
|
||||
const struct adc5_data *data,
|
||||
u16 adc_code, int *result_mdec);
|
||||
|
||||
int qcom_adc5_prescaling_from_dt(u32 num, u32 den);
|
||||
|
||||
int qcom_adc5_hw_settle_time_from_dt(u32 value, const unsigned int *hw_settle);
|
||||
|
||||
int qcom_adc5_avg_samples_from_dt(u32 value);
|
||||
|
||||
int qcom_adc5_decimation_from_dt(u32 value, const unsigned int *decimation);
|
||||
|
||||
int qcom_vadc_decimation_from_dt(u32 value);
|
||||
|
||||
#endif /* QCOM_VADC_COMMON_H */
|
|
@ -13,6 +13,7 @@
|
|||
struct iio_dev;
|
||||
struct iio_chan_spec;
|
||||
struct device;
|
||||
struct device_node;
|
||||
|
||||
/**
|
||||
* struct iio_channel - everything needed for a consumer to use a channel
|
||||
|
@ -97,6 +98,41 @@ void iio_channel_release_all(struct iio_channel *chan);
|
|||
*/
|
||||
struct iio_channel *devm_iio_channel_get_all(struct device *dev);
|
||||
|
||||
/**
|
||||
* of_iio_channel_get_by_name() - get description of all that is needed to access channel.
|
||||
* @np: Pointer to consumer device tree node
|
||||
* @consumer_channel: Unique name to identify the channel on the consumer
|
||||
* side. This typically describes the channels use within
|
||||
* the consumer. E.g. 'battery_voltage'
|
||||
*/
|
||||
#ifdef CONFIG_OF
|
||||
struct iio_channel *of_iio_channel_get_by_name(struct device_node *np, const char *name);
|
||||
#else
|
||||
static inline struct iio_channel *
|
||||
of_iio_channel_get_by_name(struct device_node *np, const char *name)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* devm_of_iio_channel_get_by_name() - Resource managed version of of_iio_channel_get_by_name().
|
||||
* @dev: Pointer to consumer device.
|
||||
* @np: Pointer to consumer device tree node
|
||||
* @consumer_channel: Unique name to identify the channel on the consumer
|
||||
* side. This typically describes the channels use within
|
||||
* the consumer. E.g. 'battery_voltage'
|
||||
*
|
||||
* Returns a pointer to negative errno if it is not able to get the iio channel
|
||||
* otherwise returns valid pointer for iio channel.
|
||||
*
|
||||
* The allocated iio channel is automatically released when the device is
|
||||
* unbound.
|
||||
*/
|
||||
struct iio_channel *devm_of_iio_channel_get_by_name(struct device *dev,
|
||||
struct device_node *np,
|
||||
const char *consumer_channel);
|
||||
|
||||
struct iio_cb_buffer;
|
||||
/**
|
||||
* iio_channel_get_all_cb() - register callback for triggered capture
|
||||
|
|
Загрузка…
Ссылка в новой задаче