Commit 9f827d80 authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman
Browse files

Merge tag 'iio-for-4.4a' of...

Merge tag 'iio-for-4.4a' of git://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio into staging-next

Jonathan writes:

First round of new driver, new functionality and cleanups for IIO in the 4.4 cycle

New device support
* APDS9960 ALS + proximity driver
* bmg160 SPI devices.
* HDC100x humidity sensors
* Holt HI-8435 threshold detector
* mma8453Q accelerometer added to the mma8452 driver
* mma86452FC and mma8653FC accelerometers added to the mma8452 driver
* mxc4005 accelerometer
* PulsedLight LIDAR
* SensorTech VZ89x volatile organic compound sensor
* UPISEMI uS5182d ALS and proximity sensors

New core functionality
* triggered events - use triggers to check for changes in threshold type
  detectors on devices with out interrupt support.  First user is the holt
  comparator.
* chemical concentration and resistance channel types.

New driver functionality
* vf610
  - buffer support.
  - followup coccinelle warning fix.

Core rework
* buffers
  - break out callback buffer to own module.
  - move buffer implementations to a new subdirectory
* percolate the error code form iio_event_getfd out to userspace
  rather than giving a missleading error later on.

Cleanups
* adddac drivers
  - use BIT macro where appropriate.
* meter drivers
  - use BIT macro where appropriate.

* ad7303
 - add an OF match table to line up with the binding docs.
* adc128s052
  - add an OF match table to line up with the binding docs.
* adf4350
  - add an OF match table to line up with the binding docs
* as3935
  - add an OF match table to line up with the binding docs.
* berlin2-adc
  - use GENMASK and BIT for masks
  - prevent attempting to sample multiple channels at once by moving a
    mutex scop
  - coding style cleanups
* bmg150_magn
  - kconfig sort order was wrong - fix it.
* bmg160
  - use i2c regmap and drop all uses of i2c_client
  - separate i2c and core driver
* cc10001_adc
  - kconfig sort order was wrong - fix it.
* evgen (dummy driver helper module)
  - move interrupt generation to irq_work to reduce differences between
    the dummy driver and real hardware drivers.
* hmc5843
  - set the name dynamically rather than to a fixed value for one of the
    suported parts.
  - export module alias information to allow autoprobing of module.
* lpc32xx
  - on failure to get resource or irq return -ENXIO as uppose to -EBUSY
* max1027
  - set .of_match_table to actually allow OF style matching.
* max5821
  - add MODULE_DEVICE_TABLE for OF table.
* mma8452
  - refactor to separate out chip specific data.
  - add freefall / motion interrupt source for devices that do their
    interrupts slightly differently.
  - update copywrite notice.
  - leave naming of events directory in sysfs to the core
* mcp320x
  - set .of_match_table so that it can be use for OF style matching.
* mlx90614
  - Implement filter configuration (note the datasheet changed as a result
    of the driver reviews to include the values we needed ;)
* opt3001
  - drop .owner field as assigned by platform driver core.
* si7020
  - replace a bitmask on the humidity values with a more correct range
    check.
* stk310
  - improved error handling.
  - use BIT macro where appropriate and use the resulting defines
    instead of magic numbers in the code.
  - fix indentation
* st-sensors
  - add debugfs register read hook
* tsl4531
  - fix error handling in check_id
* twl6030
  - fix module autoload for OF
* iio-trig-sysfs
  - document add and remove attribute
* trigger in staging
  - code alignment fixes.
  - braces on both branches of if statement if needed for one.
* xilinx-xadc
  - push interrupts into hardirq context as there isn't much in them
    any more and it avoids breaking PREEMPT_RT builds due to the use
    of a spinlock between the hardirq and the thread.

Tools
* event-monitor
  - report unsupported events.  We keep expanding what can come from drivers
    so give a helpful error if one turns up in an out of date userspace
    program.
* generic-buffer
  - helpful message about needing to enable a channel to start the buffer.
