Staging/IIO patches for 4.12-rc1

Here is the big staging tree update for 4.12-rc1.  And it's a big one,
 adding about 350k new lines of crap^Wcode, mostly all in a big dump of
 media drivers from Intel.  But there's other new drivers in here as
 well, yet-another-wifi driver, new IIO drivers, and a new crypto
 accelerator.  We also deleted a bunch of stuff, mostly in patch
 cleanups, but also the Android ION code has shrunk a lot, and the
 Android low memory killer driver was finally deleted, much to the
 celebration of the -mm developers.
 
 All of these have been in linux-next with a few build issues that will
 show up when you merge to your tree, I'll follow up with fixes for those
 after this gets merged.
 
 Signed-off-by: Greg Kroah-Hartman <[email protected]>
 -----BEGIN PGP SIGNATURE-----
 
 iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCWQzzlQ8cZ3JlZ0Brcm9h
 aC5jb20ACgkQMUfUDdst+ylNMgCcD+GoaF/Ml7YnULRl2GG/526II78AnitZ8qjd
 rPqeowMIewYu9fgckLUc
 =7rzO
 -----END PGP SIGNATURE-----

Merge tag 'staging-4.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging

Pull staging/IIO updates from Greg KH:
 "Here is the big staging tree update for 4.12-rc1.

  It's a big one, adding about 350k new lines of crap^Wcode, mostly all
  in a big dump of media drivers from Intel. But there's other new
  drivers in here as well, yet-another-wifi driver, new IIO drivers, and
  a new crypto accelerator.

  We also deleted a bunch of stuff, mostly in patch cleanups, but also
  the Android ION code has shrunk a lot, and the Android low memory
  killer driver was finally deleted, much to the celebration of the -mm
  developers.

  All of these have been in linux-next with a few build issues that will
  show up when you merge to your tree"

Merge conflicts in the new rtl8723bs driver (due to the wifi changes
this merge window) handled as per linux-next, courtesy of Stephen
Rothwell.

* tag 'staging-4.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging: (1182 commits)
  staging: fsl-mc/dpio: add cpu <--> LE conversion for dpaa2_fd
  staging: ks7010: remove line continuations in quoted strings
  staging: vt6656: use tabs instead of spaces
  staging: android: ion: Fix unnecessary initialization of static variable
  staging: media: atomisp: fix range checking on clk_num
  staging: media: atomisp: fix misspelled word in comment
  staging: media: atomisp: kmap() can't fail
  staging: atomisp: remove #ifdef for runtime PM functions
  staging: atomisp: satm include directory is gone
  atomisp: remove some more unused files
  atomisp: remove hmm_load/store/clear indirections
  atomisp: kill off mmgr_free
  atomisp: clean up the hmm init/cleanup indirections
  atomisp: handle allocation calls before init in the hmm layer
  staging: fsl-dpaa2/eth: Add maintainer for Ethernet driver
  staging: fsl-dpaa2/eth: Add TODO file
  staging: fsl-dpaa2/eth: Add trace points
  staging: fsl-dpaa2/eth: Add driver specific stats
  staging: fsl-dpaa2/eth: Add ethtool support
  staging: fsl-dpaa2/eth: Add Freescale DPAA2 Ethernet driver
  ...
This commit is contained in:
Linus Torvalds 2017-05-05 18:16:23 -07:00
commit c6a677c6f3
1787 changed files with 359290 additions and 15278 deletions

View file

@ -5,6 +5,37 @@
menu "Accelerometers"
config ADXL345
tristate
config ADXL345_I2C
tristate "Analog Devices ADXL345 3-Axis Digital Accelerometer I2C Driver"
depends on INPUT_ADXL34X=n
depends on I2C
select ADXL345
select REGMAP_I2C
help
Say Y here if you want to build support for the Analog Devices
ADXL345 3-axis digital accelerometer.
To compile this driver as a module, choose M here: the module
will be called adxl345_i2c and you will also get adxl345_core
for the core module.
config ADXL345_SPI
tristate "Analog Devices ADXL345 3-Axis Digital Accelerometer SPI Driver"
depends on INPUT_ADXL34X=n
depends on SPI
select ADXL345
select REGMAP_SPI
help
Say Y here if you want to build support for the Analog Devices
ADXL345 3-axis digital accelerometer.
To compile this driver as a module, choose M here: the module
will be called adxl345_spi and you will also get adxl345_core
for the core module.
config BMA180
tristate "Bosch BMA180/BMA250 3-Axis Accelerometer Driver"
depends on I2C

View file

@ -3,6 +3,9 @@
#
# When adding new entries keep the list in alphabetical order
obj-$(CONFIG_ADXL345) += adxl345_core.o
obj-$(CONFIG_ADXL345_I2C) += adxl345_i2c.o
obj-$(CONFIG_ADXL345_SPI) += adxl345_spi.o
obj-$(CONFIG_BMA180) += bma180.o
obj-$(CONFIG_BMA220) += bma220_spi.o
obj-$(CONFIG_BMC150_ACCEL) += bmc150-accel-core.o

View file

@ -0,0 +1,18 @@
/*
* ADXL345 3-Axis Digital Accelerometer
*
* Copyright (c) 2017 Eva Rachel Retuya <eraretuya@gmail.com>
*
* This file is subject to the terms and conditions of version 2 of
* the GNU General Public License. See the file COPYING in the main
* directory of this archive for more details.
*/
#ifndef _ADXL345_H_
#define _ADXL345_H_
int adxl345_core_probe(struct device *dev, struct regmap *regmap,
const char *name);
int adxl345_core_remove(struct device *dev);
#endif /* _ADXL345_H_ */

View file

@ -0,0 +1,179 @@
/*
* ADXL345 3-Axis Digital Accelerometer IIO core driver
*
* Copyright (c) 2017 Eva Rachel Retuya <eraretuya@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.
*/
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/iio/iio.h>
#include "adxl345.h"
#define ADXL345_REG_DEVID 0x00
#define ADXL345_REG_POWER_CTL 0x2D
#define ADXL345_REG_DATA_FORMAT 0x31
#define ADXL345_REG_DATAX0 0x32
#define ADXL345_REG_DATAY0 0x34
#define ADXL345_REG_DATAZ0 0x36
#define ADXL345_POWER_CTL_MEASURE BIT(3)
#define ADXL345_POWER_CTL_STANDBY 0x00
#define ADXL345_DATA_FORMAT_FULL_RES BIT(3) /* Up to 13-bits resolution */
#define ADXL345_DATA_FORMAT_2G 0
#define ADXL345_DATA_FORMAT_4G 1
#define ADXL345_DATA_FORMAT_8G 2
#define ADXL345_DATA_FORMAT_16G 3
#define ADXL345_DEVID 0xE5
/*
* In full-resolution mode, scale factor is maintained at ~4 mg/LSB
* in all g ranges.
*
* At +/- 16g with 13-bit resolution, scale is computed as:
* (16 + 16) * 9.81 / (2^13 - 1) = 0.0383
*/
static const int adxl345_uscale = 38300;
struct adxl345_data {
struct regmap *regmap;
u8 data_range;
};
#define ADXL345_CHANNEL(reg, axis) { \
.type = IIO_ACCEL, \
.modified = 1, \
.channel2 = IIO_MOD_##axis, \
.address = reg, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
}
static const struct iio_chan_spec adxl345_channels[] = {
ADXL345_CHANNEL(ADXL345_REG_DATAX0, X),
ADXL345_CHANNEL(ADXL345_REG_DATAY0, Y),
ADXL345_CHANNEL(ADXL345_REG_DATAZ0, Z),
};
static int adxl345_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct adxl345_data *data = iio_priv(indio_dev);
__le16 regval;
int ret;
switch (mask) {
case IIO_CHAN_INFO_RAW:
/*
* Data is stored in adjacent registers:
* ADXL345_REG_DATA(X0/Y0/Z0) contain the least significant byte
* and ADXL345_REG_DATA(X0/Y0/Z0) + 1 the most significant byte
*/
ret = regmap_bulk_read(data->regmap, chan->address, &regval,
sizeof(regval));
if (ret < 0)
return ret;
*val = sign_extend32(le16_to_cpu(regval), 12);
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
*val = 0;
*val2 = adxl345_uscale;
return IIO_VAL_INT_PLUS_MICRO;
}
return -EINVAL;
}
static const struct iio_info adxl345_info = {
.driver_module = THIS_MODULE,
.read_raw = adxl345_read_raw,
};
int adxl345_core_probe(struct device *dev, struct regmap *regmap,
const char *name)
{
struct adxl345_data *data;
struct iio_dev *indio_dev;
u32 regval;
int ret;
ret = regmap_read(regmap, ADXL345_REG_DEVID, &regval);
if (ret < 0) {
dev_err(dev, "Error reading device ID: %d\n", ret);
return ret;
}
if (regval != ADXL345_DEVID) {
dev_err(dev, "Invalid device ID: %x, expected %x\n",
regval, ADXL345_DEVID);
return -ENODEV;
}
indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
data = iio_priv(indio_dev);
dev_set_drvdata(dev, indio_dev);
data->regmap = regmap;
/* Enable full-resolution mode */
data->data_range = ADXL345_DATA_FORMAT_FULL_RES;
ret = regmap_write(data->regmap, ADXL345_REG_DATA_FORMAT,
data->data_range);
if (ret < 0) {
dev_err(dev, "Failed to set data range: %d\n", ret);
return ret;
}
indio_dev->dev.parent = dev;
indio_dev->name = name;
indio_dev->info = &adxl345_info;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = adxl345_channels;
indio_dev->num_channels = ARRAY_SIZE(adxl345_channels);
/* Enable measurement mode */
ret = regmap_write(data->regmap, ADXL345_REG_POWER_CTL,
ADXL345_POWER_CTL_MEASURE);
if (ret < 0) {
dev_err(dev, "Failed to enable measurement mode: %d\n", ret);
return ret;
}
ret = iio_device_register(indio_dev);
if (ret < 0) {
dev_err(dev, "iio_device_register failed: %d\n", ret);
regmap_write(data->regmap, ADXL345_REG_POWER_CTL,
ADXL345_POWER_CTL_STANDBY);
}
return ret;
}
EXPORT_SYMBOL_GPL(adxl345_core_probe);
int adxl345_core_remove(struct device *dev)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct adxl345_data *data = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
return regmap_write(data->regmap, ADXL345_REG_POWER_CTL,
ADXL345_POWER_CTL_STANDBY);
}
EXPORT_SYMBOL_GPL(adxl345_core_remove);
MODULE_AUTHOR("Eva Rachel Retuya <[email protected]>");
MODULE_DESCRIPTION("ADXL345 3-Axis Digital Accelerometer core driver");
MODULE_LICENSE("GPL v2");

View file

@ -0,0 +1,73 @@
/*
* ADXL345 3-Axis Digital Accelerometer I2C driver
*
* Copyright (c) 2017 Eva Rachel Retuya <eraretuya@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.
*
* 7-bit I2C slave address: 0x1D (ALT ADDRESS pin tied to VDDIO) or
* 0x53 (ALT ADDRESS pin grounded)
*/
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include "adxl345.h"
static const struct regmap_config adxl345_i2c_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
};
static int adxl345_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct regmap *regmap;
regmap = devm_regmap_init_i2c(client, &adxl345_i2c_regmap_config);
if (IS_ERR(regmap)) {
dev_err(&client->dev, "Error initializing i2c regmap: %ld\n",
PTR_ERR(regmap));
return PTR_ERR(regmap);
}
return adxl345_core_probe(&client->dev, regmap, id ? id->name : NULL);
}
static int adxl345_i2c_remove(struct i2c_client *client)
{
return adxl345_core_remove(&client->dev);
}
static const struct i2c_device_id adxl345_i2c_id[] = {
{ "adxl345", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, adxl345_i2c_id);
static const struct of_device_id adxl345_of_match[] = {
{ .compatible = "adi,adxl345" },
{ },
};
MODULE_DEVICE_TABLE(of, adxl345_of_match);
static struct i2c_driver adxl345_i2c_driver = {
.driver = {
.name = "adxl345_i2c",
.of_match_table = adxl345_of_match,
},
.probe = adxl345_i2c_probe,
.remove = adxl345_i2c_remove,
.id_table = adxl345_i2c_id,
};
module_i2c_driver(adxl345_i2c_driver);
MODULE_AUTHOR("Eva Rachel Retuya <[email protected]>");
MODULE_DESCRIPTION("ADXL345 3-Axis Digital Accelerometer I2C driver");
MODULE_LICENSE("GPL v2");

View file

@ -0,0 +1,81 @@
/*
* ADXL345 3-Axis Digital Accelerometer SPI driver
*
* Copyright (c) 2017 Eva Rachel Retuya <eraretuya@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.
*/
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/spi/spi.h>
#include "adxl345.h"
#define ADXL345_MAX_SPI_FREQ_HZ 5000000
static const struct regmap_config adxl345_spi_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
/* Setting bits 7 and 6 enables multiple-byte read */
.read_flag_mask = BIT(7) | BIT(6),
};
static int adxl345_spi_probe(struct spi_device *spi)
{
const struct spi_device_id *id = spi_get_device_id(spi);
struct regmap *regmap;
/* Bail out if max_speed_hz exceeds 5 MHz */
if (spi->max_speed_hz > ADXL345_MAX_SPI_FREQ_HZ) {
dev_err(&spi->dev, "SPI CLK, %d Hz exceeds 5 MHz\n",
spi->max_speed_hz);
return -EINVAL;
}
regmap = devm_regmap_init_spi(spi, &adxl345_spi_regmap_config);
if (IS_ERR(regmap)) {
dev_err(&spi->dev, "Error initializing spi regmap: %ld\n",
PTR_ERR(regmap));
return PTR_ERR(regmap);
}
return adxl345_core_probe(&spi->dev, regmap, id->name);
}
static int adxl345_spi_remove(struct spi_device *spi)
{
return adxl345_core_remove(&spi->dev);
}
static const struct spi_device_id adxl345_spi_id[] = {
{ "adxl345", 0 },
{ }
};
MODULE_DEVICE_TABLE(spi, adxl345_spi_id);
static const struct of_device_id adxl345_of_match[] = {
{ .compatible = "adi,adxl345" },
{ },
};
MODULE_DEVICE_TABLE(of, adxl345_of_match);
static struct spi_driver adxl345_spi_driver = {
.driver = {
.name = "adxl345_spi",
.of_match_table = adxl345_of_match,
},
.probe = adxl345_spi_probe,
.remove = adxl345_spi_remove,
.id_table = adxl345_spi_id,
};
module_spi_driver(adxl345_spi_driver);
MODULE_AUTHOR("Eva Rachel Retuya <[email protected]>");
MODULE_DESCRIPTION("ADXL345 3-Axis Digital Accelerometer SPI driver");
MODULE_LICENSE("GPL v2");

View file

@ -18,6 +18,7 @@
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/of_device.h>
#include <linux/of.h>
#include <linux/bitops.h>
#include <linux/slab.h>
@ -32,7 +33,7 @@
#define BMA180_DRV_NAME "bma180"
#define BMA180_IRQ_NAME "bma180_event"
enum {
enum chip_ids {
BMA180,
BMA250,
};
@ -41,11 +42,11 @@ struct bma180_data;
struct bma180_part_info {
const struct iio_chan_spec *channels;
unsigned num_channels;
unsigned int num_channels;
const int *scale_table;
unsigned num_scales;
unsigned int num_scales;
const int *bw_table;
unsigned num_bw;
unsigned int num_bw;
u8 int_reset_reg, int_reset_mask;
u8 sleep_reg, sleep_mask;
@ -408,7 +409,7 @@ err:
dev_err(&data->client->dev, "failed to disable the chip\n");
}
static ssize_t bma180_show_avail(char *buf, const int *vals, unsigned n,
static ssize_t bma180_show_avail(char *buf, const int *vals, unsigned int n,
bool micros)
{
size_t len = 0;
@ -707,6 +708,7 @@ static int bma180_probe(struct i2c_client *client,
{
struct bma180_data *data;
struct iio_dev *indio_dev;
enum chip_ids chip;
int ret;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
@ -716,7 +718,11 @@ static int bma180_probe(struct i2c_client *client,
data = iio_priv(indio_dev);
i2c_set_clientdata(client, indio_dev);
data->client = client;
data->part_info = &bma180_part_info[id->driver_data];
if (client->dev.of_node)
chip = (enum chip_ids)of_device_get_match_data(&client->dev);
else
chip = id->driver_data;
data->part_info = &bma180_part_info[chip];
ret = data->part_info->chip_config(data);
if (ret < 0)
@ -844,10 +850,24 @@ static struct i2c_device_id bma180_ids[] = {
MODULE_DEVICE_TABLE(i2c, bma180_ids);
static const struct of_device_id bma180_of_match[] = {
{
.compatible = "bosch,bma180",
.data = (void *)BMA180
},
{
.compatible = "bosch,bma250",
.data = (void *)BMA250
},
{ }
};
MODULE_DEVICE_TABLE(of, bma180_of_match);
static struct i2c_driver bma180_driver = {
.driver = {
.name = "bma180",
.pm = BMA180_PM_OPS,
.of_match_table = bma180_of_match,
},
.probe = bma180_probe,
.remove = bma180_remove,

View file

@ -41,12 +41,20 @@ static const struct i2c_device_id mma7455_i2c_ids[] = {
};
MODULE_DEVICE_TABLE(i2c, mma7455_i2c_ids);
static const struct of_device_id mma7455_of_match[] = {
{ .compatible = "fsl,mma7455" },
{ .compatible = "fsl,mma7456" },
{ }
};
MODULE_DEVICE_TABLE(of, mma7455_of_match);
static struct i2c_driver mma7455_i2c_driver = {
.probe = mma7455_i2c_probe,
.remove = mma7455_i2c_remove,
.id_table = mma7455_i2c_ids,
.driver = {
.name = "mma7455-i2c",
.of_match_table = mma7455_of_match,
},
};
module_i2c_driver(mma7455_i2c_driver);

View file

@ -253,6 +253,12 @@ static const struct i2c_device_id mma7660_i2c_id[] = {
};
MODULE_DEVICE_TABLE(i2c, mma7660_i2c_id);
static const struct of_device_id mma7660_of_match[] = {
{ .compatible = "fsl,mma7660" },
{ }
};
MODULE_DEVICE_TABLE(of, mma7660_of_match);
static const struct acpi_device_id mma7660_acpi_id[] = {
{"MMA7660", 0},
{}
@ -264,6 +270,7 @@ static struct i2c_driver mma7660_driver = {
.driver = {
.name = "mma7660",
.pm = MMA7660_PM_OPS,
.of_match_table = mma7660_of_match,
.acpi_match_table = ACPI_PTR(mma7660_acpi_id),
},
.probe = mma7660_probe,

View file

@ -130,6 +130,17 @@ config AD799X
To compile this driver as a module, choose M here: the module will be
called ad799x.
config ASPEED_ADC
tristate "Aspeed ADC"
depends on ARCH_ASPEED || COMPILE_TEST
depends on COMMON_CLK
help
If you say yes here you get support for the ADC included in Aspeed
BMC SoCs.
To compile this driver as a module, choose M here: the module will be
called aspeed_adc.
config AT91_ADC
tristate "Atmel AT91 ADC"
depends on ARCH_AT91
@ -205,6 +216,17 @@ config CC10001_ADC
This driver can also be built as a module. If so, the module will be
called cc10001_adc.
config CPCAP_ADC
tristate "Motorola CPCAP PMIC ADC driver"
depends on MFD_CPCAP
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
help
Say yes here to build support for Motorola CPCAP PMIC ADC.
This driver can also be built as a module. If so, the module will be
called cpcap-adc.
config DA9150_GPADC
tristate "Dialog DA9150 GPADC driver support"
depends on MFD_DA9150
@ -328,6 +350,18 @@ config LPC18XX_ADC
To compile this driver as a module, choose M here: the module will be
called lpc18xx_adc.
config LPC32XX_ADC
tristate "NXP LPC32XX ADC"
depends on ARCH_LPC32XX || COMPILE_TEST
depends on HAS_IOMEM
help
Say yes here to build support for the integrated ADC inside the
LPC32XX SoC. Note that this feature uses the same hardware as the
touchscreen driver, so you should either select only one of the two
drivers (lpc32xx_adc or lpc32xx_ts) or, in the OpenFirmware case,
activate only one via device tree selection. Provides direct access
via sysfs.
config LTC2485
tristate "Linear Technology LTC2485 ADC driver"
depends on I2C
@ -337,6 +371,16 @@ config LTC2485
To compile this driver as a module, choose M here: the module will be
called ltc2485.
config LTC2497
tristate "Linear Technology LTC2497 ADC driver"
depends on I2C
help
Say yes here to build support for Linear Technology LTC2497
16-Bit 8-/16-Channel Delta Sigma ADC.
To compile this driver as a module, choose M here: the module will be
called ltc2497.
config MAX1027
tristate "Maxim max1027 ADC driver"
depends on SPI
@ -358,6 +402,18 @@ config MAX11100
To compile this driver as a module, choose M here: the module will be
called max11100.
config MAX1118
tristate "Maxim max1117/max1118/max1119 ADCs driver"
depends on SPI
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
help
Say yes here to build support for Maxim max1117/max1118/max1119
8-bit, dual-channel ADCs.
To compile this driver as a module, choose M here: the module will be
called max1118.
config MAX1363
tristate "Maxim max1363 ADC driver"
depends on I2C
@ -377,6 +433,16 @@ config MAX1363
To compile this driver as a module, choose M here: the module will be
called max1363.
config MAX9611
tristate "Maxim max9611/max9612 ADC driver"
depends on I2C
help
Say yes here to build support for Maxim max9611/max9612 current sense
amplifier with 12-bits ADC interface.
To compile this driver as a module, choose M here: the module will be
called max9611.
config MCP320X
tristate "Microchip Technology MCP3x01/02/04/08"
depends on SPI
@ -451,6 +517,20 @@ config PALMAS_GPADC
is used in smartphones and tablets and supports a 16 channel
general purpose ADC.
config QCOM_VADC_COMMON
tristate
config QCOM_PM8XXX_XOADC
tristate "Qualcomm SSBI PM8xxx PMIC XOADCs"
depends on MFD_PM8XXX
select QCOM_VADC_COMMON
help
ADC driver for the XOADC portions of the Qualcomm PM8xxx PMICs
using SSBI transport: PM8018, PM8038, PM8058, PM8921.
To compile this driver as a module, choose M here: the module
will be called qcom-pm8xxx-xoadc.
config QCOM_SPMI_IADC
tristate "Qualcomm SPMI PMIC current ADC"
depends on SPMI
@ -469,6 +549,7 @@ config QCOM_SPMI_VADC
tristate "Qualcomm SPMI PMIC voltage ADC"
depends on SPMI
select REGMAP_SPMI
select QCOM_VADC_COMMON
help
This is the IIO Voltage ADC driver for Qualcomm QPNP VADC Chip.
@ -503,6 +584,17 @@ config ROCKCHIP_SARADC
To compile this driver as a module, choose M here: the
module will be called rockchip_saradc.
config SPEAR_ADC
tristate "ST SPEAr ADC"
depends on PLAT_SPEAR || COMPILE_TEST
depends on HAS_IOMEM
help
Say yes here to build support for the integrated ADC inside the
ST SPEAr SoC. Provides direct access via sysfs.
To compile this driver as a module, choose M here: the
module will be called spear_adc.
config STM32_ADC_CORE
tristate "STMicroelectronics STM32 adc core"
depends on ARCH_STM32 || COMPILE_TEST
@ -532,7 +624,7 @@ config STM32_ADC
config STX104
tristate "Apex Embedded Systems STX104 driver"
depends on X86 && ISA_BUS_API
depends on PC104 && X86 && ISA_BUS_API
select GPIOLIB
help
Say yes here to build support for the Apex Embedded Systems STX104
@ -545,6 +637,24 @@ config STX104
The base port addresses for the devices may be configured via the base
array module parameter.
config SUN4I_GPADC
tristate "Support for the Allwinner SoCs GPADC"
depends on IIO
depends on MFD_SUN4I_GPADC || MACH_SUN8I
depends on THERMAL || !THERMAL_OF
help
Say yes here to build support for Allwinner (A10, A13 and A31) SoCs
GPADC. This ADC provides 4 channels which can be used as an ADC or as
a touchscreen input and one channel for thermal sensor.
The thermal sensor slows down ADC readings and can be disabled by
disabling CONFIG_THERMAL_OF. However, the thermal sensor should be
enabled by default since the SoC temperature is usually more critical
than ADC readings.
To compile this driver as a module, choose M here: the module will be
called sun4i-gpadc-iio.
config TI_ADC081C
tristate "Texas Instruments ADC081C/ADC101C/ADC121C family"
depends on I2C

View file

@ -14,6 +14,7 @@ obj-$(CONFIG_AD7791) += ad7791.o
obj-$(CONFIG_AD7793) += ad7793.o
obj-$(CONFIG_AD7887) += ad7887.o
obj-$(CONFIG_AD799X) += ad799x.o
obj-$(CONFIG_ASPEED_ADC) += aspeed_adc.o
obj-$(CONFIG_AT91_ADC) += at91_adc.o
obj-$(CONFIG_AT91_SAMA5D2_ADC) += at91-sama5d2_adc.o
obj-$(CONFIG_AXP20X_ADC) += axp20x_adc.o
@ -21,6 +22,7 @@ obj-$(CONFIG_AXP288_ADC) += axp288_adc.o
obj-$(CONFIG_BCM_IPROC_ADC) += bcm_iproc_adc.o
obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o
obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o
obj-$(CONFIG_CPCAP_ADC) += cpcap-adc.o
obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o
obj-$(CONFIG_ENVELOPE_DETECTOR) += envelope-detector.o
obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
@ -31,10 +33,14 @@ obj-$(CONFIG_IMX7D_ADC) += imx7d_adc.o
obj-$(CONFIG_INA2XX_ADC) += ina2xx-adc.o
obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
obj-$(CONFIG_LPC18XX_ADC) += lpc18xx_adc.o
obj-$(CONFIG_LPC32XX_ADC) += lpc32xx_adc.o
obj-$(CONFIG_LTC2485) += ltc2485.o
obj-$(CONFIG_LTC2497) += ltc2497.o
obj-$(CONFIG_MAX1027) += max1027.o
obj-$(CONFIG_MAX11100) += max11100.o
obj-$(CONFIG_MAX1118) += max1118.o
obj-$(CONFIG_MAX1363) += max1363.o
obj-$(CONFIG_MAX9611) += max9611.o
obj-$(CONFIG_MCP320X) += mcp320x.o
obj-$(CONFIG_MCP3422) += mcp3422.o
obj-$(CONFIG_MEDIATEK_MT6577_AUXADC) += mt6577_auxadc.o
@ -44,10 +50,14 @@ obj-$(CONFIG_MXS_LRADC_ADC) += mxs-lradc-adc.o
obj-$(CONFIG_NAU7802) += nau7802.o
obj-$(CONFIG_PALMAS_GPADC) += palmas_gpadc.o
obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o
obj-$(CONFIG_QCOM_VADC_COMMON) += qcom-vadc-common.o
obj-$(CONFIG_QCOM_SPMI_VADC) += qcom-spmi-vadc.o
obj-$(CONFIG_QCOM_PM8XXX_XOADC) += qcom-pm8xxx-xoadc.o
obj-$(CONFIG_RCAR_GYRO_ADC) += rcar-gyroadc.o
obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
obj-$(CONFIG_SPEAR_ADC) += spear_adc.o
obj-$(CONFIG_STX104) += stx104.o
obj-$(CONFIG_SUN4I_GPADC) += sun4i-gpadc-iio.o
obj-$(CONFIG_STM32_ADC_CORE) += stm32-adc-core.o
obj-$(CONFIG_STM32_ADC) += stm32-adc.o
obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o

View file

@ -520,7 +520,7 @@ static struct attribute *ad799x_event_attributes[] = {
NULL,
};
static struct attribute_group ad799x_event_attrs_group = {
static const struct attribute_group ad799x_event_attrs_group = {
.attrs = ad799x_event_attributes,
};

View file

@ -0,0 +1,295 @@
/*
* Aspeed AST2400/2500 ADC
*
* Copyright (C) 2017 Google, Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
*/
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/spinlock.h>
#include <linux/types.h>
#include <linux/iio/iio.h>
#include <linux/iio/driver.h>
#define ASPEED_RESOLUTION_BITS 10
#define ASPEED_CLOCKS_PER_SAMPLE 12
#define ASPEED_REG_ENGINE_CONTROL 0x00
#define ASPEED_REG_INTERRUPT_CONTROL 0x04
#define ASPEED_REG_VGA_DETECT_CONTROL 0x08
#define ASPEED_REG_CLOCK_CONTROL 0x0C
#define ASPEED_REG_MAX 0xC0
#define ASPEED_OPERATION_MODE_POWER_DOWN (0x0 << 1)
#define ASPEED_OPERATION_MODE_STANDBY (0x1 << 1)
#define ASPEED_OPERATION_MODE_NORMAL (0x7 << 1)
#define ASPEED_ENGINE_ENABLE BIT(0)
struct aspeed_adc_model_data {
const char *model_name;
unsigned int min_sampling_rate; // Hz
unsigned int max_sampling_rate; // Hz
unsigned int vref_voltage; // mV
};
struct aspeed_adc_data {
struct device *dev;
void __iomem *base;
spinlock_t clk_lock;
struct clk_hw *clk_prescaler;
struct clk_hw *clk_scaler;
};
#define ASPEED_CHAN(_idx, _data_reg_addr) { \
.type = IIO_VOLTAGE, \
.indexed = 1, \
.channel = (_idx), \
.address = (_data_reg_addr), \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
BIT(IIO_CHAN_INFO_SAMP_FREQ), \
}
static const struct iio_chan_spec aspeed_adc_iio_channels[] = {
ASPEED_CHAN(0, 0x10),
ASPEED_CHAN(1, 0x12),
ASPEED_CHAN(2, 0x14),
ASPEED_CHAN(3, 0x16),
ASPEED_CHAN(4, 0x18),
ASPEED_CHAN(5, 0x1A),
ASPEED_CHAN(6, 0x1C),
ASPEED_CHAN(7, 0x1E),
ASPEED_CHAN(8, 0x20),
ASPEED_CHAN(9, 0x22),
ASPEED_CHAN(10, 0x24),
ASPEED_CHAN(11, 0x26),
ASPEED_CHAN(12, 0x28),
ASPEED_CHAN(13, 0x2A),
ASPEED_CHAN(14, 0x2C),
ASPEED_CHAN(15, 0x2E),
};
static int aspeed_adc_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct aspeed_adc_data *data = iio_priv(indio_dev);
const struct aspeed_adc_model_data *model_data =
of_device_get_match_data(data->dev);
switch (mask) {
case IIO_CHAN_INFO_RAW:
*val = readw(data->base + chan->address);
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
*val = model_data->vref_voltage;
*val2 = ASPEED_RESOLUTION_BITS;
return IIO_VAL_FRACTIONAL_LOG2;
case IIO_CHAN_INFO_SAMP_FREQ:
*val = clk_get_rate(data->clk_scaler->clk) /
ASPEED_CLOCKS_PER_SAMPLE;
return IIO_VAL_INT;
default:
return -EINVAL;
}
}
static int aspeed_adc_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
struct aspeed_adc_data *data = iio_priv(indio_dev);
const struct aspeed_adc_model_data *model_data =
of_device_get_match_data(data->dev);
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
if (val < model_data->min_sampling_rate ||
val > model_data->max_sampling_rate)
return -EINVAL;
clk_set_rate(data->clk_scaler->clk,
val * ASPEED_CLOCKS_PER_SAMPLE);
return 0;
case IIO_CHAN_INFO_SCALE:
case IIO_CHAN_INFO_RAW:
/*
* Technically, these could be written but the only reasons
* for doing so seem better handled in userspace. EPERM is
* returned to signal this is a policy choice rather than a
* hardware limitation.
*/
return -EPERM;
default:
return -EINVAL;
}
}
static int aspeed_adc_reg_access(struct iio_dev *indio_dev,
unsigned int reg, unsigned int writeval,
unsigned int *readval)
{
struct aspeed_adc_data *data = iio_priv(indio_dev);
if (!readval || reg % 4 || reg > ASPEED_REG_MAX)
return -EINVAL;
*readval = readl(data->base + reg);
return 0;
}
static const struct iio_info aspeed_adc_iio_info = {
.driver_module = THIS_MODULE,
.read_raw = aspeed_adc_read_raw,
.write_raw = aspeed_adc_write_raw,
.debugfs_reg_access = aspeed_adc_reg_access,
};
static int aspeed_adc_probe(struct platform_device *pdev)
{
struct iio_dev *indio_dev;
struct aspeed_adc_data *data;
const struct aspeed_adc_model_data *model_data;
struct resource *res;
const char *clk_parent_name;
int ret;
u32 adc_engine_control_reg_val;
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
data = iio_priv(indio_dev);
data->dev = &pdev->dev;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
data->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(data->base))
return PTR_ERR(data->base);
/* Register ADC clock prescaler with source specified by device tree. */
spin_lock_init(&data->clk_lock);
clk_parent_name = of_clk_get_parent_name(pdev->dev.of_node, 0);
data->clk_prescaler = clk_hw_register_divider(
&pdev->dev, "prescaler", clk_parent_name, 0,
data->base + ASPEED_REG_CLOCK_CONTROL,
17, 15, 0, &data->clk_lock);
if (IS_ERR(data->clk_prescaler))
return PTR_ERR(data->clk_prescaler);
/*
* Register ADC clock scaler downstream from the prescaler. Allow rate
* setting to adjust the prescaler as well.
*/
data->clk_scaler = clk_hw_register_divider(
&pdev->dev, "scaler", "prescaler",
CLK_SET_RATE_PARENT,
data->base + ASPEED_REG_CLOCK_CONTROL,
0, 10, 0, &data->clk_lock);
if (IS_ERR(data->clk_scaler)) {
ret = PTR_ERR(data->clk_scaler);
goto scaler_error;
}
/* Start all channels in normal mode. */
clk_prepare_enable(data->clk_scaler->clk);
adc_engine_control_reg_val = GENMASK(31, 16) |
ASPEED_OPERATION_MODE_NORMAL | ASPEED_ENGINE_ENABLE;
writel(adc_engine_control_reg_val,
data->base + ASPEED_REG_ENGINE_CONTROL);
model_data = of_device_get_match_data(&pdev->dev);
indio_dev->name = model_data->model_name;
indio_dev->dev.parent = &pdev->dev;
indio_dev->info = &aspeed_adc_iio_info;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = aspeed_adc_iio_channels;
indio_dev->num_channels = ARRAY_SIZE(aspeed_adc_iio_channels);
ret = iio_device_register(indio_dev);
if (ret)
goto iio_register_error;
return 0;
iio_register_error:
writel(ASPEED_OPERATION_MODE_POWER_DOWN,
data->base + ASPEED_REG_ENGINE_CONTROL);
clk_disable_unprepare(data->clk_scaler->clk);
clk_hw_unregister_divider(data->clk_scaler);
scaler_error:
clk_hw_unregister_divider(data->clk_prescaler);
return ret;
}
static int aspeed_adc_remove(struct platform_device *pdev)
{
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
struct aspeed_adc_data *data = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
writel(ASPEED_OPERATION_MODE_POWER_DOWN,
data->base + ASPEED_REG_ENGINE_CONTROL);
clk_disable_unprepare(data->clk_scaler->clk);
clk_hw_unregister_divider(data->clk_scaler);
clk_hw_unregister_divider(data->clk_prescaler);
return 0;
}
static const struct aspeed_adc_model_data ast2400_model_data = {
.model_name = "ast2400-adc",
.vref_voltage = 2500, // mV
.min_sampling_rate = 10000,
.max_sampling_rate = 500000,
};
static const struct aspeed_adc_model_data ast2500_model_data = {
.model_name = "ast2500-adc",
.vref_voltage = 1800, // mV
.min_sampling_rate = 1,
.max_sampling_rate = 1000000,
};
static const struct of_device_id aspeed_adc_matches[] = {
{ .compatible = "aspeed,ast2400-adc", .data = &ast2400_model_data },
{ .compatible = "aspeed,ast2500-adc", .data = &ast2500_model_data },
{},
};
MODULE_DEVICE_TABLE(of, aspeed_adc_matches);
static struct platform_driver aspeed_adc_driver = {
.probe = aspeed_adc_probe,
.remove = aspeed_adc_remove,
.driver = {
.name = KBUILD_MODNAME,
.of_match_table = aspeed_adc_matches,
}
};
module_platform_driver(aspeed_adc_driver);
MODULE_AUTHOR("Rick Altherr <[email protected]>");
MODULE_DESCRIPTION("Aspeed AST2400/2500 ADC Driver");
MODULE_LICENSE("GPL");

1007
drivers/iio/adc/cpcap-adc.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -579,7 +579,7 @@ static int exynos_read_s3c64xx_ts(struct iio_dev *indio_dev, int *x, int *y)
static irqreturn_t exynos_adc_isr(int irq, void *dev_id)
{
struct exynos_adc *info = (struct exynos_adc *)dev_id;
struct exynos_adc *info = dev_id;
u32 mask = info->data->mask;
/* Read value */

View file

@ -369,7 +369,7 @@ static struct attribute *hx711_attributes[] = {
NULL,
};
static struct attribute_group hx711_attribute_group = {
static const struct attribute_group hx711_attribute_group = {
.attrs = hx711_attributes,
};

View file

@ -365,7 +365,7 @@ static int imx7d_adc_read_data(struct imx7d_adc *info)
static irqreturn_t imx7d_adc_isr(int irq, void *dev_id)
{
struct imx7d_adc *info = (struct imx7d_adc *)dev_id;
struct imx7d_adc *info = dev_id;
int status;
status = readl(info->regs + IMX7D_REG_ADC_INT_STATUS);

View file

@ -28,6 +28,7 @@
#include <linux/iio/sysfs.h>
#include <linux/kthread.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/regmap.h>
#include <linux/util_macros.h>
@ -635,6 +636,7 @@ static int ina2xx_probe(struct i2c_client *client,
struct iio_dev *indio_dev;
struct iio_buffer *buffer;
unsigned int val;
enum ina2xx_ids type;
int ret;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip));
@ -652,7 +654,11 @@ static int ina2xx_probe(struct i2c_client *client,
return PTR_ERR(chip->regmap);
}
chip->config = &ina2xx_config[id->driver_data];
if (client->dev.of_node)
type = (enum ina2xx_ids)of_device_get_match_data(&client->dev);
else
type = id->driver_data;
chip->config = &ina2xx_config[type];
mutex_init(&chip->state_lock);
@ -726,9 +732,35 @@ static const struct i2c_device_id ina2xx_id[] = {
};
MODULE_DEVICE_TABLE(i2c, ina2xx_id);
static const struct of_device_id ina2xx_of_match[] = {
{
.compatible = "ti,ina219",
.data = (void *)ina219
},
{
.compatible = "ti,ina220",
.data = (void *)ina219
},
{
.compatible = "ti,ina226",
.data = (void *)ina226
},
{
.compatible = "ti,ina230",
.data = (void *)ina226
},
{
.compatible = "ti,ina231",
.data = (void *)ina226
},
{},
};
MODULE_DEVICE_TABLE(of, ina2xx_of_match);
static struct i2c_driver ina2xx_driver = {
.driver = {
.name = KBUILD_MODNAME,
.of_match_table = ina2xx_of_match,
},
.probe = ina2xx_probe,
.remove = ina2xx_remove,

View file

@ -0,0 +1,219 @@
/*
* lpc32xx_adc.c - Support for ADC in LPC32XX
*
* 3-channel, 10-bit ADC
*
* Copyright (C) 2011, 2012 Roland Stigge <stigge@antcom.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/completion.h>
#include <linux/of.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
/*
* LPC32XX registers definitions
*/
#define LPC32XXAD_SELECT(x) ((x) + 0x04)
#define LPC32XXAD_CTRL(x) ((x) + 0x08)
#define LPC32XXAD_VALUE(x) ((x) + 0x48)
/* Bit definitions for LPC32XXAD_SELECT: */
/* constant, always write this value! */
#define LPC32XXAD_REFm 0x00000200
/* constant, always write this value! */
#define LPC32XXAD_REFp 0x00000080
/* multiple of this is the channel number: 0, 1, 2 */
#define LPC32XXAD_IN 0x00000010
/* constant, always write this value! */
#define LPC32XXAD_INTERNAL 0x00000004
/* Bit definitions for LPC32XXAD_CTRL: */
#define LPC32XXAD_STROBE 0x00000002
#define LPC32XXAD_PDN_CTRL 0x00000004
/* Bit definitions for LPC32XXAD_VALUE: */
#define LPC32XXAD_VALUE_MASK 0x000003FF
#define LPC32XXAD_NAME "lpc32xx-adc"
struct lpc32xx_adc_state {
void __iomem *adc_base;
struct clk *clk;
struct completion completion;
u32 value;
};
static int lpc32xx_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val,
int *val2,
long mask)
{
struct lpc32xx_adc_state *st = iio_priv(indio_dev);
if (mask == IIO_CHAN_INFO_RAW) {
mutex_lock(&indio_dev->mlock);
clk_prepare_enable(st->clk);
/* Measurement setup */
__raw_writel(LPC32XXAD_INTERNAL | (chan->address) |
LPC32XXAD_REFp | LPC32XXAD_REFm,
LPC32XXAD_SELECT(st->adc_base));
/* Trigger conversion */
__raw_writel(LPC32XXAD_PDN_CTRL | LPC32XXAD_STROBE,
LPC32XXAD_CTRL(st->adc_base));
wait_for_completion(&st->completion); /* set by ISR */
clk_disable_unprepare(st->clk);
*val = st->value;
mutex_unlock(&indio_dev->mlock);
return IIO_VAL_INT;
}
return -EINVAL;
}
static const struct iio_info lpc32xx_adc_iio_info = {
.read_raw = &lpc32xx_read_raw,
.driver_module = THIS_MODULE,
};
#define LPC32XX_ADC_CHANNEL(_index) { \
.type = IIO_VOLTAGE, \
.indexed = 1, \
.channel = _index, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.address = LPC32XXAD_IN * _index, \
.scan_index = _index, \
}
static const struct iio_chan_spec lpc32xx_adc_iio_channels[] = {
LPC32XX_ADC_CHANNEL(0),
LPC32XX_ADC_CHANNEL(1),
LPC32XX_ADC_CHANNEL(2),
};
static irqreturn_t lpc32xx_adc_isr(int irq, void *dev_id)
{
struct lpc32xx_adc_state *st = dev_id;
/* Read value and clear irq */
st->value = __raw_readl(LPC32XXAD_VALUE(st->adc_base)) &
LPC32XXAD_VALUE_MASK;
complete(&st->completion);
return IRQ_HANDLED;
}
static int lpc32xx_adc_probe(struct platform_device *pdev)
{
struct lpc32xx_adc_state *st = NULL;
struct resource *res;
int retval = -ENODEV;
struct iio_dev *iodev = NULL;
int irq;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev, "failed to get platform I/O memory\n");
return -ENXIO;
}
iodev = devm_iio_device_alloc(&pdev->dev, sizeof(*st));
if (!iodev)
return -ENOMEM;
st = iio_priv(iodev);
st->adc_base = devm_ioremap(&pdev->dev, res->start,
resource_size(res));
if (!st->adc_base) {
dev_err(&pdev->dev, "failed mapping memory\n");
return -EBUSY;
}
st->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(st->clk)) {
dev_err(&pdev->dev, "failed getting clock\n");
return PTR_ERR(st->clk);
}
irq = platform_get_irq(pdev, 0);
if (irq <= 0) {
dev_err(&pdev->dev, "failed getting interrupt resource\n");
return -ENXIO;
}
retval = devm_request_irq(&pdev->dev, irq, lpc32xx_adc_isr, 0,
LPC32XXAD_NAME, st);
if (retval < 0) {
dev_err(&pdev->dev, "failed requesting interrupt\n");
return retval;
}
platform_set_drvdata(pdev, iodev);
init_completion(&st->completion);
iodev->name = LPC32XXAD_NAME;
iodev->dev.parent = &pdev->dev;
iodev->info = &lpc32xx_adc_iio_info;
iodev->modes = INDIO_DIRECT_MODE;
iodev->channels = lpc32xx_adc_iio_channels;
iodev->num_channels = ARRAY_SIZE(lpc32xx_adc_iio_channels);
retval = devm_iio_device_register(&pdev->dev, iodev);
if (retval)
return retval;
dev_info(&pdev->dev, "LPC32XX ADC driver loaded, IRQ %d\n", irq);
return 0;
}
#ifdef CONFIG_OF
static const struct of_device_id lpc32xx_adc_match[] = {
{ .compatible = "nxp,lpc3220-adc" },
{},
};
MODULE_DEVICE_TABLE(of, lpc32xx_adc_match);
#endif
static struct platform_driver lpc32xx_adc_driver = {
.probe = lpc32xx_adc_probe,
.driver = {
.name = LPC32XXAD_NAME,
.of_match_table = of_match_ptr(lpc32xx_adc_match),
},
};
module_platform_driver(lpc32xx_adc_driver);
MODULE_AUTHOR("Roland Stigge <[email protected]>");
MODULE_DESCRIPTION("LPC32XX ADC driver");
MODULE_LICENSE("GPL");

279
drivers/iio/adc/ltc2497.c Normal file
View file

@ -0,0 +1,279 @@
/*
* ltc2497.c - Driver for Analog Devices/Linear Technology LTC2497 ADC
*
* Copyright (C) 2017 Analog Devices Inc.
*
* Licensed under the GPL-2.
*
* Datasheet: http://cds.linear.com/docs/en/datasheet/2497fd.pdf
*/
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/regulator/consumer.h>
#define LTC2497_ENABLE 0xA0
#define LTC2497_SGL BIT(4)
#define LTC2497_DIFF 0
#define LTC2497_SIGN BIT(3)
#define LTC2497_CONFIG_DEFAULT LTC2497_ENABLE
#define LTC2497_CONVERSION_TIME_MS 150ULL
struct ltc2497_st {
struct i2c_client *client;
struct regulator *ref;
ktime_t time_prev;
u8 addr_prev;
/*
* DMA (thus cache coherency maintenance) requires the
* transfer buffers to live in their own cache lines.
*/
__be32 buf ____cacheline_aligned;
};
static int ltc2497_wait_conv(struct ltc2497_st *st)
{
s64 time_elapsed;
time_elapsed = ktime_ms_delta(ktime_get(), st->time_prev);
if (time_elapsed < LTC2497_CONVERSION_TIME_MS) {
/* delay if conversion time not passed
* since last read or write
*/
if (msleep_interruptible(
LTC2497_CONVERSION_TIME_MS - time_elapsed))
return -ERESTARTSYS;
return 0;
}
if (time_elapsed - LTC2497_CONVERSION_TIME_MS <= 0) {
/* We're in automatic mode -
* so the last reading is stil not outdated
*/
return 0;
}
return 1;
}
static int ltc2497_read(struct ltc2497_st *st, u8 address, int *val)
{
struct i2c_client *client = st->client;
int ret;
ret = ltc2497_wait_conv(st);
if (ret < 0)
return ret;
if (ret || st->addr_prev != address) {
ret = i2c_smbus_write_byte(st->client,
LTC2497_ENABLE | address);
if (ret < 0)
return ret;
st->addr_prev = address;
if (msleep_interruptible(LTC2497_CONVERSION_TIME_MS))
return -ERESTARTSYS;
}
ret = i2c_master_recv(client, (char *)&st->buf, 3);
if (ret < 0) {
dev_err(&client->dev, "i2c_master_recv failed\n");
return ret;
}
st->time_prev = ktime_get();
/* convert and shift the result,
* and finally convert from offset binary to signed integer
*/
*val = (be32_to_cpu(st->buf) >> 14) - (1 << 17);
return ret;
}
static int ltc2497_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct ltc2497_st *st = iio_priv(indio_dev);
int ret;
switch (mask) {
case IIO_CHAN_INFO_RAW:
mutex_lock(&indio_dev->mlock);
ret = ltc2497_read(st, chan->address, val);
mutex_unlock(&indio_dev->mlock);
if (ret < 0)
return ret;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
ret = regulator_get_voltage(st->ref);
if (ret < 0)
return ret;
*val = ret / 1000;
*val2 = 17;
return IIO_VAL_FRACTIONAL_LOG2;
default:
return -EINVAL;
}
}
#define LTC2497_CHAN(_chan, _addr) { \
.type = IIO_VOLTAGE, \
.indexed = 1, \
.channel = (_chan), \
.address = (_addr | (_chan / 2) | ((_chan & 1) ? LTC2497_SIGN : 0)), \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
}
#define LTC2497_CHAN_DIFF(_chan, _addr) { \
.type = IIO_VOLTAGE, \
.indexed = 1, \
.channel = (_chan) * 2 + ((_addr) & LTC2497_SIGN ? 1 : 0), \
.channel2 = (_chan) * 2 + ((_addr) & LTC2497_SIGN ? 0 : 1),\
.address = (_addr | _chan), \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
.differential = 1, \
}
static const struct iio_chan_spec ltc2497_channel[] = {
LTC2497_CHAN(0, LTC2497_SGL),
LTC2497_CHAN(1, LTC2497_SGL),
LTC2497_CHAN(2, LTC2497_SGL),
LTC2497_CHAN(3, LTC2497_SGL),
LTC2497_CHAN(4, LTC2497_SGL),
LTC2497_CHAN(5, LTC2497_SGL),
LTC2497_CHAN(6, LTC2497_SGL),
LTC2497_CHAN(7, LTC2497_SGL),
LTC2497_CHAN(8, LTC2497_SGL),
LTC2497_CHAN(9, LTC2497_SGL),
LTC2497_CHAN(10, LTC2497_SGL),
LTC2497_CHAN(11, LTC2497_SGL),
LTC2497_CHAN(12, LTC2497_SGL),
LTC2497_CHAN(13, LTC2497_SGL),
LTC2497_CHAN(14, LTC2497_SGL),
LTC2497_CHAN(15, LTC2497_SGL),
LTC2497_CHAN_DIFF(0, LTC2497_DIFF),
LTC2497_CHAN_DIFF(1, LTC2497_DIFF),
LTC2497_CHAN_DIFF(2, LTC2497_DIFF),
LTC2497_CHAN_DIFF(3, LTC2497_DIFF),
LTC2497_CHAN_DIFF(4, LTC2497_DIFF),
LTC2497_CHAN_DIFF(5, LTC2497_DIFF),
LTC2497_CHAN_DIFF(6, LTC2497_DIFF),
LTC2497_CHAN_DIFF(7, LTC2497_DIFF),
LTC2497_CHAN_DIFF(0, LTC2497_DIFF | LTC2497_SIGN),
LTC2497_CHAN_DIFF(1, LTC2497_DIFF | LTC2497_SIGN),
LTC2497_CHAN_DIFF(2, LTC2497_DIFF | LTC2497_SIGN),
LTC2497_CHAN_DIFF(3, LTC2497_DIFF | LTC2497_SIGN),
LTC2497_CHAN_DIFF(4, LTC2497_DIFF | LTC2497_SIGN),
LTC2497_CHAN_DIFF(5, LTC2497_DIFF | LTC2497_SIGN),
LTC2497_CHAN_DIFF(6, LTC2497_DIFF | LTC2497_SIGN),
LTC2497_CHAN_DIFF(7, LTC2497_DIFF | LTC2497_SIGN),
};
static const struct iio_info ltc2497_info = {
.read_raw = ltc2497_read_raw,
.driver_module = THIS_MODULE,
};
static int ltc2497_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct iio_dev *indio_dev;
struct ltc2497_st *st;
int ret;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C |
I2C_FUNC_SMBUS_WRITE_BYTE))
return -EOPNOTSUPP;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*st));
if (!indio_dev)
return -ENOMEM;
st = iio_priv(indio_dev);
i2c_set_clientdata(client, indio_dev);
st->client = client;
indio_dev->dev.parent = &client->dev;
indio_dev->name = id->name;
indio_dev->info = &ltc2497_info;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = ltc2497_channel;
indio_dev->num_channels = ARRAY_SIZE(ltc2497_channel);
st->ref = devm_regulator_get(&client->dev, "vref");
if (IS_ERR(st->ref))
return PTR_ERR(st->ref);
ret = regulator_enable(st->ref);
if (ret < 0)
return ret;
ret = i2c_smbus_write_byte(st->client, LTC2497_CONFIG_DEFAULT);
if (ret < 0)
goto err_regulator_disable;
st->addr_prev = LTC2497_CONFIG_DEFAULT;
st->time_prev = ktime_get();
ret = iio_device_register(indio_dev);
if (ret < 0)
goto err_regulator_disable;
return 0;
err_regulator_disable:
regulator_disable(st->ref);
return ret;
}
static int ltc2497_remove(struct i2c_client *client)
{
struct iio_dev *indio_dev = i2c_get_clientdata(client);
struct ltc2497_st *st = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
regulator_disable(st->ref);
return 0;
}
static const struct i2c_device_id ltc2497_id[] = {
{ "ltc2497", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, ltc2497_id);
static const struct of_device_id ltc2497_of_match[] = {
{ .compatible = "lltc,ltc2497", },
{},
};
MODULE_DEVICE_TABLE(of, ltc2497_of_match);
static struct i2c_driver ltc2497_driver = {
.driver = {
.name = "ltc2497",
.of_match_table = of_match_ptr(ltc2497_of_match),
},
.probe = ltc2497_probe,
.remove = ltc2497_remove,
.id_table = ltc2497_id,
};
module_i2c_driver(ltc2497_driver);
MODULE_AUTHOR("Michael Hennerich <[email protected]>");
MODULE_DESCRIPTION("Linear Technology LTC2497 ADC driver");
MODULE_LICENSE("GPL v2");

View file

@ -364,7 +364,7 @@ static int max1027_set_trigger_state(struct iio_trigger *trig, bool state)
static irqreturn_t max1027_trigger_handler(int irq, void *private)
{
struct iio_poll_func *pf = (struct iio_poll_func *)private;
struct iio_poll_func *pf = private;
struct iio_dev *indio_dev = pf->indio_dev;
struct max1027_state *st = iio_priv(indio_dev);

View file

@ -124,8 +124,8 @@ static int max11100_probe(struct spi_device *spi)
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),
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))
@ -167,7 +167,6 @@ 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,

307
drivers/iio/adc/max1118.c Normal file
View file

@ -0,0 +1,307 @@
/*
* MAX1117/MAX1118/MAX1119 8-bit, dual-channel ADCs driver
*
* Copyright (c) 2017 Akinobu Mita <akinobu.mita@gmail.com>
*
* This file is subject to the terms and conditions of version 2 of
* the GNU General Public License. See the file COPYING in the main
* directory of this archive for more details.
*
* Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX1117-MAX1119.pdf
*
* SPI interface connections
*
* SPI MAXIM
* Master Direction MAX1117/8/9
* ------ --------- -----------
* nCS --> CNVST
* SCK --> SCLK
* MISO <-- DOUT
* ------ --------- -----------
*/
#include <linux/module.h>
#include <linux/spi/spi.h>
#include <linux/iio/iio.h>
#include <linux/iio/buffer.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/regulator/consumer.h>
enum max1118_id {
max1117,
max1118,
max1119,
};
struct max1118 {
struct spi_device *spi;
struct mutex lock;
struct regulator *reg;
u8 data ____cacheline_aligned;
};
#define MAX1118_CHANNEL(ch) \
{ \
.type = IIO_VOLTAGE, \
.indexed = 1, \
.channel = (ch), \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
.scan_index = ch, \
.scan_type = { \
.sign = 'u', \
.realbits = 8, \
.storagebits = 8, \
}, \
}
static const struct iio_chan_spec max1118_channels[] = {
MAX1118_CHANNEL(0),
MAX1118_CHANNEL(1),
IIO_CHAN_SOFT_TIMESTAMP(2),
};
static int max1118_read(struct spi_device *spi, int channel)
{
struct iio_dev *indio_dev = spi_get_drvdata(spi);
struct max1118 *adc = iio_priv(indio_dev);
struct spi_transfer xfers[] = {
/*
* To select CH1 for conversion, CNVST pin must be brought high
* and low for a second time.
*/
{
.len = 0,
.delay_usecs = 1, /* > CNVST Low Time 100 ns */
.cs_change = 1,
},
/*
* The acquisition interval begins with the falling edge of
* CNVST. The total acquisition and conversion process takes
* <7.5us.
*/
{
.len = 0,
.delay_usecs = 8,
},
{
.rx_buf = &adc->data,
.len = 1,
},
};
int ret;
if (channel == 0)
ret = spi_sync_transfer(spi, xfers + 1, 2);
else
ret = spi_sync_transfer(spi, xfers, 3);
if (ret)
return ret;
return adc->data;
}
static int max1118_get_vref_mV(struct spi_device *spi)
{
struct iio_dev *indio_dev = spi_get_drvdata(spi);
struct max1118 *adc = iio_priv(indio_dev);
const struct spi_device_id *id = spi_get_device_id(spi);
int vref_uV;
switch (id->driver_data) {
case max1117:
return 2048;
case max1119:
return 4096;
case max1118:
vref_uV = regulator_get_voltage(adc->reg);
if (vref_uV < 0)
return vref_uV;
return vref_uV / 1000;
}
return -ENODEV;
}
static int max1118_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct max1118 *adc = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_RAW:
mutex_lock(&adc->lock);
*val = max1118_read(adc->spi, chan->channel);
mutex_unlock(&adc->lock);
if (*val < 0)
return *val;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
*val = max1118_get_vref_mV(adc->spi);
if (*val < 0)
return *val;
*val2 = 8;
return IIO_VAL_FRACTIONAL_LOG2;
}
return -EINVAL;
}
static const struct iio_info max1118_info = {
.read_raw = max1118_read_raw,
.driver_module = THIS_MODULE,
};
static irqreturn_t max1118_trigger_handler(int irq, void *p)
{
struct iio_poll_func *pf = p;
struct iio_dev *indio_dev = pf->indio_dev;
struct max1118 *adc = iio_priv(indio_dev);
u8 data[16] = { }; /* 2x 8-bit ADC data + padding + 8 bytes timestamp */
int scan_index;
int i = 0;
mutex_lock(&adc->lock);
for_each_set_bit(scan_index, indio_dev->active_scan_mask,
indio_dev->masklength) {
const struct iio_chan_spec *scan_chan =
&indio_dev->channels[scan_index];
int ret = max1118_read(adc->spi, scan_chan->channel);
if (ret < 0) {
dev_warn(&adc->spi->dev,
"failed to get conversion data\n");
goto out;
}
data[i] = ret;
i++;
}
iio_push_to_buffers_with_timestamp(indio_dev, data,
iio_get_time_ns(indio_dev));
out:
mutex_unlock(&adc->lock);
iio_trigger_notify_done(indio_dev->trig);
return IRQ_HANDLED;
}
static int max1118_probe(struct spi_device *spi)
{
struct iio_dev *indio_dev;
struct max1118 *adc;
const struct spi_device_id *id = spi_get_device_id(spi);
int ret;
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc));
if (!indio_dev)
return -ENOMEM;
adc = iio_priv(indio_dev);
adc->spi = spi;
mutex_init(&adc->lock);
if (id->driver_data == max1118) {
adc->reg = devm_regulator_get(&spi->dev, "vref");
if (IS_ERR(adc->reg)) {
dev_err(&spi->dev, "failed to get vref regulator\n");
return PTR_ERR(adc->reg);
}
ret = regulator_enable(adc->reg);
if (ret)
return ret;
}
spi_set_drvdata(spi, indio_dev);
indio_dev->name = spi_get_device_id(spi)->name;
indio_dev->dev.parent = &spi->dev;
indio_dev->info = &max1118_info;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = max1118_channels;
indio_dev->num_channels = ARRAY_SIZE(max1118_channels);
/*
* To reinitiate a conversion on CH0, it is necessary to allow for a
* conversion to be complete and all of the data to be read out. Once
* a conversion has been completed, the MAX1117/MAX1118/MAX1119 will go
* into AutoShutdown mode until the next conversion is initiated.
*/
max1118_read(spi, 0);
ret = iio_triggered_buffer_setup(indio_dev, NULL,
max1118_trigger_handler, NULL);
if (ret)
goto err_reg_disable;
ret = iio_device_register(indio_dev);
if (ret)
goto err_buffer_cleanup;
return 0;
err_buffer_cleanup:
iio_triggered_buffer_cleanup(indio_dev);
err_reg_disable:
if (id->driver_data == max1118)
regulator_disable(adc->reg);
return ret;
}
static int max1118_remove(struct spi_device *spi)
{
struct iio_dev *indio_dev = spi_get_drvdata(spi);
struct max1118 *adc = iio_priv(indio_dev);
const struct spi_device_id *id = spi_get_device_id(spi);
iio_device_unregister(indio_dev);
iio_triggered_buffer_cleanup(indio_dev);
if (id->driver_data == max1118)
return regulator_disable(adc->reg);
return 0;
}
static const struct spi_device_id max1118_id[] = {
{ "max1117", max1117 },
{ "max1118", max1118 },
{ "max1119", max1119 },
{}
};
MODULE_DEVICE_TABLE(spi, max1118_id);
#ifdef CONFIG_OF
static const struct of_device_id max1118_dt_ids[] = {
{ .compatible = "maxim,max1117" },
{ .compatible = "maxim,max1118" },
{ .compatible = "maxim,max1119" },
{},
};
MODULE_DEVICE_TABLE(of, max1118_dt_ids);
#endif
static struct spi_driver max1118_spi_driver = {
.driver = {
.name = "max1118",
.of_match_table = of_match_ptr(max1118_dt_ids),
},
.probe = max1118_probe,
.remove = max1118_remove,
.id_table = max1118_id,
};
module_spi_driver(max1118_spi_driver);
MODULE_AUTHOR("Akinobu Mita <[email protected]>");
MODULE_DESCRIPTION("MAXIM MAX1117/MAX1118/MAX1119 ADCs driver");
MODULE_LICENSE("GPL v2");

View file

