summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatt Ranostay <mranostay@gmail.com>2015-12-10 01:04:49 -0500
committerJonathan Cameron <jic23@kernel.org>2015-12-12 07:14:37 -0500
commit4d33615df58bf308626489cbfb8acbc8bbd45658 (patch)
tree2d6d54bf0408df7066312b42a8f7d4775a3dd8ba
parentb41fa86b67bd338d4ffa0b69f0fb1c3013a489e0 (diff)
iio: light: add MAX30100 oximeter driver support
MAX30100 is an heart rate and pulse oximeter sensor that works using two LEDS of different wavelengths, and detecting the light reflected back. This patchset adds support for both IR and RED LED channels which can be processed in userspace to determine heart rate and blood oxygen levels. Signed-off-by: Matt Ranostay <mranostay@gmail.com> Signed-off-by: Jonathan Cameron <jic23@kernel.org>
-rw-r--r--Documentation/devicetree/bindings/iio/health/max30100.txt21
-rw-r--r--drivers/iio/Kconfig1
-rw-r--r--drivers/iio/Makefile1
-rw-r--r--drivers/iio/health/Kconfig21
-rw-r--r--drivers/iio/health/Makefile7
-rw-r--r--drivers/iio/health/max30100.c453
6 files changed, 504 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/iio/health/max30100.txt b/Documentation/devicetree/bindings/iio/health/max30100.txt
new file mode 100644
index 000000000000..f6fbac66ad06
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/health/max30100.txt
@@ -0,0 +1,21 @@
1Maxim MAX30100 heart rate and pulse oximeter sensor
2
3* https://datasheets.maximintegrated.com/en/ds/MAX30100.pdf
4
5Required properties:
6 - compatible: must be "maxim,max30100"
7 - reg: the I2C address of the sensor
8 - interrupt-parent: should be the phandle for the interrupt controller
9 - interrupts: the sole interrupt generated by the device
10
11 Refer to interrupt-controller/interrupts.txt for generic
12 interrupt client node bindings.
13
14Example:
15
16max30100@057 {
17 compatible = "maxim,max30100";
18 reg = <57>;
19 interrupt-parent = <&gpio1>;
20 interrupts = <16 2>;
21};
diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
index ac8715e56ba1..505e921f0b19 100644
--- a/drivers/iio/Kconfig
+++ b/drivers/iio/Kconfig
@@ -69,6 +69,7 @@ source "drivers/iio/dac/Kconfig"
69source "drivers/iio/dummy/Kconfig" 69source "drivers/iio/dummy/Kconfig"
70source "drivers/iio/frequency/Kconfig" 70source "drivers/iio/frequency/Kconfig"
71source "drivers/iio/gyro/Kconfig" 71source "drivers/iio/gyro/Kconfig"
72source "drivers/iio/health/Kconfig"
72source "drivers/iio/humidity/Kconfig" 73source "drivers/iio/humidity/Kconfig"
73source "drivers/iio/imu/Kconfig" 74source "drivers/iio/imu/Kconfig"
74source "drivers/iio/light/Kconfig" 75source "drivers/iio/light/Kconfig"
diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
index f670b82298aa..20f649073462 100644
--- a/drivers/iio/Makefile
+++ b/drivers/iio/Makefile
@@ -21,6 +21,7 @@ obj-y += dac/
21obj-y += dummy/ 21obj-y += dummy/
22obj-y += gyro/ 22obj-y += gyro/
23obj-y += frequency/ 23obj-y += frequency/
24obj-y += health/
24obj-y += humidity/ 25obj-y += humidity/
25obj-y += imu/ 26obj-y += imu/
26obj-y += light/ 27obj-y += light/
diff --git a/drivers/iio/health/Kconfig b/drivers/iio/health/Kconfig
new file mode 100644
index 000000000000..a647679da805
--- /dev/null
+++ b/drivers/iio/health/Kconfig
@@ -0,0 +1,21 @@
1#
2# Health sensors
3#
4# When adding new entries keep the list in alphabetical order
5
6menu "Health sensors"
7
8config MAX30100
9 tristate "MAX30100 heart rate and pulse oximeter sensor"
10 depends on I2C
11 select REGMAP_I2C
12 select IIO_BUFFER
13 select IIO_KFIFO_BUF
14 help
15 Say Y here to build I2C interface support for the Maxim
16 MAX30100 heart rate, and pulse oximeter sensor.
17
18 To compile this driver as a module, choose M here: the
19 module will be called max30100.
20
21endmenu
diff --git a/drivers/iio/health/Makefile b/drivers/iio/health/Makefile
new file mode 100644
index 000000000000..7c475d7faad8
--- /dev/null
+++ b/drivers/iio/health/Makefile
@@ -0,0 +1,7 @@
1#
2# Makefile for IIO Health sensors
3#
4
5# When adding new entries keep the list in alphabetical order
6
7obj-$(CONFIG_MAX30100) += max30100.o
diff --git a/drivers/iio/health/max30100.c b/drivers/iio/health/max30100.c
new file mode 100644
index 000000000000..9d1c81f91dd7
--- /dev/null
+++ b/drivers/iio/health/max30100.c
@@ -0,0 +1,453 @@
1/*
2 * max30100.c - Support for MAX30100 heart rate and pulse oximeter sensor
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 * TODO: allow LED current and pulse length controls via device tree properties
17 */
18
19#include <linux/module.h>
20#include <linux/init.h>
21#include <linux/interrupt.h>
22#include <linux/delay.h>
23#include <linux/err.h>
24#include <linux/irq.h>
25#include <linux/i2c.h>
26#include <linux/mutex.h>
27#include <linux/regmap.h>
28#include <linux/iio/iio.h>
29#include <linux/iio/buffer.h>
30#include <linux/iio/kfifo_buf.h>
31
32#define MAX30100_REGMAP_NAME "max30100_regmap"
33#define MAX30100_DRV_NAME "max30100"
34
35#define MAX30100_REG_INT_STATUS 0x00
36#define MAX30100_REG_INT_STATUS_PWR_RDY BIT(0)
37#define MAX30100_REG_INT_STATUS_SPO2_RDY BIT(4)
38#define MAX30100_REG_INT_STATUS_HR_RDY BIT(5)
39#define MAX30100_REG_INT_STATUS_FIFO_RDY BIT(7)
40
41#define MAX30100_REG_INT_ENABLE 0x01
42#define MAX30100_REG_INT_ENABLE_SPO2_EN BIT(0)
43#define MAX30100_REG_INT_ENABLE_HR_EN BIT(1)
44#define MAX30100_REG_INT_ENABLE_FIFO_EN BIT(3)
45#define MAX30100_REG_INT_ENABLE_MASK 0xf0
46#define MAX30100_REG_INT_ENABLE_MASK_SHIFT 4
47
48#define MAX30100_REG_FIFO_WR_PTR 0x02
49#define MAX30100_REG_FIFO_OVR_CTR 0x03
50#define MAX30100_REG_FIFO_RD_PTR 0x04
51#define MAX30100_REG_FIFO_DATA 0x05
52#define MAX30100_REG_FIFO_DATA_ENTRY_COUNT 16
53#define MAX30100_REG_FIFO_DATA_ENTRY_LEN 4
54
55#define MAX30100_REG_MODE_CONFIG 0x06
56#define MAX30100_REG_MODE_CONFIG_MODE_SPO2_EN BIT(0)
57#define MAX30100_REG_MODE_CONFIG_MODE_HR_EN BIT(1)
58#define MAX30100_REG_MODE_CONFIG_MODE_MASK 0x03
59#define MAX30100_REG_MODE_CONFIG_TEMP_EN BIT(3)
60#define MAX30100_REG_MODE_CONFIG_PWR BIT(7)
61
62#define MAX30100_REG_SPO2_CONFIG 0x07
63#define MAX30100_REG_SPO2_CONFIG_100HZ BIT(2)
64#define MAX30100_REG_SPO2_CONFIG_HI_RES_EN BIT(6)
65#define MAX30100_REG_SPO2_CONFIG_1600US 0x3
66
67#define MAX30100_REG_LED_CONFIG 0x09
68#define MAX30100_REG_LED_CONFIG_RED_LED_SHIFT 4
69
70#define MAX30100_REG_LED_CONFIG_24MA 0x07
71#define MAX30100_REG_LED_CONFIG_50MA 0x0f
72
73#define MAX30100_REG_TEMP_INTEGER 0x16
74#define MAX30100_REG_TEMP_FRACTION 0x17
75
76struct max30100_data {
77 struct i2c_client *client;
78 struct iio_dev *indio_dev;
79 struct mutex lock;
80 struct regmap *regmap;
81
82 __be16 buffer[2]; /* 2 16-bit channels */
83};
84
85static bool max30100_is_volatile_reg(struct device *dev, unsigned int reg)
86{
87 switch (reg) {
88 case MAX30100_REG_INT_STATUS:
89 case MAX30100_REG_MODE_CONFIG:
90 case MAX30100_REG_FIFO_WR_PTR:
91 case MAX30100_REG_FIFO_OVR_CTR:
92 case MAX30100_REG_FIFO_RD_PTR:
93 case MAX30100_REG_FIFO_DATA:
94 case MAX30100_REG_TEMP_INTEGER:
95 case MAX30100_REG_TEMP_FRACTION:
96 return true;
97 default:
98 return false;
99 }
100}
101
102static const struct regmap_config max30100_regmap_config = {
103 .name = MAX30100_REGMAP_NAME,
104
105 .reg_bits = 8,
106 .val_bits = 8,
107
108 .max_register = MAX30100_REG_TEMP_FRACTION,
109 .cache_type = REGCACHE_FLAT,
110
111 .volatile_reg = max30100_is_volatile_reg,
112};
113
114static const unsigned long max30100_scan_masks[] = {0x3, 0};
115
116static const struct iio_chan_spec max30100_channels[] = {
117 {
118 .type = IIO_INTENSITY,
119 .channel2 = IIO_MOD_LIGHT_IR,
120 .modified = 1,
121
122 .scan_index = 0,
123 .scan_type = {
124 .sign = 'u',
125 .realbits = 16,
126 .storagebits = 16,
127 .endianness = IIO_BE,
128 },
129 },
130 {
131 .type = IIO_INTENSITY,
132 .channel2 = IIO_MOD_LIGHT_RED,
133 .modified = 1,
134
135 .scan_index = 1,
136 .scan_type = {
137 .sign = 'u',
138 .realbits = 16,
139 .storagebits = 16,
140 .endianness = IIO_BE,
141 },
142 },
143 {
144 .type = IIO_TEMP,
145 .info_mask_separate =
146 BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
147 .scan_index = -1,
148 },
149};
150
151static int max30100_set_powermode(struct max30100_data *data, bool state)
152{
153 return regmap_update_bits(data->regmap, MAX30100_REG_MODE_CONFIG,
154 MAX30100_REG_MODE_CONFIG_PWR,
155 state ? 0 : MAX30100_REG_MODE_CONFIG_PWR);
156}
157
158static int max30100_clear_fifo(struct max30100_data *data)
159{
160 int ret;
161
162 ret = regmap_write(data->regmap, MAX30100_REG_FIFO_WR_PTR, 0);
163 if (ret)
164 return ret;
165
166 ret = regmap_write(data->regmap, MAX30100_REG_FIFO_OVR_CTR, 0);
167 if (ret)
168 return ret;
169
170 return regmap_write(data->regmap, MAX30100_REG_FIFO_RD_PTR, 0);
171}
172
173static int max30100_buffer_postenable(struct iio_dev *indio_dev)
174{
175 struct max30100_data *data = iio_priv(indio_dev);
176 int ret;
177
178 ret = max30100_set_powermode(data, true);
179 if (ret)
180 return ret;
181
182 return max30100_clear_fifo(data);
183}
184
185static int max30100_buffer_predisable(struct iio_dev *indio_dev)
186{
187 struct max30100_data *data = iio_priv(indio_dev);
188
189 return max30100_set_powermode(data, false);
190}
191
192static const struct iio_buffer_setup_ops max30100_buffer_setup_ops = {
193 .postenable = max30100_buffer_postenable,
194 .predisable = max30100_buffer_predisable,
195};
196
197static inline int max30100_fifo_count(struct max30100_data *data)
198{
199 unsigned int val;
200 int ret;
201
202 ret = regmap_read(data->regmap, MAX30100_REG_INT_STATUS, &val);
203 if (ret)
204 return ret;
205
206 /* FIFO is almost full */
207 if (val & MAX30100_REG_INT_STATUS_FIFO_RDY)
208 return MAX30100_REG_FIFO_DATA_ENTRY_COUNT - 1;
209
210 return 0;
211}
212
213static int max30100_read_measurement(struct max30100_data *data)
214{
215 int ret;
216
217 ret = i2c_smbus_read_i2c_block_data(data->client,
218 MAX30100_REG_FIFO_DATA,
219 MAX30100_REG_FIFO_DATA_ENTRY_LEN,
220 (u8 *) &data->buffer);
221
222 return (ret == MAX30100_REG_FIFO_DATA_ENTRY_LEN) ? 0 : ret;
223}
224
225static irqreturn_t max30100_interrupt_handler(int irq, void *private)
226{
227 struct iio_dev *indio_dev = private;
228 struct max30100_data *data = iio_priv(indio_dev);
229 int ret, cnt = 0;
230
231 mutex_lock(&data->lock);
232
233 while (cnt-- || (cnt = max30100_fifo_count(data) > 0)) {
234 ret = max30100_read_measurement(data);
235 if (ret)
236 break;
237
238 iio_push_to_buffers(data->indio_dev, data->buffer);
239 }
240
241 mutex_unlock(&data->lock);
242
243 return IRQ_HANDLED;
244}
245
246static int max30100_chip_init(struct max30100_data *data)
247{
248 int ret;
249
250 /* RED IR LED = 24mA, IR LED = 50mA */
251 ret = regmap_write(data->regmap, MAX30100_REG_LED_CONFIG,
252 (MAX30100_REG_LED_CONFIG_24MA <<
253 MAX30100_REG_LED_CONFIG_RED_LED_SHIFT) |
254 MAX30100_REG_LED_CONFIG_50MA);
255 if (ret)
256 return ret;
257
258 /* enable hi-res SPO2 readings at 100Hz */
259 ret = regmap_write(data->regmap, MAX30100_REG_SPO2_CONFIG,
260 MAX30100_REG_SPO2_CONFIG_HI_RES_EN |
261 MAX30100_REG_SPO2_CONFIG_100HZ);
262 if (ret)
263 return ret;
264
265 /* enable SPO2 mode */
266 ret = regmap_update_bits(data->regmap, MAX30100_REG_MODE_CONFIG,
267 MAX30100_REG_MODE_CONFIG_MODE_MASK,
268 MAX30100_REG_MODE_CONFIG_MODE_HR_EN |
269 MAX30100_REG_MODE_CONFIG_MODE_SPO2_EN);
270 if (ret)
271 return ret;
272
273 /* enable FIFO interrupt */
274 return regmap_update_bits(data->regmap, MAX30100_REG_INT_ENABLE,
275 MAX30100_REG_INT_ENABLE_MASK,
276 MAX30100_REG_INT_ENABLE_FIFO_EN
277 << MAX30100_REG_INT_ENABLE_MASK_SHIFT);
278}
279
280static int max30100_read_temp(struct max30100_data *data, int *val)
281{
282 int ret;
283 unsigned int reg;
284
285 ret = regmap_read(data->regmap, MAX30100_REG_TEMP_INTEGER, &reg);
286 if (ret < 0)
287 return ret;
288 *val = reg << 4;
289
290 ret = regmap_read(data->regmap, MAX30100_REG_TEMP_FRACTION, &reg);
291 if (ret < 0)
292 return ret;
293
294 *val |= reg & 0xf;
295 *val = sign_extend32(*val, 11);
296
297 return 0;
298}
299
300static int max30100_get_temp(struct max30100_data *data, int *val)
301{
302 int ret;
303
304 /* start acquisition */
305 ret = regmap_update_bits(data->regmap, MAX30100_REG_MODE_CONFIG,
306 MAX30100_REG_MODE_CONFIG_TEMP_EN,
307 MAX30100_REG_MODE_CONFIG_TEMP_EN);
308 if (ret)
309 return ret;
310
311 usleep_range(35000, 50000);
312
313 return max30100_read_temp(data, val);
314}
315
316static int max30100_read_raw(struct iio_dev *indio_dev,
317 struct iio_chan_spec const *chan,
318 int *val, int *val2, long mask)
319{
320 struct max30100_data *data = iio_priv(indio_dev);
321 int ret = -EINVAL;
322
323 switch (mask) {
324 case IIO_CHAN_INFO_RAW:
325 /*
326 * Temperature reading can only be acquired while engine
327 * is running
328 */
329 mutex_lock(&indio_dev->mlock);
330
331 if (!iio_buffer_enabled(indio_dev))
332 ret = -EAGAIN;
333 else {
334 ret = max30100_get_temp(data, val);
335 if (!ret)
336 ret = IIO_VAL_INT;
337
338 }
339
340 mutex_unlock(&indio_dev->mlock);
341 break;
342 case IIO_CHAN_INFO_SCALE:
343 *val = 1; /* 0.0625 */
344 *val2 = 16;
345 ret = IIO_VAL_FRACTIONAL;
346 break;
347 }
348
349 return ret;
350}
351
352static const struct iio_info max30100_info = {
353 .driver_module = THIS_MODULE,
354 .read_raw = max30100_read_raw,
355};
356
357static int max30100_probe(struct i2c_client *client,
358 const struct i2c_device_id *id)
359{
360 struct max30100_data *data;
361 struct iio_buffer *buffer;
362 struct iio_dev *indio_dev;
363 int ret;
364
365 indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
366 if (!indio_dev)
367 return -ENOMEM;
368
369 buffer = devm_iio_kfifo_allocate(&client->dev);
370 if (!buffer)
371 return -ENOMEM;
372
373 iio_device_attach_buffer(indio_dev, buffer);
374
375 indio_dev->name = MAX30100_DRV_NAME;
376 indio_dev->channels = max30100_channels;
377 indio_dev->info = &max30100_info;
378 indio_dev->num_channels = ARRAY_SIZE(max30100_channels);
379 indio_dev->available_scan_masks = max30100_scan_masks;
380 indio_dev->modes = (INDIO_BUFFER_SOFTWARE | INDIO_DIRECT_MODE);
381 indio_dev->setup_ops = &max30100_buffer_setup_ops;
382
383 data = iio_priv(indio_dev);
384 data->indio_dev = indio_dev;
385 data->client = client;
386
387 mutex_init(&data->lock);
388 i2c_set_clientdata(client, indio_dev);
389
390 data->regmap = devm_regmap_init_i2c(client, &max30100_regmap_config);
391 if (IS_ERR(data->regmap)) {
392 dev_err(&client->dev, "regmap initialization failed.\n");
393 return PTR_ERR(data->regmap);
394 }
395 max30100_set_powermode(data, false);
396
397 ret = max30100_chip_init(data);
398 if (ret)
399 return ret;
400
401 if (client->irq <= 0) {
402 dev_err(&client->dev, "no valid irq defined\n");
403 return -EINVAL;
404 }
405 ret = devm_request_threaded_irq(&client->dev, client->irq,
406 NULL, max30100_interrupt_handler,
407 IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
408 "max30100_irq", indio_dev);
409 if (ret) {
410 dev_err(&client->dev, "request irq (%d) failed\n", client->irq);
411 return ret;
412 }
413
414 return iio_device_register(indio_dev);
415}
416
417static int max30100_remove(struct i2c_client *client)
418{
419 struct iio_dev *indio_dev = i2c_get_clientdata(client);
420 struct max30100_data *data = iio_priv(indio_dev);
421
422 iio_device_unregister(indio_dev);
423 max30100_set_powermode(data, false);
424
425 return 0;
426}
427
428static const struct i2c_device_id max30100_id[] = {
429 { "max30100", 0 },
430 {}
431};
432MODULE_DEVICE_TABLE(i2c, max30100_id);
433
434static const struct of_device_id max30100_dt_ids[] = {
435 { .compatible = "maxim,max30100" },
436 { }
437};
438MODULE_DEVICE_TABLE(of, max30100_dt_ids);
439
440static struct i2c_driver max30100_driver = {
441 .driver = {
442 .name = MAX30100_DRV_NAME,
443 .of_match_table = of_match_ptr(max30100_dt_ids),
444 },
445 .probe = max30100_probe,
446 .remove = max30100_remove,
447 .id_table = max30100_id,
448};
449module_i2c_driver(max30100_driver);
450
451MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
452MODULE_DESCRIPTION("MAX30100 heart rate and pulse oximeter sensor");
453MODULE_LICENSE("GPL");