]>
Commit | Line | Data |
---|---|---|
c942fddf | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
b3c590ce MR |
2 | /* |
3 | * max30102.c - Support for MAX30102 heart rate and pulse oximeter sensor | |
4 | * | |
5 | * Copyright (C) 2017 Matt Ranostay <matt@ranostay.consulting> | |
6 | * | |
90579b69 PMS |
7 | * Support for MAX30105 optical particle sensor |
8 | * Copyright (C) 2017 Peter Meerwald-Stadler <pmeerw@pmeerw.net> | |
9 | * | |
90579b69 | 10 | * 7-bit I2C chip address: 0x57 |
b3c590ce MR |
11 | * TODO: proximity power saving feature |
12 | */ | |
13 | ||
14 | #include <linux/module.h> | |
15 | #include <linux/init.h> | |
16 | #include <linux/interrupt.h> | |
17 | #include <linux/delay.h> | |
18 | #include <linux/err.h> | |
19 | #include <linux/irq.h> | |
20 | #include <linux/i2c.h> | |
21 | #include <linux/mutex.h> | |
22 | #include <linux/of.h> | |
23 | #include <linux/regmap.h> | |
24 | #include <linux/iio/iio.h> | |
25 | #include <linux/iio/buffer.h> | |
26 | #include <linux/iio/kfifo_buf.h> | |
27 | ||
28 | #define MAX30102_REGMAP_NAME "max30102_regmap" | |
29 | #define MAX30102_DRV_NAME "max30102" | |
84b0ce05 | 30 | #define MAX30102_PART_NUMBER 0x15 |
b3c590ce | 31 | |
90579b69 PMS |
32 | enum max30102_chip_id { |
33 | max30102, | |
34 | max30105, | |
35 | }; | |
36 | ||
ad90e570 PMS |
37 | enum max3012_led_idx { |
38 | MAX30102_LED_RED, | |
39 | MAX30102_LED_IR, | |
90579b69 | 40 | MAX30105_LED_GREEN, |
ad90e570 PMS |
41 | }; |
42 | ||
b3c590ce MR |
43 | #define MAX30102_REG_INT_STATUS 0x00 |
44 | #define MAX30102_REG_INT_STATUS_PWR_RDY BIT(0) | |
45 | #define MAX30102_REG_INT_STATUS_PROX_INT BIT(4) | |
46 | #define MAX30102_REG_INT_STATUS_ALC_OVF BIT(5) | |
47 | #define MAX30102_REG_INT_STATUS_PPG_RDY BIT(6) | |
48 | #define MAX30102_REG_INT_STATUS_FIFO_RDY BIT(7) | |
49 | ||
50 | #define MAX30102_REG_INT_ENABLE 0x02 | |
51 | #define MAX30102_REG_INT_ENABLE_PROX_INT_EN BIT(4) | |
52 | #define MAX30102_REG_INT_ENABLE_ALC_OVF_EN BIT(5) | |
53 | #define MAX30102_REG_INT_ENABLE_PPG_EN BIT(6) | |
54 | #define MAX30102_REG_INT_ENABLE_FIFO_EN BIT(7) | |
55 | #define MAX30102_REG_INT_ENABLE_MASK 0xf0 | |
56 | #define MAX30102_REG_INT_ENABLE_MASK_SHIFT 4 | |
57 | ||
58 | #define MAX30102_REG_FIFO_WR_PTR 0x04 | |
59 | #define MAX30102_REG_FIFO_OVR_CTR 0x05 | |
60 | #define MAX30102_REG_FIFO_RD_PTR 0x06 | |
61 | #define MAX30102_REG_FIFO_DATA 0x07 | |
fef79edc | 62 | #define MAX30102_REG_FIFO_DATA_BYTES 3 |
b3c590ce MR |
63 | |
64 | #define MAX30102_REG_FIFO_CONFIG 0x08 | |
65 | #define MAX30102_REG_FIFO_CONFIG_AVG_4SAMPLES BIT(1) | |
66 | #define MAX30102_REG_FIFO_CONFIG_AVG_SHIFT 5 | |
67 | #define MAX30102_REG_FIFO_CONFIG_AFULL BIT(0) | |
68 | ||
69 | #define MAX30102_REG_MODE_CONFIG 0x09 | |
83e6415d | 70 | #define MAX30102_REG_MODE_CONFIG_MODE_NONE 0x00 |
7b0b0ec1 PMS |
71 | #define MAX30102_REG_MODE_CONFIG_MODE_HR 0x02 /* red LED */ |
72 | #define MAX30102_REG_MODE_CONFIG_MODE_HR_SPO2 0x03 /* red + IR LED */ | |
73 | #define MAX30102_REG_MODE_CONFIG_MODE_MULTI 0x07 /* multi-LED mode */ | |
74 | #define MAX30102_REG_MODE_CONFIG_MODE_MASK GENMASK(2, 0) | |
b3c590ce MR |
75 | #define MAX30102_REG_MODE_CONFIG_PWR BIT(7) |
76 | ||
90579b69 PMS |
77 | #define MAX30102_REG_MODE_CONTROL_SLOT21 0x11 /* multi-LED control */ |
78 | #define MAX30102_REG_MODE_CONTROL_SLOT43 0x12 | |
79 | #define MAX30102_REG_MODE_CONTROL_SLOT_MASK (GENMASK(6, 4) | GENMASK(2, 0)) | |
80 | #define MAX30102_REG_MODE_CONTROL_SLOT_SHIFT 4 | |
81 | ||
b3c590ce MR |
82 | #define MAX30102_REG_SPO2_CONFIG 0x0a |
83 | #define MAX30102_REG_SPO2_CONFIG_PULSE_411_US 0x03 | |
84 | #define MAX30102_REG_SPO2_CONFIG_SR_400HZ 0x03 | |
85 | #define MAX30102_REG_SPO2_CONFIG_SR_MASK 0x07 | |
86 | #define MAX30102_REG_SPO2_CONFIG_SR_MASK_SHIFT 2 | |
87 | #define MAX30102_REG_SPO2_CONFIG_ADC_4096_STEPS BIT(0) | |
88 | #define MAX30102_REG_SPO2_CONFIG_ADC_MASK_SHIFT 5 | |
89 | ||
90 | #define MAX30102_REG_RED_LED_CONFIG 0x0c | |
91 | #define MAX30102_REG_IR_LED_CONFIG 0x0d | |
90579b69 | 92 | #define MAX30105_REG_GREEN_LED_CONFIG 0x0e |
b3c590ce MR |
93 | |
94 | #define MAX30102_REG_TEMP_CONFIG 0x21 | |
95 | #define MAX30102_REG_TEMP_CONFIG_TEMP_EN BIT(0) | |
96 | ||
97 | #define MAX30102_REG_TEMP_INTEGER 0x1f | |
98 | #define MAX30102_REG_TEMP_FRACTION 0x20 | |
99 | ||
84b0ce05 PMS |
100 | #define MAX30102_REG_REV_ID 0xfe |
101 | #define MAX30102_REG_PART_ID 0xff | |
102 | ||
b3c590ce MR |
103 | struct max30102_data { |
104 | struct i2c_client *client; | |
105 | struct iio_dev *indio_dev; | |
106 | struct mutex lock; | |
107 | struct regmap *regmap; | |
90579b69 | 108 | enum max30102_chip_id chip_id; |
b3c590ce | 109 | |
90579b69 PMS |
110 | u8 buffer[12]; |
111 | __be32 processed_buffer[3]; /* 3 x 18-bit (padded to 32-bits) */ | |
b3c590ce MR |
112 | }; |
113 | ||
114 | static const struct regmap_config max30102_regmap_config = { | |
115 | .name = MAX30102_REGMAP_NAME, | |
116 | ||
117 | .reg_bits = 8, | |
118 | .val_bits = 8, | |
119 | }; | |
120 | ||
ad90e570 PMS |
121 | static const unsigned long max30102_scan_masks[] = { |
122 | BIT(MAX30102_LED_RED) | BIT(MAX30102_LED_IR), | |
123 | 0 | |
124 | }; | |
b3c590ce | 125 | |
90579b69 PMS |
126 | static const unsigned long max30105_scan_masks[] = { |
127 | BIT(MAX30102_LED_RED) | BIT(MAX30102_LED_IR), | |
128 | BIT(MAX30102_LED_RED) | BIT(MAX30102_LED_IR) | | |
129 | BIT(MAX30105_LED_GREEN), | |
130 | 0 | |
131 | }; | |
132 | ||
839a74cd PMS |
133 | #define MAX30102_INTENSITY_CHANNEL(_si, _mod) { \ |
134 | .type = IIO_INTENSITY, \ | |
135 | .channel2 = _mod, \ | |
136 | .modified = 1, \ | |
137 | .scan_index = _si, \ | |
138 | .scan_type = { \ | |
139 | .sign = 'u', \ | |
140 | .shift = 8, \ | |
141 | .realbits = 18, \ | |
142 | .storagebits = 32, \ | |
143 | .endianness = IIO_BE, \ | |
144 | }, \ | |
145 | } | |
146 | ||
b3c590ce | 147 | static const struct iio_chan_spec max30102_channels[] = { |
ad90e570 PMS |
148 | MAX30102_INTENSITY_CHANNEL(MAX30102_LED_RED, IIO_MOD_LIGHT_RED), |
149 | MAX30102_INTENSITY_CHANNEL(MAX30102_LED_IR, IIO_MOD_LIGHT_IR), | |
b3c590ce MR |
150 | { |
151 | .type = IIO_TEMP, | |
152 | .info_mask_separate = | |
153 | BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), | |
154 | .scan_index = -1, | |
155 | }, | |
156 | }; | |
157 | ||
90579b69 PMS |
158 | static const struct iio_chan_spec max30105_channels[] = { |
159 | MAX30102_INTENSITY_CHANNEL(MAX30102_LED_RED, IIO_MOD_LIGHT_RED), | |
160 | MAX30102_INTENSITY_CHANNEL(MAX30102_LED_IR, IIO_MOD_LIGHT_IR), | |
161 | MAX30102_INTENSITY_CHANNEL(MAX30105_LED_GREEN, IIO_MOD_LIGHT_GREEN), | |
162 | { | |
163 | .type = IIO_TEMP, | |
164 | .info_mask_separate = | |
165 | BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), | |
166 | .scan_index = -1, | |
167 | }, | |
168 | }; | |
169 | ||
83e6415d | 170 | static int max30102_set_power(struct max30102_data *data, bool en) |
b3c590ce MR |
171 | { |
172 | return regmap_update_bits(data->regmap, MAX30102_REG_MODE_CONFIG, | |
173 | MAX30102_REG_MODE_CONFIG_PWR, | |
83e6415d PMS |
174 | en ? 0 : MAX30102_REG_MODE_CONFIG_PWR); |
175 | } | |
176 | ||
177 | static int max30102_set_powermode(struct max30102_data *data, u8 mode, bool en) | |
178 | { | |
179 | u8 reg = mode; | |
180 | ||
181 | if (!en) | |
182 | reg |= MAX30102_REG_MODE_CONFIG_PWR; | |
183 | ||
184 | return regmap_update_bits(data->regmap, MAX30102_REG_MODE_CONFIG, | |
185 | MAX30102_REG_MODE_CONFIG_PWR | | |
186 | MAX30102_REG_MODE_CONFIG_MODE_MASK, reg); | |
b3c590ce MR |
187 | } |
188 | ||
90579b69 PMS |
189 | #define MAX30102_MODE_CONTROL_LED_SLOTS(slot2, slot1) \ |
190 | ((slot2 << MAX30102_REG_MODE_CONTROL_SLOT_SHIFT) | slot1) | |
191 | ||
b3c590ce MR |
192 | static int max30102_buffer_postenable(struct iio_dev *indio_dev) |
193 | { | |
194 | struct max30102_data *data = iio_priv(indio_dev); | |
90579b69 | 195 | int ret; |
83e6415d PMS |
196 | u8 reg; |
197 | ||
90579b69 PMS |
198 | switch (*indio_dev->active_scan_mask) { |
199 | case BIT(MAX30102_LED_RED) | BIT(MAX30102_LED_IR): | |
200 | reg = MAX30102_REG_MODE_CONFIG_MODE_HR_SPO2; | |
201 | break; | |
202 | case BIT(MAX30102_LED_RED) | BIT(MAX30102_LED_IR) | | |
203 | BIT(MAX30105_LED_GREEN): | |
204 | ret = regmap_update_bits(data->regmap, | |
205 | MAX30102_REG_MODE_CONTROL_SLOT21, | |
206 | MAX30102_REG_MODE_CONTROL_SLOT_MASK, | |
207 | MAX30102_MODE_CONTROL_LED_SLOTS(2, 1)); | |
208 | if (ret) | |
209 | return ret; | |
210 | ||
211 | ret = regmap_update_bits(data->regmap, | |
212 | MAX30102_REG_MODE_CONTROL_SLOT43, | |
213 | MAX30102_REG_MODE_CONTROL_SLOT_MASK, | |
214 | MAX30102_MODE_CONTROL_LED_SLOTS(0, 3)); | |
215 | if (ret) | |
216 | return ret; | |
217 | ||
218 | reg = MAX30102_REG_MODE_CONFIG_MODE_MULTI; | |
219 | break; | |
220 | default: | |
221 | return -EINVAL; | |
222 | } | |
b3c590ce | 223 | |
83e6415d | 224 | return max30102_set_powermode(data, reg, true); |
b3c590ce MR |
225 | } |
226 | ||
227 | static int max30102_buffer_predisable(struct iio_dev *indio_dev) | |
228 | { | |
229 | struct max30102_data *data = iio_priv(indio_dev); | |
230 | ||
83e6415d PMS |
231 | return max30102_set_powermode(data, MAX30102_REG_MODE_CONFIG_MODE_NONE, |
232 | false); | |
b3c590ce MR |
233 | } |
234 | ||
235 | static const struct iio_buffer_setup_ops max30102_buffer_setup_ops = { | |
236 | .postenable = max30102_buffer_postenable, | |
237 | .predisable = max30102_buffer_predisable, | |
238 | }; | |
239 | ||
240 | static inline int max30102_fifo_count(struct max30102_data *data) | |
241 | { | |
242 | unsigned int val; | |
243 | int ret; | |
244 | ||
245 | ret = regmap_read(data->regmap, MAX30102_REG_INT_STATUS, &val); | |
246 | if (ret) | |
247 | return ret; | |
248 | ||
249 | /* FIFO has one sample slot left */ | |
250 | if (val & MAX30102_REG_INT_STATUS_FIFO_RDY) | |
251 | return 1; | |
252 | ||
253 | return 0; | |
254 | } | |
255 | ||
fef79edc PMS |
256 | #define MAX30102_COPY_DATA(i) \ |
257 | memcpy(&data->processed_buffer[(i)], \ | |
258 | &buffer[(i) * MAX30102_REG_FIFO_DATA_BYTES], \ | |
259 | MAX30102_REG_FIFO_DATA_BYTES) | |
260 | ||
90579b69 PMS |
261 | static int max30102_read_measurement(struct max30102_data *data, |
262 | unsigned int measurements) | |
b3c590ce MR |
263 | { |
264 | int ret; | |
265 | u8 *buffer = (u8 *) &data->buffer; | |
266 | ||
267 | ret = i2c_smbus_read_i2c_block_data(data->client, | |
268 | MAX30102_REG_FIFO_DATA, | |
90579b69 PMS |
269 | measurements * |
270 | MAX30102_REG_FIFO_DATA_BYTES, | |
b3c590ce MR |
271 | buffer); |
272 | ||
90579b69 PMS |
273 | switch (measurements) { |
274 | case 3: | |
275 | MAX30102_COPY_DATA(2); | |
df561f66 | 276 | fallthrough; |
9ffa68f6 | 277 | case 2: |
90579b69 | 278 | MAX30102_COPY_DATA(1); |
df561f66 | 279 | fallthrough; |
9ffa68f6 | 280 | case 1: |
90579b69 PMS |
281 | MAX30102_COPY_DATA(0); |
282 | break; | |
283 | default: | |
284 | return -EINVAL; | |
285 | } | |
b3c590ce | 286 | |
90579b69 PMS |
287 | return (ret == measurements * MAX30102_REG_FIFO_DATA_BYTES) ? |
288 | 0 : -EINVAL; | |
b3c590ce MR |
289 | } |
290 | ||
291 | static irqreturn_t max30102_interrupt_handler(int irq, void *private) | |
292 | { | |
293 | struct iio_dev *indio_dev = private; | |
294 | struct max30102_data *data = iio_priv(indio_dev); | |
90579b69 PMS |
295 | unsigned int measurements = bitmap_weight(indio_dev->active_scan_mask, |
296 | indio_dev->masklength); | |
b3c590ce MR |
297 | int ret, cnt = 0; |
298 | ||
299 | mutex_lock(&data->lock); | |
300 | ||
301 | while (cnt || (cnt = max30102_fifo_count(data)) > 0) { | |
90579b69 | 302 | ret = max30102_read_measurement(data, measurements); |
b3c590ce MR |
303 | if (ret) |
304 | break; | |
305 | ||
306 | iio_push_to_buffers(data->indio_dev, data->processed_buffer); | |
307 | cnt--; | |
308 | } | |
309 | ||
310 | mutex_unlock(&data->lock); | |
311 | ||
312 | return IRQ_HANDLED; | |
313 | } | |
314 | ||
315 | static int max30102_get_current_idx(unsigned int val, int *reg) | |
316 | { | |
317 | /* each step is 0.200 mA */ | |
318 | *reg = val / 200; | |
319 | ||
320 | return *reg > 0xff ? -EINVAL : 0; | |
321 | } | |
322 | ||
323 | static int max30102_led_init(struct max30102_data *data) | |
324 | { | |
325 | struct device *dev = &data->client->dev; | |
326 | struct device_node *np = dev->of_node; | |
327 | unsigned int val; | |
328 | int reg, ret; | |
329 | ||
330 | ret = of_property_read_u32(np, "maxim,red-led-current-microamp", &val); | |
331 | if (ret) { | |
332 | dev_info(dev, "no red-led-current-microamp set\n"); | |
333 | ||
334 | /* Default to 7 mA RED LED */ | |
335 | val = 7000; | |
336 | } | |
337 | ||
338 | ret = max30102_get_current_idx(val, ®); | |
339 | if (ret) { | |
340 | dev_err(dev, "invalid RED LED current setting %d\n", val); | |
341 | return ret; | |
342 | } | |
343 | ||
344 | ret = regmap_write(data->regmap, MAX30102_REG_RED_LED_CONFIG, reg); | |
345 | if (ret) | |
346 | return ret; | |
347 | ||
90579b69 PMS |
348 | if (data->chip_id == max30105) { |
349 | ret = of_property_read_u32(np, | |
350 | "maxim,green-led-current-microamp", &val); | |
351 | if (ret) { | |
352 | dev_info(dev, "no green-led-current-microamp set\n"); | |
353 | ||
354 | /* Default to 7 mA green LED */ | |
355 | val = 7000; | |
356 | } | |
357 | ||
358 | ret = max30102_get_current_idx(val, ®); | |
359 | if (ret) { | |
360 | dev_err(dev, "invalid green LED current setting %d\n", | |
361 | val); | |
362 | return ret; | |
363 | } | |
364 | ||
365 | ret = regmap_write(data->regmap, MAX30105_REG_GREEN_LED_CONFIG, | |
366 | reg); | |
367 | if (ret) | |
368 | return ret; | |
369 | } | |
370 | ||
b3c590ce MR |
371 | ret = of_property_read_u32(np, "maxim,ir-led-current-microamp", &val); |
372 | if (ret) { | |
373 | dev_info(dev, "no ir-led-current-microamp set\n"); | |
374 | ||
375 | /* Default to 7 mA IR LED */ | |
376 | val = 7000; | |
377 | } | |
378 | ||
379 | ret = max30102_get_current_idx(val, ®); | |
380 | if (ret) { | |
dd86dbf9 | 381 | dev_err(dev, "invalid IR LED current setting %d\n", val); |
b3c590ce MR |
382 | return ret; |
383 | } | |
384 | ||
385 | return regmap_write(data->regmap, MAX30102_REG_IR_LED_CONFIG, reg); | |
386 | } | |
387 | ||
388 | static int max30102_chip_init(struct max30102_data *data) | |
389 | { | |
390 | int ret; | |
391 | ||
392 | /* setup LED current settings */ | |
393 | ret = max30102_led_init(data); | |
394 | if (ret) | |
395 | return ret; | |
396 | ||
83e6415d | 397 | /* configure 18-bit HR + SpO2 readings at 400Hz */ |
b3c590ce MR |
398 | ret = regmap_write(data->regmap, MAX30102_REG_SPO2_CONFIG, |
399 | (MAX30102_REG_SPO2_CONFIG_ADC_4096_STEPS | |
400 | << MAX30102_REG_SPO2_CONFIG_ADC_MASK_SHIFT) | | |
401 | (MAX30102_REG_SPO2_CONFIG_SR_400HZ | |
402 | << MAX30102_REG_SPO2_CONFIG_SR_MASK_SHIFT) | | |
403 | MAX30102_REG_SPO2_CONFIG_PULSE_411_US); | |
404 | if (ret) | |
405 | return ret; | |
406 | ||
b3c590ce MR |
407 | /* average 4 samples + generate FIFO interrupt */ |
408 | ret = regmap_write(data->regmap, MAX30102_REG_FIFO_CONFIG, | |
409 | (MAX30102_REG_FIFO_CONFIG_AVG_4SAMPLES | |
410 | << MAX30102_REG_FIFO_CONFIG_AVG_SHIFT) | | |
411 | MAX30102_REG_FIFO_CONFIG_AFULL); | |
412 | if (ret) | |
413 | return ret; | |
414 | ||
415 | /* enable FIFO interrupt */ | |
416 | return regmap_update_bits(data->regmap, MAX30102_REG_INT_ENABLE, | |
417 | MAX30102_REG_INT_ENABLE_MASK, | |
418 | MAX30102_REG_INT_ENABLE_FIFO_EN); | |
419 | } | |
420 | ||
421 | static int max30102_read_temp(struct max30102_data *data, int *val) | |
422 | { | |
423 | int ret; | |
424 | unsigned int reg; | |
425 | ||
426 | ret = regmap_read(data->regmap, MAX30102_REG_TEMP_INTEGER, ®); | |
427 | if (ret < 0) | |
428 | return ret; | |
429 | *val = reg << 4; | |
430 | ||
431 | ret = regmap_read(data->regmap, MAX30102_REG_TEMP_FRACTION, ®); | |
432 | if (ret < 0) | |
433 | return ret; | |
434 | ||
435 | *val |= reg & 0xf; | |
436 | *val = sign_extend32(*val, 11); | |
437 | ||
438 | return 0; | |
439 | } | |
440 | ||
a9c47abb | 441 | static int max30102_get_temp(struct max30102_data *data, int *val, bool en) |
b3c590ce MR |
442 | { |
443 | int ret; | |
444 | ||
a9c47abb | 445 | if (en) { |
83e6415d | 446 | ret = max30102_set_power(data, true); |
a9c47abb PMS |
447 | if (ret) |
448 | return ret; | |
449 | } | |
450 | ||
b3c590ce MR |
451 | /* start acquisition */ |
452 | ret = regmap_update_bits(data->regmap, MAX30102_REG_TEMP_CONFIG, | |
453 | MAX30102_REG_TEMP_CONFIG_TEMP_EN, | |
454 | MAX30102_REG_TEMP_CONFIG_TEMP_EN); | |
455 | if (ret) | |
a9c47abb | 456 | goto out; |
b3c590ce MR |
457 | |
458 | msleep(35); | |
a9c47abb PMS |
459 | ret = max30102_read_temp(data, val); |
460 | ||
461 | out: | |
462 | if (en) | |
83e6415d | 463 | max30102_set_power(data, false); |
b3c590ce | 464 | |
a9c47abb | 465 | return ret; |
b3c590ce MR |
466 | } |
467 | ||
468 | static int max30102_read_raw(struct iio_dev *indio_dev, | |
469 | struct iio_chan_spec const *chan, | |
470 | int *val, int *val2, long mask) | |
471 | { | |
472 | struct max30102_data *data = iio_priv(indio_dev); | |
473 | int ret = -EINVAL; | |
474 | ||
475 | switch (mask) { | |
476 | case IIO_CHAN_INFO_RAW: | |
477 | /* | |
a9c47abb PMS |
478 | * Temperature reading can only be acquired when not in |
479 | * shutdown; leave shutdown briefly when buffer not running | |
b3c590ce MR |
480 | */ |
481 | mutex_lock(&indio_dev->mlock); | |
b3c590ce | 482 | if (!iio_buffer_enabled(indio_dev)) |
a9c47abb PMS |
483 | ret = max30102_get_temp(data, val, true); |
484 | else | |
485 | ret = max30102_get_temp(data, val, false); | |
b3c590ce | 486 | mutex_unlock(&indio_dev->mlock); |
a9c47abb PMS |
487 | if (ret) |
488 | return ret; | |
489 | ||
490 | ret = IIO_VAL_INT; | |
b3c590ce MR |
491 | break; |
492 | case IIO_CHAN_INFO_SCALE: | |
ad44a9f8 | 493 | *val = 1000; /* 62.5 */ |
b3c590ce MR |
494 | *val2 = 16; |
495 | ret = IIO_VAL_FRACTIONAL; | |
496 | break; | |
497 | } | |
498 | ||
499 | return ret; | |
500 | } | |
501 | ||
502 | static const struct iio_info max30102_info = { | |
b3c590ce MR |
503 | .read_raw = max30102_read_raw, |
504 | }; | |
505 | ||
506 | static int max30102_probe(struct i2c_client *client, | |
507 | const struct i2c_device_id *id) | |
508 | { | |
509 | struct max30102_data *data; | |
510 | struct iio_buffer *buffer; | |
511 | struct iio_dev *indio_dev; | |
512 | int ret; | |
84b0ce05 | 513 | unsigned int reg; |
b3c590ce MR |
514 | |
515 | indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); | |
516 | if (!indio_dev) | |
517 | return -ENOMEM; | |
518 | ||
519 | buffer = devm_iio_kfifo_allocate(&client->dev); | |
520 | if (!buffer) | |
521 | return -ENOMEM; | |
522 | ||
523 | iio_device_attach_buffer(indio_dev, buffer); | |
524 | ||
525 | indio_dev->name = MAX30102_DRV_NAME; | |
b3c590ce | 526 | indio_dev->info = &max30102_info; |
b3c590ce MR |
527 | indio_dev->modes = (INDIO_BUFFER_SOFTWARE | INDIO_DIRECT_MODE); |
528 | indio_dev->setup_ops = &max30102_buffer_setup_ops; | |
529 | ||
530 | data = iio_priv(indio_dev); | |
531 | data->indio_dev = indio_dev; | |
532 | data->client = client; | |
90579b69 | 533 | data->chip_id = id->driver_data; |
b3c590ce MR |
534 | |
535 | mutex_init(&data->lock); | |
536 | i2c_set_clientdata(client, indio_dev); | |
537 | ||
90579b69 PMS |
538 | switch (data->chip_id) { |
539 | case max30105: | |
540 | indio_dev->channels = max30105_channels; | |
541 | indio_dev->num_channels = ARRAY_SIZE(max30105_channels); | |
542 | indio_dev->available_scan_masks = max30105_scan_masks; | |
543 | break; | |
544 | case max30102: | |
545 | indio_dev->channels = max30102_channels; | |
546 | indio_dev->num_channels = ARRAY_SIZE(max30102_channels); | |
547 | indio_dev->available_scan_masks = max30102_scan_masks; | |
548 | break; | |
549 | default: | |
550 | return -ENODEV; | |
551 | } | |
552 | ||
b3c590ce MR |
553 | data->regmap = devm_regmap_init_i2c(client, &max30102_regmap_config); |
554 | if (IS_ERR(data->regmap)) { | |
c31c946a | 555 | dev_err(&client->dev, "regmap initialization failed\n"); |
b3c590ce MR |
556 | return PTR_ERR(data->regmap); |
557 | } | |
d0b950c7 | 558 | |
84b0ce05 PMS |
559 | /* check part ID */ |
560 | ret = regmap_read(data->regmap, MAX30102_REG_PART_ID, ®); | |
561 | if (ret) | |
562 | return ret; | |
563 | if (reg != MAX30102_PART_NUMBER) | |
564 | return -ENODEV; | |
565 | ||
566 | /* show revision ID */ | |
567 | ret = regmap_read(data->regmap, MAX30102_REG_REV_ID, ®); | |
568 | if (ret) | |
569 | return ret; | |
570 | dev_dbg(&client->dev, "max3010x revision %02x\n", reg); | |
571 | ||
83e6415d PMS |
572 | /* clear mode setting, chip shutdown */ |
573 | ret = max30102_set_powermode(data, MAX30102_REG_MODE_CONFIG_MODE_NONE, | |
574 | false); | |
d0b950c7 PMS |
575 | if (ret) |
576 | return ret; | |
b3c590ce MR |
577 | |
578 | ret = max30102_chip_init(data); | |
579 | if (ret) | |
580 | return ret; | |
581 | ||
582 | if (client->irq <= 0) { | |
583 | dev_err(&client->dev, "no valid irq defined\n"); | |
584 | return -EINVAL; | |
585 | } | |
586 | ||
587 | ret = devm_request_threaded_irq(&client->dev, client->irq, | |
588 | NULL, max30102_interrupt_handler, | |
589 | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, | |
590 | "max30102_irq", indio_dev); | |
591 | if (ret) { | |
592 | dev_err(&client->dev, "request irq (%d) failed\n", client->irq); | |
593 | return ret; | |
594 | } | |
595 | ||
596 | return iio_device_register(indio_dev); | |
597 | } | |
598 | ||
599 | static int max30102_remove(struct i2c_client *client) | |
600 | { | |
601 | struct iio_dev *indio_dev = i2c_get_clientdata(client); | |
602 | struct max30102_data *data = iio_priv(indio_dev); | |
603 | ||
604 | iio_device_unregister(indio_dev); | |
83e6415d | 605 | max30102_set_power(data, false); |
b3c590ce MR |
606 | |
607 | return 0; | |
608 | } | |
609 | ||
610 | static const struct i2c_device_id max30102_id[] = { | |
90579b69 PMS |
611 | { "max30102", max30102 }, |
612 | { "max30105", max30105 }, | |
b3c590ce MR |
613 | {} |
614 | }; | |
615 | MODULE_DEVICE_TABLE(i2c, max30102_id); | |
616 | ||
617 | static const struct of_device_id max30102_dt_ids[] = { | |
618 | { .compatible = "maxim,max30102" }, | |
90579b69 | 619 | { .compatible = "maxim,max30105" }, |
b3c590ce MR |
620 | { } |
621 | }; | |
622 | MODULE_DEVICE_TABLE(of, max30102_dt_ids); | |
623 | ||
624 | static struct i2c_driver max30102_driver = { | |
625 | .driver = { | |
626 | .name = MAX30102_DRV_NAME, | |
627 | .of_match_table = of_match_ptr(max30102_dt_ids), | |
628 | }, | |
629 | .probe = max30102_probe, | |
630 | .remove = max30102_remove, | |
631 | .id_table = max30102_id, | |
632 | }; | |
633 | module_i2c_driver(max30102_driver); | |
634 | ||
635 | MODULE_AUTHOR("Matt Ranostay <matt@ranostay.consulting>"); | |
90579b69 | 636 | MODULE_DESCRIPTION("MAX30102 heart rate/pulse oximeter and MAX30105 particle sensor driver"); |
b3c590ce | 637 | MODULE_LICENSE("GPL"); |