1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * hp206c.c - HOPERF HP206C precision barometer and altimeter sensor |
4 | * |
5 | * Copyright (c) 2016, Intel Corporation. |
6 | * |
7 | * (7-bit I2C slave address 0x76) |
8 | * |
9 | * Datasheet: |
10 | * http://www.hoperf.com/upload/sensor/HP206C_DataSheet_EN_V2.0.pdf |
11 | */ |
12 | |
13 | #include <linux/module.h> |
14 | #include <linux/i2c.h> |
15 | #include <linux/iio/iio.h> |
16 | #include <linux/iio/sysfs.h> |
17 | #include <linux/delay.h> |
18 | #include <linux/util_macros.h> |
19 | #include <linux/acpi.h> |
20 | |
21 | #include <asm/unaligned.h> |
22 | |
23 | /* I2C commands: */ |
24 | #define HP206C_CMD_SOFT_RST 0x06 |
25 | |
26 | #define HP206C_CMD_ADC_CVT 0x40 |
27 | |
28 | #define HP206C_CMD_ADC_CVT_OSR_4096 0x00 |
29 | #define HP206C_CMD_ADC_CVT_OSR_2048 0x04 |
30 | #define HP206C_CMD_ADC_CVT_OSR_1024 0x08 |
31 | #define HP206C_CMD_ADC_CVT_OSR_512 0x0c |
32 | #define HP206C_CMD_ADC_CVT_OSR_256 0x10 |
33 | #define HP206C_CMD_ADC_CVT_OSR_128 0x14 |
34 | |
35 | #define HP206C_CMD_ADC_CVT_CHNL_PT 0x00 |
36 | #define HP206C_CMD_ADC_CVT_CHNL_T 0x02 |
37 | |
38 | #define HP206C_CMD_READ_P 0x30 |
39 | #define HP206C_CMD_READ_T 0x32 |
40 | |
41 | #define HP206C_CMD_READ_REG 0x80 |
42 | #define HP206C_CMD_WRITE_REG 0xc0 |
43 | |
44 | #define HP206C_REG_INT_EN 0x0b |
45 | #define HP206C_REG_INT_CFG 0x0c |
46 | |
47 | #define HP206C_REG_INT_SRC 0x0d |
48 | #define HP206C_FLAG_DEV_RDY 0x40 |
49 | |
50 | #define HP206C_REG_PARA 0x0f |
51 | #define HP206C_FLAG_CMPS_EN 0x80 |
52 | |
53 | /* Maximum spin for DEV_RDY */ |
54 | #define HP206C_MAX_DEV_RDY_WAIT_COUNT 20 |
55 | #define HP206C_DEV_RDY_WAIT_US 20000 |
56 | |
57 | struct hp206c_data { |
58 | struct mutex mutex; |
59 | struct i2c_client *client; |
60 | int temp_osr_index; |
61 | int pres_osr_index; |
62 | }; |
63 | |
64 | struct hp206c_osr_setting { |
65 | u8 osr_mask; |
66 | unsigned int temp_conv_time_us; |
67 | unsigned int pres_conv_time_us; |
68 | }; |
69 | |
70 | /* Data from Table 5 in datasheet. */ |
71 | static const struct hp206c_osr_setting hp206c_osr_settings[] = { |
72 | { HP206C_CMD_ADC_CVT_OSR_4096, 65600, 131100 }, |
73 | { HP206C_CMD_ADC_CVT_OSR_2048, 32800, 65600 }, |
74 | { HP206C_CMD_ADC_CVT_OSR_1024, 16400, 32800 }, |
75 | { HP206C_CMD_ADC_CVT_OSR_512, 8200, 16400 }, |
76 | { HP206C_CMD_ADC_CVT_OSR_256, 4100, 8200 }, |
77 | { HP206C_CMD_ADC_CVT_OSR_128, 2100, 4100 }, |
78 | }; |
79 | static const int hp206c_osr_rates[] = { 4096, 2048, 1024, 512, 256, 128 }; |
80 | static const char hp206c_osr_rates_str[] = "4096 2048 1024 512 256 128" ; |
81 | |
82 | static inline int hp206c_read_reg(struct i2c_client *client, u8 reg) |
83 | { |
84 | return i2c_smbus_read_byte_data(client, HP206C_CMD_READ_REG | reg); |
85 | } |
86 | |
87 | static inline int hp206c_write_reg(struct i2c_client *client, u8 reg, u8 val) |
88 | { |
89 | return i2c_smbus_write_byte_data(client, |
90 | HP206C_CMD_WRITE_REG | reg, value: val); |
91 | } |
92 | |
93 | static int hp206c_read_20bit(struct i2c_client *client, u8 cmd) |
94 | { |
95 | int ret; |
96 | u8 values[3]; |
97 | |
98 | ret = i2c_smbus_read_i2c_block_data(client, command: cmd, length: sizeof(values), values); |
99 | if (ret < 0) |
100 | return ret; |
101 | if (ret != sizeof(values)) |
102 | return -EIO; |
103 | return get_unaligned_be24(p: &values[0]) & GENMASK(19, 0); |
104 | } |
105 | |
106 | /* Spin for max 160ms until DEV_RDY is 1, or return error. */ |
107 | static int hp206c_wait_dev_rdy(struct iio_dev *indio_dev) |
108 | { |
109 | int ret; |
110 | int count = 0; |
111 | struct hp206c_data *data = iio_priv(indio_dev); |
112 | struct i2c_client *client = data->client; |
113 | |
114 | while (++count <= HP206C_MAX_DEV_RDY_WAIT_COUNT) { |
115 | ret = hp206c_read_reg(client, HP206C_REG_INT_SRC); |
116 | if (ret < 0) { |
117 | dev_err(&indio_dev->dev, "Failed READ_REG INT_SRC: %d\n" , ret); |
118 | return ret; |
119 | } |
120 | if (ret & HP206C_FLAG_DEV_RDY) |
121 | return 0; |
122 | usleep_range(HP206C_DEV_RDY_WAIT_US, HP206C_DEV_RDY_WAIT_US * 3 / 2); |
123 | } |
124 | return -ETIMEDOUT; |
125 | } |
126 | |
127 | static int hp206c_set_compensation(struct i2c_client *client, bool enabled) |
128 | { |
129 | int val; |
130 | |
131 | val = hp206c_read_reg(client, HP206C_REG_PARA); |
132 | if (val < 0) |
133 | return val; |
134 | if (enabled) |
135 | val |= HP206C_FLAG_CMPS_EN; |
136 | else |
137 | val &= ~HP206C_FLAG_CMPS_EN; |
138 | |
139 | return hp206c_write_reg(client, HP206C_REG_PARA, val); |
140 | } |
141 | |
142 | /* Do a soft reset */ |
143 | static int hp206c_soft_reset(struct iio_dev *indio_dev) |
144 | { |
145 | int ret; |
146 | struct hp206c_data *data = iio_priv(indio_dev); |
147 | struct i2c_client *client = data->client; |
148 | |
149 | ret = i2c_smbus_write_byte(client, HP206C_CMD_SOFT_RST); |
150 | if (ret) { |
151 | dev_err(&client->dev, "Failed to reset device: %d\n" , ret); |
152 | return ret; |
153 | } |
154 | |
155 | usleep_range(min: 400, max: 600); |
156 | |
157 | ret = hp206c_wait_dev_rdy(indio_dev); |
158 | if (ret) { |
159 | dev_err(&client->dev, "Device not ready after soft reset: %d\n" , ret); |
160 | return ret; |
161 | } |
162 | |
163 | ret = hp206c_set_compensation(client, enabled: true); |
164 | if (ret) |
165 | dev_err(&client->dev, "Failed to enable compensation: %d\n" , ret); |
166 | return ret; |
167 | } |
168 | |
169 | static int hp206c_conv_and_read(struct iio_dev *indio_dev, |
170 | u8 conv_cmd, u8 read_cmd, |
171 | unsigned int sleep_us) |
172 | { |
173 | int ret; |
174 | struct hp206c_data *data = iio_priv(indio_dev); |
175 | struct i2c_client *client = data->client; |
176 | |
177 | ret = hp206c_wait_dev_rdy(indio_dev); |
178 | if (ret < 0) { |
179 | dev_err(&indio_dev->dev, "Device not ready: %d\n" , ret); |
180 | return ret; |
181 | } |
182 | |
183 | ret = i2c_smbus_write_byte(client, value: conv_cmd); |
184 | if (ret < 0) { |
185 | dev_err(&indio_dev->dev, "Failed convert: %d\n" , ret); |
186 | return ret; |
187 | } |
188 | |
189 | usleep_range(min: sleep_us, max: sleep_us * 3 / 2); |
190 | |
191 | ret = hp206c_wait_dev_rdy(indio_dev); |
192 | if (ret < 0) { |
193 | dev_err(&indio_dev->dev, "Device not ready: %d\n" , ret); |
194 | return ret; |
195 | } |
196 | |
197 | ret = hp206c_read_20bit(client, cmd: read_cmd); |
198 | if (ret < 0) |
199 | dev_err(&indio_dev->dev, "Failed read: %d\n" , ret); |
200 | |
201 | return ret; |
202 | } |
203 | |
204 | static int hp206c_read_raw(struct iio_dev *indio_dev, |
205 | struct iio_chan_spec const *chan, int *val, |
206 | int *val2, long mask) |
207 | { |
208 | int ret; |
209 | struct hp206c_data *data = iio_priv(indio_dev); |
210 | const struct hp206c_osr_setting *osr_setting; |
211 | u8 conv_cmd; |
212 | |
213 | mutex_lock(&data->mutex); |
214 | |
215 | switch (mask) { |
216 | case IIO_CHAN_INFO_OVERSAMPLING_RATIO: |
217 | switch (chan->type) { |
218 | case IIO_TEMP: |
219 | *val = hp206c_osr_rates[data->temp_osr_index]; |
220 | ret = IIO_VAL_INT; |
221 | break; |
222 | |
223 | case IIO_PRESSURE: |
224 | *val = hp206c_osr_rates[data->pres_osr_index]; |
225 | ret = IIO_VAL_INT; |
226 | break; |
227 | default: |
228 | ret = -EINVAL; |
229 | } |
230 | break; |
231 | |
232 | case IIO_CHAN_INFO_RAW: |
233 | switch (chan->type) { |
234 | case IIO_TEMP: |
235 | osr_setting = &hp206c_osr_settings[data->temp_osr_index]; |
236 | conv_cmd = HP206C_CMD_ADC_CVT | |
237 | osr_setting->osr_mask | |
238 | HP206C_CMD_ADC_CVT_CHNL_T; |
239 | ret = hp206c_conv_and_read(indio_dev, |
240 | conv_cmd, |
241 | HP206C_CMD_READ_T, |
242 | sleep_us: osr_setting->temp_conv_time_us); |
243 | if (ret >= 0) { |
244 | /* 20 significant bits are provided. |
245 | * Extend sign over the rest. |
246 | */ |
247 | *val = sign_extend32(value: ret, index: 19); |
248 | ret = IIO_VAL_INT; |
249 | } |
250 | break; |
251 | |
252 | case IIO_PRESSURE: |
253 | osr_setting = &hp206c_osr_settings[data->pres_osr_index]; |
254 | conv_cmd = HP206C_CMD_ADC_CVT | |
255 | osr_setting->osr_mask | |
256 | HP206C_CMD_ADC_CVT_CHNL_PT; |
257 | ret = hp206c_conv_and_read(indio_dev, |
258 | conv_cmd, |
259 | HP206C_CMD_READ_P, |
260 | sleep_us: osr_setting->pres_conv_time_us); |
261 | if (ret >= 0) { |
262 | *val = ret; |
263 | ret = IIO_VAL_INT; |
264 | } |
265 | break; |
266 | default: |
267 | ret = -EINVAL; |
268 | } |
269 | break; |
270 | |
271 | case IIO_CHAN_INFO_SCALE: |
272 | switch (chan->type) { |
273 | case IIO_TEMP: |
274 | *val = 0; |
275 | *val2 = 10000; |
276 | ret = IIO_VAL_INT_PLUS_MICRO; |
277 | break; |
278 | |
279 | case IIO_PRESSURE: |
280 | *val = 0; |
281 | *val2 = 1000; |
282 | ret = IIO_VAL_INT_PLUS_MICRO; |
283 | break; |
284 | default: |
285 | ret = -EINVAL; |
286 | } |
287 | break; |
288 | |
289 | default: |
290 | ret = -EINVAL; |
291 | } |
292 | |
293 | mutex_unlock(lock: &data->mutex); |
294 | return ret; |
295 | } |
296 | |
297 | static int hp206c_write_raw(struct iio_dev *indio_dev, |
298 | struct iio_chan_spec const *chan, |
299 | int val, int val2, long mask) |
300 | { |
301 | int ret = 0; |
302 | struct hp206c_data *data = iio_priv(indio_dev); |
303 | |
304 | if (mask != IIO_CHAN_INFO_OVERSAMPLING_RATIO) |
305 | return -EINVAL; |
306 | mutex_lock(&data->mutex); |
307 | switch (chan->type) { |
308 | case IIO_TEMP: |
309 | data->temp_osr_index = find_closest_descending(val, |
310 | hp206c_osr_rates, ARRAY_SIZE(hp206c_osr_rates)); |
311 | break; |
312 | case IIO_PRESSURE: |
313 | data->pres_osr_index = find_closest_descending(val, |
314 | hp206c_osr_rates, ARRAY_SIZE(hp206c_osr_rates)); |
315 | break; |
316 | default: |
317 | ret = -EINVAL; |
318 | } |
319 | mutex_unlock(lock: &data->mutex); |
320 | return ret; |
321 | } |
322 | |
323 | static const struct iio_chan_spec hp206c_channels[] = { |
324 | { |
325 | .type = IIO_TEMP, |
326 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | |
327 | BIT(IIO_CHAN_INFO_SCALE) | |
328 | BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), |
329 | }, |
330 | { |
331 | .type = IIO_PRESSURE, |
332 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | |
333 | BIT(IIO_CHAN_INFO_SCALE) | |
334 | BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), |
335 | } |
336 | }; |
337 | |
338 | static IIO_CONST_ATTR_SAMP_FREQ_AVAIL(hp206c_osr_rates_str); |
339 | |
340 | static struct attribute *hp206c_attributes[] = { |
341 | &iio_const_attr_sampling_frequency_available.dev_attr.attr, |
342 | NULL, |
343 | }; |
344 | |
345 | static const struct attribute_group hp206c_attribute_group = { |
346 | .attrs = hp206c_attributes, |
347 | }; |
348 | |
349 | static const struct iio_info hp206c_info = { |
350 | .attrs = &hp206c_attribute_group, |
351 | .read_raw = hp206c_read_raw, |
352 | .write_raw = hp206c_write_raw, |
353 | }; |
354 | |
355 | static int hp206c_probe(struct i2c_client *client) |
356 | { |
357 | const struct i2c_device_id *id = i2c_client_get_device_id(client); |
358 | struct iio_dev *indio_dev; |
359 | struct hp206c_data *data; |
360 | int ret; |
361 | |
362 | if (!i2c_check_functionality(adap: client->adapter, |
363 | I2C_FUNC_SMBUS_BYTE | |
364 | I2C_FUNC_SMBUS_BYTE_DATA | |
365 | I2C_FUNC_SMBUS_READ_I2C_BLOCK)) { |
366 | dev_err(&client->dev, "Adapter does not support " |
367 | "all required i2c functionality\n" ); |
368 | return -ENODEV; |
369 | } |
370 | |
371 | indio_dev = devm_iio_device_alloc(parent: &client->dev, sizeof_priv: sizeof(*data)); |
372 | if (!indio_dev) |
373 | return -ENOMEM; |
374 | |
375 | data = iio_priv(indio_dev); |
376 | data->client = client; |
377 | mutex_init(&data->mutex); |
378 | |
379 | indio_dev->info = &hp206c_info; |
380 | indio_dev->name = id->name; |
381 | indio_dev->modes = INDIO_DIRECT_MODE; |
382 | indio_dev->channels = hp206c_channels; |
383 | indio_dev->num_channels = ARRAY_SIZE(hp206c_channels); |
384 | |
385 | i2c_set_clientdata(client, data: indio_dev); |
386 | |
387 | /* Do a soft reset on probe */ |
388 | ret = hp206c_soft_reset(indio_dev); |
389 | if (ret) { |
390 | dev_err(&client->dev, "Failed to reset on startup: %d\n" , ret); |
391 | return -ENODEV; |
392 | } |
393 | |
394 | return devm_iio_device_register(&client->dev, indio_dev); |
395 | } |
396 | |
397 | static const struct i2c_device_id hp206c_id[] = { |
398 | {"hp206c" }, |
399 | {} |
400 | }; |
401 | MODULE_DEVICE_TABLE(i2c, hp206c_id); |
402 | |
403 | #ifdef CONFIG_ACPI |
404 | static const struct acpi_device_id hp206c_acpi_match[] = { |
405 | {"HOP206C" , 0}, |
406 | { }, |
407 | }; |
408 | MODULE_DEVICE_TABLE(acpi, hp206c_acpi_match); |
409 | #endif |
410 | |
411 | static struct i2c_driver hp206c_driver = { |
412 | .probe = hp206c_probe, |
413 | .id_table = hp206c_id, |
414 | .driver = { |
415 | .name = "hp206c" , |
416 | .acpi_match_table = ACPI_PTR(hp206c_acpi_match), |
417 | }, |
418 | }; |
419 | |
420 | module_i2c_driver(hp206c_driver); |
421 | |
422 | MODULE_DESCRIPTION("HOPERF HP206C precision barometer and altimeter sensor" ); |
423 | MODULE_AUTHOR("Leonard Crestez <leonard.crestez@intel.com>" ); |
424 | MODULE_LICENSE("GPL v2" ); |
425 | |