blob: c02aaa126334308e5b440f2f904979edfdabd7ff [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/>.
*/
#ifndef _MAX86915_H_
#define _MAX86915_H_
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/irq.h>
#include <linux/input.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/interrupt.h>
#include <linux/leds.h>
#include <linux/miscdevice.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/uaccess.h>
#include <linux/workqueue.h>
#include <linux/spinlock.h>
#include <linux/gpio.h>
#include <linux/kthread.h>
#include <linux/sched.h>
#include <linux/time.h>
#include <linux/timer.h>
#include <linux/file.h>
#include <linux/syscalls.h>
#include <linux/pinctrl/consumer.h>
#include <linux/pm_qos.h>
#ifdef CONFIG_OF
#include <linux/of_gpio.h>
#endif
#define HEADER_VERSION "14"
/* MAX86915 Register Map */
#define MAX86915_INTERRUPT_STATUS 0x00
#define PWR_RDY_MASK 0x01
#define PROX_INT_MASK 0x10
#define ALC_OVF_MASK 0x20
#define PPG_RDY_MASK 0x40
#define A_FULL_MASK 0x80
#define MAX86915_INTERRUPT_STATUS_2 0x01
#define MAX86915_INTERRUPT_ENABLE 0x02
#define MAX86915_INTERRUPT_ENABLE_2 0x03
#define MAX86915_FIFO_WRITE_POINTER 0x04
#define MAX86915_OVF_COUNTER 0x05
#define MAX86915_FIFO_READ_POINTER 0x06
#define MAX86915_FIFO_DATA 0x07
#define MAX86915_FIFO_CONFIG 0x08
#define MAX86915_FIFO_A_FULL_MASK 0x0F
#define MAX86915_FIFO_A_FULL_OFFSET 0x00
#define MAX86915_FIFO_ROLLS_ON_MASK 0x10
#define MAX86915_FIFO_ROLLS_ON_OFFSET 0x04
#define MAX86915_SMP_AVE_MASK 0xE0
#define MAX86915_SMP_AVE_OFFSET 0x05
#define MAX86915_MODE_CONFIGURATION 0x09
#define MAX86915_MODE_MASK 0x03
#define MAX86915_MODE_OFFSET 0x00
#define MAX86915_AMB_LIGHT_DET_MASK 0x04
#define MAX86915_AMB_LIGHT_DET_OFFSET 0x02
#define MAX86915_RESET_MASK 0x40
#define MAX86915_RESET_OFFSET 0x06
#define MAX86915_SHDN_MASK 0x80
#define MAX86915_SHDN_OFFSET 0x07
#define MAX86915_MODE_CONFIGURATION_2 0x0A
#define MAX86915_LED_PW_MASK 0x03
#define MAX86915_LED_PW_OFFSET 0x00
#define MAX86915_SR_MASK 0x1C
#define MAX86915_SR_OFFSET 0x02
#define MAX86915_ADC_RGE_MASK 0x60
#define MAX86915_ADC_RGE_OFFSET 0x05
#define MAX86915_LED1_PA 0x0C
#define MAX86915_LED2_PA 0x0D
#define MAX86915_LED3_PA 0x0E
#define MAX86915_LED4_PA 0x0F
#define MAX86915_LED_RANGE 0x11
#define MAX86915_LED1_RGE_MASK 0x03
#define MAX86915_LED1_RGE_OFFSET 0x00
#define MAX86915_LED2_RGE_MASK 0x0C
#define MAX86915_LED2_RGE_OFFSET 0x02
#define MAX86915_LED3_RGE_MASK 0x30
#define MAX86915_LED3_RGE_OFFSET 0x04
#define MAX86915_LED4_RGE_MASK 0xC0
#define MAX86915_LED4_RGE_OFFSET 0x06
#define MAX86915_PILOT_PA 0x12
#define MAX86915_LED_SEQ_REG_1 0x13
#define MAX86915_LED1_MASK 0x0F
#define MAX86915_LED1_OFFSET 0x00
#define MAX86915_LED2_MASK 0xF0
#define MAX86915_LED2_OFFSET 0x04
#define MAX86915_LED_SEQ_REG_2 0x14
#define MAX86915_LED3_MASK 0x0F
#define MAX86915_LED3_OFFSET 0x00
#define MAX86915_LED4_MASK 0xF0
#define MAX86915_LED4_OFFSET 0x04
#define MAX86915_DAC1_XTALK_CODE 0x26
#define MAX86915_DAC2_XTALK_CODE 0x27
#define MAX86915_DAC3_XTALK_CODE 0x28
#define MAX86915_DAC4_XTALK_CODE 0x29
#define MAX86915_REV_ID_REG 0xFE
#define MAX86915_PART_ID_REG 0xFF
/* Fixed Value Definition */
#define IR_LED_CH 1
#define RED_LED_CH 2
#define GREEN_LED_CH 3
#define BLUE_LED_CH 4
#define MAX_LED_NUM 4
#define NUM_BYTES_PER_SAMPLE 3
#define MAX86915_COUNT_MAX 65532
#define MAX86915_FIFO_SIZE 32
#define PWR_ON 1
#define PWR_OFF 0
#define PM_RESUME 1
#define PM_SUSPEND 0
#define NAME_LEN 32
#define MAX_EOL_RESULT 1024
#define MAX_EOL_SPEC 36
#define MAX_EOL_XTALK_SPEC 2
/* End of Line Test */
#define SELF_IR_CH 0
#define SELF_RED_CH 1
#define SELF_GREEN_CH 2
#define SELF_BLUE_CH 3
#define SELF_MAX_CH_NUM 4
#define CONVER_FLOAT 65536
#define INTEGER_LEN 16
#define DECIMAL_LEN 10
#define MAX_I2C_BLOCK_SIZE 252
#define SELF_MODE_400HZ 0
#define SELF_MODE_100HZ 1
#define SELF_DIVID_400HZ 1
#define SELF_DIVID_100HZ 4
#define EOL_ITEM_CNT 18
#define EOL_SYSTEM_NOISE "system noise"
#define EOL_IR_MID "IR Mid DC Level"
#define EOL_RED_MID "RED Mid DC Level"
#define EOL_GREEN_MID "GREEN Mid DC Level"
#define EOL_BLUE_MID "BLUE Mid DC Level"
#define EOL_IR_HIGH "IR High DC Level"
#define EOL_RED_HIGH "RED High DC Level"
#define EOL_GREEN_HIGH "GREEN High DC Level"
#define EOL_BLUE_HIGH "BLUE High DC Level"
#define EOL_IR_XTALK "IR X-talk Cancell DC Level"
#define EOL_RED_XTALK "RED X-talk Cancell DC Level"
#define EOL_GREEN_XTALK "GREEN X-talk Cancell DC Level"
#define EOL_BLUE_XTALK "BLUE X-talk Cancell DC Level"
#define EOL_IR_NOISE "IR Noise"
#define EOL_RED_NOISE "RED Noise"
#define EOL_GREEN_NOISE "GREEN Noise"
#define EOL_BLUE_NOISE "BLUE Noise"
#define EOL_FREQUENCY "Frequency"
#define EOL_HRM_XTALK "HRM WINDOW X-TALK"
#define EOL_PASS_FAIL 0
#define EOL_MEASURE 1
#define EOL_SPEC_MIN 2
#define EOL_SPEC_MAX 3
#define EOL_SPEC_NUM_MAX 4
#define EOL_15_MODE 0
#define EOL_SEMI_FT 1
#define EOL_XTALK 2
#define EOL_XTALK_SIZE 50
#define EOL_XTALK_SKIP_CNT 50
/* #define HRM_DBG */
/* #define HRM_INFO */
#ifndef HRM_dbg
#ifdef HRM_DBG
#define HRM_dbg(format, arg...) \
printk(KERN_DEBUG "HRM_dbg : "format, ##arg);
#define HRM_err(format, arg...) \
printk(KERN_DEBUG "HRM_err : "format, ##arg);
#else
#define HRM_dbg(format, arg...) {if (hrm_debug)\
printk(KERN_DEBUG "HRM_dbg : "format, ##arg);\
}
#define HRM_err(format, arg...) {if (hrm_debug)\
printk(KERN_DEBUG "HRM_err : "format, ##arg);\
}
#endif
#endif
#ifndef HRM_info
#ifdef HRM_INFO
#define HRM_info(format, arg...) \
printk(KERN_INFO "HRM_info : "format, ##arg);
#else
#define HRM_info(format, arg...) {if (hrm_info)\
printk(KERN_INFO "HRM_info : "format, ##arg);\
}
#endif
#endif
enum {
PART_TYPE_NONE = 0,
PART_TYPE_MAX86915_ES = 30,
PART_TYPE_MAX86915_CS_15 = 31,
PART_TYPE_MAX86915_CS_21 = 32,
PART_TYPE_MAX86915_CS_22 = 33,
PART_TYPE_MAX86915_CS_211 = 34,
PART_TYPE_MAX86915_CS_221 = 35,
PART_TYPE_MAX86917_1 = 40,
PART_TYPE_MAX86917_2 = 41,
};
enum {
AWB_CONFIG0 = 0,
AWB_CONFIG1,
AWB_CONFIG2,
};
enum {
DEBUG_REG_STATUS = 1,
DEBUG_ENABLE_AGC,
DEBUG_DISABLE_AGC,
};
enum agc_mode {
M_NONE,
M_HRM,
M_SDK
};
enum {
_EOL_STATE_TYPE_NEW_INIT = 0,
_EOL_STATE_TYPE_NEW_FLICKER_INIT, /* step 1. flicker_step. */
_EOL_STATE_TYPE_NEW_FLICKER_MODE,
_EOL_STATE_TYPE_NEW_DC_INIT, /* step 2. dc_step. */
_EOL_STATE_TYPE_NEW_DC_MODE_MID,
_EOL_STATE_TYPE_NEW_DC_MODE_HIGH,
_EOL_STATE_TYPE_NEW_DC_XTALK,
_EOL_STATE_TYPE_NEW_FREQ_INIT, /* step 3. freq_step. */
_EOL_STATE_TYPE_NEW_FREQ_MODE,
_EOL_STATE_TYPE_NEW_END,
_EOL_STATE_TYPE_NEW_STOP,
_EOL_STATE_TYPE_NEW_XTALK_MODE,
};
enum op_mode {
MODE_NONE = 0,
MODE_HRM = 1,
MODE_AMBIENT = 2,
MODE_PROX = 3,
MODE_SDK_IR = 10,
MODE_SDK_RED = 11,
MODE_SDK_GREEN = 12,
MODE_SDK_BLUE = 13,
MODE_SVC_IR = 14,
MODE_SVC_RED = 15,
MODE_SVC_GREEN = 16,
MODE_SVC_BLUE = 17,
MODE_UNKNOWN
};
/* to protect the system performance issue. */
union int_status {
struct {
struct {
unsigned char pwr_rdy:1;
unsigned char:1;
unsigned char:1;
unsigned char uv_rdy:1;
unsigned char prox_int:1;
unsigned char alc_ovf:1;
unsigned char ppg_rdy:1;
unsigned char a_full:1;
};
struct {
unsigned char:1;
unsigned char die_temp_rdy:1;
unsigned char ecg_imp_rdy:1;
unsigned char:3;
unsigned char blue_rdy:1;
unsigned char vdd_oor:1;
};
};
u8 val[2];
};
struct max86915_div_data {
char left_integer[INTEGER_LEN];
char left_decimal[DECIMAL_LEN];
char right_integer[INTEGER_LEN];
char right_decimal[DECIMAL_LEN];
char result_integer[INTEGER_LEN];
char result_decimal[DECIMAL_LEN];
};
/* step flicker mode */
struct max86915_eol_flicker_data {
int count;
int state;
int retry;
int index;
u64 max;
u64 buf[SELF_RED_CH][EOL_NEW_FLICKER_SIZE];
u64 sum[SELF_RED_CH];
u64 average[SELF_RED_CH];
u64 std_sum[SELF_RED_CH];
int done;
};
/* step dc level */
struct max86915_eol_hrm_data {
int count;
int index;
int ir_current;
int red_current;
int green_current;
int blue_current;
u64 buf[SELF_MAX_CH_NUM][EOL_NEW_HRM_SIZE];
u64 sum[SELF_MAX_CH_NUM];
u64 average[SELF_MAX_CH_NUM];
u64 std_sum[SELF_MAX_CH_NUM];
u64 std_sum2[SELF_MAX_CH_NUM];
int min_pos[SELF_MAX_CH_NUM];
int max_pos[SELF_MAX_CH_NUM];
int min_val[SELF_MAX_CH_NUM];
int max_val[SELF_MAX_CH_NUM];
int done;
};
/* step freq_step */
struct max86915_eol_freq_data {
int state;
int retry;
int skew_retry;
unsigned long end_time;
unsigned long start_time;
struct timeval start_time_t;
struct timeval work_time_t;
unsigned long prev_time;
int count;
int skip_count;
int done;
};
struct max86915_eol_xtalk_data {
int count;
int index;
s64 buf[EOL_XTALK_SIZE];
s64 sum;
s64 average;
int done;
};
struct max86915_eol_item_data {
char test_type[10];
u32 system_noise[EOL_SPEC_NUM_MAX];
u32 ir_mid[EOL_SPEC_NUM_MAX];
u32 red_mid[EOL_SPEC_NUM_MAX];
u32 green_mid[EOL_SPEC_NUM_MAX];
u32 blue_mid[EOL_SPEC_NUM_MAX];
u32 ir_high[EOL_SPEC_NUM_MAX];
u32 red_high[EOL_SPEC_NUM_MAX];
u32 green_high[EOL_SPEC_NUM_MAX];
u32 blue_high[EOL_SPEC_NUM_MAX];
u32 ir_xtalk[EOL_SPEC_NUM_MAX];
u32 red_xtalk[EOL_SPEC_NUM_MAX];
u32 green_xtalk[EOL_SPEC_NUM_MAX];
u32 blue_xtalk[EOL_SPEC_NUM_MAX];
u32 ir_noise[EOL_SPEC_NUM_MAX];
u32 red_noise[EOL_SPEC_NUM_MAX];
u32 green_noise[EOL_SPEC_NUM_MAX];
u32 blue_noise[EOL_SPEC_NUM_MAX];
u32 frequency[EOL_SPEC_NUM_MAX];
u32 xtalk[EOL_SPEC_NUM_MAX];
};
struct max86915_eol_data {
int eol_count;
u8 state;
int eol_hrm_flag;
struct timeval start_time;
struct timeval end_time;
struct max86915_eol_flicker_data eol_flicker_data;
struct max86915_eol_hrm_data eol_hrm_data[EOL_NEW_HRM_ARRY_SIZE];
struct max86915_eol_freq_data eol_freq_data; /* test the ir routine. */
struct max86915_eol_xtalk_data eol_xtalk_data;
struct max86915_eol_item_data eol_item_data;
};
struct mode_count {
s32 hrm_cnt;
s32 amb_cnt;
s32 prox_cnt;
s32 sdk_cnt;
s32 cgm_cnt;
s32 unkn_cnt;
};
struct output_data {
enum op_mode mode;
s32 main_num;
s32 data_main[6];
s32 sub_num;
s32 data_sub[8];
s32 fifo_main[4][32];
s32 fifo_num;
};
/* max86915 Data Structure */
struct max86915_device_data {
struct i2c_client *client;
struct device *dev;
struct input_dev *hrm_input_dev;
struct mutex i2clock;
struct mutex activelock;
struct mutex suspendlock;
struct mutex flickerdatalock;
struct miscdevice miscdev;
struct pinctrl *hrm_pinctrl;
struct pinctrl_state *pins_sleep;
struct pinctrl_state *pins_idle;
char *led_3p3;
char *vdd_1p8;
char *i2c_1p8;
enum op_mode enabled_mode;
enum op_mode susp_mode;
u32 sampling_period_ns;
u8 mode_sdk_enabled;
u8 mode_sdk_prev;
u8 mode_svc_enabled;
u8 regulator_state;
s32 pin_hrm_int;
s32 pin_hrm_en;
s32 dev_irq;
u8 irq_state;
s32 hrm_threshold;
s32 prox_threshold;
u8 always_on;
s32 eol_test_is_enable;
u8 eol_test_status;
u8 eol_test_type;
s32 pre_eol_test_is_enable;
u32 reg_read_buf;
u8 debug_mode;
struct mode_count mode_cnt;
char *lib_ver;
#ifdef CONFIG_ARCH_QCOM
struct pm_qos_request pm_qos_req_fpm;
#endif
bool pm_state;
u8 agc_mode;
char *eol_test_result;
u32 eol_spec[MAX_EOL_SPEC];
u32 eol_semi_spec[MAX_EOL_SPEC];
u32 eol_xtalk_spec[MAX_EOL_XTALK_SPEC];
u8 part_type;
u8 flex_mode;
s32 num_samples;
u16 sample_cnt;
u8 awb_flicker_status;
u8 led_current1;
u8 led_current2;
u8 led_current3;
u8 led_current4;
u8 default_current1;
u8 default_current2;
u8 default_current3;
u8 default_current4;
u32 init_current[4];
u8 led_range[4];
u8 xtalk_code1;
u8 xtalk_code2;
u8 xtalk_code3;
u8 xtalk_code4;
u8 default_xtalk_code1;
u8 default_xtalk_code2;
u8 default_xtalk_code3;
u8 default_xtalk_code4;
u8 *fifo_buf;
u16 awb_sample_cnt;
int *flicker_data;
int flicker_data_cnt;
/* AGC routine start */
int agc_sum[4];
u32 agc_current[4];
u8 agc_led_set;
s32 agc_led_out_percent;
s32 agc_corr_coeff;
s32 agc_min_num_samples;
s32 agc_sensitivity_percent;
s32 change_by_percent_of_range[4];
s32 change_by_percent_of_current_setting[4];
s32 change_led_by_absolute_count[4];
s32 reached_thresh[4];
s32 threshold_default;
int (*update_led)(struct max86915_device_data*, int, int);
/* AGC routine end */
struct max86915_eol_data eol_data;
int isTrimmed;
u32 i2c_err_cnt;
u16 ir_curr;
u16 red_curr;
u16 green_curr;
u16 blue_curr;
u32 ir_adc;
u32 red_adc;
u32 green_adc;
u32 blue_adc;
u32 agc_ch_adc[4];
int agc_enabled;
int fifo_data[MAX_LED_NUM][MAX86915_FIFO_SIZE];
int fifo_samples;
u16 agc_sample_cnt[4];
int prev_ppg[MAX_LED_NUM];
int prev_current[MAX_LED_NUM];
int prev_agc_current[MAX_LED_NUM];
s32 remove_cnt[MAX_LED_NUM];
s32 prev_agc_average[MAX_LED_NUM];
};
static int max86915_eol_set_mode(struct max86915_device_data *data, int onoff);
#ifdef CONFIG_ARCH_QCOM
extern int sensors_create_symlink(struct kobject *target, const char *name);
extern void sensors_remove_symlink(struct kobject *target, const char *name);
extern int sensors_register(struct device **dev, void *drvdata,
struct device_attribute *attributes[], char *name);
#else
extern int sensors_create_symlink(struct input_dev *inputdev);
extern void sensors_remove_symlink(struct input_dev *inputdev);
extern int sensors_register(struct device *dev, void *drvdata,
struct device_attribute *attributes[], char *name);
#endif
extern void sensors_unregister(struct device *dev,
struct device_attribute *attributes[]);
extern unsigned int lpcharge;
#endif /* _MAX86915_H_ */