// SPDX-License-Identifier: GPL-2.0-or-later /* Sensirion SHT3x-DIS humidity and temperature sensor driver. * The SHT3x comes in many different versions, this driver is for the * I2C version only. * * Copyright (C) 2016 Sensirion AG, Switzerland * Author: David Frey <david.frey@sensirion.com> * Author: Pascal Sachs <pascal.sachs@sensirion.com>
*/
/* delays for single-shot mode i2c commands, both in us */ #define SHT3X_SINGLE_WAIT_TIME_HPM 15000 #define SHT3X_SINGLE_WAIT_TIME_MPM 6000 #define SHT3X_SINGLE_WAIT_TIME_LPM 4000
struct sht3x_data { struct i2c_client *client; enum sht3x_chips chip_id; struct mutex i2c_lock; /* lock for sending i2c commands */ struct mutex data_lock; /* lock for updating driver data */
u8 mode; constunsignedchar *command;
u32 wait_time; /* in us*/ unsignedlong last_update; /* last update in periodic mode*/ enum sht3x_repeatability repeatability;
u32 serial_number;
/* * cached values for temperature and humidity and limits * the limits arrays have the following order: * max, max_hyst, min, min_hyst
*/ int temperature; int temperature_limits[SHT3X_NUM_LIMIT_CMD];
u32 humidity;
u32 humidity_limits[SHT3X_NUM_LIMIT_CMD];
};
mutex_lock(&data->data_lock); /* * Only update cached readings once per update interval in periodic * mode. In single shot mode the sensor measures values on demand, so * every time the sysfs interface is called, a measurement is triggered. * In periodic mode however, the measurement process is handled * internally by the sensor and reading out sensor values only makes * sense if a new reading is available.
*/ if (time_after(jiffies, data->last_update + interval_jiffies)) {
ret = sht3x_read_from_command(client, data, data->command, buf, sizeof(buf), data->wait_time); if (ret) goto out;
val = be16_to_cpup((__be16 *)buf);
data->temperature = sht3x_extract_temperature(val);
val = be16_to_cpup((__be16 *)(buf + 3));
data->humidity = sht3x_extract_humidity(val);
data->last_update = jiffies;
}
out:
mutex_unlock(&data->data_lock); if (ret) return ERR_PTR(ret);
/* * limits_update must only be called from probe or with data_lock held
*/ staticint limits_update(struct sht3x_data *data)
{ int ret;
u8 index; int temperature;
u32 humidity;
u16 raw; char buffer[SHT3X_RESPONSE_LENGTH]; conststruct sht3x_limit_commands *commands; struct i2c_client *client = data->client;
for (index = 0; index < SHT3X_NUM_LIMIT_CMD; index++) {
commands = &limit_commands[index];
ret = sht3x_read_from_command(client, data,
commands->read_command, buffer,
SHT3X_RESPONSE_LENGTH, 0);
ret = kstrtobool(buf, &status); if (ret) return ret;
mutex_lock(&data->i2c_lock);
if (status)
ret = i2c_master_send(client, (char *)&sht3x_cmd_heater_on,
SHT3X_CMD_LENGTH); else
ret = i2c_master_send(client, (char *)&sht3x_cmd_heater_off,
SHT3X_CMD_LENGTH);
mutex_lock(&data->data_lock); /* mode did not change */ if (mode == data->mode) {
mutex_unlock(&data->data_lock); return 0;
}
mutex_lock(&data->i2c_lock); /* * Abort periodic measure mode. * To do any changes to the configuration while in periodic mode, we * have to send a break command to the sensor, which then falls back * to single shot (mode = 0).
*/ if (data->mode > 0) {
ret = i2c_master_send(client, sht3x_cmd_break,
SHT3X_CMD_LENGTH); if (ret != SHT3X_CMD_LENGTH) goto out;
data->mode = 0;
}
switch (type) { case hwmon_chip: switch (attr) { case hwmon_chip_update_interval: return 0644; default: break;
} break; case hwmon_temp: switch (attr) { case hwmon_temp_input: case hwmon_temp_alarm: return 0444; case hwmon_temp_max: case hwmon_temp_max_hyst: case hwmon_temp_min: case hwmon_temp_min_hyst: return 0644; default: break;
} break; case hwmon_humidity: if (chip_data->chip_id == sts3x) break; switch (attr) { case hwmon_humidity_input: case hwmon_humidity_alarm: return 0444; case hwmon_humidity_max: case hwmon_humidity_max_hyst: case hwmon_humidity_min: case hwmon_humidity_min_hyst: return 0644; default: break;
} break; default: break;
}
return 0;
}
staticint sht3x_read(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long *val)
{ enum sht3x_limits index; int ret;
switch (type) { case hwmon_chip: switch (attr) { case hwmon_chip_update_interval:
*val = update_interval_read(dev); break; default: return -EOPNOTSUPP;
} break; case hwmon_temp: switch (attr) { case hwmon_temp_input: return temp1_input_read(dev, val); case hwmon_temp_alarm:
ret = temp1_alarm_read(dev); if (ret < 0) return ret;
*val = ret; break; case hwmon_temp_max:
index = limit_max;
*val = temp1_limit_read(dev, index); break; case hwmon_temp_max_hyst:
index = limit_max_hyst;
*val = temp1_limit_read(dev, index); break; case hwmon_temp_min:
index = limit_min;
*val = temp1_limit_read(dev, index); break; case hwmon_temp_min_hyst:
index = limit_min_hyst;
*val = temp1_limit_read(dev, index); break; default: return -EOPNOTSUPP;
} break; case hwmon_humidity: switch (attr) { case hwmon_humidity_input: return humidity1_input_read(dev, val); case hwmon_humidity_alarm:
ret = humidity1_alarm_read(dev); if (ret < 0) return ret;
*val = ret; break; case hwmon_humidity_max:
index = limit_max;
*val = humidity1_limit_read(dev, index); break; case hwmon_humidity_max_hyst:
index = limit_max_hyst;
*val = humidity1_limit_read(dev, index); break; case hwmon_humidity_min:
index = limit_min;
*val = humidity1_limit_read(dev, index); break; case hwmon_humidity_min_hyst:
index = limit_min_hyst;
*val = humidity1_limit_read(dev, index); break; default: return -EOPNOTSUPP;
} break; default: return -EOPNOTSUPP;
}
return 0;
}
staticint sht3x_write(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long val)
{ enum sht3x_limits index;
switch (type) { case hwmon_chip: switch (attr) { case hwmon_chip_update_interval: return update_interval_write(dev, val); default: return -EOPNOTSUPP;
} case hwmon_temp: switch (attr) { case hwmon_temp_max:
index = limit_max; break; case hwmon_temp_max_hyst:
index = limit_max_hyst; break; case hwmon_temp_min:
index = limit_min; break; case hwmon_temp_min_hyst:
index = limit_min_hyst; break; default: return -EOPNOTSUPP;
} return temp1_limit_write(dev, index, val); case hwmon_humidity: switch (attr) { case hwmon_humidity_max:
index = limit_max; break; case hwmon_humidity_max_hyst:
index = limit_max_hyst; break; case hwmon_humidity_min:
index = limit_min; break; case hwmon_humidity_min_hyst:
index = limit_min_hyst; break; default: return -EOPNOTSUPP;
} return humidity1_limit_write(dev, index, val); default: return -EOPNOTSUPP;
}
}
/* * we require full i2c support since the sht3x uses multi-byte read and * writes as well as multi-byte commands which are not supported by * the smbus protocol
*/ if (!i2c_check_functionality(adap, I2C_FUNC_I2C)) return -ENODEV;
ret = i2c_master_send(client, sht3x_cmd_clear_status_reg,
SHT3X_CMD_LENGTH); if (ret != SHT3X_CMD_LENGTH) return ret < 0 ? ret : -ENODEV;
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM;
/* * An attempt to read limits register too early * causes a NACK response from the chip. * Waiting for an empirical delay of 500 us solves the issue.
*/
usleep_range(500, 600);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.