@ -1007,7 +1007,7 @@ static struct attribute *max1363_event_attributes[] = {
NULL,
};
static struct attribute_group max1363_event_attribute_group = {
static const struct attribute_group max1363_event_attribute_group = {
.attrs = max1363_event_attributes,
};

585
drivers/iio/adc/max9611.c Normal file
View file

@ -0,0 +1,585 @@
/*
* iio/adc/max9611.c
*
* Maxim max9611/max9612 high side current sense amplifier with
* 12-bit ADC interface.
*
* Copyright (C) 2017 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.
*/
/*
* This driver supports input common-mode voltage, current-sense
* amplifier with programmable gains and die temperature reading from
* Maxim max9611/max9612.
*
* Op-amp, analog comparator, and watchdog functionalities are not
* supported by this driver.
*/
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/module.h>
#include <linux/of_device.h>
#define DRIVER_NAME "max9611"
/* max9611 register addresses */
#define MAX9611_REG_CSA_DATA 0x00
#define MAX9611_REG_RS_DATA 0x02
#define MAX9611_REG_TEMP_DATA 0x08
#define MAX9611_REG_CTRL1 0x0a
#define MAX9611_REG_CTRL2 0x0b
/* max9611 REG1 mux configuration options */
#define MAX9611_MUX_MASK GENMASK(3, 0)
#define MAX9611_MUX_SENSE_1x 0x00
#define MAX9611_MUX_SENSE_4x 0x01
#define MAX9611_MUX_SENSE_8x 0x02
#define MAX9611_INPUT_VOLT 0x03
#define MAX9611_MUX_TEMP 0x06
/* max9611 voltage (both csa and input) helper macros */
#define MAX9611_VOLTAGE_SHIFT 0x04
#define MAX9611_VOLTAGE_RAW(_r) ((_r) >> MAX9611_VOLTAGE_SHIFT)
/*
* max9611 current sense amplifier voltage output:
* LSB and offset values depends on selected gain (1x, 4x, 8x)
*
* GAIN LSB (nV) OFFSET (LSB steps)
* 1x 107500 1
* 4x 26880 1
* 8x 13440 3
*
* The complete formula to calculate current sense voltage is:
* (((adc_read >> 4) - offset) / ((1 / LSB) * 10^-3)
*/
#define MAX9611_CSA_1X_LSB_nV 107500
#define MAX9611_CSA_4X_LSB_nV 26880
#define MAX9611_CSA_8X_LSB_nV 13440
#define MAX9611_CSA_1X_OFFS_RAW 1
#define MAX9611_CSA_4X_OFFS_RAW 1
#define MAX9611_CSA_8X_OFFS_RAW 3
/*
* max9611 common input mode (CIM): LSB is 14mV, with 14mV offset at 25 C
*
* The complete formula to calculate input common voltage is:
* (((adc_read >> 4) * 1000) - offset) / (1 / 14 * 1000)
*/
#define MAX9611_CIM_LSB_mV 14
#define MAX9611_CIM_OFFSET_RAW 1
/*
* max9611 temperature reading: LSB is 480 milli degrees Celsius
*
* The complete formula to calculate temperature is:
* ((adc_read >> 7) * 1000) / (1 / 480 * 1000)
*/
#define MAX9611_TEMP_MAX_POS 0x7f80
#define MAX9611_TEMP_MAX_NEG 0xff80
#define MAX9611_TEMP_MIN_NEG 0xd980
#define MAX9611_TEMP_MASK GENMASK(7, 15)
#define MAX9611_TEMP_SHIFT 0x07
#define MAX9611_TEMP_RAW(_r) ((_r) >> MAX9611_TEMP_SHIFT)
#define MAX9611_TEMP_SCALE_NUM 1000000
#define MAX9611_TEMP_SCALE_DIV 2083
struct max9611_dev {
struct device *dev;
struct i2c_client *i2c_client;
struct mutex lock;
unsigned int shunt_resistor_uohm;
};
enum max9611_conf_ids {
CONF_SENSE_1x,
CONF_SENSE_4x,
CONF_SENSE_8x,
CONF_IN_VOLT,
CONF_TEMP,
};
/**
* max9611_mux_conf - associate ADC mux configuration with register address
* where data shall be read from
*/
static const unsigned int max9611_mux_conf[][2] = {
/* CONF_SENSE_1x */
{ MAX9611_MUX_SENSE_1x, MAX9611_REG_CSA_DATA },
/* CONF_SENSE_4x */
{ MAX9611_MUX_SENSE_4x, MAX9611_REG_CSA_DATA },
/* CONF_SENSE_8x */
{ MAX9611_MUX_SENSE_8x, MAX9611_REG_CSA_DATA },
/* CONF_IN_VOLT */
{ MAX9611_INPUT_VOLT, MAX9611_REG_RS_DATA },
/* CONF_TEMP */
{ MAX9611_MUX_TEMP, MAX9611_REG_TEMP_DATA },
};
enum max9611_csa_gain {
CSA_GAIN_1x,
CSA_GAIN_4x,
CSA_GAIN_8x,
};
enum max9611_csa_gain_params {
CSA_GAIN_LSB_nV,
CSA_GAIN_OFFS_RAW,
};
/**
* max9611_csa_gain_conf - associate gain multiplier with LSB and
* offset values.
*
* Group together parameters associated with configurable gain
* on current sense amplifier path to ADC interface.
* Current sense read routine adjusts gain until it gets a meaningful
* value; use this structure to retrieve the correct LSB and offset values.
*/
static const unsigned int max9611_gain_conf[][2] = {
{ /* [0] CSA_GAIN_1x */
MAX9611_CSA_1X_LSB_nV,
MAX9611_CSA_1X_OFFS_RAW,
},
{ /* [1] CSA_GAIN_4x */
MAX9611_CSA_4X_LSB_nV,
MAX9611_CSA_4X_OFFS_RAW,
},
{ /* [2] CSA_GAIN_8x */
MAX9611_CSA_8X_LSB_nV,
MAX9611_CSA_8X_OFFS_RAW,
},
};
enum max9611_chan_addrs {
MAX9611_CHAN_VOLTAGE_INPUT,
MAX9611_CHAN_VOLTAGE_SENSE,
MAX9611_CHAN_TEMPERATURE,
MAX9611_CHAN_CURRENT_LOAD,
MAX9611_CHAN_POWER_LOAD,
};
static const struct iio_chan_spec max9611_channels[] = {
{
.type = IIO_TEMP,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE),
.address = MAX9611_CHAN_TEMPERATURE,
},
{
.type = IIO_VOLTAGE,
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
.address = MAX9611_CHAN_VOLTAGE_SENSE,
.indexed = 1,
.channel = 0,
},
{
.type = IIO_VOLTAGE,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_OFFSET),
.address = MAX9611_CHAN_VOLTAGE_INPUT,
.indexed = 1,
.channel = 1,
},
{
.type = IIO_CURRENT,
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
.address = MAX9611_CHAN_CURRENT_LOAD,
},
{
.type = IIO_POWER,
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
.address = MAX9611_CHAN_POWER_LOAD
},
};
/**
* max9611_read_single() - read a single value from ADC interface
*
* Data registers are 16 bit long, spread between two 8 bit registers
* with consecutive addresses.
* Configure ADC mux first, then read register at address "reg_addr".
* The smbus_read_word routine asks for 16 bits and the ADC is kind enough
* to return values from "reg_addr" and "reg_addr + 1" consecutively.
* Data are transmitted with big-endian ordering: MSB arrives first.
*
* @max9611: max9611 device
* @selector: index for mux and register configuration
* @raw_val: the value returned from ADC
*/
static int max9611_read_single(struct max9611_dev *max9611,
enum max9611_conf_ids selector,
u16 *raw_val)
{
int ret;
u8 mux_conf = max9611_mux_conf[selector][0] & MAX9611_MUX_MASK;
u8 reg_addr = max9611_mux_conf[selector][1];
/*
* Keep mutex lock held during read-write to avoid mux register
* (CTRL1) re-configuration.
*/
mutex_lock(&max9611->lock);
ret = i2c_smbus_write_byte_data(max9611->i2c_client,
MAX9611_REG_CTRL1, mux_conf);
if (ret) {
dev_err(max9611->dev, "i2c write byte failed: 0x%2x - 0x%2x\n",
MAX9611_REG_CTRL1, mux_conf);
mutex_unlock(&max9611->lock);
return ret;
}
/*
* need a delay here to make register configuration
* stabilize. 1 msec at least, from empirical testing.
*/
usleep_range(1000, 2000);
ret = i2c_smbus_read_word_swapped(max9611->i2c_client, reg_addr);
if (ret < 0) {
dev_err(max9611->dev, "i2c read word from 0x%2x failed\n",
reg_addr);
mutex_unlock(&max9611->lock);
return ret;
}
*raw_val = ret;
mutex_unlock(&max9611->lock);
return 0;
}
/**
* max9611_read_csa_voltage() - read current sense amplifier output voltage
*
* Current sense amplifier output voltage is read through a configurable
* 1x, 4x or 8x gain.
* Start with plain 1x gain, and adjust gain control properly until a
* meaningful value is read from ADC output.
*
* @max9611: max9611 device
* @adc_raw: raw value read from ADC output
* @csa_gain: gain configuration option selector
*/
static int max9611_read_csa_voltage(struct max9611_dev *max9611,
u16 *adc_raw,
enum max9611_csa_gain *csa_gain)
{
enum max9611_conf_ids gain_selectors[] = {
CONF_SENSE_1x,
CONF_SENSE_4x,
CONF_SENSE_8x
};
unsigned int i;
int ret;
for (i = 0; i < ARRAY_SIZE(gain_selectors); ++i) {
ret = max9611_read_single(max9611, gain_selectors[i], adc_raw);
if (ret)
return ret;
if (*adc_raw > 0) {
*csa_gain = gain_selectors[i];
return 0;
}
}
return -EIO;
}
static int max9611_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct max9611_dev *dev = iio_priv(indio_dev);
enum max9611_csa_gain gain_selector;
const unsigned int *csa_gain;
u16 adc_data;
int ret;
switch (mask) {
case IIO_CHAN_INFO_RAW:
switch (chan->address) {
case MAX9611_CHAN_TEMPERATURE:
ret = max9611_read_single(dev, CONF_TEMP,
&adc_data);
if (ret)
return -EINVAL;
*val = MAX9611_TEMP_RAW(adc_data);
return IIO_VAL_INT;
case MAX9611_CHAN_VOLTAGE_INPUT:
ret = max9611_read_single(dev, CONF_IN_VOLT,
&adc_data);
if (ret)
return -EINVAL;
*val = MAX9611_VOLTAGE_RAW(adc_data);
return IIO_VAL_INT;
}
break;
case IIO_CHAN_INFO_OFFSET:
/* MAX9611_CHAN_VOLTAGE_INPUT */
*val = MAX9611_CIM_OFFSET_RAW;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
switch (chan->address) {
case MAX9611_CHAN_TEMPERATURE:
*val = MAX9611_TEMP_SCALE_NUM;
*val2 = MAX9611_TEMP_SCALE_DIV;
return IIO_VAL_FRACTIONAL;
case MAX9611_CHAN_VOLTAGE_INPUT:
*val = MAX9611_CIM_LSB_mV;
return IIO_VAL_INT;
}
break;
case IIO_CHAN_INFO_PROCESSED:
switch (chan->address) {
case MAX9611_CHAN_VOLTAGE_SENSE:
/*
* processed (mV): (raw - offset) * LSB (nV) / 10^6
*
* Even if max9611 can output raw csa voltage readings,
* use a produced value as scale depends on gain.
*/
ret = max9611_read_csa_voltage(dev, &adc_data,
&gain_selector);
if (ret)
return -EINVAL;
csa_gain = max9611_gain_conf[gain_selector];
adc_data -= csa_gain[CSA_GAIN_OFFS_RAW];
*val = MAX9611_VOLTAGE_RAW(adc_data) *
csa_gain[CSA_GAIN_LSB_nV];
*val2 = 1000000;
return IIO_VAL_FRACTIONAL;
case MAX9611_CHAN_CURRENT_LOAD:
/* processed (mA): Vcsa (nV) / Rshunt (uOhm) */
ret = max9611_read_csa_voltage(dev, &adc_data,
&gain_selector);
if (ret)
return -EINVAL;
csa_gain = max9611_gain_conf[gain_selector];
adc_data -= csa_gain[CSA_GAIN_OFFS_RAW];
*val = MAX9611_VOLTAGE_RAW(adc_data) *
csa_gain[CSA_GAIN_LSB_nV];
*val2 = dev->shunt_resistor_uohm;
return IIO_VAL_FRACTIONAL;
case MAX9611_CHAN_POWER_LOAD:
/*
* processed (mW): Vin (mV) * Vcsa (uV) /
* Rshunt (uOhm)
*/
ret = max9611_read_single(dev, CONF_IN_VOLT,
&adc_data);
if (ret)
return -EINVAL;
adc_data -= MAX9611_CIM_OFFSET_RAW;
*val = MAX9611_VOLTAGE_RAW(adc_data) *
MAX9611_CIM_LSB_mV;
ret = max9611_read_csa_voltage(dev, &adc_data,
&gain_selector);
if (ret)
return -EINVAL;
csa_gain = max9611_gain_conf[gain_selector];
/* divide by 10^3 here to avoid 32bit overflow */
adc_data -= csa_gain[CSA_GAIN_OFFS_RAW];
*val *= MAX9611_VOLTAGE_RAW(adc_data) *
csa_gain[CSA_GAIN_LSB_nV] / 1000;
*val2 = dev->shunt_resistor_uohm;
return IIO_VAL_FRACTIONAL;
}
break;
}
return -EINVAL;
}
static ssize_t max9611_shunt_resistor_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct max9611_dev *max9611 = iio_priv(dev_to_iio_dev(dev));
unsigned int i, r;
i = max9611->shunt_resistor_uohm / 1000;
r = max9611->shunt_resistor_uohm % 1000;
return sprintf(buf, "%u.%03u\n", i, r);
}
static IIO_DEVICE_ATTR(in_power_shunt_resistor, 0444,
max9611_shunt_resistor_show, NULL, 0);
static IIO_DEVICE_ATTR(in_current_shunt_resistor, 0444,
max9611_shunt_resistor_show, NULL, 0);
static struct attribute *max9611_attributes[] = {
&iio_dev_attr_in_power_shunt_resistor.dev_attr.attr,
&iio_dev_attr_in_current_shunt_resistor.dev_attr.attr,
NULL,
};
static const struct attribute_group max9611_attribute_group = {
.attrs = max9611_attributes,
};
static const struct iio_info indio_info = {
.driver_module = THIS_MODULE,
.read_raw = max9611_read_raw,
.attrs = &max9611_attribute_group,
};
static int max9611_init(struct max9611_dev *max9611)
{
struct i2c_client *client = max9611->i2c_client;
u16 regval;
int ret;
if (!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_WRITE_BYTE |
I2C_FUNC_SMBUS_READ_WORD_DATA)) {
dev_err(max9611->dev,
"I2c adapter does not support smbus write_byte or read_word functionalities: aborting probe.\n");
return -EINVAL;
}
/* Make sure die temperature is in range to test communications. */
ret = max9611_read_single(max9611, CONF_TEMP, &regval);
if (ret)
return ret;
regval = ret & MAX9611_TEMP_MASK;
if ((regval > MAX9611_TEMP_MAX_POS &&
regval < MAX9611_TEMP_MIN_NEG) ||
regval > MAX9611_TEMP_MAX_NEG) {
dev_err(max9611->dev,
"Invalid value received from ADC 0x%4x: aborting\n",
regval);
return -EIO;
}
/* Mux shall be zeroed back before applying other configurations */
ret = i2c_smbus_write_byte_data(max9611->i2c_client,
MAX9611_REG_CTRL1, 0);
if (ret) {
dev_err(max9611->dev, "i2c write byte failed: 0x%2x - 0x%2x\n",
MAX9611_REG_CTRL1, 0);
return ret;
}
ret = i2c_smbus_write_byte_data(max9611->i2c_client,
MAX9611_REG_CTRL2, 0);
if (ret) {
dev_err(max9611->dev, "i2c write byte failed: 0x%2x - 0x%2x\n",
MAX9611_REG_CTRL2, 0);
return ret;
}
usleep_range(1000, 2000);
return 0;
}
static const struct of_device_id max9611_of_table[] = {
{.compatible = "maxim,max9611", .data = "max9611"},
{.compatible = "maxim,max9612", .data = "max9612"},
{ },
};
MODULE_DEVICE_TABLE(of, max9611_of_table);
static int max9611_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
const char * const shunt_res_prop = "shunt-resistor-micro-ohms";
const struct device_node *of_node = client->dev.of_node;
const struct of_device_id *of_id =
of_match_device(max9611_of_table, &client->dev);
struct max9611_dev *max9611;
struct iio_dev *indio_dev;
unsigned int of_shunt;
int ret;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*max9611));
if (IS_ERR(indio_dev))
return PTR_ERR(indio_dev);
i2c_set_clientdata(client, indio_dev);
max9611 = iio_priv(indio_dev);
max9611->dev = &client->dev;
max9611->i2c_client = client;
mutex_init(&max9611->lock);
ret = of_property_read_u32(of_node, shunt_res_prop, &of_shunt);
if (ret) {
dev_err(&client->dev,
"Missing %s property for %s node\n",
shunt_res_prop, of_node->full_name);
return ret;
}
max9611->shunt_resistor_uohm = of_shunt;
ret = max9611_init(max9611);
if (ret)
return ret;
indio_dev->dev.parent = &client->dev;
indio_dev->dev.of_node = client->dev.of_node;
indio_dev->name = of_id->data;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->info = &indio_info;
indio_dev->channels = max9611_channels;
indio_dev->num_channels = ARRAY_SIZE(max9611_channels);
return devm_iio_device_register(&client->dev, indio_dev);
}
static struct i2c_driver max9611_driver = {
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
.of_match_table = max9611_of_table,
},
.probe = max9611_probe,
};
module_i2c_driver(max9611_driver);
MODULE_AUTHOR("Jacopo Mondi <[email protected]>");
MODULE_DESCRIPTION("Maxim max9611/12 current sense amplifier with 12bit ADC");
MODULE_LICENSE("GPL v2");

View file

@ -18,7 +18,9 @@
#include <linux/io.h>
#include <linux/iio/iio.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
@ -163,6 +165,9 @@
#define MESON_SAR_ADC_REG13_12BIT_CALIBRATION_MASK GENMASK(13, 8)
#define MESON_SAR_ADC_MAX_FIFO_SIZE 32
#define MESON_SAR_ADC_TIMEOUT 100 /* ms */
/* for use with IIO_VAL_INT_PLUS_MICRO */
#define MILLION 1000000
#define MESON_SAR_ADC_CHAN(_chan) { \
.type = IIO_VOLTAGE, \
@ -170,7 +175,9 @@
.channel = _chan, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
BIT(IIO_CHAN_INFO_AVERAGE_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
BIT(IIO_CHAN_INFO_CALIBBIAS) | \
BIT(IIO_CHAN_INFO_CALIBSCALE), \
.datasheet_name = "SAR_ADC_CH"#_chan, \
}
@ -229,6 +236,9 @@ struct meson_sar_adc_priv {
struct clk_gate clk_gate;
struct clk *adc_div_clk;
struct clk_divider clk_div;
struct completion done;
int calibbias;
int calibscale;
};
static const struct regmap_config meson_sar_adc_regmap_config = {
@ -248,6 +258,17 @@ static unsigned int meson_sar_adc_get_fifo_count(struct iio_dev *indio_dev)
return FIELD_GET(MESON_SAR_ADC_REG0_FIFO_COUNT_MASK, regval);
}
static int meson_sar_adc_calib_val(struct iio_dev *indio_dev, int val)
{
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
int tmp;
/* use val_calib = scale * val_raw + offset calibration function */
tmp = div_s64((s64)val * priv->calibscale, MILLION) + priv->calibbias;
return clamp(tmp, 0, (1 << priv->data->resolution) - 1);
}
static int meson_sar_adc_wait_busy_clear(struct iio_dev *indio_dev)
{
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
@ -274,33 +295,31 @@ static int meson_sar_adc_read_raw_sample(struct iio_dev *indio_dev,
int *val)
{
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
int ret, regval, fifo_chan, fifo_val, sum = 0, count = 0;
int regval, fifo_chan, fifo_val, count;
ret = meson_sar_adc_wait_busy_clear(indio_dev);
if (ret)
return ret;
if(!wait_for_completion_timeout(&priv->done,
msecs_to_jiffies(MESON_SAR_ADC_TIMEOUT)))
return -ETIMEDOUT;
while (meson_sar_adc_get_fifo_count(indio_dev) > 0 &&
count < MESON_SAR_ADC_MAX_FIFO_SIZE) {
regmap_read(priv->regmap, MESON_SAR_ADC_FIFO_RD, &regval);
fifo_chan = FIELD_GET(MESON_SAR_ADC_FIFO_RD_CHAN_ID_MASK,
regval);
if (fifo_chan != chan->channel)
continue;
fifo_val = FIELD_GET(MESON_SAR_ADC_FIFO_RD_SAMPLE_VALUE_MASK,
regval);
fifo_val &= (BIT(priv->data->resolution) - 1);
sum += fifo_val;
count++;
count = meson_sar_adc_get_fifo_count(indio_dev);
if (count != 1) {
dev_err(&indio_dev->dev,
"ADC FIFO has %d element(s) instead of one\n", count);
return -EINVAL;
}
if (!count)
return -ENOENT;
regmap_read(priv->regmap, MESON_SAR_ADC_FIFO_RD, &regval);
fifo_chan = FIELD_GET(MESON_SAR_ADC_FIFO_RD_CHAN_ID_MASK, regval);
if (fifo_chan != chan->channel) {
dev_err(&indio_dev->dev,
"ADC FIFO entry belongs to channel %d instead of %d\n",
fifo_chan, chan->channel);
return -EINVAL;
}
*val = sum / count;
fifo_val = FIELD_GET(MESON_SAR_ADC_FIFO_RD_SAMPLE_VALUE_MASK, regval);
fifo_val &= GENMASK(priv->data->resolution - 1, 0);
*val = meson_sar_adc_calib_val(indio_dev, fifo_val);
return 0;
}
@ -378,6 +397,12 @@ static void meson_sar_adc_start_sample_engine(struct iio_dev *indio_dev)
{
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
reinit_completion(&priv->done);
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0,
MESON_SAR_ADC_REG0_FIFO_IRQ_EN,
MESON_SAR_ADC_REG0_FIFO_IRQ_EN);
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0,
MESON_SAR_ADC_REG0_SAMPLE_ENGINE_ENABLE,
MESON_SAR_ADC_REG0_SAMPLE_ENGINE_ENABLE);
@ -391,6 +416,9 @@ static void meson_sar_adc_stop_sample_engine(struct iio_dev *indio_dev)
{
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0,
MESON_SAR_ADC_REG0_FIFO_IRQ_EN, 0);
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0,
MESON_SAR_ADC_REG0_SAMPLING_STOP,
MESON_SAR_ADC_REG0_SAMPLING_STOP);
@ -516,6 +544,15 @@ static int meson_sar_adc_iio_info_read_raw(struct iio_dev *indio_dev,
*val2 = priv->data->resolution;
return IIO_VAL_FRACTIONAL_LOG2;
case IIO_CHAN_INFO_CALIBBIAS:
*val = priv->calibbias;
return IIO_VAL_INT;
case IIO_CHAN_INFO_CALIBSCALE:
*val = priv->calibscale / MILLION;
*val2 = priv->calibscale % MILLION;
return IIO_VAL_INT_PLUS_MICRO;
default:
return -EINVAL;
}
@ -643,6 +680,7 @@ static int meson_sar_adc_hw_enable(struct iio_dev *indio_dev)
{
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
int ret;
u32 regval;
ret = meson_sar_adc_lock(indio_dev);
if (ret)
@ -667,6 +705,9 @@ static int meson_sar_adc_hw_enable(struct iio_dev *indio_dev)
goto err_sana_clk;
}
regval = FIELD_PREP(MESON_SAR_ADC_REG0_FIFO_CNT_IRQ_MASK, 1);
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0,
MESON_SAR_ADC_REG0_FIFO_CNT_IRQ_MASK, regval);
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG11,
MESON_SAR_ADC_REG11_BANDGAP_EN,
MESON_SAR_ADC_REG11_BANDGAP_EN);
@ -728,6 +769,66 @@ static int meson_sar_adc_hw_disable(struct iio_dev *indio_dev)
return 0;
}
static irqreturn_t meson_sar_adc_irq(int irq, void *data)
{
struct iio_dev *indio_dev = data;
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
unsigned int cnt, threshold;
u32 regval;
regmap_read(priv->regmap, MESON_SAR_ADC_REG0, &regval);
cnt = FIELD_GET(MESON_SAR_ADC_REG0_FIFO_COUNT_MASK, regval);
threshold = FIELD_GET(MESON_SAR_ADC_REG0_FIFO_CNT_IRQ_MASK, regval);
if (cnt < threshold)
return IRQ_NONE;
complete(&priv->done);
return IRQ_HANDLED;
}
static int meson_sar_adc_calib(struct iio_dev *indio_dev)
{
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
int ret, nominal0, nominal1, value0, value1;
/* use points 25% and 75% for calibration */
nominal0 = (1 << priv->data->resolution) / 4;
nominal1 = (1 << priv->data->resolution) * 3 / 4;
meson_sar_adc_set_chan7_mux(indio_dev, CHAN7_MUX_VDD_DIV4);
usleep_range(10, 20);
ret = meson_sar_adc_get_sample(indio_dev,
&meson_sar_adc_iio_channels[7],
MEAN_AVERAGING, EIGHT_SAMPLES, &value0);
if (ret < 0)
goto out;
meson_sar_adc_set_chan7_mux(indio_dev, CHAN7_MUX_VDD_MUL3_DIV4);
usleep_range(10, 20);
ret = meson_sar_adc_get_sample(indio_dev,
&meson_sar_adc_iio_channels[7],
MEAN_AVERAGING, EIGHT_SAMPLES, &value1);
if (ret < 0)
goto out;
if (value1 <= value0) {
ret = -EINVAL;
goto out;
}
priv->calibscale = div_s64((nominal1 - nominal0) * (s64)MILLION,
value1 - value0);
priv->calibbias = nominal0 - div_s64((s64)value0 * priv->calibscale,
MILLION);
ret = 0;
out:
meson_sar_adc_set_chan7_mux(indio_dev, CHAN7_MUX_CH7_INPUT);
return ret;
}
static const struct iio_info meson_sar_adc_iio_info = {
.read_raw = meson_sar_adc_iio_info_read_raw,
.driver_module = THIS_MODULE,
@ -770,7 +871,7 @@ static int meson_sar_adc_probe(struct platform_device *pdev)
struct resource *res;
void __iomem *base;
const struct of_device_id *match;
int ret;
int irq, ret;
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*priv));
if (!indio_dev) {
@ -779,6 +880,7 @@ static int meson_sar_adc_probe(struct platform_device *pdev)
}
priv = iio_priv(indio_dev);
init_completion(&priv->done);
match = of_match_device(meson_sar_adc_of_match, &pdev->dev);
priv->data = match->data;
@ -797,6 +899,15 @@ static int meson_sar_adc_probe(struct platform_device *pdev)
if (IS_ERR(base))
return PTR_ERR(base);
irq = irq_of_parse_and_map(pdev->dev.of_node, 0);
if (!irq)
return -EINVAL;
ret = devm_request_irq(&pdev->dev, irq, meson_sar_adc_irq, IRQF_SHARED,
dev_name(&pdev->dev), indio_dev);
if (ret)
return ret;
priv->regmap = devm_regmap_init_mmio(&pdev->dev, base,
&meson_sar_adc_regmap_config);
if (IS_ERR(priv->regmap))
@ -857,6 +968,8 @@ static int meson_sar_adc_probe(struct platform_device *pdev)
return PTR_ERR(priv->vref);
}
priv->calibscale = MILLION;
ret = meson_sar_adc_init(indio_dev);
if (ret)
goto err;
@ -865,6 +978,10 @@ static int meson_sar_adc_probe(struct platform_device *pdev)
if (ret)
goto err;
ret = meson_sar_adc_calib(indio_dev);
if (ret)
dev_warn(&pdev->dev, "calibration failed\n");
platform_set_drvdata(pdev, indio_dev);
ret = iio_device_register(indio_dev);

File diff suppressed because it is too large Load diff

View file

