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:
Greg Kroah-Hartman 2016-02-01 13:10:03 -08:00
commit fec2f3335c
53 changed files with 2532 additions and 266 deletions

View File

@ -3054,6 +3054,7 @@ D: PLX USB338x driver
D: PCA9634 driver D: PCA9634 driver
D: Option GTM671WFS D: Option GTM671WFS
D: Fintek F81216A D: Fintek F81216A
D: AD5761 iio driver
D: Various kernel hacks D: Various kernel hacks
S: Qtechnology A/S S: Qtechnology A/S
S: Valby Langgade 142 S: Valby Langgade 142

View File

@ -497,7 +497,9 @@ Description:
6kohm_to_gnd: connected to ground via a 6kOhm resistor, 6kohm_to_gnd: connected to ground via a 6kOhm resistor,
20kohm_to_gnd: connected to ground via a 20kOhm resistor, 20kohm_to_gnd: connected to ground via a 20kOhm resistor,
100kohm_to_gnd: connected to ground via an 100kOhm 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, 500kohm_to_gnd: connected to ground via a 500kOhm resistor,
640kohm_to_gnd: connected to ground via a 640kOhm resistor,
three_state: left floating. three_state: left floating.
For a list of available output power down options read For a list of available output power down options read
outX_powerdown_mode_available. If Y is not present the outX_powerdown_mode_available. If Y is not present the
@ -1491,3 +1493,10 @@ Description:
This ABI is especially applicable for humidity sensors This ABI is especially applicable for humidity sensors
to heatup the device and get rid of any condensation to heatup the device and get rid of any condensation
in some humidity environment 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.

View File

@ -1,8 +1,10 @@
Freescale MMA8452Q, MMA8453Q, MMA8652FC or MMA8653FC triaxial accelerometer Freescale MMA8451Q, MMA8452Q, MMA8453Q, MMA8652FC or MMA8653FC
triaxial accelerometer
Required properties: Required properties:
- compatible: should contain one of - compatible: should contain one of
* "fsl,mma8451"
* "fsl,mma8452" * "fsl,mma8452"
* "fsl,mma8453" * "fsl,mma8453"
* "fsl,mma8652" * "fsl,mma8652"

View File

@ -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>;
}

View File

@ -6,6 +6,7 @@ Required properties:
"microchip,mcp3422" or "microchip,mcp3422" or
"microchip,mcp3423" or "microchip,mcp3423" or
"microchip,mcp3424" or "microchip,mcp3424" or
"microchip,mcp3425" or
"microchip,mcp3426" or "microchip,mcp3426" or
"microchip,mcp3427" or "microchip,mcp3427" or
"microchip,mcp3428" "microchip,mcp3428"

View File

@ -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>;
};

View File

@ -11,11 +11,19 @@ Required properties:
Refer to interrupt-controller/interrupts.txt for generic Refer to interrupt-controller/interrupts.txt for generic
interrupt client node bindings. 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: Example:
max30100@057 { max30100@057 {
compatible = "maxim,max30100"; compatible = "maxim,max30100";
reg = <57>; reg = <57>;
maxim,led-current-microamp = <24000 50000>;
interrupt-parent = <&gpio1>; interrupt-parent = <&gpio1>;
interrupts = <16 2>; interrupts = <16 2>;
}; };

View 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>;
};

View File

@ -28,6 +28,7 @@ arm ARM Ltd.
armadeus ARMadeus Systems SARL armadeus ARMadeus Systems SARL
artesyn Artesyn Embedded Technologies Inc. artesyn Artesyn Embedded Technologies Inc.
asahi-kasei Asahi Kasei Corp. asahi-kasei Asahi Kasei Corp.
atlas Atlas Scientific LLC
atmel Atmel Corporation atmel Atmel Corporation
auo AU Optronics Corporation auo AU Optronics Corporation
avago Avago Technologies avago Avago Technologies

View File

@ -1945,6 +1945,12 @@ M: Nicolas Ferre <nicolas.ferre@atmel.com>
S: Supported S: Supported
F: drivers/tty/serial/atmel_serial.c 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 ATMEL Audio ALSA driver
M: Nicolas Ferre <nicolas.ferre@atmel.com> M: Nicolas Ferre <nicolas.ferre@atmel.com>
L: alsa-devel@alsa-project.org (moderated for non-subscribers) L: alsa-devel@alsa-project.org (moderated for non-subscribers)

View File

@ -143,7 +143,7 @@ config MMA8452
select IIO_TRIGGERED_BUFFER select IIO_TRIGGERED_BUFFER
help help
Say yes here to build support for the following Freescale 3-axis 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 To compile this driver as a module, choose M here: the module
will be called mma8452. will be called mma8452.

View File

@ -1,6 +1,7 @@
/* /*
* mma8452.c - Support for following Freescale 3-axis accelerometers: * mma8452.c - Support for following Freescale 3-axis accelerometers:
* *
* MMA8451Q (14 bit)
* MMA8452Q (12 bit) * MMA8452Q (12 bit)
* MMA8453Q (10 bit) * MMA8453Q (10 bit)
* MMA8652FC (12 bit) * MMA8652FC (12 bit)
@ -15,7 +16,7 @@
* *
* 7-bit I2C slave address 0x1c/0x1d (pin selectable) * 7-bit I2C slave address 0x1c/0x1d (pin selectable)
* *
* TODO: orientation / freefall events, autosleep * TODO: orientation events, autosleep
*/ */
#include <linux/module.h> #include <linux/module.h>
@ -85,8 +86,9 @@
#define MMA8452_INT_FF_MT BIT(2) #define MMA8452_INT_FF_MT BIT(2)
#define MMA8452_INT_TRANS BIT(5) #define MMA8452_INT_TRANS BIT(5)
#define MMA8452_DEVICE_ID 0x2a #define MMA8451_DEVICE_ID 0x1a
#define MMA8453_DEVICE_ID 0x3a #define MMA8452_DEVICE_ID 0x2a
#define MMA8453_DEVICE_ID 0x3a
#define MMA8652_DEVICE_ID 0x4a #define MMA8652_DEVICE_ID 0x4a
#define MMA8653_DEVICE_ID 0x5a #define MMA8653_DEVICE_ID 0x5a
@ -416,6 +418,51 @@ fail:
return ret; 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, static int mma8452_set_hp_filter_frequency(struct mma8452_data *data,
int val, int val2) 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; const struct mma_chip_info *chip = data->chip_info;
int ret; int ret;
ret = i2c_smbus_read_byte_data(data->client, switch (dir) {
data->chip_info->ev_cfg); case IIO_EV_DIR_FALLING:
if (ret < 0) return mma8452_freefall_mode_enabled(data);
return ret; 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, 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; const struct mma_chip_info *chip = data->chip_info;
int val; int val;
val = i2c_smbus_read_byte_data(data->client, chip->ev_cfg); switch (dir) {
if (val < 0) case IIO_EV_DIR_FALLING:
return val; 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) if (state) {
val |= BIT(chan->scan_index + chip->ev_cfg_chan_shift); if (mma8452_freefall_mode_enabled(data)) {
else val &= ~BIT(idx_x + chip->ev_cfg_chan_shift);
val &= ~BIT(chan->scan_index + 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 &= ~BIT(chan->scan_index + chip->ev_cfg_chan_shift);
val |= MMA8452_FF_MT_CFG_OAE; }
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) 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) if (src < 0)
return; 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) if (src & data->chip_info->ev_src_xe)
iio_push_event(indio_dev, iio_push_event(indio_dev,
IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_X, 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; 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[] = { static const struct iio_event_spec mma8452_transient_event[] = {
{ {
.type = IIO_EV_TYPE_MAG, .type = IIO_EV_TYPE_MAG,
@ -781,6 +885,24 @@ static struct attribute_group mma8452_event_attribute_group = {
.attrs = mma8452_event_attributes, .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) { \ #define MMA8452_CHANNEL(axis, idx, bits) { \
.type = IIO_ACCEL, \ .type = IIO_ACCEL, \
.modified = 1, \ .modified = 1, \
@ -822,11 +944,20 @@ static struct attribute_group mma8452_event_attribute_group = {
.num_event_specs = ARRAY_SIZE(mma8452_motion_event), \ .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[] = { static const struct iio_chan_spec mma8452_channels[] = {
MMA8452_CHANNEL(X, idx_x, 12), MMA8452_CHANNEL(X, idx_x, 12),
MMA8452_CHANNEL(Y, idx_y, 12), MMA8452_CHANNEL(Y, idx_y, 12),
MMA8452_CHANNEL(Z, idx_z, 12), MMA8452_CHANNEL(Z, idx_z, 12),
IIO_CHAN_SOFT_TIMESTAMP(idx_ts), IIO_CHAN_SOFT_TIMESTAMP(idx_ts),
MMA8452_FREEFALL_CHANNEL(IIO_MOD_X_AND_Y_AND_Z),
}; };
static const struct iio_chan_spec mma8453_channels[] = { 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(Y, idx_y, 10),
MMA8452_CHANNEL(Z, idx_z, 10), MMA8452_CHANNEL(Z, idx_z, 10),
IIO_CHAN_SOFT_TIMESTAMP(idx_ts), IIO_CHAN_SOFT_TIMESTAMP(idx_ts),
MMA8452_FREEFALL_CHANNEL(IIO_MOD_X_AND_Y_AND_Z),
}; };
static const struct iio_chan_spec mma8652_channels[] = { 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(Y, idx_y, 12),
MMA8652_CHANNEL(Z, idx_z, 12), MMA8652_CHANNEL(Z, idx_z, 12),
IIO_CHAN_SOFT_TIMESTAMP(idx_ts), IIO_CHAN_SOFT_TIMESTAMP(idx_ts),
MMA8652_FREEFALL_CHANNEL(IIO_MOD_X_AND_Y_AND_Z),
}; };
static const struct iio_chan_spec mma8653_channels[] = { 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(Y, idx_y, 10),
MMA8652_CHANNEL(Z, idx_z, 10), MMA8652_CHANNEL(Z, idx_z, 10),
IIO_CHAN_SOFT_TIMESTAMP(idx_ts), IIO_CHAN_SOFT_TIMESTAMP(idx_ts),
MMA8652_FREEFALL_CHANNEL(IIO_MOD_X_AND_Y_AND_Z),
}; };
enum { enum {
mma8451,
mma8452, mma8452,
mma8453, mma8453,
mma8652, mma8652,
@ -858,17 +993,34 @@ enum {
}; };
static const struct mma_chip_info mma_chip_info_table[] = { 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] = { [mma8452] = {
.chip_id = MMA8452_DEVICE_ID, .chip_id = MMA8452_DEVICE_ID,
.channels = mma8452_channels, .channels = mma8452_channels,
.num_channels = ARRAY_SIZE(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} }, .mma_scales = { {0, 9577}, {0, 19154}, {0, 38307} },
.ev_cfg = MMA8452_TRANSIENT_CFG, .ev_cfg = MMA8452_TRANSIENT_CFG,
.ev_cfg_ele = MMA8452_TRANSIENT_CFG_ELE, .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[] = { 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,mma8452", .data = &mma_chip_info_table[mma8452] },
{ .compatible = "fsl,mma8453", .data = &mma_chip_info_table[mma8453] }, { .compatible = "fsl,mma8453", .data = &mma_chip_info_table[mma8453] },
{ .compatible = "fsl,mma8652", .data = &mma_chip_info_table[mma8652] }, { .compatible = "fsl,mma8652", .data = &mma_chip_info_table[mma8652] },
@ -1085,6 +1238,7 @@ static int mma8452_probe(struct i2c_client *client,
return ret; return ret;
switch (ret) { switch (ret) {
case MMA8451_DEVICE_ID:
case MMA8452_DEVICE_ID: case MMA8452_DEVICE_ID:
case MMA8453_DEVICE_ID: case MMA8453_DEVICE_ID:
case MMA8652_DEVICE_ID: case MMA8652_DEVICE_ID:
@ -1190,6 +1344,10 @@ static int mma8452_probe(struct i2c_client *client,
if (ret < 0) if (ret < 0)
goto buffer_cleanup; goto buffer_cleanup;
ret = mma8452_set_freefall_mode(data, false);
if (ret)
return ret;
return 0; return 0;
buffer_cleanup: buffer_cleanup:

View File

@ -67,6 +67,8 @@
#define ST_ACCEL_1_DRDY_IRQ_ADDR 0x22 #define ST_ACCEL_1_DRDY_IRQ_ADDR 0x22
#define ST_ACCEL_1_DRDY_IRQ_INT1_MASK 0x10 #define ST_ACCEL_1_DRDY_IRQ_INT1_MASK 0x10
#define ST_ACCEL_1_DRDY_IRQ_INT2_MASK 0x08 #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 #define ST_ACCEL_1_MULTIREAD_BIT true
/* CUSTOM VALUES FOR SENSOR 2 */ /* CUSTOM VALUES FOR SENSOR 2 */
@ -92,6 +94,8 @@
#define ST_ACCEL_2_DRDY_IRQ_ADDR 0x22 #define ST_ACCEL_2_DRDY_IRQ_ADDR 0x22
#define ST_ACCEL_2_DRDY_IRQ_INT1_MASK 0x02 #define ST_ACCEL_2_DRDY_IRQ_INT1_MASK 0x02
#define ST_ACCEL_2_DRDY_IRQ_INT2_MASK 0x10 #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 #define ST_ACCEL_2_MULTIREAD_BIT true
/* CUSTOM VALUES FOR SENSOR 3 */ /* CUSTOM VALUES FOR SENSOR 3 */
@ -125,6 +129,8 @@
#define ST_ACCEL_3_DRDY_IRQ_ADDR 0x23 #define ST_ACCEL_3_DRDY_IRQ_ADDR 0x23
#define ST_ACCEL_3_DRDY_IRQ_INT1_MASK 0x80 #define ST_ACCEL_3_DRDY_IRQ_INT1_MASK 0x80
#define ST_ACCEL_3_DRDY_IRQ_INT2_MASK 0x00 #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_ADDR 0x23
#define ST_ACCEL_3_IG1_EN_MASK 0x08 #define ST_ACCEL_3_IG1_EN_MASK 0x08
#define ST_ACCEL_3_MULTIREAD_BIT false #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_ADDR 0x22
#define ST_ACCEL_5_DRDY_IRQ_INT1_MASK 0x04 #define ST_ACCEL_5_DRDY_IRQ_INT1_MASK 0x04
#define ST_ACCEL_5_DRDY_IRQ_INT2_MASK 0x20 #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_ADDR 0x21
#define ST_ACCEL_5_IG1_EN_MASK 0x08 #define ST_ACCEL_5_IG1_EN_MASK 0x08
#define ST_ACCEL_5_MULTIREAD_BIT false #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, .addr = ST_ACCEL_1_DRDY_IRQ_ADDR,
.mask_int1 = ST_ACCEL_1_DRDY_IRQ_INT1_MASK, .mask_int1 = ST_ACCEL_1_DRDY_IRQ_INT1_MASK,
.mask_int2 = ST_ACCEL_1_DRDY_IRQ_INT2_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, .multi_read_bit = ST_ACCEL_1_MULTIREAD_BIT,
.bootime = 2, .bootime = 2,
@ -355,6 +365,8 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = {
.addr = ST_ACCEL_2_DRDY_IRQ_ADDR, .addr = ST_ACCEL_2_DRDY_IRQ_ADDR,
.mask_int1 = ST_ACCEL_2_DRDY_IRQ_INT1_MASK, .mask_int1 = ST_ACCEL_2_DRDY_IRQ_INT1_MASK,
.mask_int2 = ST_ACCEL_2_DRDY_IRQ_INT2_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, .multi_read_bit = ST_ACCEL_2_MULTIREAD_BIT,
.bootime = 2, .bootime = 2,
@ -430,6 +442,8 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = {
.addr = ST_ACCEL_3_DRDY_IRQ_ADDR, .addr = ST_ACCEL_3_DRDY_IRQ_ADDR,
.mask_int1 = ST_ACCEL_3_DRDY_IRQ_INT1_MASK, .mask_int1 = ST_ACCEL_3_DRDY_IRQ_INT1_MASK,
.mask_int2 = ST_ACCEL_3_DRDY_IRQ_INT2_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 = { .ig1 = {
.en_addr = ST_ACCEL_3_IG1_EN_ADDR, .en_addr = ST_ACCEL_3_IG1_EN_ADDR,
.en_mask = ST_ACCEL_3_IG1_EN_MASK, .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, .addr = ST_ACCEL_5_DRDY_IRQ_ADDR,
.mask_int1 = ST_ACCEL_5_DRDY_IRQ_INT1_MASK, .mask_int1 = ST_ACCEL_5_DRDY_IRQ_INT1_MASK,
.mask_int2 = ST_ACCEL_5_DRDY_IRQ_INT2_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, .multi_read_bit = ST_ACCEL_5_MULTIREAD_BIT,
.bootime = 2, /* guess */ .bootime = 2, /* guess */

View File

@ -131,6 +131,16 @@ config AT91_ADC
To compile this driver as a module, choose M here: the module will be To compile this driver as a module, choose M here: the module will be
called at91_adc. 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 config AXP288_ADC
tristate "X-Powers AXP288 ADC driver" tristate "X-Powers AXP288 ADC driver"
depends on MFD_AXP20X depends on MFD_AXP20X
@ -265,11 +275,11 @@ config MCP320X
called mcp320x. called mcp320x.
config MCP3422 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 depends on I2C
help help
Say yes here to build support for Microchip Technology's Say yes here to build support for Microchip Technology's MCP3421
MCP3422, MCP3423, MCP3424, MCP3426, MCP3427 or MCP3428 MCP3422, MCP3423, MCP3424, MCP3425, MCP3426, MCP3427 or MCP3428
analog to digital converters. analog to digital converters.
This driver can also be built as a module. If so, the module will be This driver can also be built as a module. If so, the module will be

View File

@ -14,6 +14,7 @@ obj-$(CONFIG_AD7793) += ad7793.o
obj-$(CONFIG_AD7887) += ad7887.o obj-$(CONFIG_AD7887) += ad7887.o
obj-$(CONFIG_AD799X) += ad799x.o obj-$(CONFIG_AD799X) += ad799x.o
obj-$(CONFIG_AT91_ADC) += at91_adc.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_AXP288_ADC) += axp288_adc.o
obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o
obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o

View 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");

View File

@ -46,7 +46,7 @@ struct axp288_adc_info {
struct regmap *regmap; struct regmap *regmap;
}; };
static const struct iio_chan_spec const axp288_adc_channels[] = { static const struct iio_chan_spec axp288_adc_channels[] = {
{ {
.indexed = 1, .indexed = 1,
.type = IIO_TEMP, .type = IIO_TEMP,

View File

@ -187,26 +187,27 @@ out:
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \ .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, \ .type = IIO_VOLTAGE, \
.indexed = 1, \ .indexed = 1, \
.channel = (num * 2), \ .channel = (chan1), \
.channel2 = (num * 2 + 1), \ .channel2 = (chan2), \
.address = (num * 2), \ .address = (chan1), \
.differential = 1, \ .differential = 1, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \
} }
static const struct iio_chan_spec mcp3201_channels[] = { 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[] = { static const struct iio_chan_spec mcp3202_channels[] = {
MCP320X_VOLTAGE_CHANNEL(0), MCP320X_VOLTAGE_CHANNEL(0),
MCP320X_VOLTAGE_CHANNEL(1), 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[] = { 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(1),
MCP320X_VOLTAGE_CHANNEL(2), MCP320X_VOLTAGE_CHANNEL(2),
MCP320X_VOLTAGE_CHANNEL(3), MCP320X_VOLTAGE_CHANNEL(3),
MCP320X_VOLTAGE_CHANNEL_DIFF(0), MCP320X_VOLTAGE_CHANNEL_DIFF(0, 1),
MCP320X_VOLTAGE_CHANNEL_DIFF(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[] = { 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(5),
MCP320X_VOLTAGE_CHANNEL(6), MCP320X_VOLTAGE_CHANNEL(6),
MCP320X_VOLTAGE_CHANNEL(7), MCP320X_VOLTAGE_CHANNEL(7),
MCP320X_VOLTAGE_CHANNEL_DIFF(0), MCP320X_VOLTAGE_CHANNEL_DIFF(0, 1),
MCP320X_VOLTAGE_CHANNEL_DIFF(1), MCP320X_VOLTAGE_CHANNEL_DIFF(1, 0),
MCP320X_VOLTAGE_CHANNEL_DIFF(2), MCP320X_VOLTAGE_CHANNEL_DIFF(2, 3),
MCP320X_VOLTAGE_CHANNEL_DIFF(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 = { static const struct iio_info mcp320x_info = {

View File

@ -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 * Copyright (C) 2013, Angelo Compagnucci
* Author: Angelo Compagnucci <angelo.compagnucci@gmail.com> * Author: Angelo Compagnucci <angelo.compagnucci@gmail.com>
* *
* Datasheet: http://ww1.microchip.com/downloads/en/devicedoc/22088b.pdf * 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/22226a.pdf
* http://ww1.microchip.com/downloads/en/DeviceDoc/22072b.pdf
* *
* This driver exports the value of analog input voltage to sysfs, the * This driver exports the value of analog input voltage to sysfs, the
* voltage unit is nV. * voltage unit is nV.
@ -357,6 +358,7 @@ static int mcp3422_probe(struct i2c_client *client,
switch (adc->id) { switch (adc->id) {
case 1: case 1:
case 5:
indio_dev->channels = mcp3421_channels; indio_dev->channels = mcp3421_channels;
indio_dev->num_channels = ARRAY_SIZE(mcp3421_channels); indio_dev->num_channels = ARRAY_SIZE(mcp3421_channels);
break; break;
@ -395,6 +397,7 @@ static const struct i2c_device_id mcp3422_id[] = {
{ "mcp3422", 2 }, { "mcp3422", 2 },
{ "mcp3423", 3 }, { "mcp3423", 3 },
{ "mcp3424", 4 }, { "mcp3424", 4 },
{ "mcp3425", 5 },
{ "mcp3426", 6 }, { "mcp3426", 6 },
{ "mcp3427", 7 }, { "mcp3427", 7 },
{ "mcp3428", 8 }, { "mcp3428", 8 },
@ -421,5 +424,5 @@ static struct i2c_driver mcp3422_driver = {
module_i2c_driver(mcp3422_driver); module_i2c_driver(mcp3422_driver);
MODULE_AUTHOR("Angelo Compagnucci <angelo.compagnucci@gmail.com>"); 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"); MODULE_LICENSE("GPL v2");

View File

@ -4,6 +4,19 @@
menu "Chemical Sensors" 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 config IAQCORE
tristate "AMS iAQ-Core VOC sensors" tristate "AMS iAQ-Core VOC sensors"
depends on I2C depends on I2C

View File

@ -3,5 +3,6 @@
# #
# When adding new entries keep the list in alphabetical order # 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_IAQCORE) += ams-iaq-core.o
obj-$(CONFIG_VZ89X) += vz89x.o obj-$(CONFIG_VZ89X) += vz89x.o

View 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 *) &reg, 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, &reg);
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,
&reg, 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");

View File

@ -18,13 +18,15 @@
#include <asm/unaligned.h> #include <asm/unaligned.h>
#include <linux/iio/common/st_sensors.h> #include <linux/iio/common/st_sensors.h>
#include "st_sensors_core.h"
static inline u32 st_sensors_get_unaligned_le24(const u8 *p) static inline u32 st_sensors_get_unaligned_le24(const u8 *p)
{ {
return (s32)((p[0] | p[1] << 8 | p[2] << 16) << 8) >> 8; 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, int st_sensors_write_data_with_mask(struct iio_dev *indio_dev,
u8 reg_addr, u8 mask, u8 data) u8 reg_addr, u8 mask, u8 data)
{ {
int err; int err;
u8 new_data; u8 new_data;

View 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

View File

@ -14,32 +14,65 @@
#include <linux/iio/iio.h> #include <linux/iio/iio.h>
#include <linux/iio/trigger.h> #include <linux/iio/trigger.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/iio/common/st_sensors.h> #include <linux/iio/common/st_sensors.h>
#include "st_sensors_core.h"
int st_sensors_allocate_trigger(struct iio_dev *indio_dev, int st_sensors_allocate_trigger(struct iio_dev *indio_dev,
const struct iio_trigger_ops *trigger_ops) const struct iio_trigger_ops *trigger_ops)
{ {
int err; int err, irq;
struct st_sensor_data *sdata = iio_priv(indio_dev); struct st_sensor_data *sdata = iio_priv(indio_dev);
unsigned long irq_trig;
sdata->trig = iio_trigger_alloc("%s-trigger", indio_dev->name); sdata->trig = iio_trigger_alloc("%s-trigger", indio_dev->name);
if (sdata->trig == NULL) { if (sdata->trig == NULL) {
err = -ENOMEM;
dev_err(&indio_dev->dev, "failed to allocate iio trigger.\n"); 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, iio_trigger_generic_data_rdy_poll,
NULL, NULL,
IRQF_TRIGGER_RISING, irq_trig,
sdata->trig->name, sdata->trig->name,
sdata->trig); sdata->trig);
if (err) { if (err) {
dev_err(&indio_dev->dev, "failed to request trigger IRQ.\n"); 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); 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: iio_trigger_register_error:
free_irq(sdata->get_irq_data_ready(indio_dev), sdata->trig); free_irq(sdata->get_irq_data_ready(indio_dev), sdata->trig);
request_irq_error: iio_trigger_free:
iio_trigger_free(sdata->trig); iio_trigger_free(sdata->trig);
iio_trigger_alloc_error:
return err; return err;
} }
EXPORT_SYMBOL(st_sensors_allocate_trigger); EXPORT_SYMBOL(st_sensors_allocate_trigger);

View File

@ -111,6 +111,16 @@ config AD5755
To compile this driver as a module, choose M here: the To compile this driver as a module, choose M here: the
module will be called ad5755. 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 config AD5764
tristate "Analog Devices AD5764/64R/44/44R DAC driver" tristate "Analog Devices AD5764/64R/44/44R DAC driver"
depends on SPI_MASTER depends on SPI_MASTER
@ -176,11 +186,11 @@ config MAX5821
10 bits DAC. 10 bits DAC.
config MCP4725 config MCP4725
tristate "MCP4725 DAC driver" tristate "MCP4725/6 DAC driver"
depends on I2C depends on I2C
---help--- ---help---
Say Y here if you want to build a driver for the Microchip 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. interface.
To compile this driver as a module, choose M here: the module To compile this driver as a module, choose M here: the module

View File

@ -12,6 +12,7 @@ obj-$(CONFIG_AD5504) += ad5504.o
obj-$(CONFIG_AD5446) += ad5446.o obj-$(CONFIG_AD5446) += ad5446.o
obj-$(CONFIG_AD5449) += ad5449.o obj-$(CONFIG_AD5449) += ad5449.o
obj-$(CONFIG_AD5755) += ad5755.o obj-$(CONFIG_AD5755) += ad5755.o
obj-$(CONFIG_AD5761) += ad5761.o
obj-$(CONFIG_AD5764) += ad5764.o obj-$(CONFIG_AD5764) += ad5764.o
obj-$(CONFIG_AD5791) += ad5791.o obj-$(CONFIG_AD5791) += ad5791.o
obj-$(CONFIG_AD5686) += ad5686.o obj-$(CONFIG_AD5686) += ad5686.o

430
drivers/iio/dac/ad5761.c Normal file
View 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");

View File

@ -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> * Copyright (C) 2012 Peter Meerwald <pmeerw@pmeerw.net>
* *
@ -134,6 +134,12 @@ static const char * const mcp4725_powerdown_modes[] = {
"500kohm_to_gnd" "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, static int mcp4725_get_powerdown_mode(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan) const struct iio_chan_spec *chan)
{ {
@ -182,11 +188,24 @@ static ssize_t mcp4725_write_powerdown(struct iio_dev *indio_dev,
return len; return len;
} }
static const struct iio_enum mcp4725_powerdown_mode_enum = { enum {
.items = mcp4725_powerdown_modes, MCP4725,
.num_items = ARRAY_SIZE(mcp4725_powerdown_modes), MCP4726,
.get = mcp4725_get_powerdown_mode, };
.set = mcp4725_set_powerdown_mode,
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[] = { 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, .write = mcp4725_write_powerdown,
.shared = IIO_SEPARATE, .shared = IIO_SEPARATE,
}, },
IIO_ENUM("powerdown_mode", IIO_SEPARATE, &mcp4725_powerdown_mode_enum), IIO_ENUM("powerdown_mode", IIO_SEPARATE,
IIO_ENUM_AVAILABLE("powerdown_mode", &mcp4725_powerdown_mode_enum), &mcp472x_powerdown_mode_enum[MCP4725]),
IIO_ENUM_AVAILABLE("powerdown_mode",
&mcp472x_powerdown_mode_enum[MCP4725]),
{ }, { },
}; };
static const struct iio_chan_spec mcp4725_channel = { static const struct iio_chan_spec_ext_info mcp4726_ext_info[] = {
.type = IIO_VOLTAGE, {
.indexed = 1, .name = "powerdown",
.output = 1, .read = mcp4725_read_powerdown,
.channel = 0, .write = mcp4725_write_powerdown,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), .shared = IIO_SEPARATE,
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), },
.ext_info = mcp4725_ext_info, 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) 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->dev.parent = &client->dev;
indio_dev->info = &mcp4725_info; 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->num_channels = 1;
indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->modes = INDIO_DIRECT_MODE;
@ -315,7 +361,7 @@ static int mcp4725_probe(struct i2c_client *client,
} }
pd = (inbuf[0] >> 1) & 0x3; pd = (inbuf[0] >> 1) & 0x3;
data->powerdown = pd > 0 ? true : false; 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); data->dac_value = (inbuf[1] << 4) | (inbuf[2] >> 4);
return iio_device_register(indio_dev); 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[] = { static const struct i2c_device_id mcp4725_id[] = {
{ "mcp4725", 0 }, { "mcp4725", MCP4725 },
{ "mcp4726", MCP4726 },
{ } { }
}; };
MODULE_DEVICE_TABLE(i2c, mcp4725_id); MODULE_DEVICE_TABLE(i2c, mcp4725_id);
@ -345,5 +392,5 @@ static struct i2c_driver mcp4725_driver = {
module_i2c_driver(mcp4725_driver); module_i2c_driver(mcp4725_driver);
MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>"); MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>");
MODULE_DESCRIPTION("MCP4725 12-bit DAC"); MODULE_DESCRIPTION("MCP4725/6 12-bit DAC");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");

View File

@ -185,6 +185,11 @@ static const struct st_sensor_settings st_gyro_sensors_settings[] = {
.drdy_irq = { .drdy_irq = {
.addr = ST_GYRO_1_DRDY_IRQ_ADDR, .addr = ST_GYRO_1_DRDY_IRQ_ADDR,
.mask_int2 = ST_GYRO_1_DRDY_IRQ_INT2_MASK, .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, .multi_read_bit = ST_GYRO_1_MULTIREAD_BIT,
.bootime = 2, .bootime = 2,
@ -248,6 +253,11 @@ static const struct st_sensor_settings st_gyro_sensors_settings[] = {
.drdy_irq = { .drdy_irq = {
.addr = ST_GYRO_2_DRDY_IRQ_ADDR, .addr = ST_GYRO_2_DRDY_IRQ_ADDR,
.mask_int2 = ST_GYRO_2_DRDY_IRQ_INT2_MASK, .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, .multi_read_bit = ST_GYRO_2_MULTIREAD_BIT,
.bootime = 2, .bootime = 2,
@ -307,6 +317,11 @@ static const struct st_sensor_settings st_gyro_sensors_settings[] = {
.drdy_irq = { .drdy_irq = {
.addr = ST_GYRO_3_DRDY_IRQ_ADDR, .addr = ST_GYRO_3_DRDY_IRQ_ADDR,
.mask_int2 = ST_GYRO_3_DRDY_IRQ_INT2_MASK, .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, .multi_read_bit = ST_GYRO_3_MULTIREAD_BIT,
.bootime = 2, .bootime = 2,

View File

@ -13,7 +13,7 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * 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> #include <linux/module.h>
@ -24,6 +24,7 @@
#include <linux/irq.h> #include <linux/irq.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/of.h>
#include <linux/regmap.h> #include <linux/regmap.h>
#include <linux/iio/iio.h> #include <linux/iio/iio.h>
#include <linux/iio/buffer.h> #include <linux/iio/buffer.h>
@ -65,6 +66,7 @@
#define MAX30100_REG_SPO2_CONFIG_1600US 0x3 #define MAX30100_REG_SPO2_CONFIG_1600US 0x3
#define MAX30100_REG_LED_CONFIG 0x09 #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_RED_LED_SHIFT 4
#define MAX30100_REG_LED_CONFIG_24MA 0x07 #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, .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 unsigned long max30100_scan_masks[] = {0x3, 0};
static const struct iio_chan_spec max30100_channels[] = { 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; 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], &reg);
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], &reg);
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) static int max30100_chip_init(struct max30100_data *data)
{ {
int ret; int ret;
/* RED IR LED = 24mA, IR LED = 50mA */ /* setup LED current settings */
ret = regmap_write(data->regmap, MAX30100_REG_LED_CONFIG, ret = max30100_led_init(data);
(MAX30100_REG_LED_CONFIG_24MA <<
MAX30100_REG_LED_CONFIG_RED_LED_SHIFT) |
MAX30100_REG_LED_CONFIG_50MA);
if (ret) if (ret)
return ret; return ret;

View File

@ -43,14 +43,16 @@ config SI7005
humidity and temperature sensor. humidity and temperature sensor.
To compile this driver as a module, choose M here: the module 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 config SI7020
tristate "Si7013/20/21 Relative Humidity and Temperature Sensors" tristate "Si7013/20/21 Relative Humidity and Temperature Sensors"
depends on I2C depends on I2C
help help
Say yes here to build support for the Silicon Labs Si7013/20/21 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 To compile this driver as a module, choose M here: the module
will be called si7020. will be called si7020.

View File

@ -50,12 +50,32 @@
#define DHT11_EDGES_PER_READ (2 * DHT11_BITS_PER_READ + \ #define DHT11_EDGES_PER_READ (2 * DHT11_BITS_PER_READ + \
DHT11_EDGES_PREAMBLE + 1) 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_START_TRANSMISSION 18 /* ms */
#define DHT11_SENSOR_RESPONSE 80000 #define DHT11_MIN_TIMERES 34000 /* ns */
#define DHT11_START_BIT 50000 #define DHT11_THRESHOLD 49000 /* ns */
#define DHT11_DATA_BIT_LOW 27000 #define DHT11_AMBIG_LOW 23000 /* ns */
#define DHT11_DATA_BIT_HIGH 70000 #define DHT11_AMBIG_HIGH 30000 /* ns */
struct dht11 { struct dht11 {
struct device *dev; struct device *dev;
@ -76,43 +96,39 @@ struct dht11 {
struct {s64 ts; int value; } edges[DHT11_EDGES_PER_READ]; 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; unsigned char ret = 0;
int i; int i;
for (i = 0; i < 8; ++i) { for (i = 0; i < 8; ++i) {
ret <<= 1; ret <<= 1;
if (timing[i] >= threshold) if (bits[i])
++ret; ++ret;
} }
return 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; 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) { for (i = 0; i < DHT11_BITS_PER_READ; ++i) {
t = dht11->edges[offset + 2 * i + 2].ts - t = dht11->edges[offset + 2 * i + 2].ts -
dht11->edges[offset + 2 * i + 1].ts; dht11->edges[offset + 2 * i + 1].ts;
if (!dht11->edges[offset + 2 * i + 1].value) if (!dht11->edges[offset + 2 * i + 1].value)
return -EIO; /* lost synchronisation */ return -EIO; /* lost synchronisation */
timing[i] = t / timeres; bits[i] = t > DHT11_THRESHOLD;
} }
hum_int = dht11_decode_byte(timing, threshold); hum_int = dht11_decode_byte(bits);
hum_dec = dht11_decode_byte(&timing[8], threshold); hum_dec = dht11_decode_byte(&bits[8]);
temp_int = dht11_decode_byte(&timing[16], threshold); temp_int = dht11_decode_byte(&bits[16]);
temp_dec = dht11_decode_byte(&timing[24], threshold); temp_dec = dht11_decode_byte(&bits[24]);
checksum = dht11_decode_byte(&timing[32], threshold); checksum = dht11_decode_byte(&bits[32]);
if (((hum_int + hum_dec + temp_int + temp_dec) & 0xff) != checksum) if (((hum_int + hum_dec + temp_int + temp_dec) & 0xff) != checksum)
return -EIO; return -EIO;
@ -161,12 +177,12 @@ static int dht11_read_raw(struct iio_dev *iio_dev,
int *val, int *val2, long m) int *val, int *val2, long m)
{ {
struct dht11 *dht11 = iio_priv(iio_dev); struct dht11 *dht11 = iio_priv(iio_dev);
int ret, timeres; int ret, timeres, offset;
mutex_lock(&dht11->lock); mutex_lock(&dht11->lock);
if (dht11->timestamp + DHT11_DATA_VALID_TIME < ktime_get_real_ns()) { if (dht11->timestamp + DHT11_DATA_VALID_TIME < ktime_get_real_ns()) {
timeres = ktime_get_resolution_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", dev_err(dht11->dev, "timeresolution %dns too low\n",
timeres); timeres);
/* In theory a better clock could become available /* In theory a better clock could become available
@ -176,6 +192,10 @@ static int dht11_read_raw(struct iio_dev *iio_dev,
ret = -EAGAIN; ret = -EAGAIN;
goto err; 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); reinit_completion(&dht11->completion);
@ -208,11 +228,14 @@ static int dht11_read_raw(struct iio_dev *iio_dev,
if (ret < 0) if (ret < 0)
goto err; goto err;
ret = dht11_decode(dht11, offset = DHT11_EDGES_PREAMBLE +
dht11->num_edges == DHT11_EDGES_PER_READ ? dht11->num_edges - DHT11_EDGES_PER_READ;
DHT11_EDGES_PREAMBLE : for (; offset >= 0; --offset) {
DHT11_EDGES_PREAMBLE - 2, ret = dht11_decode(dht11, offset);
timeres); if (!ret)
break;
}
if (ret) if (ret)
goto err; goto err;
} }

View File

@ -170,6 +170,7 @@ static int si7005_probe(struct i2c_client *client,
static const struct i2c_device_id si7005_id[] = { static const struct i2c_device_id si7005_id[] = {
{ "si7005", 0 }, { "si7005", 0 },
{ "th02", 0 },
{ } { }
}; };
MODULE_DEVICE_TABLE(i2c, si7005_id); MODULE_DEVICE_TABLE(i2c, si7005_id);

View File

@ -149,6 +149,7 @@ static int si7020_probe(struct i2c_client *client,
static const struct i2c_device_id si7020_id[] = { static const struct i2c_device_id si7020_id[] = {
{ "si7020", 0 }, { "si7020", 0 },
{ "th06", 0 },
{ } { }
}; };
MODULE_DEVICE_TABLE(i2c, si7020_id); MODULE_DEVICE_TABLE(i2c, si7020_id);

View File

@ -727,8 +727,7 @@ static const struct iio_info mpu_info = {
/** /**
* inv_check_and_setup_chip() - check and setup chip. * inv_check_and_setup_chip() - check and setup chip.
*/ */
static int inv_check_and_setup_chip(struct inv_mpu6050_state *st, static int inv_check_and_setup_chip(struct inv_mpu6050_state *st)
const struct i2c_device_id *id)
{ {
int result; int result;
@ -795,7 +794,7 @@ static int inv_mpu_probe(struct i2c_client *client,
if (pdata) if (pdata)
st->plat_data = *pdata; st->plat_data = *pdata;
/* power is turned on inside check chip type*/ /* 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) if (result)
return result; return result;

View File

@ -77,6 +77,7 @@ static const char * const iio_chan_type_name_spec[] = {
[IIO_VELOCITY] = "velocity", [IIO_VELOCITY] = "velocity",
[IIO_CONCENTRATION] = "concentration", [IIO_CONCENTRATION] = "concentration",
[IIO_RESISTANCE] = "resistance", [IIO_RESISTANCE] = "resistance",
[IIO_PH] = "ph",
}; };
static const char * const iio_modifier_names[] = { static const char * const iio_modifier_names[] = {

View File

@ -65,19 +65,25 @@
#define OPT3001_REG_EXPONENT(n) ((n) >> 12) #define OPT3001_REG_EXPONENT(n) ((n) >> 12)
#define OPT3001_REG_MANTISSA(n) ((n) & 0xfff) #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 * 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 opt3001 {
struct i2c_client *client; struct i2c_client *client;
struct device *dev; struct device *dev;
struct mutex lock; struct mutex lock;
u16 ok_to_ignore_lock:1; bool ok_to_ignore_lock;
u16 result_ready:1; bool result_ready;
wait_queue_head_t result_ready_queue; wait_queue_head_t result_ready_queue;
u16 result; u16 result;
@ -89,6 +95,8 @@ struct opt3001 {
u8 high_thresh_exp; u8 high_thresh_exp;
u8 low_thresh_exp; u8 low_thresh_exp;
bool use_irq;
}; };
struct opt3001_scale { struct opt3001_scale {
@ -227,26 +235,30 @@ static int opt3001_get_lux(struct opt3001 *opt, int *val, int *val2)
u16 reg; u16 reg;
u8 exponent; u8 exponent;
u16 value; u16 value;
long timeout;
/* 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 * Enable the end-of-conversion interrupt mechanism. Note that
* this value later on. * 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); ret = i2c_smbus_write_word_swapped(opt->client,
if (ret < 0) { OPT3001_LOW_LIMIT,
dev_err(opt->dev, "failed to write register %02x\n", OPT3001_LOW_LIMIT_EOC_ENABLE);
OPT3001_LOW_LIMIT); if (ret < 0) {
return ret; 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; 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 */ /* Configure for single-conversion mode and start a new conversion */
ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_CONFIGURATION); ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_CONFIGURATION);
if (ret < 0) { if (ret < 0) {
@ -266,32 +278,69 @@ static int opt3001_get_lux(struct opt3001 *opt, int *val, int *val2)
goto err; goto err;
} }
/* Wait for the IRQ to indicate the conversion is complete */ if (opt->use_irq) {
ret = wait_event_timeout(opt->result_ready_queue, opt->result_ready, /* Wait for the IRQ to indicate the conversion is complete */
OPT3001_RESULT_READY_TIMEOUT); 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: err:
/* Disallow IRQ to access the device while lock is active */ if (opt->use_irq)
opt->ok_to_ignore_lock = false; /* Disallow IRQ to access the device while lock is active */
opt->ok_to_ignore_lock = false;
if (ret == 0) if (ret == 0)
return -ETIMEDOUT; return -ETIMEDOUT;
else if (ret < 0) else if (ret < 0)
return ret; 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 * Disable the end-of-conversion interrupt mechanism by
* that selectively clearing those enable bits would affect the actual * restoring the low-level limit value (clearing
* limit value due to bit-overlap and therefore can't be done. * OPT3001_LOW_LIMIT_EOC_ENABLE). Note that selectively clearing
*/ * those enable bits would affect the actual limit value due to
value = (opt->low_thresh_exp << 12) | opt->low_thresh_mantissa; * bit-overlap and therefore can't be done.
ret = i2c_smbus_write_word_swapped(opt->client, OPT3001_LOW_LIMIT, */
value); value = (opt->low_thresh_exp << 12) | opt->low_thresh_mantissa;
if (ret < 0) { ret = i2c_smbus_write_word_swapped(opt->client,
dev_err(opt->dev, "failed to write register %02x\n", OPT3001_LOW_LIMIT,
OPT3001_LOW_LIMIT); value);
return ret; if (ret < 0) {
dev_err(opt->dev, "failed to write register %02x\n",
OPT3001_LOW_LIMIT);
return ret;
}
} }
exponent = OPT3001_REG_EXPONENT(opt->result); exponent = OPT3001_REG_EXPONENT(opt->result);
@ -325,13 +374,13 @@ static int opt3001_set_int_time(struct opt3001 *opt, int time)
reg = ret; reg = ret;
switch (time) { switch (time) {
case 100000: case OPT3001_INT_TIME_SHORT:
reg &= ~OPT3001_CONFIGURATION_CT; reg &= ~OPT3001_CONFIGURATION_CT;
opt->int_time = 100000; opt->int_time = OPT3001_INT_TIME_SHORT;
break; break;
case 800000: case OPT3001_INT_TIME_LONG:
reg |= OPT3001_CONFIGURATION_CT; reg |= OPT3001_CONFIGURATION_CT;
opt->int_time = 800000; opt->int_time = OPT3001_INT_TIME_LONG;
break; break;
default: default:
return -EINVAL; return -EINVAL;
@ -597,9 +646,9 @@ static int opt3001_configure(struct opt3001 *opt)
/* Reflect status of the device's integration time setting */ /* Reflect status of the device's integration time setting */
if (reg & OPT3001_CONFIGURATION_CT) if (reg & OPT3001_CONFIGURATION_CT)
opt->int_time = 800000; opt->int_time = OPT3001_INT_TIME_LONG;
else else
opt->int_time = 100000; opt->int_time = OPT3001_INT_TIME_SHORT;
/* Ensure device is in shutdown initially */ /* Ensure device is in shutdown initially */
opt3001_set_mode(opt, &reg, OPT3001_CONFIGURATION_M_SHUTDOWN); opt3001_set_mode(opt, &reg, OPT3001_CONFIGURATION_M_SHUTDOWN);
@ -733,12 +782,18 @@ static int opt3001_probe(struct i2c_client *client,
return ret; return ret;
} }
ret = request_threaded_irq(irq, NULL, opt3001_irq, /* Make use of INT pin only if valid IRQ no. is given */
IRQF_TRIGGER_FALLING | IRQF_ONESHOT, if (irq > 0) {
"opt3001", iio); ret = request_threaded_irq(irq, NULL, opt3001_irq,
if (ret) { IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
dev_err(dev, "failed to request IRQ #%d\n", irq); "opt3001", iio);
return ret; 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; return 0;
@ -751,7 +806,8 @@ static int opt3001_remove(struct i2c_client *client)
int ret; int ret;
u16 reg; 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); ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_CONFIGURATION);
if (ret < 0) { if (ret < 0) {

View File

@ -252,7 +252,7 @@ struct ak_def {
u8 data_regs[3]; 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, .type = AK8975,
.raw_to_gauss = ak8975_raw_to_gauss, .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 ak8975_data {
struct i2c_client *client; struct i2c_client *client;
struct ak_def *def; const struct ak_def *def;
struct attribute_group attrs; struct attribute_group attrs;
struct mutex lock; struct mutex lock;
u8 asa[3]; u8 asa[3];

View File

@ -175,6 +175,8 @@
#define ST_MAGN_3_BDU_MASK 0x10 #define ST_MAGN_3_BDU_MASK 0x10
#define ST_MAGN_3_DRDY_IRQ_ADDR 0x62 #define ST_MAGN_3_DRDY_IRQ_ADDR 0x62
#define ST_MAGN_3_DRDY_INT_MASK 0x01 #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_FS_AVL_15000_GAIN 1500
#define ST_MAGN_3_MULTIREAD_BIT false #define ST_MAGN_3_MULTIREAD_BIT false
#define ST_MAGN_3_OUT_X_L_ADDR 0x68 #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 = { .drdy_irq = {
.addr = ST_MAGN_3_DRDY_IRQ_ADDR, .addr = ST_MAGN_3_DRDY_IRQ_ADDR,
.mask_int1 = ST_MAGN_3_DRDY_INT_MASK, .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, .multi_read_bit = ST_MAGN_3_MULTIREAD_BIT,
.bootime = 2, .bootime = 2,

View File

@ -10,11 +10,11 @@ config BMP280
depends on I2C depends on I2C
select REGMAP_I2C select REGMAP_I2C
help help
Say yes here to build support for Bosch Sensortec BMP280 Say yes here to build support for Bosch Sensortec BMP280
pressure and temperature sensor. pressure and temperature sensor.
To compile this driver as a module, choose M here: the module To compile this driver as a module, choose M here: the module
will be called bmp280. will be called bmp280.
config HID_SENSOR_PRESS config HID_SENSOR_PRESS
depends on HID_SENSOR_HUB depends on HID_SENSOR_HUB
@ -27,18 +27,33 @@ config HID_SENSOR_PRESS
Say yes here to build support for the HID SENSOR Say yes here to build support for the HID SENSOR
Pressure driver Pressure driver
To compile this driver as a module, choose M here: the module To compile this driver as a module, choose M here: the module
will be called hid-sensor-press. will be called hid-sensor-press.
config MPL115 config MPL115
tristate
config MPL115_I2C
tristate "Freescale MPL115A2 pressure sensor driver" tristate "Freescale MPL115A2 pressure sensor driver"
depends on I2C depends on I2C
select MPL115
help help
Say yes here to build support for the Freescale MPL115A2 Say yes here to build support for the Freescale MPL115A2
pressure sensor connected via I2C. pressure sensor connected via I2C.
To compile this driver as a module, choose M here: the module To compile this driver as a module, choose M here: the module
will be called mpl115. 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 config MPL3115
tristate "Freescale MPL3115A2 pressure sensor driver" tristate "Freescale MPL3115A2 pressure sensor driver"
@ -49,8 +64,8 @@ config MPL3115
Say yes here to build support for the Freescale MPL3115A2 Say yes here to build support for the Freescale MPL3115A2
pressure sensor / altimeter. pressure sensor / altimeter.
To compile this driver as a module, choose M here: the module To compile this driver as a module, choose M here: the module
will be called mpl3115. will be called mpl3115.
config MS5611 config MS5611
tristate "Measurement Specialties MS5611 pressure sensor driver" tristate "Measurement Specialties MS5611 pressure sensor driver"
@ -82,7 +97,7 @@ config MS5611_SPI
config MS5637 config MS5637
tristate "Measurement Specialties MS5637 pressure & temperature sensor" tristate "Measurement Specialties MS5637 pressure & temperature sensor"
depends on I2C depends on I2C
select IIO_MS_SENSORS_I2C select IIO_MS_SENSORS_I2C
help help
If you say yes here you get support for the Measurement Specialties If you say yes here you get support for the Measurement Specialties
MS5637 pressure and temperature sensor. MS5637 pressure and temperature sensor.
@ -128,7 +143,7 @@ config T5403
Say yes here to build support for the EPCOS T5403 pressure sensor Say yes here to build support for the EPCOS T5403 pressure sensor
connected via I2C. connected via I2C.
To compile this driver as a module, choose M here: the module To compile this driver as a module, choose M here: the module
will be called t5403. will be called t5403.
endmenu endmenu

View File

@ -6,6 +6,8 @@
obj-$(CONFIG_BMP280) += bmp280.o obj-$(CONFIG_BMP280) += bmp280.o
obj-$(CONFIG_HID_SENSOR_PRESS) += hid-sensor-press.o obj-$(CONFIG_HID_SENSOR_PRESS) += hid-sensor-press.o
obj-$(CONFIG_MPL115) += mpl115.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_MPL3115) += mpl3115.o
obj-$(CONFIG_MS5611) += ms5611_core.o obj-$(CONFIG_MS5611) += ms5611_core.o
obj-$(CONFIG_MS5611_I2C) += ms5611_i2c.o obj-$(CONFIG_MS5611_I2C) += ms5611_i2c.o

View File

@ -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> * Copyright (c) 2014 Peter Meerwald <pmeerw@pmeerw.net>
* *
@ -7,17 +7,16 @@
* the GNU General Public License. See the file COPYING in the main * the GNU General Public License. See the file COPYING in the main
* directory of this archive for more details. * directory of this archive for more details.
* *
* (7-bit I2C slave address 0x60)
*
* TODO: shutdown pin * TODO: shutdown pin
* *
*/ */
#include <linux/module.h> #include <linux/module.h>
#include <linux/i2c.h>
#include <linux/iio/iio.h> #include <linux/iio/iio.h>
#include <linux/delay.h> #include <linux/delay.h>
#include "mpl115.h"
#define MPL115_PADC 0x00 /* pressure ADC output value, MSB first, 10 bit */ #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_TADC 0x02 /* temperature ADC output value, MSB first, 10 bit */
#define MPL115_A0 0x04 /* 12 bit integer, 3 bit fraction */ #define MPL115_A0 0x04 /* 12 bit integer, 3 bit fraction */
@ -27,16 +26,18 @@
#define MPL115_CONVERT 0x12 /* convert temperature and pressure */ #define MPL115_CONVERT 0x12 /* convert temperature and pressure */
struct mpl115_data { struct mpl115_data {
struct i2c_client *client; struct device *dev;
struct mutex lock; struct mutex lock;
s16 a0; s16 a0;
s16 b1, b2; s16 b1, b2;
s16 c12; s16 c12;
const struct mpl115_ops *ops;
}; };
static int mpl115_request(struct mpl115_data *data) 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) if (ret < 0)
return ret; return ret;
@ -57,12 +58,12 @@ static int mpl115_comp_pressure(struct mpl115_data *data, int *val, int *val2)
if (ret < 0) if (ret < 0)
goto done; goto done;
ret = i2c_smbus_read_word_swapped(data->client, MPL115_PADC); ret = data->ops->read(data->dev, MPL115_PADC);
if (ret < 0) if (ret < 0)
goto done; goto done;
padc = ret >> 6; padc = ret >> 6;
ret = i2c_smbus_read_word_swapped(data->client, MPL115_TADC); ret = data->ops->read(data->dev, MPL115_TADC);
if (ret < 0) if (ret < 0)
goto done; goto done;
tadc = ret >> 6; tadc = ret >> 6;
@ -90,7 +91,7 @@ static int mpl115_read_temp(struct mpl115_data *data)
ret = mpl115_request(data); ret = mpl115_request(data);
if (ret < 0) if (ret < 0)
goto done; goto done;
ret = i2c_smbus_read_word_swapped(data->client, MPL115_TADC); ret = data->ops->read(data->dev, MPL115_TADC);
done: done:
mutex_unlock(&data->lock); mutex_unlock(&data->lock);
return ret; return ret;
@ -145,66 +146,53 @@ static const struct iio_info mpl115_info = {
.driver_module = THIS_MODULE, .driver_module = THIS_MODULE,
}; };
static int mpl115_probe(struct i2c_client *client, int mpl115_probe(struct device *dev, const char *name,
const struct i2c_device_id *id) const struct mpl115_ops *ops)
{ {
struct mpl115_data *data; struct mpl115_data *data;
struct iio_dev *indio_dev; struct iio_dev *indio_dev;
int ret; int ret;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA)) indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
return -ENODEV;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (!indio_dev) if (!indio_dev)
return -ENOMEM; return -ENOMEM;
data = iio_priv(indio_dev); data = iio_priv(indio_dev);
data->client = client; data->dev = dev;
data->ops = ops;
mutex_init(&data->lock); mutex_init(&data->lock);
i2c_set_clientdata(client, indio_dev);
indio_dev->info = &mpl115_info; indio_dev->info = &mpl115_info;
indio_dev->name = id->name; indio_dev->name = name;
indio_dev->dev.parent = &client->dev; indio_dev->dev.parent = dev;
indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = mpl115_channels; indio_dev->channels = mpl115_channels;
indio_dev->num_channels = ARRAY_SIZE(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) if (ret < 0)
return ret; return ret;
data->a0 = 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) if (ret < 0)
return ret; return ret;
data->b1 = 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) if (ret < 0)
return ret; return ret;
data->b2 = 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) if (ret < 0)
return ret; return ret;
data->c12 = ret; data->c12 = ret;
return devm_iio_device_register(&client->dev, indio_dev); return devm_iio_device_register(dev, indio_dev);
} }
EXPORT_SYMBOL_GPL(mpl115_probe);
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);
MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>"); MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>");
MODULE_DESCRIPTION("Freescale MPL115 pressure/temperature driver"); MODULE_DESCRIPTION("Freescale MPL115 pressure/temperature driver");

View 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

View 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");

View 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");

View File

@ -62,6 +62,8 @@
#define ST_PRESS_LPS331AP_DRDY_IRQ_ADDR 0x22 #define ST_PRESS_LPS331AP_DRDY_IRQ_ADDR 0x22
#define ST_PRESS_LPS331AP_DRDY_IRQ_INT1_MASK 0x04 #define ST_PRESS_LPS331AP_DRDY_IRQ_INT1_MASK 0x04
#define ST_PRESS_LPS331AP_DRDY_IRQ_INT2_MASK 0x20 #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_MULTIREAD_BIT true
#define ST_PRESS_LPS331AP_TEMP_OFFSET 42500 #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_ADDR 0x23
#define ST_PRESS_LPS25H_DRDY_IRQ_INT1_MASK 0x01 #define ST_PRESS_LPS25H_DRDY_IRQ_INT1_MASK 0x01
#define ST_PRESS_LPS25H_DRDY_IRQ_INT2_MASK 0x10 #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_MULTIREAD_BIT true
#define ST_PRESS_LPS25H_TEMP_OFFSET 42500 #define ST_PRESS_LPS25H_TEMP_OFFSET 42500
#define ST_PRESS_LPS25H_OUT_XL_ADDR 0x28 #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, .addr = ST_PRESS_LPS331AP_DRDY_IRQ_ADDR,
.mask_int1 = ST_PRESS_LPS331AP_DRDY_IRQ_INT1_MASK, .mask_int1 = ST_PRESS_LPS331AP_DRDY_IRQ_INT1_MASK,
.mask_int2 = ST_PRESS_LPS331AP_DRDY_IRQ_INT2_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, .multi_read_bit = ST_PRESS_LPS331AP_MULTIREAD_BIT,
.bootime = 2, .bootime = 2,
@ -304,6 +310,8 @@ static const struct st_sensor_settings st_press_sensors_settings[] = {
.addr = ST_PRESS_LPS25H_DRDY_IRQ_ADDR, .addr = ST_PRESS_LPS25H_DRDY_IRQ_ADDR,
.mask_int1 = ST_PRESS_LPS25H_DRDY_IRQ_INT1_MASK, .mask_int1 = ST_PRESS_LPS25H_DRDY_IRQ_INT1_MASK,
.mask_int2 = ST_PRESS_LPS25H_DRDY_IRQ_INT2_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, .multi_read_bit = ST_PRESS_LPS25H_MULTIREAD_BIT,
.bootime = 2, .bootime = 2,

View File

@ -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] What: /sys/bus/iio/devices/device[n]/in_illuminance0[_input|_raw]
KernelVersion: 2.6.35 KernelVersion: 2.6.35
Contact: linux-iio@vger.kernel.org Contact: linux-iio@vger.kernel.org

View File

@ -236,7 +236,7 @@ static int ad7192_setup(struct ad7192_state *st,
st->mclk = pdata->ext_clk_hz; st->mclk = pdata->ext_clk_hz;
else else
st->mclk = AD7192_INT_FREQ_MHZ; st->mclk = AD7192_INT_FREQ_MHZ;
break; break;
default: default:
ret = -EINVAL; ret = -EINVAL;
goto out; goto out;

View File

@ -21,8 +21,8 @@
*/ */
#define AD7150_STATUS 0 #define AD7150_STATUS 0
#define AD7150_STATUS_OUT1 (1 << 3) #define AD7150_STATUS_OUT1 BIT(3)
#define AD7150_STATUS_OUT2 (1 << 5) #define AD7150_STATUS_OUT2 BIT(5)
#define AD7150_CH1_DATA_HIGH 1 #define AD7150_CH1_DATA_HIGH 1
#define AD7150_CH2_DATA_HIGH 3 #define AD7150_CH2_DATA_HIGH 3
#define AD7150_CH1_AVG_HIGH 5 #define AD7150_CH1_AVG_HIGH 5
@ -36,7 +36,7 @@
#define AD7150_CH2_TIMEOUT 13 #define AD7150_CH2_TIMEOUT 13
#define AD7150_CH2_SETUP 14 #define AD7150_CH2_SETUP 14
#define AD7150_CFG 15 #define AD7150_CFG 15
#define AD7150_CFG_FIX (1 << 7) #define AD7150_CFG_FIX BIT(7)
#define AD7150_PD_TIMER 16 #define AD7150_PD_TIMER 16
#define AD7150_CH1_CAPDAC 17 #define AD7150_CH1_CAPDAC 17
#define AD7150_CH2_CAPDAC 18 #define AD7150_CH2_CAPDAC 18
@ -160,8 +160,9 @@ static int ad7150_read_event_config(struct iio_dev *indio_dev,
/* lock should be held */ /* lock should be held */
static int ad7150_write_event_params(struct iio_dev *indio_dev, static int ad7150_write_event_params(struct iio_dev *indio_dev,
unsigned int chan, enum iio_event_type type, unsigned int chan,
enum iio_event_direction dir) enum iio_event_type type,
enum iio_event_direction dir)
{ {
int ret; int ret;
u16 value; 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, static int ad7150_write_event_config(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan, enum iio_event_type type, const struct iio_chan_spec *chan,
enum iio_event_direction dir, int state) enum iio_event_type type,
enum iio_event_direction dir, int state)
{ {
u8 thresh_type, cfg, adaptive; u8 thresh_type, cfg, adaptive;
int ret; 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, static int ad7150_write_event_value(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan, const struct iio_chan_spec *chan,
enum iio_event_type type, enum iio_event_type type,
enum iio_event_direction dir, enum iio_event_direction dir,
enum iio_event_info info, enum iio_event_info info,
int val, int val2) int val, int val2)
{ {
int ret; int ret;
struct ad7150_chip_info *chip = iio_priv(indio_dev); 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, static ssize_t ad7150_store_timeout(struct device *dev,
struct device_attribute *attr, struct device_attribute *attr,
const char *buf, const char *buf,
size_t len) size_t len)
{ {
struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct ad7150_chip_info *chip = iio_priv(indio_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, static int ad7150_probe(struct i2c_client *client,
const struct i2c_device_id *id) const struct i2c_device_id *id)
{ {
int ret; int ret;
struct ad7150_chip_info *chip; struct ad7150_chip_info *chip;

View File

@ -119,6 +119,8 @@ struct st_sensor_bdu {
* @addr: address of the register. * @addr: address of the register.
* @mask_int1: mask to enable/disable IRQ on INT1 pin. * @mask_int1: mask to enable/disable IRQ on INT1 pin.
* @mask_int2: mask to enable/disable IRQ on INT2 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. * struct ig1 - represents the Interrupt Generator 1 of sensors.
* @en_addr: address of the enable ig1 register. * @en_addr: address of the enable ig1 register.
* @en_mask: mask to write the on/off value for enable. * @en_mask: mask to write the on/off value for enable.
@ -127,6 +129,8 @@ struct st_sensor_data_ready_irq {
u8 addr; u8 addr;
u8 mask_int1; u8 mask_int1;
u8 mask_int2; u8 mask_int2;
u8 addr_ihl;
u8 mask_ihl;
struct { struct {
u8 en_addr; u8 en_addr;
u8 en_mask; u8 en_mask;

View 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

View File

@ -37,6 +37,7 @@ enum iio_chan_type {
IIO_VELOCITY, IIO_VELOCITY,
IIO_CONCENTRATION, IIO_CONCENTRATION,
IIO_RESISTANCE, IIO_RESISTANCE,
IIO_PH,
}; };
enum iio_modifier { enum iio_modifier {