Commit 3ceefa3f authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman

Merge tag 'iio-for-4.19b' of...

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

Jonathan writes:

Second set of IIO new device support, features and cleanups.

There are also a couple of fixes that can wait for the coming merge
window.

Core new features

* Support for phase channels (used in time of flight sensors amongst
  other things)
* Support for deep UV light channel modifier.

New Device Support

* AD4758 DAC
  - New driver and dt bindings.
* adxl345
  - Support the adxl375 +-200g part which is register compatible.
* isl29501 Time of flight sensor.
  - New driver
* meson-saradc
  - Support the Meson8m2 Socs - right now this is just an ID, but there will
    be additional difference in future.
* mpu6050
  - New ID for 6515 variant.
* si1133 UV sensor.
  - New driver
* Spreadtrum SC27xx PMIC ADC
  - New driver and dt bindings.

Features

* adxl345
  - Add calibration offset readback and writing.
  - Add sampling frequency control.

Fixes and Cleanups

* ad5933
  - Use a macro for the channel definition to reduce duplication.
* ad9523
  - Replace use of core mlock with a local lock. Part of ongoing efforts
    to avoid confusing the purpose of mlock which is only about iio core
    state changes.
  - Fix displayed phase which was out by a factor of 10.
* adxl345
  - Add a link to the datasheet.
  - Rework the use of the address field in the chan_spec structures to
    allow addition of more per channel information.
* adis imu
  - Mark switch fall throughs.
* at91-sama5d2
  - Fix some casting on big endian systems.
* bmp280
  - Drop some DT elements that aren't used and should mostly be done from
    userspace rather than in DT.
* hx711
  - add clock-frequency dt binding and resulting delay to deal with capacitance
    issue on some boards.
  - fix a spurious unit-address in the example.
* ina2xx
  - Avoid a possible kthread_stop with a stale task_struct.
* ltc2632
  - Remove some unused local variables (assigned but value never used).
* max1363
  - Use device_get_match_data to remove some boilerplate.
* mma8452
  - Mark switch fall throughs.
* sca3000
  - Fix a missing return in a switch statement (a bad fallthrough
    previously!)
* sigma-delta-modulator
  - Drop incorrect unit address from the DT example.
* st_accel
  - Use device_get_match_data to drop some boiler plate.
  - Move to probe_new for i2c driver as second parameter not used.
* st_sensors library
  - Use a strlcpy (safe in this case).
* st_lsm6dsx
  - Add some error logging.
* ti-ads7950
  - SPDX
  - Allow simultaneous buffered and polled reads. Needed on a Lego Mindstorms
    EV3 where some channels are used for power supply monitoring at a very low
    rate.
* ti-dac5571
  - Remove an unused variable.
* xadc
  - Drop some dead code.
