mirror of https://gitee.com/openkylin/linux.git
First round of IIO new device support, features and cleanups for the 4.8 cycle.
New device support * ads1015 - add ads1115 support * bma220 accelerometer - new driver - triggered buffer support. * bmc150 - add bmm150 support. * bmp280 - bme280 support with addition of humidity channel. * max5487 potentiometer - new driver * MMA7660FC accelerometer. - New driver * st-pressure - support for the lps22hb * loop trigger. - This one is *nasty* but we have real applications (parrot drones) where it is useful. The trigger basically spins as hard as it can firing off a new trigger each time all triggered devices come back to say they are done. It doesn't hang a machine even when doing it on a dummy driver. A lot nicer than having this implemented within lots of device drivers anyway. Core stuff * Add support to create IIO devices via configfs (similar to we did for triggers a while back) + docs. * New channel types - IIO_ELECTRICAL_CONDUCTIVITY * Couple of MAINTAINERS patches to list the device tree bindings. * Make trigger ops structure non optional (comment fix). It hasn't been for an awful long time, but that's not what the description said. New features * ak8975 - support adapters that are limited to byte data only by allowing the emulated block read i2c function that was recently introduced. * atlas-ph - support atlas-ec (electrical conductivity sensor) * bmi160 - add available frequency and scale attributes to make the driver more user friendly (and avoid having to read the datasheet to know what will work). * dummy - move creation to configfs interface. It's not real hardware so we are not that worried about the ABI breakage ;) * mma8452 - oversampling ration support * nau7802 - expose available gains to make life easier for userspace. * st-sensors - allow use of emulation for SMBus block reads as all the st parts support it. * ti-ads1015 - list datasheet names to allow their use by inkernel consumers. * Various module alias additions to help auto probing. Drop one redundant one as well. Cleanups * ad7266, ad7476, ad7887, ad7923, ad799x - use direct mode claim function rather than open coding it during sensor read (prevents switching on buffers mid read). * ad7793, ad7791 - use direct mode claim to prevent frequency changes when buffers running. * afe440x - These are ABI breaking but the driver requires custom userspace code to do anything useful anyway and that is still being written and under control of TI. Ultimately we may have other libraries to do pulse oximetry with these devices but we aren't aware of any yet. - kernel-doc format fixes - drop ifdef fun around of_match_ptr - it's not worth the mess to save a tiny amount of space. - drop some unnecessary register initializations. - drop the weird locked gain modes as they gain us nothing (can just set all gains separately). - remove handling of offset attributes seeing as no channels actually have them (oops) - Drop the LED3 input channel as it's an alias for ALED2. - *big one* remove channel names - an experiment that turned out to not make sense - see patch for details. - use regmap fields to clean up code. - tie the tia gain stages to appropriate channels in the ABI as that is what they really effect. Same with the LED currents. - cleanout some unused defines and fix a missnamed one. * atlas-ph - reorganise to allow support of other similar parts. * bmc150 - document supported chips in kconfig help. * jsa1212 - drop an unneeded i2c functionality check for functionality the driver doesn't use. * mxs-lradc - simply touch screen registration code. - remove the touch screen unregister as all devm based now. - disable only those channels that are masked in hardware stop (others are already dealt with elsewhere) * st-sensors - unexport st_sensors_get_buffer_element as nothing outside the st-sensors core driver uses it. - fix handling of failure to start up regulators. * tpl0102 - drop an i2c functionality test for features that aren't needed. * ti-am335x - use variable name rather than type in sizeof for clarity. - use SIMPLE_DEV_PM_OPS helper macro to tidy up a bit. Tools * Add install / uninstall to makefile. Someone cares, so presumably some people will find it useful! * generic_buffer - rename to iio_generic_buffer to line up with other tools. - handle cleanup when receiving signals - Add a --device-num option and a --trigger-num option rather than relying on naming which doesn't work if you have two of the same part. -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iQIcBAABCAAGBQJXWFXGAAoJEFSFNJnE9BaIQ4kP/jcoVTR8w6EMdcr1BErrvFgm IMbMMK8wJtc1dhKcp77h/DQjCunPAaDMgdtg1uJcreRDl1dY1jiFh/PMLrt/eods tsJZcN1h40aLlPEdMQFl3oiaWjpUm23gdDtfiCHA+sM/FhQ+A+83EdonU7hbAGT2 E2NAb5xkC+74WEUit5qEouz++4JRnsJEpBx3A7mwkiUH9RnFA9OflsTdIFSIEbzb V4yMWeYBAlhwKPy/7z6/hAMK/+Psabw8FppCAw2HcADVpT517rkl4MpvErSZeFGz cr46+ZWhXZ4PaG2jG2xVLpqSbqLSfWp8uqfj0WavW2gFn/1jCB+FST904q7IPVW2 QPxALqxMAMSzOHxTYmDLfJ4IJ5vvLKroom4ixKgbl7jKXF3+PTooR6U4hGQmLK7K VKWjb/kIdHpuRT3/pG2kysMtJkk1av5BBZkl9sWUEFgCd4vYP1eB1Ah3aE/P26OH BCueMDaJqKAzmVa6Dx5MCLlJpxHpQl/OBz5wdXiMRbYeky/QHgzaTmi/HZ0wwXvn zFHAglWzLq7q0HY+q7wwI20R16cRYIve3HOaLFvQdXd1xpBP1BF+aAxT/vSb+otS 1OWCEHAxq4XEM7VcRKv9XL59gXuSB8ZaSl6k82+IjjfgOqLHh8BSygVle7iu14TE glokfXQRC7xhgfC1zQf9 =Hq1a -----END PGP SIGNATURE----- Merge tag 'iio-for-4.8a' of git://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio into staging-next Jonathan writes: First round of IIO new device support, features and cleanups for the 4.8 cycle. New device support * ads1015 - add ads1115 support * bma220 accelerometer - new driver - triggered buffer support. * bmc150 - add bmm150 support. * bmp280 - bme280 support with addition of humidity channel. * max5487 potentiometer - new driver * MMA7660FC accelerometer. - New driver * st-pressure - support for the lps22hb * loop trigger. - This one is *nasty* but we have real applications (parrot drones) where it is useful. The trigger basically spins as hard as it can firing off a new trigger each time all triggered devices come back to say they are done. It doesn't hang a machine even when doing it on a dummy driver. A lot nicer than having this implemented within lots of device drivers anyway. Core stuff * Add support to create IIO devices via configfs (similar to we did for triggers a while back) + docs. * New channel types - IIO_ELECTRICAL_CONDUCTIVITY * Couple of MAINTAINERS patches to list the device tree bindings. * Make trigger ops structure non optional (comment fix). It hasn't been for an awful long time, but that's not what the description said. New features * ak8975 - support adapters that are limited to byte data only by allowing the emulated block read i2c function that was recently introduced. * atlas-ph - support atlas-ec (electrical conductivity sensor) * bmi160 - add available frequency and scale attributes to make the driver more user friendly (and avoid having to read the datasheet to know what will work). * dummy - move creation to configfs interface. It's not real hardware so we are not that worried about the ABI breakage ;) * mma8452 - oversampling ration support * nau7802 - expose available gains to make life easier for userspace. * st-sensors - allow use of emulation for SMBus block reads as all the st parts support it. * ti-ads1015 - list datasheet names to allow their use by inkernel consumers. * Various module alias additions to help auto probing. Drop one redundant one as well. Cleanups * ad7266, ad7476, ad7887, ad7923, ad799x - use direct mode claim function rather than open coding it during sensor read (prevents switching on buffers mid read). * ad7793, ad7791 - use direct mode claim to prevent frequency changes when buffers running. * afe440x - These are ABI breaking but the driver requires custom userspace code to do anything useful anyway and that is still being written and under control of TI. Ultimately we may have other libraries to do pulse oximetry with these devices but we aren't aware of any yet. - kernel-doc format fixes - drop ifdef fun around of_match_ptr - it's not worth the mess to save a tiny amount of space. - drop some unnecessary register initializations. - drop the weird locked gain modes as they gain us nothing (can just set all gains separately). - remove handling of offset attributes seeing as no channels actually have them (oops) - Drop the LED3 input channel as it's an alias for ALED2. - *big one* remove channel names - an experiment that turned out to not make sense - see patch for details. - use regmap fields to clean up code. - tie the tia gain stages to appropriate channels in the ABI as that is what they really effect. Same with the LED currents. - cleanout some unused defines and fix a missnamed one. * atlas-ph - reorganise to allow support of other similar parts. * bmc150 - document supported chips in kconfig help. * jsa1212 - drop an unneeded i2c functionality check for functionality the driver doesn't use. * mxs-lradc - simply touch screen registration code. - remove the touch screen unregister as all devm based now. - disable only those channels that are masked in hardware stop (others are already dealt with elsewhere) * st-sensors - unexport st_sensors_get_buffer_element as nothing outside the st-sensors core driver uses it. - fix handling of failure to start up regulators. * tpl0102 - drop an i2c functionality test for features that aren't needed. * ti-am335x - use variable name rather than type in sizeof for clarity. - use SIMPLE_DEV_PM_OPS helper macro to tidy up a bit. Tools * Add install / uninstall to makefile. Someone cares, so presumably some people will find it useful! * generic_buffer - rename to iio_generic_buffer to line up with other tools. - handle cleanup when receiving signals - Add a --device-num option and a --trigger-num option rather than relying on naming which doesn't work if you have two of the same part.
This commit is contained in:
commit
aabb406008
|
@ -19,3 +19,16 @@ KernelVersion: 4.4
|
|||
Description:
|
||||
High resolution timers directory. Creating a directory here
|
||||
will result in creating a hrtimer trigger in the IIO subsystem.
|
||||
|
||||
What: /config/iio/devices
|
||||
Date: April 2016
|
||||
KernelVersion: 4.7
|
||||
Description:
|
||||
Industrial IO software devices directory.
|
||||
|
||||
What: /config/iio/devices/dummy
|
||||
Date: April 2016
|
||||
KernelVersion: 4.7
|
||||
Description:
|
||||
Dummy IIO devices directory. Creating a directory here will result
|
||||
in creating a dummy IIO device in the IIO subystem.
|
||||
|
|
|
@ -1565,3 +1565,10 @@ Description:
|
|||
* 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.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_electricalconductivity_raw
|
||||
KernelVersion: 4.8
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Raw (unscaled no offset etc.) electric conductivity reading that
|
||||
can be processed to siemens per meter.
|
||||
|
|
|
@ -1,54 +1,41 @@
|
|||
What: /sys/bus/iio/devices/iio:deviceX/tia_resistanceY
|
||||
/sys/bus/iio/devices/iio:deviceX/tia_capacitanceY
|
||||
Date: December 2015
|
||||
KernelVersion:
|
||||
Contact: Andrew F. Davis <afd@ti.com>
|
||||
Description:
|
||||
Get and set the resistance and the capacitance settings for the
|
||||
Transimpedance Amplifier. Y is 1 for Rf1 and Cf1, Y is 2 for
|
||||
Rf2 and Cf2 values.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/tia_separate_en
|
||||
Date: December 2015
|
||||
KernelVersion:
|
||||
Contact: Andrew F. Davis <afd@ti.com>
|
||||
Description:
|
||||
Enable or disable separate settings for the TransImpedance
|
||||
Amplifier above, when disabled both values are set by the
|
||||
first channel.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_intensity_ledY_raw
|
||||
/sys/bus/iio/devices/iio:deviceX/in_intensity_ledY_ambient_raw
|
||||
Date: December 2015
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_intensityY_raw
|
||||
Date: May 2016
|
||||
KernelVersion:
|
||||
Contact: Andrew F. Davis <afd@ti.com>
|
||||
Description:
|
||||
Get measured values from the ADC for these stages. Y is the
|
||||
specific LED number. The values are expressed in 24-bit twos
|
||||
complement.
|
||||
specific stage number corresponding to datasheet stage names
|
||||
as follows:
|
||||
1 -> LED2
|
||||
2 -> ALED2/LED3
|
||||
3 -> LED1
|
||||
4 -> ALED1/LED4
|
||||
Note that channels 5 and 6 represent LED2-ALED2 and LED1-ALED1
|
||||
respectively which simply helper channels containing the
|
||||
calculated difference in the value of stage 1 - 2 and 3 - 4.
|
||||
The values are expressed in 24-bit twos complement.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_intensity_ledY-ledY_ambient_raw
|
||||
Date: December 2015
|
||||
KernelVersion:
|
||||
Contact: Andrew F. Davis <afd@ti.com>
|
||||
Description:
|
||||
Get differential values from the ADC for these stages. Y is the
|
||||
specific LED number. The values are expressed in 24-bit twos
|
||||
complement for the specified LEDs.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/out_current_ledY_offset
|
||||
/sys/bus/iio/devices/iio:deviceX/out_current_ledY_ambient_offset
|
||||
Date: December 2015
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_intensityY_offset
|
||||
Date: May 2016
|
||||
KernelVersion:
|
||||
Contact: Andrew F. Davis <afd@ti.com>
|
||||
Description:
|
||||
Get and set the offset cancellation DAC setting for these
|
||||
stages. The values are expressed in 5-bit sign-magnitude.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/out_current_ledY_raw
|
||||
Date: December 2015
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_intensityY_resistance
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_intensityY_capacitance
|
||||
Date: May 2016
|
||||
KernelVersion:
|
||||
Contact: Andrew F. Davis <afd@ti.com>
|
||||
Description:
|
||||
Get and set the LED current for the specified LED. Y is the
|
||||
specific LED number.
|
||||
Get and set the resistance and the capacitance settings for the
|
||||
Transimpedance Amplifier during the associated stage.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/out_currentY_raw
|
||||
Date: May 2016
|
||||
KernelVersion:
|
||||
Contact: Andrew F. Davis <afd@ti.com>
|
||||
Description:
|
||||
Get and set the LED current for the specified LED active during
|
||||
this stage. Y is the specific stage number.
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
* Atlas Scientific EC-SM OEM sensor
|
||||
|
||||
http://www.atlas-scientific.com/_files/_datasheets/_oem/EC_oem_datasheet.pdf
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: must be "atlas,ec-sm"
|
||||
- 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:
|
||||
|
||||
atlas@64 {
|
||||
compatible = "atlas,ec-sm";
|
||||
reg = <0x64>;
|
||||
interrupt-parent = <&gpio1>;
|
||||
interrupts = <16 2>;
|
||||
};
|
|
@ -64,3 +64,4 @@ Pressure sensors:
|
|||
- st,lps001wp-press
|
||||
- st,lps25h-press
|
||||
- st,lps331ap-press
|
||||
- st,lps22hb-press
|
||||
|
|
|
@ -5773,6 +5773,7 @@ R: Lars-Peter Clausen <lars@metafoo.de>
|
|||
R: Peter Meerwald-Stadler <pmeerw@pmeerw.net>
|
||||
L: linux-iio@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/iio/
|
||||
F: drivers/iio/
|
||||
F: drivers/staging/iio/
|
||||
F: include/linux/iio/
|
||||
|
@ -10841,6 +10842,7 @@ STAGING - INDUSTRIAL IO
|
|||
M: Jonathan Cameron <jic23@kernel.org>
|
||||
L: linux-iio@vger.kernel.org
|
||||
S: Odd Fixes
|
||||
F: Documentation/devicetree/bindings/staging/iio/
|
||||
F: drivers/staging/iio/
|
||||
|
||||
STAGING - LIRC (LINUX INFRARED REMOTE CONTROL) DRIVERS
|
||||
|
|
|
@ -46,6 +46,14 @@ config IIO_CONSUMERS_PER_TRIGGER
|
|||
This value controls the maximum number of consumers that a
|
||||
given trigger may handle. Default is 2.
|
||||
|
||||
config IIO_SW_DEVICE
|
||||
tristate "Enable software IIO device support"
|
||||
select IIO_CONFIGFS
|
||||
help
|
||||
Provides IIO core support for software devices. A software
|
||||
device can be created via configfs or directly by a driver
|
||||
using the API provided.
|
||||
|
||||
config IIO_SW_TRIGGER
|
||||
tristate "Enable software triggers support"
|
||||
select IIO_CONFIGFS
|
||||
|
|
|
@ -8,6 +8,7 @@ industrialio-$(CONFIG_IIO_BUFFER) += industrialio-buffer.o
|
|||
industrialio-$(CONFIG_IIO_TRIGGER) += industrialio-trigger.o
|
||||
|
||||
obj-$(CONFIG_IIO_CONFIGFS) += industrialio-configfs.o
|
||||
obj-$(CONFIG_IIO_SW_DEVICE) += industrialio-sw-device.o
|
||||
obj-$(CONFIG_IIO_SW_TRIGGER) += industrialio-sw-trigger.o
|
||||
obj-$(CONFIG_IIO_TRIGGERED_EVENT) += industrialio-triggered-event.o
|
||||
|
||||
|
|
|
@ -17,6 +17,16 @@ config BMA180
|
|||
To compile this driver as a module, choose M here: the
|
||||
module will be called bma180.
|
||||
|
||||
config BMA220
|
||||
tristate "Bosch BMA220 3-Axis Accelerometer Driver"
|
||||
depends on SPI
|
||||
help
|
||||
Say yes here to add support for the Bosch BMA220 triaxial
|
||||
acceleration sensor.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called bma220_spi.
|
||||
|
||||
config BMC150_ACCEL
|
||||
tristate "Bosch BMC150 Accelerometer Driver"
|
||||
select IIO_BUFFER
|
||||
|
@ -136,6 +146,16 @@ config MMA7455_SPI
|
|||
To compile this driver as a module, choose M here: the module
|
||||
will be called mma7455_spi.
|
||||
|
||||
config MMA7660
|
||||
tristate "Freescale MMA7660FC 3-Axis Accelerometer Driver"
|
||||
depends on I2C
|
||||
help
|
||||
Say yes here to get support for the Freescale MMA7660FC 3-Axis
|
||||
accelerometer.
|
||||
|
||||
Choosing M will build the driver as a module. If so, the module
|
||||
will be called mma7660.
|
||||
|
||||
config MMA8452
|
||||
tristate "Freescale MMA8452Q and similar Accelerometers Driver"
|
||||
depends on I2C
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
# When adding new entries keep the list in alphabetical order
|
||||
obj-$(CONFIG_BMA180) += bma180.o
|
||||
obj-$(CONFIG_BMA220) += bma220_spi.o
|
||||
obj-$(CONFIG_BMC150_ACCEL) += bmc150-accel-core.o
|
||||
obj-$(CONFIG_BMC150_ACCEL_I2C) += bmc150-accel-i2c.o
|
||||
obj-$(CONFIG_BMC150_ACCEL_SPI) += bmc150-accel-spi.o
|
||||
|
@ -15,6 +16,8 @@ obj-$(CONFIG_MMA7455) += mma7455_core.o
|
|||
obj-$(CONFIG_MMA7455_I2C) += mma7455_i2c.o
|
||||
obj-$(CONFIG_MMA7455_SPI) += mma7455_spi.o
|
||||
|
||||
obj-$(CONFIG_MMA7660) += mma7660.o
|
||||
|
||||
obj-$(CONFIG_MMA8452) += mma8452.o
|
||||
|
||||
obj-$(CONFIG_MMA9551_CORE) += mma9551_core.o
|
||||
|
|
|
@ -0,0 +1,338 @@
|
|||
/**
|
||||
* BMA220 Digital triaxial acceleration sensor driver
|
||||
*
|
||||
* 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/acpi.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/spi/spi.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
|
||||
#define BMA220_REG_ID 0x00
|
||||
#define BMA220_REG_ACCEL_X 0x02
|
||||
#define BMA220_REG_ACCEL_Y 0x03
|
||||
#define BMA220_REG_ACCEL_Z 0x04
|
||||
#define BMA220_REG_RANGE 0x11
|
||||
#define BMA220_REG_SUSPEND 0x18
|
||||
|
||||
#define BMA220_CHIP_ID 0xDD
|
||||
#define BMA220_READ_MASK 0x80
|
||||
#define BMA220_RANGE_MASK 0x03
|
||||
#define BMA220_DATA_SHIFT 2
|
||||
#define BMA220_SUSPEND_SLEEP 0xFF
|
||||
#define BMA220_SUSPEND_WAKE 0x00
|
||||
|
||||
#define BMA220_DEVICE_NAME "bma220"
|
||||
#define BMA220_SCALE_AVAILABLE "0.623 1.248 2.491 4.983"
|
||||
|
||||
#define BMA220_ACCEL_CHANNEL(index, reg, axis) { \
|
||||
.type = IIO_ACCEL, \
|
||||
.address = reg, \
|
||||
.modified = 1, \
|
||||
.channel2 = IIO_MOD_##axis, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.scan_index = index, \
|
||||
.scan_type = { \
|
||||
.sign = 's', \
|
||||
.realbits = 6, \
|
||||
.storagebits = 8, \
|
||||
.shift = BMA220_DATA_SHIFT, \
|
||||
.endianness = IIO_CPU, \
|
||||
}, \
|
||||
}
|
||||
|
||||
enum bma220_axis {
|
||||
AXIS_X,
|
||||
AXIS_Y,
|
||||
AXIS_Z,
|
||||
};
|
||||
|
||||
static IIO_CONST_ATTR(in_accel_scale_available, BMA220_SCALE_AVAILABLE);
|
||||
|
||||
static struct attribute *bma220_attributes[] = {
|
||||
&iio_const_attr_in_accel_scale_available.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group bma220_attribute_group = {
|
||||
.attrs = bma220_attributes,
|
||||
};
|
||||
|
||||
static const int bma220_scale_table[][4] = {
|
||||
{0, 623000}, {1, 248000}, {2, 491000}, {4, 983000}
|
||||
};
|
||||
|
||||
struct bma220_data {
|
||||
struct spi_device *spi_device;
|
||||
struct mutex lock;
|
||||
s8 buffer[16]; /* 3x8-bit channels + 5x8 padding + 8x8 timestamp */
|
||||
u8 tx_buf[2] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec bma220_channels[] = {
|
||||
BMA220_ACCEL_CHANNEL(0, BMA220_REG_ACCEL_X, X),
|
||||
BMA220_ACCEL_CHANNEL(1, BMA220_REG_ACCEL_Y, Y),
|
||||
BMA220_ACCEL_CHANNEL(2, BMA220_REG_ACCEL_Z, Z),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(3),
|
||||
};
|
||||
|
||||
static inline int bma220_read_reg(struct spi_device *spi, u8 reg)
|
||||
{
|
||||
return spi_w8r8(spi, reg | BMA220_READ_MASK);
|
||||
}
|
||||
|
||||
static const unsigned long bma220_accel_scan_masks[] = {
|
||||
BIT(AXIS_X) | BIT(AXIS_Y) | BIT(AXIS_Z),
|
||||
0
|
||||
};
|
||||
|
||||
static irqreturn_t bma220_trigger_handler(int irq, void *p)
|
||||
{
|
||||
int ret;
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct bma220_data *data = iio_priv(indio_dev);
|
||||
struct spi_device *spi = data->spi_device;
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
data->tx_buf[0] = BMA220_REG_ACCEL_X | BMA220_READ_MASK;
|
||||
ret = spi_write_then_read(spi, data->tx_buf, 1, data->buffer,
|
||||
ARRAY_SIZE(bma220_channels) - 1);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
|
||||
pf->timestamp);
|
||||
err:
|
||||
mutex_unlock(&data->lock);
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int bma220_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
int ret;
|
||||
u8 range_idx;
|
||||
struct bma220_data *data = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = bma220_read_reg(data->spi_device, chan->address);
|
||||
if (ret < 0)
|
||||
return -EINVAL;
|
||||
*val = sign_extend32(ret >> BMA220_DATA_SHIFT, 5);
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
ret = bma220_read_reg(data->spi_device, BMA220_REG_RANGE);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
range_idx = ret & BMA220_RANGE_MASK;
|
||||
*val = bma220_scale_table[range_idx][0];
|
||||
*val2 = bma220_scale_table[range_idx][1];
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int bma220_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
int i;
|
||||
int ret;
|
||||
int index = -1;
|
||||
struct bma220_data *data = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
for (i = 0; i < ARRAY_SIZE(bma220_scale_table); i++)
|
||||
if (val == bma220_scale_table[i][0] &&
|
||||
val2 == bma220_scale_table[i][1]) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
if (index < 0)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
data->tx_buf[0] = BMA220_REG_RANGE;
|
||||
data->tx_buf[1] = index;
|
||||
ret = spi_write(data->spi_device, data->tx_buf,
|
||||
sizeof(data->tx_buf));
|
||||
if (ret < 0)
|
||||
dev_err(&data->spi_device->dev,
|
||||
"failed to set measurement range\n");
|
||||
mutex_unlock(&data->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct iio_info bma220_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = bma220_read_raw,
|
||||
.write_raw = bma220_write_raw,
|
||||
.attrs = &bma220_attribute_group,
|
||||
};
|
||||
|
||||
static int bma220_init(struct spi_device *spi)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = bma220_read_reg(spi, BMA220_REG_ID);
|
||||
if (ret != BMA220_CHIP_ID)
|
||||
return -ENODEV;
|
||||
|
||||
/* Make sure the chip is powered on */
|
||||
ret = bma220_read_reg(spi, BMA220_REG_SUSPEND);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
else if (ret == BMA220_SUSPEND_WAKE)
|
||||
return bma220_read_reg(spi, BMA220_REG_SUSPEND);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bma220_deinit(struct spi_device *spi)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Make sure the chip is powered off */
|
||||
ret = bma220_read_reg(spi, BMA220_REG_SUSPEND);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
else if (ret == BMA220_SUSPEND_SLEEP)
|
||||
return bma220_read_reg(spi, BMA220_REG_SUSPEND);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bma220_probe(struct spi_device *spi)
|
||||
{
|
||||
int ret;
|
||||
struct iio_dev *indio_dev;
|
||||
struct bma220_data *data;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*data));
|
||||
if (!indio_dev) {
|
||||
dev_err(&spi->dev, "iio allocation failed!\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
data = iio_priv(indio_dev);
|
||||
data->spi_device = spi;
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
mutex_init(&data->lock);
|
||||
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->info = &bma220_info;
|
||||
indio_dev->name = BMA220_DEVICE_NAME;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = bma220_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(bma220_channels);
|
||||
indio_dev->available_scan_masks = bma220_accel_scan_masks;
|
||||
|
||||
ret = bma220_init(data->spi_device);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, NULL,
|
||||
bma220_trigger_handler, NULL);
|
||||
if (ret < 0) {
|
||||
dev_err(&spi->dev, "iio triggered buffer setup failed\n");
|
||||
goto err_suspend;
|
||||
}
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret < 0) {
|
||||
dev_err(&spi->dev, "iio_device_register failed\n");
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
goto err_suspend;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_suspend:
|
||||
return bma220_deinit(spi);
|
||||
}
|
||||
|
||||
static int bma220_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
|
||||
return bma220_deinit(spi);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int bma220_suspend(struct device *dev)
|
||||
{
|
||||
struct bma220_data *data =
|
||||
iio_priv(spi_get_drvdata(to_spi_device(dev)));
|
||||
|
||||
/* The chip can be suspended/woken up by a simple register read. */
|
||||
return bma220_read_reg(data->spi_device, BMA220_REG_SUSPEND);
|
||||
}
|
||||
|
||||
static int bma220_resume(struct device *dev)
|
||||
{
|
||||
struct bma220_data *data =
|
||||
iio_priv(spi_get_drvdata(to_spi_device(dev)));
|
||||
|
||||
return bma220_read_reg(data->spi_device, BMA220_REG_SUSPEND);
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(bma220_pm_ops, bma220_suspend, bma220_resume);
|
||||
|
||||
#define BMA220_PM_OPS (&bma220_pm_ops)
|
||||
#else
|
||||
#define BMA220_PM_OPS NULL
|
||||
#endif
|
||||
|
||||
static const struct spi_device_id bma220_spi_id[] = {
|
||||
{"bma220", 0},
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct acpi_device_id bma220_acpi_id[] = {
|
||||
{"BMA0220", 0},
|
||||
{}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(spi, bma220_spi_id);
|
||||
|
||||
static struct spi_driver bma220_driver = {
|
||||
.driver = {
|
||||
.name = "bma220_spi",
|
||||
.pm = BMA220_PM_OPS,
|
||||
.acpi_match_table = ACPI_PTR(bma220_acpi_id),
|
||||
},
|
||||
.probe = bma220_probe,
|
||||
.remove = bma220_remove,
|
||||
.id_table = bma220_spi_id,
|
||||
};
|
||||
|
||||
module_spi_driver(bma220_driver);
|
||||
|
||||
MODULE_AUTHOR("Tiberiu Breana <tiberiu.a.breana@intel.com>");
|
||||
MODULE_DESCRIPTION("BMA220 acceleration sensor driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -0,0 +1,277 @@
|
|||
/**
|
||||
* Freescale MMA7660FC 3-Axis Accelerometer
|
||||
*
|
||||
* 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 driver for Freescale MMA7660FC; 7-bit I2C address: 0x4c.
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
#define MMA7660_DRIVER_NAME "mma7660"
|
||||
|
||||
#define MMA7660_REG_XOUT 0x00
|
||||
#define MMA7660_REG_YOUT 0x01
|
||||
#define MMA7660_REG_ZOUT 0x02
|
||||
#define MMA7660_REG_OUT_BIT_ALERT BIT(6)
|
||||
|
||||
#define MMA7660_REG_MODE 0x07
|
||||
#define MMA7660_REG_MODE_BIT_MODE BIT(0)
|
||||
#define MMA7660_REG_MODE_BIT_TON BIT(2)
|
||||
|
||||
#define MMA7660_I2C_READ_RETRIES 5
|
||||
|
||||
/*
|
||||
* The accelerometer has one measurement range:
|
||||
*
|
||||
* -1.5g - +1.5g (6-bit, signed)
|
||||
*
|
||||
* scale = (1.5 + 1.5) * 9.81 / (2^6 - 1) = 0.467142857
|
||||
*/
|
||||
|
||||
#define MMA7660_SCALE_AVAIL "0.467142857"
|
||||
|
||||
const int mma7660_nscale = 467142857;
|
||||
|
||||
#define MMA7660_CHANNEL(reg, axis) { \
|
||||
.type = IIO_ACCEL, \
|
||||
.address = reg, \
|
||||
.modified = 1, \
|
||||
.channel2 = IIO_MOD_##axis, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec mma7660_channels[] = {
|
||||
MMA7660_CHANNEL(MMA7660_REG_XOUT, X),
|
||||
MMA7660_CHANNEL(MMA7660_REG_YOUT, Y),
|
||||
MMA7660_CHANNEL(MMA7660_REG_ZOUT, Z),
|
||||
};
|
||||
|
||||
enum mma7660_mode {
|
||||
MMA7660_MODE_STANDBY,
|
||||
MMA7660_MODE_ACTIVE
|
||||
};
|
||||
|
||||
struct mma7660_data {
|
||||
struct i2c_client *client;
|
||||
struct mutex lock;
|
||||
enum mma7660_mode mode;
|
||||
};
|
||||
|
||||
static IIO_CONST_ATTR(in_accel_scale_available, MMA7660_SCALE_AVAIL);
|
||||
|
||||
static struct attribute *mma7660_attributes[] = {
|
||||
&iio_const_attr_in_accel_scale_available.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group mma7660_attribute_group = {
|
||||
.attrs = mma7660_attributes
|
||||
};
|
||||
|
||||
static int mma7660_set_mode(struct mma7660_data *data,
|
||||
enum mma7660_mode mode)
|
||||
{
|
||||
int ret;
|
||||
struct i2c_client *client = data->client;
|
||||
|
||||
if (mode == data->mode)
|
||||
return 0;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(client, MMA7660_REG_MODE);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "failed to read sensor mode\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (mode == MMA7660_MODE_ACTIVE) {
|
||||
ret &= ~MMA7660_REG_MODE_BIT_TON;
|
||||
ret |= MMA7660_REG_MODE_BIT_MODE;
|
||||
} else {
|
||||
ret &= ~MMA7660_REG_MODE_BIT_TON;
|
||||
ret &= ~MMA7660_REG_MODE_BIT_MODE;
|
||||
}
|
||||
|
||||
ret = i2c_smbus_write_byte_data(client, MMA7660_REG_MODE, ret);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "failed to change sensor mode\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
data->mode = mode;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mma7660_read_accel(struct mma7660_data *data, u8 address)
|
||||
{
|
||||
int ret, retries = MMA7660_I2C_READ_RETRIES;
|
||||
struct i2c_client *client = data->client;
|
||||
|
||||
/*
|
||||
* Read data. If the Alert bit is set, the register was read at
|
||||
* the same time as the device was attempting to update the content.
|
||||
* The solution is to read the register again. Do this only
|
||||
* MMA7660_I2C_READ_RETRIES times to avoid spending too much time
|
||||
* in the kernel.
|
||||
*/
|
||||
do {
|
||||
ret = i2c_smbus_read_byte_data(client, address);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "register read failed\n");
|
||||
return ret;
|
||||
}
|
||||
} while (retries-- > 0 && ret & MMA7660_REG_OUT_BIT_ALERT);
|
||||
|
||||
if (ret & MMA7660_REG_OUT_BIT_ALERT) {
|
||||
dev_err(&client->dev, "all register read retries failed\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mma7660_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct mma7660_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
mutex_lock(&data->lock);
|
||||
ret = mma7660_read_accel(data, chan->address);
|
||||
mutex_unlock(&data->lock);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = sign_extend32(ret, 5);
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = 0;
|
||||
*val2 = mma7660_nscale;
|
||||
return IIO_VAL_INT_PLUS_NANO;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct iio_info mma7660_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = mma7660_read_raw,
|
||||
.attrs = &mma7660_attribute_group,
|
||||
};
|
||||
|
||||
static int mma7660_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
int ret;
|
||||
struct iio_dev *indio_dev;
|
||||
struct mma7660_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);
|
||||
data->mode = MMA7660_MODE_STANDBY;
|
||||
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->info = &mma7660_info;
|
||||
indio_dev->name = MMA7660_DRIVER_NAME;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = mma7660_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(mma7660_channels);
|
||||
|
||||
ret = mma7660_set_mode(data, MMA7660_MODE_ACTIVE);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "device_register failed\n");
|
||||
mma7660_set_mode(data, MMA7660_MODE_STANDBY);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mma7660_remove(struct i2c_client *client)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
return mma7660_set_mode(iio_priv(indio_dev), MMA7660_MODE_STANDBY);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int mma7660_suspend(struct device *dev)
|
||||
{
|
||||
struct mma7660_data *data;
|
||||
|
||||
data = iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
|
||||
|
||||
return mma7660_set_mode(data, MMA7660_MODE_STANDBY);
|
||||
}
|
||||
|
||||
static int mma7660_resume(struct device *dev)
|
||||
{
|
||||
struct mma7660_data *data;
|
||||
|
||||
data = iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
|
||||
|
||||
return mma7660_set_mode(data, MMA7660_MODE_ACTIVE);
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(mma7660_pm_ops, mma7660_suspend, mma7660_resume);
|
||||
|
||||
#define MMA7660_PM_OPS (&mma7660_pm_ops)
|
||||
#else
|
||||
#define MMA7660_PM_OPS NULL
|
||||
#endif
|
||||
|
||||
static const struct i2c_device_id mma7660_i2c_id[] = {
|
||||
{"mma7660", 0},
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct acpi_device_id mma7660_acpi_id[] = {
|
||||
{"MMA7660", 0},
|
||||
{}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(acpi, mma7660_acpi_id);
|
||||
|
||||
static struct i2c_driver mma7660_driver = {
|
||||
.driver = {
|
||||
.name = "mma7660",
|
||||
.pm = MMA7660_PM_OPS,
|
||||
.acpi_match_table = ACPI_PTR(mma7660_acpi_id),
|
||||
},
|
||||
.probe = mma7660_probe,
|
||||
.remove = mma7660_remove,
|
||||
.id_table = mma7660_i2c_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(mma7660_driver);
|
||||
|
||||
MODULE_AUTHOR("Constantin Musca <constantin.musca@intel.com>");
|
||||
MODULE_DESCRIPTION("Freescale MMA7660FC 3-Axis Accelerometer driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -76,6 +76,8 @@
|
|||
#define MMA8452_CTRL_DR_DEFAULT 0x4 /* 50 Hz sample frequency */
|
||||
#define MMA8452_CTRL_REG2 0x2b
|
||||
#define MMA8452_CTRL_REG2_RST BIT(6)
|
||||
#define MMA8452_CTRL_REG2_MODS_SHIFT 3
|
||||
#define MMA8452_CTRL_REG2_MODS_MASK 0x1b
|
||||
#define MMA8452_CTRL_REG4 0x2d
|
||||
#define MMA8452_CTRL_REG5 0x2e
|
||||
#define MMA8452_OFF_X 0x2f
|
||||
|
@ -257,20 +259,17 @@ static const int mma8452_samp_freq[8][2] = {
|
|||
{6, 250000}, {1, 560000}
|
||||
};
|
||||
|
||||
/* Datasheet table 35 (step time vs sample frequency) */
|
||||
static const int mma8452_transient_time_step_us[8] = {
|
||||
1250,
|
||||
2500,
|
||||
5000,
|
||||
10000,
|
||||
20000,
|
||||
20000,
|
||||
20000,
|
||||
20000
|
||||
/* Datasheet table: step time "Relationship with the ODR" (sample frequency) */
|
||||
static const int mma8452_transient_time_step_us[4][8] = {
|
||||
{ 1250, 2500, 5000, 10000, 20000, 20000, 20000, 20000 }, /* normal */
|
||||
{ 1250, 2500, 5000, 10000, 20000, 80000, 80000, 80000 }, /* l p l n */
|
||||
{ 1250, 2500, 2500, 2500, 2500, 2500, 2500, 2500 }, /* high res*/
|
||||
{ 1250, 2500, 5000, 10000, 20000, 80000, 160000, 160000 } /* l p */
|
||||
};
|
||||
|
||||
/* Datasheet table 18 (normal mode) */
|
||||
static const int mma8452_hp_filter_cutoff[8][4][2] = {
|
||||
/* Datasheet table "High-Pass Filter Cutoff Options" */
|
||||
static const int mma8452_hp_filter_cutoff[4][8][4][2] = {
|
||||
{ /* normal */
|
||||
{ {16, 0}, {8, 0}, {4, 0}, {2, 0} }, /* 800 Hz sample */
|
||||
{ {16, 0}, {8, 0}, {4, 0}, {2, 0} }, /* 400 Hz sample */
|
||||
{ {8, 0}, {4, 0}, {2, 0}, {1, 0} }, /* 200 Hz sample */
|
||||
|
@ -279,8 +278,61 @@ static const int mma8452_hp_filter_cutoff[8][4][2] = {
|
|||
{ {2, 0}, {1, 0}, {0, 500000}, {0, 250000} }, /* 12.5 Hz sample */
|
||||
{ {2, 0}, {1, 0}, {0, 500000}, {0, 250000} }, /* 6.25 Hz sample */
|
||||
{ {2, 0}, {1, 0}, {0, 500000}, {0, 250000} } /* 1.56 Hz sample */
|
||||
},
|
||||
{ /* low noise low power */
|
||||
{ {16, 0}, {8, 0}, {4, 0}, {2, 0} },
|
||||
{ {16, 0}, {8, 0}, {4, 0}, {2, 0} },
|
||||
{ {8, 0}, {4, 0}, {2, 0}, {1, 0} },
|
||||
{ {4, 0}, {2, 0}, {1, 0}, {0, 500000} },
|
||||
{ {2, 0}, {1, 0}, {0, 500000}, {0, 250000} },
|
||||
{ {0, 500000}, {0, 250000}, {0, 125000}, {0, 063000} },
|
||||
{ {0, 500000}, {0, 250000}, {0, 125000}, {0, 063000} },
|
||||
{ {0, 500000}, {0, 250000}, {0, 125000}, {0, 063000} }
|
||||
},
|
||||
{ /* high resolution */
|
||||
{ {16, 0}, {8, 0}, {4, 0}, {2, 0} },
|
||||
{ {16, 0}, {8, 0}, {4, 0}, {2, 0} },
|
||||
{ {16, 0}, {8, 0}, {4, 0}, {2, 0} },
|
||||
{ {16, 0}, {8, 0}, {4, 0}, {2, 0} },
|
||||
{ {16, 0}, {8, 0}, {4, 0}, {2, 0} },
|
||||
{ {16, 0}, {8, 0}, {4, 0}, {2, 0} },
|
||||
{ {16, 0}, {8, 0}, {4, 0}, {2, 0} },
|
||||
{ {16, 0}, {8, 0}, {4, 0}, {2, 0} }
|
||||
},
|
||||
{ /* low power */
|
||||
{ {16, 0}, {8, 0}, {4, 0}, {2, 0} },
|
||||
{ {8, 0}, {4, 0}, {2, 0}, {1, 0} },
|
||||
{ {4, 0}, {2, 0}, {1, 0}, {0, 500000} },
|
||||
{ {2, 0}, {1, 0}, {0, 500000}, {0, 250000} },
|
||||
{ {1, 0}, {0, 500000}, {0, 250000}, {0, 125000} },
|
||||
{ {0, 250000}, {0, 125000}, {0, 063000}, {0, 031000} },
|
||||
{ {0, 250000}, {0, 125000}, {0, 063000}, {0, 031000} },
|
||||
{ {0, 250000}, {0, 125000}, {0, 063000}, {0, 031000} }
|
||||
}
|
||||
};
|
||||
|
||||
/* Datasheet table "MODS Oversampling modes averaging values at each ODR" */
|
||||
static const u16 mma8452_os_ratio[4][8] = {
|
||||
/* 800 Hz, 400 Hz, ... , 1.56 Hz */
|
||||
{ 2, 4, 4, 4, 4, 16, 32, 128 }, /* normal */
|
||||
{ 2, 4, 4, 4, 4, 4, 8, 32 }, /* low power low noise */
|
||||
{ 2, 4, 8, 16, 32, 128, 256, 1024 }, /* high resolution */
|
||||
{ 2, 2, 2, 2, 2, 2, 4, 16 } /* low power */
|
||||
};
|
||||
|
||||
static int mma8452_get_power_mode(struct mma8452_data *data)
|
||||
{
|
||||
int reg;
|
||||
|
||||
reg = i2c_smbus_read_byte_data(data->client,
|
||||
MMA8452_CTRL_REG2);
|
||||
if (reg < 0)
|
||||
return reg;
|
||||
|
||||
return ((reg & MMA8452_CTRL_REG2_MODS_MASK) >>
|
||||
MMA8452_CTRL_REG2_MODS_SHIFT);
|
||||
}
|
||||
|
||||
static ssize_t mma8452_show_samp_freq_avail(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
|
@ -306,10 +358,39 @@ static ssize_t mma8452_show_hp_cutoff_avail(struct device *dev,
|
|||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct mma8452_data *data = iio_priv(indio_dev);
|
||||
int i = mma8452_get_odr_index(data);
|
||||
int i, j;
|
||||
|
||||
return mma8452_show_int_plus_micros(buf, mma8452_hp_filter_cutoff[i],
|
||||
ARRAY_SIZE(mma8452_hp_filter_cutoff[0]));
|
||||
i = mma8452_get_odr_index(data);
|
||||
j = mma8452_get_power_mode(data);
|
||||
if (j < 0)
|
||||
return j;
|
||||
|
||||
return mma8452_show_int_plus_micros(buf, mma8452_hp_filter_cutoff[j][i],
|
||||
ARRAY_SIZE(mma8452_hp_filter_cutoff[0][0]));
|
||||
}
|
||||
|
||||
static ssize_t mma8452_show_os_ratio_avail(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct mma8452_data *data = iio_priv(indio_dev);
|
||||
int i = mma8452_get_odr_index(data);
|
||||
int j;
|
||||
u16 val = 0;
|
||||
size_t len = 0;
|
||||
|
||||
for (j = 0; j < ARRAY_SIZE(mma8452_os_ratio); j++) {
|
||||
if (val == mma8452_os_ratio[j][i])
|
||||
continue;
|
||||
|
||||
val = mma8452_os_ratio[j][i];
|
||||
|
||||
len += scnprintf(buf + len, PAGE_SIZE - len, "%d ", val);
|
||||
}
|
||||
buf[len - 1] = '\n';
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(mma8452_show_samp_freq_avail);
|
||||
|
@ -317,6 +398,8 @@ static IIO_DEVICE_ATTR(in_accel_scale_available, S_IRUGO,
|
|||
mma8452_show_scale_avail, NULL, 0);
|
||||
static IIO_DEVICE_ATTR(in_accel_filter_high_pass_3db_frequency_available,
|
||||
S_IRUGO, mma8452_show_hp_cutoff_avail, NULL, 0);
|
||||
static IIO_DEVICE_ATTR(in_accel_oversampling_ratio_available, S_IRUGO,
|
||||
mma8452_show_os_ratio_avail, NULL, 0);
|
||||
|
||||
static int mma8452_get_samp_freq_index(struct mma8452_data *data,
|
||||
int val, int val2)
|
||||
|
@ -335,24 +418,33 @@ static int mma8452_get_scale_index(struct mma8452_data *data, int val, int val2)
|
|||
static int mma8452_get_hp_filter_index(struct mma8452_data *data,
|
||||
int val, int val2)
|
||||
{
|
||||
int i = mma8452_get_odr_index(data);
|
||||
int i, j;
|
||||
|
||||
return mma8452_get_int_plus_micros_index(mma8452_hp_filter_cutoff[i],
|
||||
ARRAY_SIZE(mma8452_hp_filter_cutoff[0]), val, val2);
|
||||
i = mma8452_get_odr_index(data);
|
||||
j = mma8452_get_power_mode(data);
|
||||
if (j < 0)
|
||||
return j;
|
||||
|
||||
return mma8452_get_int_plus_micros_index(mma8452_hp_filter_cutoff[j][i],
|
||||
ARRAY_SIZE(mma8452_hp_filter_cutoff[0][0]), val, val2);
|
||||
}
|
||||
|
||||
static int mma8452_read_hp_filter(struct mma8452_data *data, int *hz, int *uHz)
|
||||
{
|
||||
int i, ret;
|
||||
int j, i, ret;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(data->client, MMA8452_HP_FILTER_CUTOFF);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
i = mma8452_get_odr_index(data);
|
||||
j = mma8452_get_power_mode(data);
|
||||
if (j < 0)
|
||||
return j;
|
||||
|
||||
ret &= MMA8452_HP_FILTER_CUTOFF_SEL_MASK;
|
||||
*hz = mma8452_hp_filter_cutoff[i][ret][0];
|
||||
*uHz = mma8452_hp_filter_cutoff[i][ret][1];
|
||||
*hz = mma8452_hp_filter_cutoff[j][i][ret][0];
|
||||
*uHz = mma8452_hp_filter_cutoff[j][i][ret][1];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -414,6 +506,15 @@ static int mma8452_read_raw(struct iio_dev *indio_dev,
|
|||
}
|
||||
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
|
||||
ret = mma8452_get_power_mode(data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
i = mma8452_get_odr_index(data);
|
||||
|
||||
*val = mma8452_os_ratio[ret][i];
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
|
@ -480,6 +581,21 @@ static int mma8452_change_config(struct mma8452_data *data, u8 reg, u8 val)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int mma8452_set_power_mode(struct mma8452_data *data, u8 mode)
|
||||
{
|
||||
int reg;
|
||||
|
||||
reg = i2c_smbus_read_byte_data(data->client,
|
||||
MMA8452_CTRL_REG2);
|
||||
if (reg < 0)
|
||||
return reg;
|
||||
|
||||
reg &= ~MMA8452_CTRL_REG2_MODS_MASK;
|
||||
reg |= mode << MMA8452_CTRL_REG2_MODS_SHIFT;
|
||||
|
||||
return mma8452_change_config(data, MMA8452_CTRL_REG2, reg);
|
||||
}
|
||||
|
||||
/* 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)
|
||||
{
|
||||
|
@ -597,6 +713,14 @@ static int mma8452_write_raw(struct iio_dev *indio_dev,
|
|||
return mma8452_change_config(data, MMA8452_DATA_CFG,
|
||||
data->data_cfg);
|
||||
|
||||
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
|
||||
ret = mma8452_get_odr_index(data);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(mma8452_os_ratio); i++) {
|
||||
if (mma8452_os_ratio[i][ret] == val)
|
||||
return mma8452_set_power_mode(data, i);
|
||||
}
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -610,7 +734,7 @@ static int mma8452_read_thresh(struct iio_dev *indio_dev,
|
|||
int *val, int *val2)
|
||||
{
|
||||
struct mma8452_data *data = iio_priv(indio_dev);
|
||||
int ret, us;
|
||||
int ret, us, power_mode;
|
||||
|
||||
switch (info) {
|
||||
case IIO_EV_INFO_VALUE:
|
||||
|
@ -629,7 +753,11 @@ static int mma8452_read_thresh(struct iio_dev *indio_dev,
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
us = ret * mma8452_transient_time_step_us[
|
||||
power_mode = mma8452_get_power_mode(data);
|
||||
if (power_mode < 0)
|
||||
return power_mode;
|
||||
|
||||
us = ret * mma8452_transient_time_step_us[power_mode][
|
||||
mma8452_get_odr_index(data)];
|
||||
*val = us / USEC_PER_SEC;
|
||||
*val2 = us % USEC_PER_SEC;
|
||||
|
@ -677,8 +805,12 @@ static int mma8452_write_thresh(struct iio_dev *indio_dev,
|
|||
val);
|
||||
|
||||
case IIO_EV_INFO_PERIOD:
|
||||
ret = mma8452_get_power_mode(data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
steps = (val * USEC_PER_SEC + val2) /
|
||||
mma8452_transient_time_step_us[
|
||||
mma8452_transient_time_step_us[ret][
|
||||
mma8452_get_odr_index(data)];
|
||||
|
||||
if (steps < 0 || steps > 0xff)
|
||||
|
@ -978,7 +1110,8 @@ static struct attribute_group mma8452_event_attribute_group = {
|
|||
BIT(IIO_CHAN_INFO_CALIBBIAS), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
|
||||
BIT(IIO_CHAN_INFO_SCALE) | \
|
||||
BIT(IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY), \
|
||||
BIT(IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY) | \
|
||||
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
|
||||
.scan_index = idx, \
|
||||
.scan_type = { \
|
||||
.sign = 's', \
|
||||
|
@ -998,7 +1131,8 @@ static struct attribute_group mma8452_event_attribute_group = {
|
|||
.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), \
|
||||
BIT(IIO_CHAN_INFO_SCALE) | \
|
||||
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
|
||||
.scan_index = idx, \
|
||||
.scan_type = { \
|
||||
.sign = 's', \
|
||||
|
@ -1171,6 +1305,7 @@ static struct attribute *mma8452_attributes[] = {
|
|||
&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
|
||||
&iio_dev_attr_in_accel_scale_available.dev_attr.attr,
|
||||
&iio_dev_attr_in_accel_filter_high_pass_3db_frequency_available.dev_attr.attr,
|
||||
&iio_dev_attr_in_accel_oversampling_ratio_available.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
|
|
|
@ -757,13 +757,15 @@ int st_accel_common_probe(struct iio_dev *indio_dev)
|
|||
indio_dev->info = &accel_info;
|
||||
mutex_init(&adata->tb.buf_lock);
|
||||
|
||||
st_sensors_power_enable(indio_dev);
|
||||
err = st_sensors_power_enable(indio_dev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = st_sensors_check_device_support(indio_dev,
|
||||
ARRAY_SIZE(st_accel_sensors_settings),
|
||||
st_accel_sensors_settings);
|
||||
if (err < 0)
|
||||
return err;
|
||||
goto st_accel_power_off;
|
||||
|
||||
adata->num_data_channels = ST_ACCEL_NUMBER_DATA_CHANNELS;
|
||||
adata->multiread_bit = adata->sensor_settings->multi_read_bit;
|
||||
|
@ -780,11 +782,11 @@ int st_accel_common_probe(struct iio_dev *indio_dev)
|
|||
|
||||
err = st_sensors_init_sensor(indio_dev, adata->dev->platform_data);
|
||||
if (err < 0)
|
||||
return err;
|
||||
goto st_accel_power_off;
|
||||
|
||||
err = st_accel_allocate_ring(indio_dev);
|
||||
if (err < 0)
|
||||
return err;
|
||||
goto st_accel_power_off;
|
||||
|
||||
if (irq > 0) {
|
||||
err = st_sensors_allocate_trigger(indio_dev,
|
||||
|
@ -807,6 +809,8 @@ int st_accel_common_probe(struct iio_dev *indio_dev)
|
|||
st_sensors_deallocate_trigger(indio_dev);
|
||||
st_accel_probe_trigger_error:
|
||||
st_accel_deallocate_ring(indio_dev);
|
||||
st_accel_power_off:
|
||||
st_sensors_power_disable(indio_dev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
|
|
@ -154,12 +154,11 @@ static int ad7266_read_raw(struct iio_dev *indio_dev,
|
|||
|
||||
switch (m) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (iio_buffer_enabled(indio_dev))
|
||||
return -EBUSY;
|
||||
|
||||
ret = ad7266_read_single(st, val, chan->address);
|
||||
ret = iio_device_claim_direct_mode(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = ad7266_read_single(st, val, chan->address);
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
|
||||
*val = (*val >> 2) & 0xfff;
|
||||
if (chan->scan_type.sign == 's')
|
||||
|
|
|
@ -106,12 +106,11 @@ static int ad7476_read_raw(struct iio_dev *indio_dev,
|
|||
|
||||
switch (m) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
if (iio_buffer_enabled(indio_dev))
|
||||
ret = -EBUSY;
|
||||
else
|
||||
ret = ad7476_scan_direct(st);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
ret = iio_device_claim_direct_mode(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = ad7476_scan_direct(st);
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
|
|
@ -272,30 +272,22 @@ static ssize_t ad7791_write_frequency(struct device *dev,
|
|||
struct ad7791_state *st = iio_priv(indio_dev);
|
||||
int i, ret;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
if (iio_buffer_enabled(indio_dev)) {
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return -EBUSY;
|
||||
}
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
ret = -EINVAL;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ad7791_sample_freq_avail); i++) {
|
||||
if (sysfs_streq(ad7791_sample_freq_avail[i], buf)) {
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
st->filter &= ~AD7791_FILTER_RATE_MASK;
|
||||
st->filter |= i;
|
||||
ad_sd_write_reg(&st->sd, AD7791_REG_FILTER,
|
||||
sizeof(st->filter), st->filter);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
ret = 0;
|
||||
for (i = 0; i < ARRAY_SIZE(ad7791_sample_freq_avail); i++)
|
||||
if (sysfs_streq(ad7791_sample_freq_avail[i], buf))
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == ARRAY_SIZE(ad7791_sample_freq_avail))
|
||||
return -EINVAL;
|
||||
|
||||
return ret ? ret : len;
|
||||
ret = iio_device_claim_direct_mode(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
st->filter &= ~AD7791_FILTER_RATE_MASK;
|
||||
st->filter |= i;
|
||||
ad_sd_write_reg(&st->sd, AD7791_REG_FILTER, sizeof(st->filter),
|
||||
st->filter);
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
|
||||
|
|
|
@ -369,13 +369,6 @@ static ssize_t ad7793_write_frequency(struct device *dev,
|
|||
long lval;
|
||||
int i, ret;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
if (iio_buffer_enabled(indio_dev)) {
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return -EBUSY;
|
||||
}
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
ret = kstrtol(buf, 10, &lval);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -383,20 +376,21 @@ static ssize_t ad7793_write_frequency(struct device *dev,
|
|||
if (lval == 0)
|
||||
return -EINVAL;
|
||||
|
||||
ret = -EINVAL;
|
||||
|
||||
for (i = 0; i < 16; i++)
|
||||
if (lval == st->chip_info->sample_freq_avail[i]) {
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
st->mode &= ~AD7793_MODE_RATE(-1);
|
||||
st->mode |= AD7793_MODE_RATE(i);
|
||||
ad_sd_write_reg(&st->sd, AD7793_REG_MODE,
|
||||
sizeof(st->mode), st->mode);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
ret = 0;
|
||||
}
|
||||
if (lval == st->chip_info->sample_freq_avail[i])
|
||||
break;
|
||||
if (i == 16)
|
||||
return -EINVAL;
|
||||
|
||||
return ret ? ret : len;
|
||||
ret = iio_device_claim_direct_mode(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
st->mode &= ~AD7793_MODE_RATE(-1);
|
||||
st->mode |= AD7793_MODE_RATE(i);
|
||||
ad_sd_write_reg(&st->sd, AD7793_REG_MODE, sizeof(st->mode), st->mode);
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
|
||||
|
|
|
@ -156,12 +156,11 @@ static int ad7887_read_raw(struct iio_dev *indio_dev,
|
|||
|
||||
switch (m) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
if (iio_buffer_enabled(indio_dev))
|
||||
ret = -EBUSY;
|
||||
else
|
||||
ret = ad7887_scan_direct(st, chan->address);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
ret = iio_device_claim_direct_mode(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = ad7887_scan_direct(st, chan->address);
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
|
|
@ -233,12 +233,11 @@ static int ad7923_read_raw(struct iio_dev *indio_dev,
|
|||
|
||||
switch (m) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
if (iio_buffer_enabled(indio_dev))
|
||||
ret = -EBUSY;
|
||||
else
|
||||
ret = ad7923_scan_direct(st, chan->address);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
ret = iio_device_claim_direct_mode(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = ad7923_scan_direct(st, chan->address);
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
|
|
@ -282,12 +282,11 @@ static int ad799x_read_raw(struct iio_dev *indio_dev,
|
|||
|
||||
switch (m) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
if (iio_buffer_enabled(indio_dev))
|
||||
ret = -EBUSY;
|
||||
else
|
||||
ret = ad799x_scan_direct(st, chan->scan_index);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
ret = iio_device_claim_direct_mode(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = ad799x_scan_direct(st, chan->scan_index);
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
@ -395,11 +394,9 @@ static int ad799x_write_event_config(struct iio_dev *indio_dev,
|
|||
struct ad799x_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
if (iio_buffer_enabled(indio_dev)) {
|
||||
ret = -EBUSY;
|
||||
goto done;
|
||||
}
|
||||
ret = iio_device_claim_direct_mode(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (state)
|
||||
st->config |= BIT(chan->scan_index) << AD799X_CHANNEL_SHIFT;
|
||||
|
@ -412,10 +409,7 @@ static int ad799x_write_event_config(struct iio_dev *indio_dev,
|
|||
st->config &= ~AD7998_ALERT_EN;
|
||||
|
||||
ret = ad799x_write_config(st, st->config);
|
||||
|
||||
done:
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
@ -373,13 +373,6 @@ static u32 mxs_lradc_plate_mask(struct mxs_lradc *lradc)
|
|||
return LRADC_CTRL0_MX28_PLATE_MASK;
|
||||
}
|
||||
|
||||
static u32 mxs_lradc_irq_en_mask(struct mxs_lradc *lradc)
|
||||
{
|
||||
if (lradc->soc == IMX23_LRADC)
|
||||
return LRADC_CTRL1_MX23_LRADC_IRQ_EN_MASK;
|
||||
return LRADC_CTRL1_MX28_LRADC_IRQ_EN_MASK;
|
||||
}
|
||||
|
||||
static u32 mxs_lradc_irq_mask(struct mxs_lradc *lradc)
|
||||
{
|
||||
if (lradc->soc == IMX23_LRADC)
|
||||
|
@ -1120,18 +1113,16 @@ static int mxs_lradc_ts_register(struct mxs_lradc *lradc)
|
|||
{
|
||||
struct input_dev *input;
|
||||
struct device *dev = lradc->dev;
|
||||
int ret;
|
||||
|
||||
if (!lradc->use_touchscreen)
|
||||
return 0;
|
||||
|
||||
input = input_allocate_device();
|
||||
input = devm_input_allocate_device(dev);
|
||||
if (!input)
|
||||
return -ENOMEM;
|
||||
|
||||
input->name = DRIVER_NAME;
|
||||
input->id.bustype = BUS_HOST;
|
||||
input->dev.parent = dev;
|
||||
input->open = mxs_lradc_ts_open;
|
||||
input->close = mxs_lradc_ts_close;
|
||||
|
||||
|
@ -1146,20 +1137,8 @@ static int mxs_lradc_ts_register(struct mxs_lradc *lradc)
|
|||
|
||||
lradc->ts_input = input;
|
||||
input_set_drvdata(input, lradc);
|
||||
ret = input_register_device(input);
|
||||
if (ret)
|
||||
input_free_device(lradc->ts_input);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void mxs_lradc_ts_unregister(struct mxs_lradc *lradc)
|
||||
{
|
||||
if (!lradc->use_touchscreen)
|
||||
return;
|
||||
|
||||
mxs_lradc_disable_ts(lradc);
|
||||
input_unregister_device(lradc->ts_input);
|
||||
return input_register_device(input);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1510,7 +1489,9 @@ static void mxs_lradc_hw_stop(struct mxs_lradc *lradc)
|
|||
{
|
||||
int i;
|
||||
|
||||
mxs_lradc_reg_clear(lradc, mxs_lradc_irq_en_mask(lradc), LRADC_CTRL1);
|
||||
mxs_lradc_reg_clear(lradc,
|
||||
lradc->buffer_vchans << LRADC_CTRL1_LRADC_IRQ_EN_OFFSET,
|
||||
LRADC_CTRL1);
|
||||
|
||||
for (i = 0; i < LRADC_MAX_DELAY_CHANS; i++)
|
||||
mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(i));
|
||||
|
@ -1721,13 +1702,11 @@ static int mxs_lradc_probe(struct platform_device *pdev)
|
|||
ret = iio_device_register(iio);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to register IIO device\n");
|
||||
goto err_ts;
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_ts:
|
||||
mxs_lradc_ts_unregister(lradc);
|
||||
err_ts_register:
|
||||
mxs_lradc_hw_stop(lradc);
|
||||
err_dev:
|
||||
|
@ -1745,7 +1724,6 @@ static int mxs_lradc_remove(struct platform_device *pdev)
|
|||
struct mxs_lradc *lradc = iio_priv(iio);
|
||||
|
||||
iio_device_unregister(iio);
|
||||
mxs_lradc_ts_unregister(lradc);
|
||||
mxs_lradc_hw_stop(lradc);
|
||||
mxs_lradc_trigger_remove(iio);
|
||||
iio_triggered_buffer_cleanup(iio);
|
||||
|
|
|
@ -79,10 +79,29 @@ static const struct iio_chan_spec nau7802_chan_array[] = {
|
|||
static const u16 nau7802_sample_freq_avail[] = {10, 20, 40, 80,
|
||||
10, 10, 10, 320};
|
||||
|
||||
static ssize_t nau7802_show_scales(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct nau7802_state *st = iio_priv(dev_to_iio_dev(dev));
|
||||
int i, len = 0;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(st->scale_avail); i++)
|
||||
len += scnprintf(buf + len, PAGE_SIZE - len, "0.%09d ",
|
||||
st->scale_avail[i]);
|
||||
|
||||
buf[len-1] = '\n';
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("10 40 80 320");
|
||||
|
||||
static IIO_DEVICE_ATTR(in_voltage_scale_available, S_IRUGO, nau7802_show_scales,
|
||||
NULL, 0);
|
||||
|
||||
static struct attribute *nau7802_attributes[] = {
|
||||
&iio_const_attr_sampling_frequency_available.dev_attr.attr,
|
||||
&iio_dev_attr_in_voltage_scale_available.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
|
|
|
@ -55,6 +55,11 @@
|
|||
#define ADS1015_DEFAULT_DATA_RATE 4
|
||||
#define ADS1015_DEFAULT_CHAN 0
|
||||
|
||||
enum {
|
||||
ADS1015,
|
||||
ADS1115,
|
||||
};
|
||||
|
||||
enum ads1015_channels {
|
||||
ADS1015_AIN0_AIN1 = 0,
|
||||
ADS1015_AIN0_AIN3,
|
||||
|
@ -71,6 +76,10 @@ static const unsigned int ads1015_data_rate[] = {
|
|||
128, 250, 490, 920, 1600, 2400, 3300, 3300
|
||||
};
|
||||
|
||||
static const unsigned int ads1115_data_rate[] = {
|
||||
8, 16, 32, 64, 128, 250, 475, 860
|
||||
};
|
||||
|
||||
static const struct {
|
||||
int scale;
|
||||
int uscale;
|
||||
|
@ -101,6 +110,7 @@ static const struct {
|
|||
.shift = 4, \
|
||||
.endianness = IIO_CPU, \
|
||||
}, \
|
||||
.datasheet_name = "AIN"#_chan, \
|
||||
}
|
||||
|
||||
#define ADS1015_V_DIFF_CHAN(_chan, _chan2, _addr) { \
|
||||
|
@ -121,6 +131,45 @@ static const struct {
|
|||
.shift = 4, \
|
||||
.endianness = IIO_CPU, \
|
||||
}, \
|
||||
.datasheet_name = "AIN"#_chan"-AIN"#_chan2, \
|
||||
}
|
||||
|
||||
#define ADS1115_V_CHAN(_chan, _addr) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.address = _addr, \
|
||||
.channel = _chan, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_SCALE) | \
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ), \
|
||||
.scan_index = _addr, \
|
||||
.scan_type = { \
|
||||
.sign = 's', \
|
||||
.realbits = 16, \
|
||||
.storagebits = 16, \
|
||||
.endianness = IIO_CPU, \
|
||||
}, \
|
||||
.datasheet_name = "AIN"#_chan, \
|
||||
}
|
||||
|
||||
#define ADS1115_V_DIFF_CHAN(_chan, _chan2, _addr) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.differential = 1, \
|
||||
.indexed = 1, \
|
||||
.address = _addr, \
|
||||
.channel = _chan, \
|
||||
.channel2 = _chan2, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_SCALE) | \
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ), \
|
||||
.scan_index = _addr, \
|
||||
.scan_type = { \
|
||||
.sign = 's', \
|
||||
.realbits = 16, \
|
||||
.storagebits = 16, \
|
||||
.endianness = IIO_CPU, \
|
||||
}, \
|
||||
.datasheet_name = "AIN"#_chan"-AIN"#_chan2, \
|
||||
}
|
||||
|
||||
struct ads1015_data {
|
||||
|
@ -131,6 +180,8 @@ struct ads1015_data {
|
|||
*/
|
||||
struct mutex lock;
|
||||
struct ads1015_channel_data channel_data[ADS1015_CHANNELS];
|
||||
|
||||
unsigned int *data_rate;
|
||||
};
|
||||
|
||||
static bool ads1015_is_writeable_reg(struct device *dev, unsigned int reg)
|
||||
|
@ -157,6 +208,18 @@ static const struct iio_chan_spec ads1015_channels[] = {
|
|||
IIO_CHAN_SOFT_TIMESTAMP(ADS1015_TIMESTAMP),
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec ads1115_channels[] = {
|
||||
ADS1115_V_DIFF_CHAN(0, 1, ADS1015_AIN0_AIN1),
|
||||
ADS1115_V_DIFF_CHAN(0, 3, ADS1015_AIN0_AIN3),
|
||||
ADS1115_V_DIFF_CHAN(1, 3, ADS1015_AIN1_AIN3),
|
||||
ADS1115_V_DIFF_CHAN(2, 3, ADS1015_AIN2_AIN3),
|
||||
ADS1115_V_CHAN(0, ADS1015_AIN0),
|
||||
ADS1115_V_CHAN(1, ADS1015_AIN1),
|
||||
ADS1115_V_CHAN(2, ADS1015_AIN2),
|
||||
ADS1115_V_CHAN(3, ADS1015_AIN3),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(ADS1015_TIMESTAMP),
|
||||
};
|
||||
|
||||
static int ads1015_set_power_state(struct ads1015_data *data, bool on)
|
||||
{
|
||||
int ret;
|
||||
|
@ -196,7 +259,7 @@ int ads1015_get_adc_result(struct ads1015_data *data, int chan, int *val)
|
|||
return ret;
|
||||
|
||||
if (change) {
|
||||
conv_time = DIV_ROUND_UP(USEC_PER_SEC, ads1015_data_rate[dr]);
|
||||
conv_time = DIV_ROUND_UP(USEC_PER_SEC, data->data_rate[dr]);
|
||||
usleep_range(conv_time, conv_time + 1);
|
||||
}
|
||||
|
||||
|
@ -263,7 +326,7 @@ static int ads1015_set_data_rate(struct ads1015_data *data, int chan, int rate)
|
|||
int i, ret, rindex = -1;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ads1015_data_rate); i++)
|
||||
if (ads1015_data_rate[i] == rate) {
|
||||
if (data->data_rate[i] == rate) {
|
||||
rindex = i;
|
||||
break;
|
||||
}
|
||||
|
@ -291,7 +354,9 @@ static int ads1015_read_raw(struct iio_dev *indio_dev,
|
|||
mutex_lock(&indio_dev->mlock);
|
||||
mutex_lock(&data->lock);
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
case IIO_CHAN_INFO_RAW: {
|
||||
int shift = chan->scan_type.shift;
|
||||
|
||||
if (iio_buffer_enabled(indio_dev)) {
|
||||
ret = -EBUSY;
|
||||
break;
|
||||
|
@ -307,8 +372,7 @@ static int ads1015_read_raw(struct iio_dev *indio_dev,
|
|||
break;
|
||||
}
|
||||
|
||||
/* 12 bit res, D0 is bit 4 in conversion register */
|
||||
*val = sign_extend32(*val >> 4, 11);
|
||||
*val = sign_extend32(*val >> shift, 15 - shift);
|
||||
|
||||
ret = ads1015_set_power_state(data, false);
|
||||
if (ret < 0)
|
||||
|
@ -316,6 +380,7 @@ static int ads1015_read_raw(struct iio_dev *indio_dev,
|
|||
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
}
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
idx = data->channel_data[chan->address].pga;
|
||||
*val = ads1015_scale[idx].scale;
|
||||
|
@ -324,7 +389,7 @@ static int ads1015_read_raw(struct iio_dev *indio_dev,
|
|||
break;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
idx = data->channel_data[chan->address].data_rate;
|
||||
*val = ads1015_data_rate[idx];
|
||||
*val = data->data_rate[idx];
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
default:
|
||||
|
@ -380,12 +445,15 @@ static const struct iio_buffer_setup_ops ads1015_buffer_setup_ops = {
|
|||
};
|
||||
|
||||
static IIO_CONST_ATTR(scale_available, "3 2 1 0.5 0.25 0.125");
|
||||
static IIO_CONST_ATTR(sampling_frequency_available,
|
||||
"128 250 490 920 1600 2400 3300");
|
||||
|
||||
static IIO_CONST_ATTR_NAMED(ads1015_sampling_frequency_available,
|
||||
sampling_frequency_available, "128 250 490 920 1600 2400 3300");
|
||||
static IIO_CONST_ATTR_NAMED(ads1115_sampling_frequency_available,
|
||||
sampling_frequency_available, "8 16 32 64 128 250 475 860");
|
||||
|
||||
static struct attribute *ads1015_attributes[] = {
|
||||
&iio_const_attr_scale_available.dev_attr.attr,
|
||||
&iio_const_attr_sampling_frequency_available.dev_attr.attr,
|
||||
&iio_const_attr_ads1015_sampling_frequency_available.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
@ -393,11 +461,28 @@ static const struct attribute_group ads1015_attribute_group = {
|
|||
.attrs = ads1015_attributes,
|
||||
};
|
||||
|
||||
static const struct iio_info ads1015_info = {
|
||||
static struct attribute *ads1115_attributes[] = {
|
||||
&iio_const_attr_scale_available.dev_attr.attr,
|
||||
&iio_const_attr_ads1115_sampling_frequency_available.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group ads1115_attribute_group = {
|
||||
.attrs = ads1115_attributes,
|
||||
};
|
||||
|
||||
static struct iio_info ads1015_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = ads1015_read_raw,
|
||||
.write_raw = ads1015_write_raw,
|
||||
.attrs = &ads1015_attribute_group,
|
||||
.attrs = &ads1015_attribute_group,
|
||||
};
|
||||
|
||||
static struct iio_info ads1115_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = ads1015_read_raw,
|
||||
.write_raw = ads1015_write_raw,
|
||||
.attrs = &ads1115_attribute_group,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
|
@ -500,12 +585,24 @@ static int ads1015_probe(struct i2c_client *client,
|
|||
mutex_init(&data->lock);
|
||||
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->info = &ads1015_info;
|
||||
indio_dev->name = ADS1015_DRV_NAME;
|
||||
indio_dev->channels = ads1015_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(ads1015_channels);
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
switch (id->driver_data) {
|
||||
case ADS1015:
|
||||
indio_dev->channels = ads1015_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(ads1015_channels);
|
||||
indio_dev->info = &ads1015_info;
|
||||
data->data_rate = (unsigned int *) &ads1015_data_rate;
|
||||
break;
|
||||
case ADS1115:
|
||||
indio_dev->channels = ads1115_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(ads1115_channels);
|
||||
indio_dev->info = &ads1115_info;
|
||||
data->data_rate = (unsigned int *) &ads1115_data_rate;
|
||||
break;
|
||||
}
|
||||
|
||||
/* we need to keep this ABI the same as used by hwmon ADS1015 driver */
|
||||
ads1015_get_channels_config(client);
|
||||
|
||||
|
@ -590,7 +687,8 @@ static const struct dev_pm_ops ads1015_pm_ops = {
|
|||
};
|
||||
|
||||
static const struct i2c_device_id ads1015_id[] = {
|
||||
{"ads1015", 0},
|
||||
{"ads1015", ADS1015},
|
||||
{"ads1115", ADS1115},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ads1015_id);
|
||||
|
|
|
@ -326,8 +326,7 @@ static int tiadc_channel_init(struct iio_dev *indio_dev, int channels)
|
|||
int i;
|
||||
|
||||
indio_dev->num_channels = channels;
|
||||
chan_array = kcalloc(channels,
|
||||
sizeof(struct iio_chan_spec), GFP_KERNEL);
|
||||
chan_array = kcalloc(channels, sizeof(*chan_array), GFP_KERNEL);
|
||||
if (chan_array == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -467,8 +466,7 @@ static int tiadc_probe(struct platform_device *pdev)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev,
|
||||
sizeof(struct tiadc_device));
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*indio_dev));
|
||||
if (indio_dev == NULL) {
|
||||
dev_err(&pdev->dev, "failed to allocate iio device\n");
|
||||
return -ENOMEM;
|
||||
|
@ -531,8 +529,7 @@ static int tiadc_remove(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int tiadc_suspend(struct device *dev)
|
||||
static int __maybe_unused tiadc_suspend(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct tiadc_device *adc_dev = iio_priv(indio_dev);
|
||||
|
@ -550,7 +547,7 @@ static int tiadc_suspend(struct device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int tiadc_resume(struct device *dev)
|
||||
static int __maybe_unused tiadc_resume(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct tiadc_device *adc_dev = iio_priv(indio_dev);
|
||||
|
@ -567,14 +564,7 @@ static int tiadc_resume(struct device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops tiadc_pm_ops = {
|
||||
.suspend = tiadc_suspend,
|
||||
.resume = tiadc_resume,
|
||||
};
|
||||
#define TIADC_PM_OPS (&tiadc_pm_ops)
|
||||
#else
|
||||
#define TIADC_PM_OPS NULL
|
||||
#endif
|
||||
static SIMPLE_DEV_PM_OPS(tiadc_pm_ops, tiadc_suspend, tiadc_resume);
|
||||
|
||||
static const struct of_device_id ti_adc_dt_ids[] = {
|
||||
{ .compatible = "ti,am3359-adc", },
|
||||
|
@ -585,7 +575,7 @@ MODULE_DEVICE_TABLE(of, ti_adc_dt_ids);
|
|||
static struct platform_driver tiadc_driver = {
|
||||
.driver = {
|
||||
.name = "TI-am335x-adc",
|
||||
.pm = TIADC_PM_OPS,
|
||||
.pm = &tiadc_pm_ops,
|
||||
.of_match_table = ti_adc_dt_ids,
|
||||
},
|
||||
.probe = tiadc_probe,
|
||||
|
|
|
@ -5,15 +5,17 @@
|
|||
menu "Chemical Sensors"
|
||||
|
||||
config ATLAS_PH_SENSOR
|
||||
tristate "Atlas Scientific OEM pH-SM sensor"
|
||||
tristate "Atlas Scientific OEM SM sensors"
|
||||
depends on I2C
|
||||
select REGMAP_I2C
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
select IRQ_WORK
|
||||
help
|
||||
Say Y here to build I2C interface support for the Atlas
|
||||
Scientific OEM pH-SM sensor.
|
||||
Say Y here to build I2C interface support for the following
|
||||
Atlas Scientific OEM SM sensors:
|
||||
* pH SM sensor
|
||||
* EC SM sensor
|
||||
|
||||
To compile this driver as module, choose M here: the
|
||||
module will be called atlas-ph-sensor.
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include <linux/irq_work.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
|
@ -43,29 +44,50 @@
|
|||
|
||||
#define ATLAS_REG_PWR_CONTROL 0x06
|
||||
|
||||
#define ATLAS_REG_CALIB_STATUS 0x0d
|
||||
#define ATLAS_REG_CALIB_STATUS_MASK 0x07
|
||||
#define ATLAS_REG_CALIB_STATUS_LOW BIT(0)
|
||||
#define ATLAS_REG_CALIB_STATUS_MID BIT(1)
|
||||
#define ATLAS_REG_CALIB_STATUS_HIGH BIT(2)
|
||||
#define ATLAS_REG_PH_CALIB_STATUS 0x0d
|
||||
#define ATLAS_REG_PH_CALIB_STATUS_MASK 0x07
|
||||
#define ATLAS_REG_PH_CALIB_STATUS_LOW BIT(0)
|
||||
#define ATLAS_REG_PH_CALIB_STATUS_MID BIT(1)
|
||||
#define ATLAS_REG_PH_CALIB_STATUS_HIGH BIT(2)
|
||||
|
||||
#define ATLAS_REG_TEMP_DATA 0x0e
|
||||
#define ATLAS_REG_EC_CALIB_STATUS 0x0f
|
||||
#define ATLAS_REG_EC_CALIB_STATUS_MASK 0x0f
|
||||
#define ATLAS_REG_EC_CALIB_STATUS_DRY BIT(0)
|
||||
#define ATLAS_REG_EC_CALIB_STATUS_SINGLE BIT(1)
|
||||
#define ATLAS_REG_EC_CALIB_STATUS_LOW BIT(2)
|
||||
#define ATLAS_REG_EC_CALIB_STATUS_HIGH BIT(3)
|
||||
|
||||
#define ATLAS_REG_PH_TEMP_DATA 0x0e
|
||||
#define ATLAS_REG_PH_DATA 0x16
|
||||
|
||||
#define ATLAS_REG_EC_PROBE 0x08
|
||||
#define ATLAS_REG_EC_TEMP_DATA 0x10
|
||||
#define ATLAS_REG_EC_DATA 0x18
|
||||
#define ATLAS_REG_TDS_DATA 0x1c
|
||||
#define ATLAS_REG_PSS_DATA 0x20
|
||||
|
||||
#define ATLAS_PH_INT_TIME_IN_US 450000
|
||||
#define ATLAS_EC_INT_TIME_IN_US 650000
|
||||
|
||||
enum {
|
||||
ATLAS_PH_SM,
|
||||
ATLAS_EC_SM,
|
||||
};
|
||||
|
||||
struct atlas_data {
|
||||
struct i2c_client *client;
|
||||
struct iio_trigger *trig;
|
||||
struct atlas_device *chip;
|
||||
struct regmap *regmap;
|
||||
struct irq_work work;
|
||||
|
||||
__be32 buffer[4]; /* 32-bit pH data + 32-bit pad + 64-bit timestamp */
|
||||
__be32 buffer[6]; /* 96-bit data + 32-bit pad + 64-bit timestamp */
|
||||
};
|
||||
|
||||
static const struct regmap_range atlas_volatile_ranges[] = {
|
||||
regmap_reg_range(ATLAS_REG_INT_CONTROL, ATLAS_REG_INT_CONTROL),
|
||||
regmap_reg_range(ATLAS_REG_PH_DATA, ATLAS_REG_PH_DATA + 4),
|
||||
regmap_reg_range(ATLAS_REG_EC_DATA, ATLAS_REG_PSS_DATA + 4),
|
||||
};
|
||||
|
||||
static const struct regmap_access_table atlas_volatile_table = {
|
||||
|
@ -80,13 +102,14 @@ static const struct regmap_config atlas_regmap_config = {
|
|||
.val_bits = 8,
|
||||
|
||||
.volatile_table = &atlas_volatile_table,
|
||||
.max_register = ATLAS_REG_PH_DATA + 4,
|
||||
.max_register = ATLAS_REG_PSS_DATA + 4,
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec atlas_channels[] = {
|
||||
static const struct iio_chan_spec atlas_ph_channels[] = {
|
||||
{
|
||||
.type = IIO_PH,
|
||||
.address = ATLAS_REG_PH_DATA,
|
||||
.info_mask_separate =
|
||||
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
|
||||
.scan_index = 0,
|
||||
|
@ -100,7 +123,7 @@ static const struct iio_chan_spec atlas_channels[] = {
|
|||
IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
{
|
||||
.type = IIO_TEMP,
|
||||
.address = ATLAS_REG_TEMP_DATA,
|
||||
.address = ATLAS_REG_PH_TEMP_DATA,
|
||||
.info_mask_separate =
|
||||
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
|
||||
.output = 1,
|
||||
|
@ -108,6 +131,142 @@ static const struct iio_chan_spec atlas_channels[] = {
|
|||
},
|
||||
};
|
||||
|
||||
#define ATLAS_EC_CHANNEL(_idx, _addr) \
|
||||
{\
|
||||
.type = IIO_CONCENTRATION, \
|
||||
.indexed = 1, \
|
||||
.channel = _idx, \
|
||||
.address = _addr, \
|
||||
.info_mask_separate = \
|
||||
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.scan_index = _idx + 1, \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = 32, \
|
||||
.storagebits = 32, \
|
||||
.endianness = IIO_BE, \
|
||||
}, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec atlas_ec_channels[] = {
|
||||
{
|
||||
.type = IIO_ELECTRICALCONDUCTIVITY,
|
||||
.address = ATLAS_REG_EC_DATA,
|
||||
.info_mask_separate =
|
||||
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
|
||||
.scan_index = 0,
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
.realbits = 32,
|
||||
.storagebits = 32,
|
||||
.endianness = IIO_BE,
|
||||
},
|
||||
},
|
||||
ATLAS_EC_CHANNEL(0, ATLAS_REG_TDS_DATA),
|
||||
ATLAS_EC_CHANNEL(1, ATLAS_REG_PSS_DATA),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(3),
|
||||
{
|
||||
.type = IIO_TEMP,
|
||||
.address = ATLAS_REG_EC_TEMP_DATA,
|
||||
.info_mask_separate =
|
||||
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
|
||||
.output = 1,
|
||||
.scan_index = -1
|
||||
},
|
||||
};
|
||||
|
||||
static int atlas_check_ph_calibration(struct atlas_data *data)
|
||||
{
|
||||
struct device *dev = &data->client->dev;
|
||||
int ret;
|
||||
unsigned int val;
|
||||
|
||||
ret = regmap_read(data->regmap, ATLAS_REG_PH_CALIB_STATUS, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!(val & ATLAS_REG_PH_CALIB_STATUS_MASK)) {
|
||||
dev_warn(dev, "device has not been calibrated\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(val & ATLAS_REG_PH_CALIB_STATUS_LOW))
|
||||
dev_warn(dev, "device missing low point calibration\n");
|
||||
|
||||
if (!(val & ATLAS_REG_PH_CALIB_STATUS_MID))
|
||||
dev_warn(dev, "device missing mid point calibration\n");
|
||||
|
||||
if (!(val & ATLAS_REG_PH_CALIB_STATUS_HIGH))
|
||||
dev_warn(dev, "device missing high point calibration\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int atlas_check_ec_calibration(struct atlas_data *data)
|
||||
{
|
||||
struct device *dev = &data->client->dev;
|
||||
int ret;
|
||||
unsigned int val;
|
||||
|
||||
ret = regmap_bulk_read(data->regmap, ATLAS_REG_EC_PROBE, &val, 2);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev_info(dev, "probe set to K = %d.%.2d", be16_to_cpu(val) / 100,
|
||||
be16_to_cpu(val) % 100);
|
||||
|
||||
ret = regmap_read(data->regmap, ATLAS_REG_EC_CALIB_STATUS, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!(val & ATLAS_REG_EC_CALIB_STATUS_MASK)) {
|
||||
dev_warn(dev, "device has not been calibrated\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(val & ATLAS_REG_EC_CALIB_STATUS_DRY))
|
||||
dev_warn(dev, "device missing dry point calibration\n");
|
||||
|
||||
if (val & ATLAS_REG_EC_CALIB_STATUS_SINGLE) {
|
||||
dev_warn(dev, "device using single point calibration\n");
|
||||
} else {
|
||||
if (!(val & ATLAS_REG_EC_CALIB_STATUS_LOW))
|
||||
dev_warn(dev, "device missing low point calibration\n");
|
||||
|
||||
if (!(val & ATLAS_REG_EC_CALIB_STATUS_HIGH))
|
||||
dev_warn(dev, "device missing high point calibration\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct atlas_device {
|
||||
const struct iio_chan_spec *channels;
|
||||
int num_channels;
|
||||
int data_reg;
|
||||
|
||||
int (*calibration)(struct atlas_data *data);
|
||||
int delay;
|
||||
};
|
||||
|
||||
static struct atlas_device atlas_devices[] = {
|
||||
[ATLAS_PH_SM] = {
|
||||
.channels = atlas_ph_channels,
|
||||
.num_channels = 3,
|
||||
.data_reg = ATLAS_REG_PH_DATA,
|
||||
.calibration = &atlas_check_ph_calibration,
|
||||
.delay = ATLAS_PH_INT_TIME_IN_US,
|
||||
},
|
||||
[ATLAS_EC_SM] = {
|
||||
.channels = atlas_ec_channels,
|
||||
.num_channels = 5,
|
||||
.data_reg = ATLAS_REG_EC_DATA,
|
||||
.calibration = &atlas_check_ec_calibration,
|
||||
.delay = ATLAS_EC_INT_TIME_IN_US,
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
static int atlas_set_powermode(struct atlas_data *data, int on)
|
||||
{
|
||||
return regmap_write(data->regmap, ATLAS_REG_PWR_CONTROL, on);
|
||||
|
@ -178,8 +337,9 @@ static irqreturn_t atlas_trigger_handler(int irq, void *private)
|
|||
struct atlas_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = regmap_bulk_read(data->regmap, ATLAS_REG_PH_DATA,
|
||||
(u8 *) &data->buffer, sizeof(data->buffer[0]));
|
||||
ret = regmap_bulk_read(data->regmap, data->chip->data_reg,
|
||||
(u8 *) &data->buffer,
|
||||
sizeof(__be32) * (data->chip->num_channels - 2));
|
||||
|
||||
if (!ret)
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
|
||||
|
@ -200,7 +360,7 @@ static irqreturn_t atlas_interrupt_handler(int irq, void *private)
|
|||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int atlas_read_ph_measurement(struct atlas_data *data, __be32 *val)
|
||||
static int atlas_read_measurement(struct atlas_data *data, int reg, __be32 *val)
|
||||
{
|
||||
struct device *dev = &data->client->dev;
|
||||
int suspended = pm_runtime_suspended(dev);
|
||||
|
@ -213,11 +373,9 @@ static int atlas_read_ph_measurement(struct atlas_data *data, __be32 *val)
|
|||
}
|
||||
|
||||
if (suspended)
|
||||
usleep_range(ATLAS_PH_INT_TIME_IN_US,
|
||||
ATLAS_PH_INT_TIME_IN_US + 100000);
|
||||
usleep_range(data->chip->delay, data->chip->delay + 100000);
|
||||
|
||||
ret = regmap_bulk_read(data->regmap, ATLAS_REG_PH_DATA,
|
||||
(u8 *) val, sizeof(*val));
|
||||
ret = regmap_bulk_read(data->regmap, reg, (u8 *) val, sizeof(*val));
|
||||
|
||||
pm_runtime_mark_last_busy(dev);
|
||||
pm_runtime_put_autosuspend(dev);
|
||||
|
@ -242,12 +400,15 @@ static int atlas_read_raw(struct iio_dev *indio_dev,
|
|||
(u8 *) ®, sizeof(reg));
|
||||
break;
|
||||
case IIO_PH:
|
||||
case IIO_CONCENTRATION:
|
||||
case IIO_ELECTRICALCONDUCTIVITY:
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
|
||||
if (iio_buffer_enabled(indio_dev))
|
||||
ret = -EBUSY;
|
||||
else
|
||||
ret = atlas_read_ph_measurement(data, ®);
|
||||
ret = atlas_read_measurement(data,
|
||||
chan->address, ®);
|
||||
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
break;
|
||||
|
@ -271,6 +432,14 @@ static int atlas_read_raw(struct iio_dev *indio_dev,
|
|||
*val = 1; /* 0.001 */
|
||||
*val2 = 1000;
|
||||
break;
|
||||
case IIO_ELECTRICALCONDUCTIVITY:
|
||||
*val = 1; /* 0.00001 */
|
||||
*val = 100000;
|
||||
break;
|
||||
case IIO_CONCENTRATION:
|
||||
*val = 0; /* 0.000000001 */
|
||||
*val2 = 1000;
|
||||
return IIO_VAL_INT_PLUS_NANO;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -303,37 +472,26 @@ static const struct iio_info atlas_info = {
|
|||
.write_raw = atlas_write_raw,
|
||||
};
|
||||
|
||||
static int atlas_check_calibration(struct atlas_data *data)
|
||||
{
|
||||
struct device *dev = &data->client->dev;
|
||||
int ret;
|
||||
unsigned int val;
|
||||
|
||||
ret = regmap_read(data->regmap, ATLAS_REG_CALIB_STATUS, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!(val & ATLAS_REG_CALIB_STATUS_MASK)) {
|
||||
dev_warn(dev, "device has not been calibrated\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(val & ATLAS_REG_CALIB_STATUS_LOW))
|
||||
dev_warn(dev, "device missing low point calibration\n");
|
||||
|
||||
if (!(val & ATLAS_REG_CALIB_STATUS_MID))
|
||||
dev_warn(dev, "device missing mid point calibration\n");
|
||||
|
||||
if (!(val & ATLAS_REG_CALIB_STATUS_HIGH))
|
||||
dev_warn(dev, "device missing high point calibration\n");
|
||||
|
||||
return 0;
|
||||
static const struct i2c_device_id atlas_id[] = {
|
||||
{ "atlas-ph-sm", ATLAS_PH_SM},
|
||||
{ "atlas-ec-sm", ATLAS_EC_SM},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, atlas_id);
|
||||
|
||||
static const struct of_device_id atlas_dt_ids[] = {
|
||||
{ .compatible = "atlas,ph-sm", .data = (void *)ATLAS_PH_SM, },
|
||||
{ .compatible = "atlas,ec-sm", .data = (void *)ATLAS_EC_SM, },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, atlas_dt_ids);
|
||||
|
||||
static int atlas_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct atlas_data *data;
|
||||
struct atlas_device *chip;
|
||||
const struct of_device_id *of_id;
|
||||
struct iio_trigger *trig;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret;
|
||||
|
@ -342,10 +500,16 @@ static int atlas_probe(struct i2c_client *client,
|
|||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
of_id = of_match_device(atlas_dt_ids, &client->dev);
|
||||
if (!of_id)
|
||||
chip = &atlas_devices[id->driver_data];
|
||||
else
|
||||
chip = &atlas_devices[(unsigned long)of_id->data];
|
||||
|
||||
indio_dev->info = &atlas_info;
|
||||
indio_dev->name = ATLAS_DRV_NAME;
|
||||
indio_dev->channels = atlas_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(atlas_channels);
|
||||
indio_dev->channels = chip->channels;
|
||||
indio_dev->num_channels = chip->num_channels;
|
||||
indio_dev->modes = INDIO_BUFFER_SOFTWARE | INDIO_DIRECT_MODE;
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
|
||||
|
@ -358,6 +522,7 @@ static int atlas_probe(struct i2c_client *client,
|
|||
data = iio_priv(indio_dev);
|
||||
data->client = client;
|
||||
data->trig = trig;
|
||||
data->chip = chip;
|
||||
trig->dev.parent = indio_dev->dev.parent;
|
||||
trig->ops = &atlas_interrupt_trigger_ops;
|
||||
iio_trigger_set_drvdata(trig, indio_dev);
|
||||
|
@ -379,7 +544,7 @@ static int atlas_probe(struct i2c_client *client,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = atlas_check_calibration(data);
|
||||
ret = chip->calibration(data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -480,18 +645,6 @@ static const struct dev_pm_ops atlas_pm_ops = {
|
|||
atlas_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
static const struct i2c_device_id atlas_id[] = {
|
||||
{ "atlas-ph-sm", 0 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, atlas_id);
|
||||
|
||||
static const struct of_device_id atlas_dt_ids[] = {
|
||||
{ .compatible = "atlas,ph-sm" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, atlas_dt_ids);
|
||||
|
||||
static struct i2c_driver atlas_driver = {
|
||||
.driver = {
|
||||
.name = ATLAS_DRV_NAME,
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
#include <linux/iio/common/st_sensors.h>
|
||||
|
||||
|
||||
int st_sensors_get_buffer_element(struct iio_dev *indio_dev, u8 *buf)
|
||||
static int st_sensors_get_buffer_element(struct iio_dev *indio_dev, u8 *buf)
|
||||
{
|
||||
int i, len;
|
||||
int total = 0;
|
||||
|
@ -49,7 +49,6 @@ int st_sensors_get_buffer_element(struct iio_dev *indio_dev, u8 *buf)
|
|||
|
||||
return total;
|
||||
}
|
||||
EXPORT_SYMBOL(st_sensors_get_buffer_element);
|
||||
|
||||
irqreturn_t st_sensors_trigger_handler(int irq, void *p)
|
||||
{
|
||||
|
|
|
@ -228,7 +228,7 @@ int st_sensors_set_axis_enable(struct iio_dev *indio_dev, u8 axis_enable)
|
|||
}
|
||||
EXPORT_SYMBOL(st_sensors_set_axis_enable);
|
||||
|
||||
void st_sensors_power_enable(struct iio_dev *indio_dev)
|
||||
int st_sensors_power_enable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct st_sensor_data *pdata = iio_priv(indio_dev);
|
||||
int err;
|
||||
|
@ -237,18 +237,37 @@ void st_sensors_power_enable(struct iio_dev *indio_dev)
|
|||
pdata->vdd = devm_regulator_get_optional(indio_dev->dev.parent, "vdd");
|
||||
if (!IS_ERR(pdata->vdd)) {
|
||||
err = regulator_enable(pdata->vdd);
|
||||
if (err != 0)
|
||||
if (err != 0) {
|
||||
dev_warn(&indio_dev->dev,
|
||||
"Failed to enable specified Vdd supply\n");
|
||||
return err;
|
||||
}
|
||||
} else {
|
||||
err = PTR_ERR(pdata->vdd);
|
||||
if (err != -ENODEV)
|
||||
return err;
|
||||
}
|
||||
|
||||
pdata->vdd_io = devm_regulator_get_optional(indio_dev->dev.parent, "vddio");
|
||||
if (!IS_ERR(pdata->vdd_io)) {
|
||||
err = regulator_enable(pdata->vdd_io);
|
||||
if (err != 0)
|
||||
if (err != 0) {
|
||||
dev_warn(&indio_dev->dev,
|
||||
"Failed to enable specified Vdd_IO supply\n");
|
||||
goto st_sensors_disable_vdd;
|
||||
}
|
||||
} else {
|
||||
err = PTR_ERR(pdata->vdd_io);
|
||||
if (err != -ENODEV)
|
||||
goto st_sensors_disable_vdd;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
st_sensors_disable_vdd:
|
||||
if (!IS_ERR_OR_NULL(pdata->vdd))
|
||||
regulator_disable(pdata->vdd);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(st_sensors_power_enable);
|
||||
|
||||
|
@ -256,10 +275,10 @@ void st_sensors_power_disable(struct iio_dev *indio_dev)
|
|||
{
|
||||
struct st_sensor_data *pdata = iio_priv(indio_dev);
|
||||
|
||||
if (!IS_ERR(pdata->vdd))
|
||||
if (!IS_ERR_OR_NULL(pdata->vdd))
|
||||
regulator_disable(pdata->vdd);
|
||||
|
||||
if (!IS_ERR(pdata->vdd_io))
|
||||
if (!IS_ERR_OR_NULL(pdata->vdd_io))
|
||||
regulator_disable(pdata->vdd_io);
|
||||
}
|
||||
EXPORT_SYMBOL(st_sensors_power_disable);
|
||||
|
|
|
@ -48,8 +48,8 @@ static int st_sensors_i2c_read_multiple_byte(
|
|||
if (multiread_bit)
|
||||
reg_addr |= ST_SENSORS_I2C_MULTIREAD;
|
||||
|
||||
return i2c_smbus_read_i2c_block_data(to_i2c_client(dev),
|
||||
reg_addr, len, data);
|
||||
return i2c_smbus_read_i2c_block_data_or_emulated(to_i2c_client(dev),
|
||||
reg_addr, len, data);
|
||||
}
|
||||
|
||||
static int st_sensors_i2c_write_byte(struct st_sensor_transfer_buffer *tb,
|
||||
|
|
|
@ -10,6 +10,7 @@ config IIO_DUMMY_EVGEN
|
|||
|
||||
config IIO_SIMPLE_DUMMY
|
||||
tristate "An example driver with no hardware requirements"
|
||||
depends on IIO_SW_DEVICE
|
||||
help
|
||||
Driver intended mainly as documentation for how to write
|
||||
a driver. May also be useful for testing userspace code
|
||||
|
|
|
@ -17,26 +17,18 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/events.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/sw_device.h>
|
||||
#include "iio_simple_dummy.h"
|
||||
|
||||
/*
|
||||
* A few elements needed to fake a bus for this driver
|
||||
* Note instances parameter controls how many of these
|
||||
* dummy devices are registered.
|
||||
*/
|
||||
static unsigned instances = 1;
|
||||
module_param(instances, uint, 0);
|
||||
|
||||
/* Pointer array used to fake bus elements */
|
||||
static struct iio_dev **iio_dummy_devs;
|
||||
|
||||
/* Fake a name for the part number, usually obtained from the id table */
|
||||
static const char *iio_dummy_part_number = "iio_dummy_part_no";
|
||||
static struct config_item_type iio_dummy_type = {
|
||||
.ct_owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct iio_dummy_accel_calibscale - realworld to register mapping
|
||||
|
@ -572,12 +564,18 @@ static int iio_dummy_init_device(struct iio_dev *indio_dev)
|
|||
* const struct i2c_device_id *id)
|
||||
* SPI: iio_dummy_probe(struct spi_device *spi)
|
||||
*/
|
||||
static int iio_dummy_probe(int index)
|
||||
static struct iio_sw_device *iio_dummy_probe(const char *name)
|
||||
{
|
||||
int ret;
|
||||
struct iio_dev *indio_dev;
|
||||
struct iio_dummy_state *st;
|
||||
struct iio_sw_device *swd;
|
||||
|
||||
swd = kzalloc(sizeof(*swd), GFP_KERNEL);
|
||||
if (!swd) {
|
||||
ret = -ENOMEM;
|
||||
goto error_kzalloc;
|
||||
}
|
||||
/*
|
||||
* Allocate an IIO device.
|
||||
*
|
||||
|
@ -608,7 +606,7 @@ static int iio_dummy_probe(int index)
|
|||
* i2c_set_clientdata(client, indio_dev);
|
||||
* spi_set_drvdata(spi, indio_dev);
|
||||
*/
|
||||
iio_dummy_devs[index] = indio_dev;
|
||||
swd->device = indio_dev;
|
||||
|
||||
/*
|
||||
* Set the device name.
|
||||
|
@ -619,7 +617,7 @@ static int iio_dummy_probe(int index)
|
|||
* indio_dev->name = id->name;
|
||||
* indio_dev->name = spi_get_device_id(spi)->name;
|
||||
*/
|
||||
indio_dev->name = iio_dummy_part_number;
|
||||
indio_dev->name = kstrdup(name, GFP_KERNEL);
|
||||
|
||||
/* Provide description of available channels */
|
||||
indio_dev->channels = iio_dummy_channels;
|
||||
|
@ -646,7 +644,9 @@ static int iio_dummy_probe(int index)
|
|||
if (ret < 0)
|
||||
goto error_unconfigure_buffer;
|
||||
|
||||
return 0;
|
||||
iio_swd_group_init_type_name(swd, name, &iio_dummy_type);
|
||||
|
||||
return swd;
|
||||
error_unconfigure_buffer:
|
||||
iio_simple_dummy_unconfigure_buffer(indio_dev);
|
||||
error_unregister_events:
|
||||
|
@ -654,16 +654,18 @@ static int iio_dummy_probe(int index)
|
|||
error_free_device:
|
||||
iio_device_free(indio_dev);
|
||||
error_ret:
|
||||
return ret;
|
||||
kfree(swd);
|
||||
error_kzalloc:
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* iio_dummy_remove() - device instance removal function
|
||||
* @index: device index.
|
||||
* @swd: pointer to software IIO device abstraction
|
||||
*
|
||||
* Parameters follow those of iio_dummy_probe for buses.
|
||||
*/
|
||||
static void iio_dummy_remove(int index)
|
||||
static int iio_dummy_remove(struct iio_sw_device *swd)
|
||||
{
|
||||
/*
|
||||
* Get a pointer to the device instance iio_dev structure
|
||||
|
@ -671,7 +673,7 @@ static void iio_dummy_remove(int index)
|
|||
* struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
* struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
*/
|
||||
struct iio_dev *indio_dev = iio_dummy_devs[index];
|
||||
struct iio_dev *indio_dev = swd->device;
|
||||
|
||||
/* Unregister the device */
|
||||
iio_device_unregister(indio_dev);
|
||||
|
@ -684,11 +686,13 @@ static void iio_dummy_remove(int index)
|
|||
iio_simple_dummy_events_unregister(indio_dev);
|
||||
|
||||
/* Free all structures */
|
||||
kfree(indio_dev->name);
|
||||
iio_device_free(indio_dev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
/**
|
||||
* iio_dummy_init() - device driver registration
|
||||
* module_iio_sw_device_driver() - device driver registration
|
||||
*
|
||||
* Varies depending on bus type of the device. As there is no device
|
||||
* here, call probe directly. For information on device registration
|
||||
|
@ -697,50 +701,18 @@ static void iio_dummy_remove(int index)
|
|||
* spi:
|
||||
* Documentation/spi/spi-summary
|
||||
*/
|
||||
static __init int iio_dummy_init(void)
|
||||
{
|
||||
int i, ret;
|
||||
static const struct iio_sw_device_ops iio_dummy_device_ops = {
|
||||
.probe = iio_dummy_probe,
|
||||
.remove = iio_dummy_remove,
|
||||
};
|
||||
|
||||
if (instances > 10) {
|
||||
instances = 1;
|
||||
return -EINVAL;
|
||||
}
|
||||
static struct iio_sw_device_type iio_dummy_device = {
|
||||
.name = "dummy",
|
||||
.owner = THIS_MODULE,
|
||||
.ops = &iio_dummy_device_ops,
|
||||
};
|
||||
|
||||
/* Fake a bus */
|
||||
iio_dummy_devs = kcalloc(instances, sizeof(*iio_dummy_devs),
|
||||
GFP_KERNEL);
|
||||
/* Here we have no actual device so call probe */
|
||||
for (i = 0; i < instances; i++) {
|
||||
ret = iio_dummy_probe(i);
|
||||
if (ret < 0)
|
||||
goto error_remove_devs;
|
||||
}
|
||||
return 0;
|
||||
|
||||
error_remove_devs:
|
||||
while (i--)
|
||||
iio_dummy_remove(i);
|
||||
|
||||
kfree(iio_dummy_devs);
|
||||
return ret;
|
||||
}
|
||||
module_init(iio_dummy_init);
|
||||
|
||||
/**
|
||||
* iio_dummy_exit() - device driver removal
|
||||
*
|
||||
* Varies depending on bus type of the device.
|
||||
* As there is no device here, call remove directly.
|
||||
*/
|
||||
static __exit void iio_dummy_exit(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < instances; i++)
|
||||
iio_dummy_remove(i);
|
||||
kfree(iio_dummy_devs);
|
||||
}
|
||||
module_exit(iio_dummy_exit);
|
||||
module_iio_sw_device_driver(iio_dummy_device);
|
||||
|
||||
MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>");
|
||||
MODULE_DESCRIPTION("IIO dummy driver");
|
||||
|
|
|
@ -425,13 +425,15 @@ int st_gyro_common_probe(struct iio_dev *indio_dev)
|
|||
indio_dev->info = &gyro_info;
|
||||
mutex_init(&gdata->tb.buf_lock);
|
||||
|
||||
st_sensors_power_enable(indio_dev);
|
||||
err = st_sensors_power_enable(indio_dev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = st_sensors_check_device_support(indio_dev,
|
||||
ARRAY_SIZE(st_gyro_sensors_settings),
|
||||
st_gyro_sensors_settings);
|
||||
if (err < 0)
|
||||
return err;
|
||||
goto st_gyro_power_off;
|
||||
|
||||
gdata->num_data_channels = ST_GYRO_NUMBER_DATA_CHANNELS;
|
||||
gdata->multiread_bit = gdata->sensor_settings->multi_read_bit;
|
||||
|
@ -445,11 +447,11 @@ int st_gyro_common_probe(struct iio_dev *indio_dev)
|
|||
err = st_sensors_init_sensor(indio_dev,
|
||||
(struct st_sensors_platform_data *)&gyro_pdata);
|
||||
if (err < 0)
|
||||
return err;
|
||||
goto st_gyro_power_off;
|
||||
|
||||
err = st_gyro_allocate_ring(indio_dev);
|
||||
if (err < 0)
|
||||
return err;
|
||||
goto st_gyro_power_off;
|
||||
|
||||
if (irq > 0) {
|
||||
err = st_sensors_allocate_trigger(indio_dev,
|
||||
|
@ -472,6 +474,8 @@ int st_gyro_common_probe(struct iio_dev *indio_dev)
|
|||
st_sensors_deallocate_trigger(indio_dev);
|
||||
st_gyro_probe_trigger_error:
|
||||
st_gyro_deallocate_ring(indio_dev);
|
||||
st_gyro_power_off:
|
||||
st_sensors_power_disable(indio_dev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* AFE4403 Heart Rate Monitors and Low-Cost Pulse Oximeters
|
||||
*
|
||||
* Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/
|
||||
* Copyright (C) 2015-2016 Texas Instruments Incorporated - http://www.ti.com/
|
||||
* Andrew F. Davis <afd@ti.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
@ -39,127 +39,90 @@
|
|||
#define AFE4403_TIAGAIN 0x20
|
||||
#define AFE4403_TIA_AMB_GAIN 0x21
|
||||
|
||||
/* AFE4403 GAIN register fields */
|
||||
#define AFE4403_TIAGAIN_RES_MASK GENMASK(2, 0)
|
||||
#define AFE4403_TIAGAIN_RES_SHIFT 0
|
||||
#define AFE4403_TIAGAIN_CAP_MASK GENMASK(7, 3)
|
||||
#define AFE4403_TIAGAIN_CAP_SHIFT 3
|
||||
enum afe4403_fields {
|
||||
/* Gains */
|
||||
F_RF_LED1, F_CF_LED1,
|
||||
F_RF_LED, F_CF_LED,
|
||||
|
||||
/* AFE4403 LEDCNTRL register fields */
|
||||
#define AFE440X_LEDCNTRL_LED1_MASK GENMASK(15, 8)
|
||||
#define AFE440X_LEDCNTRL_LED1_SHIFT 8
|
||||
#define AFE440X_LEDCNTRL_LED2_MASK GENMASK(7, 0)
|
||||
#define AFE440X_LEDCNTRL_LED2_SHIFT 0
|
||||
#define AFE440X_LEDCNTRL_LED_RANGE_MASK GENMASK(17, 16)
|
||||
#define AFE440X_LEDCNTRL_LED_RANGE_SHIFT 16
|
||||
/* LED Current */
|
||||
F_ILED1, F_ILED2,
|
||||
|
||||
/* AFE4403 CONTROL2 register fields */
|
||||
#define AFE440X_CONTROL2_PWR_DWN_TX BIT(2)
|
||||
#define AFE440X_CONTROL2_EN_SLOW_DIAG BIT(8)
|
||||
#define AFE440X_CONTROL2_DIAG_OUT_TRI BIT(10)
|
||||
#define AFE440X_CONTROL2_TX_BRDG_MOD BIT(11)
|
||||
#define AFE440X_CONTROL2_TX_REF_MASK GENMASK(18, 17)
|
||||
#define AFE440X_CONTROL2_TX_REF_SHIFT 17
|
||||
/* sentinel */
|
||||
F_MAX_FIELDS
|
||||
};
|
||||
|
||||
/* AFE4404 NULL fields */
|
||||
#define NULL_MASK 0
|
||||
#define NULL_SHIFT 0
|
||||
|
||||
/* AFE4403 LEDCNTRL values */
|
||||
#define AFE440X_LEDCNTRL_RANGE_TX_HALF 0x1
|
||||
#define AFE440X_LEDCNTRL_RANGE_TX_FULL 0x2
|
||||
#define AFE440X_LEDCNTRL_RANGE_TX_OFF 0x3
|
||||
|
||||
/* AFE4403 CONTROL2 values */
|
||||
#define AFE440X_CONTROL2_TX_REF_025 0x0
|
||||
#define AFE440X_CONTROL2_TX_REF_050 0x1
|
||||
#define AFE440X_CONTROL2_TX_REF_100 0x2
|
||||
#define AFE440X_CONTROL2_TX_REF_075 0x3
|
||||
|
||||
/* AFE4403 CONTROL3 values */
|
||||
#define AFE440X_CONTROL3_CLK_DIV_2 0x0
|
||||
#define AFE440X_CONTROL3_CLK_DIV_4 0x2
|
||||
#define AFE440X_CONTROL3_CLK_DIV_6 0x3
|
||||
#define AFE440X_CONTROL3_CLK_DIV_8 0x4
|
||||
#define AFE440X_CONTROL3_CLK_DIV_12 0x5
|
||||
#define AFE440X_CONTROL3_CLK_DIV_1 0x7
|
||||
|
||||
/* AFE4403 TIAGAIN_CAP values */
|
||||
#define AFE4403_TIAGAIN_CAP_5_P 0x0
|
||||
#define AFE4403_TIAGAIN_CAP_10_P 0x1
|
||||
#define AFE4403_TIAGAIN_CAP_20_P 0x2
|
||||
#define AFE4403_TIAGAIN_CAP_30_P 0x3
|
||||
#define AFE4403_TIAGAIN_CAP_55_P 0x8
|
||||
#define AFE4403_TIAGAIN_CAP_155_P 0x10
|
||||
|
||||
/* AFE4403 TIAGAIN_RES values */
|
||||
#define AFE4403_TIAGAIN_RES_500_K 0x0
|
||||
#define AFE4403_TIAGAIN_RES_250_K 0x1
|
||||
#define AFE4403_TIAGAIN_RES_100_K 0x2
|
||||
#define AFE4403_TIAGAIN_RES_50_K 0x3
|
||||
#define AFE4403_TIAGAIN_RES_25_K 0x4
|
||||
#define AFE4403_TIAGAIN_RES_10_K 0x5
|
||||
#define AFE4403_TIAGAIN_RES_1_M 0x6
|
||||
#define AFE4403_TIAGAIN_RES_NONE 0x7
|
||||
static const struct reg_field afe4403_reg_fields[] = {
|
||||
/* Gains */
|
||||
[F_RF_LED1] = REG_FIELD(AFE4403_TIAGAIN, 0, 2),
|
||||
[F_CF_LED1] = REG_FIELD(AFE4403_TIAGAIN, 3, 7),
|
||||
[F_RF_LED] = REG_FIELD(AFE4403_TIA_AMB_GAIN, 0, 2),
|
||||
[F_CF_LED] = REG_FIELD(AFE4403_TIA_AMB_GAIN, 3, 7),
|
||||
/* LED Current */
|
||||
[F_ILED1] = REG_FIELD(AFE440X_LEDCNTRL, 0, 7),
|
||||
[F_ILED2] = REG_FIELD(AFE440X_LEDCNTRL, 8, 15),
|
||||
};
|
||||
|
||||
/**
|
||||
* struct afe4403_data
|
||||
* @dev - Device structure
|
||||
* @spi - SPI device handle
|
||||
* @regmap - Register map of the device
|
||||
* @regulator - Pointer to the regulator for the IC
|
||||
* @trig - IIO trigger for this device
|
||||
* @irq - ADC_RDY line interrupt number
|
||||
* struct afe4403_data - AFE4403 device instance data
|
||||
* @dev: Device structure
|
||||
* @spi: SPI device handle
|
||||
* @regmap: Register map of the device
|
||||
* @fields: Register fields of the device
|
||||
* @regulator: Pointer to the regulator for the IC
|
||||
* @trig: IIO trigger for this device
|
||||
* @irq: ADC_RDY line interrupt number
|
||||
*/
|
||||
struct afe4403_data {
|
||||
struct device *dev;
|
||||
struct spi_device *spi;
|
||||
struct regmap *regmap;
|
||||
struct regmap_field *fields[F_MAX_FIELDS];
|
||||
struct regulator *regulator;
|
||||
struct iio_trigger *trig;
|
||||
int irq;
|
||||
};
|
||||
|
||||
enum afe4403_chan_id {
|
||||
LED2 = 1,
|
||||
ALED2,
|
||||
LED1,
|
||||
ALED1,
|
||||
LED2,
|
||||
ALED2,
|
||||
LED1_ALED1,
|
||||
LED2_ALED2,
|
||||
ILED1,
|
||||
ILED2,
|
||||
LED1_ALED1,
|
||||
};
|
||||
|
||||
static const struct afe440x_reg_info afe4403_reg_info[] = {
|
||||
[LED1] = AFE440X_REG_INFO(AFE440X_LED1VAL, 0, NULL),
|
||||
[ALED1] = AFE440X_REG_INFO(AFE440X_ALED1VAL, 0, NULL),
|
||||
[LED2] = AFE440X_REG_INFO(AFE440X_LED2VAL, 0, NULL),
|
||||
[ALED2] = AFE440X_REG_INFO(AFE440X_ALED2VAL, 0, NULL),
|
||||
[LED1_ALED1] = AFE440X_REG_INFO(AFE440X_LED1_ALED1VAL, 0, NULL),
|
||||
[LED2_ALED2] = AFE440X_REG_INFO(AFE440X_LED2_ALED2VAL, 0, NULL),
|
||||
[ILED1] = AFE440X_REG_INFO(AFE440X_LEDCNTRL, 0, AFE440X_LEDCNTRL_LED1),
|
||||
[ILED2] = AFE440X_REG_INFO(AFE440X_LEDCNTRL, 0, AFE440X_LEDCNTRL_LED2),
|
||||
static const unsigned int afe4403_channel_values[] = {
|
||||
[LED2] = AFE440X_LED2VAL,
|
||||
[ALED2] = AFE440X_ALED2VAL,
|
||||
[LED1] = AFE440X_LED1VAL,
|
||||
[ALED1] = AFE440X_ALED1VAL,
|
||||
[LED2_ALED2] = AFE440X_LED2_ALED2VAL,
|
||||
[LED1_ALED1] = AFE440X_LED1_ALED1VAL,
|
||||
};
|
||||
|
||||
static const unsigned int afe4403_channel_leds[] = {
|
||||
[LED2] = F_ILED2,
|
||||
[LED1] = F_ILED1,
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec afe4403_channels[] = {
|
||||
/* ADC values */
|
||||
AFE440X_INTENSITY_CHAN(LED1, "led1", 0),
|
||||
AFE440X_INTENSITY_CHAN(ALED1, "led1_ambient", 0),
|
||||
AFE440X_INTENSITY_CHAN(LED2, "led2", 0),
|
||||
AFE440X_INTENSITY_CHAN(ALED2, "led2_ambient", 0),
|
||||
AFE440X_INTENSITY_CHAN(LED1_ALED1, "led1-led1_ambient", 0),
|
||||
AFE440X_INTENSITY_CHAN(LED2_ALED2, "led2-led2_ambient", 0),
|
||||
AFE440X_INTENSITY_CHAN(LED2, 0),
|
||||
AFE440X_INTENSITY_CHAN(ALED2, 0),
|
||||
AFE440X_INTENSITY_CHAN(LED1, 0),
|
||||
AFE440X_INTENSITY_CHAN(ALED1, 0),
|
||||
AFE440X_INTENSITY_CHAN(LED2_ALED2, 0),
|
||||
AFE440X_INTENSITY_CHAN(LED1_ALED1, 0),
|
||||
/* LED current */
|
||||
AFE440X_CURRENT_CHAN(ILED1, "led1"),
|
||||
AFE440X_CURRENT_CHAN(ILED2, "led2"),
|
||||
AFE440X_CURRENT_CHAN(LED2),
|
||||
AFE440X_CURRENT_CHAN(LED1),
|
||||
};
|
||||
|
||||
static const struct afe440x_val_table afe4403_res_table[] = {
|
||||
{ 500000 }, { 250000 }, { 100000 }, { 50000 },
|
||||
{ 25000 }, { 10000 }, { 1000000 }, { 0 },
|
||||
};
|
||||
AFE440X_TABLE_ATTR(tia_resistance_available, afe4403_res_table);
|
||||
AFE440X_TABLE_ATTR(in_intensity_resistance_available, afe4403_res_table);
|
||||
|
||||
static const struct afe440x_val_table afe4403_cap_table[] = {
|
||||
{ 0, 5000 }, { 0, 10000 }, { 0, 20000 }, { 0, 25000 },
|
||||
|
@ -171,7 +134,7 @@ static const struct afe440x_val_table afe4403_cap_table[] = {
|
|||
{ 0, 205000 }, { 0, 210000 }, { 0, 220000 }, { 0, 225000 },
|
||||
{ 0, 230000 }, { 0, 235000 }, { 0, 245000 }, { 0, 250000 },
|
||||
};
|
||||
AFE440X_TABLE_ATTR(tia_capacitance_available, afe4403_cap_table);
|
||||
AFE440X_TABLE_ATTR(in_intensity_capacitance_available, afe4403_cap_table);
|
||||
|
||||
static ssize_t afe440x_show_register(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
|
@ -180,38 +143,21 @@ static ssize_t afe440x_show_register(struct device *dev,
|
|||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct afe4403_data *afe = iio_priv(indio_dev);
|
||||
struct afe440x_attr *afe440x_attr = to_afe440x_attr(attr);
|
||||
unsigned int reg_val, type;
|
||||
unsigned int reg_val;
|
||||
int vals[2];
|
||||
int ret, val_len;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(afe->regmap, afe440x_attr->reg, ®_val);
|
||||
ret = regmap_field_read(afe->fields[afe440x_attr->field], ®_val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
reg_val &= afe440x_attr->mask;
|
||||
reg_val >>= afe440x_attr->shift;
|
||||
|
||||
switch (afe440x_attr->type) {
|
||||
case SIMPLE:
|
||||
type = IIO_VAL_INT;
|
||||
val_len = 1;
|
||||
vals[0] = reg_val;
|
||||
break;
|
||||
case RESISTANCE:
|
||||
case CAPACITANCE:
|
||||
type = IIO_VAL_INT_PLUS_MICRO;
|
||||
val_len = 2;
|
||||
if (reg_val < afe440x_attr->table_size) {
|
||||
vals[0] = afe440x_attr->val_table[reg_val].integer;
|
||||
vals[1] = afe440x_attr->val_table[reg_val].fract;
|
||||
break;
|
||||
}
|
||||
if (reg_val >= afe440x_attr->table_size)
|
||||
return -EINVAL;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return iio_format_value(buf, type, val_len, vals);
|
||||
vals[0] = afe440x_attr->val_table[reg_val].integer;
|
||||
vals[1] = afe440x_attr->val_table[reg_val].fract;
|
||||
|
||||
return iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO, 2, vals);
|
||||
}
|
||||
|
||||
static ssize_t afe440x_store_register(struct device *dev,
|
||||
|
@ -227,48 +173,43 @@ static ssize_t afe440x_store_register(struct device *dev,
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
switch (afe440x_attr->type) {
|
||||
case SIMPLE:
|
||||
val = integer;
|
||||
break;
|
||||
case RESISTANCE:
|
||||
case CAPACITANCE:
|
||||
for (val = 0; val < afe440x_attr->table_size; val++)
|
||||
if (afe440x_attr->val_table[val].integer == integer &&
|
||||
afe440x_attr->val_table[val].fract == fract)
|
||||
break;
|
||||
if (val == afe440x_attr->table_size)
|
||||
return -EINVAL;
|
||||
break;
|
||||
default:
|
||||
for (val = 0; val < afe440x_attr->table_size; val++)
|
||||
if (afe440x_attr->val_table[val].integer == integer &&
|
||||
afe440x_attr->val_table[val].fract == fract)
|
||||
break;
|
||||
if (val == afe440x_attr->table_size)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = regmap_update_bits(afe->regmap, afe440x_attr->reg,
|
||||
afe440x_attr->mask,
|
||||
(val << afe440x_attr->shift));
|
||||
ret = regmap_field_write(afe->fields[afe440x_attr->field], val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static AFE440X_ATTR(tia_separate_en, AFE4403_TIAGAIN, AFE440X_TIAGAIN_ENSEPGAIN, SIMPLE, NULL, 0);
|
||||
static AFE440X_ATTR(in_intensity1_resistance, F_RF_LED, afe4403_res_table);
|
||||
static AFE440X_ATTR(in_intensity1_capacitance, F_CF_LED, afe4403_cap_table);
|
||||
|
||||
static AFE440X_ATTR(tia_resistance1, AFE4403_TIAGAIN, AFE4403_TIAGAIN_RES, RESISTANCE, afe4403_res_table, ARRAY_SIZE(afe4403_res_table));
|
||||
static AFE440X_ATTR(tia_capacitance1, AFE4403_TIAGAIN, AFE4403_TIAGAIN_CAP, CAPACITANCE, afe4403_cap_table, ARRAY_SIZE(afe4403_cap_table));
|
||||
static AFE440X_ATTR(in_intensity2_resistance, F_RF_LED, afe4403_res_table);
|
||||
static AFE440X_ATTR(in_intensity2_capacitance, F_CF_LED, afe4403_cap_table);
|
||||
|
||||
static AFE440X_ATTR(tia_resistance2, AFE4403_TIA_AMB_GAIN, AFE4403_TIAGAIN_RES, RESISTANCE, afe4403_res_table, ARRAY_SIZE(afe4403_res_table));
|
||||
static AFE440X_ATTR(tia_capacitance2, AFE4403_TIA_AMB_GAIN, AFE4403_TIAGAIN_RES, CAPACITANCE, afe4403_cap_table, ARRAY_SIZE(afe4403_cap_table));
|
||||
static AFE440X_ATTR(in_intensity3_resistance, F_RF_LED1, afe4403_res_table);
|
||||
static AFE440X_ATTR(in_intensity3_capacitance, F_CF_LED1, afe4403_cap_table);
|
||||
|
||||
static AFE440X_ATTR(in_intensity4_resistance, F_RF_LED1, afe4403_res_table);
|
||||
static AFE440X_ATTR(in_intensity4_capacitance, F_CF_LED1, afe4403_cap_table);
|
||||
|
||||
static struct attribute *afe440x_attributes[] = {
|
||||
&afe440x_attr_tia_separate_en.dev_attr.attr,
|
||||
&afe440x_attr_tia_resistance1.dev_attr.attr,
|
||||
&afe440x_attr_tia_capacitance1.dev_attr.attr,
|
||||
&afe440x_attr_tia_resistance2.dev_attr.attr,
|
||||
&afe440x_attr_tia_capacitance2.dev_attr.attr,
|
||||
&dev_attr_tia_resistance_available.attr,
|
||||
&dev_attr_tia_capacitance_available.attr,
|
||||
&dev_attr_in_intensity_resistance_available.attr,
|
||||
&dev_attr_in_intensity_capacitance_available.attr,
|
||||
&afe440x_attr_in_intensity1_resistance.dev_attr.attr,
|
||||
&afe440x_attr_in_intensity1_capacitance.dev_attr.attr,
|
||||
&afe440x_attr_in_intensity2_resistance.dev_attr.attr,
|
||||
&afe440x_attr_in_intensity2_capacitance.dev_attr.attr,
|
||||
&afe440x_attr_in_intensity3_resistance.dev_attr.attr,
|
||||
&afe440x_attr_in_intensity3_capacitance.dev_attr.attr,
|
||||
&afe440x_attr_in_intensity4_resistance.dev_attr.attr,
|
||||
&afe440x_attr_in_intensity4_capacitance.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
|
@ -309,35 +250,26 @@ static int afe4403_read_raw(struct iio_dev *indio_dev,
|
|||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct afe4403_data *afe = iio_priv(indio_dev);
|
||||
const struct afe440x_reg_info reg_info = afe4403_reg_info[chan->address];
|
||||
unsigned int reg = afe4403_channel_values[chan->address];
|
||||
unsigned int field = afe4403_channel_leds[chan->address];
|
||||
int ret;
|
||||
|
||||
switch (chan->type) {
|
||||
case IIO_INTENSITY:
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = afe4403_read(afe, reg_info.reg, val);
|
||||
ret = afe4403_read(afe, reg, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
ret = regmap_read(afe->regmap, reg_info.offreg,
|
||||
val);
|
||||
if (ret)
|
||||
return ret;
|
||||
*val &= reg_info.mask;
|
||||
*val >>= reg_info.shift;
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
break;
|
||||
case IIO_CURRENT:
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = regmap_read(afe->regmap, reg_info.reg, val);
|
||||
ret = regmap_field_read(afe->fields[field], val);
|
||||
if (ret)
|
||||
return ret;
|
||||
*val &= reg_info.mask;
|
||||
*val >>= reg_info.shift;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = 0;
|
||||
|
@ -357,25 +289,13 @@ static int afe4403_write_raw(struct iio_dev *indio_dev,
|
|||
int val, int val2, long mask)
|
||||
{
|
||||
struct afe4403_data *afe = iio_priv(indio_dev);
|
||||
const struct afe440x_reg_info reg_info = afe4403_reg_info[chan->address];
|
||||
unsigned int field = afe4403_channel_leds[chan->address];
|
||||
|
||||
switch (chan->type) {
|
||||
case IIO_INTENSITY:
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
return regmap_update_bits(afe->regmap,
|
||||
reg_info.offreg,
|
||||
reg_info.mask,
|
||||
(val << reg_info.shift));
|
||||
}
|
||||
break;
|
||||
case IIO_CURRENT:
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
return regmap_update_bits(afe->regmap,
|
||||
reg_info.reg,
|
||||
reg_info.mask,
|
||||
(val << reg_info.shift));
|
||||
return regmap_field_write(afe->fields[field], val);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
@ -410,7 +330,7 @@ static irqreturn_t afe4403_trigger_handler(int irq, void *private)
|
|||
for_each_set_bit(bit, indio_dev->active_scan_mask,
|
||||
indio_dev->masklength) {
|
||||
ret = spi_write_then_read(afe->spi,
|
||||
&afe4403_reg_info[bit].reg, 1,
|
||||
&afe4403_channel_values[bit], 1,
|
||||
rx, 3);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
@ -472,12 +392,8 @@ static const struct iio_trigger_ops afe4403_trigger_ops = {
|
|||
|
||||
static const struct reg_sequence afe4403_reg_sequences[] = {
|
||||
AFE4403_TIMING_PAIRS,
|
||||
{ AFE440X_CONTROL1, AFE440X_CONTROL1_TIMEREN | 0x000007},
|
||||
{ AFE4403_TIA_AMB_GAIN, AFE4403_TIAGAIN_RES_1_M },
|
||||
{ AFE440X_LEDCNTRL, (0x14 << AFE440X_LEDCNTRL_LED1_SHIFT) |
|
||||
(0x14 << AFE440X_LEDCNTRL_LED2_SHIFT) },
|
||||
{ AFE440X_CONTROL2, AFE440X_CONTROL2_TX_REF_050 <<
|
||||
AFE440X_CONTROL2_TX_REF_SHIFT },
|
||||
{ AFE440X_CONTROL1, AFE440X_CONTROL1_TIMEREN },
|
||||
{ AFE4403_TIAGAIN, AFE440X_TIAGAIN_ENSEPGAIN },
|
||||
};
|
||||
|
||||
static const struct regmap_range afe4403_yes_ranges[] = {
|
||||
|
@ -498,13 +414,11 @@ static const struct regmap_config afe4403_regmap_config = {
|
|||
.volatile_table = &afe4403_volatile_table,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id afe4403_of_match[] = {
|
||||
{ .compatible = "ti,afe4403", },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, afe4403_of_match);
|
||||
#endif
|
||||
|
||||
static int __maybe_unused afe4403_suspend(struct device *dev)
|
||||
{
|
||||
|
@ -553,7 +467,7 @@ static int afe4403_probe(struct spi_device *spi)
|
|||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct afe4403_data *afe;
|
||||
int ret;
|
||||
int i, ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*afe));
|
||||
if (!indio_dev)
|
||||
|
@ -572,6 +486,15 @@ static int afe4403_probe(struct spi_device *spi)
|
|||
return PTR_ERR(afe->regmap);
|
||||
}
|
||||
|
||||
for (i = 0; i < F_MAX_FIELDS; i++) {
|
||||
afe->fields[i] = devm_regmap_field_alloc(afe->dev, afe->regmap,
|
||||
afe4403_reg_fields[i]);
|
||||
if (IS_ERR(afe->fields[i])) {
|
||||
dev_err(afe->dev, "Unable to allocate regmap fields\n");
|
||||
return PTR_ERR(afe->fields[i]);
|
||||
}
|
||||
}
|
||||
|
||||
afe->regulator = devm_regulator_get(afe->dev, "tx_sup");
|
||||
if (IS_ERR(afe->regulator)) {
|
||||
dev_err(afe->dev, "Unable to get regulator\n");
|
||||
|
@ -694,7 +617,7 @@ MODULE_DEVICE_TABLE(spi, afe4403_ids);
|
|||
static struct spi_driver afe4403_spi_driver = {
|
||||
.driver = {
|
||||
.name = AFE4403_DRIVER_NAME,
|
||||
.of_match_table = of_match_ptr(afe4403_of_match),
|
||||
.of_match_table = afe4403_of_match,
|
||||
.pm = &afe4403_pm_ops,
|
||||
},
|
||||
.probe = afe4403_probe,
|
||||
|
@ -704,5 +627,5 @@ static struct spi_driver afe4403_spi_driver = {
|
|||
module_spi_driver(afe4403_spi_driver);
|
||||
|
||||
MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>");
|
||||
MODULE_DESCRIPTION("TI AFE4403 Heart Rate and Pulse Oximeter");
|
||||
MODULE_DESCRIPTION("TI AFE4403 Heart Rate Monitor and Pulse Oximeter AFE");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* AFE4404 Heart Rate Monitors and Low-Cost Pulse Oximeters
|
||||
*
|
||||
* Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/
|
||||
* Copyright (C) 2015-2016 Texas Instruments Incorporated - http://www.ti.com/
|
||||
* Andrew F. Davis <afd@ti.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
@ -48,118 +48,102 @@
|
|||
#define AFE4404_AVG_LED2_ALED2VAL 0x3f
|
||||
#define AFE4404_AVG_LED1_ALED1VAL 0x40
|
||||
|
||||
/* AFE4404 GAIN register fields */
|
||||
#define AFE4404_TIA_GAIN_RES_MASK GENMASK(2, 0)
|
||||
#define AFE4404_TIA_GAIN_RES_SHIFT 0
|
||||
#define AFE4404_TIA_GAIN_CAP_MASK GENMASK(5, 3)
|
||||
#define AFE4404_TIA_GAIN_CAP_SHIFT 3
|
||||
|
||||
/* AFE4404 LEDCNTRL register fields */
|
||||
#define AFE4404_LEDCNTRL_ILED1_MASK GENMASK(5, 0)
|
||||
#define AFE4404_LEDCNTRL_ILED1_SHIFT 0
|
||||
#define AFE4404_LEDCNTRL_ILED2_MASK GENMASK(11, 6)
|
||||
#define AFE4404_LEDCNTRL_ILED2_SHIFT 6
|
||||
#define AFE4404_LEDCNTRL_ILED3_MASK GENMASK(17, 12)
|
||||
#define AFE4404_LEDCNTRL_ILED3_SHIFT 12
|
||||
|
||||
/* AFE4404 CONTROL2 register fields */
|
||||
#define AFE440X_CONTROL2_ILED_2X_MASK BIT(17)
|
||||
#define AFE440X_CONTROL2_ILED_2X_SHIFT 17
|
||||
#define AFE440X_CONTROL2_OSC_ENABLE BIT(9)
|
||||
|
||||
/* AFE4404 CONTROL3 register fields */
|
||||
#define AFE440X_CONTROL3_OSC_ENABLE BIT(9)
|
||||
enum afe4404_fields {
|
||||
/* Gains */
|
||||
F_TIA_GAIN_SEP, F_TIA_CF_SEP,
|
||||
F_TIA_GAIN, TIA_CF,
|
||||
|
||||
/* AFE4404 OFFDAC register current fields */
|
||||
#define AFE4404_OFFDAC_CURR_LED1_MASK GENMASK(9, 5)
|
||||
#define AFE4404_OFFDAC_CURR_LED1_SHIFT 5
|
||||
#define AFE4404_OFFDAC_CURR_LED2_MASK GENMASK(19, 15)
|
||||
#define AFE4404_OFFDAC_CURR_LED2_SHIFT 15
|
||||
#define AFE4404_OFFDAC_CURR_LED3_MASK GENMASK(4, 0)
|
||||
#define AFE4404_OFFDAC_CURR_LED3_SHIFT 0
|
||||
#define AFE4404_OFFDAC_CURR_ALED1_MASK GENMASK(14, 10)
|
||||
#define AFE4404_OFFDAC_CURR_ALED1_SHIFT 10
|
||||
#define AFE4404_OFFDAC_CURR_ALED2_MASK GENMASK(4, 0)
|
||||
#define AFE4404_OFFDAC_CURR_ALED2_SHIFT 0
|
||||
/* LED Current */
|
||||
F_ILED1, F_ILED2, F_ILED3,
|
||||
|
||||
/* AFE4404 NULL fields */
|
||||
#define NULL_MASK 0
|
||||
#define NULL_SHIFT 0
|
||||
/* Offset DAC */
|
||||
F_OFFDAC_AMB2, F_OFFDAC_LED1, F_OFFDAC_AMB1, F_OFFDAC_LED2,
|
||||
|
||||
/* AFE4404 TIA_GAIN_CAP values */
|
||||
#define AFE4404_TIA_GAIN_CAP_5_P 0x0
|
||||
#define AFE4404_TIA_GAIN_CAP_2_5_P 0x1
|
||||
#define AFE4404_TIA_GAIN_CAP_10_P 0x2
|
||||
#define AFE4404_TIA_GAIN_CAP_7_5_P 0x3
|
||||
#define AFE4404_TIA_GAIN_CAP_20_P 0x4
|
||||
#define AFE4404_TIA_GAIN_CAP_17_5_P 0x5
|
||||
#define AFE4404_TIA_GAIN_CAP_25_P 0x6
|
||||
#define AFE4404_TIA_GAIN_CAP_22_5_P 0x7
|
||||
/* sentinel */
|
||||
F_MAX_FIELDS
|
||||
};
|
||||
|
||||
/* AFE4404 TIA_GAIN_RES values */
|
||||
#define AFE4404_TIA_GAIN_RES_500_K 0x0
|
||||
#define AFE4404_TIA_GAIN_RES_250_K 0x1
|
||||
#define AFE4404_TIA_GAIN_RES_100_K 0x2
|
||||
#define AFE4404_TIA_GAIN_RES_50_K 0x3
|
||||
#define AFE4404_TIA_GAIN_RES_25_K 0x4
|
||||
#define AFE4404_TIA_GAIN_RES_10_K 0x5
|
||||
#define AFE4404_TIA_GAIN_RES_1_M 0x6
|
||||
#define AFE4404_TIA_GAIN_RES_2_M 0x7
|
||||
static const struct reg_field afe4404_reg_fields[] = {
|
||||
/* Gains */
|
||||
[F_TIA_GAIN_SEP] = REG_FIELD(AFE4404_TIA_GAIN_SEP, 0, 2),
|
||||
[F_TIA_CF_SEP] = REG_FIELD(AFE4404_TIA_GAIN_SEP, 3, 5),
|
||||
[F_TIA_GAIN] = REG_FIELD(AFE4404_TIA_GAIN, 0, 2),
|
||||
[TIA_CF] = REG_FIELD(AFE4404_TIA_GAIN, 3, 5),
|
||||
/* LED Current */
|
||||
[F_ILED1] = REG_FIELD(AFE440X_LEDCNTRL, 0, 5),
|
||||
[F_ILED2] = REG_FIELD(AFE440X_LEDCNTRL, 6, 11),
|
||||
[F_ILED3] = REG_FIELD(AFE440X_LEDCNTRL, 12, 17),
|
||||
/* Offset DAC */
|
||||
[F_OFFDAC_AMB2] = REG_FIELD(AFE4404_OFFDAC, 0, 4),
|
||||
[F_OFFDAC_LED1] = REG_FIELD(AFE4404_OFFDAC, 5, 9),
|
||||
[F_OFFDAC_AMB1] = REG_FIELD(AFE4404_OFFDAC, 10, 14),
|
||||
[F_OFFDAC_LED2] = REG_FIELD(AFE4404_OFFDAC, 15, 19),
|
||||
};
|
||||
|
||||
/**
|
||||
* struct afe4404_data
|
||||
* @dev - Device structure
|
||||
* @regmap - Register map of the device
|
||||
* @regulator - Pointer to the regulator for the IC
|
||||
* @trig - IIO trigger for this device
|
||||
* @irq - ADC_RDY line interrupt number
|
||||
* struct afe4404_data - AFE4404 device instance data
|
||||
* @dev: Device structure
|
||||
* @regmap: Register map of the device
|
||||
* @fields: Register fields of the device
|
||||
* @regulator: Pointer to the regulator for the IC
|
||||
* @trig: IIO trigger for this device
|
||||
* @irq: ADC_RDY line interrupt number
|
||||
*/
|
||||
struct afe4404_data {
|
||||
struct device *dev;
|
||||
struct regmap *regmap;
|
||||
struct regmap_field *fields[F_MAX_FIELDS];
|
||||
struct regulator *regulator;
|
||||
struct iio_trigger *trig;
|
||||
int irq;
|
||||
};
|
||||
|
||||
enum afe4404_chan_id {
|
||||
LED2 = 1,
|
||||
ALED2,
|
||||
LED1,
|
||||
ALED1,
|
||||
LED2,
|
||||
ALED2,
|
||||
LED3,
|
||||
LED1_ALED1,
|
||||
LED2_ALED2,
|
||||
ILED1,
|
||||
ILED2,
|
||||
ILED3,
|
||||
LED1_ALED1,
|
||||
};
|
||||
|
||||
static const struct afe440x_reg_info afe4404_reg_info[] = {
|
||||
[LED1] = AFE440X_REG_INFO(AFE440X_LED1VAL, AFE4404_OFFDAC, AFE4404_OFFDAC_CURR_LED1),
|
||||
[ALED1] = AFE440X_REG_INFO(AFE440X_ALED1VAL, AFE4404_OFFDAC, AFE4404_OFFDAC_CURR_ALED1),
|
||||
[LED2] = AFE440X_REG_INFO(AFE440X_LED2VAL, AFE4404_OFFDAC, AFE4404_OFFDAC_CURR_LED2),
|
||||
[ALED2] = AFE440X_REG_INFO(AFE440X_ALED2VAL, AFE4404_OFFDAC, AFE4404_OFFDAC_CURR_ALED2),
|
||||
[LED3] = AFE440X_REG_INFO(AFE440X_ALED2VAL, 0, NULL),
|
||||
[LED1_ALED1] = AFE440X_REG_INFO(AFE440X_LED1_ALED1VAL, 0, NULL),
|
||||
[LED2_ALED2] = AFE440X_REG_INFO(AFE440X_LED2_ALED2VAL, 0, NULL),
|
||||
[ILED1] = AFE440X_REG_INFO(AFE440X_LEDCNTRL, 0, AFE4404_LEDCNTRL_ILED1),
|
||||
[ILED2] = AFE440X_REG_INFO(AFE440X_LEDCNTRL, 0, AFE4404_LEDCNTRL_ILED2),
|
||||
[ILED3] = AFE440X_REG_INFO(AFE440X_LEDCNTRL, 0, AFE4404_LEDCNTRL_ILED3),
|
||||
static const unsigned int afe4404_channel_values[] = {
|
||||
[LED2] = AFE440X_LED2VAL,
|
||||
[ALED2] = AFE440X_ALED2VAL,
|
||||
[LED1] = AFE440X_LED1VAL,
|
||||
[ALED1] = AFE440X_ALED1VAL,
|
||||
[LED2_ALED2] = AFE440X_LED2_ALED2VAL,
|
||||
[LED1_ALED1] = AFE440X_LED1_ALED1VAL,
|
||||
};
|
||||
|
||||
static const unsigned int afe4404_channel_leds[] = {
|
||||
[LED2] = F_ILED2,
|
||||
[ALED2] = F_ILED3,
|
||||
[LED1] = F_ILED1,
|
||||
};
|
||||
|
||||
static const unsigned int afe4404_channel_offdacs[] = {
|
||||
[LED2] = F_OFFDAC_LED2,
|
||||
[ALED2] = F_OFFDAC_AMB2,
|
||||
[LED1] = F_OFFDAC_LED1,
|
||||
[ALED1] = F_OFFDAC_AMB1,
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec afe4404_channels[] = {
|
||||
/* ADC values */
|
||||
AFE440X_INTENSITY_CHAN(LED1, "led1", BIT(IIO_CHAN_INFO_OFFSET)),
|
||||
AFE440X_INTENSITY_CHAN(ALED1, "led1_ambient", BIT(IIO_CHAN_INFO_OFFSET)),
|
||||
AFE440X_INTENSITY_CHAN(LED2, "led2", BIT(IIO_CHAN_INFO_OFFSET)),
|
||||
AFE440X_INTENSITY_CHAN(ALED2, "led2_ambient", BIT(IIO_CHAN_INFO_OFFSET)),
|
||||
AFE440X_INTENSITY_CHAN(LED3, "led3", BIT(IIO_CHAN_INFO_OFFSET)),
|
||||
AFE440X_INTENSITY_CHAN(LED1_ALED1, "led1-led1_ambient", 0),
|
||||
AFE440X_INTENSITY_CHAN(LED2_ALED2, "led2-led2_ambient", 0),
|
||||
AFE440X_INTENSITY_CHAN(LED2, BIT(IIO_CHAN_INFO_OFFSET)),
|
||||
AFE440X_INTENSITY_CHAN(ALED2, BIT(IIO_CHAN_INFO_OFFSET)),
|
||||
AFE440X_INTENSITY_CHAN(LED1, BIT(IIO_CHAN_INFO_OFFSET)),
|
||||
AFE440X_INTENSITY_CHAN(ALED1, BIT(IIO_CHAN_INFO_OFFSET)),
|
||||
AFE440X_INTENSITY_CHAN(LED2_ALED2, 0),
|
||||
AFE440X_INTENSITY_CHAN(LED1_ALED1, 0),
|
||||
/* LED current */
|
||||
AFE440X_CURRENT_CHAN(ILED1, "led1"),
|
||||
AFE440X_CURRENT_CHAN(ILED2, "led2"),
|
||||
AFE440X_CURRENT_CHAN(ILED3, "led3"),
|
||||
AFE440X_CURRENT_CHAN(LED2),
|
||||
AFE440X_CURRENT_CHAN(ALED2),
|
||||
AFE440X_CURRENT_CHAN(LED1),
|
||||
};
|
||||
|
||||
static const struct afe440x_val_table afe4404_res_table[] = {
|
||||
|
@ -172,7 +156,7 @@ static const struct afe440x_val_table afe4404_res_table[] = {
|
|||
{ .integer = 1000000, .fract = 0 },
|
||||
{ .integer = 2000000, .fract = 0 },
|
||||
};
|
||||
AFE440X_TABLE_ATTR(tia_resistance_available, afe4404_res_table);
|
||||
AFE440X_TABLE_ATTR(in_intensity_resistance_available, afe4404_res_table);
|
||||
|
||||
static const struct afe440x_val_table afe4404_cap_table[] = {
|
||||
{ .integer = 0, .fract = 5000 },
|
||||
|
@ -184,7 +168,7 @@ static const struct afe440x_val_table afe4404_cap_table[] = {
|
|||
{ .integer = 0, .fract = 25000 },
|
||||
{ .integer = 0, .fract = 22500 },
|
||||
};
|
||||
AFE440X_TABLE_ATTR(tia_capacitance_available, afe4404_cap_table);
|
||||
AFE440X_TABLE_ATTR(in_intensity_capacitance_available, afe4404_cap_table);
|
||||
|
||||
static ssize_t afe440x_show_register(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
|
@ -193,38 +177,21 @@ static ssize_t afe440x_show_register(struct device *dev,
|
|||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct afe4404_data *afe = iio_priv(indio_dev);
|
||||
struct afe440x_attr *afe440x_attr = to_afe440x_attr(attr);
|
||||
unsigned int reg_val, type;
|
||||
unsigned int reg_val;
|
||||
int vals[2];
|
||||
int ret, val_len;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(afe->regmap, afe440x_attr->reg, ®_val);
|
||||
ret = regmap_field_read(afe->fields[afe440x_attr->field], ®_val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
reg_val &= afe440x_attr->mask;
|
||||
reg_val >>= afe440x_attr->shift;
|
||||
|
||||
switch (afe440x_attr->type) {
|
||||
case SIMPLE:
|
||||
type = IIO_VAL_INT;
|
||||
val_len = 1;
|
||||
vals[0] = reg_val;
|
||||
break;
|
||||
case RESISTANCE:
|
||||
case CAPACITANCE:
|
||||
type = IIO_VAL_INT_PLUS_MICRO;
|
||||
val_len = 2;
|
||||
if (reg_val < afe440x_attr->table_size) {
|
||||
vals[0] = afe440x_attr->val_table[reg_val].integer;
|
||||
vals[1] = afe440x_attr->val_table[reg_val].fract;
|
||||
break;
|
||||
}
|
||||
if (reg_val >= afe440x_attr->table_size)
|
||||
return -EINVAL;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return iio_format_value(buf, type, val_len, vals);
|
||||
vals[0] = afe440x_attr->val_table[reg_val].integer;
|
||||
vals[1] = afe440x_attr->val_table[reg_val].fract;
|
||||
|
||||
return iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO, 2, vals);
|
||||
}
|
||||
|
||||
static ssize_t afe440x_store_register(struct device *dev,
|
||||
|
@ -240,48 +207,43 @@ static ssize_t afe440x_store_register(struct device *dev,
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
switch (afe440x_attr->type) {
|
||||
case SIMPLE:
|
||||
val = integer;
|
||||
break;
|
||||
case RESISTANCE:
|
||||
case CAPACITANCE:
|
||||
for (val = 0; val < afe440x_attr->table_size; val++)
|
||||
if (afe440x_attr->val_table[val].integer == integer &&
|
||||
afe440x_attr->val_table[val].fract == fract)
|
||||
break;
|
||||
if (val == afe440x_attr->table_size)
|
||||
return -EINVAL;
|
||||
break;
|
||||
default:
|
||||
for (val = 0; val < afe440x_attr->table_size; val++)
|
||||
if (afe440x_attr->val_table[val].integer == integer &&
|
||||
afe440x_attr->val_table[val].fract == fract)
|
||||
break;
|
||||
if (val == afe440x_attr->table_size)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = regmap_update_bits(afe->regmap, afe440x_attr->reg,
|
||||
afe440x_attr->mask,
|
||||
(val << afe440x_attr->shift));
|
||||
ret = regmap_field_write(afe->fields[afe440x_attr->field], val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static AFE440X_ATTR(tia_separate_en, AFE4404_TIA_GAIN_SEP, AFE440X_TIAGAIN_ENSEPGAIN, SIMPLE, NULL, 0);
|
||||
static AFE440X_ATTR(in_intensity1_resistance, F_TIA_GAIN_SEP, afe4404_res_table);
|
||||
static AFE440X_ATTR(in_intensity1_capacitance, F_TIA_CF_SEP, afe4404_cap_table);
|
||||
|
||||
static AFE440X_ATTR(tia_resistance1, AFE4404_TIA_GAIN, AFE4404_TIA_GAIN_RES, RESISTANCE, afe4404_res_table, ARRAY_SIZE(afe4404_res_table));
|
||||
static AFE440X_ATTR(tia_capacitance1, AFE4404_TIA_GAIN, AFE4404_TIA_GAIN_CAP, CAPACITANCE, afe4404_cap_table, ARRAY_SIZE(afe4404_cap_table));
|
||||
static AFE440X_ATTR(in_intensity2_resistance, F_TIA_GAIN_SEP, afe4404_res_table);
|
||||
static AFE440X_ATTR(in_intensity2_capacitance, F_TIA_CF_SEP, afe4404_cap_table);
|
||||
|
||||
static AFE440X_ATTR(tia_resistance2, AFE4404_TIA_GAIN_SEP, AFE4404_TIA_GAIN_RES, RESISTANCE, afe4404_res_table, ARRAY_SIZE(afe4404_res_table));
|
||||
static AFE440X_ATTR(tia_capacitance2, AFE4404_TIA_GAIN_SEP, AFE4404_TIA_GAIN_CAP, CAPACITANCE, afe4404_cap_table, ARRAY_SIZE(afe4404_cap_table));
|
||||
static AFE440X_ATTR(in_intensity3_resistance, F_TIA_GAIN, afe4404_res_table);
|
||||
static AFE440X_ATTR(in_intensity3_capacitance, TIA_CF, afe4404_cap_table);
|
||||
|
||||
static AFE440X_ATTR(in_intensity4_resistance, F_TIA_GAIN, afe4404_res_table);
|
||||
static AFE440X_ATTR(in_intensity4_capacitance, TIA_CF, afe4404_cap_table);
|
||||
|
||||
static struct attribute *afe440x_attributes[] = {
|
||||
&afe440x_attr_tia_separate_en.dev_attr.attr,
|
||||
&afe440x_attr_tia_resistance1.dev_attr.attr,
|
||||
&afe440x_attr_tia_capacitance1.dev_attr.attr,
|
||||
&afe440x_attr_tia_resistance2.dev_attr.attr,
|
||||
&afe440x_attr_tia_capacitance2.dev_attr.attr,
|
||||
&dev_attr_tia_resistance_available.attr,
|
||||
&dev_attr_tia_capacitance_available.attr,
|
||||
&dev_attr_in_intensity_resistance_available.attr,
|
||||
&dev_attr_in_intensity_capacitance_available.attr,
|
||||
&afe440x_attr_in_intensity1_resistance.dev_attr.attr,
|
||||
&afe440x_attr_in_intensity1_capacitance.dev_attr.attr,
|
||||
&afe440x_attr_in_intensity2_resistance.dev_attr.attr,
|
||||
&afe440x_attr_in_intensity2_capacitance.dev_attr.attr,
|
||||
&afe440x_attr_in_intensity3_resistance.dev_attr.attr,
|
||||
&afe440x_attr_in_intensity3_capacitance.dev_attr.attr,
|
||||
&afe440x_attr_in_intensity4_resistance.dev_attr.attr,
|
||||
&afe440x_attr_in_intensity4_capacitance.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
|
@ -294,35 +256,32 @@ static int afe4404_read_raw(struct iio_dev *indio_dev,
|
|||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct afe4404_data *afe = iio_priv(indio_dev);
|
||||
const struct afe440x_reg_info reg_info = afe4404_reg_info[chan->address];
|
||||
unsigned int value_reg = afe4404_channel_values[chan->address];
|
||||
unsigned int led_field = afe4404_channel_leds[chan->address];
|
||||
unsigned int offdac_field = afe4404_channel_offdacs[chan->address];
|
||||
int ret;
|
||||
|
||||
switch (chan->type) {
|
||||
case IIO_INTENSITY:
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = regmap_read(afe->regmap, reg_info.reg, val);
|
||||
ret = regmap_read(afe->regmap, value_reg, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
ret = regmap_read(afe->regmap, reg_info.offreg,
|
||||
val);
|
||||
ret = regmap_field_read(afe->fields[offdac_field], val);
|
||||
if (ret)
|
||||
return ret;
|
||||
*val &= reg_info.mask;
|
||||
*val >>= reg_info.shift;
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
break;
|
||||
case IIO_CURRENT:
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = regmap_read(afe->regmap, reg_info.reg, val);
|
||||
ret = regmap_field_read(afe->fields[led_field], val);
|
||||
if (ret)
|
||||
return ret;
|
||||
*val &= reg_info.mask;
|
||||
*val >>= reg_info.shift;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = 0;
|
||||
|
@ -342,25 +301,20 @@ static int afe4404_write_raw(struct iio_dev *indio_dev,
|
|||
int val, int val2, long mask)
|
||||
{
|
||||
struct afe4404_data *afe = iio_priv(indio_dev);
|
||||
const struct afe440x_reg_info reg_info = afe4404_reg_info[chan->address];
|
||||
unsigned int led_field = afe4404_channel_leds[chan->address];
|
||||
unsigned int offdac_field = afe4404_channel_offdacs[chan->address];
|
||||
|
||||
switch (chan->type) {
|
||||
case IIO_INTENSITY:
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
return regmap_update_bits(afe->regmap,
|
||||
reg_info.offreg,
|
||||
reg_info.mask,
|
||||
(val << reg_info.shift));
|
||||
return regmap_field_write(afe->fields[offdac_field], val);
|
||||
}
|
||||
break;
|
||||
case IIO_CURRENT:
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
return regmap_update_bits(afe->regmap,
|
||||
reg_info.reg,
|
||||
reg_info.mask,
|
||||
(val << reg_info.shift));
|
||||
return regmap_field_write(afe->fields[led_field], val);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
@ -387,7 +341,7 @@ static irqreturn_t afe4404_trigger_handler(int irq, void *private)
|
|||
|
||||
for_each_set_bit(bit, indio_dev->active_scan_mask,
|
||||
indio_dev->masklength) {
|
||||
ret = regmap_read(afe->regmap, afe4404_reg_info[bit].reg,
|
||||
ret = regmap_read(afe->regmap, afe4404_channel_values[bit],
|
||||
&buffer[i++]);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
@ -443,11 +397,8 @@ static const struct iio_trigger_ops afe4404_trigger_ops = {
|
|||
static const struct reg_sequence afe4404_reg_sequences[] = {
|
||||
AFE4404_TIMING_PAIRS,
|
||||
{ AFE440X_CONTROL1, AFE440X_CONTROL1_TIMEREN },
|
||||
{ AFE4404_TIA_GAIN, AFE4404_TIA_GAIN_RES_50_K },
|
||||
{ AFE440X_LEDCNTRL, (0xf << AFE4404_LEDCNTRL_ILED1_SHIFT) |
|
||||
(0x3 << AFE4404_LEDCNTRL_ILED2_SHIFT) |
|
||||
(0x3 << AFE4404_LEDCNTRL_ILED3_SHIFT) },
|
||||
{ AFE440X_CONTROL2, AFE440X_CONTROL3_OSC_ENABLE },
|
||||
{ AFE4404_TIA_GAIN_SEP, AFE440X_TIAGAIN_ENSEPGAIN },
|
||||
{ AFE440X_CONTROL2, AFE440X_CONTROL2_OSC_ENABLE },
|
||||
};
|
||||
|
||||
static const struct regmap_range afe4404_yes_ranges[] = {
|
||||
|
@ -469,13 +420,11 @@ static const struct regmap_config afe4404_regmap_config = {
|
|||
.volatile_table = &afe4404_volatile_table,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id afe4404_of_match[] = {
|
||||
{ .compatible = "ti,afe4404", },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, afe4404_of_match);
|
||||
#endif
|
||||
|
||||
static int __maybe_unused afe4404_suspend(struct device *dev)
|
||||
{
|
||||
|
@ -525,7 +474,7 @@ static int afe4404_probe(struct i2c_client *client,
|
|||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct afe4404_data *afe;
|
||||
int ret;
|
||||
int i, ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*afe));
|
||||
if (!indio_dev)
|
||||
|
@ -543,6 +492,15 @@ static int afe4404_probe(struct i2c_client *client,
|
|||
return PTR_ERR(afe->regmap);
|
||||
}
|
||||
|
||||
for (i = 0; i < F_MAX_FIELDS; i++) {
|
||||
afe->fields[i] = devm_regmap_field_alloc(afe->dev, afe->regmap,
|
||||
afe4404_reg_fields[i]);
|
||||
if (IS_ERR(afe->fields[i])) {
|
||||
dev_err(afe->dev, "Unable to allocate regmap fields\n");
|
||||
return PTR_ERR(afe->fields[i]);
|
||||
}
|
||||
}
|
||||
|
||||
afe->regulator = devm_regulator_get(afe->dev, "tx_sup");
|
||||
if (IS_ERR(afe->regulator)) {
|
||||
dev_err(afe->dev, "Unable to get regulator\n");
|
||||
|
@ -665,7 +623,7 @@ MODULE_DEVICE_TABLE(i2c, afe4404_ids);
|
|||
static struct i2c_driver afe4404_i2c_driver = {
|
||||
.driver = {
|
||||
.name = AFE4404_DRIVER_NAME,
|
||||
.of_match_table = of_match_ptr(afe4404_of_match),
|
||||
.of_match_table = afe4404_of_match,
|
||||
.pm = &afe4404_pm_ops,
|
||||
},
|
||||
.probe = afe4404_probe,
|
||||
|
@ -675,5 +633,5 @@ static struct i2c_driver afe4404_i2c_driver = {
|
|||
module_i2c_driver(afe4404_i2c_driver);
|
||||
|
||||
MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>");
|
||||
MODULE_DESCRIPTION("TI AFE4404 Heart Rate and Pulse Oximeter");
|
||||
MODULE_DESCRIPTION("TI AFE4404 Heart Rate Monitor and Pulse Oximeter AFE");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
|
|
@ -71,8 +71,7 @@
|
|||
#define AFE440X_CONTROL1_TIMEREN BIT(8)
|
||||
|
||||
/* TIAGAIN register fields */
|
||||
#define AFE440X_TIAGAIN_ENSEPGAIN_MASK BIT(15)
|
||||
#define AFE440X_TIAGAIN_ENSEPGAIN_SHIFT 15
|
||||
#define AFE440X_TIAGAIN_ENSEPGAIN BIT(15)
|
||||
|
||||
/* CONTROL2 register fields */
|
||||
#define AFE440X_CONTROL2_PDN_AFE BIT(0)
|
||||
|
@ -89,22 +88,7 @@
|
|||
#define AFE440X_CONTROL0_WRITE 0x0
|
||||
#define AFE440X_CONTROL0_READ 0x1
|
||||
|
||||
struct afe440x_reg_info {
|
||||
unsigned int reg;
|
||||
unsigned int offreg;
|
||||
unsigned int shift;
|
||||
unsigned int mask;
|
||||
};
|
||||
|
||||
#define AFE440X_REG_INFO(_reg, _offreg, _sm) \
|
||||
{ \
|
||||
.reg = _reg, \
|
||||
.offreg = _offreg, \
|
||||
.shift = _sm ## _SHIFT, \
|
||||
.mask = _sm ## _MASK, \
|
||||
}
|
||||
|
||||
#define AFE440X_INTENSITY_CHAN(_index, _name, _mask) \
|
||||
#define AFE440X_INTENSITY_CHAN(_index, _mask) \
|
||||
{ \
|
||||
.type = IIO_INTENSITY, \
|
||||
.channel = _index, \
|
||||
|
@ -116,29 +100,23 @@ struct afe440x_reg_info {
|
|||
.storagebits = 32, \
|
||||
.endianness = IIO_CPU, \
|
||||
}, \
|
||||
.extend_name = _name, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
_mask, \
|
||||
.indexed = true, \
|
||||
}
|
||||
|
||||
#define AFE440X_CURRENT_CHAN(_index, _name) \
|
||||
#define AFE440X_CURRENT_CHAN(_index) \
|
||||
{ \
|
||||
.type = IIO_CURRENT, \
|
||||
.channel = _index, \
|
||||
.address = _index, \
|
||||
.scan_index = _index, \
|
||||
.extend_name = _name, \
|
||||
.scan_index = -1, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.indexed = true, \
|
||||
.output = true, \
|
||||
}
|
||||
|
||||
enum afe440x_reg_type {
|
||||
SIMPLE,
|
||||
RESISTANCE,
|
||||
CAPACITANCE,
|
||||
};
|
||||
|
||||
struct afe440x_val_table {
|
||||
int integer;
|
||||
int fract;
|
||||
|
@ -164,10 +142,7 @@ static DEVICE_ATTR_RO(_name)
|
|||
|
||||
struct afe440x_attr {
|
||||
struct device_attribute dev_attr;
|
||||
unsigned int reg;
|
||||
unsigned int shift;
|
||||
unsigned int mask;
|
||||
enum afe440x_reg_type type;
|
||||
unsigned int field;
|
||||
const struct afe440x_val_table *val_table;
|
||||
unsigned int table_size;
|
||||
};
|
||||
|
@ -175,17 +150,14 @@ struct afe440x_attr {
|
|||
#define to_afe440x_attr(_dev_attr) \
|
||||
container_of(_dev_attr, struct afe440x_attr, dev_attr)
|
||||
|
||||
#define AFE440X_ATTR(_name, _reg, _field, _type, _table, _size) \
|
||||
#define AFE440X_ATTR(_name, _field, _table) \
|
||||
struct afe440x_attr afe440x_attr_##_name = { \
|
||||
.dev_attr = __ATTR(_name, (S_IRUGO | S_IWUSR), \
|
||||
afe440x_show_register, \
|
||||
afe440x_store_register), \
|
||||
.reg = _reg, \
|
||||
.shift = _field ## _SHIFT, \
|
||||
.mask = _field ## _MASK, \
|
||||
.type = _type, \
|
||||
.field = _field, \
|
||||
.val_table = _table, \
|
||||
.table_size = _size, \
|
||||
.table_size = ARRAY_SIZE(_table), \
|
||||
}
|
||||
|
||||
#endif /* _AFE440X_H */
|
||||
|
|
|
@ -278,6 +278,7 @@ static const struct i2c_device_id am2315_i2c_id[] = {
|
|||
{"am2315", 0},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, am2315_i2c_id);
|
||||
|
||||
static const struct acpi_device_id am2315_acpi_id[] = {
|
||||
{"AOS2315", 0},
|
||||
|
|
|
@ -236,6 +236,7 @@ static const struct i2c_device_id htu21_id[] = {
|
|||
{"ms8607-humidity", MS8607},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, htu21_id);
|
||||
|
||||
static struct i2c_driver htu21_driver = {
|
||||
.probe = htu21_probe,
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
#include "bmi160.h"
|
||||
|
||||
|
@ -466,10 +467,36 @@ static int bmi160_write_raw(struct iio_dev *indio_dev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
IIO_CONST_ATTR(in_accel_sampling_frequency_available,
|
||||
"0.78125 1.5625 3.125 6.25 12.5 25 50 100 200 400 800 1600");
|
||||
static
|
||||
IIO_CONST_ATTR(in_anglvel_sampling_frequency_available,
|
||||
"25 50 100 200 400 800 1600 3200");
|
||||
static
|
||||
IIO_CONST_ATTR(in_accel_scale_available,
|
||||
"0.000598 0.001197 0.002394 0.004788");
|
||||
static
|
||||
IIO_CONST_ATTR(in_anglvel_scale_available,
|
||||
"0.001065 0.000532 0.000266 0.000133 0.000066");
|
||||
|
||||
static struct attribute *bmi160_attrs[] = {
|
||||
&iio_const_attr_in_accel_sampling_frequency_available.dev_attr.attr,
|
||||
&iio_const_attr_in_anglvel_sampling_frequency_available.dev_attr.attr,
|
||||
&iio_const_attr_in_accel_scale_available.dev_attr.attr,
|
||||
&iio_const_attr_in_anglvel_scale_available.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group bmi160_attrs_group = {
|
||||
.attrs = bmi160_attrs,
|
||||
};
|
||||
|
||||
static const struct iio_info bmi160_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = bmi160_read_raw,
|
||||
.write_raw = bmi160_write_raw,
|
||||
.attrs = &bmi160_attrs_group,
|
||||
};
|
||||
|
||||
static const char *bmi160_match_acpi_device(struct device *dev)
|
||||
|
|
|
@ -80,6 +80,7 @@ static const char * const iio_chan_type_name_spec[] = {
|
|||
[IIO_RESISTANCE] = "resistance",
|
||||
[IIO_PH] = "ph",
|
||||
[IIO_UVINDEX] = "uvindex",
|
||||
[IIO_ELECTRICALCONDUCTIVITY] = "electricalconductivity",
|
||||
};
|
||||
|
||||
static const char * const iio_modifier_names[] = {
|
||||
|
|
|
@ -0,0 +1,182 @@
|
|||
/*
|
||||
* The Industrial I/O core, software IIO devices functions
|
||||
*
|
||||
* Copyright (c) 2016 Intel Corporation
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kmod.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <linux/iio/sw_device.h>
|
||||
#include <linux/iio/configfs.h>
|
||||
#include <linux/configfs.h>
|
||||
|
||||
static struct config_group *iio_devices_group;
|
||||
static struct config_item_type iio_device_type_group_type;
|
||||
|
||||
static struct config_item_type iio_devices_group_type = {
|
||||
.ct_owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static LIST_HEAD(iio_device_types_list);
|
||||
static DEFINE_MUTEX(iio_device_types_lock);
|
||||
|
||||
static
|
||||
struct iio_sw_device_type *__iio_find_sw_device_type(const char *name,
|
||||
unsigned len)
|
||||
{
|
||||
struct iio_sw_device_type *d = NULL, *iter;
|
||||
|
||||
list_for_each_entry(iter, &iio_device_types_list, list)
|
||||
if (!strcmp(iter->name, name)) {
|
||||
d = iter;
|
||||
break;
|
||||
}
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
int iio_register_sw_device_type(struct iio_sw_device_type *d)
|
||||
{
|
||||
struct iio_sw_device_type *iter;
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&iio_device_types_lock);
|
||||
iter = __iio_find_sw_device_type(d->name, strlen(d->name));
|
||||
if (iter)
|
||||
ret = -EBUSY;
|
||||
else
|
||||
list_add_tail(&d->list, &iio_device_types_list);
|
||||
mutex_unlock(&iio_device_types_lock);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
d->group = configfs_register_default_group(iio_devices_group, d->name,
|
||||
&iio_device_type_group_type);
|
||||
if (IS_ERR(d->group))
|
||||
ret = PTR_ERR(d->group);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(iio_register_sw_device_type);
|
||||
|
||||
void iio_unregister_sw_device_type(struct iio_sw_device_type *dt)
|
||||
{
|
||||
struct iio_sw_device_type *iter;
|
||||
|
||||
mutex_lock(&iio_device_types_lock);
|
||||
iter = __iio_find_sw_device_type(dt->name, strlen(dt->name));
|
||||
if (iter)
|
||||
list_del(&dt->list);
|
||||
mutex_unlock(&iio_device_types_lock);
|
||||
|
||||
configfs_unregister_default_group(dt->group);
|
||||
}
|
||||
EXPORT_SYMBOL(iio_unregister_sw_device_type);
|
||||
|
||||
static
|
||||
struct iio_sw_device_type *iio_get_sw_device_type(const char *name)
|
||||
{
|
||||
struct iio_sw_device_type *dt;
|
||||
|
||||
mutex_lock(&iio_device_types_lock);
|
||||
dt = __iio_find_sw_device_type(name, strlen(name));
|
||||
if (dt && !try_module_get(dt->owner))
|
||||
dt = NULL;
|
||||
mutex_unlock(&iio_device_types_lock);
|
||||
|
||||
return dt;
|
||||
}
|
||||
|
||||
struct iio_sw_device *iio_sw_device_create(const char *type, const char *name)
|
||||
{
|
||||
struct iio_sw_device *d;
|
||||
struct iio_sw_device_type *dt;
|
||||
|
||||
dt = iio_get_sw_device_type(type);
|
||||
if (!dt) {
|
||||
pr_err("Invalid device type: %s\n", type);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
d = dt->ops->probe(name);
|
||||
if (IS_ERR(d))
|
||||
goto out_module_put;
|
||||
|
||||
d->device_type = dt;
|
||||
|
||||
return d;
|
||||
out_module_put:
|
||||
module_put(dt->owner);
|
||||
return d;
|
||||
}
|
||||
EXPORT_SYMBOL(iio_sw_device_create);
|
||||
|
||||
void iio_sw_device_destroy(struct iio_sw_device *d)
|
||||
{
|
||||
struct iio_sw_device_type *dt = d->device_type;
|
||||
|
||||
dt->ops->remove(d);
|
||||
module_put(dt->owner);
|
||||
}
|
||||
EXPORT_SYMBOL(iio_sw_device_destroy);
|
||||
|
||||
static struct config_group *device_make_group(struct config_group *group,
|
||||
const char *name)
|
||||
{
|
||||
struct iio_sw_device *d;
|
||||
|
||||
d = iio_sw_device_create(group->cg_item.ci_name, name);
|
||||
if (IS_ERR(d))
|
||||
return ERR_CAST(d);
|
||||
|
||||
config_item_set_name(&d->group.cg_item, "%s", name);
|
||||
|
||||
return &d->group;
|
||||
}
|
||||
|
||||
static void device_drop_group(struct config_group *group,
|
||||
struct config_item *item)
|
||||
{
|
||||
struct iio_sw_device *d = to_iio_sw_device(item);
|
||||
|
||||
iio_sw_device_destroy(d);
|
||||
config_item_put(item);
|
||||
}
|
||||
|
||||
static struct configfs_group_operations device_ops = {
|
||||
.make_group = &device_make_group,
|
||||
.drop_item = &device_drop_group,
|
||||
};
|
||||
|
||||
static struct config_item_type iio_device_type_group_type = {
|
||||
.ct_group_ops = &device_ops,
|
||||
.ct_owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int __init iio_sw_device_init(void)
|
||||
{
|
||||
iio_devices_group =
|
||||
configfs_register_default_group(&iio_configfs_subsys.su_group,
|
||||
"devices",
|
||||
&iio_devices_group_type);
|
||||
return PTR_ERR_OR_ZERO(iio_devices_group);
|
||||
}
|
||||
module_init(iio_sw_device_init);
|
||||
|
||||
static void __exit iio_sw_device_exit(void)
|
||||
{
|
||||
configfs_unregister_default_group(iio_devices_group);
|
||||
}
|
||||
module_exit(iio_sw_device_exit);
|
||||
|
||||
MODULE_AUTHOR("Daniel Baluta <daniel.baluta@intel.com>");
|
||||
MODULE_DESCRIPTION("Industrial I/O software devices support");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -68,6 +68,10 @@ int iio_trigger_register(struct iio_trigger *trig_info)
|
|||
{
|
||||
int ret;
|
||||
|
||||
/* trig_info->ops is required for the module member */
|
||||
if (!trig_info->ops)
|
||||
return -EINVAL;
|
||||
|
||||
trig_info->id = ida_simple_get(&iio_trigger_ida, 0, 0, GFP_KERNEL);
|
||||
if (trig_info->id < 0)
|
||||
return trig_info->id;
|
||||
|
@ -164,8 +168,7 @@ EXPORT_SYMBOL(iio_trigger_poll_chained);
|
|||
|
||||
void iio_trigger_notify_done(struct iio_trigger *trig)
|
||||
{
|
||||
if (atomic_dec_and_test(&trig->use_count) && trig->ops &&
|
||||
trig->ops->try_reenable)
|
||||
if (atomic_dec_and_test(&trig->use_count) && trig->ops->try_reenable)
|
||||
if (trig->ops->try_reenable(trig))
|
||||
/* Missed an interrupt so launch new poll now */
|
||||
iio_trigger_poll(trig);
|
||||
|
@ -219,7 +222,7 @@ static int iio_trigger_attach_poll_func(struct iio_trigger *trig,
|
|||
return ret;
|
||||
}
|
||||
|
||||
if (trig->ops && trig->ops->set_trigger_state && notinuse) {
|
||||
if (trig->ops->set_trigger_state && notinuse) {
|
||||
ret = trig->ops->set_trigger_state(trig, true);
|
||||
if (ret < 0)
|
||||
module_put(pf->indio_dev->info->driver_module);
|
||||
|
@ -236,7 +239,7 @@ static int iio_trigger_detach_poll_func(struct iio_trigger *trig,
|
|||
= (bitmap_weight(trig->pool,
|
||||
CONFIG_IIO_CONSUMERS_PER_TRIGGER)
|
||||
== 1);
|
||||
if (trig->ops && trig->ops->set_trigger_state && no_other_users) {
|
||||
if (trig->ops->set_trigger_state && no_other_users) {
|
||||
ret = trig->ops->set_trigger_state(trig, false);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -358,7 +361,7 @@ static ssize_t iio_trigger_write_current(struct device *dev,
|
|||
return ret;
|
||||
}
|
||||
|
||||
if (trig && trig->ops && trig->ops->validate_device) {
|
||||
if (trig && trig->ops->validate_device) {
|
||||
ret = trig->ops->validate_device(trig, indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
|
|
@ -325,9 +325,6 @@ static int jsa1212_probe(struct i2c_client *client,
|
|||
struct regmap *regmap;
|
||||
int ret;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
|
|
@ -44,6 +44,7 @@ config BMC150_MAGN_I2C
|
|||
This driver is only implementing magnetometer part, which has
|
||||
its own address and register map.
|
||||
|
||||
This driver also supports I2C Bosch BMC156 and BMM150 chips.
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called bmc150_magn_i2c.
|
||||
|
||||
|
@ -60,6 +61,7 @@ config BMC150_MAGN_SPI
|
|||
This driver is only implementing magnetometer part, which has
|
||||
its own address and register map.
|
||||
|
||||
This driver also supports SPI Bosch BMC156 and BMM150 chips.
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called bmc150_magn_spi.
|
||||
|
||||
|
|
|
@ -430,8 +430,8 @@ static int ak8975_who_i_am(struct i2c_client *client,
|
|||
* AK8975 | DEVICE_ID | NA
|
||||
* AK8963 | DEVICE_ID | NA
|
||||
*/
|
||||
ret = i2c_smbus_read_i2c_block_data(client, AK09912_REG_WIA1,
|
||||
2, wia_val);
|
||||
ret = i2c_smbus_read_i2c_block_data_or_emulated(
|
||||
client, AK09912_REG_WIA1, 2, wia_val);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "Error reading WIA\n");
|
||||
return ret;
|
||||
|
@ -543,9 +543,9 @@ static int ak8975_setup(struct i2c_client *client)
|
|||
}
|
||||
|
||||
/* Get asa data and store in the device data. */
|
||||
ret = i2c_smbus_read_i2c_block_data(client,
|
||||
data->def->ctrl_regs[ASA_BASE],
|
||||
3, data->asa);
|
||||
ret = i2c_smbus_read_i2c_block_data_or_emulated(
|
||||
client, data->def->ctrl_regs[ASA_BASE],
|
||||
3, data->asa);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "Not able to read asa data\n");
|
||||
return ret;
|
||||
|
@ -686,6 +686,7 @@ static int ak8975_read_axis(struct iio_dev *indio_dev, int index, int *val)
|
|||
struct ak8975_data *data = iio_priv(indio_dev);
|
||||
const struct i2c_client *client = data->client;
|
||||
const struct ak_def *def = data->def;
|
||||
u16 buff;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
|
@ -694,14 +695,17 @@ static int ak8975_read_axis(struct iio_dev *indio_dev, int index, int *val)
|
|||
if (ret)
|
||||
goto exit;
|
||||
|
||||
ret = i2c_smbus_read_word_data(client, def->data_regs[index]);
|
||||
ret = i2c_smbus_read_i2c_block_data_or_emulated(
|
||||
client, def->data_regs[index],
|
||||
sizeof(buff), (u8*)&buff);
|
||||
if (ret < 0)
|
||||
goto exit;
|
||||
|
||||
mutex_unlock(&data->lock);
|
||||
|
||||
/* Clamp to valid range. */
|
||||
*val = clamp_t(s16, ret, -def->range, def->range);
|
||||
/* Swap bytes and convert to valid range. */
|
||||
buff = le16_to_cpu(buff);
|
||||
*val = clamp_t(s16, buff, -def->range, def->range);
|
||||
return IIO_VAL_INT;
|
||||
|
||||
exit:
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
* 3-axis magnetometer driver supporting following I2C Bosch-Sensortec chips:
|
||||
* - BMC150
|
||||
* - BMC156
|
||||
* - BMM150
|
||||
*
|
||||
* Copyright (c) 2016, Intel Corporation.
|
||||
*
|
||||
|
@ -49,6 +50,7 @@ static int bmc150_magn_i2c_remove(struct i2c_client *client)
|
|||
static const struct acpi_device_id bmc150_magn_acpi_match[] = {
|
||||
{"BMC150B", 0},
|
||||
{"BMC156B", 0},
|
||||
{"BMM150B", 0},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, bmc150_magn_acpi_match);
|
||||
|
@ -56,6 +58,7 @@ MODULE_DEVICE_TABLE(acpi, bmc150_magn_acpi_match);
|
|||
static const struct i2c_device_id bmc150_magn_i2c_id[] = {
|
||||
{"bmc150_magn", 0},
|
||||
{"bmc156_magn", 0},
|
||||
{"bmm150_magn", 0},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, bmc150_magn_i2c_id);
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
* 3-axis magnetometer driver support following SPI Bosch-Sensortec chips:
|
||||
* - BMC150
|
||||
* - BMC156
|
||||
* - BMM150
|
||||
*
|
||||
* Copyright (c) 2016, Intel Corporation.
|
||||
*
|
||||
|
@ -41,6 +42,7 @@ static int bmc150_magn_spi_remove(struct spi_device *spi)
|
|||
static const struct spi_device_id bmc150_magn_spi_id[] = {
|
||||
{"bmc150_magn", 0},
|
||||
{"bmc156_magn", 0},
|
||||
{"bmm150_magn", 0},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, bmc150_magn_spi_id);
|
||||
|
@ -48,6 +50,7 @@ MODULE_DEVICE_TABLE(spi, bmc150_magn_spi_id);
|
|||
static const struct acpi_device_id bmc150_magn_acpi_match[] = {
|
||||
{"BMC150B", 0},
|
||||
{"BMC156B", 0},
|
||||
{"BMM150B", 0},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, bmc150_magn_acpi_match);
|
||||
|
|
|
@ -588,13 +588,15 @@ int st_magn_common_probe(struct iio_dev *indio_dev)
|
|||
indio_dev->info = &magn_info;
|
||||
mutex_init(&mdata->tb.buf_lock);
|
||||
|
||||
st_sensors_power_enable(indio_dev);
|
||||
err = st_sensors_power_enable(indio_dev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = st_sensors_check_device_support(indio_dev,
|
||||
ARRAY_SIZE(st_magn_sensors_settings),
|
||||
st_magn_sensors_settings);
|
||||
if (err < 0)
|
||||
return err;
|
||||
goto st_magn_power_off;
|
||||
|
||||
mdata->num_data_channels = ST_MAGN_NUMBER_DATA_CHANNELS;
|
||||
mdata->multiread_bit = mdata->sensor_settings->multi_read_bit;
|
||||
|
@ -607,11 +609,11 @@ int st_magn_common_probe(struct iio_dev *indio_dev)
|
|||
|
||||
err = st_sensors_init_sensor(indio_dev, NULL);
|
||||
if (err < 0)
|
||||
return err;
|
||||
goto st_magn_power_off;
|
||||
|
||||
err = st_magn_allocate_ring(indio_dev);
|
||||
if (err < 0)
|
||||
return err;
|
||||
goto st_magn_power_off;
|
||||
|
||||
if (irq > 0) {
|
||||
err = st_sensors_allocate_trigger(indio_dev,
|
||||
|
@ -634,6 +636,8 @@ int st_magn_common_probe(struct iio_dev *indio_dev)
|
|||
st_sensors_deallocate_trigger(indio_dev);
|
||||
st_magn_probe_trigger_error:
|
||||
st_magn_deallocate_ring(indio_dev);
|
||||
st_magn_power_off:
|
||||
st_sensors_power_disable(indio_dev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
|
|
@ -15,6 +15,17 @@ config DS1803
|
|||
To compile this driver as a module, choose M here: the
|
||||
module will be called ds1803.
|
||||
|
||||
config MAX5487
|
||||
tristate "Maxim MAX5487/MAX5488/MAX5489 Digital Potentiometer driver"
|
||||
depends on SPI
|
||||
help
|
||||
Say yes here to build support for the Maxim
|
||||
MAX5487, MAX5488, MAX5489 digital potentiometer
|
||||
chips.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called max5487.
|
||||
|
||||
config MCP4131
|
||||
tristate "Microchip MCP413X/414X/415X/416X/423X/424X/425X/426X Digital Potentiometer driver"
|
||||
depends on SPI
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
# When adding new entries keep the list in alphabetical order
|
||||
obj-$(CONFIG_DS1803) += ds1803.o
|
||||
obj-$(CONFIG_MAX5487) += max5487.o
|
||||
obj-$(CONFIG_MCP4131) += mcp4131.o
|
||||
obj-$(CONFIG_MCP4531) += mcp4531.o
|
||||
obj-$(CONFIG_TPL0102) += tpl0102.o
|
||||
|
|
|
@ -0,0 +1,161 @@
|
|||
/*
|
||||
* max5487.c - Support for MAX5487, MAX5488, MAX5489 digital potentiometers
|
||||
*
|
||||
* Copyright (C) 2016 Cristina-Gabriela Moraru <cristina.moraru09@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.
|
||||
*
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/acpi.h>
|
||||
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/iio.h>
|
||||
|
||||
#define MAX5487_WRITE_WIPER_A (0x01 << 8)
|
||||
#define MAX5487_WRITE_WIPER_B (0x02 << 8)
|
||||
|
||||
/* copy both wiper regs to NV regs */
|
||||
#define MAX5487_COPY_AB_TO_NV (0x23 << 8)
|
||||
/* copy both NV regs to wiper regs */
|
||||
#define MAX5487_COPY_NV_TO_AB (0x33 << 8)
|
||||
|
||||
#define MAX5487_MAX_POS 255
|
||||
|
||||
struct max5487_data {
|
||||
struct spi_device *spi;
|
||||
int kohms;
|
||||
};
|
||||
|
||||
#define MAX5487_CHANNEL(ch, addr) { \
|
||||
.type = IIO_RESISTANCE, \
|
||||
.indexed = 1, \
|
||||
.output = 1, \
|
||||
.channel = ch, \
|
||||
.address = addr, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec max5487_channels[] = {
|
||||
MAX5487_CHANNEL(0, MAX5487_WRITE_WIPER_A),
|
||||
MAX5487_CHANNEL(1, MAX5487_WRITE_WIPER_B),
|
||||
};
|
||||
|
||||
static int max5487_write_cmd(struct spi_device *spi, u16 cmd)
|
||||
{
|
||||
return spi_write(spi, (const void *) &cmd, sizeof(u16));
|
||||
}
|
||||
|
||||
static int max5487_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct max5487_data *data = iio_priv(indio_dev);
|
||||
|
||||
if (mask != IIO_CHAN_INFO_SCALE)
|
||||
return -EINVAL;
|
||||
|
||||
*val = 1000 * data->kohms;
|
||||
*val2 = MAX5487_MAX_POS;
|
||||
|
||||
return IIO_VAL_FRACTIONAL;
|
||||
}
|
||||
|
||||
static int max5487_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
struct max5487_data *data = iio_priv(indio_dev);
|
||||
|
||||
if (mask != IIO_CHAN_INFO_RAW)
|
||||
return -EINVAL;
|
||||
|
||||
if (val < 0 || val > MAX5487_MAX_POS)
|
||||
return -EINVAL;
|
||||
|
||||
return max5487_write_cmd(data->spi, chan->address | val);
|
||||
}
|
||||
|
||||
static const struct iio_info max5487_info = {
|
||||
.read_raw = max5487_read_raw,
|
||||
.write_raw = max5487_write_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int max5487_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct max5487_data *data;
|
||||
const struct spi_device_id *id = spi_get_device_id(spi);
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_set_drvdata(&spi->dev, indio_dev);
|
||||
data = iio_priv(indio_dev);
|
||||
|
||||
data->spi = spi;
|
||||
data->kohms = id->driver_data;
|
||||
|
||||
indio_dev->info = &max5487_info;
|
||||
indio_dev->name = id->name;
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = max5487_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(max5487_channels);
|
||||
|
||||
/* restore both wiper regs from NV regs */
|
||||
ret = max5487_write_cmd(data->spi, MAX5487_COPY_NV_TO_AB);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return iio_device_register(indio_dev);
|
||||
}
|
||||
|
||||
static int max5487_spi_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(&spi->dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
/* save both wiper regs to NV regs */
|
||||
return max5487_write_cmd(spi, MAX5487_COPY_AB_TO_NV);
|
||||
}
|
||||
|
||||
static const struct spi_device_id max5487_id[] = {
|
||||
{ "MAX5487", 10 },
|
||||
{ "MAX5488", 50 },
|
||||
{ "MAX5489", 100 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, max5487_id);
|
||||
|
||||
static const struct acpi_device_id max5487_acpi_match[] = {
|
||||
{ "MAX5487", 10 },
|
||||
{ "MAX5488", 50 },
|
||||
{ "MAX5489", 100 },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, max5487_acpi_match);
|
||||
|
||||
static struct spi_driver max5487_driver = {
|
||||
.driver = {
|
||||
.name = "max5487",
|
||||
.owner = THIS_MODULE,
|
||||
.acpi_match_table = ACPI_PTR(max5487_acpi_match),
|
||||
},
|
||||
.id_table = max5487_id,
|
||||
.probe = max5487_spi_probe,
|
||||
.remove = max5487_spi_remove
|
||||
};
|
||||
module_spi_driver(max5487_driver);
|
||||
|
||||
MODULE_AUTHOR("Cristina-Gabriela Moraru <cristina.moraru09@gmail.com>");
|
||||
MODULE_DESCRIPTION("max5487 SPI driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -116,10 +116,6 @@ static int tpl0102_probe(struct i2c_client *client,
|
|||
struct tpl0102_data *data;
|
||||
struct iio_dev *indio_dev;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter,
|
||||
I2C_FUNC_SMBUS_WORD_DATA))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
|
|
@ -12,7 +12,8 @@ config BMP280
|
|||
select REGMAP_I2C
|
||||
help
|
||||
Say yes here to build support for Bosch Sensortec BMP180 and BMP280
|
||||
pressure and temperature sensors.
|
||||
pressure and temperature sensors. Also supports the BE280 with
|
||||
an additional humidty sensor channel.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called bmp280.
|
||||
|
@ -130,7 +131,7 @@ config IIO_ST_PRESS
|
|||
select IIO_TRIGGERED_BUFFER if (IIO_BUFFER)
|
||||
help
|
||||
Say yes here to build support for STMicroelectronics pressure
|
||||
sensors: LPS001WP, LPS25H, LPS331AP.
|
||||
sensors: LPS001WP, LPS25H, LPS331AP, LPS22HB.
|
||||
|
||||
This driver can also be built as a module. If so, these modules
|
||||
will be created:
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
* Datasheet:
|
||||
* https://ae-bst.resource.bosch.com/media/_tech/media/datasheets/BST-BMP180-DS000-121.pdf
|
||||
* https://ae-bst.resource.bosch.com/media/_tech/media/datasheets/BST-BMP280-DS001-12.pdf
|
||||
* https://ae-bst.resource.bosch.com/media/_tech/media/datasheets/BST-BME280_DS001-11.pdf
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "bmp280: " fmt
|
||||
|
@ -23,6 +24,8 @@
|
|||
#include <linux/iio/sysfs.h>
|
||||
|
||||
/* BMP280 specific registers */
|
||||
#define BMP280_REG_HUMIDITY_LSB 0xFE
|
||||
#define BMP280_REG_HUMIDITY_MSB 0xFD
|
||||
#define BMP280_REG_TEMP_XLSB 0xFC
|
||||
#define BMP280_REG_TEMP_LSB 0xFB
|
||||
#define BMP280_REG_TEMP_MSB 0xFA
|
||||
|
@ -31,7 +34,17 @@
|
|||
#define BMP280_REG_PRESS_MSB 0xF7
|
||||
|
||||
#define BMP280_REG_CONFIG 0xF5
|
||||
#define BMP280_REG_CTRL_MEAS 0xF4
|
||||
#define BMP280_REG_STATUS 0xF3
|
||||
#define BMP280_REG_CTRL_HUMIDITY 0xF2
|
||||
|
||||
/* Due to non linear mapping, and data sizes we can't do a bulk read */
|
||||
#define BMP280_REG_COMP_H1 0xA1
|
||||
#define BMP280_REG_COMP_H2 0xE1
|
||||
#define BMP280_REG_COMP_H3 0xE3
|
||||
#define BMP280_REG_COMP_H4 0xE4
|
||||
#define BMP280_REG_COMP_H5 0xE5
|
||||
#define BMP280_REG_COMP_H6 0xE7
|
||||
|
||||
#define BMP280_REG_COMP_TEMP_START 0x88
|
||||
#define BMP280_COMP_TEMP_REG_COUNT 6
|
||||
|
@ -46,6 +59,15 @@
|
|||
#define BMP280_FILTER_8X (BIT(3) | BIT(2))
|
||||
#define BMP280_FILTER_16X BIT(4)
|
||||
|
||||
#define BMP280_OSRS_HUMIDITY_MASK (BIT(2) | BIT(1) | BIT(0))
|
||||
#define BMP280_OSRS_HUMIDITIY_X(osrs_h) ((osrs_h) << 0)
|
||||
#define BMP280_OSRS_HUMIDITY_SKIP 0
|
||||
#define BMP280_OSRS_HUMIDITY_1X BMP280_OSRS_HUMIDITIY_X(1)
|
||||
#define BMP280_OSRS_HUMIDITY_2X BMP280_OSRS_HUMIDITIY_X(2)
|
||||
#define BMP280_OSRS_HUMIDITY_4X BMP280_OSRS_HUMIDITIY_X(3)
|
||||
#define BMP280_OSRS_HUMIDITY_8X BMP280_OSRS_HUMIDITIY_X(4)
|
||||
#define BMP280_OSRS_HUMIDITY_16X BMP280_OSRS_HUMIDITIY_X(5)
|
||||
|
||||
#define BMP280_OSRS_TEMP_MASK (BIT(7) | BIT(6) | BIT(5))
|
||||
#define BMP280_OSRS_TEMP_SKIP 0
|
||||
#define BMP280_OSRS_TEMP_X(osrs_t) ((osrs_t) << 5)
|
||||
|
@ -92,6 +114,7 @@
|
|||
|
||||
#define BMP180_CHIP_ID 0x55
|
||||
#define BMP280_CHIP_ID 0x58
|
||||
#define BME280_CHIP_ID 0x60
|
||||
#define BMP280_SOFT_RESET_VAL 0xB6
|
||||
|
||||
struct bmp280_data {
|
||||
|
@ -103,6 +126,7 @@ struct bmp280_data {
|
|||
/* log of base 2 of oversampling rate */
|
||||
u8 oversampling_press;
|
||||
u8 oversampling_temp;
|
||||
u8 oversampling_humid;
|
||||
|
||||
/*
|
||||
* Carryover value from temperature conversion, used in pressure
|
||||
|
@ -120,9 +144,13 @@ struct bmp280_chip_info {
|
|||
const int *oversampling_press_avail;
|
||||
int num_oversampling_press_avail;
|
||||
|
||||
const int *oversampling_humid_avail;
|
||||
int num_oversampling_humid_avail;
|
||||
|
||||
int (*chip_config)(struct bmp280_data *);
|
||||
int (*read_temp)(struct bmp280_data *, int *);
|
||||
int (*read_press)(struct bmp280_data *, int *, int *);
|
||||
int (*read_humid)(struct bmp280_data *, int *, int *);
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -143,12 +171,18 @@ static const struct iio_chan_spec bmp280_channels[] = {
|
|||
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
|
||||
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
|
||||
},
|
||||
{
|
||||
.type = IIO_HUMIDITYRELATIVE,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
|
||||
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
|
||||
},
|
||||
};
|
||||
|
||||
static bool bmp280_is_writeable_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case BMP280_REG_CONFIG:
|
||||
case BMP280_REG_CTRL_HUMIDITY:
|
||||
case BMP280_REG_CTRL_MEAS:
|
||||
case BMP280_REG_RESET:
|
||||
return true;
|
||||
|
@ -160,6 +194,8 @@ static bool bmp280_is_writeable_reg(struct device *dev, unsigned int reg)
|
|||
static bool bmp280_is_volatile_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case BMP280_REG_HUMIDITY_LSB:
|
||||
case BMP280_REG_HUMIDITY_MSB:
|
||||
case BMP280_REG_TEMP_XLSB:
|
||||
case BMP280_REG_TEMP_LSB:
|
||||
case BMP280_REG_TEMP_MSB:
|
||||
|
@ -177,13 +213,77 @@ static const struct regmap_config bmp280_regmap_config = {
|
|||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
|
||||
.max_register = BMP280_REG_TEMP_XLSB,
|
||||
.max_register = BMP280_REG_HUMIDITY_LSB,
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
|
||||
.writeable_reg = bmp280_is_writeable_reg,
|
||||
.volatile_reg = bmp280_is_volatile_reg,
|
||||
};
|
||||
|
||||
/*
|
||||
* Returns humidity in percent, resolution is 0.01 percent. Output value of
|
||||
* "47445" represents 47445/1024 = 46.333 %RH.
|
||||
*
|
||||
* Taken from BME280 datasheet, Section 4.2.3, "Compensation formula".
|
||||
*/
|
||||
|
||||
static u32 bmp280_compensate_humidity(struct bmp280_data *data,
|
||||
s32 adc_humidity)
|
||||
{
|
||||
struct device *dev = &data->client->dev;
|
||||
unsigned int H1, H3, tmp;
|
||||
int H2, H4, H5, H6, ret, var;
|
||||
|
||||
ret = regmap_read(data->regmap, BMP280_REG_COMP_H1, &H1);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read H1 comp value\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regmap_bulk_read(data->regmap, BMP280_REG_COMP_H2, &tmp, 2);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read H2 comp value\n");
|
||||
return ret;
|
||||
}
|
||||
H2 = sign_extend32(le16_to_cpu(tmp), 15);
|
||||
|
||||
ret = regmap_read(data->regmap, BMP280_REG_COMP_H3, &H3);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read H3 comp value\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regmap_bulk_read(data->regmap, BMP280_REG_COMP_H4, &tmp, 2);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read H4 comp value\n");
|
||||
return ret;
|
||||
}
|
||||
H4 = sign_extend32(((be16_to_cpu(tmp) >> 4) & 0xff0) |
|
||||
(be16_to_cpu(tmp) & 0xf), 11);
|
||||
|
||||
ret = regmap_bulk_read(data->regmap, BMP280_REG_COMP_H5, &tmp, 2);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read H5 comp value\n");
|
||||
return ret;
|
||||
}
|
||||
H5 = sign_extend32(((le16_to_cpu(tmp) >> 4) & 0xfff), 11);
|
||||
|
||||
ret = regmap_read(data->regmap, BMP280_REG_COMP_H6, &tmp);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read H6 comp value\n");
|
||||
return ret;
|
||||
}
|
||||
H6 = sign_extend32(tmp, 7);
|
||||
|
||||
var = ((s32)data->t_fine) - 76800;
|
||||
var = ((((adc_humidity << 14) - (H4 << 20) - (H5 * var)) + 16384) >> 15)
|
||||
* (((((((var * H6) >> 10) * (((var * H3) >> 11) + 32768)) >> 10)
|
||||
+ 2097152) * H2 + 8192) >> 14);
|
||||
var -= ((((var >> 15) * (var >> 15)) >> 7) * H1) >> 4;
|
||||
|
||||
return var >> 12;
|
||||
};
|
||||
|
||||
/*
|
||||
* Returns temperature in DegC, resolution is 0.01 DegC. Output value of
|
||||
* "5123" equals 51.23 DegC. t_fine carries fine temperature as global
|
||||
|
@ -324,6 +424,34 @@ static int bmp280_read_press(struct bmp280_data *data,
|
|||
return IIO_VAL_FRACTIONAL;
|
||||
}
|
||||
|
||||
static int bmp280_read_humid(struct bmp280_data *data, int *val, int *val2)
|
||||
{
|
||||
int ret;
|
||||
__be16 tmp = 0;
|
||||
s32 adc_humidity;
|
||||
u32 comp_humidity;
|
||||
|
||||
/* Read and compensate temperature so we get a reading of t_fine. */
|
||||
ret = bmp280_read_temp(data, NULL);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = regmap_bulk_read(data->regmap, BMP280_REG_HUMIDITY_MSB,
|
||||
(u8 *) &tmp, 2);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev, "failed to read humidity\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
adc_humidity = be16_to_cpu(tmp);
|
||||
comp_humidity = bmp280_compensate_humidity(data, adc_humidity);
|
||||
|
||||
*val = comp_humidity;
|
||||
*val2 = 1024;
|
||||
|
||||
return IIO_VAL_FRACTIONAL;
|
||||
}
|
||||
|
||||
static int bmp280_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
|
@ -336,6 +464,9 @@ static int bmp280_read_raw(struct iio_dev *indio_dev,
|
|||
switch (mask) {
|
||||
case IIO_CHAN_INFO_PROCESSED:
|
||||
switch (chan->type) {
|
||||
case IIO_HUMIDITYRELATIVE:
|
||||
ret = data->chip_info->read_humid(data, val, val2);
|
||||
break;
|
||||
case IIO_PRESSURE:
|
||||
ret = data->chip_info->read_press(data, val, val2);
|
||||
break;
|
||||
|
@ -349,6 +480,10 @@ static int bmp280_read_raw(struct iio_dev *indio_dev,
|
|||
break;
|
||||
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
|
||||
switch (chan->type) {
|
||||
case IIO_HUMIDITYRELATIVE:
|
||||
*val = 1 << data->oversampling_humid;
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
case IIO_PRESSURE:
|
||||
*val = 1 << data->oversampling_press;
|
||||
ret = IIO_VAL_INT;
|
||||
|
@ -372,6 +507,23 @@ static int bmp280_read_raw(struct iio_dev *indio_dev,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int bmp280_write_oversampling_ratio_humid(struct bmp280_data *data,
|
||||
int val)
|
||||
{
|
||||
int i;
|
||||
const int *avail = data->chip_info->oversampling_humid_avail;
|
||||
const int n = data->chip_info->num_oversampling_humid_avail;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
if (avail[i] == val) {
|
||||
data->oversampling_humid = ilog2(val);
|
||||
|
||||
return data->chip_info->chip_config(data);
|
||||
}
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int bmp280_write_oversampling_ratio_temp(struct bmp280_data *data,
|
||||
int val)
|
||||
{
|
||||
|
@ -417,6 +569,9 @@ static int bmp280_write_raw(struct iio_dev *indio_dev,
|
|||
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
|
||||
mutex_lock(&data->lock);
|
||||
switch (chan->type) {
|
||||
case IIO_HUMIDITYRELATIVE:
|
||||
ret = bmp280_write_oversampling_ratio_humid(data, val);
|
||||
break;
|
||||
case IIO_PRESSURE:
|
||||
ret = bmp280_write_oversampling_ratio_press(data, val);
|
||||
break;
|
||||
|
@ -535,6 +690,37 @@ static const struct bmp280_chip_info bmp280_chip_info = {
|
|||
.read_press = bmp280_read_press,
|
||||
};
|
||||
|
||||
static int bme280_chip_config(struct bmp280_data *data)
|
||||
{
|
||||
int ret = bmp280_chip_config(data);
|
||||
u8 osrs = BMP280_OSRS_HUMIDITIY_X(data->oversampling_humid + 1);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return regmap_update_bits(data->regmap, BMP280_REG_CTRL_HUMIDITY,
|
||||
BMP280_OSRS_HUMIDITY_MASK, osrs);
|
||||
}
|
||||
|
||||
static const struct bmp280_chip_info bme280_chip_info = {
|
||||
.regmap_config = &bmp280_regmap_config,
|
||||
|
||||
.oversampling_temp_avail = bmp280_oversampling_avail,
|
||||
.num_oversampling_temp_avail = ARRAY_SIZE(bmp280_oversampling_avail),
|
||||
|
||||
.oversampling_press_avail = bmp280_oversampling_avail,
|
||||
.num_oversampling_press_avail = ARRAY_SIZE(bmp280_oversampling_avail),
|
||||
|
||||
.oversampling_humid_avail = bmp280_oversampling_avail,
|
||||
.num_oversampling_humid_avail = ARRAY_SIZE(bmp280_oversampling_avail),
|
||||
|
||||
.chip_config = bme280_chip_config,
|
||||
.read_temp = bmp280_read_temp,
|
||||
.read_press = bmp280_read_press,
|
||||
.read_humid = bmp280_read_humid,
|
||||
};
|
||||
|
||||
|
||||
static bool bmp180_is_writeable_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
|
@ -849,21 +1035,29 @@ static int bmp280_probe(struct i2c_client *client,
|
|||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->name = id->name;
|
||||
indio_dev->channels = bmp280_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(bmp280_channels);
|
||||
indio_dev->info = &bmp280_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
switch (id->driver_data) {
|
||||
case BMP180_CHIP_ID:
|
||||
indio_dev->num_channels = 2;
|
||||
data->chip_info = &bmp180_chip_info;
|
||||
data->oversampling_press = ilog2(8);
|
||||
data->oversampling_temp = ilog2(1);
|
||||
break;
|
||||
case BMP280_CHIP_ID:
|
||||
indio_dev->num_channels = 2;
|
||||
data->chip_info = &bmp280_chip_info;
|
||||
data->oversampling_press = ilog2(16);
|
||||
data->oversampling_temp = ilog2(2);
|
||||
break;
|
||||
case BME280_CHIP_ID:
|
||||
indio_dev->num_channels = 3;
|
||||
data->chip_info = &bme280_chip_info;
|
||||
data->oversampling_press = ilog2(16);
|
||||
data->oversampling_humid = ilog2(16);
|
||||
data->oversampling_temp = ilog2(2);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -895,6 +1089,7 @@ static const struct acpi_device_id bmp280_acpi_match[] = {
|
|||
{"BMP0280", BMP280_CHIP_ID },
|
||||
{"BMP0180", BMP180_CHIP_ID },
|
||||
{"BMP0085", BMP180_CHIP_ID },
|
||||
{"BME0280", BME280_CHIP_ID },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, bmp280_acpi_match);
|
||||
|
@ -903,6 +1098,7 @@ static const struct i2c_device_id bmp280_id[] = {
|
|||
{"bmp280", BMP280_CHIP_ID },
|
||||
{"bmp180", BMP180_CHIP_ID },
|
||||
{"bmp085", BMP180_CHIP_ID },
|
||||
{"bme280", BME280_CHIP_ID },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, bmp280_id);
|
||||
|
|
|
@ -401,6 +401,7 @@ static const struct i2c_device_id hp206c_id[] = {
|
|||
{"hp206c"},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, hp206c_id);
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
static const struct acpi_device_id hp206c_acpi_match[] = {
|
||||
|
|
|
@ -173,6 +173,7 @@ static const struct i2c_device_id ms5637_id[] = {
|
|||
{"ms8607-temppressure", 1},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ms5637_id);
|
||||
|
||||
static struct i2c_driver ms5637_driver = {
|
||||
.probe = ms5637_probe,
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#define LPS001WP_PRESS_DEV_NAME "lps001wp"
|
||||
#define LPS25H_PRESS_DEV_NAME "lps25h"
|
||||
#define LPS331AP_PRESS_DEV_NAME "lps331ap"
|
||||
#define LPS22HB_PRESS_DEV_NAME "lps22hb"
|
||||
|
||||
/**
|
||||
* struct st_sensors_platform_data - default press platform data
|
||||
|
|
|
@ -113,6 +113,26 @@
|
|||
#define ST_PRESS_LPS25H_OUT_XL_ADDR 0x28
|
||||
#define ST_TEMP_LPS25H_OUT_L_ADDR 0x2b
|
||||
|
||||
/* CUSTOM VALUES FOR LPS22HB SENSOR */
|
||||
#define ST_PRESS_LPS22HB_WAI_EXP 0xb1
|
||||
#define ST_PRESS_LPS22HB_ODR_ADDR 0x10
|
||||
#define ST_PRESS_LPS22HB_ODR_MASK 0x70
|
||||
#define ST_PRESS_LPS22HB_ODR_AVL_1HZ_VAL 0x01
|
||||
#define ST_PRESS_LPS22HB_ODR_AVL_10HZ_VAL 0x02
|
||||
#define ST_PRESS_LPS22HB_ODR_AVL_25HZ_VAL 0x03
|
||||
#define ST_PRESS_LPS22HB_ODR_AVL_50HZ_VAL 0x04
|
||||
#define ST_PRESS_LPS22HB_ODR_AVL_75HZ_VAL 0x05
|
||||
#define ST_PRESS_LPS22HB_PW_ADDR 0x10
|
||||
#define ST_PRESS_LPS22HB_PW_MASK 0x70
|
||||
#define ST_PRESS_LPS22HB_BDU_ADDR 0x10
|
||||
#define ST_PRESS_LPS22HB_BDU_MASK 0x02
|
||||
#define ST_PRESS_LPS22HB_DRDY_IRQ_ADDR 0x12
|
||||
#define ST_PRESS_LPS22HB_DRDY_IRQ_INT1_MASK 0x04
|
||||
#define ST_PRESS_LPS22HB_DRDY_IRQ_INT2_MASK 0x08
|
||||
#define ST_PRESS_LPS22HB_IHL_IRQ_ADDR 0x12
|
||||
#define ST_PRESS_LPS22HB_IHL_IRQ_MASK 0x80
|
||||
#define ST_PRESS_LPS22HB_MULTIREAD_BIT true
|
||||
|
||||
static const struct iio_chan_spec st_press_1_channels[] = {
|
||||
{
|
||||
.type = IIO_PRESSURE,
|
||||
|
@ -183,6 +203,27 @@ static const struct iio_chan_spec st_press_lps001wp_channels[] = {
|
|||
IIO_CHAN_SOFT_TIMESTAMP(1)
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec st_press_lps22hb_channels[] = {
|
||||
{
|
||||
.type = IIO_PRESSURE,
|
||||
.channel2 = IIO_NO_MOD,
|
||||
.address = ST_PRESS_1_OUT_XL_ADDR,
|
||||
.scan_index = 0,
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
.realbits = 24,
|
||||
.storagebits = 24,
|
||||
.endianness = IIO_LE,
|
||||
},
|
||||
.info_mask_separate =
|
||||
BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
|
||||
.modified = 0,
|
||||
},
|
||||
IIO_CHAN_SOFT_TIMESTAMP(1)
|
||||
};
|
||||
|
||||
static const struct st_sensor_settings st_press_sensors_settings[] = {
|
||||
{
|
||||
.wai = ST_PRESS_LPS331AP_WAI_EXP,
|
||||
|
@ -326,6 +367,51 @@ static const struct st_sensor_settings st_press_sensors_settings[] = {
|
|||
.multi_read_bit = ST_PRESS_LPS25H_MULTIREAD_BIT,
|
||||
.bootime = 2,
|
||||
},
|
||||
{
|
||||
.wai = ST_PRESS_LPS22HB_WAI_EXP,
|
||||
.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
|
||||
.sensors_supported = {
|
||||
[0] = LPS22HB_PRESS_DEV_NAME,
|
||||
},
|
||||
.ch = (struct iio_chan_spec *)st_press_lps22hb_channels,
|
||||
.num_ch = ARRAY_SIZE(st_press_lps22hb_channels),
|
||||
.odr = {
|
||||
.addr = ST_PRESS_LPS22HB_ODR_ADDR,
|
||||
.mask = ST_PRESS_LPS22HB_ODR_MASK,
|
||||
.odr_avl = {
|
||||
{ 1, ST_PRESS_LPS22HB_ODR_AVL_1HZ_VAL, },
|
||||
{ 10, ST_PRESS_LPS22HB_ODR_AVL_10HZ_VAL, },
|
||||
{ 25, ST_PRESS_LPS22HB_ODR_AVL_25HZ_VAL, },
|
||||
{ 50, ST_PRESS_LPS22HB_ODR_AVL_50HZ_VAL, },
|
||||
{ 75, ST_PRESS_LPS22HB_ODR_AVL_75HZ_VAL, },
|
||||
},
|
||||
},
|
||||
.pw = {
|
||||
.addr = ST_PRESS_LPS22HB_PW_ADDR,
|
||||
.mask = ST_PRESS_LPS22HB_PW_MASK,
|
||||
.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
|
||||
},
|
||||
.fs = {
|
||||
.fs_avl = {
|
||||
[0] = {
|
||||
.num = ST_PRESS_FS_AVL_1260MB,
|
||||
.gain = ST_PRESS_KPASCAL_NANO_SCALE,
|
||||
},
|
||||
},
|
||||
},
|
||||
.bdu = {
|
||||
.addr = ST_PRESS_LPS22HB_BDU_ADDR,
|
||||
.mask = ST_PRESS_LPS22HB_BDU_MASK,
|
||||
},
|
||||
.drdy_irq = {
|
||||
.addr = ST_PRESS_LPS22HB_DRDY_IRQ_ADDR,
|
||||
.mask_int1 = ST_PRESS_LPS22HB_DRDY_IRQ_INT1_MASK,
|
||||
.mask_int2 = ST_PRESS_LPS22HB_DRDY_IRQ_INT2_MASK,
|
||||
.addr_ihl = ST_PRESS_LPS22HB_IHL_IRQ_ADDR,
|
||||
.mask_ihl = ST_PRESS_LPS22HB_IHL_IRQ_MASK,
|
||||
},
|
||||
.multi_read_bit = ST_PRESS_LPS22HB_MULTIREAD_BIT,
|
||||
},
|
||||
};
|
||||
|
||||
static int st_press_write_raw(struct iio_dev *indio_dev,
|
||||
|
@ -441,23 +527,24 @@ int st_press_common_probe(struct iio_dev *indio_dev)
|
|||
indio_dev->info = &press_info;
|
||||
mutex_init(&press_data->tb.buf_lock);
|
||||
|
||||
st_sensors_power_enable(indio_dev);
|
||||
err = st_sensors_power_enable(indio_dev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = st_sensors_check_device_support(indio_dev,
|
||||
ARRAY_SIZE(st_press_sensors_settings),
|
||||
st_press_sensors_settings);
|
||||
if (err < 0)
|
||||
return err;
|
||||
goto st_press_power_off;
|
||||
|
||||
press_data->num_data_channels = ST_PRESS_NUMBER_DATA_CHANNELS;
|
||||
press_data->multiread_bit = press_data->sensor_settings->multi_read_bit;
|
||||
indio_dev->channels = press_data->sensor_settings->ch;
|
||||
indio_dev->num_channels = press_data->sensor_settings->num_ch;
|
||||
|
||||
if (press_data->sensor_settings->fs.addr != 0)
|
||||
press_data->current_fullscale =
|
||||
(struct st_sensor_fullscale_avl *)
|
||||
&press_data->sensor_settings->fs.fs_avl[0];
|
||||
press_data->current_fullscale =
|
||||
(struct st_sensor_fullscale_avl *)
|
||||
&press_data->sensor_settings->fs.fs_avl[0];
|
||||
|
||||
press_data->odr = press_data->sensor_settings->odr.odr_avl[0].hz;
|
||||
|
||||
|
@ -469,11 +556,11 @@ int st_press_common_probe(struct iio_dev *indio_dev)
|
|||
|
||||
err = st_sensors_init_sensor(indio_dev, press_data->dev->platform_data);
|
||||
if (err < 0)
|
||||
return err;
|
||||
goto st_press_power_off;
|
||||
|
||||
err = st_press_allocate_ring(indio_dev);
|
||||
if (err < 0)
|
||||
return err;
|
||||
goto st_press_power_off;
|
||||
|
||||
if (irq > 0) {
|
||||
err = st_sensors_allocate_trigger(indio_dev,
|
||||
|
@ -496,6 +583,8 @@ int st_press_common_probe(struct iio_dev *indio_dev)
|
|||
st_sensors_deallocate_trigger(indio_dev);
|
||||
st_press_probe_trigger_error:
|
||||
st_press_deallocate_ring(indio_dev);
|
||||
st_press_power_off:
|
||||
st_sensors_power_disable(indio_dev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
|
|
@ -32,6 +32,10 @@ static const struct of_device_id st_press_of_match[] = {
|
|||
.compatible = "st,lps331ap-press",
|
||||
.data = LPS331AP_PRESS_DEV_NAME,
|
||||
},
|
||||
{
|
||||
.compatible = "st,lps22hb-press",
|
||||
.data = LPS22HB_PRESS_DEV_NAME,
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, st_press_of_match);
|
||||
|
|
|
@ -50,6 +50,7 @@ static const struct spi_device_id st_press_id_table[] = {
|
|||
{ LPS001WP_PRESS_DEV_NAME },
|
||||
{ LPS25H_PRESS_DEV_NAME },
|
||||
{ LPS331AP_PRESS_DEV_NAME },
|
||||
{ LPS22HB_PRESS_DEV_NAME },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, st_press_id_table);
|
||||
|
|
|
@ -461,4 +461,3 @@ module_spi_driver(as3935_driver);
|
|||
MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
|
||||
MODULE_DESCRIPTION("AS3935 lightning sensor");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("spi:as3935");
|
||||
|
|
|
@ -174,6 +174,7 @@ static const struct i2c_device_id tsys02d_id[] = {
|
|||
{"tsys02d", 0},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, tsys02d_id);
|
||||
|
||||
static struct i2c_driver tsys02d_driver = {
|
||||
.probe = tsys02d_probe,
|
||||
|
|
|
@ -24,6 +24,18 @@ config IIO_INTERRUPT_TRIGGER
|
|||
To compile this driver as a module, choose M here: the
|
||||
module will be called iio-trig-interrupt.
|
||||
|
||||
config IIO_TIGHTLOOP_TRIGGER
|
||||
tristate "A kthread based hammering loop trigger"
|
||||
depends on IIO_SW_TRIGGER
|
||||
help
|
||||
An experimental trigger, used to allow sensors to be sampled as fast
|
||||
as possible under the limitations of whatever else is going on.
|
||||
Uses a tight loop in a kthread. Will only work with lower half only
|
||||
trigger consumers.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called iio-trig-loop.
|
||||
|
||||
config IIO_SYSFS_TRIGGER
|
||||
tristate "SYSFS trigger"
|
||||
depends on SYSFS
|
||||
|
|
|
@ -7,3 +7,4 @@
|
|||
obj-$(CONFIG_IIO_HRTIMER_TRIGGER) += iio-trig-hrtimer.o
|
||||
obj-$(CONFIG_IIO_INTERRUPT_TRIGGER) += iio-trig-interrupt.o
|
||||
obj-$(CONFIG_IIO_SYSFS_TRIGGER) += iio-trig-sysfs.o
|
||||
obj-$(CONFIG_IIO_TIGHTLOOP_TRIGGER) += iio-trig-loop.o
|
||||
|
|
|
@ -0,0 +1,143 @@
|
|||
/*
|
||||
* Copyright 2016 Jonathan Cameron <jic23@kernel.org>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*
|
||||
* Based on a mashup of the hrtimer trigger and continuous sampling proposal of
|
||||
* Gregor Boirie <gregor.boirie@parrot.com>
|
||||
*
|
||||
* Note this is still rather experimental and may eat babies.
|
||||
*
|
||||
* Todo
|
||||
* * Protect against connection of devices that 'need' the top half
|
||||
* handler.
|
||||
* * Work out how to run top half handlers in this context if it is
|
||||
* safe to do so (timestamp grabbing for example)
|
||||
*
|
||||
* Tested against a max1363. Used about 33% cpu for the thread and 20%
|
||||
* for generic_buffer piping to /dev/null. Watermark set at 64 on a 128
|
||||
* element kfifo buffer.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/irq_work.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/freezer.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include <linux/iio/sw_trigger.h>
|
||||
|
||||
struct iio_loop_info {
|
||||
struct iio_sw_trigger swt;
|
||||
struct task_struct *task;
|
||||
};
|
||||
|
||||
static struct config_item_type iio_loop_type = {
|
||||
.ct_owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int iio_loop_thread(void *data)
|
||||
{
|
||||
struct iio_trigger *trig = data;
|
||||
|
||||
set_freezable();
|
||||
|
||||
do {
|
||||
iio_trigger_poll_chained(trig);
|
||||
} while (likely(!kthread_freezable_should_stop(NULL)));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iio_loop_trigger_set_state(struct iio_trigger *trig, bool state)
|
||||
{
|
||||
struct iio_loop_info *loop_trig = iio_trigger_get_drvdata(trig);
|
||||
|
||||
if (state) {
|
||||
loop_trig->task = kthread_run(iio_loop_thread,
|
||||
trig, trig->name);
|
||||
if (unlikely(IS_ERR(loop_trig->task))) {
|
||||
dev_err(&trig->dev,
|
||||
"failed to create trigger loop thread\n");
|
||||
return PTR_ERR(loop_trig->task);
|
||||
}
|
||||
} else {
|
||||
kthread_stop(loop_trig->task);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct iio_trigger_ops iio_loop_trigger_ops = {
|
||||
.set_trigger_state = iio_loop_trigger_set_state,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static struct iio_sw_trigger *iio_trig_loop_probe(const char *name)
|
||||
{
|
||||
struct iio_loop_info *trig_info;
|
||||
int ret;
|
||||
|
||||
trig_info = kzalloc(sizeof(*trig_info), GFP_KERNEL);
|
||||
if (!trig_info)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
trig_info->swt.trigger = iio_trigger_alloc("%s", name);
|
||||
if (!trig_info->swt.trigger) {
|
||||
ret = -ENOMEM;
|
||||
goto err_free_trig_info;
|
||||
}
|
||||
|
||||
iio_trigger_set_drvdata(trig_info->swt.trigger, trig_info);
|
||||
trig_info->swt.trigger->ops = &iio_loop_trigger_ops;
|
||||
|
||||
ret = iio_trigger_register(trig_info->swt.trigger);
|
||||
if (ret)
|
||||
goto err_free_trigger;
|
||||
|
||||
iio_swt_group_init_type_name(&trig_info->swt, name, &iio_loop_type);
|
||||
|
||||
return &trig_info->swt;
|
||||
|
||||
err_free_trigger:
|
||||
iio_trigger_free(trig_info->swt.trigger);
|
||||
err_free_trig_info:
|
||||
kfree(trig_info);
|
||||
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
static int iio_trig_loop_remove(struct iio_sw_trigger *swt)
|
||||
{
|
||||
struct iio_loop_info *trig_info;
|
||||
|
||||
trig_info = iio_trigger_get_drvdata(swt->trigger);
|
||||
|
||||
iio_trigger_unregister(swt->trigger);
|
||||
iio_trigger_free(swt->trigger);
|
||||
kfree(trig_info);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct iio_sw_trigger_ops iio_trig_loop_ops = {
|
||||
.probe = iio_trig_loop_probe,
|
||||
.remove = iio_trig_loop_remove,
|
||||
};
|
||||
|
||||
static struct iio_sw_trigger_type iio_trig_loop = {
|
||||
.name = "loop",
|
||||
.owner = THIS_MODULE,
|
||||
.ops = &iio_trig_loop_ops,
|
||||
};
|
||||
|
||||
module_iio_sw_trigger_driver(iio_trig_loop);
|
||||
|
||||
MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>");
|
||||
MODULE_DESCRIPTION("Loop based trigger for the iio subsystem");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:iio-trig-loop");
|
|
@ -251,8 +251,6 @@ struct st_sensor_data {
|
|||
|
||||
#ifdef CONFIG_IIO_BUFFER
|
||||
irqreturn_t st_sensors_trigger_handler(int irq, void *p);
|
||||
|
||||
int st_sensors_get_buffer_element(struct iio_dev *indio_dev, u8 *buf);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_IIO_TRIGGER
|
||||
|
@ -280,7 +278,7 @@ int st_sensors_set_enable(struct iio_dev *indio_dev, bool enable);
|
|||
|
||||
int st_sensors_set_axis_enable(struct iio_dev *indio_dev, u8 axis_enable);
|
||||
|
||||
void st_sensors_power_enable(struct iio_dev *indio_dev);
|
||||
int st_sensors_power_enable(struct iio_dev *indio_dev);
|
||||
|
||||
void st_sensors_power_disable(struct iio_dev *indio_dev);
|
||||
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* Industrial I/O software device interface
|
||||
*
|
||||
* Copyright (c) 2016 Intel Corporation
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __IIO_SW_DEVICE
|
||||
#define __IIO_SW_DEVICE
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/configfs.h>
|
||||
|
||||
#define module_iio_sw_device_driver(__iio_sw_device_type) \
|
||||
module_driver(__iio_sw_device_type, iio_register_sw_device_type, \
|
||||
iio_unregister_sw_device_type)
|
||||
|
||||
struct iio_sw_device_ops;
|
||||
|
||||
struct iio_sw_device_type {
|
||||
const char *name;
|
||||
struct module *owner;
|
||||
const struct iio_sw_device_ops *ops;
|
||||
struct list_head list;
|
||||
struct config_group *group;
|
||||
};
|
||||
|
||||
struct iio_sw_device {
|
||||
struct iio_dev *device;
|
||||
struct iio_sw_device_type *device_type;
|
||||
struct config_group group;
|
||||
};
|
||||
|
||||
struct iio_sw_device_ops {
|
||||
struct iio_sw_device* (*probe)(const char *);
|
||||
int (*remove)(struct iio_sw_device *);
|
||||
};
|
||||
|
||||
static inline
|
||||
struct iio_sw_device *to_iio_sw_device(struct config_item *item)
|
||||
{
|
||||
return container_of(to_config_group(item), struct iio_sw_device,
|
||||
group);
|
||||
}
|
||||
|
||||
int iio_register_sw_device_type(struct iio_sw_device_type *dt);
|
||||
void iio_unregister_sw_device_type(struct iio_sw_device_type *dt);
|
||||
|
||||
struct iio_sw_device *iio_sw_device_create(const char *, const char *);
|
||||
void iio_sw_device_destroy(struct iio_sw_device *);
|
||||
|
||||
int iio_sw_device_type_configfs_register(struct iio_sw_device_type *dt);
|
||||
void iio_sw_device_type_configfs_unregister(struct iio_sw_device_type *dt);
|
||||
|
||||
static inline
|
||||
void iio_swd_group_init_type_name(struct iio_sw_device *d,
|
||||
const char *name,
|
||||
struct config_item_type *type)
|
||||
{
|
||||
#ifdef CONFIG_CONFIGFS_FS
|
||||
config_group_init_type_name(&d->group, name, type);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* __IIO_SW_DEVICE */
|
|
@ -39,6 +39,7 @@ enum iio_chan_type {
|
|||
IIO_RESISTANCE,
|
||||
IIO_PH,
|
||||
IIO_UVINDEX,
|
||||
IIO_ELECTRICALCONDUCTIVITY,
|
||||
};
|
||||
|
||||
enum iio_modifier {
|
||||
|
|
|
@ -1,16 +1,31 @@
|
|||
CC = $(CROSS_COMPILE)gcc
|
||||
CFLAGS += -Wall -g -D_GNU_SOURCE
|
||||
|
||||
all: iio_event_monitor lsiio generic_buffer
|
||||
BINDIR=usr/bin
|
||||
INSTALL_PROGRAM=install -m 755 -p
|
||||
DEL_FILE=rm -f
|
||||
|
||||
all: iio_event_monitor lsiio iio_generic_buffer
|
||||
|
||||
iio_event_monitor: iio_event_monitor.o iio_utils.o
|
||||
|
||||
lsiio: lsiio.o iio_utils.o
|
||||
|
||||
generic_buffer: generic_buffer.o iio_utils.o
|
||||
iio_generic_buffer: iio_generic_buffer.o iio_utils.o
|
||||
|
||||
%.o: %.c iio_utils.h
|
||||
|
||||
install:
|
||||
- mkdir -p $(INSTALL_ROOT)/$(BINDIR)
|
||||
- $(INSTALL_PROGRAM) "iio_event_monitor" "$(INSTALL_ROOT)/$(BINDIR)/iio_event_monitor"
|
||||
- $(INSTALL_PROGRAM) "lsiio" "$(INSTALL_ROOT)/$(BINDIR)/lsiio"
|
||||
- $(INSTALL_PROGRAM) "iio_generic_buffer" "$(INSTALL_ROOT)/$(BINDIR)/iio_generic_buffer"
|
||||
|
||||
uninstall:
|
||||
$(DEL_FILE) "$(INSTALL_ROOT)/$(BINDIR)/iio_event_monitor"
|
||||
$(DEL_FILE) "$(INSTALL_ROOT)/$(BINDIR)/lsiio"
|
||||
$(DEL_FILE) "$(INSTALL_ROOT)/$(BINDIR)/iio_generic_buffer"
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f *.o iio_event_monitor lsiio generic_buffer
|
||||
rm -f *.o iio_event_monitor lsiio iio_generic_buffer
|
||||
|
|
|
@ -32,6 +32,8 @@
|
|||
#include <endian.h>
|
||||
#include <getopt.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include <signal.h>
|
||||
#include "iio_utils.h"
|
||||
|
||||
/**
|
||||
|
@ -249,11 +251,82 @@ void print_usage(void)
|
|||
" -e Disable wait for event (new data)\n"
|
||||
" -g Use trigger-less mode\n"
|
||||
" -l <n> Set buffer length to n samples\n"
|
||||
" -n <name> Set device name (mandatory)\n"
|
||||
" -t <name> Set trigger name\n"
|
||||
" --device-name -n <name>\n"
|
||||
" --device-num -N <num>\n"
|
||||
" Set device by name or number (mandatory)\n"
|
||||
" --trigger-name -t <name>\n"
|
||||
" --trigger-num -T <num>\n"
|
||||
" Set trigger by name or number\n"
|
||||
" -w <n> Set delay between reads in us (event-less mode)\n");
|
||||
}
|
||||
|
||||
enum autochan autochannels = AUTOCHANNELS_DISABLED;
|
||||
char *dev_dir_name = NULL;
|
||||
char *buf_dir_name = NULL;
|
||||
bool current_trigger_set = false;
|
||||
|
||||
void cleanup(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Disable trigger */
|
||||
if (dev_dir_name && current_trigger_set) {
|
||||
/* Disconnect the trigger - just write a dummy name. */
|
||||
ret = write_sysfs_string("trigger/current_trigger",
|
||||
dev_dir_name, "NULL");
|
||||
if (ret < 0)
|
||||
fprintf(stderr, "Failed to disable trigger: %s\n",
|
||||
strerror(-ret));
|
||||
current_trigger_set = false;
|
||||
}
|
||||
|
||||
/* Disable buffer */
|
||||
if (buf_dir_name) {
|
||||
ret = write_sysfs_int("enable", buf_dir_name, 0);
|
||||
if (ret < 0)
|
||||
fprintf(stderr, "Failed to disable buffer: %s\n",
|
||||
strerror(-ret));
|
||||
}
|
||||
|
||||
/* Disable channels if auto-enabled */
|
||||
if (dev_dir_name && autochannels == AUTOCHANNELS_ACTIVE) {
|
||||
ret = enable_disable_all_channels(dev_dir_name, 0);
|
||||
if (ret)
|
||||
fprintf(stderr, "Failed to disable all channels\n");
|
||||
autochannels = AUTOCHANNELS_DISABLED;
|
||||
}
|
||||
}
|
||||
|
||||
void sig_handler(int signum)
|
||||
{
|
||||
fprintf(stderr, "Caught signal %d\n", signum);
|
||||
cleanup();
|
||||
exit(-signum);
|
||||
}
|
||||
|
||||
void register_cleanup(void)
|
||||
{
|
||||
struct sigaction sa = { .sa_handler = sig_handler };
|
||||
const int signums[] = { SIGINT, SIGTERM, SIGABRT };
|
||||
int ret, i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(signums); ++i) {
|
||||
ret = sigaction(signums[i], &sa, NULL);
|
||||
if (ret) {
|
||||
perror("Failed to register signal handler");
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const struct option longopts[] = {
|
||||
{ "device-name", 1, 0, 'n' },
|
||||
{ "device-num", 1, 0, 'N' },
|
||||
{ "trigger-name", 1, 0, 't' },
|
||||
{ "trigger-num", 1, 0, 'T' },
|
||||
{ },
|
||||
};
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
unsigned long num_loops = 2;
|
||||
|
@ -261,26 +334,25 @@ int main(int argc, char **argv)
|
|||
unsigned long buf_len = 128;
|
||||
|
||||
int ret, c, i, j, toread;
|
||||
int fp;
|
||||
int fp = -1;
|
||||
|
||||
int num_channels;
|
||||
int num_channels = 0;
|
||||
char *trigger_name = NULL, *device_name = NULL;
|
||||
char *dev_dir_name, *buf_dir_name;
|
||||
|
||||
int datardytrigger = 1;
|
||||
char *data;
|
||||
char *data = NULL;
|
||||
ssize_t read_size;
|
||||
int dev_num, trig_num;
|
||||
char *buffer_access;
|
||||
int dev_num = -1, trig_num;
|
||||
char *buffer_access = NULL;
|
||||
int scan_size;
|
||||
int noevents = 0;
|
||||
int notrigger = 0;
|
||||
enum autochan autochannels = AUTOCHANNELS_DISABLED;
|
||||
char *dummy;
|
||||
|
||||
struct iio_channel_info *channels;
|
||||
|
||||
while ((c = getopt(argc, argv, "ac:egl:n:t:w:")) != -1) {
|
||||
register_cleanup();
|
||||
|
||||
while ((c = getopt_long(argc, argv, "ac:egl:n:N:t:T:w:", longopts, NULL)) != -1) {
|
||||
switch (c) {
|
||||
case 'a':
|
||||
autochannels = AUTOCHANNELS_ENABLED;
|
||||
|
@ -288,8 +360,10 @@ int main(int argc, char **argv)
|
|||
case 'c':
|
||||
errno = 0;
|
||||
num_loops = strtoul(optarg, &dummy, 10);
|
||||
if (errno)
|
||||
return -errno;
|
||||
if (errno) {
|
||||
ret = -errno;
|
||||
goto error;
|
||||
}
|
||||
|
||||
break;
|
||||
case 'e':
|
||||
|
@ -301,49 +375,102 @@ int main(int argc, char **argv)
|
|||
case 'l':
|
||||
errno = 0;
|
||||
buf_len = strtoul(optarg, &dummy, 10);
|
||||
if (errno)
|
||||
return -errno;
|
||||
if (errno) {
|
||||
ret = -errno;
|
||||
goto error;
|
||||
}
|
||||
|
||||
break;
|
||||
case 'n':
|
||||
device_name = optarg;
|
||||
device_name = strdup(optarg);
|
||||
break;
|
||||
case 'N':
|
||||
errno = 0;
|
||||
dev_num = strtoul(optarg, &dummy, 10);
|
||||
if (errno) {
|
||||
ret = -errno;
|
||||
goto error;
|
||||
}
|
||||
break;
|
||||
case 't':
|
||||
trigger_name = optarg;
|
||||
datardytrigger = 0;
|
||||
trigger_name = strdup(optarg);
|
||||
break;
|
||||
case 'T':
|
||||
errno = 0;
|
||||
trig_num = strtoul(optarg, &dummy, 10);
|
||||
if (errno)
|
||||
return -errno;
|
||||
break;
|
||||
case 'w':
|
||||
errno = 0;
|
||||
timedelay = strtoul(optarg, &dummy, 10);
|
||||
if (errno)
|
||||
return -errno;
|
||||
if (errno) {
|
||||
ret = -errno;
|
||||
goto error;
|
||||
}
|
||||
break;
|
||||
case '?':
|
||||
print_usage();
|
||||
return -1;
|
||||
ret = -1;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
if (!device_name) {
|
||||
fprintf(stderr, "Device name not set\n");
|
||||
print_usage();
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Find the device requested */
|
||||
dev_num = find_type_by_name(device_name, "iio:device");
|
||||
if (dev_num < 0) {
|
||||
fprintf(stderr, "Failed to find the %s\n", device_name);
|
||||
return dev_num;
|
||||
if (dev_num < 0 && !device_name) {
|
||||
fprintf(stderr, "Device not set\n");
|
||||
print_usage();
|
||||
ret = -1;
|
||||
goto error;
|
||||
} else if (dev_num >= 0 && device_name) {
|
||||
fprintf(stderr, "Only one of --device-num or --device-name needs to be set\n");
|
||||
print_usage();
|
||||
ret = -1;
|
||||
goto error;
|
||||
} else if (dev_num < 0) {
|
||||
dev_num = find_type_by_name(device_name, "iio:device");
|
||||
if (dev_num < 0) {
|
||||
fprintf(stderr, "Failed to find the %s\n", device_name);
|
||||
ret = dev_num;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
printf("iio device number being used is %d\n", dev_num);
|
||||
|
||||
ret = asprintf(&dev_dir_name, "%siio:device%d", iio_dir, dev_num);
|
||||
if (ret < 0)
|
||||
return -ENOMEM;
|
||||
/* Fetch device_name if specified by number */
|
||||
if (!device_name) {
|
||||
device_name = malloc(IIO_MAX_NAME_LENGTH);
|
||||
if (!device_name) {
|
||||
ret = -ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
ret = read_sysfs_string("name", dev_dir_name, device_name);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "Failed to read name of device %d\n", dev_num);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
if (!notrigger) {
|
||||
if (notrigger) {
|
||||
printf("trigger-less mode selected\n");
|
||||
} if (trig_num > 0) {
|
||||
char *trig_dev_name;
|
||||
ret = asprintf(&trig_dev_name, "%strigger%d", iio_dir, trig_num);
|
||||
if (ret < 0) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
trigger_name = malloc(IIO_MAX_NAME_LENGTH);
|
||||
ret = read_sysfs_string("name", trig_dev_name, trigger_name);
|
||||
free(trig_dev_name);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "Failed to read trigger%d name from\n", trig_num);
|
||||
return ret;
|
||||
}
|
||||
printf("iio trigger number being used is %d\n", trig_num);
|
||||
} else {
|
||||
if (!trigger_name) {
|
||||
/*
|
||||
* Build the trigger name. If it is device associated
|
||||
|
@ -354,7 +481,7 @@ int main(int argc, char **argv)
|
|||
"%s-dev%d", device_name, dev_num);
|
||||
if (ret < 0) {
|
||||
ret = -ENOMEM;
|
||||
goto error_free_dev_dir_name;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -367,7 +494,7 @@ int main(int argc, char **argv)
|
|||
"%s-trigger", device_name);
|
||||
if (ret < 0) {
|
||||
ret = -ENOMEM;
|
||||
goto error_free_dev_dir_name;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -376,12 +503,10 @@ int main(int argc, char **argv)
|
|||
fprintf(stderr, "Failed to find the trigger %s\n",
|
||||
trigger_name);
|
||||
ret = trig_num;
|
||||
goto error_free_triggername;
|
||||
goto error;
|
||||
}
|
||||
|
||||
printf("iio trigger number being used is %d\n", trig_num);
|
||||
} else {
|
||||
printf("trigger-less mode selected\n");
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -392,7 +517,7 @@ int main(int argc, char **argv)
|
|||
if (ret) {
|
||||
fprintf(stderr, "Problem reading scan element information\n"
|
||||
"diag %s\n", dev_dir_name);
|
||||
goto error_free_triggername;
|
||||
goto error;
|
||||
}
|
||||
if (num_channels && autochannels == AUTOCHANNELS_ENABLED) {
|
||||
fprintf(stderr, "Auto-channels selected but some channels "
|
||||
|
@ -407,7 +532,7 @@ int main(int argc, char **argv)
|
|||
ret = enable_disable_all_channels(dev_dir_name, 1);
|
||||
if (ret) {
|
||||
fprintf(stderr, "Failed to enable all channels\n");
|
||||
goto error_free_triggername;
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* This flags that we need to disable the channels again */
|
||||
|
@ -419,12 +544,12 @@ int main(int argc, char **argv)
|
|||
fprintf(stderr, "Problem reading scan element "
|
||||
"information\n"
|
||||
"diag %s\n", dev_dir_name);
|
||||
goto error_disable_channels;
|
||||
goto error;
|
||||
}
|
||||
if (!num_channels) {
|
||||
fprintf(stderr, "Still no channels after "
|
||||
"auto-enabling, giving up\n");
|
||||
goto error_disable_channels;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -436,7 +561,7 @@ int main(int argc, char **argv)
|
|||
"/*_en or pass -a to autoenable channels and "
|
||||
"try again.\n", dev_dir_name);
|
||||
ret = -ENOENT;
|
||||
goto error_free_triggername;
|
||||
goto error;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -448,7 +573,7 @@ int main(int argc, char **argv)
|
|||
"%siio:device%d/buffer", iio_dir, dev_num);
|
||||
if (ret < 0) {
|
||||
ret = -ENOMEM;
|
||||
goto error_free_channels;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!notrigger) {
|
||||
|
@ -463,34 +588,34 @@ int main(int argc, char **argv)
|
|||
if (ret < 0) {
|
||||
fprintf(stderr,
|
||||
"Failed to write current_trigger file\n");
|
||||
goto error_free_buf_dir_name;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
/* Setup ring buffer parameters */
|
||||
ret = write_sysfs_int("length", buf_dir_name, buf_len);
|
||||
if (ret < 0)
|
||||
goto error_free_buf_dir_name;
|
||||
goto error;
|
||||
|
||||
/* Enable the buffer */
|
||||
ret = write_sysfs_int("enable", buf_dir_name, 1);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr,
|
||||
"Failed to enable buffer: %s\n", strerror(-ret));
|
||||
goto error_free_buf_dir_name;
|
||||
goto error;
|
||||
}
|
||||
|
||||
scan_size = size_from_channelarray(channels, num_channels);
|
||||
data = malloc(scan_size * buf_len);
|
||||
if (!data) {
|
||||
ret = -ENOMEM;
|
||||
goto error_free_buf_dir_name;
|
||||
goto error;
|
||||
}
|
||||
|
||||
ret = asprintf(&buffer_access, "/dev/iio:device%d", dev_num);
|
||||
if (ret < 0) {
|
||||
ret = -ENOMEM;
|
||||
goto error_free_data;
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Attempt to open non blocking the access dev */
|
||||
|
@ -498,7 +623,7 @@ int main(int argc, char **argv)
|
|||
if (fp == -1) { /* TODO: If it isn't there make the node */
|
||||
ret = -errno;
|
||||
fprintf(stderr, "Failed to open %s\n", buffer_access);
|
||||
goto error_free_buffer_access;
|
||||
goto error;
|
||||
}
|
||||
|
||||
for (j = 0; j < num_loops; j++) {
|
||||
|
@ -511,7 +636,7 @@ int main(int argc, char **argv)
|
|||
ret = poll(&pfd, 1, -1);
|
||||
if (ret < 0) {
|
||||
ret = -errno;
|
||||
goto error_close_buffer_access;
|
||||
goto error;
|
||||
} else if (ret == 0) {
|
||||
continue;
|
||||
}
|
||||
|
@ -536,45 +661,21 @@ int main(int argc, char **argv)
|
|||
num_channels);
|
||||
}
|
||||
|
||||
/* Stop the buffer */
|
||||
ret = write_sysfs_int("enable", buf_dir_name, 0);
|
||||
if (ret < 0)
|
||||
goto error_close_buffer_access;
|
||||
error:
|
||||
cleanup();
|
||||
|
||||
if (!notrigger)
|
||||
/* Disconnect the trigger - just write a dummy name. */
|
||||
ret = write_sysfs_string("trigger/current_trigger",
|
||||
dev_dir_name, "NULL");
|
||||
if (ret < 0)
|
||||
fprintf(stderr, "Failed to write to %s\n",
|
||||
dev_dir_name);
|
||||
|
||||
error_close_buffer_access:
|
||||
if (close(fp) == -1)
|
||||
if (fp >= 0 && close(fp) == -1)
|
||||
perror("Failed to close buffer");
|
||||
|
||||
error_free_buffer_access:
|
||||
free(buffer_access);
|
||||
error_free_data:
|
||||
free(data);
|
||||
error_free_buf_dir_name:
|
||||
free(buf_dir_name);
|
||||
error_free_channels:
|
||||
for (i = num_channels - 1; i >= 0; i--) {
|
||||
free(channels[i].name);
|
||||
free(channels[i].generic_name);
|
||||
}
|
||||
free(channels);
|
||||
error_free_triggername:
|
||||
if (datardytrigger)
|
||||
free(trigger_name);
|
||||
error_disable_channels:
|
||||
if (autochannels == AUTOCHANNELS_ACTIVE) {
|
||||
ret = enable_disable_all_channels(dev_dir_name, 0);
|
||||
if (ret)
|
||||
fprintf(stderr, "Failed to disable all channels\n");
|
||||
}
|
||||
error_free_dev_dir_name:
|
||||
free(trigger_name);
|
||||
free(device_name);
|
||||
free(dev_dir_name);
|
||||
|
||||
return ret;
|
Loading…
Reference in New Issue