blob: e273cb0a4a5686c4a6ef39adc166bc148836071b [file] [log] [blame]
/*
* Copyright (c) 2014 JY Kim, jy.kim@maximintegrated.com
* Copyright (c) 2013 Maxim Integrated Products, Inc.
*
* This software is licensed under the terms of the GNU General Public
* License, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define VENDOR "MAXIM"
#define MAX86915_CHIP_NAME "MAX86915"
#define MAX86917_CHIP_NAME "MAX86917"
#define VERSION "3"
#define SUB_VERSION "0"
#define VENDOR_VERSION "m"
#define MODULE_NAME_HRM "hrm_sensor"
#define SLAVE_ADDR_MAX 0x57
#define MAX86915_I2C_RETRY_DELAY 10
#define MAX86915_I2C_MAX_RETRIES 5
#define DEFAULT_THRESHOLD -4194303
#define MAX86915_THRESHOLD_DEFAULT 100000
/* Default Register Value */
#define MAX86915_PART_ID 0x2B
#define MAX86915_REV_ID 0x01
#define MAX86915_HRM_DEFAULT_CURRENT1 0x46 /* IR */
#define MAX86915_HRM_DEFAULT_CURRENT2 0x00 /* RED */
#define MAX86915_HRM_DEFAULT_CURRENT3 0x00 /* GREEN */
#define MAX86915_HRM_DEFAULT_CURRENT4 0x00 /* BLUE */
#define MAX86915_HRM_DEFAULT_XTALK_CODE1 0x00 /* IR */
#define MAX86915_HRM_DEFAULT_XTALK_CODE2 0x00 /* RED */
#define MAX86915_HRM_DEFAULT_XTALK_CODE3 0x00 /* GREEN */
#define MAX86915_HRM_DEFAULT_XTALK_CODE4 0x00 /* BLUE */
#define MAX86915_DEFAULT_XTALK_CODE1 0x00 /* IR */
#define MAX86915_DEFAULT_XTALK_CODE2 0x00 /* RED */
#define MAX86915_DEFAULT_XTALK_CODE3 0x00 /* GREEN */
#define MAX86915_DEFAULT_XTALK_CODE4 0x00 /* BLUE */
#define MAX86915_IR_INIT_CURRENT 0x46 /* IR */
#define MAX86915_RED_INIT_CURRENT 0x41 /* RED */
#define MAX86915_GREEN_INIT_CURRENT 0x28 /* GREEN */
#define MAX86915_BLUE_INIT_CURRENT 0x3C /* BLUE */
#define MAX86915_IR_CURRENT_STEP 0x04 /* IR */
#define MAX86915_RED_CURRENT_STEP 0x04 /* RED */
#define MAX86915_GREEN_CURRENT_STEP 0x03 /* GREEN */
#define MAX86915_BLUE_CURRENT_STEP 0x05 /* BLUE */
/* AWB/Flicker Definition */
#define AWB_INTERVAL 20 /* 20 sample(from 17 to 28) */
#define CONFIG_SKIP_CNT 8
#define FLICKER_DATA_CNT 200
#define MAX86915_IOCTL_MAGIC 0xFD
#define MAX86915_IOCTL_READ_FLICKER _IOR(MAX86915_IOCTL_MAGIC, 0x01, int *)
#define AWB_MAX86915_CONFIG_TH2 480000
#define AWB_MAX86915_CONFIG_TH3 40000
/* AGC Configuration */
#define AGC_IR 0
#define AGC_RED 1
#define AGC_GREEN 2
#define AGC_BLUE 3
#define AGC_SKIP_CNT 5
#define AGC_LED_SKIP_CNT 16
#ifdef MAXIM_AGC
#define MAX86915_DEFAULT_LED_RGE 0x02 /* 150mA AGCCONF*/
#define MAX86915_MIN_CURRENT 0
#define MAX86915_MAX_CURRENT ((MAX86915_DEFAULT_LED_RGE+1)*51000)
#define MAX86915_CURRENT_FULL_SCALE \
(MAX86915_MAX_CURRENT - MAX86915_MIN_CURRENT)
#define MAX86915_CURRENT_PER_STEP ((MAX86915_DEFAULT_LED_RGE+1) * 200)
#else
#define MAX86915_DEFAULT_LED_RGE1 0x02 /* 150mA AGCCONF*/
#define MAX86915_DEFAULT_LED_RGE2 0x02 /* 150mA AGCCONF*/
#define MAX86915_DEFAULT_LED_RGE3 0x02 /* 150mA AGCCONF*/
#define MAX86915_DEFAULT_LED_RGE4 0x02 /* 150mA AGCCONF*/
#define MAX86915_MIN_CURRENT 0
#define MAX86915_MAX_CURRENT(X) (((X)+1)*51000)
#define MAX86915_CURRENT_FULL_SCALE(X) \
(MAX86915_MAX_CURRENT(X) - MAX86915_MIN_CURRENT)
#define MAX86915_CURRENT_PER_STEP(X) (((X)+1) * 200)
#endif
#define MAX86915_MIN_DIODE_VAL 0
#define MAX86915_MAX_DIODE_VAL ((1 << 19) - 1)
#define MAX86915_AGC_DEFAULT_LED_OUT_RANGE 70
#define MAX86915_AGC_DEFAULT_CORRECTION_COEFF 50
#define MAX86915_AGC_DEFAULT_SENSITIVITY_PERCENT 14
#define MAX86915_AGC_DEFAULT_MIN_NUM_PERCENT (10)
/* Linear AGC */
#define MAX86915_AGC_ERROR_RANGE1 8
#define MAX86915_AGC_ERROR_RANGE2 28
#define ILLEGAL_OUTPUT_POINTER -1
#define CONSTRAINT_VIOLATION -2
/* turnning parameter of dc_step. */
#define EOL_NEW_HRM_ARRY_SIZE 3
#define EOL_NEW_MODE_DC_MID 0
#define EOL_NEW_MODE_DC_HIGH 1
#define EOL_NEW_MODE_DC_XTALK 2
/* turnning parameter of skip count */
#define EOL_NEW_START_SKIP_CNT 134
/* turnning parameter of flicker_step. */
#define EOL_NEW_FLICKER_RETRY_CNT 48
#define EOL_NEW_FLICKER_SKIP_CNT 34
#define EOL_NEW_FLICKER_THRESH 8000
#define EOL_NEW_DC_MODE_SKIP_CNT 134
#define EOL_NEW_DC_FREQ_SKIP_CNT 134
#define EOL_NEW_DC_MIDDLE_SKIP_CNT 134
#define EOL_NEW_DC_HIGH_SKIP_CNT 134
#define EOL_NEW_DC_XTALK_SKIP_CNT 134
#define EOL_NEW_STD_TOTAL_CNT (116 - 12)
/* turnning parameter of frep_step. */
#define EOL_NEW_FREQ_MIN 385
#define EOL_NEW_FREQ_RETRY_CNT 5
#define EOL_NEW_FREQ_SKEW_RETRY_CNT 5
#define EOL_NEW_FREQ_SKIP_CNT 10
/* total buffer size. */
#define EOL_NEW_HRM_SIZE 400 /* <=this size should be lager than below size. */
#define EOL_NEW_FLICKER_SIZE 256
#define EOL_NEW_DC_MIDDLE_SIZE 256
#define EOL_NEW_DC_HIGH_SIZE 256
#define EOL_NEW_DC_XTALK_SIZE 256
#define EOL_NEW_STD2_SIZE 10
#define EOL_NEW_XTALK_SKIP_CNT 50
#define EOL_NEW_XTALK_SIZE 50
/* the led current of DC step */
#define EOL_NEW_DC_MID_LED_IR_CURRENT 0xA6
#define EOL_NEW_DC_MID_LED_RED_CURRENT 0xA6
#define EOL_NEW_DC_MID_LED_GREEN_CURRENT 0xA6
#define EOL_NEW_DC_MID_LED_BLUE_CURRENT 0xA6
#define EOL_NEW_DC_HIGH_LED_IR_CURRENT 0xFA
#define EOL_NEW_DC_HIGH_LED_RED_CURRENT 0xFA
#define EOL_NEW_DC_HIGH_LED_GREEN_CURRENT 0xE9
#define EOL_NEW_DC_HIGH_LED_BLUE_CURRENT 0xE9
/* the led current of Frequency step */
#define EOL_NEW_FREQUENCY_LED_IR_CURRENT 0xFA
#define EOL_NEW_FREQUENCY_LED_RED_CURRENT 0xFA
#define EOL_NEW_FREQUENCY_LED_GREEN_CURRENT 0xFA
#define EOL_NEW_FREQUENCY_LED_BLUE_CURRENT 0xFA
#define EOL_NEW_HRM_COMPLETE_ALL_STEP 0x07
#define EOL_SUCCESS 0x1
#define EOL_NEW_TYPE 0x1
#define DEFAULT_FIFO_CNT 1
#define CONFIG_4CH_INPUT
/* #define CONFIG_HRM_GREEN_BLUE */
#include "max86915.h"
static int hrm_debug = 1;
static int hrm_info;
module_param(hrm_debug, int, S_IRUGO | S_IWUSR);
module_param(hrm_info, int, S_IRUGO | S_IWUSR);
static struct max86915_device_data *max86915_data;
static u8 agc_debug_enabled = 1;
static u8 fifo_full_cnt = DEFAULT_FIFO_CNT;
/* #define DEBUG_HRMSENSOR */
#ifdef DEBUG_HRMSENSOR
static void max86915_debug_var(struct max86915_device_data *data)
{
HRM_dbg("===== %s =====\n", __func__);
HRM_dbg("%s client %p slave_addr 0x%x\n", __func__,
data->client, data->client->addr);
HRM_dbg("%s dev %p\n", __func__, data->dev);
HRM_dbg("%s hrm_input_dev %p\n", __func__, data->hrm_input_dev);
HRM_dbg("%s hrm_pinctrl %p\n", __func__, data->hrm_pinctrl);
HRM_dbg("%s pins_sleep %p\n", __func__, data->pins_sleep);
HRM_dbg("%s pins_idle %p\n", __func__, data->pins_idle);
HRM_dbg("%s led_3p3 %s\n", __func__, data->led_3p3);
HRM_dbg("%s vdd_1p8 %s\n", __func__, data->vdd_1p8);
HRM_dbg("%s i2c_1p8 %s\n", __func__, data->i2c_1p8);
HRM_dbg("%s hrm_enabled_mode %d\n", __func__, data->enabled_mode);
HRM_dbg("%s hrm_sampling_rate %d\n", __func__, data->hrm_sampling_rate);
HRM_dbg("%s regulator_state %d\n", __func__, data->regulator_state);
HRM_dbg("%s hrm_int %d\n", __func__, data->pin_hrm_int);
HRM_dbg("%s hrm_en %d\n", __func__, data->pin_hrm_en);
HRM_dbg("%s hrm_irq %d\n", __func__, data->dev_irq);
HRM_dbg("%s irq_state %d\n", __func__, data->irq_state);
HRM_dbg("%s hrm_threshold %d\n", __func__, data->hrm_threshold);
HRM_dbg("%s eol_test_is_enable %d\n", __func__,
data->eol_test_is_enable);
HRM_dbg("%s eol_test_status %d\n", __func__,
data->eol_test_status);
HRM_dbg("%s pre_eol_test_is_enable %d\n", __func__,
data->pre_eol_test_is_enable);
HRM_dbg("%s lib_ver %s\n", __func__, data->lib_ver);
HRM_dbg("===== %s =====\n", __func__);
}
#endif
static int max86915_write_reg(struct max86915_device_data *device,
u8 reg_addr, u8 data)
{
int err;
int tries = 0;
int num = 1;
u8 buffer[2] = { reg_addr, data };
struct i2c_msg msgs[] = {
{
.addr = device->client->addr,
.flags = device->client->flags & I2C_M_TEN,
.len = 2,
.buf = buffer,
},
};
if (!device->pm_state || device->regulator_state == 0) {
HRM_err("%s - write error, pm suspend or reg_state %d\n",
__func__, device->regulator_state);
err = -EFAULT;
return err;
}
mutex_lock(&device->suspendlock);
do {
err = i2c_transfer(device->client->adapter, msgs, num);
if (err != num)
msleep_interruptible(MAX86915_I2C_RETRY_DELAY);
if (err < 0)
HRM_err("%s - i2c_transfer error = %d\n", __func__, err);
} while ((err != num) && (++tries < MAX86915_I2C_MAX_RETRIES));
mutex_unlock(&device->suspendlock);
if (err != num) {
HRM_err("%s -write transfer error:%d\n", __func__, err);
err = -EIO;
device->i2c_err_cnt++;
return err;
}
return 0;
}
static int max86915_read_reg(struct max86915_device_data *device,
u8 reg_addr, u8 *buffer, int length)
{
int err = -1;
int tries = 0; /* # of attempts to read the device */
int num = 2;
struct i2c_msg msgs[] = {
{
.addr = device->client->addr,
.flags = device->client->flags & I2C_M_TEN,
.len = 1,
.buf = buffer,
},
{
.addr = device->client->addr,
.flags = (device->client->flags & I2C_M_TEN) | I2C_M_RD,
.len = length,
.buf = buffer,
},
};
if (!device->pm_state || device->regulator_state == 0) {
HRM_err("%s - read error, pm suspend or reg_state %d\n",
__func__, device->regulator_state);
err = -EFAULT;
return err;
}
mutex_lock(&device->suspendlock);
do {
buffer[0] = reg_addr;
err = i2c_transfer(device->client->adapter, msgs, num);
if (err != num)
msleep_interruptible(MAX86915_I2C_RETRY_DELAY);
if (err < 0)
HRM_err("%s - i2c_transfer error = %d\n", __func__, err);
} while ((err != num) && (++tries < MAX86915_I2C_MAX_RETRIES));
mutex_unlock(&device->suspendlock);
if (err != num) {
HRM_err("%s -read transfer error:%d\n", __func__, err);
err = -EIO;
device->i2c_err_cnt++;
} else
err = 0;
return err;
}
static int max86915_print_reg_status(void)
{
int reg, err;
u8 recvData;
for (reg = 0; reg < 0x55; reg++) {
err = max86915_read_reg(max86915_data, reg, &recvData, 1);
if (err != 0) {
HRM_err("%s - error reading 0x%02x err:%d\n",
__func__, reg, err);
} else {
HRM_dbg("%s - 0x%02x = 0x%02x\n",
__func__, reg, recvData);
}
}
return 0;
}
static int max86915_set_sampling_rate(u32 sampling_period_ns)
{
int err = 0;
u8 recvData;
switch (sampling_period_ns) {
case 10000000:
fifo_full_cnt = 1;
break;
case 5000000:
fifo_full_cnt = 2;
break;
case 2500000:
fifo_full_cnt = 4;
break;
case 1250000:
fifo_full_cnt = 8;
break;
default:
HRM_err("%s - %dns is not supported.\n", __func__, sampling_period_ns);
return -EIO;
}
if (max86915_data->enabled_mode == MODE_SDK_IR) {
err = max86915_write_reg(max86915_data, MAX86915_MODE_CONFIGURATION, 0x00);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_MODE_CONFIGURATION!\n",
__func__);
return -EIO;
}
if (fifo_full_cnt == 8) {
/* 16uA, 800Hz, 70us */
err = max86915_write_reg(max86915_data, MAX86915_MODE_CONFIGURATION_2,
0x50);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_MODE_CONFIGURATION_2!\n",
__func__);
return -EIO;
}
/* 1 Samples avg */
err = max86915_write_reg(max86915_data, MAX86915_FIFO_CONFIG,
0x1f);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_FIFO_CONFIG!\n",
__func__);
return -EIO;
}
} else if (fifo_full_cnt == 4) {
/* 16uA, 400Hz, 120us */
err = max86915_write_reg(max86915_data, MAX86915_MODE_CONFIGURATION_2,
0x50);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_MODE_CONFIGURATION_2!\n",
__func__);
return -EIO;
}
/* 2 Samples avg */
err = max86915_write_reg(max86915_data, MAX86915_FIFO_CONFIG,
0x3f);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_FIFO_CONFIG!\n",
__func__);
return -EIO;
}
} else if (fifo_full_cnt == 2) {
/* 16uA, 200Hz, 120us */
err = max86915_write_reg(max86915_data, MAX86915_MODE_CONFIGURATION_2,
0x50);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_MODE_CONFIGURATION_2!\n",
__func__);
return -EIO;
}
/* 4 Samples avg */
err = max86915_write_reg(max86915_data, MAX86915_FIFO_CONFIG,
0x5f);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_FIFO_CONFIG!\n",
__func__);
return -EIO;
}
} else if (fifo_full_cnt == 1) {
/* 32uA, 400Hz, 120us */
err = max86915_write_reg(max86915_data, MAX86915_MODE_CONFIGURATION_2,
0x6D);
/* 4 Samples avg */
err = max86915_write_reg(max86915_data, MAX86915_FIFO_CONFIG,
0x5f);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_FIFO_CONFIG!\n",
__func__);
return -EIO;
}
}
/* SDK mode only use FLEX MODE 0x03 After changing FIFO CONFIG skip initial sample. */
err = max86915_read_reg(max86915_data, MAX86915_FIFO_CONFIG, &recvData, 1);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_FIFO_CONFIG!\n",
__func__);
return -EIO;
}
max86915_data->agc_led_set = AGC_LED_SKIP_CNT >>
((recvData & MAX86915_SMP_AVE_MASK) >> MAX86915_SMP_AVE_OFFSET);
err = max86915_write_reg(max86915_data, MAX86915_MODE_CONFIGURATION, 0x03);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_MODE_CONFIGURATION_2!\n",
__func__);
return -EIO;
}
max86915_data->sample_cnt = 0;
err = sampling_period_ns;
}
return err;
}
static int max86915_get_chipid(u64 *chip_id)
{
u8 recvData;
int err;
u32 c1_code = 0;
u32 c2_code = 0;
u32 c3_code = 0;
u32 c4_code = 0;
*chip_id = 0;
err = max86915_write_reg(max86915_data, 0xFF, 0x54);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_MODE_TEST0!\n",
__func__);
return -EIO;
}
err = max86915_write_reg(max86915_data, 0xFF, 0x4d);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_MODE_TEST1!\n",
__func__);
return -EIO;
}
err = max86915_read_reg(max86915_data, 0xC1, &recvData, 1);
if (err != 0) {
HRM_err("%s - max86915_read_reg err:%d, address:0x%02x\n",
__func__, err, recvData);
return -EIO;
}
c1_code = recvData;
err = max86915_read_reg(max86915_data, 0xC2, &recvData, 1);
if (err != 0) {
HRM_err("%s - max86915_read_reg err:%d, address:0x%02x\n",
__func__, err, recvData);
return -EIO;
}
c2_code = recvData;
err = max86915_read_reg(max86915_data, 0xC3, &recvData, 1);
if (err != 0) {
HRM_err("%s - max86915_read_reg err:%d, address:0x%02x\n",
__func__, err, recvData);
return -EIO;
}
c3_code = recvData;
err = max86915_read_reg(max86915_data, 0xC4, &recvData, 1);
if (err != 0) {
HRM_err("%s - max86915_read_reg err:%d, address:0x%02x\n",
__func__, err, recvData);
return -EIO;
}
c4_code = recvData;
err = max86915_write_reg(max86915_data, 0xFF, 0x00);
if (err != 0) {
HRM_err("%s - error initializing MAX86900_MODE_TEST0!\n",
__func__);
return -EIO;
}
*chip_id = (((u64)c1_code) << 24) + (((u64)c2_code) << 16)
+ (((u64)c3_code) << 8) + (c4_code);
HRM_info("%s - Device ID = %lld\n", __func__, *chip_id);
return 0;
}
static int max86915_get_part_id(struct max86915_device_data *data)
{
u8 recvData;
u8 recvDataSub;
int err;
err = max86915_write_reg(data, 0xFF, 0x54);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_MODE_TEST0!\n",
__func__);
return -EIO;
}
err = max86915_write_reg(data, 0xFF, 0x4d);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_MODE_TEST1!\n",
__func__);
return -EIO;
}
err = max86915_read_reg(data, 0xC1, &recvData, 1);
if (err != 0) {
HRM_err("%s - max86915_read_reg err:%d, address:0x%02x\n",
__func__, err, recvData);
return -EIO;
}
err = max86915_write_reg(data, 0xFF, 0x00);
if (err != 0) {
HRM_err("%s - error initializing MAX86900_MODE_TEST0!\n",
__func__);
return -EIO;
}
HRM_dbg("%s - 0xC1 :%x\n", __func__, recvData);
if (recvData == 0x83)
return PART_TYPE_MAX86915_ES;
else if (recvData == 0x86)
return PART_TYPE_MAX86915_CS_15;
else if (recvData == 0x88) {
err = max86915_read_reg(data, 0xFE, &recvDataSub, 1);
if (err != 0) {
HRM_err("%s - max86915_read_reg err:%d, address:0x%02x\n",
__func__, err, recvDataSub);
return -EIO;
}
HRM_dbg("%s - 0xFE :0x%02x\n", __func__, recvDataSub);
if (recvDataSub == 0x41)
return PART_TYPE_MAX86915_CS_21;
else if (recvDataSub == 0xC1)
return PART_TYPE_MAX86915_CS_22;
else if (recvDataSub == 0x42)
return PART_TYPE_MAX86915_CS_211;
else if (recvDataSub == 0xC2)
return PART_TYPE_MAX86915_CS_221;
return PART_TYPE_NONE;
}
err = max86915_read_reg(data, 0xFE, &recvDataSub, 1);
if (err != 0) {
HRM_err("%s - max86915_read_reg err:%d, address:0x%02x\n",
__func__, err, recvDataSub);
return -EIO;
}
HRM_dbg("%s - 0xFE :0x%02x\n", __func__, recvDataSub);
if (recvDataSub == 0x41)
return PART_TYPE_MAX86915_CS_21;
else if (recvDataSub == 0xC1)
return PART_TYPE_MAX86915_CS_22;
else if (recvDataSub == 0x42) {
if ((recvData & 0x0F) == 0x0B)
return PART_TYPE_MAX86917_1;
else
return PART_TYPE_MAX86915_CS_211;
} else if (recvDataSub == 0xC2) {
if ((recvData & 0x0F) == 0x0B)
return PART_TYPE_MAX86917_2;
else
return PART_TYPE_MAX86915_CS_221;
}
return PART_TYPE_NONE;
}
static int max86915_get_part_type(u16 *part_type)
{
int err;
u8 buffer[2] = {0, };
err = max86915_read_reg(max86915_data, MAX86915_REV_ID_REG, buffer, 2);
if (err) {
HRM_err("%s - Max86915 WHOAMI read fail0\n", __func__);
err = -ENODEV;
goto err_of_read_part_type;
}
if (buffer[1] == MAX86915_PART_ID) {
max86915_data->part_type = max86915_get_part_id(max86915_data);
} else {
HRM_err("%s - Max86915 WHOAMI read fail2\n", __func__);
err = -ENODEV;
goto err_of_read_part_type;
}
*part_type = max86915_data->part_type;
return err;
err_of_read_part_type:
return -EINVAL;
}
static int max86915_get_led_test(u8 *result)
{
int err = 0;
u8 recvData = 0;
int i = 0;
int retry = 0;
*result = 0;
err = max86915_write_reg(max86915_data, MAX86915_MODE_CONFIGURATION,
0x1 << MAX86915_RESET_OFFSET);
if (err != 0) {
HRM_err("%s - error writing MAX86915_MODE_CONFIGURATION err:%d\n",
__func__, err);
return -EIO;
}
usleep_range(100000, 110000);
err = max86915_write_reg(max86915_data, MAX86915_LED1_PA, 0xFF);
if (err != 0) {
HRM_err("%s - error writing MAX86915_LED1_PA err:%d\n",
__func__, err);
return -EIO;
}
err = max86915_write_reg(max86915_data, MAX86915_LED2_PA, 0xFF);
if (err != 0) {
HRM_err("%s - error writing MAX86915_LED2_PA err:%d\n",
__func__, err);
return -EIO;
}
err = max86915_write_reg(max86915_data, MAX86915_LED3_PA, 0x08);
if (err != 0) {
HRM_err("%s - error writing MAX86915_LED3_PA err:%d\n",
__func__, err);
return -EIO;
}
err = max86915_write_reg(max86915_data, MAX86915_LED4_PA, 0x08);
if (err != 0) {
HRM_err("%s - error writing MAX86915_LED4_PA err:%d\n",
__func__, err);
return -EIO;
}
err = max86915_write_reg(max86915_data, MAX86915_MODE_CONFIGURATION, 0x01);
if (err != 0) {
HRM_err("%s - error writing MAX86915_MODE_CONFIGURATION err:%d\n",
__func__, err);
return -EIO;
}
usleep_range(1200, 1210);
for (retry = 0 ; retry < 3; retry++) {
*result = 0;
for (i = 0; i < MAX_LED_NUM; i++) {
err = max86915_write_reg(max86915_data, 0x31, 0x10 << i);
if (err != 0) {
HRM_err("%s - error writing MAX86915_LED_TEST_EN%d err:%d\n",
__func__, i, err);
return -EIO;
}
usleep_range(100, 110);
err = max86915_write_reg(max86915_data, 0x31, (0x10 << i) | 0x01);
if (err != 0) {
HRM_err("%s - error writing MAX86915_LED_TEST_EN%d err:%d\n",
__func__, i, err);
return -EIO;
}
usleep_range(1000, 1100);
err = max86915_read_reg(max86915_data, 0x32, &recvData, 1);
if (err != 0) {
HRM_err("%s - error reading MAX86915_COMP_OUT%d err:%d\n",
__func__, i, err);
return -EIO;
}
*result |= (recvData & (1 << i));
}
if (*result == 0)
break;
HRM_err("%s - fail retry %d = 0x%02x\n", __func__, retry, *result);
}
err = max86915_write_reg(max86915_data, MAX86915_MODE_CONFIGURATION,
0x1 << MAX86915_SHDN_OFFSET);
if (err != 0) {
HRM_err("%s - error writing MAX86915_MODE_CONFIGURATION err:%d\n",
__func__, err);
return -EIO;
}
return 0;
}
static int max86915_set_current(u8 d1, u8 d2, u8 d3, u8 d4)
{
int err = 0;
u8 recvData;
max86915_data->led_current1 = d1; /* set ir; */
max86915_data->led_current2 = d2; /* set red; */
max86915_data->led_current3 = d3; /* set ir; */
max86915_data->led_current4 = d4; /* set red; */
HRM_info("%s - 0x%x 0x%x 0x%x 0x%x\n", __func__,
max86915_data->led_current1, max86915_data->led_current2,
max86915_data->led_current3, max86915_data->led_current4);
if (max86915_data->part_type < PART_TYPE_MAX86915_CS_211) {
err = max86915_write_reg(max86915_data, MAX86915_MODE_CONFIGURATION, 0x00);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_MODE_CONFIGURATION!\n",
__func__);
return -EIO;
}
}
err = max86915_write_reg(max86915_data, MAX86915_LED1_PA,
max86915_data->led_current1);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_LED1_PA!\n",
__func__);
return -EIO;
}
err = max86915_write_reg(max86915_data, MAX86915_LED2_PA,
max86915_data->led_current2);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_LED2_PA!\n",
__func__);
return -EIO;
}
err = max86915_write_reg(max86915_data, MAX86915_LED3_PA,
max86915_data->led_current3);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_LED3_PA!\n",
__func__);
return -EIO;
}
err = max86915_write_reg(max86915_data, MAX86915_LED4_PA,
max86915_data->led_current4);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_LED4_PA!\n",
__func__);
return -EIO;
}
if (max86915_data->part_type < PART_TYPE_MAX86915_CS_211) {
err = max86915_read_reg(max86915_data, MAX86915_FIFO_CONFIG, &recvData, 1);
max86915_data->agc_led_set = AGC_LED_SKIP_CNT >>
((recvData & MAX86915_SMP_AVE_MASK) >> MAX86915_SMP_AVE_OFFSET);
err = max86915_write_reg(max86915_data, MAX86915_MODE_CONFIGURATION, 0x03);
}
return err;
}
static int max86915_set_xtalk(u8 d1, u8 d2, u8 d3, u8 d4)
{
int err = 0;
max86915_data->xtalk_code1 = d1; /* set ir; */
max86915_data->xtalk_code2 = d2; /* set red; */
max86915_data->xtalk_code3 = d3; /* set ir; */
max86915_data->xtalk_code4 = d4; /* set red; */
HRM_info("%s - 0x%x 0x%x 0x%x 0x%x\n", __func__,
max86915_data->xtalk_code1, max86915_data->xtalk_code2,
max86915_data->xtalk_code3, max86915_data->xtalk_code4);
if (max86915_data->led_current1 == MAX86915_MIN_CURRENT)
max86915_data->default_current1 = max86915_data->init_current[AGC_IR]
+ MAX86915_IR_CURRENT_STEP * max86915_data->xtalk_code1;
if (max86915_data->led_current2 == MAX86915_MIN_CURRENT)
max86915_data->default_current2 = max86915_data->init_current[AGC_RED]
+ MAX86915_RED_CURRENT_STEP * max86915_data->xtalk_code2;
if (max86915_data->led_current3 == MAX86915_MIN_CURRENT)
max86915_data->default_current3 = max86915_data->init_current[AGC_GREEN]
+ MAX86915_GREEN_CURRENT_STEP * max86915_data->xtalk_code3;
if (max86915_data->led_current4 == MAX86915_MIN_CURRENT)
max86915_data->default_current4 = max86915_data->init_current[AGC_BLUE]
+ MAX86915_BLUE_CURRENT_STEP * max86915_data->xtalk_code4;
HRM_info("%s - current 0x%x 0x%x 0x%x 0x%x\n", __func__,
max86915_data->default_current1, max86915_data->default_current2,
max86915_data->default_current3, max86915_data->default_current4);
if (max86915_data->enabled_mode) {
err = max86915_write_reg(max86915_data, MAX86915_DAC1_XTALK_CODE,
max86915_data->xtalk_code1);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_DAC1_XTALK_CODE!\n",
__func__);
return -EIO;
}
err = max86915_write_reg(max86915_data, MAX86915_DAC2_XTALK_CODE,
max86915_data->xtalk_code2);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_DAC2_XTALK_CODE!\n",
__func__);
return -EIO;
}
err = max86915_write_reg(max86915_data, MAX86915_DAC3_XTALK_CODE,
max86915_data->xtalk_code3);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_DAC3_XTALK_CODE!\n",
__func__);
return -EIO;
}
err = max86915_write_reg(max86915_data, MAX86915_DAC4_XTALK_CODE,
max86915_data->xtalk_code4);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_DAC4_XTALK_CODE!\n",
__func__);
return -EIO;
}
}
return err;
}
static int max86915_update_led_current(struct max86915_device_data *data,
int new_led_uA,
int led_num)
{
int err;
u8 led_reg_val;
int led_reg_addr;
u8 recvData;
if (new_led_uA > MAX86915_MAX_CURRENT(data->led_range[led_num])) {
HRM_dbg("%s - Tried to set LED%d to %duA. Max is %duA\n",
__func__, led_num, new_led_uA,
MAX86915_MAX_CURRENT(data->led_range[led_num]));
new_led_uA = MAX86915_MAX_CURRENT(data->led_range[led_num]);
} else if (new_led_uA < MAX86915_MIN_CURRENT) {
HRM_dbg("%s - Tried to set LED%d to %duA. Min is %duA\n",
__func__, led_num, new_led_uA, MAX86915_MIN_CURRENT);
new_led_uA = MAX86915_MIN_CURRENT;
}
led_reg_val = new_led_uA / MAX86915_CURRENT_PER_STEP(data->led_range[led_num]);
led_reg_addr = MAX86915_LED1_PA + led_num;
HRM_dbg("%s - Setting ADD 0x%x, LED%d to 0x%.2X (%duA)\n",
__func__, led_reg_addr, led_num, led_reg_val, new_led_uA);
if (data->part_type < PART_TYPE_MAX86915_CS_211) {
err = max86915_write_reg(data, MAX86915_MODE_CONFIGURATION, 0x00);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_MODE_CONFIGURATION!\n", __func__);
return -EIO;
}
}
err = max86915_write_reg(data, led_reg_addr, led_reg_val);
if (data->part_type < PART_TYPE_MAX86915_CS_211) {
err = max86915_read_reg(data, MAX86915_FIFO_CONFIG, &recvData, 1);
data->agc_led_set = AGC_LED_SKIP_CNT >> ((recvData &
MAX86915_SMP_AVE_MASK) >> MAX86915_SMP_AVE_OFFSET);
err = max86915_write_reg(data, MAX86915_MODE_CONFIGURATION, 0x03);
}
if (err != 0) {
HRM_err("%s - error initializing register 0x%.2X!\n",
__func__, led_reg_addr);
return -EIO;
}
switch (led_num) {
case AGC_IR:
data->led_current1 = led_reg_val; /* set ir */
break;
case AGC_RED:
data->led_current2 = led_reg_val; /* set red */
break;
case AGC_GREEN:
data->led_current3 = led_reg_val; /* set green */
break;
case AGC_BLUE:
data->led_current4 = led_reg_val; /* set blue */
break;
}
return 0;
}
static int max86915_init_status(struct max86915_device_data *data)
{
int err = 0;
u8 recvData;
data->agc_enabled = 0;
err = max86915_write_reg(data, MAX86915_MODE_CONFIGURATION, MAX86915_RESET_MASK);
if (err != 0) {
HRM_err("%s - error sw shutdown device!\n", __func__);
return -EIO;
}
/* Interrupt Clear */
err = max86915_read_reg(data, MAX86915_INTERRUPT_STATUS, &recvData, 1);
if (err != 0) {
HRM_err("%s - max86915_read_reg err:%d, address:0x%02x\n",
__func__, err, recvData);
return -EIO;
}
/* Interrupt2 Clear */
err = max86915_read_reg(data, MAX86915_INTERRUPT_STATUS_2, &recvData, 1);
if (err != 0) {
HRM_err("%s - max86915_read_reg err:%d, address:0x%02x\n",
__func__, err, recvData);
return -EIO;
}
HRM_info("%s - init done, part_type = %u\n", __func__, data->part_type);
return err;
}
static void max86915_div_str(char *left_integer, char *left_decimal, char *right_integer,
char *right_decimal, char *result_integer, char *result_decimal)
{
int i;
unsigned long long j;
unsigned long long loperand, roperand;
unsigned long long ldigit, rdigit, temp;
char str[10] = { 0, };
ldigit = 0;
rdigit = 0;
sscanf(left_integer, "%19llu", &loperand);
for (i = DECIMAL_LEN - 2; i >= 0; i--) {
if (left_decimal[i] != '0') {
ldigit = (unsigned long long)i + 1;
break;
}
}
sscanf(right_integer, "%19llu", &roperand);
for (i = DECIMAL_LEN - 2; i >= 0; i--) {
if (right_decimal[i] != '0') {
rdigit = (unsigned long long)i + 1;
break;
}
}
if (ldigit < rdigit)
ldigit = rdigit;
for (j = 0; j < ldigit; j++) {
loperand *= 10;
roperand *= 10;
}
if (ldigit != 0) {
snprintf(str, sizeof(str), "%%%llullu", ldigit);
sscanf(left_decimal, str, &temp);
loperand += temp;
snprintf(str, sizeof(str), "%%%llullu", ldigit);
sscanf(right_decimal, str, &temp);
roperand += temp;
}
if (roperand == 0)
roperand = 1;
temp = loperand / roperand;
snprintf(result_integer, INTEGER_LEN, "%llu", temp);
loperand -= roperand * temp;
loperand *= 10;
for (i = 0; i < DECIMAL_LEN; i++) {
temp = loperand / roperand;
if (temp != 0) {
loperand -= roperand * temp;
loperand *= 10;
} else
loperand *= 10;
result_decimal[i] = '0' + temp;
}
result_decimal[DECIMAL_LEN - 1] = 0;
}
static void max86915_div_float(struct max86915_div_data *div_data, u64 left_operand, u64 right_operand)
{
snprintf(div_data->left_integer, INTEGER_LEN, "%llu", left_operand);
memset(div_data->left_decimal, '0', sizeof(div_data->left_decimal));
div_data->left_decimal[DECIMAL_LEN - 1] = 0;
snprintf(div_data->right_integer, INTEGER_LEN, "%llu", right_operand);
memset(div_data->right_decimal, '0', sizeof(div_data->right_decimal));
div_data->right_decimal[DECIMAL_LEN - 1] = 0;
max86915_div_str(div_data->left_integer, div_data->left_decimal, div_data->right_integer,
div_data->right_decimal, div_data->result_integer, div_data->result_decimal);
}
static void max86915_plus_str(char *integer_operand,
char *decimal_operand, char *result_integer, char *result_decimal)
{
unsigned long long a, b;
int i;
for (i = 0; i < DECIMAL_LEN - 1; i++) {
decimal_operand[i] -= '0';
result_decimal[i] -= '0';
}
for (i = 0; i < DECIMAL_LEN - 1; i++)
result_decimal[i] += decimal_operand[i];
sscanf(integer_operand, "%19llu", &a);
sscanf(result_integer, "%19llu", &b);
for (i = DECIMAL_LEN - 1; i >= 0; i--) {
if (result_decimal[i] >= 10) {
if (i > 0)
result_decimal[i - 1] += 1;
else
a += 1;
result_decimal[i] -= 10;
}
}
a += b;
for (i = 0; i < DECIMAL_LEN - 1; i++)
result_decimal[i] += '0';
snprintf(result_integer, INTEGER_LEN, "%llu", a);
}
static void max86915_float_sqrt(char *integer_operand, char *decimal_operand)
{
int i;
char left_integer[INTEGER_LEN] = { '\0', }, left_decimal[DECIMAL_LEN] = { '\0', };
char result_integer[INTEGER_LEN] = { '\0', }, result_decimal[DECIMAL_LEN] = { '\0', };
char temp_integer[INTEGER_LEN] = { '\0', }, temp_decimal[DECIMAL_LEN] = { '\0', };
strncpy(left_integer, integer_operand, INTEGER_LEN - 1);
strncpy(left_decimal, decimal_operand, DECIMAL_LEN - 1);
for (i = 0; i < 100; i++) {
max86915_div_str(left_integer, left_decimal, integer_operand,
decimal_operand, result_integer, result_decimal);
max86915_plus_str(integer_operand, decimal_operand, result_integer, result_decimal);
snprintf(temp_integer, INTEGER_LEN, "%d", 2);
memset(temp_decimal, '0', sizeof(temp_decimal));
temp_decimal[DECIMAL_LEN - 1] = 0;
max86915_div_str(result_integer, result_decimal, temp_integer,
temp_decimal, integer_operand, decimal_operand);
}
}
#ifdef MAXIM_AGC
static int agc_adj_calculator(struct max86915_device_data *data,
s32 *change_by_percent_of_range,
s32 *change_by_percent_of_current_setting,
s32 *change_led_by_absolute_count,
s32 *set_led_to_absolute_count,
s32 target_percent_of_range,
s32 correction_coefficient,
s32 allowed_error_in_percentage,
s32 *reached_thresh,
s32 current_average,
s32 number_of_samples_averaged,
s32 led_drive_current_value)
{
s32 diode_min_val;
s32 diode_max_val;
s32 diode_min_current;
s32 diode_fs_current;
s32 current_percent_of_range = 0;
s32 delta = 0;
s32 desired_delta = 0;
s32 current_power_percent = 0;
if (change_by_percent_of_range == 0
|| change_by_percent_of_current_setting == 0
|| change_led_by_absolute_count == 0
|| set_led_to_absolute_count == 0)
return ILLEGAL_OUTPUT_POINTER;
if (target_percent_of_range > 90 || target_percent_of_range < 10)
return CONSTRAINT_VIOLATION;
if (correction_coefficient > 100 || correction_coefficient < 0)
return CONSTRAINT_VIOLATION;
if (allowed_error_in_percentage > 100
|| allowed_error_in_percentage < 0)
return CONSTRAINT_VIOLATION;
#if ((MAX86915_MAX_DIODE_VAL-MAX86915_MIN_DIODE_VAL) <= 0 \
|| (MAX86915_MAX_DIODE_VAL < 0) || (MAX86915_MIN_DIODE_VAL < 0))
#error "Illegal max86915 diode Min/Max Pair"
#endif
#if ((MAX86915_CURRENT_FULL_SCALE) <= 0 \
|| (MAX86915_MAX_CURRENT < 0) || (MAX86915_MIN_CURRENT < 0))
#error "Illegal max86915 LED Min/Max current Pair"
#endif
diode_min_val = MAX86915_MIN_DIODE_VAL;
diode_max_val = MAX86915_MAX_DIODE_VAL;
diode_min_current = MAX86915_MIN_CURRENT;
diode_fs_current = MAX86915_CURRENT_FULL_SCALE;
if (led_drive_current_value > MAX86915_MAX_CURRENT
|| led_drive_current_value < MAX86915_MIN_CURRENT)
return CONSTRAINT_VIOLATION;
if (current_average < MAX86915_MIN_DIODE_VAL
|| current_average > MAX86915_MAX_DIODE_VAL)
return CONSTRAINT_VIOLATION;
current_percent_of_range = 100 *
(current_average - diode_min_val) /
(diode_max_val - diode_min_val);
delta = current_percent_of_range - target_percent_of_range;
delta = delta * correction_coefficient / 100;
if (!(*reached_thresh))
allowed_error_in_percentage =
allowed_error_in_percentage * 2 / 7;
if (delta > -allowed_error_in_percentage
&& delta < allowed_error_in_percentage) {
*change_by_percent_of_range = 0;
*change_by_percent_of_current_setting = 0;
*change_led_by_absolute_count = 0;
*set_led_to_absolute_count = led_drive_current_value;
*reached_thresh = 1;
return 0;
}
current_power_percent = 100 *
(led_drive_current_value - diode_min_current) /
diode_fs_current;
if (delta < 0) {
if (current_power_percent > 97
&& (current_percent_of_range
< (data->threshold_default * 100) / (diode_max_val - diode_min_val))) {
desired_delta = -delta * (102 - current_power_percent) /
(100 - current_percent_of_range);
} else
desired_delta = -delta * (100 - current_power_percent) /
(100 - current_percent_of_range);
}
if (delta > 0)
desired_delta = -delta * (current_power_percent)
/ (current_percent_of_range);
*change_by_percent_of_range = desired_delta;
*change_led_by_absolute_count = (desired_delta
* diode_fs_current / 100);
*change_by_percent_of_current_setting =
(*change_led_by_absolute_count * 100)
/ (led_drive_current_value);
*set_led_to_absolute_count = led_drive_current_value
+ *change_led_by_absolute_count;
if (*set_led_to_absolute_count > MAX86915_MAX_CURRENT)
*set_led_to_absolute_count = MAX86915_MAX_CURRENT;
return 0;
}
#else
static int linear_search_agc(struct max86915_device_data *data, int ppg_average, int led_num)
{
int xtalk_adc = 0;
int target_adc = MAX86915_MAX_DIODE_VAL * data->agc_led_out_percent / 100;
long long target_current;
int agc_range = MAX86915_AGC_ERROR_RANGE1;
if (ppg_average < MAX86915_MIN_DIODE_VAL
|| ppg_average > MAX86915_MAX_DIODE_VAL)
return CONSTRAINT_VIOLATION;
HRM_info("%s - (%d) ppg_average : %d\n", __func__, led_num, ppg_average);
if (ppg_average == MAX86915_MAX_DIODE_VAL) {
if (data->prev_agc_current[led_num] >= data->agc_current[led_num])
target_current = data->agc_current[led_num] / 2;
else
target_current = (data->agc_current[led_num] + data->prev_agc_current[led_num]) / 2;
if (target_current > MAX86915_MAX_CURRENT(data->led_range[led_num]))
target_current = MAX86915_MAX_CURRENT(data->led_range[led_num]);
else if (target_current < MAX86915_MIN_CURRENT)
target_current = MAX86915_MIN_CURRENT;
data->prev_agc_current[led_num] = data->agc_current[led_num];
data->change_led_by_absolute_count[led_num] = target_current - data->agc_current[led_num];
data->agc_current[led_num] = target_current;
return 0;
} else if (ppg_average == MAX86915_MIN_DIODE_VAL) {
if (data->prev_agc_current[led_num] < data->agc_current[led_num])
data->prev_agc_current[led_num] = MAX86915_MAX_CURRENT(data->led_range[led_num]);
target_current = (data->agc_current[led_num]
+ data->prev_agc_current[led_num]
+ MAX86915_CURRENT_PER_STEP(data->led_range[led_num])) / 2;
if (target_current > MAX86915_MAX_CURRENT(data->led_range[led_num]))
target_current = MAX86915_MAX_CURRENT(data->led_range[led_num]);
else if (target_current < MAX86915_MIN_CURRENT)
target_current = MAX86915_MIN_CURRENT;
data->prev_agc_current[led_num] = data->agc_current[led_num];
data->change_led_by_absolute_count[led_num] = target_current - data->agc_current[led_num];
data->agc_current[led_num] = target_current;
return 0;
}
if (data->reached_thresh[led_num]) {
agc_range = MAX86915_AGC_ERROR_RANGE2;
if (ppg_average <= (data->prev_agc_average[led_num] * 4 / 10)) {
data->remove_cnt[led_num]++;
if (data->remove_cnt[led_num] > 10) {
data->remove_cnt[led_num] = 0;
data->reached_thresh[led_num] = 0;
}
} else
data->remove_cnt[led_num] = 0;
}
HRM_info("%s, (%d) target : %d, ppg_average : %d prev_average : %d reach_thd : %d\n",
__func__, led_num, target_adc, ppg_average,
data->prev_agc_average[led_num], data->reached_thresh[led_num]);
if (ppg_average < MAX86915_MAX_DIODE_VAL * (data->agc_led_out_percent + agc_range) / 100
&& ppg_average > MAX86915_MAX_DIODE_VAL * (data->agc_led_out_percent - agc_range) / 100) {
data->reached_thresh[led_num] = 1;
data->change_led_by_absolute_count[led_num] = 0;
data->prev_agc_average[led_num] = ppg_average;
return 0;
}
switch (led_num) {
case AGC_IR:
xtalk_adc = MAX86915_MAX_DIODE_VAL / 2 * data->xtalk_code1 / 31;
target_adc += xtalk_adc;
break;
case AGC_RED:
xtalk_adc = MAX86915_MAX_DIODE_VAL / 2 * data->xtalk_code2 / 31;
target_adc += xtalk_adc;
break;
case AGC_GREEN:
xtalk_adc = MAX86915_MAX_DIODE_VAL / 2 * data->xtalk_code3 / 31;
target_adc += xtalk_adc;
break;
case AGC_BLUE:
xtalk_adc = MAX86915_MAX_DIODE_VAL / 2 * data->xtalk_code4 / 31;
target_adc += xtalk_adc;
break;
default:
return -EIO;
}
HRM_info("%s - (%d) target2 : %d\n", __func__, led_num, target_adc);
target_current = target_adc - (ppg_average + xtalk_adc);
if (data->agc_current[led_num] > data->prev_current[led_num])
target_current *= (data->agc_current[led_num] - data->prev_current[led_num]);
else if (data->agc_current[led_num] < data->prev_current[led_num])
target_current *= (data->prev_current[led_num] - data->agc_current[led_num]);
if (ppg_average + xtalk_adc > data->prev_ppg[led_num])
target_current /= (ppg_average + xtalk_adc - data->prev_ppg[led_num]);
else if (ppg_average + xtalk_adc < data->prev_ppg[led_num])
target_current /= (data->prev_ppg[led_num] - (ppg_average + xtalk_adc));
target_current += data->agc_current[led_num];
if (target_current > MAX86915_MAX_CURRENT(data->led_range[led_num]))
target_current = MAX86915_MAX_CURRENT(data->led_range[led_num]);
else if (target_current < MAX86915_MIN_CURRENT)
target_current = MAX86915_MIN_CURRENT;
data->change_led_by_absolute_count[led_num] = target_current - data->agc_current[led_num];
data->prev_current[led_num] = data->agc_current[led_num];
data->agc_current[led_num] = target_current;
data->prev_ppg[led_num] = ppg_average + xtalk_adc;
HRM_info("%s - data->agc_current[%d] : %u, %lld\n",
__func__, led_num, data->agc_current[led_num], target_current);
return 0;
}
#endif
static int max86915_reset_sdk_agc_var(int channel)
{
HRM_dbg("%s - channel %d reset\n", __func__, channel);
/* LED PA to 0 */
switch (channel) {
case 0:
max86915_data->led_current1 = 0x0;
max86915_data->reached_thresh[AGC_IR] = 0;
max86915_data->agc_sum[AGC_IR] = 0;
max86915_data->agc_sample_cnt[AGC_IR] = 0;
max86915_data->agc_current[AGC_IR] = (max86915_data->led_current1
* MAX86915_CURRENT_PER_STEP(max86915_data->led_range[channel]));
break;
case 1:
max86915_data->led_current2 = 0x0;
max86915_data->reached_thresh[AGC_RED] = 0;
max86915_data->agc_sum[AGC_RED] = 0;
max86915_data->agc_sample_cnt[AGC_RED] = 0;
max86915_data->agc_current[AGC_RED] = (max86915_data->led_current2
* MAX86915_CURRENT_PER_STEP(max86915_data->led_range[channel]));
break;
case 2:
max86915_data->led_current3 = 0x0;
max86915_data->reached_thresh[AGC_GREEN] = 0;
max86915_data->agc_sum[AGC_GREEN] = 0;
max86915_data->agc_sample_cnt[AGC_GREEN] = 0;
max86915_data->agc_current[AGC_GREEN] = (max86915_data->led_current3
* MAX86915_CURRENT_PER_STEP(max86915_data->led_range[channel]));
break;
case 3:
max86915_data->led_current4 = 0x0;
max86915_data->reached_thresh[AGC_BLUE] = 0;
max86915_data->agc_sum[AGC_BLUE] = 0;
max86915_data->agc_sample_cnt[AGC_BLUE] = 0;
max86915_data->agc_current[AGC_BLUE] = (max86915_data->led_current4
* MAX86915_CURRENT_PER_STEP(max86915_data->led_range[channel]));
break;
}
max86915_set_current(max86915_data->led_current1,
max86915_data->led_current2,
max86915_data->led_current3,
max86915_data->led_current4);
return 0;
}
static int max86915_hrm_agc(struct max86915_device_data *data, int counts, int led_num)
{
int err = 0;
int avg = 0;
/* HRM_info("%s - led_num(%d) = %d, %d\n", __func__, led_num, counts, data->agc_sample_cnt[led_num]); */
if (led_num < AGC_IR || led_num > AGC_BLUE)
return -EIO;
data->agc_sum[led_num] += counts;
data->agc_sample_cnt[led_num]++;
if (data->agc_sample_cnt[led_num] < data->agc_min_num_samples)
return 0;
data->agc_sample_cnt[led_num] = 0;
avg = data->agc_sum[led_num] / data->agc_min_num_samples;
data->agc_sum[led_num] = 0;
/* HRM_info("%s - led_num(%d) = %d, %d\n", __func__, led_num, counts, avg); */
#ifdef MAXIM_AGC
err = agc_adj_calculator(data,
&data->change_by_percent_of_range[led_num],
&data->change_by_percent_of_current_setting[led_num],
&data->change_led_by_absolute_count[led_num],
&data->agc_current[led_num],
data->agc_led_out_percent,
data->agc_corr_coeff,
data->agc_sensitivity_percent,
&data->reached_thresh[led_num],
avg,
data->agc_min_num_samples,
data->agc_current[led_num]);
#else
err = linear_search_agc(data, avg, led_num);
#endif
if (err)
return err;
/* HRM_info("%s - %d-a:%d\n", __func__, led_num, data->change_led_by_absolute_count[led_num]); */
if (data->change_led_by_absolute_count[led_num] == 0) {
if (led_num == AGC_IR) {
data->ir_adc = counts;
data->ir_curr = data->led_current1;
} else if (led_num == AGC_RED) {
data->red_adc = counts;
data->red_curr = data->led_current2;
} else if (led_num == AGC_GREEN) {
data->green_adc = counts;
data->green_curr = data->led_current3;
} else if (led_num == AGC_BLUE) {
data->blue_adc = counts;
data->blue_curr = data->led_current4;
}
/*
* HRM_info("%s - ir_curr = 0x%2x, red_curr = 0x%2x, ir_adc = %d, red_adc = %d\n",
* __func__, data->ir_curr, data->red_curr, data->ir_adc, data->red_adc);
*/
return 0;
}
err = data->update_led(data, data->agc_current[led_num], led_num);
/* skip 2 sample after changing current */
max86915_data->agc_enabled = 0;
max86915_data->sample_cnt = AGC_SKIP_CNT - 2;
if (err)
HRM_err("%s - failed\n", __func__);
return err;
}
static int max86915_cal_agc(int ir, int red, int green, int blue)
{
int err = 0;
int led_num0, led_num1, led_num2, led_num3;
led_num0 = AGC_IR;
led_num1 = AGC_RED;
led_num2 = AGC_GREEN;
led_num3 = AGC_BLUE;
if (max86915_data->agc_mode == M_HRM) {
if (ir >= max86915_data->threshold_default) { /* Detect */
err |= max86915_hrm_agc(max86915_data, ir, led_num0); /* IR */
if (max86915_data->agc_current[led_num1] != MAX86915_MIN_CURRENT) {
err |= max86915_hrm_agc(max86915_data, red, led_num1); /* RED */
} else {
/* init */
max86915_data->agc_current[led_num1]
= max86915_data->init_current[led_num1]
* MAX86915_CURRENT_PER_STEP(max86915_data->led_range[led_num1]);
max86915_data->agc_enabled = 0;
max86915_data->sample_cnt = 0;
max86915_data->reached_thresh[led_num1] = 0;
max86915_data->prev_current[led_num1] = 0;
max86915_data->prev_agc_current[led_num1]
= MAX86915_MAX_CURRENT(max86915_data->led_range[led_num1]);
max86915_data->prev_ppg[led_num1] = 0;
max86915_data->agc_sum[led_num1] = 0;
max86915_data->agc_sample_cnt[led_num1] = 0;
max86915_data->update_led(max86915_data,
max86915_data->agc_current[led_num1], led_num1);
}
#ifdef CONFIG_HRM_GREEN_BLUE
if (max86915_data->agc_current[led_num2] != MAX86915_MIN_CURRENT) {
err |= max86915_hrm_agc(max86915_data, green, led_num2); /* GREEN */
} else {
/* init */
max86915_data->agc_current[led_num2]
= max86915_data->init_current[led_num2]
* MAX86915_CURRENT_PER_STEP(max86915_data->led_range[led_num2]);
max86915_data->agc_enabled = 0;
max86915_data->sample_cnt = 0;
max86915_data->reached_thresh[led_num2] = 0;
max86915_data->prev_current[led_num2] = 0;
max86915_data->prev_agc_current[led_num2]
= MAX86915_MAX_CURRENT(max86915_data->led_range[led_num2]);
max86915_data->prev_ppg[led_num2] = 0;
max86915_data->agc_sum[led_num2] = 0;
max86915_data->agc_sample_cnt[led_num2] = 0;
max86915_data->update_led(max86915_data,
max86915_data->agc_current[led_num2], led_num2);
}
if (max86915_data->agc_current[led_num3] != MAX86915_MIN_CURRENT) {
err |= max86915_hrm_agc(max86915_data, blue, led_num3); /* BLUE */
} else {
/* init */
max86915_data->agc_current[led_num3]
= max86915_data->init_current[led_num3]
* MAX86915_CURRENT_PER_STEP(max86915_data->led_range[led_num3]);
max86915_data->agc_enabled = 0;
max86915_data->sample_cnt = 0;
max86915_data->reached_thresh[led_num3] = 0;
max86915_data->prev_current[led_num3] = 0;
max86915_data->prev_agc_current[led_num3]
= MAX86915_MAX_CURRENT(max86915_data->led_range[led_num3]);
max86915_data->prev_ppg[led_num3] = 0;
max86915_data->agc_sum[led_num3] = 0;
max86915_data->agc_sample_cnt[led_num3] = 0;
max86915_data->update_led(max86915_data,
max86915_data->agc_current[led_num3], led_num3);
}
#endif
} else {
if (max86915_data->agc_current[led_num1] != MAX86915_MIN_CURRENT) { /* Disable led_num1 */
/* init */
max86915_data->agc_current[led_num1] = MAX86915_MIN_CURRENT;
max86915_data->reached_thresh[led_num1] = 0;
max86915_data->prev_current[led_num1] = 0;
max86915_data->prev_agc_current[led_num1]
= MAX86915_MAX_CURRENT(max86915_data->led_range[led_num1]);
max86915_data->prev_ppg[led_num1] = 0;
max86915_data->agc_sum[led_num1] = 0;
max86915_data->agc_sample_cnt[led_num1] = 0;
max86915_data->update_led(max86915_data,
max86915_data->agc_current[led_num1], led_num1);
/* init */
max86915_data->agc_current[led_num0]
= max86915_data->init_current[led_num0]
* MAX86915_CURRENT_PER_STEP(max86915_data->led_range[led_num0]);
max86915_data->agc_enabled = 0;
max86915_data->sample_cnt = 0;
max86915_data->reached_thresh[led_num0] = 0;
max86915_data->prev_current[led_num0] = 0;
max86915_data->prev_agc_current[led_num0]
= MAX86915_MAX_CURRENT(max86915_data->led_range[led_num0]);
max86915_data->prev_ppg[led_num0] = 0;
max86915_data->agc_sum[led_num0] = 0;
max86915_data->agc_sample_cnt[led_num0] = 0;
max86915_data->update_led(max86915_data,
max86915_data->agc_current[led_num0], led_num0);
}
#ifdef CONFIG_HRM_GREEN_BLUE
if (max86915_data->agc_current[led_num2] != MAX86915_MIN_CURRENT) { /* Disable led_num2 */
/* init */
max86915_data->agc_current[led_num2] = MAX86915_MIN_CURRENT;
max86915_data->reached_thresh[led_num2] = 0;
max86915_data->prev_current[led_num2] = 0;
max86915_data->prev_agc_current[led_num2]
= MAX86915_MAX_CURRENT(max86915_data->led_range[led_num2]);
max86915_data->prev_ppg[led_num2] = 0;
max86915_data->agc_sum[led_num2] = 0;
max86915_data->agc_sample_cnt[led_num2] = 0;
max86915_data->update_led(max86915_data,
max86915_data->agc_current[led_num2], led_num2);
}
if (max86915_data->agc_current[led_num3] != MAX86915_MIN_CURRENT) { /* Disable led_num3 */
/* init */
max86915_data->agc_current[led_num3] = MAX86915_MIN_CURRENT;
max86915_data->reached_thresh[led_num3] = 0;
max86915_data->prev_current[led_num3] = 0;
max86915_data->prev_agc_current[led_num3]
= MAX86915_MAX_CURRENT(max86915_data->led_range[led_num3]);
max86915_data->prev_ppg[led_num3] = 0;
max86915_data->agc_sum[led_num3] = 0;
max86915_data->agc_sample_cnt[led_num3] = 0;
max86915_data->update_led(max86915_data,
max86915_data->agc_current[led_num3], led_num3);
}
#endif
}
} else if (max86915_data->agc_mode == M_SDK) {
if (max86915_data->mode_sdk_enabled & 0x01) { /* IR channel */
if (max86915_data->agc_current[led_num0] != MAX86915_MIN_CURRENT) {
err |= max86915_hrm_agc(max86915_data, ir, led_num0);
} else {
max86915_data->agc_current[led_num0]
= max86915_data->default_current1
* MAX86915_CURRENT_PER_STEP(max86915_data->led_range[led_num0]);
max86915_data->agc_enabled = 0;
max86915_data->sample_cnt = 0;
max86915_data->reached_thresh[led_num0] = 0;
max86915_data->prev_current[led_num0] = 0;
max86915_data->prev_agc_current[led_num0]
= MAX86915_MAX_CURRENT(max86915_data->led_range[led_num0]);
max86915_data->prev_ppg[led_num0] = 0;
max86915_data->agc_sum[led_num0] = 0;
max86915_data->agc_sample_cnt[led_num0] = 0;
max86915_data->update_led(max86915_data,
max86915_data->agc_current[led_num0], led_num0);
}
}
if (max86915_data->mode_sdk_enabled & 0x02) { /* RED channel */
if (max86915_data->agc_current[led_num1] != MAX86915_MIN_CURRENT) {
err |= max86915_hrm_agc(max86915_data, red, led_num1);
} else {
max86915_data->agc_current[led_num1]
= max86915_data->default_current2
* MAX86915_CURRENT_PER_STEP(max86915_data->led_range[led_num1]);
max86915_data->agc_enabled = 0;
max86915_data->sample_cnt = 0;
max86915_data->reached_thresh[led_num1] = 0;
max86915_data->prev_current[led_num1] = 0;
max86915_data->prev_agc_current[led_num1]
= MAX86915_MAX_CURRENT(max86915_data->led_range[led_num1]);
max86915_data->prev_ppg[led_num1] = 0;
max86915_data->agc_sum[led_num1] = 0;
max86915_data->agc_sample_cnt[led_num1] = 0;
max86915_data->update_led(max86915_data,
max86915_data->agc_current[led_num1], led_num1);
}
}
if (max86915_data->mode_sdk_enabled & 0x04) { /* Green channel */
if (max86915_data->agc_current[led_num2] != MAX86915_MIN_CURRENT) {
err |= max86915_hrm_agc(max86915_data, green, led_num2);
} else {
max86915_data->agc_current[led_num2]
= max86915_data->default_current3
* MAX86915_CURRENT_PER_STEP(max86915_data->led_range[led_num2]);
max86915_data->agc_enabled = 0;
max86915_data->sample_cnt = 0;
max86915_data->reached_thresh[led_num2] = 0;
max86915_data->prev_current[led_num2] = 0;
max86915_data->prev_agc_current[led_num2]
= MAX86915_MAX_CURRENT(max86915_data->led_range[led_num2]);
max86915_data->prev_ppg[led_num2] = 0;
max86915_data->agc_sum[led_num2] = 0;
max86915_data->agc_sample_cnt[led_num2] = 0;
max86915_data->update_led(max86915_data,
max86915_data->agc_current[led_num2], led_num2);
}
}
if (max86915_data->mode_sdk_enabled & 0x08) { /* BLUE channel */
if (max86915_data->agc_current[led_num3] != MAX86915_MIN_CURRENT) {
err |= max86915_hrm_agc(max86915_data, blue, led_num3);
} else {
max86915_data->agc_current[led_num3]
= max86915_data->default_current4
* MAX86915_CURRENT_PER_STEP(max86915_data->led_range[led_num3]);
max86915_data->agc_enabled = 0;
max86915_data->sample_cnt = 0;
max86915_data->reached_thresh[led_num3] = 0;
max86915_data->prev_current[led_num3] = 0;
max86915_data->prev_agc_current[led_num3]
= MAX86915_MAX_CURRENT(max86915_data->led_range[led_num3]);
max86915_data->prev_ppg[led_num3] = 0;
max86915_data->agc_sum[led_num3] = 0;
max86915_data->agc_sample_cnt[led_num3] = 0;
max86915_data->update_led(max86915_data,
max86915_data->agc_current[led_num3], led_num3);
}
}
}
return err;
}
static void max86915_init_eol_data(struct max86915_eol_data *eol_data)
{
int i = 0;
memset(eol_data, 0, sizeof(struct max86915_eol_data));
do_gettimeofday(&eol_data->start_time);
for (i = 0; i < SELF_MAX_CH_NUM; i++)
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_HIGH].min_val[i] = 0x80000;
eol_data->state = _EOL_STATE_TYPE_NEW_INIT;
}
static void max86915_eol_flicker_data(int ir_data, struct max86915_eol_data *eol_data)
{
int i = 0;
int eol_flicker_retry = eol_data->eol_flicker_data.retry;
int eol_flicker_count = eol_data->eol_flicker_data.count;
int eol_flicker_index = eol_data->eol_flicker_data.index;
u64 data[2];
u64 average[2];
if (!eol_flicker_count)
HRM_dbg("%s - EOL FLICKER STEP 1 STARTED !!\n", __func__);
if (eol_flicker_count < EOL_NEW_FLICKER_SKIP_CNT)
goto eol_flicker_exit;
if (!eol_data->eol_flicker_data.state) {
if (ir_data < EOL_NEW_FLICKER_THRESH)
eol_data->eol_flicker_data.state = 1;
else {
if (eol_flicker_retry < EOL_NEW_FLICKER_RETRY_CNT) {
HRM_dbg("%s - EOL FLICKER STEP 1 Retry : %d\n",
__func__, eol_data->eol_flicker_data.retry);
eol_data->eol_flicker_data.retry++;
goto eol_flicker_exit;
} else
eol_data->eol_flicker_data.state = 1;
}
}
if (eol_flicker_index < EOL_NEW_FLICKER_SIZE) {
eol_data->eol_flicker_data.buf[SELF_IR_CH][eol_flicker_index]
= (u64)ir_data * CONVER_FLOAT;
eol_data->eol_flicker_data.sum[SELF_IR_CH]
+= eol_data->eol_flicker_data.buf[SELF_IR_CH][eol_flicker_index];
if (eol_data->eol_flicker_data.max
< eol_data->eol_flicker_data.buf[SELF_IR_CH][eol_flicker_index])
eol_data->eol_flicker_data.max
= eol_data->eol_flicker_data.buf[SELF_IR_CH][eol_flicker_index];
eol_data->eol_flicker_data.index++;
} else if (eol_flicker_index == EOL_NEW_FLICKER_SIZE && eol_data->eol_flicker_data.done != 1) {
do_div(eol_data->eol_flicker_data.sum[SELF_IR_CH], EOL_NEW_FLICKER_SIZE);
eol_data->eol_flicker_data.average[SELF_IR_CH] = eol_data->eol_flicker_data.sum[SELF_IR_CH];
average[SELF_IR_CH] = eol_data->eol_flicker_data.average[SELF_IR_CH];
do_div(average[SELF_IR_CH], CONVER_FLOAT);
for (i = 0; i < EOL_NEW_FLICKER_SIZE; i++) {
do_div(eol_data->eol_flicker_data.buf[SELF_IR_CH][i], CONVER_FLOAT);
/*
* HRM_dbg("EOL FLICKER STEP 1 ir_data %d, %llu",
* i, eol_data->eol_flicker_data.buf[SELF_IR_CH][i]);
*/
data[SELF_IR_CH] = eol_data->eol_flicker_data.buf[SELF_IR_CH][i];
eol_data->eol_flicker_data.std_sum[SELF_IR_CH]
+= (data[SELF_IR_CH] - average[SELF_IR_CH])
* (data[SELF_IR_CH] - average[SELF_IR_CH]);
}
eol_data->eol_flicker_data.done = 1;
HRM_dbg("%s - EOL FLICKER STEP 1 Done\n", __func__);
}
eol_flicker_exit:
eol_data->eol_flicker_data.count++;
}
static void max86915_eol_std_sum(struct max86915_eol_hrm_data *eol_hrm_data)
{
int ch_i = 0, std_i = 0;
int buf_i = 0, buf_idx = 0;
u64 buf[10] = {0, };
u64 avg = 0, sum = 0, sum_max = 0;
HRM_dbg("%s - Skip Pos:%d, %d, %d, %d, %d, %d, %d, %d\n", __func__,
eol_hrm_data->min_pos[0], eol_hrm_data->max_pos[0],
eol_hrm_data->min_pos[1], eol_hrm_data->max_pos[1],
eol_hrm_data->min_pos[2], eol_hrm_data->max_pos[2],
eol_hrm_data->min_pos[3], eol_hrm_data->max_pos[3]);
for (ch_i = 0; ch_i < SELF_MAX_CH_NUM; ch_i++) {
sum_max = 0;
buf_idx = 0;
for (std_i = 0; std_i < 25; std_i++) {
sum = 0;
avg = 0;
for (buf_i = 0; buf_i < 10; buf_i++) {
buf[buf_i] = eol_hrm_data->buf[ch_i][buf_idx];
if ((buf_idx == eol_hrm_data->min_pos[ch_i])
|| (buf_idx == eol_hrm_data->max_pos[ch_i]))
buf_i--;
else
avg += buf[buf_i];
buf_idx++;
}
do_div(avg, 10);
for (buf_i = 0; buf_i < 10; buf_i++)
sum += (buf[buf_i] - avg)*(buf[buf_i] - avg);
HRM_dbg("%s - sum %d %d : %lld, %lld\n", __func__, ch_i, std_i, avg, sum);
if (sum > sum_max)
sum_max = sum;
}
eol_hrm_data->std_sum2[ch_i] = sum_max;
}
}
static void max86915_eol_hrm_data(int ir_data, int red_data, int green_data, int blue_data,
struct max86915_eol_data *eol_data, u32 array_pos, u32 eol_skip_cnt, u32 eol_step_size)
{
int i = 0;
int hrm_eol_count = eol_data->eol_hrm_data[array_pos].count;
int hrm_eol_index = eol_data->eol_hrm_data[array_pos].index;
int ir_current = eol_data->eol_hrm_data[array_pos].ir_current;
int red_current = eol_data->eol_hrm_data[array_pos].red_current;
int green_current = eol_data->eol_hrm_data[array_pos].green_current;
int blue_current = eol_data->eol_hrm_data[array_pos].blue_current;
u64 data[4];
u64 average[4];
u32 hrm_eol_skip_cnt = eol_skip_cnt;
u32 hrm_eol_size = eol_step_size;
if (!hrm_eol_count) {
HRM_dbg("%s - STEP [%d], [%d] started IR : %d , RED : %d, GREEN : %d, BLUE : %d,eol_skip_cnt : %d, eol_step_size : %d\n",
__func__, array_pos, array_pos + 2, ir_current, red_current,
green_current, blue_current, eol_skip_cnt, eol_step_size);
}
if (eol_step_size > EOL_NEW_HRM_SIZE) {
HRM_dbg("%s - FATAL ERROR !!!! the buffer size should be smaller than EOL_NEW_HRM_SIZE\n", __func__);
goto hrm_eol_exit;
}
if (hrm_eol_count < hrm_eol_skip_cnt)
goto hrm_eol_exit;
if (hrm_eol_index < hrm_eol_size) {
eol_data->eol_hrm_data[array_pos].buf[SELF_IR_CH][hrm_eol_index] = (u64)ir_data * CONVER_FLOAT;
eol_data->eol_hrm_data[array_pos].buf[SELF_RED_CH][hrm_eol_index] = (u64)red_data * CONVER_FLOAT;
eol_data->eol_hrm_data[array_pos].buf[SELF_GREEN_CH][hrm_eol_index] = (u64)green_data * CONVER_FLOAT;
eol_data->eol_hrm_data[array_pos].buf[SELF_BLUE_CH][hrm_eol_index] = (u64)blue_data * CONVER_FLOAT;
eol_data->eol_hrm_data[array_pos].sum[SELF_IR_CH]
+= eol_data->eol_hrm_data[array_pos].buf[SELF_IR_CH][hrm_eol_index];
eol_data->eol_hrm_data[array_pos].sum[SELF_RED_CH]
+= eol_data->eol_hrm_data[array_pos].buf[SELF_RED_CH][hrm_eol_index];
eol_data->eol_hrm_data[array_pos].sum[SELF_GREEN_CH]
+= eol_data->eol_hrm_data[array_pos].buf[SELF_GREEN_CH][hrm_eol_index];
eol_data->eol_hrm_data[array_pos].sum[SELF_BLUE_CH]
+= eol_data->eol_hrm_data[array_pos].buf[SELF_BLUE_CH][hrm_eol_index];
if (array_pos == EOL_NEW_MODE_DC_HIGH
&& eol_data->eol_hrm_data[array_pos].index < EOL_NEW_STD_TOTAL_CNT) {
if (ir_data >= eol_data->eol_hrm_data[array_pos].max_val[SELF_IR_CH]) {
eol_data->eol_hrm_data[array_pos].max_val[SELF_IR_CH] = ir_data;
eol_data->eol_hrm_data[array_pos].max_pos[SELF_IR_CH]
= eol_data->eol_hrm_data[array_pos].index;
}
if (ir_data < eol_data->eol_hrm_data[array_pos].min_val[SELF_IR_CH]) {
eol_data->eol_hrm_data[array_pos].min_val[SELF_IR_CH] = ir_data;
eol_data->eol_hrm_data[array_pos].min_pos[SELF_IR_CH]
= eol_data->eol_hrm_data[array_pos].index;
}
if (red_data >= eol_data->eol_hrm_data[array_pos].max_val[SELF_RED_CH]) {
eol_data->eol_hrm_data[array_pos].max_val[SELF_RED_CH] = red_data;
eol_data->eol_hrm_data[array_pos].max_pos[SELF_RED_CH]
= eol_data->eol_hrm_data[array_pos].index;
}
if (red_data < eol_data->eol_hrm_data[array_pos].min_val[SELF_RED_CH]) {
eol_data->eol_hrm_data[array_pos].min_val[SELF_RED_CH] = red_data;
eol_data->eol_hrm_data[array_pos].min_pos[SELF_RED_CH]
= eol_data->eol_hrm_data[array_pos].index;
}
if (green_data >= eol_data->eol_hrm_data[array_pos].max_val[SELF_GREEN_CH]) {
eol_data->eol_hrm_data[array_pos].max_val[SELF_GREEN_CH] = green_data;
eol_data->eol_hrm_data[array_pos].max_pos[SELF_GREEN_CH]
= eol_data->eol_hrm_data[array_pos].index;
}
if (green_data < eol_data->eol_hrm_data[array_pos].min_val[SELF_GREEN_CH]) {
eol_data->eol_hrm_data[array_pos].min_val[SELF_GREEN_CH] = green_data;
eol_data->eol_hrm_data[array_pos].min_pos[SELF_GREEN_CH]
= eol_data->eol_hrm_data[array_pos].index;
}
if (blue_data >= eol_data->eol_hrm_data[array_pos].max_val[SELF_BLUE_CH]) {
eol_data->eol_hrm_data[array_pos].max_val[SELF_BLUE_CH] = blue_data;
eol_data->eol_hrm_data[array_pos].max_pos[SELF_BLUE_CH]
= eol_data->eol_hrm_data[array_pos].index;
}
if (blue_data < eol_data->eol_hrm_data[array_pos].min_val[SELF_BLUE_CH]) {
eol_data->eol_hrm_data[array_pos].min_val[SELF_BLUE_CH]
= blue_data;
eol_data->eol_hrm_data[array_pos].min_pos[SELF_BLUE_CH]
= eol_data->eol_hrm_data[array_pos].index;
}
}
eol_data->eol_hrm_data[array_pos].index++;
} else if (hrm_eol_index == hrm_eol_size && eol_data->eol_hrm_data[array_pos].done != 1) {
do_div(eol_data->eol_hrm_data[array_pos].sum[SELF_IR_CH], hrm_eol_size);
eol_data->eol_hrm_data[array_pos].average[SELF_IR_CH]
= eol_data->eol_hrm_data[array_pos].sum[SELF_IR_CH];
do_div(eol_data->eol_hrm_data[array_pos].sum[SELF_RED_CH], hrm_eol_size);
eol_data->eol_hrm_data[array_pos].average[SELF_RED_CH]
= eol_data->eol_hrm_data[array_pos].sum[SELF_RED_CH];
do_div(eol_data->eol_hrm_data[array_pos].sum[SELF_GREEN_CH], hrm_eol_size);
eol_data->eol_hrm_data[array_pos].average[SELF_GREEN_CH]
= eol_data->eol_hrm_data[array_pos].sum[SELF_GREEN_CH];
do_div(eol_data->eol_hrm_data[array_pos].sum[SELF_BLUE_CH], hrm_eol_size);
eol_data->eol_hrm_data[array_pos].average[SELF_BLUE_CH]
= eol_data->eol_hrm_data[array_pos].sum[SELF_BLUE_CH];
average[SELF_IR_CH] = eol_data->eol_hrm_data[array_pos].average[SELF_IR_CH];
do_div(average[SELF_IR_CH], CONVER_FLOAT);
average[SELF_RED_CH] = eol_data->eol_hrm_data[array_pos].average[SELF_RED_CH];
do_div(average[SELF_RED_CH], CONVER_FLOAT);
average[SELF_GREEN_CH] = eol_data->eol_hrm_data[array_pos].average[SELF_GREEN_CH];
do_div(average[SELF_GREEN_CH], CONVER_FLOAT);
average[SELF_BLUE_CH] = eol_data->eol_hrm_data[array_pos].average[SELF_BLUE_CH];
do_div(average[SELF_BLUE_CH], CONVER_FLOAT);
for (i = 0; i < hrm_eol_size; i++) {
do_div(eol_data->eol_hrm_data[array_pos].buf[SELF_IR_CH][i], CONVER_FLOAT);
data[SELF_IR_CH] = eol_data->eol_hrm_data[array_pos].buf[SELF_IR_CH][i];
do_div(eol_data->eol_hrm_data[array_pos].buf[SELF_RED_CH][i], CONVER_FLOAT);
data[SELF_RED_CH] = eol_data->eol_hrm_data[array_pos].buf[SELF_RED_CH][i];
do_div(eol_data->eol_hrm_data[array_pos].buf[SELF_GREEN_CH][i], CONVER_FLOAT);
data[SELF_GREEN_CH] = eol_data->eol_hrm_data[array_pos].buf[SELF_GREEN_CH][i];
do_div(eol_data->eol_hrm_data[array_pos].buf[SELF_BLUE_CH][i], CONVER_FLOAT);
data[SELF_BLUE_CH] = eol_data->eol_hrm_data[array_pos].buf[SELF_BLUE_CH][i];
eol_data->eol_hrm_data[array_pos].std_sum[SELF_IR_CH]
+= (data[SELF_IR_CH] - average[SELF_IR_CH])
* (data[SELF_IR_CH] - average[SELF_IR_CH]);
eol_data->eol_hrm_data[array_pos].std_sum[SELF_RED_CH]
+= (data[SELF_RED_CH] - average[SELF_RED_CH])
* (data[SELF_RED_CH] - average[SELF_RED_CH]);
eol_data->eol_hrm_data[array_pos].std_sum[SELF_GREEN_CH]
+= (data[SELF_GREEN_CH] - average[SELF_GREEN_CH])
* (data[SELF_GREEN_CH] - average[SELF_GREEN_CH]);
eol_data->eol_hrm_data[array_pos].std_sum[SELF_BLUE_CH]
+= (data[SELF_BLUE_CH] - average[SELF_BLUE_CH])
* (data[SELF_BLUE_CH] - average[SELF_BLUE_CH]);
}
if (array_pos == EOL_NEW_MODE_DC_HIGH)
max86915_eol_std_sum(&eol_data->eol_hrm_data[array_pos]);
eol_data->eol_hrm_data[array_pos].done = 1;
eol_data->eol_hrm_flag |= 1 << array_pos;
HRM_dbg("%s - STEP [%d], [%d], [%x] Done\n",
__func__, array_pos, array_pos + 2, eol_data->eol_hrm_flag);
}
hrm_eol_exit:
eol_data->eol_hrm_data[array_pos].count++;
}
static void max86915_eol_freq_data(struct max86915_eol_data *eol_data, u8 divid)
{
int freq_state = eol_data->eol_freq_data.state;
unsigned long freq_end_time = eol_data->eol_freq_data.end_time;
unsigned long freq_prev_time = eol_data->eol_freq_data.prev_time;
unsigned long freq_current_time = jiffies;
int freq_count = eol_data->eol_freq_data.skip_count;
unsigned long freq_interval;
int freq_start_time;
int freq_work_time;
u32 freq_skip_cnt = EOL_NEW_FREQ_SKIP_CNT / divid;
u32 freq_interrupt_min = EOL_NEW_FREQ_MIN / divid;
u32 freq_timeout = (HZ / (HZ / divid));
if (freq_count < freq_skip_cnt)
goto seq3_exit;
switch (freq_state) {
case 0:
HRM_dbg("%s - EOL FREQ 7 stated ###\n", __func__);
eol_data->eol_freq_data.start_time = jiffies;
do_gettimeofday(&eol_data->eol_freq_data.start_time_t);
eol_data->eol_freq_data.end_time = jiffies + HZ; /* timeout in 1s */
eol_data->eol_freq_data.state = 1;
eol_data->eol_freq_data.count++;
break;
case 1:
if (time_is_after_eq_jiffies(freq_end_time)) {
if (time_is_after_eq_jiffies(freq_prev_time)) {
eol_data->eol_freq_data.count++;
} else {
if (eol_data->eol_freq_data.skew_retry < EOL_NEW_FREQ_RETRY_CNT) {
eol_data->eol_freq_data.skew_retry++;
freq_interval = freq_current_time-freq_prev_time;
HRM_dbg("%s - !!!!!! EOL FREQ 7 Clock skew too large retry : %d ,interval : %lu ms, count : %d\n", __func__,
eol_data->eol_freq_data.skew_retry, freq_interval*10,
eol_data->eol_freq_data.count);
eol_data->eol_freq_data.state = 0;
eol_data->eol_freq_data.count = 0;
eol_data->eol_freq_data.prev_time = 0;
}
}
do_gettimeofday(&eol_data->eol_freq_data.work_time_t);
} else {
if (eol_data->eol_freq_data.count < freq_interrupt_min
&& eol_data->eol_freq_data.retry < EOL_NEW_FREQ_SKEW_RETRY_CNT) {
HRM_dbg("%s - !!!!!! EOL FREQ 7 CNT Gap too large : %d, retry : %d\n", __func__,
eol_data->eol_freq_data.count, eol_data->eol_freq_data.retry);
eol_data->eol_freq_data.retry++;
eol_data->eol_freq_data.state = 0;
eol_data->eol_freq_data.count = 0;
eol_data->eol_freq_data.prev_time = 0;
} else {
eol_data->eol_freq_data.count *= divid;
eol_data->eol_freq_data.done = 1;
freq_start_time = (eol_data->eol_freq_data.start_time_t.tv_sec * 1000)
+ (eol_data->eol_freq_data.start_time_t.tv_usec / 1000);
freq_work_time = (eol_data->eol_freq_data.work_time_t.tv_sec * 1000)
+ (eol_data->eol_freq_data.work_time_t.tv_usec / 1000);
HRM_dbg("%s - EOL FREQ 7 Done###\n", __func__);
HRM_dbg("%s - EOL FREQ 7 sample_no_cnt : %d, time : %d ms, divid : %d\n", __func__,
eol_data->eol_freq_data.count, freq_work_time - freq_start_time, divid);
}
}
break;
default:
HRM_dbg("%s - EOL FREQ 7 Should not call this routine !!!###\n", __func__);
break;
}
seq3_exit:
eol_data->eol_freq_data.prev_time = freq_current_time + freq_timeout; /* timeout in 10 or 40 ms or 100 ms */
eol_data->eol_freq_data.skip_count++;
}
u32 max86915_eol_sqrt(u64 sqsum)
{
u64 sq_rt;
u64 g0, g1, g2, g3, g4;
unsigned int seed;
unsigned int next;
unsigned int step;
g4 = sqsum / 100000000;
g3 = (sqsum - g4 * 100000000) / 1000000;
g2 = (sqsum - g4 * 100000000 - g3 * 1000000) / 10000;
g1 = (sqsum - g4 * 100000000 - g3 * 1000000 - g2 * 10000) / 100;
g0 = (sqsum - g4 * 100000000 - g3 * 1000000 - g2 * 10000 - g1 * 100);
next = g4;
step = 0;
seed = 0;
while (((seed + 1) * (step + 1)) <= next) {
step++;
seed++;
}
sq_rt = (u64)seed * 10000;
next = (next - (seed * step)) * 100 + g3;
step = 0;
seed = 2 * seed * 10;
while (((seed + 1) * (step + 1)) <= next) {
step++;
seed++;
}
sq_rt = sq_rt + step * 1000;
next = (next - seed * step) * 100 + g2;
seed = (seed + step) * 10;
step = 0;
while (((seed + 1) * (step + 1)) <= next) {
step++;
seed++;
}
sq_rt = sq_rt + step * 100;
next = (next - seed * step) * 100 + g1;
seed = (seed + step) * 10;
step = 0;
while (((seed + 1) * (step + 1)) <= next) {
step++;
seed++;
}
sq_rt = sq_rt + step * 10;
next = (next - seed * step) * 100 + g0;
seed = (seed + step) * 10;
step = 0;
while (((seed + 1) * (step + 1)) <= next) {
step++;
seed++;
}
sq_rt = sq_rt + step;
return (u32) sq_rt;
}
void max86915_eol_xtalk_data(int ir_data, struct max86915_eol_data *eol_data)
{
int eol_xtalk_count = eol_data->eol_xtalk_data.count;
int eol_xtalk_index = eol_data->eol_xtalk_data.index;
if (!eol_xtalk_count)
HRM_dbg("%s - EOL XTALK STEP 1 STARTED !!\n", __func__);
if (eol_xtalk_count < EOL_XTALK_SKIP_CNT)
goto xtalk_eol_exit;
if (eol_xtalk_index < EOL_XTALK_SIZE) {
eol_data->eol_xtalk_data.buf[eol_xtalk_index] = (s64)ir_data;
eol_data->eol_xtalk_data.sum += eol_data->eol_xtalk_data.buf[eol_xtalk_index];
eol_data->eol_xtalk_data.index++;
} else {
eol_data->eol_xtalk_data.average
= div64_s64(eol_data->eol_xtalk_data.sum, EOL_XTALK_SIZE);
eol_data->eol_xtalk_data.done = EOL_SUCCESS;
HRM_dbg("%s - EOL XTALK STEP 1 Done\n", __func__);
}
xtalk_eol_exit:
eol_data->eol_xtalk_data.count++;
}
void max86915_make_eol_item(struct max86915_eol_data *eol_data)
{
switch (max86915_data->eol_test_type) {
case EOL_15_MODE:
snprintf(eol_data->eol_item_data.test_type, 5, "%s", "MAIN");
eol_data->eol_item_data.system_noise[EOL_SPEC_MIN] = max86915_data->eol_spec[0];
eol_data->eol_item_data.system_noise[EOL_SPEC_MAX] = max86915_data->eol_spec[1];
eol_data->eol_item_data.ir_mid[EOL_SPEC_MIN] = max86915_data->eol_spec[2];
eol_data->eol_item_data.ir_mid[EOL_SPEC_MAX] = max86915_data->eol_spec[3];
eol_data->eol_item_data.red_mid[EOL_SPEC_MIN] = max86915_data->eol_spec[4];
eol_data->eol_item_data.red_mid[EOL_SPEC_MAX] = max86915_data->eol_spec[5];
eol_data->eol_item_data.green_mid[EOL_SPEC_MIN] = max86915_data->eol_spec[6];
eol_data->eol_item_data.green_mid[EOL_SPEC_MAX] = max86915_data->eol_spec[7];
eol_data->eol_item_data.blue_mid[EOL_SPEC_MIN] = max86915_data->eol_spec[8];
eol_data->eol_item_data.blue_mid[EOL_SPEC_MAX] = max86915_data->eol_spec[9];
eol_data->eol_item_data.ir_high[EOL_SPEC_MIN] = max86915_data->eol_spec[10];
eol_data->eol_item_data.ir_high[EOL_SPEC_MAX] = max86915_data->eol_spec[11];
eol_data->eol_item_data.red_high[EOL_SPEC_MIN] = max86915_data->eol_spec[12];
eol_data->eol_item_data.red_high[EOL_SPEC_MAX] = max86915_data->eol_spec[13];
eol_data->eol_item_data.green_high[EOL_SPEC_MIN] = max86915_data->eol_spec[14];
eol_data->eol_item_data.green_high[EOL_SPEC_MAX] = max86915_data->eol_spec[15];
eol_data->eol_item_data.blue_high[EOL_SPEC_MIN] = max86915_data->eol_spec[16];
eol_data->eol_item_data.blue_high[EOL_SPEC_MAX] = max86915_data->eol_spec[17];
eol_data->eol_item_data.ir_xtalk[EOL_SPEC_MIN] = max86915_data->eol_spec[18];
eol_data->eol_item_data.ir_xtalk[EOL_SPEC_MAX] = max86915_data->eol_spec[19];
eol_data->eol_item_data.red_xtalk[EOL_SPEC_MIN] = max86915_data->eol_spec[20];
eol_data->eol_item_data.red_xtalk[EOL_SPEC_MAX] = max86915_data->eol_spec[21];
eol_data->eol_item_data.green_xtalk[EOL_SPEC_MIN] = max86915_data->eol_spec[22];
eol_data->eol_item_data.green_xtalk[EOL_SPEC_MAX] = max86915_data->eol_spec[23];
eol_data->eol_item_data.blue_xtalk[EOL_SPEC_MIN] = max86915_data->eol_spec[24];
eol_data->eol_item_data.blue_xtalk[EOL_SPEC_MAX] = max86915_data->eol_spec[25];
eol_data->eol_item_data.ir_noise[EOL_SPEC_MIN] = max86915_data->eol_spec[26];
eol_data->eol_item_data.ir_noise[EOL_SPEC_MAX] = max86915_data->eol_spec[27];
eol_data->eol_item_data.red_noise[EOL_SPEC_MIN] = max86915_data->eol_spec[28];
eol_data->eol_item_data.red_noise[EOL_SPEC_MAX] = max86915_data->eol_spec[29];
eol_data->eol_item_data.green_noise[EOL_SPEC_MIN] = max86915_data->eol_spec[30];
eol_data->eol_item_data.green_noise[EOL_SPEC_MAX] = max86915_data->eol_spec[31];
eol_data->eol_item_data.blue_noise[EOL_SPEC_MIN] = max86915_data->eol_spec[32];
eol_data->eol_item_data.blue_noise[EOL_SPEC_MAX] = max86915_data->eol_spec[33];
eol_data->eol_item_data.frequency[EOL_SPEC_MIN] = max86915_data->eol_spec[34];
eol_data->eol_item_data.frequency[EOL_SPEC_MAX] = max86915_data->eol_spec[35];
break;
case EOL_SEMI_FT:
snprintf(eol_data->eol_item_data.test_type, 5, "%s", "SEMI");
eol_data->eol_item_data.system_noise[EOL_SPEC_MIN] = max86915_data->eol_semi_spec[0];
eol_data->eol_item_data.system_noise[EOL_SPEC_MAX] = max86915_data->eol_semi_spec[1];
eol_data->eol_item_data.ir_mid[EOL_SPEC_MIN] = max86915_data->eol_semi_spec[2];
eol_data->eol_item_data.ir_mid[EOL_SPEC_MAX] = max86915_data->eol_semi_spec[3];
eol_data->eol_item_data.red_mid[EOL_SPEC_MIN] = max86915_data->eol_semi_spec[4];
eol_data->eol_item_data.red_mid[EOL_SPEC_MAX] = max86915_data->eol_semi_spec[5];
eol_data->eol_item_data.green_mid[EOL_SPEC_MIN] = max86915_data->eol_semi_spec[6];
eol_data->eol_item_data.green_mid[EOL_SPEC_MAX] = max86915_data->eol_semi_spec[7];
eol_data->eol_item_data.blue_mid[EOL_SPEC_MIN] = max86915_data->eol_semi_spec[8];
eol_data->eol_item_data.blue_mid[EOL_SPEC_MAX] = max86915_data->eol_semi_spec[9];
eol_data->eol_item_data.ir_high[EOL_SPEC_MIN] = max86915_data->eol_semi_spec[10];
eol_data->eol_item_data.ir_high[EOL_SPEC_MAX] = max86915_data->eol_semi_spec[11];
eol_data->eol_item_data.red_high[EOL_SPEC_MIN] = max86915_data->eol_semi_spec[12];
eol_data->eol_item_data.red_high[EOL_SPEC_MAX] = max86915_data->eol_semi_spec[13];
eol_data->eol_item_data.green_high[EOL_SPEC_MIN] = max86915_data->eol_semi_spec[14];
eol_data->eol_item_data.green_high[EOL_SPEC_MAX] = max86915_data->eol_semi_spec[15];
eol_data->eol_item_data.blue_high[EOL_SPEC_MIN] = max86915_data->eol_semi_spec[16];
eol_data->eol_item_data.blue_high[EOL_SPEC_MAX] = max86915_data->eol_semi_spec[17];
eol_data->eol_item_data.ir_xtalk[EOL_SPEC_MIN] = max86915_data->eol_semi_spec[18];
eol_data->eol_item_data.ir_xtalk[EOL_SPEC_MAX] = max86915_data->eol_semi_spec[19];
eol_data->eol_item_data.red_xtalk[EOL_SPEC_MIN] = max86915_data->eol_semi_spec[20];
eol_data->eol_item_data.red_xtalk[EOL_SPEC_MAX] = max86915_data->eol_semi_spec[21];
eol_data->eol_item_data.green_xtalk[EOL_SPEC_MIN] = max86915_data->eol_semi_spec[22];
eol_data->eol_item_data.green_xtalk[EOL_SPEC_MAX] = max86915_data->eol_semi_spec[23];
eol_data->eol_item_data.blue_xtalk[EOL_SPEC_MIN] = max86915_data->eol_semi_spec[24];
eol_data->eol_item_data.blue_xtalk[EOL_SPEC_MAX] = max86915_data->eol_semi_spec[25];
eol_data->eol_item_data.ir_noise[EOL_SPEC_MIN] = max86915_data->eol_semi_spec[26];
eol_data->eol_item_data.ir_noise[EOL_SPEC_MAX] = max86915_data->eol_semi_spec[27];
eol_data->eol_item_data.red_noise[EOL_SPEC_MIN] = max86915_data->eol_semi_spec[28];
eol_data->eol_item_data.red_noise[EOL_SPEC_MAX] = max86915_data->eol_semi_spec[29];
eol_data->eol_item_data.green_noise[EOL_SPEC_MIN] = max86915_data->eol_semi_spec[30];
eol_data->eol_item_data.green_noise[EOL_SPEC_MAX] = max86915_data->eol_semi_spec[31];
eol_data->eol_item_data.blue_noise[EOL_SPEC_MIN] = max86915_data->eol_semi_spec[32];
eol_data->eol_item_data.blue_noise[EOL_SPEC_MAX] = max86915_data->eol_semi_spec[33];
eol_data->eol_item_data.frequency[EOL_SPEC_MIN] = max86915_data->eol_semi_spec[34];
eol_data->eol_item_data.frequency[EOL_SPEC_MAX] = max86915_data->eol_semi_spec[35];
break;
case EOL_XTALK:
snprintf(eol_data->eol_item_data.test_type, 6, "%s", "XTALK");
eol_data->eol_item_data.xtalk[EOL_SPEC_MIN] = (s32) max86915_data->eol_xtalk_spec[0];
eol_data->eol_item_data.xtalk[EOL_SPEC_MAX] = (s32) max86915_data->eol_xtalk_spec[1];
break;
default:
break;
}
switch (max86915_data->eol_test_type) {
case EOL_15_MODE:
case EOL_SEMI_FT:
eol_data->eol_item_data.system_noise[EOL_MEASURE] =
(u32) (eol_data->eol_flicker_data.average[SELF_IR_CH] / CONVER_FLOAT);
eol_data->eol_item_data.ir_mid[EOL_MEASURE] =
(u32) (eol_data->eol_hrm_data[EOL_NEW_MODE_DC_MID].average[SELF_IR_CH] / CONVER_FLOAT);
eol_data->eol_item_data.red_mid[EOL_MEASURE] =
(u32) (eol_data->eol_hrm_data[EOL_NEW_MODE_DC_MID].average[SELF_RED_CH] / CONVER_FLOAT);
eol_data->eol_item_data.green_mid[EOL_MEASURE] =
(u32) (eol_data->eol_hrm_data[EOL_NEW_MODE_DC_MID].average[SELF_GREEN_CH] / CONVER_FLOAT);
eol_data->eol_item_data.blue_mid[EOL_MEASURE] =
(u32) (eol_data->eol_hrm_data[EOL_NEW_MODE_DC_MID].average[SELF_BLUE_CH] / CONVER_FLOAT);
eol_data->eol_item_data.ir_high[EOL_MEASURE] =
(u32) (eol_data->eol_hrm_data[EOL_NEW_MODE_DC_HIGH].average[SELF_IR_CH] / CONVER_FLOAT);
eol_data->eol_item_data.red_high[EOL_MEASURE] =
(u32) (eol_data->eol_hrm_data[EOL_NEW_MODE_DC_HIGH].average[SELF_RED_CH] / CONVER_FLOAT);
eol_data->eol_item_data.green_high[EOL_MEASURE] =
(u32) (eol_data->eol_hrm_data[EOL_NEW_MODE_DC_HIGH].average[SELF_GREEN_CH] / CONVER_FLOAT);
eol_data->eol_item_data.blue_high[EOL_MEASURE] =
(u32) (eol_data->eol_hrm_data[EOL_NEW_MODE_DC_HIGH].average[SELF_BLUE_CH] / CONVER_FLOAT);
eol_data->eol_item_data.ir_xtalk[EOL_MEASURE] =
(u32) (eol_data->eol_hrm_data[EOL_NEW_MODE_DC_XTALK].average[SELF_IR_CH] / CONVER_FLOAT);
eol_data->eol_item_data.red_xtalk[EOL_MEASURE] =
(u32) (eol_data->eol_hrm_data[EOL_NEW_MODE_DC_XTALK].average[SELF_RED_CH] / CONVER_FLOAT);
eol_data->eol_item_data.green_xtalk[EOL_MEASURE] =
(u32) (eol_data->eol_hrm_data[EOL_NEW_MODE_DC_XTALK].average[SELF_GREEN_CH] / CONVER_FLOAT);
eol_data->eol_item_data.blue_xtalk[EOL_MEASURE] =
(u32) (eol_data->eol_hrm_data[EOL_NEW_MODE_DC_XTALK].average[SELF_BLUE_CH] / CONVER_FLOAT);
eol_data->eol_item_data.ir_noise[EOL_MEASURE] =
max86915_eol_sqrt(eol_data->eol_hrm_data[EOL_NEW_MODE_DC_HIGH].std_sum2[SELF_IR_CH] / EOL_NEW_STD2_SIZE);
eol_data->eol_item_data.red_noise[EOL_MEASURE] =
max86915_eol_sqrt(eol_data->eol_hrm_data[EOL_NEW_MODE_DC_HIGH].std_sum2[SELF_RED_CH] / EOL_NEW_STD2_SIZE);
eol_data->eol_item_data.green_noise[EOL_MEASURE] =
max86915_eol_sqrt (eol_data->eol_hrm_data[EOL_NEW_MODE_DC_HIGH].std_sum2[SELF_GREEN_CH] / EOL_NEW_STD2_SIZE);
eol_data->eol_item_data.blue_noise[EOL_MEASURE] =
max86915_eol_sqrt(eol_data->eol_hrm_data[EOL_NEW_MODE_DC_HIGH].std_sum2[SELF_BLUE_CH] / EOL_NEW_STD2_SIZE);
eol_data->eol_item_data.frequency[EOL_MEASURE] = (u32) eol_data->eol_freq_data.count;
if (eol_data->eol_item_data.system_noise[EOL_MEASURE] >= eol_data->eol_item_data.system_noise[EOL_SPEC_MIN]
&& eol_data->eol_item_data.system_noise[EOL_MEASURE] <= eol_data->eol_item_data.system_noise[EOL_SPEC_MAX])
eol_data->eol_item_data.system_noise[EOL_PASS_FAIL] = true;
else
eol_data->eol_item_data.system_noise[EOL_PASS_FAIL] = false;
if (eol_data->eol_item_data.ir_mid[EOL_MEASURE] >= eol_data->eol_item_data.ir_mid[EOL_SPEC_MIN]
&& eol_data->eol_item_data.ir_mid[EOL_MEASURE] <= eol_data->eol_item_data.ir_mid[EOL_SPEC_MAX])
eol_data->eol_item_data.ir_mid[EOL_PASS_FAIL] = true;
else
eol_data->eol_item_data.ir_mid[EOL_PASS_FAIL] = false;
if (eol_data->eol_item_data.red_mid[EOL_MEASURE] >= eol_data->eol_item_data.red_mid[EOL_SPEC_MIN]
&& eol_data->eol_item_data.red_mid[EOL_MEASURE] <= eol_data->eol_item_data.red_mid[EOL_SPEC_MAX])
eol_data->eol_item_data.red_mid[EOL_PASS_FAIL] = true;
else
eol_data->eol_item_data.red_mid[EOL_PASS_FAIL] = false;
if (eol_data->eol_item_data.green_mid[EOL_MEASURE] >= eol_data->eol_item_data.green_mid[EOL_SPEC_MIN]
&& eol_data->eol_item_data.green_mid[EOL_MEASURE] <= eol_data->eol_item_data.green_mid[EOL_SPEC_MAX])
eol_data->eol_item_data.green_mid[EOL_PASS_FAIL] = true;
else
eol_data->eol_item_data.green_mid[EOL_PASS_FAIL] = false;
if (eol_data->eol_item_data.blue_mid[EOL_MEASURE] >= eol_data->eol_item_data.blue_mid[EOL_SPEC_MIN]
&& eol_data->eol_item_data.blue_mid[EOL_MEASURE] <= eol_data->eol_item_data.blue_mid[EOL_SPEC_MAX])
eol_data->eol_item_data.blue_mid[EOL_PASS_FAIL] = true;
else
eol_data->eol_item_data.blue_mid[EOL_PASS_FAIL] = false;
if (eol_data->eol_item_data.ir_high[EOL_MEASURE] >= eol_data->eol_item_data.ir_high[EOL_SPEC_MIN]
&& eol_data->eol_item_data.ir_high[EOL_MEASURE] <= eol_data->eol_item_data.ir_high[EOL_SPEC_MAX])
eol_data->eol_item_data.ir_high[EOL_PASS_FAIL] = true;
else
eol_data->eol_item_data.ir_high[EOL_PASS_FAIL] = false;
if (eol_data->eol_item_data.red_high[EOL_MEASURE] >= eol_data->eol_item_data.red_high[EOL_SPEC_MIN]
&& eol_data->eol_item_data.red_high[EOL_MEASURE] <= eol_data->eol_item_data.red_high[EOL_SPEC_MAX])
eol_data->eol_item_data.red_high[EOL_PASS_FAIL] = true;
else
eol_data->eol_item_data.red_high[EOL_PASS_FAIL] = false;
if (eol_data->eol_item_data.green_high[EOL_MEASURE] >= eol_data->eol_item_data.green_high[EOL_SPEC_MIN]
&& eol_data->eol_item_data.green_high[EOL_MEASURE] <= eol_data->eol_item_data.green_high[EOL_SPEC_MAX])
eol_data->eol_item_data.green_high[EOL_PASS_FAIL] = true;
else
eol_data->eol_item_data.green_high[EOL_PASS_FAIL] = false;
if (eol_data->eol_item_data.blue_high[EOL_MEASURE] >= eol_data->eol_item_data.blue_high[EOL_SPEC_MIN]
&& eol_data->eol_item_data.blue_high[EOL_MEASURE] <= eol_data->eol_item_data.blue_high[EOL_SPEC_MAX])
eol_data->eol_item_data.blue_high[EOL_PASS_FAIL] = true;
else
eol_data->eol_item_data.blue_high[EOL_PASS_FAIL] = false;
if (eol_data->eol_item_data.ir_xtalk[EOL_MEASURE] >= eol_data->eol_item_data.ir_xtalk[EOL_SPEC_MIN]
&& eol_data->eol_item_data.ir_xtalk[EOL_MEASURE] <= eol_data->eol_item_data.ir_xtalk[EOL_SPEC_MAX])
eol_data->eol_item_data.ir_xtalk[EOL_PASS_FAIL] = true;
else
eol_data->eol_item_data.ir_xtalk[EOL_PASS_FAIL] = false;
if (eol_data->eol_item_data.red_xtalk[EOL_MEASURE] >= eol_data->eol_item_data.red_xtalk[EOL_SPEC_MIN]
&& eol_data->eol_item_data.red_xtalk[EOL_MEASURE] <= eol_data->eol_item_data.red_xtalk[EOL_SPEC_MAX])
eol_data->eol_item_data.red_xtalk[EOL_PASS_FAIL] = true;
else
eol_data->eol_item_data.red_xtalk[EOL_PASS_FAIL] = false;
if (eol_data->eol_item_data.green_xtalk[EOL_MEASURE] >= eol_data->eol_item_data.green_xtalk[EOL_SPEC_MIN]
&& eol_data->eol_item_data.green_xtalk[EOL_MEASURE] <= eol_data->eol_item_data.green_xtalk[EOL_SPEC_MAX])
eol_data->eol_item_data.green_xtalk[EOL_PASS_FAIL] = true;
else
eol_data->eol_item_data.green_xtalk[EOL_PASS_FAIL] = false;
if (eol_data->eol_item_data.blue_xtalk[EOL_MEASURE] >= eol_data->eol_item_data.blue_xtalk[EOL_SPEC_MIN]
&& eol_data->eol_item_data.blue_xtalk[EOL_MEASURE] <= eol_data->eol_item_data.blue_xtalk[EOL_SPEC_MAX])
eol_data->eol_item_data.blue_xtalk[EOL_PASS_FAIL] = true;
else
eol_data->eol_item_data.blue_xtalk[EOL_PASS_FAIL] = false;
if (eol_data->eol_item_data.ir_noise[EOL_MEASURE] >= eol_data->eol_item_data.ir_noise[EOL_SPEC_MIN]
&& eol_data->eol_item_data.ir_noise[EOL_MEASURE] <= eol_data->eol_item_data.ir_noise[EOL_SPEC_MAX])
eol_data->eol_item_data.ir_noise[EOL_PASS_FAIL] = true;
else
eol_data->eol_item_data.ir_noise[EOL_PASS_FAIL] = false;
if (eol_data->eol_item_data.red_noise[EOL_MEASURE] >= eol_data->eol_item_data.red_noise[EOL_SPEC_MIN]
&& eol_data->eol_item_data.red_noise[EOL_MEASURE] <= eol_data->eol_item_data.red_noise[EOL_SPEC_MAX])
eol_data->eol_item_data.red_noise[EOL_PASS_FAIL] = true;
else
eol_data->eol_item_data.red_noise[EOL_PASS_FAIL] = false;
if (eol_data->eol_item_data.green_noise[EOL_MEASURE] >= eol_data->eol_item_data.green_noise[EOL_SPEC_MIN]
&& eol_data->eol_item_data.green_noise[EOL_MEASURE] <= eol_data->eol_item_data.green_noise[EOL_SPEC_MAX])
eol_data->eol_item_data.green_noise[EOL_PASS_FAIL] = true;
else
eol_data->eol_item_data.green_noise[EOL_PASS_FAIL] = false;
if (eol_data->eol_item_data.blue_noise[EOL_MEASURE] >= eol_data->eol_item_data.blue_noise[EOL_SPEC_MIN]
&& eol_data->eol_item_data.blue_noise[EOL_MEASURE] <= eol_data->eol_item_data.blue_noise[EOL_SPEC_MAX])
eol_data->eol_item_data.blue_noise[EOL_PASS_FAIL] = true;
else
eol_data->eol_item_data.blue_noise[EOL_PASS_FAIL] = false;
if (eol_data->eol_item_data.frequency[EOL_MEASURE] >= eol_data->eol_item_data.frequency[EOL_SPEC_MIN]
&& eol_data->eol_item_data.frequency[EOL_MEASURE] <= eol_data->eol_item_data.frequency[EOL_SPEC_MAX])
eol_data->eol_item_data.frequency[EOL_PASS_FAIL] = true;
else
eol_data->eol_item_data.frequency[EOL_PASS_FAIL] = false;
break;
case EOL_XTALK:
eol_data->eol_item_data.xtalk[EOL_MEASURE] = (s32) eol_data->eol_xtalk_data.average;
if (eol_data->eol_item_data.xtalk[EOL_MEASURE] >= eol_data->eol_item_data.xtalk[EOL_SPEC_MIN]
&& eol_data->eol_item_data.xtalk[EOL_MEASURE] <= eol_data->eol_item_data.xtalk[EOL_SPEC_MAX])
eol_data->eol_item_data.xtalk[EOL_PASS_FAIL] = true;
else
eol_data->eol_item_data.xtalk[EOL_PASS_FAIL] = false;
break;
default:
break;
}
}
static void max86915_eol_conv2float(struct max86915_eol_data *eol_data, u8 mode, struct max86915_device_data *device)
{
struct max86915_div_data div_data;
char flicker_max[2][INTEGER_LEN] = { {'\0', }, };
char ir_mdc_avg[2][INTEGER_LEN] = { {'\0', }, }, red_mdc_avg[2][INTEGER_LEN] = { {'\0', }, };
char green_mdc_avg[2][INTEGER_LEN] = { {'\0', }, }, blue_mdc_avg[2][INTEGER_LEN] = { {'\0', }, };
char ir_hdc_avg[2][INTEGER_LEN] = { {'\0', }, }, ir_hdc_std[2][INTEGER_LEN] = { {'\0', }, };
char red_hdc_avg[2][INTEGER_LEN] = { {'\0', }, }, red_hdc_std[2][INTEGER_LEN] = { {'\0', }, };
char green_hdc_avg[2][INTEGER_LEN] = { {'\0', }, }, green_hdc_std[2][INTEGER_LEN] = { {'\0', }, };
char blue_hdc_avg[2][INTEGER_LEN] = { {'\0', }, }, blue_hdc_std[2][INTEGER_LEN] = { {'\0', }, };
char ir_xtalk_avg[2][INTEGER_LEN] = { {'\0', }, }, red_xtalk_avg[2][INTEGER_LEN] = { {'\0', }, };
char green_xtalk_avg[2][INTEGER_LEN] = { {'\0', }, }, blue_xtalk_avg[2][INTEGER_LEN] = { {'\0', }, };
char ir_hdc_std2[2][INTEGER_LEN] = { {'\0', }, };
char red_hdc_std2[2][INTEGER_LEN] = { {'\0', }, };
char green_hdc_std2[2][INTEGER_LEN] = { {'\0', }, };
char blue_hdc_std2[2][INTEGER_LEN] = { {'\0', }, };
HRM_info("%s - start this function.\n", __func__);
if (device->pre_eol_test_is_enable != 2) {
/* flicker */
max86915_div_float(&div_data, eol_data->eol_flicker_data.max, CONVER_FLOAT);
strncpy(flicker_max[0], div_data.result_integer, INTEGER_LEN - 1);
strncpy(flicker_max[1], div_data.result_decimal, INTEGER_LEN - 1);
}
if (device->pre_eol_test_is_enable == 0) {
/* dc mid */
max86915_div_float(&div_data, eol_data->eol_hrm_data[EOL_NEW_MODE_DC_MID].average[SELF_IR_CH], CONVER_FLOAT);
strncpy(ir_mdc_avg[0], div_data.result_integer, INTEGER_LEN - 1);
strncpy(ir_mdc_avg[1], div_data.result_decimal, INTEGER_LEN - 1);
max86915_div_float(&div_data, eol_data->eol_hrm_data[EOL_NEW_MODE_DC_MID].average[SELF_RED_CH], CONVER_FLOAT);
strncpy(red_mdc_avg[0], div_data.result_integer, INTEGER_LEN - 1);
strncpy(red_mdc_avg[1], div_data.result_decimal, INTEGER_LEN - 1);
max86915_div_float(&div_data, eol_data->eol_hrm_data[EOL_NEW_MODE_DC_MID].average[SELF_GREEN_CH], CONVER_FLOAT);
strncpy(green_mdc_avg[0], div_data.result_integer, INTEGER_LEN - 1);
strncpy(green_mdc_avg[1], div_data.result_decimal, INTEGER_LEN - 1);
max86915_div_float(&div_data, eol_data->eol_hrm_data[EOL_NEW_MODE_DC_MID].average[SELF_BLUE_CH], CONVER_FLOAT);
strncpy(blue_mdc_avg[0], div_data.result_integer, INTEGER_LEN - 1);
strncpy(blue_mdc_avg[1], div_data.result_decimal, INTEGER_LEN - 1);
/* dc high */
max86915_div_float(&div_data,
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_HIGH].average[SELF_IR_CH], CONVER_FLOAT);
strncpy(ir_hdc_avg[0], div_data.result_integer, INTEGER_LEN - 1);
strncpy(ir_hdc_avg[1], div_data.result_decimal, INTEGER_LEN - 1);
max86915_div_float(&div_data,
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_HIGH].std_sum[SELF_IR_CH], EOL_NEW_DC_HIGH_SIZE);
strncpy(ir_hdc_std[0], div_data.result_integer, INTEGER_LEN - 1);
strncpy(ir_hdc_std[1], div_data.result_decimal, INTEGER_LEN - 1);
max86915_div_float(&div_data,
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_HIGH].std_sum2[SELF_IR_CH], EOL_NEW_STD2_SIZE);
strncpy(ir_hdc_std2[0], div_data.result_integer, INTEGER_LEN - 1);
strncpy(ir_hdc_std2[1], div_data.result_decimal, INTEGER_LEN - 1);
max86915_div_float(&div_data,
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_HIGH].average[SELF_RED_CH], CONVER_FLOAT);
strncpy(red_hdc_avg[0], div_data.result_integer, INTEGER_LEN - 1);
strncpy(red_hdc_avg[1], div_data.result_decimal, INTEGER_LEN - 1);
max86915_div_float(&div_data,
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_HIGH].std_sum[SELF_RED_CH], EOL_NEW_DC_HIGH_SIZE);
strncpy(red_hdc_std[0], div_data.result_integer, INTEGER_LEN - 1);
strncpy(red_hdc_std[1], div_data.result_decimal, INTEGER_LEN - 1);
max86915_div_float(&div_data,
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_HIGH].std_sum2[SELF_RED_CH], EOL_NEW_STD2_SIZE);
strncpy(red_hdc_std2[0], div_data.result_integer, INTEGER_LEN - 1);
strncpy(red_hdc_std2[1], div_data.result_decimal, INTEGER_LEN - 1);
max86915_div_float(&div_data,
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_HIGH].average[SELF_GREEN_CH], CONVER_FLOAT);
strncpy(green_hdc_avg[0], div_data.result_integer, INTEGER_LEN - 1);
strncpy(green_hdc_avg[1], div_data.result_decimal, INTEGER_LEN - 1);
max86915_div_float(&div_data,
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_HIGH].std_sum[SELF_GREEN_CH], EOL_NEW_DC_HIGH_SIZE);
strncpy(green_hdc_std[0], div_data.result_integer, INTEGER_LEN - 1);
strncpy(green_hdc_std[1], div_data.result_decimal, INTEGER_LEN - 1);
max86915_div_float(&div_data,
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_HIGH].std_sum2[SELF_GREEN_CH], EOL_NEW_STD2_SIZE);
strncpy(green_hdc_std2[0], div_data.result_integer, INTEGER_LEN - 1);
strncpy(green_hdc_std2[1], div_data.result_decimal, INTEGER_LEN - 1);
max86915_div_float(&div_data,
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_HIGH].average[SELF_BLUE_CH], CONVER_FLOAT);
strncpy(blue_hdc_avg[0], div_data.result_integer, INTEGER_LEN - 1);
strncpy(blue_hdc_avg[1], div_data.result_decimal, INTEGER_LEN - 1);
max86915_div_float(&div_data,
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_HIGH].std_sum[SELF_BLUE_CH], EOL_NEW_DC_HIGH_SIZE);
strncpy(blue_hdc_std[0], div_data.result_integer, INTEGER_LEN - 1);
strncpy(blue_hdc_std[1], div_data.result_decimal, INTEGER_LEN - 1);
max86915_div_float(&div_data,
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_HIGH].std_sum2[SELF_BLUE_CH], EOL_NEW_STD2_SIZE);
strncpy(blue_hdc_std2[0], div_data.result_integer, INTEGER_LEN - 1);
strncpy(blue_hdc_std2[1], div_data.result_decimal, INTEGER_LEN - 1);
/* dc xtalk */
max86915_div_float(&div_data,
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_XTALK].average[SELF_IR_CH], CONVER_FLOAT);
strncpy(ir_xtalk_avg[0], div_data.result_integer, INTEGER_LEN - 1);
strncpy(ir_xtalk_avg[1], div_data.result_decimal, INTEGER_LEN - 1);
max86915_div_float(&div_data,
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_XTALK].average[SELF_RED_CH], CONVER_FLOAT);
strncpy(red_xtalk_avg[0], div_data.result_integer, INTEGER_LEN - 1);
strncpy(red_xtalk_avg[1], div_data.result_decimal, INTEGER_LEN - 1);
max86915_div_float(&div_data,
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_XTALK].average[SELF_GREEN_CH], CONVER_FLOAT);
strncpy(green_xtalk_avg[0], div_data.result_integer, INTEGER_LEN - 1);
strncpy(green_xtalk_avg[1], div_data.result_decimal, INTEGER_LEN - 1);
max86915_div_float(&div_data,
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_XTALK].average[SELF_BLUE_CH], CONVER_FLOAT);
strncpy(blue_xtalk_avg[0], div_data.result_integer, INTEGER_LEN - 1);
strncpy(blue_xtalk_avg[1], div_data.result_decimal, INTEGER_LEN - 1);
HRM_info("%s - hdc_std : %s.%c%c, %s.%c%c, %s.%c%c, %s.%c%c, "
"%s.%c%c, %s.%c%c, %s.%c%c, %s.%c%c\n", __func__,
ir_hdc_std[0], ir_hdc_std[1][0], ir_hdc_std[1][1],
red_hdc_std[0], red_hdc_std[1][0], red_hdc_std[1][1],
green_hdc_std[0], green_hdc_std[1][0], green_hdc_std[1][1],
blue_hdc_std[0], blue_hdc_std[1][0], blue_hdc_std[1][1],
ir_hdc_std2[0], ir_hdc_std2[1][0], ir_hdc_std2[1][1],
red_hdc_std2[0], red_hdc_std2[1][0], red_hdc_std2[1][1],
green_hdc_std2[0], green_hdc_std2[1][0], green_hdc_std2[1][1],
blue_hdc_std2[0], blue_hdc_std2[1][0], blue_hdc_std2[1][1]);
max86915_float_sqrt(ir_hdc_std[0], ir_hdc_std[1]);
max86915_float_sqrt(red_hdc_std[0], red_hdc_std[1]);
max86915_float_sqrt(green_hdc_std[0], green_hdc_std[1]);
max86915_float_sqrt(blue_hdc_std[0], blue_hdc_std[1]);
max86915_float_sqrt(ir_hdc_std2[0], ir_hdc_std2[1]);
max86915_float_sqrt(red_hdc_std2[0], red_hdc_std2[1]);
max86915_float_sqrt(green_hdc_std2[0], green_hdc_std2[1]);
max86915_float_sqrt(blue_hdc_std2[0], blue_hdc_std2[1]);
}
if (device->pre_eol_test_is_enable == 1) { /* PRE EOL System Noise */
snprintf(device->eol_test_result, MAX_EOL_RESULT, "%s.%c%c\n",
flicker_max[0], flicker_max[1][0], flicker_max[1][1]);
} else if (device->pre_eol_test_is_enable == 2) { /* PRE EOL FREQ */
snprintf(device->eol_test_result, MAX_EOL_RESULT, "%d\n",
eol_data->eol_freq_data.count);
} else if (device->pre_eol_test_is_enable == 3) { /* PRE EOL BOTH */
snprintf(device->eol_test_result, MAX_EOL_RESULT, "%s.%c%c, %d\n",
flicker_max[0], flicker_max[1][0], flicker_max[1][1],
eol_data->eol_freq_data.count);
} else {
if (device->part_type < PART_TYPE_MAX86917_1) { /* MAX86915 */
snprintf(device->eol_test_result, MAX_EOL_RESULT, "%s.%c%c, 0.00, 0.00, 0.00, 0.00, %s.%c%c, %s.%c%c, %s.%c%c, %s.%c%c, %s.%c%c, %s.%c%c, %s.%c%c, %s.%c%c, %s.%c%c, %s.%c%c, %s.%c%c, %s.%c%c, %s.%c%c, %s.%c%c, %s.%c%c, %s.%c%c, %d\n",
flicker_max[0], flicker_max[1][0], flicker_max[1][1],
ir_mdc_avg[0], ir_mdc_avg[1][0], ir_mdc_avg[1][1],
red_mdc_avg[0], red_mdc_avg[1][0], red_mdc_avg[1][1],
green_mdc_avg[0], green_mdc_avg[1][0], green_mdc_avg[1][1],
blue_mdc_avg[0], blue_mdc_avg[1][0], blue_mdc_avg[1][1],
ir_hdc_avg[0], ir_hdc_avg[1][0], ir_hdc_avg[1][1],
red_hdc_avg[0], red_hdc_avg[1][0], red_hdc_avg[1][1],
green_hdc_avg[0], green_hdc_avg[1][0], green_hdc_avg[1][1],
blue_hdc_avg[0], blue_hdc_avg[1][0], blue_hdc_avg[1][1],
ir_xtalk_avg[0], ir_xtalk_avg[1][0], ir_xtalk_avg[1][1],
red_xtalk_avg[0], red_xtalk_avg[1][0], red_xtalk_avg[1][1],
green_xtalk_avg[0], green_xtalk_avg[1][0], green_xtalk_avg[1][1],
blue_xtalk_avg[0], blue_xtalk_avg[1][0], blue_xtalk_avg[1][1],
ir_hdc_std[0], ir_hdc_std[1][0], ir_hdc_std[1][1],
red_hdc_std[0], red_hdc_std[1][0], red_hdc_std[1][1],
green_hdc_std[0], green_hdc_std[1][0], green_hdc_std[1][1],
blue_hdc_std[0], blue_hdc_std[1][0], blue_hdc_std[1][1],
eol_data->eol_freq_data.count);
} else { /* MAX86917 */
max86915_make_eol_item(eol_data);
snprintf(device->eol_test_result, MAX_EOL_RESULT,
"%s,%d,"
"%s,%d,%d,%d,%d,"
"%s,%d,%d,%d,%d,"
"%s,%d,%d,%d,%d,"
"%s,%d,%d,%d,%d,"
"%s,%d,%d,%d,%d,"
"%s,%d,%d,%d,%d,"
"%s,%d,%d,%d,%d,"
"%s,%d,%d,%d,%d,"
"%s,%d,%d,%d,%d,"
"%s,%d,%d,%d,%d,"
"%s,%d,%d,%d,%d,"
"%s,%d,%d,%d,%d,"
"%s,%d,%d,%d,%d,"
"%s,%d,%d,%d,%d,"
"%s,%d,%d,%d,%d,"
"%s,%d,%d,%d,%d,"
"%s,%d,%d,%d,%d,"
"%s,%d,%d,%d,%d",
eol_data->eol_item_data.test_type, EOL_ITEM_CNT,
EOL_SYSTEM_NOISE,
eol_data->eol_item_data.system_noise[EOL_PASS_FAIL],
eol_data->eol_item_data.system_noise[EOL_MEASURE],
eol_data->eol_item_data.system_noise[EOL_SPEC_MIN],
eol_data->eol_item_data.system_noise[EOL_SPEC_MAX],
EOL_IR_MID,
eol_data->eol_item_data.ir_mid[EOL_PASS_FAIL],
eol_data->eol_item_data.ir_mid[EOL_MEASURE],
eol_data->eol_item_data.ir_mid[EOL_SPEC_MIN],
eol_data->eol_item_data.ir_mid[EOL_SPEC_MAX],
EOL_RED_MID,
eol_data->eol_item_data.red_mid[EOL_PASS_FAIL],
eol_data->eol_item_data.red_mid[EOL_MEASURE],
eol_data->eol_item_data.red_mid[EOL_SPEC_MIN],
eol_data->eol_item_data.red_mid[EOL_SPEC_MAX],
EOL_GREEN_MID,
eol_data->eol_item_data.green_mid[EOL_PASS_FAIL],
eol_data->eol_item_data.green_mid[EOL_MEASURE],
eol_data->eol_item_data.green_mid[EOL_SPEC_MIN],
eol_data->eol_item_data.green_mid[EOL_SPEC_MAX],
EOL_BLUE_MID,
eol_data->eol_item_data.blue_mid[EOL_PASS_FAIL],
eol_data->eol_item_data.blue_mid[EOL_MEASURE],
eol_data->eol_item_data.blue_mid[EOL_SPEC_MIN],
eol_data->eol_item_data.blue_mid[EOL_SPEC_MAX],
EOL_IR_HIGH,
eol_data->eol_item_data.ir_high[EOL_PASS_FAIL],
eol_data->eol_item_data.ir_high[EOL_MEASURE],
eol_data->eol_item_data.ir_high[EOL_SPEC_MIN],
eol_data->eol_item_data.ir_high[EOL_SPEC_MAX],
EOL_RED_HIGH,
eol_data->eol_item_data.red_high[EOL_PASS_FAIL],
eol_data->eol_item_data.red_high[EOL_MEASURE],
eol_data->eol_item_data.red_high[EOL_SPEC_MIN],
eol_data->eol_item_data.red_high[EOL_SPEC_MAX],
EOL_GREEN_HIGH,
eol_data->eol_item_data.green_high[EOL_PASS_FAIL],
eol_data->eol_item_data.green_high[EOL_MEASURE],
eol_data->eol_item_data.green_high[EOL_SPEC_MIN],
eol_data->eol_item_data.green_high[EOL_SPEC_MAX],
EOL_BLUE_HIGH,
eol_data->eol_item_data.blue_high[EOL_PASS_FAIL],
eol_data->eol_item_data.blue_high[EOL_MEASURE],
eol_data->eol_item_data.blue_high[EOL_SPEC_MIN],
eol_data->eol_item_data.blue_high[EOL_SPEC_MAX],
EOL_IR_XTALK,
eol_data->eol_item_data.ir_xtalk[EOL_PASS_FAIL],
eol_data->eol_item_data.ir_xtalk[EOL_MEASURE],
eol_data->eol_item_data.ir_xtalk[EOL_SPEC_MIN],
eol_data->eol_item_data.ir_xtalk[EOL_SPEC_MAX],
EOL_RED_XTALK,
eol_data->eol_item_data.red_xtalk[EOL_PASS_FAIL],
eol_data->eol_item_data.red_xtalk[EOL_MEASURE],
eol_data->eol_item_data.red_xtalk[EOL_SPEC_MIN],
eol_data->eol_item_data.red_xtalk[EOL_SPEC_MAX],
EOL_GREEN_XTALK,
eol_data->eol_item_data.green_xtalk[EOL_PASS_FAIL],
eol_data->eol_item_data.green_xtalk[EOL_MEASURE],
eol_data->eol_item_data.green_xtalk[EOL_SPEC_MIN],
eol_data->eol_item_data.green_xtalk[EOL_SPEC_MAX],
EOL_BLUE_XTALK,
eol_data->eol_item_data.blue_xtalk[EOL_PASS_FAIL],
eol_data->eol_item_data.blue_xtalk[EOL_MEASURE],
eol_data->eol_item_data.blue_xtalk[EOL_SPEC_MIN],
eol_data->eol_item_data.blue_xtalk[EOL_SPEC_MAX],
EOL_IR_NOISE,
eol_data->eol_item_data.ir_noise[EOL_PASS_FAIL],
eol_data->eol_item_data.ir_noise[EOL_MEASURE],
eol_data->eol_item_data.ir_noise[EOL_SPEC_MIN],
eol_data->eol_item_data.ir_noise[EOL_SPEC_MAX],
EOL_RED_NOISE,
eol_data->eol_item_data.red_noise[EOL_PASS_FAIL],
eol_data->eol_item_data.red_noise[EOL_MEASURE],
eol_data->eol_item_data.red_noise[EOL_SPEC_MIN],
eol_data->eol_item_data.red_noise[EOL_SPEC_MAX],
EOL_GREEN_NOISE,
eol_data->eol_item_data.green_noise[EOL_PASS_FAIL],
eol_data->eol_item_data.green_noise[EOL_MEASURE],
eol_data->eol_item_data.green_noise[EOL_SPEC_MIN],
eol_data->eol_item_data.green_noise[EOL_SPEC_MAX],
EOL_BLUE_NOISE,
eol_data->eol_item_data.blue_noise[EOL_PASS_FAIL],
eol_data->eol_item_data.blue_noise[EOL_MEASURE],
eol_data->eol_item_data.blue_noise[EOL_SPEC_MIN],
eol_data->eol_item_data.blue_noise[EOL_SPEC_MAX],
EOL_FREQUENCY,
eol_data->eol_item_data.frequency[EOL_PASS_FAIL],
eol_data->eol_item_data.frequency[EOL_MEASURE],
eol_data->eol_item_data.frequency[EOL_SPEC_MIN],
eol_data->eol_item_data.frequency[EOL_SPEC_MAX]);
}
}
HRM_dbg("%s - result : %s\n", __func__, device->eol_test_result);
}
static void max86915_eol_check_done(struct max86915_eol_data *eol_data, u8 mode, struct max86915_device_data *device)
{
int i = 0;
int start_time;
int end_time;
if (device->eol_test_result != NULL)
kfree(device->eol_test_result);
device->eol_test_result = kzalloc(sizeof(char) * MAX_EOL_RESULT, GFP_KERNEL);
if (device->eol_test_result == NULL) {
HRM_err("%s - couldn't eol_test_result allocate memory\n",
__func__);
return;
}
if ((eol_data->eol_flicker_data.done || (device->pre_eol_test_is_enable == 2))
&& ((eol_data->eol_hrm_flag == EOL_NEW_HRM_COMPLETE_ALL_STEP) || (device->pre_eol_test_is_enable))
&& (eol_data->eol_freq_data.done || (device->pre_eol_test_is_enable == 1))) {
do_gettimeofday(&eol_data->end_time);
start_time = (eol_data->start_time.tv_sec * 1000) + (eol_data->start_time.tv_usec / 1000);
end_time = (eol_data->end_time.tv_sec * 1000) + (eol_data->end_time.tv_usec / 1000);
HRM_dbg("%s - EOL Tested Time : %d ms Tested Count : %d\n",
__func__, end_time - start_time, device->sample_cnt);
HRM_dbg("%s - SETTING CUREENT %x, %x\n", __func__,
EOL_NEW_DC_HIGH_LED_IR_CURRENT, EOL_NEW_DC_HIGH_LED_RED_CURRENT);
for (i = 0; i < EOL_NEW_HRM_ARRY_SIZE; i++)
HRM_dbg("%s - DOUBLE CHECK STEP[%d], done : %d\n",
__func__, i + 2, eol_data->eol_hrm_data[i].done);
if (device->pre_eol_test_is_enable != 2) {
for (i = 0; i < EOL_NEW_FLICKER_SIZE; i++)
HRM_dbg("%s - EOL_NEW_FLICKER_MODE EOLRAW,%llu,%d\n",
__func__, eol_data->eol_flicker_data.buf[SELF_IR_CH][i], 0);
}
if (device->pre_eol_test_is_enable == 0) {
for (i = 0; i < EOL_NEW_DC_MIDDLE_SIZE; i++)
HRM_dbg("%s - EOL_NEW_DC_MIDDLE_MODE EOLRAW,%llu,%llu,%llu,%llu\n", __func__,
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_MID].buf[SELF_IR_CH][i],
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_MID].buf[SELF_RED_CH][i],
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_MID].buf[SELF_GREEN_CH][i],
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_MID].buf[SELF_BLUE_CH][i]);
for (i = 0; i < EOL_NEW_DC_HIGH_SIZE; i++)
HRM_dbg("%s - EOL_NEW_DC_HIGH_MODE EOLRAW,%llu,%llu,%llu,%llu\n", __func__,
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_HIGH].buf[SELF_IR_CH][i],
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_HIGH].buf[SELF_RED_CH][i],
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_HIGH].buf[SELF_GREEN_CH][i],
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_HIGH].buf[SELF_BLUE_CH][i]);
for (i = 0; i < EOL_NEW_DC_XTALK_SIZE; i++)
HRM_dbg("%s - EOL_NEW_DC_XTALK EOLRAW,%llu,%llu,%llu,%llu\n", __func__,
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_XTALK].buf[SELF_IR_CH][i],
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_XTALK].buf[SELF_RED_CH][i],
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_XTALK].buf[SELF_GREEN_CH][i],
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_XTALK].buf[SELF_BLUE_CH][i]);
HRM_dbg("%s - EOL_NEW_FLICKER_MODE RESULT,%llu,%llu\n", __func__,
eol_data->eol_flicker_data.average[SELF_IR_CH],
eol_data->eol_flicker_data.std_sum[SELF_IR_CH]);
HRM_dbg("%s - EOL_NEW_DC_MIDDLE_MODE RESULT,%llu,%llu,%llu,%llu,%llu,%llu,%llu,%llu\n", __func__,
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_MID].average[SELF_IR_CH],
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_MID].std_sum[SELF_IR_CH],
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_MID].average[SELF_RED_CH],
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_MID].std_sum[SELF_RED_CH],
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_MID].average[SELF_GREEN_CH],
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_MID].std_sum[SELF_GREEN_CH],
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_MID].average[SELF_BLUE_CH],
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_MID].std_sum[SELF_BLUE_CH]);
HRM_dbg("%s - EOL_NEW_DC_HIGH_MODE RESULT,%llu,%llu,%llu,%llu,%llu,%llu,%llu,%llu\n", __func__,
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_HIGH].average[SELF_IR_CH],
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_HIGH].std_sum[SELF_IR_CH],
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_HIGH].average[SELF_RED_CH],
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_HIGH].std_sum[SELF_RED_CH],
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_HIGH].average[SELF_GREEN_CH],
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_HIGH].std_sum[SELF_GREEN_CH],
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_HIGH].average[SELF_BLUE_CH],
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_HIGH].std_sum[SELF_BLUE_CH]);
HRM_dbg("%s - EOL_NEW_DC_XTALK RESULT,%llu,%llu,%llu,%llu,%llu,%llu,%llu,%llu\n", __func__,
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_XTALK].average[SELF_IR_CH],
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_XTALK].std_sum[SELF_IR_CH],
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_XTALK].average[SELF_RED_CH],
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_XTALK].std_sum[SELF_RED_CH],
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_XTALK].average[SELF_GREEN_CH],
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_XTALK].std_sum[SELF_GREEN_CH],
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_XTALK].average[SELF_BLUE_CH],
eol_data->eol_hrm_data[EOL_NEW_MODE_DC_XTALK].std_sum[SELF_BLUE_CH]);
}
if (device->pre_eol_test_is_enable != 1) {
HRM_dbg("%s - EOL FREQ RESULT,%d\n", __func__,
eol_data->eol_freq_data.count);
}
max86915_eol_conv2float(eol_data, mode, device);
device->eol_test_status = 1;
device->eol_data.state = _EOL_STATE_TYPE_NEW_STOP;
}
if (eol_data->eol_xtalk_data.done) {
for (i = 0; i < EOL_XTALK_SIZE; i++)
HRM_dbg("%s - EOL_XTALK_MODE EOLRAW,%lld\n",
__func__, eol_data->eol_xtalk_data.buf[i]);
max86915_make_eol_item(eol_data);
snprintf(device->eol_test_result, MAX_EOL_RESULT,
"%s,%d,%s,%d,%d,%d,%d",
eol_data->eol_item_data.test_type, 1,
EOL_HRM_XTALK,
eol_data->eol_item_data.xtalk[EOL_PASS_FAIL],
eol_data->eol_item_data.xtalk[EOL_MEASURE],
eol_data->eol_item_data.xtalk[EOL_SPEC_MIN],
eol_data->eol_item_data.xtalk[EOL_SPEC_MAX]);
HRM_dbg("%s result - %s\n", __func__, device->eol_test_result);
device->eol_test_status = 1;
}
}
static int max86915_enable_eol_flicker(struct max86915_device_data *data)
{
int err;
data->sample_cnt = 0;
data->num_samples = 1;
data->flex_mode = 1;
err = max86915_init_status(data);
/* 400Hz, LED_PW=400us, SPO2_ADC_RANGE=4096nA */
err = max86915_write_reg(data,
MAX86915_MODE_CONFIGURATION_2, 0x0F);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_MODE_CONFIGURATION_2!\n",
__func__);
return -EIO;
}
err = max86915_write_reg(data,
MAX86915_FIFO_CONFIG, 0x0C | MAX86915_FIFO_ROLLS_ON_MASK);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_FIFO_CONFIG!\n",
__func__);
return -EIO;
}
err = max86915_write_reg(data,
MAX86915_INTERRUPT_ENABLE, A_FULL_MASK);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_INTERRUPT_ENABLE!\n",
__func__);
return -EIO;
}
err = max86915_write_reg(data, MAX86915_LED_SEQ_REG_1,
0x01);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_LED_SEQ_REG_1!\n",
__func__);
return -EIO;
}
err = max86915_write_reg(data,
MAX86915_MODE_CONFIGURATION, 0x07);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_MODE_CONFIGURATION!\n",
__func__);
return -EIO;
}
data->eol_data.state = _EOL_STATE_TYPE_NEW_FLICKER_INIT;
return err;
}
static int max86915_enable_eol_dc(struct max86915_device_data *data)
{
int err;
u8 flex_config[2] = {0, };
/* data->led = 1; */ /* Prevent resetting MAX86915_LED_CONFIGURATION */
data->num_samples = 0;
data->flex_mode = 0;
flex_config[0] = (RED_LED_CH << MAX86915_LED2_OFFSET) | IR_LED_CH;
flex_config[1] = (BLUE_LED_CH << MAX86915_LED4_OFFSET) | GREEN_LED_CH;
if (flex_config[0] & MAX86915_LED1_MASK) {
data->num_samples++;
data->flex_mode |= (1 << 0);
}
if (flex_config[0] & MAX86915_LED2_MASK) {
data->num_samples++;
data->flex_mode |= (1 << 1);
}
if (flex_config[1] & MAX86915_LED3_MASK) {
data->num_samples++;
data->flex_mode |= (1 << 2);
}
if (flex_config[1] & MAX86915_LED4_MASK) {
data->num_samples++;
data->flex_mode |= (1 << 3);
}
HRM_info("%s - flexmode : 0x%02x, num_samples : %d\n", __func__,
data->flex_mode, data->num_samples);
err = max86915_write_reg(data, MAX86915_MODE_CONFIGURATION, 0x00);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_MODE_CONFIGURATION!\n",
__func__);
return -EIO;
}
err = max86915_write_reg(data, MAX86915_LED1_PA,
EOL_NEW_DC_MID_LED_IR_CURRENT);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_LED1_PA!\n",
__func__);
return -EIO;
}
err = max86915_write_reg(data, MAX86915_LED2_PA,
EOL_NEW_DC_MID_LED_RED_CURRENT);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_LED2_PA!\n",
__func__);
return -EIO;
}
err = max86915_write_reg(data, MAX86915_LED3_PA,
EOL_NEW_DC_MID_LED_GREEN_CURRENT);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_LED3_PA!\n",
__func__);
return -EIO;
}
err = max86915_write_reg(data, MAX86915_LED4_PA,
EOL_NEW_DC_MID_LED_BLUE_CURRENT);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_LED4_PA!\n",
__func__);
return -EIO;
}
/* LED Range */
err = max86915_write_reg(data, MAX86915_LED_RANGE,
(MAX86915_DEFAULT_LED_RGE1 << MAX86915_LED1_RGE_OFFSET)
| (MAX86915_DEFAULT_LED_RGE2 << MAX86915_LED2_RGE_OFFSET)
| (MAX86915_DEFAULT_LED_RGE3 << MAX86915_LED3_RGE_OFFSET)
| (MAX86915_DEFAULT_LED_RGE4 << MAX86915_LED4_RGE_OFFSET));
if (err != 0) {
HRM_err("%s - error initializing MAX86915_LED_RANGE!\n",
__func__);
return -EIO;
}
/* XTALK */
err = max86915_write_reg(data, MAX86915_DAC1_XTALK_CODE,
0x00);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_DAC1_XTALK_CODE!\n",
__func__);
return -EIO;
}
err = max86915_write_reg(data, MAX86915_DAC2_XTALK_CODE,
0x00);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_DAC2_XTALK_CODE!\n",
__func__);
return -EIO;
}
err = max86915_write_reg(data, MAX86915_DAC3_XTALK_CODE,
0x00);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_DAC3_XTALK_CODE!\n",
__func__);
return -EIO;
}
err = max86915_write_reg(data, MAX86915_DAC4_XTALK_CODE,
0x00);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_DAC4_XTALK_CODE!\n",
__func__);
return -EIO;
}
err = max86915_write_reg(data, MAX86915_INTERRUPT_ENABLE, A_FULL_MASK);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_INTERRUPT_ENABLE!\n",
__func__);
return -EIO;
}
err = max86915_write_reg(data, MAX86915_LED_SEQ_REG_1,
flex_config[0]);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_LED_SEQ_REG_1!\n",
__func__);
return -EIO;
}
err = max86915_write_reg(data, MAX86915_LED_SEQ_REG_2,
flex_config[1]);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_LED_SEQ_REG_2!\n",
__func__);
return -EIO;
}
/* 32uA, 400Hz, 120us */
err = max86915_write_reg(data, MAX86915_MODE_CONFIGURATION_2,
0x0D | (0x03 << MAX86915_ADC_RGE_OFFSET));
if (err != 0) {
HRM_err("%s - error initializing MAX86915_MODE_CONFIGURATION_2!\n",
__func__);
return -EIO;
}
err = max86915_write_reg(data, MAX86915_FIFO_CONFIG, 0x00);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_FIFO_CONFIG!\n",
__func__);
return -EIO;
}
err = max86915_write_reg(data, MAX86915_MODE_CONFIGURATION, 0x03);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_MODE_CONFIGURATION!\n",
__func__);
return -EIO;
}
data->eol_data.state = _EOL_STATE_TYPE_NEW_DC_MODE_MID;
return 0;
}
static int max86915_enable_eol_freq(struct max86915_device_data *data)
{
int err;
u8 flex_config[2] = {0, };
data->num_samples = 0;
data->flex_mode = 0;
flex_config[0] = (RED_LED_CH << MAX86915_LED2_OFFSET) | IR_LED_CH;
flex_config[1] = (BLUE_LED_CH << MAX86915_LED4_OFFSET) | GREEN_LED_CH;
if (flex_config[0] & MAX86915_LED1_MASK) {
data->num_samples++;
data->flex_mode |= (1 << 0);
}
if (flex_config[0] & MAX86915_LED2_MASK) {
data->num_samples++;
data->flex_mode |= (1 << 1);
}
if (flex_config[1] & MAX86915_LED3_MASK) {
data->num_samples++;
data->flex_mode |= (1 << 2);
}
if (flex_config[1] & MAX86915_LED4_MASK) {
data->num_samples++;
data->flex_mode |= (1 << 3);
}
HRM_info("%s - flexmode : 0x%02x, num_samples : %d\n", __func__,
data->flex_mode, data->num_samples);
err = max86915_init_status(data);
if (err < 0)
HRM_err("%s - failed to init_status\n", __func__);
err = max86915_write_reg(data, MAX86915_MODE_CONFIGURATION, 0x00);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_MODE_CONFIGURATION!\n",
__func__);
return -EIO;
}
err = max86915_write_reg(data, MAX86915_LED_SEQ_REG_1,
flex_config[0]);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_LED_SEQ_REG_1!\n",
__func__);
return -EIO;
}
err = max86915_write_reg(data, MAX86915_LED_SEQ_REG_2,
flex_config[1]);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_LED_SEQ_REG_2!\n",
__func__);
return -EIO;
}
err = max86915_write_reg(data, MAX86915_LED1_PA,
EOL_NEW_FREQUENCY_LED_IR_CURRENT);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_LED1_PA!\n",
__func__);
return -EIO;
}
err = max86915_write_reg(data, MAX86915_LED2_PA,
EOL_NEW_FREQUENCY_LED_RED_CURRENT);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_LED2_PA!\n",
__func__);
return -EIO;
}
err = max86915_write_reg(data, MAX86915_LED3_PA,
EOL_NEW_FREQUENCY_LED_GREEN_CURRENT);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_LED3_PA!\n",
__func__);
return -EIO;
}
err = max86915_write_reg(data, MAX86915_LED4_PA,
EOL_NEW_FREQUENCY_LED_BLUE_CURRENT);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_LED4_PA!\n",
__func__);
return -EIO;
}
/* LED Range */
err = max86915_write_reg(data, MAX86915_LED_RANGE,
(MAX86915_DEFAULT_LED_RGE1 << MAX86915_LED1_RGE_OFFSET)
| (MAX86915_DEFAULT_LED_RGE2 << MAX86915_LED2_RGE_OFFSET)
| (MAX86915_DEFAULT_LED_RGE3 << MAX86915_LED3_RGE_OFFSET)
| (MAX86915_DEFAULT_LED_RGE4 << MAX86915_LED4_RGE_OFFSET));
if (err != 0) {
HRM_err("%s - error initializing MAX86915_LED_RANGE!\n",
__func__);
return -EIO;
}
/* XTALK */
err = max86915_write_reg(data, MAX86915_DAC1_XTALK_CODE,
0x00);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_DAC1_XTALK_CODE!\n",
__func__);
return -EIO;
}
err = max86915_write_reg(data, MAX86915_DAC2_XTALK_CODE,
0x00);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_DAC2_XTALK_CODE!\n",
__func__);
return -EIO;
}
err = max86915_write_reg(data, MAX86915_DAC3_XTALK_CODE,
0x00);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_DAC3_XTALK_CODE!\n",
__func__);
return -EIO;
}
err = max86915_write_reg(data, MAX86915_DAC4_XTALK_CODE,
0x00);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_DAC4_XTALK_CODE!\n",
__func__);
return -EIO;
}
/* 32uA, 400Hz, 120us */
err = max86915_write_reg(data, MAX86915_MODE_CONFIGURATION_2,
0x0D | (0x03 << MAX86915_ADC_RGE_OFFSET)); /* Maximum PW is 100us for 4ch/400Hz */
if (err != 0) {
HRM_err("%s - error initializing MAX86915_MODE_CONFIGURATION_2!\n",
__func__);
return -EIO;
}
/* AVERAGE 4 */
err = max86915_write_reg(data, MAX86915_FIFO_CONFIG,
(0x02 << MAX86915_SMP_AVE_OFFSET) & MAX86915_SMP_AVE_MASK);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_FIFO_CONFIG!\n",
__func__);
return -EIO;
}
err = max86915_write_reg(data, MAX86915_INTERRUPT_ENABLE, PPG_RDY_MASK);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_INTERRUPT_ENABLE!\n",
__func__);
return -EIO;
}
err = max86915_write_reg(data, MAX86915_MODE_CONFIGURATION, 0x03);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_MODE_CONFIGURATION!\n",
__func__);
return -EIO;
}
/* set the mode */
data->eol_data.state = _EOL_STATE_TYPE_NEW_FREQ_MODE;
return err;
}
static int max86915_get_num_samples_in_fifo(struct max86915_device_data *data)
{
int fifo_rd_ptr;
int fifo_wr_ptr;
int fifo_ovf_cnt;
char fifo_data[3];
int num_samples = 0;
int ret;
ret = max86915_read_reg(data, MAX86915_FIFO_WRITE_POINTER, fifo_data, 3);
if (ret < 0) {
HRM_err("%s - read reg err:%d\n", __func__, ret);
return ret;
}
fifo_wr_ptr = fifo_data[0] & 0x1F;
fifo_ovf_cnt = fifo_data[1] & 0x1F;
fifo_rd_ptr = fifo_data[2] & 0x1F;
if (fifo_ovf_cnt || fifo_rd_ptr == fifo_wr_ptr) {
if (fifo_ovf_cnt > 0)
HRM_err("%s - FIFO is Full. OVF: %d RD:%d WR:%d\n",
__func__, fifo_ovf_cnt, fifo_rd_ptr, fifo_wr_ptr);
num_samples = MAX86915_FIFO_SIZE;
} else {
if (fifo_wr_ptr > fifo_rd_ptr)
num_samples = fifo_wr_ptr - fifo_rd_ptr;
else if (fifo_wr_ptr < fifo_rd_ptr)
num_samples = MAX86915_FIFO_SIZE +
fifo_wr_ptr - fifo_rd_ptr;
}
return num_samples;
}
static int max86915_get_fifo_settings(struct max86915_device_data *data, u16 *ch)
{
int num_item = 0;
int i;
u16 tmp;
int ret;
u8 buf[2];
/*
* TODO: Somehow when the data corrupted in the bus,
* and i2c functions don't return error messages.
*/
ret = max86915_read_reg(data, MAX86915_LED_SEQ_REG_1, buf, 2);
if (ret < 0)
return ret;
tmp = ((u16)buf[1] << 8) | (u16)buf[0];
*ch = tmp;
for (i = 0; i < 4; i++) {
if (tmp & 0x000F)
num_item++;
tmp >>= 4;
}
return num_item;
}
static int max86915_read_fifo(struct max86915_device_data *sd, char *fifo_buf, int num_bytes)
{
int ret = 0;
int data_offset = 0;
int to_read = 0;
while (num_bytes > 0) {
if (num_bytes > MAX_I2C_BLOCK_SIZE) {
to_read = MAX_I2C_BLOCK_SIZE;
num_bytes -= to_read;
} else {
to_read = num_bytes;
num_bytes = 0;
}
ret = max86915_read_reg(sd, MAX86915_FIFO_DATA, &fifo_buf[data_offset], to_read);
if (ret < 0)
break;
data_offset += to_read;
}
return ret;
}
static int max86915_fifo_read_data(struct max86915_device_data *data)
{
int ret = 0;
int num_samples;
int num_bytes;
int num_channel = 0;
u16 fd_settings = 0;
int i;
int j;
int offset1;
int offset2;
int index;
int samples[16] = {0, };
u8 *fifo_buf = data->fifo_buf;
int fifo_mode = -1;
if (data->enabled_mode == MODE_SDK_IR) {
num_samples = fifo_full_cnt;
num_channel = 4;
} else if (data->eol_test_is_enable == 1) {
num_samples = 20;
num_channel = data->num_samples;
} else {
num_samples = max86915_get_num_samples_in_fifo(data);
if (num_samples <= 0 || num_samples > MAX86915_FIFO_SIZE) {
ret = num_samples;
goto fail;
}
num_channel = max86915_get_fifo_settings(data, &fd_settings);
if (num_channel < 0) {
ret = num_channel;
goto fail;
}
}
num_bytes = num_channel * num_samples * NUM_BYTES_PER_SAMPLE;
ret = max86915_read_fifo(data, fifo_buf, num_bytes);
if (ret < 0)
goto fail;
/* clear the fifo_data buffer */
for (i = 0; i < MAX86915_FIFO_SIZE; i++)
for (j = 0; j < MAX_LED_NUM; j++)
data->fifo_data[j][i] = 0;
data->fifo_samples = 0;
for (i = 0; i < num_samples; i++) {
offset1 = i * NUM_BYTES_PER_SAMPLE * num_channel;
offset2 = 0;
for (j = 0; j < MAX_LED_NUM; j++) {
if (data->flex_mode & (1 << j)) {
index = offset1 + offset2;
samples[j] = ((int)fifo_buf[index + 0] << 16)
| ((int)fifo_buf[index + 1] << 8)
| ((int)fifo_buf[index + 2]);
samples[j] &= 0x7ffff;
data->fifo_data[j][i] = samples[j];
offset2 += NUM_BYTES_PER_SAMPLE;
}
}
}
data->fifo_samples = num_samples;
return ret;
fail:
HRM_err("%s - failed, ret: %d, fifo_mode: %d\n", __func__, ret, fifo_mode);
return ret;
}
static int max86915_fifo_irq_handler(struct max86915_device_data *data)
{
int ret;
ret = max86915_fifo_read_data(data);
if (ret < 0) {
HRM_dbg("%s fifo. ret: %d\n", __func__, ret);
return ret;
}
return ret;
}
static int max86915_eol_read_data(struct output_data *data, struct max86915_device_data *device, int *raw_data)
{
int err = 0;
int i = 0, j = 0;
int total_eol_cnt = (330 + (400 * 20)); /* maximnum count */
u8 recvData[MAX_LED_NUM * NUM_BYTES_PER_SAMPLE] = { 0x00, };
switch (device->eol_data.state) {
case _EOL_STATE_TYPE_NEW_FREQ_MODE:
case _EOL_STATE_TYPE_NEW_END:
case _EOL_STATE_TYPE_NEW_STOP:
case _EOL_STATE_TYPE_NEW_XTALK_MODE:
err = max86915_read_reg(device, MAX86915_FIFO_DATA, recvData,
device->num_samples * NUM_BYTES_PER_SAMPLE);
if (err != 0) {
HRM_err("%s - max86915_read_reg err:%d, address:0x%02x\n",
__func__, err, recvData[0]);
return -EIO;
}
for (i = 0; i < MAX_LED_NUM; i++) {
if (device->flex_mode & (1 << i)) {
raw_data[i] = recvData[j++] << 16 & 0x30000;
raw_data[i] += recvData[j++] << 8;
raw_data[i] += recvData[j++] << 0;
} else
raw_data[i] = 0;
}
device->sample_cnt++;
device->eol_data.eol_count++;
break;
default:
err = max86915_fifo_irq_handler(device);
if (err != 0) {
HRM_err("%s - max86915_fifo_irq_handler err:%d\n",
__func__, err);
return -EIO;
}
data->fifo_num = device->fifo_samples;
device->sample_cnt += device->fifo_samples;
device->eol_data.eol_count += device->fifo_samples;
break;
}
switch (device->eol_data.state) {
case _EOL_STATE_TYPE_NEW_INIT:
HRM_dbg("%s - EOL_STATE_TYPE_NEW_INIT\n", __func__);
data->fifo_num = 0; /* doesn't report the data */
err = 1;
if (device->eol_data.eol_count >= EOL_NEW_START_SKIP_CNT) {
device->eol_data.state = _EOL_STATE_TYPE_NEW_FLICKER_INIT;
device->eol_data.eol_count = 0;
}
break;
case _EOL_STATE_TYPE_NEW_FLICKER_INIT:
for (i = 0; i < device->fifo_samples; i++) {
data->fifo_main[AGC_IR][i] = device->fifo_data[AGC_IR][i];
data->fifo_main[AGC_RED][i] = 0;
device->eol_data.eol_flicker_data.max = 0;
}
if (device->eol_data.eol_count >= EOL_NEW_START_SKIP_CNT) {
HRM_dbg("%s - FINISH : _EOL_STATE_TYPE_NEW_FLICKER_INIT\n", __func__);
device->eol_data.state = _EOL_STATE_TYPE_NEW_FLICKER_MODE;
device->eol_data.eol_count = 0;
}
break;
case _EOL_STATE_TYPE_NEW_FLICKER_MODE:
for (i = 0; i < device->fifo_samples; i++) {
device->fifo_data[AGC_IR][i] = device->fifo_data[AGC_IR][i] >> 3;
data->fifo_main[AGC_IR][i] = device->fifo_data[AGC_IR][i];
data->fifo_main[AGC_RED][i] = 0;
max86915_eol_flicker_data(data->fifo_main[AGC_IR][i], &device->eol_data);
}
if (device->eol_data.eol_flicker_data.done == EOL_SUCCESS) {
HRM_dbg("%s - FINISH : _EOL_STATE_TYPE_NEW_FLICKER_MODE : %d, %d\n", __func__,
device->eol_data.eol_flicker_data.index, device->eol_data.eol_flicker_data.done);
if (device->pre_eol_test_is_enable == 3) /* PRE EOL for both */
max86915_enable_eol_freq(device);
else if (device->pre_eol_test_is_enable == 1) /* Finish PRE EOL */
max86915_eol_check_done(&device->eol_data, SELF_MODE_400HZ, device);
else
max86915_enable_eol_dc(device);
device->eol_data.eol_count = 0;
/* clear the remained datasheet after changing mode (interrupt TRIGGER mode). */
err = max86915_fifo_irq_handler(device);
if (err != 0) {
HRM_err("%s - max86915_fifo_irq_handler err:%d\n",
__func__, err);
return -EIO;
}
}
break;
case _EOL_STATE_TYPE_NEW_DC_MODE_MID:
for (i = 0; i < device->fifo_samples; i++) {
data->fifo_main[AGC_IR][i] = device->fifo_data[AGC_IR][i];
data->fifo_main[AGC_RED][i] = device->fifo_data[AGC_RED][i];
data->fifo_main[AGC_GREEN][i] = device->fifo_data[AGC_GREEN][i];
data->fifo_main[AGC_BLUE][i] = device->fifo_data[AGC_BLUE][i];
max86915_eol_hrm_data(data->fifo_main[AGC_IR][i], data->fifo_main[AGC_RED][i],
data->fifo_main[AGC_GREEN][i], data->fifo_main[AGC_BLUE][i], &device->eol_data,
EOL_NEW_MODE_DC_MID, EOL_NEW_DC_MIDDLE_SKIP_CNT, EOL_NEW_DC_MIDDLE_SIZE);
}
if (device->eol_data.eol_hrm_data[EOL_NEW_MODE_DC_MID].done == EOL_SUCCESS) {
HRM_dbg("%s - FINISH : _EOL_STATE_TYPE_NEW_DC_MODE_MID\n", __func__);
err = max86915_set_current(EOL_NEW_DC_HIGH_LED_IR_CURRENT, EOL_NEW_DC_HIGH_LED_RED_CURRENT,
EOL_NEW_DC_HIGH_LED_GREEN_CURRENT, EOL_NEW_DC_HIGH_LED_BLUE_CURRENT);
device->eol_data.state = _EOL_STATE_TYPE_NEW_DC_MODE_HIGH;
device->eol_data.eol_count = 0;
}
break;
case _EOL_STATE_TYPE_NEW_DC_MODE_HIGH:
for (i = 0; i < device->fifo_samples; i++) {
data->fifo_main[AGC_IR][i] = device->fifo_data[AGC_IR][i];
data->fifo_main[AGC_RED][i] = device->fifo_data[AGC_RED][i];
data->fifo_main[AGC_GREEN][i] = device->fifo_data[AGC_GREEN][i];
data->fifo_main[AGC_BLUE][i] = device->fifo_data[AGC_BLUE][i];
max86915_eol_hrm_data(data->fifo_main[AGC_IR][i], data->fifo_main[AGC_RED][i],
data->fifo_main[AGC_GREEN][i], data->fifo_main[AGC_BLUE][i], &device->eol_data,
EOL_NEW_MODE_DC_HIGH, EOL_NEW_DC_HIGH_SKIP_CNT, EOL_NEW_DC_HIGH_SIZE);
}
if (device->eol_data.eol_hrm_data[EOL_NEW_MODE_DC_HIGH].done == EOL_SUCCESS) {
HRM_dbg("%s - FINISH : _EOL_STATE_TYPE_NEW_DC_MODE_HIGH\n", __func__);
err = max86915_write_reg(device, MAX86915_DAC1_XTALK_CODE,
0x1f);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_DAC1_XTALK_CODE!\n",
__func__);
return -EIO;
}
err = max86915_write_reg(device, MAX86915_DAC2_XTALK_CODE,
0x1f);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_DAC2_XTALK_CODE!\n",
__func__);
return -EIO;
}
err = max86915_write_reg(device, MAX86915_DAC3_XTALK_CODE,
0x1f);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_DAC3_XTALK_CODE!\n",
__func__);
return -EIO;
}
err = max86915_write_reg(device, MAX86915_DAC4_XTALK_CODE,
0x1f);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_DAC4_XTALK_CODE!\n",
__func__);
return -EIO;
}
device->eol_data.state = _EOL_STATE_TYPE_NEW_DC_XTALK;
device->eol_data.eol_count = 0;
}
break;
case _EOL_STATE_TYPE_NEW_DC_XTALK:
for (i = 0; i < device->fifo_samples; i++) {
data->fifo_main[AGC_IR][i] = device->fifo_data[AGC_IR][i];
data->fifo_main[AGC_RED][i] = device->fifo_data[AGC_RED][i];
data->fifo_main[AGC_GREEN][i] = device->fifo_data[AGC_GREEN][i];
data->fifo_main[AGC_BLUE][i] = device->fifo_data[AGC_BLUE][i];
max86915_eol_hrm_data(data->fifo_main[AGC_IR][i], data->fifo_main[AGC_RED][i],
data->fifo_main[AGC_GREEN][i], data->fifo_main[AGC_BLUE][i], &device->eol_data,
EOL_NEW_MODE_DC_XTALK, EOL_NEW_DC_XTALK_SKIP_CNT, EOL_NEW_DC_XTALK_SIZE);
}
if (device->eol_data.eol_hrm_data[EOL_NEW_MODE_DC_XTALK].done == EOL_SUCCESS) {
HRM_dbg("%s - FINISH : _EOL_STATE_TYPE_NEW_DC_XTALK\n", __func__);
max86915_enable_eol_freq(device);
device->eol_data.eol_count = 0;
}
break;
case _EOL_STATE_TYPE_NEW_FREQ_MODE:
if (device->eol_data.eol_freq_data.done != EOL_SUCCESS) {
max86915_eol_freq_data(&device->eol_data, SELF_DIVID_100HZ);
max86915_eol_check_done(&device->eol_data, SELF_MODE_400HZ, device);
}
if (!(device->eol_test_status) && device->sample_cnt >= total_eol_cnt)
device->eol_data.state = _EOL_STATE_TYPE_NEW_END;
break;
case _EOL_STATE_TYPE_NEW_END:
HRM_err("%s - FAIL EOL TEST : HR_STATE: _EOL_STATE_TYPE_NEW_END !!!\n", __func__);
max86915_eol_check_done(&device->eol_data, SELF_MODE_400HZ, device);
device->eol_data.state = _EOL_STATE_TYPE_NEW_STOP;
break;
case _EOL_STATE_TYPE_NEW_STOP:
max86915_eol_set_mode(device, PWR_OFF);
device->pre_eol_test_is_enable = 0;
device->enabled_mode = MODE_NONE;
break;
case _EOL_STATE_TYPE_NEW_XTALK_MODE:
max86915_eol_xtalk_data(raw_data[0], &device->eol_data);
if (device->eol_data.eol_xtalk_data.done == EOL_SUCCESS) {
HRM_dbg("%s FINISH : _EOL_STATE_TYPE_XTALK_MODE\n", __func__);
max86915_eol_check_done(&device->eol_data, SELF_MODE_400HZ, device);
device->eol_data.state = _EOL_STATE_TYPE_NEW_STOP;
device->eol_data.eol_count = 0;
}
break;
default:
break;
}
return err;
}
static int max86915_hrm_read_data(struct max86915_device_data *device, int *data)
{
int err;
u8 recvData[MAX_LED_NUM * NUM_BYTES_PER_SAMPLE] = { 0x00, };
int i, j = 0;
int ret = 0;
if (device->sample_cnt == MAX86915_COUNT_MAX)
device->sample_cnt = 0;
err = max86915_read_reg(device, MAX86915_FIFO_DATA, recvData,
device->num_samples * NUM_BYTES_PER_SAMPLE);
if (err != 0) {
HRM_err("%s - max86915_read_reg err:%d, address:0x%02x\n",
__func__, err, recvData[0]);
return -EIO;
}
for (i = 0; i < MAX_LED_NUM; i++) {
if (device->flex_mode & (1 << i)) {
data[i] = recvData[j++] << 16 & 0x70000;
data[i] += recvData[j++] << 8;
data[i] += recvData[j++] << 0;
} else
data[i] = 0;
}
if ((device->sample_cnt % 1000) == 0)
HRM_info("%s - %u, %u, %u, %u\n", __func__,
data[0], data[1], data[2], data[3]);
if (device->agc_led_set == 0) {
device->agc_ch_adc[0] = data[0];
device->agc_ch_adc[1] = data[1];
device->agc_ch_adc[2] = data[2];
device->agc_ch_adc[3] = data[3];
device->sample_cnt++;
} else {
data[0] = device->agc_ch_adc[0];
data[1] = device->agc_ch_adc[1];
data[2] = device->agc_ch_adc[2];
data[3] = device->agc_ch_adc[3];
}
return ret;
}
static int max86915_awb_flicker_read_data(struct max86915_device_data *device, int *data)
{
int err;
u8 *recvData = device->fifo_buf;
int ret = 0;
int mode_changed = 0;
int i;
u8 irq_status = 0;
int previous_awb_data = 0;
err = max86915_read_reg(device, MAX86915_INTERRUPT_STATUS, recvData, 1);
if (err != 0) {
HRM_err("%s - max86915_read_reg err:%d, address:0x%02x\n",
__func__, err, recvData[0]);
return -EIO;
}
irq_status = recvData[0];
if (irq_status != 0x80) {
HRM_err("%s - irq_status 0x%x, return 1\n", __func__, irq_status);
return 1;
}
err = max86915_read_reg(device, MAX86915_FIFO_DATA, recvData,
AWB_INTERVAL * NUM_BYTES_PER_SAMPLE);
if (err != 0) {
HRM_err("%s - max86915_read_reg err:%d, address:0x%02x\n",
__func__, err, recvData[0]);
return -EIO;
}
*data = (recvData[0 + (AWB_INTERVAL - 1)*NUM_BYTES_PER_SAMPLE] << 16 & 0x70000)
+ (recvData[1 + (AWB_INTERVAL - 1)*NUM_BYTES_PER_SAMPLE] << 8)
+ (recvData[2 + (AWB_INTERVAL - 1)*NUM_BYTES_PER_SAMPLE] << 0);
previous_awb_data = (recvData[0 + (AWB_INTERVAL - 2)*NUM_BYTES_PER_SAMPLE] << 16 & 0x70000)
+ (recvData[1 + (AWB_INTERVAL - 2)*NUM_BYTES_PER_SAMPLE] << 8)
+ (recvData[2 + (AWB_INTERVAL - 2)*NUM_BYTES_PER_SAMPLE] << 0);
if (device->awb_sample_cnt > CONFIG_SKIP_CNT) {
mutex_lock(&device->flickerdatalock);
for (i = 0; i < AWB_INTERVAL; i++) {
if (device->flicker_data_cnt < FLICKER_DATA_CNT) {
device->flicker_data[device->flicker_data_cnt++] =
(recvData[0 + i*NUM_BYTES_PER_SAMPLE] << 16 & 0x70000)
+ (recvData[1 + i*NUM_BYTES_PER_SAMPLE] << 8)
+ (recvData[2 + i*NUM_BYTES_PER_SAMPLE] << 0);
}
}
mutex_unlock(&device->flickerdatalock);
}
/* Change Configuation */
if (device->awb_flicker_status == AWB_CONFIG1) {
if (*data > AWB_MAX86915_CONFIG_TH2
&& previous_awb_data > AWB_MAX86915_CONFIG_TH2) { /* Change to AWB_CONFIG2 */
err = max86915_write_reg(device, MAX86915_MODE_CONFIGURATION, 0x00);
/* 16384 uA setting */
err = max86915_write_reg(device,
MAX86915_MODE_CONFIGURATION_2, 0x6F);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_MODE_CONFIGURATION_2!\n",
__func__);
return -EIO;
}
device->awb_flicker_status = AWB_CONFIG2;
mode_changed = 1;
}
} else if (device->awb_flicker_status == AWB_CONFIG2) {
if (*data < AWB_MAX86915_CONFIG_TH3
&& previous_awb_data < AWB_MAX86915_CONFIG_TH3) { /* Change to AWB_CONFIG1 */
err = max86915_write_reg(device, MAX86915_MODE_CONFIGURATION, 0x00);
/* 4096 uA setting */
err = max86915_write_reg(device,
MAX86915_MODE_CONFIGURATION_2, 0x0F);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_MODE_CONFIGURATION_2!\n",
__func__);
return -EIO;
}
device->awb_flicker_status = AWB_CONFIG1;
mode_changed = 1;
}
}
if (device->awb_sample_cnt > CONFIG_SKIP_CNT) {
if (device->awb_flicker_status < AWB_CONFIG2)
*data = *data >> 3; /* 2uA setting should devided by 8 */
ret = 0;
} else {
ret = 1;
HRM_dbg("%s - awb sample cnt %d, less than CONFIG SKIP CNT\n", __func__,
device->awb_sample_cnt);
}
if (mode_changed == 1) {
/* Flush Buffer */
err = max86915_write_reg(device,
MAX86915_MODE_CONFIGURATION, 0x07);
if (err != 0) {
HRM_err("%s - error init MAX86915_MODE_CONFIGURATION!\n",
__func__);
return -EIO;
}
device->flicker_data_cnt = 0;
device->awb_sample_cnt = 0;
ret = 1;
HRM_dbg("%s - mode changed to : %d\n", __func__, device->awb_flicker_status);
} else
device->awb_sample_cnt += AWB_INTERVAL;
return ret;
}
static long max86915_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
void __user *argp = (void __user *)arg;
int ret = 0;
struct max86915_device_data *data = container_of(file->private_data,
struct max86915_device_data, miscdev);
HRM_info("%s - ioctl start\n", __func__);
mutex_lock(&data->flickerdatalock);
switch (cmd) {
case MAX86915_IOCTL_READ_FLICKER:
ret = copy_to_user(argp,
data->flicker_data,
sizeof(int)*FLICKER_DATA_CNT);
if (unlikely(ret))
goto ioctl_error;
break;
default:
HRM_err("%s - invalid cmd\n", __func__);
break;
}
mutex_unlock(&data->flickerdatalock);
return ret;
ioctl_error:
mutex_unlock(&data->flickerdatalock);
HRM_err("%s - read flicker data err(%d)\n", __func__, ret);
return -ret;
}
static const struct file_operations max86915_fops = {
.owner = THIS_MODULE,
.open = nonseekable_open,
.unlocked_ioctl = max86915_ioctl,
};
static int max86915_read_data(struct output_data *data)
{
int err = 0;
int ret;
int raw_data[4] = {0x00, };
u8 recvData;
int i = 0;
int j = 0;
int data_fifo[MAX_LED_NUM][MAX86915_FIFO_SIZE];
data->sub_num = 0;
data->fifo_num = 0;
for (i = 0; i < MAX86915_FIFO_SIZE; i++) {
for (j = 0; j < MAX_LED_NUM; j++) {
data_fifo[j][i] = 0;
data->fifo_main[j][i] = 0;
}
}
switch (max86915_data->enabled_mode) {
case MODE_HRM:
#ifndef ENABLE_POLL_DELAY
case MODE_SDK_IR:
#endif
case MODE_PROX:
if (max86915_data->eol_test_is_enable)
err = max86915_eol_read_data(data, max86915_data, raw_data);
else
err = max86915_hrm_read_data(max86915_data, raw_data);
break;
case MODE_AMBIENT:
err = max86915_awb_flicker_read_data(max86915_data, raw_data);
break;
#ifdef ENABLE_POLL_DELAY
case MODE_SDK_IR:
err = max86915_fifo_irq_handler(max86915_data);
#ifdef CONFIG_4CH_IOCTL
mutex_lock(&max86915_data->flickerdatalock);
for (i = 0; i < max86915_data->fifo_samples; i++) {
if (max86915_data->agc_led_set > 0) {
max86915_data->flicker_data[i*4+0] = max86915_data->agc_ch_adc[0];
max86915_data->flicker_data[i*4+1] = max86915_data->agc_ch_adc[1];
max86915_data->flicker_data[i*4+2] = max86915_data->agc_ch_adc[2];
max86915_data->flicker_data[i*4+3] = max86915_data->agc_ch_adc[3];
} else {
max86915_data->flicker_data[i*4+0] = max86915_data->fifo_data[0][i];
max86915_data->flicker_data[i*4+1] = max86915_data->fifo_data[1][i];
max86915_data->flicker_data[i*4+2] = max86915_data->fifo_data[2][i];
max86915_data->flicker_data[i*4+3] = max86915_data->fifo_data[3][i];
max86915_data->agc_ch_adc[0] = max86915_data->fifo_data[0][max86915_data->fifo_samples-1];
max86915_data->agc_ch_adc[1] = max86915_data->fifo_data[1][max86915_data->fifo_samples-1];
max86915_data->agc_ch_adc[2] = max86915_data->fifo_data[2][max86915_data->fifo_samples-1];
max86915_data->agc_ch_adc[3] = max86915_data->fifo_data[3][max86915_data->fifo_samples-1];
}
}
mutex_unlock(&max86915_data->flickerdatalock);
#endif
#ifdef CONFIG_4CH_INPUT
for (i = 0; i < max86915_data->fifo_samples; i++) {
if (max86915_data->agc_led_set > 0) {
data->fifo_main[AGC_IR][i] = max86915_data->agc_ch_adc[0];
data->fifo_main[AGC_RED][i] = max86915_data->agc_ch_adc[1];
data->fifo_main[AGC_GREEN][i] = max86915_data->agc_ch_adc[2];
data->fifo_main[AGC_BLUE][i] = max86915_data->agc_ch_adc[3];
} else {
data->fifo_main[AGC_IR][i] = max86915_data->fifo_data[0][i];
data->fifo_main[AGC_RED][i] = max86915_data->fifo_data[1][i];
data->fifo_main[AGC_GREEN][i] = max86915_data->fifo_data[2][i];
data->fifo_main[AGC_BLUE][i] = max86915_data->fifo_data[3][i];
max86915_data->agc_ch_adc[0] = max86915_data->fifo_data[0][max86915_data->fifo_samples-1];
max86915_data->agc_ch_adc[1] = max86915_data->fifo_data[1][max86915_data->fifo_samples-1];
max86915_data->agc_ch_adc[2] = max86915_data->fifo_data[2][max86915_data->fifo_samples-1];
max86915_data->agc_ch_adc[3] = max86915_data->fifo_data[3][max86915_data->fifo_samples-1];
}
}
data->fifo_num = fifo_full_cnt;
#endif
if (max86915_data->agc_led_set == 0) /* for AGC */
max86915_data->sample_cnt += max86915_data->fifo_samples;
break;
#endif
default:
err = 1; /* error case */
break;
}
if (err < 0)
HRM_err("%s - read_data err : %d\n", __func__, err);
if (err == 0) {
data->main_num = 2;
if (max86915_data->enabled_mode == MODE_AMBIENT) {
data->data_main[0] = raw_data[0];
if (max86915_data->flicker_data_cnt == FLICKER_DATA_CNT) {
data->data_main[1] = -2;
max86915_data->flicker_data_cnt = 0;
} else {
data->data_main[1] = 0;
}
#ifndef ENABLE_POLL_DELAY
} else if (max86915_data->enabled_mode == MODE_SDK_IR) {
data->main_num = 4;
data->data_main[0] = raw_data[0];
data->data_main[1] = raw_data[1];
data->data_main[2] = raw_data[2];
data->data_main[3] = raw_data[3];
#else
} else if (max86915_data->enabled_mode == MODE_SDK_IR) {
#ifdef CONFIG_4CH_IOCTL
data->data_main[1] = -3;
#endif
#ifdef CONFIG_4CH_INPUT
data->data_main[1] = -3;
raw_data[0] = data->fifo_main[AGC_IR][0];
raw_data[1] = data->fifo_main[AGC_RED][0];
raw_data[2] = data->fifo_main[AGC_GREEN][0];
raw_data[3] = data->fifo_main[AGC_BLUE][0];
#endif
#endif
} else if (max86915_data->enabled_mode == MODE_HRM) {
data->main_num = 2;
data->data_main[0] = raw_data[0];
data->data_main[1] = raw_data[1];
#ifdef CONFIG_HRM_GREEN_BLUE
data->main_num = 4;
data->data_main[2] = raw_data[2];
data->data_main[3] = raw_data[3];
#endif
} else {
data->main_num = 2;
data->data_main[0] = raw_data[0];
data->data_main[1] = raw_data[1];
}
data->mode = max86915_data->enabled_mode;
ret = 0;
} else
ret = 1;
/* Interrupt Clear */
err = max86915_read_reg(max86915_data, MAX86915_INTERRUPT_STATUS, &recvData, 1);
if (err != 0) {
HRM_err("%s - max86915_read_reg err:%d, address:0x%02x\n",
__func__, err, recvData);
return -EIO;
}
if (max86915_data->agc_enabled == 0 && max86915_data->enabled_mode != MODE_AMBIENT) {
if (max86915_data->sample_cnt > AGC_SKIP_CNT)
max86915_data->agc_enabled = 1;
else {
data->main_num = 0;
data->fifo_num = 0;
}
}
if (max86915_data->agc_mode != M_NONE
&& agc_debug_enabled
&& max86915_data->agc_enabled
&& (max86915_data->agc_led_set == 0)) {
max86915_cal_agc(raw_data[0], raw_data[1], raw_data[2], raw_data[3]);
}
if (max86915_data->agc_led_set > 0) {
if (max86915_data->enabled_mode == MODE_SDK_IR) {
if (max86915_data->agc_led_set < fifo_full_cnt)
max86915_data->agc_led_set = 0;
else
max86915_data->agc_led_set -= fifo_full_cnt;
} else {
max86915_data->agc_led_set--;
}
}
return ret;
}
static int max86915_init_hrm_reg(struct max86915_device_data *data)
{
int err, i;
u8 flex_config[2] = {0, };
data->sample_cnt = 0;
data->agc_led_set = 0;
data->agc_ch_adc[0] = 0;
data->agc_ch_adc[1] = 0;
data->agc_ch_adc[2] = 0;
data->agc_ch_adc[3] = 0;
flex_config[0] = (RED_LED_CH << MAX86915_LED2_OFFSET) | IR_LED_CH;
flex_config[1] = 0x00;
#ifdef CONFIG_HRM_GREEN_BLUE
flex_config[1] = (BLUE_LED_CH << MAX86915_LED4_OFFSET) | GREEN_LED_CH;
#endif
data->num_samples = 2;
data->flex_mode = 3;
#ifdef CONFIG_HRM_GREEN_BLUE
data->num_samples = 4;
data->flex_mode = 0xf;
#endif
data->led_current1 = data->init_current[AGC_IR];
data->led_current2 = MAX86915_HRM_DEFAULT_CURRENT2;
#ifdef CONFIG_HRM_GREEN_BLUE
data->led_current3 = MAX86915_HRM_DEFAULT_CURRENT3;
data->led_current4 = MAX86915_HRM_DEFAULT_CURRENT4;
#endif
data->xtalk_code1 = MAX86915_HRM_DEFAULT_XTALK_CODE1;
data->xtalk_code2 = MAX86915_HRM_DEFAULT_XTALK_CODE2;
#ifdef CONFIG_HRM_GREEN_BLUE
data->xtalk_code3 = MAX86915_HRM_DEFAULT_XTALK_CODE3;
data->xtalk_code4 = MAX86915_HRM_DEFAULT_XTALK_CODE4;
#endif
HRM_info("%s - flexmode : 0x%02x, num_samples : %d\n", __func__,
data->flex_mode, data->num_samples);
/*write LED currents ir=1, red=2, violet=4*/
err = max86915_write_reg(data, MAX86915_LED1_PA,
data->led_current1);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_LED1_PA!\n",
__func__);
return -EIO;
}
err = max86915_write_reg(data, MAX86915_LED2_PA,
data->led_current2);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_LED2_PA!\n",
__func__);
return -EIO;
}
#ifdef CONFIG_HRM_GREEN_BLUE
err = max86915_write_reg(data, MAX86915_LED3_PA,
data->led_current3);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_LED3_PA!\n",
__func__);
return -EIO;
}
err = max86915_write_reg(data, MAX86915_LED4_PA,
data->led_current4);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_LED4_PA!\n",
__func__);
return -EIO;
}
#endif
/* LED Range */
err = max86915_write_reg(data, MAX86915_LED_RANGE,
(data->led_range[0] << MAX86915_LED1_RGE_OFFSET)
| (data->led_range[1] << MAX86915_LED2_RGE_OFFSET)
| (data->led_range[2] << MAX86915_LED3_RGE_OFFSET)
| (data->led_range[3] << MAX86915_LED4_RGE_OFFSET));
if (err != 0) {
HRM_err("%s - error initializing MAX86915_LED_RANGE!\n",
__func__);
return -EIO;
}
/* XTALK */
err = max86915_write_reg(data, MAX86915_DAC1_XTALK_CODE,
data->xtalk_code1);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_DAC1_XTALK_CODE!\n",
__func__);
return -EIO;
}
err = max86915_write_reg(data, MAX86915_DAC2_XTALK_CODE,
data->xtalk_code2);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_DAC2_XTALK_CODE!\n",
__func__);
return -EIO;
}
#ifdef CONFIG_HRM_GREEN_BLUE
err = max86915_write_reg(data, MAX86915_DAC3_XTALK_CODE,
data->xtalk_code3);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_DAC3_XTALK_CODE!\n",
__func__);
return -EIO;
}
err = max86915_write_reg(data, MAX86915_DAC4_XTALK_CODE,
data->xtalk_code4);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_DAC4_XTALK_CODE!\n",
__func__);
return -EIO;
}
#endif
err = max86915_write_reg(data, MAX86915_INTERRUPT_ENABLE, PPG_RDY_MASK);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_INTERRUPT_ENABLE!\n",
__func__);
return -EIO;
}
err = max86915_write_reg(data, MAX86915_LED_SEQ_REG_1,
flex_config[0]);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_LED_SEQ_REG_1!\n",
__func__);
return -EIO;
}
err = max86915_write_reg(data, MAX86915_LED_SEQ_REG_2,
flex_config[1]);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_LED_SEQ_REG_2!\n",
__func__);
return -EIO;
}
/* 400 Hz Sampling Rate */
err = max86915_write_reg(data, MAX86915_MODE_CONFIGURATION_2,
0x0D | (0x03 << MAX86915_ADC_RGE_OFFSET));
if (err != 0) {
HRM_err("%s - error initializing MAX86915_MODE_CONFIGURATION_2!\n",
__func__);
return -EIO;
}
err = max86915_write_reg(data, MAX86915_FIFO_CONFIG,
(0x02 << MAX86915_SMP_AVE_OFFSET) & MAX86915_SMP_AVE_MASK);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_FIFO_CONFIG!\n",
__func__);
return -EIO;
}
err = max86915_write_reg(data, MAX86915_MODE_CONFIGURATION, 0x03);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_MODE_CONFIGURATION!\n",
__func__);
return -EIO;
}
/* AGC control */
data->agc_current[AGC_IR] =
(data->led_current1 * MAX86915_CURRENT_PER_STEP(data->led_range[0]));
data->agc_current[AGC_RED] =
(data->led_current2 * MAX86915_CURRENT_PER_STEP(data->led_range[1]));
#ifdef CONFIG_HRM_GREEN_BLUE
data->agc_current[AGC_GREEN] =
(data->led_current3 * MAX86915_CURRENT_PER_STEP(data->led_range[2]));
data->agc_current[AGC_BLUE] =
(data->led_current4 * MAX86915_CURRENT_PER_STEP(data->led_range[3]));
#endif
for (i = 0; i < data->num_samples; i++) {
data->reached_thresh[i] = 0;
data->agc_sum[i] = 0;
data->prev_current[i] = 0;
data->prev_agc_current[i] = MAX86915_MAX_CURRENT(data->led_range[i]);
data->prev_ppg[i] = 0;
}
return 0;
}
static int max86915_init_sdk_reg(struct max86915_device_data *data)
{
int err, i;
u8 flex_config[2] = {0, };
data->sample_cnt = 0;
data->agc_led_set = 0;
data->agc_ch_adc[0] = 0;
data->agc_ch_adc[1] = 0;
data->agc_ch_adc[2] = 0;
data->agc_ch_adc[3] = 0;
data->num_samples = 4;
data->flex_mode = 0xf;
flex_config[0] = (RED_LED_CH << MAX86915_LED2_OFFSET) | IR_LED_CH;
flex_config[1] = (BLUE_LED_CH << MAX86915_LED4_OFFSET) | GREEN_LED_CH;
data->led_current1 = MAX86915_MIN_CURRENT;
data->led_current2 = MAX86915_MIN_CURRENT;
data->led_current3 = MAX86915_MIN_CURRENT;
data->led_current4 = MAX86915_MIN_CURRENT;
/*
* data->led_current1 = (data->mode_sdk_enabled & (1<<0)) ? 0xff : 0x00;
* data->led_current2 = (data->mode_sdk_enabled & (1<<1)) ? 0xff : 0x00;
* data->led_current3 = (data->mode_sdk_enabled & (1<<2)) ? 0xff : 0x00;
* data->led_current4 = (data->mode_sdk_enabled & (1<<3)) ? 0xff : 0x00;
* data->xtalk_code1 = MAX86915_DEFAULT_XTALK_CODE1;
* data->xtalk_code2 = MAX86915_DEFAULT_XTALK_CODE2;
* data->xtalk_code3 = MAX86915_DEFAULT_XTALK_CODE3;
* data->xtalk_code4 = MAX86915_DEFAULT_XTALK_CODE4;
*/
HRM_info("%s - flexmode : 0x%02x, num_samples : %d\n", __func__,
data->flex_mode, data->num_samples);
/* write LED currents ir=1, red=2, violet=4 */
err = max86915_write_reg(data, MAX86915_LED1_PA,
data->led_current1);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_LED1_PA!\n",
__func__);
return -EIO;
}
err = max86915_write_reg(data, MAX86915_LED2_PA,
data->led_current2);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_LED2_PA!\n",
__func__);
return -EIO;
}
err = max86915_write_reg(data, MAX86915_LED3_PA,
data->led_current3);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_LED3_PA!\n",
__func__);
return -EIO;
}
err = max86915_write_reg(data, MAX86915_LED4_PA,
data->led_current4);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_LED4_PA!\n",
__func__);
return -EIO;
}
/* LED Range */
err = max86915_write_reg(data, MAX86915_LED_RANGE,
(data->led_range[0] << MAX86915_LED1_RGE_OFFSET)
| (data->led_range[1] << MAX86915_LED2_RGE_OFFSET)
| (data->led_range[2] << MAX86915_LED3_RGE_OFFSET)
| (data->led_range[3] << MAX86915_LED4_RGE_OFFSET));
if (err != 0) {
HRM_err("%s - error initializing MAX86915_LED_RANGE!\n",
__func__);
return -EIO;
}
/* XTALK */
err = max86915_write_reg(data, MAX86915_DAC1_XTALK_CODE,
data->xtalk_code1);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_DAC1_XTALK_CODE!\n",
__func__);
return -EIO;
}
err = max86915_write_reg(data, MAX86915_DAC2_XTALK_CODE,
data->xtalk_code2);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_DAC2_XTALK_CODE!\n",
__func__);
return -EIO;
}
err = max86915_write_reg(data, MAX86915_DAC3_XTALK_CODE,
data->xtalk_code3);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_DAC3_XTALK_CODE!\n",
__func__);
return -EIO;
}
err = max86915_write_reg(data, MAX86915_DAC4_XTALK_CODE,
data->xtalk_code4);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_DAC4_XTALK_CODE!\n",
__func__);
return -EIO;
}
err = max86915_write_reg(data, MAX86915_INTERRUPT_ENABLE, PPG_RDY_MASK);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_INTERRUPT_ENABLE!\n",
__func__);
return -EIO;
}
err = max86915_write_reg(data, MAX86915_LED_SEQ_REG_1,
flex_config[0]);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_LED_SEQ_REG_1!\n",
__func__);
return -EIO;
}
err = max86915_write_reg(data, MAX86915_LED_SEQ_REG_2,
flex_config[1]);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_LED_SEQ_REG_2!\n",
__func__);
return -EIO;
}
#ifdef ENABLE_POLL_DELAY
if (fifo_full_cnt == 8) {
/* 16uA, 800Hz, 70us */
err = max86915_write_reg(data, MAX86915_MODE_CONFIGURATION_2,
0x50);
/* 1 Samples avg */
err = max86915_write_reg(data, MAX86915_FIFO_CONFIG,
0x1f);
} else if (fifo_full_cnt == 4) {
/* 16uA, 400Hz, 120us */
err = max86915_write_reg(data, MAX86915_MODE_CONFIGURATION_2,
0x50);
/* 2 Samples avg */
err = max86915_write_reg(data, MAX86915_FIFO_CONFIG,
0x3f);
} else if (fifo_full_cnt == 2) {
/* 16uA, 200Hz, 120us */
err = max86915_write_reg(data, MAX86915_MODE_CONFIGURATION_2,
0x50);
/* 4 Samples avg */
err = max86915_write_reg(data, MAX86915_FIFO_CONFIG,
0x5f);
} else if (fifo_full_cnt == 1) {
/* 16uA, 400Hz, 120us */
err = max86915_write_reg(data, MAX86915_MODE_CONFIGURATION_2,
0x4D);
/* 4 Samples avg */
err = max86915_write_reg(data, MAX86915_FIFO_CONFIG,
0x5f);
}
if (err != 0) {
HRM_err("%s - error initializing MAX86915_MODE_CONFIGURATION_2!\n",
__func__);
HRM_err("%s - error initializing MAX86915_FIFO_CONFIG!\n",
__func__);
return -EIO;
}
#else
/* 400 Hz Sampling Rate & 120us Pulse Width */
err = max86915_write_reg(data, MAX86915_MODE_CONFIGURATION_2,
0x0D | (0x03 << MAX86915_ADC_RGE_OFFSET));
if (err != 0) {
HRM_err("%s - error initializing MAX86915_MODE_CONFIGURATION_2!\n",
__func__);
return -EIO;
}
err = max86915_write_reg(data, MAX86915_FIFO_CONFIG,
(0x02 << MAX86915_SMP_AVE_OFFSET) & MAX86915_SMP_AVE_MASK);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_FIFO_CONFIG!\n",
__func__);
return -EIO;
}
#endif
err = max86915_write_reg(data, MAX86915_MODE_CONFIGURATION, 0x03);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_MODE_CONFIGURATION!\n",
__func__);
return -EIO;
}
/* AGC control */
data->agc_current[AGC_IR] =
(data->led_current1 * MAX86915_CURRENT_PER_STEP(data->led_range[0]));
data->agc_current[AGC_RED] =
(data->led_current2 * MAX86915_CURRENT_PER_STEP(data->led_range[1]));
data->agc_current[AGC_GREEN] =
(data->led_current3 * MAX86915_CURRENT_PER_STEP(data->led_range[2]));
data->agc_current[AGC_BLUE] =
(data->led_current4 * MAX86915_CURRENT_PER_STEP(data->led_range[3]));
for (i = 0; i < data->num_samples; i++) {
data->reached_thresh[i] = 0;
data->agc_sum[i] = 0;
data->prev_current[i] = 0;
data->prev_agc_current[i] = MAX86915_MAX_CURRENT(data->led_range[i]);
data->prev_ppg[i] = 0;
}
return 0;
}
static int max86915_set_reg_hrm(struct max86915_device_data *data)
{
int err = 0;
err = max86915_init_status(data);
err = max86915_init_hrm_reg(data);
data->agc_mode = M_HRM;
return err;
}
static int max86915_set_reg_sdk(struct max86915_device_data *data)
{
int err = 0;
err = max86915_init_status(data);
err = max86915_init_sdk_reg(data);
data->agc_mode = M_SDK;
data->agc_enabled = 1;
return err;
}
static int max86915_set_reg_svc(struct max86915_device_data *data)
{
int err = 0;
err = max86915_init_status(data);
err = max86915_init_sdk_reg(data);
data->agc_mode = M_NONE;
err = max86915_write_reg(data, MAX86915_INTERRUPT_ENABLE, 0);
err = max86915_write_reg(data, MAX86915_MODE_CONFIGURATION_2, 0x44);
return err;
}
static int max86915_set_reg_ambient(struct max86915_device_data *data)
{
int err = 0;
u8 recvData = 0;
err = max86915_init_status(data);
err = max86915_init_hrm_reg(data);
/* Mode change to AWB */
err = max86915_write_reg(data,
MAX86915_MODE_CONFIGURATION, MAX86915_RESET_MASK);
if (err != 0) {
HRM_err("%s - error sw shutdown data!\n", __func__);
return -EIO;
}
/* Interrupt Clear */
err = max86915_read_reg(data, MAX86915_INTERRUPT_STATUS, &recvData, 1);
if (err != 0) {
HRM_err("%s - max86915_read_reg err:%d, address:0x%02x\n",
__func__, err, recvData);
return -EIO;
}
/* Interrupt2 Clear */
err = max86915_read_reg(data, MAX86915_INTERRUPT_STATUS_2, &recvData, 1);
if (err != 0) {
HRM_err("%s - max86915_read_reg err:%d, address:0x%02x\n",
__func__, err, recvData);
return -EIO;
}
/* 400Hz, LED_PW=400us, SPO2_ADC_RANGE=4096nA */
err = max86915_write_reg(data,
MAX86915_MODE_CONFIGURATION_2, 0x0F);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_MODE_CONFIGURATION_2!\n",
__func__);
return -EIO;
}
err = max86915_write_reg(data,
MAX86915_FIFO_CONFIG, ((32 - AWB_INTERVAL) & MAX86915_FIFO_A_FULL_MASK));
if (err != 0) {
HRM_err("%s - error initializing MAX86915_FIFO_CONFIG!\n",
__func__);
return -EIO;
}
err = max86915_write_reg(data,
MAX86915_INTERRUPT_ENABLE, A_FULL_MASK);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_INTERRUPT_ENABLE!\n",
__func__);
return -EIO;
}
err = max86915_write_reg(data, MAX86915_LED_SEQ_REG_1,
0x01);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_LED_SEQ_REG_1!\n",
__func__);
return -EIO;
}
err = max86915_write_reg(data,
MAX86915_MODE_CONFIGURATION, 0x07);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_MODE_CONFIGURATION!\n",
__func__);
return -EIO;
}
data->awb_sample_cnt = 0;
data->flicker_data_cnt = 0;
data->awb_flicker_status = AWB_CONFIG1;
data->agc_mode = M_NONE;
return err;
}
static int max86915_set_reg_prox(struct max86915_device_data *data)
{
int err = 0;
err = max86915_init_status(data);
err = max86915_init_hrm_reg(data);
max86915_set_current(0xff, 0, 0, 0);
data->agc_mode = M_NONE;
return err;
}
static int max86915_enable(struct max86915_device_data *data, enum op_mode mode)
{
int err = 0;
HRM_dbg("%s - enable_m : %d cur_m : %d", __func__, mode, max86915_data->enabled_mode);
if (data->hrm_threshold != DEFAULT_THRESHOLD) {
if (data->threshold_default != data->hrm_threshold)
data->threshold_default = data->hrm_threshold;
} else
data->hrm_threshold = data->threshold_default;
if (mode == MODE_HRM)
err = max86915_set_reg_hrm(max86915_data);
else if (mode == MODE_AMBIENT)
err = max86915_set_reg_ambient(max86915_data);
else if (mode == MODE_PROX)
err = max86915_set_reg_prox(max86915_data);
else if (mode == MODE_SDK_IR)
err = max86915_set_reg_sdk(max86915_data);
else if (mode == MODE_SVC_IR)
err = max86915_set_reg_svc(max86915_data);
else
HRM_err("%s - MODE_UNKNOWN\n", __func__);
max86915_data->agc_sample_cnt[0] = 0;
max86915_data->agc_sample_cnt[1] = 0;
max86915_data->agc_sample_cnt[2] = 0;
max86915_data->agc_sample_cnt[3] = 0;
if (err < 0) {
HRM_err("%s - fail err = %d\n", __func__, err);
return err;
}
return 0;
}
static int max86915_disable(struct max86915_device_data *data, enum op_mode mode)
{
int err = 0;
HRM_dbg("%s - disable_m : %d cur_m : %d\n", __func__, mode, max86915_data->enabled_mode);
if (mode == 0) {
err = max86915_write_reg(data, MAX86915_MODE_CONFIGURATION, MAX86915_RESET_MASK);
if (err != 0) {
HRM_err("%s - error init MAX86915_MODE_CONFIGURATION!\n",
__func__);
return -EIO;
}
err = max86915_write_reg(data, MAX86915_MODE_CONFIGURATION, MAX86915_SHDN_MASK);
if (err != 0) {
HRM_err("%s - error init MAX86915_MODE_CONFIGURATION!\n",
__func__);
return -EIO;
}
data->awb_sample_cnt = 0;
data->flicker_data_cnt = 0;
data->led_current1 = 0;
data->led_current2 = 0;
data->led_current3 = 0;
data->led_current4 = 0;
}
if (err < 0) {
HRM_err("%s - fail err = %d\n", __func__, err);
return err;
}
return 0;
}
static void max86915_pin_control(struct max86915_device_data *data, bool pin_set)
{
int status = 0;
if (!data->hrm_pinctrl) {
HRM_err("%s - hrm_pinctrl is null\n", __func__);
return;
}
if (pin_set) {
if (!IS_ERR_OR_NULL(data->pins_idle)) {
status = pinctrl_select_state(data->hrm_pinctrl,
data->pins_idle);
if (status)
HRM_err("%s - can't set pin default state\n",
__func__);
HRM_info("%s idle\n", __func__);
}
} else {
if (!IS_ERR_OR_NULL(data->pins_sleep)) {
status = pinctrl_select_state(data->hrm_pinctrl,
data->pins_sleep);
if (status)
HRM_err("%s - can't set pin sleep state\n",
__func__);
HRM_info("%s sleep\n", __func__);
}
}
}
static void max86915_irq_set_state(struct max86915_device_data *data, int irq_enable)
{
HRM_dbg("%s - irq_enable : %d, irq_state : %d\n",
__func__, irq_enable, data->irq_state);
if (irq_enable) {
if (data->irq_state++ == 0) {
max86915_pin_control(data, true);
}
} else {
if (data->irq_state == 0)
return;
if (--data->irq_state <= 0) {
max86915_pin_control(data, false);
data->irq_state = 0;
}
}
}
static int max86915_led_power_ctrl(struct max86915_device_data *data, int onoff)
{
int rc = 0;
if (onoff == PWR_ON) {
rc = gpio_direction_output(data->pin_hrm_en, 1);
if (rc) {
HRM_err("%s - gpio direction output failed, rc=%d\n",
__func__, rc);
}
usleep_range(2000, 2100);
} else {
gpio_set_value(data->pin_hrm_en, 0);
rc = gpio_direction_input(data->pin_hrm_en);
if (rc) {
HRM_err("%s - gpio direction input failed, rc=%d\n",
__func__, rc);
}
}
return rc;
}
static int max86915_power_ctrl(struct max86915_device_data *data, int onoff)
{
int rc = 0;
static int i2c_1p8_enable;
struct regulator *regulator_vdd_1p8 = NULL;
struct regulator *regulator_i2c_1p8 = NULL;
HRM_dbg("%s - onoff : %d, state : %d\n",
__func__, onoff, data->regulator_state);
if (onoff == PWR_ON) {
if (data->regulator_state != 0) {
HRM_dbg("%s - duplicate regulator\n", __func__);
data->regulator_state++;
return 0;
}
data->regulator_state++;
data->pm_state = PM_RESUME;
} else {
if (data->regulator_state == 0) {
HRM_dbg("%s - already off the regulator\n", __func__);
return 0;
} else if (data->regulator_state != 1) {
HRM_dbg("%s - duplicate regulator\n", __func__);
data->regulator_state--;
return 0;
}
data->regulator_state--;
}
if (data->i2c_1p8 != NULL) {
regulator_i2c_1p8 = regulator_get(NULL, data->i2c_1p8);
if (IS_ERR(regulator_i2c_1p8) || regulator_i2c_1p8 == NULL) {
HRM_err("%s - get i2c_1p8 regulator failed, %d\n", __func__, PTR_ERR(regulator_i2c_1p8));
rc = -EINVAL;
regulator_i2c_1p8 = NULL;
goto get_i2c_1p8_failed;
}
}
if (data->vdd_1p8 != NULL) {
regulator_vdd_1p8 =
regulator_get(&data->client->dev, data->vdd_1p8);
if (IS_ERR(regulator_vdd_1p8) || regulator_vdd_1p8 == NULL) {
HRM_dbg("%s - get vdd_1p8 regulator failed, %d\n", __func__, PTR_ERR(regulator_vdd_1p8));
regulator_vdd_1p8 = NULL;
goto get_vdd_1p8_failed;
}
}
if (onoff == PWR_ON) {
if (data->i2c_1p8 != NULL && i2c_1p8_enable == 0) {
rc = regulator_enable(regulator_i2c_1p8);
i2c_1p8_enable = 1;
if (rc) {
HRM_err("%s - enable i2c_1p8 failed, rc=%d\n", __func__, rc);
goto enable_i2c_1p8_failed;
}
}
if (regulator_vdd_1p8 != NULL) {
rc = regulator_enable(regulator_vdd_1p8);
if (rc) {
HRM_err("%s - enable vdd_1p8 failed, rc=%d\n",
__func__, rc);
goto enable_vdd_1p8_failed;
}
}
} else {
if (regulator_vdd_1p8 != NULL) {
rc = regulator_disable(regulator_vdd_1p8);
if (rc) {
HRM_err("%s - disable vdd_1p8 failed, rc=%d\n",
__func__, rc);
goto done;
}
}
#ifdef I2C_1P8_DISABLE
if (data->i2c_1p8 != NULL) {
rc = regulator_disable(regulator_i2c_1p8);
i2c_1p8_enable = 0;
if (rc) {
HRM_err("%s - disable i2c_1p8 failed, rc=%d\n", __func__, rc);
goto done;
}
}
#endif
}
goto done;
enable_vdd_1p8_failed:
#ifdef I2C_1P8_DISABLE
if (data->i2c_1p8 != NULL) {
regulator_disable(regulator_i2c_1p8);
i2c_1p8_enable = 0;
}
#endif
enable_i2c_1p8_failed:
done:
regulator_put(regulator_vdd_1p8);
get_vdd_1p8_failed:
if (data->i2c_1p8 != NULL)
regulator_put(regulator_i2c_1p8);
get_i2c_1p8_failed:
return rc;
}
static int max86915_eol_set_mode(struct max86915_device_data *data, int onoff)
{
int err = 0;
HRM_dbg("%s - onoff = %d\n", __func__, onoff);
if (onoff == PWR_ON) {
data->eol_test_is_enable = 1;
data->agc_mode = M_NONE;
max86915_irq_set_state(data, PWR_ON);
if (!data->always_on) {
err = max86915_power_ctrl(data, PWR_ON);
if (err < 0)
HRM_err("%s - max86915_power_ctrl fail err = %d\n", __func__, err);
}
err = max86915_led_power_ctrl(data, PWR_ON);
if (err)
HRM_err("%s - max86915_led_power_ctrl fail err = %d\n", __func__, err);
max86915_init_eol_data(&data->eol_data);
if (data->eol_test_type == EOL_XTALK) {
data->eol_data.state = _EOL_STATE_TYPE_NEW_XTALK_MODE;
err = max86915_set_reg_hrm(data);
} else {
if (data->pre_eol_test_is_enable == 2) /* Pre Test Only for Frequency */
err = max86915_enable_eol_freq(data);
else
err = max86915_enable_eol_flicker(data);
}
if (err < 0) {
data->eol_test_is_enable = 0;
err = max86915_disable(data, MODE_NONE);
if (err != 0)
HRM_err("%s - max86915_disable err : %d\n", __func__, err);
err = max86915_led_power_ctrl(data, PWR_OFF);
if (err)
HRM_err("%s - max86915_led_power_ctrl fail err = %d\n", __func__, err);
if (!data->always_on) {
err = max86915_power_ctrl(data, PWR_OFF);
if (err < 0)
HRM_err("%s - hrm_regulator_on fail err = %d\n",
__func__, err);
}
max86915_irq_set_state(data, PWR_OFF);
HRM_err("%s - eol test enable fail err = %d\n", __func__, err);
return err;
}
} else {
data->eol_test_is_enable = 0;
err = max86915_disable(data, MODE_NONE);
if (err != 0) {
HRM_err("%s - max86915_disable err : %d\n", __func__, err);
return err;
}
err = max86915_led_power_ctrl(data, PWR_OFF);
if (err) {
HRM_err("%s - max86915_led_power_ctrl fail err = %d\n", __func__, err);
return err;
}
if (!data->always_on) {
err = max86915_power_ctrl(data, PWR_OFF);
if (err < 0)
HRM_err("%s - hrm_regulator_on fail err = %d\n",
__func__, err);
}
max86915_irq_set_state(data, PWR_OFF);
}
return err;
}
static void max86915_set_mode(struct max86915_device_data *data,
int onoff, enum op_mode mode)
{
int err;
if (onoff == PWR_ON) {
max86915_irq_set_state(data, PWR_ON);
if (!data->always_on) {
err = max86915_power_ctrl(data, PWR_ON);
if (err < 0)
HRM_err("%s - hrm_regulator_on fail err = %d\n",
__func__, err);
}
err = max86915_led_power_ctrl(data, PWR_ON);
if (err)
HRM_err("%s - max86915_led_power_ctrl fail err = %d\n", __func__, err);
err = max86915_enable(data, mode);
if (err == 0)
data->enabled_mode = mode;
else {
data->enabled_mode = MODE_NONE;
err = max86915_disable(data, MODE_NONE);
if (err != 0)
HRM_err("%s - max86915_disable err : %d\n", __func__, err);
err = max86915_led_power_ctrl(data, PWR_OFF);
if (err)
HRM_err("%s - max86915_led_power_ctrl fail err = %d\n", __func__, err);
if (!data->always_on) {
err = max86915_power_ctrl(data, PWR_OFF);
if (err < 0)
HRM_err("%s - hrm_regulator_on fail err = %d\n", __func__, err);
}
max86915_irq_set_state(data, PWR_OFF);
HRM_err("%s - enable err : %d\n", __func__, err);
}
if (err < 0 && mode == MODE_AMBIENT) {
input_report_rel(data->hrm_input_dev,
REL_Y, -5 + 1); /* F_ERR_I2C -5 detected i2c error */
input_sync(data->hrm_input_dev);
HRM_err("%s - awb mode enable error %d\n", __func__, err);
}
} else {
if (data->regulator_state == 0) {
HRM_dbg("%s - already power off - disable skip\n",
__func__);
return;
}
err = max86915_disable(data, mode);
if (err != 0)
HRM_err("%s - disable err : %d\n", __func__, err);
data->enabled_mode = 0;
data->mode_sdk_enabled = 0;
data->mode_svc_enabled = 0;
err = max86915_led_power_ctrl(data, PWR_OFF);
if (err)
HRM_err("%s - max86915_led_power_ctrl fail err = %d\n", __func__, err);
if (!data->always_on) {
err = max86915_power_ctrl(data, PWR_OFF);
if (err < 0)
HRM_err("%s - hrm_regulator_off fail err = %d\n", __func__, err);
}
max86915_irq_set_state(data, PWR_OFF);
}
HRM_info("%s - set mode complete, onoff : %d m : %d c : %d\n",
__func__, onoff, mode, data->enabled_mode);
}
/* hrm input enable/disable sysfs */
static ssize_t max86915_enable_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct max86915_device_data *data = dev_get_drvdata(dev);
return snprintf(buf, strlen(buf), "%d\n", data->enabled_mode);
}
static ssize_t max86915_enable_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct max86915_device_data *data = dev_get_drvdata(dev);
int on_off;
enum op_mode mode;
mutex_lock(&data->activelock);
if (sysfs_streq(buf, "0")) {
on_off = PWR_OFF;
mode = MODE_NONE;
} else if (sysfs_streq(buf, "1")) {
on_off = PWR_ON;
mode = MODE_HRM;
data->mode_cnt.hrm_cnt++;
} else if (sysfs_streq(buf, "2")) {
on_off = PWR_ON;
mode = MODE_AMBIENT;
data->mode_cnt.amb_cnt++;
} else if (sysfs_streq(buf, "3")) {
on_off = PWR_ON;
mode = MODE_PROX;
data->mode_cnt.prox_cnt++;
} else if (sysfs_streq(buf, "10")) {
on_off = PWR_ON;
mode = MODE_SDK_IR;
data->mode_cnt.sdk_cnt++;
} else if (sysfs_streq(buf, "11")) {
on_off = PWR_ON;
mode = MODE_SDK_RED;
} else if (sysfs_streq(buf, "12")) {
on_off = PWR_ON;
mode = MODE_SDK_GREEN;
} else if (sysfs_streq(buf, "13")) {
on_off = PWR_ON;
mode = MODE_SDK_BLUE;
} else if (sysfs_streq(buf, "-10")) {
on_off = PWR_OFF;
mode = MODE_SDK_IR;
} else if (sysfs_streq(buf, "-11")) {
on_off = PWR_OFF;
mode = MODE_SDK_RED;
} else if (sysfs_streq(buf, "-12")) {
on_off = PWR_OFF;
mode = MODE_SDK_GREEN;
} else if (sysfs_streq(buf, "-13")) {
on_off = PWR_OFF;
mode = MODE_SDK_BLUE;
} else if (sysfs_streq(buf, "14")) {
on_off = PWR_ON;
mode = MODE_SVC_IR;
data->mode_cnt.unkn_cnt++;
} else if (sysfs_streq(buf, "15")) {
on_off = PWR_ON;
mode = MODE_SVC_RED;
} else if (sysfs_streq(buf, "16")) {
on_off = PWR_ON;
mode = MODE_SVC_GREEN;
} else if (sysfs_streq(buf, "17")) {
on_off = PWR_ON;
mode = MODE_SVC_BLUE;
} else if (sysfs_streq(buf, "-14")) {
on_off = PWR_OFF;
mode = MODE_SVC_IR;
} else if (sysfs_streq(buf, "-15")) {
on_off = PWR_OFF;
mode = MODE_SVC_RED;
} else if (sysfs_streq(buf, "-16")) {
on_off = PWR_OFF;
mode = MODE_SVC_GREEN;
} else if (sysfs_streq(buf, "-17")) {
on_off = PWR_OFF;
mode = MODE_SVC_BLUE;
} else {
HRM_err("%s - invalid value %d\n", __func__, *buf);
data->mode_cnt.unkn_cnt++;
mutex_unlock(&data->activelock);
return -EINVAL;
}
if (mode == MODE_SDK_IR || mode == MODE_SDK_RED
|| mode == MODE_SDK_GREEN || mode == MODE_SDK_BLUE) {
HRM_dbg("%s - SDK en : %d m : %d c : %d\n", __func__, on_off, mode, data->mode_sdk_enabled);
if (on_off == PWR_ON) {
if (data->mode_sdk_enabled & (1<<(mode - MODE_SDK_IR))) { /* already enabled */
/* Do Nothing */
HRM_dbg("%s - SDK %d mode already enabled\n", __func__, mode);
mutex_unlock(&data->activelock);
return count;
}
if (data->mode_sdk_enabled == 0) {
data->mode_sdk_enabled |= (1<<(mode - MODE_SDK_IR));
mode = MODE_SDK_IR;
data->mode_cnt.sdk_cnt++;
} else {
data->mode_sdk_enabled |= (1<<(mode - MODE_SDK_IR));
mutex_unlock(&data->activelock);
return count;
}
} else {
if ((data->mode_sdk_enabled & (1<<(mode - MODE_SDK_IR))) == 0) { /* Not Enabled */
/* Do Nothing */
HRM_dbg("%s - SDK %d mode not enabled\n", __func__, mode);
mutex_unlock(&data->activelock);
return count;
}
/* Oring disable mode */
data->mode_sdk_enabled &= ~(1<<(mode - MODE_SDK_IR));
max86915_reset_sdk_agc_var(mode - MODE_SDK_IR);
if (data->mode_sdk_enabled == 0) {
mode = MODE_NONE;
} else {
mutex_unlock(&data->activelock);
return count;
}
}
} else if (mode == MODE_SVC_IR || mode == MODE_SVC_RED
|| mode == MODE_SVC_GREEN || mode == MODE_SVC_BLUE) {
HRM_dbg("%s - SVC en : %d m : %d c : %d\n", __func__, on_off, mode, data->mode_svc_enabled);
if (on_off == PWR_ON) {
if (data->mode_svc_enabled & (1<<(mode - MODE_SVC_IR))) { /* already enabled */
/* Do Nothing */
HRM_dbg("%s - SVC %d mode already enabled\n", __func__, mode);
mutex_unlock(&data->activelock);
return count;
}
if (data->mode_svc_enabled == 0) {
data->mode_svc_enabled |= (1<<(mode - MODE_SVC_IR));
mode = MODE_SVC_IR;
data->mode_cnt.unkn_cnt++;
} else {
data->mode_svc_enabled |= (1<<(mode - MODE_SVC_IR));
mutex_unlock(&data->activelock);
return count;
}
} else {
if ((data->mode_svc_enabled & (1<<(mode - MODE_SVC_IR))) == 0) { /* Not Enabled */
/* Do Nothing */
HRM_dbg("%s - SVC %d mode not enabled\n", __func__, mode);
mutex_unlock(&data->activelock);
return count;
}
/* Oring disable mode */
data->mode_svc_enabled &= ~(1<<(mode - MODE_SVC_IR));
if (data->mode_svc_enabled == 0) {
mode = MODE_NONE;
} else {
mutex_unlock(&data->activelock);
return count;
}
}
}
HRM_dbg("%s - en : %d m : %d c : %d\n", __func__, on_off, mode, data->enabled_mode);
max86915_set_mode(data, on_off, mode);
mutex_unlock(&data->activelock);
return count;
}
static ssize_t max86915_poll_delay_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct max86915_device_data *data = dev_get_drvdata(dev);
return snprintf(buf, PAGE_SIZE, "%d\n", data->sampling_period_ns);
}
static ssize_t max86915_poll_delay_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
struct max86915_device_data *data = dev_get_drvdata(dev);
u32 sampling_period_ns = 0;
int err = 0;
mutex_lock(&data->activelock);
err = kstrtoint(buf, 10, &sampling_period_ns);
if (err < 0) {
HRM_err("%s - kstrtoint failed.(%d)\n", __func__, err);
mutex_unlock(&data->activelock);
return err;
}
err = max86915_set_sampling_rate(sampling_period_ns);
if (err > 0)
data->sampling_period_ns = err;
HRM_dbg("%s - hrm sensor sampling rate is setted as %dns\n", __func__, sampling_period_ns);
mutex_unlock(&data->activelock);
return size;
}
static DEVICE_ATTR(enable, S_IRUGO|S_IWUSR|S_IWGRP,
max86915_enable_show, max86915_enable_store);
static DEVICE_ATTR(poll_delay, S_IRUGO|S_IWUSR|S_IWGRP,
max86915_poll_delay_show, max86915_poll_delay_store);
static struct attribute *hrm_sysfs_attrs[] = {
&dev_attr_enable.attr,
&dev_attr_poll_delay.attr,
NULL
};
static struct attribute_group hrm_attribute_group = {
.attrs = hrm_sysfs_attrs,
};
/* hrm_sensor sysfs */
static ssize_t max86915_name_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
char chip_name[NAME_LEN];
switch (max86915_data->part_type) {
case PART_TYPE_MAX86915_ES:
case PART_TYPE_MAX86915_CS_15:
case PART_TYPE_MAX86915_CS_21:
case PART_TYPE_MAX86915_CS_22:
case PART_TYPE_MAX86915_CS_211:
case PART_TYPE_MAX86915_CS_221:
strlcpy(chip_name, MAX86915_CHIP_NAME, strlen(MAX86915_CHIP_NAME) + 1);
break;
case PART_TYPE_MAX86917_1:
case PART_TYPE_MAX86917_2:
strlcpy(chip_name, MAX86917_CHIP_NAME, strlen(MAX86917_CHIP_NAME) + 1);
break;
default:
strlcpy(chip_name, "NONE", 5);
break;
}
return snprintf(buf, PAGE_SIZE, "%s\n", chip_name);
}
static ssize_t max86915_vendor_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
char vendor[NAME_LEN];
strlcpy(vendor, VENDOR, strlen(VENDOR) + 1);
return snprintf(buf, PAGE_SIZE, "%s\n", vendor);
}
static ssize_t max86915_led_current_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
int err;
int led1, led2, led3, led4;
int range1, range2, range3, range4;
u32 led_current, led_range;
u8 reg_value;
struct max86915_device_data *data = dev_get_drvdata(dev);
led_range = (max86915_data->led_range[0] << 0)
| (max86915_data->led_range[1] << 4)
| (max86915_data->led_range[2] << 8)
| (max86915_data->led_range[3] << 12);
mutex_lock(&data->activelock);
err = sscanf(buf, "%8x, %4x", &led_current, &led_range);
if (err < 0) {
HRM_err("%s - sscanf failed, err = %x\n", __func__, err);
mutex_unlock(&data->activelock);
return err;
}
led1 = 0x000000ff & led_current;
led2 = (0x0000ff00 & led_current) >> 8;
led3 = (0x00ff0000 & led_current) >> 16;
led4 = (0xff000000 & led_current) >> 24;
HRM_dbg("%s - led_current = %08x : 0x%02x, 0x%02x, 0x%02x, 0x%02x\n",
__func__, led_current, led1, led2, led3, led4);
range1 = 0x0003 & led_range;
range2 = (0x0030 & led_range) >> 4;
range3 = (0x0300 & led_range) >> 8;
range4 = (0x3000 & led_range) >> 12;
HRM_dbg("%s - led_range = %04x : 0x%02x, 0x%02x, 0x%02x, 0x%02x\n",
__func__, led_range, range1, range2, range3, range4);
max86915_data->led_range[0] = range1;
max86915_data->led_range[1] = range2;
max86915_data->led_range[2] = range3;
max86915_data->led_range[3] = range4;
reg_value = (0x0003 & led_range) | ((0x0030 & led_range) >> 2)
| ((0x0300 & led_range) >> 4) | ((0x3000 & led_range) >> 6);
err = max86915_write_reg(max86915_data, MAX86915_LED_RANGE, reg_value);
if (err != 0) {
HRM_err("%s - error MAX86915_LED_RANGE!\n",
__func__);
mutex_unlock(&data->activelock);
return -EIO;
}
err = max86915_set_current(led1, led2, led3, led4);
if (err < 0) {
HRM_err("%s - set current failed, err = %x\n", __func__, err);
mutex_unlock(&data->activelock);
return err;
}
mutex_unlock(&data->activelock);
return size;
}
static ssize_t max86915_led_current_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct max86915_device_data *data = dev_get_drvdata(dev);
u32 led_current, led_range;
mutex_lock(&data->activelock);
led_current = (data->led_current1 & 0xff) | ((data->led_current2 & 0xff) << 8)
| ((data->led_current3 & 0xff) << 16) | ((data->led_current4 & 0xff) << 24);
led_range = (data->led_range[0] & 0x03) | ((data->led_range[1] & 0x03) << 4)
| ((data->led_range[2] & 0x03) << 8) | ((data->led_range[3] & 0x03) << 12);
mutex_unlock(&data->activelock);
HRM_info("%s - led1 0x%02x, led2 0x%02x, led3 0x%02x, led4 0x%02x\n",
__func__, data->led_current1, data->led_current2, data->led_current3, data->led_current4);
HRM_info("%s - range1 0x%02x, range2 0x%02x, range3 0x%02x, range4 0x%02x\n",
__func__, data->led_range[0], data->led_range[1], data->led_range[2], data->led_range[3]);
return snprintf(buf, PAGE_SIZE, "%08X, %04X\n", led_current, led_range);
}
static ssize_t max86915_led_test_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int err;
u8 result;
struct max86915_device_data *data = dev_get_drvdata(dev);
if (!data->always_on) {
err = max86915_power_ctrl(data, PWR_ON);
if (err < 0)
HRM_err("%s - hrm_regulator_on fail err = %d\n", __func__, err);
}
err = max86915_led_power_ctrl(data, PWR_ON);
if (err)
HRM_err("%s - max86915_led_power_ctrl fail err = %d\n", __func__, err);
mutex_lock(&data->activelock);
err = max86915_get_led_test(&result);
if (err < 0) {
HRM_err("%s - led test failed, err = %x\n", __func__, err);
mutex_unlock(&data->activelock);
return err;
}
mutex_unlock(&data->activelock);
err = max86915_disable(data, MODE_NONE);
if (err != 0)
HRM_err("%s - max86915_disable err : %d\n", __func__, err);
err = max86915_led_power_ctrl(data, PWR_OFF);
if (err)
HRM_err("%s - max86915_led_power_ctrl fail err = %d\n", __func__, err);
if (!data->always_on) {
err = max86915_power_ctrl(data, PWR_OFF);
if (err < 0)
HRM_err("%s - hrm_regulator_off fail err = %d\n", __func__, err);
}
HRM_info("%s - result = 0x%02x\n", __func__, result);
return snprintf(buf, PAGE_SIZE, "0x%02x\n", result);
}
static ssize_t max86915_flush_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
struct max86915_device_data *data = dev_get_drvdata(dev);
int ret = 0;
u8 handle = 0;
mutex_lock(&data->activelock);
ret = kstrtou8(buf, 10, &handle);
if (ret < 0) {
HRM_err("%s - kstrtou8 failed.(%d)\n", __func__, ret);
mutex_unlock(&data->activelock);
return ret;
}
HRM_dbg("%s - handle = %d\n", __func__, handle);
mutex_unlock(&data->activelock);
input_report_rel(data->hrm_input_dev, REL_MISC, handle);
return size;
}
static ssize_t max86915_int_pin_check_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
/* need to check if this should be implemented */
HRM_dbg("%s - not implement\n", __func__);
return snprintf(buf, PAGE_SIZE, "%d\n", 0);
}
static ssize_t max86915_lib_ver_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
struct max86915_device_data *data = dev_get_drvdata(dev);
size_t buf_len;
mutex_lock(&data->activelock);
buf_len = strlen(buf) + 1;
if (buf_len > NAME_LEN)
buf_len = NAME_LEN;
if (data->lib_ver != NULL)
kfree(data->lib_ver);
data->lib_ver = kzalloc(sizeof(char) * buf_len, GFP_KERNEL);
if (data->lib_ver == NULL) {
HRM_err("%s - couldn't allocate memory\n", __func__);
mutex_unlock(&data->activelock);
return -ENOMEM;
}
strlcpy(data->lib_ver, buf, buf_len);
HRM_info("%s - lib_ver = %s\n", __func__, data->lib_ver);
mutex_unlock(&data->activelock);
return size;
}
static ssize_t max86915_lib_ver_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct max86915_device_data *data = dev_get_drvdata(dev);
if (data->lib_ver == NULL) {
HRM_err("%s - data->lib_ver is NULL\n", __func__);
return snprintf(buf, PAGE_SIZE, "%s\n", "NULL");
}
HRM_info("%s - lib_ver = %s\n", __func__, data->lib_ver);
return snprintf(buf, PAGE_SIZE, "%s\n", data->lib_ver);
}
static ssize_t max86915_threshold_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
struct max86915_device_data *data = dev_get_drvdata(dev);
int err = 0;
mutex_lock(&data->activelock);
err = kstrtoint(buf, 10, &data->hrm_threshold);
if (err < 0) {
HRM_err("%s - kstrtoint failed.(%d)\n", __func__, err);
mutex_unlock(&data->activelock);
return err;
}
HRM_info("%s - threshold = %d\n", __func__, data->hrm_threshold);
mutex_unlock(&data->activelock);
return size;
}
static ssize_t max86915_threshold_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct max86915_device_data *data = dev_get_drvdata(dev);
if (data->hrm_threshold) {
HRM_info("%s - threshold = %d\n", __func__, data->hrm_threshold);
return snprintf(buf, PAGE_SIZE, "%d\n", data->hrm_threshold);
}
HRM_info("%s - threshold = 0\n", __func__);
return snprintf(buf, PAGE_SIZE, "%d\n", 0);
}
static ssize_t max86915_prox_thd_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
struct max86915_device_data *data = dev_get_drvdata(dev);
int err = 0;
mutex_lock(&data->activelock);
err = kstrtoint(buf, 10, &data->prox_threshold);
if (err < 0) {
HRM_err("%s - kstrtoint failed.(%d)\n", __func__, err);
mutex_unlock(&data->activelock);
return err;
}
HRM_info("%s - prox threshold = %d\n", __func__, data->prox_threshold);
mutex_unlock(&data->activelock);
return size;
}
static ssize_t max86915_prox_thd_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct max86915_device_data *data = dev_get_drvdata(dev);
if (data->prox_threshold) {
HRM_info("%s - prox threshold = %d\n",
__func__, data->prox_threshold);
return snprintf(buf, PAGE_SIZE, "%d\n", data->prox_threshold);
}
HRM_info("%s - prox threshold = 0\n", __func__);
return snprintf(buf, PAGE_SIZE, "%d\n", 0);
}
static ssize_t max86915_eol_test_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
int test_onoff;
struct max86915_device_data *data = dev_get_drvdata(dev);
mutex_lock(&data->activelock);
if (sysfs_streq(buf, "1")) { /* eol_test start */
test_onoff = 1;
data->eol_test_type = EOL_15_MODE;
data->enabled_mode = MODE_HRM;
} else if (sysfs_streq(buf, "2")) { /* semi ft test start */
test_onoff = 1;
data->eol_test_type = EOL_SEMI_FT;
data->enabled_mode = MODE_HRM;
} else if (sysfs_streq(buf, "3")) { /* xtalk test start */
test_onoff = 1;
data->eol_test_type = EOL_XTALK;
data->enabled_mode = MODE_HRM;
} else if (sysfs_streq(buf, "0")) { /* eol_test stop */
test_onoff = 0;
data->pre_eol_test_is_enable = 0;
data->enabled_mode = MODE_NONE;
} else {
HRM_err("%s - invalid value %d\n", __func__, *buf);
mutex_unlock(&data->activelock);
return -EINVAL;
}
HRM_dbg("%s - %d\n", __func__, test_onoff);
if (data->eol_test_is_enable == test_onoff) {
HRM_dbg("%s - invalid eol status Pre: %d, AF : %d\n", __func__,
data->eol_test_is_enable, test_onoff);
mutex_unlock(&data->activelock);
return -EINVAL;
}
if (max86915_eol_set_mode(data, test_onoff) < 0)
data->eol_test_is_enable = 0;
mutex_unlock(&data->activelock);
return size;
}
static ssize_t max86915_eol_test_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct max86915_device_data *data = dev_get_drvdata(dev);
return snprintf(buf, PAGE_SIZE, "%u\n", data->eol_test_is_enable);
}
static ssize_t max86915_eol_test_result_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct max86915_device_data *data = dev_get_drvdata(dev);
mutex_lock(&data->activelock);
if (data->eol_test_status == 0) {
HRM_err("%s - data->eol_test_status is NULL\n",
__func__);
data->eol_test_status = 0;
mutex_unlock(&data->activelock);
return snprintf(buf, PAGE_SIZE, "%s\n", "NO_EOL_TEST");
}
HRM_dbg("%s - result = %d\n", __func__, data->eol_test_status);
data->eol_test_status = 0;
mutex_unlock(&data->activelock);
return snprintf(buf, PAGE_SIZE, "%s\n", data->eol_test_result);
}
static ssize_t max86915_eol_test_status_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct max86915_device_data *data = dev_get_drvdata(dev);
return snprintf(buf, PAGE_SIZE, "%u\n", data->eol_test_status);
}
static ssize_t max86915_pre_eol_test_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
struct max86915_device_data *data = dev_get_drvdata(dev);
mutex_lock(&data->activelock);
if (sysfs_streq(buf, "1")) { /* PRE EOL SYSTEM NOISE */
data->pre_eol_test_is_enable = 1;
} else if (sysfs_streq(buf, "2")) { /* PRE EOL FREQ */
data->pre_eol_test_is_enable = 2;
} else if (sysfs_streq(buf, "3")) { /* PRE EOL BOTH */
data->pre_eol_test_is_enable = 3;
} else if (sysfs_streq(buf, "0")) { /* pre_eol_test stop */
data->pre_eol_test_is_enable = 0;
} else {
HRM_err("%s - invalid value %d\n", __func__, *buf);
mutex_unlock(&data->activelock);
return -EINVAL;
}
HRM_dbg("%s - %d\n", __func__, data->pre_eol_test_is_enable);
mutex_unlock(&data->activelock);
return size;
}
static ssize_t max86915_pre_eol_test_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct max86915_device_data *data = dev_get_drvdata(dev);
return snprintf(buf, PAGE_SIZE, "%u\n", data->pre_eol_test_is_enable);
}
static ssize_t max86915_read_reg_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct max86915_device_data *data = dev_get_drvdata(dev);
HRM_info("%s - val=0x%06x\n", __func__, data->reg_read_buf);
return snprintf(buf, PAGE_SIZE, "%d\n", data->reg_read_buf);
}
static ssize_t max86915_read_reg_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
struct max86915_device_data *data = dev_get_drvdata(dev);
int err = -1;
unsigned int cmd = 0;
u8 val = 0;
mutex_lock(&data->i2clock);
if (data->regulator_state == 0) {
HRM_dbg("%s - need to power on\n", __func__);
mutex_unlock(&data->i2clock);
return size;
}
err = sscanf(buf, "%8x", &cmd);
if (err == 0) {
HRM_err("%s - sscanf fail\n", __func__);
mutex_unlock(&data->i2clock);
return size;
}
err = max86915_read_reg(data, (u8)cmd, &val, 1);
if (err != 0) {
HRM_err("%s - err=%d, val=0x%06x\n",
__func__, err, val);
mutex_unlock(&data->i2clock);
return size;
}
data->reg_read_buf = (u32)val;
mutex_unlock(&data->i2clock);
return size;
}
static ssize_t max86915_write_reg_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
struct max86915_device_data *data = dev_get_drvdata(dev);
int err = -1;
unsigned int cmd = 0;
unsigned int val = 0;
mutex_lock(&data->i2clock);
if (data->regulator_state == 0) {
HRM_dbg("%s - need to power on.\n", __func__);
mutex_unlock(&data->i2clock);
return size;
}
err = sscanf(buf, "%8x, %8x", &cmd, &val);
if (err == 0) {
HRM_err("%s - sscanf fail %s\n", __func__, buf);
mutex_unlock(&data->i2clock);
return size;
}
err = max86915_write_reg(data, (u8)cmd, (u8)val);
if (err < 0) {
HRM_err("%s - fail err = %d\n", __func__, err);
mutex_unlock(&data->i2clock);
return err;
}
mutex_unlock(&data->i2clock);
return size;
}
static ssize_t max86915_debug_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct max86915_device_data *data = dev_get_drvdata(dev);
HRM_info("%s - debug mode = %u\n", __func__, data->debug_mode);
return snprintf(buf, PAGE_SIZE, "%u\n", data->debug_mode);
}
static ssize_t max86915_debug_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
struct max86915_device_data *data = dev_get_drvdata(dev);
int err;
s32 mode;
mutex_lock(&data->activelock);
err = kstrtoint(buf, 10, &mode);
if (err < 0) {
HRM_err("%s - kstrtoint failed.(%d)\n", __func__, err);
mutex_unlock(&data->activelock);
return err;
}
data->debug_mode = (u8)mode;
HRM_info("%s - mode = %d\n", __func__, mode);
switch (data->debug_mode) {
case DEBUG_REG_STATUS:
max86915_print_reg_status();
break;
case DEBUG_ENABLE_AGC:
agc_debug_enabled = 1;
break;
case DEBUG_DISABLE_AGC:
agc_debug_enabled = 0;
break;
default:
break;
}
mutex_unlock(&data->activelock);
return size;
}
static ssize_t max86915_device_id_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct max86915_device_data *data = dev_get_drvdata(dev);
int err;
u64 device_id = 0;
if (!data->always_on) {
err = max86915_power_ctrl(data, PWR_ON);
if (err < 0)
HRM_err("%s - hrm_regulator_on fail err = %d\n", __func__, err);
}
err = max86915_led_power_ctrl(data, PWR_ON);
if (err)
HRM_err("%s - max86915_led_power_ctrl fail err = %d\n", __func__, err);
mutex_lock(&data->activelock);
max86915_get_chipid(&device_id);
mutex_unlock(&data->activelock);
err = max86915_disable(data, MODE_NONE);
if (err != 0)
HRM_err("%s - disable err : %d\n", __func__, err);
err = max86915_led_power_ctrl(data, PWR_OFF);
if (err)
HRM_err("%s - max86915_led_power_ctrl fail err = %d\n", __func__, err);
if (!data->always_on) {
err = max86915_power_ctrl(data, PWR_OFF);
if (err < 0)
HRM_err("%s - hrm_regulator_off fail err = %d\n", __func__, err);
}
return snprintf(buf, PAGE_SIZE, "%lld\n", device_id);
}
static ssize_t max86915_part_type_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct max86915_device_data *data = dev_get_drvdata(dev);
int err;
u16 part_type = 0;
if (!data->always_on) {
err = max86915_power_ctrl(data, PWR_ON);
if (err < 0)
HRM_err("%s - hrm_regulator_on fail err = %d\n", __func__, err);
}
err = max86915_led_power_ctrl(data, PWR_ON);
if (err)
HRM_err("%s - max86915_led_power_ctrl fail err = %d\n", __func__, err);
mutex_lock(&data->activelock);
max86915_get_part_type(&part_type);
mutex_unlock(&data->activelock);
err = max86915_disable(data, MODE_NONE);
if (err != 0)
HRM_err("%s - disable err : %d\n", __func__, err);
err = max86915_led_power_ctrl(data, PWR_OFF);
if (err)
HRM_err("%s - max86915_led_power_ctrl fail err = %d\n", __func__, err);
if (!data->always_on) {
err = max86915_power_ctrl(data, PWR_OFF);
if (err < 0)
HRM_err("%s - hrm_regulator_off fail err = %d\n", __func__, err);
}
return snprintf(buf, PAGE_SIZE, "%d\n", part_type);
}
static ssize_t max86915_i2c_err_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct max86915_device_data *data = dev_get_drvdata(dev);
u32 err_cnt = 0;
err_cnt = data->i2c_err_cnt;
return snprintf(buf, PAGE_SIZE, "%d\n", err_cnt);
}
static ssize_t max86915_i2c_err_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
struct max86915_device_data *data = dev_get_drvdata(dev);
data->i2c_err_cnt = 0;
return size;
}
static ssize_t max86915_curr_adc_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct max86915_device_data *data = dev_get_drvdata(dev);
u16 ir_curr = 0;
u16 red_curr = 0;
u32 ir_adc = 0;
u32 red_adc = 0;
ir_curr = data->ir_curr;
red_curr = data->red_curr;
/*
* green_curr = data->green_curr;
* blue_curr = data->blue_curr;
*/
ir_adc = data->ir_adc;
red_adc = data->red_adc;
/*
* green_adc = data->green_adc;
* blue_adc = data->blue_adc;
*/
return snprintf(buf, PAGE_SIZE,
"\"HRIC\":\"%d\",\"HRRC\":\"%d\",\"HRIA\":\"%d\",\"HRRA\":\"%d\"\n",
ir_curr, red_curr, ir_adc, red_adc);
}
static ssize_t max86915_curr_adc_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
struct max86915_device_data *data = dev_get_drvdata(dev);
data->ir_curr = 0;
data->red_curr = 0;
data->ir_adc = 0;
data->red_adc = 0;
return size;
}
static ssize_t max86915_mode_cnt_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct max86915_device_data *data = dev_get_drvdata(dev);
return snprintf(buf, PAGE_SIZE,
"\"CNT_HRM\":\"%d\",\"CNT_AMB\":\"%d\",\"CNT_PROX\":\"%d\",\"CNT_SDK\":\"%d\",\"CNT_CGM\":\"%d\",\"CNT_UNKN\":\"%d\"\n",
data->mode_cnt.hrm_cnt, data->mode_cnt.amb_cnt, data->mode_cnt.prox_cnt,
data->mode_cnt.sdk_cnt, data->mode_cnt.cgm_cnt, data->mode_cnt.unkn_cnt);
}
static ssize_t max86915_mode_cnt_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
struct max86915_device_data *data = dev_get_drvdata(dev);
data->mode_cnt.hrm_cnt = 0;
data->mode_cnt.amb_cnt = 0;
data->mode_cnt.prox_cnt = 0;
data->mode_cnt.sdk_cnt = 0;
data->mode_cnt.cgm_cnt = 0;
data->mode_cnt.unkn_cnt = 0;
return size;
}
static ssize_t max86915_factory_cmd_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct max86915_device_data *data = dev_get_drvdata(dev);
static int cmd_result;
mutex_lock(&data->activelock);
if (data->isTrimmed)
cmd_result = 1;
else
cmd_result = 0;
HRM_dbg("%s - cmd_result = %d\n", __func__, cmd_result);
mutex_unlock(&data->activelock);
return snprintf(buf, PAGE_SIZE, "%d\n", cmd_result);
}
static ssize_t max86915_version_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
HRM_info("%s - cmd_result = %s.%s.%s%s\n", __func__,
VERSION, SUB_VERSION, HEADER_VERSION, VENDOR_VERSION);
return snprintf(buf, PAGE_SIZE, "%s.%s.%s%s\n",
VERSION, SUB_VERSION, HEADER_VERSION, VENDOR_VERSION);
}
static ssize_t max86915_sensor_info_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
HRM_dbg("%s - sensor_info_data not support\n", __func__);
return snprintf(buf, PAGE_SIZE, "NOT SUPPORT\n");
}
static ssize_t max86915_xtalk_code_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
int err;
int xtalk1, xtalk2, xtalk3, xtalk4;
u32 xtalk_code;
struct max86915_device_data *data = dev_get_drvdata(dev);
mutex_lock(&data->activelock);
err = sscanf(buf, "%8x", &xtalk_code);
if (err < 0) {
HRM_err("%s - sscanf failed, err = %x\n", __func__, err);
mutex_unlock(&data->activelock);
return err;
}
xtalk1 = 0x0000001f & xtalk_code;
xtalk2 = (0x00001f00 & xtalk_code) >> 8;
xtalk3 = (0x001f0000 & xtalk_code) >> 16;
xtalk4 = (0x1f000000 & xtalk_code) >> 24;
HRM_info("%s - 0x%02x, 0x%02x, 0x%02x, 0x%02x\n", __func__, xtalk1, xtalk2, xtalk3, xtalk4);
err = max86915_set_xtalk(xtalk1, xtalk2, xtalk3, xtalk4);
if (err < 0) {
HRM_err("%s - set xtalk failed, err = %x\n", __func__, err);
mutex_unlock(&data->activelock);
return err;
}
mutex_unlock(&data->activelock);
return size;
}
static ssize_t max86915_xtalk_code_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
u32 xtalk_code;
struct max86915_device_data *data = dev_get_drvdata(dev);
mutex_lock(&data->activelock);
xtalk_code = (data->xtalk_code1 & 0x1f) | ((data->xtalk_code2 & 0x1f) << 8)
| ((data->xtalk_code3 & 0x1f) << 16) | ((data->xtalk_code4 & 0x1f) << 24);
mutex_unlock(&data->activelock);
HRM_info("%s - xtalk1 0x%02x, xtalk2 0x%02x, xtalk3 0x%02x, xtalk4 0x%02x\n",
__func__, data->xtalk_code1, data->xtalk_code2, data->xtalk_code3, data->xtalk_code4);
return snprintf(buf, PAGE_SIZE, "%08X\n", xtalk_code);
}
static DEVICE_ATTR(name, S_IRUGO, max86915_name_show, NULL);
static DEVICE_ATTR(vendor, S_IRUGO, max86915_vendor_show, NULL);
static DEVICE_ATTR(led_current, S_IRUGO | S_IWUSR | S_IWGRP,
max86915_led_current_show, max86915_led_current_store);
static DEVICE_ATTR(led_test, S_IRUGO, max86915_led_test_show, NULL);
static DEVICE_ATTR(hrm_flush, S_IWUSR | S_IWGRP, NULL, max86915_flush_store);
static DEVICE_ATTR(int_pin_check, S_IRUGO, max86915_int_pin_check_show, NULL);
static DEVICE_ATTR(lib_ver, S_IRUGO | S_IWUSR | S_IWGRP,
max86915_lib_ver_show, max86915_lib_ver_store);
static DEVICE_ATTR(threshold, S_IRUGO | S_IWUSR | S_IWGRP,
max86915_threshold_show, max86915_threshold_store);
static DEVICE_ATTR(prox_thd, S_IRUGO | S_IWUSR | S_IWGRP,
max86915_prox_thd_show, max86915_prox_thd_store);
static DEVICE_ATTR(eol_test, S_IRUGO | S_IWUSR | S_IWGRP,
max86915_eol_test_show, max86915_eol_test_store);
static DEVICE_ATTR(eol_test_result, S_IRUGO, max86915_eol_test_result_show, NULL);
static DEVICE_ATTR(eol_test_status, S_IRUGO, max86915_eol_test_status_show, NULL);
static DEVICE_ATTR(pre_eol_test, S_IRUGO | S_IWUSR | S_IWGRP,
max86915_pre_eol_test_show, max86915_pre_eol_test_store);
static DEVICE_ATTR(read_reg, S_IRUGO | S_IWUSR | S_IWGRP,
max86915_read_reg_show, max86915_read_reg_store);
static DEVICE_ATTR(write_reg, S_IWUSR | S_IWGRP, NULL, max86915_write_reg_store);
static DEVICE_ATTR(hrm_debug, S_IRUGO | S_IWUSR | S_IWGRP,
max86915_debug_show, max86915_debug_store);
static DEVICE_ATTR(device_id, S_IRUGO, max86915_device_id_show, NULL);
static DEVICE_ATTR(part_type, S_IRUGO, max86915_part_type_show, NULL);
static DEVICE_ATTR(i2c_err_cnt, S_IRUGO | S_IWUSR | S_IWGRP, max86915_i2c_err_show, max86915_i2c_err_store);
static DEVICE_ATTR(curr_adc, S_IRUGO | S_IWUSR | S_IWGRP, max86915_curr_adc_show, max86915_curr_adc_store);
static DEVICE_ATTR(mode_cnt, S_IRUGO | S_IWUSR | S_IWGRP, max86915_mode_cnt_show, max86915_mode_cnt_store);
static DEVICE_ATTR(hrm_factory_cmd, S_IRUGO, max86915_factory_cmd_show, NULL);
static DEVICE_ATTR(hrm_version, S_IRUGO, max86915_version_show, NULL);
static DEVICE_ATTR(sensor_info, S_IRUGO, max86915_sensor_info_show, NULL);
static DEVICE_ATTR(xtalk_code, S_IRUGO | S_IWUSR | S_IWGRP,
max86915_xtalk_code_show, max86915_xtalk_code_store);
static struct device_attribute *max86915_sensor_attrs[] = {
&dev_attr_name,
&dev_attr_vendor,
&dev_attr_led_current,
&dev_attr_led_test,
&dev_attr_hrm_flush,
&dev_attr_int_pin_check,
&dev_attr_lib_ver,
&dev_attr_threshold,
&dev_attr_prox_thd,
&dev_attr_eol_test,
&dev_attr_eol_test_result,
&dev_attr_eol_test_status,
&dev_attr_pre_eol_test,
&dev_attr_read_reg,
&dev_attr_write_reg,
&dev_attr_device_id,
&dev_attr_part_type,
&dev_attr_i2c_err_cnt,
&dev_attr_curr_adc,
&dev_attr_mode_cnt,
&dev_attr_hrm_debug,
&dev_attr_hrm_factory_cmd,
&dev_attr_hrm_version,
&dev_attr_sensor_info,
&dev_attr_xtalk_code,
NULL,
};
irqreturn_t max86915_irq_handler(int dev_irq, void *device)
{
int err;
struct max86915_device_data *data = device;
struct output_data read_data;
int i;
static unsigned int sample_cnt;
HRM_info("%s - hrm_irq = %d\n", __func__, dev_irq);
memset(&read_data, 0, sizeof(struct output_data));
if (data->regulator_state == 0 || data->enabled_mode == 0 || data->mode_svc_enabled != 0) {
HRM_dbg("%s - stop irq handler (reg_state : %d, enabled_mode : %d, rear_led : 0x%x)\n",
__func__, data->regulator_state, data->enabled_mode, data->mode_svc_enabled);
return IRQ_HANDLED;
}
#ifdef CONFIG_ARCH_QCOM
pm_qos_add_request(&data->pm_qos_req_fpm, PM_QOS_CPU_DMA_LATENCY,
PM_QOS_DEFAULT_VALUE);
#endif
err = max86915_read_data(&read_data);
if (err == 0) {
if (data->hrm_input_dev == NULL) {
HRM_err("%s - hrm_input_dev is NULL\n", __func__);
} else {
if (read_data.fifo_num) {
for (i = 0; i < read_data.fifo_num; i++) {
input_report_rel(data->hrm_input_dev,
REL_X, read_data.fifo_main[0][i] + 1);
input_report_rel(data->hrm_input_dev,
REL_Y, read_data.fifo_main[1][i] + 1);
input_report_rel(data->hrm_input_dev,
REL_Z, read_data.fifo_main[2][i] + 1);
input_report_rel(data->hrm_input_dev,
REL_RX, read_data.fifo_main[3][i] + 1);
input_sync(data->hrm_input_dev);
}
} else {
for (i = 0; i < read_data.main_num; i++)
input_report_rel(data->hrm_input_dev,
REL_X + i, read_data.data_main[i] + 1);
for (i = 0; i < read_data.sub_num; i++)
input_report_abs(data->hrm_input_dev,
ABS_X + i, read_data.data_sub[i] + 1);
if (read_data.main_num || read_data.sub_num)
input_sync(data->hrm_input_dev);
}
if (sample_cnt++ > 100) {
HRM_dbg("%s - mode:0x%x main:%d,%d,%d,%d sub:%d,%d,%d,%d,%d,%d,%d,%d\n", __func__,
read_data.mode, read_data.data_main[0], read_data.data_main[1],
read_data.data_main[2], read_data.data_main[3], read_data.data_sub[0],
read_data.data_sub[1], read_data.data_sub[2], read_data.data_sub[3], read_data.data_sub[4],
read_data.data_sub[5], read_data.data_sub[6], read_data.data_sub[7]);
sample_cnt = 0;
} else {
HRM_info("%s - mode:0x%x main:%d,%d,%d,%d sub:%d,%d,%d,%d,%d,%d,%d,%d\n", __func__,
read_data.mode, read_data.data_main[0], read_data.data_main[1],
read_data.data_main[2], read_data.data_main[3], read_data.data_sub[0],
read_data.data_sub[1], read_data.data_sub[2], read_data.data_sub[3], read_data.data_sub[4],
read_data.data_sub[5], read_data.data_sub[6], read_data.data_sub[7]);
}
}
}
#ifdef CONFIG_ARCH_QCOM
pm_qos_remove_request(&data->pm_qos_req_fpm);
#endif
return IRQ_HANDLED;
}
static int max86915_setup_irq(struct max86915_device_data *data)
{
int errorno = -EIO;
errorno = request_threaded_irq(data->dev_irq, NULL,
max86915_irq_handler, IRQF_TRIGGER_FALLING|IRQF_ONESHOT,
"hrm_sensor_irq", data);
if (errorno < 0) {
HRM_err("%s - failed for setup dev_irq errono= %d\n",
__func__, errorno);
errorno = -ENODEV;
return errorno;
}
max86915_pin_control(data, false);
return errorno;
}
static void max86915_init_var1(struct max86915_device_data *data)
{
data->client = NULL;
data->dev = NULL;
data->hrm_input_dev = NULL;
data->hrm_pinctrl = NULL;
data->pins_sleep = NULL;
data->pins_idle = NULL;
data->led_3p3 = NULL;
data->vdd_1p8 = NULL;
data->i2c_1p8 = NULL;
data->enabled_mode = 0;
data->sampling_period_ns = 0;
data->mode_sdk_enabled = 0;
data->mode_svc_enabled = 0;
data->regulator_state = 0;
data->irq_state = 0;
data->hrm_threshold = DEFAULT_THRESHOLD;
data->eol_test_is_enable = 0;
data->eol_test_status = 0;
data->pre_eol_test_is_enable = 0;
data->reg_read_buf = 0;
data->lib_ver = NULL;
data->pm_state = PM_RESUME;
}
static int max86915_init_var2(struct max86915_device_data *data)
{
int err;
u8 buffer[2] = {0, };
err = max86915_read_reg(data, MAX86915_REV_ID_REG, buffer, 2);
if (err) {
HRM_err("%s MAX86915 WHOAMI read fail0\n", __func__);
return -EINVAL;
}
data->threshold_default = MAX86915_THRESHOLD_DEFAULT;
data->i2c_err_cnt = 0;
data->ir_curr = 0;
data->red_curr = 0;
data->green_curr = 0;
data->blue_curr = 0;
data->ir_adc = 0;
data->red_adc = 0;
data->green_adc = 0;
data->blue_adc = 0;
data->led_range[0] = MAX86915_DEFAULT_LED_RGE1;
data->led_range[1] = MAX86915_DEFAULT_LED_RGE2;
data->led_range[2] = MAX86915_DEFAULT_LED_RGE3;
data->led_range[3] = MAX86915_DEFAULT_LED_RGE4;
if (data->init_current[AGC_IR] == 0) {
data->init_current[AGC_IR] = MAX86915_IR_INIT_CURRENT;
data->init_current[AGC_RED] = MAX86915_RED_INIT_CURRENT;
data->init_current[AGC_GREEN] = MAX86915_GREEN_INIT_CURRENT;
data->init_current[AGC_BLUE] = MAX86915_BLUE_INIT_CURRENT;
}
if (buffer[1] == MAX86915_PART_ID) {
data->default_current1 = data->init_current[AGC_IR];
data->default_current2 = data->init_current[AGC_RED];
data->default_current3 = data->init_current[AGC_GREEN];
data->default_current4 = data->init_current[AGC_BLUE];
data->default_xtalk_code1 = MAX86915_DEFAULT_XTALK_CODE1;
data->default_xtalk_code2 = MAX86915_DEFAULT_XTALK_CODE2;
data->default_xtalk_code3 = MAX86915_DEFAULT_XTALK_CODE3;
data->default_xtalk_code4 = MAX86915_DEFAULT_XTALK_CODE4;
data->part_type = max86915_get_part_id(data);
} else {
HRM_err("%s MAX86915 WHOAMI read fail\n", __func__);
return -EINVAL;
}
/* AGC setting */
data->update_led = max86915_update_led_current;
data->agc_led_out_percent = MAX86915_AGC_DEFAULT_LED_OUT_RANGE;
data->agc_corr_coeff = MAX86915_AGC_DEFAULT_CORRECTION_COEFF;
data->agc_min_num_samples = MAX86915_AGC_DEFAULT_MIN_NUM_PERCENT;
data->agc_sensitivity_percent = MAX86915_AGC_DEFAULT_SENSITIVITY_PERCENT;
data->threshold_default = MAX86915_THRESHOLD_DEFAULT;
data->remove_cnt[AGC_IR] = 0;
data->remove_cnt[AGC_RED] = 0;
data->remove_cnt[AGC_GREEN] = 0;
data->remove_cnt[AGC_BLUE] = 0;
return 0;
}
static int max86915_parse_dt(struct max86915_device_data *data)
{
struct device *dev = &data->client->dev;
struct device_node *dNode = dev->of_node;
enum of_gpio_flags flags;
u32 threshold[2];
int i;
if (dNode == NULL)
return -ENODEV;
data->pin_hrm_int = of_get_named_gpio_flags(dNode,
"hrmsensor,hrm_int-gpio", 0, &flags);
if (data->pin_hrm_int < 0) {
HRM_err("%s - get hrm_int error\n", __func__);
return -ENODEV;
}
data->pin_hrm_en = of_get_named_gpio_flags(dNode,
"hrmsensor,hrm_boost_en-gpio", 0, &flags);
if (data->pin_hrm_en < 0) {
HRM_err("%s - get hrm_en error\n", __func__);
return -ENODEV;
}
if (of_property_read_string(dNode, "hrmsensor,vdd_1p8",
(char const **)&data->vdd_1p8) < 0)
HRM_dbg("%s - vdd_1p8 doesn`t exist\n", __func__);
if (of_property_read_string(dNode, "hrmsensor,i2c_1p8",
(char const **)&data->i2c_1p8) < 0)
HRM_dbg("%s - i2c_1p8 doesn`t exist\n", __func__);
data->hrm_pinctrl = devm_pinctrl_get(dev);
if (IS_ERR_OR_NULL(data->hrm_pinctrl)) {
HRM_err("%s - get pinctrl(%li) error\n",
__func__, PTR_ERR(data->hrm_pinctrl));
data->hrm_pinctrl = NULL;
return -EINVAL;
}
data->pins_sleep =
pinctrl_lookup_state(data->hrm_pinctrl, "sleep");
if (IS_ERR_OR_NULL(data->pins_sleep)) {
HRM_err("%s - get pins_sleep(%li) error\n",
__func__, PTR_ERR(data->pins_sleep));
devm_pinctrl_put(data->hrm_pinctrl);
data->pins_sleep = NULL;
return -EINVAL;
}
data->pins_idle =
pinctrl_lookup_state(data->hrm_pinctrl, "idle");
if (IS_ERR_OR_NULL(data->pins_idle)) {
HRM_err("%s - get pins_idle(%li) error\n",
__func__, PTR_ERR(data->pins_idle));
devm_pinctrl_put(data->hrm_pinctrl);
data->pins_idle = NULL;
return -EINVAL;
}
data->always_on = of_property_read_bool(dev->of_node, "hrmsensor,always");
if (data->always_on)
HRM_dbg("%s - VDD_1p8 always on\n", __func__);
if (of_property_read_u32_array(dNode, "hrmsensor,thd",
threshold, ARRAY_SIZE(threshold)) < 0) {
HRM_err("%s - get threshold error\n", __func__);
} else {
data->hrm_threshold = threshold[0];
data->prox_threshold = threshold[1];
}
HRM_dbg("%s - hrm_threshold = %d, prox_threshold = %d\n",
__func__, data->hrm_threshold, data->prox_threshold);
if (of_property_read_u32_array(dNode, "hrmsensor,init_curr",
data->init_current, ARRAY_SIZE(data->init_current)) < 0) {
HRM_err("%s - get init_curr error\n", __func__);
data->init_current[AGC_IR] = 0;
data->init_current[AGC_RED] = 0;
data->init_current[AGC_GREEN] = 0;
data->init_current[AGC_BLUE] = 0;
}
HRM_dbg("%s - init_curr = 0x%.2x 0x%.2x 0x%.2x 0x%.2x\n", __func__,
data->init_current[AGC_IR], data->init_current[AGC_RED],
data->init_current[AGC_GREEN], data->init_current[AGC_BLUE]);
if (of_property_read_u32_array(dNode, "hrmsensor,eol",
data->eol_spec, ARRAY_SIZE(data->eol_spec)) < 0) {
HRM_err("%s - get eol_spec error\n", __func__);
}
for (i=0 ; i < ARRAY_SIZE(data->eol_spec) ; i+=2) {
HRM_info("%s - eol_spec : min : %d, max %d\n", __func__, data->eol_spec[i], data->eol_spec[i+1]);
}
if (of_property_read_u32_array(dNode, "hrmsensor,eol_semi",
data->eol_semi_spec, ARRAY_SIZE(data->eol_semi_spec)) < 0) {
HRM_err("%s - get eol_semi_spec error\n", __func__);
}
for (i=0 ; i < ARRAY_SIZE(data->eol_semi_spec) ; i+=2) {
HRM_info("%s - semi_spec : min : %d, max %d\n",
__func__, data->eol_semi_spec[i], data->eol_semi_spec[i+1]);
}
if (of_property_read_u32_array(dNode, "hrmsensor,eol_xtalk",
data->eol_xtalk_spec, ARRAY_SIZE(data->eol_xtalk_spec)) < 0) {
HRM_err("%s - get eol_xtalk_spec error\n", __func__);
}
HRM_info("%s - eol_xtalk_spec : min : %d, max %d\n", __func__, data->eol_spec[0], data->eol_spec[1]);
return 0;
}
static int max86915_setup_gpio(struct max86915_device_data *data)
{
int errorno = -EIO;
errorno = gpio_request(data->pin_hrm_int, "hrm_int");
if (errorno) {
HRM_err("%s - failed to request hrm_int\n", __func__);
return errorno;
}
errorno = gpio_direction_input(data->pin_hrm_int);
if (errorno) {
HRM_err("%s - failed to set hrm_int as input\n", __func__);
goto err_gpio_direction_input;
}
data->dev_irq = gpio_to_irq(data->pin_hrm_int);
errorno = gpio_request(data->pin_hrm_en, "hrm_en");
if (errorno) {
HRM_err("%s - failed to request hrm_en\n", __func__);
goto err_gpio_direction_input;
}
goto done;
err_gpio_direction_input:
gpio_free(data->pin_hrm_int);
done:
return errorno;
}
static int max86915_trim_check(void)
{
u8 recvData;
u8 reg93_val;
int err;
max86915_data->isTrimmed = 0;
err = max86915_write_reg(max86915_data, 0xFF, 0x54);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_MODE_TEST0!\n",
__func__);
return -EIO;
}
err = max86915_write_reg(max86915_data, 0xFF, 0x4d);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_MODE_TEST1!\n",
__func__);
return -EIO;
}
err = max86915_read_reg(max86915_data, 0x93, &recvData, 1);
if (err != 0) {
HRM_err("%s - max86915_read_reg err:%d, address:0x%02x\n",
__func__, err, recvData);
return -EIO;
}
reg93_val = recvData;
err = max86915_write_reg(max86915_data, 0xFF, 0x00);
if (err != 0) {
HRM_err("%s - error initializing MAX86915_MODE_TEST3!\n",
__func__);
return -EIO;
}
max86915_data->isTrimmed = (reg93_val & 0x20);
HRM_dbg("%s - isTrimmed :%d, reg93_val : %d\n", __func__, max86915_data->isTrimmed, reg93_val);
return err;
}
int max86915_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
int err = -ENODEV;
struct max86915_device_data *data;
HRM_dbg("%s - start\n", __func__);
/* check to make sure that the adapter supports I2C */
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
HRM_err("%s - I2C_FUNC_I2C not supported\n", __func__);
return -ENODEV;
}
/* allocate some memory for the device */
data = kzalloc(sizeof(struct max86915_device_data), GFP_KERNEL);
if (data == NULL) {
HRM_err("%s - couldn't allocate device data memory\n", __func__);
return -ENOMEM;
}
data->flicker_data = kzalloc(sizeof(int)*FLICKER_DATA_CNT, GFP_KERNEL);
if (data->flicker_data == NULL) {
HRM_err("%s - couldn't allocate flicker data memory\n", __func__);
goto err_flicker_alloc_fail;
}
data->fifo_buf = kzalloc(NUM_BYTES_PER_SAMPLE*MAX86915_FIFO_SIZE*MAX_LED_NUM, GFP_KERNEL);
if (data->fifo_buf == NULL) {
HRM_err("%s - couldn't allocate fifo_buf data memory\n", __func__);
goto err_fifo_buf_alloc_fail;
}
max86915_data = data;
max86915_init_var1(data);
data->client = client;
data->miscdev.minor = MISC_DYNAMIC_MINOR;
data->miscdev.name = "max_hrm";
data->miscdev.fops = &max86915_fops;
data->miscdev.mode = S_IRUGO;
i2c_set_clientdata(client, data);
HRM_info("%s client = %p\n", __func__, client);
err = misc_register(&data->miscdev);
if (err < 0) {
HRM_err("%s - failed to misc device register\n", __func__);
goto err_misc_register;
}
mutex_init(&data->i2clock);
mutex_init(&data->activelock);
mutex_init(&data->suspendlock);
mutex_init(&data->flickerdatalock);
err = max86915_parse_dt(data);
if (err < 0) {
HRM_err("%s - failed to parse dt\n", __func__);
err = -ENODEV;
goto err_parse_dt;
}
err = max86915_setup_gpio(data);
if (err) {
HRM_err("%s - failed to setup gpio\n", __func__);
goto err_setup_gpio;
}
err = max86915_power_ctrl(data, PWR_ON);
if (err < 0) {
HRM_err("%s - failed to power on ctrl\n", __func__);
goto err_power_on;
}
err = max86915_led_power_ctrl(data, PWR_ON);
if (err) {
HRM_err("%s - max86915_led_power_ctrl fail err = %d\n", __func__, err);
goto err_led_power_ctrl_fail;
}
if (data->client->addr != SLAVE_ADDR_MAX) {
err = -EIO;
HRM_err("%s - slave address error, 0x%02x\n", __func__, data->client->addr);
goto err_init_fail;
}
err = max86915_init_var2(data);
if (err < 0) {
HRM_err("%s - failed to init_var2\n", __func__);
goto err_init_fail;
}
err = max86915_init_status(data);
if (err < 0) {
HRM_err("%s - failed to init_status\n", __func__);
goto err_init_fail;
}
max86915_trim_check();
data->hrm_input_dev = input_allocate_device();
if (!data->hrm_input_dev) {
HRM_err("%s - could not allocate input device\n", __func__);
goto err_input_allocate_device;
}
data->hrm_input_dev->name = MODULE_NAME_HRM;
input_set_drvdata(data->hrm_input_dev, data);
input_set_capability(data->hrm_input_dev, EV_REL, REL_X);
input_set_capability(data->hrm_input_dev, EV_REL, REL_Y);
input_set_capability(data->hrm_input_dev, EV_REL, REL_Z);
input_set_capability(data->hrm_input_dev, EV_REL, REL_RX);
input_set_capability(data->hrm_input_dev, EV_REL, REL_MISC);
input_set_capability(data->hrm_input_dev, EV_ABS, ABS_X);
input_set_capability(data->hrm_input_dev, EV_ABS, ABS_Y);
input_set_capability(data->hrm_input_dev, EV_ABS, ABS_Z);
input_set_capability(data->hrm_input_dev, EV_ABS, ABS_RX);
input_set_capability(data->hrm_input_dev, EV_ABS, ABS_RY);
input_set_capability(data->hrm_input_dev, EV_ABS, ABS_RZ);
input_set_capability(data->hrm_input_dev, EV_ABS, ABS_THROTTLE);
input_set_capability(data->hrm_input_dev, EV_ABS, ABS_RUDDER);
err = input_register_device(data->hrm_input_dev);
if (err < 0) {
input_free_device(data->hrm_input_dev);
HRM_err("%s - could not register input device\n", __func__);
goto err_input_register_device;
}
#ifdef CONFIG_ARCH_QCOM
err = sensors_create_symlink(&data->hrm_input_dev->dev.kobj,
data->hrm_input_dev->name);
#else
err = sensors_create_symlink(data->hrm_input_dev);
#endif
if (err < 0) {
HRM_err("%s - could not create_symlink\n", __func__);
goto err_sensors_create_symlink;
}
err = sysfs_create_group(&data->hrm_input_dev->dev.kobj,
&hrm_attribute_group);
if (err) {
HRM_err("%s - could not create sysfs group\n", __func__);
goto err_sysfs_create_group;
}
#ifdef CONFIG_ARCH_QCOM
err = sensors_register(&data->dev, data, max86915_sensor_attrs,
MODULE_NAME_HRM);
#else
err = sensors_register(data->dev, data, max86915_sensor_attrs,
MODULE_NAME_HRM);
#endif
if (err) {
HRM_err("%s - cound not register hrm_sensor(%d).\n", __func__, err);
goto hrm_sensor_register_failed;
}
err = max86915_setup_irq(data);
if (err) {
HRM_err("%s - could not setup dev_irq\n", __func__);
goto err_setup_irq;
}
err = max86915_disable(data, MODE_NONE);
if (err != 0) {
HRM_err("%s - max86915_disable err : %d\n", __func__, err);
goto dev_set_drvdata_failed;
}
err = max86915_led_power_ctrl(data, PWR_OFF);
if (err) {
HRM_err("%s - failed to power off ctrl\n", __func__);
goto dev_set_drvdata_failed;
}
if (!data->always_on) {
err = max86915_power_ctrl(data, PWR_OFF);
if (err < 0) {
HRM_err("%s - failed to power off ctrl\n", __func__);
goto dev_set_drvdata_failed;
}
}
HRM_dbg("%s - success\n", __func__);
goto done;
dev_set_drvdata_failed:
free_irq(data->dev_irq, data);
err_setup_irq:
sensors_unregister(data->dev, max86915_sensor_attrs);
hrm_sensor_register_failed:
sysfs_remove_group(&data->hrm_input_dev->dev.kobj,
&hrm_attribute_group);
err_sysfs_create_group:
#ifdef CONFIG_ARCH_QCOM
sensors_remove_symlink(&data->hrm_input_dev->dev.kobj,
data->hrm_input_dev->name);
#else
sensors_remove_symlink(data->hrm_input_dev);
#endif
err_sensors_create_symlink:
input_unregister_device(data->hrm_input_dev);
err_input_register_device:
err_input_allocate_device:
err_init_fail:
max86915_led_power_ctrl(data, PWR_OFF);
err_led_power_ctrl_fail:
max86915_power_ctrl(data, PWR_OFF);
err_power_on:
gpio_free(data->pin_hrm_int);
gpio_free(data->pin_hrm_en);
err_setup_gpio:
err_parse_dt:
if (data->hrm_pinctrl) {
devm_pinctrl_put(data->hrm_pinctrl);
data->hrm_pinctrl = NULL;
}
if (data->pins_idle)
data->pins_idle = NULL;
if (data->pins_sleep)
data->pins_sleep = NULL;
mutex_destroy(&data->i2clock);
mutex_destroy(&data->activelock);
mutex_destroy(&data->suspendlock);
mutex_destroy(&data->flickerdatalock);
misc_deregister(&data->miscdev);
err_misc_register:
kfree(data->fifo_buf);
err_fifo_buf_alloc_fail:
kfree(data->flicker_data);
err_flicker_alloc_fail:
kfree(data);
HRM_err("%s failed\n", __func__);
done:
return err;
}
int max86915_remove(struct i2c_client *client)
{
struct max86915_device_data *data = i2c_get_clientdata(client);
HRM_dbg("%s - start\n", __func__);
max86915_power_ctrl(data, PWR_OFF);
sensors_unregister(data->dev, max86915_sensor_attrs);
sysfs_remove_group(&data->hrm_input_dev->dev.kobj,
&hrm_attribute_group);
#ifdef CONFIG_ARCH_QCOM
sensors_remove_symlink(&data->hrm_input_dev->dev.kobj,
data->hrm_input_dev->name);
#else
sensors_remove_symlink(data->hrm_input_dev);
#endif
input_unregister_device(data->hrm_input_dev);
if (data->hrm_pinctrl) {
devm_pinctrl_put(data->hrm_pinctrl);
data->hrm_pinctrl = NULL;
}
if (data->pins_idle)
data->pins_idle = NULL;
if (data->pins_sleep)
data->pins_sleep = NULL;
disable_irq(data->dev_irq);
free_irq(data->dev_irq, data);
gpio_free(data->pin_hrm_int);
gpio_free(data->pin_hrm_en);
mutex_destroy(&data->i2clock);
mutex_destroy(&data->activelock);
mutex_destroy(&data->suspendlock);
mutex_destroy(&data->flickerdatalock);
misc_deregister(&data->miscdev);
if (data->eol_test_result != NULL)
kfree(data->eol_test_result);
kfree(data->fifo_buf);
kfree(data->flicker_data);
kfree(data->lib_ver);
kfree(data);
i2c_set_clientdata(client, NULL);
data = NULL;
return 0;
}
static void max86915_shutdown(struct i2c_client *client)
{
HRM_dbg("%s - start\n", __func__);
}
#ifdef CONFIG_PM
static int max86915_pm_suspend(struct device *dev)
{
struct max86915_device_data *data = dev_get_drvdata(dev);
int err = 0;
HRM_dbg("%s - %d\n", __func__, data->enabled_mode);
if (data->enabled_mode == MODE_SVC_IR) {
return err;
}
if (data->enabled_mode != 0 || data->regulator_state != 0) {
mutex_lock(&data->activelock);
err = max86915_disable(data, MODE_NONE);
if (err != 0)
HRM_err("%s - disable err : %d\n", __func__, err);
err = max86915_led_power_ctrl(data, PWR_OFF);
if (err)
HRM_err("%s - failed to power off ctrl\n", __func__);
if (!data->always_on) {
err = max86915_power_ctrl(data, PWR_OFF);
if (err < 0)
HRM_err("%s - hrm_regulator_off fail err = %d\n",
__func__, err);
}
max86915_irq_set_state(data, PWR_OFF);
mutex_unlock(&data->activelock);
}
mutex_lock(&data->suspendlock);
data->pm_state = PM_SUSPEND;
mutex_unlock(&data->suspendlock);
return err;
}
static int max86915_pm_resume(struct device *dev)
{
struct max86915_device_data *data = dev_get_drvdata(dev);
int err = 0;
HRM_dbg("%s - %d\n", __func__, data->enabled_mode);
if (data->enabled_mode == MODE_SVC_IR) {
return err;
}
mutex_lock(&data->suspendlock);
data->pm_state = PM_RESUME;
mutex_unlock(&data->suspendlock);
if (data->enabled_mode != 0) {
mutex_lock(&data->activelock);
max86915_irq_set_state(data, PWR_ON);
if (!data->always_on) {
err = max86915_power_ctrl(data, PWR_ON);
if (err < 0)
HRM_err("%s - hrm_regulator_on fail err = %d\n", __func__, err);
}
err = max86915_led_power_ctrl(data, PWR_ON);
if (err < 0)
HRM_err("%s - failed to power off ctrl\n", __func__);
err = max86915_enable(data, data->enabled_mode);
if (err != 0)
HRM_err("%s - enable err : %d\n", __func__, err);
if (err < 0 && data->enabled_mode == MODE_AMBIENT) {
input_report_rel(data->hrm_input_dev,
REL_Y, -5 + 1); /* F_ERR_I2C -5 detected i2c error */
input_sync(data->hrm_input_dev);
HRM_err("%s - awb mode enable error : %d\n", __func__, err);
}
mutex_unlock(&data->activelock);
}
return err;
}
static const struct dev_pm_ops max86915_pm_ops = {
.suspend = max86915_pm_suspend,
.resume = max86915_pm_resume
};
#endif
static const struct of_device_id max86915_match_table[] = {
{ .compatible = "hrm_max",},
{},
};
static const struct i2c_device_id max86915_device_id[] = {
{ "max86915", 0 },
{ }
};
/* descriptor of the hrmsensor I2C driver */
static struct i2c_driver max86915_i2c_driver = {
.driver = {
.name = "max86915",
.owner = THIS_MODULE,
#if defined(CONFIG_PM)
.pm = &max86915_pm_ops,
#endif
.of_match_table = max86915_match_table,
},
.probe = max86915_probe,
.remove = max86915_remove,
.shutdown = max86915_shutdown,
.id_table = max86915_device_id,
};
/* initialization and exit functions */
static int __init max86915_init(void)
{
if (!lpcharge)
return i2c_add_driver(&max86915_i2c_driver);
else
return 0;
}
static void __exit max86915_exit(void)
{
i2c_del_driver(&max86915_i2c_driver);
}
module_init(max86915_init);
module_exit(max86915_exit);
MODULE_DESCRIPTION("max86915 Driver");
MODULE_AUTHOR("Samsung Electronics");
MODULE_LICENSE("GPL");