Commit | Line | Data |
---|---|---|
4839367d MR |
1 | /* |
2 | * hdc100x.c - Support for the TI HDC100x temperature + humidity sensors | |
3 | * | |
4 | * Copyright (C) 2015 Matt Ranostay <mranostay@gmail.com> | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License as published by | |
8 | * the Free Software Foundation; either version 2 of the License, or | |
9 | * (at your option) any later version. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * GNU General Public License for more details. | |
15 | * | |
16 | */ | |
17 | ||
18 | #include <linux/delay.h> | |
19 | #include <linux/module.h> | |
20 | #include <linux/init.h> | |
21 | #include <linux/i2c.h> | |
22 | ||
23 | #include <linux/iio/iio.h> | |
24 | #include <linux/iio/sysfs.h> | |
25 | ||
26 | #define HDC100X_REG_TEMP 0x00 | |
27 | #define HDC100X_REG_HUMIDITY 0x01 | |
28 | ||
29 | #define HDC100X_REG_CONFIG 0x02 | |
30 | #define HDC100X_REG_CONFIG_HEATER_EN BIT(13) | |
31 | ||
32 | struct hdc100x_data { | |
33 | struct i2c_client *client; | |
34 | struct mutex lock; | |
35 | u16 config; | |
36 | ||
37 | /* integration time of the sensor */ | |
38 | int adc_int_us[2]; | |
39 | }; | |
40 | ||
41 | /* integration time in us */ | |
42 | static const int hdc100x_int_time[][3] = { | |
43 | { 6350, 3650, 0 }, /* IIO_TEMP channel*/ | |
44 | { 6500, 3850, 2500 }, /* IIO_HUMIDITYRELATIVE channel */ | |
45 | }; | |
46 | ||
47 | /* HDC100X_REG_CONFIG shift and mask values */ | |
48 | static const struct { | |
49 | int shift; | |
50 | int mask; | |
51 | } hdc100x_resolution_shift[2] = { | |
52 | { /* IIO_TEMP channel */ | |
53 | .shift = 10, | |
54 | .mask = 1 | |
55 | }, | |
56 | { /* IIO_HUMIDITYRELATIVE channel */ | |
57 | .shift = 8, | |
0e35cf5c | 58 | .mask = 3, |
4839367d MR |
59 | }, |
60 | }; | |
61 | ||
62 | static IIO_CONST_ATTR(temp_integration_time_available, | |
63 | "0.00365 0.00635"); | |
64 | ||
65 | static IIO_CONST_ATTR(humidityrelative_integration_time_available, | |
66 | "0.0025 0.00385 0.0065"); | |
67 | ||
68 | static IIO_CONST_ATTR(out_current_heater_raw_available, | |
69 | "0 1"); | |
70 | ||
71 | static struct attribute *hdc100x_attributes[] = { | |
72 | &iio_const_attr_temp_integration_time_available.dev_attr.attr, | |
73 | &iio_const_attr_humidityrelative_integration_time_available.dev_attr.attr, | |
74 | &iio_const_attr_out_current_heater_raw_available.dev_attr.attr, | |
75 | NULL | |
76 | }; | |
77 | ||
78 | static struct attribute_group hdc100x_attribute_group = { | |
79 | .attrs = hdc100x_attributes, | |
80 | }; | |
81 | ||
82 | static const struct iio_chan_spec hdc100x_channels[] = { | |
83 | { | |
84 | .type = IIO_TEMP, | |
85 | .address = HDC100X_REG_TEMP, | |
86 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | | |
87 | BIT(IIO_CHAN_INFO_SCALE) | | |
88 | BIT(IIO_CHAN_INFO_INT_TIME) | | |
89 | BIT(IIO_CHAN_INFO_OFFSET), | |
90 | }, | |
91 | { | |
92 | .type = IIO_HUMIDITYRELATIVE, | |
93 | .address = HDC100X_REG_HUMIDITY, | |
94 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | | |
95 | BIT(IIO_CHAN_INFO_SCALE) | | |
96 | BIT(IIO_CHAN_INFO_INT_TIME) | |
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 hdc100x_update_config(struct hdc100x_data *data, int mask, int val) | |
107 | { | |
108 | int tmp = (~mask & data->config) | val; | |
109 | int ret; | |
110 | ||
111 | ret = i2c_smbus_write_word_swapped(data->client, | |
112 | HDC100X_REG_CONFIG, tmp); | |
113 | if (!ret) | |
114 | data->config = tmp; | |
115 | ||
116 | return ret; | |
117 | } | |
118 | ||
119 | static int hdc100x_set_it_time(struct hdc100x_data *data, int chan, int val2) | |
120 | { | |
121 | int shift = hdc100x_resolution_shift[chan].shift; | |
122 | int ret = -EINVAL; | |
123 | int i; | |
124 | ||
125 | for (i = 0; i < ARRAY_SIZE(hdc100x_int_time[chan]); i++) { | |
126 | if (val2 && val2 == hdc100x_int_time[chan][i]) { | |
127 | ret = hdc100x_update_config(data, | |
128 | hdc100x_resolution_shift[chan].mask << shift, | |
129 | i << shift); | |
130 | if (!ret) | |
131 | data->adc_int_us[chan] = val2; | |
132 | break; | |
133 | } | |
134 | } | |
135 | ||
136 | return ret; | |
137 | } | |
138 | ||
139 | static int hdc100x_get_measurement(struct hdc100x_data *data, | |
140 | struct iio_chan_spec const *chan) | |
141 | { | |
142 | struct i2c_client *client = data->client; | |
143 | int delay = data->adc_int_us[chan->address]; | |
144 | int ret; | |
145 | int val; | |
146 | ||
147 | /* start measurement */ | |
148 | ret = i2c_smbus_write_byte(client, chan->address); | |
149 | if (ret < 0) { | |
150 | dev_err(&client->dev, "cannot start measurement"); | |
151 | return ret; | |
152 | } | |
153 | ||
154 | /* wait for integration time to pass */ | |
155 | usleep_range(delay, delay + 1000); | |
156 | ||
157 | /* | |
158 | * i2c_smbus_read_word_data cannot() be used here due to the command | |
159 | * value not being understood and causes NAKs preventing any reading | |
160 | * from being accessed. | |
161 | */ | |
162 | ret = i2c_smbus_read_byte(client); | |
163 | if (ret < 0) { | |
164 | dev_err(&client->dev, "cannot read high byte measurement"); | |
165 | return ret; | |
166 | } | |
94bef000 | 167 | val = ret << 8; |
4839367d MR |
168 | |
169 | ret = i2c_smbus_read_byte(client); | |
170 | if (ret < 0) { | |
171 | dev_err(&client->dev, "cannot read low byte measurement"); | |
172 | return ret; | |
173 | } | |
94bef000 | 174 | val |= ret; |
4839367d MR |
175 | |
176 | return val; | |
177 | } | |
178 | ||
179 | static int hdc100x_get_heater_status(struct hdc100x_data *data) | |
180 | { | |
181 | return !!(data->config & HDC100X_REG_CONFIG_HEATER_EN); | |
182 | } | |
183 | ||
184 | static int hdc100x_read_raw(struct iio_dev *indio_dev, | |
185 | struct iio_chan_spec const *chan, int *val, | |
186 | int *val2, long mask) | |
187 | { | |
188 | struct hdc100x_data *data = iio_priv(indio_dev); | |
189 | ||
190 | switch (mask) { | |
191 | case IIO_CHAN_INFO_RAW: { | |
192 | int ret; | |
193 | ||
194 | mutex_lock(&data->lock); | |
195 | if (chan->type == IIO_CURRENT) { | |
196 | *val = hdc100x_get_heater_status(data); | |
197 | ret = IIO_VAL_INT; | |
198 | } else { | |
199 | ret = hdc100x_get_measurement(data, chan); | |
200 | if (ret >= 0) { | |
201 | *val = ret; | |
202 | ret = IIO_VAL_INT; | |
203 | } | |
204 | } | |
205 | mutex_unlock(&data->lock); | |
206 | return ret; | |
207 | } | |
208 | case IIO_CHAN_INFO_INT_TIME: | |
209 | *val = 0; | |
210 | *val2 = data->adc_int_us[chan->address]; | |
211 | return IIO_VAL_INT_PLUS_MICRO; | |
212 | case IIO_CHAN_INFO_SCALE: | |
213 | if (chan->type == IIO_TEMP) { | |
09bc0dda | 214 | *val = 165000; |
94bef000 | 215 | *val2 = 65536; |
4839367d MR |
216 | return IIO_VAL_FRACTIONAL; |
217 | } else { | |
94bef000 MR |
218 | *val = 100; |
219 | *val2 = 65536; | |
220 | return IIO_VAL_FRACTIONAL; | |
4839367d MR |
221 | } |
222 | break; | |
223 | case IIO_CHAN_INFO_OFFSET: | |
94bef000 MR |
224 | *val = -15887; |
225 | *val2 = 515151; | |
d3a21ce0 | 226 | return IIO_VAL_INT_PLUS_MICRO; |
4839367d MR |
227 | default: |
228 | return -EINVAL; | |
229 | } | |
230 | } | |
231 | ||
232 | static int hdc100x_write_raw(struct iio_dev *indio_dev, | |
233 | struct iio_chan_spec const *chan, | |
234 | int val, int val2, long mask) | |
235 | { | |
236 | struct hdc100x_data *data = iio_priv(indio_dev); | |
237 | int ret = -EINVAL; | |
238 | ||
239 | switch (mask) { | |
240 | case IIO_CHAN_INFO_INT_TIME: | |
241 | if (val != 0) | |
242 | return -EINVAL; | |
243 | ||
244 | mutex_lock(&data->lock); | |
245 | ret = hdc100x_set_it_time(data, chan->address, val2); | |
246 | mutex_unlock(&data->lock); | |
247 | return ret; | |
248 | case IIO_CHAN_INFO_RAW: | |
249 | if (chan->type != IIO_CURRENT || val2 != 0) | |
250 | return -EINVAL; | |
251 | ||
252 | mutex_lock(&data->lock); | |
253 | ret = hdc100x_update_config(data, HDC100X_REG_CONFIG_HEATER_EN, | |
254 | val ? HDC100X_REG_CONFIG_HEATER_EN : 0); | |
255 | mutex_unlock(&data->lock); | |
256 | return ret; | |
257 | default: | |
258 | return -EINVAL; | |
259 | } | |
260 | } | |
261 | ||
262 | static const struct iio_info hdc100x_info = { | |
263 | .read_raw = hdc100x_read_raw, | |
264 | .write_raw = hdc100x_write_raw, | |
265 | .attrs = &hdc100x_attribute_group, | |
266 | .driver_module = THIS_MODULE, | |
267 | }; | |
268 | ||
269 | static int hdc100x_probe(struct i2c_client *client, | |
270 | const struct i2c_device_id *id) | |
271 | { | |
272 | struct iio_dev *indio_dev; | |
273 | struct hdc100x_data *data; | |
274 | ||
275 | if (!i2c_check_functionality(client->adapter, | |
276 | I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BYTE)) | |
f8d9d3b4 | 277 | return -EOPNOTSUPP; |
4839367d MR |
278 | |
279 | indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); | |
280 | if (!indio_dev) | |
281 | return -ENOMEM; | |
282 | ||
283 | data = iio_priv(indio_dev); | |
284 | i2c_set_clientdata(client, indio_dev); | |
285 | data->client = client; | |
286 | mutex_init(&data->lock); | |
287 | ||
288 | indio_dev->dev.parent = &client->dev; | |
289 | indio_dev->name = dev_name(&client->dev); | |
290 | indio_dev->modes = INDIO_DIRECT_MODE; | |
291 | indio_dev->info = &hdc100x_info; | |
292 | ||
293 | indio_dev->channels = hdc100x_channels; | |
294 | indio_dev->num_channels = ARRAY_SIZE(hdc100x_channels); | |
295 | ||
296 | /* be sure we are in a known state */ | |
297 | hdc100x_set_it_time(data, 0, hdc100x_int_time[0][0]); | |
298 | hdc100x_set_it_time(data, 1, hdc100x_int_time[1][0]); | |
299 | ||
300 | return devm_iio_device_register(&client->dev, indio_dev); | |
301 | } | |
302 | ||
303 | static const struct i2c_device_id hdc100x_id[] = { | |
304 | { "hdc100x", 0 }, | |
305 | { } | |
306 | }; | |
307 | MODULE_DEVICE_TABLE(i2c, hdc100x_id); | |
308 | ||
309 | static struct i2c_driver hdc100x_driver = { | |
310 | .driver = { | |
311 | .name = "hdc100x", | |
312 | }, | |
313 | .probe = hdc100x_probe, | |
314 | .id_table = hdc100x_id, | |
315 | }; | |
316 | module_i2c_driver(hdc100x_driver); | |
317 | ||
318 | MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>"); | |
319 | MODULE_DESCRIPTION("TI HDC100x humidity and temperature sensor driver"); | |
320 | MODULE_LICENSE("GPL"); |