First round of new features, device support and cleanups for IIO in the 4.9 cycle.
Device support * ak8974 - New driver and bindings for this 2009 vintage magnetometer (it was very popular back then!) * atlas-ph-sensor - ORP sensor support(I had to look up what one of these was) * cio-dac - New driver for Measurement Computing DAC boards * dmard06 - New driver for Domintech DMARDO6 accelerometer. Also vendor prefix. * dmard09 - New driver for Domintech DMARD09 accelerometer. * maxim-thermocouple - max6675 and max31855 new driver * mt6577 auxdac - new driver for this Mediatek chip mt2701, mt6577 and mt8173 have this hardware. * ti-adc161s626 - new driver for this TI single channel differential ADC. * vcnl4000 - support vcnl4010 and vcnl4020 which are compatible for all features currently supported by this driver. New features * Core - Allow retrieving of underlying iio_dev from a callback buffer handle. This is needed to allow client drivers to perform operations such as configuring the trigger used. * hid-sensors - asynchronous resume support to avoid really long resume times. * kxcjk-1013 - add the mysterious KIOX000A ACPI id seen in the wild. * Tools - lsiio now enumerates processed as well as raw channels. Cleanup * ad7298 - use iio_device_claim_direct_mode and friends to simplify locking around mode switching and drop some boilerplate. * ad7793 - use iio_device_claim_direct_mode and friends to simplify locking around mode switching and drop some boilerplate. *ade7854
- checkpatch fixups (alignment of parameters) * atlas-ph-sensor - use iio_device_claim_direct_mode and friends to simplify locking around mode switching and drop some boilerplate. - Switch to REGCACHE_NONE as there are no useful register to cache. * bma180 - use iio_device_claim_direct_mode and friends to simplify locking around mode switching and drop some boilerplate. * hdc100x - Add mention of the HDC1000 and HDC1008 to the Kconfig help text. * isl29018 - Add driver specific prefixes to defines and function names. - Remove excessive logging. - Drop newlines which add nothing to readability. - General tidying up of comments. - Drop I2C_CLASS_HWMON as irrelevant to driver. * isl29028 - Add driver specific prefixes to defines, enums and function names. - Drop comma's from available attribute output as not ABI compliant. - Drop I2C_CLASS_HWMON as irrelevant to driver. * kxsd9 - devicetree bindings. * mag3110 - This one wasn't locking to protect against mode switches during raw_reads. Use the iio_claim_direct_mode function to fix this buglet. * maxim-theromcouple - Fix missing selects for triggered buffer support in Kconfig. * nau7802 - Use complete instead of complete_all as only one completion at a time. * sx9500 - Use complete instead of complete_all as only one completion at a time. * us5182d - Add a missing error code asignment instead of checking the result of an already checked statement. * vcnl4000 - Use BIT macro where appropriate. - Refactor return codes in read_raw callback. - Add some missing locking for concurrent accesses to the device. -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iQIuBAABCAAYBQJXvMBjERxqaWMyM0BrZXJuZWwub3JnAAoJEFSFNJnE9BaInZAP /0yQg+tm9eBpjaVAdfV2ecuf0hVNdCjFg/7n5MLuSN1aDTPRn3O3nuwp8R8ARoe+ 7ctBFTIOJVqQpLYpunde1iJcOtEk2DIJEnxZQJjExOgn7AwIS4fgqSZQP7u5vDoT xrZe8nf5fcvMa1KIvWAadDNflYnn+Ug464Vt8tTdxL6XBIj0xcf8i62aS44XgGwS 6boHXW0pVg1/QtvJmuajVCdxSouZyikDXWEpJx0UUlLmv2enF+KaJnKVwfbQ7rf/ UG5ZhdOarbD4miXm8YC+x46v9FeWysatVpMu7Ch5LHrVVvBQCjIubcsZX/qH9cbS 5VfV00NENHkNEnUL8luVAbLbwxLSq9pFy3LjJOzWmGlBQxT8X6uinA3XbZoqdouf 5qu+TFUg1RDkL/6GSOsZ4EAGJfY7D9bCbQ0LQUaqPfcdAFZToE8ldLHapU1GtYjt Tj0xUxlzQ3fsRH6hTyhy2goAw0s88ruxcUpld8chCd2JWWD2OZJf7TzHGJ2GuuAj 2yIYXjlVSKlM9yNCrFOyBrKxeg9n5eQI2f9KBiZZpNzxO2dFwaJkAiMF09sleTVv 7O58g86ywrohp0eT/1sX6qYENJhZROPJGFMbYVHXhWwD1tqGs69zFVGa6ndL1i7w MXJ7c+1XjnaAuHEIPRyGZ6/Np27z6Jz0CpxGB4CyHseS =d6Kh -----END PGP SIGNATURE----- Merge tag 'iio-for-4.9a' of git://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio into work-testing Jonathan writes: First round of new features, device support and cleanups for IIO in the 4.9 cycle. Device support * ak8974 - New driver and bindings for this 2009 vintage magnetometer (it was very popular back then!) * atlas-ph-sensor - ORP sensor support(I had to look up what one of these was) * cio-dac - New driver for Measurement Computing DAC boards * dmard06 - New driver for Domintech DMARDO6 accelerometer. Also vendor prefix. * dmard09 - New driver for Domintech DMARD09 accelerometer. * maxim-thermocouple - max6675 and max31855 new driver * mt6577 auxdac - new driver for this Mediatek chip mt2701, mt6577 and mt8173 have this hardware. * ti-adc161s626 - new driver for this TI single channel differential ADC. * vcnl4000 - support vcnl4010 and vcnl4020 which are compatible for all features currently supported by this driver. New features * Core - Allow retrieving of underlying iio_dev from a callback buffer handle. This is needed to allow client drivers to perform operations such as configuring the trigger used. * hid-sensors - asynchronous resume support to avoid really long resume times. * kxcjk-1013 - add the mysterious KIOX000A ACPI id seen in the wild. * Tools - lsiio now enumerates processed as well as raw channels. Cleanup * ad7298 - use iio_device_claim_direct_mode and friends to simplify locking around mode switching and drop some boilerplate. * ad7793 - use iio_device_claim_direct_mode and friends to simplify locking around mode switching and drop some boilerplate. *ade7854
- checkpatch fixups (alignment of parameters) * atlas-ph-sensor - use iio_device_claim_direct_mode and friends to simplify locking around mode switching and drop some boilerplate. - Switch to REGCACHE_NONE as there are no useful register to cache. * bma180 - use iio_device_claim_direct_mode and friends to simplify locking around mode switching and drop some boilerplate. * hdc100x - Add mention of the HDC1000 and HDC1008 to the Kconfig help text. * isl29018 - Add driver specific prefixes to defines and function names. - Remove excessive logging. - Drop newlines which add nothing to readability. - General tidying up of comments. - Drop I2C_CLASS_HWMON as irrelevant to driver. * isl29028 - Add driver specific prefixes to defines, enums and function names. - Drop comma's from available attribute output as not ABI compliant. - Drop I2C_CLASS_HWMON as irrelevant to driver. * kxsd9 - devicetree bindings. * mag3110 - This one wasn't locking to protect against mode switches during raw_reads. Use the iio_claim_direct_mode function to fix this buglet. * maxim-theromcouple - Fix missing selects for triggered buffer support in Kconfig. * nau7802 - Use complete instead of complete_all as only one completion at a time. * sx9500 - Use complete instead of complete_all as only one completion at a time. * us5182d - Add a missing error code asignment instead of checking the result of an already checked statement. * vcnl4000 - Use BIT macro where appropriate. - Refactor return codes in read_raw callback. - Add some missing locking for concurrent accesses to the device.
This commit is contained in:
commit
d5bd1eba19
|
@ -38,6 +38,7 @@ dallas,ds4510 CPU Supervisor with Nonvolatile Memory and Programmable I/O
|
|||
dallas,ds75 Digital Thermometer and Thermostat
|
||||
dlg,da9053 DA9053: flexible system level PMIC with multicore support
|
||||
dlg,da9063 DA9063: system PMIC for quad-core application processors
|
||||
domintech,dmard09 DMARD09: 3-axis Accelerometer
|
||||
epson,rx8010 I2C-BUS INTERFACE REAL TIME CLOCK MODULE
|
||||
epson,rx8025 High-Stability. I2C-Bus INTERFACE REAL TIME CLOCK MODULE
|
||||
epson,rx8581 I2C-BUS INTERFACE REAL TIME CLOCK MODULE
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
Device tree bindings for Domintech DMARD05, DMARD06, DMARD07 accelerometers
|
||||
|
||||
Required properties:
|
||||
- compatible : Should be "domintech,dmard05"
|
||||
or "domintech,dmard06"
|
||||
or "domintech,dmard07"
|
||||
- reg : I2C address of the chip. Should be 0x1c
|
||||
|
||||
Example:
|
||||
&i2c1 {
|
||||
/* ... */
|
||||
|
||||
accelerometer@1c {
|
||||
compatible = "domintech,dmard06";
|
||||
reg = <0x1c>;
|
||||
};
|
||||
|
||||
/* ... */
|
||||
};
|
|
@ -0,0 +1,22 @@
|
|||
Kionix KXSD9 Accelerometer device tree bindings
|
||||
|
||||
Required properties:
|
||||
- compatible: should be set to "kionix,kxsd9"
|
||||
- reg: i2c slave address
|
||||
|
||||
Optional properties:
|
||||
- vdd-supply: The input supply for VDD
|
||||
- iovdd-supply: The input supply for IOVDD
|
||||
- interrupts: The movement detection interrupt
|
||||
- mount-matrix: See mount-matrix.txt
|
||||
|
||||
Example:
|
||||
|
||||
kxsd9@18 {
|
||||
compatible = "kionix,kxsd9";
|
||||
reg = <0x18>;
|
||||
interrupt-parent = <&foo>;
|
||||
interrupts = <57 IRQ_TYPE_EDGE_FALLING>;
|
||||
iovdd-supply = <&bar>;
|
||||
vdd-supply = <&baz>;
|
||||
};
|
|
@ -0,0 +1,29 @@
|
|||
* Mediatek AUXADC - Analog to Digital Converter on Mediatek mobile soc (mt65xx/mt81xx/mt27xx)
|
||||
===============
|
||||
|
||||
The Auxiliary Analog/Digital Converter (AUXADC) is an ADC found
|
||||
in some Mediatek SoCs which among other things measures the temperatures
|
||||
in the SoC. It can be used directly with register accesses, but it is also
|
||||
used by thermal controller which reads the temperatures from the AUXADC
|
||||
directly via its own bus interface. See
|
||||
Documentation/devicetree/bindings/thermal/mediatek-thermal.txt
|
||||
for the Thermal Controller which holds a phandle to the AUXADC.
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be one of:
|
||||
- "mediatek,mt2701-auxadc": For MT2701 family of SoCs
|
||||
- "mediatek,mt8173-auxadc": For MT8173 family of SoCs
|
||||
- reg: Address range of the AUXADC unit.
|
||||
- clocks: Should contain a clock specifier for each entry in clock-names
|
||||
- clock-names: Should contain "main".
|
||||
- #io-channel-cells: Should be 1, see ../iio-bindings.txt
|
||||
|
||||
Example:
|
||||
|
||||
auxadc: adc@11001000 {
|
||||
compatible = "mediatek,mt2701-auxadc";
|
||||
reg = <0 0x11001000 0 0x1000>;
|
||||
clocks = <&pericfg CLK_PERI_AUXADC>;
|
||||
clock-names = "main";
|
||||
#io-channel-cells = <1>;
|
||||
};
|
|
@ -0,0 +1,16 @@
|
|||
* Texas Instruments ADC141S626 and ADC161S626 chips
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "ti,adc141s626" or "ti,adc161s626"
|
||||
- reg: spi chip select number for the device
|
||||
|
||||
Recommended properties:
|
||||
- spi-max-frequency: Definition as per
|
||||
Documentation/devicetree/bindings/spi/spi-bus.txt
|
||||
|
||||
Example:
|
||||
adc@0 {
|
||||
compatible = "ti,adc161s626";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <4300000>;
|
||||
};
|
|
@ -0,0 +1,22 @@
|
|||
* Atlas Scientific ORP-SM OEM sensor
|
||||
|
||||
https://www.atlas-scientific.com/_files/_datasheets/_oem/ORP_oem_datasheet.pdf
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: must be "atlas,orp-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@66 {
|
||||
compatible = "atlas,orp-sm";
|
||||
reg = <0x66>;
|
||||
interrupt-parent = <&gpio1>;
|
||||
interrupts = <16 2>;
|
||||
};
|
|
@ -0,0 +1,29 @@
|
|||
* Asahi Kasei AK8974 magnetometer sensor
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : should be "asahi-kasei,ak8974"
|
||||
- reg : the I2C address of the magnetometer
|
||||
|
||||
Optional properties:
|
||||
|
||||
- avdd-supply: regulator supply for the analog voltage
|
||||
(see regulator/regulator.txt)
|
||||
- dvdd-supply: regulator supply for the digital voltage
|
||||
(see regulator/regulator.txt)
|
||||
- interrupts: data ready (DRDY) and interrupt (INT1) lines
|
||||
from the chip, the DRDY interrupt must be placed first.
|
||||
The interrupts can be triggered on rising or falling
|
||||
edges alike.
|
||||
- mount-matrix: an optional 3x3 mounting rotation matrix
|
||||
|
||||
Example:
|
||||
|
||||
ak8974@0f {
|
||||
compatible = "asahi-kasei,ak8974";
|
||||
reg = <0x0f>;
|
||||
avdd-supply = <&foo_reg>;
|
||||
dvdd-supply = <&bar_reg>;
|
||||
interrupts = <0 IRQ_TYPE_EDGE_RISING>,
|
||||
<1 IRQ_TYPE_EDGE_RISING>;
|
||||
};
|
|
@ -0,0 +1,21 @@
|
|||
Maxim thermocouple support
|
||||
|
||||
* https://datasheets.maximintegrated.com/en/ds/MAX6675.pdf
|
||||
* https://datasheets.maximintegrated.com/en/ds/MAX31855.pdf
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: must be "maxim,max31855" or "maxim,max6675"
|
||||
- reg: SPI chip select number for the device
|
||||
- spi-max-frequency: must be 4300000
|
||||
- spi-cpha: must be defined for max6675 to enable SPI mode 1
|
||||
|
||||
Refer to spi/spi-bus.txt for generic SPI slave bindings.
|
||||
|
||||
Example:
|
||||
|
||||
max31855@0 {
|
||||
compatible = "maxim,max31855";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <4300000>;
|
||||
};
|
|
@ -1,21 +0,0 @@
|
|||
MediaTek AUXADC
|
||||
===============
|
||||
|
||||
The Auxiliary Analog/Digital Converter (AUXADC) is an ADC found
|
||||
in some Mediatek SoCs which among other things measures the temperatures
|
||||
in the SoC. It can be used directly with register accesses, but it is also
|
||||
used by thermal controller which reads the temperatures from the AUXADC
|
||||
directly via its own bus interface. See
|
||||
Documentation/devicetree/bindings/thermal/mediatek-thermal.txt
|
||||
for the Thermal Controller which holds a phandle to the AUXADC.
|
||||
|
||||
Required properties:
|
||||
- compatible: Must be "mediatek,mt8173-auxadc"
|
||||
- reg: Address range of the AUXADC unit
|
||||
|
||||
Example:
|
||||
|
||||
auxadc: auxadc@11001000 {
|
||||
compatible = "mediatek,mt8173-auxadc";
|
||||
reg = <0 0x11001000 0 0x1000>;
|
||||
};
|
|
@ -75,6 +75,7 @@ digilent Diglent, Inc.
|
|||
dlg Dialog Semiconductor
|
||||
dlink D-Link Corporation
|
||||
dmo Data Modul AG
|
||||
domintech Domintech Co., Ltd.
|
||||
dptechnics DPTechnics
|
||||
dragino Dragino Technology Co., Limited
|
||||
ea Embedded Artists AB
|
||||
|
|
13
MAINTAINERS
13
MAINTAINERS
|
@ -1965,6 +1965,13 @@ S: Maintained
|
|||
F: drivers/media/i2c/as3645a.c
|
||||
F: include/media/i2c/as3645a.h
|
||||
|
||||
ASAHI KASEI AK8974 DRIVER
|
||||
M: Linus Walleij <linus.walleij@linaro.org>
|
||||
L: linux-iio@vger.kernel.org
|
||||
W: http://www.akm.com/
|
||||
S: Supported
|
||||
F: drivers/iio/magnetometer/ak8974.c
|
||||
|
||||
ASC7621 HARDWARE MONITOR DRIVER
|
||||
M: George Joseph <george.joseph@fairview5.com>
|
||||
L: linux-hwmon@vger.kernel.org
|
||||
|
@ -7497,6 +7504,12 @@ L: linux-iio@vger.kernel.org
|
|||
S: Maintained
|
||||
F: drivers/iio/potentiometer/mcp4531.c
|
||||
|
||||
MEASUREMENT COMPUTING CIO-DAC IIO DRIVER
|
||||
M: William Breathitt Gray <vilhelm.gray@gmail.com>
|
||||
L: linux-iio@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/iio/dac/cio-dac.c
|
||||
|
||||
MEDIA DRIVERS FOR RENESAS - FCP
|
||||
M: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
|
||||
L: linux-media@vger.kernel.org
|
||||
|
|
|
@ -50,6 +50,27 @@ config BMC150_ACCEL_SPI
|
|||
tristate
|
||||
select REGMAP_SPI
|
||||
|
||||
config DMARD06
|
||||
tristate "Domintech DMARD06 Digital Accelerometer Driver"
|
||||
depends on OF || COMPILE_TEST
|
||||
depends on I2C
|
||||
help
|
||||
Say yes here to build support for the Domintech low-g tri-axial
|
||||
digital accelerometers: DMARD05, DMARD06, DMARD07.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called dmard06.
|
||||
|
||||
config DMARD09
|
||||
tristate "Domintech DMARD09 3-axis Accelerometer Driver"
|
||||
depends on I2C
|
||||
help
|
||||
Say yes here to get support for the Domintech DMARD09 3-axis
|
||||
accelerometer.
|
||||
|
||||
Choosing M will build the driver as a module. If so, the module
|
||||
will be called dmard09.
|
||||
|
||||
config HID_SENSOR_ACCEL_3D
|
||||
depends on HID_SENSOR_HUB
|
||||
select IIO_BUFFER
|
||||
|
|
|
@ -8,6 +8,8 @@ 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
|
||||
obj-$(CONFIG_DMARD06) += dmard06.o
|
||||
obj-$(CONFIG_DMARD09) += dmard09.o
|
||||
obj-$(CONFIG_HID_SENSOR_ACCEL_3D) += hid-sensor-accel-3d.o
|
||||
obj-$(CONFIG_KXCJK1013) += kxcjk-1013.o
|
||||
obj-$(CONFIG_KXSD9) += kxsd9.o
|
||||
|
|
|
@ -469,13 +469,14 @@ static int bma180_read_raw(struct iio_dev *indio_dev,
|
|||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = iio_device_claim_direct_mode(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&data->mutex);
|
||||
if (iio_buffer_enabled(indio_dev)) {
|
||||
mutex_unlock(&data->mutex);
|
||||
return -EBUSY;
|
||||
}
|
||||
ret = bma180_get_data_reg(data, chan->scan_index);
|
||||
mutex_unlock(&data->mutex);
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = sign_extend32(ret >> chan->scan_type.shift,
|
||||
|
|
|
@ -0,0 +1,241 @@
|
|||
/*
|
||||
* IIO driver for Domintech DMARD06 accelerometer
|
||||
*
|
||||
* Copyright (C) 2016 Aleksei Mamlin <mamlinav@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/iio/iio.h>
|
||||
|
||||
#define DMARD06_DRV_NAME "dmard06"
|
||||
|
||||
/* Device data registers */
|
||||
#define DMARD06_CHIP_ID_REG 0x0f
|
||||
#define DMARD06_TOUT_REG 0x40
|
||||
#define DMARD06_XOUT_REG 0x41
|
||||
#define DMARD06_YOUT_REG 0x42
|
||||
#define DMARD06_ZOUT_REG 0x43
|
||||
#define DMARD06_CTRL1_REG 0x44
|
||||
|
||||
/* Device ID value */
|
||||
#define DMARD05_CHIP_ID 0x05
|
||||
#define DMARD06_CHIP_ID 0x06
|
||||
#define DMARD07_CHIP_ID 0x07
|
||||
|
||||
/* Device values */
|
||||
#define DMARD05_AXIS_SCALE_VAL 15625
|
||||
#define DMARD06_AXIS_SCALE_VAL 31250
|
||||
#define DMARD06_TEMP_CENTER_VAL 25
|
||||
#define DMARD06_SIGN_BIT 7
|
||||
|
||||
/* Device power modes */
|
||||
#define DMARD06_MODE_NORMAL 0x27
|
||||
#define DMARD06_MODE_POWERDOWN 0x00
|
||||
|
||||
/* Device channels */
|
||||
#define DMARD06_ACCEL_CHANNEL(_axis, _reg) { \
|
||||
.type = IIO_ACCEL, \
|
||||
.address = _reg, \
|
||||
.channel2 = IIO_MOD_##_axis, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.modified = 1, \
|
||||
}
|
||||
|
||||
#define DMARD06_TEMP_CHANNEL(_reg) { \
|
||||
.type = IIO_TEMP, \
|
||||
.address = _reg, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_OFFSET), \
|
||||
}
|
||||
|
||||
struct dmard06_data {
|
||||
struct i2c_client *client;
|
||||
u8 chip_id;
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec dmard06_channels[] = {
|
||||
DMARD06_ACCEL_CHANNEL(X, DMARD06_XOUT_REG),
|
||||
DMARD06_ACCEL_CHANNEL(Y, DMARD06_YOUT_REG),
|
||||
DMARD06_ACCEL_CHANNEL(Z, DMARD06_ZOUT_REG),
|
||||
DMARD06_TEMP_CHANNEL(DMARD06_TOUT_REG),
|
||||
};
|
||||
|
||||
static int dmard06_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct dmard06_data *dmard06 = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = i2c_smbus_read_byte_data(dmard06->client,
|
||||
chan->address);
|
||||
if (ret < 0) {
|
||||
dev_err(&dmard06->client->dev,
|
||||
"Error reading data: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*val = sign_extend32(ret, DMARD06_SIGN_BIT);
|
||||
|
||||
if (dmard06->chip_id == DMARD06_CHIP_ID)
|
||||
*val = *val >> 1;
|
||||
|
||||
switch (chan->type) {
|
||||
case IIO_ACCEL:
|
||||
return IIO_VAL_INT;
|
||||
case IIO_TEMP:
|
||||
if (dmard06->chip_id != DMARD06_CHIP_ID)
|
||||
*val = *val / 2;
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
switch (chan->type) {
|
||||
case IIO_TEMP:
|
||||
*val = DMARD06_TEMP_CENTER_VAL;
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
switch (chan->type) {
|
||||
case IIO_ACCEL:
|
||||
*val = 0;
|
||||
if (dmard06->chip_id == DMARD06_CHIP_ID)
|
||||
*val2 = DMARD06_AXIS_SCALE_VAL;
|
||||
else
|
||||
*val2 = DMARD05_AXIS_SCALE_VAL;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct iio_info dmard06_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = dmard06_read_raw,
|
||||
};
|
||||
|
||||
static int dmard06_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
int ret;
|
||||
struct iio_dev *indio_dev;
|
||||
struct dmard06_data *dmard06;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
|
||||
dev_err(&client->dev, "I2C check functionality failed\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*dmard06));
|
||||
if (!indio_dev) {
|
||||
dev_err(&client->dev, "Failed to allocate iio device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
dmard06 = iio_priv(indio_dev);
|
||||
dmard06->client = client;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(dmard06->client, DMARD06_CHIP_ID_REG);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "Error reading chip id: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (ret != DMARD05_CHIP_ID && ret != DMARD06_CHIP_ID &&
|
||||
ret != DMARD07_CHIP_ID) {
|
||||
dev_err(&client->dev, "Invalid chip id: %02d\n", ret);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
dmard06->chip_id = ret;
|
||||
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->name = DMARD06_DRV_NAME;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = dmard06_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(dmard06_channels);
|
||||
indio_dev->info = &dmard06_info;
|
||||
|
||||
return devm_iio_device_register(&client->dev, indio_dev);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int dmard06_suspend(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
|
||||
struct dmard06_data *dmard06 = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(dmard06->client, DMARD06_CTRL1_REG,
|
||||
DMARD06_MODE_POWERDOWN);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dmard06_resume(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
|
||||
struct dmard06_data *dmard06 = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(dmard06->client, DMARD06_CTRL1_REG,
|
||||
DMARD06_MODE_NORMAL);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(dmard06_pm_ops, dmard06_suspend, dmard06_resume);
|
||||
#define DMARD06_PM_OPS (&dmard06_pm_ops)
|
||||
#else
|
||||
#define DMARD06_PM_OPS NULL
|
||||
#endif
|
||||
|
||||
static const struct i2c_device_id dmard06_id[] = {
|
||||
{ "dmard05", 0 },
|
||||
{ "dmard06", 0 },
|
||||
{ "dmard07", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, dmard06_id);
|
||||
|
||||
static const struct of_device_id dmard06_of_match[] = {
|
||||
{ .compatible = "domintech,dmard05" },
|
||||
{ .compatible = "domintech,dmard06" },
|
||||
{ .compatible = "domintech,dmard07" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, dmard06_of_match);
|
||||
|
||||
static struct i2c_driver dmard06_driver = {
|
||||
.probe = dmard06_probe,
|
||||
.id_table = dmard06_id,
|
||||
.driver = {
|
||||
.name = DMARD06_DRV_NAME,
|
||||
.of_match_table = of_match_ptr(dmard06_of_match),
|
||||
.pm = DMARD06_PM_OPS,
|
||||
},
|
||||
};
|
||||
module_i2c_driver(dmard06_driver);
|
||||
|
||||
MODULE_AUTHOR("Aleksei Mamlin <mamlinav@gmail.com>");
|
||||
MODULE_DESCRIPTION("Domintech DMARD06 accelerometer driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -0,0 +1,157 @@
|
|||
/*
|
||||
* IIO driver for the 3-axis accelerometer Domintech DMARD09.
|
||||
*
|
||||
* Copyright (c) 2016, Jelle van der Waa <jelle@vdwaa.nl>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*/
|
||||
|
||||
#include <asm/unaligned.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/iio/iio.h>
|
||||
|
||||
#define DMARD09_DRV_NAME "dmard09"
|
||||
|
||||
#define DMARD09_REG_CHIPID 0x18
|
||||
#define DMARD09_REG_STAT 0x0A
|
||||
#define DMARD09_REG_X 0x0C
|
||||
#define DMARD09_REG_Y 0x0E
|
||||
#define DMARD09_REG_Z 0x10
|
||||
#define DMARD09_CHIPID 0x95
|
||||
|
||||
#define DMARD09_BUF_LEN 8
|
||||
#define DMARD09_AXIS_X 0
|
||||
#define DMARD09_AXIS_Y 1
|
||||
#define DMARD09_AXIS_Z 2
|
||||
#define DMARD09_AXIS_X_OFFSET ((DMARD09_AXIS_X + 1) * 2)
|
||||
#define DMARD09_AXIS_Y_OFFSET ((DMARD09_AXIS_Y + 1 )* 2)
|
||||
#define DMARD09_AXIS_Z_OFFSET ((DMARD09_AXIS_Z + 1) * 2)
|
||||
|
||||
struct dmard09_data {
|
||||
struct i2c_client *client;
|
||||
};
|
||||
|
||||
#define DMARD09_CHANNEL(_axis, offset) { \
|
||||
.type = IIO_ACCEL, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.modified = 1, \
|
||||
.address = offset, \
|
||||
.channel2 = IIO_MOD_##_axis, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec dmard09_channels[] = {
|
||||
DMARD09_CHANNEL(X, DMARD09_AXIS_X_OFFSET),
|
||||
DMARD09_CHANNEL(Y, DMARD09_AXIS_Y_OFFSET),
|
||||
DMARD09_CHANNEL(Z, DMARD09_AXIS_Z_OFFSET),
|
||||
};
|
||||
|
||||
static int dmard09_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct dmard09_data *data = iio_priv(indio_dev);
|
||||
u8 buf[DMARD09_BUF_LEN];
|
||||
int ret;
|
||||
s16 accel;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
/*
|
||||
* Read from the DMAR09_REG_STAT register, since the chip
|
||||
* caches reads from the individual X, Y, Z registers.
|
||||
*/
|
||||
ret = i2c_smbus_read_i2c_block_data(data->client,
|
||||
DMARD09_REG_STAT,
|
||||
DMARD09_BUF_LEN, buf);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev, "Error reading reg %d\n",
|
||||
DMARD09_REG_STAT);
|
||||
return ret;
|
||||
}
|
||||
|
||||
accel = get_unaligned_le16(&buf[chan->address]);
|
||||
|
||||
/* Remove lower 3 bits and sign extend */
|
||||
accel <<= 4;
|
||||
accel >>= 7;
|
||||
|
||||
*val = accel;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct iio_info dmard09_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = dmard09_read_raw,
|
||||
};
|
||||
|
||||
static int dmard09_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
int ret;
|
||||
struct iio_dev *indio_dev;
|
||||
struct dmard09_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;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(data->client, DMARD09_REG_CHIPID);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "Error reading chip id %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (ret != DMARD09_CHIPID) {
|
||||
dev_err(&client->dev, "Invalid chip id %d\n", ret);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->name = DMARD09_DRV_NAME;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = dmard09_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(dmard09_channels);
|
||||
indio_dev->info = &dmard09_info;
|
||||
|
||||
return devm_iio_device_register(&client->dev, indio_dev);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id dmard09_id[] = {
|
||||
{ "dmard09", 0},
|
||||
{ },
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(i2c, dmard09_id);
|
||||
|
||||
static struct i2c_driver dmard09_driver = {
|
||||
.driver = {
|
||||
.name = DMARD09_DRV_NAME
|
||||
},
|
||||
.probe = dmard09_probe,
|
||||
.id_table = dmard09_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(dmard09_driver);
|
||||
|
||||
MODULE_AUTHOR("Jelle van der Waa <jelle@vdwaa.nl>");
|
||||
MODULE_DESCRIPTION("DMARD09 3-axis accelerometer driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -1392,6 +1392,7 @@ static const struct acpi_device_id kx_acpi_match[] = {
|
|||
{"KXCJ1013", KXCJK1013},
|
||||
{"KXCJ1008", KXCJ91008},
|
||||
{"KXCJ9000", KXCJ91008},
|
||||
{"KIOX000A", KXCJ91008},
|
||||
{"KXTJ1009", KXTJ21009},
|
||||
{"SMO8500", KXCJ91008},
|
||||
{ },
|
||||
|
|
|
@ -317,6 +317,19 @@ config MCP3422
|
|||
This driver can also be built as a module. If so, the module will be
|
||||
called mcp3422.
|
||||
|
||||
config MEDIATEK_MT6577_AUXADC
|
||||
tristate "MediaTek AUXADC driver"
|
||||
depends on ARCH_MEDIATEK || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
Say yes here to enable support for MediaTek mt65xx AUXADC.
|
||||
|
||||
The driver supports immediate mode operation to read from one of sixteen
|
||||
channels (external or internal).
|
||||
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called mt6577_auxadc.
|
||||
|
||||
config MEN_Z188_ADC
|
||||
tristate "MEN 16z188 ADC IP Core support"
|
||||
depends on MCB
|
||||
|
@ -426,6 +439,18 @@ config TI_ADC128S052
|
|||
This driver can also be built as a module. If so, the module will be
|
||||
called ti-adc128s052.
|
||||
|
||||
config TI_ADC161S626
|
||||
tristate "Texas Instruments ADC161S626 1-channel differential ADC"
|
||||
depends on SPI
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
If you say yes here you get support for Texas Instruments ADC141S626,
|
||||
and ADC161S626 chips.
|
||||
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called ti-adc161s626.
|
||||
|
||||
config TI_ADS1015
|
||||
tristate "Texas Instruments ADS1015 ADC"
|
||||
depends on I2C && !SENSORS_ADS1015
|
||||
|
|
|
@ -31,6 +31,7 @@ obj-$(CONFIG_MAX1027) += max1027.o
|
|||
obj-$(CONFIG_MAX1363) += max1363.o
|
||||
obj-$(CONFIG_MCP320X) += mcp320x.o
|
||||
obj-$(CONFIG_MCP3422) += mcp3422.o
|
||||
obj-$(CONFIG_MEDIATEK_MT6577_AUXADC) += mt6577_auxadc.o
|
||||
obj-$(CONFIG_MEN_Z188_ADC) += men_z188_adc.o
|
||||
obj-$(CONFIG_MXS_LRADC) += mxs-lradc.o
|
||||
obj-$(CONFIG_NAU7802) += nau7802.o
|
||||
|
@ -41,6 +42,7 @@ obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
|
|||
obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
|
||||
obj-$(CONFIG_TI_ADC0832) += ti-adc0832.o
|
||||
obj-$(CONFIG_TI_ADC128S052) += ti-adc128s052.o
|
||||
obj-$(CONFIG_TI_ADC161S626) += ti-adc161s626.o
|
||||
obj-$(CONFIG_TI_ADS1015) += ti-ads1015.o
|
||||
obj-$(CONFIG_TI_ADS8688) += ti-ads8688.o
|
||||
obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o
|
||||
|
|
|
@ -239,16 +239,16 @@ static int ad7298_read_raw(struct iio_dev *indio_dev,
|
|||
|
||||
switch (m) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED) {
|
||||
ret = -EBUSY;
|
||||
} else {
|
||||
if (chan->address == AD7298_CH_TEMP)
|
||||
ret = ad7298_scan_temp(st, val);
|
||||
else
|
||||
ret = ad7298_scan_direct(st, chan->address);
|
||||
}
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
ret = iio_device_claim_direct_mode(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (chan->address == AD7298_CH_TEMP)
|
||||
ret = ad7298_scan_temp(st, val);
|
||||
else
|
||||
ret = ad7298_scan_direct(st, chan->address);
|
||||
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
|
|
@ -519,11 +519,9 @@ static int ad7793_write_raw(struct iio_dev *indio_dev,
|
|||
int ret, i;
|
||||
unsigned int tmp;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
if (iio_buffer_enabled(indio_dev)) {
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return -EBUSY;
|
||||
}
|
||||
ret = iio_device_claim_direct_mode(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
|
@ -548,7 +546,7 @@ static int ad7793_write_raw(struct iio_dev *indio_dev,
|
|||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,291 @@
|
|||
/*
|
||||
* Copyright (c) 2016 MediaTek Inc.
|
||||
* Author: Zhiyong Tao <zhiyong.tao@mediatek.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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iio/iio.h>
|
||||
|
||||
/* Register definitions */
|
||||
#define MT6577_AUXADC_CON0 0x00
|
||||
#define MT6577_AUXADC_CON1 0x04
|
||||
#define MT6577_AUXADC_CON2 0x10
|
||||
#define MT6577_AUXADC_STA BIT(0)
|
||||
|
||||
#define MT6577_AUXADC_DAT0 0x14
|
||||
#define MT6577_AUXADC_RDY0 BIT(12)
|
||||
|
||||
#define MT6577_AUXADC_MISC 0x94
|
||||
#define MT6577_AUXADC_PDN_EN BIT(14)
|
||||
|
||||
#define MT6577_AUXADC_DAT_MASK 0xfff
|
||||
#define MT6577_AUXADC_SLEEP_US 1000
|
||||
#define MT6577_AUXADC_TIMEOUT_US 10000
|
||||
#define MT6577_AUXADC_POWER_READY_MS 1
|
||||
#define MT6577_AUXADC_SAMPLE_READY_US 25
|
||||
|
||||
struct mt6577_auxadc_device {
|
||||
void __iomem *reg_base;
|
||||
struct clk *adc_clk;
|
||||
struct mutex lock;
|
||||
};
|
||||
|
||||
#define MT6577_AUXADC_CHANNEL(idx) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = (idx), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec mt6577_auxadc_iio_channels[] = {
|
||||
MT6577_AUXADC_CHANNEL(0),
|
||||
MT6577_AUXADC_CHANNEL(1),
|
||||
MT6577_AUXADC_CHANNEL(2),
|
||||
MT6577_AUXADC_CHANNEL(3),
|
||||
MT6577_AUXADC_CHANNEL(4),
|
||||
MT6577_AUXADC_CHANNEL(5),
|
||||
MT6577_AUXADC_CHANNEL(6),
|
||||
MT6577_AUXADC_CHANNEL(7),
|
||||
MT6577_AUXADC_CHANNEL(8),
|
||||
MT6577_AUXADC_CHANNEL(9),
|
||||
MT6577_AUXADC_CHANNEL(10),
|
||||
MT6577_AUXADC_CHANNEL(11),
|
||||
MT6577_AUXADC_CHANNEL(12),
|
||||
MT6577_AUXADC_CHANNEL(13),
|
||||
MT6577_AUXADC_CHANNEL(14),
|
||||
MT6577_AUXADC_CHANNEL(15),
|
||||
};
|
||||
|
||||
static inline void mt6577_auxadc_mod_reg(void __iomem *reg,
|
||||
u32 or_mask, u32 and_mask)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
val = readl(reg);
|
||||
val |= or_mask;
|
||||
val &= ~and_mask;
|
||||
writel(val, reg);
|
||||
}
|
||||
|
||||
static int mt6577_auxadc_read(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan)
|
||||
{
|
||||
u32 val;
|
||||
void __iomem *reg_channel;
|
||||
int ret;
|
||||
struct mt6577_auxadc_device *adc_dev = iio_priv(indio_dev);
|
||||
|
||||
reg_channel = adc_dev->reg_base + MT6577_AUXADC_DAT0 +
|
||||
chan->channel * 0x04;
|
||||
|
||||
mutex_lock(&adc_dev->lock);
|
||||
|
||||
mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_CON1,
|
||||
0, 1 << chan->channel);
|
||||
|
||||
/* read channel and make sure old ready bit == 0 */
|
||||
ret = readl_poll_timeout(reg_channel, val,
|
||||
((val & MT6577_AUXADC_RDY0) == 0),
|
||||
MT6577_AUXADC_SLEEP_US,
|
||||
MT6577_AUXADC_TIMEOUT_US);
|
||||
if (ret < 0) {
|
||||
dev_err(indio_dev->dev.parent,
|
||||
"wait for channel[%d] ready bit clear time out\n",
|
||||
chan->channel);
|
||||
goto err_timeout;
|
||||
}
|
||||
|
||||
/* set bit to trigger sample */
|
||||
mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_CON1,
|
||||
1 << chan->channel, 0);
|
||||
|
||||
/* we must delay here for hardware sample channel data */
|
||||
udelay(MT6577_AUXADC_SAMPLE_READY_US);
|
||||
|
||||
/* check MTK_AUXADC_CON2 if auxadc is idle */
|
||||
ret = readl_poll_timeout(adc_dev->reg_base + MT6577_AUXADC_CON2, val,
|
||||
((val & MT6577_AUXADC_STA) == 0),
|
||||
MT6577_AUXADC_SLEEP_US,
|
||||
MT6577_AUXADC_TIMEOUT_US);
|
||||
if (ret < 0) {
|
||||
dev_err(indio_dev->dev.parent,
|
||||
"wait for auxadc idle time out\n");
|
||||
goto err_timeout;
|
||||
}
|
||||
|
||||
/* read channel and make sure ready bit == 1 */
|
||||
ret = readl_poll_timeout(reg_channel, val,
|
||||
((val & MT6577_AUXADC_RDY0) != 0),
|
||||
MT6577_AUXADC_SLEEP_US,
|
||||
MT6577_AUXADC_TIMEOUT_US);
|
||||
if (ret < 0) {
|
||||
dev_err(indio_dev->dev.parent,
|
||||
"wait for channel[%d] data ready time out\n",
|
||||
chan->channel);
|
||||
goto err_timeout;
|
||||
}
|
||||
|
||||
/* read data */
|
||||
val = readl(reg_channel) & MT6577_AUXADC_DAT_MASK;
|
||||
|
||||
mutex_unlock(&adc_dev->lock);
|
||||
|
||||
return val;
|
||||
|
||||
err_timeout:
|
||||
|
||||
mutex_unlock(&adc_dev->lock);
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static int mt6577_auxadc_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val,
|
||||
int *val2,
|
||||
long info)
|
||||
{
|
||||
switch (info) {
|
||||
case IIO_CHAN_INFO_PROCESSED:
|
||||
*val = mt6577_auxadc_read(indio_dev, chan);
|
||||
if (*val < 0) {
|
||||
dev_err(indio_dev->dev.parent,
|
||||
"failed to sample data on channel[%d]\n",
|
||||
chan->channel);
|
||||
return *val;
|
||||
}
|
||||
return IIO_VAL_INT;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct iio_info mt6577_auxadc_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = &mt6577_auxadc_read_raw,
|
||||
};
|
||||
|
||||
static int mt6577_auxadc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct mt6577_auxadc_device *adc_dev;
|
||||
unsigned long adc_clk_rate;
|
||||
struct resource *res;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc_dev));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
adc_dev = iio_priv(indio_dev);
|
||||
indio_dev->dev.parent = &pdev->dev;
|
||||
indio_dev->name = dev_name(&pdev->dev);
|
||||
indio_dev->info = &mt6577_auxadc_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = mt6577_auxadc_iio_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(mt6577_auxadc_iio_channels);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
adc_dev->reg_base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(adc_dev->reg_base)) {
|
||||
dev_err(&pdev->dev, "failed to get auxadc base address\n");
|
||||
return PTR_ERR(adc_dev->reg_base);
|
||||
}
|
||||
|
||||
adc_dev->adc_clk = devm_clk_get(&pdev->dev, "main");
|
||||
if (IS_ERR(adc_dev->adc_clk)) {
|
||||
dev_err(&pdev->dev, "failed to get auxadc clock\n");
|
||||
return PTR_ERR(adc_dev->adc_clk);
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(adc_dev->adc_clk);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to enable auxadc clock\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
adc_clk_rate = clk_get_rate(adc_dev->adc_clk);
|
||||
if (!adc_clk_rate) {
|
||||
ret = -EINVAL;
|
||||
dev_err(&pdev->dev, "null clock rate\n");
|
||||
goto err_disable_clk;
|
||||
}
|
||||
|
||||
mutex_init(&adc_dev->lock);
|
||||
|
||||
mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_MISC,
|
||||
MT6577_AUXADC_PDN_EN, 0);
|
||||
mdelay(MT6577_AUXADC_POWER_READY_MS);
|
||||
|
||||
platform_set_drvdata(pdev, indio_dev);
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed to register iio device\n");
|
||||
goto err_power_off;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_power_off:
|
||||
mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_MISC,
|
||||
0, MT6577_AUXADC_PDN_EN);
|
||||
err_disable_clk:
|
||||
clk_disable_unprepare(adc_dev->adc_clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mt6577_auxadc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
struct mt6577_auxadc_device *adc_dev = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_MISC,
|
||||
0, MT6577_AUXADC_PDN_EN);
|
||||
|
||||
clk_disable_unprepare(adc_dev->adc_clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id mt6577_auxadc_of_match[] = {
|
||||
{ .compatible = "mediatek,mt2701-auxadc", },
|
||||
{ .compatible = "mediatek,mt8173-auxadc", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mt6577_auxadc_of_match);
|
||||
|
||||
static struct platform_driver mt6577_auxadc_driver = {
|
||||
.driver = {
|
||||
.name = "mt6577-auxadc",
|
||||
.of_match_table = mt6577_auxadc_of_match,
|
||||
},
|
||||
.probe = mt6577_auxadc_probe,
|
||||
.remove = mt6577_auxadc_remove,
|
||||
};
|
||||
module_platform_driver(mt6577_auxadc_driver);
|
||||
|
||||
MODULE_AUTHOR("Zhiyong Tao <zhiyong.tao@mediatek.com>");
|
||||
MODULE_DESCRIPTION("MTK AUXADC Device Driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -197,7 +197,7 @@ static irqreturn_t nau7802_eoc_trigger(int irq, void *private)
|
|||
if (st->conversion_count < NAU7802_MIN_CONVERSIONS)
|
||||
st->conversion_count++;
|
||||
if (st->conversion_count >= NAU7802_MIN_CONVERSIONS)
|
||||
complete_all(&st->value_ok);
|
||||
complete(&st->value_ok);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,248 @@
|
|||
/*
|
||||
* ti-adc161s626.c - Texas Instruments ADC161S626 1-channel differential ADC
|
||||
*
|
||||
* ADC Devices Supported:
|
||||
* adc141s626 - 14-bit ADC
|
||||
* adc161s626 - 16-bit ADC
|
||||
*
|
||||
* Copyright (C) 2016 Matt Ranostay <mranostay@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
|
||||
#define TI_ADC_DRV_NAME "ti-adc161s626"
|
||||
|
||||
enum {
|
||||
TI_ADC141S626,
|
||||
TI_ADC161S626,
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec ti_adc141s626_channels[] = {
|
||||
{
|
||||
.type = IIO_VOLTAGE,
|
||||
.channel = 0,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.scan_index = 0,
|
||||
.scan_type = {
|
||||
.sign = 's',
|
||||
.realbits = 14,
|
||||
.storagebits = 16,
|
||||
},
|
||||
},
|
||||
IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec ti_adc161s626_channels[] = {
|
||||
{
|
||||
.type = IIO_VOLTAGE,
|
||||
.channel = 0,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.scan_index = 0,
|
||||
.scan_type = {
|
||||
.sign = 's',
|
||||
.realbits = 16,
|
||||
.storagebits = 16,
|
||||
},
|
||||
},
|
||||
IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
};
|
||||
|
||||
struct ti_adc_data {
|
||||
struct iio_dev *indio_dev;
|
||||
struct spi_device *spi;
|
||||
u8 read_size;
|
||||
u8 shift;
|
||||
|
||||
u8 buffer[16] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
static int ti_adc_read_measurement(struct ti_adc_data *data,
|
||||
struct iio_chan_spec const *chan, int *val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
switch (data->read_size) {
|
||||
case 2: {
|
||||
__be16 buf;
|
||||
|
||||
ret = spi_read(data->spi, (void *) &buf, 2);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*val = be16_to_cpu(buf);
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
__be32 buf;
|
||||
|
||||
ret = spi_read(data->spi, (void *) &buf, 3);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*val = be32_to_cpu(buf) >> 8;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*val = sign_extend32(*val >> data->shift, chan->scan_type.realbits - 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t ti_adc_trigger_handler(int irq, void *private)
|
||||
{
|
||||
struct iio_poll_func *pf = private;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct ti_adc_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = ti_adc_read_measurement(data, &indio_dev->channels[0],
|
||||
(int *) &data->buffer);
|
||||
if (!ret)
|
||||
iio_push_to_buffers_with_timestamp(indio_dev,
|
||||
data->buffer,
|
||||
iio_get_time_ns(indio_dev));
|
||||
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int ti_adc_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct ti_adc_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
if (mask != IIO_CHAN_INFO_RAW)
|
||||
return -EINVAL;
|
||||
|
||||
ret = iio_device_claim_direct_mode(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ti_adc_read_measurement(data, chan, val);
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
|
||||
if (!ret)
|
||||
return IIO_VAL_INT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct iio_info ti_adc_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = ti_adc_read_raw,
|
||||
};
|
||||
|
||||
static int ti_adc_probe(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct ti_adc_data *data;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
indio_dev->info = &ti_adc_info;
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->dev.of_node = spi->dev.of_node;
|
||||
indio_dev->name = TI_ADC_DRV_NAME;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
|
||||
data = iio_priv(indio_dev);
|
||||
data->spi = spi;
|
||||
|
||||
switch (spi_get_device_id(spi)->driver_data) {
|
||||
case TI_ADC141S626:
|
||||
indio_dev->channels = ti_adc141s626_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(ti_adc141s626_channels);
|
||||
data->shift = 0;
|
||||
data->read_size = 2;
|
||||
break;
|
||||
case TI_ADC161S626:
|
||||
indio_dev->channels = ti_adc161s626_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(ti_adc161s626_channels);
|
||||
data->shift = 6;
|
||||
data->read_size = 3;
|
||||
break;
|
||||
}
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, NULL,
|
||||
ti_adc_trigger_handler, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto error_unreg_buffer;
|
||||
|
||||
return 0;
|
||||
|
||||
error_unreg_buffer:
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ti_adc_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 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id ti_adc_dt_ids[] = {
|
||||
{ .compatible = "ti,adc141s626", },
|
||||
{ .compatible = "ti,adc161s626", },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ti_adc_dt_ids);
|
||||
|
||||
static const struct spi_device_id ti_adc_id[] = {
|
||||
{"adc141s626", TI_ADC141S626},
|
||||
{"adc161s626", TI_ADC161S626},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ti_adc_id);
|
||||
|
||||
static struct spi_driver ti_adc_driver = {
|
||||
.driver = {
|
||||
.name = TI_ADC_DRV_NAME,
|
||||
.of_match_table = of_match_ptr(ti_adc_dt_ids),
|
||||
},
|
||||
.probe = ti_adc_probe,
|
||||
.remove = ti_adc_remove,
|
||||
.id_table = ti_adc_id,
|
||||
};
|
||||
module_spi_driver(ti_adc_driver);
|
||||
|
||||
MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
|
||||
MODULE_DESCRIPTION("Texas Instruments ADC1x1S 1-channel differential ADC");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -18,6 +18,7 @@ struct iio_cb_buffer {
|
|||
int (*cb)(const void *data, void *private);
|
||||
void *private;
|
||||
struct iio_channel *channels;
|
||||
struct iio_dev *indio_dev;
|
||||
};
|
||||
|
||||
static struct iio_cb_buffer *buffer_to_cb_buffer(struct iio_buffer *buffer)
|
||||
|
@ -52,7 +53,6 @@ struct iio_cb_buffer *iio_channel_get_all_cb(struct device *dev,
|
|||
{
|
||||
int ret;
|
||||
struct iio_cb_buffer *cb_buff;
|
||||
struct iio_dev *indio_dev;
|
||||
struct iio_channel *chan;
|
||||
|
||||
cb_buff = kzalloc(sizeof(*cb_buff), GFP_KERNEL);
|
||||
|
@ -72,17 +72,17 @@ struct iio_cb_buffer *iio_channel_get_all_cb(struct device *dev,
|
|||
goto error_free_cb_buff;
|
||||
}
|
||||
|
||||
indio_dev = cb_buff->channels[0].indio_dev;
|
||||
cb_buff->indio_dev = cb_buff->channels[0].indio_dev;
|
||||
cb_buff->buffer.scan_mask
|
||||
= kcalloc(BITS_TO_LONGS(indio_dev->masklength), sizeof(long),
|
||||
GFP_KERNEL);
|
||||
= kcalloc(BITS_TO_LONGS(cb_buff->indio_dev->masklength),
|
||||
sizeof(long), GFP_KERNEL);
|
||||
if (cb_buff->buffer.scan_mask == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto error_release_channels;
|
||||
}
|
||||
chan = &cb_buff->channels[0];
|
||||
while (chan->indio_dev) {
|
||||
if (chan->indio_dev != indio_dev) {
|
||||
if (chan->indio_dev != cb_buff->indio_dev) {
|
||||
ret = -EINVAL;
|
||||
goto error_free_scan_mask;
|
||||
}
|
||||
|
@ -105,17 +105,14 @@ EXPORT_SYMBOL_GPL(iio_channel_get_all_cb);
|
|||
|
||||
int iio_channel_start_all_cb(struct iio_cb_buffer *cb_buff)
|
||||
{
|
||||
return iio_update_buffers(cb_buff->channels[0].indio_dev,
|
||||
&cb_buff->buffer,
|
||||
return iio_update_buffers(cb_buff->indio_dev, &cb_buff->buffer,
|
||||
NULL);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iio_channel_start_all_cb);
|
||||
|
||||
void iio_channel_stop_all_cb(struct iio_cb_buffer *cb_buff)
|
||||
{
|
||||
iio_update_buffers(cb_buff->channels[0].indio_dev,
|
||||
NULL,
|
||||
&cb_buff->buffer);
|
||||
iio_update_buffers(cb_buff->indio_dev, NULL, &cb_buff->buffer);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iio_channel_stop_all_cb);
|
||||
|
||||
|
@ -133,6 +130,13 @@ struct iio_channel
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(iio_channel_cb_get_channels);
|
||||
|
||||
struct iio_dev
|
||||
*iio_channel_cb_get_iio_dev(const struct iio_cb_buffer *cb_buffer)
|
||||
{
|
||||
return cb_buffer->indio_dev;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iio_channel_cb_get_iio_dev);
|
||||
|
||||
MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>");
|
||||
MODULE_DESCRIPTION("Industrial I/O callback buffer");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
@ -16,6 +16,7 @@ config ATLAS_PH_SENSOR
|
|||
Atlas Scientific OEM SM sensors:
|
||||
* pH SM sensor
|
||||
* EC SM sensor
|
||||
* ORP SM sensor
|
||||
|
||||
To compile this driver as module, choose M here: the
|
||||
module will be called atlas-ph-sensor.
|
||||
|
|
|
@ -66,12 +66,17 @@
|
|||
#define ATLAS_REG_TDS_DATA 0x1c
|
||||
#define ATLAS_REG_PSS_DATA 0x20
|
||||
|
||||
#define ATLAS_REG_ORP_CALIB_STATUS 0x0d
|
||||
#define ATLAS_REG_ORP_DATA 0x0e
|
||||
|
||||
#define ATLAS_PH_INT_TIME_IN_US 450000
|
||||
#define ATLAS_EC_INT_TIME_IN_US 650000
|
||||
#define ATLAS_ORP_INT_TIME_IN_US 450000
|
||||
|
||||
enum {
|
||||
ATLAS_PH_SM,
|
||||
ATLAS_EC_SM,
|
||||
ATLAS_ORP_SM,
|
||||
};
|
||||
|
||||
struct atlas_data {
|
||||
|
@ -84,26 +89,10 @@ struct atlas_data {
|
|||
__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 = {
|
||||
.yes_ranges = atlas_volatile_ranges,
|
||||
.n_yes_ranges = ARRAY_SIZE(atlas_volatile_ranges),
|
||||
};
|
||||
|
||||
static const struct regmap_config atlas_regmap_config = {
|
||||
.name = ATLAS_REGMAP_NAME,
|
||||
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
|
||||
.volatile_table = &atlas_volatile_table,
|
||||
.max_register = ATLAS_REG_PSS_DATA + 4,
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec atlas_ph_channels[] = {
|
||||
|
@ -175,6 +164,23 @@ static const struct iio_chan_spec atlas_ec_channels[] = {
|
|||
},
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec atlas_orp_channels[] = {
|
||||
{
|
||||
.type = IIO_VOLTAGE,
|
||||
.address = ATLAS_REG_ORP_DATA,
|
||||
.info_mask_separate =
|
||||
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
|
||||
.scan_index = 0,
|
||||
.scan_type = {
|
||||
.sign = 's',
|
||||
.realbits = 32,
|
||||
.storagebits = 32,
|
||||
.endianness = IIO_BE,
|
||||
},
|
||||
},
|
||||
IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
};
|
||||
|
||||
static int atlas_check_ph_calibration(struct atlas_data *data)
|
||||
{
|
||||
struct device *dev = &data->client->dev;
|
||||
|
@ -240,6 +246,22 @@ static int atlas_check_ec_calibration(struct atlas_data *data)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int atlas_check_orp_calibration(struct atlas_data *data)
|
||||
{
|
||||
struct device *dev = &data->client->dev;
|
||||
int ret;
|
||||
unsigned int val;
|
||||
|
||||
ret = regmap_read(data->regmap, ATLAS_REG_ORP_CALIB_STATUS, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!val)
|
||||
dev_warn(dev, "device has not been calibrated\n");
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
struct atlas_device {
|
||||
const struct iio_chan_spec *channels;
|
||||
int num_channels;
|
||||
|
@ -264,7 +286,13 @@ static struct atlas_device atlas_devices[] = {
|
|||
.calibration = &atlas_check_ec_calibration,
|
||||
.delay = ATLAS_EC_INT_TIME_IN_US,
|
||||
},
|
||||
|
||||
[ATLAS_ORP_SM] = {
|
||||
.channels = atlas_orp_channels,
|
||||
.num_channels = 2,
|
||||
.data_reg = ATLAS_REG_ORP_DATA,
|
||||
.calibration = &atlas_check_orp_calibration,
|
||||
.delay = ATLAS_ORP_INT_TIME_IN_US,
|
||||
},
|
||||
};
|
||||
|
||||
static int atlas_set_powermode(struct atlas_data *data, int on)
|
||||
|
@ -402,15 +430,14 @@ static int atlas_read_raw(struct iio_dev *indio_dev,
|
|||
case IIO_PH:
|
||||
case IIO_CONCENTRATION:
|
||||
case IIO_ELECTRICALCONDUCTIVITY:
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
case IIO_VOLTAGE:
|
||||
ret = iio_device_claim_direct_mode(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (iio_buffer_enabled(indio_dev))
|
||||
ret = -EBUSY;
|
||||
else
|
||||
ret = atlas_read_measurement(data,
|
||||
chan->address, ®);
|
||||
ret = atlas_read_measurement(data, chan->address, ®);
|
||||
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
|
@ -440,6 +467,10 @@ static int atlas_read_raw(struct iio_dev *indio_dev,
|
|||
*val = 0; /* 0.000000001 */
|
||||
*val2 = 1000;
|
||||
return IIO_VAL_INT_PLUS_NANO;
|
||||
case IIO_VOLTAGE:
|
||||
*val = 1; /* 0.1 */
|
||||
*val2 = 10;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -475,6 +506,7 @@ static const struct iio_info atlas_info = {
|
|||
static const struct i2c_device_id atlas_id[] = {
|
||||
{ "atlas-ph-sm", ATLAS_PH_SM},
|
||||
{ "atlas-ec-sm", ATLAS_EC_SM},
|
||||
{ "atlas-orp-sm", ATLAS_ORP_SM},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, atlas_id);
|
||||
|
@ -482,6 +514,7 @@ 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, },
|
||||
{ .compatible = "atlas,orp-sm", .data = (void *)ATLAS_ORP_SM, },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, atlas_dt_ids);
|
||||
|
|
|
@ -122,6 +122,14 @@ int hid_sensor_power_state(struct hid_sensor_common *st, bool state)
|
|||
#endif
|
||||
}
|
||||
|
||||
static void hid_sensor_set_power_work(struct work_struct *work)
|
||||
{
|
||||
struct hid_sensor_common *attrb = container_of(work,
|
||||
struct hid_sensor_common,
|
||||
work);
|
||||
_hid_sensor_power_state(attrb, true);
|
||||
}
|
||||
|
||||
static int hid_sensor_data_rdy_trigger_set_state(struct iio_trigger *trig,
|
||||
bool state)
|
||||
{
|
||||
|
@ -130,6 +138,7 @@ static int hid_sensor_data_rdy_trigger_set_state(struct iio_trigger *trig,
|
|||
|
||||
void hid_sensor_remove_trigger(struct hid_sensor_common *attrb)
|
||||
{
|
||||
cancel_work_sync(&attrb->work);
|
||||
iio_trigger_unregister(attrb->trigger);
|
||||
iio_trigger_free(attrb->trigger);
|
||||
}
|
||||
|
@ -170,6 +179,9 @@ int hid_sensor_setup_trigger(struct iio_dev *indio_dev, const char *name,
|
|||
goto error_unreg_trigger;
|
||||
|
||||
iio_device_set_drvdata(indio_dev, attrb);
|
||||
|
||||
INIT_WORK(&attrb->work, hid_sensor_set_power_work);
|
||||
|
||||
pm_suspend_ignore_children(&attrb->pdev->dev, true);
|
||||
pm_runtime_enable(&attrb->pdev->dev);
|
||||
/* Default to 3 seconds, but can be changed from sysfs */
|
||||
|
@ -202,7 +214,15 @@ static int hid_sensor_resume(struct device *dev)
|
|||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
struct hid_sensor_common *attrb = iio_device_get_drvdata(indio_dev);
|
||||
schedule_work(&attrb->work);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hid_sensor_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
struct hid_sensor_common *attrb = iio_device_get_drvdata(indio_dev);
|
||||
return _hid_sensor_power_state(attrb, true);
|
||||
}
|
||||
|
||||
|
@ -211,7 +231,7 @@ static int hid_sensor_resume(struct device *dev)
|
|||
const struct dev_pm_ops hid_sensor_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(hid_sensor_suspend, hid_sensor_resume)
|
||||
SET_RUNTIME_PM_OPS(hid_sensor_suspend,
|
||||
hid_sensor_resume, NULL)
|
||||
hid_sensor_runtime_resume, NULL)
|
||||
};
|
||||
EXPORT_SYMBOL(hid_sensor_pm_ops);
|
||||
|
||||
|
|
|
@ -181,6 +181,15 @@ config AD7303
|
|||
To compile this driver as module choose M here: the module will be called
|
||||
ad7303.
|
||||
|
||||
config CIO_DAC
|
||||
tristate "Measurement Computing CIO-DAC IIO driver"
|
||||
depends on X86 && ISA_BUS_API
|
||||
help
|
||||
Say yes here to build support for the Measurement Computing CIO-DAC
|
||||
analog output device family (CIO-DAC16, CIO-DAC08, PC104-DAC06). The
|
||||
base port addresses for the devices may be configured via the base
|
||||
array module parameter.
|
||||
|
||||
config LPC18XX_DAC
|
||||
tristate "NXP LPC18xx DAC driver"
|
||||
depends on ARCH_LPC18XX || COMPILE_TEST
|
||||
|
|
|
@ -20,6 +20,7 @@ obj-$(CONFIG_AD5764) += ad5764.o
|
|||
obj-$(CONFIG_AD5791) += ad5791.o
|
||||
obj-$(CONFIG_AD5686) += ad5686.o
|
||||
obj-$(CONFIG_AD7303) += ad7303.o
|
||||
obj-$(CONFIG_CIO_DAC) += cio-dac.o
|
||||
obj-$(CONFIG_LPC18XX_DAC) += lpc18xx_dac.o
|
||||
obj-$(CONFIG_M62332) += m62332.o
|
||||
obj-$(CONFIG_MAX517) += max517.o
|
||||
|
|
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
* IIO driver for the Measurement Computing CIO-DAC
|
||||
* Copyright (C) 2016 William Breathitt Gray
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* This driver supports the following Measurement Computing devices: CIO-DAC16,
|
||||
* CIO-DAC06, and PC104-DAC06.
|
||||
*/
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/types.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/isa.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
|
||||
#define CIO_DAC_NUM_CHAN 16
|
||||
|
||||
#define CIO_DAC_CHAN(chan) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.channel = chan, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.indexed = 1, \
|
||||
.output = 1 \
|
||||
}
|
||||
|
||||
#define CIO_DAC_EXTENT 32
|
||||
|
||||
static unsigned int base[max_num_isa_dev(CIO_DAC_EXTENT)];
|
||||
static unsigned int num_cio_dac;
|
||||
module_param_array(base, uint, &num_cio_dac, 0);
|
||||
MODULE_PARM_DESC(base, "Measurement Computing CIO-DAC base addresses");
|
||||
|
||||
/**
|
||||
* struct cio_dac_iio - IIO device private data structure
|
||||
* @chan_out_states: channels' output states
|
||||
* @base: base port address of the IIO device
|
||||
*/
|
||||
struct cio_dac_iio {
|
||||
int chan_out_states[CIO_DAC_NUM_CHAN];
|
||||
unsigned int base;
|
||||
};
|
||||
|
||||
static int cio_dac_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val, int *val2, long mask)
|
||||
{
|
||||
struct cio_dac_iio *const priv = iio_priv(indio_dev);
|
||||
|
||||
if (mask != IIO_CHAN_INFO_RAW)
|
||||
return -EINVAL;
|
||||
|
||||
*val = priv->chan_out_states[chan->channel];
|
||||
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
static int cio_dac_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int val, int val2, long mask)
|
||||
{
|
||||
struct cio_dac_iio *const priv = iio_priv(indio_dev);
|
||||
const unsigned int chan_addr_offset = 2 * chan->channel;
|
||||
|
||||
if (mask != IIO_CHAN_INFO_RAW)
|
||||
return -EINVAL;
|
||||
|
||||
/* DAC can only accept up to a 16-bit value */
|
||||
if ((unsigned int)val > 65535)
|
||||
return -EINVAL;
|
||||
|
||||
priv->chan_out_states[chan->channel] = val;
|
||||
outw(val, priv->base + chan_addr_offset);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct iio_info cio_dac_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = cio_dac_read_raw,
|
||||
.write_raw = cio_dac_write_raw
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec cio_dac_channels[CIO_DAC_NUM_CHAN] = {
|
||||
CIO_DAC_CHAN(0), CIO_DAC_CHAN(1), CIO_DAC_CHAN(2), CIO_DAC_CHAN(3),
|
||||
CIO_DAC_CHAN(4), CIO_DAC_CHAN(5), CIO_DAC_CHAN(6), CIO_DAC_CHAN(7),
|
||||
CIO_DAC_CHAN(8), CIO_DAC_CHAN(9), CIO_DAC_CHAN(10), CIO_DAC_CHAN(11),
|
||||
CIO_DAC_CHAN(12), CIO_DAC_CHAN(13), CIO_DAC_CHAN(14), CIO_DAC_CHAN(15)
|
||||
};
|
||||
|
||||
static int cio_dac_probe(struct device *dev, unsigned int id)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct cio_dac_iio *priv;
|
||||
unsigned int i;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(dev, sizeof(*priv));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!devm_request_region(dev, base[id], CIO_DAC_EXTENT,
|
||||
dev_name(dev))) {
|
||||
dev_err(dev, "Unable to request port addresses (0x%X-0x%X)\n",
|
||||
base[id], base[id] + CIO_DAC_EXTENT);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
indio_dev->info = &cio_dac_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = cio_dac_channels;
|
||||
indio_dev->num_channels = CIO_DAC_NUM_CHAN;
|
||||
indio_dev->name = dev_name(dev);
|
||||
|
||||
priv = iio_priv(indio_dev);
|
||||
priv->base = base[id];
|
||||
|
||||
/* initialize DAC outputs to 0V */
|
||||
for (i = 0; i < 32; i += 2)
|
||||
outw(0, base[id] + i);
|
||||
|
||||
return devm_iio_device_register(dev, indio_dev);
|
||||
}
|
||||
|
||||
static struct isa_driver cio_dac_driver = {
|
||||
.probe = cio_dac_probe,
|
||||
.driver = {
|
||||
.name = "cio-dac"
|
||||
}
|
||||
};
|
||||
|
||||
module_isa_driver(cio_dac_driver, num_cio_dac);
|
||||
|
||||
MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
|
||||
MODULE_DESCRIPTION("Measurement Computing CIO-DAC IIO driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -26,11 +26,11 @@ config HDC100X
|
|||
tristate "TI HDC100x relative humidity and temperature sensor"
|
||||
depends on I2C
|
||||
help
|
||||
Say yes here to build support for the TI HDC100x series of
|
||||
relative humidity and temperature sensors.
|
||||
Say yes here to build support for the Texas Instruments
|
||||
HDC1000 and HDC1008 relative humidity and temperature sensors.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called hdc100x.
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called hdc100x.
|
||||
|
||||
config HTU21
|
||||
tristate "Measurement Specialties HTU21 humidity & temperature sensor"
|
||||
|
|
|
@ -333,11 +333,11 @@ config US5182D
|
|||
will be called us5182d.
|
||||
|
||||
config VCNL4000
|
||||
tristate "VCNL4000 combined ALS and proximity sensor"
|
||||
tristate "VCNL4000/4010/4020 combined ALS and proximity sensor"
|
||||
depends on I2C
|
||||
help
|
||||
Say Y here if you want to build a driver for the Vishay VCNL4000
|
||||
combined ambient light and proximity sensor.
|
||||
Say Y here if you want to build a driver for the Vishay VCNL4000,
|
||||
VCNL4010, VCNL4020 combined ambient light and proximity sensor.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called vcnl4000.
|
||||
|
|
|
@ -894,7 +894,7 @@ static int us5182d_probe(struct i2c_client *client,
|
|||
goto out_err;
|
||||
|
||||
if (data->default_continuous) {
|
||||
pm_runtime_set_active(&client->dev);
|
||||
ret = pm_runtime_set_active(&client->dev);
|
||||
if (ret < 0)
|
||||
goto out_err;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* vcnl4000.c - Support for Vishay VCNL4000 combined ambient light and
|
||||
* proximity sensor
|
||||
* vcnl4000.c - Support for Vishay VCNL4000/4010/4020 combined ambient
|
||||
* light and proximity sensor
|
||||
*
|
||||
* Copyright 2012 Peter Meerwald <pmeerw@pmeerw.net>
|
||||
*
|
||||
|
@ -13,6 +13,8 @@
|
|||
* TODO:
|
||||
* allow to adjust IR current
|
||||
* proximity threshold and event handling
|
||||
* periodic ALS/proximity measurement (VCNL4010/20)
|
||||
* interrupts (VCNL4010/20)
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
|
@ -24,6 +26,8 @@
|
|||
#include <linux/iio/sysfs.h>
|
||||
|
||||
#define VCNL4000_DRV_NAME "vcnl4000"
|
||||
#define VCNL4000_ID 0x01
|
||||
#define VCNL4010_ID 0x02 /* for VCNL4020, VCNL4010 */
|
||||
|
||||
#define VCNL4000_COMMAND 0x80 /* Command register */
|
||||
#define VCNL4000_PROD_REV 0x81 /* Product ID and Revision ID */
|
||||
|
@ -37,13 +41,14 @@
|
|||
#define VCNL4000_PS_MOD_ADJ 0x8a /* Proximity modulator timing adjustment */
|
||||
|
||||
/* Bit masks for COMMAND register */
|
||||
#define VCNL4000_AL_RDY 0x40 /* ALS data ready? */
|
||||
#define VCNL4000_PS_RDY 0x20 /* proximity data ready? */
|
||||
#define VCNL4000_AL_OD 0x10 /* start on-demand ALS measurement */
|
||||
#define VCNL4000_PS_OD 0x08 /* start on-demand proximity measurement */
|
||||
#define VCNL4000_AL_RDY BIT(6) /* ALS data ready? */
|
||||
#define VCNL4000_PS_RDY BIT(5) /* proximity data ready? */
|
||||
#define VCNL4000_AL_OD BIT(4) /* start on-demand ALS measurement */
|
||||
#define VCNL4000_PS_OD BIT(3) /* start on-demand proximity measurement */
|
||||
|
||||
struct vcnl4000_data {
|
||||
struct i2c_client *client;
|
||||
struct mutex lock;
|
||||
};
|
||||
|
||||
static const struct i2c_device_id vcnl4000_id[] = {
|
||||
|
@ -59,16 +64,18 @@ static int vcnl4000_measure(struct vcnl4000_data *data, u8 req_mask,
|
|||
__be16 buf;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
|
||||
ret = i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND,
|
||||
req_mask);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto fail;
|
||||
|
||||
/* wait for data to become ready */
|
||||
while (tries--) {
|
||||
ret = i2c_smbus_read_byte_data(data->client, VCNL4000_COMMAND);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto fail;
|
||||
if (ret & rdy_mask)
|
||||
break;
|
||||
msleep(20); /* measurement takes up to 100 ms */
|
||||
|
@ -77,17 +84,23 @@ static int vcnl4000_measure(struct vcnl4000_data *data, u8 req_mask,
|
|||
if (tries < 0) {
|
||||
dev_err(&data->client->dev,
|
||||
"vcnl4000_measure() failed, data not ready\n");
|
||||
return -EIO;
|
||||
ret = -EIO;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = i2c_smbus_read_i2c_block_data(data->client,
|
||||
data_reg, sizeof(buf), (u8 *) &buf);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto fail;
|
||||
|
||||
mutex_unlock(&data->lock);
|
||||
*val = be16_to_cpu(buf);
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
mutex_unlock(&data->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec vcnl4000_channels[] = {
|
||||
|
@ -105,7 +118,7 @@ static int vcnl4000_read_raw(struct iio_dev *indio_dev,
|
|||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
int ret = -EINVAL;
|
||||
int ret;
|
||||
struct vcnl4000_data *data = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
|
@ -117,32 +130,27 @@ static int vcnl4000_read_raw(struct iio_dev *indio_dev,
|
|||
VCNL4000_AL_RESULT_HI, val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_PROXIMITY:
|
||||
ret = vcnl4000_measure(data,
|
||||
VCNL4000_PS_OD, VCNL4000_PS_RDY,
|
||||
VCNL4000_PS_RESULT_HI, val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
break;
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
if (chan->type == IIO_LIGHT) {
|
||||
*val = 0;
|
||||
*val2 = 250000;
|
||||
ret = IIO_VAL_INT_PLUS_MICRO;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (chan->type != IIO_LIGHT)
|
||||
return -EINVAL;
|
||||
|
||||
return ret;
|
||||
*val = 0;
|
||||
*val2 = 250000;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct iio_info vcnl4000_info = {
|
||||
|
@ -155,7 +163,7 @@ static int vcnl4000_probe(struct i2c_client *client,
|
|||
{
|
||||
struct vcnl4000_data *data;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret;
|
||||
int ret, prod_id;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
|
@ -164,13 +172,19 @@ static int vcnl4000_probe(struct i2c_client *client,
|
|||
data = iio_priv(indio_dev);
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
data->client = client;
|
||||
mutex_init(&data->lock);
|
||||
|
||||
ret = i2c_smbus_read_byte_data(data->client, VCNL4000_PROD_REV);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
dev_info(&client->dev, "VCNL4000 Ambient light/proximity sensor, Prod %02x, Rev: %02x\n",
|
||||
ret >> 4, ret & 0xf);
|
||||
prod_id = ret >> 4;
|
||||
if (prod_id != VCNL4010_ID && prod_id != VCNL4000_ID)
|
||||
return -ENODEV;
|
||||
|
||||
dev_dbg(&client->dev, "%s Ambient light/proximity sensor, Rev: %02x\n",
|
||||
(prod_id == VCNL4010_ID) ? "VCNL4010/4020" : "VCNL4000",
|
||||
ret & 0xf);
|
||||
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->info = &vcnl4000_info;
|
||||
|
|
|
@ -5,8 +5,22 @@
|
|||
|
||||
menu "Magnetometer sensors"
|
||||
|
||||
config AK8974
|
||||
tristate "Asahi Kasei AK8974 3-Axis Magnetometer"
|
||||
depends on I2C
|
||||
depends on OF
|
||||
select REGMAP_I2C
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
Say yes here to build support for Asahi Kasei AK8974 or
|
||||
AMI305 I2C-based 3-axis magnetometer chips.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called ak8974.
|
||||
|
||||
config AK8975
|
||||
tristate "Asahi Kasei AK 3-Axis Magnetometer"
|
||||
tristate "Asahi Kasei AK8975 3-Axis Magnetometer"
|
||||
depends on I2C
|
||||
depends on GPIOLIB || COMPILE_TEST
|
||||
select IIO_BUFFER
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#
|
||||
|
||||
# When adding new entries keep the list in alphabetical order
|
||||
obj-$(CONFIG_AK8974) += ak8974.o
|
||||
obj-$(CONFIG_AK8975) += ak8975.o
|
||||
obj-$(CONFIG_BMC150_MAGN) += bmc150_magn.o
|
||||
obj-$(CONFIG_BMC150_MAGN_I2C) += bmc150_magn_i2c.o
|
||||
|
|
|
@ -0,0 +1,863 @@
|
|||
/*
|
||||
* Driver for the Asahi Kasei EMD Corporation AK8974
|
||||
* and Aichi Steel AMI305 magnetometer chips.
|
||||
* Based on a patch from Samu Onkalo and the AK8975 IIO driver.
|
||||
*
|
||||
* Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
||||
* Copyright (c) 2010 NVIDIA Corporation.
|
||||
* Copyright (C) 2016 Linaro Ltd.
|
||||
*
|
||||
* Author: Samu Onkalo <samu.p.onkalo@nokia.com>
|
||||
* Author: Linus Walleij <linus.walleij@linaro.org>
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h> /* For irq_get_irq_data() */
|
||||
#include <linux/completion.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
|
||||
/*
|
||||
* 16-bit registers are little-endian. LSB is at the address defined below
|
||||
* and MSB is at the next higher address.
|
||||
*/
|
||||
|
||||
/* These registers are common for AK8974 and AMI305 */
|
||||
#define AK8974_SELFTEST 0x0C
|
||||
#define AK8974_SELFTEST_IDLE 0x55
|
||||
#define AK8974_SELFTEST_OK 0xAA
|
||||
|
||||
#define AK8974_INFO 0x0D
|
||||
|
||||
#define AK8974_WHOAMI 0x0F
|
||||
#define AK8974_WHOAMI_VALUE_AMI305 0x47
|
||||
#define AK8974_WHOAMI_VALUE_AK8974 0x48
|
||||
|
||||
#define AK8974_DATA_X 0x10
|
||||
#define AK8974_DATA_Y 0x12
|
||||
#define AK8974_DATA_Z 0x14
|
||||
#define AK8974_INT_SRC 0x16
|
||||
#define AK8974_STATUS 0x18
|
||||
#define AK8974_INT_CLEAR 0x1A
|
||||
#define AK8974_CTRL1 0x1B
|
||||
#define AK8974_CTRL2 0x1C
|
||||
#define AK8974_CTRL3 0x1D
|
||||
#define AK8974_INT_CTRL 0x1E
|
||||
#define AK8974_INT_THRES 0x26 /* Absolute any axis value threshold */
|
||||
#define AK8974_PRESET 0x30
|
||||
|
||||
/* AK8974-specific offsets */
|
||||
#define AK8974_OFFSET_X 0x20
|
||||
#define AK8974_OFFSET_Y 0x22
|
||||
#define AK8974_OFFSET_Z 0x24
|
||||
/* AMI305-specific offsets */
|
||||
#define AMI305_OFFSET_X 0x6C
|
||||
#define AMI305_OFFSET_Y 0x72
|
||||
#define AMI305_OFFSET_Z 0x78
|
||||
|
||||
/* Different temperature registers */
|
||||
#define AK8974_TEMP 0x31
|
||||
#define AMI305_TEMP 0x60
|
||||
|
||||
#define AK8974_INT_X_HIGH BIT(7) /* Axis over +threshold */
|
||||
#define AK8974_INT_Y_HIGH BIT(6)
|
||||
#define AK8974_INT_Z_HIGH BIT(5)
|
||||
#define AK8974_INT_X_LOW BIT(4) /* Axis below -threshold */
|
||||
#define AK8974_INT_Y_LOW BIT(3)
|
||||
#define AK8974_INT_Z_LOW BIT(2)
|
||||
#define AK8974_INT_RANGE BIT(1) /* Range overflow (any axis) */
|
||||
|
||||
#define AK8974_STATUS_DRDY BIT(6) /* Data ready */
|
||||
#define AK8974_STATUS_OVERRUN BIT(5) /* Data overrun */
|
||||
#define AK8974_STATUS_INT BIT(4) /* Interrupt occurred */
|
||||
|
||||
#define AK8974_CTRL1_POWER BIT(7) /* 0 = standby; 1 = active */
|
||||
#define AK8974_CTRL1_RATE BIT(4) /* 0 = 10 Hz; 1 = 20 Hz */
|
||||
#define AK8974_CTRL1_FORCE_EN BIT(1) /* 0 = normal; 1 = force */
|
||||
#define AK8974_CTRL1_MODE2 BIT(0) /* 0 */
|
||||
|
||||
#define AK8974_CTRL2_INT_EN BIT(4) /* 1 = enable interrupts */
|
||||
#define AK8974_CTRL2_DRDY_EN BIT(3) /* 1 = enable data ready signal */
|
||||
#define AK8974_CTRL2_DRDY_POL BIT(2) /* 1 = data ready active high */
|
||||
#define AK8974_CTRL2_RESDEF (AK8974_CTRL2_DRDY_POL)
|
||||
|
||||
#define AK8974_CTRL3_RESET BIT(7) /* Software reset */
|
||||
#define AK8974_CTRL3_FORCE BIT(6) /* Start forced measurement */
|
||||
#define AK8974_CTRL3_SELFTEST BIT(4) /* Set selftest register */
|
||||
#define AK8974_CTRL3_RESDEF 0x00
|
||||
|
||||
#define AK8974_INT_CTRL_XEN BIT(7) /* Enable interrupt for this axis */
|
||||
#define AK8974_INT_CTRL_YEN BIT(6)
|
||||
#define AK8974_INT_CTRL_ZEN BIT(5)
|
||||
#define AK8974_INT_CTRL_XYZEN (BIT(7)|BIT(6)|BIT(5))
|
||||
#define AK8974_INT_CTRL_POL BIT(3) /* 0 = active low; 1 = active high */
|
||||
#define AK8974_INT_CTRL_PULSE BIT(1) /* 0 = latched; 1 = pulse (50 usec) */
|
||||
#define AK8974_INT_CTRL_RESDEF (AK8974_INT_CTRL_XYZEN | AK8974_INT_CTRL_POL)
|
||||
|
||||
/* The AMI305 has elaborate FW version and serial number registers */
|
||||
#define AMI305_VER 0xE8
|
||||
#define AMI305_SN 0xEA
|
||||
|
||||
#define AK8974_MAX_RANGE 2048
|
||||
|
||||
#define AK8974_POWERON_DELAY 50
|
||||
#define AK8974_ACTIVATE_DELAY 1
|
||||
#define AK8974_SELFTEST_DELAY 1
|
||||
/*
|
||||
* Set the autosuspend to two orders of magnitude larger than the poweron
|
||||
* delay to make sane reasonable power tradeoff savings (5 seconds in
|
||||
* this case).
|
||||
*/
|
||||
#define AK8974_AUTOSUSPEND_DELAY 5000
|
||||
|
||||
#define AK8974_MEASTIME 3
|
||||
|
||||
#define AK8974_PWR_ON 1
|
||||
#define AK8974_PWR_OFF 0
|
||||
|
||||
/**
|
||||
* struct ak8974 - state container for the AK8974 driver
|
||||
* @i2c: parent I2C client
|
||||
* @orientation: mounting matrix, flipped axis etc
|
||||
* @map: regmap to access the AK8974 registers over I2C
|
||||
* @regs: the avdd and dvdd power regulators
|
||||
* @name: the name of the part
|
||||
* @variant: the whoami ID value (for selecting code paths)
|
||||
* @lock: locks the magnetometer for exclusive use during a measurement
|
||||
* @drdy_irq: uses the DRDY IRQ line
|
||||
* @drdy_complete: completion for DRDY
|
||||
* @drdy_active_low: the DRDY IRQ is active low
|
||||
*/
|
||||
struct ak8974 {
|
||||
struct i2c_client *i2c;
|
||||
struct iio_mount_matrix orientation;
|
||||
struct regmap *map;
|
||||
struct regulator_bulk_data regs[2];
|
||||
const char *name;
|
||||
u8 variant;
|
||||
struct mutex lock;
|
||||
bool drdy_irq;
|
||||
struct completion drdy_complete;
|
||||
bool drdy_active_low;
|
||||
};
|
||||
|
||||
static const char ak8974_reg_avdd[] = "avdd";
|
||||
static const char ak8974_reg_dvdd[] = "dvdd";
|
||||
|
||||
static int ak8974_set_power(struct ak8974 *ak8974, bool mode)
|
||||
{
|
||||
int ret;
|
||||
u8 val;
|
||||
|
||||
val = mode ? AK8974_CTRL1_POWER : 0;
|
||||
val |= AK8974_CTRL1_FORCE_EN;
|
||||
ret = regmap_write(ak8974->map, AK8974_CTRL1, val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (mode)
|
||||
msleep(AK8974_ACTIVATE_DELAY);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ak8974_reset(struct ak8974 *ak8974)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Power on to get register access. Sets CTRL1 reg to reset state */
|
||||
ret = ak8974_set_power(ak8974, AK8974_PWR_ON);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = regmap_write(ak8974->map, AK8974_CTRL2, AK8974_CTRL2_RESDEF);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = regmap_write(ak8974->map, AK8974_CTRL3, AK8974_CTRL3_RESDEF);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = regmap_write(ak8974->map, AK8974_INT_CTRL,
|
||||
AK8974_INT_CTRL_RESDEF);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* After reset, power off is default state */
|
||||
return ak8974_set_power(ak8974, AK8974_PWR_OFF);
|
||||
}
|
||||
|
||||
static int ak8974_configure(struct ak8974 *ak8974)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = regmap_write(ak8974->map, AK8974_CTRL2, AK8974_CTRL2_DRDY_EN |
|
||||
AK8974_CTRL2_INT_EN);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = regmap_write(ak8974->map, AK8974_CTRL3, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = regmap_write(ak8974->map, AK8974_INT_CTRL, AK8974_INT_CTRL_POL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return regmap_write(ak8974->map, AK8974_PRESET, 0);
|
||||
}
|
||||
|
||||
static int ak8974_trigmeas(struct ak8974 *ak8974)
|
||||
{
|
||||
unsigned int clear;
|
||||
u8 mask;
|
||||
u8 val;
|
||||
int ret;
|
||||
|
||||
/* Clear any previous measurement overflow status */
|
||||
ret = regmap_read(ak8974->map, AK8974_INT_CLEAR, &clear);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* If we have a DRDY IRQ line, use it */
|
||||
if (ak8974->drdy_irq) {
|
||||
mask = AK8974_CTRL2_INT_EN |
|
||||
AK8974_CTRL2_DRDY_EN |
|
||||
AK8974_CTRL2_DRDY_POL;
|
||||
val = AK8974_CTRL2_DRDY_EN;
|
||||
|
||||
if (!ak8974->drdy_active_low)
|
||||
val |= AK8974_CTRL2_DRDY_POL;
|
||||
|
||||
init_completion(&ak8974->drdy_complete);
|
||||
ret = regmap_update_bits(ak8974->map, AK8974_CTRL2,
|
||||
mask, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Force a measurement */
|
||||
return regmap_update_bits(ak8974->map,
|
||||
AK8974_CTRL3,
|
||||
AK8974_CTRL3_FORCE,
|
||||
AK8974_CTRL3_FORCE);
|
||||
}
|
||||
|
||||
static int ak8974_await_drdy(struct ak8974 *ak8974)
|
||||
{
|
||||
int timeout = 2;
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
if (ak8974->drdy_irq) {
|
||||
ret = wait_for_completion_timeout(&ak8974->drdy_complete,
|
||||
1 + msecs_to_jiffies(1000));
|
||||
if (!ret) {
|
||||
dev_err(&ak8974->i2c->dev,
|
||||
"timeout waiting for DRDY IRQ\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Default delay-based poll loop */
|
||||
do {
|
||||
msleep(AK8974_MEASTIME);
|
||||
ret = regmap_read(ak8974->map, AK8974_STATUS, &val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (val & AK8974_STATUS_DRDY)
|
||||
return 0;
|
||||
} while (--timeout);
|
||||
if (!timeout) {
|
||||
dev_err(&ak8974->i2c->dev,
|
||||
"timeout waiting for DRDY\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ak8974_getresult(struct ak8974 *ak8974, s16 *result)
|
||||
{
|
||||
unsigned int src;
|
||||
int ret;
|
||||
|
||||
ret = ak8974_await_drdy(ak8974);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = regmap_read(ak8974->map, AK8974_INT_SRC, &src);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Out of range overflow! Strong magnet close? */
|
||||
if (src & AK8974_INT_RANGE) {
|
||||
dev_err(&ak8974->i2c->dev,
|
||||
"range overflow in sensor\n");
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
ret = regmap_bulk_read(ak8974->map, AK8974_DATA_X, result, 6);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static irqreturn_t ak8974_drdy_irq(int irq, void *d)
|
||||
{
|
||||
struct ak8974 *ak8974 = d;
|
||||
|
||||
if (!ak8974->drdy_irq)
|
||||
return IRQ_NONE;
|
||||
|
||||
/* TODO: timestamp here to get good measurement stamps */
|
||||
return IRQ_WAKE_THREAD;
|
||||
}
|
||||
|
||||
static irqreturn_t ak8974_drdy_irq_thread(int irq, void *d)
|
||||
{
|
||||
struct ak8974 *ak8974 = d;
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
/* Check if this was a DRDY from us */
|
||||
ret = regmap_read(ak8974->map, AK8974_STATUS, &val);
|
||||
if (ret < 0) {
|
||||
dev_err(&ak8974->i2c->dev, "error reading DRDY status\n");
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
if (val & AK8974_STATUS_DRDY) {
|
||||
/* Yes this was our IRQ */
|
||||
complete(&ak8974->drdy_complete);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/* We may be on a shared IRQ, let the next client check */
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
static int ak8974_selftest(struct ak8974 *ak8974)
|
||||
{
|
||||
struct device *dev = &ak8974->i2c->dev;
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(ak8974->map, AK8974_SELFTEST, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (val != AK8974_SELFTEST_IDLE) {
|
||||
dev_err(dev, "selftest not idle before test\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Trigger self-test */
|
||||
ret = regmap_update_bits(ak8974->map,
|
||||
AK8974_CTRL3,
|
||||
AK8974_CTRL3_SELFTEST,
|
||||
AK8974_CTRL3_SELFTEST);
|
||||
if (ret) {
|
||||
dev_err(dev, "could not write CTRL3\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
msleep(AK8974_SELFTEST_DELAY);
|
||||
|
||||
ret = regmap_read(ak8974->map, AK8974_SELFTEST, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (val != AK8974_SELFTEST_OK) {
|
||||
dev_err(dev, "selftest result NOT OK (%02x)\n", val);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
ret = regmap_read(ak8974->map, AK8974_SELFTEST, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (val != AK8974_SELFTEST_IDLE) {
|
||||
dev_err(dev, "selftest not idle after test (%02x)\n", val);
|
||||
return -EIO;
|
||||
}
|
||||
dev_dbg(dev, "passed self-test\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ak8974_get_u16_val(struct ak8974 *ak8974, u8 reg, u16 *val)
|
||||
{
|
||||
int ret;
|
||||
u16 bulk;
|
||||
|
||||
ret = regmap_bulk_read(ak8974->map, reg, &bulk, 2);
|
||||
if (ret)
|
||||
return ret;
|
||||
*val = le16_to_cpu(bulk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ak8974_detect(struct ak8974 *ak8974)
|
||||
{
|
||||
unsigned int whoami;
|
||||
const char *name;
|
||||
int ret;
|
||||
unsigned int fw;
|
||||
u16 sn;
|
||||
|
||||
ret = regmap_read(ak8974->map, AK8974_WHOAMI, &whoami);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
switch (whoami) {
|
||||
case AK8974_WHOAMI_VALUE_AMI305:
|
||||
name = "ami305";
|
||||
ret = regmap_read(ak8974->map, AMI305_VER, &fw);
|
||||
if (ret)
|
||||
return ret;
|
||||
fw &= 0x7f; /* only bits 0 thru 6 valid */
|
||||
ret = ak8974_get_u16_val(ak8974, AMI305_SN, &sn);
|
||||
if (ret)
|
||||
return ret;
|
||||
dev_info(&ak8974->i2c->dev,
|
||||
"detected %s, FW ver %02x, S/N: %04x\n",
|
||||
name, fw, sn);
|
||||
break;
|
||||
case AK8974_WHOAMI_VALUE_AK8974:
|
||||
name = "ak8974";
|
||||
dev_info(&ak8974->i2c->dev, "detected AK8974\n");
|
||||
break;
|
||||
default:
|
||||
dev_err(&ak8974->i2c->dev, "unsupported device (%02x) ",
|
||||
whoami);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ak8974->name = name;
|
||||
ak8974->variant = whoami;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ak8974_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2,
|
||||
long mask)
|
||||
{
|
||||
struct ak8974 *ak8974 = iio_priv(indio_dev);
|
||||
s16 hw_values[3];
|
||||
int ret = -EINVAL;
|
||||
|
||||
pm_runtime_get_sync(&ak8974->i2c->dev);
|
||||
mutex_lock(&ak8974->lock);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (chan->address > 2) {
|
||||
dev_err(&ak8974->i2c->dev, "faulty channel address\n");
|
||||
ret = -EIO;
|
||||
goto out_unlock;
|
||||
}
|
||||
ret = ak8974_trigmeas(ak8974);
|
||||
if (ret)
|
||||
goto out_unlock;
|
||||
ret = ak8974_getresult(ak8974, hw_values);
|
||||
if (ret)
|
||||
goto out_unlock;
|
||||
|
||||
/*
|
||||
* We read all axes and discard all but one, for optimized
|
||||
* reading, use the triggered buffer.
|
||||
*/
|
||||
*val = le16_to_cpu(hw_values[chan->address]);
|
||||
|
||||
ret = IIO_VAL_INT;
|
||||
}
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&ak8974->lock);
|
||||
pm_runtime_mark_last_busy(&ak8974->i2c->dev);
|
||||
pm_runtime_put_autosuspend(&ak8974->i2c->dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ak8974_fill_buffer(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct ak8974 *ak8974 = iio_priv(indio_dev);
|
||||
int ret;
|
||||
s16 hw_values[8]; /* Three axes + 64bit padding */
|
||||
|
||||
pm_runtime_get_sync(&ak8974->i2c->dev);
|
||||
mutex_lock(&ak8974->lock);
|
||||
|
||||
ret = ak8974_trigmeas(ak8974);
|
||||
if (ret) {
|
||||
dev_err(&ak8974->i2c->dev, "error triggering measure\n");
|
||||
goto out_unlock;
|
||||
}
|
||||
ret = ak8974_getresult(ak8974, hw_values);
|
||||
if (ret) {
|
||||
dev_err(&ak8974->i2c->dev, "error getting measures\n");
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, hw_values,
|
||||
iio_get_time_ns(indio_dev));
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&ak8974->lock);
|
||||
pm_runtime_mark_last_busy(&ak8974->i2c->dev);
|
||||
pm_runtime_put_autosuspend(&ak8974->i2c->dev);
|
||||
}
|
||||
|
||||
static irqreturn_t ak8974_handle_trigger(int irq, void *p)
|
||||
{
|
||||
const struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
|
||||
ak8974_fill_buffer(indio_dev);
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static const struct iio_mount_matrix *
|
||||
ak8974_get_mount_matrix(const struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan)
|
||||
{
|
||||
struct ak8974 *ak8974 = iio_priv(indio_dev);
|
||||
|
||||
return &ak8974->orientation;
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec_ext_info ak8974_ext_info[] = {
|
||||
IIO_MOUNT_MATRIX(IIO_SHARED_BY_DIR, ak8974_get_mount_matrix),
|
||||
{ },
|
||||
};
|
||||
|
||||
#define AK8974_AXIS_CHANNEL(axis, index) \
|
||||
{ \
|
||||
.type = IIO_MAGN, \
|
||||
.modified = 1, \
|
||||
.channel2 = IIO_MOD_##axis, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.ext_info = ak8974_ext_info, \
|
||||
.address = index, \
|
||||
.scan_index = index, \
|
||||
.scan_type = { \
|
||||
.sign = 's', \
|
||||
.realbits = 16, \
|
||||
.storagebits = 16, \
|
||||
.endianness = IIO_LE \
|
||||
}, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec ak8974_channels[] = {
|
||||
AK8974_AXIS_CHANNEL(X, 0),
|
||||
AK8974_AXIS_CHANNEL(Y, 1),
|
||||
AK8974_AXIS_CHANNEL(Z, 2),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(3),
|
||||
};
|
||||
|
||||
static const unsigned long ak8974_scan_masks[] = { 0x7, 0 };
|
||||
|
||||
static const struct iio_info ak8974_info = {
|
||||
.read_raw = &ak8974_read_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static bool ak8974_writeable_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
struct i2c_client *i2c = to_i2c_client(dev);
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(i2c);
|
||||
struct ak8974 *ak8974 = iio_priv(indio_dev);
|
||||
|
||||
switch (reg) {
|
||||
case AK8974_CTRL1:
|
||||
case AK8974_CTRL2:
|
||||
case AK8974_CTRL3:
|
||||
case AK8974_INT_CTRL:
|
||||
case AK8974_INT_THRES:
|
||||
case AK8974_INT_THRES + 1:
|
||||
case AK8974_PRESET:
|
||||
case AK8974_PRESET + 1:
|
||||
return true;
|
||||
case AK8974_OFFSET_X:
|
||||
case AK8974_OFFSET_X + 1:
|
||||
case AK8974_OFFSET_Y:
|
||||
case AK8974_OFFSET_Y + 1:
|
||||
case AK8974_OFFSET_Z:
|
||||
case AK8974_OFFSET_Z + 1:
|
||||
if (ak8974->variant == AK8974_WHOAMI_VALUE_AK8974)
|
||||
return true;
|
||||
return false;
|
||||
case AMI305_OFFSET_X:
|
||||
case AMI305_OFFSET_X + 1:
|
||||
case AMI305_OFFSET_Y:
|
||||
case AMI305_OFFSET_Y + 1:
|
||||
case AMI305_OFFSET_Z:
|
||||
case AMI305_OFFSET_Z + 1:
|
||||
if (ak8974->variant == AK8974_WHOAMI_VALUE_AMI305)
|
||||
return true;
|
||||
return false;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct regmap_config ak8974_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.max_register = 0xff,
|
||||
.writeable_reg = ak8974_writeable_reg,
|
||||
};
|
||||
|
||||
static int ak8974_probe(struct i2c_client *i2c,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct ak8974 *ak8974;
|
||||
unsigned long irq_trig;
|
||||
int irq = i2c->irq;
|
||||
int ret;
|
||||
|
||||
/* Register with IIO */
|
||||
indio_dev = devm_iio_device_alloc(&i2c->dev, sizeof(*ak8974));
|
||||
if (indio_dev == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
ak8974 = iio_priv(indio_dev);
|
||||
i2c_set_clientdata(i2c, indio_dev);
|
||||
ak8974->i2c = i2c;
|
||||
mutex_init(&ak8974->lock);
|
||||
|
||||
ret = of_iio_read_mount_matrix(&i2c->dev,
|
||||
"mount-matrix",
|
||||
&ak8974->orientation);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ak8974->regs[0].supply = ak8974_reg_avdd;
|
||||
ak8974->regs[1].supply = ak8974_reg_dvdd;
|
||||
|
||||
ret = devm_regulator_bulk_get(&i2c->dev,
|
||||
ARRAY_SIZE(ak8974->regs),
|
||||
ak8974->regs);
|
||||
if (ret < 0) {
|
||||
dev_err(&i2c->dev, "cannot get regulators\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regulator_bulk_enable(ARRAY_SIZE(ak8974->regs), ak8974->regs);
|
||||
if (ret < 0) {
|
||||
dev_err(&i2c->dev, "cannot enable regulators\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Take runtime PM online */
|
||||
pm_runtime_get_noresume(&i2c->dev);
|
||||
pm_runtime_set_active(&i2c->dev);
|
||||
pm_runtime_enable(&i2c->dev);
|
||||
|
||||
ak8974->map = devm_regmap_init_i2c(i2c, &ak8974_regmap_config);
|
||||
if (IS_ERR(ak8974->map)) {
|
||||
dev_err(&i2c->dev, "failed to allocate register map\n");
|
||||
return PTR_ERR(ak8974->map);
|
||||
}
|
||||
|
||||
ret = ak8974_set_power(ak8974, AK8974_PWR_ON);
|
||||
if (ret) {
|
||||
dev_err(&i2c->dev, "could not power on\n");
|
||||
goto power_off;
|
||||
}
|
||||
|
||||
ret = ak8974_detect(ak8974);
|
||||
if (ret) {
|
||||
dev_err(&i2c->dev, "neither AK8974 nor AMI305 found\n");
|
||||
goto power_off;
|
||||
}
|
||||
|
||||
ret = ak8974_selftest(ak8974);
|
||||
if (ret)
|
||||
dev_err(&i2c->dev, "selftest failed (continuing anyway)\n");
|
||||
|
||||
ret = ak8974_reset(ak8974);
|
||||
if (ret) {
|
||||
dev_err(&i2c->dev, "AK8974 reset failed\n");
|
||||
goto power_off;
|
||||
}
|
||||
|
||||
pm_runtime_set_autosuspend_delay(&i2c->dev,
|
||||
AK8974_AUTOSUSPEND_DELAY);
|
||||
pm_runtime_use_autosuspend(&i2c->dev);
|
||||
pm_runtime_put(&i2c->dev);
|
||||
|
||||
indio_dev->dev.parent = &i2c->dev;
|
||||
indio_dev->channels = ak8974_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(ak8974_channels);
|
||||
indio_dev->info = &ak8974_info;
|
||||
indio_dev->available_scan_masks = ak8974_scan_masks;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->name = ak8974->name;
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, NULL,
|
||||
ak8974_handle_trigger,
|
||||
NULL);
|
||||
if (ret) {
|
||||
dev_err(&i2c->dev, "triggered buffer setup failed\n");
|
||||
goto disable_pm;
|
||||
}
|
||||
|
||||
/* If we have a valid DRDY IRQ, make use of it */
|
||||
if (irq > 0) {
|
||||
irq_trig = irqd_get_trigger_type(irq_get_irq_data(irq));
|
||||
if (irq_trig == IRQF_TRIGGER_RISING) {
|
||||
dev_info(&i2c->dev, "enable rising edge DRDY IRQ\n");
|
||||
} else if (irq_trig == IRQF_TRIGGER_FALLING) {
|
||||
ak8974->drdy_active_low = true;
|
||||
dev_info(&i2c->dev, "enable falling edge DRDY IRQ\n");
|
||||
} else {
|
||||
irq_trig = IRQF_TRIGGER_RISING;
|
||||
}
|
||||
irq_trig |= IRQF_ONESHOT;
|
||||
irq_trig |= IRQF_SHARED;
|
||||
|
||||
ret = devm_request_threaded_irq(&i2c->dev,
|
||||
irq,
|
||||
ak8974_drdy_irq,
|
||||
ak8974_drdy_irq_thread,
|
||||
irq_trig,
|
||||
ak8974->name,
|
||||
ak8974);
|
||||
if (ret) {
|
||||
dev_err(&i2c->dev, "unable to request DRDY IRQ "
|
||||
"- proceeding without IRQ\n");
|
||||
goto no_irq;
|
||||
}
|
||||
ak8974->drdy_irq = true;
|
||||
}
|
||||
|
||||
no_irq:
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret) {
|
||||
dev_err(&i2c->dev, "device register failed\n");
|
||||
goto cleanup_buffer;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
cleanup_buffer:
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
disable_pm:
|
||||
pm_runtime_put_noidle(&i2c->dev);
|
||||
pm_runtime_disable(&i2c->dev);
|
||||
ak8974_set_power(ak8974, AK8974_PWR_OFF);
|
||||
power_off:
|
||||
regulator_bulk_disable(ARRAY_SIZE(ak8974->regs), ak8974->regs);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __exit ak8974_remove(struct i2c_client *i2c)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(i2c);
|
||||
struct ak8974 *ak8974 = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
pm_runtime_get_sync(&i2c->dev);
|
||||
pm_runtime_put_noidle(&i2c->dev);
|
||||
pm_runtime_disable(&i2c->dev);
|
||||
ak8974_set_power(ak8974, AK8974_PWR_OFF);
|
||||
regulator_bulk_disable(ARRAY_SIZE(ak8974->regs), ak8974->regs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int ak8974_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct ak8974 *ak8974 =
|
||||
iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
|
||||
|
||||
ak8974_set_power(ak8974, AK8974_PWR_OFF);
|
||||
regulator_bulk_disable(ARRAY_SIZE(ak8974->regs), ak8974->regs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ak8974_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct ak8974 *ak8974 =
|
||||
iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
|
||||
int ret;
|
||||
|
||||
ret = regulator_bulk_enable(ARRAY_SIZE(ak8974->regs), ak8974->regs);
|
||||
if (ret)
|
||||
return ret;
|
||||
msleep(AK8974_POWERON_DELAY);
|
||||
ret = ak8974_set_power(ak8974, AK8974_PWR_ON);
|
||||
if (ret)
|
||||
goto out_regulator_disable;
|
||||
|
||||
ret = ak8974_configure(ak8974);
|
||||
if (ret)
|
||||
goto out_disable_power;
|
||||
|
||||
return 0;
|
||||
|
||||
out_disable_power:
|
||||
ak8974_set_power(ak8974, AK8974_PWR_OFF);
|
||||
out_regulator_disable:
|
||||
regulator_bulk_disable(ARRAY_SIZE(ak8974->regs), ak8974->regs);
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
static const struct dev_pm_ops ak8974_dev_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
|
||||
pm_runtime_force_resume)
|
||||
SET_RUNTIME_PM_OPS(ak8974_runtime_suspend,
|
||||
ak8974_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
static const struct i2c_device_id ak8974_id[] = {
|
||||
{"ami305", 0 },
|
||||
{"ak8974", 0 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ak8974_id);
|
||||
|
||||
static const struct of_device_id ak8974_of_match[] = {
|
||||
{ .compatible = "asahi-kasei,ak8974", },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ak8974_of_match);
|
||||
|
||||
static struct i2c_driver ak8974_driver = {
|
||||
.driver = {
|
||||
.name = "ak8974",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &ak8974_dev_pm_ops,
|
||||
.of_match_table = of_match_ptr(ak8974_of_match),
|
||||
},
|
||||
.probe = ak8974_probe,
|
||||
.remove = __exit_p(ak8974_remove),
|
||||
.id_table = ak8974_id,
|
||||
};
|
||||
module_i2c_driver(ak8974_driver);
|
||||
|
||||
MODULE_DESCRIPTION("AK8974 and AMI305 3-axis magnetometer driver");
|
||||
MODULE_AUTHOR("Samu Onkalo");
|
||||
MODULE_AUTHOR("Linus Walleij");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -154,34 +154,41 @@ static int mag3110_read_raw(struct iio_dev *indio_dev,
|
|||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (iio_buffer_enabled(indio_dev))
|
||||
return -EBUSY;
|
||||
ret = iio_device_claim_direct_mode(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
switch (chan->type) {
|
||||
case IIO_MAGN: /* in 0.1 uT / LSB */
|
||||
ret = mag3110_read(data, buffer);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto release;
|
||||
*val = sign_extend32(
|
||||
be16_to_cpu(buffer[chan->scan_index]), 15);
|
||||
return IIO_VAL_INT;
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
case IIO_TEMP: /* in 1 C / LSB */
|
||||
mutex_lock(&data->lock);
|
||||
ret = mag3110_request(data);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&data->lock);
|
||||
return ret;
|
||||
goto release;
|
||||
}
|
||||
ret = i2c_smbus_read_byte_data(data->client,
|
||||
MAG3110_DIE_TEMP);
|
||||
mutex_unlock(&data->lock);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto release;
|
||||
*val = sign_extend32(ret, 7);
|
||||
return IIO_VAL_INT;
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
ret = -EINVAL;
|
||||
}
|
||||
release:
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
return ret;
|
||||
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
switch (chan->type) {
|
||||
case IIO_MAGN:
|
||||
|
|
|
@ -516,7 +516,7 @@ static irqreturn_t sx9500_irq_thread_handler(int irq, void *private)
|
|||
sx9500_push_events(indio_dev);
|
||||
|
||||
if (val & SX9500_CONVDONE_IRQ)
|
||||
complete_all(&data->completion);
|
||||
complete(&data->completion);
|
||||
|
||||
out:
|
||||
mutex_unlock(&data->mutex);
|
||||
|
|
|
@ -3,6 +3,22 @@
|
|||
#
|
||||
menu "Temperature sensors"
|
||||
|
||||
config MAXIM_THERMOCOUPLE
|
||||
tristate "Maxim thermocouple sensors"
|
||||
depends on SPI
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
If you say yes here you get support for the Maxim series of
|
||||
thermocouple sensors connected via SPI.
|
||||
|
||||
Supported sensors:
|
||||
* MAX6675
|
||||
* MAX31855
|
||||
|
||||
This driver can also be built as a module. If so, the module will
|
||||
be called maxim_thermocouple.
|
||||
|
||||
config MLX90614
|
||||
tristate "MLX90614 contact-less infrared sensor"
|
||||
depends on I2C
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
# Makefile for industrial I/O temperature drivers
|
||||
#
|
||||
|
||||
obj-$(CONFIG_MAXIM_THERMOCOUPLE) += maxim_thermocouple.o
|
||||
obj-$(CONFIG_MLX90614) += mlx90614.o
|
||||
obj-$(CONFIG_TMP006) += tmp006.o
|
||||
obj-$(CONFIG_TSYS01) += tsys01.o
|
||||
|
|
|
@ -0,0 +1,281 @@
|
|||
/*
|
||||
* maxim_thermocouple.c - Support for Maxim thermocouple chips
|
||||
*
|
||||
* Copyright (C) 2016 Matt Ranostay <mranostay@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
|
||||
#define MAXIM_THERMOCOUPLE_DRV_NAME "maxim_thermocouple"
|
||||
|
||||
enum {
|
||||
MAX6675,
|
||||
MAX31855,
|
||||
};
|
||||
|
||||
const struct iio_chan_spec max6675_channels[] = {
|
||||
{ /* thermocouple temperature */
|
||||
.type = IIO_TEMP,
|
||||
.info_mask_separate =
|
||||
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
|
||||
.scan_index = 0,
|
||||
.scan_type = {
|
||||
.sign = 's',
|
||||
.realbits = 13,
|
||||
.storagebits = 16,
|
||||
.shift = 3,
|
||||
.endianness = IIO_BE,
|
||||
},
|
||||
},
|
||||
IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
};
|
||||
|
||||
const struct iio_chan_spec max31855_channels[] = {
|
||||
{ /* thermocouple temperature */
|
||||
.type = IIO_TEMP,
|
||||
.address = 2,
|
||||
.info_mask_separate =
|
||||
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
|
||||
.scan_index = 0,
|
||||
.scan_type = {
|
||||
.sign = 's',
|
||||
.realbits = 14,
|
||||
.storagebits = 16,
|
||||
.shift = 2,
|
||||
.endianness = IIO_BE,
|
||||
},
|
||||
},
|
||||
{ /* cold junction temperature */
|
||||
.type = IIO_TEMP,
|
||||
.address = 0,
|
||||
.channel2 = IIO_MOD_TEMP_AMBIENT,
|
||||
.modified = 1,
|
||||
.info_mask_separate =
|
||||
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
|
||||
.scan_index = 1,
|
||||
.scan_type = {
|
||||
.sign = 's',
|
||||
.realbits = 12,
|
||||
.storagebits = 16,
|
||||
.shift = 4,
|
||||
.endianness = IIO_BE,
|
||||
},
|
||||
},
|
||||
IIO_CHAN_SOFT_TIMESTAMP(2),
|
||||
};
|
||||
|
||||
static const unsigned long max31855_scan_masks[] = {0x3, 0};
|
||||
|
||||
struct maxim_thermocouple_chip {
|
||||
const struct iio_chan_spec *channels;
|
||||
const unsigned long *scan_masks;
|
||||
u8 num_channels;
|
||||
u8 read_size;
|
||||
|
||||
/* bit-check for valid input */
|
||||
u32 status_bit;
|
||||
};
|
||||
|
||||
const struct maxim_thermocouple_chip maxim_thermocouple_chips[] = {
|
||||
[MAX6675] = {
|
||||
.channels = max6675_channels,
|
||||
.num_channels = ARRAY_SIZE(max6675_channels),
|
||||
.read_size = 2,
|
||||
.status_bit = BIT(2),
|
||||
},
|
||||
[MAX31855] = {
|
||||
.channels = max31855_channels,
|
||||
.num_channels = ARRAY_SIZE(max31855_channels),
|
||||
.read_size = 4,
|
||||
.scan_masks = max31855_scan_masks,
|
||||
.status_bit = BIT(16),
|
||||
},
|
||||
};
|
||||
|
||||
struct maxim_thermocouple_data {
|
||||
struct spi_device *spi;
|
||||
const struct maxim_thermocouple_chip *chip;
|
||||
|
||||
u8 buffer[16] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
static int maxim_thermocouple_read(struct maxim_thermocouple_data *data,
|
||||
struct iio_chan_spec const *chan, int *val)
|
||||
{
|
||||
unsigned int storage_bytes = data->chip->read_size;
|
||||
unsigned int shift = chan->scan_type.shift + (chan->address * 8);
|
||||
unsigned int buf;
|
||||
int ret;
|
||||
|
||||
ret = spi_read(data->spi, (void *) &buf, storage_bytes);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
switch (storage_bytes) {
|
||||
case 2:
|
||||
*val = be16_to_cpu(buf);
|
||||
break;
|
||||
case 4:
|
||||
*val = be32_to_cpu(buf);
|
||||
break;
|
||||
}
|
||||
|
||||
/* check to be sure this is a valid reading */
|
||||
if (*val & data->chip->status_bit)
|
||||
return -EINVAL;
|
||||
|
||||
*val = sign_extend32(*val >> shift, chan->scan_type.realbits - 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t maxim_thermocouple_trigger_handler(int irq, void *private)
|
||||
{
|
||||
struct iio_poll_func *pf = private;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct maxim_thermocouple_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = spi_read(data->spi, data->buffer, data->chip->read_size);
|
||||
if (!ret) {
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
|
||||
iio_get_time_ns(indio_dev));
|
||||
}
|
||||
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int maxim_thermocouple_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct maxim_thermocouple_data *data = iio_priv(indio_dev);
|
||||
int ret = -EINVAL;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = iio_device_claim_direct_mode(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = maxim_thermocouple_read(data, chan, val);
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
|
||||
if (!ret)
|
||||
return IIO_VAL_INT;
|
||||
|
||||
break;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
switch (chan->channel2) {
|
||||
case IIO_MOD_TEMP_AMBIENT:
|
||||
*val = 62;
|
||||
*val2 = 500000; /* 1000 * 0.0625 */
|
||||
ret = IIO_VAL_INT_PLUS_MICRO;
|
||||
break;
|
||||
default:
|
||||
*val = 250; /* 1000 * 0.25 */
|
||||
ret = IIO_VAL_INT;
|
||||
};
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct iio_info maxim_thermocouple_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = maxim_thermocouple_read_raw,
|
||||
};
|
||||
|
||||
static int maxim_thermocouple_probe(struct spi_device *spi)
|
||||
{
|
||||
const struct spi_device_id *id = spi_get_device_id(spi);
|
||||
struct iio_dev *indio_dev;
|
||||
struct maxim_thermocouple_data *data;
|
||||
const struct maxim_thermocouple_chip *chip =
|
||||
&maxim_thermocouple_chips[id->driver_data];
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
indio_dev->info = &maxim_thermocouple_info;
|
||||
indio_dev->name = MAXIM_THERMOCOUPLE_DRV_NAME;
|
||||
indio_dev->channels = chip->channels;
|
||||
indio_dev->available_scan_masks = chip->scan_masks;
|
||||
indio_dev->num_channels = chip->num_channels;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
data = iio_priv(indio_dev);
|
||||
data->spi = spi;
|
||||
data->chip = chip;
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, NULL,
|
||||
maxim_thermocouple_trigger_handler, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto error_unreg_buffer;
|
||||
|
||||
return 0;
|
||||
|
||||
error_unreg_buffer:
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int maxim_thermocouple_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 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id maxim_thermocouple_id[] = {
|
||||
{"max6675", MAX6675},
|
||||
{"max31855", MAX31855},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, maxim_thermocouple_id);
|
||||
|
||||
static struct spi_driver maxim_thermocouple_driver = {
|
||||
.driver = {
|
||||
.name = MAXIM_THERMOCOUPLE_DRV_NAME,
|
||||
},
|
||||
.probe = maxim_thermocouple_probe,
|
||||
.remove = maxim_thermocouple_remove,
|
||||
.id_table = maxim_thermocouple_id,
|
||||
};
|
||||
module_spi_driver(maxim_thermocouple_driver);
|
||||
|
||||
MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
|
||||
MODULE_DESCRIPTION("Maxim thermocouple sensors");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -32,25 +32,25 @@
|
|||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/acpi.h>
|
||||
|
||||
#define CONVERSION_TIME_MS 100
|
||||
#define ISL29018_CONV_TIME_MS 100
|
||||
|
||||
#define ISL29018_REG_ADD_COMMAND1 0x00
|
||||
#define COMMMAND1_OPMODE_SHIFT 5
|
||||
#define COMMMAND1_OPMODE_MASK (7 << COMMMAND1_OPMODE_SHIFT)
|
||||
#define COMMMAND1_OPMODE_POWER_DOWN 0
|
||||
#define COMMMAND1_OPMODE_ALS_ONCE 1
|
||||
#define COMMMAND1_OPMODE_IR_ONCE 2
|
||||
#define COMMMAND1_OPMODE_PROX_ONCE 3
|
||||
#define ISL29018_CMD1_OPMODE_SHIFT 5
|
||||
#define ISL29018_CMD1_OPMODE_MASK (7 << ISL29018_CMD1_OPMODE_SHIFT)
|
||||
#define ISL29018_CMD1_OPMODE_POWER_DOWN 0
|
||||
#define ISL29018_CMD1_OPMODE_ALS_ONCE 1
|
||||
#define ISL29018_CMD1_OPMODE_IR_ONCE 2
|
||||
#define ISL29018_CMD1_OPMODE_PROX_ONCE 3
|
||||
|
||||
#define ISL29018_REG_ADD_COMMANDII 0x01
|
||||
#define COMMANDII_RESOLUTION_SHIFT 2
|
||||
#define COMMANDII_RESOLUTION_MASK (0x3 << COMMANDII_RESOLUTION_SHIFT)
|
||||
#define ISL29018_REG_ADD_COMMAND2 0x01
|
||||
#define ISL29018_CMD2_RESOLUTION_SHIFT 2
|
||||
#define ISL29018_CMD2_RESOLUTION_MASK (0x3 << ISL29018_CMD2_RESOLUTION_SHIFT)
|
||||
|
||||
#define COMMANDII_RANGE_SHIFT 0
|
||||
#define COMMANDII_RANGE_MASK (0x3 << COMMANDII_RANGE_SHIFT)
|
||||
#define ISL29018_CMD2_RANGE_SHIFT 0
|
||||
#define ISL29018_CMD2_RANGE_MASK (0x3 << ISL29018_CMD2_RANGE_SHIFT)
|
||||
|
||||
#define COMMANDII_SCHEME_SHIFT 7
|
||||
#define COMMANDII_SCHEME_MASK (0x1 << COMMANDII_SCHEME_SHIFT)
|
||||
#define ISL29018_CMD2_SCHEME_SHIFT 7
|
||||
#define ISL29018_CMD2_SCHEME_MASK (0x1 << ISL29018_CMD2_SCHEME_SHIFT)
|
||||
|
||||
#define ISL29018_REG_ADD_DATA_LSB 0x02
|
||||
#define ISL29018_REG_ADD_DATA_MSB 0x03
|
||||
|
@ -127,13 +127,13 @@ static int isl29018_set_integration_time(struct isl29018_chip *chip,
|
|||
if (i >= ARRAY_SIZE(isl29018_int_utimes[chip->type]))
|
||||
return -EINVAL;
|
||||
|
||||
ret = regmap_update_bits(chip->regmap, ISL29018_REG_ADD_COMMANDII,
|
||||
COMMANDII_RESOLUTION_MASK,
|
||||
i << COMMANDII_RESOLUTION_SHIFT);
|
||||
ret = regmap_update_bits(chip->regmap, ISL29018_REG_ADD_COMMAND2,
|
||||
ISL29018_CMD2_RESOLUTION_MASK,
|
||||
i << ISL29018_CMD2_RESOLUTION_SHIFT);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* keep the same range when integration time changes */
|
||||
/* Keep the same range when integration time changes */
|
||||
int_time = chip->int_time;
|
||||
for (i = 0; i < ARRAY_SIZE(isl29018_scales[int_time]); ++i) {
|
||||
if (chip->scale.scale == isl29018_scales[int_time][i].scale &&
|
||||
|
@ -163,9 +163,9 @@ static int isl29018_set_scale(struct isl29018_chip *chip, int scale, int uscale)
|
|||
if (i >= ARRAY_SIZE(isl29018_scales[chip->int_time]))
|
||||
return -EINVAL;
|
||||
|
||||
ret = regmap_update_bits(chip->regmap, ISL29018_REG_ADD_COMMANDII,
|
||||
COMMANDII_RANGE_MASK,
|
||||
i << COMMANDII_RANGE_SHIFT);
|
||||
ret = regmap_update_bits(chip->regmap, ISL29018_REG_ADD_COMMAND2,
|
||||
ISL29018_CMD2_RANGE_MASK,
|
||||
i << ISL29018_CMD2_RANGE_SHIFT);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
@ -183,13 +183,13 @@ static int isl29018_read_sensor_input(struct isl29018_chip *chip, int mode)
|
|||
|
||||
/* Set mode */
|
||||
status = regmap_write(chip->regmap, ISL29018_REG_ADD_COMMAND1,
|
||||
mode << COMMMAND1_OPMODE_SHIFT);
|
||||
mode << ISL29018_CMD1_OPMODE_SHIFT);
|
||||
if (status) {
|
||||
dev_err(dev,
|
||||
"Error in setting operating mode err %d\n", status);
|
||||
return status;
|
||||
}
|
||||
msleep(CONVERSION_TIME_MS);
|
||||
msleep(ISL29018_CONV_TIME_MS);
|
||||
status = regmap_read(chip->regmap, ISL29018_REG_ADD_DATA_LSB, &lsb);
|
||||
if (status < 0) {
|
||||
dev_err(dev,
|
||||
|
@ -213,8 +213,8 @@ static int isl29018_read_lux(struct isl29018_chip *chip, int *lux)
|
|||
int lux_data;
|
||||
unsigned int data_x_range;
|
||||
|
||||
lux_data = isl29018_read_sensor_input(chip, COMMMAND1_OPMODE_ALS_ONCE);
|
||||
|
||||
lux_data = isl29018_read_sensor_input(chip,
|
||||
ISL29018_CMD1_OPMODE_ALS_ONCE);
|
||||
if (lux_data < 0)
|
||||
return lux_data;
|
||||
|
||||
|
@ -230,8 +230,8 @@ static int isl29018_read_ir(struct isl29018_chip *chip, int *ir)
|
|||
{
|
||||
int ir_data;
|
||||
|
||||
ir_data = isl29018_read_sensor_input(chip, COMMMAND1_OPMODE_IR_ONCE);
|
||||
|
||||
ir_data = isl29018_read_sensor_input(chip,
|
||||
ISL29018_CMD1_OPMODE_IR_ONCE);
|
||||
if (ir_data < 0)
|
||||
return ir_data;
|
||||
|
||||
|
@ -249,16 +249,16 @@ static int isl29018_read_proximity_ir(struct isl29018_chip *chip, int scheme,
|
|||
struct device *dev = regmap_get_device(chip->regmap);
|
||||
|
||||
/* Do proximity sensing with required scheme */
|
||||
status = regmap_update_bits(chip->regmap, ISL29018_REG_ADD_COMMANDII,
|
||||
COMMANDII_SCHEME_MASK,
|
||||
scheme << COMMANDII_SCHEME_SHIFT);
|
||||
status = regmap_update_bits(chip->regmap, ISL29018_REG_ADD_COMMAND2,
|
||||
ISL29018_CMD2_SCHEME_MASK,
|
||||
scheme << ISL29018_CMD2_SCHEME_SHIFT);
|
||||
if (status) {
|
||||
dev_err(dev, "Error in setting operating mode\n");
|
||||
return status;
|
||||
}
|
||||
|
||||
prox_data = isl29018_read_sensor_input(chip,
|
||||
COMMMAND1_OPMODE_PROX_ONCE);
|
||||
ISL29018_CMD1_OPMODE_PROX_ONCE);
|
||||
if (prox_data < 0)
|
||||
return prox_data;
|
||||
|
||||
|
@ -267,8 +267,8 @@ static int isl29018_read_proximity_ir(struct isl29018_chip *chip, int scheme,
|
|||
return 0;
|
||||
}
|
||||
|
||||
ir_data = isl29018_read_sensor_input(chip, COMMMAND1_OPMODE_IR_ONCE);
|
||||
|
||||
ir_data = isl29018_read_sensor_input(chip,
|
||||
ISL29018_CMD1_OPMODE_IR_ONCE);
|
||||
if (ir_data < 0)
|
||||
return ir_data;
|
||||
|
||||
|
@ -280,7 +280,7 @@ static int isl29018_read_proximity_ir(struct isl29018_chip *chip, int scheme,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t show_scale_available(struct device *dev,
|
||||
static ssize_t isl29018_show_scale_available(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
|
@ -297,7 +297,7 @@ static ssize_t show_scale_available(struct device *dev,
|
|||
return len;
|
||||
}
|
||||
|
||||
static ssize_t show_int_time_available(struct device *dev,
|
||||
static ssize_t isl29018_show_int_time_available(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
|
@ -313,8 +313,7 @@ static ssize_t show_int_time_available(struct device *dev,
|
|||
return len;
|
||||
}
|
||||
|
||||
/* proximity scheme */
|
||||
static ssize_t show_prox_infrared_suppression(struct device *dev,
|
||||
static ssize_t isl29018_show_prox_infrared_suppression(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
|
@ -322,13 +321,13 @@ static ssize_t show_prox_infrared_suppression(struct device *dev,
|
|||
struct isl29018_chip *chip = iio_priv(indio_dev);
|
||||
|
||||
/*
|
||||
* return the "proximity scheme" i.e. if the chip does on chip
|
||||
* Return the "proximity scheme" i.e. if the chip does on chip
|
||||
* infrared suppression (1 means perform on chip suppression)
|
||||
*/
|
||||
return sprintf(buf, "%d\n", chip->prox_scheme);
|
||||
}
|
||||
|
||||
static ssize_t store_prox_infrared_suppression(struct device *dev,
|
||||
static ssize_t isl29018_store_prox_infrared_suppression(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
|
@ -338,13 +337,11 @@ static ssize_t store_prox_infrared_suppression(struct device *dev,
|
|||
|
||||
if (kstrtoint(buf, 10, &val))
|
||||
return -EINVAL;
|
||||
if (!(val == 0 || val == 1)) {
|
||||
dev_err(dev, "The mode is not supported\n");
|
||||
if (!(val == 0 || val == 1))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* get the "proximity scheme" i.e. if the chip does on chip
|
||||
* Get the "proximity scheme" i.e. if the chip does on chip
|
||||
* infrared suppression (1 means perform on chip suppression)
|
||||
*/
|
||||
mutex_lock(&chip->lock);
|
||||
|
@ -354,7 +351,6 @@ static ssize_t store_prox_infrared_suppression(struct device *dev,
|
|||
return count;
|
||||
}
|
||||
|
||||
/* Channel IO */
|
||||
static int isl29018_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val,
|
||||
|
@ -491,13 +487,13 @@ static const struct iio_chan_spec isl29023_channels[] = {
|
|||
};
|
||||
|
||||
static IIO_DEVICE_ATTR(in_illuminance_integration_time_available, S_IRUGO,
|
||||
show_int_time_available, NULL, 0);
|
||||
isl29018_show_int_time_available, NULL, 0);
|
||||
static IIO_DEVICE_ATTR(in_illuminance_scale_available, S_IRUGO,
|
||||
show_scale_available, NULL, 0);
|
||||
isl29018_show_scale_available, NULL, 0);
|
||||
static IIO_DEVICE_ATTR(proximity_on_chip_ambient_infrared_suppression,
|
||||
S_IRUGO | S_IWUSR,
|
||||
show_prox_infrared_suppression,
|
||||
store_prox_infrared_suppression, 0);
|
||||
isl29018_show_prox_infrared_suppression,
|
||||
isl29018_store_prox_infrared_suppression, 0);
|
||||
|
||||
#define ISL29018_DEV_ATTR(name) (&iio_dev_attr_##name.dev_attr.attr)
|
||||
|
||||
|
@ -541,7 +537,7 @@ static int isl29035_detect(struct isl29018_chip *chip)
|
|||
if (id != ISL29035_DEVICE_ID)
|
||||
return -ENODEV;
|
||||
|
||||
/* clear out brownout bit */
|
||||
/* Clear brownout bit */
|
||||
return regmap_update_bits(chip->regmap, ISL29035_REG_DEVICE_ID,
|
||||
ISL29035_BOUT_MASK, 0);
|
||||
}
|
||||
|
@ -574,7 +570,7 @@ static int isl29018_chip_init(struct isl29018_chip *chip)
|
|||
* conversions, clear the test registers, and then rewrite all
|
||||
* registers to the desired values.
|
||||
* ...
|
||||
* FOR ISL29011, ISL29018, ISL29021, ISL29023
|
||||
* For ISL29011, ISL29018, ISL29021, ISL29023
|
||||
* 1. Write 0x00 to register 0x08 (TEST)
|
||||
* 2. Write 0x00 to register 0x00 (CMD1)
|
||||
* 3. Rewrite all registers to the desired values
|
||||
|
@ -603,7 +599,7 @@ static int isl29018_chip_init(struct isl29018_chip *chip)
|
|||
|
||||
usleep_range(1000, 2000); /* per data sheet, page 10 */
|
||||
|
||||
/* set defaults */
|
||||
/* Set defaults */
|
||||
status = isl29018_set_scale(chip, chip->scale.scale,
|
||||
chip->scale.uscale);
|
||||
if (status < 0) {
|
||||
|
@ -635,7 +631,7 @@ static const struct iio_info isl29023_info = {
|
|||
.write_raw = isl29018_write_raw,
|
||||
};
|
||||
|
||||
static bool is_volatile_reg(struct device *dev, unsigned int reg)
|
||||
static bool isl29018_is_volatile_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case ISL29018_REG_ADD_DATA_LSB:
|
||||
|
@ -649,37 +645,32 @@ static bool is_volatile_reg(struct device *dev, unsigned int reg)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* isl29018_regmap_config: regmap configuration.
|
||||
* Use RBTREE mechanism for caching.
|
||||
*/
|
||||
static const struct regmap_config isl29018_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.volatile_reg = is_volatile_reg,
|
||||
.volatile_reg = isl29018_is_volatile_reg,
|
||||
.max_register = ISL29018_REG_TEST,
|
||||
.num_reg_defaults_raw = ISL29018_REG_TEST + 1,
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
};
|
||||
|
||||
/* isl29035_regmap_config: regmap configuration for ISL29035 */
|
||||
static const struct regmap_config isl29035_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.volatile_reg = is_volatile_reg,
|
||||
.volatile_reg = isl29018_is_volatile_reg,
|
||||
.max_register = ISL29035_REG_DEVICE_ID,
|
||||
.num_reg_defaults_raw = ISL29035_REG_DEVICE_ID + 1,
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
};
|
||||
|
||||
struct chip_info {
|
||||
struct isl29018_chip_info {
|
||||
const struct iio_chan_spec *channels;
|
||||
int num_channels;
|
||||
const struct iio_info *indio_info;
|
||||
const struct regmap_config *regmap_cfg;
|
||||
};
|
||||
|
||||
static const struct chip_info chip_info_tbl[] = {
|
||||
static const struct isl29018_chip_info isl29018_chip_info_tbl[] = {
|
||||
[isl29018] = {
|
||||
.channels = isl29018_channels,
|
||||
.num_channels = ARRAY_SIZE(isl29018_channels),
|
||||
|
@ -724,10 +715,8 @@ static int isl29018_probe(struct i2c_client *client,
|
|||
int dev_id = 0;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip));
|
||||
if (!indio_dev) {
|
||||
dev_err(&client->dev, "iio allocation fails\n");
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
}
|
||||
chip = iio_priv(indio_dev);
|
||||
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
|
@ -750,7 +739,7 @@ static int isl29018_probe(struct i2c_client *client,
|
|||
chip->suspended = false;
|
||||
|
||||
chip->regmap = devm_regmap_init_i2c(client,
|
||||
chip_info_tbl[dev_id].regmap_cfg);
|
||||
isl29018_chip_info_tbl[dev_id].regmap_cfg);
|
||||
if (IS_ERR(chip->regmap)) {
|
||||
err = PTR_ERR(chip->regmap);
|
||||
dev_err(&client->dev, "regmap initialization fails: %d\n", err);
|
||||
|
@ -761,19 +750,13 @@ static int isl29018_probe(struct i2c_client *client,
|
|||
if (err)
|
||||
return err;
|
||||
|
||||
indio_dev->info = chip_info_tbl[dev_id].indio_info;
|
||||
indio_dev->channels = chip_info_tbl[dev_id].channels;
|
||||
indio_dev->num_channels = chip_info_tbl[dev_id].num_channels;
|
||||
indio_dev->info = isl29018_chip_info_tbl[dev_id].indio_info;
|
||||
indio_dev->channels = isl29018_chip_info_tbl[dev_id].channels;
|
||||
indio_dev->num_channels = isl29018_chip_info_tbl[dev_id].num_channels;
|
||||
indio_dev->name = name;
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
err = devm_iio_device_register(&client->dev, indio_dev);
|
||||
if (err) {
|
||||
dev_err(&client->dev, "iio registration fails\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return devm_iio_device_register(&client->dev, indio_dev);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
|
@ -840,7 +823,6 @@ static const struct of_device_id isl29018_of_match[] = {
|
|||
MODULE_DEVICE_TABLE(of, isl29018_of_match);
|
||||
|
||||
static struct i2c_driver isl29018_driver = {
|
||||
.class = I2C_CLASS_HWMON,
|
||||
.driver = {
|
||||
.name = "isl29018",
|
||||
.acpi_match_table = ACPI_PTR(isl29018_acpi_match),
|
||||
|
|
|
@ -27,29 +27,27 @@
|
|||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
#define CONVERSION_TIME_MS 100
|
||||
#define ISL29028_CONV_TIME_MS 100
|
||||
|
||||
#define ISL29028_REG_CONFIGURE 0x01
|
||||
|
||||
#define CONFIGURE_ALS_IR_MODE_ALS 0
|
||||
#define CONFIGURE_ALS_IR_MODE_IR BIT(0)
|
||||
#define CONFIGURE_ALS_IR_MODE_MASK BIT(0)
|
||||
#define ISL29028_CONF_ALS_IR_MODE_ALS 0
|
||||
#define ISL29028_CONF_ALS_IR_MODE_IR BIT(0)
|
||||
#define ISL29028_CONF_ALS_IR_MODE_MASK BIT(0)
|
||||
|
||||
#define CONFIGURE_ALS_RANGE_LOW_LUX 0
|
||||
#define CONFIGURE_ALS_RANGE_HIGH_LUX BIT(1)
|
||||
#define CONFIGURE_ALS_RANGE_MASK BIT(1)
|
||||
#define ISL29028_CONF_ALS_RANGE_LOW_LUX 0
|
||||
#define ISL29028_CONF_ALS_RANGE_HIGH_LUX BIT(1)
|
||||
#define ISL29028_CONF_ALS_RANGE_MASK BIT(1)
|
||||
|
||||
#define CONFIGURE_ALS_DIS 0
|
||||
#define CONFIGURE_ALS_EN BIT(2)
|
||||
#define CONFIGURE_ALS_EN_MASK BIT(2)
|
||||
#define ISL29028_CONF_ALS_DIS 0
|
||||
#define ISL29028_CONF_ALS_EN BIT(2)
|
||||
#define ISL29028_CONF_ALS_EN_MASK BIT(2)
|
||||
|
||||
#define CONFIGURE_PROX_DRIVE BIT(3)
|
||||
#define ISL29028_CONF_PROX_SLP_SH 4
|
||||
#define ISL29028_CONF_PROX_SLP_MASK (7 << ISL29028_CONF_PROX_SLP_SH)
|
||||
|
||||
#define CONFIGURE_PROX_SLP_SH 4
|
||||
#define CONFIGURE_PROX_SLP_MASK (7 << CONFIGURE_PROX_SLP_SH)
|
||||
|
||||
#define CONFIGURE_PROX_EN BIT(7)
|
||||
#define CONFIGURE_PROX_EN_MASK BIT(7)
|
||||
#define ISL29028_CONF_PROX_EN BIT(7)
|
||||
#define ISL29028_CONF_PROX_EN_MASK BIT(7)
|
||||
|
||||
#define ISL29028_REG_INTERRUPT 0x02
|
||||
|
||||
|
@ -62,10 +60,10 @@
|
|||
|
||||
#define ISL29028_NUM_REGS (ISL29028_REG_TEST2_MODE + 1)
|
||||
|
||||
enum als_ir_mode {
|
||||
MODE_NONE = 0,
|
||||
MODE_ALS,
|
||||
MODE_IR
|
||||
enum isl29028_als_ir_mode {
|
||||
ISL29028_MODE_NONE = 0,
|
||||
ISL29028_MODE_ALS,
|
||||
ISL29028_MODE_IR,
|
||||
};
|
||||
|
||||
struct isl29028_chip {
|
||||
|
@ -76,7 +74,7 @@ struct isl29028_chip {
|
|||
bool enable_prox;
|
||||
|
||||
int lux_scale;
|
||||
int als_ir_mode;
|
||||
enum isl29028_als_ir_mode als_ir_mode;
|
||||
};
|
||||
|
||||
static int isl29028_set_proxim_sampling(struct isl29028_chip *chip,
|
||||
|
@ -91,7 +89,8 @@ static int isl29028_set_proxim_sampling(struct isl29028_chip *chip,
|
|||
break;
|
||||
}
|
||||
return regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE,
|
||||
CONFIGURE_PROX_SLP_MASK, sel << CONFIGURE_PROX_SLP_SH);
|
||||
ISL29028_CONF_PROX_SLP_MASK,
|
||||
sel << ISL29028_CONF_PROX_SLP_SH);
|
||||
}
|
||||
|
||||
static int isl29028_enable_proximity(struct isl29028_chip *chip, bool enable)
|
||||
|
@ -100,9 +99,9 @@ static int isl29028_enable_proximity(struct isl29028_chip *chip, bool enable)
|
|||
int val = 0;
|
||||
|
||||
if (enable)
|
||||
val = CONFIGURE_PROX_EN;
|
||||
val = ISL29028_CONF_PROX_EN;
|
||||
ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE,
|
||||
CONFIGURE_PROX_EN_MASK, val);
|
||||
ISL29028_CONF_PROX_EN_MASK, val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
@ -113,40 +112,40 @@ static int isl29028_enable_proximity(struct isl29028_chip *chip, bool enable)
|
|||
|
||||
static int isl29028_set_als_scale(struct isl29028_chip *chip, int lux_scale)
|
||||
{
|
||||
int val = (lux_scale == 2000) ? CONFIGURE_ALS_RANGE_HIGH_LUX :
|
||||
CONFIGURE_ALS_RANGE_LOW_LUX;
|
||||
int val = (lux_scale == 2000) ? ISL29028_CONF_ALS_RANGE_HIGH_LUX :
|
||||
ISL29028_CONF_ALS_RANGE_LOW_LUX;
|
||||
|
||||
return regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE,
|
||||
CONFIGURE_ALS_RANGE_MASK, val);
|
||||
ISL29028_CONF_ALS_RANGE_MASK, val);
|
||||
}
|
||||
|
||||
static int isl29028_set_als_ir_mode(struct isl29028_chip *chip,
|
||||
enum als_ir_mode mode)
|
||||
enum isl29028_als_ir_mode mode)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
switch (mode) {
|
||||
case MODE_ALS:
|
||||
case ISL29028_MODE_ALS:
|
||||
ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE,
|
||||
CONFIGURE_ALS_IR_MODE_MASK,
|
||||
CONFIGURE_ALS_IR_MODE_ALS);
|
||||
ISL29028_CONF_ALS_IR_MODE_MASK,
|
||||
ISL29028_CONF_ALS_IR_MODE_ALS);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE,
|
||||
CONFIGURE_ALS_RANGE_MASK,
|
||||
CONFIGURE_ALS_RANGE_HIGH_LUX);
|
||||
ISL29028_CONF_ALS_RANGE_MASK,
|
||||
ISL29028_CONF_ALS_RANGE_HIGH_LUX);
|
||||
break;
|
||||
|
||||
case MODE_IR:
|
||||
case ISL29028_MODE_IR:
|
||||
ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE,
|
||||
CONFIGURE_ALS_IR_MODE_MASK,
|
||||
CONFIGURE_ALS_IR_MODE_IR);
|
||||
ISL29028_CONF_ALS_IR_MODE_MASK,
|
||||
ISL29028_CONF_ALS_IR_MODE_IR);
|
||||
break;
|
||||
|
||||
case MODE_NONE:
|
||||
case ISL29028_MODE_NONE:
|
||||
return regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE,
|
||||
CONFIGURE_ALS_EN_MASK, CONFIGURE_ALS_DIS);
|
||||
ISL29028_CONF_ALS_EN_MASK, ISL29028_CONF_ALS_DIS);
|
||||
}
|
||||
|
||||
if (ret < 0)
|
||||
|
@ -154,12 +153,13 @@ static int isl29028_set_als_ir_mode(struct isl29028_chip *chip,
|
|||
|
||||
/* Enable the ALS/IR */
|
||||
ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE,
|
||||
CONFIGURE_ALS_EN_MASK, CONFIGURE_ALS_EN);
|
||||
ISL29028_CONF_ALS_EN_MASK,
|
||||
ISL29028_CONF_ALS_EN);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Need to wait for conversion time if ALS/IR mode enabled */
|
||||
mdelay(CONVERSION_TIME_MS);
|
||||
mdelay(ISL29028_CONV_TIME_MS);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -223,14 +223,14 @@ static int isl29028_als_get(struct isl29028_chip *chip, int *als_data)
|
|||
int ret;
|
||||
int als_ir_data;
|
||||
|
||||
if (chip->als_ir_mode != MODE_ALS) {
|
||||
ret = isl29028_set_als_ir_mode(chip, MODE_ALS);
|
||||
if (chip->als_ir_mode != ISL29028_MODE_ALS) {
|
||||
ret = isl29028_set_als_ir_mode(chip, ISL29028_MODE_ALS);
|
||||
if (ret < 0) {
|
||||
dev_err(dev,
|
||||
"Error in enabling ALS mode err %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
chip->als_ir_mode = MODE_ALS;
|
||||
chip->als_ir_mode = ISL29028_MODE_ALS;
|
||||
}
|
||||
|
||||
ret = isl29028_read_als_ir(chip, &als_ir_data);
|
||||
|
@ -256,14 +256,14 @@ static int isl29028_ir_get(struct isl29028_chip *chip, int *ir_data)
|
|||
struct device *dev = regmap_get_device(chip->regmap);
|
||||
int ret;
|
||||
|
||||
if (chip->als_ir_mode != MODE_IR) {
|
||||
ret = isl29028_set_als_ir_mode(chip, MODE_IR);
|
||||
if (chip->als_ir_mode != ISL29028_MODE_IR) {
|
||||
ret = isl29028_set_als_ir_mode(chip, ISL29028_MODE_IR);
|
||||
if (ret < 0) {
|
||||
dev_err(dev,
|
||||
"Error in enabling IR mode err %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
chip->als_ir_mode = MODE_IR;
|
||||
chip->als_ir_mode = ISL29028_MODE_IR;
|
||||
}
|
||||
return isl29028_read_als_ir(chip, ir_data);
|
||||
}
|
||||
|
@ -383,8 +383,8 @@ static int isl29028_read_raw(struct iio_dev *indio_dev,
|
|||
}
|
||||
|
||||
static IIO_CONST_ATTR(in_proximity_sampling_frequency_available,
|
||||
"1, 3, 5, 10, 13, 20, 83, 100");
|
||||
static IIO_CONST_ATTR(in_illuminance_scale_available, "125, 2000");
|
||||
"1 3 5 10 13 20 83 100");
|
||||
static IIO_CONST_ATTR(in_illuminance_scale_available, "125 2000");
|
||||
|
||||
#define ISL29028_DEV_ATTR(name) (&iio_dev_attr_##name.dev_attr.attr)
|
||||
#define ISL29028_CONST_ATTR(name) (&iio_const_attr_##name.dev_attr.attr)
|
||||
|
@ -428,7 +428,7 @@ static int isl29028_chip_init(struct isl29028_chip *chip)
|
|||
chip->enable_prox = false;
|
||||
chip->prox_sampling = 20;
|
||||
chip->lux_scale = 2000;
|
||||
chip->als_ir_mode = MODE_NONE;
|
||||
chip->als_ir_mode = ISL29028_MODE_NONE;
|
||||
|
||||
ret = regmap_write(chip->regmap, ISL29028_REG_TEST1_MODE, 0x0);
|
||||
if (ret < 0) {
|
||||
|
@ -462,7 +462,7 @@ static int isl29028_chip_init(struct isl29028_chip *chip)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static bool is_volatile_reg(struct device *dev, unsigned int reg)
|
||||
static bool isl29028_is_volatile_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case ISL29028_REG_INTERRUPT:
|
||||
|
@ -478,7 +478,7 @@ static bool is_volatile_reg(struct device *dev, unsigned int reg)
|
|||
static const struct regmap_config isl29028_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.volatile_reg = is_volatile_reg,
|
||||
.volatile_reg = isl29028_is_volatile_reg,
|
||||
.max_register = ISL29028_NUM_REGS - 1,
|
||||
.num_reg_defaults_raw = ISL29028_NUM_REGS,
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
|
@ -546,7 +546,6 @@ static const struct of_device_id isl29028_of_match[] = {
|
|||
MODULE_DEVICE_TABLE(of, isl29028_of_match);
|
||||
|
||||
static struct i2c_driver isl29028_driver = {
|
||||
.class = I2C_CLASS_HWMON,
|
||||
.driver = {
|
||||
.name = "isl29028",
|
||||
.of_match_table = isl29028_of_match,
|
||||
|
|
|
@ -23,8 +23,8 @@
|
|||
#include "ade7854.h"
|
||||
|
||||
static ssize_t ade7854_read_8bit(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int ret;
|
||||
u8 val = 0;
|
||||
|
@ -40,8 +40,8 @@ static ssize_t ade7854_read_8bit(struct device *dev,
|
|||
}
|
||||
|
||||
static ssize_t ade7854_read_16bit(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int ret;
|
||||
u16 val = 0;
|
||||
|
@ -57,8 +57,8 @@ static ssize_t ade7854_read_16bit(struct device *dev,
|
|||
}
|
||||
|
||||
static ssize_t ade7854_read_24bit(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int ret;
|
||||
u32 val;
|
||||
|
@ -74,8 +74,8 @@ static ssize_t ade7854_read_24bit(struct device *dev,
|
|||
}
|
||||
|
||||
static ssize_t ade7854_read_32bit(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int ret;
|
||||
u32 val = 0;
|
||||
|
@ -91,9 +91,9 @@ static ssize_t ade7854_read_32bit(struct device *dev,
|
|||
}
|
||||
|
||||
static ssize_t ade7854_write_8bit(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf,
|
||||
size_t len)
|
||||
struct device_attribute *attr,
|
||||
const char *buf,
|
||||
size_t len)
|
||||
{
|
||||
struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
|
@ -112,9 +112,9 @@ error_ret:
|
|||
}
|
||||
|
||||
static ssize_t ade7854_write_16bit(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf,
|
||||
size_t len)
|
||||
struct device_attribute *attr,
|
||||
const char *buf,
|
||||
size_t len)
|
||||
{
|
||||
struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
|
@ -133,9 +133,9 @@ error_ret:
|
|||
}
|
||||
|
||||
static ssize_t ade7854_write_24bit(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf,
|
||||
size_t len)
|
||||
struct device_attribute *attr,
|
||||
const char *buf,
|
||||
size_t len)
|
||||
{
|
||||
struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
|
@ -154,9 +154,9 @@ error_ret:
|
|||
}
|
||||
|
||||
static ssize_t ade7854_write_32bit(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf,
|
||||
size_t len)
|
||||
struct device_attribute *attr,
|
||||
const char *buf,
|
||||
size_t len)
|
||||
{
|
||||
struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
|
|
|
@ -236,6 +236,7 @@ struct hid_sensor_common {
|
|||
struct hid_sensor_hub_attribute_info report_state;
|
||||
struct hid_sensor_hub_attribute_info power_state;
|
||||
struct hid_sensor_hub_attribute_info sensitivity;
|
||||
struct work_struct work;
|
||||
};
|
||||
|
||||
/* Convert from hid unit expo to regular exponent */
|
||||
|
|
|
@ -164,6 +164,18 @@ void iio_channel_stop_all_cb(struct iio_cb_buffer *cb_buff);
|
|||
struct iio_channel
|
||||
*iio_channel_cb_get_channels(const struct iio_cb_buffer *cb_buffer);
|
||||
|
||||
/**
|
||||
* iio_channel_cb_get_iio_dev() - get access to the underlying device.
|
||||
* @cb_buffer: The callback buffer from whom we want the device
|
||||
* information.
|
||||
*
|
||||
* This function allows one to obtain information about the device.
|
||||
* The primary aim is to allow drivers that are consuming a device to query
|
||||
* things like current trigger.
|
||||
*/
|
||||
struct iio_dev
|
||||
*iio_channel_cb_get_iio_dev(const struct iio_cb_buffer *cb_buffer);
|
||||
|
||||
/**
|
||||
* iio_read_channel_raw() - read from a given channel
|
||||
* @chan: The channel being queried.
|
||||
|
|
|
@ -51,7 +51,8 @@ static int dump_channels(const char *dev_dir_name)
|
|||
|
||||
while (ent = readdir(dp), ent)
|
||||
if (check_prefix(ent->d_name, "in_") &&
|
||||
check_postfix(ent->d_name, "_raw"))
|
||||
(check_postfix(ent->d_name, "_raw") ||
|
||||
check_postfix(ent->d_name, "_input")))
|
||||
printf(" %-10s\n", ent->d_name);
|
||||
|
||||
return (closedir(dp) == -1) ? -errno : 0;
|
||||
|
|
Loading…
Reference in New Issue