@ -28,6 +28,8 @@
#include <dt-bindings/iio/qcom,spmi-vadc.h>
#include "qcom-vadc-common.h"
/* VADC register and bit definitions */
#define VADC_REVISION2 0x1
#define VADC_REVISION2_SUPPORTED_VADC 1
@ -75,83 +77,9 @@
#define VADC_DATA 0x60 /* 16 bits */
#define VADC_CONV_TIME_MIN_US 2000
#define VADC_CONV_TIME_MAX_US 2100
/* Min ADC code represents 0V */
#define VADC_MIN_ADC_CODE 0x6000
/* Max ADC code represents full-scale range of 1.8V */
#define VADC_MAX_ADC_CODE 0xa800
#define VADC_ABSOLUTE_RANGE_UV 625000
#define VADC_RATIOMETRIC_RANGE 1800
#define VADC_DEF_PRESCALING 0 /* 1:1 */
#define VADC_DEF_DECIMATION 0 /* 512 */
#define VADC_DEF_HW_SETTLE_TIME 0 /* 0 us */
#define VADC_DEF_AVG_SAMPLES 0 /* 1 sample */
#define VADC_DEF_CALIB_TYPE VADC_CALIB_ABSOLUTE
#define VADC_DECIMATION_MIN 512
#define VADC_DECIMATION_MAX 4096
#define VADC_HW_SETTLE_DELAY_MAX 10000
#define VADC_AVG_SAMPLES_MAX 512
#define KELVINMIL_CELSIUSMIL 273150
#define PMI_CHG_SCALE_1 -138890
#define PMI_CHG_SCALE_2 391750000000LL
#define VADC_CHAN_MIN VADC_USBIN
#define VADC_CHAN_MAX VADC_LR_MUX3_BUF_PU1_PU2_XO_THERM
/**
* struct vadc_map_pt - Map the graph representation for ADC channel
* @x: Represent the ADC digitized code.
* @y: Represent the physical data which can be temperature, voltage,
* resistance.
*/
struct vadc_map_pt {
s32 x;
s32 y;
};
/*
* VADC_CALIB_ABSOLUTE: uses the 625mV and 1.25V as reference channels.
* VADC_CALIB_RATIOMETRIC: uses the reference voltage (1.8V) and GND for
* calibration.
*/
enum vadc_calibration {
VADC_CALIB_ABSOLUTE = 0,
VADC_CALIB_RATIOMETRIC
};
/**
* struct vadc_linear_graph - Represent ADC characteristics.
* @dy: numerator slope to calculate the gain.
* @dx: denominator slope to calculate the gain.
* @gnd: A/D word of the ground reference used for the channel.
*
* Each ADC device has different offset and gain parameters which are
* computed to calibrate the device.
*/
struct vadc_linear_graph {
s32 dy;
s32 dx;
s32 gnd;
};
/**
* struct vadc_prescale_ratio - Represent scaling ratio for ADC input.
* @num: the inverse numerator of the gain applied to the input channel.
* @den: the inverse denominator of the gain applied to the input channel.
*/
struct vadc_prescale_ratio {
u32 num;
u32 den;
};
/**
* struct vadc_channel_prop - VADC channel property.
* @channel: channel number, refer to the channel list.
@ -162,9 +90,8 @@ struct vadc_prescale_ratio {
* start of conversion.
* @avg_samples: ability to provide single result from the ADC
* that is an average of multiple measurements.
* @scale_fn: Represents the scaling function to convert voltage
* @scale_fn_type: Represents the scaling function to convert voltage
* physical units desired by the client for the channel.
* Referenced from enum vadc_scale_fn_type.
*/
struct vadc_channel_prop {
unsigned int channel;
@ -173,7 +100,7 @@ struct vadc_channel_prop {
unsigned int prescale;
unsigned int hw_settle_time;
unsigned int avg_samples;
unsigned int scale_fn;
enum vadc_scale_fn_type scale_fn_type;
};
/**
@ -204,35 +131,6 @@ struct vadc_priv {
struct mutex lock;
};
/**
* struct vadc_scale_fn - Scaling function prototype
* @scale: Function pointer to one of the scaling functions
* which takes the adc properties, channel properties,
* and returns the physical result.
*/
struct vadc_scale_fn {
int (*scale)(struct vadc_priv *, const struct vadc_channel_prop *,
u16, int *);
};
/**
* enum vadc_scale_fn_type - Scaling function to convert ADC code to
* physical scaled units for the channel.
* SCALE_DEFAULT: Default scaling to convert raw adc code to voltage (uV).
* SCALE_THERM_100K_PULLUP: Returns temperature in millidegC.
* Uses a mapping table with 100K pullup.
* SCALE_PMIC_THERM: Returns result in milli degree's Centigrade.
* SCALE_XOTHERM: Returns XO thermistor voltage in millidegC.
* SCALE_PMI_CHG_TEMP: Conversion for PMI CHG temp
*/
enum vadc_scale_fn_type {
SCALE_DEFAULT = 0,
SCALE_THERM_100K_PULLUP,
SCALE_PMIC_THERM,
SCALE_XOTHERM,
SCALE_PMI_CHG_TEMP,
};
static const struct vadc_prescale_ratio vadc_prescale_ratios[] = {
{.num = 1, .den = 1},
{.num = 1, .den = 3},
@ -244,44 +142,6 @@ static const struct vadc_prescale_ratio vadc_prescale_ratios[] = {
{.num = 1, .den = 10}
};
/* Voltage to temperature */
static const struct vadc_map_pt adcmap_100k_104ef_104fb[] = {
{1758, -40},
{1742, -35},
{1719, -30},
{1691, -25},
{1654, -20},
{1608, -15},
{1551, -10},
{1483, -5},
{1404, 0},
{1315, 5},
{1218, 10},
{1114, 15},
{1007, 20},
{900, 25},
{795, 30},
{696, 35},
{605, 40},
{522, 45},
{448, 50},
{383, 55},
{327, 60},
{278, 65},
{237, 70},
{202, 75},
{172, 80},
{146, 85},
{125, 90},
{107, 95},
{92, 100},
{79, 105},
{68, 110},
{59, 115},
{51, 120},
{44, 125}
};
static int vadc_read(struct vadc_priv *vadc, u16 offset, u8 *data)
{
return regmap_bulk_read(vadc->regmap, vadc->base + offset, data, 1);
@ -553,159 +413,6 @@ err:
return ret;
}
static int vadc_map_voltage_temp(const struct vadc_map_pt *pts,
u32 tablesize, s32 input, s64 *output)
{
bool descending = 1;
u32 i = 0;
if (!pts)
return -EINVAL;
/* Check if table is descending or ascending */
if (tablesize > 1) {
if (pts[0].x < pts[1].x)
descending = 0;
}
while (i < tablesize) {
if ((descending) && (pts[i].x < input)) {
/* table entry is less than measured*/
/* value and table is descending, stop */
break;
} else if ((!descending) &&
(pts[i].x > input)) {
/* table entry is greater than measured*/
/*value and table is ascending, stop */
break;
}
i++;
}
if (i == 0) {
*output = pts[0].y;
} else if (i == tablesize) {
*output = pts[tablesize - 1].y;
} else {
/* result is between search_index and search_index-1 */
/* interpolate linearly */
*output = (((s32)((pts[i].y - pts[i - 1].y) *
(input - pts[i - 1].x)) /
(pts[i].x - pts[i - 1].x)) +
pts[i - 1].y);
}
return 0;
}
static void vadc_scale_calib(struct vadc_priv *vadc, u16 adc_code,
const struct vadc_channel_prop *prop,
s64 *scale_voltage)
{
*scale_voltage = (adc_code -
vadc->graph[prop->calibration].gnd);
*scale_voltage *= vadc->graph[prop->calibration].dx;
*scale_voltage = div64_s64(*scale_voltage,
vadc->graph[prop->calibration].dy);
if (prop->calibration == VADC_CALIB_ABSOLUTE)
*scale_voltage +=
vadc->graph[prop->calibration].dx;
if (*scale_voltage < 0)
*scale_voltage = 0;
}
static int vadc_scale_volt(struct vadc_priv *vadc,
const struct vadc_channel_prop *prop, u16 adc_code,
int *result_uv)
{
const struct vadc_prescale_ratio *prescale;
s64 voltage = 0, result = 0;
vadc_scale_calib(vadc, adc_code, prop, &voltage);
prescale = &vadc_prescale_ratios[prop->prescale];
voltage = voltage * prescale->den;
result = div64_s64(voltage, prescale->num);
*result_uv = result;
return 0;
}
static int vadc_scale_therm(struct vadc_priv *vadc,
const struct vadc_channel_prop *prop, u16 adc_code,
int *result_mdec)
{
s64 voltage = 0, result = 0;
vadc_scale_calib(vadc, adc_code, prop, &voltage);
if (prop->calibration == VADC_CALIB_ABSOLUTE)
voltage = div64_s64(voltage, 1000);
vadc_map_voltage_temp(adcmap_100k_104ef_104fb,
ARRAY_SIZE(adcmap_100k_104ef_104fb),
voltage, &result);
result *= 1000;
*result_mdec = result;
return 0;
}
static int vadc_scale_die_temp(struct vadc_priv *vadc,
const struct vadc_channel_prop *prop,
u16 adc_code, int *result_mdec)
{
const struct vadc_prescale_ratio *prescale;
s64 voltage = 0;
u64 temp; /* Temporary variable for do_div */
vadc_scale_calib(vadc, adc_code, prop, &voltage);
if (voltage > 0) {
prescale = &vadc_prescale_ratios[prop->prescale];
temp = voltage * prescale->den;
do_div(temp, prescale->num * 2);
voltage = temp;
} else {
voltage = 0;
}
voltage -= KELVINMIL_CELSIUSMIL;
*result_mdec = voltage;
return 0;
}
static int vadc_scale_chg_temp(struct vadc_priv *vadc,
const struct vadc_channel_prop *prop,
u16 adc_code, int *result_mdec)
{
const struct vadc_prescale_ratio *prescale;
s64 voltage = 0, result = 0;
vadc_scale_calib(vadc, adc_code, prop, &voltage);
prescale = &vadc_prescale_ratios[prop->prescale];
voltage = voltage * prescale->den;
voltage = div64_s64(voltage, prescale->num);
voltage = ((PMI_CHG_SCALE_1) * (voltage * 2));
voltage = (voltage + PMI_CHG_SCALE_2);
result = div64_s64(voltage, 1000000);
*result_mdec = result;
return 0;
}
static int vadc_decimation_from_dt(u32 value)
{
if (!is_power_of_2(value) || value < VADC_DECIMATION_MIN ||
value > VADC_DECIMATION_MAX)
return -EINVAL;
return __ffs64(value / VADC_DECIMATION_MIN);
}
static int vadc_prescaling_from_dt(u32 num, u32 den)
{
unsigned int pre;
@ -742,14 +449,6 @@ static int vadc_avg_samples_from_dt(u32 value)
return __ffs64(value);
}
static struct vadc_scale_fn scale_fn[] = {
[SCALE_DEFAULT] = {vadc_scale_volt},
[SCALE_THERM_100K_PULLUP] = {vadc_scale_therm},
[SCALE_PMIC_THERM] = {vadc_scale_die_temp},
[SCALE_XOTHERM] = {vadc_scale_therm},
[SCALE_PMI_CHG_TEMP] = {vadc_scale_chg_temp},
};
static int vadc_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val, int *val2,
long mask)
@ -766,7 +465,13 @@ static int vadc_read_raw(struct iio_dev *indio_dev,
if (ret)
break;
scale_fn[prop->scale_fn].scale(vadc, prop, adc_code, val);
ret = qcom_vadc_scale(prop->scale_fn_type,
&vadc->graph[prop->calibration],
&vadc_prescale_ratios[prop->prescale],
(prop->calibration == VADC_CALIB_ABSOLUTE),
adc_code, val);
if (ret)
break;
return IIO_VAL_INT;
case IIO_CHAN_INFO_RAW:
@ -809,7 +514,7 @@ struct vadc_channels {
unsigned int prescale_index;
enum iio_chan_type type;
long info_mask;
unsigned int scale_fn;
enum vadc_scale_fn_type scale_fn_type;
};
#define VADC_CHAN(_dname, _type, _mask, _pre, _scale) \
@ -818,7 +523,7 @@ struct vadc_channels {
.prescale_index = _pre, \
.type = _type, \
.info_mask = _mask, \
.scale_fn = _scale \
.scale_fn_type = _scale \
}, \
#define VADC_NO_CHAN(_dname, _type, _mask, _pre) \
@ -976,7 +681,7 @@ static int vadc_get_dt_channel_data(struct device *dev,
ret = of_property_read_u32(node, "qcom,decimation", &value);
if (!ret) {
ret = vadc_decimation_from_dt(value);
ret = qcom_vadc_decimation_from_dt(value);
if (ret < 0) {
dev_err(dev, "%02x invalid decimation %d\n",
chan, value);
@ -1068,7 +773,7 @@ static int vadc_get_dt_data(struct vadc_priv *vadc, struct device_node *node)
return ret;
}
prop.scale_fn = vadc_chans[prop.channel].scale_fn;
prop.scale_fn_type = vadc_chans[prop.channel].scale_fn_type;
vadc->chan_props[index] = prop;
vadc_chan = &vadc_chans[prop.channel];

View file

@ -0,0 +1,230 @@
#include <linux/bug.h>
#include <linux/kernel.h>
#include <linux/bitops.h>
#include <linux/math64.h>
#include <linux/log2.h>
#include <linux/err.h>
#include "qcom-vadc-common.h"
/* Voltage to temperature */
static const struct vadc_map_pt adcmap_100k_104ef_104fb[] = {
{1758, -40},
{1742, -35},
{1719, -30},
{1691, -25},
{1654, -20},
{1608, -15},
{1551, -10},
{1483, -5},
{1404, 0},
{1315, 5},
{1218, 10},
{1114, 15},
{1007, 20},
{900, 25},
{795, 30},
{696, 35},
{605, 40},
{522, 45},
{448, 50},
{383, 55},
{327, 60},
{278, 65},
{237, 70},
{202, 75},
{172, 80},
{146, 85},
{125, 90},
{107, 95},
{92, 100},
{79, 105},
{68, 110},
{59, 115},
{51, 120},
{44, 125}
};
static int qcom_vadc_map_voltage_temp(const struct vadc_map_pt *pts,
u32 tablesize, s32 input, s64 *output)
{
bool descending = 1;
u32 i = 0;
if (!pts)
return -EINVAL;
/* Check if table is descending or ascending */
if (tablesize > 1) {
if (pts[0].x < pts[1].x)
descending = 0;
}
while (i < tablesize) {
if ((descending) && (pts[i].x < input)) {
/* table entry is less than measured*/
/* value and table is descending, stop */
break;
} else if ((!descending) &&
(pts[i].x > input)) {
/* table entry is greater than measured*/
/*value and table is ascending, stop */
break;
}
i++;
}
if (i == 0) {
*output = pts[0].y;
} else if (i == tablesize) {
*output = pts[tablesize - 1].y;
} else {
/* result is between search_index and search_index-1 */
/* interpolate linearly */
*output = (((s32)((pts[i].y - pts[i - 1].y) *
(input - pts[i - 1].x)) /
(pts[i].x - pts[i - 1].x)) +
pts[i - 1].y);
}
return 0;
}
static void qcom_vadc_scale_calib(const struct vadc_linear_graph *calib_graph,
u16 adc_code,
bool absolute,
s64 *scale_voltage)
{
*scale_voltage = (adc_code - calib_graph->gnd);
*scale_voltage *= calib_graph->dx;
*scale_voltage = div64_s64(*scale_voltage, calib_graph->dy);
if (absolute)
*scale_voltage += calib_graph->dx;
if (*scale_voltage < 0)
*scale_voltage = 0;
}
static int qcom_vadc_scale_volt(const struct vadc_linear_graph *calib_graph,
const struct vadc_prescale_ratio *prescale,
bool absolute, u16 adc_code,
int *result_uv)
{
s64 voltage = 0, result = 0;
qcom_vadc_scale_calib(calib_graph, adc_code, absolute, &voltage);
voltage = voltage * prescale->den;
result = div64_s64(voltage, prescale->num);
*result_uv = result;
return 0;
}
static int qcom_vadc_scale_therm(const struct vadc_linear_graph *calib_graph,
const struct vadc_prescale_ratio *prescale,
bool absolute, u16 adc_code,
int *result_mdec)
{
s64 voltage = 0, result = 0;
int ret;
qcom_vadc_scale_calib(calib_graph, adc_code, absolute, &voltage);
if (absolute)
voltage = div64_s64(voltage, 1000);
ret = qcom_vadc_map_voltage_temp(adcmap_100k_104ef_104fb,
ARRAY_SIZE(adcmap_100k_104ef_104fb),
voltage, &result);
if (ret)
return ret;
result *= 1000;
*result_mdec = result;
return 0;
}
static int qcom_vadc_scale_die_temp(const struct vadc_linear_graph *calib_graph,
const struct vadc_prescale_ratio *prescale,
bool absolute,
u16 adc_code, int *result_mdec)
{
s64 voltage = 0;
u64 temp; /* Temporary variable for do_div */
qcom_vadc_scale_calib(calib_graph, adc_code, absolute, &voltage);
if (voltage > 0) {
temp = voltage * prescale->den;
do_div(temp, prescale->num * 2);
voltage = temp;
} else {
voltage = 0;
}
voltage -= KELVINMIL_CELSIUSMIL;
*result_mdec = voltage;
return 0;
}
static int qcom_vadc_scale_chg_temp(const struct vadc_linear_graph *calib_graph,
const struct vadc_prescale_ratio *prescale,
bool absolute,
u16 adc_code, int *result_mdec)
{
s64 voltage = 0, result = 0;
qcom_vadc_scale_calib(calib_graph, adc_code, absolute, &voltage);
voltage = voltage * prescale->den;
voltage = div64_s64(voltage, prescale->num);
voltage = ((PMI_CHG_SCALE_1) * (voltage * 2));
voltage = (voltage + PMI_CHG_SCALE_2);
result = div64_s64(voltage, 1000000);
*result_mdec = result;
return 0;
}
int qcom_vadc_scale(enum vadc_scale_fn_type scaletype,
const struct vadc_linear_graph *calib_graph,
const struct vadc_prescale_ratio *prescale,
bool absolute,
u16 adc_code, int *result)
{
switch (scaletype) {
case SCALE_DEFAULT:
return qcom_vadc_scale_volt(calib_graph, prescale,
absolute, adc_code,
result);
case SCALE_THERM_100K_PULLUP:
case SCALE_XOTHERM:
return qcom_vadc_scale_therm(calib_graph, prescale,
absolute, adc_code,
result);
case SCALE_PMIC_THERM:
return qcom_vadc_scale_die_temp(calib_graph, prescale,
absolute, adc_code,
result);
case SCALE_PMI_CHG_TEMP:
return qcom_vadc_scale_chg_temp(calib_graph, prescale,
absolute, adc_code,
result);
default:
return -EINVAL;
}
}
EXPORT_SYMBOL(qcom_vadc_scale);
int qcom_vadc_decimation_from_dt(u32 value)
{
if (!is_power_of_2(value) || value < VADC_DECIMATION_MIN ||
value > VADC_DECIMATION_MAX)
return -EINVAL;
return __ffs64(value / VADC_DECIMATION_MIN);
}
EXPORT_SYMBOL(qcom_vadc_decimation_from_dt);

View file

@ -0,0 +1,108 @@
/*
* Code shared between the different Qualcomm PMIC voltage ADCs
*/
#ifndef QCOM_VADC_COMMON_H
#define QCOM_VADC_COMMON_H
#define VADC_CONV_TIME_MIN_US 2000
#define VADC_CONV_TIME_MAX_US 2100
/* Min ADC code represents 0V */
#define VADC_MIN_ADC_CODE 0x6000
/* Max ADC code represents full-scale range of 1.8V */
#define VADC_MAX_ADC_CODE 0xa800
#define VADC_ABSOLUTE_RANGE_UV 625000
#define VADC_RATIOMETRIC_RANGE 1800
#define VADC_DEF_PRESCALING 0 /* 1:1 */
#define VADC_DEF_DECIMATION 0 /* 512 */
#define VADC_DEF_HW_SETTLE_TIME 0 /* 0 us */
#define VADC_DEF_AVG_SAMPLES 0 /* 1 sample */
#define VADC_DEF_CALIB_TYPE VADC_CALIB_ABSOLUTE
#define VADC_DECIMATION_MIN 512
#define VADC_DECIMATION_MAX 4096
#define VADC_HW_SETTLE_DELAY_MAX 10000
#define VADC_AVG_SAMPLES_MAX 512
#define KELVINMIL_CELSIUSMIL 273150
#define PMI_CHG_SCALE_1 -138890
#define PMI_CHG_SCALE_2 391750000000LL
/**
* struct vadc_map_pt - Map the graph representation for ADC channel
* @x: Represent the ADC digitized code.
* @y: Represent the physical data which can be temperature, voltage,
* resistance.
*/
struct vadc_map_pt {
s32 x;
s32 y;
};
/*
* VADC_CALIB_ABSOLUTE: uses the 625mV and 1.25V as reference channels.
* VADC_CALIB_RATIOMETRIC: uses the reference voltage (1.8V) and GND for
* calibration.
*/
enum vadc_calibration {
VADC_CALIB_ABSOLUTE = 0,
VADC_CALIB_RATIOMETRIC
};
/**
* struct vadc_linear_graph - Represent ADC characteristics.
* @dy: numerator slope to calculate the gain.
* @dx: denominator slope to calculate the gain.
* @gnd: A/D word of the ground reference used for the channel.
*
* Each ADC device has different offset and gain parameters which are
* computed to calibrate the device.
*/
struct vadc_linear_graph {
s32 dy;
s32 dx;
s32 gnd;
};
/**
* struct vadc_prescale_ratio - Represent scaling ratio for ADC input.
* @num: the inverse numerator of the gain applied to the input channel.
* @den: the inverse denominator of the gain applied to the input channel.
*/
struct vadc_prescale_ratio {
u32 num;
u32 den;
};
/**
* enum vadc_scale_fn_type - Scaling function to convert ADC code to
* physical scaled units for the channel.
* SCALE_DEFAULT: Default scaling to convert raw adc code to voltage (uV).
* SCALE_THERM_100K_PULLUP: Returns temperature in millidegC.
* Uses a mapping table with 100K pullup.
* SCALE_PMIC_THERM: Returns result in milli degree's Centigrade.
* SCALE_XOTHERM: Returns XO thermistor voltage in millidegC.
* SCALE_PMI_CHG_TEMP: Conversion for PMI CHG temp
*/
enum vadc_scale_fn_type {
SCALE_DEFAULT = 0,
SCALE_THERM_100K_PULLUP,
SCALE_PMIC_THERM,
SCALE_XOTHERM,
SCALE_PMI_CHG_TEMP,
};
int qcom_vadc_scale(enum vadc_scale_fn_type scaletype,
const struct vadc_linear_graph *calib_graph,
const struct vadc_prescale_ratio *prescale,
bool absolute,
u16 adc_code, int *result_mdec);
int qcom_vadc_decimation_from_dt(u32 value);
#endif /* QCOM_VADC_COMMON_H */

View file

@ -109,7 +109,7 @@ static int rockchip_saradc_read_raw(struct iio_dev *indio_dev,
static irqreturn_t rockchip_saradc_isr(int irq, void *dev_id)
{
struct rockchip_saradc *info = (struct rockchip_saradc *)dev_id;
struct rockchip_saradc *info = dev_id;
/* Read value */
info->last_val = readl_relaxed(info->regs + SARADC_DATA);

395
drivers/iio/adc/spear_adc.c Normal file
View file

@ -0,0 +1,395 @@
/*
* ST SPEAr ADC driver
*
* Copyright 2012 Stefan Roese <sr@denx.de>
*
* Licensed under the GPL-2.
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/completion.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
/* SPEAR registers definitions */
#define SPEAR600_ADC_SCAN_RATE_LO(x) ((x) & 0xFFFF)
#define SPEAR600_ADC_SCAN_RATE_HI(x) (((x) >> 0x10) & 0xFFFF)
#define SPEAR_ADC_CLK_LOW(x) (((x) & 0xf) << 0)
#define SPEAR_ADC_CLK_HIGH(x) (((x) & 0xf) << 4)
/* Bit definitions for SPEAR_ADC_STATUS */
#define SPEAR_ADC_STATUS_START_CONVERSION BIT(0)
#define SPEAR_ADC_STATUS_CHANNEL_NUM(x) ((x) << 1)
#define SPEAR_ADC_STATUS_ADC_ENABLE BIT(4)
#define SPEAR_ADC_STATUS_AVG_SAMPLE(x) ((x) << 5)
#define SPEAR_ADC_STATUS_VREF_INTERNAL BIT(9)
#define SPEAR_ADC_DATA_MASK 0x03ff
#define SPEAR_ADC_DATA_BITS 10
#define SPEAR_ADC_MOD_NAME "spear-adc"
#define SPEAR_ADC_CHANNEL_NUM 8
#define SPEAR_ADC_CLK_MIN 2500000
#define SPEAR_ADC_CLK_MAX 20000000
struct adc_regs_spear3xx {
u32 status;
u32 average;
u32 scan_rate;
u32 clk; /* Not avail for 1340 & 1310 */
u32 ch_ctrl[SPEAR_ADC_CHANNEL_NUM];
u32 ch_data[SPEAR_ADC_CHANNEL_NUM];
};
struct chan_data {
u32 lsb;
u32 msb;
};
struct adc_regs_spear6xx {
u32 status;
u32 pad[2];
u32 clk;
u32 ch_ctrl[SPEAR_ADC_CHANNEL_NUM];
struct chan_data ch_data[SPEAR_ADC_CHANNEL_NUM];
u32 scan_rate_lo;
u32 scan_rate_hi;
struct chan_data average;
};
struct spear_adc_state {
struct device_node *np;
struct adc_regs_spear3xx __iomem *adc_base_spear3xx;
struct adc_regs_spear6xx __iomem *adc_base_spear6xx;
struct clk *clk;
struct completion completion;
u32 current_clk;
u32 sampling_freq;
u32 avg_samples;
u32 vref_external;
u32 value;
};
/*
* Functions to access some SPEAr ADC register. Abstracted into
* static inline functions, because of different register offsets
* on different SoC variants (SPEAr300 vs SPEAr600 etc).
*/
static void spear_adc_set_status(struct spear_adc_state *st, u32 val)
{
__raw_writel(val, &st->adc_base_spear6xx->status);
}
static void spear_adc_set_clk(struct spear_adc_state *st, u32 val)
{
u32 clk_high, clk_low, count;
u32 apb_clk = clk_get_rate(st->clk);
count = DIV_ROUND_UP(apb_clk, val);
clk_low = count / 2;
clk_high = count - clk_low;
st->current_clk = apb_clk / count;
__raw_writel(SPEAR_ADC_CLK_LOW(clk_low) | SPEAR_ADC_CLK_HIGH(clk_high),
&st->adc_base_spear6xx->clk);
}
static void spear_adc_set_ctrl(struct spear_adc_state *st, int n,
u32 val)
{
__raw_writel(val, &st->adc_base_spear6xx->ch_ctrl[n]);
}
static u32 spear_adc_get_average(struct spear_adc_state *st)
{
if (of_device_is_compatible(st->np, "st,spear600-adc")) {
return __raw_readl(&st->adc_base_spear6xx->average.msb) &
SPEAR_ADC_DATA_MASK;
} else {
return __raw_readl(&st->adc_base_spear3xx->average) &
SPEAR_ADC_DATA_MASK;
}
}
static void spear_adc_set_scanrate(struct spear_adc_state *st, u32 rate)
{
if (of_device_is_compatible(st->np, "st,spear600-adc")) {
__raw_writel(SPEAR600_ADC_SCAN_RATE_LO(rate),
&st->adc_base_spear6xx->scan_rate_lo);
__raw_writel(SPEAR600_ADC_SCAN_RATE_HI(rate),
&st->adc_base_spear6xx->scan_rate_hi);
} else {
__raw_writel(rate, &st->adc_base_spear3xx->scan_rate);
}
}
static int spear_adc_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val,
int *val2,
long mask)
{
struct spear_adc_state *st = iio_priv(indio_dev);
u32 status;
switch (mask) {
case IIO_CHAN_INFO_RAW:
mutex_lock(&indio_dev->mlock);
status = SPEAR_ADC_STATUS_CHANNEL_NUM(chan->channel) |
SPEAR_ADC_STATUS_AVG_SAMPLE(st->avg_samples) |
SPEAR_ADC_STATUS_START_CONVERSION |
SPEAR_ADC_STATUS_ADC_ENABLE;
if (st->vref_external == 0)
status |= SPEAR_ADC_STATUS_VREF_INTERNAL;
spear_adc_set_status(st, status);
wait_for_completion(&st->completion); /* set by ISR */
*val = st->value;
mutex_unlock(&indio_dev->mlock);
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
*val = st->vref_external;
*val2 = SPEAR_ADC_DATA_BITS;
return IIO_VAL_FRACTIONAL_LOG2;
case IIO_CHAN_INFO_SAMP_FREQ:
*val = st->current_clk;
return IIO_VAL_INT;
}
return -EINVAL;
}
static int spear_adc_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val,
int val2,
long mask)
{
struct spear_adc_state *st = iio_priv(indio_dev);
int ret = 0;
if (mask != IIO_CHAN_INFO_SAMP_FREQ)
return -EINVAL;
mutex_lock(&indio_dev->mlock);
if ((val < SPEAR_ADC_CLK_MIN) ||
(val > SPEAR_ADC_CLK_MAX) ||
(val2 != 0)) {
ret = -EINVAL;
goto out;
}
spear_adc_set_clk(st, val);
out:
mutex_unlock(&indio_dev->mlock);
return ret;
}
#define SPEAR_ADC_CHAN(idx) { \
.type = IIO_VOLTAGE, \
.indexed = 1, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),\
.channel = idx, \
}
static const struct iio_chan_spec spear_adc_iio_channels[] = {
SPEAR_ADC_CHAN(0),
SPEAR_ADC_CHAN(1),
SPEAR_ADC_CHAN(2),
SPEAR_ADC_CHAN(3),
SPEAR_ADC_CHAN(4),
SPEAR_ADC_CHAN(5),
SPEAR_ADC_CHAN(6),
SPEAR_ADC_CHAN(7),
};
static irqreturn_t spear_adc_isr(int irq, void *dev_id)
{
struct spear_adc_state *st = dev_id;
/* Read value to clear IRQ */
st->value = spear_adc_get_average(st);
complete(&st->completion);
return IRQ_HANDLED;
}
static int spear_adc_configure(struct spear_adc_state *st)
{
int i;
/* Reset ADC core */
spear_adc_set_status(st, 0);
__raw_writel(0, &st->adc_base_spear6xx->clk);
for (i = 0; i < 8; i++)
spear_adc_set_ctrl(st, i, 0);
spear_adc_set_scanrate(st, 0);
spear_adc_set_clk(st, st->sampling_freq);
return 0;
}
static const struct iio_info spear_adc_info = {
.read_raw = &spear_adc_read_raw,
.write_raw = &spear_adc_write_raw,
.driver_module = THIS_MODULE,
};
static int spear_adc_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct device *dev = &pdev->dev;
struct spear_adc_state *st;
struct resource *res;
struct iio_dev *indio_dev = NULL;
int ret = -ENODEV;
int irq;
indio_dev = devm_iio_device_alloc(dev, sizeof(struct spear_adc_state));
if (!indio_dev) {
dev_err(dev, "failed allocating iio device\n");
return -ENOMEM;
}
st = iio_priv(indio_dev);
st->np = np;
/*
* SPEAr600 has a different register layout than other SPEAr SoC's
* (e.g. SPEAr3xx). Let's provide two register base addresses
* to support multi-arch kernels.
*/
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
st->adc_base_spear6xx = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(st->adc_base_spear6xx))
return PTR_ERR(st->adc_base_spear6xx);
st->adc_base_spear3xx =
(struct adc_regs_spear3xx __iomem *)st->adc_base_spear6xx;
st->clk = devm_clk_get(dev, NULL);
if (IS_ERR(st->clk)) {
dev_err(dev, "failed getting clock\n");
return PTR_ERR(st->clk);
}
ret = clk_prepare_enable(st->clk);
if (ret) {
dev_err(dev, "failed enabling clock\n");
return ret;
}
irq = platform_get_irq(pdev, 0);
if (irq <= 0) {
dev_err(dev, "failed getting interrupt resource\n");
ret = -EINVAL;
goto errout2;
}
ret = devm_request_irq(dev, irq, spear_adc_isr, 0, SPEAR_ADC_MOD_NAME,
st);
if (ret < 0) {
dev_err(dev, "failed requesting interrupt\n");
goto errout2;
}
if (of_property_read_u32(np, "sampling-frequency",
&st->sampling_freq)) {
dev_err(dev, "sampling-frequency missing in DT\n");
ret = -EINVAL;
goto errout2;
}
/*
* Optional avg_samples defaults to 0, resulting in single data
* conversion
*/
of_property_read_u32(np, "average-samples", &st->avg_samples);
/*
* Optional vref_external defaults to 0, resulting in internal vref
* selection
*/
of_property_read_u32(np, "vref-external", &st->vref_external);
spear_adc_configure(st);
platform_set_drvdata(pdev, indio_dev);
init_completion(&st->completion);
indio_dev->name = SPEAR_ADC_MOD_NAME;
indio_dev->dev.parent = dev;
indio_dev->info = &spear_adc_info;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = spear_adc_iio_channels;
indio_dev->num_channels = ARRAY_SIZE(spear_adc_iio_channels);
ret = iio_device_register(indio_dev);
if (ret)
goto errout2;
dev_info(dev, "SPEAR ADC driver loaded, IRQ %d\n", irq);
return 0;
errout2:
clk_disable_unprepare(st->clk);
return ret;
}
static int spear_adc_remove(struct platform_device *pdev)
{
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
struct spear_adc_state *st = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
clk_disable_unprepare(st->clk);
return 0;
}
#ifdef CONFIG_OF
static const struct of_device_id spear_adc_dt_ids[] = {
{ .compatible = "st,spear600-adc", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, spear_adc_dt_ids);
#endif
static struct platform_driver spear_adc_driver = {
.probe = spear_adc_probe,
.remove = spear_adc_remove,
.driver = {
.name = SPEAR_ADC_MOD_NAME,
.of_match_table = of_match_ptr(spear_adc_dt_ids),
},
};
module_platform_driver(spear_adc_driver);
MODULE_AUTHOR("Stefan Roese <[email protected]>");
MODULE_DESCRIPTION("SPEAr ADC driver");
MODULE_LICENSE("GPL");

View file

@ -60,6 +60,8 @@
#define STM32F4_EOC BIT(1)
/* STM32F4_ADC_CR1 - bit fields */
#define STM32F4_RES_SHIFT 24
#define STM32F4_RES_MASK GENMASK(25, 24)
#define STM32F4_SCAN BIT(8)
#define STM32F4_EOCIE BIT(5)
@ -141,6 +143,7 @@ struct stm32_adc_regs {
* @lock: spinlock
* @bufi: data buffer index
* @num_conv: expected number of scan conversions
* @res: data resolution (e.g. RES bitfield value)
* @trigger_polarity: external trigger polarity (e.g. exten)
* @dma_chan: dma channel
* @rx_buf: dma rx buffer cpu address
@ -157,6 +160,7 @@ struct stm32_adc {
spinlock_t lock; /* interrupt lock */
unsigned int bufi;
unsigned int num_conv;
u32 res;
u32 trigger_polarity;
struct dma_chan *dma_chan;
u8 *rx_buf;
@ -196,6 +200,11 @@ static const struct stm32_adc_chan_spec stm32f4_adc123_channels[] = {
{ IIO_VOLTAGE, 15, "in15" },
};
static const unsigned int stm32f4_adc_resolutions[] = {
/* sorted values so the index matches RES[1:0] in STM32F4_ADC_CR1 */
12, 10, 8, 6,
};
/**
* stm32f4_sq - describe regular sequence registers
* - L: sequence len (register & bit field)
@ -302,6 +311,14 @@ static void stm32_adc_conv_irq_disable(struct stm32_adc *adc)
stm32_adc_clr_bits(adc, STM32F4_ADC_CR1, STM32F4_EOCIE);
}
static void stm32_adc_set_res(struct stm32_adc *adc)
{
u32 val = stm32_adc_readl(adc, STM32F4_ADC_CR1);
val = (val & ~STM32F4_RES_MASK) | (adc->res << STM32F4_RES_SHIFT);
stm32_adc_writel(adc, STM32F4_ADC_CR1, val);
}
/**
* stm32_adc_start_conv() - Start conversions for regular channels.
* @adc: stm32 adc instance
@ -870,11 +887,37 @@ static const struct iio_chan_spec_ext_info stm32_adc_ext_info[] = {
{},
};
static int stm32_adc_of_get_resolution(struct iio_dev *indio_dev)
{
struct device_node *node = indio_dev->dev.of_node;
struct stm32_adc *adc = iio_priv(indio_dev);
unsigned int i;
u32 res;
if (of_property_read_u32(node, "assigned-resolution-bits", &res))
res = stm32f4_adc_resolutions[0];
for (i = 0; i < ARRAY_SIZE(stm32f4_adc_resolutions); i++)
if (res == stm32f4_adc_resolutions[i])
break;
if (i >= ARRAY_SIZE(stm32f4_adc_resolutions)) {
dev_err(&indio_dev->dev, "Bad resolution: %u bits\n", res);
return -EINVAL;
}
dev_dbg(&indio_dev->dev, "Using %u bits resolution\n", res);
adc->res = i;
return 0;
}
static void stm32_adc_chan_init_one(struct iio_dev *indio_dev,
struct iio_chan_spec *chan,
const struct stm32_adc_chan_spec *channel,
int scan_index)
{
struct stm32_adc *adc = iio_priv(indio_dev);
chan->type = channel->type;
chan->channel = channel->channel;
chan->datasheet_name = channel->name;
@ -883,7 +926,7 @@ static void stm32_adc_chan_init_one(struct iio_dev *indio_dev,
chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE);
chan->scan_type.sign = 'u';
chan->scan_type.realbits = 12;
chan->scan_type.realbits = stm32f4_adc_resolutions[adc->res];
chan->scan_type.storagebits = 16;
chan->ext_info = stm32_adc_ext_info;
}
@ -1022,6 +1065,11 @@ static int stm32_adc_probe(struct platform_device *pdev)
return ret;
}
ret = stm32_adc_of_get_resolution(indio_dev);
if (ret < 0)
goto err_clk_disable;
stm32_adc_set_res(adc);
ret = stm32_adc_chan_of_init(indio_dev);
if (ret < 0)
goto err_clk_disable;

View file

@ -318,6 +318,7 @@ static int stx104_probe(struct device *dev, unsigned int id)
}
indio_dev->name = dev_name(dev);
indio_dev->dev.parent = dev;
priv = iio_priv(indio_dev);
priv->base = base[id];

View file

@ -0,0 +1,719 @@
/* ADC driver for sunxi platforms' (A10, A13 and A31) GPADC
*
* Copyright (c) 2016 Quentin Schulz <quentin.schulz@free-electrons.com>
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License version 2 as published by the
* Free Software Foundation.
*
* The Allwinner SoCs all have an ADC that can also act as a touchscreen
* controller and a thermal sensor.
* The thermal sensor works only when the ADC acts as a touchscreen controller
* and is configured to throw an interrupt every fixed periods of time (let say
* every X seconds).
* One would be tempted to disable the IP on the hardware side rather than
* disabling interrupts to save some power but that resets the internal clock of
* the IP, resulting in having to wait X seconds every time we want to read the
* value of the thermal sensor.
* This is also the reason of using autosuspend in pm_runtime. If there was no
* autosuspend, the thermal sensor would need X seconds after every
* pm_runtime_get_sync to get a value from the ADC. The autosuspend allows the
* thermal sensor to be requested again in a certain time span before it gets
* shutdown for not being used.
*/
#include <linux/completion.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/thermal.h>
#include <linux/delay.h>
#include <linux/iio/iio.h>
#include <linux/iio/driver.h>
#include <linux/iio/machine.h>
#include <linux/mfd/sun4i-gpadc.h>
static unsigned int sun4i_gpadc_chan_select(unsigned int chan)
{
return SUN4I_GPADC_CTRL1_ADC_CHAN_SELECT(chan);
}
static unsigned int sun6i_gpadc_chan_select(unsigned int chan)
{
return SUN6I_GPADC_CTRL1_ADC_CHAN_SELECT(chan);
}
struct gpadc_data {
int temp_offset;
int temp_scale;
unsigned int tp_mode_en;
unsigned int tp_adc_select;
unsigned int (*adc_chan_select)(unsigned int chan);
unsigned int adc_chan_mask;
};
static const struct gpadc_data sun4i_gpadc_data = {
.temp_offset = -1932,
.temp_scale = 133,
.tp_mode_en = SUN4I_GPADC_CTRL1_TP_MODE_EN,
.tp_adc_select = SUN4I_GPADC_CTRL1_TP_ADC_SELECT,
.adc_chan_select = &sun4i_gpadc_chan_select,
.adc_chan_mask = SUN4I_GPADC_CTRL1_ADC_CHAN_MASK,
};
static const struct gpadc_data sun5i_gpadc_data = {
.temp_offset = -1447,
.temp_scale = 100,
.tp_mode_en = SUN4I_GPADC_CTRL1_TP_MODE_EN,
.tp_adc_select = SUN4I_GPADC_CTRL1_TP_ADC_SELECT,
.adc_chan_select = &sun4i_gpadc_chan_select,
.adc_chan_mask = SUN4I_GPADC_CTRL1_ADC_CHAN_MASK,
};
static const struct gpadc_data sun6i_gpadc_data = {
.temp_offset = -1623,
.temp_scale = 167,
.tp_mode_en = SUN6I_GPADC_CTRL1_TP_MODE_EN,
.tp_adc_select = SUN6I_GPADC_CTRL1_TP_ADC_SELECT,
.adc_chan_select = &sun6i_gpadc_chan_select,
.adc_chan_mask = SUN6I_GPADC_CTRL1_ADC_CHAN_MASK,
};
static const struct gpadc_data sun8i_a33_gpadc_data = {
.temp_offset = -1662,
.temp_scale = 162,
.tp_mode_en = SUN8I_GPADC_CTRL1_CHOP_TEMP_EN,
};
struct sun4i_gpadc_iio {
struct iio_dev *indio_dev;
struct completion completion;
int temp_data;
u32 adc_data;
struct regmap *regmap;
unsigned int fifo_data_irq;
atomic_t ignore_fifo_data_irq;
unsigned int temp_data_irq;
atomic_t ignore_temp_data_irq;
const struct gpadc_data *data;
bool no_irq;
/* prevents concurrent reads of temperature and ADC */
struct mutex mutex;
};
#define SUN4I_GPADC_ADC_CHANNEL(_channel, _name) { \
.type = IIO_VOLTAGE, \
.indexed = 1, \
.channel = _channel, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
.datasheet_name = _name, \
}
static struct iio_map sun4i_gpadc_hwmon_maps[] = {
{
.adc_channel_label = "temp_adc",
.consumer_dev_name = "iio_hwmon.0",
},
{ /* sentinel */ },
};
static const struct iio_chan_spec sun4i_gpadc_channels[] = {
SUN4I_GPADC_ADC_CHANNEL(0, "adc_chan0"),
SUN4I_GPADC_ADC_CHANNEL(1, "adc_chan1"),
SUN4I_GPADC_ADC_CHANNEL(2, "adc_chan2"),
SUN4I_GPADC_ADC_CHANNEL(3, "adc_chan3"),
{
.type = IIO_TEMP,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_OFFSET),
.datasheet_name = "temp_adc",
},
};
static const struct iio_chan_spec sun4i_gpadc_channels_no_temp[] = {
SUN4I_GPADC_ADC_CHANNEL(0, "adc_chan0"),
SUN4I_GPADC_ADC_CHANNEL(1, "adc_chan1"),
SUN4I_GPADC_ADC_CHANNEL(2, "adc_chan2"),
SUN4I_GPADC_ADC_CHANNEL(3, "adc_chan3"),
};
static const struct iio_chan_spec sun8i_a33_gpadc_channels[] = {
{
.type = IIO_TEMP,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_OFFSET),
.datasheet_name = "temp_adc",
},
};
static const struct regmap_config sun4i_gpadc_regmap_config = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
.fast_io = true,
};
static int sun4i_prepare_for_irq(struct iio_dev *indio_dev, int channel,
unsigned int irq)
{
struct sun4i_gpadc_iio *info = iio_priv(indio_dev);
int ret;
u32 reg;
pm_runtime_get_sync(indio_dev->dev.parent);
reinit_completion(&info->completion);
ret = regmap_write(info->regmap, SUN4I_GPADC_INT_FIFOC,
SUN4I_GPADC_INT_FIFOC_TP_FIFO_TRIG_LEVEL(1) |
SUN4I_GPADC_INT_FIFOC_TP_FIFO_FLUSH);
if (ret)
return ret;
ret = regmap_read(info->regmap, SUN4I_GPADC_CTRL1, &reg);
if (ret)
return ret;
if (irq == info->fifo_data_irq) {
ret = regmap_write(info->regmap, SUN4I_GPADC_CTRL1,
info->data->tp_mode_en |
info->data->tp_adc_select |
info->data->adc_chan_select(channel));
/*
* When the IP changes channel, it needs a bit of time to get
* correct values.
*/
if ((reg & info->data->adc_chan_mask) !=
info->data->adc_chan_select(channel))
mdelay(10);
} else {
/*
* The temperature sensor returns valid data only when the ADC
* operates in touchscreen mode.
*/
ret = regmap_write(info->regmap, SUN4I_GPADC_CTRL1,
info->data->tp_mode_en);
}
if (ret)
return ret;
/*
* When the IP changes mode between ADC or touchscreen, it
* needs a bit of time to get correct values.
*/
if ((reg & info->data->tp_adc_select) != info->data->tp_adc_select)
mdelay(100);
return 0;
}
static int sun4i_gpadc_read(struct iio_dev *indio_dev, int channel, int *val,
unsigned int irq)
{
struct sun4i_gpadc_iio *info = iio_priv(indio_dev);
int ret;
mutex_lock(&info->mutex);
ret = sun4i_prepare_for_irq(indio_dev, channel, irq);
if (ret)
goto err;
enable_irq(irq);
/*
* The temperature sensor throws an interruption periodically (currently
* set at periods of ~0.6s in sun4i_gpadc_runtime_resume). A 1s delay
* makes sure an interruption occurs in normal conditions. If it doesn't
* occur, then there is a timeout.
*/
if (!wait_for_completion_timeout(&info->completion,
msecs_to_jiffies(1000))) {
ret = -ETIMEDOUT;
goto err;
}
if (irq == info->fifo_data_irq)
*val = info->adc_data;
else
*val = info->temp_data;
ret = 0;
pm_runtime_mark_last_busy(indio_dev->dev.parent);
err:
pm_runtime_put_autosuspend(indio_dev->dev.parent);
mutex_unlock(&info->mutex);
return ret;
}
static int sun4i_gpadc_adc_read(struct iio_dev *indio_dev, int channel,
int *val)
{
struct sun4i_gpadc_iio *info = iio_priv(indio_dev);
return sun4i_gpadc_read(indio_dev, channel, val, info->fifo_data_irq);
}
static int sun4i_gpadc_temp_read(struct iio_dev *indio_dev, int *val)
{
struct sun4i_gpadc_iio *info = iio_priv(indio_dev);
if (info->no_irq) {
pm_runtime_get_sync(indio_dev->dev.parent);
regmap_read(info->regmap, SUN4I_GPADC_TEMP_DATA, val);
pm_runtime_mark_last_busy(indio_dev->dev.parent);
pm_runtime_put_autosuspend(indio_dev->dev.parent);
return 0;
}
return sun4i_gpadc_read(indio_dev, 0, val, info->temp_data_irq);
}
static int sun4i_gpadc_temp_offset(struct iio_dev *indio_dev, int *val)
{
struct sun4i_gpadc_iio *info = iio_priv(indio_dev);
*val = info->data->temp_offset;
return 0;
}
static int sun4i_gpadc_temp_scale(struct iio_dev *indio_dev, int *val)
{
struct sun4i_gpadc_iio *info = iio_priv(indio_dev);
*val = info->data->temp_scale;
return 0;
}
static int sun4i_gpadc_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val,
int *val2, long mask)
{
int ret;
switch (mask) {
case IIO_CHAN_INFO_OFFSET:
ret = sun4i_gpadc_temp_offset(indio_dev, val);
if (ret)
return ret;
return IIO_VAL_INT;
case IIO_CHAN_INFO_RAW:
if (chan->type == IIO_VOLTAGE)
ret = sun4i_gpadc_adc_read(indio_dev, chan->channel,
val);
else
ret = sun4i_gpadc_temp_read(indio_dev, val);
if (ret)
return ret;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
if (chan->type == IIO_VOLTAGE) {
/* 3000mV / 4096 * raw */
*val = 0;
*val2 = 732421875;
return IIO_VAL_INT_PLUS_NANO;
}
ret = sun4i_gpadc_temp_scale(indio_dev, val);
if (ret)
return ret;
return IIO_VAL_INT;
default:
return -EINVAL;
}
return -EINVAL;
}
static const struct iio_info sun4i_gpadc_iio_info = {
.read_raw = sun4i_gpadc_read_raw,
.driver_module = THIS_MODULE,
};
static irqreturn_t sun4i_gpadc_temp_data_irq_handler(int irq, void *dev_id)
{
struct sun4i_gpadc_iio *info = dev_id;
if (atomic_read(&info->ignore_temp_data_irq))
goto out;
if (!regmap_read(info->regmap, SUN4I_GPADC_TEMP_DATA, &info->temp_data))
complete(&info->completion);
out:
disable_irq_nosync(info->temp_data_irq);
return IRQ_HANDLED;
}
static irqreturn_t sun4i_gpadc_fifo_data_irq_handler(int irq, void *dev_id)
{
struct sun4i_gpadc_iio *info = dev_id;
if (atomic_read(&info->ignore_fifo_data_irq))
goto out;
if (!regmap_read(info->regmap, SUN4I_GPADC_DATA, &info->adc_data))
complete(&info->completion);
out:
disable_irq_nosync(info->fifo_data_irq);
return IRQ_HANDLED;
}
static int sun4i_gpadc_runtime_suspend(struct device *dev)
{
struct sun4i_gpadc_iio *info = iio_priv(dev_get_drvdata(dev));
/* Disable the ADC on IP */
regmap_write(info->regmap, SUN4I_GPADC_CTRL1, 0);
/* Disable temperature sensor on IP */
regmap_write(info->regmap, SUN4I_GPADC_TPR, 0);
return 0;
}
static int sun4i_gpadc_runtime_resume(struct device *dev)
{
struct sun4i_gpadc_iio *info = iio_priv(dev_get_drvdata(dev));
/* clkin = 6MHz */
regmap_write(info->regmap, SUN4I_GPADC_CTRL0,
SUN4I_GPADC_CTRL0_ADC_CLK_DIVIDER(2) |
SUN4I_GPADC_CTRL0_FS_DIV(7) |
SUN4I_GPADC_CTRL0_T_ACQ(63));
regmap_write(info->regmap, SUN4I_GPADC_CTRL1, info->data->tp_mode_en);
regmap_write(info->regmap, SUN4I_GPADC_CTRL3,
SUN4I_GPADC_CTRL3_FILTER_EN |
SUN4I_GPADC_CTRL3_FILTER_TYPE(1));
/* period = SUN4I_GPADC_TPR_TEMP_PERIOD * 256 * 16 / clkin; ~0.6s */
regmap_write(info->regmap, SUN4I_GPADC_TPR,
SUN4I_GPADC_TPR_TEMP_ENABLE |
SUN4I_GPADC_TPR_TEMP_PERIOD(800));
return 0;
}
static int sun4i_gpadc_get_temp(void *data, int *temp)
{
struct sun4i_gpadc_iio *info = data;
int val, scale, offset;
if (sun4i_gpadc_temp_read(info->indio_dev, &val))
return -ETIMEDOUT;
sun4i_gpadc_temp_scale(info->indio_dev, &scale);
sun4i_gpadc_temp_offset(info->indio_dev, &offset);
*temp = (val + offset) * scale;
return 0;
}
static const struct thermal_zone_of_device_ops sun4i_ts_tz_ops = {
.get_temp = &sun4i_gpadc_get_temp,
};
static const struct dev_pm_ops sun4i_gpadc_pm_ops = {
.runtime_suspend = &sun4i_gpadc_runtime_suspend,
.runtime_resume = &sun4i_gpadc_runtime_resume,
};
static int sun4i_irq_init(struct platform_device *pdev, const char *name,
irq_handler_t handler, const char *devname,
unsigned int *irq, atomic_t *atomic)
{
int ret;
struct sun4i_gpadc_dev *mfd_dev = dev_get_drvdata(pdev->dev.parent);
struct sun4i_gpadc_iio *info = iio_priv(dev_get_drvdata(&pdev->dev));
/*
* Once the interrupt is activated, the IP continuously performs
* conversions thus throws interrupts. The interrupt is activated right
* after being requested but we want to control when these interrupts
* occur thus we disable it right after being requested. However, an
* interrupt might occur between these two instructions and we have to
* make sure that does not happen, by using atomic flags. We set the
* flag before requesting the interrupt and unset it right after
* disabling the interrupt. When an interrupt occurs between these two
* instructions, reading the atomic flag will tell us to ignore the
* interrupt.
*/
atomic_set(atomic, 1);
ret = platform_get_irq_byname(pdev, name);
if (ret < 0) {
dev_err(&pdev->dev, "no %s interrupt registered\n", name);
return ret;
}
ret = regmap_irq_get_virq(mfd_dev->regmap_irqc, ret);
if (ret < 0) {
dev_err(&pdev->dev, "failed to get virq for irq %s\n", name);
return ret;
}
*irq = ret;
ret = devm_request_any_context_irq(&pdev->dev, *irq, handler, 0,
devname, info);
if (ret < 0) {
dev_err(&pdev->dev, "could not request %s interrupt: %d\n",
name, ret);
return ret;
}
disable_irq(*irq);
atomic_set(atomic, 0);
return 0;
}
static const struct of_device_id sun4i_gpadc_of_id[] = {
{
.compatible = "allwinner,sun8i-a33-ths",
.data = &sun8i_a33_gpadc_data,
},
{ /* sentinel */ }
};
static int sun4i_gpadc_probe_dt(struct platform_device *pdev,
struct iio_dev *indio_dev)
{
struct sun4i_gpadc_iio *info = iio_priv(indio_dev);
const struct of_device_id *of_dev;
struct thermal_zone_device *tzd;
struct resource *mem;
void __iomem *base;
int ret;
of_dev = of_match_device(sun4i_gpadc_of_id, &pdev->dev);
if (!of_dev)
return -ENODEV;
info->no_irq = true;
info->data = (struct gpadc_data *)of_dev->data;
indio_dev->num_channels = ARRAY_SIZE(sun8i_a33_gpadc_channels);
indio_dev->channels = sun8i_a33_gpadc_channels;
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(&pdev->dev, mem);
if (IS_ERR(base))
return PTR_ERR(base);
info->regmap = devm_regmap_init_mmio(&pdev->dev, base,
&sun4i_gpadc_regmap_config);
if (IS_ERR(info->regmap)) {
ret = PTR_ERR(info->regmap);
dev_err(&pdev->dev, "failed to init regmap: %d\n", ret);
return ret;
}
if (!IS_ENABLED(CONFIG_THERMAL_OF))
return 0;
tzd = devm_thermal_zone_of_sensor_register(&pdev->dev, 0, info,
&sun4i_ts_tz_ops);
if (IS_ERR(tzd))
dev_err(&pdev->dev, "could not register thermal sensor: %ld\n",
PTR_ERR(tzd));
return PTR_ERR_OR_ZERO(tzd);
}
static int sun4i_gpadc_probe_mfd(struct platform_device *pdev,
struct iio_dev *indio_dev)
{
struct sun4i_gpadc_iio *info = iio_priv(indio_dev);
struct sun4i_gpadc_dev *sun4i_gpadc_dev =
dev_get_drvdata(pdev->dev.parent);
int ret;
info->no_irq = false;
info->regmap = sun4i_gpadc_dev->regmap;
indio_dev->num_channels = ARRAY_SIZE(sun4i_gpadc_channels);
indio_dev->channels = sun4i_gpadc_channels;
info->data = (struct gpadc_data *)platform_get_device_id(pdev)->driver_data;
/*
* Since the controller needs to be in touchscreen mode for its thermal
* sensor to operate properly, and that switching between the two modes
* needs a delay, always registering in the thermal framework will
* significantly slow down the conversion rate of the ADCs.
*
* Therefore, instead of depending on THERMAL_OF in Kconfig, we only
* register the sensor if that option is enabled, eventually leaving
* that choice to the user.
*/
if (IS_ENABLED(CONFIG_THERMAL_OF)) {
/*
* This driver is a child of an MFD which has a node in the DT
* but not its children, because of DT backward compatibility
* for A10, A13 and A31 SoCs. Therefore, the resulting devices
* of this driver do not have an of_node variable.
* However, its parent (the MFD driver) has an of_node variable
* and since devm_thermal_zone_of_sensor_register uses its first
* argument to match the phandle defined in the node of the
* thermal driver with the of_node of the device passed as first
* argument and the third argument to call ops from
* thermal_zone_of_device_ops, the solution is to use the parent
* device as first argument to match the phandle with its
* of_node, and the device from this driver as third argument to
* return the temperature.
*/
struct thermal_zone_device *tzd;
tzd = devm_thermal_zone_of_sensor_register(pdev->dev.parent, 0,
info,
&sun4i_ts_tz_ops);
if (IS_ERR(tzd)) {
dev_err(&pdev->dev,
"could not register thermal sensor: %ld\n",
PTR_ERR(tzd));
return PTR_ERR(tzd);
}
} else {
indio_dev->num_channels =
ARRAY_SIZE(sun4i_gpadc_channels_no_temp);
indio_dev->channels = sun4i_gpadc_channels_no_temp;
}
if (IS_ENABLED(CONFIG_THERMAL_OF)) {
ret = sun4i_irq_init(pdev, "TEMP_DATA_PENDING",
sun4i_gpadc_temp_data_irq_handler,
"temp_data", &info->temp_data_irq,
&info->ignore_temp_data_irq);
if (ret < 0)
return ret;
}
ret = sun4i_irq_init(pdev, "FIFO_DATA_PENDING",
sun4i_gpadc_fifo_data_irq_handler, "fifo_data",
&info->fifo_data_irq, &info->ignore_fifo_data_irq);
if (ret < 0)
return ret;
if (IS_ENABLED(CONFIG_THERMAL_OF)) {
ret = iio_map_array_register(indio_dev, sun4i_gpadc_hwmon_maps);
if (ret < 0) {
dev_err(&pdev->dev,
"failed to register iio map array\n");
return ret;
}
}
return 0;
}
static int sun4i_gpadc_probe(struct platform_device *pdev)
{
struct sun4i_gpadc_iio *info;
struct iio_dev *indio_dev;
int ret;
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));
if (!indio_dev)
return -ENOMEM;
info = iio_priv(indio_dev);
platform_set_drvdata(pdev, indio_dev);
mutex_init(&info->mutex);
info->indio_dev = indio_dev;
init_completion(&info->completion);
indio_dev->name = dev_name(&pdev->dev);
indio_dev->dev.parent = &pdev->dev;
indio_dev->dev.of_node = pdev->dev.of_node;
indio_dev->info = &sun4i_gpadc_iio_info;
indio_dev->modes = INDIO_DIRECT_MODE;
if (pdev->dev.of_node)
ret = sun4i_gpadc_probe_dt(pdev, indio_dev);
else
ret = sun4i_gpadc_probe_mfd(pdev, indio_dev);
if (ret)
return ret;
pm_runtime_set_autosuspend_delay(&pdev->dev,
SUN4I_GPADC_AUTOSUSPEND_DELAY);
pm_runtime_use_autosuspend(&pdev->dev);
pm_runtime_set_suspended(&pdev->dev);
pm_runtime_enable(&pdev->dev);
ret = devm_iio_device_register(&pdev->dev, indio_dev);
if (ret < 0) {
dev_err(&pdev->dev, "could not register the device\n");
goto err_map;
}
return 0;
err_map:
if (!info->no_irq && IS_ENABLED(CONFIG_THERMAL_OF))
iio_map_array_unregister(indio_dev);
pm_runtime_put(&pdev->dev);
pm_runtime_disable(&pdev->dev);
return ret;
}
static int sun4i_gpadc_remove(struct platform_device *pdev)
{
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
struct sun4i_gpadc_iio *info = iio_priv(indio_dev);
pm_runtime_put(&pdev->dev);
pm_runtime_disable(&pdev->dev);
if (!info->no_irq && IS_ENABLED(CONFIG_THERMAL_OF))
iio_map_array_unregister(indio_dev);
return 0;
}
static const struct platform_device_id sun4i_gpadc_id[] = {
{ "sun4i-a10-gpadc-iio", (kernel_ulong_t)&sun4i_gpadc_data },
{ "sun5i-a13-gpadc-iio", (kernel_ulong_t)&sun5i_gpadc_data },
{ "sun6i-a31-gpadc-iio", (kernel_ulong_t)&sun6i_gpadc_data },
{ /* sentinel */ },
};
static struct platform_driver sun4i_gpadc_driver = {
.driver = {
.name = "sun4i-gpadc-iio",
.of_match_table = sun4i_gpadc_of_id,
.pm = &sun4i_gpadc_pm_ops,
},
.id_table = sun4i_gpadc_id,
.probe = sun4i_gpadc_probe,
.remove = sun4i_gpadc_remove,
};
module_platform_driver(sun4i_gpadc_driver);
MODULE_DESCRIPTION("ADC driver for sunxi platforms");
MODULE_AUTHOR("Quentin Schulz <[email protected]>");
MODULE_LICENSE("GPL v2");

View file

@ -15,6 +15,7 @@
*/
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
@ -55,7 +56,7 @@
#define ADS1015_DEFAULT_DATA_RATE 4
#define ADS1015_DEFAULT_CHAN 0
enum {
enum chip_ids {
ADS1015,
ADS1115,
};
@ -578,6 +579,7 @@ static int ads1015_probe(struct i2c_client *client,
struct iio_dev *indio_dev;
struct ads1015_data *data;
int ret;
enum chip_ids chip;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (!indio_dev)
@ -593,7 +595,11 @@ static int ads1015_probe(struct i2c_client *client,
indio_dev->name = ADS1015_DRV_NAME;
indio_dev->modes = INDIO_DIRECT_MODE;
switch (id->driver_data) {
if (client->dev.of_node)
chip = (enum chip_ids)of_device_get_match_data(&client->dev);
else
chip = id->driver_data;
switch (chip) {
case ADS1015:
indio_dev->channels = ads1015_channels;
indio_dev->num_channels = ARRAY_SIZE(ads1015_channels);
@ -698,9 +704,23 @@ static const struct i2c_device_id ads1015_id[] = {
};
MODULE_DEVICE_TABLE(i2c, ads1015_id);
static const struct of_device_id ads1015_of_match[] = {
{
.compatible = "ti,ads1015",
.data = (void *)ADS1015
},
{
.compatible = "ti,ads1115",
.data = (void *)ADS1115
},
{}
};
MODULE_DEVICE_TABLE(of, ads1015_of_match);
static struct i2c_driver ads1015_driver = {
.driver = {
.name = ADS1015_DRV_NAME,
.of_match_table = ads1015_of_match,
.pm = &ads1015_pm_ops,
},
.probe = ads1015_probe,

View file

@ -584,7 +584,7 @@ static int vf610_adc_read_data(struct vf610_adc *info)
static irqreturn_t vf610_adc_isr(int irq, void *dev_id)
{
struct iio_dev *indio_dev = (struct iio_dev *)dev_id;
struct iio_dev *indio_dev = dev_id;
struct vf610_adc *info = iio_priv(indio_dev);
int coco;

View file

@ -163,7 +163,7 @@ static int ams_iaqcore_probe(struct i2c_client *client,
mutex_init(&data->lock);
indio_dev->dev.parent = &client->dev;
indio_dev->info = &ams_iaqcore_info,
indio_dev->info = &ams_iaqcore_info;
indio_dev->name = dev_name(&client->dev);
indio_dev->modes = INDIO_DIRECT_MODE;

View file

@ -393,7 +393,7 @@ static int vz89x_probe(struct i2c_client *client,
mutex_init(&data->lock);
indio_dev->dev.parent = &client->dev;
indio_dev->info = &vz89x_info,
indio_dev->info = &vz89x_info;
indio_dev->name = dev_name(&client->dev);
indio_dev->modes = INDIO_DIRECT_MODE;

View file

@ -267,31 +267,12 @@ static int cros_ec_sensors_probe(struct platform_device *pdev)
else
state->core.read_ec_sensors_data = cros_ec_sensors_read_cmd;
ret = iio_triggered_buffer_setup(indio_dev, NULL,
cros_ec_sensors_capture, NULL);
ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
cros_ec_sensors_capture, NULL);
if (ret)
return ret;
ret = iio_device_register(indio_dev);
if (ret)
goto error_uninit_buffer;
return 0;
error_uninit_buffer:
iio_triggered_buffer_cleanup(indio_dev);
return ret;
}
static int cros_ec_sensors_remove(struct platform_device *pdev)
{
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
iio_device_unregister(indio_dev);
iio_triggered_buffer_cleanup(indio_dev);
return 0;
return devm_iio_device_register(dev, indio_dev);
}
static const struct platform_device_id cros_ec_sensors_ids[] = {
@ -313,7 +294,6 @@ static struct platform_driver cros_ec_sensors_platform_driver = {
.name = "cros-ec-sensors",
},
.probe = cros_ec_sensors_probe,
.remove = cros_ec_sensors_remove,
.id_table = cros_ec_sensors_ids,
};
module_platform_driver(cros_ec_sensors_platform_driver);

View file

@ -38,6 +38,12 @@ static struct {
{HID_USAGE_SENSOR_ACCEL_3D,
HID_USAGE_SENSOR_UNITS_G, 9, 806650000},
{HID_USAGE_SENSOR_GRAVITY_VECTOR, 0, 9, 806650000},
{HID_USAGE_SENSOR_GRAVITY_VECTOR,
HID_USAGE_SENSOR_UNITS_METERS_PER_SEC_SQRD, 1, 0},
{HID_USAGE_SENSOR_GRAVITY_VECTOR,
HID_USAGE_SENSOR_UNITS_G, 9, 806650000},
{HID_USAGE_SENSOR_GYRO_3D, 0, 0, 17453293},
{HID_USAGE_SENSOR_GYRO_3D,
HID_USAGE_SENSOR_UNITS_RADIANS_PER_SECOND, 1, 0},
@ -62,6 +68,11 @@ static struct {
{HID_USAGE_SENSOR_TIME_TIMESTAMP, 0, 1000000000, 0},
{HID_USAGE_SENSOR_TIME_TIMESTAMP, HID_USAGE_SENSOR_UNITS_MILLISECOND,
1000000, 0},
{HID_USAGE_SENSOR_TEMPERATURE, 0, 1000, 0},
{HID_USAGE_SENSOR_TEMPERATURE, HID_USAGE_SENSOR_UNITS_DEGREES, 1000, 0},
{HID_USAGE_SENSOR_HUMIDITY, 0, 1000, 0},
};
static int pow_10(unsigned power)
@ -221,7 +232,15 @@ int hid_sensor_write_samp_freq_value(struct hid_sensor_common *st,
if (ret < 0 || value < 0)
ret = -EINVAL;
return ret;
ret = sensor_hub_get_feature(st->hsdev,
st->poll.report_id,
st->poll.index, sizeof(value), &value);
if (ret < 0 || value < 0)
return -EINVAL;
st->poll_interval = value;
return 0;
}
EXPORT_SYMBOL(hid_sensor_write_samp_freq_value);
@ -266,7 +285,16 @@ int hid_sensor_write_raw_hyst_value(struct hid_sensor_common *st,
if (ret < 0 || value < 0)
ret = -EINVAL;
return ret;
ret = sensor_hub_get_feature(st->hsdev,
st->sensitivity.report_id,
st->sensitivity.index, sizeof(value),
&value);
if (ret < 0 || value < 0)
return -EINVAL;
st->raw_hystersis = value;
return 0;
}
EXPORT_SYMBOL(hid_sensor_write_raw_hyst_value);
@ -369,6 +397,9 @@ int hid_sensor_get_reporting_interval(struct hid_sensor_hub_device *hsdev,
/* Default unit of measure is milliseconds */
if (st->poll.units == 0)
st->poll.units = HID_USAGE_SENSOR_UNITS_MILLISECOND;
st->poll_interval = -1;
return 0;
}
@ -399,6 +430,8 @@ int hid_sensor_parse_common_attributes(struct hid_sensor_hub_device *hsdev,
HID_USAGE_SENSOR_PROP_SENSITIVITY_ABS,
&st->sensitivity);
st->raw_hystersis = -1;
sensor_hub_input_get_attribute_info(hsdev,
HID_INPUT_REPORT, usage_id,
HID_USAGE_SENSOR_TIME_TIMESTAMP,

View file

@ -51,6 +51,8 @@ static int _hid_sensor_power_state(struct hid_sensor_common *st, bool state)
st->report_state.report_id,
st->report_state.index,
HID_USAGE_SENSOR_PROP_REPORTING_STATE_ALL_EVENTS_ENUM);
poll_value = hid_sensor_read_poll_value(st);
} else {
int val;
@ -87,9 +89,7 @@ static int _hid_sensor_power_state(struct hid_sensor_common *st, bool state)
sensor_hub_get_feature(st->hsdev, st->power_state.report_id,
st->power_state.index,
sizeof(state_val), &state_val);
if (state)
poll_value = hid_sensor_read_poll_value(st);
if (poll_value > 0)
if (state && poll_value)
msleep_interruptible(poll_value * 2);
return 0;
@ -127,6 +127,20 @@ static void hid_sensor_set_power_work(struct work_struct *work)
struct hid_sensor_common *attrb = container_of(work,
struct hid_sensor_common,
work);
if (attrb->poll_interval >= 0)
sensor_hub_set_feature(attrb->hsdev, attrb->poll.report_id,
attrb->poll.index,
sizeof(attrb->poll_interval),
&attrb->poll_interval);
if (attrb->raw_hystersis >= 0)
sensor_hub_set_feature(attrb->hsdev,
attrb->sensitivity.report_id,
attrb->sensitivity.index,
sizeof(attrb->raw_hystersis),
&attrb->raw_hystersis);
_hid_sensor_power_state(attrb, true);
}
@ -138,6 +152,10 @@ static int hid_sensor_data_rdy_trigger_set_state(struct iio_trigger *trig,
void hid_sensor_remove_trigger(struct hid_sensor_common *attrb)
{
pm_runtime_disable(&attrb->pdev->dev);
pm_runtime_set_suspended(&attrb->pdev->dev);
pm_runtime_put_noidle(&attrb->pdev->dev);
cancel_work_sync(&attrb->work);
iio_trigger_unregister(attrb->trigger);
iio_trigger_free(attrb->trigger);

View file

@ -74,7 +74,7 @@ EXPORT_SYMBOL(ms_sensors_reset);
int ms_sensors_read_prom_word(void *cli, int cmd, u16 *word)
{
int ret;
struct i2c_client *client = (struct i2c_client *)cli;
struct i2c_client *client = cli;
ret = i2c_smbus_read_word_swapped(client, cmd);
if (ret < 0) {
@ -107,7 +107,7 @@ int ms_sensors_convert_and_read(void *cli, u8 conv, u8 rd,
{
int ret;
__be32 buf = 0;
struct i2c_client *client = (struct i2c_client *)cli;
struct i2c_client *client = cli;
/* Trigger conversion */
ret = i2c_smbus_write_byte(client, conv);

View file

@ -551,6 +551,7 @@ static int quad8_probe(struct device *dev, unsigned int id)
indio_dev->num_channels = ARRAY_SIZE(quad8_channels);
indio_dev->channels = quad8_channels;
indio_dev->name = dev_name(dev);
indio_dev->dev.parent = dev;
priv = iio_priv(indio_dev);
priv->base = base[id];

View file

@ -7,7 +7,7 @@ menu "Counters"
config 104_QUAD_8
tristate "ACCES 104-QUAD-8 driver"
depends on X86 && ISA_BUS_API
depends on PC104 && X86 && ISA_BUS_API
help
Say yes here to build support for the ACCES 104-QUAD-8 quadrature
encoder counter/interface device family (104-QUAD-8, 104-QUAD-4).

View file

@ -118,6 +118,16 @@ config AD5624R_SPI
Say yes here to build support for Analog Devices AD5624R, AD5644R and
AD5664R converters (DAC). This driver uses the common SPI interface.
config LTC2632
tristate "Linear Technology LTC2632-12/10/8 DAC spi driver"
depends on SPI
help
Say yes here to build support for Linear Technology
LTC2632-12, LTC2632-10, LTC2632-8 converters (DAC).
To compile this driver as a module, choose M here: the
module will be called ltc2632.
config AD5686
tristate "Analog Devices AD5686R/AD5685R/AD5684R DAC SPI driver"
depends on SPI
@ -274,6 +284,21 @@ config MCP4922
To compile this driver as a module, choose M here: the module
will be called mcp4922.
config STM32_DAC
tristate "STMicroelectronics STM32 DAC"
depends on (ARCH_STM32 && OF) || COMPILE_TEST
depends on REGULATOR
select STM32_DAC_CORE
help
Say yes here to build support for STMicroelectronics STM32 Digital
to Analog Converter (DAC).
This driver can also be built as a module. If so, the module
will be called stm32-dac.
config STM32_DAC_CORE
tristate
config VF610_DAC
tristate "Vybrid vf610 DAC driver"
depends on OF

View file

@ -24,9 +24,12 @@ obj-$(CONFIG_AD8801) += ad8801.o
obj-$(CONFIG_CIO_DAC) += cio-dac.o
obj-$(CONFIG_DPOT_DAC) += dpot-dac.o
obj-$(CONFIG_LPC18XX_DAC) += lpc18xx_dac.o
obj-$(CONFIG_LTC2632) += ltc2632.o
obj-$(CONFIG_M62332) += m62332.o
obj-$(CONFIG_MAX517) += max517.o
obj-$(CONFIG_MAX5821) += max5821.o
obj-$(CONFIG_MCP4725) += mcp4725.o
obj-$(CONFIG_MCP4922) += mcp4922.o
obj-$(CONFIG_STM32_DAC_CORE) += stm32-dac-core.o
obj-$(CONFIG_STM32_DAC) += stm32-dac.o
obj-$(CONFIG_VF610_DAC) += vf610_dac.o

View file

@ -212,7 +212,7 @@ static struct attribute *ad5504_ev_attributes[] = {
NULL,
};
static struct attribute_group ad5504_ev_attribute_group = {
static const struct attribute_group ad5504_ev_attribute_group = {
.attrs = ad5504_ev_attributes,
};
@ -223,7 +223,7 @@ static irqreturn_t ad5504_event_handler(int irq, void *private)
0,
IIO_EV_TYPE_THRESH,
IIO_EV_DIR_RISING),
iio_get_time_ns((struct iio_dev *)private));
iio_get_time_ns(private));
return IRQ_HANDLED;
}

View file

@ -184,9 +184,9 @@ static const struct iio_chan_spec_ext_info ad7303_ext_info[] = {
.address = (chan), \
.scan_type = { \
.sign = 'u', \
.realbits = '8', \
.storagebits = '8', \
.shift = '0', \
.realbits = 8, \
.storagebits = 8, \
.shift = 0, \
}, \
.ext_info = ad7303_ext_info, \
}

View file

@ -119,6 +119,7 @@ static int cio_dac_probe(struct device *dev, unsigned int id)
indio_dev->channels = cio_dac_channels;
indio_dev->num_channels = CIO_DAC_NUM_CHAN;
indio_dev->name = dev_name(dev);
indio_dev->dev.parent = dev;
priv = iio_priv(indio_dev);
priv->base = base[id];

314
drivers/iio/dac/ltc2632.c Normal file
View file

@ -0,0 +1,314 @@
/*
* LTC2632 Digital to analog convertors spi driver
*
* Copyright 2017 Maxime Roussin-Bélanger
*
* Licensed under the GPL-2.
*/
#include <linux/device.h>
#include <linux/spi/spi.h>
#include <linux/module.h>
#include <linux/iio/iio.h>
#define LTC2632_DAC_CHANNELS 2
#define LTC2632_ADDR_DAC0 0x0
#define LTC2632_ADDR_DAC1 0x1
#define LTC2632_CMD_WRITE_INPUT_N 0x0
#define LTC2632_CMD_UPDATE_DAC_N 0x1
#define LTC2632_CMD_WRITE_INPUT_N_UPDATE_ALL 0x2
#define LTC2632_CMD_WRITE_INPUT_N_UPDATE_N 0x3
#define LTC2632_CMD_POWERDOWN_DAC_N 0x4
#define LTC2632_CMD_POWERDOWN_CHIP 0x5
#define LTC2632_CMD_INTERNAL_REFER 0x6
#define LTC2632_CMD_EXTERNAL_REFER 0x7
/**
* struct ltc2632_chip_info - chip specific information
* @channels: channel spec for the DAC
* @vref_mv: reference voltage
*/
struct ltc2632_chip_info {
const struct iio_chan_spec *channels;
const int vref_mv;
};
/**
* struct ltc2632_state - driver instance specific data
* @spi_dev: pointer to the spi_device struct
* @powerdown_cache_mask used to show current channel powerdown state
*/
struct ltc2632_state {
struct spi_device *spi_dev;
unsigned int powerdown_cache_mask;
};
enum ltc2632_supported_device_ids {
ID_LTC2632L12,
ID_LTC2632L10,
ID_LTC2632L8,
ID_LTC2632H12,
ID_LTC2632H10,
ID_LTC2632H8,
};
static int ltc2632_spi_write(struct spi_device *spi,
u8 cmd, u8 addr, u16 val, u8 shift)
{
u32 data;
u8 msg[3];
/*
* The input shift register is 24 bits wide.
* The next four are the command bits, C3 to C0,
* followed by the 4-bit DAC address, A3 to A0, and then the
* 12-, 10-, 8-bit data-word. The data-word comprises the 12-,
* 10-, 8-bit input code followed by 4, 6, or 8 don't care bits.
*/
data = (cmd << 20) | (addr << 16) | (val << shift);
msg[0] = data >> 16;
msg[1] = data >> 8;
msg[2] = data;
return spi_write(spi, msg, sizeof(msg));
}
static int ltc2632_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val,
int *val2,
long m)
{
struct ltc2632_chip_info *chip_info;
const struct ltc2632_state *st = iio_priv(indio_dev);
const struct spi_device_id *spi_dev_id = spi_get_device_id(st->spi_dev);
chip_info = (struct ltc2632_chip_info *)spi_dev_id->driver_data;
switch (m) {
case IIO_CHAN_INFO_SCALE:
*val = chip_info->vref_mv;
*val2 = chan->scan_type.realbits;
return IIO_VAL_FRACTIONAL_LOG2;
}
return -EINVAL;
}
static int ltc2632_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val,
int val2,
long mask)
{
struct ltc2632_state *st = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_RAW:
if (val >= (1 << chan->scan_type.realbits) || val < 0)
return -EINVAL;
return ltc2632_spi_write(st->spi_dev,
LTC2632_CMD_WRITE_INPUT_N_UPDATE_N,
chan->address, val,
chan->scan_type.shift);
default:
return -EINVAL;
}
}
static ssize_t ltc2632_read_dac_powerdown(struct iio_dev *indio_dev,
uintptr_t private,
const struct iio_chan_spec *chan,
char *buf)
{
struct ltc2632_state *st = iio_priv(indio_dev);
return sprintf(buf, "%d\n",
!!(st->powerdown_cache_mask & (1 << chan->channel)));
}
static ssize_t ltc2632_write_dac_powerdown(struct iio_dev *indio_dev,
uintptr_t private,
const struct iio_chan_spec *chan,
const char *buf,
size_t len)
{
bool pwr_down;
int ret;
struct ltc2632_state *st = iio_priv(indio_dev);
ret = strtobool(buf, &pwr_down);
if (ret)
return ret;
if (pwr_down)
st->powerdown_cache_mask |= (1 << chan->channel);
else
st->powerdown_cache_mask &= ~(1 << chan->channel);
ret = ltc2632_spi_write(st->spi_dev,
LTC2632_CMD_POWERDOWN_DAC_N,
chan->channel, 0, 0);
return ret ? ret : len;
}
static const struct iio_info ltc2632_info = {
.write_raw = ltc2632_write_raw,
.read_raw = ltc2632_read_raw,
.driver_module = THIS_MODULE,
};
static const struct iio_chan_spec_ext_info ltc2632_ext_info[] = {
{
.name = "powerdown",
.read = ltc2632_read_dac_powerdown,
.write = ltc2632_write_dac_powerdown,
.shared = IIO_SEPARATE,
},
{ },
};
#define LTC2632_CHANNEL(_chan, _bits) { \
.type = IIO_VOLTAGE, \
.indexed = 1, \
.output = 1, \
.channel = (_chan), \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
.address = (_chan), \
.scan_type = { \
.realbits = (_bits), \
.shift = 16 - (_bits), \
}, \
.ext_info = ltc2632_ext_info, \
}
#define DECLARE_LTC2632_CHANNELS(_name, _bits) \
const struct iio_chan_spec _name ## _channels[] = { \
LTC2632_CHANNEL(0, _bits), \
LTC2632_CHANNEL(1, _bits), \
}
static DECLARE_LTC2632_CHANNELS(ltc2632l12, 12);
static DECLARE_LTC2632_CHANNELS(ltc2632l10, 10);
static DECLARE_LTC2632_CHANNELS(ltc2632l8, 8);
static DECLARE_LTC2632_CHANNELS(ltc2632h12, 12);
static DECLARE_LTC2632_CHANNELS(ltc2632h10, 10);
static DECLARE_LTC2632_CHANNELS(ltc2632h8, 8);
static const struct ltc2632_chip_info ltc2632_chip_info_tbl[] = {
[ID_LTC2632L12] = {
.channels = ltc2632l12_channels,
.vref_mv = 2500,
},
[ID_LTC2632L10] = {
.channels = ltc2632l10_channels,
.vref_mv = 2500,
},
[ID_LTC2632L8] = {
.channels = ltc2632l8_channels,
.vref_mv = 2500,
},
[ID_LTC2632H12] = {
.channels = ltc2632h12_channels,
.vref_mv = 4096,
},
[ID_LTC2632H10] = {
.channels = ltc2632h10_channels,
.vref_mv = 4096,
},
[ID_LTC2632H8] = {
.channels = ltc2632h8_channels,
.vref_mv = 4096,
},
};
static int ltc2632_probe(struct spi_device *spi)
{
struct ltc2632_state *st;
struct iio_dev *indio_dev;
struct ltc2632_chip_info *chip_info;
int ret;
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
if (!indio_dev)
return -ENOMEM;
st = iio_priv(indio_dev);
spi_set_drvdata(spi, indio_dev);
st->spi_dev = spi;
chip_info = (struct ltc2632_chip_info *)
spi_get_device_id(spi)->driver_data;
indio_dev->dev.parent = &spi->dev;
indio_dev->name = dev_of_node(&spi->dev) ? dev_of_node(&spi->dev)->name
: spi_get_device_id(spi)->name;
indio_dev->info = &ltc2632_info;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = chip_info->channels;
indio_dev->num_channels = LTC2632_DAC_CHANNELS;
ret = ltc2632_spi_write(spi, LTC2632_CMD_INTERNAL_REFER, 0, 0, 0);
if (ret) {
dev_err(&spi->dev,
"Set internal reference command failed, %d\n", ret);
return ret;
}
return devm_iio_device_register(&spi->dev, indio_dev);
}
static const struct spi_device_id ltc2632_id[] = {
{ "ltc2632-l12", (kernel_ulong_t)&ltc2632_chip_info_tbl[ID_LTC2632L12] },
{ "ltc2632-l10", (kernel_ulong_t)&ltc2632_chip_info_tbl[ID_LTC2632L10] },
{ "ltc2632-l8", (kernel_ulong_t)&ltc2632_chip_info_tbl[ID_LTC2632L8] },
{ "ltc2632-h12", (kernel_ulong_t)&ltc2632_chip_info_tbl[ID_LTC2632H12] },
{ "ltc2632-h10", (kernel_ulong_t)&ltc2632_chip_info_tbl[ID_LTC2632H10] },
{ "ltc2632-h8", (kernel_ulong_t)&ltc2632_chip_info_tbl[ID_LTC2632H8] },
{}
};
MODULE_DEVICE_TABLE(spi, ltc2632_id);
static struct spi_driver ltc2632_driver = {
.driver = {
.name = "ltc2632",
},
.probe = ltc2632_probe,
.id_table = ltc2632_id,
};
module_spi_driver(ltc2632_driver);
static const struct of_device_id ltc2632_of_match[] = {
{
.compatible = "lltc,ltc2632-l12",
.data = &ltc2632_chip_info_tbl[ID_LTC2632L12]
}, {
.compatible = "lltc,ltc2632-l10",
.data = &ltc2632_chip_info_tbl[ID_LTC2632L10]
}, {
.compatible = "lltc,ltc2632-l8",
.data = &ltc2632_chip_info_tbl[ID_LTC2632L8]
}, {
.compatible = "lltc,ltc2632-h12",
.data = &ltc2632_chip_info_tbl[ID_LTC2632H12]
}, {
.compatible = "lltc,ltc2632-h10",
.data = &ltc2632_chip_info_tbl[ID_LTC2632H10]
}, {
.compatible = "lltc,ltc2632-h8",
.data = &ltc2632_chip_info_tbl[ID_LTC2632H8]
},
{}
};
MODULE_DEVICE_TABLE(of, ltc2632_of_match);
MODULE_AUTHOR("Maxime Roussin-Belanger <[email protected]>");
MODULE_DESCRIPTION("LTC2632 DAC SPI driver");
MODULE_LICENSE("GPL v2");

View file

@ -392,6 +392,7 @@ MODULE_DEVICE_TABLE(of, max5821_of_match);
static struct i2c_driver max5821_driver = {
.driver = {
.name = "max5821",
.of_match_table = max5821_of_match,
.pm = MAX5821_PM_OPS,
},
.probe = max5821_probe,

View file

@ -19,6 +19,7 @@
#include <linux/err.h>
#include <linux/delay.h>
#include <linux/regulator/consumer.h>
#include <linux/of_device.h>
#include <linux/of.h>
#include <linux/iio/iio.h>
@ -199,7 +200,7 @@ static ssize_t mcp4725_write_powerdown(struct iio_dev *indio_dev,
return len;
}
enum {
enum chip_id {
MCP4725,
MCP4726,
};
@ -406,7 +407,10 @@ static int mcp4725_probe(struct i2c_client *client,
data = iio_priv(indio_dev);
i2c_set_clientdata(client, indio_dev);
data->client = client;
data->id = id->driver_data;
if (client->dev.of_node)
data->id = (enum chip_id)of_device_get_match_data(&client->dev);
else
data->id = id->driver_data;
pdata = dev_get_platdata(&client->dev);
if (!pdata) {
@ -525,9 +529,25 @@ static const struct i2c_device_id mcp4725_id[] = {
};
MODULE_DEVICE_TABLE(i2c, mcp4725_id);
#ifdef CONFIG_OF
static const struct of_device_id mcp4725_of_match[] = {
{
.compatible = "microchip,mcp4725",
.data = (void *)MCP4725
},
{
.compatible = "microchip,mcp4726",
.data = (void *)MCP4726
},
{ }
};
MODULE_DEVICE_TABLE(of, mcp4725_of_match);
#endif
static struct i2c_driver mcp4725_driver = {
.driver = {
.name = MCP4725_DRV_NAME,
.of_match_table = of_match_ptr(mcp4725_of_match),
.pm = MCP4725_PM_OPS,
},
.probe = mcp4725_probe,

View file

@ -0,0 +1,180 @@
/*
* This file is part of STM32 DAC driver
*
* Copyright (C) 2017, STMicroelectronics - All Rights Reserved
* Author: Fabrice Gasnier <fabrice.gasnier@st.com>.
*
* License type: GPLv2
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/of_platform.h>
#include <linux/regulator/consumer.h>
#include <linux/reset.h>
#include "stm32-dac-core.h"
/**
* struct stm32_dac_priv - stm32 DAC core private data
* @pclk: peripheral clock common for all DACs
* @rst: peripheral reset control
* @vref: regulator reference
* @common: Common data for all DAC instances
*/
struct stm32_dac_priv {
struct clk *pclk;
struct reset_control *rst;
struct regulator *vref;
struct stm32_dac_common common;
};
static struct stm32_dac_priv *to_stm32_dac_priv(struct stm32_dac_common *com)
{
return container_of(com, struct stm32_dac_priv, common);
}
static const struct regmap_config stm32_dac_regmap_cfg = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = sizeof(u32),
.max_register = 0x3fc,
};
static int stm32_dac_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct stm32_dac_priv *priv;
struct regmap *regmap;
struct resource *res;
void __iomem *mmio;
int ret;
if (!dev->of_node)
return -ENODEV;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
mmio = devm_ioremap_resource(dev, res);
if (IS_ERR(mmio))
return PTR_ERR(mmio);
regmap = devm_regmap_init_mmio(dev, mmio, &stm32_dac_regmap_cfg);
if (IS_ERR(regmap))
return PTR_ERR(regmap);
priv->common.regmap = regmap;
priv->vref = devm_regulator_get(dev, "vref");
if (IS_ERR(priv->vref)) {
ret = PTR_ERR(priv->vref);
dev_err(dev, "vref get failed, %d\n", ret);
return ret;
}
ret = regulator_enable(priv->vref);
if (ret < 0) {
dev_err(dev, "vref enable failed\n");
return ret;
}
ret = regulator_get_voltage(priv->vref);
if (ret < 0) {
dev_err(dev, "vref get voltage failed, %d\n", ret);
goto err_vref;
}
priv->common.vref_mv = ret / 1000;
dev_dbg(dev, "vref+=%dmV\n", priv->common.vref_mv);
priv->pclk = devm_clk_get(dev, "pclk");
if (IS_ERR(priv->pclk)) {
ret = PTR_ERR(priv->pclk);
dev_err(dev, "pclk get failed\n");
goto err_vref;
}
ret = clk_prepare_enable(priv->pclk);
if (ret < 0) {
dev_err(dev, "pclk enable failed\n");
goto err_vref;
}
priv->rst = devm_reset_control_get(dev, NULL);
if (!IS_ERR(priv->rst)) {
reset_control_assert(priv->rst);
udelay(2);
reset_control_deassert(priv->rst);
}
/* When clock speed is higher than 80MHz, set HFSEL */
priv->common.hfsel = (clk_get_rate(priv->pclk) > 80000000UL);
ret = regmap_update_bits(regmap, STM32_DAC_CR, STM32H7_DAC_CR_HFSEL,
priv->common.hfsel ? STM32H7_DAC_CR_HFSEL : 0);
if (ret)
goto err_pclk;
platform_set_drvdata(pdev, &priv->common);
ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, dev);
if (ret < 0) {
dev_err(dev, "failed to populate DT children\n");
goto err_pclk;
}
return 0;
err_pclk:
clk_disable_unprepare(priv->pclk);
err_vref:
regulator_disable(priv->vref);
return ret;
}
static int stm32_dac_remove(struct platform_device *pdev)
{
struct stm32_dac_common *common = platform_get_drvdata(pdev);
struct stm32_dac_priv *priv = to_stm32_dac_priv(common);
of_platform_depopulate(&pdev->dev);
clk_disable_unprepare(priv->pclk);
regulator_disable(priv->vref);
return 0;
}
static const struct of_device_id stm32_dac_of_match[] = {
{ .compatible = "st,stm32h7-dac-core", },
{},
};
MODULE_DEVICE_TABLE(of, stm32_dac_of_match);
static struct platform_driver stm32_dac_driver = {
.probe = stm32_dac_probe,
.remove = stm32_dac_remove,
.driver = {
.name = "stm32-dac-core",
.of_match_table = stm32_dac_of_match,
},
};
module_platform_driver(stm32_dac_driver);
MODULE_AUTHOR("Fabrice Gasnier <[email protected]>");
MODULE_DESCRIPTION("STMicroelectronics STM32 DAC core driver");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:stm32-dac-core");

View file

@ -0,0 +1,51 @@
/*
* This file is part of STM32 DAC driver
*
* Copyright (C) 2017, STMicroelectronics - All Rights Reserved
* Author: Fabrice Gasnier <fabrice.gasnier@st.com>.
*
* License type: GPLv2
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __STM32_DAC_CORE_H
#define __STM32_DAC_CORE_H
#include <linux/regmap.h>
/* STM32 DAC registers */
#define STM32_DAC_CR 0x00
#define STM32_DAC_DHR12R1 0x08
#define STM32_DAC_DHR12R2 0x14
#define STM32_DAC_DOR1 0x2C
#define STM32_DAC_DOR2 0x30
/* STM32_DAC_CR bit fields */
#define STM32_DAC_CR_EN1 BIT(0)
#define STM32H7_DAC_CR_HFSEL BIT(15)
#define STM32_DAC_CR_EN2 BIT(16)
/**
* struct stm32_dac_common - stm32 DAC driver common data (for all instances)
* @regmap: DAC registers shared via regmap
* @vref_mv: reference voltage (mv)
* @hfsel: high speed bus clock selected
*/
struct stm32_dac_common {
struct regmap *regmap;
int vref_mv;
bool hfsel;
};
#endif

334
drivers/iio/dac/stm32-dac.c Normal file
View file

@ -0,0 +1,334 @@
/*
* This file is part of STM32 DAC driver
*
* Copyright (C) 2017, STMicroelectronics - All Rights Reserved
* Authors: Amelie Delaunay <amelie.delaunay@st.com>
* Fabrice Gasnier <fabrice.gasnier@st.com>
*
* License type: GPLv2
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/bitfield.h>
#include <linux/delay.h>
#include <linux/iio/iio.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include "stm32-dac-core.h"
#define STM32_DAC_CHANNEL_1 1
#define STM32_DAC_CHANNEL_2 2
#define STM32_DAC_IS_CHAN_1(ch) ((ch) & STM32_DAC_CHANNEL_1)
/**
* struct stm32_dac - private data of DAC driver
* @common: reference to DAC common data
*/
struct stm32_dac {
struct stm32_dac_common *common;
};
static int stm32_dac_is_enabled(struct iio_dev *indio_dev, int channel)
{
struct stm32_dac *dac = iio_priv(indio_dev);
u32 en, val;
int ret;
ret = regmap_read(dac->common->regmap, STM32_DAC_CR, &val);
if (ret < 0)
return ret;
if (STM32_DAC_IS_CHAN_1(channel))
en = FIELD_GET(STM32_DAC_CR_EN1, val);
else
en = FIELD_GET(STM32_DAC_CR_EN2, val);
return !!en;
}
static int stm32_dac_set_enable_state(struct iio_dev *indio_dev, int ch,
bool enable)
{
struct stm32_dac *dac = iio_priv(indio_dev);
u32 msk = STM32_DAC_IS_CHAN_1(ch) ? STM32_DAC_CR_EN1 : STM32_DAC_CR_EN2;
u32 en = enable ? msk : 0;
int ret;
ret = regmap_update_bits(dac->common->regmap, STM32_DAC_CR, msk, en);
if (ret < 0) {
dev_err(&indio_dev->dev, "%s failed\n", en ?
"Enable" : "Disable");
return ret;
}
/*
* When HFSEL is set, it is not allowed to write the DHRx register
* during 8 clock cycles after the ENx bit is set. It is not allowed
* to make software/hardware trigger during this period either.
*/
if (en && dac->common->hfsel)
udelay(1);
return 0;
}
static int stm32_dac_get_value(struct stm32_dac *dac, int channel, int *val)
{
int ret;
if (STM32_DAC_IS_CHAN_1(channel))
ret = regmap_read(dac->common->regmap, STM32_DAC_DOR1, val);
else
ret = regmap_read(dac->common->regmap, STM32_DAC_DOR2, val);
return ret ? ret : IIO_VAL_INT;
}
static int stm32_dac_set_value(struct stm32_dac *dac, int channel, int val)
{
int ret;
if (STM32_DAC_IS_CHAN_1(channel))
ret = regmap_write(dac->common->regmap, STM32_DAC_DHR12R1, val);
else
ret = regmap_write(dac->common->regmap, STM32_DAC_DHR12R2, val);
return ret;
}
static int stm32_dac_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct stm32_dac *dac = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_RAW:
return stm32_dac_get_value(dac, chan->channel, val);
case IIO_CHAN_INFO_SCALE:
*val = dac->common->vref_mv;
*val2 = chan->scan_type.realbits;
return IIO_VAL_FRACTIONAL_LOG2;
default:
return -EINVAL;
}
}
static int stm32_dac_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
struct stm32_dac *dac = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_RAW:
return stm32_dac_set_value(dac, chan->channel, val);
default:
return -EINVAL;
}
}
static int stm32_dac_debugfs_reg_access(struct iio_dev *indio_dev,
unsigned reg, unsigned writeval,
unsigned *readval)
{
struct stm32_dac *dac = iio_priv(indio_dev);
if (!readval)
return regmap_write(dac->common->regmap, reg, writeval);
else
return regmap_read(dac->common->regmap, reg, readval);
}
static const struct iio_info stm32_dac_iio_info = {
.read_raw = stm32_dac_read_raw,
.write_raw = stm32_dac_write_raw,
.debugfs_reg_access = stm32_dac_debugfs_reg_access,
.driver_module = THIS_MODULE,
};
static const char * const stm32_dac_powerdown_modes[] = {
"three_state",
};
static int stm32_dac_get_powerdown_mode(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan)
{
return 0;
}
static int stm32_dac_set_powerdown_mode(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
unsigned int type)
{
return 0;
}
static ssize_t stm32_dac_read_powerdown(struct iio_dev *indio_dev,
uintptr_t private,
const struct iio_chan_spec *chan,
char *buf)
{
int ret = stm32_dac_is_enabled(indio_dev, chan->channel);
if (ret < 0)
return ret;
return sprintf(buf, "%d\n", ret ? 0 : 1);
}
static ssize_t stm32_dac_write_powerdown(struct iio_dev *indio_dev,
uintptr_t private,
const struct iio_chan_spec *chan,
const char *buf, size_t len)
{
bool powerdown;
int ret;
ret = strtobool(buf, &powerdown);
if (ret)
return ret;
ret = stm32_dac_set_enable_state(indio_dev, chan->channel, !powerdown);
if (ret)
return ret;
return len;
}
static const struct iio_enum stm32_dac_powerdown_mode_en = {
.items = stm32_dac_powerdown_modes,
.num_items = ARRAY_SIZE(stm32_dac_powerdown_modes),
.get = stm32_dac_get_powerdown_mode,
.set = stm32_dac_set_powerdown_mode,
};
static const struct iio_chan_spec_ext_info stm32_dac_ext_info[] = {
{
.name = "powerdown",
.read = stm32_dac_read_powerdown,
.write = stm32_dac_write_powerdown,
.shared = IIO_SEPARATE,
},
IIO_ENUM("powerdown_mode", IIO_SEPARATE, &stm32_dac_powerdown_mode_en),
IIO_ENUM_AVAILABLE("powerdown_mode", &stm32_dac_powerdown_mode_en),
{},
};
#define STM32_DAC_CHANNEL(chan, name) { \
.type = IIO_VOLTAGE, \
.indexed = 1, \
.output = 1, \
.channel = chan, \
.info_mask_separate = \
BIT(IIO_CHAN_INFO_RAW) | \
BIT(IIO_CHAN_INFO_SCALE), \
/* scan_index is always 0 as num_channels is 1 */ \
.scan_type = { \
.sign = 'u', \
.realbits = 12, \
.storagebits = 16, \
}, \
.datasheet_name = name, \
.ext_info = stm32_dac_ext_info \
}
static const struct iio_chan_spec stm32_dac_channels[] = {
STM32_DAC_CHANNEL(STM32_DAC_CHANNEL_1, "out1"),
STM32_DAC_CHANNEL(STM32_DAC_CHANNEL_2, "out2"),
};
static int stm32_dac_chan_of_init(struct iio_dev *indio_dev)
{
struct device_node *np = indio_dev->dev.of_node;
unsigned int i;
u32 channel;
int ret;
ret = of_property_read_u32(np, "reg", &channel);
if (ret) {
dev_err(&indio_dev->dev, "Failed to read reg property\n");
return ret;
}
for (i = 0; i < ARRAY_SIZE(stm32_dac_channels); i++) {
if (stm32_dac_channels[i].channel == channel)
break;
}
if (i >= ARRAY_SIZE(stm32_dac_channels)) {
dev_err(&indio_dev->dev, "Invalid st,dac-channel\n");
return -EINVAL;
}
indio_dev->channels = &stm32_dac_channels[i];
/*
* Expose only one channel here, as they can be used independently,
* with separate trigger. Then separate IIO devices are instantiated
* to manage this.
*/
indio_dev->num_channels = 1;
return 0;
};
static int stm32_dac_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct iio_dev *indio_dev;
struct stm32_dac *dac;
int ret;
if (!np)
return -ENODEV;
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*dac));
if (!indio_dev)
return -ENOMEM;
platform_set_drvdata(pdev, indio_dev);
dac = iio_priv(indio_dev);
dac->common = dev_get_drvdata(pdev->dev.parent);
indio_dev->name = dev_name(&pdev->dev);
indio_dev->dev.parent = &pdev->dev;
indio_dev->dev.of_node = pdev->dev.of_node;
indio_dev->info = &stm32_dac_iio_info;
indio_dev->modes = INDIO_DIRECT_MODE;
ret = stm32_dac_chan_of_init(indio_dev);
if (ret < 0)
return ret;
return devm_iio_device_register(&pdev->dev, indio_dev);
}
static const struct of_device_id stm32_dac_of_match[] = {
{ .compatible = "st,stm32-dac", },
{},
};
MODULE_DEVICE_TABLE(of, stm32_dac_of_match);
static struct platform_driver stm32_dac_driver = {
.probe = stm32_dac_probe,
.driver = {
.name = "stm32-dac",
.of_match_table = stm32_dac_of_match,
},
};
module_platform_driver(stm32_dac_driver);
MODULE_ALIAS("platform:stm32-dac");
MODULE_AUTHOR("Amelie Delaunay <[email protected]>");
MODULE_DESCRIPTION("STMicroelectronics STM32 DAC driver");
MODULE_LICENSE("GPL v2");

View file

@ -377,9 +377,16 @@ static const struct i2c_device_id itg3200_id[] = {
};
MODULE_DEVICE_TABLE(i2c, itg3200_id);
static const struct of_device_id itg3200_of_match[] = {
{ .compatible = "invensense,itg3200" },
{ }
};
MODULE_DEVICE_TABLE(of, itg3200_of_match);
static struct i2c_driver itg3200_driver = {
.driver = {
.name = "itg3200",
.of_match_table = itg3200_of_match,
.pm = &itg3200_pm_ops,
},
.id_table = itg3200_id,

View file

@ -70,9 +70,8 @@ static int mpu3050_i2c_probe(struct i2c_client *client,
dev_err(&client->dev, "failed to allocate I2C mux\n");
else {
mpu3050->i2cmux->priv = mpu3050;
ret = i2c_mux_add_adapter(mpu3050->i2cmux, 0, 0, 0);
if (ret)
dev_err(&client->dev, "failed to add I2C mux\n");
/* Ignore failure, not critical */
i2c_mux_add_adapter(mpu3050->i2cmux, 0, 0, 0);
}
return 0;

View file

@ -46,6 +46,19 @@ config MAX30100
To compile this driver as a module, choose M here: the
module will be called max30100.
config MAX30102
tristate "MAX30102 heart rate and pulse oximeter sensor"
depends on I2C
select REGMAP_I2C
select IIO_BUFFER
select IIO_KFIFO_BUF
help
Say Y here to build I2C interface support for the Maxim
MAX30102 heart rate, and pulse oximeter sensor.
To compile this driver as a module, choose M here: the
module will be called max30102.
endmenu
endmenu

View file

@ -7,3 +7,4 @@
obj-$(CONFIG_AFE4403) += afe4403.o
obj-$(CONFIG_AFE4404) += afe4404.o
obj-$(CONFIG_MAX30100) += max30100.o
obj-$(CONFIG_MAX30102) += max30102.o

View file

@ -449,6 +449,7 @@ static int max30100_probe(struct i2c_client *client,
indio_dev->available_scan_masks = max30100_scan_masks;
indio_dev->modes = (INDIO_BUFFER_SOFTWARE | INDIO_DIRECT_MODE);
indio_dev->setup_ops = &max30100_buffer_setup_ops;
indio_dev->dev.parent = &client->dev;
data = iio_priv(indio_dev);
data->indio_dev = indio_dev;

View file

@ -0,0 +1,486 @@
/*
* max30102.c - Support for MAX30102 heart rate and pulse oximeter sensor
*
* Copyright (C) 2017 Matt Ranostay <matt@ranostay.consulting>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* TODO: proximity power saving feature
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/irq.h>
#include <linux/i2c.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/regmap.h>
#include <linux/iio/iio.h>
#include <linux/iio/buffer.h>
#include <linux/iio/kfifo_buf.h>
#define MAX30102_REGMAP_NAME "max30102_regmap"
#define MAX30102_DRV_NAME "max30102"
#define MAX30102_REG_INT_STATUS 0x00
#define MAX30102_REG_INT_STATUS_PWR_RDY BIT(0)
#define MAX30102_REG_INT_STATUS_PROX_INT BIT(4)
#define MAX30102_REG_INT_STATUS_ALC_OVF BIT(5)
#define MAX30102_REG_INT_STATUS_PPG_RDY BIT(6)
#define MAX30102_REG_INT_STATUS_FIFO_RDY BIT(7)
#define MAX30102_REG_INT_ENABLE 0x02
#define MAX30102_REG_INT_ENABLE_PROX_INT_EN BIT(4)
#define MAX30102_REG_INT_ENABLE_ALC_OVF_EN BIT(5)
#define MAX30102_REG_INT_ENABLE_PPG_EN BIT(6)
#define MAX30102_REG_INT_ENABLE_FIFO_EN BIT(7)
#define MAX30102_REG_INT_ENABLE_MASK 0xf0
#define MAX30102_REG_INT_ENABLE_MASK_SHIFT 4
#define MAX30102_REG_FIFO_WR_PTR 0x04
#define MAX30102_REG_FIFO_OVR_CTR 0x05
#define MAX30102_REG_FIFO_RD_PTR 0x06
#define MAX30102_REG_FIFO_DATA 0x07
#define MAX30102_REG_FIFO_DATA_ENTRY_LEN 6
#define MAX30102_REG_FIFO_CONFIG 0x08
#define MAX30102_REG_FIFO_CONFIG_AVG_4SAMPLES BIT(1)
#define MAX30102_REG_FIFO_CONFIG_AVG_SHIFT 5
#define MAX30102_REG_FIFO_CONFIG_AFULL BIT(0)
#define MAX30102_REG_MODE_CONFIG 0x09
#define MAX30102_REG_MODE_CONFIG_MODE_SPO2_EN BIT(0)
#define MAX30102_REG_MODE_CONFIG_MODE_HR_EN BIT(1)
#define MAX30102_REG_MODE_CONFIG_MODE_MASK 0x03
#define MAX30102_REG_MODE_CONFIG_PWR BIT(7)
#define MAX30102_REG_SPO2_CONFIG 0x0a
#define MAX30102_REG_SPO2_CONFIG_PULSE_411_US 0x03
#define MAX30102_REG_SPO2_CONFIG_SR_400HZ 0x03
#define MAX30102_REG_SPO2_CONFIG_SR_MASK 0x07
#define MAX30102_REG_SPO2_CONFIG_SR_MASK_SHIFT 2
#define MAX30102_REG_SPO2_CONFIG_ADC_4096_STEPS BIT(0)
#define MAX30102_REG_SPO2_CONFIG_ADC_MASK_SHIFT 5
#define MAX30102_REG_RED_LED_CONFIG 0x0c
#define MAX30102_REG_IR_LED_CONFIG 0x0d
#define MAX30102_REG_TEMP_CONFIG 0x21
#define MAX30102_REG_TEMP_CONFIG_TEMP_EN BIT(0)
#define MAX30102_REG_TEMP_INTEGER 0x1f
#define MAX30102_REG_TEMP_FRACTION 0x20
struct max30102_data {
struct i2c_client *client;
struct iio_dev *indio_dev;
struct mutex lock;
struct regmap *regmap;
u8 buffer[8];
__be32 processed_buffer[2]; /* 2 x 18-bit (padded to 32-bits) */
};
static const struct regmap_config max30102_regmap_config = {
.name = MAX30102_REGMAP_NAME,
.reg_bits = 8,
.val_bits = 8,
};
static const unsigned long max30102_scan_masks[] = {0x3, 0};
static const struct iio_chan_spec max30102_channels[] = {
{
.type = IIO_INTENSITY,
.channel2 = IIO_MOD_LIGHT_RED,
.modified = 1,
.scan_index = 0,
.scan_type = {
.sign = 'u',
.shift = 8,
.realbits = 18,
.storagebits = 32,
.endianness = IIO_BE,
},
},
{
.type = IIO_INTENSITY,
.channel2 = IIO_MOD_LIGHT_IR,
.modified = 1,
.scan_index = 1,
.scan_type = {
.sign = 'u',
.shift = 8,
.realbits = 18,
.storagebits = 32,
.endianness = IIO_BE,
},
},
{
.type = IIO_TEMP,
.info_mask_separate =
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
.scan_index = -1,
},
};
static int max30102_set_powermode(struct max30102_data *data, bool state)
{
return regmap_update_bits(data->regmap, MAX30102_REG_MODE_CONFIG,
MAX30102_REG_MODE_CONFIG_PWR,
state ? 0 : MAX30102_REG_MODE_CONFIG_PWR);
}
static int max30102_buffer_postenable(struct iio_dev *indio_dev)
{
struct max30102_data *data = iio_priv(indio_dev);
return max30102_set_powermode(data, true);
}
static int max30102_buffer_predisable(struct iio_dev *indio_dev)
{
struct max30102_data *data = iio_priv(indio_dev);
return max30102_set_powermode(data, false);
}
static const struct iio_buffer_setup_ops max30102_buffer_setup_ops = {
.postenable = max30102_buffer_postenable,
.predisable = max30102_buffer_predisable,
};
static inline int max30102_fifo_count(struct max30102_data *data)
{
unsigned int val;
int ret;
ret = regmap_read(data->regmap, MAX30102_REG_INT_STATUS, &val);
if (ret)
return ret;
/* FIFO has one sample slot left */
if (val & MAX30102_REG_INT_STATUS_FIFO_RDY)
return 1;
return 0;
}
static int max30102_read_measurement(struct max30102_data *data)
{
int ret;
u8 *buffer = (u8 *) &data->buffer;
ret = i2c_smbus_read_i2c_block_data(data->client,
MAX30102_REG_FIFO_DATA,
MAX30102_REG_FIFO_DATA_ENTRY_LEN,
buffer);
memcpy(&data->processed_buffer[0], &buffer[0], 3);
memcpy(&data->processed_buffer[1], &buffer[3], 3);
return (ret == MAX30102_REG_FIFO_DATA_ENTRY_LEN) ? 0 : -EINVAL;
}
static irqreturn_t max30102_interrupt_handler(int irq, void *private)
{
struct iio_dev *indio_dev = private;
struct max30102_data *data = iio_priv(indio_dev);
int ret, cnt = 0;
mutex_lock(&data->lock);
while (cnt || (cnt = max30102_fifo_count(data)) > 0) {
ret = max30102_read_measurement(data);
if (ret)
break;
iio_push_to_buffers(data->indio_dev, data->processed_buffer);
cnt--;
}
mutex_unlock(&data->lock);
return IRQ_HANDLED;
}
static int max30102_get_current_idx(unsigned int val, int *reg)
{
/* each step is 0.200 mA */
*reg = val / 200;
return *reg > 0xff ? -EINVAL : 0;
}
static int max30102_led_init(struct max30102_data *data)
{
struct device *dev = &data->client->dev;
struct device_node *np = dev->of_node;
unsigned int val;
int reg, ret;
ret = of_property_read_u32(np, "maxim,red-led-current-microamp", &val);
if (ret) {
dev_info(dev, "no red-led-current-microamp set\n");
/* Default to 7 mA RED LED */
val = 7000;
}
ret = max30102_get_current_idx(val, &reg);
if (ret) {
dev_err(dev, "invalid RED LED current setting %d\n", val);
return ret;
}
ret = regmap_write(data->regmap, MAX30102_REG_RED_LED_CONFIG, reg);
if (ret)
return ret;
ret = of_property_read_u32(np, "maxim,ir-led-current-microamp", &val);
if (ret) {
dev_info(dev, "no ir-led-current-microamp set\n");
/* Default to 7 mA IR LED */
val = 7000;
}
ret = max30102_get_current_idx(val, &reg);
if (ret) {
dev_err(dev, "invalid IR LED current setting %d", val);
return ret;
}
return regmap_write(data->regmap, MAX30102_REG_IR_LED_CONFIG, reg);
}
static int max30102_chip_init(struct max30102_data *data)
{
int ret;
/* setup LED current settings */
ret = max30102_led_init(data);
if (ret)
return ret;
/* enable 18-bit HR + SPO2 readings at 400Hz */
ret = regmap_write(data->regmap, MAX30102_REG_SPO2_CONFIG,
(MAX30102_REG_SPO2_CONFIG_ADC_4096_STEPS
<< MAX30102_REG_SPO2_CONFIG_ADC_MASK_SHIFT) |
(MAX30102_REG_SPO2_CONFIG_SR_400HZ
<< MAX30102_REG_SPO2_CONFIG_SR_MASK_SHIFT) |
MAX30102_REG_SPO2_CONFIG_PULSE_411_US);
if (ret)
return ret;
/* enable SPO2 mode */
ret = regmap_update_bits(data->regmap, MAX30102_REG_MODE_CONFIG,
MAX30102_REG_MODE_CONFIG_MODE_MASK,
MAX30102_REG_MODE_CONFIG_MODE_HR_EN |
MAX30102_REG_MODE_CONFIG_MODE_SPO2_EN);
if (ret)
return ret;
/* average 4 samples + generate FIFO interrupt */
ret = regmap_write(data->regmap, MAX30102_REG_FIFO_CONFIG,
(MAX30102_REG_FIFO_CONFIG_AVG_4SAMPLES
<< MAX30102_REG_FIFO_CONFIG_AVG_SHIFT) |
MAX30102_REG_FIFO_CONFIG_AFULL);
if (ret)
return ret;
/* enable FIFO interrupt */
return regmap_update_bits(data->regmap, MAX30102_REG_INT_ENABLE,
MAX30102_REG_INT_ENABLE_MASK,
MAX30102_REG_INT_ENABLE_FIFO_EN);
}
static int max30102_read_temp(struct max30102_data *data, int *val)
{
int ret;
unsigned int reg;
ret = regmap_read(data->regmap, MAX30102_REG_TEMP_INTEGER, &reg);
if (ret < 0)
return ret;
*val = reg << 4;
ret = regmap_read(data->regmap, MAX30102_REG_TEMP_FRACTION, &reg);
if (ret < 0)
return ret;
*val |= reg & 0xf;
*val = sign_extend32(*val, 11);
return 0;
}
static int max30102_get_temp(struct max30102_data *data, int *val)
{
int ret;
/* start acquisition */
ret = regmap_update_bits(data->regmap, MAX30102_REG_TEMP_CONFIG,
MAX30102_REG_TEMP_CONFIG_TEMP_EN,
MAX30102_REG_TEMP_CONFIG_TEMP_EN);
if (ret)
return ret;
msleep(35);
return max30102_read_temp(data, val);
}
static int max30102_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct max30102_data *data = iio_priv(indio_dev);
int ret = -EINVAL;
switch (mask) {
case IIO_CHAN_INFO_RAW:
/*
* Temperature reading can only be acquired while engine
* is running
*/
mutex_lock(&indio_dev->mlock);
if (!iio_buffer_enabled(indio_dev))
ret = -EBUSY;
else {
ret = max30102_get_temp(data, val);
if (!ret)
ret = IIO_VAL_INT;
}
mutex_unlock(&indio_dev->mlock);
break;
case IIO_CHAN_INFO_SCALE:
*val = 1; /* 0.0625 */
*val2 = 16;
ret = IIO_VAL_FRACTIONAL;
break;
}
return ret;
}
static const struct iio_info max30102_info = {
.driver_module = THIS_MODULE,
.read_raw = max30102_read_raw,
};
static int max30102_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct max30102_data *data;
struct iio_buffer *buffer;
struct iio_dev *indio_dev;
int ret;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
buffer = devm_iio_kfifo_allocate(&client->dev);
if (!buffer)
return -ENOMEM;
iio_device_attach_buffer(indio_dev, buffer);
indio_dev->name = MAX30102_DRV_NAME;
indio_dev->channels = max30102_channels;
indio_dev->info = &max30102_info;
indio_dev->num_channels = ARRAY_SIZE(max30102_channels);
indio_dev->available_scan_masks = max30102_scan_masks;
indio_dev->modes = (INDIO_BUFFER_SOFTWARE | INDIO_DIRECT_MODE);
indio_dev->setup_ops = &max30102_buffer_setup_ops;
indio_dev->dev.parent = &client->dev;
data = iio_priv(indio_dev);
data->indio_dev = indio_dev;
data->client = client;
mutex_init(&data->lock);
i2c_set_clientdata(client, indio_dev);
data->regmap = devm_regmap_init_i2c(client, &max30102_regmap_config);
if (IS_ERR(data->regmap)) {
dev_err(&client->dev, "regmap initialization failed.\n");
return PTR_ERR(data->regmap);
}
max30102_set_powermode(data, false);
ret = max30102_chip_init(data);
if (ret)
return ret;
if (client->irq <= 0) {
dev_err(&client->dev, "no valid irq defined\n");
return -EINVAL;
}
ret = devm_request_threaded_irq(&client->dev, client->irq,
NULL, max30102_interrupt_handler,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
"max30102_irq", indio_dev);
if (ret) {
dev_err(&client->dev, "request irq (%d) failed\n", client->irq);
return ret;
}
return iio_device_register(indio_dev);
}
static int max30102_remove(struct i2c_client *client)
{
struct iio_dev *indio_dev = i2c_get_clientdata(client);
struct max30102_data *data = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
max30102_set_powermode(data, false);
return 0;
}
static const struct i2c_device_id max30102_id[] = {
{ "max30102", 0 },
{}
};
MODULE_DEVICE_TABLE(i2c, max30102_id);
static const struct of_device_id max30102_dt_ids[] = {
{ .compatible = "maxim,max30102" },
{ }
};
MODULE_DEVICE_TABLE(of, max30102_dt_ids);
static struct i2c_driver max30102_driver = {
.driver = {
.name = MAX30102_DRV_NAME,
.of_match_table = of_match_ptr(max30102_dt_ids),
},
.probe = max30102_probe,
.remove = max30102_remove,
.id_table = max30102_id,
};
module_i2c_driver(max30102_driver);
MODULE_AUTHOR("Matt Ranostay <[email protected]>");
MODULE_DESCRIPTION("MAX30102 heart rate and pulse oximeter sensor");
MODULE_LICENSE("GPL");

View file

@ -36,6 +36,20 @@ config HDC100X
To compile this driver as a module, choose M here: the module
will be called hdc100x.
config HID_SENSOR_HUMIDITY
tristate "HID Environmental humidity sensor"
depends on HID_SENSOR_HUB
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
select HID_SENSOR_IIO_COMMON
select HID_SENSOR_IIO_TRIGGER
help
Say yes here to build support for the HID SENSOR
humidity driver
To compile this driver as a module, choose M here: the module
will be called hid-sensor-humidity.
config HTS221
tristate "STMicroelectronics HTS221 sensor Driver"
depends on (I2C || SPI)

View file

@ -5,6 +5,7 @@
obj-$(CONFIG_AM2315) += am2315.o
obj-$(CONFIG_DHT11) += dht11.o
obj-$(CONFIG_HDC100X) += hdc100x.o
obj-$(CONFIG_HID_SENSOR_HUMIDITY) += hid-sensor-humidity.o
hts221-y := hts221_core.o \
hts221_buffer.o
@ -15,3 +16,5 @@ obj-$(CONFIG_HTS221_SPI) += hts221_spi.o
obj-$(CONFIG_HTU21) += htu21.o
obj-$(CONFIG_SI7005) += si7005.o
obj-$(CONFIG_SI7020) += si7020.o
ccflags-y += -I$(srctree)/drivers/iio/common/hid-sensors

View file

@ -79,7 +79,7 @@ static struct attribute *hdc100x_attributes[] = {
NULL
};
static struct attribute_group hdc100x_attribute_group = {
static const struct attribute_group hdc100x_attribute_group = {
.attrs = hdc100x_attributes,
};

View file

@ -0,0 +1,315 @@
/*
* HID Sensors Driver
* Copyright (c) 2017, Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program.
*/
#include <linux/device.h>
#include <linux/hid-sensor-hub.h>
#include <linux/iio/buffer.h>
#include <linux/iio/iio.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include "hid-sensor-trigger.h"
struct hid_humidity_state {
struct hid_sensor_common common_attributes;
struct hid_sensor_hub_attribute_info humidity_attr;
s32 humidity_data;
int scale_pre_decml;
int scale_post_decml;
int scale_precision;
int value_offset;
};
/* Channel definitions */
static const struct iio_chan_spec humidity_channels[] = {
{
.type = IIO_HUMIDITYRELATIVE,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
BIT(IIO_CHAN_INFO_HYSTERESIS),
},
IIO_CHAN_SOFT_TIMESTAMP(1)
};
/* Adjust channel real bits based on report descriptor */
static void humidity_adjust_channel_bit_mask(struct iio_chan_spec *channels,
int channel, int size)
{
channels[channel].scan_type.sign = 's';
/* Real storage bits will change based on the report desc. */
channels[channel].scan_type.realbits = size * 8;
/* Maximum size of a sample to capture is s32 */
channels[channel].scan_type.storagebits = sizeof(s32) * 8;
}
static int humidity_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct hid_humidity_state *humid_st = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_RAW:
if (chan->type != IIO_HUMIDITYRELATIVE)
return -EINVAL;
hid_sensor_power_state(&humid_st->common_attributes, true);
*val = sensor_hub_input_attr_get_raw_value(
humid_st->common_attributes.hsdev,
HID_USAGE_SENSOR_HUMIDITY,
HID_USAGE_SENSOR_ATMOSPHERIC_HUMIDITY,
humid_st->humidity_attr.report_id,
SENSOR_HUB_SYNC);
hid_sensor_power_state(&humid_st->common_attributes, false);
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
*val = humid_st->scale_pre_decml;
*val2 = humid_st->scale_post_decml;
return humid_st->scale_precision;
case IIO_CHAN_INFO_OFFSET:
*val = humid_st->value_offset;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SAMP_FREQ:
return hid_sensor_read_samp_freq_value(
&humid_st->common_attributes, val, val2);
case IIO_CHAN_INFO_HYSTERESIS:
return hid_sensor_read_raw_hyst_value(
&humid_st->common_attributes, val, val2);
default:
return -EINVAL;
}
}
static int humidity_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
struct hid_humidity_state *humid_st = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
return hid_sensor_write_samp_freq_value(
&humid_st->common_attributes, val, val2);
case IIO_CHAN_INFO_HYSTERESIS:
return hid_sensor_write_raw_hyst_value(
&humid_st->common_attributes, val, val2);
default:
return -EINVAL;
}
}
static const struct iio_info humidity_info = {
.driver_module = THIS_MODULE,
.read_raw = &humidity_read_raw,
.write_raw = &humidity_write_raw,
};
/* Callback handler to send event after all samples are received and captured */
static int humidity_proc_event(struct hid_sensor_hub_device *hsdev,
unsigned int usage_id, void *pdev)
{
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
struct hid_humidity_state *humid_st = iio_priv(indio_dev);
if (atomic_read(&humid_st->common_attributes.data_ready))
iio_push_to_buffers_with_timestamp(indio_dev,
&humid_st->humidity_data,
iio_get_time_ns(indio_dev));
return 0;
}
/* Capture samples in local storage */
static int humidity_capture_sample(struct hid_sensor_hub_device *hsdev,
unsigned int usage_id, size_t raw_len,
char *raw_data, void *pdev)
{
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
struct hid_humidity_state *humid_st = iio_priv(indio_dev);
switch (usage_id) {
case HID_USAGE_SENSOR_ATMOSPHERIC_HUMIDITY:
humid_st->humidity_data = *(s32 *)raw_data;
return 0;
default:
return -EINVAL;
}
}
/* Parse report which is specific to an usage id */
static int humidity_parse_report(struct platform_device *pdev,
struct hid_sensor_hub_device *hsdev,
struct iio_chan_spec *channels,
unsigned int usage_id,
struct hid_humidity_state *st)
{
int ret;
ret = sensor_hub_input_get_attribute_info(hsdev, HID_INPUT_REPORT,
usage_id,
HID_USAGE_SENSOR_ATMOSPHERIC_HUMIDITY,
&st->humidity_attr);
if (ret < 0)
return ret;
humidity_adjust_channel_bit_mask(channels, 0, st->humidity_attr.size);
st->scale_precision = hid_sensor_format_scale(
HID_USAGE_SENSOR_HUMIDITY,
&st->humidity_attr,
&st->scale_pre_decml,
&st->scale_post_decml);
/* Set Sensitivity field ids, when there is no individual modifier */
if (st->common_attributes.sensitivity.index < 0)
sensor_hub_input_get_attribute_info(hsdev,
HID_FEATURE_REPORT, usage_id,
HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS |
HID_USAGE_SENSOR_ATMOSPHERIC_HUMIDITY,
&st->common_attributes.sensitivity);
return ret;
}
static struct hid_sensor_hub_callbacks humidity_callbacks = {
.send_event = &humidity_proc_event,
.capture_sample = &humidity_capture_sample,
};
/* Function to initialize the processing for usage id */
static int hid_humidity_probe(struct platform_device *pdev)
{
static const char *name = "humidity";
struct iio_dev *indio_dev;
struct hid_humidity_state *humid_st;
struct iio_chan_spec *humid_chans;
struct hid_sensor_hub_device *hsdev = dev_get_platdata(&pdev->dev);
int ret;
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*humid_st));
if (!indio_dev)
return -ENOMEM;
humid_st = iio_priv(indio_dev);
humid_st->common_attributes.hsdev = hsdev;
humid_st->common_attributes.pdev = pdev;
ret = hid_sensor_parse_common_attributes(hsdev,
HID_USAGE_SENSOR_HUMIDITY,
&humid_st->common_attributes);
if (ret)
return ret;
humid_chans = devm_kmemdup(&indio_dev->dev, humidity_channels,
sizeof(humidity_channels), GFP_KERNEL);
if (!humid_chans)
return -ENOMEM;
ret = humidity_parse_report(pdev, hsdev, humid_chans,
HID_USAGE_SENSOR_HUMIDITY, humid_st);
if (ret)
return ret;
indio_dev->channels = humid_chans;
indio_dev->num_channels = ARRAY_SIZE(humidity_channels);
indio_dev->dev.parent = &pdev->dev;
indio_dev->info = &humidity_info;
indio_dev->name = name;
indio_dev->modes = INDIO_DIRECT_MODE;
ret = devm_iio_triggered_buffer_setup(&pdev->dev, indio_dev,
&iio_pollfunc_store_time, NULL, NULL);
if (ret)
return ret;
atomic_set(&humid_st->common_attributes.data_ready, 0);
ret = hid_sensor_setup_trigger(indio_dev, name,
&humid_st->common_attributes);
if (ret)
return ret;
platform_set_drvdata(pdev, indio_dev);
humidity_callbacks.pdev = pdev;
ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_HUMIDITY,
&humidity_callbacks);
if (ret)
goto error_remove_trigger;
ret = iio_device_register(indio_dev);
if (ret)
goto error_remove_callback;
return ret;
error_remove_callback:
sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_HUMIDITY);
error_remove_trigger:
hid_sensor_remove_trigger(&humid_st->common_attributes);
return ret;
}
/* Function to deinitialize the processing for usage id */
static int hid_humidity_remove(struct platform_device *pdev)
{
struct hid_sensor_hub_device *hsdev = dev_get_platdata(&pdev->dev);
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
struct hid_humidity_state *humid_st = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_HUMIDITY);
hid_sensor_remove_trigger(&humid_st->common_attributes);
return 0;
}
static const struct platform_device_id hid_humidity_ids[] = {
{
/* Format: HID-SENSOR-usage_id_in_hex_lowercase */
.name = "HID-SENSOR-200032",
},
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(platform, hid_humidity_ids);
static struct platform_driver hid_humidity_platform_driver = {
.id_table = hid_humidity_ids,
.driver = {
.name = KBUILD_MODNAME,
.pm = &hid_sensor_pm_ops,
},
.probe = hid_humidity_probe,
.remove = hid_humidity_remove,
};
module_platform_driver(hid_humidity_platform_driver);
MODULE_DESCRIPTION("HID Environmental humidity sensor");
MODULE_AUTHOR("Song Hongyan <[email protected]>");
MODULE_LICENSE("GPL v2");

View file

@ -41,7 +41,7 @@ static const struct iio_trigger_ops hts221_trigger_ops = {
static irqreturn_t hts221_trigger_handler_thread(int irq, void *private)
{
struct hts221_hw *hw = (struct hts221_hw *)private;
struct hts221_hw *hw = private;
u8 status;
int err;

View file

@ -113,6 +113,12 @@ static const struct inv_mpu6050_hw hw_info[] = {
.reg = &reg_set_6050,
.config = &chip_config_6050,
},
{
.whoami = INV_MPU9250_WHOAMI_VALUE,
.name = "MPU9250",
.reg = &reg_set_6500,
.config = &chip_config_6050,
},
{
.whoami = INV_ICM20608_WHOAMI_VALUE,
.name = "ICM20608",

View file

@ -17,6 +17,7 @@
#include <linux/i2c.h>
#include <linux/iio/iio.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include "inv_mpu_iio.h"
static const struct regmap_config inv_mpu_regmap_config = {
@ -69,7 +70,8 @@ static int inv_mpu6050_deselect_bypass(struct i2c_mux_core *muxc, u32 chan_id)
return 0;
}
static const char *inv_mpu_match_acpi_device(struct device *dev, int *chip_id)
static const char *inv_mpu_match_acpi_device(struct device *dev,
enum inv_devices *chip_id)
{
const struct acpi_device_id *id;
@ -93,7 +95,8 @@ static int inv_mpu_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct inv_mpu6050_state *st;
int result, chip_type;
int result;
enum inv_devices chip_type;
struct regmap *regmap;
const char *name;
@ -101,8 +104,13 @@ static int inv_mpu_probe(struct i2c_client *client,
I2C_FUNC_SMBUS_I2C_BLOCK))
return -EOPNOTSUPP;
if (id) {
chip_type = (int)id->driver_data;
if (client->dev.of_node) {
chip_type = (enum inv_devices)
of_device_get_match_data(&client->dev);
name = client->name;
} else if (id) {
chip_type = (enum inv_devices)
id->driver_data;
name = id->name;
} else if (ACPI_HANDLE(&client->dev)) {
name = inv_mpu_match_acpi_device(&client->dev, &chip_type);
@ -170,12 +178,38 @@ static const struct i2c_device_id inv_mpu_id[] = {
{"mpu6050", INV_MPU6050},
{"mpu6500", INV_MPU6500},
{"mpu9150", INV_MPU9150},
{"mpu9250", INV_MPU9250},
{"icm20608", INV_ICM20608},
{}
};
MODULE_DEVICE_TABLE(i2c, inv_mpu_id);
static const struct of_device_id inv_of_match[] = {
{
.compatible = "invensense,mpu6050",
.data = (void *)INV_MPU6050
},
{
.compatible = "invensense,mpu6500",
.data = (void *)INV_MPU6500
},
{
.compatible = "invensense,mpu9150",
.data = (void *)INV_MPU9150
},
{
.compatible = "invensense,mpu9250",
.data = (void *)INV_MPU9250
},
{
.compatible = "invensense,icm20608",
.data = (void *)INV_ICM20608
},
{ }
};
MODULE_DEVICE_TABLE(of, inv_of_match);
static const struct acpi_device_id inv_acpi_match[] = {
{"INVN6500", INV_MPU6500},
{ },
@ -188,6 +222,7 @@ static struct i2c_driver inv_mpu_driver = {
.remove = inv_mpu_remove,
.id_table = inv_mpu_id,
.driver = {
.of_match_table = inv_of_match,
.acpi_match_table = ACPI_PTR(inv_acpi_match),
.name = "inv-mpu6050-i2c",
.pm = &inv_mpu_pmops,

View file

@ -70,6 +70,7 @@ enum inv_devices {
INV_MPU6500,
INV_MPU6000,
INV_MPU9150,
INV_MPU9250,
INV_ICM20608,
INV_NUM_PARTS
};
@ -226,6 +227,7 @@ struct inv_mpu6050_state {
#define INV_MPU6050_WHOAMI_VALUE 0x68
#define INV_MPU6500_WHOAMI_VALUE 0x70
#define INV_MPU9150_WHOAMI_VALUE 0x68
#define INV_MPU9250_WHOAMI_VALUE 0x71
#define INV_ICM20608_WHOAMI_VALUE 0xAF
/* scan element definition */

View file

@ -82,6 +82,7 @@ static const struct spi_device_id inv_mpu_id[] = {
{"mpu6000", INV_MPU6000},
{"mpu6500", INV_MPU6500},
{"mpu9150", INV_MPU9150},
{"mpu9250", INV_MPU9250},
{"icm20608", INV_ICM20608},
{}
};

View file

@ -8,7 +8,7 @@ config IIO_ST_LSM6DSX
select IIO_ST_LSM6DSX_SPI if (SPI_MASTER)
help
Say yes here to build support for STMicroelectronics LSM6DSx imu
sensor. Supported devices: lsm6ds3, lsm6dsm
sensor. Supported devices: lsm6ds3, lsm6ds3h, lsm6dsl, lsm6dsm
To compile this driver as a module, choose M here: the module
will be called st_lsm6dsx.

View file

@ -15,11 +15,16 @@
#include <linux/device.h>
#define ST_LSM6DS3_DEV_NAME "lsm6ds3"
#define ST_LSM6DS3H_DEV_NAME "lsm6ds3h"
#define ST_LSM6DSL_DEV_NAME "lsm6dsl"
#define ST_LSM6DSM_DEV_NAME "lsm6dsm"
enum st_lsm6dsx_hw_id {
ST_LSM6DS3_ID,
ST_LSM6DS3H_ID,
ST_LSM6DSL_ID,
ST_LSM6DSM_ID,
ST_LSM6DSX_MAX_ID,
};
#define ST_LSM6DSX_CHAN_SIZE 2
@ -50,7 +55,7 @@ struct st_lsm6dsx_reg {
struct st_lsm6dsx_settings {
u8 wai;
u16 max_fifo_size;
enum st_lsm6dsx_hw_id id;
enum st_lsm6dsx_hw_id id[ST_LSM6DSX_MAX_ID];
};
enum st_lsm6dsx_sensor_id {
@ -66,6 +71,7 @@ enum st_lsm6dsx_fifo_mode {
/**
* struct st_lsm6dsx_sensor - ST IMU sensor instance
* @name: Sensor name.
* @id: Sensor identifier.
* @hw: Pointer to instance of struct st_lsm6dsx_hw.
* @gain: Configured sensor sensitivity.
@ -78,6 +84,7 @@ enum st_lsm6dsx_fifo_mode {
* @ts: Latest timestamp from the interrupt handler.
*/
struct st_lsm6dsx_sensor {
char name[32];
enum st_lsm6dsx_sensor_id id;
struct st_lsm6dsx_hw *hw;
@ -128,7 +135,7 @@ struct st_lsm6dsx_hw {
#endif /* CONFIG_SPI_MASTER */
};
int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id,
int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, const char *name,
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);

View file

@ -1,9 +1,10 @@
/*
* 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):
* LSM6DS3/LSM6DS3H/LSM6DSL/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
@ -206,7 +207,7 @@ out:
}
/**
* st_lsm6dsx_read_fifo() - LSM6DS3-LSM6DSM read FIFO routine
* st_lsm6dsx_read_fifo() - LSM6DS3-LSM6DS3H-LSM6DSL-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.
@ -363,7 +364,7 @@ static int st_lsm6dsx_update_fifo(struct iio_dev *iio_dev, bool enable)
static irqreturn_t st_lsm6dsx_handler_irq(int irq, void *private)
{
struct st_lsm6dsx_hw *hw = (struct st_lsm6dsx_hw *)private;
struct st_lsm6dsx_hw *hw = private;
struct st_lsm6dsx_sensor *sensor;
int i;
@ -387,7 +388,7 @@ static irqreturn_t st_lsm6dsx_handler_irq(int irq, void *private)
static irqreturn_t st_lsm6dsx_handler_thread(int irq, void *private)
{
struct st_lsm6dsx_hw *hw = (struct st_lsm6dsx_hw *)private;
struct st_lsm6dsx_hw *hw = private;
int count;
mutex_lock(&hw->fifo_lock);

View file

@ -17,7 +17,7 @@
* - Gyroscope supported full-scale [dps]: +-125/+-245/+-500/+-1000/+-2000
* - FIFO size: 8KB
*
* - LSM6DSM:
* - LSM6DS3H/LSM6DSL/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
@ -74,12 +74,6 @@
#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)
@ -164,14 +158,26 @@ static const struct st_lsm6dsx_fs_table_entry st_lsm6dsx_fs_table[] = {
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 = 0x69,
.max_fifo_size = 8192,
.id = {
[0] = ST_LSM6DS3_ID,
},
},
{
.wai = ST_LSM6DSM_WHOAMI,
.max_fifo_size = ST_LSM6DSM_MAX_FIFO_SIZE,
.id = ST_LSM6DSM_ID,
.wai = 0x69,
.max_fifo_size = 4096,
.id = {
[0] = ST_LSM6DS3H_ID,
},
},
{
.wai = 0x6a,
.max_fifo_size = 4096,
.id = {
[0] = ST_LSM6DSL_ID,
[1] = ST_LSM6DSM_ID,
},
},
};
@ -241,11 +247,15 @@ out:
static int st_lsm6dsx_check_whoami(struct st_lsm6dsx_hw *hw, int id)
{
int err, i;
int err, i, j;
u8 data;
for (i = 0; i < ARRAY_SIZE(st_lsm6dsx_sensor_settings); i++) {
if (id == st_lsm6dsx_sensor_settings[i].id)
for (j = 0; j < ST_LSM6DSX_MAX_ID; j++) {
if (id == st_lsm6dsx_sensor_settings[i].id[j])
break;
}
if (j < ST_LSM6DSX_MAX_ID)
break;
}
@ -298,32 +308,40 @@ static int st_lsm6dsx_set_full_scale(struct st_lsm6dsx_sensor *sensor,
return 0;
}
static int st_lsm6dsx_set_odr(struct st_lsm6dsx_sensor *sensor, u16 odr)
static int st_lsm6dsx_check_odr(struct st_lsm6dsx_sensor *sensor, u16 odr,
u8 *val)
{
enum st_lsm6dsx_sensor_id id = sensor->id;
int i, err;
u8 val;
int i;
for (i = 0; i < ST_LSM6DSX_ODR_LIST_SIZE; i++)
if (st_lsm6dsx_odr_table[id].odr_avl[i].hz == odr)
if (st_lsm6dsx_odr_table[sensor->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;
*val = st_lsm6dsx_odr_table[sensor->id].odr_avl[i].val;
sensor->odr = odr;
return 0;
}
static int st_lsm6dsx_set_odr(struct st_lsm6dsx_sensor *sensor, u16 odr)
{
enum st_lsm6dsx_sensor_id id = sensor->id;
int err;
u8 val;
err = st_lsm6dsx_check_odr(sensor, odr, &val);
if (err < 0)
return err;
return st_lsm6dsx_write_with_mask(sensor->hw,
st_lsm6dsx_odr_table[id].reg.addr,
st_lsm6dsx_odr_table[id].reg.mask,
val);
}
int st_lsm6dsx_sensor_enable(struct st_lsm6dsx_sensor *sensor)
{
int err;
@ -426,9 +444,12 @@ static int st_lsm6dsx_write_raw(struct iio_dev *iio_dev,
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);
case IIO_CHAN_INFO_SAMP_FREQ: {
u8 data;
err = st_lsm6dsx_check_odr(sensor, val, &data);
break;
}
default:
err = -EINVAL;
break;
@ -538,19 +559,11 @@ static const unsigned long st_lsm6dsx_available_scan_masks[] = {0x7, 0x0};
static int st_lsm6dsx_of_get_drdy_pin(struct st_lsm6dsx_hw *hw, int *drdy_pin)
{
struct device_node *np = hw->dev->of_node;
int err;
if (!np)
return -EINVAL;
err = of_property_read_u32(np, "st,drdy-int-pin", drdy_pin);
if (err == -ENODATA) {
/* if the property has not been specified use default value */
*drdy_pin = 1;
err = 0;
}
return err;
return of_property_read_u32(np, "st,drdy-int-pin", drdy_pin);
}
static int st_lsm6dsx_get_drdy_reg(struct st_lsm6dsx_hw *hw, u8 *drdy_reg)
@ -621,7 +634,8 @@ static int st_lsm6dsx_init_device(struct st_lsm6dsx_hw *hw)
}
static struct iio_dev *st_lsm6dsx_alloc_iiodev(struct st_lsm6dsx_hw *hw,
enum st_lsm6dsx_sensor_id id)
enum st_lsm6dsx_sensor_id id,
const char *name)
{
struct st_lsm6dsx_sensor *sensor;
struct iio_dev *iio_dev;
@ -645,27 +659,30 @@ static struct iio_dev *st_lsm6dsx_alloc_iiodev(struct st_lsm6dsx_hw *hw,
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;
scnprintf(sensor->name, sizeof(sensor->name), "%s_accel",
name);
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;
scnprintf(sensor->name, sizeof(sensor->name), "%s_gyro",
name);
break;
default:
return NULL;
}
iio_dev->name = sensor->name;
return iio_dev;
}
int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id,
int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, const char *name,
const struct st_lsm6dsx_transfer_function *tf_ops)
{
struct st_lsm6dsx_hw *hw;
@ -689,7 +706,7 @@ int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id,
return err;
for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
hw->iio_devs[i] = st_lsm6dsx_alloc_iiodev(hw, i);
hw->iio_devs[i] = st_lsm6dsx_alloc_iiodev(hw, i, name);
if (!hw->iio_devs[i])
return -ENOMEM;
}

View file

@ -61,7 +61,7 @@ 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,
(int)id->driver_data, id->name,
&st_lsm6dsx_transfer_fn);
}
@ -70,6 +70,14 @@ static const struct of_device_id st_lsm6dsx_i2c_of_match[] = {
.compatible = "st,lsm6ds3",
.data = (void *)ST_LSM6DS3_ID,
},
{
.compatible = "st,lsm6ds3h",
.data = (void *)ST_LSM6DS3H_ID,
},
{
.compatible = "st,lsm6dsl",
.data = (void *)ST_LSM6DSL_ID,
},
{
.compatible = "st,lsm6dsm",
.data = (void *)ST_LSM6DSM_ID,
@ -80,6 +88,8 @@ 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_LSM6DS3H_DEV_NAME, ST_LSM6DS3H_ID },
{ ST_LSM6DSL_DEV_NAME, ST_LSM6DSL_ID },
{ ST_LSM6DSM_DEV_NAME, ST_LSM6DSM_ID },
{},
};

View file

@ -78,7 +78,7 @@ 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,
(int)id->driver_data, id->name,
&st_lsm6dsx_transfer_fn);
}
@ -87,6 +87,14 @@ static const struct of_device_id st_lsm6dsx_spi_of_match[] = {
.compatible = "st,lsm6ds3",
.data = (void *)ST_LSM6DS3_ID,
},
{
.compatible = "st,lsm6ds3h",
.data = (void *)ST_LSM6DS3H_ID,
},
{
.compatible = "st,lsm6dsl",
.data = (void *)ST_LSM6DSL_ID,
},
{
.compatible = "st,lsm6dsm",
.data = (void *)ST_LSM6DSM_ID,
@ -97,6 +105,8 @@ 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_LSM6DS3H_DEV_NAME, ST_LSM6DS3H_ID },
{ ST_LSM6DSL_DEV_NAME, ST_LSM6DSL_ID },
{ ST_LSM6DSM_DEV_NAME, ST_LSM6DSM_ID },
{},
};

View file

@ -136,6 +136,16 @@ config CM36651
To compile this driver as a module, choose M here:
the module will be called cm36651.
config IIO_CROS_EC_LIGHT_PROX
tristate "ChromeOS EC Light and Proximity Sensors"
depends on IIO_CROS_EC_SENSORS_CORE
help
Say Y here if you use the light and proximity sensors
presented by the ChromeOS EC Sensor hub.
To compile this driver as a module, choose M here:
the module will be called cros_ec_light_prox.
config GP2AP020A00F
tristate "Sharp GP2AP020A00F Proximity/ALS sensor"
depends on I2C
@ -395,4 +405,14 @@ config VEML6070
To compile this driver as a module, choose M here: the
module will be called veml6070.
config VL6180
tristate "VL6180 ALS, range and proximity sensor"
depends on I2C
help
Say Y here if you want to build a driver for the STMicroelectronics
VL6180 combined ambient light, range and proximity sensor.
To compile this driver as a module, choose M here: the
module will be called vl6180.
endmenu

View file

@ -15,6 +15,7 @@ obj-$(CONFIG_CM3232) += cm3232.o
obj-$(CONFIG_CM3323) += cm3323.o
obj-$(CONFIG_CM3605) += cm3605.o
obj-$(CONFIG_CM36651) += cm36651.o
obj-$(CONFIG_IIO_CROS_EC_LIGHT_PROX) += cros_ec_light_prox.o
obj-$(CONFIG_GP2AP020A00F) += gp2ap020a00f.o
obj-$(CONFIG_HID_SENSOR_ALS) += hid-sensor-als.o
obj-$(CONFIG_HID_SENSOR_PROX) += hid-sensor-prox.o
@ -37,3 +38,4 @@ obj-$(CONFIG_TSL4531) += tsl4531.o
obj-$(CONFIG_US5182D) += us5182d.o
obj-$(CONFIG_VCNL4000) += vcnl4000.o
obj-$(CONFIG_VEML6070) += veml6070.o
obj-$(CONFIG_VL6180) += vl6180.o

View file

@ -343,7 +343,7 @@ static struct attribute *apds9960_attributes[] = {
NULL,
};
static struct attribute_group apds9960_attribute_group = {
static const struct attribute_group apds9960_attribute_group = {
.attrs = apds9960_attributes,
};
@ -1112,6 +1112,8 @@ static int apds9960_runtime_resume(struct device *dev)
#endif
static const struct dev_pm_ops apds9960_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
pm_runtime_force_resume)
SET_RUNTIME_PM_OPS(apds9960_runtime_suspend,
apds9960_runtime_resume, NULL)
};
@ -1122,9 +1124,16 @@ static const struct i2c_device_id apds9960_id[] = {
};
MODULE_DEVICE_TABLE(i2c, apds9960_id);
static const struct of_device_id apds9960_of_match[] = {
{ .compatible = "avago,apds9960" },
{ }
};
MODULE_DEVICE_TABLE(of, apds9960_of_match);
static struct i2c_driver apds9960_driver = {
.driver = {
.name = APDS9960_DRV_NAME,
.of_match_table = apds9960_of_match,
.pm = &apds9960_pm_ops,
},
.probe = apds9960_probe,

View file

@ -212,7 +212,7 @@ static struct attribute *bh1750_attributes[] = {
NULL,
};
static struct attribute_group bh1750_attribute_group = {
static const struct attribute_group bh1750_attribute_group = {
.attrs = bh1750_attributes,
};

View file

@ -0,0 +1,289 @@
/*
* cros_ec_light_prox - Driver for light and prox sensors behing CrosEC.
*
* Copyright (C) 2017 Google, Inc
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/iio/buffer.h>
#include <linux/iio/iio.h>
#include <linux/iio/kfifo_buf.h>
#include <linux/iio/trigger.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/kernel.h>
#include <linux/mfd/cros_ec.h>
#include <linux/mfd/cros_ec_commands.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include "../common/cros_ec_sensors/cros_ec_sensors_core.h"
/*
* We only represent one entry for light or proximity. EC is merging different
* light sensors to return the what the eye would see. For proximity, we
* currently support only one light source.
*/
#define CROS_EC_LIGHT_PROX_MAX_CHANNELS (1 + 1)
/* State data for ec_sensors iio driver. */
struct cros_ec_light_prox_state {
/* Shared by all sensors */
struct cros_ec_sensors_core_state core;
struct iio_chan_spec channels[CROS_EC_LIGHT_PROX_MAX_CHANNELS];
};
static int cros_ec_light_prox_read(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct cros_ec_light_prox_state *st = iio_priv(indio_dev);
u16 data = 0;
s64 val64;
int ret = IIO_VAL_INT;
int idx = chan->scan_index;
mutex_lock(&st->core.cmd_lock);
switch (mask) {
case IIO_CHAN_INFO_RAW:
if (chan->type == IIO_PROXIMITY) {
if (cros_ec_sensors_read_cmd(indio_dev, 1 << idx,
(s16 *)&data) < 0) {
ret = -EIO;
break;
}
*val = data;
} else {
ret = -EINVAL;
}
break;
case IIO_CHAN_INFO_PROCESSED:
if (chan->type == IIO_LIGHT) {
if (cros_ec_sensors_read_cmd(indio_dev, 1 << idx,
(s16 *)&data) < 0) {
ret = -EIO;
break;
}
/*
* The data coming from the light sensor is
* pre-processed and represents the ambient light
* illuminance reading expressed in lux.
*/
*val = data;
ret = IIO_VAL_INT;
} else {
ret = -EINVAL;
}
break;
case IIO_CHAN_INFO_CALIBBIAS:
st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_OFFSET;
st->core.param.sensor_offset.flags = 0;
if (cros_ec_motion_send_host_cmd(&st->core, 0)) {
ret = -EIO;
break;
}
/* Save values */
st->core.calib[0] = st->core.resp->sensor_offset.offset[0];
*val = st->core.calib[idx];
break;
case IIO_CHAN_INFO_CALIBSCALE:
/*
* RANGE is used for calibration
* scale is a number x.y, where x is coded on 16 bits,
* y coded on 16 bits, between 0 and 9999.
*/
st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_RANGE;
st->core.param.sensor_range.data = EC_MOTION_SENSE_NO_VALUE;
if (cros_ec_motion_send_host_cmd(&st->core, 0)) {
ret = -EIO;
break;
}
val64 = st->core.resp->sensor_range.ret;
*val = val64 >> 16;
*val2 = (val64 & 0xffff) * 100;
ret = IIO_VAL_INT_PLUS_MICRO;
break;
default:
ret = cros_ec_sensors_core_read(&st->core, chan, val, val2,
mask);
break;
}
mutex_unlock(&st->core.cmd_lock);
return ret;
}
static int cros_ec_light_prox_write(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
struct cros_ec_light_prox_state *st = iio_priv(indio_dev);
int ret = 0;
int idx = chan->scan_index;
mutex_lock(&st->core.cmd_lock);
switch (mask) {
case IIO_CHAN_INFO_CALIBBIAS:
st->core.calib[idx] = val;
/* Send to EC for each axis, even if not complete */
st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_OFFSET;
st->core.param.sensor_offset.flags = MOTION_SENSE_SET_OFFSET;
st->core.param.sensor_offset.offset[0] = st->core.calib[0];
st->core.param.sensor_offset.temp =
EC_MOTION_SENSE_INVALID_CALIB_TEMP;
if (cros_ec_motion_send_host_cmd(&st->core, 0))
ret = -EIO;
break;
case IIO_CHAN_INFO_CALIBSCALE:
st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_RANGE;
st->core.param.sensor_range.data = (val << 16) | (val2 / 100);
if (cros_ec_motion_send_host_cmd(&st->core, 0))
ret = -EIO;
break;
default:
ret = cros_ec_sensors_core_write(&st->core, chan, val, val2,
mask);
break;
}
mutex_unlock(&st->core.cmd_lock);
return ret;
}
static const struct iio_info cros_ec_light_prox_info = {
.read_raw = &cros_ec_light_prox_read,
.write_raw = &cros_ec_light_prox_write,
.driver_module = THIS_MODULE,
};
static int cros_ec_light_prox_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct cros_ec_dev *ec_dev = dev_get_drvdata(dev->parent);
struct cros_ec_device *ec_device;
struct iio_dev *indio_dev;
struct cros_ec_light_prox_state *state;
struct iio_chan_spec *channel;
int ret;
if (!ec_dev || !ec_dev->ec_dev) {
dev_warn(dev, "No CROS EC device found.\n");
return -EINVAL;
}
ec_device = ec_dev->ec_dev;
indio_dev = devm_iio_device_alloc(dev, sizeof(*state));
if (!indio_dev)
return -ENOMEM;
ret = cros_ec_sensors_core_init(pdev, indio_dev, true);
if (ret)
return ret;
indio_dev->info = &cros_ec_light_prox_info;
state = iio_priv(indio_dev);
state->core.type = state->core.resp->info.type;
state->core.loc = state->core.resp->info.location;
channel = state->channels;
/* Common part */
channel->info_mask_shared_by_all =
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
BIT(IIO_CHAN_INFO_FREQUENCY);
channel->scan_type.realbits = CROS_EC_SENSOR_BITS;
channel->scan_type.storagebits = CROS_EC_SENSOR_BITS;
channel->scan_type.shift = 0;
channel->scan_index = 0;
channel->ext_info = cros_ec_sensors_ext_info;
channel->scan_type.sign = 'u';
state->core.calib[0] = 0;
/* Sensor specific */
switch (state->core.type) {
case MOTIONSENSE_TYPE_LIGHT:
channel->type = IIO_LIGHT;
channel->info_mask_separate =
BIT(IIO_CHAN_INFO_PROCESSED) |
BIT(IIO_CHAN_INFO_CALIBBIAS) |
BIT(IIO_CHAN_INFO_CALIBSCALE);
break;
case MOTIONSENSE_TYPE_PROX:
channel->type = IIO_PROXIMITY;
channel->info_mask_separate =
BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_CALIBBIAS) |
BIT(IIO_CHAN_INFO_CALIBSCALE);
break;
default:
dev_warn(dev, "Unknown motion sensor\n");
return -EINVAL;
}
/* Timestamp */
channel++;
channel->type = IIO_TIMESTAMP;
channel->channel = -1;
channel->scan_index = 1;
channel->scan_type.sign = 's';
channel->scan_type.realbits = 64;
channel->scan_type.storagebits = 64;
indio_dev->channels = state->channels;
indio_dev->num_channels = CROS_EC_LIGHT_PROX_MAX_CHANNELS;
state->core.read_ec_sensors_data = cros_ec_sensors_read_cmd;
ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
cros_ec_sensors_capture, NULL);
if (ret)
return ret;
return devm_iio_device_register(dev, indio_dev);
}
static const struct platform_device_id cros_ec_light_prox_ids[] = {
{
.name = "cros-ec-prox",
},
{
.name = "cros-ec-light",
},
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(platform, cros_ec_light_prox_ids);
static struct platform_driver cros_ec_light_prox_platform_driver = {
.driver = {
.name = "cros-ec-light-prox",
},
.probe = cros_ec_light_prox_probe,
.id_table = cros_ec_light_prox_ids,
};
module_platform_driver(cros_ec_light_prox_platform_driver);
MODULE_DESCRIPTION("ChromeOS EC light/proximity sensors driver");
MODULE_LICENSE("GPL v2");

View file

@ -240,6 +240,13 @@ static int prox_parse_report(struct platform_device *pdev,
st->common_attributes.sensitivity.index,
st->common_attributes.sensitivity.report_id);
}
if (st->common_attributes.sensitivity.index < 0)
sensor_hub_input_get_attribute_info(hsdev,
HID_FEATURE_REPORT, usage_id,
HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS |
HID_USAGE_SENSOR_HUMAN_PRESENCE,
&st->common_attributes.sensitivity);
return ret;
}

