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