diff --git a/drivers/iio/adc/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c index cf1b048b0665..fc9510716ac7 100644 --- a/drivers/iio/adc/ad_sigma_delta.c +++ b/drivers/iio/adc/ad_sigma_delta.c @@ -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; diff --git a/drivers/iio/adc/xilinx-xadc-core.c b/drivers/iio/adc/xilinx-xadc-core.c index 0127e8513166..3f6be5ac049a 100644 --- a/drivers/iio/adc/xilinx-xadc-core.c +++ b/drivers/iio/adc/xilinx-xadc-core.c @@ -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); diff --git a/drivers/iio/adc/xilinx-xadc.h b/drivers/iio/adc/xilinx-xadc.h index 62edbdae1244..8c0009585c16 100644 --- a/drivers/iio/adc/xilinx-xadc.h +++ b/drivers/iio/adc/xilinx-xadc.h @@ -68,6 +68,7 @@ struct xadc { spinlock_t lock; struct completion completion; + int irq; }; struct xadc_ops { diff --git a/drivers/iio/chemical/Kconfig b/drivers/iio/chemical/Kconfig index 5cb5be7612b4..b8e005be4f87 100644 --- a/drivers/iio/chemical/Kconfig +++ b/drivers/iio/chemical/Kconfig @@ -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 diff --git a/drivers/iio/chemical/Makefile b/drivers/iio/chemical/Makefile index a629b29d1e0b..2f4c4ba4d781 100644 --- a/drivers/iio/chemical/Makefile +++ b/drivers/iio/chemical/Makefile @@ -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 diff --git a/drivers/iio/chemical/bme680.h b/drivers/iio/chemical/bme680.h new file mode 100644 index 000000000000..e049323f209a --- /dev/null +++ b/drivers/iio/chemical/bme680.h @@ -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_ */ diff --git a/drivers/iio/chemical/bme680_core.c b/drivers/iio/chemical/bme680_core.c new file mode 100644 index 000000000000..7d9bb62baa3f --- /dev/null +++ b/drivers/iio/chemical/bme680_core.c @@ -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 + * + * Datasheet: + * https://ae-bst.resource.bosch.com/media/_tech/media/datasheets/BST-BME680-DS001-00.pdf + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#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 "); +MODULE_DESCRIPTION("Bosch BME680 Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/chemical/bme680_i2c.c b/drivers/iio/chemical/bme680_i2c.c new file mode 100644 index 000000000000..06d4be539d2e --- /dev/null +++ b/drivers/iio/chemical/bme680_i2c.c @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * BME680 - I2C Driver + * + * Copyright (C) 2018 Himanshu Jha + * + * 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 +#include +#include +#include + +#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 "); +MODULE_DESCRIPTION("BME680 I2C driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/chemical/bme680_spi.c b/drivers/iio/chemical/bme680_spi.c new file mode 100644 index 000000000000..c9fb05e8d0b9 --- /dev/null +++ b/drivers/iio/chemical/bme680_spi.c @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * BME680 - SPI Driver + * + * Copyright (C) 2018 Himanshu Jha + */ +#include +#include +#include +#include + +#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 "); +MODULE_DESCRIPTION("Bosch BME680 SPI driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/frequency/ad9523.c b/drivers/iio/frequency/ad9523.c index 37504739c277..f4a508107f0d 100644 --- a/drivers/iio/frequency/ad9523.c +++ b/drivers/iio/frequency/ad9523.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -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; diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig index f17f701a9b61..d66ea754ffff 100644 --- a/drivers/iio/light/Kconfig +++ b/drivers/iio/light/Kconfig @@ -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. diff --git a/drivers/iio/light/si1133.c b/drivers/iio/light/si1133.c index d3fbeb3bc463..015a21f0c2ef 100644 --- a/drivers/iio/light/si1133.c +++ b/drivers/iio/light/si1133.c @@ -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, diff --git a/drivers/iio/light/vcnl4000.c b/drivers/iio/light/vcnl4000.c index c599a90506ad..04fd0d4b6f19 100644 --- a/drivers/iio/light/vcnl4000.c +++ b/drivers/iio/light/vcnl4000.c @@ -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 @@ -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 @@ -26,8 +28,9 @@ #include #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;