parents 99207b80 1d2f1e08
......@@ -581,6 +581,7 @@ What: /sys/.../iio:deviceX/events/in_voltageY_supply_thresh_rising_en
What: /sys/.../iio:deviceX/events/in_voltageY_supply_thresh_falling_en
What: /sys/.../iio:deviceX/events/in_voltageY_thresh_rising_en
What: /sys/.../iio:deviceX/events/in_voltageY_thresh_falling_en
What: /sys/.../iio:deviceX/events/in_voltageY_thresh_either_en
What: /sys/.../iio:deviceX/events/in_tempY_thresh_rising_en
What: /sys/.../iio:deviceX/events/in_tempY_thresh_falling_en
KernelVersion: 2.6.37
......@@ -1459,3 +1460,22 @@ Description:
measurements and return the average value as output data. Each
value resulted from <type>[_name]_oversampling_ratio measurements
is considered as one sample for <type>[_name]_sampling_frequency.
What: /sys/bus/iio/devices/iio:deviceX/in_concentration_raw
What: /sys/bus/iio/devices/iio:deviceX/in_concentrationX_raw
What: /sys/bus/iio/devices/iio:deviceX/in_concentration_co2_raw
What: /sys/bus/iio/devices/iio:deviceX/in_concentrationX_co2_raw
What: /sys/bus/iio/devices/iio:deviceX/in_concentration_voc_raw
What: /sys/bus/iio/devices/iio:deviceX/in_concentrationX_voc_raw
KernelVersion: 4.3
Contact: linux-iio@vger.kernel.org
Description:
Raw (unscaled no offset etc.) percentage reading of a substance.
What: /sys/bus/iio/devices/iio:deviceX/in_resistance_raw
What: /sys/bus/iio/devices/iio:deviceX/in_resistanceX_raw
KernelVersion: 4.3
Contact: linux-iio@vger.kernel.org
Description:
Raw (unscaled no offset etc.) resistance reading that can be processed
into an ohm value.
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_sensing_mode
Date: August 2015
KernelVersion: 4.2.0
Contact: source@cogentembedded.com
Description:
Program sensor type for threshold detector inputs.
Could be either "GND-Open" or "Supply-Open" mode. Y is a
threshold detector input channel. Channels 0..7, 8..15, 16..23
and 24..31 has common sensor types.
What: /sys/bus/iio/devices/iio:deviceX/events/in_voltageY_thresh_falling_value
Date: August 2015
KernelVersion: 4.2.0
Contact: source@cogentembedded.com
Description:
Channel Y low voltage threshold. If sensor input voltage goes lower then
this value then the threshold falling event is pushed.
Depending on in_voltageY_sensing_mode the low voltage threshold
is separately set for "GND-Open" and "Supply-Open" modes.
Channels 0..31 have common low threshold values, but could have different
sensing_modes.
The low voltage threshold range is between 2..21V.
Hysteresis between low and high thresholds can not be lower then 2 and
can not be odd.
If falling threshold results hysteresis to odd value then rising
threshold is automatically subtracted by one.
What: /sys/bus/iio/devices/iio:deviceX/events/in_voltageY_thresh_rising_value
Date: August 2015
KernelVersion: 4.2.0
Contact: source@cogentembedded.com
Description:
Channel Y high voltage threshold. If sensor input voltage goes higher then
this value then the threshold rising event is pushed.
Depending on in_voltageY_sensing_mode the high voltage threshold
is separately set for "GND-Open" and "Supply-Open" modes.
Channels 0..31 have common high threshold values, but could have different
sensing_modes.
The high voltage threshold range is between 3..22V.
Hysteresis between low and high thresholds can not be lower then 2 and
can not be odd.
If rising threshold results hysteresis to odd value then falling
threshold is automatically appended by one.
What: /sys/bus/iio/devices/iio:deviceX/in_concentration_VOC_short_raw
Date: September 2015
KernelVersion: 4.3
Contact: Matt Ranostay <mranostay@gmail.com>
Description:
Get the raw calibration VOC value from the sensor.
This value has little application outside of calibration.
What: /sys/bus/iio/devices/iio:deviceX/out_current_heater_raw
What: /sys/bus/iio/devices/iio:deviceX/out_current_heater_raw_available
KernelVersion: 4.3
Contact: linux-iio@vger.kernel.org
Description:
Controls the heater device within the humidity sensor to get
rid of excess condensation.
Valid control values are 0 = OFF, and 1 = ON.
......@@ -18,3 +18,25 @@ Description:
trigger. In order to associate the trigger with an IIO device
one should write this name string to
/sys/bus/iio/devices/iio:deviceY/trigger/current_trigger.
What: /sys/bus/iio/devices/iio_sysfs_trigger/add_trigger
KernelVersion: 2.6.39
Contact: linux-iio@vger.kernel.org
Description:
This attribute is provided by the iio-trig-sysfs stand-alone
driver and it is used to activate the creation of a new trigger.
In order to achieve this, one should write a positive integer
into the associated file, which will serve as the id of the
trigger. If the trigger with the specified id is already present
in the system, an invalid argument message will be returned.
What: /sys/bus/iio/devices/iio_sysfs_trigger/remove_trigger
KernelVersion: 2.6.39
Contact: linux-iio@vger.kernel.org
Description:
This attribute is used to unregister and delete a previously
created trigger from the list of available triggers. In order to
achieve this, one should write a positive integer into the
associated file, representing the id of the trigger that needs
to be removed. If the trigger can't be found, an invalid
argument message will be returned to the user.
......@@ -578,7 +578,7 @@
work together.
</para>
<sect2 id="iiotrigbufsetup"> <title> IIO triggered buffer setup</title>
!Edrivers/iio/industrialio-triggered-buffer.c
!Edrivers/iio/buffer/industrialio-triggered-buffer.c
!Finclude/linux/iio/iio.h iio_buffer_setup_ops
......
......@@ -54,7 +54,6 @@ epson,rx8581 I2C-BUS INTERFACE REAL TIME CLOCK MODULE
fsl,mag3110 MAG3110: Xtrinsic High Accuracy, 3D Magnetometer
fsl,mc13892 MC13892: Power Management Integrated Circuit (PMIC) for i.MX35/51
fsl,mma8450 MMA8450Q: Xtrinsic Low-power, 3-axis Xtrinsic Accelerometer
fsl,mma8452 MMA8452Q: 3-axis 12-bit / 8-bit Digital Accelerometer
fsl,mpr121 MPR121: Proximity Capacitive Touch Sensor Controller
fsl,sgtl5000 SGTL5000: Ultra Low-Power Audio Codec
gmt,g751 G751: Digital Temperature Sensor and Thermal Watchdog with Two-Wire Interface
......@@ -80,6 +79,7 @@ oki,ml86v7667 OKI ML86V7667 video decoder
ovti,ov5642 OV5642: Color CMOS QSXGA (5-megapixel) Image Sensor with OmniBSI and Embedded TrueFocus
pericom,pt7c4338 Real-time Clock Module
plx,pex8648 48-Lane, 12-Port PCI Express Gen 2 (5.0 GT/s) Switch
pulsedlight,lidar-lite-v2 Pulsedlight LIDAR range-finding sensor
ramtron,24c64 i2c serial eeprom (24cxx)
ricoh,r2025sd I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC
ricoh,r2221tl I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC
......@@ -88,6 +88,7 @@ ricoh,rs5c372b I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC
ricoh,rv5c386 I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC
ricoh,rv5c387a I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC
samsung,24ad0xd1 S524AD0XF1 (128K/256K-bit Serial EEPROM for Low Power)
sgx,vz89x SGX Sensortech VZ89X Sensors
sii,s35390a 2-wire CMOS real-time clock
skyworks,sky81452 Skyworks SKY81452: Six-Channel White LED Driver with Touch Panel Bias Supply
st-micro,24c256 i2c serial eeprom (24cxx)
......
Freescale MMA8452Q, MMA8453Q, MMA8652FC or MMA8653FC triaxial accelerometer
Required properties:
- compatible: should contain one of
* "fsl,mma8452"
* "fsl,mma8453"
* "fsl,mma8652"
* "fsl,mma8653"
- reg: the I2C address of the chip
Optional properties:
- interrupt-parent: should be the phandle for the interrupt controller
- interrupts: interrupt mapping for GPIO IRQ
Example:
mma8453fc@1d {
compatible = "fsl,mma8453";
reg = <0x1d>;
interrupt-parent = <&gpio1>;
interrupts = <5 0>;
};
Holt Integrated Circuits HI-8435 threshold detector bindings
Required properties:
- compatible: should be "holt,hi8435"
- reg: spi chip select number for the device
Recommended properties:
- spi-max-frequency: definition as per
Documentation/devicetree/bindings/spi/spi-bus.txt
Optional properties:
- gpios: GPIO used for controlling the reset pin
Example:
sensor@0 {
compatible = "holt,hi8435";
reg = <0>;
gpios = <&gpio6 1 0>;
spi-max-frequency = <1000000>;
};
* Avago APDS9960 gesture/RGB/ALS/proximity sensor
http://www.avagotech.com/docs/AV02-4191EN
Required properties:
- compatible: must be "avago,apds9960"
- reg: the I2c address of the sensor
- interrupt-parent: should be the phandle for the interrupt controller
- interrupts : the sole interrupt generated by the device
Refer to interrupt-controller/interrupts.txt for generic interrupt client
node bindings.
Example:
apds9960@39 {
compatible = "avago,apds9960";
reg = <0x39>;
interrupt-parent = <&gpio1>;
interrupts = <16 1>;
};
* UPISEMI us5182d I2C ALS and Proximity sensor
Required properties:
- compatible: must be "upisemi,usd5182"
- reg: the I2C address of the device
Optional properties:
- upisemi,glass-coef: glass attenuation factor - compensation factor of
resolution 1000 for material transmittance.
- upisemi,dark-ths: array of 8 elements containing 16-bit thresholds (adc
counts) corresponding to every scale.
- upisemi,upper-dark-gain: 8-bit dark gain compensation factor(4 int and 4
fractional bits - Q4.4) applied when light > threshold
- upisemi,lower-dark-gain: 8-bit dark gain compensation factor(4 int and 4
fractional bits - Q4.4) applied when light < threshold
If the optional properties are not specified these factors will default to the
values in the below example.
The glass-coef defaults to no compensation for the covering material.
The threshold array defaults to experimental values that work with US5182D
sensor on evaluation board - roughly between 12-32 lux.
There will be no dark-gain compensation by default when ALS > thresh
(0 * dark-gain), and a 1.35 compensation factor when ALS < thresh.
Example:
usd5182@39 {
compatible = "upisemi,usd5182";
reg = <0x39>;
upisemi,glass-coef = < 1000 >;
upisemi,dark-ths = /bits/ 16 <170 200 512 512 800 2000 4000 8000>;
upisemi,upper-dark-gain = /bits/ 8 <0x00>;
upisemi,lower-dark-gain = /bits/ 8 <0x16>;
};
......@@ -101,6 +101,7 @@ himax Himax Technologies, Inc.
hisilicon Hisilicon Limited.
hit Hitachi Ltd.
hitex Hitex Development Tools
holt Holt Integrated Circuits, Inc.
honeywell Honeywell
hp Hewlett Packard
i2se I2SE GmbH
......@@ -169,6 +170,7 @@ phytec PHYTEC Messtechnik GmbH
picochip Picochip Ltd
plathome Plat'Home Co., Ltd.
pixcir PIXCIR MICROELECTRONICS Co., Ltd
pulsedlight PulsedLight, Inc
powervr PowerVR (deprecated, use img)
qca Qualcomm Atheros, Inc.
qcom Qualcomm Technologies, Inc
......@@ -191,6 +193,7 @@ sbs Smart Battery System
schindler Schindler
seagate Seagate Technology PLC
semtech Semtech Corporation
sgx SGX Sensortech
sharp Sharp Corporation
sil Silicon Image
silabs Silicon Laboratories
......@@ -223,6 +226,7 @@ toshiba Toshiba Corporation
toumaz Toumaz
tplink TP-LINK Technologies Co., Ltd.
truly Truly Semiconductors Limited
upisemi uPI Semiconductor Corp.
usi Universal Scientific Industrial Co., Ltd.
v3 V3 Semiconductor
variscite Variscite Ltd.
......
......@@ -6918,6 +6918,13 @@ S: Supported
F: include/linux/mlx5/
F: drivers/infiniband/hw/mlx5/
MELEXIS MLX90614 DRIVER
M: Crt Mori <cmo@melexis.com>
L: linux-iio@vger.kernel.org
W: http://www.melexis.com
S: Supported
F: drivers/iio/temperature/mlx90614.c
MN88472 MEDIA DRIVER
M: Antti Palosaari <crope@iki.fi>
L: linux-media@vger.kernel.org
......
......@@ -19,27 +19,7 @@ config IIO_BUFFER
acquisition methods.
if IIO_BUFFER
config IIO_BUFFER_CB
bool "IIO callback buffer used for push in-kernel interfaces"
help
Should be selected by any drivers that do in-kernel push
usage. That is, those where the data is pushed to the consumer.
config IIO_KFIFO_BUF
tristate "Industrial I/O buffering based on kfifo"
help
A simple fifo based on kfifo. Note that this currently provides
no buffer events so it is up to userspace to work out how
often to read from the buffer.
config IIO_TRIGGERED_BUFFER
tristate
select IIO_TRIGGER
select IIO_KFIFO_BUF
help
Provides helper functions for setting up triggered buffers.
source "drivers/iio/buffer/Kconfig"
endif # IIO_BUFFER
config IIO_TRIGGER
......@@ -58,9 +38,16 @@ config IIO_CONSUMERS_PER_TRIGGER
This value controls the maximum number of consumers that a
given trigger may handle. Default is 2.
config IIO_TRIGGERED_EVENT
tristate
select IIO_TRIGGER
help
Provides helper functions for setting up triggered events.
source "drivers/iio/accel/Kconfig"
source "drivers/iio/adc/Kconfig"
source "drivers/iio/amplifiers/Kconfig"
source "drivers/iio/chemical/Kconfig"
source "drivers/iio/common/Kconfig"
source "drivers/iio/dac/Kconfig"
source "drivers/iio/frequency/Kconfig"
......
......@@ -6,14 +6,14 @@ obj-$(CONFIG_IIO) += industrialio.o
industrialio-y := industrialio-core.o industrialio-event.o inkern.o
industrialio-$(CONFIG_IIO_BUFFER) += industrialio-buffer.o
industrialio-$(CONFIG_IIO_TRIGGER) += industrialio-trigger.o
industrialio-$(CONFIG_IIO_BUFFER_CB) += buffer_cb.o
obj-$(CONFIG_IIO_TRIGGERED_BUFFER) += industrialio-triggered-buffer.o
obj-$(CONFIG_IIO_KFIFO_BUF) += kfifo_buf.o
obj-$(CONFIG_IIO_TRIGGERED_EVENT) += industrialio-triggered-event.o
obj-y += accel/
obj-y += adc/
obj-y += amplifiers/
obj-y += buffer/
obj-y += chemical/
obj-y += common/
obj-y += dac/
obj-y += gyro/
......
......@@ -100,13 +100,13 @@ config KXCJK1013
be called kxcjk-1013.
config MMA8452
tristate "Freescale MMA8452Q Accelerometer Driver"
tristate "Freescale MMA8452Q and similar Accelerometers Driver"
depends on I2C
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
help
Say yes here to build support for the Freescale MMA8452Q 3-axis
accelerometer.
Say yes here to build support for the following Freescale 3-axis
accelerometers: MMA8452Q, MMA8453Q, MMA8652FC, MMA8653FC.
To compile this driver as a module, choose M here: the module
will be called mma8452.
......@@ -137,6 +137,19 @@ config MMA9553
To compile this driver as a module, choose M here: the module
will be called mma9553.
config MXC4005
tristate "Memsic MXC4005XC 3-Axis Accelerometer Driver"
depends on I2C
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
select REGMAP_I2C
help
Say yes here to build support for the Memsic MXC4005XC 3-axis
accelerometer.
To compile this driver as a module, choose M. The module will be
called mxc4005.
config STK8312
tristate "Sensortek STK8312 3-Axis Accelerometer Driver"
depends on I2C
......
......@@ -14,6 +14,8 @@ obj-$(CONFIG_MMA9551_CORE) += mma9551_core.o
obj-$(CONFIG_MMA9551) += mma9551.o
obj-$(CONFIG_MMA9553) += mma9553.o
obj-$(CONFIG_MXC4005) += mxc4005.o
obj-$(CONFIG_STK8312) += stk8312.o
obj-$(CONFIG_STK8BA50) += stk8ba50.o
......
/*
* mma8452.c - Support for Freescale MMA8452Q 3-axis 12-bit accelerometer
* mma8452.c - Support for following Freescale 3-axis accelerometers:
*
* MMA8452Q (12 bit)
* MMA8453Q (10 bit)
* MMA8652FC (12 bit)
* MMA8653FC (10 bit)
*
* Copyright 2015 Martin Kepplinger <martin.kepplinger@theobroma-systems.com>
* Copyright 2014 Peter Meerwald <pmeerw@pmeerw.net>
*
* This file is subject to the terms and conditions of version 2 of
......@@ -22,10 +28,11 @@
#include <linux/iio/triggered_buffer.h>
#include <linux/iio/events.h>
#include <linux/delay.h>
#include <linux/of_device.h>
#define MMA8452_STATUS 0x00
#define MMA8452_STATUS_DRDY (BIT(2) | BIT(1) | BIT(0))
#define MMA8452_OUT_X 0x01 /* MSB first, 12-bit */
#define MMA8452_OUT_X 0x01 /* MSB first */
#define MMA8452_OUT_Y 0x03
#define MMA8452_OUT_Z 0x05
#define MMA8452_INT_SRC 0x0c
......@@ -38,6 +45,16 @@
#define MMA8452_DATA_CFG_HPF_MASK BIT(4)
#define MMA8452_HP_FILTER_CUTOFF 0x0f
#define MMA8452_HP_FILTER_CUTOFF_SEL_MASK GENMASK(1, 0)
#define MMA8452_FF_MT_CFG 0x15
#define MMA8452_FF_MT_CFG_OAE BIT(6)
#define MMA8452_FF_MT_CFG_ELE BIT(7)
#define MMA8452_FF_MT_SRC 0x16
#define MMA8452_FF_MT_SRC_XHE BIT(1)
#define MMA8452_FF_MT_SRC_YHE BIT(3)
#define MMA8452_FF_MT_SRC_ZHE BIT(5)
#define MMA8452_FF_MT_THS 0x17
#define MMA8452_FF_MT_THS_MASK 0x7f
#define MMA8452_FF_MT_COUNT 0x18
#define MMA8452_TRANSIENT_CFG 0x1d
#define MMA8452_TRANSIENT_CFG_HPF_BYP BIT(0)
#define MMA8452_TRANSIENT_CFG_CHAN(chan) BIT(chan + 1)
......@@ -65,15 +82,65 @@
#define MMA8452_MAX_REG 0x31
#define MMA8452_INT_DRDY BIT(0)
#define MMA8452_INT_FF_MT BIT(2)
#define MMA8452_INT_TRANS BIT(5)
#define MMA8452_DEVICE_ID 0x2a
#define MMA8453_DEVICE_ID 0x3a
#define MMA8652_DEVICE_ID 0x4a
#define MMA8653_DEVICE_ID 0x5a
struct mma8452_data {
struct i2c_client *client;
struct mutex lock;
u8 ctrl_reg1;
u8 data_cfg;
const struct mma_chip_info *chip_info;
};
/**
* struct mma_chip_info - chip specific data for Freescale's accelerometers
* @chip_id: WHO_AM_I register's value
* @channels: struct iio_chan_spec matching the device's
* capabilities
* @num_channels: number of channels
* @mma_scales: scale factors for converting register values
* to m/s^2; 3 modes: 2g, 4g, 8g; 2 integers
* per mode: m/s^2 and micro m/s^2
* @ev_cfg: event config register address
* @ev_cfg_ele: latch bit in event config register
* @ev_cfg_chan_shift: number of the bit to enable events in X
* direction; in event config register
* @ev_src: event source register address
* @ev_src_xe: bit in event source register that indicates
* an event in X direction
* @ev_src_ye: bit in event source register that indicates
* an event in Y direction
* @ev_src_ze: bit in event source register that indicates
* an event in Z direction
* @ev_ths: event threshold register address
* @ev_ths_mask: mask for the threshold value
* @ev_count: event count (period) register address
*
* Since not all chips supported by the driver support comparing high pass
* filtered data for events (interrupts), different interrupt sources are
* used for different chips and the relevant registers are included here.
*/
struct mma_chip_info {
u8 chip_id;
const struct iio_chan_spec *channels;
int num_channels;
const int mma_scales[3][2];
u8 ev_cfg;
u8 ev_cfg_ele;
u8 ev_cfg_chan_shift;
u8 ev_src;
u8 ev_src_xe;
u8 ev_src_ye;
u8 ev_src_ze;
u8 ev_ths;
u8 ev_ths_mask;
u8 ev_count;
};
static int mma8452_drdy(struct mma8452_data *data)
......@@ -143,16 +210,6 @@ static const int mma8452_samp_freq[8][2] = {
{6, 250000}, {1, 560000}
};
/*
* Hardware has fullscale of -2G, -4G, -8G corresponding to raw value -2048
* The userspace interface uses m/s^2 and we declare micro units
* So scale factor is given by:
* g * N * 1000000 / 2048 for N = 2, 4, 8 and g = 9.80665
*/
static const int mma8452_scales[3][2] = {
{0, 9577}, {0, 19154}, {0, 38307}
};
/* Datasheet table 35 (step time vs sample frequency) */
static const int mma8452_transient_time_step_us[8] = {
1250,
......@@ -189,8 +246,11 @@ static ssize_t mma8452_show_scale_avail(struct device *dev,
struct device_attribute *attr,
char *buf)
{
return mma8452_show_int_plus_micros(buf, mma8452_scales,
ARRAY_SIZE(mma8452_scales));
struct mma8452_data *data = iio_priv(i2c_get_clientdata(
to_i2c_client(dev)));
return mma8452_show_int_plus_micros(buf, data->chip_info->mma_scales,
ARRAY_SIZE(data->chip_info->mma_scales));
}
static ssize_t mma8452_show_hp_cutoff_avail(struct device *dev,
......@@ -221,9 +281,8 @@ static int mma8452_get_samp_freq_index(struct mma8452_data *data,
static int mma8452_get_scale_index(struct mma8452_data *data, int val, int val2)
{
return mma8452_get_int_plus_micros_index(mma8452_scales,
ARRAY_SIZE(mma8452_scales),
val, val2);
return mma8452_get_int_plus_micros_index(data->chip_info->mma_scales,
ARRAY_SIZE(data->chip_info->mma_scales), val, val2);
}
static int mma8452_get_hp_filter_index(struct mma8452_data *data,
......@@ -270,14 +329,15 @@ static int mma8452_read_raw(struct iio_dev *indio_dev,
if (ret < 0)
return ret;
*val = sign_extend32(be16_to_cpu(buffer[chan->scan_index]) >> 4,
11);
*val = sign_extend32(be16_to_cpu(
buffer[chan->scan_index]) >> chan->scan_type.shift,
chan->scan_type.realbits - 1);
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
i = data->data_cfg & MMA8452_DATA_CFG_FS_MASK;
*val = mma8452_scales[i][0];
*val2 = mma8452_scales[i][1];
*val = data->chip_info->mma_scales[i][0];
*val2 = data->chip_info->mma_scales[i][1];
return IIO_VAL_INT_PLUS_MICRO;
case IIO_CHAN_INFO_SAMP_FREQ:
......@@ -439,17 +499,17 @@ static int mma8452_read_thresh(struct iio_dev *indio_dev,
switch (info) {
case IIO_EV_INFO_VALUE:
ret = i2c_smbus_read_byte_data(data->client,
MMA8452_TRANSIENT_THS);
data->chip_info->ev_ths);
if (ret < 0)
return ret;
*val = ret & MMA8452_TRANSIENT_THS_MASK;
*val = ret & data->chip_info->ev_ths_mask;
return IIO_VAL_INT;
case IIO_EV_INFO_PERIOD:
ret = i2c_smbus_read_byte_data(data->client,
MMA8452_TRANSIENT_COUNT);
data->chip_info->ev_count);
if (ret < 0)
return ret;
......@@ -497,7 +557,8 @@ static int mma8452_write_thresh(struct iio_dev *indio_dev,
if (val < 0 || val > MMA8452_TRANSIENT_THS_MASK)
return -EINVAL;
return mma8452_change_config(data, MMA8452_TRANSIENT_THS, val);
return mma8452_change_config(data, data->chip_info->ev_ths,
val);
case IIO_EV_INFO_PERIOD:
steps = (val * USEC_PER_SEC + val2) /
......@@ -507,7 +568,7 @@ static int mma8452_write_thresh(struct iio_dev *indio_dev,
if (steps < 0 || steps > 0xff)
return -EINVAL;
return mma8452_change_config(data, MMA8452_TRANSIENT_COUNT,
return mma8452_change_config(data, data->chip_info->ev_count,
steps);
case IIO_EV_INFO_HIGH_PASS_FILTER_3DB:
......@@ -538,13 +599,15 @@ static int mma8452_read_event_config(struct iio_dev *indio_dev,
enum iio_event_direction dir)
{
struct mma8452_data *data = iio_priv(indio_dev);
const struct mma_chip_info *chip = data->chip_info;
int ret;
ret = i2c_smbus_read_byte_data(data->client, MMA8452_TRANSIENT_CFG);
ret = i2c_smbus_read_byte_data(data->client,
data->chip_info->ev_cfg);
if (ret < 0)
return ret;
return ret & MMA8452_TRANSIENT_CFG_CHAN(chan->scan_index) ? 1 : 0;
return !!(ret & BIT(chan->scan_index + chip->ev_cfg_chan_shift));
}
static int mma8452_write_event_config(struct iio_dev *indio_dev,
......@@ -554,20 +617,22 @@ static int mma8452_write_event_config(struct iio_dev *indio_dev,
int state)
{
struct mma8452_data *data = iio_priv(indio_dev);
const struct mma_chip_info *chip = data->chip_info;
int val;
val = i2c_smbus_read_byte_data(data->client, MMA8452_TRANSIENT_CFG);
val = i2c_smbus_read_byte_data(data->client, chip->ev_cfg);
if (val < 0)
return val;
if (state)
val |= MMA8452_TRANSIENT_CFG_CHAN(chan->scan_index);
val |= BIT(chan->scan_index + chip->ev_cfg_chan_shift);
else
val &= ~MMA8452_TRANSIENT_CFG_CHAN(chan->scan_index);
val &= ~BIT(chan->scan_index + chip->ev_cfg_chan_shift);
val |= MMA8452_TRANSIENT_CFG_ELE;
val |= chip->ev_cfg_ele;
val |= MMA8452_FF_MT_CFG_OAE;
return mma8452_change_config(data, MMA8452_TRANSIENT_CFG, val);
return mma8452_change_config(data, chip->ev_cfg, val);
}
static void mma8452_transient_interrupt(struct iio_dev *indio_dev)
......@@ -576,25 +641,25 @@ static void mma8452_transient_interrupt(struct iio_dev *indio_dev)
s64 ts = iio_get_time_ns();
int src;
src = i2c_smbus_read_byte_data(data->client, MMA8452_TRANSIENT_SRC);
src = i2c_smbus_read_byte_data(data->client, data->chip_info->ev_src);
if (src < 0)
return;
if (src & MMA8452_TRANSIENT_SRC_XTRANSE)
if (src & data->chip_info->ev_src_xe)
iio_push_event(indio_dev,
IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_X,
IIO_EV_TYPE_MAG,
IIO_EV_DIR_RISING),
ts);
if (src & MMA8452_TRANSIENT_SRC_YTRANSE)
if (src & data->chip_info->ev_src_ye)
iio_push_event(indio_dev,
IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_Y,
IIO_EV_TYPE_MAG,
IIO_EV_DIR_RISING),
ts);
if (src & MMA8452_TRANSIENT_SRC_ZTRANSE)
if (src & data->chip_info->ev_src_ze)
iio_push_event(indio_dev,
IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_Z,
IIO_EV_TYPE_MAG,
......@@ -606,6 +671,7 @@ static irqreturn_t mma8452_interrupt(int irq, void *p)
{
struct iio_dev *indio_dev = p;
struct mma8452_data *data = iio_priv(indio_dev);
const struct mma_chip_info *chip = data->chip_info;
int ret = IRQ_NONE;
int src;
......@@ -618,7 +684,10 @@ static irqreturn_t mma8452_interrupt(int irq, void *p)
ret = IRQ_HANDLED;
}
if (src & MMA8452_INT_TRANS) {
if ((src & MMA8452_INT_TRANS &&
chip->ev_src == MMA8452_TRANSIENT_SRC) ||
(src & MMA8452_INT_FF_MT &&
chip->ev_src == MMA8452_FF_MT_SRC)) {
mma8452_transient_interrupt(indio_dev);
ret = IRQ_HANDLED;
}
......@@ -680,6 +749,16 @@ static const struct iio_event_spec mma8452_transient_event[] = {
},
};
static const struct iio_event_spec mma8452_motion_event[] = {
{
.type = IIO_EV_TYPE_MAG,
.dir = IIO_EV_DIR_RISING,
.mask_separate = BIT(IIO_EV_INFO_ENABLE),
.mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) |
BIT(IIO_EV_INFO_PERIOD)
},
};
/*
* Threshold is configured in fixed 8G/127 steps regardless of
* currently selected scale for measurement.
......@@ -693,10 +772,9 @@ static struct attribute *mma8452_event_attributes[] = {
static struct attribute_group mma8452_event_attribute_group = {
.attrs = mma8452_event_attributes,
.name = "events",
};
#define MMA8452_CHANNEL(axis, idx) { \
#define MMA8452_CHANNEL(axis, idx, bits) { \
.type = IIO_ACCEL, \
.modified = 1, \
.channel2 = IIO_MOD_##axis, \
......@@ -708,22 +786,144 @@ static struct attribute_group mma8452_event_attribute_group = {
.scan_index = idx, \
.scan_type = { \
.sign = 's', \
.realbits = 12, \
.realbits = (bits), \
.storagebits = 16, \
.shift = 4, \
.shift = 16 - (bits), \
.endianness = IIO_BE, \
}, \
.event_spec = mma8452_transient_event, \
.num_event_specs = ARRAY_SIZE(mma8452_transient_event), \
}
#define MMA8652_CHANNEL(axis, idx, bits) { \
.type = IIO_ACCEL, \
.modified = 1, \
.channel2 = IIO_MOD_##axis, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
BIT(IIO_CHAN_INFO_CALIBBIAS), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
BIT(IIO_CHAN_INFO_SCALE), \
.scan_index = idx, \
.scan_type = { \
.sign = 's', \
.realbits = (bits), \
.storagebits = 16, \
.shift = 16 - (bits), \
.endianness = IIO_BE, \
}, \
.event_spec = mma8452_motion_event, \
.num_event_specs = ARRAY_SIZE(mma8452_motion_event), \
}
static const struct iio_chan_spec mma8452_channels[] = {
MMA8452_CHANNEL(X, 0),
MMA8452_CHANNEL(Y, 1),
MMA8452_CHANNEL(Z, 2),
MMA8452_CHANNEL(X, 0, 12),
MMA8452_CHANNEL(Y, 1, 12),
MMA8452_CHANNEL(Z, 2, 12),
IIO_CHAN_SOFT_TIMESTAMP(3),
};
static const struct iio_chan_spec mma8453_channels[] = {
MMA8452_CHANNEL(X, 0, 10),
MMA8452_CHANNEL(Y, 1, 10),
MMA8452_CHANNEL(Z, 2, 10),
IIO_CHAN_SOFT_TIMESTAMP(3),
};
static const struct iio_chan_spec mma8652_channels[] = {
MMA8652_CHANNEL(X, 0, 12),
MMA8652_CHANNEL(Y, 1, 12),
MMA8652_CHANNEL(Z, 2, 12),
IIO_CHAN_SOFT_TIMESTAMP(3),
};
static const struct iio_chan_spec mma8653_channels[] = {
MMA8652_CHANNEL(X, 0, 10),
MMA8652_CHANNEL(Y, 1, 10),
MMA8652_CHANNEL(Z, 2, 10),
IIO_CHAN_SOFT_TIMESTAMP(3),
};
enum {
mma8452,
mma8453,
mma8652,
mma8653,
};
static const struct mma_chip_info mma_chip_info_table[] = {
[mma8452] = {
.chip_id = MMA8452_DEVICE_ID,
.channels = mma8452_channels,
.num_channels = ARRAY_SIZE(mma8452_channels),
/*
* Hardware has fullscale of -2G, -4G, -8G corresponding to
* raw value -2048 for 12 bit or -512 for 10 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
*/
.mma_scales = { {0, 9577}, {0, 19154}, {0, 38307} },
.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,
},
[mma8453] = {
.chip_id = MMA8453_DEVICE_ID,
.channels = mma8453_channels,
.num_channels = ARRAY_SIZE(mma8453_channels),
.mma_scales = { {0, 38307}, {0, 76614}, {0, 153228} },
.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,
},
[mma8652] = {
.chip_id = MMA8652_DEVICE_ID,
.channels = mma8652_channels,
.num_channels = ARRAY_SIZE(mma8652_channels),
.mma_scales = { {0, 9577}, {0, 19154}, {0, 38307} },
.ev_cfg = MMA8452_FF_MT_CFG,
.ev_cfg_ele = MMA8452_FF_MT_CFG_ELE,
.ev_cfg_chan_shift = 3,
.ev_src = MMA8452_FF_MT_SRC,
.ev_src_xe = MMA8452_FF_MT_SRC_XHE,
.ev_src_ye = MMA8452_FF_MT_SRC_YHE,
.ev_src_ze = MMA8452_FF_MT_SRC_ZHE,
.ev_ths = MMA8452_FF_MT_THS,
.ev_ths_mask = MMA8452_FF_MT_THS_MASK,
.ev_count = MMA8452_FF_MT_COUNT,
},
[mma8653] = {
.chip_id = MMA8653_DEVICE_ID,
.channels = mma8653_channels,
.num_channels = ARRAY_SIZE(mma8653_channels),
.mma_scales = { {0, 38307}, {0, 76614}, {0, 153228} },
.ev_cfg = MMA8452_FF_MT_CFG,
.ev_cfg_ele = MMA8452_FF_MT_CFG_ELE,
.ev_cfg_chan_shift = 3,
.ev_src = MMA8452_FF_MT_SRC,
.ev_src_xe = MMA8452_FF_MT_SRC_XHE,
.ev_src_ye = MMA8452_FF_MT_SRC_YHE,
.ev_src_ze = MMA8452_FF_MT_SRC_ZHE,
.ev_ths = MMA8452_FF_MT_THS,
.ev_ths_mask = MMA8452_FF_MT_THS_MASK,
.ev_count = MMA8452_FF_MT_COUNT,
},
};
static struct attribute *mma8452_attributes[] = {
&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
&iio_dev_attr_in_accel_scale_available.dev_attr.attr,
......@@ -841,18 +1041,28 @@ static int mma8452_reset(struct i2c_client *client)
return -ETIMEDOUT;
}
static const struct of_device_id mma8452_dt_ids[] = {
{ .compatible = "fsl,mma8452", .data = &mma_chip_info_table[mma8452] },
{ .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] },
{ }
};
MODULE_DEVICE_TABLE(of, mma8452_dt_ids);
static int mma8452_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct mma8452_data *data;
struct iio_dev *indio_dev;
int ret;
const struct of_device_id *match;
ret = i2c_smbus_read_byte_data(client, MMA8452_WHO_AM_I);
if (ret < 0)
return ret;
if (ret != MMA8452_DEVICE_ID)
match = of_match_device(mma8452_dt_ids, &client->dev);
if (!match) {
dev_err(&client->dev, "unknown device model\n");
return -ENODEV;
}
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (!indio_dev)
......@@ -861,14 +1071,33 @@ static int mma8452_probe(struct i2c_client *client,
data = iio_priv(indio_dev);
data->client = client;
mutex_init(&data->lock);
data->chip_info = match->data;
ret = i2c_smbus_read_byte_data(client, MMA8452_WHO_AM_I);
if (ret < 0)
return ret;
switch (ret) {
case MMA8452_DEVICE_ID:
case MMA8453_DEVICE_ID:
case MMA8652_DEVICE_ID:
case MMA8653_DEVICE_ID:
if (ret == data->chip_info->chip_id)
break;
default:
return -ENODEV;
}
dev_info(&client->dev, "registering %s accelerometer; ID 0x%x\n",
match->compatible, data->chip_info->chip_id);
i2c_set_clientdata(client, indio_dev);
indio_dev->info = &mma8452_info;
indio_dev->name = id->name;
indio_dev->dev.parent = &client->dev;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = mma8452_channels;
indio_dev->num_channels = ARRAY_SIZE(mma8452_channels);
indio_dev->channels = data->chip_info->channels;
indio_dev->num_channels = data->chip_info->num_channels;
indio_dev->available_scan_masks = mma8452_scan_masks;
ret = mma8452_reset(client);
......@@ -892,13 +1121,15 @@ static int mma8452_probe(struct i2c_client *client,
if (client->irq) {
/*
* Although we enable the transient interrupt source once and
* for all here the transient event detection itself is not
* enabled until userspace asks for it by
* mma8452_write_event_config()
* Although we enable the interrupt sources once and for
* all here the event detection itself is not enabled until
* userspace asks for it by mma8452_write_event_config()
*/
int supported_interrupts = MMA8452_INT_DRDY | MMA8452_INT_TRANS;
int enabled_interrupts = MMA8452_INT_TRANS;
int supported_interrupts = MMA8452_INT_DRDY |
MMA8452_INT_TRANS |
MMA8452_INT_FF_MT;
int enabled_interrupts = MMA8452_INT_TRANS |
MMA8452_INT_FF_MT;
/* Assume wired to INT1 pin */
ret = i2c_smbus_write_byte_data(client,
......@@ -987,17 +1218,14 @@ static SIMPLE_DEV_PM_OPS(mma8452_pm_ops, mma8452_suspend, mma8452_resume);
#endif
static const struct i2c_device_id mma8452_id[] = {
{ "mma8452", 0 },
{ "mma8452", mma8452 },
{ "mma8453", mma8453 },
{ "mma8652", mma8652 },
{ "mma8653", mma8653 },
{ }
};
MODULE_DEVICE_TABLE(i2c, mma8452_id);
static const struct of_device_id mma8452_dt_ids[] = {
{ .compatible = "fsl,mma8452" },
{ }
};
MODULE_DEVICE_TABLE(of, mma8452_dt_ids);
static struct i2c_driver mma8452_driver = {
.driver = {
.name = "mma8452",
......
/*
* 3-axis accelerometer driver for MXC4005XC Memsic sensor
*
* Copyright (c) 2014, Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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/module.h>
#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>
#include <linux/iio/buffer.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/iio/trigger_consumer.h>
#define MXC4005_DRV_NAME "mxc4005"
#define MXC4005_IRQ_NAME "mxc4005_event"
#define MXC4005_REGMAP_NAME "mxc4005_regmap"
#define MXC4005_REG_XOUT_UPPER 0x03
#define MXC4005_REG_XOUT_LOWER 0x04
#define MXC4005_REG_YOUT_UPPER 0x05
#define MXC4005_REG_YOUT_LOWER 0x06
#define MXC4005_REG_ZOUT_UPPER 0x07
#define MXC4005_REG_ZOUT_LOWER 0x08
#define MXC4005_REG_INT_MASK1 0x0B
#define MXC4005_REG_INT_MASK1_BIT_DRDYE 0x01
#define MXC4005_REG_INT_CLR1 0x01
#define MXC4005_REG_INT_CLR1_BIT_DRDYC 0x01
#define MXC4005_REG_CONTROL 0x0D
#define MXC4005_REG_CONTROL_MASK_FSR GENMASK(6, 5)
#define MXC4005_CONTROL_FSR_SHIFT 5
#define MXC4005_REG_DEVICE_ID 0x0E
enum mxc4005_axis {
AXIS_X,
AXIS_Y,
AXIS_Z,
};
enum mxc4005_range {
MXC4005_RANGE_2G,
MXC4005_RANGE_4G,
MXC4005_RANGE_8G,
};
struct mxc4005_data {
struct device *dev;
struct mutex mutex;
struct regmap *regmap;
struct iio_trigger *dready_trig;
__be16 buffer[8];
bool trigger_enabled;
};
/*
* MXC4005 can operate in the following ranges:
* +/- 2G, 4G, 8G (the default +/-2G)
*
* (2 + 2) * 9.81 / (2^12 - 1) = 0.009582
* (4 + 4) * 9.81 / (2^12 - 1) = 0.019164
* (8 + 8) * 9.81 / (2^12 - 1) = 0.038329
*/
static const struct {
u8 range;
int scale;
} mxc4005_scale_table[] = {
{MXC4005_RANGE_2G, 9582},
{MXC4005_RANGE_4G, 19164},
{MXC4005_RANGE_8G, 38329},
};
static IIO_CONST_ATTR(in_accel_scale_available, "0.009582 0.019164 0.038329");
static struct attribute *mxc4005_attributes[] = {
&iio_const_attr_in_accel_scale_available.dev_attr.attr,
NULL,
};
static const struct attribute_group mxc4005_attrs_group = {
.attrs = mxc4005_attributes,
};
static bool mxc4005_is_readable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case MXC4005_REG_XOUT_UPPER:
case MXC4005_REG_XOUT_LOWER:
case MXC4005_REG_YOUT_UPPER:
case MXC4005_REG_YOUT_LOWER:
case MXC4005_REG_ZOUT_UPPER:
case MXC4005_REG_ZOUT_LOWER:
case MXC4005_REG_DEVICE_ID:
case MXC4005_REG_CONTROL:
return true;
default:
return false;
}
}
static bool mxc4005_is_writeable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case MXC4005_REG_INT_CLR1:
case MXC4005_REG_INT_MASK1:
case MXC4005_REG_CONTROL:
return true;
default:
return false;
}
}
static const struct regmap_config mxc4005_regmap_config = {
.name = MXC4005_REGMAP_NAME,
.reg_bits = 8,
.val_bits = 8,
.max_register = MXC4005_REG_DEVICE_ID,
.readable_reg = mxc4005_is_readable_reg,
.writeable_reg = mxc4005_is_writeable_reg,
};
static int mxc4005_read_xyz(struct mxc4005_data *data)
{
int ret;
ret = regmap_bulk_read(data->regmap, MXC4005_REG_XOUT_UPPER,
(u8 *) data->buffer, sizeof(data->buffer));
if (ret < 0) {
dev_err(data->dev, "failed to read axes\n");
return ret;
}
return 0;
}
static int mxc4005_read_axis(struct mxc4005_data *data,
unsigned int addr)
{
__be16 reg;
int ret;
ret = regmap_bulk_read(data->regmap, addr, (u8 *) &reg, sizeof(reg));
if (ret < 0) {
dev_err(data->dev, "failed to read reg %02x\n", addr);
return ret;
}
return be16_to_cpu(reg);
}
static int mxc4005_read_scale(struct mxc4005_data *data)
{
unsigned int reg;
int ret;
int i;
ret = regmap_read(data->regmap, MXC4005_REG_CONTROL, &reg);
if (ret < 0) {
dev_err(data->dev, "failed to read reg_control\n");
return ret;
}
i = reg >> MXC4005_CONTROL_FSR_SHIFT;
if (i < 0 || i >= ARRAY_SIZE(mxc4005_scale_table))
return -EINVAL;
return mxc4005_scale_table[i].scale;
}
static int mxc4005_set_scale(struct mxc4005_data *data, int val)
{
unsigned int reg;
int i;
int ret;
for (i = 0; i < ARRAY_SIZE(mxc4005_scale_table); i++) {
if (mxc4005_scale_table[i].scale == val) {
reg = i << MXC4005_CONTROL_FSR_SHIFT;
ret = regmap_update_bits(data->regmap,
MXC4005_REG_CONTROL,
MXC4005_REG_CONTROL_MASK_FSR,
reg);
if (ret < 0)
dev_err(data->dev,
"failed to write reg_control\n");
return ret;
}
}
return -EINVAL;
}
static int mxc4005_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct mxc4005_data *data = iio_priv(indio_dev);
int ret;
switch (mask) {
case IIO_CHAN_INFO_RAW:
switch (chan->type) {
case IIO_ACCEL:
if (iio_buffer_enabled(indio_dev))
return -EBUSY;
ret = mxc4005_read_axis(data, chan->address);
if (ret < 0)
return ret;
*val = sign_extend32(ret >> chan->scan_type.shift,
chan->scan_type.realbits - 1);
return IIO_VAL_INT;
default:
return -EINVAL;
}
case IIO_CHAN_INFO_SCALE:
ret = mxc4005_read_scale(data);
if (ret < 0)
return ret;
*val = 0;
*val2 = ret;
return IIO_VAL_INT_PLUS_MICRO;
default:
return -EINVAL;
}
}
static int mxc4005_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
struct mxc4005_data *data = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_SCALE:
if (val != 0)
return -EINVAL;
return mxc4005_set_scale(data, val2);
default:
return -EINVAL;
}
}
static const struct iio_info mxc4005_info = {
.driver_module = THIS_MODULE,
.read_raw = mxc4005_read_raw,
.write_raw = mxc4005_write_raw,
.attrs = &mxc4005_attrs_group,
};
static const unsigned long mxc4005_scan_masks[] = {
BIT(AXIS_X) | BIT(AXIS_Y) | BIT(AXIS_Z),
0
};
#define MXC4005_CHANNEL(_axis, _addr) { \
.type = IIO_ACCEL, \
.modified = 1, \
.channel2 = IIO_MOD_##_axis, \
.address = _addr, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
.scan_index = AXIS_##_axis, \
.scan_type = { \
.sign = 's', \
.realbits = 12, \
.storagebits = 16, \
.shift = 4, \
.endianness = IIO_BE, \
}, \
}
static const struct iio_chan_spec mxc4005_channels[] = {
MXC4005_CHANNEL(X, MXC4005_REG_XOUT_UPPER),
MXC4005_CHANNEL(Y, MXC4005_REG_YOUT_UPPER),
MXC4005_CHANNEL(Z, MXC4005_REG_ZOUT_UPPER),
IIO_CHAN_SOFT_TIMESTAMP(3),
};
static irqreturn_t mxc4005_trigger_handler(int irq, void *private)
{
struct iio_poll_func *pf = private;
struct iio_dev *indio_dev = pf->indio_dev;
struct mxc4005_data *data = iio_priv(indio_dev);
int ret;
ret = mxc4005_read_xyz(data);
if (ret < 0)
goto err;
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 mxc4005_clr_intr(struct mxc4005_data *data)
{
int ret;
/* clear interrupt */
ret = regmap_write(data->regmap, MXC4005_REG_INT_CLR1,
MXC4005_REG_INT_CLR1_BIT_DRDYC);
if (ret < 0) {
dev_err(data->dev, "failed to write to reg_int_clr1\n");
return ret;
}
return 0;
}
static int mxc4005_set_trigger_state(struct iio_trigger *trig,
bool state)
{
struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
struct mxc4005_data *data = iio_priv(indio_dev);
int ret;
mutex_lock(&data->mutex);
if (state) {
ret = regmap_write(data->regmap, MXC4005_REG_INT_MASK1,
MXC4005_REG_INT_MASK1_BIT_DRDYE);
} else {
ret = regmap_write(data->regmap, MXC4005_REG_INT_MASK1,
~MXC4005_REG_INT_MASK1_BIT_DRDYE);
}
if (ret < 0) {
mutex_unlock(&data->mutex);
dev_err(data->dev, "failed to update reg_int_mask1");
return ret;
}
data->trigger_enabled = state;
mutex_unlock(&data->mutex);
return 0;
}
static int mxc4005_trigger_try_reen(struct iio_trigger *trig)
{
struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
struct mxc4005_data *data = iio_priv(indio_dev);
if (!data->dready_trig)
return 0;
return mxc4005_clr_intr(data);
}
static const struct iio_trigger_ops mxc4005_trigger_ops = {
.set_trigger_state = mxc4005_set_trigger_state,
.try_reenable = mxc4005_trigger_try_reen,
.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;
unsigned int reg;
ret = regmap_read(data->regmap, MXC4005_REG_DEVICE_ID, &reg);
if (ret < 0) {
dev_err(data->dev, "failed to read chip id\n");
return ret;
}
dev_dbg(data->dev, "MXC4005 chip id %02x\n", reg);
return 0;
}
static int mxc4005_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct mxc4005_data *data;
struct iio_dev *indio_dev;
struct regmap *regmap;
int ret;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
regmap = devm_regmap_init_i2c(client, &mxc4005_regmap_config);
if (IS_ERR(regmap)) {
dev_err(&client->dev, "failed to initialize regmap\n");
return PTR_ERR(regmap);
}
data = iio_priv(indio_dev);
i2c_set_clientdata(client, indio_dev);
data->dev = &client->dev;
data->regmap = regmap;
ret = mxc4005_chip_init(data);
if (ret < 0) {
dev_err(&client->dev, "failed to initialize chip\n");
return ret;
}
mutex_init(&data->mutex);
indio_dev->dev.parent = &client->dev;
indio_dev->channels = mxc4005_channels;
indio_dev->num_channels = ARRAY_SIZE(mxc4005_channels);
indio_dev->available_scan_masks = mxc4005_scan_masks;
indio_dev->name = MXC4005_DRV_NAME;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->info = &mxc4005_info;
ret = iio_triggered_buffer_setup(indio_dev,
iio_pollfunc_store_time,
mxc4005_trigger_handler,
NULL);
if (ret < 0) {
dev_err(&client->dev,
"failed to setup iio triggered buffer\n");
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",
indio_dev->name,
indio_dev->id);
if (!data->dready_trig)
return -ENOMEM;
ret = devm_request_threaded_irq(&client->dev, client->irq,
iio_trigger_generic_data_rdy_poll,
NULL,
IRQF_TRIGGER_FALLING |
IRQF_ONESHOT,
MXC4005_IRQ_NAME,
data->dready_trig);
if (ret) {
dev_err(&client->dev,
"failed to init threaded irq\n");
goto err_buffer_cleanup;
}
data->dready_trig->dev.parent = &client->dev;
data->dready_trig->ops = &mxc4005_trigger_ops;
iio_trigger_set_drvdata(data->dready_trig, indio_dev);
indio_dev->trig = data->dready_trig;
iio_trigger_get(indio_dev->trig);
ret = iio_trigger_register(data->dready_trig);
if (ret) {
dev_err(&client->dev,
"failed to register trigger\n");
goto err_trigger_unregister;
}
}
ret = iio_device_register(indio_dev);
if (ret < 0) {
dev_err(&client->dev,
"unable to register iio device %d\n", ret);
goto err_buffer_cleanup;
}
return 0;
err_trigger_unregister:
iio_trigger_unregister(data->dready_trig);
err_buffer_cleanup:
iio_triggered_buffer_cleanup(indio_dev);
return ret;
}
static int mxc4005_remove(struct i2c_client *client)
{
struct iio_dev *indio_dev = i2c_get_clientdata(client);
struct mxc4005_data *data = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
iio_triggered_buffer_cleanup(indio_dev);
if (data->dready_trig)
iio_trigger_unregister(data->dready_trig);
return 0;
}
static const struct acpi_device_id mxc4005_acpi_match[] = {
{"MXC4005", 0},
{ },
};
MODULE_DEVICE_TABLE(acpi, mxc4005_acpi_match);
static const struct i2c_device_id mxc4005_id[] = {
{"mxc4005", 0},
{ },
};
MODULE_DEVICE_TABLE(i2c, mxc4005_id);
static struct i2c_driver mxc4005_driver = {
.driver = {
.name = MXC4005_DRV_NAME,
.acpi_match_table = ACPI_PTR(mxc4005_acpi_match),
},
.probe = mxc4005_probe,
.remove = mxc4005_remove,
.id_table = mxc4005_id,
};
module_i2c_driver(mxc4005_driver);
MODULE_AUTHOR("Teodora Baluta <teodora.baluta@intel.com>");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("MXC4005 3-axis accelerometer driver");
......@@ -618,6 +618,7 @@ static const struct iio_info accel_info = {
.attrs = &st_accel_attribute_group,
.read_raw = &st_accel_read_raw,
.write_raw = &st_accel_write_raw,
.debugfs_reg_access = &st_sensors_debugfs_reg_access,
};
#ifdef CONFIG_IIO_TRIGGER
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment