2019-05-27 09:55:06 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2017-02-04 13:22:51 +03:00
/*
* max30102 . c - Support for MAX30102 heart rate and pulse oximeter sensor
*
* Copyright ( C ) 2017 Matt Ranostay < matt @ ranostay . consulting >
*
2017-10-27 22:45:42 +03:00
* Support for MAX30105 optical particle sensor
* Copyright ( C ) 2017 Peter Meerwald - Stadler < pmeerw @ pmeerw . net >
*
* 7 - bit I2C chip address : 0x57
2017-02-04 13:22:51 +03:00
* TODO : proximity power saving feature
*/
# include <linux/module.h>
# include <linux/init.h>
# include <linux/interrupt.h>
# include <linux/delay.h>
# include <linux/err.h>
# include <linux/irq.h>
# include <linux/i2c.h>
# include <linux/mutex.h>
# include <linux/of.h>
# include <linux/regmap.h>
# include <linux/iio/iio.h>
# include <linux/iio/buffer.h>
# include <linux/iio/kfifo_buf.h>
# define MAX30102_REGMAP_NAME "max30102_regmap"
# define MAX30102_DRV_NAME "max30102"
2017-10-27 22:45:36 +03:00
# define MAX30102_PART_NUMBER 0x15
2017-02-04 13:22:51 +03:00
2017-10-27 22:45:42 +03:00
enum max30102_chip_id {
max30102 ,
max30105 ,
} ;
2017-10-27 22:45:39 +03:00
enum max3012_led_idx {
MAX30102_LED_RED ,
MAX30102_LED_IR ,
2017-10-27 22:45:42 +03:00
MAX30105_LED_GREEN ,
2017-10-27 22:45:39 +03:00
} ;
2017-02-04 13:22:51 +03:00
# define MAX30102_REG_INT_STATUS 0x00
# define MAX30102_REG_INT_STATUS_PWR_RDY BIT(0)
# define MAX30102_REG_INT_STATUS_PROX_INT BIT(4)
# define MAX30102_REG_INT_STATUS_ALC_OVF BIT(5)
# define MAX30102_REG_INT_STATUS_PPG_RDY BIT(6)
# define MAX30102_REG_INT_STATUS_FIFO_RDY BIT(7)
# define MAX30102_REG_INT_ENABLE 0x02
# define MAX30102_REG_INT_ENABLE_PROX_INT_EN BIT(4)
# define MAX30102_REG_INT_ENABLE_ALC_OVF_EN BIT(5)
# define MAX30102_REG_INT_ENABLE_PPG_EN BIT(6)
# define MAX30102_REG_INT_ENABLE_FIFO_EN BIT(7)
# define MAX30102_REG_INT_ENABLE_MASK 0xf0
# define MAX30102_REG_INT_ENABLE_MASK_SHIFT 4
# define MAX30102_REG_FIFO_WR_PTR 0x04
# define MAX30102_REG_FIFO_OVR_CTR 0x05
# define MAX30102_REG_FIFO_RD_PTR 0x06
# define MAX30102_REG_FIFO_DATA 0x07
2017-10-27 22:45:41 +03:00
# define MAX30102_REG_FIFO_DATA_BYTES 3
2017-02-04 13:22:51 +03:00
# define MAX30102_REG_FIFO_CONFIG 0x08
# define MAX30102_REG_FIFO_CONFIG_AVG_4SAMPLES BIT(1)
# define MAX30102_REG_FIFO_CONFIG_AVG_SHIFT 5
# define MAX30102_REG_FIFO_CONFIG_AFULL BIT(0)
# define MAX30102_REG_MODE_CONFIG 0x09
2017-10-27 22:45:40 +03:00
# define MAX30102_REG_MODE_CONFIG_MODE_NONE 0x00
2017-10-27 22:45:34 +03:00
# define MAX30102_REG_MODE_CONFIG_MODE_HR 0x02 /* red LED */
# define MAX30102_REG_MODE_CONFIG_MODE_HR_SPO2 0x03 /* red + IR LED */
# define MAX30102_REG_MODE_CONFIG_MODE_MULTI 0x07 /* multi-LED mode */
# define MAX30102_REG_MODE_CONFIG_MODE_MASK GENMASK(2, 0)
2017-02-04 13:22:51 +03:00
# define MAX30102_REG_MODE_CONFIG_PWR BIT(7)
2017-10-27 22:45:42 +03:00
# 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
2017-02-04 13:22:51 +03:00
# define MAX30102_REG_SPO2_CONFIG 0x0a
# define MAX30102_REG_SPO2_CONFIG_PULSE_411_US 0x03
# define MAX30102_REG_SPO2_CONFIG_SR_400HZ 0x03
# define MAX30102_REG_SPO2_CONFIG_SR_MASK 0x07
# define MAX30102_REG_SPO2_CONFIG_SR_MASK_SHIFT 2
# define MAX30102_REG_SPO2_CONFIG_ADC_4096_STEPS BIT(0)
# define MAX30102_REG_SPO2_CONFIG_ADC_MASK_SHIFT 5
# define MAX30102_REG_RED_LED_CONFIG 0x0c
# define MAX30102_REG_IR_LED_CONFIG 0x0d
2017-10-27 22:45:42 +03:00
# define MAX30105_REG_GREEN_LED_CONFIG 0x0e
2017-02-04 13:22:51 +03:00
# define MAX30102_REG_TEMP_CONFIG 0x21
# define MAX30102_REG_TEMP_CONFIG_TEMP_EN BIT(0)
# define MAX30102_REG_TEMP_INTEGER 0x1f
# define MAX30102_REG_TEMP_FRACTION 0x20
2017-10-27 22:45:36 +03:00
# define MAX30102_REG_REV_ID 0xfe
# define MAX30102_REG_PART_ID 0xff
2017-02-04 13:22:51 +03:00
struct max30102_data {
struct i2c_client * client ;
struct iio_dev * indio_dev ;
struct mutex lock ;
struct regmap * regmap ;
2017-10-27 22:45:42 +03:00
enum max30102_chip_id chip_id ;
2017-02-04 13:22:51 +03:00
2017-10-27 22:45:42 +03:00
u8 buffer [ 12 ] ;
__be32 processed_buffer [ 3 ] ; /* 3 x 18-bit (padded to 32-bits) */
2017-02-04 13:22:51 +03:00
} ;
static const struct regmap_config max30102_regmap_config = {
. name = MAX30102_REGMAP_NAME ,
. reg_bits = 8 ,
. val_bits = 8 ,
} ;
2017-10-27 22:45:39 +03:00
static const unsigned long max30102_scan_masks [ ] = {
BIT ( MAX30102_LED_RED ) | BIT ( MAX30102_LED_IR ) ,
0
} ;
2017-02-04 13:22:51 +03:00
2017-10-27 22:45:42 +03:00
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
} ;
2017-10-27 22:45:37 +03:00
# define MAX30102_INTENSITY_CHANNEL(_si, _mod) { \
. type = IIO_INTENSITY , \
. channel2 = _mod , \
. modified = 1 , \
. scan_index = _si , \
. scan_type = { \
. sign = ' u ' , \
. shift = 8 , \
. realbits = 18 , \
. storagebits = 32 , \
. endianness = IIO_BE , \
} , \
}
2017-02-04 13:22:51 +03:00
static const struct iio_chan_spec max30102_channels [ ] = {
2017-10-27 22:45:39 +03:00
MAX30102_INTENSITY_CHANNEL ( MAX30102_LED_RED , IIO_MOD_LIGHT_RED ) ,
MAX30102_INTENSITY_CHANNEL ( MAX30102_LED_IR , IIO_MOD_LIGHT_IR ) ,
2017-02-04 13:22:51 +03:00
{
. type = IIO_TEMP ,
. info_mask_separate =
BIT ( IIO_CHAN_INFO_RAW ) | BIT ( IIO_CHAN_INFO_SCALE ) ,
. scan_index = - 1 ,
} ,
} ;
2017-10-27 22:45:42 +03:00
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 ,
} ,
} ;
2017-10-27 22:45:40 +03:00
static int max30102_set_power ( struct max30102_data * data , bool en )
2017-02-04 13:22:51 +03:00
{
return regmap_update_bits ( data - > regmap , MAX30102_REG_MODE_CONFIG ,
MAX30102_REG_MODE_CONFIG_PWR ,
2017-10-27 22:45:40 +03:00
en ? 0 : MAX30102_REG_MODE_CONFIG_PWR ) ;
}
static int max30102_set_powermode ( struct max30102_data * data , u8 mode , bool en )
{
u8 reg = mode ;
if ( ! en )
reg | = MAX30102_REG_MODE_CONFIG_PWR ;
return regmap_update_bits ( data - > regmap , MAX30102_REG_MODE_CONFIG ,
MAX30102_REG_MODE_CONFIG_PWR |
MAX30102_REG_MODE_CONFIG_MODE_MASK , reg ) ;
2017-02-04 13:22:51 +03:00
}
2017-10-27 22:45:42 +03:00
# define MAX30102_MODE_CONTROL_LED_SLOTS(slot2, slot1) \
( ( slot2 < < MAX30102_REG_MODE_CONTROL_SLOT_SHIFT ) | slot1 )
2017-02-04 13:22:51 +03:00
static int max30102_buffer_postenable ( struct iio_dev * indio_dev )
{
struct max30102_data * data = iio_priv ( indio_dev ) ;
2017-10-27 22:45:42 +03:00
int ret ;
2017-10-27 22:45:40 +03:00
u8 reg ;
2017-10-27 22:45:42 +03:00
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 ;
}
2017-02-04 13:22:51 +03:00
2017-10-27 22:45:40 +03:00
return max30102_set_powermode ( data , reg , true ) ;
2017-02-04 13:22:51 +03:00
}
static int max30102_buffer_predisable ( struct iio_dev * indio_dev )
{
struct max30102_data * data = iio_priv ( indio_dev ) ;
2017-10-27 22:45:40 +03:00
return max30102_set_powermode ( data , MAX30102_REG_MODE_CONFIG_MODE_NONE ,
false ) ;
2017-02-04 13:22:51 +03:00
}
static const struct iio_buffer_setup_ops max30102_buffer_setup_ops = {
. postenable = max30102_buffer_postenable ,
. predisable = max30102_buffer_predisable ,
} ;
static inline int max30102_fifo_count ( struct max30102_data * data )
{
unsigned int val ;
int ret ;
ret = regmap_read ( data - > regmap , MAX30102_REG_INT_STATUS , & val ) ;
if ( ret )
return ret ;
/* FIFO has one sample slot left */
if ( val & MAX30102_REG_INT_STATUS_FIFO_RDY )
return 1 ;
return 0 ;
}
2017-10-27 22:45:41 +03:00
# define MAX30102_COPY_DATA(i) \
memcpy ( & data - > processed_buffer [ ( i ) ] , \
& buffer [ ( i ) * MAX30102_REG_FIFO_DATA_BYTES ] , \
MAX30102_REG_FIFO_DATA_BYTES )
2017-10-27 22:45:42 +03:00
static int max30102_read_measurement ( struct max30102_data * data ,
unsigned int measurements )
2017-02-04 13:22:51 +03:00
{
int ret ;
u8 * buffer = ( u8 * ) & data - > buffer ;
ret = i2c_smbus_read_i2c_block_data ( data - > client ,
MAX30102_REG_FIFO_DATA ,
2017-10-27 22:45:42 +03:00
measurements *
MAX30102_REG_FIFO_DATA_BYTES ,
2017-02-04 13:22:51 +03:00
buffer ) ;
2017-10-27 22:45:42 +03:00
switch ( measurements ) {
case 3 :
MAX30102_COPY_DATA ( 2 ) ;
2018-08-15 19:23:49 +03:00
/* fall through */
case 2 :
2017-10-27 22:45:42 +03:00
MAX30102_COPY_DATA ( 1 ) ;
2018-08-15 19:23:49 +03:00
/* fall through */
case 1 :
2017-10-27 22:45:42 +03:00
MAX30102_COPY_DATA ( 0 ) ;
break ;
default :
return - EINVAL ;
}
2017-02-04 13:22:51 +03:00
2017-10-27 22:45:42 +03:00
return ( ret = = measurements * MAX30102_REG_FIFO_DATA_BYTES ) ?
0 : - EINVAL ;
2017-02-04 13:22:51 +03:00
}
static irqreturn_t max30102_interrupt_handler ( int irq , void * private )
{
struct iio_dev * indio_dev = private ;
struct max30102_data * data = iio_priv ( indio_dev ) ;
2017-10-27 22:45:42 +03:00
unsigned int measurements = bitmap_weight ( indio_dev - > active_scan_mask ,
indio_dev - > masklength ) ;
2017-02-04 13:22:51 +03:00
int ret , cnt = 0 ;
mutex_lock ( & data - > lock ) ;
while ( cnt | | ( cnt = max30102_fifo_count ( data ) ) > 0 ) {
2017-10-27 22:45:42 +03:00
ret = max30102_read_measurement ( data , measurements ) ;
2017-02-04 13:22:51 +03:00
if ( ret )
break ;
iio_push_to_buffers ( data - > indio_dev , data - > processed_buffer ) ;
cnt - - ;
}
mutex_unlock ( & data - > lock ) ;
return IRQ_HANDLED ;
}
static int max30102_get_current_idx ( unsigned int val , int * reg )
{
/* each step is 0.200 mA */
* reg = val / 200 ;
return * reg > 0xff ? - EINVAL : 0 ;
}
static int max30102_led_init ( struct max30102_data * data )
{
struct device * dev = & data - > client - > dev ;
struct device_node * np = dev - > of_node ;
unsigned int val ;
int reg , ret ;
ret = of_property_read_u32 ( np , " maxim,red-led-current-microamp " , & val ) ;
if ( ret ) {
dev_info ( dev , " no red-led-current-microamp set \n " ) ;
/* Default to 7 mA RED LED */
val = 7000 ;
}
ret = max30102_get_current_idx ( val , & reg ) ;
if ( ret ) {
dev_err ( dev , " invalid RED LED current setting %d \n " , val ) ;
return ret ;
}
ret = regmap_write ( data - > regmap , MAX30102_REG_RED_LED_CONFIG , reg ) ;
if ( ret )
return ret ;
2017-10-27 22:45:42 +03:00
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 , & reg ) ;
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 ;
}
2017-02-04 13:22:51 +03:00
ret = of_property_read_u32 ( np , " maxim,ir-led-current-microamp " , & val ) ;
if ( ret ) {
dev_info ( dev , " no ir-led-current-microamp set \n " ) ;
/* Default to 7 mA IR LED */
val = 7000 ;
}
ret = max30102_get_current_idx ( val , & reg ) ;
if ( ret ) {
2017-10-27 22:45:32 +03:00
dev_err ( dev , " invalid IR LED current setting %d \n " , val ) ;
2017-02-04 13:22:51 +03:00
return ret ;
}
return regmap_write ( data - > regmap , MAX30102_REG_IR_LED_CONFIG , reg ) ;
}
static int max30102_chip_init ( struct max30102_data * data )
{
int ret ;
/* setup LED current settings */
ret = max30102_led_init ( data ) ;
if ( ret )
return ret ;
2017-10-27 22:45:40 +03:00
/* configure 18-bit HR + SpO2 readings at 400Hz */
2017-02-04 13:22:51 +03:00
ret = regmap_write ( data - > regmap , MAX30102_REG_SPO2_CONFIG ,
( MAX30102_REG_SPO2_CONFIG_ADC_4096_STEPS
< < MAX30102_REG_SPO2_CONFIG_ADC_MASK_SHIFT ) |
( MAX30102_REG_SPO2_CONFIG_SR_400HZ
< < MAX30102_REG_SPO2_CONFIG_SR_MASK_SHIFT ) |
MAX30102_REG_SPO2_CONFIG_PULSE_411_US ) ;
if ( ret )
return ret ;
/* average 4 samples + generate FIFO interrupt */
ret = regmap_write ( data - > regmap , MAX30102_REG_FIFO_CONFIG ,
( MAX30102_REG_FIFO_CONFIG_AVG_4SAMPLES
< < MAX30102_REG_FIFO_CONFIG_AVG_SHIFT ) |
MAX30102_REG_FIFO_CONFIG_AFULL ) ;
if ( ret )
return ret ;
/* enable FIFO interrupt */
return regmap_update_bits ( data - > regmap , MAX30102_REG_INT_ENABLE ,
MAX30102_REG_INT_ENABLE_MASK ,
MAX30102_REG_INT_ENABLE_FIFO_EN ) ;
}
static int max30102_read_temp ( struct max30102_data * data , int * val )
{
int ret ;
unsigned int reg ;
ret = regmap_read ( data - > regmap , MAX30102_REG_TEMP_INTEGER , & reg ) ;
if ( ret < 0 )
return ret ;
* val = reg < < 4 ;
ret = regmap_read ( data - > regmap , MAX30102_REG_TEMP_FRACTION , & reg ) ;
if ( ret < 0 )
return ret ;
* val | = reg & 0xf ;
* val = sign_extend32 ( * val , 11 ) ;
return 0 ;
}
2017-10-27 22:45:38 +03:00
static int max30102_get_temp ( struct max30102_data * data , int * val , bool en )
2017-02-04 13:22:51 +03:00
{
int ret ;
2017-10-27 22:45:38 +03:00
if ( en ) {
2017-10-27 22:45:40 +03:00
ret = max30102_set_power ( data , true ) ;
2017-10-27 22:45:38 +03:00
if ( ret )
return ret ;
}
2017-02-04 13:22:51 +03:00
/* start acquisition */
ret = regmap_update_bits ( data - > regmap , MAX30102_REG_TEMP_CONFIG ,
MAX30102_REG_TEMP_CONFIG_TEMP_EN ,
MAX30102_REG_TEMP_CONFIG_TEMP_EN ) ;
if ( ret )
2017-10-27 22:45:38 +03:00
goto out ;
2017-02-04 13:22:51 +03:00
msleep ( 35 ) ;
2017-10-27 22:45:38 +03:00
ret = max30102_read_temp ( data , val ) ;
out :
if ( en )
2017-10-27 22:45:40 +03:00
max30102_set_power ( data , false ) ;
2017-02-04 13:22:51 +03:00
2017-10-27 22:45:38 +03:00
return ret ;
2017-02-04 13:22:51 +03:00
}
static int max30102_read_raw ( struct iio_dev * indio_dev ,
struct iio_chan_spec const * chan ,
int * val , int * val2 , long mask )
{
struct max30102_data * data = iio_priv ( indio_dev ) ;
int ret = - EINVAL ;
switch ( mask ) {
case IIO_CHAN_INFO_RAW :
/*
2017-10-27 22:45:38 +03:00
* Temperature reading can only be acquired when not in
* shutdown ; leave shutdown briefly when buffer not running
2017-02-04 13:22:51 +03:00
*/
mutex_lock ( & indio_dev - > mlock ) ;
if ( ! iio_buffer_enabled ( indio_dev ) )
2017-10-27 22:45:38 +03:00
ret = max30102_get_temp ( data , val , true ) ;
else
ret = max30102_get_temp ( data , val , false ) ;
2017-02-04 13:22:51 +03:00
mutex_unlock ( & indio_dev - > mlock ) ;
2017-10-27 22:45:38 +03:00
if ( ret )
return ret ;
ret = IIO_VAL_INT ;
2017-02-04 13:22:51 +03:00
break ;
case IIO_CHAN_INFO_SCALE :
2017-10-27 22:45:31 +03:00
* val = 1000 ; /* 62.5 */
2017-02-04 13:22:51 +03:00
* val2 = 16 ;
ret = IIO_VAL_FRACTIONAL ;
break ;
}
return ret ;
}
static const struct iio_info max30102_info = {
. read_raw = max30102_read_raw ,
} ;
static int max30102_probe ( struct i2c_client * client ,
const struct i2c_device_id * id )
{
struct max30102_data * data ;
struct iio_buffer * buffer ;
struct iio_dev * indio_dev ;
int ret ;
2017-10-27 22:45:36 +03:00
unsigned int reg ;
2017-02-04 13:22:51 +03:00
indio_dev = devm_iio_device_alloc ( & client - > dev , sizeof ( * data ) ) ;
if ( ! indio_dev )
return - ENOMEM ;
buffer = devm_iio_kfifo_allocate ( & client - > dev ) ;
if ( ! buffer )
return - ENOMEM ;
iio_device_attach_buffer ( indio_dev , buffer ) ;
indio_dev - > name = MAX30102_DRV_NAME ;
indio_dev - > info = & max30102_info ;
indio_dev - > modes = ( INDIO_BUFFER_SOFTWARE | INDIO_DIRECT_MODE ) ;
indio_dev - > setup_ops = & max30102_buffer_setup_ops ;
2017-02-11 15:34:18 +03:00
indio_dev - > dev . parent = & client - > dev ;
2017-02-04 13:22:51 +03:00
data = iio_priv ( indio_dev ) ;
data - > indio_dev = indio_dev ;
data - > client = client ;
2017-10-27 22:45:42 +03:00
data - > chip_id = id - > driver_data ;
2017-02-04 13:22:51 +03:00
mutex_init ( & data - > lock ) ;
i2c_set_clientdata ( client , indio_dev ) ;
2017-10-27 22:45:42 +03:00
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 ;
}
2017-02-04 13:22:51 +03:00
data - > regmap = devm_regmap_init_i2c ( client , & max30102_regmap_config ) ;
if ( IS_ERR ( data - > regmap ) ) {
2017-10-27 22:45:33 +03:00
dev_err ( & client - > dev , " regmap initialization failed \n " ) ;
2017-02-04 13:22:51 +03:00
return PTR_ERR ( data - > regmap ) ;
}
2017-10-27 22:45:35 +03:00
2017-10-27 22:45:36 +03:00
/* check part ID */
ret = regmap_read ( data - > regmap , MAX30102_REG_PART_ID , & reg ) ;
if ( ret )
return ret ;
if ( reg ! = MAX30102_PART_NUMBER )
return - ENODEV ;
/* show revision ID */
ret = regmap_read ( data - > regmap , MAX30102_REG_REV_ID , & reg ) ;
if ( ret )
return ret ;
dev_dbg ( & client - > dev , " max3010x revision %02x \n " , reg ) ;
2017-10-27 22:45:40 +03:00
/* clear mode setting, chip shutdown */
ret = max30102_set_powermode ( data , MAX30102_REG_MODE_CONFIG_MODE_NONE ,
false ) ;
2017-10-27 22:45:35 +03:00
if ( ret )
return ret ;
2017-02-04 13:22:51 +03:00
ret = max30102_chip_init ( data ) ;
if ( ret )
return ret ;
if ( client - > irq < = 0 ) {
dev_err ( & client - > dev , " no valid irq defined \n " ) ;
return - EINVAL ;
}
ret = devm_request_threaded_irq ( & client - > dev , client - > irq ,
NULL , max30102_interrupt_handler ,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT ,
" max30102_irq " , indio_dev ) ;
if ( ret ) {
dev_err ( & client - > dev , " request irq (%d) failed \n " , client - > irq ) ;
return ret ;
}
return iio_device_register ( indio_dev ) ;
}
static int max30102_remove ( struct i2c_client * client )
{
struct iio_dev * indio_dev = i2c_get_clientdata ( client ) ;
struct max30102_data * data = iio_priv ( indio_dev ) ;
iio_device_unregister ( indio_dev ) ;
2017-10-27 22:45:40 +03:00
max30102_set_power ( data , false ) ;
2017-02-04 13:22:51 +03:00
return 0 ;
}
static const struct i2c_device_id max30102_id [ ] = {
2017-10-27 22:45:42 +03:00
{ " max30102 " , max30102 } ,
{ " max30105 " , max30105 } ,
2017-02-04 13:22:51 +03:00
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , max30102_id ) ;
static const struct of_device_id max30102_dt_ids [ ] = {
{ . compatible = " maxim,max30102 " } ,
2017-10-27 22:45:42 +03:00
{ . compatible = " maxim,max30105 " } ,
2017-02-04 13:22:51 +03:00
{ }
} ;
MODULE_DEVICE_TABLE ( of , max30102_dt_ids ) ;
static struct i2c_driver max30102_driver = {
. driver = {
. name = MAX30102_DRV_NAME ,
. of_match_table = of_match_ptr ( max30102_dt_ids ) ,
} ,
. probe = max30102_probe ,
. remove = max30102_remove ,
. id_table = max30102_id ,
} ;
module_i2c_driver ( max30102_driver ) ;
MODULE_AUTHOR ( " Matt Ranostay <matt@ranostay.consulting> " ) ;
2017-10-27 22:45:42 +03:00
MODULE_DESCRIPTION ( " MAX30102 heart rate/pulse oximeter and MAX30105 particle sensor driver " ) ;
2017-02-04 13:22:51 +03:00
MODULE_LICENSE ( " GPL " ) ;