Staging and IIO driver update for 4.7-rc1
Here's the big staging and iio driver update for 4.7-rc1. I think we almost broke even with this release, only adding a few more lines than we removed, which isn't bad overall given that there's a bunch of new iio drivers added. The Lustre developers seem to have woken up from their sleep and have been doing a great job in cleaning up the code and pruning unused or old cruft, the filesystem is almost readable :) Other than that, just a lot of basic coding style cleanups in the churn. All have been in linux-next for a while with no reported issues. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iEYEABECAAYFAlc/00QACgkQMUfUDdst+ynXYQCdG9oEsw4CCItbjGfQau5YVGbd TOcAnA19tZz+Wcg3sLT8Zsm979dgVvDt =9UG/ -----END PGP SIGNATURE----- Merge tag 'staging-4.7-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging Pull staging and IIO driver updates from Greg KH: "Here's the big staging and iio driver update for 4.7-rc1. I think we almost broke even with this release, only adding a few more lines than we removed, which isn't bad overall given that there's a bunch of new iio drivers added. The Lustre developers seem to have woken up from their sleep and have been doing a great job in cleaning up the code and pruning unused or old cruft, the filesystem is almost readable :) Other than that, just a lot of basic coding style cleanups in the churn. All have been in linux-next for a while with no reported issues" * tag 'staging-4.7-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging: (938 commits) Staging: emxx_udc: emxx_udc: fixed coding style issue staging/gdm724x: fix "alignment should match open parenthesis" issues staging/gdm724x: Fix avoid CamelCase staging: unisys: rename misleading var ii with frag staging: unisys: visorhba: switch success handling to error handling staging: unisys: visorhba: main path needs to flow down the left margin staging: unisys: visorinput: handle_locking_key() simplifications staging: unisys: visorhba: fail gracefully for thread creation failures staging: unisys: visornic: comment restructuring and removing bad diction staging: unisys: fix format string %Lx to %llx for u64 staging: unisys: remove unused struct members staging: unisys: visorchannel: correct variable misspelling staging: unisys: visorhba: replace functionlike macro with function staging: dgnc: Need to check for NULL of ch staging: dgnc: remove redundant condition check staging: dgnc: fix 'line over 80 characters' staging: dgnc: clean up the dgnc_get_modem_info() staging: lustre: lnet: enable configuration per NI interface staging: lustre: o2iblnd: properly set ibr_why staging: lustre: o2iblnd: remove last of kiblnd_tunables_fini ...
This commit is contained in:
commit
2f37dd131c
|
@ -1233,7 +1233,7 @@ KernelVersion: 3.4
|
|||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Proximity measurement indicating that some
|
||||
object is near the sensor, usually be observing
|
||||
object is near the sensor, usually by observing
|
||||
reflectivity of infrared or ultrasound emitted.
|
||||
Often these sensors are unit less and as such conversion
|
||||
to SI units is not possible. Higher proximity measurements
|
||||
|
@ -1255,12 +1255,23 @@ Description:
|
|||
What: /sys/.../iio:deviceX/in_intensityY_raw
|
||||
What: /sys/.../iio:deviceX/in_intensityY_ir_raw
|
||||
What: /sys/.../iio:deviceX/in_intensityY_both_raw
|
||||
What: /sys/.../iio:deviceX/in_intensityY_uv_raw
|
||||
KernelVersion: 3.4
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Unit-less light intensity. Modifiers both and ir indicate
|
||||
that measurements contains visible and infrared light
|
||||
components or just infrared light, respectively.
|
||||
components or just infrared light, respectively. Modifier uv indicates
|
||||
that measurements contain ultraviolet light components.
|
||||
|
||||
What: /sys/.../iio:deviceX/in_uvindex_input
|
||||
KernelVersion: 4.6
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
UV light intensity index measuring the human skin's response to
|
||||
different wavelength of sunlight weighted according to the
|
||||
standardised CIE Erythemal Action Spectrum. UV index values range
|
||||
from 0 (low) to >=11 (extreme).
|
||||
|
||||
What: /sys/.../iio:deviceX/in_intensity_red_integration_time
|
||||
What: /sys/.../iio:deviceX/in_intensity_green_integration_time
|
||||
|
@ -1501,3 +1512,56 @@ Contact: linux-iio@vger.kernel.org
|
|||
Description:
|
||||
Raw (unscaled no offset etc.) pH reading of a substance as a negative
|
||||
base-10 logarithm of hydrodium ions in a litre of water.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/mount_matrix
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_mount_matrix
|
||||
What: /sys/bus/iio/devices/iio:deviceX/out_mount_matrix
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_anglvel_mount_matrix
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_accel_mount_matrix
|
||||
KernelVersion: 4.6
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Mounting matrix for IIO sensors. This is a rotation matrix which
|
||||
informs userspace about sensor chip's placement relative to the
|
||||
main hardware it is mounted on.
|
||||
Main hardware placement is defined according to the local
|
||||
reference frame related to the physical quantity the sensor
|
||||
measures.
|
||||
Given that the rotation matrix is defined in a board specific
|
||||
way (platform data and / or device-tree), the main hardware
|
||||
reference frame definition is left to the implementor's choice
|
||||
(see below for a magnetometer example).
|
||||
Applications should apply this rotation matrix to samples so
|
||||
that when main hardware reference frame is aligned onto local
|
||||
reference frame, then sensor chip reference frame is also
|
||||
perfectly aligned with it.
|
||||
Matrix is a 3x3 unitary matrix and typically looks like
|
||||
[0, 1, 0; 1, 0, 0; 0, 0, -1]. Identity matrix
|
||||
[1, 0, 0; 0, 1, 0; 0, 0, 1] means sensor chip and main hardware
|
||||
are perfectly aligned with each other.
|
||||
|
||||
For example, a mounting matrix for a magnetometer sensor informs
|
||||
userspace about sensor chip's ORIENTATION relative to the main
|
||||
hardware.
|
||||
More specifically, main hardware orientation 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 ;
|
||||
* Z is perpendicular to the ground plane and positive upwards.
|
||||
|
||||
An implementor might consider that for a hand-held device, a
|
||||
'natural' orientation would be 'front facing camera at the top'.
|
||||
The main hardware reference frame could then be described as :
|
||||
* Y is in the plane of the screen and is positive towards the
|
||||
top of the screen ;
|
||||
* X is in the plane of the screen, perpendicular to Y axis, and
|
||||
positive towards the right hand side of the screen ;
|
||||
* Z is perpendicular to the screen plane and positive out of the
|
||||
screen.
|
||||
Another example for a quadrotor UAV might be :
|
||||
* Y is in the plane of the propellers and positive towards the
|
||||
front-view camera;
|
||||
* X is in the plane of the propellers, perpendicular to Y axis,
|
||||
and positive towards the starboard side of the UAV ;
|
||||
* Z is perpendicular to propellers plane and positive upwards.
|
||||
|
|
|
@ -136,6 +136,8 @@ X!Edrivers/base/interface.c
|
|||
!Iinclude/linux/seqno-fence.h
|
||||
!Edrivers/dma-buf/reservation.c
|
||||
!Iinclude/linux/reservation.h
|
||||
!Edrivers/dma-buf/sync_file.c
|
||||
!Iinclude/linux/sync_file.h
|
||||
!Edrivers/base/dma-coherent.c
|
||||
!Edrivers/base/dma-mapping.c
|
||||
</sect1>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
Freescale MMA8451Q, MMA8452Q, MMA8453Q, MMA8652FC or MMA8653FC
|
||||
Freescale MMA8451Q, MMA8452Q, MMA8453Q, MMA8652FC, MMA8653FC or FXLS8471Q
|
||||
triaxial accelerometer
|
||||
|
||||
Required properties:
|
||||
|
@ -9,6 +9,7 @@ Required properties:
|
|||
* "fsl,mma8453"
|
||||
* "fsl,mma8652"
|
||||
* "fsl,mma8653"
|
||||
* "fsl,fxls8471"
|
||||
|
||||
- reg: the I2C address of the chip
|
||||
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
NXP LPC1850 ADC bindings
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "nxp,lpc1850-adc"
|
||||
- reg: Offset and length of the register set for the ADC device
|
||||
- interrupts: The interrupt number for the ADC device
|
||||
- clocks: The root clock of the ADC controller
|
||||
- vref-supply: The regulator supply ADC reference voltage
|
||||
- resets: phandle to reset controller and line specifier
|
||||
|
||||
Example:
|
||||
|
||||
adc0: adc@400e3000 {
|
||||
compatible = "nxp,lpc1850-adc";
|
||||
reg = <0x400e3000 0x1000>;
|
||||
interrupts = <17>;
|
||||
clocks = <&ccu1 CLK_APB3_ADC0>;
|
||||
vref-supply = <®_vdda>;
|
||||
resets = <&rgu 40>;
|
||||
status = "disabled";
|
||||
};
|
|
@ -1,7 +1,11 @@
|
|||
Rockchip Successive Approximation Register (SAR) A/D Converter bindings
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "rockchip,saradc" or "rockchip,rk3066-tsadc"
|
||||
- compatible: should be "rockchip,<name>-saradc" or "rockchip,rk3066-tsadc"
|
||||
- "rockchip,saradc": for rk3188, rk3288
|
||||
- "rockchip,rk3066-tsadc": for rk3036
|
||||
- "rockchip,rk3399-saradc": for rk3399
|
||||
|
||||
- reg: physical base address of the controller and length of memory mapped
|
||||
region.
|
||||
- interrupts: The interrupt number to the cpu. The interrupt specifier format
|
||||
|
|
|
@ -0,0 +1,155 @@
|
|||
Analog Devices AD5592R/AD5593R DAC/ADC device driver
|
||||
|
||||
Required properties for the AD5592R:
|
||||
- compatible: Must be "adi,ad5592r"
|
||||
- reg: SPI chip select number for the device
|
||||
- spi-max-frequency: Max SPI frequency to use (< 30000000)
|
||||
- spi-cpol: The AD5592R requires inverse clock polarity (CPOL) mode
|
||||
|
||||
Required properties for the AD5593R:
|
||||
- compatible: Must be "adi,ad5593r"
|
||||
- reg: I2C address of the device
|
||||
|
||||
Required properties for all supported chips:
|
||||
- #address-cells: Should be 1.
|
||||
- #size-cells: Should be 0.
|
||||
- channel nodes:
|
||||
Each child node represents one channel and has the following
|
||||
Required properties:
|
||||
* reg: Pin on which this channel is connected to.
|
||||
* adi,mode: Mode or function of this channel.
|
||||
Macros specifying the valid values
|
||||
can be found in <dt-bindings/iio/adi,ad5592r.h>.
|
||||
|
||||
The following values are currently supported:
|
||||
* CH_MODE_UNUSED (the pin is unused)
|
||||
* CH_MODE_ADC (the pin is ADC input)
|
||||
* CH_MODE_DAC (the pin is DAC output)
|
||||
* CH_MODE_DAC_AND_ADC (the pin is DAC output
|
||||
but can be monitored by an ADC, since
|
||||
there is no disadvantage this
|
||||
this should be considered as the
|
||||
preferred DAC mode)
|
||||
* CH_MODE_GPIO (the pin is registered
|
||||
with GPIOLIB)
|
||||
Optional properties:
|
||||
* adi,off-state: State of this channel when unused or the
|
||||
device gets removed. Macros specifying the
|
||||
valid values can be found in
|
||||
<dt-bindings/iio/adi,ad5592r.h>.
|
||||
|
||||
* CH_OFFSTATE_PULLDOWN (the pin is pulled down)
|
||||
* CH_OFFSTATE_OUT_LOW (the pin is output low)
|
||||
* CH_OFFSTATE_OUT_HIGH (the pin is output high)
|
||||
* CH_OFFSTATE_OUT_TRISTATE (the pin is
|
||||
tristated output)
|
||||
|
||||
|
||||
Optional properties:
|
||||
- vref-supply: Phandle to the external reference voltage supply. This should
|
||||
only be set if there is an external reference voltage connected to the VREF
|
||||
pin. If the property is not set the internal 2.5V reference is used.
|
||||
- reset-gpios : GPIO spec for the RESET pin. If specified, it will be
|
||||
asserted during driver probe.
|
||||
- gpio-controller: Marks the device node as a GPIO controller.
|
||||
- #gpio-cells: Should be 2. The first cell is the GPIO number and the second
|
||||
cell specifies GPIO flags, as defined in <dt-bindings/gpio/gpio.h>.
|
||||
|
||||
AD5592R Example:
|
||||
|
||||
#include <dt-bindings/iio/adi,ad5592r.h>
|
||||
|
||||
vref: regulator-vref {
|
||||
compatible = "regulator-fixed";
|
||||
regulator-name = "vref-ad559x";
|
||||
regulator-min-microvolt = <3300000>;
|
||||
regulator-max-microvolt = <3300000>;
|
||||
regulator-always-on;
|
||||
};
|
||||
|
||||
ad5592r@0 {
|
||||
#size-cells = <0>;
|
||||
#address-cells = <1>;
|
||||
#gpio-cells = <2>;
|
||||
compatible = "adi,ad5592r";
|
||||
reg = <0>;
|
||||
|
||||
spi-max-frequency = <1000000>;
|
||||
spi-cpol;
|
||||
|
||||
vref-supply = <&vref>; /* optional */
|
||||
reset-gpios = <&gpio0 86 0>; /* optional */
|
||||
gpio-controller;
|
||||
|
||||
channel@0 {
|
||||
reg = <0>;
|
||||
adi,mode = <CH_MODE_DAC>;
|
||||
};
|
||||
channel@1 {
|
||||
reg = <1>;
|
||||
adi,mode = <CH_MODE_ADC>;
|
||||
};
|
||||
channel@2 {
|
||||
reg = <2>;
|
||||
adi,mode = <CH_MODE_DAC_AND_ADC>;
|
||||
};
|
||||
channel@3 {
|
||||
reg = <3>;
|
||||
adi,mode = <CH_MODE_DAC_AND_ADC>;
|
||||
adi,off-state = <CH_OFFSTATE_PULLDOWN>;
|
||||
};
|
||||
channel@4 {
|
||||
reg = <4>;
|
||||
adi,mode = <CH_MODE_UNUSED>;
|
||||
adi,off-state = <CH_OFFSTATE_PULLDOWN>;
|
||||
};
|
||||
channel@5 {
|
||||
reg = <5>;
|
||||
adi,mode = <CH_MODE_GPIO>;
|
||||
adi,off-state = <CH_OFFSTATE_PULLDOWN>;
|
||||
};
|
||||
channel@6 {
|
||||
reg = <6>;
|
||||
adi,mode = <CH_MODE_GPIO>;
|
||||
adi,off-state = <CH_OFFSTATE_PULLDOWN>;
|
||||
};
|
||||
channel@7 {
|
||||
reg = <7>;
|
||||
adi,mode = <CH_MODE_GPIO>;
|
||||
adi,off-state = <CH_OFFSTATE_PULLDOWN>;
|
||||
};
|
||||
};
|
||||
|
||||
AD5593R Example:
|
||||
|
||||
#include <dt-bindings/iio/adi,ad5592r.h>
|
||||
|
||||
ad5593r@10 {
|
||||
#size-cells = <0>;
|
||||
#address-cells = <1>;
|
||||
#gpio-cells = <2>;
|
||||
compatible = "adi,ad5593r";
|
||||
reg = <0x10>;
|
||||
gpio-controller;
|
||||
|
||||
channel@0 {
|
||||
reg = <0>;
|
||||
adi,mode = <CH_MODE_DAC>;
|
||||
adi,off-state = <CH_OFFSTATE_PULLDOWN>;
|
||||
};
|
||||
channel@1 {
|
||||
reg = <1>;
|
||||
adi,mode = <CH_MODE_ADC>;
|
||||
adi,off-state = <CH_OFFSTATE_PULLDOWN>;
|
||||
};
|
||||
channel@2 {
|
||||
reg = <2>;
|
||||
adi,mode = <CH_MODE_DAC_AND_ADC>;
|
||||
adi,off-state = <CH_OFFSTATE_PULLDOWN>;
|
||||
};
|
||||
channel@6 {
|
||||
reg = <6>;
|
||||
adi,mode = <CH_MODE_GPIO>;
|
||||
adi,off-state = <CH_OFFSTATE_PULLDOWN>;
|
||||
};
|
||||
};
|
|
@ -0,0 +1,20 @@
|
|||
NXP LPC1850 DAC bindings
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "nxp,lpc1850-dac"
|
||||
- reg: Offset and length of the register set for the ADC device
|
||||
- interrupts: The interrupt number for the ADC device
|
||||
- clocks: The root clock of the ADC controller
|
||||
- vref-supply: The regulator supply ADC reference voltage
|
||||
- resets: phandle to reset controller and line specifier
|
||||
|
||||
Example:
|
||||
dac: dac@400e1000 {
|
||||
compatible = "nxp,lpc1850-dac";
|
||||
reg = <0x400e1000 0x1000>;
|
||||
interrupts = <0>;
|
||||
clocks = <&ccu1 CLK_APB3_DAC>;
|
||||
vref-supply = <®_vdda>;
|
||||
resets = <&rgu 42>;
|
||||
status = "disabled";
|
||||
};
|
|
@ -8,10 +8,23 @@ Required properties:
|
|||
- interrupt-parent : should be the phandle for the interrupt controller
|
||||
- interrupts : interrupt mapping for GPIO IRQ
|
||||
|
||||
Optional properties:
|
||||
- mount-matrix: an optional 3x3 mounting rotation matrix
|
||||
|
||||
|
||||
Example:
|
||||
mpu6050@68 {
|
||||
compatible = "invensense,mpu6050";
|
||||
reg = <0x68>;
|
||||
interrupt-parent = <&gpio1>;
|
||||
interrupts = <18 1>;
|
||||
mount-matrix = "-0.984807753012208", /* x0 */
|
||||
"0", /* y0 */
|
||||
"-0.173648177666930", /* z0 */
|
||||
"0", /* x1 */
|
||||
"-1", /* y1 */
|
||||
"0", /* z1 */
|
||||
"-0.173648177666930", /* x2 */
|
||||
"0", /* y2 */
|
||||
"0.984807753012208"; /* z2 */
|
||||
};
|
||||
|
|
|
@ -8,6 +8,8 @@ Required properties:
|
|||
Optional properties:
|
||||
|
||||
- gpios : should be device tree identifier of the magnetometer DRDY pin
|
||||
- vdd-supply: an optional regulator that needs to be on to provide VDD
|
||||
- mount-matrix: an optional 3x3 mounting rotation matrix
|
||||
|
||||
Example:
|
||||
|
||||
|
@ -15,4 +17,14 @@ ak8975@0c {
|
|||
compatible = "asahi-kasei,ak8975";
|
||||
reg = <0x0c>;
|
||||
gpios = <&gpj0 7 0>;
|
||||
vdd-supply = <&ldo_3v3_gnss>;
|
||||
mount-matrix = "-0.984807753012208", /* x0 */
|
||||
"0", /* y0 */
|
||||
"-0.173648177666930", /* z0 */
|
||||
"0", /* x1 */
|
||||
"-1", /* y1 */
|
||||
"0", /* z1 */
|
||||
"-0.173648177666930", /* x2 */
|
||||
"0", /* y2 */
|
||||
"0.984807753012208"; /* z2 */
|
||||
};
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
* Maxim Integrated DS1803 digital potentiometer driver
|
||||
|
||||
The node for this driver must be a child node of a I2C controller, hence
|
||||
all mandatory properties for your controller must be specified. See directory:
|
||||
|
||||
Documentation/devicetree/bindings/i2c
|
||||
|
||||
for more details.
|
||||
|
||||
Required properties:
|
||||
- compatible: Must be one of the following, depending on the
|
||||
model:
|
||||
"maxim,ds1803-010",
|
||||
"maxim,ds1803-050",
|
||||
"maxim,ds1803-100"
|
||||
|
||||
Example:
|
||||
ds1803: ds1803@1 {
|
||||
reg = <0x28>;
|
||||
compatible = "maxim,ds1803-010";
|
||||
};
|
|
@ -0,0 +1,84 @@
|
|||
* Microchip MCP413X/414X/415X/416X/423X/424X/425X/426X Digital Potentiometer
|
||||
driver
|
||||
|
||||
The node for this driver must be a child node of a SPI controller, hence
|
||||
all mandatory properties described in
|
||||
|
||||
Documentation/devicetree/bindings/spi/spi-bus.txt
|
||||
|
||||
must be specified.
|
||||
|
||||
Required properties:
|
||||
- compatible: Must be one of the following, depending on the
|
||||
model:
|
||||
"microchip,mcp4131-502"
|
||||
"microchip,mcp4131-103"
|
||||
"microchip,mcp4131-503"
|
||||
"microchip,mcp4131-104"
|
||||
"microchip,mcp4132-502"
|
||||
"microchip,mcp4132-103"
|
||||
"microchip,mcp4132-503"
|
||||
"microchip,mcp4132-104"
|
||||
"microchip,mcp4141-502"
|
||||
"microchip,mcp4141-103"
|
||||
"microchip,mcp4141-503"
|
||||
"microchip,mcp4141-104"
|
||||
"microchip,mcp4142-502"
|
||||
"microchip,mcp4142-103"
|
||||
"microchip,mcp4142-503"
|
||||
"microchip,mcp4142-104"
|
||||
"microchip,mcp4151-502"
|
||||
"microchip,mcp4151-103"
|
||||
"microchip,mcp4151-503"
|
||||
"microchip,mcp4151-104"
|
||||
"microchip,mcp4152-502"
|
||||
"microchip,mcp4152-103"
|
||||
"microchip,mcp4152-503"
|
||||
"microchip,mcp4152-104"
|
||||
"microchip,mcp4161-502"
|
||||
"microchip,mcp4161-103"
|
||||
"microchip,mcp4161-503"
|
||||
"microchip,mcp4161-104"
|
||||
"microchip,mcp4162-502"
|
||||
"microchip,mcp4162-103"
|
||||
"microchip,mcp4162-503"
|
||||
"microchip,mcp4162-104"
|
||||
"microchip,mcp4231-502"
|
||||
"microchip,mcp4231-103"
|
||||
"microchip,mcp4231-503"
|
||||
"microchip,mcp4231-104"
|
||||
"microchip,mcp4232-502"
|
||||
"microchip,mcp4232-103"
|
||||
"microchip,mcp4232-503"
|
||||
"microchip,mcp4232-104"
|
||||
"microchip,mcp4241-502"
|
||||
"microchip,mcp4241-103"
|
||||
"microchip,mcp4241-503"
|
||||
"microchip,mcp4241-104"
|
||||
"microchip,mcp4242-502"
|
||||
"microchip,mcp4242-103"
|
||||
"microchip,mcp4242-503"
|
||||
"microchip,mcp4242-104"
|
||||
"microchip,mcp4251-502"
|
||||
"microchip,mcp4251-103"
|
||||
"microchip,mcp4251-503"
|
||||
"microchip,mcp4251-104"
|
||||
"microchip,mcp4252-502"
|
||||
"microchip,mcp4252-103"
|
||||
"microchip,mcp4252-503"
|
||||
"microchip,mcp4252-104"
|
||||
"microchip,mcp4261-502"
|
||||
"microchip,mcp4261-103"
|
||||
"microchip,mcp4261-503"
|
||||
"microchip,mcp4261-104"
|
||||
"microchip,mcp4262-502"
|
||||
"microchip,mcp4262-103"
|
||||
"microchip,mcp4262-503"
|
||||
"microchip,mcp4262-104"
|
||||
|
||||
Example:
|
||||
mcp4131: mcp4131@0 {
|
||||
compatible = "mcp4131-502";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <500000>;
|
||||
};
|
|
@ -0,0 +1,17 @@
|
|||
HopeRF HP03 digital pressure/temperature sensors
|
||||
|
||||
Required properties:
|
||||
- compatible: must be "hoperf,hp03"
|
||||
- xclr-gpio: must be device tree identifier of the XCLR pin.
|
||||
The XCLR pin is a reset of the ADC in the chip,
|
||||
it must be pulled HI before the conversion and
|
||||
readout of the value from the ADC registers and
|
||||
pulled LO afterward.
|
||||
|
||||
Example:
|
||||
|
||||
hp03@0x77 {
|
||||
compatible = "hoperf,hp03";
|
||||
reg = <0x77>;
|
||||
xclr-gpio = <&portc 0 0x0>;
|
||||
};
|
|
@ -0,0 +1,19 @@
|
|||
MEAS ms5611 family pressure sensors
|
||||
|
||||
Pressure sensors from MEAS Switzerland with SPI and I2C bus interfaces.
|
||||
|
||||
Required properties:
|
||||
- compatible: "meas,ms5611" or "meas,ms5607"
|
||||
- reg: the I2C address or SPI chip select the device will respond to
|
||||
|
||||
Optional properties:
|
||||
- vdd-supply: an optional regulator that needs to be on to provide VDD
|
||||
power to the sensor.
|
||||
|
||||
Example:
|
||||
|
||||
ms5607@77 {
|
||||
compatible = "meas,ms5607";
|
||||
reg = <0x77>;
|
||||
vdd-supply = <&ldo_3v3_gnss>;
|
||||
};
|
|
@ -16,6 +16,10 @@ Optional properties:
|
|||
- st,drdy-int-pin: the pin on the package that will be used to signal
|
||||
"data ready" (valid values: 1 or 2). This property is not configurable
|
||||
on all sensors.
|
||||
- drive-open-drain: the interrupt/data ready line will be configured
|
||||
as open drain, which is useful if several sensors share the same
|
||||
interrupt line. (This binding is taken from pinctrl/pinctrl-bindings.txt)
|
||||
This is a boolean property.
|
||||
|
||||
Sensors may also have applicable pin control settings, those use the
|
||||
standard bindings from pinctrl/pinctrl-bindings.txt.
|
||||
|
@ -37,6 +41,7 @@ Accelerometers:
|
|||
- st,lsm330-accel
|
||||
- st,lsm303agr-accel
|
||||
- st,lis2dh12-accel
|
||||
- st,h3lis331dl-accel
|
||||
|
||||
Gyroscopes:
|
||||
- st,l3g4200d-gyro
|
||||
|
@ -46,6 +51,7 @@ Gyroscopes:
|
|||
- st,l3gd20-gyro
|
||||
- st,l3g4is-gyro
|
||||
- st,lsm330-gyro
|
||||
- st,lsm9ds0-gyro
|
||||
|
||||
Magnetometers:
|
||||
- st,lsm303agr-magn
|
||||
|
|
|
@ -151,6 +151,7 @@ lsi LSI Corp. (LSI Logic)
|
|||
lltc Linear Technology Corporation
|
||||
marvell Marvell Technology Group Ltd.
|
||||
maxim Maxim Integrated Products
|
||||
meas Measurement Specialties
|
||||
mediatek MediaTek Inc.
|
||||
melexis Melexis N.V.
|
||||
merrii Merrii Technology Co., Ltd.
|
||||
|
|
|
@ -268,6 +268,10 @@ IIO
|
|||
devm_iio_kfifo_free()
|
||||
devm_iio_trigger_alloc()
|
||||
devm_iio_trigger_free()
|
||||
devm_iio_channel_get()
|
||||
devm_iio_channel_release()
|
||||
devm_iio_channel_get_all()
|
||||
devm_iio_channel_release_all()
|
||||
|
||||
INPUT
|
||||
devm_input_allocate_device()
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
Sync File API Guide
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Gustavo Padovan
|
||||
<gustavo at padovan dot org>
|
||||
|
||||
This document serves as a guide for device drivers writers on what the
|
||||
sync_file API is, and how drivers can support it. Sync file is the carrier of
|
||||
the fences(struct fence) that needs to synchronized between drivers or across
|
||||
process boundaries.
|
||||
|
||||
The sync_file API is meant to be used to send and receive fence information
|
||||
to/from userspace. It enables userspace to do explicit fencing, where instead
|
||||
of attaching a fence to the buffer a producer driver (such as a GPU or V4L
|
||||
driver) sends the fence related to the buffer to userspace via a sync_file.
|
||||
|
||||
The sync_file then can be sent to the consumer (DRM driver for example), that
|
||||
will not use the buffer for anything before the fence(s) signals, i.e., the
|
||||
driver that issued the fence is not using/processing the buffer anymore, so it
|
||||
signals that the buffer is ready to use. And vice-versa for the consumer ->
|
||||
producer part of the cycle.
|
||||
|
||||
Sync files allows userspace awareness on buffer sharing synchronization between
|
||||
drivers.
|
||||
|
||||
Sync file was originally added in the Android kernel but current Linux Desktop
|
||||
can benefit a lot from it.
|
||||
|
||||
in-fences and out-fences
|
||||
------------------------
|
||||
|
||||
Sync files can go either to or from userspace. When a sync_file is sent from
|
||||
the driver to userspace we call the fences it contains 'out-fences'. They are
|
||||
related to a buffer that the driver is processing or is going to process, so
|
||||
the driver an create out-fence to be able to notify, through fence_signal(),
|
||||
when it has finished using (or processing) that buffer. Out-fences are fences
|
||||
that the driver creates.
|
||||
|
||||
On the other hand if the driver receives fence(s) through a sync_file from
|
||||
userspace we call these fence(s) 'in-fences'. Receiveing in-fences means that
|
||||
we need to wait for the fence(s) to signal before using any buffer related to
|
||||
the in-fences.
|
||||
|
||||
Creating Sync Files
|
||||
-------------------
|
||||
|
||||
When a driver needs to send an out-fence userspace it creates a sync_file.
|
||||
|
||||
Interface:
|
||||
struct sync_file *sync_file_create(struct fence *fence);
|
||||
|
||||
The caller pass the out-fence and gets back the sync_file. That is just the
|
||||
first step, next it needs to install an fd on sync_file->file. So it gets an
|
||||
fd:
|
||||
|
||||
fd = get_unused_fd_flags(O_CLOEXEC);
|
||||
|
||||
and installs it on sync_file->file:
|
||||
|
||||
fd_install(fd, sync_file->file);
|
||||
|
||||
The sync_file fd now can be sent to userspace.
|
||||
|
||||
If the creation process fail, or the sync_file needs to be released by any
|
||||
other reason fput(sync_file->file) should be used.
|
||||
|
||||
References:
|
||||
[1] struct sync_file in include/linux/sync_file.h
|
||||
[2] All interfaces mentioned above defined in include/linux/sync_file.h
|
12
MAINTAINERS
12
MAINTAINERS
|
@ -776,6 +776,15 @@ S: Supported
|
|||
F: drivers/android/
|
||||
F: drivers/staging/android/
|
||||
|
||||
ANDROID ION DRIVER
|
||||
M: Laura Abbott <labbott@redhat.com>
|
||||
M: Sumit Semwal <sumit.semwal@linaro.org>
|
||||
L: devel@driverdev.osuosl.org
|
||||
S: Supported
|
||||
F: drivers/staging/android/ion
|
||||
F: drivers/staging/android/uapi/ion.h
|
||||
F: drivers/staging/android/uapi/ion_test.h
|
||||
|
||||
AOA (Apple Onboard Audio) ALSA DRIVER
|
||||
M: Johannes Berg <johannes@sipsolutions.net>
|
||||
L: linuxppc-dev@lists.ozlabs.org
|
||||
|
@ -4746,6 +4755,7 @@ F: sound/soc/fsl/mpc8610_hpcd.c
|
|||
|
||||
FREESCALE QORIQ MANAGEMENT COMPLEX DRIVER
|
||||
M: "J. German Rivera" <German.Rivera@freescale.com>
|
||||
M: Stuart Yoder <stuart.yoder@nxp.com>
|
||||
L: linux-kernel@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/staging/fsl-mc/
|
||||
|
@ -5617,7 +5627,7 @@ IIO SUBSYSTEM AND DRIVERS
|
|||
M: Jonathan Cameron <jic23@kernel.org>
|
||||
R: Hartmut Knaack <knaack.h@gmx.de>
|
||||
R: Lars-Peter Clausen <lars@metafoo.de>
|
||||
R: Peter Meerwald <pmeerw@pmeerw.net>
|
||||
R: Peter Meerwald-Stadler <pmeerw@pmeerw.net>
|
||||
L: linux-iio@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/iio/
|
||||
|
|
|
@ -114,6 +114,8 @@ source "drivers/rtc/Kconfig"
|
|||
|
||||
source "drivers/dma/Kconfig"
|
||||
|
||||
source "drivers/dma-buf/Kconfig"
|
||||
|
||||
source "drivers/dca/Kconfig"
|
||||
|
||||
source "drivers/auxdisplay/Kconfig"
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
menu "DMABUF options"
|
||||
|
||||
config SYNC_FILE
|
||||
bool "sync_file support for fences"
|
||||
default n
|
||||
select ANON_INODES
|
||||
select DMA_SHARED_BUFFER
|
||||
---help---
|
||||
This option enables the fence framework synchronization to export
|
||||
sync_files to userspace that can represent one or more fences.
|
||||
endmenu
|
|
@ -1 +1,2 @@
|
|||
obj-y := dma-buf.o fence.o reservation.o seqno-fence.o
|
||||
obj-$(CONFIG_SYNC_FILE) += sync_file.o
|
||||
|
|
|
@ -0,0 +1,395 @@
|
|||
/*
|
||||
* drivers/dma-buf/sync_file.c
|
||||
*
|
||||
* Copyright (C) 2012 Google, Inc.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/export.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/anon_inodes.h>
|
||||
#include <linux/sync_file.h>
|
||||
#include <uapi/linux/sync_file.h>
|
||||
|
||||
static const struct file_operations sync_file_fops;
|
||||
|
||||
static struct sync_file *sync_file_alloc(int size)
|
||||
{
|
||||
struct sync_file *sync_file;
|
||||
|
||||
sync_file = kzalloc(size, GFP_KERNEL);
|
||||
if (!sync_file)
|
||||
return NULL;
|
||||
|
||||
sync_file->file = anon_inode_getfile("sync_file", &sync_file_fops,
|
||||
sync_file, 0);
|
||||
if (IS_ERR(sync_file->file))
|
||||
goto err;
|
||||
|
||||
kref_init(&sync_file->kref);
|
||||
|
||||
init_waitqueue_head(&sync_file->wq);
|
||||
|
||||
return sync_file;
|
||||
|
||||
err:
|
||||
kfree(sync_file);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void fence_check_cb_func(struct fence *f, struct fence_cb *cb)
|
||||
{
|
||||
struct sync_file_cb *check;
|
||||
struct sync_file *sync_file;
|
||||
|
||||
check = container_of(cb, struct sync_file_cb, cb);
|
||||
sync_file = check->sync_file;
|
||||
|
||||
if (atomic_dec_and_test(&sync_file->status))
|
||||
wake_up_all(&sync_file->wq);
|
||||
}
|
||||
|
||||
/**
|
||||
* sync_file_create() - creates a sync file
|
||||
* @fence: fence to add to the sync_fence
|
||||
*
|
||||
* Creates a sync_file containg @fence. Once this is called, the sync_file
|
||||
* takes ownership of @fence. The sync_file can be released with
|
||||
* fput(sync_file->file). Returns the sync_file or NULL in case of error.
|
||||
*/
|
||||
struct sync_file *sync_file_create(struct fence *fence)
|
||||
{
|
||||
struct sync_file *sync_file;
|
||||
|
||||
sync_file = sync_file_alloc(offsetof(struct sync_file, cbs[1]));
|
||||
if (!sync_file)
|
||||
return NULL;
|
||||
|
||||
sync_file->num_fences = 1;
|
||||
atomic_set(&sync_file->status, 1);
|
||||
snprintf(sync_file->name, sizeof(sync_file->name), "%s-%s%d-%d",
|
||||
fence->ops->get_driver_name(fence),
|
||||
fence->ops->get_timeline_name(fence), fence->context,
|
||||
fence->seqno);
|
||||
|
||||
sync_file->cbs[0].fence = fence;
|
||||
sync_file->cbs[0].sync_file = sync_file;
|
||||
if (fence_add_callback(fence, &sync_file->cbs[0].cb,
|
||||
fence_check_cb_func))
|
||||
atomic_dec(&sync_file->status);
|
||||
|
||||
return sync_file;
|
||||
}
|
||||
EXPORT_SYMBOL(sync_file_create);
|
||||
|
||||
/**
|
||||
* sync_file_fdget() - get a sync_file from an fd
|
||||
* @fd: fd referencing a fence
|
||||
*
|
||||
* Ensures @fd references a valid sync_file, increments the refcount of the
|
||||
* backing file. Returns the sync_file or NULL in case of error.
|
||||
*/
|
||||
static struct sync_file *sync_file_fdget(int fd)
|
||||
{
|
||||
struct file *file = fget(fd);
|
||||
|
||||
if (!file)
|
||||
return NULL;
|
||||
|
||||
if (file->f_op != &sync_file_fops)
|
||||
goto err;
|
||||
|
||||
return file->private_data;
|
||||
|
||||
err:
|
||||
fput(file);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void sync_file_add_pt(struct sync_file *sync_file, int *i,
|
||||
struct fence *fence)
|
||||
{
|
||||
sync_file->cbs[*i].fence = fence;
|
||||
sync_file->cbs[*i].sync_file = sync_file;
|
||||
|
||||
if (!fence_add_callback(fence, &sync_file->cbs[*i].cb,
|
||||
fence_check_cb_func)) {
|
||||
fence_get(fence);
|
||||
(*i)++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* sync_file_merge() - merge two sync_files
|
||||
* @name: name of new fence
|
||||
* @a: sync_file a
|
||||
* @b: sync_file b
|
||||
*
|
||||
* Creates a new sync_file which contains copies of all the fences in both
|
||||
* @a and @b. @a and @b remain valid, independent sync_file. Returns the
|
||||
* new merged sync_file or NULL in case of error.
|
||||
*/
|
||||
static struct sync_file *sync_file_merge(const char *name, struct sync_file *a,
|
||||
struct sync_file *b)
|
||||
{
|
||||
int num_fences = a->num_fences + b->num_fences;
|
||||
struct sync_file *sync_file;
|
||||
int i, i_a, i_b;
|
||||
unsigned long size = offsetof(struct sync_file, cbs[num_fences]);
|
||||
|
||||
sync_file = sync_file_alloc(size);
|
||||
if (!sync_file)
|
||||
return NULL;
|
||||
|
||||
atomic_set(&sync_file->status, num_fences);
|
||||
|
||||
/*
|
||||
* Assume sync_file a and b are both ordered and have no
|
||||
* duplicates with the same context.
|
||||
*
|
||||
* If a sync_file can only be created with sync_file_merge
|
||||
* and sync_file_create, this is a reasonable assumption.
|
||||
*/
|
||||
for (i = i_a = i_b = 0; i_a < a->num_fences && i_b < b->num_fences; ) {
|
||||
struct fence *pt_a = a->cbs[i_a].fence;
|
||||
struct fence *pt_b = b->cbs[i_b].fence;
|
||||
|
||||
if (pt_a->context < pt_b->context) {
|
||||
sync_file_add_pt(sync_file, &i, pt_a);
|
||||
|
||||
i_a++;
|
||||
} else if (pt_a->context > pt_b->context) {
|
||||
sync_file_add_pt(sync_file, &i, pt_b);
|
||||
|
||||
i_b++;
|
||||
} else {
|
||||
if (pt_a->seqno - pt_b->seqno <= INT_MAX)
|
||||
sync_file_add_pt(sync_file, &i, pt_a);
|
||||
else
|
||||
sync_file_add_pt(sync_file, &i, pt_b);
|
||||
|
||||
i_a++;
|
||||
i_b++;
|
||||
}
|
||||
}
|
||||
|
||||
for (; i_a < a->num_fences; i_a++)
|
||||
sync_file_add_pt(sync_file, &i, a->cbs[i_a].fence);
|
||||
|
||||
for (; i_b < b->num_fences; i_b++)
|
||||
sync_file_add_pt(sync_file, &i, b->cbs[i_b].fence);
|
||||
|
||||
if (num_fences > i)
|
||||
atomic_sub(num_fences - i, &sync_file->status);
|
||||
sync_file->num_fences = i;
|
||||
|
||||
strlcpy(sync_file->name, name, sizeof(sync_file->name));
|
||||
return sync_file;
|
||||
}
|
||||
|
||||
static void sync_file_free(struct kref *kref)
|
||||
{
|
||||
struct sync_file *sync_file = container_of(kref, struct sync_file,
|
||||
kref);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < sync_file->num_fences; ++i) {
|
||||
fence_remove_callback(sync_file->cbs[i].fence,
|
||||
&sync_file->cbs[i].cb);
|
||||
fence_put(sync_file->cbs[i].fence);
|
||||
}
|
||||
|
||||
kfree(sync_file);
|
||||
}
|
||||
|
||||
static int sync_file_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct sync_file *sync_file = file->private_data;
|
||||
|
||||
kref_put(&sync_file->kref, sync_file_free);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int sync_file_poll(struct file *file, poll_table *wait)
|
||||
{
|
||||
struct sync_file *sync_file = file->private_data;
|
||||
int status;
|
||||
|
||||
poll_wait(file, &sync_file->wq, wait);
|
||||
|
||||
status = atomic_read(&sync_file->status);
|
||||
|
||||
if (!status)
|
||||
return POLLIN;
|
||||
if (status < 0)
|
||||
return POLLERR;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long sync_file_ioctl_merge(struct sync_file *sync_file,
|
||||
unsigned long arg)
|
||||
{
|
||||
int fd = get_unused_fd_flags(O_CLOEXEC);
|
||||
int err;
|
||||
struct sync_file *fence2, *fence3;
|
||||
struct sync_merge_data data;
|
||||
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
|
||||
if (copy_from_user(&data, (void __user *)arg, sizeof(data))) {
|
||||
err = -EFAULT;
|
||||
goto err_put_fd;
|
||||
}
|
||||
|
||||
if (data.flags || data.pad) {
|
||||
err = -EINVAL;
|
||||
goto err_put_fd;
|
||||
}
|
||||
|
||||
fence2 = sync_file_fdget(data.fd2);
|
||||
if (!fence2) {
|
||||
err = -ENOENT;
|
||||
goto err_put_fd;
|
||||
}
|
||||
|
||||
data.name[sizeof(data.name) - 1] = '\0';
|
||||
fence3 = sync_file_merge(data.name, sync_file, fence2);
|
||||
if (!fence3) {
|
||||
err = -ENOMEM;
|
||||
goto err_put_fence2;
|
||||
}
|
||||
|
||||
data.fence = fd;
|
||||
if (copy_to_user((void __user *)arg, &data, sizeof(data))) {
|
||||
err = -EFAULT;
|
||||
goto err_put_fence3;
|
||||
}
|
||||
|
||||
fd_install(fd, fence3->file);
|
||||
fput(fence2->file);
|
||||
return 0;
|
||||
|
||||
err_put_fence3:
|
||||
fput(fence3->file);
|
||||
|
||||
err_put_fence2:
|
||||
fput(fence2->file);
|
||||
|
||||
err_put_fd:
|
||||
put_unused_fd(fd);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void sync_fill_fence_info(struct fence *fence,
|
||||
struct sync_fence_info *info)
|
||||
{
|
||||
strlcpy(info->obj_name, fence->ops->get_timeline_name(fence),
|
||||
sizeof(info->obj_name));
|
||||
strlcpy(info->driver_name, fence->ops->get_driver_name(fence),
|
||||
sizeof(info->driver_name));
|
||||
if (fence_is_signaled(fence))
|
||||
info->status = fence->status >= 0 ? 1 : fence->status;
|
||||
else
|
||||
info->status = 0;
|
||||
info->timestamp_ns = ktime_to_ns(fence->timestamp);
|
||||
}
|
||||
|
||||
static long sync_file_ioctl_fence_info(struct sync_file *sync_file,
|
||||
unsigned long arg)
|
||||
{
|
||||
struct sync_file_info info;
|
||||
struct sync_fence_info *fence_info = NULL;
|
||||
__u32 size;
|
||||
int ret, i;
|
||||
|
||||
if (copy_from_user(&info, (void __user *)arg, sizeof(info)))
|
||||
return -EFAULT;
|
||||
|
||||
if (info.flags || info.pad)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Passing num_fences = 0 means that userspace doesn't want to
|
||||
* retrieve any sync_fence_info. If num_fences = 0 we skip filling
|
||||
* sync_fence_info and return the actual number of fences on
|
||||
* info->num_fences.
|
||||
*/
|
||||
if (!info.num_fences)
|
||||
goto no_fences;
|
||||
|
||||
if (info.num_fences < sync_file->num_fences)
|
||||
return -EINVAL;
|
||||
|
||||
size = sync_file->num_fences * sizeof(*fence_info);
|
||||
fence_info = kzalloc(size, GFP_KERNEL);
|
||||
if (!fence_info)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < sync_file->num_fences; ++i)
|
||||
sync_fill_fence_info(sync_file->cbs[i].fence, &fence_info[i]);
|
||||
|
||||
if (copy_to_user(u64_to_user_ptr(info.sync_fence_info), fence_info,
|
||||
size)) {
|
||||
ret = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
no_fences:
|
||||
strlcpy(info.name, sync_file->name, sizeof(info.name));
|
||||
info.status = atomic_read(&sync_file->status);
|
||||
if (info.status >= 0)
|
||||
info.status = !info.status;
|
||||
|
||||
info.num_fences = sync_file->num_fences;
|
||||
|
||||
if (copy_to_user((void __user *)arg, &info, sizeof(info)))
|
||||
ret = -EFAULT;
|
||||
else
|
||||
ret = 0;
|
||||
|
||||
out:
|
||||
kfree(fence_info);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static long sync_file_ioctl(struct file *file, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
struct sync_file *sync_file = file->private_data;
|
||||
|
||||
switch (cmd) {
|
||||
case SYNC_IOC_MERGE:
|
||||
return sync_file_ioctl_merge(sync_file, arg);
|
||||
|
||||
case SYNC_IOC_FILE_INFO:
|
||||
return sync_file_ioctl_fence_info(sync_file, arg);
|
||||
|
||||
default:
|
||||
return -ENOTTY;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct file_operations sync_file_fops = {
|
||||
.release = sync_file_release,
|
||||
.poll = sync_file_poll,
|
||||
.unlocked_ioctl = sync_file_ioctl,
|
||||
.compat_ioctl = sync_file_ioctl,
|
||||
};
|
||||
|
|
@ -28,11 +28,6 @@
|
|||
#define BO_LOCKED 0x4000
|
||||
#define BO_PINNED 0x2000
|
||||
|
||||
static inline void __user *to_user_ptr(u64 address)
|
||||
{
|
||||
return (void __user *)(uintptr_t)address;
|
||||
}
|
||||
|
||||
static struct etnaviv_gem_submit *submit_create(struct drm_device *dev,
|
||||
struct etnaviv_gpu *gpu, size_t nr)
|
||||
{
|
||||
|
@ -347,21 +342,21 @@ int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data,
|
|||
cmdbuf->exec_state = args->exec_state;
|
||||
cmdbuf->ctx = file->driver_priv;
|
||||
|
||||
ret = copy_from_user(bos, to_user_ptr(args->bos),
|
||||
ret = copy_from_user(bos, u64_to_user_ptr(args->bos),
|
||||
args->nr_bos * sizeof(*bos));
|
||||
if (ret) {
|
||||
ret = -EFAULT;
|
||||
goto err_submit_cmds;
|
||||
}
|
||||
|
||||
ret = copy_from_user(relocs, to_user_ptr(args->relocs),
|
||||
ret = copy_from_user(relocs, u64_to_user_ptr(args->relocs),
|
||||
args->nr_relocs * sizeof(*relocs));
|
||||
if (ret) {
|
||||
ret = -EFAULT;
|
||||
goto err_submit_cmds;
|
||||
}
|
||||
|
||||
ret = copy_from_user(stream, to_user_ptr(args->stream),
|
||||
ret = copy_from_user(stream, u64_to_user_ptr(args->stream),
|
||||
args->stream_size);
|
||||
if (ret) {
|
||||
ret = -EFAULT;
|
||||
|
|
|
@ -3577,11 +3577,6 @@ static inline i915_reg_t i915_vgacntrl_reg(struct drm_device *dev)
|
|||
return VGACNTRL;
|
||||
}
|
||||
|
||||
static inline void __user *to_user_ptr(u64 address)
|
||||
{
|
||||
return (void __user *)(uintptr_t)address;
|
||||
}
|
||||
|
||||
static inline unsigned long msecs_to_jiffies_timeout(const unsigned int m)
|
||||
{
|
||||
unsigned long j = msecs_to_jiffies(m);
|
||||
|
|
|
@ -324,7 +324,7 @@ i915_gem_phys_pwrite(struct drm_i915_gem_object *obj,
|
|||
{
|
||||
struct drm_device *dev = obj->base.dev;
|
||||
void *vaddr = obj->phys_handle->vaddr + args->offset;
|
||||
char __user *user_data = to_user_ptr(args->data_ptr);
|
||||
char __user *user_data = u64_to_user_ptr(args->data_ptr);
|
||||
int ret = 0;
|
||||
|
||||
/* We manually control the domain here and pretend that it
|
||||
|
@ -605,7 +605,7 @@ i915_gem_shmem_pread(struct drm_device *dev,
|
|||
int needs_clflush = 0;
|
||||
struct sg_page_iter sg_iter;
|
||||
|
||||
user_data = to_user_ptr(args->data_ptr);
|
||||
user_data = u64_to_user_ptr(args->data_ptr);
|
||||
remain = args->size;
|
||||
|
||||
obj_do_bit17_swizzling = i915_gem_object_needs_bit17_swizzle(obj);
|
||||
|
@ -692,7 +692,7 @@ i915_gem_pread_ioctl(struct drm_device *dev, void *data,
|
|||
return 0;
|
||||
|
||||
if (!access_ok(VERIFY_WRITE,
|
||||
to_user_ptr(args->data_ptr),
|
||||
u64_to_user_ptr(args->data_ptr),
|
||||
args->size))
|
||||
return -EFAULT;
|
||||
|
||||
|
@ -783,7 +783,7 @@ i915_gem_gtt_pwrite_fast(struct drm_device *dev,
|
|||
if (ret)
|
||||
goto out_unpin;
|
||||
|
||||
user_data = to_user_ptr(args->data_ptr);
|
||||
user_data = u64_to_user_ptr(args->data_ptr);
|
||||
remain = args->size;
|
||||
|
||||
offset = i915_gem_obj_ggtt_offset(obj) + args->offset;
|
||||
|
@ -907,7 +907,7 @@ i915_gem_shmem_pwrite(struct drm_device *dev,
|
|||
int needs_clflush_before = 0;
|
||||
struct sg_page_iter sg_iter;
|
||||
|
||||
user_data = to_user_ptr(args->data_ptr);
|
||||
user_data = u64_to_user_ptr(args->data_ptr);
|
||||
remain = args->size;
|
||||
|
||||
obj_do_bit17_swizzling = i915_gem_object_needs_bit17_swizzle(obj);
|
||||
|
@ -1036,12 +1036,12 @@ i915_gem_pwrite_ioctl(struct drm_device *dev, void *data,
|
|||
return 0;
|
||||
|
||||
if (!access_ok(VERIFY_READ,
|
||||
to_user_ptr(args->data_ptr),
|
||||
u64_to_user_ptr(args->data_ptr),
|
||||
args->size))
|
||||
return -EFAULT;
|
||||
|
||||
if (likely(!i915.prefault_disable)) {
|
||||
ret = fault_in_multipages_readable(to_user_ptr(args->data_ptr),
|
||||
ret = fault_in_multipages_readable(u64_to_user_ptr(args->data_ptr),
|
||||
args->size);
|
||||
if (ret)
|
||||
return -EFAULT;
|
||||
|
|
|
@ -514,7 +514,7 @@ i915_gem_execbuffer_relocate_vma(struct i915_vma *vma,
|
|||
struct drm_i915_gem_exec_object2 *entry = vma->exec_entry;
|
||||
int remain, ret;
|
||||
|
||||
user_relocs = to_user_ptr(entry->relocs_ptr);
|
||||
user_relocs = u64_to_user_ptr(entry->relocs_ptr);
|
||||
|
||||
remain = entry->relocation_count;
|
||||
while (remain) {
|
||||
|
@ -865,7 +865,7 @@ i915_gem_execbuffer_relocate_slow(struct drm_device *dev,
|
|||
u64 invalid_offset = (u64)-1;
|
||||
int j;
|
||||
|
||||
user_relocs = to_user_ptr(exec[i].relocs_ptr);
|
||||
user_relocs = u64_to_user_ptr(exec[i].relocs_ptr);
|
||||
|
||||
if (copy_from_user(reloc+total, user_relocs,
|
||||
exec[i].relocation_count * sizeof(*reloc))) {
|
||||
|
@ -1009,7 +1009,7 @@ validate_exec_list(struct drm_device *dev,
|
|||
invalid_flags |= EXEC_OBJECT_NEEDS_GTT;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
char __user *ptr = to_user_ptr(exec[i].relocs_ptr);
|
||||
char __user *ptr = u64_to_user_ptr(exec[i].relocs_ptr);
|
||||
int length; /* limited by fault_in_pages_readable() */
|
||||
|
||||
if (exec[i].flags & invalid_flags)
|
||||
|
@ -1696,7 +1696,7 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
|
|||
return -ENOMEM;
|
||||
}
|
||||
ret = copy_from_user(exec_list,
|
||||
to_user_ptr(args->buffers_ptr),
|
||||
u64_to_user_ptr(args->buffers_ptr),
|
||||
sizeof(*exec_list) * args->buffer_count);
|
||||
if (ret != 0) {
|
||||
DRM_DEBUG("copy %d exec entries failed %d\n",
|
||||
|
@ -1732,7 +1732,7 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
|
|||
ret = i915_gem_do_execbuffer(dev, data, file, &exec2, exec2_list);
|
||||
if (!ret) {
|
||||
struct drm_i915_gem_exec_object __user *user_exec_list =
|
||||
to_user_ptr(args->buffers_ptr);
|
||||
u64_to_user_ptr(args->buffers_ptr);
|
||||
|
||||
/* Copy the new buffer offsets back to the user's exec list. */
|
||||
for (i = 0; i < args->buffer_count; i++) {
|
||||
|
@ -1786,7 +1786,7 @@ i915_gem_execbuffer2(struct drm_device *dev, void *data,
|
|||
return -ENOMEM;
|
||||
}
|
||||
ret = copy_from_user(exec2_list,
|
||||
to_user_ptr(args->buffers_ptr),
|
||||
u64_to_user_ptr(args->buffers_ptr),
|
||||
sizeof(*exec2_list) * args->buffer_count);
|
||||
if (ret != 0) {
|
||||
DRM_DEBUG("copy %d exec entries failed %d\n",
|
||||
|
@ -1799,7 +1799,7 @@ i915_gem_execbuffer2(struct drm_device *dev, void *data,
|
|||
if (!ret) {
|
||||
/* Copy the new buffer offsets back to the user's exec list. */
|
||||
struct drm_i915_gem_exec_object2 __user *user_exec_list =
|
||||
to_user_ptr(args->buffers_ptr);
|
||||
u64_to_user_ptr(args->buffers_ptr);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < args->buffer_count; i++) {
|
||||
|
|
|
@ -28,11 +28,6 @@
|
|||
#define BO_LOCKED 0x4000
|
||||
#define BO_PINNED 0x2000
|
||||
|
||||
static inline void __user *to_user_ptr(u64 address)
|
||||
{
|
||||
return (void __user *)(uintptr_t)address;
|
||||
}
|
||||
|
||||
static struct msm_gem_submit *submit_create(struct drm_device *dev,
|
||||
struct msm_gpu *gpu, int nr)
|
||||
{
|
||||
|
@ -68,7 +63,7 @@ static int submit_lookup_objects(struct msm_gem_submit *submit,
|
|||
struct drm_gem_object *obj;
|
||||
struct msm_gem_object *msm_obj;
|
||||
void __user *userptr =
|
||||
to_user_ptr(args->bos + (i * sizeof(submit_bo)));
|
||||
u64_to_user_ptr(args->bos + (i * sizeof(submit_bo)));
|
||||
|
||||
ret = copy_from_user(&submit_bo, userptr, sizeof(submit_bo));
|
||||
if (ret) {
|
||||
|
@ -257,7 +252,7 @@ static int submit_reloc(struct msm_gem_submit *submit, struct msm_gem_object *ob
|
|||
for (i = 0; i < nr_relocs; i++) {
|
||||
struct drm_msm_gem_submit_reloc submit_reloc;
|
||||
void __user *userptr =
|
||||
to_user_ptr(relocs + (i * sizeof(submit_reloc)));
|
||||
u64_to_user_ptr(relocs + (i * sizeof(submit_reloc)));
|
||||
uint32_t iova, off;
|
||||
bool valid;
|
||||
|
||||
|
@ -356,7 +351,7 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data,
|
|||
for (i = 0; i < args->nr_cmds; i++) {
|
||||
struct drm_msm_gem_submit_cmd submit_cmd;
|
||||
void __user *userptr =
|
||||
to_user_ptr(args->cmds + (i * sizeof(submit_cmd)));
|
||||
u64_to_user_ptr(args->cmds + (i * sizeof(submit_cmd)));
|
||||
struct msm_gem_object *msm_obj;
|
||||
uint32_t iova;
|
||||
|
||||
|
|
|
@ -64,7 +64,7 @@ config IIO_ST_ACCEL_3AXIS
|
|||
help
|
||||
Say yes here to build support for STMicroelectronics accelerometers:
|
||||
LSM303DLH, LSM303DLHC, LIS3DH, LSM330D, LSM330DL, LSM330DLC,
|
||||
LIS331DLH, LSM303DL, LSM303DLM, LSM330, LIS2DH12.
|
||||
LIS331DLH, LSM303DL, LSM303DLM, LSM330, LIS2DH12, H3LIS331DL.
|
||||
|
||||
This driver can also be built as a module. If so, these modules
|
||||
will be created:
|
||||
|
@ -143,7 +143,8 @@ config MMA8452
|
|||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
Say yes here to build support for the following Freescale 3-axis
|
||||
accelerometers: MMA8451Q, MMA8452Q, MMA8453Q, MMA8652FC, MMA8653FC.
|
||||
accelerometers: MMA8451Q, MMA8452Q, MMA8453Q, MMA8652FC, MMA8653FC,
|
||||
FXLS8471Q.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called mma8452.
|
||||
|
|
|
@ -25,7 +25,6 @@
|
|||
#include <linux/delay.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/iio/iio.h>
|
||||
|
@ -138,6 +137,7 @@ enum bmc150_accel_axis {
|
|||
AXIS_X,
|
||||
AXIS_Y,
|
||||
AXIS_Z,
|
||||
AXIS_MAX,
|
||||
};
|
||||
|
||||
enum bmc150_power_modes {
|
||||
|
@ -188,7 +188,6 @@ enum bmc150_accel_trigger_id {
|
|||
|
||||
struct bmc150_accel_data {
|
||||
struct regmap *regmap;
|
||||
struct device *dev;
|
||||
int irq;
|
||||
struct bmc150_accel_interrupt interrupts[BMC150_ACCEL_INTERRUPTS];
|
||||
atomic_t active_intr;
|
||||
|
@ -246,16 +245,18 @@ static const struct {
|
|||
{500000, BMC150_ACCEL_SLEEP_500_MS},
|
||||
{1000000, BMC150_ACCEL_SLEEP_1_SEC} };
|
||||
|
||||
static const struct regmap_config bmc150_i2c_regmap_conf = {
|
||||
const struct regmap_config bmc150_regmap_conf = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.max_register = 0x3f,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(bmc150_regmap_conf);
|
||||
|
||||
static int bmc150_accel_set_mode(struct bmc150_accel_data *data,
|
||||
enum bmc150_power_modes mode,
|
||||
int dur_us)
|
||||
{
|
||||
struct device *dev = regmap_get_device(data->regmap);
|
||||
int i;
|
||||
int ret;
|
||||
u8 lpw_bits;
|
||||
|
@ -279,11 +280,11 @@ static int bmc150_accel_set_mode(struct bmc150_accel_data *data,
|
|||
lpw_bits = mode << BMC150_ACCEL_PMU_MODE_SHIFT;
|
||||
lpw_bits |= (dur_val << BMC150_ACCEL_PMU_BIT_SLEEP_DUR_SHIFT);
|
||||
|
||||
dev_dbg(data->dev, "Set Mode bits %x\n", lpw_bits);
|
||||
dev_dbg(dev, "Set Mode bits %x\n", lpw_bits);
|
||||
|
||||
ret = regmap_write(data->regmap, BMC150_ACCEL_REG_PMU_LPW, lpw_bits);
|
||||
if (ret < 0) {
|
||||
dev_err(data->dev, "Error writing reg_pmu_lpw\n");
|
||||
dev_err(dev, "Error writing reg_pmu_lpw\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -316,23 +317,24 @@ static int bmc150_accel_set_bw(struct bmc150_accel_data *data, int val,
|
|||
|
||||
static int bmc150_accel_update_slope(struct bmc150_accel_data *data)
|
||||
{
|
||||
struct device *dev = regmap_get_device(data->regmap);
|
||||
int ret;
|
||||
|
||||
ret = regmap_write(data->regmap, BMC150_ACCEL_REG_INT_6,
|
||||
data->slope_thres);
|
||||
if (ret < 0) {
|
||||
dev_err(data->dev, "Error writing reg_int_6\n");
|
||||
dev_err(dev, "Error writing reg_int_6\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regmap_update_bits(data->regmap, BMC150_ACCEL_REG_INT_5,
|
||||
BMC150_ACCEL_SLOPE_DUR_MASK, data->slope_dur);
|
||||
if (ret < 0) {
|
||||
dev_err(data->dev, "Error updating reg_int_5\n");
|
||||
dev_err(dev, "Error updating reg_int_5\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_dbg(data->dev, "%s: %x %x\n", __func__, data->slope_thres,
|
||||
dev_dbg(dev, "%s: %x %x\n", __func__, data->slope_thres,
|
||||
data->slope_dur);
|
||||
|
||||
return ret;
|
||||
|
@ -378,20 +380,21 @@ static int bmc150_accel_get_startup_times(struct bmc150_accel_data *data)
|
|||
|
||||
static int bmc150_accel_set_power_state(struct bmc150_accel_data *data, bool on)
|
||||
{
|
||||
struct device *dev = regmap_get_device(data->regmap);
|
||||
int ret;
|
||||
|
||||
if (on) {
|
||||
ret = pm_runtime_get_sync(data->dev);
|
||||
ret = pm_runtime_get_sync(dev);
|
||||
} else {
|
||||
pm_runtime_mark_last_busy(data->dev);
|
||||
ret = pm_runtime_put_autosuspend(data->dev);
|
||||
pm_runtime_mark_last_busy(dev);
|
||||
ret = pm_runtime_put_autosuspend(dev);
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(data->dev,
|
||||
dev_err(dev,
|
||||
"Failed: bmc150_accel_set_power_state for %d\n", on);
|
||||
if (on)
|
||||
pm_runtime_put_noidle(data->dev);
|
||||
pm_runtime_put_noidle(dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -445,6 +448,7 @@ static void bmc150_accel_interrupts_setup(struct iio_dev *indio_dev,
|
|||
static int bmc150_accel_set_interrupt(struct bmc150_accel_data *data, int i,
|
||||
bool state)
|
||||
{
|
||||
struct device *dev = regmap_get_device(data->regmap);
|
||||
struct bmc150_accel_interrupt *intr = &data->interrupts[i];
|
||||
const struct bmc150_accel_interrupt_info *info = intr->info;
|
||||
int ret;
|
||||
|
@ -474,7 +478,7 @@ static int bmc150_accel_set_interrupt(struct bmc150_accel_data *data, int i,
|
|||
ret = regmap_update_bits(data->regmap, info->map_reg, info->map_bitmask,
|
||||
(state ? info->map_bitmask : 0));
|
||||
if (ret < 0) {
|
||||
dev_err(data->dev, "Error updating reg_int_map\n");
|
||||
dev_err(dev, "Error updating reg_int_map\n");
|
||||
goto out_fix_power_state;
|
||||
}
|
||||
|
||||
|
@ -482,7 +486,7 @@ static int bmc150_accel_set_interrupt(struct bmc150_accel_data *data, int i,
|
|||
ret = regmap_update_bits(data->regmap, info->en_reg, info->en_bitmask,
|
||||
(state ? info->en_bitmask : 0));
|
||||
if (ret < 0) {
|
||||
dev_err(data->dev, "Error updating reg_int_en\n");
|
||||
dev_err(dev, "Error updating reg_int_en\n");
|
||||
goto out_fix_power_state;
|
||||
}
|
||||
|
||||
|
@ -500,6 +504,7 @@ static int bmc150_accel_set_interrupt(struct bmc150_accel_data *data, int i,
|
|||
|
||||
static int bmc150_accel_set_scale(struct bmc150_accel_data *data, int val)
|
||||
{
|
||||
struct device *dev = regmap_get_device(data->regmap);
|
||||
int ret, i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(data->chip_info->scale_table); ++i) {
|
||||
|
@ -508,8 +513,7 @@ static int bmc150_accel_set_scale(struct bmc150_accel_data *data, int val)
|
|||
BMC150_ACCEL_REG_PMU_RANGE,
|
||||
data->chip_info->scale_table[i].reg_range);
|
||||
if (ret < 0) {
|
||||
dev_err(data->dev,
|
||||
"Error writing pmu_range\n");
|
||||
dev_err(dev, "Error writing pmu_range\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -523,6 +527,7 @@ static int bmc150_accel_set_scale(struct bmc150_accel_data *data, int val)
|
|||
|
||||
static int bmc150_accel_get_temp(struct bmc150_accel_data *data, int *val)
|
||||
{
|
||||
struct device *dev = regmap_get_device(data->regmap);
|
||||
int ret;
|
||||
unsigned int value;
|
||||
|
||||
|
@ -530,7 +535,7 @@ static int bmc150_accel_get_temp(struct bmc150_accel_data *data, int *val)
|
|||
|
||||
ret = regmap_read(data->regmap, BMC150_ACCEL_REG_TEMP, &value);
|
||||
if (ret < 0) {
|
||||
dev_err(data->dev, "Error reading reg_temp\n");
|
||||
dev_err(dev, "Error reading reg_temp\n");
|
||||
mutex_unlock(&data->mutex);
|
||||
return ret;
|
||||
}
|
||||
|
@ -545,6 +550,7 @@ static int bmc150_accel_get_axis(struct bmc150_accel_data *data,
|
|||
struct iio_chan_spec const *chan,
|
||||
int *val)
|
||||
{
|
||||
struct device *dev = regmap_get_device(data->regmap);
|
||||
int ret;
|
||||
int axis = chan->scan_index;
|
||||
__le16 raw_val;
|
||||
|
@ -559,7 +565,7 @@ static int bmc150_accel_get_axis(struct bmc150_accel_data *data,
|
|||
ret = regmap_bulk_read(data->regmap, BMC150_ACCEL_AXIS_TO_REG(axis),
|
||||
&raw_val, sizeof(raw_val));
|
||||
if (ret < 0) {
|
||||
dev_err(data->dev, "Error reading axis %d\n", axis);
|
||||
dev_err(dev, "Error reading axis %d\n", axis);
|
||||
bmc150_accel_set_power_state(data, false);
|
||||
mutex_unlock(&data->mutex);
|
||||
return ret;
|
||||
|
@ -831,6 +837,7 @@ static int bmc150_accel_set_watermark(struct iio_dev *indio_dev, unsigned val)
|
|||
static int bmc150_accel_fifo_transfer(struct bmc150_accel_data *data,
|
||||
char *buffer, int samples)
|
||||
{
|
||||
struct device *dev = regmap_get_device(data->regmap);
|
||||
int sample_length = 3 * 2;
|
||||
int ret;
|
||||
int total_length = samples * sample_length;
|
||||
|
@ -854,7 +861,8 @@ static int bmc150_accel_fifo_transfer(struct bmc150_accel_data *data,
|
|||
}
|
||||
|
||||
if (ret)
|
||||
dev_err(data->dev, "Error transferring data from fifo in single steps of %zu\n",
|
||||
dev_err(dev,
|
||||
"Error transferring data from fifo in single steps of %zu\n",
|
||||
step);
|
||||
|
||||
return ret;
|
||||
|
@ -864,6 +872,7 @@ static int __bmc150_accel_fifo_flush(struct iio_dev *indio_dev,
|
|||
unsigned samples, bool irq)
|
||||
{
|
||||
struct bmc150_accel_data *data = iio_priv(indio_dev);
|
||||
struct device *dev = regmap_get_device(data->regmap);
|
||||
int ret, i;
|
||||
u8 count;
|
||||
u16 buffer[BMC150_ACCEL_FIFO_LENGTH * 3];
|
||||
|
@ -873,7 +882,7 @@ static int __bmc150_accel_fifo_flush(struct iio_dev *indio_dev,
|
|||
|
||||
ret = regmap_read(data->regmap, BMC150_ACCEL_REG_FIFO_STATUS, &val);
|
||||
if (ret < 0) {
|
||||
dev_err(data->dev, "Error reading reg_fifo_status\n");
|
||||
dev_err(dev, "Error reading reg_fifo_status\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -1105,27 +1114,23 @@ static const struct iio_info bmc150_accel_info_fifo = {
|
|||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static const unsigned long bmc150_accel_scan_masks[] = {
|
||||
BIT(AXIS_X) | BIT(AXIS_Y) | BIT(AXIS_Z),
|
||||
0};
|
||||
|
||||
static irqreturn_t bmc150_accel_trigger_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct bmc150_accel_data *data = iio_priv(indio_dev);
|
||||
int bit, ret, i = 0;
|
||||
unsigned int raw_val;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&data->mutex);
|
||||
for_each_set_bit(bit, indio_dev->active_scan_mask,
|
||||
indio_dev->masklength) {
|
||||
ret = regmap_bulk_read(data->regmap,
|
||||
BMC150_ACCEL_AXIS_TO_REG(bit), &raw_val,
|
||||
2);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&data->mutex);
|
||||
goto err_read;
|
||||
}
|
||||
data->buffer[i++] = raw_val;
|
||||
}
|
||||
ret = regmap_bulk_read(data->regmap, BMC150_ACCEL_REG_XOUT_L,
|
||||
data->buffer, AXIS_MAX * 2);
|
||||
mutex_unlock(&data->mutex);
|
||||
if (ret < 0)
|
||||
goto err_read;
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
|
||||
pf->timestamp);
|
||||
|
@ -1139,6 +1144,7 @@ static int bmc150_accel_trig_try_reen(struct iio_trigger *trig)
|
|||
{
|
||||
struct bmc150_accel_trigger *t = iio_trigger_get_drvdata(trig);
|
||||
struct bmc150_accel_data *data = t->data;
|
||||
struct device *dev = regmap_get_device(data->regmap);
|
||||
int ret;
|
||||
|
||||
/* new data interrupts don't need ack */
|
||||
|
@ -1152,8 +1158,7 @@ static int bmc150_accel_trig_try_reen(struct iio_trigger *trig)
|
|||
BMC150_ACCEL_INT_MODE_LATCH_RESET);
|
||||
mutex_unlock(&data->mutex);
|
||||
if (ret < 0) {
|
||||
dev_err(data->dev,
|
||||
"Error writing reg_int_rst_latch\n");
|
||||
dev_err(dev, "Error writing reg_int_rst_latch\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -1204,13 +1209,14 @@ static const struct iio_trigger_ops bmc150_accel_trigger_ops = {
|
|||
static int bmc150_accel_handle_roc_event(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct bmc150_accel_data *data = iio_priv(indio_dev);
|
||||
struct device *dev = regmap_get_device(data->regmap);
|
||||
int dir;
|
||||
int ret;
|
||||
unsigned int val;
|
||||
|
||||
ret = regmap_read(data->regmap, BMC150_ACCEL_REG_INT_STATUS_2, &val);
|
||||
if (ret < 0) {
|
||||
dev_err(data->dev, "Error reading reg_int_status_2\n");
|
||||
dev_err(dev, "Error reading reg_int_status_2\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -1253,6 +1259,7 @@ static irqreturn_t bmc150_accel_irq_thread_handler(int irq, void *private)
|
|||
{
|
||||
struct iio_dev *indio_dev = private;
|
||||
struct bmc150_accel_data *data = iio_priv(indio_dev);
|
||||
struct device *dev = regmap_get_device(data->regmap);
|
||||
bool ack = false;
|
||||
int ret;
|
||||
|
||||
|
@ -1276,7 +1283,7 @@ static irqreturn_t bmc150_accel_irq_thread_handler(int irq, void *private)
|
|||
BMC150_ACCEL_INT_MODE_LATCH_INT |
|
||||
BMC150_ACCEL_INT_MODE_LATCH_RESET);
|
||||
if (ret)
|
||||
dev_err(data->dev, "Error writing reg_int_rst_latch\n");
|
||||
dev_err(dev, "Error writing reg_int_rst_latch\n");
|
||||
|
||||
ret = IRQ_HANDLED;
|
||||
} else {
|
||||
|
@ -1347,13 +1354,14 @@ static void bmc150_accel_unregister_triggers(struct bmc150_accel_data *data,
|
|||
static int bmc150_accel_triggers_setup(struct iio_dev *indio_dev,
|
||||
struct bmc150_accel_data *data)
|
||||
{
|
||||
struct device *dev = regmap_get_device(data->regmap);
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < BMC150_ACCEL_TRIGGERS; i++) {
|
||||
struct bmc150_accel_trigger *t = &data->triggers[i];
|
||||
|
||||
t->indio_trig = devm_iio_trigger_alloc(data->dev,
|
||||
bmc150_accel_triggers[i].name,
|
||||
t->indio_trig = devm_iio_trigger_alloc(dev,
|
||||
bmc150_accel_triggers[i].name,
|
||||
indio_dev->name,
|
||||
indio_dev->id);
|
||||
if (!t->indio_trig) {
|
||||
|
@ -1361,7 +1369,7 @@ static int bmc150_accel_triggers_setup(struct iio_dev *indio_dev,
|
|||
break;
|
||||
}
|
||||
|
||||
t->indio_trig->dev.parent = data->dev;
|
||||
t->indio_trig->dev.parent = dev;
|
||||
t->indio_trig->ops = &bmc150_accel_trigger_ops;
|
||||
t->intr = bmc150_accel_triggers[i].intr;
|
||||
t->data = data;
|
||||
|
@ -1385,12 +1393,13 @@ static int bmc150_accel_triggers_setup(struct iio_dev *indio_dev,
|
|||
|
||||
static int bmc150_accel_fifo_set_mode(struct bmc150_accel_data *data)
|
||||
{
|
||||
struct device *dev = regmap_get_device(data->regmap);
|
||||
u8 reg = BMC150_ACCEL_REG_FIFO_CONFIG1;
|
||||
int ret;
|
||||
|
||||
ret = regmap_write(data->regmap, reg, data->fifo_mode);
|
||||
if (ret < 0) {
|
||||
dev_err(data->dev, "Error writing reg_fifo_config1\n");
|
||||
dev_err(dev, "Error writing reg_fifo_config1\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -1400,7 +1409,7 @@ static int bmc150_accel_fifo_set_mode(struct bmc150_accel_data *data)
|
|||
ret = regmap_write(data->regmap, BMC150_ACCEL_REG_FIFO_CONFIG0,
|
||||
data->watermark);
|
||||
if (ret < 0)
|
||||
dev_err(data->dev, "Error writing reg_fifo_config0\n");
|
||||
dev_err(dev, "Error writing reg_fifo_config0\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -1484,17 +1493,17 @@ static const struct iio_buffer_setup_ops bmc150_accel_buffer_ops = {
|
|||
|
||||
static int bmc150_accel_chip_init(struct bmc150_accel_data *data)
|
||||
{
|
||||
struct device *dev = regmap_get_device(data->regmap);
|
||||
int ret, i;
|
||||
unsigned int val;
|
||||
|
||||
ret = regmap_read(data->regmap, BMC150_ACCEL_REG_CHIP_ID, &val);
|
||||
if (ret < 0) {
|
||||
dev_err(data->dev,
|
||||
"Error: Reading chip id\n");
|
||||
dev_err(dev, "Error: Reading chip id\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_dbg(data->dev, "Chip Id %x\n", val);
|
||||
dev_dbg(dev, "Chip Id %x\n", val);
|
||||
for (i = 0; i < ARRAY_SIZE(bmc150_accel_chip_info_tbl); i++) {
|
||||
if (bmc150_accel_chip_info_tbl[i].chip_id == val) {
|
||||
data->chip_info = &bmc150_accel_chip_info_tbl[i];
|
||||
|
@ -1503,7 +1512,7 @@ static int bmc150_accel_chip_init(struct bmc150_accel_data *data)
|
|||
}
|
||||
|
||||
if (!data->chip_info) {
|
||||
dev_err(data->dev, "Invalid chip %x\n", val);
|
||||
dev_err(dev, "Invalid chip %x\n", val);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
|
@ -1520,8 +1529,7 @@ static int bmc150_accel_chip_init(struct bmc150_accel_data *data)
|
|||
ret = regmap_write(data->regmap, BMC150_ACCEL_REG_PMU_RANGE,
|
||||
BMC150_ACCEL_DEF_RANGE_4G);
|
||||
if (ret < 0) {
|
||||
dev_err(data->dev,
|
||||
"Error writing reg_pmu_range\n");
|
||||
dev_err(dev, "Error writing reg_pmu_range\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -1539,8 +1547,7 @@ static int bmc150_accel_chip_init(struct bmc150_accel_data *data)
|
|||
BMC150_ACCEL_INT_MODE_LATCH_INT |
|
||||
BMC150_ACCEL_INT_MODE_LATCH_RESET);
|
||||
if (ret < 0) {
|
||||
dev_err(data->dev,
|
||||
"Error writing reg_int_rst_latch\n");
|
||||
dev_err(dev, "Error writing reg_int_rst_latch\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -1560,7 +1567,6 @@ int bmc150_accel_core_probe(struct device *dev, struct regmap *regmap, int irq,
|
|||
|
||||
data = iio_priv(indio_dev);
|
||||
dev_set_drvdata(dev, indio_dev);
|
||||
data->dev = dev;
|
||||
data->irq = irq;
|
||||
|
||||
data->regmap = regmap;
|
||||
|
@ -1575,6 +1581,7 @@ int bmc150_accel_core_probe(struct device *dev, struct regmap *regmap, int irq,
|
|||
indio_dev->channels = data->chip_info->channels;
|
||||
indio_dev->num_channels = data->chip_info->num_channels;
|
||||
indio_dev->name = name ? name : data->chip_info->name;
|
||||
indio_dev->available_scan_masks = bmc150_accel_scan_masks;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &bmc150_accel_info;
|
||||
|
||||
|
@ -1583,13 +1590,13 @@ int bmc150_accel_core_probe(struct device *dev, struct regmap *regmap, int irq,
|
|||
bmc150_accel_trigger_handler,
|
||||
&bmc150_accel_buffer_ops);
|
||||
if (ret < 0) {
|
||||
dev_err(data->dev, "Failed: iio triggered buffer setup\n");
|
||||
dev_err(dev, "Failed: iio triggered buffer setup\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (data->irq > 0) {
|
||||
ret = devm_request_threaded_irq(
|
||||
data->dev, data->irq,
|
||||
dev, data->irq,
|
||||
bmc150_accel_irq_handler,
|
||||
bmc150_accel_irq_thread_handler,
|
||||
IRQF_TRIGGER_RISING,
|
||||
|
@ -1607,7 +1614,7 @@ int bmc150_accel_core_probe(struct device *dev, struct regmap *regmap, int irq,
|
|||
ret = regmap_write(data->regmap, BMC150_ACCEL_REG_INT_RST_LATCH,
|
||||
BMC150_ACCEL_INT_MODE_LATCH_RESET);
|
||||
if (ret < 0) {
|
||||
dev_err(data->dev, "Error writing reg_int_rst_latch\n");
|
||||
dev_err(dev, "Error writing reg_int_rst_latch\n");
|
||||
goto err_buffer_cleanup;
|
||||
}
|
||||
|
||||
|
@ -1656,9 +1663,9 @@ int bmc150_accel_core_remove(struct device *dev)
|
|||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
pm_runtime_disable(data->dev);
|
||||
pm_runtime_set_suspended(data->dev);
|
||||
pm_runtime_put_noidle(data->dev);
|
||||
pm_runtime_disable(dev);
|
||||
pm_runtime_set_suspended(dev);
|
||||
pm_runtime_put_noidle(dev);
|
||||
|
||||
bmc150_accel_unregister_triggers(data, BMC150_ACCEL_TRIGGERS - 1);
|
||||
|
||||
|
@ -1707,7 +1714,7 @@ static int bmc150_accel_runtime_suspend(struct device *dev)
|
|||
struct bmc150_accel_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
dev_dbg(data->dev, __func__);
|
||||
dev_dbg(dev, __func__);
|
||||
ret = bmc150_accel_set_mode(data, BMC150_ACCEL_SLEEP_MODE_SUSPEND, 0);
|
||||
if (ret < 0)
|
||||
return -EAGAIN;
|
||||
|
@ -1722,7 +1729,7 @@ static int bmc150_accel_runtime_resume(struct device *dev)
|
|||
int ret;
|
||||
int sleep_val;
|
||||
|
||||
dev_dbg(data->dev, __func__);
|
||||
dev_dbg(dev, __func__);
|
||||
|
||||
ret = bmc150_accel_set_mode(data, BMC150_ACCEL_SLEEP_MODE_NORMAL, 0);
|
||||
if (ret < 0)
|
||||
|
|
|
@ -28,11 +28,6 @@
|
|||
|
||||
#include "bmc150-accel.h"
|
||||
|
||||
static const struct regmap_config bmc150_i2c_regmap_conf = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
};
|
||||
|
||||
static int bmc150_accel_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
|
@ -43,7 +38,7 @@ static int bmc150_accel_probe(struct i2c_client *client,
|
|||
i2c_check_functionality(client->adapter,
|
||||
I2C_FUNC_SMBUS_READ_I2C_BLOCK);
|
||||
|
||||
regmap = devm_regmap_init_i2c(client, &bmc150_i2c_regmap_conf);
|
||||
regmap = devm_regmap_init_i2c(client, &bmc150_regmap_conf);
|
||||
if (IS_ERR(regmap)) {
|
||||
dev_err(&client->dev, "Failed to initialize i2c regmap\n");
|
||||
return PTR_ERR(regmap);
|
||||
|
|
|
@ -25,18 +25,12 @@
|
|||
|
||||
#include "bmc150-accel.h"
|
||||
|
||||
static const struct regmap_config bmc150_spi_regmap_conf = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.max_register = 0x3f,
|
||||
};
|
||||
|
||||
static int bmc150_accel_probe(struct spi_device *spi)
|
||||
{
|
||||
struct regmap *regmap;
|
||||
const struct spi_device_id *id = spi_get_device_id(spi);
|
||||
|
||||
regmap = devm_regmap_init_spi(spi, &bmc150_spi_regmap_conf);
|
||||
regmap = devm_regmap_init_spi(spi, &bmc150_regmap_conf);
|
||||
if (IS_ERR(regmap)) {
|
||||
dev_err(&spi->dev, "Failed to initialize spi regmap\n");
|
||||
return PTR_ERR(regmap);
|
||||
|
|
|
@ -16,5 +16,6 @@ int bmc150_accel_core_probe(struct device *dev, struct regmap *regmap, int irq,
|
|||
const char *name, bool block_supported);
|
||||
int bmc150_accel_core_remove(struct device *dev);
|
||||
extern const struct dev_pm_ops bmc150_accel_pm_ops;
|
||||
extern const struct regmap_config bmc150_regmap_conf;
|
||||
|
||||
#endif /* _BMC150_ACCEL_H_ */
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/iio/iio.h>
|
||||
|
@ -115,6 +114,7 @@ enum kxcjk1013_axis {
|
|||
AXIS_X,
|
||||
AXIS_Y,
|
||||
AXIS_Z,
|
||||
AXIS_MAX,
|
||||
};
|
||||
|
||||
enum kxcjk1013_mode {
|
||||
|
@ -922,7 +922,7 @@ static const struct iio_event_spec kxcjk1013_event = {
|
|||
.realbits = 12, \
|
||||
.storagebits = 16, \
|
||||
.shift = 4, \
|
||||
.endianness = IIO_CPU, \
|
||||
.endianness = IIO_LE, \
|
||||
}, \
|
||||
.event_spec = &kxcjk1013_event, \
|
||||
.num_event_specs = 1 \
|
||||
|
@ -953,25 +953,23 @@ static const struct iio_info kxcjk1013_info = {
|
|||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static const unsigned long kxcjk1013_scan_masks[] = {0x7, 0};
|
||||
|
||||
static irqreturn_t kxcjk1013_trigger_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct kxcjk1013_data *data = iio_priv(indio_dev);
|
||||
int bit, ret, i = 0;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&data->mutex);
|
||||
|
||||
for_each_set_bit(bit, indio_dev->active_scan_mask,
|
||||
indio_dev->masklength) {
|
||||
ret = kxcjk1013_get_acc_reg(data, bit);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&data->mutex);
|
||||
goto err;
|
||||
}
|
||||
data->buffer[i++] = ret;
|
||||
}
|
||||
ret = i2c_smbus_read_i2c_block_data_or_emulated(data->client,
|
||||
KXCJK1013_REG_XOUT_L,
|
||||
AXIS_MAX * 2,
|
||||
(u8 *)data->buffer);
|
||||
mutex_unlock(&data->mutex);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
|
||||
data->timestamp);
|
||||
|
@ -1204,6 +1202,7 @@ static int kxcjk1013_probe(struct i2c_client *client,
|
|||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->channels = kxcjk1013_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(kxcjk1013_channels);
|
||||
indio_dev->available_scan_masks = kxcjk1013_scan_masks;
|
||||
indio_dev->name = name;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &kxcjk1013_info;
|
||||
|
|
|
@ -55,11 +55,11 @@
|
|||
|
||||
struct mma7455_data {
|
||||
struct regmap *regmap;
|
||||
struct device *dev;
|
||||
};
|
||||
|
||||
static int mma7455_drdy(struct mma7455_data *mma7455)
|
||||
{
|
||||
struct device *dev = regmap_get_device(mma7455->regmap);
|
||||
unsigned int reg;
|
||||
int tries = 3;
|
||||
int ret;
|
||||
|
@ -75,7 +75,7 @@ static int mma7455_drdy(struct mma7455_data *mma7455)
|
|||
msleep(20);
|
||||
}
|
||||
|
||||
dev_warn(mma7455->dev, "data not ready\n");
|
||||
dev_warn(dev, "data not ready\n");
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
|
@ -260,7 +260,6 @@ int mma7455_core_probe(struct device *dev, struct regmap *regmap,
|
|||
dev_set_drvdata(dev, indio_dev);
|
||||
mma7455 = iio_priv(indio_dev);
|
||||
mma7455->regmap = regmap;
|
||||
mma7455->dev = dev;
|
||||
|
||||
indio_dev->info = &mma7455_info;
|
||||
indio_dev->name = name;
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
* MMA8453Q (10 bit)
|
||||
* MMA8652FC (12 bit)
|
||||
* MMA8653FC (10 bit)
|
||||
* FXLS8471Q (14 bit)
|
||||
*
|
||||
* Copyright 2015 Martin Kepplinger <martin.kepplinger@theobroma-systems.com>
|
||||
* Copyright 2014 Peter Meerwald <pmeerw@pmeerw.net>
|
||||
|
@ -16,7 +17,7 @@
|
|||
*
|
||||
* 7-bit I2C slave address 0x1c/0x1d (pin selectable)
|
||||
*
|
||||
* TODO: orientation events, autosleep
|
||||
* TODO: orientation events
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
|
@ -31,6 +32,7 @@
|
|||
#include <linux/delay.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#define MMA8452_STATUS 0x00
|
||||
#define MMA8452_STATUS_DRDY (BIT(2) | BIT(1) | BIT(0))
|
||||
|
@ -91,6 +93,9 @@
|
|||
#define MMA8453_DEVICE_ID 0x3a
|
||||
#define MMA8652_DEVICE_ID 0x4a
|
||||
#define MMA8653_DEVICE_ID 0x5a
|
||||
#define FXLS8471_DEVICE_ID 0x6a
|
||||
|
||||
#define MMA8452_AUTO_SUSPEND_DELAY_MS 2000
|
||||
|
||||
struct mma8452_data {
|
||||
struct i2c_client *client;
|
||||
|
@ -172,6 +177,31 @@ static int mma8452_drdy(struct mma8452_data *data)
|
|||
return -EIO;
|
||||
}
|
||||
|
||||
static int mma8452_set_runtime_pm_state(struct i2c_client *client, bool on)
|
||||
{
|
||||
#ifdef CONFIG_PM
|
||||
int ret;
|
||||
|
||||
if (on) {
|
||||
ret = pm_runtime_get_sync(&client->dev);
|
||||
} else {
|
||||
pm_runtime_mark_last_busy(&client->dev);
|
||||
ret = pm_runtime_put_autosuspend(&client->dev);
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev,
|
||||
"failed to change power state to %d\n", on);
|
||||
if (on)
|
||||
pm_runtime_put_noidle(&client->dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mma8452_read(struct mma8452_data *data, __be16 buf[3])
|
||||
{
|
||||
int ret = mma8452_drdy(data);
|
||||
|
@ -179,8 +209,16 @@ static int mma8452_read(struct mma8452_data *data, __be16 buf[3])
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return i2c_smbus_read_i2c_block_data(data->client, MMA8452_OUT_X,
|
||||
3 * sizeof(__be16), (u8 *)buf);
|
||||
ret = mma8452_set_runtime_pm_state(data->client, true);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = i2c_smbus_read_i2c_block_data(data->client, MMA8452_OUT_X,
|
||||
3 * sizeof(__be16), (u8 *)buf);
|
||||
|
||||
ret = mma8452_set_runtime_pm_state(data->client, false);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t mma8452_show_int_plus_micros(char *buf, const int (*vals)[2],
|
||||
|
@ -357,7 +395,8 @@ static int mma8452_read_raw(struct iio_dev *indio_dev,
|
|||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
ret = i2c_smbus_read_byte_data(data->client,
|
||||
MMA8452_OFF_X + chan->scan_index);
|
||||
MMA8452_OFF_X +
|
||||
chan->scan_index);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
@ -392,24 +431,47 @@ static int mma8452_active(struct mma8452_data *data)
|
|||
data->ctrl_reg1);
|
||||
}
|
||||
|
||||
/* returns >0 if active, 0 if in standby and <0 on error */
|
||||
static int mma8452_is_active(struct mma8452_data *data)
|
||||
{
|
||||
int reg;
|
||||
|
||||
reg = i2c_smbus_read_byte_data(data->client, MMA8452_CTRL_REG1);
|
||||
if (reg < 0)
|
||||
return reg;
|
||||
|
||||
return reg & MMA8452_CTRL_ACTIVE;
|
||||
}
|
||||
|
||||
static int mma8452_change_config(struct mma8452_data *data, u8 reg, u8 val)
|
||||
{
|
||||
int ret;
|
||||
int is_active;
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
|
||||
/* config can only be changed when in standby */
|
||||
ret = mma8452_standby(data);
|
||||
if (ret < 0)
|
||||
is_active = mma8452_is_active(data);
|
||||
if (is_active < 0) {
|
||||
ret = is_active;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* config can only be changed when in standby */
|
||||
if (is_active > 0) {
|
||||
ret = mma8452_standby(data);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = i2c_smbus_write_byte_data(data->client, reg, val);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
|
||||
ret = mma8452_active(data);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
if (is_active > 0) {
|
||||
ret = mma8452_active(data);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
fail:
|
||||
|
@ -418,7 +480,7 @@ static int mma8452_change_config(struct mma8452_data *data, u8 reg, u8 val)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* returns >0 if in freefall mode, 0 if not or <0 if an error occured */
|
||||
/* returns >0 if in freefall mode, 0 if not or <0 if an error occurred */
|
||||
static int mma8452_freefall_mode_enabled(struct mma8452_data *data)
|
||||
{
|
||||
int val;
|
||||
|
@ -668,7 +730,8 @@ static int mma8452_read_event_config(struct iio_dev *indio_dev,
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return !!(ret & BIT(chan->scan_index + chip->ev_cfg_chan_shift));
|
||||
return !!(ret & BIT(chan->scan_index +
|
||||
chip->ev_cfg_chan_shift));
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -682,7 +745,11 @@ static int mma8452_write_event_config(struct iio_dev *indio_dev,
|
|||
{
|
||||
struct mma8452_data *data = iio_priv(indio_dev);
|
||||
const struct mma_chip_info *chip = data->chip_info;
|
||||
int val;
|
||||
int val, ret;
|
||||
|
||||
ret = mma8452_set_runtime_pm_state(data->client, state);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
switch (dir) {
|
||||
case IIO_EV_DIR_FALLING:
|
||||
|
@ -990,6 +1057,7 @@ enum {
|
|||
mma8453,
|
||||
mma8652,
|
||||
mma8653,
|
||||
fxls8471,
|
||||
};
|
||||
|
||||
static const struct mma_chip_info mma_chip_info_table[] = {
|
||||
|
@ -1003,7 +1071,7 @@ static const struct mma_chip_info mma_chip_info_table[] = {
|
|||
* bit.
|
||||
* The userspace interface uses m/s^2 and we declare micro units
|
||||
* So scale factor for 12 bit here is given by:
|
||||
* g * N * 1000000 / 2048 for N = 2, 4, 8 and g=9.80665
|
||||
* g * N * 1000000 / 2048 for N = 2, 4, 8 and g=9.80665
|
||||
*/
|
||||
.mma_scales = { {0, 2394}, {0, 4788}, {0, 9577} },
|
||||
.ev_cfg = MMA8452_TRANSIENT_CFG,
|
||||
|
@ -1081,6 +1149,22 @@ static const struct mma_chip_info mma_chip_info_table[] = {
|
|||
.ev_ths_mask = MMA8452_FF_MT_THS_MASK,
|
||||
.ev_count = MMA8452_FF_MT_COUNT,
|
||||
},
|
||||
[fxls8471] = {
|
||||
.chip_id = FXLS8471_DEVICE_ID,
|
||||
.channels = mma8451_channels,
|
||||
.num_channels = ARRAY_SIZE(mma8451_channels),
|
||||
.mma_scales = { {0, 2394}, {0, 4788}, {0, 9577} },
|
||||
.ev_cfg = MMA8452_TRANSIENT_CFG,
|
||||
.ev_cfg_ele = MMA8452_TRANSIENT_CFG_ELE,
|
||||
.ev_cfg_chan_shift = 1,
|
||||
.ev_src = MMA8452_TRANSIENT_SRC,
|
||||
.ev_src_xe = MMA8452_TRANSIENT_SRC_XTRANSE,
|
||||
.ev_src_ye = MMA8452_TRANSIENT_SRC_YTRANSE,
|
||||
.ev_src_ze = MMA8452_TRANSIENT_SRC_ZTRANSE,
|
||||
.ev_ths = MMA8452_TRANSIENT_THS,
|
||||
.ev_ths_mask = MMA8452_TRANSIENT_THS_MASK,
|
||||
.ev_count = MMA8452_TRANSIENT_COUNT,
|
||||
},
|
||||
};
|
||||
|
||||
static struct attribute *mma8452_attributes[] = {
|
||||
|
@ -1114,7 +1198,11 @@ static int mma8452_data_rdy_trigger_set_state(struct iio_trigger *trig,
|
|||
{
|
||||
struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
|
||||
struct mma8452_data *data = iio_priv(indio_dev);
|
||||
int reg;
|
||||
int reg, ret;
|
||||
|
||||
ret = mma8452_set_runtime_pm_state(data->client, state);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
reg = i2c_smbus_read_byte_data(data->client, MMA8452_CTRL_REG4);
|
||||
if (reg < 0)
|
||||
|
@ -1206,6 +1294,7 @@ static const struct of_device_id mma8452_dt_ids[] = {
|
|||
{ .compatible = "fsl,mma8453", .data = &mma_chip_info_table[mma8453] },
|
||||
{ .compatible = "fsl,mma8652", .data = &mma_chip_info_table[mma8652] },
|
||||
{ .compatible = "fsl,mma8653", .data = &mma_chip_info_table[mma8653] },
|
||||
{ .compatible = "fsl,fxls8471", .data = &mma_chip_info_table[fxls8471] },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mma8452_dt_ids);
|
||||
|
@ -1243,6 +1332,7 @@ static int mma8452_probe(struct i2c_client *client,
|
|||
case MMA8453_DEVICE_ID:
|
||||
case MMA8652_DEVICE_ID:
|
||||
case MMA8653_DEVICE_ID:
|
||||
case FXLS8471_DEVICE_ID:
|
||||
if (ret == data->chip_info->chip_id)
|
||||
break;
|
||||
default:
|
||||
|
@ -1340,6 +1430,15 @@ static int mma8452_probe(struct i2c_client *client,
|
|||
goto buffer_cleanup;
|
||||
}
|
||||
|
||||
ret = pm_runtime_set_active(&client->dev);
|
||||
if (ret < 0)
|
||||
goto buffer_cleanup;
|
||||
|
||||
pm_runtime_enable(&client->dev);
|
||||
pm_runtime_set_autosuspend_delay(&client->dev,
|
||||
MMA8452_AUTO_SUSPEND_DELAY_MS);
|
||||
pm_runtime_use_autosuspend(&client->dev);
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret < 0)
|
||||
goto buffer_cleanup;
|
||||
|
@ -1364,6 +1463,11 @@ static int mma8452_remove(struct i2c_client *client)
|
|||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
pm_runtime_disable(&client->dev);
|
||||
pm_runtime_set_suspended(&client->dev);
|
||||
pm_runtime_put_noidle(&client->dev);
|
||||
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
mma8452_trigger_cleanup(indio_dev);
|
||||
mma8452_standby(iio_priv(indio_dev));
|
||||
|
@ -1371,6 +1475,45 @@ static int mma8452_remove(struct i2c_client *client)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int mma8452_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
|
||||
struct mma8452_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
ret = mma8452_standby(data);
|
||||
mutex_unlock(&data->lock);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev, "powering off device failed\n");
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mma8452_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
|
||||
struct mma8452_data *data = iio_priv(indio_dev);
|
||||
int ret, sleep_val;
|
||||
|
||||
ret = mma8452_active(data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = mma8452_get_odr_index(data);
|
||||
sleep_val = 1000 / mma8452_samp_freq[ret][0];
|
||||
if (sleep_val < 20)
|
||||
usleep_range(sleep_val * 1000, 20000);
|
||||
else
|
||||
msleep_interruptible(sleep_val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int mma8452_suspend(struct device *dev)
|
||||
{
|
||||
|
@ -1383,18 +1526,21 @@ static int mma8452_resume(struct device *dev)
|
|||
return mma8452_active(iio_priv(i2c_get_clientdata(
|
||||
to_i2c_client(dev))));
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(mma8452_pm_ops, mma8452_suspend, mma8452_resume);
|
||||
#define MMA8452_PM_OPS (&mma8452_pm_ops)
|
||||
#else
|
||||
#define MMA8452_PM_OPS NULL
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops mma8452_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(mma8452_suspend, mma8452_resume)
|
||||
SET_RUNTIME_PM_OPS(mma8452_runtime_suspend,
|
||||
mma8452_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
static const struct i2c_device_id mma8452_id[] = {
|
||||
{ "mma8451", mma8451 },
|
||||
{ "mma8452", mma8452 },
|
||||
{ "mma8453", mma8453 },
|
||||
{ "mma8652", mma8652 },
|
||||
{ "mma8653", mma8653 },
|
||||
{ "fxls8471", fxls8471 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, mma8452_id);
|
||||
|
@ -1403,7 +1549,7 @@ static struct i2c_driver mma8452_driver = {
|
|||
.driver = {
|
||||
.name = "mma8452",
|
||||
.of_match_table = of_match_ptr(mma8452_dt_ids),
|
||||
.pm = MMA8452_PM_OPS,
|
||||
.pm = &mma8452_pm_ops,
|
||||
},
|
||||
.probe = mma8452_probe,
|
||||
.remove = mma8452_remove,
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
#include <linux/interrupt.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/events.h>
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
#include <linux/i2c.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
|
@ -380,31 +379,6 @@ static const struct iio_trigger_ops mxc4005_trigger_ops = {
|
|||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int mxc4005_gpio_probe(struct i2c_client *client,
|
||||
struct mxc4005_data *data)
|
||||
{
|
||||
struct device *dev;
|
||||
struct gpio_desc *gpio;
|
||||
int ret;
|
||||
|
||||
if (!client)
|
||||
return -EINVAL;
|
||||
|
||||
dev = &client->dev;
|
||||
|
||||
gpio = devm_gpiod_get_index(dev, "mxc4005_int", 0, GPIOD_IN);
|
||||
if (IS_ERR(gpio)) {
|
||||
dev_err(dev, "failed to get acpi gpio index\n");
|
||||
return PTR_ERR(gpio);
|
||||
}
|
||||
|
||||
ret = gpiod_to_irq(gpio);
|
||||
|
||||
dev_dbg(dev, "GPIO resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mxc4005_chip_init(struct mxc4005_data *data)
|
||||
{
|
||||
int ret;
|
||||
|
@ -470,9 +444,6 @@ static int mxc4005_probe(struct i2c_client *client,
|
|||
return ret;
|
||||
}
|
||||
|
||||
if (client->irq < 0)
|
||||
client->irq = mxc4005_gpio_probe(client, data);
|
||||
|
||||
if (client->irq > 0) {
|
||||
data->dready_trig = devm_iio_trigger_alloc(&client->dev,
|
||||
"%s-dev%d",
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <linux/types.h>
|
||||
#include <linux/iio/common/st_sensors.h>
|
||||
|
||||
#define H3LIS331DL_DRIVER_NAME "h3lis331dl_accel"
|
||||
#define LIS3LV02DL_ACCEL_DEV_NAME "lis3lv02dl_accel"
|
||||
#define LSM303DLHC_ACCEL_DEV_NAME "lsm303dlhc_accel"
|
||||
#define LIS3DH_ACCEL_DEV_NAME "lis3dh"
|
||||
|
|
|
@ -39,6 +39,9 @@
|
|||
#define ST_ACCEL_FS_AVL_6G 6
|
||||
#define ST_ACCEL_FS_AVL_8G 8
|
||||
#define ST_ACCEL_FS_AVL_16G 16
|
||||
#define ST_ACCEL_FS_AVL_100G 100
|
||||
#define ST_ACCEL_FS_AVL_200G 200
|
||||
#define ST_ACCEL_FS_AVL_400G 400
|
||||
|
||||
/* CUSTOM VALUES FOR SENSOR 1 */
|
||||
#define ST_ACCEL_1_WAI_EXP 0x33
|
||||
|
@ -96,6 +99,8 @@
|
|||
#define ST_ACCEL_2_DRDY_IRQ_INT2_MASK 0x10
|
||||
#define ST_ACCEL_2_IHL_IRQ_ADDR 0x22
|
||||
#define ST_ACCEL_2_IHL_IRQ_MASK 0x80
|
||||
#define ST_ACCEL_2_OD_IRQ_ADDR 0x22
|
||||
#define ST_ACCEL_2_OD_IRQ_MASK 0x40
|
||||
#define ST_ACCEL_2_MULTIREAD_BIT true
|
||||
|
||||
/* CUSTOM VALUES FOR SENSOR 3 */
|
||||
|
@ -177,10 +182,39 @@
|
|||
#define ST_ACCEL_5_DRDY_IRQ_INT2_MASK 0x20
|
||||
#define ST_ACCEL_5_IHL_IRQ_ADDR 0x22
|
||||
#define ST_ACCEL_5_IHL_IRQ_MASK 0x80
|
||||
#define ST_ACCEL_5_OD_IRQ_ADDR 0x22
|
||||
#define ST_ACCEL_5_OD_IRQ_MASK 0x40
|
||||
#define ST_ACCEL_5_IG1_EN_ADDR 0x21
|
||||
#define ST_ACCEL_5_IG1_EN_MASK 0x08
|
||||
#define ST_ACCEL_5_MULTIREAD_BIT false
|
||||
|
||||
/* CUSTOM VALUES FOR SENSOR 6 */
|
||||
#define ST_ACCEL_6_WAI_EXP 0x32
|
||||
#define ST_ACCEL_6_ODR_ADDR 0x20
|
||||
#define ST_ACCEL_6_ODR_MASK 0x18
|
||||
#define ST_ACCEL_6_ODR_AVL_50HZ_VAL 0x00
|
||||
#define ST_ACCEL_6_ODR_AVL_100HZ_VAL 0x01
|
||||
#define ST_ACCEL_6_ODR_AVL_400HZ_VAL 0x02
|
||||
#define ST_ACCEL_6_ODR_AVL_1000HZ_VAL 0x03
|
||||
#define ST_ACCEL_6_PW_ADDR 0x20
|
||||
#define ST_ACCEL_6_PW_MASK 0x20
|
||||
#define ST_ACCEL_6_FS_ADDR 0x23
|
||||
#define ST_ACCEL_6_FS_MASK 0x30
|
||||
#define ST_ACCEL_6_FS_AVL_100_VAL 0x00
|
||||
#define ST_ACCEL_6_FS_AVL_200_VAL 0x01
|
||||
#define ST_ACCEL_6_FS_AVL_400_VAL 0x03
|
||||
#define ST_ACCEL_6_FS_AVL_100_GAIN IIO_G_TO_M_S_2(49000)
|
||||
#define ST_ACCEL_6_FS_AVL_200_GAIN IIO_G_TO_M_S_2(98000)
|
||||
#define ST_ACCEL_6_FS_AVL_400_GAIN IIO_G_TO_M_S_2(195000)
|
||||
#define ST_ACCEL_6_BDU_ADDR 0x23
|
||||
#define ST_ACCEL_6_BDU_MASK 0x80
|
||||
#define ST_ACCEL_6_DRDY_IRQ_ADDR 0x22
|
||||
#define ST_ACCEL_6_DRDY_IRQ_INT1_MASK 0x02
|
||||
#define ST_ACCEL_6_DRDY_IRQ_INT2_MASK 0x10
|
||||
#define ST_ACCEL_6_IHL_IRQ_ADDR 0x22
|
||||
#define ST_ACCEL_6_IHL_IRQ_MASK 0x80
|
||||
#define ST_ACCEL_6_MULTIREAD_BIT true
|
||||
|
||||
static const struct iio_chan_spec st_accel_8bit_channels[] = {
|
||||
ST_SENSORS_LSM_CHANNELS(IIO_ACCEL,
|
||||
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
|
||||
|
@ -302,6 +336,7 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = {
|
|||
.mask_int2 = ST_ACCEL_1_DRDY_IRQ_INT2_MASK,
|
||||
.addr_ihl = ST_ACCEL_1_IHL_IRQ_ADDR,
|
||||
.mask_ihl = ST_ACCEL_1_IHL_IRQ_MASK,
|
||||
.addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR,
|
||||
},
|
||||
.multi_read_bit = ST_ACCEL_1_MULTIREAD_BIT,
|
||||
.bootime = 2,
|
||||
|
@ -367,6 +402,9 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = {
|
|||
.mask_int2 = ST_ACCEL_2_DRDY_IRQ_INT2_MASK,
|
||||
.addr_ihl = ST_ACCEL_2_IHL_IRQ_ADDR,
|
||||
.mask_ihl = ST_ACCEL_2_IHL_IRQ_MASK,
|
||||
.addr_od = ST_ACCEL_2_OD_IRQ_ADDR,
|
||||
.mask_od = ST_ACCEL_2_OD_IRQ_MASK,
|
||||
.addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR,
|
||||
},
|
||||
.multi_read_bit = ST_ACCEL_2_MULTIREAD_BIT,
|
||||
.bootime = 2,
|
||||
|
@ -444,6 +482,7 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = {
|
|||
.mask_int2 = ST_ACCEL_3_DRDY_IRQ_INT2_MASK,
|
||||
.addr_ihl = ST_ACCEL_3_IHL_IRQ_ADDR,
|
||||
.mask_ihl = ST_ACCEL_3_IHL_IRQ_MASK,
|
||||
.addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR,
|
||||
.ig1 = {
|
||||
.en_addr = ST_ACCEL_3_IG1_EN_ADDR,
|
||||
.en_mask = ST_ACCEL_3_IG1_EN_MASK,
|
||||
|
@ -502,6 +541,7 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = {
|
|||
.drdy_irq = {
|
||||
.addr = ST_ACCEL_4_DRDY_IRQ_ADDR,
|
||||
.mask_int1 = ST_ACCEL_4_DRDY_IRQ_INT1_MASK,
|
||||
.addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR,
|
||||
},
|
||||
.multi_read_bit = ST_ACCEL_4_MULTIREAD_BIT,
|
||||
.bootime = 2, /* guess */
|
||||
|
@ -553,10 +593,75 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = {
|
|||
.mask_int2 = ST_ACCEL_5_DRDY_IRQ_INT2_MASK,
|
||||
.addr_ihl = ST_ACCEL_5_IHL_IRQ_ADDR,
|
||||
.mask_ihl = ST_ACCEL_5_IHL_IRQ_MASK,
|
||||
.addr_od = ST_ACCEL_5_OD_IRQ_ADDR,
|
||||
.mask_od = ST_ACCEL_5_OD_IRQ_MASK,
|
||||
.addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR,
|
||||
},
|
||||
.multi_read_bit = ST_ACCEL_5_MULTIREAD_BIT,
|
||||
.bootime = 2, /* guess */
|
||||
},
|
||||
{
|
||||
.wai = ST_ACCEL_6_WAI_EXP,
|
||||
.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
|
||||
.sensors_supported = {
|
||||
[0] = H3LIS331DL_DRIVER_NAME,
|
||||
},
|
||||
.ch = (struct iio_chan_spec *)st_accel_12bit_channels,
|
||||
.odr = {
|
||||
.addr = ST_ACCEL_6_ODR_ADDR,
|
||||
.mask = ST_ACCEL_6_ODR_MASK,
|
||||
.odr_avl = {
|
||||
{ 50, ST_ACCEL_6_ODR_AVL_50HZ_VAL },
|
||||
{ 100, ST_ACCEL_6_ODR_AVL_100HZ_VAL, },
|
||||
{ 400, ST_ACCEL_6_ODR_AVL_400HZ_VAL, },
|
||||
{ 1000, ST_ACCEL_6_ODR_AVL_1000HZ_VAL, },
|
||||
},
|
||||
},
|
||||
.pw = {
|
||||
.addr = ST_ACCEL_6_PW_ADDR,
|
||||
.mask = ST_ACCEL_6_PW_MASK,
|
||||
.value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE,
|
||||
.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
|
||||
},
|
||||
.enable_axis = {
|
||||
.addr = ST_SENSORS_DEFAULT_AXIS_ADDR,
|
||||
.mask = ST_SENSORS_DEFAULT_AXIS_MASK,
|
||||
},
|
||||
.fs = {
|
||||
.addr = ST_ACCEL_6_FS_ADDR,
|
||||
.mask = ST_ACCEL_6_FS_MASK,
|
||||
.fs_avl = {
|
||||
[0] = {
|
||||
.num = ST_ACCEL_FS_AVL_100G,
|
||||
.value = ST_ACCEL_6_FS_AVL_100_VAL,
|
||||
.gain = ST_ACCEL_6_FS_AVL_100_GAIN,
|
||||
},
|
||||
[1] = {
|
||||
.num = ST_ACCEL_FS_AVL_200G,
|
||||
.value = ST_ACCEL_6_FS_AVL_200_VAL,
|
||||
.gain = ST_ACCEL_6_FS_AVL_200_GAIN,
|
||||
},
|
||||
[2] = {
|
||||
.num = ST_ACCEL_FS_AVL_400G,
|
||||
.value = ST_ACCEL_6_FS_AVL_400_VAL,
|
||||
.gain = ST_ACCEL_6_FS_AVL_400_GAIN,
|
||||
},
|
||||
},
|
||||
},
|
||||
.bdu = {
|
||||
.addr = ST_ACCEL_6_BDU_ADDR,
|
||||
.mask = ST_ACCEL_6_BDU_MASK,
|
||||
},
|
||||
.drdy_irq = {
|
||||
.addr = ST_ACCEL_6_DRDY_IRQ_ADDR,
|
||||
.mask_int1 = ST_ACCEL_6_DRDY_IRQ_INT1_MASK,
|
||||
.mask_int2 = ST_ACCEL_6_DRDY_IRQ_INT2_MASK,
|
||||
.addr_ihl = ST_ACCEL_6_IHL_IRQ_ADDR,
|
||||
.mask_ihl = ST_ACCEL_6_IHL_IRQ_MASK,
|
||||
},
|
||||
.multi_read_bit = ST_ACCEL_6_MULTIREAD_BIT,
|
||||
.bootime = 2,
|
||||
},
|
||||
};
|
||||
|
||||
static int st_accel_read_raw(struct iio_dev *indio_dev,
|
||||
|
|
|
@ -76,6 +76,10 @@ static const struct of_device_id st_accel_of_match[] = {
|
|||
.compatible = "st,lis2dh12-accel",
|
||||
.data = LIS2DH12_ACCEL_DEV_NAME,
|
||||
},
|
||||
{
|
||||
.compatible = "st,h3lis331dl-accel",
|
||||
.data = H3LIS331DL_DRIVER_NAME,
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, st_accel_of_match);
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
|
|
|
@ -242,6 +242,16 @@ config LP8788_ADC
|
|||
To compile this driver as a module, choose M here: the module will be
|
||||
called lp8788_adc.
|
||||
|
||||
config LPC18XX_ADC
|
||||
tristate "NXP LPC18xx ADC driver"
|
||||
depends on ARCH_LPC18XX || COMPILE_TEST
|
||||
depends on OF && HAS_IOMEM
|
||||
help
|
||||
Say yes here to build support for NXP LPC18XX ADC.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called lpc18xx_adc.
|
||||
|
||||
config MAX1027
|
||||
tristate "Maxim max1027 ADC driver"
|
||||
depends on SPI
|
||||
|
@ -375,11 +385,11 @@ config ROCKCHIP_SARADC
|
|||
module will be called rockchip_saradc.
|
||||
|
||||
config TI_ADC081C
|
||||
tristate "Texas Instruments ADC081C021/027"
|
||||
tristate "Texas Instruments ADC081C/ADC101C/ADC121C family"
|
||||
depends on I2C
|
||||
help
|
||||
If you say yes here you get support for Texas Instruments ADC081C021
|
||||
and ADC081C027 ADC chips.
|
||||
If you say yes here you get support for Texas Instruments ADC081C,
|
||||
ADC101C and ADC121C ADC chips.
|
||||
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called ti-adc081c.
|
||||
|
|
|
@ -25,6 +25,7 @@ obj-$(CONFIG_HI8435) += hi8435.o
|
|||
obj-$(CONFIG_IMX7D_ADC) += imx7d_adc.o
|
||||
obj-$(CONFIG_INA2XX_ADC) += ina2xx-adc.o
|
||||
obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
|
||||
obj-$(CONFIG_LPC18XX_ADC) += lpc18xx_adc.o
|
||||
obj-$(CONFIG_MAX1027) += max1027.o
|
||||
obj-$(CONFIG_MAX1363) += max1363.o
|
||||
obj-$(CONFIG_MCP320X) += mcp320x.o
|
||||
|
|
|
@ -477,7 +477,7 @@ static int ad799x_read_event_value(struct iio_dev *indio_dev,
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
*val = (ret >> chan->scan_type.shift) &
|
||||
GENMASK(chan->scan_type.realbits - 1 , 0);
|
||||
GENMASK(chan->scan_type.realbits - 1, 0);
|
||||
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
|
|
@ -66,8 +66,10 @@
|
|||
#define AT91_SAMA5D2_MR_PRESCAL(v) ((v) << AT91_SAMA5D2_MR_PRESCAL_OFFSET)
|
||||
#define AT91_SAMA5D2_MR_PRESCAL_OFFSET 8
|
||||
#define AT91_SAMA5D2_MR_PRESCAL_MAX 0xff
|
||||
#define AT91_SAMA5D2_MR_PRESCAL_MASK GENMASK(15, 8)
|
||||
/* Startup Time */
|
||||
#define AT91_SAMA5D2_MR_STARTUP(v) ((v) << 16)
|
||||
#define AT91_SAMA5D2_MR_STARTUP_MASK GENMASK(19, 16)
|
||||
/* Analog Change */
|
||||
#define AT91_SAMA5D2_MR_ANACH BIT(23)
|
||||
/* Tracking Time */
|
||||
|
@ -92,13 +94,13 @@
|
|||
/* Last Converted Data Register */
|
||||
#define AT91_SAMA5D2_LCDR 0x20
|
||||
/* Interrupt Enable Register */
|
||||
#define AT91_SAMA5D2_IER 0x24
|
||||
#define AT91_SAMA5D2_IER 0x24
|
||||
/* Interrupt Disable Register */
|
||||
#define AT91_SAMA5D2_IDR 0x28
|
||||
#define AT91_SAMA5D2_IDR 0x28
|
||||
/* Interrupt Mask Register */
|
||||
#define AT91_SAMA5D2_IMR 0x2c
|
||||
#define AT91_SAMA5D2_IMR 0x2c
|
||||
/* Interrupt Status Register */
|
||||
#define AT91_SAMA5D2_ISR 0x30
|
||||
#define AT91_SAMA5D2_ISR 0x30
|
||||
/* Last Channel Trigger Mode Register */
|
||||
#define AT91_SAMA5D2_LCTMR 0x34
|
||||
/* Last Channel Compare Window Register */
|
||||
|
@ -106,17 +108,20 @@
|
|||
/* Overrun Status Register */
|
||||
#define AT91_SAMA5D2_OVER 0x3c
|
||||
/* Extended Mode Register */
|
||||
#define AT91_SAMA5D2_EMR 0x40
|
||||
#define AT91_SAMA5D2_EMR 0x40
|
||||
/* Compare Window Register */
|
||||
#define AT91_SAMA5D2_CWR 0x44
|
||||
#define AT91_SAMA5D2_CWR 0x44
|
||||
/* Channel Gain Register */
|
||||
#define AT91_SAMA5D2_CGR 0x48
|
||||
#define AT91_SAMA5D2_CGR 0x48
|
||||
|
||||
/* Channel Offset Register */
|
||||
#define AT91_SAMA5D2_COR 0x4c
|
||||
#define AT91_SAMA5D2_COR 0x4c
|
||||
#define AT91_SAMA5D2_COR_DIFF_OFFSET 16
|
||||
|
||||
/* Channel Data Register 0 */
|
||||
#define AT91_SAMA5D2_CDR0 0x50
|
||||
/* Analog Control Register */
|
||||
#define AT91_SAMA5D2_ACR 0x94
|
||||
#define AT91_SAMA5D2_ACR 0x94
|
||||
/* Touchscreen Mode Register */
|
||||
#define AT91_SAMA5D2_TSMR 0xb0
|
||||
/* Touchscreen X Position Register */
|
||||
|
@ -130,7 +135,7 @@
|
|||
/* Correction Select Register */
|
||||
#define AT91_SAMA5D2_COSR 0xd0
|
||||
/* Correction Value Register */
|
||||
#define AT91_SAMA5D2_CVR 0xd4
|
||||
#define AT91_SAMA5D2_CVR 0xd4
|
||||
/* Channel Error Correction Register */
|
||||
#define AT91_SAMA5D2_CECR 0xd8
|
||||
/* Write Protection Mode Register */
|
||||
|
@ -140,7 +145,7 @@
|
|||
/* Version Register */
|
||||
#define AT91_SAMA5D2_VERSION 0xfc
|
||||
|
||||
#define AT91_AT91_SAMA5D2_CHAN(num, addr) \
|
||||
#define AT91_SAMA5D2_CHAN_SINGLE(num, addr) \
|
||||
{ \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.channel = num, \
|
||||
|
@ -156,6 +161,24 @@
|
|||
.indexed = 1, \
|
||||
}
|
||||
|
||||
#define AT91_SAMA5D2_CHAN_DIFF(num, num2, addr) \
|
||||
{ \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.differential = 1, \
|
||||
.channel = num, \
|
||||
.channel2 = num2, \
|
||||
.address = addr, \
|
||||
.scan_type = { \
|
||||
.sign = 's', \
|
||||
.realbits = 12, \
|
||||
}, \
|
||||
.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),\
|
||||
.datasheet_name = "CH"#num"-CH"#num2, \
|
||||
.indexed = 1, \
|
||||
}
|
||||
|
||||
#define at91_adc_readl(st, reg) readl_relaxed(st->base + reg)
|
||||
#define at91_adc_writel(st, reg, val) writel_relaxed(val, st->base + reg)
|
||||
|
||||
|
@ -185,18 +208,24 @@ struct at91_adc_state {
|
|||
};
|
||||
|
||||
static const struct iio_chan_spec at91_adc_channels[] = {
|
||||
AT91_AT91_SAMA5D2_CHAN(0, 0x50),
|
||||
AT91_AT91_SAMA5D2_CHAN(1, 0x54),
|
||||
AT91_AT91_SAMA5D2_CHAN(2, 0x58),
|
||||
AT91_AT91_SAMA5D2_CHAN(3, 0x5c),
|
||||
AT91_AT91_SAMA5D2_CHAN(4, 0x60),
|
||||
AT91_AT91_SAMA5D2_CHAN(5, 0x64),
|
||||
AT91_AT91_SAMA5D2_CHAN(6, 0x68),
|
||||
AT91_AT91_SAMA5D2_CHAN(7, 0x6c),
|
||||
AT91_AT91_SAMA5D2_CHAN(8, 0x70),
|
||||
AT91_AT91_SAMA5D2_CHAN(9, 0x74),
|
||||
AT91_AT91_SAMA5D2_CHAN(10, 0x78),
|
||||
AT91_AT91_SAMA5D2_CHAN(11, 0x7c),
|
||||
AT91_SAMA5D2_CHAN_SINGLE(0, 0x50),
|
||||
AT91_SAMA5D2_CHAN_SINGLE(1, 0x54),
|
||||
AT91_SAMA5D2_CHAN_SINGLE(2, 0x58),
|
||||
AT91_SAMA5D2_CHAN_SINGLE(3, 0x5c),
|
||||
AT91_SAMA5D2_CHAN_SINGLE(4, 0x60),
|
||||
AT91_SAMA5D2_CHAN_SINGLE(5, 0x64),
|
||||
AT91_SAMA5D2_CHAN_SINGLE(6, 0x68),
|
||||
AT91_SAMA5D2_CHAN_SINGLE(7, 0x6c),
|
||||
AT91_SAMA5D2_CHAN_SINGLE(8, 0x70),
|
||||
AT91_SAMA5D2_CHAN_SINGLE(9, 0x74),
|
||||
AT91_SAMA5D2_CHAN_SINGLE(10, 0x78),
|
||||
AT91_SAMA5D2_CHAN_SINGLE(11, 0x7c),
|
||||
AT91_SAMA5D2_CHAN_DIFF(0, 1, 0x50),
|
||||
AT91_SAMA5D2_CHAN_DIFF(2, 3, 0x58),
|
||||
AT91_SAMA5D2_CHAN_DIFF(4, 5, 0x60),
|
||||
AT91_SAMA5D2_CHAN_DIFF(6, 7, 0x68),
|
||||
AT91_SAMA5D2_CHAN_DIFF(8, 9, 0x70),
|
||||
AT91_SAMA5D2_CHAN_DIFF(10, 11, 0x78),
|
||||
};
|
||||
|
||||
static unsigned at91_adc_startup_time(unsigned startup_time_min,
|
||||
|
@ -226,7 +255,7 @@ static unsigned at91_adc_startup_time(unsigned startup_time_min,
|
|||
static void at91_adc_setup_samp_freq(struct at91_adc_state *st, unsigned freq)
|
||||
{
|
||||
struct iio_dev *indio_dev = iio_priv_to_dev(st);
|
||||
unsigned f_per, prescal, startup;
|
||||
unsigned f_per, prescal, startup, mr;
|
||||
|
||||
f_per = clk_get_rate(st->per_clk);
|
||||
prescal = (f_per / (2 * freq)) - 1;
|
||||
|
@ -234,10 +263,11 @@ static void at91_adc_setup_samp_freq(struct at91_adc_state *st, unsigned freq)
|
|||
startup = at91_adc_startup_time(st->soc_info.startup_time,
|
||||
freq / 1000);
|
||||
|
||||
at91_adc_writel(st, AT91_SAMA5D2_MR,
|
||||
AT91_SAMA5D2_MR_TRANSFER(2)
|
||||
| AT91_SAMA5D2_MR_STARTUP(startup)
|
||||
| AT91_SAMA5D2_MR_PRESCAL(prescal));
|
||||
mr = at91_adc_readl(st, AT91_SAMA5D2_MR);
|
||||
mr &= ~(AT91_SAMA5D2_MR_STARTUP_MASK | AT91_SAMA5D2_MR_PRESCAL_MASK);
|
||||
mr |= AT91_SAMA5D2_MR_STARTUP(startup);
|
||||
mr |= AT91_SAMA5D2_MR_PRESCAL(prescal);
|
||||
at91_adc_writel(st, AT91_SAMA5D2_MR, mr);
|
||||
|
||||
dev_dbg(&indio_dev->dev, "freq: %u, startup: %u, prescal: %u\n",
|
||||
freq, startup, prescal);
|
||||
|
@ -278,6 +308,7 @@ static int at91_adc_read_raw(struct iio_dev *indio_dev,
|
|||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct at91_adc_state *st = iio_priv(indio_dev);
|
||||
u32 cor = 0;
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
|
@ -286,6 +317,11 @@ static int at91_adc_read_raw(struct iio_dev *indio_dev,
|
|||
|
||||
st->chan = chan;
|
||||
|
||||
if (chan->differential)
|
||||
cor = (BIT(chan->channel) | BIT(chan->channel2)) <<
|
||||
AT91_SAMA5D2_COR_DIFF_OFFSET;
|
||||
|
||||
at91_adc_writel(st, AT91_SAMA5D2_COR, cor);
|
||||
at91_adc_writel(st, AT91_SAMA5D2_CHER, BIT(chan->channel));
|
||||
at91_adc_writel(st, AT91_SAMA5D2_IER, BIT(chan->channel));
|
||||
at91_adc_writel(st, AT91_SAMA5D2_CR, AT91_SAMA5D2_CR_START);
|
||||
|
@ -298,6 +334,8 @@ static int at91_adc_read_raw(struct iio_dev *indio_dev,
|
|||
|
||||
if (ret > 0) {
|
||||
*val = st->conversion_value;
|
||||
if (chan->scan_type.sign == 's')
|
||||
*val = sign_extend32(*val, 11);
|
||||
ret = IIO_VAL_INT;
|
||||
st->conversion_done = false;
|
||||
}
|
||||
|
@ -310,6 +348,8 @@ static int at91_adc_read_raw(struct iio_dev *indio_dev,
|
|||
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = st->vref_uv / 1000;
|
||||
if (chan->differential)
|
||||
*val *= 2;
|
||||
*val2 = chan->scan_type.realbits;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
|
||||
|
@ -444,6 +484,12 @@ static int at91_adc_probe(struct platform_device *pdev)
|
|||
|
||||
at91_adc_writel(st, AT91_SAMA5D2_CR, AT91_SAMA5D2_CR_SWRST);
|
||||
at91_adc_writel(st, AT91_SAMA5D2_IDR, 0xffffffff);
|
||||
/*
|
||||
* Transfer field must be set to 2 according to the datasheet and
|
||||
* allows different analog settings for each channel.
|
||||
*/
|
||||
at91_adc_writel(st, AT91_SAMA5D2_MR,
|
||||
AT91_SAMA5D2_MR_TRANSFER(2) | AT91_SAMA5D2_MR_ANACH);
|
||||
|
||||
at91_adc_setup_samp_freq(st, st->soc_info.min_sample_rate);
|
||||
|
||||
|
|
|
@ -797,8 +797,8 @@ static u32 calc_startup_ticks_9x5(u32 startup_time, u32 adc_clk_khz)
|
|||
* Startup Time = <lookup_table_value> / ADC Clock
|
||||
*/
|
||||
const int startup_lookup[] = {
|
||||
0 , 8 , 16 , 24 ,
|
||||
64 , 80 , 96 , 112,
|
||||
0, 8, 16, 24,
|
||||
64, 80, 96, 112,
|
||||
512, 576, 640, 704,
|
||||
768, 832, 896, 960
|
||||
};
|
||||
|
@ -924,14 +924,14 @@ static int at91_adc_probe_dt(struct at91_adc_state *st,
|
|||
ret = -EINVAL;
|
||||
goto error_ret;
|
||||
}
|
||||
trig->name = name;
|
||||
trig->name = name;
|
||||
|
||||
if (of_property_read_u32(trig_node, "trigger-value", &prop)) {
|
||||
dev_err(&idev->dev, "Missing trigger-value property in the DT.\n");
|
||||
ret = -EINVAL;
|
||||
goto error_ret;
|
||||
}
|
||||
trig->value = prop;
|
||||
trig->value = prop;
|
||||
trig->is_external = of_property_read_bool(trig_node, "trigger-external");
|
||||
i++;
|
||||
}
|
||||
|
|
|
@ -185,9 +185,9 @@ static int ina2xx_read_raw(struct iio_dev *indio_dev,
|
|||
case IIO_CHAN_INFO_SCALE:
|
||||
switch (chan->address) {
|
||||
case INA2XX_SHUNT_VOLTAGE:
|
||||
/* processed (mV) = raw*1000/shunt_div */
|
||||
/* processed (mV) = raw/shunt_div */
|
||||
*val2 = chip->config->shunt_div;
|
||||
*val = 1000;
|
||||
*val = 1;
|
||||
return IIO_VAL_FRACTIONAL;
|
||||
|
||||
case INA2XX_BUS_VOLTAGE:
|
||||
|
@ -350,6 +350,23 @@ static ssize_t ina2xx_allow_async_readout_store(struct device *dev,
|
|||
return len;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set current LSB to 1mA, shunt is in uOhms
|
||||
* (equation 13 in datasheet). We hardcode a Current_LSB
|
||||
* of 1.0 x10-6. The only remaining parameter is RShunt.
|
||||
* There is no need to expose the CALIBRATION register
|
||||
* to the user for now. But we need to reset this register
|
||||
* if the user updates RShunt after driver init, e.g upon
|
||||
* reading an EEPROM/Probe-type value.
|
||||
*/
|
||||
static int ina2xx_set_calibration(struct ina2xx_chip_info *chip)
|
||||
{
|
||||
u16 regval = DIV_ROUND_CLOSEST(chip->config->calibration_factor,
|
||||
chip->shunt_resistor);
|
||||
|
||||
return regmap_write(chip->regmap, INA2XX_CALIBRATION, regval);
|
||||
}
|
||||
|
||||
static int set_shunt_resistor(struct ina2xx_chip_info *chip, unsigned int val)
|
||||
{
|
||||
if (val <= 0 || val > chip->config->calibration_factor)
|
||||
|
@ -385,6 +402,11 @@ static ssize_t ina2xx_shunt_resistor_store(struct device *dev,
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Update the Calibration register */
|
||||
ret = ina2xx_set_calibration(chip);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
|
@ -602,24 +624,11 @@ static const struct iio_info ina2xx_info = {
|
|||
/* Initialize the configuration and calibration registers. */
|
||||
static int ina2xx_init(struct ina2xx_chip_info *chip, unsigned int config)
|
||||
{
|
||||
u16 regval;
|
||||
int ret;
|
||||
|
||||
ret = regmap_write(chip->regmap, INA2XX_CONFIG, config);
|
||||
int ret = regmap_write(chip->regmap, INA2XX_CONFIG, config);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Set current LSB to 1mA, shunt is in uOhms
|
||||
* (equation 13 in datasheet). We hardcode a Current_LSB
|
||||
* of 1.0 x10-6. The only remaining parameter is RShunt.
|
||||
* There is no need to expose the CALIBRATION register
|
||||
* to the user for now.
|
||||
*/
|
||||
regval = DIV_ROUND_CLOSEST(chip->config->calibration_factor,
|
||||
chip->shunt_resistor);
|
||||
|
||||
return regmap_write(chip->regmap, INA2XX_CALIBRATION, regval);
|
||||
return ina2xx_set_calibration(chip);
|
||||
}
|
||||
|
||||
static int ina2xx_probe(struct i2c_client *client,
|
||||
|
|
|
@ -0,0 +1,231 @@
|
|||
/*
|
||||
* IIO ADC driver for NXP LPC18xx ADC
|
||||
*
|
||||
* Copyright (C) 2016 Joachim Eastwood <manabian@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* UNSUPPORTED hardware features:
|
||||
* - Hardware triggers
|
||||
* - Burst mode
|
||||
* - Interrupts
|
||||
* - DMA
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/driver.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
/* LPC18XX ADC registers and bits */
|
||||
#define LPC18XX_ADC_CR 0x000
|
||||
#define LPC18XX_ADC_CR_CLKDIV_SHIFT 8
|
||||
#define LPC18XX_ADC_CR_PDN BIT(21)
|
||||
#define LPC18XX_ADC_CR_START_NOW (0x1 << 24)
|
||||
#define LPC18XX_ADC_GDR 0x004
|
||||
|
||||
/* Data register bits */
|
||||
#define LPC18XX_ADC_SAMPLE_SHIFT 6
|
||||
#define LPC18XX_ADC_SAMPLE_MASK 0x3ff
|
||||
#define LPC18XX_ADC_CONV_DONE BIT(31)
|
||||
|
||||
/* Clock should be 4.5 MHz or less */
|
||||
#define LPC18XX_ADC_CLK_TARGET 4500000
|
||||
|
||||
struct lpc18xx_adc {
|
||||
struct regulator *vref;
|
||||
void __iomem *base;
|
||||
struct device *dev;
|
||||
struct mutex lock;
|
||||
struct clk *clk;
|
||||
u32 cr_reg;
|
||||
};
|
||||
|
||||
#define LPC18XX_ADC_CHAN(_idx) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = _idx, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec lpc18xx_adc_iio_channels[] = {
|
||||
LPC18XX_ADC_CHAN(0),
|
||||
LPC18XX_ADC_CHAN(1),
|
||||
LPC18XX_ADC_CHAN(2),
|
||||
LPC18XX_ADC_CHAN(3),
|
||||
LPC18XX_ADC_CHAN(4),
|
||||
LPC18XX_ADC_CHAN(5),
|
||||
LPC18XX_ADC_CHAN(6),
|
||||
LPC18XX_ADC_CHAN(7),
|
||||
};
|
||||
|
||||
static int lpc18xx_adc_read_chan(struct lpc18xx_adc *adc, unsigned int ch)
|
||||
{
|
||||
int ret;
|
||||
u32 reg;
|
||||
|
||||
reg = adc->cr_reg | BIT(ch) | LPC18XX_ADC_CR_START_NOW;
|
||||
writel(reg, adc->base + LPC18XX_ADC_CR);
|
||||
|
||||
ret = readl_poll_timeout(adc->base + LPC18XX_ADC_GDR, reg,
|
||||
reg & LPC18XX_ADC_CONV_DONE, 3, 9);
|
||||
if (ret) {
|
||||
dev_warn(adc->dev, "adc read timed out\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return (reg >> LPC18XX_ADC_SAMPLE_SHIFT) & LPC18XX_ADC_SAMPLE_MASK;
|
||||
}
|
||||
|
||||
static int lpc18xx_adc_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct lpc18xx_adc *adc = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
mutex_lock(&adc->lock);
|
||||
*val = lpc18xx_adc_read_chan(adc, chan->channel);
|
||||
mutex_unlock(&adc->lock);
|
||||
if (*val < 0)
|
||||
return *val;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = regulator_get_voltage(adc->vref) / 1000;
|
||||
*val2 = 10;
|
||||
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct iio_info lpc18xx_adc_info = {
|
||||
.read_raw = lpc18xx_adc_read_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int lpc18xx_adc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct lpc18xx_adc *adc;
|
||||
struct resource *res;
|
||||
unsigned int clkdiv;
|
||||
unsigned long rate;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, indio_dev);
|
||||
adc = iio_priv(indio_dev);
|
||||
adc->dev = &pdev->dev;
|
||||
mutex_init(&adc->lock);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
adc->base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(adc->base))
|
||||
return PTR_ERR(adc->base);
|
||||
|
||||
adc->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(adc->clk)) {
|
||||
dev_err(&pdev->dev, "error getting clock\n");
|
||||
return PTR_ERR(adc->clk);
|
||||
}
|
||||
|
||||
rate = clk_get_rate(adc->clk);
|
||||
clkdiv = DIV_ROUND_UP(rate, LPC18XX_ADC_CLK_TARGET);
|
||||
|
||||
adc->vref = devm_regulator_get(&pdev->dev, "vref");
|
||||
if (IS_ERR(adc->vref)) {
|
||||
dev_err(&pdev->dev, "error getting regulator\n");
|
||||
return PTR_ERR(adc->vref);
|
||||
}
|
||||
|
||||
indio_dev->name = dev_name(&pdev->dev);
|
||||
indio_dev->dev.parent = &pdev->dev;
|
||||
indio_dev->info = &lpc18xx_adc_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = lpc18xx_adc_iio_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(lpc18xx_adc_iio_channels);
|
||||
|
||||
ret = regulator_enable(adc->vref);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "unable to enable regulator\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(adc->clk);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "unable to enable clock\n");
|
||||
goto dis_reg;
|
||||
}
|
||||
|
||||
adc->cr_reg = (clkdiv << LPC18XX_ADC_CR_CLKDIV_SHIFT) |
|
||||
LPC18XX_ADC_CR_PDN;
|
||||
writel(adc->cr_reg, adc->base + LPC18XX_ADC_CR);
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "unable to register device\n");
|
||||
goto dis_clk;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
dis_clk:
|
||||
writel(0, adc->base + LPC18XX_ADC_CR);
|
||||
clk_disable_unprepare(adc->clk);
|
||||
dis_reg:
|
||||
regulator_disable(adc->vref);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int lpc18xx_adc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
struct lpc18xx_adc *adc = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
writel(0, adc->base + LPC18XX_ADC_CR);
|
||||
clk_disable_unprepare(adc->clk);
|
||||
regulator_disable(adc->vref);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id lpc18xx_adc_match[] = {
|
||||
{ .compatible = "nxp,lpc1850-adc" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, lpc18xx_adc_match);
|
||||
|
||||
static struct platform_driver lpc18xx_adc_driver = {
|
||||
.probe = lpc18xx_adc_probe,
|
||||
.remove = lpc18xx_adc_remove,
|
||||
.driver = {
|
||||
.name = "lpc18xx-adc",
|
||||
.of_match_table = lpc18xx_adc_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(lpc18xx_adc_driver);
|
||||
|
||||
MODULE_DESCRIPTION("LPC18xx ADC driver");
|
||||
MODULE_AUTHOR("Joachim Eastwood <manabian@gmail.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -61,9 +61,9 @@
|
|||
|
||||
static const int mcp3422_scales[4][4] = {
|
||||
{ 1000000, 500000, 250000, 125000 },
|
||||
{ 250000 , 125000, 62500 , 31250 },
|
||||
{ 62500 , 31250 , 15625 , 7812 },
|
||||
{ 15625 , 7812 , 3906 , 1953 } };
|
||||
{ 250000, 125000, 62500, 31250 },
|
||||
{ 62500, 31250, 15625, 7812 },
|
||||
{ 15625, 7812, 3906, 1953 } };
|
||||
|
||||
/* Constant msleep times for data acquisitions */
|
||||
static const int mcp3422_read_times[4] = {
|
||||
|
|
|
@ -686,6 +686,17 @@ static void mxs_lradc_prepare_pressure(struct mxs_lradc *lradc)
|
|||
|
||||
static void mxs_lradc_enable_touch_detection(struct mxs_lradc *lradc)
|
||||
{
|
||||
/* Configure the touchscreen type */
|
||||
if (lradc->soc == IMX28_LRADC) {
|
||||
mxs_lradc_reg_clear(lradc, LRADC_CTRL0_MX28_TOUCH_SCREEN_TYPE,
|
||||
LRADC_CTRL0);
|
||||
|
||||
if (lradc->use_touchscreen == MXS_LRADC_TOUCHSCREEN_5WIRE)
|
||||
mxs_lradc_reg_set(lradc,
|
||||
LRADC_CTRL0_MX28_TOUCH_SCREEN_TYPE,
|
||||
LRADC_CTRL0);
|
||||
}
|
||||
|
||||
mxs_lradc_setup_touch_detection(lradc);
|
||||
|
||||
lradc->cur_plate = LRADC_TOUCH;
|
||||
|
@ -1127,6 +1138,7 @@ static int mxs_lradc_ts_register(struct mxs_lradc *lradc)
|
|||
__set_bit(EV_ABS, input->evbit);
|
||||
__set_bit(EV_KEY, input->evbit);
|
||||
__set_bit(BTN_TOUCH, input->keybit);
|
||||
__set_bit(INPUT_PROP_DIRECT, input->propbit);
|
||||
input_set_abs_params(input, ABS_X, 0, LRADC_SINGLE_SAMPLE_MASK, 0, 0);
|
||||
input_set_abs_params(input, ABS_Y, 0, LRADC_SINGLE_SAMPLE_MASK, 0, 0);
|
||||
input_set_abs_params(input, ABS_PRESSURE, 0, LRADC_SINGLE_SAMPLE_MASK,
|
||||
|
@ -1475,18 +1487,13 @@ static const struct iio_chan_spec mx28_lradc_chan_spec[] = {
|
|||
MXS_ADC_CHAN(15, IIO_VOLTAGE, "VDD5V"),
|
||||
};
|
||||
|
||||
static int mxs_lradc_hw_init(struct mxs_lradc *lradc)
|
||||
static void mxs_lradc_hw_init(struct mxs_lradc *lradc)
|
||||
{
|
||||
/* The ADC always uses DELAY CHANNEL 0. */
|
||||
const u32 adc_cfg =
|
||||
(1 << (LRADC_DELAY_TRIGGER_DELAYS_OFFSET + 0)) |
|
||||
(LRADC_DELAY_TIMER_PER << LRADC_DELAY_DELAY_OFFSET);
|
||||
|
||||
int ret = stmp_reset_block(lradc->base);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Configure DELAY CHANNEL 0 for generic ADC sampling. */
|
||||
mxs_lradc_reg_wrt(lradc, adc_cfg, LRADC_DELAY(0));
|
||||
|
||||
|
@ -1495,20 +1502,8 @@ static int mxs_lradc_hw_init(struct mxs_lradc *lradc)
|
|||
mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(2));
|
||||
mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(3));
|
||||
|
||||
/* Configure the touchscreen type */
|
||||
if (lradc->soc == IMX28_LRADC) {
|
||||
mxs_lradc_reg_clear(lradc, LRADC_CTRL0_MX28_TOUCH_SCREEN_TYPE,
|
||||
LRADC_CTRL0);
|
||||
|
||||
if (lradc->use_touchscreen == MXS_LRADC_TOUCHSCREEN_5WIRE)
|
||||
mxs_lradc_reg_set(lradc, LRADC_CTRL0_MX28_TOUCH_SCREEN_TYPE,
|
||||
LRADC_CTRL0);
|
||||
}
|
||||
|
||||
/* Start internal temperature sensing. */
|
||||
mxs_lradc_reg_wrt(lradc, 0, LRADC_CTRL2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mxs_lradc_hw_stop(struct mxs_lradc *lradc)
|
||||
|
@ -1708,11 +1703,13 @@ static int mxs_lradc_probe(struct platform_device *pdev)
|
|||
}
|
||||
}
|
||||
|
||||
/* Configure the hardware. */
|
||||
ret = mxs_lradc_hw_init(lradc);
|
||||
ret = stmp_reset_block(lradc->base);
|
||||
if (ret)
|
||||
goto err_dev;
|
||||
|
||||
/* Configure the hardware. */
|
||||
mxs_lradc_hw_init(lradc);
|
||||
|
||||
/* Register the touchscreen input device. */
|
||||
if (touch_ret == 0) {
|
||||
ret = mxs_lradc_ts_register(lradc);
|
||||
|
|
|
@ -159,6 +159,22 @@ static const struct rockchip_saradc_data rk3066_tsadc_data = {
|
|||
.clk_rate = 50000,
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec rockchip_rk3399_saradc_iio_channels[] = {
|
||||
ADC_CHANNEL(0, "adc0"),
|
||||
ADC_CHANNEL(1, "adc1"),
|
||||
ADC_CHANNEL(2, "adc2"),
|
||||
ADC_CHANNEL(3, "adc3"),
|
||||
ADC_CHANNEL(4, "adc4"),
|
||||
ADC_CHANNEL(5, "adc5"),
|
||||
};
|
||||
|
||||
static const struct rockchip_saradc_data rk3399_saradc_data = {
|
||||
.num_bits = 10,
|
||||
.channels = rockchip_rk3399_saradc_iio_channels,
|
||||
.num_channels = ARRAY_SIZE(rockchip_rk3399_saradc_iio_channels),
|
||||
.clk_rate = 1000000,
|
||||
};
|
||||
|
||||
static const struct of_device_id rockchip_saradc_match[] = {
|
||||
{
|
||||
.compatible = "rockchip,saradc",
|
||||
|
@ -166,6 +182,9 @@ static const struct of_device_id rockchip_saradc_match[] = {
|
|||
}, {
|
||||
.compatible = "rockchip,rk3066-tsadc",
|
||||
.data = &rk3066_tsadc_data,
|
||||
}, {
|
||||
.compatible = "rockchip,rk3399-saradc",
|
||||
.data = &rk3399_saradc_data,
|
||||
},
|
||||
{},
|
||||
};
|
||||
|
|
|
@ -1,9 +1,21 @@
|
|||
/*
|
||||
* TI ADC081C/ADC101C/ADC121C 8/10/12-bit ADC driver
|
||||
*
|
||||
* Copyright (C) 2012 Avionic Design GmbH
|
||||
* Copyright (C) 2016 Intel
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Datasheets:
|
||||
* http://www.ti.com/lit/ds/symlink/adc081c021.pdf
|
||||
* http://www.ti.com/lit/ds/symlink/adc101c021.pdf
|
||||
* http://www.ti.com/lit/ds/symlink/adc121c021.pdf
|
||||
*
|
||||
* The devices have a very similar interface and differ mostly in the number of
|
||||
* bits handled. For the 8-bit and 10-bit models the least-significant 4 or 2
|
||||
* bits of value registers are reserved.
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
|
@ -12,11 +24,17 @@
|
|||
#include <linux/of.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
struct adc081c {
|
||||
struct i2c_client *i2c;
|
||||
struct regulator *ref;
|
||||
|
||||
/* 8, 10 or 12 */
|
||||
int bits;
|
||||
};
|
||||
|
||||
#define REG_CONV_RES 0x00
|
||||
|
@ -34,7 +52,7 @@ static int adc081c_read_raw(struct iio_dev *iio,
|
|||
if (err < 0)
|
||||
return err;
|
||||
|
||||
*value = (err >> 4) & 0xff;
|
||||
*value = (err & 0xFFF) >> (12 - adc->bits);
|
||||
return IIO_VAL_INT;
|
||||
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
|
@ -43,7 +61,7 @@ static int adc081c_read_raw(struct iio_dev *iio,
|
|||
return err;
|
||||
|
||||
*value = err / 1000;
|
||||
*shift = 8;
|
||||
*shift = adc->bits;
|
||||
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
|
||||
|
@ -54,10 +72,53 @@ static int adc081c_read_raw(struct iio_dev *iio,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec adc081c_channel = {
|
||||
.type = IIO_VOLTAGE,
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
#define ADCxx1C_CHAN(_bits) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = (_bits), \
|
||||
.storagebits = 16, \
|
||||
.shift = 12 - (_bits), \
|
||||
.endianness = IIO_CPU, \
|
||||
}, \
|
||||
}
|
||||
|
||||
#define DEFINE_ADCxx1C_CHANNELS(_name, _bits) \
|
||||
static const struct iio_chan_spec _name ## _channels[] = { \
|
||||
ADCxx1C_CHAN((_bits)), \
|
||||
IIO_CHAN_SOFT_TIMESTAMP(1), \
|
||||
}; \
|
||||
|
||||
#define ADC081C_NUM_CHANNELS 2
|
||||
|
||||
struct adcxx1c_model {
|
||||
const struct iio_chan_spec* channels;
|
||||
int bits;
|
||||
};
|
||||
|
||||
#define ADCxx1C_MODEL(_name, _bits) \
|
||||
{ \
|
||||
.channels = _name ## _channels, \
|
||||
.bits = (_bits), \
|
||||
}
|
||||
|
||||
DEFINE_ADCxx1C_CHANNELS(adc081c, 8);
|
||||
DEFINE_ADCxx1C_CHANNELS(adc101c, 10);
|
||||
DEFINE_ADCxx1C_CHANNELS(adc121c, 12);
|
||||
|
||||
/* Model ids are indexes in _models array */
|
||||
enum adcxx1c_model_id {
|
||||
ADC081C = 0,
|
||||
ADC101C = 1,
|
||||
ADC121C = 2,
|
||||
};
|
||||
|
||||
static struct adcxx1c_model adcxx1c_models[] = {
|
||||
ADCxx1C_MODEL(adc081c, 8),
|
||||
ADCxx1C_MODEL(adc101c, 10),
|
||||
ADCxx1C_MODEL(adc121c, 12),
|
||||
};
|
||||
|
||||
static const struct iio_info adc081c_info = {
|
||||
|
@ -65,11 +126,30 @@ static const struct iio_info adc081c_info = {
|
|||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static irqreturn_t adc081c_trigger_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct adc081c *data = iio_priv(indio_dev);
|
||||
u16 buf[8]; /* 2 bytes data + 6 bytes padding + 8 bytes timestamp */
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_read_word_swapped(data->i2c, REG_CONV_RES);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
buf[0] = ret;
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, buf, iio_get_time_ns());
|
||||
out:
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int adc081c_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct iio_dev *iio;
|
||||
struct adc081c *adc;
|
||||
struct adcxx1c_model *model = &adcxx1c_models[id->driver_data];
|
||||
int err;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA))
|
||||
|
@ -81,6 +161,7 @@ static int adc081c_probe(struct i2c_client *client,
|
|||
|
||||
adc = iio_priv(iio);
|
||||
adc->i2c = client;
|
||||
adc->bits = model->bits;
|
||||
|
||||
adc->ref = devm_regulator_get(&client->dev, "vref");
|
||||
if (IS_ERR(adc->ref))
|
||||
|
@ -95,18 +176,26 @@ static int adc081c_probe(struct i2c_client *client,
|
|||
iio->modes = INDIO_DIRECT_MODE;
|
||||
iio->info = &adc081c_info;
|
||||
|
||||
iio->channels = &adc081c_channel;
|
||||
iio->num_channels = 1;
|
||||
iio->channels = model->channels;
|
||||
iio->num_channels = ADC081C_NUM_CHANNELS;
|
||||
|
||||
err = iio_triggered_buffer_setup(iio, NULL, adc081c_trigger_handler, NULL);
|
||||
if (err < 0) {
|
||||
dev_err(&client->dev, "iio triggered buffer setup failed\n");
|
||||
goto err_regulator_disable;
|
||||
}
|
||||
|
||||
err = iio_device_register(iio);
|
||||
if (err < 0)
|
||||
goto regulator_disable;
|
||||
goto err_buffer_cleanup;
|
||||
|
||||
i2c_set_clientdata(client, iio);
|
||||
|
||||
return 0;
|
||||
|
||||
regulator_disable:
|
||||
err_buffer_cleanup:
|
||||
iio_triggered_buffer_cleanup(iio);
|
||||
err_regulator_disable:
|
||||
regulator_disable(adc->ref);
|
||||
|
||||
return err;
|
||||
|
@ -118,13 +207,16 @@ static int adc081c_remove(struct i2c_client *client)
|
|||
struct adc081c *adc = iio_priv(iio);
|
||||
|
||||
iio_device_unregister(iio);
|
||||
iio_triggered_buffer_cleanup(iio);
|
||||
regulator_disable(adc->ref);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id adc081c_id[] = {
|
||||
{ "adc081c", 0 },
|
||||
{ "adc081c", ADC081C },
|
||||
{ "adc101c", ADC101C },
|
||||
{ "adc121c", ADC121C },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, adc081c_id);
|
||||
|
@ -132,6 +224,8 @@ MODULE_DEVICE_TABLE(i2c, adc081c_id);
|
|||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id adc081c_of_match[] = {
|
||||
{ .compatible = "ti,adc081c" },
|
||||
{ .compatible = "ti,adc101c" },
|
||||
{ .compatible = "ti,adc121c" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, adc081c_of_match);
|
||||
|
@ -149,5 +243,5 @@ static struct i2c_driver adc081c_driver = {
|
|||
module_i2c_driver(adc081c_driver);
|
||||
|
||||
MODULE_AUTHOR("Thierry Reding <thierry.reding@avionic-design.de>");
|
||||
MODULE_DESCRIPTION("Texas Instruments ADC081C021/027 driver");
|
||||
MODULE_DESCRIPTION("Texas Instruments ADC081C/ADC101C/ADC121C driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
|
|
@ -714,19 +714,19 @@ static int vf610_write_raw(struct iio_dev *indio_dev,
|
|||
int i;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
for (i = 0;
|
||||
i < ARRAY_SIZE(info->sample_freq_avail);
|
||||
i++)
|
||||
if (val == info->sample_freq_avail[i]) {
|
||||
info->adc_feature.sample_rate = i;
|
||||
vf610_adc_sample_set(info);
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
for (i = 0;
|
||||
i < ARRAY_SIZE(info->sample_freq_avail);
|
||||
i++)
|
||||
if (val == info->sample_freq_avail[i]) {
|
||||
info->adc_feature.sample_rate = i;
|
||||
vf610_adc_sample_set(info);
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
|
|
|
@ -115,7 +115,7 @@ int hid_sensor_power_state(struct hid_sensor_common *st, bool state)
|
|||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
#else
|
||||
atomic_set(&st->user_requested_state, state);
|
||||
return _hid_sensor_power_state(st, state);
|
||||
|
|
|
@ -106,7 +106,7 @@ int ms_sensors_convert_and_read(void *cli, u8 conv, u8 rd,
|
|||
unsigned int delay, u32 *adc)
|
||||
{
|
||||
int ret;
|
||||
__be32 buf = 0;
|
||||
__be32 buf = 0;
|
||||
struct i2c_client *client = (struct i2c_client *)cli;
|
||||
|
||||
/* Trigger conversion */
|
||||
|
|
|
@ -24,81 +24,30 @@
|
|||
|
||||
int st_sensors_get_buffer_element(struct iio_dev *indio_dev, u8 *buf)
|
||||
{
|
||||
u8 *addr;
|
||||
int i, n = 0, len;
|
||||
int i, len;
|
||||
int total = 0;
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
unsigned int num_data_channels = sdata->num_data_channels;
|
||||
unsigned int byte_for_channel =
|
||||
indio_dev->channels[0].scan_type.storagebits >> 3;
|
||||
|
||||
addr = kmalloc(num_data_channels, GFP_KERNEL);
|
||||
if (!addr) {
|
||||
len = -ENOMEM;
|
||||
goto st_sensors_get_buffer_element_error;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_data_channels; i++) {
|
||||
unsigned int bytes_to_read;
|
||||
|
||||
if (test_bit(i, indio_dev->active_scan_mask)) {
|
||||
addr[n] = indio_dev->channels[i].address;
|
||||
n++;
|
||||
bytes_to_read = indio_dev->channels[i].scan_type.storagebits >> 3;
|
||||
len = sdata->tf->read_multiple_byte(&sdata->tb,
|
||||
sdata->dev, indio_dev->channels[i].address,
|
||||
bytes_to_read,
|
||||
buf + total, sdata->multiread_bit);
|
||||
|
||||
if (len < bytes_to_read)
|
||||
return -EIO;
|
||||
|
||||
/* Advance the buffer pointer */
|
||||
total += len;
|
||||
}
|
||||
}
|
||||
switch (n) {
|
||||
case 1:
|
||||
len = sdata->tf->read_multiple_byte(&sdata->tb, sdata->dev,
|
||||
addr[0], byte_for_channel, buf, sdata->multiread_bit);
|
||||
break;
|
||||
case 2:
|
||||
if ((addr[1] - addr[0]) == byte_for_channel) {
|
||||
len = sdata->tf->read_multiple_byte(&sdata->tb,
|
||||
sdata->dev, addr[0], byte_for_channel * n,
|
||||
buf, sdata->multiread_bit);
|
||||
} else {
|
||||
u8 *rx_array;
|
||||
rx_array = kmalloc(byte_for_channel * num_data_channels,
|
||||
GFP_KERNEL);
|
||||
if (!rx_array) {
|
||||
len = -ENOMEM;
|
||||
goto st_sensors_free_memory;
|
||||
}
|
||||
|
||||
len = sdata->tf->read_multiple_byte(&sdata->tb,
|
||||
sdata->dev, addr[0],
|
||||
byte_for_channel * num_data_channels,
|
||||
rx_array, sdata->multiread_bit);
|
||||
if (len < 0) {
|
||||
kfree(rx_array);
|
||||
goto st_sensors_free_memory;
|
||||
}
|
||||
|
||||
for (i = 0; i < n * byte_for_channel; i++) {
|
||||
if (i < n)
|
||||
buf[i] = rx_array[i];
|
||||
else
|
||||
buf[i] = rx_array[n + i];
|
||||
}
|
||||
kfree(rx_array);
|
||||
len = byte_for_channel * n;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
len = sdata->tf->read_multiple_byte(&sdata->tb, sdata->dev,
|
||||
addr[0], byte_for_channel * num_data_channels,
|
||||
buf, sdata->multiread_bit);
|
||||
break;
|
||||
default:
|
||||
len = -EINVAL;
|
||||
goto st_sensors_free_memory;
|
||||
}
|
||||
if (len != byte_for_channel * n) {
|
||||
len = -EIO;
|
||||
goto st_sensors_free_memory;
|
||||
}
|
||||
|
||||
st_sensors_free_memory:
|
||||
kfree(addr);
|
||||
st_sensors_get_buffer_element_error:
|
||||
return len;
|
||||
return total;
|
||||
}
|
||||
EXPORT_SYMBOL(st_sensors_get_buffer_element);
|
||||
|
||||
|
@ -109,6 +58,24 @@ irqreturn_t st_sensors_trigger_handler(int irq, void *p)
|
|||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
|
||||
/* If we have a status register, check if this IRQ came from us */
|
||||
if (sdata->sensor_settings->drdy_irq.addr_stat_drdy) {
|
||||
u8 status;
|
||||
|
||||
len = sdata->tf->read_byte(&sdata->tb, sdata->dev,
|
||||
sdata->sensor_settings->drdy_irq.addr_stat_drdy,
|
||||
&status);
|
||||
if (len < 0)
|
||||
dev_err(sdata->dev, "could not read channel status\n");
|
||||
|
||||
/*
|
||||
* If this was not caused by any channels on this sensor,
|
||||
* return IRQ_NONE
|
||||
*/
|
||||
if (!(status & (u8)indio_dev->active_scan_mask[0]))
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
len = st_sensors_get_buffer_element(indio_dev, sdata->buffer_data);
|
||||
if (len < 0)
|
||||
goto st_sensors_get_buffer_element_error;
|
||||
|
|
|
@ -301,6 +301,14 @@ static int st_sensors_set_drdy_int_pin(struct iio_dev *indio_dev,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (pdata->open_drain) {
|
||||
if (!sdata->sensor_settings->drdy_irq.addr_od)
|
||||
dev_err(&indio_dev->dev,
|
||||
"open drain requested but unsupported.\n");
|
||||
else
|
||||
sdata->int_pin_open_drain = true;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -321,6 +329,8 @@ static struct st_sensors_platform_data *st_sensors_of_probe(struct device *dev,
|
|||
else
|
||||
pdata->drdy_int_pin = defdata ? defdata->drdy_int_pin : 0;
|
||||
|
||||
pdata->open_drain = of_property_read_bool(np, "drive-open-drain");
|
||||
|
||||
return pdata;
|
||||
}
|
||||
#else
|
||||
|
@ -374,6 +384,16 @@ int st_sensors_init_sensor(struct iio_dev *indio_dev,
|
|||
return err;
|
||||
}
|
||||
|
||||
if (sdata->int_pin_open_drain) {
|
||||
dev_info(&indio_dev->dev,
|
||||
"set interrupt line to open drain mode\n");
|
||||
err = st_sensors_write_data_with_mask(indio_dev,
|
||||
sdata->sensor_settings->drdy_irq.addr_od,
|
||||
sdata->sensor_settings->drdy_irq.mask_od, 1);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
err = st_sensors_set_axis_enable(indio_dev, ST_SENSORS_ENABLE_ALL_AXIS);
|
||||
|
||||
return err;
|
||||
|
|
|
@ -64,6 +64,19 @@ int st_sensors_allocate_trigger(struct iio_dev *indio_dev,
|
|||
"rising edge\n", irq_trig);
|
||||
irq_trig = IRQF_TRIGGER_RISING;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the interrupt pin is Open Drain, by definition this
|
||||
* means that the interrupt line may be shared with other
|
||||
* peripherals. But to do this we also need to have a status
|
||||
* register and mask to figure out if this sensor was firing
|
||||
* the IRQ or not, so we can tell the interrupt handle that
|
||||
* it was "our" interrupt.
|
||||
*/
|
||||
if (sdata->int_pin_open_drain &&
|
||||
sdata->sensor_settings->drdy_irq.addr_stat_drdy)
|
||||
irq_trig |= IRQF_SHARED;
|
||||
|
||||
err = request_threaded_irq(irq,
|
||||
iio_trigger_generic_data_rdy_poll,
|
||||
NULL,
|
||||
|
|
|
@ -74,6 +74,33 @@ config AD5449
|
|||
To compile this driver as a module, choose M here: the
|
||||
module will be called ad5449.
|
||||
|
||||
config AD5592R_BASE
|
||||
tristate
|
||||
|
||||
config AD5592R
|
||||
tristate "Analog Devices AD5592R ADC/DAC driver"
|
||||
depends on SPI_MASTER
|
||||
select GPIOLIB
|
||||
select AD5592R_BASE
|
||||
help
|
||||
Say yes here to build support for Analog Devices AD5592R
|
||||
Digital to Analog / Analog to Digital Converter.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ad5592r.
|
||||
|
||||
config AD5593R
|
||||
tristate "Analog Devices AD5593R ADC/DAC driver"
|
||||
depends on I2C
|
||||
select GPIOLIB
|
||||
select AD5592R_BASE
|
||||
help
|
||||
Say yes here to build support for Analog Devices AD5593R
|
||||
Digital to Analog / Analog to Digital Converter.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ad5593r.
|
||||
|
||||
config AD5504
|
||||
tristate "Analog Devices AD5504/AD5501 DAC SPI driver"
|
||||
depends on SPI
|
||||
|
@ -154,6 +181,16 @@ config AD7303
|
|||
To compile this driver as module choose M here: the module will be called
|
||||
ad7303.
|
||||
|
||||
config LPC18XX_DAC
|
||||
tristate "NXP LPC18xx DAC driver"
|
||||
depends on ARCH_LPC18XX || COMPILE_TEST
|
||||
depends on OF && HAS_IOMEM
|
||||
help
|
||||
Say yes here to build support for NXP LPC18XX DAC.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called lpc18xx_dac.
|
||||
|
||||
config M62332
|
||||
tristate "Mitsubishi M62332 DAC driver"
|
||||
depends on I2C
|
||||
|
|
|
@ -11,12 +11,16 @@ obj-$(CONFIG_AD5064) += ad5064.o
|
|||
obj-$(CONFIG_AD5504) += ad5504.o
|
||||
obj-$(CONFIG_AD5446) += ad5446.o
|
||||
obj-$(CONFIG_AD5449) += ad5449.o
|
||||
obj-$(CONFIG_AD5592R_BASE) += ad5592r-base.o
|
||||
obj-$(CONFIG_AD5592R) += ad5592r.o
|
||||
obj-$(CONFIG_AD5593R) += ad5593r.o
|
||||
obj-$(CONFIG_AD5755) += ad5755.o
|
||||
obj-$(CONFIG_AD5761) += ad5761.o
|
||||
obj-$(CONFIG_AD5764) += ad5764.o
|
||||
obj-$(CONFIG_AD5791) += ad5791.o
|
||||
obj-$(CONFIG_AD5686) += ad5686.o
|
||||
obj-$(CONFIG_AD7303) += ad7303.o
|
||||
obj-$(CONFIG_LPC18XX_DAC) += lpc18xx_dac.o
|
||||
obj-$(CONFIG_M62332) += m62332.o
|
||||
obj-$(CONFIG_MAX517) += max517.o
|
||||
obj-$(CONFIG_MAX5821) += max5821.o
|
||||
|
|
|
@ -0,0 +1,691 @@
|
|||
/*
|
||||
* AD5592R Digital <-> Analog converters driver
|
||||
*
|
||||
* Copyright 2014-2016 Analog Devices Inc.
|
||||
* Author: Paul Cercueil <paul.cercueil@analog.com>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/property.h>
|
||||
|
||||
#include <dt-bindings/iio/adi,ad5592r.h>
|
||||
|
||||
#include "ad5592r-base.h"
|
||||
|
||||
static int ad5592r_gpio_get(struct gpio_chip *chip, unsigned offset)
|
||||
{
|
||||
struct ad5592r_state *st = gpiochip_get_data(chip);
|
||||
int ret = 0;
|
||||
u8 val;
|
||||
|
||||
mutex_lock(&st->gpio_lock);
|
||||
|
||||
if (st->gpio_out & BIT(offset))
|
||||
val = st->gpio_val;
|
||||
else
|
||||
ret = st->ops->gpio_read(st, &val);
|
||||
|
||||
mutex_unlock(&st->gpio_lock);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return !!(val & BIT(offset));
|
||||
}
|
||||
|
||||
static void ad5592r_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
|
||||
{
|
||||
struct ad5592r_state *st = gpiochip_get_data(chip);
|
||||
|
||||
mutex_lock(&st->gpio_lock);
|
||||
|
||||
if (value)
|
||||
st->gpio_val |= BIT(offset);
|
||||
else
|
||||
st->gpio_val &= ~BIT(offset);
|
||||
|
||||
st->ops->reg_write(st, AD5592R_REG_GPIO_SET, st->gpio_val);
|
||||
|
||||
mutex_unlock(&st->gpio_lock);
|
||||
}
|
||||
|
||||
static int ad5592r_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
|
||||
{
|
||||
struct ad5592r_state *st = gpiochip_get_data(chip);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&st->gpio_lock);
|
||||
|
||||
st->gpio_out &= ~BIT(offset);
|
||||
st->gpio_in |= BIT(offset);
|
||||
|
||||
ret = st->ops->reg_write(st, AD5592R_REG_GPIO_OUT_EN, st->gpio_out);
|
||||
if (ret < 0)
|
||||
goto err_unlock;
|
||||
|
||||
ret = st->ops->reg_write(st, AD5592R_REG_GPIO_IN_EN, st->gpio_in);
|
||||
|
||||
err_unlock:
|
||||
mutex_unlock(&st->gpio_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad5592r_gpio_direction_output(struct gpio_chip *chip,
|
||||
unsigned offset, int value)
|
||||
{
|
||||
struct ad5592r_state *st = gpiochip_get_data(chip);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&st->gpio_lock);
|
||||
|
||||
if (value)
|
||||
st->gpio_val |= BIT(offset);
|
||||
else
|
||||
st->gpio_val &= ~BIT(offset);
|
||||
|
||||
st->gpio_in &= ~BIT(offset);
|
||||
st->gpio_out |= BIT(offset);
|
||||
|
||||
ret = st->ops->reg_write(st, AD5592R_REG_GPIO_SET, st->gpio_val);
|
||||
if (ret < 0)
|
||||
goto err_unlock;
|
||||
|
||||
ret = st->ops->reg_write(st, AD5592R_REG_GPIO_OUT_EN, st->gpio_out);
|
||||
if (ret < 0)
|
||||
goto err_unlock;
|
||||
|
||||
ret = st->ops->reg_write(st, AD5592R_REG_GPIO_IN_EN, st->gpio_in);
|
||||
|
||||
err_unlock:
|
||||
mutex_unlock(&st->gpio_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad5592r_gpio_request(struct gpio_chip *chip, unsigned offset)
|
||||
{
|
||||
struct ad5592r_state *st = gpiochip_get_data(chip);
|
||||
|
||||
if (!(st->gpio_map & BIT(offset))) {
|
||||
dev_err(st->dev, "GPIO %d is reserved by alternate function\n",
|
||||
offset);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ad5592r_gpio_init(struct ad5592r_state *st)
|
||||
{
|
||||
if (!st->gpio_map)
|
||||
return 0;
|
||||
|
||||
st->gpiochip.label = dev_name(st->dev);
|
||||
st->gpiochip.base = -1;
|
||||
st->gpiochip.ngpio = 8;
|
||||
st->gpiochip.parent = st->dev;
|
||||
st->gpiochip.can_sleep = true;
|
||||
st->gpiochip.direction_input = ad5592r_gpio_direction_input;
|
||||
st->gpiochip.direction_output = ad5592r_gpio_direction_output;
|
||||
st->gpiochip.get = ad5592r_gpio_get;
|
||||
st->gpiochip.set = ad5592r_gpio_set;
|
||||
st->gpiochip.request = ad5592r_gpio_request;
|
||||
st->gpiochip.owner = THIS_MODULE;
|
||||
|
||||
mutex_init(&st->gpio_lock);
|
||||
|
||||
return gpiochip_add_data(&st->gpiochip, st);
|
||||
}
|
||||
|
||||
static void ad5592r_gpio_cleanup(struct ad5592r_state *st)
|
||||
{
|
||||
if (st->gpio_map)
|
||||
gpiochip_remove(&st->gpiochip);
|
||||
}
|
||||
|
||||
static int ad5592r_reset(struct ad5592r_state *st)
|
||||
{
|
||||
struct gpio_desc *gpio;
|
||||
struct iio_dev *iio_dev = iio_priv_to_dev(st);
|
||||
|
||||
gpio = devm_gpiod_get_optional(st->dev, "reset", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(gpio))
|
||||
return PTR_ERR(gpio);
|
||||
|
||||
if (gpio) {
|
||||
udelay(1);
|
||||
gpiod_set_value(gpio, 1);
|
||||
} else {
|
||||
mutex_lock(&iio_dev->mlock);
|
||||
/* Writing this magic value resets the device */
|
||||
st->ops->reg_write(st, AD5592R_REG_RESET, 0xdac);
|
||||
mutex_unlock(&iio_dev->mlock);
|
||||
}
|
||||
|
||||
udelay(250);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ad5592r_get_vref(struct ad5592r_state *st)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (st->reg) {
|
||||
ret = regulator_get_voltage(st->reg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return ret / 1000;
|
||||
} else {
|
||||
return 2500;
|
||||
}
|
||||
}
|
||||
|
||||
static int ad5592r_set_channel_modes(struct ad5592r_state *st)
|
||||
{
|
||||
const struct ad5592r_rw_ops *ops = st->ops;
|
||||
int ret;
|
||||
unsigned i;
|
||||
struct iio_dev *iio_dev = iio_priv_to_dev(st);
|
||||
u8 pulldown = 0, tristate = 0, dac = 0, adc = 0;
|
||||
u16 read_back;
|
||||
|
||||
for (i = 0; i < st->num_channels; i++) {
|
||||
switch (st->channel_modes[i]) {
|
||||
case CH_MODE_DAC:
|
||||
dac |= BIT(i);
|
||||
break;
|
||||
|
||||
case CH_MODE_ADC:
|
||||
adc |= BIT(i);
|
||||
break;
|
||||
|
||||
case CH_MODE_DAC_AND_ADC:
|
||||
dac |= BIT(i);
|
||||
adc |= BIT(i);
|
||||
break;
|
||||
|
||||
case CH_MODE_GPIO:
|
||||
st->gpio_map |= BIT(i);
|
||||
st->gpio_in |= BIT(i); /* Default to input */
|
||||
break;
|
||||
|
||||
case CH_MODE_UNUSED:
|
||||
/* fall-through */
|
||||
default:
|
||||
switch (st->channel_offstate[i]) {
|
||||
case CH_OFFSTATE_OUT_TRISTATE:
|
||||
tristate |= BIT(i);
|
||||
break;
|
||||
|
||||
case CH_OFFSTATE_OUT_LOW:
|
||||
st->gpio_out |= BIT(i);
|
||||
break;
|
||||
|
||||
case CH_OFFSTATE_OUT_HIGH:
|
||||
st->gpio_out |= BIT(i);
|
||||
st->gpio_val |= BIT(i);
|
||||
break;
|
||||
|
||||
case CH_OFFSTATE_PULLDOWN:
|
||||
/* fall-through */
|
||||
default:
|
||||
pulldown |= BIT(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mutex_lock(&iio_dev->mlock);
|
||||
|
||||
/* Pull down unused pins to GND */
|
||||
ret = ops->reg_write(st, AD5592R_REG_PULLDOWN, pulldown);
|
||||
if (ret)
|
||||
goto err_unlock;
|
||||
|
||||
ret = ops->reg_write(st, AD5592R_REG_TRISTATE, tristate);
|
||||
if (ret)
|
||||
goto err_unlock;
|
||||
|
||||
/* Configure pins that we use */
|
||||
ret = ops->reg_write(st, AD5592R_REG_DAC_EN, dac);
|
||||
if (ret)
|
||||
goto err_unlock;
|
||||
|
||||
ret = ops->reg_write(st, AD5592R_REG_ADC_EN, adc);
|
||||
if (ret)
|
||||
goto err_unlock;
|
||||
|
||||
ret = ops->reg_write(st, AD5592R_REG_GPIO_SET, st->gpio_val);
|
||||
if (ret)
|
||||
goto err_unlock;
|
||||
|
||||
ret = ops->reg_write(st, AD5592R_REG_GPIO_OUT_EN, st->gpio_out);
|
||||
if (ret)
|
||||
goto err_unlock;
|
||||
|
||||
ret = ops->reg_write(st, AD5592R_REG_GPIO_IN_EN, st->gpio_in);
|
||||
if (ret)
|
||||
goto err_unlock;
|
||||
|
||||
/* Verify that we can read back at least one register */
|
||||
ret = ops->reg_read(st, AD5592R_REG_ADC_EN, &read_back);
|
||||
if (!ret && (read_back & 0xff) != adc)
|
||||
ret = -EIO;
|
||||
|
||||
err_unlock:
|
||||
mutex_unlock(&iio_dev->mlock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad5592r_reset_channel_modes(struct ad5592r_state *st)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(st->channel_modes); i++)
|
||||
st->channel_modes[i] = CH_MODE_UNUSED;
|
||||
|
||||
return ad5592r_set_channel_modes(st);
|
||||
}
|
||||
|
||||
static int ad5592r_write_raw(struct iio_dev *iio_dev,
|
||||
struct iio_chan_spec const *chan, int val, int val2, long mask)
|
||||
{
|
||||
struct ad5592r_state *st = iio_priv(iio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
|
||||
if (val >= (1 << chan->scan_type.realbits) || val < 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (!chan->output)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&iio_dev->mlock);
|
||||
ret = st->ops->write_dac(st, chan->channel, val);
|
||||
if (!ret)
|
||||
st->cached_dac[chan->channel] = val;
|
||||
mutex_unlock(&iio_dev->mlock);
|
||||
return ret;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
if (chan->type == IIO_VOLTAGE) {
|
||||
bool gain;
|
||||
|
||||
if (val == st->scale_avail[0][0] &&
|
||||
val2 == st->scale_avail[0][1])
|
||||
gain = false;
|
||||
else if (val == st->scale_avail[1][0] &&
|
||||
val2 == st->scale_avail[1][1])
|
||||
gain = true;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&iio_dev->mlock);
|
||||
|
||||
ret = st->ops->reg_read(st, AD5592R_REG_CTRL,
|
||||
&st->cached_gp_ctrl);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&iio_dev->mlock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (chan->output) {
|
||||
if (gain)
|
||||
st->cached_gp_ctrl |=
|
||||
AD5592R_REG_CTRL_DAC_RANGE;
|
||||
else
|
||||
st->cached_gp_ctrl &=
|
||||
~AD5592R_REG_CTRL_DAC_RANGE;
|
||||
} else {
|
||||
if (gain)
|
||||
st->cached_gp_ctrl |=
|
||||
AD5592R_REG_CTRL_ADC_RANGE;
|
||||
else
|
||||
st->cached_gp_ctrl &=
|
||||
~AD5592R_REG_CTRL_ADC_RANGE;
|
||||
}
|
||||
|
||||
ret = st->ops->reg_write(st, AD5592R_REG_CTRL,
|
||||
st->cached_gp_ctrl);
|
||||
mutex_unlock(&iio_dev->mlock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ad5592r_read_raw(struct iio_dev *iio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long m)
|
||||
{
|
||||
struct ad5592r_state *st = iio_priv(iio_dev);
|
||||
u16 read_val;
|
||||
int ret;
|
||||
|
||||
switch (m) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
mutex_lock(&iio_dev->mlock);
|
||||
|
||||
if (!chan->output) {
|
||||
ret = st->ops->read_adc(st, chan->channel, &read_val);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
|
||||
if ((read_val >> 12 & 0x7) != (chan->channel & 0x7)) {
|
||||
dev_err(st->dev, "Error while reading channel %u\n",
|
||||
chan->channel);
|
||||
ret = -EIO;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
read_val &= GENMASK(11, 0);
|
||||
|
||||
} else {
|
||||
read_val = st->cached_dac[chan->channel];
|
||||
}
|
||||
|
||||
dev_dbg(st->dev, "Channel %u read: 0x%04hX\n",
|
||||
chan->channel, read_val);
|
||||
|
||||
*val = (int) read_val;
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = ad5592r_get_vref(st);
|
||||
|
||||
if (chan->type == IIO_TEMP) {
|
||||
s64 tmp = *val * (3767897513LL / 25LL);
|
||||
*val = div_s64_rem(tmp, 1000000000LL, val2);
|
||||
|
||||
ret = IIO_VAL_INT_PLUS_MICRO;
|
||||
} else {
|
||||
int mult;
|
||||
|
||||
mutex_lock(&iio_dev->mlock);
|
||||
|
||||
if (chan->output)
|
||||
mult = !!(st->cached_gp_ctrl &
|
||||
AD5592R_REG_CTRL_DAC_RANGE);
|
||||
else
|
||||
mult = !!(st->cached_gp_ctrl &
|
||||
AD5592R_REG_CTRL_ADC_RANGE);
|
||||
|
||||
*val *= ++mult;
|
||||
|
||||
*val2 = chan->scan_type.realbits;
|
||||
ret = IIO_VAL_FRACTIONAL_LOG2;
|
||||
}
|
||||
break;
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
ret = ad5592r_get_vref(st);
|
||||
|
||||
mutex_lock(&iio_dev->mlock);
|
||||
|
||||
if (st->cached_gp_ctrl & AD5592R_REG_CTRL_ADC_RANGE)
|
||||
*val = (-34365 * 25) / ret;
|
||||
else
|
||||
*val = (-75365 * 25) / ret;
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&iio_dev->mlock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad5592r_write_raw_get_fmt(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, long mask)
|
||||
{
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
return IIO_VAL_INT_PLUS_NANO;
|
||||
|
||||
default:
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct iio_info ad5592r_info = {
|
||||
.read_raw = ad5592r_read_raw,
|
||||
.write_raw = ad5592r_write_raw,
|
||||
.write_raw_get_fmt = ad5592r_write_raw_get_fmt,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static ssize_t ad5592r_show_scale_available(struct iio_dev *iio_dev,
|
||||
uintptr_t private,
|
||||
const struct iio_chan_spec *chan,
|
||||
char *buf)
|
||||
{
|
||||
struct ad5592r_state *st = iio_priv(iio_dev);
|
||||
|
||||
return sprintf(buf, "%d.%09u %d.%09u\n",
|
||||
st->scale_avail[0][0], st->scale_avail[0][1],
|
||||
st->scale_avail[1][0], st->scale_avail[1][1]);
|
||||
}
|
||||
|
||||
static struct iio_chan_spec_ext_info ad5592r_ext_info[] = {
|
||||
{
|
||||
.name = "scale_available",
|
||||
.read = ad5592r_show_scale_available,
|
||||
.shared = true,
|
||||
},
|
||||
{},
|
||||
};
|
||||
|
||||
static void ad5592r_setup_channel(struct iio_dev *iio_dev,
|
||||
struct iio_chan_spec *chan, bool output, unsigned id)
|
||||
{
|
||||
chan->type = IIO_VOLTAGE;
|
||||
chan->indexed = 1;
|
||||
chan->output = output;
|
||||
chan->channel = id;
|
||||
chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
|
||||
chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE);
|
||||
chan->scan_type.sign = 'u';
|
||||
chan->scan_type.realbits = 12;
|
||||
chan->scan_type.storagebits = 16;
|
||||
chan->ext_info = ad5592r_ext_info;
|
||||
}
|
||||
|
||||
static int ad5592r_alloc_channels(struct ad5592r_state *st)
|
||||
{
|
||||
unsigned i, curr_channel = 0,
|
||||
num_channels = st->num_channels;
|
||||
struct iio_dev *iio_dev = iio_priv_to_dev(st);
|
||||
struct iio_chan_spec *channels;
|
||||
struct fwnode_handle *child;
|
||||
u32 reg, tmp;
|
||||
int ret;
|
||||
|
||||
device_for_each_child_node(st->dev, child) {
|
||||
ret = fwnode_property_read_u32(child, "reg", ®);
|
||||
if (ret || reg > ARRAY_SIZE(st->channel_modes))
|
||||
continue;
|
||||
|
||||
ret = fwnode_property_read_u32(child, "adi,mode", &tmp);
|
||||
if (!ret)
|
||||
st->channel_modes[reg] = tmp;
|
||||
|
||||
fwnode_property_read_u32(child, "adi,off-state", &tmp);
|
||||
if (!ret)
|
||||
st->channel_offstate[reg] = tmp;
|
||||
}
|
||||
|
||||
channels = devm_kzalloc(st->dev,
|
||||
(1 + 2 * num_channels) * sizeof(*channels), GFP_KERNEL);
|
||||
if (!channels)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < num_channels; i++) {
|
||||
switch (st->channel_modes[i]) {
|
||||
case CH_MODE_DAC:
|
||||
ad5592r_setup_channel(iio_dev, &channels[curr_channel],
|
||||
true, i);
|
||||
curr_channel++;
|
||||
break;
|
||||
|
||||
case CH_MODE_ADC:
|
||||
ad5592r_setup_channel(iio_dev, &channels[curr_channel],
|
||||
false, i);
|
||||
curr_channel++;
|
||||
break;
|
||||
|
||||
case CH_MODE_DAC_AND_ADC:
|
||||
ad5592r_setup_channel(iio_dev, &channels[curr_channel],
|
||||
true, i);
|
||||
curr_channel++;
|
||||
ad5592r_setup_channel(iio_dev, &channels[curr_channel],
|
||||
false, i);
|
||||
curr_channel++;
|
||||
break;
|
||||
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
channels[curr_channel].type = IIO_TEMP;
|
||||
channels[curr_channel].channel = 8;
|
||||
channels[curr_channel].info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_OFFSET);
|
||||
curr_channel++;
|
||||
|
||||
iio_dev->num_channels = curr_channel;
|
||||
iio_dev->channels = channels;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ad5592r_init_scales(struct ad5592r_state *st, int vref_mV)
|
||||
{
|
||||
s64 tmp = (s64)vref_mV * 1000000000LL >> 12;
|
||||
|
||||
st->scale_avail[0][0] =
|
||||
div_s64_rem(tmp, 1000000000LL, &st->scale_avail[0][1]);
|
||||
st->scale_avail[1][0] =
|
||||
div_s64_rem(tmp * 2, 1000000000LL, &st->scale_avail[1][1]);
|
||||
}
|
||||
|
||||
int ad5592r_probe(struct device *dev, const char *name,
|
||||
const struct ad5592r_rw_ops *ops)
|
||||
{
|
||||
struct iio_dev *iio_dev;
|
||||
struct ad5592r_state *st;
|
||||
int ret;
|
||||
|
||||
iio_dev = devm_iio_device_alloc(dev, sizeof(*st));
|
||||
if (!iio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
st = iio_priv(iio_dev);
|
||||
st->dev = dev;
|
||||
st->ops = ops;
|
||||
st->num_channels = 8;
|
||||
dev_set_drvdata(dev, iio_dev);
|
||||
|
||||
st->reg = devm_regulator_get_optional(dev, "vref");
|
||||
if (IS_ERR(st->reg)) {
|
||||
if ((PTR_ERR(st->reg) != -ENODEV) && dev->of_node)
|
||||
return PTR_ERR(st->reg);
|
||||
|
||||
st->reg = NULL;
|
||||
} else {
|
||||
ret = regulator_enable(st->reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
iio_dev->dev.parent = dev;
|
||||
iio_dev->name = name;
|
||||
iio_dev->info = &ad5592r_info;
|
||||
iio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
ad5592r_init_scales(st, ad5592r_get_vref(st));
|
||||
|
||||
ret = ad5592r_reset(st);
|
||||
if (ret)
|
||||
goto error_disable_reg;
|
||||
|
||||
ret = ops->reg_write(st, AD5592R_REG_PD,
|
||||
(st->reg == NULL) ? AD5592R_REG_PD_EN_REF : 0);
|
||||
if (ret)
|
||||
goto error_disable_reg;
|
||||
|
||||
ret = ad5592r_alloc_channels(st);
|
||||
if (ret)
|
||||
goto error_disable_reg;
|
||||
|
||||
ret = ad5592r_set_channel_modes(st);
|
||||
if (ret)
|
||||
goto error_reset_ch_modes;
|
||||
|
||||
ret = iio_device_register(iio_dev);
|
||||
if (ret)
|
||||
goto error_reset_ch_modes;
|
||||
|
||||
ret = ad5592r_gpio_init(st);
|
||||
if (ret)
|
||||
goto error_dev_unregister;
|
||||
|
||||
return 0;
|
||||
|
||||
error_dev_unregister:
|
||||
iio_device_unregister(iio_dev);
|
||||
|
||||
error_reset_ch_modes:
|
||||
ad5592r_reset_channel_modes(st);
|
||||
|
||||
error_disable_reg:
|
||||
if (st->reg)
|
||||
regulator_disable(st->reg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ad5592r_probe);
|
||||
|
||||
int ad5592r_remove(struct device *dev)
|
||||
{
|
||||
struct iio_dev *iio_dev = dev_get_drvdata(dev);
|
||||
struct ad5592r_state *st = iio_priv(iio_dev);
|
||||
|
||||
iio_device_unregister(iio_dev);
|
||||
ad5592r_reset_channel_modes(st);
|
||||
ad5592r_gpio_cleanup(st);
|
||||
|
||||
if (st->reg)
|
||||
regulator_disable(st->reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ad5592r_remove);
|
||||
|
||||
MODULE_AUTHOR("Paul Cercueil <paul.cercueil@analog.com>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD5592R multi-channel converters");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* AD5592R / AD5593R Digital <-> Analog converters driver
|
||||
*
|
||||
* Copyright 2015-2016 Analog Devices Inc.
|
||||
* Author: Paul Cercueil <paul.cercueil@analog.com>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#ifndef __DRIVERS_IIO_DAC_AD5592R_BASE_H__
|
||||
#define __DRIVERS_IIO_DAC_AD5592R_BASE_H__
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/cache.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
|
||||
struct device;
|
||||
struct ad5592r_state;
|
||||
|
||||
enum ad5592r_registers {
|
||||
AD5592R_REG_NOOP = 0x0,
|
||||
AD5592R_REG_DAC_READBACK = 0x1,
|
||||
AD5592R_REG_ADC_SEQ = 0x2,
|
||||
AD5592R_REG_CTRL = 0x3,
|
||||
AD5592R_REG_ADC_EN = 0x4,
|
||||
AD5592R_REG_DAC_EN = 0x5,
|
||||
AD5592R_REG_PULLDOWN = 0x6,
|
||||
AD5592R_REG_LDAC = 0x7,
|
||||
AD5592R_REG_GPIO_OUT_EN = 0x8,
|
||||
AD5592R_REG_GPIO_SET = 0x9,
|
||||
AD5592R_REG_GPIO_IN_EN = 0xA,
|
||||
AD5592R_REG_PD = 0xB,
|
||||
AD5592R_REG_OPEN_DRAIN = 0xC,
|
||||
AD5592R_REG_TRISTATE = 0xD,
|
||||
AD5592R_REG_RESET = 0xF,
|
||||
};
|
||||
|
||||
#define AD5592R_REG_PD_EN_REF BIT(9)
|
||||
#define AD5592R_REG_CTRL_ADC_RANGE BIT(5)
|
||||
#define AD5592R_REG_CTRL_DAC_RANGE BIT(4)
|
||||
|
||||
struct ad5592r_rw_ops {
|
||||
int (*write_dac)(struct ad5592r_state *st, unsigned chan, u16 value);
|
||||
int (*read_adc)(struct ad5592r_state *st, unsigned chan, u16 *value);
|
||||
int (*reg_write)(struct ad5592r_state *st, u8 reg, u16 value);
|
||||
int (*reg_read)(struct ad5592r_state *st, u8 reg, u16 *value);
|
||||
int (*gpio_read)(struct ad5592r_state *st, u8 *value);
|
||||
};
|
||||
|
||||
struct ad5592r_state {
|
||||
struct device *dev;
|
||||
struct regulator *reg;
|
||||
struct gpio_chip gpiochip;
|
||||
struct mutex gpio_lock; /* Protect cached gpio_out, gpio_val, etc. */
|
||||
unsigned int num_channels;
|
||||
const struct ad5592r_rw_ops *ops;
|
||||
int scale_avail[2][2];
|
||||
u16 cached_dac[8];
|
||||
u16 cached_gp_ctrl;
|
||||
u8 channel_modes[8];
|
||||
u8 channel_offstate[8];
|
||||
u8 gpio_map;
|
||||
u8 gpio_out;
|
||||
u8 gpio_in;
|
||||
u8 gpio_val;
|
||||
|
||||
__be16 spi_msg ____cacheline_aligned;
|
||||
__be16 spi_msg_nop;
|
||||
};
|
||||
|
||||
int ad5592r_probe(struct device *dev, const char *name,
|
||||
const struct ad5592r_rw_ops *ops);
|
||||
int ad5592r_remove(struct device *dev);
|
||||
|
||||
#endif /* __DRIVERS_IIO_DAC_AD5592R_BASE_H__ */
|
|
@ -0,0 +1,164 @@
|
|||
/*
|
||||
* AD5592R Digital <-> Analog converters driver
|
||||
*
|
||||
* Copyright 2015-2016 Analog Devices Inc.
|
||||
* Author: Paul Cercueil <paul.cercueil@analog.com>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include "ad5592r-base.h"
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#define AD5592R_GPIO_READBACK_EN BIT(10)
|
||||
#define AD5592R_LDAC_READBACK_EN BIT(6)
|
||||
|
||||
static int ad5592r_spi_wnop_r16(struct ad5592r_state *st, u16 *buf)
|
||||
{
|
||||
struct spi_device *spi = container_of(st->dev, struct spi_device, dev);
|
||||
struct spi_transfer t = {
|
||||
.tx_buf = &st->spi_msg_nop,
|
||||
.rx_buf = buf,
|
||||
.len = 2
|
||||
};
|
||||
|
||||
st->spi_msg_nop = 0; /* NOP */
|
||||
|
||||
return spi_sync_transfer(spi, &t, 1);
|
||||
}
|
||||
|
||||
static int ad5592r_write_dac(struct ad5592r_state *st, unsigned chan, u16 value)
|
||||
{
|
||||
struct spi_device *spi = container_of(st->dev, struct spi_device, dev);
|
||||
|
||||
st->spi_msg = cpu_to_be16(BIT(15) | (chan << 12) | value);
|
||||
|
||||
return spi_write(spi, &st->spi_msg, sizeof(st->spi_msg));
|
||||
}
|
||||
|
||||
static int ad5592r_read_adc(struct ad5592r_state *st, unsigned chan, u16 *value)
|
||||
{
|
||||
struct spi_device *spi = container_of(st->dev, struct spi_device, dev);
|
||||
int ret;
|
||||
|
||||
st->spi_msg = cpu_to_be16((AD5592R_REG_ADC_SEQ << 11) | BIT(chan));
|
||||
|
||||
ret = spi_write(spi, &st->spi_msg, sizeof(st->spi_msg));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Invalid data:
|
||||
* See Figure 40. Single-Channel ADC Conversion Sequence
|
||||
*/
|
||||
ret = ad5592r_spi_wnop_r16(st, &st->spi_msg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ad5592r_spi_wnop_r16(st, &st->spi_msg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*value = be16_to_cpu(st->spi_msg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ad5592r_reg_write(struct ad5592r_state *st, u8 reg, u16 value)
|
||||
{
|
||||
struct spi_device *spi = container_of(st->dev, struct spi_device, dev);
|
||||
|
||||
st->spi_msg = cpu_to_be16((reg << 11) | value);
|
||||
|
||||
return spi_write(spi, &st->spi_msg, sizeof(st->spi_msg));
|
||||
}
|
||||
|
||||
static int ad5592r_reg_read(struct ad5592r_state *st, u8 reg, u16 *value)
|
||||
{
|
||||
struct spi_device *spi = container_of(st->dev, struct spi_device, dev);
|
||||
int ret;
|
||||
|
||||
st->spi_msg = cpu_to_be16((AD5592R_REG_LDAC << 11) |
|
||||
AD5592R_LDAC_READBACK_EN | (reg << 2));
|
||||
|
||||
ret = spi_write(spi, &st->spi_msg, sizeof(st->spi_msg));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ad5592r_spi_wnop_r16(st, &st->spi_msg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*value = be16_to_cpu(st->spi_msg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ad5593r_gpio_read(struct ad5592r_state *st, u8 *value)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ad5592r_reg_write(st, AD5592R_REG_GPIO_IN_EN,
|
||||
AD5592R_GPIO_READBACK_EN | st->gpio_in);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ad5592r_spi_wnop_r16(st, &st->spi_msg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*value = (u8) be16_to_cpu(st->spi_msg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct ad5592r_rw_ops ad5592r_rw_ops = {
|
||||
.write_dac = ad5592r_write_dac,
|
||||
.read_adc = ad5592r_read_adc,
|
||||
.reg_write = ad5592r_reg_write,
|
||||
.reg_read = ad5592r_reg_read,
|
||||
.gpio_read = ad5593r_gpio_read,
|
||||
};
|
||||
|
||||
static int ad5592r_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
const struct spi_device_id *id = spi_get_device_id(spi);
|
||||
|
||||
return ad5592r_probe(&spi->dev, id->name, &ad5592r_rw_ops);
|
||||
}
|
||||
|
||||
static int ad5592r_spi_remove(struct spi_device *spi)
|
||||
{
|
||||
return ad5592r_remove(&spi->dev);
|
||||
}
|
||||
|
||||
static const struct spi_device_id ad5592r_spi_ids[] = {
|
||||
{ .name = "ad5592r", },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ad5592r_spi_ids);
|
||||
|
||||
static const struct of_device_id ad5592r_of_match[] = {
|
||||
{ .compatible = "adi,ad5592r", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ad5592r_of_match);
|
||||
|
||||
static struct spi_driver ad5592r_spi_driver = {
|
||||
.driver = {
|
||||
.name = "ad5592r",
|
||||
.of_match_table = of_match_ptr(ad5592r_of_match),
|
||||
},
|
||||
.probe = ad5592r_spi_probe,
|
||||
.remove = ad5592r_spi_remove,
|
||||
.id_table = ad5592r_spi_ids,
|
||||
};
|
||||
module_spi_driver(ad5592r_spi_driver);
|
||||
|
||||
MODULE_AUTHOR("Paul Cercueil <paul.cercueil@analog.com>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD5592R multi-channel converters");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -0,0 +1,131 @@
|
|||
/*
|
||||
* AD5593R Digital <-> Analog converters driver
|
||||
*
|
||||
* Copyright 2015-2016 Analog Devices Inc.
|
||||
* Author: Paul Cercueil <paul.cercueil@analog.com>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include "ad5592r-base.h"
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#define AD5593R_MODE_CONF (0 << 4)
|
||||
#define AD5593R_MODE_DAC_WRITE (1 << 4)
|
||||
#define AD5593R_MODE_ADC_READBACK (4 << 4)
|
||||
#define AD5593R_MODE_DAC_READBACK (5 << 4)
|
||||
#define AD5593R_MODE_GPIO_READBACK (6 << 4)
|
||||
#define AD5593R_MODE_REG_READBACK (7 << 4)
|
||||
|
||||
static int ad5593r_write_dac(struct ad5592r_state *st, unsigned chan, u16 value)
|
||||
{
|
||||
struct i2c_client *i2c = to_i2c_client(st->dev);
|
||||
|
||||
return i2c_smbus_write_word_swapped(i2c,
|
||||
AD5593R_MODE_DAC_WRITE | chan, value);
|
||||
}
|
||||
|
||||
static int ad5593r_read_adc(struct ad5592r_state *st, unsigned chan, u16 *value)
|
||||
{
|
||||
struct i2c_client *i2c = to_i2c_client(st->dev);
|
||||
s32 val;
|
||||
|
||||
val = i2c_smbus_write_word_swapped(i2c,
|
||||
AD5593R_MODE_CONF | AD5592R_REG_ADC_SEQ, BIT(chan));
|
||||
if (val < 0)
|
||||
return (int) val;
|
||||
|
||||
val = i2c_smbus_read_word_swapped(i2c, AD5593R_MODE_ADC_READBACK);
|
||||
if (val < 0)
|
||||
return (int) val;
|
||||
|
||||
*value = (u16) val;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ad5593r_reg_write(struct ad5592r_state *st, u8 reg, u16 value)
|
||||
{
|
||||
struct i2c_client *i2c = to_i2c_client(st->dev);
|
||||
|
||||
return i2c_smbus_write_word_swapped(i2c,
|
||||
AD5593R_MODE_CONF | reg, value);
|
||||
}
|
||||
|
||||
static int ad5593r_reg_read(struct ad5592r_state *st, u8 reg, u16 *value)
|
||||
{
|
||||
struct i2c_client *i2c = to_i2c_client(st->dev);
|
||||
s32 val;
|
||||
|
||||
val = i2c_smbus_read_word_swapped(i2c, AD5593R_MODE_REG_READBACK | reg);
|
||||
if (val < 0)
|
||||
return (int) val;
|
||||
|
||||
*value = (u16) val;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ad5593r_gpio_read(struct ad5592r_state *st, u8 *value)
|
||||
{
|
||||
struct i2c_client *i2c = to_i2c_client(st->dev);
|
||||
s32 val;
|
||||
|
||||
val = i2c_smbus_read_word_swapped(i2c, AD5593R_MODE_GPIO_READBACK);
|
||||
if (val < 0)
|
||||
return (int) val;
|
||||
|
||||
*value = (u8) val;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct ad5592r_rw_ops ad5593r_rw_ops = {
|
||||
.write_dac = ad5593r_write_dac,
|
||||
.read_adc = ad5593r_read_adc,
|
||||
.reg_write = ad5593r_reg_write,
|
||||
.reg_read = ad5593r_reg_read,
|
||||
.gpio_read = ad5593r_gpio_read,
|
||||
};
|
||||
|
||||
static int ad5593r_i2c_probe(struct i2c_client *i2c,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
return ad5592r_probe(&i2c->dev, id->name, &ad5593r_rw_ops);
|
||||
}
|
||||
|
||||
static int ad5593r_i2c_remove(struct i2c_client *i2c)
|
||||
{
|
||||
return ad5592r_remove(&i2c->dev);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id ad5593r_i2c_ids[] = {
|
||||
{ .name = "ad5593r", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ad5593r_i2c_ids);
|
||||
|
||||
static const struct of_device_id ad5593r_of_match[] = {
|
||||
{ .compatible = "adi,ad5593r", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ad5593r_of_match);
|
||||
|
||||
static struct i2c_driver ad5593r_driver = {
|
||||
.driver = {
|
||||
.name = "ad5593r",
|
||||
.of_match_table = of_match_ptr(ad5593r_of_match),
|
||||
},
|
||||
.probe = ad5593r_i2c_probe,
|
||||
.remove = ad5593r_i2c_remove,
|
||||
.id_table = ad5593r_i2c_ids,
|
||||
};
|
||||
module_i2c_driver(ad5593r_driver);
|
||||
|
||||
MODULE_AUTHOR("Paul Cercueil <paul.cercueil@analog.com>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD5592R multi-channel converters");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -0,0 +1,210 @@
|
|||
/*
|
||||
* IIO DAC driver for NXP LPC18xx DAC
|
||||
*
|
||||
* Copyright (C) 2016 Joachim Eastwood <manabian@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* UNSUPPORTED hardware features:
|
||||
* - Interrupts
|
||||
* - DMA
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/driver.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
/* LPC18XX DAC registers and bits */
|
||||
#define LPC18XX_DAC_CR 0x000
|
||||
#define LPC18XX_DAC_CR_VALUE_SHIFT 6
|
||||
#define LPC18XX_DAC_CR_VALUE_MASK 0x3ff
|
||||
#define LPC18XX_DAC_CR_BIAS BIT(16)
|
||||
#define LPC18XX_DAC_CTRL 0x004
|
||||
#define LPC18XX_DAC_CTRL_DMA_ENA BIT(3)
|
||||
|
||||
struct lpc18xx_dac {
|
||||
struct regulator *vref;
|
||||
void __iomem *base;
|
||||
struct mutex lock;
|
||||
struct clk *clk;
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec lpc18xx_dac_iio_channels[] = {
|
||||
{
|
||||
.type = IIO_VOLTAGE,
|
||||
.output = 1,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
},
|
||||
};
|
||||
|
||||
static int lpc18xx_dac_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct lpc18xx_dac *dac = iio_priv(indio_dev);
|
||||
u32 reg;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
reg = readl(dac->base + LPC18XX_DAC_CR);
|
||||
*val = reg >> LPC18XX_DAC_CR_VALUE_SHIFT;
|
||||
*val &= LPC18XX_DAC_CR_VALUE_MASK;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = regulator_get_voltage(dac->vref) / 1000;
|
||||
*val2 = 10;
|
||||
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int lpc18xx_dac_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
struct lpc18xx_dac *dac = iio_priv(indio_dev);
|
||||
u32 reg;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (val < 0 || val > LPC18XX_DAC_CR_VALUE_MASK)
|
||||
return -EINVAL;
|
||||
|
||||
reg = LPC18XX_DAC_CR_BIAS;
|
||||
reg |= val << LPC18XX_DAC_CR_VALUE_SHIFT;
|
||||
|
||||
mutex_lock(&dac->lock);
|
||||
writel(reg, dac->base + LPC18XX_DAC_CR);
|
||||
writel(LPC18XX_DAC_CTRL_DMA_ENA, dac->base + LPC18XX_DAC_CTRL);
|
||||
mutex_unlock(&dac->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct iio_info lpc18xx_dac_info = {
|
||||
.read_raw = lpc18xx_dac_read_raw,
|
||||
.write_raw = lpc18xx_dac_write_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int lpc18xx_dac_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct lpc18xx_dac *dac;
|
||||
struct resource *res;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*dac));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, indio_dev);
|
||||
dac = iio_priv(indio_dev);
|
||||
mutex_init(&dac->lock);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
dac->base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(dac->base))
|
||||
return PTR_ERR(dac->base);
|
||||
|
||||
dac->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(dac->clk)) {
|
||||
dev_err(&pdev->dev, "error getting clock\n");
|
||||
return PTR_ERR(dac->clk);
|
||||
}
|
||||
|
||||
dac->vref = devm_regulator_get(&pdev->dev, "vref");
|
||||
if (IS_ERR(dac->vref)) {
|
||||
dev_err(&pdev->dev, "error getting regulator\n");
|
||||
return PTR_ERR(dac->vref);
|
||||
}
|
||||
|
||||
indio_dev->name = dev_name(&pdev->dev);
|
||||
indio_dev->dev.parent = &pdev->dev;
|
||||
indio_dev->info = &lpc18xx_dac_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = lpc18xx_dac_iio_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(lpc18xx_dac_iio_channels);
|
||||
|
||||
ret = regulator_enable(dac->vref);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "unable to enable regulator\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(dac->clk);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "unable to enable clock\n");
|
||||
goto dis_reg;
|
||||
}
|
||||
|
||||
writel(0, dac->base + LPC18XX_DAC_CTRL);
|
||||
writel(0, dac->base + LPC18XX_DAC_CR);
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "unable to register device\n");
|
||||
goto dis_clk;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
dis_clk:
|
||||
clk_disable_unprepare(dac->clk);
|
||||
dis_reg:
|
||||
regulator_disable(dac->vref);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int lpc18xx_dac_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
struct lpc18xx_dac *dac = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
writel(0, dac->base + LPC18XX_DAC_CTRL);
|
||||
clk_disable_unprepare(dac->clk);
|
||||
regulator_disable(dac->vref);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id lpc18xx_dac_match[] = {
|
||||
{ .compatible = "nxp,lpc1850-dac" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, lpc18xx_dac_match);
|
||||
|
||||
static struct platform_driver lpc18xx_dac_driver = {
|
||||
.probe = lpc18xx_dac_probe,
|
||||
.remove = lpc18xx_dac_remove,
|
||||
.driver = {
|
||||
.name = "lpc18xx-dac",
|
||||
.of_match_table = lpc18xx_dac_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(lpc18xx_dac_driver);
|
||||
|
||||
MODULE_DESCRIPTION("LPC18xx DAC driver");
|
||||
MODULE_AUTHOR("Joachim Eastwood <manabian@gmail.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -284,7 +284,7 @@ struct ad9523_state {
|
|||
} data[2] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
static int ad9523_read(struct iio_dev *indio_dev, unsigned addr)
|
||||
static int ad9523_read(struct iio_dev *indio_dev, unsigned int addr)
|
||||
{
|
||||
struct ad9523_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
@ -318,7 +318,8 @@ static int ad9523_read(struct iio_dev *indio_dev, unsigned addr)
|
|||
return ret;
|
||||
};
|
||||
|
||||
static int ad9523_write(struct iio_dev *indio_dev, unsigned addr, unsigned val)
|
||||
static int ad9523_write(struct iio_dev *indio_dev,
|
||||
unsigned int addr, unsigned int val)
|
||||
{
|
||||
struct ad9523_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
@ -351,11 +352,11 @@ static int ad9523_io_update(struct iio_dev *indio_dev)
|
|||
}
|
||||
|
||||
static int ad9523_vco_out_map(struct iio_dev *indio_dev,
|
||||
unsigned ch, unsigned out)
|
||||
unsigned int ch, unsigned int out)
|
||||
{
|
||||
struct ad9523_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
unsigned mask;
|
||||
unsigned int mask;
|
||||
|
||||
switch (ch) {
|
||||
case 0 ... 3:
|
||||
|
@ -405,7 +406,7 @@ static int ad9523_vco_out_map(struct iio_dev *indio_dev,
|
|||
}
|
||||
|
||||
static int ad9523_set_clock_provider(struct iio_dev *indio_dev,
|
||||
unsigned ch, unsigned long freq)
|
||||
unsigned int ch, unsigned long freq)
|
||||
{
|
||||
struct ad9523_state *st = iio_priv(indio_dev);
|
||||
long tmp1, tmp2;
|
||||
|
@ -619,7 +620,7 @@ static int ad9523_read_raw(struct iio_dev *indio_dev,
|
|||
long m)
|
||||
{
|
||||
struct ad9523_state *st = iio_priv(indio_dev);
|
||||
unsigned code;
|
||||
unsigned int code;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
|
@ -655,7 +656,7 @@ static int ad9523_write_raw(struct iio_dev *indio_dev,
|
|||
long mask)
|
||||
{
|
||||
struct ad9523_state *st = iio_priv(indio_dev);
|
||||
unsigned reg;
|
||||
unsigned int reg;
|
||||
int ret, tmp, code;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
|
@ -709,8 +710,8 @@ static int ad9523_write_raw(struct iio_dev *indio_dev,
|
|||
}
|
||||
|
||||
static int ad9523_reg_access(struct iio_dev *indio_dev,
|
||||
unsigned reg, unsigned writeval,
|
||||
unsigned *readval)
|
||||
unsigned int reg, unsigned int writeval,
|
||||
unsigned int *readval)
|
||||
{
|
||||
int ret;
|
||||
|
||||
|
|
|
@ -93,7 +93,7 @@ config IIO_ST_GYRO_3AXIS
|
|||
select IIO_TRIGGERED_BUFFER if (IIO_BUFFER)
|
||||
help
|
||||
Say yes here to build support for STMicroelectronics gyroscopes:
|
||||
L3G4200D, LSM330DL, L3GD20, LSM330DLC, L3G4IS, LSM330.
|
||||
L3G4200D, LSM330DL, L3GD20, LSM330DLC, L3G4IS, LSM330, LSM9DS0.
|
||||
|
||||
This driver can also be built as a module. If so, these modules
|
||||
will be created:
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
#include <linux/delay.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/iio/iio.h>
|
||||
|
@ -31,7 +30,6 @@
|
|||
#include "bmg160.h"
|
||||
|
||||
#define BMG160_IRQ_NAME "bmg160_event"
|
||||
#define BMG160_GPIO_NAME "gpio_int"
|
||||
|
||||
#define BMG160_REG_CHIP_ID 0x00
|
||||
#define BMG160_CHIP_ID_VAL 0x0F
|
||||
|
@ -97,7 +95,6 @@
|
|||
#define BMG160_AUTO_SUSPEND_DELAY_MS 2000
|
||||
|
||||
struct bmg160_data {
|
||||
struct device *dev;
|
||||
struct regmap *regmap;
|
||||
struct iio_trigger *dready_trig;
|
||||
struct iio_trigger *motion_trig;
|
||||
|
@ -116,6 +113,7 @@ enum bmg160_axis {
|
|||
AXIS_X,
|
||||
AXIS_Y,
|
||||
AXIS_Z,
|
||||
AXIS_MAX,
|
||||
};
|
||||
|
||||
static const struct {
|
||||
|
@ -138,11 +136,12 @@ static const struct {
|
|||
|
||||
static int bmg160_set_mode(struct bmg160_data *data, u8 mode)
|
||||
{
|
||||
struct device *dev = regmap_get_device(data->regmap);
|
||||
int ret;
|
||||
|
||||
ret = regmap_write(data->regmap, BMG160_REG_PMU_LPW, mode);
|
||||
if (ret < 0) {
|
||||
dev_err(data->dev, "Error writing reg_pmu_lpw\n");
|
||||
dev_err(dev, "Error writing reg_pmu_lpw\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -163,6 +162,7 @@ static int bmg160_convert_freq_to_bit(int val)
|
|||
|
||||
static int bmg160_set_bw(struct bmg160_data *data, int val)
|
||||
{
|
||||
struct device *dev = regmap_get_device(data->regmap);
|
||||
int ret;
|
||||
int bw_bits;
|
||||
|
||||
|
@ -172,7 +172,7 @@ static int bmg160_set_bw(struct bmg160_data *data, int val)
|
|||
|
||||
ret = regmap_write(data->regmap, BMG160_REG_PMU_BW, bw_bits);
|
||||
if (ret < 0) {
|
||||
dev_err(data->dev, "Error writing reg_pmu_bw\n");
|
||||
dev_err(dev, "Error writing reg_pmu_bw\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -183,18 +183,19 @@ static int bmg160_set_bw(struct bmg160_data *data, int val)
|
|||
|
||||
static int bmg160_chip_init(struct bmg160_data *data)
|
||||
{
|
||||
struct device *dev = regmap_get_device(data->regmap);
|
||||
int ret;
|
||||
unsigned int val;
|
||||
|
||||
ret = regmap_read(data->regmap, BMG160_REG_CHIP_ID, &val);
|
||||
if (ret < 0) {
|
||||
dev_err(data->dev, "Error reading reg_chip_id\n");
|
||||
dev_err(dev, "Error reading reg_chip_id\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_dbg(data->dev, "Chip Id %x\n", val);
|
||||
dev_dbg(dev, "Chip Id %x\n", val);
|
||||
if (val != BMG160_CHIP_ID_VAL) {
|
||||
dev_err(data->dev, "invalid chip %x\n", val);
|
||||
dev_err(dev, "invalid chip %x\n", val);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
|
@ -213,14 +214,14 @@ static int bmg160_chip_init(struct bmg160_data *data)
|
|||
/* Set Default Range */
|
||||
ret = regmap_write(data->regmap, BMG160_REG_RANGE, BMG160_RANGE_500DPS);
|
||||
if (ret < 0) {
|
||||
dev_err(data->dev, "Error writing reg_range\n");
|
||||
dev_err(dev, "Error writing reg_range\n");
|
||||
return ret;
|
||||
}
|
||||
data->dps_range = BMG160_RANGE_500DPS;
|
||||
|
||||
ret = regmap_read(data->regmap, BMG160_REG_SLOPE_THRES, &val);
|
||||
if (ret < 0) {
|
||||
dev_err(data->dev, "Error reading reg_slope_thres\n");
|
||||
dev_err(dev, "Error reading reg_slope_thres\n");
|
||||
return ret;
|
||||
}
|
||||
data->slope_thres = val;
|
||||
|
@ -229,7 +230,7 @@ static int bmg160_chip_init(struct bmg160_data *data)
|
|||
ret = regmap_update_bits(data->regmap, BMG160_REG_INT_EN_1,
|
||||
BMG160_INT1_BIT_OD, 0);
|
||||
if (ret < 0) {
|
||||
dev_err(data->dev, "Error updating bits in reg_int_en_1\n");
|
||||
dev_err(dev, "Error updating bits in reg_int_en_1\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -237,7 +238,7 @@ static int bmg160_chip_init(struct bmg160_data *data)
|
|||
BMG160_INT_MODE_LATCH_INT |
|
||||
BMG160_INT_MODE_LATCH_RESET);
|
||||
if (ret < 0) {
|
||||
dev_err(data->dev,
|
||||
dev_err(dev,
|
||||
"Error writing reg_motion_intr\n");
|
||||
return ret;
|
||||
}
|
||||
|
@ -248,20 +249,21 @@ static int bmg160_chip_init(struct bmg160_data *data)
|
|||
static int bmg160_set_power_state(struct bmg160_data *data, bool on)
|
||||
{
|
||||
#ifdef CONFIG_PM
|
||||
struct device *dev = regmap_get_device(data->regmap);
|
||||
int ret;
|
||||
|
||||
if (on)
|
||||
ret = pm_runtime_get_sync(data->dev);
|
||||
ret = pm_runtime_get_sync(dev);
|
||||
else {
|
||||
pm_runtime_mark_last_busy(data->dev);
|
||||
ret = pm_runtime_put_autosuspend(data->dev);
|
||||
pm_runtime_mark_last_busy(dev);
|
||||
ret = pm_runtime_put_autosuspend(dev);
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(data->dev,
|
||||
"Failed: bmg160_set_power_state for %d\n", on);
|
||||
dev_err(dev, "Failed: bmg160_set_power_state for %d\n", on);
|
||||
|
||||
if (on)
|
||||
pm_runtime_put_noidle(data->dev);
|
||||
pm_runtime_put_noidle(dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -273,6 +275,7 @@ static int bmg160_set_power_state(struct bmg160_data *data, bool on)
|
|||
static int bmg160_setup_any_motion_interrupt(struct bmg160_data *data,
|
||||
bool status)
|
||||
{
|
||||
struct device *dev = regmap_get_device(data->regmap);
|
||||
int ret;
|
||||
|
||||
/* Enable/Disable INT_MAP0 mapping */
|
||||
|
@ -280,7 +283,7 @@ static int bmg160_setup_any_motion_interrupt(struct bmg160_data *data,
|
|||
BMG160_INT_MAP_0_BIT_ANY,
|
||||
(status ? BMG160_INT_MAP_0_BIT_ANY : 0));
|
||||
if (ret < 0) {
|
||||
dev_err(data->dev, "Error updating bits reg_int_map0\n");
|
||||
dev_err(dev, "Error updating bits reg_int_map0\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -290,8 +293,7 @@ static int bmg160_setup_any_motion_interrupt(struct bmg160_data *data,
|
|||
ret = regmap_write(data->regmap, BMG160_REG_SLOPE_THRES,
|
||||
data->slope_thres);
|
||||
if (ret < 0) {
|
||||
dev_err(data->dev,
|
||||
"Error writing reg_slope_thres\n");
|
||||
dev_err(dev, "Error writing reg_slope_thres\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -299,8 +301,7 @@ static int bmg160_setup_any_motion_interrupt(struct bmg160_data *data,
|
|||
BMG160_INT_MOTION_X | BMG160_INT_MOTION_Y |
|
||||
BMG160_INT_MOTION_Z);
|
||||
if (ret < 0) {
|
||||
dev_err(data->dev,
|
||||
"Error writing reg_motion_intr\n");
|
||||
dev_err(dev, "Error writing reg_motion_intr\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -315,8 +316,7 @@ static int bmg160_setup_any_motion_interrupt(struct bmg160_data *data,
|
|||
BMG160_INT_MODE_LATCH_INT |
|
||||
BMG160_INT_MODE_LATCH_RESET);
|
||||
if (ret < 0) {
|
||||
dev_err(data->dev,
|
||||
"Error writing reg_rst_latch\n");
|
||||
dev_err(dev, "Error writing reg_rst_latch\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
@ -329,7 +329,7 @@ static int bmg160_setup_any_motion_interrupt(struct bmg160_data *data,
|
|||
}
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(data->dev, "Error writing reg_int_en0\n");
|
||||
dev_err(dev, "Error writing reg_int_en0\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -339,6 +339,7 @@ static int bmg160_setup_any_motion_interrupt(struct bmg160_data *data,
|
|||
static int bmg160_setup_new_data_interrupt(struct bmg160_data *data,
|
||||
bool status)
|
||||
{
|
||||
struct device *dev = regmap_get_device(data->regmap);
|
||||
int ret;
|
||||
|
||||
/* Enable/Disable INT_MAP1 mapping */
|
||||
|
@ -346,7 +347,7 @@ static int bmg160_setup_new_data_interrupt(struct bmg160_data *data,
|
|||
BMG160_INT_MAP_1_BIT_NEW_DATA,
|
||||
(status ? BMG160_INT_MAP_1_BIT_NEW_DATA : 0));
|
||||
if (ret < 0) {
|
||||
dev_err(data->dev, "Error updating bits in reg_int_map1\n");
|
||||
dev_err(dev, "Error updating bits in reg_int_map1\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -355,9 +356,8 @@ static int bmg160_setup_new_data_interrupt(struct bmg160_data *data,
|
|||
BMG160_INT_MODE_NON_LATCH_INT |
|
||||
BMG160_INT_MODE_LATCH_RESET);
|
||||
if (ret < 0) {
|
||||
dev_err(data->dev,
|
||||
"Error writing reg_rst_latch\n");
|
||||
return ret;
|
||||
dev_err(dev, "Error writing reg_rst_latch\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regmap_write(data->regmap, BMG160_REG_INT_EN_0,
|
||||
|
@ -369,16 +369,15 @@ static int bmg160_setup_new_data_interrupt(struct bmg160_data *data,
|
|||
BMG160_INT_MODE_LATCH_INT |
|
||||
BMG160_INT_MODE_LATCH_RESET);
|
||||
if (ret < 0) {
|
||||
dev_err(data->dev,
|
||||
"Error writing reg_rst_latch\n");
|
||||
return ret;
|
||||
dev_err(dev, "Error writing reg_rst_latch\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regmap_write(data->regmap, BMG160_REG_INT_EN_0, 0);
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(data->dev, "Error writing reg_int_en0\n");
|
||||
dev_err(dev, "Error writing reg_int_en0\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -401,6 +400,7 @@ static int bmg160_get_bw(struct bmg160_data *data, int *val)
|
|||
|
||||
static int bmg160_set_scale(struct bmg160_data *data, int val)
|
||||
{
|
||||
struct device *dev = regmap_get_device(data->regmap);
|
||||
int ret, i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(bmg160_scale_table); ++i) {
|
||||
|
@ -408,8 +408,7 @@ static int bmg160_set_scale(struct bmg160_data *data, int val)
|
|||
ret = regmap_write(data->regmap, BMG160_REG_RANGE,
|
||||
bmg160_scale_table[i].dps_range);
|
||||
if (ret < 0) {
|
||||
dev_err(data->dev,
|
||||
"Error writing reg_range\n");
|
||||
dev_err(dev, "Error writing reg_range\n");
|
||||
return ret;
|
||||
}
|
||||
data->dps_range = bmg160_scale_table[i].dps_range;
|
||||
|
@ -422,6 +421,7 @@ static int bmg160_set_scale(struct bmg160_data *data, int val)
|
|||
|
||||
static int bmg160_get_temp(struct bmg160_data *data, int *val)
|
||||
{
|
||||
struct device *dev = regmap_get_device(data->regmap);
|
||||
int ret;
|
||||
unsigned int raw_val;
|
||||
|
||||
|
@ -434,7 +434,7 @@ static int bmg160_get_temp(struct bmg160_data *data, int *val)
|
|||
|
||||
ret = regmap_read(data->regmap, BMG160_REG_TEMP, &raw_val);
|
||||
if (ret < 0) {
|
||||
dev_err(data->dev, "Error reading reg_temp\n");
|
||||
dev_err(dev, "Error reading reg_temp\n");
|
||||
bmg160_set_power_state(data, false);
|
||||
mutex_unlock(&data->mutex);
|
||||
return ret;
|
||||
|
@ -451,6 +451,7 @@ static int bmg160_get_temp(struct bmg160_data *data, int *val)
|
|||
|
||||
static int bmg160_get_axis(struct bmg160_data *data, int axis, int *val)
|
||||
{
|
||||
struct device *dev = regmap_get_device(data->regmap);
|
||||
int ret;
|
||||
__le16 raw_val;
|
||||
|
||||
|
@ -464,7 +465,7 @@ static int bmg160_get_axis(struct bmg160_data *data, int axis, int *val)
|
|||
ret = regmap_bulk_read(data->regmap, BMG160_AXIS_TO_REG(axis), &raw_val,
|
||||
sizeof(raw_val));
|
||||
if (ret < 0) {
|
||||
dev_err(data->dev, "Error reading axis %d\n", axis);
|
||||
dev_err(dev, "Error reading axis %d\n", axis);
|
||||
bmg160_set_power_state(data, false);
|
||||
mutex_unlock(&data->mutex);
|
||||
return ret;
|
||||
|
@ -764,26 +765,23 @@ static const struct iio_info bmg160_info = {
|
|||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static const unsigned long bmg160_accel_scan_masks[] = {
|
||||
BIT(AXIS_X) | BIT(AXIS_Y) | BIT(AXIS_Z),
|
||||
0};
|
||||
|
||||
static irqreturn_t bmg160_trigger_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct bmg160_data *data = iio_priv(indio_dev);
|
||||
int bit, ret, i = 0;
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&data->mutex);
|
||||
for_each_set_bit(bit, indio_dev->active_scan_mask,
|
||||
indio_dev->masklength) {
|
||||
ret = regmap_bulk_read(data->regmap, BMG160_AXIS_TO_REG(bit),
|
||||
&val, 2);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&data->mutex);
|
||||
goto err;
|
||||
}
|
||||
data->buffer[i++] = val;
|
||||
}
|
||||
ret = regmap_bulk_read(data->regmap, BMG160_REG_XOUT_L,
|
||||
data->buffer, AXIS_MAX * 2);
|
||||
mutex_unlock(&data->mutex);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
|
||||
pf->timestamp);
|
||||
|
@ -797,6 +795,7 @@ static int bmg160_trig_try_reen(struct iio_trigger *trig)
|
|||
{
|
||||
struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
|
||||
struct bmg160_data *data = iio_priv(indio_dev);
|
||||
struct device *dev = regmap_get_device(data->regmap);
|
||||
int ret;
|
||||
|
||||
/* new data interrupts don't need ack */
|
||||
|
@ -808,7 +807,7 @@ static int bmg160_trig_try_reen(struct iio_trigger *trig)
|
|||
BMG160_INT_MODE_LATCH_INT |
|
||||
BMG160_INT_MODE_LATCH_RESET);
|
||||
if (ret < 0) {
|
||||
dev_err(data->dev, "Error writing reg_rst_latch\n");
|
||||
dev_err(dev, "Error writing reg_rst_latch\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -868,13 +867,14 @@ static irqreturn_t bmg160_event_handler(int irq, void *private)
|
|||
{
|
||||
struct iio_dev *indio_dev = private;
|
||||
struct bmg160_data *data = iio_priv(indio_dev);
|
||||
struct device *dev = regmap_get_device(data->regmap);
|
||||
int ret;
|
||||
int dir;
|
||||
unsigned int val;
|
||||
|
||||
ret = regmap_read(data->regmap, BMG160_REG_INT_STATUS_2, &val);
|
||||
if (ret < 0) {
|
||||
dev_err(data->dev, "Error reading reg_int_status2\n");
|
||||
dev_err(dev, "Error reading reg_int_status2\n");
|
||||
goto ack_intr_status;
|
||||
}
|
||||
|
||||
|
@ -911,8 +911,7 @@ static irqreturn_t bmg160_event_handler(int irq, void *private)
|
|||
BMG160_INT_MODE_LATCH_INT |
|
||||
BMG160_INT_MODE_LATCH_RESET);
|
||||
if (ret < 0)
|
||||
dev_err(data->dev,
|
||||
"Error writing reg_rst_latch\n");
|
||||
dev_err(dev, "Error writing reg_rst_latch\n");
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
|
@ -956,29 +955,6 @@ static const struct iio_buffer_setup_ops bmg160_buffer_setup_ops = {
|
|||
.postdisable = bmg160_buffer_postdisable,
|
||||
};
|
||||
|
||||
static int bmg160_gpio_probe(struct bmg160_data *data)
|
||||
|
||||
{
|
||||
struct device *dev;
|
||||
struct gpio_desc *gpio;
|
||||
|
||||
dev = data->dev;
|
||||
|
||||
/* data ready gpio interrupt pin */
|
||||
gpio = devm_gpiod_get_index(dev, BMG160_GPIO_NAME, 0, GPIOD_IN);
|
||||
if (IS_ERR(gpio)) {
|
||||
dev_err(dev, "acpi gpio get index failed\n");
|
||||
return PTR_ERR(gpio);
|
||||
}
|
||||
|
||||
data->irq = gpiod_to_irq(gpio);
|
||||
|
||||
dev_dbg(dev, "GPIO resource, no:%d irq:%d\n", desc_to_gpio(gpio),
|
||||
data->irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *bmg160_match_acpi_device(struct device *dev)
|
||||
{
|
||||
const struct acpi_device_id *id;
|
||||
|
@ -1003,7 +979,6 @@ int bmg160_core_probe(struct device *dev, struct regmap *regmap, int irq,
|
|||
|
||||
data = iio_priv(indio_dev);
|
||||
dev_set_drvdata(dev, indio_dev);
|
||||
data->dev = dev;
|
||||
data->irq = irq;
|
||||
data->regmap = regmap;
|
||||
|
||||
|
@ -1020,12 +995,10 @@ int bmg160_core_probe(struct device *dev, struct regmap *regmap, int irq,
|
|||
indio_dev->channels = bmg160_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(bmg160_channels);
|
||||
indio_dev->name = name;
|
||||
indio_dev->available_scan_masks = bmg160_accel_scan_masks;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &bmg160_info;
|
||||
|
||||
if (data->irq <= 0)
|
||||
bmg160_gpio_probe(data);
|
||||
|
||||
if (data->irq > 0) {
|
||||
ret = devm_request_threaded_irq(dev,
|
||||
data->irq,
|
||||
|
@ -1168,7 +1141,7 @@ static int bmg160_runtime_suspend(struct device *dev)
|
|||
|
||||
ret = bmg160_set_mode(data, BMG160_MODE_SUSPEND);
|
||||
if (ret < 0) {
|
||||
dev_err(data->dev, "set mode failed\n");
|
||||
dev_err(dev, "set mode failed\n");
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#define L3GD20_GYRO_DEV_NAME "l3gd20"
|
||||
#define L3G4IS_GYRO_DEV_NAME "l3g4is_ui"
|
||||
#define LSM330_GYRO_DEV_NAME "lsm330_gyro"
|
||||
#define LSM9DS0_GYRO_DEV_NAME "lsm9ds0_gyro"
|
||||
|
||||
/**
|
||||
* struct st_sensors_platform_data - gyro platform data
|
||||
|
|
|
@ -190,6 +190,7 @@ static const struct st_sensor_settings st_gyro_sensors_settings[] = {
|
|||
* drain settings, but only for INT1 and not
|
||||
* for the DRDY line on INT2.
|
||||
*/
|
||||
.addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR,
|
||||
},
|
||||
.multi_read_bit = ST_GYRO_1_MULTIREAD_BIT,
|
||||
.bootime = 2,
|
||||
|
@ -203,6 +204,7 @@ static const struct st_sensor_settings st_gyro_sensors_settings[] = {
|
|||
[2] = LSM330DLC_GYRO_DEV_NAME,
|
||||
[3] = L3G4IS_GYRO_DEV_NAME,
|
||||
[4] = LSM330_GYRO_DEV_NAME,
|
||||
[5] = LSM9DS0_GYRO_DEV_NAME,
|
||||
},
|
||||
.ch = (struct iio_chan_spec *)st_gyro_16bit_channels,
|
||||
.odr = {
|
||||
|
@ -258,6 +260,7 @@ static const struct st_sensor_settings st_gyro_sensors_settings[] = {
|
|||
* drain settings, but only for INT1 and not
|
||||
* for the DRDY line on INT2.
|
||||
*/
|
||||
.addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR,
|
||||
},
|
||||
.multi_read_bit = ST_GYRO_2_MULTIREAD_BIT,
|
||||
.bootime = 2,
|
||||
|
@ -322,6 +325,7 @@ static const struct st_sensor_settings st_gyro_sensors_settings[] = {
|
|||
* drain settings, but only for INT1 and not
|
||||
* for the DRDY line on INT2.
|
||||
*/
|
||||
.addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR,
|
||||
},
|
||||
.multi_read_bit = ST_GYRO_3_MULTIREAD_BIT,
|
||||
.bootime = 2,
|
||||
|
|
|
@ -48,6 +48,10 @@ static const struct of_device_id st_gyro_of_match[] = {
|
|||
.compatible = "st,lsm330-gyro",
|
||||
.data = LSM330_GYRO_DEV_NAME,
|
||||
},
|
||||
{
|
||||
.compatible = "st,lsm9ds0-gyro",
|
||||
.data = LSM9DS0_GYRO_DEV_NAME,
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, st_gyro_of_match);
|
||||
|
@ -93,6 +97,7 @@ static const struct i2c_device_id st_gyro_id_table[] = {
|
|||
{ L3GD20_GYRO_DEV_NAME },
|
||||
{ L3G4IS_GYRO_DEV_NAME },
|
||||
{ LSM330_GYRO_DEV_NAME },
|
||||
{ LSM9DS0_GYRO_DEV_NAME },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, st_gyro_id_table);
|
||||
|
|
|
@ -54,6 +54,7 @@ static const struct spi_device_id st_gyro_id_table[] = {
|
|||
{ L3GD20_GYRO_DEV_NAME },
|
||||
{ L3G4IS_GYRO_DEV_NAME },
|
||||
{ LSM330_GYRO_DEV_NAME },
|
||||
{ LSM9DS0_GYRO_DEV_NAME },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, st_gyro_id_table);
|
||||
|
|
|
@ -3,6 +3,16 @@
|
|||
#
|
||||
menu "Humidity sensors"
|
||||
|
||||
config AM2315
|
||||
tristate "Aosong AM2315 relative humidity and temperature sensor"
|
||||
depends on I2C
|
||||
help
|
||||
If you say yes here you get support for the Aosong AM2315
|
||||
relative humidity and ambient temperature sensor.
|
||||
|
||||
This driver can also be built as a module. If so, the module will
|
||||
be called am2315.
|
||||
|
||||
config DHT11
|
||||
tristate "DHT11 (and compatible sensors) driver"
|
||||
depends on GPIOLIB || COMPILE_TEST
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
# Makefile for IIO humidity sensor drivers
|
||||
#
|
||||
|
||||
obj-$(CONFIG_AM2315) += am2315.o
|
||||
obj-$(CONFIG_DHT11) += dht11.o
|
||||
obj-$(CONFIG_HDC100X) += hdc100x.o
|
||||
obj-$(CONFIG_HTU21) += htu21.o
|
||||
|
|
|
@ -0,0 +1,303 @@
|
|||
/**
|
||||
* Aosong AM2315 relative humidity and temperature
|
||||
*
|
||||
* Copyright (c) 2016, Intel Corporation.
|
||||
*
|
||||
* This file is subject to the terms and conditions of version 2 of
|
||||
* the GNU General Public License. See the file COPYING in the main
|
||||
* directory of this archive for more details.
|
||||
*
|
||||
* 7-bit I2C address: 0x5C.
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
|
||||
#define AM2315_REG_HUM_MSB 0x00
|
||||
#define AM2315_REG_HUM_LSB 0x01
|
||||
#define AM2315_REG_TEMP_MSB 0x02
|
||||
#define AM2315_REG_TEMP_LSB 0x03
|
||||
|
||||
#define AM2315_FUNCTION_READ 0x03
|
||||
#define AM2315_HUM_OFFSET 2
|
||||
#define AM2315_TEMP_OFFSET 4
|
||||
#define AM2315_ALL_CHANNEL_MASK GENMASK(1, 0)
|
||||
|
||||
#define AM2315_DRIVER_NAME "am2315"
|
||||
|
||||
struct am2315_data {
|
||||
struct i2c_client *client;
|
||||
struct mutex lock;
|
||||
s16 buffer[8]; /* 2x16-bit channels + 2x16 padding + 4x16 timestamp */
|
||||
};
|
||||
|
||||
struct am2315_sensor_data {
|
||||
s16 hum_data;
|
||||
s16 temp_data;
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec am2315_channels[] = {
|
||||
{
|
||||
.type = IIO_HUMIDITYRELATIVE,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
.scan_index = 0,
|
||||
.scan_type = {
|
||||
.sign = 's',
|
||||
.realbits = 16,
|
||||
.storagebits = 16,
|
||||
.endianness = IIO_CPU,
|
||||
},
|
||||
},
|
||||
{
|
||||
.type = IIO_TEMP,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
.scan_index = 1,
|
||||
.scan_type = {
|
||||
.sign = 's',
|
||||
.realbits = 16,
|
||||
.storagebits = 16,
|
||||
.endianness = IIO_CPU,
|
||||
},
|
||||
},
|
||||
IIO_CHAN_SOFT_TIMESTAMP(2),
|
||||
};
|
||||
|
||||
/* CRC calculation algorithm, as specified in the datasheet (page 13). */
|
||||
static u16 am2315_crc(u8 *data, u8 nr_bytes)
|
||||
{
|
||||
int i;
|
||||
u16 crc = 0xffff;
|
||||
|
||||
while (nr_bytes--) {
|
||||
crc ^= *data++;
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (crc & 0x01) {
|
||||
crc >>= 1;
|
||||
crc ^= 0xA001;
|
||||
} else {
|
||||
crc >>= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
/* Simple function that sends a few bytes to the device to wake it up. */
|
||||
static void am2315_ping(struct i2c_client *client)
|
||||
{
|
||||
i2c_smbus_read_byte_data(client, AM2315_REG_HUM_MSB);
|
||||
}
|
||||
|
||||
static int am2315_read_data(struct am2315_data *data,
|
||||
struct am2315_sensor_data *sensor_data)
|
||||
{
|
||||
int ret;
|
||||
/* tx_buf format: <function code> <start addr> <nr of regs to read> */
|
||||
u8 tx_buf[3] = { AM2315_FUNCTION_READ, AM2315_REG_HUM_MSB, 4 };
|
||||
/*
|
||||
* rx_buf format:
|
||||
* <function code> <number of registers read>
|
||||
* <humidity MSB> <humidity LSB> <temp MSB> <temp LSB>
|
||||
* <CRC LSB> <CRC MSB>
|
||||
*/
|
||||
u8 rx_buf[8];
|
||||
u16 crc;
|
||||
|
||||
/* First wake up the device. */
|
||||
am2315_ping(data->client);
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
ret = i2c_master_send(data->client, tx_buf, sizeof(tx_buf));
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev, "failed to send read request\n");
|
||||
goto exit_unlock;
|
||||
}
|
||||
/* Wait 2-3 ms, then read back the data sent by the device. */
|
||||
usleep_range(2000, 3000);
|
||||
/* Do a bulk data read, then pick out what we need. */
|
||||
ret = i2c_master_recv(data->client, rx_buf, sizeof(rx_buf));
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev, "failed to read sensor data\n");
|
||||
goto exit_unlock;
|
||||
}
|
||||
mutex_unlock(&data->lock);
|
||||
/*
|
||||
* Do a CRC check on the data and compare it to the value
|
||||
* calculated by the device.
|
||||
*/
|
||||
crc = am2315_crc(rx_buf, sizeof(rx_buf) - 2);
|
||||
if ((crc & 0xff) != rx_buf[6] || (crc >> 8) != rx_buf[7]) {
|
||||
dev_err(&data->client->dev, "failed to verify sensor data\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
sensor_data->hum_data = (rx_buf[AM2315_HUM_OFFSET] << 8) |
|
||||
rx_buf[AM2315_HUM_OFFSET + 1];
|
||||
sensor_data->temp_data = (rx_buf[AM2315_TEMP_OFFSET] << 8) |
|
||||
rx_buf[AM2315_TEMP_OFFSET + 1];
|
||||
|
||||
return ret;
|
||||
|
||||
exit_unlock:
|
||||
mutex_unlock(&data->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static irqreturn_t am2315_trigger_handler(int irq, void *p)
|
||||
{
|
||||
int i;
|
||||
int ret;
|
||||
int bit;
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct am2315_data *data = iio_priv(indio_dev);
|
||||
struct am2315_sensor_data sensor_data;
|
||||
|
||||
ret = am2315_read_data(data, &sensor_data);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&data->lock);
|
||||
goto err;
|
||||
}
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
if (*(indio_dev->active_scan_mask) == AM2315_ALL_CHANNEL_MASK) {
|
||||
data->buffer[0] = sensor_data.hum_data;
|
||||
data->buffer[1] = sensor_data.temp_data;
|
||||
} else {
|
||||
i = 0;
|
||||
for_each_set_bit(bit, indio_dev->active_scan_mask,
|
||||
indio_dev->masklength) {
|
||||
data->buffer[i] = (bit ? sensor_data.temp_data :
|
||||
sensor_data.hum_data);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&data->lock);
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
|
||||
pf->timestamp);
|
||||
err:
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int am2315_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
int ret;
|
||||
struct am2315_sensor_data sensor_data;
|
||||
struct am2315_data *data = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = am2315_read_data(data, &sensor_data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = (chan->type == IIO_HUMIDITYRELATIVE) ?
|
||||
sensor_data.hum_data : sensor_data.temp_data;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = 100;
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct iio_info am2315_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = am2315_read_raw,
|
||||
};
|
||||
|
||||
static int am2315_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
int ret;
|
||||
struct iio_dev *indio_dev;
|
||||
struct am2315_data *data;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
||||
if (!indio_dev) {
|
||||
dev_err(&client->dev, "iio allocation failed!\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
data = iio_priv(indio_dev);
|
||||
data->client = client;
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
mutex_init(&data->lock);
|
||||
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->info = &am2315_info;
|
||||
indio_dev->name = AM2315_DRIVER_NAME;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = am2315_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(am2315_channels);
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, NULL,
|
||||
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;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id am2315_i2c_id[] = {
|
||||
{"am2315", 0},
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct acpi_device_id am2315_acpi_id[] = {
|
||||
{"AOS2315", 0},
|
||||
{}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(acpi, am2315_acpi_id);
|
||||
|
||||
static struct i2c_driver am2315_driver = {
|
||||
.driver = {
|
||||
.name = "am2315",
|
||||
.acpi_match_table = ACPI_PTR(am2315_acpi_id),
|
||||
},
|
||||
.probe = am2315_probe,
|
||||
.remove = am2315_remove,
|
||||
.id_table = am2315_i2c_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(am2315_driver);
|
||||
|
||||
MODULE_AUTHOR("Tiberiu Breana <tiberiu.a.breana@intel.com>");
|
||||
MODULE_DESCRIPTION("Aosong AM2315 relative humidity and temperature");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -96,6 +96,24 @@ struct dht11 {
|
|||
struct {s64 ts; int value; } edges[DHT11_EDGES_PER_READ];
|
||||
};
|
||||
|
||||
#ifdef CONFIG_DYNAMIC_DEBUG
|
||||
/*
|
||||
* dht11_edges_print: show the data as actually received by the
|
||||
* driver.
|
||||
*/
|
||||
static void dht11_edges_print(struct dht11 *dht11)
|
||||
{
|
||||
int i;
|
||||
|
||||
dev_dbg(dht11->dev, "%d edges detected:\n", dht11->num_edges);
|
||||
for (i = 1; i < dht11->num_edges; ++i) {
|
||||
dev_dbg(dht11->dev, "%d: %lld ns %s\n", i,
|
||||
dht11->edges[i].ts - dht11->edges[i - 1].ts,
|
||||
dht11->edges[i - 1].value ? "high" : "low");
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_DYNAMIC_DEBUG */
|
||||
|
||||
static unsigned char dht11_decode_byte(char *bits)
|
||||
{
|
||||
unsigned char ret = 0;
|
||||
|
@ -119,8 +137,12 @@ static int dht11_decode(struct dht11 *dht11, int offset)
|
|||
for (i = 0; i < DHT11_BITS_PER_READ; ++i) {
|
||||
t = dht11->edges[offset + 2 * i + 2].ts -
|
||||
dht11->edges[offset + 2 * i + 1].ts;
|
||||
if (!dht11->edges[offset + 2 * i + 1].value)
|
||||
return -EIO; /* lost synchronisation */
|
||||
if (!dht11->edges[offset + 2 * i + 1].value) {
|
||||
dev_dbg(dht11->dev,
|
||||
"lost synchronisation at edge %d\n",
|
||||
offset + 2 * i + 1);
|
||||
return -EIO;
|
||||
}
|
||||
bits[i] = t > DHT11_THRESHOLD;
|
||||
}
|
||||
|
||||
|
@ -130,8 +152,10 @@ static int dht11_decode(struct dht11 *dht11, int offset)
|
|||
temp_dec = dht11_decode_byte(&bits[24]);
|
||||
checksum = dht11_decode_byte(&bits[32]);
|
||||
|
||||
if (((hum_int + hum_dec + temp_int + temp_dec) & 0xff) != checksum)
|
||||
if (((hum_int + hum_dec + temp_int + temp_dec) & 0xff) != checksum) {
|
||||
dev_dbg(dht11->dev, "invalid checksum\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
dht11->timestamp = ktime_get_boot_ns();
|
||||
if (hum_int < 20) { /* DHT22 */
|
||||
|
@ -182,6 +206,7 @@ static int dht11_read_raw(struct iio_dev *iio_dev,
|
|||
mutex_lock(&dht11->lock);
|
||||
if (dht11->timestamp + DHT11_DATA_VALID_TIME < ktime_get_boot_ns()) {
|
||||
timeres = ktime_get_resolution_ns();
|
||||
dev_dbg(dht11->dev, "current timeresolution: %dns\n", timeres);
|
||||
if (timeres > DHT11_MIN_TIMERES) {
|
||||
dev_err(dht11->dev, "timeresolution %dns too low\n",
|
||||
timeres);
|
||||
|
@ -219,10 +244,13 @@ static int dht11_read_raw(struct iio_dev *iio_dev,
|
|||
|
||||
free_irq(dht11->irq, iio_dev);
|
||||
|
||||
#ifdef CONFIG_DYNAMIC_DEBUG
|
||||
dht11_edges_print(dht11);
|
||||
#endif
|
||||
|
||||
if (ret == 0 && dht11->num_edges < DHT11_EDGES_PER_READ - 1) {
|
||||
dev_err(&iio_dev->dev,
|
||||
"Only %d signal edges detected\n",
|
||||
dht11->num_edges);
|
||||
dev_err(dht11->dev, "Only %d signal edges detected\n",
|
||||
dht11->num_edges);
|
||||
ret = -ETIMEDOUT;
|
||||
}
|
||||
if (ret < 0)
|
||||
|
|
|
@ -25,6 +25,8 @@ config ADIS16480
|
|||
Say yes here to build support for Analog Devices ADIS16375, ADIS16480,
|
||||
ADIS16485, ADIS16488 inertial sensors.
|
||||
|
||||
source "drivers/iio/imu/bmi160/Kconfig"
|
||||
|
||||
config KMX61
|
||||
tristate "Kionix KMX61 6-axis accelerometer and magnetometer"
|
||||
depends on I2C
|
||||
|
|
|
@ -13,6 +13,7 @@ adis_lib-$(CONFIG_IIO_ADIS_LIB_BUFFER) += adis_trigger.o
|
|||
adis_lib-$(CONFIG_IIO_ADIS_LIB_BUFFER) += adis_buffer.o
|
||||
obj-$(CONFIG_IIO_ADIS_LIB) += adis_lib.o
|
||||
|
||||
obj-y += bmi160/
|
||||
obj-y += inv_mpu6050/
|
||||
|
||||
obj-$(CONFIG_KMX61) += kmx61.o
|
||||
|
|
|
@ -324,7 +324,12 @@ static int adis_self_test(struct adis *adis)
|
|||
|
||||
msleep(adis->data->startup_delay);
|
||||
|
||||
return adis_check_status(adis);
|
||||
ret = adis_check_status(adis);
|
||||
|
||||
if (adis->data->self_test_no_autoclear)
|
||||
adis_write_reg_16(adis, adis->data->msc_ctrl_reg, 0x00);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
#
|
||||
# BMI160 IMU driver
|
||||
#
|
||||
|
||||
config BMI160
|
||||
tristate
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
|
||||
config BMI160_I2C
|
||||
tristate "Bosch BMI160 I2C driver"
|
||||
depends on I2C
|
||||
select BMI160
|
||||
select REGMAP_I2C
|
||||
help
|
||||
If you say yes here you get support for BMI160 IMU on I2C with
|
||||
accelerometer, gyroscope and external BMG160 magnetometer.
|
||||
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called bmi160_i2c.
|
||||
|
||||
config BMI160_SPI
|
||||
tristate "Bosch BMI160 SPI driver"
|
||||
depends on SPI
|
||||
select BMI160
|
||||
select REGMAP_SPI
|
||||
help
|
||||
If you say yes here you get support for BMI160 IMU on SPI with
|
||||
accelerometer, gyroscope and external BMG160 magnetometer.
|
||||
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called bmi160_spi.
|
|
@ -0,0 +1,6 @@
|
|||
#
|
||||
# Makefile for Bosch BMI160 IMU
|
||||
#
|
||||
obj-$(CONFIG_BMI160) += bmi160_core.o
|
||||
obj-$(CONFIG_BMI160_I2C) += bmi160_i2c.o
|
||||
obj-$(CONFIG_BMI160_SPI) += bmi160_spi.o
|
|
@ -0,0 +1,10 @@
|
|||
#ifndef BMI160_H_
|
||||
#define BMI160_H_
|
||||
|
||||
extern const struct regmap_config bmi160_regmap_config;
|
||||
|
||||
int bmi160_core_probe(struct device *dev, struct regmap *regmap,
|
||||
const char *name, bool use_spi);
|
||||
void bmi160_core_remove(struct device *dev);
|
||||
|
||||
#endif /* BMI160_H_ */
|
|
@ -0,0 +1,596 @@
|
|||
/*
|
||||
* BMI160 - Bosch IMU (accel, gyro plus external magnetometer)
|
||||
*
|
||||
* Copyright (c) 2016, Intel Corporation.
|
||||
*
|
||||
* This file is subject to the terms and conditions of version 2 of
|
||||
* the GNU General Public License. See the file COPYING in the main
|
||||
* directory of this archive for more details.
|
||||
*
|
||||
* IIO core driver for BMI160, with support for I2C/SPI busses
|
||||
*
|
||||
* TODO: magnetometer, interrupts, hardware FIFO
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
|
||||
#include "bmi160.h"
|
||||
|
||||
#define BMI160_REG_CHIP_ID 0x00
|
||||
#define BMI160_CHIP_ID_VAL 0xD1
|
||||
|
||||
#define BMI160_REG_PMU_STATUS 0x03
|
||||
|
||||
/* X axis data low byte address, the rest can be obtained using axis offset */
|
||||
#define BMI160_REG_DATA_MAGN_XOUT_L 0x04
|
||||
#define BMI160_REG_DATA_GYRO_XOUT_L 0x0C
|
||||
#define BMI160_REG_DATA_ACCEL_XOUT_L 0x12
|
||||
|
||||
#define BMI160_REG_ACCEL_CONFIG 0x40
|
||||
#define BMI160_ACCEL_CONFIG_ODR_MASK GENMASK(3, 0)
|
||||
#define BMI160_ACCEL_CONFIG_BWP_MASK GENMASK(6, 4)
|
||||
|
||||
#define BMI160_REG_ACCEL_RANGE 0x41
|
||||
#define BMI160_ACCEL_RANGE_2G 0x03
|
||||
#define BMI160_ACCEL_RANGE_4G 0x05
|
||||
#define BMI160_ACCEL_RANGE_8G 0x08
|
||||
#define BMI160_ACCEL_RANGE_16G 0x0C
|
||||
|
||||
#define BMI160_REG_GYRO_CONFIG 0x42
|
||||
#define BMI160_GYRO_CONFIG_ODR_MASK GENMASK(3, 0)
|
||||
#define BMI160_GYRO_CONFIG_BWP_MASK GENMASK(5, 4)
|
||||
|
||||
#define BMI160_REG_GYRO_RANGE 0x43
|
||||
#define BMI160_GYRO_RANGE_2000DPS 0x00
|
||||
#define BMI160_GYRO_RANGE_1000DPS 0x01
|
||||
#define BMI160_GYRO_RANGE_500DPS 0x02
|
||||
#define BMI160_GYRO_RANGE_250DPS 0x03
|
||||
#define BMI160_GYRO_RANGE_125DPS 0x04
|
||||
|
||||
#define BMI160_REG_CMD 0x7E
|
||||
#define BMI160_CMD_ACCEL_PM_SUSPEND 0x10
|
||||
#define BMI160_CMD_ACCEL_PM_NORMAL 0x11
|
||||
#define BMI160_CMD_ACCEL_PM_LOW_POWER 0x12
|
||||
#define BMI160_CMD_GYRO_PM_SUSPEND 0x14
|
||||
#define BMI160_CMD_GYRO_PM_NORMAL 0x15
|
||||
#define BMI160_CMD_GYRO_PM_FAST_STARTUP 0x17
|
||||
#define BMI160_CMD_SOFTRESET 0xB6
|
||||
|
||||
#define BMI160_REG_DUMMY 0x7F
|
||||
|
||||
#define BMI160_ACCEL_PMU_MIN_USLEEP 3200
|
||||
#define BMI160_ACCEL_PMU_MAX_USLEEP 3800
|
||||
#define BMI160_GYRO_PMU_MIN_USLEEP 55000
|
||||
#define BMI160_GYRO_PMU_MAX_USLEEP 80000
|
||||
#define BMI160_SOFTRESET_USLEEP 1000
|
||||
|
||||
#define BMI160_CHANNEL(_type, _axis, _index) { \
|
||||
.type = _type, \
|
||||
.modified = 1, \
|
||||
.channel2 = IIO_MOD_##_axis, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ), \
|
||||
.scan_index = _index, \
|
||||
.scan_type = { \
|
||||
.sign = 's', \
|
||||
.realbits = 16, \
|
||||
.storagebits = 16, \
|
||||
.endianness = IIO_LE, \
|
||||
}, \
|
||||
}
|
||||
|
||||
/* scan indexes follow DATA register order */
|
||||
enum bmi160_scan_axis {
|
||||
BMI160_SCAN_EXT_MAGN_X = 0,
|
||||
BMI160_SCAN_EXT_MAGN_Y,
|
||||
BMI160_SCAN_EXT_MAGN_Z,
|
||||
BMI160_SCAN_RHALL,
|
||||
BMI160_SCAN_GYRO_X,
|
||||
BMI160_SCAN_GYRO_Y,
|
||||
BMI160_SCAN_GYRO_Z,
|
||||
BMI160_SCAN_ACCEL_X,
|
||||
BMI160_SCAN_ACCEL_Y,
|
||||
BMI160_SCAN_ACCEL_Z,
|
||||
BMI160_SCAN_TIMESTAMP,
|
||||
};
|
||||
|
||||
enum bmi160_sensor_type {
|
||||
BMI160_ACCEL = 0,
|
||||
BMI160_GYRO,
|
||||
BMI160_EXT_MAGN,
|
||||
BMI160_NUM_SENSORS /* must be last */
|
||||
};
|
||||
|
||||
struct bmi160_data {
|
||||
struct regmap *regmap;
|
||||
};
|
||||
|
||||
const struct regmap_config bmi160_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
};
|
||||
EXPORT_SYMBOL(bmi160_regmap_config);
|
||||
|
||||
struct bmi160_regs {
|
||||
u8 data; /* LSB byte register for X-axis */
|
||||
u8 config;
|
||||
u8 config_odr_mask;
|
||||
u8 config_bwp_mask;
|
||||
u8 range;
|
||||
u8 pmu_cmd_normal;
|
||||
u8 pmu_cmd_suspend;
|
||||
};
|
||||
|
||||
static struct bmi160_regs bmi160_regs[] = {
|
||||
[BMI160_ACCEL] = {
|
||||
.data = BMI160_REG_DATA_ACCEL_XOUT_L,
|
||||
.config = BMI160_REG_ACCEL_CONFIG,
|
||||
.config_odr_mask = BMI160_ACCEL_CONFIG_ODR_MASK,
|
||||
.config_bwp_mask = BMI160_ACCEL_CONFIG_BWP_MASK,
|
||||
.range = BMI160_REG_ACCEL_RANGE,
|
||||
.pmu_cmd_normal = BMI160_CMD_ACCEL_PM_NORMAL,
|
||||
.pmu_cmd_suspend = BMI160_CMD_ACCEL_PM_SUSPEND,
|
||||
},
|
||||
[BMI160_GYRO] = {
|
||||
.data = BMI160_REG_DATA_GYRO_XOUT_L,
|
||||
.config = BMI160_REG_GYRO_CONFIG,
|
||||
.config_odr_mask = BMI160_GYRO_CONFIG_ODR_MASK,
|
||||
.config_bwp_mask = BMI160_GYRO_CONFIG_BWP_MASK,
|
||||
.range = BMI160_REG_GYRO_RANGE,
|
||||
.pmu_cmd_normal = BMI160_CMD_GYRO_PM_NORMAL,
|
||||
.pmu_cmd_suspend = BMI160_CMD_GYRO_PM_SUSPEND,
|
||||
},
|
||||
};
|
||||
|
||||
struct bmi160_pmu_time {
|
||||
unsigned long min;
|
||||
unsigned long max;
|
||||
};
|
||||
|
||||
static struct bmi160_pmu_time bmi160_pmu_time[] = {
|
||||
[BMI160_ACCEL] = {
|
||||
.min = BMI160_ACCEL_PMU_MIN_USLEEP,
|
||||
.max = BMI160_ACCEL_PMU_MAX_USLEEP
|
||||
},
|
||||
[BMI160_GYRO] = {
|
||||
.min = BMI160_GYRO_PMU_MIN_USLEEP,
|
||||
.max = BMI160_GYRO_PMU_MIN_USLEEP,
|
||||
},
|
||||
};
|
||||
|
||||
struct bmi160_scale {
|
||||
u8 bits;
|
||||
int uscale;
|
||||
};
|
||||
|
||||
struct bmi160_odr {
|
||||
u8 bits;
|
||||
int odr;
|
||||
int uodr;
|
||||
};
|
||||
|
||||
static const struct bmi160_scale bmi160_accel_scale[] = {
|
||||
{ BMI160_ACCEL_RANGE_2G, 598},
|
||||
{ BMI160_ACCEL_RANGE_4G, 1197},
|
||||
{ BMI160_ACCEL_RANGE_8G, 2394},
|
||||
{ BMI160_ACCEL_RANGE_16G, 4788},
|
||||
};
|
||||
|
||||
static const struct bmi160_scale bmi160_gyro_scale[] = {
|
||||
{ BMI160_GYRO_RANGE_2000DPS, 1065},
|
||||
{ BMI160_GYRO_RANGE_1000DPS, 532},
|
||||
{ BMI160_GYRO_RANGE_500DPS, 266},
|
||||
{ BMI160_GYRO_RANGE_250DPS, 133},
|
||||
{ BMI160_GYRO_RANGE_125DPS, 66},
|
||||
};
|
||||
|
||||
struct bmi160_scale_item {
|
||||
const struct bmi160_scale *tbl;
|
||||
int num;
|
||||
};
|
||||
|
||||
static const struct bmi160_scale_item bmi160_scale_table[] = {
|
||||
[BMI160_ACCEL] = {
|
||||
.tbl = bmi160_accel_scale,
|
||||
.num = ARRAY_SIZE(bmi160_accel_scale),
|
||||
},
|
||||
[BMI160_GYRO] = {
|
||||
.tbl = bmi160_gyro_scale,
|
||||
.num = ARRAY_SIZE(bmi160_gyro_scale),
|
||||
},
|
||||
};
|
||||
|
||||
static const struct bmi160_odr bmi160_accel_odr[] = {
|
||||
{0x01, 0, 78125},
|
||||
{0x02, 1, 5625},
|
||||
{0x03, 3, 125},
|
||||
{0x04, 6, 25},
|
||||
{0x05, 12, 5},
|
||||
{0x06, 25, 0},
|
||||
{0x07, 50, 0},
|
||||
{0x08, 100, 0},
|
||||
{0x09, 200, 0},
|
||||
{0x0A, 400, 0},
|
||||
{0x0B, 800, 0},
|
||||
{0x0C, 1600, 0},
|
||||
};
|
||||
|
||||
static const struct bmi160_odr bmi160_gyro_odr[] = {
|
||||
{0x06, 25, 0},
|
||||
{0x07, 50, 0},
|
||||
{0x08, 100, 0},
|
||||
{0x09, 200, 0},
|
||||
{0x0A, 400, 0},
|
||||
{0x0B, 8000, 0},
|
||||
{0x0C, 1600, 0},
|
||||
{0x0D, 3200, 0},
|
||||
};
|
||||
|
||||
struct bmi160_odr_item {
|
||||
const struct bmi160_odr *tbl;
|
||||
int num;
|
||||
};
|
||||
|
||||
static const struct bmi160_odr_item bmi160_odr_table[] = {
|
||||
[BMI160_ACCEL] = {
|
||||
.tbl = bmi160_accel_odr,
|
||||
.num = ARRAY_SIZE(bmi160_accel_odr),
|
||||
},
|
||||
[BMI160_GYRO] = {
|
||||
.tbl = bmi160_gyro_odr,
|
||||
.num = ARRAY_SIZE(bmi160_gyro_odr),
|
||||
},
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec bmi160_channels[] = {
|
||||
BMI160_CHANNEL(IIO_ACCEL, X, BMI160_SCAN_ACCEL_X),
|
||||
BMI160_CHANNEL(IIO_ACCEL, Y, BMI160_SCAN_ACCEL_Y),
|
||||
BMI160_CHANNEL(IIO_ACCEL, Z, BMI160_SCAN_ACCEL_Z),
|
||||
BMI160_CHANNEL(IIO_ANGL_VEL, X, BMI160_SCAN_GYRO_X),
|
||||
BMI160_CHANNEL(IIO_ANGL_VEL, Y, BMI160_SCAN_GYRO_Y),
|
||||
BMI160_CHANNEL(IIO_ANGL_VEL, Z, BMI160_SCAN_GYRO_Z),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(BMI160_SCAN_TIMESTAMP),
|
||||
};
|
||||
|
||||
static enum bmi160_sensor_type bmi160_to_sensor(enum iio_chan_type iio_type)
|
||||
{
|
||||
switch (iio_type) {
|
||||
case IIO_ACCEL:
|
||||
return BMI160_ACCEL;
|
||||
case IIO_ANGL_VEL:
|
||||
return BMI160_GYRO;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
int bmi160_set_mode(struct bmi160_data *data, enum bmi160_sensor_type t,
|
||||
bool mode)
|
||||
{
|
||||
int ret;
|
||||
u8 cmd;
|
||||
|
||||
if (mode)
|
||||
cmd = bmi160_regs[t].pmu_cmd_normal;
|
||||
else
|
||||
cmd = bmi160_regs[t].pmu_cmd_suspend;
|
||||
|
||||
ret = regmap_write(data->regmap, BMI160_REG_CMD, cmd);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
usleep_range(bmi160_pmu_time[t].min, bmi160_pmu_time[t].max);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
int bmi160_set_scale(struct bmi160_data *data, enum bmi160_sensor_type t,
|
||||
int uscale)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < bmi160_scale_table[t].num; i++)
|
||||
if (bmi160_scale_table[t].tbl[i].uscale == uscale)
|
||||
break;
|
||||
|
||||
if (i == bmi160_scale_table[t].num)
|
||||
return -EINVAL;
|
||||
|
||||
return regmap_write(data->regmap, bmi160_regs[t].range,
|
||||
bmi160_scale_table[t].tbl[i].bits);
|
||||
}
|
||||
|
||||
static
|
||||
int bmi160_get_scale(struct bmi160_data *data, enum bmi160_sensor_type t,
|
||||
int *uscale)
|
||||
{
|
||||
int i, ret, val;
|
||||
|
||||
ret = regmap_read(data->regmap, bmi160_regs[t].range, &val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < bmi160_scale_table[t].num; i++)
|
||||
if (bmi160_scale_table[t].tbl[i].bits == val) {
|
||||
*uscale = bmi160_scale_table[t].tbl[i].uscale;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int bmi160_get_data(struct bmi160_data *data, int chan_type,
|
||||
int axis, int *val)
|
||||
{
|
||||
u8 reg;
|
||||
int ret;
|
||||
__le16 sample;
|
||||
enum bmi160_sensor_type t = bmi160_to_sensor(chan_type);
|
||||
|
||||
reg = bmi160_regs[t].data + (axis - IIO_MOD_X) * sizeof(__le16);
|
||||
|
||||
ret = regmap_bulk_read(data->regmap, reg, &sample, sizeof(__le16));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = sign_extend32(le16_to_cpu(sample), 15);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
int bmi160_set_odr(struct bmi160_data *data, enum bmi160_sensor_type t,
|
||||
int odr, int uodr)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < bmi160_odr_table[t].num; i++)
|
||||
if (bmi160_odr_table[t].tbl[i].odr == odr &&
|
||||
bmi160_odr_table[t].tbl[i].uodr == uodr)
|
||||
break;
|
||||
|
||||
if (i >= bmi160_odr_table[t].num)
|
||||
return -EINVAL;
|
||||
|
||||
return regmap_update_bits(data->regmap,
|
||||
bmi160_regs[t].config,
|
||||
bmi160_odr_table[t].tbl[i].bits,
|
||||
bmi160_regs[t].config_odr_mask);
|
||||
}
|
||||
|
||||
static int bmi160_get_odr(struct bmi160_data *data, enum bmi160_sensor_type t,
|
||||
int *odr, int *uodr)
|
||||
{
|
||||
int i, val, ret;
|
||||
|
||||
ret = regmap_read(data->regmap, bmi160_regs[t].config, &val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
val &= bmi160_regs[t].config_odr_mask;
|
||||
|
||||
for (i = 0; i < bmi160_odr_table[t].num; i++)
|
||||
if (val == bmi160_odr_table[t].tbl[i].bits)
|
||||
break;
|
||||
|
||||
if (i >= bmi160_odr_table[t].num)
|
||||
return -EINVAL;
|
||||
|
||||
*odr = bmi160_odr_table[t].tbl[i].odr;
|
||||
*uodr = bmi160_odr_table[t].tbl[i].uodr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t bmi160_trigger_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct bmi160_data *data = iio_priv(indio_dev);
|
||||
s16 buf[16]; /* 3 sens x 3 axis x s16 + 3 x s16 pad + 4 x s16 tstamp */
|
||||
int i, ret, j = 0, base = BMI160_REG_DATA_MAGN_XOUT_L;
|
||||
__le16 sample;
|
||||
|
||||
for_each_set_bit(i, indio_dev->active_scan_mask,
|
||||
indio_dev->masklength) {
|
||||
ret = regmap_bulk_read(data->regmap, base + i * sizeof(__le16),
|
||||
&sample, sizeof(__le16));
|
||||
if (ret < 0)
|
||||
goto done;
|
||||
buf[j++] = sample;
|
||||
}
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, buf, iio_get_time_ns());
|
||||
done:
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int bmi160_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
int ret;
|
||||
struct bmi160_data *data = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = bmi160_get_data(data, chan->type, chan->channel2, val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = 0;
|
||||
ret = bmi160_get_scale(data,
|
||||
bmi160_to_sensor(chan->type), val2);
|
||||
return ret < 0 ? ret : IIO_VAL_INT_PLUS_MICRO;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
ret = bmi160_get_odr(data, bmi160_to_sensor(chan->type),
|
||||
val, val2);
|
||||
return ret < 0 ? ret : IIO_VAL_INT_PLUS_MICRO;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bmi160_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
struct bmi160_data *data = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
return bmi160_set_scale(data,
|
||||
bmi160_to_sensor(chan->type), val2);
|
||||
break;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
return bmi160_set_odr(data, bmi160_to_sensor(chan->type),
|
||||
val, val2);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct iio_info bmi160_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = bmi160_read_raw,
|
||||
.write_raw = bmi160_write_raw,
|
||||
};
|
||||
|
||||
static const char *bmi160_match_acpi_device(struct device *dev)
|
||||
{
|
||||
const struct acpi_device_id *id;
|
||||
|
||||
id = acpi_match_device(dev->driver->acpi_match_table, dev);
|
||||
if (!id)
|
||||
return NULL;
|
||||
|
||||
return dev_name(dev);
|
||||
}
|
||||
|
||||
static int bmi160_chip_init(struct bmi160_data *data, bool use_spi)
|
||||
{
|
||||
int ret;
|
||||
unsigned int val;
|
||||
struct device *dev = regmap_get_device(data->regmap);
|
||||
|
||||
ret = regmap_write(data->regmap, BMI160_REG_CMD, BMI160_CMD_SOFTRESET);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
usleep_range(BMI160_SOFTRESET_USLEEP, BMI160_SOFTRESET_USLEEP + 1);
|
||||
|
||||
/*
|
||||
* CS rising edge is needed before starting SPI, so do a dummy read
|
||||
* See Section 3.2.1, page 86 of the datasheet
|
||||
*/
|
||||
if (use_spi) {
|
||||
ret = regmap_read(data->regmap, BMI160_REG_DUMMY, &val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regmap_read(data->regmap, BMI160_REG_CHIP_ID, &val);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Error reading chip id\n");
|
||||
return ret;
|
||||
}
|
||||
if (val != BMI160_CHIP_ID_VAL) {
|
||||
dev_err(dev, "Wrong chip id, got %x expected %x\n",
|
||||
val, BMI160_CHIP_ID_VAL);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ret = bmi160_set_mode(data, BMI160_ACCEL, true);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = bmi160_set_mode(data, BMI160_GYRO, true);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void bmi160_chip_uninit(struct bmi160_data *data)
|
||||
{
|
||||
bmi160_set_mode(data, BMI160_GYRO, false);
|
||||
bmi160_set_mode(data, BMI160_ACCEL, false);
|
||||
}
|
||||
|
||||
int bmi160_core_probe(struct device *dev, struct regmap *regmap,
|
||||
const char *name, bool use_spi)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct bmi160_data *data;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
data = iio_priv(indio_dev);
|
||||
dev_set_drvdata(dev, indio_dev);
|
||||
data->regmap = regmap;
|
||||
|
||||
ret = bmi160_chip_init(data, use_spi);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (!name && ACPI_HANDLE(dev))
|
||||
name = bmi160_match_acpi_device(dev);
|
||||
|
||||
indio_dev->dev.parent = dev;
|
||||
indio_dev->channels = bmi160_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(bmi160_channels);
|
||||
indio_dev->name = name;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &bmi160_info;
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, NULL,
|
||||
bmi160_trigger_handler, NULL);
|
||||
if (ret < 0)
|
||||
goto uninit;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret < 0)
|
||||
goto buffer_cleanup;
|
||||
|
||||
return 0;
|
||||
buffer_cleanup:
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
uninit:
|
||||
bmi160_chip_uninit(data);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(bmi160_core_probe);
|
||||
|
||||
void bmi160_core_remove(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct bmi160_data *data = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
bmi160_chip_uninit(data);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(bmi160_core_remove);
|
||||
|
||||
MODULE_AUTHOR("Daniel Baluta <daniel.baluta@intel.com");
|
||||
MODULE_DESCRIPTION("Bosch BMI160 driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* BMI160 - Bosch IMU, I2C bits
|
||||
*
|
||||
* Copyright (c) 2016, Intel Corporation.
|
||||
*
|
||||
* This file is subject to the terms and conditions of version 2 of
|
||||
* the GNU General Public License. See the file COPYING in the main
|
||||
* directory of this archive for more details.
|
||||
*
|
||||
* 7-bit I2C slave address is:
|
||||
* - 0x68 if SDO is pulled to GND
|
||||
* - 0x69 if SDO is pulled to VDDIO
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/acpi.h>
|
||||
|
||||
#include "bmi160.h"
|
||||
|
||||
static int bmi160_i2c_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct regmap *regmap;
|
||||
const char *name = NULL;
|
||||
|
||||
regmap = devm_regmap_init_i2c(client, &bmi160_regmap_config);
|
||||
if (IS_ERR(regmap)) {
|
||||
dev_err(&client->dev, "Failed to register i2c regmap %d\n",
|
||||
(int)PTR_ERR(regmap));
|
||||
return PTR_ERR(regmap);
|
||||
}
|
||||
|
||||
if (id)
|
||||
name = id->name;
|
||||
|
||||
return bmi160_core_probe(&client->dev, regmap, name, false);
|
||||
}
|
||||
|
||||
static int bmi160_i2c_remove(struct i2c_client *client)
|
||||
{
|
||||
bmi160_core_remove(&client->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id bmi160_i2c_id[] = {
|
||||
{"bmi160", 0},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, bmi160_i2c_id);
|
||||
|
||||
static const struct acpi_device_id bmi160_acpi_match[] = {
|
||||
{"BMI0160", 0},
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, bmi160_acpi_match);
|
||||
|
||||
static struct i2c_driver bmi160_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "bmi160_i2c",
|
||||
.acpi_match_table = ACPI_PTR(bmi160_acpi_match),
|
||||
},
|
||||
.probe = bmi160_i2c_probe,
|
||||
.remove = bmi160_i2c_remove,
|
||||
.id_table = bmi160_i2c_id,
|
||||
};
|
||||
module_i2c_driver(bmi160_i2c_driver);
|
||||
|
||||
MODULE_AUTHOR("Daniel Baluta <daniel.baluta@intel.com>");
|
||||
MODULE_DESCRIPTION("BMI160 I2C driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* BMI160 - Bosch IMU, SPI bits
|
||||
*
|
||||
* Copyright (c) 2016, Intel Corporation.
|
||||
*
|
||||
* This file is subject to the terms and conditions of version 2 of
|
||||
* the GNU General Public License. See the file COPYING in the main
|
||||
* directory of this archive for more details.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/acpi.h>
|
||||
|
||||
#include "bmi160.h"
|
||||
|
||||
static int bmi160_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
struct regmap *regmap;
|
||||
const struct spi_device_id *id = spi_get_device_id(spi);
|
||||
|
||||
regmap = devm_regmap_init_spi(spi, &bmi160_regmap_config);
|
||||
if (IS_ERR(regmap)) {
|
||||
dev_err(&spi->dev, "Failed to register spi regmap %d\n",
|
||||
(int)PTR_ERR(regmap));
|
||||
return PTR_ERR(regmap);
|
||||
}
|
||||
return bmi160_core_probe(&spi->dev, regmap, id->name, true);
|
||||
}
|
||||
|
||||
static int bmi160_spi_remove(struct spi_device *spi)
|
||||
{
|
||||
bmi160_core_remove(&spi->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id bmi160_spi_id[] = {
|
||||
{"bmi160", 0},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, bmi160_spi_id);
|
||||
|
||||
static const struct acpi_device_id bmi160_acpi_match[] = {
|
||||
{"BMI0160", 0},
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, bmi160_acpi_match);
|
||||
|
||||
static struct spi_driver bmi160_spi_driver = {
|
||||
.probe = bmi160_spi_probe,
|
||||
.remove = bmi160_spi_remove,
|
||||
.id_table = bmi160_spi_id,
|
||||
.driver = {
|
||||
.acpi_match_table = ACPI_PTR(bmi160_acpi_match),
|
||||
.name = "bmi160_spi",
|
||||
},
|
||||
};
|
||||
module_spi_driver(bmi160_spi_driver);
|
||||
|
||||
MODULE_AUTHOR("Daniel Baluta <daniel.baluta@intel.com");
|
||||
MODULE_DESCRIPTION("Bosch BMI160 SPI driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -13,10 +13,8 @@ config INV_MPU6050_I2C
|
|||
select INV_MPU6050_IIO
|
||||
select REGMAP_I2C
|
||||
help
|
||||
This driver supports the Invensense MPU6050 devices.
|
||||
This driver can also support MPU6500 in MPU6050 compatibility mode
|
||||
and also in MPU6500 mode with some limitations.
|
||||
It is a gyroscope/accelerometer combo device.
|
||||
This driver supports the Invensense MPU6050/6500/9150 motion tracking
|
||||
devices over I2C.
|
||||
This driver can be built as a module. The module will be called
|
||||
inv-mpu6050-i2c.
|
||||
|
||||
|
@ -26,7 +24,7 @@ config INV_MPU6050_SPI
|
|||
select INV_MPU6050_IIO
|
||||
select REGMAP_SPI
|
||||
help
|
||||
This driver supports the Invensense MPU6050 devices.
|
||||
It is a gyroscope/accelerometer combo device.
|
||||
This driver supports the Invensense MPU6000/6500/9150 motion tracking
|
||||
devices over SPI.
|
||||
This driver can be built as a module. The module will be called
|
||||
inv-mpu6050-spi.
|
||||
|
|
|
@ -87,16 +87,29 @@ static const struct inv_mpu6050_chip_config chip_config_6050 = {
|
|||
.accl_fs = INV_MPU6050_FS_02G,
|
||||
};
|
||||
|
||||
/* Indexed by enum inv_devices */
|
||||
static const struct inv_mpu6050_hw hw_info[] = {
|
||||
{
|
||||
.num_reg = 117,
|
||||
.whoami = INV_MPU6050_WHOAMI_VALUE,
|
||||
.name = "MPU6050",
|
||||
.reg = ®_set_6050,
|
||||
.config = &chip_config_6050,
|
||||
},
|
||||
{
|
||||
.whoami = INV_MPU6500_WHOAMI_VALUE,
|
||||
.name = "MPU6500",
|
||||
.reg = ®_set_6500,
|
||||
.config = &chip_config_6050,
|
||||
},
|
||||
{
|
||||
.num_reg = 117,
|
||||
.name = "MPU6050",
|
||||
.whoami = INV_MPU6000_WHOAMI_VALUE,
|
||||
.name = "MPU6000",
|
||||
.reg = ®_set_6050,
|
||||
.config = &chip_config_6050,
|
||||
},
|
||||
{
|
||||
.whoami = INV_MPU9150_WHOAMI_VALUE,
|
||||
.name = "MPU9150",
|
||||
.reg = ®_set_6050,
|
||||
.config = &chip_config_6050,
|
||||
},
|
||||
|
@ -599,6 +612,10 @@ inv_fifo_rate_show(struct device *dev, struct device_attribute *attr,
|
|||
/**
|
||||
* inv_attr_show() - calling this function will show current
|
||||
* parameters.
|
||||
*
|
||||
* Deprecated in favor of IIO mounting matrix API.
|
||||
*
|
||||
* See inv_get_mount_matrix()
|
||||
*/
|
||||
static ssize_t inv_attr_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
|
@ -643,6 +660,18 @@ static int inv_mpu6050_validate_trigger(struct iio_dev *indio_dev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const struct iio_mount_matrix *
|
||||
inv_get_mount_matrix(const struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan)
|
||||
{
|
||||
return &((struct inv_mpu6050_state *)iio_priv(indio_dev))->orientation;
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec_ext_info inv_ext_info[] = {
|
||||
IIO_MOUNT_MATRIX(IIO_SHARED_BY_TYPE, inv_get_mount_matrix),
|
||||
{ },
|
||||
};
|
||||
|
||||
#define INV_MPU6050_CHAN(_type, _channel2, _index) \
|
||||
{ \
|
||||
.type = _type, \
|
||||
|
@ -659,6 +688,7 @@ static int inv_mpu6050_validate_trigger(struct iio_dev *indio_dev,
|
|||
.shift = 0, \
|
||||
.endianness = IIO_BE, \
|
||||
}, \
|
||||
.ext_info = inv_ext_info, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec inv_mpu_channels[] = {
|
||||
|
@ -691,14 +721,16 @@ static IIO_CONST_ATTR(in_accel_scale_available,
|
|||
"0.000598 0.001196 0.002392 0.004785");
|
||||
static IIO_DEV_ATTR_SAMP_FREQ(S_IRUGO | S_IWUSR, inv_fifo_rate_show,
|
||||
inv_mpu6050_fifo_rate_store);
|
||||
|
||||
/* Deprecated: kept for userspace backward compatibility. */
|
||||
static IIO_DEVICE_ATTR(in_gyro_matrix, S_IRUGO, inv_attr_show, NULL,
|
||||
ATTR_GYRO_MATRIX);
|
||||
static IIO_DEVICE_ATTR(in_accel_matrix, S_IRUGO, inv_attr_show, NULL,
|
||||
ATTR_ACCL_MATRIX);
|
||||
|
||||
static struct attribute *inv_attributes[] = {
|
||||
&iio_dev_attr_in_gyro_matrix.dev_attr.attr,
|
||||
&iio_dev_attr_in_accel_matrix.dev_attr.attr,
|
||||
&iio_dev_attr_in_gyro_matrix.dev_attr.attr, /* deprecated */
|
||||
&iio_dev_attr_in_accel_matrix.dev_attr.attr, /* deprecated */
|
||||
&iio_dev_attr_sampling_frequency.dev_attr.attr,
|
||||
&iio_const_attr_sampling_frequency_available.dev_attr.attr,
|
||||
&iio_const_attr_in_accel_scale_available.dev_attr.attr,
|
||||
|
@ -725,6 +757,7 @@ static const struct iio_info mpu_info = {
|
|||
static int inv_check_and_setup_chip(struct inv_mpu6050_state *st)
|
||||
{
|
||||
int result;
|
||||
unsigned int regval;
|
||||
|
||||
st->hw = &hw_info[st->chip_type];
|
||||
st->reg = hw_info[st->chip_type].reg;
|
||||
|
@ -735,6 +768,17 @@ static int inv_check_and_setup_chip(struct inv_mpu6050_state *st)
|
|||
if (result)
|
||||
return result;
|
||||
msleep(INV_MPU6050_POWER_UP_TIME);
|
||||
|
||||
/* check chip self-identification */
|
||||
result = regmap_read(st->map, INV_MPU6050_REG_WHOAMI, ®val);
|
||||
if (result)
|
||||
return result;
|
||||
if (regval != st->hw->whoami) {
|
||||
dev_warn(regmap_get_device(st->map),
|
||||
"whoami mismatch got %#02x expected %#02hhx for %s\n",
|
||||
regval, st->hw->whoami, st->hw->name);
|
||||
}
|
||||
|
||||
/*
|
||||
* toggle power state. After reset, the sleep bit could be on
|
||||
* or off depending on the OTP settings. Toggling power would
|
||||
|
@ -773,14 +817,31 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name,
|
|||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
BUILD_BUG_ON(ARRAY_SIZE(hw_info) != INV_NUM_PARTS);
|
||||
if (chip_type < 0 || chip_type >= INV_NUM_PARTS) {
|
||||
dev_err(dev, "Bad invensense chip_type=%d name=%s\n",
|
||||
chip_type, name);
|
||||
return -ENODEV;
|
||||
}
|
||||
st = iio_priv(indio_dev);
|
||||
st->chip_type = chip_type;
|
||||
st->powerup_count = 0;
|
||||
st->irq = irq;
|
||||
st->map = regmap;
|
||||
|
||||
pdata = dev_get_platdata(dev);
|
||||
if (pdata)
|
||||
if (!pdata) {
|
||||
result = of_iio_read_mount_matrix(dev, "mount-matrix",
|
||||
&st->orientation);
|
||||
if (result) {
|
||||
dev_err(dev, "Failed to retrieve mounting matrix %d\n",
|
||||
result);
|
||||
return result;
|
||||
}
|
||||
} else {
|
||||
st->plat_data = *pdata;
|
||||
}
|
||||
|
||||
/* power is turned on inside check chip type*/
|
||||
result = inv_check_and_setup_chip(st);
|
||||
if (result)
|
||||
|
|
|
@ -169,13 +169,14 @@ static int inv_mpu_remove(struct i2c_client *client)
|
|||
static const struct i2c_device_id inv_mpu_id[] = {
|
||||
{"mpu6050", INV_MPU6050},
|
||||
{"mpu6500", INV_MPU6500},
|
||||
{"mpu9150", INV_MPU9150},
|
||||
{}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(i2c, inv_mpu_id);
|
||||
|
||||
static const struct acpi_device_id inv_acpi_match[] = {
|
||||
{"INVN6500", 0},
|
||||
{"INVN6500", INV_MPU6500},
|
||||
{ },
|
||||
};
|
||||
|
||||
|
|
|
@ -69,6 +69,7 @@ enum inv_devices {
|
|||
INV_MPU6050,
|
||||
INV_MPU6500,
|
||||
INV_MPU6000,
|
||||
INV_MPU9150,
|
||||
INV_NUM_PARTS
|
||||
};
|
||||
|
||||
|
@ -94,13 +95,13 @@ struct inv_mpu6050_chip_config {
|
|||
|
||||
/**
|
||||
* struct inv_mpu6050_hw - Other important hardware information.
|
||||
* @num_reg: Number of registers on device.
|
||||
* @whoami: Self identification byte from WHO_AM_I register
|
||||
* @name: name of the chip.
|
||||
* @reg: register map of the chip.
|
||||
* @config: configuration of the chip.
|
||||
*/
|
||||
struct inv_mpu6050_hw {
|
||||
u8 num_reg;
|
||||
u8 whoami;
|
||||
u8 *name;
|
||||
const struct inv_mpu6050_reg_map *reg;
|
||||
const struct inv_mpu6050_chip_config *config;
|
||||
|
@ -115,7 +116,8 @@ struct inv_mpu6050_hw {
|
|||
* @hw: Other hardware-specific information.
|
||||
* @chip_type: chip type.
|
||||
* @time_stamp_lock: spin lock to time stamp.
|
||||
* @plat_data: platform data.
|
||||
* @plat_data: platform data (deprecated in favor of @orientation).
|
||||
* @orientation: sensor chip orientation relative to main hardware.
|
||||
* @timestamps: kfifo queue to store time stamp.
|
||||
* @map regmap pointer.
|
||||
* @irq interrupt number.
|
||||
|
@ -132,6 +134,7 @@ struct inv_mpu6050_state {
|
|||
struct i2c_client *mux_client;
|
||||
unsigned int powerup_count;
|
||||
struct inv_mpu6050_platform_data plat_data;
|
||||
struct iio_mount_matrix orientation;
|
||||
DECLARE_KFIFO(timestamps, long long, TIMESTAMP_FIFO_SIZE);
|
||||
struct regmap *map;
|
||||
int irq;
|
||||
|
@ -216,6 +219,13 @@ struct inv_mpu6050_state {
|
|||
#define INV_MPU6050_MIN_FIFO_RATE 4
|
||||
#define INV_MPU6050_ONE_K_HZ 1000
|
||||
|
||||
#define INV_MPU6050_REG_WHOAMI 117
|
||||
|
||||
#define INV_MPU6000_WHOAMI_VALUE 0x68
|
||||
#define INV_MPU6050_WHOAMI_VALUE 0x68
|
||||
#define INV_MPU6500_WHOAMI_VALUE 0x70
|
||||
#define INV_MPU9150_WHOAMI_VALUE 0x68
|
||||
|
||||
/* scan element definition */
|
||||
enum inv_mpu6050_scan {
|
||||
INV_MPU6050_SCAN_ACCL_X,
|
||||
|
|
|
@ -44,9 +44,19 @@ static int inv_mpu_i2c_disable(struct iio_dev *indio_dev)
|
|||
static int inv_mpu_probe(struct spi_device *spi)
|
||||
{
|
||||
struct regmap *regmap;
|
||||
const struct spi_device_id *id = spi_get_device_id(spi);
|
||||
const char *name = id ? id->name : NULL;
|
||||
const int chip_type = id ? id->driver_data : 0;
|
||||
const struct spi_device_id *spi_id;
|
||||
const struct acpi_device_id *acpi_id;
|
||||
const char *name = NULL;
|
||||
enum inv_devices chip_type;
|
||||
|
||||
if ((spi_id = spi_get_device_id(spi))) {
|
||||
chip_type = (enum inv_devices)spi_id->driver_data;
|
||||
name = spi_id->name;
|
||||
} else if ((acpi_id = acpi_match_device(spi->dev.driver->acpi_match_table, &spi->dev))) {
|
||||
chip_type = (enum inv_devices)acpi_id->driver_data;
|
||||
} else {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
regmap = devm_regmap_init_spi(spi, &inv_mpu_regmap_config);
|
||||
if (IS_ERR(regmap)) {
|
||||
|
@ -70,13 +80,15 @@ static int inv_mpu_remove(struct spi_device *spi)
|
|||
*/
|
||||
static const struct spi_device_id inv_mpu_id[] = {
|
||||
{"mpu6000", INV_MPU6000},
|
||||
{"mpu6500", INV_MPU6500},
|
||||
{"mpu9150", INV_MPU9150},
|
||||
{}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(spi, inv_mpu_id);
|
||||
|
||||
static const struct acpi_device_id inv_acpi_match[] = {
|
||||
{"INVN6000", 0},
|
||||
{"INVN6000", INV_MPU6000},
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, inv_acpi_match);
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/anon_inodes.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include "iio_core.h"
|
||||
#include "iio_core_trigger.h"
|
||||
|
@ -78,6 +79,7 @@ static const char * const iio_chan_type_name_spec[] = {
|
|||
[IIO_CONCENTRATION] = "concentration",
|
||||
[IIO_RESISTANCE] = "resistance",
|
||||
[IIO_PH] = "ph",
|
||||
[IIO_UVINDEX] = "uvindex",
|
||||
};
|
||||
|
||||
static const char * const iio_modifier_names[] = {
|
||||
|
@ -100,6 +102,7 @@ static const char * const iio_modifier_names[] = {
|
|||
[IIO_MOD_LIGHT_RED] = "red",
|
||||
[IIO_MOD_LIGHT_GREEN] = "green",
|
||||
[IIO_MOD_LIGHT_BLUE] = "blue",
|
||||
[IIO_MOD_LIGHT_UV] = "uv",
|
||||
[IIO_MOD_QUATERNION] = "quaternion",
|
||||
[IIO_MOD_TEMP_AMBIENT] = "ambient",
|
||||
[IIO_MOD_TEMP_OBJECT] = "object",
|
||||
|
@ -409,6 +412,88 @@ ssize_t iio_enum_write(struct iio_dev *indio_dev,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(iio_enum_write);
|
||||
|
||||
static const struct iio_mount_matrix iio_mount_idmatrix = {
|
||||
.rotation = {
|
||||
"1", "0", "0",
|
||||
"0", "1", "0",
|
||||
"0", "0", "1"
|
||||
}
|
||||
};
|
||||
|
||||
static int iio_setup_mount_idmatrix(const struct device *dev,
|
||||
struct iio_mount_matrix *matrix)
|
||||
{
|
||||
*matrix = iio_mount_idmatrix;
|
||||
dev_info(dev, "mounting matrix not found: using identity...\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ssize_t iio_show_mount_matrix(struct iio_dev *indio_dev, uintptr_t priv,
|
||||
const struct iio_chan_spec *chan, char *buf)
|
||||
{
|
||||
const struct iio_mount_matrix *mtx = ((iio_get_mount_matrix_t *)
|
||||
priv)(indio_dev, chan);
|
||||
|
||||
if (IS_ERR(mtx))
|
||||
return PTR_ERR(mtx);
|
||||
|
||||
if (!mtx)
|
||||
mtx = &iio_mount_idmatrix;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%s, %s, %s; %s, %s, %s; %s, %s, %s\n",
|
||||
mtx->rotation[0], mtx->rotation[1], mtx->rotation[2],
|
||||
mtx->rotation[3], mtx->rotation[4], mtx->rotation[5],
|
||||
mtx->rotation[6], mtx->rotation[7], mtx->rotation[8]);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iio_show_mount_matrix);
|
||||
|
||||
/**
|
||||
* of_iio_read_mount_matrix() - retrieve iio device mounting matrix from
|
||||
* device-tree "mount-matrix" property
|
||||
* @dev: device the mounting matrix property is assigned to
|
||||
* @propname: device specific mounting matrix property name
|
||||
* @matrix: where to store retrieved matrix
|
||||
*
|
||||
* If device is assigned no mounting matrix property, a default 3x3 identity
|
||||
* matrix will be filled in.
|
||||
*
|
||||
* Return: 0 if success, or a negative error code on failure.
|
||||
*/
|
||||
#ifdef CONFIG_OF
|
||||
int of_iio_read_mount_matrix(const struct device *dev,
|
||||
const char *propname,
|
||||
struct iio_mount_matrix *matrix)
|
||||
{
|
||||
if (dev->of_node) {
|
||||
int err = of_property_read_string_array(dev->of_node,
|
||||
propname, matrix->rotation,
|
||||
ARRAY_SIZE(iio_mount_idmatrix.rotation));
|
||||
|
||||
if (err == ARRAY_SIZE(iio_mount_idmatrix.rotation))
|
||||
return 0;
|
||||
|
||||
if (err >= 0)
|
||||
/* Invalid number of matrix entries. */
|
||||
return -EINVAL;
|
||||
|
||||
if (err != -EINVAL)
|
||||
/* Invalid matrix declaration format. */
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Matrix was not declared at all: fallback to identity. */
|
||||
return iio_setup_mount_idmatrix(dev, matrix);
|
||||
}
|
||||
#else
|
||||
int of_iio_read_mount_matrix(const struct device *dev,
|
||||
const char *propname,
|
||||
struct iio_mount_matrix *matrix)
|
||||
{
|
||||
return iio_setup_mount_idmatrix(dev, matrix);
|
||||
}
|
||||
#endif
|
||||
EXPORT_SYMBOL(of_iio_read_mount_matrix);
|
||||
|
||||
/**
|
||||
* iio_format_value() - Formats a IIO value into its string representation
|
||||
* @buf: The buffer to which the formatted value gets written
|
||||
|
@ -1375,6 +1460,44 @@ void devm_iio_device_unregister(struct device *dev, struct iio_dev *indio_dev)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(devm_iio_device_unregister);
|
||||
|
||||
/**
|
||||
* iio_device_claim_direct_mode - Keep device in direct mode
|
||||
* @indio_dev: the iio_dev associated with the device
|
||||
*
|
||||
* If the device is in direct mode it is guaranteed to stay
|
||||
* that way until iio_device_release_direct_mode() is called.
|
||||
*
|
||||
* Use with iio_device_release_direct_mode()
|
||||
*
|
||||
* Returns: 0 on success, -EBUSY on failure
|
||||
*/
|
||||
int iio_device_claim_direct_mode(struct iio_dev *indio_dev)
|
||||
{
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
|
||||
if (iio_buffer_enabled(indio_dev)) {
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return -EBUSY;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iio_device_claim_direct_mode);
|
||||
|
||||
/**
|
||||
* iio_device_release_direct_mode - releases claim on direct mode
|
||||
* @indio_dev: the iio_dev associated with the device
|
||||
*
|
||||
* Release the claim. Device is no longer guaranteed to stay
|
||||
* in direct mode.
|
||||
*
|
||||
* Use with iio_device_claim_direct_mode()
|
||||
*/
|
||||
void iio_device_release_direct_mode(struct iio_dev *indio_dev)
|
||||
{
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iio_device_release_direct_mode);
|
||||
|
||||
subsys_initcall(iio_init);
|
||||
module_exit(iio_exit);
|
||||
|
||||
|
|
|
@ -356,6 +356,54 @@ void iio_channel_release(struct iio_channel *channel)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(iio_channel_release);
|
||||
|
||||
static void devm_iio_channel_free(struct device *dev, void *res)
|
||||
{
|
||||
struct iio_channel *channel = *(struct iio_channel **)res;
|
||||
|
||||
iio_channel_release(channel);
|
||||
}
|
||||
|
||||
static int devm_iio_channel_match(struct device *dev, void *res, void *data)
|
||||
{
|
||||
struct iio_channel **r = res;
|
||||
|
||||
if (!r || !*r) {
|
||||
WARN_ON(!r || !*r);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return *r == data;
|
||||
}
|
||||
|
||||
struct iio_channel *devm_iio_channel_get(struct device *dev,
|
||||
const char *channel_name)
|
||||
{
|
||||
struct iio_channel **ptr, *channel;
|
||||
|
||||
ptr = devres_alloc(devm_iio_channel_free, sizeof(*ptr), GFP_KERNEL);
|
||||
if (!ptr)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
channel = iio_channel_get(dev, channel_name);
|
||||
if (IS_ERR(channel)) {
|
||||
devres_free(ptr);
|
||||
return channel;
|
||||
}
|
||||
|
||||
*ptr = channel;
|
||||
devres_add(dev, ptr);
|
||||
|
||||
return channel;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_iio_channel_get);
|
||||
|
||||
void devm_iio_channel_release(struct device *dev, struct iio_channel *channel)
|
||||
{
|
||||
WARN_ON(devres_release(dev, devm_iio_channel_free,
|
||||
devm_iio_channel_match, channel));
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_iio_channel_release);
|
||||
|
||||
struct iio_channel *iio_channel_get_all(struct device *dev)
|
||||
{
|
||||
const char *name;
|
||||
|
@ -441,6 +489,42 @@ void iio_channel_release_all(struct iio_channel *channels)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(iio_channel_release_all);
|
||||
|
||||
static void devm_iio_channel_free_all(struct device *dev, void *res)
|
||||
{
|
||||
struct iio_channel *channels = *(struct iio_channel **)res;
|
||||
|
||||
iio_channel_release_all(channels);
|
||||
}
|
||||
|
||||
struct iio_channel *devm_iio_channel_get_all(struct device *dev)
|
||||
{
|
||||
struct iio_channel **ptr, *channels;
|
||||
|
||||
ptr = devres_alloc(devm_iio_channel_free_all, sizeof(*ptr), GFP_KERNEL);
|
||||
if (!ptr)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
channels = iio_channel_get_all(dev);
|
||||
if (IS_ERR(channels)) {
|
||||
devres_free(ptr);
|
||||
return channels;
|
||||
}
|
||||
|
||||
*ptr = channels;
|
||||
devres_add(dev, ptr);
|
||||
|
||||
return channels;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_iio_channel_get_all);
|
||||
|
||||
void devm_iio_channel_release_all(struct device *dev,
|
||||
struct iio_channel *channels)
|
||||
{
|
||||
WARN_ON(devres_release(dev, devm_iio_channel_free_all,
|
||||
devm_iio_channel_match, channels));
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_iio_channel_release_all);
|
||||
|
||||
static int iio_channel_read(struct iio_channel *chan, int *val, int *val2,
|
||||
enum iio_chan_info_enum info)
|
||||
{
|
||||
|
@ -452,7 +536,7 @@ static int iio_channel_read(struct iio_channel *chan, int *val, int *val2,
|
|||
if (val2 == NULL)
|
||||
val2 = &unused;
|
||||
|
||||
if(!iio_channel_has_info(chan->channel, info))
|
||||
if (!iio_channel_has_info(chan->channel, info))
|
||||
return -EINVAL;
|
||||
|
||||
if (chan->indio_dev->info->read_raw_multi) {
|
||||
|
|
|
@ -73,6 +73,17 @@ config BH1750
|
|||
To compile this driver as a module, choose M here: the module will
|
||||
be called bh1750.
|
||||
|
||||
config BH1780
|
||||
tristate "ROHM BH1780 ambient light sensor"
|
||||
depends on I2C
|
||||
depends on !SENSORS_BH1780
|
||||
help
|
||||
Say Y here to build support for the ROHM BH1780GLI ambient
|
||||
light sensor.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will
|
||||
be called bh1780.
|
||||
|
||||
config CM32181
|
||||
depends on I2C
|
||||
tristate "CM32181 driver"
|
||||
|
@ -223,6 +234,17 @@ config LTR501
|
|||
This driver can also be built as a module. If so, the module
|
||||
will be called ltr501.
|
||||
|
||||
config MAX44000
|
||||
tristate "MAX44000 Ambient and Infrared Proximity Sensor"
|
||||
depends on I2C
|
||||
select REGMAP_I2C
|
||||
help
|
||||
Say Y here if you want to build support for Maxim Integrated's
|
||||
MAX44000 ambient and infrared proximity sensor device.
|
||||
|
||||
To compile this driver as a module, choose M here:
|
||||
the module will be called max44000.
|
||||
|
||||
config OPT3001
|
||||
tristate "Texas Instruments OPT3001 Light Sensor"
|
||||
depends on I2C
|
||||
|
@ -320,4 +342,14 @@ config VCNL4000
|
|||
To compile this driver as a module, choose M here: the
|
||||
module will be called vcnl4000.
|
||||
|
||||
config VEML6070
|
||||
tristate "VEML6070 UV A light sensor"
|
||||
depends on I2C
|
||||
help
|
||||
Say Y here if you want to build a driver for the Vishay VEML6070 UV A
|
||||
light sensor.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called veml6070.
|
||||
|
||||
endmenu
|
||||
|
|
|
@ -9,6 +9,7 @@ obj-$(CONFIG_AL3320A) += al3320a.o
|
|||
obj-$(CONFIG_APDS9300) += apds9300.o
|
||||
obj-$(CONFIG_APDS9960) += apds9960.o
|
||||
obj-$(CONFIG_BH1750) += bh1750.o
|
||||
obj-$(CONFIG_BH1780) += bh1780.o
|
||||
obj-$(CONFIG_CM32181) += cm32181.o
|
||||
obj-$(CONFIG_CM3232) += cm3232.o
|
||||
obj-$(CONFIG_CM3323) += cm3323.o
|
||||
|
@ -20,6 +21,7 @@ obj-$(CONFIG_ISL29125) += isl29125.o
|
|||
obj-$(CONFIG_JSA1212) += jsa1212.o
|
||||
obj-$(CONFIG_SENSORS_LM3533) += lm3533-als.o
|
||||
obj-$(CONFIG_LTR501) += ltr501.o
|
||||
obj-$(CONFIG_MAX44000) += max44000.o
|
||||
obj-$(CONFIG_OPT3001) += opt3001.o
|
||||
obj-$(CONFIG_PA12203001) += pa12203001.o
|
||||
obj-$(CONFIG_RPR0521) += rpr0521.o
|
||||
|
@ -30,3 +32,4 @@ obj-$(CONFIG_TCS3472) += tcs3472.o
|
|||
obj-$(CONFIG_TSL4531) += tsl4531.o
|
||||
obj-$(CONFIG_US5182D) += us5182d.o
|
||||
obj-$(CONFIG_VCNL4000) += vcnl4000.o
|
||||
obj-$(CONFIG_VEML6070) += veml6070.o
|
||||
|
|
|
@ -321,8 +321,12 @@ static const struct iio_chan_spec apds9960_channels[] = {
|
|||
};
|
||||
|
||||
/* integration time in us */
|
||||
static const int apds9960_int_time[][2] =
|
||||
{ {28000, 246}, {100000, 219}, {200000, 182}, {700000, 0} };
|
||||
static const int apds9960_int_time[][2] = {
|
||||
{ 28000, 246},
|
||||
{100000, 219},
|
||||
{200000, 182},
|
||||
{700000, 0}
|
||||
};
|
||||
|
||||
/* gain mapping */
|
||||
static const int apds9960_pxs_gain_map[] = {1, 2, 4, 8};
|
||||
|
@ -491,9 +495,10 @@ static int apds9960_read_raw(struct iio_dev *indio_dev,
|
|||
case IIO_INTENSITY:
|
||||
ret = regmap_bulk_read(data->regmap, chan->address,
|
||||
&buf, 2);
|
||||
if (!ret)
|
||||
if (!ret) {
|
||||
ret = IIO_VAL_INT;
|
||||
*val = le16_to_cpu(buf);
|
||||
*val = le16_to_cpu(buf);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
|
|
|
@ -0,0 +1,297 @@
|
|||
/*
|
||||
* ROHM 1780GLI Ambient Light Sensor Driver
|
||||
*
|
||||
* Copyright (C) 2016 Linaro Ltd.
|
||||
* Author: Linus Walleij <linus.walleij@linaro.org>
|
||||
* Loosely based on the previous BH1780 ALS misc driver
|
||||
* Copyright (C) 2010 Texas Instruments
|
||||
* Author: Hemanth V <hemanthv@ti.com>
|
||||
*/
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/bitops.h>
|
||||
|
||||
#define BH1780_CMD_BIT BIT(7)
|
||||
#define BH1780_REG_CONTROL 0x00
|
||||
#define BH1780_REG_PARTID 0x0A
|
||||
#define BH1780_REG_MANFID 0x0B
|
||||
#define BH1780_REG_DLOW 0x0C
|
||||
#define BH1780_REG_DHIGH 0x0D
|
||||
|
||||
#define BH1780_REVMASK GENMASK(3,0)
|
||||
#define BH1780_POWMASK GENMASK(1,0)
|
||||
#define BH1780_POFF (0x0)
|
||||
#define BH1780_PON (0x3)
|
||||
|
||||
/* power on settling time in ms */
|
||||
#define BH1780_PON_DELAY 2
|
||||
/* max time before value available in ms */
|
||||
#define BH1780_INTERVAL 250
|
||||
|
||||
struct bh1780_data {
|
||||
struct i2c_client *client;
|
||||
};
|
||||
|
||||
static int bh1780_write(struct bh1780_data *bh1780, u8 reg, u8 val)
|
||||
{
|
||||
int ret = i2c_smbus_write_byte_data(bh1780->client,
|
||||
BH1780_CMD_BIT | reg,
|
||||
val);
|
||||
if (ret < 0)
|
||||
dev_err(&bh1780->client->dev,
|
||||
"i2c_smbus_write_byte_data failed error "
|
||||
"%d, register %01x\n",
|
||||
ret, reg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bh1780_read(struct bh1780_data *bh1780, u8 reg)
|
||||
{
|
||||
int ret = i2c_smbus_read_byte_data(bh1780->client,
|
||||
BH1780_CMD_BIT | reg);
|
||||
if (ret < 0)
|
||||
dev_err(&bh1780->client->dev,
|
||||
"i2c_smbus_read_byte_data failed error "
|
||||
"%d, register %01x\n",
|
||||
ret, reg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bh1780_read_word(struct bh1780_data *bh1780, u8 reg)
|
||||
{
|
||||
int ret = i2c_smbus_read_word_data(bh1780->client,
|
||||
BH1780_CMD_BIT | reg);
|
||||
if (ret < 0)
|
||||
dev_err(&bh1780->client->dev,
|
||||
"i2c_smbus_read_word_data failed error "
|
||||
"%d, register %01x\n",
|
||||
ret, reg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bh1780_debugfs_reg_access(struct iio_dev *indio_dev,
|
||||
unsigned int reg, unsigned int writeval,
|
||||
unsigned int *readval)
|
||||
{
|
||||
struct bh1780_data *bh1780 = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
if (!readval)
|
||||
bh1780_write(bh1780, (u8)reg, (u8)writeval);
|
||||
|
||||
ret = bh1780_read(bh1780, (u8)reg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*readval = ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bh1780_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct bh1780_data *bh1780 = iio_priv(indio_dev);
|
||||
int value;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
switch (chan->type) {
|
||||
case IIO_LIGHT:
|
||||
pm_runtime_get_sync(&bh1780->client->dev);
|
||||
value = bh1780_read_word(bh1780, BH1780_REG_DLOW);
|
||||
if (value < 0)
|
||||
return value;
|
||||
pm_runtime_mark_last_busy(&bh1780->client->dev);
|
||||
pm_runtime_put_autosuspend(&bh1780->client->dev);
|
||||
*val = value;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
case IIO_CHAN_INFO_INT_TIME:
|
||||
*val = 0;
|
||||
*val2 = BH1780_INTERVAL * 1000;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct iio_info bh1780_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = bh1780_read_raw,
|
||||
.debugfs_reg_access = bh1780_debugfs_reg_access,
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec bh1780_channels[] = {
|
||||
{
|
||||
.type = IIO_LIGHT,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_INT_TIME)
|
||||
}
|
||||
};
|
||||
|
||||
static int bh1780_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
int ret;
|
||||
struct bh1780_data *bh1780;
|
||||
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
|
||||
struct iio_dev *indio_dev;
|
||||
|
||||
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE))
|
||||
return -EIO;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*bh1780));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
bh1780 = iio_priv(indio_dev);
|
||||
bh1780->client = client;
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
|
||||
/* Power up the device */
|
||||
ret = bh1780_write(bh1780, BH1780_REG_CONTROL, BH1780_PON);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
msleep(BH1780_PON_DELAY);
|
||||
pm_runtime_get_noresume(&client->dev);
|
||||
pm_runtime_set_active(&client->dev);
|
||||
pm_runtime_enable(&client->dev);
|
||||
|
||||
ret = bh1780_read(bh1780, BH1780_REG_PARTID);
|
||||
if (ret < 0)
|
||||
goto out_disable_pm;
|
||||
dev_info(&client->dev,
|
||||
"Ambient Light Sensor, Rev : %lu\n",
|
||||
(ret & BH1780_REVMASK));
|
||||
|
||||
/*
|
||||
* As the device takes 250 ms to even come up with a fresh
|
||||
* measurement after power-on, do not shut it down unnecessarily.
|
||||
* Set autosuspend to a five seconds.
|
||||
*/
|
||||
pm_runtime_set_autosuspend_delay(&client->dev, 5000);
|
||||
pm_runtime_use_autosuspend(&client->dev);
|
||||
pm_runtime_put(&client->dev);
|
||||
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->info = &bh1780_info;
|
||||
indio_dev->name = id->name;
|
||||
indio_dev->channels = bh1780_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(bh1780_channels);
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto out_disable_pm;
|
||||
return 0;
|
||||
|
||||
out_disable_pm:
|
||||
pm_runtime_put_noidle(&client->dev);
|
||||
pm_runtime_disable(&client->dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bh1780_remove(struct i2c_client *client)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
struct bh1780_data *bh1780 = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
pm_runtime_get_sync(&client->dev);
|
||||
pm_runtime_put_noidle(&client->dev);
|
||||
pm_runtime_disable(&client->dev);
|
||||
ret = bh1780_write(bh1780, BH1780_REG_CONTROL, BH1780_POFF);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "failed to power off\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int bh1780_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct bh1780_data *bh1780 = i2c_get_clientdata(client);
|
||||
int ret;
|
||||
|
||||
ret = bh1780_write(bh1780, BH1780_REG_CONTROL, BH1780_POFF);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to runtime suspend\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bh1780_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct bh1780_data *bh1780 = i2c_get_clientdata(client);
|
||||
int ret;
|
||||
|
||||
ret = bh1780_write(bh1780, BH1780_REG_CONTROL, BH1780_PON);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to runtime resume\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Wait for power on, then for a value to be available */
|
||||
msleep(BH1780_PON_DELAY + BH1780_INTERVAL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
static const struct dev_pm_ops bh1780_dev_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
|
||||
pm_runtime_force_resume)
|
||||
SET_RUNTIME_PM_OPS(bh1780_runtime_suspend,
|
||||
bh1780_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
static const struct i2c_device_id bh1780_id[] = {
|
||||
{ "bh1780", 0 },
|
||||
{ },
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(i2c, bh1780_id);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id of_bh1780_match[] = {
|
||||
{ .compatible = "rohm,bh1780gli", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, of_bh1780_match);
|
||||
#endif
|
||||
|
||||
static struct i2c_driver bh1780_driver = {
|
||||
.probe = bh1780_probe,
|
||||
.remove = bh1780_remove,
|
||||
.id_table = bh1780_id,
|
||||
.driver = {
|
||||
.name = "bh1780",
|
||||
.pm = &bh1780_dev_pm_ops,
|
||||
.of_match_table = of_match_ptr(of_bh1780_match),
|
||||
},
|
||||
};
|
||||
|
||||
module_i2c_driver(bh1780_driver);
|
||||
|
||||
MODULE_DESCRIPTION("ROHM BH1780GLI Ambient Light Sensor Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");
|
|
@ -0,0 +1,639 @@
|
|||
/*
|
||||
* MAX44000 Ambient and Infrared Proximity Sensor
|
||||
*
|
||||
* Copyright (c) 2016, Intel Corporation.
|
||||
*
|
||||
* This file is subject to the terms and conditions of version 2 of
|
||||
* the GNU General Public License. See the file COPYING in the main
|
||||
* directory of this archive for more details.
|
||||
*
|
||||
* Data sheet: https://datasheets.maximintegrated.com/en/ds/MAX44000.pdf
|
||||
*
|
||||
* 7-bit I2C slave address 0x4a
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/util_macros.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/acpi.h>
|
||||
|
||||
#define MAX44000_DRV_NAME "max44000"
|
||||
|
||||
/* Registers in datasheet order */
|
||||
#define MAX44000_REG_STATUS 0x00
|
||||
#define MAX44000_REG_CFG_MAIN 0x01
|
||||
#define MAX44000_REG_CFG_RX 0x02
|
||||
#define MAX44000_REG_CFG_TX 0x03
|
||||
#define MAX44000_REG_ALS_DATA_HI 0x04
|
||||
#define MAX44000_REG_ALS_DATA_LO 0x05
|
||||
#define MAX44000_REG_PRX_DATA 0x16
|
||||
#define MAX44000_REG_ALS_UPTHR_HI 0x06
|
||||
#define MAX44000_REG_ALS_UPTHR_LO 0x07
|
||||
#define MAX44000_REG_ALS_LOTHR_HI 0x08
|
||||
#define MAX44000_REG_ALS_LOTHR_LO 0x09
|
||||
#define MAX44000_REG_PST 0x0a
|
||||
#define MAX44000_REG_PRX_IND 0x0b
|
||||
#define MAX44000_REG_PRX_THR 0x0c
|
||||
#define MAX44000_REG_TRIM_GAIN_GREEN 0x0f
|
||||
#define MAX44000_REG_TRIM_GAIN_IR 0x10
|
||||
|
||||
/* REG_CFG bits */
|
||||
#define MAX44000_CFG_ALSINTE 0x01
|
||||
#define MAX44000_CFG_PRXINTE 0x02
|
||||
#define MAX44000_CFG_MASK 0x1c
|
||||
#define MAX44000_CFG_MODE_SHUTDOWN 0x00
|
||||
#define MAX44000_CFG_MODE_ALS_GIR 0x04
|
||||
#define MAX44000_CFG_MODE_ALS_G 0x08
|
||||
#define MAX44000_CFG_MODE_ALS_IR 0x0c
|
||||
#define MAX44000_CFG_MODE_ALS_PRX 0x10
|
||||
#define MAX44000_CFG_MODE_PRX 0x14
|
||||
#define MAX44000_CFG_TRIM 0x20
|
||||
|
||||
/*
|
||||
* Upper 4 bits are not documented but start as 1 on powerup
|
||||
* Setting them to 0 causes proximity to misbehave so set them to 1
|
||||
*/
|
||||
#define MAX44000_REG_CFG_RX_DEFAULT 0xf0
|
||||
|
||||
/* REG_RX bits */
|
||||
#define MAX44000_CFG_RX_ALSTIM_MASK 0x0c
|
||||
#define MAX44000_CFG_RX_ALSTIM_SHIFT 2
|
||||
#define MAX44000_CFG_RX_ALSPGA_MASK 0x03
|
||||
#define MAX44000_CFG_RX_ALSPGA_SHIFT 0
|
||||
|
||||
/* REG_TX bits */
|
||||
#define MAX44000_LED_CURRENT_MASK 0xf
|
||||
#define MAX44000_LED_CURRENT_MAX 11
|
||||
#define MAX44000_LED_CURRENT_DEFAULT 6
|
||||
|
||||
#define MAX44000_ALSDATA_OVERFLOW 0x4000
|
||||
|
||||
struct max44000_data {
|
||||
struct mutex lock;
|
||||
struct regmap *regmap;
|
||||
};
|
||||
|
||||
/* Default scale is set to the minimum of 0.03125 or 1 / (1 << 5) lux */
|
||||
#define MAX44000_ALS_TO_LUX_DEFAULT_FRACTION_LOG2 5
|
||||
|
||||
/* Scale can be multiplied by up to 128x via ALSPGA for measurement gain */
|
||||
static const int max44000_alspga_shift[] = {0, 2, 4, 7};
|
||||
#define MAX44000_ALSPGA_MAX_SHIFT 7
|
||||
|
||||
/*
|
||||
* Scale can be multiplied by up to 64x via ALSTIM because of lost resolution
|
||||
*
|
||||
* This scaling factor is hidden from userspace and instead accounted for when
|
||||
* reading raw values from the device.
|
||||
*
|
||||
* This makes it possible to cleanly expose ALSPGA as IIO_CHAN_INFO_SCALE and
|
||||
* ALSTIM as IIO_CHAN_INFO_INT_TIME without the values affecting each other.
|
||||
*
|
||||
* Handling this internally is also required for buffer support because the
|
||||
* channel's scan_type can't be modified dynamically.
|
||||
*/
|
||||
static const int max44000_alstim_shift[] = {0, 2, 4, 6};
|
||||
#define MAX44000_ALSTIM_SHIFT(alstim) (2 * (alstim))
|
||||
|
||||
/* Available integration times with pretty manual alignment: */
|
||||
static const int max44000_int_time_avail_ns_array[] = {
|
||||
100000000,
|
||||
25000000,
|
||||
6250000,
|
||||
1562500,
|
||||
};
|
||||
static const char max44000_int_time_avail_str[] =
|
||||
"0.100 "
|
||||
"0.025 "
|
||||
"0.00625 "
|
||||
"0.001625";
|
||||
|
||||
/* Available scales (internal to ulux) with pretty manual alignment: */
|
||||
static const int max44000_scale_avail_ulux_array[] = {
|
||||
31250,
|
||||
125000,
|
||||
500000,
|
||||
4000000,
|
||||
};
|
||||
static const char max44000_scale_avail_str[] =
|
||||
"0.03125 "
|
||||
"0.125 "
|
||||
"0.5 "
|
||||
"4";
|
||||
|
||||
#define MAX44000_SCAN_INDEX_ALS 0
|
||||
#define MAX44000_SCAN_INDEX_PRX 1
|
||||
|
||||
static const struct iio_chan_spec max44000_channels[] = {
|
||||
{
|
||||
.type = IIO_LIGHT,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_INT_TIME),
|
||||
.scan_index = MAX44000_SCAN_INDEX_ALS,
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
.realbits = 14,
|
||||
.storagebits = 16,
|
||||
}
|
||||
},
|
||||
{
|
||||
.type = IIO_PROXIMITY,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
|
||||
.scan_index = MAX44000_SCAN_INDEX_PRX,
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
.realbits = 8,
|
||||
.storagebits = 16,
|
||||
}
|
||||
},
|
||||
IIO_CHAN_SOFT_TIMESTAMP(2),
|
||||
{
|
||||
.type = IIO_CURRENT,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
.extend_name = "led",
|
||||
.output = 1,
|
||||
.scan_index = -1,
|
||||
},
|
||||
};
|
||||
|
||||
static int max44000_read_alstim(struct max44000_data *data)
|
||||
{
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(data->regmap, MAX44000_REG_CFG_RX, &val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return (val & MAX44000_CFG_RX_ALSTIM_MASK) >> MAX44000_CFG_RX_ALSTIM_SHIFT;
|
||||
}
|
||||
|
||||
static int max44000_write_alstim(struct max44000_data *data, int val)
|
||||
{
|
||||
return regmap_write_bits(data->regmap, MAX44000_REG_CFG_RX,
|
||||
MAX44000_CFG_RX_ALSTIM_MASK,
|
||||
val << MAX44000_CFG_RX_ALSTIM_SHIFT);
|
||||
}
|
||||
|
||||
static int max44000_read_alspga(struct max44000_data *data)
|
||||
{
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(data->regmap, MAX44000_REG_CFG_RX, &val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return (val & MAX44000_CFG_RX_ALSPGA_MASK) >> MAX44000_CFG_RX_ALSPGA_SHIFT;
|
||||
}
|
||||
|
||||
static int max44000_write_alspga(struct max44000_data *data, int val)
|
||||
{
|
||||
return regmap_write_bits(data->regmap, MAX44000_REG_CFG_RX,
|
||||
MAX44000_CFG_RX_ALSPGA_MASK,
|
||||
val << MAX44000_CFG_RX_ALSPGA_SHIFT);
|
||||
}
|
||||
|
||||
static int max44000_read_alsval(struct max44000_data *data)
|
||||
{
|
||||
u16 regval;
|
||||
int alstim, ret;
|
||||
|
||||
ret = regmap_bulk_read(data->regmap, MAX44000_REG_ALS_DATA_HI,
|
||||
®val, sizeof(regval));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
alstim = ret = max44000_read_alstim(data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
regval = be16_to_cpu(regval);
|
||||
|
||||
/*
|
||||
* Overflow is explained on datasheet page 17.
|
||||
*
|
||||
* It's a warning that either the G or IR channel has become saturated
|
||||
* and that the value in the register is likely incorrect.
|
||||
*
|
||||
* The recommendation is to change the scale (ALSPGA).
|
||||
* The driver just returns the max representable value.
|
||||
*/
|
||||
if (regval & MAX44000_ALSDATA_OVERFLOW)
|
||||
return 0x3FFF;
|
||||
|
||||
return regval << MAX44000_ALSTIM_SHIFT(alstim);
|
||||
}
|
||||
|
||||
static int max44000_write_led_current_raw(struct max44000_data *data, int val)
|
||||
{
|
||||
/* Maybe we should clamp the value instead? */
|
||||
if (val < 0 || val > MAX44000_LED_CURRENT_MAX)
|
||||
return -ERANGE;
|
||||
if (val >= 8)
|
||||
val += 4;
|
||||
return regmap_write_bits(data->regmap, MAX44000_REG_CFG_TX,
|
||||
MAX44000_LED_CURRENT_MASK, val);
|
||||
}
|
||||
|
||||
static int max44000_read_led_current_raw(struct max44000_data *data)
|
||||
{
|
||||
unsigned int regval;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(data->regmap, MAX44000_REG_CFG_TX, ®val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
regval &= MAX44000_LED_CURRENT_MASK;
|
||||
if (regval >= 8)
|
||||
regval -= 4;
|
||||
return regval;
|
||||
}
|
||||
|
||||
static int max44000_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct max44000_data *data = iio_priv(indio_dev);
|
||||
int alstim, alspga;
|
||||
unsigned int regval;
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
switch (chan->type) {
|
||||
case IIO_LIGHT:
|
||||
mutex_lock(&data->lock);
|
||||
ret = max44000_read_alsval(data);
|
||||
mutex_unlock(&data->lock);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = ret;
|
||||
return IIO_VAL_INT;
|
||||
|
||||
case IIO_PROXIMITY:
|
||||
mutex_lock(&data->lock);
|
||||
ret = regmap_read(data->regmap, MAX44000_REG_PRX_DATA, ®val);
|
||||
mutex_unlock(&data->lock);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = regval;
|
||||
return IIO_VAL_INT;
|
||||
|
||||
case IIO_CURRENT:
|
||||
mutex_lock(&data->lock);
|
||||
ret = max44000_read_led_current_raw(data);
|
||||
mutex_unlock(&data->lock);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = ret;
|
||||
return IIO_VAL_INT;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
switch (chan->type) {
|
||||
case IIO_CURRENT:
|
||||
/* Output register is in 10s of miliamps */
|
||||
*val = 10;
|
||||
return IIO_VAL_INT;
|
||||
|
||||
case IIO_LIGHT:
|
||||
mutex_lock(&data->lock);
|
||||
alspga = ret = max44000_read_alspga(data);
|
||||
mutex_unlock(&data->lock);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Avoid negative shifts */
|
||||
*val = (1 << MAX44000_ALSPGA_MAX_SHIFT);
|
||||
*val2 = MAX44000_ALS_TO_LUX_DEFAULT_FRACTION_LOG2
|
||||
+ MAX44000_ALSPGA_MAX_SHIFT
|
||||
- max44000_alspga_shift[alspga];
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
case IIO_CHAN_INFO_INT_TIME:
|
||||
mutex_lock(&data->lock);
|
||||
alstim = ret = max44000_read_alstim(data);
|
||||
mutex_unlock(&data->lock);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = 0;
|
||||
*val2 = max44000_int_time_avail_ns_array[alstim];
|
||||
return IIO_VAL_INT_PLUS_NANO;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int max44000_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
struct max44000_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
if (mask == IIO_CHAN_INFO_RAW && chan->type == IIO_CURRENT) {
|
||||
mutex_lock(&data->lock);
|
||||
ret = max44000_write_led_current_raw(data, val);
|
||||
mutex_unlock(&data->lock);
|
||||
return ret;
|
||||
} else if (mask == IIO_CHAN_INFO_INT_TIME && chan->type == IIO_LIGHT) {
|
||||
s64 valns = val * NSEC_PER_SEC + val2;
|
||||
int alstim = find_closest_descending(valns,
|
||||
max44000_int_time_avail_ns_array,
|
||||
ARRAY_SIZE(max44000_int_time_avail_ns_array));
|
||||
mutex_lock(&data->lock);
|
||||
ret = max44000_write_alstim(data, alstim);
|
||||
mutex_unlock(&data->lock);
|
||||
return ret;
|
||||
} else if (mask == IIO_CHAN_INFO_SCALE && chan->type == IIO_LIGHT) {
|
||||
s64 valus = val * USEC_PER_SEC + val2;
|
||||
int alspga = find_closest(valus,
|
||||
max44000_scale_avail_ulux_array,
|
||||
ARRAY_SIZE(max44000_scale_avail_ulux_array));
|
||||
mutex_lock(&data->lock);
|
||||
ret = max44000_write_alspga(data, alspga);
|
||||
mutex_unlock(&data->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int max44000_write_raw_get_fmt(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
long mask)
|
||||
{
|
||||
if (mask == IIO_CHAN_INFO_INT_TIME && chan->type == IIO_LIGHT)
|
||||
return IIO_VAL_INT_PLUS_NANO;
|
||||
else if (mask == IIO_CHAN_INFO_SCALE && chan->type == IIO_LIGHT)
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
else
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
static IIO_CONST_ATTR(illuminance_integration_time_available, max44000_int_time_avail_str);
|
||||
static IIO_CONST_ATTR(illuminance_scale_available, max44000_scale_avail_str);
|
||||
|
||||
static struct attribute *max44000_attributes[] = {
|
||||
&iio_const_attr_illuminance_integration_time_available.dev_attr.attr,
|
||||
&iio_const_attr_illuminance_scale_available.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group max44000_attribute_group = {
|
||||
.attrs = max44000_attributes,
|
||||
};
|
||||
|
||||
static const struct iio_info max44000_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = max44000_read_raw,
|
||||
.write_raw = max44000_write_raw,
|
||||
.write_raw_get_fmt = max44000_write_raw_get_fmt,
|
||||
.attrs = &max44000_attribute_group,
|
||||
};
|
||||
|
||||
static bool max44000_readable_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case MAX44000_REG_STATUS:
|
||||
case MAX44000_REG_CFG_MAIN:
|
||||
case MAX44000_REG_CFG_RX:
|
||||
case MAX44000_REG_CFG_TX:
|
||||
case MAX44000_REG_ALS_DATA_HI:
|
||||
case MAX44000_REG_ALS_DATA_LO:
|
||||
case MAX44000_REG_PRX_DATA:
|
||||
case MAX44000_REG_ALS_UPTHR_HI:
|
||||
case MAX44000_REG_ALS_UPTHR_LO:
|
||||
case MAX44000_REG_ALS_LOTHR_HI:
|
||||
case MAX44000_REG_ALS_LOTHR_LO:
|
||||
case MAX44000_REG_PST:
|
||||
case MAX44000_REG_PRX_IND:
|
||||
case MAX44000_REG_PRX_THR:
|
||||
case MAX44000_REG_TRIM_GAIN_GREEN:
|
||||
case MAX44000_REG_TRIM_GAIN_IR:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool max44000_writeable_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case MAX44000_REG_CFG_MAIN:
|
||||
case MAX44000_REG_CFG_RX:
|
||||
case MAX44000_REG_CFG_TX:
|
||||
case MAX44000_REG_ALS_UPTHR_HI:
|
||||
case MAX44000_REG_ALS_UPTHR_LO:
|
||||
case MAX44000_REG_ALS_LOTHR_HI:
|
||||
case MAX44000_REG_ALS_LOTHR_LO:
|
||||
case MAX44000_REG_PST:
|
||||
case MAX44000_REG_PRX_IND:
|
||||
case MAX44000_REG_PRX_THR:
|
||||
case MAX44000_REG_TRIM_GAIN_GREEN:
|
||||
case MAX44000_REG_TRIM_GAIN_IR:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool max44000_volatile_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case MAX44000_REG_STATUS:
|
||||
case MAX44000_REG_ALS_DATA_HI:
|
||||
case MAX44000_REG_ALS_DATA_LO:
|
||||
case MAX44000_REG_PRX_DATA:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool max44000_precious_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
return reg == MAX44000_REG_STATUS;
|
||||
}
|
||||
|
||||
static const struct regmap_config max44000_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
|
||||
.max_register = MAX44000_REG_PRX_DATA,
|
||||
.readable_reg = max44000_readable_reg,
|
||||
.writeable_reg = max44000_writeable_reg,
|
||||
.volatile_reg = max44000_volatile_reg,
|
||||
.precious_reg = max44000_precious_reg,
|
||||
|
||||
.use_single_rw = 1,
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
};
|
||||
|
||||
static irqreturn_t max44000_trigger_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct max44000_data *data = iio_priv(indio_dev);
|
||||
u16 buf[8]; /* 2x u16 + padding + 8 bytes timestamp */
|
||||
int index = 0;
|
||||
unsigned int regval;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
if (test_bit(MAX44000_SCAN_INDEX_ALS, indio_dev->active_scan_mask)) {
|
||||
ret = max44000_read_alsval(data);
|
||||
if (ret < 0)
|
||||
goto out_unlock;
|
||||
buf[index++] = ret;
|
||||
}
|
||||
if (test_bit(MAX44000_SCAN_INDEX_PRX, indio_dev->active_scan_mask)) {
|
||||
ret = regmap_read(data->regmap, MAX44000_REG_PRX_DATA, ®val);
|
||||
if (ret < 0)
|
||||
goto out_unlock;
|
||||
buf[index] = regval;
|
||||
}
|
||||
mutex_unlock(&data->lock);
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, buf, iio_get_time_ns());
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
return IRQ_HANDLED;
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&data->lock);
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int max44000_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct max44000_data *data;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret, reg;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
data = iio_priv(indio_dev);
|
||||
data->regmap = devm_regmap_init_i2c(client, &max44000_regmap_config);
|
||||
if (IS_ERR(data->regmap)) {
|
||||
dev_err(&client->dev, "regmap_init failed!\n");
|
||||
return PTR_ERR(data->regmap);
|
||||
}
|
||||
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
mutex_init(&data->lock);
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->info = &max44000_info;
|
||||
indio_dev->name = MAX44000_DRV_NAME;
|
||||
indio_dev->channels = max44000_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(max44000_channels);
|
||||
|
||||
/*
|
||||
* The device doesn't have a reset function so we just clear some
|
||||
* important bits at probe time to ensure sane operation.
|
||||
*
|
||||
* Since we don't support interrupts/events the threshold values are
|
||||
* not important. We also don't touch trim values.
|
||||
*/
|
||||
|
||||
/* Reset ALS scaling bits */
|
||||
ret = regmap_write(data->regmap, MAX44000_REG_CFG_RX,
|
||||
MAX44000_REG_CFG_RX_DEFAULT);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "failed to write default CFG_RX: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* By default the LED pulse used for the proximity sensor is disabled.
|
||||
* Set a middle value so that we get some sort of valid data by default.
|
||||
*/
|
||||
ret = max44000_write_led_current_raw(data, MAX44000_LED_CURRENT_DEFAULT);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "failed to write init config: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Reset CFG bits to ALS_PRX mode which allows easy reading of both values. */
|
||||
reg = MAX44000_CFG_TRIM | MAX44000_CFG_MODE_ALS_PRX;
|
||||
ret = regmap_write(data->regmap, MAX44000_REG_CFG_MAIN, reg);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "failed to write init config: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Read status at least once to clear any stale interrupt bits. */
|
||||
ret = regmap_read(data->regmap, MAX44000_REG_STATUS, ®);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "failed to read init status: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, NULL, max44000_trigger_handler, NULL);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "iio triggered buffer setup failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return iio_device_register(indio_dev);
|
||||
}
|
||||
|
||||
static int max44000_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;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id max44000_id[] = {
|
||||
{"max44000", 0},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, max44000_id);
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
static const struct acpi_device_id max44000_acpi_match[] = {
|
||||
{"MAX44000", 0},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, max44000_acpi_match);
|
||||
#endif
|
||||
|
||||
static struct i2c_driver max44000_driver = {
|
||||
.driver = {
|
||||
.name = MAX44000_DRV_NAME,
|
||||
.acpi_match_table = ACPI_PTR(max44000_acpi_match),
|
||||
},
|
||||
.probe = max44000_probe,
|
||||
.remove = max44000_remove,
|
||||
.id_table = max44000_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(max44000_driver);
|
||||
|
||||
MODULE_AUTHOR("Crestez Dan Leonard <leonard.crestez@intel.com>");
|
||||
MODULE_DESCRIPTION("MAX44000 Ambient and Infrared Proximity Sensor");
|
||||
MODULE_LICENSE("GPL v2");
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue