mirror of https://gitee.com/openkylin/linux.git
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. -----BEGIN PGP SIGNATURE----- iQJFBAABCAAvFiEEbilms4eEBlKRJoGxVIU0mcT0FogFAlth+aERHGppYzIzQGtl cm5lbC5vcmcACgkQVIU0mcT0FojlVw//bnip7t+HAAExI9gAMYUYre8rpC+LrVy7 vA+Z64p2NRRtsAskYCul0b+g+eX83qth3sl1fDDvHgwuZx5/Pe5Pny6K8Wx/824c roEBu3qh5q7tke+E5GINhu60qIJZX6bNevkZ3UhTHgmjBgvBYP5MdWPM2OY7LOUt 5/XkcYOaDO4Q9SHHZeXvbuTUPW1bLTRd2iN+KOSIkQ0wdoG8lwNPLuu+gpSJgQAb n5fR8dRlnvuLsefrCaoNzmrpoD/zQbU5PS31btSghCJtFJrnuiaGrIIRjBPIuuM8 2jG7RA1Lka0hdR1IFDAfeAMzS1OkxqzAKtdQeBUxv0x0291JqJdI5Wkmo/sSehRd bkJaWVY96+PglbOyXJ9t2RfQrZ8pgAoHTqUXK+qCBP1N/XpcnsDWM+zbeWPPTaDx xuXCba58p8W0D1UeYdSdjTORAxekEFBAakTXfsTHrNg8w78WAZLGLg0cTST6umRl FNzDMzZLS4kJ1a8er8+yH2PgjW5WLqkP5iG7K0Z1js4A2Fxu9EH6M3Hym2hxtLVA OHq9qkMD5OrOVYoKxusX8ilyWE0DLimvEfw25mNXtBnGxE43jlGDVT48dUwXpIJK Ll9ihASfixc0q5tSE0fjuIuprMYI6bzL4laH60usFTK35EmL1jtBeOo3F6AZ6HL5 4LEHBG9L9vI= =lYzx -----END PGP SIGNATURE----- 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.
This commit is contained in:
commit
c0f460ffeb
|
@ -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,15 +1226,15 @@ static int xadc_probe(struct platform_device *pdev)
|
|||
if (ret)
|
||||
goto err_free_samplerate_trigger;
|
||||
|
||||
ret = xadc->ops->setup(pdev, indio_dev, irq);
|
||||
if (ret)
|
||||
goto err_clk_disable_unprepare;
|
||||
|
||||
ret = request_irq(irq, xadc->ops->interrupt_handler, 0,
|
||||
ret = request_irq(xadc->irq, xadc->ops->interrupt_handler, 0,
|
||||
dev_name(&pdev->dev), indio_dev);
|
||||
if (ret)
|
||||
goto err_clk_disable_unprepare;
|
||||
|
||||
ret = xadc->ops->setup(pdev, indio_dev, xadc->irq);
|
||||
if (ret)
|
||||
goto err_free_irq;
|
||||
|
||||
for (i = 0; i < 16; i++)
|
||||
xadc_read_adc_reg(xadc, XADC_REG_THRESHOLD(i),
|
||||
&xadc->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
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
/* 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_ */
|
|
@ -0,0 +1,959 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Bosch BME680 - Temperature, Pressure, Humidity & Gas Sensor
|
||||
*
|
||||
* Copyright (C) 2017 - 2018 Bosch Sensortec GmbH
|
||||
* Copyright (C) 2018 Himanshu Jha <himanshujha199640@gmail.com>
|
||||
*
|
||||
* Datasheet:
|
||||
* https://ae-bst.resource.bosch.com/media/_tech/media/datasheets/BST-BME680-DS001-00.pdf
|
||||
*/
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/log2.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
#include "bme680.h"
|
||||
|
||||
struct bme680_calib {
|
||||
u16 par_t1;
|
||||
s16 par_t2;
|
||||
s8 par_t3;
|
||||
u16 par_p1;
|
||||
s16 par_p2;
|
||||
s8 par_p3;
|
||||
s16 par_p4;
|
||||
s16 par_p5;
|
||||
s8 par_p6;
|
||||
s8 par_p7;
|
||||
s16 par_p8;
|
||||
s16 par_p9;
|
||||
u8 par_p10;
|
||||
u16 par_h1;
|
||||
u16 par_h2;
|
||||
s8 par_h3;
|
||||
s8 par_h4;
|
||||
s8 par_h5;
|
||||
s8 par_h6;
|
||||
s8 par_h7;
|
||||
s8 par_gh1;
|
||||
s16 par_gh2;
|
||||
s8 par_gh3;
|
||||
u8 res_heat_range;
|
||||
s8 res_heat_val;
|
||||
s8 range_sw_err;
|
||||
};
|
||||
|
||||
struct bme680_data {
|
||||
struct regmap *regmap;
|
||||
struct bme680_calib bme680;
|
||||
u8 oversampling_temp;
|
||||
u8 oversampling_press;
|
||||
u8 oversampling_humid;
|
||||
u16 heater_dur;
|
||||
u16 heater_temp;
|
||||
/*
|
||||
* Carryover value from temperature conversion, used in pressure
|
||||
* and humidity compensation calculations.
|
||||
*/
|
||||
s32 t_fine;
|
||||
};
|
||||
|
||||
const struct regmap_config bme680_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
};
|
||||
EXPORT_SYMBOL(bme680_regmap_config);
|
||||
|
||||
static const struct iio_chan_spec bme680_channels[] = {
|
||||
{
|
||||
.type = IIO_TEMP,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
|
||||
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
|
||||
},
|
||||
{
|
||||
.type = IIO_PRESSURE,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
|
||||
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
|
||||
},
|
||||
{
|
||||
.type = IIO_HUMIDITYRELATIVE,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
|
||||
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
|
||||
},
|
||||
{
|
||||
.type = IIO_RESISTANCE,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
|
||||
},
|
||||
};
|
||||
|
||||
static const int bme680_oversampling_avail[] = { 1, 2, 4, 8, 16 };
|
||||
|
||||
static int bme680_read_calib(struct bme680_data *data,
|
||||
struct bme680_calib *calib)
|
||||
{
|
||||
struct device *dev = regmap_get_device(data->regmap);
|
||||
unsigned int tmp, tmp_msb, tmp_lsb;
|
||||
int ret;
|
||||
__le16 buf;
|
||||
|
||||
/* Temperature related coefficients */
|
||||
ret = regmap_bulk_read(data->regmap, BME680_T1_LSB_REG,
|
||||
(u8 *) &buf, 2);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read BME680_T1_LSB_REG\n");
|
||||
return ret;
|
||||
}
|
||||
calib->par_t1 = le16_to_cpu(buf);
|
||||
|
||||
ret = regmap_bulk_read(data->regmap, BME680_T2_LSB_REG,
|
||||
(u8 *) &buf, 2);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read BME680_T2_LSB_REG\n");
|
||||
return ret;
|
||||
}
|
||||
calib->par_t2 = le16_to_cpu(buf);
|
||||
|
||||
ret = regmap_read(data->regmap, BME680_T3_REG, &tmp);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read BME680_T3_REG\n");
|
||||
return ret;
|
||||
}
|
||||
calib->par_t3 = tmp;
|
||||
|
||||
/* Pressure related coefficients */
|
||||
ret = regmap_bulk_read(data->regmap, BME680_P1_LSB_REG,
|
||||
(u8 *) &buf, 2);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read BME680_P1_LSB_REG\n");
|
||||
return ret;
|
||||
}
|
||||
calib->par_p1 = le16_to_cpu(buf);
|
||||
|
||||
ret = regmap_bulk_read(data->regmap, BME680_P2_LSB_REG,
|
||||
(u8 *) &buf, 2);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read BME680_P2_LSB_REG\n");
|
||||
return ret;
|
||||
}
|
||||
calib->par_p2 = le16_to_cpu(buf);
|
||||
|
||||
ret = regmap_read(data->regmap, BME680_P3_REG, &tmp);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read BME680_P3_REG\n");
|
||||
return ret;
|
||||
}
|
||||
calib->par_p3 = tmp;
|
||||
|
||||
ret = regmap_bulk_read(data->regmap, BME680_P4_LSB_REG,
|
||||
(u8 *) &buf, 2);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read BME680_P4_LSB_REG\n");
|
||||
return ret;
|
||||
}
|
||||
calib->par_p4 = le16_to_cpu(buf);
|
||||
|
||||
ret = regmap_bulk_read(data->regmap, BME680_P5_LSB_REG,
|
||||
(u8 *) &buf, 2);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read BME680_P5_LSB_REG\n");
|
||||
return ret;
|
||||
}
|
||||
calib->par_p5 = le16_to_cpu(buf);
|
||||
|
||||
ret = regmap_read(data->regmap, BME680_P6_REG, &tmp);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read BME680_P6_REG\n");
|
||||
return ret;
|
||||
}
|
||||
calib->par_p6 = tmp;
|
||||
|
||||
ret = regmap_read(data->regmap, BME680_P7_REG, &tmp);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read BME680_P7_REG\n");
|
||||
return ret;
|
||||
}
|
||||
calib->par_p7 = tmp;
|
||||
|
||||
ret = regmap_bulk_read(data->regmap, BME680_P8_LSB_REG,
|
||||
(u8 *) &buf, 2);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read BME680_P8_LSB_REG\n");
|
||||
return ret;
|
||||
}
|
||||
calib->par_p8 = le16_to_cpu(buf);
|
||||
|
||||
ret = regmap_bulk_read(data->regmap, BME680_P9_LSB_REG,
|
||||
(u8 *) &buf, 2);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read BME680_P9_LSB_REG\n");
|
||||
return ret;
|
||||
}
|
||||
calib->par_p9 = le16_to_cpu(buf);
|
||||
|
||||
ret = regmap_read(data->regmap, BME680_P10_REG, &tmp);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read BME680_P10_REG\n");
|
||||
return ret;
|
||||
}
|
||||
calib->par_p10 = tmp;
|
||||
|
||||
/* Humidity related coefficients */
|
||||
ret = regmap_read(data->regmap, BME680_H1_MSB_REG, &tmp_msb);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read BME680_H1_MSB_REG\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regmap_read(data->regmap, BME680_H1_LSB_REG, &tmp_lsb);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read BME680_H1_LSB_REG\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
calib->par_h1 = (tmp_msb << BME680_HUM_REG_SHIFT_VAL) |
|
||||
(tmp_lsb & BME680_BIT_H1_DATA_MSK);
|
||||
|
||||
ret = regmap_read(data->regmap, BME680_H2_MSB_REG, &tmp_msb);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read BME680_H2_MSB_REG\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regmap_read(data->regmap, BME680_H2_LSB_REG, &tmp_lsb);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read BME680_H2_LSB_REG\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
calib->par_h2 = (tmp_msb << BME680_HUM_REG_SHIFT_VAL) |
|
||||
(tmp_lsb >> BME680_HUM_REG_SHIFT_VAL);
|
||||
|
||||
ret = regmap_read(data->regmap, BME680_H3_REG, &tmp);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read BME680_H3_REG\n");
|
||||
return ret;
|
||||
}
|
||||
calib->par_h3 = tmp;
|
||||
|
||||
ret = regmap_read(data->regmap, BME680_H4_REG, &tmp);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read BME680_H4_REG\n");
|
||||
return ret;
|
||||
}
|
||||
calib->par_h4 = tmp;
|
||||
|
||||
ret = regmap_read(data->regmap, BME680_H5_REG, &tmp);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read BME680_H5_REG\n");
|
||||
return ret;
|
||||
}
|
||||
calib->par_h5 = tmp;
|
||||
|
||||
ret = regmap_read(data->regmap, BME680_H6_REG, &tmp);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read BME680_H6_REG\n");
|
||||
return ret;
|
||||
}
|
||||
calib->par_h6 = tmp;
|
||||
|
||||
ret = regmap_read(data->regmap, BME680_H7_REG, &tmp);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read BME680_H7_REG\n");
|
||||
return ret;
|
||||
}
|
||||
calib->par_h7 = tmp;
|
||||
|
||||
/* Gas heater related coefficients */
|
||||
ret = regmap_read(data->regmap, BME680_GH1_REG, &tmp);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read BME680_GH1_REG\n");
|
||||
return ret;
|
||||
}
|
||||
calib->par_gh1 = tmp;
|
||||
|
||||
ret = regmap_bulk_read(data->regmap, BME680_GH2_LSB_REG,
|
||||
(u8 *) &buf, 2);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read BME680_GH2_LSB_REG\n");
|
||||
return ret;
|
||||
}
|
||||
calib->par_gh2 = le16_to_cpu(buf);
|
||||
|
||||
ret = regmap_read(data->regmap, BME680_GH3_REG, &tmp);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read BME680_GH3_REG\n");
|
||||
return ret;
|
||||
}
|
||||
calib->par_gh3 = tmp;
|
||||
|
||||
/* Other coefficients */
|
||||
ret = regmap_read(data->regmap, BME680_REG_RES_HEAT_RANGE, &tmp);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read resistance heat range\n");
|
||||
return ret;
|
||||
}
|
||||
calib->res_heat_range = (tmp & BME680_RHRANGE_MSK) / 16;
|
||||
|
||||
ret = regmap_read(data->regmap, BME680_REG_RES_HEAT_VAL, &tmp);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read resistance heat value\n");
|
||||
return ret;
|
||||
}
|
||||
calib->res_heat_val = tmp;
|
||||
|
||||
ret = regmap_read(data->regmap, BME680_REG_RANGE_SW_ERR, &tmp);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read range software error\n");
|
||||
return ret;
|
||||
}
|
||||
calib->range_sw_err = (tmp & BME680_RSERROR_MSK) / 16;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Taken from Bosch BME680 API:
|
||||
* https://github.com/BoschSensortec/BME680_driver/blob/63bb5336/bme680.c#L876
|
||||
*
|
||||
* Returns temperature measurement in DegC, resolutions is 0.01 DegC. Therefore,
|
||||
* output value of "3233" represents 32.33 DegC.
|
||||
*/
|
||||
static s16 bme680_compensate_temp(struct bme680_data *data,
|
||||
s32 adc_temp)
|
||||
{
|
||||
struct bme680_calib *calib = &data->bme680;
|
||||
s64 var1, var2, var3;
|
||||
s16 calc_temp;
|
||||
|
||||
var1 = (adc_temp >> 3) - (calib->par_t1 << 1);
|
||||
var2 = (var1 * calib->par_t2) >> 11;
|
||||
var3 = ((var1 >> 1) * (var1 >> 1)) >> 12;
|
||||
var3 = (var3 * (calib->par_t3 << 4)) >> 14;
|
||||
data->t_fine = var2 + var3;
|
||||
calc_temp = (data->t_fine * 5 + 128) >> 8;
|
||||
|
||||
return calc_temp;
|
||||
}
|
||||
|
||||
/*
|
||||
* Taken from Bosch BME680 API:
|
||||
* https://github.com/BoschSensortec/BME680_driver/blob/63bb5336/bme680.c#L896
|
||||
*
|
||||
* Returns pressure measurement in Pa. Output value of "97356" represents
|
||||
* 97356 Pa = 973.56 hPa.
|
||||
*/
|
||||
static u32 bme680_compensate_press(struct bme680_data *data,
|
||||
u32 adc_press)
|
||||
{
|
||||
struct bme680_calib *calib = &data->bme680;
|
||||
s32 var1, var2, var3, press_comp;
|
||||
|
||||
var1 = (data->t_fine >> 1) - 64000;
|
||||
var2 = ((((var1 >> 2) * (var1 >> 2)) >> 11) * calib->par_p6) >> 2;
|
||||
var2 = var2 + (var1 * calib->par_p5 << 1);
|
||||
var2 = (var2 >> 2) + (calib->par_p4 << 16);
|
||||
var1 = (((((var1 >> 2) * (var1 >> 2)) >> 13) *
|
||||
(calib->par_p3 << 5)) >> 3) +
|
||||
((calib->par_p2 * var1) >> 1);
|
||||
var1 = var1 >> 18;
|
||||
var1 = ((32768 + var1) * calib->par_p1) >> 15;
|
||||
press_comp = 1048576 - adc_press;
|
||||
press_comp = ((press_comp - (var2 >> 12)) * 3125);
|
||||
|
||||
if (press_comp >= BME680_MAX_OVERFLOW_VAL)
|
||||
press_comp = ((press_comp / (u32)var1) << 1);
|
||||
else
|
||||
press_comp = ((press_comp << 1) / (u32)var1);
|
||||
|
||||
var1 = (calib->par_p9 * (((press_comp >> 3) *
|
||||
(press_comp >> 3)) >> 13)) >> 12;
|
||||
var2 = ((press_comp >> 2) * calib->par_p8) >> 13;
|
||||
var3 = ((press_comp >> 8) * (press_comp >> 8) *
|
||||
(press_comp >> 8) * calib->par_p10) >> 17;
|
||||
|
||||
press_comp += (var1 + var2 + var3 + (calib->par_p7 << 7)) >> 4;
|
||||
|
||||
return press_comp;
|
||||
}
|
||||
|
||||
/*
|
||||
* Taken from Bosch BME680 API:
|
||||
* https://github.com/BoschSensortec/BME680_driver/blob/63bb5336/bme680.c#L937
|
||||
*
|
||||
* Returns humidity measurement in percent, resolution is 0.001 percent. Output
|
||||
* value of "43215" represents 43.215 %rH.
|
||||
*/
|
||||
static u32 bme680_compensate_humid(struct bme680_data *data,
|
||||
u16 adc_humid)
|
||||
{
|
||||
struct bme680_calib *calib = &data->bme680;
|
||||
s32 var1, var2, var3, var4, var5, var6, temp_scaled, calc_hum;
|
||||
|
||||
temp_scaled = (data->t_fine * 5 + 128) >> 8;
|
||||
var1 = (adc_humid - ((s32) ((s32) calib->par_h1 * 16))) -
|
||||
(((temp_scaled * (s32) calib->par_h3) / 100) >> 1);
|
||||
var2 = ((s32) calib->par_h2 *
|
||||
(((temp_scaled * calib->par_h4) / 100) +
|
||||
(((temp_scaled * ((temp_scaled * calib->par_h5) / 100))
|
||||
>> 6) / 100) + (1 << 14))) >> 10;
|
||||
var3 = var1 * var2;
|
||||
var4 = calib->par_h6 << 7;
|
||||
var4 = (var4 + ((temp_scaled * calib->par_h7) / 100)) >> 4;
|
||||
var5 = ((var3 >> 14) * (var3 >> 14)) >> 10;
|
||||
var6 = (var4 * var5) >> 1;
|
||||
calc_hum = (((var3 + var6) >> 10) * 1000) >> 12;
|
||||
|
||||
if (calc_hum > 100000) /* Cap at 100%rH */
|
||||
calc_hum = 100000;
|
||||
else if (calc_hum < 0)
|
||||
calc_hum = 0;
|
||||
|
||||
return calc_hum;
|
||||
}
|
||||
|
||||
/*
|
||||
* Taken from Bosch BME680 API:
|
||||
* https://github.com/BoschSensortec/BME680_driver/blob/63bb5336/bme680.c#L973
|
||||
*
|
||||
* Returns gas measurement in Ohm. Output value of "82986" represent 82986 ohms.
|
||||
*/
|
||||
static u32 bme680_compensate_gas(struct bme680_data *data, u16 gas_res_adc,
|
||||
u8 gas_range)
|
||||
{
|
||||
struct bme680_calib *calib = &data->bme680;
|
||||
s64 var1;
|
||||
u64 var2;
|
||||
s64 var3;
|
||||
u32 calc_gas_res;
|
||||
|
||||
/* Look up table for the possible gas range values */
|
||||
const u32 lookupTable[16] = {2147483647u, 2147483647u,
|
||||
2147483647u, 2147483647u, 2147483647u,
|
||||
2126008810u, 2147483647u, 2130303777u,
|
||||
2147483647u, 2147483647u, 2143188679u,
|
||||
2136746228u, 2147483647u, 2126008810u,
|
||||
2147483647u, 2147483647u};
|
||||
|
||||
var1 = ((1340 + (5 * (s64) calib->range_sw_err)) *
|
||||
((s64) lookupTable[gas_range])) >> 16;
|
||||
var2 = ((gas_res_adc << 15) - 16777216) + var1;
|
||||
var3 = ((125000 << (15 - gas_range)) * var1) >> 9;
|
||||
var3 += (var2 >> 1);
|
||||
calc_gas_res = div64_s64(var3, (s64) var2);
|
||||
|
||||
return calc_gas_res;
|
||||
}
|
||||
|
||||
/*
|
||||
* Taken from Bosch BME680 API:
|
||||
* https://github.com/BoschSensortec/BME680_driver/blob/63bb5336/bme680.c#L1002
|
||||
*/
|
||||
static u8 bme680_calc_heater_res(struct bme680_data *data, u16 temp)
|
||||
{
|
||||
struct bme680_calib *calib = &data->bme680;
|
||||
s32 var1, var2, var3, var4, var5, heatr_res_x100;
|
||||
u8 heatr_res;
|
||||
|
||||
if (temp > 400) /* Cap temperature */
|
||||
temp = 400;
|
||||
|
||||
var1 = (((s32) BME680_AMB_TEMP * calib->par_gh3) / 1000) * 256;
|
||||
var2 = (calib->par_gh1 + 784) * (((((calib->par_gh2 + 154009) *
|
||||
temp * 5) / 100)
|
||||
+ 3276800) / 10);
|
||||
var3 = var1 + (var2 / 2);
|
||||
var4 = (var3 / (calib->res_heat_range + 4));
|
||||
var5 = 131 * calib->res_heat_val + 65536;
|
||||
heatr_res_x100 = ((var4 / var5) - 250) * 34;
|
||||
heatr_res = (heatr_res_x100 + 50) / 100;
|
||||
|
||||
return heatr_res;
|
||||
}
|
||||
|
||||
/*
|
||||
* Taken from Bosch BME680 API:
|
||||
* https://github.com/BoschSensortec/BME680_driver/blob/63bb5336/bme680.c#L1188
|
||||
*/
|
||||
static u8 bme680_calc_heater_dur(u16 dur)
|
||||
{
|
||||
u8 durval, factor = 0;
|
||||
|
||||
if (dur >= 0xfc0) {
|
||||
durval = 0xff; /* Max duration */
|
||||
} else {
|
||||
while (dur > 0x3F) {
|
||||
dur = dur / 4;
|
||||
factor += 1;
|
||||
}
|
||||
durval = dur + (factor * 64);
|
||||
}
|
||||
|
||||
return durval;
|
||||
}
|
||||
|
||||
static int bme680_set_mode(struct bme680_data *data, bool mode)
|
||||
{
|
||||
struct device *dev = regmap_get_device(data->regmap);
|
||||
int ret;
|
||||
|
||||
if (mode) {
|
||||
ret = regmap_write_bits(data->regmap, BME680_REG_CTRL_MEAS,
|
||||
BME680_MODE_MASK, BME680_MODE_FORCED);
|
||||
if (ret < 0)
|
||||
dev_err(dev, "failed to set forced mode\n");
|
||||
|
||||
} else {
|
||||
ret = regmap_write_bits(data->regmap, BME680_REG_CTRL_MEAS,
|
||||
BME680_MODE_MASK, BME680_MODE_SLEEP);
|
||||
if (ret < 0)
|
||||
dev_err(dev, "failed to set sleep mode\n");
|
||||
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bme680_chip_config(struct bme680_data *data)
|
||||
{
|
||||
struct device *dev = regmap_get_device(data->regmap);
|
||||
int ret;
|
||||
u8 osrs = FIELD_PREP(BME680_OSRS_HUMIDITY_MASK,
|
||||
data->oversampling_humid + 1);
|
||||
/*
|
||||
* Highly recommended to set oversampling of humidity before
|
||||
* temperature/pressure oversampling.
|
||||
*/
|
||||
ret = regmap_update_bits(data->regmap, BME680_REG_CTRL_HUMIDITY,
|
||||
BME680_OSRS_HUMIDITY_MASK, osrs);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to write ctrl_hum register\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* IIR filter settings */
|
||||
ret = regmap_update_bits(data->regmap, BME680_REG_CONFIG,
|
||||
BME680_FILTER_MASK,
|
||||
BME680_FILTER_COEFF_VAL);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to write config register\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
osrs = FIELD_PREP(BME680_OSRS_TEMP_MASK, data->oversampling_temp + 1) |
|
||||
FIELD_PREP(BME680_OSRS_PRESS_MASK, data->oversampling_press + 1);
|
||||
|
||||
ret = regmap_write_bits(data->regmap, BME680_REG_CTRL_MEAS,
|
||||
BME680_OSRS_TEMP_MASK |
|
||||
BME680_OSRS_PRESS_MASK,
|
||||
osrs);
|
||||
if (ret < 0)
|
||||
dev_err(dev, "failed to write ctrl_meas register\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bme680_gas_config(struct bme680_data *data)
|
||||
{
|
||||
struct device *dev = regmap_get_device(data->regmap);
|
||||
int ret;
|
||||
u8 heatr_res, heatr_dur;
|
||||
|
||||
heatr_res = bme680_calc_heater_res(data, data->heater_temp);
|
||||
|
||||
/* set target heater temperature */
|
||||
ret = regmap_write(data->regmap, BME680_REG_RES_HEAT_0, heatr_res);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to write res_heat_0 register\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
heatr_dur = bme680_calc_heater_dur(data->heater_dur);
|
||||
|
||||
/* set target heating duration */
|
||||
ret = regmap_write(data->regmap, BME680_REG_GAS_WAIT_0, heatr_dur);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failted to write gas_wait_0 register\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Selecting the runGas and NB conversion settings for the sensor */
|
||||
ret = regmap_update_bits(data->regmap, BME680_REG_CTRL_GAS_1,
|
||||
BME680_RUN_GAS_MASK | BME680_NB_CONV_MASK,
|
||||
BME680_RUN_GAS_EN_BIT | BME680_NB_CONV_0_VAL);
|
||||
if (ret < 0)
|
||||
dev_err(dev, "failed to write ctrl_gas_1 register\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bme680_read_temp(struct bme680_data *data,
|
||||
int *val, int *val2)
|
||||
{
|
||||
struct device *dev = regmap_get_device(data->regmap);
|
||||
int ret;
|
||||
__be32 tmp = 0;
|
||||
s32 adc_temp;
|
||||
s16 comp_temp;
|
||||
|
||||
/* set forced mode to trigger measurement */
|
||||
ret = bme680_set_mode(data, true);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = regmap_bulk_read(data->regmap, BME680_REG_TEMP_MSB,
|
||||
(u8 *) &tmp, 3);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read temperature\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
adc_temp = be32_to_cpu(tmp) >> 12;
|
||||
if (adc_temp == BME680_MEAS_SKIPPED) {
|
||||
/* reading was skipped */
|
||||
dev_err(dev, "reading temperature skipped\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
comp_temp = bme680_compensate_temp(data, adc_temp);
|
||||
/*
|
||||
* val might be NULL if we're called by the read_press/read_humid
|
||||
* routine which is callled to get t_fine value used in
|
||||
* compensate_press/compensate_humid to get compensated
|
||||
* pressure/humidity readings.
|
||||
*/
|
||||
if (val && val2) {
|
||||
*val = comp_temp;
|
||||
*val2 = 100;
|
||||
return IIO_VAL_FRACTIONAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bme680_read_press(struct bme680_data *data,
|
||||
int *val, int *val2)
|
||||
{
|
||||
struct device *dev = regmap_get_device(data->regmap);
|
||||
int ret;
|
||||
__be32 tmp = 0;
|
||||
s32 adc_press;
|
||||
|
||||
/* Read and compensate temperature to get a reading of t_fine */
|
||||
ret = bme680_read_temp(data, NULL, NULL);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = regmap_bulk_read(data->regmap, BME680_REG_PRESS_MSB,
|
||||
(u8 *) &tmp, 3);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read pressure\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
adc_press = be32_to_cpu(tmp) >> 12;
|
||||
if (adc_press == BME680_MEAS_SKIPPED) {
|
||||
/* reading was skipped */
|
||||
dev_err(dev, "reading pressure skipped\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*val = bme680_compensate_press(data, adc_press);
|
||||
*val2 = 100;
|
||||
return IIO_VAL_FRACTIONAL;
|
||||
}
|
||||
|
||||
static int bme680_read_humid(struct bme680_data *data,
|
||||
int *val, int *val2)
|
||||
{
|
||||
struct device *dev = regmap_get_device(data->regmap);
|
||||
int ret;
|
||||
__be16 tmp = 0;
|
||||
s32 adc_humidity;
|
||||
u32 comp_humidity;
|
||||
|
||||
/* Read and compensate temperature to get a reading of t_fine */
|
||||
ret = bme680_read_temp(data, NULL, NULL);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = regmap_bulk_read(data->regmap, BM6880_REG_HUMIDITY_MSB,
|
||||
(u8 *) &tmp, 2);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read humidity\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
adc_humidity = be16_to_cpu(tmp);
|
||||
if (adc_humidity == BME680_MEAS_SKIPPED) {
|
||||
/* reading was skipped */
|
||||
dev_err(dev, "reading humidity skipped\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
comp_humidity = bme680_compensate_humid(data, adc_humidity);
|
||||
|
||||
*val = comp_humidity;
|
||||
*val2 = 1000;
|
||||
return IIO_VAL_FRACTIONAL;
|
||||
}
|
||||
|
||||
static int bme680_read_gas(struct bme680_data *data,
|
||||
int *val)
|
||||
{
|
||||
struct device *dev = regmap_get_device(data->regmap);
|
||||
int ret;
|
||||
__be16 tmp = 0;
|
||||
unsigned int check;
|
||||
u16 adc_gas_res;
|
||||
u8 gas_range;
|
||||
|
||||
/* Set heater settings */
|
||||
ret = bme680_gas_config(data);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to set gas config\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* set forced mode to trigger measurement */
|
||||
ret = bme680_set_mode(data, true);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = regmap_read(data->regmap, BME680_REG_MEAS_STAT_0, &check);
|
||||
if (check & BME680_GAS_MEAS_BIT) {
|
||||
dev_err(dev, "gas measurement incomplete\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
ret = regmap_read(data->regmap, BME680_REG_GAS_R_LSB, &check);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read gas_r_lsb register\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* occurs if either the gas heating duration was insuffient
|
||||
* to reach the target heater temperature or the target
|
||||
* heater temperature was too high for the heater sink to
|
||||
* reach.
|
||||
*/
|
||||
if ((check & BME680_GAS_STAB_BIT) == 0) {
|
||||
dev_err(dev, "heater failed to reach the target temperature\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = regmap_bulk_read(data->regmap, BME680_REG_GAS_MSB,
|
||||
(u8 *) &tmp, 2);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to read gas resistance\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
gas_range = check & BME680_GAS_RANGE_MASK;
|
||||
adc_gas_res = be16_to_cpu(tmp) >> BME680_ADC_GAS_RES_SHIFT;
|
||||
|
||||
*val = bme680_compensate_gas(data, adc_gas_res, gas_range);
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
static int bme680_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct bme680_data *data = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_PROCESSED:
|
||||
switch (chan->type) {
|
||||
case IIO_TEMP:
|
||||
return bme680_read_temp(data, val, val2);
|
||||
case IIO_PRESSURE:
|
||||
return bme680_read_press(data, val, val2);
|
||||
case IIO_HUMIDITYRELATIVE:
|
||||
return bme680_read_humid(data, val, val2);
|
||||
case IIO_RESISTANCE:
|
||||
return bme680_read_gas(data, val);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
|
||||
switch (chan->type) {
|
||||
case IIO_TEMP:
|
||||
*val = 1 << data->oversampling_temp;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_PRESSURE:
|
||||
*val = 1 << data->oversampling_press;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_HUMIDITYRELATIVE:
|
||||
*val = 1 << data->oversampling_humid;
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int bme680_write_oversampling_ratio_temp(struct bme680_data *data,
|
||||
int val)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(bme680_oversampling_avail); i++) {
|
||||
if (bme680_oversampling_avail[i] == val) {
|
||||
data->oversampling_temp = ilog2(val);
|
||||
|
||||
return bme680_chip_config(data);
|
||||
}
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int bme680_write_oversampling_ratio_press(struct bme680_data *data,
|
||||
int val)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(bme680_oversampling_avail); i++) {
|
||||
if (bme680_oversampling_avail[i] == val) {
|
||||
data->oversampling_press = ilog2(val);
|
||||
|
||||
return bme680_chip_config(data);
|
||||
}
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int bme680_write_oversampling_ratio_humid(struct bme680_data *data,
|
||||
int val)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(bme680_oversampling_avail); i++) {
|
||||
if (bme680_oversampling_avail[i] == val) {
|
||||
data->oversampling_humid = ilog2(val);
|
||||
|
||||
return bme680_chip_config(data);
|
||||
}
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int bme680_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
struct bme680_data *data = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
|
||||
switch (chan->type) {
|
||||
case IIO_TEMP:
|
||||
return bme680_write_oversampling_ratio_temp(data, val);
|
||||
case IIO_PRESSURE:
|
||||
return bme680_write_oversampling_ratio_press(data, val);
|
||||
case IIO_HUMIDITYRELATIVE:
|
||||
return bme680_write_oversampling_ratio_humid(data, val);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static const char bme680_oversampling_ratio_show[] = "1 2 4 8 16";
|
||||
|
||||
static IIO_CONST_ATTR(oversampling_ratio_available,
|
||||
bme680_oversampling_ratio_show);
|
||||
|
||||
static struct attribute *bme680_attributes[] = {
|
||||
&iio_const_attr_oversampling_ratio_available.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group bme680_attribute_group = {
|
||||
.attrs = bme680_attributes,
|
||||
};
|
||||
|
||||
static const struct iio_info bme680_info = {
|
||||
.read_raw = &bme680_read_raw,
|
||||
.write_raw = &bme680_write_raw,
|
||||
.attrs = &bme680_attribute_group,
|
||||
};
|
||||
|
||||
static const char *bme680_match_acpi_device(struct device *dev)
|
||||
{
|
||||
const struct acpi_device_id *id;
|
||||
|
||||
id = acpi_match_device(dev->driver->acpi_match_table, dev);
|
||||
if (!id)
|
||||
return NULL;
|
||||
|
||||
return dev_name(dev);
|
||||
}
|
||||
|
||||
int bme680_core_probe(struct device *dev, struct regmap *regmap,
|
||||
const char *name)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct bme680_data *data;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!name && ACPI_HANDLE(dev))
|
||||
name = bme680_match_acpi_device(dev);
|
||||
|
||||
data = iio_priv(indio_dev);
|
||||
dev_set_drvdata(dev, indio_dev);
|
||||
data->regmap = regmap;
|
||||
indio_dev->dev.parent = dev;
|
||||
indio_dev->name = name;
|
||||
indio_dev->channels = bme680_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(bme680_channels);
|
||||
indio_dev->info = &bme680_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
/* default values for the sensor */
|
||||
data->oversampling_humid = ilog2(2); /* 2X oversampling rate */
|
||||
data->oversampling_press = ilog2(4); /* 4X oversampling rate */
|
||||
data->oversampling_temp = ilog2(8); /* 8X oversampling rate */
|
||||
data->heater_temp = 320; /* degree Celsius */
|
||||
data->heater_dur = 150; /* milliseconds */
|
||||
|
||||
ret = bme680_chip_config(data);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to set chip_config data\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = bme680_gas_config(data);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to set gas config data\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = bme680_read_calib(data, &data->bme680);
|
||||
if (ret < 0) {
|
||||
dev_err(dev,
|
||||
"failed to read calibration coefficients at probe\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return devm_iio_device_register(dev, indio_dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(bme680_core_probe);
|
||||
|
||||
MODULE_AUTHOR("Himanshu Jha <himanshujha199640@gmail.com>");
|
||||
MODULE_DESCRIPTION("Bosch BME680 Driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -0,0 +1,85 @@
|
|||
// 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");
|
|
@ -0,0 +1,125 @@
|
|||
// 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,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* 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 */
|
||||
|
||||
struct vcnl4000_data {
|
||||
struct i2c_client *client;
|
||||
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;
|
||||
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;
|
||||
|
|
Loading…
Reference in New Issue