3653 lines
98 KiB
C
3653 lines
98 KiB
C
/**
|
|
*\mainpage
|
|
* ADPD143 driver
|
|
* (c) copyright 2014 Analog Devices Inc.
|
|
* Licensed under the GPL version 3 or later.
|
|
* \date April 2014
|
|
* \version 1.1 Driver
|
|
* \version Linux 3.4.0
|
|
*/
|
|
#include <linux/init.h> /* module initialization api */
|
|
#include <linux/module.h> /* module functionality */
|
|
#include <linux/i2c.h> /* i2c related functionality */
|
|
#include <linux/slab.h>
|
|
#include <linux/pm.h> /* device Power Management functionality */
|
|
#include <linux/errno.h>
|
|
#include <linux/err.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/input.h>
|
|
#include <linux/workqueue.h>
|
|
#include <linux/kobject.h>
|
|
#include <linux/average.h>
|
|
#include <linux/uaccess.h>
|
|
#include <linux/gpio.h>
|
|
#include <linux/irq.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/gpio.h>
|
|
#include <linux/of_gpio.h>
|
|
#include <linux/moduleparam.h>
|
|
#include <linux/regulator/consumer.h>
|
|
#include <linux/fs.h>
|
|
#include "adpd143.h"
|
|
#include <linux/pinctrl/consumer.h>
|
|
#include "../pinctrl/core.h"
|
|
|
|
static int dbg_enable;
|
|
|
|
module_param(dbg_enable, int, S_IRUGO | S_IWUSR);
|
|
|
|
/* ADPD143 driver version*/
|
|
#define ADPD143_VERSION "1.2"
|
|
/* ADPD143 release date*/
|
|
#define ADPD143_RELEASE_DATE "Tue Apr 22 12:11:42 IST 2014"
|
|
|
|
|
|
#define ADPD_DEV_NAME "adpd143"
|
|
|
|
/*INPUT DEVICE NAME LIST*/
|
|
#define MODULE_NAME_HRM "hrm_sensor"
|
|
#define MODULE_NAME_HRMLED "hrmled_sensor"
|
|
#define CHIP_NAME "ADPD143RI"
|
|
#define VENDOR "ADI"
|
|
|
|
#define MAX_EOL_RESULT 132
|
|
|
|
/*I2C RELATED*/
|
|
#define I2C_RETRY_DELAY 5
|
|
#define I2C_RETRIES 5
|
|
|
|
/*ADPD143 DRIVER RELATED*/
|
|
#define MAX_BUFFER 128
|
|
#define GLOBAL_OP_MODE_OFF 0
|
|
#define GLOBAL_OP_MODE_IDLE 1
|
|
#define GLOBAL_OP_MODE_GO 2
|
|
|
|
#define SET_GLOBAL_OP_MODE(val, cmd) ((val & 0xFC) | cmd)
|
|
|
|
#define FRM_FILE 0
|
|
#define FRM_ARR 1
|
|
#define EN_FIFO_CLK 0x3B5B
|
|
#define DS_FIFO_CLK 0x3050
|
|
|
|
/******************************************************************/
|
|
/**************** ADPD143 USER OPERATING MODE ********************/
|
|
/*
|
|
Operating mode defined to user
|
|
*/
|
|
#define IDLE_MODE 0x00
|
|
#define SAMPLE_XY_A_MODE 0x30
|
|
#define SAMPLE_XY_AB_MODE 0x31
|
|
#define SAMPLE_XY_B_MODE 0x32
|
|
#define GET_USR_MODE(usr_val) ((usr_val & 0xF0) >> 4)
|
|
#define GET_USR_SUB_MODE(usr_val) (usr_val & 0xF)
|
|
/******************************************************************/
|
|
/**************** ADPD143 OPERATING MODE & DATA MODE *************/
|
|
#define R_RDATA_FLAG_EN (0x1 << 15)
|
|
#define R_PACK_START_EN (0x0 << 14)
|
|
#define R_RDOUT_MODE (0x0 << 13)
|
|
#define R_FIFO_PREVENT_EN (0x1 << 12)
|
|
#define R_SAMP_OUT_MODE (0x0 << 9)
|
|
#define DEFAULT_OP_MODE_CFG_VAL(cfg_x) (R_RDATA_FLAG_EN + R_PACK_START_EN +\
|
|
R_RDOUT_MODE + R_FIFO_PREVENT_EN+\
|
|
R_SAMP_OUT_MODE)
|
|
|
|
#define SET_R_OP_MODE_A(val) ((val & 0xF000) >> 12)
|
|
#define SET_R_DATAMODE_A(val) ((val & 0x0F00) >> 8)
|
|
#define SET_R_OP_MODE_B(val) ((val & 0x00F0) >> 4)
|
|
#define SET_R_DATAMODE_B(val) ((val & 0x000F))
|
|
/**
|
|
@brief Used to get the value need to be set for slot-A, B operating mode
|
|
and data out mode.
|
|
*/
|
|
#define SET_MODE_VALUE(val) (SET_R_OP_MODE_A(val) |\
|
|
(SET_R_DATAMODE_A(val) << 2) |\
|
|
(SET_R_OP_MODE_B(val) << 5) |\
|
|
(SET_R_DATAMODE_B(val) << 6))
|
|
/******************************************************************/
|
|
/**************** ADPD143 INTERRUPT MASK *************************/
|
|
#define INTR_MASK_IDLE_USR 0x0000
|
|
#define INTR_MASK_SAMPLE_USR 0x0020
|
|
#define INTR_MASK_S_SAMP_XY_A_USR 0x0020
|
|
#define INTR_MASK_S_SAMP_XY_AB_USR 0x0040
|
|
#define INTR_MASK_S_SAMP_XY_B_USR 0x0040
|
|
#define SET_INTR_MASK(usr_val) ((~(INTR_MASK_##usr_val)) & 0x1FF)
|
|
/******************************************************************/
|
|
/***************** ADPD143 PRINT *********************************/
|
|
#ifdef ADPD_DBG
|
|
#define ADPD143_dbg(format, arg...) \
|
|
printk(KERN_DEBUG "ADPD143 : "format, ##arg);
|
|
#else
|
|
#define ADPD143_dbg(format, arg...) {if (dbg_enable)\
|
|
printk(KERN_DEBUG "ADPD143 : "format, ##arg);\
|
|
}
|
|
#endif
|
|
|
|
#ifdef ADPD_INFO
|
|
#define ADPD143_info(format, arg...) \
|
|
printk(KERN_INFO "ADPD143 : "format, ##arg);
|
|
#else
|
|
#define ADPD143_info(format, arg...) {if (0)\
|
|
; }
|
|
#endif
|
|
/******************************************************************/
|
|
/***************** ADPD143 DIFFERENT MODE ************************/
|
|
/*
|
|
Main mode
|
|
*/
|
|
#define IDLE_USR 0
|
|
#define SAMPLE_USR 3
|
|
#define TIA_ADC_USR 5
|
|
/*
|
|
Sub mode
|
|
*/
|
|
#define S_SAMP_XY_A 0
|
|
#define S_SAMP_XY_AB 1
|
|
#define S_SAMP_XY_B 2
|
|
/*
|
|
OPERATING MODE ==> MAIN_mode[20:16] |
|
|
OP_mode_A[15:12] |
|
|
DATA_mode_A[11:8]|
|
|
OP_mode_B[7:4] |
|
|
DATA_mode_B[3:0]
|
|
*/
|
|
#define IDLE_OFF ((IDLE_USR << 16) | (0x0000))
|
|
|
|
#define SAMPLE_XY_A ((SAMPLE_USR << 16) | (0x1400))
|
|
#define SAMPLE_XY_AB ((SAMPLE_USR << 16) | (0x1414))
|
|
#define SAMPLE_XY_B ((SAMPLE_USR << 16) | (0x0014))
|
|
|
|
#define MAX_MODE 6
|
|
#define MAX_IDLE_MODE 1
|
|
#define MAX_SAMPLE_MODE 3
|
|
|
|
#define MODE(val) __arr_mode_##val
|
|
#define MODE_SIZE(val) MAX_##val##_MODE
|
|
|
|
|
|
#define MAX_LIB_VER 20
|
|
#define OSC_TRIM_ADDR26_REG 0x26
|
|
#define OSC_TRIM_ADDR28_REG 0x28
|
|
#define OSC_TRIM_ADDR29_REG 0x29
|
|
#define OSC_TRIM_32K_REG 0x4B
|
|
#define OSC_TRIM_32M_REG 0x4D
|
|
#define OSC_DEFAULT_32K_SET 0x2695
|
|
#define OSC_DEFAULT_32M_SET 0x4272
|
|
#define OSCILLATOR_TRIM_FILE_PATH "/efs/osc_trim"
|
|
|
|
#define ALC_OFF_MODE 0
|
|
#define ALC_TIA_MODE 1
|
|
#define ALC_TNF_MODE 2
|
|
#define ALC_FLOAT_MODE 3
|
|
|
|
#define HRM_LDO_ON 1
|
|
#define HRM_LDO_OFF 0
|
|
|
|
#define IRRED_OFF 0
|
|
#define IR_MODE 1
|
|
#define RED_MODE 2
|
|
#define IRRED_MODE 3
|
|
|
|
#define HRM_IDLE_MODE 0
|
|
#define HRM_SAMPLE_MODE 1
|
|
#define HRM_TIA_ADC_MODE 2
|
|
|
|
#define SAMPLE_MAX_COUNT 65532
|
|
|
|
#define CH1_FLOAT_STR 1200
|
|
#define CH2_FLOAT_STR 1600
|
|
#define CH3_FLOAT_STR 2200
|
|
#define CH4_FLOAT_STR 4500
|
|
/**
|
|
mode mapping structure used to hold the number mode actually supported by
|
|
driver
|
|
*/
|
|
struct mode_mapping {
|
|
char *name;
|
|
int *mode_code;
|
|
unsigned int size;
|
|
};
|
|
int __arr_mode_IDLE[MAX_IDLE_MODE] =
|
|
{ IDLE_OFF };
|
|
int __arr_mode_SAMPLE[MAX_SAMPLE_MODE] =
|
|
{ SAMPLE_XY_A, SAMPLE_XY_AB, SAMPLE_XY_B };
|
|
int __arr_mode_TIA_ADC_SAMPLE[MAX_SAMPLE_MODE] =
|
|
{ SAMPLE_XY_A, SAMPLE_XY_AB, SAMPLE_XY_B };
|
|
|
|
struct mode_mapping __mode_recv_frm_usr[MAX_MODE] = {
|
|
{
|
|
.name = "IDLE",
|
|
.mode_code = MODE(IDLE),
|
|
.size = MODE_SIZE(IDLE)
|
|
},
|
|
{ .name = "UNSUPPORTED1",
|
|
.mode_code = 0,
|
|
.size = 0,
|
|
},
|
|
{ .name = "UNSUPPORTED2",
|
|
.mode_code = 0,
|
|
.size = 0,
|
|
},
|
|
{
|
|
.name = "SAMPLE",
|
|
.mode_code = MODE(SAMPLE),
|
|
.size = MODE_SIZE(SAMPLE)
|
|
},
|
|
{ .name = "UNSUPPORTED3",
|
|
.mode_code = 0,
|
|
.size = 0,
|
|
},
|
|
{
|
|
.name = "TIA_ADC_SAMPLE",
|
|
.mode_code = MODE(TIA_ADC_SAMPLE),
|
|
.size = MODE_SIZE(SAMPLE)
|
|
}
|
|
};
|
|
|
|
/**
|
|
general data buffer that is required to store the recieved buffer
|
|
*/
|
|
unsigned short data_buffer[MAX_BUFFER] = { 0 };
|
|
|
|
struct wrk_q_timestamp {
|
|
struct timeval interrupt_trigger;
|
|
};
|
|
|
|
struct adpd143_stats {
|
|
atomic_t interrupts;
|
|
atomic_t fifo_requires_sync;
|
|
atomic_t fifo_bytes[4];
|
|
atomic_t wq_pending;
|
|
atomic_t wq_schedule_time_peak_usec;
|
|
atomic_t wq_schedule_time_last_usec;
|
|
struct ewma wq_schedule_time_avg_usec;
|
|
atomic_t data_process_time_peak_usec;
|
|
atomic_t data_process_time_last_usec;
|
|
struct ewma data_process_time_avg_usec;
|
|
struct wrk_q_timestamp stamp;
|
|
};
|
|
|
|
/**
|
|
structure hold adpd143 chip structure, and parameter used
|
|
to control the adpd143 software part.
|
|
*/
|
|
struct adpd143_data {
|
|
struct i2c_client *client;
|
|
struct mutex mutex;/*for chip structure*/
|
|
struct device *dev;
|
|
struct input_dev *hrm_input_dev;
|
|
struct input_dev *hrmled_input_dev;
|
|
struct adpd_platform_data *ptr_config;
|
|
|
|
struct work_struct work;
|
|
struct workqueue_struct *ptr_adpd143_wq_st;
|
|
|
|
struct pinctrl *p;
|
|
struct pinctrl_state *pins_sleep;
|
|
struct pinctrl_state *pins_idle;
|
|
|
|
int irq;
|
|
int hrm_int;
|
|
int pre_hrmled_enable;
|
|
int sample_cnt;
|
|
struct regulator *leda;
|
|
unsigned short *ptr_data;
|
|
|
|
struct adpd143_stats stats;
|
|
|
|
unsigned short intr_mask_val;
|
|
unsigned short intr_status_val;
|
|
unsigned short fifo_size;
|
|
|
|
/*sysfs register read write */
|
|
unsigned short sysfs_I2C_regaddr;
|
|
unsigned short sysfs_I2C_regval;
|
|
unsigned short sysfslastreadval;
|
|
|
|
atomic_t hrm_enabled;
|
|
atomic_t hrmled_enabled;
|
|
atomic_t adpd_mode;
|
|
|
|
int (*read)(struct adpd143_data *, u8 reg_addr, int len, u16 *buf);
|
|
int (*write)(struct adpd143_data *, u8 reg_addr, int len, u16 data);
|
|
u8 eol_test_is_enable;
|
|
u8 eol_test_status;
|
|
char *eol_test_result;
|
|
char *eol_lib_ver;
|
|
char *elf_lib_ver;
|
|
const char *led_3p3;
|
|
const char *vdd_1p8;
|
|
|
|
int osc_trim_32K_value;
|
|
int osc_trim_32M_value;
|
|
int osc_trim_addr26_value;
|
|
int osc_trim_addr28_value;
|
|
int osc_trim_addr29_value;
|
|
u8 osc_trim_open_enable;
|
|
|
|
unsigned ch1_f_str;
|
|
unsigned ch2_f_str;
|
|
unsigned ch3_f_str;
|
|
unsigned ch4_f_str;
|
|
|
|
unsigned is_sub_pmic;
|
|
};
|
|
|
|
/**
|
|
structure hold adpd143 configuration data to be written during probe.
|
|
*/
|
|
static struct adpd_platform_data adpd143_config_data = {
|
|
.config_size = 54,
|
|
.config_data = {
|
|
0x00100001,
|
|
0x000100ff,
|
|
0x00020005,
|
|
0x00060000,
|
|
0x00111011,
|
|
0x0012000A,
|
|
0x00130320,
|
|
0x00140449,
|
|
0x00150333,
|
|
0x001C4040,
|
|
0x001D4040,
|
|
0x00181400,
|
|
0x00191400,
|
|
0x001a1650,
|
|
0x001b1000,
|
|
0x001e1ff0,
|
|
0x001f1Fc0,
|
|
0x00201ad0,
|
|
0x00211470,
|
|
0x00231032,
|
|
0x00241039,
|
|
0x002502CC,
|
|
0x00340000,
|
|
0x00260000,
|
|
0x00270800,
|
|
0x00280000,
|
|
0x00294003,
|
|
0x00300330,
|
|
0x00310213,
|
|
0x00421d37,
|
|
0x0043ada5,
|
|
0x003924D2,
|
|
0x00350330,
|
|
0x00360813,
|
|
0x00441d37,
|
|
0x0045ada5,
|
|
0x003b24D2,
|
|
0x00590808,
|
|
0x00320320,
|
|
0x00330113,
|
|
0x003a22d4,
|
|
0x003c0006,
|
|
0x00401010,
|
|
0x0041004c,
|
|
0x004c0004,
|
|
0x004f2090,
|
|
0x00500000,
|
|
0x00523000,
|
|
0x0053e400,
|
|
0x00580000,
|
|
0x005b1831,
|
|
0x005d1b31,
|
|
0x005e2808,
|
|
0x004e0040,
|
|
},
|
|
};
|
|
|
|
static struct adpd_platform_data adpd143_tia_adc_config_data = {
|
|
.config_size = 66,
|
|
.config_data = {
|
|
0x00100001, 0x000100ff, 0x00020005, 0x00060000,
|
|
0x00111011, 0x00120020, 0x00130320,
|
|
0x00140000, /* No PD connect first for dark calibration */
|
|
0x00150000,
|
|
0x00233034, 0x0024303A, 0x002502CC,
|
|
0x00340100, /* LED off */
|
|
0x00260000, 0x00270800, 0x00280000,
|
|
0x00300220, 0x00350220, /* Pulse offset */
|
|
0x00310440, 0x00360244, /* Pulse count 0x0813/0120 */
|
|
0x00294003,
|
|
0x003924D2, 0x003b1a70, /* AFE_CTRL */
|
|
0x00421d34, 0x00441d26, /* AFE_TRIM */
|
|
0x0043b065, 0x0045ae65, /* AFE_MUX_SW */
|
|
0x005e0808, 0x00597a08, /* float */
|
|
0x00168080, 0x00178080, 0x001C8080, 0x001D8080, /*AFE ch gain */
|
|
0x005b0000,
|
|
0x00370000, 0x003d0000,
|
|
0x00460000, 0x00470080, 0x00480000,
|
|
0x00490000, 0x004a0000, 0x005d0000,
|
|
0x00320320, 0x00330113, 0x003a22d4,
|
|
0x003c3206, /* 32Mhz/PD cathod to VDD - hrm : 0x003c0006 */
|
|
0x00401010, 0x0041004c, 0x004c0004,
|
|
0x004d4272, 0x004f2090, 0x00500000, 0x00523000,
|
|
0x0053e400, 0x004e0040,
|
|
0x005a0000,
|
|
/* slot B-float:subtract first, add second -hrm:0x00580000 */
|
|
0x00580040,
|
|
/* ADC offset start */
|
|
0x00180000,
|
|
0x00190000,
|
|
0x001a0000,
|
|
0x001b0000,
|
|
0x001e0000,
|
|
0x001f0000,
|
|
0x00200000,
|
|
0x00210000,
|
|
/* ADC offset end */
|
|
},
|
|
};
|
|
static int adpd143_configuration(struct adpd143_data *pst_adpd, unsigned char config);
|
|
static int adpd143_TIA_ADC_configuration(struct adpd143_data *pst_adpd, unsigned char config);
|
|
|
|
/* for TIA_ADC runtime Dark Calibration */
|
|
static unsigned short is_TIA_Dark_Cal = 0;
|
|
static unsigned short is_Float_Dark_Cal = 0;
|
|
static unsigned rawDarkCh[8];
|
|
static unsigned rawDataCh[8];
|
|
|
|
/**
|
|
This function is used to parse data from the string "0xXXX 1 0xYYY"
|
|
@param buf is pointer point constant address
|
|
@param cnt store number value need to parse
|
|
@param data parse data are stored in it
|
|
@return void
|
|
*/
|
|
static void
|
|
cmd_parsing(const char *buf, unsigned short cnt, unsigned short *data)
|
|
{
|
|
|
|
char **bp = (char **)&buf;
|
|
char *token, minus, parsing_cnt = 0;
|
|
int val;
|
|
int pos;
|
|
|
|
while ((token = strsep(bp, " "))) {
|
|
pos = 0;
|
|
minus = false;
|
|
if ((char)token[pos] == '-') {
|
|
minus = true;
|
|
pos++;
|
|
}
|
|
if ((token[pos] == '0') &&
|
|
(token[pos + 1] == 'x' || token[pos + 1] == 'X')) {
|
|
if (kstrtoul(&token[pos + 2], 16,
|
|
(long unsigned int *)&val))
|
|
val = 0;
|
|
if (minus)
|
|
val *= (-1);
|
|
} else {
|
|
if (kstrtoul(&token[pos], 10,
|
|
(long unsigned int *)&val))
|
|
val = 0;
|
|
if (minus)
|
|
val *= (-1);
|
|
}
|
|
|
|
if (parsing_cnt < cnt)
|
|
*(data + parsing_cnt) = val;
|
|
else
|
|
break;
|
|
parsing_cnt++;
|
|
}
|
|
}
|
|
|
|
/**
|
|
This function is a inline function mapped to i2c read functionality
|
|
@param pst_adpd the ADPD143 data structure
|
|
@param reg is the address from which the value need to be read out
|
|
@return u16 value present in the register.
|
|
*/
|
|
static inline u16
|
|
reg_read(struct adpd143_data *pst_adpd, u16 reg)
|
|
{
|
|
u16 value;
|
|
if (!pst_adpd->read(pst_adpd, reg, 1, &value))
|
|
return value;
|
|
return 0xFFFF;
|
|
}
|
|
|
|
/**
|
|
This function is a inline function mapped to i2c read functionality
|
|
@param pst_adpd the ADPD143 data structure
|
|
@param reg is the address from which the value need to be read out
|
|
@param len number of data need to be readout
|
|
@param buf is pointer store the value present in the register.
|
|
@return u16 status of i2c read.
|
|
*/
|
|
static inline u16
|
|
multi_reg_read(struct adpd143_data *pst_adpd, u16 reg, u16 len, u16 *buf)
|
|
{
|
|
return pst_adpd->read(pst_adpd, reg, len, buf);
|
|
}
|
|
|
|
/**
|
|
This function is a inline function mapped to i2c write functionality
|
|
@param pst_adpd the ADPD143 data structure
|
|
@param reg is the address to which the data need to be written
|
|
@param value is the data need to be write.
|
|
@return u16 status of i2c write.
|
|
*/
|
|
static inline u16
|
|
reg_write(struct adpd143_data *pst_adpd, u16 reg, u16 value)
|
|
{
|
|
return pst_adpd->write(pst_adpd, reg, 1, value);
|
|
}
|
|
|
|
/**
|
|
This function is used for I2C read from the sysfs
|
|
file system of the ADPD143 Chip
|
|
@param pst_adpd the ADPD143 data structure
|
|
@return u16 status of i2c read.
|
|
*/
|
|
static u16
|
|
adpd143_sysfs_I2C_read(struct adpd143_data *pst_adpd)
|
|
{
|
|
return reg_read(pst_adpd, pst_adpd->sysfs_I2C_regaddr);
|
|
}
|
|
|
|
/**
|
|
This function is used for I2C write from the sysfs
|
|
file system of the ADPD143 Chip
|
|
@param pst_adpd the ADPD143 data structure
|
|
@return u16 the bytes of written data.
|
|
*/
|
|
static u16
|
|
adpd143_sysfs_I2C_write(struct adpd143_data *pst_adpd)
|
|
{
|
|
u16 err;
|
|
err = reg_write(pst_adpd, pst_adpd->sysfs_I2C_regaddr,
|
|
pst_adpd->sysfs_I2C_regval);
|
|
if (err)
|
|
return 0xFFFF;
|
|
else
|
|
return pst_adpd->sysfs_I2C_regval;
|
|
}
|
|
|
|
/**
|
|
This function is used to parse string
|
|
@param recv_buf pointer point a string
|
|
@return int
|
|
*/
|
|
static int
|
|
parse_data(char *recv_buf)
|
|
{
|
|
char **bp = (char **)&recv_buf;
|
|
char *token, parsing_cnt = 0;
|
|
long val;
|
|
int test_data;
|
|
unsigned int data = 0;
|
|
int ret = 0;
|
|
char test[10] = {'\0'};
|
|
|
|
while ((token = strsep(bp, " \t"))) {
|
|
memset(test, '\0', 10);
|
|
memcpy(test, token, strlen(token));
|
|
memmove(test+2, test, strlen(test));
|
|
test[0] = '0';
|
|
test[1] = 'x';
|
|
ret = kstrtol(test, 0, &val);
|
|
if (ret) {
|
|
if (ret == -ERANGE) {
|
|
ADPD143_info("out of range\n");
|
|
val = 0;
|
|
}
|
|
if (ret == -EINVAL) {
|
|
ADPD143_info("parsing error\n");
|
|
sscanf(test, "%x", &test_data);
|
|
val = test_data;
|
|
}
|
|
}
|
|
|
|
if (parsing_cnt == 0) {
|
|
data = (int) (0xFF & val);
|
|
if (data == 0)
|
|
return -1;
|
|
}
|
|
if (parsing_cnt == 1) {
|
|
data = data << 16;
|
|
data |= (0xFFFF & val);
|
|
}
|
|
parsing_cnt++;
|
|
if (parsing_cnt > 2)
|
|
break;
|
|
}
|
|
return data;
|
|
}
|
|
|
|
/**
|
|
This function is used for reading configuration file
|
|
@param pst_adpd the ADPD143 data structure
|
|
@return int status of the configuration file.
|
|
*/
|
|
static int
|
|
adpd143_read_config_file(struct adpd143_data *pst_adpd)
|
|
{
|
|
mm_segment_t old_fs;
|
|
struct file *fpt_adpd = NULL;
|
|
struct adpd_platform_data *ptr_config = NULL;
|
|
int line_cnt = 0;
|
|
int start = 0;
|
|
int cnt = 0;
|
|
int ret = 0;
|
|
int i = 0;
|
|
char get_char;
|
|
char *recieved_buf = NULL;
|
|
loff_t pos = 0;
|
|
ptr_config = pst_adpd->ptr_config;
|
|
|
|
old_fs = get_fs();
|
|
set_fs(KERNEL_DS);
|
|
|
|
fpt_adpd = filp_open("/data/misc/adpd143_config.dcfg", O_RDONLY, 0666);
|
|
if (IS_ERR(fpt_adpd)) {
|
|
ADPD143_dbg("unable to find de file %ld\n", PTR_ERR(fpt_adpd));
|
|
set_fs(old_fs);
|
|
ret = /*PTR_ERR(fpt_adpd_filp); */ -1;
|
|
ptr_config->config_size = 0;
|
|
goto err_filp_open;
|
|
}
|
|
|
|
recieved_buf = kzalloc(sizeof(char) * 15, GFP_KERNEL);
|
|
memset(recieved_buf, '\0', sizeof(char) * 15);
|
|
|
|
while (vfs_read(fpt_adpd, &get_char, sizeof(char), &pos)) {
|
|
if (get_char == '\n') {
|
|
line_cnt++;
|
|
if (cnt > 1) {
|
|
ret = parse_data(recieved_buf);
|
|
if (ret == -1) {
|
|
ADPD143_dbg("Error in reading dcfg\n");
|
|
break;
|
|
}
|
|
ADPD143_info("0x%08x\n", ret);
|
|
ptr_config->config_data[i] = ret;
|
|
i++;
|
|
}
|
|
memset(recieved_buf, '\0', sizeof(char) * 15);
|
|
start = 0;
|
|
cnt = 0;
|
|
ret = 0;
|
|
} else {
|
|
if (get_char != '#') {
|
|
if (start != 0xF) {
|
|
recieved_buf[cnt] = get_char;
|
|
cnt++;
|
|
}
|
|
} else {
|
|
start = 0xF;
|
|
}
|
|
}
|
|
}
|
|
|
|
filp_close(fpt_adpd, NULL);
|
|
|
|
set_fs(old_fs);
|
|
kfree(recieved_buf);
|
|
ptr_config->config_size = i;
|
|
return 0;
|
|
err_filp_open:
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
This function is used for reading interrupt and FIFO status
|
|
@param pst_adpd the ADPD143 data structure
|
|
@return unsigned short interrupt status value
|
|
*/
|
|
static unsigned short
|
|
adpd143_rd_intr_fifo(struct adpd143_data *pst_adpd)
|
|
{
|
|
unsigned short regval = 0;
|
|
unsigned short fifo_size = 0;
|
|
unsigned short usr_mode = 0;
|
|
regval = reg_read(pst_adpd, ADPD_INT_STATUS_ADDR);
|
|
fifo_size = ((regval & 0xFF00) >> 8);
|
|
pst_adpd->fifo_size = ((fifo_size / 2) + (fifo_size & 1));
|
|
pst_adpd->intr_status_val = (regval & 0xFF);
|
|
|
|
ADPD143_info("Intr_status 0x%x, FIFO_SIZE 0x%x\n",
|
|
pst_adpd->intr_status_val, pst_adpd->fifo_size);
|
|
|
|
if (fifo_size <= 16)
|
|
atomic_inc(&pst_adpd->stats.fifo_bytes[0]);
|
|
else if (fifo_size <= 32)
|
|
atomic_inc(&pst_adpd->stats.fifo_bytes[1]);
|
|
else if (fifo_size <= 64)
|
|
atomic_inc(&pst_adpd->stats.fifo_bytes[2]);
|
|
else
|
|
atomic_inc(&pst_adpd->stats.fifo_bytes[3]);
|
|
|
|
usr_mode = atomic_read(&pst_adpd->adpd_mode);
|
|
if (0 != usr_mode) {
|
|
unsigned int mask = 0;
|
|
unsigned int mod = 0;
|
|
|
|
switch (GET_USR_SUB_MODE(usr_mode)) {
|
|
case S_SAMP_XY_AB:
|
|
mask = 0x07;
|
|
break;
|
|
default:
|
|
mask = 0x03;
|
|
break;
|
|
};
|
|
|
|
mod = pst_adpd->fifo_size & mask;
|
|
if (mod) {
|
|
ADPD143_info("Keeping %d samples in FIFO from %d\n",
|
|
mod, pst_adpd->fifo_size);
|
|
pst_adpd->fifo_size &= ~mask;
|
|
atomic_inc(&pst_adpd->stats.fifo_requires_sync);
|
|
}
|
|
}
|
|
|
|
return pst_adpd->intr_status_val;
|
|
}
|
|
|
|
/**
|
|
This function is used for clearing the Interrupt as well as FIFO
|
|
@param pst_adpd the ADPD143 data structure
|
|
@return void
|
|
*/
|
|
static void
|
|
adpd143_clr_intr_fifo(struct adpd143_data *pst_adpd)
|
|
{
|
|
/*below code is added for verification */
|
|
unsigned short regval;
|
|
regval = reg_read(pst_adpd, ADPD_INT_STATUS_ADDR);
|
|
ADPD143_info("fifo_size: 0x%04x, Status: 0x%x\n",
|
|
((regval & 0xFF00) >> 8), (regval & 0xFF));
|
|
|
|
reg_write(pst_adpd, ADPD_INT_STATUS_ADDR, 0x80FF);
|
|
regval = reg_read(pst_adpd, ADPD_INT_STATUS_ADDR);
|
|
ADPD143_info("After clear - fifo_size: 0x%04x, Status: 0x%x\n",
|
|
((regval & 0xFF00) >> 8), (regval & 0xFF));
|
|
}
|
|
|
|
/**
|
|
This function is used for clearing the Interrupt status
|
|
@param pst_adpd the ADPD143 data structure
|
|
@param mode operating mode of ADPD143
|
|
@return void
|
|
*/
|
|
static void
|
|
adpd143_clr_intr_status(struct adpd143_data *pst_adpd, unsigned short mode)
|
|
{
|
|
reg_write(pst_adpd, ADPD_INT_STATUS_ADDR,
|
|
pst_adpd->intr_status_val);
|
|
}
|
|
|
|
/**
|
|
This function is used to read out FIFO data from FIFO
|
|
@param pst_adpd the ADPD143 data structure
|
|
@return unsigned int FIFO size
|
|
*/
|
|
static unsigned int
|
|
adpd143_rd_fifo_data(struct adpd143_data *pst_adpd)
|
|
{
|
|
int loop_cnt = 0;
|
|
|
|
if (!pst_adpd->fifo_size)
|
|
goto err_rd_fifo_data;
|
|
|
|
if (0 != atomic_read(&pst_adpd->adpd_mode) &&
|
|
pst_adpd->fifo_size & 0x3) {
|
|
pr_err("Unexpected FIFO_SIZE=%d,\
|
|
should be multiple of four (channels)!\n",
|
|
pst_adpd->fifo_size);
|
|
}
|
|
|
|
/* reg_write(pst_adpd, ADPD_ACCESS_CTRL_ADDR, 0x0001);*/
|
|
reg_write(pst_adpd, ADPD_TEST_PD_ADDR, EN_FIFO_CLK);
|
|
multi_reg_read(pst_adpd, ADPD_DATA_BUFFER_ADDR,
|
|
pst_adpd->fifo_size, pst_adpd->ptr_data);
|
|
/* reg_write(pst_adpd, ADPD_ACCESS_CTRL_ADDR, 0x0000);*/
|
|
reg_write(pst_adpd, ADPD_TEST_PD_ADDR, DS_FIFO_CLK);
|
|
|
|
for (; loop_cnt < pst_adpd->fifo_size; loop_cnt++) {
|
|
ADPD143_info("[0x%04x] 0x%04x\n", loop_cnt,
|
|
pst_adpd->ptr_data[loop_cnt]);
|
|
}
|
|
return pst_adpd->fifo_size;
|
|
err_rd_fifo_data:
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
This function is a thing need to configure before changing the register
|
|
@param pst_adpd the ADPD143 data structure
|
|
@param global_mode OFF, IDLE, GO are the three mode
|
|
@return void
|
|
*/
|
|
static void
|
|
adpd143_config_prerequisite(struct adpd143_data *pst_adpd, u32 global_mode)
|
|
{
|
|
unsigned short regval = 0;
|
|
regval = reg_read(pst_adpd, ADPD_OP_MODE_ADDR);
|
|
regval = SET_GLOBAL_OP_MODE(regval, global_mode);
|
|
ADPD143_info("reg 0x%04x,\tafter set 0x%04x\n",
|
|
regval, SET_GLOBAL_OP_MODE(regval, global_mode));
|
|
reg_write(pst_adpd, ADPD_OP_MODE_ADDR, regval);
|
|
}
|
|
|
|
/**
|
|
This function is used for switching the adpd143 mode
|
|
@param pst_adpd the ADPD143 data structure
|
|
@param usr_mode user mode
|
|
@return void
|
|
*/
|
|
static void
|
|
adpd143_mode_switching(struct adpd143_data *pst_adpd, u32 usr_mode)
|
|
{
|
|
unsigned int opmode_val = 0;
|
|
unsigned short mode_val = 0;
|
|
unsigned short intr_mask_val = 0;
|
|
int i;
|
|
|
|
unsigned short mode = GET_USR_MODE(usr_mode);
|
|
unsigned short sub_mode = GET_USR_SUB_MODE(usr_mode);
|
|
/*disabling further avoid further interrupt to trigger */
|
|
disable_irq(pst_adpd->irq);
|
|
|
|
/*stop the pending work */
|
|
/*this function Gurantee that wrk is not pending or executing on CPU */
|
|
cancel_work_sync(&pst_adpd->work);
|
|
|
|
atomic_set(&pst_adpd->adpd_mode, 0);
|
|
mutex_lock(&pst_adpd->mutex);
|
|
|
|
/*Depending upon the mode get the value need to write Operatin mode*/
|
|
opmode_val = *(__mode_recv_frm_usr[mode].mode_code + (sub_mode));
|
|
|
|
adpd143_config_prerequisite(pst_adpd, GLOBAL_OP_MODE_IDLE);
|
|
|
|
/*switch to IDLE mode */
|
|
mode_val = DEFAULT_OP_MODE_CFG_VAL(pst_adpd);
|
|
mode_val += SET_MODE_VALUE(IDLE_OFF);
|
|
intr_mask_val = SET_INTR_MASK(IDLE_USR);
|
|
|
|
reg_write(pst_adpd, ADPD_OP_MODE_CFG_ADDR, mode_val);
|
|
reg_write(pst_adpd, ADPD_INT_MASK_ADDR, intr_mask_val);
|
|
|
|
/*clear FIFO and flush buffer */
|
|
adpd143_clr_intr_fifo(pst_adpd);
|
|
adpd143_rd_intr_fifo(pst_adpd);
|
|
if (pst_adpd->fifo_size != 0) {
|
|
adpd143_clr_intr_status(pst_adpd, IDLE_USR);
|
|
ADPD143_info("Flushing FIFO\n");
|
|
adpd143_rd_fifo_data(pst_adpd);
|
|
}
|
|
|
|
|
|
adpd143_config_prerequisite(pst_adpd, GLOBAL_OP_MODE_GO);
|
|
msleep(20);
|
|
adpd143_config_prerequisite(pst_adpd, GLOBAL_OP_MODE_IDLE);
|
|
|
|
/*Find Interrupt mask value */
|
|
switch (mode) {
|
|
case IDLE_USR:
|
|
ADPD143_info("IDLE MODE\n");
|
|
intr_mask_val = SET_INTR_MASK(IDLE_USR);
|
|
atomic_set(&pst_adpd->hrm_enabled, HRM_IDLE_MODE);
|
|
break;
|
|
case SAMPLE_USR:
|
|
ADPD143_info("SAMPLE MODE\n");
|
|
/*enable interrupt only when data written to FIFO */
|
|
switch (sub_mode) {
|
|
case S_SAMP_XY_A:
|
|
intr_mask_val = SET_INTR_MASK(S_SAMP_XY_A_USR);
|
|
break;
|
|
case S_SAMP_XY_AB:
|
|
intr_mask_val = SET_INTR_MASK(S_SAMP_XY_AB_USR);
|
|
break;
|
|
case S_SAMP_XY_B:
|
|
intr_mask_val = SET_INTR_MASK(S_SAMP_XY_B_USR);
|
|
break;
|
|
default:
|
|
intr_mask_val = SET_INTR_MASK(SAMPLE_USR);
|
|
break;
|
|
};
|
|
intr_mask_val |= 0xC000;
|
|
atomic_set(&pst_adpd->hrm_enabled, HRM_SAMPLE_MODE);
|
|
adpd143_configuration(pst_adpd, 1);
|
|
break;
|
|
case TIA_ADC_USR:
|
|
ADPD143_info("TIA_ADC_USR SAMPLE MODE\n");
|
|
/* for TIA_ADC runtime Dark Calibration */
|
|
pst_adpd->ch1_f_str = CH1_FLOAT_STR;
|
|
pst_adpd->ch2_f_str = CH2_FLOAT_STR;
|
|
pst_adpd->ch3_f_str = CH3_FLOAT_STR;
|
|
pst_adpd->ch4_f_str = CH4_FLOAT_STR;
|
|
is_TIA_Dark_Cal = is_Float_Dark_Cal = 0;
|
|
for(i = 0; i < 8; i++) rawDarkCh[i] = rawDataCh[i] = 0;
|
|
|
|
/*enable interrupt only when data written to FIFO */
|
|
switch (sub_mode) {
|
|
case S_SAMP_XY_A:
|
|
intr_mask_val = SET_INTR_MASK(S_SAMP_XY_A_USR);
|
|
break;
|
|
case S_SAMP_XY_AB:
|
|
intr_mask_val = SET_INTR_MASK(S_SAMP_XY_AB_USR);
|
|
break;
|
|
case S_SAMP_XY_B:
|
|
intr_mask_val = SET_INTR_MASK(S_SAMP_XY_B_USR);
|
|
break;
|
|
default:
|
|
intr_mask_val = SET_INTR_MASK(SAMPLE_USR);
|
|
break;
|
|
};
|
|
intr_mask_val |= 0xC000;
|
|
atomic_set(&pst_adpd->hrm_enabled, HRM_TIA_ADC_MODE);
|
|
adpd143_TIA_ADC_configuration(pst_adpd, 1);
|
|
break;
|
|
default:
|
|
/*This case won't occur */
|
|
ADPD143_dbg("invalid mode\n");
|
|
break;
|
|
};
|
|
|
|
ADPD143_info("INT_MASK_ADDR[0x%04x] = 0x%04x\n", ADPD_INT_MASK_ADDR,
|
|
intr_mask_val);
|
|
pst_adpd->intr_mask_val = intr_mask_val;
|
|
|
|
/*Fetch Default opmode configuration other than OP mode
|
|
and DATA_OUT mode */
|
|
mode_val = DEFAULT_OP_MODE_CFG_VAL(pst_adpd);
|
|
/*Get Mode value from the opmode value,
|
|
to hardocde the macro, change SET_MODE_VALUE macro and concern
|
|
mode macro
|
|
*/
|
|
mode_val += SET_MODE_VALUE(opmode_val);
|
|
|
|
ADPD143_info("OP_MODE_CFG[0x%04x] = 0x%04x\n",
|
|
ADPD_OP_MODE_CFG_ADDR, mode_val);
|
|
|
|
atomic_set(&pst_adpd->adpd_mode, usr_mode);
|
|
|
|
reg_write(pst_adpd, ADPD_INT_MASK_ADDR, pst_adpd->intr_mask_val);
|
|
reg_write(pst_adpd, ADPD_OP_MODE_CFG_ADDR, mode_val);
|
|
|
|
mutex_unlock(&pst_adpd->mutex);
|
|
|
|
enable_irq(pst_adpd->irq);
|
|
|
|
if (mode != IDLE_USR)
|
|
adpd143_config_prerequisite(pst_adpd, GLOBAL_OP_MODE_GO);
|
|
else
|
|
adpd143_config_prerequisite(pst_adpd, GLOBAL_OP_MODE_OFF);
|
|
}
|
|
|
|
/**
|
|
This function is used for sending Sample event depend upon
|
|
the Sample mode
|
|
@param pst_adpd the ADPD143 data structure
|
|
@return void
|
|
*/
|
|
/* for making float mode saturated at the point */
|
|
#define FLOAT_STR_CONTROL 7200
|
|
/* for TIA_ADC runtime Dark Calibration */
|
|
#define TIA_DARK_CAL_CNT 5
|
|
#define FLOAT_DARK_CAL_CNT 5
|
|
static void
|
|
adpd143_sample_event(struct adpd143_data *pst_adpd)
|
|
{
|
|
unsigned short usr_mode = 0;
|
|
unsigned short sub_mode = 0;
|
|
unsigned short cnt = 0;
|
|
unsigned int ch;
|
|
unsigned short uncoated_ch3 = 0;
|
|
unsigned short coated_ch4 = 0;
|
|
unsigned short k = 0;
|
|
|
|
ADPD143_info("%s\n", __func__);
|
|
|
|
usr_mode = atomic_read(&pst_adpd->adpd_mode);
|
|
sub_mode = GET_USR_SUB_MODE(usr_mode);
|
|
|
|
pst_adpd->sample_cnt++;
|
|
if ((pst_adpd->sample_cnt % 200) == 1) {
|
|
printk("%s: user mode : %d, sub mode : %d\n",
|
|
__func__, GET_USR_MODE(usr_mode),GET_USR_SUB_MODE(usr_mode));
|
|
if (pst_adpd->sample_cnt == SAMPLE_MAX_COUNT)
|
|
pst_adpd->sample_cnt = 0;
|
|
}
|
|
|
|
switch (sub_mode) {
|
|
case S_SAMP_XY_A:
|
|
if (pst_adpd->fifo_size < 4 || (pst_adpd->fifo_size & 0x3)) {
|
|
pr_err("Unexpected FIFO_SIZE=%d\n",
|
|
pst_adpd->fifo_size);
|
|
break;
|
|
}
|
|
|
|
if(GET_USR_MODE(usr_mode) != TIA_ADC_USR) {
|
|
for (cnt = 0; cnt < pst_adpd->fifo_size; cnt += 4) {
|
|
if (atomic_read(&pst_adpd->hrmled_enabled)) {
|
|
input_event(pst_adpd->hrmled_input_dev,
|
|
EV_REL, REL_Z, sub_mode + 1);
|
|
input_event(pst_adpd->hrmled_input_dev, EV_MSC,
|
|
MSC_RAW, pst_adpd->ptr_data[cnt] + 1);
|
|
input_event(pst_adpd->hrmled_input_dev, EV_MSC,
|
|
MSC_RAW, pst_adpd->ptr_data[cnt + 1] + 1);
|
|
input_event(pst_adpd->hrmled_input_dev, EV_MSC,
|
|
MSC_RAW, pst_adpd->ptr_data[cnt + 2] + 1);
|
|
input_event(pst_adpd->hrmled_input_dev, EV_MSC,
|
|
MSC_RAW, pst_adpd->ptr_data[cnt + 3] + 1);
|
|
input_sync(pst_adpd->hrmled_input_dev);
|
|
} else {
|
|
input_event(pst_adpd->hrm_input_dev,
|
|
EV_REL, REL_Z, sub_mode + 1);
|
|
input_event(pst_adpd->hrm_input_dev, EV_MSC,
|
|
MSC_RAW, pst_adpd->ptr_data[cnt] + 1);
|
|
input_event(pst_adpd->hrm_input_dev, EV_MSC,
|
|
MSC_RAW, pst_adpd->ptr_data[cnt + 1] + 1);
|
|
input_event(pst_adpd->hrm_input_dev, EV_MSC,
|
|
MSC_RAW, pst_adpd->ptr_data[cnt + 2] + 1);
|
|
input_event(pst_adpd->hrm_input_dev, EV_MSC,
|
|
MSC_RAW, pst_adpd->ptr_data[cnt + 3] + 1);
|
|
input_sync(pst_adpd->hrm_input_dev);
|
|
}
|
|
}
|
|
} else {
|
|
for (cnt = 0; cnt < pst_adpd->fifo_size; cnt += 4) {
|
|
/* for TIA_ADC runtime Dark Calibration */
|
|
if (is_TIA_Dark_Cal < TIA_DARK_CAL_CNT
|
|
&& is_TIA_Dark_Cal > 1) {
|
|
for(k = 0; k < 4; k++) {
|
|
if (rawDarkCh[k] < pst_adpd->ptr_data[cnt+k]
|
|
|| !rawDarkCh[k])
|
|
rawDarkCh[k] = pst_adpd->ptr_data[cnt+k];
|
|
}
|
|
is_TIA_Dark_Cal++;
|
|
continue;
|
|
} else if (is_TIA_Dark_Cal >= TIA_DARK_CAL_CNT
|
|
&& is_TIA_Dark_Cal < 100) {
|
|
rawDarkCh[0] -= 1470;
|
|
rawDarkCh[1] -= 205;
|
|
rawDarkCh[2] -= 210;
|
|
rawDarkCh[3] -= 230;
|
|
is_TIA_Dark_Cal = 100;
|
|
continue;
|
|
} else if (is_TIA_Dark_Cal < TIA_DARK_CAL_CNT) {
|
|
is_TIA_Dark_Cal++;
|
|
continue;
|
|
} else if (is_TIA_Dark_Cal == 100) {
|
|
reg_write(pst_adpd, 0x10,0x0001);
|
|
reg_write(pst_adpd, 0x14,0x0441);
|
|
reg_write(pst_adpd, 0x10,0x0002);
|
|
is_TIA_Dark_Cal++;
|
|
continue;
|
|
}
|
|
/* for making float mode saturated at the point */
|
|
for(ch = 0; ch < 4; ++ch) {
|
|
rawDataCh[ch] =
|
|
rawDarkCh[ch] - pst_adpd->ptr_data[cnt+ch];
|
|
rawDataCh[ch] =
|
|
((int)rawDataCh[ch] < 0)? 0 : rawDataCh[ch];
|
|
}
|
|
input_event(pst_adpd->hrm_input_dev,
|
|
EV_REL, REL_Z, sub_mode + 1);
|
|
input_event(pst_adpd->hrm_input_dev, EV_MSC,
|
|
MSC_RAW, rawDataCh[0]);
|
|
input_event(pst_adpd->hrm_input_dev, EV_MSC,
|
|
MSC_RAW, rawDataCh[1]);
|
|
input_event(pst_adpd->hrm_input_dev, EV_MSC,
|
|
MSC_RAW, rawDataCh[2]);
|
|
uncoated_ch3 = rawDataCh[2];
|
|
input_event(pst_adpd->hrm_input_dev, EV_MSC,
|
|
MSC_RAW, rawDataCh[3]);
|
|
input_sync(pst_adpd->hrm_input_dev);
|
|
coated_ch4 = rawDataCh[3];
|
|
ADPD143_dbg("adpd143 SLOT A(TIA) unch3:%d, ch4=%d\n",
|
|
uncoated_ch3, coated_ch4);
|
|
}
|
|
}
|
|
break;
|
|
case S_SAMP_XY_AB:
|
|
if (pst_adpd->fifo_size < 8 || (pst_adpd->fifo_size & 0x7)) {
|
|
pr_err("Unexpected FIFO_SIZE=%d\n",
|
|
pst_adpd->fifo_size);
|
|
break;
|
|
}
|
|
|
|
if (GET_USR_MODE(usr_mode) != TIA_ADC_USR) {
|
|
for (cnt = 0; cnt < pst_adpd->fifo_size; cnt += 8) {
|
|
unsigned int sum_slot_a = 0;
|
|
unsigned int sum_slot_b = 0;
|
|
|
|
if (atomic_read(&pst_adpd->hrmled_enabled)) {
|
|
input_event(pst_adpd->hrmled_input_dev,
|
|
EV_REL, REL_Z, sub_mode + 1);
|
|
|
|
for (ch = 0; ch < 4; ++ch) {
|
|
sum_slot_a +=
|
|
pst_adpd->ptr_data[cnt+ch];
|
|
input_event(pst_adpd->hrmled_input_dev,
|
|
EV_MSC, MSC_RAW,
|
|
pst_adpd->ptr_data[cnt+ch] + 1);
|
|
}
|
|
input_event(pst_adpd->hrmled_input_dev,
|
|
EV_REL, REL_X, sum_slot_a + 1);
|
|
|
|
for (ch = 4; ch < 8; ++ch) {
|
|
sum_slot_b +=
|
|
pst_adpd->ptr_data[cnt+ch];
|
|
input_event(pst_adpd->hrmled_input_dev,
|
|
EV_MSC, MSC_RAW,
|
|
pst_adpd->ptr_data[cnt+ch] + 1);
|
|
}
|
|
input_event(pst_adpd->hrmled_input_dev,
|
|
EV_REL, REL_Y, sum_slot_b + 1);
|
|
input_sync(pst_adpd->hrmled_input_dev);
|
|
} else {
|
|
input_event(pst_adpd->hrm_input_dev,
|
|
EV_REL, REL_Z, sub_mode + 1);
|
|
for (ch = 0; ch < 4; ++ch) {
|
|
sum_slot_a +=
|
|
pst_adpd->ptr_data[cnt+ch];
|
|
input_event(pst_adpd->hrm_input_dev,
|
|
EV_MSC, MSC_RAW,
|
|
pst_adpd->ptr_data[cnt+ch] + 1);
|
|
}
|
|
input_event(pst_adpd->hrm_input_dev,
|
|
EV_REL, REL_X, sum_slot_a + 1);
|
|
|
|
for (ch = 4; ch < 8; ++ch) {
|
|
sum_slot_b +=
|
|
pst_adpd->ptr_data[cnt+ch];
|
|
input_event(pst_adpd->hrm_input_dev,
|
|
EV_MSC, MSC_RAW,
|
|
pst_adpd->ptr_data[cnt+ch] + 1);
|
|
}
|
|
input_event(pst_adpd->hrm_input_dev,
|
|
EV_REL, REL_Y, sum_slot_b + 1);
|
|
input_sync(pst_adpd->hrm_input_dev);
|
|
ADPD143_dbg("adpd143 sum_slot_a:%d, sum_slot_b:%d\n",
|
|
sum_slot_a, sum_slot_b);
|
|
}
|
|
}
|
|
} else {
|
|
for (cnt = 0; cnt < pst_adpd->fifo_size; cnt += 8) {
|
|
input_event(pst_adpd->hrm_input_dev,
|
|
EV_REL, REL_Z, sub_mode + 1);
|
|
/* for TIA_ADC runtime Dark Calibration */
|
|
if (is_TIA_Dark_Cal < TIA_DARK_CAL_CNT
|
|
&& is_TIA_Dark_Cal > 1) {
|
|
for(k = 0; k < 4; k++) {
|
|
if (rawDarkCh[k] < pst_adpd->ptr_data[cnt+k]
|
|
|| !rawDarkCh[k])
|
|
rawDarkCh[k] = pst_adpd->ptr_data[cnt+k];
|
|
}
|
|
is_TIA_Dark_Cal++;
|
|
} else if (is_TIA_Dark_Cal >= TIA_DARK_CAL_CNT
|
|
&& is_TIA_Dark_Cal < 100) {
|
|
rawDarkCh[0] -= 1470;
|
|
rawDarkCh[1] -= 205;
|
|
rawDarkCh[2] -= 210;
|
|
rawDarkCh[3] -= 230;
|
|
is_TIA_Dark_Cal = 100;
|
|
} else if (is_TIA_Dark_Cal < TIA_DARK_CAL_CNT) {
|
|
is_TIA_Dark_Cal++;
|
|
}
|
|
/* for TIA_ADC runtime Dark Calibration */
|
|
if (is_Float_Dark_Cal < TIA_DARK_CAL_CNT &&
|
|
is_Float_Dark_Cal > 1) {
|
|
for(k = 4; k < 8; k++) {
|
|
if (rawDarkCh[k] < pst_adpd->ptr_data[cnt+k]
|
|
|| !rawDarkCh[k])
|
|
rawDarkCh[k] = pst_adpd->ptr_data[cnt+k];
|
|
}
|
|
is_Float_Dark_Cal++;
|
|
continue;
|
|
} else if (is_Float_Dark_Cal >= TIA_DARK_CAL_CNT &&
|
|
is_Float_Dark_Cal < 100) {
|
|
is_Float_Dark_Cal = 100;
|
|
continue;
|
|
} else if (is_Float_Dark_Cal < TIA_DARK_CAL_CNT) {
|
|
is_Float_Dark_Cal++;
|
|
continue;
|
|
} else if (is_Float_Dark_Cal == 100) {
|
|
reg_write(pst_adpd, 0x10,0x0001);
|
|
reg_write(pst_adpd, 0x14,0x0441);
|
|
reg_write(pst_adpd, 0x10,0x0002);
|
|
is_Float_Dark_Cal++;
|
|
continue;
|
|
}
|
|
/* for making float mode saturated at the point */
|
|
for(ch = 0; ch < 4; ++ch) {
|
|
rawDataCh[ch]
|
|
= rawDarkCh[ch] - pst_adpd->ptr_data[cnt+ch];
|
|
rawDataCh[ch]
|
|
= ((int)rawDataCh[ch] < 0)? 0 : rawDataCh[ch];
|
|
}
|
|
|
|
for (ch = 0; ch < 4; ++ch) {
|
|
if (ch == 2) uncoated_ch3 = rawDataCh[2];
|
|
if (ch == 3) {
|
|
coated_ch4 = rawDataCh[3];
|
|
/* for approximating the code continuity between float and TIA_ADC */
|
|
input_event(pst_adpd->hrm_input_dev,
|
|
EV_MSC, MSC_RAW, rawDataCh[ch]*2 );
|
|
} else {
|
|
input_event(pst_adpd->hrm_input_dev,
|
|
EV_MSC, MSC_RAW, rawDataCh[ch]);
|
|
ADPD143_dbg("adpd143 SLOT A %d:%d\n",
|
|
ch, rawDataCh[ch]);
|
|
}
|
|
}
|
|
if (uncoated_ch3 == 0) {
|
|
uncoated_ch3++;
|
|
coated_ch4++;
|
|
}
|
|
input_event(pst_adpd->hrm_input_dev, EV_REL,
|
|
REL_X, (((unsigned)coated_ch4) * 100) / uncoated_ch3);
|
|
|
|
/* for making float mode saturated at the point */
|
|
for (ch = 4; ch < 8; ++ch) {
|
|
ADPD143_dbg("adpd142 SLOT B(float mode) : rawDataCh%d : %d, rawDarkCh%d : %d\n",
|
|
ch, pst_adpd->ptr_data[cnt+ch], ch, rawDarkCh[ch]);
|
|
rawDataCh[ch] =
|
|
pst_adpd->ptr_data[cnt+ch] - rawDarkCh[ch];
|
|
rawDataCh[ch] =
|
|
((int)rawDataCh[ch] < 0)? 0 : rawDataCh[ch];
|
|
|
|
if ((int)rawDataCh[ch] >=
|
|
(FLOAT_STR_CONTROL - rawDarkCh[ch])) {
|
|
rawDataCh[ch] =
|
|
FLOAT_STR_CONTROL - rawDarkCh[ch];
|
|
if (ch == 4 &&
|
|
(((rawDataCh[0] < pst_adpd->ch1_f_str)
|
|
&& (rawDataCh[0] > pst_adpd->ch1_f_str - 20))
|
|
|| (pst_adpd->ch1_f_str == CH1_FLOAT_STR)))
|
|
pst_adpd->ch1_f_str = rawDataCh[0];
|
|
if (ch == 5 &&
|
|
(((rawDataCh[1] < pst_adpd->ch2_f_str)
|
|
&& (rawDataCh[1] > pst_adpd->ch2_f_str - 20))
|
|
|| (pst_adpd->ch2_f_str == CH2_FLOAT_STR)))
|
|
pst_adpd->ch2_f_str = rawDataCh[1];
|
|
if (ch == 6 &&
|
|
(((rawDataCh[2] < pst_adpd->ch3_f_str)
|
|
&& (rawDataCh[2] > pst_adpd->ch3_f_str - 20))
|
|
|| (pst_adpd->ch3_f_str == CH3_FLOAT_STR)))
|
|
pst_adpd->ch3_f_str = rawDataCh[2];
|
|
if (ch == 7 &&
|
|
(((rawDataCh[3] < pst_adpd->ch4_f_str)
|
|
&& (rawDataCh[3] > pst_adpd->ch4_f_str - 10))
|
|
|| (pst_adpd->ch4_f_str == CH4_FLOAT_STR)))
|
|
pst_adpd->ch4_f_str = rawDataCh[3];
|
|
}
|
|
}
|
|
|
|
if ((int)rawDataCh[0] >= pst_adpd->ch1_f_str)
|
|
rawDataCh[4] =
|
|
FLOAT_STR_CONTROL - rawDarkCh[4];
|
|
|
|
if ((int)rawDataCh[1] >= pst_adpd->ch2_f_str)
|
|
rawDataCh[5] =
|
|
FLOAT_STR_CONTROL - rawDarkCh[5];
|
|
|
|
if ((int)rawDataCh[2] >= pst_adpd->ch3_f_str)
|
|
rawDataCh[6] =
|
|
FLOAT_STR_CONTROL - rawDarkCh[6];
|
|
|
|
if ((int)rawDataCh[3] >= pst_adpd->ch4_f_str)
|
|
rawDataCh[7] = FLOAT_STR_CONTROL;
|
|
|
|
for (ch = 4; ch < 8; ++ch) {
|
|
if (GET_USR_MODE(usr_mode) == TIA_ADC_USR) {
|
|
if (ch == 6)
|
|
uncoated_ch3 = rawDataCh[6];
|
|
if (ch == 7)
|
|
coated_ch4 = rawDataCh[7];
|
|
input_event(pst_adpd->hrm_input_dev,
|
|
EV_MSC, MSC_RAW, rawDataCh[ch]);
|
|
ADPD143_dbg("adpd143 SLOT B(After Dark Cal) %d:%d\n",
|
|
ch, rawDataCh[ch]);
|
|
}
|
|
}
|
|
if (uncoated_ch3 == 0) {
|
|
uncoated_ch3++;
|
|
coated_ch4++;
|
|
}
|
|
input_event(pst_adpd->hrm_input_dev,
|
|
EV_REL, REL_Y,
|
|
(((unsigned)coated_ch4)*100)/uncoated_ch3);
|
|
input_sync(pst_adpd->hrm_input_dev);
|
|
}
|
|
}
|
|
break;
|
|
case S_SAMP_XY_B:
|
|
if (pst_adpd->fifo_size < 4 || (pst_adpd->fifo_size & 0x3)) {
|
|
pr_err("Unexpected FIFO_SIZE=%d\n",
|
|
pst_adpd->fifo_size);
|
|
break;
|
|
}
|
|
|
|
if( GET_USR_MODE(usr_mode) != TIA_ADC_USR ) {
|
|
for (cnt = 0; cnt < pst_adpd->fifo_size; cnt += 4) {
|
|
if (atomic_read(&pst_adpd->hrmled_enabled)) {
|
|
input_event(pst_adpd->hrmled_input_dev, EV_REL,
|
|
REL_Z, sub_mode + 1);
|
|
input_event(pst_adpd->hrmled_input_dev, EV_MSC,
|
|
MSC_RAW, pst_adpd->ptr_data[cnt] + 1);
|
|
input_event(pst_adpd->hrmled_input_dev, EV_MSC,
|
|
MSC_RAW, pst_adpd->ptr_data[cnt + 1] + 1);
|
|
input_event(pst_adpd->hrmled_input_dev, EV_MSC,
|
|
MSC_RAW, pst_adpd->ptr_data[cnt + 2] + 1);
|
|
input_event(pst_adpd->hrmled_input_dev, EV_MSC,
|
|
MSC_RAW, pst_adpd->ptr_data[cnt + 3] + 1);
|
|
input_sync(pst_adpd->hrmled_input_dev);
|
|
} else {
|
|
input_event(pst_adpd->hrm_input_dev, EV_REL,
|
|
REL_Z, sub_mode + 1);
|
|
input_event(pst_adpd->hrm_input_dev, EV_MSC,
|
|
MSC_RAW, pst_adpd->ptr_data[cnt] + 1);
|
|
input_event(pst_adpd->hrm_input_dev, EV_MSC,
|
|
MSC_RAW, pst_adpd->ptr_data[cnt + 1] + 1);
|
|
input_event(pst_adpd->hrm_input_dev, EV_MSC,
|
|
MSC_RAW, pst_adpd->ptr_data[cnt + 2] + 1);
|
|
input_event(pst_adpd->hrm_input_dev, EV_MSC,
|
|
MSC_RAW, pst_adpd->ptr_data[cnt + 3] + 1);
|
|
input_sync(pst_adpd->hrm_input_dev);
|
|
}
|
|
}
|
|
} else {
|
|
for (cnt = 0; cnt < pst_adpd->fifo_size; cnt += 4) {
|
|
/* for TIA_ADC runtime Dark Calibration */
|
|
if (is_Float_Dark_Cal < FLOAT_DARK_CAL_CNT &&
|
|
is_Float_Dark_Cal > 1) {
|
|
for(k = 4; k < 8; k++) {
|
|
if (rawDarkCh[k] < pst_adpd->ptr_data[cnt+k-4]
|
|
|| !rawDarkCh[k])
|
|
rawDarkCh[k] = pst_adpd->ptr_data[cnt+k-4];
|
|
}
|
|
is_Float_Dark_Cal++;
|
|
continue;
|
|
} else if (is_Float_Dark_Cal >= FLOAT_DARK_CAL_CNT
|
|
&& is_Float_Dark_Cal < 100) {
|
|
is_Float_Dark_Cal = 100;
|
|
continue;
|
|
} else if (is_Float_Dark_Cal < FLOAT_DARK_CAL_CNT) {
|
|
is_Float_Dark_Cal++;
|
|
continue;
|
|
} else if (is_Float_Dark_Cal == 100) {
|
|
reg_write(pst_adpd, 0x10,0x0001);
|
|
reg_write(pst_adpd, 0x14,0x0441);
|
|
reg_write(pst_adpd, 0x10,0x0002);
|
|
is_Float_Dark_Cal++;
|
|
}
|
|
|
|
/* for making float mode saturated at the point */
|
|
for(ch = 4; ch < 8; ++ch) {
|
|
rawDataCh[ch] =
|
|
pst_adpd->ptr_data[cnt+ch] - rawDarkCh[ch];
|
|
rawDataCh[ch] =
|
|
((int)rawDataCh[ch] < 0) ? 0 : rawDataCh[ch];
|
|
|
|
if ((int)rawDataCh[ch] >=
|
|
(FLOAT_STR_CONTROL - rawDarkCh[ch]))
|
|
rawDataCh[ch] =
|
|
FLOAT_STR_CONTROL - rawDarkCh[ch];
|
|
}
|
|
|
|
input_event(pst_adpd->hrm_input_dev, EV_REL,
|
|
REL_Z, sub_mode + 1);
|
|
input_event(pst_adpd->hrm_input_dev, EV_MSC,
|
|
MSC_RAW, rawDataCh[4]);
|
|
input_event(pst_adpd->hrm_input_dev, EV_MSC,
|
|
MSC_RAW, rawDataCh[5]);
|
|
input_event(pst_adpd->hrm_input_dev, EV_MSC,
|
|
MSC_RAW, rawDataCh[6]);
|
|
uncoated_ch3 = rawDataCh[6];
|
|
input_event(pst_adpd->hrm_input_dev, EV_MSC,
|
|
MSC_RAW, rawDataCh[7]);
|
|
input_sync(pst_adpd->hrm_input_dev);
|
|
coated_ch4 = rawDataCh[7];
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
};
|
|
}
|
|
|
|
/**
|
|
This function is used for handling FIFO when interrupt occured
|
|
@param pst_adpd the ADPD143 data structure
|
|
@return void
|
|
*/
|
|
static void
|
|
adpd143_data_handler(struct adpd143_data *pst_adpd)
|
|
{
|
|
unsigned short usr_mode = 0;
|
|
unsigned short mode = 0;
|
|
unsigned short sub_mode = 0;
|
|
mutex_lock(&pst_adpd->mutex);
|
|
usr_mode = atomic_read(&pst_adpd->adpd_mode);
|
|
mode = GET_USR_MODE(usr_mode);
|
|
sub_mode = GET_USR_SUB_MODE(usr_mode);
|
|
|
|
ADPD143_info("mode - 0x%x,\t sub_mode - 0x%x\n", mode, sub_mode);
|
|
adpd143_rd_intr_fifo(pst_adpd);
|
|
|
|
if (pst_adpd->intr_status_val == 0) {
|
|
mutex_unlock(&pst_adpd->mutex);
|
|
return;
|
|
}
|
|
|
|
adpd143_clr_intr_status(pst_adpd, mode);
|
|
|
|
switch (mode) {
|
|
case IDLE_USR:
|
|
ADPD143_dbg("IDLE_MODE\n");
|
|
adpd143_rd_fifo_data(pst_adpd);
|
|
break;
|
|
case SAMPLE_USR:
|
|
ADPD143_info("SAMPLE MODE\n");
|
|
adpd143_rd_fifo_data(pst_adpd);
|
|
adpd143_sample_event(pst_adpd);
|
|
break;
|
|
/* for supporting TIA_ADC mode */
|
|
case TIA_ADC_USR:
|
|
ADPD143_info("SAMPLE TIA ADC MODE\n");
|
|
adpd143_rd_fifo_data(pst_adpd);
|
|
adpd143_sample_event(pst_adpd);
|
|
break;
|
|
default:
|
|
ADPD143_info("DEFAULT MODE\n");
|
|
adpd143_rd_fifo_data(pst_adpd);
|
|
break;
|
|
};
|
|
|
|
mutex_unlock(&pst_adpd->mutex);
|
|
}
|
|
|
|
/**
|
|
This function is a handler for WorkQueue
|
|
@param ptr_work linux work structure
|
|
@return void
|
|
*/
|
|
static void
|
|
adpd143_wq_handler(struct work_struct *ptr_work)
|
|
{
|
|
|
|
struct adpd143_data *pst_adpd = container_of(ptr_work,
|
|
struct adpd143_data, work);
|
|
|
|
struct timeval wkq_start;
|
|
struct timeval wkq_comp;
|
|
int diff_usec = 0;
|
|
|
|
do_gettimeofday(&wkq_start);
|
|
|
|
diff_usec = timeval_compare(&wkq_start,
|
|
&pst_adpd->stats.stamp.interrupt_trigger);
|
|
|
|
if (diff_usec > 1) {
|
|
if (diff_usec >
|
|
atomic_read(&pst_adpd->stats.wq_schedule_time_peak_usec))
|
|
atomic_set(&pst_adpd->stats.wq_schedule_time_peak_usec,
|
|
diff_usec);
|
|
atomic_set(&pst_adpd->stats.wq_schedule_time_last_usec,
|
|
diff_usec);
|
|
ewma_add(&pst_adpd->stats.wq_schedule_time_avg_usec, diff_usec);
|
|
}
|
|
|
|
adpd143_data_handler(pst_adpd);
|
|
|
|
do_gettimeofday(&wkq_comp);
|
|
|
|
diff_usec = timeval_compare(&wkq_comp, &wkq_start);
|
|
|
|
if (diff_usec > 1) {
|
|
if (diff_usec >
|
|
atomic_read(&pst_adpd->stats.data_process_time_peak_usec))
|
|
atomic_set(
|
|
&pst_adpd->stats.data_process_time_peak_usec,
|
|
diff_usec);
|
|
atomic_set(&pst_adpd->stats.data_process_time_last_usec,
|
|
diff_usec);
|
|
ewma_add(&pst_adpd->stats.data_process_time_avg_usec,
|
|
diff_usec);
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
This function is used for handling Interrupt from ADPD143
|
|
@param irq is Interrupt number
|
|
@param dev_id is pointer point to ADPD143 data structure
|
|
@return irqreturn_t is a Interrupt flag
|
|
*/
|
|
static irqreturn_t
|
|
adpd143_isr_handler(int irq, void *dev_id)
|
|
{
|
|
struct adpd143_data *pst_adpd = dev_id;
|
|
|
|
atomic_inc(&pst_adpd->stats.interrupts);
|
|
|
|
if (!work_pending(&pst_adpd->work)) {
|
|
do_gettimeofday(&pst_adpd->stats.stamp.interrupt_trigger);
|
|
ADPD143_info("%s\n", __func__);
|
|
if (!queue_work(pst_adpd->ptr_adpd143_wq_st, &pst_adpd->work))
|
|
atomic_inc(&pst_adpd->stats.wq_pending);
|
|
} else {
|
|
atomic_inc(&pst_adpd->stats.wq_pending);
|
|
ADPD143_info("work_pending !!\n");
|
|
}
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
/**
|
|
This function is used for updating the ADPD143 structure after configuration
|
|
@param pst_adpd the ADPD143 data structure
|
|
@return void
|
|
*/
|
|
static void
|
|
adpd143_update_config(struct adpd143_data *pst_adpd)
|
|
{
|
|
return;
|
|
}
|
|
|
|
/**
|
|
This function is used for loading the configuration data to ADPD143 chip
|
|
0 - From file "/data/misc/adpd143_configuration.dcfg"
|
|
1 - From Static defined Array
|
|
@param pst_adpd the ADPD143 data structure
|
|
@param config configuration command
|
|
@return int status
|
|
*/
|
|
static int
|
|
adpd143_configuration(struct adpd143_data *pst_adpd, unsigned char config)
|
|
{
|
|
struct adpd_platform_data *ptr_config;
|
|
unsigned short addr;
|
|
unsigned short data;
|
|
unsigned short cnt = 0;
|
|
int ret = 0;
|
|
|
|
/* must be removed in TIA mode */
|
|
/* adpd143_mode_switching(pst_adpd, 0); */
|
|
if (config == FRM_FILE) {
|
|
ret = adpd143_read_config_file(pst_adpd);
|
|
/* ADPD143_info("ARRAY_SIZE - %d\n", size);*/
|
|
} else {
|
|
ret = FRM_ARR;
|
|
}
|
|
if (ret == 0)
|
|
ptr_config = pst_adpd->ptr_config;
|
|
else
|
|
ptr_config = &adpd143_config_data;
|
|
|
|
for (cnt = 0; cnt < ptr_config->config_size; cnt++) {
|
|
addr = (unsigned short) ((0xFFFF0000 &
|
|
ptr_config->config_data[cnt]) >> 16);
|
|
data = (unsigned short) (0x0000FFFF &
|
|
ptr_config->config_data[cnt]);
|
|
|
|
ADPD143_dbg("addr[0x%04x] = 0x%04x\n", addr, data);
|
|
reg_write(pst_adpd, addr, data);
|
|
}
|
|
|
|
adpd143_update_config(pst_adpd);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
adpd143_TIA_ADC_configuration(struct adpd143_data *pst_adpd, unsigned char config)
|
|
{
|
|
struct adpd_platform_data *ptr_config;
|
|
unsigned short addr;
|
|
unsigned short data;
|
|
unsigned short cnt = 0;
|
|
int ret = 0;
|
|
|
|
pr_info("%s start", __func__);
|
|
if (config == FRM_FILE) {
|
|
ret = adpd143_read_config_file(pst_adpd);
|
|
/* ADPD143_info("ARRAY_SIZE - %d\n", size);*/
|
|
} else {
|
|
ret = FRM_ARR;
|
|
}
|
|
if (ret == 0)
|
|
ptr_config = pst_adpd->ptr_config;
|
|
else
|
|
ptr_config = &adpd143_tia_adc_config_data;
|
|
|
|
for (cnt = 0; cnt < ptr_config->config_size; cnt++) {
|
|
addr = (unsigned short) ((0xFFFF0000 &
|
|
ptr_config->config_data[cnt]) >> 16);
|
|
data = (unsigned short) (0x0000FFFF &
|
|
ptr_config->config_data[cnt]);
|
|
|
|
ADPD143_dbg("addr[0x%04x] = 0x%04x\n", addr, data);
|
|
reg_write(pst_adpd, addr, data);
|
|
}
|
|
|
|
adpd143_update_config(pst_adpd);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* read & write efs for hrm sensor */
|
|
static int osc_trim_efs_register_open(struct adpd143_data *pst_adpd)
|
|
{
|
|
struct file *osc_filp = NULL;
|
|
char buffer[60] = {0, };
|
|
int err = 0;
|
|
int osc_trim_32k, osc_trim_32m;
|
|
int osc_trim_addr26, osc_trim_addr28, osc_trim_addr29;
|
|
mm_segment_t old_fs;
|
|
|
|
old_fs = get_fs();
|
|
set_fs(KERNEL_DS);
|
|
|
|
osc_filp = filp_open(OSCILLATOR_TRIM_FILE_PATH, O_RDONLY, 0666);
|
|
if (IS_ERR(osc_filp)) {
|
|
err = PTR_ERR(osc_filp);
|
|
if (err != -ENOENT)
|
|
pr_err("%s: Can't open oscillator trim file\n",
|
|
__func__);
|
|
set_fs(old_fs);
|
|
return err;
|
|
}
|
|
|
|
err = osc_filp->f_op->read(osc_filp,
|
|
(char *)buffer,
|
|
60 * sizeof(char), &osc_filp->f_pos);
|
|
if (err != (60 * sizeof(char))) {
|
|
pr_err("%s: Can't read the oscillator trim data from file\n",
|
|
__func__);
|
|
err = -EIO;
|
|
}
|
|
sscanf(buffer, "%d,%d,%d,%d,%d", &osc_trim_32k, &osc_trim_32m,
|
|
&osc_trim_addr26, &osc_trim_addr28, &osc_trim_addr29);
|
|
pr_info("%s: (%d,%d,%d,%d,%d)\n", __func__, osc_trim_32k,
|
|
osc_trim_32m, osc_trim_addr26, osc_trim_addr28, osc_trim_addr29);
|
|
|
|
pst_adpd->osc_trim_32K_value = osc_trim_32k;
|
|
pst_adpd->osc_trim_32M_value = osc_trim_32m;
|
|
pst_adpd->osc_trim_addr26_value = osc_trim_addr26;
|
|
pst_adpd->osc_trim_addr28_value = osc_trim_addr28;
|
|
pst_adpd->osc_trim_addr29_value = osc_trim_addr29;
|
|
|
|
filp_close(osc_filp, current->files);
|
|
set_fs(old_fs);
|
|
|
|
return err;
|
|
}
|
|
|
|
static int osc_trim_efs_register_save(struct adpd143_data *pst_adpd)
|
|
{
|
|
struct file *osc_filp = NULL;
|
|
mm_segment_t old_fs;
|
|
char buffer[60] = {0, };
|
|
int osc_trim_32k, osc_trim_32m;
|
|
int osc_trim_addr26, osc_trim_addr28, osc_trim_addr29;
|
|
int err = 0;
|
|
|
|
old_fs = get_fs();
|
|
set_fs(KERNEL_DS);
|
|
|
|
osc_filp = filp_open(OSCILLATOR_TRIM_FILE_PATH,
|
|
O_CREAT | O_TRUNC | O_WRONLY, 0666);
|
|
if (IS_ERR(osc_filp)) {
|
|
pr_err("%s: Can't open oscillator trim file\n",
|
|
__func__);
|
|
set_fs(old_fs);
|
|
err = PTR_ERR(osc_filp);
|
|
return err;
|
|
}
|
|
|
|
osc_trim_32k = reg_read(pst_adpd, OSC_TRIM_32K_REG);
|
|
osc_trim_32m = reg_read(pst_adpd, OSC_TRIM_32M_REG);
|
|
osc_trim_addr26 = reg_read(pst_adpd, OSC_TRIM_ADDR26_REG);
|
|
osc_trim_addr28 = reg_read(pst_adpd, OSC_TRIM_ADDR28_REG);
|
|
osc_trim_addr29 = reg_read(pst_adpd, OSC_TRIM_ADDR29_REG);
|
|
|
|
sprintf(buffer, "%d,%d,%d,%d,%d", osc_trim_32k, osc_trim_32m,
|
|
osc_trim_addr26, osc_trim_addr28, osc_trim_addr29);
|
|
pr_info("%s: (%d,%d,%d,%d,%d)\n", __func__, osc_trim_32k, osc_trim_32m,
|
|
osc_trim_addr26, osc_trim_addr28, osc_trim_addr29);
|
|
|
|
err = osc_filp->f_op->write(osc_filp,
|
|
(char *)&buffer,
|
|
60 * sizeof(char), &osc_filp->f_pos);
|
|
if (err != (60 * sizeof(char))) {
|
|
pr_err("%s: Can't write the oscillator trim data to file\n",
|
|
__func__);
|
|
err = -EIO;
|
|
}
|
|
pst_adpd->osc_trim_32K_value = osc_trim_32k;
|
|
pst_adpd->osc_trim_32M_value = osc_trim_32m;
|
|
pst_adpd->osc_trim_addr26_value = osc_trim_addr26;
|
|
pst_adpd->osc_trim_addr28_value = osc_trim_addr28;
|
|
pst_adpd->osc_trim_addr29_value = osc_trim_addr29;
|
|
|
|
filp_close(osc_filp, current->files);
|
|
set_fs(old_fs);
|
|
|
|
return err;
|
|
}
|
|
|
|
|
|
/**
|
|
This function clears all the statistic counters.
|
|
@param pst_adpd the ADPD143 data structure
|
|
@return void
|
|
*/
|
|
static void
|
|
adpd_stat_reset(struct adpd143_data *pst_adpd)
|
|
{
|
|
atomic_set(&pst_adpd->stats.interrupts, 0);
|
|
atomic_set(&pst_adpd->stats.wq_pending, 0);
|
|
atomic_set(&pst_adpd->stats.wq_schedule_time_peak_usec, 0);
|
|
atomic_set(&pst_adpd->stats.wq_schedule_time_last_usec, 0);
|
|
atomic_set(&pst_adpd->stats.data_process_time_peak_usec, 0);
|
|
atomic_set(&pst_adpd->stats.data_process_time_last_usec, 0);
|
|
atomic_set(&pst_adpd->stats.fifo_requires_sync, 0);
|
|
atomic_set(&pst_adpd->stats.fifo_bytes[0], 0);
|
|
atomic_set(&pst_adpd->stats.fifo_bytes[1], 0);
|
|
atomic_set(&pst_adpd->stats.fifo_bytes[2], 0);
|
|
atomic_set(&pst_adpd->stats.fifo_bytes[3], 0);
|
|
ewma_init(&pst_adpd->stats.wq_schedule_time_avg_usec, 2048, 128);
|
|
ewma_init(&pst_adpd->stats.data_process_time_avg_usec, 2048, 128);
|
|
}
|
|
|
|
/* SAMPLE - SYSFS ATTRIBUTE*/
|
|
/**
|
|
This function is used for getting the status of the sample enable bit
|
|
@param dev linux device structure
|
|
@param attr pointer point linux device_attribute structure
|
|
@param buf pointer point the buffer
|
|
@return ssize_t size of data presnt in the buffer
|
|
*/
|
|
static ssize_t
|
|
hrm_attr_get_enable(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct adpd143_data *pst_adpd = dev_get_drvdata(dev);
|
|
int val = atomic_read(&pst_adpd->hrm_enabled);
|
|
return sprintf(buf, "%d\n", val);
|
|
}
|
|
|
|
/**
|
|
This function is used for enabling the Sample mode
|
|
@param dev linux device structure
|
|
@param attr pointer point linux device_attribute structure
|
|
@param buf pointer point the buffer
|
|
@param size buffer size
|
|
@return ssize_t size of data written to the buffer
|
|
*/
|
|
static ssize_t
|
|
hrm_attr_set_enable(struct device *dev, struct device_attribute *attr,
|
|
const char *buf, size_t size)
|
|
{
|
|
struct adpd143_data *pst_adpd = dev_get_drvdata(dev);
|
|
unsigned short parse_data[2];
|
|
unsigned short mode = 0;
|
|
unsigned short osc_trim_32k, osc_trim_32m;
|
|
unsigned short osc_trim_addr26 = 0, osc_trim_addr28 = 0, osc_trim_addr29 = 0;
|
|
int err;
|
|
|
|
int val;
|
|
int n = sscanf(buf, "%d", &val);
|
|
(void)n;
|
|
memset(parse_data, 0, sizeof(parse_data));
|
|
|
|
if (pst_adpd->osc_trim_open_enable == 1) {
|
|
err = osc_trim_efs_register_open(pst_adpd);
|
|
if (err < 0) {
|
|
pr_err("%s: osc_trim_efs_register() empty(%d)\n",
|
|
__func__, err);
|
|
osc_trim_32k = OSC_DEFAULT_32K_SET;
|
|
osc_trim_32m = OSC_DEFAULT_32M_SET;
|
|
} else {
|
|
osc_trim_32k = (unsigned short)pst_adpd->osc_trim_32K_value;
|
|
osc_trim_32m = (unsigned short)pst_adpd->osc_trim_32M_value;
|
|
osc_trim_addr26 = (unsigned short)pst_adpd->osc_trim_addr26_value;
|
|
osc_trim_addr28 = (unsigned short)pst_adpd->osc_trim_addr28_value;
|
|
osc_trim_addr29 = (unsigned short)pst_adpd->osc_trim_addr29_value;
|
|
|
|
reg_write(pst_adpd, OSC_TRIM_ADDR26_REG, osc_trim_addr26);
|
|
reg_write(pst_adpd, OSC_TRIM_ADDR28_REG, osc_trim_addr28);
|
|
reg_write(pst_adpd, OSC_TRIM_ADDR29_REG, osc_trim_addr29);
|
|
}
|
|
pr_info("%s: 32K[%02x], 32M[%02x], addr26[%02x], addr28[%02x], addr29[%02x]\n",
|
|
__func__, osc_trim_32k, osc_trim_32m, osc_trim_addr26,
|
|
osc_trim_addr28, osc_trim_addr29);
|
|
reg_write(pst_adpd, OSC_TRIM_32K_REG, osc_trim_32k);
|
|
reg_write(pst_adpd, OSC_TRIM_32M_REG, osc_trim_32m);
|
|
pst_adpd->osc_trim_open_enable = 0;
|
|
}
|
|
|
|
if (val == 1) {
|
|
pr_info("%s: enable HRM.\n", __func__);
|
|
cmd_parsing("0x32", 1, parse_data);
|
|
atomic_set(&pst_adpd->hrm_enabled, HRM_SAMPLE_MODE);
|
|
} else if (val == 2) {
|
|
pr_info("%s: enable Ambient Light Measurement.\n",
|
|
__func__);
|
|
cmd_parsing("0x51", 1, parse_data);
|
|
atomic_set(&pst_adpd->hrm_enabled, HRM_TIA_ADC_MODE);
|
|
} else {
|
|
pr_info("%s: disable.\n", __func__);
|
|
cmd_parsing("0x0", 1, parse_data);
|
|
atomic_set(&pst_adpd->hrm_enabled, HRM_IDLE_MODE);
|
|
}
|
|
mode = GET_USR_MODE(parse_data[0]);
|
|
|
|
if (GET_USR_MODE(parse_data[0]) < MAX_MODE) {
|
|
if ((GET_USR_SUB_MODE(parse_data[0])) <
|
|
__mode_recv_frm_usr[mode].size) {
|
|
adpd143_mode_switching(pst_adpd, parse_data[0]);
|
|
} else {
|
|
ADPD143_dbg("Sub mode Out of bound\n");
|
|
adpd143_mode_switching(pst_adpd, 0);
|
|
}
|
|
} else {
|
|
ADPD143_dbg("Mode out of bound\n");
|
|
adpd143_mode_switching(pst_adpd, 0);
|
|
}
|
|
return size;
|
|
}
|
|
|
|
/* GENERAL - SYSFS ATTRIBUTE*/
|
|
/**
|
|
This function is used for getting the status of the adpd_mode
|
|
@param dev linux device structure
|
|
@param attr pointer point linux device_attribute structure
|
|
@param buf pointer point the buffer
|
|
@return ssize_t size of data presnt in the buffer
|
|
*/
|
|
static ssize_t
|
|
attr_get_mode(struct device *dev, struct device_attribute *attr, char *buf)
|
|
{
|
|
struct adpd143_data *pst_adpd = dev_get_drvdata(dev);
|
|
int val = atomic_read(&pst_adpd->adpd_mode);
|
|
return sprintf(buf, "%d\n", val);
|
|
}
|
|
|
|
/**
|
|
This function is used for switching ADPD143 mode
|
|
@param dev linux device structure
|
|
@param attr pointer point linux device_attribute structure
|
|
@param buf pointer point the buffer
|
|
@param size buffer size
|
|
@return ssize_t size of data written to the buffer
|
|
*/
|
|
static ssize_t
|
|
attr_set_mode(struct device *dev, struct device_attribute *attr,
|
|
const char *buf, size_t size)
|
|
{
|
|
struct adpd143_data *pst_adpd = dev_get_drvdata(dev);
|
|
unsigned short parse_data[2];
|
|
unsigned short mode = 0;
|
|
|
|
memset(parse_data, 0, sizeof(parse_data));
|
|
cmd_parsing(buf, 1, parse_data);
|
|
|
|
ADPD143_info("Mode requested 0x%02x\n", parse_data[0]);
|
|
pr_info("%s: data0[%02x], data1[%02x]\n",
|
|
__func__, parse_data[0], parse_data[1]);
|
|
mode = GET_USR_MODE(parse_data[0]);
|
|
if (GET_USR_MODE(parse_data[0]) < MAX_MODE) {
|
|
if ((GET_USR_SUB_MODE(parse_data[0])) <
|
|
__mode_recv_frm_usr[mode].size) {
|
|
adpd143_mode_switching(pst_adpd, parse_data[0]);
|
|
} else {
|
|
ADPD143_dbg("Sub mode Out of bound\n");
|
|
adpd143_mode_switching(pst_adpd, 0);
|
|
}
|
|
} else {
|
|
ADPD143_dbg("Mode out of bound\n");
|
|
adpd143_mode_switching(pst_adpd, 0);
|
|
}
|
|
|
|
return size;
|
|
}
|
|
|
|
/**
|
|
This function is used for reading the register value
|
|
@param dev linux device structure
|
|
@param attr pointer point linux device_attribute structure
|
|
@param buf pointer point the buffer
|
|
@return ssize_t size of data presnt in the buffer
|
|
*/
|
|
static ssize_t
|
|
attr_reg_read_get(struct device *dev, struct device_attribute *attr, char *buf)
|
|
{
|
|
struct adpd143_data *pst_adpd = dev_get_drvdata(dev);
|
|
ADPD143_dbg("Regval: 0x%4x\n", pst_adpd->sysfslastreadval);
|
|
return sprintf(buf, "0x%04x\n", pst_adpd->sysfslastreadval);
|
|
}
|
|
|
|
/**
|
|
This function is used for writing the register for reading back
|
|
@param dev linux device structure
|
|
@param attr pointer point linux device_attribute structure
|
|
@param buf pointer point the buffer
|
|
@param size buffer size
|
|
@return ssize_t size of data written to the buffer
|
|
*/
|
|
static ssize_t
|
|
attr_reg_read_set(struct device *dev, struct device_attribute *attr,
|
|
const char *buf, size_t size)
|
|
{
|
|
struct adpd143_data *pst_adpd = dev_get_drvdata(dev);
|
|
unsigned short addr, cnt;
|
|
unsigned short parse_data[4];
|
|
unsigned short ret;
|
|
|
|
memset(parse_data, 0, sizeof(unsigned short) * 4);
|
|
cmd_parsing(buf, 2, parse_data);
|
|
addr = parse_data[0];
|
|
cnt = parse_data[1];
|
|
|
|
mutex_lock(&pst_adpd->mutex);
|
|
|
|
pst_adpd->sysfs_I2C_regaddr = addr;
|
|
|
|
ret = adpd143_sysfs_I2C_read(pst_adpd);
|
|
if (ret != 0xFFFF) {
|
|
ADPD143_dbg("RegRead_Store: addr = 0x%04X,value = 0x%04X\n",
|
|
addr, ret);
|
|
pst_adpd->sysfslastreadval = ret;
|
|
} else {
|
|
ADPD143_dbg("%s: Error\n", __func__);
|
|
pst_adpd->sysfslastreadval = (unsigned short) -1;
|
|
}
|
|
|
|
mutex_unlock(&pst_adpd->mutex);
|
|
return size;
|
|
}
|
|
|
|
static int adpd_power_enable(struct adpd143_data *data, int en)
|
|
{
|
|
int rc = 0;
|
|
struct regulator *regulator_led_3p3;
|
|
struct regulator *regulator_vdd_1p8;
|
|
|
|
if (data->is_sub_pmic) {
|
|
regulator_vdd_1p8 = regulator_get(NULL, data->vdd_1p8);
|
|
if (IS_ERR(regulator_vdd_1p8) || regulator_vdd_1p8 == NULL) {
|
|
pr_err("%s: vdd_1p8 regulator_get fail\n", __func__);
|
|
return -ENODEV;
|
|
}
|
|
|
|
regulator_led_3p3 = regulator_get(NULL, data->led_3p3);
|
|
if (IS_ERR(regulator_led_3p3) || regulator_led_3p3 == NULL) {
|
|
pr_err("%s: vdd_3p3 regulator_get fail\n", __func__);
|
|
regulator_put(regulator_vdd_1p8);
|
|
return -ENODEV;
|
|
}
|
|
} else {
|
|
regulator_vdd_1p8 = regulator_get(&data->client->dev, "adpd143_1p8");
|
|
if (IS_ERR(regulator_vdd_1p8) || regulator_vdd_1p8 == NULL) {
|
|
pr_err("%s: vdd_1p8 regulator_get fail\n", __func__);
|
|
return -ENODEV;
|
|
}
|
|
|
|
regulator_led_3p3 = regulator_get(&data->client->dev, "adpd143_3p3");
|
|
if (IS_ERR(regulator_led_3p3) || regulator_led_3p3 == NULL) {
|
|
pr_err("%s: vdd_3p3 regulator_get fail\n", __func__);
|
|
regulator_put(regulator_vdd_1p8);
|
|
return -ENODEV;
|
|
}
|
|
}
|
|
pr_info("%s: onoff = %d\n", __func__, en);
|
|
|
|
if (en == HRM_LDO_ON) {
|
|
if (data->is_sub_pmic) {
|
|
rc = regulator_set_voltage(regulator_vdd_1p8, 1800000, 1800000);
|
|
if (rc < 0) {
|
|
pr_err("%s: set vdd_1p8 failed, rc=%d\n",
|
|
__func__, rc);
|
|
goto done;
|
|
}
|
|
}
|
|
rc = regulator_enable(regulator_vdd_1p8);
|
|
if (rc) {
|
|
pr_err("%s: enable vdd_1p8 failed, rc=%d\n",
|
|
__func__, rc);
|
|
goto done;
|
|
}
|
|
|
|
if (data->is_sub_pmic) {
|
|
rc = regulator_set_voltage(regulator_led_3p3, 3300000, 3300000);
|
|
if (rc < 0) {
|
|
pr_err("%s: set led_3p3 failed, rc=%d\n",
|
|
__func__, rc);
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
rc = regulator_enable(regulator_led_3p3);
|
|
if (rc) {
|
|
pr_err("%s: enable led_3p3 failed, rc=%d\n",
|
|
__func__, rc);
|
|
goto done;
|
|
}
|
|
} else {
|
|
rc = regulator_disable(regulator_vdd_1p8);
|
|
if (rc) {
|
|
pr_err("%s: disable vdd_1p8 failed, rc=%d\n",
|
|
__func__, rc);
|
|
goto done;
|
|
}
|
|
|
|
rc = regulator_disable(regulator_led_3p3);
|
|
if (rc) {
|
|
pr_err("%s: disable led_3p3 failed, rc=%d\n",
|
|
__func__, rc);
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
done:
|
|
regulator_put(regulator_led_3p3);
|
|
regulator_put(regulator_vdd_1p8);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int adpd_parse_dt(struct adpd143_data *data, struct device *dev)
|
|
{
|
|
struct device_node *this_node = dev->of_node;
|
|
enum of_gpio_flags flags;
|
|
int rc;
|
|
|
|
if (this_node == NULL)
|
|
return -ENODEV;
|
|
|
|
data->hrm_int = of_get_named_gpio_flags(this_node,
|
|
"adpd143,irq_gpio", 0, &flags);
|
|
pr_err("%s: get hrm_int(data->hrm_int)(%d) \n", __func__, data->hrm_int);
|
|
if (data->hrm_int < 0) {
|
|
pr_err("%s: get hrm_int(%d) error\n", __func__, data->hrm_int);
|
|
return -ENODEV;
|
|
}
|
|
|
|
rc = gpio_request(data->hrm_int, "hrm_int");
|
|
if (rc) {
|
|
pr_err("%s: failed to request hrm_int\n", __func__);
|
|
goto err_int_gpio_req;
|
|
}
|
|
|
|
rc = gpio_direction_input(data->hrm_int);
|
|
if (rc) {
|
|
pr_err("%s: failed to set hrm_int as input\n", __func__);
|
|
goto err_int_gpio_direction_input;
|
|
}
|
|
|
|
if (of_property_read_u32(this_node, "adpd143,use-sub-pmic",
|
|
&data->is_sub_pmic))
|
|
data->is_sub_pmic = 0;
|
|
|
|
if (data->is_sub_pmic) {
|
|
if (of_property_read_string(this_node, "adpd143,vdd_1p8",
|
|
&data->vdd_1p8) < 0)
|
|
pr_err("%s: get vdd_1p8 error\n", __func__);
|
|
|
|
if (of_property_read_string(this_node, "adpd143,led_3p3",
|
|
&data->led_3p3) < 0)
|
|
pr_err("%s: get led_3p3 error\n", __func__);
|
|
}
|
|
|
|
data->p = devm_pinctrl_get(dev);
|
|
if (IS_ERR(data->p)) {
|
|
pr_err("%s: failed pinctrl_get\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
data->pins_sleep = pinctrl_lookup_state(data->p, PINCTRL_STATE_SLEEP);
|
|
if(IS_ERR(data->pins_sleep)) {
|
|
pr_err("%s : could not get pins sleep_state (%li)\n",
|
|
__func__, PTR_ERR(data->pins_sleep));
|
|
pinctrl_put(data->p);
|
|
return -EINVAL;
|
|
}
|
|
|
|
data->pins_idle = pinctrl_lookup_state(data->p, PINCTRL_STATE_IDLE);
|
|
if(IS_ERR(data->pins_idle)) {
|
|
pr_err("%s : could not get pins idle_state (%li)\n",
|
|
__func__, PTR_ERR(data->pins_idle));
|
|
pinctrl_put(data->p);
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
err_int_gpio_direction_input:
|
|
gpio_free(data->hrm_int);
|
|
err_int_gpio_req:
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
This function is used for writing a particular data to the register
|
|
@param dev linux device structure
|
|
@param attr pointer point linux device_attribute structure
|
|
@param buf pointer point the buffer
|
|
@param size buffer size
|
|
@return ssize_t size of data written to the buffer
|
|
*/
|
|
static ssize_t
|
|
attr_reg_write_set(struct device *dev, struct device_attribute *attr,
|
|
const char *buf, size_t size)
|
|
{
|
|
struct adpd143_data *pst_adpd = dev_get_drvdata(dev);
|
|
unsigned short cnt;
|
|
unsigned short parse_data[4];
|
|
unsigned short ret;
|
|
|
|
memset(parse_data, 0, sizeof(unsigned short) * 4);
|
|
cmd_parsing(buf, 3, parse_data);
|
|
if (parse_data[1] != 1) {
|
|
ADPD143_dbg("few many argument!!\n");
|
|
goto err_reg_write_argument;
|
|
}
|
|
|
|
pst_adpd->sysfs_I2C_regaddr = parse_data[0];
|
|
cnt = parse_data[1];
|
|
pst_adpd->sysfs_I2C_regval = parse_data[2];
|
|
mutex_lock(&pst_adpd->mutex);
|
|
ret = adpd143_sysfs_I2C_write(pst_adpd);
|
|
if (ret == pst_adpd->sysfs_I2C_regval) {
|
|
ADPD143_dbg("Reg[0x%04x] = 0x%04x\n",
|
|
pst_adpd->sysfs_I2C_regaddr,
|
|
pst_adpd->sysfs_I2C_regval);
|
|
} else {
|
|
ADPD143_dbg("Reg write error!!\n");
|
|
}
|
|
|
|
adpd143_update_config(pst_adpd);
|
|
|
|
mutex_unlock(&pst_adpd->mutex);
|
|
err_reg_write_argument:
|
|
return size;
|
|
}
|
|
|
|
/**
|
|
This function is used for getting the status of configuration
|
|
TBD
|
|
@param dev linux device structure
|
|
@param attr pointer point linux device_attribute structure
|
|
@param buf pointer point the buffer
|
|
@return ssize_t size of data presnt in the buffer
|
|
*/
|
|
static ssize_t
|
|
attr_config_get(struct device *dev, struct device_attribute *attr, char *buf)
|
|
{
|
|
return sprintf(buf, "status of config thread\n");
|
|
}
|
|
|
|
/**
|
|
This function is used for wrting the configuration value to the register
|
|
0 - write the configuration data present in file to ADPD143
|
|
1 - write the configuration data present in Array to ADPD143
|
|
@param dev linux device structure
|
|
@param attr pointer point linux device_attribute structure
|
|
@param buf pointer point the buffer
|
|
@param size buffer size
|
|
@return ssize_t size of data written to the buffer
|
|
*/
|
|
static ssize_t
|
|
attr_config_set(struct device *dev, struct device_attribute *attr,
|
|
const char *buf, size_t size)
|
|
{
|
|
struct adpd143_data *pst_adpd = dev_get_drvdata(dev);
|
|
unsigned short parse_data[1];
|
|
memset(parse_data, 0, sizeof(unsigned short) * 1);
|
|
cmd_parsing(buf, 1, parse_data);
|
|
|
|
if (parse_data[0] == FRM_ARR)
|
|
adpd143_configuration(pst_adpd, FRM_ARR);
|
|
else if (parse_data[0] == FRM_FILE)
|
|
adpd143_configuration(pst_adpd, FRM_FILE);
|
|
else
|
|
ADPD143_dbg("set 1 to config\n");
|
|
return size;
|
|
}
|
|
|
|
|
|
/**
|
|
This function is used for getting the status of adpd143 and driver
|
|
@param dev linux device structure
|
|
@param attr pointer point linux device_attribute structure
|
|
@param buf pointer point the buffer
|
|
@return ssize_t size of data presnt in the buffer
|
|
*/
|
|
static ssize_t
|
|
attr_stat_get(struct device *dev, struct device_attribute *attr, char *buf)
|
|
{
|
|
struct adpd143_data *pst_adpd = dev_get_drvdata(dev);
|
|
unsigned int interrupts = atomic_read(&pst_adpd->stats.interrupts);
|
|
unsigned int wq_pending = atomic_read(&pst_adpd->stats.wq_pending);
|
|
unsigned int wq_schedule_time_peak_usec =
|
|
atomic_read(&pst_adpd->stats.wq_schedule_time_peak_usec);
|
|
unsigned int wq_schedule_time_last_usec =
|
|
atomic_read(&pst_adpd->stats.wq_schedule_time_last_usec);
|
|
unsigned int data_process_time_peak_usec =
|
|
atomic_read(&pst_adpd->stats.data_process_time_peak_usec);
|
|
unsigned int data_process_time_last_usec =
|
|
atomic_read(&pst_adpd->stats.data_process_time_last_usec);
|
|
|
|
return sprintf(buf, "\
|
|
interrupts : %u\n\
|
|
wq_pending : %u\n\
|
|
wq_schedule_time_peak_usec : %u\n\
|
|
wq_schedule_time_avg_usec : %d\n\
|
|
wq_schedule_time_last_usec : %u\n\
|
|
data_process_time_peak_usec : %u\n\
|
|
data_process_time_avg_usec : %d\n\
|
|
data_process_time_last_usec : %u\n\
|
|
fifo_requires_sync : %d\n\
|
|
fifo bytes history : [%d %d %d %d]\n\
|
|
ADPD143 driver version : %s\n\
|
|
ADPD143 Release date : %s\n",
|
|
|
|
interrupts, wq_pending,
|
|
wq_schedule_time_peak_usec,
|
|
(int)ewma_read(&pst_adpd->stats.wq_schedule_time_avg_usec),
|
|
wq_schedule_time_last_usec,
|
|
data_process_time_peak_usec,
|
|
(int)ewma_read(&pst_adpd->stats.data_process_time_avg_usec),
|
|
data_process_time_last_usec,
|
|
atomic_read(&pst_adpd->stats.fifo_requires_sync),
|
|
atomic_read(&pst_adpd->stats.fifo_bytes[0]),
|
|
atomic_read(&pst_adpd->stats.fifo_bytes[1]),
|
|
atomic_read(&pst_adpd->stats.fifo_bytes[2]),
|
|
atomic_read(&pst_adpd->stats.fifo_bytes[3]),
|
|
ADPD143_VERSION,
|
|
ADPD143_RELEASE_DATE);
|
|
|
|
}
|
|
|
|
#define ADPD143_STAT_RESET 1
|
|
|
|
/**
|
|
This function is used for wrting the adpd stat value to zero
|
|
@param dev linux device structure
|
|
@param attr pointer point linux device_attribute structure
|
|
@param buf pointer point the buffer
|
|
@param size buffer size
|
|
@return ssize_t size of data written to the buffer
|
|
*/
|
|
static ssize_t
|
|
attr_stat_set(struct device *dev, struct device_attribute *attr,
|
|
const char *buf, size_t size)
|
|
{
|
|
struct adpd143_data *pst_adpd = dev_get_drvdata(dev);
|
|
unsigned short parse_data[1];
|
|
|
|
memset(parse_data, 0, sizeof(unsigned short) * 1);
|
|
cmd_parsing(buf, 1, parse_data);
|
|
|
|
if (parse_data[0] == ADPD143_STAT_RESET) {
|
|
ADPD143_dbg("Resetting statistics\n");
|
|
adpd_stat_reset(pst_adpd);
|
|
}
|
|
|
|
return size;
|
|
}
|
|
|
|
/**
|
|
array of hrm attributes
|
|
*/
|
|
static struct device_attribute hrm_attributes[] = {
|
|
__ATTR(enable, 0664, hrm_attr_get_enable, hrm_attr_set_enable),
|
|
};
|
|
|
|
/**
|
|
This function is used for creating sysfs attribute for hrm
|
|
@param dev linux device structure
|
|
@return int status of attribute creation
|
|
*/
|
|
static int
|
|
create_sysfs_interfaces_hrm(struct device *dev)
|
|
{
|
|
int i;
|
|
for (i = 0; i < ARRAY_SIZE(hrm_attributes); i++)
|
|
if (device_create_file(dev, hrm_attributes + i))
|
|
goto err_sysfs_interface_hrm;
|
|
return 0;
|
|
err_sysfs_interface_hrm:
|
|
for (; i >= 0; i--)
|
|
device_remove_file(dev, hrm_attributes + i);
|
|
dev_err(dev, "%s: Unable to create interface\n", __func__);
|
|
return -1;
|
|
}
|
|
|
|
/* SAMPLE - SYSFS ATTRIBUTE*/
|
|
/**
|
|
This function is used for getting the status of the sample enable bit
|
|
@param dev linux device structure
|
|
@param attr pointer point linux device_attribute structure
|
|
@param buf pointer point the buffer
|
|
@return ssize_t size of data presnt in the buffer
|
|
*/
|
|
static ssize_t
|
|
hrmled_attr_get_enable(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct adpd143_data *pst_adpd = dev_get_drvdata(dev);
|
|
int val = atomic_read(&pst_adpd->hrmled_enabled);
|
|
return sprintf(buf, "%d\n", val);
|
|
}
|
|
|
|
/**
|
|
This function is used for enabling the Sample mode
|
|
@param dev linux device structure
|
|
@param attr pointer point linux device_attribute structure
|
|
@param buf pointer point the buffer
|
|
@param size buffer size
|
|
@return ssize_t size of data written to the buffer
|
|
*/
|
|
static ssize_t
|
|
hrmled_attr_set_enable(struct device *dev, struct device_attribute *attr,
|
|
const char *buf, size_t size)
|
|
{
|
|
struct adpd143_data *pst_adpd = dev_get_drvdata(dev);
|
|
unsigned short parse_data[2];
|
|
unsigned short mode = 0;
|
|
int val;
|
|
|
|
int n = sscanf(buf, "%d", &val);
|
|
(void)n;
|
|
memset(parse_data, 0, sizeof(parse_data));
|
|
|
|
pr_err("%s: pst_adpd->pre_hrmled_enable:%d, val:%d\n",
|
|
__func__, pst_adpd->pre_hrmled_enable, val);
|
|
if (pst_adpd->pre_hrmled_enable != val) {
|
|
switch(val) {
|
|
case IRRED_OFF:
|
|
pr_info("%s: IRRED_OFF.\n", __func__);
|
|
cmd_parsing("0x0", 1, parse_data);
|
|
atomic_set(&pst_adpd->hrmled_enabled, IRRED_OFF);
|
|
break;
|
|
case IR_MODE:
|
|
pr_info("%s: IR_MODE.\n", __func__);
|
|
cmd_parsing("0x32", 1, parse_data);
|
|
atomic_set(&pst_adpd->hrmled_enabled, IR_MODE);
|
|
break;
|
|
case RED_MODE:
|
|
pr_info("%s: RED_MODE.\n", __func__);
|
|
cmd_parsing("0x30", 1, parse_data);
|
|
atomic_set(&pst_adpd->hrmled_enabled, RED_MODE);
|
|
break;
|
|
case IRRED_MODE:
|
|
pr_info("%s: IRRED_MODE.\n", __func__);
|
|
cmd_parsing("0x31", 1, parse_data);
|
|
atomic_set(&pst_adpd->hrmled_enabled, IRRED_MODE);
|
|
break;
|
|
default:
|
|
pr_err("%s: invalid value\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
mode = GET_USR_MODE(parse_data[0]);
|
|
|
|
if (GET_USR_MODE(parse_data[0]) < MAX_MODE) {
|
|
if ((GET_USR_SUB_MODE(parse_data[0])) <
|
|
__mode_recv_frm_usr[mode].size) {
|
|
adpd143_mode_switching(pst_adpd, parse_data[0]);
|
|
} else {
|
|
ADPD143_dbg("Sub mode Out of bound\n");
|
|
adpd143_mode_switching(pst_adpd, 0);
|
|
}
|
|
} else {
|
|
ADPD143_dbg("Mode out of bound\n");
|
|
adpd143_mode_switching(pst_adpd, 0);
|
|
}
|
|
|
|
pst_adpd->pre_hrmled_enable = val;
|
|
return size;
|
|
}
|
|
/**
|
|
array of hrmled attributes
|
|
*/
|
|
static struct device_attribute hrmled_attributes[] = {
|
|
__ATTR(enable, 0664, hrmled_attr_get_enable, hrmled_attr_set_enable),
|
|
};
|
|
|
|
/**
|
|
This function is used for creating sysfs attribute for hrmled
|
|
@param dev linux device structure
|
|
@return int status of attribute creation
|
|
*/
|
|
static int
|
|
create_sysfs_interfaces_hrmled(struct device *dev)
|
|
{
|
|
int i;
|
|
for (i = 0; i < ARRAY_SIZE(hrmled_attributes); i++)
|
|
if (device_create_file(dev, hrmled_attributes + i))
|
|
goto err_sysfs_interface_hrmled;
|
|
return 0;
|
|
err_sysfs_interface_hrmled:
|
|
for (; i >= 0; i--)
|
|
device_remove_file(dev, hrmled_attributes + i);
|
|
dev_err(dev, "%s: Unable to create interface\n", __func__);
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
This function is used for removing sysfs attribute for sample
|
|
@param dev linux device structure
|
|
@return void
|
|
*/
|
|
static void
|
|
remove_sysfs_interfaces_hrm(struct device *dev)
|
|
{
|
|
int i;
|
|
for (i = 0; i < ARRAY_SIZE(hrm_attributes); i++)
|
|
device_remove_file(dev, hrm_attributes + i);
|
|
}
|
|
|
|
/**
|
|
This function is used for removing sysfs attribute for sample
|
|
@param dev linux device structure
|
|
@return void
|
|
*/
|
|
static void
|
|
remove_sysfs_interfaces_hrmled(struct device *dev)
|
|
{
|
|
int i;
|
|
for (i = 0; i < ARRAY_SIZE(hrmled_attributes); i++)
|
|
device_remove_file(dev, hrmled_attributes + i);
|
|
}
|
|
|
|
/**
|
|
This function is used for creating sysfs attribute
|
|
@param dev linux device structure
|
|
@return int status of attribute creation
|
|
*/
|
|
static int
|
|
create_sysfs_interfaces(struct device *dev)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
This function is used for removing sysfs attribute
|
|
@param dev linux device structure
|
|
@return void
|
|
*/
|
|
static void
|
|
remove_sysfs_interfaces(struct device *dev)
|
|
{
|
|
|
|
}
|
|
|
|
/**
|
|
This function is used for registering input event for Sample
|
|
@param pst_adpd pointer point ADPD143 data structure
|
|
@return s32 status of this function
|
|
*/
|
|
static s32
|
|
adpd143_input_init_sample(struct adpd143_data *pst_adpd)
|
|
{
|
|
int err;
|
|
|
|
/* for hrm sensor */
|
|
pst_adpd->hrm_input_dev = input_allocate_device();
|
|
if (!pst_adpd->hrm_input_dev) {
|
|
err = -ENOMEM;
|
|
dev_err(&pst_adpd->client->dev,
|
|
"hrm input dev allocation fail\n");
|
|
goto err_hrm_allocate;
|
|
}
|
|
|
|
pst_adpd->hrm_input_dev->name = MODULE_NAME_HRM;
|
|
input_set_drvdata(pst_adpd->hrm_input_dev, pst_adpd);
|
|
|
|
__set_bit(EV_MSC, pst_adpd->hrm_input_dev->evbit);
|
|
__set_bit(EV_REL, pst_adpd->hrm_input_dev->evbit);
|
|
__set_bit(MSC_RAW, pst_adpd->hrm_input_dev->mscbit);
|
|
__set_bit(REL_X, pst_adpd->hrm_input_dev->relbit);
|
|
__set_bit(REL_Y, pst_adpd->hrm_input_dev->relbit);
|
|
__set_bit(REL_Z, pst_adpd->hrm_input_dev->relbit);
|
|
__set_bit(REL_MISC, pst_adpd->hrm_input_dev->relbit);
|
|
|
|
err = input_register_device(pst_adpd->hrm_input_dev);
|
|
if (err) {
|
|
dev_err(&pst_adpd->client->dev,
|
|
"unable to register input dev %s\n",
|
|
pst_adpd->hrm_input_dev->name);
|
|
goto err_hrm_register_failed;
|
|
}
|
|
|
|
err = sensors_create_symlink(pst_adpd->hrm_input_dev);
|
|
if (err < 0) {
|
|
pr_err("%s: create_symlink error\n", __func__);
|
|
goto err_hrm_register_failed;
|
|
}
|
|
|
|
/* for hrmled sensor */
|
|
pst_adpd->hrmled_input_dev = input_allocate_device();
|
|
if (!pst_adpd->hrmled_input_dev) {
|
|
err = -ENOMEM;
|
|
dev_err(&pst_adpd->client->dev,
|
|
"hrmled input dev allocation fail\n");
|
|
goto err_hrmled_allocate;
|
|
}
|
|
|
|
pst_adpd->hrmled_input_dev->name = MODULE_NAME_HRMLED;
|
|
input_set_drvdata(pst_adpd->hrmled_input_dev, pst_adpd);
|
|
|
|
__set_bit(EV_MSC, pst_adpd->hrmled_input_dev->evbit);
|
|
__set_bit(EV_REL, pst_adpd->hrmled_input_dev->evbit);
|
|
__set_bit(MSC_RAW, pst_adpd->hrmled_input_dev->mscbit);
|
|
__set_bit(REL_X, pst_adpd->hrmled_input_dev->relbit);
|
|
__set_bit(REL_Y, pst_adpd->hrmled_input_dev->relbit);
|
|
__set_bit(REL_Z, pst_adpd->hrmled_input_dev->relbit);
|
|
__set_bit(REL_MISC, pst_adpd->hrmled_input_dev->relbit);
|
|
|
|
err = input_register_device(pst_adpd->hrmled_input_dev);
|
|
if (err) {
|
|
dev_err(&pst_adpd->client->dev,
|
|
"unable to register input dev %s\n",
|
|
pst_adpd->hrmled_input_dev->name);
|
|
goto err_hrmled_register_failed;
|
|
}
|
|
|
|
err = sensors_create_symlink(pst_adpd->hrmled_input_dev);
|
|
if (err < 0) {
|
|
pr_err("%s: create_symlink error\n", __func__);
|
|
goto err_hrmled_register_failed;
|
|
}
|
|
|
|
return 0;
|
|
err_hrmled_register_failed:
|
|
input_free_device(pst_adpd->hrmled_input_dev);
|
|
err_hrmled_allocate:
|
|
err_hrm_register_failed:
|
|
input_free_device(pst_adpd->hrm_input_dev);
|
|
err_hrm_allocate:
|
|
return err;
|
|
}
|
|
|
|
/**
|
|
This function is used for unregistering input event for Sample
|
|
@param pst_adpd pointer point ADPD143 data structure
|
|
@return void
|
|
*/
|
|
static void
|
|
adpd143_input_cleanup_sample(struct adpd143_data *pst_adpd)
|
|
{
|
|
input_set_drvdata(pst_adpd->hrm_input_dev, NULL);
|
|
input_unregister_device(pst_adpd->hrm_input_dev);
|
|
input_free_device(pst_adpd->hrm_input_dev);
|
|
|
|
input_set_drvdata(pst_adpd->hrmled_input_dev, NULL);
|
|
input_unregister_device(pst_adpd->hrmled_input_dev);
|
|
input_free_device(pst_adpd->hrmled_input_dev);
|
|
}
|
|
|
|
/**
|
|
This function is used for registering input event for ADPD143
|
|
@param pst_adpd pointer point ADPD143 data structure
|
|
@return s32 status of the function
|
|
*/
|
|
static s32
|
|
adpd143_input_init(struct adpd143_data *pst_adpd)
|
|
{
|
|
return adpd143_input_init_sample(pst_adpd);
|
|
}
|
|
|
|
/**
|
|
This function is used for unregistering input event done for ADPD143
|
|
@param pst_adpd pointer point ADPD143 data structure
|
|
@return void
|
|
*/
|
|
static void
|
|
adpd143_input_cleanup(struct adpd143_data *pst_adpd)
|
|
{
|
|
adpd143_input_cleanup_sample(pst_adpd);
|
|
}
|
|
|
|
/**
|
|
This function is used for registering sysfs attribute for ADPD143
|
|
@param pst_adpd pointer point ADPD143 data structure
|
|
@return s32 status of the called function
|
|
*/
|
|
static s32
|
|
adpd143_sysfs_init(struct adpd143_data *pst_adpd)
|
|
{
|
|
if (create_sysfs_interfaces(&pst_adpd->client->dev))
|
|
goto err_sysfs_create_gen;
|
|
|
|
if (create_sysfs_interfaces_hrm(&pst_adpd->hrm_input_dev->dev))
|
|
goto err_sysfs_create_sample;
|
|
|
|
if (create_sysfs_interfaces_hrmled(&pst_adpd->hrmled_input_dev->dev))
|
|
goto err_sysfs_create_sample;
|
|
|
|
return 0;
|
|
err_sysfs_create_sample:
|
|
|
|
remove_sysfs_interfaces(&pst_adpd->client->dev);
|
|
err_sysfs_create_gen:
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
This function is used for unregistering sysfs attribute for ADPD143
|
|
@param pst_adpd pointer point ADPD143 data structure
|
|
@return void
|
|
*/
|
|
static void
|
|
adpd143_sysfs_cleanup(struct adpd143_data *pst_adpd)
|
|
{
|
|
remove_sysfs_interfaces(&pst_adpd->client->dev);
|
|
remove_sysfs_interfaces_hrm(&pst_adpd->hrm_input_dev->dev);
|
|
remove_sysfs_interfaces_hrmled(&pst_adpd->hrmled_input_dev->dev);
|
|
}
|
|
|
|
/**
|
|
This function is used for assigning initial assignment value to
|
|
ADPD143 data structure
|
|
@param pst_adpd pointer point ADPD143 data structure
|
|
@return void
|
|
*/
|
|
static void
|
|
adpd143_struct_assign(struct adpd143_data *pst_adpd)
|
|
{
|
|
pst_adpd->ptr_data = data_buffer;
|
|
}
|
|
|
|
/**
|
|
This function is used for initializing ADPD143
|
|
@param pst_adpd pointer point ADPD143 data structure
|
|
@param id pointer point i2c device id
|
|
@return s32 status of the called function
|
|
*/
|
|
static s32
|
|
adpd143_initialization(struct adpd143_data *pst_adpd,
|
|
const struct i2c_device_id *id)
|
|
{
|
|
int err = 0;
|
|
|
|
if (adpd143_input_init(pst_adpd)) {
|
|
err = -1;
|
|
pr_err("%s: err line %d\n", __func__, __LINE__);
|
|
goto err_input_init;
|
|
}
|
|
if (adpd143_sysfs_init(pst_adpd)) {
|
|
pr_err("%s: err line %d\n", __func__, __LINE__);
|
|
err = -1;
|
|
goto err_sysfs_init;
|
|
}
|
|
|
|
adpd143_struct_assign(pst_adpd);
|
|
|
|
memset(&pst_adpd->stats, 0, sizeof(pst_adpd->stats));
|
|
adpd_stat_reset(pst_adpd);
|
|
|
|
INIT_WORK(&pst_adpd->work, adpd143_wq_handler);
|
|
pst_adpd->ptr_adpd143_wq_st =
|
|
create_workqueue("adpd143_wq");
|
|
if (!pst_adpd->ptr_adpd143_wq_st) {
|
|
err = -ENOMEM;
|
|
pr_err("%s %d\n", __func__, __LINE__);
|
|
goto err_wq_creation_init;
|
|
}
|
|
|
|
if (!pst_adpd->client->irq) {
|
|
pr_err("%s %d\n", __func__, __LINE__);
|
|
goto err_work_queue_init;
|
|
}
|
|
pst_adpd->hrm_int = pst_adpd->client->irq;
|
|
pst_adpd->irq = pst_adpd->hrm_int;
|
|
|
|
irq_set_irq_type(pst_adpd->irq, IRQ_TYPE_EDGE_FALLING);
|
|
err = request_irq(pst_adpd->irq, adpd143_isr_handler,
|
|
IRQF_TRIGGER_FALLING, dev_name(&pst_adpd->client->dev),
|
|
pst_adpd);
|
|
if (err) {
|
|
ADPD143_dbg("irq %d busy?\n", pst_adpd->irq);
|
|
goto err_work_queue_init;
|
|
}
|
|
disable_irq_nosync(pst_adpd->irq);
|
|
|
|
pst_adpd->ptr_config = kzalloc(sizeof(struct adpd_platform_data),
|
|
GFP_KERNEL);
|
|
if (pst_adpd->ptr_config == NULL) {
|
|
err = -ENOMEM;
|
|
pr_err("%s %d\n", __func__, __LINE__);
|
|
goto err_work_queue_init;
|
|
}
|
|
|
|
enable_irq(pst_adpd->irq);
|
|
adpd143_configuration(pst_adpd, 1);
|
|
adpd143_mode_switching(pst_adpd, 0); /* turn off : idle mode, temp*/
|
|
pst_adpd->osc_trim_open_enable = 1;
|
|
pst_adpd->pre_hrmled_enable = 0;
|
|
pst_adpd->sample_cnt = 0;
|
|
|
|
return 0;
|
|
//err_gpio_direction_input:
|
|
//gpio_free(pst_adpd->hrm_int);
|
|
err_work_queue_init:
|
|
destroy_workqueue(pst_adpd->ptr_adpd143_wq_st);
|
|
err_wq_creation_init:
|
|
|
|
err_sysfs_init:
|
|
adpd143_input_cleanup(pst_adpd);
|
|
err_input_init:
|
|
return err;
|
|
}
|
|
|
|
/**
|
|
This function is used for cleanup ADPD143
|
|
@param pst_adpd pointer point ADPD143 data structure
|
|
@return void
|
|
*/
|
|
static void
|
|
adpd143_initialization_cleanup(struct adpd143_data *pst_adpd)
|
|
{
|
|
adpd143_mode_switching(pst_adpd, 0);
|
|
|
|
free_irq(pst_adpd->irq, pst_adpd);
|
|
|
|
destroy_workqueue(pst_adpd->ptr_adpd143_wq_st);
|
|
adpd143_sysfs_cleanup(pst_adpd);
|
|
adpd143_input_cleanup(pst_adpd);
|
|
kobject_uevent(&pst_adpd->client->dev.kobj, KOBJ_OFFLINE);
|
|
}
|
|
|
|
void adpd143_pin_control(struct adpd143_data *data, bool pin_set)
|
|
{
|
|
int status = 0;
|
|
data->p->state = NULL;
|
|
if (pin_set) {
|
|
if (!IS_ERR(data->pins_idle)) {
|
|
status = pinctrl_select_state(data->p,
|
|
data->pins_idle);
|
|
if (status)
|
|
pr_err("%s: can't set pin default state\n",
|
|
__func__);
|
|
pr_debug("%s idle\n", __func__);
|
|
}
|
|
} else {
|
|
if (!IS_ERR(data->pins_sleep)) {
|
|
status = pinctrl_select_state(data->p,
|
|
data->pins_sleep);
|
|
if (status)
|
|
pr_err("%s: can't set pin sleep state\n",
|
|
__func__);
|
|
pr_debug("%s sleep\n", __func__);
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef CONFIG_PM
|
|
static s32
|
|
adpd143_i2c_suspend(struct device *dev)
|
|
{
|
|
struct adpd143_data *pst_adpd = dev_get_drvdata(dev);
|
|
unsigned short parse_data[2];
|
|
unsigned short mode = 0;
|
|
|
|
pr_info("%s \n", __func__);
|
|
adpd143_pin_control(pst_adpd, false);
|
|
|
|
memset(parse_data, 0, sizeof(parse_data));
|
|
if (atomic_read(&pst_adpd->hrmled_enabled) ||
|
|
atomic_read(&pst_adpd->hrm_enabled)) {
|
|
cmd_parsing("0x0", 1, parse_data);
|
|
mode = GET_USR_MODE(parse_data[0]);
|
|
|
|
if (GET_USR_MODE(parse_data[0]) < MAX_MODE) {
|
|
if ((GET_USR_SUB_MODE(parse_data[0])) <
|
|
__mode_recv_frm_usr[mode].size) {
|
|
adpd143_mode_switching(pst_adpd, parse_data[0]);
|
|
} else {
|
|
ADPD143_dbg("Sub mode Out of bound\n");
|
|
adpd143_mode_switching(pst_adpd, 0);
|
|
}
|
|
} else {
|
|
ADPD143_dbg("Mode out of bound\n");
|
|
adpd143_mode_switching(pst_adpd, 0);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static s32
|
|
adpd143_i2c_resume(struct device *dev)
|
|
{
|
|
struct adpd143_data *pst_adpd = dev_get_drvdata(dev);
|
|
unsigned short parse_data[2];
|
|
unsigned short mode = 0;
|
|
|
|
pr_info("%s \n", __func__);
|
|
adpd143_pin_control(pst_adpd, true);
|
|
|
|
memset(parse_data, 0, sizeof(parse_data));
|
|
if (atomic_read(&pst_adpd->hrm_enabled)) {
|
|
switch (atomic_read(&pst_adpd->hrm_enabled)) {
|
|
case HRM_SAMPLE_MODE:
|
|
cmd_parsing("0x31", 1, parse_data);
|
|
break;
|
|
case HRM_TIA_ADC_MODE:
|
|
cmd_parsing("0x51", 1, parse_data);
|
|
break;
|
|
default:
|
|
pr_err("%s: invalid value\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
goto mode_switching;
|
|
|
|
}
|
|
|
|
if (atomic_read(&pst_adpd->hrmled_enabled)) {
|
|
switch (atomic_read(&pst_adpd->hrmled_enabled)) {
|
|
case IR_MODE:
|
|
pr_info("%s: IR_MODE.\n", __func__);
|
|
cmd_parsing("0x32", 1, parse_data);
|
|
break;
|
|
case RED_MODE:
|
|
pr_info("%s: RED_MODE.\n", __func__);
|
|
cmd_parsing("0x30", 1, parse_data);
|
|
break;
|
|
case IRRED_MODE:
|
|
pr_info("%s: IRRED_MODE.\n", __func__);
|
|
cmd_parsing("0x31", 1, parse_data);
|
|
break;
|
|
default:
|
|
pr_err("%s: invalid value\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
goto mode_switching;
|
|
}
|
|
|
|
ADPD143_dbg("No active hrm sensor\n");
|
|
return 0;
|
|
mode_switching:
|
|
mode = GET_USR_MODE(parse_data[0]);
|
|
|
|
if (GET_USR_MODE(parse_data[0]) < MAX_MODE) {
|
|
if ((GET_USR_SUB_MODE(parse_data[0])) <
|
|
__mode_recv_frm_usr[mode].size) {
|
|
adpd143_mode_switching(pst_adpd, parse_data[0]);
|
|
} else {
|
|
ADPD143_dbg("Sub mode Out of bound\n");
|
|
adpd143_mode_switching(pst_adpd, 0);
|
|
}
|
|
} else {
|
|
ADPD143_dbg("Mode out of bound\n");
|
|
adpd143_mode_switching(pst_adpd, 0);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#else
|
|
#define adpd143_i2c_resume NULL
|
|
#define adpd143_i2c_suspend NULL
|
|
#endif /* CONFIG_PM */
|
|
|
|
|
|
/**
|
|
This function is used for i2c read communication between ADPD143 and AP
|
|
@param pst_adpd pointer point ADPD143 data structure
|
|
@param reg_addr address need to be fetch
|
|
@param len number byte to be read
|
|
@param buf pointer point the read out data.
|
|
@return s32 status of the called function
|
|
*/
|
|
static int
|
|
adpd143_i2c_read(struct adpd143_data *pst_adpd, u8 reg_addr, int len,
|
|
u16 *buf)
|
|
{
|
|
int err;
|
|
int tries = 0;
|
|
int icnt = 0;
|
|
|
|
struct i2c_msg msgs[] = {
|
|
{
|
|
.addr = pst_adpd->client->addr,
|
|
.flags = pst_adpd->client->flags & I2C_M_TEN,
|
|
.len = 1,
|
|
.buf = (s8 *)®_addr,
|
|
},
|
|
{
|
|
.addr = pst_adpd->client->addr,
|
|
.flags = (pst_adpd->client->flags & I2C_M_TEN) |
|
|
I2C_M_RD,
|
|
.len = len * sizeof(unsigned short),
|
|
.buf = (s8 *)buf,
|
|
},
|
|
};
|
|
|
|
do {
|
|
err = i2c_transfer(pst_adpd->client->adapter, msgs, 2);
|
|
if (err != 2)
|
|
msleep_interruptible(I2C_RETRY_DELAY);
|
|
} while ((err != 2) && (++tries < I2C_RETRIES));
|
|
|
|
if (err != 2) {
|
|
dev_err(&pst_adpd->client->dev, "read transfer error\n");
|
|
err = -EIO;
|
|
} else {
|
|
err = 0;
|
|
}
|
|
|
|
for (icnt = 0; icnt < len; icnt++) {
|
|
/*convert big endian to CPU format*/
|
|
buf[icnt] = be16_to_cpu(buf[icnt]);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
/**
|
|
This function is used for i2c write communication between ADPD143 and AP
|
|
@param pst_adpd pointer point ADPD143 data structure
|
|
@param reg_addr address need to be fetch
|
|
@param len number byte to be read
|
|
@param data value to be written on the register.
|
|
@return s32 status of the called function
|
|
*/
|
|
static int
|
|
adpd143_i2c_write(struct adpd143_data *pst_adpd, u8 reg_addr, int len,
|
|
u16 data)
|
|
{
|
|
struct i2c_msg msgs[1];
|
|
int err;
|
|
int tries = 0;
|
|
unsigned short data_to_write = cpu_to_be16(data);
|
|
char buf[4];
|
|
|
|
buf[0] = (s8) reg_addr;
|
|
memcpy(buf + 1, &data_to_write, sizeof(unsigned short));
|
|
msgs[0].addr = pst_adpd->client->addr;
|
|
msgs[0].flags = pst_adpd->client->flags & I2C_M_TEN;
|
|
msgs[0].len = 1 + (1 * sizeof(unsigned short));
|
|
msgs[0].buf = buf;
|
|
|
|
do {
|
|
err = i2c_transfer(pst_adpd->client->adapter, msgs, 1);
|
|
if (err != 1)
|
|
msleep_interruptible(I2C_RETRY_DELAY);
|
|
} while ((err != 1) && (++tries < I2C_RETRIES));
|
|
|
|
if (err != 1) {
|
|
dev_err(&pst_adpd->client->dev, "write transfer error\n");
|
|
err = -EIO;
|
|
} else {
|
|
err = 0;
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
/* set sysfs for hrm sensor */
|
|
static ssize_t adpd143_name_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
pr_info("%s: %s \n", __func__, CHIP_NAME);
|
|
|
|
return snprintf(buf, PAGE_SIZE, "%s\n", CHIP_NAME);
|
|
}
|
|
|
|
static ssize_t adpd143_vendor_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
pr_info("%s: %s \n", __func__, VENDOR);
|
|
|
|
return snprintf(buf, PAGE_SIZE, "%s\n", VENDOR);
|
|
}
|
|
|
|
static ssize_t eol_test_result_store(struct device *dev,
|
|
struct device_attribute *attr, const char *buf, size_t size);
|
|
|
|
static ssize_t eol_test_store(struct device *dev,
|
|
struct device_attribute *attr, const char *buf, size_t size)
|
|
{
|
|
struct adpd143_data *pst_adpd = dev_get_drvdata(dev);
|
|
|
|
if (sysfs_streq(buf, "1")) /* eol_test start */
|
|
pst_adpd->eol_test_is_enable = 1;
|
|
else if (sysfs_streq(buf, "0")) /* eol_test stop */
|
|
pst_adpd->eol_test_is_enable = 0;
|
|
else {
|
|
pr_debug("%s: invalid value %d\n", __func__, *buf);
|
|
return -EINVAL;
|
|
}
|
|
|
|
pst_adpd->eol_test_status = 0;
|
|
pr_info("%s: (%c), value(%u) \n",
|
|
__func__, *buf, pst_adpd->eol_test_is_enable);
|
|
|
|
return size;
|
|
}
|
|
|
|
static ssize_t eol_test_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct adpd143_data *pst_adpd = dev_get_drvdata(dev);
|
|
return snprintf(buf, PAGE_SIZE, "%u\n", pst_adpd->eol_test_is_enable);
|
|
}
|
|
|
|
static ssize_t eol_test_result_store(struct device *dev,
|
|
struct device_attribute *attr, const char *buf, size_t size)
|
|
{
|
|
struct adpd143_data *pst_adpd = dev_get_drvdata(dev);
|
|
int err;
|
|
unsigned int buf_len;
|
|
|
|
buf_len = strlen(buf) + 1;
|
|
if (buf_len > MAX_EOL_RESULT)
|
|
buf_len = MAX_EOL_RESULT;
|
|
|
|
if (pst_adpd->eol_test_result != NULL)
|
|
kfree(pst_adpd->eol_test_result);
|
|
|
|
pst_adpd->eol_test_result = kzalloc(sizeof(char) * buf_len, GFP_KERNEL);
|
|
if (pst_adpd->eol_test_result == NULL) {
|
|
pr_err("%s: couldn't allocate memory\n", __func__);
|
|
return -ENOMEM;
|
|
}
|
|
strncpy(pst_adpd->eol_test_result, buf, buf_len);
|
|
pr_info("%s: result = %s, buf_len(%u)\n",
|
|
__func__, pst_adpd->eol_test_result, buf_len);
|
|
pst_adpd->eol_test_status = 1;
|
|
|
|
err = osc_trim_efs_register_save(pst_adpd);
|
|
if (err < 0) {
|
|
pr_err("%s: osc_trim_efs_register_save() empty(%d)\n",
|
|
__func__, err);
|
|
}
|
|
|
|
return size;
|
|
}
|
|
|
|
static ssize_t eol_test_result_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct adpd143_data *pst_adpd = dev_get_drvdata(dev);
|
|
|
|
if (pst_adpd->eol_test_result == NULL) {
|
|
pr_info("%s: data->eol_test_result is NULL\n",
|
|
__func__);
|
|
pst_adpd->eol_test_status = 0;
|
|
return snprintf(buf, PAGE_SIZE, "%s\n", "NO_EOL_TEST");
|
|
}
|
|
pr_info("%s: result = %s\n",
|
|
__func__, pst_adpd->eol_test_result);
|
|
pst_adpd->eol_test_status = 0;
|
|
return snprintf(buf, PAGE_SIZE, "%s\n", pst_adpd->eol_test_result);
|
|
}
|
|
|
|
static ssize_t eol_test_status_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct adpd143_data *pst_adpd = dev_get_drvdata(dev);
|
|
|
|
return snprintf(buf, PAGE_SIZE, "%u\n", pst_adpd->eol_test_status);
|
|
}
|
|
|
|
static ssize_t adpd143_eol_lib_ver_store(struct device *dev,
|
|
struct device_attribute *attr, const char *buf, size_t size)
|
|
{
|
|
struct adpd143_data *pst_adpd = dev_get_drvdata(dev);
|
|
|
|
unsigned int buf_len;
|
|
buf_len = strlen(buf) + 1;
|
|
if (buf_len > MAX_LIB_VER)
|
|
buf_len = MAX_LIB_VER;
|
|
|
|
if (pst_adpd->eol_lib_ver != NULL)
|
|
kfree(pst_adpd->eol_lib_ver);
|
|
|
|
pst_adpd->eol_lib_ver = kzalloc(sizeof(char) * buf_len, GFP_KERNEL);
|
|
if (pst_adpd->eol_lib_ver == NULL) {
|
|
pr_err("%s: couldn't allocate memory\n", __func__);
|
|
return -ENOMEM;
|
|
}
|
|
strncpy(pst_adpd->eol_lib_ver, buf, buf_len);
|
|
pr_info("%s: eol_lib_ver = %s\n", __func__, pst_adpd->eol_lib_ver);
|
|
return size;
|
|
}
|
|
|
|
static ssize_t adpd143_eol_lib_ver_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct adpd143_data *pst_adpd = dev_get_drvdata(dev);
|
|
|
|
if (pst_adpd->eol_lib_ver == NULL) {
|
|
pr_info("%s: data->eol_lib_ver is NULL\n", __func__);
|
|
return snprintf(buf, PAGE_SIZE, "%s\n", "NULL");
|
|
}
|
|
pr_info("%s: eol_lib_ver = %s\n", __func__, pst_adpd->eol_lib_ver);
|
|
return snprintf(buf, PAGE_SIZE, "%s\n", pst_adpd->eol_lib_ver);
|
|
}
|
|
|
|
static ssize_t adpd143_elf_lib_ver_store(struct device *dev,
|
|
struct device_attribute *attr, const char *buf, size_t size)
|
|
{
|
|
struct adpd143_data *pst_adpd = dev_get_drvdata(dev);
|
|
|
|
unsigned int buf_len;
|
|
buf_len = strlen(buf) + 1;
|
|
if (buf_len > MAX_LIB_VER)
|
|
buf_len = MAX_LIB_VER;
|
|
|
|
if (pst_adpd->elf_lib_ver != NULL)
|
|
kfree(pst_adpd->elf_lib_ver);
|
|
|
|
pst_adpd->elf_lib_ver = kzalloc(sizeof(char) * buf_len, GFP_KERNEL);
|
|
if (pst_adpd->elf_lib_ver == NULL) {
|
|
pr_err("%s: couldn't allocate memory\n", __func__);
|
|
return -ENOMEM;
|
|
}
|
|
strncpy(pst_adpd->elf_lib_ver, buf, buf_len);
|
|
return size;
|
|
}
|
|
|
|
static ssize_t adpd143_elf_lib_ver_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct adpd143_data *pst_adpd = dev_get_drvdata(dev);
|
|
|
|
if (pst_adpd->elf_lib_ver == NULL) {
|
|
pr_info("%s: data->elf_lib_ver is NULL\n", __func__);
|
|
return snprintf(buf, PAGE_SIZE, "%s\n", "NULL");
|
|
}
|
|
pr_info("%s: elf_lib_ver = %s\n", __func__, pst_adpd->elf_lib_ver);
|
|
return snprintf(buf, PAGE_SIZE, "%s\n", pst_adpd->elf_lib_ver);
|
|
}
|
|
|
|
static ssize_t adpd143_hrm_flush_store(struct device *dev,
|
|
struct device_attribute *attr, const char *buf, size_t size)
|
|
{
|
|
struct adpd143_data *pst_adpd = dev_get_drvdata(dev);
|
|
int ret = 0;
|
|
u8 handle = 0;
|
|
|
|
ret = kstrtou8(buf, 10, &handle);
|
|
if (ret < 0) {
|
|
pr_err("%s: kstrtou8 failed.(%d)\n", __func__, ret);
|
|
return ret;
|
|
}
|
|
pr_info("%s: handle = %d\n", __func__, handle);
|
|
|
|
input_report_rel(pst_adpd->hrm_input_dev, REL_MISC, handle);
|
|
return size;
|
|
}
|
|
|
|
|
|
static ssize_t adpd143_alc_enable_store(struct device *dev,
|
|
struct device_attribute *attr, const char *buf, size_t size)
|
|
{
|
|
struct adpd143_data *pst_adpd = dev_get_drvdata(dev);
|
|
u8 val;
|
|
unsigned short mode = 0;
|
|
unsigned short parse_data[2];
|
|
int rc = 0;
|
|
|
|
rc = kstrtou8(buf, 10, &val);
|
|
pr_info("%s: enable Ambient Light Measurement val:%d.\n",
|
|
__func__, val);
|
|
|
|
if (val >= ALC_TIA_MODE && val <= ALC_FLOAT_MODE) {
|
|
switch(val) {
|
|
case ALC_TIA_MODE:
|
|
pr_info("%s: mode:%d\n", __func__, ALC_TIA_MODE);
|
|
cmd_parsing("0x50", 1, parse_data);
|
|
break;
|
|
case ALC_TNF_MODE:
|
|
pr_info("%s: mode:%d\n", __func__, ALC_TNF_MODE);
|
|
cmd_parsing("0x51", 1, parse_data);
|
|
break;
|
|
case ALC_FLOAT_MODE:
|
|
pr_info("%s: mode:%d\n", __func__, ALC_FLOAT_MODE);
|
|
cmd_parsing("0x52", 1, parse_data);
|
|
break;
|
|
default:
|
|
pr_info("%s: invalid mode\n", __func__);
|
|
cmd_parsing("0x0", 1, parse_data);
|
|
break;
|
|
}
|
|
atomic_set(&pst_adpd->hrm_enabled, 0);
|
|
} else {
|
|
pr_info("%s: disable.\n", __func__);
|
|
cmd_parsing("0x0", 1, parse_data);
|
|
atomic_set(&pst_adpd->hrm_enabled, 0);
|
|
}
|
|
|
|
mode = GET_USR_MODE(parse_data[0]);
|
|
|
|
if (GET_USR_MODE(parse_data[0]) < MAX_MODE) {
|
|
if ((GET_USR_SUB_MODE(parse_data[0])) <
|
|
__mode_recv_frm_usr[mode].size) {
|
|
adpd143_mode_switching(pst_adpd, parse_data[0]);
|
|
} else {
|
|
ADPD143_dbg("Sub mode Out of bound\n");
|
|
adpd143_mode_switching(pst_adpd, 0);
|
|
}
|
|
} else {
|
|
ADPD143_dbg("Mode out of bound\n");
|
|
adpd143_mode_switching(pst_adpd, 0);
|
|
}
|
|
return size;
|
|
}
|
|
|
|
static ssize_t adpd143_alc_enable_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct adpd143_data *pst_adpd = dev_get_drvdata(dev);
|
|
|
|
if (pst_adpd->elf_lib_ver == NULL) {
|
|
pr_info("%s: data->elf_lib_ver is NULL\n", __func__);
|
|
return snprintf(buf, PAGE_SIZE, "%s\n", "NULL");
|
|
}
|
|
pr_info("%s: elf_lib_ver = %s\n", __func__, pst_adpd->elf_lib_ver);
|
|
return snprintf(buf, PAGE_SIZE, "%s\n", pst_adpd->elf_lib_ver);
|
|
}
|
|
|
|
static DEVICE_ATTR(name, S_IRUGO, adpd143_name_show, NULL);
|
|
static DEVICE_ATTR(vendor, S_IRUGO, adpd143_vendor_show, NULL);
|
|
static DEVICE_ATTR(eol_test, S_IRUGO | S_IWUSR | S_IWGRP,
|
|
eol_test_show, eol_test_store);
|
|
static DEVICE_ATTR(eol_test_result, S_IRUGO | S_IWUSR | S_IWGRP,
|
|
eol_test_result_show, eol_test_result_store);
|
|
static DEVICE_ATTR(eol_test_status, S_IRUGO, eol_test_status_show, NULL);
|
|
static DEVICE_ATTR(eol_lib_ver, S_IRUGO | S_IWUSR | S_IWGRP,
|
|
adpd143_eol_lib_ver_show, adpd143_eol_lib_ver_store);
|
|
static DEVICE_ATTR(elf_lib_ver, S_IRUGO | S_IWUSR | S_IWGRP,
|
|
adpd143_elf_lib_ver_show, adpd143_elf_lib_ver_store);
|
|
static DEVICE_ATTR(adpd_mode, S_IRUGO | S_IWUSR | S_IWGRP,
|
|
attr_get_mode, attr_set_mode);
|
|
static DEVICE_ATTR(adpd_reg_read, S_IRUGO | S_IWUSR | S_IWGRP,
|
|
attr_reg_read_get, attr_reg_read_set);
|
|
static DEVICE_ATTR(adpd_reg_write, S_IRUGO | S_IWUSR | S_IWGRP,
|
|
NULL, attr_reg_write_set);
|
|
static DEVICE_ATTR(adpd_configuration, S_IRUGO | S_IWUSR | S_IWGRP,
|
|
attr_config_get, attr_config_set);
|
|
static DEVICE_ATTR(adpd_stat, S_IRUGO | S_IWUSR | S_IWGRP,
|
|
attr_stat_get, attr_stat_set);
|
|
static DEVICE_ATTR(hrm_flush, S_IWUSR | S_IWGRP,
|
|
NULL, adpd143_hrm_flush_store);
|
|
static DEVICE_ATTR(alc_enable, S_IRUGO | S_IWUSR | S_IWGRP,
|
|
adpd143_alc_enable_show, adpd143_alc_enable_store);
|
|
|
|
static struct device_attribute *hrm_sensor_attrs[] = {
|
|
&dev_attr_name,
|
|
&dev_attr_vendor,
|
|
&dev_attr_eol_test,
|
|
&dev_attr_eol_test_result,
|
|
&dev_attr_eol_test_status,
|
|
&dev_attr_eol_lib_ver,
|
|
&dev_attr_elf_lib_ver,
|
|
&dev_attr_adpd_mode,
|
|
&dev_attr_adpd_reg_read,
|
|
&dev_attr_adpd_reg_write,
|
|
&dev_attr_adpd_configuration,
|
|
&dev_attr_adpd_stat,
|
|
&dev_attr_hrm_flush,
|
|
&dev_attr_alc_enable,
|
|
NULL,
|
|
};
|
|
|
|
static ssize_t adpd143_hrmled_name_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
return snprintf(buf, PAGE_SIZE, "%s\n", CHIP_NAME);
|
|
}
|
|
|
|
static ssize_t adpd143_hrmled_vendor_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
return snprintf(buf, PAGE_SIZE, "%s\n", VENDOR);
|
|
}
|
|
|
|
static ssize_t adpd143_hrmled_flush_store(struct device *dev,
|
|
struct device_attribute *attr, const char *buf, size_t size)
|
|
{
|
|
struct adpd143_data *pst_adpd = dev_get_drvdata(dev);
|
|
int ret = 0;
|
|
u8 handle = 0;
|
|
|
|
ret = kstrtou8(buf, 10, &handle);
|
|
if (ret < 0) {
|
|
pr_err("%s: kstrtou8 failed.(%d)\n", __func__, ret);
|
|
return ret;
|
|
}
|
|
pr_info("%s: handle = %d\n", __func__, handle);
|
|
|
|
input_report_rel(pst_adpd->hrmled_input_dev, REL_MISC, handle);
|
|
return size;
|
|
}
|
|
|
|
static struct device_attribute dev_attr_hrmled_name =
|
|
__ATTR(name, S_IRUGO, adpd143_hrmled_name_show, NULL);
|
|
static struct device_attribute dev_attr_hrmled_vendor =
|
|
__ATTR(vendor, S_IRUGO, adpd143_hrmled_vendor_show, NULL);
|
|
static DEVICE_ATTR(hrmled_flush, S_IWUSR | S_IWGRP,
|
|
NULL, adpd143_hrmled_flush_store);
|
|
|
|
static struct device_attribute *hrmled_sensor_attrs[] = {
|
|
&dev_attr_hrmled_name,
|
|
&dev_attr_hrmled_vendor,
|
|
&dev_attr_hrmled_flush,
|
|
NULL,
|
|
};
|
|
|
|
/**
|
|
This function is used for ADPD143 probe function
|
|
@param client pointer point to the linux i2c client structure
|
|
@param id pointer point to the linux i2c device id
|
|
@return s32 status of the probe function
|
|
*/
|
|
static s32
|
|
adpd_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
|
{
|
|
struct adpd143_data *pst_adpd = NULL;
|
|
struct adpd_platform_data *pdata = NULL;
|
|
int err = 0, i = 0;
|
|
unsigned short u16_regval = 0;
|
|
pr_err("%s: is called Success\n", __func__);
|
|
|
|
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
|
|
dev_err(&client->dev, "client not i2c capable\n");
|
|
err = -ENODEV;
|
|
pr_err("[SENSOR] %s: exit_check_functionality.\n", __func__);
|
|
goto exit_check_functionality_failed;
|
|
}
|
|
|
|
pst_adpd = kzalloc(sizeof(struct adpd143_data), GFP_KERNEL);
|
|
if (pst_adpd == NULL) {
|
|
err = -ENOMEM;
|
|
pr_err("[SENSOR] %s: exit_mem_allocate.\n", __func__);
|
|
goto exit_mem_allocate_failed;
|
|
}
|
|
|
|
if (client->dev.of_node) {
|
|
pdata = devm_kzalloc (&client->dev ,
|
|
sizeof(struct adpd_platform_data), GFP_KERNEL);
|
|
if (!pdata) {
|
|
dev_err(&client->dev, "Failed to allocate memory\n");
|
|
if (pst_adpd)
|
|
kfree(pst_adpd);
|
|
return -ENOMEM;
|
|
}
|
|
} else
|
|
pdata = client->dev.platform_data;
|
|
if (!pdata) {
|
|
pr_err("%s: missing pdata!\n", __func__);
|
|
if (pst_adpd)
|
|
kfree(pst_adpd);
|
|
return err;
|
|
}
|
|
|
|
err = adpd_parse_dt(pst_adpd, &client->dev);
|
|
if (err) {
|
|
pr_err("%s: parse dt fail\n", __func__);
|
|
goto adpd_parse_dt_err;
|
|
}
|
|
|
|
pst_adpd->client = client;
|
|
pst_adpd->ptr_config = pdata;
|
|
pst_adpd->read = adpd143_i2c_read;
|
|
pst_adpd->write = adpd143_i2c_write;
|
|
/*Need to allocate and assign data then use the below function */
|
|
i2c_set_clientdata(client, (struct adpd143_data *)pst_adpd);
|
|
/*chip ID verification */
|
|
|
|
adpd_power_enable(pst_adpd, 1);
|
|
err = adpd_power_enable(pst_adpd, 1);
|
|
if (err < 0)
|
|
pr_err("%s: adpd_power_enable fail err = %d\n", __func__, err);
|
|
msleep(100);
|
|
|
|
mutex_init(&pst_adpd->mutex);
|
|
|
|
u16_regval = reg_read(pst_adpd, ADPD_CHIPID_ADDR);
|
|
|
|
switch (u16_regval) {
|
|
case ADPD_CHIPID(3):
|
|
case ADPD_CHIPID(4):
|
|
for(i=0; i<8; i++) rawDarkCh[i] = rawDataCh[i] = 0;
|
|
err = 0;
|
|
ADPD143_dbg("chipID value = 0x%x\n", u16_regval);
|
|
break;
|
|
default:
|
|
err = 1;
|
|
break;
|
|
};
|
|
if (err) {
|
|
ADPD143_dbg("chipID value = 0x%x\n", u16_regval);
|
|
pr_err("[SENSOR] %s: exit_chipid_verification.\n", __func__);
|
|
goto exit_chipid_verification;
|
|
}
|
|
ADPD143_info("chipID value = 0x%x\n", u16_regval);
|
|
|
|
//pst_adpd->dev = &client->dev;
|
|
pr_info("%s: start \n", __func__);
|
|
|
|
if (adpd143_initialization(pst_adpd, id)) {
|
|
pr_err("[SENSOR] %s: exit_adpd143_init_exit.\n", __func__);
|
|
goto exit_adpd143_init;
|
|
}
|
|
|
|
/* set sysfs for hrm sensor */
|
|
err = sensors_register(&pst_adpd->dev, pst_adpd, hrm_sensor_attrs,
|
|
"hrm_sensor");
|
|
if (err) {
|
|
pr_err("[SENSOR] %s: cound not register hrm_sensor(%d).\n",
|
|
__func__, err);
|
|
goto hrm_sensor_register_failed;
|
|
}
|
|
|
|
/* set sysfs for hrm sensor */
|
|
err = sensors_register(&pst_adpd->dev, pst_adpd, hrmled_sensor_attrs,
|
|
"hrmled_sensor");
|
|
if (err) {
|
|
pr_err("[SENSOR] %s: cound not register hrmled_sensor(%d).\n",
|
|
__func__, err);
|
|
goto hrmled_sensor_register_failed;
|
|
}
|
|
|
|
pr_info("%s: success\n", __func__);
|
|
|
|
goto done;
|
|
hrmled_sensor_register_failed:
|
|
sensors_unregister(pst_adpd->dev, hrm_sensor_attrs);
|
|
hrm_sensor_register_failed:
|
|
exit_adpd143_init:
|
|
exit_chipid_verification:
|
|
mutex_destroy(&pst_adpd->mutex);
|
|
adpd_power_enable(pst_adpd, 0);
|
|
adpd_parse_dt_err:
|
|
exit_mem_allocate_failed:
|
|
kfree(pst_adpd);
|
|
exit_check_functionality_failed:
|
|
dev_err(&client->dev, "%s: Driver Init failed\n", ADPD_DEV_NAME);
|
|
pr_err("%s: failed\n", __func__);
|
|
|
|
done:
|
|
return err;
|
|
}
|
|
|
|
/**
|
|
This function is used for ADPD143 remove function
|
|
@param client pointer point to the linux i2c client structure
|
|
@return s32 status of the remove function
|
|
*/
|
|
static s32
|
|
adpd_i2c_remove(struct i2c_client *client)
|
|
{
|
|
struct adpd143_data *pst_adpd = i2c_get_clientdata(client);
|
|
ADPD143_dbg("%s\n", __func__);
|
|
adpd143_initialization_cleanup(pst_adpd);
|
|
gpio_free(pst_adpd->hrm_int);
|
|
kfree(pst_adpd->ptr_config);
|
|
kfree(pst_adpd);
|
|
pst_adpd = NULL;
|
|
|
|
i2c_set_clientdata(client, NULL);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_PM
|
|
/**
|
|
device power management operation structure
|
|
*/
|
|
static const struct dev_pm_ops adpd_pm_ops = {
|
|
.resume = adpd143_i2c_resume,
|
|
.suspend = adpd143_i2c_suspend,
|
|
};
|
|
#endif
|
|
|
|
/**
|
|
This table tell which framework it supported
|
|
@brief the name has to get matched to the board configuration file setup
|
|
*/
|
|
/*static struct i2c_device_id adpd_id[] = { {ADPD_DEV_NAME, 0}, {} };
|
|
|
|
MODULE_DEVICE_TABLE(i2c, adpd_id);*/
|
|
|
|
static struct of_device_id adpd_match_table[] = {
|
|
{ .compatible = "adpd143",},
|
|
{},
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(i2c, adpd_match_table);
|
|
|
|
static const struct i2c_device_id adpd143_device_id[] = {
|
|
{ "adpd143", 0 },
|
|
{ }
|
|
};
|
|
|
|
/**
|
|
i2c operation structure
|
|
*/
|
|
struct i2c_driver adpd143_i2c_driver = {
|
|
.driver = {
|
|
.name = ADPD_DEV_NAME,
|
|
.owner = THIS_MODULE,
|
|
#ifdef CONFIG_PM
|
|
.pm = &adpd_pm_ops,
|
|
#endif
|
|
.of_match_table = adpd_match_table,
|
|
},
|
|
.probe = adpd_i2c_probe,
|
|
.remove = adpd_i2c_remove,
|
|
.id_table = adpd143_device_id,
|
|
};
|
|
|
|
static struct i2c_client *i2c_client;
|
|
|
|
#ifdef ADPD_AUTO_PROBE
|
|
static int
|
|
i2c_check_dev_attach(char *slave_name, unsigned short *slave_addrs,
|
|
unsigned short cnt)
|
|
{
|
|
struct i2c_board_info info;
|
|
struct i2c_adapter *i2c_adapter = NULL;
|
|
int ret = 0;
|
|
unsigned short *scan_device = NULL;
|
|
unsigned short count = 0;
|
|
|
|
/*need to check whether we need to free the memory */
|
|
scan_device = kzalloc(sizeof(unsigned short) * (cnt + 2), GFP_KERNEL);
|
|
if (IS_ERR(scan_device)) {
|
|
ret = -ENOMEM; /* out of memory */
|
|
goto i2c_check_attach_mem_fail;
|
|
}
|
|
|
|
memset(scan_device, '\0', sizeof(unsigned short) * (cnt + 2));
|
|
for (count = 0; count < cnt; count++) {
|
|
*(scan_device + count) = *(slave_addrs + count);
|
|
ADPD143_info("list of slave addr = 0x%x\n",
|
|
*(scan_device + count));
|
|
}
|
|
*(scan_device + count) = I2C_CLIENT_END;
|
|
|
|
count = 0;
|
|
|
|
do {
|
|
i2c_adapter = i2c_get_adapter(count);
|
|
if (i2c_adapter != NULL) {
|
|
memset(&info, 0, sizeof(struct i2c_board_info));
|
|
strlcpy(info.type, slave_name /*"adpd143" */ ,
|
|
I2C_NAME_SIZE);
|
|
/*need to check i2c_new_device instead of
|
|
i2c_new_probed_device*/
|
|
i2c_client =
|
|
i2c_new_probed_device(i2c_adapter, &info,
|
|
(const unsigned short *)
|
|
scan_device, NULL);
|
|
if (i2c_client != NULL) {
|
|
ADPD143_dbg("I2C busnum - %d\n", count);
|
|
ADPD143_dbg(
|
|
"device is attached to the bus i2c-%d\n",
|
|
count);
|
|
} else {
|
|
|
|
}
|
|
i2c_put_adapter(i2c_adapter);
|
|
} else {
|
|
ADPD143_info("Not valid adapter\n");
|
|
}
|
|
count++;
|
|
} while (i2c_client == NULL && count < 20);
|
|
|
|
kfree(scan_device);
|
|
|
|
if (i2c_client == NULL) {
|
|
/*No such device or address */
|
|
return -ENXIO;
|
|
} else {
|
|
return 0;
|
|
}
|
|
|
|
i2c_check_attach_mem_fail:
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
This function is get called when the module is inserted
|
|
@return inti status of the adpd143_multisensor_init
|
|
*/
|
|
static int __init
|
|
adpd143_multisensor_init(void)
|
|
{
|
|
#ifdef ADPD_AUTO_PROBE
|
|
ADPD143_dbg("%s\n", __func__);
|
|
unsigned short addr[] = { ADPD143_SLAVE_ADDR };
|
|
if (!i2c_check_dev_attach(ADPD_DEV_NAME, addr, 1)) {
|
|
return i2c_add_driver(&adpd143_i2c_driver);
|
|
} else {
|
|
pr_err("i2c bus connect error\n");
|
|
return -1;
|
|
}
|
|
#else
|
|
ADPD143_dbg("%s\n", __func__);
|
|
return i2c_add_driver(&adpd143_i2c_driver);
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
This function is get called when the module is removed
|
|
@return void
|
|
*/
|
|
static void __exit
|
|
adpd143_multisensor_exit(void)
|
|
{
|
|
ADPD143_dbg("%s\n", __func__);
|
|
i2c_del_driver(&adpd143_i2c_driver);
|
|
if (i2c_client)
|
|
i2c_unregister_device(i2c_client);
|
|
}
|
|
|
|
module_init(adpd143_multisensor_init);
|
|
module_exit(adpd143_multisensor_exit);
|
|
MODULE_DESCRIPTION();
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_AUTHOR("");
|