On Sun, 19 Nov 2017 13:00:51 -0800 Matt Ranostay <matt.ranostay@xxxxxxxxxxxx> wrote: > On Fri, Oct 27, 2017 at 12:45 PM, Peter Meerwald-Stadler > <pmeerw@xxxxxxxxxx> wrote: > > The Maxim MAX30105 part adds a third LED (green) and uses a multi-LED > > measuring mode producing three measurements > > > > Signed-off-by: Peter Meerwald-Stadler <pmeerw@xxxxxxxxxx> > > Cc: Matt Ranostay <matt.ranostay@xxxxxxxxxxxx> > > Acked-by: Matt Ranostay <matt.ranostay@xxxxxxxxxxxx> Applied. > > > --- > > drivers/iio/health/max30102.c | 144 ++++++++++++++++++++++++++++++++++++++---- > > 1 file changed, 130 insertions(+), 14 deletions(-) > > > > diff --git a/drivers/iio/health/max30102.c b/drivers/iio/health/max30102.c > > index 86058f0..15ccadc 100644 > > --- a/drivers/iio/health/max30102.c > > +++ b/drivers/iio/health/max30102.c > > @@ -3,6 +3,9 @@ > > * > > * Copyright (C) 2017 Matt Ranostay <matt@ranostay.consulting> > > * > > + * Support for MAX30105 optical particle sensor > > + * Copyright (C) 2017 Peter Meerwald-Stadler <pmeerw@xxxxxxxxxx> > > + * > > * This program is free software; you can redistribute it and/or modify > > * it under the terms of the GNU General Public License as published by > > * the Free Software Foundation; either version 2 of the License, or > > @@ -13,6 +16,7 @@ > > * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > > * GNU General Public License for more details. > > * > > + * 7-bit I2C chip address: 0x57 > > * TODO: proximity power saving feature > > */ > > > > @@ -34,9 +38,15 @@ > > #define MAX30102_DRV_NAME "max30102" > > #define MAX30102_PART_NUMBER 0x15 > > > > +enum max30102_chip_id { > > + max30102, > > + max30105, > > +}; > > + > > enum max3012_led_idx { > > MAX30102_LED_RED, > > MAX30102_LED_IR, > > + MAX30105_LED_GREEN, > > }; > > > > #define MAX30102_REG_INT_STATUS 0x00 > > @@ -73,6 +83,11 @@ enum max3012_led_idx { > > #define MAX30102_REG_MODE_CONFIG_MODE_MASK GENMASK(2, 0) > > #define MAX30102_REG_MODE_CONFIG_PWR BIT(7) > > > > +#define MAX30102_REG_MODE_CONTROL_SLOT21 0x11 /* multi-LED control */ > > +#define MAX30102_REG_MODE_CONTROL_SLOT43 0x12 > > +#define MAX30102_REG_MODE_CONTROL_SLOT_MASK (GENMASK(6, 4) | GENMASK(2, 0)) > > +#define MAX30102_REG_MODE_CONTROL_SLOT_SHIFT 4 > > I assume slot is some time multiplexing of the LEDs? > > > + > > #define MAX30102_REG_SPO2_CONFIG 0x0a > > #define MAX30102_REG_SPO2_CONFIG_PULSE_411_US 0x03 > > #define MAX30102_REG_SPO2_CONFIG_SR_400HZ 0x03 > > @@ -83,6 +98,7 @@ enum max3012_led_idx { > > > > #define MAX30102_REG_RED_LED_CONFIG 0x0c > > #define MAX30102_REG_IR_LED_CONFIG 0x0d > > +#define MAX30105_REG_GREEN_LED_CONFIG 0x0e > > > > #define MAX30102_REG_TEMP_CONFIG 0x21 > > #define MAX30102_REG_TEMP_CONFIG_TEMP_EN BIT(0) > > @@ -98,9 +114,10 @@ struct max30102_data { > > struct iio_dev *indio_dev; > > struct mutex lock; > > struct regmap *regmap; > > + enum max30102_chip_id chip_id; > > > > - u8 buffer[8]; > > - __be32 processed_buffer[2]; /* 2 x 18-bit (padded to 32-bits) */ > > + u8 buffer[12]; > > + __be32 processed_buffer[3]; /* 3 x 18-bit (padded to 32-bits) */ > > }; > > > > static const struct regmap_config max30102_regmap_config = { > > @@ -115,6 +132,13 @@ static const unsigned long max30102_scan_masks[] = { > > 0 > > }; > > > > +static const unsigned long max30105_scan_masks[] = { > > + BIT(MAX30102_LED_RED) | BIT(MAX30102_LED_IR), > > + BIT(MAX30102_LED_RED) | BIT(MAX30102_LED_IR) | > > + BIT(MAX30105_LED_GREEN), > > + 0 > > +}; > > + > > #define MAX30102_INTENSITY_CHANNEL(_si, _mod) { \ > > .type = IIO_INTENSITY, \ > > .channel2 = _mod, \ > > @@ -140,6 +164,18 @@ static const struct iio_chan_spec max30102_channels[] = { > > }, > > }; > > > > +static const struct iio_chan_spec max30105_channels[] = { > > + MAX30102_INTENSITY_CHANNEL(MAX30102_LED_RED, IIO_MOD_LIGHT_RED), > > + MAX30102_INTENSITY_CHANNEL(MAX30102_LED_IR, IIO_MOD_LIGHT_IR), > > + MAX30102_INTENSITY_CHANNEL(MAX30105_LED_GREEN, IIO_MOD_LIGHT_GREEN), > > + { > > + .type = IIO_TEMP, > > + .info_mask_separate = > > + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), > > + .scan_index = -1, > > + }, > > +}; > > + > > static int max30102_set_power(struct max30102_data *data, bool en) > > { > > return regmap_update_bits(data->regmap, MAX30102_REG_MODE_CONFIG, > > @@ -159,12 +195,40 @@ static int max30102_set_powermode(struct max30102_data *data, u8 mode, bool en) > > MAX30102_REG_MODE_CONFIG_MODE_MASK, reg); > > } > > > > +#define MAX30102_MODE_CONTROL_LED_SLOTS(slot2, slot1) \ > > + ((slot2 << MAX30102_REG_MODE_CONTROL_SLOT_SHIFT) | slot1) > > + > > static int max30102_buffer_postenable(struct iio_dev *indio_dev) > > { > > struct max30102_data *data = iio_priv(indio_dev); > > + int ret; > > u8 reg; > > > > - reg = MAX30102_REG_MODE_CONFIG_MODE_HR_SPO2; > > + switch (*indio_dev->active_scan_mask) { > > + case BIT(MAX30102_LED_RED) | BIT(MAX30102_LED_IR): > > + reg = MAX30102_REG_MODE_CONFIG_MODE_HR_SPO2; > > + break; > > + case BIT(MAX30102_LED_RED) | BIT(MAX30102_LED_IR) | > > + BIT(MAX30105_LED_GREEN): > > + ret = regmap_update_bits(data->regmap, > > + MAX30102_REG_MODE_CONTROL_SLOT21, > > + MAX30102_REG_MODE_CONTROL_SLOT_MASK, > > + MAX30102_MODE_CONTROL_LED_SLOTS(2, 1)); > > + if (ret) > > + return ret; > > + > > + ret = regmap_update_bits(data->regmap, > > + MAX30102_REG_MODE_CONTROL_SLOT43, > > + MAX30102_REG_MODE_CONTROL_SLOT_MASK, > > + MAX30102_MODE_CONTROL_LED_SLOTS(0, 3)); > > + if (ret) > > + return ret; > > + > > + reg = MAX30102_REG_MODE_CONFIG_MODE_MULTI; > > + break; > > + default: > > + return -EINVAL; > > + } > > > > return max30102_set_powermode(data, reg, true); > > } > > @@ -203,32 +267,46 @@ static inline int max30102_fifo_count(struct max30102_data *data) > > &buffer[(i) * MAX30102_REG_FIFO_DATA_BYTES], \ > > MAX30102_REG_FIFO_DATA_BYTES) > > > > -static int max30102_read_measurement(struct max30102_data *data) > > +static int max30102_read_measurement(struct max30102_data *data, > > + unsigned int measurements) > > { > > int ret; > > u8 *buffer = (u8 *) &data->buffer; > > > > ret = i2c_smbus_read_i2c_block_data(data->client, > > MAX30102_REG_FIFO_DATA, > > - 2 * MAX30102_REG_FIFO_DATA_BYTES, > > + measurements * > > + MAX30102_REG_FIFO_DATA_BYTES, > > buffer); > > > > - MAX30102_COPY_DATA(0); > > - MAX30102_COPY_DATA(1); > > + switch (measurements) { > > + case 3: > > + MAX30102_COPY_DATA(2); > > + case 2: /* fall-through */ > > + MAX30102_COPY_DATA(1); > > + case 1: /* fall-through */ > > + MAX30102_COPY_DATA(0); > > + break; > > + default: > > + return -EINVAL; > > + } > > > > - return (ret == 2 * MAX30102_REG_FIFO_DATA_BYTES) ? 0 : -EINVAL; > > + return (ret == measurements * MAX30102_REG_FIFO_DATA_BYTES) ? > > + 0 : -EINVAL; > > } > > > > static irqreturn_t max30102_interrupt_handler(int irq, void *private) > > { > > struct iio_dev *indio_dev = private; > > struct max30102_data *data = iio_priv(indio_dev); > > + unsigned int measurements = bitmap_weight(indio_dev->active_scan_mask, > > + indio_dev->masklength); > > int ret, cnt = 0; > > > > mutex_lock(&data->lock); > > > > while (cnt || (cnt = max30102_fifo_count(data)) > 0) { > > - ret = max30102_read_measurement(data); > > + ret = max30102_read_measurement(data, measurements); > > if (ret) > > break; > > > > @@ -274,6 +352,29 @@ static int max30102_led_init(struct max30102_data *data) > > if (ret) > > return ret; > > > > + if (data->chip_id == max30105) { > > + ret = of_property_read_u32(np, > > + "maxim,green-led-current-microamp", &val); > > + if (ret) { > > + dev_info(dev, "no green-led-current-microamp set\n"); > > + > > + /* Default to 7 mA green LED */ > > + val = 7000; > > + } > > + > > + ret = max30102_get_current_idx(val, ®); > > + if (ret) { > > + dev_err(dev, "invalid green LED current setting %d\n", > > + val); > > + return ret; > > + } > > + > > + ret = regmap_write(data->regmap, MAX30105_REG_GREEN_LED_CONFIG, > > + reg); > > + if (ret) > > + return ret; > > + } > > + > > ret = of_property_read_u32(np, "maxim,ir-led-current-microamp", &val); > > if (ret) { > > dev_info(dev, "no ir-led-current-microamp set\n"); > > @@ -429,10 +530,7 @@ static int max30102_probe(struct i2c_client *client, > > iio_device_attach_buffer(indio_dev, buffer); > > > > indio_dev->name = MAX30102_DRV_NAME; > > - indio_dev->channels = max30102_channels; > > indio_dev->info = &max30102_info; > > - indio_dev->num_channels = ARRAY_SIZE(max30102_channels); > > - indio_dev->available_scan_masks = max30102_scan_masks; > > indio_dev->modes = (INDIO_BUFFER_SOFTWARE | INDIO_DIRECT_MODE); > > indio_dev->setup_ops = &max30102_buffer_setup_ops; > > indio_dev->dev.parent = &client->dev; > > @@ -440,10 +538,26 @@ static int max30102_probe(struct i2c_client *client, > > data = iio_priv(indio_dev); > > data->indio_dev = indio_dev; > > data->client = client; > > + data->chip_id = id->driver_data; > > > > mutex_init(&data->lock); > > i2c_set_clientdata(client, indio_dev); > > > > + switch (data->chip_id) { > > + case max30105: > > + indio_dev->channels = max30105_channels; > > + indio_dev->num_channels = ARRAY_SIZE(max30105_channels); > > + indio_dev->available_scan_masks = max30105_scan_masks; > > + break; > > + case max30102: > > + indio_dev->channels = max30102_channels; > > + indio_dev->num_channels = ARRAY_SIZE(max30102_channels); > > + indio_dev->available_scan_masks = max30102_scan_masks; > > + break; > > + default: > > + return -ENODEV; > > + } > > + > > data->regmap = devm_regmap_init_i2c(client, &max30102_regmap_config); > > if (IS_ERR(data->regmap)) { > > dev_err(&client->dev, "regmap initialization failed\n"); > > @@ -502,13 +616,15 @@ static int max30102_remove(struct i2c_client *client) > > } > > > > static const struct i2c_device_id max30102_id[] = { > > - { "max30102", 0 }, > > + { "max30102", max30102 }, > > + { "max30105", max30105 }, > > {} > > }; > > MODULE_DEVICE_TABLE(i2c, max30102_id); > > > > static const struct of_device_id max30102_dt_ids[] = { > > { .compatible = "maxim,max30102" }, > > + { .compatible = "maxim,max30105" }, > > { } > > }; > > MODULE_DEVICE_TABLE(of, max30102_dt_ids); > > @@ -525,5 +641,5 @@ static struct i2c_driver max30102_driver = { > > module_i2c_driver(max30102_driver); > > > > MODULE_AUTHOR("Matt Ranostay <matt@ranostay.consulting>"); > > -MODULE_DESCRIPTION("MAX30102 heart rate and pulse oximeter sensor"); > > +MODULE_DESCRIPTION("MAX30102 heart rate/pulse oximeter and MAX30105 particle sensor driver"); > > MODULE_LICENSE("GPL"); > > -- > > 2.7.4 > > -- To unsubscribe from this list: send the line "unsubscribe linux-iio" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html