View file

@ -690,7 +690,7 @@ static struct attribute *lm3533_als_event_attributes[] = {
NULL
};
static struct attribute_group lm3533_als_event_attribute_group = {
static const struct attribute_group lm3533_als_event_attribute_group = {
.attrs = lm3533_als_event_attributes
};
@ -714,7 +714,7 @@ static struct attribute *lm3533_als_attributes[] = {
NULL
};
static struct attribute_group lm3533_als_attribute_group = {
static const struct attribute_group lm3533_als_attribute_group = {
.attrs = lm3533_als_attributes
};

View file

@ -884,9 +884,19 @@ static const struct i2c_device_id tsl2563_id[] = {
};
MODULE_DEVICE_TABLE(i2c, tsl2563_id);
static const struct of_device_id tsl2563_of_match[] = {
{ .compatible = "amstaos,tsl2560" },
{ .compatible = "amstaos,tsl2561" },
{ .compatible = "amstaos,tsl2562" },
{ .compatible = "amstaos,tsl2563" },
{}
};
MODULE_DEVICE_TABLE(of, tsl2563_of_match);
static struct i2c_driver tsl2563_i2c_driver = {
.driver = {
.name = "tsl2563",
.of_match_table = tsl2563_of_match,
.pm = TSL2563_PM_OPS,
},
.probe = tsl2563_probe,

View file

@ -972,10 +972,17 @@ static const struct i2c_device_id us5182d_id[] = {
MODULE_DEVICE_TABLE(i2c, us5182d_id);
static const struct of_device_id us5182d_of_match[] = {
{ .compatible = "upisemi,usd5182" },
{}
};
MODULE_DEVICE_TABLE(of, us5182d_of_match);
static struct i2c_driver us5182d_driver = {
.driver = {
.name = US5182D_DRV_NAME,
.pm = &us5182d_pm_ops,
.of_match_table = us5182d_of_match,
.acpi_match_table = ACPI_PTR(us5182d_acpi_match),
},
.probe = us5182d_probe,

543
drivers/iio/light/vl6180.c Normal file
View file

@ -0,0 +1,543 @@
/*
* vl6180.c - Support for STMicroelectronics VL6180 ALS, range and proximity
* sensor
*
* Copyright 2017 Peter Meerwald-Stadler <pmeerw@pmeerw.net>
* Copyright 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.
*
* IIO driver for VL6180 (7-bit I2C slave address 0x29)
*
* Range: 0 to 100mm
* ALS: < 1 Lux up to 100 kLux
* IR: 850nm
*
* TODO: irq, threshold events, continuous mode, hardware buffer
*/
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/mutex.h>
#include <linux/err.h>
#include <linux/of.h>
#include <linux/delay.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#define VL6180_DRV_NAME "vl6180"
/* Device identification register and value */
#define VL6180_MODEL_ID 0x000
#define VL6180_MODEL_ID_VAL 0xb4
/* Configuration registers */
#define VL6180_INTR_CONFIG 0x014
#define VL6180_INTR_CLEAR 0x015
#define VL6180_OUT_OF_RESET 0x016
#define VL6180_HOLD 0x017
#define VL6180_RANGE_START 0x018
#define VL6180_ALS_START 0x038
#define VL6180_ALS_GAIN 0x03f
#define VL6180_ALS_IT 0x040
/* Status registers */
#define VL6180_RANGE_STATUS 0x04d
#define VL6180_ALS_STATUS 0x04e
#define VL6180_INTR_STATUS 0x04f
/* Result value registers */
#define VL6180_ALS_VALUE 0x050
#define VL6180_RANGE_VALUE 0x062
#define VL6180_RANGE_RATE 0x066
/* bits of the RANGE_START and ALS_START register */
#define VL6180_MODE_CONT BIT(1) /* continuous mode */
#define VL6180_STARTSTOP BIT(0) /* start measurement, auto-reset */
/* bits of the INTR_STATUS and INTR_CONFIG register */
#define VL6180_ALS_READY BIT(5)
#define VL6180_RANGE_READY BIT(2)
/* bits of the INTR_CLEAR register */
#define VL6180_CLEAR_ERROR BIT(2)
#define VL6180_CLEAR_ALS BIT(1)
#define VL6180_CLEAR_RANGE BIT(0)
/* bits of the HOLD register */
#define VL6180_HOLD_ON BIT(0)
/* default value for the ALS_IT register */
#define VL6180_ALS_IT_100 0x63 /* 100 ms */
/* values for the ALS_GAIN register */
#define VL6180_ALS_GAIN_1 0x46
#define VL6180_ALS_GAIN_1_25 0x45
#define VL6180_ALS_GAIN_1_67 0x44
#define VL6180_ALS_GAIN_2_5 0x43
#define VL6180_ALS_GAIN_5 0x42
#define VL6180_ALS_GAIN_10 0x41
#define VL6180_ALS_GAIN_20 0x40
#define VL6180_ALS_GAIN_40 0x47
struct vl6180_data {
struct i2c_client *client;
struct mutex lock;
};
enum { VL6180_ALS, VL6180_RANGE, VL6180_PROX };
/**
* struct vl6180_chan_regs - Registers for accessing channels
* @drdy_mask: Data ready bit in status register
* @start_reg: Conversion start register
* @value_reg: Result value register
* @word: Register word length
*/
struct vl6180_chan_regs {
u8 drdy_mask;
u16 start_reg, value_reg;
bool word;
};
static const struct vl6180_chan_regs vl6180_chan_regs_table[] = {
[VL6180_ALS] = {
.drdy_mask = VL6180_ALS_READY,
.start_reg = VL6180_ALS_START,
.value_reg = VL6180_ALS_VALUE,
.word = true,
},
[VL6180_RANGE] = {
.drdy_mask = VL6180_RANGE_READY,
.start_reg = VL6180_RANGE_START,
.value_reg = VL6180_RANGE_VALUE,
.word = false,
},
[VL6180_PROX] = {
.drdy_mask = VL6180_RANGE_READY,
.start_reg = VL6180_RANGE_START,
.value_reg = VL6180_RANGE_RATE,
.word = true,
},
};
static int vl6180_read(struct i2c_client *client, u16 cmd, void *databuf,
u8 len)
{
__be16 cmdbuf = cpu_to_be16(cmd);
struct i2c_msg msgs[2] = {
{ .addr = client->addr, .len = sizeof(cmdbuf), .buf = (u8 *) &cmdbuf },
{ .addr = client->addr, .len = len, .buf = databuf,
.flags = I2C_M_RD } };
int ret;
ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
if (ret < 0)
dev_err(&client->dev, "failed reading register 0x%04x\n", cmd);
return ret;
}
static int vl6180_read_byte(struct i2c_client *client, u16 cmd)
{
u8 data;
int ret;
ret = vl6180_read(client, cmd, &data, sizeof(data));
if (ret < 0)
return ret;
return data;
}
static int vl6180_read_word(struct i2c_client *client, u16 cmd)
{
__be16 data;
int ret;
ret = vl6180_read(client, cmd, &data, sizeof(data));
if (ret < 0)
return ret;
return be16_to_cpu(data);
}
static int vl6180_write_byte(struct i2c_client *client, u16 cmd, u8 val)
{
u8 buf[3];
struct i2c_msg msgs[1] = {
{ .addr = client->addr, .len = sizeof(buf), .buf = (u8 *) &buf } };
int ret;
buf[0] = cmd >> 8;
buf[1] = cmd & 0xff;
buf[2] = val;
ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
if (ret < 0) {
dev_err(&client->dev, "failed writing register 0x%04x\n", cmd);
return ret;
}
return 0;
}
static int vl6180_write_word(struct i2c_client *client, u16 cmd, u16 val)
{
__be16 buf[2];
struct i2c_msg msgs[1] = {
{ .addr = client->addr, .len = sizeof(buf), .buf = (u8 *) &buf } };
int ret;
buf[0] = cpu_to_be16(cmd);
buf[1] = cpu_to_be16(val);
ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
if (ret < 0) {
dev_err(&client->dev, "failed writing register 0x%04x\n", cmd);
return ret;
}
return 0;
}
static int vl6180_measure(struct vl6180_data *data, int addr)
{
struct i2c_client *client = data->client;
int tries = 20, ret;
u16 value;
mutex_lock(&data->lock);
/* Start single shot measurement */
ret = vl6180_write_byte(client,
vl6180_chan_regs_table[addr].start_reg, VL6180_STARTSTOP);
if (ret < 0)
goto fail;
while (tries--) {
ret = vl6180_read_byte(client, VL6180_INTR_STATUS);
if (ret < 0)
goto fail;
if (ret & vl6180_chan_regs_table[addr].drdy_mask)
break;
msleep(20);
}
if (tries < 0) {
ret = -EIO;
goto fail;
}
/* Read result value from appropriate registers */
ret = vl6180_chan_regs_table[addr].word ?
vl6180_read_word(client, vl6180_chan_regs_table[addr].value_reg) :
vl6180_read_byte(client, vl6180_chan_regs_table[addr].value_reg);
if (ret < 0)
goto fail;
value = ret;
/* Clear the interrupt flag after data read */
ret = vl6180_write_byte(client, VL6180_INTR_CLEAR,
VL6180_CLEAR_ERROR | VL6180_CLEAR_ALS | VL6180_CLEAR_RANGE);
if (ret < 0)
goto fail;
ret = value;
fail:
mutex_unlock(&data->lock);
return ret;
}
static const struct iio_chan_spec vl6180_channels[] = {
{
.type = IIO_LIGHT,
.address = VL6180_ALS,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_INT_TIME) |
BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_HARDWAREGAIN),
}, {
.type = IIO_DISTANCE,
.address = VL6180_RANGE,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE),
}, {
.type = IIO_PROXIMITY,
.address = VL6180_PROX,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
}
};
/*
* Columns 3 & 4 represent the same value in decimal and hex notations.
* Kept in order to avoid the datatype conversion while reading the
* hardware_gain.
*/
static const int vl6180_als_gain[8][4] = {
{ 1, 0, 70, VL6180_ALS_GAIN_1 },
{ 1, 250000, 69, VL6180_ALS_GAIN_1_25 },
{ 1, 670000, 68, VL6180_ALS_GAIN_1_67 },
{ 2, 500000, 67, VL6180_ALS_GAIN_2_5 },
{ 5, 0, 66, VL6180_ALS_GAIN_5 },
{ 10, 0, 65, VL6180_ALS_GAIN_10 },
{ 20, 0, 64, VL6180_ALS_GAIN_20 },
{ 40, 0, 71, VL6180_ALS_GAIN_40 }
};
static int vl6180_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct vl6180_data *data = iio_priv(indio_dev);
int ret, i;
switch (mask) {
case IIO_CHAN_INFO_RAW:
ret = vl6180_measure(data, chan->address);
if (ret < 0)
return ret;
*val = ret;
return IIO_VAL_INT;
case IIO_CHAN_INFO_INT_TIME:
ret = vl6180_read_word(data->client, VL6180_ALS_IT);
if (ret < 0)
return ret;
*val = 0; /* 1 count = 1ms (0 = 1ms) */
*val2 = (ret + 1) * 1000; /* convert to seconds */
return IIO_VAL_INT_PLUS_MICRO;
case IIO_CHAN_INFO_SCALE:
switch (chan->type) {
case IIO_LIGHT:
*val = 0; /* one ALS count is 0.32 Lux */
*val2 = 320000;
break;
case IIO_DISTANCE:
*val = 0; /* sensor reports mm, scale to meter */
*val2 = 1000;
break;
default:
return -EINVAL;
}
return IIO_VAL_INT_PLUS_MICRO;
case IIO_CHAN_INFO_HARDWAREGAIN:
ret = vl6180_read_byte(data->client, VL6180_ALS_GAIN);
if (ret < 0)
return -EINVAL;
for (i = 0; i < ARRAY_SIZE(vl6180_als_gain); i++) {
if (ret == vl6180_als_gain[i][2]) {
*val = vl6180_als_gain[i][0];
*val2 = vl6180_als_gain[i][1];
}
}
return IIO_VAL_INT_PLUS_MICRO;
default:
return -EINVAL;
}
}
static IIO_CONST_ATTR(als_gain_available, "1 1.25 1.67 2.5 5 10 20 40");
static struct attribute *vl6180_attributes[] = {
&iio_const_attr_als_gain_available.dev_attr.attr,
NULL
};
static const struct attribute_group vl6180_attribute_group = {
.attrs = vl6180_attributes,
};
/* HOLD is needed before updating any config registers */
static int vl6180_hold(struct vl6180_data *data, bool hold)
{
return vl6180_write_byte(data->client, VL6180_HOLD,
hold ? VL6180_HOLD_ON : 0);
}
static int vl6180_set_als_gain(struct vl6180_data *data, int val, int val2)
{
int i, ret;
for (i = 0; i < ARRAY_SIZE(vl6180_als_gain); i++) {
if (val == vl6180_als_gain[i][0] &&
val2 == vl6180_als_gain[i][1]) {
mutex_lock(&data->lock);
ret = vl6180_hold(data, true);
if (ret < 0)
goto fail;
ret = vl6180_write_byte(data->client, VL6180_ALS_GAIN,
vl6180_als_gain[i][3]);
fail:
vl6180_hold(data, false);
mutex_unlock(&data->lock);
return ret;
}
}
return -EINVAL;
}
static int vl6180_set_it(struct vl6180_data *data, int val2)
{
int ret;
mutex_lock(&data->lock);
ret = vl6180_hold(data, true);
if (ret < 0)
goto fail;
ret = vl6180_write_word(data->client, VL6180_ALS_IT,
(val2 - 500) / 1000); /* write value in ms */
fail:
vl6180_hold(data, false);
mutex_unlock(&data->lock);
return ret;
}
static int vl6180_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
struct vl6180_data *data = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_INT_TIME:
if (val != 0 || val2 < 500 || val2 >= 512500)
return -EINVAL;
return vl6180_set_it(data, val2);
case IIO_CHAN_INFO_HARDWAREGAIN:
if (chan->type != IIO_LIGHT)
return -EINVAL;
return vl6180_set_als_gain(data, val, val2);
default:
return -EINVAL;
}
}
static const struct iio_info vl6180_info = {
.read_raw = vl6180_read_raw,
.write_raw = vl6180_write_raw,
.attrs = &vl6180_attribute_group,
.driver_module = THIS_MODULE,
};
static int vl6180_init(struct vl6180_data *data)
{
struct i2c_client *client = data->client;
int ret;
ret = vl6180_read_byte(client, VL6180_MODEL_ID);
if (ret < 0)
return ret;
if (ret != VL6180_MODEL_ID_VAL) {
dev_err(&client->dev, "invalid model ID %02x\n", ret);
return -ENODEV;
}
ret = vl6180_hold(data, true);
if (ret < 0)
return ret;
ret = vl6180_read_byte(client, VL6180_OUT_OF_RESET);
if (ret < 0)
return ret;
/*
* Detect false reset condition here. This bit is always set when the
* system comes out of reset.
*/
if (ret != 0x01)
dev_info(&client->dev, "device is not fresh out of reset\n");
/* Enable ALS and Range ready interrupts */
ret = vl6180_write_byte(client, VL6180_INTR_CONFIG,
VL6180_ALS_READY | VL6180_RANGE_READY);
if (ret < 0)
return ret;
/* ALS integration time: 100ms */
ret = vl6180_write_word(client, VL6180_ALS_IT, VL6180_ALS_IT_100);
if (ret < 0)
return ret;
/* ALS gain: 1 */
ret = vl6180_write_byte(client, VL6180_ALS_GAIN, VL6180_ALS_GAIN_1);
if (ret < 0)
return ret;
ret = vl6180_write_byte(client, VL6180_OUT_OF_RESET, 0x00);
if (ret < 0)
return ret;
return vl6180_hold(data, false);
}
static int vl6180_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct vl6180_data *data;
struct iio_dev *indio_dev;
int ret;
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;
mutex_init(&data->lock);
indio_dev->dev.parent = &client->dev;
indio_dev->info = &vl6180_info;
indio_dev->channels = vl6180_channels;
indio_dev->num_channels = ARRAY_SIZE(vl6180_channels);
indio_dev->name = VL6180_DRV_NAME;
indio_dev->modes = INDIO_DIRECT_MODE;
ret = vl6180_init(data);
if (ret < 0)
return ret;
return devm_iio_device_register(&client->dev, indio_dev);
}
static const struct of_device_id vl6180_of_match[] = {
{ .compatible = "st,vl6180", },
{ },
};
MODULE_DEVICE_TABLE(of, vl6180_of_match);
static const struct i2c_device_id vl6180_id[] = {
{ "vl6180", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, vl6180_id);
static struct i2c_driver vl6180_driver = {
.driver = {
.name = VL6180_DRV_NAME,
.of_match_table = of_match_ptr(vl6180_of_match),
},
.probe = vl6180_probe,
.id_table = vl6180_id,
};
module_i2c_driver(vl6180_driver);
MODULE_AUTHOR("Peter Meerwald-Stadler <[email protected]>");
MODULE_AUTHOR("Manivannan Sadhasivam <[email protected]>");
MODULE_DESCRIPTION("STMicro VL6180 ALS, range and proximity sensor driver");
MODULE_LICENSE("GPL");

View file

@ -63,9 +63,18 @@ static const struct i2c_device_id bmc150_magn_i2c_id[] = {
};
MODULE_DEVICE_TABLE(i2c, bmc150_magn_i2c_id);
static const struct of_device_id bmc150_magn_of_match[] = {
{ .compatible = "bosch,bmc150_magn" },
{ .compatible = "bosch,bmc156_magn" },
{ .compatible = "bosch,bmm150_magn" },
{ }
};
MODULE_DEVICE_TABLE(of, bmc150_magn_of_match);
static struct i2c_driver bmc150_magn_driver = {
.driver = {
.name = "bmc150_magn_i2c",
.of_match_table = bmc150_magn_of_match,
.acpi_match_table = ACPI_PTR(bmc150_magn_acpi_match),
.pm = &bmc150_magn_pm_ops,
},

View file

@ -441,9 +441,16 @@ static const struct i2c_device_id mag3110_id[] = {
};
MODULE_DEVICE_TABLE(i2c, mag3110_id);
static const struct of_device_id mag3110_of_match[] = {
{ .compatible = "fsl,mag3110" },
{ }
};
MODULE_DEVICE_TABLE(of, mag3110_of_match);
static struct i2c_driver mag3110_driver = {
.driver = {
.name = "mag3110",
.of_match_table = mag3110_of_match,
.pm = MAG3110_PM_OPS,
},
.probe = mag3110_probe,

View file

@ -325,6 +325,7 @@ static int lmp91000_probe(struct i2c_client *client,
indio_dev->channels = lmp91000_channels;
indio_dev->num_channels = ARRAY_SIZE(lmp91000_channels);
indio_dev->name = LMP91000_DRV_NAME;
indio_dev->dev.parent = &client->dev;
indio_dev->modes = INDIO_DIRECT_MODE;
i2c_set_clientdata(client, indio_dev);

View file

@ -175,11 +175,12 @@ static u32 bmp280_compensate_humidity(struct bmp280_data *data,
}
H6 = sign_extend32(tmp, 7);
var = ((s32)data->t_fine) - 76800;
var = ((((adc_humidity << 14) - (H4 << 20) - (H5 * var)) + 16384) >> 15)
* (((((((var * H6) >> 10) * (((var * H3) >> 11) + 32768)) >> 10)
+ 2097152) * H2 + 8192) >> 14);
var -= ((((var >> 15) * (var >> 15)) >> 7) * H1) >> 4;
var = ((s32)data->t_fine) - (s32)76800;
var = ((((adc_humidity << 14) - (H4 << 20) - (H5 * var))
+ (s32)16384) >> 15) * (((((((var * H6) >> 10)
* (((var * (s32)H3) >> 11) + (s32)32768)) >> 10)
+ (s32)2097152) * H2 + 8192) >> 14);
var -= ((((var >> 15) * (var >> 15)) >> 7) * (s32)H1) >> 4;
return var >> 12;
};

View file

@ -297,9 +297,16 @@ static const struct i2c_device_id hp03_id[] = {
};
MODULE_DEVICE_TABLE(i2c, hp03_id);
static const struct of_device_id hp03_of_match[] = {
{ .compatible = "hoperf,hp03" },
{ },
};
MODULE_DEVICE_TABLE(of, hp03_of_match);
static struct i2c_driver hp03_driver = {
.driver = {
.name = "hp03",
.of_match_table = hp03_of_match,
},
.probe = hp03_probe,
.remove = hp03_remove,

View file

@ -321,9 +321,16 @@ static const struct i2c_device_id mpl3115_id[] = {
};
MODULE_DEVICE_TABLE(i2c, mpl3115_id);
static const struct of_device_id mpl3115_of_match[] = {
{ .compatible = "fsl,mpl3115" },
{ }
};
MODULE_DEVICE_TABLE(of, mpl3115_of_match);
static struct i2c_driver mpl3115_driver = {
.driver = {
.name = "mpl3115",
.of_match_table = mpl3115_of_match,
.pm = MPL3115_PM_OPS,
},
.probe = mpl3115_probe,

View file

@ -751,7 +751,7 @@ static void zpa2326_suspend(struct iio_dev *indio_dev)
*/
static irqreturn_t zpa2326_handle_irq(int irq, void *data)
{
struct iio_dev *indio_dev = (struct iio_dev *)data;
struct iio_dev *indio_dev = data;
if (iio_buffer_enabled(indio_dev)) {
/* Timestamping needed for buffered sampling only. */
@ -790,7 +790,7 @@ static irqreturn_t zpa2326_handle_irq(int irq, void *data)
*/
static irqreturn_t zpa2326_handle_threaded_irq(int irq, void *data)
{
struct iio_dev *indio_dev = (struct iio_dev *)data;
struct iio_dev *indio_dev = data;
struct zpa2326_private *priv = iio_priv(indio_dev);
unsigned int val;
bool cont;

View file

@ -32,6 +32,17 @@ config LIDAR_LITE_V2
To compile this driver as a module, choose M here: the
module will be called pulsedlight-lite-v2
config SRF04
tristate "Devantech SRF04 ultrasonic ranger sensor"
depends on GPIOLIB
help
Say Y here to build a driver for Devantech SRF04 ultrasonic
ranger sensor. This driver can be used to measure the distance
of objects. It is using two GPIOs.
To compile this driver as a module, choose M here: the
module will be called srf04.
config SX9500
tristate "SX9500 Semtech proximity sensor"
select IIO_BUFFER

View file

@ -5,5 +5,6 @@
# When adding new entries keep the list in alphabetical order
obj-$(CONFIG_AS3935) += as3935.o
obj-$(CONFIG_LIDAR_LITE_V2) += pulsedlight-lidar-lite-v2.o
obj-$(CONFIG_SRF04) += srf04.o
obj-$(CONFIG_SRF08) += srf08.o
obj-$(CONFIG_SX9500) += sx9500.o

View file

@ -50,7 +50,6 @@
#define AS3935_TUNE_CAP 0x08
#define AS3935_CALIBRATE 0x3D
#define AS3935_WRITE_DATA BIT(15)
#define AS3935_READ_DATA BIT(14)
#define AS3935_ADDRESS(x) ((x) << 8)
@ -105,7 +104,7 @@ static int as3935_write(struct as3935_state *st,
{
u8 *buf = st->buf;
buf[0] = (AS3935_WRITE_DATA | AS3935_ADDRESS(reg)) >> 8;
buf[0] = AS3935_ADDRESS(reg) >> 8;
buf[1] = val;
return spi_write(st->spi, buf, 2);
@ -155,7 +154,7 @@ static struct attribute *as3935_attributes[] = {
NULL,
};
static struct attribute_group as3935_attribute_group = {
static const struct attribute_group as3935_attribute_group = {
.attrs = as3935_attributes,
};

View file

@ -278,6 +278,7 @@ static int lidar_probe(struct i2c_client *client,
indio_dev->name = LIDAR_DRV_NAME;
indio_dev->channels = lidar_channels;
indio_dev->num_channels = ARRAY_SIZE(lidar_channels);
indio_dev->dev.parent = &client->dev;
indio_dev->modes = INDIO_DIRECT_MODE;
i2c_set_clientdata(client, indio_dev);

View file

@ -0,0 +1,304 @@
/*
* SRF04: ultrasonic sensor for distance measuring by using GPIOs
*
* Copyright (c) 2017 Andreas Klinger <ak@it-klinger.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* For details about the device see:
* http://www.robot-electronics.co.uk/htm/srf04tech.htm
*
* the measurement cycle as timing diagram looks like:
*
* +---+
* GPIO | |
* trig: --+ +------------------------------------------------------
* ^ ^
* |<->|
* udelay(10)
*
* ultra +-+ +-+ +-+
* sonic | | | | | |
* burst: ---------+ +-+ +-+ +-----------------------------------------
* .
* ultra . +-+ +-+ +-+
* sonic . | | | | | |
* echo: ----------------------------------+ +-+ +-+ +----------------
* . .
* +------------------------+
* GPIO | |
* echo: -------------------+ +---------------
* ^ ^
* interrupt interrupt
* (ts_rising) (ts_falling)
* |<---------------------->|
* pulse time measured
* --> one round trip of ultra sonic waves
*/
#include <linux/err.h>
#include <linux/gpio/consumer.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
struct srf04_data {
struct device *dev;
struct gpio_desc *gpiod_trig;
struct gpio_desc *gpiod_echo;
struct mutex lock;
int irqnr;
ktime_t ts_rising;
ktime_t ts_falling;
struct completion rising;
struct completion falling;
};
static irqreturn_t srf04_handle_irq(int irq, void *dev_id)
{
struct iio_dev *indio_dev = dev_id;
struct srf04_data *data = iio_priv(indio_dev);
ktime_t now = ktime_get();
if (gpiod_get_value(data->gpiod_echo)) {
data->ts_rising = now;
complete(&data->rising);
} else {
data->ts_falling = now;
complete(&data->falling);
}
return IRQ_HANDLED;
}
static int srf04_read(struct srf04_data *data)
{
int ret;
ktime_t ktime_dt;
u64 dt_ns;
u32 time_ns, distance_mm;
/*
* just one read-echo-cycle can take place at a time
* ==> lock against concurrent reading calls
*/
mutex_lock(&data->lock);
reinit_completion(&data->rising);
reinit_completion(&data->falling);
gpiod_set_value(data->gpiod_trig, 1);
udelay(10);
gpiod_set_value(data->gpiod_trig, 0);
/* it cannot take more than 20 ms */
ret = wait_for_completion_killable_timeout(&data->rising, HZ/50);
if (ret < 0) {
mutex_unlock(&data->lock);
return ret;
} else if (ret == 0) {
mutex_unlock(&data->lock);
return -ETIMEDOUT;
}
ret = wait_for_completion_killable_timeout(&data->falling, HZ/50);
if (ret < 0) {
mutex_unlock(&data->lock);
return ret;
} else if (ret == 0) {
mutex_unlock(&data->lock);
return -ETIMEDOUT;
}
ktime_dt = ktime_sub(data->ts_falling, data->ts_rising);
mutex_unlock(&data->lock);
dt_ns = ktime_to_ns(ktime_dt);
/*
* measuring more than 3 meters is beyond the capabilities of
* the sensor
* ==> filter out invalid results for not measuring echos of
* another us sensor
*
* formula:
* distance 3 m
* time = ---------- = --------- = 9404389 ns
* speed 319 m/s
*
* using a minimum speed at -20 °C of 319 m/s
*/
if (dt_ns > 9404389)
return -EIO;
time_ns = dt_ns;
/*
* the speed as function of the temperature is approximately:
*
* speed = 331,5 + 0,6 * Temp
* with Temp in °C
* and speed in m/s
*
* use 343 m/s as ultrasonic speed at 20 °C here in absence of the
* temperature
*
* therefore:
* time 343
* distance = ------ * -----
* 10^6 2
* with time in ns
* and distance in mm (one way)
*
* because we limit to 3 meters the multiplication with 343 just
* fits into 32 bit
*/
distance_mm = time_ns * 343 / 2000000;
return distance_mm;
}
static int srf04_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *channel, int *val,
int *val2, long info)
{
struct srf04_data *data = iio_priv(indio_dev);
int ret;
if (channel->type != IIO_DISTANCE)
return -EINVAL;
switch (info) {
case IIO_CHAN_INFO_RAW:
ret = srf04_read(data);
if (ret < 0)
return ret;
*val = ret;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
/*
* theoretical maximum resolution is 3 mm
* 1 LSB is 1 mm
*/
*val = 0;
*val2 = 1000;
return IIO_VAL_INT_PLUS_MICRO;
default:
return -EINVAL;
}
}
static const struct iio_info srf04_iio_info = {
.driver_module = THIS_MODULE,
.read_raw = srf04_read_raw,
};
static const struct iio_chan_spec srf04_chan_spec[] = {
{
.type = IIO_DISTANCE,
.info_mask_separate =
BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE),
},
};
static int srf04_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct srf04_data *data;
struct iio_dev *indio_dev;
int ret;
indio_dev = devm_iio_device_alloc(dev, sizeof(struct srf04_data));
if (!indio_dev) {
dev_err(dev, "failed to allocate IIO device\n");
return -ENOMEM;
}
data = iio_priv(indio_dev);
data->dev = dev;
mutex_init(&data->lock);
init_completion(&data->rising);
init_completion(&data->falling);
data->gpiod_trig = devm_gpiod_get(dev, "trig", GPIOD_OUT_LOW);
if (IS_ERR(data->gpiod_trig)) {
dev_err(dev, "failed to get trig-gpios: err=%ld\n",
PTR_ERR(data->gpiod_trig));
return PTR_ERR(data->gpiod_trig);
}
data->gpiod_echo = devm_gpiod_get(dev, "echo", GPIOD_IN);
if (IS_ERR(data->gpiod_echo)) {
dev_err(dev, "failed to get echo-gpios: err=%ld\n",
PTR_ERR(data->gpiod_echo));
return PTR_ERR(data->gpiod_echo);
}
if (gpiod_cansleep(data->gpiod_echo)) {
dev_err(data->dev, "cansleep-GPIOs not supported\n");
return -ENODEV;
}
data->irqnr = gpiod_to_irq(data->gpiod_echo);
if (data->irqnr < 0) {
dev_err(data->dev, "gpiod_to_irq: %d\n", data->irqnr);
return data->irqnr;
}
ret = devm_request_irq(dev, data->irqnr, srf04_handle_irq,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
pdev->name, indio_dev);
if (ret < 0) {
dev_err(data->dev, "request_irq: %d\n", ret);
return ret;
}
platform_set_drvdata(pdev, indio_dev);
indio_dev->name = "srf04";
indio_dev->dev.parent = &pdev->dev;
indio_dev->info = &srf04_iio_info;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = srf04_chan_spec;
indio_dev->num_channels = ARRAY_SIZE(srf04_chan_spec);
return devm_iio_device_register(dev, indio_dev);
}
static const struct of_device_id of_srf04_match[] = {
{ .compatible = "devantech,srf04", },
{},
};
MODULE_DEVICE_TABLE(of, of_srf04_match);
static struct platform_driver srf04_driver = {
.probe = srf04_probe,
.driver = {
.name = "srf04-gpio",
.of_match_table = of_srf04_match,
},
};
module_platform_driver(srf04_driver);
MODULE_AUTHOR("Andreas Klinger <[email protected]>");
MODULE_DESCRIPTION("SRF04 ultrasonic sensor for distance measuring using GPIOs");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:srf04");

View file

@ -19,6 +19,20 @@ config MAXIM_THERMOCOUPLE
This driver can also be built as a module. If so, the module will
be called maxim_thermocouple.
config HID_SENSOR_TEMP
tristate "HID Environmental temperature sensor"
depends on HID_SENSOR_HUB
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
select HID_SENSOR_IIO_COMMON
select HID_SENSOR_IIO_TRIGGER
help
Say yes here to build support for the HID SENSOR
temperature driver
To compile this driver as a module, choose M here: the module
will be called hid-sensor-temperature.
config MLX90614
tristate "MLX90614 contact-less infrared sensor"
depends on I2C

Some files were not shown because too many files have changed in this diff Show more