Commit c0f460ff authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman

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

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

Jonathan writes:

Third set of IIO new device support, cleanups and features for the 4.19 cycle.

This is a somewhat sneaky late pull request given Linus has announced
a likely 1 week delay.  If it is too late I'll resend after the merge
window.  I merged in the fixes branch as those are primarily around
things queued for the merge window.

Particulary good to see the output of our Himanshu Jha, a GSOC student
who developed the bme680 driver.

New device support:
* bme680 gas sensor (with temperature, humidity and pressure)
  - new driver to support this device
* vcn4000
  - support vcnl4200 (lots of rework to allow this)
  - ids added for VCNL4010 and VCNL4020 sensors

New features:
* ad9523
  - support the various external signal options via gpios.

Cleanups and fixes
* ad_sigma_delta
  - unsigned long for a timeout.
* ad9523
  - fix a wrong return value that was indicating successful write failed
    and might lead to an infinite loop.
* si1133
  - fix an impossible test.
  - fix an uninitialsed variable by reading from the device in all paths.
* xilinx xadc
  - check for return values in clk related functions
  - limit the pcap clock frequency to supported ranges.
  - stash the irq to avoid calling platform_get_irq from the remove path.
  - ensure the irq is actually requested before we enable the hardware to
    output it.
parents 799d8a8e 496fb59e
......@@ -209,6 +209,7 @@ static int ad_sd_calibrate(struct ad_sigma_delta *sigma_delta,
unsigned int mode, unsigned int channel)
{
int ret;
unsigned long timeout;
ret = ad_sigma_delta_set_channel(sigma_delta, channel);
if (ret)
......@@ -224,8 +225,8 @@ static int ad_sd_calibrate(struct ad_sigma_delta *sigma_delta,
sigma_delta->irq_dis = false;
enable_irq(sigma_delta->spi->irq);
ret = wait_for_completion_timeout(&sigma_delta->completion, 2*HZ);
if (ret == 0) {
timeout = wait_for_completion_timeout(&sigma_delta->completion, 2 * HZ);
if (timeout == 0) {
sigma_delta->irq_dis = true;
disable_irq_nosync(sigma_delta->spi->irq);
ret = -EIO;
......
......@@ -322,6 +322,7 @@ static irqreturn_t xadc_zynq_interrupt_handler(int irq, void *devid)
#define XADC_ZYNQ_TCK_RATE_MAX 50000000
#define XADC_ZYNQ_IGAP_DEFAULT 20
#define XADC_ZYNQ_PCAP_RATE_MAX 200000000
static int xadc_zynq_setup(struct platform_device *pdev,
struct iio_dev *indio_dev, int irq)
......@@ -332,6 +333,7 @@ static int xadc_zynq_setup(struct platform_device *pdev,
unsigned int div;
unsigned int igap;
unsigned int tck_rate;
int ret;
/* TODO: Figure out how to make igap and tck_rate configurable */
igap = XADC_ZYNQ_IGAP_DEFAULT;
......@@ -340,6 +342,15 @@ static int xadc_zynq_setup(struct platform_device *pdev,
xadc->zynq_intmask = ~0;
pcap_rate = clk_get_rate(xadc->clk);
if (!pcap_rate)
return -EINVAL;
if (pcap_rate > XADC_ZYNQ_PCAP_RATE_MAX) {
ret = clk_set_rate(xadc->clk,
(unsigned long)XADC_ZYNQ_PCAP_RATE_MAX);
if (ret)
return ret;
}
if (tck_rate > pcap_rate / 2) {
div = 2;
......@@ -366,6 +377,12 @@ static int xadc_zynq_setup(struct platform_device *pdev,
XADC_ZYNQ_CFG_REDGE | XADC_ZYNQ_CFG_WEDGE |
tck_div | XADC_ZYNQ_CFG_IGAP(igap));
if (pcap_rate > XADC_ZYNQ_PCAP_RATE_MAX) {
ret = clk_set_rate(xadc->clk, pcap_rate);
if (ret)
return ret;
}
return 0;
}
......@@ -887,6 +904,9 @@ static int xadc_write_raw(struct iio_dev *indio_dev,
unsigned long clk_rate = xadc_get_dclk_rate(xadc);
unsigned int div;
if (!clk_rate)
return -EINVAL;
if (info != IIO_CHAN_INFO_SAMP_FREQ)
return -EINVAL;
......@@ -1155,6 +1175,7 @@ static int xadc_probe(struct platform_device *pdev)
xadc = iio_priv(indio_dev);
xadc->ops = id->data;
xadc->irq = irq;
init_completion(&xadc->completion);
mutex_init(&xadc->mutex);
spin_lock_init(&xadc->lock);
......@@ -1205,14 +1226,14 @@ static int xadc_probe(struct platform_device *pdev)
if (ret)
goto err_free_samplerate_trigger;
ret = xadc->ops->setup(pdev, indio_dev, irq);
ret = request_irq(xadc->irq, xadc->ops->interrupt_handler, 0,
dev_name(&pdev->dev), indio_dev);
if (ret)
goto err_clk_disable_unprepare;
ret = request_irq(irq, xadc->ops->interrupt_handler, 0,
dev_name(&pdev->dev), indio_dev);
ret = xadc->ops->setup(pdev, indio_dev, xadc->irq);
if (ret)
goto err_clk_disable_unprepare;
goto err_free_irq;
for (i = 0; i < 16; i++)
xadc_read_adc_reg(xadc, XADC_REG_THRESHOLD(i),
......@@ -1237,8 +1258,10 @@ static int xadc_probe(struct platform_device *pdev)
goto err_free_irq;
/* Disable all alarms */
xadc_update_adc_reg(xadc, XADC_REG_CONF1, XADC_CONF1_ALARM_MASK,
XADC_CONF1_ALARM_MASK);
ret = xadc_update_adc_reg(xadc, XADC_REG_CONF1, XADC_CONF1_ALARM_MASK,
XADC_CONF1_ALARM_MASK);
if (ret)
goto err_free_irq;
/* Set thresholds to min/max */
for (i = 0; i < 16; i++) {
......@@ -1266,7 +1289,7 @@ static int xadc_probe(struct platform_device *pdev)
return 0;
err_free_irq:
free_irq(irq, indio_dev);
free_irq(xadc->irq, indio_dev);
err_clk_disable_unprepare:
clk_disable_unprepare(xadc->clk);
err_free_samplerate_trigger:
......@@ -1288,7 +1311,6 @@ static int xadc_remove(struct platform_device *pdev)
{
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
struct xadc *xadc = iio_priv(indio_dev);
int irq = platform_get_irq(pdev, 0);
iio_device_unregister(indio_dev);
if (xadc->ops->flags & XADC_FLAGS_BUFFERED) {
......@@ -1296,7 +1318,7 @@ static int xadc_remove(struct platform_device *pdev)
iio_trigger_free(xadc->convst_trigger);
iio_triggered_buffer_cleanup(indio_dev);
}
free_irq(irq, indio_dev);
free_irq(xadc->irq, indio_dev);
clk_disable_unprepare(xadc->clk);
cancel_delayed_work(&xadc->zynq_unmask_work);
kfree(xadc->data);
......
......@@ -68,6 +68,7 @@ struct xadc {
spinlock_t lock;
struct completion completion;
int irq;
};
struct xadc_ops {
......
......@@ -21,6 +21,29 @@ config ATLAS_PH_SENSOR
To compile this driver as module, choose M here: the
module will be called atlas-ph-sensor.
config BME680
tristate "Bosch Sensortec BME680 sensor driver"
depends on (I2C || SPI)
select REGMAP
select BME680_I2C if I2C
select BME680_SPI if SPI
help
Say yes here to build support for Bosch Sensortec BME680 sensor with
temperature, pressure, humidity and gas sensing capability.
This driver can also be built as a module. If so, the module for I2C
would be called bme680_i2c and bme680_spi for SPI support.
config BME680_I2C
tristate
depends on I2C && BME680
select REGMAP_I2C
config BME680_SPI
tristate
depends on SPI && BME680
select REGMAP_SPI
config CCS811
tristate "AMS CCS811 VOC sensor"
depends on I2C
......
......@@ -4,6 +4,9 @@
# When adding new entries keep the list in alphabetical order
obj-$(CONFIG_ATLAS_PH_SENSOR) += atlas-ph-sensor.o
obj-$(CONFIG_BME680) += bme680_core.o
obj-$(CONFIG_BME680_I2C) += bme680_i2c.o
obj-$(CONFIG_BME680_SPI) += bme680_spi.o
obj-$(CONFIG_CCS811) += ccs811.o
obj-$(CONFIG_IAQCORE) += ams-iaq-core.o
obj-$(CONFIG_VZ89X) += vz89x.o
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef BME680_H_
#define BME680_H_
#define BME680_REG_CHIP_I2C_ID 0xD0
#define BME680_REG_CHIP_SPI_ID 0x50
#define BME680_CHIP_ID_VAL 0x61
#define BME680_REG_SOFT_RESET_I2C 0xE0
#define BME680_REG_SOFT_RESET_SPI 0x60
#define BME680_CMD_SOFTRESET 0xB6
#define BME680_REG_STATUS 0x73
#define BME680_SPI_MEM_PAGE_BIT BIT(4)
#define BME680_SPI_MEM_PAGE_1_VAL 1
#define BME680_REG_TEMP_MSB 0x22
#define BME680_REG_PRESS_MSB 0x1F
#define BM6880_REG_HUMIDITY_MSB 0x25
#define BME680_REG_GAS_MSB 0x2A
#define BME680_REG_GAS_R_LSB 0x2B
#define BME680_GAS_STAB_BIT BIT(4)
#define BME680_REG_CTRL_HUMIDITY 0x72
#define BME680_OSRS_HUMIDITY_MASK GENMASK(2, 0)
#define BME680_REG_CTRL_MEAS 0x74
#define BME680_OSRS_TEMP_MASK GENMASK(7, 5)
#define BME680_OSRS_PRESS_MASK GENMASK(4, 2)
#define BME680_MODE_MASK GENMASK(1, 0)
#define BME680_MODE_FORCED 1
#define BME680_MODE_SLEEP 0
#define BME680_REG_CONFIG 0x75
#define BME680_FILTER_MASK GENMASK(4, 2)
#define BME680_FILTER_COEFF_VAL BIT(1)
/* TEMP/PRESS/HUMID reading skipped */
#define BME680_MEAS_SKIPPED 0x8000
#define BME680_MAX_OVERFLOW_VAL 0x40000000
#define BME680_HUM_REG_SHIFT_VAL 4
#define BME680_BIT_H1_DATA_MSK 0x0F
#define BME680_REG_RES_HEAT_RANGE 0x02
#define BME680_RHRANGE_MSK 0x30
#define BME680_REG_RES_HEAT_VAL 0x00
#define BME680_REG_RANGE_SW_ERR 0x04
#define BME680_RSERROR_MSK 0xF0
#define BME680_REG_RES_HEAT_0 0x5A
#define BME680_REG_GAS_WAIT_0 0x64
#define BME680_GAS_RANGE_MASK 0x0F
#define BME680_ADC_GAS_RES_SHIFT 6
#define BME680_AMB_TEMP 25
#define BME680_REG_CTRL_GAS_1 0x71
#define BME680_RUN_GAS_MASK BIT(4)
#define BME680_NB_CONV_MASK GENMASK(3, 0)
#define BME680_RUN_GAS_EN_BIT BIT(4)
#define BME680_NB_CONV_0_VAL 0
#define BME680_REG_MEAS_STAT_0 0x1D
#define BME680_GAS_MEAS_BIT BIT(6)
/* Calibration Parameters */
#define BME680_T2_LSB_REG 0x8A
#define BME680_T3_REG 0x8C
#define BME680_P1_LSB_REG 0x8E
#define BME680_P2_LSB_REG 0x90
#define BME680_P3_REG 0x92
#define BME680_P4_LSB_REG 0x94
#define BME680_P5_LSB_REG 0x96
#define BME680_P7_REG 0x98
#define BME680_P6_REG 0x99
#define BME680_P8_LSB_REG 0x9C
#define BME680_P9_LSB_REG 0x9E
#define BME680_P10_REG 0xA0
#define BME680_H2_LSB_REG 0xE2
#define BME680_H2_MSB_REG 0xE1
#define BME680_H1_MSB_REG 0xE3
#define BME680_H1_LSB_REG 0xE2
#define BME680_H3_REG 0xE4
#define BME680_H4_REG 0xE5
#define BME680_H5_REG 0xE6
#define BME680_H6_REG 0xE7
#define BME680_H7_REG 0xE8
#define BME680_T1_LSB_REG 0xE9
#define BME680_GH2_LSB_REG 0xEB
#define BME680_GH1_REG 0xED
#define BME680_GH3_REG 0xEE
extern const struct regmap_config bme680_regmap_config;
int bme680_core_probe(struct device *dev, struct regmap *regmap,
const char *name);
#endif /* BME680_H_ */
This diff is collapsed.
// SPDX-License-Identifier: GPL-2.0
/*
* BME680 - I2C Driver
*
* Copyright (C) 2018 Himanshu Jha <himanshujha199640@gmail.com>
*
* 7-Bit I2C slave address is:
* - 0x76 if SDO is pulled to GND
* - 0x77 if SDO is pulled to VDDIO
*
* Note: SDO pin cannot be left floating otherwise I2C address
* will be undefined.
*/
#include <linux/acpi.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include "bme680.h"
static int bme680_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct regmap *regmap;
const char *name = NULL;
unsigned int val;
int ret;
regmap = devm_regmap_init_i2c(client, &bme680_regmap_config);
if (IS_ERR(regmap)) {
dev_err(&client->dev, "Failed to register i2c regmap %d\n",
(int)PTR_ERR(regmap));
return PTR_ERR(regmap);
}
ret = regmap_write(regmap, BME680_REG_SOFT_RESET_I2C,
BME680_CMD_SOFTRESET);
if (ret < 0) {
dev_err(&client->dev, "Failed to reset chip\n");
return ret;
}
ret = regmap_read(regmap, BME680_REG_CHIP_I2C_ID, &val);
if (ret < 0) {
dev_err(&client->dev, "Error reading I2C chip ID\n");
return ret;
}
if (val != BME680_CHIP_ID_VAL) {
dev_err(&client->dev, "Wrong chip ID, got %x expected %x\n",
val, BME680_CHIP_ID_VAL);
return -ENODEV;
}
if (id)
name = id->name;
return bme680_core_probe(&client->dev, regmap, name);
}
static const struct i2c_device_id bme680_i2c_id[] = {
{"bme680", 0},
{},
};
MODULE_DEVICE_TABLE(i2c, bme680_i2c_id);
static const struct acpi_device_id bme680_acpi_match[] = {
{"BME0680", 0},
{},
};
MODULE_DEVICE_TABLE(acpi, bme680_acpi_match);
static struct i2c_driver bme680_i2c_driver = {
.driver = {
.name = "bme680_i2c",
.acpi_match_table = ACPI_PTR(bme680_acpi_match),
},
.probe = bme680_i2c_probe,
.id_table = bme680_i2c_id,
};
module_i2c_driver(bme680_i2c_driver);
MODULE_AUTHOR("Himanshu Jha <himanshujha199640@gmail.com>");
MODULE_DESCRIPTION("BME680 I2C driver");
MODULE_LICENSE("GPL v2");
// SPDX-License-Identifier: GPL-2.0
/*
* BME680 - SPI Driver
*
* Copyright (C) 2018 Himanshu Jha <himanshujha199640@gmail.com>
*/
#include <linux/acpi.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/spi/spi.h>
#include "bme680.h"
static int bme680_regmap_spi_write(void *context, const void *data,
size_t count)
{
struct spi_device *spi = context;
u8 buf[2];
memcpy(buf, data, 2);
/*
* The SPI register address (= full register address without bit 7)
* and the write command (bit7 = RW = '0')
*/
buf[0] &= ~0x80;
return spi_write_then_read(spi, buf, 2, NULL, 0);
}
static int bme680_regmap_spi_read(void *context, const void *reg,
size_t reg_size, void *val, size_t val_size)
{
struct spi_device *spi = context;
return spi_write_then_read(spi, reg, reg_size, val, val_size);
}
static struct regmap_bus bme680_regmap_bus = {
.write = bme680_regmap_spi_write,
.read = bme680_regmap_spi_read,
.reg_format_endian_default = REGMAP_ENDIAN_BIG,
.val_format_endian_default = REGMAP_ENDIAN_BIG,
};
static int bme680_spi_probe(struct spi_device *spi)
{
const struct spi_device_id *id = spi_get_device_id(spi);
struct regmap *regmap;
unsigned int val;
int ret;
spi->bits_per_word = 8;
ret = spi_setup(spi);
if (ret < 0) {
dev_err(&spi->dev, "spi_setup failed!\n");
return ret;
}
regmap = devm_regmap_init(&spi->dev, &bme680_regmap_bus,
&spi->dev, &bme680_regmap_config);
if (IS_ERR(regmap)) {
dev_err(&spi->dev, "Failed to register spi regmap %d\n",
(int)PTR_ERR(regmap));
return PTR_ERR(regmap);
}
ret = regmap_write(regmap, BME680_REG_SOFT_RESET_SPI,
BME680_CMD_SOFTRESET);
if (ret < 0) {
dev_err(&spi->dev, "Failed to reset chip\n");
return ret;
}
/* after power-on reset, Page 0(0x80-0xFF) of spi_mem_page is active */
ret = regmap_read(regmap, BME680_REG_CHIP_SPI_ID, &val);
if (ret < 0) {
dev_err(&spi->dev, "Error reading SPI chip ID\n");
return ret;
}
if (val != BME680_CHIP_ID_VAL) {
dev_err(&spi->dev, "Wrong chip ID, got %x expected %x\n",
val, BME680_CHIP_ID_VAL);
return -ENODEV;
}
/*
* select Page 1 of spi_mem_page to enable access to
* to registers from address 0x00 to 0x7F.
*/
ret = regmap_write_bits(regmap, BME680_REG_STATUS,
BME680_SPI_MEM_PAGE_BIT,
BME680_SPI_MEM_PAGE_1_VAL);
if (ret < 0) {
dev_err(&spi->dev, "failed to set page 1 of spi_mem_page\n");
return ret;
}
return bme680_core_probe(&spi->dev, regmap, id->name);
}
static const struct spi_device_id bme680_spi_id[] = {
{"bme680", 0},
{},
};
MODULE_DEVICE_TABLE(spi, bme680_spi_id);
static const struct acpi_device_id bme680_acpi_match[] = {
{"BME0680", 0},
{},
};
MODULE_DEVICE_TABLE(acpi, bme680_acpi_match);
static struct spi_driver bme680_spi_driver = {
.driver = {
.name = "bme680_spi",
.acpi_match_table = ACPI_PTR(bme680_acpi_match),
},
.probe = bme680_spi_probe,
.id_table = bme680_spi_id,
};
module_spi_driver(bme680_spi_driver);
MODULE_AUTHOR("Himanshu Jha <himanshujha199640@gmail.com>");
MODULE_DESCRIPTION("Bosch BME680 SPI driver");
MODULE_LICENSE("GPL v2");
......@@ -12,6 +12,7 @@
#include <linux/sysfs.h>
#include <linux/spi/spi.h>
#include <linux/regulator/consumer.h>
#include <linux/gpio/consumer.h>
#include <linux/err.h>
#include <linux/module.h>
#include <linux/delay.h>
......@@ -268,6 +269,9 @@ struct ad9523_state {
struct regulator *reg;
struct ad9523_platform_data *pdata;
struct iio_chan_spec ad9523_channels[AD9523_NUM_CHAN];
struct gpio_desc *pwrdown_gpio;
struct gpio_desc *reset_gpio;
struct gpio_desc *sync_gpio;
unsigned long vcxo_freq;
unsigned long vco_freq;
......@@ -518,7 +522,7 @@ static ssize_t ad9523_store(struct device *dev,
return ret;
if (!state)
return 0;
return len;
mutex_lock(&st->lock);
switch ((u32)this_attr->address) {
......@@ -988,6 +992,32 @@ static int ad9523_probe(struct spi_device *spi)
return ret;
}
st->pwrdown_gpio = devm_gpiod_get_optional(&spi->dev, "powerdown",
GPIOD_OUT_HIGH);
if (IS_ERR(st->pwrdown_gpio)) {
ret = PTR_ERR(st->pwrdown_gpio);
goto error_disable_reg;
}
st->reset_gpio = devm_gpiod_get_optional(&spi->dev, "reset",
GPIOD_OUT_LOW);
if (IS_ERR(st->reset_gpio)) {
ret = PTR_ERR(st->reset_gpio);
goto error_disable_reg;
}
if (st->reset_gpio) {
udelay(1);
gpiod_direction_output(st->reset_gpio, 1);
}
st->sync_gpio = devm_gpiod_get_optional(&spi->dev, "sync",
GPIOD_OUT_HIGH);
if (IS_ERR(st->sync_gpio)) {
ret = PTR_ERR(st->sync_gpio);
goto error_disable_reg;
}
spi_set_drvdata(spi, indio_dev);
st->spi = spi;
st->pdata = pdata;
......
......@@ -450,11 +450,12 @@ config US5182D
will be called us5182d.
config VCNL4000
tristate "VCNL4000/4010/4020 combined ALS and proximity sensor"
tristate "VCNL4000/4010/4020/4200 combined ALS and proximity sensor"
depends on I2C
help
Say Y here if you want to build a driver for the Vishay VCNL4000,
VCNL4010, VCNL4020 combined ambient light and proximity sensor.
VCNL4010, VCNL4020, VCNL4200 combined ambient light and proximity
sensor.
To compile this driver as a module, choose M here: the
module will be called vcnl4000.
......
......@@ -409,6 +409,9 @@ static int si1133_command(struct si1133_data *data, u8 cmd)
err = -ETIMEDOUT;
goto out;
}
err = regmap_read(data->regmap, SI1133_REG_RESPONSE0, &resp);
if (err)
goto out;
} else {
err = regmap_read_poll_timeout(data->regmap,
SI1133_REG_RESPONSE0, resp,
......@@ -838,7 +841,7 @@ static int si1133_write_raw(struct iio_dev *iio_dev,
switch (chan->type) {
case IIO_INTENSITY:
case IIO_UVINDEX:
if (val != 0 || val != 1)
if (val != 0 && val != 1)
return -EINVAL;
return si1133_update_adcsens(data,
......
/*
* vcnl4000.c - Support for Vishay VCNL4000/4010/4020 combined ambient
* vcnl4000.c - Support for Vishay VCNL4000/4010/4020/4200 combined ambient
* light and proximity sensor
*
* Copyright 2012 Peter Meerwald <pmeerw@pmeerw.net>
......@@ -8,13 +8,15 @@
* the GNU General Public License. See the file COPYING in the main
* directory of this archive for more details.
*
* IIO driver for VCNL4000 (7-bit I2C slave address 0x13)
* IIO driver for:
* VCNL4000/10/20 (7-bit I2C slave address 0x13)
* VCNL4200 (7-bit I2C slave address 0x51)
*
* TODO:
* allow to adjust IR current
* proximity threshold and event handling
* periodic ALS/proximity measurement (VCNL4010/20)
* interrupts (VCNL4010/20)
* interrupts (VCNL4010/20, VCNL4200)
*/
#include <linux/module.h>
......@@ -26,8 +28,9 @@
#include <linux/iio/sysfs.h>
#define VCNL4000_DRV_NAME "vcnl4000"
#define VCNL4000_ID 0x01
#define VCNL4010_ID 0x02 /* for VCNL4020, VCNL4010 */
#define VCNL4000_PROD_ID 0x01
#define VCNL4010_PROD_ID 0x02 /* for VCNL4020, VCNL4010 */
#define VCNL4200_PROD_ID 0x58
#define VCNL4000_COMMAND 0x80 /* Command register */
#define VCNL4000_PROD_REV 0x81 /* Product ID and Revision ID */
......@@ -40,23 +43,124 @@
#define VCNL4000_PS_MEAS_FREQ 0x89 /* Proximity test signal frequency */
#define VCNL4000_PS_MOD_ADJ 0x8a /* Proximity modulator timing adjustment */
#define VCNL4200_AL_CONF 0x00 /* Ambient light configuration */
#define VCNL4200_PS_CONF1 0x03 /* Proximity configuration */
#define VCNL4200_PS_DATA 0x08 /* Proximity data */
#define VCNL4200_AL_DATA 0x09 /* Ambient light data */
#define VCNL4200_DEV_ID 0x0e /* Device ID, slave address and version */
/* Bit masks for COMMAND register */
#define VCNL4000_AL_RDY BIT(6) /* ALS data ready? */
#define VCNL4000_PS_RDY BIT(5) /* proximity data ready? */
#define VCNL4000_AL_OD BIT(4) /* start on-demand ALS measurement */
#define VCNL4000_PS_OD BIT(3) /* start on-demand proximity measurement */
enum vcnl4000_device_ids {
VCNL4000,
VCNL4010,
VCNL4200,
};
struct vcnl4200_channel {
u8 reg;
ktime_t last_measurement;
ktime_t sampling_rate;
struct mutex lock;
};
struct vcnl4000_data {
struct i2c_client *client;
struct mutex lock;
enum vcnl4000_device_ids id;
int rev;
int al_scale;
const struct vcnl4000_chip_spec *chip_spec;
struct mutex vcnl4000_lock;
struct vcnl4200_channel vcnl4200_al;
struct vcnl4200_channel vcnl4200_ps;
};
struct vcnl4000_chip_spec {
const char *prod;
int (*init)(struct vcnl4000_data *data);
int (*measure_light)(struct vcnl4000_data *data, int *val);
int (*measure_proximity)(struct vcnl4000_data *data, int *val);
};
static const struct i2c_device_id vcnl4000_id[] = {
{ "vcnl4000", 0 },
{ "vcnl4000", VCNL4000 },
{ "vcnl4010", VCNL4010 },
{ "vcnl4020", VCNL4010 },
{ "vcnl4200", VCNL4200 },
{ }
};
MODULE_DEVICE_TABLE(i2c, vcnl4000_id);
static int vcnl4000_init(struct vcnl4000_data *data)
{
int ret, prod_id;
ret = i2c_smbus_read_byte_data(data->client, VCNL4000_PROD_REV);
if (ret < 0)
return ret;
prod_id = ret >> 4;
switch (prod_id) {
case VCNL4000_PROD_ID:
if (data->id != VCNL4000)
dev_warn(&data->client->dev,
"wrong device id, use vcnl4000");
break;
case VCNL4010_PROD_ID:
if (data->id != VCNL4010)
dev_warn(&data->client->dev,
"wrong device id, use vcnl4010/4020");
break;
default:
return -ENODEV;
}
data->rev = ret & 0xf;
data->al_scale = 250000;
mutex_init(&data->vcnl4000_lock);
return 0;
};
static int vcnl4200_init(struct vcnl4000_data *data)
{
int ret;
ret = i2c_smbus_read_word_data(data->client, VCNL4200_DEV_ID);
if (ret < 0)
return ret;
if ((ret & 0xff) != VCNL4200_PROD_ID)
return -ENODEV;
data->rev = (ret >> 8) & 0xf;
/* Set defaults and enable both channels */
ret = i2c_smbus_write_byte_data(data->client, VCNL4200_AL_CONF, 0x00);
if (ret < 0)
return ret;
ret = i2c_smbus_write_byte_data(data->client, VCNL4200_PS_CONF1, 0x00);
if (ret < 0)
return ret;
data->al_scale = 24000;
data->vcnl4200_al.reg = VCNL4200_AL_DATA;
data->vcnl4200_ps.reg = VCNL4200_PS_DATA;
/* Integration time is 50ms, but the experiments show 54ms in total. */
data->vcnl4200_al.sampling_rate = ktime_set(0, 54000 * 1000);
data->vcnl4200_ps.sampling_rate = ktime_set(0, 4200 * 1000);
data->vcnl4200_al.last_measurement = ktime_set(0, 0);
data->vcnl4200_ps.last_measurement = ktime_set(0, 0);
mutex_init(&data->vcnl4200_al.lock);
mutex_init(&data->vcnl4200_ps.lock);
return 0;
};
static int vcnl4000_measure(struct vcnl4000_data *data, u8 req_mask,
u8 rdy_mask, u8 data_reg, int *val)
{
......@@ -64,7 +168,7 @@ static int vcnl4000_measure(struct vcnl4000_data *data, u8 req_mask,
__be16 buf;
int ret;
mutex_lock(&data->lock);
mutex_lock(&data->vcnl4000_lock);
ret = i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND,
req_mask);
......@@ -93,16 +197,88 @@ static int vcnl4000_measure(struct vcnl4000_data *data, u8 req_mask,
if (ret < 0)
goto fail;
mutex_unlock(&data->lock);
mutex_unlock(&data->vcnl4000_lock);
*val = be16_to_cpu(buf);
return 0;
fail:
mutex_unlock(&data->lock);
mutex_unlock(&data->vcnl4000_lock);
return ret;
}
static int vcnl4200_measure(struct vcnl4000_data *data,
struct vcnl4200_channel *chan, int *val)
{
int ret;
s64 delta;
ktime_t next_measurement;
mutex_lock(&chan->lock);
next_measurement = ktime_add(chan->last_measurement,
chan->sampling_rate);
delta = ktime_us_delta(next_measurement, ktime_get());
if (delta > 0)
usleep_range(delta, delta + 500);
chan->last_measurement = ktime_get();
mutex_unlock(&chan->lock);
ret = i2c_smbus_read_word_data(data->client, chan->reg);
if (ret < 0)
return ret;
*val = ret;
return 0;
}
static int vcnl4000_measure_light(struct vcnl4000_data *data, int *val)
{
return vcnl4000_measure(data,
VCNL4000_AL_OD, VCNL4000_AL_RDY,
VCNL4000_AL_RESULT_HI, val);
}
static int vcnl4200_measure_light(struct vcnl4000_data *data, int *val)
{
return vcnl4200_measure(data, &data->vcnl4200_al, val);
}
static int vcnl4000_measure_proximity(struct vcnl4000_data *data, int *val)
{
return vcnl4000_measure(data,
VCNL4000_PS_OD, VCNL4000_PS_RDY,
VCNL4000_PS_RESULT_HI, val);
}
static int vcnl4200_measure_proximity(struct vcnl4000_data *data, int *val)
{
return vcnl4200_measure(data, &data->vcnl4200_ps, val);
}
static const struct vcnl4000_chip_spec vcnl4000_chip_spec_cfg[] = {
[VCNL4000] = {
.prod = "VCNL4000",
.init = vcnl4000_init,
.measure_light = vcnl4000_measure_light,
.measure_proximity = vcnl4000_measure_proximity,
},
[VCNL4010] = {
.prod = "VCNL4010/4020",
.init = vcnl4000_init,
.measure_light = vcnl4000_measure_light,
.measure_proximity = vcnl4000_measure_proximity,
},
[VCNL4200] = {
.prod = "VCNL4200",
.init = vcnl4200_init,
.measure_light = vcnl4200_measure_light,
.measure_proximity = vcnl4200_measure_proximity,
},
};
static const struct iio_chan_spec vcnl4000_channels[] = {
{
.type = IIO_LIGHT,
......@@ -125,16 +301,12 @@ static int vcnl4000_read_raw(struct iio_dev *indio_dev,
case IIO_CHAN_INFO_RAW:
switch (chan->type) {
case IIO_LIGHT:
ret = vcnl4000_measure(data,
VCNL4000_AL_OD, VCNL4000_AL_RDY,
VCNL4000_AL_RESULT_HI, val);
ret = data->chip_spec->measure_light(data, val);
if (ret < 0)
return ret;
return IIO_VAL_INT;
case IIO_PROXIMITY:
ret = vcnl4000_measure(data,
VCNL4000_PS_OD, VCNL4000_PS_RDY,
VCNL4000_PS_RESULT_HI, val);
ret = data->chip_spec->measure_proximity(data, val);
if (ret < 0)
return ret;
return IIO_VAL_INT;
......@@ -146,7 +318,7 @@ static int vcnl4000_read_raw(struct iio_dev *indio_dev,
return -EINVAL;
*val = 0;
*val2 = 250000;
*val2 = data->al_scale;
return IIO_VAL_INT_PLUS_MICRO;
default:
return -EINVAL;
......@@ -162,7 +334,7 @@ static int vcnl4000_probe(struct i2c_client *client,
{
struct vcnl4000_data *data;
struct iio_dev *indio_dev;
int ret, prod_id;
int ret;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (!indio_dev)
......@@ -171,19 +343,15 @@ static int vcnl4000_probe(struct i2c_client *client,
data = iio_priv(indio_dev);
i2c_set_clientdata(client, indio_dev);
data->client = client;
mutex_init(&data->lock);
data->id = id->driver_data;
data->chip_spec = &vcnl4000_chip_spec_cfg[data->id];
ret = i2c_smbus_read_byte_data(data->client, VCNL4000_PROD_REV);
ret = data->chip_spec->init(data);
if (ret < 0)
return ret;
prod_id = ret >> 4;
if (prod_id != VCNL4010_ID && prod_id != VCNL4000_ID)
return -ENODEV;
dev_dbg(&client->dev, "%s Ambient light/proximity sensor, Rev: %02x\n",
(prod_id == VCNL4010_ID) ? "VCNL4010/4020" : "VCNL4000",
ret & 0xf);
data->chip_spec->prod, data->rev);
indio_dev->dev.parent = &client->dev;
indio_dev->info = &vcnl4000_info;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment