linux/drivers/iio/humidity/hdc3020.c
Greg Kroah-Hartman d4551c189d IIO: 1st set of new device support, features and cleanup for 6.9
IIO Backend support
 ===================
 
 New approach from Nuno Sa to the problem of reuse of drivers with
 IIO devices that are actually the combination of a highspeed chip
 and an FPGA core handling the data capture and flows. It will hopefully
 also apply to some other split designs. The ad9467 and axi-adi drivers
 are converted over to this framework.
 
 New device support
 ==================
 
 adi,admfm2000
 - New driver for this dual microwave down converter.
 ams,as73211
 - Add support for as7331 UV sensor.
 richtek,rtq6056
 - Add support for related parts RTQ6053 and RTQ6059
 st,lsm6dsx
 - Add ASM330LHHXG1 accelerometer and gyro support (mainly IDs)
 ti,ads1298
 - New driver for this medical ADC.
 
 Features
 ========
 
 tests
 - Unit tests for the gain-time-scale helper library.
 bosch,bmi088
 - I2C support.
 bosh,bmi160
 - Add 10EC5280 ACPI ID. Used in a number of devices that won't get fixed.
   The ID is actually a PCI ID belonging to realtech. No response was received
   to earlier attempts to notify them of this.
   The manufacturers of some devices have replied to say they will not fix
   this incorrect ID. Add the ID and hope it isn't a problem.
 bosch,bmi323
 - Add BOSC0200 ACPI ID. Note this is a duplicate of one in the bmc150
   driver (it appears these parts share a windows driver).
   Both drivers perform an ID check that is safe on the other part before
   successfully probing.
 hid-sensors-als
 - Add color temperature and chromaticity support. Note this is a replacement
   for the series reverted in 6.8 that correctly handles all the potential
   channel combinations.
 honeywell,hsc030pa
 - Triggered buffer support (after driver cleanup).
 honeywell,mprls00025pa
 - Improved error handling.
 - New DT binding to allow use of part number triplet as provided in data sheet
   to specify equivalent of most of the binding more efficiently.
 - SPI support.
 memsic,mxc4005
 - ACPI ID MDA6655 as seen in the Chuwi Minibook X 2023
 ti,hdc3020
 - Add threshold event support (after some driver cleanup)
 veml,vcnl4000
 - Switch to high resolution proximity measurement.
 
 Cleanup
 =======
 Various minor typo fixes and better use of defines etc.
 
 Treewide
 - Stop using ACPI_PTR(). The savings in space are small and not worth
   the complexity of __maybe_unused of ifdef guards.  To avoid use in
   new IIO drivers based on copy and paste, clean it out.
 - cleanup.h based handling of iio_device_claim_direct_mode()/
   iio_device_release_direct_mode() using scope_cond_guard().
   In many drivers this is combined with other automated cleanup
   to give maximum simplifications.
   An initial set of drivers are converted over to this infrastructure.
 
 Tools
 - Use rewinddir() instead of seekdir() to return to start of file.
 
 core
 - Make iio_bus_type constant.
 
 adi,ad16475
 - Use irq_get_trigger_type() instead of opencoding.
 adi,ad16480
 - Use irq_get_trigger_type() instead of opencoding.
 adi,ad-sigma-delta
 - Avoid overwriting IRQ flags if provided by firmware.
 ams,as73211
 - Use IIO_VAL_FRACTIONAL for scales to simplify the code and potentially
   improve accuracy.
 gts-library
 - Use a div64_u64() instead of a loop to do a division.
 honeywell,mprls00025pa
 - Clean up dt-binding doc.
 - Drop defaults when DT binding not providing values. Very unlikely
   these were useful given they were wrong for vast majority of supported
   devices.
 - Whitespace cleanup
 miramems,da280
 - Use i2c_get_match_data() to replace hand rolled ACPI matching code.
 semtech,sx9324
 - Avoid unnecessary copying of property strings.
 st,lsm6dsx
 - Improve docs, particularly wrt to making addition of new device
   support less noisy.
 st,lsm9ds0
 - Use dev_err_probe() in all probe() error handling.
 - Improved header includes.
 - Tidy up termination of ID tables.
 ti,ads1014
 - Correct upper bound on PGA (wrong value had no actual impact)
 ti,afe4403/4404
 - devm_ useage to simplify error handling in probe() and allow() remove to
   be dropped.
 voltage-divider
 - Add dt-binding for io-channel-cells to allow such a device to be both
   an IIO consumer and IIO producer at the same time.
 -----BEGIN PGP SIGNATURE-----
 
 iQJFBAABCAAvFiEEbilms4eEBlKRJoGxVIU0mcT0FogFAmXXmXERHGppYzIzQGtl
 cm5lbC5vcmcACgkQVIU0mcT0Foj8Bg/+OZgU0FOEhUXidF58ulMBPTmWG4qBsCCK
 nB8MhimNn9XcoUDkZwns5ABgRKJvYVqK3VXaGj7HXRcpHzjOAHDCD1aS9EDdwHuo
 prH9Q5L7iVMhOjKcK3UwtzbUOAjrrmE4Oa74RH4oDLKV9ws6mb1VQfoOgpRNTsTK
 rA0A3B4W78U+UxLZPWIuYDUS/BgDfSfB024VFtUhMvo0B6G52u6hoDk04//hQuW8
 IE0db4VxJLuRDYinBiTbtFtXWO8FNWd8r37MlQKx/llEKtjzI1ZttOK8DyQRudJ7
 rLP3nO8svCcQ9oJn0YrnFL9y6PlW/ASUaXH47xBfAiCdKgHlQTf3kDyzwddCqQRD
 3cqghbUbYp65Nm1o2TXYQio4zEInS/1ZMzpezb9K1+oiz/xGarjMCR+J4Za4cw76
 5jCfnp8vBPDX7JdGwYgbK9nXkXAJ5ewdm7ad03t3oPwfAQPlXfAHIWDT8evNDVlc
 L94RSmKeMKL1BG3jOr7mJbqIwivf799pgudVJMuBfnlYR+gkBBVvFWhXrgvsJWhQ
 gKhqfEYCjtJ/t483zcJporvWBmmmf6gUvNOB4x6oA0j85GPGoDlTdfK54dkD+98k
 KP83Wd4LqDOM6V6PE+L7yTW05sOgT2qDHYrw/qAPoEogThSSCoqRm/kWwPBv3toL
 0ykQId5/UBk=
 =xkgf
 -----END PGP SIGNATURE-----

Merge tag 'iio-for-6.9a' of http://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio into char-misc-next

Jonathan writes:

IIO: 1st set of new device support, features and cleanup for 6.9

IIO Backend support
===================

New approach from Nuno Sa to the problem of reuse of drivers with
IIO devices that are actually the combination of a highspeed chip
and an FPGA core handling the data capture and flows. It will hopefully
also apply to some other split designs. The ad9467 and axi-adi drivers
are converted over to this framework.

New device support
==================

adi,admfm2000
- New driver for this dual microwave down converter.
ams,as73211
- Add support for as7331 UV sensor.
richtek,rtq6056
- Add support for related parts RTQ6053 and RTQ6059
st,lsm6dsx
- Add ASM330LHHXG1 accelerometer and gyro support (mainly IDs)
ti,ads1298
- New driver for this medical ADC.

Features
========

tests
- Unit tests for the gain-time-scale helper library.
bosch,bmi088
- I2C support.
bosh,bmi160
- Add 10EC5280 ACPI ID. Used in a number of devices that won't get fixed.
  The ID is actually a PCI ID belonging to realtech. No response was received
  to earlier attempts to notify them of this.
  The manufacturers of some devices have replied to say they will not fix
  this incorrect ID. Add the ID and hope it isn't a problem.
bosch,bmi323
- Add BOSC0200 ACPI ID. Note this is a duplicate of one in the bmc150
  driver (it appears these parts share a windows driver).
  Both drivers perform an ID check that is safe on the other part before
  successfully probing.
hid-sensors-als
- Add color temperature and chromaticity support. Note this is a replacement
  for the series reverted in 6.8 that correctly handles all the potential
  channel combinations.
honeywell,hsc030pa
- Triggered buffer support (after driver cleanup).
honeywell,mprls00025pa
- Improved error handling.
- New DT binding to allow use of part number triplet as provided in data sheet
  to specify equivalent of most of the binding more efficiently.
- SPI support.
memsic,mxc4005
- ACPI ID MDA6655 as seen in the Chuwi Minibook X 2023
ti,hdc3020
- Add threshold event support (after some driver cleanup)
veml,vcnl4000
- Switch to high resolution proximity measurement.

Cleanup
=======
Various minor typo fixes and better use of defines etc.

Treewide
- Stop using ACPI_PTR(). The savings in space are small and not worth
  the complexity of __maybe_unused of ifdef guards.  To avoid use in
  new IIO drivers based on copy and paste, clean it out.
- cleanup.h based handling of iio_device_claim_direct_mode()/
  iio_device_release_direct_mode() using scope_cond_guard().
  In many drivers this is combined with other automated cleanup
  to give maximum simplifications.
  An initial set of drivers are converted over to this infrastructure.

Tools
- Use rewinddir() instead of seekdir() to return to start of file.

core
- Make iio_bus_type constant.

adi,ad16475
- Use irq_get_trigger_type() instead of opencoding.
adi,ad16480
- Use irq_get_trigger_type() instead of opencoding.
adi,ad-sigma-delta
- Avoid overwriting IRQ flags if provided by firmware.
ams,as73211
- Use IIO_VAL_FRACTIONAL for scales to simplify the code and potentially
  improve accuracy.
gts-library
- Use a div64_u64() instead of a loop to do a division.
honeywell,mprls00025pa
- Clean up dt-binding doc.
- Drop defaults when DT binding not providing values. Very unlikely
  these were useful given they were wrong for vast majority of supported
  devices.
- Whitespace cleanup
miramems,da280
- Use i2c_get_match_data() to replace hand rolled ACPI matching code.
semtech,sx9324
- Avoid unnecessary copying of property strings.
st,lsm6dsx
- Improve docs, particularly wrt to making addition of new device
  support less noisy.
st,lsm9ds0
- Use dev_err_probe() in all probe() error handling.
- Improved header includes.
- Tidy up termination of ID tables.
ti,ads1014
- Correct upper bound on PGA (wrong value had no actual impact)
ti,afe4403/4404
- devm_ useage to simplify error handling in probe() and allow() remove to
  be dropped.
voltage-divider
- Add dt-binding for io-channel-cells to allow such a device to be both
  an IIO consumer and IIO producer at the same time.

* tag 'iio-for-6.9a' of http://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio: (106 commits)
  iio: imu: bmi323: Add ACPI Match Table
  iio: accel: bmc150: Document duplicate ACPI entries with bmi323 driver
  iio: adc: ti-ads1298: Add driver
  dt-bindings: iio: adc: ti-ads1298: Add bindings
  iio: pressure: hsc030pa add triggered buffer
  iio: pressure: hsc030pa add mandatory delay
  iio: pressure: hsc030pa: update datasheet URLs
  iio: pressure: hsc030pa: include cleanup
  iio: pressure: hsc030pa: use signed type to hold div_64() result
  dt-bindings: iio: pressure: honeywell,hsc030pa.yaml add spi props
  iio: st_sensors: lsm9ds0: Use common style for terminator in ID tables
  iio: st_sensors: lsm9ds0: Don't use "proxy" headers
  iio: st_sensors: lsm9ds0: Use dev_err_probe() everywhere
  iio: adc: adi-axi-adc: move to backend framework
  iio: adc: ad9467: convert to backend framework
  iio: add the IIO backend framework
  iio: buffer-dmaengine: export buffer alloc and free functions
  of: property: add device link support for io-backends
  dt-bindings: adc: axi-adc: update bindings for backend framework
  dt-bindings: adc: ad9467: add new io-backend property
  ...
2024-02-25 14:11:41 +01:00

647 lines
16 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// SPDX-License-Identifier: GPL-2.0+
/*
* hdc3020.c - Support for the TI HDC3020,HDC3021 and HDC3022
* temperature + relative humidity sensors
*
* Copyright (C) 2023
*
* Copyright (C) 2024 Liebherr-Electronics and Drives GmbH
*
* Datasheet: https://www.ti.com/lit/ds/symlink/hdc3020.pdf
*/
#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <linux/cleanup.h>
#include <linux/crc8.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/units.h>
#include <asm/unaligned.h>
#include <linux/iio/events.h>
#include <linux/iio/iio.h>
#define HDC3020_S_AUTO_10HZ_MOD0 0x2737
#define HDC3020_S_STATUS 0x3041
#define HDC3020_HEATER_DISABLE 0x3066
#define HDC3020_HEATER_ENABLE 0x306D
#define HDC3020_HEATER_CONFIG 0x306E
#define HDC3020_EXIT_AUTO 0x3093
#define HDC3020_S_T_RH_THRESH_LOW 0x6100
#define HDC3020_S_T_RH_THRESH_LOW_CLR 0x610B
#define HDC3020_S_T_RH_THRESH_HIGH_CLR 0x6116
#define HDC3020_S_T_RH_THRESH_HIGH 0x611D
#define HDC3020_R_T_RH_AUTO 0xE000
#define HDC3020_R_T_LOW_AUTO 0xE002
#define HDC3020_R_T_HIGH_AUTO 0xE003
#define HDC3020_R_RH_LOW_AUTO 0xE004
#define HDC3020_R_RH_HIGH_AUTO 0xE005
#define HDC3020_R_T_RH_THRESH_LOW 0xE102
#define HDC3020_R_T_RH_THRESH_LOW_CLR 0xE109
#define HDC3020_R_T_RH_THRESH_HIGH_CLR 0xE114
#define HDC3020_R_T_RH_THRESH_HIGH 0xE11F
#define HDC3020_R_STATUS 0xF32D
#define HDC3020_THRESH_TEMP_MASK GENMASK(8, 0)
#define HDC3020_THRESH_TEMP_TRUNC_SHIFT 7
#define HDC3020_THRESH_HUM_MASK GENMASK(15, 9)
#define HDC3020_THRESH_HUM_TRUNC_SHIFT 9
#define HDC3020_STATUS_T_LOW_ALERT BIT(6)
#define HDC3020_STATUS_T_HIGH_ALERT BIT(7)
#define HDC3020_STATUS_RH_LOW_ALERT BIT(8)
#define HDC3020_STATUS_RH_HIGH_ALERT BIT(9)
#define HDC3020_READ_RETRY_TIMES 10
#define HDC3020_BUSY_DELAY_MS 10
#define HDC3020_CRC8_POLYNOMIAL 0x31
#define HDC3020_MIN_TEMP -40
#define HDC3020_MAX_TEMP 125
struct hdc3020_data {
struct i2c_client *client;
/*
* Ensure that the sensor configuration (currently only heater is
* supported) will not be changed during the process of reading
* sensor data (this driver will try HDC3020_READ_RETRY_TIMES times
* if the device does not respond).
*/
struct mutex lock;
};
static const int hdc3020_heater_vals[] = {0, 1, 0x3FFF};
static const struct iio_event_spec hdc3020_t_rh_event[] = {
{
.type = IIO_EV_TYPE_THRESH,
.dir = IIO_EV_DIR_RISING,
.mask_separate = BIT(IIO_EV_INFO_VALUE) |
BIT(IIO_EV_INFO_HYSTERESIS),
},
{
.type = IIO_EV_TYPE_THRESH,
.dir = IIO_EV_DIR_FALLING,
.mask_separate = BIT(IIO_EV_INFO_VALUE) |
BIT(IIO_EV_INFO_HYSTERESIS),
},
};
static const struct iio_chan_spec hdc3020_channels[] = {
{
.type = IIO_TEMP,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_PEAK) |
BIT(IIO_CHAN_INFO_TROUGH) | BIT(IIO_CHAN_INFO_OFFSET),
.event_spec = hdc3020_t_rh_event,
.num_event_specs = ARRAY_SIZE(hdc3020_t_rh_event),
},
{
.type = IIO_HUMIDITYRELATIVE,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_PEAK) |
BIT(IIO_CHAN_INFO_TROUGH),
.event_spec = hdc3020_t_rh_event,
.num_event_specs = ARRAY_SIZE(hdc3020_t_rh_event),
},
{
/*
* For setting the internal heater, which can be switched on to
* prevent or remove any condensation that may develop when the
* ambient environment approaches its dew point temperature.
*/
.type = IIO_CURRENT,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
.info_mask_separate_available = BIT(IIO_CHAN_INFO_RAW),
.output = 1,
},
};
DECLARE_CRC8_TABLE(hdc3020_crc8_table);
static int hdc3020_write_bytes(struct hdc3020_data *data, u8 *buf, u8 len)
{
struct i2c_client *client = data->client;
struct i2c_msg msg;
int ret, cnt;
msg.addr = client->addr;
msg.flags = 0;
msg.buf = buf;
msg.len = len;
/*
* During the measurement process, HDC3020 will not return data.
* So wait for a while and try again
*/
for (cnt = 0; cnt < HDC3020_READ_RETRY_TIMES; cnt++) {
ret = i2c_transfer(client->adapter, &msg, 1);
if (ret == 1)
return 0;
mdelay(HDC3020_BUSY_DELAY_MS);
}
dev_err(&client->dev, "Could not write sensor command\n");
return -ETIMEDOUT;
}
static
int hdc3020_read_bytes(struct hdc3020_data *data, u16 reg, u8 *buf, int len)
{
u8 reg_buf[2];
int ret, cnt;
struct i2c_client *client = data->client;
struct i2c_msg msg[2] = {
[0] = {
.addr = client->addr,
.flags = 0,
.buf = reg_buf,
.len = 2,
},
[1] = {
.addr = client->addr,
.flags = I2C_M_RD,
.buf = buf,
.len = len,
},
};
put_unaligned_be16(reg, reg_buf);
/*
* During the measurement process, HDC3020 will not return data.
* So wait for a while and try again
*/
for (cnt = 0; cnt < HDC3020_READ_RETRY_TIMES; cnt++) {
ret = i2c_transfer(client->adapter, msg, 2);
if (ret == 2)
return 0;
mdelay(HDC3020_BUSY_DELAY_MS);
}
dev_err(&client->dev, "Could not read sensor data\n");
return -ETIMEDOUT;
}
static int hdc3020_read_be16(struct hdc3020_data *data, u16 reg)
{
u8 crc, buf[3];
int ret;
ret = hdc3020_read_bytes(data, reg, buf, 3);
if (ret < 0)
return ret;
crc = crc8(hdc3020_crc8_table, buf, 2, CRC8_INIT_VALUE);
if (crc != buf[2])
return -EINVAL;
return get_unaligned_be16(buf);
}
static int hdc3020_exec_cmd(struct hdc3020_data *data, u16 reg)
{
u8 reg_buf[2];
put_unaligned_be16(reg, reg_buf);
return hdc3020_write_bytes(data, reg_buf, 2);
}
static int hdc3020_read_measurement(struct hdc3020_data *data,
enum iio_chan_type type, int *val)
{
u8 crc, buf[6];
int ret;
ret = hdc3020_read_bytes(data, HDC3020_R_T_RH_AUTO, buf, 6);
if (ret < 0)
return ret;
/* CRC check of the temperature measurement */
crc = crc8(hdc3020_crc8_table, buf, 2, CRC8_INIT_VALUE);
if (crc != buf[2])
return -EINVAL;
/* CRC check of the relative humidity measurement */
crc = crc8(hdc3020_crc8_table, buf + 3, 2, CRC8_INIT_VALUE);
if (crc != buf[5])
return -EINVAL;
if (type == IIO_TEMP)
*val = get_unaligned_be16(buf);
else if (type == IIO_HUMIDITYRELATIVE)
*val = get_unaligned_be16(&buf[3]);
else
return -EINVAL;
return 0;
}
static int hdc3020_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val,
int *val2, long mask)
{
struct hdc3020_data *data = iio_priv(indio_dev);
int ret;
if (chan->type != IIO_TEMP && chan->type != IIO_HUMIDITYRELATIVE)
return -EINVAL;
switch (mask) {
case IIO_CHAN_INFO_RAW: {
guard(mutex)(&data->lock);
ret = hdc3020_read_measurement(data, chan->type, val);
if (ret < 0)
return ret;
return IIO_VAL_INT;
}
case IIO_CHAN_INFO_PEAK: {
guard(mutex)(&data->lock);
if (chan->type == IIO_TEMP)
ret = hdc3020_read_be16(data, HDC3020_R_T_HIGH_AUTO);
else
ret = hdc3020_read_be16(data, HDC3020_R_RH_HIGH_AUTO);
if (ret < 0)
return ret;
*val = ret;
return IIO_VAL_INT;
}
case IIO_CHAN_INFO_TROUGH: {
guard(mutex)(&data->lock);
if (chan->type == IIO_TEMP)
ret = hdc3020_read_be16(data, HDC3020_R_T_LOW_AUTO);
else
ret = hdc3020_read_be16(data, HDC3020_R_RH_LOW_AUTO);
if (ret < 0)
return ret;
*val = ret;
return IIO_VAL_INT;
}
case IIO_CHAN_INFO_SCALE:
*val2 = 65536;
if (chan->type == IIO_TEMP)
*val = 175;
else
*val = 100;
return IIO_VAL_FRACTIONAL;
case IIO_CHAN_INFO_OFFSET:
if (chan->type != IIO_TEMP)
return -EINVAL;
*val = -16852;
return IIO_VAL_INT;
default:
return -EINVAL;
}
}
static int hdc3020_read_available(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
const int **vals,
int *type, int *length, long mask)
{
if (mask != IIO_CHAN_INFO_RAW || chan->type != IIO_CURRENT)
return -EINVAL;
*vals = hdc3020_heater_vals;
*type = IIO_VAL_INT;
return IIO_AVAIL_RANGE;
}
static int hdc3020_update_heater(struct hdc3020_data *data, int val)
{
u8 buf[5];
int ret;
if (val < hdc3020_heater_vals[0] || val > hdc3020_heater_vals[2])
return -EINVAL;
if (!val)
hdc3020_exec_cmd(data, HDC3020_HEATER_DISABLE);
put_unaligned_be16(HDC3020_HEATER_CONFIG, buf);
put_unaligned_be16(val & GENMASK(13, 0), &buf[2]);
buf[4] = crc8(hdc3020_crc8_table, buf + 2, 2, CRC8_INIT_VALUE);
ret = hdc3020_write_bytes(data, buf, 5);
if (ret < 0)
return ret;
return hdc3020_exec_cmd(data, HDC3020_HEATER_ENABLE);
}
static int hdc3020_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
struct hdc3020_data *data = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_RAW:
if (chan->type != IIO_CURRENT)
return -EINVAL;
guard(mutex)(&data->lock);
return hdc3020_update_heater(data, val);
}
return -EINVAL;
}
static int hdc3020_write_thresh(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
enum iio_event_type type,
enum iio_event_direction dir,
enum iio_event_info info,
int val, int val2)
{
struct hdc3020_data *data = iio_priv(indio_dev);
u8 buf[5];
u64 tmp;
u16 reg;
int ret;
/* Supported temperature range is from 40 to 125 degree celsius */
if (val < HDC3020_MIN_TEMP || val > HDC3020_MAX_TEMP)
return -EINVAL;
/* Select threshold register */
if (info == IIO_EV_INFO_VALUE) {
if (dir == IIO_EV_DIR_RISING)
reg = HDC3020_S_T_RH_THRESH_HIGH;
else
reg = HDC3020_S_T_RH_THRESH_LOW;
} else {
if (dir == IIO_EV_DIR_RISING)
reg = HDC3020_S_T_RH_THRESH_HIGH_CLR;
else
reg = HDC3020_S_T_RH_THRESH_LOW_CLR;
}
guard(mutex)(&data->lock);
ret = hdc3020_read_be16(data, reg);
if (ret < 0)
return ret;
switch (chan->type) {
case IIO_TEMP:
/*
* Calculate temperature threshold, shift it down to get the
* truncated threshold representation in the 9LSBs while keeping
* the current humidity threshold in the 7 MSBs.
*/
tmp = ((u64)(((val + 45) * MICRO) + val2)) * 65535ULL;
tmp = div_u64(tmp, MICRO * 175);
val = tmp >> HDC3020_THRESH_TEMP_TRUNC_SHIFT;
val = FIELD_PREP(HDC3020_THRESH_TEMP_MASK, val);
val |= (FIELD_GET(HDC3020_THRESH_HUM_MASK, ret) <<
HDC3020_THRESH_HUM_TRUNC_SHIFT);
break;
case IIO_HUMIDITYRELATIVE:
/*
* Calculate humidity threshold, shift it down and up to get the
* truncated threshold representation in the 7MSBs while keeping
* the current temperature threshold in the 9 LSBs.
*/
tmp = ((u64)((val * MICRO) + val2)) * 65535ULL;
tmp = div_u64(tmp, MICRO * 100);
val = tmp >> HDC3020_THRESH_HUM_TRUNC_SHIFT;
val = FIELD_PREP(HDC3020_THRESH_HUM_MASK, val);
val |= FIELD_GET(HDC3020_THRESH_TEMP_MASK, ret);
break;
default:
return -EOPNOTSUPP;
}
put_unaligned_be16(reg, buf);
put_unaligned_be16(val, buf + 2);
buf[4] = crc8(hdc3020_crc8_table, buf + 2, 2, CRC8_INIT_VALUE);
return hdc3020_write_bytes(data, buf, 5);
}
static int hdc3020_read_thresh(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
enum iio_event_type type,
enum iio_event_direction dir,
enum iio_event_info info,
int *val, int *val2)
{
struct hdc3020_data *data = iio_priv(indio_dev);
u16 reg;
int ret;
/* Select threshold register */
if (info == IIO_EV_INFO_VALUE) {
if (dir == IIO_EV_DIR_RISING)
reg = HDC3020_R_T_RH_THRESH_HIGH;
else
reg = HDC3020_R_T_RH_THRESH_LOW;
} else {
if (dir == IIO_EV_DIR_RISING)
reg = HDC3020_R_T_RH_THRESH_HIGH_CLR;
else
reg = HDC3020_R_T_RH_THRESH_LOW_CLR;
}
guard(mutex)(&data->lock);
ret = hdc3020_read_be16(data, reg);
if (ret < 0)
return ret;
switch (chan->type) {
case IIO_TEMP:
/*
* Get the temperature threshold from 9 LSBs, shift them to get
* the truncated temperature threshold representation and
* calculate the threshold according to the formula in the
* datasheet.
*/
*val = FIELD_GET(HDC3020_THRESH_TEMP_MASK, ret);
*val = *val << HDC3020_THRESH_TEMP_TRUNC_SHIFT;
*val = -2949075 + (175 * (*val));
*val2 = 65535;
return IIO_VAL_FRACTIONAL;
case IIO_HUMIDITYRELATIVE:
/*
* Get the humidity threshold from 7 MSBs, shift them to get the
* truncated humidity threshold representation and calculate the
* threshold according to the formula in the datasheet.
*/
*val = FIELD_GET(HDC3020_THRESH_HUM_MASK, ret);
*val = (*val << HDC3020_THRESH_HUM_TRUNC_SHIFT) * 100;
*val2 = 65535;
return IIO_VAL_FRACTIONAL;
default:
return -EOPNOTSUPP;
}
}
static irqreturn_t hdc3020_interrupt_handler(int irq, void *private)
{
struct iio_dev *indio_dev = private;
struct hdc3020_data *data;
s64 time;
int ret;
data = iio_priv(indio_dev);
ret = hdc3020_read_be16(data, HDC3020_R_STATUS);
if (ret < 0)
return IRQ_HANDLED;
if (!(ret & (HDC3020_STATUS_T_HIGH_ALERT | HDC3020_STATUS_T_LOW_ALERT |
HDC3020_STATUS_RH_HIGH_ALERT | HDC3020_STATUS_RH_LOW_ALERT)))
return IRQ_NONE;
time = iio_get_time_ns(indio_dev);
if (ret & HDC3020_STATUS_T_HIGH_ALERT)
iio_push_event(indio_dev,
IIO_MOD_EVENT_CODE(IIO_TEMP, 0,
IIO_NO_MOD,
IIO_EV_TYPE_THRESH,
IIO_EV_DIR_RISING),
time);
if (ret & HDC3020_STATUS_T_LOW_ALERT)
iio_push_event(indio_dev,
IIO_MOD_EVENT_CODE(IIO_TEMP, 0,
IIO_NO_MOD,
IIO_EV_TYPE_THRESH,
IIO_EV_DIR_FALLING),
time);
if (ret & HDC3020_STATUS_RH_HIGH_ALERT)
iio_push_event(indio_dev,
IIO_MOD_EVENT_CODE(IIO_HUMIDITYRELATIVE, 0,
IIO_NO_MOD,
IIO_EV_TYPE_THRESH,
IIO_EV_DIR_RISING),
time);
if (ret & HDC3020_STATUS_RH_LOW_ALERT)
iio_push_event(indio_dev,
IIO_MOD_EVENT_CODE(IIO_HUMIDITYRELATIVE, 0,
IIO_NO_MOD,
IIO_EV_TYPE_THRESH,
IIO_EV_DIR_FALLING),
time);
return IRQ_HANDLED;
}
static const struct iio_info hdc3020_info = {
.read_raw = hdc3020_read_raw,
.write_raw = hdc3020_write_raw,
.read_avail = hdc3020_read_available,
.read_event_value = hdc3020_read_thresh,
.write_event_value = hdc3020_write_thresh,
};
static void hdc3020_stop(void *data)
{
hdc3020_exec_cmd((struct hdc3020_data *)data, HDC3020_EXIT_AUTO);
}
static int hdc3020_probe(struct i2c_client *client)
{
struct iio_dev *indio_dev;
struct hdc3020_data *data;
int ret;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
return -EOPNOTSUPP;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
data = iio_priv(indio_dev);
data->client = client;
mutex_init(&data->lock);
crc8_populate_msb(hdc3020_crc8_table, HDC3020_CRC8_POLYNOMIAL);
indio_dev->name = "hdc3020";
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->info = &hdc3020_info;
indio_dev->channels = hdc3020_channels;
indio_dev->num_channels = ARRAY_SIZE(hdc3020_channels);
if (client->irq) {
ret = devm_request_threaded_irq(&client->dev, client->irq,
NULL, hdc3020_interrupt_handler,
IRQF_ONESHOT, "hdc3020",
indio_dev);
if (ret)
return dev_err_probe(&client->dev, ret,
"Failed to request IRQ\n");
/*
* The alert output is activated by default upon power up,
* hardware reset, and soft reset. Clear the status register.
*/
ret = hdc3020_exec_cmd(data, HDC3020_S_STATUS);
if (ret)
return ret;
}
ret = hdc3020_exec_cmd(data, HDC3020_S_AUTO_10HZ_MOD0);
if (ret)
return dev_err_probe(&client->dev, ret,
"Unable to set up measurement\n");
ret = devm_add_action_or_reset(&data->client->dev, hdc3020_stop, data);
if (ret)
return ret;
ret = devm_iio_device_register(&data->client->dev, indio_dev);
if (ret)
return dev_err_probe(&client->dev, ret, "Failed to add device");
return 0;
}
static const struct i2c_device_id hdc3020_id[] = {
{ "hdc3020" },
{ "hdc3021" },
{ "hdc3022" },
{ }
};
MODULE_DEVICE_TABLE(i2c, hdc3020_id);
static const struct of_device_id hdc3020_dt_ids[] = {
{ .compatible = "ti,hdc3020" },
{ .compatible = "ti,hdc3021" },
{ .compatible = "ti,hdc3022" },
{ }
};
MODULE_DEVICE_TABLE(of, hdc3020_dt_ids);
static struct i2c_driver hdc3020_driver = {
.driver = {
.name = "hdc3020",
.of_match_table = hdc3020_dt_ids,
},
.probe = hdc3020_probe,
.id_table = hdc3020_id,
};
module_i2c_driver(hdc3020_driver);
MODULE_AUTHOR("Javier Carrasco <javier.carrasco.cruz@gmail.com>");
MODULE_AUTHOR("Li peiyu <579lpy@gmail.com>");
MODULE_DESCRIPTION("TI HDC3020 humidity and temperature sensor driver");
MODULE_LICENSE("GPL");