mirror of https://gitee.com/openkylin/linux.git
First set of new device support, features and cleanups for IIO in the 5.4 cycle
Note includes a merge from i3c tree to get support needed for stm_lsm6dsx driver support for l3c devices. Done from immutable branch. A counter subsystem patche in here as well. Alongside the new device support (which is always good), Chuhong's work on using devres managed APIs has cleaned up a number of drivers. New device support * adis16460 - New driver based on ADIS framework which needed addition of support for cs_change_delay. Includes device tree binding. * cros_ec - Support fo the veyron-minnie which uses an older interface. * lsm6dsx - Support for LSM6DSTR-C gyro + magnetometer sensor (new IDs mainly) - Support for ISM330DHCX acc + gyro sensor (extensive rework needed!) * Maxim 5432 - New driver support MAX5432-MAX5435 family of potentiometers. * noa1305 - New driver for this ON Semiconductor Ambient light sensor. Features and cleanups * tree wide - Drop error prints after platform_get_irq as already prints errors internally if any occur. * docs - Document mounting matrix. - Fix a missing newline at end of file. * ad2s1210 - Switch to device managed APIs for all of probe and drop explicit remove. * ad7192 - Add of_device_id array to explicity handling DT bindings. * ad7606 - Lots of rework leading to support for software configure modes in ad7616 parts. - Debugfs register access support. * am2315 - Switch to device managed APIs for all of probe and drop explicit remove. * apds9960 - Typo in module description. * cm36651 - Convert to i2c_new_dummy_device. - Swithc to device managed APIs for all of probe adn drop explicit remove. * cros_ec - Calibscale support for accel, gyro and magnetometer. - Tidy up some error codes to return the error from the stack rather than -EIO. - Determine protocol version. - Add a sign vector to the core to fix sensor rotation if necessary. Cannot just be done with mount matrix as already in use in many devices. - Tidy up INFO_SCALE being in both the separate and shared lists. - Drop a lot of dplicate code from the cros-ec-accel-legacy driver and use the core provided code instead. - Make frequency range available to userspace. * counter / ftm-quaddec - Switch to device managed APIs for all of probe adn drop explicit remove. * hdc100x - Switch to device managed APIs for all of probe and drop explicit remove. * hi8435 - Use gpiod_set_value_cansleep as we don't care here and there is a board out there where it needs to sleep. - Switch to device managed APIs for all of probe and drop explict remove. * hp03 - Convert to i2c_new_dummy_device. * maxim thermocouple - Switch to device managed APIs for all of probe and drop explicit remove. * mmc35240 - Fix typo in constant naming. * mpu6050 - Use devm_add_action_or_reset in place of explicit error handling. - Make text in Kconfig more explicit about which parts are supported. * mxc4005 - Switch to device managed APIs for all of probe and drop explicit remove. * pms7003 - Convert device tree bindings to yaml. - Add a MAINTAINERS entry * sc27xx - Introduce a local struct device *dev pointer to avoid lots of deref. - Use devm_add_action_or_reset in place of explicit error handling. * sca3000 - Typo fix in naming. * si1145 - Switch to device managed APIs for all of probe and drop explicit remove. * st_sensors - Lots of rework to enable switch to regmap. - Regmap conversion at the end. - Tidy up some inconsistencies in buffer setup ops. - Tidy up an oddity by dropping get_irq_data_ready function in favour of direct access. - Stop allocating buffer in buffer enable in favour of just embedding a large enough constant size buffer in the iio_priv accessed structure. * st_lsm6dsx - l3c device support (LSM6DSO and LSM6DSR) - tidy up irq return logic which was strangely written. - fix up an ABI quirk where this driver used separate scale attributes, even though they were always shared by type. * stk33xx - Device tree bindings include manufacturer ID. * stm32-adc - Add control for supply to analog switches including DT bindings. * stm32 timer - Drop the quadrature mode support. Believed there were no users so take this opportunity to drop this unwanted ABI. * tsl2772 - Switch to device mangage APIs for all of probe and drop explicit remove. - Use regulator_bulk_* APIs to reduce repitition. * veml6070 - Convert to i2c_new_dummy_device. -----BEGIN PGP SIGNATURE----- iQJFBAABCAAvFiEEbilms4eEBlKRJoGxVIU0mcT0FogFAl1RxVQRHGppYzIzQGtl cm5lbC5vcmcACgkQVIU0mcT0FohP8xAAjbWf431vsHQSt0Lmn4coh0XCBT26R9ys ybIga540Mcy1q91H+biBenShLVImCBtXEBJ57YdVjtLgkr1HJAJjvxCXdE79TJet Ak39CmTcTGrRyzYin2MOpWJJgCFc4Fg+E/kB8K7KIcperzQ48GDCzL5aJE/PksME Ay/rZDno+vPKGLmvxYCuSpmINq1YhVMQu/IPYO5sc6zRdDtEV29TAdZ/x8aaD2oM r1E22hm0h2wVLQRpbLUA5XUgoetQGmIYaUgbJPkpvxhuQYIlbLavODP7S0xDqAbT VCRzrzeR8eHxA+EeZDE8IWC5xbmFTUBdUHhHfCDETONOi5CjaPC/QnowNOi1IC0H k5XgBAO6Zju5QWpu+nHVdjwdJ1RftLVeCPatXorHLWWJv6UP8j/cNNKDAvBTlvhq yfG/zW1VMTr/Q2VkokHnr2D8JxcARf/+AcR1BOu+pVszJ/+kgnXVNu3QLMilCTnN +fzEfbErL7BfUBh19IqdB40XOojppuavyzAruiOjLGVrFRk3lwFP8FvVDy8MtPbO /roYxohwc1M/kK3+Sl+4LfY2mX7orStZ5NNAYcu9yzg/6vv3Oh5Pxw2UsDto4MCC ikDffzIGxXnJUzi3uIBthyvTHMX2y/EykikbjMkfsGOtCiFdcPGUl4iirsWkpebj zrpff+V1vXM= =aez5 -----END PGP SIGNATURE----- Merge tag 'iio-for-5.4a' of git://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio into staging-next Jonathan writes: First set of new device support, features and cleanups for IIO in the 5.4 cycle Note includes a merge from i3c tree to get support needed for stm_lsm6dsx driver support for l3c devices. Done from immutable branch. A counter subsystem patche in here as well. Alongside the new device support (which is always good), Chuhong's work on using devres managed APIs has cleaned up a number of drivers. New device support * adis16460 - New driver based on ADIS framework which needed addition of support for cs_change_delay. Includes device tree binding. * cros_ec - Support fo the veyron-minnie which uses an older interface. * lsm6dsx - Support for LSM6DSTR-C gyro + magnetometer sensor (new IDs mainly) - Support for ISM330DHCX acc + gyro sensor (extensive rework needed!) * Maxim 5432 - New driver support MAX5432-MAX5435 family of potentiometers. * noa1305 - New driver for this ON Semiconductor Ambient light sensor. Features and cleanups * tree wide - Drop error prints after platform_get_irq as already prints errors internally if any occur. * docs - Document mounting matrix. - Fix a missing newline at end of file. * ad2s1210 - Switch to device managed APIs for all of probe and drop explicit remove. * ad7192 - Add of_device_id array to explicity handling DT bindings. * ad7606 - Lots of rework leading to support for software configure modes in ad7616 parts. - Debugfs register access support. * am2315 - Switch to device managed APIs for all of probe and drop explicit remove. * apds9960 - Typo in module description. * cm36651 - Convert to i2c_new_dummy_device. - Swithc to device managed APIs for all of probe adn drop explicit remove. * cros_ec - Calibscale support for accel, gyro and magnetometer. - Tidy up some error codes to return the error from the stack rather than -EIO. - Determine protocol version. - Add a sign vector to the core to fix sensor rotation if necessary. Cannot just be done with mount matrix as already in use in many devices. - Tidy up INFO_SCALE being in both the separate and shared lists. - Drop a lot of dplicate code from the cros-ec-accel-legacy driver and use the core provided code instead. - Make frequency range available to userspace. * counter / ftm-quaddec - Switch to device managed APIs for all of probe adn drop explicit remove. * hdc100x - Switch to device managed APIs for all of probe and drop explicit remove. * hi8435 - Use gpiod_set_value_cansleep as we don't care here and there is a board out there where it needs to sleep. - Switch to device managed APIs for all of probe and drop explict remove. * hp03 - Convert to i2c_new_dummy_device. * maxim thermocouple - Switch to device managed APIs for all of probe and drop explicit remove. * mmc35240 - Fix typo in constant naming. * mpu6050 - Use devm_add_action_or_reset in place of explicit error handling. - Make text in Kconfig more explicit about which parts are supported. * mxc4005 - Switch to device managed APIs for all of probe and drop explicit remove. * pms7003 - Convert device tree bindings to yaml. - Add a MAINTAINERS entry * sc27xx - Introduce a local struct device *dev pointer to avoid lots of deref. - Use devm_add_action_or_reset in place of explicit error handling. * sca3000 - Typo fix in naming. * si1145 - Switch to device managed APIs for all of probe and drop explicit remove. * st_sensors - Lots of rework to enable switch to regmap. - Regmap conversion at the end. - Tidy up some inconsistencies in buffer setup ops. - Tidy up an oddity by dropping get_irq_data_ready function in favour of direct access. - Stop allocating buffer in buffer enable in favour of just embedding a large enough constant size buffer in the iio_priv accessed structure. * st_lsm6dsx - l3c device support (LSM6DSO and LSM6DSR) - tidy up irq return logic which was strangely written. - fix up an ABI quirk where this driver used separate scale attributes, even though they were always shared by type. * stk33xx - Device tree bindings include manufacturer ID. * stm32-adc - Add control for supply to analog switches including DT bindings. * stm32 timer - Drop the quadrature mode support. Believed there were no users so take this opportunity to drop this unwanted ABI. * tsl2772 - Switch to device mangage APIs for all of probe and drop explicit remove. - Use regulator_bulk_* APIs to reduce repitition. * veml6070 - Convert to i2c_new_dummy_device. * tag 'iio-for-5.4a' of git://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio: (84 commits) iio: hi8435: Drop hi8435_remove() by using devres for remaining elements iio: hi8435: Use gpiod_set_value_cansleep() iio:st_sensors: remove buffer allocation at each buffer enable iio: imu: inv_mpu6050: be more explicit on supported chips iio: light: noa1305: Add support for NOA1305 dt-bindings: Add binding document for NOA1305 iio: remove get_irq_data_ready() function pointer and use IRQ number directly iio: imu: st_lsm6dsx: make IIO_CHAN_INFO_SCALE shared by type iio: tsl2772: Use regulator_bulk_() APIs iio: tsl2772: Use devm_iio_device_register iio: tsl2772: Use devm_add_action_or_reset for tsl2772_chip_off iio: tsl2772: Use devm_add_action_or_reset iio: Remove dev_err() usage after platform_get_irq() iio: light: si1145: Use device-managed APIs iio:pressure: preenable/postenable/predisable fixup for ST press buffer iio:magn: preenable/postenable/predisable fixup for ST magn buffer iio:gyro: preenable/postenable/predisable fixup for ST gyro buffer iio:accel: preenable/postenable/predisable fixup for ST accel buffer dt-bindings: iio: imu: st_lsm6dsx: add ism330dhcx device bindings iio: imu: st_lsm6dsx: add support to ISM330DHCX ...
This commit is contained in:
commit
6aed51d891
|
@ -13,4 +13,4 @@ Description:
|
|||
error on writing
|
||||
If DFSDM input is SPI Slave:
|
||||
Reading returns value previously set.
|
||||
Writing value before starting conversions.
|
||||
Writing value before starting conversions.
|
||||
|
|
|
@ -91,29 +91,6 @@ Description:
|
|||
When counting down the counter start from preset value
|
||||
and fire event when reach 0.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_count_quadrature_mode_available
|
||||
KernelVersion: 4.12
|
||||
Contact: benjamin.gaignard@st.com
|
||||
Description:
|
||||
Reading returns the list possible quadrature modes.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_count0_quadrature_mode
|
||||
KernelVersion: 4.12
|
||||
Contact: benjamin.gaignard@st.com
|
||||
Description:
|
||||
Configure the device counter quadrature modes:
|
||||
channel_A:
|
||||
Encoder A input servers as the count input and B as
|
||||
the UP/DOWN direction control input.
|
||||
|
||||
channel_B:
|
||||
Encoder B input serves as the count input and A as
|
||||
the UP/DOWN direction control input.
|
||||
|
||||
quadrature:
|
||||
Encoder A and B inputs are mixed to get direction
|
||||
and count with a scale of 0.25.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_count_enable_mode_available
|
||||
KernelVersion: 4.12
|
||||
Contact: benjamin.gaignard@st.com
|
||||
|
|
|
@ -47,6 +47,12 @@ Required properties:
|
|||
Optional properties:
|
||||
- A pinctrl state named "default" for each ADC channel may be defined to set
|
||||
inX ADC pins in mode of operation for analog input on external pin.
|
||||
- booster-supply: Phandle to the embedded booster regulator that can be used
|
||||
to supply ADC analog input switches on stm32h7 and stm32mp1.
|
||||
- vdd-supply: Phandle to the vdd input voltage. It can be used to supply ADC
|
||||
analog input switches on stm32mp1.
|
||||
- st,syscfg: Phandle to system configuration controller. It can be used to
|
||||
control the analog circuitry on stm32mp1.
|
||||
|
||||
Contents of a stm32 adc child node:
|
||||
-----------------------------------
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
* Plantower PMS7003 particulate matter sensor
|
||||
|
||||
Required properties:
|
||||
- compatible: must one of:
|
||||
"plantower,pms1003"
|
||||
"plantower,pms3003"
|
||||
"plantower,pms5003"
|
||||
"plantower,pms6003"
|
||||
"plantower,pms7003"
|
||||
"plantower,pmsa003"
|
||||
- vcc-supply: phandle to the regulator that provides power to the sensor
|
||||
|
||||
Optional properties:
|
||||
- plantower,set-gpios: phandle to the GPIO connected to the SET line
|
||||
- reset-gpios: phandle to the GPIO connected to the RESET line
|
||||
|
||||
Refer to serial/slave-device.txt for generic serial attached device bindings.
|
||||
|
||||
Example:
|
||||
|
||||
&uart0 {
|
||||
air-pollution-sensor {
|
||||
compatible = "plantower,pms7003";
|
||||
vcc-supply = <®_vcc5v0>;
|
||||
};
|
||||
};
|
|
@ -0,0 +1,51 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/chemical/plantower,pms7003.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Plantower PMS7003 air pollution sensor
|
||||
|
||||
maintainers:
|
||||
- Tomasz Duszynski <tduszyns@gmail.com>
|
||||
|
||||
description: |
|
||||
Air pollution sensor capable of measuring mass concentration of dust
|
||||
particles.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- plantower,pms1003
|
||||
- plantower,pms3003
|
||||
- plantower,pms5003
|
||||
- plantower,pms6003
|
||||
- plantower,pms7003
|
||||
- plantower,pmsa003
|
||||
|
||||
vcc-supply:
|
||||
description: regulator that provides power to the sensor
|
||||
maxItems: 1
|
||||
|
||||
plantower,set-gpios:
|
||||
description: GPIO connected to the SET line
|
||||
maxItems: 1
|
||||
|
||||
reset-gpios:
|
||||
description: GPIO connected to the RESET line
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- vcc-supply
|
||||
|
||||
examples:
|
||||
- |
|
||||
serial {
|
||||
air-pollution-sensor {
|
||||
compatible = "plantower,pms7003";
|
||||
vcc-supply = <®_vcc5v0>;
|
||||
};
|
||||
};
|
||||
|
||||
...
|
|
@ -0,0 +1,53 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/imu/adi,adis16460.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Analog Devices ADIS16460 and similar IMUs
|
||||
|
||||
maintainers:
|
||||
- Dragos Bogdan <dragos.bogdan@analog.com>
|
||||
|
||||
description: |
|
||||
Analog Devices ADIS16460 and similar IMUs
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/ADIS16460.pdf
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- adi,adis16460
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
spi-cpha: true
|
||||
|
||||
spi-cpol: true
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
spi0 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
imu@0 {
|
||||
compatible = "adi,adis16460";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <5000000>;
|
||||
spi-cpol;
|
||||
spi-cpha;
|
||||
interrupt-parent = <&gpio0>;
|
||||
interrupts = <0 IRQ_TYPE_LEVEL_HIGH>;
|
||||
};
|
||||
};
|
|
@ -11,6 +11,8 @@ Required properties:
|
|||
"st,asm330lhh"
|
||||
"st,lsm6dsox"
|
||||
"st,lsm6dsr"
|
||||
"st,lsm6ds3tr-c"
|
||||
"st,ism330dhcx"
|
||||
- reg: i2c address of the sensor / spi cs line
|
||||
|
||||
Optional properties:
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/light/noa1305.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: ON Semiconductor NOA1305 Ambient Light Sensor
|
||||
|
||||
maintainers:
|
||||
- Martyn Welch <martyn.welch@collabora.com>
|
||||
|
||||
description: |
|
||||
Ambient sensing with an i2c interface.
|
||||
|
||||
https://www.onsemi.com/pub/Collateral/NOA1305-D.PDF
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- onnn,noa1305
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
vin-supply:
|
||||
description: Regulator that provides power to the sensor
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
examples:
|
||||
- |
|
||||
i2c {
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
light@39 {
|
||||
compatible = "onnn,noa1305";
|
||||
reg = <0x39>;
|
||||
};
|
||||
};
|
||||
...
|
|
@ -0,0 +1,49 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/light/stk33xx.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: |
|
||||
Sensortek STK33xx I2C Ambient Light and Proximity sensor
|
||||
|
||||
maintainers:
|
||||
- Jonathan Cameron <jic23@kernel.org>
|
||||
|
||||
description: |
|
||||
Ambient light and proximity sensor over an i2c interface.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- sensortek,stk3310
|
||||
- sensortek,stk3311
|
||||
- sensortek,stk3335
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
i2c {
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
stk3310@48 {
|
||||
compatible = "sensortek,stk3310";
|
||||
reg = <0x48>;
|
||||
interrupt-parent = <&gpio1>;
|
||||
interrupts = <5 IRQ_TYPE_LEVEL_LOW>;
|
||||
};
|
||||
};
|
||||
...
|
|
@ -0,0 +1,203 @@
|
|||
For discussion. Unclear are:
|
||||
* is the definition of +/- values practical or counterintuitive?
|
||||
* are the definitions unambiguous and easy to follow?
|
||||
* are the examples correct?
|
||||
* should we have HOWTO engineer a correct matrix for a new device (without comparing to a different one)?
|
||||
|
||||
====
|
||||
|
||||
|
||||
Mounting matrix
|
||||
|
||||
The mounting matrix is a device tree property used to orient any device
|
||||
that produce three-dimensional data in relation to the world where it is
|
||||
deployed.
|
||||
|
||||
The purpose of the mounting matrix is to translate the sensor frame of
|
||||
reference into the device frame of reference using a translation matrix as
|
||||
defined in linear algebra.
|
||||
|
||||
The typical usecase is that where a component has an internal representation
|
||||
of the (x,y,z) triplets, such as different registers to read these coordinates,
|
||||
and thus implying that the component should be mounted in a certain orientation
|
||||
relative to some specific device frame of reference.
|
||||
|
||||
For example a device with some kind of screen, where the user is supposed to
|
||||
interact with the environment using an accelerometer, gyroscope or magnetometer
|
||||
mounted on the same chassis as this screen, will likely take the screen as
|
||||
reference to (x,y,z) orientation, with (x,y) corresponding to these axes on the
|
||||
screen and (z) being depth, the axis perpendicular to the screen.
|
||||
|
||||
For a screen you probably want (x) coordinates to go from negative on the left
|
||||
to positive on the right, (y) from negative on the bottom to positive on top
|
||||
and (z) depth to be negative under the screen and positive in front of it,
|
||||
toward the face of the user.
|
||||
|
||||
A sensor can be mounted in any angle along the axes relative to the frame of
|
||||
reference. This means that the sensor may be flipped upside-down, left-right,
|
||||
or tilted at any angle relative to the frame of reference.
|
||||
|
||||
Another frame of reference is how the device with its sensor relates to the
|
||||
external world, the environment where the device is deployed. Usually the data
|
||||
from the sensor is used to figure out how the device is oriented with respect
|
||||
to this world. When using the mounting matrix, the sensor and device orientation
|
||||
becomes identical and we can focus on the data as it relates to the surrounding
|
||||
world.
|
||||
|
||||
Device-to-world examples for some three-dimensional sensor types:
|
||||
|
||||
- Accelerometers have their world frame of reference toward the center of
|
||||
gravity, usually to the core of the planet. A reading of the (x,y,z) values
|
||||
from the sensor will give a projection of the gravity vector through the
|
||||
device relative to the center of the planet, i.e. relative to its surface at
|
||||
this point. Up and down in the world relative to the device frame of
|
||||
reference can thus be determined. and users would likely expect a value of
|
||||
9.81 m/s^2 upwards along the (z) axis, i.e. out of the screen when the device
|
||||
is held with its screen flat on the planets surface and 0 on the other axes,
|
||||
as the gravity vector is projected 1:1 onto the sensors (z)-axis.
|
||||
|
||||
If you tilt the device, the g vector virtually coming out of the display
|
||||
is projected onto the (x,y) plane of the display panel.
|
||||
|
||||
Example:
|
||||
|
||||
^ z: +g ^ z: > 0
|
||||
! /!
|
||||
! x=y=0 / ! x: > 0
|
||||
+--------+ +--------+
|
||||
! ! ! !
|
||||
+--------+ +--------+
|
||||
! /
|
||||
! /
|
||||
v v
|
||||
center of center of
|
||||
gravity gravity
|
||||
|
||||
|
||||
If the device is tilted to the left, you get a positive x value. If you point
|
||||
its top towards surface, you get a negative y axis.
|
||||
|
||||
(---------)
|
||||
! ! y: -g
|
||||
! ! ^
|
||||
! ! !
|
||||
! !
|
||||
! ! x: +g <- z: +g -> x: -g
|
||||
! 1 2 3 !
|
||||
! 4 5 6 ! !
|
||||
! 7 8 9 ! v
|
||||
! * 0 # ! y: +g
|
||||
(---------)
|
||||
|
||||
|
||||
- Magnetometers (compasses) have their world frame of reference relative to the
|
||||
geomagnetic field. The system orientation vis-a-vis the world is defined with
|
||||
respect to the local earth geomagnetic reference frame where (y) is in the
|
||||
ground plane and positive towards magnetic North, (x) is in the ground plane,
|
||||
perpendicular to the North axis and positive towards the East and (z) is
|
||||
perpendicular to the ground plane and positive upwards.
|
||||
|
||||
|
||||
^^^ North: y > 0
|
||||
|
||||
(---------)
|
||||
! !
|
||||
! !
|
||||
! !
|
||||
! ! >
|
||||
! ! > North: x > 0
|
||||
! 1 2 3 ! >
|
||||
! 4 5 6 !
|
||||
! 7 8 9 !
|
||||
! * 0 # !
|
||||
(---------)
|
||||
|
||||
Since the geomagnetic field is not uniform this definition fails if we come
|
||||
closer to the poles.
|
||||
|
||||
Sensors and driver can not and should not take care of this because there
|
||||
are complex calculations and empirical data to be taken care of. We leave
|
||||
this up to user space.
|
||||
|
||||
The definition we take:
|
||||
|
||||
If the device is placed at the equator and the top is pointing north, the
|
||||
display is readable by a person standing upright on the earth surface, this
|
||||
defines a positive y value.
|
||||
|
||||
|
||||
- Gyroscopes detects the movement relative the device itself. The angular
|
||||
velocity is defined as orthogonal to the plane of rotation, so if you put the
|
||||
device on a flat surface and spin it around the z axis (such as rotating a
|
||||
device with a screen lying flat on a table), you should get a negative value
|
||||
along the (z) axis if rotated clockwise, and a positive value if rotated
|
||||
counter-clockwise according to the right-hand rule.
|
||||
|
||||
|
||||
(---------) y > 0
|
||||
! ! v---\
|
||||
! !
|
||||
! !
|
||||
! ! <--\
|
||||
! ! ! z > 0
|
||||
! 1 2 3 ! --/
|
||||
! 4 5 6 !
|
||||
! 7 8 9 !
|
||||
! * 0 # !
|
||||
(---------)
|
||||
|
||||
|
||||
So unless the sensor is ideally mounted, we need a means to indicate the
|
||||
relative orientation of any given sensor of this type with respect to the
|
||||
frame of reference.
|
||||
|
||||
To achieve this, use the device tree property "mount-matrix" for the sensor.
|
||||
|
||||
This supplies a 3x3 rotation matrix in the strict linear algebraic sense,
|
||||
to orient the senor axes relative to a desired point of reference. This means
|
||||
the resulting values from the sensor, after scaling to proper units, should be
|
||||
multiplied by this matrix to give the proper vectors values in three-dimensional
|
||||
space, relative to the device or world point of reference.
|
||||
|
||||
For more information, consult:
|
||||
https://en.wikipedia.org/wiki/Rotation_matrix
|
||||
|
||||
The mounting matrix has the layout:
|
||||
|
||||
(mxx, myx, mzx)
|
||||
(mxy, myy, mzy)
|
||||
(mxz, myz, mzz)
|
||||
|
||||
Values are intended to be multiplied as:
|
||||
|
||||
x' = mxx * x + myx * y + mzx * z
|
||||
y' = mxy * x + myy * y + mzy * z
|
||||
z' = mxz * x + myz * y + mzz * z
|
||||
|
||||
It is represented as an array of strings containing the real values for
|
||||
producing the transformation matrix.
|
||||
|
||||
Examples:
|
||||
|
||||
Identity matrix (nothing happens to the coordinates, which means the device was
|
||||
mechanically mounted in an ideal way and we need no transformation):
|
||||
|
||||
mount-matrix = "1", "0", "0",
|
||||
"0", "1", "0",
|
||||
"0", "0", "1";
|
||||
|
||||
The sensor is mounted 30 degrees (Pi/6 radians) tilted along the X axis, so we
|
||||
compensate by performing a -30 degrees rotation around the X axis:
|
||||
|
||||
mount-matrix = "1", "0", "0",
|
||||
"0", "0.866", "0.5",
|
||||
"0", "-0.5", "0.866";
|
||||
|
||||
The sensor is flipped 180 degrees (Pi radians) around the Z axis, i.e. mounted
|
||||
upside-down:
|
||||
|
||||
mount-matrix = "0.998", "0.054", "0",
|
||||
"-0.054", "0.998", "0",
|
||||
"0", "0", "1";
|
||||
|
||||
???: this does not match "180 degrees" - factors indicate ca. 3 degrees compensation
|
|
@ -0,0 +1,44 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/potentiometer/max5432.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Maxim Integrated MAX5432-MAX5435 Digital Potentiometers
|
||||
|
||||
maintainers:
|
||||
- Martin Kaiser <martin@kaiser.cx>
|
||||
|
||||
description: |
|
||||
Maxim Integrated MAX5432-MAX5435 Digital Potentiometers connected via I2C
|
||||
|
||||
Datasheet:
|
||||
https://datasheets.maximintegrated.com/en/ds/MAX5432-MAX5435.pdf
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- maxim,max5432
|
||||
- maxim,max5433
|
||||
- maxim,max5434
|
||||
- maxim,max5435
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
max5434@28 {
|
||||
compatible = "maxim,max5434";
|
||||
reg = <0x28>;
|
||||
};
|
||||
};
|
|
@ -813,6 +813,8 @@ patternProperties:
|
|||
description: Semtech Corporation
|
||||
"^sensirion,.*":
|
||||
description: Sensirion AG
|
||||
"^sensortek,.*":
|
||||
description: Sensortek Technology Corporation
|
||||
"^sff,.*":
|
||||
description: Small Form Factor Committee
|
||||
"^sgd,.*":
|
||||
|
|
14
MAINTAINERS
14
MAINTAINERS
|
@ -945,6 +945,14 @@ L: linux-iio@vger.kernel.org
|
|||
F: include/linux/iio/imu/adis.h
|
||||
F: drivers/iio/imu/adis.c
|
||||
|
||||
ANALOG DEVICES INC ADIS16460 DRIVER
|
||||
M: Dragos Bogdan <dragos.bogdan@analog.com>
|
||||
S: Supported
|
||||
L: linux-iio@vger.kernel.org
|
||||
W: http://ez.analog.com/community/linux-device-drivers
|
||||
F: drivers/iio/imu/adis16460.c
|
||||
F: Documentation/devicetree/bindings/iio/imu/adi,adis16460.yaml
|
||||
|
||||
ANALOG DEVICES INC ADP5061 DRIVER
|
||||
M: Stefan Popa <stefan.popa@analog.com>
|
||||
L: linux-pm@vger.kernel.org
|
||||
|
@ -12776,6 +12784,12 @@ F: drivers/i2c/busses/i2c-puv3.c
|
|||
F: drivers/video/fbdev/fb-puv3.c
|
||||
F: drivers/rtc/rtc-puv3.c
|
||||
|
||||
PLANTOWER PMS7003 AIR POLLUTION SENSOR DRIVER
|
||||
M: Tomasz Duszynski <tduszyns@gmail.com>
|
||||
S: Maintained
|
||||
F: drivers/iio/chemical/pms7003.c
|
||||
F: Documentation/devicetree/bindings/iio/chemical/plantower,pms7003.yaml
|
||||
|
||||
PMBUS HARDWARE MONITORING DRIVERS
|
||||
M: Guenter Roeck <linux@roeck-us.net>
|
||||
L: linux-hwmon@vger.kernel.org
|
||||
|
|
|
@ -100,16 +100,18 @@ static void ftm_quaddec_init(struct ftm_quaddec *ftm)
|
|||
ftm_set_write_protection(ftm);
|
||||
}
|
||||
|
||||
static void ftm_quaddec_disable(struct ftm_quaddec *ftm)
|
||||
static void ftm_quaddec_disable(void *ftm)
|
||||
{
|
||||
ftm_clear_write_protection(ftm);
|
||||
ftm_write(ftm, FTM_MODE, 0);
|
||||
ftm_write(ftm, FTM_QDCTRL, 0);
|
||||
struct ftm_quaddec *ftm_qua = ftm;
|
||||
|
||||
ftm_clear_write_protection(ftm_qua);
|
||||
ftm_write(ftm_qua, FTM_MODE, 0);
|
||||
ftm_write(ftm_qua, FTM_QDCTRL, 0);
|
||||
/*
|
||||
* This is enough to disable the counter. No clock has been
|
||||
* selected by writing to FTM_SC in init()
|
||||
*/
|
||||
ftm_set_write_protection(ftm);
|
||||
ftm_set_write_protection(ftm_qua);
|
||||
}
|
||||
|
||||
static int ftm_quaddec_get_prescaler(struct counter_device *counter,
|
||||
|
@ -317,20 +319,13 @@ static int ftm_quaddec_probe(struct platform_device *pdev)
|
|||
|
||||
ftm_quaddec_init(ftm);
|
||||
|
||||
ret = counter_register(&ftm->counter);
|
||||
ret = devm_add_action_or_reset(&pdev->dev, ftm_quaddec_disable, ftm);
|
||||
if (ret)
|
||||
ftm_quaddec_disable(ftm);
|
||||
return ret;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ftm_quaddec_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct ftm_quaddec *ftm = platform_get_drvdata(pdev);
|
||||
|
||||
counter_unregister(&ftm->counter);
|
||||
|
||||
ftm_quaddec_disable(ftm);
|
||||
ret = devm_counter_register(&pdev->dev, &ftm->counter);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -346,7 +341,6 @@ static struct platform_driver ftm_quaddec_driver = {
|
|||
.of_match_table = ftm_quaddec_match,
|
||||
},
|
||||
.probe = ftm_quaddec_probe,
|
||||
.remove = ftm_quaddec_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(ftm_quaddec_driver);
|
||||
|
|
|
@ -200,6 +200,59 @@ struct i3c_device *dev_to_i3cdev(struct device *dev)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(dev_to_i3cdev);
|
||||
|
||||
/**
|
||||
* i3c_device_match_id() - Returns the i3c_device_id entry matching @i3cdev
|
||||
* @i3cdev: I3C device
|
||||
* @id_table: I3C device match table
|
||||
*
|
||||
* Return: a pointer to an i3c_device_id object or NULL if there's no match.
|
||||
*/
|
||||
const struct i3c_device_id *
|
||||
i3c_device_match_id(struct i3c_device *i3cdev,
|
||||
const struct i3c_device_id *id_table)
|
||||
{
|
||||
struct i3c_device_info devinfo;
|
||||
const struct i3c_device_id *id;
|
||||
|
||||
i3c_device_get_info(i3cdev, &devinfo);
|
||||
|
||||
/*
|
||||
* The lower 32bits of the provisional ID is just filled with a random
|
||||
* value, try to match using DCR info.
|
||||
*/
|
||||
if (!I3C_PID_RND_LOWER_32BITS(devinfo.pid)) {
|
||||
u16 manuf = I3C_PID_MANUF_ID(devinfo.pid);
|
||||
u16 part = I3C_PID_PART_ID(devinfo.pid);
|
||||
u16 ext_info = I3C_PID_EXTRA_INFO(devinfo.pid);
|
||||
|
||||
/* First try to match by manufacturer/part ID. */
|
||||
for (id = id_table; id->match_flags != 0; id++) {
|
||||
if ((id->match_flags & I3C_MATCH_MANUF_AND_PART) !=
|
||||
I3C_MATCH_MANUF_AND_PART)
|
||||
continue;
|
||||
|
||||
if (manuf != id->manuf_id || part != id->part_id)
|
||||
continue;
|
||||
|
||||
if ((id->match_flags & I3C_MATCH_EXTRA_INFO) &&
|
||||
ext_info != id->extra_info)
|
||||
continue;
|
||||
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
/* Fallback to DCR match. */
|
||||
for (id = id_table; id->match_flags != 0; id++) {
|
||||
if ((id->match_flags & I3C_MATCH_DCR) &&
|
||||
id->dcr == devinfo.dcr)
|
||||
return id;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(i3c_device_match_id);
|
||||
|
||||
/**
|
||||
* i3c_driver_register_with_owner() - register an I3C device driver
|
||||
*
|
||||
|
|
|
@ -276,51 +276,6 @@ static const struct device_type i3c_device_type = {
|
|||
.uevent = i3c_device_uevent,
|
||||
};
|
||||
|
||||
static const struct i3c_device_id *
|
||||
i3c_device_match_id(struct i3c_device *i3cdev,
|
||||
const struct i3c_device_id *id_table)
|
||||
{
|
||||
struct i3c_device_info devinfo;
|
||||
const struct i3c_device_id *id;
|
||||
|
||||
i3c_device_get_info(i3cdev, &devinfo);
|
||||
|
||||
/*
|
||||
* The lower 32bits of the provisional ID is just filled with a random
|
||||
* value, try to match using DCR info.
|
||||
*/
|
||||
if (!I3C_PID_RND_LOWER_32BITS(devinfo.pid)) {
|
||||
u16 manuf = I3C_PID_MANUF_ID(devinfo.pid);
|
||||
u16 part = I3C_PID_PART_ID(devinfo.pid);
|
||||
u16 ext_info = I3C_PID_EXTRA_INFO(devinfo.pid);
|
||||
|
||||
/* First try to match by manufacturer/part ID. */
|
||||
for (id = id_table; id->match_flags != 0; id++) {
|
||||
if ((id->match_flags & I3C_MATCH_MANUF_AND_PART) !=
|
||||
I3C_MATCH_MANUF_AND_PART)
|
||||
continue;
|
||||
|
||||
if (manuf != id->manuf_id || part != id->part_id)
|
||||
continue;
|
||||
|
||||
if ((id->match_flags & I3C_MATCH_EXTRA_INFO) &&
|
||||
ext_info != id->extra_info)
|
||||
continue;
|
||||
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
/* Fallback to DCR match. */
|
||||
for (id = id_table; id->match_flags != 0; id++) {
|
||||
if ((id->match_flags & I3C_MATCH_DCR) &&
|
||||
id->dcr == devinfo.dcr)
|
||||
return id;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int i3c_device_match(struct device *dev, struct device_driver *drv)
|
||||
{
|
||||
struct i3c_device *i3cdev;
|
||||
|
|
|
@ -202,9 +202,7 @@ config HID_SENSOR_ACCEL_3D
|
|||
|
||||
config IIO_CROS_EC_ACCEL_LEGACY
|
||||
tristate "ChromeOS EC Legacy Accelerometer Sensor"
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
select CROS_EC_LPC_REGISTER_DEVICE
|
||||
depends on IIO_CROS_EC_SENSORS_CORE
|
||||
help
|
||||
Say yes here to get support for accelerometers on Chromebook using
|
||||
legacy EC firmware.
|
||||
|
|
|
@ -5,13 +5,14 @@
|
|||
* Copyright 2017 Google, Inc
|
||||
*
|
||||
* This driver uses the memory mapper cros-ec interface to communicate
|
||||
* with the Chrome OS EC about accelerometer data.
|
||||
* with the Chrome OS EC about accelerometer data or older commands.
|
||||
* Accelerometer access is presented through iio sysfs.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/common/cros_ec_sensors_core.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/kfifo_buf.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
|
@ -25,160 +26,41 @@
|
|||
|
||||
#define DRV_NAME "cros-ec-accel-legacy"
|
||||
|
||||
#define CROS_EC_SENSOR_LEGACY_NUM 2
|
||||
/*
|
||||
* Sensor scale hard coded at 10 bits per g, computed as:
|
||||
* g / (2^10 - 1) = 0.009586168; with g = 9.80665 m.s^-2
|
||||
*/
|
||||
#define ACCEL_LEGACY_NSCALE 9586168
|
||||
|
||||
/* Indices for EC sensor values. */
|
||||
enum {
|
||||
X,
|
||||
Y,
|
||||
Z,
|
||||
MAX_AXIS,
|
||||
};
|
||||
|
||||
/* State data for cros_ec_accel_legacy iio driver. */
|
||||
struct cros_ec_accel_legacy_state {
|
||||
struct cros_ec_device *ec;
|
||||
|
||||
/*
|
||||
* Array holding data from a single capture. 2 bytes per channel
|
||||
* for the 3 channels plus the timestamp which is always last and
|
||||
* 8-bytes aligned.
|
||||
*/
|
||||
s16 capture_data[8];
|
||||
s8 sign[MAX_AXIS];
|
||||
u8 sensor_num;
|
||||
};
|
||||
|
||||
static int ec_cmd_read_u8(struct cros_ec_device *ec, unsigned int offset,
|
||||
u8 *dest)
|
||||
static int cros_ec_accel_legacy_read_cmd(struct iio_dev *indio_dev,
|
||||
unsigned long scan_mask, s16 *data)
|
||||
{
|
||||
return ec->cmd_readmem(ec, offset, 1, dest);
|
||||
}
|
||||
|
||||
static int ec_cmd_read_u16(struct cros_ec_device *ec, unsigned int offset,
|
||||
u16 *dest)
|
||||
{
|
||||
__le16 tmp;
|
||||
int ret = ec->cmd_readmem(ec, offset, 2, &tmp);
|
||||
|
||||
*dest = le16_to_cpu(tmp);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* read_ec_until_not_busy() - Read from EC status byte until it reads not busy.
|
||||
* @st: Pointer to state information for device.
|
||||
*
|
||||
* This function reads EC status until its busy bit gets cleared. It does not
|
||||
* wait indefinitely and returns -EIO if the EC status is still busy after a
|
||||
* few hundreds milliseconds.
|
||||
*
|
||||
* Return: 8-bit status if ok, -EIO on error
|
||||
*/
|
||||
static int read_ec_until_not_busy(struct cros_ec_accel_legacy_state *st)
|
||||
{
|
||||
struct cros_ec_device *ec = st->ec;
|
||||
u8 status;
|
||||
int attempts = 0;
|
||||
|
||||
ec_cmd_read_u8(ec, EC_MEMMAP_ACC_STATUS, &status);
|
||||
while (status & EC_MEMMAP_ACC_STATUS_BUSY_BIT) {
|
||||
/* Give up after enough attempts, return error. */
|
||||
if (attempts++ >= 50)
|
||||
return -EIO;
|
||||
|
||||
/* Small delay every so often. */
|
||||
if (attempts % 5 == 0)
|
||||
msleep(25);
|
||||
|
||||
ec_cmd_read_u8(ec, EC_MEMMAP_ACC_STATUS, &status);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* read_ec_accel_data_unsafe() - Read acceleration data from EC shared memory.
|
||||
* @st: Pointer to state information for device.
|
||||
* @scan_mask: Bitmap of the sensor indices to scan.
|
||||
* @data: Location to store data.
|
||||
*
|
||||
* This is the unsafe function for reading the EC data. It does not guarantee
|
||||
* that the EC will not modify the data as it is being read in.
|
||||
*/
|
||||
static void read_ec_accel_data_unsafe(struct cros_ec_accel_legacy_state *st,
|
||||
unsigned long scan_mask, s16 *data)
|
||||
{
|
||||
int i = 0;
|
||||
int num_enabled = bitmap_weight(&scan_mask, MAX_AXIS);
|
||||
|
||||
/* Read all sensors enabled in scan_mask. Each value is 2 bytes. */
|
||||
while (num_enabled--) {
|
||||
i = find_next_bit(&scan_mask, MAX_AXIS, i);
|
||||
ec_cmd_read_u16(st->ec,
|
||||
EC_MEMMAP_ACC_DATA +
|
||||
sizeof(s16) *
|
||||
(1 + i + st->sensor_num * MAX_AXIS),
|
||||
data);
|
||||
*data *= st->sign[i];
|
||||
i++;
|
||||
data++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* read_ec_accel_data() - Read acceleration data from EC shared memory.
|
||||
* @st: Pointer to state information for device.
|
||||
* @scan_mask: Bitmap of the sensor indices to scan.
|
||||
* @data: Location to store data.
|
||||
*
|
||||
* This is the safe function for reading the EC data. It guarantees that
|
||||
* the data sampled was not modified by the EC while being read.
|
||||
*
|
||||
* Return: 0 if ok, -ve on error
|
||||
*/
|
||||
static int read_ec_accel_data(struct cros_ec_accel_legacy_state *st,
|
||||
unsigned long scan_mask, s16 *data)
|
||||
{
|
||||
u8 samp_id = 0xff;
|
||||
u8 status = 0;
|
||||
struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
int attempts = 0;
|
||||
unsigned int i;
|
||||
u8 sensor_num;
|
||||
|
||||
/*
|
||||
* Continually read all data from EC until the status byte after
|
||||
* all reads reflects that the EC is not busy and the sample id
|
||||
* matches the sample id from before all reads. This guarantees
|
||||
* that data read in was not modified by the EC while reading.
|
||||
* Read all sensor data through a command.
|
||||
* Save sensor_num, it is assumed to stay.
|
||||
*/
|
||||
while ((status & (EC_MEMMAP_ACC_STATUS_BUSY_BIT |
|
||||
EC_MEMMAP_ACC_STATUS_SAMPLE_ID_MASK)) != samp_id) {
|
||||
/* If we have tried to read too many times, return error. */
|
||||
if (attempts++ >= 5)
|
||||
return -EIO;
|
||||
sensor_num = st->param.info.sensor_num;
|
||||
st->param.cmd = MOTIONSENSE_CMD_DUMP;
|
||||
st->param.dump.max_sensor_count = CROS_EC_SENSOR_LEGACY_NUM;
|
||||
ret = cros_ec_motion_send_host_cmd(st,
|
||||
sizeof(st->resp->dump) + CROS_EC_SENSOR_LEGACY_NUM *
|
||||
sizeof(struct ec_response_motion_sensor_data));
|
||||
st->param.info.sensor_num = sensor_num;
|
||||
if (ret != 0) {
|
||||
dev_warn(&indio_dev->dev, "Unable to read sensor data\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Read status byte until EC is not busy. */
|
||||
ret = read_ec_until_not_busy(st);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
status = ret;
|
||||
|
||||
/*
|
||||
* Store the current sample id so that we can compare to the
|
||||
* sample id after reading the data.
|
||||
*/
|
||||
samp_id = status & EC_MEMMAP_ACC_STATUS_SAMPLE_ID_MASK;
|
||||
|
||||
/* Read all EC data, format it, and store it into data. */
|
||||
read_ec_accel_data_unsafe(st, scan_mask, data);
|
||||
|
||||
/* Read status byte. */
|
||||
ec_cmd_read_u8(st->ec, EC_MEMMAP_ACC_STATUS, &status);
|
||||
for_each_set_bit(i, &scan_mask, indio_dev->masklength) {
|
||||
*data = st->resp->dump.sensor[sensor_num].data[i] *
|
||||
st->sign[i];
|
||||
data++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -188,28 +70,40 @@ static int cros_ec_accel_legacy_read(struct iio_dev *indio_dev,
|
|||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct cros_ec_accel_legacy_state *st = iio_priv(indio_dev);
|
||||
struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
|
||||
s16 data = 0;
|
||||
int ret = IIO_VAL_INT;
|
||||
int ret;
|
||||
int idx = chan->scan_index;
|
||||
|
||||
mutex_lock(&st->cmd_lock);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = read_ec_accel_data(st, (1 << chan->scan_index), &data);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = st->read_ec_sensors_data(indio_dev, 1 << idx, &data);
|
||||
if (ret < 0)
|
||||
break;
|
||||
ret = IIO_VAL_INT;
|
||||
*val = data;
|
||||
return IIO_VAL_INT;
|
||||
break;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
WARN_ON(st->type != MOTIONSENSE_TYPE_ACCEL);
|
||||
*val = 0;
|
||||
*val2 = ACCEL_LEGACY_NSCALE;
|
||||
return IIO_VAL_INT_PLUS_NANO;
|
||||
ret = IIO_VAL_INT_PLUS_NANO;
|
||||
break;
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
/* Calibration not supported. */
|
||||
*val = 0;
|
||||
return IIO_VAL_INT;
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
ret = cros_ec_sensors_core_read(st, chan, val, val2,
|
||||
mask);
|
||||
break;
|
||||
}
|
||||
mutex_unlock(&st->cmd_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cros_ec_accel_legacy_write(struct iio_dev *indio_dev,
|
||||
|
@ -231,86 +125,14 @@ static const struct iio_info cros_ec_accel_legacy_info = {
|
|||
.write_raw = &cros_ec_accel_legacy_write,
|
||||
};
|
||||
|
||||
/**
|
||||
* cros_ec_accel_legacy_capture() - The trigger handler function
|
||||
* @irq: The interrupt number.
|
||||
* @p: Private data - always a pointer to the poll func.
|
||||
*
|
||||
* On a trigger event occurring, if the pollfunc is attached then this
|
||||
* handler is called as a threaded interrupt (and hence may sleep). It
|
||||
* is responsible for grabbing data from the device and pushing it into
|
||||
* the associated buffer.
|
||||
*
|
||||
* Return: IRQ_HANDLED
|
||||
/*
|
||||
* Present the channel using HTML5 standard:
|
||||
* need to invert X and Y and invert some lid axis.
|
||||
*/
|
||||
static irqreturn_t cros_ec_accel_legacy_capture(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct cros_ec_accel_legacy_state *st = iio_priv(indio_dev);
|
||||
|
||||
/* Clear capture data. */
|
||||
memset(st->capture_data, 0, sizeof(st->capture_data));
|
||||
|
||||
/*
|
||||
* Read data based on which channels are enabled in scan mask. Note
|
||||
* that on a capture we are always reading the calibrated data.
|
||||
*/
|
||||
read_ec_accel_data(st, *indio_dev->active_scan_mask, st->capture_data);
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, (void *)st->capture_data,
|
||||
iio_get_time_ns(indio_dev));
|
||||
|
||||
/*
|
||||
* Tell the core we are done with this trigger and ready for the
|
||||
* next one.
|
||||
*/
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static char *cros_ec_accel_legacy_loc_strings[] = {
|
||||
[MOTIONSENSE_LOC_BASE] = "base",
|
||||
[MOTIONSENSE_LOC_LID] = "lid",
|
||||
[MOTIONSENSE_LOC_MAX] = "unknown",
|
||||
};
|
||||
|
||||
static ssize_t cros_ec_accel_legacy_loc(struct iio_dev *indio_dev,
|
||||
uintptr_t private,
|
||||
const struct iio_chan_spec *chan,
|
||||
char *buf)
|
||||
{
|
||||
struct cros_ec_accel_legacy_state *st = iio_priv(indio_dev);
|
||||
|
||||
return sprintf(buf, "%s\n",
|
||||
cros_ec_accel_legacy_loc_strings[st->sensor_num +
|
||||
MOTIONSENSE_LOC_BASE]);
|
||||
}
|
||||
|
||||
static ssize_t cros_ec_accel_legacy_id(struct iio_dev *indio_dev,
|
||||
uintptr_t private,
|
||||
const struct iio_chan_spec *chan,
|
||||
char *buf)
|
||||
{
|
||||
struct cros_ec_accel_legacy_state *st = iio_priv(indio_dev);
|
||||
|
||||
return sprintf(buf, "%d\n", st->sensor_num);
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec_ext_info cros_ec_accel_legacy_ext_info[] = {
|
||||
{
|
||||
.name = "id",
|
||||
.shared = IIO_SHARED_BY_ALL,
|
||||
.read = cros_ec_accel_legacy_id,
|
||||
},
|
||||
{
|
||||
.name = "location",
|
||||
.shared = IIO_SHARED_BY_ALL,
|
||||
.read = cros_ec_accel_legacy_loc,
|
||||
},
|
||||
{ }
|
||||
};
|
||||
#define CROS_EC_ACCEL_ROTATE_AXIS(_axis) \
|
||||
((_axis) == CROS_EC_SENSOR_Z ? CROS_EC_SENSOR_Z : \
|
||||
((_axis) == CROS_EC_SENSOR_X ? CROS_EC_SENSOR_Y : \
|
||||
CROS_EC_SENSOR_X))
|
||||
|
||||
#define CROS_EC_ACCEL_LEGACY_CHAN(_axis) \
|
||||
{ \
|
||||
|
@ -321,28 +143,28 @@ static const struct iio_chan_spec_ext_info cros_ec_accel_legacy_ext_info[] = {
|
|||
BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_CALIBBIAS), \
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.ext_info = cros_ec_accel_legacy_ext_info, \
|
||||
.ext_info = cros_ec_sensors_ext_info, \
|
||||
.scan_type = { \
|
||||
.sign = 's', \
|
||||
.realbits = 16, \
|
||||
.storagebits = 16, \
|
||||
.realbits = CROS_EC_SENSOR_BITS, \
|
||||
.storagebits = CROS_EC_SENSOR_BITS, \
|
||||
}, \
|
||||
.scan_index = CROS_EC_ACCEL_ROTATE_AXIS(_axis), \
|
||||
} \
|
||||
|
||||
static struct iio_chan_spec ec_accel_channels[] = {
|
||||
CROS_EC_ACCEL_LEGACY_CHAN(X),
|
||||
CROS_EC_ACCEL_LEGACY_CHAN(Y),
|
||||
CROS_EC_ACCEL_LEGACY_CHAN(Z),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(MAX_AXIS)
|
||||
static const struct iio_chan_spec cros_ec_accel_legacy_channels[] = {
|
||||
CROS_EC_ACCEL_LEGACY_CHAN(CROS_EC_SENSOR_X),
|
||||
CROS_EC_ACCEL_LEGACY_CHAN(CROS_EC_SENSOR_Y),
|
||||
CROS_EC_ACCEL_LEGACY_CHAN(CROS_EC_SENSOR_Z),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(CROS_EC_SENSOR_MAX_AXIS)
|
||||
};
|
||||
|
||||
static int cros_ec_accel_legacy_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct cros_ec_dev *ec = dev_get_drvdata(dev->parent);
|
||||
struct cros_ec_sensor_platform *sensor_platform = dev_get_platdata(dev);
|
||||
struct iio_dev *indio_dev;
|
||||
struct cros_ec_accel_legacy_state *state;
|
||||
struct cros_ec_sensors_core_state *state;
|
||||
int ret;
|
||||
|
||||
if (!ec || !ec->ec_dev) {
|
||||
|
@ -350,46 +172,32 @@ static int cros_ec_accel_legacy_probe(struct platform_device *pdev)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!ec->ec_dev->cmd_readmem) {
|
||||
dev_warn(&pdev->dev, "EC does not support direct reads.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*state));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, indio_dev);
|
||||
state = iio_priv(indio_dev);
|
||||
state->ec = ec->ec_dev;
|
||||
state->sensor_num = sensor_platform->sensor_num;
|
||||
ret = cros_ec_sensors_core_init(pdev, indio_dev, true);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
indio_dev->dev.parent = dev;
|
||||
indio_dev->name = pdev->name;
|
||||
indio_dev->channels = ec_accel_channels;
|
||||
/*
|
||||
* Present the channel using HTML5 standard:
|
||||
* need to invert X and Y and invert some lid axis.
|
||||
*/
|
||||
ec_accel_channels[X].scan_index = Y;
|
||||
ec_accel_channels[Y].scan_index = X;
|
||||
ec_accel_channels[Z].scan_index = Z;
|
||||
|
||||
state->sign[Y] = 1;
|
||||
|
||||
if (state->sensor_num == MOTIONSENSE_LOC_LID)
|
||||
state->sign[X] = state->sign[Z] = -1;
|
||||
else
|
||||
state->sign[X] = state->sign[Z] = 1;
|
||||
|
||||
indio_dev->num_channels = ARRAY_SIZE(ec_accel_channels);
|
||||
indio_dev->dev.parent = &pdev->dev;
|
||||
indio_dev->info = &cros_ec_accel_legacy_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
state = iio_priv(indio_dev);
|
||||
|
||||
if (state->ec->cmd_readmem != NULL)
|
||||
state->read_ec_sensors_data = cros_ec_sensors_read_lpc;
|
||||
else
|
||||
state->read_ec_sensors_data = cros_ec_accel_legacy_read_cmd;
|
||||
|
||||
indio_dev->channels = cros_ec_accel_legacy_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(cros_ec_accel_legacy_channels);
|
||||
/* The lid sensor needs to be presented inverted. */
|
||||
if (state->loc == MOTIONSENSE_LOC_LID) {
|
||||
state->sign[CROS_EC_SENSOR_X] = -1;
|
||||
state->sign[CROS_EC_SENSOR_Z] = -1;
|
||||
}
|
||||
|
||||
ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
|
||||
cros_ec_accel_legacy_capture,
|
||||
NULL);
|
||||
cros_ec_sensors_capture, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
|
|
@ -424,7 +424,7 @@ static int mxc4005_probe(struct i2c_client *client,
|
|||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &mxc4005_info;
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev,
|
||||
ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev,
|
||||
iio_pollfunc_store_time,
|
||||
mxc4005_trigger_handler,
|
||||
NULL);
|
||||
|
@ -452,7 +452,7 @@ static int mxc4005_probe(struct i2c_client *client,
|
|||
if (ret) {
|
||||
dev_err(&client->dev,
|
||||
"failed to init threaded irq\n");
|
||||
goto err_buffer_cleanup;
|
||||
return ret;
|
||||
}
|
||||
|
||||
data->dready_trig->dev.parent = &client->dev;
|
||||
|
@ -460,43 +460,16 @@ static int mxc4005_probe(struct i2c_client *client,
|
|||
iio_trigger_set_drvdata(data->dready_trig, indio_dev);
|
||||
indio_dev->trig = data->dready_trig;
|
||||
iio_trigger_get(indio_dev->trig);
|
||||
ret = iio_trigger_register(data->dready_trig);
|
||||
ret = devm_iio_trigger_register(&client->dev,
|
||||
data->dready_trig);
|
||||
if (ret) {
|
||||
dev_err(&client->dev,
|
||||
"failed to register trigger\n");
|
||||
goto err_trigger_unregister;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev,
|
||||
"unable to register iio device %d\n", ret);
|
||||
goto err_buffer_cleanup;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_trigger_unregister:
|
||||
iio_trigger_unregister(data->dready_trig);
|
||||
err_buffer_cleanup:
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mxc4005_remove(struct i2c_client *client)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
struct mxc4005_data *data = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
if (data->dready_trig)
|
||||
iio_trigger_unregister(data->dready_trig);
|
||||
|
||||
return 0;
|
||||
return devm_iio_device_register(&client->dev, indio_dev);
|
||||
}
|
||||
|
||||
static const struct acpi_device_id mxc4005_acpi_match[] = {
|
||||
|
@ -517,7 +490,6 @@ static struct i2c_driver mxc4005_driver = {
|
|||
.acpi_match_table = ACPI_PTR(mxc4005_acpi_match),
|
||||
},
|
||||
.probe = mxc4005_probe,
|
||||
.remove = mxc4005_remove,
|
||||
.id_table = mxc4005_id,
|
||||
};
|
||||
|
||||
|
|
|
@ -111,7 +111,7 @@
|
|||
/* Currently unsupported */
|
||||
#define SCA3000_MD_CTRL_AND_Y BIT(3)
|
||||
#define SCA3000_MD_CTRL_AND_X BIT(4)
|
||||
#define SAC3000_MD_CTRL_AND_Z BIT(5)
|
||||
#define SCA3000_MD_CTRL_AND_Z BIT(5)
|
||||
|
||||
/*
|
||||
* Some control registers of complex access methods requiring this register to
|
||||
|
|
|
@ -68,6 +68,7 @@ static const struct st_sensors_platform_data default_accel_pdata = {
|
|||
.drdy_int_pin = 1,
|
||||
};
|
||||
|
||||
const struct st_sensor_settings *st_accel_get_settings(const char *name);
|
||||
int st_accel_common_probe(struct iio_dev *indio_dev);
|
||||
void st_accel_common_remove(struct iio_dev *indio_dev);
|
||||
|
||||
|
|
|
@ -29,65 +29,51 @@ int st_accel_trig_set_state(struct iio_trigger *trig, bool state)
|
|||
return st_sensors_set_dataready_irq(indio_dev, state);
|
||||
}
|
||||
|
||||
static int st_accel_buffer_preenable(struct iio_dev *indio_dev)
|
||||
{
|
||||
return st_sensors_set_enable(indio_dev, true);
|
||||
}
|
||||
|
||||
static int st_accel_buffer_postenable(struct iio_dev *indio_dev)
|
||||
{
|
||||
int err;
|
||||
struct st_sensor_data *adata = iio_priv(indio_dev);
|
||||
|
||||
adata->buffer_data = kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
|
||||
if (adata->buffer_data == NULL) {
|
||||
err = -ENOMEM;
|
||||
goto allocate_memory_error;
|
||||
}
|
||||
|
||||
err = iio_triggered_buffer_postenable(indio_dev);
|
||||
if (err < 0)
|
||||
goto st_accel_buffer_postenable_error;
|
||||
return err;
|
||||
|
||||
err = st_sensors_set_axis_enable(indio_dev,
|
||||
(u8)indio_dev->active_scan_mask[0]);
|
||||
(u8)indio_dev->active_scan_mask[0]);
|
||||
if (err < 0)
|
||||
goto st_sensors_set_axis_enable_error;
|
||||
goto st_accel_buffer_predisable;
|
||||
|
||||
return err;
|
||||
err = st_sensors_set_enable(indio_dev, true);
|
||||
if (err < 0)
|
||||
goto st_accel_buffer_enable_all_axis;
|
||||
|
||||
st_sensors_set_axis_enable_error:
|
||||
return 0;
|
||||
|
||||
st_accel_buffer_enable_all_axis:
|
||||
st_sensors_set_axis_enable(indio_dev, ST_SENSORS_ENABLE_ALL_AXIS);
|
||||
st_accel_buffer_predisable:
|
||||
iio_triggered_buffer_predisable(indio_dev);
|
||||
st_accel_buffer_postenable_error:
|
||||
kfree(adata->buffer_data);
|
||||
allocate_memory_error:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int st_accel_buffer_predisable(struct iio_dev *indio_dev)
|
||||
{
|
||||
int err, err2;
|
||||
struct st_sensor_data *adata = iio_priv(indio_dev);
|
||||
|
||||
err = st_sensors_set_axis_enable(indio_dev, ST_SENSORS_ENABLE_ALL_AXIS);
|
||||
if (err < 0)
|
||||
goto st_accel_buffer_predisable_error;
|
||||
|
||||
err = st_sensors_set_enable(indio_dev, false);
|
||||
if (err < 0)
|
||||
goto st_accel_buffer_predisable_error;
|
||||
goto st_accel_buffer_predisable;
|
||||
|
||||
st_accel_buffer_predisable_error:
|
||||
err = st_sensors_set_axis_enable(indio_dev, ST_SENSORS_ENABLE_ALL_AXIS);
|
||||
|
||||
st_accel_buffer_predisable:
|
||||
err2 = iio_triggered_buffer_predisable(indio_dev);
|
||||
if (!err)
|
||||
err = err2;
|
||||
|
||||
kfree(adata->buffer_data);
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct iio_buffer_setup_ops st_accel_buffer_setup_ops = {
|
||||
.preenable = &st_accel_buffer_preenable,
|
||||
.postenable = &st_accel_buffer_postenable,
|
||||
.predisable = &st_accel_buffer_predisable,
|
||||
};
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
#include <linux/acpi.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/gpio.h>
|
||||
|
@ -1147,32 +1146,45 @@ static int apply_acpi_orientation(struct iio_dev *indio_dev,
|
|||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* st_accel_get_settings() - get sensor settings from device name
|
||||
* @name: device name buffer reference.
|
||||
*
|
||||
* Return: valid reference on success, NULL otherwise.
|
||||
*/
|
||||
const struct st_sensor_settings *st_accel_get_settings(const char *name)
|
||||
{
|
||||
int index = st_sensors_get_settings_index(name,
|
||||
st_accel_sensors_settings,
|
||||
ARRAY_SIZE(st_accel_sensors_settings));
|
||||
if (index < 0)
|
||||
return NULL;
|
||||
|
||||
return &st_accel_sensors_settings[index];
|
||||
}
|
||||
EXPORT_SYMBOL(st_accel_get_settings);
|
||||
|
||||
int st_accel_common_probe(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct st_sensor_data *adata = iio_priv(indio_dev);
|
||||
struct st_sensors_platform_data *pdata =
|
||||
(struct st_sensors_platform_data *)adata->dev->platform_data;
|
||||
int irq = adata->get_irq_data_ready(indio_dev);
|
||||
struct iio_chan_spec *channels;
|
||||
size_t channels_size;
|
||||
int err;
|
||||
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &accel_info;
|
||||
mutex_init(&adata->tb.buf_lock);
|
||||
|
||||
err = st_sensors_power_enable(indio_dev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = st_sensors_check_device_support(indio_dev,
|
||||
ARRAY_SIZE(st_accel_sensors_settings),
|
||||
st_accel_sensors_settings);
|
||||
err = st_sensors_verify_id(indio_dev);
|
||||
if (err < 0)
|
||||
goto st_accel_power_off;
|
||||
|
||||
adata->num_data_channels = ST_ACCEL_NUMBER_DATA_CHANNELS;
|
||||
adata->multiread_bit = adata->sensor_settings->multi_read_bit;
|
||||
indio_dev->num_channels = ST_SENSORS_NUMBER_ALL_CHANNELS;
|
||||
|
||||
channels_size = indio_dev->num_channels * sizeof(struct iio_chan_spec);
|
||||
|
@ -1204,7 +1216,7 @@ int st_accel_common_probe(struct iio_dev *indio_dev)
|
|||
if (err < 0)
|
||||
goto st_accel_power_off;
|
||||
|
||||
if (irq > 0) {
|
||||
if (adata->irq > 0) {
|
||||
err = st_sensors_allocate_trigger(indio_dev,
|
||||
ST_ACCEL_TRIGGER_OPS);
|
||||
if (err < 0)
|
||||
|
@ -1221,7 +1233,7 @@ int st_accel_common_probe(struct iio_dev *indio_dev)
|
|||
return 0;
|
||||
|
||||
st_accel_device_register_error:
|
||||
if (irq > 0)
|
||||
if (adata->irq > 0)
|
||||
st_sensors_deallocate_trigger(indio_dev);
|
||||
st_accel_probe_trigger_error:
|
||||
st_accel_deallocate_ring(indio_dev);
|
||||
|
@ -1239,7 +1251,7 @@ void st_accel_common_remove(struct iio_dev *indio_dev)
|
|||
st_sensors_power_disable(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
if (adata->get_irq_data_ready(indio_dev) > 0)
|
||||
if (adata->irq > 0)
|
||||
st_sensors_deallocate_trigger(indio_dev);
|
||||
|
||||
st_accel_deallocate_ring(indio_dev);
|
||||
|
|
|
@ -150,22 +150,33 @@ MODULE_DEVICE_TABLE(i2c, st_accel_id_table);
|
|||
|
||||
static int st_accel_i2c_probe(struct i2c_client *client)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
const struct st_sensor_settings *settings;
|
||||
struct st_sensor_data *adata;
|
||||
struct iio_dev *indio_dev;
|
||||
const char *match;
|
||||
int ret;
|
||||
|
||||
match = device_get_match_data(&client->dev);
|
||||
if (match)
|
||||
strlcpy(client->name, match, sizeof(client->name));
|
||||
|
||||
settings = st_accel_get_settings(client->name);
|
||||
if (!settings) {
|
||||
dev_err(&client->dev, "device name %s not recognized.\n",
|
||||
client->name);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*adata));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
adata = iio_priv(indio_dev);
|
||||
adata->sensor_settings = (struct st_sensor_settings *)settings;
|
||||
|
||||
match = device_get_match_data(&client->dev);
|
||||
if (match)
|
||||
strlcpy(client->name, match, sizeof(client->name));
|
||||
|
||||
st_sensors_i2c_configure(indio_dev, client, adata);
|
||||
ret = st_sensors_i2c_configure(indio_dev, client);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = st_accel_common_probe(indio_dev);
|
||||
if (ret < 0)
|
||||
|
|
|
@ -102,19 +102,31 @@ MODULE_DEVICE_TABLE(of, st_accel_of_match);
|
|||
|
||||
static int st_accel_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
const struct st_sensor_settings *settings;
|
||||
struct st_sensor_data *adata;
|
||||
struct iio_dev *indio_dev;
|
||||
int err;
|
||||
|
||||
st_sensors_of_name_probe(&spi->dev, st_accel_of_match,
|
||||
spi->modalias, sizeof(spi->modalias));
|
||||
|
||||
settings = st_accel_get_settings(spi->modalias);
|
||||
if (!settings) {
|
||||
dev_err(&spi->dev, "device name %s not recognized.\n",
|
||||
spi->modalias);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adata));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
adata = iio_priv(indio_dev);
|
||||
adata->sensor_settings = (struct st_sensor_settings *)settings;
|
||||
|
||||
st_sensors_of_name_probe(&spi->dev, st_accel_of_match,
|
||||
spi->modalias, sizeof(spi->modalias));
|
||||
st_sensors_spi_configure(indio_dev, spi, adata);
|
||||
err = st_sensors_spi_configure(indio_dev, spi);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = st_accel_common_probe(indio_dev);
|
||||
if (err < 0)
|
||||
|
|
|
@ -35,6 +35,11 @@ static const unsigned int ad7606_scale_avail[2] = {
|
|||
152588, 305176
|
||||
};
|
||||
|
||||
|
||||
static const unsigned int ad7616_sw_scale_avail[3] = {
|
||||
76293, 152588, 305176
|
||||
};
|
||||
|
||||
static const unsigned int ad7606_oversampling_avail[7] = {
|
||||
1, 2, 4, 8, 16, 32, 64,
|
||||
};
|
||||
|
@ -55,6 +60,29 @@ static int ad7606_reset(struct ad7606_state *st)
|
|||
return -ENODEV;
|
||||
}
|
||||
|
||||
static int ad7606_reg_access(struct iio_dev *indio_dev,
|
||||
unsigned int reg,
|
||||
unsigned int writeval,
|
||||
unsigned int *readval)
|
||||
{
|
||||
struct ad7606_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&st->lock);
|
||||
if (readval) {
|
||||
ret = st->bops->reg_read(st, reg);
|
||||
if (ret < 0)
|
||||
goto err_unlock;
|
||||
*readval = ret;
|
||||
ret = 0;
|
||||
} else {
|
||||
ret = st->bops->reg_write(st, reg, writeval);
|
||||
}
|
||||
err_unlock:
|
||||
mutex_unlock(&st->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad7606_read_samples(struct ad7606_state *st)
|
||||
{
|
||||
unsigned int num = st->chip_info->num_channels;
|
||||
|
@ -308,29 +336,6 @@ static const struct attribute_group ad7606_attribute_group_range = {
|
|||
.attrs = ad7606_attributes_range,
|
||||
};
|
||||
|
||||
#define AD760X_CHANNEL(num, mask) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = num, \
|
||||
.address = num, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),\
|
||||
.info_mask_shared_by_all = mask, \
|
||||
.scan_index = num, \
|
||||
.scan_type = { \
|
||||
.sign = 's', \
|
||||
.realbits = 16, \
|
||||
.storagebits = 16, \
|
||||
.endianness = IIO_CPU, \
|
||||
}, \
|
||||
}
|
||||
|
||||
#define AD7605_CHANNEL(num) \
|
||||
AD760X_CHANNEL(num, 0)
|
||||
|
||||
#define AD7606_CHANNEL(num) \
|
||||
AD760X_CHANNEL(num, BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO))
|
||||
|
||||
static const struct iio_chan_spec ad7605_channels[] = {
|
||||
IIO_CHAN_SOFT_TIMESTAMP(4),
|
||||
AD7605_CHANNEL(0),
|
||||
|
@ -519,6 +524,14 @@ static const struct iio_info ad7606_info_os_and_range = {
|
|||
.validate_trigger = &ad7606_validate_trigger,
|
||||
};
|
||||
|
||||
static const struct iio_info ad7606_info_os_range_and_debug = {
|
||||
.read_raw = &ad7606_read_raw,
|
||||
.write_raw = &ad7606_write_raw,
|
||||
.debugfs_reg_access = &ad7606_reg_access,
|
||||
.attrs = &ad7606_attribute_group_os_and_range,
|
||||
.validate_trigger = &ad7606_validate_trigger,
|
||||
};
|
||||
|
||||
static const struct iio_info ad7606_info_os = {
|
||||
.read_raw = &ad7606_read_raw,
|
||||
.write_raw = &ad7606_write_raw,
|
||||
|
@ -617,35 +630,27 @@ int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,
|
|||
if (ret)
|
||||
dev_warn(st->dev, "failed to RESET: no RESET GPIO specified\n");
|
||||
|
||||
/* AD7616 requires al least 15ms to reconfigure after a reset */
|
||||
if (msleep_interruptible(15))
|
||||
return -ERESTARTSYS;
|
||||
|
||||
st->write_scale = ad7606_write_scale_hw;
|
||||
st->write_os = ad7606_write_os_hw;
|
||||
|
||||
if (st->chip_info->sw_mode_config)
|
||||
if (st->bops->sw_mode_config)
|
||||
st->sw_mode_en = device_property_present(st->dev,
|
||||
"adi,sw-mode");
|
||||
|
||||
if (st->sw_mode_en) {
|
||||
/* Scale of 0.076293 is only available in sw mode */
|
||||
st->scale_avail = ad7616_sw_scale_avail;
|
||||
st->num_scales = ARRAY_SIZE(ad7616_sw_scale_avail);
|
||||
|
||||
/* After reset, in software mode, ±10 V is set by default */
|
||||
memset32(st->range, 2, ARRAY_SIZE(st->range));
|
||||
indio_dev->info = &ad7606_info_os_and_range;
|
||||
indio_dev->info = &ad7606_info_os_range_and_debug;
|
||||
|
||||
/*
|
||||
* In software mode, the range gpio has no longer its function.
|
||||
* Instead, the scale can be configured individually for each
|
||||
* channel from the range registers.
|
||||
*/
|
||||
if (st->chip_info->write_scale_sw)
|
||||
st->write_scale = st->chip_info->write_scale_sw;
|
||||
|
||||
/*
|
||||
* In software mode, the oversampling is no longer configured
|
||||
* with GPIO pins. Instead, the oversampling can be configured
|
||||
* in configuratiion register.
|
||||
*/
|
||||
if (st->chip_info->write_os_sw)
|
||||
st->write_os = st->chip_info->write_os_sw;
|
||||
|
||||
ret = st->chip_info->sw_mode_config(indio_dev);
|
||||
ret = st->bops->sw_mode_config(indio_dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -8,6 +8,36 @@
|
|||
#ifndef IIO_ADC_AD7606_H_
|
||||
#define IIO_ADC_AD7606_H_
|
||||
|
||||
#define AD760X_CHANNEL(num, mask_sep, mask_type, mask_all) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = num, \
|
||||
.address = num, \
|
||||
.info_mask_separate = mask_sep, \
|
||||
.info_mask_shared_by_type = mask_type, \
|
||||
.info_mask_shared_by_all = mask_all, \
|
||||
.scan_index = num, \
|
||||
.scan_type = { \
|
||||
.sign = 's', \
|
||||
.realbits = 16, \
|
||||
.storagebits = 16, \
|
||||
.endianness = IIO_CPU, \
|
||||
}, \
|
||||
}
|
||||
|
||||
#define AD7605_CHANNEL(num) \
|
||||
AD760X_CHANNEL(num, BIT(IIO_CHAN_INFO_RAW), \
|
||||
BIT(IIO_CHAN_INFO_SCALE), 0)
|
||||
|
||||
#define AD7606_CHANNEL(num) \
|
||||
AD760X_CHANNEL(num, BIT(IIO_CHAN_INFO_RAW), \
|
||||
BIT(IIO_CHAN_INFO_SCALE), \
|
||||
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO))
|
||||
|
||||
#define AD7616_CHANNEL(num) \
|
||||
AD760X_CHANNEL(num, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),\
|
||||
0, BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO))
|
||||
|
||||
/**
|
||||
* struct ad7606_chip_info - chip specific information
|
||||
* @channels: channel specification
|
||||
|
@ -16,12 +46,6 @@
|
|||
* oversampling ratios.
|
||||
* @oversampling_num number of elements stored in oversampling_avail array
|
||||
* @os_req_reset some devices require a reset to update oversampling
|
||||
* @write_scale_sw pointer to the function which writes the scale via spi
|
||||
in software mode
|
||||
* @write_os_sw pointer to the function which writes the os via spi
|
||||
in software mode
|
||||
* @sw_mode_config: pointer to a function which configured the device
|
||||
* for software mode
|
||||
*/
|
||||
struct ad7606_chip_info {
|
||||
const struct iio_chan_spec *channels;
|
||||
|
@ -29,9 +53,6 @@ struct ad7606_chip_info {
|
|||
const unsigned int *oversampling_avail;
|
||||
unsigned int oversampling_num;
|
||||
bool os_req_reset;
|
||||
int (*write_scale_sw)(struct iio_dev *indio_dev, int ch, int val);
|
||||
int (*write_os_sw)(struct iio_dev *indio_dev, int val);
|
||||
int (*sw_mode_config)(struct iio_dev *indio_dev);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -63,6 +84,7 @@ struct ad7606_chip_info {
|
|||
* @complete completion to indicate end of conversion
|
||||
* @trig The IIO trigger associated with the device.
|
||||
* @data buffer for reading data from the device
|
||||
* @d16 be16 buffer for reading data from the device
|
||||
*/
|
||||
struct ad7606_state {
|
||||
struct device *dev;
|
||||
|
@ -96,15 +118,32 @@ struct ad7606_state {
|
|||
* 16 * 16-bit samples + 64-bit timestamp
|
||||
*/
|
||||
unsigned short data[20] ____cacheline_aligned;
|
||||
__be16 d16[2];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ad7606_bus_ops - driver bus operations
|
||||
* @read_block function pointer for reading blocks of data
|
||||
* @sw_mode_config: pointer to a function which configured the device
|
||||
* for software mode
|
||||
* @reg_read function pointer for reading spi register
|
||||
* @reg_write function pointer for writing spi register
|
||||
* @write_mask function pointer for write spi register with mask
|
||||
* @rd_wr_cmd pointer to the function which calculates the spi address
|
||||
*/
|
||||
struct ad7606_bus_ops {
|
||||
/* more methods added in future? */
|
||||
int (*read_block)(struct device *dev, int num, void *data);
|
||||
int (*sw_mode_config)(struct iio_dev *indio_dev);
|
||||
int (*reg_read)(struct ad7606_state *st, unsigned int addr);
|
||||
int (*reg_write)(struct ad7606_state *st,
|
||||
unsigned int addr,
|
||||
unsigned int val);
|
||||
int (*write_mask)(struct ad7606_state *st,
|
||||
unsigned int addr,
|
||||
unsigned long mask,
|
||||
unsigned int val);
|
||||
u16 (*rd_wr_cmd)(int addr, char isWriteOp);
|
||||
};
|
||||
|
||||
int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,
|
||||
|
|
|
@ -53,10 +53,8 @@ static int ad7606_par_probe(struct platform_device *pdev)
|
|||
int irq;
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(&pdev->dev, "no irq: %d\n", irq);
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
addr = devm_ioremap_resource(&pdev->dev, res);
|
||||
|
|
|
@ -15,6 +15,51 @@
|
|||
|
||||
#define MAX_SPI_FREQ_HZ 23500000 /* VDRIVE above 4.75 V */
|
||||
|
||||
#define AD7616_CONFIGURATION_REGISTER 0x02
|
||||
#define AD7616_OS_MASK GENMASK(4, 2)
|
||||
#define AD7616_BURST_MODE BIT(6)
|
||||
#define AD7616_SEQEN_MODE BIT(5)
|
||||
#define AD7616_RANGE_CH_A_ADDR_OFF 0x04
|
||||
#define AD7616_RANGE_CH_B_ADDR_OFF 0x06
|
||||
/*
|
||||
* Range of channels from a group are stored in 2 registers.
|
||||
* 0, 1, 2, 3 in a register followed by 4, 5, 6, 7 in second register.
|
||||
* For channels from second group(8-15) the order is the same, only with
|
||||
* an offset of 2 for register address.
|
||||
*/
|
||||
#define AD7616_RANGE_CH_ADDR(ch) ((ch) >> 2)
|
||||
/* The range of the channel is stored on 2 bits*/
|
||||
#define AD7616_RANGE_CH_MSK(ch) (0b11 << (((ch) & 0b11) * 2))
|
||||
#define AD7616_RANGE_CH_MODE(ch, mode) ((mode) << ((((ch) & 0b11)) * 2))
|
||||
static const struct iio_chan_spec ad7616_sw_channels[] = {
|
||||
IIO_CHAN_SOFT_TIMESTAMP(16),
|
||||
AD7616_CHANNEL(0),
|
||||
AD7616_CHANNEL(1),
|
||||
AD7616_CHANNEL(2),
|
||||
AD7616_CHANNEL(3),
|
||||
AD7616_CHANNEL(4),
|
||||
AD7616_CHANNEL(5),
|
||||
AD7616_CHANNEL(6),
|
||||
AD7616_CHANNEL(7),
|
||||
AD7616_CHANNEL(8),
|
||||
AD7616_CHANNEL(9),
|
||||
AD7616_CHANNEL(10),
|
||||
AD7616_CHANNEL(11),
|
||||
AD7616_CHANNEL(12),
|
||||
AD7616_CHANNEL(13),
|
||||
AD7616_CHANNEL(14),
|
||||
AD7616_CHANNEL(15),
|
||||
};
|
||||
|
||||
static u16 ad7616_spi_rd_wr_cmd(int addr, char isWriteOp)
|
||||
{
|
||||
/*
|
||||
* The address of register consist of one w/r bit
|
||||
* 6 bits of address followed by one reserved bit.
|
||||
*/
|
||||
return ((addr & 0x7F) << 1) | ((isWriteOp & 0x1) << 7);
|
||||
}
|
||||
|
||||
static int ad7606_spi_read_block(struct device *dev,
|
||||
int count, void *buf)
|
||||
{
|
||||
|
@ -35,17 +80,145 @@ static int ad7606_spi_read_block(struct device *dev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int ad7606_spi_reg_read(struct ad7606_state *st, unsigned int addr)
|
||||
{
|
||||
struct spi_device *spi = to_spi_device(st->dev);
|
||||
struct spi_transfer t[] = {
|
||||
{
|
||||
.tx_buf = &st->d16[0],
|
||||
.len = 2,
|
||||
.cs_change = 0,
|
||||
}, {
|
||||
.rx_buf = &st->d16[1],
|
||||
.len = 2,
|
||||
},
|
||||
};
|
||||
int ret;
|
||||
|
||||
st->d16[0] = cpu_to_be16(st->bops->rd_wr_cmd(addr, 0) << 8);
|
||||
|
||||
ret = spi_sync_transfer(spi, t, ARRAY_SIZE(t));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return be16_to_cpu(st->d16[1]);
|
||||
}
|
||||
|
||||
static int ad7606_spi_reg_write(struct ad7606_state *st,
|
||||
unsigned int addr,
|
||||
unsigned int val)
|
||||
{
|
||||
struct spi_device *spi = to_spi_device(st->dev);
|
||||
|
||||
st->d16[0] = cpu_to_be16((st->bops->rd_wr_cmd(addr, 1) << 8) |
|
||||
(val & 0x1FF));
|
||||
|
||||
return spi_write(spi, &st->d16[0], sizeof(st->d16[0]));
|
||||
}
|
||||
|
||||
static int ad7606_spi_write_mask(struct ad7606_state *st,
|
||||
unsigned int addr,
|
||||
unsigned long mask,
|
||||
unsigned int val)
|
||||
{
|
||||
int readval;
|
||||
|
||||
readval = st->bops->reg_read(st, addr);
|
||||
if (readval < 0)
|
||||
return readval;
|
||||
|
||||
readval &= ~mask;
|
||||
readval |= val;
|
||||
|
||||
return st->bops->reg_write(st, addr, readval);
|
||||
}
|
||||
|
||||
static int ad7616_write_scale_sw(struct iio_dev *indio_dev, int ch, int val)
|
||||
{
|
||||
struct ad7606_state *st = iio_priv(indio_dev);
|
||||
unsigned int ch_addr, mode, ch_index;
|
||||
|
||||
|
||||
/*
|
||||
* Ad7616 has 16 channels divided in group A and group B.
|
||||
* The range of channels from A are stored in registers with address 4
|
||||
* while channels from B are stored in register with address 6.
|
||||
* The last bit from channels determines if it is from group A or B
|
||||
* because the order of channels in iio is 0A, 0B, 1A, 1B...
|
||||
*/
|
||||
ch_index = ch >> 1;
|
||||
|
||||
ch_addr = AD7616_RANGE_CH_ADDR(ch_index);
|
||||
|
||||
if ((ch & 0x1) == 0) /* channel A */
|
||||
ch_addr += AD7616_RANGE_CH_A_ADDR_OFF;
|
||||
else /* channel B */
|
||||
ch_addr += AD7616_RANGE_CH_B_ADDR_OFF;
|
||||
|
||||
/* 0b01 for 2.5v, 0b10 for 5v and 0b11 for 10v */
|
||||
mode = AD7616_RANGE_CH_MODE(ch_index, ((val + 1) & 0b11));
|
||||
return st->bops->write_mask(st, ch_addr, AD7616_RANGE_CH_MSK(ch_index),
|
||||
mode);
|
||||
}
|
||||
|
||||
static int ad7616_write_os_sw(struct iio_dev *indio_dev, int val)
|
||||
{
|
||||
struct ad7606_state *st = iio_priv(indio_dev);
|
||||
|
||||
return st->bops->write_mask(st, AD7616_CONFIGURATION_REGISTER,
|
||||
AD7616_OS_MASK, val << 2);
|
||||
}
|
||||
|
||||
static int ad7616_sw_mode_config(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct ad7606_state *st = iio_priv(indio_dev);
|
||||
|
||||
/*
|
||||
* Scale can be configured individually for each channel
|
||||
* in software mode.
|
||||
*/
|
||||
indio_dev->channels = ad7616_sw_channels;
|
||||
|
||||
st->write_scale = ad7616_write_scale_sw;
|
||||
st->write_os = &ad7616_write_os_sw;
|
||||
|
||||
/* Activate Burst mode and SEQEN MODE */
|
||||
return st->bops->write_mask(st,
|
||||
AD7616_CONFIGURATION_REGISTER,
|
||||
AD7616_BURST_MODE | AD7616_SEQEN_MODE,
|
||||
AD7616_BURST_MODE | AD7616_SEQEN_MODE);
|
||||
}
|
||||
|
||||
static const struct ad7606_bus_ops ad7606_spi_bops = {
|
||||
.read_block = ad7606_spi_read_block,
|
||||
};
|
||||
|
||||
static const struct ad7606_bus_ops ad7616_spi_bops = {
|
||||
.read_block = ad7606_spi_read_block,
|
||||
.reg_read = ad7606_spi_reg_read,
|
||||
.reg_write = ad7606_spi_reg_write,
|
||||
.write_mask = ad7606_spi_write_mask,
|
||||
.rd_wr_cmd = ad7616_spi_rd_wr_cmd,
|
||||
.sw_mode_config = ad7616_sw_mode_config,
|
||||
};
|
||||
|
||||
static int ad7606_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
const struct spi_device_id *id = spi_get_device_id(spi);
|
||||
const struct ad7606_bus_ops *bops;
|
||||
|
||||
switch (id->driver_data) {
|
||||
case ID_AD7616:
|
||||
bops = &ad7616_spi_bops;
|
||||
break;
|
||||
default:
|
||||
bops = &ad7606_spi_bops;
|
||||
break;
|
||||
}
|
||||
|
||||
return ad7606_probe(&spi->dev, spi->irq, NULL,
|
||||
id->name, id->driver_data,
|
||||
&ad7606_spi_bops);
|
||||
bops);
|
||||
}
|
||||
|
||||
static const struct spi_device_id ad7606_id_table[] = {
|
||||
|
|
|
@ -1179,10 +1179,8 @@ static int at91_adc_probe(struct platform_device *pdev)
|
|||
idev->info = &at91_adc_info;
|
||||
|
||||
st->irq = platform_get_irq(pdev, 0);
|
||||
if (st->irq < 0) {
|
||||
dev_err(&pdev->dev, "No IRQ ID is designated\n");
|
||||
if (st->irq < 0)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
|
||||
|
|
|
@ -225,10 +225,8 @@ static int axp288_adc_probe(struct platform_device *pdev)
|
|||
|
||||
info = iio_priv(indio_dev);
|
||||
info->irq = platform_get_irq(pdev, 0);
|
||||
if (info->irq < 0) {
|
||||
dev_err(&pdev->dev, "no irq resource?\n");
|
||||
if (info->irq < 0)
|
||||
return info->irq;
|
||||
}
|
||||
platform_set_drvdata(pdev, indio_dev);
|
||||
info->regmap = axp20x->regmap;
|
||||
/*
|
||||
|
|
|
@ -540,11 +540,8 @@ static int iproc_adc_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
adc_priv->irqno = platform_get_irq(pdev, 0);
|
||||
if (adc_priv->irqno <= 0) {
|
||||
dev_err(&pdev->dev, "platform_get_irq failed\n");
|
||||
ret = -ENODEV;
|
||||
return ret;
|
||||
}
|
||||
if (adc_priv->irqno <= 0)
|
||||
return -ENODEV;
|
||||
|
||||
ret = regmap_update_bits(adc_priv->regmap, IPROC_REGCTL2,
|
||||
IPROC_ADC_AUXIN_SCAN_ENA, 0);
|
||||
|
|
|
@ -337,10 +337,8 @@ static int da9150_gpadc_probe(struct platform_device *pdev)
|
|||
init_completion(&gpadc->complete);
|
||||
|
||||
irq = platform_get_irq_byname(pdev, "GPADC");
|
||||
if (irq < 0) {
|
||||
dev_err(dev, "Failed to get IRQ: %d\n", irq);
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
}
|
||||
|
||||
ret = devm_request_threaded_irq(dev, irq, NULL, da9150_gpadc_irq,
|
||||
IRQF_ONESHOT, "GPADC", gpadc);
|
||||
|
|
|
@ -357,11 +357,8 @@ static int envelope_detector_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
env->comp_irq = platform_get_irq_byname(pdev, "comp");
|
||||
if (env->comp_irq < 0) {
|
||||
if (env->comp_irq != -EPROBE_DEFER)
|
||||
dev_err(dev, "failed to get compare interrupt\n");
|
||||
if (env->comp_irq < 0)
|
||||
return env->comp_irq;
|
||||
}
|
||||
|
||||
ret = devm_request_irq(dev, env->comp_irq, envelope_detector_comp_isr,
|
||||
0, "envelope-detector", env);
|
||||
|
|
|
@ -805,10 +805,8 @@ static int exynos_adc_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(&pdev->dev, "no irq resource?\n");
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
}
|
||||
info->irq = irq;
|
||||
|
||||
irq = platform_get_irq(pdev, 1);
|
||||
|
|
|
@ -340,7 +340,6 @@ static int mx25_gcq_probe(struct platform_device *pdev)
|
|||
|
||||
priv->irq = platform_get_irq(pdev, 0);
|
||||
if (priv->irq <= 0) {
|
||||
dev_err(dev, "Failed to get IRQ\n");
|
||||
ret = priv->irq;
|
||||
if (!ret)
|
||||
ret = -ENXIO;
|
||||
|
|
|
@ -456,6 +456,11 @@ static irqreturn_t hi8435_trigger_handler(int irq, void *private)
|
|||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void hi8435_triggered_event_cleanup(void *data)
|
||||
{
|
||||
iio_triggered_event_cleanup(data);
|
||||
}
|
||||
|
||||
static int hi8435_probe(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *idev;
|
||||
|
@ -477,7 +482,7 @@ static int hi8435_probe(struct spi_device *spi)
|
|||
hi8435_writeb(priv, HI8435_CTRL_REG, 0);
|
||||
} else {
|
||||
udelay(5);
|
||||
gpiod_set_value(reset_gpio, 1);
|
||||
gpiod_set_value_cansleep(reset_gpio, 1);
|
||||
}
|
||||
|
||||
spi_set_drvdata(spi, idev);
|
||||
|
@ -513,27 +518,13 @@ static int hi8435_probe(struct spi_device *spi)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = iio_device_register(idev);
|
||||
if (ret < 0) {
|
||||
dev_err(&spi->dev, "unable to register device\n");
|
||||
goto unregister_triggered_event;
|
||||
}
|
||||
ret = devm_add_action_or_reset(&spi->dev,
|
||||
hi8435_triggered_event_cleanup,
|
||||
idev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
|
||||
unregister_triggered_event:
|
||||
iio_triggered_event_cleanup(idev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hi8435_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *idev = spi_get_drvdata(spi);
|
||||
|
||||
iio_device_unregister(idev);
|
||||
iio_triggered_event_cleanup(idev);
|
||||
|
||||
return 0;
|
||||
return devm_iio_device_register(&spi->dev, idev);
|
||||
}
|
||||
|
||||
static const struct of_device_id hi8435_dt_ids[] = {
|
||||
|
@ -554,7 +545,6 @@ static struct spi_driver hi8435_driver = {
|
|||
.of_match_table = of_match_ptr(hi8435_dt_ids),
|
||||
},
|
||||
.probe = hi8435_probe,
|
||||
.remove = hi8435_remove,
|
||||
.id_table = hi8435_id,
|
||||
};
|
||||
module_spi_driver(hi8435_driver);
|
||||
|
|
|
@ -492,10 +492,8 @@ static int imx7d_adc_probe(struct platform_device *pdev)
|
|||
return PTR_ERR(info->regs);
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(dev, "No irq resource?\n");
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
}
|
||||
|
||||
info->clk = devm_clk_get(dev, "adc");
|
||||
if (IS_ERR(info->clk)) {
|
||||
|
|
|
@ -172,10 +172,8 @@ static int lpc32xx_adc_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq <= 0) {
|
||||
dev_err(&pdev->dev, "failed getting interrupt resource\n");
|
||||
if (irq <= 0)
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
retval = devm_request_irq(&pdev->dev, irq, lpc32xx_adc_isr, 0,
|
||||
LPC32XXAD_NAME, st);
|
||||
|
|
|
@ -225,7 +225,6 @@ static int npcm_adc_probe(struct platform_device *pdev)
|
|||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq <= 0) {
|
||||
dev_err(dev, "failed getting interrupt resource\n");
|
||||
ret = -EINVAL;
|
||||
goto err_disable_clk;
|
||||
}
|
||||
|
|
|
@ -244,10 +244,8 @@ static int rockchip_saradc_probe(struct platform_device *pdev)
|
|||
init_completion(&info->completion);
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(&pdev->dev, "no irq resource?\n");
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
}
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, irq, rockchip_saradc_isr,
|
||||
0, dev_name(&pdev->dev), info);
|
||||
|
|
|
@ -504,88 +504,85 @@ static void sc27xx_adc_free_hwlock(void *_data)
|
|||
|
||||
static int sc27xx_adc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct sc27xx_adc_data *sc27xx_data;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*sc27xx_data));
|
||||
indio_dev = devm_iio_device_alloc(dev, sizeof(*sc27xx_data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
sc27xx_data = iio_priv(indio_dev);
|
||||
|
||||
sc27xx_data->regmap = dev_get_regmap(pdev->dev.parent, NULL);
|
||||
sc27xx_data->regmap = dev_get_regmap(dev->parent, NULL);
|
||||
if (!sc27xx_data->regmap) {
|
||||
dev_err(&pdev->dev, "failed to get ADC regmap\n");
|
||||
dev_err(dev, "failed to get ADC regmap\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(np, "reg", &sc27xx_data->base);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to get ADC base address\n");
|
||||
dev_err(dev, "failed to get ADC base address\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
sc27xx_data->irq = platform_get_irq(pdev, 0);
|
||||
if (sc27xx_data->irq < 0) {
|
||||
dev_err(&pdev->dev, "failed to get ADC irq number\n");
|
||||
if (sc27xx_data->irq < 0)
|
||||
return sc27xx_data->irq;
|
||||
}
|
||||
|
||||
ret = of_hwspin_lock_get_id(np, 0);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed to get hwspinlock id\n");
|
||||
dev_err(dev, "failed to get hwspinlock id\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
sc27xx_data->hwlock = hwspin_lock_request_specific(ret);
|
||||
if (!sc27xx_data->hwlock) {
|
||||
dev_err(&pdev->dev, "failed to request hwspinlock\n");
|
||||
dev_err(dev, "failed to request hwspinlock\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
ret = devm_add_action(&pdev->dev, sc27xx_adc_free_hwlock,
|
||||
ret = devm_add_action_or_reset(dev, sc27xx_adc_free_hwlock,
|
||||
sc27xx_data->hwlock);
|
||||
if (ret) {
|
||||
sc27xx_adc_free_hwlock(sc27xx_data->hwlock);
|
||||
dev_err(&pdev->dev, "failed to add hwspinlock action\n");
|
||||
dev_err(dev, "failed to add hwspinlock action\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
init_completion(&sc27xx_data->completion);
|
||||
sc27xx_data->dev = &pdev->dev;
|
||||
sc27xx_data->dev = dev;
|
||||
|
||||
ret = sc27xx_adc_enable(sc27xx_data);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to enable ADC module\n");
|
||||
dev_err(dev, "failed to enable ADC module\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = devm_add_action(&pdev->dev, sc27xx_adc_disable, sc27xx_data);
|
||||
ret = devm_add_action_or_reset(dev, sc27xx_adc_disable, sc27xx_data);
|
||||
if (ret) {
|
||||
sc27xx_adc_disable(sc27xx_data);
|
||||
dev_err(&pdev->dev, "failed to add ADC disable action\n");
|
||||
dev_err(dev, "failed to add ADC disable action\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = devm_request_threaded_irq(&pdev->dev, sc27xx_data->irq, NULL,
|
||||
ret = devm_request_threaded_irq(dev, sc27xx_data->irq, NULL,
|
||||
sc27xx_adc_isr, IRQF_ONESHOT,
|
||||
pdev->name, sc27xx_data);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to request ADC irq\n");
|
||||
dev_err(dev, "failed to request ADC irq\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
indio_dev->dev.parent = &pdev->dev;
|
||||
indio_dev->name = dev_name(&pdev->dev);
|
||||
indio_dev->dev.parent = dev;
|
||||
indio_dev->name = dev_name(dev);
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &sc27xx_info;
|
||||
indio_dev->channels = sc27xx_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(sc27xx_channels);
|
||||
ret = devm_iio_device_register(&pdev->dev, indio_dev);
|
||||
ret = devm_iio_device_register(dev, indio_dev);
|
||||
if (ret)
|
||||
dev_err(&pdev->dev, "could not register iio (ADC)");
|
||||
dev_err(dev, "could not register iio (ADC)");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -301,7 +301,6 @@ static int spear_adc_probe(struct platform_device *pdev)
|
|||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq <= 0) {
|
||||
dev_err(dev, "failed getting interrupt resource\n");
|
||||
ret = -EINVAL;
|
||||
goto errout2;
|
||||
}
|
||||
|
|
|
@ -14,9 +14,11 @@
|
|||
#include <linux/irqchip/chained_irq.h>
|
||||
#include <linux/irqdesc.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
|
@ -51,6 +53,17 @@
|
|||
|
||||
#define STM32_ADC_CORE_SLEEP_DELAY_MS 2000
|
||||
|
||||
/* SYSCFG registers */
|
||||
#define STM32MP1_SYSCFG_PMCSETR 0x04
|
||||
#define STM32MP1_SYSCFG_PMCCLRR 0x44
|
||||
|
||||
/* SYSCFG bit fields */
|
||||
#define STM32MP1_SYSCFG_ANASWVDD_MASK BIT(9)
|
||||
|
||||
/* SYSCFG capability flags */
|
||||
#define HAS_VBOOSTER BIT(0)
|
||||
#define HAS_ANASWVDD BIT(1)
|
||||
|
||||
/**
|
||||
* stm32_adc_common_regs - stm32 common registers, compatible dependent data
|
||||
* @csr: common status register offset
|
||||
|
@ -74,11 +87,13 @@ struct stm32_adc_priv;
|
|||
* @regs: common registers for all instances
|
||||
* @clk_sel: clock selection routine
|
||||
* @max_clk_rate_hz: maximum analog clock rate (Hz, from datasheet)
|
||||
* @has_syscfg: SYSCFG capability flags
|
||||
*/
|
||||
struct stm32_adc_priv_cfg {
|
||||
const struct stm32_adc_common_regs *regs;
|
||||
int (*clk_sel)(struct platform_device *, struct stm32_adc_priv *);
|
||||
u32 max_clk_rate_hz;
|
||||
unsigned int has_syscfg;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -87,22 +102,32 @@ struct stm32_adc_priv_cfg {
|
|||
* @domain: irq domain reference
|
||||
* @aclk: clock reference for the analog circuitry
|
||||
* @bclk: bus clock common for all ADCs, depends on part used
|
||||
* @booster: booster supply reference
|
||||
* @vdd: vdd supply reference
|
||||
* @vdda: vdda analog supply reference
|
||||
* @vref: regulator reference
|
||||
* @vdd_uv: vdd supply voltage (microvolts)
|
||||
* @vdda_uv: vdda supply voltage (microvolts)
|
||||
* @cfg: compatible configuration data
|
||||
* @common: common data for all ADC instances
|
||||
* @ccr_bak: backup CCR in low power mode
|
||||
* @syscfg: reference to syscon, system control registers
|
||||
*/
|
||||
struct stm32_adc_priv {
|
||||
int irq[STM32_ADC_MAX_ADCS];
|
||||
struct irq_domain *domain;
|
||||
struct clk *aclk;
|
||||
struct clk *bclk;
|
||||
struct regulator *booster;
|
||||
struct regulator *vdd;
|
||||
struct regulator *vdda;
|
||||
struct regulator *vref;
|
||||
int vdd_uv;
|
||||
int vdda_uv;
|
||||
const struct stm32_adc_priv_cfg *cfg;
|
||||
struct stm32_adc_common common;
|
||||
u32 ccr_bak;
|
||||
struct regmap *syscfg;
|
||||
};
|
||||
|
||||
static struct stm32_adc_priv *to_stm32_adc_priv(struct stm32_adc_common *com)
|
||||
|
@ -349,7 +374,6 @@ static int stm32_adc_irq_probe(struct platform_device *pdev,
|
|||
*/
|
||||
if (i && priv->irq[i] == -ENXIO)
|
||||
continue;
|
||||
dev_err(&pdev->dev, "failed to get irq\n");
|
||||
|
||||
return priv->irq[i];
|
||||
}
|
||||
|
@ -390,6 +414,82 @@ static void stm32_adc_irq_remove(struct platform_device *pdev,
|
|||
}
|
||||
}
|
||||
|
||||
static int stm32_adc_core_switches_supply_en(struct stm32_adc_priv *priv,
|
||||
struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* On STM32H7 and STM32MP1, the ADC inputs are multiplexed with analog
|
||||
* switches (via PCSEL) which have reduced performances when their
|
||||
* supply is below 2.7V (vdda by default):
|
||||
* - Voltage booster can be used, to get full ADC performances
|
||||
* (increases power consumption).
|
||||
* - Vdd can be used to supply them, if above 2.7V (STM32MP1 only).
|
||||
*
|
||||
* Recommended settings for ANASWVDD and EN_BOOSTER:
|
||||
* - vdda < 2.7V but vdd > 2.7V: ANASWVDD = 1, EN_BOOSTER = 0 (stm32mp1)
|
||||
* - vdda < 2.7V and vdd < 2.7V: ANASWVDD = 0, EN_BOOSTER = 1
|
||||
* - vdda >= 2.7V: ANASWVDD = 0, EN_BOOSTER = 0 (default)
|
||||
*/
|
||||
if (priv->vdda_uv < 2700000) {
|
||||
if (priv->syscfg && priv->vdd_uv > 2700000) {
|
||||
ret = regulator_enable(priv->vdd);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "vdd enable failed %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regmap_write(priv->syscfg,
|
||||
STM32MP1_SYSCFG_PMCSETR,
|
||||
STM32MP1_SYSCFG_ANASWVDD_MASK);
|
||||
if (ret < 0) {
|
||||
regulator_disable(priv->vdd);
|
||||
dev_err(dev, "vdd select failed, %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
dev_dbg(dev, "analog switches supplied by vdd\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (priv->booster) {
|
||||
/*
|
||||
* This is optional, as this is a trade-off between
|
||||
* analog performance and power consumption.
|
||||
*/
|
||||
ret = regulator_enable(priv->booster);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "booster enable failed %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
dev_dbg(dev, "analog switches supplied by booster\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Fallback using vdda (default), nothing to do */
|
||||
dev_dbg(dev, "analog switches supplied by vdda (%d uV)\n",
|
||||
priv->vdda_uv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void stm32_adc_core_switches_supply_dis(struct stm32_adc_priv *priv)
|
||||
{
|
||||
if (priv->vdda_uv < 2700000) {
|
||||
if (priv->syscfg && priv->vdd_uv > 2700000) {
|
||||
regmap_write(priv->syscfg, STM32MP1_SYSCFG_PMCCLRR,
|
||||
STM32MP1_SYSCFG_ANASWVDD_MASK);
|
||||
regulator_disable(priv->vdd);
|
||||
return;
|
||||
}
|
||||
if (priv->booster)
|
||||
regulator_disable(priv->booster);
|
||||
}
|
||||
}
|
||||
|
||||
static int stm32_adc_core_hw_start(struct device *dev)
|
||||
{
|
||||
struct stm32_adc_common *common = dev_get_drvdata(dev);
|
||||
|
@ -402,10 +502,21 @@ static int stm32_adc_core_hw_start(struct device *dev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
ret = regulator_get_voltage(priv->vdda);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "vdda get voltage failed, %d\n", ret);
|
||||
goto err_vdda_disable;
|
||||
}
|
||||
priv->vdda_uv = ret;
|
||||
|
||||
ret = stm32_adc_core_switches_supply_en(priv, dev);
|
||||
if (ret < 0)
|
||||
goto err_vdda_disable;
|
||||
|
||||
ret = regulator_enable(priv->vref);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "vref enable failed\n");
|
||||
goto err_vdda_disable;
|
||||
goto err_switches_dis;
|
||||
}
|
||||
|
||||
if (priv->bclk) {
|
||||
|
@ -433,6 +544,8 @@ static int stm32_adc_core_hw_start(struct device *dev)
|
|||
clk_disable_unprepare(priv->bclk);
|
||||
err_regulator_disable:
|
||||
regulator_disable(priv->vref);
|
||||
err_switches_dis:
|
||||
stm32_adc_core_switches_supply_dis(priv);
|
||||
err_vdda_disable:
|
||||
regulator_disable(priv->vdda);
|
||||
|
||||
|
@ -451,9 +564,80 @@ static void stm32_adc_core_hw_stop(struct device *dev)
|
|||
if (priv->bclk)
|
||||
clk_disable_unprepare(priv->bclk);
|
||||
regulator_disable(priv->vref);
|
||||
stm32_adc_core_switches_supply_dis(priv);
|
||||
regulator_disable(priv->vdda);
|
||||
}
|
||||
|
||||
static int stm32_adc_core_switches_probe(struct device *dev,
|
||||
struct stm32_adc_priv *priv)
|
||||
{
|
||||
struct device_node *np = dev->of_node;
|
||||
int ret;
|
||||
|
||||
/* Analog switches supply can be controlled by syscfg (optional) */
|
||||
priv->syscfg = syscon_regmap_lookup_by_phandle(np, "st,syscfg");
|
||||
if (IS_ERR(priv->syscfg)) {
|
||||
ret = PTR_ERR(priv->syscfg);
|
||||
if (ret != -ENODEV) {
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(dev, "Can't probe syscfg: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
priv->syscfg = NULL;
|
||||
}
|
||||
|
||||
/* Booster can be used to supply analog switches (optional) */
|
||||
if (priv->cfg->has_syscfg & HAS_VBOOSTER &&
|
||||
of_property_read_bool(np, "booster-supply")) {
|
||||
priv->booster = devm_regulator_get_optional(dev, "booster");
|
||||
if (IS_ERR(priv->booster)) {
|
||||
ret = PTR_ERR(priv->booster);
|
||||
if (ret != -ENODEV) {
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(dev, "can't get booster %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
priv->booster = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Vdd can be used to supply analog switches (optional) */
|
||||
if (priv->cfg->has_syscfg & HAS_ANASWVDD &&
|
||||
of_property_read_bool(np, "vdd-supply")) {
|
||||
priv->vdd = devm_regulator_get_optional(dev, "vdd");
|
||||
if (IS_ERR(priv->vdd)) {
|
||||
ret = PTR_ERR(priv->vdd);
|
||||
if (ret != -ENODEV) {
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(dev, "can't get vdd %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
priv->vdd = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (priv->vdd) {
|
||||
ret = regulator_enable(priv->vdd);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "vdd enable failed %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regulator_get_voltage(priv->vdd);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "vdd get voltage failed %d\n", ret);
|
||||
regulator_disable(priv->vdd);
|
||||
return ret;
|
||||
}
|
||||
priv->vdd_uv = ret;
|
||||
|
||||
regulator_disable(priv->vdd);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stm32_adc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct stm32_adc_priv *priv;
|
||||
|
@ -514,6 +698,10 @@ static int stm32_adc_probe(struct platform_device *pdev)
|
|||
priv->bclk = NULL;
|
||||
}
|
||||
|
||||
ret = stm32_adc_core_switches_probe(dev, priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pm_runtime_get_noresume(dev);
|
||||
pm_runtime_set_active(dev);
|
||||
pm_runtime_set_autosuspend_delay(dev, STM32_ADC_CORE_SLEEP_DELAY_MS);
|
||||
|
@ -611,12 +799,14 @@ static const struct stm32_adc_priv_cfg stm32h7_adc_priv_cfg = {
|
|||
.regs = &stm32h7_adc_common_regs,
|
||||
.clk_sel = stm32h7_adc_clk_sel,
|
||||
.max_clk_rate_hz = 36000000,
|
||||
.has_syscfg = HAS_VBOOSTER,
|
||||
};
|
||||
|
||||
static const struct stm32_adc_priv_cfg stm32mp1_adc_priv_cfg = {
|
||||
.regs = &stm32h7_adc_common_regs,
|
||||
.clk_sel = stm32h7_adc_clk_sel,
|
||||
.max_clk_rate_hz = 40000000,
|
||||
.has_syscfg = HAS_VBOOSTER | HAS_ANASWVDD,
|
||||
};
|
||||
|
||||
static const struct of_device_id stm32_adc_of_match[] = {
|
||||
|
|
|
@ -1919,10 +1919,8 @@ static int stm32_adc_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
adc->irq = platform_get_irq(pdev, 0);
|
||||
if (adc->irq < 0) {
|
||||
dev_err(&pdev->dev, "failed to get irq\n");
|
||||
if (adc->irq < 0)
|
||||
return adc->irq;
|
||||
}
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, adc->irq, stm32_adc_isr,
|
||||
0, pdev->name, adc);
|
||||
|
|
|
@ -1601,11 +1601,8 @@ static int stm32_dfsdm_adc_probe(struct platform_device *pdev)
|
|||
* So IRQ associated to filter instance 0 is dedicated to the Filter 0.
|
||||
*/
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
if (irq != -EPROBE_DEFER)
|
||||
dev_err(dev, "Failed to get IRQ: %d\n", irq);
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
}
|
||||
|
||||
ret = devm_request_irq(dev, irq, stm32_dfsdm_irq,
|
||||
0, pdev->name, adc);
|
||||
|
|
|
@ -460,10 +460,8 @@ static int sun4i_irq_init(struct platform_device *pdev, const char *name,
|
|||
atomic_set(atomic, 1);
|
||||
|
||||
ret = platform_get_irq_byname(pdev, name);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "no %s interrupt registered\n", name);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regmap_irq_get_virq(mfd_dev->regmap_irqc, ret);
|
||||
if (ret < 0) {
|
||||
|
|
|
@ -905,10 +905,8 @@ static int twl6030_gpadc_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(&pdev->dev, "failed to get irq\n");
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
}
|
||||
|
||||
ret = devm_request_threaded_irq(dev, irq, NULL,
|
||||
twl6030_gpadc_irq_handler,
|
||||
|
|
|
@ -821,10 +821,8 @@ static int vf610_adc_probe(struct platform_device *pdev)
|
|||
return PTR_ERR(info->regs);
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(&pdev->dev, "no irq resource?\n");
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
}
|
||||
|
||||
ret = devm_request_irq(info->dev, irq,
|
||||
vf610_adc_isr, 0,
|
||||
|
|
|
@ -63,10 +63,35 @@ static int cros_ec_sensors_read(struct iio_dev *indio_dev,
|
|||
|
||||
/* Save values */
|
||||
for (i = CROS_EC_SENSOR_X; i < CROS_EC_SENSOR_MAX_AXIS; i++)
|
||||
st->core.calib[i] =
|
||||
st->core.calib[i].offset =
|
||||
st->core.resp->sensor_offset.offset[i];
|
||||
ret = IIO_VAL_INT;
|
||||
*val = st->core.calib[idx];
|
||||
*val = st->core.calib[idx].offset;
|
||||
break;
|
||||
case IIO_CHAN_INFO_CALIBSCALE:
|
||||
st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_SCALE;
|
||||
st->core.param.sensor_offset.flags = 0;
|
||||
|
||||
ret = cros_ec_motion_send_host_cmd(&st->core, 0);
|
||||
if (ret == -EPROTO) {
|
||||
/* Reading calibscale is not supported on older EC. */
|
||||
*val = 1;
|
||||
*val2 = 0;
|
||||
ret = IIO_VAL_INT_PLUS_MICRO;
|
||||
break;
|
||||
} else if (ret) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Save values */
|
||||
for (i = CROS_EC_SENSOR_X; i < CROS_EC_SENSOR_MAX_AXIS; i++)
|
||||
st->core.calib[i].scale =
|
||||
st->core.resp->sensor_scale.scale[i];
|
||||
|
||||
*val = st->core.calib[idx].scale >> 15;
|
||||
*val2 = ((st->core.calib[idx].scale & 0x7FFF) * 1000000LL) /
|
||||
MOTION_SENSE_DEFAULT_SCALE;
|
||||
ret = IIO_VAL_INT_PLUS_MICRO;
|
||||
break;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_RANGE;
|
||||
|
@ -134,7 +159,7 @@ static int cros_ec_sensors_write(struct iio_dev *indio_dev,
|
|||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
st->core.calib[idx] = val;
|
||||
st->core.calib[idx].offset = val;
|
||||
|
||||
/* Send to EC for each axis, even if not complete */
|
||||
st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_OFFSET;
|
||||
|
@ -142,10 +167,25 @@ static int cros_ec_sensors_write(struct iio_dev *indio_dev,
|
|||
MOTION_SENSE_SET_OFFSET;
|
||||
for (i = CROS_EC_SENSOR_X; i < CROS_EC_SENSOR_MAX_AXIS; i++)
|
||||
st->core.param.sensor_offset.offset[i] =
|
||||
st->core.calib[i];
|
||||
st->core.calib[i].offset;
|
||||
st->core.param.sensor_offset.temp =
|
||||
EC_MOTION_SENSE_INVALID_CALIB_TEMP;
|
||||
|
||||
ret = cros_ec_motion_send_host_cmd(&st->core, 0);
|
||||
break;
|
||||
case IIO_CHAN_INFO_CALIBSCALE:
|
||||
st->core.calib[idx].scale = val;
|
||||
/* Send to EC for each axis, even if not complete */
|
||||
|
||||
st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_SCALE;
|
||||
st->core.param.sensor_offset.flags =
|
||||
MOTION_SENSE_SET_OFFSET;
|
||||
for (i = CROS_EC_SENSOR_X; i < CROS_EC_SENSOR_MAX_AXIS; i++)
|
||||
st->core.param.sensor_scale.scale[i] =
|
||||
st->core.calib[i].scale;
|
||||
st->core.param.sensor_scale.temp =
|
||||
EC_MOTION_SENSE_INVALID_CALIB_TEMP;
|
||||
|
||||
ret = cros_ec_motion_send_host_cmd(&st->core, 0);
|
||||
break;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
|
@ -175,6 +215,7 @@ static int cros_ec_sensors_write(struct iio_dev *indio_dev,
|
|||
static const struct iio_info ec_sensors_info = {
|
||||
.read_raw = &cros_ec_sensors_read,
|
||||
.write_raw = &cros_ec_sensors_write,
|
||||
.read_avail = &cros_ec_sensors_core_read_avail,
|
||||
};
|
||||
|
||||
static int cros_ec_sensors_probe(struct platform_device *pdev)
|
||||
|
@ -206,11 +247,14 @@ static int cros_ec_sensors_probe(struct platform_device *pdev)
|
|||
/* Common part */
|
||||
channel->info_mask_separate =
|
||||
BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_CALIBBIAS);
|
||||
BIT(IIO_CHAN_INFO_CALIBBIAS) |
|
||||
BIT(IIO_CHAN_INFO_CALIBSCALE);
|
||||
channel->info_mask_shared_by_all =
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_FREQUENCY) |
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ);
|
||||
channel->info_mask_shared_by_all_available =
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ);
|
||||
channel->scan_type.realbits = CROS_EC_SENSOR_BITS;
|
||||
channel->scan_type.storagebits = CROS_EC_SENSOR_BITS;
|
||||
channel->scan_index = i;
|
||||
|
|
|
@ -25,6 +25,62 @@ static char *cros_ec_loc[] = {
|
|||
[MOTIONSENSE_LOC_MAX] = "unknown",
|
||||
};
|
||||
|
||||
static int cros_ec_get_host_cmd_version_mask(struct cros_ec_device *ec_dev,
|
||||
u16 cmd_offset, u16 cmd, u32 *mask)
|
||||
{
|
||||
int ret;
|
||||
struct {
|
||||
struct cros_ec_command msg;
|
||||
union {
|
||||
struct ec_params_get_cmd_versions params;
|
||||
struct ec_response_get_cmd_versions resp;
|
||||
};
|
||||
} __packed buf = {
|
||||
.msg = {
|
||||
.command = EC_CMD_GET_CMD_VERSIONS + cmd_offset,
|
||||
.insize = sizeof(struct ec_response_get_cmd_versions),
|
||||
.outsize = sizeof(struct ec_params_get_cmd_versions)
|
||||
},
|
||||
.params = {.cmd = cmd}
|
||||
};
|
||||
|
||||
ret = cros_ec_cmd_xfer_status(ec_dev, &buf.msg);
|
||||
if (ret >= 0)
|
||||
*mask = buf.resp.version_mask;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void get_default_min_max_freq(enum motionsensor_type type,
|
||||
u32 *min_freq,
|
||||
u32 *max_freq)
|
||||
{
|
||||
switch (type) {
|
||||
case MOTIONSENSE_TYPE_ACCEL:
|
||||
case MOTIONSENSE_TYPE_GYRO:
|
||||
*min_freq = 12500;
|
||||
*max_freq = 100000;
|
||||
break;
|
||||
case MOTIONSENSE_TYPE_MAG:
|
||||
*min_freq = 5000;
|
||||
*max_freq = 25000;
|
||||
break;
|
||||
case MOTIONSENSE_TYPE_PROX:
|
||||
case MOTIONSENSE_TYPE_LIGHT:
|
||||
*min_freq = 100;
|
||||
*max_freq = 50000;
|
||||
break;
|
||||
case MOTIONSENSE_TYPE_BARO:
|
||||
*min_freq = 250;
|
||||
*max_freq = 20000;
|
||||
break;
|
||||
case MOTIONSENSE_TYPE_ACTIVITY:
|
||||
default:
|
||||
*min_freq = 0;
|
||||
*max_freq = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int cros_ec_sensors_core_init(struct platform_device *pdev,
|
||||
struct iio_dev *indio_dev,
|
||||
bool physical_device)
|
||||
|
@ -33,6 +89,8 @@ int cros_ec_sensors_core_init(struct platform_device *pdev,
|
|||
struct cros_ec_sensors_core_state *state = iio_priv(indio_dev);
|
||||
struct cros_ec_dev *ec = dev_get_drvdata(pdev->dev.parent);
|
||||
struct cros_ec_sensor_platform *sensor_platform = dev_get_platdata(dev);
|
||||
u32 ver_mask;
|
||||
int ret;
|
||||
|
||||
platform_set_drvdata(pdev, indio_dev);
|
||||
|
||||
|
@ -47,8 +105,15 @@ int cros_ec_sensors_core_init(struct platform_device *pdev,
|
|||
|
||||
mutex_init(&state->cmd_lock);
|
||||
|
||||
ret = cros_ec_get_host_cmd_version_mask(state->ec,
|
||||
ec->cmd_offset,
|
||||
EC_CMD_MOTION_SENSE_CMD,
|
||||
&ver_mask);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Set up the host command structure. */
|
||||
state->msg->version = 2;
|
||||
state->msg->version = fls(ver_mask) - 1;
|
||||
state->msg->command = EC_CMD_MOTION_SENSE_CMD + ec->cmd_offset;
|
||||
state->msg->outsize = sizeof(struct ec_params_motion_sense);
|
||||
|
||||
|
@ -60,12 +125,29 @@ int cros_ec_sensors_core_init(struct platform_device *pdev,
|
|||
|
||||
state->param.cmd = MOTIONSENSE_CMD_INFO;
|
||||
state->param.info.sensor_num = sensor_platform->sensor_num;
|
||||
if (cros_ec_motion_send_host_cmd(state, 0)) {
|
||||
ret = cros_ec_motion_send_host_cmd(state, 0);
|
||||
if (ret) {
|
||||
dev_warn(dev, "Can not access sensor info\n");
|
||||
return -EIO;
|
||||
return ret;
|
||||
}
|
||||
state->type = state->resp->info.type;
|
||||
state->loc = state->resp->info.location;
|
||||
|
||||
/* Set sign vector, only used for backward compatibility. */
|
||||
memset(state->sign, 1, CROS_EC_SENSOR_MAX_AXIS);
|
||||
|
||||
/* 0 is a correct value used to stop the device */
|
||||
state->frequencies[0] = 0;
|
||||
if (state->msg->version < 3) {
|
||||
get_default_min_max_freq(state->resp->info.type,
|
||||
&state->frequencies[1],
|
||||
&state->frequencies[2]);
|
||||
} else {
|
||||
state->frequencies[1] =
|
||||
state->resp->info_3.min_frequency;
|
||||
state->frequencies[2] =
|
||||
state->resp->info_3.max_frequency;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -86,7 +168,7 @@ int cros_ec_motion_send_host_cmd(struct cros_ec_sensors_core_state *state,
|
|||
|
||||
ret = cros_ec_cmd_xfer_status(state->ec, state->msg);
|
||||
if (ret < 0)
|
||||
return -EIO;
|
||||
return ret;
|
||||
|
||||
if (ret &&
|
||||
state->resp != (struct ec_response_motion_sense *)state->msg->data)
|
||||
|
@ -118,7 +200,7 @@ static ssize_t cros_ec_sensors_calibrate(struct iio_dev *indio_dev,
|
|||
} else {
|
||||
/* Save values */
|
||||
for (i = CROS_EC_SENSOR_X; i < CROS_EC_SENSOR_MAX_AXIS; i++)
|
||||
st->calib[i] = st->resp->perform_calib.offset[i];
|
||||
st->calib[i].offset = st->resp->perform_calib.offset[i];
|
||||
}
|
||||
mutex_unlock(&st->cmd_lock);
|
||||
|
||||
|
@ -268,6 +350,7 @@ static int cros_ec_sensors_read_data_unsafe(struct iio_dev *indio_dev,
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*data *= st->sign[i];
|
||||
data++;
|
||||
}
|
||||
|
||||
|
@ -396,7 +479,7 @@ int cros_ec_sensors_core_read(struct cros_ec_sensors_core_state *st,
|
|||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
int ret = IIO_VAL_INT;
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
|
@ -404,22 +487,27 @@ int cros_ec_sensors_core_read(struct cros_ec_sensors_core_state *st,
|
|||
st->param.ec_rate.data =
|
||||
EC_MOTION_SENSE_NO_VALUE;
|
||||
|
||||
if (cros_ec_motion_send_host_cmd(st, 0))
|
||||
ret = -EIO;
|
||||
else
|
||||
*val = st->resp->ec_rate.ret;
|
||||
ret = cros_ec_motion_send_host_cmd(st, 0);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
*val = st->resp->ec_rate.ret;
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
case IIO_CHAN_INFO_FREQUENCY:
|
||||
st->param.cmd = MOTIONSENSE_CMD_SENSOR_ODR;
|
||||
st->param.sensor_odr.data =
|
||||
EC_MOTION_SENSE_NO_VALUE;
|
||||
|
||||
if (cros_ec_motion_send_host_cmd(st, 0))
|
||||
ret = -EIO;
|
||||
else
|
||||
*val = st->resp->sensor_odr.ret;
|
||||
ret = cros_ec_motion_send_host_cmd(st, 0);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
*val = st->resp->sensor_odr.ret;
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -427,11 +515,32 @@ int cros_ec_sensors_core_read(struct cros_ec_sensors_core_state *st,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(cros_ec_sensors_core_read);
|
||||
|
||||
int cros_ec_sensors_core_read_avail(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
const int **vals,
|
||||
int *type,
|
||||
int *length,
|
||||
long mask)
|
||||
{
|
||||
struct cros_ec_sensors_core_state *state = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
*length = ARRAY_SIZE(state->frequencies);
|
||||
*vals = (const int *)&state->frequencies;
|
||||
*type = IIO_VAL_INT;
|
||||
return IIO_AVAIL_LIST;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cros_ec_sensors_core_read_avail);
|
||||
|
||||
int cros_ec_sensors_core_write(struct cros_ec_sensors_core_state *st,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
int ret = 0;
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_FREQUENCY:
|
||||
|
@ -441,17 +550,16 @@ int cros_ec_sensors_core_write(struct cros_ec_sensors_core_state *st,
|
|||
/* Always roundup, so caller gets at least what it asks for. */
|
||||
st->param.sensor_odr.roundup = 1;
|
||||
|
||||
if (cros_ec_motion_send_host_cmd(st, 0))
|
||||
ret = -EIO;
|
||||
ret = cros_ec_motion_send_host_cmd(st, 0);
|
||||
break;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
st->param.cmd = MOTIONSENSE_CMD_EC_RATE;
|
||||
st->param.ec_rate.data = val;
|
||||
|
||||
if (cros_ec_motion_send_host_cmd(st, 0))
|
||||
ret = -EIO;
|
||||
else
|
||||
st->curr_sampl_freq = val;
|
||||
ret = cros_ec_motion_send_host_cmd(st, 0);
|
||||
if (ret)
|
||||
break;
|
||||
st->curr_sampl_freq = val;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
|
|
|
@ -17,15 +17,16 @@
|
|||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/irqreturn.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include <linux/iio/common/st_sensors.h>
|
||||
|
||||
|
||||
static int st_sensors_get_buffer_element(struct iio_dev *indio_dev, u8 *buf)
|
||||
{
|
||||
int i;
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
unsigned int num_data_channels = sdata->num_data_channels;
|
||||
int i;
|
||||
|
||||
for_each_set_bit(i, indio_dev->active_scan_mask, num_data_channels) {
|
||||
const struct iio_chan_spec *channel = &indio_dev->channels[i];
|
||||
|
@ -36,11 +37,8 @@ static int st_sensors_get_buffer_element(struct iio_dev *indio_dev, u8 *buf)
|
|||
channel->scan_type.storagebits >> 3;
|
||||
|
||||
buf = PTR_ALIGN(buf, storage_bytes);
|
||||
if (sdata->tf->read_multiple_byte(&sdata->tb, sdata->dev,
|
||||
channel->address,
|
||||
bytes_to_read, buf,
|
||||
sdata->multiread_bit) <
|
||||
bytes_to_read)
|
||||
if (regmap_bulk_read(sdata->regmap, channel->address,
|
||||
buf, bytes_to_read) < 0)
|
||||
return -EIO;
|
||||
|
||||
/* Advance the buffer pointer */
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <asm/unaligned.h>
|
||||
#include <linux/iio/common/st_sensors.h>
|
||||
|
||||
|
@ -28,19 +29,10 @@ static inline u32 st_sensors_get_unaligned_le24(const u8 *p)
|
|||
int st_sensors_write_data_with_mask(struct iio_dev *indio_dev,
|
||||
u8 reg_addr, u8 mask, u8 data)
|
||||
{
|
||||
int err;
|
||||
u8 new_data;
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
|
||||
err = sdata->tf->read_byte(&sdata->tb, sdata->dev, reg_addr, &new_data);
|
||||
if (err < 0)
|
||||
goto st_sensors_write_data_with_mask_error;
|
||||
|
||||
new_data = ((new_data & (~mask)) | ((data << __ffs(mask)) & mask));
|
||||
err = sdata->tf->write_byte(&sdata->tb, sdata->dev, reg_addr, new_data);
|
||||
|
||||
st_sensors_write_data_with_mask_error:
|
||||
return err;
|
||||
return regmap_update_bits(sdata->regmap,
|
||||
reg_addr, mask, data << __ffs(mask));
|
||||
}
|
||||
|
||||
int st_sensors_debugfs_reg_access(struct iio_dev *indio_dev,
|
||||
|
@ -48,19 +40,15 @@ int st_sensors_debugfs_reg_access(struct iio_dev *indio_dev,
|
|||
unsigned *readval)
|
||||
{
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
u8 readdata;
|
||||
int err;
|
||||
|
||||
if (!readval)
|
||||
return sdata->tf->write_byte(&sdata->tb, sdata->dev,
|
||||
(u8)reg, (u8)writeval);
|
||||
return regmap_write(sdata->regmap, reg, writeval);
|
||||
|
||||
err = sdata->tf->read_byte(&sdata->tb, sdata->dev, (u8)reg, &readdata);
|
||||
err = regmap_read(sdata->regmap, reg, readval);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
*readval = (unsigned)readdata;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(st_sensors_debugfs_reg_access);
|
||||
|
@ -545,7 +533,7 @@ int st_sensors_set_fullscale_by_gain(struct iio_dev *indio_dev, int scale)
|
|||
EXPORT_SYMBOL(st_sensors_set_fullscale_by_gain);
|
||||
|
||||
static int st_sensors_read_axis_data(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *ch, int *data)
|
||||
struct iio_chan_spec const *ch, int *data)
|
||||
{
|
||||
int err;
|
||||
u8 *outdata;
|
||||
|
@ -554,13 +542,12 @@ static int st_sensors_read_axis_data(struct iio_dev *indio_dev,
|
|||
|
||||
byte_for_channel = DIV_ROUND_UP(ch->scan_type.realbits +
|
||||
ch->scan_type.shift, 8);
|
||||
outdata = kmalloc(byte_for_channel, GFP_KERNEL);
|
||||
outdata = kmalloc(byte_for_channel, GFP_DMA | GFP_KERNEL);
|
||||
if (!outdata)
|
||||
return -ENOMEM;
|
||||
|
||||
err = sdata->tf->read_multiple_byte(&sdata->tb, sdata->dev,
|
||||
ch->address, byte_for_channel,
|
||||
outdata, sdata->multiread_bit);
|
||||
err = regmap_bulk_read(sdata->regmap, ch->address,
|
||||
outdata, byte_for_channel);
|
||||
if (err < 0)
|
||||
goto st_sensors_free_memory;
|
||||
|
||||
|
@ -608,69 +595,55 @@ int st_sensors_read_info_raw(struct iio_dev *indio_dev,
|
|||
}
|
||||
EXPORT_SYMBOL(st_sensors_read_info_raw);
|
||||
|
||||
static int st_sensors_init_interface_mode(struct iio_dev *indio_dev,
|
||||
const struct st_sensor_settings *sensor_settings)
|
||||
/*
|
||||
* st_sensors_get_settings_index() - get index of the sensor settings for a
|
||||
* specific device from list of settings
|
||||
* @name: device name buffer reference.
|
||||
* @list: sensor settings list.
|
||||
* @list_length: length of sensor settings list.
|
||||
*
|
||||
* Return: non negative number on success (valid index),
|
||||
* negative error code otherwise.
|
||||
*/
|
||||
int st_sensors_get_settings_index(const char *name,
|
||||
const struct st_sensor_settings *list,
|
||||
const int list_length)
|
||||
{
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
struct device_node *np = sdata->dev->of_node;
|
||||
struct st_sensors_platform_data *pdata;
|
||||
int i, n;
|
||||
|
||||
pdata = (struct st_sensors_platform_data *)sdata->dev->platform_data;
|
||||
if (((np && of_property_read_bool(np, "spi-3wire")) ||
|
||||
(pdata && pdata->spi_3wire)) && sensor_settings->sim.addr) {
|
||||
int err;
|
||||
|
||||
err = sdata->tf->write_byte(&sdata->tb, sdata->dev,
|
||||
sensor_settings->sim.addr,
|
||||
sensor_settings->sim.value);
|
||||
if (err < 0) {
|
||||
dev_err(&indio_dev->dev,
|
||||
"failed to init interface mode\n");
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int st_sensors_check_device_support(struct iio_dev *indio_dev,
|
||||
int num_sensors_list,
|
||||
const struct st_sensor_settings *sensor_settings)
|
||||
{
|
||||
int i, n, err = 0;
|
||||
u8 wai;
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
|
||||
for (i = 0; i < num_sensors_list; i++) {
|
||||
for (i = 0; i < list_length; i++) {
|
||||
for (n = 0; n < ST_SENSORS_MAX_4WAI; n++) {
|
||||
if (strcmp(indio_dev->name,
|
||||
sensor_settings[i].sensors_supported[n]) == 0) {
|
||||
break;
|
||||
}
|
||||
if (strcmp(name, list[i].sensors_supported[n]) == 0)
|
||||
return i;
|
||||
}
|
||||
if (n < ST_SENSORS_MAX_4WAI)
|
||||
break;
|
||||
}
|
||||
if (i == num_sensors_list) {
|
||||
dev_err(&indio_dev->dev, "device name %s not recognized.\n",
|
||||
indio_dev->name);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
err = st_sensors_init_interface_mode(indio_dev, &sensor_settings[i]);
|
||||
if (err < 0)
|
||||
return err;
|
||||
return -ENODEV;
|
||||
}
|
||||
EXPORT_SYMBOL(st_sensors_get_settings_index);
|
||||
|
||||
if (sensor_settings[i].wai_addr) {
|
||||
err = sdata->tf->read_byte(&sdata->tb, sdata->dev,
|
||||
sensor_settings[i].wai_addr, &wai);
|
||||
/*
|
||||
* st_sensors_verify_id() - verify sensor ID (WhoAmI) is matching with the
|
||||
* expected value
|
||||
* @indio_dev: IIO device reference.
|
||||
*
|
||||
* Return: 0 on success (valid sensor ID), else a negative error code.
|
||||
*/
|
||||
int st_sensors_verify_id(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
int wai, err;
|
||||
|
||||
if (sdata->sensor_settings->wai_addr) {
|
||||
err = regmap_read(sdata->regmap,
|
||||
sdata->sensor_settings->wai_addr, &wai);
|
||||
if (err < 0) {
|
||||
dev_err(&indio_dev->dev,
|
||||
"failed to read Who-Am-I register.\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
if (sensor_settings[i].wai != wai) {
|
||||
if (sdata->sensor_settings->wai != wai) {
|
||||
dev_err(&indio_dev->dev,
|
||||
"%s: WhoAmI mismatch (0x%x).\n",
|
||||
indio_dev->name, wai);
|
||||
|
@ -678,12 +651,9 @@ int st_sensors_check_device_support(struct iio_dev *indio_dev,
|
|||
}
|
||||
}
|
||||
|
||||
sdata->sensor_settings =
|
||||
(struct st_sensor_settings *)&sensor_settings[i];
|
||||
|
||||
return i;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(st_sensors_check_device_support);
|
||||
EXPORT_SYMBOL(st_sensors_verify_id);
|
||||
|
||||
ssize_t st_sensors_sysfs_sampling_frequency_avail(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
|
|
|
@ -13,68 +13,58 @@
|
|||
#include <linux/iio/iio.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include <linux/iio/common/st_sensors_i2c.h>
|
||||
|
||||
|
||||
#define ST_SENSORS_I2C_MULTIREAD 0x80
|
||||
|
||||
static unsigned int st_sensors_i2c_get_irq(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
|
||||
return to_i2c_client(sdata->dev)->irq;
|
||||
}
|
||||
|
||||
static int st_sensors_i2c_read_byte(struct st_sensor_transfer_buffer *tb,
|
||||
struct device *dev, u8 reg_addr, u8 *res_byte)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = i2c_smbus_read_byte_data(to_i2c_client(dev), reg_addr);
|
||||
if (err < 0)
|
||||
goto st_accel_i2c_read_byte_error;
|
||||
|
||||
*res_byte = err & 0xff;
|
||||
|
||||
st_accel_i2c_read_byte_error:
|
||||
return err < 0 ? err : 0;
|
||||
}
|
||||
|
||||
static int st_sensors_i2c_read_multiple_byte(
|
||||
struct st_sensor_transfer_buffer *tb, struct device *dev,
|
||||
u8 reg_addr, int len, u8 *data, bool multiread_bit)
|
||||
{
|
||||
if (multiread_bit)
|
||||
reg_addr |= ST_SENSORS_I2C_MULTIREAD;
|
||||
|
||||
return i2c_smbus_read_i2c_block_data_or_emulated(to_i2c_client(dev),
|
||||
reg_addr, len, data);
|
||||
}
|
||||
|
||||
static int st_sensors_i2c_write_byte(struct st_sensor_transfer_buffer *tb,
|
||||
struct device *dev, u8 reg_addr, u8 data)
|
||||
{
|
||||
return i2c_smbus_write_byte_data(to_i2c_client(dev), reg_addr, data);
|
||||
}
|
||||
|
||||
static const struct st_sensor_transfer_function st_sensors_tf_i2c = {
|
||||
.read_byte = st_sensors_i2c_read_byte,
|
||||
.write_byte = st_sensors_i2c_write_byte,
|
||||
.read_multiple_byte = st_sensors_i2c_read_multiple_byte,
|
||||
static const struct regmap_config st_sensors_i2c_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
};
|
||||
|
||||
void st_sensors_i2c_configure(struct iio_dev *indio_dev,
|
||||
struct i2c_client *client, struct st_sensor_data *sdata)
|
||||
static const struct regmap_config st_sensors_i2c_regmap_multiread_bit_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.read_flag_mask = ST_SENSORS_I2C_MULTIREAD,
|
||||
};
|
||||
|
||||
/*
|
||||
* st_sensors_i2c_configure() - configure I2C interface
|
||||
* @indio_dev: IIO device reference.
|
||||
* @client: i2c client reference.
|
||||
*
|
||||
* Return: 0 on success, else a negative error code.
|
||||
*/
|
||||
int st_sensors_i2c_configure(struct iio_dev *indio_dev,
|
||||
struct i2c_client *client)
|
||||
{
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
const struct regmap_config *config;
|
||||
|
||||
if (sdata->sensor_settings->multi_read_bit)
|
||||
config = &st_sensors_i2c_regmap_multiread_bit_config;
|
||||
else
|
||||
config = &st_sensors_i2c_regmap_config;
|
||||
|
||||
sdata->regmap = devm_regmap_init_i2c(client, config);
|
||||
if (IS_ERR(sdata->regmap)) {
|
||||
dev_err(&client->dev, "Failed to register i2c regmap (%d)\n",
|
||||
(int)PTR_ERR(sdata->regmap));
|
||||
return PTR_ERR(sdata->regmap);
|
||||
}
|
||||
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->name = client->name;
|
||||
|
||||
sdata->dev = &client->dev;
|
||||
sdata->tf = &st_sensors_tf_i2c;
|
||||
sdata->get_irq_data_ready = st_sensors_i2c_get_irq;
|
||||
sdata->irq = client->irq;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(st_sensors_i2c_configure);
|
||||
|
||||
|
|
|
@ -11,108 +11,108 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include <linux/iio/common/st_sensors_spi.h>
|
||||
|
||||
#include "st_sensors_core.h"
|
||||
|
||||
#define ST_SENSORS_SPI_MULTIREAD 0xc0
|
||||
#define ST_SENSORS_SPI_READ 0x80
|
||||
|
||||
static unsigned int st_sensors_spi_get_irq(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
|
||||
return to_spi_device(sdata->dev)->irq;
|
||||
}
|
||||
|
||||
static int st_sensors_spi_read(struct st_sensor_transfer_buffer *tb,
|
||||
struct device *dev, u8 reg_addr, int len, u8 *data, bool multiread_bit)
|
||||
{
|
||||
int err;
|
||||
|
||||
struct spi_transfer xfers[] = {
|
||||
{
|
||||
.tx_buf = tb->tx_buf,
|
||||
.bits_per_word = 8,
|
||||
.len = 1,
|
||||
},
|
||||
{
|
||||
.rx_buf = tb->rx_buf,
|
||||
.bits_per_word = 8,
|
||||
.len = len,
|
||||
}
|
||||
};
|
||||
|
||||
mutex_lock(&tb->buf_lock);
|
||||
if ((multiread_bit) && (len > 1))
|
||||
tb->tx_buf[0] = reg_addr | ST_SENSORS_SPI_MULTIREAD;
|
||||
else
|
||||
tb->tx_buf[0] = reg_addr | ST_SENSORS_SPI_READ;
|
||||
|
||||
err = spi_sync_transfer(to_spi_device(dev), xfers, ARRAY_SIZE(xfers));
|
||||
if (err)
|
||||
goto acc_spi_read_error;
|
||||
|
||||
memcpy(data, tb->rx_buf, len);
|
||||
mutex_unlock(&tb->buf_lock);
|
||||
return len;
|
||||
|
||||
acc_spi_read_error:
|
||||
mutex_unlock(&tb->buf_lock);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int st_sensors_spi_read_byte(struct st_sensor_transfer_buffer *tb,
|
||||
struct device *dev, u8 reg_addr, u8 *res_byte)
|
||||
{
|
||||
return st_sensors_spi_read(tb, dev, reg_addr, 1, res_byte, false);
|
||||
}
|
||||
|
||||
static int st_sensors_spi_read_multiple_byte(
|
||||
struct st_sensor_transfer_buffer *tb, struct device *dev,
|
||||
u8 reg_addr, int len, u8 *data, bool multiread_bit)
|
||||
{
|
||||
return st_sensors_spi_read(tb, dev, reg_addr, len, data, multiread_bit);
|
||||
}
|
||||
|
||||
static int st_sensors_spi_write_byte(struct st_sensor_transfer_buffer *tb,
|
||||
struct device *dev, u8 reg_addr, u8 data)
|
||||
{
|
||||
int err;
|
||||
|
||||
struct spi_transfer xfers = {
|
||||
.tx_buf = tb->tx_buf,
|
||||
.bits_per_word = 8,
|
||||
.len = 2,
|
||||
};
|
||||
|
||||
mutex_lock(&tb->buf_lock);
|
||||
tb->tx_buf[0] = reg_addr;
|
||||
tb->tx_buf[1] = data;
|
||||
|
||||
err = spi_sync_transfer(to_spi_device(dev), &xfers, 1);
|
||||
mutex_unlock(&tb->buf_lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct st_sensor_transfer_function st_sensors_tf_spi = {
|
||||
.read_byte = st_sensors_spi_read_byte,
|
||||
.write_byte = st_sensors_spi_write_byte,
|
||||
.read_multiple_byte = st_sensors_spi_read_multiple_byte,
|
||||
static const struct regmap_config st_sensors_spi_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
};
|
||||
|
||||
void st_sensors_spi_configure(struct iio_dev *indio_dev,
|
||||
struct spi_device *spi, struct st_sensor_data *sdata)
|
||||
static const struct regmap_config st_sensors_spi_regmap_multiread_bit_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.read_flag_mask = ST_SENSORS_SPI_MULTIREAD,
|
||||
};
|
||||
|
||||
/*
|
||||
* st_sensors_is_spi_3_wire() - check if SPI 3-wire mode has been selected
|
||||
* @spi: spi device reference.
|
||||
*
|
||||
* Return: true if SPI 3-wire mode is selected, false otherwise.
|
||||
*/
|
||||
static bool st_sensors_is_spi_3_wire(struct spi_device *spi)
|
||||
{
|
||||
struct device_node *np = spi->dev.of_node;
|
||||
struct st_sensors_platform_data *pdata;
|
||||
|
||||
pdata = (struct st_sensors_platform_data *)spi->dev.platform_data;
|
||||
if ((np && of_property_read_bool(np, "spi-3wire")) ||
|
||||
(pdata && pdata->spi_3wire)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* st_sensors_configure_spi_3_wire() - configure SPI 3-wire if needed
|
||||
* @spi: spi device reference.
|
||||
* @settings: sensor specific settings reference.
|
||||
*
|
||||
* Return: 0 on success, else a negative error code.
|
||||
*/
|
||||
static int st_sensors_configure_spi_3_wire(struct spi_device *spi,
|
||||
struct st_sensor_settings *settings)
|
||||
{
|
||||
if (settings->sim.addr) {
|
||||
u8 buffer[] = {
|
||||
settings->sim.addr,
|
||||
settings->sim.value
|
||||
};
|
||||
|
||||
return spi_write(spi, buffer, 2);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* st_sensors_spi_configure() - configure SPI interface
|
||||
* @indio_dev: IIO device reference.
|
||||
* @spi: spi device reference.
|
||||
*
|
||||
* Return: 0 on success, else a negative error code.
|
||||
*/
|
||||
int st_sensors_spi_configure(struct iio_dev *indio_dev,
|
||||
struct spi_device *spi)
|
||||
{
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
const struct regmap_config *config;
|
||||
int err;
|
||||
|
||||
if (st_sensors_is_spi_3_wire(spi)) {
|
||||
err = st_sensors_configure_spi_3_wire(spi,
|
||||
sdata->sensor_settings);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (sdata->sensor_settings->multi_read_bit)
|
||||
config = &st_sensors_spi_regmap_multiread_bit_config;
|
||||
else
|
||||
config = &st_sensors_spi_regmap_config;
|
||||
|
||||
sdata->regmap = devm_regmap_init_spi(spi, config);
|
||||
if (IS_ERR(sdata->regmap)) {
|
||||
dev_err(&spi->dev, "Failed to register spi regmap (%d)\n",
|
||||
(int)PTR_ERR(sdata->regmap));
|
||||
return PTR_ERR(sdata->regmap);
|
||||
}
|
||||
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->name = spi->modalias;
|
||||
|
||||
sdata->dev = &spi->dev;
|
||||
sdata->tf = &st_sensors_tf_spi;
|
||||
sdata->get_irq_data_ready = st_sensors_spi_get_irq;
|
||||
sdata->irq = spi->irq;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(st_sensors_spi_configure);
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/iio/common/st_sensors.h>
|
||||
#include "st_sensors_core.h"
|
||||
|
||||
|
@ -26,8 +27,7 @@
|
|||
static int st_sensors_new_samples_available(struct iio_dev *indio_dev,
|
||||
struct st_sensor_data *sdata)
|
||||
{
|
||||
u8 status;
|
||||
int ret;
|
||||
int ret, status;
|
||||
|
||||
/* How would I know if I can't check it? */
|
||||
if (!sdata->sensor_settings->drdy_irq.stat_drdy.addr)
|
||||
|
@ -37,9 +37,9 @@ static int st_sensors_new_samples_available(struct iio_dev *indio_dev,
|
|||
if (!indio_dev->active_scan_mask)
|
||||
return 0;
|
||||
|
||||
ret = sdata->tf->read_byte(&sdata->tb, sdata->dev,
|
||||
sdata->sensor_settings->drdy_irq.stat_drdy.addr,
|
||||
&status);
|
||||
ret = regmap_read(sdata->regmap,
|
||||
sdata->sensor_settings->drdy_irq.stat_drdy.addr,
|
||||
&status);
|
||||
if (ret < 0) {
|
||||
dev_err(sdata->dev,
|
||||
"error checking samples available\n");
|
||||
|
@ -121,9 +121,9 @@ static irqreturn_t st_sensors_irq_thread(int irq, void *p)
|
|||
int st_sensors_allocate_trigger(struct iio_dev *indio_dev,
|
||||
const struct iio_trigger_ops *trigger_ops)
|
||||
{
|
||||
int err, irq;
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
unsigned long irq_trig;
|
||||
int err;
|
||||
|
||||
sdata->trig = iio_trigger_alloc("%s-trigger", indio_dev->name);
|
||||
if (sdata->trig == NULL) {
|
||||
|
@ -135,8 +135,7 @@ int st_sensors_allocate_trigger(struct iio_dev *indio_dev,
|
|||
sdata->trig->ops = trigger_ops;
|
||||
sdata->trig->dev.parent = sdata->dev;
|
||||
|
||||
irq = sdata->get_irq_data_ready(indio_dev);
|
||||
irq_trig = irqd_get_trigger_type(irq_get_irq_data(irq));
|
||||
irq_trig = irqd_get_trigger_type(irq_get_irq_data(sdata->irq));
|
||||
/*
|
||||
* If the IRQ is triggered on falling edge, we need to mark the
|
||||
* interrupt as active low, if the hardware supports this.
|
||||
|
@ -206,12 +205,12 @@ int st_sensors_allocate_trigger(struct iio_dev *indio_dev,
|
|||
sdata->sensor_settings->drdy_irq.stat_drdy.addr)
|
||||
irq_trig |= IRQF_SHARED;
|
||||
|
||||
err = request_threaded_irq(sdata->get_irq_data_ready(indio_dev),
|
||||
st_sensors_irq_handler,
|
||||
st_sensors_irq_thread,
|
||||
irq_trig,
|
||||
sdata->trig->name,
|
||||
sdata->trig);
|
||||
err = request_threaded_irq(sdata->irq,
|
||||
st_sensors_irq_handler,
|
||||
st_sensors_irq_thread,
|
||||
irq_trig,
|
||||
sdata->trig->name,
|
||||
sdata->trig);
|
||||
if (err) {
|
||||
dev_err(&indio_dev->dev, "failed to request trigger IRQ.\n");
|
||||
goto iio_trigger_free;
|
||||
|
@ -227,7 +226,7 @@ int st_sensors_allocate_trigger(struct iio_dev *indio_dev,
|
|||
return 0;
|
||||
|
||||
iio_trigger_register_error:
|
||||
free_irq(sdata->get_irq_data_ready(indio_dev), sdata->trig);
|
||||
free_irq(sdata->irq, sdata->trig);
|
||||
iio_trigger_free:
|
||||
iio_trigger_free(sdata->trig);
|
||||
return err;
|
||||
|
@ -239,7 +238,7 @@ void st_sensors_deallocate_trigger(struct iio_dev *indio_dev)
|
|||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
|
||||
iio_trigger_unregister(sdata->trig);
|
||||
free_irq(sdata->get_irq_data_ready(indio_dev), sdata->trig);
|
||||
free_irq(sdata->irq, sdata->trig);
|
||||
iio_trigger_free(sdata->trig);
|
||||
}
|
||||
EXPORT_SYMBOL(st_sensors_deallocate_trigger);
|
||||
|
|
|
@ -32,6 +32,7 @@ static const struct st_sensors_platform_data gyro_pdata = {
|
|||
.drdy_int_pin = 2,
|
||||
};
|
||||
|
||||
const struct st_sensor_settings *st_gyro_get_settings(const char *name);
|
||||
int st_gyro_common_probe(struct iio_dev *indio_dev);
|
||||
void st_gyro_common_remove(struct iio_dev *indio_dev);
|
||||
|
||||
|
|
|
@ -29,61 +29,51 @@ int st_gyro_trig_set_state(struct iio_trigger *trig, bool state)
|
|||
return st_sensors_set_dataready_irq(indio_dev, state);
|
||||
}
|
||||
|
||||
static int st_gyro_buffer_preenable(struct iio_dev *indio_dev)
|
||||
{
|
||||
return st_sensors_set_enable(indio_dev, true);
|
||||
}
|
||||
|
||||
static int st_gyro_buffer_postenable(struct iio_dev *indio_dev)
|
||||
{
|
||||
int err;
|
||||
struct st_sensor_data *gdata = iio_priv(indio_dev);
|
||||
|
||||
gdata->buffer_data = kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
|
||||
if (gdata->buffer_data == NULL) {
|
||||
err = -ENOMEM;
|
||||
goto allocate_memory_error;
|
||||
}
|
||||
|
||||
err = st_sensors_set_axis_enable(indio_dev,
|
||||
(u8)indio_dev->active_scan_mask[0]);
|
||||
if (err < 0)
|
||||
goto st_gyro_buffer_postenable_error;
|
||||
|
||||
err = iio_triggered_buffer_postenable(indio_dev);
|
||||
if (err < 0)
|
||||
goto st_gyro_buffer_postenable_error;
|
||||
return err;
|
||||
|
||||
return err;
|
||||
err = st_sensors_set_axis_enable(indio_dev,
|
||||
(u8)indio_dev->active_scan_mask[0]);
|
||||
if (err < 0)
|
||||
goto st_gyro_buffer_predisable;
|
||||
|
||||
st_gyro_buffer_postenable_error:
|
||||
kfree(gdata->buffer_data);
|
||||
allocate_memory_error:
|
||||
err = st_sensors_set_enable(indio_dev, true);
|
||||
if (err < 0)
|
||||
goto st_gyro_buffer_enable_all_axis;
|
||||
|
||||
return 0;
|
||||
|
||||
st_gyro_buffer_enable_all_axis:
|
||||
st_sensors_set_axis_enable(indio_dev, ST_SENSORS_ENABLE_ALL_AXIS);
|
||||
st_gyro_buffer_predisable:
|
||||
iio_triggered_buffer_predisable(indio_dev);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int st_gyro_buffer_predisable(struct iio_dev *indio_dev)
|
||||
{
|
||||
int err;
|
||||
struct st_sensor_data *gdata = iio_priv(indio_dev);
|
||||
|
||||
err = iio_triggered_buffer_predisable(indio_dev);
|
||||
if (err < 0)
|
||||
goto st_gyro_buffer_predisable_error;
|
||||
|
||||
err = st_sensors_set_axis_enable(indio_dev, ST_SENSORS_ENABLE_ALL_AXIS);
|
||||
if (err < 0)
|
||||
goto st_gyro_buffer_predisable_error;
|
||||
int err, err2;
|
||||
|
||||
err = st_sensors_set_enable(indio_dev, false);
|
||||
if (err < 0)
|
||||
goto st_gyro_buffer_predisable;
|
||||
|
||||
err = st_sensors_set_axis_enable(indio_dev, ST_SENSORS_ENABLE_ALL_AXIS);
|
||||
|
||||
st_gyro_buffer_predisable:
|
||||
err2 = iio_triggered_buffer_predisable(indio_dev);
|
||||
if (!err)
|
||||
err = err2;
|
||||
|
||||
st_gyro_buffer_predisable_error:
|
||||
kfree(gdata->buffer_data);
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct iio_buffer_setup_ops st_gyro_buffer_setup_ops = {
|
||||
.preenable = &st_gyro_buffer_preenable,
|
||||
.postenable = &st_gyro_buffer_postenable,
|
||||
.predisable = &st_gyro_buffer_predisable,
|
||||
};
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/gpio.h>
|
||||
|
@ -368,28 +367,41 @@ static const struct iio_trigger_ops st_gyro_trigger_ops = {
|
|||
#define ST_GYRO_TRIGGER_OPS NULL
|
||||
#endif
|
||||
|
||||
/*
|
||||
* st_gyro_get_settings() - get sensor settings from device name
|
||||
* @name: device name buffer reference.
|
||||
*
|
||||
* Return: valid reference on success, NULL otherwise.
|
||||
*/
|
||||
const struct st_sensor_settings *st_gyro_get_settings(const char *name)
|
||||
{
|
||||
int index = st_sensors_get_settings_index(name,
|
||||
st_gyro_sensors_settings,
|
||||
ARRAY_SIZE(st_gyro_sensors_settings));
|
||||
if (index < 0)
|
||||
return NULL;
|
||||
|
||||
return &st_gyro_sensors_settings[index];
|
||||
}
|
||||
EXPORT_SYMBOL(st_gyro_get_settings);
|
||||
|
||||
int st_gyro_common_probe(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct st_sensor_data *gdata = iio_priv(indio_dev);
|
||||
int irq = gdata->get_irq_data_ready(indio_dev);
|
||||
int err;
|
||||
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &gyro_info;
|
||||
mutex_init(&gdata->tb.buf_lock);
|
||||
|
||||
err = st_sensors_power_enable(indio_dev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = st_sensors_check_device_support(indio_dev,
|
||||
ARRAY_SIZE(st_gyro_sensors_settings),
|
||||
st_gyro_sensors_settings);
|
||||
err = st_sensors_verify_id(indio_dev);
|
||||
if (err < 0)
|
||||
goto st_gyro_power_off;
|
||||
|
||||
gdata->num_data_channels = ST_GYRO_NUMBER_DATA_CHANNELS;
|
||||
gdata->multiread_bit = gdata->sensor_settings->multi_read_bit;
|
||||
indio_dev->channels = gdata->sensor_settings->ch;
|
||||
indio_dev->num_channels = ST_SENSORS_NUMBER_ALL_CHANNELS;
|
||||
|
||||
|
@ -406,7 +418,7 @@ int st_gyro_common_probe(struct iio_dev *indio_dev)
|
|||
if (err < 0)
|
||||
goto st_gyro_power_off;
|
||||
|
||||
if (irq > 0) {
|
||||
if (gdata->irq > 0) {
|
||||
err = st_sensors_allocate_trigger(indio_dev,
|
||||
ST_GYRO_TRIGGER_OPS);
|
||||
if (err < 0)
|
||||
|
@ -423,7 +435,7 @@ int st_gyro_common_probe(struct iio_dev *indio_dev)
|
|||
return 0;
|
||||
|
||||
st_gyro_device_register_error:
|
||||
if (irq > 0)
|
||||
if (gdata->irq > 0)
|
||||
st_sensors_deallocate_trigger(indio_dev);
|
||||
st_gyro_probe_trigger_error:
|
||||
st_gyro_deallocate_ring(indio_dev);
|
||||
|
@ -441,7 +453,7 @@ void st_gyro_common_remove(struct iio_dev *indio_dev)
|
|||
st_sensors_power_disable(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
if (gdata->get_irq_data_ready(indio_dev) > 0)
|
||||
if (gdata->irq > 0)
|
||||
st_sensors_deallocate_trigger(indio_dev);
|
||||
|
||||
st_gyro_deallocate_ring(indio_dev);
|
||||
|
|
|
@ -63,21 +63,33 @@ MODULE_DEVICE_TABLE(of, st_gyro_of_match);
|
|||
#endif
|
||||
|
||||
static int st_gyro_i2c_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
const struct st_sensor_settings *settings;
|
||||
struct st_sensor_data *gdata;
|
||||
struct iio_dev *indio_dev;
|
||||
int err;
|
||||
|
||||
st_sensors_of_name_probe(&client->dev, st_gyro_of_match,
|
||||
client->name, sizeof(client->name));
|
||||
|
||||
settings = st_gyro_get_settings(client->name);
|
||||
if (!settings) {
|
||||
dev_err(&client->dev, "device name %s not recognized.\n",
|
||||
client->name);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*gdata));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
gdata = iio_priv(indio_dev);
|
||||
st_sensors_of_name_probe(&client->dev, st_gyro_of_match,
|
||||
client->name, sizeof(client->name));
|
||||
gdata->sensor_settings = (struct st_sensor_settings *)settings;
|
||||
|
||||
st_sensors_i2c_configure(indio_dev, client, gdata);
|
||||
err = st_sensors_i2c_configure(indio_dev, client);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = st_gyro_common_probe(indio_dev);
|
||||
if (err < 0)
|
||||
|
|
|
@ -69,19 +69,31 @@ MODULE_DEVICE_TABLE(of, st_gyro_of_match);
|
|||
|
||||
static int st_gyro_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
const struct st_sensor_settings *settings;
|
||||
struct st_sensor_data *gdata;
|
||||
struct iio_dev *indio_dev;
|
||||
int err;
|
||||
|
||||
st_sensors_of_name_probe(&spi->dev, st_gyro_of_match,
|
||||
spi->modalias, sizeof(spi->modalias));
|
||||
|
||||
settings = st_gyro_get_settings(spi->modalias);
|
||||
if (!settings) {
|
||||
dev_err(&spi->dev, "device name %s not recognized.\n",
|
||||
spi->modalias);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*gdata));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
gdata = iio_priv(indio_dev);
|
||||
gdata->sensor_settings = (struct st_sensor_settings *)settings;
|
||||
|
||||
st_sensors_of_name_probe(&spi->dev, st_gyro_of_match,
|
||||
spi->modalias, sizeof(spi->modalias));
|
||||
st_sensors_spi_configure(indio_dev, spi, gdata);
|
||||
err = st_sensors_spi_configure(indio_dev, spi);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = st_gyro_common_probe(indio_dev);
|
||||
if (err < 0)
|
||||
|
|
|
@ -240,32 +240,15 @@ static int am2315_probe(struct i2c_client *client,
|
|||
indio_dev->channels = am2315_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(am2315_channels);
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, iio_pollfunc_store_time,
|
||||
ret = devm_iio_triggered_buffer_setup(&client->dev,
|
||||
indio_dev, iio_pollfunc_store_time,
|
||||
am2315_trigger_handler, NULL);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "iio triggered buffer setup failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret < 0)
|
||||
goto err_buffer_cleanup;
|
||||
|
||||
return 0;
|
||||
|
||||
err_buffer_cleanup:
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int am2315_remove(struct i2c_client *client)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
|
||||
return 0;
|
||||
return devm_iio_device_register(&client->dev, indio_dev);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id am2315_i2c_id[] = {
|
||||
|
@ -287,7 +270,6 @@ static struct i2c_driver am2315_driver = {
|
|||
.acpi_match_table = ACPI_PTR(am2315_acpi_id),
|
||||
},
|
||||
.probe = am2315_probe,
|
||||
.remove = am2315_remove,
|
||||
.id_table = am2315_i2c_id,
|
||||
};
|
||||
|
||||
|
|
|
@ -385,28 +385,16 @@ static int hdc100x_probe(struct i2c_client *client,
|
|||
hdc100x_set_it_time(data, 1, hdc100x_int_time[1][0]);
|
||||
hdc100x_update_config(data, HDC100X_REG_CONFIG_ACQ_MODE, 0);
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, NULL,
|
||||
ret = devm_iio_triggered_buffer_setup(&client->dev,
|
||||
indio_dev, NULL,
|
||||
hdc100x_trigger_handler,
|
||||
&hdc_buffer_setup_ops);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "iio triggered buffer setup failed\n");
|
||||
return ret;
|
||||
}
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret < 0)
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hdc100x_remove(struct i2c_client *client)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
|
||||
return 0;
|
||||
return devm_iio_device_register(&client->dev, indio_dev);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id hdc100x_id[] = {
|
||||
|
@ -436,7 +424,6 @@ static struct i2c_driver hdc100x_driver = {
|
|||
.of_match_table = of_match_ptr(hdc100x_dt_ids),
|
||||
},
|
||||
.probe = hdc100x_probe,
|
||||
.remove = hdc100x_remove,
|
||||
.id_table = hdc100x_id,
|
||||
};
|
||||
module_i2c_driver(hdc100x_driver);
|
||||
|
|
|
@ -17,6 +17,18 @@ config ADIS16400
|
|||
adis16365, adis16400 and adis16405 triaxial inertial sensors
|
||||
(adis16400 series also have magnetometers).
|
||||
|
||||
config ADIS16460
|
||||
tristate "Analog Devices ADIS16460 and similar IMU driver"
|
||||
depends on SPI
|
||||
select IIO_ADIS_LIB
|
||||
select IIO_ADIS_LIB_BUFFER if IIO_BUFFER
|
||||
help
|
||||
Say yes here to build support for Analog Devices ADIS16460 inertial
|
||||
sensor.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called adis16460.
|
||||
|
||||
config ADIS16480
|
||||
tristate "Analog Devices ADIS16480 and similar IMU driver"
|
||||
depends on SPI
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
# When adding new entries keep the list in alphabetical order
|
||||
obj-$(CONFIG_ADIS16400) += adis16400.o
|
||||
obj-$(CONFIG_ADIS16460) += adis16460.o
|
||||
obj-$(CONFIG_ADIS16480) += adis16480.o
|
||||
|
||||
adis_lib-y += adis.o
|
||||
|
|
|
@ -39,18 +39,24 @@ int adis_write_reg(struct adis *adis, unsigned int reg,
|
|||
.len = 2,
|
||||
.cs_change = 1,
|
||||
.delay_usecs = adis->data->write_delay,
|
||||
.cs_change_delay = adis->data->cs_change_delay,
|
||||
.cs_change_delay_unit = SPI_DELAY_UNIT_USECS,
|
||||
}, {
|
||||
.tx_buf = adis->tx + 2,
|
||||
.bits_per_word = 8,
|
||||
.len = 2,
|
||||
.cs_change = 1,
|
||||
.delay_usecs = adis->data->write_delay,
|
||||
.cs_change_delay = adis->data->cs_change_delay,
|
||||
.cs_change_delay_unit = SPI_DELAY_UNIT_USECS,
|
||||
}, {
|
||||
.tx_buf = adis->tx + 4,
|
||||
.bits_per_word = 8,
|
||||
.len = 2,
|
||||
.cs_change = 1,
|
||||
.delay_usecs = adis->data->write_delay,
|
||||
.cs_change_delay = adis->data->cs_change_delay,
|
||||
.cs_change_delay_unit = SPI_DELAY_UNIT_USECS,
|
||||
}, {
|
||||
.tx_buf = adis->tx + 6,
|
||||
.bits_per_word = 8,
|
||||
|
@ -133,12 +139,16 @@ int adis_read_reg(struct adis *adis, unsigned int reg,
|
|||
.len = 2,
|
||||
.cs_change = 1,
|
||||
.delay_usecs = adis->data->write_delay,
|
||||
.cs_change_delay = adis->data->cs_change_delay,
|
||||
.cs_change_delay_unit = SPI_DELAY_UNIT_USECS,
|
||||
}, {
|
||||
.tx_buf = adis->tx + 2,
|
||||
.bits_per_word = 8,
|
||||
.len = 2,
|
||||
.cs_change = 1,
|
||||
.delay_usecs = adis->data->read_delay,
|
||||
.cs_change_delay = adis->data->cs_change_delay,
|
||||
.cs_change_delay_unit = SPI_DELAY_UNIT_USECS,
|
||||
}, {
|
||||
.tx_buf = adis->tx + 4,
|
||||
.rx_buf = adis->rx,
|
||||
|
@ -146,6 +156,8 @@ int adis_read_reg(struct adis *adis, unsigned int reg,
|
|||
.len = 2,
|
||||
.cs_change = 1,
|
||||
.delay_usecs = adis->data->read_delay,
|
||||
.cs_change_delay = adis->data->cs_change_delay,
|
||||
.cs_change_delay_unit = SPI_DELAY_UNIT_USECS,
|
||||
}, {
|
||||
.rx_buf = adis->rx + 2,
|
||||
.bits_per_word = 8,
|
||||
|
|
|
@ -0,0 +1,489 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* ADIS16460 IMU driver
|
||||
*
|
||||
* Copyright 2019 Analog Devices Inc.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/imu/adis.h>
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
|
||||
#define ADIS16460_REG_FLASH_CNT 0x00
|
||||
#define ADIS16460_REG_DIAG_STAT 0x02
|
||||
#define ADIS16460_REG_X_GYRO_LOW 0x04
|
||||
#define ADIS16460_REG_X_GYRO_OUT 0x06
|
||||
#define ADIS16460_REG_Y_GYRO_LOW 0x08
|
||||
#define ADIS16460_REG_Y_GYRO_OUT 0x0A
|
||||
#define ADIS16460_REG_Z_GYRO_LOW 0x0C
|
||||
#define ADIS16460_REG_Z_GYRO_OUT 0x0E
|
||||
#define ADIS16460_REG_X_ACCL_LOW 0x10
|
||||
#define ADIS16460_REG_X_ACCL_OUT 0x12
|
||||
#define ADIS16460_REG_Y_ACCL_LOW 0x14
|
||||
#define ADIS16460_REG_Y_ACCL_OUT 0x16
|
||||
#define ADIS16460_REG_Z_ACCL_LOW 0x18
|
||||
#define ADIS16460_REG_Z_ACCL_OUT 0x1A
|
||||
#define ADIS16460_REG_SMPL_CNTR 0x1C
|
||||
#define ADIS16460_REG_TEMP_OUT 0x1E
|
||||
#define ADIS16460_REG_X_DELT_ANG 0x24
|
||||
#define ADIS16460_REG_Y_DELT_ANG 0x26
|
||||
#define ADIS16460_REG_Z_DELT_ANG 0x28
|
||||
#define ADIS16460_REG_X_DELT_VEL 0x2A
|
||||
#define ADIS16460_REG_Y_DELT_VEL 0x2C
|
||||
#define ADIS16460_REG_Z_DELT_VEL 0x2E
|
||||
#define ADIS16460_REG_MSC_CTRL 0x32
|
||||
#define ADIS16460_REG_SYNC_SCAL 0x34
|
||||
#define ADIS16460_REG_DEC_RATE 0x36
|
||||
#define ADIS16460_REG_FLTR_CTRL 0x38
|
||||
#define ADIS16460_REG_GLOB_CMD 0x3E
|
||||
#define ADIS16460_REG_X_GYRO_OFF 0x40
|
||||
#define ADIS16460_REG_Y_GYRO_OFF 0x42
|
||||
#define ADIS16460_REG_Z_GYRO_OFF 0x44
|
||||
#define ADIS16460_REG_X_ACCL_OFF 0x46
|
||||
#define ADIS16460_REG_Y_ACCL_OFF 0x48
|
||||
#define ADIS16460_REG_Z_ACCL_OFF 0x4A
|
||||
#define ADIS16460_REG_LOT_ID1 0x52
|
||||
#define ADIS16460_REG_LOT_ID2 0x54
|
||||
#define ADIS16460_REG_PROD_ID 0x56
|
||||
#define ADIS16460_REG_SERIAL_NUM 0x58
|
||||
#define ADIS16460_REG_CAL_SGNTR 0x60
|
||||
#define ADIS16460_REG_CAL_CRC 0x62
|
||||
#define ADIS16460_REG_CODE_SGNTR 0x64
|
||||
#define ADIS16460_REG_CODE_CRC 0x66
|
||||
|
||||
struct adis16460_chip_info {
|
||||
unsigned int num_channels;
|
||||
const struct iio_chan_spec *channels;
|
||||
unsigned int gyro_max_val;
|
||||
unsigned int gyro_max_scale;
|
||||
unsigned int accel_max_val;
|
||||
unsigned int accel_max_scale;
|
||||
};
|
||||
|
||||
struct adis16460 {
|
||||
const struct adis16460_chip_info *chip_info;
|
||||
struct adis adis;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
|
||||
static int adis16460_show_serial_number(void *arg, u64 *val)
|
||||
{
|
||||
struct adis16460 *adis16460 = arg;
|
||||
u16 serial;
|
||||
int ret;
|
||||
|
||||
ret = adis_read_reg_16(&adis16460->adis, ADIS16460_REG_SERIAL_NUM,
|
||||
&serial);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = serial;
|
||||
|
||||
return 0;
|
||||
}
|
||||
DEFINE_SIMPLE_ATTRIBUTE(adis16460_serial_number_fops,
|
||||
adis16460_show_serial_number, NULL, "0x%.4llx\n");
|
||||
|
||||
static int adis16460_show_product_id(void *arg, u64 *val)
|
||||
{
|
||||
struct adis16460 *adis16460 = arg;
|
||||
u16 prod_id;
|
||||
int ret;
|
||||
|
||||
ret = adis_read_reg_16(&adis16460->adis, ADIS16460_REG_PROD_ID,
|
||||
&prod_id);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = prod_id;
|
||||
|
||||
return 0;
|
||||
}
|
||||
DEFINE_SIMPLE_ATTRIBUTE(adis16460_product_id_fops,
|
||||
adis16460_show_product_id, NULL, "%llu\n");
|
||||
|
||||
static int adis16460_show_flash_count(void *arg, u64 *val)
|
||||
{
|
||||
struct adis16460 *adis16460 = arg;
|
||||
u32 flash_count;
|
||||
int ret;
|
||||
|
||||
ret = adis_read_reg_32(&adis16460->adis, ADIS16460_REG_FLASH_CNT,
|
||||
&flash_count);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = flash_count;
|
||||
|
||||
return 0;
|
||||
}
|
||||
DEFINE_SIMPLE_ATTRIBUTE(adis16460_flash_count_fops,
|
||||
adis16460_show_flash_count, NULL, "%lld\n");
|
||||
|
||||
static int adis16460_debugfs_init(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct adis16460 *adis16460 = iio_priv(indio_dev);
|
||||
|
||||
debugfs_create_file("serial_number", 0400, indio_dev->debugfs_dentry,
|
||||
adis16460, &adis16460_serial_number_fops);
|
||||
debugfs_create_file("product_id", 0400, indio_dev->debugfs_dentry,
|
||||
adis16460, &adis16460_product_id_fops);
|
||||
debugfs_create_file("flash_count", 0400, indio_dev->debugfs_dentry,
|
||||
adis16460, &adis16460_flash_count_fops);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static int adis16460_debugfs_init(struct iio_dev *indio_dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static int adis16460_set_freq(struct iio_dev *indio_dev, int val, int val2)
|
||||
{
|
||||
struct adis16460 *st = iio_priv(indio_dev);
|
||||
unsigned int t;
|
||||
|
||||
t = val * 1000 + val2 / 1000;
|
||||
if (t <= 0)
|
||||
return -EINVAL;
|
||||
|
||||
t = 2048000 / t;
|
||||
if (t > 2048)
|
||||
t = 2048;
|
||||
|
||||
if (t != 0)
|
||||
t--;
|
||||
|
||||
return adis_write_reg_16(&st->adis, ADIS16460_REG_DEC_RATE, t);
|
||||
}
|
||||
|
||||
static int adis16460_get_freq(struct iio_dev *indio_dev, int *val, int *val2)
|
||||
{
|
||||
struct adis16460 *st = iio_priv(indio_dev);
|
||||
uint16_t t;
|
||||
int ret;
|
||||
unsigned int freq;
|
||||
|
||||
ret = adis_read_reg_16(&st->adis, ADIS16460_REG_DEC_RATE, &t);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
freq = 2048000 / (t + 1);
|
||||
*val = freq / 1000;
|
||||
*val2 = (freq % 1000) * 1000;
|
||||
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
}
|
||||
|
||||
static int adis16460_read_raw(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, int *val, int *val2, long info)
|
||||
{
|
||||
struct adis16460 *st = iio_priv(indio_dev);
|
||||
|
||||
switch (info) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
return adis_single_conversion(indio_dev, chan, 0, val);
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
switch (chan->type) {
|
||||
case IIO_ANGL_VEL:
|
||||
*val = st->chip_info->gyro_max_scale;
|
||||
*val2 = st->chip_info->gyro_max_val;
|
||||
return IIO_VAL_FRACTIONAL;
|
||||
case IIO_ACCEL:
|
||||
*val = st->chip_info->accel_max_scale;
|
||||
*val2 = st->chip_info->accel_max_val;
|
||||
return IIO_VAL_FRACTIONAL;
|
||||
case IIO_TEMP:
|
||||
*val = 50; /* 50 milli degrees Celsius/LSB */
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
*val = 500; /* 25 degrees Celsius = 0x0000 */
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
return adis16460_get_freq(indio_dev, val, val2);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int adis16460_write_raw(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, int val, int val2, long info)
|
||||
{
|
||||
switch (info) {
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
return adis16460_set_freq(indio_dev, val, val2);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
enum {
|
||||
ADIS16460_SCAN_GYRO_X,
|
||||
ADIS16460_SCAN_GYRO_Y,
|
||||
ADIS16460_SCAN_GYRO_Z,
|
||||
ADIS16460_SCAN_ACCEL_X,
|
||||
ADIS16460_SCAN_ACCEL_Y,
|
||||
ADIS16460_SCAN_ACCEL_Z,
|
||||
ADIS16460_SCAN_TEMP,
|
||||
};
|
||||
|
||||
#define ADIS16460_MOD_CHANNEL(_type, _mod, _address, _si, _bits) \
|
||||
{ \
|
||||
.type = (_type), \
|
||||
.modified = 1, \
|
||||
.channel2 = (_mod), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
|
||||
.address = (_address), \
|
||||
.scan_index = (_si), \
|
||||
.scan_type = { \
|
||||
.sign = 's', \
|
||||
.realbits = (_bits), \
|
||||
.storagebits = (_bits), \
|
||||
.endianness = IIO_BE, \
|
||||
}, \
|
||||
}
|
||||
|
||||
#define ADIS16460_GYRO_CHANNEL(_mod) \
|
||||
ADIS16460_MOD_CHANNEL(IIO_ANGL_VEL, IIO_MOD_ ## _mod, \
|
||||
ADIS16460_REG_ ## _mod ## _GYRO_LOW, ADIS16460_SCAN_GYRO_ ## _mod, \
|
||||
32)
|
||||
|
||||
#define ADIS16460_ACCEL_CHANNEL(_mod) \
|
||||
ADIS16460_MOD_CHANNEL(IIO_ACCEL, IIO_MOD_ ## _mod, \
|
||||
ADIS16460_REG_ ## _mod ## _ACCL_LOW, ADIS16460_SCAN_ACCEL_ ## _mod, \
|
||||
32)
|
||||
|
||||
#define ADIS16460_TEMP_CHANNEL() { \
|
||||
.type = IIO_TEMP, \
|
||||
.indexed = 1, \
|
||||
.channel = 0, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_SCALE) | \
|
||||
BIT(IIO_CHAN_INFO_OFFSET), \
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
|
||||
.address = ADIS16460_REG_TEMP_OUT, \
|
||||
.scan_index = ADIS16460_SCAN_TEMP, \
|
||||
.scan_type = { \
|
||||
.sign = 's', \
|
||||
.realbits = 16, \
|
||||
.storagebits = 16, \
|
||||
.endianness = IIO_BE, \
|
||||
}, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec adis16460_channels[] = {
|
||||
ADIS16460_GYRO_CHANNEL(X),
|
||||
ADIS16460_GYRO_CHANNEL(Y),
|
||||
ADIS16460_GYRO_CHANNEL(Z),
|
||||
ADIS16460_ACCEL_CHANNEL(X),
|
||||
ADIS16460_ACCEL_CHANNEL(Y),
|
||||
ADIS16460_ACCEL_CHANNEL(Z),
|
||||
ADIS16460_TEMP_CHANNEL(),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(7)
|
||||
};
|
||||
|
||||
static const struct adis16460_chip_info adis16460_chip_info = {
|
||||
.channels = adis16460_channels,
|
||||
.num_channels = ARRAY_SIZE(adis16460_channels),
|
||||
/*
|
||||
* storing the value in rad/degree and the scale in degree
|
||||
* gives us the result in rad and better precession than
|
||||
* storing the scale directly in rad.
|
||||
*/
|
||||
.gyro_max_val = IIO_RAD_TO_DEGREE(200 << 16),
|
||||
.gyro_max_scale = 1,
|
||||
.accel_max_val = IIO_M_S_2_TO_G(20000 << 16),
|
||||
.accel_max_scale = 5,
|
||||
};
|
||||
|
||||
static const struct iio_info adis16460_info = {
|
||||
.read_raw = &adis16460_read_raw,
|
||||
.write_raw = &adis16460_write_raw,
|
||||
.update_scan_mode = adis_update_scan_mode,
|
||||
.debugfs_reg_access = adis_debugfs_reg_access,
|
||||
};
|
||||
|
||||
static int adis16460_enable_irq(struct adis *adis, bool enable)
|
||||
{
|
||||
/*
|
||||
* There is no way to gate the data-ready signal internally inside the
|
||||
* ADIS16460 :(
|
||||
*/
|
||||
if (enable)
|
||||
enable_irq(adis->spi->irq);
|
||||
else
|
||||
disable_irq(adis->spi->irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adis16460_initial_setup(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct adis16460 *st = iio_priv(indio_dev);
|
||||
uint16_t prod_id;
|
||||
unsigned int device_id;
|
||||
int ret;
|
||||
|
||||
adis_reset(&st->adis);
|
||||
msleep(222);
|
||||
|
||||
ret = adis_write_reg_16(&st->adis, ADIS16460_REG_GLOB_CMD, BIT(1));
|
||||
if (ret)
|
||||
return ret;
|
||||
msleep(75);
|
||||
|
||||
ret = adis_check_status(&st->adis);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = adis_read_reg_16(&st->adis, ADIS16460_REG_PROD_ID, &prod_id);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = sscanf(indio_dev->name, "adis%u\n", &device_id);
|
||||
if (ret != 1)
|
||||
return -EINVAL;
|
||||
|
||||
if (prod_id != device_id)
|
||||
dev_warn(&indio_dev->dev, "Device ID(%u) and product ID(%u) do not match.",
|
||||
device_id, prod_id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define ADIS16460_DIAG_STAT_IN_CLK_OOS 7
|
||||
#define ADIS16460_DIAG_STAT_FLASH_MEM 6
|
||||
#define ADIS16460_DIAG_STAT_SELF_TEST 5
|
||||
#define ADIS16460_DIAG_STAT_OVERRANGE 4
|
||||
#define ADIS16460_DIAG_STAT_SPI_COMM 3
|
||||
#define ADIS16460_DIAG_STAT_FLASH_UPT 2
|
||||
|
||||
static const char * const adis16460_status_error_msgs[] = {
|
||||
[ADIS16460_DIAG_STAT_IN_CLK_OOS] = "Input clock out of sync",
|
||||
[ADIS16460_DIAG_STAT_FLASH_MEM] = "Flash memory failure",
|
||||
[ADIS16460_DIAG_STAT_SELF_TEST] = "Self test diagnostic failure",
|
||||
[ADIS16460_DIAG_STAT_OVERRANGE] = "Sensor overrange",
|
||||
[ADIS16460_DIAG_STAT_SPI_COMM] = "SPI communication failure",
|
||||
[ADIS16460_DIAG_STAT_FLASH_UPT] = "Flash update failure",
|
||||
};
|
||||
|
||||
static const struct adis_data adis16460_data = {
|
||||
.diag_stat_reg = ADIS16460_REG_DIAG_STAT,
|
||||
.glob_cmd_reg = ADIS16460_REG_GLOB_CMD,
|
||||
.has_paging = false,
|
||||
.read_delay = 5,
|
||||
.write_delay = 5,
|
||||
.cs_change_delay = 16,
|
||||
.status_error_msgs = adis16460_status_error_msgs,
|
||||
.status_error_mask = BIT(ADIS16460_DIAG_STAT_IN_CLK_OOS) |
|
||||
BIT(ADIS16460_DIAG_STAT_FLASH_MEM) |
|
||||
BIT(ADIS16460_DIAG_STAT_SELF_TEST) |
|
||||
BIT(ADIS16460_DIAG_STAT_OVERRANGE) |
|
||||
BIT(ADIS16460_DIAG_STAT_SPI_COMM) |
|
||||
BIT(ADIS16460_DIAG_STAT_FLASH_UPT),
|
||||
.enable_irq = adis16460_enable_irq,
|
||||
};
|
||||
|
||||
static int adis16460_probe(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct adis16460 *st;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
||||
if (indio_dev == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
|
||||
st->chip_info = &adis16460_chip_info;
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->name = spi_get_device_id(spi)->name;
|
||||
indio_dev->channels = st->chip_info->channels;
|
||||
indio_dev->num_channels = st->chip_info->num_channels;
|
||||
indio_dev->info = &adis16460_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
ret = adis_init(&st->adis, indio_dev, spi, &adis16460_data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = adis_setup_buffer_and_trigger(&st->adis, indio_dev, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
adis16460_enable_irq(&st->adis, 0);
|
||||
|
||||
ret = adis16460_initial_setup(indio_dev);
|
||||
if (ret)
|
||||
goto error_cleanup_buffer;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto error_cleanup_buffer;
|
||||
|
||||
adis16460_debugfs_init(indio_dev);
|
||||
|
||||
return 0;
|
||||
|
||||
error_cleanup_buffer:
|
||||
adis_cleanup_buffer_and_trigger(&st->adis, indio_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adis16460_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct adis16460 *st = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
adis_cleanup_buffer_and_trigger(&st->adis, indio_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id adis16460_ids[] = {
|
||||
{ "adis16460", 0 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, adis16460_ids);
|
||||
|
||||
static const struct of_device_id adis16460_of_match[] = {
|
||||
{ .compatible = "adi,adis16460" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, adis16460_of_match);
|
||||
|
||||
static struct spi_driver adis16460_driver = {
|
||||
.driver = {
|
||||
.name = "adis16460",
|
||||
.of_match_table = adis16460_of_match,
|
||||
},
|
||||
.id_table = adis16460_ids,
|
||||
.probe = adis16460_probe,
|
||||
.remove = adis16460_remove,
|
||||
};
|
||||
module_spi_driver(adis16460_driver);
|
||||
|
||||
MODULE_AUTHOR("Dragos Bogdan <dragos.bogdan@analog.com>");
|
||||
MODULE_DESCRIPTION("Analog Devices ADIS16460 IMU driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -14,8 +14,9 @@ config INV_MPU6050_I2C
|
|||
select INV_MPU6050_IIO
|
||||
select REGMAP_I2C
|
||||
help
|
||||
This driver supports the Invensense MPU6050/6500/9150 and
|
||||
ICM20608/20602 motion tracking devices over I2C.
|
||||
This driver supports the Invensense MPU6000/6050/6500/6515,
|
||||
MPU9150/9250/9255 and ICM20608/20602 motion tracking devices
|
||||
over I2C.
|
||||
This driver can be built as a module. The module will be called
|
||||
inv-mpu6050-i2c.
|
||||
|
||||
|
@ -25,7 +26,8 @@ config INV_MPU6050_SPI
|
|||
select INV_MPU6050_IIO
|
||||
select REGMAP_SPI
|
||||
help
|
||||
This driver supports the Invensense MPU6050/6500/9150 and
|
||||
ICM20608/20602 motion tracking devices over SPI.
|
||||
This driver supports the Invensense MPU6000/6050/6500/6515,
|
||||
MPU9150/9250/9255 and ICM20608/20602 motion tracking devices
|
||||
over SPI.
|
||||
This driver can be built as a module. The module will be called
|
||||
inv-mpu6050-spi.
|
||||
|
|
|
@ -1137,10 +1137,9 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name,
|
|||
if (result)
|
||||
return result;
|
||||
|
||||
result = devm_add_action(dev, inv_mpu_core_disable_regulator_action,
|
||||
result = devm_add_action_or_reset(dev, inv_mpu_core_disable_regulator_action,
|
||||
st);
|
||||
if (result) {
|
||||
inv_mpu_core_disable_regulator_action(st);
|
||||
dev_err(dev, "Failed to setup regulator cleanup action %d\n",
|
||||
result);
|
||||
return result;
|
||||
|
|
|
@ -2,15 +2,17 @@
|
|||
|
||||
config IIO_ST_LSM6DSX
|
||||
tristate "ST_LSM6DSx driver for STM 6-axis IMU MEMS sensors"
|
||||
depends on (I2C || SPI)
|
||||
depends on (I2C || SPI || I3C)
|
||||
select IIO_BUFFER
|
||||
select IIO_KFIFO_BUF
|
||||
select IIO_ST_LSM6DSX_I2C if (I2C)
|
||||
select IIO_ST_LSM6DSX_SPI if (SPI_MASTER)
|
||||
select IIO_ST_LSM6DSX_I3C if (I3C)
|
||||
help
|
||||
Say yes here to build support for STMicroelectronics LSM6DSx imu
|
||||
sensor. Supported devices: lsm6ds3, lsm6ds3h, lsm6dsl, lsm6dsm,
|
||||
ism330dlc, lsm6dso, lsm6dsox, asm330lhh, lsm6dsr
|
||||
ism330dlc, lsm6dso, lsm6dsox, asm330lhh, lsm6dsr, lsm6ds3tr-c,
|
||||
ism330dhcx
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called st_lsm6dsx.
|
||||
|
@ -24,3 +26,8 @@ config IIO_ST_LSM6DSX_SPI
|
|||
tristate
|
||||
depends on IIO_ST_LSM6DSX
|
||||
select REGMAP_SPI
|
||||
|
||||
config IIO_ST_LSM6DSX_I3C
|
||||
tristate
|
||||
depends on IIO_ST_LSM6DSX
|
||||
select REGMAP_I3C
|
||||
|
|
|
@ -5,3 +5,4 @@ st_lsm6dsx-y := st_lsm6dsx_core.o st_lsm6dsx_buffer.o \
|
|||
obj-$(CONFIG_IIO_ST_LSM6DSX) += st_lsm6dsx.o
|
||||
obj-$(CONFIG_IIO_ST_LSM6DSX_I2C) += st_lsm6dsx_i2c.o
|
||||
obj-$(CONFIG_IIO_ST_LSM6DSX_SPI) += st_lsm6dsx_spi.o
|
||||
obj-$(CONFIG_IIO_ST_LSM6DSX_I3C) += st_lsm6dsx_i3c.o
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
#define ST_ASM330LHH_DEV_NAME "asm330lhh"
|
||||
#define ST_LSM6DSOX_DEV_NAME "lsm6dsox"
|
||||
#define ST_LSM6DSR_DEV_NAME "lsm6dsr"
|
||||
#define ST_LSM6DS3TRC_DEV_NAME "lsm6ds3tr-c"
|
||||
#define ST_ISM330DHCX_DEV_NAME "ism330dhcx"
|
||||
|
||||
enum st_lsm6dsx_hw_id {
|
||||
ST_LSM6DS3_ID,
|
||||
|
@ -33,6 +35,8 @@ enum st_lsm6dsx_hw_id {
|
|||
ST_ASM330LHH_ID,
|
||||
ST_LSM6DSOX_ID,
|
||||
ST_LSM6DSR_ID,
|
||||
ST_LSM6DS3TRC_ID,
|
||||
ST_ISM330DHCX_ID,
|
||||
ST_LSM6DSX_MAX_ID,
|
||||
};
|
||||
|
||||
|
@ -54,8 +58,8 @@ enum st_lsm6dsx_hw_id {
|
|||
.address = addr, \
|
||||
.modified = 1, \
|
||||
.channel2 = mod, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
|
||||
.scan_index = scan_idx, \
|
||||
.scan_type = { \
|
||||
|
@ -198,6 +202,9 @@ struct st_lsm6dsx_ext_dev_settings {
|
|||
* @wai: Sensor WhoAmI default value.
|
||||
* @max_fifo_size: Sensor max fifo length in FIFO words.
|
||||
* @id: List of hw id/device name supported by the driver configuration.
|
||||
* @channels: IIO channels supported by the device.
|
||||
* @odr_table: Hw sensors odr table (Hz + val).
|
||||
* @fs_table: Hw sensors gain table (gain + val).
|
||||
* @decimator: List of decimator register info (addr + mask).
|
||||
* @batch: List of FIFO batching register info (addr + mask).
|
||||
* @fifo_ops: Sensor hw FIFO parameters.
|
||||
|
@ -211,6 +218,12 @@ struct st_lsm6dsx_settings {
|
|||
enum st_lsm6dsx_hw_id hw_id;
|
||||
const char *name;
|
||||
} id[ST_LSM6DSX_MAX_ID];
|
||||
struct {
|
||||
const struct iio_chan_spec *chan;
|
||||
int len;
|
||||
} channels[2];
|
||||
struct st_lsm6dsx_odr_table_entry odr_table[2];
|
||||
struct st_lsm6dsx_fs_table_entry fs_table[2];
|
||||
struct st_lsm6dsx_reg decimator[ST_LSM6DSX_MAX_ID];
|
||||
struct st_lsm6dsx_reg batch[ST_LSM6DSX_MAX_ID];
|
||||
struct st_lsm6dsx_fifo_ops fifo_ops;
|
||||
|
|
|
@ -2,10 +2,10 @@
|
|||
/*
|
||||
* STMicroelectronics st_lsm6dsx FIFO buffer library driver
|
||||
*
|
||||
* LSM6DS3/LSM6DS3H/LSM6DSL/LSM6DSM/ISM330DLC: The FIFO buffer can be
|
||||
* configured to store data from gyroscope and accelerometer. Samples are
|
||||
* queued without any tag according to a specific pattern based on
|
||||
* 'FIFO data sets' (6 bytes each):
|
||||
* LSM6DS3/LSM6DS3H/LSM6DSL/LSM6DSM/ISM330DLC/LSM6DS3TR-C:
|
||||
* The FIFO buffer can be configured to store data from gyroscope and
|
||||
* accelerometer. Samples are queued without any tag according to a
|
||||
* specific pattern based on 'FIFO data sets' (6 bytes each):
|
||||
* - 1st data set is reserved for gyroscope data
|
||||
* - 2nd data set is reserved for accelerometer data
|
||||
* The FIFO pattern changes depending on the ODRs and decimation factors
|
||||
|
@ -14,9 +14,10 @@
|
|||
* (e.g. Gx, Gy, Gz, Ax, Ay, Az), then data are repeated depending on the
|
||||
* value of the decimation factor and ODR set for each FIFO data set.
|
||||
*
|
||||
* LSM6DSO/LSM6DSOX/ASM330LHH/LSM6DSR: The FIFO buffer can be configured to
|
||||
* store data from gyroscope and accelerometer. Each sample is queued with
|
||||
* a tag (1B) indicating data source (gyroscope, accelerometer, hw timer).
|
||||
* LSM6DSO/LSM6DSOX/ASM330LHH/LSM6DSR/ISM330DHCX: The FIFO buffer can be
|
||||
* configured to store data from gyroscope and accelerometer. Each sample
|
||||
* is queued with a tag (1B) indicating data source (gyroscope, accelerometer,
|
||||
* hw timer).
|
||||
*
|
||||
* FIFO supported modes:
|
||||
* - BYPASS: FIFO disabled
|
||||
|
@ -670,7 +671,7 @@ static irqreturn_t st_lsm6dsx_handler_thread(int irq, void *private)
|
|||
count = hw->settings->fifo_ops.read_fifo(hw);
|
||||
mutex_unlock(&hw->fifo_lock);
|
||||
|
||||
return !count ? IRQ_NONE : IRQ_HANDLED;
|
||||
return count ? IRQ_HANDLED : IRQ_NONE;
|
||||
}
|
||||
|
||||
static int st_lsm6dsx_buffer_preenable(struct iio_dev *iio_dev)
|
||||
|
|
|
@ -18,13 +18,13 @@
|
|||
* - Gyroscope supported full-scale [dps]: +-125/+-245/+-500/+-1000/+-2000
|
||||
* - FIFO size: 8KB
|
||||
*
|
||||
* - LSM6DS3H/LSM6DSL/LSM6DSM/ISM330DLC:
|
||||
* - LSM6DS3H/LSM6DSL/LSM6DSM/ISM330DLC/LSM6DS3TR-C:
|
||||
* - Accelerometer/Gyroscope supported ODR [Hz]: 13, 26, 52, 104, 208, 416
|
||||
* - Accelerometer supported full-scale [g]: +-2/+-4/+-8/+-16
|
||||
* - Gyroscope supported full-scale [dps]: +-125/+-245/+-500/+-1000/+-2000
|
||||
* - FIFO size: 4KB
|
||||
*
|
||||
* - LSM6DSO/LSM6DSOX/ASM330LHH/LSM6DSR
|
||||
* - LSM6DSO/LSM6DSOX/ASM330LHH/LSM6DSR/ISM330DHCX:
|
||||
* - Accelerometer/Gyroscope supported ODR [Hz]: 13, 26, 52, 104, 208, 416
|
||||
* - Accelerometer supported full-scale [g]: +-2/+-4/+-8/+-16
|
||||
* - Gyroscope supported full-scale [dps]: +-125/+-245/+-500/+-1000/+-2000
|
||||
|
@ -61,62 +61,18 @@
|
|||
#define ST_LSM6DSX_REG_INT2_ON_INT1_ADDR 0x13
|
||||
#define ST_LSM6DSX_REG_INT2_ON_INT1_MASK BIT(5)
|
||||
|
||||
#define ST_LSM6DSX_REG_ACC_OUT_X_L_ADDR 0x28
|
||||
#define ST_LSM6DSX_REG_ACC_OUT_Y_L_ADDR 0x2a
|
||||
#define ST_LSM6DSX_REG_ACC_OUT_Z_L_ADDR 0x2c
|
||||
|
||||
#define ST_LSM6DSX_REG_GYRO_OUT_X_L_ADDR 0x22
|
||||
#define ST_LSM6DSX_REG_GYRO_OUT_Y_L_ADDR 0x24
|
||||
#define ST_LSM6DSX_REG_GYRO_OUT_Z_L_ADDR 0x26
|
||||
|
||||
static const struct st_lsm6dsx_odr_table_entry st_lsm6dsx_odr_table[] = {
|
||||
[ST_LSM6DSX_ID_ACC] = {
|
||||
.reg = {
|
||||
.addr = 0x10,
|
||||
.mask = GENMASK(7, 4),
|
||||
},
|
||||
.odr_avl[0] = { 13, 0x01 },
|
||||
.odr_avl[1] = { 26, 0x02 },
|
||||
.odr_avl[2] = { 52, 0x03 },
|
||||
.odr_avl[3] = { 104, 0x04 },
|
||||
.odr_avl[4] = { 208, 0x05 },
|
||||
.odr_avl[5] = { 416, 0x06 },
|
||||
},
|
||||
[ST_LSM6DSX_ID_GYRO] = {
|
||||
.reg = {
|
||||
.addr = 0x11,
|
||||
.mask = GENMASK(7, 4),
|
||||
},
|
||||
.odr_avl[0] = { 13, 0x01 },
|
||||
.odr_avl[1] = { 26, 0x02 },
|
||||
.odr_avl[2] = { 52, 0x03 },
|
||||
.odr_avl[3] = { 104, 0x04 },
|
||||
.odr_avl[4] = { 208, 0x05 },
|
||||
.odr_avl[5] = { 416, 0x06 },
|
||||
}
|
||||
static const struct iio_chan_spec st_lsm6dsx_acc_channels[] = {
|
||||
ST_LSM6DSX_CHANNEL(IIO_ACCEL, 0x28, IIO_MOD_X, 0),
|
||||
ST_LSM6DSX_CHANNEL(IIO_ACCEL, 0x2a, IIO_MOD_Y, 1),
|
||||
ST_LSM6DSX_CHANNEL(IIO_ACCEL, 0x2c, IIO_MOD_Z, 2),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(3),
|
||||
};
|
||||
|
||||
static const struct st_lsm6dsx_fs_table_entry st_lsm6dsx_fs_table[] = {
|
||||
[ST_LSM6DSX_ID_ACC] = {
|
||||
.reg = {
|
||||
.addr = 0x10,
|
||||
.mask = GENMASK(3, 2),
|
||||
},
|
||||
.fs_avl[0] = { IIO_G_TO_M_S_2(61), 0x0 },
|
||||
.fs_avl[1] = { IIO_G_TO_M_S_2(122), 0x2 },
|
||||
.fs_avl[2] = { IIO_G_TO_M_S_2(244), 0x3 },
|
||||
.fs_avl[3] = { IIO_G_TO_M_S_2(488), 0x1 },
|
||||
},
|
||||
[ST_LSM6DSX_ID_GYRO] = {
|
||||
.reg = {
|
||||
.addr = 0x11,
|
||||
.mask = GENMASK(3, 2),
|
||||
},
|
||||
.fs_avl[0] = { IIO_DEGREE_TO_RAD(8750), 0x0 },
|
||||
.fs_avl[1] = { IIO_DEGREE_TO_RAD(17500), 0x1 },
|
||||
.fs_avl[2] = { IIO_DEGREE_TO_RAD(35000), 0x2 },
|
||||
.fs_avl[3] = { IIO_DEGREE_TO_RAD(70000), 0x3 },
|
||||
}
|
||||
static const struct iio_chan_spec st_lsm6dsx_gyro_channels[] = {
|
||||
ST_LSM6DSX_CHANNEL(IIO_ANGL_VEL, 0x22, IIO_MOD_X, 0),
|
||||
ST_LSM6DSX_CHANNEL(IIO_ANGL_VEL, 0x24, IIO_MOD_Y, 1),
|
||||
ST_LSM6DSX_CHANNEL(IIO_ANGL_VEL, 0x26, IIO_MOD_Z, 2),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(3),
|
||||
};
|
||||
|
||||
static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
|
||||
|
@ -129,6 +85,64 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
|
|||
.name = ST_LSM6DS3_DEV_NAME,
|
||||
},
|
||||
},
|
||||
.channels = {
|
||||
[ST_LSM6DSX_ID_ACC] = {
|
||||
.chan = st_lsm6dsx_acc_channels,
|
||||
.len = ARRAY_SIZE(st_lsm6dsx_acc_channels),
|
||||
},
|
||||
[ST_LSM6DSX_ID_GYRO] = {
|
||||
.chan = st_lsm6dsx_gyro_channels,
|
||||
.len = ARRAY_SIZE(st_lsm6dsx_gyro_channels),
|
||||
},
|
||||
},
|
||||
.odr_table = {
|
||||
[ST_LSM6DSX_ID_ACC] = {
|
||||
.reg = {
|
||||
.addr = 0x10,
|
||||
.mask = GENMASK(7, 4),
|
||||
},
|
||||
.odr_avl[0] = { 13, 0x01 },
|
||||
.odr_avl[1] = { 26, 0x02 },
|
||||
.odr_avl[2] = { 52, 0x03 },
|
||||
.odr_avl[3] = { 104, 0x04 },
|
||||
.odr_avl[4] = { 208, 0x05 },
|
||||
.odr_avl[5] = { 416, 0x06 },
|
||||
},
|
||||
[ST_LSM6DSX_ID_GYRO] = {
|
||||
.reg = {
|
||||
.addr = 0x11,
|
||||
.mask = GENMASK(7, 4),
|
||||
},
|
||||
.odr_avl[0] = { 13, 0x01 },
|
||||
.odr_avl[1] = { 26, 0x02 },
|
||||
.odr_avl[2] = { 52, 0x03 },
|
||||
.odr_avl[3] = { 104, 0x04 },
|
||||
.odr_avl[4] = { 208, 0x05 },
|
||||
.odr_avl[5] = { 416, 0x06 },
|
||||
},
|
||||
},
|
||||
.fs_table = {
|
||||
[ST_LSM6DSX_ID_ACC] = {
|
||||
.reg = {
|
||||
.addr = 0x10,
|
||||
.mask = GENMASK(3, 2),
|
||||
},
|
||||
.fs_avl[0] = { IIO_G_TO_M_S_2(61), 0x0 },
|
||||
.fs_avl[1] = { IIO_G_TO_M_S_2(122), 0x2 },
|
||||
.fs_avl[2] = { IIO_G_TO_M_S_2(244), 0x3 },
|
||||
.fs_avl[3] = { IIO_G_TO_M_S_2(488), 0x1 },
|
||||
},
|
||||
[ST_LSM6DSX_ID_GYRO] = {
|
||||
.reg = {
|
||||
.addr = 0x11,
|
||||
.mask = GENMASK(3, 2),
|
||||
},
|
||||
.fs_avl[0] = { IIO_DEGREE_TO_RAD(8750), 0x0 },
|
||||
.fs_avl[1] = { IIO_DEGREE_TO_RAD(17500), 0x1 },
|
||||
.fs_avl[2] = { IIO_DEGREE_TO_RAD(35000), 0x2 },
|
||||
.fs_avl[3] = { IIO_DEGREE_TO_RAD(70000), 0x3 },
|
||||
},
|
||||
},
|
||||
.decimator = {
|
||||
[ST_LSM6DSX_ID_ACC] = {
|
||||
.addr = 0x08,
|
||||
|
@ -179,6 +193,64 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
|
|||
.name = ST_LSM6DS3H_DEV_NAME,
|
||||
},
|
||||
},
|
||||
.channels = {
|
||||
[ST_LSM6DSX_ID_ACC] = {
|
||||
.chan = st_lsm6dsx_acc_channels,
|
||||
.len = ARRAY_SIZE(st_lsm6dsx_acc_channels),
|
||||
},
|
||||
[ST_LSM6DSX_ID_GYRO] = {
|
||||
.chan = st_lsm6dsx_gyro_channels,
|
||||
.len = ARRAY_SIZE(st_lsm6dsx_gyro_channels),
|
||||
},
|
||||
},
|
||||
.odr_table = {
|
||||
[ST_LSM6DSX_ID_ACC] = {
|
||||
.reg = {
|
||||
.addr = 0x10,
|
||||
.mask = GENMASK(7, 4),
|
||||
},
|
||||
.odr_avl[0] = { 13, 0x01 },
|
||||
.odr_avl[1] = { 26, 0x02 },
|
||||
.odr_avl[2] = { 52, 0x03 },
|
||||
.odr_avl[3] = { 104, 0x04 },
|
||||
.odr_avl[4] = { 208, 0x05 },
|
||||
.odr_avl[5] = { 416, 0x06 },
|
||||
},
|
||||
[ST_LSM6DSX_ID_GYRO] = {
|
||||
.reg = {
|
||||
.addr = 0x11,
|
||||
.mask = GENMASK(7, 4),
|
||||
},
|
||||
.odr_avl[0] = { 13, 0x01 },
|
||||
.odr_avl[1] = { 26, 0x02 },
|
||||
.odr_avl[2] = { 52, 0x03 },
|
||||
.odr_avl[3] = { 104, 0x04 },
|
||||
.odr_avl[4] = { 208, 0x05 },
|
||||
.odr_avl[5] = { 416, 0x06 },
|
||||
},
|
||||
},
|
||||
.fs_table = {
|
||||
[ST_LSM6DSX_ID_ACC] = {
|
||||
.reg = {
|
||||
.addr = 0x10,
|
||||
.mask = GENMASK(3, 2),
|
||||
},
|
||||
.fs_avl[0] = { IIO_G_TO_M_S_2(61), 0x0 },
|
||||
.fs_avl[1] = { IIO_G_TO_M_S_2(122), 0x2 },
|
||||
.fs_avl[2] = { IIO_G_TO_M_S_2(244), 0x3 },
|
||||
.fs_avl[3] = { IIO_G_TO_M_S_2(488), 0x1 },
|
||||
},
|
||||
[ST_LSM6DSX_ID_GYRO] = {
|
||||
.reg = {
|
||||
.addr = 0x11,
|
||||
.mask = GENMASK(3, 2),
|
||||
},
|
||||
.fs_avl[0] = { IIO_DEGREE_TO_RAD(8750), 0x0 },
|
||||
.fs_avl[1] = { IIO_DEGREE_TO_RAD(17500), 0x1 },
|
||||
.fs_avl[2] = { IIO_DEGREE_TO_RAD(35000), 0x2 },
|
||||
.fs_avl[3] = { IIO_DEGREE_TO_RAD(70000), 0x3 },
|
||||
},
|
||||
},
|
||||
.decimator = {
|
||||
[ST_LSM6DSX_ID_ACC] = {
|
||||
.addr = 0x08,
|
||||
|
@ -233,6 +305,67 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
|
|||
}, {
|
||||
.hw_id = ST_ISM330DLC_ID,
|
||||
.name = ST_ISM330DLC_DEV_NAME,
|
||||
}, {
|
||||
.hw_id = ST_LSM6DS3TRC_ID,
|
||||
.name = ST_LSM6DS3TRC_DEV_NAME,
|
||||
},
|
||||
},
|
||||
.channels = {
|
||||
[ST_LSM6DSX_ID_ACC] = {
|
||||
.chan = st_lsm6dsx_acc_channels,
|
||||
.len = ARRAY_SIZE(st_lsm6dsx_acc_channels),
|
||||
},
|
||||
[ST_LSM6DSX_ID_GYRO] = {
|
||||
.chan = st_lsm6dsx_gyro_channels,
|
||||
.len = ARRAY_SIZE(st_lsm6dsx_gyro_channels),
|
||||
},
|
||||
},
|
||||
.odr_table = {
|
||||
[ST_LSM6DSX_ID_ACC] = {
|
||||
.reg = {
|
||||
.addr = 0x10,
|
||||
.mask = GENMASK(7, 4),
|
||||
},
|
||||
.odr_avl[0] = { 13, 0x01 },
|
||||
.odr_avl[1] = { 26, 0x02 },
|
||||
.odr_avl[2] = { 52, 0x03 },
|
||||
.odr_avl[3] = { 104, 0x04 },
|
||||
.odr_avl[4] = { 208, 0x05 },
|
||||
.odr_avl[5] = { 416, 0x06 },
|
||||
},
|
||||
[ST_LSM6DSX_ID_GYRO] = {
|
||||
.reg = {
|
||||
.addr = 0x11,
|
||||
.mask = GENMASK(7, 4),
|
||||
},
|
||||
.odr_avl[0] = { 13, 0x01 },
|
||||
.odr_avl[1] = { 26, 0x02 },
|
||||
.odr_avl[2] = { 52, 0x03 },
|
||||
.odr_avl[3] = { 104, 0x04 },
|
||||
.odr_avl[4] = { 208, 0x05 },
|
||||
.odr_avl[5] = { 416, 0x06 },
|
||||
},
|
||||
},
|
||||
.fs_table = {
|
||||
[ST_LSM6DSX_ID_ACC] = {
|
||||
.reg = {
|
||||
.addr = 0x10,
|
||||
.mask = GENMASK(3, 2),
|
||||
},
|
||||
.fs_avl[0] = { IIO_G_TO_M_S_2(61), 0x0 },
|
||||
.fs_avl[1] = { IIO_G_TO_M_S_2(122), 0x2 },
|
||||
.fs_avl[2] = { IIO_G_TO_M_S_2(244), 0x3 },
|
||||
.fs_avl[3] = { IIO_G_TO_M_S_2(488), 0x1 },
|
||||
},
|
||||
[ST_LSM6DSX_ID_GYRO] = {
|
||||
.reg = {
|
||||
.addr = 0x11,
|
||||
.mask = GENMASK(3, 2),
|
||||
},
|
||||
.fs_avl[0] = { IIO_DEGREE_TO_RAD(8750), 0x0 },
|
||||
.fs_avl[1] = { IIO_DEGREE_TO_RAD(17500), 0x1 },
|
||||
.fs_avl[2] = { IIO_DEGREE_TO_RAD(35000), 0x2 },
|
||||
.fs_avl[3] = { IIO_DEGREE_TO_RAD(70000), 0x3 },
|
||||
},
|
||||
},
|
||||
.decimator = {
|
||||
|
@ -288,6 +421,64 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
|
|||
.name = ST_LSM6DSOX_DEV_NAME,
|
||||
},
|
||||
},
|
||||
.channels = {
|
||||
[ST_LSM6DSX_ID_ACC] = {
|
||||
.chan = st_lsm6dsx_acc_channels,
|
||||
.len = ARRAY_SIZE(st_lsm6dsx_acc_channels),
|
||||
},
|
||||
[ST_LSM6DSX_ID_GYRO] = {
|
||||
.chan = st_lsm6dsx_gyro_channels,
|
||||
.len = ARRAY_SIZE(st_lsm6dsx_gyro_channels),
|
||||
},
|
||||
},
|
||||
.odr_table = {
|
||||
[ST_LSM6DSX_ID_ACC] = {
|
||||
.reg = {
|
||||
.addr = 0x10,
|
||||
.mask = GENMASK(7, 4),
|
||||
},
|
||||
.odr_avl[0] = { 13, 0x01 },
|
||||
.odr_avl[1] = { 26, 0x02 },
|
||||
.odr_avl[2] = { 52, 0x03 },
|
||||
.odr_avl[3] = { 104, 0x04 },
|
||||
.odr_avl[4] = { 208, 0x05 },
|
||||
.odr_avl[5] = { 416, 0x06 },
|
||||
},
|
||||
[ST_LSM6DSX_ID_GYRO] = {
|
||||
.reg = {
|
||||
.addr = 0x11,
|
||||
.mask = GENMASK(7, 4),
|
||||
},
|
||||
.odr_avl[0] = { 13, 0x01 },
|
||||
.odr_avl[1] = { 26, 0x02 },
|
||||
.odr_avl[2] = { 52, 0x03 },
|
||||
.odr_avl[3] = { 104, 0x04 },
|
||||
.odr_avl[4] = { 208, 0x05 },
|
||||
.odr_avl[5] = { 416, 0x06 },
|
||||
},
|
||||
},
|
||||
.fs_table = {
|
||||
[ST_LSM6DSX_ID_ACC] = {
|
||||
.reg = {
|
||||
.addr = 0x10,
|
||||
.mask = GENMASK(3, 2),
|
||||
},
|
||||
.fs_avl[0] = { IIO_G_TO_M_S_2(61), 0x0 },
|
||||
.fs_avl[1] = { IIO_G_TO_M_S_2(122), 0x2 },
|
||||
.fs_avl[2] = { IIO_G_TO_M_S_2(244), 0x3 },
|
||||
.fs_avl[3] = { IIO_G_TO_M_S_2(488), 0x1 },
|
||||
},
|
||||
[ST_LSM6DSX_ID_GYRO] = {
|
||||
.reg = {
|
||||
.addr = 0x11,
|
||||
.mask = GENMASK(3, 2),
|
||||
},
|
||||
.fs_avl[0] = { IIO_DEGREE_TO_RAD(8750), 0x0 },
|
||||
.fs_avl[1] = { IIO_DEGREE_TO_RAD(17500), 0x1 },
|
||||
.fs_avl[2] = { IIO_DEGREE_TO_RAD(35000), 0x2 },
|
||||
.fs_avl[3] = { IIO_DEGREE_TO_RAD(70000), 0x3 },
|
||||
},
|
||||
},
|
||||
.batch = {
|
||||
[ST_LSM6DSX_ID_ACC] = {
|
||||
.addr = 0x09,
|
||||
|
@ -356,6 +547,64 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
|
|||
.name = ST_ASM330LHH_DEV_NAME,
|
||||
},
|
||||
},
|
||||
.channels = {
|
||||
[ST_LSM6DSX_ID_ACC] = {
|
||||
.chan = st_lsm6dsx_acc_channels,
|
||||
.len = ARRAY_SIZE(st_lsm6dsx_acc_channels),
|
||||
},
|
||||
[ST_LSM6DSX_ID_GYRO] = {
|
||||
.chan = st_lsm6dsx_gyro_channels,
|
||||
.len = ARRAY_SIZE(st_lsm6dsx_gyro_channels),
|
||||
},
|
||||
},
|
||||
.odr_table = {
|
||||
[ST_LSM6DSX_ID_ACC] = {
|
||||
.reg = {
|
||||
.addr = 0x10,
|
||||
.mask = GENMASK(7, 4),
|
||||
},
|
||||
.odr_avl[0] = { 13, 0x01 },
|
||||
.odr_avl[1] = { 26, 0x02 },
|
||||
.odr_avl[2] = { 52, 0x03 },
|
||||
.odr_avl[3] = { 104, 0x04 },
|
||||
.odr_avl[4] = { 208, 0x05 },
|
||||
.odr_avl[5] = { 416, 0x06 },
|
||||
},
|
||||
[ST_LSM6DSX_ID_GYRO] = {
|
||||
.reg = {
|
||||
.addr = 0x11,
|
||||
.mask = GENMASK(7, 4),
|
||||
},
|
||||
.odr_avl[0] = { 13, 0x01 },
|
||||
.odr_avl[1] = { 26, 0x02 },
|
||||
.odr_avl[2] = { 52, 0x03 },
|
||||
.odr_avl[3] = { 104, 0x04 },
|
||||
.odr_avl[4] = { 208, 0x05 },
|
||||
.odr_avl[5] = { 416, 0x06 },
|
||||
},
|
||||
},
|
||||
.fs_table = {
|
||||
[ST_LSM6DSX_ID_ACC] = {
|
||||
.reg = {
|
||||
.addr = 0x10,
|
||||
.mask = GENMASK(3, 2),
|
||||
},
|
||||
.fs_avl[0] = { IIO_G_TO_M_S_2(61), 0x0 },
|
||||
.fs_avl[1] = { IIO_G_TO_M_S_2(122), 0x2 },
|
||||
.fs_avl[2] = { IIO_G_TO_M_S_2(244), 0x3 },
|
||||
.fs_avl[3] = { IIO_G_TO_M_S_2(488), 0x1 },
|
||||
},
|
||||
[ST_LSM6DSX_ID_GYRO] = {
|
||||
.reg = {
|
||||
.addr = 0x11,
|
||||
.mask = GENMASK(3, 2),
|
||||
},
|
||||
.fs_avl[0] = { IIO_DEGREE_TO_RAD(8750), 0x0 },
|
||||
.fs_avl[1] = { IIO_DEGREE_TO_RAD(17500), 0x1 },
|
||||
.fs_avl[2] = { IIO_DEGREE_TO_RAD(35000), 0x2 },
|
||||
.fs_avl[3] = { IIO_DEGREE_TO_RAD(70000), 0x3 },
|
||||
},
|
||||
},
|
||||
.batch = {
|
||||
[ST_LSM6DSX_ID_ACC] = {
|
||||
.addr = 0x09,
|
||||
|
@ -396,6 +645,67 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
|
|||
{
|
||||
.hw_id = ST_LSM6DSR_ID,
|
||||
.name = ST_LSM6DSR_DEV_NAME,
|
||||
}, {
|
||||
.hw_id = ST_ISM330DHCX_ID,
|
||||
.name = ST_ISM330DHCX_DEV_NAME,
|
||||
},
|
||||
},
|
||||
.channels = {
|
||||
[ST_LSM6DSX_ID_ACC] = {
|
||||
.chan = st_lsm6dsx_acc_channels,
|
||||
.len = ARRAY_SIZE(st_lsm6dsx_acc_channels),
|
||||
},
|
||||
[ST_LSM6DSX_ID_GYRO] = {
|
||||
.chan = st_lsm6dsx_gyro_channels,
|
||||
.len = ARRAY_SIZE(st_lsm6dsx_gyro_channels),
|
||||
},
|
||||
},
|
||||
.odr_table = {
|
||||
[ST_LSM6DSX_ID_ACC] = {
|
||||
.reg = {
|
||||
.addr = 0x10,
|
||||
.mask = GENMASK(7, 4),
|
||||
},
|
||||
.odr_avl[0] = { 13, 0x01 },
|
||||
.odr_avl[1] = { 26, 0x02 },
|
||||
.odr_avl[2] = { 52, 0x03 },
|
||||
.odr_avl[3] = { 104, 0x04 },
|
||||
.odr_avl[4] = { 208, 0x05 },
|
||||
.odr_avl[5] = { 416, 0x06 },
|
||||
},
|
||||
[ST_LSM6DSX_ID_GYRO] = {
|
||||
.reg = {
|
||||
.addr = 0x11,
|
||||
.mask = GENMASK(7, 4),
|
||||
},
|
||||
.odr_avl[0] = { 13, 0x01 },
|
||||
.odr_avl[1] = { 26, 0x02 },
|
||||
.odr_avl[2] = { 52, 0x03 },
|
||||
.odr_avl[3] = { 104, 0x04 },
|
||||
.odr_avl[4] = { 208, 0x05 },
|
||||
.odr_avl[5] = { 416, 0x06 },
|
||||
},
|
||||
},
|
||||
.fs_table = {
|
||||
[ST_LSM6DSX_ID_ACC] = {
|
||||
.reg = {
|
||||
.addr = 0x10,
|
||||
.mask = GENMASK(3, 2),
|
||||
},
|
||||
.fs_avl[0] = { IIO_G_TO_M_S_2(61), 0x0 },
|
||||
.fs_avl[1] = { IIO_G_TO_M_S_2(122), 0x2 },
|
||||
.fs_avl[2] = { IIO_G_TO_M_S_2(244), 0x3 },
|
||||
.fs_avl[3] = { IIO_G_TO_M_S_2(488), 0x1 },
|
||||
},
|
||||
[ST_LSM6DSX_ID_GYRO] = {
|
||||
.reg = {
|
||||
.addr = 0x11,
|
||||
.mask = GENMASK(3, 2),
|
||||
},
|
||||
.fs_avl[0] = { IIO_DEGREE_TO_RAD(8750), 0x0 },
|
||||
.fs_avl[1] = { IIO_DEGREE_TO_RAD(17500), 0x1 },
|
||||
.fs_avl[2] = { IIO_DEGREE_TO_RAD(35000), 0x2 },
|
||||
.fs_avl[3] = { IIO_DEGREE_TO_RAD(70000), 0x3 },
|
||||
},
|
||||
},
|
||||
.batch = {
|
||||
|
@ -459,26 +769,6 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
|
|||
},
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec st_lsm6dsx_acc_channels[] = {
|
||||
ST_LSM6DSX_CHANNEL(IIO_ACCEL, ST_LSM6DSX_REG_ACC_OUT_X_L_ADDR,
|
||||
IIO_MOD_X, 0),
|
||||
ST_LSM6DSX_CHANNEL(IIO_ACCEL, ST_LSM6DSX_REG_ACC_OUT_Y_L_ADDR,
|
||||
IIO_MOD_Y, 1),
|
||||
ST_LSM6DSX_CHANNEL(IIO_ACCEL, ST_LSM6DSX_REG_ACC_OUT_Z_L_ADDR,
|
||||
IIO_MOD_Z, 2),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(3),
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec st_lsm6dsx_gyro_channels[] = {
|
||||
ST_LSM6DSX_CHANNEL(IIO_ANGL_VEL, ST_LSM6DSX_REG_GYRO_OUT_X_L_ADDR,
|
||||
IIO_MOD_X, 0),
|
||||
ST_LSM6DSX_CHANNEL(IIO_ANGL_VEL, ST_LSM6DSX_REG_GYRO_OUT_Y_L_ADDR,
|
||||
IIO_MOD_Y, 1),
|
||||
ST_LSM6DSX_CHANNEL(IIO_ANGL_VEL, ST_LSM6DSX_REG_GYRO_OUT_Z_L_ADDR,
|
||||
IIO_MOD_Z, 2),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(3),
|
||||
};
|
||||
|
||||
int st_lsm6dsx_set_page(struct st_lsm6dsx_hw *hw, bool enable)
|
||||
{
|
||||
const struct st_lsm6dsx_shub_settings *hub_settings;
|
||||
|
@ -533,23 +823,22 @@ static int st_lsm6dsx_check_whoami(struct st_lsm6dsx_hw *hw, int id,
|
|||
static int st_lsm6dsx_set_full_scale(struct st_lsm6dsx_sensor *sensor,
|
||||
u32 gain)
|
||||
{
|
||||
struct st_lsm6dsx_hw *hw = sensor->hw;
|
||||
const struct st_lsm6dsx_reg *reg;
|
||||
const struct st_lsm6dsx_fs_table_entry *fs_table;
|
||||
unsigned int data;
|
||||
int i, err;
|
||||
u8 val;
|
||||
|
||||
fs_table = &sensor->hw->settings->fs_table[sensor->id];
|
||||
for (i = 0; i < ST_LSM6DSX_FS_LIST_SIZE; i++)
|
||||
if (st_lsm6dsx_fs_table[sensor->id].fs_avl[i].gain == gain)
|
||||
if (fs_table->fs_avl[i].gain == gain)
|
||||
break;
|
||||
|
||||
if (i == ST_LSM6DSX_FS_LIST_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
val = st_lsm6dsx_fs_table[sensor->id].fs_avl[i].val;
|
||||
reg = &st_lsm6dsx_fs_table[sensor->id].reg;
|
||||
data = ST_LSM6DSX_SHIFT_VAL(val, reg->mask);
|
||||
err = st_lsm6dsx_update_bits_locked(hw, reg->addr, reg->mask, data);
|
||||
data = ST_LSM6DSX_SHIFT_VAL(fs_table->fs_avl[i].val,
|
||||
fs_table->reg.mask);
|
||||
err = st_lsm6dsx_update_bits_locked(sensor->hw, fs_table->reg.addr,
|
||||
fs_table->reg.mask, data);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
|
@ -560,20 +849,22 @@ static int st_lsm6dsx_set_full_scale(struct st_lsm6dsx_sensor *sensor,
|
|||
|
||||
int st_lsm6dsx_check_odr(struct st_lsm6dsx_sensor *sensor, u16 odr, u8 *val)
|
||||
{
|
||||
const struct st_lsm6dsx_odr_table_entry *odr_table;
|
||||
int i;
|
||||
|
||||
odr_table = &sensor->hw->settings->odr_table[sensor->id];
|
||||
for (i = 0; i < ST_LSM6DSX_ODR_LIST_SIZE; i++)
|
||||
/*
|
||||
* ext devices can run at different odr respect to
|
||||
* accel sensor
|
||||
*/
|
||||
if (st_lsm6dsx_odr_table[sensor->id].odr_avl[i].hz >= odr)
|
||||
if (odr_table->odr_avl[i].hz >= odr)
|
||||
break;
|
||||
|
||||
if (i == ST_LSM6DSX_ODR_LIST_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
*val = st_lsm6dsx_odr_table[sensor->id].odr_avl[i].val;
|
||||
*val = odr_table->odr_avl[i].val;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -638,7 +929,7 @@ static int st_lsm6dsx_set_odr(struct st_lsm6dsx_sensor *sensor, u16 req_odr)
|
|||
return err;
|
||||
}
|
||||
|
||||
reg = &st_lsm6dsx_odr_table[ref_sensor->id].reg;
|
||||
reg = &hw->settings->odr_table[ref_sensor->id].reg;
|
||||
data = ST_LSM6DSX_SHIFT_VAL(val, reg->mask);
|
||||
return st_lsm6dsx_update_bits_locked(hw, reg->addr, reg->mask, data);
|
||||
}
|
||||
|
@ -783,11 +1074,12 @@ st_lsm6dsx_sysfs_sampling_frequency_avail(struct device *dev,
|
|||
{
|
||||
struct st_lsm6dsx_sensor *sensor = iio_priv(dev_get_drvdata(dev));
|
||||
enum st_lsm6dsx_sensor_id id = sensor->id;
|
||||
struct st_lsm6dsx_hw *hw = sensor->hw;
|
||||
int i, len = 0;
|
||||
|
||||
for (i = 0; i < ST_LSM6DSX_ODR_LIST_SIZE; i++)
|
||||
len += scnprintf(buf + len, PAGE_SIZE - len, "%d ",
|
||||
st_lsm6dsx_odr_table[id].odr_avl[i].hz);
|
||||
hw->settings->odr_table[id].odr_avl[i].hz);
|
||||
buf[len - 1] = '\n';
|
||||
|
||||
return len;
|
||||
|
@ -799,11 +1091,12 @@ static ssize_t st_lsm6dsx_sysfs_scale_avail(struct device *dev,
|
|||
{
|
||||
struct st_lsm6dsx_sensor *sensor = iio_priv(dev_get_drvdata(dev));
|
||||
enum st_lsm6dsx_sensor_id id = sensor->id;
|
||||
struct st_lsm6dsx_hw *hw = sensor->hw;
|
||||
int i, len = 0;
|
||||
|
||||
for (i = 0; i < ST_LSM6DSX_FS_LIST_SIZE; i++)
|
||||
len += scnprintf(buf + len, PAGE_SIZE - len, "0.%06u ",
|
||||
st_lsm6dsx_fs_table[id].fs_avl[i].gain);
|
||||
hw->settings->fs_table[id].fs_avl[i].gain);
|
||||
buf[len - 1] = '\n';
|
||||
|
||||
return len;
|
||||
|
@ -1033,28 +1326,24 @@ static struct iio_dev *st_lsm6dsx_alloc_iiodev(struct st_lsm6dsx_hw *hw,
|
|||
iio_dev->modes = INDIO_DIRECT_MODE;
|
||||
iio_dev->dev.parent = hw->dev;
|
||||
iio_dev->available_scan_masks = st_lsm6dsx_available_scan_masks;
|
||||
iio_dev->channels = hw->settings->channels[id].chan;
|
||||
iio_dev->num_channels = hw->settings->channels[id].len;
|
||||
|
||||
sensor = iio_priv(iio_dev);
|
||||
sensor->id = id;
|
||||
sensor->hw = hw;
|
||||
sensor->odr = st_lsm6dsx_odr_table[id].odr_avl[0].hz;
|
||||
sensor->gain = st_lsm6dsx_fs_table[id].fs_avl[0].gain;
|
||||
sensor->odr = hw->settings->odr_table[id].odr_avl[0].hz;
|
||||
sensor->gain = hw->settings->fs_table[id].fs_avl[0].gain;
|
||||
sensor->watermark = 1;
|
||||
|
||||
switch (id) {
|
||||
case ST_LSM6DSX_ID_ACC:
|
||||
iio_dev->channels = st_lsm6dsx_acc_channels;
|
||||
iio_dev->num_channels = ARRAY_SIZE(st_lsm6dsx_acc_channels);
|
||||
iio_dev->info = &st_lsm6dsx_acc_info;
|
||||
|
||||
scnprintf(sensor->name, sizeof(sensor->name), "%s_accel",
|
||||
name);
|
||||
break;
|
||||
case ST_LSM6DSX_ID_GYRO:
|
||||
iio_dev->channels = st_lsm6dsx_gyro_channels;
|
||||
iio_dev->num_channels = ARRAY_SIZE(st_lsm6dsx_gyro_channels);
|
||||
iio_dev->info = &st_lsm6dsx_gyro_info;
|
||||
|
||||
scnprintf(sensor->name, sizeof(sensor->name), "%s_gyro",
|
||||
name);
|
||||
break;
|
||||
|
|
|
@ -75,6 +75,14 @@ static const struct of_device_id st_lsm6dsx_i2c_of_match[] = {
|
|||
.compatible = "st,lsm6dsr",
|
||||
.data = (void *)ST_LSM6DSR_ID,
|
||||
},
|
||||
{
|
||||
.compatible = "st,lsm6ds3tr-c",
|
||||
.data = (void *)ST_LSM6DS3TRC_ID,
|
||||
},
|
||||
{
|
||||
.compatible = "st,ism330dhcx",
|
||||
.data = (void *)ST_ISM330DHCX_ID,
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, st_lsm6dsx_i2c_of_match);
|
||||
|
@ -89,6 +97,8 @@ static const struct i2c_device_id st_lsm6dsx_i2c_id_table[] = {
|
|||
{ ST_ASM330LHH_DEV_NAME, ST_ASM330LHH_ID },
|
||||
{ ST_LSM6DSOX_DEV_NAME, ST_LSM6DSOX_ID },
|
||||
{ ST_LSM6DSR_DEV_NAME, ST_LSM6DSR_ID },
|
||||
{ ST_LSM6DS3TRC_DEV_NAME, ST_LSM6DS3TRC_ID },
|
||||
{ ST_ISM330DHCX_DEV_NAME, ST_ISM330DHCX_ID },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, st_lsm6dsx_i2c_id_table);
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2018 Synopsys, Inc. and/or its affiliates.
|
||||
*
|
||||
* Author: Vitor Soares <vitor.soares@synopsys.com>
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/i3c/device.h>
|
||||
#include <linux/i3c/master.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include "st_lsm6dsx.h"
|
||||
|
||||
static const struct i3c_device_id st_lsm6dsx_i3c_ids[] = {
|
||||
I3C_DEVICE(0x0104, 0x006C, (void *)ST_LSM6DSO_ID),
|
||||
I3C_DEVICE(0x0104, 0x006B, (void *)ST_LSM6DSR_ID),
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i3c, st_lsm6dsx_i3c_ids);
|
||||
|
||||
static int st_lsm6dsx_i3c_probe(struct i3c_device *i3cdev)
|
||||
{
|
||||
struct regmap_config st_lsm6dsx_i3c_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
};
|
||||
const struct i3c_device_id *id = i3c_device_match_id(i3cdev,
|
||||
st_lsm6dsx_i3c_ids);
|
||||
struct regmap *regmap;
|
||||
|
||||
regmap = devm_regmap_init_i3c(i3cdev, &st_lsm6dsx_i3c_regmap_config);
|
||||
if (IS_ERR(regmap)) {
|
||||
dev_err(&i3cdev->dev, "Failed to register i3c regmap %d\n",
|
||||
(int)PTR_ERR(regmap));
|
||||
return PTR_ERR(regmap);
|
||||
}
|
||||
|
||||
return st_lsm6dsx_probe(&i3cdev->dev, 0, (uintptr_t)id->data, regmap);
|
||||
}
|
||||
|
||||
static struct i3c_driver st_lsm6dsx_driver = {
|
||||
.driver = {
|
||||
.name = "st_lsm6dsx_i3c",
|
||||
.pm = &st_lsm6dsx_pm_ops,
|
||||
},
|
||||
.probe = st_lsm6dsx_i3c_probe,
|
||||
.id_table = st_lsm6dsx_i3c_ids,
|
||||
};
|
||||
module_i3c_driver(st_lsm6dsx_driver);
|
||||
|
||||
MODULE_AUTHOR("Vitor Soares <vitor.soares@synopsys.com>");
|
||||
MODULE_DESCRIPTION("STMicroelectronics st_lsm6dsx i3c driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -75,6 +75,14 @@ static const struct of_device_id st_lsm6dsx_spi_of_match[] = {
|
|||
.compatible = "st,lsm6dsr",
|
||||
.data = (void *)ST_LSM6DSR_ID,
|
||||
},
|
||||
{
|
||||
.compatible = "st,lsm6ds3tr-c",
|
||||
.data = (void *)ST_LSM6DS3TRC_ID,
|
||||
},
|
||||
{
|
||||
.compatible = "st,ism330dhcx",
|
||||
.data = (void *)ST_ISM330DHCX_ID,
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, st_lsm6dsx_spi_of_match);
|
||||
|
@ -89,6 +97,8 @@ static const struct spi_device_id st_lsm6dsx_spi_id_table[] = {
|
|||
{ ST_ASM330LHH_DEV_NAME, ST_ASM330LHH_ID },
|
||||
{ ST_LSM6DSOX_DEV_NAME, ST_LSM6DSOX_ID },
|
||||
{ ST_LSM6DSR_DEV_NAME, ST_LSM6DSR_ID },
|
||||
{ ST_LSM6DS3TRC_DEV_NAME, ST_LSM6DS3TRC_ID },
|
||||
{ ST_ISM330DHCX_DEV_NAME, ST_ISM330DHCX_ID },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, st_lsm6dsx_spi_id_table);
|
||||
|
|
|
@ -303,6 +303,7 @@ config MAX44000
|
|||
config MAX44009
|
||||
tristate "MAX44009 Ambient Light Sensor"
|
||||
depends on I2C
|
||||
select REGMAP_I2C
|
||||
help
|
||||
Say Y here if you want to build support for Maxim Integrated's
|
||||
MAX44009 ambient light sensor device.
|
||||
|
@ -310,6 +311,16 @@ config MAX44009
|
|||
To compile this driver as a module, choose M here:
|
||||
the module will be called max44009.
|
||||
|
||||
config NOA1305
|
||||
tristate "ON Semiconductor NOA1305 ambient light sensor"
|
||||
depends on I2C
|
||||
help
|
||||
Say Y here if you want to build support for the ON Semiconductor
|
||||
NOA1305 ambient light sensor.
|
||||
|
||||
To compile this driver as a module, choose M here:
|
||||
The module will be called noa1305.
|
||||
|
||||
config OPT3001
|
||||
tristate "Texas Instruments OPT3001 Light Sensor"
|
||||
depends on I2C
|
||||
|
|
|
@ -29,6 +29,7 @@ obj-$(CONFIG_LTR501) += ltr501.o
|
|||
obj-$(CONFIG_LV0104CS) += lv0104cs.o
|
||||
obj-$(CONFIG_MAX44000) += max44000.o
|
||||
obj-$(CONFIG_MAX44009) += max44009.o
|
||||
obj-$(CONFIG_NOA1305) += noa1305.o
|
||||
obj-$(CONFIG_OPT3001) += opt3001.o
|
||||
obj-$(CONFIG_PA12203001) += pa12203001.o
|
||||
obj-$(CONFIG_RPR0521) += rpr0521.o
|
||||
|
|
|
@ -1135,5 +1135,5 @@ static struct i2c_driver apds9960_driver = {
|
|||
module_i2c_driver(apds9960_driver);
|
||||
|
||||
MODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>");
|
||||
MODULE_DESCRIPTION("ADPS9960 Gesture/RGB/ALS/Proximity sensor");
|
||||
MODULE_DESCRIPTION("APDS9960 Gesture/RGB/ALS/Proximity sensor");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
@ -101,15 +101,16 @@ static int cm3323_init(struct iio_dev *indio_dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void cm3323_disable(struct iio_dev *indio_dev)
|
||||
static void cm3323_disable(void *data)
|
||||
{
|
||||
int ret;
|
||||
struct cm3323_data *data = iio_priv(indio_dev);
|
||||
struct iio_dev *indio_dev = data;
|
||||
struct cm3323_data *cm_data = iio_priv(indio_dev);
|
||||
|
||||
ret = i2c_smbus_write_word_data(data->client, CM3323_CMD_CONF,
|
||||
ret = i2c_smbus_write_word_data(cm_data->client, CM3323_CMD_CONF,
|
||||
CM3323_CONF_SD_BIT);
|
||||
if (ret < 0)
|
||||
dev_err(&data->client->dev, "Error writing reg_conf\n");
|
||||
dev_err(&cm_data->client->dev, "Error writing reg_conf\n");
|
||||
}
|
||||
|
||||
static int cm3323_set_it_bits(struct cm3323_data *data, int val, int val2)
|
||||
|
@ -243,26 +244,11 @@ static int cm3323_probe(struct i2c_client *client,
|
|||
return ret;
|
||||
}
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "failed to register iio dev\n");
|
||||
goto err_init;
|
||||
}
|
||||
ret = devm_add_action_or_reset(&client->dev, cm3323_disable, indio_dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
err_init:
|
||||
cm3323_disable(indio_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cm3323_remove(struct i2c_client *client)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
cm3323_disable(indio_dev);
|
||||
|
||||
return 0;
|
||||
return devm_iio_device_register(&client->dev, indio_dev);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id cm3323_id[] = {
|
||||
|
@ -276,7 +262,6 @@ static struct i2c_driver cm3323_driver = {
|
|||
.name = CM3323_DRV_NAME,
|
||||
},
|
||||
.probe = cm3323_probe,
|
||||
.remove = cm3323_remove,
|
||||
.id_table = cm3323_id,
|
||||
};
|
||||
|
||||
|
|
|
@ -646,18 +646,18 @@ static int cm36651_probe(struct i2c_client *client,
|
|||
i2c_set_clientdata(client, indio_dev);
|
||||
|
||||
cm36651->client = client;
|
||||
cm36651->ps_client = i2c_new_dummy(client->adapter,
|
||||
cm36651->ps_client = i2c_new_dummy_device(client->adapter,
|
||||
CM36651_I2C_ADDR_PS);
|
||||
if (!cm36651->ps_client) {
|
||||
if (IS_ERR(cm36651->ps_client)) {
|
||||
dev_err(&client->dev, "%s: new i2c device failed\n", __func__);
|
||||
ret = -ENODEV;
|
||||
ret = PTR_ERR(cm36651->ps_client);
|
||||
goto error_disable_reg;
|
||||
}
|
||||
|
||||
cm36651->ara_client = i2c_new_dummy(client->adapter, CM36651_ARA);
|
||||
if (!cm36651->ara_client) {
|
||||
cm36651->ara_client = i2c_new_dummy_device(client->adapter, CM36651_ARA);
|
||||
if (IS_ERR(cm36651->ara_client)) {
|
||||
dev_err(&client->dev, "%s: new i2c device failed\n", __func__);
|
||||
ret = -ENODEV;
|
||||
ret = PTR_ERR(cm36651->ara_client);
|
||||
goto error_i2c_unregister_ps;
|
||||
}
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ static int cros_ec_light_prox_read(struct iio_dev *indio_dev,
|
|||
struct cros_ec_light_prox_state *st = iio_priv(indio_dev);
|
||||
u16 data = 0;
|
||||
s64 val64;
|
||||
int ret = IIO_VAL_INT;
|
||||
int ret;
|
||||
int idx = chan->scan_index;
|
||||
|
||||
mutex_lock(&st->core.cmd_lock);
|
||||
|
@ -50,23 +50,22 @@ static int cros_ec_light_prox_read(struct iio_dev *indio_dev,
|
|||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (chan->type == IIO_PROXIMITY) {
|
||||
if (cros_ec_sensors_read_cmd(indio_dev, 1 << idx,
|
||||
(s16 *)&data) < 0) {
|
||||
ret = -EIO;
|
||||
ret = cros_ec_sensors_read_cmd(indio_dev, 1 << idx,
|
||||
(s16 *)&data);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
*val = data;
|
||||
ret = IIO_VAL_INT;
|
||||
} else {
|
||||
ret = -EINVAL;
|
||||
}
|
||||
break;
|
||||
case IIO_CHAN_INFO_PROCESSED:
|
||||
if (chan->type == IIO_LIGHT) {
|
||||
if (cros_ec_sensors_read_cmd(indio_dev, 1 << idx,
|
||||
(s16 *)&data) < 0) {
|
||||
ret = -EIO;
|
||||
ret = cros_ec_sensors_read_cmd(indio_dev, 1 << idx,
|
||||
(s16 *)&data);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* The data coming from the light sensor is
|
||||
* pre-processed and represents the ambient light
|
||||
|
@ -82,15 +81,16 @@ static int cros_ec_light_prox_read(struct iio_dev *indio_dev,
|
|||
st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_OFFSET;
|
||||
st->core.param.sensor_offset.flags = 0;
|
||||
|
||||
if (cros_ec_motion_send_host_cmd(&st->core, 0)) {
|
||||
ret = -EIO;
|
||||
ret = cros_ec_motion_send_host_cmd(&st->core, 0);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
|
||||
/* Save values */
|
||||
st->core.calib[0] = st->core.resp->sensor_offset.offset[0];
|
||||
st->core.calib[0].offset =
|
||||
st->core.resp->sensor_offset.offset[0];
|
||||
|
||||
*val = st->core.calib[idx];
|
||||
*val = st->core.calib[idx].offset;
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
case IIO_CHAN_INFO_CALIBSCALE:
|
||||
/*
|
||||
|
@ -101,10 +101,9 @@ static int cros_ec_light_prox_read(struct iio_dev *indio_dev,
|
|||
st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_RANGE;
|
||||
st->core.param.sensor_range.data = EC_MOTION_SENSE_NO_VALUE;
|
||||
|
||||
if (cros_ec_motion_send_host_cmd(&st->core, 0)) {
|
||||
ret = -EIO;
|
||||
ret = cros_ec_motion_send_host_cmd(&st->core, 0);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
|
||||
val64 = st->core.resp->sensor_range.ret;
|
||||
*val = val64 >> 16;
|
||||
|
@ -127,28 +126,27 @@ static int cros_ec_light_prox_write(struct iio_dev *indio_dev,
|
|||
int val, int val2, long mask)
|
||||
{
|
||||
struct cros_ec_light_prox_state *st = iio_priv(indio_dev);
|
||||
int ret = 0;
|
||||
int ret;
|
||||
int idx = chan->scan_index;
|
||||
|
||||
mutex_lock(&st->core.cmd_lock);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
st->core.calib[idx] = val;
|
||||
st->core.calib[idx].offset = val;
|
||||
/* Send to EC for each axis, even if not complete */
|
||||
st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_OFFSET;
|
||||
st->core.param.sensor_offset.flags = MOTION_SENSE_SET_OFFSET;
|
||||
st->core.param.sensor_offset.offset[0] = st->core.calib[0];
|
||||
st->core.param.sensor_offset.offset[0] =
|
||||
st->core.calib[0].offset;
|
||||
st->core.param.sensor_offset.temp =
|
||||
EC_MOTION_SENSE_INVALID_CALIB_TEMP;
|
||||
if (cros_ec_motion_send_host_cmd(&st->core, 0))
|
||||
ret = -EIO;
|
||||
ret = cros_ec_motion_send_host_cmd(&st->core, 0);
|
||||
break;
|
||||
case IIO_CHAN_INFO_CALIBSCALE:
|
||||
st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_RANGE;
|
||||
st->core.param.sensor_range.data = (val << 16) | (val2 / 100);
|
||||
if (cros_ec_motion_send_host_cmd(&st->core, 0))
|
||||
ret = -EIO;
|
||||
ret = cros_ec_motion_send_host_cmd(&st->core, 0);
|
||||
break;
|
||||
default:
|
||||
ret = cros_ec_sensors_core_write(&st->core, chan, val, val2,
|
||||
|
@ -164,6 +162,7 @@ static int cros_ec_light_prox_write(struct iio_dev *indio_dev,
|
|||
static const struct iio_info cros_ec_light_prox_info = {
|
||||
.read_raw = &cros_ec_light_prox_read,
|
||||
.write_raw = &cros_ec_light_prox_write,
|
||||
.read_avail = &cros_ec_sensors_core_read_avail,
|
||||
};
|
||||
|
||||
static int cros_ec_light_prox_probe(struct platform_device *pdev)
|
||||
|
@ -198,6 +197,8 @@ static int cros_ec_light_prox_probe(struct platform_device *pdev)
|
|||
channel->info_mask_shared_by_all =
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
|
||||
BIT(IIO_CHAN_INFO_FREQUENCY);
|
||||
channel->info_mask_shared_by_all_available =
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ);
|
||||
channel->scan_type.realbits = CROS_EC_SENSOR_BITS;
|
||||
channel->scan_type.storagebits = CROS_EC_SENSOR_BITS;
|
||||
channel->scan_type.shift = 0;
|
||||
|
@ -205,8 +206,6 @@ static int cros_ec_light_prox_probe(struct platform_device *pdev)
|
|||
channel->ext_info = cros_ec_sensors_ext_info;
|
||||
channel->scan_type.sign = 'u';
|
||||
|
||||
state->core.calib[0] = 0;
|
||||
|
||||
/* Sensor specific */
|
||||
switch (state->core.type) {
|
||||
case MOTIONSENSE_TYPE_LIGHT:
|
||||
|
|
|
@ -0,0 +1,312 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Support for ON Semiconductor NOA1305 ambient light sensor
|
||||
*
|
||||
* Copyright (C) 2016 Emcraft Systems
|
||||
* Copyright (C) 2019 Collabora Ltd.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#define NOA1305_REG_POWER_CONTROL 0x0
|
||||
#define NOA1305_POWER_CONTROL_DOWN 0x00
|
||||
#define NOA1305_POWER_CONTROL_ON 0x08
|
||||
#define NOA1305_REG_RESET 0x1
|
||||
#define NOA1305_RESET_RESET 0x10
|
||||
#define NOA1305_REG_INTEGRATION_TIME 0x2
|
||||
#define NOA1305_INTEGR_TIME_800MS 0x00
|
||||
#define NOA1305_INTEGR_TIME_400MS 0x01
|
||||
#define NOA1305_INTEGR_TIME_200MS 0x02
|
||||
#define NOA1305_INTEGR_TIME_100MS 0x03
|
||||
#define NOA1305_INTEGR_TIME_50MS 0x04
|
||||
#define NOA1305_INTEGR_TIME_25MS 0x05
|
||||
#define NOA1305_INTEGR_TIME_12_5MS 0x06
|
||||
#define NOA1305_INTEGR_TIME_6_25MS 0x07
|
||||
#define NOA1305_REG_INT_SELECT 0x3
|
||||
#define NOA1305_INT_SEL_ACTIVE_HIGH 0x01
|
||||
#define NOA1305_INT_SEL_ACTIVE_LOW 0x02
|
||||
#define NOA1305_INT_SEL_INACTIVE 0x03
|
||||
#define NOA1305_REG_INT_THRESH_LSB 0x4
|
||||
#define NOA1305_REG_INT_THRESH_MSB 0x5
|
||||
#define NOA1305_REG_ALS_DATA_LSB 0x6
|
||||
#define NOA1305_REG_ALS_DATA_MSB 0x7
|
||||
#define NOA1305_REG_DEVICE_ID_LSB 0x8
|
||||
#define NOA1305_REG_DEVICE_ID_MSB 0x9
|
||||
|
||||
#define NOA1305_DEVICE_ID 0x0519
|
||||
#define NOA1305_DRIVER_NAME "noa1305"
|
||||
|
||||
struct noa1305_priv {
|
||||
struct i2c_client *client;
|
||||
struct regmap *regmap;
|
||||
struct regulator *vin_reg;
|
||||
};
|
||||
|
||||
static int noa1305_measure(struct noa1305_priv *priv)
|
||||
{
|
||||
__le16 data;
|
||||
int ret;
|
||||
|
||||
ret = regmap_bulk_read(priv->regmap, NOA1305_REG_ALS_DATA_LSB, &data,
|
||||
2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return le16_to_cpu(data);
|
||||
}
|
||||
|
||||
static int noa1305_scale(struct noa1305_priv *priv, int *val, int *val2)
|
||||
{
|
||||
int data;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(priv->regmap, NOA1305_REG_INTEGRATION_TIME, &data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Lux = count / (<Integration Constant> * <Integration Time>)
|
||||
*
|
||||
* Integration Constant = 7.7
|
||||
* Integration Time in Seconds
|
||||
*/
|
||||
switch (data) {
|
||||
case NOA1305_INTEGR_TIME_800MS:
|
||||
*val = 100;
|
||||
*val2 = 77 * 8;
|
||||
break;
|
||||
case NOA1305_INTEGR_TIME_400MS:
|
||||
*val = 100;
|
||||
*val2 = 77 * 4;
|
||||
case NOA1305_INTEGR_TIME_200MS:
|
||||
*val = 100;
|
||||
*val2 = 77 * 2;
|
||||
break;
|
||||
case NOA1305_INTEGR_TIME_100MS:
|
||||
*val = 100;
|
||||
*val2 = 77;
|
||||
break;
|
||||
case NOA1305_INTEGR_TIME_50MS:
|
||||
*val = 1000;
|
||||
*val2 = 77 * 5;
|
||||
break;
|
||||
case NOA1305_INTEGR_TIME_25MS:
|
||||
*val = 10000;
|
||||
*val2 = 77 * 25;
|
||||
break;
|
||||
case NOA1305_INTEGR_TIME_12_5MS:
|
||||
*val = 100000;
|
||||
*val2 = 77 * 125;
|
||||
break;
|
||||
case NOA1305_INTEGR_TIME_6_25MS:
|
||||
*val = 1000000;
|
||||
*val2 = 77 * 625;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return IIO_VAL_FRACTIONAL;
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec noa1305_channels[] = {
|
||||
{
|
||||
.type = IIO_LIGHT,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
|
||||
}
|
||||
};
|
||||
|
||||
static int noa1305_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
int ret = -EINVAL;
|
||||
struct noa1305_priv *priv = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
switch (chan->type) {
|
||||
case IIO_LIGHT:
|
||||
ret = noa1305_measure(priv);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = ret;
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
switch (chan->type) {
|
||||
case IIO_LIGHT:
|
||||
return noa1305_scale(priv, val, val2);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct iio_info noa1305_info = {
|
||||
.read_raw = noa1305_read_raw,
|
||||
};
|
||||
|
||||
static bool noa1305_writable_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case NOA1305_REG_POWER_CONTROL:
|
||||
case NOA1305_REG_RESET:
|
||||
case NOA1305_REG_INTEGRATION_TIME:
|
||||
case NOA1305_REG_INT_SELECT:
|
||||
case NOA1305_REG_INT_THRESH_LSB:
|
||||
case NOA1305_REG_INT_THRESH_MSB:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct regmap_config noa1305_regmap_config = {
|
||||
.name = NOA1305_DRIVER_NAME,
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.max_register = NOA1305_REG_DEVICE_ID_MSB,
|
||||
.writeable_reg = noa1305_writable_reg,
|
||||
};
|
||||
|
||||
static void noa1305_reg_remove(void *data)
|
||||
{
|
||||
struct noa1305_priv *priv = data;
|
||||
|
||||
regulator_disable(priv->vin_reg);
|
||||
}
|
||||
|
||||
static int noa1305_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct noa1305_priv *priv;
|
||||
struct iio_dev *indio_dev;
|
||||
struct regmap *regmap;
|
||||
__le16 data;
|
||||
unsigned int dev_id;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*priv));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
regmap = devm_regmap_init_i2c(client, &noa1305_regmap_config);
|
||||
if (IS_ERR(regmap)) {
|
||||
dev_err(&client->dev, "Regmap initialization failed.\n");
|
||||
return PTR_ERR(regmap);
|
||||
}
|
||||
|
||||
priv = iio_priv(indio_dev);
|
||||
|
||||
priv->vin_reg = devm_regulator_get(&client->dev, "vin");
|
||||
if (IS_ERR(priv->vin_reg)) {
|
||||
dev_err(&client->dev, "get regulator vin failed\n");
|
||||
return PTR_ERR(priv->vin_reg);
|
||||
}
|
||||
|
||||
ret = regulator_enable(priv->vin_reg);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "enable regulator vin failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = devm_add_action_or_reset(&client->dev, noa1305_reg_remove, priv);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "addition of devm action failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
priv->client = client;
|
||||
priv->regmap = regmap;
|
||||
|
||||
ret = regmap_bulk_read(regmap, NOA1305_REG_DEVICE_ID_LSB, &data, 2);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "ID reading failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_id = le16_to_cpu(data);
|
||||
if (dev_id != NOA1305_DEVICE_ID) {
|
||||
dev_err(&client->dev, "Unknown device ID: 0x%x\n", dev_id);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ret = regmap_write(regmap, NOA1305_REG_POWER_CONTROL,
|
||||
NOA1305_POWER_CONTROL_ON);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "Enabling power control failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regmap_write(regmap, NOA1305_REG_RESET, NOA1305_RESET_RESET);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "Device reset failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regmap_write(regmap, NOA1305_REG_INTEGRATION_TIME,
|
||||
NOA1305_INTEGR_TIME_800MS);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "Setting integration time failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->info = &noa1305_info;
|
||||
indio_dev->channels = noa1305_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(noa1305_channels);
|
||||
indio_dev->name = NOA1305_DRIVER_NAME;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
ret = devm_iio_device_register(&client->dev, indio_dev);
|
||||
if (ret)
|
||||
dev_err(&client->dev, "registering device failed\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct of_device_id noa1305_of_match[] = {
|
||||
{ .compatible = "onnn,noa1305" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, noa1305_of_match);
|
||||
|
||||
static const struct i2c_device_id noa1305_ids[] = {
|
||||
{ "noa1305", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, noa1305_ids);
|
||||
|
||||
static struct i2c_driver noa1305_driver = {
|
||||
.driver = {
|
||||
.name = NOA1305_DRIVER_NAME,
|
||||
.of_match_table = noa1305_of_match,
|
||||
},
|
||||
.probe = noa1305_probe,
|
||||
.id_table = noa1305_ids,
|
||||
};
|
||||
|
||||
module_i2c_driver(noa1305_driver);
|
||||
|
||||
MODULE_AUTHOR("Sergei Miroshnichenko <sergeimir@emcraft.com>");
|
||||
MODULE_AUTHOR("Martyn Welch <martyn.welch@collabora.com");
|
||||
MODULE_DESCRIPTION("ON Semiconductor NOA1305 ambient light sensor");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -1261,7 +1261,7 @@ static int si1145_probe_trigger(struct iio_dev *indio_dev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
ret = iio_trigger_register(trig);
|
||||
ret = devm_iio_trigger_register(&client->dev, trig);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -1271,16 +1271,6 @@ static int si1145_probe_trigger(struct iio_dev *indio_dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void si1145_remove_trigger(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct si1145_data *data = iio_priv(indio_dev);
|
||||
|
||||
if (data->trig) {
|
||||
iio_trigger_unregister(data->trig);
|
||||
data->trig = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int si1145_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
|
@ -1332,7 +1322,8 @@ static int si1145_probe(struct i2c_client *client,
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, NULL,
|
||||
ret = devm_iio_triggered_buffer_setup(&client->dev,
|
||||
indio_dev, NULL,
|
||||
si1145_trigger_handler, &si1145_buffer_setup_ops);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
@ -1340,23 +1331,12 @@ static int si1145_probe(struct i2c_client *client,
|
|||
if (client->irq) {
|
||||
ret = si1145_probe_trigger(indio_dev);
|
||||
if (ret < 0)
|
||||
goto error_free_buffer;
|
||||
return ret;
|
||||
} else {
|
||||
dev_info(&client->dev, "no irq, using polling\n");
|
||||
}
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret < 0)
|
||||
goto error_free_trigger;
|
||||
|
||||
return 0;
|
||||
|
||||
error_free_trigger:
|
||||
si1145_remove_trigger(indio_dev);
|
||||
error_free_buffer:
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
|
||||
return ret;
|
||||
return devm_iio_device_register(&client->dev, indio_dev);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id si1145_ids[] = {
|
||||
|
@ -1371,23 +1351,11 @@ static const struct i2c_device_id si1145_ids[] = {
|
|||
};
|
||||
MODULE_DEVICE_TABLE(i2c, si1145_ids);
|
||||
|
||||
static int si1145_remove(struct i2c_client *client)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
si1145_remove_trigger(indio_dev);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct i2c_driver si1145_driver = {
|
||||
.driver = {
|
||||
.name = "si1145",
|
||||
},
|
||||
.probe = si1145_probe,
|
||||
.remove = si1145_remove,
|
||||
.id_table = si1145_ids,
|
||||
};
|
||||
|
||||
|
|
|
@ -679,9 +679,18 @@ static const struct acpi_device_id stk3310_acpi_id[] = {
|
|||
|
||||
MODULE_DEVICE_TABLE(acpi, stk3310_acpi_id);
|
||||
|
||||
static const struct of_device_id stk3310_of_match[] = {
|
||||
{ .compatible = "sensortek,stk3310", },
|
||||
{ .compatible = "sensortek,stk3311", },
|
||||
{ .compatible = "sensortek,stk3335", },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, stk3310_of_match);
|
||||
|
||||
static struct i2c_driver stk3310_driver = {
|
||||
.driver = {
|
||||
.name = "stk3310",
|
||||
.of_match_table = stk3310_of_match,
|
||||
.pm = STK3310_PM_OPS,
|
||||
.acpi_match_table = ACPI_PTR(stk3310_acpi_id),
|
||||
},
|
||||
|
|
|
@ -134,6 +134,12 @@ enum {
|
|||
TSL2772_CHIP_SUSPENDED = 2
|
||||
};
|
||||
|
||||
enum {
|
||||
TSL2772_SUPPLY_VDD = 0,
|
||||
TSL2772_SUPPLY_VDDIO = 1,
|
||||
TSL2772_NUM_SUPPLIES = 2
|
||||
};
|
||||
|
||||
/* Per-device data */
|
||||
struct tsl2772_als_info {
|
||||
u16 als_ch0;
|
||||
|
@ -161,8 +167,7 @@ struct tsl2772_chip {
|
|||
struct mutex prox_mutex;
|
||||
struct mutex als_mutex;
|
||||
struct i2c_client *client;
|
||||
struct regulator *vdd_supply;
|
||||
struct regulator *vddio_supply;
|
||||
struct regulator_bulk_data supplies[TSL2772_NUM_SUPPLIES];
|
||||
u16 prox_data;
|
||||
struct tsl2772_als_info als_cur_info;
|
||||
struct tsl2772_settings settings;
|
||||
|
@ -697,46 +702,7 @@ static void tsl2772_disable_regulators_action(void *_data)
|
|||
{
|
||||
struct tsl2772_chip *chip = _data;
|
||||
|
||||
regulator_disable(chip->vdd_supply);
|
||||
regulator_disable(chip->vddio_supply);
|
||||
}
|
||||
|
||||
static int tsl2772_enable_regulator(struct tsl2772_chip *chip,
|
||||
struct regulator *regulator)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = regulator_enable(regulator);
|
||||
if (ret < 0) {
|
||||
dev_err(&chip->client->dev, "Failed to enable regulator: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct regulator *tsl2772_get_regulator(struct tsl2772_chip *chip,
|
||||
char *name)
|
||||
{
|
||||
struct regulator *regulator;
|
||||
int ret;
|
||||
|
||||
regulator = devm_regulator_get(&chip->client->dev, name);
|
||||
if (IS_ERR(regulator)) {
|
||||
if (PTR_ERR(regulator) != -EPROBE_DEFER)
|
||||
dev_err(&chip->client->dev,
|
||||
"Failed to get %s regulator %d\n",
|
||||
name, (int)PTR_ERR(regulator));
|
||||
|
||||
return regulator;
|
||||
}
|
||||
|
||||
ret = tsl2772_enable_regulator(chip, regulator);
|
||||
if (ret < 0)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
return regulator;
|
||||
regulator_bulk_disable(ARRAY_SIZE(chip->supplies), chip->supplies);
|
||||
}
|
||||
|
||||
static int tsl2772_chip_on(struct iio_dev *indio_dev)
|
||||
|
@ -860,6 +826,13 @@ static int tsl2772_chip_off(struct iio_dev *indio_dev)
|
|||
return tsl2772_write_control_reg(chip, 0x00);
|
||||
}
|
||||
|
||||
static void tsl2772_chip_off_action(void *data)
|
||||
{
|
||||
struct iio_dev *indio_dev = data;
|
||||
|
||||
tsl2772_chip_off(indio_dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* tsl2772_invoke_change - power cycle the device to implement the user
|
||||
* parameters
|
||||
|
@ -1797,20 +1770,32 @@ static int tsl2772_probe(struct i2c_client *clientp,
|
|||
chip->client = clientp;
|
||||
i2c_set_clientdata(clientp, indio_dev);
|
||||
|
||||
chip->vddio_supply = tsl2772_get_regulator(chip, "vddio");
|
||||
if (IS_ERR(chip->vddio_supply))
|
||||
return PTR_ERR(chip->vddio_supply);
|
||||
chip->supplies[TSL2772_SUPPLY_VDD].supply = "vdd";
|
||||
chip->supplies[TSL2772_SUPPLY_VDDIO].supply = "vddio";
|
||||
|
||||
chip->vdd_supply = tsl2772_get_regulator(chip, "vdd");
|
||||
if (IS_ERR(chip->vdd_supply)) {
|
||||
regulator_disable(chip->vddio_supply);
|
||||
return PTR_ERR(chip->vdd_supply);
|
||||
ret = devm_regulator_bulk_get(&clientp->dev,
|
||||
ARRAY_SIZE(chip->supplies),
|
||||
chip->supplies);
|
||||
if (ret < 0) {
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(&clientp->dev,
|
||||
"Failed to get regulators: %d\n",
|
||||
ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = devm_add_action(&clientp->dev, tsl2772_disable_regulators_action,
|
||||
chip);
|
||||
ret = regulator_bulk_enable(ARRAY_SIZE(chip->supplies), chip->supplies);
|
||||
if (ret < 0) {
|
||||
dev_err(&clientp->dev, "Failed to enable regulators: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = devm_add_action_or_reset(&clientp->dev,
|
||||
tsl2772_disable_regulators_action,
|
||||
chip);
|
||||
if (ret < 0) {
|
||||
tsl2772_disable_regulators_action(chip);
|
||||
dev_err(&clientp->dev, "Failed to setup regulator cleanup action %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
|
@ -1877,15 +1862,13 @@ static int tsl2772_probe(struct i2c_client *clientp,
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret) {
|
||||
tsl2772_chip_off(indio_dev);
|
||||
dev_err(&clientp->dev,
|
||||
"%s: iio registration failed\n", __func__);
|
||||
ret = devm_add_action_or_reset(&clientp->dev,
|
||||
tsl2772_chip_off_action,
|
||||
indio_dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return devm_iio_device_register(&clientp->dev, indio_dev);
|
||||
}
|
||||
|
||||
static int tsl2772_suspend(struct device *dev)
|
||||
|
@ -1895,8 +1878,7 @@ static int tsl2772_suspend(struct device *dev)
|
|||
int ret;
|
||||
|
||||
ret = tsl2772_chip_off(indio_dev);
|
||||
regulator_disable(chip->vdd_supply);
|
||||
regulator_disable(chip->vddio_supply);
|
||||
regulator_bulk_disable(ARRAY_SIZE(chip->supplies), chip->supplies);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -1907,32 +1889,15 @@ static int tsl2772_resume(struct device *dev)
|
|||
struct tsl2772_chip *chip = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = tsl2772_enable_regulator(chip, chip->vddio_supply);
|
||||
ret = regulator_bulk_enable(ARRAY_SIZE(chip->supplies), chip->supplies);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = tsl2772_enable_regulator(chip, chip->vdd_supply);
|
||||
if (ret < 0) {
|
||||
regulator_disable(chip->vddio_supply);
|
||||
return ret;
|
||||
}
|
||||
|
||||
usleep_range(TSL2772_BOOT_MIN_SLEEP_TIME, TSL2772_BOOT_MAX_SLEEP_TIME);
|
||||
|
||||
return tsl2772_chip_on(indio_dev);
|
||||
}
|
||||
|
||||
static int tsl2772_remove(struct i2c_client *client)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
|
||||
tsl2772_chip_off(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id tsl2772_idtable[] = {
|
||||
{ "tsl2571", tsl2571 },
|
||||
{ "tsl2671", tsl2671 },
|
||||
|
@ -1979,7 +1944,6 @@ static struct i2c_driver tsl2772_driver = {
|
|||
},
|
||||
.id_table = tsl2772_idtable,
|
||||
.probe = tsl2772_probe,
|
||||
.remove = tsl2772_remove,
|
||||
};
|
||||
|
||||
module_i2c_driver(tsl2772_driver);
|
||||
|
|
|
@ -158,10 +158,10 @@ static int veml6070_probe(struct i2c_client *client,
|
|||
indio_dev->name = VEML6070_DRV_NAME;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
data->client2 = i2c_new_dummy(client->adapter, VEML6070_ADDR_DATA_LSB);
|
||||
if (!data->client2) {
|
||||
data->client2 = i2c_new_dummy_device(client->adapter, VEML6070_ADDR_DATA_LSB);
|
||||
if (IS_ERR(data->client2)) {
|
||||
dev_err(&client->dev, "i2c device for second chip address failed\n");
|
||||
return -ENODEV;
|
||||
return PTR_ERR(data->client2);
|
||||
}
|
||||
|
||||
data->config = VEML6070_IT_10 | VEML6070_COMMAND_RSRVD |
|
||||
|
|
|
@ -53,7 +53,7 @@
|
|||
#define MMC35240_CTRL1_BW_SHIFT 0
|
||||
|
||||
#define MMC35240_WAIT_CHARGE_PUMP 50000 /* us */
|
||||
#define MMC53240_WAIT_SET_RESET 1000 /* us */
|
||||
#define MMC35240_WAIT_SET_RESET 1000 /* us */
|
||||
|
||||
/*
|
||||
* Memsic OTP process code piece is put here for reference:
|
||||
|
@ -225,7 +225,7 @@ static int mmc35240_init(struct mmc35240_data *data)
|
|||
ret = mmc35240_hw_set(data, true);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
usleep_range(MMC53240_WAIT_SET_RESET, MMC53240_WAIT_SET_RESET + 1);
|
||||
usleep_range(MMC35240_WAIT_SET_RESET, MMC35240_WAIT_SET_RESET + 1);
|
||||
|
||||
ret = mmc35240_hw_set(data, false);
|
||||
if (ret < 0)
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#define LIS2MDL_MAGN_DEV_NAME "lis2mdl"
|
||||
#define LSM9DS1_MAGN_DEV_NAME "lsm9ds1_magn"
|
||||
|
||||
const struct st_sensor_settings *st_magn_get_settings(const char *name);
|
||||
int st_magn_common_probe(struct iio_dev *indio_dev);
|
||||
void st_magn_common_remove(struct iio_dev *indio_dev);
|
||||
|
||||
|
|
|
@ -32,39 +32,32 @@ int st_magn_trig_set_state(struct iio_trigger *trig, bool state)
|
|||
static int st_magn_buffer_postenable(struct iio_dev *indio_dev)
|
||||
{
|
||||
int err;
|
||||
struct st_sensor_data *mdata = iio_priv(indio_dev);
|
||||
|
||||
mdata->buffer_data = kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
|
||||
if (mdata->buffer_data == NULL) {
|
||||
err = -ENOMEM;
|
||||
goto allocate_memory_error;
|
||||
}
|
||||
|
||||
err = iio_triggered_buffer_postenable(indio_dev);
|
||||
if (err < 0)
|
||||
goto st_magn_buffer_postenable_error;
|
||||
return err;
|
||||
|
||||
return st_sensors_set_enable(indio_dev, true);
|
||||
err = st_sensors_set_enable(indio_dev, true);
|
||||
if (err < 0)
|
||||
goto st_magn_buffer_predisable;
|
||||
|
||||
st_magn_buffer_postenable_error:
|
||||
kfree(mdata->buffer_data);
|
||||
allocate_memory_error:
|
||||
return 0;
|
||||
|
||||
st_magn_buffer_predisable:
|
||||
iio_triggered_buffer_predisable(indio_dev);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int st_magn_buffer_predisable(struct iio_dev *indio_dev)
|
||||
{
|
||||
int err;
|
||||
struct st_sensor_data *mdata = iio_priv(indio_dev);
|
||||
int err, err2;
|
||||
|
||||
err = st_sensors_set_enable(indio_dev, false);
|
||||
if (err < 0)
|
||||
goto st_magn_buffer_predisable_error;
|
||||
|
||||
err = iio_triggered_buffer_predisable(indio_dev);
|
||||
err2 = iio_triggered_buffer_predisable(indio_dev);
|
||||
if (!err)
|
||||
err = err2;
|
||||
|
||||
st_magn_buffer_predisable_error:
|
||||
kfree(mdata->buffer_data);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/gpio.h>
|
||||
|
@ -470,28 +469,41 @@ static const struct iio_trigger_ops st_magn_trigger_ops = {
|
|||
#define ST_MAGN_TRIGGER_OPS NULL
|
||||
#endif
|
||||
|
||||
/*
|
||||
* st_magn_get_settings() - get sensor settings from device name
|
||||
* @name: device name buffer reference.
|
||||
*
|
||||
* Return: valid reference on success, NULL otherwise.
|
||||
*/
|
||||
const struct st_sensor_settings *st_magn_get_settings(const char *name)
|
||||
{
|
||||
int index = st_sensors_get_settings_index(name,
|
||||
st_magn_sensors_settings,
|
||||
ARRAY_SIZE(st_magn_sensors_settings));
|
||||
if (index < 0)
|
||||
return NULL;
|
||||
|
||||
return &st_magn_sensors_settings[index];
|
||||
}
|
||||
EXPORT_SYMBOL(st_magn_get_settings);
|
||||
|
||||
int st_magn_common_probe(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct st_sensor_data *mdata = iio_priv(indio_dev);
|
||||
int irq = mdata->get_irq_data_ready(indio_dev);
|
||||
int err;
|
||||
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &magn_info;
|
||||
mutex_init(&mdata->tb.buf_lock);
|
||||
|
||||
err = st_sensors_power_enable(indio_dev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = st_sensors_check_device_support(indio_dev,
|
||||
ARRAY_SIZE(st_magn_sensors_settings),
|
||||
st_magn_sensors_settings);
|
||||
err = st_sensors_verify_id(indio_dev);
|
||||
if (err < 0)
|
||||
goto st_magn_power_off;
|
||||
|
||||
mdata->num_data_channels = ST_MAGN_NUMBER_DATA_CHANNELS;
|
||||
mdata->multiread_bit = mdata->sensor_settings->multi_read_bit;
|
||||
indio_dev->channels = mdata->sensor_settings->ch;
|
||||
indio_dev->num_channels = ST_SENSORS_NUMBER_ALL_CHANNELS;
|
||||
|
||||
|
@ -507,7 +519,7 @@ int st_magn_common_probe(struct iio_dev *indio_dev)
|
|||
if (err < 0)
|
||||
goto st_magn_power_off;
|
||||
|
||||
if (irq > 0) {
|
||||
if (mdata->irq > 0) {
|
||||
err = st_sensors_allocate_trigger(indio_dev,
|
||||
ST_MAGN_TRIGGER_OPS);
|
||||
if (err < 0)
|
||||
|
@ -524,7 +536,7 @@ int st_magn_common_probe(struct iio_dev *indio_dev)
|
|||
return 0;
|
||||
|
||||
st_magn_device_register_error:
|
||||
if (irq > 0)
|
||||
if (mdata->irq > 0)
|
||||
st_sensors_deallocate_trigger(indio_dev);
|
||||
st_magn_probe_trigger_error:
|
||||
st_magn_deallocate_ring(indio_dev);
|
||||
|
@ -542,7 +554,7 @@ void st_magn_common_remove(struct iio_dev *indio_dev)
|
|||
st_sensors_power_disable(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
if (mdata->get_irq_data_ready(indio_dev) > 0)
|
||||
if (mdata->irq > 0)
|
||||
st_sensors_deallocate_trigger(indio_dev);
|
||||
|
||||
st_magn_deallocate_ring(indio_dev);
|
||||
|
|
|
@ -55,21 +55,33 @@ MODULE_DEVICE_TABLE(of, st_magn_of_match);
|
|||
#endif
|
||||
|
||||
static int st_magn_i2c_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
const struct st_sensor_settings *settings;
|
||||
struct st_sensor_data *mdata;
|
||||
struct iio_dev *indio_dev;
|
||||
int err;
|
||||
|
||||
st_sensors_of_name_probe(&client->dev, st_magn_of_match,
|
||||
client->name, sizeof(client->name));
|
||||
|
||||
settings = st_magn_get_settings(client->name);
|
||||
if (!settings) {
|
||||
dev_err(&client->dev, "device name %s not recognized.\n",
|
||||
client->name);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*mdata));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
mdata = iio_priv(indio_dev);
|
||||
st_sensors_of_name_probe(&client->dev, st_magn_of_match,
|
||||
client->name, sizeof(client->name));
|
||||
mdata->sensor_settings = (struct st_sensor_settings *)settings;
|
||||
|
||||
st_sensors_i2c_configure(indio_dev, client, mdata);
|
||||
err = st_sensors_i2c_configure(indio_dev, client);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = st_magn_common_probe(indio_dev);
|
||||
if (err < 0)
|
||||
|
|
|
@ -51,19 +51,31 @@ MODULE_DEVICE_TABLE(of, st_magn_of_match);
|
|||
|
||||
static int st_magn_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
const struct st_sensor_settings *settings;
|
||||
struct st_sensor_data *mdata;
|
||||
struct iio_dev *indio_dev;
|
||||
int err;
|
||||
|
||||
st_sensors_of_name_probe(&spi->dev, st_magn_of_match,
|
||||
spi->modalias, sizeof(spi->modalias));
|
||||
|
||||
settings = st_magn_get_settings(spi->modalias);
|
||||
if (!settings) {
|
||||
dev_err(&spi->dev, "device name %s not recognized.\n",
|
||||
spi->modalias);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*mdata));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
mdata = iio_priv(indio_dev);
|
||||
mdata->sensor_settings = (struct st_sensor_settings *)settings;
|
||||
|
||||
st_sensors_of_name_probe(&spi->dev, st_magn_of_match,
|
||||
spi->modalias, sizeof(spi->modalias));
|
||||
st_sensors_spi_configure(indio_dev, spi, mdata);
|
||||
err = st_sensors_spi_configure(indio_dev, spi);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = st_magn_common_probe(indio_dev);
|
||||
if (err < 0)
|
||||
|
|
|
@ -26,6 +26,17 @@ config DS1803
|
|||
To compile this driver as a module, choose M here: the
|
||||
module will be called ds1803.
|
||||
|
||||
config MAX5432
|
||||
tristate "Maxim MAX5432-MAX5435 Digital Potentiometer driver"
|
||||
depends on I2C
|
||||
help
|
||||
Say yes here to build support for the Maxim
|
||||
MAX5432, MAX5433, MAX5434 and MAX5435 digital
|
||||
potentiometer chips.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called max5432.
|
||||
|
||||
config MAX5481
|
||||
tristate "Maxim MAX5481-MAX5484 Digital Potentiometer driver"
|
||||
depends on SPI
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
# When adding new entries keep the list in alphabetical order
|
||||
obj-$(CONFIG_AD5272) += ad5272.o
|
||||
obj-$(CONFIG_DS1803) += ds1803.o
|
||||
obj-$(CONFIG_MAX5432) += max5432.o
|
||||
obj-$(CONFIG_MAX5481) += max5481.o
|
||||
obj-$(CONFIG_MAX5487) += max5487.o
|
||||
obj-$(CONFIG_MCP4018) += mcp4018.o
|
||||
|
|
|
@ -0,0 +1,135 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Maxim Integrated MAX5432-MAX5435 digital potentiometer driver
|
||||
* Copyright (C) 2019 Martin Kaiser <martin@kaiser.cx>
|
||||
*
|
||||
* Datasheet:
|
||||
* https://datasheets.maximintegrated.com/en/ds/MAX5432-MAX5435.pdf
|
||||
*/
|
||||
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/limits.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
|
||||
/* All chip variants have 32 wiper positions. */
|
||||
#define MAX5432_MAX_POS 31
|
||||
|
||||
#define MAX5432_OHM_50K (50 * 1000)
|
||||
#define MAX5432_OHM_100K (100 * 1000)
|
||||
|
||||
/* Update the volatile (currently active) setting. */
|
||||
#define MAX5432_CMD_VREG 0x11
|
||||
|
||||
struct max5432_data {
|
||||
struct i2c_client *client;
|
||||
unsigned long ohm;
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec max5432_channels[] = {
|
||||
{
|
||||
.type = IIO_RESISTANCE,
|
||||
.indexed = 1,
|
||||
.output = 1,
|
||||
.channel = 0,
|
||||
.address = MAX5432_CMD_VREG,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
|
||||
}
|
||||
};
|
||||
|
||||
static int max5432_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct max5432_data *data = iio_priv(indio_dev);
|
||||
|
||||
if (mask != IIO_CHAN_INFO_SCALE)
|
||||
return -EINVAL;
|
||||
|
||||
if (unlikely(data->ohm > INT_MAX))
|
||||
return -ERANGE;
|
||||
|
||||
*val = data->ohm;
|
||||
*val2 = MAX5432_MAX_POS;
|
||||
|
||||
return IIO_VAL_FRACTIONAL;
|
||||
}
|
||||
|
||||
static int max5432_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
struct max5432_data *data = iio_priv(indio_dev);
|
||||
u8 data_byte;
|
||||
|
||||
if (mask != IIO_CHAN_INFO_RAW)
|
||||
return -EINVAL;
|
||||
|
||||
if (val < 0 || val > MAX5432_MAX_POS)
|
||||
return -EINVAL;
|
||||
|
||||
if (val2 != 0)
|
||||
return -EINVAL;
|
||||
|
||||
/* Wiper position is in bits D7-D3. (D2-D0 are don't care bits.) */
|
||||
data_byte = val << 3;
|
||||
return i2c_smbus_write_byte_data(data->client, chan->address,
|
||||
data_byte);
|
||||
}
|
||||
|
||||
static const struct iio_info max5432_info = {
|
||||
.read_raw = max5432_read_raw,
|
||||
.write_raw = max5432_write_raw,
|
||||
};
|
||||
|
||||
static int max5432_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
struct iio_dev *indio_dev;
|
||||
struct max5432_data *data;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(dev, sizeof(struct max5432_data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
|
||||
data = iio_priv(indio_dev);
|
||||
data->client = client;
|
||||
data->ohm = (unsigned long)of_device_get_match_data(dev);
|
||||
|
||||
indio_dev->dev.parent = dev;
|
||||
indio_dev->info = &max5432_info;
|
||||
indio_dev->channels = max5432_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(max5432_channels);
|
||||
indio_dev->name = client->name;
|
||||
|
||||
return devm_iio_device_register(dev, indio_dev);
|
||||
}
|
||||
|
||||
static const struct of_device_id max5432_dt_ids[] = {
|
||||
{ .compatible = "maxim,max5432", .data = (void *)MAX5432_OHM_50K },
|
||||
{ .compatible = "maxim,max5433", .data = (void *)MAX5432_OHM_100K },
|
||||
{ .compatible = "maxim,max5434", .data = (void *)MAX5432_OHM_50K },
|
||||
{ .compatible = "maxim,max5435", .data = (void *)MAX5432_OHM_100K },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, max5432_dt_ids);
|
||||
|
||||
static struct i2c_driver max5432_driver = {
|
||||
.driver = {
|
||||
.name = "max5432",
|
||||
.of_match_table = of_match_ptr(max5432_dt_ids),
|
||||
},
|
||||
.probe = max5432_probe,
|
||||
};
|
||||
|
||||
module_i2c_driver(max5432_driver);
|
||||
|
||||
MODULE_AUTHOR("Martin Kaiser <martin@kaiser.cx>");
|
||||
MODULE_DESCRIPTION("max5432-max5435 digital potentiometers");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -39,26 +39,29 @@ static int cros_ec_baro_read(struct iio_dev *indio_dev,
|
|||
{
|
||||
struct cros_ec_baro_state *st = iio_priv(indio_dev);
|
||||
u16 data = 0;
|
||||
int ret = IIO_VAL_INT;
|
||||
int ret;
|
||||
int idx = chan->scan_index;
|
||||
|
||||
mutex_lock(&st->core.cmd_lock);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (cros_ec_sensors_read_cmd(indio_dev, 1 << idx,
|
||||
(s16 *)&data) < 0)
|
||||
ret = -EIO;
|
||||
ret = cros_ec_sensors_read_cmd(indio_dev, 1 << idx,
|
||||
(s16 *)&data);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
*val = data;
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_RANGE;
|
||||
st->core.param.sensor_range.data = EC_MOTION_SENSE_NO_VALUE;
|
||||
|
||||
if (cros_ec_motion_send_host_cmd(&st->core, 0)) {
|
||||
ret = -EIO;
|
||||
ret = cros_ec_motion_send_host_cmd(&st->core, 0);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
|
||||
*val = st->core.resp->sensor_range.ret;
|
||||
|
||||
/* scale * in_pressure_raw --> kPa */
|
||||
|
@ -152,8 +155,6 @@ static int cros_ec_baro_probe(struct platform_device *pdev)
|
|||
channel->ext_info = cros_ec_sensors_ext_info;
|
||||
channel->scan_type.sign = 'u';
|
||||
|
||||
state->core.calib[0] = 0;
|
||||
|
||||
/* Sensor specific */
|
||||
switch (state->core.type) {
|
||||
case MOTIONSENSE_TYPE_BARO:
|
||||
|
|
|
@ -243,10 +243,10 @@ static int hp03_probe(struct i2c_client *client,
|
|||
* which has it's dedicated I2C address and contains
|
||||
* the calibration constants for the sensor.
|
||||
*/
|
||||
priv->eeprom_client = i2c_new_dummy(client->adapter, HP03_EEPROM_ADDR);
|
||||
if (!priv->eeprom_client) {
|
||||
priv->eeprom_client = i2c_new_dummy_device(client->adapter, HP03_EEPROM_ADDR);
|
||||
if (IS_ERR(priv->eeprom_client)) {
|
||||
dev_err(dev, "New EEPROM I2C device failed\n");
|
||||
return -ENODEV;
|
||||
return PTR_ERR(priv->eeprom_client);
|
||||
}
|
||||
|
||||
priv->eeprom_regmap = regmap_init_i2c(priv->eeprom_client,
|
||||
|
|
|
@ -41,6 +41,7 @@ static const struct st_sensors_platform_data default_press_pdata = {
|
|||
.drdy_int_pin = 1,
|
||||
};
|
||||
|
||||
const struct st_sensor_settings *st_press_get_settings(const char *name);
|
||||
int st_press_common_probe(struct iio_dev *indio_dev);
|
||||
void st_press_common_remove(struct iio_dev *indio_dev);
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue