Commit | Line | Data |
---|---|---|
d6ad8058 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
4839367d MR |
2 | /* |
3 | * hdc100x.c - Support for the TI HDC100x temperature + humidity sensors | |
4 | * | |
d6ad8058 MR |
5 | * Copyright (C) 2015, 2018 |
6 | * Author: Matt Ranostay <matt.ranostay@konsulko.com> | |
4839367d | 7 | * |
8f4c9575 MS |
8 | * Datasheets: |
9 | * http://www.ti.com/product/HDC1000/datasheet | |
10 | * http://www.ti.com/product/HDC1008/datasheet | |
11 | * http://www.ti.com/product/HDC1010/datasheet | |
12 | * http://www.ti.com/product/HDC1050/datasheet | |
13 | * http://www.ti.com/product/HDC1080/datasheet | |
4839367d MR |
14 | */ |
15 | ||
16 | #include <linux/delay.h> | |
17 | #include <linux/module.h> | |
18 | #include <linux/init.h> | |
19 | #include <linux/i2c.h> | |
20 | ||
21 | #include <linux/iio/iio.h> | |
22 | #include <linux/iio/sysfs.h> | |
16bf793f AS |
23 | #include <linux/iio/buffer.h> |
24 | #include <linux/iio/trigger_consumer.h> | |
25 | #include <linux/iio/triggered_buffer.h> | |
4839367d MR |
26 | |
27 | #define HDC100X_REG_TEMP 0x00 | |
28 | #define HDC100X_REG_HUMIDITY 0x01 | |
29 | ||
30 | #define HDC100X_REG_CONFIG 0x02 | |
16bf793f | 31 | #define HDC100X_REG_CONFIG_ACQ_MODE BIT(12) |
4839367d MR |
32 | #define HDC100X_REG_CONFIG_HEATER_EN BIT(13) |
33 | ||
34 | struct hdc100x_data { | |
35 | struct i2c_client *client; | |
36 | struct mutex lock; | |
37 | u16 config; | |
38 | ||
39 | /* integration time of the sensor */ | |
40 | int adc_int_us[2]; | |
41 | }; | |
42 | ||
43 | /* integration time in us */ | |
44 | static const int hdc100x_int_time[][3] = { | |
45 | { 6350, 3650, 0 }, /* IIO_TEMP channel*/ | |
46 | { 6500, 3850, 2500 }, /* IIO_HUMIDITYRELATIVE channel */ | |
47 | }; | |
48 | ||
49 | /* HDC100X_REG_CONFIG shift and mask values */ | |
50 | static const struct { | |
51 | int shift; | |
52 | int mask; | |
53 | } hdc100x_resolution_shift[2] = { | |
54 | { /* IIO_TEMP channel */ | |
55 | .shift = 10, | |
56 | .mask = 1 | |
57 | }, | |
58 | { /* IIO_HUMIDITYRELATIVE channel */ | |
59 | .shift = 8, | |
0e35cf5c | 60 | .mask = 3, |
4839367d MR |
61 | }, |
62 | }; | |
63 | ||
64 | static IIO_CONST_ATTR(temp_integration_time_available, | |
65 | "0.00365 0.00635"); | |
66 | ||
67 | static IIO_CONST_ATTR(humidityrelative_integration_time_available, | |
68 | "0.0025 0.00385 0.0065"); | |
69 | ||
70 | static IIO_CONST_ATTR(out_current_heater_raw_available, | |
71 | "0 1"); | |
72 | ||
73 | static struct attribute *hdc100x_attributes[] = { | |
74 | &iio_const_attr_temp_integration_time_available.dev_attr.attr, | |
75 | &iio_const_attr_humidityrelative_integration_time_available.dev_attr.attr, | |
76 | &iio_const_attr_out_current_heater_raw_available.dev_attr.attr, | |
77 | NULL | |
78 | }; | |
79 | ||
757cff86 | 80 | static const struct attribute_group hdc100x_attribute_group = { |
4839367d MR |
81 | .attrs = hdc100x_attributes, |
82 | }; | |
83 | ||
84 | static const struct iio_chan_spec hdc100x_channels[] = { | |
85 | { | |
86 | .type = IIO_TEMP, | |
87 | .address = HDC100X_REG_TEMP, | |
88 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | | |
89 | BIT(IIO_CHAN_INFO_SCALE) | | |
90 | BIT(IIO_CHAN_INFO_INT_TIME) | | |
91 | BIT(IIO_CHAN_INFO_OFFSET), | |
16bf793f AS |
92 | .scan_index = 0, |
93 | .scan_type = { | |
94 | .sign = 's', | |
95 | .realbits = 16, | |
96 | .storagebits = 16, | |
97 | .endianness = IIO_BE, | |
98 | }, | |
4839367d MR |
99 | }, |
100 | { | |
101 | .type = IIO_HUMIDITYRELATIVE, | |
102 | .address = HDC100X_REG_HUMIDITY, | |
103 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | | |
104 | BIT(IIO_CHAN_INFO_SCALE) | | |
16bf793f AS |
105 | BIT(IIO_CHAN_INFO_INT_TIME), |
106 | .scan_index = 1, | |
107 | .scan_type = { | |
108 | .sign = 'u', | |
109 | .realbits = 16, | |
110 | .storagebits = 16, | |
111 | .endianness = IIO_BE, | |
112 | }, | |
4839367d MR |
113 | }, |
114 | { | |
115 | .type = IIO_CURRENT, | |
116 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), | |
117 | .extend_name = "heater", | |
118 | .output = 1, | |
16bf793f | 119 | .scan_index = -1, |
4839367d | 120 | }, |
16bf793f | 121 | IIO_CHAN_SOFT_TIMESTAMP(2), |
4839367d MR |
122 | }; |
123 | ||
16bf793f AS |
124 | static const unsigned long hdc100x_scan_masks[] = {0x3, 0}; |
125 | ||
4839367d MR |
126 | static int hdc100x_update_config(struct hdc100x_data *data, int mask, int val) |
127 | { | |
128 | int tmp = (~mask & data->config) | val; | |
129 | int ret; | |
130 | ||
131 | ret = i2c_smbus_write_word_swapped(data->client, | |
132 | HDC100X_REG_CONFIG, tmp); | |
133 | if (!ret) | |
134 | data->config = tmp; | |
135 | ||
136 | return ret; | |
137 | } | |
138 | ||
139 | static int hdc100x_set_it_time(struct hdc100x_data *data, int chan, int val2) | |
140 | { | |
141 | int shift = hdc100x_resolution_shift[chan].shift; | |
142 | int ret = -EINVAL; | |
143 | int i; | |
144 | ||
145 | for (i = 0; i < ARRAY_SIZE(hdc100x_int_time[chan]); i++) { | |
146 | if (val2 && val2 == hdc100x_int_time[chan][i]) { | |
147 | ret = hdc100x_update_config(data, | |
148 | hdc100x_resolution_shift[chan].mask << shift, | |
149 | i << shift); | |
150 | if (!ret) | |
151 | data->adc_int_us[chan] = val2; | |
152 | break; | |
153 | } | |
154 | } | |
155 | ||
156 | return ret; | |
157 | } | |
158 | ||
159 | static int hdc100x_get_measurement(struct hdc100x_data *data, | |
160 | struct iio_chan_spec const *chan) | |
161 | { | |
162 | struct i2c_client *client = data->client; | |
163 | int delay = data->adc_int_us[chan->address]; | |
164 | int ret; | |
0d9dcf85 | 165 | __be16 val; |
4839367d MR |
166 | |
167 | /* start measurement */ | |
168 | ret = i2c_smbus_write_byte(client, chan->address); | |
169 | if (ret < 0) { | |
170 | dev_err(&client->dev, "cannot start measurement"); | |
171 | return ret; | |
172 | } | |
173 | ||
174 | /* wait for integration time to pass */ | |
175 | usleep_range(delay, delay + 1000); | |
176 | ||
0d9dcf85 AS |
177 | /* read measurement */ |
178 | ret = i2c_master_recv(data->client, (char *)&val, sizeof(val)); | |
4839367d | 179 | if (ret < 0) { |
0d9dcf85 | 180 | dev_err(&client->dev, "cannot read sensor data\n"); |
4839367d MR |
181 | return ret; |
182 | } | |
0d9dcf85 | 183 | return be16_to_cpu(val); |
4839367d MR |
184 | } |
185 | ||
186 | static int hdc100x_get_heater_status(struct hdc100x_data *data) | |
187 | { | |
188 | return !!(data->config & HDC100X_REG_CONFIG_HEATER_EN); | |
189 | } | |
190 | ||
191 | static int hdc100x_read_raw(struct iio_dev *indio_dev, | |
192 | struct iio_chan_spec const *chan, int *val, | |
193 | int *val2, long mask) | |
194 | { | |
195 | struct hdc100x_data *data = iio_priv(indio_dev); | |
196 | ||
197 | switch (mask) { | |
198 | case IIO_CHAN_INFO_RAW: { | |
199 | int ret; | |
200 | ||
201 | mutex_lock(&data->lock); | |
202 | if (chan->type == IIO_CURRENT) { | |
203 | *val = hdc100x_get_heater_status(data); | |
204 | ret = IIO_VAL_INT; | |
205 | } else { | |
16bf793f AS |
206 | ret = iio_device_claim_direct_mode(indio_dev); |
207 | if (ret) { | |
208 | mutex_unlock(&data->lock); | |
209 | return ret; | |
210 | } | |
211 | ||
4839367d | 212 | ret = hdc100x_get_measurement(data, chan); |
16bf793f | 213 | iio_device_release_direct_mode(indio_dev); |
4839367d MR |
214 | if (ret >= 0) { |
215 | *val = ret; | |
216 | ret = IIO_VAL_INT; | |
217 | } | |
218 | } | |
219 | mutex_unlock(&data->lock); | |
220 | return ret; | |
221 | } | |
222 | case IIO_CHAN_INFO_INT_TIME: | |
223 | *val = 0; | |
224 | *val2 = data->adc_int_us[chan->address]; | |
225 | return IIO_VAL_INT_PLUS_MICRO; | |
226 | case IIO_CHAN_INFO_SCALE: | |
227 | if (chan->type == IIO_TEMP) { | |
09bc0dda | 228 | *val = 165000; |
94bef000 | 229 | *val2 = 65536; |
4839367d MR |
230 | return IIO_VAL_FRACTIONAL; |
231 | } else { | |
94bef000 MR |
232 | *val = 100; |
233 | *val2 = 65536; | |
234 | return IIO_VAL_FRACTIONAL; | |
4839367d MR |
235 | } |
236 | break; | |
237 | case IIO_CHAN_INFO_OFFSET: | |
94bef000 MR |
238 | *val = -15887; |
239 | *val2 = 515151; | |
d3a21ce0 | 240 | return IIO_VAL_INT_PLUS_MICRO; |
4839367d MR |
241 | default: |
242 | return -EINVAL; | |
243 | } | |
244 | } | |
245 | ||
246 | static int hdc100x_write_raw(struct iio_dev *indio_dev, | |
247 | struct iio_chan_spec const *chan, | |
248 | int val, int val2, long mask) | |
249 | { | |
250 | struct hdc100x_data *data = iio_priv(indio_dev); | |
251 | int ret = -EINVAL; | |
252 | ||
253 | switch (mask) { | |
254 | case IIO_CHAN_INFO_INT_TIME: | |
255 | if (val != 0) | |
256 | return -EINVAL; | |
257 | ||
258 | mutex_lock(&data->lock); | |
259 | ret = hdc100x_set_it_time(data, chan->address, val2); | |
260 | mutex_unlock(&data->lock); | |
261 | return ret; | |
262 | case IIO_CHAN_INFO_RAW: | |
263 | if (chan->type != IIO_CURRENT || val2 != 0) | |
264 | return -EINVAL; | |
265 | ||
266 | mutex_lock(&data->lock); | |
267 | ret = hdc100x_update_config(data, HDC100X_REG_CONFIG_HEATER_EN, | |
268 | val ? HDC100X_REG_CONFIG_HEATER_EN : 0); | |
269 | mutex_unlock(&data->lock); | |
270 | return ret; | |
271 | default: | |
272 | return -EINVAL; | |
273 | } | |
274 | } | |
275 | ||
16bf793f AS |
276 | static int hdc100x_buffer_postenable(struct iio_dev *indio_dev) |
277 | { | |
278 | struct hdc100x_data *data = iio_priv(indio_dev); | |
279 | int ret; | |
280 | ||
281 | /* Buffer is enabled. First set ACQ Mode, then attach poll func */ | |
282 | mutex_lock(&data->lock); | |
283 | ret = hdc100x_update_config(data, HDC100X_REG_CONFIG_ACQ_MODE, | |
284 | HDC100X_REG_CONFIG_ACQ_MODE); | |
285 | mutex_unlock(&data->lock); | |
286 | if (ret) | |
287 | return ret; | |
288 | ||
289 | return iio_triggered_buffer_postenable(indio_dev); | |
290 | } | |
291 | ||
292 | static int hdc100x_buffer_predisable(struct iio_dev *indio_dev) | |
293 | { | |
294 | struct hdc100x_data *data = iio_priv(indio_dev); | |
295 | int ret; | |
296 | ||
297 | /* First detach poll func, then reset ACQ mode. OK to disable buffer */ | |
298 | ret = iio_triggered_buffer_predisable(indio_dev); | |
299 | if (ret) | |
300 | return ret; | |
301 | ||
302 | mutex_lock(&data->lock); | |
303 | ret = hdc100x_update_config(data, HDC100X_REG_CONFIG_ACQ_MODE, 0); | |
304 | mutex_unlock(&data->lock); | |
305 | ||
306 | return ret; | |
307 | } | |
308 | ||
309 | static const struct iio_buffer_setup_ops hdc_buffer_setup_ops = { | |
310 | .postenable = hdc100x_buffer_postenable, | |
311 | .predisable = hdc100x_buffer_predisable, | |
312 | }; | |
313 | ||
314 | static irqreturn_t hdc100x_trigger_handler(int irq, void *p) | |
315 | { | |
316 | struct iio_poll_func *pf = p; | |
317 | struct iio_dev *indio_dev = pf->indio_dev; | |
318 | struct hdc100x_data *data = iio_priv(indio_dev); | |
319 | struct i2c_client *client = data->client; | |
320 | int delay = data->adc_int_us[0] + data->adc_int_us[1]; | |
321 | int ret; | |
322 | s16 buf[8]; /* 2x s16 + padding + 8 byte timestamp */ | |
323 | ||
324 | /* dual read starts at temp register */ | |
325 | mutex_lock(&data->lock); | |
326 | ret = i2c_smbus_write_byte(client, HDC100X_REG_TEMP); | |
327 | if (ret < 0) { | |
328 | dev_err(&client->dev, "cannot start measurement\n"); | |
329 | goto err; | |
330 | } | |
331 | usleep_range(delay, delay + 1000); | |
332 | ||
333 | ret = i2c_master_recv(client, (u8 *)buf, 4); | |
334 | if (ret < 0) { | |
335 | dev_err(&client->dev, "cannot read sensor data\n"); | |
336 | goto err; | |
337 | } | |
338 | ||
339 | iio_push_to_buffers_with_timestamp(indio_dev, buf, | |
340 | iio_get_time_ns(indio_dev)); | |
341 | err: | |
342 | mutex_unlock(&data->lock); | |
343 | iio_trigger_notify_done(indio_dev->trig); | |
344 | ||
345 | return IRQ_HANDLED; | |
346 | } | |
347 | ||
4839367d MR |
348 | static const struct iio_info hdc100x_info = { |
349 | .read_raw = hdc100x_read_raw, | |
350 | .write_raw = hdc100x_write_raw, | |
351 | .attrs = &hdc100x_attribute_group, | |
4839367d MR |
352 | }; |
353 | ||
354 | static int hdc100x_probe(struct i2c_client *client, | |
355 | const struct i2c_device_id *id) | |
356 | { | |
357 | struct iio_dev *indio_dev; | |
358 | struct hdc100x_data *data; | |
16bf793f | 359 | int ret; |
4839367d | 360 | |
0d9dcf85 AS |
361 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA | |
362 | I2C_FUNC_SMBUS_BYTE | I2C_FUNC_I2C)) | |
f8d9d3b4 | 363 | return -EOPNOTSUPP; |
4839367d MR |
364 | |
365 | indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); | |
366 | if (!indio_dev) | |
367 | return -ENOMEM; | |
368 | ||
369 | data = iio_priv(indio_dev); | |
370 | i2c_set_clientdata(client, indio_dev); | |
371 | data->client = client; | |
372 | mutex_init(&data->lock); | |
373 | ||
374 | indio_dev->dev.parent = &client->dev; | |
375 | indio_dev->name = dev_name(&client->dev); | |
376 | indio_dev->modes = INDIO_DIRECT_MODE; | |
377 | indio_dev->info = &hdc100x_info; | |
378 | ||
379 | indio_dev->channels = hdc100x_channels; | |
380 | indio_dev->num_channels = ARRAY_SIZE(hdc100x_channels); | |
16bf793f | 381 | indio_dev->available_scan_masks = hdc100x_scan_masks; |
4839367d MR |
382 | |
383 | /* be sure we are in a known state */ | |
384 | hdc100x_set_it_time(data, 0, hdc100x_int_time[0][0]); | |
385 | hdc100x_set_it_time(data, 1, hdc100x_int_time[1][0]); | |
16bf793f AS |
386 | hdc100x_update_config(data, HDC100X_REG_CONFIG_ACQ_MODE, 0); |
387 | ||
388 | ret = iio_triggered_buffer_setup(indio_dev, NULL, | |
389 | hdc100x_trigger_handler, | |
390 | &hdc_buffer_setup_ops); | |
391 | if (ret < 0) { | |
392 | dev_err(&client->dev, "iio triggered buffer setup failed\n"); | |
393 | return ret; | |
394 | } | |
395 | ret = iio_device_register(indio_dev); | |
396 | if (ret < 0) | |
397 | iio_triggered_buffer_cleanup(indio_dev); | |
398 | ||
399 | return ret; | |
400 | } | |
401 | ||
402 | static int hdc100x_remove(struct i2c_client *client) | |
403 | { | |
404 | struct iio_dev *indio_dev = i2c_get_clientdata(client); | |
405 | ||
406 | iio_device_unregister(indio_dev); | |
407 | iio_triggered_buffer_cleanup(indio_dev); | |
4839367d | 408 | |
16bf793f | 409 | return 0; |
4839367d MR |
410 | } |
411 | ||
412 | static const struct i2c_device_id hdc100x_id[] = { | |
413 | { "hdc100x", 0 }, | |
8f4c9575 MS |
414 | { "hdc1000", 0 }, |
415 | { "hdc1008", 0 }, | |
416 | { "hdc1010", 0 }, | |
417 | { "hdc1050", 0 }, | |
418 | { "hdc1080", 0 }, | |
4839367d MR |
419 | { } |
420 | }; | |
421 | MODULE_DEVICE_TABLE(i2c, hdc100x_id); | |
422 | ||
a2d80de8 MS |
423 | static const struct of_device_id hdc100x_dt_ids[] = { |
424 | { .compatible = "ti,hdc1000" }, | |
425 | { .compatible = "ti,hdc1008" }, | |
426 | { .compatible = "ti,hdc1010" }, | |
427 | { .compatible = "ti,hdc1050" }, | |
428 | { .compatible = "ti,hdc1080" }, | |
429 | { } | |
430 | }; | |
431 | MODULE_DEVICE_TABLE(of, hdc100x_dt_ids); | |
432 | ||
4839367d MR |
433 | static struct i2c_driver hdc100x_driver = { |
434 | .driver = { | |
435 | .name = "hdc100x", | |
a2d80de8 | 436 | .of_match_table = of_match_ptr(hdc100x_dt_ids), |
4839367d MR |
437 | }, |
438 | .probe = hdc100x_probe, | |
16bf793f | 439 | .remove = hdc100x_remove, |
4839367d MR |
440 | .id_table = hdc100x_id, |
441 | }; | |
442 | module_i2c_driver(hdc100x_driver); | |
443 | ||
d6ad8058 | 444 | MODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>"); |
4839367d MR |
445 | MODULE_DESCRIPTION("TI HDC100x humidity and temperature sensor driver"); |
446 | MODULE_LICENSE("GPL"); |