diff options
Diffstat (limited to 'drivers/input/sensor/kxte9_gsensor/kxte9.c')
-rwxr-xr-x | drivers/input/sensor/kxte9_gsensor/kxte9.c | 1798 |
1 files changed, 1798 insertions, 0 deletions
diff --git a/drivers/input/sensor/kxte9_gsensor/kxte9.c b/drivers/input/sensor/kxte9_gsensor/kxte9.c new file mode 100755 index 00000000..2f25a4f8 --- /dev/null +++ b/drivers/input/sensor/kxte9_gsensor/kxte9.c @@ -0,0 +1,1798 @@ +/* drivers/i2c/chips/kxte9.c - KXTE9 accelerometer driver + * + * Copyright (C) 2010 Kionix, Inc. + * Written by Kuching Tan <kuchingtan@kionix.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ +#include <linux/module.h> +#include <linux/err.h> +#include <linux/errno.h> +#include <linux/delay.h> +#include <linux/fs.h> +#include <linux/i2c.h> +#include <linux/input.h> +#include <linux/uaccess.h> +#include <linux/workqueue.h> +#include <linux/irq.h> +#include <linux/gpio.h> +#include <linux/interrupt.h> +#include <linux/slab.h> +//#include <linux/kxte9.h> +#include <linux/platform_device.h> +#include <linux/miscdevice.h> +#include <mach/hardware.h> +#include "kxte9.h" +//#include <linux/earlysuspend.h> +#include <mach/wmt-i2c-bus.h> + +#define NAME "kxte9" +#define G_MAX 2000 +/* OUTPUT REGISTERS */ +#define CT_RESP 0x0C +#define WHO_AM_I 0x0F +#define TILT_POS_CUR 0x10 +#define TILT_POS_PRE 0x11 +#define XOUT 0x12 +#define INT_STATUS_REG 0x16 +#define INT_SRC_REG2 0x17 +#define INT_REL 0x1A +/* CONTROL REGISTERS */ +#define CTRL_REG1 0x1B +#define CTRL_REG2 0x1C +#define CTRL_REG3 0x1D +#define INT_CTRL1 0x1E +#define INT_CTRL2 0x1F +#define TILT_TIMER 0x28 +#define WUF_TIMER 0x29 +#define B2S_TIMER 0x2A +#define WUF_THRESH 0x5A +#define B2S_THRESH 0x5B +/* CTRL_REG1 BITS */ +#define PC1_OFF 0x00 +#define PC1_ON 0x80 +/* INT_SRC_REG2 BITS */ +#define TPS 0x01 +#define WUFS 0x02 +#define B2SS 0x04 +/* Direction Mask */ +/* Used for TILT_POS_CUR, TILT_POS_PRE */ +/* INT_SRC_REG1, CTRL_REG2 */ +#define DIR_LE 0x20 +#define DIR_RI 0x10 +#define DIR_DO 0x08 +#define DIR_UP 0x04 +#define DIR_FD 0x02 +#define DIR_FU 0x01 +/* ODR MASKS */ +#define ODRM 0x18 // CTRL_REG1 +#define OWUFM 0x03 // CTRL_REG3 +#define OB2SM 0x0C // CTRL_REG3 +/* INPUT_ABS CONSTANTS */ +#define FUZZ 32 +#define FLAT 32 +/* RESUME STATE INDICES */ +#define RES_CTRL_REG1 0 +#define RES_CTRL_REG3 1 +#define RES_INT_CTRL1 2 +#define RES_TILT_TIMER 3 +#define RES_WUF_TIMER 4 +#define RES_B2S_TIMER 5 +#define RES_WUF_THRESH 6 +#define RES_B2S_THRESH 7 +#define RES_CURRENT_ODR 8 +#define RESUME_ENTRIES 9 +/* OFFSET and SENSITIVITY */ +//#define OFFSET 32 //6-bit +#define OFFSET 128 //8-bit +#define SENS 16 + +#define IOCTL_BUFFER_SIZE 64 + +//#define WM3445_A0 +//#define INT_MODE + +////////////////////////////////////////////////////////////////////////// +//#define DEBUG_WMT_GSENSOR +#ifdef DEBUG_WMT_GSENSOR +#define kxte9_dbg(fmt, args...) printk(KERN_ALERT "[%s]: " fmt, __FUNCTION__ , ## args) +//#define kxte9_dbg(fmt, args...) if (kpadall_isrundbg()) printk(KERN_ALERT "[%s]: " fmt, __FUNCTION__, ## args) +#else +#define kxte9_dbg(fmt, args...) +#endif + +#undef errlog +#undef klog +#define errlog(fmt, args...) printk(KERN_ERR "[%s]: " fmt, __FUNCTION__, ## args) +#define klog(fmt, args...) printk(KERN_DEBUG "[%s]: " fmt, __FUNCTION__, ## args) +////////////////////////////////////////////////////////////////////////// + +/* + * The following table lists the maximum appropriate poll interval for each + * available output data rate. + */ +struct { + unsigned int interval; + u8 mask; +} kxte9_odr_table[] = { + {1000, ODR1E}, + {334, ODR3E}, + {100, ODR10E}, + {25, ODR40E}, + {8, ODR125E}, +}; + +struct kxte9_data { + //struct i2c_client *client; + struct kxte9_platform_data *pdata; + struct mutex lock; + struct delayed_work input_work; + struct input_dev *input_dev; +#ifdef INT_MODE + struct work_struct irq_work; +#endif +#ifdef CONFIG_HAS_EARLYSUSPEND + struct early_suspend earlysuspend; +#endif + int hw_initialized; + atomic_t enabled; + u8 resume[RESUME_ENTRIES]; + int i2c_xfer_complete; + int suspend; +}; + +struct gsensor_config +{ + int op; + int samp; + int xyz_axis[3][3]; // (axis,direction) + struct proc_dir_entry* sensor_proc; + int sensorlevel; + unsigned int avg_count; + unsigned int kxte9_8bit; + int name; + int bmp; + unsigned int ctraddr; + unsigned int ocaddr; + unsigned int idaddr; + unsigned int peaddr; + unsigned int pcaddr; + unsigned int itbmp; + unsigned int itaddr; + unsigned int isbmp; + unsigned int isaddr; + int irq; +}; + +static struct gsensor_config gconf = { + .op = 0, + .samp = 40, + .xyz_axis = { + {ABS_X, -1}, + {ABS_Y, 1}, + {ABS_Z, -1}, + }, + .sensor_proc = NULL, + .avg_count = 4, + .kxte9_8bit = 1, + .name = 3, +#ifdef WM3445_A0 + .bmp = 0x100, /* GPIO 8 */ + .ctraddr = GPIO_BASE_ADDR + 0x40, + .ocaddr = GPIO_BASE_ADDR + 0x80, + .idaddr = GPIO_BASE_ADDR + 0x00, + .peaddr = GPIO_BASE_ADDR + 0x480, + .pcaddr = GPIO_BASE_ADDR + 0x4c0, + .itbmp = 0x30000, /* Rising Edge */ + .itaddr = GPIO_BASE_ADDR + 0x300, + .isbmp = 0x100, + .isaddr = GPIO_BASE_ADDR + 0x304, + .irq = IRQ_GPIO8, +#else + .bmp = 0x8, /* GPIO 3 */ + .ctraddr = GPIO_BASE_ADDR + 0x40, + .ocaddr = GPIO_BASE_ADDR + 0x80, + .idaddr = GPIO_BASE_ADDR + 0x00, + .peaddr = GPIO_BASE_ADDR + 0x480, + .pcaddr = GPIO_BASE_ADDR + 0x4c0, + .itbmp = 0x83000000, /* Rising Edge */ + .itaddr = GPIO_BASE_ADDR + 0x300, + .isbmp = 0x8, + .isaddr = GPIO_BASE_ADDR + 0x320, + .irq = 5, +#endif +}; + +static struct kxte9_platform_data kxte9_pdata = { + .min_interval = 1, + .poll_interval = 100, + .ctrl_reg1_init = ODR10E & ~B2SE & ~WUFE & ~TPE, + .engine_odr_init = OB2S1 | OWUF1, + .int_ctrl_init = KXTE9_IEA, + .tilt_timer_init = 0x00, + .wuf_timer_init = 0x00, + .wuf_thresh_init = 0x20, + .b2s_timer_init = 0x00, + .b2s_thresh_init = 0x60, +}; + +#ifdef WM3445_A0 +#define SET_GPIO_GSENSOR_INT() {\ + REG32_VAL(gconf.ctraddr) &= ~gconf.bmp; \ + REG32_VAL(gconf.ocaddr) &= ~gconf.bmp; \ + REG32_VAL(gconf.peaddr) |= gconf.bmp; \ + REG32_VAL(gconf.pcaddr) &= ~gconf.bmp; \ + REG32_VAL(gconf.itaddr) |= gconf.itbmp; \ + REG32_VAL(GPIO_BASE_ADDR + 0x308) |= gconf.bmp; \ + REG32_VAL(gconf.isaddr) |= gconf.isbmp; \ +} +#define ENABLE_SENSOR_INT(enable) { \ + if (enable) \ + {\ + REG32_VAL(GPIO_BASE_ADDR + 0x308) &= ~gconf.bmp; \ + } else {\ + REG32_VAL(GPIO_BASE_ADDR + 0x308) |= gconf.bmp; \ + }\ +} + +#else +#define SET_GPIO_GSENSOR_INT() {\ + REG32_VAL(gconf.ctraddr) |= gconf.bmp; \ + REG32_VAL(gconf.ocaddr) &= ~gconf.bmp; \ + REG32_VAL(gconf.pcaddr) &= ~gconf.bmp; \ + REG32_VAL(gconf.itaddr) |= gconf.itbmp; \ + REG32_VAL(gconf.isaddr) |= gconf.isbmp; \ +} +#endif + +#define X_CONVERT(x) x*gconf.xyz_axis[ABS_X][1] +#define Y_CONVERT(y) y*gconf.xyz_axis[ABS_Y][1] +#define Z_CONVERT(z) z*gconf.xyz_axis[ABS_Z][1] + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void kxte9_early_suspend(struct early_suspend *h); +static void kxte9_late_resume(struct early_suspend *h); +#endif + +extern int wmt_getsyspara(char *varname, unsigned char *varval, int *varlen); +//extern int wmt_i2c_xfer_continue_if_4(struct i2c_msg *msg, unsigned int num, int bus_id); +extern int i2c_api_do_send(int bus_id, char chip_addr, char sub_addr, char *buf, unsigned int size); +extern int i2c_api_do_recv(int bus_id, char chip_addr, char sub_addr, char *buf, unsigned int size); +extern unsigned int wmt_read_oscr(void); + +static struct kxte9_data *te9 = NULL; +static atomic_t kxte9_dev_open_count; +static struct kobject *android_gsensor_kobj = NULL; +static void kxte9_read_callback(void *data); +struct i2c_msg *kxte9_msg; +unsigned char *i2c_read_buf; +unsigned char *i2c_write_buf; +static struct timer_list kxte9_timer; +static unsigned char *x_count; +static unsigned char *y_count; +static unsigned char *z_count; +static unsigned int x_total = 0, y_total = 0, z_total = 0; +static unsigned int xyz_index = 0; + +static int wait_i2c_xfer_complete(void) +{ + unsigned int now_time = 0; + unsigned int delay_time = 0; + + now_time = wmt_read_oscr(); + while (!te9->i2c_xfer_complete) { + delay_time = wmt_read_oscr() - now_time; + if (delay_time > 60000) {//20ms + printk(KERN_WARNING "[kxte9] transfer timeout!\n"); + return 0; + } + } + return 1; +} + +static void kxte9_read_callback(void *data) +{ + int xyz[3]; + + if (te9->suspend) { + te9->i2c_xfer_complete = 1; + return; + } + if (xyz_index >= gconf.avg_count) + xyz_index = 0; + x_total -= x_count[xyz_index]; + y_total -= y_count[xyz_index]; + z_total -= z_count[xyz_index]; + if (gconf.kxte9_8bit) { + x_count[xyz_index] = i2c_read_buf[0]; + y_count[xyz_index] = i2c_read_buf[1]; + z_count[xyz_index] = i2c_read_buf[2]; + } else { + x_count[xyz_index] = i2c_read_buf[0] & ~0x03; + y_count[xyz_index] = i2c_read_buf[1] & ~0x03; + z_count[xyz_index] = i2c_read_buf[2] & ~0x03; + } + x_total += x_count[xyz_index]; + y_total += y_count[xyz_index]; + z_total += z_count[xyz_index]; + xyz[ABS_X] = (x_total/gconf.avg_count - OFFSET) << 4; + xyz[ABS_Y] = (y_total/gconf.avg_count - OFFSET) << 4; + xyz[ABS_Z] = (z_total/gconf.avg_count - OFFSET) << 4; + //printk(KERN_DEBUG " [%d] x:%d y:%d z:%d\n", xyz_index, x_count[xyz_index], y_count[xyz_index], z_count[xyz_index]); + //printk(KERN_DEBUG " total x:%d y:%d z:%d\n", x_total, y_total, z_total); + //printk(KERN_DEBUG " avg x:%d y:%d z:%d\n", x_total/gconf.avg_count, y_total/gconf.avg_count, z_total/gconf.avg_count); + //printk(KERN_DEBUG "report x:%d y:%d z:%d\n", xyz[ABS_X], xyz[ABS_Y], xyz[ABS_Z]); + xyz_index++; + input_report_abs(te9->input_dev, ABS_X, X_CONVERT(xyz[gconf.xyz_axis[ABS_X][0]])); + input_report_abs(te9->input_dev, ABS_Y, Y_CONVERT(xyz[gconf.xyz_axis[ABS_Y][0]])); + input_report_abs(te9->input_dev, ABS_Z, Z_CONVERT(xyz[gconf.xyz_axis[ABS_Z][0]])); + input_sync(te9->input_dev); + te9->i2c_xfer_complete = 1; + mod_timer(&kxte9_timer, jiffies + msecs_to_jiffies(te9->pdata->poll_interval)); +} + +static void kxte9_read_data(u8 addr, int len) +{ + i2c_write_buf[0] = addr; + kxte9_msg[0].addr = KXTE9_I2C_ADDR; + kxte9_msg[0].flags = 0 ; + kxte9_msg[0].len = 1; + kxte9_msg[0].buf = i2c_write_buf; + kxte9_msg[1].addr = KXTE9_I2C_ADDR; + kxte9_msg[1].flags = I2C_M_RD; + kxte9_msg[1].len = len; + kxte9_msg[1].buf = i2c_read_buf; + wmt_i2c_transfer(kxte9_msg, 2, 0, kxte9_read_callback, 0); +} + +static int kxte9_i2c_read(u8 addr, u8 *data, int len) +{ +/* + int err; + + struct i2c_msg msgs[] = { + { + .addr = KXTE9_I2C_ADDR, + .flags = 0 & ~(I2C_M_RD), //te9->client->flags & I2C_M_TEN, + .len = 1, + .buf = &addr, + }, + { + .addr = KXTE9_I2C_ADDR, //te9->client->addr, + .flags = (I2C_M_RD), //(te9->client->flags & I2C_M_TEN) | I2C_M_RD, + .len = len, + .buf = data, + }, + }; + err = wmt_i2c_xfer_continue_if_4(msgs, 2, 0); + + if(err != 2) + errlog("read transfer error\n"); + else + err = 0; + + return err; +*/ + int ret; + ret = i2c_api_do_recv(0, KXTE9_I2C_ADDR, addr, data, len); + if (ret <= 0) { + errlog("i2c_api_do_recv error!\n"); + return -1; + } + return 0; +} + +static int kxte9_i2c_write(u8 addr, u8 *data, int len) +{ +/* + int err; + int i; + u8 buf[len + 1]; + + struct i2c_msg msgs[] = { + { + .addr = KXTE9_I2C_ADDR, //te9->client->addr, + .flags = 0 & ~(I2C_M_RD), //te9->client->flags & I2C_M_TEN, + .len = len + 1, + .buf = buf, + }, + }; + + buf[0] = addr; + for (i = 0; i < len; i++) + buf[i + 1] = data[i]; + + err = wmt_i2c_xfer_continue_if_4(msgs, 1, 0); + if(err != 1) + errlog("write transfer error\n"); + else + err = 0; + return err; +*/ + int ret; + ret = i2c_api_do_send(0, KXTE9_I2C_ADDR, addr, data, len); + if (ret <= 0) { + errlog("i2c_api_do_send error!\n"); + return -1; + } + return 0; + +} + +int kxte9_get_bits(u8 reg_addr, u8* bits_value, u8 bits_mask) +{ + int err; + u8 reg_data; + + err = kxte9_i2c_read(reg_addr, ®_data, 1); + kxte9_dbg("kxte9_i2c_read(%x, 1)=%x, err=%d\n", reg_addr, reg_data, err); + if(err < 0) + return err; + + *bits_value = reg_data & bits_mask; + + return 1; +} + +int kxte9_get_byte(u8 reg_addr, u8* reg_value) +{ + int err; + u8 reg_data; + + err = kxte9_i2c_read(reg_addr, ®_data, 1); + kxte9_dbg("kxte9_i2c_read(%x, 1)=%x, err=%d\n", reg_addr, reg_data, err); + if(err < 0) + return err; + + *reg_value = reg_data; + + return 1; +} + +int kxte9_set_bits(int res_index, u8 reg_addr, u8 bits_value, u8 bits_mask) +{ + int err=0, err1=0, retval=0; + u8 reg_data = 0x00, reg_bits = 0x00, bits_set = 0x00; + + // Turn off PC1 + reg_data = te9->resume[RES_CTRL_REG1] & ~PC1_ON; + + err = kxte9_i2c_write(CTRL_REG1, ®_data, 1); + kxte9_dbg("kxte9_i2c_write(%x, %x, 1), err=%d\n", CTRL_REG1, reg_data, err); + if(err < 0) + goto exit0; + + // Read from device register + err = kxte9_i2c_read(reg_addr, ®_data, 1); + kxte9_dbg("kxte9_i2c_read(%x, 1)=%x, err=%d\n", reg_addr, reg_data, err); + if(err < 0) + goto exit0; + + // Apply mask to device register; + reg_bits = reg_data & bits_mask; + + // Update resume state data + bits_set = bits_mask & bits_value; + te9->resume[res_index] &= ~bits_mask; + te9->resume[res_index] |= bits_set; + + // Return 0 if value in device register and value to be written is the same + if(reg_bits == bits_set) + retval = 0; + // Else, return 1 + else + retval = 1; + + // Write to device register + err = kxte9_i2c_write(reg_addr, &te9->resume[res_index], 1); + kxte9_dbg("kxte9_i2c_write(%x, %x, 1), err=%d\n", reg_addr, te9->resume[res_index], err); + if(err < 0) + goto exit0; + +exit0: + // Turn on PC1 + reg_data = te9->resume[RES_CTRL_REG1] | PC1_ON; + + err1 = kxte9_i2c_write(CTRL_REG1, ®_data, 1); + kxte9_dbg("kxte9_i2c_write(%x, %x, 1), err=%d\n", CTRL_REG1, reg_data, err); + if(err1 < 0) + return err1; + + if(err < 0) + return err; + + return retval; +} + +int kxte9_set_byte(int res_index, u8 reg_addr, u8 reg_value) +{ + int err, err1, retval=0; + u8 reg_data; + + // Turn off PC1 + reg_data = te9->resume[RES_CTRL_REG1] & ~PC1_ON; + + err = kxte9_i2c_write(CTRL_REG1, ®_data, 1); + kxte9_dbg("kxte9_i2c_write(%x, %x, 1), err=%d\n", CTRL_REG1, reg_data, err); + if(err < 0) + goto exit0; + + // Read from device register + err = kxte9_i2c_read(reg_addr, ®_data, 1); + kxte9_dbg("kxte9_i2c_read(%x, 1)=%x, err=%d\n", reg_addr, reg_data, err); + if(err < 0) + goto exit0; + + // Update resume state data + te9->resume[res_index] = reg_value; + + // Return 0 if value in device register and value to be written is the same + if(reg_data == reg_value) + retval = 0; + // Else, return 1 + else + retval = 1; + + // Write to device register + err = kxte9_i2c_write(reg_addr, &te9->resume[res_index], 1); + kxte9_dbg("kxte9_i2c_write(%x, %x, 1), err=%d\n", reg_addr, te9->resume[res_index], err); + if(err < 0) + goto exit0; + +exit0: + // Turn on PC1 + reg_data = te9->resume[RES_CTRL_REG1] | PC1_ON; + err1 = kxte9_i2c_write(CTRL_REG1, ®_data, 1); + kxte9_dbg("kxte9_i2c_write(%x, %x, 1), err=%d\n", CTRL_REG1, reg_data, err); + if(err1 < 0) + return err1; + + if(err < 0) + return err; + + return retval; +} + +int kxte9_set_pc1_off(void) +{ + u8 reg_data; + + reg_data = te9->resume[RES_CTRL_REG1] & ~PC1_ON; + + kxte9_dbg("kxte9_i2c_write(%x, %x, 1)\n", CTRL_REG1, reg_data); + return kxte9_i2c_write(CTRL_REG1, ®_data, 1); +} + +int kxte9_set_pc1_on(void) +{ + u8 reg_data; + + reg_data = te9->resume[RES_CTRL_REG1] | PC1_ON; + + kxte9_dbg("kxte9_i2c_write(%x, %x, 1)\n", CTRL_REG1, reg_data); + return kxte9_i2c_write(CTRL_REG1, ®_data, 1); +} + +static int kxte9_verify(void) +{ + int err; + u8 buf; + + err = kxte9_i2c_read(WHO_AM_I, &buf, 1); + kxte9_dbg("kxte9_i2c_read(%x, 1)=%x, err=%d\n", WHO_AM_I, buf, err); + if(err < 0) + errlog( "read err int source\n"); + if(buf != 0) + err = -1; + return err; +} + +static int kxte9_hw_init(void) +{ + int err; + u8 buf = PC1_OFF; + + err = kxte9_i2c_write(CTRL_REG1, &buf, 1); + kxte9_dbg("kxte9_i2c_write(%x, %x, 1), err=%d\n", CTRL_REG1, buf, err); + if(err < 0) + return err; + err = kxte9_i2c_write(CTRL_REG3, &te9->resume[RES_CTRL_REG3], 1); + kxte9_dbg("kxte9_i2c_write(%x, %x, 1), err=%d\n", CTRL_REG3, te9->resume[RES_CTRL_REG3], err); + if(err < 0) + return err; + err = kxte9_i2c_write(INT_CTRL1, &te9->resume[RES_INT_CTRL1], 1); + kxte9_dbg("kxte9_i2c_write(%x, %x, 1), err=%d\n", INT_CTRL1, te9->resume[RES_INT_CTRL1], err); + if(err < 0) + return err; + err = kxte9_i2c_write(TILT_TIMER, &te9->resume[RES_TILT_TIMER], 1); + kxte9_dbg("kxte9_i2c_write(%x, %x, 1), err=%d\n", TILT_TIMER, te9->resume[RES_TILT_TIMER], err); + if(err < 0) + return err; + err = kxte9_i2c_write(WUF_TIMER, &te9->resume[RES_WUF_TIMER], 1); + kxte9_dbg("kxte9_i2c_write(%x, %x, 1), err=%d\n", WUF_TIMER, te9->resume[RES_WUF_TIMER], err); + if(err < 0) + return err; + err = kxte9_i2c_write(B2S_TIMER, &te9->resume[RES_B2S_TIMER], 1); + kxte9_dbg("kxte9_i2c_write(%x, %x, 1), err=%d\n", B2S_TIMER, te9->resume[RES_B2S_TIMER], err); + if(err < 0) + return err; + err = kxte9_i2c_write(WUF_THRESH, &te9->resume[RES_WUF_THRESH], 1); + kxte9_dbg("kxte9_i2c_write(%x, %x, 1), err=%d\n", WUF_THRESH, te9->resume[RES_WUF_THRESH], err); + if(err < 0) + return err; + err = kxte9_i2c_write(B2S_THRESH, &te9->resume[RES_B2S_THRESH], 1); + kxte9_dbg("kxte9_i2c_write(%x, %x, 1), err=%d\n", B2S_THRESH, te9->resume[RES_B2S_THRESH], err); + if(err < 0) + return err; + buf = te9->resume[RES_CTRL_REG1] | PC1_ON; + err = kxte9_i2c_write(CTRL_REG1, &buf, 1); + kxte9_dbg("kxte9_i2c_write(%x, %x, 1), err=%d\n", CTRL_REG1, buf, err); + if(err < 0) + return err; + + te9->resume[RES_CTRL_REG1] = buf; + te9->hw_initialized = 1; + + return 0; +} + +static void kxte9_device_power_off(void) +{ + int err; + u8 buf = PC1_OFF; + + err = kxte9_i2c_write(CTRL_REG1, &buf, 1); + kxte9_dbg("kxte9_i2c_write(%x, %x, 1), err=%d\n", CTRL_REG1, buf, err); + if(err < 0) + errlog("soft power off failed\n"); +#ifdef INT_MODE + ENABLE_SENSOR_INT(0); +// disable_irq(gconf.irq); +#endif + te9->hw_initialized = 0; +} + +static int kxte9_device_power_on(void) +{ + int err; + + if(!te9->hw_initialized) { + //mdelay(110); + err = kxte9_hw_init(); + if(err < 0) { + kxte9_device_power_off(); + return err; + } + } +#ifdef INT_MODE + ENABLE_SENSOR_INT(1); +// enable_irq(gconf.irq); +#endif + return 0; +} + +static u8 kxte9_resolve_dir(u8 dir) +{ + switch (dir) { + case 0x20: /* -X */ + if (gconf.xyz_axis[ABS_X][1] < 0) + dir = 0x10; + if (gconf.xyz_axis[ABS_Y][0] == 0) + dir >>= 2; + if (gconf.xyz_axis[ABS_Z][0] == 0) + dir >>= 4; + break; + case 0x10: /* +X */ + if (gconf.xyz_axis[ABS_X][1] < 0) + dir = 0x20; + if (gconf.xyz_axis[ABS_Y][0] == 0) + dir >>= 2; + if (gconf.xyz_axis[ABS_Z][0] == 0) + dir >>= 4; + break; + case 0x08: /* -Y */ + if (gconf.xyz_axis[ABS_Y][1] < 0) + dir = 0x04; + if (gconf.xyz_axis[ABS_X][0] == 1) + dir <<= 2; + if (gconf.xyz_axis[ABS_Z][0] == 1) + dir >>= 2; + break; + case 0x04: /* +Y */ + if (gconf.xyz_axis[ABS_Y][1] < 0) + dir = 0x08; + if (gconf.xyz_axis[ABS_X][0] == 1) + dir <<= 2; + if (gconf.xyz_axis[ABS_Z][0] == 1) + dir >>= 2; + break; + case 0x02: /* -Z */ + if (gconf.xyz_axis[ABS_Z][1] < 0) + dir = 0x01; + if (gconf.xyz_axis[ABS_X][0] == 2) + dir <<= 4; + if (gconf.xyz_axis[ABS_Y][0] == 2) + dir <<= 2; + break; + case 0x01: /* +Z */ + if (gconf.xyz_axis[ABS_Z][1] < 0) + dir = 0x02; + if (gconf.xyz_axis[ABS_X][0] == 2) + dir <<= 4; + if (gconf.xyz_axis[ABS_Y][0] == 2) + dir <<= 2; + break; + default: + return -EINVAL; + } + + return dir; +} + +#ifdef INT_MODE +static void kxte9_irq_work_func(struct work_struct *work) +{ +/* + * int_status output: + * [INT_SRC_REG1][INT_SRC_REG2][TILT_POS_PRE][TILT_POS_CUR] + * INT_SRC_REG2, TILT_POS_PRE, and TILT_POS_CUR directions are translated + * based on platform data variables. + */ + + int err; + int i; + int int_status = 0; + u8 status; + u8 b2s_comp; + u8 wuf_comp; + u8 buf[2]; + + err = kxte9_i2c_read(INT_STATUS_REG, &status, 1); + kxte9_dbg("kxte9_i2c_read(%x, 1)=%x, err=%d\n", INT_STATUS_REG, status, err); + if(err < 0) + errlog("read err int source\n"); + int_status = status << 24; + if((status & TPS) > 0) { + err = kxte9_i2c_read(TILT_POS_CUR, buf, 2); + kxte9_dbg("kxte9_i2c_read(%x, 2)=%x,%x, err=%d\n", TILT_POS_CUR, buf[0], buf[1], err); + if(err < 0) + errlog("read err tilt dir\n"); + int_status |= kxte9_resolve_dir(buf[0]); + int_status |= (kxte9_resolve_dir(buf[1])) << 8; + kxte9_dbg("IRQ TILT [%x]\n", kxte9_resolve_dir(buf[0])); + } + if((status & WUFS) > 0) { + kxte9_dbg("for WUFS\n"); + err = kxte9_i2c_read(INT_SRC_REG2, buf, 1); + kxte9_dbg("kxte9_i2c_read(%x, 1)=%x, err=%d\n", INT_SRC_REG2, buf[0], err); + if(err < 0) + kxte9_dbg("reading err wuf dir\n"); + int_status |= (kxte9_resolve_dir(buf[0])) << 16; + b2s_comp = (te9->resume[RES_CTRL_REG3] & 0x0C) >> 2; + wuf_comp = te9->resume[RES_CTRL_REG3] & 0x03; + if(!te9->resume[RES_CURRENT_ODR] && + !(te9->resume[RES_CTRL_REG1] & ODR125E) && + !(b2s_comp & wuf_comp)) { + /* set the new poll interval based on wuf odr */ + for (i = 0; i < ARRAY_SIZE(kxte9_odr_table); i++) { + if(kxte9_odr_table[i].mask == wuf_comp << 3) { + te9->pdata->poll_interval = kxte9_odr_table[i].interval; + break; + } + } + if(te9->input_dev) { + cancel_delayed_work_sync(&te9->input_work); + schedule_delayed_work(&te9->input_work, + msecs_to_jiffies(te9->pdata->poll_interval)); + } + } + } + if((status & B2SS) > 0) { + kxte9_dbg("fro B2SS\n"); + b2s_comp = (te9->resume[RES_CTRL_REG3] & 0x0C) >> 2; + wuf_comp = te9->resume[RES_CTRL_REG3] & 0x03; + if(!te9->resume[RES_CURRENT_ODR] && + !(te9->resume[RES_CTRL_REG1] & ODR125E) && + !(b2s_comp & wuf_comp)) { + /* set the new poll interval based on b2s odr */ + for (i = 1; i < ARRAY_SIZE(kxte9_odr_table); i++) { + if(kxte9_odr_table[i].mask == b2s_comp << 3) { + te9->pdata->poll_interval = kxte9_odr_table[i].interval; + break; + } + } + if(te9->input_dev) { + cancel_delayed_work_sync(&te9->input_work); + schedule_delayed_work(&te9->input_work, + msecs_to_jiffies(te9->pdata->poll_interval)); + } + } + } + input_report_abs(te9->input_dev, ABS_MISC, int_status); + input_sync(te9->input_dev); + err = kxte9_i2c_read(INT_REL, buf, 1); + kxte9_dbg("kxte9_i2c_read(%x, 1)=%x, err=%d\n", INT_REL, buf[0], err); + if(err < 0) + errlog("error clearing interrupt\n"); + //enable_irq(gconf.irq); +} + +static irqreturn_t kxte9_isr(int irq, void *dev) +{ + unsigned int status = REG32_VAL(gconf.isaddr); + + // clr int status ...?? + if ((status & gconf.isbmp) != 0) + { + kxte9_dbg("\n"); + REG32_VAL(gconf.isaddr) |= gconf.isbmp; + udelay(5); + // disable int and satrt irq work + disable_irq_nosync(irq); + schedule_work(&te9->irq_work); + return IRQ_HANDLED; + } + return IRQ_NONE; +} +#endif + +static int kxte9_update_odr(int poll_interval) +{ + int err = -1; + int i; + u8 config; + + /* Convert the poll interval into an output data rate configuration + * that is as low as possible. The ordering of these checks must be + * maintained due to the cascading cut off values - poll intervals are + * checked from shortest to longest. At each check, if the next lower + * ODR cannot support the current poll interval, we stop searching */ + for (i = 0; i < ARRAY_SIZE(kxte9_odr_table); i++) { + config = kxte9_odr_table[i].mask; + if(poll_interval >= kxte9_odr_table[i].interval) + break; + } + + config |= (te9->resume[RES_CTRL_REG1] & ~(ODR40E | ODR125E)); + te9->resume[RES_CTRL_REG1] = config; + if(atomic_read(&te9->enabled)) { + err = kxte9_set_byte(RES_CTRL_REG1, CTRL_REG1, config); + if(err < 0) + return err; + } + klog("Set new ODR to 0x%02X\n", config); + return 0; +} + +static void kxte9_input_work_func(unsigned long unused) +{ + //mutex_lock(&te9->lock); + if (te9->suspend) + return; + if (te9->i2c_xfer_complete) { + te9->i2c_xfer_complete = 0; + kxte9_read_data(XOUT, 3); + } + //mutex_unlock(&te9->lock); +} + +static int kxte9_enable(void) +{ + int err; + int int_status = 0; + u8 buf; + + if(!atomic_cmpxchg(&te9->enabled, 0, 1)) { + err = kxte9_device_power_on(); + err = kxte9_i2c_read(INT_REL, &buf, 1); + kxte9_dbg("kxte9_i2c_read(%x, 1)=%x, err=%d\n", INT_REL, buf, err); + if(err < 0) { + errlog("error clearing interrupt: %d\n", err); + atomic_set(&te9->enabled, 0); + return err; + } + if((te9->resume[RES_CTRL_REG1] & TPS) > 0) { + err = kxte9_i2c_read(TILT_POS_CUR, &buf, 1); + kxte9_dbg("kxte9_i2c_read(%x, 1)=%x, err=%d\n", TILT_POS_CUR, buf, err); + if(err < 0) + errlog("kxte9 error reading current tilt\n"); + int_status |= kxte9_resolve_dir(buf); + input_report_abs(te9->input_dev, ABS_MISC, int_status); + input_sync(te9->input_dev); + } + kxte9_msg = kzalloc(2*sizeof(struct i2c_msg), GFP_ATOMIC); + i2c_read_buf = kzalloc(3*sizeof(unsigned char), GFP_ATOMIC); + i2c_write_buf = kzalloc(sizeof(char), GFP_ATOMIC); + setup_timer(&kxte9_timer, kxte9_input_work_func, 0); + mod_timer(&kxte9_timer, jiffies + msecs_to_jiffies(te9->pdata->poll_interval)); + kxte9_dbg("Enabled\n"); + } + return 0; +} + +static int kxte9_disable(void) +{ + if(atomic_cmpxchg(&te9->enabled, 1, 0)) { + del_timer_sync(&kxte9_timer); + wait_i2c_xfer_complete(); + kfree(kxte9_msg); + kfree(i2c_read_buf); + kfree(i2c_write_buf); +#ifdef WM3445_A0 + ENABLE_SENSOR_INT(1); +#endif + kxte9_device_power_off(); + kxte9_dbg(" Disabled\n"); + //#endif + } + return 0; +} + +int kxte9_input_open(struct input_dev *input) +{ + kxte9_dbg("\n"); + return kxte9_enable(); +} + +void kxte9_input_close(struct input_dev *dev) +{ + kxte9_dbg("\n"); + kxte9_disable(); +} + +static int kxte9_input_init(void) +{ + int err; + + te9->input_dev = input_allocate_device(); + if(!te9->input_dev) { + err = -ENOMEM; + errlog("input device allocate failed\n"); + goto err0; + } + te9->input_dev->open = kxte9_input_open; + te9->input_dev->close = kxte9_input_close; + + input_set_drvdata(te9->input_dev, te9); + + set_bit(EV_ABS, te9->input_dev->evbit); + set_bit(ABS_MISC, te9->input_dev->absbit); + + input_set_abs_params(te9->input_dev, ABS_X, -G_MAX, G_MAX, FUZZ, FLAT); + input_set_abs_params(te9->input_dev, ABS_Y, -G_MAX, G_MAX, FUZZ, FLAT); + input_set_abs_params(te9->input_dev, ABS_Z, -G_MAX, G_MAX, FUZZ, FLAT); + + te9->input_dev->name = INPUT_NAME_ACC; + + err = input_register_device(te9->input_dev); + if(err) { + errlog("unable to register input polled device %s: %d\n", + te9->input_dev->name, err); + goto err1; + } + + return 0; +err1: + input_free_device(te9->input_dev); +err0: + return err; +} + +static void kxte9_input_cleanup(void) +{ + input_unregister_device(te9->input_dev); +} + +/* sysfs */ +static ssize_t kxte9_delay_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", te9->pdata->poll_interval); +} + +static ssize_t kxte9_delay_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int val = simple_strtoul(buf, NULL, 10); + u8 ctrl; + + te9->pdata->poll_interval = max(val, te9->pdata->min_interval); + kxte9_update_odr(te9->pdata->poll_interval); + ctrl = te9->resume[RES_CTRL_REG1] & 0x18; + te9->resume[RES_CURRENT_ODR] = ctrl; + /* All ODRs are changed when this method is used. */ + ctrl = (ctrl >> 1) | (ctrl >> 3); + kxte9_i2c_write(CTRL_REG3, &ctrl, 1); + kxte9_dbg("kxte9_i2c_write(%x, %x, 1)\n", CTRL_REG3, ctrl); + te9->resume[RES_CTRL_REG3] = ctrl; + return count; +} + +static ssize_t kxte9_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", atomic_read(&te9->enabled)); +} + +static ssize_t kxte9_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int val = simple_strtoul(buf, NULL, 10); + if(val) + kxte9_enable(); + else + kxte9_disable(); + return count; +} + +static ssize_t kxte9_tilt_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u8 tilt; + + if(te9->resume[RES_CTRL_REG1] & TPE) { + kxte9_i2c_read(TILT_POS_CUR, &tilt, 1); + kxte9_dbg("kxte9_i2c_read(%x, 1)=%x\n", TILT_POS_CUR, tilt); + return sprintf(buf, "%d\n", kxte9_resolve_dir(tilt)); + } else { + return sprintf(buf, "%d\n", 0); + } +} + +static ssize_t kxte9_tilt_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int val = simple_strtoul(buf, NULL, 10); + u8 ctrl; + if(val) + te9->resume[RES_CTRL_REG1] |= TPE; + else + te9->resume[RES_CTRL_REG1] &= (~TPE); + ctrl = te9->resume[RES_CTRL_REG1]; + kxte9_i2c_write(CTRL_REG1, &ctrl, 1); + kxte9_dbg("kxte9_i2c_write(%x, %x, 1)\n", CTRL_REG1, ctrl); + return count; +} + +static ssize_t kxte9_wake_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u8 val = te9->resume[RES_CTRL_REG1] & WUFE; + if(val) + return sprintf(buf, "%d\n", 1); + else + return sprintf(buf, "%d\n", 0); +} + +static ssize_t kxte9_wake_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int val = simple_strtoul(buf, NULL, 10); + u8 ctrl; + if(val) + te9->resume[RES_CTRL_REG1] |= (WUFE | B2SE); + else + te9->resume[RES_CTRL_REG1] &= (~WUFE & ~B2SE); + ctrl = te9->resume[RES_CTRL_REG1]; + kxte9_i2c_write(CTRL_REG1, &ctrl, 1); + kxte9_dbg("kxte9_i2c_write(%x, %x, 1)\n", CTRL_REG1, ctrl); + return count; +} + +static ssize_t kxte9_selftest_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int val = simple_strtoul(buf, NULL, 10); + u8 ctrl = 0x00; + if(val) + ctrl = 0xCA; + kxte9_i2c_write(0x3A, &ctrl, 1); + kxte9_dbg("kxte9_i2c_write(0x3A, %x, 1)\n", ctrl); + return count; +} + +static DEVICE_ATTR(delay, S_IRUGO|S_IWUSR, kxte9_delay_show, kxte9_delay_store); +static DEVICE_ATTR(enable, S_IRUGO|S_IWUSR, kxte9_enable_show, + kxte9_enable_store); +static DEVICE_ATTR(tilt, S_IRUGO|S_IWUSR, kxte9_tilt_show, kxte9_tilt_store); +static DEVICE_ATTR(wake, S_IRUGO|S_IWUSR, kxte9_wake_show, kxte9_wake_store); +static DEVICE_ATTR(selftest, S_IWUSR, NULL, kxte9_selftest_store); + +static struct attribute *kxte9_attributes[] = { + &dev_attr_delay.attr, + &dev_attr_enable.attr, + &dev_attr_tilt.attr, + &dev_attr_wake.attr, + &dev_attr_selftest.attr, + NULL +}; + +static struct attribute_group kxte9_attribute_group = { + .attrs = kxte9_attributes +}; +/* /sysfs */ + +static int kxte9_get_count(char *buf, int bufsize) +{ + const char ACC_REG_SIZE = 3; + int err; + /* Data bytes from hardware xL, xH, yL, yH, zL, zH */ + u8 acc_data[ACC_REG_SIZE]; + /* x,y,z hardware values */ + int xyz[3]; + + if((!buf)||(bufsize<=(sizeof(xyz)*3))) + return -1; + + err = kxte9_i2c_read(XOUT, acc_data, ACC_REG_SIZE); + kxte9_dbg("kxte9_i2c_read(%x, %x)=%x,%x,%x, err=%d\n", XOUT, ACC_REG_SIZE, + acc_data[0], acc_data[1], acc_data[2], err); + if(err < 0) + return err; + + sprintf(buf, "%d %d %d", acc_data[0], acc_data[1], acc_data[2]); + + return err; +} + +static int kxte9_get_mg(char *buf, int bufsize) +{ + const char ACC_REG_SIZE = 3; + int err; + /* Data bytes from hardware xL, xH, yL, yH, zL, zH */ + u8 acc_data[ACC_REG_SIZE]; + /* x,y,z hardware values */ + int xyz[3], mg[3]; + + if((!buf)||(bufsize<=(sizeof(mg)))) + return -1; + + err = kxte9_i2c_read(XOUT, acc_data, ACC_REG_SIZE); + kxte9_dbg("kxte9_i2c_read(%x, %x)=%x,%x,%x, err=%x\n", XOUT, ACC_REG_SIZE, + acc_data[0], acc_data[1], acc_data[2], err); + if(err < 0) + return err; + + xyz[0] = ((int)(acc_data[0]) - OFFSET) << 4; + xyz[1] = ((int)(acc_data[1]) - OFFSET) << 4; + xyz[2] = ((int)(acc_data[2]) - OFFSET) << 4; + + mg[0] = X_CONVERT(xyz[gconf.xyz_axis[ABS_X][0]]); + mg[1] = Y_CONVERT(xyz[gconf.xyz_axis[ABS_Y][0]]); + mg[2] = Z_CONVERT(xyz[gconf.xyz_axis[ABS_Z][0]]); + + sprintf(buf, "%d %d %d",mg[0], mg[1], mg[2]); + + kxte9_dbg(" [%5d] [%5d] [%5d]\n", mg[0], mg[1], mg[2]); + + return err; +} + +static void kxte9_dump_reg(void) +{ + u8 regval = 0; + + klog("**********************kxte9 reg val***************\n"); + // ct_resp + kxte9_i2c_read(CT_RESP, ®val, 1); + klog("ct_resp:0x%x\n", regval); + // who_am_i + kxte9_i2c_read(WHO_AM_I, ®val, 1); + klog("who_am_i:0x%x\n", regval); + // tilt_pos_cur + kxte9_i2c_read(TILT_POS_CUR, ®val, 1); + klog("tilt_pos_cur:0x%x\n", regval); + // int_src_reg1 + kxte9_i2c_read(INT_STATUS_REG, ®val, 1); + klog("int_src_reg1:0x%x\n", regval); + // int_src_reg2 + kxte9_i2c_read(INT_SRC_REG2, ®val, 1); + klog("int_src_reg2:0x%x\n", regval); + // status_reg + kxte9_i2c_read(0x18, ®val, 1); + klog("status_reg:0x%x\n", regval); + // int_rel + kxte9_i2c_read(INT_REL, ®val, 1); + klog("int_rel:0x%x\n", regval); + // ctrl_reg1 + kxte9_i2c_read(CTRL_REG1, ®val, 1); + klog("ctrl_reg1:0x%x\n", regval); + // ctrl_reg2 + kxte9_i2c_read(CTRL_REG2, ®val, 1); + klog("ctrl_reg2:0x%x\n", regval); + // ctrl_reg3 + kxte9_i2c_read(CTRL_REG3, ®val, 1); + klog("ctrl_reg3:0x%x\n", regval); + // int_ctrl_reg1 + kxte9_i2c_read(INT_CTRL1, ®val, 1); + klog("int_ctrl_reg1:0x%x\n", regval); + // int_ctrl_reg2 + kxte9_i2c_read(0x1F, ®val, 1); + klog("int_ctrl_reg2:0x%x\n", regval); + // tilt_timer + kxte9_i2c_read(TILT_TIMER, ®val, 1); + klog("tilt_timer:0x%x\n", regval); + // wuf_timer + kxte9_i2c_read(WUF_TIMER, ®val, 1); + klog("wuf_timer:0x%x\n", regval); + // b2s_timer + kxte9_i2c_read(B2S_TIMER, ®val, 1); + klog("b2s_timer:0x%x\n", regval); + // wuf_thresh + kxte9_i2c_read(WUF_THRESH, ®val, 1); + klog("wuf_thresh:0x%x\n", regval); + // b2s_thresh + kxte9_i2c_read(B2S_THRESH, ®val, 1); + klog("b2s_thresh:0x%x\n", regval); + // tilt_angle + kxte9_i2c_read(0x5c, ®val, 1); + klog("tilt_angle:0x%x\n", regval); + // hyst_set + kxte9_i2c_read(0x5f, ®val, 1); + klog("hyst_set:0x%x\n", regval); + klog("**********************************************************"); +} + +static int kxte9_open(struct inode *inode, struct file *file) +{ + int ret = -1; + + if(kxte9_enable() < 0) + return ret; + + atomic_inc(&kxte9_dev_open_count); + kxte9_dbg("opened %d times\n",\ + atomic_read(&kxte9_dev_open_count)); + kxte9_dump_reg(); + return 0; +} + +static int kxte9_release(struct inode *inode, struct file *file) +{ + int open_count; + + atomic_dec(&kxte9_dev_open_count); + open_count = (int)atomic_read(&kxte9_dev_open_count); + + if(open_count == 0) + kxte9_disable(); + + kxte9_dbg("opened %d times\n",\ + atomic_read(&kxte9_dev_open_count)); + return 0; +} + +static int kxte9_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + char buffer[IOCTL_BUFFER_SIZE]; + void __user *data; + u8 reg_buffer = 0x00; + const u8 set = 0xFF, unset = 0x00; + int retval=0, val_int=0; + short val_short=0; + + switch (cmd) { + case KXTE9_IOCTL_GET_COUNT: + data = (void __user *) arg; + if(data == NULL){ + retval = -EFAULT; + goto err_out; + } + retval = kxte9_get_count(buffer, sizeof(buffer)); + if(retval < 0) + goto err_out; + + if(copy_to_user(data, buffer, sizeof(buffer))) { + retval = -EFAULT; + goto err_out; + } + break; + + case KXTE9_IOCTL_GET_MG: + data = (void __user *) arg; + if(data == NULL){ + retval = -EFAULT; + goto err_out; + } + retval = kxte9_get_mg(buffer, sizeof(buffer)); + if(retval < 0) + goto err_out; + + if(copy_to_user(data, buffer, sizeof(buffer))) { + retval = -EFAULT; + goto err_out; + } + break; + + case KXTE9_IOCTL_ENABLE_OUTPUT: + retval = kxte9_set_pc1_on(); + if(retval < 0) + goto err_out; + break; + + case KXTE9_IOCTL_DISABLE_OUTPUT: + retval = kxte9_set_pc1_off(); + if(retval < 0) + goto err_out; + break; + + case KXTE9_IOCTL_GET_ENABLE: + data = (void __user *) arg; + if(data == NULL){ + retval = -EFAULT; + goto err_out; + } + retval = kxte9_get_bits(CTRL_REG1, ®_buffer, PC1_ON); + if(retval < 0) + goto err_out; + + val_short = (short)reg_buffer; + + if(copy_to_user(data, &val_short, sizeof(val_short))) { + retval = -EFAULT; + goto err_out; + } + break; + + case KXTE9_IOCTL_RESET: + retval = kxte9_set_bits(RES_CTRL_REG3, CTRL_REG3, set, SRST); + if(retval < 0) + goto err_out; + break; + + case KXTE9_IOCTL_UPDATE_ODR: + data = (void __user *) arg; + if(data == NULL){ + retval = -EFAULT; + goto err_out; + } + if(copy_from_user(&val_int, data, sizeof(val_int))) { + retval = -EFAULT; + goto err_out; + } + + mutex_lock(&te9->lock); + te9->pdata->poll_interval = max(val_int, te9->pdata->min_interval); + mutex_unlock(&te9->lock); + + retval = kxte9_update_odr(te9->pdata->poll_interval); + if(retval < 0) + goto err_out; + break; + + case KXTE9_IOCTL_ENABLE_CTC: + retval = kxte9_set_bits(RES_CTRL_REG3, CTRL_REG3, set, CTC); + if(retval < 0) + goto err_out; + break; + + case KXTE9_IOCTL_DISABLE_CTC: + retval = kxte9_set_bits(RES_CTRL_REG3, CTRL_REG3, unset, CTC); + if(retval < 0) + goto err_out; + break; + + case KXTE9_IOCTL_GET_CT_RESP: + data = (void __user *) arg; + if(data == NULL){ + retval = -EFAULT; + goto err_out; + } + retval = kxte9_get_byte(CT_RESP, ®_buffer); + if(retval < 0) + goto err_out; + + buffer[0] = (char)reg_buffer; + if(copy_to_user(data, buffer, sizeof(buffer))) { + retval = -EFAULT; + goto err_out; + } + break; + + default: + retval = -ENOIOCTLCMD; + break; + } + +err_out: + return retval; +} + +static struct file_operations kxte9_fops = { + .owner = THIS_MODULE, + .open = kxte9_open, + .release = kxte9_release, + //.ioctl = kxte9_ioctl, +}; + +static struct miscdevice kxte9_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = NAME_DEV, + .fops = &kxte9_fops, +}; + +static int kxte9_probe(void) +{ + int err = -1; + + te9 = kzalloc(sizeof(*te9), GFP_KERNEL); + if(te9 == NULL) { + errlog("failed to allocate memory for module data\n"); + err = -ENOMEM; + goto err0; + } + te9->pdata = &kxte9_pdata; + mutex_init(&te9->lock); + mutex_lock(&te9->lock); + + x_count = kzalloc(gconf.avg_count, GFP_KERNEL); + y_count = kzalloc(gconf.avg_count, GFP_KERNEL); + z_count = kzalloc(gconf.avg_count, GFP_KERNEL); + + if ((1000000/gconf.samp)%1000) + te9->pdata->poll_interval = 1000/gconf.samp + 1; + else + te9->pdata->poll_interval = 1000/gconf.samp; + printk(KERN_INFO "G-Sensor Time Interval: %d ms\n", te9->pdata->poll_interval); + + android_gsensor_kobj = kobject_create_and_add("kxte9_gsensor", NULL); + if (android_gsensor_kobj == NULL) { + errlog("gsensor_sysfs_init:subsystem_register failed\n"); + err = -ENOMEM; + goto err0; + } + + err = sysfs_create_group(android_gsensor_kobj, &kxte9_attribute_group); + if(err) + goto err1; + + if(te9->pdata->init) { + err = te9->pdata->init(); + if(err < 0) + goto err2; + } + + memset(te9->resume, 0, ARRAY_SIZE(te9->resume)); + te9->resume[RES_CTRL_REG1] = te9->pdata->ctrl_reg1_init; + te9->resume[RES_CTRL_REG3] = te9->pdata->engine_odr_init; + te9->resume[RES_INT_CTRL1] = te9->pdata->int_ctrl_init; + te9->resume[RES_TILT_TIMER] = te9->pdata->tilt_timer_init; + te9->resume[RES_WUF_TIMER] = te9->pdata->wuf_timer_init; + te9->resume[RES_B2S_TIMER] = te9->pdata->b2s_timer_init; + te9->resume[RES_WUF_THRESH] = te9->pdata->wuf_thresh_init; + te9->resume[RES_B2S_THRESH] = te9->pdata->b2s_thresh_init; + te9->hw_initialized = 0; + te9->i2c_xfer_complete = 1; + +#ifdef INT_MODE + // init gpio + SET_GPIO_GSENSOR_INT(); + INIT_WORK(&te9->irq_work, kxte9_irq_work_func); +#endif + + err = kxte9_device_power_on(); + if(err < 0) + goto err3; + atomic_set(&te9->enabled, 1); + + err = kxte9_verify(); + if(err < 0) { + errlog("kxte9_verify failed\n"); + goto err4; + } + + err = kxte9_update_odr(te9->pdata->poll_interval); + if(err < 0) { + errlog("update_odr failed\n"); + goto err4; + } + + err = kxte9_input_init(); + if(err < 0) + goto err4; + + err = misc_register(&kxte9_device); + if(err) { + errlog("misc. device failed to register.\n"); + goto err5; + } + +#ifdef CONFIG_HAS_EARLYSUSPEND + te9->earlysuspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; + te9->earlysuspend.suspend = kxte9_early_suspend; + te9->earlysuspend.resume = kxte9_late_resume; + register_early_suspend(&te9->earlysuspend); +#endif + + atomic_set(&te9->enabled, 0); + +#ifdef INT_MODE + err = request_irq(gconf.irq, kxte9_isr, + IRQF_TRIGGER_RISING | IRQF_DISABLED, "kxte9_irq", te9); + if(err < 0) { + pr_err("%s: request irq failed: %d\n", __func__, err); + goto err6; + } + +#ifdef WM3445_A0 + // enable int + ENABLE_SENSOR_INT(1); +#endif + + // enable kxte9 + /*err = kxte9_enable(); + if (err < 0) { + errlog("kxte9_enable failed.\n"); + goto err6; + }*/ +#endif + + mutex_unlock(&te9->lock); + //kxte9_dump_reg(); + + return 0; + +#ifdef INT_MODE +err6: + misc_deregister(&kxte9_device); +#endif +err5: + kxte9_input_cleanup(); +err4: + kxte9_device_power_off(); +err3: + if(te9->pdata->exit) + te9->pdata->exit(); +err2: + //kfree(te9->pdata); + sysfs_remove_group(android_gsensor_kobj, &kxte9_attribute_group); +err1: + kobject_del(android_gsensor_kobj); + mutex_unlock(&te9->lock); + kfree(te9); +err0: + return err; +} + +static int kxte9_remove(void) +{ + cancel_delayed_work_sync(&te9->input_work); +#ifdef INT_MODE + free_irq(gconf.irq, te9); +#endif + kxte9_input_cleanup(); + misc_deregister(&kxte9_device); + kxte9_device_power_off(); + if(te9->pdata->exit) + te9->pdata->exit(); + kobject_del(android_gsensor_kobj); + sysfs_remove_group(android_gsensor_kobj, &kxte9_attribute_group); + kfree(te9); + + return 0; +} + +#ifdef CONFIG_PM +#if 0 +#ifdef CONFIG_HAS_EARLYSUSPEND +static void kxte9_early_suspend(struct early_suspend *h) +{ + kxte9_dbg("start\n"); + te9->suspend = 1; + kxte9_disable(); + kxte9_dbg("exit\n"); +} + +static void kxte9_late_resume(struct early_suspend *h) +{ + kxte9_dbg("start\n"); + te9->suspend = 0; + kxte9_enable(); + kxte9_dbg("exit\n"); +} +#endif +#endif +static int kxte9_resume(struct platform_device *pdev) +{ + te9->suspend = 0; + kxte9_enable(); + return 0; +} + +static int kxte9_suspend(struct platform_device *pdev, pm_message_t state) +{ + te9->suspend = 1; + kxte9_disable(); + return 0; +} +#endif + +static void kxte9_platform_release(struct device *device) +{ + kxte9_dbg("Do nothing!!!\n"); + return; +} + +static struct platform_device kxte9_plt_device = { + .name = "kxte9", + .id = 0, + .dev = { + .release = kxte9_platform_release, + }, +}; + +static struct platform_driver kxte9_plt_driver = { + .probe = NULL, //kxte9_probe, + .remove = NULL, //kxte9_remove, + .suspend = kxte9_suspend, + .resume = kxte9_resume, + .driver = { + .name = "kxte9", + }, +}; + +/* + * Get the configure of sensor from u-boot. + * Return: 1--success, 0--error. + */ +static int get_axisset(void) +{ + char varbuf[64]; + int n; + int varlen; + + memset(varbuf, 0, sizeof(varbuf)); + varlen = sizeof(varbuf); + if (wmt_getsyspara("wmt.io.gsensor", varbuf, &varlen)) { + printk(KERN_DEBUG "KXTE9: wmt.io.gsensor not defined!\n"); + return 0; + } else { + n = sscanf(varbuf, "%d:%d:%d:%d:%d:%d:%d:%d:%d:%d", + &gconf.op, + &gconf.samp, + &(gconf.xyz_axis[0][0]), + &(gconf.xyz_axis[0][1]), + &(gconf.xyz_axis[1][0]), + &(gconf.xyz_axis[1][1]), + &(gconf.xyz_axis[2][0]), + &(gconf.xyz_axis[2][1]), + &gconf.avg_count, + &gconf.kxte9_8bit); + + if (n != 10) { + kxte9_dbg(KERN_ERR "KXTE9: wmt.io.gsensor format is incorrect!\n"); + return 0; + } + } + if (gconf.op != 1) + return 0; + printk(KERN_INFO "KXTE9 G-Sensor driver init\n"); + kxte9_dbg("AVG Count: %d, KXTE9 8bit: %d\n", + gconf.avg_count, + gconf.kxte9_8bit); + if (gconf.samp > 100) { + printk(KERN_ERR "Sample Rate can't be greater than 100 !\n"); + gconf.samp = 100; + } + if (gconf.avg_count == 0) + gconf.avg_count = 1; + if (gconf.kxte9_8bit > 1) + gconf.kxte9_8bit = 1; + kxte9_dbg("wmt.io.gsensor = %d:%d:%d:%d:%d:%d:%d:%d:%d:%d\n", + gconf.op, + gconf.samp, + gconf.xyz_axis[0][0], + gconf.xyz_axis[0][1], + gconf.xyz_axis[1][0], + gconf.xyz_axis[1][1], + gconf.xyz_axis[2][0], + gconf.xyz_axis[2][1], + gconf.avg_count, + gconf.kxte9_8bit); + return 1; +} + +static int get_gpioset(void) +{ + char varbuf[96]; + int n; + int varlen; + + memset(varbuf, 0, sizeof(varbuf)); + varlen = sizeof(varbuf); + if (wmt_getsyspara("wmt.gpt.gsensor", varbuf, &varlen)) { + printk(KERN_DEBUG "Can't get gsensor's gpio config from u-boot! -> Use default config\n"); + return 0; + } else { + n = sscanf(varbuf, "%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x", + &gconf.name, + &gconf.bmp, + &(gconf.ctraddr), + &(gconf.ocaddr), + &(gconf.idaddr), + &(gconf.peaddr), + &(gconf.pcaddr), + &(gconf.itbmp), + &(gconf.itaddr), + &(gconf.isbmp), + &(gconf.isaddr), + &(gconf.irq)); + if (n != 12) { + printk(KERN_ERR "wmt.gpt.gsensor format is incorrect in u-boot!!!\n"); + return 0; + } + + gconf.ctraddr += WMT_MMAP_OFFSET; + gconf.ocaddr += WMT_MMAP_OFFSET; + gconf.idaddr += WMT_MMAP_OFFSET; + gconf.peaddr += WMT_MMAP_OFFSET; + gconf.pcaddr += WMT_MMAP_OFFSET; + gconf.itaddr += WMT_MMAP_OFFSET; + gconf.isaddr += WMT_MMAP_OFFSET; + } + return 1; +} + +static int __init kxte9_init(void) +{ + int ret = 0; + + // parsing u-boot arg + ret = get_axisset(); // get gsensor config from u-boot + if (!ret) + return -EINVAL; + + get_gpioset(); + + // initail + if ((ret = kxte9_probe()) != 0) { + errlog(" Error for kxte9_probe !!!\n"); + return ret; + } + atomic_set(&kxte9_dev_open_count, 0); + + if ((ret = platform_device_register(&kxte9_plt_device)) != 0) { + errlog("Can't register kxte9_plt_device platform devcie!!!\n"); + return ret; + } + if ((ret = platform_driver_register(&kxte9_plt_driver)) != 0) { + errlog("Can't register kxte9_plt_driver platform driver!!!\n"); + return ret; + } + klog("gsensor_kxte9 driver is loaded successfully!\n"); + return ret; +} + +static void __exit kxte9_exit(void) +{ + platform_driver_unregister(&kxte9_plt_driver); + platform_device_unregister(&kxte9_plt_device); + atomic_set(&kxte9_dev_open_count, 0); + kxte9_remove(); +} + +module_init(kxte9_init); +module_exit(kxte9_exit); + +MODULE_DESCRIPTION("KXTE9 accelerometer driver"); +MODULE_AUTHOR("Kuching Tan <kuchingtan@kionix.com>"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(VERSION_DEV); |