blob: 505eb4d951ce2f6f95f46b3967f5e76e0d68aba9 [file] [log] [blame]
/*
* Copyright (c) 2017, NXP
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT maxim_max30101
#include <zephyr/logging/log.h>
#include "max30101.h"
LOG_MODULE_REGISTER(MAX30101, CONFIG_SENSOR_LOG_LEVEL);
static int max30101_sample_fetch(const struct device *dev,
enum sensor_channel chan)
{
struct max30101_data *data = dev->data;
const struct max30101_config *config = dev->config;
uint8_t buffer[MAX30101_MAX_BYTES_PER_SAMPLE];
uint32_t fifo_data;
int fifo_chan;
int num_bytes;
int i;
/* Read all the active channels for one sample */
num_bytes = data->num_channels * MAX30101_BYTES_PER_CHANNEL;
if (i2c_burst_read_dt(&config->i2c, MAX30101_REG_FIFO_DATA, buffer,
num_bytes)) {
LOG_ERR("Could not fetch sample");
return -EIO;
}
fifo_chan = 0;
for (i = 0; i < num_bytes; i += 3) {
/* Each channel is 18-bits */
fifo_data = (buffer[i] << 16) | (buffer[i + 1] << 8) |
(buffer[i + 2]);
fifo_data &= MAX30101_FIFO_DATA_MASK;
/* Save the raw data */
data->raw[fifo_chan++] = fifo_data;
}
return 0;
}
static int max30101_channel_get(const struct device *dev,
enum sensor_channel chan,
struct sensor_value *val)
{
struct max30101_data *data = dev->data;
enum max30101_led_channel led_chan;
int fifo_chan;
switch (chan) {
case SENSOR_CHAN_RED:
led_chan = MAX30101_LED_CHANNEL_RED;
break;
case SENSOR_CHAN_IR:
led_chan = MAX30101_LED_CHANNEL_IR;
break;
case SENSOR_CHAN_GREEN:
led_chan = MAX30101_LED_CHANNEL_GREEN;
break;
default:
LOG_ERR("Unsupported sensor channel");
return -ENOTSUP;
}
/* Check if the led channel is active by looking up the associated fifo
* channel. If the fifo channel isn't valid, then the led channel
* isn't active.
*/
fifo_chan = data->map[led_chan];
if (fifo_chan >= MAX30101_MAX_NUM_CHANNELS) {
LOG_ERR("Inactive sensor channel");
return -ENOTSUP;
}
/* TODO: Scale the raw data to standard units */
val->val1 = data->raw[fifo_chan];
val->val2 = 0;
return 0;
}
static DEVICE_API(sensor, max30101_driver_api) = {
.sample_fetch = max30101_sample_fetch,
.channel_get = max30101_channel_get,
#if CONFIG_MAX30101_TRIGGER
.trigger_set = max30101_trigger_set,
#endif
};
static int max30101_init(const struct device *dev)
{
const struct max30101_config *config = dev->config;
struct max30101_data *data = dev->data;
uint8_t part_id;
uint8_t mode_cfg;
uint32_t led_chan;
if (!device_is_ready(config->i2c.bus)) {
LOG_ERR("Bus device is not ready");
return -ENODEV;
}
/* Check the part id to make sure this is MAX30101 */
if (i2c_reg_read_byte_dt(&config->i2c, MAX30101_REG_PART_ID,
&part_id)) {
LOG_ERR("Could not get Part ID");
return -EIO;
}
if (part_id != MAX30101_PART_ID) {
LOG_ERR("Got Part ID 0x%02x, expected 0x%02x",
part_id, MAX30101_PART_ID);
return -EIO;
}
/* Reset the sensor */
if (i2c_reg_write_byte_dt(&config->i2c, MAX30101_REG_MODE_CFG,
MAX30101_MODE_CFG_RESET_MASK)) {
return -EIO;
}
/* Wait for reset to be cleared */
do {
if (i2c_reg_read_byte_dt(&config->i2c, MAX30101_REG_MODE_CFG,
&mode_cfg)) {
LOG_ERR("Could read mode cfg after reset");
return -EIO;
}
} while (mode_cfg & MAX30101_MODE_CFG_RESET_MASK);
/* Write the FIFO configuration register */
if (i2c_reg_write_byte_dt(&config->i2c, MAX30101_REG_FIFO_CFG,
config->fifo)) {
return -EIO;
}
/* Write the mode configuration register */
if (i2c_reg_write_byte_dt(&config->i2c, MAX30101_REG_MODE_CFG,
max30101_mode_convert[config->mode])) {
return -EIO;
}
/* Write the SpO2 configuration register */
if (i2c_reg_write_byte_dt(&config->i2c, MAX30101_REG_SPO2_CFG,
config->spo2)) {
return -EIO;
}
/* Write the LED pulse amplitude registers */
if (i2c_reg_write_byte_dt(&config->i2c, MAX30101_REG_LED1_PA,
config->led_pa[0])) {
return -EIO;
}
if (i2c_reg_write_byte_dt(&config->i2c, MAX30101_REG_LED2_PA,
config->led_pa[1])) {
return -EIO;
}
if (i2c_reg_write_byte_dt(&config->i2c, MAX30101_REG_LED3_PA,
config->led_pa[2])) {
return -EIO;
}
if (i2c_reg_write_byte_dt(&config->i2c, MAX30101_REG_LED4_PA, config->led_pa[2])) {
return -EIO;
}
if (!config->mode) {
uint8_t multi_led[2];
/* Write the multi-LED mode control registers */
multi_led[0] = (config->slot[1] << 4) | (config->slot[0]);
multi_led[1] = (config->slot[3] << 4) | (config->slot[2]);
if (i2c_reg_write_byte_dt(&config->i2c, MAX30101_REG_MULTI_LED, multi_led[0])) {
return -EIO;
}
if (i2c_reg_write_byte_dt(&config->i2c, MAX30101_REG_MULTI_LED + 1, multi_led[1])) {
return -EIO;
}
}
#if CONFIG_MAX30101_TRIGGER
if (max30101_init_interrupts(dev)) {
LOG_ERR("Failed to initialize interrupts");
return -EIO;
}
#endif
/* Initialize the channel map and active channel count */
data->num_channels = 0U;
for (led_chan = 0U; led_chan < MAX30101_MAX_NUM_CHANNELS; led_chan++) {
data->map[led_chan] = MAX30101_MAX_NUM_CHANNELS;
}
/* Count the number of active channels and build a map that translates
* the LED channel number (red/ir/green) to the fifo channel number.
*/
for (int fifo_chan = 0; fifo_chan < MAX30101_MAX_NUM_CHANNELS; fifo_chan++) {
led_chan = (config->slot[fifo_chan] & MAX30101_SLOT_LED_MASK) - 1;
if (led_chan < MAX30101_MAX_NUM_CHANNELS) {
data->map[led_chan] = fifo_chan;
data->num_channels++;
}
}
return 0;
}
#define MAX30101_CHECK(n) \
BUILD_ASSERT(DT_INST_PROP_LEN(n, led_pa) == 3, \
"MAX30101 led-pa property must have exactly 3 elements"); \
BUILD_ASSERT(DT_INST_PROP_LEN(n, led_slot) == 4, \
"MAX30101 led-slot property must have exactly 4 elements")
#define MAX30101_SLOT_CFG(n) \
COND_CODE_1(DT_INST_ENUM_HAS_VALUE(n, acq_mode, heart_rate), \
(MAX30101_HR_SLOTS), \
(COND_CODE_1(DT_INST_ENUM_HAS_VALUE(n, acq_mode, spo2), \
(MAX30101_SPO2_SLOTS), \
(MAX30101_MULTI_LED(n)) \
)) \
)
#define MAX30101_INIT(n) \
MAX30101_CHECK(n); \
static const struct max30101_config max30101_config_##n = { \
.i2c = I2C_DT_SPEC_INST_GET(n), \
.fifo = (DT_INST_ENUM_IDX(n, smp_ave) << MAX30101_FIFO_CFG_SMP_AVE_SHIFT) | \
(DT_INST_PROP(n, fifo_rollover_en) \
<< MAX30101_FIFO_CFG_ROLLOVER_EN_SHIFT) | \
(DT_INST_PROP(n, fifo_watermark) << MAX30101_FIFO_CFG_FIFO_FULL_SHIFT), \
.mode = DT_INST_ENUM_IDX(n, acq_mode), \
.spo2 = (DT_INST_ENUM_IDX(n, adc_rge) << MAX30101_SPO2_ADC_RGE_SHIFT) | \
(DT_INST_ENUM_IDX(n, smp_sr) << MAX30101_SPO2_SR_SHIFT) | \
(DT_INST_ENUM_IDX(n, led_pw) << MAX30101_SPO2_PW_SHIFT), \
.led_pa = DT_INST_PROP(n, led_pa), \
.slot = MAX30101_SLOT_CFG(n), \
IF_ENABLED(CONFIG_MAX30101_TRIGGER, \
(.irq_gpio = GPIO_DT_SPEC_INST_GET_OR(n, irq_gpios, {0}),) \
) }; \
static struct max30101_data max30101_data_##n; \
SENSOR_DEVICE_DT_INST_DEFINE(n, max30101_init, NULL, &max30101_data_##n, \
&max30101_config_##n, POST_KERNEL, \
CONFIG_SENSOR_INIT_PRIORITY, &max30101_driver_api);
DT_INST_FOREACH_STATUS_OKAY(MAX30101_INIT)