mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-12-29 17:25:38 +00:00
First round of new IIO device support, features and cleanups for the 4.6 cycle.
Device Support * ad5761 - new driver * at91_sama5d2 ADC. - new driver and MAINTAINERS entry. - minor cleanups followed. * atlas pH-SM - new driver (this has possibly the prettiest data sheet I've ever seen) * mcp3422 - mcp3425 ADC added. * mcp4725 - mcp4726 DAC added. * mma8452 - mma8451q accelerometer added. * mpl115 - mpl115a1 added (a lot bigger than it seems as this is an SPI part whereas previous parts were i2c). * si7005 - Hoperf th02 (seems to be a repackaged part) * si7020 - Hoperf th06 (seems to be a repackaged part) New features * Core - IIO_PH type. Does what it says on the tin. * max30100 - LED current configuration support. * mcp320x - more differential measurement combinations. * mma8452 - free fall deteciton - opt3001 - enable operation without a IRQ line. - device tree docs. Somehow the original docs have disappeared down a rabbit hole, so here is a new set. * st-sensors - Support active-low interrupts. Cleanups and minor / not so minor reworks * Documentation - drop some defunct ABI from the docs in staging. * presure / Kconfig - white space cleanup. * ad7150 - BIT macro usage - Alignment fixes * ad7192 - false indent fixed. * ak8975 - constify the ak_def structures * axp288 - drop a redundant double const. * dht11 - substantial reliability improvements by being more tolerant of missing start bits. - simplify the decoding algorithm * mma8452 - whitespace cleanup * mpl115 - don't bother setting i2c_client_data as nothing uses it. * mpu6050 - drop unused function parameter. * opt3001 - extract integration time as constants. - trivial refactoring. -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iQIcBAABCAAGBQJWr8HEAAoJEFSFNJnE9BaIIWMP/A6hJdAi3QOipB75QYBHL3R2 7fe7Bcd83i2to/Ohk9XirRwyP7kCqt1ZP7cnaIAevNxmwQ9fqIFgrxCK8BfF+lMk 60PXfEdinPk1rWguqEMwaYq1xVAOTgWYl8F4GVG3nGIvTHovAYaZW0UcTUl5Gz+l WErEZQ5xgnf7e/29ds74eD3YaOti8E1mzhBLXFJH7lllsSMJttbp9Xb5J0QJrv4a Ly8Ru5DBRGZYKi0VplFIjncKLZgX/VUUSPNL+NXtPnGD2Qp2zx0bJ3eIqNmeEYZl isAGIc6l4XPXQLczyRaPbcQLobMxOHMP2uuXxY/OAbe2DX9/6ILcB2QigARAFHf2 7vVcx3JlxXUtwS/bPpZSd5hHrvjWLR7+gyuZTA4b/scV4+GvknMl8e6IIh4Ux3/T d24V37bOIENrXCoyf81veap0xca6xQNSzCVuo8+1+QYqn6DxvoJw3p38Cmxb8eB8 7M+nQJP9SwLtbSUak8g1KVrtWBNsMrMGwoiuZq5SkK1PQTSXnSaJPTL0OoPoefNi uIY5qD5FrUp9jlNbBP2M2E3CcG5CJcMElxyBgd7F5lEVxSWPOuyOcpJHIoCCCgIf o6vwOYXXfD4ytv/E6jg+Bbv3ZYCylFhcoZOqPgTC/sOnSI9RwhJ6XdAg6VU8sv07 piH1AVWb91HKRN2dt2ZB =fgTH -----END PGP SIGNATURE----- Merge tag 'iio-for-4.6a' of git://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio into staging-next Jonathan writes: First round of new IIO device support, features and cleanups for the 4.6 cycle. Device Support * ad5761 - new driver * at91_sama5d2 ADC. - new driver and MAINTAINERS entry. - minor cleanups followed. * atlas pH-SM - new driver (this has possibly the prettiest data sheet I've ever seen) * mcp3422 - mcp3425 ADC added. * mcp4725 - mcp4726 DAC added. * mma8452 - mma8451q accelerometer added. * mpl115 - mpl115a1 added (a lot bigger than it seems as this is an SPI part whereas previous parts were i2c). * si7005 - Hoperf th02 (seems to be a repackaged part) * si7020 - Hoperf th06 (seems to be a repackaged part) New features * Core - IIO_PH type. Does what it says on the tin. * max30100 - LED current configuration support. * mcp320x - more differential measurement combinations. * mma8452 - free fall deteciton - opt3001 - enable operation without a IRQ line. - device tree docs. Somehow the original docs have disappeared down a rabbit hole, so here is a new set. * st-sensors - Support active-low interrupts. Cleanups and minor / not so minor reworks * Documentation - drop some defunct ABI from the docs in staging. * presure / Kconfig - white space cleanup. * ad7150 - BIT macro usage - Alignment fixes * ad7192 - false indent fixed. * ak8975 - constify the ak_def structures * axp288 - drop a redundant double const. * dht11 - substantial reliability improvements by being more tolerant of missing start bits. - simplify the decoding algorithm * mma8452 - whitespace cleanup * mpl115 - don't bother setting i2c_client_data as nothing uses it. * mpu6050 - drop unused function parameter. * opt3001 - extract integration time as constants. - trivial refactoring.
This commit is contained in:
commit
fec2f3335c
1
CREDITS
1
CREDITS
@ -3054,6 +3054,7 @@ D: PLX USB338x driver
|
||||
D: PCA9634 driver
|
||||
D: Option GTM671WFS
|
||||
D: Fintek F81216A
|
||||
D: AD5761 iio driver
|
||||
D: Various kernel hacks
|
||||
S: Qtechnology A/S
|
||||
S: Valby Langgade 142
|
||||
|
@ -497,7 +497,9 @@ Description:
|
||||
6kohm_to_gnd: connected to ground via a 6kOhm resistor,
|
||||
20kohm_to_gnd: connected to ground via a 20kOhm resistor,
|
||||
100kohm_to_gnd: connected to ground via an 100kOhm resistor,
|
||||
125kohm_to_gnd: connected to ground via an 125kOhm resistor,
|
||||
500kohm_to_gnd: connected to ground via a 500kOhm resistor,
|
||||
640kohm_to_gnd: connected to ground via a 640kOhm resistor,
|
||||
three_state: left floating.
|
||||
For a list of available output power down options read
|
||||
outX_powerdown_mode_available. If Y is not present the
|
||||
@ -1491,3 +1493,10 @@ Description:
|
||||
This ABI is especially applicable for humidity sensors
|
||||
to heatup the device and get rid of any condensation
|
||||
in some humidity environment
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_ph_raw
|
||||
KernelVersion: 4.5
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Raw (unscaled no offset etc.) pH reading of a substance as a negative
|
||||
base-10 logarithm of hydrodium ions in a litre of water.
|
||||
|
@ -1,8 +1,10 @@
|
||||
Freescale MMA8452Q, MMA8453Q, MMA8652FC or MMA8653FC triaxial accelerometer
|
||||
Freescale MMA8451Q, MMA8452Q, MMA8453Q, MMA8652FC or MMA8653FC
|
||||
triaxial accelerometer
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: should contain one of
|
||||
* "fsl,mma8451"
|
||||
* "fsl,mma8452"
|
||||
* "fsl,mma8453"
|
||||
* "fsl,mma8652"
|
||||
|
@ -0,0 +1,28 @@
|
||||
* AT91 SAMA5D2 Analog to Digital Converter (ADC)
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "atmel,sama5d2-adc".
|
||||
- reg: Should contain ADC registers location and length.
|
||||
- interrupts: Should contain the IRQ line for the ADC.
|
||||
- clocks: phandle to device clock.
|
||||
- clock-names: Must be "adc_clk".
|
||||
- vref-supply: Supply used as reference for conversions.
|
||||
- vddana-supply: Supply for the adc device.
|
||||
- atmel,min-sample-rate-hz: Minimum sampling rate, it depends on SoC.
|
||||
- atmel,max-sample-rate-hz: Maximum sampling rate, it depends on SoC.
|
||||
- atmel,startup-time-ms: Startup time expressed in ms, it depends on SoC.
|
||||
|
||||
Example:
|
||||
|
||||
adc: adc@fc030000 {
|
||||
compatible = "atmel,sama5d2-adc";
|
||||
reg = <0xfc030000 0x100>;
|
||||
interrupts = <40 IRQ_TYPE_LEVEL_HIGH 7>;
|
||||
clocks = <&adc_clk>;
|
||||
clock-names = "adc_clk";
|
||||
atmel,min-sample-rate-hz = <200000>;
|
||||
atmel,max-sample-rate-hz = <20000000>;
|
||||
atmel,startup-time-ms = <4>;
|
||||
vddana-supply = <&vdd_3v3_lp_reg>;
|
||||
vref-supply = <&vdd_3v3_lp_reg>;
|
||||
}
|
@ -6,6 +6,7 @@ Required properties:
|
||||
"microchip,mcp3422" or
|
||||
"microchip,mcp3423" or
|
||||
"microchip,mcp3424" or
|
||||
"microchip,mcp3425" or
|
||||
"microchip,mcp3426" or
|
||||
"microchip,mcp3427" or
|
||||
"microchip,mcp3428"
|
||||
|
@ -0,0 +1,22 @@
|
||||
* Atlas Scientific pH-SM OEM sensor
|
||||
|
||||
http://www.atlas-scientific.com/_files/_datasheets/_oem/pH_oem_datasheet.pdf
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: must be "atlas,ph-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@65 {
|
||||
compatible = "atlas,ph-sm";
|
||||
reg = <0x65>;
|
||||
interrupt-parent = <&gpio1>;
|
||||
interrupts = <16 2>;
|
||||
};
|
@ -11,11 +11,19 @@ Required properties:
|
||||
Refer to interrupt-controller/interrupts.txt for generic
|
||||
interrupt client node bindings.
|
||||
|
||||
Optional properties:
|
||||
- maxim,led-current-microamp: configuration for LED current in microamperes
|
||||
while the engine is running. First indexed value is the configuration for
|
||||
the RED LED, and second value is for the IR LED.
|
||||
|
||||
Refer to the datasheet for the allowed current values.
|
||||
|
||||
Example:
|
||||
|
||||
max30100@057 {
|
||||
compatible = "maxim,max30100";
|
||||
reg = <57>;
|
||||
maxim,led-current-microamp = <24000 50000>;
|
||||
interrupt-parent = <&gpio1>;
|
||||
interrupts = <16 2>;
|
||||
};
|
||||
|
26
Documentation/devicetree/bindings/iio/light/opt3001.txt
Normal file
26
Documentation/devicetree/bindings/iio/light/opt3001.txt
Normal file
@ -0,0 +1,26 @@
|
||||
* Texas Instruments OPT3001 Ambient Light Sensor
|
||||
|
||||
The driver supports interrupt-driven and interrupt-less operation, depending
|
||||
on whether an interrupt property has been populated into the DT. Note that
|
||||
the optional generation of IIO events on rising/falling light threshold changes
|
||||
requires the use of interrupts. Without interrupts, only the simple reading
|
||||
of the current light value is supported through the IIO API.
|
||||
|
||||
http://www.ti.com/product/opt3001
|
||||
|
||||
Required properties:
|
||||
- compatible: should be "ti,opt3001"
|
||||
- reg: the I2C address of the sensor
|
||||
|
||||
Optional properties:
|
||||
- interrupt-parent: should be the phandle for the interrupt controller
|
||||
- interrupts: interrupt mapping for GPIO IRQ (configure for falling edge)
|
||||
|
||||
Example:
|
||||
|
||||
opt3001@44 {
|
||||
compatible = "ti,opt3001";
|
||||
reg = <0x44>;
|
||||
interrupt-parent = <&gpio1>;
|
||||
interrupts = <28 IRQ_TYPE_EDGE_FALLING>;
|
||||
};
|
@ -28,6 +28,7 @@ arm ARM Ltd.
|
||||
armadeus ARMadeus Systems SARL
|
||||
artesyn Artesyn Embedded Technologies Inc.
|
||||
asahi-kasei Asahi Kasei Corp.
|
||||
atlas Atlas Scientific LLC
|
||||
atmel Atmel Corporation
|
||||
auo AU Optronics Corporation
|
||||
avago Avago Technologies
|
||||
|
@ -1945,6 +1945,12 @@ M: Nicolas Ferre <nicolas.ferre@atmel.com>
|
||||
S: Supported
|
||||
F: drivers/tty/serial/atmel_serial.c
|
||||
|
||||
ATMEL SAMA5D2 ADC DRIVER
|
||||
M: Ludovic Desroches <ludovic.desroches@atmel.com>
|
||||
L: linux-iio@vger.kernel.org
|
||||
S: Supported
|
||||
F: drivers/iio/adc/at91-sama5d2_adc.c
|
||||
|
||||
ATMEL Audio ALSA driver
|
||||
M: Nicolas Ferre <nicolas.ferre@atmel.com>
|
||||
L: alsa-devel@alsa-project.org (moderated for non-subscribers)
|
||||
|
@ -143,7 +143,7 @@ config MMA8452
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
Say yes here to build support for the following Freescale 3-axis
|
||||
accelerometers: MMA8452Q, MMA8453Q, MMA8652FC, MMA8653FC.
|
||||
accelerometers: MMA8451Q, MMA8452Q, MMA8453Q, MMA8652FC, MMA8653FC.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called mma8452.
|
||||
|
@ -1,6 +1,7 @@
|
||||
/*
|
||||
* mma8452.c - Support for following Freescale 3-axis accelerometers:
|
||||
*
|
||||
* MMA8451Q (14 bit)
|
||||
* MMA8452Q (12 bit)
|
||||
* MMA8453Q (10 bit)
|
||||
* MMA8652FC (12 bit)
|
||||
@ -15,7 +16,7 @@
|
||||
*
|
||||
* 7-bit I2C slave address 0x1c/0x1d (pin selectable)
|
||||
*
|
||||
* TODO: orientation / freefall events, autosleep
|
||||
* TODO: orientation events, autosleep
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
@ -85,8 +86,9 @@
|
||||
#define MMA8452_INT_FF_MT BIT(2)
|
||||
#define MMA8452_INT_TRANS BIT(5)
|
||||
|
||||
#define MMA8452_DEVICE_ID 0x2a
|
||||
#define MMA8453_DEVICE_ID 0x3a
|
||||
#define MMA8451_DEVICE_ID 0x1a
|
||||
#define MMA8452_DEVICE_ID 0x2a
|
||||
#define MMA8453_DEVICE_ID 0x3a
|
||||
#define MMA8652_DEVICE_ID 0x4a
|
||||
#define MMA8653_DEVICE_ID 0x5a
|
||||
|
||||
@ -416,6 +418,51 @@ static int mma8452_change_config(struct mma8452_data *data, u8 reg, u8 val)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* returns >0 if in freefall mode, 0 if not or <0 if an error occured */
|
||||
static int mma8452_freefall_mode_enabled(struct mma8452_data *data)
|
||||
{
|
||||
int val;
|
||||
const struct mma_chip_info *chip = data->chip_info;
|
||||
|
||||
val = i2c_smbus_read_byte_data(data->client, chip->ev_cfg);
|
||||
if (val < 0)
|
||||
return val;
|
||||
|
||||
return !(val & MMA8452_FF_MT_CFG_OAE);
|
||||
}
|
||||
|
||||
static int mma8452_set_freefall_mode(struct mma8452_data *data, bool state)
|
||||
{
|
||||
int val;
|
||||
const struct mma_chip_info *chip = data->chip_info;
|
||||
|
||||
if ((state && mma8452_freefall_mode_enabled(data)) ||
|
||||
(!state && !(mma8452_freefall_mode_enabled(data))))
|
||||
return 0;
|
||||
|
||||
val = i2c_smbus_read_byte_data(data->client, chip->ev_cfg);
|
||||
if (val < 0)
|
||||
return val;
|
||||
|
||||
if (state) {
|
||||
val |= BIT(idx_x + chip->ev_cfg_chan_shift);
|
||||
val |= BIT(idx_y + chip->ev_cfg_chan_shift);
|
||||
val |= BIT(idx_z + chip->ev_cfg_chan_shift);
|
||||
val &= ~MMA8452_FF_MT_CFG_OAE;
|
||||
} else {
|
||||
val &= ~BIT(idx_x + chip->ev_cfg_chan_shift);
|
||||
val &= ~BIT(idx_y + chip->ev_cfg_chan_shift);
|
||||
val &= ~BIT(idx_z + chip->ev_cfg_chan_shift);
|
||||
val |= MMA8452_FF_MT_CFG_OAE;
|
||||
}
|
||||
|
||||
val = mma8452_change_config(data, chip->ev_cfg, val);
|
||||
if (val)
|
||||
return val;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mma8452_set_hp_filter_frequency(struct mma8452_data *data,
|
||||
int val, int val2)
|
||||
{
|
||||
@ -609,12 +656,22 @@ static int mma8452_read_event_config(struct iio_dev *indio_dev,
|
||||
const struct mma_chip_info *chip = data->chip_info;
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(data->client,
|
||||
data->chip_info->ev_cfg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
switch (dir) {
|
||||
case IIO_EV_DIR_FALLING:
|
||||
return mma8452_freefall_mode_enabled(data);
|
||||
case IIO_EV_DIR_RISING:
|
||||
if (mma8452_freefall_mode_enabled(data))
|
||||
return 0;
|
||||
|
||||
return !!(ret & BIT(chan->scan_index + chip->ev_cfg_chan_shift));
|
||||
ret = i2c_smbus_read_byte_data(data->client,
|
||||
data->chip_info->ev_cfg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return !!(ret & BIT(chan->scan_index + chip->ev_cfg_chan_shift));
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int mma8452_write_event_config(struct iio_dev *indio_dev,
|
||||
@ -627,19 +684,35 @@ static int mma8452_write_event_config(struct iio_dev *indio_dev,
|
||||
const struct mma_chip_info *chip = data->chip_info;
|
||||
int val;
|
||||
|
||||
val = i2c_smbus_read_byte_data(data->client, chip->ev_cfg);
|
||||
if (val < 0)
|
||||
return val;
|
||||
switch (dir) {
|
||||
case IIO_EV_DIR_FALLING:
|
||||
return mma8452_set_freefall_mode(data, state);
|
||||
case IIO_EV_DIR_RISING:
|
||||
val = i2c_smbus_read_byte_data(data->client, chip->ev_cfg);
|
||||
if (val < 0)
|
||||
return val;
|
||||
|
||||
if (state)
|
||||
val |= BIT(chan->scan_index + chip->ev_cfg_chan_shift);
|
||||
else
|
||||
val &= ~BIT(chan->scan_index + chip->ev_cfg_chan_shift);
|
||||
if (state) {
|
||||
if (mma8452_freefall_mode_enabled(data)) {
|
||||
val &= ~BIT(idx_x + chip->ev_cfg_chan_shift);
|
||||
val &= ~BIT(idx_y + chip->ev_cfg_chan_shift);
|
||||
val &= ~BIT(idx_z + chip->ev_cfg_chan_shift);
|
||||
val |= MMA8452_FF_MT_CFG_OAE;
|
||||
}
|
||||
val |= BIT(chan->scan_index + chip->ev_cfg_chan_shift);
|
||||
} else {
|
||||
if (mma8452_freefall_mode_enabled(data))
|
||||
return 0;
|
||||
|
||||
val |= chip->ev_cfg_ele;
|
||||
val |= MMA8452_FF_MT_CFG_OAE;
|
||||
val &= ~BIT(chan->scan_index + chip->ev_cfg_chan_shift);
|
||||
}
|
||||
|
||||
return mma8452_change_config(data, chip->ev_cfg, val);
|
||||
val |= chip->ev_cfg_ele;
|
||||
|
||||
return mma8452_change_config(data, chip->ev_cfg, val);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static void mma8452_transient_interrupt(struct iio_dev *indio_dev)
|
||||
@ -652,6 +725,16 @@ static void mma8452_transient_interrupt(struct iio_dev *indio_dev)
|
||||
if (src < 0)
|
||||
return;
|
||||
|
||||
if (mma8452_freefall_mode_enabled(data)) {
|
||||
iio_push_event(indio_dev,
|
||||
IIO_MOD_EVENT_CODE(IIO_ACCEL, 0,
|
||||
IIO_MOD_X_AND_Y_AND_Z,
|
||||
IIO_EV_TYPE_MAG,
|
||||
IIO_EV_DIR_FALLING),
|
||||
ts);
|
||||
return;
|
||||
}
|
||||
|
||||
if (src & data->chip_info->ev_src_xe)
|
||||
iio_push_event(indio_dev,
|
||||
IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_X,
|
||||
@ -745,6 +828,27 @@ static int mma8452_reg_access_dbg(struct iio_dev *indio_dev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct iio_event_spec mma8452_freefall_event[] = {
|
||||
{
|
||||
.type = IIO_EV_TYPE_MAG,
|
||||
.dir = IIO_EV_DIR_FALLING,
|
||||
.mask_separate = BIT(IIO_EV_INFO_ENABLE),
|
||||
.mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) |
|
||||
BIT(IIO_EV_INFO_PERIOD) |
|
||||
BIT(IIO_EV_INFO_HIGH_PASS_FILTER_3DB)
|
||||
},
|
||||
};
|
||||
|
||||
static const struct iio_event_spec mma8652_freefall_event[] = {
|
||||
{
|
||||
.type = IIO_EV_TYPE_MAG,
|
||||
.dir = IIO_EV_DIR_FALLING,
|
||||
.mask_separate = BIT(IIO_EV_INFO_ENABLE),
|
||||
.mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) |
|
||||
BIT(IIO_EV_INFO_PERIOD)
|
||||
},
|
||||
};
|
||||
|
||||
static const struct iio_event_spec mma8452_transient_event[] = {
|
||||
{
|
||||
.type = IIO_EV_TYPE_MAG,
|
||||
@ -781,6 +885,24 @@ static struct attribute_group mma8452_event_attribute_group = {
|
||||
.attrs = mma8452_event_attributes,
|
||||
};
|
||||
|
||||
#define MMA8452_FREEFALL_CHANNEL(modifier) { \
|
||||
.type = IIO_ACCEL, \
|
||||
.modified = 1, \
|
||||
.channel2 = modifier, \
|
||||
.scan_index = -1, \
|
||||
.event_spec = mma8452_freefall_event, \
|
||||
.num_event_specs = ARRAY_SIZE(mma8452_freefall_event), \
|
||||
}
|
||||
|
||||
#define MMA8652_FREEFALL_CHANNEL(modifier) { \
|
||||
.type = IIO_ACCEL, \
|
||||
.modified = 1, \
|
||||
.channel2 = modifier, \
|
||||
.scan_index = -1, \
|
||||
.event_spec = mma8652_freefall_event, \
|
||||
.num_event_specs = ARRAY_SIZE(mma8652_freefall_event), \
|
||||
}
|
||||
|
||||
#define MMA8452_CHANNEL(axis, idx, bits) { \
|
||||
.type = IIO_ACCEL, \
|
||||
.modified = 1, \
|
||||
@ -822,11 +944,20 @@ static struct attribute_group mma8452_event_attribute_group = {
|
||||
.num_event_specs = ARRAY_SIZE(mma8452_motion_event), \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec mma8451_channels[] = {
|
||||
MMA8452_CHANNEL(X, idx_x, 14),
|
||||
MMA8452_CHANNEL(Y, idx_y, 14),
|
||||
MMA8452_CHANNEL(Z, idx_z, 14),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(idx_ts),
|
||||
MMA8452_FREEFALL_CHANNEL(IIO_MOD_X_AND_Y_AND_Z),
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec mma8452_channels[] = {
|
||||
MMA8452_CHANNEL(X, idx_x, 12),
|
||||
MMA8452_CHANNEL(Y, idx_y, 12),
|
||||
MMA8452_CHANNEL(Z, idx_z, 12),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(idx_ts),
|
||||
MMA8452_FREEFALL_CHANNEL(IIO_MOD_X_AND_Y_AND_Z),
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec mma8453_channels[] = {
|
||||
@ -834,6 +965,7 @@ static const struct iio_chan_spec mma8453_channels[] = {
|
||||
MMA8452_CHANNEL(Y, idx_y, 10),
|
||||
MMA8452_CHANNEL(Z, idx_z, 10),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(idx_ts),
|
||||
MMA8452_FREEFALL_CHANNEL(IIO_MOD_X_AND_Y_AND_Z),
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec mma8652_channels[] = {
|
||||
@ -841,6 +973,7 @@ static const struct iio_chan_spec mma8652_channels[] = {
|
||||
MMA8652_CHANNEL(Y, idx_y, 12),
|
||||
MMA8652_CHANNEL(Z, idx_z, 12),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(idx_ts),
|
||||
MMA8652_FREEFALL_CHANNEL(IIO_MOD_X_AND_Y_AND_Z),
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec mma8653_channels[] = {
|
||||
@ -848,9 +981,11 @@ static const struct iio_chan_spec mma8653_channels[] = {
|
||||
MMA8652_CHANNEL(Y, idx_y, 10),
|
||||
MMA8652_CHANNEL(Z, idx_z, 10),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(idx_ts),
|
||||
MMA8652_FREEFALL_CHANNEL(IIO_MOD_X_AND_Y_AND_Z),
|
||||
};
|
||||
|
||||
enum {
|
||||
mma8451,
|
||||
mma8452,
|
||||
mma8453,
|
||||
mma8652,
|
||||
@ -858,17 +993,34 @@ enum {
|
||||
};
|
||||
|
||||
static const struct mma_chip_info mma_chip_info_table[] = {
|
||||
[mma8451] = {
|
||||
.chip_id = MMA8451_DEVICE_ID,
|
||||
.channels = mma8451_channels,
|
||||
.num_channels = ARRAY_SIZE(mma8451_channels),
|
||||
/*
|
||||
* Hardware has fullscale of -2G, -4G, -8G corresponding to
|
||||
* raw value -8192 for 14 bit, -2048 for 12 bit or -512 for 10
|
||||
* bit.
|
||||
* The userspace interface uses m/s^2 and we declare micro units
|
||||
* So scale factor for 12 bit here is given by:
|
||||
* g * N * 1000000 / 2048 for N = 2, 4, 8 and g=9.80665
|
||||
*/
|
||||
.mma_scales = { {0, 2394}, {0, 4788}, {0, 9577} },
|
||||
.ev_cfg = MMA8452_TRANSIENT_CFG,
|
||||
.ev_cfg_ele = MMA8452_TRANSIENT_CFG_ELE,
|
||||
.ev_cfg_chan_shift = 1,
|
||||
.ev_src = MMA8452_TRANSIENT_SRC,
|
||||
.ev_src_xe = MMA8452_TRANSIENT_SRC_XTRANSE,
|
||||
.ev_src_ye = MMA8452_TRANSIENT_SRC_YTRANSE,
|
||||
.ev_src_ze = MMA8452_TRANSIENT_SRC_ZTRANSE,
|
||||
.ev_ths = MMA8452_TRANSIENT_THS,
|
||||
.ev_ths_mask = MMA8452_TRANSIENT_THS_MASK,
|
||||
.ev_count = MMA8452_TRANSIENT_COUNT,
|
||||
},
|
||||
[mma8452] = {
|
||||
.chip_id = MMA8452_DEVICE_ID,
|
||||
.channels = mma8452_channels,
|
||||
.num_channels = ARRAY_SIZE(mma8452_channels),
|
||||
/*
|
||||
* Hardware has fullscale of -2G, -4G, -8G corresponding to
|
||||
* raw value -2048 for 12 bit or -512 for 10 bit.
|
||||
* The userspace interface uses m/s^2 and we declare micro units
|
||||
* So scale factor for 12 bit here is given by:
|
||||
* g * N * 1000000 / 2048 for N = 2, 4, 8 and g=9.80665
|
||||
*/
|
||||
.mma_scales = { {0, 9577}, {0, 19154}, {0, 38307} },
|
||||
.ev_cfg = MMA8452_TRANSIENT_CFG,
|
||||
.ev_cfg_ele = MMA8452_TRANSIENT_CFG_ELE,
|
||||
@ -1049,6 +1201,7 @@ static int mma8452_reset(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct of_device_id mma8452_dt_ids[] = {
|
||||
{ .compatible = "fsl,mma8451", .data = &mma_chip_info_table[mma8451] },
|
||||
{ .compatible = "fsl,mma8452", .data = &mma_chip_info_table[mma8452] },
|
||||
{ .compatible = "fsl,mma8453", .data = &mma_chip_info_table[mma8453] },
|
||||
{ .compatible = "fsl,mma8652", .data = &mma_chip_info_table[mma8652] },
|
||||
@ -1085,6 +1238,7 @@ static int mma8452_probe(struct i2c_client *client,
|
||||
return ret;
|
||||
|
||||
switch (ret) {
|
||||
case MMA8451_DEVICE_ID:
|
||||
case MMA8452_DEVICE_ID:
|
||||
case MMA8453_DEVICE_ID:
|
||||
case MMA8652_DEVICE_ID:
|
||||
@ -1190,6 +1344,10 @@ static int mma8452_probe(struct i2c_client *client,
|
||||
if (ret < 0)
|
||||
goto buffer_cleanup;
|
||||
|
||||
ret = mma8452_set_freefall_mode(data, false);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
|
||||
buffer_cleanup:
|
||||
|
@ -67,6 +67,8 @@
|
||||
#define ST_ACCEL_1_DRDY_IRQ_ADDR 0x22
|
||||
#define ST_ACCEL_1_DRDY_IRQ_INT1_MASK 0x10
|
||||
#define ST_ACCEL_1_DRDY_IRQ_INT2_MASK 0x08
|
||||
#define ST_ACCEL_1_IHL_IRQ_ADDR 0x25
|
||||
#define ST_ACCEL_1_IHL_IRQ_MASK 0x02
|
||||
#define ST_ACCEL_1_MULTIREAD_BIT true
|
||||
|
||||
/* CUSTOM VALUES FOR SENSOR 2 */
|
||||
@ -92,6 +94,8 @@
|
||||
#define ST_ACCEL_2_DRDY_IRQ_ADDR 0x22
|
||||
#define ST_ACCEL_2_DRDY_IRQ_INT1_MASK 0x02
|
||||
#define ST_ACCEL_2_DRDY_IRQ_INT2_MASK 0x10
|
||||
#define ST_ACCEL_2_IHL_IRQ_ADDR 0x22
|
||||
#define ST_ACCEL_2_IHL_IRQ_MASK 0x80
|
||||
#define ST_ACCEL_2_MULTIREAD_BIT true
|
||||
|
||||
/* CUSTOM VALUES FOR SENSOR 3 */
|
||||
@ -125,6 +129,8 @@
|
||||
#define ST_ACCEL_3_DRDY_IRQ_ADDR 0x23
|
||||
#define ST_ACCEL_3_DRDY_IRQ_INT1_MASK 0x80
|
||||
#define ST_ACCEL_3_DRDY_IRQ_INT2_MASK 0x00
|
||||
#define ST_ACCEL_3_IHL_IRQ_ADDR 0x23
|
||||
#define ST_ACCEL_3_IHL_IRQ_MASK 0x40
|
||||
#define ST_ACCEL_3_IG1_EN_ADDR 0x23
|
||||
#define ST_ACCEL_3_IG1_EN_MASK 0x08
|
||||
#define ST_ACCEL_3_MULTIREAD_BIT false
|
||||
@ -169,6 +175,8 @@
|
||||
#define ST_ACCEL_5_DRDY_IRQ_ADDR 0x22
|
||||
#define ST_ACCEL_5_DRDY_IRQ_INT1_MASK 0x04
|
||||
#define ST_ACCEL_5_DRDY_IRQ_INT2_MASK 0x20
|
||||
#define ST_ACCEL_5_IHL_IRQ_ADDR 0x22
|
||||
#define ST_ACCEL_5_IHL_IRQ_MASK 0x80
|
||||
#define ST_ACCEL_5_IG1_EN_ADDR 0x21
|
||||
#define ST_ACCEL_5_IG1_EN_MASK 0x08
|
||||
#define ST_ACCEL_5_MULTIREAD_BIT false
|
||||
@ -292,6 +300,8 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = {
|
||||
.addr = ST_ACCEL_1_DRDY_IRQ_ADDR,
|
||||
.mask_int1 = ST_ACCEL_1_DRDY_IRQ_INT1_MASK,
|
||||
.mask_int2 = ST_ACCEL_1_DRDY_IRQ_INT2_MASK,
|
||||
.addr_ihl = ST_ACCEL_1_IHL_IRQ_ADDR,
|
||||
.mask_ihl = ST_ACCEL_1_IHL_IRQ_MASK,
|
||||
},
|
||||
.multi_read_bit = ST_ACCEL_1_MULTIREAD_BIT,
|
||||
.bootime = 2,
|
||||
@ -355,6 +365,8 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = {
|
||||
.addr = ST_ACCEL_2_DRDY_IRQ_ADDR,
|
||||
.mask_int1 = ST_ACCEL_2_DRDY_IRQ_INT1_MASK,
|
||||
.mask_int2 = ST_ACCEL_2_DRDY_IRQ_INT2_MASK,
|
||||
.addr_ihl = ST_ACCEL_2_IHL_IRQ_ADDR,
|
||||
.mask_ihl = ST_ACCEL_2_IHL_IRQ_MASK,
|
||||
},
|
||||
.multi_read_bit = ST_ACCEL_2_MULTIREAD_BIT,
|
||||
.bootime = 2,
|
||||
@ -430,6 +442,8 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = {
|
||||
.addr = ST_ACCEL_3_DRDY_IRQ_ADDR,
|
||||
.mask_int1 = ST_ACCEL_3_DRDY_IRQ_INT1_MASK,
|
||||
.mask_int2 = ST_ACCEL_3_DRDY_IRQ_INT2_MASK,
|
||||
.addr_ihl = ST_ACCEL_3_IHL_IRQ_ADDR,
|
||||
.mask_ihl = ST_ACCEL_3_IHL_IRQ_MASK,
|
||||
.ig1 = {
|
||||
.en_addr = ST_ACCEL_3_IG1_EN_ADDR,
|
||||
.en_mask = ST_ACCEL_3_IG1_EN_MASK,
|
||||
@ -537,6 +551,8 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = {
|
||||
.addr = ST_ACCEL_5_DRDY_IRQ_ADDR,
|
||||
.mask_int1 = ST_ACCEL_5_DRDY_IRQ_INT1_MASK,
|
||||
.mask_int2 = ST_ACCEL_5_DRDY_IRQ_INT2_MASK,
|
||||
.addr_ihl = ST_ACCEL_5_IHL_IRQ_ADDR,
|
||||
.mask_ihl = ST_ACCEL_5_IHL_IRQ_MASK,
|
||||
},
|
||||
.multi_read_bit = ST_ACCEL_5_MULTIREAD_BIT,
|
||||
.bootime = 2, /* guess */
|
||||
|
@ -131,6 +131,16 @@ config AT91_ADC
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called at91_adc.
|
||||
|
||||
config AT91_SAMA5D2_ADC
|
||||
tristate "Atmel AT91 SAMA5D2 ADC"
|
||||
depends on ARCH_AT91 || COMPILE_TEST
|
||||
help
|
||||
Say yes here to build support for Atmel SAMA5D2 ADC which is
|
||||
available on SAMA5D2 SoC family.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called at91-sama5d2_adc.
|
||||
|
||||
config AXP288_ADC
|
||||
tristate "X-Powers AXP288 ADC driver"
|
||||
depends on MFD_AXP20X
|
||||
@ -265,11 +275,11 @@ config MCP320X
|
||||
called mcp320x.
|
||||
|
||||
config MCP3422
|
||||
tristate "Microchip Technology MCP3422/3/4/6/7/8 driver"
|
||||
tristate "Microchip Technology MCP3421/2/3/4/5/6/7/8 driver"
|
||||
depends on I2C
|
||||
help
|
||||
Say yes here to build support for Microchip Technology's
|
||||
MCP3422, MCP3423, MCP3424, MCP3426, MCP3427 or MCP3428
|
||||
Say yes here to build support for Microchip Technology's MCP3421
|
||||
MCP3422, MCP3423, MCP3424, MCP3425, MCP3426, MCP3427 or MCP3428
|
||||
analog to digital converters.
|
||||
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
|
@ -14,6 +14,7 @@ obj-$(CONFIG_AD7793) += ad7793.o
|
||||
obj-$(CONFIG_AD7887) += ad7887.o
|
||||
obj-$(CONFIG_AD799X) += ad799x.o
|
||||
obj-$(CONFIG_AT91_ADC) += at91_adc.o
|
||||
obj-$(CONFIG_AT91_SAMA5D2_ADC) += at91-sama5d2_adc.o
|
||||
obj-$(CONFIG_AXP288_ADC) += axp288_adc.o
|
||||
obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o
|
||||
obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o
|
||||
|
508
drivers/iio/adc/at91-sama5d2_adc.c
Normal file
508
drivers/iio/adc/at91-sama5d2_adc.c
Normal file
@ -0,0 +1,508 @@
|
||||
/*
|
||||
* Atmel ADC driver for SAMA5D2 devices and compatible.
|
||||
*
|
||||
* Copyright (C) 2015 Atmel,
|
||||
* 2015 Ludovic Desroches <ludovic.desroches@atmel.com>
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
/* Control Register */
|
||||
#define AT91_SAMA5D2_CR 0x00
|
||||
/* Software Reset */
|
||||
#define AT91_SAMA5D2_CR_SWRST BIT(0)
|
||||
/* Start Conversion */
|
||||
#define AT91_SAMA5D2_CR_START BIT(1)
|
||||
/* Touchscreen Calibration */
|
||||
#define AT91_SAMA5D2_CR_TSCALIB BIT(2)
|
||||
/* Comparison Restart */
|
||||
#define AT91_SAMA5D2_CR_CMPRST BIT(4)
|
||||
|
||||
/* Mode Register */
|
||||
#define AT91_SAMA5D2_MR 0x04
|
||||
/* Trigger Selection */
|
||||
#define AT91_SAMA5D2_MR_TRGSEL(v) ((v) << 1)
|
||||
/* ADTRG */
|
||||
#define AT91_SAMA5D2_MR_TRGSEL_TRIG0 0
|
||||
/* TIOA0 */
|
||||
#define AT91_SAMA5D2_MR_TRGSEL_TRIG1 1
|
||||
/* TIOA1 */
|
||||
#define AT91_SAMA5D2_MR_TRGSEL_TRIG2 2
|
||||
/* TIOA2 */
|
||||
#define AT91_SAMA5D2_MR_TRGSEL_TRIG3 3
|
||||
/* PWM event line 0 */
|
||||
#define AT91_SAMA5D2_MR_TRGSEL_TRIG4 4
|
||||
/* PWM event line 1 */
|
||||
#define AT91_SAMA5D2_MR_TRGSEL_TRIG5 5
|
||||
/* TIOA3 */
|
||||
#define AT91_SAMA5D2_MR_TRGSEL_TRIG6 6
|
||||
/* RTCOUT0 */
|
||||
#define AT91_SAMA5D2_MR_TRGSEL_TRIG7 7
|
||||
/* Sleep Mode */
|
||||
#define AT91_SAMA5D2_MR_SLEEP BIT(5)
|
||||
/* Fast Wake Up */
|
||||
#define AT91_SAMA5D2_MR_FWUP BIT(6)
|
||||
/* Prescaler Rate Selection */
|
||||
#define AT91_SAMA5D2_MR_PRESCAL(v) ((v) << AT91_SAMA5D2_MR_PRESCAL_OFFSET)
|
||||
#define AT91_SAMA5D2_MR_PRESCAL_OFFSET 8
|
||||
#define AT91_SAMA5D2_MR_PRESCAL_MAX 0xff
|
||||
/* Startup Time */
|
||||
#define AT91_SAMA5D2_MR_STARTUP(v) ((v) << 16)
|
||||
/* Analog Change */
|
||||
#define AT91_SAMA5D2_MR_ANACH BIT(23)
|
||||
/* Tracking Time */
|
||||
#define AT91_SAMA5D2_MR_TRACKTIM(v) ((v) << 24)
|
||||
#define AT91_SAMA5D2_MR_TRACKTIM_MAX 0xff
|
||||
/* Transfer Time */
|
||||
#define AT91_SAMA5D2_MR_TRANSFER(v) ((v) << 28)
|
||||
#define AT91_SAMA5D2_MR_TRANSFER_MAX 0x3
|
||||
/* Use Sequence Enable */
|
||||
#define AT91_SAMA5D2_MR_USEQ BIT(31)
|
||||
|
||||
/* Channel Sequence Register 1 */
|
||||
#define AT91_SAMA5D2_SEQR1 0x08
|
||||
/* Channel Sequence Register 2 */
|
||||
#define AT91_SAMA5D2_SEQR2 0x0c
|
||||
/* Channel Enable Register */
|
||||
#define AT91_SAMA5D2_CHER 0x10
|
||||
/* Channel Disable Register */
|
||||
#define AT91_SAMA5D2_CHDR 0x14
|
||||
/* Channel Status Register */
|
||||
#define AT91_SAMA5D2_CHSR 0x18
|
||||
/* Last Converted Data Register */
|
||||
#define AT91_SAMA5D2_LCDR 0x20
|
||||
/* Interrupt Enable Register */
|
||||
#define AT91_SAMA5D2_IER 0x24
|
||||
/* Interrupt Disable Register */
|
||||
#define AT91_SAMA5D2_IDR 0x28
|
||||
/* Interrupt Mask Register */
|
||||
#define AT91_SAMA5D2_IMR 0x2c
|
||||
/* Interrupt Status Register */
|
||||
#define AT91_SAMA5D2_ISR 0x30
|
||||
/* Last Channel Trigger Mode Register */
|
||||
#define AT91_SAMA5D2_LCTMR 0x34
|
||||
/* Last Channel Compare Window Register */
|
||||
#define AT91_SAMA5D2_LCCWR 0x38
|
||||
/* Overrun Status Register */
|
||||
#define AT91_SAMA5D2_OVER 0x3c
|
||||
/* Extended Mode Register */
|
||||
#define AT91_SAMA5D2_EMR 0x40
|
||||
/* Compare Window Register */
|
||||
#define AT91_SAMA5D2_CWR 0x44
|
||||
/* Channel Gain Register */
|
||||
#define AT91_SAMA5D2_CGR 0x48
|
||||
/* Channel Offset Register */
|
||||
#define AT91_SAMA5D2_COR 0x4c
|
||||
/* Channel Data Register 0 */
|
||||
#define AT91_SAMA5D2_CDR0 0x50
|
||||
/* Analog Control Register */
|
||||
#define AT91_SAMA5D2_ACR 0x94
|
||||
/* Touchscreen Mode Register */
|
||||
#define AT91_SAMA5D2_TSMR 0xb0
|
||||
/* Touchscreen X Position Register */
|
||||
#define AT91_SAMA5D2_XPOSR 0xb4
|
||||
/* Touchscreen Y Position Register */
|
||||
#define AT91_SAMA5D2_YPOSR 0xb8
|
||||
/* Touchscreen Pressure Register */
|
||||
#define AT91_SAMA5D2_PRESSR 0xbc
|
||||
/* Trigger Register */
|
||||
#define AT91_SAMA5D2_TRGR 0xc0
|
||||
/* Correction Select Register */
|
||||
#define AT91_SAMA5D2_COSR 0xd0
|
||||
/* Correction Value Register */
|
||||
#define AT91_SAMA5D2_CVR 0xd4
|
||||
/* Channel Error Correction Register */
|
||||
#define AT91_SAMA5D2_CECR 0xd8
|
||||
/* Write Protection Mode Register */
|
||||
#define AT91_SAMA5D2_WPMR 0xe4
|
||||
/* Write Protection Status Register */
|
||||
#define AT91_SAMA5D2_WPSR 0xe8
|
||||
/* Version Register */
|
||||
#define AT91_SAMA5D2_VERSION 0xfc
|
||||
|
||||
#define AT91_AT91_SAMA5D2_CHAN(num, addr) \
|
||||
{ \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.channel = num, \
|
||||
.address = addr, \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = 12, \
|
||||
}, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),\
|
||||
.datasheet_name = "CH"#num, \
|
||||
.indexed = 1, \
|
||||
}
|
||||
|
||||
#define at91_adc_readl(st, reg) readl_relaxed(st->base + reg)
|
||||
#define at91_adc_writel(st, reg, val) writel_relaxed(val, st->base + reg)
|
||||
|
||||
struct at91_adc_soc_info {
|
||||
unsigned startup_time;
|
||||
unsigned min_sample_rate;
|
||||
unsigned max_sample_rate;
|
||||
};
|
||||
|
||||
struct at91_adc_state {
|
||||
void __iomem *base;
|
||||
int irq;
|
||||
struct clk *per_clk;
|
||||
struct regulator *reg;
|
||||
struct regulator *vref;
|
||||
int vref_uv;
|
||||
const struct iio_chan_spec *chan;
|
||||
bool conversion_done;
|
||||
u32 conversion_value;
|
||||
struct at91_adc_soc_info soc_info;
|
||||
wait_queue_head_t wq_data_available;
|
||||
/*
|
||||
* lock to prevent concurrent 'single conversion' requests through
|
||||
* sysfs.
|
||||
*/
|
||||
struct mutex lock;
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec at91_adc_channels[] = {
|
||||
AT91_AT91_SAMA5D2_CHAN(0, 0x50),
|
||||
AT91_AT91_SAMA5D2_CHAN(1, 0x54),
|
||||
AT91_AT91_SAMA5D2_CHAN(2, 0x58),
|
||||
AT91_AT91_SAMA5D2_CHAN(3, 0x5c),
|
||||
AT91_AT91_SAMA5D2_CHAN(4, 0x60),
|
||||
AT91_AT91_SAMA5D2_CHAN(5, 0x64),
|
||||
AT91_AT91_SAMA5D2_CHAN(6, 0x68),
|
||||
AT91_AT91_SAMA5D2_CHAN(7, 0x6c),
|
||||
AT91_AT91_SAMA5D2_CHAN(8, 0x70),
|
||||
AT91_AT91_SAMA5D2_CHAN(9, 0x74),
|
||||
AT91_AT91_SAMA5D2_CHAN(10, 0x78),
|
||||
AT91_AT91_SAMA5D2_CHAN(11, 0x7c),
|
||||
};
|
||||
|
||||
static unsigned at91_adc_startup_time(unsigned startup_time_min,
|
||||
unsigned adc_clk_khz)
|
||||
{
|
||||
const unsigned startup_lookup[] = {
|
||||
0, 8, 16, 24,
|
||||
64, 80, 96, 112,
|
||||
512, 576, 640, 704,
|
||||
768, 832, 896, 960
|
||||
};
|
||||
unsigned ticks_min, i;
|
||||
|
||||
/*
|
||||
* Since the adc frequency is checked before, there is no reason
|
||||
* to not meet the startup time constraint.
|
||||
*/
|
||||
|
||||
ticks_min = startup_time_min * adc_clk_khz / 1000;
|
||||
for (i = 0; i < ARRAY_SIZE(startup_lookup); i++)
|
||||
if (startup_lookup[i] > ticks_min)
|
||||
break;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static void at91_adc_setup_samp_freq(struct at91_adc_state *st, unsigned freq)
|
||||
{
|
||||
struct iio_dev *indio_dev = iio_priv_to_dev(st);
|
||||
unsigned f_per, prescal, startup;
|
||||
|
||||
f_per = clk_get_rate(st->per_clk);
|
||||
prescal = (f_per / (2 * freq)) - 1;
|
||||
|
||||
startup = at91_adc_startup_time(st->soc_info.startup_time,
|
||||
freq / 1000);
|
||||
|
||||
at91_adc_writel(st, AT91_SAMA5D2_MR,
|
||||
AT91_SAMA5D2_MR_TRANSFER(2)
|
||||
| AT91_SAMA5D2_MR_STARTUP(startup)
|
||||
| AT91_SAMA5D2_MR_PRESCAL(prescal));
|
||||
|
||||
dev_dbg(&indio_dev->dev, "freq: %u, startup: %u, prescal: %u\n",
|
||||
freq, startup, prescal);
|
||||
}
|
||||
|
||||
static unsigned at91_adc_get_sample_freq(struct at91_adc_state *st)
|
||||
{
|
||||
unsigned f_adc, f_per = clk_get_rate(st->per_clk);
|
||||
unsigned mr, prescal;
|
||||
|
||||
mr = at91_adc_readl(st, AT91_SAMA5D2_MR);
|
||||
prescal = (mr >> AT91_SAMA5D2_MR_PRESCAL_OFFSET)
|
||||
& AT91_SAMA5D2_MR_PRESCAL_MAX;
|
||||
f_adc = f_per / (2 * (prescal + 1));
|
||||
|
||||
return f_adc;
|
||||
}
|
||||
|
||||
static irqreturn_t at91_adc_interrupt(int irq, void *private)
|
||||
{
|
||||
struct iio_dev *indio = private;
|
||||
struct at91_adc_state *st = iio_priv(indio);
|
||||
u32 status = at91_adc_readl(st, AT91_SAMA5D2_ISR);
|
||||
u32 imr = at91_adc_readl(st, AT91_SAMA5D2_IMR);
|
||||
|
||||
if (status & imr) {
|
||||
st->conversion_value = at91_adc_readl(st, st->chan->address);
|
||||
st->conversion_done = true;
|
||||
wake_up_interruptible(&st->wq_data_available);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
static int at91_adc_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct at91_adc_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
mutex_lock(&st->lock);
|
||||
|
||||
st->chan = chan;
|
||||
|
||||
at91_adc_writel(st, AT91_SAMA5D2_CHER, BIT(chan->channel));
|
||||
at91_adc_writel(st, AT91_SAMA5D2_IER, BIT(chan->channel));
|
||||
at91_adc_writel(st, AT91_SAMA5D2_CR, AT91_SAMA5D2_CR_START);
|
||||
|
||||
ret = wait_event_interruptible_timeout(st->wq_data_available,
|
||||
st->conversion_done,
|
||||
msecs_to_jiffies(1000));
|
||||
if (ret == 0)
|
||||
ret = -ETIMEDOUT;
|
||||
|
||||
if (ret > 0) {
|
||||
*val = st->conversion_value;
|
||||
ret = IIO_VAL_INT;
|
||||
st->conversion_done = false;
|
||||
}
|
||||
|
||||
at91_adc_writel(st, AT91_SAMA5D2_IDR, BIT(chan->channel));
|
||||
at91_adc_writel(st, AT91_SAMA5D2_CHDR, BIT(chan->channel));
|
||||
|
||||
mutex_unlock(&st->lock);
|
||||
return ret;
|
||||
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = st->vref_uv / 1000;
|
||||
*val2 = chan->scan_type.realbits;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
*val = at91_adc_get_sample_freq(st);
|
||||
return IIO_VAL_INT;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int at91_adc_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
struct at91_adc_state *st = iio_priv(indio_dev);
|
||||
|
||||
if (mask != IIO_CHAN_INFO_SAMP_FREQ)
|
||||
return -EINVAL;
|
||||
|
||||
if (val < st->soc_info.min_sample_rate ||
|
||||
val > st->soc_info.max_sample_rate)
|
||||
return -EINVAL;
|
||||
|
||||
at91_adc_setup_samp_freq(st, val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct iio_info at91_adc_info = {
|
||||
.read_raw = &at91_adc_read_raw,
|
||||
.write_raw = &at91_adc_write_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int at91_adc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct at91_adc_state *st;
|
||||
struct resource *res;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*st));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
indio_dev->dev.parent = &pdev->dev;
|
||||
indio_dev->name = dev_name(&pdev->dev);
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &at91_adc_info;
|
||||
indio_dev->channels = at91_adc_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(at91_adc_channels);
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
|
||||
ret = of_property_read_u32(pdev->dev.of_node,
|
||||
"atmel,min-sample-rate-hz",
|
||||
&st->soc_info.min_sample_rate);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev,
|
||||
"invalid or missing value for atmel,min-sample-rate-hz\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(pdev->dev.of_node,
|
||||
"atmel,max-sample-rate-hz",
|
||||
&st->soc_info.max_sample_rate);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev,
|
||||
"invalid or missing value for atmel,max-sample-rate-hz\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(pdev->dev.of_node, "atmel,startup-time-ms",
|
||||
&st->soc_info.startup_time);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev,
|
||||
"invalid or missing value for atmel,startup-time-ms\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
init_waitqueue_head(&st->wq_data_available);
|
||||
mutex_init(&st->lock);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res)
|
||||
return -EINVAL;
|
||||
|
||||
st->base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(st->base))
|
||||
return PTR_ERR(st->base);
|
||||
|
||||
st->irq = platform_get_irq(pdev, 0);
|
||||
if (st->irq <= 0) {
|
||||
if (!st->irq)
|
||||
st->irq = -ENXIO;
|
||||
|
||||
return st->irq;
|
||||
}
|
||||
|
||||
st->per_clk = devm_clk_get(&pdev->dev, "adc_clk");
|
||||
if (IS_ERR(st->per_clk))
|
||||
return PTR_ERR(st->per_clk);
|
||||
|
||||
st->reg = devm_regulator_get(&pdev->dev, "vddana");
|
||||
if (IS_ERR(st->reg))
|
||||
return PTR_ERR(st->reg);
|
||||
|
||||
st->vref = devm_regulator_get(&pdev->dev, "vref");
|
||||
if (IS_ERR(st->vref))
|
||||
return PTR_ERR(st->vref);
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, st->irq, at91_adc_interrupt, 0,
|
||||
pdev->dev.driver->name, indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regulator_enable(st->reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regulator_enable(st->vref);
|
||||
if (ret)
|
||||
goto reg_disable;
|
||||
|
||||
st->vref_uv = regulator_get_voltage(st->vref);
|
||||
if (st->vref_uv <= 0) {
|
||||
ret = -EINVAL;
|
||||
goto vref_disable;
|
||||
}
|
||||
|
||||
at91_adc_writel(st, AT91_SAMA5D2_CR, AT91_SAMA5D2_CR_SWRST);
|
||||
at91_adc_writel(st, AT91_SAMA5D2_IDR, 0xffffffff);
|
||||
|
||||
at91_adc_setup_samp_freq(st, st->soc_info.min_sample_rate);
|
||||
|
||||
ret = clk_prepare_enable(st->per_clk);
|
||||
if (ret)
|
||||
goto vref_disable;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret < 0)
|
||||
goto per_clk_disable_unprepare;
|
||||
|
||||
dev_info(&pdev->dev, "version: %x\n",
|
||||
readl_relaxed(st->base + AT91_SAMA5D2_VERSION));
|
||||
|
||||
return 0;
|
||||
|
||||
per_clk_disable_unprepare:
|
||||
clk_disable_unprepare(st->per_clk);
|
||||
vref_disable:
|
||||
regulator_disable(st->vref);
|
||||
reg_disable:
|
||||
regulator_disable(st->reg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int at91_adc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
struct at91_adc_state *st = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
clk_disable_unprepare(st->per_clk);
|
||||
|
||||
regulator_disable(st->vref);
|
||||
regulator_disable(st->reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id at91_adc_dt_match[] = {
|
||||
{
|
||||
.compatible = "atmel,sama5d2-adc",
|
||||
}, {
|
||||
/* sentinel */
|
||||
}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, at91_adc_dt_match);
|
||||
|
||||
static struct platform_driver at91_adc_driver = {
|
||||
.probe = at91_adc_probe,
|
||||
.remove = at91_adc_remove,
|
||||
.driver = {
|
||||
.name = "at91-sama5d2_adc",
|
||||
.of_match_table = at91_adc_dt_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(at91_adc_driver)
|
||||
|
||||
MODULE_AUTHOR("Ludovic Desroches <ludovic.desroches@atmel.com>");
|
||||
MODULE_DESCRIPTION("Atmel AT91 SAMA5D2 ADC");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -46,7 +46,7 @@ struct axp288_adc_info {
|
||||
struct regmap *regmap;
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec const axp288_adc_channels[] = {
|
||||
static const struct iio_chan_spec axp288_adc_channels[] = {
|
||||
{
|
||||
.indexed = 1,
|
||||
.type = IIO_TEMP,
|
||||
|
@ -187,26 +187,27 @@ static int mcp320x_read_raw(struct iio_dev *indio_dev,
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \
|
||||
}
|
||||
|
||||
#define MCP320X_VOLTAGE_CHANNEL_DIFF(num) \
|
||||
#define MCP320X_VOLTAGE_CHANNEL_DIFF(chan1, chan2) \
|
||||
{ \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = (num * 2), \
|
||||
.channel2 = (num * 2 + 1), \
|
||||
.address = (num * 2), \
|
||||
.channel = (chan1), \
|
||||
.channel2 = (chan2), \
|
||||
.address = (chan1), \
|
||||
.differential = 1, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec mcp3201_channels[] = {
|
||||
MCP320X_VOLTAGE_CHANNEL_DIFF(0),
|
||||
MCP320X_VOLTAGE_CHANNEL_DIFF(0, 1),
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec mcp3202_channels[] = {
|
||||
MCP320X_VOLTAGE_CHANNEL(0),
|
||||
MCP320X_VOLTAGE_CHANNEL(1),
|
||||
MCP320X_VOLTAGE_CHANNEL_DIFF(0),
|
||||
MCP320X_VOLTAGE_CHANNEL_DIFF(0, 1),
|
||||
MCP320X_VOLTAGE_CHANNEL_DIFF(1, 0),
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec mcp3204_channels[] = {
|
||||
@ -214,8 +215,10 @@ static const struct iio_chan_spec mcp3204_channels[] = {
|
||||
MCP320X_VOLTAGE_CHANNEL(1),
|
||||
MCP320X_VOLTAGE_CHANNEL(2),
|
||||
MCP320X_VOLTAGE_CHANNEL(3),
|
||||
MCP320X_VOLTAGE_CHANNEL_DIFF(0),
|
||||
MCP320X_VOLTAGE_CHANNEL_DIFF(1),
|
||||
MCP320X_VOLTAGE_CHANNEL_DIFF(0, 1),
|
||||
MCP320X_VOLTAGE_CHANNEL_DIFF(1, 0),
|
||||
MCP320X_VOLTAGE_CHANNEL_DIFF(2, 3),
|
||||
MCP320X_VOLTAGE_CHANNEL_DIFF(3, 2),
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec mcp3208_channels[] = {
|
||||
@ -227,10 +230,14 @@ static const struct iio_chan_spec mcp3208_channels[] = {
|
||||
MCP320X_VOLTAGE_CHANNEL(5),
|
||||
MCP320X_VOLTAGE_CHANNEL(6),
|
||||
MCP320X_VOLTAGE_CHANNEL(7),
|
||||
MCP320X_VOLTAGE_CHANNEL_DIFF(0),
|
||||
MCP320X_VOLTAGE_CHANNEL_DIFF(1),
|
||||
MCP320X_VOLTAGE_CHANNEL_DIFF(2),
|
||||
MCP320X_VOLTAGE_CHANNEL_DIFF(3),
|
||||
MCP320X_VOLTAGE_CHANNEL_DIFF(0, 1),
|
||||
MCP320X_VOLTAGE_CHANNEL_DIFF(1, 0),
|
||||
MCP320X_VOLTAGE_CHANNEL_DIFF(2, 3),
|
||||
MCP320X_VOLTAGE_CHANNEL_DIFF(3, 2),
|
||||
MCP320X_VOLTAGE_CHANNEL_DIFF(4, 5),
|
||||
MCP320X_VOLTAGE_CHANNEL_DIFF(5, 4),
|
||||
MCP320X_VOLTAGE_CHANNEL_DIFF(6, 7),
|
||||
MCP320X_VOLTAGE_CHANNEL_DIFF(7, 6),
|
||||
};
|
||||
|
||||
static const struct iio_info mcp320x_info = {
|
||||
|
@ -1,11 +1,12 @@
|
||||
/*
|
||||
* mcp3422.c - driver for the Microchip mcp3422/3/4/6/7/8 chip family
|
||||
* mcp3422.c - driver for the Microchip mcp3421/2/3/4/5/6/7/8 chip family
|
||||
*
|
||||
* Copyright (C) 2013, Angelo Compagnucci
|
||||
* Author: Angelo Compagnucci <angelo.compagnucci@gmail.com>
|
||||
*
|
||||
* Datasheet: http://ww1.microchip.com/downloads/en/devicedoc/22088b.pdf
|
||||
* http://ww1.microchip.com/downloads/en/DeviceDoc/22226a.pdf
|
||||
* http://ww1.microchip.com/downloads/en/DeviceDoc/22072b.pdf
|
||||
*
|
||||
* This driver exports the value of analog input voltage to sysfs, the
|
||||
* voltage unit is nV.
|
||||
@ -357,6 +358,7 @@ static int mcp3422_probe(struct i2c_client *client,
|
||||
|
||||
switch (adc->id) {
|
||||
case 1:
|
||||
case 5:
|
||||
indio_dev->channels = mcp3421_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(mcp3421_channels);
|
||||
break;
|
||||
@ -395,6 +397,7 @@ static const struct i2c_device_id mcp3422_id[] = {
|
||||
{ "mcp3422", 2 },
|
||||
{ "mcp3423", 3 },
|
||||
{ "mcp3424", 4 },
|
||||
{ "mcp3425", 5 },
|
||||
{ "mcp3426", 6 },
|
||||
{ "mcp3427", 7 },
|
||||
{ "mcp3428", 8 },
|
||||
@ -421,5 +424,5 @@ static struct i2c_driver mcp3422_driver = {
|
||||
module_i2c_driver(mcp3422_driver);
|
||||
|
||||
MODULE_AUTHOR("Angelo Compagnucci <angelo.compagnucci@gmail.com>");
|
||||
MODULE_DESCRIPTION("Microchip mcp3422/3/4/6/7/8 driver");
|
||||
MODULE_DESCRIPTION("Microchip mcp3421/2/3/4/5/6/7/8 driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
@ -4,6 +4,19 @@
|
||||
|
||||
menu "Chemical Sensors"
|
||||
|
||||
config ATLAS_PH_SENSOR
|
||||
tristate "Atlas Scientific OEM pH-SM sensor"
|
||||
depends on I2C
|
||||
select REGMAP_I2C
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
Say Y here to build I2C interface support for the Atlas
|
||||
Scientific OEM pH-SM sensor.
|
||||
|
||||
To compile this driver as module, choose M here: the
|
||||
module will be called atlas-ph-sensor.
|
||||
|
||||
config IAQCORE
|
||||
tristate "AMS iAQ-Core VOC sensors"
|
||||
depends on I2C
|
||||
|
@ -3,5 +3,6 @@
|
||||
#
|
||||
|
||||
# When adding new entries keep the list in alphabetical order
|
||||
obj-$(CONFIG_ATLAS_PH_SENSOR) += atlas-ph-sensor.o
|
||||
obj-$(CONFIG_IAQCORE) += ams-iaq-core.o
|
||||
obj-$(CONFIG_VZ89X) += vz89x.o
|
||||
|
511
drivers/iio/chemical/atlas-ph-sensor.c
Normal file
511
drivers/iio/chemical/atlas-ph-sensor.c
Normal file
@ -0,0 +1,511 @@
|
||||
/*
|
||||
* atlas-ph-sensor.c - Support for Atlas Scientific OEM pH-SM sensor
|
||||
*
|
||||
* Copyright (C) 2015 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/interrupt.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/irq_work.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#define ATLAS_REGMAP_NAME "atlas_ph_regmap"
|
||||
#define ATLAS_DRV_NAME "atlas_ph"
|
||||
|
||||
#define ATLAS_REG_DEV_TYPE 0x00
|
||||
#define ATLAS_REG_DEV_VERSION 0x01
|
||||
|
||||
#define ATLAS_REG_INT_CONTROL 0x04
|
||||
#define ATLAS_REG_INT_CONTROL_EN BIT(3)
|
||||
|
||||
#define ATLAS_REG_PWR_CONTROL 0x06
|
||||
|
||||
#define ATLAS_REG_CALIB_STATUS 0x0d
|
||||
#define ATLAS_REG_CALIB_STATUS_MASK 0x07
|
||||
#define ATLAS_REG_CALIB_STATUS_LOW BIT(0)
|
||||
#define ATLAS_REG_CALIB_STATUS_MID BIT(1)
|
||||
#define ATLAS_REG_CALIB_STATUS_HIGH BIT(2)
|
||||
|
||||
#define ATLAS_REG_TEMP_DATA 0x0e
|
||||
#define ATLAS_REG_PH_DATA 0x16
|
||||
|
||||
#define ATLAS_PH_INT_TIME_IN_US 450000
|
||||
|
||||
struct atlas_data {
|
||||
struct i2c_client *client;
|
||||
struct iio_trigger *trig;
|
||||
struct regmap *regmap;
|
||||
struct irq_work work;
|
||||
|
||||
__be32 buffer[4]; /* 32-bit pH 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_CALIB_STATUS, ATLAS_REG_CALIB_STATUS),
|
||||
regmap_reg_range(ATLAS_REG_TEMP_DATA, ATLAS_REG_TEMP_DATA + 4),
|
||||
regmap_reg_range(ATLAS_REG_PH_DATA, ATLAS_REG_PH_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_PH_DATA + 4,
|
||||
.cache_type = REGCACHE_FLAT,
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec atlas_channels[] = {
|
||||
{
|
||||
.type = IIO_PH,
|
||||
.info_mask_separate =
|
||||
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
|
||||
.scan_index = 0,
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
.realbits = 32,
|
||||
.storagebits = 32,
|
||||
.endianness = IIO_BE,
|
||||
},
|
||||
},
|
||||
IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
{
|
||||
.type = IIO_TEMP,
|
||||
.address = ATLAS_REG_TEMP_DATA,
|
||||
.info_mask_separate =
|
||||
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
|
||||
.output = 1,
|
||||
.scan_index = -1
|
||||
},
|
||||
};
|
||||
|
||||
static int atlas_set_powermode(struct atlas_data *data, int on)
|
||||
{
|
||||
return regmap_write(data->regmap, ATLAS_REG_PWR_CONTROL, on);
|
||||
}
|
||||
|
||||
static int atlas_set_interrupt(struct atlas_data *data, bool state)
|
||||
{
|
||||
return regmap_update_bits(data->regmap, ATLAS_REG_INT_CONTROL,
|
||||
ATLAS_REG_INT_CONTROL_EN,
|
||||
state ? ATLAS_REG_INT_CONTROL_EN : 0);
|
||||
}
|
||||
|
||||
static int atlas_buffer_postenable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct atlas_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = iio_triggered_buffer_postenable(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = pm_runtime_get_sync(&data->client->dev);
|
||||
if (ret < 0) {
|
||||
pm_runtime_put_noidle(&data->client->dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return atlas_set_interrupt(data, true);
|
||||
}
|
||||
|
||||
static int atlas_buffer_predisable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct atlas_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = iio_triggered_buffer_predisable(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = atlas_set_interrupt(data, false);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pm_runtime_mark_last_busy(&data->client->dev);
|
||||
return pm_runtime_put_autosuspend(&data->client->dev);
|
||||
}
|
||||
|
||||
static const struct iio_trigger_ops atlas_interrupt_trigger_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static const struct iio_buffer_setup_ops atlas_buffer_setup_ops = {
|
||||
.postenable = atlas_buffer_postenable,
|
||||
.predisable = atlas_buffer_predisable,
|
||||
};
|
||||
|
||||
static void atlas_work_handler(struct irq_work *work)
|
||||
{
|
||||
struct atlas_data *data = container_of(work, struct atlas_data, work);
|
||||
|
||||
iio_trigger_poll(data->trig);
|
||||
}
|
||||
|
||||
static irqreturn_t atlas_trigger_handler(int irq, void *private)
|
||||
{
|
||||
struct iio_poll_func *pf = private;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct atlas_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_read_i2c_block_data(data->client, ATLAS_REG_PH_DATA,
|
||||
sizeof(data->buffer[0]), (u8 *) &data->buffer);
|
||||
|
||||
if (ret > 0)
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
|
||||
iio_get_time_ns());
|
||||
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t atlas_interrupt_handler(int irq, void *private)
|
||||
{
|
||||
struct iio_dev *indio_dev = private;
|
||||
struct atlas_data *data = iio_priv(indio_dev);
|
||||
|
||||
irq_work_queue(&data->work);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int atlas_read_ph_measurement(struct atlas_data *data, __be32 *val)
|
||||
{
|
||||
struct device *dev = &data->client->dev;
|
||||
int suspended = pm_runtime_suspended(dev);
|
||||
int ret;
|
||||
|
||||
ret = pm_runtime_get_sync(dev);
|
||||
if (ret < 0) {
|
||||
pm_runtime_put_noidle(dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (suspended)
|
||||
usleep_range(ATLAS_PH_INT_TIME_IN_US,
|
||||
ATLAS_PH_INT_TIME_IN_US + 100000);
|
||||
|
||||
ret = regmap_bulk_read(data->regmap, ATLAS_REG_PH_DATA,
|
||||
(u8 *) val, sizeof(*val));
|
||||
|
||||
pm_runtime_mark_last_busy(dev);
|
||||
pm_runtime_put_autosuspend(dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int atlas_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct atlas_data *data = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW: {
|
||||
int ret;
|
||||
__be32 reg;
|
||||
|
||||
switch (chan->type) {
|
||||
case IIO_TEMP:
|
||||
ret = regmap_bulk_read(data->regmap, chan->address,
|
||||
(u8 *) ®, sizeof(reg));
|
||||
break;
|
||||
case IIO_PH:
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
|
||||
if (iio_buffer_enabled(indio_dev))
|
||||
ret = -EBUSY;
|
||||
else
|
||||
ret = atlas_read_ph_measurement(data, ®);
|
||||
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
if (!ret) {
|
||||
*val = be32_to_cpu(reg);
|
||||
ret = IIO_VAL_INT;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
switch (chan->type) {
|
||||
case IIO_TEMP:
|
||||
*val = 1; /* 0.01 */
|
||||
*val2 = 100;
|
||||
break;
|
||||
case IIO_PH:
|
||||
*val = 1; /* 0.001 */
|
||||
*val2 = 1000;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
return IIO_VAL_FRACTIONAL;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int atlas_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
struct atlas_data *data = iio_priv(indio_dev);
|
||||
__be32 reg = cpu_to_be32(val);
|
||||
|
||||
if (val2 != 0 || val < 0 || val > 20000)
|
||||
return -EINVAL;
|
||||
|
||||
if (mask != IIO_CHAN_INFO_RAW || chan->type != IIO_TEMP)
|
||||
return -EINVAL;
|
||||
|
||||
return regmap_bulk_write(data->regmap, chan->address,
|
||||
®, sizeof(reg));
|
||||
}
|
||||
|
||||
static const struct iio_info atlas_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = atlas_read_raw,
|
||||
.write_raw = atlas_write_raw,
|
||||
};
|
||||
|
||||
static int atlas_check_calibration(struct atlas_data *data)
|
||||
{
|
||||
struct device *dev = &data->client->dev;
|
||||
int ret;
|
||||
unsigned int val;
|
||||
|
||||
ret = regmap_read(data->regmap, ATLAS_REG_CALIB_STATUS, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!(val & ATLAS_REG_CALIB_STATUS_MASK)) {
|
||||
dev_warn(dev, "device has not been calibrated\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(val & ATLAS_REG_CALIB_STATUS_LOW))
|
||||
dev_warn(dev, "device missing low point calibration\n");
|
||||
|
||||
if (!(val & ATLAS_REG_CALIB_STATUS_MID))
|
||||
dev_warn(dev, "device missing mid point calibration\n");
|
||||
|
||||
if (!(val & ATLAS_REG_CALIB_STATUS_HIGH))
|
||||
dev_warn(dev, "device missing high point calibration\n");
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
static int atlas_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct atlas_data *data;
|
||||
struct iio_trigger *trig;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
indio_dev->info = &atlas_info;
|
||||
indio_dev->name = ATLAS_DRV_NAME;
|
||||
indio_dev->channels = atlas_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(atlas_channels);
|
||||
indio_dev->modes = INDIO_BUFFER_SOFTWARE | INDIO_DIRECT_MODE;
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
|
||||
trig = devm_iio_trigger_alloc(&client->dev, "%s-dev%d",
|
||||
indio_dev->name, indio_dev->id);
|
||||
|
||||
if (!trig)
|
||||
return -ENOMEM;
|
||||
|
||||
data = iio_priv(indio_dev);
|
||||
data->client = client;
|
||||
data->trig = trig;
|
||||
trig->dev.parent = indio_dev->dev.parent;
|
||||
trig->ops = &atlas_interrupt_trigger_ops;
|
||||
iio_trigger_set_drvdata(trig, indio_dev);
|
||||
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
|
||||
data->regmap = devm_regmap_init_i2c(client, &atlas_regmap_config);
|
||||
if (IS_ERR(data->regmap)) {
|
||||
dev_err(&client->dev, "regmap initialization failed\n");
|
||||
return PTR_ERR(data->regmap);
|
||||
}
|
||||
|
||||
ret = pm_runtime_set_active(&client->dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (client->irq <= 0) {
|
||||
dev_err(&client->dev, "no valid irq defined\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = atlas_check_calibration(data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = iio_trigger_register(trig);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "failed to register trigger\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
|
||||
&atlas_trigger_handler, &atlas_buffer_setup_ops);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "cannot setup iio trigger\n");
|
||||
goto unregister_trigger;
|
||||
}
|
||||
|
||||
init_irq_work(&data->work, atlas_work_handler);
|
||||
|
||||
/* interrupt pin toggles on new conversion */
|
||||
ret = devm_request_threaded_irq(&client->dev, client->irq,
|
||||
NULL, atlas_interrupt_handler,
|
||||
IRQF_TRIGGER_RISING |
|
||||
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
||||
"atlas_irq",
|
||||
indio_dev);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "request irq (%d) failed\n", client->irq);
|
||||
goto unregister_buffer;
|
||||
}
|
||||
|
||||
ret = atlas_set_powermode(data, 1);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "cannot power device on");
|
||||
goto unregister_buffer;
|
||||
}
|
||||
|
||||
pm_runtime_enable(&client->dev);
|
||||
pm_runtime_set_autosuspend_delay(&client->dev, 2500);
|
||||
pm_runtime_use_autosuspend(&client->dev);
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "unable to register device\n");
|
||||
goto unregister_pm;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
unregister_pm:
|
||||
pm_runtime_disable(&client->dev);
|
||||
atlas_set_powermode(data, 0);
|
||||
|
||||
unregister_buffer:
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
|
||||
unregister_trigger:
|
||||
iio_trigger_unregister(data->trig);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int atlas_remove(struct i2c_client *client)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
struct atlas_data *data = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
iio_trigger_unregister(data->trig);
|
||||
|
||||
pm_runtime_disable(&client->dev);
|
||||
pm_runtime_set_suspended(&client->dev);
|
||||
pm_runtime_put_noidle(&client->dev);
|
||||
|
||||
return atlas_set_powermode(data, 0);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int atlas_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct atlas_data *data =
|
||||
iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
|
||||
|
||||
return atlas_set_powermode(data, 0);
|
||||
}
|
||||
|
||||
static int atlas_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct atlas_data *data =
|
||||
iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
|
||||
|
||||
return atlas_set_powermode(data, 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops atlas_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(atlas_runtime_suspend,
|
||||
atlas_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
static const struct i2c_device_id atlas_id[] = {
|
||||
{ "atlas-ph-sm", 0 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, atlas_id);
|
||||
|
||||
static const struct of_device_id atlas_dt_ids[] = {
|
||||
{ .compatible = "atlas,ph-sm" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, atlas_dt_ids);
|
||||
|
||||
static struct i2c_driver atlas_driver = {
|
||||
.driver = {
|
||||
.name = ATLAS_DRV_NAME,
|
||||
.of_match_table = of_match_ptr(atlas_dt_ids),
|
||||
.pm = &atlas_pm_ops,
|
||||
},
|
||||
.probe = atlas_probe,
|
||||
.remove = atlas_remove,
|
||||
.id_table = atlas_id,
|
||||
};
|
||||
module_i2c_driver(atlas_driver);
|
||||
|
||||
MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
|
||||
MODULE_DESCRIPTION("Atlas Scientific pH-SM sensor");
|
||||
MODULE_LICENSE("GPL");
|
@ -18,13 +18,15 @@
|
||||
#include <asm/unaligned.h>
|
||||
#include <linux/iio/common/st_sensors.h>
|
||||
|
||||
#include "st_sensors_core.h"
|
||||
|
||||
static inline u32 st_sensors_get_unaligned_le24(const u8 *p)
|
||||
{
|
||||
return (s32)((p[0] | p[1] << 8 | p[2] << 16) << 8) >> 8;
|
||||
}
|
||||
|
||||
static int st_sensors_write_data_with_mask(struct iio_dev *indio_dev,
|
||||
u8 reg_addr, u8 mask, u8 data)
|
||||
int st_sensors_write_data_with_mask(struct iio_dev *indio_dev,
|
||||
u8 reg_addr, u8 mask, u8 data)
|
||||
{
|
||||
int err;
|
||||
u8 new_data;
|
||||
|
8
drivers/iio/common/st_sensors/st_sensors_core.h
Normal file
8
drivers/iio/common/st_sensors/st_sensors_core.h
Normal file
@ -0,0 +1,8 @@
|
||||
/*
|
||||
* Local functions in the ST Sensors core
|
||||
*/
|
||||
#ifndef __ST_SENSORS_CORE_H
|
||||
#define __ST_SENSORS_CORE_H
|
||||
int st_sensors_write_data_with_mask(struct iio_dev *indio_dev,
|
||||
u8 reg_addr, u8 mask, u8 data);
|
||||
#endif
|
@ -14,32 +14,65 @@
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
#include <linux/iio/common/st_sensors.h>
|
||||
|
||||
#include "st_sensors_core.h"
|
||||
|
||||
int st_sensors_allocate_trigger(struct iio_dev *indio_dev,
|
||||
const struct iio_trigger_ops *trigger_ops)
|
||||
{
|
||||
int err;
|
||||
int err, irq;
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
unsigned long irq_trig;
|
||||
|
||||
sdata->trig = iio_trigger_alloc("%s-trigger", indio_dev->name);
|
||||
if (sdata->trig == NULL) {
|
||||
err = -ENOMEM;
|
||||
dev_err(&indio_dev->dev, "failed to allocate iio trigger.\n");
|
||||
goto iio_trigger_alloc_error;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
err = request_threaded_irq(sdata->get_irq_data_ready(indio_dev),
|
||||
irq = sdata->get_irq_data_ready(indio_dev);
|
||||
irq_trig = irqd_get_trigger_type(irq_get_irq_data(irq));
|
||||
/*
|
||||
* If the IRQ is triggered on falling edge, we need to mark the
|
||||
* interrupt as active low, if the hardware supports this.
|
||||
*/
|
||||
if (irq_trig == IRQF_TRIGGER_FALLING) {
|
||||
if (!sdata->sensor_settings->drdy_irq.addr_ihl) {
|
||||
dev_err(&indio_dev->dev,
|
||||
"falling edge specified for IRQ but hardware "
|
||||
"only support rising edge, will request "
|
||||
"rising edge\n");
|
||||
irq_trig = IRQF_TRIGGER_RISING;
|
||||
} else {
|
||||
/* Set up INT active low i.e. falling edge */
|
||||
err = st_sensors_write_data_with_mask(indio_dev,
|
||||
sdata->sensor_settings->drdy_irq.addr_ihl,
|
||||
sdata->sensor_settings->drdy_irq.mask_ihl, 1);
|
||||
if (err < 0)
|
||||
goto iio_trigger_free;
|
||||
dev_info(&indio_dev->dev,
|
||||
"interrupts on the falling edge\n");
|
||||
}
|
||||
} else if (irq_trig == IRQF_TRIGGER_RISING) {
|
||||
dev_info(&indio_dev->dev,
|
||||
"interrupts on the rising edge\n");
|
||||
|
||||
} else {
|
||||
dev_err(&indio_dev->dev,
|
||||
"unsupported IRQ trigger specified (%lx), only "
|
||||
"rising and falling edges supported, enforce "
|
||||
"rising edge\n", irq_trig);
|
||||
irq_trig = IRQF_TRIGGER_RISING;
|
||||
}
|
||||
err = request_threaded_irq(irq,
|
||||
iio_trigger_generic_data_rdy_poll,
|
||||
NULL,
|
||||
IRQF_TRIGGER_RISING,
|
||||
irq_trig,
|
||||
sdata->trig->name,
|
||||
sdata->trig);
|
||||
if (err) {
|
||||
dev_err(&indio_dev->dev, "failed to request trigger IRQ.\n");
|
||||
goto request_irq_error;
|
||||
goto iio_trigger_free;
|
||||
}
|
||||
|
||||
iio_trigger_set_drvdata(sdata->trig, indio_dev);
|
||||
@ -57,9 +90,8 @@ int st_sensors_allocate_trigger(struct iio_dev *indio_dev,
|
||||
|
||||
iio_trigger_register_error:
|
||||
free_irq(sdata->get_irq_data_ready(indio_dev), sdata->trig);
|
||||
request_irq_error:
|
||||
iio_trigger_free:
|
||||
iio_trigger_free(sdata->trig);
|
||||
iio_trigger_alloc_error:
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(st_sensors_allocate_trigger);
|
||||
|
@ -111,6 +111,16 @@ config AD5755
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ad5755.
|
||||
|
||||
config AD5761
|
||||
tristate "Analog Devices AD5761/61R/21/21R DAC driver"
|
||||
depends on SPI_MASTER
|
||||
help
|
||||
Say yes here to build support for Analog Devices AD5761, AD5761R, AD5721,
|
||||
AD5721R Digital to Analog Converter.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ad5761.
|
||||
|
||||
config AD5764
|
||||
tristate "Analog Devices AD5764/64R/44/44R DAC driver"
|
||||
depends on SPI_MASTER
|
||||
@ -176,11 +186,11 @@ config MAX5821
|
||||
10 bits DAC.
|
||||
|
||||
config MCP4725
|
||||
tristate "MCP4725 DAC driver"
|
||||
tristate "MCP4725/6 DAC driver"
|
||||
depends on I2C
|
||||
---help---
|
||||
Say Y here if you want to build a driver for the Microchip
|
||||
MCP 4725 12-bit digital-to-analog converter (DAC) with I2C
|
||||
MCP 4725/6 12-bit digital-to-analog converter (DAC) with I2C
|
||||
interface.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
|
@ -12,6 +12,7 @@ obj-$(CONFIG_AD5504) += ad5504.o
|
||||
obj-$(CONFIG_AD5446) += ad5446.o
|
||||
obj-$(CONFIG_AD5449) += ad5449.o
|
||||
obj-$(CONFIG_AD5755) += ad5755.o
|
||||
obj-$(CONFIG_AD5761) += ad5761.o
|
||||
obj-$(CONFIG_AD5764) += ad5764.o
|
||||
obj-$(CONFIG_AD5791) += ad5791.o
|
||||
obj-$(CONFIG_AD5686) += ad5686.o
|
||||
|
430
drivers/iio/dac/ad5761.c
Normal file
430
drivers/iio/dac/ad5761.c
Normal file
@ -0,0 +1,430 @@
|
||||
/*
|
||||
* AD5721, AD5721R, AD5761, AD5761R, Voltage Output Digital to Analog Converter
|
||||
*
|
||||
* Copyright 2016 Qtechnology A/S
|
||||
* 2016 Ricardo Ribalda <ricardo.ribalda@gmail.com>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/platform_data/ad5761.h>
|
||||
|
||||
#define AD5761_ADDR(addr) ((addr & 0xf) << 16)
|
||||
#define AD5761_ADDR_NOOP 0x0
|
||||
#define AD5761_ADDR_DAC_WRITE 0x3
|
||||
#define AD5761_ADDR_CTRL_WRITE_REG 0x4
|
||||
#define AD5761_ADDR_SW_DATA_RESET 0x7
|
||||
#define AD5761_ADDR_DAC_READ 0xb
|
||||
#define AD5761_ADDR_CTRL_READ_REG 0xc
|
||||
#define AD5761_ADDR_SW_FULL_RESET 0xf
|
||||
|
||||
#define AD5761_CTRL_USE_INTVREF BIT(5)
|
||||
#define AD5761_CTRL_ETS BIT(6)
|
||||
|
||||
/**
|
||||
* struct ad5761_chip_info - chip specific information
|
||||
* @int_vref: Value of the internal reference voltage in mV - 0 if external
|
||||
* reference voltage is used
|
||||
* @channel: channel specification
|
||||
*/
|
||||
|
||||
struct ad5761_chip_info {
|
||||
unsigned long int_vref;
|
||||
const struct iio_chan_spec channel;
|
||||
};
|
||||
|
||||
struct ad5761_range_params {
|
||||
int m;
|
||||
int c;
|
||||
};
|
||||
|
||||
enum ad5761_supported_device_ids {
|
||||
ID_AD5721,
|
||||
ID_AD5721R,
|
||||
ID_AD5761,
|
||||
ID_AD5761R,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ad5761_state - driver instance specific data
|
||||
* @spi: spi_device
|
||||
* @vref_reg: reference voltage regulator
|
||||
* @use_intref: true when the internal voltage reference is used
|
||||
* @vref: actual voltage reference in mVolts
|
||||
* @range: output range mode used
|
||||
* @data: cache aligned spi buffer
|
||||
*/
|
||||
struct ad5761_state {
|
||||
struct spi_device *spi;
|
||||
struct regulator *vref_reg;
|
||||
|
||||
bool use_intref;
|
||||
int vref;
|
||||
enum ad5761_voltage_range range;
|
||||
|
||||
/*
|
||||
* DMA (thus cache coherency maintenance) requires the
|
||||
* transfer buffers to live in their own cache lines.
|
||||
*/
|
||||
union {
|
||||
__be32 d32;
|
||||
u8 d8[4];
|
||||
} data[3] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
static const struct ad5761_range_params ad5761_range_params[] = {
|
||||
[AD5761_VOLTAGE_RANGE_M10V_10V] = {
|
||||
.m = 80,
|
||||
.c = 40,
|
||||
},
|
||||
[AD5761_VOLTAGE_RANGE_0V_10V] = {
|
||||
.m = 40,
|
||||
.c = 0,
|
||||
},
|
||||
[AD5761_VOLTAGE_RANGE_M5V_5V] = {
|
||||
.m = 40,
|
||||
.c = 20,
|
||||
},
|
||||
[AD5761_VOLTAGE_RANGE_0V_5V] = {
|
||||
.m = 20,
|
||||
.c = 0,
|
||||
},
|
||||
[AD5761_VOLTAGE_RANGE_M2V5_7V5] = {
|
||||
.m = 40,
|
||||
.c = 10,
|
||||
},
|
||||
[AD5761_VOLTAGE_RANGE_M3V_3V] = {
|
||||
.m = 24,
|
||||
.c = 12,
|
||||
},
|
||||
[AD5761_VOLTAGE_RANGE_0V_16V] = {
|
||||
.m = 64,
|
||||
.c = 0,
|
||||
},
|
||||
[AD5761_VOLTAGE_RANGE_0V_20V] = {
|
||||
.m = 80,
|
||||
.c = 0,
|
||||
},
|
||||
};
|
||||
|
||||
static int _ad5761_spi_write(struct ad5761_state *st, u8 addr, u16 val)
|
||||
{
|
||||
st->data[0].d32 = cpu_to_be32(AD5761_ADDR(addr) | val);
|
||||
|
||||
return spi_write(st->spi, &st->data[0].d8[1], 3);
|
||||
}
|
||||
|
||||
static int ad5761_spi_write(struct iio_dev *indio_dev, u8 addr, u16 val)
|
||||
{
|
||||
struct ad5761_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
ret = _ad5761_spi_write(st, addr, val);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int _ad5761_spi_read(struct ad5761_state *st, u8 addr, u16 *val)
|
||||
{
|
||||
int ret;
|
||||
struct spi_transfer xfers[] = {
|
||||
{
|
||||
.tx_buf = &st->data[0].d8[1],
|
||||
.bits_per_word = 8,
|
||||
.len = 3,
|
||||
.cs_change = true,
|
||||
}, {
|
||||
.tx_buf = &st->data[1].d8[1],
|
||||
.rx_buf = &st->data[2].d8[1],
|
||||
.bits_per_word = 8,
|
||||
.len = 3,
|
||||
},
|
||||
};
|
||||
|
||||
st->data[0].d32 = cpu_to_be32(AD5761_ADDR(addr));
|
||||
st->data[1].d32 = cpu_to_be32(AD5761_ADDR(AD5761_ADDR_NOOP));
|
||||
|
||||
ret = spi_sync_transfer(st->spi, xfers, ARRAY_SIZE(xfers));
|
||||
|
||||
*val = be32_to_cpu(st->data[2].d32);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad5761_spi_read(struct iio_dev *indio_dev, u8 addr, u16 *val)
|
||||
{
|
||||
struct ad5761_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
ret = _ad5761_spi_read(st, addr, val);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad5761_spi_set_range(struct ad5761_state *st,
|
||||
enum ad5761_voltage_range range)
|
||||
{
|
||||
u16 aux;
|
||||
int ret;
|
||||
|
||||
aux = (range & 0x7) | AD5761_CTRL_ETS;
|
||||
|
||||
if (st->use_intref)
|
||||
aux |= AD5761_CTRL_USE_INTVREF;
|
||||
|
||||
ret = _ad5761_spi_write(st, AD5761_ADDR_SW_FULL_RESET, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = _ad5761_spi_write(st, AD5761_ADDR_CTRL_WRITE_REG, aux);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
st->range = range;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ad5761_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val,
|
||||
int *val2,
|
||||
long mask)
|
||||
{
|
||||
struct ad5761_state *st;
|
||||
int ret;
|
||||
u16 aux;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = ad5761_spi_read(indio_dev, AD5761_ADDR_DAC_READ, &aux);
|
||||
if (ret)
|
||||
return ret;
|
||||
*val = aux >> chan->scan_type.shift;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
st = iio_priv(indio_dev);
|
||||
*val = st->vref * ad5761_range_params[st->range].m;
|
||||
*val /= 10;
|
||||
*val2 = chan->scan_type.realbits;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
st = iio_priv(indio_dev);
|
||||
*val = -(1 << chan->scan_type.realbits);
|
||||
*val *= ad5761_range_params[st->range].c;
|
||||
*val /= ad5761_range_params[st->range].m;
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int ad5761_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val,
|
||||
int val2,
|
||||
long mask)
|
||||
{
|
||||
u16 aux;
|
||||
|
||||
if (mask != IIO_CHAN_INFO_RAW)
|
||||
return -EINVAL;
|
||||
|
||||
if (val2 || (val << chan->scan_type.shift) > 0xffff || val < 0)
|
||||
return -EINVAL;
|
||||
|
||||
aux = val << chan->scan_type.shift;
|
||||
|
||||
return ad5761_spi_write(indio_dev, AD5761_ADDR_DAC_WRITE, aux);
|
||||
}
|
||||
|
||||
static const struct iio_info ad5761_info = {
|
||||
.read_raw = &ad5761_read_raw,
|
||||
.write_raw = &ad5761_write_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
#define AD5761_CHAN(_bits) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.output = 1, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
|
||||
BIT(IIO_CHAN_INFO_OFFSET), \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = (_bits), \
|
||||
.storagebits = 16, \
|
||||
.shift = 16 - (_bits), \
|
||||
}, \
|
||||
}
|
||||
|
||||
static const struct ad5761_chip_info ad5761_chip_infos[] = {
|
||||
[ID_AD5721] = {
|
||||
.int_vref = 0,
|
||||
.channel = AD5761_CHAN(12),
|
||||
},
|
||||
[ID_AD5721R] = {
|
||||
.int_vref = 2500,
|
||||
.channel = AD5761_CHAN(12),
|
||||
},
|
||||
[ID_AD5761] = {
|
||||
.int_vref = 0,
|
||||
.channel = AD5761_CHAN(16),
|
||||
},
|
||||
[ID_AD5761R] = {
|
||||
.int_vref = 2500,
|
||||
.channel = AD5761_CHAN(16),
|
||||
},
|
||||
};
|
||||
|
||||
static int ad5761_get_vref(struct ad5761_state *st,
|
||||
const struct ad5761_chip_info *chip_info)
|
||||
{
|
||||
int ret;
|
||||
|
||||
st->vref_reg = devm_regulator_get_optional(&st->spi->dev, "vref");
|
||||
if (PTR_ERR(st->vref_reg) == -ENODEV) {
|
||||
/* Use Internal regulator */
|
||||
if (!chip_info->int_vref) {
|
||||
dev_err(&st->spi->dev,
|
||||
"Voltage reference not found\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
st->use_intref = true;
|
||||
st->vref = chip_info->int_vref;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (IS_ERR(st->vref_reg)) {
|
||||
dev_err(&st->spi->dev,
|
||||
"Error getting voltage reference regulator\n");
|
||||
return PTR_ERR(st->vref_reg);
|
||||
}
|
||||
|
||||
ret = regulator_enable(st->vref_reg);
|
||||
if (ret) {
|
||||
dev_err(&st->spi->dev,
|
||||
"Failed to enable voltage reference\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regulator_get_voltage(st->vref_reg);
|
||||
if (ret < 0) {
|
||||
dev_err(&st->spi->dev,
|
||||
"Failed to get voltage reference value\n");
|
||||
goto disable_regulator_vref;
|
||||
}
|
||||
|
||||
if (ret < 2000000 || ret > 3000000) {
|
||||
dev_warn(&st->spi->dev,
|
||||
"Invalid external voltage ref. value %d uV\n", ret);
|
||||
ret = -EIO;
|
||||
goto disable_regulator_vref;
|
||||
}
|
||||
|
||||
st->vref = ret / 1000;
|
||||
st->use_intref = false;
|
||||
|
||||
return 0;
|
||||
|
||||
disable_regulator_vref:
|
||||
regulator_disable(st->vref_reg);
|
||||
st->vref_reg = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad5761_probe(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *iio_dev;
|
||||
struct ad5761_state *st;
|
||||
int ret;
|
||||
const struct ad5761_chip_info *chip_info =
|
||||
&ad5761_chip_infos[spi_get_device_id(spi)->driver_data];
|
||||
enum ad5761_voltage_range voltage_range = AD5761_VOLTAGE_RANGE_0V_5V;
|
||||
struct ad5761_platform_data *pdata = dev_get_platdata(&spi->dev);
|
||||
|
||||
iio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
||||
if (!iio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
st = iio_priv(iio_dev);
|
||||
|
||||
st->spi = spi;
|
||||
spi_set_drvdata(spi, iio_dev);
|
||||
|
||||
ret = ad5761_get_vref(st, chip_info);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (pdata)
|
||||
voltage_range = pdata->voltage_range;
|
||||
|
||||
ret = ad5761_spi_set_range(st, voltage_range);
|
||||
if (ret)
|
||||
goto disable_regulator_err;
|
||||
|
||||
iio_dev->dev.parent = &spi->dev;
|
||||
iio_dev->info = &ad5761_info;
|
||||
iio_dev->modes = INDIO_DIRECT_MODE;
|
||||
iio_dev->channels = &chip_info->channel;
|
||||
iio_dev->num_channels = 1;
|
||||
iio_dev->name = spi_get_device_id(st->spi)->name;
|
||||
ret = iio_device_register(iio_dev);
|
||||
if (ret)
|
||||
goto disable_regulator_err;
|
||||
|
||||
return 0;
|
||||
|
||||
disable_regulator_err:
|
||||
if (!IS_ERR_OR_NULL(st->vref_reg))
|
||||
regulator_disable(st->vref_reg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad5761_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *iio_dev = spi_get_drvdata(spi);
|
||||
struct ad5761_state *st = iio_priv(iio_dev);
|
||||
|
||||
iio_device_unregister(iio_dev);
|
||||
|
||||
if (!IS_ERR_OR_NULL(st->vref_reg))
|
||||
regulator_disable(st->vref_reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id ad5761_id[] = {
|
||||
{"ad5721", ID_AD5721},
|
||||
{"ad5721r", ID_AD5721R},
|
||||
{"ad5761", ID_AD5761},
|
||||
{"ad5761r", ID_AD5761R},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ad5761_id);
|
||||
|
||||
static struct spi_driver ad5761_driver = {
|
||||
.driver = {
|
||||
.name = "ad5761",
|
||||
},
|
||||
.probe = ad5761_probe,
|
||||
.remove = ad5761_remove,
|
||||
.id_table = ad5761_id,
|
||||
};
|
||||
module_spi_driver(ad5761_driver);
|
||||
|
||||
MODULE_AUTHOR("Ricardo Ribalda <ricardo.ribalda@gmail.com>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD5721, AD5721R, AD5761, AD5761R driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* mcp4725.c - Support for Microchip MCP4725
|
||||
* mcp4725.c - Support for Microchip MCP4725/6
|
||||
*
|
||||
* Copyright (C) 2012 Peter Meerwald <pmeerw@pmeerw.net>
|
||||
*
|
||||
@ -134,6 +134,12 @@ static const char * const mcp4725_powerdown_modes[] = {
|
||||
"500kohm_to_gnd"
|
||||
};
|
||||
|
||||
static const char * const mcp4726_powerdown_modes[] = {
|
||||
"1kohm_to_gnd",
|
||||
"125kohm_to_gnd",
|
||||
"640kohm_to_gnd"
|
||||
};
|
||||
|
||||
static int mcp4725_get_powerdown_mode(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan)
|
||||
{
|
||||
@ -182,11 +188,24 @@ static ssize_t mcp4725_write_powerdown(struct iio_dev *indio_dev,
|
||||
return len;
|
||||
}
|
||||
|
||||
static const struct iio_enum mcp4725_powerdown_mode_enum = {
|
||||
.items = mcp4725_powerdown_modes,
|
||||
.num_items = ARRAY_SIZE(mcp4725_powerdown_modes),
|
||||
.get = mcp4725_get_powerdown_mode,
|
||||
.set = mcp4725_set_powerdown_mode,
|
||||
enum {
|
||||
MCP4725,
|
||||
MCP4726,
|
||||
};
|
||||
|
||||
static const struct iio_enum mcp472x_powerdown_mode_enum[] = {
|
||||
[MCP4725] = {
|
||||
.items = mcp4725_powerdown_modes,
|
||||
.num_items = ARRAY_SIZE(mcp4725_powerdown_modes),
|
||||
.get = mcp4725_get_powerdown_mode,
|
||||
.set = mcp4725_set_powerdown_mode,
|
||||
},
|
||||
[MCP4726] = {
|
||||
.items = mcp4726_powerdown_modes,
|
||||
.num_items = ARRAY_SIZE(mcp4726_powerdown_modes),
|
||||
.get = mcp4725_get_powerdown_mode,
|
||||
.set = mcp4725_set_powerdown_mode,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec_ext_info mcp4725_ext_info[] = {
|
||||
@ -196,19 +215,46 @@ static const struct iio_chan_spec_ext_info mcp4725_ext_info[] = {
|
||||
.write = mcp4725_write_powerdown,
|
||||
.shared = IIO_SEPARATE,
|
||||
},
|
||||
IIO_ENUM("powerdown_mode", IIO_SEPARATE, &mcp4725_powerdown_mode_enum),
|
||||
IIO_ENUM_AVAILABLE("powerdown_mode", &mcp4725_powerdown_mode_enum),
|
||||
IIO_ENUM("powerdown_mode", IIO_SEPARATE,
|
||||
&mcp472x_powerdown_mode_enum[MCP4725]),
|
||||
IIO_ENUM_AVAILABLE("powerdown_mode",
|
||||
&mcp472x_powerdown_mode_enum[MCP4725]),
|
||||
{ },
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec mcp4725_channel = {
|
||||
.type = IIO_VOLTAGE,
|
||||
.indexed = 1,
|
||||
.output = 1,
|
||||
.channel = 0,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
|
||||
.ext_info = mcp4725_ext_info,
|
||||
static const struct iio_chan_spec_ext_info mcp4726_ext_info[] = {
|
||||
{
|
||||
.name = "powerdown",
|
||||
.read = mcp4725_read_powerdown,
|
||||
.write = mcp4725_write_powerdown,
|
||||
.shared = IIO_SEPARATE,
|
||||
},
|
||||
IIO_ENUM("powerdown_mode", IIO_SEPARATE,
|
||||
&mcp472x_powerdown_mode_enum[MCP4726]),
|
||||
IIO_ENUM_AVAILABLE("powerdown_mode",
|
||||
&mcp472x_powerdown_mode_enum[MCP4726]),
|
||||
{ },
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec mcp472x_channel[] = {
|
||||
[MCP4725] = {
|
||||
.type = IIO_VOLTAGE,
|
||||
.indexed = 1,
|
||||
.output = 1,
|
||||
.channel = 0,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
|
||||
.ext_info = mcp4725_ext_info,
|
||||
},
|
||||
[MCP4726] = {
|
||||
.type = IIO_VOLTAGE,
|
||||
.indexed = 1,
|
||||
.output = 1,
|
||||
.channel = 0,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
|
||||
.ext_info = mcp4726_ext_info,
|
||||
},
|
||||
};
|
||||
|
||||
static int mcp4725_set_value(struct iio_dev *indio_dev, int val)
|
||||
@ -301,7 +347,7 @@ static int mcp4725_probe(struct i2c_client *client,
|
||||
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->info = &mcp4725_info;
|
||||
indio_dev->channels = &mcp4725_channel;
|
||||
indio_dev->channels = &mcp472x_channel[id->driver_data];
|
||||
indio_dev->num_channels = 1;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
@ -315,7 +361,7 @@ static int mcp4725_probe(struct i2c_client *client,
|
||||
}
|
||||
pd = (inbuf[0] >> 1) & 0x3;
|
||||
data->powerdown = pd > 0 ? true : false;
|
||||
data->powerdown_mode = pd ? pd-1 : 2; /* 500kohm_to_gnd */
|
||||
data->powerdown_mode = pd ? pd - 1 : 2; /* largest register to gnd */
|
||||
data->dac_value = (inbuf[1] << 4) | (inbuf[2] >> 4);
|
||||
|
||||
return iio_device_register(indio_dev);
|
||||
@ -328,7 +374,8 @@ static int mcp4725_remove(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id mcp4725_id[] = {
|
||||
{ "mcp4725", 0 },
|
||||
{ "mcp4725", MCP4725 },
|
||||
{ "mcp4726", MCP4726 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, mcp4725_id);
|
||||
@ -345,5 +392,5 @@ static struct i2c_driver mcp4725_driver = {
|
||||
module_i2c_driver(mcp4725_driver);
|
||||
|
||||
MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>");
|
||||
MODULE_DESCRIPTION("MCP4725 12-bit DAC");
|
||||
MODULE_DESCRIPTION("MCP4725/6 12-bit DAC");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -185,6 +185,11 @@ static const struct st_sensor_settings st_gyro_sensors_settings[] = {
|
||||
.drdy_irq = {
|
||||
.addr = ST_GYRO_1_DRDY_IRQ_ADDR,
|
||||
.mask_int2 = ST_GYRO_1_DRDY_IRQ_INT2_MASK,
|
||||
/*
|
||||
* The sensor has IHL (active low) and open
|
||||
* drain settings, but only for INT1 and not
|
||||
* for the DRDY line on INT2.
|
||||
*/
|
||||
},
|
||||
.multi_read_bit = ST_GYRO_1_MULTIREAD_BIT,
|
||||
.bootime = 2,
|
||||
@ -248,6 +253,11 @@ static const struct st_sensor_settings st_gyro_sensors_settings[] = {
|
||||
.drdy_irq = {
|
||||
.addr = ST_GYRO_2_DRDY_IRQ_ADDR,
|
||||
.mask_int2 = ST_GYRO_2_DRDY_IRQ_INT2_MASK,
|
||||
/*
|
||||
* The sensor has IHL (active low) and open
|
||||
* drain settings, but only for INT1 and not
|
||||
* for the DRDY line on INT2.
|
||||
*/
|
||||
},
|
||||
.multi_read_bit = ST_GYRO_2_MULTIREAD_BIT,
|
||||
.bootime = 2,
|
||||
@ -307,6 +317,11 @@ static const struct st_sensor_settings st_gyro_sensors_settings[] = {
|
||||
.drdy_irq = {
|
||||
.addr = ST_GYRO_3_DRDY_IRQ_ADDR,
|
||||
.mask_int2 = ST_GYRO_3_DRDY_IRQ_INT2_MASK,
|
||||
/*
|
||||
* The sensor has IHL (active low) and open
|
||||
* drain settings, but only for INT1 and not
|
||||
* for the DRDY line on INT2.
|
||||
*/
|
||||
},
|
||||
.multi_read_bit = ST_GYRO_3_MULTIREAD_BIT,
|
||||
.bootime = 2,
|
||||
|
@ -13,7 +13,7 @@
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* TODO: allow LED current and pulse length controls via device tree properties
|
||||
* TODO: enable pulse length controls via device tree properties
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
@ -24,6 +24,7 @@
|
||||
#include <linux/irq.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
@ -65,6 +66,7 @@
|
||||
#define MAX30100_REG_SPO2_CONFIG_1600US 0x3
|
||||
|
||||
#define MAX30100_REG_LED_CONFIG 0x09
|
||||
#define MAX30100_REG_LED_CONFIG_LED_MASK 0x0f
|
||||
#define MAX30100_REG_LED_CONFIG_RED_LED_SHIFT 4
|
||||
|
||||
#define MAX30100_REG_LED_CONFIG_24MA 0x07
|
||||
@ -111,6 +113,12 @@ static const struct regmap_config max30100_regmap_config = {
|
||||
.volatile_reg = max30100_is_volatile_reg,
|
||||
};
|
||||
|
||||
static const unsigned int max30100_led_current_mapping[] = {
|
||||
4400, 7600, 11000, 14200, 17400,
|
||||
20800, 24000, 27100, 30600, 33800,
|
||||
37000, 40200, 43600, 46800, 50000
|
||||
};
|
||||
|
||||
static const unsigned long max30100_scan_masks[] = {0x3, 0};
|
||||
|
||||
static const struct iio_chan_spec max30100_channels[] = {
|
||||
@ -243,15 +251,76 @@ static irqreturn_t max30100_interrupt_handler(int irq, void *private)
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int max30100_get_current_idx(unsigned int val, int *reg)
|
||||
{
|
||||
int idx;
|
||||
|
||||
/* LED turned off */
|
||||
if (val == 0) {
|
||||
*reg = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (idx = 0; idx < ARRAY_SIZE(max30100_led_current_mapping); idx++) {
|
||||
if (max30100_led_current_mapping[idx] == val) {
|
||||
*reg = idx + 1;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int max30100_led_init(struct max30100_data *data)
|
||||
{
|
||||
struct device *dev = &data->client->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
unsigned int val[2];
|
||||
int reg, ret;
|
||||
|
||||
ret = of_property_read_u32_array(np, "maxim,led-current-microamp",
|
||||
(unsigned int *) &val, 2);
|
||||
if (ret) {
|
||||
/* Default to 24 mA RED LED, 50 mA IR LED */
|
||||
reg = (MAX30100_REG_LED_CONFIG_24MA <<
|
||||
MAX30100_REG_LED_CONFIG_RED_LED_SHIFT) |
|
||||
MAX30100_REG_LED_CONFIG_50MA;
|
||||
dev_warn(dev, "no led-current-microamp set");
|
||||
|
||||
return regmap_write(data->regmap, MAX30100_REG_LED_CONFIG, reg);
|
||||
}
|
||||
|
||||
/* RED LED current */
|
||||
ret = max30100_get_current_idx(val[0], ®);
|
||||
if (ret) {
|
||||
dev_err(dev, "invalid RED current setting %d", val[0]);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regmap_update_bits(data->regmap, MAX30100_REG_LED_CONFIG,
|
||||
MAX30100_REG_LED_CONFIG_LED_MASK <<
|
||||
MAX30100_REG_LED_CONFIG_RED_LED_SHIFT,
|
||||
reg << MAX30100_REG_LED_CONFIG_RED_LED_SHIFT);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* IR LED current */
|
||||
ret = max30100_get_current_idx(val[1], ®);
|
||||
if (ret) {
|
||||
dev_err(dev, "invalid IR current setting %d", val[1]);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return regmap_update_bits(data->regmap, MAX30100_REG_LED_CONFIG,
|
||||
MAX30100_REG_LED_CONFIG_LED_MASK, reg);
|
||||
}
|
||||
|
||||
static int max30100_chip_init(struct max30100_data *data)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* RED IR LED = 24mA, IR LED = 50mA */
|
||||
ret = regmap_write(data->regmap, MAX30100_REG_LED_CONFIG,
|
||||
(MAX30100_REG_LED_CONFIG_24MA <<
|
||||
MAX30100_REG_LED_CONFIG_RED_LED_SHIFT) |
|
||||
MAX30100_REG_LED_CONFIG_50MA);
|
||||
/* setup LED current settings */
|
||||
ret = max30100_led_init(data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -43,14 +43,16 @@ config SI7005
|
||||
humidity and temperature sensor.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called si7005.
|
||||
will be called si7005. This driver also
|
||||
supports Hoperf TH02 Humidity and Temperature Sensor.
|
||||
|
||||
config SI7020
|
||||
tristate "Si7013/20/21 Relative Humidity and Temperature Sensors"
|
||||
depends on I2C
|
||||
help
|
||||
Say yes here to build support for the Silicon Labs Si7013/20/21
|
||||
Relative Humidity and Temperature Sensors.
|
||||
Relative Humidity and Temperature Sensors. This driver also
|
||||
supports Hoperf TH06 Humidity and Temperature Sensor.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called si7020.
|
||||
|
@ -50,12 +50,32 @@
|
||||
#define DHT11_EDGES_PER_READ (2 * DHT11_BITS_PER_READ + \
|
||||
DHT11_EDGES_PREAMBLE + 1)
|
||||
|
||||
/* Data transmission timing (nano seconds) */
|
||||
/*
|
||||
* Data transmission timing:
|
||||
* Data bits are encoded as pulse length (high time) on the data line.
|
||||
* 0-bit: 22-30uS -- typically 26uS (AM2302)
|
||||
* 1-bit: 68-75uS -- typically 70uS (AM2302)
|
||||
* The acutal timings also depend on the properties of the cable, with
|
||||
* longer cables typically making pulses shorter.
|
||||
*
|
||||
* Our decoding depends on the time resolution of the system:
|
||||
* timeres > 34uS ... don't know what a 1-tick pulse is
|
||||
* 34uS > timeres > 30uS ... no problem (30kHz and 32kHz clocks)
|
||||
* 30uS > timeres > 23uS ... don't know what a 2-tick pulse is
|
||||
* timeres < 23uS ... no problem
|
||||
*
|
||||
* Luckily clocks in the 33-44kHz range are quite uncommon, so we can
|
||||
* support most systems if the threshold for decoding a pulse as 1-bit
|
||||
* is chosen carefully. If somebody really wants to support clocks around
|
||||
* 40kHz, where this driver is most unreliable, there are two options.
|
||||
* a) select an implementation using busy loop polling on those systems
|
||||
* b) use the checksum to do some probabilistic decoding
|
||||
*/
|
||||
#define DHT11_START_TRANSMISSION 18 /* ms */
|
||||
#define DHT11_SENSOR_RESPONSE 80000
|
||||
#define DHT11_START_BIT 50000
|
||||
#define DHT11_DATA_BIT_LOW 27000
|
||||
#define DHT11_DATA_BIT_HIGH 70000
|
||||
#define DHT11_MIN_TIMERES 34000 /* ns */
|
||||
#define DHT11_THRESHOLD 49000 /* ns */
|
||||
#define DHT11_AMBIG_LOW 23000 /* ns */
|
||||
#define DHT11_AMBIG_HIGH 30000 /* ns */
|
||||
|
||||
struct dht11 {
|
||||
struct device *dev;
|
||||
@ -76,43 +96,39 @@ struct dht11 {
|
||||
struct {s64 ts; int value; } edges[DHT11_EDGES_PER_READ];
|
||||
};
|
||||
|
||||
static unsigned char dht11_decode_byte(int *timing, int threshold)
|
||||
static unsigned char dht11_decode_byte(char *bits)
|
||||
{
|
||||
unsigned char ret = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 8; ++i) {
|
||||
ret <<= 1;
|
||||
if (timing[i] >= threshold)
|
||||
if (bits[i])
|
||||
++ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dht11_decode(struct dht11 *dht11, int offset, int timeres)
|
||||
static int dht11_decode(struct dht11 *dht11, int offset)
|
||||
{
|
||||
int i, t, timing[DHT11_BITS_PER_READ], threshold;
|
||||
int i, t;
|
||||
char bits[DHT11_BITS_PER_READ];
|
||||
unsigned char temp_int, temp_dec, hum_int, hum_dec, checksum;
|
||||
|
||||
threshold = DHT11_DATA_BIT_HIGH / timeres;
|
||||
if (DHT11_DATA_BIT_LOW / timeres + 1 >= threshold)
|
||||
pr_err("dht11: WARNING: decoding ambiguous\n");
|
||||
|
||||
/* scale down with timeres and check validity */
|
||||
for (i = 0; i < DHT11_BITS_PER_READ; ++i) {
|
||||
t = dht11->edges[offset + 2 * i + 2].ts -
|
||||
dht11->edges[offset + 2 * i + 1].ts;
|
||||
if (!dht11->edges[offset + 2 * i + 1].value)
|
||||
return -EIO; /* lost synchronisation */
|
||||
timing[i] = t / timeres;
|
||||
bits[i] = t > DHT11_THRESHOLD;
|
||||
}
|
||||
|
||||
hum_int = dht11_decode_byte(timing, threshold);
|
||||
hum_dec = dht11_decode_byte(&timing[8], threshold);
|
||||
temp_int = dht11_decode_byte(&timing[16], threshold);
|
||||
temp_dec = dht11_decode_byte(&timing[24], threshold);
|
||||
checksum = dht11_decode_byte(&timing[32], threshold);
|
||||
hum_int = dht11_decode_byte(bits);
|
||||
hum_dec = dht11_decode_byte(&bits[8]);
|
||||
temp_int = dht11_decode_byte(&bits[16]);
|
||||
temp_dec = dht11_decode_byte(&bits[24]);
|
||||
checksum = dht11_decode_byte(&bits[32]);
|
||||
|
||||
if (((hum_int + hum_dec + temp_int + temp_dec) & 0xff) != checksum)
|
||||
return -EIO;
|
||||
@ -161,12 +177,12 @@ static int dht11_read_raw(struct iio_dev *iio_dev,
|
||||
int *val, int *val2, long m)
|
||||
{
|
||||
struct dht11 *dht11 = iio_priv(iio_dev);
|
||||
int ret, timeres;
|
||||
int ret, timeres, offset;
|
||||
|
||||
mutex_lock(&dht11->lock);
|
||||
if (dht11->timestamp + DHT11_DATA_VALID_TIME < ktime_get_real_ns()) {
|
||||
timeres = ktime_get_resolution_ns();
|
||||
if (DHT11_DATA_BIT_HIGH < 2 * timeres) {
|
||||
if (timeres > DHT11_MIN_TIMERES) {
|
||||
dev_err(dht11->dev, "timeresolution %dns too low\n",
|
||||
timeres);
|
||||
/* In theory a better clock could become available
|
||||
@ -176,6 +192,10 @@ static int dht11_read_raw(struct iio_dev *iio_dev,
|
||||
ret = -EAGAIN;
|
||||
goto err;
|
||||
}
|
||||
if (timeres > DHT11_AMBIG_LOW && timeres < DHT11_AMBIG_HIGH)
|
||||
dev_warn(dht11->dev,
|
||||
"timeresolution: %dns - decoding ambiguous\n",
|
||||
timeres);
|
||||
|
||||
reinit_completion(&dht11->completion);
|
||||
|
||||
@ -208,11 +228,14 @@ static int dht11_read_raw(struct iio_dev *iio_dev,
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
ret = dht11_decode(dht11,
|
||||
dht11->num_edges == DHT11_EDGES_PER_READ ?
|
||||
DHT11_EDGES_PREAMBLE :
|
||||
DHT11_EDGES_PREAMBLE - 2,
|
||||
timeres);
|
||||
offset = DHT11_EDGES_PREAMBLE +
|
||||
dht11->num_edges - DHT11_EDGES_PER_READ;
|
||||
for (; offset >= 0; --offset) {
|
||||
ret = dht11_decode(dht11, offset);
|
||||
if (!ret)
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret)
|
||||
goto err;
|
||||
}
|
||||
|
@ -170,6 +170,7 @@ static int si7005_probe(struct i2c_client *client,
|
||||
|
||||
static const struct i2c_device_id si7005_id[] = {
|
||||
{ "si7005", 0 },
|
||||
{ "th02", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, si7005_id);
|
||||
|
@ -149,6 +149,7 @@ static int si7020_probe(struct i2c_client *client,
|
||||
|
||||
static const struct i2c_device_id si7020_id[] = {
|
||||
{ "si7020", 0 },
|
||||
{ "th06", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, si7020_id);
|
||||
|
@ -727,8 +727,7 @@ static const struct iio_info mpu_info = {
|
||||
/**
|
||||
* inv_check_and_setup_chip() - check and setup chip.
|
||||
*/
|
||||
static int inv_check_and_setup_chip(struct inv_mpu6050_state *st,
|
||||
const struct i2c_device_id *id)
|
||||
static int inv_check_and_setup_chip(struct inv_mpu6050_state *st)
|
||||
{
|
||||
int result;
|
||||
|
||||
@ -795,7 +794,7 @@ static int inv_mpu_probe(struct i2c_client *client,
|
||||
if (pdata)
|
||||
st->plat_data = *pdata;
|
||||
/* power is turned on inside check chip type*/
|
||||
result = inv_check_and_setup_chip(st, id);
|
||||
result = inv_check_and_setup_chip(st);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
|
@ -77,6 +77,7 @@ static const char * const iio_chan_type_name_spec[] = {
|
||||
[IIO_VELOCITY] = "velocity",
|
||||
[IIO_CONCENTRATION] = "concentration",
|
||||
[IIO_RESISTANCE] = "resistance",
|
||||
[IIO_PH] = "ph",
|
||||
};
|
||||
|
||||
static const char * const iio_modifier_names[] = {
|
||||
|
@ -65,19 +65,25 @@
|
||||
#define OPT3001_REG_EXPONENT(n) ((n) >> 12)
|
||||
#define OPT3001_REG_MANTISSA(n) ((n) & 0xfff)
|
||||
|
||||
#define OPT3001_INT_TIME_LONG 800000
|
||||
#define OPT3001_INT_TIME_SHORT 100000
|
||||
|
||||
/*
|
||||
* Time to wait for conversion result to be ready. The device datasheet
|
||||
* worst-case max value is 880ms. Add some slack to be on the safe side.
|
||||
* sect. 6.5 states results are ready after total integration time plus 3ms.
|
||||
* This results in worst-case max values of 113ms or 883ms, respectively.
|
||||
* Add some slack to be on the safe side.
|
||||
*/
|
||||
#define OPT3001_RESULT_READY_TIMEOUT msecs_to_jiffies(1000)
|
||||
#define OPT3001_RESULT_READY_SHORT 150
|
||||
#define OPT3001_RESULT_READY_LONG 1000
|
||||
|
||||
struct opt3001 {
|
||||
struct i2c_client *client;
|
||||
struct device *dev;
|
||||
|
||||
struct mutex lock;
|
||||
u16 ok_to_ignore_lock:1;
|
||||
u16 result_ready:1;
|
||||
bool ok_to_ignore_lock;
|
||||
bool result_ready;
|
||||
wait_queue_head_t result_ready_queue;
|
||||
u16 result;
|
||||
|
||||
@ -89,6 +95,8 @@ struct opt3001 {
|
||||
|
||||
u8 high_thresh_exp;
|
||||
u8 low_thresh_exp;
|
||||
|
||||
bool use_irq;
|
||||
};
|
||||
|
||||
struct opt3001_scale {
|
||||
@ -227,26 +235,30 @@ static int opt3001_get_lux(struct opt3001 *opt, int *val, int *val2)
|
||||
u16 reg;
|
||||
u8 exponent;
|
||||
u16 value;
|
||||
long timeout;
|
||||
|
||||
/*
|
||||
* Enable the end-of-conversion interrupt mechanism. Note that doing
|
||||
* so will overwrite the low-level limit value however we will restore
|
||||
* this value later on.
|
||||
*/
|
||||
ret = i2c_smbus_write_word_swapped(opt->client, OPT3001_LOW_LIMIT,
|
||||
OPT3001_LOW_LIMIT_EOC_ENABLE);
|
||||
if (ret < 0) {
|
||||
dev_err(opt->dev, "failed to write register %02x\n",
|
||||
OPT3001_LOW_LIMIT);
|
||||
return ret;
|
||||
if (opt->use_irq) {
|
||||
/*
|
||||
* Enable the end-of-conversion interrupt mechanism. Note that
|
||||
* doing so will overwrite the low-level limit value however we
|
||||
* will restore this value later on.
|
||||
*/
|
||||
ret = i2c_smbus_write_word_swapped(opt->client,
|
||||
OPT3001_LOW_LIMIT,
|
||||
OPT3001_LOW_LIMIT_EOC_ENABLE);
|
||||
if (ret < 0) {
|
||||
dev_err(opt->dev, "failed to write register %02x\n",
|
||||
OPT3001_LOW_LIMIT);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Allow IRQ to access the device despite lock being set */
|
||||
opt->ok_to_ignore_lock = true;
|
||||
}
|
||||
|
||||
/* Reset data-ready indicator flag (will be set in the IRQ routine) */
|
||||
/* Reset data-ready indicator flag */
|
||||
opt->result_ready = false;
|
||||
|
||||
/* Allow IRQ to access the device despite lock being set */
|
||||
opt->ok_to_ignore_lock = true;
|
||||
|
||||
/* Configure for single-conversion mode and start a new conversion */
|
||||
ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_CONFIGURATION);
|
||||
if (ret < 0) {
|
||||
@ -266,32 +278,69 @@ static int opt3001_get_lux(struct opt3001 *opt, int *val, int *val2)
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Wait for the IRQ to indicate the conversion is complete */
|
||||
ret = wait_event_timeout(opt->result_ready_queue, opt->result_ready,
|
||||
OPT3001_RESULT_READY_TIMEOUT);
|
||||
if (opt->use_irq) {
|
||||
/* Wait for the IRQ to indicate the conversion is complete */
|
||||
ret = wait_event_timeout(opt->result_ready_queue,
|
||||
opt->result_ready,
|
||||
msecs_to_jiffies(OPT3001_RESULT_READY_LONG));
|
||||
} else {
|
||||
/* Sleep for result ready time */
|
||||
timeout = (opt->int_time == OPT3001_INT_TIME_SHORT) ?
|
||||
OPT3001_RESULT_READY_SHORT : OPT3001_RESULT_READY_LONG;
|
||||
msleep(timeout);
|
||||
|
||||
/* Check result ready flag */
|
||||
ret = i2c_smbus_read_word_swapped(opt->client,
|
||||
OPT3001_CONFIGURATION);
|
||||
if (ret < 0) {
|
||||
dev_err(opt->dev, "failed to read register %02x\n",
|
||||
OPT3001_CONFIGURATION);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (!(ret & OPT3001_CONFIGURATION_CRF)) {
|
||||
ret = -ETIMEDOUT;
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Obtain value */
|
||||
ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_RESULT);
|
||||
if (ret < 0) {
|
||||
dev_err(opt->dev, "failed to read register %02x\n",
|
||||
OPT3001_RESULT);
|
||||
goto err;
|
||||
}
|
||||
opt->result = ret;
|
||||
opt->result_ready = true;
|
||||
}
|
||||
|
||||
err:
|
||||
/* Disallow IRQ to access the device while lock is active */
|
||||
opt->ok_to_ignore_lock = false;
|
||||
if (opt->use_irq)
|
||||
/* Disallow IRQ to access the device while lock is active */
|
||||
opt->ok_to_ignore_lock = false;
|
||||
|
||||
if (ret == 0)
|
||||
return -ETIMEDOUT;
|
||||
else if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Disable the end-of-conversion interrupt mechanism by restoring the
|
||||
* low-level limit value (clearing OPT3001_LOW_LIMIT_EOC_ENABLE). Note
|
||||
* that selectively clearing those enable bits would affect the actual
|
||||
* limit value due to bit-overlap and therefore can't be done.
|
||||
*/
|
||||
value = (opt->low_thresh_exp << 12) | opt->low_thresh_mantissa;
|
||||
ret = i2c_smbus_write_word_swapped(opt->client, OPT3001_LOW_LIMIT,
|
||||
value);
|
||||
if (ret < 0) {
|
||||
dev_err(opt->dev, "failed to write register %02x\n",
|
||||
OPT3001_LOW_LIMIT);
|
||||
return ret;
|
||||
if (opt->use_irq) {
|
||||
/*
|
||||
* Disable the end-of-conversion interrupt mechanism by
|
||||
* restoring the low-level limit value (clearing
|
||||
* OPT3001_LOW_LIMIT_EOC_ENABLE). Note that selectively clearing
|
||||
* those enable bits would affect the actual limit value due to
|
||||
* bit-overlap and therefore can't be done.
|
||||
*/
|
||||
value = (opt->low_thresh_exp << 12) | opt->low_thresh_mantissa;
|
||||
ret = i2c_smbus_write_word_swapped(opt->client,
|
||||
OPT3001_LOW_LIMIT,
|
||||
value);
|
||||
if (ret < 0) {
|
||||
dev_err(opt->dev, "failed to write register %02x\n",
|
||||
OPT3001_LOW_LIMIT);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
exponent = OPT3001_REG_EXPONENT(opt->result);
|
||||
@ -325,13 +374,13 @@ static int opt3001_set_int_time(struct opt3001 *opt, int time)
|
||||
reg = ret;
|
||||
|
||||
switch (time) {
|
||||
case 100000:
|
||||
case OPT3001_INT_TIME_SHORT:
|
||||
reg &= ~OPT3001_CONFIGURATION_CT;
|
||||
opt->int_time = 100000;
|
||||
opt->int_time = OPT3001_INT_TIME_SHORT;
|
||||
break;
|
||||
case 800000:
|
||||
case OPT3001_INT_TIME_LONG:
|
||||
reg |= OPT3001_CONFIGURATION_CT;
|
||||
opt->int_time = 800000;
|
||||
opt->int_time = OPT3001_INT_TIME_LONG;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
@ -597,9 +646,9 @@ static int opt3001_configure(struct opt3001 *opt)
|
||||
|
||||
/* Reflect status of the device's integration time setting */
|
||||
if (reg & OPT3001_CONFIGURATION_CT)
|
||||
opt->int_time = 800000;
|
||||
opt->int_time = OPT3001_INT_TIME_LONG;
|
||||
else
|
||||
opt->int_time = 100000;
|
||||
opt->int_time = OPT3001_INT_TIME_SHORT;
|
||||
|
||||
/* Ensure device is in shutdown initially */
|
||||
opt3001_set_mode(opt, ®, OPT3001_CONFIGURATION_M_SHUTDOWN);
|
||||
@ -733,12 +782,18 @@ static int opt3001_probe(struct i2c_client *client,
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = request_threaded_irq(irq, NULL, opt3001_irq,
|
||||
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
||||
"opt3001", iio);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to request IRQ #%d\n", irq);
|
||||
return ret;
|
||||
/* Make use of INT pin only if valid IRQ no. is given */
|
||||
if (irq > 0) {
|
||||
ret = request_threaded_irq(irq, NULL, opt3001_irq,
|
||||
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
||||
"opt3001", iio);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to request IRQ #%d\n", irq);
|
||||
return ret;
|
||||
}
|
||||
opt->use_irq = true;
|
||||
} else {
|
||||
dev_dbg(opt->dev, "enabling interrupt-less operation\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -751,7 +806,8 @@ static int opt3001_remove(struct i2c_client *client)
|
||||
int ret;
|
||||
u16 reg;
|
||||
|
||||
free_irq(client->irq, iio);
|
||||
if (opt->use_irq)
|
||||
free_irq(client->irq, iio);
|
||||
|
||||
ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_CONFIGURATION);
|
||||
if (ret < 0) {
|
||||
|
@ -252,7 +252,7 @@ struct ak_def {
|
||||
u8 data_regs[3];
|
||||
};
|
||||
|
||||
static struct ak_def ak_def_array[AK_MAX_TYPE] = {
|
||||
static const struct ak_def ak_def_array[AK_MAX_TYPE] = {
|
||||
{
|
||||
.type = AK8975,
|
||||
.raw_to_gauss = ak8975_raw_to_gauss,
|
||||
@ -360,7 +360,7 @@ static struct ak_def ak_def_array[AK_MAX_TYPE] = {
|
||||
*/
|
||||
struct ak8975_data {
|
||||
struct i2c_client *client;
|
||||
struct ak_def *def;
|
||||
const struct ak_def *def;
|
||||
struct attribute_group attrs;
|
||||
struct mutex lock;
|
||||
u8 asa[3];
|
||||
|
@ -175,6 +175,8 @@
|
||||
#define ST_MAGN_3_BDU_MASK 0x10
|
||||
#define ST_MAGN_3_DRDY_IRQ_ADDR 0x62
|
||||
#define ST_MAGN_3_DRDY_INT_MASK 0x01
|
||||
#define ST_MAGN_3_IHL_IRQ_ADDR 0x63
|
||||
#define ST_MAGN_3_IHL_IRQ_MASK 0x04
|
||||
#define ST_MAGN_3_FS_AVL_15000_GAIN 1500
|
||||
#define ST_MAGN_3_MULTIREAD_BIT false
|
||||
#define ST_MAGN_3_OUT_X_L_ADDR 0x68
|
||||
@ -480,6 +482,8 @@ static const struct st_sensor_settings st_magn_sensors_settings[] = {
|
||||
.drdy_irq = {
|
||||
.addr = ST_MAGN_3_DRDY_IRQ_ADDR,
|
||||
.mask_int1 = ST_MAGN_3_DRDY_INT_MASK,
|
||||
.addr_ihl = ST_MAGN_3_IHL_IRQ_ADDR,
|
||||
.mask_ihl = ST_MAGN_3_IHL_IRQ_MASK,
|
||||
},
|
||||
.multi_read_bit = ST_MAGN_3_MULTIREAD_BIT,
|
||||
.bootime = 2,
|
||||
|
@ -10,11 +10,11 @@ config BMP280
|
||||
depends on I2C
|
||||
select REGMAP_I2C
|
||||
help
|
||||
Say yes here to build support for Bosch Sensortec BMP280
|
||||
pressure and temperature sensor.
|
||||
Say yes here to build support for Bosch Sensortec BMP280
|
||||
pressure and temperature sensor.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called bmp280.
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called bmp280.
|
||||
|
||||
config HID_SENSOR_PRESS
|
||||
depends on HID_SENSOR_HUB
|
||||
@ -27,18 +27,33 @@ config HID_SENSOR_PRESS
|
||||
Say yes here to build support for the HID SENSOR
|
||||
Pressure driver
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called hid-sensor-press.
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called hid-sensor-press.
|
||||
|
||||
config MPL115
|
||||
tristate
|
||||
|
||||
config MPL115_I2C
|
||||
tristate "Freescale MPL115A2 pressure sensor driver"
|
||||
depends on I2C
|
||||
select MPL115
|
||||
help
|
||||
Say yes here to build support for the Freescale MPL115A2
|
||||
pressure sensor connected via I2C.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called mpl115.
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called mpl115_i2c.
|
||||
|
||||
config MPL115_SPI
|
||||
tristate "Freescale MPL115A1 pressure sensor driver"
|
||||
depends on SPI_MASTER
|
||||
select MPL115
|
||||
help
|
||||
Say yes here to build support for the Freescale MPL115A1
|
||||
pressure sensor connected via SPI.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called mpl115_spi.
|
||||
|
||||
config MPL3115
|
||||
tristate "Freescale MPL3115A2 pressure sensor driver"
|
||||
@ -49,8 +64,8 @@ config MPL3115
|
||||
Say yes here to build support for the Freescale MPL3115A2
|
||||
pressure sensor / altimeter.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called mpl3115.
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called mpl3115.
|
||||
|
||||
config MS5611
|
||||
tristate "Measurement Specialties MS5611 pressure sensor driver"
|
||||
@ -82,7 +97,7 @@ config MS5611_SPI
|
||||
config MS5637
|
||||
tristate "Measurement Specialties MS5637 pressure & temperature sensor"
|
||||
depends on I2C
|
||||
select IIO_MS_SENSORS_I2C
|
||||
select IIO_MS_SENSORS_I2C
|
||||
help
|
||||
If you say yes here you get support for the Measurement Specialties
|
||||
MS5637 pressure and temperature sensor.
|
||||
@ -128,7 +143,7 @@ config T5403
|
||||
Say yes here to build support for the EPCOS T5403 pressure sensor
|
||||
connected via I2C.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called t5403.
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called t5403.
|
||||
|
||||
endmenu
|
||||
|
@ -6,6 +6,8 @@
|
||||
obj-$(CONFIG_BMP280) += bmp280.o
|
||||
obj-$(CONFIG_HID_SENSOR_PRESS) += hid-sensor-press.o
|
||||
obj-$(CONFIG_MPL115) += mpl115.o
|
||||
obj-$(CONFIG_MPL115_I2C) += mpl115_i2c.o
|
||||
obj-$(CONFIG_MPL115_SPI) += mpl115_spi.o
|
||||
obj-$(CONFIG_MPL3115) += mpl3115.o
|
||||
obj-$(CONFIG_MS5611) += ms5611_core.o
|
||||
obj-$(CONFIG_MS5611_I2C) += ms5611_i2c.o
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* mpl115.c - Support for Freescale MPL115A2 pressure/temperature sensor
|
||||
* mpl115.c - Support for Freescale MPL115A pressure/temperature sensor
|
||||
*
|
||||
* Copyright (c) 2014 Peter Meerwald <pmeerw@pmeerw.net>
|
||||
*
|
||||
@ -7,17 +7,16 @@
|
||||
* the GNU General Public License. See the file COPYING in the main
|
||||
* directory of this archive for more details.
|
||||
*
|
||||
* (7-bit I2C slave address 0x60)
|
||||
*
|
||||
* TODO: shutdown pin
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include "mpl115.h"
|
||||
|
||||
#define MPL115_PADC 0x00 /* pressure ADC output value, MSB first, 10 bit */
|
||||
#define MPL115_TADC 0x02 /* temperature ADC output value, MSB first, 10 bit */
|
||||
#define MPL115_A0 0x04 /* 12 bit integer, 3 bit fraction */
|
||||
@ -27,16 +26,18 @@
|
||||
#define MPL115_CONVERT 0x12 /* convert temperature and pressure */
|
||||
|
||||
struct mpl115_data {
|
||||
struct i2c_client *client;
|
||||
struct device *dev;
|
||||
struct mutex lock;
|
||||
s16 a0;
|
||||
s16 b1, b2;
|
||||
s16 c12;
|
||||
const struct mpl115_ops *ops;
|
||||
};
|
||||
|
||||
static int mpl115_request(struct mpl115_data *data)
|
||||
{
|
||||
int ret = i2c_smbus_write_byte_data(data->client, MPL115_CONVERT, 0);
|
||||
int ret = data->ops->write(data->dev, MPL115_CONVERT, 0);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@ -57,12 +58,12 @@ static int mpl115_comp_pressure(struct mpl115_data *data, int *val, int *val2)
|
||||
if (ret < 0)
|
||||
goto done;
|
||||
|
||||
ret = i2c_smbus_read_word_swapped(data->client, MPL115_PADC);
|
||||
ret = data->ops->read(data->dev, MPL115_PADC);
|
||||
if (ret < 0)
|
||||
goto done;
|
||||
padc = ret >> 6;
|
||||
|
||||
ret = i2c_smbus_read_word_swapped(data->client, MPL115_TADC);
|
||||
ret = data->ops->read(data->dev, MPL115_TADC);
|
||||
if (ret < 0)
|
||||
goto done;
|
||||
tadc = ret >> 6;
|
||||
@ -90,7 +91,7 @@ static int mpl115_read_temp(struct mpl115_data *data)
|
||||
ret = mpl115_request(data);
|
||||
if (ret < 0)
|
||||
goto done;
|
||||
ret = i2c_smbus_read_word_swapped(data->client, MPL115_TADC);
|
||||
ret = data->ops->read(data->dev, MPL115_TADC);
|
||||
done:
|
||||
mutex_unlock(&data->lock);
|
||||
return ret;
|
||||
@ -145,66 +146,53 @@ static const struct iio_info mpl115_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int mpl115_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
int mpl115_probe(struct device *dev, const char *name,
|
||||
const struct mpl115_ops *ops)
|
||||
{
|
||||
struct mpl115_data *data;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA))
|
||||
return -ENODEV;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
||||
indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
data = iio_priv(indio_dev);
|
||||
data->client = client;
|
||||
data->dev = dev;
|
||||
data->ops = ops;
|
||||
mutex_init(&data->lock);
|
||||
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
indio_dev->info = &mpl115_info;
|
||||
indio_dev->name = id->name;
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->name = name;
|
||||
indio_dev->dev.parent = dev;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = mpl115_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(mpl115_channels);
|
||||
|
||||
ret = i2c_smbus_read_word_swapped(data->client, MPL115_A0);
|
||||
ret = data->ops->init(data->dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = data->ops->read(data->dev, MPL115_A0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
data->a0 = ret;
|
||||
ret = i2c_smbus_read_word_swapped(data->client, MPL115_B1);
|
||||
ret = data->ops->read(data->dev, MPL115_B1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
data->b1 = ret;
|
||||
ret = i2c_smbus_read_word_swapped(data->client, MPL115_B2);
|
||||
ret = data->ops->read(data->dev, MPL115_B2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
data->b2 = ret;
|
||||
ret = i2c_smbus_read_word_swapped(data->client, MPL115_C12);
|
||||
ret = data->ops->read(data->dev, MPL115_C12);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
data->c12 = ret;
|
||||
|
||||
return devm_iio_device_register(&client->dev, indio_dev);
|
||||
return devm_iio_device_register(dev, indio_dev);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id mpl115_id[] = {
|
||||
{ "mpl115", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, mpl115_id);
|
||||
|
||||
static struct i2c_driver mpl115_driver = {
|
||||
.driver = {
|
||||
.name = "mpl115",
|
||||
},
|
||||
.probe = mpl115_probe,
|
||||
.id_table = mpl115_id,
|
||||
};
|
||||
module_i2c_driver(mpl115_driver);
|
||||
EXPORT_SYMBOL_GPL(mpl115_probe);
|
||||
|
||||
MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>");
|
||||
MODULE_DESCRIPTION("Freescale MPL115 pressure/temperature driver");
|
||||
|
24
drivers/iio/pressure/mpl115.h
Normal file
24
drivers/iio/pressure/mpl115.h
Normal file
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Freescale MPL115A pressure/temperature sensor
|
||||
*
|
||||
* Copyright (c) 2014 Peter Meerwald <pmeerw@pmeerw.net>
|
||||
* Copyright (c) 2016 Akinobu Mita <akinobu.mita@gmail.com>
|
||||
*
|
||||
* This file is subject to the terms and conditions of version 2 of
|
||||
* the GNU General Public License. See the file COPYING in the main
|
||||
* directory of this archive for more details.
|
||||
*/
|
||||
|
||||
#ifndef _MPL115_H_
|
||||
#define _MPL115_H_
|
||||
|
||||
struct mpl115_ops {
|
||||
int (*init)(struct device *);
|
||||
int (*read)(struct device *, u8);
|
||||
int (*write)(struct device *, u8, u8);
|
||||
};
|
||||
|
||||
int mpl115_probe(struct device *dev, const char *name,
|
||||
const struct mpl115_ops *ops);
|
||||
|
||||
#endif
|
67
drivers/iio/pressure/mpl115_i2c.c
Normal file
67
drivers/iio/pressure/mpl115_i2c.c
Normal file
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Freescale MPL115A2 pressure/temperature sensor
|
||||
*
|
||||
* Copyright (c) 2014 Peter Meerwald <pmeerw@pmeerw.net>
|
||||
*
|
||||
* This file is subject to the terms and conditions of version 2 of
|
||||
* the GNU General Public License. See the file COPYING in the main
|
||||
* directory of this archive for more details.
|
||||
*
|
||||
* (7-bit I2C slave address 0x60)
|
||||
*
|
||||
* Datasheet: http://www.nxp.com/files/sensors/doc/data_sheet/MPL115A2.pdf
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
|
||||
#include "mpl115.h"
|
||||
|
||||
static int mpl115_i2c_init(struct device *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mpl115_i2c_read(struct device *dev, u8 address)
|
||||
{
|
||||
return i2c_smbus_read_word_swapped(to_i2c_client(dev), address);
|
||||
}
|
||||
|
||||
static int mpl115_i2c_write(struct device *dev, u8 address, u8 value)
|
||||
{
|
||||
return i2c_smbus_write_byte_data(to_i2c_client(dev), address, value);
|
||||
}
|
||||
|
||||
static const struct mpl115_ops mpl115_i2c_ops = {
|
||||
.init = mpl115_i2c_init,
|
||||
.read = mpl115_i2c_read,
|
||||
.write = mpl115_i2c_write,
|
||||
};
|
||||
|
||||
static int mpl115_i2c_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA))
|
||||
return -ENODEV;
|
||||
|
||||
return mpl115_probe(&client->dev, id->name, &mpl115_i2c_ops);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id mpl115_i2c_id[] = {
|
||||
{ "mpl115", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, mpl115_i2c_id);
|
||||
|
||||
static struct i2c_driver mpl115_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "mpl115",
|
||||
},
|
||||
.probe = mpl115_i2c_probe,
|
||||
.id_table = mpl115_i2c_id,
|
||||
};
|
||||
module_i2c_driver(mpl115_i2c_driver);
|
||||
|
||||
MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>");
|
||||
MODULE_DESCRIPTION("Freescale MPL115A2 pressure/temperature driver");
|
||||
MODULE_LICENSE("GPL");
|
106
drivers/iio/pressure/mpl115_spi.c
Normal file
106
drivers/iio/pressure/mpl115_spi.c
Normal file
@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Freescale MPL115A1 pressure/temperature sensor
|
||||
*
|
||||
* Copyright (c) 2016 Akinobu Mita <akinobu.mita@gmail.com>
|
||||
*
|
||||
* This file is subject to the terms and conditions of version 2 of
|
||||
* the GNU General Public License. See the file COPYING in the main
|
||||
* directory of this archive for more details.
|
||||
*
|
||||
* Datasheet: http://www.nxp.com/files/sensors/doc/data_sheet/MPL115A1.pdf
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#include "mpl115.h"
|
||||
|
||||
#define MPL115_SPI_WRITE(address) ((address) << 1)
|
||||
#define MPL115_SPI_READ(address) (0x80 | (address) << 1)
|
||||
|
||||
struct mpl115_spi_buf {
|
||||
u8 tx[4];
|
||||
u8 rx[4];
|
||||
};
|
||||
|
||||
static int mpl115_spi_init(struct device *dev)
|
||||
{
|
||||
struct spi_device *spi = to_spi_device(dev);
|
||||
struct mpl115_spi_buf *buf;
|
||||
|
||||
buf = devm_kzalloc(dev, sizeof(*buf), GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
spi_set_drvdata(spi, buf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mpl115_spi_read(struct device *dev, u8 address)
|
||||
{
|
||||
struct spi_device *spi = to_spi_device(dev);
|
||||
struct mpl115_spi_buf *buf = spi_get_drvdata(spi);
|
||||
struct spi_transfer xfer = {
|
||||
.tx_buf = buf->tx,
|
||||
.rx_buf = buf->rx,
|
||||
.len = 4,
|
||||
};
|
||||
int ret;
|
||||
|
||||
buf->tx[0] = MPL115_SPI_READ(address);
|
||||
buf->tx[2] = MPL115_SPI_READ(address + 1);
|
||||
|
||||
ret = spi_sync_transfer(spi, &xfer, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return (buf->rx[1] << 8) | buf->rx[3];
|
||||
}
|
||||
|
||||
static int mpl115_spi_write(struct device *dev, u8 address, u8 value)
|
||||
{
|
||||
struct spi_device *spi = to_spi_device(dev);
|
||||
struct mpl115_spi_buf *buf = spi_get_drvdata(spi);
|
||||
struct spi_transfer xfer = {
|
||||
.tx_buf = buf->tx,
|
||||
.len = 2,
|
||||
};
|
||||
|
||||
buf->tx[0] = MPL115_SPI_WRITE(address);
|
||||
buf->tx[1] = value;
|
||||
|
||||
return spi_sync_transfer(spi, &xfer, 1);
|
||||
}
|
||||
|
||||
static const struct mpl115_ops mpl115_spi_ops = {
|
||||
.init = mpl115_spi_init,
|
||||
.read = mpl115_spi_read,
|
||||
.write = mpl115_spi_write,
|
||||
};
|
||||
|
||||
static int mpl115_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
const struct spi_device_id *id = spi_get_device_id(spi);
|
||||
|
||||
return mpl115_probe(&spi->dev, id->name, &mpl115_spi_ops);
|
||||
}
|
||||
|
||||
static const struct spi_device_id mpl115_spi_ids[] = {
|
||||
{ "mpl115", 0 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, mpl115_spi_ids);
|
||||
|
||||
static struct spi_driver mpl115_spi_driver = {
|
||||
.driver = {
|
||||
.name = "mpl115",
|
||||
},
|
||||
.probe = mpl115_spi_probe,
|
||||
.id_table = mpl115_spi_ids,
|
||||
};
|
||||
module_spi_driver(mpl115_spi_driver);
|
||||
|
||||
MODULE_AUTHOR("Akinobu Mita <akinobu.mita@gmail.com>");
|
||||
MODULE_DESCRIPTION("Freescale MPL115A1 pressure/temperature driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -62,6 +62,8 @@
|
||||
#define ST_PRESS_LPS331AP_DRDY_IRQ_ADDR 0x22
|
||||
#define ST_PRESS_LPS331AP_DRDY_IRQ_INT1_MASK 0x04
|
||||
#define ST_PRESS_LPS331AP_DRDY_IRQ_INT2_MASK 0x20
|
||||
#define ST_PRESS_LPS331AP_IHL_IRQ_ADDR 0x22
|
||||
#define ST_PRESS_LPS331AP_IHL_IRQ_MASK 0x80
|
||||
#define ST_PRESS_LPS331AP_MULTIREAD_BIT true
|
||||
#define ST_PRESS_LPS331AP_TEMP_OFFSET 42500
|
||||
|
||||
@ -100,6 +102,8 @@
|
||||
#define ST_PRESS_LPS25H_DRDY_IRQ_ADDR 0x23
|
||||
#define ST_PRESS_LPS25H_DRDY_IRQ_INT1_MASK 0x01
|
||||
#define ST_PRESS_LPS25H_DRDY_IRQ_INT2_MASK 0x10
|
||||
#define ST_PRESS_LPS25H_IHL_IRQ_ADDR 0x22
|
||||
#define ST_PRESS_LPS25H_IHL_IRQ_MASK 0x80
|
||||
#define ST_PRESS_LPS25H_MULTIREAD_BIT true
|
||||
#define ST_PRESS_LPS25H_TEMP_OFFSET 42500
|
||||
#define ST_PRESS_LPS25H_OUT_XL_ADDR 0x28
|
||||
@ -220,6 +224,8 @@ static const struct st_sensor_settings st_press_sensors_settings[] = {
|
||||
.addr = ST_PRESS_LPS331AP_DRDY_IRQ_ADDR,
|
||||
.mask_int1 = ST_PRESS_LPS331AP_DRDY_IRQ_INT1_MASK,
|
||||
.mask_int2 = ST_PRESS_LPS331AP_DRDY_IRQ_INT2_MASK,
|
||||
.addr_ihl = ST_PRESS_LPS331AP_IHL_IRQ_ADDR,
|
||||
.mask_ihl = ST_PRESS_LPS331AP_IHL_IRQ_MASK,
|
||||
},
|
||||
.multi_read_bit = ST_PRESS_LPS331AP_MULTIREAD_BIT,
|
||||
.bootime = 2,
|
||||
@ -304,6 +310,8 @@ static const struct st_sensor_settings st_press_sensors_settings[] = {
|
||||
.addr = ST_PRESS_LPS25H_DRDY_IRQ_ADDR,
|
||||
.mask_int1 = ST_PRESS_LPS25H_DRDY_IRQ_INT1_MASK,
|
||||
.mask_int2 = ST_PRESS_LPS25H_DRDY_IRQ_INT2_MASK,
|
||||
.addr_ihl = ST_PRESS_LPS25H_IHL_IRQ_ADDR,
|
||||
.mask_ihl = ST_PRESS_LPS25H_IHL_IRQ_MASK,
|
||||
},
|
||||
.multi_read_bit = ST_PRESS_LPS25H_MULTIREAD_BIT,
|
||||
.bootime = 2,
|
||||
|
@ -1,31 +1,3 @@
|
||||
|
||||
What: /sys/bus/iio/devices/device[n]/range
|
||||
KernelVersion: 2.6.37
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Hardware dependent ADC Full Scale Range used for some ambient
|
||||
light sensors in calculating lux.
|
||||
|
||||
What: /sys/bus/iio/devices/device[n]/range_available
|
||||
KernelVersion: 2.6.37
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Hardware dependent supported vales for ADC Full Scale Range.
|
||||
|
||||
What: /sys/bus/iio/devices/device[n]/adc_resolution
|
||||
KernelVersion: 2.6.37
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Hardware dependent ADC resolution of the ambient light sensor
|
||||
used in calculating the lux.
|
||||
|
||||
What: /sys/bus/iio/devices/device[n]/adc_resolution_available
|
||||
KernelVersion: 2.6.37
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Hardware dependent list of possible values supported for the
|
||||
adc_resolution of the given sensor.
|
||||
|
||||
What: /sys/bus/iio/devices/device[n]/in_illuminance0[_input|_raw]
|
||||
KernelVersion: 2.6.35
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
|
@ -236,7 +236,7 @@ static int ad7192_setup(struct ad7192_state *st,
|
||||
st->mclk = pdata->ext_clk_hz;
|
||||
else
|
||||
st->mclk = AD7192_INT_FREQ_MHZ;
|
||||
break;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
|
@ -21,8 +21,8 @@
|
||||
*/
|
||||
|
||||
#define AD7150_STATUS 0
|
||||
#define AD7150_STATUS_OUT1 (1 << 3)
|
||||
#define AD7150_STATUS_OUT2 (1 << 5)
|
||||
#define AD7150_STATUS_OUT1 BIT(3)
|
||||
#define AD7150_STATUS_OUT2 BIT(5)
|
||||
#define AD7150_CH1_DATA_HIGH 1
|
||||
#define AD7150_CH2_DATA_HIGH 3
|
||||
#define AD7150_CH1_AVG_HIGH 5
|
||||
@ -36,7 +36,7 @@
|
||||
#define AD7150_CH2_TIMEOUT 13
|
||||
#define AD7150_CH2_SETUP 14
|
||||
#define AD7150_CFG 15
|
||||
#define AD7150_CFG_FIX (1 << 7)
|
||||
#define AD7150_CFG_FIX BIT(7)
|
||||
#define AD7150_PD_TIMER 16
|
||||
#define AD7150_CH1_CAPDAC 17
|
||||
#define AD7150_CH2_CAPDAC 18
|
||||
@ -160,8 +160,9 @@ static int ad7150_read_event_config(struct iio_dev *indio_dev,
|
||||
|
||||
/* lock should be held */
|
||||
static int ad7150_write_event_params(struct iio_dev *indio_dev,
|
||||
unsigned int chan, enum iio_event_type type,
|
||||
enum iio_event_direction dir)
|
||||
unsigned int chan,
|
||||
enum iio_event_type type,
|
||||
enum iio_event_direction dir)
|
||||
{
|
||||
int ret;
|
||||
u16 value;
|
||||
@ -209,8 +210,9 @@ static int ad7150_write_event_params(struct iio_dev *indio_dev,
|
||||
}
|
||||
|
||||
static int ad7150_write_event_config(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, enum iio_event_type type,
|
||||
enum iio_event_direction dir, int state)
|
||||
const struct iio_chan_spec *chan,
|
||||
enum iio_event_type type,
|
||||
enum iio_event_direction dir, int state)
|
||||
{
|
||||
u8 thresh_type, cfg, adaptive;
|
||||
int ret;
|
||||
@ -302,11 +304,11 @@ static int ad7150_read_event_value(struct iio_dev *indio_dev,
|
||||
}
|
||||
|
||||
static int ad7150_write_event_value(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
enum iio_event_type type,
|
||||
enum iio_event_direction dir,
|
||||
enum iio_event_info info,
|
||||
int val, int val2)
|
||||
const struct iio_chan_spec *chan,
|
||||
enum iio_event_type type,
|
||||
enum iio_event_direction dir,
|
||||
enum iio_event_info info,
|
||||
int val, int val2)
|
||||
{
|
||||
int ret;
|
||||
struct ad7150_chip_info *chip = iio_priv(indio_dev);
|
||||
@ -365,9 +367,9 @@ static ssize_t ad7150_show_timeout(struct device *dev,
|
||||
}
|
||||
|
||||
static ssize_t ad7150_store_timeout(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 *indio_dev = dev_to_iio_dev(dev);
|
||||
struct ad7150_chip_info *chip = iio_priv(indio_dev);
|
||||
@ -580,7 +582,7 @@ static const struct iio_info ad7150_info = {
|
||||
*/
|
||||
|
||||
static int ad7150_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
int ret;
|
||||
struct ad7150_chip_info *chip;
|
||||
|
@ -119,6 +119,8 @@ struct st_sensor_bdu {
|
||||
* @addr: address of the register.
|
||||
* @mask_int1: mask to enable/disable IRQ on INT1 pin.
|
||||
* @mask_int2: mask to enable/disable IRQ on INT2 pin.
|
||||
* @addr_ihl: address to enable/disable active low on the INT lines.
|
||||
* @mask_ihl: mask to enable/disable active low on the INT lines.
|
||||
* struct ig1 - represents the Interrupt Generator 1 of sensors.
|
||||
* @en_addr: address of the enable ig1 register.
|
||||
* @en_mask: mask to write the on/off value for enable.
|
||||
@ -127,6 +129,8 @@ struct st_sensor_data_ready_irq {
|
||||
u8 addr;
|
||||
u8 mask_int1;
|
||||
u8 mask_int2;
|
||||
u8 addr_ihl;
|
||||
u8 mask_ihl;
|
||||
struct {
|
||||
u8 en_addr;
|
||||
u8 en_mask;
|
||||
|
44
include/linux/platform_data/ad5761.h
Normal file
44
include/linux/platform_data/ad5761.h
Normal file
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* AD5721, AD5721R, AD5761, AD5761R, Voltage Output Digital to Analog Converter
|
||||
*
|
||||
* Copyright 2016 Qtechnology A/S
|
||||
* 2016 Ricardo Ribalda <ricardo.ribalda@gmail.com>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
#ifndef __LINUX_PLATFORM_DATA_AD5761_H__
|
||||
#define __LINUX_PLATFORM_DATA_AD5761_H__
|
||||
|
||||
/**
|
||||
* enum ad5761_voltage_range - Voltage range the AD5761 is configured for.
|
||||
* @AD5761_VOLTAGE_RANGE_M10V_10V: -10V to 10V
|
||||
* @AD5761_VOLTAGE_RANGE_0V_10V: 0V to 10V
|
||||
* @AD5761_VOLTAGE_RANGE_M5V_5V: -5V to 5V
|
||||
* @AD5761_VOLTAGE_RANGE_0V_5V: 0V to 5V
|
||||
* @AD5761_VOLTAGE_RANGE_M2V5_7V5: -2.5V to 7.5V
|
||||
* @AD5761_VOLTAGE_RANGE_M3V_3V: -3V to 3V
|
||||
* @AD5761_VOLTAGE_RANGE_0V_16V: 0V to 16V
|
||||
* @AD5761_VOLTAGE_RANGE_0V_20V: 0V to 20V
|
||||
*/
|
||||
|
||||
enum ad5761_voltage_range {
|
||||
AD5761_VOLTAGE_RANGE_M10V_10V,
|
||||
AD5761_VOLTAGE_RANGE_0V_10V,
|
||||
AD5761_VOLTAGE_RANGE_M5V_5V,
|
||||
AD5761_VOLTAGE_RANGE_0V_5V,
|
||||
AD5761_VOLTAGE_RANGE_M2V5_7V5,
|
||||
AD5761_VOLTAGE_RANGE_M3V_3V,
|
||||
AD5761_VOLTAGE_RANGE_0V_16V,
|
||||
AD5761_VOLTAGE_RANGE_0V_20V,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ad5761_platform_data - AD5761 DAC driver platform data
|
||||
* @voltage_range: Voltage range the AD5761 is configured for
|
||||
*/
|
||||
|
||||
struct ad5761_platform_data {
|
||||
enum ad5761_voltage_range voltage_range;
|
||||
};
|
||||
|
||||
#endif
|
@ -37,6 +37,7 @@ enum iio_chan_type {
|
||||
IIO_VELOCITY,
|
||||
IIO_CONCENTRATION,
|
||||
IIO_RESISTANCE,
|
||||
IIO_PH,
|
||||
};
|
||||
|
||||
enum iio_modifier {
|
||||
|
Loading…
Reference in New Issue
Block a user