Second round of IIO new device support, cleanups and features for the 4.11 cycle
New device support: * lsm6dsx imu - new driver and bindings. * max11100 adc - new driver and bindings. * tlc4541 - new driver * tmp007 thermopile - new driver. Core * in kernel interfaces - pass through raw values if no scaling provided and a processed value is requested. * trigger - close a race condition in acquiring trigger reference. - constify device_type structures. - rework the viio_trigger_alloc function to be much neater and easier to read. - free trigger resources correctly on some error paths. Avoids putting a module we don't have. Documentation * ABI - specify a unit for proximity measurements. Cleanups and features * ads1015 - constify iio_info structure. * ads7950 cleanups following merge in previous pull - Add device tree bindings - Drop the ti prefix from the module name in common with other drivers. - Change regulator name to vref to match datasheet and other drivers. * ak8974 - remove a redundant zero timeout check. * bmi160 - use variable names for sizeof instead of types. * cm3605 - mark PM functions as __maybe_unused to avoid a build warning. * isl29028 (on it's way towards moving out of staging). - alignment fixes and newline improvements. - combine proxim_get and read_proxim for simpler code. - drop unused ISL29028_DEV_ATTR macro - move some error logging into functions to cut out repitition. - make error messages more consistent. - tidy up some brackets. - drop the enable flag that nothing uses. - only set proximity rate and ALS scale when relevant channel type is enabled. - runtime pm support. * lsm6dsx - fix wrong values for gyro sensitivitiy. * mag3110 - claim direct mode during sysfs reads to avoid a race condition. * max1363 - export OF device table IDs as module aliases. * max30100 - use msleep for long uncritical delays. * mcp4531 - export OF device table as module aliases. * ms5611 - claim direct mode during sysfs reads to avoid a race condition. * opt3001 - export OF device table as module aliases. * sx9500 - claim direct mode during oversampling changes to avoid a race condition. -----BEGIN PGP SIGNATURE----- iQJFBAABCAAvFiEEbilms4eEBlKRJoGxVIU0mcT0FogFAliFK5sRHGppYzIzQGtl cm5lbC5vcmcACgkQVIU0mcT0FoixSQ/+PrWf4PWdYSy+YlxxVzWMkJ3QhHYvDxpz mmH5GmsUB0RGi205VqAsvEF55Gcp5tzMYPXkjoxD1nXtuZ8sbVzkTSvrEGIqgA8b QUDdu76ntzJYWHVqouh8SywCJJbR/ssjZbIMzHvpYL/pty7+ICnaevauQf6n93Hh 51yaPdtDNu4hq/lQdUz0QySGn9UJG7HUUKIfSDgZCh9q8VcQ8bmCf8MaRilJTzo9 q8ONaziyB77w07JVeQLR/W8WO+KCRor0qqlokNtGDNAE2EOre6ul64Ded6TXLEez 9ag/IYkECN3tImuHHJ9AKlCPOl39viZeP6sjvJc4glujZ0WKVuT76tlVP5XknuCE myy74d9Jt5/N43SYiQpRRm4Eadje56kCZtZhFidhRaEV74eQaOYG6fMNJ9Q/HJ4B d9Ykw7ZjU08DDOdQNtfQ5DGH72bKu94DSImqjmxXskUL6quZbxCnKPvI/vm//9xb 7jSHtORTT8WxX/Ut9MTE59S0FS5x/8ivNxWxzqLDS0phcA4e1cLXJyFh0npxxYeu XdvJb+BefcfsPNbhhPl/anz577TJPTgNL4P2j1ano0duVCNV33p3y9Z2YNCkgaI1 PtRVCBzMhaV60EQGBzBheNmN1+jeGu7q8o3UDhaoc4sX4ILut0oDE4PIDROmuItc QPLTmZkG20o= =Wvje -----END PGP SIGNATURE----- Merge tag 'iio-for-4.11b' of git://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio into work-next Jonathan writes: Second round of IIO new device support, cleanups and features for the 4.11 cycle New device support: * lsm6dsx imu - new driver and bindings. * max11100 adc - new driver and bindings. * tlc4541 - new driver * tmp007 thermopile - new driver. Core * in kernel interfaces - pass through raw values if no scaling provided and a processed value is requested. * trigger - close a race condition in acquiring trigger reference. - constify device_type structures. - rework the viio_trigger_alloc function to be much neater and easier to read. - free trigger resources correctly on some error paths. Avoids putting a module we don't have. Documentation * ABI - specify a unit for proximity measurements. Cleanups and features * ads1015 - constify iio_info structure. * ads7950 cleanups following merge in previous pull - Add device tree bindings - Drop the ti prefix from the module name in common with other drivers. - Change regulator name to vref to match datasheet and other drivers. * ak8974 - remove a redundant zero timeout check. * bmi160 - use variable names for sizeof instead of types. * cm3605 - mark PM functions as __maybe_unused to avoid a build warning. * isl29028 (on it's way towards moving out of staging). - alignment fixes and newline improvements. - combine proxim_get and read_proxim for simpler code. - drop unused ISL29028_DEV_ATTR macro - move some error logging into functions to cut out repitition. - make error messages more consistent. - tidy up some brackets. - drop the enable flag that nothing uses. - only set proximity rate and ALS scale when relevant channel type is enabled. - runtime pm support. * lsm6dsx - fix wrong values for gyro sensitivitiy. * mag3110 - claim direct mode during sysfs reads to avoid a race condition. * max1363 - export OF device table IDs as module aliases. * max30100 - use msleep for long uncritical delays. * mcp4531 - export OF device table as module aliases. * ms5611 - claim direct mode during sysfs reads to avoid a race condition. * opt3001 - export OF device table as module aliases. * sx9500 - claim direct mode during oversampling changes to avoid a race condition.
This commit is contained in:
commit
08cad739ba
|
@ -1255,7 +1255,8 @@ Description:
|
|||
reflectivity of infrared or ultrasound emitted.
|
||||
Often these sensors are unit less and as such conversion
|
||||
to SI units is not possible. Higher proximity measurements
|
||||
indicate closer objects, and vice versa.
|
||||
indicate closer objects, and vice versa. Units after
|
||||
application of scale and offset are meters.
|
||||
|
||||
What: /sys/.../iio:deviceX/in_illuminance_input
|
||||
What: /sys/.../iio:deviceX/in_illuminance_raw
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
* Maxim max11100 Analog to Digital Converter (ADC)
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "maxim,max11100"
|
||||
- reg: the adc unit address
|
||||
- vref-supply: phandle to the regulator that provides reference voltage
|
||||
|
||||
Optional properties:
|
||||
- spi-max-frequency: SPI maximum frequency
|
||||
|
||||
Example:
|
||||
|
||||
max11100: adc@0 {
|
||||
compatible = "maxim,max11100";
|
||||
reg = <0>;
|
||||
vref-supply = <&adc0_vref>;
|
||||
spi-max-frequency = <240000>;
|
||||
};
|
|
@ -0,0 +1,23 @@
|
|||
* Texas Instruments ADS7950 family of A/DC chips
|
||||
|
||||
Required properties:
|
||||
- compatible: Must be one of "ti,ads7950", "ti,ads7951", "ti,ads7952",
|
||||
"ti,ads7953", "ti,ads7954", "ti,ads7955", "ti,ads7956", "ti,ads7957",
|
||||
"ti,ads7958", "ti,ads7959", "ti,ads7960", or "ti,ads7961"
|
||||
- reg: SPI chip select number for the device
|
||||
- #io-channel-cells: Must be 1 as per ../iio-bindings.txt
|
||||
- vref-supply: phandle to a regulator node that supplies the 2.5V or 5V
|
||||
reference voltage
|
||||
|
||||
Recommended properties:
|
||||
- spi-max-frequency: Definition as per
|
||||
Documentation/devicetree/bindings/spi/spi-bus.txt
|
||||
|
||||
Example:
|
||||
adc@0 {
|
||||
compatible = "ti,ads7957";
|
||||
reg = <0>;
|
||||
#io-channel-cells = <1>;
|
||||
vref-supply = <&refin_supply>;
|
||||
spi-max-frequency = <10000000>;
|
||||
};
|
|
@ -0,0 +1,24 @@
|
|||
* ST_LSM6DSx driver for STM 6-axis (acc + gyro) imu Mems sensors
|
||||
|
||||
Required properties:
|
||||
- compatible: must be one of:
|
||||
"st,lsm6ds3"
|
||||
"st,lsm6dsm"
|
||||
- reg: i2c address of the sensor / spi cs line
|
||||
|
||||
Optional properties:
|
||||
- interrupt-parent: should be the phandle for the interrupt controller
|
||||
- interrupts: interrupt mapping for IRQ. It should be configured with
|
||||
flags IRQ_TYPE_LEVEL_HIGH or IRQ_TYPE_EDGE_RISING.
|
||||
|
||||
Refer to interrupt-controller/interrupts.txt for generic interrupt
|
||||
client node bindings.
|
||||
|
||||
Example:
|
||||
|
||||
lsm6dsm@6b {
|
||||
compatible = "st,lsm6dsm";
|
||||
reg = <0x6b>;
|
||||
interrupt-parent = <&gpio0>;
|
||||
interrupts = <0 IRQ_TYPE_EDGE_RISING>;
|
||||
};
|
|
@ -0,0 +1,27 @@
|
|||
* TI TMP007 - IR thermopile sensor with integrated math engine
|
||||
|
||||
Link to datasheet: http://www.ti.com/lit/ds/symlink/tmp007.pdf
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: should be "ti,tmp007"
|
||||
- reg: the I2C address of the sensor (changeable via ADR pins)
|
||||
------------------------------
|
||||
|ADR1 | ADR0 | Device Address|
|
||||
------------------------------
|
||||
0 0 0x40
|
||||
0 1 0x41
|
||||
0 SDA 0x42
|
||||
0 SCL 0x43
|
||||
1 0 0x44
|
||||
1 1 0x45
|
||||
1 SDA 0x46
|
||||
1 SCL 0x47
|
||||
|
||||
Example:
|
||||
|
||||
tmp007@40 {
|
||||
compatible = "ti,tmp007";
|
||||
reg = <0x40>;
|
||||
};
|
||||
|
|
@ -326,6 +326,15 @@ config MAX1027
|
|||
To compile this driver as a module, choose M here: the module will be
|
||||
called max1027.
|
||||
|
||||
config MAX11100
|
||||
tristate "Maxim max11100 ADC driver"
|
||||
depends on SPI_MASTER
|
||||
help
|
||||
Say yes here to build support for Maxim max11100 SPI ADC
|
||||
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called max11100.
|
||||
|
||||
config MAX1363
|
||||
tristate "Maxim max1363 ADC driver"
|
||||
depends on I2C
|
||||
|
@ -603,6 +612,18 @@ config TI_AM335X_ADC
|
|||
To compile this driver as a module, choose M here: the module will be
|
||||
called ti_am335x_adc.
|
||||
|
||||
config TI_TLC4541
|
||||
tristate "Texas Instruments TLC4541 ADC driver"
|
||||
depends on SPI
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
Say yes here to build support for Texas Instruments TLC4541 / TLC3541
|
||||
ADC chips.
|
||||
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called ti-tlc4541.
|
||||
|
||||
config TWL4030_MADC
|
||||
tristate "TWL4030 MADC (Monitoring A/D Converter)"
|
||||
depends on TWL4030_CORE
|
||||
|
|
|
@ -32,6 +32,7 @@ obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
|
|||
obj-$(CONFIG_LPC18XX_ADC) += lpc18xx_adc.o
|
||||
obj-$(CONFIG_LTC2485) += ltc2485.o
|
||||
obj-$(CONFIG_MAX1027) += max1027.o
|
||||
obj-$(CONFIG_MAX11100) += max11100.o
|
||||
obj-$(CONFIG_MAX1363) += max1363.o
|
||||
obj-$(CONFIG_MCP320X) += mcp320x.o
|
||||
obj-$(CONFIG_MCP3422) += mcp3422.o
|
||||
|
@ -55,6 +56,7 @@ obj-$(CONFIG_TI_ADS1015) += ti-ads1015.o
|
|||
obj-$(CONFIG_TI_ADS7950) += ti-ads7950.o
|
||||
obj-$(CONFIG_TI_ADS8688) += ti-ads8688.o
|
||||
obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o
|
||||
obj-$(CONFIG_TI_TLC4541) += ti-tlc4541.o
|
||||
obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o
|
||||
obj-$(CONFIG_TWL6030_GPADC) += twl6030-gpadc.o
|
||||
obj-$(CONFIG_VF610_ADC) += vf610_adc.o
|
||||
|
|
|
@ -0,0 +1,181 @@
|
|||
/*
|
||||
* iio/adc/max11100.c
|
||||
* Maxim max11100 ADC Driver with IIO interface
|
||||
*
|
||||
* Copyright (C) 2016-17 Renesas Electronics Corporation
|
||||
* Copyright (C) 2016-17 Jacopo Mondi
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include <linux/delay.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/driver.h>
|
||||
|
||||
/*
|
||||
* LSB is the ADC single digital step
|
||||
* 1 LSB = (vref_mv / 2 ^ 16)
|
||||
*
|
||||
* LSB is used to calculate analog voltage value
|
||||
* from the number of ADC steps count
|
||||
*
|
||||
* Ain = (count * LSB)
|
||||
*/
|
||||
#define MAX11100_LSB_DIV (1 << 16)
|
||||
|
||||
struct max11100_state {
|
||||
struct regulator *vref_reg;
|
||||
struct spi_device *spi;
|
||||
|
||||
/*
|
||||
* DMA (thus cache coherency maintenance) requires the
|
||||
* transfer buffers to live in their own cache lines.
|
||||
*/
|
||||
u8 buffer[3] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
static struct iio_chan_spec max11100_channels[] = {
|
||||
{ /* [0] */
|
||||
.type = IIO_VOLTAGE,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
},
|
||||
};
|
||||
|
||||
static int max11100_read_single(struct iio_dev *indio_dev, int *val)
|
||||
{
|
||||
int ret;
|
||||
struct max11100_state *state = iio_priv(indio_dev);
|
||||
|
||||
ret = spi_read(state->spi, state->buffer, sizeof(state->buffer));
|
||||
if (ret) {
|
||||
dev_err(&indio_dev->dev, "SPI transfer failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* the first 8 bits sent out from ADC must be 0s */
|
||||
if (state->buffer[0]) {
|
||||
dev_err(&indio_dev->dev, "Invalid value: buffer[0] != 0\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*val = (state->buffer[1] << 8) | state->buffer[2];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max11100_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long info)
|
||||
{
|
||||
int ret, vref_uv;
|
||||
struct max11100_state *state = iio_priv(indio_dev);
|
||||
|
||||
switch (info) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = max11100_read_single(indio_dev, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
vref_uv = regulator_get_voltage(state->vref_reg);
|
||||
if (vref_uv < 0)
|
||||
/* dummy regulator "get_voltage" returns -EINVAL */
|
||||
return -EINVAL;
|
||||
|
||||
*val = vref_uv / 1000;
|
||||
*val2 = MAX11100_LSB_DIV;
|
||||
return IIO_VAL_FRACTIONAL;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct iio_info max11100_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = max11100_read_raw,
|
||||
};
|
||||
|
||||
static int max11100_probe(struct spi_device *spi)
|
||||
{
|
||||
int ret;
|
||||
struct iio_dev *indio_dev;
|
||||
struct max11100_state *state;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*state));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
|
||||
state = iio_priv(indio_dev);
|
||||
state->spi = spi;
|
||||
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->dev.of_node = spi->dev.of_node;
|
||||
indio_dev->name = "max11100";
|
||||
indio_dev->info = &max11100_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = max11100_channels,
|
||||
indio_dev->num_channels = ARRAY_SIZE(max11100_channels),
|
||||
|
||||
state->vref_reg = devm_regulator_get(&spi->dev, "vref");
|
||||
if (IS_ERR(state->vref_reg))
|
||||
return PTR_ERR(state->vref_reg);
|
||||
|
||||
ret = regulator_enable(state->vref_reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto disable_regulator;
|
||||
|
||||
return 0;
|
||||
|
||||
disable_regulator:
|
||||
regulator_disable(state->vref_reg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int max11100_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct max11100_state *state = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
regulator_disable(state->vref_reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id max11100_ids[] = {
|
||||
{.compatible = "maxim,max11100"},
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, max11100_ids);
|
||||
|
||||
static struct spi_driver max11100_driver = {
|
||||
.driver = {
|
||||
.name = "max11100",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_match_ptr(max11100_ids),
|
||||
},
|
||||
.probe = max11100_probe,
|
||||
.remove = max11100_remove,
|
||||
};
|
||||
|
||||
module_spi_driver(max11100_driver);
|
||||
|
||||
MODULE_AUTHOR("Jacopo Mondi <jacopo@jmondi.org>");
|
||||
MODULE_DESCRIPTION("Maxim max11100 ADC Driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -1567,6 +1567,7 @@ static const struct of_device_id max1363_of_match[] = {
|
|||
MAX1363_COMPATIBLE("maxim,max11647", max11647),
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, max1363_of_match);
|
||||
#endif
|
||||
|
||||
static int max1363_probe(struct i2c_client *client,
|
||||
|
|
|
@ -472,14 +472,14 @@ static const struct attribute_group ads1115_attribute_group = {
|
|||
.attrs = ads1115_attributes,
|
||||
};
|
||||
|
||||
static struct iio_info ads1015_info = {
|
||||
static const struct iio_info ads1015_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = ads1015_read_raw,
|
||||
.write_raw = ads1015_write_raw,
|
||||
.attrs = &ads1015_attribute_group,
|
||||
};
|
||||
|
||||
static struct iio_info ads1115_info = {
|
||||
static const struct iio_info ads1115_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = ads1015_read_raw,
|
||||
.write_raw = ads1015_write_raw,
|
||||
|
|
|
@ -411,15 +411,15 @@ static int ti_ads7950_probe(struct spi_device *spi)
|
|||
spi_message_init_with_transfers(&st->scan_single_msg,
|
||||
st->scan_single_xfer, 3);
|
||||
|
||||
st->reg = devm_regulator_get(&spi->dev, "refin");
|
||||
st->reg = devm_regulator_get(&spi->dev, "vref");
|
||||
if (IS_ERR(st->reg)) {
|
||||
dev_err(&spi->dev, "Failed get get regulator \"refin\"\n");
|
||||
dev_err(&spi->dev, "Failed get get regulator \"vref\"\n");
|
||||
return PTR_ERR(st->reg);
|
||||
}
|
||||
|
||||
ret = regulator_enable(st->reg);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev, "Failed to enable regulator \"refin\"\n");
|
||||
dev_err(&spi->dev, "Failed to enable regulator \"vref\"\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -459,25 +459,25 @@ static int ti_ads7950_remove(struct spi_device *spi)
|
|||
}
|
||||
|
||||
static const struct spi_device_id ti_ads7950_id[] = {
|
||||
{"ti-ads7950", TI_ADS7950},
|
||||
{"ti-ads7951", TI_ADS7951},
|
||||
{"ti-ads7952", TI_ADS7952},
|
||||
{"ti-ads7953", TI_ADS7953},
|
||||
{"ti-ads7954", TI_ADS7954},
|
||||
{"ti-ads7955", TI_ADS7955},
|
||||
{"ti-ads7956", TI_ADS7956},
|
||||
{"ti-ads7957", TI_ADS7957},
|
||||
{"ti-ads7958", TI_ADS7958},
|
||||
{"ti-ads7959", TI_ADS7959},
|
||||
{"ti-ads7960", TI_ADS7960},
|
||||
{"ti-ads7961", TI_ADS7961},
|
||||
{ "ads7950", TI_ADS7950 },
|
||||
{ "ads7951", TI_ADS7951 },
|
||||
{ "ads7952", TI_ADS7952 },
|
||||
{ "ads7953", TI_ADS7953 },
|
||||
{ "ads7954", TI_ADS7954 },
|
||||
{ "ads7955", TI_ADS7955 },
|
||||
{ "ads7956", TI_ADS7956 },
|
||||
{ "ads7957", TI_ADS7957 },
|
||||
{ "ads7958", TI_ADS7958 },
|
||||
{ "ads7959", TI_ADS7959 },
|
||||
{ "ads7960", TI_ADS7960 },
|
||||
{ "ads7961", TI_ADS7961 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ti_ads7950_id);
|
||||
|
||||
static struct spi_driver ti_ads7950_driver = {
|
||||
.driver = {
|
||||
.name = "ti-ads7950",
|
||||
.name = "ads7950",
|
||||
},
|
||||
.probe = ti_ads7950_probe,
|
||||
.remove = ti_ads7950_remove,
|
||||
|
|
|
@ -0,0 +1,271 @@
|
|||
/*
|
||||
* TI tlc4541 ADC Driver
|
||||
*
|
||||
* Copyright (C) 2017 Phil Reid
|
||||
*
|
||||
* Datasheets can be found here:
|
||||
* http://www.ti.com/lit/gpn/tlc3541
|
||||
* http://www.ti.com/lit/gpn/tlc4541
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* The tlc4541 requires 24 clock cycles to start a transfer.
|
||||
* Conversion then takes 2.94us to complete before data is ready
|
||||
* Data is returned MSB first.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/sysfs.h>
|
||||
|
||||
struct tlc4541_state {
|
||||
struct spi_device *spi;
|
||||
struct regulator *reg;
|
||||
struct spi_transfer scan_single_xfer[3];
|
||||
struct spi_message scan_single_msg;
|
||||
|
||||
/*
|
||||
* DMA (thus cache coherency maintenance) requires the
|
||||
* transfer buffers to live in their own cache lines.
|
||||
* 2 bytes data + 6 bytes padding + 8 bytes timestamp when
|
||||
* call iio_push_to_buffers_with_timestamp.
|
||||
*/
|
||||
__be16 rx_buf[8] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
struct tlc4541_chip_info {
|
||||
const struct iio_chan_spec *channels;
|
||||
unsigned int num_channels;
|
||||
};
|
||||
|
||||
enum tlc4541_id {
|
||||
TLC3541,
|
||||
TLC4541,
|
||||
};
|
||||
|
||||
#define TLC4541_V_CHAN(bits, bitshift) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = (bits), \
|
||||
.storagebits = 16, \
|
||||
.shift = (bitshift), \
|
||||
.endianness = IIO_BE, \
|
||||
}, \
|
||||
}
|
||||
|
||||
#define DECLARE_TLC4541_CHANNELS(name, bits, bitshift) \
|
||||
const struct iio_chan_spec name ## _channels[] = { \
|
||||
TLC4541_V_CHAN(bits, bitshift), \
|
||||
IIO_CHAN_SOFT_TIMESTAMP(1), \
|
||||
}
|
||||
|
||||
static DECLARE_TLC4541_CHANNELS(tlc3541, 14, 2);
|
||||
static DECLARE_TLC4541_CHANNELS(tlc4541, 16, 0);
|
||||
|
||||
static const struct tlc4541_chip_info tlc4541_chip_info[] = {
|
||||
[TLC3541] = {
|
||||
.channels = tlc3541_channels,
|
||||
.num_channels = ARRAY_SIZE(tlc3541_channels),
|
||||
},
|
||||
[TLC4541] = {
|
||||
.channels = tlc4541_channels,
|
||||
.num_channels = ARRAY_SIZE(tlc4541_channels),
|
||||
},
|
||||
};
|
||||
|
||||
static irqreturn_t tlc4541_trigger_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct tlc4541_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = spi_sync(st->spi, &st->scan_single_msg);
|
||||
if (ret < 0)
|
||||
goto done;
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, st->rx_buf,
|
||||
iio_get_time_ns(indio_dev));
|
||||
|
||||
done:
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int tlc4541_get_range(struct tlc4541_state *st)
|
||||
{
|
||||
int vref;
|
||||
|
||||
vref = regulator_get_voltage(st->reg);
|
||||
if (vref < 0)
|
||||
return vref;
|
||||
|
||||
vref /= 1000;
|
||||
|
||||
return vref;
|
||||
}
|
||||
|
||||
static int tlc4541_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val,
|
||||
int *val2,
|
||||
long m)
|
||||
{
|
||||
int ret = 0;
|
||||
struct tlc4541_state *st = iio_priv(indio_dev);
|
||||
|
||||
switch (m) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = iio_device_claim_direct_mode(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = spi_sync(st->spi, &st->scan_single_msg);
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = be16_to_cpu(st->rx_buf[0]);
|
||||
*val = *val >> chan->scan_type.shift;
|
||||
*val &= GENMASK(chan->scan_type.realbits - 1, 0);
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
ret = tlc4541_get_range(st);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = ret;
|
||||
*val2 = chan->scan_type.realbits;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct iio_info tlc4541_info = {
|
||||
.read_raw = &tlc4541_read_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int tlc4541_probe(struct spi_device *spi)
|
||||
{
|
||||
struct tlc4541_state *st;
|
||||
struct iio_dev *indio_dev;
|
||||
const struct tlc4541_chip_info *info;
|
||||
int ret;
|
||||
int8_t device_init = 0;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
||||
if (indio_dev == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
|
||||
st->spi = spi;
|
||||
|
||||
info = &tlc4541_chip_info[spi_get_device_id(spi)->driver_data];
|
||||
|
||||
indio_dev->name = spi_get_device_id(spi)->name;
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = info->channels;
|
||||
indio_dev->num_channels = info->num_channels;
|
||||
indio_dev->info = &tlc4541_info;
|
||||
|
||||
/* perform reset */
|
||||
spi_write(spi, &device_init, 1);
|
||||
|
||||
/* Setup default message */
|
||||
st->scan_single_xfer[0].rx_buf = &st->rx_buf[0];
|
||||
st->scan_single_xfer[0].len = 3;
|
||||
st->scan_single_xfer[1].delay_usecs = 3;
|
||||
st->scan_single_xfer[2].rx_buf = &st->rx_buf[0];
|
||||
st->scan_single_xfer[2].len = 2;
|
||||
|
||||
spi_message_init_with_transfers(&st->scan_single_msg,
|
||||
st->scan_single_xfer, 3);
|
||||
|
||||
st->reg = devm_regulator_get(&spi->dev, "vref");
|
||||
if (IS_ERR(st->reg))
|
||||
return PTR_ERR(st->reg);
|
||||
|
||||
ret = regulator_enable(st->reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, NULL,
|
||||
&tlc4541_trigger_handler, NULL);
|
||||
if (ret)
|
||||
goto error_disable_reg;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto error_cleanup_buffer;
|
||||
|
||||
return 0;
|
||||
|
||||
error_cleanup_buffer:
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
error_disable_reg:
|
||||
regulator_disable(st->reg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tlc4541_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct tlc4541_state *st = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
regulator_disable(st->reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id tlc4541_dt_ids[] = {
|
||||
{ .compatible = "ti,tlc3541", },
|
||||
{ .compatible = "ti,tlc4541", },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, tlc4541_dt_ids);
|
||||
#endif
|
||||
|
||||
static const struct spi_device_id tlc4541_id[] = {
|
||||
{"tlc3541", TLC3541},
|
||||
{"tlc4541", TLC4541},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, tlc4541_id);
|
||||
|
||||
static struct spi_driver tlc4541_driver = {
|
||||
.driver = {
|
||||
.name = "tlc4541",
|
||||
.of_match_table = of_match_ptr(tlc4541_dt_ids),
|
||||
},
|
||||
.probe = tlc4541_probe,
|
||||
.remove = tlc4541_remove,
|
||||
.id_table = tlc4541_id,
|
||||
};
|
||||
module_spi_driver(tlc4541_driver);
|
||||
|
||||
MODULE_AUTHOR("Phil Reid <preid@electromag.com.au>");
|
||||
MODULE_DESCRIPTION("Texas Instruments TLC4541 ADC");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -378,7 +378,7 @@ static int max30100_get_temp(struct max30100_data *data, int *val)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
usleep_range(35000, 50000);
|
||||
msleep(35);
|
||||
|
||||
return max30100_read_temp(data, val);
|
||||
}
|
||||
|
|
|
@ -39,6 +39,7 @@ config KMX61
|
|||
be called kmx61.
|
||||
|
||||
source "drivers/iio/imu/inv_mpu6050/Kconfig"
|
||||
source "drivers/iio/imu/st_lsm6dsx/Kconfig"
|
||||
|
||||
endmenu
|
||||
|
||||
|
|
|
@ -17,3 +17,5 @@ obj-y += bmi160/
|
|||
obj-y += inv_mpu6050/
|
||||
|
||||
obj-$(CONFIG_KMX61) += kmx61.o
|
||||
|
||||
obj-y += st_lsm6dsx/
|
||||
|
|
|
@ -325,9 +325,9 @@ static int bmi160_get_data(struct bmi160_data *data, int chan_type,
|
|||
__le16 sample;
|
||||
enum bmi160_sensor_type t = bmi160_to_sensor(chan_type);
|
||||
|
||||
reg = bmi160_regs[t].data + (axis - IIO_MOD_X) * sizeof(__le16);
|
||||
reg = bmi160_regs[t].data + (axis - IIO_MOD_X) * sizeof(sample);
|
||||
|
||||
ret = regmap_bulk_read(data->regmap, reg, &sample, sizeof(__le16));
|
||||
ret = regmap_bulk_read(data->regmap, reg, &sample, sizeof(sample));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
@ -392,8 +392,8 @@ static irqreturn_t bmi160_trigger_handler(int irq, void *p)
|
|||
|
||||
for_each_set_bit(i, indio_dev->active_scan_mask,
|
||||
indio_dev->masklength) {
|
||||
ret = regmap_bulk_read(data->regmap, base + i * sizeof(__le16),
|
||||
&sample, sizeof(__le16));
|
||||
ret = regmap_bulk_read(data->regmap, base + i * sizeof(sample),
|
||||
&sample, sizeof(sample));
|
||||
if (ret < 0)
|
||||
goto done;
|
||||
buf[j++] = sample;
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
|
||||
config IIO_ST_LSM6DSX
|
||||
tristate "ST_LSM6DSx driver for STM 6-axis IMU MEMS sensors"
|
||||
depends on (I2C || SPI)
|
||||
select IIO_BUFFER
|
||||
select IIO_KFIFO_BUF
|
||||
select IIO_ST_LSM6DSX_I2C if (I2C)
|
||||
select IIO_ST_LSM6DSX_SPI if (SPI_MASTER)
|
||||
help
|
||||
Say yes here to build support for STMicroelectronics LSM6DSx imu
|
||||
sensor. Supported devices: lsm6ds3, lsm6dsm
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called st_lsm6dsx.
|
||||
|
||||
config IIO_ST_LSM6DSX_I2C
|
||||
tristate
|
||||
depends on IIO_ST_LSM6DSX
|
||||
|
||||
config IIO_ST_LSM6DSX_SPI
|
||||
tristate
|
||||
depends on IIO_ST_LSM6DSX
|
|
@ -0,0 +1,5 @@
|
|||
st_lsm6dsx-y := st_lsm6dsx_core.o st_lsm6dsx_buffer.o
|
||||
|
||||
obj-$(CONFIG_IIO_ST_LSM6DSX) += st_lsm6dsx.o
|
||||
obj-$(CONFIG_IIO_ST_LSM6DSX_I2C) += st_lsm6dsx_i2c.o
|
||||
obj-$(CONFIG_IIO_ST_LSM6DSX_SPI) += st_lsm6dsx_spi.o
|
|
@ -0,0 +1,141 @@
|
|||
/*
|
||||
* STMicroelectronics st_lsm6dsx sensor driver
|
||||
*
|
||||
* Copyright 2016 STMicroelectronics Inc.
|
||||
*
|
||||
* Lorenzo Bianconi <lorenzo.bianconi@st.com>
|
||||
* Denis Ciocca <denis.ciocca@st.com>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#ifndef ST_LSM6DSX_H
|
||||
#define ST_LSM6DSX_H
|
||||
|
||||
#include <linux/device.h>
|
||||
|
||||
#define ST_LSM6DS3_DEV_NAME "lsm6ds3"
|
||||
#define ST_LSM6DSM_DEV_NAME "lsm6dsm"
|
||||
|
||||
enum st_lsm6dsx_hw_id {
|
||||
ST_LSM6DS3_ID,
|
||||
ST_LSM6DSM_ID,
|
||||
};
|
||||
|
||||
#define ST_LSM6DSX_CHAN_SIZE 2
|
||||
#define ST_LSM6DSX_SAMPLE_SIZE 6
|
||||
#define ST_LSM6DSX_SAMPLE_DEPTH (ST_LSM6DSX_SAMPLE_SIZE / \
|
||||
ST_LSM6DSX_CHAN_SIZE)
|
||||
|
||||
#if defined(CONFIG_SPI_MASTER)
|
||||
#define ST_LSM6DSX_RX_MAX_LENGTH 256
|
||||
#define ST_LSM6DSX_TX_MAX_LENGTH 8
|
||||
|
||||
struct st_lsm6dsx_transfer_buffer {
|
||||
u8 rx_buf[ST_LSM6DSX_RX_MAX_LENGTH];
|
||||
u8 tx_buf[ST_LSM6DSX_TX_MAX_LENGTH] ____cacheline_aligned;
|
||||
};
|
||||
#endif /* CONFIG_SPI_MASTER */
|
||||
|
||||
struct st_lsm6dsx_transfer_function {
|
||||
int (*read)(struct device *dev, u8 addr, int len, u8 *data);
|
||||
int (*write)(struct device *dev, u8 addr, int len, u8 *data);
|
||||
};
|
||||
|
||||
struct st_lsm6dsx_reg {
|
||||
u8 addr;
|
||||
u8 mask;
|
||||
};
|
||||
|
||||
struct st_lsm6dsx_settings {
|
||||
u8 wai;
|
||||
u16 max_fifo_size;
|
||||
enum st_lsm6dsx_hw_id id;
|
||||
};
|
||||
|
||||
enum st_lsm6dsx_sensor_id {
|
||||
ST_LSM6DSX_ID_ACC,
|
||||
ST_LSM6DSX_ID_GYRO,
|
||||
ST_LSM6DSX_ID_MAX,
|
||||
};
|
||||
|
||||
enum st_lsm6dsx_fifo_mode {
|
||||
ST_LSM6DSX_FIFO_BYPASS = 0x0,
|
||||
ST_LSM6DSX_FIFO_CONT = 0x6,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct st_lsm6dsx_sensor - ST IMU sensor instance
|
||||
* @id: Sensor identifier.
|
||||
* @hw: Pointer to instance of struct st_lsm6dsx_hw.
|
||||
* @gain: Configured sensor sensitivity.
|
||||
* @odr: Output data rate of the sensor [Hz].
|
||||
* @watermark: Sensor watermark level.
|
||||
* @sip: Number of samples in a given pattern.
|
||||
* @decimator: FIFO decimation factor.
|
||||
* @decimator_mask: Sensor mask for decimation register.
|
||||
* @delta_ts: Delta time between two consecutive interrupts.
|
||||
* @ts: Latest timestamp from the interrupt handler.
|
||||
*/
|
||||
struct st_lsm6dsx_sensor {
|
||||
enum st_lsm6dsx_sensor_id id;
|
||||
struct st_lsm6dsx_hw *hw;
|
||||
|
||||
u32 gain;
|
||||
u16 odr;
|
||||
|
||||
u16 watermark;
|
||||
u8 sip;
|
||||
u8 decimator;
|
||||
u8 decimator_mask;
|
||||
|
||||
s64 delta_ts;
|
||||
s64 ts;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct st_lsm6dsx_hw - ST IMU MEMS hw instance
|
||||
* @dev: Pointer to instance of struct device (I2C or SPI).
|
||||
* @irq: Device interrupt line (I2C or SPI).
|
||||
* @lock: Mutex to protect read and write operations.
|
||||
* @fifo_lock: Mutex to prevent concurrent access to the hw FIFO.
|
||||
* @fifo_mode: FIFO operating mode supported by the device.
|
||||
* @enable_mask: Enabled sensor bitmask.
|
||||
* @sip: Total number of samples (acc/gyro) in a given pattern.
|
||||
* @iio_devs: Pointers to acc/gyro iio_dev instances.
|
||||
* @settings: Pointer to the specific sensor settings in use.
|
||||
* @tf: Transfer function structure used by I/O operations.
|
||||
* @tb: Transfer buffers used by SPI I/O operations.
|
||||
*/
|
||||
struct st_lsm6dsx_hw {
|
||||
struct device *dev;
|
||||
int irq;
|
||||
|
||||
struct mutex lock;
|
||||
struct mutex fifo_lock;
|
||||
|
||||
enum st_lsm6dsx_fifo_mode fifo_mode;
|
||||
u8 enable_mask;
|
||||
u8 sip;
|
||||
|
||||
struct iio_dev *iio_devs[ST_LSM6DSX_ID_MAX];
|
||||
|
||||
const struct st_lsm6dsx_settings *settings;
|
||||
|
||||
const struct st_lsm6dsx_transfer_function *tf;
|
||||
#if defined(CONFIG_SPI_MASTER)
|
||||
struct st_lsm6dsx_transfer_buffer tb;
|
||||
#endif /* CONFIG_SPI_MASTER */
|
||||
};
|
||||
|
||||
int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id,
|
||||
const struct st_lsm6dsx_transfer_function *tf_ops);
|
||||
int st_lsm6dsx_sensor_enable(struct st_lsm6dsx_sensor *sensor);
|
||||
int st_lsm6dsx_sensor_disable(struct st_lsm6dsx_sensor *sensor);
|
||||
int st_lsm6dsx_fifo_setup(struct st_lsm6dsx_hw *hw);
|
||||
int st_lsm6dsx_write_with_mask(struct st_lsm6dsx_hw *hw, u8 addr, u8 mask,
|
||||
u8 val);
|
||||
int st_lsm6dsx_update_watermark(struct st_lsm6dsx_sensor *sensor,
|
||||
u16 watermark);
|
||||
|
||||
#endif /* ST_LSM6DSX_H */
|
|
@ -0,0 +1,454 @@
|
|||
/*
|
||||
* STMicroelectronics st_lsm6dsx FIFO buffer library driver
|
||||
*
|
||||
* LSM6DS3/LSM6DSM: The FIFO buffer can be configured to store data
|
||||
* from gyroscope and accelerometer. Samples are queued without any tag
|
||||
* according to a specific pattern based on 'FIFO data sets' (6 bytes each):
|
||||
* - 1st data set is reserved for gyroscope data
|
||||
* - 2nd data set is reserved for accelerometer data
|
||||
* The FIFO pattern changes depending on the ODRs and decimation factors
|
||||
* assigned to the FIFO data sets. The first sequence of data stored in FIFO
|
||||
* buffer contains the data of all the enabled FIFO data sets
|
||||
* (e.g. Gx, Gy, Gz, Ax, Ay, Az), then data are repeated depending on the
|
||||
* value of the decimation factor and ODR set for each FIFO data set.
|
||||
* FIFO supported modes:
|
||||
* - BYPASS: FIFO disabled
|
||||
* - CONTINUOUS: FIFO enabled. When the buffer is full, the FIFO index
|
||||
* restarts from the beginning and the oldest sample is overwritten
|
||||
*
|
||||
* Copyright 2016 STMicroelectronics Inc.
|
||||
*
|
||||
* Lorenzo Bianconi <lorenzo.bianconi@st.com>
|
||||
* Denis Ciocca <denis.ciocca@st.com>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/iio/kfifo_buf.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
|
||||
#include "st_lsm6dsx.h"
|
||||
|
||||
#define ST_LSM6DSX_REG_FIFO_THL_ADDR 0x06
|
||||
#define ST_LSM6DSX_REG_FIFO_THH_ADDR 0x07
|
||||
#define ST_LSM6DSX_FIFO_TH_MASK GENMASK(11, 0)
|
||||
#define ST_LSM6DSX_REG_FIFO_DEC_GXL_ADDR 0x08
|
||||
#define ST_LSM6DSX_REG_FIFO_MODE_ADDR 0x0a
|
||||
#define ST_LSM6DSX_FIFO_MODE_MASK GENMASK(2, 0)
|
||||
#define ST_LSM6DSX_FIFO_ODR_MASK GENMASK(6, 3)
|
||||
#define ST_LSM6DSX_REG_FIFO_DIFFL_ADDR 0x3a
|
||||
#define ST_LSM6DSX_FIFO_DIFF_MASK GENMASK(11, 0)
|
||||
#define ST_LSM6DSX_FIFO_EMPTY_MASK BIT(12)
|
||||
#define ST_LSM6DSX_REG_FIFO_OUTL_ADDR 0x3e
|
||||
|
||||
#define ST_LSM6DSX_MAX_FIFO_ODR_VAL 0x08
|
||||
|
||||
struct st_lsm6dsx_decimator_entry {
|
||||
u8 decimator;
|
||||
u8 val;
|
||||
};
|
||||
|
||||
static const
|
||||
struct st_lsm6dsx_decimator_entry st_lsm6dsx_decimator_table[] = {
|
||||
{ 0, 0x0 },
|
||||
{ 1, 0x1 },
|
||||
{ 2, 0x2 },
|
||||
{ 3, 0x3 },
|
||||
{ 4, 0x4 },
|
||||
{ 8, 0x5 },
|
||||
{ 16, 0x6 },
|
||||
{ 32, 0x7 },
|
||||
};
|
||||
|
||||
static int st_lsm6dsx_get_decimator_val(u8 val)
|
||||
{
|
||||
const int max_size = ARRAY_SIZE(st_lsm6dsx_decimator_table);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < max_size; i++)
|
||||
if (st_lsm6dsx_decimator_table[i].decimator == val)
|
||||
break;
|
||||
|
||||
return i == max_size ? 0 : st_lsm6dsx_decimator_table[i].val;
|
||||
}
|
||||
|
||||
static void st_lsm6dsx_get_max_min_odr(struct st_lsm6dsx_hw *hw,
|
||||
u16 *max_odr, u16 *min_odr)
|
||||
{
|
||||
struct st_lsm6dsx_sensor *sensor;
|
||||
int i;
|
||||
|
||||
*max_odr = 0, *min_odr = ~0;
|
||||
for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
|
||||
sensor = iio_priv(hw->iio_devs[i]);
|
||||
|
||||
if (!(hw->enable_mask & BIT(sensor->id)))
|
||||
continue;
|
||||
|
||||
*max_odr = max_t(u16, *max_odr, sensor->odr);
|
||||
*min_odr = min_t(u16, *min_odr, sensor->odr);
|
||||
}
|
||||
}
|
||||
|
||||
static int st_lsm6dsx_update_decimators(struct st_lsm6dsx_hw *hw)
|
||||
{
|
||||
struct st_lsm6dsx_sensor *sensor;
|
||||
u16 max_odr, min_odr, sip = 0;
|
||||
int err, i;
|
||||
u8 data;
|
||||
|
||||
st_lsm6dsx_get_max_min_odr(hw, &max_odr, &min_odr);
|
||||
|
||||
for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
|
||||
sensor = iio_priv(hw->iio_devs[i]);
|
||||
|
||||
/* update fifo decimators and sample in pattern */
|
||||
if (hw->enable_mask & BIT(sensor->id)) {
|
||||
sensor->sip = sensor->odr / min_odr;
|
||||
sensor->decimator = max_odr / sensor->odr;
|
||||
data = st_lsm6dsx_get_decimator_val(sensor->decimator);
|
||||
} else {
|
||||
sensor->sip = 0;
|
||||
sensor->decimator = 0;
|
||||
data = 0;
|
||||
}
|
||||
|
||||
err = st_lsm6dsx_write_with_mask(hw,
|
||||
ST_LSM6DSX_REG_FIFO_DEC_GXL_ADDR,
|
||||
sensor->decimator_mask, data);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
sip += sensor->sip;
|
||||
}
|
||||
hw->sip = sip;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int st_lsm6dsx_set_fifo_mode(struct st_lsm6dsx_hw *hw,
|
||||
enum st_lsm6dsx_fifo_mode fifo_mode)
|
||||
{
|
||||
u8 data;
|
||||
int err;
|
||||
|
||||
switch (fifo_mode) {
|
||||
case ST_LSM6DSX_FIFO_BYPASS:
|
||||
data = fifo_mode;
|
||||
break;
|
||||
case ST_LSM6DSX_FIFO_CONT:
|
||||
data = (ST_LSM6DSX_MAX_FIFO_ODR_VAL <<
|
||||
__ffs(ST_LSM6DSX_FIFO_ODR_MASK)) | fifo_mode;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = hw->tf->write(hw->dev, ST_LSM6DSX_REG_FIFO_MODE_ADDR,
|
||||
sizeof(data), &data);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
hw->fifo_mode = fifo_mode;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int st_lsm6dsx_update_watermark(struct st_lsm6dsx_sensor *sensor, u16 watermark)
|
||||
{
|
||||
u16 fifo_watermark = ~0, cur_watermark, sip = 0;
|
||||
struct st_lsm6dsx_hw *hw = sensor->hw;
|
||||
struct st_lsm6dsx_sensor *cur_sensor;
|
||||
__le16 wdata;
|
||||
int i, err;
|
||||
u8 data;
|
||||
|
||||
for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
|
||||
cur_sensor = iio_priv(hw->iio_devs[i]);
|
||||
|
||||
if (!(hw->enable_mask & BIT(cur_sensor->id)))
|
||||
continue;
|
||||
|
||||
cur_watermark = (cur_sensor == sensor) ? watermark
|
||||
: cur_sensor->watermark;
|
||||
|
||||
fifo_watermark = min_t(u16, fifo_watermark, cur_watermark);
|
||||
sip += cur_sensor->sip;
|
||||
}
|
||||
|
||||
if (!sip)
|
||||
return 0;
|
||||
|
||||
fifo_watermark = max_t(u16, fifo_watermark, sip);
|
||||
fifo_watermark = (fifo_watermark / sip) * sip;
|
||||
fifo_watermark = fifo_watermark * ST_LSM6DSX_SAMPLE_DEPTH;
|
||||
|
||||
mutex_lock(&hw->lock);
|
||||
|
||||
err = hw->tf->read(hw->dev, ST_LSM6DSX_REG_FIFO_THH_ADDR,
|
||||
sizeof(data), &data);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
fifo_watermark = ((data & ~ST_LSM6DSX_FIFO_TH_MASK) << 8) |
|
||||
(fifo_watermark & ST_LSM6DSX_FIFO_TH_MASK);
|
||||
|
||||
wdata = cpu_to_le16(fifo_watermark);
|
||||
err = hw->tf->write(hw->dev, ST_LSM6DSX_REG_FIFO_THL_ADDR,
|
||||
sizeof(wdata), (u8 *)&wdata);
|
||||
out:
|
||||
mutex_unlock(&hw->lock);
|
||||
|
||||
return err < 0 ? err : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* st_lsm6dsx_read_fifo() - LSM6DS3-LSM6DSM read FIFO routine
|
||||
* @hw: Pointer to instance of struct st_lsm6dsx_hw.
|
||||
*
|
||||
* Read samples from the hw FIFO and push them to IIO buffers.
|
||||
*
|
||||
* Return: Number of bytes read from the FIFO
|
||||
*/
|
||||
static int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw)
|
||||
{
|
||||
u16 fifo_len, pattern_len = hw->sip * ST_LSM6DSX_SAMPLE_SIZE;
|
||||
int err, acc_sip, gyro_sip, read_len, samples, offset;
|
||||
struct st_lsm6dsx_sensor *acc_sensor, *gyro_sensor;
|
||||
s64 acc_ts, acc_delta_ts, gyro_ts, gyro_delta_ts;
|
||||
u8 iio_buff[ALIGN(ST_LSM6DSX_SAMPLE_SIZE, sizeof(s64)) + sizeof(s64)];
|
||||
u8 buff[pattern_len];
|
||||
__le16 fifo_status;
|
||||
|
||||
err = hw->tf->read(hw->dev, ST_LSM6DSX_REG_FIFO_DIFFL_ADDR,
|
||||
sizeof(fifo_status), (u8 *)&fifo_status);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (fifo_status & cpu_to_le16(ST_LSM6DSX_FIFO_EMPTY_MASK))
|
||||
return 0;
|
||||
|
||||
fifo_len = (le16_to_cpu(fifo_status) & ST_LSM6DSX_FIFO_DIFF_MASK) *
|
||||
ST_LSM6DSX_CHAN_SIZE;
|
||||
samples = fifo_len / ST_LSM6DSX_SAMPLE_SIZE;
|
||||
fifo_len = (fifo_len / pattern_len) * pattern_len;
|
||||
|
||||
/*
|
||||
* compute delta timestamp between two consecutive samples
|
||||
* in order to estimate queueing time of data generated
|
||||
* by the sensor
|
||||
*/
|
||||
acc_sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_ACC]);
|
||||
acc_ts = acc_sensor->ts - acc_sensor->delta_ts;
|
||||
acc_delta_ts = div_s64(acc_sensor->delta_ts * acc_sensor->decimator,
|
||||
samples);
|
||||
|
||||
gyro_sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_GYRO]);
|
||||
gyro_ts = gyro_sensor->ts - gyro_sensor->delta_ts;
|
||||
gyro_delta_ts = div_s64(gyro_sensor->delta_ts * gyro_sensor->decimator,
|
||||
samples);
|
||||
|
||||
for (read_len = 0; read_len < fifo_len; read_len += pattern_len) {
|
||||
err = hw->tf->read(hw->dev, ST_LSM6DSX_REG_FIFO_OUTL_ADDR,
|
||||
sizeof(buff), buff);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/*
|
||||
* Data are written to the FIFO with a specific pattern
|
||||
* depending on the configured ODRs. The first sequence of data
|
||||
* stored in FIFO contains the data of all enabled sensors
|
||||
* (e.g. Gx, Gy, Gz, Ax, Ay, Az), then data are repeated
|
||||
* depending on the value of the decimation factor set for each
|
||||
* sensor.
|
||||
*
|
||||
* Supposing the FIFO is storing data from gyroscope and
|
||||
* accelerometer at different ODRs:
|
||||
* - gyroscope ODR = 208Hz, accelerometer ODR = 104Hz
|
||||
* Since the gyroscope ODR is twice the accelerometer one, the
|
||||
* following pattern is repeated every 9 samples:
|
||||
* - Gx, Gy, Gz, Ax, Ay, Az, Gx, Gy, Gz
|
||||
*/
|
||||
gyro_sip = gyro_sensor->sip;
|
||||
acc_sip = acc_sensor->sip;
|
||||
offset = 0;
|
||||
|
||||
while (acc_sip > 0 || gyro_sip > 0) {
|
||||
if (gyro_sip-- > 0) {
|
||||
memcpy(iio_buff, &buff[offset],
|
||||
ST_LSM6DSX_SAMPLE_SIZE);
|
||||
iio_push_to_buffers_with_timestamp(
|
||||
hw->iio_devs[ST_LSM6DSX_ID_GYRO],
|
||||
iio_buff, gyro_ts);
|
||||
offset += ST_LSM6DSX_SAMPLE_SIZE;
|
||||
gyro_ts += gyro_delta_ts;
|
||||
}
|
||||
|
||||
if (acc_sip-- > 0) {
|
||||
memcpy(iio_buff, &buff[offset],
|
||||
ST_LSM6DSX_SAMPLE_SIZE);
|
||||
iio_push_to_buffers_with_timestamp(
|
||||
hw->iio_devs[ST_LSM6DSX_ID_ACC],
|
||||
iio_buff, acc_ts);
|
||||
offset += ST_LSM6DSX_SAMPLE_SIZE;
|
||||
acc_ts += acc_delta_ts;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return read_len;
|
||||
}
|
||||
|
||||
static int st_lsm6dsx_flush_fifo(struct st_lsm6dsx_hw *hw)
|
||||
{
|
||||
int err;
|
||||
|
||||
mutex_lock(&hw->fifo_lock);
|
||||
|
||||
st_lsm6dsx_read_fifo(hw);
|
||||
err = st_lsm6dsx_set_fifo_mode(hw, ST_LSM6DSX_FIFO_BYPASS);
|
||||
|
||||
mutex_unlock(&hw->fifo_lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int st_lsm6dsx_update_fifo(struct iio_dev *iio_dev, bool enable)
|
||||
{
|
||||
struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev);
|
||||
struct st_lsm6dsx_hw *hw = sensor->hw;
|
||||
int err;
|
||||
|
||||
if (hw->fifo_mode != ST_LSM6DSX_FIFO_BYPASS) {
|
||||
err = st_lsm6dsx_flush_fifo(hw);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (enable) {
|
||||
err = st_lsm6dsx_sensor_enable(sensor);
|
||||
if (err < 0)
|
||||
return err;
|
||||
} else {
|
||||
err = st_lsm6dsx_sensor_disable(sensor);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
err = st_lsm6dsx_update_decimators(hw);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = st_lsm6dsx_update_watermark(sensor, sensor->watermark);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (hw->enable_mask) {
|
||||
err = st_lsm6dsx_set_fifo_mode(hw, ST_LSM6DSX_FIFO_CONT);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/*
|
||||
* store enable buffer timestamp as reference to compute
|
||||
* first delta timestamp
|
||||
*/
|
||||
sensor->ts = iio_get_time_ns(iio_dev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t st_lsm6dsx_handler_irq(int irq, void *private)
|
||||
{
|
||||
struct st_lsm6dsx_hw *hw = (struct st_lsm6dsx_hw *)private;
|
||||
struct st_lsm6dsx_sensor *sensor;
|
||||
int i;
|
||||
|
||||
if (!hw->sip)
|
||||
return IRQ_NONE;
|
||||
|
||||
for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
|
||||
sensor = iio_priv(hw->iio_devs[i]);
|
||||
|
||||
if (sensor->sip > 0) {
|
||||
s64 timestamp;
|
||||
|
||||
timestamp = iio_get_time_ns(hw->iio_devs[i]);
|
||||
sensor->delta_ts = timestamp - sensor->ts;
|
||||
sensor->ts = timestamp;
|
||||
}
|
||||
}
|
||||
|
||||
return IRQ_WAKE_THREAD;
|
||||
}
|
||||
|
||||
static irqreturn_t st_lsm6dsx_handler_thread(int irq, void *private)
|
||||
{
|
||||
struct st_lsm6dsx_hw *hw = (struct st_lsm6dsx_hw *)private;
|
||||
int count;
|
||||
|
||||
mutex_lock(&hw->fifo_lock);
|
||||
count = st_lsm6dsx_read_fifo(hw);
|
||||
mutex_unlock(&hw->fifo_lock);
|
||||
|
||||
return !count ? IRQ_NONE : IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int st_lsm6dsx_buffer_preenable(struct iio_dev *iio_dev)
|
||||
{
|
||||
return st_lsm6dsx_update_fifo(iio_dev, true);
|
||||
}
|
||||
|
||||
static int st_lsm6dsx_buffer_postdisable(struct iio_dev *iio_dev)
|
||||
{
|
||||
return st_lsm6dsx_update_fifo(iio_dev, false);
|
||||
}
|
||||
|
||||
static const struct iio_buffer_setup_ops st_lsm6dsx_buffer_ops = {
|
||||
.preenable = st_lsm6dsx_buffer_preenable,
|
||||
.postdisable = st_lsm6dsx_buffer_postdisable,
|
||||
};
|
||||
|
||||
int st_lsm6dsx_fifo_setup(struct st_lsm6dsx_hw *hw)
|
||||
{
|
||||
struct iio_buffer *buffer;
|
||||
unsigned long irq_type;
|
||||
int i, err;
|
||||
|
||||
irq_type = irqd_get_trigger_type(irq_get_irq_data(hw->irq));
|
||||
|
||||
switch (irq_type) {
|
||||
case IRQF_TRIGGER_HIGH:
|
||||
case IRQF_TRIGGER_RISING:
|
||||
break;
|
||||
default:
|
||||
dev_info(hw->dev, "mode %lx unsupported\n", irq_type);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = devm_request_threaded_irq(hw->dev, hw->irq,
|
||||
st_lsm6dsx_handler_irq,
|
||||
st_lsm6dsx_handler_thread,
|
||||
irq_type | IRQF_ONESHOT,
|
||||
"lsm6dsx", hw);
|
||||
if (err) {
|
||||
dev_err(hw->dev, "failed to request trigger irq %d\n",
|
||||
hw->irq);
|
||||
return err;
|
||||
}
|
||||
|
||||
for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
|
||||
buffer = devm_iio_kfifo_allocate(hw->dev);
|
||||
if (!buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
iio_device_attach_buffer(hw->iio_devs[i], buffer);
|
||||
hw->iio_devs[i]->modes |= INDIO_BUFFER_SOFTWARE;
|
||||
hw->iio_devs[i]->setup_ops = &st_lsm6dsx_buffer_ops;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,673 @@
|
|||
/*
|
||||
* STMicroelectronics st_lsm6dsx sensor driver
|
||||
*
|
||||
* The ST LSM6DSx IMU MEMS series consists of 3D digital accelerometer
|
||||
* and 3D digital gyroscope system-in-package with a digital I2C/SPI serial
|
||||
* interface standard output.
|
||||
* LSM6DSx IMU MEMS series has a dynamic user-selectable full-scale
|
||||
* acceleration range of +-2/+-4/+-8/+-16 g and an angular rate range of
|
||||
* +-125/+-245/+-500/+-1000/+-2000 dps
|
||||
* LSM6DSx series has an integrated First-In-First-Out (FIFO) buffer
|
||||
* allowing dynamic batching of sensor data.
|
||||
*
|
||||
* Supported sensors:
|
||||
* - LSM6DS3:
|
||||
* - Accelerometer/Gyroscope supported ODR [Hz]: 13, 26, 52, 104, 208, 416
|
||||
* - Accelerometer supported full-scale [g]: +-2/+-4/+-8/+-16
|
||||
* - Gyroscope supported full-scale [dps]: +-125/+-245/+-500/+-1000/+-2000
|
||||
* - FIFO size: 8KB
|
||||
*
|
||||
* - LSM6DSM:
|
||||
* - Accelerometer/Gyroscope supported ODR [Hz]: 13, 26, 52, 104, 208, 416
|
||||
* - Accelerometer supported full-scale [g]: +-2/+-4/+-8/+-16
|
||||
* - Gyroscope supported full-scale [dps]: +-125/+-245/+-500/+-1000/+-2000
|
||||
* - FIFO size: 4KB
|
||||
*
|
||||
* Copyright 2016 STMicroelectronics Inc.
|
||||
*
|
||||
* Lorenzo Bianconi <lorenzo.bianconi@st.com>
|
||||
* Denis Ciocca <denis.ciocca@st.com>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
#include "st_lsm6dsx.h"
|
||||
|
||||
#define ST_LSM6DSX_REG_ACC_DEC_MASK GENMASK(2, 0)
|
||||
#define ST_LSM6DSX_REG_GYRO_DEC_MASK GENMASK(5, 3)
|
||||
#define ST_LSM6DSX_REG_INT1_ADDR 0x0d
|
||||
#define ST_LSM6DSX_REG_FIFO_FTH_IRQ_MASK BIT(3)
|
||||
#define ST_LSM6DSX_REG_WHOAMI_ADDR 0x0f
|
||||
#define ST_LSM6DSX_REG_RESET_ADDR 0x12
|
||||
#define ST_LSM6DSX_REG_RESET_MASK BIT(0)
|
||||
#define ST_LSM6DSX_REG_BDU_ADDR 0x12
|
||||
#define ST_LSM6DSX_REG_BDU_MASK BIT(6)
|
||||
#define ST_LSM6DSX_REG_INT2_ON_INT1_ADDR 0x13
|
||||
#define ST_LSM6DSX_REG_INT2_ON_INT1_MASK BIT(5)
|
||||
#define ST_LSM6DSX_REG_ROUNDING_ADDR 0x16
|
||||
#define ST_LSM6DSX_REG_ROUNDING_MASK BIT(2)
|
||||
#define ST_LSM6DSX_REG_LIR_ADDR 0x58
|
||||
#define ST_LSM6DSX_REG_LIR_MASK BIT(0)
|
||||
|
||||
#define ST_LSM6DSX_REG_ACC_ODR_ADDR 0x10
|
||||
#define ST_LSM6DSX_REG_ACC_ODR_MASK GENMASK(7, 4)
|
||||
#define ST_LSM6DSX_REG_ACC_FS_ADDR 0x10
|
||||
#define ST_LSM6DSX_REG_ACC_FS_MASK GENMASK(3, 2)
|
||||
#define ST_LSM6DSX_REG_ACC_OUT_X_L_ADDR 0x28
|
||||
#define ST_LSM6DSX_REG_ACC_OUT_Y_L_ADDR 0x2a
|
||||
#define ST_LSM6DSX_REG_ACC_OUT_Z_L_ADDR 0x2c
|
||||
|
||||
#define ST_LSM6DSX_REG_GYRO_ODR_ADDR 0x11
|
||||
#define ST_LSM6DSX_REG_GYRO_ODR_MASK GENMASK(7, 4)
|
||||
#define ST_LSM6DSX_REG_GYRO_FS_ADDR 0x11
|
||||
#define ST_LSM6DSX_REG_GYRO_FS_MASK GENMASK(3, 2)
|
||||
#define ST_LSM6DSX_REG_GYRO_OUT_X_L_ADDR 0x22
|
||||
#define ST_LSM6DSX_REG_GYRO_OUT_Y_L_ADDR 0x24
|
||||
#define ST_LSM6DSX_REG_GYRO_OUT_Z_L_ADDR 0x26
|
||||
|
||||
#define ST_LSM6DS3_WHOAMI 0x69
|
||||
#define ST_LSM6DSM_WHOAMI 0x6a
|
||||
|
||||
#define ST_LSM6DS3_MAX_FIFO_SIZE 8192
|
||||
#define ST_LSM6DSM_MAX_FIFO_SIZE 4096
|
||||
|
||||
#define ST_LSM6DSX_ACC_FS_2G_GAIN IIO_G_TO_M_S_2(61)
|
||||
#define ST_LSM6DSX_ACC_FS_4G_GAIN IIO_G_TO_M_S_2(122)
|
||||
#define ST_LSM6DSX_ACC_FS_8G_GAIN IIO_G_TO_M_S_2(244)
|
||||
#define ST_LSM6DSX_ACC_FS_16G_GAIN IIO_G_TO_M_S_2(488)
|
||||
|
||||
#define ST_LSM6DSX_GYRO_FS_245_GAIN IIO_DEGREE_TO_RAD(8750)
|
||||
#define ST_LSM6DSX_GYRO_FS_500_GAIN IIO_DEGREE_TO_RAD(17500)
|
||||
#define ST_LSM6DSX_GYRO_FS_1000_GAIN IIO_DEGREE_TO_RAD(35000)
|
||||
#define ST_LSM6DSX_GYRO_FS_2000_GAIN IIO_DEGREE_TO_RAD(70000)
|
||||
|
||||
struct st_lsm6dsx_odr {
|
||||
u16 hz;
|
||||
u8 val;
|
||||
};
|
||||
|
||||
#define ST_LSM6DSX_ODR_LIST_SIZE 6
|
||||
struct st_lsm6dsx_odr_table_entry {
|
||||
struct st_lsm6dsx_reg reg;
|
||||
struct st_lsm6dsx_odr odr_avl[ST_LSM6DSX_ODR_LIST_SIZE];
|
||||
};
|
||||
|
||||
static const struct st_lsm6dsx_odr_table_entry st_lsm6dsx_odr_table[] = {
|
||||
[ST_LSM6DSX_ID_ACC] = {
|
||||
.reg = {
|
||||
.addr = ST_LSM6DSX_REG_ACC_ODR_ADDR,
|
||||
.mask = ST_LSM6DSX_REG_ACC_ODR_MASK,
|
||||
},
|
||||
.odr_avl[0] = { 13, 0x01 },
|
||||
.odr_avl[1] = { 26, 0x02 },
|
||||
.odr_avl[2] = { 52, 0x03 },
|
||||
.odr_avl[3] = { 104, 0x04 },
|
||||
.odr_avl[4] = { 208, 0x05 },
|
||||
.odr_avl[5] = { 416, 0x06 },
|
||||
},
|
||||
[ST_LSM6DSX_ID_GYRO] = {
|
||||
.reg = {
|
||||
.addr = ST_LSM6DSX_REG_GYRO_ODR_ADDR,
|
||||
.mask = ST_LSM6DSX_REG_GYRO_ODR_MASK,
|
||||
},
|
||||
.odr_avl[0] = { 13, 0x01 },
|
||||
.odr_avl[1] = { 26, 0x02 },
|
||||
.odr_avl[2] = { 52, 0x03 },
|
||||
.odr_avl[3] = { 104, 0x04 },
|
||||
.odr_avl[4] = { 208, 0x05 },
|
||||
.odr_avl[5] = { 416, 0x06 },
|
||||
}
|
||||
};
|
||||
|
||||
struct st_lsm6dsx_fs {
|
||||
u32 gain;
|
||||
u8 val;
|
||||
};
|
||||
|
||||
#define ST_LSM6DSX_FS_LIST_SIZE 4
|
||||
struct st_lsm6dsx_fs_table_entry {
|
||||
struct st_lsm6dsx_reg reg;
|
||||
struct st_lsm6dsx_fs fs_avl[ST_LSM6DSX_FS_LIST_SIZE];
|
||||
};
|
||||
|
||||
static const struct st_lsm6dsx_fs_table_entry st_lsm6dsx_fs_table[] = {
|
||||
[ST_LSM6DSX_ID_ACC] = {
|
||||
.reg = {
|
||||
.addr = ST_LSM6DSX_REG_ACC_FS_ADDR,
|
||||
.mask = ST_LSM6DSX_REG_ACC_FS_MASK,
|
||||
},
|
||||
.fs_avl[0] = { ST_LSM6DSX_ACC_FS_2G_GAIN, 0x0 },
|
||||
.fs_avl[1] = { ST_LSM6DSX_ACC_FS_4G_GAIN, 0x2 },
|
||||
.fs_avl[2] = { ST_LSM6DSX_ACC_FS_8G_GAIN, 0x3 },
|
||||
.fs_avl[3] = { ST_LSM6DSX_ACC_FS_16G_GAIN, 0x1 },
|
||||
},
|
||||
[ST_LSM6DSX_ID_GYRO] = {
|
||||
.reg = {
|
||||
.addr = ST_LSM6DSX_REG_GYRO_FS_ADDR,
|
||||
.mask = ST_LSM6DSX_REG_GYRO_FS_MASK,
|
||||
},
|
||||
.fs_avl[0] = { ST_LSM6DSX_GYRO_FS_245_GAIN, 0x0 },
|
||||
.fs_avl[1] = { ST_LSM6DSX_GYRO_FS_500_GAIN, 0x1 },
|
||||
.fs_avl[2] = { ST_LSM6DSX_GYRO_FS_1000_GAIN, 0x2 },
|
||||
.fs_avl[3] = { ST_LSM6DSX_GYRO_FS_2000_GAIN, 0x3 },
|
||||
}
|
||||
};
|
||||
|
||||
static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
|
||||
{
|
||||
.wai = ST_LSM6DS3_WHOAMI,
|
||||
.max_fifo_size = ST_LSM6DS3_MAX_FIFO_SIZE,
|
||||
.id = ST_LSM6DS3_ID,
|
||||
},
|
||||
{
|
||||
.wai = ST_LSM6DSM_WHOAMI,
|
||||
.max_fifo_size = ST_LSM6DSM_MAX_FIFO_SIZE,
|
||||
.id = ST_LSM6DSM_ID,
|
||||
},
|
||||
};
|
||||
|
||||
#define ST_LSM6DSX_CHANNEL(chan_type, addr, mod, scan_idx) \
|
||||
{ \
|
||||
.type = chan_type, \
|
||||
.address = addr, \
|
||||
.modified = 1, \
|
||||
.channel2 = mod, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
|
||||
.scan_index = scan_idx, \
|
||||
.scan_type = { \
|
||||
.sign = 's', \
|
||||
.realbits = 16, \
|
||||
.storagebits = 16, \
|
||||
.endianness = IIO_LE, \
|
||||
}, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec st_lsm6dsx_acc_channels[] = {
|
||||
ST_LSM6DSX_CHANNEL(IIO_ACCEL, ST_LSM6DSX_REG_ACC_OUT_X_L_ADDR,
|
||||
IIO_MOD_X, 0),
|
||||
ST_LSM6DSX_CHANNEL(IIO_ACCEL, ST_LSM6DSX_REG_ACC_OUT_Y_L_ADDR,
|
||||
IIO_MOD_Y, 1),
|
||||
ST_LSM6DSX_CHANNEL(IIO_ACCEL, ST_LSM6DSX_REG_ACC_OUT_Z_L_ADDR,
|
||||
IIO_MOD_Z, 2),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(3),
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec st_lsm6dsx_gyro_channels[] = {
|
||||
ST_LSM6DSX_CHANNEL(IIO_ANGL_VEL, ST_LSM6DSX_REG_GYRO_OUT_X_L_ADDR,
|
||||
IIO_MOD_X, 0),
|
||||
ST_LSM6DSX_CHANNEL(IIO_ANGL_VEL, ST_LSM6DSX_REG_GYRO_OUT_Y_L_ADDR,
|
||||
IIO_MOD_Y, 1),
|
||||
ST_LSM6DSX_CHANNEL(IIO_ANGL_VEL, ST_LSM6DSX_REG_GYRO_OUT_Z_L_ADDR,
|
||||
IIO_MOD_Z, 2),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(3),
|
||||
};
|
||||
|
||||
int st_lsm6dsx_write_with_mask(struct st_lsm6dsx_hw *hw, u8 addr, u8 mask,
|
||||
u8 val)
|
||||
{
|
||||
u8 data;
|
||||
int err;
|
||||
|
||||
mutex_lock(&hw->lock);
|
||||
|
||||
err = hw->tf->read(hw->dev, addr, sizeof(data), &data);
|
||||
if (err < 0) {
|
||||
dev_err(hw->dev, "failed to read %02x register\n", addr);
|
||||
goto out;
|
||||
}
|
||||
|
||||
data = (data & ~mask) | ((val << __ffs(mask)) & mask);
|
||||
|
||||
err = hw->tf->write(hw->dev, addr, sizeof(data), &data);
|
||||
if (err < 0)
|
||||
dev_err(hw->dev, "failed to write %02x register\n", addr);
|
||||
|
||||
out:
|
||||
mutex_unlock(&hw->lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int st_lsm6dsx_check_whoami(struct st_lsm6dsx_hw *hw, int id)
|
||||
{
|
||||
int err, i;
|
||||
u8 data;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(st_lsm6dsx_sensor_settings); i++) {
|
||||
if (id == st_lsm6dsx_sensor_settings[i].id)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == ARRAY_SIZE(st_lsm6dsx_sensor_settings)) {
|
||||
dev_err(hw->dev, "unsupported hw id [%02x]\n", id);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
err = hw->tf->read(hw->dev, ST_LSM6DSX_REG_WHOAMI_ADDR, sizeof(data),
|
||||
&data);
|
||||
if (err < 0) {
|
||||
dev_err(hw->dev, "failed to read whoami register\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
if (data != st_lsm6dsx_sensor_settings[i].wai) {
|
||||
dev_err(hw->dev, "unsupported whoami [%02x]\n", data);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
hw->settings = &st_lsm6dsx_sensor_settings[i];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int st_lsm6dsx_set_full_scale(struct st_lsm6dsx_sensor *sensor,
|
||||
u32 gain)
|
||||
{
|
||||
enum st_lsm6dsx_sensor_id id = sensor->id;
|
||||
int i, err;
|
||||
u8 val;
|
||||
|
||||
for (i = 0; i < ST_LSM6DSX_FS_LIST_SIZE; i++)
|
||||
if (st_lsm6dsx_fs_table[id].fs_avl[i].gain == gain)
|
||||
break;
|
||||
|
||||
if (i == ST_LSM6DSX_FS_LIST_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
val = st_lsm6dsx_fs_table[id].fs_avl[i].val;
|
||||
err = st_lsm6dsx_write_with_mask(sensor->hw,
|
||||
st_lsm6dsx_fs_table[id].reg.addr,
|
||||
st_lsm6dsx_fs_table[id].reg.mask,
|
||||
val);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
sensor->gain = gain;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int st_lsm6dsx_set_odr(struct st_lsm6dsx_sensor *sensor, u16 odr)
|
||||
{
|
||||
enum st_lsm6dsx_sensor_id id = sensor->id;
|
||||
int i, err;
|
||||
u8 val;
|
||||
|
||||
for (i = 0; i < ST_LSM6DSX_ODR_LIST_SIZE; i++)
|
||||
if (st_lsm6dsx_odr_table[id].odr_avl[i].hz == odr)
|
||||
break;
|
||||
|
||||
if (i == ST_LSM6DSX_ODR_LIST_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
val = st_lsm6dsx_odr_table[id].odr_avl[i].val;
|
||||
err = st_lsm6dsx_write_with_mask(sensor->hw,
|
||||
st_lsm6dsx_odr_table[id].reg.addr,
|
||||
st_lsm6dsx_odr_table[id].reg.mask,
|
||||
val);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
sensor->odr = odr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int st_lsm6dsx_sensor_enable(struct st_lsm6dsx_sensor *sensor)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = st_lsm6dsx_set_odr(sensor, sensor->odr);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
sensor->hw->enable_mask |= BIT(sensor->id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int st_lsm6dsx_sensor_disable(struct st_lsm6dsx_sensor *sensor)
|
||||
{
|
||||
enum st_lsm6dsx_sensor_id id = sensor->id;
|
||||
int err;
|
||||
|
||||
err = st_lsm6dsx_write_with_mask(sensor->hw,
|
||||
st_lsm6dsx_odr_table[id].reg.addr,
|
||||
st_lsm6dsx_odr_table[id].reg.mask, 0);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
sensor->hw->enable_mask &= ~BIT(id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int st_lsm6dsx_read_oneshot(struct st_lsm6dsx_sensor *sensor,
|
||||
u8 addr, int *val)
|
||||
{
|
||||
int err, delay;
|
||||
__le16 data;
|
||||
|
||||
err = st_lsm6dsx_sensor_enable(sensor);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
delay = 1000000 / sensor->odr;
|
||||
usleep_range(delay, 2 * delay);
|
||||
|
||||
err = sensor->hw->tf->read(sensor->hw->dev, addr, sizeof(data),
|
||||
(u8 *)&data);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
st_lsm6dsx_sensor_disable(sensor);
|
||||
|
||||
*val = (s16)data;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
static int st_lsm6dsx_read_raw(struct iio_dev *iio_dev,
|
||||
struct iio_chan_spec const *ch,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = iio_device_claim_direct_mode(iio_dev);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
ret = st_lsm6dsx_read_oneshot(sensor, ch->address, val);
|
||||
iio_device_release_direct_mode(iio_dev);
|
||||
break;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
*val = sensor->odr;
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = 0;
|
||||
*val2 = sensor->gain;
|
||||
ret = IIO_VAL_INT_PLUS_MICRO;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int st_lsm6dsx_write_raw(struct iio_dev *iio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev);
|
||||
int err;
|
||||
|
||||
err = iio_device_claim_direct_mode(iio_dev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
err = st_lsm6dsx_set_full_scale(sensor, val2);
|
||||
break;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
err = st_lsm6dsx_set_odr(sensor, val);
|
||||
break;
|
||||
default:
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
iio_device_release_direct_mode(iio_dev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int st_lsm6dsx_set_watermark(struct iio_dev *iio_dev, unsigned int val)
|
||||
{
|
||||
struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev);
|
||||
struct st_lsm6dsx_hw *hw = sensor->hw;
|
||||
int err, max_fifo_len;
|
||||
|
||||
max_fifo_len = hw->settings->max_fifo_size / ST_LSM6DSX_SAMPLE_SIZE;
|
||||
if (val < 1 || val > max_fifo_len)
|
||||
return -EINVAL;
|
||||
|
||||
err = st_lsm6dsx_update_watermark(sensor, val);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
sensor->watermark = val;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
st_lsm6dsx_sysfs_sampling_frequency_avail(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct st_lsm6dsx_sensor *sensor = iio_priv(dev_get_drvdata(dev));
|
||||
enum st_lsm6dsx_sensor_id id = sensor->id;
|
||||
int i, len = 0;
|
||||
|
||||
for (i = 0; i < ST_LSM6DSX_ODR_LIST_SIZE; i++)
|
||||
len += scnprintf(buf + len, PAGE_SIZE - len, "%d ",
|
||||
st_lsm6dsx_odr_table[id].odr_avl[i].hz);
|
||||
buf[len - 1] = '\n';
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t st_lsm6dsx_sysfs_scale_avail(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct st_lsm6dsx_sensor *sensor = iio_priv(dev_get_drvdata(dev));
|
||||
enum st_lsm6dsx_sensor_id id = sensor->id;
|
||||
int i, len = 0;
|
||||
|
||||
for (i = 0; i < ST_LSM6DSX_FS_LIST_SIZE; i++)
|
||||
len += scnprintf(buf + len, PAGE_SIZE - len, "0.%06u ",
|
||||
st_lsm6dsx_fs_table[id].fs_avl[i].gain);
|
||||
buf[len - 1] = '\n';
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(st_lsm6dsx_sysfs_sampling_frequency_avail);
|
||||
static IIO_DEVICE_ATTR(in_accel_scale_available, 0444,
|
||||
st_lsm6dsx_sysfs_scale_avail, NULL, 0);
|
||||
static IIO_DEVICE_ATTR(in_anglvel_scale_available, 0444,
|
||||
st_lsm6dsx_sysfs_scale_avail, NULL, 0);
|
||||
|
||||
static struct attribute *st_lsm6dsx_acc_attributes[] = {
|
||||
&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
|
||||
&iio_dev_attr_in_accel_scale_available.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group st_lsm6dsx_acc_attribute_group = {
|
||||
.attrs = st_lsm6dsx_acc_attributes,
|
||||
};
|
||||
|
||||
static const struct iio_info st_lsm6dsx_acc_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.attrs = &st_lsm6dsx_acc_attribute_group,
|
||||
.read_raw = st_lsm6dsx_read_raw,
|
||||
.write_raw = st_lsm6dsx_write_raw,
|
||||
.hwfifo_set_watermark = st_lsm6dsx_set_watermark,
|
||||
};
|
||||
|
||||
static struct attribute *st_lsm6dsx_gyro_attributes[] = {
|
||||
&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
|
||||
&iio_dev_attr_in_anglvel_scale_available.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group st_lsm6dsx_gyro_attribute_group = {
|
||||
.attrs = st_lsm6dsx_gyro_attributes,
|
||||
};
|
||||
|
||||
static const struct iio_info st_lsm6dsx_gyro_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.attrs = &st_lsm6dsx_gyro_attribute_group,
|
||||
.read_raw = st_lsm6dsx_read_raw,
|
||||
.write_raw = st_lsm6dsx_write_raw,
|
||||
.hwfifo_set_watermark = st_lsm6dsx_set_watermark,
|
||||
};
|
||||
|
||||
static const unsigned long st_lsm6dsx_available_scan_masks[] = {0x7, 0x0};
|
||||
|
||||
static int st_lsm6dsx_init_device(struct st_lsm6dsx_hw *hw)
|
||||
{
|
||||
int err;
|
||||
u8 data;
|
||||
|
||||
data = ST_LSM6DSX_REG_RESET_MASK;
|
||||
err = hw->tf->write(hw->dev, ST_LSM6DSX_REG_RESET_ADDR, sizeof(data),
|
||||
&data);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
msleep(200);
|
||||
|
||||
/* latch interrupts */
|
||||
err = st_lsm6dsx_write_with_mask(hw, ST_LSM6DSX_REG_LIR_ADDR,
|
||||
ST_LSM6DSX_REG_LIR_MASK, 1);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* enable Block Data Update */
|
||||
err = st_lsm6dsx_write_with_mask(hw, ST_LSM6DSX_REG_BDU_ADDR,
|
||||
ST_LSM6DSX_REG_BDU_MASK, 1);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = st_lsm6dsx_write_with_mask(hw, ST_LSM6DSX_REG_ROUNDING_ADDR,
|
||||
ST_LSM6DSX_REG_ROUNDING_MASK, 1);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* enable FIFO watermak interrupt */
|
||||
err = st_lsm6dsx_write_with_mask(hw, ST_LSM6DSX_REG_INT1_ADDR,
|
||||
ST_LSM6DSX_REG_FIFO_FTH_IRQ_MASK, 1);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* redirect INT2 on INT1 */
|
||||
return st_lsm6dsx_write_with_mask(hw, ST_LSM6DSX_REG_INT2_ON_INT1_ADDR,
|
||||
ST_LSM6DSX_REG_INT2_ON_INT1_MASK, 1);
|
||||
}
|
||||
|
||||
static struct iio_dev *st_lsm6dsx_alloc_iiodev(struct st_lsm6dsx_hw *hw,
|
||||
enum st_lsm6dsx_sensor_id id)
|
||||
{
|
||||
struct st_lsm6dsx_sensor *sensor;
|
||||
struct iio_dev *iio_dev;
|
||||
|
||||
iio_dev = devm_iio_device_alloc(hw->dev, sizeof(*sensor));
|
||||
if (!iio_dev)
|
||||
return NULL;
|
||||
|
||||
iio_dev->modes = INDIO_DIRECT_MODE;
|
||||
iio_dev->dev.parent = hw->dev;
|
||||
iio_dev->available_scan_masks = st_lsm6dsx_available_scan_masks;
|
||||
|
||||
sensor = iio_priv(iio_dev);
|
||||
sensor->id = id;
|
||||
sensor->hw = hw;
|
||||
sensor->odr = st_lsm6dsx_odr_table[id].odr_avl[0].hz;
|
||||
sensor->gain = st_lsm6dsx_fs_table[id].fs_avl[0].gain;
|
||||
sensor->watermark = 1;
|
||||
|
||||
switch (id) {
|
||||
case ST_LSM6DSX_ID_ACC:
|
||||
iio_dev->channels = st_lsm6dsx_acc_channels;
|
||||
iio_dev->num_channels = ARRAY_SIZE(st_lsm6dsx_acc_channels);
|
||||
iio_dev->name = "lsm6dsx_accel";
|
||||
iio_dev->info = &st_lsm6dsx_acc_info;
|
||||
|
||||
sensor->decimator_mask = ST_LSM6DSX_REG_ACC_DEC_MASK;
|
||||
break;
|
||||
case ST_LSM6DSX_ID_GYRO:
|
||||
iio_dev->channels = st_lsm6dsx_gyro_channels;
|
||||
iio_dev->num_channels = ARRAY_SIZE(st_lsm6dsx_gyro_channels);
|
||||
iio_dev->name = "lsm6dsx_gyro";
|
||||
iio_dev->info = &st_lsm6dsx_gyro_info;
|
||||
|
||||
sensor->decimator_mask = ST_LSM6DSX_REG_GYRO_DEC_MASK;
|
||||
break;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return iio_dev;
|
||||
}
|
||||
|
||||
int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id,
|
||||
const struct st_lsm6dsx_transfer_function *tf_ops)
|
||||
{
|
||||
struct st_lsm6dsx_hw *hw;
|
||||
int i, err;
|
||||
|
||||
hw = devm_kzalloc(dev, sizeof(*hw), GFP_KERNEL);
|
||||
if (!hw)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_set_drvdata(dev, (void *)hw);
|
||||
|
||||
mutex_init(&hw->lock);
|
||||
mutex_init(&hw->fifo_lock);
|
||||
|
||||
hw->dev = dev;
|
||||
hw->irq = irq;
|
||||
hw->tf = tf_ops;
|
||||
|
||||
err = st_lsm6dsx_check_whoami(hw, hw_id);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
|
||||
hw->iio_devs[i] = st_lsm6dsx_alloc_iiodev(hw, i);
|
||||
if (!hw->iio_devs[i])
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
err = st_lsm6dsx_init_device(hw);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (hw->irq > 0) {
|
||||
err = st_lsm6dsx_fifo_setup(hw);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
|
||||
err = devm_iio_device_register(hw->dev, hw->iio_devs[i]);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(st_lsm6dsx_probe);
|
||||
|
||||
MODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi@st.com>");
|
||||
MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
|
||||
MODULE_DESCRIPTION("STMicroelectronics st_lsm6dsx driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
* STMicroelectronics st_lsm6dsx i2c driver
|
||||
*
|
||||
* Copyright 2016 STMicroelectronics Inc.
|
||||
*
|
||||
* Lorenzo Bianconi <lorenzo.bianconi@st.com>
|
||||
* Denis Ciocca <denis.ciocca@st.com>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include "st_lsm6dsx.h"
|
||||
|
||||
static int st_lsm6dsx_i2c_read(struct device *dev, u8 addr, int len, u8 *data)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct i2c_msg msg[2];
|
||||
|
||||
msg[0].addr = client->addr;
|
||||
msg[0].flags = client->flags;
|
||||
msg[0].len = 1;
|
||||
msg[0].buf = &addr;
|
||||
|
||||
msg[1].addr = client->addr;
|
||||
msg[1].flags = client->flags | I2C_M_RD;
|
||||
msg[1].len = len;
|
||||
msg[1].buf = data;
|
||||
|
||||
return i2c_transfer(client->adapter, msg, 2);
|
||||
}
|
||||
|
||||
static int st_lsm6dsx_i2c_write(struct device *dev, u8 addr, int len, u8 *data)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct i2c_msg msg;
|
||||
u8 send[len + 1];
|
||||
|
||||
send[0] = addr;
|
||||
memcpy(&send[1], data, len * sizeof(u8));
|
||||
|
||||
msg.addr = client->addr;
|
||||
msg.flags = client->flags;
|
||||
msg.len = len + 1;
|
||||
msg.buf = send;
|
||||
|
||||
return i2c_transfer(client->adapter, &msg, 1);
|
||||
}
|
||||
|
||||
static const struct st_lsm6dsx_transfer_function st_lsm6dsx_transfer_fn = {
|
||||
.read = st_lsm6dsx_i2c_read,
|
||||
.write = st_lsm6dsx_i2c_write,
|
||||
};
|
||||
|
||||
static int st_lsm6dsx_i2c_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
return st_lsm6dsx_probe(&client->dev, client->irq,
|
||||
(int)id->driver_data,
|
||||
&st_lsm6dsx_transfer_fn);
|
||||
}
|
||||
|
||||
static const struct of_device_id st_lsm6dsx_i2c_of_match[] = {
|
||||
{
|
||||
.compatible = "st,lsm6ds3",
|
||||
.data = (void *)ST_LSM6DS3_ID,
|
||||
},
|
||||
{
|
||||
.compatible = "st,lsm6dsm",
|
||||
.data = (void *)ST_LSM6DSM_ID,
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, st_lsm6dsx_i2c_of_match);
|
||||
|
||||
static const struct i2c_device_id st_lsm6dsx_i2c_id_table[] = {
|
||||
{ ST_LSM6DS3_DEV_NAME, ST_LSM6DS3_ID },
|
||||
{ ST_LSM6DSM_DEV_NAME, ST_LSM6DSM_ID },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, st_lsm6dsx_i2c_id_table);
|
||||
|
||||
static struct i2c_driver st_lsm6dsx_driver = {
|
||||
.driver = {
|
||||
.name = "st_lsm6dsx_i2c",
|
||||
.of_match_table = of_match_ptr(st_lsm6dsx_i2c_of_match),
|
||||
},
|
||||
.probe = st_lsm6dsx_i2c_probe,
|
||||
.id_table = st_lsm6dsx_i2c_id_table,
|
||||
};
|
||||
module_i2c_driver(st_lsm6dsx_driver);
|
||||
|
||||
MODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi@st.com>");
|
||||
MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
|
||||
MODULE_DESCRIPTION("STMicroelectronics st_lsm6dsx i2c driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
* STMicroelectronics st_lsm6dsx spi driver
|
||||
*
|
||||
* Copyright 2016 STMicroelectronics Inc.
|
||||
*
|
||||
* Lorenzo Bianconi <lorenzo.bianconi@st.com>
|
||||
* Denis Ciocca <denis.ciocca@st.com>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include "st_lsm6dsx.h"
|
||||
|
||||
#define SENSORS_SPI_READ BIT(7)
|
||||
|
||||
static int st_lsm6dsx_spi_read(struct device *dev, u8 addr, int len,
|
||||
u8 *data)
|
||||
{
|
||||
struct spi_device *spi = to_spi_device(dev);
|
||||
struct st_lsm6dsx_hw *hw = spi_get_drvdata(spi);
|
||||
int err;
|
||||
|
||||
struct spi_transfer xfers[] = {
|
||||
{
|
||||
.tx_buf = hw->tb.tx_buf,
|
||||
.bits_per_word = 8,
|
||||
.len = 1,
|
||||
},
|
||||
{
|
||||
.rx_buf = hw->tb.rx_buf,
|
||||
.bits_per_word = 8,
|
||||
.len = len,
|
||||
}
|
||||
};
|
||||
|
||||
hw->tb.tx_buf[0] = addr | SENSORS_SPI_READ;
|
||||
|
||||
err = spi_sync_transfer(spi, xfers, ARRAY_SIZE(xfers));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
memcpy(data, hw->tb.rx_buf, len * sizeof(u8));
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int st_lsm6dsx_spi_write(struct device *dev, u8 addr, int len,
|
||||
u8 *data)
|
||||
{
|
||||
struct st_lsm6dsx_hw *hw;
|
||||
struct spi_device *spi;
|
||||
|
||||
if (len >= ST_LSM6DSX_TX_MAX_LENGTH)
|
||||
return -ENOMEM;
|
||||
|
||||
spi = to_spi_device(dev);
|
||||
hw = spi_get_drvdata(spi);
|
||||
|
||||
hw->tb.tx_buf[0] = addr;
|
||||
memcpy(&hw->tb.tx_buf[1], data, len);
|
||||
|
||||
return spi_write(spi, hw->tb.tx_buf, len + 1);
|
||||
}
|
||||
|
||||
static const struct st_lsm6dsx_transfer_function st_lsm6dsx_transfer_fn = {
|
||||
.read = st_lsm6dsx_spi_read,
|
||||
.write = st_lsm6dsx_spi_write,
|
||||
};
|
||||
|
||||
static int st_lsm6dsx_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
const struct spi_device_id *id = spi_get_device_id(spi);
|
||||
|
||||
return st_lsm6dsx_probe(&spi->dev, spi->irq,
|
||||
(int)id->driver_data,
|
||||
&st_lsm6dsx_transfer_fn);
|
||||
}
|
||||
|
||||
static const struct of_device_id st_lsm6dsx_spi_of_match[] = {
|
||||
{
|
||||
.compatible = "st,lsm6ds3",
|
||||
.data = (void *)ST_LSM6DS3_ID,
|
||||
},
|
||||
{
|
||||
.compatible = "st,lsm6dsm",
|
||||
.data = (void *)ST_LSM6DSM_ID,
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, st_lsm6dsx_spi_of_match);
|
||||
|
||||
static const struct spi_device_id st_lsm6dsx_spi_id_table[] = {
|
||||
{ ST_LSM6DS3_DEV_NAME, ST_LSM6DS3_ID },
|
||||
{ ST_LSM6DSM_DEV_NAME, ST_LSM6DSM_ID },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, st_lsm6dsx_spi_id_table);
|
||||
|
||||
static struct spi_driver st_lsm6dsx_driver = {
|
||||
.driver = {
|
||||
.name = "st_lsm6dsx_spi",
|
||||
.of_match_table = of_match_ptr(st_lsm6dsx_spi_of_match),
|
||||
},
|
||||
.probe = st_lsm6dsx_spi_probe,
|
||||
.id_table = st_lsm6dsx_spi_id_table,
|
||||
};
|
||||
module_spi_driver(st_lsm6dsx_driver);
|
||||
|
||||
MODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi@st.com>");
|
||||
MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
|
||||
MODULE_DESCRIPTION("STMicroelectronics st_lsm6dsx spi driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -147,8 +147,7 @@ static struct iio_trigger *__iio_trigger_find_by_name(const char *name)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static struct iio_trigger *iio_trigger_find_by_name(const char *name,
|
||||
size_t len)
|
||||
static struct iio_trigger *iio_trigger_acquire_by_name(const char *name)
|
||||
{
|
||||
struct iio_trigger *trig = NULL, *iter;
|
||||
|
||||
|
@ -156,6 +155,7 @@ static struct iio_trigger *iio_trigger_find_by_name(const char *name,
|
|||
list_for_each_entry(iter, &iio_trigger_list, list)
|
||||
if (sysfs_streq(iter->name, name)) {
|
||||
trig = iter;
|
||||
iio_trigger_get(trig);
|
||||
break;
|
||||
}
|
||||
mutex_unlock(&iio_trigger_list_lock);
|
||||
|
@ -416,20 +416,22 @@ static ssize_t iio_trigger_write_current(struct device *dev,
|
|||
}
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
trig = iio_trigger_find_by_name(buf, len);
|
||||
if (oldtrig == trig)
|
||||
return len;
|
||||
trig = iio_trigger_acquire_by_name(buf);
|
||||
if (oldtrig == trig) {
|
||||
ret = len;
|
||||
goto out_trigger_put;
|
||||
}
|
||||
|
||||
if (trig && indio_dev->info->validate_trigger) {
|
||||
ret = indio_dev->info->validate_trigger(indio_dev, trig);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto out_trigger_put;
|
||||
}
|
||||
|
||||
if (trig && trig->ops->validate_device) {
|
||||
ret = trig->ops->validate_device(trig, indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto out_trigger_put;
|
||||
}
|
||||
|
||||
indio_dev->trig = trig;
|
||||
|
@ -441,13 +443,16 @@ static ssize_t iio_trigger_write_current(struct device *dev,
|
|||
iio_trigger_put(oldtrig);
|
||||
}
|
||||
if (indio_dev->trig) {
|
||||
iio_trigger_get(indio_dev->trig);
|
||||
if (indio_dev->modes & INDIO_EVENT_TRIGGERED)
|
||||
iio_trigger_attach_poll_func(indio_dev->trig,
|
||||
indio_dev->pollfunc_event);
|
||||
}
|
||||
|
||||
return len;
|
||||
|
||||
out_trigger_put:
|
||||
iio_trigger_put(trig);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(current_trigger, S_IRUGO | S_IWUSR,
|
||||
|
@ -487,7 +492,7 @@ static void iio_trig_release(struct device *device)
|
|||
kfree(trig);
|
||||
}
|
||||
|
||||
static struct device_type iio_trig_type = {
|
||||
static const struct device_type iio_trig_type = {
|
||||
.release = iio_trig_release,
|
||||
.groups = iio_trig_dev_groups,
|
||||
};
|
||||
|
@ -513,46 +518,45 @@ static void iio_trig_subirqunmask(struct irq_data *d)
|
|||
static struct iio_trigger *viio_trigger_alloc(const char *fmt, va_list vargs)
|
||||
{
|
||||
struct iio_trigger *trig;
|
||||
int i;
|
||||
|
||||
trig = kzalloc(sizeof *trig, GFP_KERNEL);
|
||||
if (trig) {
|
||||
int i;
|
||||
trig->dev.type = &iio_trig_type;
|
||||
trig->dev.bus = &iio_bus_type;
|
||||
device_initialize(&trig->dev);
|
||||
if (!trig)
|
||||
return NULL;
|
||||
|
||||
mutex_init(&trig->pool_lock);
|
||||
trig->subirq_base
|
||||
= irq_alloc_descs(-1, 0,
|
||||
CONFIG_IIO_CONSUMERS_PER_TRIGGER,
|
||||
0);
|
||||
if (trig->subirq_base < 0) {
|
||||
kfree(trig);
|
||||
return NULL;
|
||||
}
|
||||
trig->dev.type = &iio_trig_type;
|
||||
trig->dev.bus = &iio_bus_type;
|
||||
device_initialize(&trig->dev);
|
||||
|
||||
trig->name = kvasprintf(GFP_KERNEL, fmt, vargs);
|
||||
if (trig->name == NULL) {
|
||||
irq_free_descs(trig->subirq_base,
|
||||
CONFIG_IIO_CONSUMERS_PER_TRIGGER);
|
||||
kfree(trig);
|
||||
return NULL;
|
||||
}
|
||||
trig->subirq_chip.name = trig->name;
|
||||
trig->subirq_chip.irq_mask = &iio_trig_subirqmask;
|
||||
trig->subirq_chip.irq_unmask = &iio_trig_subirqunmask;
|
||||
for (i = 0; i < CONFIG_IIO_CONSUMERS_PER_TRIGGER; i++) {
|
||||
irq_set_chip(trig->subirq_base + i,
|
||||
&trig->subirq_chip);
|
||||
irq_set_handler(trig->subirq_base + i,
|
||||
&handle_simple_irq);
|
||||
irq_modify_status(trig->subirq_base + i,
|
||||
IRQ_NOREQUEST | IRQ_NOAUTOEN,
|
||||
IRQ_NOPROBE);
|
||||
}
|
||||
get_device(&trig->dev);
|
||||
mutex_init(&trig->pool_lock);
|
||||
trig->subirq_base = irq_alloc_descs(-1, 0,
|
||||
CONFIG_IIO_CONSUMERS_PER_TRIGGER,
|
||||
0);
|
||||
if (trig->subirq_base < 0)
|
||||
goto free_trig;
|
||||
|
||||
trig->name = kvasprintf(GFP_KERNEL, fmt, vargs);
|
||||
if (trig->name == NULL)
|
||||
goto free_descs;
|
||||
|
||||
trig->subirq_chip.name = trig->name;
|
||||
trig->subirq_chip.irq_mask = &iio_trig_subirqmask;
|
||||
trig->subirq_chip.irq_unmask = &iio_trig_subirqunmask;
|
||||
for (i = 0; i < CONFIG_IIO_CONSUMERS_PER_TRIGGER; i++) {
|
||||
irq_set_chip(trig->subirq_base + i, &trig->subirq_chip);
|
||||
irq_set_handler(trig->subirq_base + i, &handle_simple_irq);
|
||||
irq_modify_status(trig->subirq_base + i,
|
||||
IRQ_NOREQUEST | IRQ_NOAUTOEN, IRQ_NOPROBE);
|
||||
}
|
||||
get_device(&trig->dev);
|
||||
|
||||
return trig;
|
||||
|
||||
free_descs:
|
||||
irq_free_descs(trig->subirq_base, CONFIG_IIO_CONSUMERS_PER_TRIGGER);
|
||||
free_trig:
|
||||
kfree(trig);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct iio_trigger *iio_trigger_alloc(const char *fmt, ...)
|
||||
|
|
|
@ -601,8 +601,14 @@ static int iio_convert_raw_to_processed_unlocked(struct iio_channel *chan,
|
|||
|
||||
scale_type = iio_channel_read(chan, &scale_val, &scale_val2,
|
||||
IIO_CHAN_INFO_SCALE);
|
||||
if (scale_type < 0)
|
||||
return scale_type;
|
||||
if (scale_type < 0) {
|
||||
/*
|
||||
* Just pass raw values as processed if no scaling is
|
||||
* available.
|
||||
*/
|
||||
*processed = raw;
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (scale_type) {
|
||||
case IIO_VAL_INT:
|
||||
|
|
|
@ -278,7 +278,7 @@ static int cm3605_remove(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int cm3605_pm_suspend(struct device *dev)
|
||||
static int __maybe_unused cm3605_pm_suspend(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct cm3605 *cm3605 = iio_priv(indio_dev);
|
||||
|
@ -289,7 +289,7 @@ static int cm3605_pm_suspend(struct device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int cm3605_pm_resume(struct device *dev)
|
||||
static int __maybe_unused cm3605_pm_resume(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct cm3605 *cm3605 = iio_priv(indio_dev);
|
||||
|
|
|
@ -840,6 +840,7 @@ static const struct of_device_id opt3001_of_match[] = {
|
|||
{ .compatible = "ti,opt3001" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, opt3001_of_match);
|
||||
|
||||
static struct i2c_driver opt3001_driver = {
|
||||
.probe = opt3001_probe,
|
||||
|
|
|
@ -278,13 +278,9 @@ static int ak8974_await_drdy(struct ak8974 *ak8974)
|
|||
if (val & AK8974_STATUS_DRDY)
|
||||
return 0;
|
||||
} while (--timeout);
|
||||
if (!timeout) {
|
||||
dev_err(&ak8974->i2c->dev,
|
||||
"timeout waiting for DRDY\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
dev_err(&ak8974->i2c->dev, "timeout waiting for DRDY\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static int ak8974_getresult(struct ak8974 *ak8974, __le16 *result)
|
||||
|
|
|
@ -222,29 +222,39 @@ static int mag3110_write_raw(struct iio_dev *indio_dev,
|
|||
int val, int val2, long mask)
|
||||
{
|
||||
struct mag3110_data *data = iio_priv(indio_dev);
|
||||
int rate;
|
||||
int rate, ret;
|
||||
|
||||
if (iio_buffer_enabled(indio_dev))
|
||||
return -EBUSY;
|
||||
ret = iio_device_claim_direct_mode(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
rate = mag3110_get_samp_freq_index(data, val, val2);
|
||||
if (rate < 0)
|
||||
return -EINVAL;
|
||||
if (rate < 0) {
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
data->ctrl_reg1 &= ~MAG3110_CTRL_DR_MASK;
|
||||
data->ctrl_reg1 |= rate << MAG3110_CTRL_DR_SHIFT;
|
||||
return i2c_smbus_write_byte_data(data->client,
|
||||
ret = i2c_smbus_write_byte_data(data->client,
|
||||
MAG3110_CTRL_REG1, data->ctrl_reg1);
|
||||
break;
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
if (val < -10000 || val > 10000)
|
||||
return -EINVAL;
|
||||
return i2c_smbus_write_word_swapped(data->client,
|
||||
if (val < -10000 || val > 10000) {
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
ret = i2c_smbus_write_word_swapped(data->client,
|
||||
MAG3110_OFF_X + 2 * chan->scan_index, val << 1);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static irqreturn_t mag3110_trigger_handler(int irq, void *p)
|
||||
|
|
|
@ -284,6 +284,7 @@ static const struct of_device_id mcp4531_of_match[] = {
|
|||
MCP4531_COMPATIBLE("microchip,mcp4662-104", MCP466x_104),
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mcp4531_of_match);
|
||||
#endif
|
||||
|
||||
static int mcp4531_probe(struct i2c_client *client,
|
||||
|
|
|
@ -308,6 +308,7 @@ static int ms5611_write_raw(struct iio_dev *indio_dev,
|
|||
{
|
||||
struct ms5611_state *st = iio_priv(indio_dev);
|
||||
const struct ms5611_osr *osr = NULL;
|
||||
int ret;
|
||||
|
||||
if (mask != IIO_CHAN_INFO_OVERSAMPLING_RATIO)
|
||||
return -EINVAL;
|
||||
|
@ -321,12 +322,11 @@ static int ms5611_write_raw(struct iio_dev *indio_dev,
|
|||
if (!osr)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&st->lock);
|
||||
ret = iio_device_claim_direct_mode(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (iio_buffer_enabled(indio_dev)) {
|
||||
mutex_unlock(&st->lock);
|
||||
return -EBUSY;
|
||||
}
|
||||
mutex_lock(&st->lock);
|
||||
|
||||
if (chan->type == IIO_TEMP)
|
||||
st->temp_osr = osr;
|
||||
|
@ -334,6 +334,8 @@ static int ms5611_write_raw(struct iio_dev *indio_dev,
|
|||
st->pressure_osr = osr;
|
||||
|
||||
mutex_unlock(&st->lock);
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -387,14 +387,18 @@ static int sx9500_read_raw(struct iio_dev *indio_dev,
|
|||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct sx9500_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (chan->type) {
|
||||
case IIO_PROXIMITY:
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (iio_buffer_enabled(indio_dev))
|
||||
return -EBUSY;
|
||||
return sx9500_read_proximity(data, chan, val);
|
||||
ret = iio_device_claim_direct_mode(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = sx9500_read_proximity(data, chan, val);
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
return ret;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
return sx9500_read_samp_freq(data, val, val2);
|
||||
default:
|
||||
|
|
|
@ -39,6 +39,16 @@ config TMP006
|
|||
This driver can also be built as a module. If so, the module will
|
||||
be called tmp006.
|
||||
|
||||
config TMP007
|
||||
tristate "TMP007 infrared thermopile sensor with Integrated Math Engine"
|
||||
depends on I2C
|
||||
help
|
||||
If you say yes here you get support for the Texas Instruments
|
||||
TMP007 infrared thermopile sensor with Integrated Math Engine.
|
||||
|
||||
This driver can also be built as a module. If so, the module will
|
||||
be called tmp007.
|
||||
|
||||
config TSYS01
|
||||
tristate "Measurement Specialties TSYS01 temperature sensor using I2C bus connection"
|
||||
depends on I2C
|
||||
|
|
|
@ -5,5 +5,6 @@
|
|||
obj-$(CONFIG_MAXIM_THERMOCOUPLE) += maxim_thermocouple.o
|
||||
obj-$(CONFIG_MLX90614) += mlx90614.o
|
||||
obj-$(CONFIG_TMP006) += tmp006.o
|
||||
obj-$(CONFIG_TMP007) += tmp007.o
|
||||
obj-$(CONFIG_TSYS01) += tsys01.o
|
||||
obj-$(CONFIG_TSYS02D) += tsys02d.o
|
||||
|
|
|
@ -0,0 +1,345 @@
|
|||
/*
|
||||
* tmp007.c - Support for TI TMP007 IR thermopile sensor with integrated math engine
|
||||
*
|
||||
* Copyright (c) 2017 Manivannan Sadhasivam <manivannanece23@gmail.com>
|
||||
*
|
||||
* This file is subject to the terms and conditions of version 2 of
|
||||
* the GNU General Public License. See the file COPYING in the main
|
||||
* directory of this archive for more details.
|
||||
*
|
||||
* Driver for the Texas Instruments I2C 16-bit IR thermopile sensor
|
||||
*
|
||||
* (7-bit I2C slave address (0x40 - 0x47), changeable via ADR pins)
|
||||
*
|
||||
* Note: This driver assumes that the sensor has been calibrated beforehand
|
||||
*
|
||||
* TODO: ALERT irq, limit threshold events
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
#define TMP007_TDIE 0x01
|
||||
#define TMP007_CONFIG 0x02
|
||||
#define TMP007_TOBJECT 0x03
|
||||
#define TMP007_STATUS 0x04
|
||||
#define TMP007_STATUS_MASK 0x05
|
||||
#define TMP007_MANUFACTURER_ID 0x1e
|
||||
#define TMP007_DEVICE_ID 0x1f
|
||||
|
||||
#define TMP007_CONFIG_CONV_EN BIT(12)
|
||||
#define TMP007_CONFIG_COMP_EN BIT(5)
|
||||
#define TMP007_CONFIG_TC_EN BIT(6)
|
||||
#define TMP007_CONFIG_CR_MASK GENMASK(11, 9)
|
||||
#define TMP007_CONFIG_CR_SHIFT 9
|
||||
|
||||
#define TMP007_STATUS_CONV_READY BIT(14)
|
||||
#define TMP007_STATUS_DATA_VALID BIT(9)
|
||||
|
||||
#define TMP007_MANUFACTURER_MAGIC 0x5449
|
||||
#define TMP007_DEVICE_MAGIC 0x0078
|
||||
|
||||
#define TMP007_TEMP_SHIFT 2
|
||||
|
||||
struct tmp007_data {
|
||||
struct i2c_client *client;
|
||||
u16 config;
|
||||
};
|
||||
|
||||
static const int tmp007_avgs[5][2] = { {4, 0}, {2, 0}, {1, 0},
|
||||
{0, 500000}, {0, 250000} };
|
||||
|
||||
static int tmp007_read_temperature(struct tmp007_data *data, u8 reg)
|
||||
{
|
||||
s32 ret;
|
||||
int tries = 50;
|
||||
|
||||
while (tries-- > 0) {
|
||||
ret = i2c_smbus_read_word_swapped(data->client,
|
||||
TMP007_STATUS);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if ((ret & TMP007_STATUS_CONV_READY) &&
|
||||
!(ret & TMP007_STATUS_DATA_VALID))
|
||||
break;
|
||||
msleep(100);
|
||||
}
|
||||
|
||||
if (tries < 0)
|
||||
return -EIO;
|
||||
|
||||
return i2c_smbus_read_word_swapped(data->client, reg);
|
||||
}
|
||||
|
||||
static int tmp007_powerdown(struct tmp007_data *data)
|
||||
{
|
||||
return i2c_smbus_write_word_swapped(data->client, TMP007_CONFIG,
|
||||
data->config & ~TMP007_CONFIG_CONV_EN);
|
||||
}
|
||||
|
||||
static int tmp007_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *channel, int *val,
|
||||
int *val2, long mask)
|
||||
{
|
||||
struct tmp007_data *data = iio_priv(indio_dev);
|
||||
s32 ret;
|
||||
int conv_rate;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
switch (channel->channel2) {
|
||||
case IIO_MOD_TEMP_AMBIENT: /* LSB: 0.03125 degree Celsius */
|
||||
ret = i2c_smbus_read_word_swapped(data->client, TMP007_TDIE);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
break;
|
||||
case IIO_MOD_TEMP_OBJECT:
|
||||
ret = tmp007_read_temperature(data, TMP007_TOBJECT);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*val = sign_extend32(ret, 15) >> TMP007_TEMP_SHIFT;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = 31;
|
||||
*val2 = 250000;
|
||||
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
conv_rate = (data->config & TMP007_CONFIG_CR_MASK)
|
||||
>> TMP007_CONFIG_CR_SHIFT;
|
||||
*val = tmp007_avgs[conv_rate][0];
|
||||
*val2 = tmp007_avgs[conv_rate][1];
|
||||
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int tmp007_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *channel, int val,
|
||||
int val2, long mask)
|
||||
{
|
||||
struct tmp007_data *data = iio_priv(indio_dev);
|
||||
int i;
|
||||
u16 tmp;
|
||||
|
||||
if (mask == IIO_CHAN_INFO_SAMP_FREQ) {
|
||||
for (i = 0; i < ARRAY_SIZE(tmp007_avgs); i++) {
|
||||
if ((val == tmp007_avgs[i][0]) &&
|
||||
(val2 == tmp007_avgs[i][1])) {
|
||||
tmp = data->config & ~TMP007_CONFIG_CR_MASK;
|
||||
tmp |= (i << TMP007_CONFIG_CR_SHIFT);
|
||||
|
||||
return i2c_smbus_write_word_swapped(data->client,
|
||||
TMP007_CONFIG,
|
||||
data->config = tmp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static IIO_CONST_ATTR(sampling_frequency_available, "4 2 1 0.5 0.25");
|
||||
|
||||
static struct attribute *tmp007_attributes[] = {
|
||||
&iio_const_attr_sampling_frequency_available.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group tmp007_attribute_group = {
|
||||
.attrs = tmp007_attributes,
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec tmp007_channels[] = {
|
||||
{
|
||||
.type = IIO_TEMP,
|
||||
.modified = 1,
|
||||
.channel2 = IIO_MOD_TEMP_AMBIENT,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
|
||||
},
|
||||
{
|
||||
.type = IIO_TEMP,
|
||||
.modified = 1,
|
||||
.channel2 = IIO_MOD_TEMP_OBJECT,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
|
||||
}
|
||||
};
|
||||
|
||||
static const struct iio_info tmp007_info = {
|
||||
.read_raw = tmp007_read_raw,
|
||||
.write_raw = tmp007_write_raw,
|
||||
.attrs = &tmp007_attribute_group,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static bool tmp007_identify(struct i2c_client *client)
|
||||
{
|
||||
int manf_id, dev_id;
|
||||
|
||||
manf_id = i2c_smbus_read_word_swapped(client, TMP007_MANUFACTURER_ID);
|
||||
if (manf_id < 0)
|
||||
return false;
|
||||
|
||||
dev_id = i2c_smbus_read_word_swapped(client, TMP007_DEVICE_ID);
|
||||
if (dev_id < 0)
|
||||
return false;
|
||||
|
||||
return (manf_id == TMP007_MANUFACTURER_MAGIC && dev_id == TMP007_DEVICE_MAGIC);
|
||||
}
|
||||
|
||||
static int tmp007_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *tmp007_id)
|
||||
{
|
||||
struct tmp007_data *data;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret;
|
||||
u16 status;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (!tmp007_identify(client)) {
|
||||
dev_err(&client->dev, "TMP007 not found\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
data = iio_priv(indio_dev);
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
data->client = client;
|
||||
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->name = dev_name(&client->dev);
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &tmp007_info;
|
||||
|
||||
indio_dev->channels = tmp007_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(tmp007_channels);
|
||||
|
||||
/*
|
||||
* Set Configuration register:
|
||||
* 1. Conversion ON
|
||||
* 2. Comparator mode
|
||||
* 3. Transient correction enable
|
||||
*/
|
||||
|
||||
ret = i2c_smbus_read_word_swapped(data->client, TMP007_CONFIG);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
data->config = ret;
|
||||
data->config |= (TMP007_CONFIG_CONV_EN | TMP007_CONFIG_COMP_EN | TMP007_CONFIG_TC_EN);
|
||||
|
||||
ret = i2c_smbus_write_word_swapped(data->client, TMP007_CONFIG,
|
||||
data->config);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Set Status Mask register:
|
||||
* 1. Conversion ready enable
|
||||
* 2. Data valid enable
|
||||
*/
|
||||
|
||||
ret = i2c_smbus_read_word_swapped(data->client, TMP007_STATUS_MASK);
|
||||
if (ret < 0)
|
||||
goto error_powerdown;
|
||||
|
||||
status = ret;
|
||||
status |= (TMP007_STATUS_CONV_READY | TMP007_STATUS_DATA_VALID);
|
||||
|
||||
ret = i2c_smbus_write_word_swapped(data->client, TMP007_STATUS_MASK, status);
|
||||
if (ret < 0)
|
||||
goto error_powerdown;
|
||||
|
||||
return iio_device_register(indio_dev);
|
||||
|
||||
error_powerdown:
|
||||
tmp007_powerdown(data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tmp007_remove(struct i2c_client *client)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
struct tmp007_data *data = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
tmp007_powerdown(data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int tmp007_suspend(struct device *dev)
|
||||
{
|
||||
struct tmp007_data *data = iio_priv(i2c_get_clientdata(
|
||||
to_i2c_client(dev)));
|
||||
|
||||
return tmp007_powerdown(data);
|
||||
}
|
||||
|
||||
static int tmp007_resume(struct device *dev)
|
||||
{
|
||||
struct tmp007_data *data = iio_priv(i2c_get_clientdata(
|
||||
to_i2c_client(dev)));
|
||||
|
||||
return i2c_smbus_write_word_swapped(data->client, TMP007_CONFIG,
|
||||
data->config | TMP007_CONFIG_CONV_EN);
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(tmp007_pm_ops, tmp007_suspend, tmp007_resume);
|
||||
|
||||
static const struct of_device_id tmp007_of_match[] = {
|
||||
{ .compatible = "ti,tmp007", },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, tmp007_of_match);
|
||||
|
||||
static const struct i2c_device_id tmp007_id[] = {
|
||||
{ "tmp007", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, tmp007_id);
|
||||
|
||||
static struct i2c_driver tmp007_driver = {
|
||||
.driver = {
|
||||
.name = "tmp007",
|
||||
.of_match_table = of_match_ptr(tmp007_of_match),
|
||||
.pm = &tmp007_pm_ops,
|
||||
},
|
||||
.probe = tmp007_probe,
|
||||
.remove = tmp007_remove,
|
||||
.id_table = tmp007_id,
|
||||
};
|
||||
module_i2c_driver(tmp007_driver);
|
||||
|
||||
MODULE_AUTHOR("Manivannan Sadhasivam <manivannanece23@gmail.com>");
|
||||
MODULE_DESCRIPTION("TI TMP007 IR thermopile sensor driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -58,7 +58,7 @@ static int iio_interrupt_trigger_probe(struct platform_device *pdev)
|
|||
trig_info = kzalloc(sizeof(*trig_info), GFP_KERNEL);
|
||||
if (!trig_info) {
|
||||
ret = -ENOMEM;
|
||||
goto error_put_trigger;
|
||||
goto error_free_trigger;
|
||||
}
|
||||
iio_trigger_set_drvdata(trig, trig_info);
|
||||
trig_info->irq = irq;
|
||||
|
@ -83,8 +83,8 @@ error_release_irq:
|
|||
free_irq(irq, trig);
|
||||
error_free_trig_info:
|
||||
kfree(trig_info);
|
||||
error_put_trigger:
|
||||
iio_trigger_put(trig);
|
||||
error_free_trigger:
|
||||
iio_trigger_free(trig);
|
||||
error_ret:
|
||||
return ret;
|
||||
}
|
||||
|
@ -99,7 +99,7 @@ static int iio_interrupt_trigger_remove(struct platform_device *pdev)
|
|||
iio_trigger_unregister(trig);
|
||||
free_irq(trig_info->irq, trig);
|
||||
kfree(trig_info);
|
||||
iio_trigger_put(trig);
|
||||
iio_trigger_free(trig);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -174,7 +174,7 @@ static int iio_sysfs_trigger_probe(int id)
|
|||
return 0;
|
||||
|
||||
out2:
|
||||
iio_trigger_put(t->trig);
|
||||
iio_trigger_free(t->trig);
|
||||
free_t:
|
||||
kfree(t);
|
||||
out1:
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include <linux/regmap.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#define ISL29028_CONV_TIME_MS 100
|
||||
|
||||
|
@ -60,6 +61,8 @@
|
|||
|
||||
#define ISL29028_NUM_REGS (ISL29028_REG_TEST2_MODE + 1)
|
||||
|
||||
#define ISL29028_POWER_OFF_DELAY_MS 2000
|
||||
|
||||
enum isl29028_als_ir_mode {
|
||||
ISL29028_MODE_NONE = 0,
|
||||
ISL29028_MODE_ALS,
|
||||
|
@ -67,66 +70,94 @@ enum isl29028_als_ir_mode {
|
|||
};
|
||||
|
||||
struct isl29028_chip {
|
||||
struct mutex lock;
|
||||
struct regmap *regmap;
|
||||
|
||||
unsigned int prox_sampling;
|
||||
bool enable_prox;
|
||||
|
||||
int lux_scale;
|
||||
struct mutex lock;
|
||||
struct regmap *regmap;
|
||||
unsigned int prox_sampling;
|
||||
bool enable_prox;
|
||||
int lux_scale;
|
||||
enum isl29028_als_ir_mode als_ir_mode;
|
||||
};
|
||||
|
||||
static int isl29028_set_proxim_sampling(struct isl29028_chip *chip,
|
||||
unsigned int sampling)
|
||||
{
|
||||
struct device *dev = regmap_get_device(chip->regmap);
|
||||
static unsigned int prox_period[] = {800, 400, 200, 100, 75, 50, 12, 0};
|
||||
int sel;
|
||||
unsigned int period = DIV_ROUND_UP(1000, sampling);
|
||||
int sel, ret;
|
||||
|
||||
for (sel = 0; sel < ARRAY_SIZE(prox_period); ++sel) {
|
||||
if (period >= prox_period[sel])
|
||||
break;
|
||||
}
|
||||
return regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE,
|
||||
ISL29028_CONF_PROX_SLP_MASK,
|
||||
sel << ISL29028_CONF_PROX_SLP_SH);
|
||||
|
||||
ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE,
|
||||
ISL29028_CONF_PROX_SLP_MASK,
|
||||
sel << ISL29028_CONF_PROX_SLP_SH);
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "%s(): Error %d setting the proximity sampling\n",
|
||||
__func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
chip->prox_sampling = sampling;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int isl29028_enable_proximity(struct isl29028_chip *chip, bool enable)
|
||||
static int isl29028_enable_proximity(struct isl29028_chip *chip)
|
||||
{
|
||||
int ret;
|
||||
int val = 0;
|
||||
|
||||
if (enable)
|
||||
val = ISL29028_CONF_PROX_EN;
|
||||
ret = isl29028_set_proxim_sampling(chip, chip->prox_sampling);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE,
|
||||
ISL29028_CONF_PROX_EN_MASK, val);
|
||||
ISL29028_CONF_PROX_EN_MASK,
|
||||
ISL29028_CONF_PROX_EN);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Wait for conversion to be complete for first sample */
|
||||
mdelay(DIV_ROUND_UP(1000, chip->prox_sampling));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int isl29028_set_als_scale(struct isl29028_chip *chip, int lux_scale)
|
||||
{
|
||||
struct device *dev = regmap_get_device(chip->regmap);
|
||||
int val = (lux_scale == 2000) ? ISL29028_CONF_ALS_RANGE_HIGH_LUX :
|
||||
ISL29028_CONF_ALS_RANGE_LOW_LUX;
|
||||
int ret;
|
||||
|
||||
return regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE,
|
||||
ISL29028_CONF_ALS_RANGE_MASK, val);
|
||||
ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE,
|
||||
ISL29028_CONF_ALS_RANGE_MASK, val);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "%s(): Error %d setting the ALS scale\n", __func__,
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
chip->lux_scale = lux_scale;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int isl29028_set_als_ir_mode(struct isl29028_chip *chip,
|
||||
enum isl29028_als_ir_mode mode)
|
||||
{
|
||||
int ret = 0;
|
||||
int ret;
|
||||
|
||||
if (chip->als_ir_mode == mode)
|
||||
return 0;
|
||||
|
||||
ret = isl29028_set_als_scale(chip, chip->lux_scale);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
switch (mode) {
|
||||
case ISL29028_MODE_ALS:
|
||||
ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE,
|
||||
|
@ -139,16 +170,15 @@ static int isl29028_set_als_ir_mode(struct isl29028_chip *chip,
|
|||
ISL29028_CONF_ALS_RANGE_MASK,
|
||||
ISL29028_CONF_ALS_RANGE_HIGH_LUX);
|
||||
break;
|
||||
|
||||
case ISL29028_MODE_IR:
|
||||
ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE,
|
||||
ISL29028_CONF_ALS_IR_MODE_MASK,
|
||||
ISL29028_CONF_ALS_IR_MODE_IR);
|
||||
break;
|
||||
|
||||
case ISL29028_MODE_NONE:
|
||||
return regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE,
|
||||
ISL29028_CONF_ALS_EN_MASK, ISL29028_CONF_ALS_DIS);
|
||||
ISL29028_CONF_ALS_EN_MASK,
|
||||
ISL29028_CONF_ALS_DIS);
|
||||
}
|
||||
|
||||
if (ret < 0)
|
||||
|
@ -179,18 +209,21 @@ static int isl29028_read_als_ir(struct isl29028_chip *chip, int *als_ir)
|
|||
ret = regmap_read(chip->regmap, ISL29028_REG_ALSIR_L, &lsb);
|
||||
if (ret < 0) {
|
||||
dev_err(dev,
|
||||
"Error in reading register ALSIR_L err %d\n", ret);
|
||||
"%s(): Error %d reading register ALSIR_L\n",
|
||||
__func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regmap_read(chip->regmap, ISL29028_REG_ALSIR_U, &msb);
|
||||
if (ret < 0) {
|
||||
dev_err(dev,
|
||||
"Error in reading register ALSIR_U err %d\n", ret);
|
||||
"%s(): Error %d reading register ALSIR_U\n",
|
||||
__func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*als_ir = ((msb & 0xF) << 8) | (lsb & 0xFF);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -200,27 +233,24 @@ static int isl29028_read_proxim(struct isl29028_chip *chip, int *prox)
|
|||
unsigned int data;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(chip->regmap, ISL29028_REG_PROX_DATA, &data);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Error in reading register %d, error %d\n",
|
||||
ISL29028_REG_PROX_DATA, ret);
|
||||
return ret;
|
||||
}
|
||||
*prox = data;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int isl29028_proxim_get(struct isl29028_chip *chip, int *prox_data)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!chip->enable_prox) {
|
||||
ret = isl29028_enable_proximity(chip, true);
|
||||
ret = isl29028_enable_proximity(chip);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
chip->enable_prox = true;
|
||||
}
|
||||
return isl29028_read_proxim(chip, prox_data);
|
||||
|
||||
ret = regmap_read(chip->regmap, ISL29028_REG_PROX_DATA, &data);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "%s(): Error %d reading register PROX_DATA\n",
|
||||
__func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*prox = data;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int isl29028_als_get(struct isl29028_chip *chip, int *als_data)
|
||||
|
@ -231,7 +261,8 @@ static int isl29028_als_get(struct isl29028_chip *chip, int *als_data)
|
|||
|
||||
ret = isl29028_set_als_ir_mode(chip, ISL29028_MODE_ALS);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Error in enabling ALS mode err %d\n", ret);
|
||||
dev_err(dev, "%s(): Error %d enabling ALS mode\n", __func__,
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -250,6 +281,7 @@ static int isl29028_als_get(struct isl29028_chip *chip, int *als_data)
|
|||
als_ir_data = (als_ir_data * 49) / 100;
|
||||
|
||||
*als_data = als_ir_data;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -260,12 +292,31 @@ static int isl29028_ir_get(struct isl29028_chip *chip, int *ir_data)
|
|||
|
||||
ret = isl29028_set_als_ir_mode(chip, ISL29028_MODE_IR);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Error in enabling IR mode err %d\n", ret);
|
||||
dev_err(dev, "%s(): Error %d enabling IR mode\n", __func__,
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return isl29028_read_als_ir(chip, ir_data);
|
||||
}
|
||||
|
||||
static int isl29028_set_pm_runtime_busy(struct isl29028_chip *chip, bool on)
|
||||
{
|
||||
struct device *dev = regmap_get_device(chip->regmap);
|
||||
int ret;
|
||||
|
||||
if (on) {
|
||||
ret = pm_runtime_get_sync(dev);
|
||||
if (ret < 0)
|
||||
pm_runtime_put_noidle(dev);
|
||||
} else {
|
||||
pm_runtime_mark_last_busy(dev);
|
||||
ret = pm_runtime_put_autosuspend(dev);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Channel IO */
|
||||
static int isl29028_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
|
@ -273,58 +324,65 @@ static int isl29028_write_raw(struct iio_dev *indio_dev,
|
|||
{
|
||||
struct isl29028_chip *chip = iio_priv(indio_dev);
|
||||
struct device *dev = regmap_get_device(chip->regmap);
|
||||
int ret = -EINVAL;
|
||||
int ret;
|
||||
|
||||
ret = isl29028_set_pm_runtime_busy(chip, true);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&chip->lock);
|
||||
|
||||
ret = -EINVAL;
|
||||
switch (chan->type) {
|
||||
case IIO_PROXIMITY:
|
||||
if (mask != IIO_CHAN_INFO_SAMP_FREQ) {
|
||||
dev_err(dev,
|
||||
"proximity: mask value 0x%08lx not supported\n",
|
||||
mask);
|
||||
"%s(): proximity: Mask value 0x%08lx is not supported\n",
|
||||
__func__, mask);
|
||||
break;
|
||||
}
|
||||
|
||||
if (val < 1 || val > 100) {
|
||||
dev_err(dev,
|
||||
"Samp_freq %d is not in range[1:100]\n", val);
|
||||
"%s(): proximity: Sampling frequency %d is not in the range [1:100]\n",
|
||||
__func__, val);
|
||||
break;
|
||||
}
|
||||
ret = isl29028_set_proxim_sampling(chip, val);
|
||||
if (ret < 0) {
|
||||
dev_err(dev,
|
||||
"Setting proximity samp_freq fail, err %d\n",
|
||||
ret);
|
||||
break;
|
||||
}
|
||||
chip->prox_sampling = val;
|
||||
break;
|
||||
|
||||
ret = isl29028_set_proxim_sampling(chip, val);
|
||||
break;
|
||||
case IIO_LIGHT:
|
||||
if (mask != IIO_CHAN_INFO_SCALE) {
|
||||
dev_err(dev,
|
||||
"light: mask value 0x%08lx not supported\n",
|
||||
mask);
|
||||
"%s(): light: Mask value 0x%08lx is not supported\n",
|
||||
__func__, mask);
|
||||
break;
|
||||
}
|
||||
if ((val != 125) && (val != 2000)) {
|
||||
dev_err(dev,
|
||||
"lux scale %d is invalid [125, 2000]\n", val);
|
||||
break;
|
||||
}
|
||||
ret = isl29028_set_als_scale(chip, val);
|
||||
if (ret < 0) {
|
||||
dev_err(dev,
|
||||
"Setting lux scale fail with error %d\n", ret);
|
||||
break;
|
||||
}
|
||||
chip->lux_scale = val;
|
||||
break;
|
||||
|
||||
if (val != 125 && val != 2000) {
|
||||
dev_err(dev,
|
||||
"%s(): light: Lux scale %d is not in the set {125, 2000}\n",
|
||||
__func__, val);
|
||||
break;
|
||||
}
|
||||
|
||||
ret = isl29028_set_als_scale(chip, val);
|
||||
break;
|
||||
default:
|
||||
dev_err(dev, "Unsupported channel type\n");
|
||||
dev_err(dev, "%s(): Unsupported channel type %x\n",
|
||||
__func__, chan->type);
|
||||
break;
|
||||
}
|
||||
|
||||
mutex_unlock(&chip->lock);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = isl29028_set_pm_runtime_busy(chip, false);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -334,9 +392,15 @@ static int isl29028_read_raw(struct iio_dev *indio_dev,
|
|||
{
|
||||
struct isl29028_chip *chip = iio_priv(indio_dev);
|
||||
struct device *dev = regmap_get_device(chip->regmap);
|
||||
int ret = -EINVAL;
|
||||
int ret, pm_ret;
|
||||
|
||||
ret = isl29028_set_pm_runtime_busy(chip, true);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&chip->lock);
|
||||
|
||||
ret = -EINVAL;
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
case IIO_CHAN_INFO_PROCESSED:
|
||||
|
@ -348,35 +412,50 @@ static int isl29028_read_raw(struct iio_dev *indio_dev,
|
|||
ret = isl29028_ir_get(chip, val);
|
||||
break;
|
||||
case IIO_PROXIMITY:
|
||||
ret = isl29028_proxim_get(chip, val);
|
||||
ret = isl29028_read_proxim(chip, val);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret < 0)
|
||||
break;
|
||||
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
if (chan->type != IIO_PROXIMITY)
|
||||
break;
|
||||
|
||||
*val = chip->prox_sampling;
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
if (chan->type != IIO_LIGHT)
|
||||
break;
|
||||
*val = chip->lux_scale;
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_err(dev, "mask value 0x%08lx not supported\n", mask);
|
||||
dev_err(dev, "%s(): mask value 0x%08lx is not supported\n",
|
||||
__func__, mask);
|
||||
break;
|
||||
}
|
||||
|
||||
mutex_unlock(&chip->lock);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/**
|
||||
* Preserve the ret variable if the call to
|
||||
* isl29028_set_pm_runtime_busy() is successful so the reading
|
||||
* (if applicable) is returned to user space.
|
||||
*/
|
||||
pm_ret = isl29028_set_pm_runtime_busy(chip, false);
|
||||
if (pm_ret < 0)
|
||||
return pm_ret;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -384,7 +463,6 @@ static IIO_CONST_ATTR(in_proximity_sampling_frequency_available,
|
|||
"1 3 5 10 13 20 83 100");
|
||||
static IIO_CONST_ATTR(in_illuminance_scale_available, "125 2000");
|
||||
|
||||
#define ISL29028_DEV_ATTR(name) (&iio_dev_attr_##name.dev_attr.attr)
|
||||
#define ISL29028_CONST_ATTR(name) (&iio_const_attr_##name.dev_attr.attr)
|
||||
static struct attribute *isl29028_attributes[] = {
|
||||
ISL29028_CONST_ATTR(in_proximity_sampling_frequency_available),
|
||||
|
@ -418,27 +496,19 @@ static const struct iio_info isl29028_info = {
|
|||
.write_raw = isl29028_write_raw,
|
||||
};
|
||||
|
||||
static int isl29028_chip_init_and_power_on(struct isl29028_chip *chip)
|
||||
static int isl29028_clear_configure_reg(struct isl29028_chip *chip)
|
||||
{
|
||||
struct device *dev = regmap_get_device(chip->regmap);
|
||||
int ret;
|
||||
|
||||
ret = regmap_write(chip->regmap, ISL29028_REG_CONFIGURE, 0x0);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "%s(): write to reg %d failed, err = %d\n",
|
||||
__func__, ISL29028_REG_CONFIGURE, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = isl29028_set_proxim_sampling(chip, chip->prox_sampling);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "setting the proximity, err = %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = isl29028_set_als_scale(chip, chip->lux_scale);
|
||||
if (ret < 0)
|
||||
dev_err(dev, "setting als scale failed, err = %d\n", ret);
|
||||
dev_err(dev, "%s(): Error %d clearing the CONFIGURE register\n",
|
||||
__func__, ret);
|
||||
|
||||
chip->als_ir_mode = ISL29028_MODE_NONE;
|
||||
chip->enable_prox = false;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -472,10 +542,8 @@ static int isl29028_probe(struct i2c_client *client,
|
|||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip));
|
||||
if (!indio_dev) {
|
||||
dev_err(&client->dev, "iio allocation fails\n");
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
chip = iio_priv(indio_dev);
|
||||
|
||||
|
@ -485,53 +553,102 @@ static int isl29028_probe(struct i2c_client *client,
|
|||
chip->regmap = devm_regmap_init_i2c(client, &isl29028_regmap_config);
|
||||
if (IS_ERR(chip->regmap)) {
|
||||
ret = PTR_ERR(chip->regmap);
|
||||
dev_err(&client->dev, "regmap initialization failed: %d\n",
|
||||
ret);
|
||||
dev_err(&client->dev, "%s: Error %d initializing regmap\n",
|
||||
__func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
chip->enable_prox = false;
|
||||
chip->prox_sampling = 20;
|
||||
chip->lux_scale = 2000;
|
||||
chip->als_ir_mode = ISL29028_MODE_NONE;
|
||||
|
||||
ret = regmap_write(chip->regmap, ISL29028_REG_TEST1_MODE, 0x0);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev,
|
||||
"%s(): write to reg %d failed, err = %d\n", __func__,
|
||||
ISL29028_REG_TEST1_MODE, ret);
|
||||
return ret;
|
||||
}
|
||||
ret = regmap_write(chip->regmap, ISL29028_REG_TEST2_MODE, 0x0);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev,
|
||||
"%s(): write to reg %d failed, err = %d\n", __func__,
|
||||
ISL29028_REG_TEST2_MODE, ret);
|
||||
"%s(): Error %d writing to TEST1_MODE register\n",
|
||||
__func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = isl29028_chip_init_and_power_on(chip);
|
||||
ret = regmap_write(chip->regmap, ISL29028_REG_TEST2_MODE, 0x0);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "chip initialization failed: %d\n", ret);
|
||||
dev_err(&client->dev,
|
||||
"%s(): Error %d writing to TEST2_MODE register\n",
|
||||
__func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = isl29028_clear_configure_reg(chip);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
indio_dev->info = &isl29028_info;
|
||||
indio_dev->channels = isl29028_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(isl29028_channels);
|
||||
indio_dev->name = id->name;
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
pm_runtime_enable(&client->dev);
|
||||
pm_runtime_set_autosuspend_delay(&client->dev,
|
||||
ISL29028_POWER_OFF_DELAY_MS);
|
||||
pm_runtime_use_autosuspend(&client->dev);
|
||||
|
||||
ret = devm_iio_device_register(indio_dev->dev.parent, indio_dev);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev,
|
||||
"iio registration fails with error %d\n",
|
||||
ret);
|
||||
"%s(): iio registration failed with error %d\n",
|
||||
__func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int isl29028_remove(struct i2c_client *client)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
struct isl29028_chip *chip = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
pm_runtime_disable(&client->dev);
|
||||
pm_runtime_set_suspended(&client->dev);
|
||||
pm_runtime_put_noidle(&client->dev);
|
||||
|
||||
return isl29028_clear_configure_reg(chip);
|
||||
}
|
||||
|
||||
static int __maybe_unused isl29028_suspend(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
|
||||
struct isl29028_chip *chip = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&chip->lock);
|
||||
|
||||
ret = isl29028_clear_configure_reg(chip);
|
||||
|
||||
mutex_unlock(&chip->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __maybe_unused isl29028_resume(struct device *dev)
|
||||
{
|
||||
/**
|
||||
* The specific component (ALS/IR or proximity) will enable itself as
|
||||
* needed the next time that the user requests a reading. This is done
|
||||
* above in isl29028_set_als_ir_mode() and isl29028_enable_proximity().
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops isl29028_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(isl29028_suspend, isl29028_resume)
|
||||
SET_RUNTIME_PM_OPS(isl29028_suspend, isl29028_resume, NULL)
|
||||
};
|
||||
|
||||
static const struct i2c_device_id isl29028_id[] = {
|
||||
{"isl29028", 0},
|
||||
{}
|
||||
|
@ -548,9 +665,11 @@ MODULE_DEVICE_TABLE(of, isl29028_of_match);
|
|||
static struct i2c_driver isl29028_driver = {
|
||||
.driver = {
|
||||
.name = "isl29028",
|
||||
.pm = &isl29028_pm_ops,
|
||||
.of_match_table = isl29028_of_match,
|
||||
},
|
||||
.probe = isl29028_probe,
|
||||
.remove = isl29028_remove,
|
||||
.id_table = isl29028_id,
|
||||
};
|
||||
|
||||
|
|
|
@ -260,7 +260,7 @@ out_free_irq:
|
|||
out1:
|
||||
iio_trigger_unregister(st->trig);
|
||||
out:
|
||||
iio_trigger_put(st->trig);
|
||||
iio_trigger_free(st->trig);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -273,7 +273,7 @@ static int iio_bfin_tmr_trigger_remove(struct platform_device *pdev)
|
|||
peripheral_free(st->t->pin);
|
||||
free_irq(st->irq, st);
|
||||
iio_trigger_unregister(st->trig);
|
||||
iio_trigger_put(st->trig);
|
||||
iio_trigger_free(st->trig);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue