1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 2b3c590ceSMatt Ranostay /* 3b3c590ceSMatt Ranostay * max30102.c - Support for MAX30102 heart rate and pulse oximeter sensor 4b3c590ceSMatt Ranostay * 5b3c590ceSMatt Ranostay * Copyright (C) 2017 Matt Ranostay <matt@ranostay.consulting> 6b3c590ceSMatt Ranostay * 790579b69SPeter Meerwald-Stadler * Support for MAX30105 optical particle sensor 890579b69SPeter Meerwald-Stadler * Copyright (C) 2017 Peter Meerwald-Stadler <pmeerw@pmeerw.net> 990579b69SPeter Meerwald-Stadler * 1090579b69SPeter Meerwald-Stadler * 7-bit I2C chip address: 0x57 11b3c590ceSMatt Ranostay * TODO: proximity power saving feature 12b3c590ceSMatt Ranostay */ 13b3c590ceSMatt Ranostay 14b3c590ceSMatt Ranostay #include <linux/module.h> 15b3c590ceSMatt Ranostay #include <linux/init.h> 16b3c590ceSMatt Ranostay #include <linux/interrupt.h> 17b3c590ceSMatt Ranostay #include <linux/delay.h> 18b3c590ceSMatt Ranostay #include <linux/err.h> 19b3c590ceSMatt Ranostay #include <linux/irq.h> 20b3c590ceSMatt Ranostay #include <linux/i2c.h> 21b3c590ceSMatt Ranostay #include <linux/mutex.h> 22b3c590ceSMatt Ranostay #include <linux/of.h> 23b3c590ceSMatt Ranostay #include <linux/regmap.h> 24b3c590ceSMatt Ranostay #include <linux/iio/iio.h> 25b3c590ceSMatt Ranostay #include <linux/iio/buffer.h> 26b3c590ceSMatt Ranostay #include <linux/iio/kfifo_buf.h> 27b3c590ceSMatt Ranostay 28b3c590ceSMatt Ranostay #define MAX30102_REGMAP_NAME "max30102_regmap" 29b3c590ceSMatt Ranostay #define MAX30102_DRV_NAME "max30102" 3084b0ce05SPeter Meerwald-Stadler #define MAX30102_PART_NUMBER 0x15 31b3c590ceSMatt Ranostay 3290579b69SPeter Meerwald-Stadler enum max30102_chip_id { 3390579b69SPeter Meerwald-Stadler max30102, 3490579b69SPeter Meerwald-Stadler max30105, 3590579b69SPeter Meerwald-Stadler }; 3690579b69SPeter Meerwald-Stadler 37ad90e570SPeter Meerwald-Stadler enum max3012_led_idx { 38ad90e570SPeter Meerwald-Stadler MAX30102_LED_RED, 39ad90e570SPeter Meerwald-Stadler MAX30102_LED_IR, 4090579b69SPeter Meerwald-Stadler MAX30105_LED_GREEN, 41ad90e570SPeter Meerwald-Stadler }; 42ad90e570SPeter Meerwald-Stadler 43b3c590ceSMatt Ranostay #define MAX30102_REG_INT_STATUS 0x00 44b3c590ceSMatt Ranostay #define MAX30102_REG_INT_STATUS_PWR_RDY BIT(0) 45b3c590ceSMatt Ranostay #define MAX30102_REG_INT_STATUS_PROX_INT BIT(4) 46b3c590ceSMatt Ranostay #define MAX30102_REG_INT_STATUS_ALC_OVF BIT(5) 47b3c590ceSMatt Ranostay #define MAX30102_REG_INT_STATUS_PPG_RDY BIT(6) 48b3c590ceSMatt Ranostay #define MAX30102_REG_INT_STATUS_FIFO_RDY BIT(7) 49b3c590ceSMatt Ranostay 50b3c590ceSMatt Ranostay #define MAX30102_REG_INT_ENABLE 0x02 51b3c590ceSMatt Ranostay #define MAX30102_REG_INT_ENABLE_PROX_INT_EN BIT(4) 52b3c590ceSMatt Ranostay #define MAX30102_REG_INT_ENABLE_ALC_OVF_EN BIT(5) 53b3c590ceSMatt Ranostay #define MAX30102_REG_INT_ENABLE_PPG_EN BIT(6) 54b3c590ceSMatt Ranostay #define MAX30102_REG_INT_ENABLE_FIFO_EN BIT(7) 55b3c590ceSMatt Ranostay #define MAX30102_REG_INT_ENABLE_MASK 0xf0 56b3c590ceSMatt Ranostay #define MAX30102_REG_INT_ENABLE_MASK_SHIFT 4 57b3c590ceSMatt Ranostay 58b3c590ceSMatt Ranostay #define MAX30102_REG_FIFO_WR_PTR 0x04 59b3c590ceSMatt Ranostay #define MAX30102_REG_FIFO_OVR_CTR 0x05 60b3c590ceSMatt Ranostay #define MAX30102_REG_FIFO_RD_PTR 0x06 61b3c590ceSMatt Ranostay #define MAX30102_REG_FIFO_DATA 0x07 62fef79edcSPeter Meerwald-Stadler #define MAX30102_REG_FIFO_DATA_BYTES 3 63b3c590ceSMatt Ranostay 64b3c590ceSMatt Ranostay #define MAX30102_REG_FIFO_CONFIG 0x08 65b3c590ceSMatt Ranostay #define MAX30102_REG_FIFO_CONFIG_AVG_4SAMPLES BIT(1) 66b3c590ceSMatt Ranostay #define MAX30102_REG_FIFO_CONFIG_AVG_SHIFT 5 67b3c590ceSMatt Ranostay #define MAX30102_REG_FIFO_CONFIG_AFULL BIT(0) 68b3c590ceSMatt Ranostay 69b3c590ceSMatt Ranostay #define MAX30102_REG_MODE_CONFIG 0x09 7083e6415dSPeter Meerwald-Stadler #define MAX30102_REG_MODE_CONFIG_MODE_NONE 0x00 717b0b0ec1SPeter Meerwald-Stadler #define MAX30102_REG_MODE_CONFIG_MODE_HR 0x02 /* red LED */ 727b0b0ec1SPeter Meerwald-Stadler #define MAX30102_REG_MODE_CONFIG_MODE_HR_SPO2 0x03 /* red + IR LED */ 737b0b0ec1SPeter Meerwald-Stadler #define MAX30102_REG_MODE_CONFIG_MODE_MULTI 0x07 /* multi-LED mode */ 747b0b0ec1SPeter Meerwald-Stadler #define MAX30102_REG_MODE_CONFIG_MODE_MASK GENMASK(2, 0) 75b3c590ceSMatt Ranostay #define MAX30102_REG_MODE_CONFIG_PWR BIT(7) 76b3c590ceSMatt Ranostay 7790579b69SPeter Meerwald-Stadler #define MAX30102_REG_MODE_CONTROL_SLOT21 0x11 /* multi-LED control */ 7890579b69SPeter Meerwald-Stadler #define MAX30102_REG_MODE_CONTROL_SLOT43 0x12 7990579b69SPeter Meerwald-Stadler #define MAX30102_REG_MODE_CONTROL_SLOT_MASK (GENMASK(6, 4) | GENMASK(2, 0)) 8090579b69SPeter Meerwald-Stadler #define MAX30102_REG_MODE_CONTROL_SLOT_SHIFT 4 8190579b69SPeter Meerwald-Stadler 82b3c590ceSMatt Ranostay #define MAX30102_REG_SPO2_CONFIG 0x0a 83b3c590ceSMatt Ranostay #define MAX30102_REG_SPO2_CONFIG_PULSE_411_US 0x03 84b3c590ceSMatt Ranostay #define MAX30102_REG_SPO2_CONFIG_SR_400HZ 0x03 85b3c590ceSMatt Ranostay #define MAX30102_REG_SPO2_CONFIG_SR_MASK 0x07 86b3c590ceSMatt Ranostay #define MAX30102_REG_SPO2_CONFIG_SR_MASK_SHIFT 2 87b3c590ceSMatt Ranostay #define MAX30102_REG_SPO2_CONFIG_ADC_4096_STEPS BIT(0) 88b3c590ceSMatt Ranostay #define MAX30102_REG_SPO2_CONFIG_ADC_MASK_SHIFT 5 89b3c590ceSMatt Ranostay 90b3c590ceSMatt Ranostay #define MAX30102_REG_RED_LED_CONFIG 0x0c 91b3c590ceSMatt Ranostay #define MAX30102_REG_IR_LED_CONFIG 0x0d 9290579b69SPeter Meerwald-Stadler #define MAX30105_REG_GREEN_LED_CONFIG 0x0e 93b3c590ceSMatt Ranostay 94b3c590ceSMatt Ranostay #define MAX30102_REG_TEMP_CONFIG 0x21 95b3c590ceSMatt Ranostay #define MAX30102_REG_TEMP_CONFIG_TEMP_EN BIT(0) 96b3c590ceSMatt Ranostay 97b3c590ceSMatt Ranostay #define MAX30102_REG_TEMP_INTEGER 0x1f 98b3c590ceSMatt Ranostay #define MAX30102_REG_TEMP_FRACTION 0x20 99b3c590ceSMatt Ranostay 10084b0ce05SPeter Meerwald-Stadler #define MAX30102_REG_REV_ID 0xfe 10184b0ce05SPeter Meerwald-Stadler #define MAX30102_REG_PART_ID 0xff 10284b0ce05SPeter Meerwald-Stadler 103b3c590ceSMatt Ranostay struct max30102_data { 104b3c590ceSMatt Ranostay struct i2c_client *client; 105b3c590ceSMatt Ranostay struct iio_dev *indio_dev; 106b3c590ceSMatt Ranostay struct mutex lock; 107b3c590ceSMatt Ranostay struct regmap *regmap; 10890579b69SPeter Meerwald-Stadler enum max30102_chip_id chip_id; 109b3c590ceSMatt Ranostay 11090579b69SPeter Meerwald-Stadler u8 buffer[12]; 11190579b69SPeter Meerwald-Stadler __be32 processed_buffer[3]; /* 3 x 18-bit (padded to 32-bits) */ 112b3c590ceSMatt Ranostay }; 113b3c590ceSMatt Ranostay 114b3c590ceSMatt Ranostay static const struct regmap_config max30102_regmap_config = { 115b3c590ceSMatt Ranostay .name = MAX30102_REGMAP_NAME, 116b3c590ceSMatt Ranostay 117b3c590ceSMatt Ranostay .reg_bits = 8, 118b3c590ceSMatt Ranostay .val_bits = 8, 119b3c590ceSMatt Ranostay }; 120b3c590ceSMatt Ranostay 121ad90e570SPeter Meerwald-Stadler static const unsigned long max30102_scan_masks[] = { 122ad90e570SPeter Meerwald-Stadler BIT(MAX30102_LED_RED) | BIT(MAX30102_LED_IR), 123ad90e570SPeter Meerwald-Stadler 0 124ad90e570SPeter Meerwald-Stadler }; 125b3c590ceSMatt Ranostay 12690579b69SPeter Meerwald-Stadler static const unsigned long max30105_scan_masks[] = { 12790579b69SPeter Meerwald-Stadler BIT(MAX30102_LED_RED) | BIT(MAX30102_LED_IR), 12890579b69SPeter Meerwald-Stadler BIT(MAX30102_LED_RED) | BIT(MAX30102_LED_IR) | 12990579b69SPeter Meerwald-Stadler BIT(MAX30105_LED_GREEN), 13090579b69SPeter Meerwald-Stadler 0 13190579b69SPeter Meerwald-Stadler }; 13290579b69SPeter Meerwald-Stadler 133839a74cdSPeter Meerwald-Stadler #define MAX30102_INTENSITY_CHANNEL(_si, _mod) { \ 134839a74cdSPeter Meerwald-Stadler .type = IIO_INTENSITY, \ 135839a74cdSPeter Meerwald-Stadler .channel2 = _mod, \ 136839a74cdSPeter Meerwald-Stadler .modified = 1, \ 137839a74cdSPeter Meerwald-Stadler .scan_index = _si, \ 138839a74cdSPeter Meerwald-Stadler .scan_type = { \ 139839a74cdSPeter Meerwald-Stadler .sign = 'u', \ 140839a74cdSPeter Meerwald-Stadler .shift = 8, \ 141839a74cdSPeter Meerwald-Stadler .realbits = 18, \ 142839a74cdSPeter Meerwald-Stadler .storagebits = 32, \ 143839a74cdSPeter Meerwald-Stadler .endianness = IIO_BE, \ 144839a74cdSPeter Meerwald-Stadler }, \ 145839a74cdSPeter Meerwald-Stadler } 146839a74cdSPeter Meerwald-Stadler 147b3c590ceSMatt Ranostay static const struct iio_chan_spec max30102_channels[] = { 148ad90e570SPeter Meerwald-Stadler MAX30102_INTENSITY_CHANNEL(MAX30102_LED_RED, IIO_MOD_LIGHT_RED), 149ad90e570SPeter Meerwald-Stadler MAX30102_INTENSITY_CHANNEL(MAX30102_LED_IR, IIO_MOD_LIGHT_IR), 150b3c590ceSMatt Ranostay { 151b3c590ceSMatt Ranostay .type = IIO_TEMP, 152b3c590ceSMatt Ranostay .info_mask_separate = 153b3c590ceSMatt Ranostay BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), 154b3c590ceSMatt Ranostay .scan_index = -1, 155b3c590ceSMatt Ranostay }, 156b3c590ceSMatt Ranostay }; 157b3c590ceSMatt Ranostay 15890579b69SPeter Meerwald-Stadler static const struct iio_chan_spec max30105_channels[] = { 15990579b69SPeter Meerwald-Stadler MAX30102_INTENSITY_CHANNEL(MAX30102_LED_RED, IIO_MOD_LIGHT_RED), 16090579b69SPeter Meerwald-Stadler MAX30102_INTENSITY_CHANNEL(MAX30102_LED_IR, IIO_MOD_LIGHT_IR), 16190579b69SPeter Meerwald-Stadler MAX30102_INTENSITY_CHANNEL(MAX30105_LED_GREEN, IIO_MOD_LIGHT_GREEN), 16290579b69SPeter Meerwald-Stadler { 16390579b69SPeter Meerwald-Stadler .type = IIO_TEMP, 16490579b69SPeter Meerwald-Stadler .info_mask_separate = 16590579b69SPeter Meerwald-Stadler BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), 16690579b69SPeter Meerwald-Stadler .scan_index = -1, 16790579b69SPeter Meerwald-Stadler }, 16890579b69SPeter Meerwald-Stadler }; 16990579b69SPeter Meerwald-Stadler 17083e6415dSPeter Meerwald-Stadler static int max30102_set_power(struct max30102_data *data, bool en) 171b3c590ceSMatt Ranostay { 172b3c590ceSMatt Ranostay return regmap_update_bits(data->regmap, MAX30102_REG_MODE_CONFIG, 173b3c590ceSMatt Ranostay MAX30102_REG_MODE_CONFIG_PWR, 17483e6415dSPeter Meerwald-Stadler en ? 0 : MAX30102_REG_MODE_CONFIG_PWR); 17583e6415dSPeter Meerwald-Stadler } 17683e6415dSPeter Meerwald-Stadler 17783e6415dSPeter Meerwald-Stadler static int max30102_set_powermode(struct max30102_data *data, u8 mode, bool en) 17883e6415dSPeter Meerwald-Stadler { 17983e6415dSPeter Meerwald-Stadler u8 reg = mode; 18083e6415dSPeter Meerwald-Stadler 18183e6415dSPeter Meerwald-Stadler if (!en) 18283e6415dSPeter Meerwald-Stadler reg |= MAX30102_REG_MODE_CONFIG_PWR; 18383e6415dSPeter Meerwald-Stadler 18483e6415dSPeter Meerwald-Stadler return regmap_update_bits(data->regmap, MAX30102_REG_MODE_CONFIG, 18583e6415dSPeter Meerwald-Stadler MAX30102_REG_MODE_CONFIG_PWR | 18683e6415dSPeter Meerwald-Stadler MAX30102_REG_MODE_CONFIG_MODE_MASK, reg); 187b3c590ceSMatt Ranostay } 188b3c590ceSMatt Ranostay 18990579b69SPeter Meerwald-Stadler #define MAX30102_MODE_CONTROL_LED_SLOTS(slot2, slot1) \ 19090579b69SPeter Meerwald-Stadler ((slot2 << MAX30102_REG_MODE_CONTROL_SLOT_SHIFT) | slot1) 19190579b69SPeter Meerwald-Stadler 192b3c590ceSMatt Ranostay static int max30102_buffer_postenable(struct iio_dev *indio_dev) 193b3c590ceSMatt Ranostay { 194b3c590ceSMatt Ranostay struct max30102_data *data = iio_priv(indio_dev); 19590579b69SPeter Meerwald-Stadler int ret; 19683e6415dSPeter Meerwald-Stadler u8 reg; 197b3c590ceSMatt Ranostay 19890579b69SPeter Meerwald-Stadler switch (*indio_dev->active_scan_mask) { 19990579b69SPeter Meerwald-Stadler case BIT(MAX30102_LED_RED) | BIT(MAX30102_LED_IR): 20083e6415dSPeter Meerwald-Stadler reg = MAX30102_REG_MODE_CONFIG_MODE_HR_SPO2; 20190579b69SPeter Meerwald-Stadler break; 20290579b69SPeter Meerwald-Stadler case BIT(MAX30102_LED_RED) | BIT(MAX30102_LED_IR) | 20390579b69SPeter Meerwald-Stadler BIT(MAX30105_LED_GREEN): 20490579b69SPeter Meerwald-Stadler ret = regmap_update_bits(data->regmap, 20590579b69SPeter Meerwald-Stadler MAX30102_REG_MODE_CONTROL_SLOT21, 20690579b69SPeter Meerwald-Stadler MAX30102_REG_MODE_CONTROL_SLOT_MASK, 20790579b69SPeter Meerwald-Stadler MAX30102_MODE_CONTROL_LED_SLOTS(2, 1)); 20890579b69SPeter Meerwald-Stadler if (ret) 20990579b69SPeter Meerwald-Stadler return ret; 21090579b69SPeter Meerwald-Stadler 21190579b69SPeter Meerwald-Stadler ret = regmap_update_bits(data->regmap, 21290579b69SPeter Meerwald-Stadler MAX30102_REG_MODE_CONTROL_SLOT43, 21390579b69SPeter Meerwald-Stadler MAX30102_REG_MODE_CONTROL_SLOT_MASK, 21490579b69SPeter Meerwald-Stadler MAX30102_MODE_CONTROL_LED_SLOTS(0, 3)); 21590579b69SPeter Meerwald-Stadler if (ret) 21690579b69SPeter Meerwald-Stadler return ret; 21790579b69SPeter Meerwald-Stadler 21890579b69SPeter Meerwald-Stadler reg = MAX30102_REG_MODE_CONFIG_MODE_MULTI; 21990579b69SPeter Meerwald-Stadler break; 22090579b69SPeter Meerwald-Stadler default: 22190579b69SPeter Meerwald-Stadler return -EINVAL; 22290579b69SPeter Meerwald-Stadler } 22383e6415dSPeter Meerwald-Stadler 22483e6415dSPeter Meerwald-Stadler return max30102_set_powermode(data, reg, true); 225b3c590ceSMatt Ranostay } 226b3c590ceSMatt Ranostay 227b3c590ceSMatt Ranostay static int max30102_buffer_predisable(struct iio_dev *indio_dev) 228b3c590ceSMatt Ranostay { 229b3c590ceSMatt Ranostay struct max30102_data *data = iio_priv(indio_dev); 230b3c590ceSMatt Ranostay 23183e6415dSPeter Meerwald-Stadler return max30102_set_powermode(data, MAX30102_REG_MODE_CONFIG_MODE_NONE, 23283e6415dSPeter Meerwald-Stadler false); 233b3c590ceSMatt Ranostay } 234b3c590ceSMatt Ranostay 235b3c590ceSMatt Ranostay static const struct iio_buffer_setup_ops max30102_buffer_setup_ops = { 236b3c590ceSMatt Ranostay .postenable = max30102_buffer_postenable, 237b3c590ceSMatt Ranostay .predisable = max30102_buffer_predisable, 238b3c590ceSMatt Ranostay }; 239b3c590ceSMatt Ranostay 240b3c590ceSMatt Ranostay static inline int max30102_fifo_count(struct max30102_data *data) 241b3c590ceSMatt Ranostay { 242b3c590ceSMatt Ranostay unsigned int val; 243b3c590ceSMatt Ranostay int ret; 244b3c590ceSMatt Ranostay 245b3c590ceSMatt Ranostay ret = regmap_read(data->regmap, MAX30102_REG_INT_STATUS, &val); 246b3c590ceSMatt Ranostay if (ret) 247b3c590ceSMatt Ranostay return ret; 248b3c590ceSMatt Ranostay 249b3c590ceSMatt Ranostay /* FIFO has one sample slot left */ 250b3c590ceSMatt Ranostay if (val & MAX30102_REG_INT_STATUS_FIFO_RDY) 251b3c590ceSMatt Ranostay return 1; 252b3c590ceSMatt Ranostay 253b3c590ceSMatt Ranostay return 0; 254b3c590ceSMatt Ranostay } 255b3c590ceSMatt Ranostay 256fef79edcSPeter Meerwald-Stadler #define MAX30102_COPY_DATA(i) \ 257fef79edcSPeter Meerwald-Stadler memcpy(&data->processed_buffer[(i)], \ 258fef79edcSPeter Meerwald-Stadler &buffer[(i) * MAX30102_REG_FIFO_DATA_BYTES], \ 259fef79edcSPeter Meerwald-Stadler MAX30102_REG_FIFO_DATA_BYTES) 260fef79edcSPeter Meerwald-Stadler 26190579b69SPeter Meerwald-Stadler static int max30102_read_measurement(struct max30102_data *data, 26290579b69SPeter Meerwald-Stadler unsigned int measurements) 263b3c590ceSMatt Ranostay { 264b3c590ceSMatt Ranostay int ret; 265b3c590ceSMatt Ranostay u8 *buffer = (u8 *) &data->buffer; 266b3c590ceSMatt Ranostay 267b3c590ceSMatt Ranostay ret = i2c_smbus_read_i2c_block_data(data->client, 268b3c590ceSMatt Ranostay MAX30102_REG_FIFO_DATA, 26990579b69SPeter Meerwald-Stadler measurements * 27090579b69SPeter Meerwald-Stadler MAX30102_REG_FIFO_DATA_BYTES, 271b3c590ceSMatt Ranostay buffer); 272b3c590ceSMatt Ranostay 27390579b69SPeter Meerwald-Stadler switch (measurements) { 27490579b69SPeter Meerwald-Stadler case 3: 27590579b69SPeter Meerwald-Stadler MAX30102_COPY_DATA(2); 276*df561f66SGustavo A. R. Silva fallthrough; 2779ffa68f6SGustavo A. R. Silva case 2: 278fef79edcSPeter Meerwald-Stadler MAX30102_COPY_DATA(1); 279*df561f66SGustavo A. R. Silva fallthrough; 2809ffa68f6SGustavo A. R. Silva case 1: 28190579b69SPeter Meerwald-Stadler MAX30102_COPY_DATA(0); 28290579b69SPeter Meerwald-Stadler break; 28390579b69SPeter Meerwald-Stadler default: 28490579b69SPeter Meerwald-Stadler return -EINVAL; 28590579b69SPeter Meerwald-Stadler } 286b3c590ceSMatt Ranostay 28790579b69SPeter Meerwald-Stadler return (ret == measurements * MAX30102_REG_FIFO_DATA_BYTES) ? 28890579b69SPeter Meerwald-Stadler 0 : -EINVAL; 289b3c590ceSMatt Ranostay } 290b3c590ceSMatt Ranostay 291b3c590ceSMatt Ranostay static irqreturn_t max30102_interrupt_handler(int irq, void *private) 292b3c590ceSMatt Ranostay { 293b3c590ceSMatt Ranostay struct iio_dev *indio_dev = private; 294b3c590ceSMatt Ranostay struct max30102_data *data = iio_priv(indio_dev); 29590579b69SPeter Meerwald-Stadler unsigned int measurements = bitmap_weight(indio_dev->active_scan_mask, 29690579b69SPeter Meerwald-Stadler indio_dev->masklength); 297b3c590ceSMatt Ranostay int ret, cnt = 0; 298b3c590ceSMatt Ranostay 299b3c590ceSMatt Ranostay mutex_lock(&data->lock); 300b3c590ceSMatt Ranostay 301b3c590ceSMatt Ranostay while (cnt || (cnt = max30102_fifo_count(data)) > 0) { 30290579b69SPeter Meerwald-Stadler ret = max30102_read_measurement(data, measurements); 303b3c590ceSMatt Ranostay if (ret) 304b3c590ceSMatt Ranostay break; 305b3c590ceSMatt Ranostay 306b3c590ceSMatt Ranostay iio_push_to_buffers(data->indio_dev, data->processed_buffer); 307b3c590ceSMatt Ranostay cnt--; 308b3c590ceSMatt Ranostay } 309b3c590ceSMatt Ranostay 310b3c590ceSMatt Ranostay mutex_unlock(&data->lock); 311b3c590ceSMatt Ranostay 312b3c590ceSMatt Ranostay return IRQ_HANDLED; 313b3c590ceSMatt Ranostay } 314b3c590ceSMatt Ranostay 315b3c590ceSMatt Ranostay static int max30102_get_current_idx(unsigned int val, int *reg) 316b3c590ceSMatt Ranostay { 317b3c590ceSMatt Ranostay /* each step is 0.200 mA */ 318b3c590ceSMatt Ranostay *reg = val / 200; 319b3c590ceSMatt Ranostay 320b3c590ceSMatt Ranostay return *reg > 0xff ? -EINVAL : 0; 321b3c590ceSMatt Ranostay } 322b3c590ceSMatt Ranostay 323b3c590ceSMatt Ranostay static int max30102_led_init(struct max30102_data *data) 324b3c590ceSMatt Ranostay { 325b3c590ceSMatt Ranostay struct device *dev = &data->client->dev; 326b3c590ceSMatt Ranostay struct device_node *np = dev->of_node; 327b3c590ceSMatt Ranostay unsigned int val; 328b3c590ceSMatt Ranostay int reg, ret; 329b3c590ceSMatt Ranostay 330b3c590ceSMatt Ranostay ret = of_property_read_u32(np, "maxim,red-led-current-microamp", &val); 331b3c590ceSMatt Ranostay if (ret) { 332b3c590ceSMatt Ranostay dev_info(dev, "no red-led-current-microamp set\n"); 333b3c590ceSMatt Ranostay 334b3c590ceSMatt Ranostay /* Default to 7 mA RED LED */ 335b3c590ceSMatt Ranostay val = 7000; 336b3c590ceSMatt Ranostay } 337b3c590ceSMatt Ranostay 338b3c590ceSMatt Ranostay ret = max30102_get_current_idx(val, ®); 339b3c590ceSMatt Ranostay if (ret) { 340b3c590ceSMatt Ranostay dev_err(dev, "invalid RED LED current setting %d\n", val); 341b3c590ceSMatt Ranostay return ret; 342b3c590ceSMatt Ranostay } 343b3c590ceSMatt Ranostay 344b3c590ceSMatt Ranostay ret = regmap_write(data->regmap, MAX30102_REG_RED_LED_CONFIG, reg); 345b3c590ceSMatt Ranostay if (ret) 346b3c590ceSMatt Ranostay return ret; 347b3c590ceSMatt Ranostay 34890579b69SPeter Meerwald-Stadler if (data->chip_id == max30105) { 34990579b69SPeter Meerwald-Stadler ret = of_property_read_u32(np, 35090579b69SPeter Meerwald-Stadler "maxim,green-led-current-microamp", &val); 35190579b69SPeter Meerwald-Stadler if (ret) { 35290579b69SPeter Meerwald-Stadler dev_info(dev, "no green-led-current-microamp set\n"); 35390579b69SPeter Meerwald-Stadler 35490579b69SPeter Meerwald-Stadler /* Default to 7 mA green LED */ 35590579b69SPeter Meerwald-Stadler val = 7000; 35690579b69SPeter Meerwald-Stadler } 35790579b69SPeter Meerwald-Stadler 35890579b69SPeter Meerwald-Stadler ret = max30102_get_current_idx(val, ®); 35990579b69SPeter Meerwald-Stadler if (ret) { 36090579b69SPeter Meerwald-Stadler dev_err(dev, "invalid green LED current setting %d\n", 36190579b69SPeter Meerwald-Stadler val); 36290579b69SPeter Meerwald-Stadler return ret; 36390579b69SPeter Meerwald-Stadler } 36490579b69SPeter Meerwald-Stadler 36590579b69SPeter Meerwald-Stadler ret = regmap_write(data->regmap, MAX30105_REG_GREEN_LED_CONFIG, 36690579b69SPeter Meerwald-Stadler reg); 36790579b69SPeter Meerwald-Stadler if (ret) 36890579b69SPeter Meerwald-Stadler return ret; 36990579b69SPeter Meerwald-Stadler } 37090579b69SPeter Meerwald-Stadler 371b3c590ceSMatt Ranostay ret = of_property_read_u32(np, "maxim,ir-led-current-microamp", &val); 372b3c590ceSMatt Ranostay if (ret) { 373b3c590ceSMatt Ranostay dev_info(dev, "no ir-led-current-microamp set\n"); 374b3c590ceSMatt Ranostay 375b3c590ceSMatt Ranostay /* Default to 7 mA IR LED */ 376b3c590ceSMatt Ranostay val = 7000; 377b3c590ceSMatt Ranostay } 378b3c590ceSMatt Ranostay 379b3c590ceSMatt Ranostay ret = max30102_get_current_idx(val, ®); 380b3c590ceSMatt Ranostay if (ret) { 381dd86dbf9SPeter Meerwald-Stadler dev_err(dev, "invalid IR LED current setting %d\n", val); 382b3c590ceSMatt Ranostay return ret; 383b3c590ceSMatt Ranostay } 384b3c590ceSMatt Ranostay 385b3c590ceSMatt Ranostay return regmap_write(data->regmap, MAX30102_REG_IR_LED_CONFIG, reg); 386b3c590ceSMatt Ranostay } 387b3c590ceSMatt Ranostay 388b3c590ceSMatt Ranostay static int max30102_chip_init(struct max30102_data *data) 389b3c590ceSMatt Ranostay { 390b3c590ceSMatt Ranostay int ret; 391b3c590ceSMatt Ranostay 392b3c590ceSMatt Ranostay /* setup LED current settings */ 393b3c590ceSMatt Ranostay ret = max30102_led_init(data); 394b3c590ceSMatt Ranostay if (ret) 395b3c590ceSMatt Ranostay return ret; 396b3c590ceSMatt Ranostay 39783e6415dSPeter Meerwald-Stadler /* configure 18-bit HR + SpO2 readings at 400Hz */ 398b3c590ceSMatt Ranostay ret = regmap_write(data->regmap, MAX30102_REG_SPO2_CONFIG, 399b3c590ceSMatt Ranostay (MAX30102_REG_SPO2_CONFIG_ADC_4096_STEPS 400b3c590ceSMatt Ranostay << MAX30102_REG_SPO2_CONFIG_ADC_MASK_SHIFT) | 401b3c590ceSMatt Ranostay (MAX30102_REG_SPO2_CONFIG_SR_400HZ 402b3c590ceSMatt Ranostay << MAX30102_REG_SPO2_CONFIG_SR_MASK_SHIFT) | 403b3c590ceSMatt Ranostay MAX30102_REG_SPO2_CONFIG_PULSE_411_US); 404b3c590ceSMatt Ranostay if (ret) 405b3c590ceSMatt Ranostay return ret; 406b3c590ceSMatt Ranostay 407b3c590ceSMatt Ranostay /* average 4 samples + generate FIFO interrupt */ 408b3c590ceSMatt Ranostay ret = regmap_write(data->regmap, MAX30102_REG_FIFO_CONFIG, 409b3c590ceSMatt Ranostay (MAX30102_REG_FIFO_CONFIG_AVG_4SAMPLES 410b3c590ceSMatt Ranostay << MAX30102_REG_FIFO_CONFIG_AVG_SHIFT) | 411b3c590ceSMatt Ranostay MAX30102_REG_FIFO_CONFIG_AFULL); 412b3c590ceSMatt Ranostay if (ret) 413b3c590ceSMatt Ranostay return ret; 414b3c590ceSMatt Ranostay 415b3c590ceSMatt Ranostay /* enable FIFO interrupt */ 416b3c590ceSMatt Ranostay return regmap_update_bits(data->regmap, MAX30102_REG_INT_ENABLE, 417b3c590ceSMatt Ranostay MAX30102_REG_INT_ENABLE_MASK, 418b3c590ceSMatt Ranostay MAX30102_REG_INT_ENABLE_FIFO_EN); 419b3c590ceSMatt Ranostay } 420b3c590ceSMatt Ranostay 421b3c590ceSMatt Ranostay static int max30102_read_temp(struct max30102_data *data, int *val) 422b3c590ceSMatt Ranostay { 423b3c590ceSMatt Ranostay int ret; 424b3c590ceSMatt Ranostay unsigned int reg; 425b3c590ceSMatt Ranostay 426b3c590ceSMatt Ranostay ret = regmap_read(data->regmap, MAX30102_REG_TEMP_INTEGER, ®); 427b3c590ceSMatt Ranostay if (ret < 0) 428b3c590ceSMatt Ranostay return ret; 429b3c590ceSMatt Ranostay *val = reg << 4; 430b3c590ceSMatt Ranostay 431b3c590ceSMatt Ranostay ret = regmap_read(data->regmap, MAX30102_REG_TEMP_FRACTION, ®); 432b3c590ceSMatt Ranostay if (ret < 0) 433b3c590ceSMatt Ranostay return ret; 434b3c590ceSMatt Ranostay 435b3c590ceSMatt Ranostay *val |= reg & 0xf; 436b3c590ceSMatt Ranostay *val = sign_extend32(*val, 11); 437b3c590ceSMatt Ranostay 438b3c590ceSMatt Ranostay return 0; 439b3c590ceSMatt Ranostay } 440b3c590ceSMatt Ranostay 441a9c47abbSPeter Meerwald-Stadler static int max30102_get_temp(struct max30102_data *data, int *val, bool en) 442b3c590ceSMatt Ranostay { 443b3c590ceSMatt Ranostay int ret; 444b3c590ceSMatt Ranostay 445a9c47abbSPeter Meerwald-Stadler if (en) { 44683e6415dSPeter Meerwald-Stadler ret = max30102_set_power(data, true); 447a9c47abbSPeter Meerwald-Stadler if (ret) 448a9c47abbSPeter Meerwald-Stadler return ret; 449a9c47abbSPeter Meerwald-Stadler } 450a9c47abbSPeter Meerwald-Stadler 451b3c590ceSMatt Ranostay /* start acquisition */ 452b3c590ceSMatt Ranostay ret = regmap_update_bits(data->regmap, MAX30102_REG_TEMP_CONFIG, 453b3c590ceSMatt Ranostay MAX30102_REG_TEMP_CONFIG_TEMP_EN, 454b3c590ceSMatt Ranostay MAX30102_REG_TEMP_CONFIG_TEMP_EN); 455b3c590ceSMatt Ranostay if (ret) 456a9c47abbSPeter Meerwald-Stadler goto out; 457b3c590ceSMatt Ranostay 458b3c590ceSMatt Ranostay msleep(35); 459a9c47abbSPeter Meerwald-Stadler ret = max30102_read_temp(data, val); 460b3c590ceSMatt Ranostay 461a9c47abbSPeter Meerwald-Stadler out: 462a9c47abbSPeter Meerwald-Stadler if (en) 46383e6415dSPeter Meerwald-Stadler max30102_set_power(data, false); 464a9c47abbSPeter Meerwald-Stadler 465a9c47abbSPeter Meerwald-Stadler return ret; 466b3c590ceSMatt Ranostay } 467b3c590ceSMatt Ranostay 468b3c590ceSMatt Ranostay static int max30102_read_raw(struct iio_dev *indio_dev, 469b3c590ceSMatt Ranostay struct iio_chan_spec const *chan, 470b3c590ceSMatt Ranostay int *val, int *val2, long mask) 471b3c590ceSMatt Ranostay { 472b3c590ceSMatt Ranostay struct max30102_data *data = iio_priv(indio_dev); 473b3c590ceSMatt Ranostay int ret = -EINVAL; 474b3c590ceSMatt Ranostay 475b3c590ceSMatt Ranostay switch (mask) { 476b3c590ceSMatt Ranostay case IIO_CHAN_INFO_RAW: 477b3c590ceSMatt Ranostay /* 478a9c47abbSPeter Meerwald-Stadler * Temperature reading can only be acquired when not in 479a9c47abbSPeter Meerwald-Stadler * shutdown; leave shutdown briefly when buffer not running 480b3c590ceSMatt Ranostay */ 481b3c590ceSMatt Ranostay mutex_lock(&indio_dev->mlock); 482b3c590ceSMatt Ranostay if (!iio_buffer_enabled(indio_dev)) 483a9c47abbSPeter Meerwald-Stadler ret = max30102_get_temp(data, val, true); 484a9c47abbSPeter Meerwald-Stadler else 485a9c47abbSPeter Meerwald-Stadler ret = max30102_get_temp(data, val, false); 486b3c590ceSMatt Ranostay mutex_unlock(&indio_dev->mlock); 487a9c47abbSPeter Meerwald-Stadler if (ret) 488a9c47abbSPeter Meerwald-Stadler return ret; 489a9c47abbSPeter Meerwald-Stadler 490a9c47abbSPeter Meerwald-Stadler ret = IIO_VAL_INT; 491b3c590ceSMatt Ranostay break; 492b3c590ceSMatt Ranostay case IIO_CHAN_INFO_SCALE: 493ad44a9f8SPeter Meerwald-Stadler *val = 1000; /* 62.5 */ 494b3c590ceSMatt Ranostay *val2 = 16; 495b3c590ceSMatt Ranostay ret = IIO_VAL_FRACTIONAL; 496b3c590ceSMatt Ranostay break; 497b3c590ceSMatt Ranostay } 498b3c590ceSMatt Ranostay 499b3c590ceSMatt Ranostay return ret; 500b3c590ceSMatt Ranostay } 501b3c590ceSMatt Ranostay 502b3c590ceSMatt Ranostay static const struct iio_info max30102_info = { 503b3c590ceSMatt Ranostay .read_raw = max30102_read_raw, 504b3c590ceSMatt Ranostay }; 505b3c590ceSMatt Ranostay 506b3c590ceSMatt Ranostay static int max30102_probe(struct i2c_client *client, 507b3c590ceSMatt Ranostay const struct i2c_device_id *id) 508b3c590ceSMatt Ranostay { 509b3c590ceSMatt Ranostay struct max30102_data *data; 510b3c590ceSMatt Ranostay struct iio_buffer *buffer; 511b3c590ceSMatt Ranostay struct iio_dev *indio_dev; 512b3c590ceSMatt Ranostay int ret; 51384b0ce05SPeter Meerwald-Stadler unsigned int reg; 514b3c590ceSMatt Ranostay 515b3c590ceSMatt Ranostay indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); 516b3c590ceSMatt Ranostay if (!indio_dev) 517b3c590ceSMatt Ranostay return -ENOMEM; 518b3c590ceSMatt Ranostay 519b3c590ceSMatt Ranostay buffer = devm_iio_kfifo_allocate(&client->dev); 520b3c590ceSMatt Ranostay if (!buffer) 521b3c590ceSMatt Ranostay return -ENOMEM; 522b3c590ceSMatt Ranostay 523b3c590ceSMatt Ranostay iio_device_attach_buffer(indio_dev, buffer); 524b3c590ceSMatt Ranostay 525b3c590ceSMatt Ranostay indio_dev->name = MAX30102_DRV_NAME; 526b3c590ceSMatt Ranostay indio_dev->info = &max30102_info; 527b3c590ceSMatt Ranostay indio_dev->modes = (INDIO_BUFFER_SOFTWARE | INDIO_DIRECT_MODE); 528b3c590ceSMatt Ranostay indio_dev->setup_ops = &max30102_buffer_setup_ops; 529b3c590ceSMatt Ranostay 530b3c590ceSMatt Ranostay data = iio_priv(indio_dev); 531b3c590ceSMatt Ranostay data->indio_dev = indio_dev; 532b3c590ceSMatt Ranostay data->client = client; 53390579b69SPeter Meerwald-Stadler data->chip_id = id->driver_data; 534b3c590ceSMatt Ranostay 535b3c590ceSMatt Ranostay mutex_init(&data->lock); 536b3c590ceSMatt Ranostay i2c_set_clientdata(client, indio_dev); 537b3c590ceSMatt Ranostay 53890579b69SPeter Meerwald-Stadler switch (data->chip_id) { 53990579b69SPeter Meerwald-Stadler case max30105: 54090579b69SPeter Meerwald-Stadler indio_dev->channels = max30105_channels; 54190579b69SPeter Meerwald-Stadler indio_dev->num_channels = ARRAY_SIZE(max30105_channels); 54290579b69SPeter Meerwald-Stadler indio_dev->available_scan_masks = max30105_scan_masks; 54390579b69SPeter Meerwald-Stadler break; 54490579b69SPeter Meerwald-Stadler case max30102: 54590579b69SPeter Meerwald-Stadler indio_dev->channels = max30102_channels; 54690579b69SPeter Meerwald-Stadler indio_dev->num_channels = ARRAY_SIZE(max30102_channels); 54790579b69SPeter Meerwald-Stadler indio_dev->available_scan_masks = max30102_scan_masks; 54890579b69SPeter Meerwald-Stadler break; 54990579b69SPeter Meerwald-Stadler default: 55090579b69SPeter Meerwald-Stadler return -ENODEV; 55190579b69SPeter Meerwald-Stadler } 55290579b69SPeter Meerwald-Stadler 553b3c590ceSMatt Ranostay data->regmap = devm_regmap_init_i2c(client, &max30102_regmap_config); 554b3c590ceSMatt Ranostay if (IS_ERR(data->regmap)) { 555c31c946aSPeter Meerwald-Stadler dev_err(&client->dev, "regmap initialization failed\n"); 556b3c590ceSMatt Ranostay return PTR_ERR(data->regmap); 557b3c590ceSMatt Ranostay } 558d0b950c7SPeter Meerwald-Stadler 55984b0ce05SPeter Meerwald-Stadler /* check part ID */ 56084b0ce05SPeter Meerwald-Stadler ret = regmap_read(data->regmap, MAX30102_REG_PART_ID, ®); 56184b0ce05SPeter Meerwald-Stadler if (ret) 56284b0ce05SPeter Meerwald-Stadler return ret; 56384b0ce05SPeter Meerwald-Stadler if (reg != MAX30102_PART_NUMBER) 56484b0ce05SPeter Meerwald-Stadler return -ENODEV; 56584b0ce05SPeter Meerwald-Stadler 56684b0ce05SPeter Meerwald-Stadler /* show revision ID */ 56784b0ce05SPeter Meerwald-Stadler ret = regmap_read(data->regmap, MAX30102_REG_REV_ID, ®); 56884b0ce05SPeter Meerwald-Stadler if (ret) 56984b0ce05SPeter Meerwald-Stadler return ret; 57084b0ce05SPeter Meerwald-Stadler dev_dbg(&client->dev, "max3010x revision %02x\n", reg); 57184b0ce05SPeter Meerwald-Stadler 57283e6415dSPeter Meerwald-Stadler /* clear mode setting, chip shutdown */ 57383e6415dSPeter Meerwald-Stadler ret = max30102_set_powermode(data, MAX30102_REG_MODE_CONFIG_MODE_NONE, 57483e6415dSPeter Meerwald-Stadler false); 575d0b950c7SPeter Meerwald-Stadler if (ret) 576d0b950c7SPeter Meerwald-Stadler return ret; 577b3c590ceSMatt Ranostay 578b3c590ceSMatt Ranostay ret = max30102_chip_init(data); 579b3c590ceSMatt Ranostay if (ret) 580b3c590ceSMatt Ranostay return ret; 581b3c590ceSMatt Ranostay 582b3c590ceSMatt Ranostay if (client->irq <= 0) { 583b3c590ceSMatt Ranostay dev_err(&client->dev, "no valid irq defined\n"); 584b3c590ceSMatt Ranostay return -EINVAL; 585b3c590ceSMatt Ranostay } 586b3c590ceSMatt Ranostay 587b3c590ceSMatt Ranostay ret = devm_request_threaded_irq(&client->dev, client->irq, 588b3c590ceSMatt Ranostay NULL, max30102_interrupt_handler, 589b3c590ceSMatt Ranostay IRQF_TRIGGER_FALLING | IRQF_ONESHOT, 590b3c590ceSMatt Ranostay "max30102_irq", indio_dev); 591b3c590ceSMatt Ranostay if (ret) { 592b3c590ceSMatt Ranostay dev_err(&client->dev, "request irq (%d) failed\n", client->irq); 593b3c590ceSMatt Ranostay return ret; 594b3c590ceSMatt Ranostay } 595b3c590ceSMatt Ranostay 596b3c590ceSMatt Ranostay return iio_device_register(indio_dev); 597b3c590ceSMatt Ranostay } 598b3c590ceSMatt Ranostay 599b3c590ceSMatt Ranostay static int max30102_remove(struct i2c_client *client) 600b3c590ceSMatt Ranostay { 601b3c590ceSMatt Ranostay struct iio_dev *indio_dev = i2c_get_clientdata(client); 602b3c590ceSMatt Ranostay struct max30102_data *data = iio_priv(indio_dev); 603b3c590ceSMatt Ranostay 604b3c590ceSMatt Ranostay iio_device_unregister(indio_dev); 60583e6415dSPeter Meerwald-Stadler max30102_set_power(data, false); 606b3c590ceSMatt Ranostay 607b3c590ceSMatt Ranostay return 0; 608b3c590ceSMatt Ranostay } 609b3c590ceSMatt Ranostay 610b3c590ceSMatt Ranostay static const struct i2c_device_id max30102_id[] = { 61190579b69SPeter Meerwald-Stadler { "max30102", max30102 }, 61290579b69SPeter Meerwald-Stadler { "max30105", max30105 }, 613b3c590ceSMatt Ranostay {} 614b3c590ceSMatt Ranostay }; 615b3c590ceSMatt Ranostay MODULE_DEVICE_TABLE(i2c, max30102_id); 616b3c590ceSMatt Ranostay 617b3c590ceSMatt Ranostay static const struct of_device_id max30102_dt_ids[] = { 618b3c590ceSMatt Ranostay { .compatible = "maxim,max30102" }, 61990579b69SPeter Meerwald-Stadler { .compatible = "maxim,max30105" }, 620b3c590ceSMatt Ranostay { } 621b3c590ceSMatt Ranostay }; 622b3c590ceSMatt Ranostay MODULE_DEVICE_TABLE(of, max30102_dt_ids); 623b3c590ceSMatt Ranostay 624b3c590ceSMatt Ranostay static struct i2c_driver max30102_driver = { 625b3c590ceSMatt Ranostay .driver = { 626b3c590ceSMatt Ranostay .name = MAX30102_DRV_NAME, 627b3c590ceSMatt Ranostay .of_match_table = of_match_ptr(max30102_dt_ids), 628b3c590ceSMatt Ranostay }, 629b3c590ceSMatt Ranostay .probe = max30102_probe, 630b3c590ceSMatt Ranostay .remove = max30102_remove, 631b3c590ceSMatt Ranostay .id_table = max30102_id, 632b3c590ceSMatt Ranostay }; 633b3c590ceSMatt Ranostay module_i2c_driver(max30102_driver); 634b3c590ceSMatt Ranostay 635b3c590ceSMatt Ranostay MODULE_AUTHOR("Matt Ranostay <matt@ranostay.consulting>"); 63690579b69SPeter Meerwald-Stadler MODULE_DESCRIPTION("MAX30102 heart rate/pulse oximeter and MAX30105 particle sensor driver"); 637b3c590ceSMatt Ranostay MODULE_LICENSE("GPL"); 638