| /* |
| * You should have received a copy of the GNU General Public License |
| * along with this program. If not, see <http://www.gnu.org/licenses/>. |
| */ |
| |
| #include <linux/module.h> |
| #include <linux/slab.h> |
| #include <linux/i2c.h> |
| #include <linux/delay.h> |
| #include <linux/input.h> |
| #include <linux/gpio.h> |
| #include <linux/of_gpio.h> |
| #include <linux/fs.h> |
| #include <linux/uaccess.h> |
| #include <linux/wakelock.h> |
| #include <linux/interrupt.h> |
| #include <linux/regulator/consumer.h> |
| #include <linux/power_supply.h> |
| #include <linux/sensor/sensors_core.h> |
| #include "sx9330_wifi_reg.h" |
| #ifdef CONFIG_MUIC_NOTIFIER |
| #include <linux/muic/muic.h> |
| #include <linux/muic/muic_notifier.h> |
| #endif |
| #ifdef CONFIG_CCIC_NOTIFIER |
| #include <linux/ccic/ccic_notifier.h> |
| #endif |
| #if defined(CONFIG_USB_TYPEC_MANAGER_NOTIFIER) |
| #include <linux/usb/manager/usb_typec_manager_notifier.h> |
| #endif |
| |
| #ifdef TAG |
| #undef TAG |
| #define TAG "[GRIP_WIFI]" |
| #endif |
| #define VENDOR_NAME "SEMTECH" |
| #define MODEL_NAME "SX9330_WIFI" |
| #define MODULE_NAME "grip_sensor_wifi" |
| |
| #define I2C_M_WR 0 /* for i2c Write */ |
| #define I2c_M_RD 1 /* for i2c Read */ |
| |
| #define IDLE 0 |
| #define ACTIVE 1 |
| |
| #define SX9330_MODE_SLEEP 0 |
| #define SX9330_MODE_NORMAL 1 |
| |
| #define MAIN_SENSOR 1 |
| #define REF_SENSOR 2 |
| |
| #define DIFF_READ_NUM 10 |
| #define GRIP_LOG_TIME 15 /* 30 sec */ |
| |
| /* PH0, PH1, PH2, PH3 */ |
| #define TOTAL_BOTTON_COUNT 1 |
| #define CSX_STATUS_REG SX9330_STAT0_PROXSTAT_PH0_FLAG |
| |
| #define IRQ_PROCESS_CONDITION (MSK_IRQSTAT_TOUCH \ |
| | MSK_IRQSTAT_RELEASE \ |
| | MSK_IRQSTAT_COMP) |
| |
| #if defined(CONFIG_FOLDER_HALL) |
| #define HALLIC_PATH "/sys/class/sec/sec_flip/flipStatus" |
| #else |
| #if IS_ENABLED(CONFIG_HALL_NEW_NODE) |
| #define HALLIC_PATH "/sys/class/sec/hall_ic/hall_detect" |
| #else |
| #define HALLIC_PATH "/sys/class/sec/sec_key/hall_detect" |
| #endif |
| #endif |
| |
| struct sx9330_p { |
| struct i2c_client *client; |
| struct input_dev *input; |
| struct device *factory_device; |
| struct delayed_work init_work; |
| struct delayed_work irq_work; |
| struct delayed_work debug_work; |
| struct wake_lock grip_wake_lock; |
| struct mutex read_mutex; |
| #if defined(CONFIG_CCIC_NOTIFIER) |
| struct notifier_block cpuidle_ccic_nb; |
| #elif defined(CONFIG_MUIC_NOTIFIER) |
| struct notifier_block muic_nb; |
| #endif |
| |
| bool skip_data; |
| u8 touchTh; |
| u8 releaseTh; |
| int hallic_detect; |
| |
| int irq; |
| int gpio_nirq; |
| int state_ch1; |
| int init_done; |
| |
| atomic_t enable; |
| |
| u16 offset; |
| s32 capMain; |
| s32 useful; |
| s32 avg; |
| s32 diff; |
| |
| s32 diff_avg; |
| int diff_cnt; |
| s32 useful_avg; |
| |
| int irq_count; |
| int abnormal_mode; |
| s32 max_diff; |
| s32 max_normal_diff; |
| |
| int debug_count; |
| char hall_ic[6]; |
| |
| #ifdef CONFIG_SENSORS_SX9330_WIFI_2CH |
| int state_ch2; |
| |
| u16 offset_ch2; |
| s32 capMain_ch2; |
| s32 useful_ch2; |
| s32 avg_ch2; |
| s32 diff_ch2; |
| |
| s32 diff_avg_ch2; |
| int diff_cnt_ch2; |
| s32 useful_avg_ch2; |
| #endif |
| }; |
| |
| static int sx9330_check_hallic_state(char *file_path, char hall_ic_status[]) |
| { |
| int iRet = 0; |
| mm_segment_t old_fs; |
| struct file *filep; |
| char hall_sysfs[5]; |
| |
| old_fs = get_fs(); |
| set_fs(KERNEL_DS); |
| |
| filep = filp_open(file_path, O_RDONLY, 0440); |
| if (IS_ERR(filep)) { |
| iRet = PTR_ERR(filep); |
| pr_err("[SX9330_WIFI]: %s - file open fail %d\n", __func__, iRet); |
| set_fs(old_fs); |
| return iRet; |
| } |
| |
| iRet = filep->f_op->read(filep, hall_sysfs, |
| sizeof(hall_sysfs), &filep->f_pos); |
| |
| if (iRet <= 0) { |
| pr_err("[SX9330_WIFI]: %s - file read fail %d\n", __func__, iRet); |
| filp_close(filep, current->files); |
| set_fs(old_fs); |
| return -EIO; |
| } else { |
| strncpy(hall_ic_status, hall_sysfs, sizeof(hall_sysfs)); |
| } |
| |
| filp_close(filep, current->files); |
| set_fs(old_fs); |
| |
| return iRet; |
| } |
| |
| static int sx9330_get_nirq_state(struct sx9330_p *data) |
| { |
| return gpio_get_value_cansleep(data->gpio_nirq); |
| } |
| |
| static int sx9330_i2c_write_16bit(struct sx9330_p *data, u16 reg_addr, u32 buf) |
| { |
| int ret; |
| struct i2c_msg msg; |
| unsigned char w_buf[6]; |
| |
| w_buf[0] = (u8)(reg_addr>>8); |
| w_buf[1] = (u8)(reg_addr); |
| w_buf[2] = (u8)(buf>>24); |
| w_buf[3] = (u8)(buf>>16); |
| w_buf[4] = (u8)(buf>>8); |
| w_buf[5] = (u8)(buf); |
| |
| msg.addr = data->client->addr; |
| msg.flags = I2C_M_WR; |
| msg.len = 6; // 2bytes regaddr + 4bytes data |
| msg.buf = (u8 *)w_buf; |
| |
| ret = i2c_transfer(data->client->adapter, &msg, 1); |
| if (ret < 0) |
| pr_err("[SX9330_WIFI]: %s - i2c write error %d\n", __func__, ret); |
| |
| return ret; |
| } |
| |
| static int sx9330_i2c_read_16bit(struct sx9330_p *data, u16 reg_addr, u32 *data32) |
| { |
| int ret; |
| struct i2c_msg msg[2]; |
| u8 w_buf[2]; |
| u8 buf[4]; |
| |
| w_buf[0] = (u8)(reg_addr>>8); |
| w_buf[1] = (u8)(reg_addr); |
| |
| msg[0].addr = data->client->addr; |
| msg[0].flags = I2C_M_WR; |
| msg[0].len = 2; |
| msg[0].buf = (u8 *)w_buf; |
| |
| msg[1].addr = data->client->addr; |
| msg[1].flags = I2C_M_RD; |
| msg[1].len = 4; |
| msg[1].buf = (u8 *)buf; |
| |
| ret = i2c_transfer(data->client->adapter, msg, 2); |
| if (ret < 0) |
| pr_err("[SX9330_WIFI]: %s - i2c read error %d\n", __func__, ret); |
| |
| data32[0] = ((u32)buf[0]<<24) | ((u32)buf[1]<<16) | ((u32)buf[2]<<8) | ((u32)buf[3]); |
| |
| return ret; |
| } |
| |
| |
| static u8 sx9330_read_irqstate(struct sx9330_p *data) |
| { |
| u32 val; |
| |
| if (sx9330_i2c_read_16bit(data, SX9330_HOSTIRQSRC_REG, &val) >= 0) |
| return (val & 0x000000FF); |
| |
| return 0; |
| } |
| |
| static void sx9330_initialize_register(struct sx9330_p *data) |
| { |
| u32 val32 = 0; |
| int idx; |
| |
| for (idx = 0; idx < (int)(ARRAY_SIZE(setup_reg)); idx++) { |
| sx9330_i2c_write_16bit(data, setup_reg[idx].reg, setup_reg[idx].val); |
| pr_info("[SX9330_WIFI]: %s - Write Reg: 0x%x Value: 0x%x\n", |
| __func__, setup_reg[idx].reg, setup_reg[idx].val); |
| |
| sx9330_i2c_read_16bit(data, setup_reg[idx].reg, &val32); |
| pr_info("[SX9330_WIFI]: %s - Read Reg: 0x%x Value: 0x%x\n\n", |
| __func__, setup_reg[idx].reg, val32); |
| } |
| |
| data->init_done = ON; |
| } |
| |
| static void sx9330_initialize_chip(struct sx9330_p *data) |
| { |
| int cnt = 0; |
| |
| while((sx9330_get_nirq_state(data) == 0) && (cnt++ < 10)) { |
| sx9330_read_irqstate(data); |
| msleep(20); |
| } |
| |
| if (cnt >= 10) |
| pr_err("[SX9330_WIFI]: %s - s/w reset fail(%d)\n", __func__, cnt); |
| |
| sx9330_initialize_register(data); |
| } |
| |
| static int sx9330_set_offset_calibration(struct sx9330_p *data) |
| { |
| int ret = 0; |
| u32 retries = 0; |
| u32 status = 0; |
| |
| pr_info("[SX9330_WIFI]: %s\n", __func__); |
| |
| // Before Changing Mode ensure device is not processing a previous |
| // This timing is very short but good practice to check. |
| while (retries < 50) { |
| ret = sx9330_i2c_read_16bit(data, SX9330_TOPSTAT0_REG, &status); |
| if (ret < 0) |
| return ret; |
| |
| // If this is 0, we can issue the next command |
| if (!(status & MSK_TOPSTAT0_CMDBUSY)) { |
| pr_info("[SX9330_WIFI]: %s - TOPSTAT0 status: 0x%x, retries: %d\n", __func__, status, retries); |
| break; |
| } |
| |
| usleep_range(2*1000, 2*1000); |
| retries++; |
| } |
| |
| // send compensation |
| ret = sx9330_i2c_write_16bit(data, SX9330_CMD_REG, I2C_REGCMD_COMPEN); |
| if (ret < 0) |
| return ret; |
| |
| // After sending compensation wait until CONVSTAT=0 |
| retries = 0; |
| while (retries < 50) { |
| ret = sx9330_i2c_read_16bit(data, SX9330_STAT1_REG, &status); |
| if (ret < 0) |
| return ret; |
| |
| if (!(status & MSK_STAT1_CONVSTAT)) { |
| pr_info("[SX9330_WIFI]: %s - STAT1 status: 0x%x, retries: %d\n", __func__, status, retries); |
| break; |
| } |
| |
| usleep_range(2*1000, 2*1000); |
| retries++; |
| } |
| |
| pr_info("[SX9330_WIFI]: %s - done!\n", __func__); |
| |
| return ret; |
| } |
| |
| static void sx9330_send_event(struct sx9330_p *data, int ch, u8 state) |
| { |
| if (data->skip_data == true) { |
| pr_info("[SX9330_WIFI]: %s - skip grip event\n", __func__); |
| return; |
| } |
| |
| if (ch == MAIN_SENSOR) { |
| if (state == ACTIVE) { |
| data->state_ch1 = ACTIVE; |
| pr_info("[SX9330_WIFI]: %s - touched\n", __func__); |
| } else { |
| data->state_ch1 = IDLE; |
| pr_info("[SX9330_WIFI]: %s - released\n", __func__); |
| } |
| } |
| #ifdef CONFIG_SENSORS_SX9330_WIFI_2CH |
| else if (ch == REF_SENSOR) { |
| if (state == ACTIVE) { |
| data->state_ch2 = ACTIVE; |
| pr_info("[SX9330_WIFI]: %s - ch2 touched\n", __func__); |
| } else { |
| data->state_ch2 = IDLE; |
| pr_info("[SX9330_WIFI]: %s - ch2 released\n", __func__); |
| } |
| } |
| #endif |
| |
| switch (ch) { |
| case MAIN_SENSOR: |
| if (state == ACTIVE) |
| input_report_rel(data->input, REL_MISC, 1); |
| else |
| input_report_rel(data->input, REL_MISC, 2); |
| break; |
| #ifdef CONFIG_SENSORS_SX9330_WIFI_2CH |
| case REF_SENSOR: |
| if (state == ACTIVE) |
| input_report_rel(data->input, REL_DIAL, 1); |
| else |
| input_report_rel(data->input, REL_DIAL, 2); |
| break; |
| #endif |
| default: |
| break; |
| } |
| |
| input_sync(data->input); |
| } |
| |
| static void sx9330_display_data_reg(struct sx9330_p *data) |
| { |
| u32 val; |
| u16 reg; |
| int i; |
| |
| for (i = 0; i < TOTAL_BOTTON_COUNT; i++) { |
| pr_info("[SX9330_WIFI]: ############# %d button #############\n", i); |
| for (reg = SX9330_USEPH0_REG; reg <= SX9330_DIFFPH7_REG; ) |
| { |
| sx9330_i2c_read_16bit(data, reg, &val); |
| pr_info("[SX9330_WIFI]: %s - Register(0x%4x) data(0x%8x)\n", |
| __func__, reg, (val>>10)); |
| reg += 32; |
| } |
| } |
| } |
| |
| #ifdef CONFIG_SENSORS_SX9330_WIFI_2CH |
| static void sx9330_get_ch2_data(struct sx9330_p *data) |
| { |
| u8 msBit, msByte, lsByte; |
| u32 uData = 0; |
| const int table[16] = { |
| 0, 11000, 16500, 22000, 33000, 38500, |
| 44000, 49500, 55000, 60500, 66000, |
| 71500, 82500, 88000, 93500, 99000}; |
| u32 again_ch2 = 0; |
| |
| mutex_lock(&data->read_mutex); |
| /* Calculate out the Reference Cap information */ |
| sx9330_i2c_read_16bit(data, SX9330_USEPH0_REG + REF_SENSOR * 4, &uData); |
| data->useful_ch2 = (s32)uData >> 10; |
| if (data->useful_ch2 > 0xFFFFF) |
| data->useful_ch2 -= 0x1FFFFF; |
| |
| sx9330_i2c_read_16bit(data, SX9330_OFFSETPH0_REG + REF_SENSOR * 8, &uData); |
| data->offset_ch2 = uData & 0x7FFF; |
| |
| msBit = (u8)((data->offset_ch2 >> 14) & 0x1); |
| msByte = (u8)((data->offset_ch2 >> 7) & 0x7F); |
| lsByte = (u8)((data->offset_ch2) & 0xFF); |
| |
| /* read analog gain */ |
| sx9330_i2c_read_16bit(data, SX9330_AFEPARAMSPH0_REG + REF_SENSOR * 8, |
| &again_ch2); |
| again_ch2 = (again_ch2 & 0x1E00) >> 9; |
| |
| if(again_ch2 == 0) |
| again_ch2 = 1; |
| |
| /* Ctotal = Coffset + Cuseful */ |
| data->capMain_ch2 = (((s32)msBit * 1060800) + ((s32)msByte * 21200) + |
| ((s32)lsByte * 500)) + ((s32)data->useful_ch2 * table[again_ch2] / 1048575); |
| |
| /* avg read */ |
| sx9330_i2c_read_16bit(data, SX9330_AVGPH0_REG + REF_SENSOR * 4, &uData); |
| data->avg_ch2 = (s32)uData >> 10; |
| if (data->avg_ch2 > 0xFFFFF) |
| data->avg_ch2 -= 0x1FFFFF; |
| |
| /* diff read */ |
| sx9330_i2c_read_16bit(data, SX9330_DIFFPH0_REG + REF_SENSOR * 4, &uData); |
| data->diff_ch2 = (s32)uData >> 10; |
| if (data->diff_ch2 > 0xFFFFF) |
| data->diff_ch2 -= 0x1FFFFF; |
| |
| mutex_unlock(&data->read_mutex); |
| |
| pr_info("[SX9330_WIFI]: %s - [CH2] CapsMain: %ld, useful: %ld, avg: %ld, diff: %ld, Offset: %u\n", |
| __func__, (long int)data->capMain_ch2, (long int)data->useful_ch2, |
| (long int)data->avg_ch2, (long int)data->diff_ch2, data->offset_ch2); |
| } |
| #endif |
| |
| static void sx9330_get_data(struct sx9330_p *data) |
| { |
| u8 msBit, msByte, lsByte; |
| u32 uData = 0; |
| const int table[16] = { |
| 0, 11000, 16500, 22000, 33000, 38500, |
| 44000, 49500, 55000, 60500, 66000, |
| 71500, 82500, 88000, 93500, 99000}; |
| u32 again = 0; |
| s32 cOffset = 0; |
| s64 cUseful = 0; |
| |
| mutex_lock(&data->read_mutex); |
| |
| /* Calculate out the Main Cap information */ |
| sx9330_i2c_read_16bit(data, SX9330_USEPH0_REG + MAIN_SENSOR * 4, &uData); |
| data->useful = (s32)uData >> 10; |
| if (data->useful > 0xFFFFF) |
| data->useful -= 0x1FFFFF; |
| |
| sx9330_i2c_read_16bit(data, SX9330_OFFSETPH0_REG + MAIN_SENSOR * 8, &uData); |
| data->offset = uData & 0x7FFF; |
| |
| msBit = (u8)((data->offset >> 14) & 0x1); |
| msByte = (u8)((data->offset >> 7) & 0x7F); |
| lsByte = (u8)((data->offset) & 0x7F); |
| |
| /* read analog gain */ |
| sx9330_i2c_read_16bit(data, SX9330_AFEPARAMSPH0_REG + MAIN_SENSOR * 8, |
| &again); |
| again = (again & 0x1E00) >> 9; |
| |
| if(again == 0) |
| again = 1; |
| |
| cUseful = ((s64)data->useful * table[again]); |
| cUseful = (s32)(cUseful / 1048575); |
| cOffset = ((s32)msBit * 1060800) + ((s32)msByte * 22100) + |
| ((s32)lsByte * 500); |
| |
| /* Ctotal = Coffset + Cuseful */ |
| data->capMain = cOffset + cUseful; |
| |
| /* avg read */ |
| sx9330_i2c_read_16bit(data, SX9330_AVGPH0_REG + MAIN_SENSOR * 4, &uData); |
| data->avg = (s32)uData >> 10; |
| if (data->avg > 0xFFFFF) |
| data->avg -= 0x1FFFFF; |
| |
| /* diff read */ |
| sx9330_i2c_read_16bit(data, SX9330_DIFFPH0_REG + MAIN_SENSOR * 4, &uData); |
| data->diff = (s32)uData >> 10; |
| if (data->diff > 0xFFFFF) |
| data->diff -= 0x1FFFFF; |
| |
| mutex_unlock(&data->read_mutex); |
| |
| pr_info("[SX9330_WIFI]: %s - CapsMain: %ld, useful: %ld, avg: %ld, diff: %ld, Offset: %u\n", |
| __func__, (long int)data->capMain, (long int)data->useful, |
| (long int)data->avg, (long int)data->diff, data->offset); |
| } |
| |
| static int sx9330_set_mode(struct sx9330_p *data, unsigned char mode) |
| { |
| int ret = -EINVAL; |
| u32 retries = 0; |
| u32 status = 0; |
| |
| // Before Changing Mode ensure device is not processing a previous |
| // This timing is very short but good practice to check. |
| while (retries < 50) { |
| ret = sx9330_i2c_read_16bit(data, SX9330_TOPSTAT0_REG, &status); |
| if (ret < 0) { |
| pr_info("[SX9330_WIFI]: %s - change the mode : %u FAILED reading TOPSTAT0!!\n", __func__, mode); |
| return ret; |
| } |
| // If this is 0, we can issue the next command |
| if (!(status & MSK_TOPSTAT0_CMDBUSY)) |
| break; |
| |
| usleep_range(2*1000, 2*1000); |
| retries++; |
| } |
| |
| if (mode == SX9330_MODE_SLEEP) { |
| ret = sx9330_i2c_write_16bit(data, SX9330_CMD_REG, |
| I2C_REGCMD_EN_SLEEP); |
| } else if (mode == SX9330_MODE_NORMAL) { |
| ret = sx9330_i2c_write_16bit(data, SX9330_CMD_REG, |
| I2C_REGCMD_PHEN); |
| } |
| |
| // After sending compensation wait until CONVSTAT=0 |
| retries = 0; |
| while (retries < 50) { |
| ret = sx9330_i2c_read_16bit(data, SX9330_STAT1_REG, &status); |
| if (ret < 0) { |
| pr_info("[SX9330_WIFI]: %s - change the mode : %u FAILED reading STAT1!!\n", __func__, mode); |
| return ret; |
| } |
| |
| if (!(status & MSK_STAT1_CONVSTAT)) |
| break; |
| |
| usleep_range(2*1000, 2*1000); |
| retries++; |
| } |
| |
| // complete the original command that was sent after NORMAL mode change |
| if (mode == SX9330_MODE_NORMAL) |
| sx9330_set_offset_calibration(data); |
| |
| pr_info("[SX9330_WIFI]: %s - change the mode : %u\n", __func__, mode); |
| |
| return ret; |
| } |
| |
| static void sx9330_check_status(struct sx9330_p *data, int enable) |
| { |
| |
| u32 status = 0; |
| |
| sx9330_i2c_read_16bit(data, SX9330_STAT0_REG, &status); |
| status = status >> 24; |
| pr_info("[SX9330_WIFI]: %s - (status: 0x%x)\n", __func__, status); |
| |
| if (data->skip_data == true) { |
| input_report_rel(data->input, REL_MISC, 2); |
| input_sync(data->input); |
| return; |
| } |
| |
| if (status & (CSX_STATUS_REG << MAIN_SENSOR)) { |
| sx9330_send_event(data, MAIN_SENSOR, ACTIVE); |
| } else { |
| sx9330_send_event(data, MAIN_SENSOR, IDLE); |
| } |
| |
| #ifdef CONFIG_SENSORS_SX9330_WIFI_2CH |
| if (status & (CSX_STATUS_REG << REF_SENSOR)) { |
| sx9330_send_event(data, REF_SENSOR, ACTIVE); |
| } else { |
| sx9330_send_event(data, REF_SENSOR, IDLE); |
| } |
| #endif |
| } |
| |
| static void sx9330_set_enable(struct sx9330_p *data, int enable) |
| { |
| u8 status = 0; |
| u32 val32 = 0; |
| int idx; |
| |
| pr_info("[SX9330_WIFI]: %s\n", __func__); |
| |
| if (enable) { |
| |
| pr_info("[SX9330_WIFI]: %s - enable(status : 0x%x)\n", |
| __func__, status); |
| |
| data->diff_avg = 0; |
| data->diff_cnt = 0; |
| data->useful_avg = 0; |
| sx9330_get_data(data); |
| |
| #ifdef CONFIG_SENSORS_SX9330_WIFI_2CH |
| data->diff_avg_ch2 = 0; |
| data->diff_cnt_ch2 = 0; |
| data->useful_avg_ch2 = 0; |
| sx9330_get_ch2_data(data); |
| #endif |
| sx9330_check_status(data, enable); |
| |
| /* |
| * for debugging error case |
| */ |
| if (data->capMain == 0 || data->offset == 0) { |
| for (idx = 0; idx < (int)(ARRAY_SIZE(setup_reg)); idx++) { |
| sx9330_i2c_read_16bit(data, setup_reg[idx].reg, &val32); |
| pr_info("[SX9330_WIFI]: %s - Read Reg: 0x%x Value: 0x%x\n", |
| __func__, setup_reg[idx].reg, val32); |
| } |
| } |
| |
| msleep(20); |
| /* |
| * make sure no interrupts are pending since enabling irq |
| * will only work on next falling edge |
| */ |
| sx9330_read_irqstate(data); |
| /* |
| * enable interrupt |
| */ |
| sx9330_i2c_write_16bit(data, SX9330_HOSTIRQMSK_REG, 0x70); |
| |
| enable_irq(data->irq); |
| enable_irq_wake(data->irq); |
| } else { |
| |
| pr_info("[SX9330_WIFI]: %s - disable\n", __func__); |
| |
| /* |
| * disable interrupt |
| */ |
| sx9330_i2c_write_16bit(data, SX9330_HOSTIRQMSK_REG, 0x00); |
| |
| disable_irq(data->irq); |
| disable_irq_wake(data->irq); |
| } |
| } |
| |
| static void sx9330_set_debug_work(struct sx9330_p *data, u8 enable, |
| unsigned int time_ms) |
| { |
| if (enable == ON) { |
| data->debug_count = 0; |
| schedule_delayed_work(&data->debug_work, |
| msecs_to_jiffies(time_ms)); |
| } else { |
| cancel_delayed_work_sync(&data->debug_work); |
| } |
| } |
| |
| static ssize_t sx9330_get_offset_calibration_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| u32 val = 0; |
| struct sx9330_p *data = dev_get_drvdata(dev); |
| |
| sx9330_i2c_read_16bit(data, SX9330_HOSTIRQSRC_REG, &val); |
| |
| return snprintf(buf, PAGE_SIZE, "%d\n", val); |
| } |
| |
| static ssize_t sx9330_set_offset_calibration_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t count) |
| { |
| unsigned long val; |
| struct sx9330_p *data = dev_get_drvdata(dev); |
| |
| if (kstrtoul(buf, 10, &val)) { |
| pr_err("[SX9330_WIFI]: %s - Invalid Argument\n", __func__); |
| return -EINVAL; |
| } |
| |
| if (val) |
| sx9330_set_offset_calibration(data); |
| |
| return count; |
| } |
| |
| static ssize_t sx9330_register_write_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t count) |
| { |
| int regist = 0, val = 0; |
| struct sx9330_p *data = dev_get_drvdata(dev); |
| |
| if (sscanf(buf, "%6x,%10x", ®ist, &val) != 2) { |
| pr_err("[SX9330_WIFI]: %s - The number of data are wrong\n", |
| __func__); |
| return -EINVAL; |
| } |
| |
| sx9330_i2c_write_16bit(data, (u16)regist, (u32)val); |
| pr_info("[SX9330_WIFI]: %s - Register(0x%2x) data(0x%4x)\n", |
| __func__, regist, val); |
| |
| return count; |
| } |
| |
| static ssize_t sx9330_register_read_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| int offset = 0; |
| u32 val = 0; |
| int idx; |
| struct sx9330_p *data = dev_get_drvdata(dev); |
| |
| for (idx = 0; idx < (int)(ARRAY_SIZE(setup_reg)); idx++) { |
| sx9330_i2c_read_16bit(data, setup_reg[idx].reg, &val); |
| pr_info("[SX9330]: %s - Read Reg: 0x%x Value: 0x%x\n\n", |
| __func__, setup_reg[idx].reg, val); |
| |
| offset += snprintf(buf + offset, PAGE_SIZE - offset, |
| "Reg: 0x%x Value: 0x%08x\n", setup_reg[idx].reg, val); |
| } |
| |
| return offset; |
| } |
| |
| static ssize_t sx9330_read_data_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct sx9330_p *data = dev_get_drvdata(dev); |
| |
| sx9330_display_data_reg(data); |
| |
| return snprintf(buf, PAGE_SIZE, "%d\n", 0); |
| } |
| |
| static ssize_t sx9330_sw_reset_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct sx9330_p *data = dev_get_drvdata(dev); |
| |
| pr_info("[SX9330_WIFI]: %s\n", __func__); |
| sx9330_set_offset_calibration(data); |
| msleep(400); |
| sx9330_get_data(data); |
| #ifdef CONFIG_SENSORS_SX9330_WIFI_2CH |
| sx9330_get_ch2_data(data); |
| #endif |
| return snprintf(buf, PAGE_SIZE, "%d\n", 0); |
| } |
| |
| static ssize_t sx9330_vendor_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| return snprintf(buf, PAGE_SIZE, "%s\n", VENDOR_NAME); |
| } |
| |
| static ssize_t sx9330_name_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| return snprintf(buf, PAGE_SIZE, "%s\n", MODEL_NAME); |
| } |
| |
| static ssize_t sx9330_touch_mode_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| return snprintf(buf, PAGE_SIZE, "1\n"); |
| } |
| |
| static ssize_t sx9330_raw_data_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| static s32 sum_diff; |
| struct sx9330_p *data = dev_get_drvdata(dev); |
| |
| sx9330_get_data(data); |
| if (data->diff_cnt == 0) |
| sum_diff = data->diff; |
| else |
| sum_diff += data->diff; |
| |
| if (++data->diff_cnt >= DIFF_READ_NUM) { |
| data->diff_avg = sum_diff / DIFF_READ_NUM; |
| data->useful_avg = sum_diff / DIFF_READ_NUM; |
| data->diff_cnt = 0; |
| } |
| |
| return snprintf(buf, PAGE_SIZE, "%ld,%ld,%u,%ld,%ld\n", (long int)data->capMain, |
| (long int)data->useful, data->offset, (long int)data->diff, (long int)data->avg); |
| } |
| |
| static ssize_t sx9330_diff_avg_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct sx9330_p *data = dev_get_drvdata(dev); |
| |
| return snprintf(buf, PAGE_SIZE, "%ld\n", (long int)data->diff_avg); |
| } |
| |
| static ssize_t sx9330_useful_avg_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct sx9330_p *data = dev_get_drvdata(dev); |
| |
| return snprintf(buf, PAGE_SIZE, "%ld\n", (long int)data->useful_avg); |
| } |
| |
| static ssize_t sx9330_avgnegfilt_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct sx9330_p *data = dev_get_drvdata(dev); |
| u32 avgnegfilt = 0; |
| |
| sx9330_i2c_read_16bit(data, SX9330_AVGBFILTPH0_REG + |
| (1 << (4 + MAIN_SENSOR)), &avgnegfilt); |
| avgnegfilt = (avgnegfilt & 0x3800) >> 11; |
| |
| if (avgnegfilt == 7) |
| return snprintf(buf, PAGE_SIZE, "1\n"); |
| else if (avgnegfilt > 0 && avgnegfilt < 7) |
| return snprintf(buf, PAGE_SIZE, "1-1/%d\n", 1 << avgnegfilt); |
| else if (avgnegfilt == 0) |
| return snprintf(buf, PAGE_SIZE, "0\n"); |
| |
| return snprintf(buf, PAGE_SIZE, "not set\n"); |
| } |
| |
| static ssize_t sx9330_avgposfilt_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct sx9330_p *data = dev_get_drvdata(dev); |
| u32 avgposfilt = 0; |
| |
| sx9330_i2c_read_16bit(data, SX9330_AVGBFILTPH0_REG + |
| (1 << (4 + MAIN_SENSOR)), &avgposfilt); |
| avgposfilt = (avgposfilt & 0x700) >> 8; |
| |
| switch (avgposfilt) { |
| case 0x00: |
| return snprintf(buf, PAGE_SIZE, "0\n"); |
| case 0x01: |
| return snprintf(buf, PAGE_SIZE, "1-1/16\n"); |
| case 0x02: |
| return snprintf(buf, PAGE_SIZE, "1-1/64\n"); |
| case 0x03: |
| return snprintf(buf, PAGE_SIZE, "1-1/128\n"); |
| case 0x04: |
| return snprintf(buf, PAGE_SIZE, "1-1/256\n"); |
| case 0x05: |
| return snprintf(buf, PAGE_SIZE, "1-1/512\n"); |
| case 0x06: |
| return snprintf(buf, PAGE_SIZE, "1-1/1024\n"); |
| case 0x07: |
| return snprintf(buf, PAGE_SIZE, "1\n"); |
| default: |
| break; |
| } |
| |
| return snprintf(buf, PAGE_SIZE, "not set\n"); |
| } |
| |
| static ssize_t sx9330_gain_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| return snprintf(buf, PAGE_SIZE, "None\n"); |
| } |
| |
| static ssize_t sx9330_range_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| return snprintf(buf, PAGE_SIZE, "None\n"); |
| } |
| |
| static ssize_t sx9330_avgthresh_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct sx9330_p *data = dev_get_drvdata(dev); |
| u32 avgthresh = 0; |
| |
| sx9330_i2c_read_16bit(data, SX9330_AVGBFILTPH0_REG + |
| (1 << (4 + MAIN_SENSOR)), &avgthresh); |
| avgthresh = (avgthresh & 0x3F000000) >> 24; |
| |
| return snprintf(buf, PAGE_SIZE, "%ld\n", (long int)(16384 * avgthresh)); |
| } |
| |
| static ssize_t sx9330_rawfilt_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct sx9330_p *data = dev_get_drvdata(dev); |
| u32 rawfilt = 0; |
| |
| sx9330_i2c_read_16bit(data, SX9330_ADCFILTPH0_REG + |
| (1 << (4 + MAIN_SENSOR)), &rawfilt); |
| rawfilt = (rawfilt & 0x700000) >> 20; |
| |
| if (rawfilt > 0 && rawfilt < 8) |
| return snprintf(buf, PAGE_SIZE, "1-1/%d\n", 1 << rawfilt); |
| else if (rawfilt == 0) |
| return snprintf(buf, PAGE_SIZE, "0\n"); |
| |
| return snprintf(buf, PAGE_SIZE, "not set\n"); |
| } |
| |
| static ssize_t sx9330_sampling_freq_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct sx9330_p *data = dev_get_drvdata(dev); |
| u32 sampling_freq = 0; |
| const char *table[32] = { |
| "250", "200", "166.67", "142.86", "125", "111.11", "100", |
| "90.91", "83.33", "76.92", "71.43", "66.67", "62.50", "58.82", |
| "55.56", "52.63", "50", "45.45", "41.67", "38.46", "35.71", |
| "31.25", "27.78", "25", "20.83", "17.86", "13.89", "11.36", |
| "8.33", "6.58", "5.43", "4.63"}; |
| |
| sx9330_i2c_read_16bit(data, SX9330_AFEPARAMSPH0_REG + MAIN_SENSOR * 8, |
| &sampling_freq); |
| sampling_freq = (sampling_freq & 0xF8) >> 3; |
| |
| if (sampling_freq > 31) |
| return snprintf(buf, PAGE_SIZE, "not set\n"); |
| |
| return snprintf(buf, PAGE_SIZE, "%skHz\n", table[sampling_freq]); |
| } |
| |
| static ssize_t sx9330_scan_period_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct sx9330_p *data = dev_get_drvdata(dev); |
| s32 scan_period = 0; |
| |
| sx9330_i2c_read_16bit(data, SX9330_SCANPERIOD_REG, &scan_period); |
| scan_period = (s32)(scan_period & 0x7FF); |
| scan_period = (s32)((scan_period << 11) / 1000); |
| |
| return snprintf(buf, PAGE_SIZE, "%ld\n", (long int)scan_period); |
| } |
| |
| static ssize_t sx9330_again_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct sx9330_p *data = dev_get_drvdata(dev); |
| const char *table[16] = { |
| "Reserved", "+/-1.1", "+/-1.65", "+/-2.2", |
| "+/-3.3", "+/-3.85", "+/-4.4", "+/-4.95", |
| "+/-5.5", "+/-6.05", "+/-6.6F", "+/-7.15", |
| "+/-8.25", "+/-8.8", "+/-9.35", "+/-9.9"}; |
| u32 again = 0; |
| |
| sx9330_i2c_read_16bit(data, SX9330_AFEPARAMSPH0_REG + MAIN_SENSOR * 8, |
| &again); |
| again = (again & 0x1E00) >> 9; |
| |
| return snprintf(buf, PAGE_SIZE, "%spF\n", table[again]); |
| } |
| |
| static ssize_t sx9330_phase_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| return snprintf(buf, PAGE_SIZE, "%d\n", MAIN_SENSOR); |
| } |
| |
| static ssize_t sx9330_hysteresis_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct sx9330_p *data = dev_get_drvdata(dev); |
| const char *table[4] = {"None", "+/-6%", "+/-12%", "+/-25%"}; |
| u32 hyst = 0; |
| |
| sx9330_i2c_read_16bit(data, SX9330_ADCFILTPH0_REG + |
| (1 << (4 + MAIN_SENSOR)), &hyst); |
| hyst = (hyst & 0x30) >> 4; |
| |
| return snprintf(buf, PAGE_SIZE, "%s\n", table[hyst]); |
| } |
| |
| static ssize_t sx9330_irq_count_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct sx9330_p *data = dev_get_drvdata(dev); |
| |
| int result = 0; |
| s32 max_diff_val = 0; |
| |
| if (data->irq_count) { |
| result = -1; |
| max_diff_val = data->max_diff; |
| } else { |
| max_diff_val = data->max_normal_diff; |
| } |
| |
| pr_info("[SX9330_WIFI]: %s - called\n", __func__); |
| |
| return snprintf(buf, PAGE_SIZE, "%d,%d,%d\n", |
| result, data->irq_count, max_diff_val); |
| } |
| |
| static ssize_t sx9330_irq_count_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t count) |
| { |
| struct sx9330_p *data = dev_get_drvdata(dev); |
| |
| u8 onoff; |
| int ret; |
| |
| ret = kstrtou8(buf, 10, &onoff); |
| if (ret < 0) { |
| pr_err("[SX9330_WIFI]: %s - kstrtou8 failed.(%d)\n", __func__, ret); |
| return count; |
| } |
| |
| mutex_lock(&data->read_mutex); |
| |
| if (onoff == 0) { |
| data->abnormal_mode = OFF; |
| } else if (onoff == 1) { |
| data->abnormal_mode = ON; |
| data->irq_count = 0; |
| data->max_diff = 0; |
| data->max_normal_diff = 0; |
| } else { |
| pr_err("[SX9330_WIFI]: %s - unknown value %d\n", __func__, onoff); |
| } |
| |
| mutex_unlock(&data->read_mutex); |
| |
| pr_info("[SX9330_WIFI]: %s - %d\n", __func__, onoff); |
| |
| return count; |
| } |
| |
| static ssize_t sx9330_normal_threshold_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct sx9330_p *data = dev_get_drvdata(dev); |
| u32 threshold = 0; |
| u32 hyst = 0; |
| |
| sx9330_i2c_read_16bit(data, SX9330_ADCFILTPH0_REG + |
| (1 << (4 + MAIN_SENSOR)), &threshold); |
| |
| threshold = (threshold & 0xFF00) >> 8; |
| threshold = threshold * threshold / 2; |
| |
| sx9330_i2c_read_16bit(data, SX9330_ADCFILTPH0_REG + |
| (1 << (4 + MAIN_SENSOR)), &hyst); |
| hyst = (hyst & 0x30) >> 4; |
| |
| switch (hyst) { |
| case 0x01: /* 6% */ |
| hyst = threshold >> 4; |
| break; |
| case 0x02: /* 12% */ |
| hyst = threshold >> 3; |
| break; |
| case 0x03: /* 25% */ |
| hyst = threshold >> 2; |
| break; |
| default: |
| /* None */ |
| break; |
| } |
| |
| return snprintf(buf, PAGE_SIZE, "%d,%d\n", |
| threshold + hyst, threshold - hyst); |
| } |
| |
| static ssize_t sx9330_onoff_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct sx9330_p *data = dev_get_drvdata(dev); |
| |
| return snprintf(buf, PAGE_SIZE, "%u\n", !data->skip_data); |
| } |
| |
| static ssize_t sx9330_onoff_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t count) |
| { |
| u8 val; |
| int ret; |
| struct sx9330_p *data = dev_get_drvdata(dev); |
| |
| ret = kstrtou8(buf, 2, &val); |
| if (ret) { |
| pr_err("[SX9330_WIFI]: %s - Invalid Argument\n", __func__); |
| return ret; |
| } |
| |
| if (val == 0) { |
| data->skip_data = true; |
| if (atomic_read(&data->enable) == ON) { |
| data->state_ch1 = IDLE; |
| input_report_rel(data->input, REL_MISC, 2); |
| input_sync(data->input); |
| } |
| } else { |
| data->skip_data = false; |
| } |
| pr_info("[SX9330_WIFI]: %s -%u\n", __func__, val); |
| return count; |
| } |
| |
| static ssize_t sx9330_resolution_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct sx9330_p *data = dev_get_drvdata(dev); |
| u32 resolution = 0; |
| |
| sx9330_i2c_read_16bit(data, |
| SX9330_AFEPARAMSPH0_REG + MAIN_SENSOR*8, &resolution); |
| resolution = resolution & 0x7; |
| |
| return snprintf(buf, PAGE_SIZE, "%d\n", 1 << (resolution + 3)); |
| } |
| |
| static ssize_t sx9330_adc_filt_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct sx9330_p *data = dev_get_drvdata(dev); |
| u32 adc_filt = 0; |
| |
| sx9330_i2c_read_16bit(data, SX9330_ADCFILTPH0_REG + |
| (1 << (4 + MAIN_SENSOR)), &adc_filt); |
| adc_filt = (adc_filt & 0xC0000) >> 18; |
| |
| return snprintf(buf, PAGE_SIZE, "%d\n", 1 << adc_filt); |
| } |
| |
| static ssize_t sx9330_useful_filt_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct sx9330_p *data = dev_get_drvdata(dev); |
| u32 useful_filt = 0; |
| |
| sx9330_i2c_read_16bit(data, SX9330_AVGAFILTPH0_REG + |
| (1 << (4 + MAIN_SENSOR)), &useful_filt); |
| useful_filt = (useful_filt & 0x1000) >> 12; |
| |
| return snprintf(buf, PAGE_SIZE, "%s\n", useful_filt ? "on" : "off"); |
| } |
| |
| #ifdef CONFIG_SENSORS_SX9330_WIFI_2CH |
| static ssize_t sx9330_ch_count_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| return snprintf(buf, PAGE_SIZE, "2\n"); |
| } |
| |
| static ssize_t sx9330_gain_ch2_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| return snprintf(buf, PAGE_SIZE, "None\n"); |
| } |
| |
| static ssize_t sx9330_raw_data_ch2_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| static s32 sum_diff; |
| struct sx9330_p *data = dev_get_drvdata(dev); |
| sx9330_get_ch2_data(data); |
| |
| if (data->diff_cnt_ch2 == 0) |
| sum_diff = data->diff_ch2; |
| else |
| sum_diff += data->diff_ch2; |
| |
| if (++data->diff_cnt_ch2 >= DIFF_READ_NUM) { |
| data->diff_avg_ch2 = sum_diff / DIFF_READ_NUM; |
| data->useful_avg_ch2 = sum_diff / DIFF_READ_NUM; |
| data->diff_cnt_ch2 = 0; |
| } |
| |
| return snprintf(buf, PAGE_SIZE, "%ld,%ld,%u,%ld,%ld\n", (long int)data->capMain_ch2, |
| (long int)data->useful_ch2, data->offset_ch2, (long int)data->diff_ch2, (long int)data->avg_ch2); |
| } |
| |
| static ssize_t sx9330_normal_ch2_threshold_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct sx9330_p *data = dev_get_drvdata(dev); |
| u32 threshold = 0; |
| u32 hyst = 0; |
| |
| sx9330_i2c_read_16bit(data, SX9330_ADCFILTPH0_REG + |
| (1 << (4 + REF_SENSOR)), &threshold); |
| |
| threshold = (threshold & 0xFF00) >> 8; |
| threshold = threshold * threshold / 2; |
| |
| sx9330_i2c_read_16bit(data, SX9330_ADCFILTPH0_REG + |
| (1 << (4 + REF_SENSOR)), &hyst); |
| hyst = (hyst & 0x30) >> 4; |
| |
| switch (hyst) { |
| case 0x01: /* 6% */ |
| hyst = threshold >> 4; |
| break; |
| case 0x02: /* 12% */ |
| hyst = threshold >> 3; |
| break; |
| case 0x03: /* 25% */ |
| hyst = threshold >> 2; |
| break; |
| default: |
| /* None */ |
| break; |
| } |
| |
| return snprintf(buf, PAGE_SIZE, "%d,%d\n", |
| threshold + hyst, threshold - hyst); |
| } |
| |
| static ssize_t sx9330_diff_avg_ch2_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct sx9330_p *data = dev_get_drvdata(dev); |
| |
| return snprintf(buf, PAGE_SIZE, "%ld\n", (long int)data->diff_avg_ch2); |
| } |
| |
| static ssize_t sx9330_useful_avg_ch2_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct sx9330_p *data = dev_get_drvdata(dev); |
| |
| return snprintf(buf, PAGE_SIZE, "%ld\n", (long int)data->useful_avg_ch2); |
| } |
| #endif |
| |
| static DEVICE_ATTR(menual_calibrate, S_IRUGO | S_IWUSR | S_IWGRP, |
| sx9330_get_offset_calibration_show, |
| sx9330_set_offset_calibration_store); |
| static DEVICE_ATTR(register_write,0220, |
| NULL, sx9330_register_write_store); |
| static DEVICE_ATTR(register_read, 0444, |
| sx9330_register_read_show, NULL); |
| static DEVICE_ATTR(readback, S_IRUGO, sx9330_read_data_show, NULL); |
| static DEVICE_ATTR(reset, S_IRUGO, sx9330_sw_reset_show, NULL); |
| |
| static DEVICE_ATTR(name, S_IRUGO, sx9330_name_show, NULL); |
| static DEVICE_ATTR(vendor, S_IRUGO, sx9330_vendor_show, NULL); |
| static DEVICE_ATTR(mode, S_IRUGO, sx9330_touch_mode_show, NULL); |
| static DEVICE_ATTR(raw_data, S_IRUGO, sx9330_raw_data_show, NULL); |
| static DEVICE_ATTR(diff_avg, 0444, sx9330_diff_avg_show, NULL); |
| static DEVICE_ATTR(useful_avg, 0444, sx9330_useful_avg_show, NULL); |
| static DEVICE_ATTR(onoff, S_IRUGO | S_IWUSR | S_IWGRP, |
| sx9330_onoff_show, sx9330_onoff_store); |
| static DEVICE_ATTR(normal_threshold, 0444, |
| sx9330_normal_threshold_show, NULL); |
| static DEVICE_ATTR(avg_negfilt, 0444, sx9330_avgnegfilt_show, NULL); |
| static DEVICE_ATTR(avg_posfilt, 0444, sx9330_avgposfilt_show, NULL); |
| static DEVICE_ATTR(avg_thresh, 0444, sx9330_avgthresh_show, NULL); |
| static DEVICE_ATTR(rawfilt, 0444, sx9330_rawfilt_show, NULL); |
| static DEVICE_ATTR(sampling_freq, 0444, sx9330_sampling_freq_show, NULL); |
| static DEVICE_ATTR(scan_period, 0444, sx9330_scan_period_show, NULL); |
| static DEVICE_ATTR(gain, 0444, sx9330_gain_show, NULL); |
| static DEVICE_ATTR(range, 0444, sx9330_range_show, NULL); |
| static DEVICE_ATTR(analog_gain, 0444, sx9330_again_show, NULL); |
| static DEVICE_ATTR(phase, 0444, sx9330_phase_show, NULL); |
| static DEVICE_ATTR(hysteresis, 0444, sx9330_hysteresis_show, NULL); |
| static DEVICE_ATTR(irq_count, 0664, |
| sx9330_irq_count_show, sx9330_irq_count_store); |
| static DEVICE_ATTR(resolution, 0444, sx9330_resolution_show, NULL); |
| static DEVICE_ATTR(adc_filt, 0444, sx9330_adc_filt_show, NULL); |
| static DEVICE_ATTR(useful_filt, 0444, sx9330_useful_filt_show, NULL); |
| #ifdef CONFIG_SENSORS_SX9330_WIFI_2CH |
| static DEVICE_ATTR(ch_count, 0444, sx9330_ch_count_show, NULL); |
| static DEVICE_ATTR(gain_ch2, 0444, sx9330_gain_ch2_show, NULL); |
| static DEVICE_ATTR(raw_data_ch2, 0444, sx9330_raw_data_ch2_show, NULL); |
| static DEVICE_ATTR(normal_threshold_ch2, 0444, |
| sx9330_normal_ch2_threshold_show, NULL); |
| static DEVICE_ATTR(diff_avg_ch2, 0444, sx9330_diff_avg_ch2_show, NULL); |
| static DEVICE_ATTR(useful_avg_ch2, 0444, sx9330_useful_avg_ch2_show, NULL); |
| #endif |
| |
| static struct device_attribute *sensor_attrs[] = { |
| &dev_attr_menual_calibrate, |
| &dev_attr_register_write, |
| &dev_attr_register_read, |
| &dev_attr_readback, |
| &dev_attr_reset, |
| &dev_attr_name, |
| &dev_attr_vendor, |
| &dev_attr_mode, |
| &dev_attr_raw_data, |
| &dev_attr_diff_avg, |
| &dev_attr_useful_avg, |
| &dev_attr_onoff, |
| &dev_attr_normal_threshold, |
| &dev_attr_avg_negfilt, |
| &dev_attr_avg_posfilt, |
| &dev_attr_avg_thresh, |
| &dev_attr_rawfilt, |
| &dev_attr_sampling_freq, |
| &dev_attr_scan_period, |
| &dev_attr_gain, |
| &dev_attr_range, |
| &dev_attr_analog_gain, |
| &dev_attr_phase, |
| &dev_attr_hysteresis, |
| &dev_attr_irq_count, |
| &dev_attr_resolution, |
| &dev_attr_adc_filt, |
| &dev_attr_useful_filt, |
| #ifdef CONFIG_SENSORS_SX9330_WIFI_2CH |
| &dev_attr_ch_count, |
| &dev_attr_gain_ch2, |
| &dev_attr_raw_data_ch2, |
| &dev_attr_normal_threshold_ch2, |
| &dev_attr_diff_avg_ch2, |
| &dev_attr_useful_avg_ch2, |
| #endif |
| NULL, |
| }; |
| |
| /*****************************************************************************/ |
| |
| static ssize_t sx9330_enable_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t size) |
| { |
| u8 enable; |
| int ret; |
| struct sx9330_p *data = dev_get_drvdata(dev); |
| int pre_enable = atomic_read(&data->enable); |
| |
| ret = kstrtou8(buf, 2, &enable); |
| if (ret) { |
| pr_err("[SX9330_WIFI]: %s - Invalid Argument\n", __func__); |
| return ret; |
| } |
| |
| pr_info("[SX9330_WIFI]: %s - new_value = %u old_value = %d\n", |
| __func__, enable, pre_enable); |
| |
| if (pre_enable == enable) |
| return size; |
| |
| atomic_set(&data->enable, enable); |
| sx9330_set_enable(data, (int)enable); |
| |
| return size; |
| } |
| |
| static ssize_t sx9330_enable_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct sx9330_p *data = dev_get_drvdata(dev); |
| |
| return snprintf(buf, PAGE_SIZE, "%d\n", atomic_read(&data->enable)); |
| } |
| |
| static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR | S_IWGRP, |
| sx9330_enable_show, sx9330_enable_store); |
| |
| static struct attribute *sx9330_attributes[] = { |
| &dev_attr_enable.attr, |
| NULL |
| }; |
| |
| static struct attribute_group sx9330_attribute_group = { |
| .attrs = sx9330_attributes |
| }; |
| |
| static void sx9330_touch_process(struct sx9330_p *data) |
| { |
| u32 status = 0; |
| |
| sx9330_i2c_read_16bit(data, SX9330_STAT0_REG, &status); |
| status = status >> 24; |
| pr_info("[SX9330_WIFI]: %s - (status: 0x%x)\n", __func__, status); |
| |
| sx9330_get_data(data); |
| if (data->abnormal_mode) { |
| if (status & (CSX_STATUS_REG << MAIN_SENSOR)) { |
| if (data->max_diff < data->diff) |
| data->max_diff = data->diff; |
| data->irq_count++; |
| } |
| } |
| |
| if (data->state_ch1 == IDLE) { |
| if (status & (CSX_STATUS_REG << MAIN_SENSOR)) |
| sx9330_send_event(data, MAIN_SENSOR, ACTIVE); |
| else |
| pr_info("[SX9330_WIFI]: %s - 0x%x already released.\n", |
| __func__, status); |
| } else { // User released |
| if (!(status & (CSX_STATUS_REG << MAIN_SENSOR))) |
| sx9330_send_event(data, MAIN_SENSOR, IDLE); |
| else |
| pr_info("[SX9330_WIFI]: %s - 0x%x still touched\n", |
| __func__, status); |
| } |
| |
| #ifdef CONFIG_SENSORS_SX9330_WIFI_2CH |
| sx9330_get_ch2_data(data); |
| |
| if (data->state_ch2 == IDLE) { |
| if (status & (CSX_STATUS_REG << REF_SENSOR)) |
| sx9330_send_event(data, REF_SENSOR, ACTIVE); |
| else |
| pr_info("[SX9330_WIFI]: %s - 0x%x ch2 already released.\n", |
| __func__, status); |
| } else { // User released |
| if (!(status & (CSX_STATUS_REG << REF_SENSOR))) |
| sx9330_send_event(data, REF_SENSOR, IDLE); |
| else |
| pr_info("[SX9330_WIFI]: %s - 0x%x ch2 still touched\n", |
| __func__, status); |
| } |
| #endif |
| } |
| |
| static void sx9330_process_interrupt(struct sx9330_p *data) |
| { |
| u8 status = 0; |
| |
| /* since we are not in an interrupt don't need to disable irq. */ |
| status = sx9330_read_irqstate(data); |
| |
| if (status & IRQ_PROCESS_CONDITION) { |
| sx9330_touch_process(data); |
| } else { |
| pr_info("[SX9330_WIFI]: %s interrupt generated but skip - status : %d\n", |
| __func__, status); |
| } |
| } |
| |
| static void sx9330_init_work_func(struct work_struct *work) |
| { |
| struct sx9330_p *data = container_of((struct delayed_work *)work, |
| struct sx9330_p, init_work); |
| |
| int retry = 0; |
| |
| sx9330_initialize_chip(data); |
| |
| sx9330_set_mode(data, SX9330_MODE_NORMAL); |
| /* make sure no interrupts are pending since enabling irq |
| * will only work on next falling edge */ |
| sx9330_read_irqstate(data); |
| msleep(20); |
| |
| while(retry++ < 10) { |
| sx9330_get_data(data); |
| /* Defence code */ |
| if (data->capMain == 0 && data->avg == 0 && data->diff == 0 |
| && data->useful == 0 && data->offset == 0) { |
| pr_info("[SX9330]: Defence code for grip sensor - retry: %d\n", retry); |
| |
| sx9330_i2c_write_16bit(data, SX9330_RESET_REG, I2C_SOFTRESET_VALUE); |
| msleep(300); |
| sx9330_initialize_chip(data); |
| sx9330_set_mode(data, SX9330_MODE_NORMAL); |
| sx9330_read_irqstate(data); |
| msleep(20); |
| } else { |
| break; |
| } |
| } |
| } |
| |
| static void sx9330_irq_work_func(struct work_struct *work) |
| { |
| struct sx9330_p *data = container_of((struct delayed_work *)work, |
| struct sx9330_p, irq_work); |
| |
| if (sx9330_get_nirq_state(data) == 0) |
| sx9330_process_interrupt(data); |
| else |
| pr_err("[SX9330_WIFI]: %s - nirq read high %d\n", |
| __func__, sx9330_get_nirq_state(data)); |
| } |
| |
| static void sx9330_debug_work_func(struct work_struct *work) |
| { |
| struct sx9330_p *data = container_of((struct delayed_work *)work, |
| struct sx9330_p, debug_work); |
| static int hall_flag = 1; |
| |
| #if defined(CONFIG_FOLDER_HALL) |
| char str[2] = "0"; |
| #else |
| char str[6] = "CLOSE"; |
| #endif |
| |
| if (data->hallic_detect) { |
| sx9330_check_hallic_state(HALLIC_PATH, data->hall_ic); |
| |
| data->hall_ic[sizeof(str)-1] = '\0'; |
| |
| if (strcmp(data->hall_ic, str) == 0) { |
| if (hall_flag) { |
| pr_info("[SX9330_WIFI]: %s - hall IC is closed\n", __func__); |
| sx9330_set_offset_calibration(data); |
| hall_flag = 0; |
| } |
| } else { |
| hall_flag = 1; |
| } |
| } |
| |
| if (atomic_read(&data->enable) == ON) { |
| if (data->abnormal_mode) { |
| sx9330_get_data(data); |
| if (data->max_normal_diff < data->diff) |
| data->max_normal_diff = data->diff; |
| } else { |
| if (data->debug_count >= GRIP_LOG_TIME) { |
| sx9330_get_data(data); |
| data->debug_count = 0; |
| } else { |
| data->debug_count++; |
| } |
| } |
| } |
| |
| schedule_delayed_work(&data->debug_work, msecs_to_jiffies(2000)); |
| } |
| |
| static irqreturn_t sx9330_interrupt_thread(int irq, void *pdata) |
| { |
| struct sx9330_p *data = pdata; |
| |
| wake_lock_timeout(&data->grip_wake_lock, 3 * HZ); |
| schedule_delayed_work(&data->irq_work, msecs_to_jiffies(100)); |
| |
| return IRQ_HANDLED; |
| } |
| |
| static int sx9330_input_init(struct sx9330_p *data) |
| { |
| int ret = 0; |
| struct input_dev *dev = NULL; |
| |
| /* Create the input device */ |
| dev = input_allocate_device(); |
| if (!dev) |
| return -ENOMEM; |
| |
| dev->name = MODULE_NAME; |
| dev->id.bustype = BUS_I2C; |
| |
| input_set_capability(dev, EV_REL, REL_MISC); |
| input_set_capability(dev, EV_REL, REL_DIAL); |
| input_set_drvdata(dev, data); |
| |
| ret = input_register_device(dev); |
| if (ret < 0) { |
| input_free_device(dev); |
| return ret; |
| } |
| |
| ret = sensors_create_symlink(&dev->dev.kobj, dev->name); |
| if (ret < 0) { |
| input_unregister_device(dev); |
| return ret; |
| } |
| |
| ret = sysfs_create_group(&dev->dev.kobj, &sx9330_attribute_group); |
| if (ret < 0) { |
| sensors_remove_symlink(&dev->dev.kobj, dev->name); |
| input_unregister_device(dev); |
| return ret; |
| } |
| |
| /* save the input pointer and finish initialization */ |
| data->input = dev; |
| |
| return 0; |
| } |
| |
| static int sx9330_setup_pin(struct sx9330_p *data) |
| { |
| int ret; |
| |
| ret = gpio_request(data->gpio_nirq, "SX9330_wifi_nIRQ"); |
| if (ret < 0) { |
| pr_err("[SX9330_WIFI]: %s - gpio %d request failed (%d)\n", |
| __func__, data->gpio_nirq, ret); |
| return ret; |
| } |
| |
| ret = gpio_direction_input(data->gpio_nirq); |
| if (ret < 0) { |
| pr_err("[SX9330_WIFI]: %s - failed to set gpio %d as input (%d)\n", |
| __func__, data->gpio_nirq, ret); |
| gpio_free(data->gpio_nirq); |
| return ret; |
| } |
| |
| return 0; |
| } |
| |
| #if 0 |
| static void sx9330_set_specific_register(int regi_num, int end, int start, |
| u8 val) |
| { |
| u16 clear_bit = 0x00; |
| unsigned char temp_val; |
| |
| temp_val = setup_reg[regi_num].val; |
| clear_bit = ~((1 << (end + 1)) - (1 << start)); |
| temp_val = (temp_val & clear_bit) | (val << start); |
| setup_reg[regi_num].val = temp_val; |
| } |
| #endif |
| |
| static void sx9330_initialize_variable(struct sx9330_p *data) |
| { |
| data->init_done = OFF; |
| data->skip_data = false; |
| data->state_ch1 = IDLE; |
| #ifdef CONFIG_SENSORS_SX9330_WIFI_2CH |
| data->state_ch2 = IDLE; |
| #endif |
| atomic_set(&data->enable, OFF); |
| } |
| |
| |
| static int sx9330_read_setupreg(struct device_node *dnode, char *str, u32 *val) |
| { |
| u32 temp_val; |
| int ret; |
| |
| ret = of_property_read_u32(dnode, str, &temp_val); |
| |
| if (!ret) |
| *val = temp_val; |
| else |
| pr_err("[SX9330_WIFI]: %s - %s: property read err 0x%08x (%d)\n", |
| __func__, str, temp_val, ret); |
| |
| return ret; |
| } |
| |
| static int sx9330_parse_dt(struct sx9330_p *data, struct device *dev) |
| { |
| struct device_node *node = dev->of_node; |
| enum of_gpio_flags flags; |
| int ret; |
| |
| u32 scanperiod; |
| u32 gnrlctrl2; |
| u32 adcfiltph0, adcfiltph1; |
| u32 afeparamsph0, afeparamsph1; |
| u32 afephph0; |
| u32 avgbfilt; |
| u32 avgafilt; |
| u32 refcorra; |
| u32 advdig3, advdig4; |
| |
| if (node == NULL) |
| return -ENODEV; |
| |
| data->gpio_nirq = of_get_named_gpio_flags(node, |
| "sx9330_wifi,nirq-gpio", 0, &flags); |
| if (data->gpio_nirq < 0) { |
| pr_err("[SX9330_WIFI]: %s - get gpio_nirq error\n", __func__); |
| return -ENODEV; |
| } |
| |
| if (!sx9330_read_setupreg(node, SX9330_SCANPERIOD, &scanperiod)) |
| setup_reg[SX9330_SCANPERIOD_REG_IDX].val = scanperiod; |
| if (!sx9330_read_setupreg(node, SX9330_GNRLCTRL2, &gnrlctrl2)) |
| setup_reg[SX9330_GNRLCTRL2_REG_IDX].val = gnrlctrl2; |
| |
| /* phase 0 */ |
| if (!sx9330_read_setupreg(node, SX9330_AFEPARAMSPH0, &afeparamsph0)) |
| setup_reg[SX9330_AFEPARAMSPH0_REG_IDX].val = afeparamsph0; |
| if (!sx9330_read_setupreg(node, SX9330_AFEPHPH0, &afephph0)) |
| setup_reg[SX9330_AFEPHPH0_REG_IDX].val = afephph0; |
| if (!sx9330_read_setupreg(node, SX9330_ADCFILTPH0, &adcfiltph0)) |
| setup_reg[SX9330_ADCFILTPH0_REG_IDX].val = adcfiltph0; |
| |
| /* phase 1 */ |
| if (!sx9330_read_setupreg(node, SX9330_AFEPARAMSPH1, &afeparamsph1)) |
| setup_reg[SX9330_AFEPARAMSPH0_REG_IDX + MAIN_SENSOR*2].val = afeparamsph1; |
| if (!sx9330_read_setupreg(node, SX9330_ADCFILTPH1, &adcfiltph1)) |
| setup_reg[SX9330_ADCFILTPH0_REG_IDX + MAIN_SENSOR*8].val = adcfiltph1; |
| if (!sx9330_read_setupreg(node, SX9330_AVGBFILT, &avgbfilt)) |
| setup_reg[SX9330_AVGBFILTPH0_REG_IDX + MAIN_SENSOR*8].val = avgbfilt; |
| if (!sx9330_read_setupreg(node, SX9330_AVGAFILT, &avgafilt)) |
| setup_reg[SX9330_AVGAFILTPH0_REG_IDX + MAIN_SENSOR*8].val = avgafilt; |
| if (!sx9330_read_setupreg(node, SX9330_ADVDIG3, &advdig3)) |
| setup_reg[SX9330_ADVDIG3PH0_REG_IDX + MAIN_SENSOR*8].val = advdig3; |
| if (!sx9330_read_setupreg(node, SX9330_ADVDIG4, &advdig4)) |
| setup_reg[SX9330_ADVDIG4PH0_REG_IDX + MAIN_SENSOR*8].val = advdig4; |
| if (!sx9330_read_setupreg(node, SX9330_REFCORRA, &refcorra)) |
| setup_reg[SX9330_REFCORRA_REG_IDX].val = refcorra; |
| |
| ret = of_property_read_u32(node, "sx9330_wifi,hallic_detect", &data->hallic_detect); |
| if (ret < 0) |
| data->hallic_detect = 0; |
| |
| return 0; |
| } |
| |
| #if defined(CONFIG_CCIC_NOTIFIER) && defined(CONFIG_USB_TYPEC_MANAGER_NOTIFIER) |
| static int sx9330_ccic_handle_notification(struct notifier_block *nb, |
| unsigned long action, void *data) |
| { |
| CC_NOTI_ATTACH_TYPEDEF usb_typec_info = |
| *(CC_NOTI_ATTACH_TYPEDEF *)data; |
| struct sx9330_p *pdata = |
| container_of(nb, struct sx9330_p, cpuidle_ccic_nb); |
| static int pre_attach; |
| |
| if (usb_typec_info.src != CCIC_NOTIFY_DEV_MUIC || |
| usb_typec_info.dest != CCIC_NOTIFY_DEV_BATTERY) |
| return 0; |
| |
| if (pre_attach == usb_typec_info.attach) |
| return 0; |
| |
| if (pdata->init_done == ON) { |
| switch (usb_typec_info.cable_type) { |
| case ATTACHED_DEV_NONE_MUIC: |
| case ATTACHED_DEV_JIG_UART_OFF_MUIC: |
| case ATTACHED_DEV_JIG_UART_OFF_VB_MUIC: /* VBUS enabled */ |
| case ATTACHED_DEV_JIG_UART_OFF_VB_OTG_MUIC: /* for otg test */ |
| case ATTACHED_DEV_JIG_UART_OFF_VB_FG_MUIC: /* for fuelgauge test */ |
| case ATTACHED_DEV_JIG_UART_ON_MUIC: |
| case ATTACHED_DEV_JIG_UART_ON_VB_MUIC: /* VBUS enabled */ |
| case ATTACHED_DEV_JIG_USB_OFF_MUIC: |
| case ATTACHED_DEV_JIG_USB_ON_MUIC: |
| pr_info("[SX9330_WIFI]: %s skip cable = %d, attach = %d\n", |
| __func__, usb_typec_info.cable_type, usb_typec_info.attach); |
| break; |
| default: |
| pr_info("[SX9330_WIFI]: %s accept cable = %d, attach = %d\n", |
| __func__, usb_typec_info.cable_type, usb_typec_info.attach); |
| sx9330_set_offset_calibration(pdata); |
| break; |
| } |
| } |
| |
| pre_attach = usb_typec_info.attach; |
| |
| return 0; |
| } |
| #elif defined(CONFIG_MUIC_NOTIFIER) |
| static int sx9330_muic_notifier(struct notifier_block *nb, |
| unsigned long action, void *data) |
| { |
| struct sx9330_p *pdata = container_of(nb, struct sx9330_p, muic_nb); |
| muic_attached_dev_t attached_dev = *(muic_attached_dev_t *)data; |
| |
| switch (attached_dev) { |
| case ATTACHED_DEV_OTG_MUIC: |
| case ATTACHED_DEV_USB_MUIC: |
| case ATTACHED_DEV_TA_MUIC: |
| case ATTACHED_DEV_AFC_CHARGER_PREPARE_MUIC: |
| case ATTACHED_DEV_AFC_CHARGER_9V_MUIC: |
| if (action == MUIC_NOTIFY_CMD_ATTACH) { |
| pr_info("[SX9330_WIFI]: %s TA/USB is inserted\n", __func__); |
| } else { |
| pr_info("[SX9330_WIFI]: %s TA/USB is removed\n", __func__); |
| } |
| if (pdata->init_done == ON) |
| sx9330_set_offset_calibration(pdata); |
| else |
| pr_info("[SX9330_WIFI]: %s not initialized\n", __func__); |
| break; |
| default: |
| break; |
| } |
| |
| pr_info("[SX9330_WIFI]: %s dev=%d, action=%lu\n", __func__, attached_dev, action); |
| |
| return NOTIFY_DONE; |
| } |
| #endif |
| |
| static int sx9330_check_chip_id(struct sx9330_p *data) |
| { |
| int ret; |
| u32 value = 0; |
| |
| ret = sx9330_i2c_read_16bit(data, SX9330_INFO_REG, &value); |
| if (ret < 0) { |
| pr_err("[SX9330_WIFI]: whoami[0x%x] read failed %d\n", value, ret); |
| return ret; |
| } |
| |
| value &= 0xFF00; |
| value = value >> 8; |
| |
| switch (value) { |
| case 0x30: |
| return 0; |
| case 0x34: |
| return 0; |
| default: |
| pr_err("[SX9330_WIFI]: invalid whoami(%x)\n", value); |
| return -1; |
| } |
| } |
| |
| static int sx9330_probe(struct i2c_client *client, |
| const struct i2c_device_id *id) |
| { |
| int ret = -ENODEV; |
| struct sx9330_p *data = NULL; |
| |
| pr_info("[SX9330_WIFI]: %s - Probe Start!\n", __func__); |
| if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { |
| pr_err("[SX9330_WIFI]: %s - i2c_check_functionality error\n", |
| __func__); |
| goto exit; |
| } |
| |
| /* create memory for main struct */ |
| data = kzalloc(sizeof(struct sx9330_p), GFP_KERNEL); |
| if (data == NULL) { |
| pr_err("[SX9330_WIFI]: %s - kzalloc error\n", __func__); |
| ret = -ENOMEM; |
| goto exit_kzalloc; |
| } |
| |
| i2c_set_clientdata(client, data); |
| data->client = client; |
| data->factory_device = &client->dev; |
| |
| ret = sx9330_input_init(data); |
| if (ret < 0) |
| goto exit_input_init; |
| |
| wake_lock_init(&data->grip_wake_lock, |
| WAKE_LOCK_SUSPEND, "grip_wake_lock"); |
| mutex_init(&data->read_mutex); |
| |
| ret = sx9330_parse_dt(data, &client->dev); |
| if (ret < 0) { |
| pr_err("[SX9330_WIFI]: %s - of_node error\n", __func__); |
| ret = -ENODEV; |
| goto exit_of_node; |
| } |
| |
| ret = sx9330_setup_pin(data); |
| if (ret) { |
| pr_err("[SX9330_WIFI]: %s - could not setup pin\n", __func__); |
| goto exit_setup_pin; |
| } |
| |
| /* read chip id */ |
| ret = sx9330_check_chip_id(data); |
| if (ret < 0) { |
| pr_err("[SX9330_WIFI]: %s - chip id check failed %d\n", __func__, ret); |
| goto exit_chip_reset; |
| } |
| |
| ret = sx9330_i2c_write_16bit(data, SX9330_RESET_REG, I2C_SOFTRESET_VALUE); |
| if (ret < 0) { |
| pr_err("[SX9330_WIFI]: %s - chip reset failed %d\n", __func__, ret); |
| goto exit_chip_reset; |
| } |
| |
| sx9330_initialize_variable(data); |
| INIT_DELAYED_WORK(&data->init_work, sx9330_init_work_func); |
| INIT_DELAYED_WORK(&data->irq_work, sx9330_irq_work_func); |
| INIT_DELAYED_WORK(&data->debug_work, sx9330_debug_work_func); |
| |
| data->irq = gpio_to_irq(data->gpio_nirq); |
| /* initailize interrupt reporting */ |
| ret = request_threaded_irq(data->irq, NULL, sx9330_interrupt_thread, |
| IRQF_TRIGGER_FALLING | IRQF_ONESHOT, |
| "sx9330_wifi_irq", data); |
| if (ret < 0) { |
| pr_err("[SX9330_WIFI]: %s - failed to set request_threaded_irq %d" |
| " as returning (%d)\n", __func__, data->irq, ret); |
| goto exit_request_threaded_irq; |
| } |
| disable_irq(data->irq); |
| |
| ret = sensors_register(&data->factory_device, |
| data, sensor_attrs, MODULE_NAME); |
| if (ret) { |
| pr_err("[SX9330_WIFI] %s - cound not register sensor(%d).\n", |
| __func__, ret); |
| goto exit_register_failed; |
| } |
| |
| schedule_delayed_work(&data->init_work, msecs_to_jiffies(800)); |
| sx9330_set_debug_work(data, ON, 20000); |
| |
| #if defined(CONFIG_CCIC_NOTIFIER) && defined(CONFIG_USB_TYPEC_MANAGER_NOTIFIER) |
| manager_notifier_register(&data->cpuidle_ccic_nb, |
| sx9330_ccic_handle_notification, |
| MANAGER_NOTIFY_CCIC_USB); |
| #elif defined(CONFIG_MUIC_NOTIFIER) |
| muic_notifier_register(&data->muic_nb, sx9330_muic_notifier, |
| MUIC_NOTIFY_DEV_CPUIDLE); |
| #endif |
| |
| pr_info("[SX9330_WIFI]: %s - Probe done!\n", __func__); |
| |
| return 0; |
| |
| exit_register_failed: |
| free_irq(data->irq, data); |
| exit_request_threaded_irq: |
| exit_chip_reset: |
| gpio_free(data->gpio_nirq); |
| exit_setup_pin: |
| exit_of_node: |
| mutex_destroy(&data->read_mutex); |
| wake_lock_destroy(&data->grip_wake_lock); |
| sysfs_remove_group(&data->input->dev.kobj, &sx9330_attribute_group); |
| sensors_remove_symlink(&data->input->dev.kobj, data->input->name); |
| input_unregister_device(data->input); |
| exit_input_init: |
| kfree(data); |
| exit_kzalloc: |
| exit: |
| pr_err("[SX9330_WIFI]: %s - Probe fail!\n", __func__); |
| return ret; |
| } |
| |
| static int sx9330_remove(struct i2c_client *client) |
| { |
| struct sx9330_p *data = (struct sx9330_p *)i2c_get_clientdata(client); |
| |
| if (atomic_read(&data->enable) == ON) |
| sx9330_set_enable(data, OFF); |
| |
| sx9330_set_mode(data, SX9330_MODE_SLEEP); |
| |
| cancel_delayed_work_sync(&data->init_work); |
| cancel_delayed_work_sync(&data->irq_work); |
| cancel_delayed_work_sync(&data->debug_work); |
| free_irq(data->irq, data); |
| gpio_free(data->gpio_nirq); |
| |
| wake_lock_destroy(&data->grip_wake_lock); |
| sensors_unregister(data->factory_device, sensor_attrs); |
| sensors_remove_symlink(&data->input->dev.kobj, data->input->name); |
| sysfs_remove_group(&data->input->dev.kobj, &sx9330_attribute_group); |
| input_unregister_device(data->input); |
| mutex_destroy(&data->read_mutex); |
| |
| kfree(data); |
| |
| return 0; |
| } |
| |
| static int sx9330_suspend(struct device *dev) |
| { |
| struct sx9330_p *data = dev_get_drvdata(dev); |
| int cnt = 0; |
| |
| pr_info("[SX9330_WIFI]: %s\n", __func__); |
| /* before go to sleep, make the interrupt pin as high*/ |
| while ((sx9330_get_nirq_state(data) == 0) && (cnt++ < 3)) { |
| sx9330_read_irqstate(data); |
| msleep(20); |
| } |
| if (cnt >= 3) |
| pr_err("[SX9330_WIFI]: %s - s/w reset fail(%d)\n", __func__, cnt); |
| |
| sx9330_set_debug_work(data, OFF, 1000); |
| |
| return 0; |
| } |
| |
| static int sx9330_resume(struct device *dev) |
| { |
| struct sx9330_p *data = dev_get_drvdata(dev); |
| pr_info("[SX9330_WIFI]: %s\n", __func__); |
| sx9330_set_debug_work(data, ON, 1000); |
| |
| return 0; |
| } |
| |
| static void sx9330_shutdown(struct i2c_client *client) |
| { |
| struct sx9330_p *data = i2c_get_clientdata(client); |
| |
| pr_info("[SX9330_WIFI]: %s\n", __func__); |
| sx9330_set_debug_work(data, OFF, 1000); |
| if (atomic_read(&data->enable) == ON) |
| sx9330_set_enable(data, OFF); |
| sx9330_set_mode(data, SX9330_MODE_SLEEP); |
| } |
| |
| static struct of_device_id sx9330_match_table[] = { |
| { .compatible = "sx9330_wifi",}, |
| {}, |
| }; |
| |
| static const struct i2c_device_id sx9330_id[] = { |
| { "sx9330_match_table", 0 }, |
| { } |
| }; |
| |
| static const struct dev_pm_ops sx9330_pm_ops = { |
| .suspend = sx9330_suspend, |
| .resume = sx9330_resume, |
| }; |
| |
| static struct i2c_driver sx9330_driver = { |
| .driver = { |
| .name = MODEL_NAME, |
| .owner = THIS_MODULE, |
| .of_match_table = sx9330_match_table, |
| .pm = &sx9330_pm_ops |
| }, |
| .probe = sx9330_probe, |
| .remove = sx9330_remove, |
| .shutdown = sx9330_shutdown, |
| .id_table = sx9330_id, |
| }; |
| |
| static int __init sx9330_init(void) |
| { |
| return i2c_add_driver(&sx9330_driver); |
| } |
| |
| static void __exit sx9330_exit(void) |
| { |
| i2c_del_driver(&sx9330_driver); |
| } |
| |
| module_init(sx9330_init); |
| module_exit(sx9330_exit); |
| |
| MODULE_DESCRIPTION("Semtech Corp. SX9330 Capacitive Touch Controller Driver"); |
| MODULE_AUTHOR("Samsung Electronics"); |
| MODULE_LICENSE("GPL"); |