| /* drivers/input/misc/gp2ap110s.c - GP2AP110S00F v1.0.1 proximity sensor driver |
| * |
| * Copyright (C) 2018 Sharp Corporation |
| * |
| * This software is licensed under the terms of the GNU General Public |
| * License version 2, as published by the Free Software Foundation, and |
| * may be copied, distributed, and modified under those terms. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| */ |
| |
| #include <linux/module.h> |
| #include <linux/kernel.h> |
| #include <linux/mutex.h> |
| #include <linux/platform_device.h> |
| #include <linux/input.h> |
| #include <linux/delay.h> |
| #include <linux/errno.h> |
| #include <linux/init.h> |
| #include <linux/i2c.h> |
| #include <linux/irq.h> |
| #include <linux/gpio.h> |
| #include <linux/interrupt.h> |
| #include <linux/string.h> |
| #include <linux/slab.h> |
| #include <linux/wakelock.h> |
| #include <linux/of_gpio.h> |
| #include <linux/fs.h> |
| #include <asm/segment.h> |
| #include <asm/uaccess.h> |
| #include <linux/sensor/sensors_core.h> |
| #include "gp2ap110s.h" |
| |
| #define DEVICE_NAME "GP2AP110S" |
| #define CHIP_DEV_VENDOR "SHARP" |
| #define MODULE_NAME "proximity_sensor" |
| #define PROX_READ_NUM 40 |
| #define OFFSET_TUNE_ADC 100 |
| #define PROX_AUTO_OFFSET 0x05 |
| #define MAX_RETRY_TUNE_ADC_COUNT 3 |
| #define FORCE_CLOSE_OFFSET_THD 127 |
| #define FORCE_CLOSE_HIGH_THD 750 |
| #define FORCE_CLOSE_LOW_THD 600 |
| |
| // Settings 1 (Default) |
| #define LED_REG_VAL_1 0x14 // 10 mA |
| #define HIGH_THD_1 370 |
| #define LOW_THD_1 260 |
| |
| // (If proximity close detection fails during LCIA test) |
| #define LED_REG_VAL_2 0x24 // 20 mA |
| #define HIGH_THD_2 200 |
| #define LOW_THD_2 170 |
| |
| #define PROX_SETTINGS_FILE_PATH "/efs/FactoryApp/prox_settings" |
| |
| static int gp2ap_i2c_read(u8 reg, unsigned char *rbuf, int len, struct i2c_client *client) |
| { |
| int err = -1; |
| struct i2c_msg i2cMsg[2]; |
| uint8_t buff; |
| |
| if (client == NULL) |
| return -ENODEV; |
| |
| i2cMsg[0].addr = client->addr; |
| i2cMsg[0].flags = 0; |
| i2cMsg[0].len = 1; |
| i2cMsg[0].buf = &buff; |
| |
| buff = reg; |
| |
| i2cMsg[1].addr = client->addr; |
| i2cMsg[1].flags = I2C_M_RD; |
| i2cMsg[1].len = len; |
| i2cMsg[1].buf = rbuf; |
| |
| err = i2c_transfer(client->adapter, &i2cMsg[0], 2); |
| if (err < 0) { |
| SENSOR_ERR("i2c transfer error(%d)!!\n", err); |
| } |
| |
| return err; |
| } |
| |
| static int gp2ap_i2c_write(u8 reg, u8 wbuf, struct i2c_client *client) |
| { |
| int err = 0; |
| struct i2c_msg i2cMsg; |
| unsigned char buff[2]; |
| int retry = 10; |
| |
| if (client == NULL || (!client->adapter)) |
| return -ENODEV; |
| |
| buff[0] = reg; |
| buff[1] = wbuf; |
| |
| i2cMsg.addr = client->addr; |
| i2cMsg.flags = 0; |
| i2cMsg.len = 2; |
| i2cMsg.buf = buff; |
| |
| while(retry--) { |
| err = i2c_transfer(client->adapter, &i2cMsg, 1); |
| SENSOR_INFO("0x%x, 0x%x\n", reg, wbuf); |
| if (err >= 0) |
| return 0; |
| } |
| |
| SENSOR_ERR("i2c transfer error(%d)!!\n", err); |
| |
| return err; |
| } |
| |
| static int gp2ap_write_settings(struct gp2ap_data *data) |
| { |
| struct file *filp = NULL; |
| mm_segment_t old_fs; |
| int ret = -1; |
| char tmp_buf[14] = ""; |
| char *buf = NULL; |
| |
| int led_reg_val, ps_high_th, ps_low_th; |
| |
| if (data->prox_settings == 1) { |
| led_reg_val = data->led_reg_val_1; |
| ps_high_th = data->ps_high_th_1; |
| ps_low_th = data->ps_low_th_1; |
| } else if (data->prox_settings == 2) { |
| led_reg_val = data->led_reg_val_2; |
| ps_high_th = data->ps_high_th_2; |
| ps_low_th = data->ps_low_th_2; |
| data->min_close_offset = MAX_OFFSET; |
| } |
| |
| data->led_reg_val = led_reg_val; |
| |
| data->bytes = snprintf(tmp_buf, PAGE_SIZE, "%d,%d,%d", |
| led_reg_val, ps_high_th, ps_low_th); |
| |
| buf = kzalloc(sizeof(char) * (data->bytes), GFP_KERNEL); |
| data->bytes = snprintf(buf, PAGE_SIZE, "%d,%d,%d", |
| led_reg_val, ps_high_th, ps_low_th); |
| |
| SENSOR_INFO("tmp_buf=%s, buf=%s, bytes=%d\n", tmp_buf, buf, data->bytes); |
| |
| old_fs = get_fs(); |
| set_fs(KERNEL_DS); |
| |
| filp = filp_open(PROX_SETTINGS_FILE_PATH, |
| O_CREAT | O_TRUNC | O_RDWR | O_SYNC, 0666); |
| |
| if (filp == NULL) { |
| SENSOR_INFO("filp is NULL\n"); |
| return ret; |
| } |
| |
| if (IS_ERR(filp)) { |
| set_fs(old_fs); |
| ret = PTR_ERR(filp); |
| SENSOR_ERR("Can't open prox settings file (%d)\n", ret); |
| return ret; |
| } |
| |
| ret = vfs_write(filp, buf, data->bytes, &filp->f_pos); |
| if (ret != data->bytes) { |
| SENSOR_ERR("Can't write the prox settings data to file, ret=%d\n", ret); |
| ret = -EIO; |
| } |
| |
| filp_close(filp, current->files); |
| set_fs(old_fs); |
| |
| msleep(150); |
| |
| SENSOR_INFO("Done, ret=%d\n", ret); |
| |
| return ret; |
| } |
| |
| static int gp2ap_read_settings(struct gp2ap_data *data) |
| { |
| struct file *filp = NULL; |
| mm_segment_t old_fs; |
| int ret = -1; |
| |
| char *buf = kzalloc(sizeof(char) * (data->bytes), GFP_KERNEL); |
| |
| old_fs = get_fs(); |
| set_fs(KERNEL_DS); |
| |
| filp = filp_open(PROX_SETTINGS_FILE_PATH, O_RDONLY, 0); |
| if (IS_ERR(filp)) { |
| set_fs(old_fs); |
| ret = PTR_ERR(filp); |
| SENSOR_ERR("Can't open prox settings file (%d)\n", ret); |
| return ret; |
| } |
| |
| ret = vfs_read(filp, buf, data->bytes, &filp->f_pos); |
| if (ret <= 0) { |
| SENSOR_ERR("Can't read the prox settings data from file, bytes=%d\n", ret); |
| ret = -EIO; |
| } else { |
| sscanf(buf, "%d,%d,%d", &data->led_reg_val, &data->ps_high_th, &data->ps_low_th); |
| SENSOR_INFO("led_reg_val=%d, ps_high_th=%d, ps_low_th=%d\n", |
| data->led_reg_val, data->ps_high_th, data->ps_low_th); |
| } |
| |
| SENSOR_INFO("buf=%s\n", buf); |
| |
| filp_close(filp, current->files); |
| set_fs(old_fs); |
| |
| SENSOR_INFO("Done, ret=%d\n", ret); |
| |
| return ret; |
| } |
| |
| static void gp2ap_StartMeasurement(struct gp2ap_data *data) |
| { |
| u8 wdata; |
| wdata = 0xA0; |
| |
| gp2ap_i2c_write(REG_COM1, wdata, data->client); |
| } |
| |
| static void gp2ap_StopMeasurement(struct gp2ap_data *data) |
| { |
| u8 wdata; |
| wdata = 0x00; |
| |
| gp2ap_i2c_write(REG_COM1, wdata, data->client); |
| gp2ap_i2c_write(REG_COM2, wdata, data->client); |
| } |
| |
| static int gp2ap_get_proximity_adc(struct gp2ap_data *data) |
| { |
| u8 value[2]; |
| int ret; |
| |
| ret = gp2ap_i2c_read(REG_D0_LSB, value, sizeof(value), data->client); |
| if (ret < 0) { |
| SENSOR_ERR("fail, ret=%d\n", ret); |
| return ret; |
| } |
| |
| return (value[0] | (value[1] << 8)); |
| } |
| |
| static void gp2ap_InitData(struct gp2ap_data *data) |
| { |
| u8 wdata; |
| u8 offset; |
| int adc = 0; |
| |
| gp2ap_i2c_read(REG_DYNAMIC_CAL_RESULT, &offset, sizeof(offset), data->client); |
| SENSOR_INFO("offset=%d\n", offset); |
| |
| if(data->handle_high_offset) { |
| data->ps_high_th = data->force_close_thd_high; |
| data->ps_low_th = data->force_close_thd_low; |
| data->handle_high_offset = false; |
| SENSOR_INFO("Tune High th: high %d, low %d\n", data->ps_high_th, data->ps_low_th); |
| } else { |
| if (!data->pre_test) { |
| if (offset >= OFFSET_TUNE_ADC && |
| data->tune_adc_count < MAX_RETRY_TUNE_ADC_COUNT) { |
| adc = gp2ap_get_proximity_adc(data); |
| if(adc < 0) { |
| data->tune_adc_count++; |
| return; |
| } |
| wdata = (adc+ 1280)/256; |
| SENSOR_INFO("Tune ADC: adc %d, wdata 0x%x\n", adc, wdata); |
| if(wdata > 0x0D) |
| wdata = 0x0D; |
| data->handle_high_offset = true; |
| data->high_offset = (u16)wdata * 16; |
| gp2ap_i2c_write(0x8D, wdata, data->client); |
| data->tune_adc_count++; |
| return; |
| } |
| } |
| } |
| |
| wdata = 0x00; gp2ap_i2c_write(0x81, wdata, data->client); |
| wdata = 0x02; gp2ap_i2c_write(0x82, wdata, data->client); |
| wdata = 0x13; gp2ap_i2c_write(0x83, wdata, data->client); |
| wdata = 0x10; gp2ap_i2c_write(0x85, wdata, data->client); |
| wdata = data->led_reg_val; gp2ap_i2c_write(0x86, wdata, data->client); |
| wdata = 0x20; gp2ap_i2c_write(0x87, wdata, data->client); |
| wdata = (u8)data->ps_low_th; gp2ap_i2c_write(0x88, wdata, data->client); |
| wdata = (u8)(data->ps_low_th >> 8); gp2ap_i2c_write(0x89, wdata, data->client); |
| wdata = (u8)data->ps_high_th; gp2ap_i2c_write(0x8A, wdata, data->client); |
| wdata = (u8)(data->ps_high_th >> 8); gp2ap_i2c_write(0x8B, wdata, data->client); |
| |
| wdata = 0x88; gp2ap_i2c_write(0x8E, wdata, data->client); |
| wdata = 0x10; gp2ap_i2c_write(0xB1, wdata, data->client); |
| wdata = 0x02; gp2ap_i2c_write(0xF0, wdata, data->client); |
| wdata = 0x01; gp2ap_i2c_write(0xF1, wdata, data->client); |
| } |
| |
| static void gp2ap_InitDataDynamicCalibration(struct gp2ap_data *data) |
| { |
| u8 wdata; |
| |
| SENSOR_INFO("led_reg_val=%d", data->led_reg_val); |
| |
| wdata = 0x00; gp2ap_i2c_write(0x81, wdata, data->client); |
| wdata = 0x12; gp2ap_i2c_write(0x82, wdata, data->client); |
| wdata = 0x10; gp2ap_i2c_write(0x83, wdata, data->client); |
| wdata = 0x10; gp2ap_i2c_write(0x85, wdata, data->client); |
| wdata = data->led_reg_val; gp2ap_i2c_write(0x86, wdata, data->client); |
| wdata = 0x70; gp2ap_i2c_write(0x87, wdata, data->client); |
| wdata = 0x00; gp2ap_i2c_write(0x88, wdata, data->client); |
| wdata = 0x00; gp2ap_i2c_write(0x89, wdata, data->client); |
| wdata = 0x00; gp2ap_i2c_write(0x8A, wdata, data->client); |
| wdata = 0x00; gp2ap_i2c_write(0x8B, wdata, data->client); |
| wdata = 0x88; gp2ap_i2c_write(0x8E, wdata, data->client); |
| wdata = 0x10; gp2ap_i2c_write(0xB1, wdata, data->client); |
| wdata = 0x01; gp2ap_i2c_write(0xC1, wdata, data->client); |
| wdata = 0x21; gp2ap_i2c_write(0xC2, wdata, data->client); |
| wdata = 0x02; gp2ap_i2c_write(0xF0, wdata, data->client); |
| wdata = 0x01; gp2ap_i2c_write(0xF1, wdata, data->client); |
| } |
| |
| static ssize_t ps_enable_show(struct device *dev, |
| struct device_attribute *attr, |
| char *buf) |
| { |
| struct gp2ap_data *data = dev_get_drvdata(dev); |
| int enabled; |
| |
| SENSOR_INFO("ps_enable_show: %d\n", data->ps_enabled); |
| enabled = data->ps_enabled; |
| return sprintf(buf, "%d", enabled); |
| } |
| |
| |
| static void gp2ap_ps_setting(struct gp2ap_data *data) |
| { |
| int ret = 0; |
| SENSOR_INFO("\n"); |
| if (data->prox_settings == 0) { |
| ret = gp2ap_read_settings(data); |
| if (ret > 0) { |
| if (data->ps_high_th == data->ps_high_th_2) |
| data->prox_settings = 2; |
| else |
| data->prox_settings = 1; |
| |
| SENSOR_INFO("Applied File prox_settings=%d, led_reg_val=%d, high_thd=%d, low_thd=%d\n", |
| data->prox_settings, data->led_reg_val, data->ps_high_th, data->ps_low_th); |
| } else { |
| data->prox_settings = 1; |
| data->led_reg_val = data->led_reg_val_1; |
| data->ps_high_th = data->ps_high_th_1; |
| data->ps_low_th = data->ps_low_th_1; |
| |
| SENSOR_INFO("Applied prox_settings=%d, led_reg_val=%d, high_thd=%d, low_thd=%d\n", |
| data->prox_settings, data->led_reg_val, data->ps_high_th, data->ps_low_th); |
| } |
| } else if (data->prox_settings == 1) { |
| data->led_reg_val = data->led_reg_val_1; |
| data->ps_high_th = data->ps_high_th_1; |
| data->ps_low_th = data->ps_low_th_1; |
| |
| SENSOR_INFO("led_reg_val=%d, high_thd=%d, low_thd=%d\n", |
| data->led_reg_val, data->ps_high_th, data->ps_low_th); |
| |
| } else if (data->prox_settings == 2) { |
| data->led_reg_val = data->led_reg_val_2; |
| data->ps_high_th = data->ps_high_th_2; |
| data->ps_low_th = data->ps_low_th_2; |
| |
| SENSOR_INFO("led_reg_val=%d, high_thd=%d, low_thd=%d\n", |
| data->led_reg_val, data->ps_high_th, data->ps_low_th); |
| } |
| } |
| |
| static int gp2ap_ps_onoff(u8 onoff, struct gp2ap_data *data) |
| { |
| SENSOR_INFO("onoff= %d\n", onoff); |
| |
| if (onoff) { |
| gp2ap_ps_setting(data); |
| if (data->dynamic_calib_enabled) { |
| gp2ap_InitDataDynamicCalibration(data); |
| data->dynamic_calib_done = 0; |
| |
| gp2ap_StartMeasurement(data); |
| |
| msleep(50); |
| |
| data->dynamic_calib_done = 1; |
| SENSOR_INFO("dynamic calibration done\n"); |
| |
| if(data->zero_detect) |
| data->zero_detect = 0; |
| |
| gp2ap_InitData(data); |
| } else { |
| gp2ap_InitData(data); |
| } |
| gp2ap_StartMeasurement(data); |
| } else { |
| gp2ap_StopMeasurement(data); |
| } |
| |
| return 0; |
| } |
| |
| static ssize_t gp2ap_ps_enable_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t size) |
| { |
| struct gp2ap_data *data = dev_get_drvdata(dev); |
| bool new_value; |
| |
| if (sysfs_streq(buf, "1")) |
| new_value = true; |
| else if (sysfs_streq(buf, "0")) |
| new_value = false; |
| else { |
| SENSOR_ERR("invalid value %d\n", *buf); |
| return size; |
| } |
| |
| mutex_lock(&data->mutex_enable); |
| |
| SENSOR_INFO("new= %d, ps_enabled= %d\n", |
| new_value, data->ps_enabled); |
| |
| if (!data->ps_enabled && new_value) { |
| gp2ap_i2c_write(0x8D, 0, data->client); |
| data->ps_distance = 1; |
| data->zero_detect = 0; |
| data->ps_enabled = new_value; |
| data->tune_adc_count = 0; |
| data->handle_high_offset = false; |
| data->high_offset = 0; |
| mutex_lock(&data->mutex_ps_onoff); |
| gp2ap_ps_onoff(1, data); |
| mutex_unlock(&data->mutex_ps_onoff); |
| |
| if(data->tune_adc_count > 0 && data->tune_adc_count < MAX_RETRY_TUNE_ADC_COUNT) { |
| SENSOR_INFO("Restart sensor, data->tune_adc_count=%d\n", data->tune_adc_count); |
| mutex_lock(&data->mutex_ps_onoff); |
| gp2ap_ps_onoff(0, data); |
| gp2ap_ps_onoff(1, data); |
| mutex_unlock(&data->mutex_ps_onoff); |
| } |
| |
| enable_irq_wake(data->ps_irq); |
| enable_irq(data->ps_irq); |
| schedule_delayed_work(&data->offset_work, msecs_to_jiffies(OFFSET_TUNE_DELAY)); |
| |
| SENSOR_INFO("proximity_sensor enable!! \n"); |
| } else if (data->ps_enabled && !new_value) { |
| disable_irq_wake(data->ps_irq); |
| disable_irq(data->ps_irq); |
| |
| cancel_delayed_work_sync(&data->offset_work); |
| mutex_lock(&data->mutex_ps_onoff); |
| gp2ap_ps_onoff(0, data); |
| mutex_unlock(&data->mutex_ps_onoff); |
| data->ps_distance = 1; |
| data->ps_enabled = new_value; |
| |
| SENSOR_INFO("proximity_sensor disable!! \n"); |
| } |
| |
| mutex_unlock(&data->mutex_enable); |
| |
| return size; |
| } |
| |
| static DEVICE_ATTR(enable, 0664, ps_enable_show, gp2ap_ps_enable_store); |
| |
| static struct attribute *ps_attributes[] = |
| { |
| &dev_attr_enable.attr, |
| NULL |
| }; |
| |
| static struct attribute_group ps_attribute_group = |
| { |
| .attrs = ps_attributes |
| }; |
| |
| static int ps_input_init(struct gp2ap_data *data) |
| { |
| struct input_dev *dev; |
| int err = 0; |
| |
| /* Create the input device */ |
| dev = input_allocate_device(); |
| if (!dev) { |
| SENSOR_ERR("%s, input_allocate_device error!!\n", __func__); |
| return -ENOMEM; |
| } |
| |
| dev->name = MODULE_NAME; |
| dev->id.bustype = BUS_I2C; |
| input_set_drvdata(dev, data); |
| input_set_capability(dev, EV_ABS, ABS_DISTANCE); |
| input_set_abs_params(dev, ABS_DISTANCE, 0, 1, 0, 0); |
| |
| err = input_register_device(dev); |
| |
| if (err < 0) { |
| input_free_device(dev); |
| SENSOR_INFO("%s, input_register_device error(%d)!!\n", __func__, err); |
| return err; |
| } |
| |
| err = sensors_create_symlink(&dev->dev.kobj, dev->name); |
| |
| if (err < 0) { |
| SENSOR_ERR("create sysfs symlink error\n"); |
| input_unregister_device(dev); |
| return err; |
| } |
| |
| err = sysfs_create_group(&dev->dev.kobj, &ps_attribute_group); |
| if (err) { |
| SENSOR_INFO("sysfs_create_group failed[%s]\n", dev->name); |
| return err; |
| } |
| |
| data->ps_input_dev = dev; |
| |
| return err; |
| } |
| |
| void gp2ap_update_min_close_offset(struct gp2ap_data *data) |
| { |
| u8 offset; |
| u8 rdata_d0[2]; |
| u16 ps_count; |
| |
| gp2ap_i2c_read(REG_DYNAMIC_CAL_RESULT, &offset, sizeof(offset), data->client); |
| gp2ap_i2c_read(REG_D0_LSB, rdata_d0, sizeof(rdata_d0), data->client); |
| ps_count = (rdata_d0[1] << 8) | rdata_d0[0]; |
| |
| if (data->high_offset + offset + OFFSET_DELTA < data->min_close_offset) |
| data->min_close_offset = data->high_offset + offset + OFFSET_DELTA; |
| |
| SENSOR_INFO(":%u %u %u %u\n", ps_count, offset, data->high_offset, data->min_close_offset); |
| } |
| |
| irqreturn_t gp2ap_ps_irq_handler(int irq, void *id_data) |
| { |
| struct gp2ap_data *data = id_data; |
| u8 val; |
| |
| val = gpio_get_value(data->p_out); |
| |
| /*1: Far, 0: Near */ |
| SENSOR_INFO("val:%d en:%d\n", val, data->ps_enabled); |
| if(data->ps_enabled) |
| schedule_work(&data->ps_int_work); |
| |
| return IRQ_HANDLED; |
| } |
| |
| static void gp2ap_ps_work_int_func(struct work_struct *work) |
| { |
| struct gp2ap_data *data = container_of((struct work_struct *)work, |
| struct gp2ap_data, ps_int_work); |
| char distance; |
| u8 near_far = 1; |
| u8 rdata; |
| u8 rdata_d0[2]; |
| u8 offset; |
| |
| if (data == NULL) |
| return; |
| |
| mutex_lock(&data->mutex_interrupt); |
| gp2ap_i2c_read(REG_DYNAMIC_CAL_RESULT, &offset, sizeof(offset), data->client); |
| gp2ap_i2c_read(0x81, &rdata, sizeof(rdata), data->client); |
| if ((rdata & 0x08) == 0x08) |
| distance = 0; // Near |
| else |
| distance = 1; // Far |
| if (data->ps_distance != distance) { |
| data->ps_distance = distance; |
| } |
| |
| gp2ap_i2c_read(REG_D0_LSB, rdata_d0, sizeof(rdata_d0), data->client); |
| data->ps_count = (rdata_d0[1] << 8) | rdata_d0[0]; |
| |
| if (data->ps_count >= data->ps_high_th || (data->high_offset + offset >= data->min_close_offset && data->ps_count != 0)) |
| near_far = 0; |
| else if (data->ps_count < data->ps_low_th && (data->high_offset + offset < data->min_close_offset && data->ps_count != 0)) |
| near_far = 1; |
| |
| if (near_far == 0) { |
| data->zero_detect = 0; |
| } |
| |
| SENSOR_INFO("dis:%d near_far:%d count:%d zero:%d\n", |
| data->ps_distance, near_far, data->ps_count,data->zero_detect); |
| |
| if ((near_far == 1) && (data->ps_count == 0) && (data->zero_detect == 0)) { |
| SENSOR_INFO("zero detection!!!\n"); |
| data->zero_detect = 1; |
| mutex_lock(&data->mutex_ps_onoff); |
| disable_irq_wake(data->ps_irq); |
| disable_irq(data->ps_irq); |
| gp2ap_ps_onoff(0, data); |
| gp2ap_ps_onoff(1, data); |
| enable_irq_wake(data->ps_irq); |
| enable_irq(data->ps_irq); |
| mutex_unlock(&data->mutex_ps_onoff); |
| |
| if(!offset) { |
| gp2ap_i2c_write(0x8D, 0, data->client); |
| data->min_close_offset = MAX_OFFSET; |
| data->high_offset = 0; |
| SENSOR_INFO("offset = 0 , Restart sensor!"); |
| mutex_lock(&data->mutex_ps_onoff); |
| gp2ap_ps_onoff(0, data); |
| gp2ap_ps_onoff(1, data); |
| mutex_unlock(&data->mutex_ps_onoff); |
| } |
| } else { |
| input_report_abs(data->ps_input_dev, ABS_DISTANCE, near_far); |
| input_sync(data->ps_input_dev); |
| } |
| gp2ap_update_min_close_offset(data); |
| mutex_unlock(&data->mutex_interrupt); |
| } |
| |
| static int gp2ap_setup_irq(struct gp2ap_data *gp2ap) |
| { |
| int ret; |
| ret = gpio_request(gp2ap->p_out, "gpio_proximity_out"); |
| |
| if (ret < 0) { |
| SENSOR_ERR("gpio %d request failed (%d)\n", gp2ap->p_out, ret); |
| return ret; |
| } |
| |
| ret = gpio_direction_input(gp2ap->p_out); |
| if (ret < 0) { |
| SENSOR_ERR("failed gpio %d as input (%d)\n", gp2ap->p_out, ret); |
| goto err_gpio_direction_input; |
| } |
| |
| gp2ap->ps_irq = gpio_to_irq(gp2ap->p_out); |
| ret = request_irq(gp2ap->ps_irq, gp2ap_ps_irq_handler, |
| IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "proximity_int", gp2ap); |
| |
| if (ret < 0) { |
| SENSOR_ERR("request_irq(%d) failed for gpio %d (%d)\n", |
| gp2ap->ps_irq, gp2ap->p_out, ret); |
| goto err_request_irq; |
| } |
| |
| SENSOR_INFO("request_irq(%d) success (%d)\n", gp2ap->ps_irq, gp2ap->p_out); |
| |
| disable_irq(gp2ap->ps_irq); |
| |
| goto done; |
| err_request_irq: |
| err_gpio_direction_input: |
| gpio_free(gp2ap->p_out); |
| done: |
| return ret; |
| } |
| |
| static ssize_t name_read(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| return snprintf(buf, PAGE_SIZE, "%s\n", DEVICE_NAME); |
| } |
| |
| static ssize_t vendor_read(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| return snprintf(buf, PAGE_SIZE, "%s\n", CHIP_DEV_VENDOR); |
| } |
| |
| static ssize_t proximity_register_read_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct gp2ap_data *data = dev_get_drvdata(dev); |
| |
| u8 reg = 0; |
| int offset = 0; |
| u8 val = 0; |
| |
| for (reg = 0x80; reg <= 0x8E; reg++) { |
| gp2ap_i2c_read(reg, &val, sizeof(val), data->client); |
| SENSOR_INFO("Read Reg: 0x%2x Value: 0x%2x\n", reg, val); |
| offset += snprintf(buf + offset, PAGE_SIZE - offset, |
| "Reg: 0x%2x Value: 0x%2x\n", reg, val); |
| } |
| |
| for (reg = 0x90; reg <= 0x91; reg++) { |
| gp2ap_i2c_read(reg, &val, sizeof(val), data->client); |
| SENSOR_INFO("Read Reg: 0x%2x Value: 0x%2x\n", reg, val); |
| offset += snprintf(buf + offset, PAGE_SIZE - offset, |
| "Reg: 0x%2x Value: 0x%2x\n", reg, val); |
| } |
| |
| gp2ap_i2c_read(0xA0, &val, sizeof(val), data->client); |
| SENSOR_INFO("Read Reg: 0x%2x Value: 0x%2x\n", reg, val); |
| offset += snprintf(buf + offset, PAGE_SIZE - offset, |
| "Reg: 0x%2x Value: 0x%2x\n", reg, val); |
| |
| gp2ap_i2c_read(0xB1, &val, sizeof(val), data->client); |
| SENSOR_INFO("Read Reg: 0x%2x Value: 0x%2x\n", reg, val); |
| offset += snprintf(buf + offset, PAGE_SIZE - offset, |
| "Reg: 0x%2x Value: 0x%2x\n", reg, val); |
| |
| for (reg = 0xC0; reg <= 0xC2; reg++) { |
| gp2ap_i2c_read(reg, &val, sizeof(val), data->client); |
| SENSOR_INFO("Read Reg: 0x%2x Value: 0x%2x\n", reg, val); |
| offset += snprintf(buf + offset, PAGE_SIZE - offset, |
| "Reg: 0x%2x Value: 0x%2x\n", reg, val); |
| } |
| |
| for (reg = 0xF0; reg <= 0xF1; reg++) { |
| gp2ap_i2c_read(reg, &val, sizeof(val), data->client); |
| SENSOR_INFO("Read Reg: 0x%2x Value: 0x%2x\n", reg, val); |
| offset += snprintf(buf + offset, PAGE_SIZE - offset, |
| "Reg: 0x%2x Value: 0x%2x\n", reg, val); |
| } |
| |
| return offset; |
| } |
| |
| static ssize_t proximity_register_write_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t count) |
| { |
| struct gp2ap_data *data = dev_get_drvdata(dev); |
| |
| unsigned int reg, val; |
| int ret; |
| |
| if (sscanf(buf, "%2x,%2x", ®, &val) != 2) { |
| SENSOR_ERR("invalid value\n"); |
| return count; |
| } |
| |
| ret = gp2ap_i2c_write(reg, val, data->client); |
| if(ret < 0) |
| SENSOR_ERR("failed %d\n", ret); |
| else |
| SENSOR_INFO("Register(0x%2x) data(0x%2x)\n", reg, val); |
| |
| return count; |
| } |
| |
| static ssize_t proximity_cal_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct gp2ap_data *data = dev_get_drvdata(dev); |
| s8 value; |
| |
| gp2ap_i2c_read(REG_DYNAMIC_CAL_RESULT, &value, sizeof(value), data->client); |
| |
| return snprintf(buf, PAGE_SIZE, "%d,%d,%d\n", |
| value, data->ps_high_th, data->ps_low_th); |
| } |
| |
| static ssize_t proximity_cal_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t size) |
| { |
| //for LCiA ADC Check sequence |
| SENSOR_INFO("\n"); |
| return size; |
| } |
| |
| static ssize_t proximity_thresh_high_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct gp2ap_data *data = dev_get_drvdata(dev); |
| |
| SENSOR_INFO("%d\n", data->ps_high_th); |
| |
| return scnprintf(buf, PAGE_SIZE, "%d\n", data->ps_high_th); |
| } |
| |
| static ssize_t proximity_thresh_high_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t size) |
| { |
| struct gp2ap_data *data = dev_get_drvdata(dev); |
| u16 value = 0; |
| u8 wdata; |
| int ret; |
| |
| ret = kstrtou16(buf, 10, &value); |
| if (ret < 0) { |
| SENSOR_ERR("kstrtoul failed, ret=0x%x\n", ret); |
| return ret; |
| } |
| |
| SENSOR_INFO("thresh: %d\n", value); |
| |
| data->ps_high_th = value; |
| wdata = (u8)data->ps_high_th; |
| gp2ap_i2c_write(0x8A, wdata, data->client); |
| |
| wdata = (u8)(data->ps_high_th>>8); |
| gp2ap_i2c_write(0x8B, wdata, data->client); |
| |
| return size; |
| } |
| |
| static ssize_t proximity_thresh_low_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct gp2ap_data *data = dev_get_drvdata(dev); |
| |
| SENSOR_INFO("%d\n", data->ps_low_th); |
| |
| return scnprintf(buf, PAGE_SIZE, "%d\n", data->ps_low_th); |
| } |
| |
| static ssize_t proximity_thresh_low_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t size) |
| { |
| struct gp2ap_data *data = dev_get_drvdata(dev); |
| u16 value = 0; |
| u8 wdata; |
| int ret; |
| |
| ret = kstrtou16(buf, 10, &value); |
| if (ret < 0) { |
| SENSOR_ERR("kstrtoul failed, ret=0x%x\n", ret); |
| return ret; |
| } |
| |
| SENSOR_INFO("thresh: %d\n", value); |
| |
| data->ps_low_th = value; |
| wdata = (u8)data->ps_low_th; |
| gp2ap_i2c_write(0x88, wdata, data->client); |
| |
| wdata = (u8)(data->ps_low_th>>8); |
| gp2ap_i2c_write(0x89, wdata, data->client); |
| |
| return size; |
| } |
| |
| static ssize_t proximity_avg_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct gp2ap_data *data = dev_get_drvdata(dev); |
| return snprintf(buf, PAGE_SIZE, "%d,%d,%d\n", data->avg[0], |
| data->avg[1], data->avg[2]); |
| } |
| |
| static ssize_t proximity_avg_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t size) |
| { |
| struct gp2ap_data *data = dev_get_drvdata(dev); |
| bool new_value = false; |
| |
| if (sysfs_streq(buf, "1")) |
| new_value = true; |
| else if (sysfs_streq(buf, "0")) |
| new_value = false; |
| else { |
| SENSOR_ERR("invalid value %d\n", *buf); |
| return -EINVAL; |
| } |
| |
| SENSOR_INFO("average enable = %d\n", new_value); |
| |
| if (new_value) { |
| hrtimer_start(&data->prox_timer, data->prox_poll_delay, |
| HRTIMER_MODE_REL); |
| } else if (!new_value) { |
| hrtimer_cancel(&data->prox_timer); |
| cancel_work_sync(&data->work_prox); |
| } |
| |
| return size; |
| } |
| |
| static ssize_t modify_settings_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t size) |
| { |
| struct gp2ap_data *data = dev_get_drvdata(dev); |
| |
| if (data->ps_high_th == data->force_close_thd_high && data->ps_low_th == data->force_close_thd_low) { |
| SENSOR_INFO("Skip changing proximity settings (%d, %d)\n",data->ps_high_th,data->ps_low_th); |
| return size; |
| } |
| |
| if (sysfs_streq(buf, "1")) |
| data->prox_settings = 1; |
| else if (sysfs_streq(buf, "2")) |
| data->prox_settings = 2; |
| else { |
| SENSOR_ERR("invalid value %d\n", *buf); |
| return -EINVAL; |
| } |
| |
| SENSOR_INFO("prox_settings = %d\n", data->prox_settings); |
| |
| gp2ap_write_settings(data); |
| |
| return size; |
| } |
| |
| static ssize_t modify_settings_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct gp2ap_data *data = dev_get_drvdata(dev); |
| |
| gp2ap_read_settings(data); |
| |
| return snprintf(buf, PAGE_SIZE, "%d\n", data->prox_settings); |
| } |
| |
| static ssize_t settings_thd_high_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t size) |
| { |
| struct gp2ap_data *data = dev_get_drvdata(dev); |
| u16 value = 0; |
| int ret; |
| |
| ret = kstrtou16(buf, 10, &value); |
| if (ret < 0) { |
| SENSOR_ERR("kstrtoul failed, ret=0x%x\n", ret); |
| return ret; |
| } |
| |
| SENSOR_INFO("settings_thd_high: %d\n", value); |
| |
| data->settings_thd_high = value; |
| |
| return size; |
| } |
| |
| static ssize_t settings_thd_high_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct gp2ap_data *data = dev_get_drvdata(dev); |
| |
| return snprintf(buf, PAGE_SIZE, "%d\n", data->settings_thd_high); |
| } |
| |
| static ssize_t settings_thd_low_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t size) |
| { |
| struct gp2ap_data *data = dev_get_drvdata(dev); |
| u16 value = 0; |
| int ret; |
| |
| ret = kstrtou16(buf, 10, &value); |
| if (ret < 0) { |
| SENSOR_ERR("kstrtoul failed, ret=0x%x\n", ret); |
| return ret; |
| } |
| |
| SENSOR_INFO("settings_thd_low: %d\n", value); |
| |
| data->settings_thd_low = value; |
| |
| return size; |
| } |
| |
| static ssize_t settings_thd_low_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct gp2ap_data *data = dev_get_drvdata(dev); |
| |
| return snprintf(buf, PAGE_SIZE, "%d\n", data->settings_thd_low); |
| |
| } |
| |
| static ssize_t pre_test_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct gp2ap_data *data = dev_get_drvdata(dev); |
| |
| return snprintf(buf, PAGE_SIZE, "%d\n", data->pre_test); |
| } |
| |
| static ssize_t pre_test_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t size) |
| { |
| struct gp2ap_data *data = dev_get_drvdata(dev); |
| u16 value = 0; |
| int ret; |
| |
| ret = kstrtou16(buf, 10, &value); |
| if (ret < 0) { |
| SENSOR_ERR("kstrtoul failed, ret=0x%x\n", ret); |
| return ret; |
| } |
| |
| SENSOR_INFO("pre_test value = %d\n", value); |
| |
| data->pre_test = value; |
| |
| return size; |
| } |
| |
| |
| static void gp2ap_offset_work_func(struct work_struct *work) |
| { |
| struct gp2ap_data *data = container_of((struct delayed_work *)work, |
| struct gp2ap_data, offset_work); |
| |
| int ps_data; |
| u8 offset; |
| |
| if(!data->ps_enabled) return; |
| |
| ps_data = gp2ap_get_proximity_adc(data); |
| SENSOR_INFO("offset=%d\n", ps_data); |
| |
| if (ps_data > 0) |
| data->zero_detect = 0; |
| else if (ps_data == 0 && data->zero_detect == 0) { |
| SENSOR_INFO(" zero detection\n"); |
| data->zero_detect = 1; |
| mutex_lock(&data->mutex_ps_onoff); |
| disable_irq_wake(data->ps_irq); |
| disable_irq(data->ps_irq); |
| gp2ap_ps_onoff(0, data); |
| gp2ap_ps_onoff(1, data); |
| enable_irq_wake(data->ps_irq); |
| enable_irq(data->ps_irq); |
| mutex_unlock(&data->mutex_ps_onoff); |
| |
| gp2ap_i2c_read(REG_DYNAMIC_CAL_RESULT, &offset, sizeof(offset), data->client); |
| |
| if(!offset) { |
| gp2ap_i2c_write(0x8D, 0, data->client); |
| data->min_close_offset = MAX_OFFSET; |
| data->high_offset = 0; |
| SENSOR_INFO("offset = 0 , Restart sensor!"); |
| mutex_lock(&data->mutex_ps_onoff); |
| gp2ap_ps_onoff(0, data); |
| gp2ap_ps_onoff(1, data); |
| mutex_unlock(&data->mutex_ps_onoff); |
| } |
| } |
| gp2ap_update_min_close_offset(data); |
| schedule_delayed_work(&data->offset_work, msecs_to_jiffies(OFFSET_TUNE_DELAY)); |
| } |
| |
| static ssize_t proximity_state_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct gp2ap_data *data = dev_get_drvdata(dev); |
| int ps_data; |
| |
| ps_data = gp2ap_get_proximity_adc(data); |
| |
| return snprintf(buf, PAGE_SIZE, "%d\n", ps_data); |
| } |
| |
| static ssize_t proximity_trim_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| |
| { |
| struct gp2ap_data *data = dev_get_drvdata(dev); |
| u8 value; |
| |
| gp2ap_i2c_read(REG_DYNAMIC_CAL_RESULT, &value, sizeof(value), data->client); |
| |
| return snprintf(buf, PAGE_SIZE, "%d\n", value); |
| } |
| |
| static ssize_t dynamic_calib_enabled_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct input_dev *input_dev = to_input_dev(dev); |
| struct gp2ap_data *data = input_get_drvdata(input_dev); |
| |
| SENSOR_INFO("%s : %d\n", __func__, data->dynamic_calib_enabled); |
| |
| return sprintf(buf, "dynamic_calib_enabled:%d", data->dynamic_calib_enabled); |
| |
| } |
| |
| static ssize_t dynamic_calib_enabled_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t count) |
| { |
| struct gp2ap_data *data = dev_get_drvdata(dev); |
| char *endp; |
| int enabled = simple_strtoul(buf, &endp, 10); |
| |
| SENSOR_INFO("dynamic_calib_enabled = %s\n", buf); |
| |
| if (!(endp != buf && |
| (endp == (buf + strlen(buf)) |
| || (endp == (buf + strlen(buf) - 1) && *endp == '\n')))) { |
| SENSOR_INFO("invalid num : %s", buf); |
| return count; |
| } |
| |
| data->dynamic_calib_enabled = enabled; |
| |
| if (data->dynamic_calib_enabled) { |
| data->ps_low_th = data->calib_target + 205; |
| data->ps_high_th = data->calib_target + 305; |
| } else { |
| data->ps_low_th = 205; |
| data->ps_high_th = 305; |
| } |
| |
| return count; |
| } |
| |
| static DEVICE_ATTR(name, 0444, name_read, NULL); |
| static DEVICE_ATTR(vendor, 0444, vendor_read, NULL); |
| static DEVICE_ATTR(raw_data, 0444, proximity_state_show, NULL); |
| static DEVICE_ATTR(prox_register, 0644, proximity_register_read_show, proximity_register_write_store); |
| static DEVICE_ATTR(prox_trim, 0444, proximity_trim_show, NULL); |
| static DEVICE_ATTR(prox_cal, 0664, proximity_cal_show, proximity_cal_store); |
| static DEVICE_ATTR(thresh_high, S_IRUGO | S_IWUSR | S_IWGRP, |
| proximity_thresh_high_show, proximity_thresh_high_store); |
| static DEVICE_ATTR(thresh_low, S_IRUGO | S_IWUSR | S_IWGRP, |
| proximity_thresh_low_show, proximity_thresh_low_store); |
| static DEVICE_ATTR(dynamic_calib_enabled, 0664, |
| dynamic_calib_enabled_show, dynamic_calib_enabled_store); |
| static DEVICE_ATTR(prox_avg, 0644, |
| proximity_avg_show, proximity_avg_store); |
| static DEVICE_ATTR(modify_settings, 0664, modify_settings_show, modify_settings_store); |
| static DEVICE_ATTR(settings_thd_high, 0664, settings_thd_high_show, settings_thd_high_store); |
| static DEVICE_ATTR(settings_thd_low, 0664, settings_thd_low_show, settings_thd_low_store); |
| static DEVICE_ATTR(pre_test, 0664, pre_test_show, pre_test_store); |
| |
| static struct device_attribute *proximity_attrs[] = { |
| &dev_attr_name, |
| &dev_attr_vendor, |
| &dev_attr_raw_data, |
| &dev_attr_thresh_low, |
| &dev_attr_thresh_high, |
| &dev_attr_prox_cal, |
| &dev_attr_prox_register, |
| &dev_attr_prox_trim, |
| &dev_attr_dynamic_calib_enabled, |
| &dev_attr_prox_avg, |
| &dev_attr_modify_settings, |
| &dev_attr_settings_thd_high, |
| &dev_attr_settings_thd_low, |
| &dev_attr_pre_test, |
| NULL, |
| }; |
| |
| static enum hrtimer_restart gp2ap_prox_timer_func(struct hrtimer *timer) |
| { |
| struct gp2ap_data *data = container_of(timer, |
| struct gp2ap_data, prox_timer); |
| queue_work(data->prox_wq, &data->work_prox); |
| hrtimer_forward_now(&data->prox_timer, data->prox_poll_delay); |
| return HRTIMER_RESTART; |
| } |
| |
| static void proximity_get_avg_val(struct gp2ap_data *data) |
| { |
| int min = 0, max = 0, avg = 0; |
| int i; |
| int ps_data; |
| |
| for (i = 0; i < PROX_READ_NUM; i++) { |
| msleep(40); |
| ps_data = gp2ap_get_proximity_adc(data); |
| if(ps_data < 0) continue; |
| avg += ps_data; |
| if (!i) |
| min = ps_data; |
| else if (ps_data < min) |
| min = ps_data; |
| if (ps_data > max) |
| max = ps_data; |
| } |
| |
| avg /= PROX_READ_NUM; |
| data->avg[0] = min; |
| data->avg[1] = avg; |
| data->avg[2] = max; |
| } |
| |
| static void gp2ap_work_func_prox(struct work_struct *work) |
| { |
| struct gp2ap_data *data = container_of(work, |
| struct gp2ap_data, work_prox); |
| |
| proximity_get_avg_val(data); |
| } |
| |
| static int gp2ap110s_parse_dt(struct device *dev, struct gp2ap_data *data) |
| { |
| struct device_node *np = dev->of_node; |
| enum of_gpio_flags flags; |
| int ret; |
| |
| data->p_out = of_get_named_gpio_flags(np, "gp2ap110s,gpio_int", 0, &flags); |
| if (data->p_out < 0) { |
| SENSOR_ERR("Cannot set p_out through DTSI, ret=%d\n", data->p_out); |
| return -EINVAL; |
| } |
| |
| ret = of_property_read_u32(np, "gp2ap110s,led_reg_val_1", |
| &data->led_reg_val_1); |
| if (ret < 0) { |
| SENSOR_ERR("Cannot set led_reg_val_1 through DTSI, ret=%d\n", ret); |
| data->led_reg_val_1 = LED_REG_VAL_1; |
| } |
| |
| data->led_reg_val = data->led_reg_val_1; |
| |
| ret = of_property_read_u32(np, "gp2ap110s,ps_high_th_1", |
| &data->ps_high_th_1); |
| if (ret < 0) { |
| SENSOR_ERR("Cannot set ps_high_th_1 through DTSI, ret=%d\n", ret); |
| data->ps_high_th_1 = HIGH_THD_1; |
| } |
| |
| data->ps_high_th = data->ps_high_th_1; |
| |
| ret = of_property_read_u32(np, "gp2ap110s,ps_low_th_1", |
| &data->ps_low_th_1); |
| if (ret < 0) { |
| SENSOR_ERR("Cannot set ps_low_th_1 through DTSI, ret=%d\n", ret); |
| data->ps_low_th_1 = LOW_THD_1; |
| } |
| |
| data->ps_low_th = data->ps_low_th_1; |
| |
| ret = of_property_read_u32(np, "gp2ap110s,ps_high_th_2", |
| &data->ps_high_th_2); |
| if (ret < 0) { |
| SENSOR_ERR("Cannot set ps_high_th_2 through DTSI, ret=%d\n", ret); |
| data->ps_high_th_2 = HIGH_THD_2; |
| } |
| |
| ret = of_property_read_u32(np, "gp2ap110s,ps_low_th_2", |
| &data->ps_low_th_2); |
| if (ret < 0) { |
| SENSOR_ERR("Cannot set ps_low_th_2 through DTSI, ret=%d\n", ret); |
| data->ps_low_th_2 = LOW_THD_2; |
| } |
| |
| ret = of_property_read_u32(np, "gp2ap110s,led_reg_val_2", |
| &data->led_reg_val_2); |
| if (ret < 0) { |
| SENSOR_ERR("Cannot set led_reg_val_2 through DTSI, ret=%d\n", ret); |
| data->led_reg_val_2 = LED_REG_VAL_2; |
| } |
| |
| ret = of_property_read_u32(np, "gp2ap110s,settings_thd_low", |
| &data->settings_thd_low); |
| if (ret < 0) { |
| SENSOR_ERR("Cannot set settings_thd_low through DTSI, ret=%d\n", ret); |
| return -EINVAL; |
| } |
| |
| ret = of_property_read_u32(np, "gp2ap110s,settings_thd_high", |
| &data->settings_thd_high); |
| if (ret < 0) { |
| SENSOR_ERR("Cannot set settings_thd_high through DTSI, ret=%d\n", ret); |
| return -EINVAL; |
| } |
| |
| ret = of_property_read_u32(np, "gp2ap110s,force_close_thd_low", |
| &data->force_close_thd_low); |
| if (ret < 0) { |
| data->force_close_thd_low = FORCE_CLOSE_LOW_THD; |
| SENSOR_ERR("Cannot set through DTSI, use default force_close_thd_low = %d\n", data->force_close_thd_low); |
| } |
| |
| ret = of_property_read_u32(np, "gp2ap110s,force_close_thd_high", |
| &data->force_close_thd_high); |
| if (ret < 0) { |
| data->force_close_thd_high = FORCE_CLOSE_HIGH_THD; |
| SENSOR_ERR("Cannot set through DTSI, use default force_close_thd_high = %d\n", data->force_close_thd_high); |
| } |
| |
| return 0; |
| } |
| |
| static int gp2ap_i2c_probe(struct i2c_client *client, |
| const struct i2c_device_id *id) |
| { |
| struct gp2ap_data *gp2ap; |
| u8 value = 0; |
| int err = 0; |
| |
| SENSOR_INFO("gp2ap_probe start !! \n"); |
| |
| if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { |
| SENSOR_ERR("i2c functionality failed\n"); |
| return -ENOMEM; |
| } |
| |
| /* Allocate memory for driver data */ |
| gp2ap = kzalloc(sizeof(struct gp2ap_data), GFP_KERNEL); |
| if (!gp2ap) { |
| SENSOR_ERR("failed memory alloc\n"); |
| err = -ENOMEM; |
| goto err_mem_alloc; |
| } |
| |
| gp2ap->client = client; |
| i2c_set_clientdata(client, gp2ap); |
| |
| /* Check if the device is there or not. (Shutdown operation) */ |
| value = 0x00; |
| err = gp2ap_i2c_write(REG_COM1, value, gp2ap->client); |
| if (err < 0) { |
| SENSOR_ERR("gp2ap is not connected.(%d)\n", err); |
| goto err_no_device; |
| } |
| |
| err = gp2ap110s_parse_dt(&client->dev, gp2ap); |
| if (err) { |
| SENSOR_ERR("error in device tree"); |
| goto err_device_tree; |
| } |
| |
| mutex_init(&gp2ap->mutex_ps_onoff); |
| mutex_init(&gp2ap->mutex_enable); |
| mutex_init(&gp2ap->mutex_interrupt); |
| |
| gp2ap->ps_enabled = 0; |
| gp2ap->ps_distance = 1; |
| gp2ap->calib_target = 63; |
| gp2ap->dynamic_calib_enabled = 1; |
| gp2ap->prox_settings = 0; // keep settings value 0 at boot time |
| gp2ap->bytes = 14; // 4 bytes each for led_reg_val, thresholds & 1 byte each for "," |
| gp2ap->pre_test = 0; |
| gp2ap->zero_detect = 0; |
| gp2ap->high_offset = 0; |
| gp2ap->min_close_offset = MAX_OFFSET; |
| |
| value = 0x00; |
| gp2ap_i2c_write(0x8D, value, gp2ap->client); |
| |
| if (gp2ap->dynamic_calib_enabled) { |
| gp2ap_InitDataDynamicCalibration(gp2ap); |
| } else { |
| gp2ap_InitData(gp2ap); |
| gp2ap->ps_low_th = 205; |
| gp2ap->ps_high_th = 305; |
| } |
| |
| gp2ap->tune_adc_count = 0; |
| |
| err = ps_input_init(gp2ap); |
| |
| if (err < 0) { |
| SENSOR_ERR("failed to get input dev\n"); |
| goto err_ps_input_device; |
| } |
| |
| err = sensors_register(&gp2ap->dev, gp2ap, proximity_attrs, MODULE_NAME); |
| if (err < 0) { |
| SENSOR_INFO("could not sensors_register\n"); |
| goto err_sensors_register; |
| } |
| |
| /* For factory test mode, we use timer to get average proximity data. */ |
| /* prox_timer settings. we poll for light values using a timer. */ |
| hrtimer_init(&gp2ap->prox_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); |
| gp2ap->prox_poll_delay = ns_to_ktime(2000 * NSEC_PER_MSEC);/*2 sec*/ |
| gp2ap->prox_timer.function = gp2ap_prox_timer_func; |
| /* the timer just fires off a work queue request. we need a thread |
| to read the i2c (can be slow and blocking). */ |
| gp2ap->prox_wq = create_singlethread_workqueue("gp2a_prox_wq"); |
| if (!gp2ap->prox_wq) { |
| err = -ENOMEM; |
| SENSOR_ERR("could not create prox workqueue\n"); |
| goto err_create_prox_workqueue; |
| } |
| |
| INIT_WORK(&gp2ap->work_prox, gp2ap_work_func_prox); |
| INIT_WORK(&gp2ap->ps_int_work, gp2ap_ps_work_int_func); |
| INIT_DELAYED_WORK(&gp2ap->offset_work, gp2ap_offset_work_func); |
| |
| err = gp2ap_setup_irq(gp2ap); |
| |
| if (err) { |
| SENSOR_ERR("could not setup irq\n"); |
| goto err_setup_irq; |
| } |
| |
| SENSOR_INFO("success\n"); |
| |
| return 0; |
| err_setup_irq: |
| destroy_workqueue(gp2ap->prox_wq); |
| err_create_prox_workqueue: |
| sensors_unregister(gp2ap->dev, proximity_attrs); |
| err_sensors_register: |
| sensors_remove_symlink(&gp2ap->ps_input_dev->dev.kobj, |
| gp2ap->ps_input_dev->name); |
| sysfs_remove_group(&gp2ap->ps_input_dev->dev.kobj, |
| &ps_attribute_group); |
| input_unregister_device(gp2ap->ps_input_dev); |
| err_ps_input_device: |
| mutex_destroy(&gp2ap->mutex_interrupt); |
| mutex_destroy(&gp2ap->mutex_enable); |
| mutex_destroy(&gp2ap->mutex_ps_onoff); |
| err_device_tree: |
| err_no_device: |
| kfree(gp2ap); |
| err_mem_alloc: |
| SENSOR_ERR("failed\n"); |
| return err; |
| } |
| |
| static int gp2ap_remove(struct i2c_client *client) |
| { |
| struct gp2ap_data *data = i2c_get_clientdata(client); |
| u8 val; |
| |
| SENSOR_INFO("gp2ap_remove\n"); |
| |
| if (data != NULL) { |
| val = 0x00; |
| gp2ap_i2c_write(REG_COM1, val, data->client); |
| |
| if (data->ps_enabled) |
| disable_irq(data->ps_irq); |
| |
| free_irq(data->ps_irq, data); |
| destroy_workqueue(data->prox_wq); |
| sensors_unregister(data->dev, proximity_attrs); |
| sensors_remove_symlink(&data->ps_input_dev->dev.kobj, |
| data->ps_input_dev->name); |
| sysfs_remove_group(&data->ps_input_dev->dev.kobj, &ps_attribute_group); |
| input_unregister_device(data->ps_input_dev); |
| mutex_destroy(&data->mutex_ps_onoff); |
| mutex_destroy(&data->mutex_enable); |
| mutex_destroy(&data->mutex_interrupt); |
| kfree(data); |
| } |
| |
| return 0; |
| } |
| |
| static void gp2ap_shutdown(struct i2c_client *client) |
| { |
| struct gp2ap_data *data = i2c_get_clientdata(client); |
| u8 val; |
| |
| SENSOR_INFO("gp2ap_shutdown\n"); |
| |
| if (data != NULL) { |
| val = 0x00; |
| gp2ap_i2c_write(REG_COM1, val, data->client); |
| |
| if (data->ps_enabled) |
| disable_irq(data->ps_irq); |
| } |
| } |
| |
| static int gp2ap_suspend(struct device *pdev) |
| { |
| struct gp2ap_data *data = dev_get_drvdata(pdev); |
| |
| SENSOR_INFO("is called.\n"); |
| |
| if (data->ps_enabled) { |
| disable_irq(data->ps_irq); |
| cancel_delayed_work_sync(&data->offset_work); |
| } |
| |
| return 0; |
| } |
| |
| static int gp2ap_resume(struct device *pdev) |
| { |
| struct gp2ap_data *data = dev_get_drvdata(pdev); |
| |
| SENSOR_INFO("is called.\n"); |
| |
| if (data->ps_enabled) { |
| enable_irq(data->ps_irq); |
| schedule_delayed_work(&data->offset_work, msecs_to_jiffies(OFFSET_TUNE_DELAY)); |
| } |
| |
| return 0; |
| } |
| |
| static const struct dev_pm_ops gp2ap_pm_ops = { |
| .suspend = gp2ap_suspend, |
| .resume = gp2ap_resume |
| }; |
| |
| static const struct i2c_device_id gp2ap_device_id[] = |
| { |
| { "gp2a", 0 }, |
| { } |
| }; |
| |
| static struct of_device_id gp2ap_i2c_match_table[] = { |
| { .compatible = "sharp,gp2ap110s",}, |
| { }, |
| }; |
| |
| static struct i2c_driver gp2ap_i2c_driver = { |
| .driver = { |
| .name = "gp2a", |
| .owner = THIS_MODULE, |
| .of_match_table = gp2ap_i2c_match_table, |
| .pm = &gp2ap_pm_ops |
| }, |
| .probe = gp2ap_i2c_probe, |
| .shutdown = gp2ap_shutdown, |
| .remove = gp2ap_remove, |
| .id_table = gp2ap_device_id, |
| }; |
| |
| module_i2c_driver(gp2ap_i2c_driver); |
| |
| MODULE_AUTHOR("Samsung Electronics"); |
| MODULE_DESCRIPTION("Proximity Sensor driver for gp2ap110s00f"); |
| MODULE_LICENSE("GPL"); |