2018-02-18 08:36:46 +03:00
// SPDX-License-Identifier: GPL-2.0+
2015-12-10 09:04:49 +03:00
/*
* max30100 . c - Support for MAX30100 heart rate and pulse oximeter sensor
*
2018-02-18 08:36:46 +03:00
* Copyright ( C ) 2015 , 2018
* Author : Matt Ranostay < matt . ranostay @ konsulko . com >
2015-12-10 09:04:49 +03:00
*
2015-12-30 08:44:48 +03:00
* TODO : enable pulse length controls via device tree properties
2015-12-10 09:04:49 +03:00
*/
# 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>
2020-03-15 18:25:12 +03:00
# include <linux/property.h>
2015-12-10 09:04:49 +03:00
# include <linux/regmap.h>
# include <linux/iio/iio.h>
# include <linux/iio/buffer.h>
# include <linux/iio/kfifo_buf.h>
# define MAX30100_REGMAP_NAME "max30100_regmap"
# define MAX30100_DRV_NAME "max30100"
# define MAX30100_REG_INT_STATUS 0x00
# define MAX30100_REG_INT_STATUS_PWR_RDY BIT(0)
# define MAX30100_REG_INT_STATUS_SPO2_RDY BIT(4)
# define MAX30100_REG_INT_STATUS_HR_RDY BIT(5)
# define MAX30100_REG_INT_STATUS_FIFO_RDY BIT(7)
# define MAX30100_REG_INT_ENABLE 0x01
# define MAX30100_REG_INT_ENABLE_SPO2_EN BIT(0)
# define MAX30100_REG_INT_ENABLE_HR_EN BIT(1)
# define MAX30100_REG_INT_ENABLE_FIFO_EN BIT(3)
# define MAX30100_REG_INT_ENABLE_MASK 0xf0
# define MAX30100_REG_INT_ENABLE_MASK_SHIFT 4
# define MAX30100_REG_FIFO_WR_PTR 0x02
# define MAX30100_REG_FIFO_OVR_CTR 0x03
# define MAX30100_REG_FIFO_RD_PTR 0x04
# define MAX30100_REG_FIFO_DATA 0x05
# define MAX30100_REG_FIFO_DATA_ENTRY_COUNT 16
# define MAX30100_REG_FIFO_DATA_ENTRY_LEN 4
# define MAX30100_REG_MODE_CONFIG 0x06
# define MAX30100_REG_MODE_CONFIG_MODE_SPO2_EN BIT(0)
# define MAX30100_REG_MODE_CONFIG_MODE_HR_EN BIT(1)
# define MAX30100_REG_MODE_CONFIG_MODE_MASK 0x03
# define MAX30100_REG_MODE_CONFIG_TEMP_EN BIT(3)
# define MAX30100_REG_MODE_CONFIG_PWR BIT(7)
# define MAX30100_REG_SPO2_CONFIG 0x07
# define MAX30100_REG_SPO2_CONFIG_100HZ BIT(2)
# define MAX30100_REG_SPO2_CONFIG_HI_RES_EN BIT(6)
# define MAX30100_REG_SPO2_CONFIG_1600US 0x3
# define MAX30100_REG_LED_CONFIG 0x09
2015-12-30 08:44:48 +03:00
# define MAX30100_REG_LED_CONFIG_LED_MASK 0x0f
2015-12-10 09:04:49 +03:00
# define MAX30100_REG_LED_CONFIG_RED_LED_SHIFT 4
# define MAX30100_REG_LED_CONFIG_24MA 0x07
# define MAX30100_REG_LED_CONFIG_50MA 0x0f
# define MAX30100_REG_TEMP_INTEGER 0x16
# define MAX30100_REG_TEMP_FRACTION 0x17
struct max30100_data {
struct i2c_client * client ;
struct iio_dev * indio_dev ;
struct mutex lock ;
struct regmap * regmap ;
__be16 buffer [ 2 ] ; /* 2 16-bit channels */
} ;
static bool max30100_is_volatile_reg ( struct device * dev , unsigned int reg )
{
switch ( reg ) {
case MAX30100_REG_INT_STATUS :
case MAX30100_REG_MODE_CONFIG :
case MAX30100_REG_FIFO_WR_PTR :
case MAX30100_REG_FIFO_OVR_CTR :
case MAX30100_REG_FIFO_RD_PTR :
case MAX30100_REG_FIFO_DATA :
case MAX30100_REG_TEMP_INTEGER :
case MAX30100_REG_TEMP_FRACTION :
return true ;
default :
return false ;
}
}
static const struct regmap_config max30100_regmap_config = {
. name = MAX30100_REGMAP_NAME ,
. reg_bits = 8 ,
. val_bits = 8 ,
. max_register = MAX30100_REG_TEMP_FRACTION ,
. cache_type = REGCACHE_FLAT ,
. volatile_reg = max30100_is_volatile_reg ,
} ;
2015-12-30 08:44:48 +03:00
static const unsigned int max30100_led_current_mapping [ ] = {
4400 , 7600 , 11000 , 14200 , 17400 ,
20800 , 24000 , 27100 , 30600 , 33800 ,
37000 , 40200 , 43600 , 46800 , 50000
} ;
2015-12-10 09:04:49 +03:00
static const unsigned long max30100_scan_masks [ ] = { 0x3 , 0 } ;
static const struct iio_chan_spec max30100_channels [ ] = {
{
. type = IIO_INTENSITY ,
. channel2 = IIO_MOD_LIGHT_IR ,
. modified = 1 ,
. scan_index = 0 ,
. scan_type = {
. sign = ' u ' ,
. realbits = 16 ,
. storagebits = 16 ,
. endianness = IIO_BE ,
} ,
} ,
{
. type = IIO_INTENSITY ,
. channel2 = IIO_MOD_LIGHT_RED ,
. modified = 1 ,
. scan_index = 1 ,
. scan_type = {
. sign = ' u ' ,
. realbits = 16 ,
. storagebits = 16 ,
. endianness = IIO_BE ,
} ,
} ,
{
. type = IIO_TEMP ,
. info_mask_separate =
BIT ( IIO_CHAN_INFO_RAW ) | BIT ( IIO_CHAN_INFO_SCALE ) ,
. scan_index = - 1 ,
} ,
} ;
static int max30100_set_powermode ( struct max30100_data * data , bool state )
{
return regmap_update_bits ( data - > regmap , MAX30100_REG_MODE_CONFIG ,
MAX30100_REG_MODE_CONFIG_PWR ,
state ? 0 : MAX30100_REG_MODE_CONFIG_PWR ) ;
}
static int max30100_clear_fifo ( struct max30100_data * data )
{
int ret ;
ret = regmap_write ( data - > regmap , MAX30100_REG_FIFO_WR_PTR , 0 ) ;
if ( ret )
return ret ;
ret = regmap_write ( data - > regmap , MAX30100_REG_FIFO_OVR_CTR , 0 ) ;
if ( ret )
return ret ;
return regmap_write ( data - > regmap , MAX30100_REG_FIFO_RD_PTR , 0 ) ;
}
static int max30100_buffer_postenable ( struct iio_dev * indio_dev )
{
struct max30100_data * data = iio_priv ( indio_dev ) ;
int ret ;
ret = max30100_set_powermode ( data , true ) ;
if ( ret )
return ret ;
return max30100_clear_fifo ( data ) ;
}
static int max30100_buffer_predisable ( struct iio_dev * indio_dev )
{
struct max30100_data * data = iio_priv ( indio_dev ) ;
return max30100_set_powermode ( data , false ) ;
}
static const struct iio_buffer_setup_ops max30100_buffer_setup_ops = {
. postenable = max30100_buffer_postenable ,
. predisable = max30100_buffer_predisable ,
} ;
static inline int max30100_fifo_count ( struct max30100_data * data )
{
unsigned int val ;
int ret ;
ret = regmap_read ( data - > regmap , MAX30100_REG_INT_STATUS , & val ) ;
if ( ret )
return ret ;
/* FIFO is almost full */
if ( val & MAX30100_REG_INT_STATUS_FIFO_RDY )
return MAX30100_REG_FIFO_DATA_ENTRY_COUNT - 1 ;
return 0 ;
}
static int max30100_read_measurement ( struct max30100_data * data )
{
int ret ;
ret = i2c_smbus_read_i2c_block_data ( data - > client ,
MAX30100_REG_FIFO_DATA ,
MAX30100_REG_FIFO_DATA_ENTRY_LEN ,
( u8 * ) & data - > buffer ) ;
return ( ret = = MAX30100_REG_FIFO_DATA_ENTRY_LEN ) ? 0 : ret ;
}
static irqreturn_t max30100_interrupt_handler ( int irq , void * private )
{
struct iio_dev * indio_dev = private ;
struct max30100_data * data = iio_priv ( indio_dev ) ;
int ret , cnt = 0 ;
mutex_lock ( & data - > lock ) ;
2017-01-17 05:04:18 +03:00
while ( cnt | | ( cnt = max30100_fifo_count ( data ) ) > 0 ) {
2015-12-10 09:04:49 +03:00
ret = max30100_read_measurement ( data ) ;
if ( ret )
break ;
iio_push_to_buffers ( data - > indio_dev , data - > buffer ) ;
2016-03-26 06:42:58 +03:00
cnt - - ;
2015-12-10 09:04:49 +03:00
}
mutex_unlock ( & data - > lock ) ;
return IRQ_HANDLED ;
}
2015-12-30 08:44:48 +03:00
static int max30100_get_current_idx ( unsigned int val , int * reg )
{
int idx ;
/* LED turned off */
if ( val = = 0 ) {
* reg = 0 ;
return 0 ;
}
for ( idx = 0 ; idx < ARRAY_SIZE ( max30100_led_current_mapping ) ; idx + + ) {
if ( max30100_led_current_mapping [ idx ] = = val ) {
* reg = idx + 1 ;
return 0 ;
}
}
return - EINVAL ;
}
static int max30100_led_init ( struct max30100_data * data )
{
struct device * dev = & data - > client - > dev ;
unsigned int val [ 2 ] ;
int reg , ret ;
2020-03-15 18:25:12 +03:00
ret = device_property_read_u32_array ( dev , " maxim,led-current-microamp " ,
2015-12-30 08:44:48 +03:00
( unsigned int * ) & val , 2 ) ;
if ( ret ) {
/* Default to 24 mA RED LED, 50 mA IR LED */
reg = ( MAX30100_REG_LED_CONFIG_24MA < <
MAX30100_REG_LED_CONFIG_RED_LED_SHIFT ) |
MAX30100_REG_LED_CONFIG_50MA ;
dev_warn ( dev , " no led-current-microamp set " ) ;
return regmap_write ( data - > regmap , MAX30100_REG_LED_CONFIG , reg ) ;
}
/* RED LED current */
ret = max30100_get_current_idx ( val [ 0 ] , & reg ) ;
if ( ret ) {
dev_err ( dev , " invalid RED current setting %d " , val [ 0 ] ) ;
return ret ;
}
ret = regmap_update_bits ( data - > regmap , MAX30100_REG_LED_CONFIG ,
MAX30100_REG_LED_CONFIG_LED_MASK < <
MAX30100_REG_LED_CONFIG_RED_LED_SHIFT ,
reg < < MAX30100_REG_LED_CONFIG_RED_LED_SHIFT ) ;
if ( ret )
return ret ;
/* IR LED current */
ret = max30100_get_current_idx ( val [ 1 ] , & reg ) ;
if ( ret ) {
dev_err ( dev , " invalid IR current setting %d " , val [ 1 ] ) ;
return ret ;
}
return regmap_update_bits ( data - > regmap , MAX30100_REG_LED_CONFIG ,
MAX30100_REG_LED_CONFIG_LED_MASK , reg ) ;
}
2015-12-10 09:04:49 +03:00
static int max30100_chip_init ( struct max30100_data * data )
{
int ret ;
2015-12-30 08:44:48 +03:00
/* setup LED current settings */
ret = max30100_led_init ( data ) ;
2015-12-10 09:04:49 +03:00
if ( ret )
return ret ;
/* enable hi-res SPO2 readings at 100Hz */
ret = regmap_write ( data - > regmap , MAX30100_REG_SPO2_CONFIG ,
MAX30100_REG_SPO2_CONFIG_HI_RES_EN |
MAX30100_REG_SPO2_CONFIG_100HZ ) ;
if ( ret )
return ret ;
/* enable SPO2 mode */
ret = regmap_update_bits ( data - > regmap , MAX30100_REG_MODE_CONFIG ,
MAX30100_REG_MODE_CONFIG_MODE_MASK ,
MAX30100_REG_MODE_CONFIG_MODE_HR_EN |
MAX30100_REG_MODE_CONFIG_MODE_SPO2_EN ) ;
if ( ret )
return ret ;
/* enable FIFO interrupt */
return regmap_update_bits ( data - > regmap , MAX30100_REG_INT_ENABLE ,
MAX30100_REG_INT_ENABLE_MASK ,
MAX30100_REG_INT_ENABLE_FIFO_EN
< < MAX30100_REG_INT_ENABLE_MASK_SHIFT ) ;
}
static int max30100_read_temp ( struct max30100_data * data , int * val )
{
int ret ;
unsigned int reg ;
ret = regmap_read ( data - > regmap , MAX30100_REG_TEMP_INTEGER , & reg ) ;
if ( ret < 0 )
return ret ;
* val = reg < < 4 ;
ret = regmap_read ( data - > regmap , MAX30100_REG_TEMP_FRACTION , & reg ) ;
if ( ret < 0 )
return ret ;
* val | = reg & 0xf ;
* val = sign_extend32 ( * val , 11 ) ;
return 0 ;
}
static int max30100_get_temp ( struct max30100_data * data , int * val )
{
int ret ;
/* start acquisition */
ret = regmap_update_bits ( data - > regmap , MAX30100_REG_MODE_CONFIG ,
MAX30100_REG_MODE_CONFIG_TEMP_EN ,
MAX30100_REG_MODE_CONFIG_TEMP_EN ) ;
if ( ret )
return ret ;
2017-01-16 18:35:53 +03:00
msleep ( 35 ) ;
2015-12-10 09:04:49 +03:00
return max30100_read_temp ( data , val ) ;
}
static int max30100_read_raw ( struct iio_dev * indio_dev ,
struct iio_chan_spec const * chan ,
int * val , int * val2 , long mask )
{
struct max30100_data * data = iio_priv ( indio_dev ) ;
int ret = - EINVAL ;
switch ( mask ) {
case IIO_CHAN_INFO_RAW :
/*
* Temperature reading can only be acquired while engine
* is running
*/
mutex_lock ( & indio_dev - > mlock ) ;
if ( ! iio_buffer_enabled ( indio_dev ) )
ret = - EAGAIN ;
else {
ret = max30100_get_temp ( data , val ) ;
if ( ! ret )
ret = IIO_VAL_INT ;
}
mutex_unlock ( & indio_dev - > mlock ) ;
break ;
case IIO_CHAN_INFO_SCALE :
* val = 1 ; /* 0.0625 */
* val2 = 16 ;
ret = IIO_VAL_FRACTIONAL ;
break ;
}
return ret ;
}
static const struct iio_info max30100_info = {
. read_raw = max30100_read_raw ,
} ;
static int max30100_probe ( struct i2c_client * client ,
const struct i2c_device_id * id )
{
struct max30100_data * data ;
struct iio_dev * indio_dev ;
int ret ;
indio_dev = devm_iio_device_alloc ( & client - > dev , sizeof ( * data ) ) ;
if ( ! indio_dev )
return - ENOMEM ;
indio_dev - > name = MAX30100_DRV_NAME ;
indio_dev - > channels = max30100_channels ;
indio_dev - > info = & max30100_info ;
indio_dev - > num_channels = ARRAY_SIZE ( max30100_channels ) ;
indio_dev - > available_scan_masks = max30100_scan_masks ;
2021-02-15 13:40:22 +03:00
indio_dev - > modes = INDIO_DIRECT_MODE ;
ret = devm_iio_kfifo_buffer_setup ( & client - > dev , indio_dev ,
INDIO_BUFFER_SOFTWARE ,
& max30100_buffer_setup_ops ) ;
if ( ret )
return ret ;
2015-12-10 09:04:49 +03:00
data = iio_priv ( indio_dev ) ;
data - > indio_dev = indio_dev ;
data - > client = client ;
mutex_init ( & data - > lock ) ;
i2c_set_clientdata ( client , indio_dev ) ;
data - > regmap = devm_regmap_init_i2c ( client , & max30100_regmap_config ) ;
if ( IS_ERR ( data - > regmap ) ) {
dev_err ( & client - > dev , " regmap initialization failed. \n " ) ;
return PTR_ERR ( data - > regmap ) ;
}
max30100_set_powermode ( data , false ) ;
ret = max30100_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 , max30100_interrupt_handler ,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT ,
" max30100_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 max30100_remove ( struct i2c_client * client )
{
struct iio_dev * indio_dev = i2c_get_clientdata ( client ) ;
struct max30100_data * data = iio_priv ( indio_dev ) ;
iio_device_unregister ( indio_dev ) ;
max30100_set_powermode ( data , false ) ;
return 0 ;
}
static const struct i2c_device_id max30100_id [ ] = {
{ " max30100 " , 0 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , max30100_id ) ;
static const struct of_device_id max30100_dt_ids [ ] = {
{ . compatible = " maxim,max30100 " } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , max30100_dt_ids ) ;
static struct i2c_driver max30100_driver = {
. driver = {
. name = MAX30100_DRV_NAME ,
2020-03-15 18:25:12 +03:00
. of_match_table = max30100_dt_ids ,
2015-12-10 09:04:49 +03:00
} ,
. probe = max30100_probe ,
. remove = max30100_remove ,
. id_table = max30100_id ,
} ;
module_i2c_driver ( max30100_driver ) ;
2018-02-18 08:36:46 +03:00
MODULE_AUTHOR ( " Matt Ranostay <matt.ranostay@konsulko.com> " ) ;
2015-12-10 09:04:49 +03:00
MODULE_DESCRIPTION ( " MAX30100 heart rate and pulse oximeter sensor " ) ;
MODULE_LICENSE ( " GPL " ) ;