cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

hdc2010.c (8583B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 * hdc2010.c - Support for the TI HDC2010 and HDC2080
      4 * temperature + relative humidity sensors
      5 *
      6 * Copyright (C) 2020 Norphonic AS
      7 * Author: Eugene Zaikonnikov <ez@norphonic.com>
      8 *
      9 * Datasheet: https://www.ti.com/product/HDC2010/datasheet
     10 * Datasheet: https://www.ti.com/product/HDC2080/datasheet
     11 */
     12
     13#include <linux/module.h>
     14#include <linux/init.h>
     15#include <linux/i2c.h>
     16#include <linux/bitops.h>
     17
     18#include <linux/iio/iio.h>
     19#include <linux/iio/sysfs.h>
     20
     21#define HDC2010_REG_TEMP_LOW			0x00
     22#define HDC2010_REG_TEMP_HIGH			0x01
     23#define HDC2010_REG_HUMIDITY_LOW		0x02
     24#define HDC2010_REG_HUMIDITY_HIGH		0x03
     25#define HDC2010_REG_INTERRUPT_DRDY		0x04
     26#define HDC2010_REG_TEMP_MAX			0x05
     27#define HDC2010_REG_HUMIDITY_MAX		0x06
     28#define HDC2010_REG_INTERRUPT_EN		0x07
     29#define HDC2010_REG_TEMP_OFFSET_ADJ		0x08
     30#define HDC2010_REG_HUMIDITY_OFFSET_ADJ		0x09
     31#define HDC2010_REG_TEMP_THR_L			0x0a
     32#define HDC2010_REG_TEMP_THR_H			0x0b
     33#define HDC2010_REG_RH_THR_L			0x0c
     34#define HDC2010_REG_RH_THR_H			0x0d
     35#define HDC2010_REG_RESET_DRDY_INT_CONF		0x0e
     36#define HDC2010_REG_MEASUREMENT_CONF		0x0f
     37
     38#define HDC2010_MEAS_CONF			GENMASK(2, 1)
     39#define HDC2010_MEAS_TRIG			BIT(0)
     40#define HDC2010_HEATER_EN			BIT(3)
     41#define HDC2010_AMM				GENMASK(6, 4)
     42
     43struct hdc2010_data {
     44	struct i2c_client *client;
     45	struct mutex lock;
     46	u8 measurement_config;
     47	u8 interrupt_config;
     48	u8 drdy_config;
     49};
     50
     51enum hdc2010_addr_groups {
     52	HDC2010_GROUP_TEMP = 0,
     53	HDC2010_GROUP_HUMIDITY,
     54};
     55
     56struct hdc2010_reg_record {
     57	unsigned long primary;
     58	unsigned long peak;
     59};
     60
     61static const struct hdc2010_reg_record hdc2010_reg_translation[] = {
     62	[HDC2010_GROUP_TEMP] = {
     63		.primary = HDC2010_REG_TEMP_LOW,
     64		.peak = HDC2010_REG_TEMP_MAX,
     65	},
     66	[HDC2010_GROUP_HUMIDITY] = {
     67		.primary = HDC2010_REG_HUMIDITY_LOW,
     68		.peak = HDC2010_REG_HUMIDITY_MAX,
     69	},
     70};
     71
     72static IIO_CONST_ATTR(out_current_heater_raw_available, "0 1");
     73
     74static struct attribute *hdc2010_attributes[] = {
     75	&iio_const_attr_out_current_heater_raw_available.dev_attr.attr,
     76	NULL
     77};
     78
     79static const struct attribute_group hdc2010_attribute_group = {
     80	.attrs = hdc2010_attributes,
     81};
     82
     83static const struct iio_chan_spec hdc2010_channels[] = {
     84	{
     85		.type = IIO_TEMP,
     86		.address = HDC2010_GROUP_TEMP,
     87		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
     88			BIT(IIO_CHAN_INFO_PEAK) |
     89			BIT(IIO_CHAN_INFO_OFFSET) |
     90			BIT(IIO_CHAN_INFO_SCALE),
     91	},
     92	{
     93		.type = IIO_HUMIDITYRELATIVE,
     94		.address = HDC2010_GROUP_HUMIDITY,
     95		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
     96			BIT(IIO_CHAN_INFO_PEAK) |
     97			BIT(IIO_CHAN_INFO_SCALE),
     98	},
     99	{
    100		.type = IIO_CURRENT,
    101		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
    102		.extend_name = "heater",
    103		.output = 1,
    104	},
    105};
    106
    107static int hdc2010_update_drdy_config(struct hdc2010_data *data,
    108					     char mask, char val)
    109{
    110	u8 tmp = (~mask & data->drdy_config) | val;
    111	int ret;
    112
    113	ret = i2c_smbus_write_byte_data(data->client,
    114					HDC2010_REG_RESET_DRDY_INT_CONF, tmp);
    115	if (ret)
    116		return ret;
    117
    118	data->drdy_config = tmp;
    119
    120	return 0;
    121}
    122
    123static int hdc2010_get_prim_measurement_word(struct hdc2010_data *data,
    124					     struct iio_chan_spec const *chan)
    125{
    126	struct i2c_client *client = data->client;
    127	s32 ret;
    128
    129	ret = i2c_smbus_read_word_data(client,
    130			hdc2010_reg_translation[chan->address].primary);
    131
    132	if (ret < 0)
    133		dev_err(&client->dev, "Could not read sensor measurement word\n");
    134
    135	return ret;
    136}
    137
    138static int hdc2010_get_peak_measurement_byte(struct hdc2010_data *data,
    139					     struct iio_chan_spec const *chan)
    140{
    141	struct i2c_client *client = data->client;
    142	s32 ret;
    143
    144	ret = i2c_smbus_read_byte_data(client,
    145			hdc2010_reg_translation[chan->address].peak);
    146
    147	if (ret < 0)
    148		dev_err(&client->dev, "Could not read sensor measurement byte\n");
    149
    150	return ret;
    151}
    152
    153static int hdc2010_get_heater_status(struct hdc2010_data *data)
    154{
    155	return !!(data->drdy_config & HDC2010_HEATER_EN);
    156}
    157
    158static int hdc2010_read_raw(struct iio_dev *indio_dev,
    159			    struct iio_chan_spec const *chan, int *val,
    160			    int *val2, long mask)
    161{
    162	struct hdc2010_data *data = iio_priv(indio_dev);
    163
    164	switch (mask) {
    165	case IIO_CHAN_INFO_RAW: {
    166		int ret;
    167
    168		if (chan->type == IIO_CURRENT) {
    169			*val = hdc2010_get_heater_status(data);
    170			return IIO_VAL_INT;
    171		}
    172		ret = iio_device_claim_direct_mode(indio_dev);
    173		if (ret)
    174			return ret;
    175		mutex_lock(&data->lock);
    176		ret = hdc2010_get_prim_measurement_word(data, chan);
    177		mutex_unlock(&data->lock);
    178		iio_device_release_direct_mode(indio_dev);
    179		if (ret < 0)
    180			return ret;
    181		*val = ret;
    182		return IIO_VAL_INT;
    183	}
    184	case IIO_CHAN_INFO_PEAK: {
    185		int ret;
    186
    187		ret = iio_device_claim_direct_mode(indio_dev);
    188		if (ret)
    189			return ret;
    190		mutex_lock(&data->lock);
    191		ret = hdc2010_get_peak_measurement_byte(data, chan);
    192		mutex_unlock(&data->lock);
    193		iio_device_release_direct_mode(indio_dev);
    194		if (ret < 0)
    195			return ret;
    196		/* Scaling up the value so we can use same offset as RAW */
    197		*val = ret * 256;
    198		return IIO_VAL_INT;
    199	}
    200	case IIO_CHAN_INFO_SCALE:
    201		*val2 = 65536;
    202		if (chan->type == IIO_TEMP)
    203			*val = 165000;
    204		else
    205			*val = 100000;
    206		return IIO_VAL_FRACTIONAL;
    207	case IIO_CHAN_INFO_OFFSET:
    208		*val = -15887;
    209		*val2 = 515151;
    210		return IIO_VAL_INT_PLUS_MICRO;
    211	default:
    212		return -EINVAL;
    213	}
    214}
    215
    216static int hdc2010_write_raw(struct iio_dev *indio_dev,
    217			     struct iio_chan_spec const *chan,
    218			     int val, int val2, long mask)
    219{
    220	struct hdc2010_data *data = iio_priv(indio_dev);
    221	int new, ret;
    222
    223	switch (mask) {
    224	case IIO_CHAN_INFO_RAW:
    225		if (chan->type != IIO_CURRENT || val2 != 0)
    226			return -EINVAL;
    227
    228		switch (val) {
    229		case 1:
    230			new = HDC2010_HEATER_EN;
    231			break;
    232		case 0:
    233			new = 0;
    234			break;
    235		default:
    236			return -EINVAL;
    237		}
    238
    239		mutex_lock(&data->lock);
    240		ret = hdc2010_update_drdy_config(data, HDC2010_HEATER_EN, new);
    241		mutex_unlock(&data->lock);
    242		return ret;
    243	default:
    244		return -EINVAL;
    245	}
    246}
    247
    248static const struct iio_info hdc2010_info = {
    249	.read_raw = hdc2010_read_raw,
    250	.write_raw = hdc2010_write_raw,
    251	.attrs = &hdc2010_attribute_group,
    252};
    253
    254static int hdc2010_probe(struct i2c_client *client,
    255			 const struct i2c_device_id *id)
    256{
    257	struct iio_dev *indio_dev;
    258	struct hdc2010_data *data;
    259	u8 tmp;
    260	int ret;
    261
    262	if (!i2c_check_functionality(client->adapter,
    263	    I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BYTE | I2C_FUNC_I2C))
    264		return -EOPNOTSUPP;
    265
    266	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
    267	if (!indio_dev)
    268		return -ENOMEM;
    269
    270	data = iio_priv(indio_dev);
    271	i2c_set_clientdata(client, indio_dev);
    272	data->client = client;
    273	mutex_init(&data->lock);
    274
    275	/*
    276	 * As DEVICE ID register does not differentiate between
    277	 * HDC2010 and HDC2080, we have the name hardcoded
    278	 */
    279	indio_dev->name = "hdc2010";
    280	indio_dev->modes = INDIO_DIRECT_MODE;
    281	indio_dev->info = &hdc2010_info;
    282
    283	indio_dev->channels = hdc2010_channels;
    284	indio_dev->num_channels = ARRAY_SIZE(hdc2010_channels);
    285
    286	/* Enable Automatic Measurement Mode at 5Hz */
    287	ret = hdc2010_update_drdy_config(data, HDC2010_AMM, HDC2010_AMM);
    288	if (ret)
    289		return ret;
    290
    291	/*
    292	 * We enable both temp and humidity measurement.
    293	 * However the measurement won't start even in AMM until triggered.
    294	 */
    295	tmp = (data->measurement_config & ~HDC2010_MEAS_CONF) |
    296		HDC2010_MEAS_TRIG;
    297
    298	ret = i2c_smbus_write_byte_data(client, HDC2010_REG_MEASUREMENT_CONF, tmp);
    299	if (ret) {
    300		dev_warn(&client->dev, "Unable to set up measurement\n");
    301		if (hdc2010_update_drdy_config(data, HDC2010_AMM, 0))
    302			dev_warn(&client->dev, "Unable to restore default AMM\n");
    303		return ret;
    304	}
    305
    306	data->measurement_config = tmp;
    307
    308	return iio_device_register(indio_dev);
    309}
    310
    311static int hdc2010_remove(struct i2c_client *client)
    312{
    313	struct iio_dev *indio_dev = i2c_get_clientdata(client);
    314	struct hdc2010_data *data = iio_priv(indio_dev);
    315
    316	iio_device_unregister(indio_dev);
    317
    318	/* Disable Automatic Measurement Mode */
    319	if (hdc2010_update_drdy_config(data, HDC2010_AMM, 0))
    320		dev_warn(&client->dev, "Unable to restore default AMM\n");
    321
    322	return 0;
    323}
    324
    325static const struct i2c_device_id hdc2010_id[] = {
    326	{ "hdc2010" },
    327	{ "hdc2080" },
    328	{ }
    329};
    330MODULE_DEVICE_TABLE(i2c, hdc2010_id);
    331
    332static const struct of_device_id hdc2010_dt_ids[] = {
    333	{ .compatible = "ti,hdc2010" },
    334	{ .compatible = "ti,hdc2080" },
    335	{ }
    336};
    337MODULE_DEVICE_TABLE(of, hdc2010_dt_ids);
    338
    339static struct i2c_driver hdc2010_driver = {
    340	.driver = {
    341		.name	= "hdc2010",
    342		.of_match_table = hdc2010_dt_ids,
    343	},
    344	.probe = hdc2010_probe,
    345	.remove = hdc2010_remove,
    346	.id_table = hdc2010_id,
    347};
    348module_i2c_driver(hdc2010_driver);
    349
    350MODULE_AUTHOR("Eugene Zaikonnikov <ez@norphonic.com>");
    351MODULE_DESCRIPTION("TI HDC2010 humidity and temperature sensor driver");
    352MODULE_LICENSE("GPL");