parents 4a965c5f c5b974be
......@@ -1307,13 +1307,16 @@ What: /sys/.../iio:deviceX/in_intensityY_raw
What: /sys/.../iio:deviceX/in_intensityY_ir_raw
What: /sys/.../iio:deviceX/in_intensityY_both_raw
What: /sys/.../iio:deviceX/in_intensityY_uv_raw
What: /sys/.../iio:deviceX/in_intensityY_duv_raw
KernelVersion: 3.4
Contact: linux-iio@vger.kernel.org
Description:
Unit-less light intensity. Modifiers both and ir indicate
that measurements contain visible and infrared light
components or just infrared light, respectively. Modifier uv indicates
that measurements contain ultraviolet light components.
components or just infrared light, respectively. Modifier
uv indicates that measurements contain ultraviolet light
components. Modifier duv indicates that measurements
contain deep ultraviolet light components.
What: /sys/.../iio:deviceX/in_uvindex_input
KernelVersion: 4.6
......@@ -1675,3 +1678,10 @@ KernelVersion: 4.12
Contact: linux-iio@vger.kernel.org
Description:
Raw counter device counters direction for channel Y.
What: /sys/bus/iio/devices/iio:deviceX/in_phaseY_raw
KernelVersion: 4.18
Contact: linux-iio@vger.kernel.org
Description:
Raw (unscaled) phase difference reading from channel Y
that can be processed to radians.
\ No newline at end of file
What: /sys/bus/iio/devices/iio:deviceX/in_proximity0_agc_gain
What: /sys/bus/iio/devices/iio:deviceX/in_proximity0_agc_gain_bias
KernelVersion: 4.18
Contact: linux-iio@vger.kernel.org
Description:
This sensor has an automatic gain control (agc) loop
which sets the analog signal levels at an optimum
level by controlling programmable gain amplifiers. The
criteria for optimal gain is determined by the sensor.
Return the actual gain value as an integer in [0; 65536]
range when read from.
The agc gain read when measuring crosstalk shall be
written into in_proximity0_agc_gain_bias.
What: /sys/bus/iio/devices/iio:deviceX/in_proximity0_calib_phase_temp_a
What: /sys/bus/iio/devices/iio:deviceX/in_proximity0_calib_phase_temp_b
What: /sys/bus/iio/devices/iio:deviceX/in_proximity0_calib_phase_light_a
What: /sys/bus/iio/devices/iio:deviceX/in_proximity0_calib_phase_light_b
KernelVersion: 4.18
Contact: linux-iio@vger.kernel.org
Description:
The sensor is able to perform correction of distance
measurements due to changing temperature and ambient
light conditions. It can be programmed to correct for
a second order error polynomial.
Phase data has to be collected when temperature and
ambient light are modulated independently.
Then a least squares curve fit to a second order
polynomial has to be generated from the data. The
resultant curves have the form ax^2 + bx + c.
From those two curves, a and b coefficients shall be
stored in in_proximity0_calib_phase_temp_a and
in_proximity0_calib_phase_temp_b for temperature and
in in_proximity0_calib_phase_light_a and
in_proximity0_calib_phase_light_b for ambient light.
Those values must be integer in [0; 8355840] range.
Finally, the c constant is set by the sensor
internally.
The value stored in sensor is displayed when read from.
What: /sys/bus/iio/devices/iio:deviceX/in_intensity_ir_small_raw
KernelVersion: 4.18
Contact: linux-iio@vger.kernel.org
Description:
Unit-less infrared intensity. The intensity is measured from 1
dark photodiode. "small" indicate the surface area capturing
infrared.
What: /sys/bus/iio/devices/iio:deviceX/in_intensity_ir_large_raw
KernelVersion: 4.18
Contact: linux-iio@vger.kernel.org
Description:
Unit-less infrared intensity. The intensity is measured from 4
dark photodiodes. "large" indicate the surface area capturing
infrared.
What: /sys/bus/iio/devices/iio:deviceX/in_intensity_large_raw
KernelVersion: 4.18
Contact: linux-iio@vger.kernel.org
Description:
Unit-less light intensity with more diodes.
Analog Devices ADXL345 3-Axis, +/-(2g/4g/8g/16g) Digital Accelerometer
Analog Devices ADXL345/ADXL375 3-Axis Digital Accelerometers
http://www.analog.com/en/products/mems/accelerometers/adxl345.html
http://www.analog.com/en/products/sensors-mems/accelerometers/adxl375.html
Required properties:
- compatible : should be "adi,adxl345"
- compatible : should be one of
"adi,adxl345"
"adi,adxl375"
- reg : the I2C address or SPI chip select number of the sensor
Required properties for SPI bus usage:
......
......@@ -4,6 +4,7 @@ Required properties:
- compatible: depending on the SoC this should be one of:
- "amlogic,meson8-saradc" for Meson8
- "amlogic,meson8b-saradc" for Meson8b
- "amlogic,meson8m2-saradc" for Meson8m2
- "amlogic,meson-gxbb-saradc" for GXBB
- "amlogic,meson-gxl-saradc" for GXL
- "amlogic,meson-gxm-saradc" for GXM
......
......@@ -8,11 +8,17 @@ Required properties:
See Documentation/devicetree/bindings/gpio/gpio.txt
- avdd-supply: Definition of the regulator used as analog supply
Optional properties:
- clock-frequency: Frequency of PD_SCK in Hz
Minimum value allowed is 10 kHz because of maximum
high time of 50 microseconds.
Example:
weight@0 {
weight {
compatible = "avia,hx711";
sck-gpios = <&gpio3 10 GPIO_ACTIVE_HIGH>;
dout-gpios = <&gpio0 7 GPIO_ACTIVE_HIGH>;
avdd-suppy = <&avdd>;
clock-frequency = <100000>;
};
......@@ -7,7 +7,7 @@ Required properties:
Example node:
ads1202: adc@0 {
ads1202: adc {
compatible = "sd-modulator";
#io-channel-cells = <0>;
};
Spreadtrum SC27XX series PMICs ADC binding
Required properties:
- compatible: Should be one of the following.
"sprd,sc2720-adc"
"sprd,sc2721-adc"
"sprd,sc2723-adc"
"sprd,sc2730-adc"
"sprd,sc2731-adc"
- reg: The address offset of ADC controller.
- interrupt-parent: The interrupt controller.
- interrupts: The interrupt number for the ADC device.
- #io-channel-cells: Number of cells in an IIO specifier.
- hwlocks: Reference to a phandle of a hwlock provider node.
Example:
sc2731_pmic: pmic@0 {
compatible = "sprd,sc2731";
reg = <0>;
spi-max-frequency = <26000000>;
interrupts = <GIC_SPI 31 IRQ_TYPE_LEVEL_HIGH>;
interrupt-controller;
#interrupt-cells = <2>;
#address-cells = <1>;
#size-cells = <0>;
pmic_adc: adc@480 {
compatible = "sprd,sc2731-adc";
reg = <0x480>;
interrupt-parent = <&sc2731_pmic>;
interrupts = <0 IRQ_TYPE_LEVEL_HIGH>;
#io-channel-cells = <1>;
hwlocks = <&hwlock 4>;
};
};
Analog Devices AD5758 DAC device driver
Required properties for the AD5758:
- compatible: Must be "adi,ad5758"
- reg: SPI chip select number for the device
- spi-max-frequency: Max SPI frequency to use (< 50000000)
- spi-cpha: is the only mode that is supported
Required properties:
- adi,dc-dc-mode: Mode of operation of the dc-to-dc converter
Dynamic Power Control (DPC)
In this mode, the AD5758 circuitry senses the output
voltage and dynamically regulates the supply voltage,
VDPC+, to meet compliance requirements plus an optimized
headroom voltage for the output buffer.
Programmable Power Control (PPC)
In this mode, the VDPC+ voltage is user-programmable to
a fixed level that needs to accommodate the maximum output
load required.
The output of the DAC core is either converted to a
current or voltage output at the VIOUT pin. Only one mode
can be enabled at any one time.
The following values are currently supported:
* 1: DPC current mode
* 2: DPC voltage mode
* 3: PPC current mode
Depending on the selected output mode (voltage or current) one of the
two properties must
be present:
- adi,range-microvolt: Voltage output range
The array of voltage output ranges must contain two fields:
* <0 5000000>: 0 V to 5 V voltage range
* <0 10000000>: 0 V to 10 V voltage range
* <(-5000000) 5000000>: ±5 V voltage range
* <(-10000000) 10000000>: ±10 V voltage range
- adi,range-microamp: Current output range
The array of current output ranges must contain two fields:
* <0 20000>: 0 mA to 20 mA current range
* <0 24000>: 0 mA to 24 mA current range
* <4 24000>: 4 mA to 20 mA current range
* <(-20000) 20000>: ±20 mA current range
* <(-24000) 24000>: ±24 mA current range
* <(-1000) 22000>: −1 mA to +22 mA current range
Optional properties:
- adi,dc-dc-ilim-microamp: The dc-to-dc converter current limit
The following values are currently supported [uA]:
* 150000
* 200000
* 250000
* 300000
* 350000
* 400000
- adi,slew-time-us: The time it takes for the output to reach the
full scale [uS]
The supported range is between 133us up to 1023984375us
AD5758 Example:
dac@0 {
compatible = "adi,ad5758";
reg = <0>;
spi-max-frequency = <1000000>;
spi-cpha;
adi,dc-dc-mode = <2>;
adi,range-microvolt = <0 10000000>;
adi,dc-dc-ilim-microamp = <200000>;
adi,slew-time-us = <125000>;
};
......@@ -6,6 +6,7 @@ Required properties:
- compatible : should be one of
"invensense,mpu6050"
"invensense,mpu6500"
"invensense,mpu6515"
"invensense,mpu9150"
"invensense,mpu9250"
"invensense,mpu9255"
......
* ISL29501 Time-of-flight sensor.
Required properties:
- compatible : should be "renesas,isl29501"
- reg : the I2C address of the sensor
Example:
isl29501@57 {
compatible = "renesas,isl29501";
reg = <0x57>;
};
......@@ -8,10 +8,6 @@ Required properties:
"bosch,bme280"
Optional properties:
- chip-id: configurable chip id for non-default chip revisions
- temp-measurement-period: temperature measurement period (milliseconds)
- default-oversampling: default oversampling value to be used at startup,
value range is 0-3 with rising sensitivity.
- interrupt-parent: should be the phandle for the interrupt controller
- interrupts: interrupt mapping for IRQ
- reset-gpios: a GPIO line handling reset of the sensor: as the line is
......@@ -24,9 +20,6 @@ Example:
pressure@77 {
compatible = "bosch,bmp085";
reg = <0x77>;
chip-id = <10>;
temp-measurement-period = <100>;
default-oversampling = <2>;
interrupt-parent = <&gpio0>;
interrupts = <25 IRQ_TYPE_EDGE_RISING>;
reset-gpios = <&gpio0 26 GPIO_ACTIVE_LOW>;
......
......@@ -796,6 +796,14 @@ M: Michael Hanselmann <linux-kernel@hansmi.ch>
S: Supported
F: drivers/macintosh/ams/
ANALOG DEVICES INC AD5758 DRIVER
M: Stefan Popa <stefan.popa@analog.com>
L: linux-iio@vger.kernel.org
W: http://ez.analog.com/community/linux-device-drivers
S: Supported
F: drivers/iio/dac/ad5758.c
F: Documentation/devicetree/bindings/iio/dac/ad5758.txt
ANALOG DEVICES INC AD5686 DRIVER
M: Stefan Popa <stefan.popa@analog.com>
L: linux-pm@vger.kernel.org
......
......@@ -40,7 +40,7 @@ config ADXL345_I2C
select REGMAP_I2C
help
Say Y here if you want to build support for the Analog Devices
ADXL345 3-axis digital accelerometer.
ADXL345 or ADXL375 3-axis digital accelerometer.
To compile this driver as a module, choose M here: the module
will be called adxl345_i2c and you will also get adxl345_core
......@@ -54,7 +54,7 @@ config ADXL345_SPI
select REGMAP_SPI
help
Say Y here if you want to build support for the Analog Devices
ADXL345 3-axis digital accelerometer.
ADXL345 or ADXL375 3-axis digital accelerometer.
To compile this driver as a module, choose M here: the module
will be called adxl345_spi and you will also get adxl345_core
......
......@@ -11,8 +11,13 @@
#ifndef _ADXL345_H_
#define _ADXL345_H_
enum adxl345_device_type {
ADXL345,
ADXL375,
};
int adxl345_core_probe(struct device *dev, struct regmap *regmap,
const char *name);
enum adxl345_device_type type, const char *name);
int adxl345_core_remove(struct device *dev);
#endif /* _ADXL345_H_ */
......@@ -6,21 +6,35 @@
* 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.analog.com/media/en/technical-documentation/data-sheets/ADXL345.pdf
*/
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include "adxl345.h"
#define ADXL345_REG_DEVID 0x00
#define ADXL345_REG_OFSX 0x1e
#define ADXL345_REG_OFSY 0x1f
#define ADXL345_REG_OFSZ 0x20
#define ADXL345_REG_OFS_AXIS(index) (ADXL345_REG_OFSX + (index))
#define ADXL345_REG_BW_RATE 0x2C
#define ADXL345_REG_POWER_CTL 0x2D
#define ADXL345_REG_DATA_FORMAT 0x31
#define ADXL345_REG_DATAX0 0x32
#define ADXL345_REG_DATAY0 0x34
#define ADXL345_REG_DATAZ0 0x36
#define ADXL345_REG_DATA_AXIS(index) \
(ADXL345_REG_DATAX0 + (index) * sizeof(__le16))
#define ADXL345_BW_RATE GENMASK(3, 0)
#define ADXL345_BASE_RATE_NANO_HZ 97656250LL
#define NHZ_PER_HZ 1000000000LL
#define ADXL345_POWER_CTL_MEASURE BIT(3)
#define ADXL345_POWER_CTL_STANDBY 0x00
......@@ -42,24 +56,33 @@
*/
static const int adxl345_uscale = 38300;
/*
* The Datasheet lists a resolution of Resolution is ~49 mg per LSB. That's
* ~480mm/s**2 per LSB.
*/
static const int adxl375_uscale = 480000;
struct adxl345_data {
struct regmap *regmap;
u8 data_range;
enum adxl345_device_type type;
};
#define ADXL345_CHANNEL(reg, axis) { \
#define ADXL345_CHANNEL(index, axis) { \
.type = IIO_ACCEL, \
.modified = 1, \
.channel2 = IIO_MOD_##axis, \
.address = reg, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
.address = index, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
BIT(IIO_CHAN_INFO_CALIBBIAS), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
BIT(IIO_CHAN_INFO_SAMP_FREQ), \
}
static const struct iio_chan_spec adxl345_channels[] = {
ADXL345_CHANNEL(ADXL345_REG_DATAX0, X),
ADXL345_CHANNEL(ADXL345_REG_DATAY0, Y),
ADXL345_CHANNEL(ADXL345_REG_DATAZ0, Z),
ADXL345_CHANNEL(0, X),
ADXL345_CHANNEL(1, Y),
ADXL345_CHANNEL(2, Z),
};
static int adxl345_read_raw(struct iio_dev *indio_dev,
......@@ -67,7 +90,9 @@ static int adxl345_read_raw(struct iio_dev *indio_dev,
int *val, int *val2, long mask)
{
struct adxl345_data *data = iio_priv(indio_dev);
__le16 regval;
__le16 accel;
long long samp_freq_nhz;
unsigned int regval;
int ret;
switch (mask) {
......@@ -77,29 +102,117 @@ static int adxl345_read_raw(struct iio_dev *indio_dev,
* ADXL345_REG_DATA(X0/Y0/Z0) contain the least significant byte
* and ADXL345_REG_DATA(X0/Y0/Z0) + 1 the most significant byte
*/
ret = regmap_bulk_read(data->regmap, chan->address, &regval,
sizeof(regval));
ret = regmap_bulk_read(data->regmap,
ADXL345_REG_DATA_AXIS(chan->address),
&accel, sizeof(accel));
if (ret < 0)
return ret;
*val = sign_extend32(le16_to_cpu(regval), 12);
*val = sign_extend32(le16_to_cpu(accel), 12);
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
*val = 0;
*val2 = adxl345_uscale;
switch (data->type) {
case ADXL345:
*val2 = adxl345_uscale;
break;
case ADXL375:
*val2 = adxl375_uscale;
break;
}
return IIO_VAL_INT_PLUS_MICRO;
case IIO_CHAN_INFO_CALIBBIAS:
ret = regmap_read(data->regmap,
ADXL345_REG_OFS_AXIS(chan->address), &regval);
if (ret < 0)
return ret;
/*
* 8-bit resolution at +/- 2g, that is 4x accel data scale
* factor
*/
*val = sign_extend32(regval, 7) * 4;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SAMP_FREQ:
ret = regmap_read(data->regmap, ADXL345_REG_BW_RATE, &regval);
if (ret < 0)
return ret;
samp_freq_nhz = ADXL345_BASE_RATE_NANO_HZ <<
(regval & ADXL345_BW_RATE);
*val = div_s64_rem(samp_freq_nhz, NHZ_PER_HZ, val2);
return IIO_VAL_INT_PLUS_NANO;
}
return -EINVAL;
}
static int adxl345_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
struct adxl345_data *data = iio_priv(indio_dev);
s64 n;
switch (mask) {
case IIO_CHAN_INFO_CALIBBIAS:
/*
* 8-bit resolution at +/- 2g, that is 4x accel data scale
* factor
*/
return regmap_write(data->regmap,
ADXL345_REG_OFS_AXIS(chan->address),
val / 4);
case IIO_CHAN_INFO_SAMP_FREQ:
n = div_s64(val * NHZ_PER_HZ + val2, ADXL345_BASE_RATE_NANO_HZ);
return regmap_update_bits(data->regmap, ADXL345_REG_BW_RATE,
ADXL345_BW_RATE,
clamp_val(ilog2(n), 0,
ADXL345_BW_RATE));
}
return -EINVAL;
}
static int adxl345_write_raw_get_fmt(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
long mask)
{
switch (mask) {
case IIO_CHAN_INFO_CALIBBIAS:
return IIO_VAL_INT;
case IIO_CHAN_INFO_SAMP_FREQ:
return IIO_VAL_INT_PLUS_NANO;
default:
return -EINVAL;
}
}
static IIO_CONST_ATTR_SAMP_FREQ_AVAIL(
"0.09765625 0.1953125 0.390625 0.78125 1.5625 3.125 6.25 12.5 25 50 100 200 400 800 1600 3200"
);
static struct attribute *adxl345_attrs[] = {
&iio_const_attr_sampling_frequency_available.dev_attr.attr,
NULL,
};
static const struct attribute_group adxl345_attrs_group = {
.attrs = adxl345_attrs,
};
static const struct iio_info adxl345_info = {
.attrs = &adxl345_attrs_group,
.read_raw = adxl345_read_raw,
.write_raw = adxl345_write_raw,
.write_raw_get_fmt = adxl345_write_raw_get_fmt,
};
int adxl345_core_probe(struct device *dev, struct regmap *regmap,
const char *name)
enum adxl345_device_type type, const char *name)
{
struct adxl345_data *data;
struct iio_dev *indio_dev;
......@@ -125,6 +238,7 @@ int adxl345_core_probe(struct device *dev, struct regmap *regmap,
data = iio_priv(indio_dev);
dev_set_drvdata(dev, indio_dev);
data->regmap = regmap;
data->type = type;
/* Enable full-resolution mode */
data->data_range = ADXL345_DATA_FORMAT_FULL_RES;
......
......@@ -34,7 +34,8 @@ static int adxl345_i2c_probe(struct i2c_client *client,
return PTR_ERR(regmap);
}
return adxl345_core_probe(&client->dev, regmap, id ? id->name : NULL);
return adxl345_core_probe(&client->dev, regmap, id->driver_data,
id ? id->name : NULL);
}
static int adxl345_i2c_remove(struct i2c_client *client)
......@@ -43,7 +44,8 @@ static int adxl345_i2c_remove(struct i2c_client *client)
}
static const struct i2c_device_id adxl345_i2c_id[] = {
{ "adxl345", 0 },
{ "adxl345", ADXL345 },
{ "adxl375", ADXL375 },
{ }
};
......@@ -51,6 +53,7 @@ MODULE_DEVICE_TABLE(i2c, adxl345_i2c_id);
static const struct of_device_id adxl345_of_match[] = {
{ .compatible = "adi,adxl345" },
{ .compatible = "adi,adxl375" },
{ },
};
......
......@@ -42,7 +42,7 @@ static int adxl345_spi_probe(struct spi_device *spi)
return PTR_ERR(regmap);
}
return adxl345_core_probe(&spi->dev, regmap, id->name);
return adxl345_core_probe(&spi->dev, regmap, id->driver_data, id->name);
}
static int adxl345_spi_remove(struct spi_device *spi)
......@@ -51,7 +51,8 @@ static int adxl345_spi_remove(struct spi_device *spi)
}
static const struct spi_device_id adxl345_spi_id[] = {
{ "adxl345", 0 },
{ "adxl345", ADXL345 },
{ "adxl375", ADXL375 },
{ }
};
......@@ -59,6 +60,7 @@ MODULE_DEVICE_TABLE(spi, adxl345_spi_id);
static const struct of_device_id adxl345_of_match[] = {
{ .compatible = "adi,adxl345" },
{ .compatible = "adi,adxl375" },
{ },
};
......
......@@ -1547,6 +1547,7 @@ static int mma8452_probe(struct i2c_client *client,
case FXLS8471_DEVICE_ID:
if (ret == data->chip_info->chip_id)
break;
/* else: fall through */
default:
return -ENODEV;
}
......
......@@ -797,6 +797,7 @@ static int sca3000_write_raw(struct iio_dev *indio_dev,
mutex_lock(&st->lock);
ret = sca3000_write_3db_freq(st, val);
mutex_unlock(&st->lock);
return ret;
default:
return -EINVAL;
}
......
......@@ -14,8 +14,8 @@
#include <linux/acpi.h>
#include <linux/i2c.h>
#include <linux/iio/iio.h>
#include <linux/property.h>
#include <linux/iio/common/st_sensors.h>
#include <linux/iio/common/st_sensors_i2c.h>
#include "st_accel.h"
......@@ -107,8 +107,8 @@ MODULE_DEVICE_TABLE(of, st_accel_of_match);
#ifdef CONFIG_ACPI
static const struct acpi_device_id st_accel_acpi_match[] = {
{"SMO8840", LNG2DM},
{"SMO8A90", LNG2DM},
{"SMO8840", (kernel_ulong_t)LNG2DM_ACCEL_DEV_NAME},
{"SMO8A90", (kernel_ulong_t)LNG2DM_ACCEL_DEV_NAME},
{ },
};
MODULE_DEVICE_TABLE(acpi, st_accel_acpi_match);
......@@ -117,33 +117,33 @@ MODULE_DEVICE_TABLE(acpi, st_accel_acpi_match);
#endif
static const struct i2c_device_id st_accel_id_table[] = {
{ LSM303DLH_ACCEL_DEV_NAME, LSM303DLH },
{ LSM303DLHC_ACCEL_DEV_NAME, LSM303DLHC },
{ LIS3DH_ACCEL_DEV_NAME, LIS3DH },
{ LSM330D_ACCEL_DEV_NAME, LSM330D },
{ LSM330DL_ACCEL_DEV_NAME, LSM330DL },
{ LSM330DLC_ACCEL_DEV_NAME, LSM330DLC },
{ LIS331DLH_ACCEL_DEV_NAME, LIS331DLH },
{ LSM303DL_ACCEL_DEV_NAME, LSM303DL },
{ LSM303DLM_ACCEL_DEV_NAME, LSM303DLM },
{ LSM330_ACCEL_DEV_NAME, LSM330 },
{ LSM303AGR_ACCEL_DEV_NAME, LSM303AGR },
{ LIS2DH12_ACCEL_DEV_NAME, LIS2DH12 },
{ LIS3L02DQ_ACCEL_DEV_NAME, LIS3L02DQ },
{ LNG2DM_ACCEL_DEV_NAME, LNG2DM },
{ H3LIS331DL_ACCEL_DEV_NAME, H3LIS331DL },
{ LIS331DL_ACCEL_DEV_NAME, LIS331DL },
{ LIS3LV02DL_ACCEL_DEV_NAME, LIS3LV02DL },
{ LIS2DW12_ACCEL_DEV_NAME, LIS2DW12 },
{ LSM303DLH_ACCEL_DEV_NAME },
{ LSM303DLHC_ACCEL_DEV_NAME },
{ LIS3DH_ACCEL_DEV_NAME },
{ LSM330D_ACCEL_DEV_NAME },
{ LSM330DL_ACCEL_DEV_NAME },
{ LSM330DLC_ACCEL_DEV_NAME },
{ LIS331DLH_ACCEL_DEV_NAME },
{ LSM303DL_ACCEL_DEV_NAME },
{ LSM303DLM_ACCEL_DEV_NAME },
{ LSM330_ACCEL_DEV_NAME },
{ LSM303AGR_ACCEL_DEV_NAME },
{ LIS2DH12_ACCEL_DEV_NAME },
{ LIS3L02DQ_ACCEL_DEV_NAME },
{ LNG2DM_ACCEL_DEV_NAME },
{ H3LIS331DL_ACCEL_DEV_NAME },
{ LIS331DL_ACCEL_DEV_NAME },
{ LIS3LV02DL_ACCEL_DEV_NAME },
{ LIS2DW12_ACCEL_DEV_NAME },
{},
};
MODULE_DEVICE_TABLE(i2c, st_accel_id_table);
static int st_accel_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
static int st_accel_i2c_probe(struct i2c_client *client)
{
struct iio_dev *indio_dev;
struct st_sensor_data *adata;
const char *match;
int ret;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*adata));
......@@ -152,19 +152,9 @@ static int st_accel_i2c_probe(struct i2c_client *client,
adata = iio_priv(indio_dev);
if (client->dev.of_node) {
st_sensors_of_name_probe(&client->dev, st_accel_of_match,
client->name, sizeof(client->name));
} else if (ACPI_HANDLE(&client->dev)) {
ret = st_sensors_match_acpi_device(&client->dev);
if ((ret < 0) || (ret >= ST_ACCEL_MAX))
return -ENODEV;
strlcpy(client->name, st_accel_id_table[ret].name,
sizeof(client->name));
} else if (!id)
return -ENODEV;
match = device_get_match_data(&client->dev);
if (match)
strlcpy(client->name, match, sizeof(client->name));
st_sensors_i2c_configure(indio_dev, client, adata);
......@@ -188,7 +178,7 @@ static struct i2c_driver st_accel_driver = {
.of_match_table = of_match_ptr(st_accel_of_match),
.acpi_match_table = ACPI_PTR(st_accel_acpi_match),
},
.probe = st_accel_i2c_probe,
.probe_new = st_accel_i2c_probe,
.remove = st_accel_i2c_remove,
.id_table = st_accel_id_table,
};
......
......@@ -620,6 +620,16 @@ config ROCKCHIP_SARADC
To compile this driver as a module, choose M here: the
module will be called rockchip_saradc.
config SC27XX_ADC
tristate "Spreadtrum SC27xx series PMICs ADC"
depends on MFD_SC27XX_PMIC || COMPILE_TEST
help
Say yes here to build support for the integrated ADC inside the
Spreadtrum SC27xx series PMICs.
This driver can also be built as a module. If so, the module
will be called sc27xx_adc.
config SPEAR_ADC
tristate "ST SPEAr ADC"
depends on PLAT_SPEAR || COMPILE_TEST
......
......@@ -59,6 +59,7 @@ obj-$(CONFIG_QCOM_SPMI_VADC) += qcom-spmi-vadc.o
obj-$(CONFIG_QCOM_PM8XXX_XOADC) += qcom-pm8xxx-xoadc.o
obj-$(CONFIG_RCAR_GYRO_ADC) += rcar-gyroadc.o
obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
obj-$(CONFIG_SC27XX_ADC) += sc27xx_adc.o
obj-$(CONFIG_SPEAR_ADC) += spear_adc.o
obj-$(CONFIG_STX104) += stx104.o
obj-$(CONFIG_SUN4I_GPADC) += sun4i-gpadc-iio.o
......
......@@ -1296,6 +1296,7 @@ static int at91_adc_read_info_raw(struct iio_dev *indio_dev,
{
struct at91_adc_state *st = iio_priv(indio_dev);
u32 cor = 0;
u16 tmp_val;
int ret;
/*
......@@ -1309,7 +1310,8 @@ static int at91_adc_read_info_raw(struct iio_dev *indio_dev,
mutex_lock(&st->lock);
ret = at91_adc_read_position(st, chan->channel,
(u16 *)val);
&tmp_val);
*val = tmp_val;
mutex_unlock(&st->lock);
iio_device_release_direct_mode(indio_dev);
......@@ -1322,7 +1324,8 @@ static int at91_adc_read_info_raw(struct iio_dev *indio_dev,
mutex_lock(&st->lock);
ret = at91_adc_read_pressure(st, chan->channel,
(u16 *)val);
&tmp_val);
*val = tmp_val;
mutex_unlock(&st->lock);
iio_device_release_direct_mode(indio_dev);
......
......@@ -97,6 +97,14 @@ struct hx711_data {
* 2x32-bit channel + 64-bit timestamp
*/
u32 buffer[4];
/*
* delay after a rising edge on SCK until the data is ready DOUT
* this is dependent on the hx711 where the datasheet tells a
* maximum value of 100 ns
* but also on potential parasitic capacities on the wiring
*/
u32 data_ready_delay_ns;
u32 clock_frequency;
};
static int hx711_cycle(struct hx711_data *hx711_data)
......@@ -110,6 +118,14 @@ static int hx711_cycle(struct hx711_data *hx711_data)
*/
preempt_disable();
gpiod_set_value(hx711_data->gpiod_pd_sck, 1);
/*
* wait until DOUT is ready
* it turned out that parasitic capacities are extending the time
* until DOUT has reached it's value
*/
ndelay(hx711_data->data_ready_delay_ns);
val = gpiod_get_value(hx711_data->gpiod_dout);
/*
* here we are not waiting for 0.2 us as suggested by the datasheet,
......@@ -120,6 +136,12 @@ static int hx711_cycle(struct hx711_data *hx711_data)
gpiod_set_value(hx711_data->gpiod_pd_sck, 0);
preempt_enable();
/*
* make it a square wave for addressing cases with capacitance on
* PC_SCK
*/
ndelay(hx711_data->data_ready_delay_ns);
return val;
}
......@@ -458,6 +480,7 @@ static const struct iio_chan_spec hx711_chan_spec[] = {
static int hx711_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
struct hx711_data *hx711_data;
struct iio_dev *indio_dev;
int ret;
......@@ -530,6 +553,22 @@ static int hx711_probe(struct platform_device *pdev)
hx711_data->gain_set = 128;
hx711_data->gain_chan_a = 128;
hx711_data->clock_frequency = 400000;
ret = of_property_read_u32(np, "clock-frequency",
&hx711_data->clock_frequency);
/*
* datasheet says the high level of PD_SCK has a maximum duration
* of 50 microseconds
*/
if (hx711_data->clock_frequency < 20000) {
dev_warn(dev, "clock-frequency too low - assuming 400 kHz\n");
hx711_data->clock_frequency = 400000;
}
hx711_data->data_ready_delay_ns =
1000000000 / hx711_data->clock_frequency;
platform_set_drvdata(pdev, indio_dev);
indio_dev->name = "hx711";
......
......@@ -30,6 +30,7 @@
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/regmap.h>
#include <linux/sched/task.h>
#include <linux/util_macros.h>
#include <linux/platform_data/ina2xx.h>
......@@ -826,6 +827,7 @@ static int ina2xx_buffer_enable(struct iio_dev *indio_dev)
{
struct ina2xx_chip_info *chip = iio_priv(indio_dev);
unsigned int sampling_us = SAMPLING_PERIOD(chip);
struct task_struct *task;
dev_dbg(&indio_dev->dev, "Enabling buffer w/ scan_mask %02x, freq = %d, avg =%u\n",
(unsigned int)(*indio_dev->active_scan_mask),
......@@ -835,11 +837,17 @@ static int ina2xx_buffer_enable(struct iio_dev *indio_dev)
dev_dbg(&indio_dev->dev, "Async readout mode: %d\n",
chip->allow_async_readout);
chip->task = kthread_run(ina2xx_capture_thread, (void *)indio_dev,
"%s:%d-%uus", indio_dev->name, indio_dev->id,
sampling_us);
task = kthread_create(ina2xx_capture_thread, (void *)indio_dev,
"%s:%d-%uus", indio_dev->name, indio_dev->id,
sampling_us);
if (IS_ERR(task))
return PTR_ERR(task);
get_task_struct(task);
wake_up_process(task);
chip->task = task;
return PTR_ERR_OR_ZERO(chip->task);
return 0;
}
static int ina2xx_buffer_disable(struct iio_dev *indio_dev)
......@@ -848,6 +856,7 @@ static int ina2xx_buffer_disable(struct iio_dev *indio_dev)
if (chip->task) {
kthread_stop(chip->task);
put_task_struct(chip->task);
chip->task = NULL;
}
......
......@@ -1577,7 +1577,6 @@ static int max1363_probe(struct i2c_client *client,
struct max1363_state *st;
struct iio_dev *indio_dev;
struct regulator *vref;
const struct of_device_id *match;
indio_dev = devm_iio_device_alloc(&client->dev,
sizeof(struct max1363_state));
......@@ -1604,11 +1603,8 @@ static int max1363_probe(struct i2c_client *client,
/* this is only used for device removal purposes */
i2c_set_clientdata(client, indio_dev);
match = of_match_device(of_match_ptr(max1363_of_match),
&client->dev);
if (match)
st->chip_info = of_device_get_match_data(&client->dev);
else
st->chip_info = of_device_get_match_data(&client->dev);
if (!st->chip_info)
st->chip_info = &max1363_chip_info_tbl[id->driver_data];
st->client = client;
......
......@@ -922,6 +922,11 @@ static const struct meson_sar_adc_data meson_sar_adc_meson8b_data = {
.name = "meson-meson8b-saradc",
};
static const struct meson_sar_adc_data meson_sar_adc_meson8m2_data = {
.param = &meson_sar_adc_meson8_param,
.name = "meson-meson8m2-saradc",
};
static const struct meson_sar_adc_data meson_sar_adc_gxbb_data = {
.param = &meson_sar_adc_gxbb_param,
.name = "meson-gxbb-saradc",
......@@ -951,6 +956,10 @@ static const struct of_device_id meson_sar_adc_of_match[] = {
.compatible = "amlogic,meson8b-saradc",
.data = &meson_sar_adc_meson8b_data,
},
{
.compatible = "amlogic,meson8m2-saradc",
.data = &meson_sar_adc_meson8m2_data,
},
{
.compatible = "amlogic,meson-gxbb-saradc",
.data = &meson_sar_adc_gxbb_data,
......
// SPDX-License-Identifier: GPL-2.0
// Copyright (C) 2018 Spreadtrum Communications Inc.
#include <linux/hwspinlock.h>
#include <linux/iio/iio.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
/* PMIC global registers definition */
#define SC27XX_MODULE_EN 0xc08
#define SC27XX_MODULE_ADC_EN BIT(5)
#define SC27XX_ARM_CLK_EN 0xc10
#define SC27XX_CLK_ADC_EN BIT(5)
#define SC27XX_CLK_ADC_CLK_EN BIT(6)
/* ADC controller registers definition */
#define SC27XX_ADC_CTL 0x0
#define SC27XX_ADC_CH_CFG 0x4
#define SC27XX_ADC_DATA 0x4c
#define SC27XX_ADC_INT_EN 0x50
#define SC27XX_ADC_INT_CLR 0x54
#define SC27XX_ADC_INT_STS 0x58
#define SC27XX_ADC_INT_RAW 0x5c
/* Bits and mask definition for SC27XX_ADC_CTL register */
#define SC27XX_ADC_EN BIT(0)
#define SC27XX_ADC_CHN_RUN BIT(1)
#define SC27XX_ADC_12BIT_MODE BIT(2)
#define SC27XX_ADC_RUN_NUM_MASK GENMASK(7, 4)
#define SC27XX_ADC_RUN_NUM_SHIFT 4
/* Bits and mask definition for SC27XX_ADC_CH_CFG register */
#define SC27XX_ADC_CHN_ID_MASK GENMASK(4, 0)
#define SC27XX_ADC_SCALE_MASK GENMASK(10, 8)
#define SC27XX_ADC_SCALE_SHIFT 8
/* Bits definitions for SC27XX_ADC_INT_EN registers */
#define SC27XX_ADC_IRQ_EN BIT(0)
/* Bits definitions for SC27XX_ADC_INT_CLR registers */
#define SC27XX_ADC_IRQ_CLR BIT(0)
/* Mask definition for SC27XX_ADC_DATA register */
#define SC27XX_ADC_DATA_MASK GENMASK(11, 0)
/* Timeout (ms) for the trylock of hardware spinlocks */
#define SC27XX_ADC_HWLOCK_TIMEOUT 5000
/* Maximum ADC channel number */
#define SC27XX_ADC_CHANNEL_MAX 32
/* ADC voltage ratio definition */
#define SC27XX_VOLT_RATIO(n, d) \
(((n) << SC27XX_RATIO_NUMERATOR_OFFSET) | (d))
#define SC27XX_RATIO_NUMERATOR_OFFSET 16
#define SC27XX_RATIO_DENOMINATOR_MASK GENMASK(15, 0)
struct sc27xx_adc_data {
struct device *dev;
struct regmap *regmap;
/*
* One hardware spinlock to synchronize between the multiple
* subsystems which will access the unique ADC controller.
*/
struct hwspinlock *hwlock;
struct completion completion;
int channel_scale[SC27XX_ADC_CHANNEL_MAX];
u32 base;
int value;
int irq;
};
struct sc27xx_adc_linear_graph {
int volt0;
int adc0;
int volt1;
int adc1;
};
/*
* According to the datasheet, we can convert one ADC value to one voltage value
* through 2 points in the linear graph. If the voltage is less than 1.2v, we
* should use the small-scale graph, and if more than 1.2v, we should use the
* big-scale graph.
*/
static const struct sc27xx_adc_linear_graph big_scale_graph = {
4200, 3310,
3600, 2832,
};
static const struct sc27xx_adc_linear_graph small_scale_graph = {
1000, 3413,
100, 341,
};
static int sc27xx_adc_get_ratio(int channel, int scale)
{
switch (channel) {
case 1:
case 2:
case 3:
case 4:
return scale ? SC27XX_VOLT_RATIO(400, 1025) :
SC27XX_VOLT_RATIO(1, 1);
case 5:
return SC27XX_VOLT_RATIO(7, 29);
case 6:
return SC27XX_VOLT_RATIO(375, 9000);
case 7:
case 8:
return scale ? SC27XX_VOLT_RATIO(100, 125) :
SC27XX_VOLT_RATIO(1, 1);
case 19:
return SC27XX_VOLT_RATIO(1, 3);
default:
return SC27XX_VOLT_RATIO(1, 1);
}
return SC27XX_VOLT_RATIO(1, 1);
}
static int sc27xx_adc_read(struct sc27xx_adc_data *data, int channel,
int scale, int *val)
{
int ret;
u32 tmp;
reinit_completion(&data->completion);
ret = hwspin_lock_timeout_raw(data->hwlock, SC27XX_ADC_HWLOCK_TIMEOUT);
if (ret) {
dev_err(data->dev, "timeout to get the hwspinlock\n");
return ret;
}
ret = regmap_update_bits(data->regmap, data->base + SC27XX_ADC_CTL,
SC27XX_ADC_EN, SC27XX_ADC_EN);
if (ret)
goto unlock_adc;
/* Configure the channel id and scale */
tmp = (scale << SC27XX_ADC_SCALE_SHIFT) & SC27XX_ADC_SCALE_MASK;
tmp |= channel & SC27XX_ADC_CHN_ID_MASK;
ret = regmap_update_bits(data->regmap, data->base + SC27XX_ADC_CH_CFG,
SC27XX_ADC_CHN_ID_MASK | SC27XX_ADC_SCALE_MASK,
tmp);
if (ret)
goto disable_adc;
/* Select 12bit conversion mode, and only sample 1 time */
tmp = SC27XX_ADC_12BIT_MODE;
tmp |= (0 << SC27XX_ADC_RUN_NUM_SHIFT) & SC27XX_ADC_RUN_NUM_MASK;
ret = regmap_update_bits(data->regmap, data->base + SC27XX_ADC_CTL,
SC27XX_ADC_RUN_NUM_MASK | SC27XX_ADC_12BIT_MODE,
tmp);
if (ret)
goto disable_adc;
ret = regmap_update_bits(data->regmap, data->base + SC27XX_ADC_CTL,
SC27XX_ADC_CHN_RUN, SC27XX_ADC_CHN_RUN);
if (ret)
goto disable_adc;
wait_for_completion(&data->completion);
disable_adc:
regmap_update_bits(data->regmap, data->base + SC27XX_ADC_CTL,
SC27XX_ADC_EN, 0);
unlock_adc:
hwspin_unlock_raw(data->hwlock);
if (!ret)
*val = data->value;
return ret;
}
static irqreturn_t sc27xx_adc_isr(int irq, void *dev_id)
{
struct sc27xx_adc_data *data = dev_id;
int ret;
ret = regmap_update_bits(data->regmap, data->base + SC27XX_ADC_INT_CLR,
SC27XX_ADC_IRQ_CLR, SC27XX_ADC_IRQ_CLR);
if (ret)
return IRQ_RETVAL(ret);
ret = regmap_read(data->regmap, data->base + SC27XX_ADC_DATA,
&data->value);
if (ret)
return IRQ_RETVAL(ret);
data->value &= SC27XX_ADC_DATA_MASK;
complete(&data->completion);
return IRQ_HANDLED;
}
static void sc27xx_adc_volt_ratio(struct sc27xx_adc_data *data,
int channel, int scale,
u32 *div_numerator, u32 *div_denominator)
{
u32 ratio = sc27xx_adc_get_ratio(channel, scale);
*div_numerator = ratio >> SC27XX_RATIO_NUMERATOR_OFFSET;
*div_denominator = ratio & SC27XX_RATIO_DENOMINATOR_MASK;
}
static int sc27xx_adc_to_volt(const struct sc27xx_adc_linear_graph *graph,
int raw_adc)
{
int tmp;
tmp = (graph->volt0 - graph->volt1) * (raw_adc - graph->adc1);
tmp /= (graph->adc0 - graph->adc1);
tmp += graph->volt1;
return tmp < 0 ? 0 : tmp;
}
static int sc27xx_adc_convert_volt(struct sc27xx_adc_data *data, int channel,
int scale, int raw_adc)
{
u32 numerator, denominator;
u32 volt;
/*
* Convert ADC values to voltage values according to the linear graph,
* and channel 5 and channel 1 has been calibrated, so we can just
* return the voltage values calculated by the linear graph. But other
* channels need be calculated to the real voltage values with the
* voltage ratio.
*/
switch (channel) {
case 5:
return sc27xx_adc_to_volt(&big_scale_graph, raw_adc);
case 1:
return sc27xx_adc_to_volt(&small_scale_graph, raw_adc);
default:
volt = sc27xx_adc_to_volt(&small_scale_graph, raw_adc);
break;
}
sc27xx_adc_volt_ratio(data, channel, scale, &numerator, &denominator);
return (volt * denominator + numerator / 2) / numerator;
}
static int sc27xx_adc_read_processed(struct sc27xx_adc_data *data,
int channel, int scale, int *val)
{
int ret, raw_adc;
ret = sc27xx_adc_read(data, channel, scale, &raw_adc);
if (ret)
return ret;
*val = sc27xx_adc_convert_volt(data, channel, scale, raw_adc);
return 0;
}
static int sc27xx_adc_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct sc27xx_adc_data *data = iio_priv(indio_dev);
int scale = data->channel_scale[chan->channel];
int ret, tmp;
switch (mask) {
case IIO_CHAN_INFO_PROCESSED:
mutex_lock(&indio_dev->mlock);
ret = sc27xx_adc_read_processed(data, chan->channel, scale,
&tmp);
mutex_unlock(&indio_dev->mlock);
if (ret)
return ret;
*val = tmp;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
*val = scale;
return IIO_VAL_INT;
default:
return -EINVAL;
}
}
static int sc27xx_adc_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
struct sc27xx_adc_data *data = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_SCALE:
data->channel_scale[chan->channel] = val;
return IIO_VAL_INT;
default:
return -EINVAL;
}
}
static const struct iio_info sc27xx_info = {
.read_raw = &sc27xx_adc_read_raw,
.write_raw = &sc27xx_adc_write_raw,
};
#define SC27XX_ADC_CHANNEL(index) { \
.type = IIO_VOLTAGE, \
.channel = index, \
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) | \
BIT(IIO_CHAN_INFO_SCALE), \
.datasheet_name = "CH##index", \
.indexed = 1, \
}
static const struct iio_chan_spec sc27xx_channels[] = {
SC27XX_ADC_CHANNEL(0),
SC27XX_ADC_CHANNEL(1),
SC27XX_ADC_CHANNEL(2),
SC27XX_ADC_CHANNEL(3),
SC27XX_ADC_CHANNEL(4),
SC27XX_ADC_CHANNEL(5),
SC27XX_ADC_CHANNEL(6),
SC27XX_ADC_CHANNEL(7),
SC27XX_ADC_CHANNEL(8),
SC27XX_ADC_CHANNEL(9),
SC27XX_ADC_CHANNEL(10),
SC27XX_ADC_CHANNEL(11),
SC27XX_ADC_CHANNEL(12),
SC27XX_ADC_CHANNEL(13),
SC27XX_ADC_CHANNEL(14),
SC27XX_ADC_CHANNEL(15),
SC27XX_ADC_CHANNEL(16),
SC27XX_ADC_CHANNEL(17),
SC27XX_ADC_CHANNEL(18),
SC27XX_ADC_CHANNEL(19),
SC27XX_ADC_CHANNEL(20),
SC27XX_ADC_CHANNEL(21),
SC27XX_ADC_CHANNEL(22),
SC27XX_ADC_CHANNEL(23),
SC27XX_ADC_CHANNEL(24),
SC27XX_ADC_CHANNEL(25),
SC27XX_ADC_CHANNEL(26),
SC27XX_ADC_CHANNEL(27),
SC27XX_ADC_CHANNEL(28),
SC27XX_ADC_CHANNEL(29),
SC27XX_ADC_CHANNEL(30),
SC27XX_ADC_CHANNEL(31),
};
static int sc27xx_adc_enable(struct sc27xx_adc_data *data)
{
int ret;
ret = regmap_update_bits(data->regmap, SC27XX_MODULE_EN,
SC27XX_MODULE_ADC_EN, SC27XX_MODULE_ADC_EN);
if (ret)
return ret;
/* Enable ADC work clock and controller clock */
ret = regmap_update_bits(data->regmap, SC27XX_ARM_CLK_EN,
SC27XX_CLK_ADC_EN | SC27XX_CLK_ADC_CLK_EN,
SC27XX_CLK_ADC_EN | SC27XX_CLK_ADC_CLK_EN);
if (ret)
goto disable_adc;
ret = regmap_update_bits(data->regmap, data->base + SC27XX_ADC_INT_EN,
SC27XX_ADC_IRQ_EN, SC27XX_ADC_IRQ_EN);
if (ret)
goto disable_clk;
return 0;
disable_clk:
regmap_update_bits(data->regmap, SC27XX_ARM_CLK_EN,
SC27XX_CLK_ADC_EN | SC27XX_CLK_ADC_CLK_EN, 0);
disable_adc:
regmap_update_bits(data->regmap, SC27XX_MODULE_EN,
SC27XX_MODULE_ADC_EN, 0);
return ret;
}
static void sc27xx_adc_disable(void *_data)
{
struct sc27xx_adc_data *data = _data;
regmap_update_bits(data->regmap, data->base + SC27XX_ADC_INT_EN,
SC27XX_ADC_IRQ_EN, 0);
/* Disable ADC work clock and controller clock */
regmap_update_bits(data->regmap, SC27XX_ARM_CLK_EN,
SC27XX_CLK_ADC_EN | SC27XX_CLK_ADC_CLK_EN, 0);
regmap_update_bits(data->regmap, SC27XX_MODULE_EN,
SC27XX_MODULE_ADC_EN, 0);
}
static void sc27xx_adc_free_hwlock(void *_data)
{
struct hwspinlock *hwlock = _data;
hwspin_lock_free(hwlock);
}
static int sc27xx_adc_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct sc27xx_adc_data *sc27xx_data;
struct iio_dev *indio_dev;
int ret;
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*sc27xx_data));
if (!indio_dev)
return -ENOMEM;
sc27xx_data = iio_priv(indio_dev);
sc27xx_data->regmap = dev_get_regmap(pdev->dev.parent, NULL);
if (!sc27xx_data->regmap) {
dev_err(&pdev->dev, "failed to get ADC regmap\n");
return -ENODEV;
}
ret = of_property_read_u32(np, "reg", &sc27xx_data->base);
if (ret) {
dev_err(&pdev->dev, "failed to get ADC base address\n");
return ret;
}
sc27xx_data->irq = platform_get_irq(pdev, 0);
if (sc27xx_data->irq < 0) {
dev_err(&pdev->dev, "failed to get ADC irq number\n");
return sc27xx_data->irq;
}
ret = of_hwspin_lock_get_id(np, 0);
if (ret < 0) {
dev_err(&pdev->dev, "failed to get hwspinlock id\n");
return ret;
}
sc27xx_data->hwlock = hwspin_lock_request_specific(ret);
if (!sc27xx_data->hwlock) {
dev_err(&pdev->dev, "failed to request hwspinlock\n");
return -ENXIO;
}
ret = devm_add_action(&pdev->dev, sc27xx_adc_free_hwlock,
sc27xx_data->hwlock);
if (ret) {
sc27xx_adc_free_hwlock(sc27xx_data->hwlock);
dev_err(&pdev->dev, "failed to add hwspinlock action\n");
return ret;
}
init_completion(&sc27xx_data->completion);
sc27xx_data->dev = &pdev->dev;
ret = sc27xx_adc_enable(sc27xx_data);
if (ret) {
dev_err(&pdev->dev, "failed to enable ADC module\n");
return ret;
}
ret = devm_add_action(&pdev->dev, sc27xx_adc_disable, sc27xx_data);
if (ret) {
sc27xx_adc_disable(sc27xx_data);
dev_err(&pdev->dev, "failed to add ADC disable action\n");
return ret;
}
ret = devm_request_threaded_irq(&pdev->dev, sc27xx_data->irq, NULL,
sc27xx_adc_isr, IRQF_ONESHOT,
pdev->name, sc27xx_data);
if (ret) {
dev_err(&pdev->dev, "failed to request ADC irq\n");
return ret;
}
indio_dev->dev.parent = &pdev->dev;
indio_dev->name = dev_name(&pdev->dev);
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->info = &sc27xx_info;
indio_dev->channels = sc27xx_channels;
indio_dev->num_channels = ARRAY_SIZE(sc27xx_channels);
ret = devm_iio_device_register(&pdev->dev, indio_dev);
if (ret)
dev_err(&pdev->dev, "could not register iio (ADC)");
return ret;
}
static const struct of_device_id sc27xx_adc_of_match[] = {
{ .compatible = "sprd,sc2731-adc", },
{ }
};
static struct platform_driver sc27xx_adc_driver = {
.probe = sc27xx_adc_probe,
.driver = {
.name = "sc27xx-adc",
.of_match_table = sc27xx_adc_of_match,
},
};
module_platform_driver(sc27xx_adc_driver);
MODULE_AUTHOR("Freeman Liu <freeman.liu@spreadtrum.com>");
MODULE_DESCRIPTION("Spreadtrum SC27XX ADC Driver");
MODULE_LICENSE("GPL v2");
// SPDX-License-Identifier: GPL-2.0
/*
* Texas Instruments ADS7950 SPI ADC driver
*
......@@ -10,15 +11,6 @@
* And also on hwmon/ads79xx.c
* Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/
* Nishanth Menon
*
* 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 version 2.
*
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
* kind, whether express or implied; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/acpi.h>
......@@ -76,6 +68,9 @@ struct ti_ads7950_state {
__be16 rx_buf[TI_ADS7950_MAX_CHAN + TI_ADS7950_TIMESTAMP_SIZE]
____cacheline_aligned;
__be16 tx_buf[TI_ADS7950_MAX_CHAN];
__be16 single_tx;
__be16 single_rx;
};
struct ti_ads7950_chip_info {
......@@ -295,18 +290,26 @@ static irqreturn_t ti_ads7950_trigger_handler(int irq, void *p)
return IRQ_HANDLED;
}
static int ti_ads7950_scan_direct(struct ti_ads7950_state *st, unsigned int ch)
static int ti_ads7950_scan_direct(struct iio_dev *indio_dev, unsigned int ch)
{
struct ti_ads7950_state *st = iio_priv(indio_dev);
int ret, cmd;
mutex_lock(&indio_dev->mlock);
cmd = TI_ADS7950_CR_WRITE | TI_ADS7950_CR_CHAN(ch) | st->settings;
st->tx_buf[0] = cpu_to_be16(cmd);
st->single_tx = cpu_to_be16(cmd);
ret = spi_sync(st->spi, &st->scan_single_msg);
if (ret)
return ret;
goto out;
ret = be16_to_cpu(st->single_rx);
return be16_to_cpu(st->rx_buf[0]);
out:
mutex_unlock(&indio_dev->mlock);
return ret;
}
static int ti_ads7950_get_range(struct ti_ads7950_state *st)
......@@ -338,13 +341,7 @@ static int ti_ads7950_read_raw(struct iio_dev *indio_dev,
switch (m) {
case IIO_CHAN_INFO_RAW:
ret = iio_device_claim_direct_mode(indio_dev);
if (ret < 0)
return ret;
ret = ti_ads7950_scan_direct(st, chan->address);
iio_device_release_direct_mode(indio_dev);
ret = ti_ads7950_scan_direct(indio_dev, chan->address);
if (ret < 0)
return ret;
......@@ -410,13 +407,13 @@ static int ti_ads7950_probe(struct spi_device *spi)
* was read at the end of the first transfer.
*/
st->scan_single_xfer[0].tx_buf = &st->tx_buf[0];
st->scan_single_xfer[0].tx_buf = &st->single_tx;
st->scan_single_xfer[0].len = 2;
st->scan_single_xfer[0].cs_change = 1;
st->scan_single_xfer[1].tx_buf = &st->tx_buf[0];
st->scan_single_xfer[1].tx_buf = &st->single_tx;
st->scan_single_xfer[1].len = 2;
st->scan_single_xfer[1].cs_change = 1;
st->scan_single_xfer[2].rx_buf = &st->rx_buf[0];
st->scan_single_xfer[2].rx_buf = &st->single_rx;
st->scan_single_xfer[2].len = 2;
spi_message_init_with_transfers(&st->scan_single_msg,
......
......@@ -341,8 +341,6 @@ static int xadc_zynq_setup(struct platform_device *pdev,
pcap_rate = clk_get_rate(xadc->clk);
if (tck_rate > XADC_ZYNQ_TCK_RATE_MAX)
tck_rate = XADC_ZYNQ_TCK_RATE_MAX;
if (tck_rate > pcap_rate / 2) {
div = 2;
} else {
......@@ -1045,7 +1043,7 @@ static int xadc_parse_dt(struct iio_dev *indio_dev, struct device_node *np,
unsigned int num_channels;
const char *external_mux;
u32 ext_mux_chan;
int reg;
u32 reg;
int ret;
*conf = 0;
......
......@@ -380,8 +380,7 @@ void st_sensors_of_name_probe(struct device *dev,
return;
/* The name from the OF match takes precedence if present */
strncpy(name, of_id->data, len);
name[len - 1] = '\0';
strlcpy(name, of_id->data, len);
}
EXPORT_SYMBOL(st_sensors_of_name_probe);
#else
......
......@@ -167,6 +167,16 @@ config AD5755
To compile this driver as a module, choose M here: the
module will be called ad5755.
config AD5758
tristate "Analog Devices AD5758 DAC driver"
depends on SPI_MASTER
help
Say yes here to build support for Analog Devices AD5758 single channel
Digital to Analog Converter.
To compile this driver as a module, choose M here: the
module will be called ad5758.
config AD5761
tristate "Analog Devices AD5761/61R/21/21R DAC driver"
depends on SPI_MASTER
......
......@@ -16,6 +16,7 @@ obj-$(CONFIG_AD5592R_BASE) += ad5592r-base.o
obj-$(CONFIG_AD5592R) += ad5592r.o
obj-$(CONFIG_AD5593R) += ad5593r.o
obj-$(CONFIG_AD5755) += ad5755.o
obj-$(CONFIG_AD5755) += ad5758.o
obj-$(CONFIG_AD5761) += ad5761.o
obj-$(CONFIG_AD5764) += ad5764.o
obj-$(CONFIG_AD5791) += ad5791.o
......
// SPDX-License-Identifier: GPL-2.0+
/*
* AD5758 Digital to analog converters driver
*
* Copyright 2018 Analog Devices Inc.
*
* TODO: Currently CRC is not supported in this driver
*/
#include <linux/bsearch.h>
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/property.h>
#include <linux/spi/spi.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
/* AD5758 registers definition */
#define AD5758_NOP 0x00
#define AD5758_DAC_INPUT 0x01
#define AD5758_DAC_OUTPUT 0x02
#define AD5758_CLEAR_CODE 0x03
#define AD5758_USER_GAIN 0x04
#define AD5758_USER_OFFSET 0x05
#define AD5758_DAC_CONFIG 0x06
#define AD5758_SW_LDAC 0x07
#define AD5758_KEY 0x08
#define AD5758_GP_CONFIG1 0x09
#define AD5758_GP_CONFIG2 0x0A
#define AD5758_DCDC_CONFIG1 0x0B
#define AD5758_DCDC_CONFIG2 0x0C
#define AD5758_WDT_CONFIG 0x0F
#define AD5758_DIGITAL_DIAG_CONFIG 0x10
#define AD5758_ADC_CONFIG 0x11
#define AD5758_FAULT_PIN_CONFIG 0x12
#define AD5758_TWO_STAGE_READBACK_SELECT 0x13
#define AD5758_DIGITAL_DIAG_RESULTS 0x14
#define AD5758_ANALOG_DIAG_RESULTS 0x15
#define AD5758_STATUS 0x16
#define AD5758_CHIP_ID 0x17
#define AD5758_FREQ_MONITOR 0x18
#define AD5758_DEVICE_ID_0 0x19
#define AD5758_DEVICE_ID_1 0x1A
#define AD5758_DEVICE_ID_2 0x1B
#define AD5758_DEVICE_ID_3 0x1C
/* AD5758_DAC_CONFIG */
#define AD5758_DAC_CONFIG_RANGE_MSK GENMASK(3, 0)
#define AD5758_DAC_CONFIG_RANGE_MODE(x) (((x) & 0xF) << 0)
#define AD5758_DAC_CONFIG_INT_EN_MSK BIT(5)
#define AD5758_DAC_CONFIG_INT_EN_MODE(x) (((x) & 0x1) << 5)
#define AD5758_DAC_CONFIG_OUT_EN_MSK BIT(6)
#define AD5758_DAC_CONFIG_OUT_EN_MODE(x) (((x) & 0x1) << 6)
#define AD5758_DAC_CONFIG_SR_EN_MSK BIT(8)
#define AD5758_DAC_CONFIG_SR_EN_MODE(x) (((x) & 0x1) << 8)
#define AD5758_DAC_CONFIG_SR_CLOCK_MSK GENMASK(12, 9)
#define AD5758_DAC_CONFIG_SR_CLOCK_MODE(x) (((x) & 0xF) << 9)
#define AD5758_DAC_CONFIG_SR_STEP_MSK GENMASK(15, 13)
#define AD5758_DAC_CONFIG_SR_STEP_MODE(x) (((x) & 0x7) << 13)
/* AD5758_KEY */
#define AD5758_KEY_CODE_RESET_1 0x15FA
#define AD5758_KEY_CODE_RESET_2 0xAF51
#define AD5758_KEY_CODE_SINGLE_ADC_CONV 0x1ADC
#define AD5758_KEY_CODE_RESET_WDT 0x0D06
#define AD5758_KEY_CODE_CALIB_MEM_REFRESH 0xFCBA
/* AD5758_DCDC_CONFIG1 */
#define AD5758_DCDC_CONFIG1_DCDC_VPROG_MSK GENMASK(4, 0)
#define AD5758_DCDC_CONFIG1_DCDC_VPROG_MODE(x) (((x) & 0x1F) << 0)
#define AD5758_DCDC_CONFIG1_DCDC_MODE_MSK GENMASK(6, 5)
#define AD5758_DCDC_CONFIG1_DCDC_MODE_MODE(x) (((x) & 0x3) << 5)
#define AD5758_DCDC_CONFIG1_PROT_SW_EN_MSK BIT(7)
#define AD5758_DCDC_CONFIG1_PROT_SW_EN_MODE(x) (((x) & 0x1) << 7)
/* AD5758_DCDC_CONFIG2 */
#define AD5758_DCDC_CONFIG2_ILIMIT_MSK GENMASK(3, 1)
#define AD5758_DCDC_CONFIG2_ILIMIT_MODE(x) (((x) & 0x7) << 1)
#define AD5758_DCDC_CONFIG2_INTR_SAT_3WI_MSK BIT(11)
#define AD5758_DCDC_CONFIG2_BUSY_3WI_MSK BIT(12)
/* AD5758_DIGITAL_DIAG_RESULTS */
#define AD5758_CAL_MEM_UNREFRESHED_MSK BIT(15)
#define AD5758_WR_FLAG_MSK(x) (0x80 | ((x) & 0x1F))
#define AD5758_FULL_SCALE_MICRO 65535000000ULL
/**
* struct ad5758_state - driver instance specific data
* @spi: spi_device
* @lock: mutex lock
* @out_range: struct which stores the output range
* @dc_dc_mode: variable which stores the mode of operation
* @dc_dc_ilim: variable which stores the dc-to-dc converter current limit
* @slew_time: variable which stores the target slew time
* @pwr_down: variable which contains whether a channel is powered down or not
* @data: spi transfer buffers
*/
struct ad5758_range {
int reg;
int min;
int max;
};
struct ad5758_state {
struct spi_device *spi;
struct mutex lock;
struct ad5758_range out_range;
unsigned int dc_dc_mode;
unsigned int dc_dc_ilim;
unsigned int slew_time;
bool pwr_down;
__be32 d32[3];
};
/**
* Output ranges corresponding to bits [3:0] from DAC_CONFIG register
* 0000: 0 V to 5 V voltage range
* 0001: 0 V to 10 V voltage range
* 0010: ±5 V voltage range
* 0011: ±10 V voltage range
* 1000: 0 mA to 20 mA current range
* 1001: 0 mA to 24 mA current range
* 1010: 4 mA to 20 mA current range
* 1011: ±20 mA current range
* 1100: ±24 mA current range
* 1101: -1 mA to +22 mA current range
*/
enum ad5758_output_range {
AD5758_RANGE_0V_5V,
AD5758_RANGE_0V_10V,
AD5758_RANGE_PLUSMINUS_5V,
AD5758_RANGE_PLUSMINUS_10V,
AD5758_RANGE_0mA_20mA = 8,
AD5758_RANGE_0mA_24mA,
AD5758_RANGE_4mA_24mA,
AD5758_RANGE_PLUSMINUS_20mA,
AD5758_RANGE_PLUSMINUS_24mA,
AD5758_RANGE_MINUS_1mA_PLUS_22mA,
};
enum ad5758_dc_dc_mode {
AD5758_DCDC_MODE_POWER_OFF,
AD5758_DCDC_MODE_DPC_CURRENT,
AD5758_DCDC_MODE_DPC_VOLTAGE,
AD5758_DCDC_MODE_PPC_CURRENT,
};
static const struct ad5758_range ad5758_voltage_range[] = {
{ AD5758_RANGE_0V_5V, 0, 5000000 },
{ AD5758_RANGE_0V_10V, 0, 10000000 },
{ AD5758_RANGE_PLUSMINUS_5V, -5000000, 5000000 },
{ AD5758_RANGE_PLUSMINUS_10V, -10000000, 10000000 }
};
static const struct ad5758_range ad5758_current_range[] = {
{ AD5758_RANGE_0mA_20mA, 0, 20000},
{ AD5758_RANGE_0mA_24mA, 0, 24000 },
{ AD5758_RANGE_4mA_24mA, 4, 24000 },
{ AD5758_RANGE_PLUSMINUS_20mA, -20000, 20000 },
{ AD5758_RANGE_PLUSMINUS_24mA, -24000, 24000 },
{ AD5758_RANGE_MINUS_1mA_PLUS_22mA, -1000, 22000 },
};
static const int ad5758_sr_clk[16] = {
240000, 200000, 150000, 128000, 64000, 32000, 16000, 8000, 4000, 2000,
1000, 512, 256, 128, 64, 16
};
static const int ad5758_sr_step[8] = {
4, 12, 64, 120, 256, 500, 1820, 2048
};
static const int ad5758_dc_dc_ilim[6] = {
150000, 200000, 250000, 300000, 350000, 400000
};
static int ad5758_spi_reg_read(struct ad5758_state *st, unsigned int addr)
{
struct spi_transfer t[] = {
{
.tx_buf = &st->d32[0],
.len = 4,
.cs_change = 1,
}, {
.tx_buf = &st->d32[1],
.rx_buf = &st->d32[2],
.len = 4,
},
};
int ret;
st->d32[0] = cpu_to_be32(
(AD5758_WR_FLAG_MSK(AD5758_TWO_STAGE_READBACK_SELECT) << 24) |
(addr << 8));
st->d32[1] = cpu_to_be32(AD5758_WR_FLAG_MSK(AD5758_NOP) << 24);
ret = spi_sync_transfer(st->spi, t, ARRAY_SIZE(t));
if (ret < 0)
return ret;
return (be32_to_cpu(st->d32[2]) >> 8) & 0xFFFF;
}
static int ad5758_spi_reg_write(struct ad5758_state *st,
unsigned int addr,
unsigned int val)
{
st->d32[0] = cpu_to_be32((AD5758_WR_FLAG_MSK(addr) << 24) |
((val & 0xFFFF) << 8));
return spi_write(st->spi, &st->d32[0], sizeof(st->d32[0]));
}
static int ad5758_spi_write_mask(struct ad5758_state *st,
unsigned int addr,
unsigned long int mask,
unsigned int val)
{
int regval;
regval = ad5758_spi_reg_read(st, addr);
if (regval < 0)
return regval;
regval &= ~mask;
regval |= val;
return ad5758_spi_reg_write(st, addr, regval);
}
static int cmpfunc(const void *a, const void *b)
{
return *(int *)a - *(int *)b;
}
static int ad5758_find_closest_match(const int *array,
unsigned int size, int val)
{
int i;
for (i = 0; i < size; i++) {
if (val <= array[i])
return i;
}
return size - 1;
}
static int ad5758_wait_for_task_complete(struct ad5758_state *st,
unsigned int reg,
unsigned int mask)
{
unsigned int timeout;
int ret;
timeout = 10;
do {
ret = ad5758_spi_reg_read(st, reg);
if (ret < 0)
return ret;
if (!(ret & mask))
return 0;
usleep_range(100, 1000);
} while (--timeout);
dev_err(&st->spi->dev,
"Error reading bit 0x%x in 0x%x register\n", mask, reg);
return -EIO;
}
static int ad5758_calib_mem_refresh(struct ad5758_state *st)
{
int ret;
ret = ad5758_spi_reg_write(st, AD5758_KEY,
AD5758_KEY_CODE_CALIB_MEM_REFRESH);
if (ret < 0) {
dev_err(&st->spi->dev,
"Failed to initiate a calibration memory refresh\n");
return ret;
}
/* Wait to allow time for the internal calibrations to complete */
return ad5758_wait_for_task_complete(st, AD5758_DIGITAL_DIAG_RESULTS,
AD5758_CAL_MEM_UNREFRESHED_MSK);
}
static int ad5758_soft_reset(struct ad5758_state *st)
{
int ret;
ret = ad5758_spi_reg_write(st, AD5758_KEY, AD5758_KEY_CODE_RESET_1);
if (ret < 0)
return ret;
ret = ad5758_spi_reg_write(st, AD5758_KEY, AD5758_KEY_CODE_RESET_2);
/* Perform a software reset and wait at least 100us */
usleep_range(100, 1000);
return ret;
}
static int ad5758_set_dc_dc_conv_mode(struct ad5758_state *st,
enum ad5758_dc_dc_mode mode)
{
int ret;
ret = ad5758_spi_write_mask(st, AD5758_DCDC_CONFIG1,
AD5758_DCDC_CONFIG1_DCDC_MODE_MSK,
AD5758_DCDC_CONFIG1_DCDC_MODE_MODE(mode));
if (ret < 0)
return ret;
/*
* Poll the BUSY_3WI bit in the DCDC_CONFIG2 register until it is 0.
* This allows the 3-wire interface communication to complete.
*/
ret = ad5758_wait_for_task_complete(st, AD5758_DCDC_CONFIG2,
AD5758_DCDC_CONFIG2_BUSY_3WI_MSK);
if (ret < 0)
return ret;
st->dc_dc_mode = mode;
return ret;
}
static int ad5758_set_dc_dc_ilim(struct ad5758_state *st, unsigned int ilim)
{
int ret;
ret = ad5758_spi_write_mask(st, AD5758_DCDC_CONFIG2,
AD5758_DCDC_CONFIG2_ILIMIT_MSK,
AD5758_DCDC_CONFIG2_ILIMIT_MODE(ilim));
if (ret < 0)
return ret;
/*
* Poll the BUSY_3WI bit in the DCDC_CONFIG2 register until it is 0.
* This allows the 3-wire interface communication to complete.
*/
return ad5758_wait_for_task_complete(st, AD5758_DCDC_CONFIG2,
AD5758_DCDC_CONFIG2_BUSY_3WI_MSK);
}
static int ad5758_slew_rate_set(struct ad5758_state *st,
unsigned int sr_clk_idx,
unsigned int sr_step_idx)
{
unsigned int mode;
unsigned long int mask;
int ret;
mask = AD5758_DAC_CONFIG_SR_EN_MSK |
AD5758_DAC_CONFIG_SR_CLOCK_MSK |
AD5758_DAC_CONFIG_SR_STEP_MSK;
mode = AD5758_DAC_CONFIG_SR_EN_MODE(1) |
AD5758_DAC_CONFIG_SR_STEP_MODE(sr_step_idx) |
AD5758_DAC_CONFIG_SR_CLOCK_MODE(sr_clk_idx);
ret = ad5758_spi_write_mask(st, AD5758_DAC_CONFIG, mask, mode);
if (ret < 0)
return ret;
/* Wait to allow time for the internal calibrations to complete */
return ad5758_wait_for_task_complete(st, AD5758_DIGITAL_DIAG_RESULTS,
AD5758_CAL_MEM_UNREFRESHED_MSK);
}
static int ad5758_slew_rate_config(struct ad5758_state *st)
{
unsigned int sr_clk_idx, sr_step_idx;
int i, res;
s64 diff_new, diff_old;
u64 sr_step, calc_slew_time;
sr_clk_idx = 0;
sr_step_idx = 0;
diff_old = S64_MAX;
/*
* The slew time can be determined by using the formula:
* Slew Time = (Full Scale Out / (Step Size x Update Clk Freq))
* where Slew time is expressed in microseconds
* Given the desired slew time, the following algorithm determines the
* best match for the step size and the update clock frequency.
*/
for (i = 0; i < ARRAY_SIZE(ad5758_sr_clk); i++) {
/*
* Go through each valid update clock freq and determine a raw
* value for the step size by using the formula:
* Step Size = Full Scale Out / (Update Clk Freq * Slew Time)
*/
sr_step = AD5758_FULL_SCALE_MICRO;
do_div(sr_step, ad5758_sr_clk[i]);
do_div(sr_step, st->slew_time);
/*
* After a raw value for step size was determined, find the
* closest valid match
*/
res = ad5758_find_closest_match(ad5758_sr_step,
ARRAY_SIZE(ad5758_sr_step),
sr_step);
/* Calculate the slew time */
calc_slew_time = AD5758_FULL_SCALE_MICRO;
do_div(calc_slew_time, ad5758_sr_step[res]);
do_div(calc_slew_time, ad5758_sr_clk[i]);
/*
* Determine with how many microseconds the calculated slew time
* is different from the desired slew time and store the diff
* for the next iteration
*/
diff_new = abs(st->slew_time - calc_slew_time);
if (diff_new < diff_old) {
diff_old = diff_new;
sr_clk_idx = i;
sr_step_idx = res;
}
}
return ad5758_slew_rate_set(st, sr_clk_idx, sr_step_idx);
}
static int ad5758_set_out_range(struct ad5758_state *st, int range)
{
int ret;
ret = ad5758_spi_write_mask(st, AD5758_DAC_CONFIG,
AD5758_DAC_CONFIG_RANGE_MSK,
AD5758_DAC_CONFIG_RANGE_MODE(range));
if (ret < 0)
return ret;
/* Wait to allow time for the internal calibrations to complete */
return ad5758_wait_for_task_complete(st, AD5758_DIGITAL_DIAG_RESULTS,
AD5758_CAL_MEM_UNREFRESHED_MSK);
}
static int ad5758_fault_prot_switch_en(struct ad5758_state *st, bool enable)
{
int ret;
ret = ad5758_spi_write_mask(st, AD5758_DCDC_CONFIG1,
AD5758_DCDC_CONFIG1_PROT_SW_EN_MSK,
AD5758_DCDC_CONFIG1_PROT_SW_EN_MODE(enable));
if (ret < 0)
return ret;
/*
* Poll the BUSY_3WI bit in the DCDC_CONFIG2 register until it is 0.
* This allows the 3-wire interface communication to complete.
*/
return ad5758_wait_for_task_complete(st, AD5758_DCDC_CONFIG2,
AD5758_DCDC_CONFIG2_BUSY_3WI_MSK);
}
static int ad5758_internal_buffers_en(struct ad5758_state *st, bool enable)
{
int ret;
ret = ad5758_spi_write_mask(st, AD5758_DAC_CONFIG,
AD5758_DAC_CONFIG_INT_EN_MSK,
AD5758_DAC_CONFIG_INT_EN_MODE(enable));
if (ret < 0)
return ret;
/* Wait to allow time for the internal calibrations to complete */
return ad5758_wait_for_task_complete(st, AD5758_DIGITAL_DIAG_RESULTS,
AD5758_CAL_MEM_UNREFRESHED_MSK);
}
static int ad5758_reg_access(struct iio_dev *indio_dev,
unsigned int reg,
unsigned int writeval,
unsigned int *readval)
{
struct ad5758_state *st = iio_priv(indio_dev);
int ret;
mutex_lock(&st->lock);
if (readval) {
ret = ad5758_spi_reg_read(st, reg);
if (ret < 0) {
mutex_unlock(&st->lock);
return ret;
}
*readval = ret;
ret = 0;
} else {
ret = ad5758_spi_reg_write(st, reg, writeval);
}
mutex_unlock(&st->lock);
return ret;
}
static int ad5758_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long info)
{
struct ad5758_state *st = iio_priv(indio_dev);
int max, min, ret;
switch (info) {
case IIO_CHAN_INFO_RAW:
mutex_lock(&st->lock);
ret = ad5758_spi_reg_read(st, AD5758_DAC_INPUT);
mutex_unlock(&st->lock);
if (ret < 0)
return ret;
*val = ret;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
min = st->out_range.min;
max = st->out_range.max;
*val = (max - min) / 1000;
*val2 = 16;
return IIO_VAL_FRACTIONAL_LOG2;
case IIO_CHAN_INFO_OFFSET:
min = st->out_range.min;
max = st->out_range.max;
*val = ((min * (1 << 16)) / (max - min)) / 1000;
return IIO_VAL_INT;
default:
return -EINVAL;
}
}
static int ad5758_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long info)
{
struct ad5758_state *st = iio_priv(indio_dev);
int ret;
switch (info) {
case IIO_CHAN_INFO_RAW:
mutex_lock(&st->lock);
ret = ad5758_spi_reg_write(st, AD5758_DAC_INPUT, val);
mutex_unlock(&st->lock);
return ret;
default:
return -EINVAL;
}
}
static ssize_t ad5758_read_powerdown(struct iio_dev *indio_dev,
uintptr_t priv,
const struct iio_chan_spec *chan,
char *buf)
{
struct ad5758_state *st = iio_priv(indio_dev);
return sprintf(buf, "%d\n", st->pwr_down);
}
static ssize_t ad5758_write_powerdown(struct iio_dev *indio_dev,
uintptr_t priv,
struct iio_chan_spec const *chan,
const char *buf, size_t len)
{
struct ad5758_state *st = iio_priv(indio_dev);
bool pwr_down;
unsigned int dcdc_config1_mode, dc_dc_mode, dac_config_mode, val;
unsigned long int dcdc_config1_msk, dac_config_msk;
int ret;
ret = kstrtobool(buf, &pwr_down);
if (ret)
return ret;
mutex_lock(&st->lock);
if (pwr_down) {
dc_dc_mode = AD5758_DCDC_MODE_POWER_OFF;
val = 0;
} else {
dc_dc_mode = st->dc_dc_mode;
val = 1;
}
dcdc_config1_mode = AD5758_DCDC_CONFIG1_DCDC_MODE_MODE(dc_dc_mode) |
AD5758_DCDC_CONFIG1_PROT_SW_EN_MODE(val);
dcdc_config1_msk = AD5758_DCDC_CONFIG1_DCDC_MODE_MSK |
AD5758_DCDC_CONFIG1_PROT_SW_EN_MSK;
ret = ad5758_spi_write_mask(st, AD5758_DCDC_CONFIG1,
dcdc_config1_msk,
dcdc_config1_mode);
if (ret < 0)
goto err_unlock;
dac_config_mode = AD5758_DAC_CONFIG_OUT_EN_MODE(val) |
AD5758_DAC_CONFIG_INT_EN_MODE(val);
dac_config_msk = AD5758_DAC_CONFIG_OUT_EN_MSK |
AD5758_DAC_CONFIG_INT_EN_MSK;
ret = ad5758_spi_write_mask(st, AD5758_DAC_CONFIG,
dac_config_msk,
dac_config_mode);
if (ret < 0)
goto err_unlock;
st->pwr_down = pwr_down;
err_unlock:
mutex_unlock(&st->lock);
return ret ? ret : len;
}
static const struct iio_info ad5758_info = {
.read_raw = ad5758_read_raw,
.write_raw = ad5758_write_raw,
.debugfs_reg_access = &ad5758_reg_access,
};
static const struct iio_chan_spec_ext_info ad5758_ext_info[] = {
{
.name = "powerdown",
.read = ad5758_read_powerdown,
.write = ad5758_write_powerdown,
.shared = IIO_SHARED_BY_TYPE,
},
{ }
};
#define AD5758_DAC_CHAN(_chan_type) { \
.type = (_chan_type), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_RAW) | \
BIT(IIO_CHAN_INFO_SCALE) | \
BIT(IIO_CHAN_INFO_OFFSET), \
.indexed = 1, \
.output = 1, \
.ext_info = ad5758_ext_info, \
}
static const struct iio_chan_spec ad5758_voltage_ch[] = {
AD5758_DAC_CHAN(IIO_VOLTAGE)
};
static const struct iio_chan_spec ad5758_current_ch[] = {
AD5758_DAC_CHAN(IIO_CURRENT)
};
static bool ad5758_is_valid_mode(enum ad5758_dc_dc_mode mode)
{
switch (mode) {
case AD5758_DCDC_MODE_DPC_CURRENT:
case AD5758_DCDC_MODE_DPC_VOLTAGE:
case AD5758_DCDC_MODE_PPC_CURRENT:
return true;
default:
return false;
}
}
static int ad5758_crc_disable(struct ad5758_state *st)
{
unsigned int mask;
mask = (AD5758_WR_FLAG_MSK(AD5758_DIGITAL_DIAG_CONFIG) << 24) | 0x5C3A;
st->d32[0] = cpu_to_be32(mask);
return spi_write(st->spi, &st->d32[0], 4);
}
static int ad5758_find_out_range(struct ad5758_state *st,
const struct ad5758_range *range,
unsigned int size,
int min, int max)
{
int i;
for (i = 0; i < size; i++) {
if ((min == range[i].min) && (max == range[i].max)) {
st->out_range.reg = range[i].reg;
st->out_range.min = range[i].min;
st->out_range.max = range[i].max;
return 0;
}
}
return -EINVAL;
}
static int ad5758_parse_dt(struct ad5758_state *st)
{
unsigned int tmp, tmparray[2], size;
const struct ad5758_range *range;
int *index, ret;
st->dc_dc_ilim = 0;
ret = device_property_read_u32(&st->spi->dev,
"adi,dc-dc-ilim-microamp", &tmp);
if (ret) {
dev_dbg(&st->spi->dev,
"Missing \"dc-dc-ilim-microamp\" property\n");
} else {
index = bsearch(&tmp, ad5758_dc_dc_ilim,
ARRAY_SIZE(ad5758_dc_dc_ilim),
sizeof(int), cmpfunc);
if (!index)
dev_dbg(&st->spi->dev, "dc-dc-ilim out of range\n");
else
st->dc_dc_ilim = index - ad5758_dc_dc_ilim;
}
ret = device_property_read_u32(&st->spi->dev, "adi,dc-dc-mode",
&st->dc_dc_mode);
if (ret) {
dev_err(&st->spi->dev, "Missing \"dc-dc-mode\" property\n");
return ret;
}
if (!ad5758_is_valid_mode(st->dc_dc_mode))
return -EINVAL;
if (st->dc_dc_mode == AD5758_DCDC_MODE_DPC_VOLTAGE) {
ret = device_property_read_u32_array(&st->spi->dev,
"adi,range-microvolt",
tmparray, 2);
if (ret) {
dev_err(&st->spi->dev,
"Missing \"range-microvolt\" property\n");
return ret;
}
range = ad5758_voltage_range;
size = ARRAY_SIZE(ad5758_voltage_range);
} else {
ret = device_property_read_u32_array(&st->spi->dev,
"adi,range-microamp",
tmparray, 2);
if (ret) {
dev_err(&st->spi->dev,
"Missing \"range-microamp\" property\n");
return ret;
}
range = ad5758_current_range;
size = ARRAY_SIZE(ad5758_current_range);
}
ret = ad5758_find_out_range(st, range, size, tmparray[0], tmparray[1]);
if (ret) {
dev_err(&st->spi->dev, "range invalid\n");
return ret;
}
ret = device_property_read_u32(&st->spi->dev, "adi,slew-time-us", &tmp);
if (ret) {
dev_dbg(&st->spi->dev, "Missing \"slew-time-us\" property\n");
st->slew_time = 0;
} else {
st->slew_time = tmp;
}
return 0;
}
static int ad5758_init(struct ad5758_state *st)
{
int regval, ret;
/* Disable CRC checks */
ret = ad5758_crc_disable(st);
if (ret < 0)
return ret;
/* Perform a software reset */
ret = ad5758_soft_reset(st);
if (ret < 0)
return ret;
/* Disable CRC checks */
ret = ad5758_crc_disable(st);
if (ret < 0)
return ret;
/* Perform a calibration memory refresh */
ret = ad5758_calib_mem_refresh(st);
if (ret < 0)
return ret;
regval = ad5758_spi_reg_read(st, AD5758_DIGITAL_DIAG_RESULTS);
if (regval < 0)
return regval;
/* Clear all the error flags */
ret = ad5758_spi_reg_write(st, AD5758_DIGITAL_DIAG_RESULTS, regval);
if (ret < 0)
return ret;
/* Set the dc-to-dc current limit */
ret = ad5758_set_dc_dc_ilim(st, st->dc_dc_ilim);
if (ret < 0)
return ret;
/* Configure the dc-to-dc controller mode */
ret = ad5758_set_dc_dc_conv_mode(st, st->dc_dc_mode);
if (ret < 0)
return ret;
/* Configure the output range */
ret = ad5758_set_out_range(st, st->out_range.reg);
if (ret < 0)
return ret;
/* Enable Slew Rate Control, set the slew rate clock and step */
if (st->slew_time) {
ret = ad5758_slew_rate_config(st);
if (ret < 0)
return ret;
}
/* Enable the VIOUT fault protection switch (FPS is closed) */
ret = ad5758_fault_prot_switch_en(st, 1);
if (ret < 0)
return ret;
/* Power up the DAC and internal (INT) amplifiers */
ret = ad5758_internal_buffers_en(st, 1);
if (ret < 0)
return ret;
/* Enable VIOUT */
return ad5758_spi_write_mask(st, AD5758_DAC_CONFIG,
AD5758_DAC_CONFIG_OUT_EN_MSK,
AD5758_DAC_CONFIG_OUT_EN_MODE(1));
}
static int ad5758_probe(struct spi_device *spi)
{
struct ad5758_state *st;
struct iio_dev *indio_dev;
int ret;
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
if (!indio_dev)
return -ENOMEM;
st = iio_priv(indio_dev);
spi_set_drvdata(spi, indio_dev);
st->spi = spi;
mutex_init(&st->lock);
indio_dev->dev.parent = &spi->dev;
indio_dev->name = spi_get_device_id(spi)->name;
indio_dev->info = &ad5758_info;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->num_channels = 1;
ret = ad5758_parse_dt(st);
if (ret < 0)
return ret;
if (st->dc_dc_mode == AD5758_DCDC_MODE_DPC_VOLTAGE)
indio_dev->channels = ad5758_voltage_ch;
else
indio_dev->channels = ad5758_current_ch;
ret = ad5758_init(st);
if (ret < 0) {
dev_err(&spi->dev, "AD5758 init failed\n");
return ret;
}
return devm_iio_device_register(&st->spi->dev, indio_dev);
}
static const struct spi_device_id ad5758_id[] = {
{ "ad5758", 0 },
{}
};
MODULE_DEVICE_TABLE(spi, ad5758_id);
static struct spi_driver ad5758_driver = {
.driver = {
.name = KBUILD_MODNAME,
},
.probe = ad5758_probe,
.id_table = ad5758_id,
};
module_spi_driver(ad5758_driver);
MODULE_AUTHOR("Stefan Popa <stefan.popa@analog.com>");
MODULE_DESCRIPTION("Analog Devices AD5758 DAC");
MODULE_LICENSE("GPL v2");
......@@ -87,12 +87,7 @@ static int ltc2632_read_raw(struct iio_dev *indio_dev,
int *val2,
long m)
{
struct ltc2632_chip_info *chip_info;
const struct ltc2632_state *st = iio_priv(indio_dev);
const struct spi_device_id *spi_dev_id = spi_get_device_id(st->spi_dev);
chip_info = (struct ltc2632_chip_info *)spi_dev_id->driver_data;
switch (m) {
case IIO_CHAN_INFO_SCALE:
......
......@@ -97,9 +97,6 @@ static int dac5571_cmd_quad(struct dac5571_data *data, int channel, u16 val)
static int dac5571_pwrdwn_single(struct dac5571_data *data, int channel, u8 pwrdwn)
{
unsigned int shift;
shift = 12 - data->spec->resolution;
data->buf[1] = 0;
data->buf[0] = pwrdwn << DAC5571_SINGLE_PWRDWN_BITS;
......@@ -111,9 +108,6 @@ static int dac5571_pwrdwn_single(struct dac5571_data *data, int channel, u8 pwrd
static int dac5571_pwrdwn_quad(struct dac5571_data *data, int channel, u8 pwrdwn)
{
unsigned int shift;
shift = 16 - data->spec->resolution;
data->buf[2] = 0;
data->buf[1] = pwrdwn << DAC5571_QUAD_PWRDWN_BITS;
data->buf[0] = (channel << DAC5571_CHANNEL_SELECT) |
......
......@@ -274,6 +274,15 @@ struct ad9523_state {
unsigned long vco_out_freq[AD9523_NUM_CLK_SRC];
unsigned char vco_out_map[AD9523_NUM_CHAN_ALT_CLK_SRC];
/*
* Lock for accessing device registers. Some operations require
* multiple consecutive R/W operations, during which the device
* shouldn't be interrupted. The buffers are also shared across
* all operations so need to be protected on stand alone reads and
* writes.
*/
struct mutex lock;
/*
* DMA (thus cache coherency maintenance) requires the
* transfer buffers to live in their own cache lines.
......@@ -500,6 +509,7 @@ static ssize_t ad9523_store(struct device *dev,
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
struct ad9523_state *st = iio_priv(indio_dev);
bool state;
int ret;
......@@ -510,7 +520,7 @@ static ssize_t ad9523_store(struct device *dev,
if (!state)
return 0;
mutex_lock(&indio_dev->mlock);
mutex_lock(&st->lock);
switch ((u32)this_attr->address) {
case AD9523_SYNC:
ret = ad9523_sync(indio_dev);
......@@ -521,7 +531,7 @@ static ssize_t ad9523_store(struct device *dev,
default:
ret = -ENODEV;
}
mutex_unlock(&indio_dev->mlock);
mutex_unlock(&st->lock);
return ret ? ret : len;
}
......@@ -532,15 +542,16 @@ static ssize_t ad9523_show(struct device *dev,
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
struct ad9523_state *st = iio_priv(indio_dev);
int ret;
mutex_lock(&indio_dev->mlock);
mutex_lock(&st->lock);
ret = ad9523_read(indio_dev, AD9523_READBACK_0);
if (ret >= 0) {
ret = sprintf(buf, "%d\n", !!(ret & (1 <<
(u32)this_attr->address)));
}
mutex_unlock(&indio_dev->mlock);
mutex_unlock(&st->lock);
return ret;
}
......@@ -623,9 +634,9 @@ static int ad9523_read_raw(struct iio_dev *indio_dev,
unsigned int code;
int ret;
mutex_lock(&indio_dev->mlock);
mutex_lock(&st->lock);
ret = ad9523_read(indio_dev, AD9523_CHANNEL_CLOCK_DIST(chan->channel));
mutex_unlock(&indio_dev->mlock);
mutex_unlock(&st->lock);
if (ret < 0)
return ret;
......@@ -642,7 +653,7 @@ static int ad9523_read_raw(struct iio_dev *indio_dev,
code = (AD9523_CLK_DIST_DIV_PHASE_REV(ret) * 3141592) /
AD9523_CLK_DIST_DIV_REV(ret);
*val = code / 1000000;
*val2 = (code % 1000000) * 10;
*val2 = code % 1000000;
return IIO_VAL_INT_PLUS_MICRO;
default:
return -EINVAL;
......@@ -659,7 +670,7 @@ static int ad9523_write_raw(struct iio_dev *indio_dev,
unsigned int reg;
int ret, tmp, code;
mutex_lock(&indio_dev->mlock);
mutex_lock(&st->lock);
ret = ad9523_read(indio_dev, AD9523_CHANNEL_CLOCK_DIST(chan->channel));
if (ret < 0)
goto out;
......@@ -705,7 +716,7 @@ static int ad9523_write_raw(struct iio_dev *indio_dev,
ad9523_io_update(indio_dev);
out:
mutex_unlock(&indio_dev->mlock);
mutex_unlock(&st->lock);
return ret;
}
......@@ -713,9 +724,10 @@ static int ad9523_reg_access(struct iio_dev *indio_dev,
unsigned int reg, unsigned int writeval,
unsigned int *readval)
{
struct ad9523_state *st = iio_priv(indio_dev);
int ret;
mutex_lock(&indio_dev->mlock);
mutex_lock(&st->lock);
if (readval == NULL) {
ret = ad9523_write(indio_dev, reg | AD9523_R1B, writeval);
ad9523_io_update(indio_dev);
......@@ -728,7 +740,7 @@ static int ad9523_reg_access(struct iio_dev *indio_dev,
}
out_unlock:
mutex_unlock(&indio_dev->mlock);
mutex_unlock(&st->lock);
return ret;
}
......@@ -967,6 +979,8 @@ static int ad9523_probe(struct spi_device *spi)
st = iio_priv(indio_dev);
mutex_init(&st->lock);
st->reg = devm_regulator_get(&spi->dev, "vcc");
if (!IS_ERR(st->reg)) {
ret = regulator_enable(st->reg);
......
......@@ -81,9 +81,11 @@ int adis_write_reg(struct adis *adis, unsigned int reg,
adis->tx[9] = (value >> 24) & 0xff;
adis->tx[6] = ADIS_WRITE_REG(reg + 2);
adis->tx[7] = (value >> 16) & 0xff;
/* fall through */
case 2:
adis->tx[4] = ADIS_WRITE_REG(reg + 1);
adis->tx[5] = (value >> 8) & 0xff;
/* fall through */
case 1:
adis->tx[2] = ADIS_WRITE_REG(reg);
adis->tx[3] = value & 0xff;
......@@ -167,6 +169,7 @@ int adis_read_reg(struct adis *adis, unsigned int reg,
adis->tx[2] = ADIS_READ_REG(reg + 2);
adis->tx[3] = 0;
spi_message_add_tail(&xfers[1], &msg);
/* fall through */
case 2:
adis->tx[4] = ADIS_READ_REG(reg);
adis->tx[5] = 0;
......
......@@ -103,6 +103,12 @@ static const struct inv_mpu6050_hw hw_info[] = {
.reg = &reg_set_6500,
.config = &chip_config_6050,
},
{
.whoami = INV_MPU6515_WHOAMI_VALUE,
.name = "MPU6515",
.reg = &reg_set_6500,
.config = &chip_config_6050,
},
{
.whoami = INV_MPU6000_WHOAMI_VALUE,
.name = "MPU6000",
......
......@@ -174,6 +174,7 @@ static int inv_mpu_remove(struct i2c_client *client)
static const struct i2c_device_id inv_mpu_id[] = {
{"mpu6050", INV_MPU6050},
{"mpu6500", INV_MPU6500},
{"mpu6515", INV_MPU6515},
{"mpu9150", INV_MPU9150},
{"mpu9250", INV_MPU9250},
{"mpu9255", INV_MPU9255},
......@@ -192,6 +193,10 @@ static const struct of_device_id inv_of_match[] = {
.compatible = "invensense,mpu6500",
.data = (void *)INV_MPU6500
},
{
.compatible = "invensense,mpu6515",
.data = (void *)INV_MPU6515
},
{
.compatible = "invensense,mpu9150",
.data = (void *)INV_MPU9150
......
......@@ -71,6 +71,7 @@ struct inv_mpu6050_reg_map {
enum inv_devices {
INV_MPU6050,
INV_MPU6500,
INV_MPU6515,
INV_MPU6000,
INV_MPU9150,
INV_MPU9250,
......@@ -256,6 +257,7 @@ struct inv_mpu6050_state {
#define INV_MPU9150_WHOAMI_VALUE 0x68
#define INV_MPU9250_WHOAMI_VALUE 0x71
#define INV_MPU9255_WHOAMI_VALUE 0x73
#define INV_MPU6515_WHOAMI_VALUE 0x74
#define INV_ICM20608_WHOAMI_VALUE 0xAF
/* scan element definition */
......
......@@ -298,8 +298,11 @@ static int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw)
err = regmap_bulk_read(hw->regmap,
hw->settings->fifo_ops.fifo_diff.addr,
&fifo_status, sizeof(fifo_status));
if (err < 0)
if (err < 0) {
dev_err(hw->dev, "failed to read fifo status (err=%d)\n",
err);
return err;
}
if (fifo_status & cpu_to_le16(ST_LSM6DSX_FIFO_EMPTY_MASK))
return 0;
......@@ -313,8 +316,12 @@ static int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw)
for (read_len = 0; read_len < fifo_len; read_len += pattern_len) {
err = st_lsm6dsx_read_block(hw, hw->buff, pattern_len);
if (err < 0)
if (err < 0) {
dev_err(hw->dev,
"failed to read pattern from fifo (err=%d)\n",
err);
return err;
}
/*
* Data are written to the FIFO with a specific pattern
......@@ -385,8 +392,11 @@ static int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw)
if (unlikely(reset_ts)) {
err = st_lsm6dsx_reset_hw_ts(hw);
if (err < 0)
if (err < 0) {
dev_err(hw->dev, "failed to reset hw ts (err=%d)\n",
err);
return err;
}
}
return read_len;
}
......
......@@ -86,6 +86,7 @@ static const char * const iio_chan_type_name_spec[] = {
[IIO_INDEX] = "index",
[IIO_GRAVITY] = "gravity",
[IIO_POSITIONRELATIVE] = "positionrelative",
[IIO_PHASE] = "phase",
};
static const char * const iio_modifier_names[] = {
......@@ -109,6 +110,7 @@ static const char * const iio_modifier_names[] = {
[IIO_MOD_LIGHT_GREEN] = "green",
[IIO_MOD_LIGHT_BLUE] = "blue",
[IIO_MOD_LIGHT_UV] = "uv",
[IIO_MOD_LIGHT_DUV] = "duv",
[IIO_MOD_QUATERNION] = "quaternion",
[IIO_MOD_TEMP_AMBIENT] = "ambient",
[IIO_MOD_TEMP_OBJECT] = "object",
......
#
# Light sensors
#
......@@ -319,6 +320,17 @@ config PA12203001
This driver can also be built as a module. If so, the module
will be called pa12203001.
config SI1133
tristate "SI1133 UV Index Sensor and Ambient Light Sensor"
depends on I2C
select REGMAP_I2C
help
Say Y here if you want to build a driver for the Silicon Labs SI1133
UV Index Sensor and Ambient Light Sensor chip.
To compile this driver as a module, choose M here: the module will be
called si1133.
config SI1145
tristate "SI1132 and SI1141/2/3/5/6/7 combined ALS, UV index and proximity sensor"
depends on I2C
......
......@@ -32,6 +32,7 @@ obj-$(CONFIG_OPT3001) += opt3001.o
obj-$(CONFIG_PA12203001) += pa12203001.o
obj-$(CONFIG_RPR0521) += rpr0521.o
obj-$(CONFIG_SENSORS_TSL2563) += tsl2563.o
obj-$(CONFIG_SI1133) += si1133.o
obj-$(CONFIG_SI1145) += si1145.o
obj-$(CONFIG_STK3310) += stk3310.o
obj-$(CONFIG_ST_UVIS25) += st_uvis25_core.o
......
// SPDX-License-Identifier: GPL-2.0+
/*
* si1133.c - Support for Silabs SI1133 combined ambient
* light and UV index sensors
*
* Copyright 2018 Maxime Roussin-Belanger <maxime.roussinbelanger@gmail.com>
*/
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/util_macros.h>
#define SI1133_REG_PART_ID 0x00
#define SI1133_REG_REV_ID 0x01
#define SI1133_REG_MFR_ID 0x02
#define SI1133_REG_INFO0 0x03
#define SI1133_REG_INFO1 0x04
#define SI1133_PART_ID 0x33
#define SI1133_REG_HOSTIN0 0x0A
#define SI1133_REG_COMMAND 0x0B
#define SI1133_REG_IRQ_ENABLE 0x0F
#define SI1133_REG_RESPONSE1 0x10
#define SI1133_REG_RESPONSE0 0x11
#define SI1133_REG_IRQ_STATUS 0x12
#define SI1133_REG_MEAS_RATE 0x1A
#define SI1133_IRQ_CHANNEL_ENABLE 0xF
#define SI1133_CMD_RESET_CTR 0x00
#define SI1133_CMD_RESET_SW 0x01
#define SI1133_CMD_FORCE 0x11
#define SI1133_CMD_START_AUTONOMOUS 0x13
#define SI1133_CMD_PARAM_SET 0x80
#define SI1133_CMD_PARAM_QUERY 0x40
#define SI1133_CMD_PARAM_MASK 0x3F
#define SI1133_CMD_ERR_MASK BIT(4)
#define SI1133_CMD_SEQ_MASK 0xF
#define SI1133_MAX_CMD_CTR 0xF
#define SI1133_PARAM_REG_CHAN_LIST 0x01
#define SI1133_PARAM_REG_ADCCONFIG(x) ((x) * 4) + 2
#define SI1133_PARAM_REG_ADCSENS(x) ((x) * 4) + 3
#define SI1133_PARAM_REG_ADCPOST(x) ((x) * 4) + 4
#define SI1133_ADCMUX_MASK 0x1F
#define SI1133_ADCCONFIG_DECIM_RATE(x) (x) << 5
#define SI1133_ADCSENS_SCALE_MASK 0x70
#define SI1133_ADCSENS_SCALE_SHIFT 4
#define SI1133_ADCSENS_HSIG_MASK BIT(7)
#define SI1133_ADCSENS_HSIG_SHIFT 7
#define SI1133_ADCSENS_HW_GAIN_MASK 0xF
#define SI1133_ADCSENS_NB_MEAS(x) fls(x) << SI1133_ADCSENS_SCALE_SHIFT
#define SI1133_ADCPOST_24BIT_EN BIT(6)
#define SI1133_ADCPOST_POSTSHIFT_BITQTY(x) (x & GENMASK(2, 0)) << 3
#define SI1133_PARAM_ADCMUX_SMALL_IR 0x0
#define SI1133_PARAM_ADCMUX_MED_IR 0x1
#define SI1133_PARAM_ADCMUX_LARGE_IR 0x2
#define SI1133_PARAM_ADCMUX_WHITE 0xB
#define SI1133_PARAM_ADCMUX_LARGE_WHITE 0xD
#define SI1133_PARAM_ADCMUX_UV 0x18
#define SI1133_PARAM_ADCMUX_UV_DEEP 0x19
#define SI1133_ERR_INVALID_CMD 0x0
#define SI1133_ERR_INVALID_LOCATION_CMD 0x1
#define SI1133_ERR_SATURATION_ADC_OR_OVERFLOW_ACCUMULATION 0x2
#define SI1133_ERR_OUTPUT_BUFFER_OVERFLOW 0x3
#define SI1133_COMPLETION_TIMEOUT_MS 500
#define SI1133_CMD_MINSLEEP_US_LOW 5000
#define SI1133_CMD_MINSLEEP_US_HIGH 7500
#define SI1133_CMD_TIMEOUT_MS 25
#define SI1133_CMD_LUX_TIMEOUT_MS 5000
#define SI1133_CMD_TIMEOUT_US SI1133_CMD_TIMEOUT_MS * 1000
#define SI1133_REG_HOSTOUT(x) (x) + 0x13
#define SI1133_MEASUREMENT_FREQUENCY 1250
#define SI1133_X_ORDER_MASK 0x0070
#define SI1133_Y_ORDER_MASK 0x0007
#define si1133_get_x_order(m) ((m) & SI1133_X_ORDER_MASK) >> 4
#define si1133_get_y_order(m) ((m) & SI1133_Y_ORDER_MASK)
#define SI1133_LUX_ADC_MASK 0xE
#define SI1133_ADC_THRESHOLD 16000
#define SI1133_INPUT_FRACTION_HIGH 7
#define SI1133_INPUT_FRACTION_LOW 15
#define SI1133_LUX_OUTPUT_FRACTION 12
#define SI1133_LUX_BUFFER_SIZE 9
static const int si1133_scale_available[] = {
1, 2, 4, 8, 16, 32, 64, 128};
static IIO_CONST_ATTR(scale_available, "1 2 4 8 16 32 64 128");
static IIO_CONST_ATTR_INT_TIME_AVAIL("0.0244 0.0488 0.0975 0.195 0.390 0.780 "
"1.560 3.120 6.24 12.48 25.0 50.0");
/* A.K.A. HW_GAIN in datasheet */
enum si1133_int_time {
_24_4_us = 0,
_48_8_us = 1,
_97_5_us = 2,
_195_0_us = 3,
_390_0_us = 4,
_780_0_us = 5,
_1_560_0_us = 6,
_3_120_0_us = 7,
_6_240_0_us = 8,
_12_480_0_us = 9,
_25_ms = 10,
_50_ms = 11,
};
/* Integration time in milliseconds, nanoseconds */
static const int si1133_int_time_table[][2] = {
[_24_4_us] = {0, 24400},
[_48_8_us] = {0, 48800},
[_97_5_us] = {0, 97500},
[_195_0_us] = {0, 195000},
[_390_0_us] = {0, 390000},
[_780_0_us] = {0, 780000},
[_1_560_0_us] = {1, 560000},
[_3_120_0_us] = {3, 120000},
[_6_240_0_us] = {6, 240000},
[_12_480_0_us] = {12, 480000},
[_25_ms] = {25, 000000},
[_50_ms] = {50, 000000},
};
static const struct regmap_range si1133_reg_ranges[] = {
regmap_reg_range(0x00, 0x02),
regmap_reg_range(0x0A, 0x0B),
regmap_reg_range(0x0F, 0x0F),
regmap_reg_range(0x10, 0x12),
regmap_reg_range(0x13, 0x2C),
};
static const struct regmap_range si1133_reg_ro_ranges[] = {
regmap_reg_range(0x00, 0x02),
regmap_reg_range(0x10, 0x2C),
};
static const struct regmap_range si1133_precious_ranges[] = {
regmap_reg_range(0x12, 0x12),
};
static const struct regmap_access_table si1133_write_ranges_table = {
.yes_ranges = si1133_reg_ranges,
.n_yes_ranges = ARRAY_SIZE(si1133_reg_ranges),
.no_ranges = si1133_reg_ro_ranges,
.n_no_ranges = ARRAY_SIZE(si1133_reg_ro_ranges),
};
static const struct regmap_access_table si1133_read_ranges_table = {
.yes_ranges = si1133_reg_ranges,
.n_yes_ranges = ARRAY_SIZE(si1133_reg_ranges),
};
static const struct regmap_access_table si1133_precious_table = {
.yes_ranges = si1133_precious_ranges,
.n_yes_ranges = ARRAY_SIZE(si1133_precious_ranges),
};
static const struct regmap_config si1133_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = 0x2C,
.wr_table = &si1133_write_ranges_table,
.rd_table = &si1133_read_ranges_table,
.precious_table = &si1133_precious_table,
};
struct si1133_data {
struct regmap *regmap;
struct i2c_client *client;
/* Lock protecting one command at a time can be processed */
struct mutex mutex;
int rsp_seq;
u8 scan_mask;
u8 adc_sens[6];
u8 adc_config[6];
struct completion completion;
};
struct si1133_coeff {
s16 info;
u16 mag;
};
struct si1133_lux_coeff {
struct si1133_coeff coeff_high[4];
struct si1133_coeff coeff_low[9];
};
static const struct si1133_lux_coeff lux_coeff = {
{
{ 0, 209},
{ 1665, 93},
{ 2064, 65},
{-2671, 234}
},
{
{ 0, 0},
{ 1921, 29053},
{-1022, 36363},
{ 2320, 20789},
{ -367, 57909},
{-1774, 38240},
{ -608, 46775},
{-1503, 51831},
{-1886, 58928}
}
};
static int si1133_calculate_polynomial_inner(u32 input, u8 fraction, u16 mag,
s8 shift)
{
return ((input << fraction) / mag) << shift;
}
static int si1133_calculate_output(u32 x, u32 y, u8 x_order, u8 y_order,
u8 input_fraction, s8 sign,
const struct si1133_coeff *coeffs)
{
s8 shift;
int x1 = 1;
int x2 = 1;
int y1 = 1;
int y2 = 1;
shift = ((u16)coeffs->info & 0xFF00) >> 8;
shift ^= 0xFF;
shift += 1;
shift = -shift;
if (x_order > 0) {
x1 = si1133_calculate_polynomial_inner(x, input_fraction,
coeffs->mag, shift);
if (x_order > 1)
x2 = x1;
}
if (y_order > 0) {
y1 = si1133_calculate_polynomial_inner(y, input_fraction,
coeffs->mag, shift);
if (y_order > 1)
y2 = y1;
}
return sign * x1 * x2 * y1 * y2;
}
/*
* The algorithm is from:
* https://siliconlabs.github.io/Gecko_SDK_Doc/efm32zg/html/si1133_8c_source.html#l00716
*/
static int si1133_calc_polynomial(u32 x, u32 y, u8 input_fraction, u8 num_coeff,
const struct si1133_coeff *coeffs)
{
u8 x_order, y_order;
u8 counter;
s8 sign;
int output = 0;
for (counter = 0; counter < num_coeff; counter++) {
if (coeffs->info < 0)
sign = -1;
else
sign = 1;
x_order = si1133_get_x_order(coeffs->info);
y_order = si1133_get_y_order(coeffs->info);
if ((x_order == 0) && (y_order == 0))
output +=
sign * coeffs->mag << SI1133_LUX_OUTPUT_FRACTION;
else
output += si1133_calculate_output(x, y, x_order,
y_order,
input_fraction, sign,
coeffs);
coeffs++;
}
return abs(output);
}
static int si1133_cmd_reset_sw(struct si1133_data *data)
{
struct device *dev = &data->client->dev;
unsigned int resp;
unsigned long timeout;
int err;
err = regmap_write(data->regmap, SI1133_REG_COMMAND,
SI1133_CMD_RESET_SW);
if (err)
return err;
timeout = jiffies + msecs_to_jiffies(SI1133_CMD_TIMEOUT_MS);
while (true) {
err = regmap_read(data->regmap, SI1133_REG_RESPONSE0, &resp);
if (err == -ENXIO) {
usleep_range(SI1133_CMD_MINSLEEP_US_LOW,
SI1133_CMD_MINSLEEP_US_HIGH);
continue;
}
if ((resp & SI1133_MAX_CMD_CTR) == SI1133_MAX_CMD_CTR)
break;
if (time_after(jiffies, timeout)) {
dev_warn(dev, "Timeout on reset ctr resp: %d\n", resp);
return -ETIMEDOUT;
}
}
if (!err)
data->rsp_seq = SI1133_MAX_CMD_CTR;
return err;
}
static int si1133_parse_response_err(struct device *dev, u32 resp, u8 cmd)
{
resp &= 0xF;
switch (resp) {
case SI1133_ERR_OUTPUT_BUFFER_OVERFLOW:
dev_warn(dev, "Output buffer overflow: %#02hhx\n", cmd);
return -EOVERFLOW;
case SI1133_ERR_SATURATION_ADC_OR_OVERFLOW_ACCUMULATION:
dev_warn(dev, "Saturation of the ADC or overflow of accumulation: %#02hhx\n",
cmd);
return -EOVERFLOW;
case SI1133_ERR_INVALID_LOCATION_CMD:
dev_warn(dev,
"Parameter access to an invalid location: %#02hhx\n",
cmd);
return -EINVAL;
case SI1133_ERR_INVALID_CMD:
dev_warn(dev, "Invalid command %#02hhx\n", cmd);
return -EINVAL;
default:
dev_warn(dev, "Unknown error %#02hhx\n", cmd);
return -EINVAL;
}
}
static int si1133_cmd_reset_counter(struct si1133_data *data)
{
int err = regmap_write(data->regmap, SI1133_REG_COMMAND,
SI1133_CMD_RESET_CTR);
if (err)
return err;
data->rsp_seq = 0;
return 0;
}
static int si1133_command(struct si1133_data *data, u8 cmd)
{
struct device *dev = &data->client->dev;
u32 resp;
int err;
int expected_seq;
mutex_lock(&data->mutex);
expected_seq = (data->rsp_seq + 1) & SI1133_MAX_CMD_CTR;
if (cmd == SI1133_CMD_FORCE)
reinit_completion(&data->completion);
err = regmap_write(data->regmap, SI1133_REG_COMMAND, cmd);
if (err) {
dev_warn(dev, "Failed to write command %#02hhx, ret=%d\n", cmd,
err);
goto out;
}
if (cmd == SI1133_CMD_FORCE) {
/* wait for irq */
if (!wait_for_completion_timeout(&data->completion,
msecs_to_jiffies(SI1133_COMPLETION_TIMEOUT_MS))) {
err = -ETIMEDOUT;
goto out;
}
} else {
err = regmap_read_poll_timeout(data->regmap,
SI1133_REG_RESPONSE0, resp,
(resp & SI1133_CMD_SEQ_MASK) ==
expected_seq ||
(resp & SI1133_CMD_ERR_MASK),
SI1133_CMD_MINSLEEP_US_LOW,
SI1133_CMD_TIMEOUT_MS * 1000);
if (err) {
dev_warn(dev,
"Failed to read command %#02hhx, ret=%d\n",
cmd, err);
goto out;
}
}
if (resp & SI1133_CMD_ERR_MASK) {
err = si1133_parse_response_err(dev, resp, cmd);
si1133_cmd_reset_counter(data);
} else {
data->rsp_seq = expected_seq;
}
out:
mutex_unlock(&data->mutex);
return err;
}
static int si1133_param_set(struct si1133_data *data, u8 param, u32 value)
{
int err = regmap_write(data->regmap, SI1133_REG_HOSTIN0, value);
if (err)
return err;
return si1133_command(data, SI1133_CMD_PARAM_SET |
(param & SI1133_CMD_PARAM_MASK));
}
static int si1133_param_query(struct si1133_data *data, u8 param, u32 *result)
{
int err = si1133_command(data, SI1133_CMD_PARAM_QUERY |
(param & SI1133_CMD_PARAM_MASK));
if (err)
return err;
return regmap_read(data->regmap, SI1133_REG_RESPONSE1, result);
}
#define SI1133_CHANNEL(_ch, _type) \
.type = _type, \
.channel = _ch, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME) | \
BIT(IIO_CHAN_INFO_SCALE) | \
BIT(IIO_CHAN_INFO_HARDWAREGAIN), \
static const struct iio_chan_spec si1133_channels[] = {
{
.type = IIO_LIGHT,
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
.channel = 0,
},
{
SI1133_CHANNEL(SI1133_PARAM_ADCMUX_WHITE, IIO_INTENSITY)
.channel2 = IIO_MOD_LIGHT_BOTH,
},
{
SI1133_CHANNEL(SI1133_PARAM_ADCMUX_LARGE_WHITE, IIO_INTENSITY)
.channel2 = IIO_MOD_LIGHT_BOTH,
.extend_name = "large",
},
{
SI1133_CHANNEL(SI1133_PARAM_ADCMUX_SMALL_IR, IIO_INTENSITY)
.extend_name = "small",
.modified = 1,
.channel2 = IIO_MOD_LIGHT_IR,
},
{
SI1133_CHANNEL(SI1133_PARAM_ADCMUX_MED_IR, IIO_INTENSITY)
.modified = 1,
.channel2 = IIO_MOD_LIGHT_IR,
},
{
SI1133_CHANNEL(SI1133_PARAM_ADCMUX_LARGE_IR, IIO_INTENSITY)
.extend_name = "large",
.modified = 1,
.channel2 = IIO_MOD_LIGHT_IR,
},
{
SI1133_CHANNEL(SI1133_PARAM_ADCMUX_UV, IIO_UVINDEX)
},
{
SI1133_CHANNEL(SI1133_PARAM_ADCMUX_UV_DEEP, IIO_UVINDEX)
.modified = 1,
.channel2 = IIO_MOD_LIGHT_DUV,
}
};
static int si1133_get_int_time_index(int milliseconds, int nanoseconds)
{
int i;
for (i = 0; i < ARRAY_SIZE(si1133_int_time_table); i++) {
if (milliseconds == si1133_int_time_table[i][0] &&
nanoseconds == si1133_int_time_table[i][1])
return i;
}
return -EINVAL;
}
static int si1133_set_integration_time(struct si1133_data *data, u8 adc,
int milliseconds, int nanoseconds)
{
int index;
index = si1133_get_int_time_index(milliseconds, nanoseconds);
if (index < 0)
return index;
data->adc_sens[adc] &= 0xF0;
data->adc_sens[adc] |= index;
return si1133_param_set(data, SI1133_PARAM_REG_ADCSENS(0),
data->adc_sens[adc]);
}
static int si1133_set_chlist(struct si1133_data *data, u8 scan_mask)
{
/* channel list already set, no need to reprogram */
if (data->scan_mask == scan_mask)
return 0;
data->scan_mask = scan_mask;
return si1133_param_set(data, SI1133_PARAM_REG_CHAN_LIST, scan_mask);
}
static int si1133_chan_set_adcconfig(struct si1133_data *data, u8 adc,
u8 adc_config)
{
int err;
err = si1133_param_set(data, SI1133_PARAM_REG_ADCCONFIG(adc),
adc_config);
if (err)
return err;
data->adc_config[adc] = adc_config;
return 0;
}
static int si1133_update_adcconfig(struct si1133_data *data, uint8_t adc,
u8 mask, u8 shift, u8 value)
{
u32 adc_config;
int err;
err = si1133_param_query(data, SI1133_PARAM_REG_ADCCONFIG(adc),
&adc_config);
if (err)
return err;
adc_config &= ~mask;
adc_config |= (value << shift);
return si1133_chan_set_adcconfig(data, adc, adc_config);
}
static int si1133_set_adcmux(struct si1133_data *data, u8 adc, u8 mux)
{
if ((mux & data->adc_config[adc]) == mux)
return 0; /* mux already set to correct value */
return si1133_update_adcconfig(data, adc, SI1133_ADCMUX_MASK, 0, mux);
}
static int si1133_force_measurement(struct si1133_data *data)
{
return si1133_command(data, SI1133_CMD_FORCE);
}
static int si1133_bulk_read(struct si1133_data *data, u8 start_reg, u8 length,
u8 *buffer)
{
int err;
err = si1133_force_measurement(data);
if (err)
return err;
return regmap_bulk_read(data->regmap, start_reg, buffer, length);
}
static int si1133_measure(struct si1133_data *data,
struct iio_chan_spec const *chan,
int *val)
{
int err;
__be16 resp;
err = si1133_set_adcmux(data, 0, chan->channel);
if (err)
return err;
/* Deactivate lux measurements if they were active */
err = si1133_set_chlist(data, BIT(0));
if (err)
return err;
err = si1133_bulk_read(data, SI1133_REG_HOSTOUT(0), sizeof(resp),
(u8 *)&resp);
if (err)
return err;
*val = be16_to_cpu(resp);
return err;
}
static irqreturn_t si1133_threaded_irq_handler(int irq, void *private)
{
struct iio_dev *iio_dev = private;
struct si1133_data *data = iio_priv(iio_dev);
u32 irq_status;
int err;
err = regmap_read(data->regmap, SI1133_REG_IRQ_STATUS, &irq_status);
if (err) {
dev_err_ratelimited(&iio_dev->dev, "Error reading IRQ\n");
goto out;
}
if (irq_status != data->scan_mask)
return IRQ_NONE;
out:
complete(&data->completion);
return IRQ_HANDLED;
}
static int si1133_scale_to_swgain(int scale_integer, int scale_fractional)
{
scale_integer = find_closest(scale_integer, si1133_scale_available,
ARRAY_SIZE(si1133_scale_available));
if (scale_integer < 0 ||
scale_integer > ARRAY_SIZE(si1133_scale_available) ||
scale_fractional != 0)
return -EINVAL;
return scale_integer;
}
static int si1133_chan_set_adcsens(struct si1133_data *data, u8 adc,
u8 adc_sens)
{
int err;
err = si1133_param_set(data, SI1133_PARAM_REG_ADCSENS(adc), adc_sens);
if (err)
return err;
data->adc_sens[adc] = adc_sens;
return 0;
}
static int si1133_update_adcsens(struct si1133_data *data, u8 mask,
u8 shift, u8 value)
{
int err;
u32 adc_sens;
err = si1133_param_query(data, SI1133_PARAM_REG_ADCSENS(0),
&adc_sens);
if (err)
return err;
adc_sens &= ~mask;
adc_sens |= (value << shift);
return si1133_chan_set_adcsens(data, 0, adc_sens);
}
static int si1133_get_lux(struct si1133_data *data, int *val)
{
int err;
int lux;
u32 high_vis;
u32 low_vis;
u32 ir;
u8 buffer[SI1133_LUX_BUFFER_SIZE];
/* Activate lux channels */
err = si1133_set_chlist(data, SI1133_LUX_ADC_MASK);
if (err)
return err;
err = si1133_bulk_read(data, SI1133_REG_HOSTOUT(0),
SI1133_LUX_BUFFER_SIZE, buffer);
if (err)
return err;
high_vis = (buffer[0] << 16) | (buffer[1] << 8) | buffer[2];
low_vis = (buffer[3] << 16) | (buffer[4] << 8) | buffer[5];
ir = (buffer[6] << 16) | (buffer[7] << 8) | buffer[8];
if (high_vis > SI1133_ADC_THRESHOLD || ir > SI1133_ADC_THRESHOLD)
lux = si1133_calc_polynomial(high_vis, ir,
SI1133_INPUT_FRACTION_HIGH,
ARRAY_SIZE(lux_coeff.coeff_high),
&lux_coeff.coeff_high[0]);
else
lux = si1133_calc_polynomial(low_vis, ir,
SI1133_INPUT_FRACTION_LOW,
ARRAY_SIZE(lux_coeff.coeff_low),
&lux_coeff.coeff_low[0]);
*val = lux >> SI1133_LUX_OUTPUT_FRACTION;
return err;
}
static int si1133_read_raw(struct iio_dev *iio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct si1133_data *data = iio_priv(iio_dev);
u8 adc_sens = data->adc_sens[0];
int err;
switch (mask) {
case IIO_CHAN_INFO_PROCESSED:
switch (chan->type) {
case IIO_LIGHT:
err = si1133_get_lux(data, val);
if (err)
return err;
return IIO_VAL_INT;
default:
return -EINVAL;
}
case IIO_CHAN_INFO_RAW:
switch (chan->type) {
case IIO_INTENSITY:
case IIO_UVINDEX:
err = si1133_measure(data, chan, val);
if (err)
return err;
return IIO_VAL_INT;
default:
return -EINVAL;
}
case IIO_CHAN_INFO_INT_TIME:
switch (chan->type) {
case IIO_INTENSITY:
case IIO_UVINDEX:
adc_sens &= SI1133_ADCSENS_HW_GAIN_MASK;
*val = si1133_int_time_table[adc_sens][0];
*val2 = si1133_int_time_table[adc_sens][1];
return IIO_VAL_INT_PLUS_MICRO;
default:
return -EINVAL;
}
case IIO_CHAN_INFO_SCALE:
switch (chan->type) {
case IIO_INTENSITY:
case IIO_UVINDEX:
adc_sens &= SI1133_ADCSENS_SCALE_MASK;
adc_sens >>= SI1133_ADCSENS_SCALE_SHIFT;
*val = BIT(adc_sens);
return IIO_VAL_INT;
default:
return -EINVAL;
}
case IIO_CHAN_INFO_HARDWAREGAIN:
switch (chan->type) {
case IIO_INTENSITY:
case IIO_UVINDEX:
adc_sens >>= SI1133_ADCSENS_HSIG_SHIFT;
*val = adc_sens;
return IIO_VAL_INT;
default:
return -EINVAL;
}
default:
return -EINVAL;
}
}
static int si1133_write_raw(struct iio_dev *iio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
struct si1133_data *data = iio_priv(iio_dev);
switch (mask) {
case IIO_CHAN_INFO_SCALE:
switch (chan->type) {
case IIO_INTENSITY:
case IIO_UVINDEX:
val = si1133_scale_to_swgain(val, val2);
if (val < 0)
return val;
return si1133_update_adcsens(data,
SI1133_ADCSENS_SCALE_MASK,
SI1133_ADCSENS_SCALE_SHIFT,
val);
default:
return -EINVAL;
}
case IIO_CHAN_INFO_INT_TIME:
return si1133_set_integration_time(data, 0, val, val2);
case IIO_CHAN_INFO_HARDWAREGAIN:
switch (chan->type) {
case IIO_INTENSITY:
case IIO_UVINDEX:
if (val != 0 || val != 1)
return -EINVAL;
return si1133_update_adcsens(data,
SI1133_ADCSENS_HSIG_MASK,
SI1133_ADCSENS_HSIG_SHIFT,
val);
default:
return -EINVAL;
}
default:
return -EINVAL;
}
}
static struct attribute *si1133_attributes[] = {
&iio_const_attr_integration_time_available.dev_attr.attr,
&iio_const_attr_scale_available.dev_attr.attr,
NULL,
};
static const struct attribute_group si1133_attribute_group = {
.attrs = si1133_attributes,
};
static const struct iio_info si1133_info = {
.read_raw = si1133_read_raw,
.write_raw = si1133_write_raw,
.attrs = &si1133_attribute_group,
};
/*
* si1133_init_lux_channels - Configure 3 different channels(adc) (1,2 and 3)
* The channel configuration for the lux measurement was taken from :
* https://siliconlabs.github.io/Gecko_SDK_Doc/efm32zg/html/si1133_8c_source.html#l00578
*
* Reserved the channel 0 for the other raw measurements
*/
static int si1133_init_lux_channels(struct si1133_data *data)
{
int err;
err = si1133_chan_set_adcconfig(data, 1,
SI1133_ADCCONFIG_DECIM_RATE(1) |
SI1133_PARAM_ADCMUX_LARGE_WHITE);
if (err)
return err;
err = si1133_param_set(data, SI1133_PARAM_REG_ADCPOST(1),
SI1133_ADCPOST_24BIT_EN |
SI1133_ADCPOST_POSTSHIFT_BITQTY(0));
if (err)
return err;
err = si1133_chan_set_adcsens(data, 1, SI1133_ADCSENS_HSIG_MASK |
SI1133_ADCSENS_NB_MEAS(64) | _48_8_us);
if (err)
return err;
err = si1133_chan_set_adcconfig(data, 2,
SI1133_ADCCONFIG_DECIM_RATE(1) |
SI1133_PARAM_ADCMUX_LARGE_WHITE);
if (err)
return err;
err = si1133_param_set(data, SI1133_PARAM_REG_ADCPOST(2),
SI1133_ADCPOST_24BIT_EN |
SI1133_ADCPOST_POSTSHIFT_BITQTY(2));
if (err)
return err;
err = si1133_chan_set_adcsens(data, 2, SI1133_ADCSENS_HSIG_MASK |
SI1133_ADCSENS_NB_MEAS(1) | _3_120_0_us);
if (err)
return err;
err = si1133_chan_set_adcconfig(data, 3,
SI1133_ADCCONFIG_DECIM_RATE(1) |
SI1133_PARAM_ADCMUX_MED_IR);
if (err)
return err;
err = si1133_param_set(data, SI1133_PARAM_REG_ADCPOST(3),
SI1133_ADCPOST_24BIT_EN |
SI1133_ADCPOST_POSTSHIFT_BITQTY(2));
if (err)
return err;
return si1133_chan_set_adcsens(data, 3, SI1133_ADCSENS_HSIG_MASK |
SI1133_ADCSENS_NB_MEAS(64) | _48_8_us);
}
static int si1133_initialize(struct si1133_data *data)
{
int err;
err = si1133_cmd_reset_sw(data);
if (err)
return err;
/* Turn off autonomous mode */
err = si1133_param_set(data, SI1133_REG_MEAS_RATE, 0);
if (err)
return err;
err = si1133_init_lux_channels(data);
if (err)
return err;
return regmap_write(data->regmap, SI1133_REG_IRQ_ENABLE,
SI1133_IRQ_CHANNEL_ENABLE);
}
static int si1133_validate_ids(struct iio_dev *iio_dev)
{
struct si1133_data *data = iio_priv(iio_dev);
unsigned int part_id, rev_id, mfr_id;
int err;
err = regmap_read(data->regmap, SI1133_REG_PART_ID, &part_id);
if (err)
return err;
err = regmap_read(data->regmap, SI1133_REG_REV_ID, &rev_id);
if (err)
return err;
err = regmap_read(data->regmap, SI1133_REG_MFR_ID, &mfr_id);
if (err)
return err;
dev_info(&iio_dev->dev,
"Device ID part %#02hhx rev %#02hhx mfr %#02hhx\n",
part_id, rev_id, mfr_id);
if (part_id != SI1133_PART_ID) {
dev_err(&iio_dev->dev,
"Part ID mismatch got %#02hhx, expected %#02x\n",
part_id, SI1133_PART_ID);
return -ENODEV;
}
return 0;
}
static int si1133_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct si1133_data *data;
struct iio_dev *iio_dev;
int err;
iio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (!iio_dev)
return -ENOMEM;
data = iio_priv(iio_dev);
init_completion(&data->completion);
data->regmap = devm_regmap_init_i2c(client, &si1133_regmap_config);
if (IS_ERR(data->regmap)) {
err = PTR_ERR(data->regmap);
dev_err(&client->dev, "Failed to initialise regmap: %d\n", err);
return err;
}
i2c_set_clientdata(client, iio_dev);
data->client = client;
iio_dev->dev.parent = &client->dev;
iio_dev->name = id->name;
iio_dev->channels = si1133_channels;
iio_dev->num_channels = ARRAY_SIZE(si1133_channels);
iio_dev->info = &si1133_info;
iio_dev->modes = INDIO_DIRECT_MODE;
mutex_init(&data->mutex);
err = si1133_validate_ids(iio_dev);
if (err)
return err;
err = si1133_initialize(data);
if (err) {
dev_err(&client->dev,
"Error when initializing chip: %d\n", err);
return err;
}
if (!client->irq) {
dev_err(&client->dev,
"Required interrupt not provided, cannot proceed\n");
return -EINVAL;
}
err = devm_request_threaded_irq(&client->dev, client->irq,
NULL,
si1133_threaded_irq_handler,
IRQF_ONESHOT | IRQF_SHARED,
client->name, iio_dev);
if (err) {
dev_warn(&client->dev, "Request irq %d failed: %i\n",
client->irq, err);
return err;
}
return devm_iio_device_register(&client->dev, iio_dev);
}
static const struct i2c_device_id si1133_ids[] = {
{ "si1133", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, si1133_ids);
static struct i2c_driver si1133_driver = {
.driver = {
.name = "si1133",
},
.probe = si1133_probe,
.id_table = si1133_ids,
};
module_i2c_driver(si1133_driver);
MODULE_AUTHOR("Maxime Roussin-Belanger <maxime.roussinbelanger@gmail.com>");
MODULE_DESCRIPTION("Silabs SI1133, UV index sensor and ambient light sensor driver");
MODULE_LICENSE("GPL");
......@@ -94,9 +94,8 @@ static int st_press_i2c_probe(struct i2c_client *client,
if ((ret < 0) || (ret >= ST_PRESS_MAX))
return -ENODEV;
strncpy(client->name, st_press_id_table[ret].name,
strlcpy(client->name, st_press_id_table[ret].name,
sizeof(client->name));
client->name[sizeof(client->name) - 1] = '\0';
} else if (!id)
return -ENODEV;
......
......@@ -20,6 +20,19 @@ endmenu
menu "Proximity and distance sensors"
config ISL29501
tristate "Intersil ISL29501 Time Of Flight sensor"
depends on I2C
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
select IIO_KFIFO_BUF
help
Say Y here if you want to build a driver for the Intersil ISL29501
Time of Flight sensor.
To compile this driver as a module, choose M here: the module will be
called isl29501.
config LIDAR_LITE_V2
tristate "PulsedLight LIDAR sensor"
select IIO_BUFFER
......
......@@ -5,6 +5,7 @@
# When adding new entries keep the list in alphabetical order
obj-$(CONFIG_AS3935) += as3935.o
obj-$(CONFIG_ISL29501) += isl29501.o
obj-$(CONFIG_LIDAR_LITE_V2) += pulsedlight-lidar-lite-v2.o
obj-$(CONFIG_RFD77402) += rfd77402.o
obj-$(CONFIG_SRF04) += srf04.o
......
// SPDX-License-Identifier: GPL-2.0
/*
* isl29501.c: ISL29501 Time of Flight sensor driver.
*
* Copyright (C) 2018
* Author: Mathieu Othacehe <m.othacehe@gmail.com>
*
* 7-bit I2C slave address: 0x57
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/err.h>
#include <linux/of_device.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/buffer.h>
#include <linux/iio/triggered_buffer.h>
/* Control, setting and status registers */
#define ISL29501_DEVICE_ID 0x00
#define ISL29501_ID 0x0A
/* Sampling control registers */
#define ISL29501_INTEGRATION_PERIOD 0x10
#define ISL29501_SAMPLE_PERIOD 0x11
/* Closed loop calibration registers */
#define ISL29501_CROSSTALK_I_MSB 0x24
#define ISL29501_CROSSTALK_I_LSB 0x25
#define ISL29501_CROSSTALK_I_EXPONENT 0x26
#define ISL29501_CROSSTALK_Q_MSB 0x27
#define ISL29501_CROSSTALK_Q_LSB 0x28
#define ISL29501_CROSSTALK_Q_EXPONENT 0x29
#define ISL29501_CROSSTALK_GAIN_MSB 0x2A
#define ISL29501_CROSSTALK_GAIN_LSB 0x2B
#define ISL29501_MAGNITUDE_REF_EXP 0x2C
#define ISL29501_MAGNITUDE_REF_MSB 0x2D
#define ISL29501_MAGNITUDE_REF_LSB 0x2E
#define ISL29501_PHASE_OFFSET_MSB 0x2F
#define ISL29501_PHASE_OFFSET_LSB 0x30
/* Analog control registers */
#define ISL29501_DRIVER_RANGE 0x90
#define ISL29501_EMITTER_DAC 0x91
#define ISL29501_COMMAND_REGISTER 0xB0
/* Commands */
#define ISL29501_EMUL_SAMPLE_START_PIN 0x49
#define ISL29501_RESET_ALL_REGISTERS 0xD7
#define ISL29501_RESET_INT_SM 0xD1
/* Ambiant light and temperature corrections */
#define ISL29501_TEMP_REFERENCE 0x31
#define ISL29501_PHASE_EXPONENT 0x33
#define ISL29501_TEMP_COEFF_A 0x34
#define ISL29501_TEMP_COEFF_B 0x39
#define ISL29501_AMBIANT_COEFF_A 0x36
#define ISL29501_AMBIANT_COEFF_B 0x3B
/* Data output registers */
#define ISL29501_DISTANCE_MSB_DATA 0xD1
#define ISL29501_DISTANCE_LSB_DATA 0xD2
#define ISL29501_PRECISION_MSB 0xD3
#define ISL29501_PRECISION_LSB 0xD4
#define ISL29501_MAGNITUDE_EXPONENT 0xD5
#define ISL29501_MAGNITUDE_MSB 0xD6
#define ISL29501_MAGNITUDE_LSB 0xD7
#define ISL29501_PHASE_MSB 0xD8
#define ISL29501_PHASE_LSB 0xD9
#define ISL29501_I_RAW_EXPONENT 0xDA
#define ISL29501_I_RAW_MSB 0xDB
#define ISL29501_I_RAW_LSB 0xDC
#define ISL29501_Q_RAW_EXPONENT 0xDD
#define ISL29501_Q_RAW_MSB 0xDE
#define ISL29501_Q_RAW_LSB 0xDF
#define ISL29501_DIE_TEMPERATURE 0xE2
#define ISL29501_AMBIENT_LIGHT 0xE3
#define ISL29501_GAIN_MSB 0xE6
#define ISL29501_GAIN_LSB 0xE7
#define ISL29501_MAX_EXP_VAL 15
#define ISL29501_INT_TIME_AVAILABLE \
"0.00007 0.00014 0.00028 0.00057 0.00114 " \
"0.00228 0.00455 0.00910 0.01820 0.03640 " \
"0.07281 0.14561"
#define ISL29501_CURRENT_SCALE_AVAILABLE \
"0.0039 0.0078 0.0118 0.0157 0.0196 " \
"0.0235 0.0275 0.0314 0.0352 0.0392 " \
"0.0431 0.0471 0.0510 0.0549 0.0588"
enum isl29501_correction_coeff {
COEFF_TEMP_A,
COEFF_TEMP_B,
COEFF_LIGHT_A,
COEFF_LIGHT_B,
COEFF_MAX,
};
struct isl29501_private {
struct i2c_client *client;
struct mutex lock;
/* Exact representation of correction coefficients. */
unsigned int shadow_coeffs[COEFF_MAX];
};
enum isl29501_register_name {
REG_DISTANCE,
REG_PHASE,
REG_TEMPERATURE,
REG_AMBIENT_LIGHT,
REG_GAIN,
REG_GAIN_BIAS,
REG_PHASE_EXP,
REG_CALIB_PHASE_TEMP_A,
REG_CALIB_PHASE_TEMP_B,
REG_CALIB_PHASE_LIGHT_A,
REG_CALIB_PHASE_LIGHT_B,
REG_DISTANCE_BIAS,
REG_TEMPERATURE_BIAS,
REG_INT_TIME,
REG_SAMPLE_TIME,
REG_DRIVER_RANGE,
REG_EMITTER_DAC,
};
struct isl29501_register_desc {
u8 msb;
u8 lsb;
};
static const struct isl29501_register_desc isl29501_registers[] = {
[REG_DISTANCE] = {
.msb = ISL29501_DISTANCE_MSB_DATA,
.lsb = ISL29501_DISTANCE_LSB_DATA,
},
[REG_PHASE] = {
.msb = ISL29501_PHASE_MSB,
.lsb = ISL29501_PHASE_LSB,
},
[REG_TEMPERATURE] = {
.lsb = ISL29501_DIE_TEMPERATURE,
},
[REG_AMBIENT_LIGHT] = {
.lsb = ISL29501_AMBIENT_LIGHT,
},
[REG_GAIN] = {
.msb = ISL29501_GAIN_MSB,
.lsb = ISL29501_GAIN_LSB,
},
[REG_GAIN_BIAS] = {
.msb = ISL29501_CROSSTALK_GAIN_MSB,
.lsb = ISL29501_CROSSTALK_GAIN_LSB,
},
[REG_PHASE_EXP] = {
.lsb = ISL29501_PHASE_EXPONENT,
},
[REG_CALIB_PHASE_TEMP_A] = {
.lsb = ISL29501_TEMP_COEFF_A,
},
[REG_CALIB_PHASE_TEMP_B] = {
.lsb = ISL29501_TEMP_COEFF_B,
},
[REG_CALIB_PHASE_LIGHT_A] = {
.lsb = ISL29501_AMBIANT_COEFF_A,
},
[REG_CALIB_PHASE_LIGHT_B] = {
.lsb = ISL29501_AMBIANT_COEFF_B,
},
[REG_DISTANCE_BIAS] = {
.msb = ISL29501_PHASE_OFFSET_MSB,
.lsb = ISL29501_PHASE_OFFSET_LSB,
},
[REG_TEMPERATURE_BIAS] = {
.lsb = ISL29501_TEMP_REFERENCE,
},
[REG_INT_TIME] = {
.lsb = ISL29501_INTEGRATION_PERIOD,
},
[REG_SAMPLE_TIME] = {
.lsb = ISL29501_SAMPLE_PERIOD,
},
[REG_DRIVER_RANGE] = {
.lsb = ISL29501_DRIVER_RANGE,
},
[REG_EMITTER_DAC] = {
.lsb = ISL29501_EMITTER_DAC,
},
};
static int isl29501_register_read(struct isl29501_private *isl29501,
enum isl29501_register_name name,
u32 *val)
{
const struct isl29501_register_desc *reg = &isl29501_registers[name];
u8 msb = 0, lsb = 0;
s32 ret;
mutex_lock(&isl29501->lock);
if (reg->msb) {
ret = i2c_smbus_read_byte_data(isl29501->client, reg->msb);
if (ret < 0)
goto err;
msb = ret;
}
if (reg->lsb) {
ret = i2c_smbus_read_byte_data(isl29501->client, reg->lsb);
if (ret < 0)
goto err;
lsb = ret;
}
mutex_unlock(&isl29501->lock);
*val = (msb << 8) + lsb;
return 0;
err:
mutex_unlock(&isl29501->lock);
return ret;
}
static u32 isl29501_register_write(struct isl29501_private *isl29501,
enum isl29501_register_name name,
u32 value)
{
const struct isl29501_register_desc *reg = &isl29501_registers[name];
u8 msb, lsb;
int ret;
if (!reg->msb && value > U8_MAX)
return -ERANGE;
if (value > U16_MAX)
return -ERANGE;
if (!reg->msb) {
lsb = value & 0xFF;
} else {
msb = (value >> 8) & 0xFF;
lsb = value & 0xFF;
}
mutex_lock(&isl29501->lock);
if (reg->msb) {
ret = i2c_smbus_write_byte_data(isl29501->client,
reg->msb, msb);
if (ret < 0)
goto err;
}
ret = i2c_smbus_write_byte_data(isl29501->client, reg->lsb, lsb);
err:
mutex_unlock(&isl29501->lock);
return ret;
}
static ssize_t isl29501_read_ext(struct iio_dev *indio_dev,
uintptr_t private,
const struct iio_chan_spec *chan,
char *buf)
{
struct isl29501_private *isl29501 = iio_priv(indio_dev);
enum isl29501_register_name reg = private;
int ret;
u32 value, gain, coeff, exp;
switch (reg) {
case REG_GAIN:
case REG_GAIN_BIAS:
ret = isl29501_register_read(isl29501, reg, &gain);
if (ret < 0)
return ret;
value = gain;
break;
case REG_CALIB_PHASE_TEMP_A:
case REG_CALIB_PHASE_TEMP_B:
case REG_CALIB_PHASE_LIGHT_A:
case REG_CALIB_PHASE_LIGHT_B:
ret = isl29501_register_read(isl29501, REG_PHASE_EXP, &exp);
if (ret < 0)
return ret;
ret = isl29501_register_read(isl29501, reg, &coeff);
if (ret < 0)
return ret;
value = coeff << exp;
break;
default:
return -EINVAL;
}
return sprintf(buf, "%u\n", value);
}
static int isl29501_set_shadow_coeff(struct isl29501_private *isl29501,
enum isl29501_register_name reg,
unsigned int val)
{
enum isl29501_correction_coeff coeff;
switch (reg) {
case REG_CALIB_PHASE_TEMP_A:
coeff = COEFF_TEMP_A;
break;
case REG_CALIB_PHASE_TEMP_B:
coeff = COEFF_TEMP_B;
break;
case REG_CALIB_PHASE_LIGHT_A:
coeff = COEFF_LIGHT_A;
break;
case REG_CALIB_PHASE_LIGHT_B:
coeff = COEFF_LIGHT_B;
break;
default:
return -EINVAL;
}
isl29501->shadow_coeffs[coeff] = val;
return 0;
}
static int isl29501_write_coeff(struct isl29501_private *isl29501,
enum isl29501_correction_coeff coeff,
int val)
{
enum isl29501_register_name reg;
switch (coeff) {
case COEFF_TEMP_A:
reg = REG_CALIB_PHASE_TEMP_A;
break;
case COEFF_TEMP_B:
reg = REG_CALIB_PHASE_TEMP_B;
break;
case COEFF_LIGHT_A:
reg = REG_CALIB_PHASE_LIGHT_A;
break;
case COEFF_LIGHT_B:
reg = REG_CALIB_PHASE_LIGHT_B;
break;
default:
return -EINVAL;
}
return isl29501_register_write(isl29501, reg, val);
}
static unsigned int isl29501_find_corr_exp(unsigned int val,
unsigned int max_exp,
unsigned int max_mantissa)
{
unsigned int exp = 1;
/*
* Correction coefficients are represented under
* mantissa * 2^exponent form, where mantissa and exponent
* are stored in two separate registers of the sensor.
*
* Compute and return the lowest exponent such as:
* mantissa = value / 2^exponent
*
* where mantissa < max_mantissa.
*/
if (val <= max_mantissa)
return 0;
while ((val >> exp) > max_mantissa) {
exp++;
if (exp > max_exp)
return max_exp;
}
return exp;
}
static ssize_t isl29501_write_ext(struct iio_dev *indio_dev,
uintptr_t private,
const struct iio_chan_spec *chan,
const char *buf, size_t len)
{
struct isl29501_private *isl29501 = iio_priv(indio_dev);
enum isl29501_register_name reg = private;
unsigned int val;
int max_exp = 0;
int ret;
int i;
ret = kstrtouint(buf, 10, &val);
if (ret)
return ret;
switch (reg) {
case REG_GAIN_BIAS:
if (val > U16_MAX)
return -ERANGE;
ret = isl29501_register_write(isl29501, reg, val);
if (ret < 0)
return ret;
break;
case REG_CALIB_PHASE_TEMP_A:
case REG_CALIB_PHASE_TEMP_B:
case REG_CALIB_PHASE_LIGHT_A:
case REG_CALIB_PHASE_LIGHT_B:
if (val > (U8_MAX << ISL29501_MAX_EXP_VAL))
return -ERANGE;
/* Store the correction coefficient under its exact form. */
ret = isl29501_set_shadow_coeff(isl29501, reg, val);
if (ret < 0)
return ret;
/*
* Find the highest exponent needed to represent
* correction coefficients.
*/
for (i = 0; i < COEFF_MAX; i++) {
int corr;
int corr_exp;
corr = isl29501->shadow_coeffs[i];
corr_exp = isl29501_find_corr_exp(corr,
ISL29501_MAX_EXP_VAL,
U8_MAX / 2);
dev_dbg(&isl29501->client->dev,
"found exp of corr(%d) = %d\n", corr, corr_exp);
max_exp = max(max_exp, corr_exp);
}
/*
* Represent every correction coefficient under
* mantissa * 2^max_exponent form and force the
* writing of those coefficients on the sensor.
*/
for (i = 0; i < COEFF_MAX; i++) {
int corr;
int mantissa;
corr = isl29501->shadow_coeffs[i];
if (!corr)
continue;
mantissa = corr >> max_exp;
ret = isl29501_write_coeff(isl29501, i, mantissa);
if (ret < 0)
return ret;
}
ret = isl29501_register_write(isl29501, REG_PHASE_EXP, max_exp);
if (ret < 0)
return ret;
break;
default:
return -EINVAL;
}
return len;
}
#define _ISL29501_EXT_INFO(_name, _ident) { \
.name = _name, \
.read = isl29501_read_ext, \
.write = isl29501_write_ext, \
.private = _ident, \
.shared = IIO_SEPARATE, \
}
static const struct iio_chan_spec_ext_info isl29501_ext_info[] = {
_ISL29501_EXT_INFO("agc_gain", REG_GAIN),
_ISL29501_EXT_INFO("agc_gain_bias", REG_GAIN_BIAS),
_ISL29501_EXT_INFO("calib_phase_temp_a", REG_CALIB_PHASE_TEMP_A),
_ISL29501_EXT_INFO("calib_phase_temp_b", REG_CALIB_PHASE_TEMP_B),
_ISL29501_EXT_INFO("calib_phase_light_a", REG_CALIB_PHASE_LIGHT_A),
_ISL29501_EXT_INFO("calib_phase_light_b", REG_CALIB_PHASE_LIGHT_B),
{ },
};
#define ISL29501_DISTANCE_SCAN_INDEX 0
#define ISL29501_TIMESTAMP_SCAN_INDEX 1
static const struct iio_chan_spec isl29501_channels[] = {
{
.type = IIO_PROXIMITY,
.scan_index = ISL29501_DISTANCE_SCAN_INDEX,
.info_mask_separate =
BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_CALIBBIAS),
.scan_type = {
.sign = 'u',
.realbits = 16,
.storagebits = 16,
.endianness = IIO_CPU,
},
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME) |
BIT(IIO_CHAN_INFO_SAMP_FREQ),
.ext_info = isl29501_ext_info,
},
{
.type = IIO_PHASE,
.scan_index = -1,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE),
},
{
.type = IIO_CURRENT,
.scan_index = -1,
.output = 1,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE),
},
{
.type = IIO_TEMP,
.scan_index = -1,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_CALIBBIAS),
},
{
.type = IIO_INTENSITY,
.scan_index = -1,
.modified = 1,
.channel2 = IIO_MOD_LIGHT_CLEAR,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE),
},
IIO_CHAN_SOFT_TIMESTAMP(ISL29501_TIMESTAMP_SCAN_INDEX),
};
static int isl29501_reset_registers(struct isl29501_private *isl29501)
{
int ret;
ret = i2c_smbus_write_byte_data(isl29501->client,
ISL29501_COMMAND_REGISTER,
ISL29501_RESET_ALL_REGISTERS);
if (ret < 0) {
dev_err(&isl29501->client->dev,
"cannot reset registers %d\n", ret);
return ret;
}
ret = i2c_smbus_write_byte_data(isl29501->client,
ISL29501_COMMAND_REGISTER,
ISL29501_RESET_INT_SM);
if (ret < 0)
dev_err(&isl29501->client->dev,
"cannot reset state machine %d\n", ret);
return ret;
}
static int isl29501_begin_acquisition(struct isl29501_private *isl29501)
{
int ret;
ret = i2c_smbus_write_byte_data(isl29501->client,
ISL29501_COMMAND_REGISTER,
ISL29501_EMUL_SAMPLE_START_PIN);
if (ret < 0)
dev_err(&isl29501->client->dev,
"cannot begin acquisition %d\n", ret);
return ret;
}
static IIO_CONST_ATTR_INT_TIME_AVAIL(ISL29501_INT_TIME_AVAILABLE);
static IIO_CONST_ATTR(out_current_scale_available,
ISL29501_CURRENT_SCALE_AVAILABLE);
static struct attribute *isl29501_attributes[] = {
&iio_const_attr_integration_time_available.dev_attr.attr,
&iio_const_attr_out_current_scale_available.dev_attr.attr,
NULL
};
static const struct attribute_group isl29501_attribute_group = {
.attrs = isl29501_attributes,
};
static const int isl29501_current_scale_table[][2] = {
{0, 3900}, {0, 7800}, {0, 11800}, {0, 15700},
{0, 19600}, {0, 23500}, {0, 27500}, {0, 31400},
{0, 35200}, {0, 39200}, {0, 43100}, {0, 47100},
{0, 51000}, {0, 54900}, {0, 58800},
};
static const int isl29501_int_time[][2] = {
{0, 70}, /* 0.07 ms */
{0, 140}, /* 0.14 ms */
{0, 280}, /* 0.28 ms */
{0, 570}, /* 0.57 ms */
{0, 1140}, /* 1.14 ms */
{0, 2280}, /* 2.28 ms */
{0, 4550}, /* 4.55 ms */
{0, 9100}, /* 9.11 ms */
{0, 18200}, /* 18.2 ms */
{0, 36400}, /* 36.4 ms */
{0, 72810}, /* 72.81 ms */
{0, 145610} /* 145.28 ms */
};
static int isl29501_get_raw(struct isl29501_private *isl29501,
const struct iio_chan_spec *chan,
int *raw)
{
int ret;
switch (chan->type) {
case IIO_PROXIMITY:
ret = isl29501_register_read(isl29501, REG_DISTANCE, raw);
if (ret < 0)
return ret;
return IIO_VAL_INT;
case IIO_INTENSITY:
ret = isl29501_register_read(isl29501,
REG_AMBIENT_LIGHT,
raw);
if (ret < 0)
return ret;
return IIO_VAL_INT;
case IIO_PHASE:
ret = isl29501_register_read(isl29501, REG_PHASE, raw);
if (ret < 0)
return ret;
return IIO_VAL_INT;
case IIO_CURRENT:
ret = isl29501_register_read(isl29501, REG_EMITTER_DAC, raw);
if (ret < 0)
return ret;
return IIO_VAL_INT;
case IIO_TEMP:
ret = isl29501_register_read(isl29501, REG_TEMPERATURE, raw);
if (ret < 0)
return ret;
return IIO_VAL_INT;
default:
return -EINVAL;
}
}
static int isl29501_get_scale(struct isl29501_private *isl29501,
const struct iio_chan_spec *chan,
int *val, int *val2)
{
int ret;
u32 current_scale;
switch (chan->type) {
case IIO_PROXIMITY:
/* distance = raw_distance * 33.31 / 65536 (m) */
*val = 3331;
*val2 = 6553600;
return IIO_VAL_FRACTIONAL;
case IIO_PHASE:
/* phase = raw_phase * 2pi / 65536 (rad) */
*val = 0;
*val2 = 95874;
return IIO_VAL_INT_PLUS_NANO;
case IIO_INTENSITY:
/* light = raw_light * 35 / 10000 (mA) */
*val = 35;
*val2 = 10000;
return IIO_VAL_FRACTIONAL;
case IIO_CURRENT:
ret = isl29501_register_read(isl29501,
REG_DRIVER_RANGE,
&current_scale);
if (ret < 0)
return ret;
if (current_scale > ARRAY_SIZE(isl29501_current_scale_table))
return -EINVAL;
if (!current_scale) {
*val = 0;
*val2 = 0;
return IIO_VAL_INT;
}
*val = isl29501_current_scale_table[current_scale - 1][0];
*val2 = isl29501_current_scale_table[current_scale - 1][1];
return IIO_VAL_INT_PLUS_MICRO;
case IIO_TEMP:
/* temperature = raw_temperature * 125 / 100000 (milli °C) */
*val = 125;
*val2 = 100000;
return IIO_VAL_FRACTIONAL;
default:
return -EINVAL;
}
}
static int isl29501_get_calibbias(struct isl29501_private *isl29501,
const struct iio_chan_spec *chan,
int *bias)
{
switch (chan->type) {
case IIO_PROXIMITY:
return isl29501_register_read(isl29501,
REG_DISTANCE_BIAS,
bias);
case IIO_TEMP:
return isl29501_register_read(isl29501,
REG_TEMPERATURE_BIAS,
bias);
default:
return -EINVAL;
}
}
static int isl29501_get_inttime(struct isl29501_private *isl29501,
int *val, int *val2)
{
int ret;
u32 inttime;
ret = isl29501_register_read(isl29501, REG_INT_TIME, &inttime);
if (ret < 0)
return ret;
if (inttime >= ARRAY_SIZE(isl29501_int_time))
return -EINVAL;
*val = isl29501_int_time[inttime][0];
*val2 = isl29501_int_time[inttime][1];
return IIO_VAL_INT_PLUS_MICRO;
}
static int isl29501_get_freq(struct isl29501_private *isl29501,
int *val, int *val2)
{
int ret;
int sample_time;
unsigned long long freq;
u32 temp;
ret = isl29501_register_read(isl29501, REG_SAMPLE_TIME, &sample_time);
if (ret < 0)
return ret;
/* freq = 1 / (0.000450 * (sample_time + 1) * 10^-6) */
freq = 1000000ULL * 1000000ULL;
do_div(freq, 450 * (sample_time + 1));
temp = do_div(freq, 1000000);
*val = freq;
*val2 = temp;
return IIO_VAL_INT_PLUS_MICRO;
}
static int isl29501_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val,
int *val2, long mask)
{
struct isl29501_private *isl29501 = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_RAW:
return isl29501_get_raw(isl29501, chan, val);
case IIO_CHAN_INFO_SCALE:
return isl29501_get_scale(isl29501, chan, val, val2);
case IIO_CHAN_INFO_INT_TIME:
return isl29501_get_inttime(isl29501, val, val2);
case IIO_CHAN_INFO_SAMP_FREQ:
return isl29501_get_freq(isl29501, val, val2);
case IIO_CHAN_INFO_CALIBBIAS:
return isl29501_get_calibbias(isl29501, chan, val);
default:
return -EINVAL;
}
}
static int isl29501_set_raw(struct isl29501_private *isl29501,
const struct iio_chan_spec *chan,
int raw)
{
switch (chan->type) {
case IIO_CURRENT:
return isl29501_register_write(isl29501, REG_EMITTER_DAC, raw);
default:
return -EINVAL;
}
}
static int isl29501_set_inttime(struct isl29501_private *isl29501,
int val, int val2)
{
int i;
for (i = 0; i < ARRAY_SIZE(isl29501_int_time); i++) {
if (isl29501_int_time[i][0] == val &&
isl29501_int_time[i][1] == val2) {
return isl29501_register_write(isl29501,
REG_INT_TIME,
i);
}
}
return -EINVAL;
}
static int isl29501_set_scale(struct isl29501_private *isl29501,
const struct iio_chan_spec *chan,
int val, int val2)
{
int i;
if (chan->type != IIO_CURRENT)
return -EINVAL;
for (i = 0; i < ARRAY_SIZE(isl29501_current_scale_table); i++) {
if (isl29501_current_scale_table[i][0] == val &&
isl29501_current_scale_table[i][1] == val2) {
return isl29501_register_write(isl29501,
REG_DRIVER_RANGE,
i + 1);
}
}
return -EINVAL;
}
static int isl29501_set_calibbias(struct isl29501_private *isl29501,
const struct iio_chan_spec *chan,
int bias)
{
switch (chan->type) {
case IIO_PROXIMITY:
return isl29501_register_write(isl29501,
REG_DISTANCE_BIAS,
bias);
case IIO_TEMP:
return isl29501_register_write(isl29501,
REG_TEMPERATURE_BIAS,
bias);
default:
return -EINVAL;
}
}
static int isl29501_set_freq(struct isl29501_private *isl29501,
int val, int val2)
{
int freq;
unsigned long long sample_time;
/* sample_freq = 1 / (0.000450 * (sample_time + 1) * 10^-6) */
freq = val * 1000000 + val2 % 1000000;
sample_time = 2222ULL * 1000000ULL;
do_div(sample_time, freq);
sample_time -= 1;
if (sample_time > 255)
return -ERANGE;
return isl29501_register_write(isl29501, REG_SAMPLE_TIME, sample_time);
}
static int isl29501_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
struct isl29501_private *isl29501 = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_RAW:
return isl29501_set_raw(isl29501, chan, val);
case IIO_CHAN_INFO_INT_TIME:
return isl29501_set_inttime(isl29501, val, val2);
case IIO_CHAN_INFO_SAMP_FREQ:
return isl29501_set_freq(isl29501, val, val2);
case IIO_CHAN_INFO_SCALE:
return isl29501_set_scale(isl29501, chan, val, val2);
case IIO_CHAN_INFO_CALIBBIAS:
return isl29501_set_calibbias(isl29501, chan, val);
default:
return -EINVAL;
}
}
static const struct iio_info isl29501_info = {
.read_raw = &isl29501_read_raw,
.write_raw = &isl29501_write_raw,
.attrs = &isl29501_attribute_group,
};
static int isl29501_init_chip(struct isl29501_private *isl29501)
{
int ret;
ret = i2c_smbus_read_byte_data(isl29501->client, ISL29501_DEVICE_ID);
if (ret < 0) {
dev_err(&isl29501->client->dev, "Error reading device id\n");
return ret;
}
if (ret != ISL29501_ID) {
dev_err(&isl29501->client->dev,
"Wrong chip id, got %x expected %x\n",
ret, ISL29501_DEVICE_ID);
return -ENODEV;
}
ret = isl29501_reset_registers(isl29501);
if (ret < 0)
return ret;
return isl29501_begin_acquisition(isl29501);
}
static irqreturn_t isl29501_trigger_handler(int irq, void *p)
{
struct iio_poll_func *pf = p;
struct iio_dev *indio_dev = pf->indio_dev;
struct isl29501_private *isl29501 = iio_priv(indio_dev);
const unsigned long *active_mask = indio_dev->active_scan_mask;
u32 buffer[4] = {}; /* 1x16-bit + ts */
if (test_bit(ISL29501_DISTANCE_SCAN_INDEX, active_mask))
isl29501_register_read(isl29501, REG_DISTANCE, buffer);
iio_push_to_buffers_with_timestamp(indio_dev, buffer, pf->timestamp);
iio_trigger_notify_done(indio_dev->trig);
return IRQ_HANDLED;
}
static int isl29501_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct iio_dev *indio_dev;
struct isl29501_private *isl29501;
int ret;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*isl29501));
if (!indio_dev)
return -ENOMEM;
isl29501 = iio_priv(indio_dev);
i2c_set_clientdata(client, indio_dev);
isl29501->client = client;
mutex_init(&isl29501->lock);
ret = isl29501_init_chip(isl29501);
if (ret < 0)
return ret;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->dev.parent = &client->dev;
indio_dev->channels = isl29501_channels;
indio_dev->num_channels = ARRAY_SIZE(isl29501_channels);
indio_dev->name = client->name;
indio_dev->info = &isl29501_info;
ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev,
iio_pollfunc_store_time,
isl29501_trigger_handler,
NULL);
if (ret < 0) {
dev_err(&client->dev, "unable to setup iio triggered buffer\n");
return ret;
}
return devm_iio_device_register(&client->dev, indio_dev);
}
static const struct i2c_device_id isl29501_id[] = {
{"isl29501", 0},
{}
};
MODULE_DEVICE_TABLE(i2c, isl29501_id);
#if defined(CONFIG_OF)
static const struct of_device_id isl29501_i2c_matches[] = {
{ .compatible = "renesas,isl29501" },
{ }
};
MODULE_DEVICE_TABLE(of, isl29501_i2c_matches);
#endif
static struct i2c_driver isl29501_driver = {
.driver = {
.name = "isl29501",
},
.id_table = isl29501_id,
.probe = isl29501_probe,
};
module_i2c_driver(isl29501_driver);
MODULE_AUTHOR("Mathieu Othacehe <m.othacehe@gmail.com>");
MODULE_DESCRIPTION("ISL29501 Time of Flight sensor driver");
MODULE_LICENSE("GPL v2");
......@@ -116,45 +116,26 @@ static struct ad5933_platform_data ad5933_default_pdata = {
.vref_mv = 3300,
};
#define AD5933_CHANNEL(_type, _extend_name, _info_mask_separate, _address, \
_scan_index, _realbits) { \
.type = (_type), \
.extend_name = (_extend_name), \
.info_mask_separate = (_info_mask_separate), \
.address = (_address), \
.scan_index = (_scan_index), \
.scan_type = { \
.sign = 's', \
.realbits = (_realbits), \
.storagebits = 16, \
}, \
}
static const struct iio_chan_spec ad5933_channels[] = {
{
.type = IIO_TEMP,
.indexed = 1,
.channel = 0,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE),
.address = AD5933_REG_TEMP_DATA,
.scan_index = -1,
.scan_type = {
.sign = 's',
.realbits = 14,
.storagebits = 16,
},
}, { /* Ring Channels */
.type = IIO_VOLTAGE,
.indexed = 1,
.channel = 0,
.extend_name = "real",
.address = AD5933_REG_REAL_DATA,
.scan_index = 0,
.scan_type = {
.sign = 's',
.realbits = 16,
.storagebits = 16,
},
}, {
.type = IIO_VOLTAGE,
.indexed = 1,
.channel = 0,
.extend_name = "imag",
.address = AD5933_REG_IMAG_DATA,
.scan_index = 1,
.scan_type = {
.sign = 's',
.realbits = 16,
.storagebits = 16,
},
},
AD5933_CHANNEL(IIO_TEMP, NULL, BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE), AD5933_REG_TEMP_DATA, -1, 14),
/* Ring Channels */
AD5933_CHANNEL(IIO_VOLTAGE, "real", 0, AD5933_REG_REAL_DATA, 0, 16),
AD5933_CHANNEL(IIO_VOLTAGE, "imag", 0, AD5933_REG_IMAG_DATA, 1, 16),
};
static int ad5933_i2c_write(struct i2c_client *client, u8 reg, u8 len, u8 *data)
......
......@@ -45,6 +45,7 @@ enum iio_chan_type {
IIO_INDEX,
IIO_GRAVITY,
IIO_POSITIONRELATIVE,
IIO_PHASE,
};
enum iio_modifier {
......@@ -85,6 +86,7 @@ enum iio_modifier {
IIO_MOD_CO2,
IIO_MOD_VOC,
IIO_MOD_LIGHT_UV,
IIO_MOD_LIGHT_DUV,
};
enum iio_event_type {
......
......@@ -59,6 +59,7 @@ static const char * const iio_chan_type_name_spec[] = {
[IIO_UVINDEX] = "uvindex",
[IIO_GRAVITY] = "gravity",
[IIO_POSITIONRELATIVE] = "positionrelative",
[IIO_PHASE] = "phase",
};
static const char * const iio_ev_type_text[] = {
......@@ -97,6 +98,7 @@ static const char * const iio_modifier_names[] = {
[IIO_MOD_LIGHT_GREEN] = "green",
[IIO_MOD_LIGHT_BLUE] = "blue",
[IIO_MOD_LIGHT_UV] = "uv",
[IIO_MOD_LIGHT_DUV] = "duv",
[IIO_MOD_QUATERNION] = "quaternion",
[IIO_MOD_TEMP_AMBIENT] = "ambient",
[IIO_MOD_TEMP_OBJECT] = "object",
......@@ -153,6 +155,7 @@ static bool event_is_known(struct iio_event_data *event)
case IIO_UVINDEX:
case IIO_GRAVITY:
case IIO_POSITIONRELATIVE:
case IIO_PHASE:
break;
default:
return false;
......@@ -180,6 +183,7 @@ static bool event_is_known(struct iio_event_data *event)
case IIO_MOD_LIGHT_GREEN:
case IIO_MOD_LIGHT_BLUE:
case IIO_MOD_LIGHT_UV:
case IIO_MOD_LIGHT_DUV:
case IIO_MOD_QUATERNION:
case IIO_MOD_TEMP_AMBIENT:
case IIO_MOD_TEMP_OBJECT:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment