From 871480933a1c28f8a9fed4c4d34d06c439a7a422 Mon Sep 17 00:00:00 2001 From: Srikant Patnaik Date: Sun, 11 Jan 2015 12:28:04 +0530 Subject: Moved, renamed, and deleted files The original directory structure was scattered and unorganized. Changes are basically to make it look like kernel structure. --- drivers/input/sensor/Kconfig | 224 ++ drivers/input/sensor/Makefile | 26 + drivers/input/sensor/TP_DRIVER_NOT_USE/Kconfig | 50 + drivers/input/sensor/TP_DRIVER_NOT_USE/Makefile | 13 + .../TP_DRIVER_NOT_USE/dmt08_gsensor/Makefile | 6 + .../sensor/TP_DRIVER_NOT_USE/dmt08_gsensor/dmt08.c | 870 +++++ .../sensor/TP_DRIVER_NOT_USE/dmt08_gsensor/dmt08.h | 105 + .../TP_DRIVER_NOT_USE/dmt10_gsensor/Makefile | 6 + .../sensor/TP_DRIVER_NOT_USE/dmt10_gsensor/dmt10.c | 950 ++++++ .../sensor/TP_DRIVER_NOT_USE/dmt10_gsensor/dmt10.h | 187 + drivers/input/sensor/TP_DRIVER_NOT_USE/gsensor.c | 180 + drivers/input/sensor/TP_DRIVER_NOT_USE/gsensor.h | 43 + .../TP_DRIVER_NOT_USE/kxti9_gsensor/Makefile | 6 + .../sensor/TP_DRIVER_NOT_USE/kxti9_gsensor/kxti9.c | 1134 +++++++ .../sensor/TP_DRIVER_NOT_USE/kxti9_gsensor/kxti9.h | 103 + drivers/input/sensor/cm3232/Makefile | 34 + drivers/input/sensor/cm3232/cm3232.c | 855 +++++ drivers/input/sensor/dmard06_gsensor/Makefile | 34 + drivers/input/sensor/dmard06_gsensor/dmard06.c | 980 ++++++ drivers/input/sensor/dmard08_gsensor/Makefile | 34 + drivers/input/sensor/dmard08_gsensor/cyclequeue.c | 68 + drivers/input/sensor/dmard08_gsensor/cyclequeue.h | 18 + drivers/input/sensor/dmard08_gsensor/dmard08.c | 1019 ++++++ drivers/input/sensor/dmard08_gsensor/dmard08.h | 75 + drivers/input/sensor/dmard09_gsensor/Makefile | 35 + drivers/input/sensor/dmard09_gsensor/dmt09.c | 1685 +++++++++ drivers/input/sensor/dmard09_gsensor/dmt09.h | 183 + drivers/input/sensor/dmard10_gsensor/Makefile | 34 + drivers/input/sensor/dmard10_gsensor/dmt10.c | 1702 ++++++++++ drivers/input/sensor/dmard10_gsensor/dmt10.h | 192 ++ drivers/input/sensor/isl29023_lsensor/Makefile | 34 + drivers/input/sensor/isl29023_lsensor/isl29023.c | 1164 +++++++ drivers/input/sensor/kionix_gsensor/Makefile | 35 + drivers/input/sensor/kionix_gsensor/kionix_accel.c | 2427 +++++++++++++ drivers/input/sensor/kionix_gsensor/kionix_accel.h | 85 + drivers/input/sensor/kxte9_gsensor/Makefile | 34 + drivers/input/sensor/kxte9_gsensor/kxte9.c | 1798 ++++++++++ drivers/input/sensor/kxte9_gsensor/kxte9.h | 116 + drivers/input/sensor/kxte9_gsensor/readme.txt | 47 + drivers/input/sensor/l3g4200d_gyro/Makefile | 5 + drivers/input/sensor/l3g4200d_gyro/l3g4200d.h | 108 + drivers/input/sensor/l3g4200d_gyro/l3g4200d_gyr.c | 1681 +++++++++ drivers/input/sensor/mc3230_gsensor/Makefile | 34 + drivers/input/sensor/mc3230_gsensor/mc32x0.c | 2580 ++++++++++++++ .../input/sensor/mc3230_gsensor/mc32x0_driver.c | 505 +++ .../input/sensor/mc3230_gsensor/mc32x0_driver.h | 219 ++ drivers/input/sensor/mc3xxx_gsensor/Makefile | 34 + drivers/input/sensor/mc3xxx_gsensor/mc3xxx.c | 2719 +++++++++++++++ drivers/input/sensor/mma7660_gsensor/Makefile | 34 + drivers/input/sensor/mma7660_gsensor/mma7660.c | 1052 ++++++ drivers/input/sensor/mma7660_gsensor/mma7660.h | 106 + drivers/input/sensor/mma8452q_gsensor/Makefile | 34 + drivers/input/sensor/mma8452q_gsensor/mma8x5x.c | 982 ++++++ drivers/input/sensor/mma8452q_gsensor/mma8x5x.h | 107 + drivers/input/sensor/mmc328x_msensor/Makefile | 4 + drivers/input/sensor/mmc328x_msensor/mecs.c | 433 +++ drivers/input/sensor/mmc328x_msensor/mecs.h | 60 + drivers/input/sensor/mmc328x_msensor/mmc328x.c | 505 +++ drivers/input/sensor/mmc328x_msensor/mmc328x.h | 91 + drivers/input/sensor/mxc622x_gsensor/Makefile | 34 + drivers/input/sensor/mxc622x_gsensor/mxc622x.c | 1151 +++++++ drivers/input/sensor/mxc622x_gsensor/mxc622x.h | 83 + drivers/input/sensor/sensor.c | 114 + drivers/input/sensor/sensor.h | 91 + drivers/input/sensor/stk3310/Makefile | 34 + drivers/input/sensor/stk3310/stk3310.c | 644 ++++ drivers/input/sensor/stk8312_gsensor/Makefile | 34 + drivers/input/sensor/stk8312_gsensor/stk8312.h | 51 + drivers/input/sensor/stk8312_gsensor/stk8313.h | 52 + drivers/input/sensor/stk8312_gsensor/stk831x.c | 3590 ++++++++++++++++++++ drivers/input/sensor/us5182_lpsensor/Makefile | 34 + drivers/input/sensor/us5182_lpsensor/us5182.c | 1098 ++++++ drivers/input/sensor/us5182_lpsensor/us5182.h | 255 ++ 73 files changed, 35149 insertions(+) create mode 100755 drivers/input/sensor/Kconfig create mode 100755 drivers/input/sensor/Makefile create mode 100755 drivers/input/sensor/TP_DRIVER_NOT_USE/Kconfig create mode 100755 drivers/input/sensor/TP_DRIVER_NOT_USE/Makefile create mode 100755 drivers/input/sensor/TP_DRIVER_NOT_USE/dmt08_gsensor/Makefile create mode 100755 drivers/input/sensor/TP_DRIVER_NOT_USE/dmt08_gsensor/dmt08.c create mode 100755 drivers/input/sensor/TP_DRIVER_NOT_USE/dmt08_gsensor/dmt08.h create mode 100755 drivers/input/sensor/TP_DRIVER_NOT_USE/dmt10_gsensor/Makefile create mode 100755 drivers/input/sensor/TP_DRIVER_NOT_USE/dmt10_gsensor/dmt10.c create mode 100755 drivers/input/sensor/TP_DRIVER_NOT_USE/dmt10_gsensor/dmt10.h create mode 100755 drivers/input/sensor/TP_DRIVER_NOT_USE/gsensor.c create mode 100755 drivers/input/sensor/TP_DRIVER_NOT_USE/gsensor.h create mode 100755 drivers/input/sensor/TP_DRIVER_NOT_USE/kxti9_gsensor/Makefile create mode 100755 drivers/input/sensor/TP_DRIVER_NOT_USE/kxti9_gsensor/kxti9.c create mode 100755 drivers/input/sensor/TP_DRIVER_NOT_USE/kxti9_gsensor/kxti9.h create mode 100755 drivers/input/sensor/cm3232/Makefile create mode 100755 drivers/input/sensor/cm3232/cm3232.c create mode 100755 drivers/input/sensor/dmard06_gsensor/Makefile create mode 100755 drivers/input/sensor/dmard06_gsensor/dmard06.c create mode 100755 drivers/input/sensor/dmard08_gsensor/Makefile create mode 100755 drivers/input/sensor/dmard08_gsensor/cyclequeue.c create mode 100755 drivers/input/sensor/dmard08_gsensor/cyclequeue.h create mode 100755 drivers/input/sensor/dmard08_gsensor/dmard08.c create mode 100755 drivers/input/sensor/dmard08_gsensor/dmard08.h create mode 100755 drivers/input/sensor/dmard09_gsensor/Makefile create mode 100755 drivers/input/sensor/dmard09_gsensor/dmt09.c create mode 100755 drivers/input/sensor/dmard09_gsensor/dmt09.h create mode 100755 drivers/input/sensor/dmard10_gsensor/Makefile create mode 100755 drivers/input/sensor/dmard10_gsensor/dmt10.c create mode 100755 drivers/input/sensor/dmard10_gsensor/dmt10.h create mode 100755 drivers/input/sensor/isl29023_lsensor/Makefile create mode 100755 drivers/input/sensor/isl29023_lsensor/isl29023.c create mode 100755 drivers/input/sensor/kionix_gsensor/Makefile create mode 100755 drivers/input/sensor/kionix_gsensor/kionix_accel.c create mode 100755 drivers/input/sensor/kionix_gsensor/kionix_accel.h create mode 100755 drivers/input/sensor/kxte9_gsensor/Makefile create mode 100755 drivers/input/sensor/kxte9_gsensor/kxte9.c create mode 100755 drivers/input/sensor/kxte9_gsensor/kxte9.h create mode 100755 drivers/input/sensor/kxte9_gsensor/readme.txt create mode 100755 drivers/input/sensor/l3g4200d_gyro/Makefile create mode 100755 drivers/input/sensor/l3g4200d_gyro/l3g4200d.h create mode 100755 drivers/input/sensor/l3g4200d_gyro/l3g4200d_gyr.c create mode 100755 drivers/input/sensor/mc3230_gsensor/Makefile create mode 100755 drivers/input/sensor/mc3230_gsensor/mc32x0.c create mode 100755 drivers/input/sensor/mc3230_gsensor/mc32x0_driver.c create mode 100755 drivers/input/sensor/mc3230_gsensor/mc32x0_driver.h create mode 100644 drivers/input/sensor/mc3xxx_gsensor/Makefile create mode 100644 drivers/input/sensor/mc3xxx_gsensor/mc3xxx.c create mode 100755 drivers/input/sensor/mma7660_gsensor/Makefile create mode 100755 drivers/input/sensor/mma7660_gsensor/mma7660.c create mode 100755 drivers/input/sensor/mma7660_gsensor/mma7660.h create mode 100755 drivers/input/sensor/mma8452q_gsensor/Makefile create mode 100755 drivers/input/sensor/mma8452q_gsensor/mma8x5x.c create mode 100755 drivers/input/sensor/mma8452q_gsensor/mma8x5x.h create mode 100755 drivers/input/sensor/mmc328x_msensor/Makefile create mode 100755 drivers/input/sensor/mmc328x_msensor/mecs.c create mode 100755 drivers/input/sensor/mmc328x_msensor/mecs.h create mode 100755 drivers/input/sensor/mmc328x_msensor/mmc328x.c create mode 100755 drivers/input/sensor/mmc328x_msensor/mmc328x.h create mode 100755 drivers/input/sensor/mxc622x_gsensor/Makefile create mode 100755 drivers/input/sensor/mxc622x_gsensor/mxc622x.c create mode 100755 drivers/input/sensor/mxc622x_gsensor/mxc622x.h create mode 100755 drivers/input/sensor/sensor.c create mode 100755 drivers/input/sensor/sensor.h create mode 100755 drivers/input/sensor/stk3310/Makefile create mode 100755 drivers/input/sensor/stk3310/stk3310.c create mode 100755 drivers/input/sensor/stk8312_gsensor/Makefile create mode 100755 drivers/input/sensor/stk8312_gsensor/stk8312.h create mode 100755 drivers/input/sensor/stk8312_gsensor/stk8313.h create mode 100755 drivers/input/sensor/stk8312_gsensor/stk831x.c create mode 100755 drivers/input/sensor/us5182_lpsensor/Makefile create mode 100755 drivers/input/sensor/us5182_lpsensor/us5182.c create mode 100755 drivers/input/sensor/us5182_lpsensor/us5182.h (limited to 'drivers/input/sensor') diff --git a/drivers/input/sensor/Kconfig b/drivers/input/sensor/Kconfig new file mode 100755 index 00000000..cff1aa40 --- /dev/null +++ b/drivers/input/sensor/Kconfig @@ -0,0 +1,224 @@ +# +# WMT Sensor configuration +# +menuconfig INPUT_SENSOR + bool "WMT Sensor" + default y + help + Say Y here, and a list of supported sensor will be displayed. + This option doesn't affect the kernel. + + If unsure, say Y. + +if INPUT_SENSOR + +config WMT_SENSOR_KXTE9 + tristate "KXTE9 G-Sensor Support" + depends on ARCH_WMT + default n + help + Say Y here if you have an WMT based board with g-sensor + attached to it. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called s_wmt_gsensor_mc3230. +config WMT_SENSOR_KIONIX + tristate "KIONIX G-Sensor Support" + depends on ARCH_WMT + default n + help + Say Y here if you have an WMT based board with g-sensor + attached to it. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called s_wmt_gsensor_kionix. +config WMT_SENSOR_MC3XXX + tristate "Mcube G-Sensor Support" + depends on ARCH_WMT + default m + help + Say Y here if you have an WMT based board with g-sensor + attached to it. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called s_wmt_gsensor_mc3xxx. + +config WMT_SENSOR_DMARD08 + tristate "DMARD08 G-Sensor Support" + depends on ARCH_WMT + default y + help + Say Y here if you have an WMT based board with g-sensor + attached to it. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called sensor_dmard08. +config WMT_SENSOR_DMARD06 + tristate "DMARD06 G-Sensor Support" + depends on ARCH_WMT + default y + help + Say Y here if you have an WMT based board with g-sensor + attached to it. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called sensor_dmard06. +config WMT_SENSOR_DMARD10 + tristate "DMARD10 G-Sensor Support" + depends on ARCH_WMT + default y + help + Say Y here if you have an WMT based board with g-sensor + attached to it. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called sensor_dmard10. +config WMT_SENSOR_DMARD09 + tristate "DMARD09 G-Sensor Support" + depends on ARCH_WMT + default y + help + Say Y here if you have an WMT based board with g-sensor + attached to it. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called sensor_dmard09. +config WMT_SENSOR_MXC622X + tristate "MXC622X G-Sensor Support" + depends on ARCH_WMT + default y + help + Say Y here if you have an WMT based board with g-sensor + attached to it. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called sensor_mxc622x. +config WMT_SENSOR_MMA7660 + tristate "MMA7660 G-Sensor Support" + depends on ARCH_WMT + default y + help + Say Y here if you have an WMT based board with g-sensor + attached to it. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called sensor_mma7660. +config WMT_SENSOR_MMC328x + tristate "MMC328x M-Sensor Support" + depends on ARCH_WMT + default y + help + Say Y here if you have an WMT based board with m-sensor + attached to it. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called sensor_mmc328x. +config WMT_SENSOR_ISL29023 + tristate "ISL29023 Light sensor Support" + depends on ARCH_WMT + default y + help + Say Y here if you have an WMT based board with l-sensor + attached to it. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called sensor_isl29023. +config WMT_SENSOR_CM3232 + tristate "CM3232 Light sensor Support" + depends on ARCH_WMT + default y + help + Say Y here if you have an WMT based board with l-sensor + attached to it. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called sensor_cm3232. +config WMT_SENSOR_STK3310 + tristate "STK3310 Light sensor Support" + depends on ARCH_WMT + default y + help + Say Y here if you have an WMT based board with l-sensor + attached to it. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called sensor_stk3310. +config WMT_GYRO_L3G4200D + tristate "L3G4200D Gyroscope Support" + depends on ARCH_WMT + default m + help + Say Y here if you have an WMT based board with ST L3g4200d + gyroscope attached to it. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called s_wmt_gyro_l3g4200d. + +config WMT_SENSOR_US5182 + tristate "US5182 Light&Promixity sensor Support" + depends on ARCH_WMT + default m + help + Say Y here if you have an WMT based board with l&p-sensor + attached to it. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called s_wmt_lsensor_us5182. + +config WMT_SENSOR_MMA8452Q + tristate "MMA8452Q G-Sensor Support" + depends on ARCH_WMT + default m + help + Say Y here if you have an WMT based board with g-sensor + attached to it. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called s_wmt_gsensor_mma8542. + +config WMT_SENSOR_STK8312 + tristate "STK8312 G-Sensor Support" + depends on ARCH_WMT + default m + help + Say Y here if you have an WMT based board with g-sensor + attached to it. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called s_wmt_gsensor_STK8312. + +endif diff --git a/drivers/input/sensor/Makefile b/drivers/input/sensor/Makefile new file mode 100755 index 00000000..cc06e871 --- /dev/null +++ b/drivers/input/sensor/Makefile @@ -0,0 +1,26 @@ +# +# Makefile for the Sensor driver +# + +# Each configuration option enables a list of files. + +#obj-$(CONFIG_INPUT_SENSOR) += gsensor.o +obj-y += sensor.o +obj-$(CONFIG_WMT_SENSOR_KXTE9) += kxte9_gsensor/ +obj-$(CONFIG_WMT_SENSOR_MC3XXX) += mc3xxx_gsensor/ +obj-$(CONFIG_WMT_SENSOR_DMARD08) += dmard08_gsensor/ +obj-$(CONFIG_WMT_SENSOR_DMARD06) += dmard06_gsensor/ +obj-$(CONFIG_WMT_SENSOR_MMA7660) += mma7660_gsensor/ +obj-$(CONFIG_WMT_SENSOR_ISL29023) += isl29023_lsensor/ +obj-$(CONFIG_WMT_SENSOR_CM3232) += cm3232/ +obj-$(CONFIG_WMT_SENSOR_CM3232) += stk3310/ +obj-$(CONFIG_WMT_SENSOR_DMARD10) += dmard10_gsensor/ +obj-$(CONFIG_WMT_SENSOR_DMARD09) += dmard09_gsensor/ +obj-$(CONFIG_WMT_SENSOR_MXC622X) += mxc622x_gsensor/ +obj-$(CONFIG_WMT_SENSOR_MMC328x) += mmc328x_msensor/ +#obj-$(CONFIG_WMT_SENSOR_CM3232) += cm3232/cm3232.o +obj-$(CONFIG_WMT_GYRO_L3G4200D) += l3g4200d_gyro/ +obj-$(CONFIG_WMT_SENSOR_US5182) += us5182_lpsensor/ +obj-$(CONFIG_WMT_SENSOR_MMA8452Q) += mma8452q_gsensor/ +obj-$(CONFIG_WMT_SENSOR_STK8312) += stk8312_gsensor/ +obj-$(CONFIG_WMT_SENSOR_KIONIX) += kionix_gsensor/ \ No newline at end of file diff --git a/drivers/input/sensor/TP_DRIVER_NOT_USE/Kconfig b/drivers/input/sensor/TP_DRIVER_NOT_USE/Kconfig new file mode 100755 index 00000000..9bf96e92 --- /dev/null +++ b/drivers/input/sensor/TP_DRIVER_NOT_USE/Kconfig @@ -0,0 +1,50 @@ +# +# WMT Sensor configuration +# +menuconfig INPUT_SENSOR + bool "WMT Sensor" + help + Say Y here, and a list of supported sensor will be displayed. + This option doesn't affect the kernel. + + If unsure, say Y. + +if INPUT_SENSOR + + +config WMT_SENSOR_KXTI9 + tristate "KXTI9 G-Sensor Support" + depends on ARCH_WMT + help + Say Y here if you have an WMT based board with g-sensor + attached to it. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called sensor_kxti9. + +config WMT_SENSOR_DMT08 + tristate "DMT08 G-Sensor Support" + depends on ARCH_WMT + help + Say Y here if you have an WMT based board with g-sensor + attached to it. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called sensor_dmt08. + +config WMT_SENSOR_DMT10 + tristate "DMT10 G-Sensor Support" + depends on ARCH_WMT + help + Say Y here if you have an WMT based board with g-sensor + attached to it. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called sensor_dmt10. +endif diff --git a/drivers/input/sensor/TP_DRIVER_NOT_USE/Makefile b/drivers/input/sensor/TP_DRIVER_NOT_USE/Makefile new file mode 100755 index 00000000..ee1a0ac5 --- /dev/null +++ b/drivers/input/sensor/TP_DRIVER_NOT_USE/Makefile @@ -0,0 +1,13 @@ +# +# Makefile for the Sensor driver +# + +# Each configuration option enables a list of files. + +obj-$(CONFIG_INPUT_SENSOR) += gsensor.o + +obj-$(CONFIG_WMT_SENSOR_KXTI9) += kxti9_gsensor/ + +obj-$(CONFIG_WMT_SENSOR_DMT08) += dmt08_gsensor/ + +obj-$(CONFIG_WMT_SENSOR_DMT10) += dmt10_gsensor/ diff --git a/drivers/input/sensor/TP_DRIVER_NOT_USE/dmt08_gsensor/Makefile b/drivers/input/sensor/TP_DRIVER_NOT_USE/dmt08_gsensor/Makefile new file mode 100755 index 00000000..6edce54a --- /dev/null +++ b/drivers/input/sensor/TP_DRIVER_NOT_USE/dmt08_gsensor/Makefile @@ -0,0 +1,6 @@ +# +# Makefile for the DMARD06 Sensor driver +# + +sensor_dmt08-objs := dmt08.o +obj-$(CONFIG_WMT_SENSOR_DMT08) += sensor_dmt08.o diff --git a/drivers/input/sensor/TP_DRIVER_NOT_USE/dmt08_gsensor/dmt08.c b/drivers/input/sensor/TP_DRIVER_NOT_USE/dmt08_gsensor/dmt08.c new file mode 100755 index 00000000..2ed5f502 --- /dev/null +++ b/drivers/input/sensor/TP_DRIVER_NOT_USE/dmt08_gsensor/dmt08.c @@ -0,0 +1,870 @@ +/* + * @file drivers/misc/dmt0308.c + * @brief DMT g-sensor Linux device driver + * @author Domintech Technology Co., Ltd (http://www.domintech.com.tw) + * @version 1.31 + * @date 2012/3/27 + * + * @section LICENSE + * + * Copyright 2011 Domintech Technology Co., Ltd + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "dmt08.h" +#include "../gsensor.h" + +wait_queue_head_t open_wq; +atomic_t active; +static unsigned int interval; +struct mutex DMT_mutex; +struct delayed_work work; +struct work_struct irq_work; +atomic_t delay; + +static struct gsensor_conf gs_conf; + +void gsensor_write_offset_to_file(void); +void gsensor_read_offset_from_file(void); +char OffsetFileName[] = "/data/misc/dmt/offset.txt"; +static int Device_First_Time_Opened_flag=1; +//************************************************* +static char const *const ACCELEMETER_CLASS_NAME = "accelemeter"; +#define CHIP_ENABLE 137 +#if (defined(CONFIG_SENSORS_DMARD03) || defined(CONFIG_SENSORS_DMARD03_MODULE)) +static char const *const GSENSOR_DEVICE_NAME = "dmard03"; +#elif (defined(CONFIG_SENSORS_DMARD08) || defined(CONFIG_SENSORS_DMARD08_MODULE) || defined(CONFIG_WMT_SENSOR_DMT08)) +static char const *const GSENSOR_DEVICE_NAME = "dmard08"; +#endif + +static int device_init(void); +static void device_exit(void); + +static int device_open(struct inode*, struct file*); +static ssize_t device_write(struct file*, const char*, size_t, loff_t*); +static ssize_t device_read(struct file*, char*, size_t, loff_t*); +static long device_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); +static int device_close(struct inode*, struct file*); + +//static int device_i2c_suspend(struct i2c_client *client, pm_message_t mesg); +//static int device_i2c_resume(struct i2c_client *client); +static int __devinit device_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id); +static int __devexit device_i2c_remove(struct i2c_client *client); +static inline void device_i2c_correct_accel_sign(s16 *val); +void device_i2c_read_xyz(struct i2c_client *client, s16 *xyz); +void device_i2c_merge_register_values(struct i2c_client *client, s16 *val, u8 msb, u8 lsb); + +struct input_dev *input; + +static int DMT_GetOpenStatus(void) +{ +#if DMT_DEBUG_DATA + IN_FUNC_MSG; + printk("%s:start active=%d\n",__func__,active.counter); +#endif + wait_event_interruptible(open_wq, (atomic_read(&active) != 0)); + return 0; +} + +static int DMT_GetCloseStatus(void) +{ +#if DMT_DEBUG_DATA + IN_FUNC_MSG; + printk("%s:start active=%d\n",__func__,active.counter); +#endif + wait_event_interruptible(open_wq, (atomic_read(&active) <= 0)); + return 0; +} + +static void DMT_sysfs_update_active_status(int en) +{ + unsigned long dmt_delay; + if(en) + { + dmt_delay = msecs_to_jiffies(atomic_read(&delay)); + if(dmt_delay < 1) + dmt_delay = 1; + //printk("schedule_delayed_work start with delay time=%lu\n",dmt_delay); + schedule_delayed_work(&work,dmt_delay); + } + else + cancel_delayed_work_sync(&work); +} + +static ssize_t DMT_enable_acc_show( struct device *dev, struct device_attribute *attr, char *buf) +{ + buf="show"; + return 1; +} + +static ssize_t DMT_enable_acc_store( struct device *dev, struct device_attribute *attr, char const *buf, size_t count) +{ + int en; +#if DMT_DEBUG_DATA + printk("%s:buf=%x %x\n",__func__,buf[0],buf[1]); +#endif + if(buf[0]!= 0x30 && buf[0]!= 0x31) + { + printk("%s:illegle data !!\n",__func__); + return 0; + } + en= (buf[0]-0x30 > 0) ? 1:0; + DMT_sysfs_update_active_status(en); + return 1; +} + +static ssize_t DMT_delay_acc_show( struct device *dev, struct device_attribute *attr, char *buf){ + return 1; +} + +static ssize_t DMT_delay_acc_store( struct device *dev, struct device_attribute *attr,char const *buf, size_t count) +{ + int error; + unsigned long data; + error = strict_strtoul(buf, 10, &data); + if(error) { + pr_err("%s strict_strtoul error\n", __FUNCTION__); + return -1; + } + mutex_lock(&DMT_mutex); + interval=(unsigned int)data; + mutex_unlock(&DMT_mutex); + atomic_set(&delay, (unsigned int) data); +#if DMT_DEBUG_DATA + printk("Driver attribute set delay =%lu\n",data); +#endif + return 1; +} + +static struct device_attribute DMT_attributes[] = { + __ATTR(enable_acc, 0755, DMT_enable_acc_show, DMT_enable_acc_store), + __ATTR(delay_acc, 0755, DMT_delay_acc_show, DMT_delay_acc_store), + __ATTR_NULL, +}; + +static int create_device_attributes(struct device *dev, struct device_attribute *attrs) +{ + int i; + int err = 0; +#if DMT_DEBUG_DATA + IN_FUNC_MSG; +#endif + for (i = 0 ; NULL != attrs[i].attr.name ; ++i) { + err = device_create_file(dev, &attrs[i]); + if (0 != err) + break; + } + + if (0 != err) { + for (; i >= 0 ; --i) + device_remove_file(dev, &attrs[i]); + } + return err; +} + +int input_init(void) +{ + int err=0; + input=input_allocate_device(); + if (!input) + return -ENOMEM; + else + printk("input device allocate Success !!\n"); + /* Setup input device */ + set_bit(EV_ABS, input->evbit); + /* Accelerometer [-78.5, 78.5]m/s2 in Q16*/ + input_set_abs_params(input, ABS_X, -1024, 1024, 0, 0); + input_set_abs_params(input, ABS_Y, -1024, 1024, 0, 0); + input_set_abs_params(input, ABS_Z, -1024, 1024, 0, 0); + + /* Set name */ + input->name = "g-sensor"; + + /* Register */ + err = input_register_device(input); + if (err) { + input_free_device(input); + return err; + } + atomic_set(&active, 0); +#if DMT_DEBUG_DATA + printk("in driver ,active=%d\n",active.counter); +#endif + init_waitqueue_head(&open_wq); + + return err; +} + +typedef union { + struct { + s16 x; + s16 y; + s16 z; + } u; + s16 v[SENSOR_DATA_SIZE]; +} raw_data; +static raw_data offset; + +struct dev_data { + dev_t devno; + struct cdev cdev; + struct class *class; + struct i2c_client *client; +}; +static struct dev_data dev; + +s16 sensorlayout[3][3] = { +#if defined(CONFIG_GSEN_LAYOUT_PAT_1) + { 1, 0, 0}, { 0, 1, 0}, { 0, 0, 1}, +#elif defined(CONFIG_GSEN_LAYOUT_PAT_2) + { 0, 1, 0}, {-1, 0, 0}, { 0, 0, 1}, +#elif defined(CONFIG_GSEN_LAYOUT_PAT_3) + {-1, 0, 0}, { 0,-1, 0}, { 0, 0, 1}, +#elif defined(CONFIG_GSEN_LAYOUT_PAT_4) + { 0,-1, 0}, { 1, 0, 0}, { 0, 0, 1}, +#elif defined(CONFIG_GSEN_LAYOUT_PAT_5) + {-1, 0, 0}, { 0, 1, 0}, { 0, 0,-1}, +#elif defined(CONFIG_GSEN_LAYOUT_PAT_6) + { 0,-1, 0}, {-1, 0, 0}, { 0, 0,-1}, +#elif defined(CONFIG_GSEN_LAYOUT_PAT_7) + { 1, 0, 0}, { 0,-1, 0}, { 0, 0,-1}, +#elif defined(CONFIG_GSEN_LAYOUT_PAT_8) + { 0, 1, 0}, { 1, 0, 0}, { 0, 0,-1}, +#endif +}; + +void gsensor_read_accel_avg(int num_avg, raw_data *avg_p ) +{ + long xyz_acc[SENSOR_DATA_SIZE]; + s16 xyz[SENSOR_DATA_SIZE]; + int i, j; + + //initialize the accumulation buffer + for(i = 0; i < SENSOR_DATA_SIZE; ++i) + xyz_acc[i] = 0; + + for(i = 0; i < num_avg; i++) + { + device_i2c_read_xyz(dev.client, (s16 *)&xyz); + for(j = 0; j < SENSOR_DATA_SIZE; j++) + xyz_acc[j] += xyz[j]; + } + + // calculate averages + for(i = 0; i < SENSOR_DATA_SIZE; i++) + avg_p->v[i] = (s16) (xyz_acc[i] / num_avg); +} +/* calc delta offset */ +int gsensor_calculate_offset(int gAxis,raw_data avg) +{ + switch(gAxis) + { + case CONFIG_GSEN_CALIBRATION_GRAVITY_ON_Z_NEGATIVE: + offset.u.x = avg.u.x ; + offset.u.y = avg.u.y ; + offset.u.z = avg.u.z + DEFAULT_SENSITIVITY; + break; + case CONFIG_GSEN_CALIBRATION_GRAVITY_ON_X_POSITIVE: + offset.u.x = avg.u.x + DEFAULT_SENSITIVITY; + offset.u.y = avg.u.y ; + offset.u.z = avg.u.z ; + break; + case CONFIG_GSEN_CALIBRATION_GRAVITY_ON_Z_POSITIVE: + offset.u.x = avg.u.x ; + offset.u.y = avg.u.y ; + offset.u.z = avg.u.z - DEFAULT_SENSITIVITY; + break; + case CONFIG_GSEN_CALIBRATION_GRAVITY_ON_X_NEGATIVE: + offset.u.x = avg.u.x - DEFAULT_SENSITIVITY; + offset.u.y = avg.u.y ; + offset.u.z = avg.u.z ; + break; + case CONFIG_GSEN_CALIBRATION_GRAVITY_ON_Y_NEGATIVE: + offset.u.x = avg.u.x ; + offset.u.y = avg.u.y + DEFAULT_SENSITIVITY; + offset.u.z = avg.u.z ; + break; + case CONFIG_GSEN_CALIBRATION_GRAVITY_ON_Y_POSITIVE: + offset.u.x = avg.u.x ; + offset.u.y = avg.u.y - DEFAULT_SENSITIVITY; + offset.u.z = avg.u.z ; + break; + default: + return -ENOTTY; + } + return 0; +} + +void gsensor_calibrate(int side){ + raw_data avg; + int avg_num = 16; +#if DMT_DEBUG_DATA + IN_FUNC_MSG; +#endif + // get acceleration average reading + gsensor_read_accel_avg(avg_num, &avg); + // calculate and set the offset + gsensor_calculate_offset(side, avg); +} + +void ce_on(void){ + //omap_mux_set_gpio(OMAP_PIN_INPUT_PULLUP, CHIP_ENABLE); +} + +void ce_off(void){ + //omap_mux_set_gpio(OMAP_PIN_INPUT_PULLDOWN, CHIP_ENABLE); +} + +void gsensor_reset(void){ + ce_off(); + msleep(300); + ce_on(); +} + +void gsensor_set_offset(int val[3]) +{ + int i; +#if DMT_DEBUG_DATA + IN_FUNC_MSG; +#endif + for(i = 0; i < SENSOR_DATA_SIZE; ++i) + offset.v[i] = (s16) val[i]; +} + +static int device_i2c_suspend(struct i2c_client *client, pm_message_t mesg) +{ + return 0; +} + +static int device_i2c_resume(struct i2c_client *client) +{ + return 0; +} + +static void device_i2c_shutdown(struct i2c_client *client) +{ + flush_delayed_work_sync(&work); + cancel_delayed_work_sync(&work); +} + +struct file_operations dmt_g_sensor_fops = { + .owner = THIS_MODULE, + .read = device_read, + .write = device_write, + .unlocked_ioctl = device_ioctl, + .open = device_open, + .release = device_close, +}; + +static const struct i2c_device_id device_i2c_ids[] = { + {DEVICE_I2C_NAME, 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, device_i2c_ids); + +static struct i2c_driver device_i2c_driver = { + .driver = { + .owner = THIS_MODULE, + .name = DEVICE_I2C_NAME, + }, + .class = I2C_CLASS_HWMON, + .probe = device_i2c_probe, + .remove = __devexit_p(device_i2c_remove), + .suspend = device_i2c_suspend, + .resume = device_i2c_resume, + .shutdown = device_i2c_shutdown, + .id_table = device_i2c_ids, +}; + +static int device_open(struct inode *inode, struct file *filp){ +#if DMT_DEBUG_DATA + IN_FUNC_MSG; +#endif + //Device_First_Time_Opened_flag + if(Device_First_Time_Opened_flag) + { + Device_First_Time_Opened_flag=0; + //gsensor_read_offset_from_file(); + } + return 0; +} + +static ssize_t device_write(struct file *filp, const char *buf, size_t count, loff_t *f_pos) +{ + return 0; +} + +static ssize_t device_read(struct file *filp, char *buf, size_t count, loff_t *f_pos) +{ + s16 xyz[SENSOR_DATA_SIZE]; + int i; + + device_i2c_read_xyz(dev.client, (s16 *)&xyz); + //offset compensation + for(i = 0; i < SENSOR_DATA_SIZE; ++i) + xyz[i] -= offset.v[i]; + + if(copy_to_user(buf, &xyz, count)) + return -EFAULT; +#if DMT_DEBUG_DATA + IN_FUNC_MSG; + PRINT_X_Y_Z(xyz[0], xyz[1], xyz[2]); +#endif + + return count; +} + +static long device_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + int err = 0, ret = 0, i; + int intBuf[SENSOR_DATA_SIZE]; + s16 xyz[SENSOR_DATA_SIZE]; + //check type and number + if (_IOC_TYPE(cmd) != IOCTL_MAGIC) return -ENOTTY; + if (_IOC_NR(cmd) > SENSOR_MAXNR) return -ENOTTY; + + //check user space pointer is valid + if (_IOC_DIR(cmd) & _IOC_READ) + err = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd)); + else if (_IOC_DIR(cmd) & _IOC_WRITE) + err = !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd)); + if (err) return -EFAULT; + + switch(cmd) + { + case SENSOR_RESET: + //gsensor_reset(); + printk("RUN RESET"); + return ret; + + case SENSOR_CALIBRATION: + // get orientation info + if(copy_from_user(&intBuf, (int*)arg, sizeof(int))) return -EFAULT; + gsensor_calibrate(intBuf[0]); + // write in to file + gsensor_write_offset_to_file(); + + // return the offset + for(i = 0; i < SENSOR_DATA_SIZE; ++i) + intBuf[i] = offset.v[i]; + + ret = copy_to_user((int *)arg, &intBuf, sizeof(intBuf)); + return ret; + + case SENSOR_GET_OFFSET: + // get offset from file + gsensor_read_offset_from_file(); + + for(i = 0; i < SENSOR_DATA_SIZE; ++i) + intBuf[i] = offset.v[i]; + + ret = copy_to_user((int *)arg, &intBuf, sizeof(intBuf)); + return ret; + + case SENSOR_SET_OFFSET: + ret = copy_from_user(&intBuf, (int *)arg, sizeof(intBuf)); + gsensor_set_offset(intBuf); + // write in to file + gsensor_write_offset_to_file(); + return ret; + + case SENSOR_READ_ACCEL_XYZ: + device_i2c_read_xyz(dev.client, (s16 *)&xyz); + for(i = 0; i < SENSOR_DATA_SIZE; ++i) + intBuf[i] = xyz[i] - offset.v[i]; + + ret = copy_to_user((int*)arg, &intBuf, sizeof(intBuf)); + return ret; + case SENSOR_SETYPR: + if(copy_from_user(&intBuf, (int*)arg, sizeof(intBuf))) + { + printk("%s:copy_from_user(&intBuf, (int*)arg, sizeof(intBuf)) ERROR, -EFAULT\n",__func__); + return -EFAULT; + } + input_report_abs(input, ABS_X, intBuf[0]); + input_report_abs(input, ABS_Y, intBuf[1]); + input_report_abs(input, ABS_Z, intBuf[2]); + input_sync(input); + //printk("%s:SENSOR_SETYPR OK! x=%d,y=%d,z=%d\n",__func__,intBuf[0],intBuf[1],intBuf[2]); + + return 1; + case SENSOR_GET_OPEN_STATUS: + //printk("%s:Going into DMT_GetOpenStatus()\n",__func__); + DMT_GetOpenStatus(); + //printk("%s:DMT_GetOpenStatus() finished\n",__func__); + return 1; + break; + case SENSOR_GET_CLOSE_STATUS: + //printk("%s:Going into DMT_GetCloseStatus()\n",__func__); + DMT_GetCloseStatus(); + //printk("%s:DMT_GetCloseStatus() finished\n",__func__); + return 1; + break; + case SENSOR_GET_DELAY: + + ret = copy_to_user((int*)arg, &interval, sizeof(interval)); + return 1; + break; + default: /* redundant, as cmd was checked against MAXNR */ + return -ENOTTY; + } + + return 0; +} + +static int device_close(struct inode *inode, struct file *filp) +{ + printk("Close device\n"); + return 0; +} + +static int device_i2c_xyz_read_reg(struct i2c_client *client,u8 *buffer, int length) +{ + struct i2c_msg msg[] = + { + {.addr = client->addr, .flags = 0, .len = 1, .buf = buffer,}, + {.addr = client->addr, .flags = I2C_M_RD, .len = length, .buf = buffer,}, + }; +#if DMT_DEBUG_DATA + IN_FUNC_MSG; +#endif + return i2c_transfer(client->adapter, msg, 2); +} + +void device_i2c_read_xyz(struct i2c_client *client, s16 *xyz_p) +{ + u8 buffer[6]; + s16 xyzTmp[SENSOR_DATA_SIZE]; + int i, j; + + //get xyz high/low bytes, 0x02~0x07 + buffer[0] = 2; + device_i2c_xyz_read_reg(client, buffer, 6); + + //merge to 11-bits value + for(i = 0; i < SENSOR_DATA_SIZE; ++i){ + xyz_p[i] = 0; + device_i2c_merge_register_values(client, (xyzTmp + i), buffer[2*i], buffer[2*i + 1]); + //transfer to the default layout + for(j = 0; j < 3; j++) + xyz_p[i] += sensorlayout[i][j] * xyzTmp[j]; + } +} + +void device_i2c_merge_register_values(struct i2c_client *client, s16 *val, u8 msb, u8 lsb) +{ + *val = (((u16)msb) << 3) | (u16)lsb; + device_i2c_correct_accel_sign(val); +} + +static inline void device_i2c_correct_accel_sign(s16 *val) +{ + *val<<= (sizeof(s16) * BITS_PER_BYTE - 11); + *val>>= (sizeof(s16) * BITS_PER_BYTE - 11); +} + +static void DMT_work_func(struct work_struct *fakework) +{ + int i; + static int firsttime=0; + s16 xyz[SENSOR_DATA_SIZE]; + unsigned long t=atomic_read(&delay); + unsigned long dmt_delay = msecs_to_jiffies(t); + if(!firsttime) + { + //gsensor_read_offset_from_file(); + firsttime=1; + } + + //dmt_delay/=1000; + +#if DMT_DEBUG_DATA + IN_FUNC_MSG; + printk("t=%lu ,dmt_delay=%lu\n",t,dmt_delay); +#endif + + device_i2c_read_xyz(dev.client, (s16 *)&xyz); + for(i = 0; i < SENSOR_DATA_SIZE; ++i) + xyz[i] -= offset.v[i]; + +#if DMT_DEBUG_DATA + printk("x: %d, y: %d, z: %d\n", xyz[0], xyz[1], xyz[2]); +#endif + input_report_abs(input, ABS_X, xyz[gs_conf.xyz_axis[ABS_X][0]]*gs_conf.xyz_axis[ABS_X][1]); + input_report_abs(input, ABS_Y, xyz[gs_conf.xyz_axis[ABS_Y][0]]*gs_conf.xyz_axis[ABS_Y][1]); + input_report_abs(input, ABS_Z, xyz[gs_conf.xyz_axis[ABS_Z][0]]*gs_conf.xyz_axis[ABS_Z][1]); + input_sync(input); + + if(dmt_delay<1) + dmt_delay=1; + + schedule_delayed_work(&work, dmt_delay); +} + + +static int __devinit device_i2c_probe(struct i2c_client *client,const struct i2c_device_id *id) +{ + u8 buffer[4]; + int i; + + for(i = 0; i < SENSOR_DATA_SIZE; ++i) + offset.v[i] = 0; + + if(!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + { + printk("%s, functionality check failed\n", __func__); + return -1; + } + + gsensor_reset(); + + buffer[0] = CONTROL_REGISTERS; + device_i2c_xyz_read_reg(client, buffer, 4); + if( buffer[0] == 0x00 && buffer[1] == 0x00 && buffer[2] == 0x88 && buffer[3] == 0x08) + { + printk(KERN_INFO "i2c Read 0x08 = %d!\n",buffer[0]); + printk(KERN_INFO "i2c Read 0x09 = %d!\n",buffer[1]); + printk(KERN_INFO "i2c Read 0x0a = %d!\n",buffer[2]); + printk(KERN_INFO "i2c Read 0x0b = %d!\n",buffer[3]); + printk(KERN_INFO "@@@ %s DMT_DEVICE_NAME registered I2C driver!\n",__FUNCTION__); + dev.client = client; + } + else + { + printk(KERN_INFO "err : i2c Read 0x08 = %d!\n",buffer[0]); + printk(KERN_INFO "err : i2c Read 0x09 = %d!\n",buffer[1]); + printk(KERN_INFO "err : i2c Read 0x0a = %d!\n",buffer[2]); + printk(KERN_INFO "err : i2c Read 0x0b = %d!\n",buffer[3]); + dev.client = NULL; + return -1; + } +#if DMT_DEBUG_DATA + IN_FUNC_MSG; + //check sensorlayout[i][j] + for(i = 0; i < 3; ++i) + { + for(j = 0; j < 3; j++) + printk("%d",sensorlayout[i][j]); + printk("\n"); + } +#endif + return 0; +} + +static int __devexit device_i2c_remove(struct i2c_client *client) +{ +#if DMT_DEBUG_DATA + IN_FUNC_MSG; +#endif + return 0; +} + +int dmt08_enable(int en) +{ + printk(KERN_DEBUG "%s: enable = %d\n", __func__, en); + DMT_sysfs_update_active_status(en); + return 0; +} + +int dmt08_setDelay(int mdelay) +{ + printk(KERN_DEBUG "%s: delay = %d\n", __func__, mdelay); + atomic_set(&delay, mdelay); + return 0; +} + +int dmt08_getLSG(int *lsg) +{ + *lsg = 256; + return 0; +} + +struct gsensor_data dmt08_gs_data = { + .i2c_addr = DMT08_I2C_ADDR, + .enable = dmt08_enable, + .setDelay = dmt08_setDelay, + .getLSG = dmt08_getLSG, +}; + +static int __init device_init(void) +{ + int err=-1; + struct device *device; + int ret = 0; + IN_FUNC_MSG; + + if (get_gsensor_conf(&gs_conf)) + return -1; + + if (gs_conf.op != 1) + return -1; + + printk("G-Sensor dmt08 init\n"); + + if (gsensor_register(&dmt08_gs_data)) + return -1; + + if (gsensor_i2c_register_device() < 0) + return -1; + + atomic_set(&delay, 200); + + ret = alloc_chrdev_region(&dev.devno, 0, 1, GSENSOR_DEVICE_NAME); + if(ret) + { + printk("%s, can't allocate chrdev\n", __func__); + return ret; + } + printk("%s, register chrdev(%d, %d)\n", __func__, MAJOR(dev.devno), MINOR(dev.devno)); + + cdev_init(&dev.cdev, &dmt_g_sensor_fops); + dev.cdev.owner = THIS_MODULE; + ret = cdev_add(&dev.cdev, dev.devno, 1); + if(ret < 0) + { + printk("%s, add character device error, ret %d\n", __func__, ret); + return ret; + } + dev.class = class_create(THIS_MODULE, ACCELEMETER_CLASS_NAME); + if(IS_ERR(dev.class)) + { + printk("%s, create class, error\n", __func__); + return ret; + } + device=device_create(dev.class, NULL, dev.devno, NULL, GSENSOR_DEVICE_NAME); + + mutex_init(&DMT_mutex); + + INIT_DELAYED_WORK(&work, DMT_work_func); + printk("DMT: INIT_DELAYED_WORK\n"); + + err=input_init(); + if(err) + { + printk("%s:input_init fail, error code= %d\n", __func__, err); + } + + err = create_device_attributes(device,DMT_attributes); + + return i2c_add_driver(&device_i2c_driver); +} + + +static void __exit device_exit(void) +{ + IN_FUNC_MSG; + input_unregister_device(input); + input_free_device(input); + cdev_del(&dev.cdev); + unregister_chrdev_region(dev.devno, 1); + device_destroy(dev.class, dev.devno); + class_destroy(dev.class); + i2c_del_driver(&device_i2c_driver); +} + + +void gsensor_write_offset_to_file(void) +{ + char data[18]; + unsigned int orgfs; + long lfile=-1; + spinlock_t lock; //add by yang + spin_lock_init(&lock); //add by yang + sprintf(data,"%5d %5d %5d",offset.u.x,offset.u.y,offset.u.z); + + orgfs = get_fs(); +// Set segment descriptor associated to kernel space + set_fs(KERNEL_DS); + + lfile=sys_open(OffsetFileName,O_WRONLY|O_CREAT, 0777); + if (lfile < 0) + { + printk("sys_open %s error!!. %ld\n",OffsetFileName,lfile); + } + else + { + spin_lock(&lock); //add by yang + sys_write(lfile, data,18); + sys_close(lfile); + spin_unlock(&lock); //add by yang + } + set_fs(orgfs); +} + +void gsensor_read_offset_from_file(void) +{ + unsigned int orgfs; + char data[18]; + long lfile=-1; + orgfs = get_fs(); +// Set segment descriptor associated to kernel space + set_fs(KERNEL_DS); + + lfile = sys_open(OffsetFileName, O_RDONLY, 0); + if (lfile < 0) + { + printk("sys_open %s error!!. %ld\n",OffsetFileName,lfile); + strcpy(data,"00000 00000 00000"); + + } + else + { + sys_read(lfile, data, 18); + sys_close(lfile); + } + sscanf(data,"%hd %hd %hd",&offset.u.x,&offset.u.y,&offset.u.z); + printk("%5d %5d %5d",offset.u.x,offset.u.y,offset.u.z); + set_fs(orgfs); +} + +MODULE_AUTHOR("DMT_RD"); +MODULE_DESCRIPTION("DMT Gsensor Driver"); +MODULE_LICENSE("GPL"); + +module_init(device_init); +module_exit(device_exit); diff --git a/drivers/input/sensor/TP_DRIVER_NOT_USE/dmt08_gsensor/dmt08.h b/drivers/input/sensor/TP_DRIVER_NOT_USE/dmt08_gsensor/dmt08.h new file mode 100755 index 00000000..5a1ac3b7 --- /dev/null +++ b/drivers/input/sensor/TP_DRIVER_NOT_USE/dmt08_gsensor/dmt08.h @@ -0,0 +1,105 @@ +/* + * @file include/linux/dmt.h + * @brief DMARD05 & DMARD06 & DMARD07 g-sensor Linux device driver + * @author Domintech Technology Co., Ltd (http://www.domintech.com.tw) + * @version 1.31 + * @date 2012/3/27 + * + * @section LICENSE + * + * Copyright 2011 Domintech Technology Co., Ltd + * + * 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. + * + * + */ +#ifndef DMT_H +#define DMT_H + +#if (defined(CONFIG_SENSORS_DMARD05) || defined(CONFIG_SENSORS_DMARD05_MODULE)) +#define DEVICE_I2C_NAME "dmard05" +#define DEFAULT_SENSITIVITY 64 +#define WHO_AM_I_VALUE 0x05 +#define X_OUT 0x41 +#define SW_RESET 0x53 +#define WHO_AM_I 0x0f +#elif (defined(CONFIG_SENSORS_DMARD06) || defined(CONFIG_SENSORS_DMARD06_MODULE)) +#define DEVICE_I2C_NAME "dmard06" +#define DEFAULT_SENSITIVITY 32 +#define WHO_AM_I_VALUE 0x06 +#define X_OUT 0x41 +#define SW_RESET 0x53 +#define WHO_AM_I 0x0f +#elif (defined(CONFIG_SENSORS_DMARD07) || defined(CONFIG_SENSORS_DMARD07_MODULE)) +#define DEVICE_I2C_NAME "dmard07" +#define DEFAULT_SENSITIVITY 64 +#define WHO_AM_I_VALUE 0x07 +#define X_OUT 0x41 +#define SW_RESET 0x53 +#define WHO_AM_I 0x0f +#elif (defined(CONFIG_SENSORS_DMARD03) || defined(CONFIG_SENSORS_DMARD03_MODULE)) +#define DEVICE_I2C_NAME "dmard03" +#define DEFAULT_SENSITIVITY 256 +#define CONTROL_REGISTERS 0x08 +#elif (defined(CONFIG_SENSORS_DMARD08) || defined(CONFIG_SENSORS_DMARD08_MODULE) || defined(CONFIG_WMT_SENSOR_DMT08)) +#define DEVICE_I2C_NAME "g-sensor" +#define DEFAULT_SENSITIVITY 256 +#define CONTROL_REGISTERS 0x08 +#define DMT08_I2C_ADDR 0x1c +#endif + +//#define DMT_DEBUG_DATA 1 +#define DMT_DEBUG_DATA 0 + +#if DMT_DEBUG_DATA +#define IN_FUNC_MSG printk(KERN_INFO "@DMT@ In %s\n", __func__) +#define PRINT_X_Y_Z(x, y, z) printk(KERN_INFO "@DMT@ X/Y/Z axis: %04d , %04d , %04d\n", (x), (y), (z)) +#define PRINT_OFFSET(x, y, z) printk(KERN_INFO "@offset@ X/Y/Z axis: %04d , %04d , %04d\n",offset.x,offset.y,offset.z); +#else +#define IN_FUNC_MSG +#define PRINT_X_Y_Z(x, y, z) +#define PRINT_OFFSET(x, y, z) +#endif + +//g-senor layout configuration, choose one of the following configuration +#define CONFIG_GSEN_LAYOUT_PAT_1 +//#define CONFIG_GSEN_LAYOUT_PAT_2 +//#define CONFIG_GSEN_LAYOUT_PAT_3 +//#define CONFIG_GSEN_LAYOUT_PAT_4 +//#define CONFIG_GSEN_LAYOUT_PAT_5 +//#define CONFIG_GSEN_LAYOUT_PAT_6 +//#define CONFIG_GSEN_LAYOUT_PAT_7 +//#define CONFIG_GSEN_LAYOUT_PAT_8 + +#define CONFIG_GSEN_CALIBRATION_GRAVITY_ON_Z_NEGATIVE 1 +#define CONFIG_GSEN_CALIBRATION_GRAVITY_ON_Z_POSITIVE 2 +#define CONFIG_GSEN_CALIBRATION_GRAVITY_ON_Y_NEGATIVE 3 +#define CONFIG_GSEN_CALIBRATION_GRAVITY_ON_Y_POSITIVE 4 +#define CONFIG_GSEN_CALIBRATION_GRAVITY_ON_X_NEGATIVE 5 +#define CONFIG_GSEN_CALIBRATION_GRAVITY_ON_X_POSITIVE 6 + +#define AVG_NUM 16 + +#define IOCTL_MAGIC 0x09 +#define SENSOR_DATA_SIZE 3 + +#define SENSOR_RESET _IO(IOCTL_MAGIC, 0) +#define SENSOR_CALIBRATION _IOWR(IOCTL_MAGIC, 1, int[SENSOR_DATA_SIZE]) +#define SENSOR_GET_OFFSET _IOR(IOCTL_MAGIC, 2, int[SENSOR_DATA_SIZE]) +#define SENSOR_SET_OFFSET _IOWR(IOCTL_MAGIC, 3, int[SENSOR_DATA_SIZE]) +#define SENSOR_READ_ACCEL_XYZ _IOR(IOCTL_MAGIC, 4, int[SENSOR_DATA_SIZE]) +#define SENSOR_SETYPR _IOW(IOCTL_MAGIC, 5, int[SENSOR_DATA_SIZE]) +#define SENSOR_GET_OPEN_STATUS _IO(IOCTL_MAGIC, 6) +#define SENSOR_GET_CLOSE_STATUS _IO(IOCTL_MAGIC, 7) +#define SENSOR_GET_DELAY _IOR(IOCTL_MAGIC, 8, unsigned int*) + +#define SENSOR_MAXNR 8 + +#endif diff --git a/drivers/input/sensor/TP_DRIVER_NOT_USE/dmt10_gsensor/Makefile b/drivers/input/sensor/TP_DRIVER_NOT_USE/dmt10_gsensor/Makefile new file mode 100755 index 00000000..de723bf5 --- /dev/null +++ b/drivers/input/sensor/TP_DRIVER_NOT_USE/dmt10_gsensor/Makefile @@ -0,0 +1,6 @@ +# +# Makefile for the DMARD10 Sensor driver +# + +sensor_dmt10-objs := dmt10.o +obj-$(CONFIG_WMT_SENSOR_DMT10) += sensor_dmt10.o diff --git a/drivers/input/sensor/TP_DRIVER_NOT_USE/dmt10_gsensor/dmt10.c b/drivers/input/sensor/TP_DRIVER_NOT_USE/dmt10_gsensor/dmt10.c new file mode 100755 index 00000000..e8e7b288 --- /dev/null +++ b/drivers/input/sensor/TP_DRIVER_NOT_USE/dmt10_gsensor/dmt10.c @@ -0,0 +1,950 @@ +/* + * @file drivers/misc/dmt10.c + * @brief DMT g-sensor Linux device driver + * @author Domintech Technology Co., Ltd (http://www.domintech.com.tw) + * @version 1.03 + * + * @section LICENSE + * + * Copyright 2012 Domintech Technology Co., Ltd + * + * 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. + * + * V1.00 D10 First Release date 2012/09/21 + * V1.01 static struct dmt_data s_dmt Refresh to device_i2c_probe date 2012/11/23 + * V1.02 0x0D cck : adjustment 204.8KHz core clock date 2012/11/30 + * V1.03 write TCGYZ & TCGX : set value to 0x00 date 2012/12/10 + * + * @DMT Package version D10_General_driver v1.4 + * + */ +#include "dmt10.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../gsensor.h" + +static struct gsensor_conf gs_conf; + +static unsigned int interval; +void gsensor_write_offset_to_file(void); +void gsensor_read_offset_from_file(void); +char OffsetFileName[] = "/data/misc/dmt/offset.txt"; /* FILE offset.txt */ +static raw_data offset; +static struct dmt_data *s_dmt; +static int device_init(void); +static void device_exit(void); + +static int device_open(struct inode*, struct file*); +static long device_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); +static int device_close(struct inode*, struct file*); + +static int device_i2c_suspend(struct i2c_client *client, pm_message_t mesg); +static int device_i2c_resume(struct i2c_client *client); +static int __devinit device_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id); +static int __devexit device_i2c_remove(struct i2c_client *client); +void device_i2c_read_xyz(struct i2c_client *client, s16 *xyz); +static int device_i2c_rxdata(struct i2c_client *client, unsigned char *rxDat, int length); +static int device_i2c_txdata(struct i2c_client *client, unsigned char *txData, int length); + +static int DMT_GetOpenStatus(struct i2c_client *client){ + struct dmt_data *dmt = i2c_get_clientdata(client); + GSE_LOG("start active=%d\n",dmt->active.counter); + wait_event_interruptible(dmt->open_wq, (atomic_read(&dmt->active) != 0)); + return 0; +} + +static int DMT_GetCloseStatus(struct i2c_client *client){ + struct dmt_data *dmt = i2c_get_clientdata(client); + GSE_LOG("start active=%d\n",dmt->active.counter); + wait_event_interruptible(dmt->open_wq, (atomic_read(&dmt->active) <= 0)); + return 0; +} + +static void DMT_sysfs_update_active_status(struct dmt_data *dmt , int en){ + unsigned long dmt_delay; + if(en){ + dmt_delay = msecs_to_jiffies(atomic_read(&dmt->delay)); + if(dmt_delay < 1) + dmt_delay = 1; + + GSE_LOG("schedule_delayed_work start with delay time=%lu\n",dmt_delay); + schedule_delayed_work(&dmt->delaywork,dmt_delay); + } + else + cancel_delayed_work_sync(&dmt->delaywork); +} + +static bool get_value_as_int(char const *buf, size_t size, int *value){ + long tmp; + if (size == 0) + return false; + /* maybe text format value */ + if ((buf[0] == '0') && (size > 1)) { + if ((buf[1] == 'x') || (buf[1] == 'X')) { + /* hexadecimal format */ + if (0 != strict_strtol(buf, 16, &tmp)) + return false; + } else { + /* octal format */ + if (0 != strict_strtol(buf, 8, &tmp)) + return false; + } + } else { + /* decimal format */ + if (0 != strict_strtol(buf, 10, &tmp)) + return false; + } + + if (tmp > INT_MAX) + return false; + + *value = tmp; + return true; +} +static bool get_value_as_int64(char const *buf, size_t size, long long *value) +{ + long long tmp; + if (size == 0) + return false; + /* maybe text format value */ + if ((buf[0] == '0') && (size > 1)) { + if ((buf[1] == 'x') || (buf[1] == 'X')) { + /* hexadecimal format */ + if (0 != strict_strtoll(buf, 16, &tmp)) + return false; + } else { + /* octal format */ + if (0 != strict_strtoll(buf, 8, &tmp)) + return false; + } + } else { + /* decimal format */ + if (0 != strict_strtoll(buf, 10, &tmp)) + return false; + } + + if (tmp > LLONG_MAX) + return false; + + *value = tmp; + return true; +} + +static ssize_t dmt_sysfs_enable_show( + struct dmt_data *dmt, char *buf, int pos) +{ + char str[2][16]={"ACC enable OFF","ACC enable ON"}; + int flag; + flag=atomic_read(&dmt->enable); + return sprintf(buf, "%s\n", str[flag]); +} + +static ssize_t dmt_sysfs_enable_store( + struct dmt_data *dmt, char const *buf, size_t count, int pos) +{ + int en = 0; + if (NULL == buf) + return -EINVAL; + GSE_LOG("buf=%x %x\n", buf[0], buf[1]); + if (0 == count) + return 0; + + if (false == get_value_as_int(buf, count, &en)) + return -EINVAL; + + en = en ? 1 : 0; + + atomic_set(&dmt->enable,en); + DMT_sysfs_update_active_status(dmt , en); + return count; +} + +/***** Acceleration ***/ +static ssize_t DMT_enable_acc_show(struct device *dev, struct device_attribute *attr, char *buf){ + return dmt_sysfs_enable_show( dev_get_drvdata(dev), buf, ACC_DATA_FLAG); +} + +static ssize_t DMT_enable_acc_store( struct device *dev, struct device_attribute *attr, char const *buf, size_t count){ + return dmt_sysfs_enable_store( dev_get_drvdata(dev), buf, count, ACC_DATA_FLAG); +} + +/***** sysfs delay **************************************************/ +static ssize_t dmt_sysfs_delay_show( struct dmt_data *dmt, char *buf, int pos){ + return sprintf(buf, "%d\n", atomic_read(&dmt->delay)); +} + +static ssize_t dmt_sysfs_delay_store( struct dmt_data *dmt, char const *buf, size_t count, int pos){ + long long val = 0; + + if (NULL == buf) + return -EINVAL; + + if (0 == count) + return 0; + + if (false == get_value_as_int64(buf, count, &val)) + return -EINVAL; + + atomic_set(&dmt->delay, (unsigned int) val); + GSE_LOG("Driver attribute set delay =%lld\n", val); + + return count; +} + +/***** Accelerometer ***/ +static ssize_t DMT_delay_acc_show( struct device *dev, struct device_attribute *attr, char *buf){ + return dmt_sysfs_delay_show( dev_get_drvdata(dev), buf, ACC_DATA_FLAG); +} + +static ssize_t DMT_delay_acc_store( struct device *dev, struct device_attribute *attr,char const *buf, size_t count){ + return dmt_sysfs_delay_store( dev_get_drvdata(dev), buf, count, ACC_DATA_FLAG); +} + +static struct device_attribute DMT_attributes[] = { + __ATTR(enable_acc, 0755, DMT_enable_acc_show, DMT_enable_acc_store), + __ATTR(delay_acc, 0755, DMT_delay_acc_show, DMT_delay_acc_store), + __ATTR_NULL, +}; + +static char const *const ACCELEMETER_CLASS_NAME = "accelemeter"; +static char const *const GSENSOR_DEVICE_NAME = "dmard10"; +static char const *const device_link_name = "i2c"; +static dev_t const dmt_device_dev_t = MKDEV(MISC_MAJOR, 240); + +/***** dmt sysfs functions ******************************************/ +static int create_device_attributes(struct device *dev, struct device_attribute *attrs){ + int i; + int err = 0; + for (i = 0 ; NULL != attrs[i].attr.name ; ++i) { + err = device_create_file(dev, &attrs[i]); + if (0 != err) + break; + } + + if (0 != err) { + for (; i >= 0 ; --i) + device_remove_file(dev, &attrs[i]); + } + return err; +} + +static void remove_device_attributes( + struct device *dev, + struct device_attribute *attrs) +{ + int i; + + for (i = 0 ; NULL != attrs[i].attr.name ; ++i) + device_remove_file(dev, &attrs[i]); +} + +static int create_sysfs_interfaces(struct dmt_data *dmt) +{ + int err; + + if (NULL == dmt) + return -EINVAL; + + err = 0; + dmt->class = class_create(THIS_MODULE, ACCELEMETER_CLASS_NAME); + if (IS_ERR(dmt->class)) { + err = PTR_ERR(dmt->class); + goto exit_class_create_failed; + } + + dmt->class_dev = device_create( + dmt->class, + NULL, + dmt_device_dev_t, + dmt, + GSENSOR_DEVICE_NAME); + if (IS_ERR(dmt->class_dev)) { + err = PTR_ERR(dmt->class_dev); + goto exit_class_device_create_failed; + } + + err = sysfs_create_link( + &dmt->class_dev->kobj, + &dmt->client->dev.kobj, + device_link_name); + if (0 > err) + goto exit_sysfs_create_link_failed; + + err = create_device_attributes( + dmt->class_dev, + DMT_attributes); + if (0 > err) + goto exit_device_attributes_create_failed; +#if 0 + err = create_device_binary_attributes( + &dmt->class_dev->kobj, + dmt_bin_attributes); + if (0 > err) + goto exit_device_binary_attributes_create_failed; +#endif + + return err; + +#if 0 +exit_device_binary_attributes_create_failed: + remove_device_attributes(dmt->class_dev, dmt_attributes); +#endif +exit_device_attributes_create_failed: + sysfs_remove_link(&dmt->class_dev->kobj, device_link_name); +exit_sysfs_create_link_failed: + device_destroy(dmt->class, dmt_device_dev_t); +exit_class_device_create_failed: + dmt->class_dev = NULL; + class_destroy(dmt->class); +exit_class_create_failed: + dmt->class = NULL; + return err; +} + +static void remove_sysfs_interfaces(struct dmt_data *dmt) +{ + if (NULL == dmt) + return; + + if (NULL != dmt->class_dev) { + + remove_device_attributes( + dmt->class_dev, + DMT_attributes); + sysfs_remove_link( + &dmt->class_dev->kobj, + device_link_name); + dmt->class_dev = NULL; + } + if (NULL != dmt->class) { + device_destroy( + dmt->class, + dmt_device_dev_t); + class_destroy(dmt->class); + dmt->class = NULL; + } +} + +int input_init(struct i2c_client *client){ + struct dmt_data *dmt = i2c_get_clientdata(client); + int err=0; + dmt->input=input_allocate_device(); + if (!dmt->input){ + GSE_ERR("input device allocate ERROR !!\n"); + return -ENOMEM; + } + else + GSE_LOG("input device allocate Success !!\n"); + /* Setup input device */ + set_bit(EV_ABS, dmt->input->evbit); + /* Accelerometer [-78.5, 78.5]m/s2 in Q16 */ + input_set_abs_params(dmt->input, ABS_X, -1024, 1024, 0, 0); + input_set_abs_params(dmt->input, ABS_Y, -1024, 1024, 0, 0); + input_set_abs_params(dmt->input, ABS_Z, -1024, 1024, 0, 0); + /* Set InputDevice Name */ + dmt->input->name = INPUT_NAME_ACC; + /* Register */ + err = input_register_device(dmt->input); + if (err) { + GSE_ERR("input_register_device ERROR !!\n"); + input_free_device(dmt->input); + return err; + } + GSE_LOG("input_register_device SUCCESS %d !! \n",err); + + return err; +} + +int gsensor_calibrate(void) +{ + //struct dmt_data *dmt = i2c_get_clientdata(client); + raw_data avg; + int i, j; + long xyz_acc[SENSOR_DATA_SIZE]; + s16 xyz[SENSOR_DATA_SIZE]; + + offset.u.x=0; + offset.u.y=0; + offset.u.z=0; + /* initialize the accumulation buffer */ + for(i = 0; i < SENSOR_DATA_SIZE; ++i) + xyz_acc[i] = 0; + + for(i = 0; i < AVG_NUM; i++) { + device_i2c_read_xyz(s_dmt->client, (s16 *)&xyz); + for(j = 0; j < SENSOR_DATA_SIZE; ++j) + xyz_acc[j] += xyz[j]; + } + /* calculate averages */ + for(i = 0; i < SENSOR_DATA_SIZE; ++i) + avg.v[i] = (s16) (xyz_acc[i] / AVG_NUM); + + if(avg.v[2] < 0){ + offset.u.x = avg.v[0] ; + offset.u.y = avg.v[1] ; + offset.u.z = avg.v[2] + DEFAULT_SENSITIVITY; + return CONFIG_GSEN_CALIBRATION_GRAVITY_ON_Z_POSITIVE; + } + else{ + offset.u.x = avg.v[0] ; + offset.u.y = avg.v[1] ; + offset.u.z = avg.v[2] - DEFAULT_SENSITIVITY; + return CONFIG_GSEN_CALIBRATION_GRAVITY_ON_Z_NEGATIVE; + } + return 0; +} + +int gsensor_reset(struct i2c_client *client){ + unsigned char buffer[7], buffer2[2]; + /* 1. check D10 , VALUE_STADR = 0x55 , VALUE_STAINT = 0xAA */ + buffer[0] = REG_STADR; + buffer2[0] = REG_STAINT; + + device_i2c_rxdata(client, buffer, 2); + device_i2c_rxdata(client, buffer2, 2); + + if( buffer[0] == VALUE_STADR || buffer2[0] == VALUE_STAINT){ + GSE_LOG(" REG_STADR_VALUE = %d , REG_STAINT_VALUE = %d\n", buffer[0], buffer2[0]); + } + else{ + GSE_LOG(" REG_STADR_VALUE = %d , REG_STAINT_VALUE = %d \n", buffer[0], buffer2[0]); + return -1; + } + /* 2. Powerdown reset */ + buffer[0] = REG_PD; + buffer[1] = VALUE_PD_RST; + device_i2c_txdata(client, buffer, 2); + /* 3. ACTR => Standby mode => Download OTP to parameter reg => Standby mode => Reset data path => Standby mode */ + buffer[0] = REG_ACTR; + buffer[1] = MODE_Standby; + buffer[2] = MODE_ReadOTP; + buffer[3] = MODE_Standby; + buffer[4] = MODE_ResetDataPath; + buffer[5] = MODE_Standby; + device_i2c_txdata(client, buffer, 6); + /* 4. OSCA_EN = 1 ,TSTO = b'000(INT1 = normal, TEST0 = normal) */ + buffer[0] = REG_MISC2; + buffer[1] = VALUE_MISC2_OSCA_EN; + device_i2c_txdata(client, buffer, 2); + /* 5. AFEN = 1(AFE will powerdown after ADC) */ + buffer[0] = REG_AFEM; + buffer[1] = VALUE_AFEM_AFEN_Normal; + buffer[2] = VALUE_CKSEL_ODR_100_204; + buffer[3] = VALUE_INTC; + buffer[4] = VALUE_TAPNS_Ave_2; + buffer[5] = 0x00; // DLYC, no delay timing + buffer[6] = 0x07; // INTD=1 (push-pull), INTA=1 (active high), AUTOT=1 (enable T) + device_i2c_txdata(client, buffer, 7); + /* 6. write TCGYZ & TCGX */ + buffer[0] = REG_WDAL; // REG:0x01 + buffer[1] = 0x00; // set TC of Y,Z gain value + buffer[2] = 0x00; // set TC of X gain value + buffer[3] = 0x03; // Temperature coefficient of X,Y,Z gain + device_i2c_txdata(client, buffer, 4); + + buffer[0] = REG_ACTR; // REG:0x00 + buffer[1] = MODE_Standby; // Standby + buffer[2] = MODE_WriteOTPBuf; // WriteOTPBuf + buffer[3] = MODE_Standby; // Standby + device_i2c_txdata(client, buffer, 4); + //buffer[0] = REG_TCGYZ; + //device_i2c_rxdata(client, buffer, 2); + //GSE_LOG(" TCGYZ = %d, TCGX = %d \n", buffer[0], buffer[1]); + + /* 7. Activation mode */ + buffer[0] = REG_ACTR; + buffer[1] = MODE_Active; + device_i2c_txdata(client, buffer, 2); + return 0; +} + +void gsensor_set_offset(int val[3]){ + int i; + for(i = 0; i < SENSOR_DATA_SIZE; ++i) + offset.v[i] = (s16) val[i]; +} + +struct file_operations dmt_g_sensor_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = device_ioctl, + .open = device_open, + .release = device_close, +}; + +static struct miscdevice dmt_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = DEVICE_I2C_NAME, + .fops = &dmt_g_sensor_fops, +}; + +static int sensor_close_dev(struct i2c_client *client){ + char buffer[3]; + buffer[0] = REG_ACTR; + buffer[1] = MODE_Standby; + buffer[2] = MODE_Off; + GSE_FUN(); + return device_i2c_txdata(client,buffer, 3); +} + +static int device_i2c_suspend(struct i2c_client *client, pm_message_t mesg){ + GSE_FUN(); + return sensor_close_dev(client); +} + +static int device_i2c_resume(struct i2c_client *client){ + GSE_FUN(); + return gsensor_reset(client); +} + +static void device_i2c_shutdown(struct i2c_client *client) +{ + struct dmt_data *dmt = i2c_get_clientdata(client); + flush_delayed_work_sync(&dmt->delaywork); + cancel_delayed_work_sync(&dmt->delaywork); +} + +static int __devexit device_i2c_remove(struct i2c_client *client){ + return 0; +} + +static const struct i2c_device_id device_i2c_ids[] = { + {DEVICE_I2C_NAME, 0}, + {} +}; + +//MODULE_DEVICE_TABLE(i2c, device_i2c_ids); + +static struct i2c_driver device_i2c_driver = +{ + .driver = { + .owner = THIS_MODULE, + .name = DEVICE_I2C_NAME, + }, + .class = I2C_CLASS_HWMON, + .id_table = device_i2c_ids, + .probe = device_i2c_probe, + .remove = __devexit_p(device_i2c_remove), + .shutdown = device_i2c_shutdown, +#ifndef CONFIG_ANDROID_POWER + .suspend = device_i2c_suspend, + .resume = device_i2c_resume, +#endif + +}; + +static int device_open(struct inode *inode, struct file *filp) +{ + return 0; +} + +static long device_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + //struct i2c_client *client = (struct i2c_client*)filp->private_data; + //struct dmt_data *dmt = (struct dmt_data*)i2c_get_clientdata(client); + + int err = 0, ret = 0, i; + int intBuf[SENSOR_DATA_SIZE]; + s16 xyz[SENSOR_DATA_SIZE]; + /* check type */ + if (_IOC_TYPE(cmd) != IOCTL_MAGIC) return -ENOTTY; + + /* check user space pointer is valid */ + if (_IOC_DIR(cmd) & _IOC_READ) + err = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd)); + else if (_IOC_DIR(cmd) & _IOC_WRITE) + err = !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd)); + if (err) return -EFAULT; + + switch(cmd) + { + case SENSOR_RESET: + gsensor_reset(s_dmt->client); + return ret; + + case SENSOR_CALIBRATION: + /* get orientation info */ + //if(copy_from_user(&intBuf, (int*)arg, sizeof(intBuf))) return -EFAULT; + gsensor_calibrate(); + GSE_LOG("Sensor_calibration:%d %d %d\n",offset.u.x,offset.u.y,offset.u.z); + /* save file */ + gsensor_write_offset_to_file(); + + /* return the offset */ + for(i = 0; i < SENSOR_DATA_SIZE; ++i) + intBuf[i] = offset.v[i]; + + ret = copy_to_user((int *)arg, &intBuf, sizeof(intBuf)); + return ret; + + case SENSOR_GET_OFFSET: + /* get data from file */ + gsensor_read_offset_from_file(); + for(i = 0; i < SENSOR_DATA_SIZE; ++i) + intBuf[i] = offset.v[i]; + + ret = copy_to_user((int *)arg, &intBuf, sizeof(intBuf)); + return ret; + + case SENSOR_SET_OFFSET: + ret = copy_from_user(&intBuf, (int *)arg, sizeof(intBuf)); + gsensor_set_offset(intBuf); + /* write in to file */ + gsensor_write_offset_to_file(); + return ret; + + case SENSOR_READ_ACCEL_XYZ: + device_i2c_read_xyz(s_dmt->client, (s16 *)&xyz); + for(i = 0; i < SENSOR_DATA_SIZE; ++i) + intBuf[i] = xyz[i] - offset.v[i]; + + ret = copy_to_user((int*)arg, &intBuf, sizeof(intBuf)); + return ret; + + case SENSOR_SETYPR: + if(copy_from_user(&intBuf, (int*)arg, sizeof(intBuf))) { + GSE_LOG("%s:copy_from_user(&intBuf, (int*)arg, sizeof(intBuf)) ERROR, -EFAULT\n",__func__); + return -EFAULT; + } + input_report_abs(s_dmt->input, ABS_X, intBuf[0]); + input_report_abs(s_dmt->input, ABS_Y, -intBuf[1]); + input_report_abs(s_dmt->input, ABS_Z, -intBuf[2]); + input_sync(s_dmt->input); + GSE_LOG(KERN_INFO "%s:SENSOR_SETYPR OK! x=%d,y=%d,z=%d\n",__func__,intBuf[0],intBuf[1],intBuf[2]); + return 1; + + case SENSOR_GET_OPEN_STATUS: + GSE_LOG(KERN_INFO "%s:Going into DMT_GetOpenStatus()\n",__func__); + DMT_GetOpenStatus(s_dmt->client); + GSE_LOG(KERN_INFO "%s:DMT_GetOpenStatus() finished\n",__func__); + return 1; + break; + + case SENSOR_GET_CLOSE_STATUS: + GSE_LOG(KERN_INFO "%s:Going into DMT_GetCloseStatus()\n",__func__); + DMT_GetCloseStatus(s_dmt->client); + GSE_LOG(KERN_INFO "%s:DMT_GetCloseStatus() finished\n",__func__); + return 1; + break; + + case SENSOR_GET_DELAY: + ret = copy_to_user((int*)arg, &interval, sizeof(interval)); + return 1; + break; + + default: /* redundant, as cmd was checked against MAXNR */ + return -ENOTTY; + } + + return 0; +} + +static int device_close(struct inode *inode, struct file *filp) +{ + return 0; +} + +/***** I2C I/O function ***********************************************/ +static int device_i2c_rxdata( struct i2c_client *client, unsigned char *rxData, int length) +{ + struct i2c_msg msgs[] = + { + {.addr = client->addr, .flags = 0, .len = 1, .buf = rxData,}, + {.addr = client->addr, .flags = I2C_M_RD, .len = length, .buf = rxData,}, + }; + //unsigned char addr = rxData[0]; + if (i2c_transfer(client->adapter, msgs, 2) < 0) { + dev_err(&client->dev, "%s: transfer failed.", __func__); + return -EIO; + } + //DMT_DATA(&client->dev, "RxData: len=%02x, addr=%02x, data=%02x\n", + //length, addr, rxData[0]); + + return 0; +} + +static int device_i2c_txdata( struct i2c_client *client, unsigned char *txData, int length) +{ + struct i2c_msg msg[] = + { + {.addr = client->addr, .flags = 0, .len = length, .buf = txData,}, + }; + + if (i2c_transfer(client->adapter, msg, 1) < 0) { + dev_err(&client->dev, "%s: transfer failed.", __func__); + return -EIO; + } + //DMT_DATA(&client->dev, "TxData: len=%02x, addr=%02x data=%02x\n", + //length, txData[0], txData[1]); + return 0; +} +/* 1g = 128 becomes 1g = 1024 */ +static inline void device_i2c_correct_accel_sign(s16 *val){ + *val<<= 3; +} + +void device_i2c_merge_register_values(struct i2c_client *client, s16 *val, u8 msb, u8 lsb){ + *val = (((u16)msb) << 8) | (u16)lsb; + device_i2c_correct_accel_sign(val); +} + +void device_i2c_read_xyz(struct i2c_client *client, s16 *xyz_p){ + u8 buffer[11]; + s16 xyzTmp[SENSOR_DATA_SIZE]; + int i, j; + /* get xyz high/low bytes, 0x12 */ + buffer[0] = REG_STADR; + device_i2c_rxdata(client, buffer, 10); + + /* merge to 10-bits value */ + for(i = 0; i < SENSOR_DATA_SIZE; ++i){ + xyz_p[i] = 0; + device_i2c_merge_register_values(client, (xyzTmp + i), buffer[2*(i+1)+1], buffer[2*(i+1)]); + /* transfer to the default layout */ + for(j = 0; j < 3; j++) + xyz_p[i] += sensorlayout[i][j] * xyzTmp[j]; + } + GSE_LOG("xyz_p: %04d , %04d , %04d\n", xyz_p[0], xyz_p[1], xyz_p[2]); +} + +static void DMT_work_func(struct work_struct *delaywork) +{ + struct dmt_data *dmt = container_of(delaywork, struct dmt_data, delaywork.work); + int i; + static int firsttime=0; + s16 xyz[SENSOR_DATA_SIZE]; + + unsigned long t=atomic_read(&dmt->delay); + unsigned long dmt_delay = msecs_to_jiffies(t); + if(!firsttime){ + //gsensor_read_offset_from_file(); + firsttime=1; + } + + GSE_LOG("t=%lu , dmt_delay=%lu\n", t, dmt_delay); + device_i2c_read_xyz(dmt->client, (s16 *)&xyz); + for(i = 0; i < SENSOR_DATA_SIZE; ++i) + xyz[i] -= offset.v[i]; + + GSE_LOG("@DMTRaw@ X/Y/Z axis: %04d , %04d , %04d\n", xyz[0], xyz[1], xyz[2]); + GSE_LOG("@Offset@ X/Y/Z axis: %04d , %04d , %04d\n", offset.u.x, offset.u.y, offset.u.z); + input_report_abs(dmt->input, ABS_X, xyz[gs_conf.xyz_axis[ABS_X][0]]*gs_conf.xyz_axis[ABS_X][1]); + input_report_abs(dmt->input, ABS_Y, xyz[gs_conf.xyz_axis[ABS_Y][0]]*gs_conf.xyz_axis[ABS_Y][1]); + input_report_abs(dmt->input, ABS_Z, xyz[gs_conf.xyz_axis[ABS_Z][0]]*gs_conf.xyz_axis[ABS_Z][1]); + input_sync(dmt->input); + + if(dmt_delay < 1) + dmt_delay = 1; + schedule_delayed_work(&dmt->delaywork, dmt_delay); +} + +static int __devinit device_i2c_probe(struct i2c_client *client,const struct i2c_device_id *id){ + int i, ret = 0; + //struct dmt_data *s_dmt; + + GSE_FUN(); + for(i = 0; i < SENSOR_DATA_SIZE; ++i) + offset.v[i] = 0; + + if(!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)){ + GSE_ERR("check_functionality failed.\n"); + ret = -ENODEV; + goto exit0; + } + + /* Allocate memory for driver data */ + s_dmt = kzalloc(sizeof(struct dmt_data), GFP_KERNEL); + memset(s_dmt, 0, sizeof(struct dmt_data)); + if (s_dmt == NULL) { + GSE_ERR("alloc data failed.\n"); + ret = -ENOMEM; + goto exit1; + } + + /***** I2C initialization *****/ + s_dmt->client = client; + /* set client data */ + i2c_set_clientdata(client, s_dmt); + ret = gsensor_reset(client); + if (ret < 0) + goto exit2; + + /***** input *****/ + ret = input_init(client); + if (ret){ + GSE_ERR("input_init fail, error code= %d\n",ret); + goto exit3; + } + + /**** initialize variables in dmt_data *****/ + init_waitqueue_head(&s_dmt->open_wq); + atomic_set(&s_dmt->active, 0); + atomic_set(&s_dmt->enable, 0); + atomic_set(&s_dmt->delay, 0); + mutex_init(&s_dmt->sensor_mutex); + /***** misc *****/ + /* we have been register miscdevice in device_init, and + * marked by Eason 2013/2/4*/ + /*ret = misc_register(&dmt_device); + if (ret){ + GSE_ERR("dmt_dev register failed"); + goto exit5; + }*/ + + /***** sysfs *****/ + ret = create_sysfs_interfaces(s_dmt); + if (ret < 0){ + GSE_ERR("create sysfs failed."); + goto exit6; + } + + INIT_DELAYED_WORK(&s_dmt->delaywork, DMT_work_func); + GSE_LOG("DMT: INIT_DELAYED_WORK\n"); + return 0; + +exit6: + //misc_deregister(&dmt_device); +exit5: + input_unregister_device(s_dmt->input); +exit3: + kfree(s_dmt); +exit2: +exit1: +exit0: + return ret; +} + +int dmt10_enable(int en) +{ + printk(KERN_DEBUG "%s: enable = %d\n", __func__, en); + DMT_sysfs_update_active_status(s_dmt,en); + return 0; +} + +int dmt10_setDelay(int mdelay) +{ + printk(KERN_DEBUG "%s: delay = %d\n", __func__, mdelay); + atomic_set(&s_dmt->delay, mdelay); + return 0; +} + +int dmt10_getLSG(int *lsg) +{ + *lsg = 1024; + return 0; +} + +struct gsensor_data dmt10_gs_data = { + .i2c_addr = DMT10_I2C_ADDR, + .enable = dmt10_enable, + .setDelay = dmt10_setDelay, + .getLSG = dmt10_getLSG, +}; + +static int __init device_init(void){ + int ret = 0; + + if (get_gsensor_conf(&gs_conf)) + return -1; + + if (gs_conf.op != 3) + return -1; + printk("G-Sensor dmt10 init\n"); + + if (gsensor_register(&dmt10_gs_data)) + return -1; + + if (gsensor_i2c_register_device() < 0) + return -1; + + return i2c_add_driver(&device_i2c_driver); +} + +static void __exit device_exit(void){ + i2c_del_driver(&device_i2c_driver); +} + +void gsensor_write_offset_to_file(void){ + char data[18]; + unsigned int orgfs; + struct file *fp; + + sprintf(data,"%5d %5d %5d",offset.u.x,offset.u.y,offset.u.z); + orgfs = get_fs(); + /* Set segment descriptor associated to kernel space */ + set_fs(KERNEL_DS); + fp = filp_open(OffsetFileName, O_RDWR | O_CREAT, 0777); + if(IS_ERR(fp)){ + GSE_ERR("filp_open %s error!!.\n",OffsetFileName); + } + else{ + GSE_LOG("filp_open %s SUCCESS!!.\n",OffsetFileName); + fp->f_op->write(fp,data,18, &fp->f_pos); + filp_close(fp,NULL); + } + set_fs(orgfs); +} + +void gsensor_read_offset_from_file(void){ + unsigned int orgfs; + char data[18]; + struct file *fp; + int ux,uy,uz; + orgfs = get_fs(); + /* Set segment descriptor associated to kernel space */ + set_fs(KERNEL_DS); + + fp = filp_open(OffsetFileName, O_RDWR , 0); + GSE_FUN(); + if(IS_ERR(fp)){ + GSE_ERR("Sorry,file open ERROR !\n"); + offset.u.x=0;offset.u.y=0;offset.u.z=0; +#if AUTO_CALIBRATION + /* get acceleration average reading */ + gsensor_calibrate(); + gsensor_write_offset_to_file(); +#endif + } + else{ + GSE_LOG("filp_open %s SUCCESS!!.\n",OffsetFileName); + fp->f_op->read(fp,data,18, &fp->f_pos); + GSE_LOG("filp_read result %s\n",data); + sscanf(data,"%d %d %d",&ux,&uy,&uz); + offset.u.x=ux; + offset.u.y=uy; + offset.u.z=uz; + filp_close(fp,NULL); + } + set_fs(orgfs); +} +//********************************************************************************************************* +MODULE_AUTHOR("DMT_RD"); +MODULE_DESCRIPTION("DMT Gsensor Driver"); +MODULE_LICENSE("GPL"); + +module_init(device_init); +module_exit(device_exit); diff --git a/drivers/input/sensor/TP_DRIVER_NOT_USE/dmt10_gsensor/dmt10.h b/drivers/input/sensor/TP_DRIVER_NOT_USE/dmt10_gsensor/dmt10.h new file mode 100755 index 00000000..61343657 --- /dev/null +++ b/drivers/input/sensor/TP_DRIVER_NOT_USE/dmt10_gsensor/dmt10.h @@ -0,0 +1,187 @@ +/* + * @file include/linux/dmt10.h + * @brief DMT g-sensor Linux device driver + * @author Domintech Technology Co., Ltd (http://www.domintech.com.tw) + * @version 1.02 + * @date 2012/12/10 + * + * @section LICENSE + * + * Copyright 2012 Domintech Technology Co., Ltd + * + * 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. + * + * @DMT Package version D10_General_driver v1.4 + * + * + */ +#ifndef DMT10_H +#define DMT10_H +#include +#include +#include +#include +#include +#include +#include +#include + +#define AUTO_CALIBRATION 0 + +#define DMT_DEBUG_DATA 0 +#define GSE_TAG "[DMT_Gsensor]" +#if DMT_DEBUG_DATA +#define GSE_ERR(fmt, args...) printk(KERN_ERR GSE_TAG"%s %d : "fmt, __FUNCTION__, __LINE__, ##args) +#define GSE_LOG(fmt, args...) printk(KERN_INFO GSE_TAG fmt, ##args) +#define GSE_FUN(f) printk(KERN_INFO GSE_TAG" %s: %s: %i\n", __FILE__, __func__, __LINE__) +#define DMT_DATA(dev, ...) dev_dbg((dev), ##__VA_ARGS__) +#else +#define GSE_ERR(fmt, args...) +#define GSE_LOG(fmt, args...) +#define GSE_FUN(f) +#define DMT_DATA(dev, format, ...) +#endif + +#define DMT10_I2C_ADDR 0x18 + + +#define INPUT_NAME_ACC "g-sensor" /* Input Device Name */ +#define DEVICE_I2C_NAME "g-sensor" /* Device name for DMARD10 misc. device */ +#define REG_ACTR 0x00 +#define REG_WDAL 0x01 +#define REG_TAPNS 0x0f +#define REG_MISC2 0x1f +#define REG_AFEM 0x0c +#define REG_CKSEL 0x0d +#define REG_INTC 0x0e +#define REG_STADR 0x12 +#define REG_STAINT 0x1C +#define REG_PD 0x21 +#define REG_TCGYZ 0x26 +#define REG_X_OUT 0x41 + +#define MODE_Off 0x00 +#define MODE_ResetAtOff 0x01 +#define MODE_Standby 0x02 +#define MODE_ResetAtStandby 0x03 +#define MODE_Active 0x06 +#define MODE_Trigger 0x0a +#define MODE_ReadOTP 0x12 +#define MODE_WriteOTP 0x22 +#define MODE_WriteOTPBuf 0x42 +#define MODE_ResetDataPath 0x82 + +#define VALUE_STADR 0x55 +#define VALUE_STAINT 0xAA +#define VALUE_AFEM_AFEN_Normal 0x8f// AFEN set 1 , ATM[2:0]=b'000(normal),EN_Z/Y/X/T=1 +#define VALUE_AFEM_Normal 0x0f// AFEN set 0 , ATM[2:0]=b'000(normal),EN_Z/Y/X/T=1 +#define VALUE_INTC 0x00// INTC[6:5]=b'00 +#define VALUE_INTC_Interrupt_En 0x20// INTC[6:5]=b'01 (Data ready interrupt enable, active high at INT0) +#define VALUE_CKSEL_ODR_0_204 0x04// ODR[3:0]=b'0000 (0.78125Hz), CCK[3:0]=b'0100 (204.8kHZ) +#define VALUE_CKSEL_ODR_1_204 0x14// ODR[3:0]=b'0001 (1.5625Hz), CCK[3:0]=b'0100 (204.8kHZ) +#define VALUE_CKSEL_ODR_3_204 0x24// ODR[3:0]=b'0010 (3.125Hz), CCK[3:0]=b'0100 (204.8kHZ) +#define VALUE_CKSEL_ODR_6_204 0x34// ODR[3:0]=b'0011 (6.25Hz), CCK[3:0]=b'0100 (204.8kHZ) +#define VALUE_CKSEL_ODR_12_204 0x44// ODR[3:0]=b'0100 (12.5Hz), CCK[3:0]=b'0100 (204.8kHZ) +#define VALUE_CKSEL_ODR_25_204 0x54// ODR[3:0]=b'0101 (25Hz), CCK[3:0]=b'0100 (204.8kHZ) +#define VALUE_CKSEL_ODR_50_204 0x64// ODR[3:0]=b'0110 (50Hz), CCK[3:0]=b'0100 (204.8kHZ) +#define VALUE_CKSEL_ODR_100_204 0x74// ODR[3:0]=b'0111 (100Hz), CCK[3:0]=b'0100 (204.8kHZ) + +#define VALUE_TAPNS_NoFilter 0x00 // TAP1/TAP2 NO FILTER +#define VALUE_TAPNS_Ave_2 0x11 // TAP1/TAP2 Average 2 +#define VALUE_TAPNS_Ave_4 0x22 // TAP1/TAP2 Average 4 +#define VALUE_TAPNS_Ave_8 0x33 // TAP1/TAP2 Average 8 +#define VALUE_TAPNS_Ave_16 0x44 // TAP1/TAP2 Average 16 +#define VALUE_TAPNS_Ave_32 0x55 // TAP1/TAP2 Average 32 +#define VALUE_MISC2_OSCA_EN 0x08 +#define VALUE_PD_RST 0x52 + +#define CONFIG_GSEN_CALIBRATION_GRAVITY_ON_Z_NEGATIVE 1 +#define CONFIG_GSEN_CALIBRATION_GRAVITY_ON_Z_POSITIVE 2 +#define CONFIG_GSEN_CALIBRATION_GRAVITY_ON_Y_NEGATIVE 3 +#define CONFIG_GSEN_CALIBRATION_GRAVITY_ON_Y_POSITIVE 4 +#define CONFIG_GSEN_CALIBRATION_GRAVITY_ON_X_NEGATIVE 5 +#define CONFIG_GSEN_CALIBRATION_GRAVITY_ON_X_POSITIVE 6 + +#define AVG_NUM 16 +#define SENSOR_DATA_SIZE 3 +#define DEFAULT_SENSITIVITY 1024 + +#define IOCTL_MAGIC 0x09 +#define SENSOR_RESET _IO(IOCTL_MAGIC, 0) +#define SENSOR_CALIBRATION _IOWR(IOCTL_MAGIC, 1, int[SENSOR_DATA_SIZE]) +#define SENSOR_GET_OFFSET _IOR(IOCTL_MAGIC, 2, int[SENSOR_DATA_SIZE]) +#define SENSOR_SET_OFFSET _IOWR(IOCTL_MAGIC, 3, int[SENSOR_DATA_SIZE]) +#define SENSOR_READ_ACCEL_XYZ _IOR(IOCTL_MAGIC, 4, int[SENSOR_DATA_SIZE]) +#define SENSOR_SETYPR _IOW(IOCTL_MAGIC, 5, int[SENSOR_DATA_SIZE]) +#define SENSOR_GET_OPEN_STATUS _IO(IOCTL_MAGIC, 6) +#define SENSOR_GET_CLOSE_STATUS _IO(IOCTL_MAGIC, 7) +#define SENSOR_GET_DELAY _IOR(IOCTL_MAGIC, 8, unsigned int*) +#define SENSOR_MAXNR 8 + +/* g-senor layout configuration, choose one of the following configuration */ +#define CONFIG_GSEN_LAYOUT_PAT_1 1 +#define CONFIG_GSEN_LAYOUT_PAT_2 0 +#define CONFIG_GSEN_LAYOUT_PAT_3 0 +#define CONFIG_GSEN_LAYOUT_PAT_4 0 +#define CONFIG_GSEN_LAYOUT_PAT_5 0 +#define CONFIG_GSEN_LAYOUT_PAT_6 0 +#define CONFIG_GSEN_LAYOUT_PAT_7 0 +#define CONFIG_GSEN_LAYOUT_PAT_8 0 + +s16 sensorlayout[3][3] = { +#if CONFIG_GSEN_LAYOUT_PAT_1 + { 1, 0, 0}, { 0, 1, 0}, { 0, 0, 1}, +#elif CONFIG_GSEN_LAYOUT_PAT_2 + { 0, 1, 0}, {-1, 0, 0}, { 0, 0, 1}, +#elif CONFIG_GSEN_LAYOUT_PAT_3 + {-1, 0, 0}, { 0,-1, 0}, { 0, 0, 1}, +#elif CONFIG_GSEN_LAYOUT_PAT_4 + { 0,-1, 0}, { 1, 0, 0}, { 0, 0, 1}, +#elif CONFIG_GSEN_LAYOUT_PAT_5 + {-1, 0, 0}, { 0, 1, 0}, { 0, 0,-1}, +#elif CONFIG_GSEN_LAYOUT_PAT_6 + { 0,-1, 0}, {-1, 0, 0}, { 0, 0,-1}, +#elif CONFIG_GSEN_LAYOUT_PAT_7 + { 1, 0, 0}, { 0,-1, 0}, { 0, 0,-1}, +#elif CONFIG_GSEN_LAYOUT_PAT_8 + { 0, 1, 0}, { 1, 0, 0}, { 0, 0,-1}, +#endif +}; + +typedef union { + struct { + s16 x; + s16 y; + s16 z; + } u; + s16 v[SENSOR_DATA_SIZE]; +} raw_data; + +struct dmt_data { + dev_t devno; + struct cdev cdev; + struct device *class_dev; + struct class *class; + struct input_dev *input; + struct i2c_client *client; + struct delayed_work delaywork; + struct work_struct work; + struct mutex sensor_mutex; + wait_queue_head_t open_wq; + atomic_t active; + atomic_t delay; + atomic_t enable; +}; + +#define ACC_DATA_FLAG 0 +#define MAG_DATA_FLAG 1 +#define ORI_DATA_FLAG 2 +#define DMT_NUM_SENSORS 3 +#endif diff --git a/drivers/input/sensor/TP_DRIVER_NOT_USE/gsensor.c b/drivers/input/sensor/TP_DRIVER_NOT_USE/gsensor.c new file mode 100755 index 00000000..bd0b16af --- /dev/null +++ b/drivers/input/sensor/TP_DRIVER_NOT_USE/gsensor.c @@ -0,0 +1,180 @@ +/*++ +Copyright (c) 2012 WonderMedia Technologies, Inc. All Rights Reserved. +This PROPRIETARY SOFTWARE is the property of WonderMedia Technologies, Inc. +and may contain trade secrets and/or other confidential information of +WonderMedia Technologies, Inc. This file shall not be disclosed to any +third party, in whole or in part, without prior written consent of +WonderMedia. + +THIS PROPRIETARY SOFTWARE AND ANY RELATED DOCUMENTATION ARE PROVIDED +AS IS, WITH ALL FAULTS, AND WITHOUT WARRANTY OF ANY KIND EITHER EXPRESS +OR IMPLIED, AND WonderMedia TECHNOLOGIES, INC. DISCLAIMS ALL EXPRESS +OR IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, +QUIET ENJOYMENT OR NON-INFRINGEMENT. +--*/ + +#include +#include +#include +#include +#include +#include "gsensor.h" + +#define GSENSOR_I2C_NAME "g-sensor" + +#ifdef CONFIG_WMT_SENSOR_DMT08 +#define GSENSOR_I2C_ADDR 0x1c +#elif defined CONFIG_WMT_SENSOR_KXTI9 +#define GSENSOR_I2C_ADDR 0x0f +#endif + +struct gsensor_data *gs_data; + +extern int wmt_getsyspara(char *varname, unsigned char *varval, int *varlen); + +struct i2c_board_info gsensor_i2c_board_info = { + .type = GSENSOR_I2C_NAME, + .flags = 0x00, + .platform_data = NULL, + .archdata = NULL, + .irq = -1, +}; + +int gsensor_i2c_register_device (void) +{ + struct i2c_board_info *gsensor_i2c_bi; + struct i2c_adapter *adapter = NULL; + struct i2c_client *client = NULL; + gsensor_i2c_bi = &gsensor_i2c_board_info; + adapter = i2c_get_adapter(0);/*in bus 0*/ + + if (NULL == adapter) { + printk("can not get i2c adapter, client address error\n"); + return -1; + } + gsensor_i2c_bi->addr = gs_data->i2c_addr; + client = i2c_new_device(adapter, gsensor_i2c_bi); + if (client == NULL) { + printk("allocate i2c client failed\n"); + return -1; + } + i2c_put_adapter(adapter); + return 0; +} + +/* + * Get the configure of sensor from u-boot. + * Return: 0--success, other--error. + */ +int get_gsensor_conf(struct gsensor_conf *gs_conf) +{ + char varbuf[64]; + int n; + int varlen; + + memset(varbuf, 0, sizeof(varbuf)); + varlen = sizeof(varbuf); + if (wmt_getsyspara("wmt.io.gsensor", varbuf, &varlen)) { + printk("wmt.io.gsensor not defined!\n"); + return -1; + } else { + n = sscanf(varbuf, "%d:%d:%d:%d:%d:%d:%d:%d", + &gs_conf->op, + &gs_conf->samp, + &(gs_conf->xyz_axis[0][0]), + &(gs_conf->xyz_axis[0][1]), + &(gs_conf->xyz_axis[1][0]), + &(gs_conf->xyz_axis[1][1]), + &(gs_conf->xyz_axis[2][0]), + &(gs_conf->xyz_axis[2][1])); + printk(KERN_INFO "wmt.io.gsensor = %d:%d:%d:%d:%d:%d:%d:%d\n", + gs_conf->op, + gs_conf->samp, + gs_conf->xyz_axis[0][0], + gs_conf->xyz_axis[0][1], + gs_conf->xyz_axis[1][0], + gs_conf->xyz_axis[1][1], + gs_conf->xyz_axis[2][0], + gs_conf->xyz_axis[2][1]); + if (n != 8) { + printk("wmt.io.gsensor format is incorrect!\n"); + return -1; + } + + if (gs_conf->op <= 0) { + printk(KERN_INFO "wmt.io.gsensor is disabled\n"); + return -1; + } + } + return 0; +} + +static long gsensor_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + void __user *argp = (void __user *)arg; + int flag; + + if (!gs_data) + return -1; + + switch (cmd) { + case WMT_IOCTL_APP_SET_AFLAG: + if (copy_from_user(&flag, argp, sizeof(flag))) + return -EFAULT; + else { + if (flag < 0 || flag > 1) + return -EINVAL; + gs_data->enable(flag); + } + break; + case WMT_IOCTL_APP_GET_AFLAG: + if (copy_to_user(argp, &flag, sizeof(flag))) + return -EFAULT; + break; + case WMT_IOCTL_APP_SET_DELAY: + if (copy_from_user(&flag, argp, sizeof(flag))) + return -EFAULT; + else + gs_data->setDelay(flag); + + break; + case WMT_IOCTL_APP_GET_DELAY: + if (copy_to_user(argp, &flag, sizeof(flag))) + return -EFAULT; + break; + case WMT_IOCTL_APP_GET_LSG: + gs_data->getLSG(&flag); + if (copy_to_user(argp, &flag, sizeof(flag))) + return -EFAULT; + break; + default: + return -ENOTTY; + } + return 0; +} + +struct file_operations gsensor_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = gsensor_ioctl, +}; + +struct miscdevice gsensor_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "g-sensor", + .fops = &gsensor_fops, +}; + +int gsensor_register(struct gsensor_data *data) +{ + int err=-1; + + gs_data = data; + err = misc_register(&gsensor_device); + if (err) + printk(KERN_ERR "%s: gsensor misc register failed\n", __func__); + return err; +} + +EXPORT_SYMBOL_GPL(gsensor_i2c_register_device); +EXPORT_SYMBOL_GPL(get_gsensor_conf); +EXPORT_SYMBOL_GPL(gsensor_register); diff --git a/drivers/input/sensor/TP_DRIVER_NOT_USE/gsensor.h b/drivers/input/sensor/TP_DRIVER_NOT_USE/gsensor.h new file mode 100755 index 00000000..db644d77 --- /dev/null +++ b/drivers/input/sensor/TP_DRIVER_NOT_USE/gsensor.h @@ -0,0 +1,43 @@ +/*++ +Copyright (c) 2012 WonderMedia Technologies, Inc. All Rights Reserved. +This PROPRIETARY SOFTWARE is the property of WonderMedia Technologies, Inc. +and may contain trade secrets and/or other confidential information of +WonderMedia Technologies, Inc. This file shall not be disclosed to any +third party, in whole or in part, without prior written consent of +WonderMedia. + +THIS PROPRIETARY SOFTWARE AND ANY RELATED DOCUMENTATION ARE PROVIDED +AS IS, WITH ALL FAULTS, AND WITHOUT WARRANTY OF ANY KIND EITHER EXPRESS +OR IMPLIED, AND WonderMedia TECHNOLOGIES, INC. DISCLAIMS ALL EXPRESS +OR IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, +QUIET ENJOYMENT OR NON-INFRINGEMENT. +--*/ + +#ifndef __GSENSOR_H__ +#define __GSENSOR_H__ + +#define IOCTL_WMT 0x01 +#define WMT_IOCTL_APP_SET_AFLAG _IOW(IOCTL_WMT, 0x01, int) +#define WMT_IOCTL_APP_SET_DELAY _IOW(IOCTL_WMT, 0x02, int) +#define WMT_IOCTL_APP_GET_AFLAG _IOW(IOCTL_WMT, 0x03, int) +#define WMT_IOCTL_APP_GET_DELAY _IOW(IOCTL_WMT, 0x04, int) +#define WMT_IOCTL_APP_GET_LSG _IOW(IOCTL_WMT, 0x05, int) + +struct gsensor_conf { + int op; + int samp; + int xyz_axis[3][2]; +}; + +struct gsensor_data { + int i2c_addr; + int (*enable) (int en); + int (*setDelay) (int mdelay); + int (*getLSG) (int *lsg); +}; + +extern int gsensor_i2c_register_device (void); +extern int get_gsensor_conf(struct gsensor_conf *gs_conf); +extern int gsensor_register(struct gsensor_data *gs_data); + +#endif \ No newline at end of file diff --git a/drivers/input/sensor/TP_DRIVER_NOT_USE/kxti9_gsensor/Makefile b/drivers/input/sensor/TP_DRIVER_NOT_USE/kxti9_gsensor/Makefile new file mode 100755 index 00000000..3df5b74c --- /dev/null +++ b/drivers/input/sensor/TP_DRIVER_NOT_USE/kxti9_gsensor/Makefile @@ -0,0 +1,6 @@ +# +# Makefile for the KXTI9 Sensor driver +# + +sensor_kxti9-objs := kxti9.o +obj-$(CONFIG_WMT_SENSOR_KXTI9) += sensor_kxti9.o diff --git a/drivers/input/sensor/TP_DRIVER_NOT_USE/kxti9_gsensor/kxti9.c b/drivers/input/sensor/TP_DRIVER_NOT_USE/kxti9_gsensor/kxti9.c new file mode 100755 index 00000000..c385842e --- /dev/null +++ b/drivers/input/sensor/TP_DRIVER_NOT_USE/kxti9_gsensor/kxti9.c @@ -0,0 +1,1134 @@ +/* + * Copyright (C) 2009 Kionix, Inc. + * Written by Chris Hudson + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef KXTI9_INT_MODE +#include +#include +#include +#endif +#include +#include +#include +#include "kxti9.h" +#include "../gsensor.h" + +#define NAME "g-sensor" +#define G_MAX 2048 //8000 +/* OUTPUT REGISTERS */ +#define XOUT_L 0x06 +#define INT_SRC_REG1 0x15 +#define INT_STATUS_REG 0x16 +#define TILT_POS_CUR 0x10 +#define INT_REL 0x1A +#define WHO_AM_I 0x0F +/* CONTROL REGISTERS */ +#define DATA_CTRL 0x21 +#define CTRL_REG1 0x1B +#define INT_CTRL1 0x1E +#define CTRL_REG3 0x1D +#define TILT_TIMER 0x28 +#define WUF_TIMER 0x29 +#define WUF_THRESH 0x5A +#define TDT_TIMER 0x2B +/* CONTROL REGISTER 1 BITS */ +#define PC1_OFF 0x00 +#define PC1_ON 0x80 +/* INTERRUPT SOURCE 2 BITS */ +#define TPS 0x01 +#define TDTS0 0x04 +#define TDTS1 0x08 +/* INPUT_ABS CONSTANTS */ +#define FUZZ 0 //32 +#define FLAT 0 //32 +/* RESUME STATE INDICES */ +#define RES_DATA_CTRL 0 +#define RES_CTRL_REG1 1 +#define RES_INT_CTRL1 2 +#define RES_TILT_TIMER 3 +#define RES_CTRL_REG3 4 +#define RES_WUF_TIMER 5 +#define RES_WUF_THRESH 6 +#define RES_TDT_TIMER 7 +#define RES_TDT_H_THRESH 8 +#define RES_TDT_L_THRESH 9 +#define RES_TAP_TIMER 10 +#define RES_TOTAL_TIMER 11 +#define RES_LAT_TIMER 12 +#define RES_WIN_TIMER 13 +#define RESUME_ENTRIES 14 + +static struct gsensor_conf gs_conf; +static struct kxti9_data *gs_ti9; + +extern int gsensor_i2c_register_device (void); + +struct kxti9_platform_data kxti9_pdata = { + .min_interval = 1, + .poll_interval = 200,//1000, + + .g_range = KXTI9_G_2G, + .shift_adj = SHIFT_ADJ_2G, + + .axis_map_x = 0, + .axis_map_y = 1, + .axis_map_z = 2, + + .negate_x = 0, + .negate_y = 0, + .negate_z = 0, + + .data_odr_init = ODR12_5F, +#ifdef KXTI9_INT_MODE + .ctrl_reg1_init = KXTI9_G_8G | RES_12BIT | TDTE | WUFE | TPE, + .int_ctrl_init = KXTI9_IEN | KXTI9_IEA | KXTI9_IEL, +#else + .ctrl_reg1_init = KXTI9_G_2G | RES_12BIT, + .int_ctrl_init = 0, +#endif + .tilt_timer_init = 0x03, + .engine_odr_init = OTP12_5 | OWUF50 | OTDT400, + .wuf_timer_init = 0x16, + .wuf_thresh_init = 0x28, + .tdt_timer_init = 0x78, + .tdt_h_thresh_init = 0xFF, + .tdt_l_thresh_init = 0x14, + .tdt_tap_timer_init = 0x53, + .tdt_total_timer_init = 0x24, + .tdt_latency_timer_init = 0x10, + .tdt_window_timer_init = 0xA0, +}; + +/* + * The following table lists the maximum appropriate poll interval for each + * available output data rate. + */ +struct { + unsigned int cutoff; + u8 mask; +} kxti9_odr_table[] = { + { + 3, ODR800F}, { + 5, ODR400F}, { + 10, ODR200F}, { + 20, ODR100F}, { + 40, ODR50F}, { + 80, ODR25F}, { + 0, ODR12_5F}, +}; + +struct kxti9_data { + struct i2c_client *client; + struct kxti9_platform_data *pdata; + struct mutex lock; + struct delayed_work input_work; + struct input_dev *input_dev; +#ifdef KXTI9_INT_MODE + struct work_struct irq_work; +#endif + + int hw_initialized; + atomic_t enabled; + u8 resume[RESUME_ENTRIES]; + int res_interval; +#ifdef KXTI9_INT_MODE + int irq; +#endif +}; + +static int kxti9_i2c_read(struct kxti9_data *ti9, u8 addr, u8 *data, int len) +{ + int err; + + struct i2c_msg msgs[] = { + { + .addr = ti9->client->addr, + .flags = ti9->client->flags & I2C_M_TEN, + .len = 1, + .buf = &addr, + }, + { + .addr = ti9->client->addr, + .flags = (ti9->client->flags & I2C_M_TEN) | I2C_M_RD, + .len = len, + .buf = data, + }, + }; + err = i2c_transfer(ti9->client->adapter, msgs, 2); + + if (err != 2) + dev_err(&ti9->client->dev, "read transfer error\n"); + else + err = 0; + + return err; +} + +static int kxti9_i2c_write(struct kxti9_data *ti9, u8 addr, u8 *data, int len) +{ + int err; + int i; + u8 buf[len + 1]; + + struct i2c_msg msgs[] = { + { + .addr = ti9->client->addr, + .flags = ti9->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 = i2c_transfer(ti9->client->adapter, msgs, 1); + + if (err != 1) + dev_err(&ti9->client->dev, "write transfer error\n"); + else + err = 0; + + return err; +} + +static int kxti9_verify(struct kxti9_data *ti9) +{ + int err; + u8 buf; + + err = kxti9_i2c_read(ti9, WHO_AM_I, &buf, 1); + /*** DEBUG OUTPUT - REMOVE ***/ + dev_info(&ti9->client->dev, "WHO_AM_I = 0x%02x\n", buf); + /*** DEBUG OUTPUT - REMOVE ***/ + if (err < 0) + dev_err(&ti9->client->dev, "read err int source\n"); + if (buf != 0x04 && buf != 0x08) // jakie add 0x8 for kxtj9 + err = -1; + return err; +} + +int kxti9_update_g_range(struct kxti9_data *ti9, u8 new_g_range) +{ + int err; + u8 shift; + u8 buf; + + switch (new_g_range) { + case KXTI9_G_2G: + shift = SHIFT_ADJ_2G; + break; + case KXTI9_G_4G: + shift = SHIFT_ADJ_4G; + break; + case KXTI9_G_8G: + shift = SHIFT_ADJ_8G; + break; + default: + dev_err(&ti9->client->dev, "invalid g range request\n"); + return -EINVAL; + } + if (shift != ti9->pdata->shift_adj) { + if (ti9->pdata->shift_adj > shift) + ti9->resume[RES_WUF_THRESH] >>= + (ti9->pdata->shift_adj - shift); + if (ti9->pdata->shift_adj < shift) + ti9->resume[RES_WUF_THRESH] <<= + (shift - ti9->pdata->shift_adj); + + if (atomic_read(&ti9->enabled)) { + buf = PC1_OFF; + err = kxti9_i2c_write(ti9, CTRL_REG1, &buf, 1); + if (err < 0) + return err; + buf = ti9->resume[RES_WUF_THRESH]; + err = kxti9_i2c_write(ti9, WUF_THRESH, &buf, 1); + if (err < 0) + return err; + buf = (ti9->resume[RES_CTRL_REG1] & 0xE7) | new_g_range; + err = kxti9_i2c_write(ti9, CTRL_REG1, &buf, 1); + if (err < 0) + return err; + ti9->resume[RES_CTRL_REG1] = buf; + ti9->pdata->shift_adj = shift; + } + } + return 0; +} + +int kxti9_update_odr(struct kxti9_data *ti9, 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 slower + * ODR cannot support the current poll interval, we stop searching */ + for (i = 0; i < ARRAY_SIZE(kxti9_odr_table); i++) { + config = kxti9_odr_table[i].mask; + if (poll_interval < kxti9_odr_table[i].cutoff) + break; + } + + if (atomic_read(&ti9->enabled)) { + err = kxti9_i2c_write(ti9, DATA_CTRL, &config, 1); + if (err < 0) + return err; + /* + * Latch on input_dev - indicates that kxti9_input_init passed + * and this workqueue is available + */ + if (ti9->input_dev) { + cancel_delayed_work_sync(&ti9->input_work); + schedule_delayed_work(&ti9->input_work, + msecs_to_jiffies(poll_interval)); + } + } + ti9->resume[RES_DATA_CTRL] = config; + + return 0; +} + +static int kxti9_hw_init(struct kxti9_data *ti9) +{ + int err = -1; + u8 buf[7]; + + buf[0] = PC1_OFF; + err = kxti9_i2c_write(ti9, CTRL_REG1, buf, 1); + if (err < 0) + return err; + err = kxti9_i2c_write(ti9, DATA_CTRL, &ti9->resume[RES_DATA_CTRL], 1); + if (err < 0) + return err; + err = kxti9_i2c_write(ti9, CTRL_REG3, &ti9->resume[RES_CTRL_REG3], 1); + if (err < 0) + return err; + err = kxti9_i2c_write(ti9, TILT_TIMER, &ti9->resume[RES_TILT_TIMER], 1); + if (err < 0) + return err; + err = kxti9_i2c_write(ti9, WUF_TIMER, &ti9->resume[RES_WUF_TIMER], 1); + if (err < 0) + return err; + err = kxti9_i2c_write(ti9, WUF_THRESH, &ti9->resume[RES_WUF_THRESH], 1); + if (err < 0) + return err; + buf[0] = ti9->resume[RES_TDT_TIMER]; + buf[1] = ti9->resume[RES_TDT_H_THRESH]; + buf[2] = ti9->resume[RES_TDT_L_THRESH]; + buf[3] = ti9->resume[RES_TAP_TIMER]; + buf[4] = ti9->resume[RES_TOTAL_TIMER]; + buf[5] = ti9->resume[RES_LAT_TIMER]; + buf[6] = ti9->resume[RES_WIN_TIMER]; + err = kxti9_i2c_write(ti9, TDT_TIMER, buf, 7); + if (err < 0) + return err; + err = kxti9_i2c_write(ti9, INT_CTRL1, &ti9->resume[RES_INT_CTRL1], 1); + if (err < 0) + return err; + buf[0] = (ti9->resume[RES_CTRL_REG1] | PC1_ON); + err = kxti9_i2c_write(ti9, CTRL_REG1, buf, 1); + if (err < 0) + return err; + ti9->resume[RES_CTRL_REG1] = buf[0]; + ti9->hw_initialized = 1; + + return 0; +} + +static void kxti9_device_power_off(struct kxti9_data *ti9) +{ + int err; + u8 buf = PC1_OFF; + + err = kxti9_i2c_write(ti9, CTRL_REG1, &buf, 1); + if (err < 0) + dev_err(&ti9->client->dev, "soft power off failed\n"); +#ifdef KXTI9_INT_MODE + disable_irq(ti9->irq); +#endif + if (ti9->pdata->power_off) + ti9->pdata->power_off(); + ti9->hw_initialized = 0; +} + +static int kxti9_device_power_on(struct kxti9_data *ti9) +{ + int err; + + if (ti9->pdata->power_on) { + err = ti9->pdata->power_on(); + if (err < 0) + return err; + } +#ifdef KXTI9_INT_MODE + enable_irq(ti9->irq); +#endif + if (!ti9->hw_initialized) { + msleep(100); + err = kxti9_hw_init(ti9); + if (err < 0) { + kxti9_device_power_off(ti9); + return err; + } + } + + return 0; +} + +#ifdef KXTI9_INT_MODE +static irqreturn_t kxti9_isr(int irq, void *dev) +{ + struct kxti9_data *ti9 = dev; + + disable_irq_nosync(irq); + schedule_work(&ti9->irq_work); + + return IRQ_HANDLED; +} +#endif + +static u8 kxti9_resolve_dir(struct kxti9_data *ti9, u8 dir) +{ + switch (dir) { + case 0x20: /* -X */ + if (ti9->pdata->negate_x) + dir = 0x10; + if (ti9->pdata->axis_map_y == 0) + dir >>= 2; + if (ti9->pdata->axis_map_z == 0) + dir >>= 4; + break; + case 0x10: /* +X */ + if (ti9->pdata->negate_x) + dir = 0x20; + if (ti9->pdata->axis_map_y == 0) + dir >>= 2; + if (ti9->pdata->axis_map_z == 0) + dir >>= 4; + break; + case 0x08: /* -Y */ + if (ti9->pdata->negate_y) + dir = 0x04; + if (ti9->pdata->axis_map_x == 1) + dir <<= 2; + if (ti9->pdata->axis_map_z == 1) + dir >>= 2; + break; + case 0x04: /* +Y */ + if (ti9->pdata->negate_y) + dir = 0x08; + if (ti9->pdata->axis_map_x == 1) + dir <<= 2; + if (ti9->pdata->axis_map_z == 1) + dir >>= 2; + break; + case 0x02: /* -Z */ + if (ti9->pdata->negate_z) + dir = 0x01; + if (ti9->pdata->axis_map_x == 2) + dir <<= 4; + if (ti9->pdata->axis_map_y == 2) + dir <<= 2; + break; + case 0x01: /* +Z */ + if (ti9->pdata->negate_z) + dir = 0x02; + if (ti9->pdata->axis_map_x == 2) + dir <<= 4; + if (ti9->pdata->axis_map_y == 2) + dir <<= 2; + break; + default: + return -EINVAL; + } + + return dir; +} + +static int kxti9_get_acceleration_data(struct kxti9_data *ti9, int *xyz) +{ + int err; + /* Data bytes from hardware xL, xH, yL, yH, zL, zH */ + u8 acc_data[6]; + /* x,y,z hardware values */ + int hw_d[3]; + + err = kxti9_i2c_read(ti9, XOUT_L, acc_data, 6); + if (err < 0) + return err; + + hw_d[0] = (int) (((acc_data[1]) << 8) | acc_data[0]); + hw_d[1] = (int) (((acc_data[3]) << 8) | acc_data[2]); + hw_d[2] = (int) (((acc_data[5]) << 8) | acc_data[4]); + + hw_d[0] = (hw_d[0] & 0x8000) ? ((hw_d[0] | 0xFFFF0000) + 1) : (hw_d[0]); + hw_d[1] = (hw_d[1] & 0x8000) ? ((hw_d[1] | 0xFFFF0000) + 1) : (hw_d[1]); + hw_d[2] = (hw_d[2] & 0x8000) ? ((hw_d[2] | 0xFFFF0000) + 1) : (hw_d[2]); + + hw_d[0] >>= 4; + hw_d[1] >>= 4; + hw_d[2] >>= 4; + + xyz[0] = ((ti9->pdata->negate_x) ? (-hw_d[ti9->pdata->axis_map_x]) + : (hw_d[ti9->pdata->axis_map_x])); + xyz[1] = ((ti9->pdata->negate_y) ? (-hw_d[ti9->pdata->axis_map_y]) + : (hw_d[ti9->pdata->axis_map_y])); + xyz[2] = ((ti9->pdata->negate_z) ? (-hw_d[ti9->pdata->axis_map_z]) + : (hw_d[ti9->pdata->axis_map_z])); + + /*** DEBUG OUTPUT - REMOVE ***/ + //dev_info(&ti9->client->dev, "x:%d y:%d z:%d\n", xyz[0], xyz[1], xyz[2]); + /*** DEBUG OUTPUT - REMOVE ***/ + + return err; +} + +static void kxti9_report_values(struct kxti9_data *ti9, int *xyz) +{ + input_report_abs(ti9->input_dev, ABS_X, xyz[gs_conf.xyz_axis[ABS_X][0]]*gs_conf.xyz_axis[ABS_X][1]); + input_report_abs(ti9->input_dev, ABS_Y, xyz[gs_conf.xyz_axis[ABS_Y][0]]*gs_conf.xyz_axis[ABS_Y][1]); + input_report_abs(ti9->input_dev, ABS_Z, xyz[gs_conf.xyz_axis[ABS_Z][0]]*gs_conf.xyz_axis[ABS_Z][1]); + input_sync(ti9->input_dev); +} + +#ifdef KXTI9_INT_MODE +static void kxti9_irq_work_func(struct work_struct *work) +{ +/* + * int_status output: + * [INT_SRC_REG2][INT_SRC_REG1][TILT_POS_PRE][TILT_POS_CUR] + * INT_SRC_REG1, TILT_POS_PRE, and TILT_POS_CUR directions are translated + * based on platform data variables. + */ + + int err; + int int_status = 0; + u8 status; + u8 buf[2]; + + struct kxti9_data *ti9 + = container_of(work, struct kxti9_data, irq_work); + + err = kxti9_i2c_read(ti9, INT_STATUS_REG, &status, 1); + if (err < 0) + dev_err(&ti9->client->dev, "read err int source\n"); + int_status = status << 24; + if ((status & TPS) > 0) { + err = kxti9_i2c_read(ti9, TILT_POS_CUR, buf, 2); + if (err < 0) + dev_err(&ti9->client->dev, "read err tilt dir\n"); + int_status |= kxti9_resolve_dir(ti9, buf[0]); + int_status |= kxti9_resolve_dir(ti9, buf[1]) << 8; + /*** DEBUG OUTPUT - REMOVE ***/ + dev_info(&ti9->client->dev, "IRQ TILT [%x]\n", + kxti9_resolve_dir(ti9, buf[0])); + /*** DEBUG OUTPUT - REMOVE ***/ + } + if (((status & TDTS0) | (status & TDTS1)) > 0) { + err = kxti9_i2c_read(ti9, INT_SRC_REG1, buf, 1); + if (err < 0) + dev_err(&ti9->client->dev, "read err tap dir\n"); + int_status |= (kxti9_resolve_dir(ti9, buf[0])) << 16; + /*** DEBUG OUTPUT - REMOVE ***/ + dev_info(&ti9->client->dev, "IRQ TAP%d [%x]\n", + ((status & TDTS1) ? (2) : (1)), kxti9_resolve_dir(ti9, buf[0])); + /*** DEBUG OUTPUT - REMOVE ***/ + } + /*** DEBUG OUTPUT - REMOVE ***/ + if ((status & 0x02) > 0) { + if (((status & TDTS0) | (status & TDTS1)) > 0) + dev_info(&ti9->client->dev, "IRQ WUF + TAP\n"); + else + dev_info(&ti9->client->dev, "IRQ WUF\n"); + } + /*** DEBUG OUTPUT - REMOVE ***/ + if (int_status & 0x2FFF) { + input_report_abs(ti9->input_dev, ABS_MISC, int_status); + input_sync(ti9->input_dev); + } + err = kxti9_i2c_read(ti9, INT_REL, buf, 1); + if (err < 0) + dev_err(&ti9->client->dev, + "error clearing interrupt status: %d\n", err); + + enable_irq(ti9->irq); +} +#endif + +static int kxti9_enable(struct kxti9_data *ti9) +{ + int err; + int int_status = 0; + u8 buf; + + if (!atomic_cmpxchg(&ti9->enabled, 0, 1)) { + err = kxti9_device_power_on(ti9); + err = kxti9_i2c_read(ti9, INT_REL, &buf, 1); + if (err < 0) { + dev_err(&ti9->client->dev, + "error clearing interrupt: %d\n", err); + atomic_set(&ti9->enabled, 0); + return err; + } + if ((ti9->resume[RES_CTRL_REG1] & TPE) > 0) { + err = kxti9_i2c_read(ti9, TILT_POS_CUR, &buf, 1); + if (err < 0) { + dev_err(&ti9->client->dev, + "read err current tilt\n"); + int_status |= kxti9_resolve_dir(ti9, buf); + input_report_abs(ti9->input_dev, ABS_MISC, int_status); + input_sync(ti9->input_dev); + } + } + schedule_delayed_work(&ti9->input_work, + msecs_to_jiffies(ti9->res_interval)); + } + + return 0; +} + +static int kxti9_disable(struct kxti9_data *ti9) +{ + if (atomic_cmpxchg(&ti9->enabled, 1, 0)) { + cancel_delayed_work_sync(&ti9->input_work); + kxti9_device_power_off(ti9); + } + + return 0; +} + +static void kxti9_input_work_func(struct work_struct *work) +{ + struct kxti9_data *ti9 = container_of((struct delayed_work *)work, + struct kxti9_data, input_work); + int xyz[3] = { 0 }; + + mutex_lock(&ti9->lock); + + if (kxti9_get_acceleration_data(ti9, xyz) == 0) + kxti9_report_values(ti9, xyz); + + schedule_delayed_work(&ti9->input_work, + msecs_to_jiffies(ti9->res_interval)); + + mutex_unlock(&ti9->lock); +} + +int kxti9_input_open(struct input_dev *input) +{ + struct kxti9_data *ti9 = input_get_drvdata(input); + + return kxti9_enable(ti9); +} + +void kxti9_input_close(struct input_dev *dev) +{ + struct kxti9_data *ti9 = input_get_drvdata(dev); + + kxti9_disable(ti9); +} + +static int kxti9_input_init(struct kxti9_data *ti9) +{ + int err; + + INIT_DELAYED_WORK(&ti9->input_work, kxti9_input_work_func); + ti9->input_dev = input_allocate_device(); + if (!ti9->input_dev) { + err = -ENOMEM; + dev_err(&ti9->client->dev, "input device allocate failed\n"); + goto err0; + } + //ti9->input_dev->open = kxti9_input_open; + //ti9->input_dev->close = kxti9_input_close; + + input_set_drvdata(ti9->input_dev, ti9); + + set_bit(EV_ABS, ti9->input_dev->evbit); + set_bit(ABS_MISC, ti9->input_dev->absbit); + + input_set_abs_params(ti9->input_dev, ABS_X, -G_MAX, G_MAX, FUZZ, FLAT); + input_set_abs_params(ti9->input_dev, ABS_Y, -G_MAX, G_MAX, FUZZ, FLAT); + input_set_abs_params(ti9->input_dev, ABS_Z, -G_MAX, G_MAX, FUZZ, FLAT); + + ti9->input_dev->name = "g-sensor"; + + err = input_register_device(ti9->input_dev); + if (err) { + dev_err(&ti9->client->dev, + "unable to register input polled device %s: %d\n", + ti9->input_dev->name, err); + goto err1; + } + + return 0; +err1: + input_free_device(ti9->input_dev); +err0: + return err; +} + +static void kxti9_input_cleanup(struct kxti9_data *ti9) +{ + input_unregister_device(ti9->input_dev); +} + +/* sysfs */ +static ssize_t kxti9_delay_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct kxti9_data *ti9 = i2c_get_clientdata(client); + return sprintf(buf, "%d\n", ti9->res_interval); +} + +static ssize_t kxti9_delay_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct kxti9_data *ti9 = i2c_get_clientdata(client); + int val = simple_strtoul(buf, NULL, 10); + + ti9->res_interval = max(val, ti9->pdata->min_interval); + kxti9_update_odr(ti9, ti9->res_interval); + + return count; +} + +static ssize_t kxti9_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct kxti9_data *ti9 = i2c_get_clientdata(client); + return sprintf(buf, "%d\n", atomic_read(&ti9->enabled)); +} + +static ssize_t kxti9_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct kxti9_data *ti9 = i2c_get_clientdata(client); + int val = simple_strtoul(buf, NULL, 10); + if (val) + kxti9_enable(ti9); + else + kxti9_disable(ti9); + return count; +} + +static ssize_t kxti9_tilt_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct kxti9_data *ti9 = i2c_get_clientdata(client); + u8 tilt; + + if (ti9->resume[RES_CTRL_REG1] & TPE) { + kxti9_i2c_read(ti9, TILT_POS_CUR, &tilt, 1); + return sprintf(buf, "%d\n", kxti9_resolve_dir(ti9, tilt)); + } else { + return sprintf(buf, "%d\n", 0); + } +} + +static ssize_t kxti9_tilt_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct kxti9_data *ti9 = i2c_get_clientdata(client); + int val = simple_strtoul(buf, NULL, 10); + if (val) + ti9->resume[RES_CTRL_REG1] |= TPE; + else + ti9->resume[RES_CTRL_REG1] &= (~TPE); + kxti9_i2c_write(ti9, CTRL_REG1, &ti9->resume[RES_CTRL_REG1], 1); + return count; +} + +static ssize_t kxti9_wake_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct kxti9_data *ti9 = i2c_get_clientdata(client); + u8 val = ti9->resume[RES_CTRL_REG1] & WUFE; + if (val) + return sprintf(buf, "%d\n", 1); + else + return sprintf(buf, "%d\n", 0); +} + +static ssize_t kxti9_wake_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct kxti9_data *ti9 = i2c_get_clientdata(client); + int val = simple_strtoul(buf, NULL, 10); + if (val) + ti9->resume[RES_CTRL_REG1] |= WUFE; + else + ti9->resume[RES_CTRL_REG1] &= (~WUFE); + kxti9_i2c_write(ti9, CTRL_REG1, &ti9->resume[RES_CTRL_REG1], 1); + return count; +} + +static ssize_t kxti9_tap_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct kxti9_data *ti9 = i2c_get_clientdata(client); + u8 val = ti9->resume[RES_CTRL_REG1] & TDTE; + if (val) + return sprintf(buf, "%d\n", 1); + else + return sprintf(buf, "%d\n", 0); +} + +static ssize_t kxti9_tap_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct kxti9_data *ti9 = i2c_get_clientdata(client); + int val = simple_strtoul(buf, NULL, 10); + if (val) + ti9->resume[RES_CTRL_REG1] |= TDTE; + else + ti9->resume[RES_CTRL_REG1] &= (~TDTE); + kxti9_i2c_write(ti9, CTRL_REG1, &ti9->resume[RES_CTRL_REG1], 1); + return count; +} + +static ssize_t kxti9_selftest_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct kxti9_data *ti9 = i2c_get_clientdata(client); + int val = simple_strtoul(buf, NULL, 10); + u8 ctrl = 0x00; + if (val) + ctrl = 0xCA; + kxti9_i2c_write(ti9, 0x3A, &ctrl, 1); + return count; +} + +static DEVICE_ATTR(delay, S_IRUGO|S_IWUSR, kxti9_delay_show, kxti9_delay_store); +static DEVICE_ATTR(enable, S_IRUGO|S_IWUSR, kxti9_enable_show, + kxti9_enable_store); +static DEVICE_ATTR(tilt, S_IRUGO|S_IWUSR, kxti9_tilt_show, kxti9_tilt_store); +static DEVICE_ATTR(wake, S_IRUGO|S_IWUSR, kxti9_wake_show, kxti9_wake_store); +static DEVICE_ATTR(tap, S_IRUGO|S_IWUSR, kxti9_tap_show, kxti9_tap_store); +static DEVICE_ATTR(selftest, S_IWUSR, NULL, kxti9_selftest_store); + +static struct attribute *kxti9_attributes[] = { + &dev_attr_delay.attr, + &dev_attr_enable.attr, + &dev_attr_tilt.attr, + &dev_attr_wake.attr, + &dev_attr_tap.attr, + &dev_attr_selftest.attr, + NULL +}; + +static struct attribute_group kxti9_attribute_group = { + .attrs = kxti9_attributes +}; +/* /sysfs */ +static int __devinit kxti9_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int err = -1; + struct kxti9_data *ti9 = kzalloc(sizeof(*ti9), GFP_KERNEL); + gs_ti9 = ti9; + if (ti9 == NULL) { + dev_err(&client->dev, + "failed to allocate memory for module data\n"); + err = -ENOMEM; + goto err0; + } + /* + if (client->dev.platform_data == NULL) { + dev_err(&client->dev, "platform data is NULL; exiting\n"); + err = -ENODEV; + goto err0; + } + */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(&client->dev, "client not i2c capable\n"); + err = -ENODEV; + goto err0; + } + mutex_init(&ti9->lock); + mutex_lock(&ti9->lock); + ti9->client = client; + i2c_set_clientdata(client, ti9); + +#ifdef KXTI9_INT_MODE + INIT_WORK(&ti9->irq_work, kxti9_irq_work_func); +#endif + ti9->pdata = kmalloc(sizeof(*ti9->pdata), GFP_KERNEL); + if (ti9->pdata == NULL) + goto err1; + + err = sysfs_create_group(&client->dev.kobj, &kxti9_attribute_group); + if (err) + goto err1; + + //memcpy(ti9->pdata, client->dev.platform_data, sizeof(*ti9->pdata)); + memcpy(ti9->pdata, &kxti9_pdata, sizeof(*ti9->pdata)); + + if (ti9->pdata->init) { + err = ti9->pdata->init(); + if (err < 0) + goto err2; + } + +#ifdef KXTI9_INT_MODE + ti9->irq = gpio_to_irq(ti9->pdata->gpio); +#endif + + memset(ti9->resume, 0, ARRAY_SIZE(ti9->resume)); + ti9->resume[RES_DATA_CTRL] = ti9->pdata->data_odr_init; + ti9->resume[RES_CTRL_REG1] = ti9->pdata->ctrl_reg1_init; + ti9->resume[RES_INT_CTRL1] = ti9->pdata->int_ctrl_init; + ti9->resume[RES_TILT_TIMER] = ti9->pdata->tilt_timer_init; + ti9->resume[RES_CTRL_REG3] = ti9->pdata->engine_odr_init; + ti9->resume[RES_WUF_TIMER] = ti9->pdata->wuf_timer_init; + ti9->resume[RES_WUF_THRESH] = ti9->pdata->wuf_thresh_init; + ti9->resume[RES_TDT_TIMER] = ti9->pdata->tdt_timer_init; + ti9->resume[RES_TDT_H_THRESH] = ti9->pdata->tdt_h_thresh_init; + ti9->resume[RES_TDT_L_THRESH] = ti9->pdata->tdt_l_thresh_init; + ti9->resume[RES_TAP_TIMER] = ti9->pdata->tdt_tap_timer_init; + ti9->resume[RES_TOTAL_TIMER] = ti9->pdata->tdt_total_timer_init; + ti9->resume[RES_LAT_TIMER] = ti9->pdata->tdt_latency_timer_init; + ti9->resume[RES_WIN_TIMER] = ti9->pdata->tdt_window_timer_init; + ti9->res_interval = ti9->pdata->poll_interval; + + err = kxti9_device_power_on(ti9); + if (err < 0) + goto err3; + atomic_set(&ti9->enabled, 1); + + err = kxti9_verify(ti9); + if (err < 0) { + dev_err(&client->dev, "unresolved i2c client\n"); + goto err4; + } + + err = kxti9_update_g_range(ti9, ti9->pdata->g_range); + if (err < 0) + goto err4; + + err = kxti9_update_odr(ti9, ti9->res_interval); + if (err < 0) + goto err4; + + err = kxti9_input_init(ti9); + if (err < 0) + goto err4; + + kxti9_device_power_off(ti9); + atomic_set(&ti9->enabled, 0); + +#ifdef KXTI9_INT_MODE + err = request_irq(ti9->irq, kxti9_isr, + IRQF_TRIGGER_RISING | IRQF_DISABLED, "kxti9-irq", ti9); + if (err < 0) { + pr_err("%s: request irq failed: %d\n", __func__, err); + goto err5; + } + disable_irq_nosync(ti9->irq); +#endif + + mutex_unlock(&ti9->lock); + + return 0; + +#ifdef KXTI9_INT_MODE +err5: +#endif + kxti9_input_cleanup(ti9); +err4: + kxti9_device_power_off(ti9); +err3: + if (ti9->pdata->exit) + ti9->pdata->exit(); +err2: + kfree(ti9->pdata); + sysfs_remove_group(&client->dev.kobj, &kxti9_attribute_group); +err1: + mutex_unlock(&ti9->lock); + kfree(ti9); +err0: + return err; +} + +static int __devexit kxti9_remove(struct i2c_client *client) +{ + struct kxti9_data *ti9 = i2c_get_clientdata(client); + +#ifdef KXTI9_INT_MODE + free_irq(ti9->irq, ti9); + gpio_free(ti9->pdata->gpio); +#endif + kxti9_input_cleanup(ti9); + kxti9_device_power_off(ti9); + if (ti9->pdata->exit) + ti9->pdata->exit(); + kfree(ti9->pdata); + sysfs_remove_group(&client->dev.kobj, &kxti9_attribute_group); + kfree(ti9); + + return 0; +} + +#ifdef CONFIG_PM +static int kxti9_resume(struct i2c_client *client) +{ + //struct kxti9_data *ti9 = i2c_get_clientdata(client); + //return kxti9_enable(ti9); + return 0; +} + +static int kxti9_suspend(struct i2c_client *client, pm_message_t mesg) +{ + //struct kxti9_data *ti9 = i2c_get_clientdata(client); + //return kxti9_disable(ti9); + return 0; +} + +static void kxti9_shutdown(struct i2c_client *client) +{ + struct kxti9_data *ti9 = i2c_get_clientdata(client); + if (atomic_read(&ti9->enabled)) { + flush_delayed_work_sync(&ti9->input_work); + cancel_delayed_work_sync(&ti9->input_work); + } +} +#endif + +static const struct i2c_device_id kxti9_id[] = { + {NAME, 0}, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, kxti9_id); + +static struct i2c_driver kxti9_driver = { + .driver = { + .name = NAME, + }, + .probe = kxti9_probe, + .remove = __devexit_p(kxti9_remove), + .resume = kxti9_resume, + .suspend = kxti9_suspend, + .shutdown = kxti9_shutdown, + .id_table = kxti9_id, +}; + +int kxti9_enable_accel(int en) +{ + printk(KERN_DEBUG "%s: enable = %d\n", __func__, en); + if (en) + kxti9_enable(gs_ti9); + else + kxti9_disable(gs_ti9); + return 0; +} + +int kxti9_setDelay(int mdelay) +{ + printk(KERN_DEBUG "%s: delay = %d\n", __func__, mdelay); + gs_ti9->res_interval = mdelay; + return kxti9_update_odr(gs_ti9, gs_ti9->res_interval); +} + +int kxti9_getLSG(int *lsg) +{ + int max_count; + if (gs_ti9->resume[RES_CTRL_REG1] & RES_12BIT) + max_count = 2048; + else + max_count = 128; + + if ((gs_ti9->resume[RES_CTRL_REG1] & 0x18) == KXTI9_G_2G) + *lsg = max_count >> 1; + else if ((gs_ti9->resume[RES_CTRL_REG1] & 0x18) == KXTI9_G_4G) + *lsg = max_count >> 2; + else if ((gs_ti9->resume[RES_CTRL_REG1] & 0x18) == KXTI9_G_8G) + *lsg = max_count >> 3; + + printk(KERN_DEBUG "%s: LSG = %d\n", __func__, *lsg); + return 0; +} + +struct gsensor_data kxti9_gs_data = { + .i2c_addr = KXTI9_I2C_ADDR, + .enable = kxti9_enable_accel, + .setDelay = kxti9_setDelay, + .getLSG = kxti9_getLSG, +}; + +static int __init kxti9_init(void) +{ + if (get_gsensor_conf(&gs_conf)) + return -1; + + if (gs_conf.op != 2) + return -1; + + printk("G-Sensor kxti9 init\n"); + + if (gsensor_register(&kxti9_gs_data)) + return -1; + + if (gsensor_i2c_register_device() < 0) + return -1; + + return i2c_add_driver(&kxti9_driver); +} + +static void __exit kxti9_exit(void) +{ + i2c_del_driver(&kxti9_driver); +} + +module_init(kxti9_init); +module_exit(kxti9_exit); + +MODULE_DESCRIPTION("KXTI9 accelerometer driver"); +MODULE_AUTHOR("Chris Hudson "); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/sensor/TP_DRIVER_NOT_USE/kxti9_gsensor/kxti9.h b/drivers/input/sensor/TP_DRIVER_NOT_USE/kxti9_gsensor/kxti9.h new file mode 100755 index 00000000..c66c740a --- /dev/null +++ b/drivers/input/sensor/TP_DRIVER_NOT_USE/kxti9_gsensor/kxti9.h @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2009, Kionix, Inc. All Rights Reserved. + * Written by Chris Hudson + * + * 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 . + * + */ + +#ifndef __KXTI9_H__ +#define __KXTI9_H__ + +#define KXTI9_I2C_ADDR 0x0F +/* CONTROL REGISTER 1 BITS */ +#define RES_12BIT 0x40 +#define KXTI9_G_2G 0x00 +#define KXTI9_G_4G 0x08 +#define KXTI9_G_8G 0x10 +#define SHIFT_ADJ_2G 4 +#define SHIFT_ADJ_4G 3 +#define SHIFT_ADJ_8G 2 +#define TPE 0x01 /* tilt position function enable bit */ +#define WUFE 0x02 /* wake-up function enable bit */ +#define TDTE 0x04 /* tap/double-tap function enable bit */ +/* CONTROL REGISTER 3 BITS */ +#define OTP1_6 0x00 /* tilt ODR masks */ +#define OTP6_3 0x20 +#define OTP12_5 0x40 +#define OTP50 0x60 +#define OWUF25 0x00 /* wuf ODR masks */ +#define OWUF50 0x01 +#define OWUF100 0x02 +#define OWUF200 0x03 +#define OTDT50 0x00 /* tdt ODR masks */ +#define OTDT100 0x04 +#define OTDT200 0x08 +#define OTDT400 0x0C +/* INTERRUPT CONTROL REGISTER 1 BITS */ +#define KXTI9_IEN 0x20 /* interrupt enable */ +#define KXTI9_IEA 0x10 /* interrupt polarity */ +#define KXTI9_IEL 0x08 /* interrupt response */ +#define IEU 0x04 /* alternate unlatched response */ +/* DATA CONTROL REGISTER BITS */ +#define ODR800F 0x06 /* lpf output ODR masks */ +#define ODR400F 0x05 +#define ODR200F 0x04 +#define ODR100F 0x03 +#define ODR50F 0x02 +#define ODR25F 0x01 +#define ODR12_5F 0x00 + +#ifdef __KERNEL__ +struct kxti9_platform_data { + int poll_interval; + int min_interval; + + u8 g_range; + u8 shift_adj; + + u8 axis_map_x; + u8 axis_map_y; + u8 axis_map_z; + + u8 negate_x; + u8 negate_y; + u8 negate_z; + + u8 data_odr_init; + u8 ctrl_reg1_init; + u8 int_ctrl_init; + u8 tilt_timer_init; + u8 engine_odr_init; + u8 wuf_timer_init; + u8 wuf_thresh_init; + u8 tdt_timer_init; + u8 tdt_h_thresh_init; + u8 tdt_l_thresh_init; + u8 tdt_tap_timer_init; + u8 tdt_total_timer_init; + u8 tdt_latency_timer_init; + u8 tdt_window_timer_init; + + int (*init)(void); + void (*exit)(void); + int (*power_on)(void); + int (*power_off)(void); + + int gpio; +}; +#endif /* __KERNEL__ */ + +#endif /* __KXTI9_H__ */ + diff --git a/drivers/input/sensor/cm3232/Makefile b/drivers/input/sensor/cm3232/Makefile new file mode 100755 index 00000000..06c28edd --- /dev/null +++ b/drivers/input/sensor/cm3232/Makefile @@ -0,0 +1,34 @@ +KERNELDIR=../../../../ +#KERNELDIR=/home/hangyan/android8850/kernel/ANDROID_3.0.8 +CROSS = arm_1103_le- +CC= $(CROSS)gcc +LD= $(CROSS)ld +STRIP = $(CROSS)strip + +DEBUG = n + +# Add your debugging flag (or not) to EXTRA_CFLAGS +ifeq ($(DEBUG),y) +# DEBFLAGS = -O -g -DSCULL_DEBUG # "-O" is needed to expand inlines +DEBFLAGS = -O0 -g -DSCULL_DEBUG # "-O" is needed to expand inlines + +else + DEBFLAGS = -O2 -Wall +endif + +EXTRA_CFLAGS += $(DEBFLAGS) + + +MY_MODULE_NAME=s_wmt_lsensor_cm3232 + +obj-m := $(MY_MODULE_NAME).o +$(MY_MODULE_NAME)-objs := cm3232.o + +default: + $(MAKE) -C $(KERNELDIR) SUBDIRS=$(PWD) modules + $(STRIP) --strip-debug $(MY_MODULE_NAME).ko + rm -rf *.o *~ core .depend .*.cmd *.mod.c .tmp_versions + +clean: + rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions *.order *.symvers + diff --git a/drivers/input/sensor/cm3232/cm3232.c b/drivers/input/sensor/cm3232/cm3232.c new file mode 100755 index 00000000..207ddd3a --- /dev/null +++ b/drivers/input/sensor/cm3232/cm3232.c @@ -0,0 +1,855 @@ +/* + * cm3232.c - Intersil cm3232 ALS & Proximity Driver + * + * By Intersil Corp + * Michael DiGioia + * + * Based on isl29011.c + * by Mike DiGioia + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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; version 2 of the License. + * + * 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, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +//#include + +#include "../sensor.h" + +/* Insmod parameters */ +//I2C_CLIENT_INSMOD_1(cm3232); +#define SENSOR_I2C_NAME "cm3232" +#define SENSOR_I2C_ADDR 0x10 +#define MODULE_NAME "cm3232" + +#define REG_CMD_1 0x00 +#define REG_CMD_2 0x01 +#define REG_DATA_LSB 0x02 +#define REG_DATA_MSB 0x03 +#define ISL_MOD_MASK 0xE0 +#define ISL_MOD_POWERDOWN 0 +#define ISL_MOD_ALS_ONCE 1 +#define ISL_MOD_IR_ONCE 2 +#define ISL_MOD_RESERVED 4 +#define ISL_MOD_ALS_CONT 5 +#define ISL_MOD_IR_CONT 6 +#define IR_CURRENT_MASK 0xC0 +#define IR_FREQ_MASK 0x30 +#define SENSOR_RANGE_MASK 0x03 +#define ISL_RES_MASK 0x0C + + +#undef dbg +#define dbg(fmt, args...) //printk(KERN_ALERT "[%s]: " fmt, __FUNCTION__ , ## args) + +#undef errlog +#undef klog +#define errlog(fmt, args...) printk(KERN_ERR "[%s]: " fmt, __FUNCTION__, ## args) +#define klog(fmt, args...) printk(KERN_ALERT "[%s]: " fmt, __FUNCTION__, ## args) + +extern int wmt_getsyspara(char *varname, unsigned char *varval, int *varlen); +static int no_adc_map = 1; +static int last_mod; + +static struct i2c_client *this_client = NULL; + +struct isl_device { + struct input_polled_dev* input_poll_dev; + struct i2c_client* client; + int resolution; + int range; + int isdbg; +#ifdef CONFIG_HAS_EARLYSUSPEND + struct early_suspend earlysuspend; +#endif + +}; + +static struct isl_device* l_sensorconfig = NULL; +static struct kobject *android_lsensor_kobj = NULL; +static int l_enable = 0; // 0:don't report data, 1 + +static DEFINE_MUTEX(mutex); + +#if 0 +static int isl_set_mod(struct i2c_client *client, int mod) +{ + int ret, val, freq; + + switch (mod) { + case ISL_MOD_POWERDOWN: + case ISL_MOD_RESERVED: + goto setmod; + case ISL_MOD_ALS_ONCE: + case ISL_MOD_ALS_CONT: + freq = 0; + break; + case ISL_MOD_IR_ONCE: + case ISL_MOD_IR_CONT: + freq = 1; + break; + default: + return -EINVAL; + } + /* set IR frequency */ + val = i2c_smbus_read_byte_data(client, REG_CMD_2); + if (val < 0) + return -EINVAL; + val &= ~IR_FREQ_MASK; + if (freq) + val |= IR_FREQ_MASK; + ret = i2c_smbus_write_byte_data(client, REG_CMD_2, val); + if (ret < 0) + return -EINVAL; + +setmod: + /* set operation mod */ + val = i2c_smbus_read_byte_data(client, REG_CMD_1); + if (val < 0) + return -EINVAL; + val &= ~ISL_MOD_MASK; + val |= (mod << 5); + ret = i2c_smbus_write_byte_data(client, REG_CMD_1, val); + if (ret < 0) + return -EINVAL; + + if (mod != ISL_MOD_POWERDOWN) + last_mod = mod; + + return mod; +} + +static int isl_get_res(struct i2c_client *client) +{ + int val; + + printk(KERN_INFO MODULE_NAME ": %s cm3232 get_res call, \n", __func__); + val = i2c_smbus_read_word_data(client, 0)>>8 & 0xff; + + if (val < 0) + return -EINVAL; + + val &= ISL_RES_MASK; + val >>= 2; + + switch (val) { + case 0: + return 65536; + case 1: + return 4096; + case 2: + return 256; + case 3: + return 16; + default: + return -EINVAL; + } +} + +static int isl_get_range(struct i2c_client* client) +{ + switch (i2c_smbus_read_word_data(client, 0)>>8 & 0xff & 0x3) { + case 0: return 1000; + case 1: return 4000; + case 2: return 16000; + case 3: return 64000; + default: return -EINVAL; + } +} +#endif +//Fixme plan to transfer the adc value to the config.xml lux 2013-5-10 +static __u16 uadc[8] = {2, 8, 100, 400, 900, 1000, 1500, 1900};//customize +static __u16 ulux[9] = {128, 200, 1300, 2000, 3000, 4000, 5000, 6000, 7000}; +static __u16 adc_to_lux(__u16 adc) +{ + static long long var = 0; + int i = 0; //length of array is 8,9 + for (i=0; i<8; i++) { + if ( adc < uadc[i]){ + break; + } + } + if ( i<9) + { + var++; + if (var%2) + return ulux[i]+0; + else + return ulux[i]+1; + } + return ulux[4]; +} + + + +static int isl_get_lux_data(struct i2c_client* client) +{ + //struct isl_device* idev = i2c_get_clientdata(client); + + //__u16 resH = 0, resL = 0; + __s16 resH = 0, resL = 0; + //int range; + resL = i2c_smbus_read_word_data(client, 0x50); + //resH = i2c_smbus_read_word_data(client, 0x51)&0xff00; + if ((resL < 0) || (resH < 0)) + { + errlog("Error to read lux_data!\n"); + + return 3000;//??? + //return -1; + } + //Fixme plan to transfer the adc value to the config.xml lux 2013-5-10 + if (!no_adc_map) + resL = adc_to_lux(resL); + //printk("<<<< lux %d\n", resL); + return resL ;//* idev->range / idev->resolution; + return (resH | resL) ;//* idev->range / idev->resolution; +} + + +static int isl_set_default_config(struct i2c_client *client) +{ + //struct isl_device* idev = i2c_get_clientdata(client); + + int ret=0; + //ret = _cm3232_I2C_Write_Byte(CM3232_SLAVE_addr, CM3232_ALS_RESET); + ret = i2c_smbus_write_byte_data(client, 0, (1 << 6)); + if (ret < 0) + return -EINVAL; + //if(ret<0) + //return ret; + msleep(10); + + //ret = _cm3232_I2C_Write_Byte(CM3232_SLAVE_addr, CM3232_ALS_IT_200ms | CM3232_ALS_HS_HIGH ); + ret = i2c_smbus_write_byte_data(client, 0, (1 << 2)|(1 << 1)); + if (ret < 0) + return -EINVAL; + msleep(10); + return 0; +/* We don't know what it does ... */ +// ret = i2c_smbus_write_byte_data(client, REG_CMD_1, 0xE0); +// ret = i2c_smbus_write_byte_data(client, REG_CMD_2, 0xC3); +/* Set default to ALS continuous */ + ret = i2c_smbus_write_byte_data(client, REG_CMD_1, 0xA0); + if (ret < 0) + return -EINVAL; +/* Range: 0~16000, number of clock cycles: 65536 */ + ret = i2c_smbus_write_byte_data(client, REG_CMD_2, 0x02); // vivienne + if (ret < 0) + return -EINVAL; + //idev->resolution = isl_get_res(client); + //idev->range = isl_get_range(client);; + dbg("cm3232 set_default_config call, \n"); + + return 0; +} + +/* Return 0 if detection is successful, -ENODEV otherwise */ +static int cm3232_detect(struct i2c_client *client/*, int kind, + struct i2c_board_info *info*/) +{ + + + return 0; +} + +int isl_input_open(struct input_dev* input) +{ + return 0; +} + +void isl_input_close(struct input_dev* input) +{ +} + +static void isl_input_lux_poll(struct input_polled_dev *dev) +{ + struct isl_device* idev = dev->private; + struct input_dev* input = idev->input_poll_dev->input; + struct i2c_client* client = idev->client; + static unsigned int val=0; + + //printk("%s\n", __FUNCTION__); + if (l_enable != 0) + { + mutex_lock(&mutex); + //pm_runtime_get_sync(dev); + #if 0 + if(val>0x2000) + val=0; + val+=100; + #endif + //printk(KERN_ALERT "by flashchen val is %x",val); + input_report_abs(input, ABS_MISC, isl_get_lux_data(client)); + //input_report_abs(input, ABS_MISC, val);//isl_get_lux_data(client)); + input_sync(input); + //pm_runtime_put_sync(dev); + mutex_unlock(&mutex); + } +} + +static struct i2c_device_id cm3232_id[] = { + {"cm3232", 0}, + {} +}; + +#if 0 +static int cm3232_runtime_suspend(struct device *dev) +{ + + dev_dbg(dev, "suspend\n"); + + mutex_lock(&mutex); + pm_runtime_get_sync(dev); + //isl_set_mod(client, ISL_MOD_POWERDOWN); + pm_runtime_put_sync(dev); + mutex_unlock(&mutex); + + printk(KERN_INFO MODULE_NAME ": %s cm3232 suspend call, \n", __func__); + return 0; +} + +static int cm3232_runtime_resume(struct device *dev) +{ + + dev_dbg(dev, "resume\n"); + + mutex_lock(&mutex); + pm_runtime_get_sync(dev); + //isl_set_mod(client, last_mod); + pm_runtime_put_sync(dev); + mutex_unlock(&mutex); + + printk(KERN_INFO MODULE_NAME ": %s cm3232 resume call, \n", __func__); + return 0; +} +#endif +MODULE_DEVICE_TABLE(i2c, cm3232_id); + +/*static const struct dev_pm_ops cm3232_pm_ops = { + .runtime_suspend = cm3232_runtime_suspend, + .runtime_resume = cm3232_runtime_resume, +}; + +static struct i2c_board_info isl_info = { + I2C_BOARD_INFO("cm3232", 0x44), +}; + +static struct i2c_driver cm3232_driver = { + .driver = { + .name = "cm3232", + .pm = &cm3232_pm_ops, + }, + .probe = cm3232_probe, + .remove = cm3232_remove, + .id_table = cm3232_id, + .detect = cm3232_detect, + //.address_data = &addr_data, +};*/ + +static int mmad_open(struct inode *inode, struct file *file) +{ + dbg("Open the l-sensor node...\n"); + return 0; +} + +static int mmad_release(struct inode *inode, struct file *file) +{ + dbg("Close the l-sensor node...\n"); + return 0; +} + +static ssize_t mmad_read(struct file *fl, char __user *buf, size_t cnt, loff_t *lf) +{ + int lux_data = 0; + + mutex_lock(&mutex); + lux_data = isl_get_lux_data(l_sensorconfig->client); + mutex_unlock(&mutex); + if (lux_data < 0) + { + errlog("Failed to read lux data!\n"); + return -1; + } + printk(KERN_ALERT "lux_data is %x\n",lux_data); + //return 0; + copy_to_user(buf, &lux_data, sizeof(lux_data)); + return sizeof(lux_data); +} + +static long +mmad_ioctl(/*struct inode *inode,*/ struct file *file, unsigned int cmd, + unsigned long arg) +{ + void __user *argp = (void __user *)arg; + //char rwbuf[5]; + short enable; //amsr = -1; + unsigned int uval; + + dbg("l-sensor ioctr...\n"); + //memset(rwbuf, 0, sizeof(rwbuf)); + switch (cmd) { + case LIGHT_IOCTL_SET_ENABLE: + // enable/disable sensor + if (copy_from_user(&enable, argp, sizeof(short))) + { + printk(KERN_ERR "Can't get enable flag!!!\n"); + return -EFAULT; + } + dbg("enable=%d\n",enable); + if ((enable >=0) && (enable <=1)) + { + dbg("driver: disable/enable(%d) gsensor.\n", enable); + + //l_sensorconfig.sensor_enable = enable; + dbg("Should to implement d/e the light sensor!\n"); + l_enable = enable; + + } else { + printk(KERN_ERR "Wrong enable argument in %s !!!\n", __FUNCTION__); + return -EINVAL; + } + break; + case WMT_IOCTL_SENSOR_GET_DRVID: +#define CM3232_DRVID 0 + uval = CM3232_DRVID ; + if (copy_to_user((unsigned int*)arg, &uval, sizeof(unsigned int))) + { + return -EFAULT; + } + dbg("cm3232_driver_id:%d\n",uval); + default: + break; + } + + return 0; +} + + +static struct file_operations mmad_fops = { + .owner = THIS_MODULE, + .open = mmad_open, + .release = mmad_release, + .read = mmad_read, + .unlocked_ioctl = mmad_ioctl, +}; + + +static struct miscdevice mmad_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "lsensor_ctrl", + .fops = &mmad_fops, +}; + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void cm3232_early_suspend(struct early_suspend *h) +{ + struct i2c_client *client = l_sensorconfig->client; + + dbg("start\n"); + mutex_lock(&mutex); + //pm_runtime_get_sync(dev); + //isl_set_mod(client, ISL_MOD_POWERDOWN); + //pm_runtime_put_sync(dev); + mutex_unlock(&mutex); + dbg("exit\n"); +} + +static void cm3232_late_resume(struct early_suspend *h) +{ + struct i2c_client *client = l_sensorconfig->client; + + dbg("start\n"); + mutex_lock(&mutex); + //pm_runtime_get_sync(dev); + //isl_set_mod(client, last_mod); + isl_set_default_config(client); + //pm_runtime_put_sync(dev); + mutex_unlock(&mutex); + dbg("exit\n"); +} +#endif +static ssize_t adc_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + + int i; + int size = sizeof(uadc)/sizeof(uadc[0]); + printk("<<<%s\n", __FUNCTION__); + for (i=0; i>>\n", buf); + n = sscanf(buf, "%d:%d", &index, &tmp); + printk("<<<=0 && index=0; i--) + device_remove_file(dev, &attr[i]);//&attr[i].attr + } + return err; +} + +static void device_remove_attribute(struct device *dev, struct device_attribute *attr) +{ + int i; + for (i=0; attr[i].attr.name != NULL; i++) + device_remove_file(dev, &attr[i]); //&attr[i].attr +} + + +static int get_adc_val(void) +{ + int i, varlen, n; + __u32 buf[8]; + char varbuf[50]; + char *name = "wmt.io.lsensor"; + + varlen = sizeof(varbuf); + if (wmt_getsyspara(name, varbuf, &varlen)) + { + printk("<<<dev); + idev->input_poll_dev = input_allocate_polled_device(); + if(!idev->input_poll_dev) + { + res = -ENOMEM; + goto err_input_allocate_device; + } + idev->client = client; + idev->input_poll_dev->private = idev; + idev->input_poll_dev->poll = isl_input_lux_poll; + idev->input_poll_dev->poll_interval = 100;//50; + idev->input_poll_dev->input->open = isl_input_open; + idev->input_poll_dev->input->close = isl_input_close; + idev->input_poll_dev->input->name = "lsensor_lux"; + idev->input_poll_dev->input->id.bustype = BUS_I2C; + idev->input_poll_dev->input->dev.parent = &client->dev; + input_set_drvdata(idev->input_poll_dev->input, idev); + input_set_capability(idev->input_poll_dev->input, EV_ABS, ABS_MISC); + input_set_abs_params(idev->input_poll_dev->input, ABS_MISC, 0, 16000, 0, 0); + i2c_set_clientdata(client, idev); + /* set default config after set_clientdata */ + res = isl_set_default_config(client); + res = misc_register(&mmad_device); + if (res) { + errlog("mmad_device register failed\n"); + goto err_misc_register; + } + res = input_register_polled_device(idev->input_poll_dev); + if(res < 0) + goto err_input_register_device; + // suspend/resume register +#ifdef CONFIG_HAS_EARLYSUSPEND + idev->earlysuspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; + idev->earlysuspend.suspend = cm3232_early_suspend; + idev->earlysuspend.resume = cm3232_late_resume; + register_early_suspend(&(idev->earlysuspend)); +#endif + + dbg("cm3232 probe succeed!\n"); + //create class device sysdevice 2013-5-10 + //get_adc_val(); + sclass = class_create(THIS_MODULE, "cm3232"); + if (IS_ERR(sclass)) + { + printk("<<<%s fail to create class!\n", __FUNCTION__); + return 0; + } + + + sret = alloc_chrdev_region(&sdev_no, 0, 1, "cm3232_devno"); + if (sret) + { + printk("<<<<%s alloc_chrdev_region fail!\n", __FUNCTION__); + class_destroy(sclass); + return 0; + } + sdevice = device_create(sclass, NULL, sdev_no, NULL, "cm3232_dev"); + if (IS_ERR(sdevice)) + { + printk("<<<%s device_create fail!\n", __FUNCTION__); + class_destroy(sclass); + return 0; + } + device_create_attribute(sdevice, cm3232_attr); + + return 0; +err_input_register_device: + misc_deregister(&mmad_device); + input_free_polled_device(idev->input_poll_dev); +err_misc_register: +err_input_allocate_device: + //__pm_runtime_disable(&client->dev, false); + + kobject_del(android_lsensor_kobj); + + kfree(idev); + return res; +} + +static int cm3232_remove(struct i2c_client *client) +{ + struct isl_device* idev = i2c_get_clientdata(client); + if (!IS_ERR(sdevice)) + { + device_remove_attribute(sdevice, cm3232_attr); + device_destroy(sclass, sdev_no); + class_destroy(sclass); + + } + //unregister_early_suspend(&(idev->earlysuspend)); + misc_deregister(&mmad_device); + input_unregister_polled_device(idev->input_poll_dev); + input_free_polled_device(idev->input_poll_dev); + //sysfs_remove_group(android_lsensor_kobj, &m_isl_gr); + kobject_del(android_lsensor_kobj); + //__pm_runtime_disable(&client->dev, false); + kfree(idev); + printk(KERN_INFO MODULE_NAME ": %s cm3232 remove call, \n", __func__); + return 0; +} +//****************add platform_device & platform_driver for suspend &resume 2013-7-2 +static int ls_probe(struct platform_device *pdev){ + //printk("<<<%s\n", __FUNCTION__); + return 0; +} +static int ls_remove(struct platform_device *pdev){ + //printk("<<<%s\n", __FUNCTION__); + return 0; +} +static int ls_suspend(struct platform_device *pdev, pm_message_t state){ + printk("<<<%s\n", __FUNCTION__); + + return 0; +} + +static int ls_resume(struct platform_device *pdev){ + //return 0; + int ret = 0; + int count = 0; + printk("<<<%s\n", __FUNCTION__); +RETRY: + ret = isl_set_default_config(this_client); + if (ret < 0){ + printk("%s isl_set_default_config fail!\n", __FUNCTION__); + count++; + if (count < 5){ + mdelay(2); + goto RETRY; + } + else + return ret; + } + return 0; + +} +static void lsdev_release(struct device *dev) +{ + return; +} +static struct platform_device lsdev = { + .name = "lsdevice", + .id = -1, + .dev = { + .release = lsdev_release, + }, +}; +static struct platform_driver lsdrv = { + .probe = ls_probe, + .remove = ls_remove, + .suspend = ls_suspend, + .resume = ls_resume, + .driver = { + .name = "lsdevice", + }, +}; +//******************************************************************** + +static int __init sensor_cm3232_init(void) +{ + int ret = 0; + printk(KERN_INFO MODULE_NAME ": %s cm3232 init call, \n", __func__); + /* + * Force device to initialize: i2c-15 0x44 + * If i2c_new_device is not called, even cm3232_detect will not run + * TODO: rework to automatically initialize the device + */ + //i2c_new_device(i2c_get_adapter(15), &isl_info); + //return i2c_add_driver(&cm3232_driver); + if (!(this_client = sensor_i2c_register_device(2, SENSOR_I2C_ADDR, SENSOR_I2C_NAME))) + { + printk(KERN_EMERG"Can't register gsensor i2c device!\n"); + return -1; + } + if (cm3232_detect(this_client)) + { + errlog("Can't find light sensor cm3232!\n"); + goto detect_fail; + } + get_adc_val(); + if(cm3232_probe(this_client)) + { + errlog("Erro for probe!\n"); + goto detect_fail; + } + + ret = platform_device_register(&lsdev); + if (ret){ + printk("<< +#include +#include +#include +#include +#include +#include +#include +#include +//#include +#include +#include +#include +#include +#include +#include + +////////////////////////////////////////////////////////// +#define AKMIO 0xA1 + +/* IOCTLs for AKM library */ +#define ECS_IOCTL_INIT _IO(AKMIO, 0x01) +#define ECS_IOCTL_WRITE _IOW(AKMIO, 0x02, char[5]) +#define ECS_IOCTL_READ _IOWR(AKMIO, 0x03, char[5]) +#define ECS_IOCTL_RESET _IO(AKMIO, 0x04) +#define ECS_IOCTL_INT_STATUS _IO(AKMIO, 0x05) +#define ECS_IOCTL_FFD_STATUS _IO(AKMIO, 0x06) +#define ECS_IOCTL_SET_MODE _IOW(AKMIO, 0x07, short) +#define ECS_IOCTL_GETDATA _IOR(AKMIO, 0x08, char[RBUFF_SIZE+1]) +#define ECS_IOCTL_GET_NUMFRQ _IOR(AKMIO, 0x09, char[2]) +#define ECS_IOCTL_SET_PERST _IO(AKMIO, 0x0A) +#define ECS_IOCTL_SET_G0RST _IO(AKMIO, 0x0B) +#define ECS_IOCTL_SET_YPR _IOW(AKMIO, 0x0C, short[12]) +#define ECS_IOCTL_GET_OPEN_STATUS _IOR(AKMIO, 0x0D, int) +#define ECS_IOCTL_GET_CLOSE_STATUS _IOR(AKMIO, 0x0E, int) +#define ECS_IOCTL_GET_CALI_DATA _IOR(AKMIO, 0x0F, char[MAX_CALI_SIZE]) +#define ECS_IOCTL_GET_DELAY _IOR(AKMIO, 0x30, short) + +/* IOCTLs for APPs */ +#define ECS_IOCTL_APP_SET_MODE _IOW(AKMIO, 0x10, short) +#define ECS_IOCTL_APP_SET_MFLAG _IOW(AKMIO, 0x11, short) +#define ECS_IOCTL_APP_GET_MFLAG _IOW(AKMIO, 0x12, short) +//#define ECS_IOCTL_APP_SET_AFLAG _IOW(AKMIO, 0x13, short) +#define ECS_IOCTL_APP_GET_AFLAG _IOR(AKMIO, 0x14, short) +#define ECS_IOCTL_APP_SET_TFLAG _IOR(AKMIO, 0x15, short) +#define ECS_IOCTL_APP_GET_TFLAG _IOR(AKMIO, 0x16, short) +#define ECS_IOCTL_APP_RESET_PEDOMETER _IO(AKMIO, 0x17) +//#define ECS_IOCTL_APP_SET_DELAY _IOW(AKMIO, 0x18, short) +#define ECS_IOCTL_APP_GET_DELAY ECS_IOCTL_GET_DELAY +#define ECS_IOCTL_APP_SET_MVFLAG _IOW(AKMIO, 0x19, short) /* Set raw magnetic vector flag */ +#define ECS_IOCTL_APP_GET_MVFLAG _IOR(AKMIO, 0x1A, short) /* Get raw magnetic vector flag */ + +#define WMTGSENSOR_IOCTL_MAGIC 0x09 +#define WMT_IOCTL_SENSOR_CAL_OFFSET _IOW(WMTGSENSOR_IOCTL_MAGIC, 0x01, int) //offset calibration +#define ECS_IOCTL_APP_SET_AFLAG _IOW(WMTGSENSOR_IOCTL_MAGIC, 0x02, short) +#define ECS_IOCTL_APP_SET_DELAY _IOW(WMTGSENSOR_IOCTL_MAGIC, 0x03, short) +#define WMT_IOCTL_SENSOR_GET_DRVID _IOW(WMTGSENSOR_IOCTL_MAGIC, 0x04, unsigned int) + + +/* IOCTLs for pedometer */ +#define ECS_IOCTL_SET_STEP_CNT _IOW(AKMIO, 0x20, short) +////////////////////////////////////////////////////////////////// +#define SENSOR_DELAY_FASTEST 0 +#define SENSOR_DELAY_GAME 20 +#define SENSOR_DELAY_UI 60 +#define SENSOR_DELAY_NORMAL 200 + +#define DMARD06_DRVID 3 + +///////////////////////////////////////////////////////////////// + +#undef dbg +#define dbg(fmt, args...) if (l_sensorconfig.isdbg) printk(KERN_ALERT "[%s]: " fmt, __FUNCTION__, ## args) + +#undef errlog +#undef klog +#define errlog(fmt, args...) printk(KERN_ERR "[%s]: " fmt, __FUNCTION__, ## args) +#define klog(fmt, args...) printk(KERN_ALERT "[%s]: " fmt, __FUNCTION__, ## args) + +#define DMARD06_I2C_NAME "dmard06" +#define DMARD06_I2C_ADDR 0x1c + +#define GSENSOR_PROC_NAME "gsensor_config" +#define GSENSOR_MAJOR 161 +#define GSENSOR_NAME "dmard06" +#define GSENSOR_DRIVER_NAME "dmard06_drv" + +#define GSENDMARD06_UBOOT_NAME "wmt.io.d06sensor" + +#define MAX_WR_DMARD06_LEN (1+1) + +#define LSG 32 + +static char const *const ACCELEMETER_CLASS_NAME = "accelemeter"; +static char const *const DMARD06_DEVICE_NAME = "dmard06"; +//////////////////////////////////////////////////////////// +#define ID_REG_ADDR 0x0F +#define SWRESET_REG_ADDR 0x53 +#define T_REG_ADDR 0x40 +#define XYZ_REG_ADDR 0x41 +#define CTR1_REG_ADDR 0x44 +#define CTR2_REG_ADDR 0x45 +#define CTR3_REG_ADDR 0x46 +#define CTR4_REG_ADDR 0x47 +#define CTR5_REG_ADDR 0x48 +#define STAT_REG_ADDR 0x49 + + + +static int dmard06_init(void); +static void dmard06_exit(void); + +static int dmard06_file_open(struct inode*, struct file*); +static ssize_t dmard06_file_write(struct file*, const char*, size_t, loff_t*); +static ssize_t dmard06_file_read(struct file*, char*, size_t, loff_t*); +static int dmard06_file_close(struct inode*, struct file*); + +static int dmard06_i2c_suspend(struct platform_device *pdev, pm_message_t state); +static int dmard06_i2c_resume(struct platform_device *pdev); +static int dmard06_i2c_probe(void); +static int dmard06_i2c_remove(void); +static void dmard06_i2c_read_xyz(s8 *x, s8 *y, s8 *z); +static void dmard06_i2c_accel_value(s8 *val); +static int dmard06_probe( + struct platform_device *pdev); +static int dmard06_remove(struct platform_device *pdev); +static int dmard06_i2c_xyz_read_reg(u8* index ,u8 *buffer, int length); + + + +//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 int wmt_setsyspara(char *varname, unsigned char *varval); + +///////////////////////////////////////////////////////////////// +struct work_struct poll_work; +static struct mutex sense_data_mutex; + + +struct dmard06_config +{ + int op; + int int_gpio; //0-3 + int samp; + int xyz_axis[3][3]; // (axis,direction) + int irq; + struct proc_dir_entry* sensor_proc; + //int sensorlevel; + //int shake_enable; // 1--enable shake, 0--disable shake + //int manual_rotation; // 0--landance, 90--vertical + struct input_dev *input_dev; + //struct work_struct work; + struct delayed_work work; // for polling + struct workqueue_struct *queue; + int isdbg; // 0-- no debug log, 1--show debug log + int sensor_samp; // + int sensor_enable; // 0 --> disable sensor, 1 --> enable sensor + int test_pass; + spinlock_t spinlock; + int pollcnt; // the counts of polling + int offset[3]; // for calibration +#ifdef CONFIG_HAS_EARLYSUSPEND + struct early_suspend earlysuspend; +#endif +}; + +static struct dmard06_config l_sensorconfig = { + .op = 0, + .int_gpio = 3, + .samp = 16, + .xyz_axis = { + {ABS_X, -1}, + {ABS_Y, 1}, + {ABS_Z, -1}, + }, + .irq = 6, + .int_gpio = 3, + .sensor_proc = NULL, + //.sensorlevel = SENSOR_GRAVITYGAME_MODE, + //.shake_enable = 0, // default enable shake + .isdbg = 0, + .sensor_samp = 1, // 1 sample/second + .sensor_enable = 1, // enable sensor + .test_pass = 0, // for test program + .pollcnt = 0, // Don't report the x,y,z when the driver is loaded until 2~3 seconds + .offset = {0, 0, 0}, +}; + +static struct class* l_dev_class = NULL; +static struct device *l_clsdevice = NULL; + + +struct raw_data +{ + short x; + short y; + short z; +}; + +struct dev_data +{ + dev_t devno; + struct cdev cdev; + struct class *class; + struct i2c_client *client; +}; + +static struct dev_data dev; + +struct file_operations dmard06_fops = +{ + .owner = THIS_MODULE, + .read = dmard06_file_read, + .write = dmard06_file_write, + .open = dmard06_file_open, + .release = dmard06_file_close, +}; + +static int dmard06_file_open(struct inode *inode, struct file *filp) +{ + dbg("open...\n"); + + return 0; +} + +static ssize_t dmard06_file_write(struct file *filp, const char *buf, size_t count, loff_t *f_pos) +{ + dbg("write...\n"); + + return 0; +} + +unsigned int sample_rate_2_memsec(unsigned int rate) +{ + return (1000/rate); +} + +static int dmard06_packet_rptValue(int x, int y, int z) +{ + return ((0xFF&z) | ((0xFF&y)<<8) | ((0xFF&x)<<16)); +} + + +static void dmard06_work_func(struct work_struct *work) +{ + u8 buffer[3]; + //buffer[0] = 0x41; + u8 index = 0x41; + s8 x,y,z; + int xyz,tx,ty,tz; + + mutex_lock(&sense_data_mutex); + //read data + dmard06_i2c_xyz_read_reg(&index, buffer, 3); + mutex_unlock(&sense_data_mutex); + // check whether it's valid + // report the data + x = (s8)buffer[0]; + y = (s8)buffer[1]; + z = (s8)buffer[2]; + dmard06_i2c_accel_value(&x); + dmard06_i2c_accel_value(&y); + dmard06_i2c_accel_value(&z); + tx = x*l_sensorconfig.xyz_axis[0][1]+l_sensorconfig.offset[0]; + ty = y*l_sensorconfig.xyz_axis[1][1]+l_sensorconfig.offset[1]; + tz = z*l_sensorconfig.xyz_axis[2][1]+l_sensorconfig.offset[2]; + xyz = dmard06_packet_rptValue(tx, ty, tz); + input_report_abs(l_sensorconfig.input_dev, ABS_X, xyz); + + //input_report_abs(l_sensorconfig.input_dev, l_sensorconfig.xyz_axis[0][0], + // x*l_sensorconfig.xyz_axis[0][1]+l_sensorconfig.offset[0]); + //input_report_abs(l_sensorconfig.input_dev, l_sensorconfig.xyz_axis[1][0], + // y*l_sensorconfig.xyz_axis[1][1]+l_sensorconfig.offset[1]); + //input_report_abs(l_sensorconfig.input_dev, l_sensorconfig.xyz_axis[2][0], + //z*l_sensorconfig.xyz_axis[2][1]+l_sensorconfig.offset[2]); + input_sync(l_sensorconfig.input_dev); + dbg("x=%2x(tx=%2x),y=%2x(ty=%2x),z=%2x(tz=%2x),xyz=%x", + (char)x, (char)tx, (char)y, (char)ty, (char)z, (char)tz, xyz); + + // for next polling + queue_delayed_work(l_sensorconfig.queue, &l_sensorconfig.work, msecs_to_jiffies(sample_rate_2_memsec(l_sensorconfig.sensor_samp))); + //klog("%d=%d,%d=%d,%d=%d\n", l_sensorconfig.xyz_axis[0][0], x*l_sensorconfig.xyz_axis[0][1], + // l_sensorconfig.xyz_axis[1][0], y*l_sensorconfig.xyz_axis[1][1], + // l_sensorconfig.xyz_axis[2][0], z*l_sensorconfig.xyz_axis[2][1]); + //klog("the polling period:%d\n", msecs_to_jiffies(sample_rate_2_memsec(l_sensorconfig.sensor_samp))); + +} + + +static ssize_t dmard06_file_read(struct file *filp, char *buf, size_t count, loff_t *f_pos) +{ + int ret; + s8 x, y, z; + struct raw_data rdata; + + dbg("read...\n"); + mutex_lock(&sense_data_mutex); + dmard06_i2c_read_xyz(&x, &y, &z); + rdata.x = x; + rdata.y = y; + rdata.z = z; + + ret = copy_to_user(buf, &rdata, count); + mutex_unlock(&sense_data_mutex); + + return count; +} + +static int dmard06_file_close(struct inode *inode, struct file *filp) +{ + dbg("close...\n"); + + return 0; +} + +static void dmard06_platform_release(struct device *device) +{ + return; +} + + +static struct platform_device dmard06_device = { + .name = GSENSOR_NAME, + .id = 0, + .dev = { + .release = dmard06_platform_release, + }, +}; + +static struct platform_driver dmard06_driver = { + .probe = dmard06_probe, + .remove = dmard06_remove, + .suspend = dmard06_i2c_suspend, + .resume = dmard06_i2c_resume, + .driver = { + .name = GSENSOR_NAME, + }, +}; + +static int dmard06_i2c_xyz_write_reg(u8* index ,u8 *buffer, int length) +{ + /*int ret = 0; + u8 buf[MAX_WR_DMARD06_LEN]; + struct i2c_msg msg[1]; + + buf[0] = *index; + memcpy(buf+1, buffer, length); + msg[0].addr = DMARD06_I2C_ADDR; + msg[0].flags = 0 ; + msg[0].flags &= ~(I2C_M_RD); + msg[0].len = length+1; + msg[0].buf = buf; + if ((ret = wmt_i2c_xfer_continue_if_4(msg,1,0)) <= 0) + { + errlog("write error!\n"); + } + return ret;*/ + return i2c_api_do_send(0, DMARD06_I2C_ADDR, index, buffer, length); +} + +static int dmard06_i2c_xyz_read_reg(u8* index ,u8 *buffer, int length) +{ + /*int ret = 0; + + struct i2c_msg msg[] = + { + {.addr = DMARD06_I2C_ADDR, .flags = 0, .len = 1, .buf = index,}, + {.addr = DMARD06_I2C_ADDR, .flags = I2C_M_RD, .len = length, .buf = buffer,}, + }; + ret = wmt_i2c_xfer_continue_if_4(msg, 2,0); + if (ret <= 0) + { + errlog("read error!\n"); + } + return ret;*/ + return i2c_api_do_recv(0, DMARD06_I2C_ADDR, index, buffer, length); +} + +static void dmard06_i2c_read_xyz(s8 *x, s8 *y, s8 *z) +{ + + u8 buffer[3]; + //buffer[0] = 0x41; + u8 index = 0x41; + + dmard06_i2c_xyz_read_reg(&index, buffer, 3); + *x = (s8)buffer[0]; + *y = (s8)buffer[1]; + *z = (s8)buffer[2]; + dmard06_i2c_accel_value(x); + dmard06_i2c_accel_value(y); + dmard06_i2c_accel_value(z); + if (ABS_X == l_sensorconfig.xyz_axis[0][0]) + { + *x = l_sensorconfig.xyz_axis[0][1]*(*x); + *y = l_sensorconfig.xyz_axis[1][1]*(*y); + } else { + *x = l_sensorconfig.xyz_axis[0][1]*(*y); + *y = l_sensorconfig.xyz_axis[1][1]*(*x); + } + *z = l_sensorconfig.xyz_axis[2][1]*(*z); + + dbg("dmrd06:x=%x,y=%x,z=%x\n", *x, *y, *z); +} + +static void dmard06_i2c_accel_value(s8 *val) +{ + *val >>= 1; +} + +static int dmard06_CalOffset(int side) +{ + u8 buffer[3]; + //buffer[0] = 0x41; + u8 index = 0x41; + s8 x,y,z; + + //mutex_lock(&sense_data_mutex); + //read data + dmard06_i2c_xyz_read_reg(&index, buffer, 3); + //mutex_unlock(&sense_data_mutex); + // check whether it's valid + // report the data + x = (s8)buffer[0]; + y = (s8)buffer[1]; + z = (s8)buffer[2]; + dmard06_i2c_accel_value(&x); + dmard06_i2c_accel_value(&y); + dmard06_i2c_accel_value(&z); + l_sensorconfig.offset[0] = 0 - x*l_sensorconfig.xyz_axis[0][1]; + l_sensorconfig.offset[1] = 0 - y*l_sensorconfig.xyz_axis[1][1]; + l_sensorconfig.offset[2] = LSG - z*l_sensorconfig.xyz_axis[2][1]; + return 0; + +} + +static int dmard06_i2c_suspend(struct platform_device *pdev, pm_message_t state) +{ + dbg("...\n"); + cancel_delayed_work_sync(&l_sensorconfig.work); + + return 0; +} + +static int is_dmard06(void) +{ + int err = 0; + u8 cAddress = 0, cData = 0; + char buf[4]; + + cAddress = 0x53; + //i2c_master_send( client, (char*)&cAddress, 1); + //i2c_master_recv( client, (char*)&cData, 1); + if (dmard06_i2c_xyz_read_reg(&cAddress, &cData,1) <= 0) + { + errlog("Error to read SW_RESET register!\n"); + } + dbg("i2c Read 0x53 = %x \n", cData); + + cAddress = 0x0f; + //i2c_master_send( client, (char*)&cAddress, 1); + //i2c_master_recv( client, (char*)&cData, 1); + if (dmard06_i2c_xyz_read_reg(&cAddress, &cData,1) <= 0) + { + errlog("Can't find dmard06!\n"); + return -1; + } + dbg("i2c Read 0x0f = %d \n", cData); + + if(( cData&0x00FF) == 0x0006) + { + klog("Find DMARD06!\n"); + } + else + { + errlog("ID isn't 0x06.(0x%x) !\n",cData); + return -1; + } + + return 0; +} + +static int dmard06_i2c_remove(void) +{ + + return 0; +} + +static int dmard06_i2c_resume(struct platform_device *pdev) +{ + dbg("...\n"); + queue_delayed_work(l_sensorconfig.queue, &l_sensorconfig.work, msecs_to_jiffies(sample_rate_2_memsec(l_sensorconfig.sensor_samp))); + + + return 0; +} + +static int sensor_writeproc( struct file *file, + const char *buffer, + unsigned long count, + void *data ) +{ + + int inputval = -1; + int enable, sample = -1; + char tembuf[8]; + unsigned int amsr = 0; + int test = 0; + + mutex_lock(&sense_data_mutex); + memset(tembuf, 0, sizeof(tembuf)); + // get sensor level and set sensor level + if (sscanf(buffer, "isdbg=%d\n", &l_sensorconfig.isdbg)) + { + // only set the dbg flag + } else if (sscanf(buffer, "samp=%d\n", &sample)) + { + if (sample > 0) + { + if (sample != l_sensorconfig.sensor_samp) + { + } + //printk(KERN_ALERT "sensor samp=%d(amsr:%d) has been set.\n", sample, amsr); + } else { + printk(KERN_ALERT "Wrong sample argumnet of sensor.\n"); + } + } else if (sscanf(buffer, "enable=%d\n", &enable)) + { + if ((enable < 0) || (enable > 1)) + { + printk(KERN_ERR "The argument to enable/disable g-sensor should be 0 or 1 !!!\n"); + } else if (enable != l_sensorconfig.sensor_enable) + { + //mma_enable_disable(enable); + l_sensorconfig.sensor_enable = enable; + } + } else if (sscanf(buffer, "sensor_test=%d\n", &test)) + { // for test begin + l_sensorconfig.test_pass = 0; + } else if (sscanf(buffer, "sensor_testend=%d\n", &test)) + { // Don nothing only to be compatible the before testing program + } + mutex_unlock(&sense_data_mutex); + return count; +} + +static int sensor_readproc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len = 0; + + len = sprintf(page, + "test_pass=%d\nisdbg=%d\nrate=%d\nenable=%d\n", + l_sensorconfig.test_pass, + l_sensorconfig.isdbg, + l_sensorconfig.sensor_samp, + l_sensorconfig.sensor_enable + ); + return len; +} + + +extern int wmt_getsyspara(char *varname, unsigned char *varval, int *varlen); +static int get_axisset(void* param) +{ + char varbuf[64]; + int n; + int varlen; + + memset(varbuf, 0, sizeof(varbuf)); + varlen = sizeof(varbuf); + if (wmt_getsyspara("wmt.io.d06sensor", varbuf, &varlen)) { + printk(KERN_DEBUG "Can't get gsensor config in u-boot!!!!\n"); + return -1; + } else { + n = sscanf(varbuf, "%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d", + &l_sensorconfig.op, + &l_sensorconfig.int_gpio, + &l_sensorconfig.samp, + &(l_sensorconfig.xyz_axis[0][0]), + &(l_sensorconfig.xyz_axis[0][1]), + &(l_sensorconfig.xyz_axis[1][0]), + &(l_sensorconfig.xyz_axis[1][1]), + &(l_sensorconfig.xyz_axis[2][0]), + &(l_sensorconfig.xyz_axis[2][1]), + &l_sensorconfig.offset[0], + &l_sensorconfig.offset[1], + &l_sensorconfig.offset[2]); + if (n != 12) { + printk(KERN_ERR "gsensor format is error in u-boot!!!\n"); + return -1; + } + l_sensorconfig.sensor_samp = l_sensorconfig.samp; + + dbg("get the sensor config: %d:%d:%d:%d:%d:%d:%d:%d:%d\n", + l_sensorconfig.op, + l_sensorconfig.int_gpio, + l_sensorconfig.samp, + l_sensorconfig.xyz_axis[0][0], + l_sensorconfig.xyz_axis[0][1], + l_sensorconfig.xyz_axis[1][0], + l_sensorconfig.xyz_axis[1][1], + l_sensorconfig.xyz_axis[2][0], + l_sensorconfig.xyz_axis[2][1] + ); + } + return 0; +} + +// To contol the g-sensor for UI +static int mmad_open(struct inode *inode, struct file *file) +{ + dbg("Open the g-sensor node...\n"); + return 0; +} + +static int mmad_release(struct inode *inode, struct file *file) +{ + dbg("Close the g-sensor node...\n"); + return 0; +} + + +static int +mmad_ioctl(/*struct inode *inode,*/ struct file *file, unsigned int cmd, + unsigned long arg) +{ + void __user *argp = (void __user *)arg; + char rwbuf[5]; + short delay, enable, amsr = -1; + unsigned int sample; + int ret = 0; + int side; + char varbuff[80]; + unsigned int uval = 0; + + dbg("g-sensor ioctr...\n"); + memset(rwbuf, 0, sizeof(rwbuf)); + mutex_lock(&sense_data_mutex); + switch (cmd) { + case ECS_IOCTL_APP_SET_DELAY: + // set the rate of g-sensor + if (copy_from_user(&delay, argp, sizeof(short))) + { + printk(KERN_ALERT "Can't get set delay!!!\n"); + ret = -EFAULT; + goto errioctl; + } + klog("set delay=%d \n", delay); + //klog("before change sensor sample:%d...\n", l_sensorconfig.sensor_samp); + if ((delay >=0) && (delay < 20)) + { + delay = 20; + } else if (delay > 200) + { + delay = 200; + } + if (delay > 0) + { + l_sensorconfig.sensor_samp = 1000/delay; + } else { + errlog("error delay argument(delay=%d)!!!\n",delay); + ret = -EFAULT; + goto errioctl; + } + break; + case ECS_IOCTL_APP_SET_AFLAG: + // enable/disable sensor + if (copy_from_user(&enable, argp, sizeof(short))) + { + printk(KERN_ERR "Can't get enable flag!!!\n"); + ret = -EFAULT; + goto errioctl; + } + if ((enable >=0) && (enable <=1)) + { + dbg("driver: disable/enable(%d) gsensor.\n", enable); + + if (enable != l_sensorconfig.sensor_enable) + { + //mma_enable_disable(enable); + /*if (enable != 0) + { + queue_delayed_work(l_sensorconfig.queue, &l_sensorconfig.work, msecs_to_jiffies(sample_rate_2_memsec(l_sensorconfig.sensor_samp))); + } else { + cancel_delayed_work_sync(&l_sensorconfig.work); + flush_workqueue(l_sensorconfig.queue); + }*/ + l_sensorconfig.sensor_enable = enable; + + } + } else { + printk(KERN_ERR "Wrong enable argument in %s !!!\n", __FUNCTION__); + ret = -EFAULT; + goto errioctl; + } + break; + case WMT_IOCTL_SENSOR_GET_DRVID: + uval = DMARD06_DRVID; + if (copy_to_user((unsigned int*)arg, &uval, sizeof(unsigned int))) + { + ret = -EFAULT; + goto errioctl; + } + dbg("dmard06_driver_id:%d\n",uval); + break; + case WMT_IOCTL_SENSOR_CAL_OFFSET: + klog("-->WMT_IOCTL_SENSOR_CAL_OFFSET\n"); + if(copy_from_user(&side, (int*)argp, sizeof(int))) + { + ret = -EFAULT; + goto errioctl; + } + dbg("side=%d\n",side); + if (dmard06_CalOffset(side) != 0) + { + ret = -EFAULT; + goto errioctl; + } + // save the param + sprintf(varbuff, "%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d", + l_sensorconfig.op, + l_sensorconfig.int_gpio, + 10,//l_sensorconfig.samp, + (l_sensorconfig.xyz_axis[0][0]), + (l_sensorconfig.xyz_axis[0][1]), + (l_sensorconfig.xyz_axis[1][0]), + (l_sensorconfig.xyz_axis[1][1]), + (l_sensorconfig.xyz_axis[2][0]), + (l_sensorconfig.xyz_axis[2][1]), + l_sensorconfig.offset[0], + l_sensorconfig.offset[1], + l_sensorconfig.offset[2] + ); + wmt_setsyspara(GSENDMARD06_UBOOT_NAME, varbuff); + ret = 0; + break; + default: + break; + } + + + /*switch (cmd) { + case ECS_IOCTL_READ: + if (copy_to_user(argp, &rwbuf, sizeof(rwbuf))) + return -EFAULT; + break; + default: + break; + }*/ +errioctl: + mutex_unlock(&sense_data_mutex); + return ret; +} + + +static struct file_operations mmad_fops = { + .owner = THIS_MODULE, + .open = mmad_open, + .release = mmad_release, + .unlocked_ioctl = mmad_ioctl, +}; + + +static struct miscdevice mmad_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "sensor_ctrl", + .fops = &mmad_fops, +}; + +static int dmard06_probe( + struct platform_device *pdev) +{ + int err = 0; + + //register ctrl dev + err = misc_register(&mmad_device); + if (err != 0) + { + errlog("Can't register mma_device!\n"); + return -1; + } + // register rd/wr proc + l_sensorconfig.sensor_proc = create_proc_entry(GSENSOR_PROC_NAME, 0666, NULL/*&proc_root*/); + if (l_sensorconfig.sensor_proc != NULL) + { + l_sensorconfig.sensor_proc->write_proc = sensor_writeproc; + l_sensorconfig.sensor_proc->read_proc = sensor_readproc; + } + // init work queue + l_sensorconfig.queue = create_singlethread_workqueue("sensor-data-report"); + //INIT_WORK(&l_sensorconfig.work, mma_work_func); + INIT_DELAYED_WORK(&l_sensorconfig.work, dmard06_work_func); + mutex_init(&sense_data_mutex); + l_sensorconfig.input_dev = input_allocate_device(); + if (!l_sensorconfig.input_dev) { + err = -ENOMEM; + errlog("Failed to allocate input device\n"); + goto exit_input_dev_alloc_failed; + } + //set_bit(EV_KEY, l_sensorconfig.input_dev->evbit); + //set_bit(EV_ABS, l_sensorconfig.input_dev->evbit); + l_sensorconfig.input_dev->evbit[0] = BIT(EV_ABS) | BIT_MASK(EV_KEY); + //set_bit(KEY_NEXTSONG, l_sensorconfig.input_dev->keybit); + + /* yaw */ + //input_set_abs_params(l_sensorconfig.input_dev, ABS_RX, 0, 360*100, 0, 0); + /* pitch */ + //input_set_abs_params(l_sensorconfig.input_dev, ABS_RY, -180*100, 180*100, 0, 0); + /* roll */ + //input_set_abs_params(l_sensorconfig.input_dev, ABS_RZ, -90*100, 90*100, 0, 0); + /* x-axis acceleration */ + input_set_abs_params(l_sensorconfig.input_dev, ABS_X, -128, 128, 0, 0); + /* y-axis acceleration */ + input_set_abs_params(l_sensorconfig.input_dev, ABS_Y, -128, 128, 0, 0); + /* z-axis acceleration */ + input_set_abs_params(l_sensorconfig.input_dev, ABS_Z, -128, 128, 0, 0); + + l_sensorconfig.input_dev->name = "g-sensor"; + + err = input_register_device(l_sensorconfig.input_dev); + + if (err) { + errlog("Unable to register input device: %s\n", + l_sensorconfig.input_dev->name); + goto exit_input_register_device_failed; + } + + return 0; +exit_input_register_device_failed: + input_free_device(l_sensorconfig.input_dev); +exit_input_dev_alloc_failed: + // release proc + remove_proc_entry(GSENSOR_PROC_NAME, NULL); + l_sensorconfig.sensor_proc = NULL; + // unregister the ctrl dev + misc_deregister(&mmad_device); + return err; +} + +static int dmard06_remove(struct platform_device *pdev) +{ + if (NULL != l_sensorconfig.queue) + { + cancel_delayed_work_sync(&l_sensorconfig.work); + flush_workqueue(l_sensorconfig.queue); + destroy_workqueue(l_sensorconfig.queue); + l_sensorconfig.queue = NULL; + } + if (l_sensorconfig.sensor_proc != NULL) + { + remove_proc_entry(GSENSOR_PROC_NAME, NULL); + l_sensorconfig.sensor_proc = NULL; + } + misc_deregister(&mmad_device); + return 0; +} +#if 0 +static void dmard06_early_suspend(struct early_suspend *h) +{ + dbg("start\n"); + cancel_delayed_work_sync(&l_sensorconfig.work); + dbg("exit\n"); +} + +static void dmard06_late_resume(struct early_suspend *h) +{ + dbg("start\n"); + // init + queue_delayed_work(l_sensorconfig.queue, &l_sensorconfig.work, msecs_to_jiffies(sample_rate_2_memsec(l_sensorconfig.sensor_samp))); + dbg("exit\n"); +} +#endif + +static int __init dmard06_init(void) +{ + int ret = 0; + + // detech the device + if (is_dmard06() != 0) + { + return -1; + } + // parse g-sensor u-boot arg + ret = get_axisset(NULL); + if (ret < 0) + { + printk("<<<<<%s user choose to no sensor chip!\n", __func__); + return ret; + } + /*if ((ret != 0) || !l_sensorconfig.op) + return -EINVAL; + */ + + // Create device node + if (register_chrdev (GSENSOR_MAJOR, GSENSOR_NAME, &dmard06_fops)) { + printk (KERN_ERR "unable to get major %d\n", GSENSOR_MAJOR); + return -EIO; + } + + l_dev_class = class_create(THIS_MODULE, GSENSOR_NAME); + if (IS_ERR(l_dev_class)){ + ret = PTR_ERR(l_dev_class); + printk(KERN_ERR "Can't class_create gsensor device !!\n"); + return ret; + } + l_clsdevice = device_create(l_dev_class, NULL, MKDEV(GSENSOR_MAJOR, 0), NULL, GSENSOR_NAME); + if (IS_ERR(l_clsdevice)){ + ret = PTR_ERR(l_clsdevice); + printk(KERN_ERR "Failed to create device %s !!!",GSENSOR_NAME); + return ret; + } + INIT_WORK(&poll_work, dmard06_work_func); + + + if((ret = platform_device_register(&dmard06_device))) + { + printk(KERN_ERR "%s Can't register mma7660 platform devcie!!!\n", __FUNCTION__); + return ret; + } + if ((ret = platform_driver_register(&dmard06_driver)) != 0) + { + printk(KERN_ERR "%s Can't register mma7660 platform driver!!!\n", __FUNCTION__); + return ret; + } +#ifdef CONFIG_HAS_EARLYSUSPEND + l_sensorconfig.earlysuspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; + l_sensorconfig.earlysuspend.suspend = dmard06_early_suspend; + l_sensorconfig.earlysuspend.resume = dmard06_late_resume; + register_early_suspend(&l_sensorconfig.earlysuspend); +#endif + + klog("dmard06 g-sensor driver load!\n"); + queue_delayed_work(l_sensorconfig.queue, &l_sensorconfig.work, msecs_to_jiffies(sample_rate_2_memsec(l_sensorconfig.sensor_samp))); + + return 0; +} + +static void __exit dmard06_exit(void) +{ + //unregister_early_suspend(&l_sensorconfig.earlysuspend); + platform_driver_unregister(&dmard06_driver); + platform_device_unregister(&dmard06_device); + device_destroy(l_dev_class, MKDEV(GSENSOR_MAJOR, 0)); + unregister_chrdev(GSENSOR_MAJOR, GSENSOR_NAME); + class_destroy(l_dev_class); + +} + +MODULE_AUTHOR("DMT_RD"); +MODULE_DESCRIPTION("DMARD06 g-sensor Driver"); +MODULE_LICENSE("GPL"); + +module_init(dmard06_init); +module_exit(dmard06_exit); diff --git a/drivers/input/sensor/dmard08_gsensor/Makefile b/drivers/input/sensor/dmard08_gsensor/Makefile new file mode 100755 index 00000000..82f27563 --- /dev/null +++ b/drivers/input/sensor/dmard08_gsensor/Makefile @@ -0,0 +1,34 @@ +KERNELDIR=../../../../ +#KERNELDIR=/home/hangyan/android8850/kernel/ANDROID_3.0.8 +CROSS = arm_1103_le- +CC= $(CROSS)gcc +LD= $(CROSS)ld +STRIP = $(CROSS)strip + +DEBUG = n + +# Add your debugging flag (or not) to EXTRA_CFLAGS +ifeq ($(DEBUG),y) +# DEBFLAGS = -O -g -DSCULL_DEBUG # "-O" is needed to expand inlines +DEBFLAGS = -O0 -g -DSCULL_DEBUG # "-O" is needed to expand inlines + +else + DEBFLAGS = -O2 -Wall +endif + +EXTRA_CFLAGS += $(DEBFLAGS) + + +MY_MODULE_NAME=s_wmt_gsensor_dmard08 + +obj-m := $(MY_MODULE_NAME).o +$(MY_MODULE_NAME)-objs := dmard08.o cyclequeue.o + +default: + $(MAKE) -C $(KERNELDIR) SUBDIRS=$(PWD) modules + $(STRIP) --strip-debug $(MY_MODULE_NAME).ko + rm -rf *.o *~ core .depend .*.cmd *.mod.c .tmp_versions + +clean: + rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions *.order *.symvers + diff --git a/drivers/input/sensor/dmard08_gsensor/cyclequeue.c b/drivers/input/sensor/dmard08_gsensor/cyclequeue.c new file mode 100755 index 00000000..4d6b97cd --- /dev/null +++ b/drivers/input/sensor/dmard08_gsensor/cyclequeue.c @@ -0,0 +1,68 @@ +#include + + +#include "cyclequeue.h" + +static struct que_data que[QUEUE_LEN]; +static unsigned int head = -1; // point to the first valaid queue data +static unsigned int tail = 0; // point to the next to the last valaid queue data +static DEFINE_MUTEX(que_mutex); + +// Whether queue is full +// return 1--full,0 -- no full +int clque_is_full(void) +{ + int ret = 0; + + mutex_lock(&que_mutex); + ret = ((tail+1)%QUEUE_LEN) == head ? 1 : 0; + mutex_unlock(&que_mutex); + return ret; +} + +// Whether queue is empty +// return 1--empty,0--no empty +int clque_is_empty(void) +{ + int ret = 0; + + mutex_lock(&que_mutex); + ret = (tail == head) ? 1: 0; + mutex_unlock(&que_mutex); + return ret; +} + +// add to queue +// return:0--successful,-1--queue is full +int clque_in(struct que_data* data) +{ + /*if (clque_is_full()) + { + return -1; + }*/ + mutex_lock(&que_mutex); + que[tail].data[0] = data->data[0]; + que[tail].data[1] = data->data[1]; + que[tail].data[2] = data->data[2]; + tail = (tail+1)%QUEUE_LEN; + mutex_unlock(&que_mutex); + return 0; +} + +// out to queue +// return:0--successful,-1--queue is empty +int clque_out(struct que_data* data) +{ + /*if (clque_is_empty()) + { + return -1; + }*/ + mutex_lock(&que_mutex); + data->data[0]= que[head].data[0]; + data->data[1]= que[head].data[1]; + data->data[2]= que[head].data[2]; + head = (head+1)%QUEUE_LEN; + mutex_unlock(&que_mutex); + return 0; +} + diff --git a/drivers/input/sensor/dmard08_gsensor/cyclequeue.h b/drivers/input/sensor/dmard08_gsensor/cyclequeue.h new file mode 100755 index 00000000..52b9996f --- /dev/null +++ b/drivers/input/sensor/dmard08_gsensor/cyclequeue.h @@ -0,0 +1,18 @@ +#ifndef __CYCLEQUEUE_163704111637_H +#define __CYCLEQUEUE_163704111637_H + +#define DATA_TYPE short +#define QUEUE_LEN 16 + +struct que_data { + DATA_TYPE data[3]; +}; + +extern int clque_in(struct que_data* data); +extern int clque_out(struct que_data* data); +extern int clque_is_full(void); +extern int clque_is_empty(void); +#endif + + + diff --git a/drivers/input/sensor/dmard08_gsensor/dmard08.c b/drivers/input/sensor/dmard08_gsensor/dmard08.c new file mode 100755 index 00000000..3cbe2ac7 --- /dev/null +++ b/drivers/input/sensor/dmard08_gsensor/dmard08.c @@ -0,0 +1,1019 @@ +/* + * @file drivers/i2c/dmard08.c + * @brief DMARD08 g-sensor Linux device driver + * @author Domintech Technology Co., Ltd (http://www.domintech.com.tw) + * @version 1.22 + * @date 2011/12/01 + * + * @section LICENSE + * + * Copyright 2011 Domintech Technology Co., Ltd + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +//#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// ****Add by Steve Huang*********2011-11-18******** +#include +#include "../sensor.h" +//#include "cyclequeue.h" +// ************************************************ + +#define GSENSOR_I2C_NAME "dmard08" +#define GSENSOR_I2C_ADDR 0x1c + +#define SENSOR_DATA_SIZE 3 + +static struct i2c_client *this_client = NULL; +static struct mutex sense_data_mutex; +static struct class* l_dev_class = NULL; + +static struct wmt_gsensor_data l_sensorconfig = { + .op = 0, + .int_gpio = 3, + .samp = 5, + .xyz_axis = { + {ABS_X, -1}, + {ABS_Y, 1}, + {ABS_Z, -1}, + }, + .sensor_proc = NULL, + .isdbg = 0, + .sensor_samp = 10, // 1 sample/second + .sensor_enable = 1, // enable sensor + .test_pass = 0, // for test program + .offset={0,0,0}, +}; + + +// ****Add by Steve Huang*********2011-11-18******** +/*void gsensor_write_offset_to_file(void); +void gsensor_read_offset_from_file(void); +char OffsetFileName[] = "/data/misc/dmt/offset.txt";*/ +//************************************************** + + + +extern int wmt_getsyspara(char *varname, unsigned char *varval, int *varlen); + +/*static int dmard08_i2c_suspend(struct i2c_client *client, pm_message_t mesg); +static int dmard08_i2c_resume(struct i2c_client *client);*/ +//static int __devinit dmard08_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id); +//static int __devexit dmard08_i2c_remove(struct i2c_client *client); +void dmard08_i2c_read_xyz(struct i2c_client *client, s16 *xyz); +static inline void dmard08_i2c_correct_accel_sign(s16 *val); //check output is correct +void dmard08_i2c_merge_register_values(struct i2c_client *client, s16 *val, u8 msb, u8 lsb); //merge the register values + +struct raw_data { + short x; + short y; + short z; +}; + +struct raw_data rdata; +//static struct raw_data offset; + +struct dev_data +{ + dev_t devno; + struct cdev cdev; + struct class *class; + struct i2c_client *client; +}; +//static struct dev_data dev; + + +unsigned int sample_rate_2_memsec(unsigned int rate) +{ + return (1000/rate); +} + + +/*void gsensor_read_accel_avg(int num_avg, raw_data *avg_p) // marked by eason check again!! +{ + long xyz_acc[SENSOR_DATA_SIZE]; + s16 xyz[SENSOR_DATA_SIZE]; + int i, j; + + //initialize the accumulation buffer + for(i = 0; i < SENSOR_DATA_SIZE; ++i) + xyz_acc[i] = 0; + + for(i = 0; i < num_avg; i++) + { + device_i2c_read_xyz(l_sensorconfig.client, (s16 *)&xyz); + for(j = 0; j < SENSOR_DATA_SIZE; j++) + xyz_acc[j] += xyz[j]; + } + + // calculate averages + for(i = 0; i < SENSOR_DATA_SIZE; i++) + avg_p->v[i] = (s16) (xyz_acc[i] / num_avg); +}*/ + +/*void gsensor_calibrate(int side) //marked by eason check again +{ + raw_data avg; + int avg_num = 16; + + //IN_FUNC_MSG; + // get acceleration average reading + gsensor_read_accel_avg(avg_num, &avg); + // calculate and set the offset + gsensor_calculate_offset(side, avg); +}*/ + +/*void ce_on(void) //marked by eason check again +{ + int gppdat; + gppdat = __raw_readl(S3C64XX_GPPDAT); + gppdat |= (1 << 0); + + __raw_writel(gppdat,S3C64XX_GPPDAT); +} + +void ce_off(void) +{ + int gppdat; + gppdat = __raw_readl(S3C64XX_GPPDAT); + gppdat &= ~(1 << 0); + + __raw_writel(gppdat,S3C64XX_GPPDAT); +} + +void config_ce_pin(void) +{ + unsigned int value; + //D08's CE (pin#12) is connected to S3C64XX AP processor's port P0 + //Below codes set port P0 as digital output + value = readl(S3C64XX_GPPCON); + value &= ~ (0x3); + value |= 1 ; //Output =01 , Input = 00 , Ext. Interrupt = 10 + writel(value, S3C64XX_GPPCON); //save S3C64XX_GPPCON change +} + +void gsensor_reset(void) +{ + ce_off(); + msleep(300); + ce_on(); +}*/ + +/*void gsensor_set_offset(int val[3]) //marked by eason check again +{ + int i; + IN_FUNC_MSG; + for(i = 0; i < SENSOR_DATA_SIZE; ++i) + offset.v[i] = (s16) val[i]; +}*/ + +/* +static const struct i2c_device_id dmard08_i2c_ids[] = +{ + {GSENSOR_I2C_NAME, 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, dmard08_i2c_ids); + + +static struct i2c_driver dmard08_i2c_driver = +{ + .driver = { + .owner = THIS_MODULE, + .name = GSENSOR_I2C_NAME, + }, + .class = I2C_CLASS_HWMON, + .probe = dmard08_i2c_probe, + .remove = __devexit_p(dmard08_i2c_remove), + //.suspend = dmard08_i2c_suspend, + //.resume = dmard08_i2c_resume, + .id_table = dmard08_i2c_ids, +}; +*/ + +static int dmard08_i2c_xyz_read_reg(struct i2c_client *client,u8 *buffer, int length) //OK +{ + + struct i2c_msg msg[] = + { + {.addr = client->addr, .flags = 0, .len = 1, .buf = buffer,}, + {.addr = client->addr, .flags = I2C_M_RD, .len = length, .buf = buffer,}, + }; + return i2c_transfer(client->adapter, msg, 2); +} + +static int dmard08_i2c_xyz_write_reg(struct i2c_client *client,u8 *buffer, int length) //write reg OK +{ + struct i2c_msg msg[] = + { + {.addr = client->addr, .flags = 0, .len = length, .buf = buffer,}, + }; + return i2c_transfer(client->adapter, msg, 1); +} + +//static void dmard08_i2c_read_xyz(struct i2c_client, s16 *x, s16 *y, s16 *z) //add by eason +void dmard08_i2c_read_xyz(struct i2c_client *client, s16 *xyz_p) +{ +// s16 xTmp,yTmp,zTmp; //added by eason + s16 xyzTmp[SENSOR_DATA_SIZE]; + int i; +/*get xyz high/low bytes, 0x02~0x07*/ + u8 buffer[6]; + buffer[0] = 0x2; + mutex_lock(&sense_data_mutex); + dmard08_i2c_xyz_read_reg(client, buffer, 6); + mutex_unlock(&sense_data_mutex); + + //merge to 11-bits value + for(i = 0; i < SENSOR_DATA_SIZE; ++i){ + dmard08_i2c_merge_register_values(client, (xyzTmp + i), buffer[2*i], buffer[2*i + 1]); + } + //transfer to the default layout + for(i = 0; i < SENSOR_DATA_SIZE; ++i) + { + xyz_p[i] = xyzTmp[i]; // add by eason +/* xyz_p[i] = 0; + for(j = 0; j < 3; j++) + xyz_p[i] += sensorlayout[i][j] * xyzTmp[j]; */ + } + dbg("%x,%x,%x,",xyz_p[0], xyz_p[1], xyz_p[2]); + //printk("@DMT@ dmard08_i2c_read_xyz: X-axis: %d ,Y-axis: %d ,Z-axis: %d\n", xyz_p[0], xyz_p[1], xyz_p[2]); +} + +void dmard08_i2c_merge_register_values(struct i2c_client *client, s16 *val, u8 msb, u8 lsb) +{ + + *val = (((u16)msb) << 3) | (u16)lsb; + dmard08_i2c_correct_accel_sign(val); +} + +static inline void dmard08_i2c_correct_accel_sign(s16 *val) +{ + + *val<<= (sizeof(s16) * BITS_PER_BYTE - 11); + *val>>= (sizeof(s16) * BITS_PER_BYTE - 11); +} + +/* +static int dmard08_i2c_suspend(struct i2c_client *client, pm_message_t mesg) +{ + dbg("...\n"); + return 0; +} +*/ + +//static int __devinit dmard08_i2c_probe(struct i2c_client *client,const struct i2c_device_id *id) +static int __devinit dmard08_hw_init(struct i2c_client *client/*,const struct i2c_device_id *id*/) +{ + char cAddress = 0 , cData = 0; + u8 buffer[2]; + + //for(i = 0; i < SENSOR_DATA_SIZE; ++i) //marked by eason check again + // offset.v[i] = 0; + + + if(!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + { + dbg("I2C_FUNC_I2C not support\n"); + return -1; + } + //config_ce_pin(); //how used? + //gsensor_reset(); //how used? + /* check SW RESET */ + cAddress = 0x08; + i2c_master_send( client, (char*)&cAddress, 1); + i2c_master_recv( client, (char*)&cData, 1); + dbg( "i2c Read 0x08 = %d \n", cData); + if( cData == 0x00) + { + cAddress = 0x09; + i2c_master_send( client, (char*)&cAddress, 1); + i2c_master_recv( client, (char*)&cData, 1); + dbg( "i2c Read 0x09 = %d \n", cData); + if( cData == 0x00) + { + cAddress = 0x0a; + i2c_master_send( client, (char*)&cAddress, 1); + i2c_master_recv( client, (char*)&cData, 1); + dbg( "i2c Read 0x0a = %d \n", cData); + if( cData == 0x88) + { + cAddress = 0x0b; + i2c_master_send( client, (char*)&cAddress, 1); + i2c_master_recv( client, (char*)&cData, 1); + dbg( "i2c Read 0x0b = %d \n", cData); + if( cData == 0x08) + { + dbg( "DMT_DEVICE_NAME registered I2C driver!\n"); + l_sensorconfig.client = client; + } + else + { + dbg( "err : i2c Read 0x0B = %d!\n",cData); + l_sensorconfig.client = NULL; + return -1; + } + } + else + { + dbg( "err : i2c Read 0x0A = %d!\n",cData); + l_sensorconfig.client = NULL; + return -1; + } + } + else + { + dbg( "err : i2c Read 0x09 = %d!\n",cData); + l_sensorconfig.client = NULL; + return -1; + } + } + else + { + dbg( "err : i2c Read 0x08 = %d!\n",cData); + l_sensorconfig.client = NULL; + + return -1; + } + + /* set sampling period if samp = 1, set the sampling frequency = 684 + otherwise set the sample frequency = 342 (default) added by eason 2012/3/7*/ + if (l_sensorconfig.samp == 1) { + buffer[0] = 0x08; + buffer[1] = 0x04; + dmard08_i2c_xyz_write_reg(client, buffer, 2); + } + + /*check sensorlayout[i][j] //eason + for(i = 0; i < 3; ++i) + { + for(j = 0; j < 3; j++) + printk("%d",sensorlayout[i][j]); + printk("\n"); + } */ + + return 0; +} + +static int __devexit dmard08_i2c_remove(struct i2c_client *client) //OK +{ + dbg("...\n"); + + return 0; +} + +/* +static int dmard08_i2c_resume(struct i2c_client *client) //OK +{ + dbg("...\n"); + + return 0; +} +*/ +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.dm08sensor", varbuf, &varlen)) { + errlog("Can't get gsensor config in u-boot!!!!\n"); + return -1; + } else { + n = sscanf(varbuf, "%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d", + &l_sensorconfig.op, + &l_sensorconfig.int_gpio, + &l_sensorconfig.samp, + &(l_sensorconfig.xyz_axis[0][0]), + &(l_sensorconfig.xyz_axis[0][1]), + &(l_sensorconfig.xyz_axis[1][0]), + &(l_sensorconfig.xyz_axis[1][1]), + &(l_sensorconfig.xyz_axis[2][0]), + &(l_sensorconfig.xyz_axis[2][1]), + &(l_sensorconfig.offset[0]), + &(l_sensorconfig.offset[1]), + &(l_sensorconfig.offset[2]) + ); + if (n != 12) { + errlog("gsensor format is error in u-boot!!!\n"); + return -1; + } + l_sensorconfig.sensor_samp = l_sensorconfig.samp; + + dbg("get the sensor config: %d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d\n", + l_sensorconfig.op, + l_sensorconfig.int_gpio, + l_sensorconfig.samp, + l_sensorconfig.xyz_axis[0][0], + l_sensorconfig.xyz_axis[0][1], + l_sensorconfig.xyz_axis[1][0], + l_sensorconfig.xyz_axis[1][1], + l_sensorconfig.xyz_axis[2][0], + l_sensorconfig.xyz_axis[2][1], + l_sensorconfig.offset[0], + l_sensorconfig.offset[1], + l_sensorconfig.offset[2] + ); + } + return 0; +} + +static void dmard08_platform_release(struct device *device) +{ + dbg("...\n"); + return; +} + + +static struct platform_device dmard08_device = { + .name = GSENSOR_I2C_NAME, + .id = 0, + .dev = { + .release = dmard08_platform_release, + }, +}; + +static int dmard08_suspend(struct platform_device *pdev, pm_message_t state) +{ + dbg("...\n"); + cancel_delayed_work_sync(&l_sensorconfig.work); + + return 0; +} + + +static int dmard08_open(struct inode *node, struct file *fle) +{ + dbg("open...\n"); + return 0; +} + +/* release command for dmard08 device file */ +static int dmard08_close(struct inode *node, struct file *fle) +{ + dbg("close...\n"); + return 0; +} + +/* ioctl command for dmard08 device file */ +static long dmard08_ioctl(/*struct inode *inode,*/ struct file *file, unsigned int cmd, unsigned long arg) +{ + int err = 0; + //unsigned char data[6]; + short delay = 0; + short enable = 0; + unsigned int uval = 0; + + if (WMT_IOCTL_SENSOR_CAL_OFFSET == cmd) + { + return 0;// now do nothing + } + + /* cmd mapping */ + mutex_lock(&sense_data_mutex); + switch(cmd) + { + + case ECS_IOCTL_APP_SET_DELAY: + // set the rate of g-sensor + dbg("ECS_IOCTL_APP_SET_DELAY\n"); + if (copy_from_user(&delay,(short*)arg, sizeof(short))) + { + errlog("Can't get set delay!!!\n"); + err = -EFAULT; + goto errioctl; + } + klog("set delay=%d \n", delay); + //klog("before change sensor sample:%d...\n", l_sensorconfig.sensor_samp); + if ((delay >=0) && (delay < 20)) + { + delay = 20; + } else if (delay > 200) + { + delay = 200; + } + if (delay > 0) + { + l_sensorconfig.sensor_samp = 1000/delay; + } else { + errlog("error delay argument(delay=%d)!!!\n",delay); + err = -EFAULT; + goto errioctl; + } + break; + case ECS_IOCTL_APP_SET_AFLAG: + dbg("ECS_IOCTL_APP_SET_AFLAG\n"); + // enable/disable sensor + if (copy_from_user(&enable, (short*)arg, sizeof(short))) + { + errlog("Can't get enable flag!!!\n"); + err = -EFAULT; + goto errioctl; + } + if ((enable >=0) && (enable <=1)) + { + dbg("driver: disable/enable(%d) gsensor.\n", enable); + + if (enable != l_sensorconfig.sensor_enable) + { + // do sth ??? + //mma_enable_disable(enable); + /*if (enable != 0) + { + queue_delayed_work(l_sensorconfig.queue, &l_sensorconfig.work, msecs_to_jiffies(sample_rate_2_memsec(l_sensorconfig.sensor_samp))); + } else { + cancel_delayed_work_sync(&l_sensorconfig.work); + flush_workqueue(l_sensorconfig.queue); + }*/ + l_sensorconfig.sensor_enable = enable; + + } + } else { + errlog("Wrong enable argument!!!\n"); + err = -EFAULT; + goto errioctl; + } + break; + case WMT_IOCTL_SENSOR_GET_DRVID: + uval = DMARD08_DRVID; + if (copy_to_user((unsigned int*)arg, &uval, sizeof(unsigned int))) + { + return -EFAULT; + } + dbg("dmard08_driver_id:%d\n",uval); + break; + default: + break; + } +errioctl: + mutex_unlock(&sense_data_mutex); + return err; +} + +/* +static ssize_t dmard08_read(struct file *file, char __user *buf, size_t count, loff_t *offset) +{ + struct que_data data; + short xyz_temp[3]; + + // read data from cycle queue + while (clque_is_empty()) msleep(10); + clque_out(&data); + xyz_temp[0] = data.data[0]; + xyz_temp[1] = data.data[1]; + xyz_temp[2] = data.data[2]; + + + if(copy_to_user(buf, &xyz_temp, sizeof(xyz_temp))) + return -EFAULT; + dbg("x=%x,y=%x,z=%x\n",xyz_temp[0], xyz_temp[1], xyz_temp[2]); + return sizeof(xyz_temp); +} +*/ +/* +static ssize_t dmard08_write(struct file *file, const char __user *buf, size_t count, loff_t *offset) +{ + dbg("write...\n"); + return 0; +} +*/ + +static const struct file_operations d08_fops = { + .owner = THIS_MODULE, + .open = dmard08_open, + .release = dmard08_close, + //.read = dmard08_read, + //.wirte = dmard08_write, + .unlocked_ioctl = dmard08_ioctl, +}; + +static struct miscdevice d08_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = GSENSOR_DEV_NODE, + .fops = &d08_fops, +}; + + +static int dmard08_resume(struct platform_device *pdev) +{ + char buffer[2]; + dbg("...\n"); + + if (l_sensorconfig.samp == 1) { + buffer[0] = 0x08; + buffer[1] = 0x04; + dmard08_i2c_xyz_write_reg(l_sensorconfig.client, buffer, 2); + } + + queue_delayed_work(l_sensorconfig.queue, &l_sensorconfig.work, msecs_to_jiffies(sample_rate_2_memsec(l_sensorconfig.sensor_samp))); + return 0; +} + +static int sensor_writeproc( struct file *file, + const char *buffer, + unsigned long count, + void *data ) +{ + + //int inputval = -1; + int enable, sample = -1; + char tembuf[8]; + //unsigned int amsr = 0; + int test = 0; + + mutex_lock(&sense_data_mutex); + memset(tembuf, 0, sizeof(tembuf)); + // get sensor level and set sensor level + if (sscanf(buffer, "isdbg=%d\n", &l_sensorconfig.isdbg)) + { + // only set the dbg flag + } else if (sscanf(buffer, "samp=%d\n", &sample)) + { + if (sample > 0) + { + if (sample != l_sensorconfig.sensor_samp) + { + // should do sth + } + //printk(KERN_ALERT "sensor samp=%d(amsr:%d) has been set.\n", sample, amsr); + } else { + klog("Wrong sample argumnet of sensor.\n"); + } + } else if (sscanf(buffer, "enable=%d\n", &enable)) + { + if ((enable < 0) || (enable > 1)) + { + klog("The argument to enable/disable g-sensor should be 0 or 1 !!!\n"); + } else if (enable != l_sensorconfig.sensor_enable) + { + //mma_enable_disable(enable); + l_sensorconfig.sensor_enable = enable; + } + } else if (sscanf(buffer, "sensor_test=%d\n", &test)) + { // for test begin + l_sensorconfig.test_pass = 0; + } else if (sscanf(buffer, "sensor_testend=%d\n", &test)) + { // Don nothing only to be compatible the before testing program + } + mutex_unlock(&sense_data_mutex); + return count; +} + +static int sensor_readproc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len = 0; + + len = sprintf(page, + "test_pass=%d\nisdbg=%d\nrate=%d\nenable=%d\n", + l_sensorconfig.test_pass, + l_sensorconfig.isdbg, + l_sensorconfig.sensor_samp, + l_sensorconfig.sensor_enable + ); + return len; +} + +static void read_work_func(struct work_struct *work) +{ + s16 xyz[SENSOR_DATA_SIZE]; + s16 txyz[SENSOR_DATA_SIZE]; + + if (! l_sensorconfig.sensor_enable) + { + // no report data + queue_delayed_work(l_sensorconfig.queue, &l_sensorconfig.work, msecs_to_jiffies(sample_rate_2_memsec(l_sensorconfig.sensor_samp))); + return; + } + // read data to one cycle que + //dbg("read...\n"); + dmard08_i2c_read_xyz(l_sensorconfig.client, (s16 *)xyz); + + // x + txyz[0] = xyz[l_sensorconfig.xyz_axis[0][0]]*l_sensorconfig.xyz_axis[0][1]+l_sensorconfig.offset[0]; + // y + txyz[1] = xyz[l_sensorconfig.xyz_axis[1][0]]*l_sensorconfig.xyz_axis[1][1]+l_sensorconfig.offset[1]; + // z + txyz[2] = xyz[l_sensorconfig.xyz_axis[2][0]]*l_sensorconfig.xyz_axis[2][1]+l_sensorconfig.offset[2]; + + input_report_abs(l_sensorconfig.input_dev, ABS_X, txyz[0]); + input_report_abs(l_sensorconfig.input_dev, ABS_Y, txyz[1]); + input_report_abs(l_sensorconfig.input_dev, ABS_Z, txyz[2]); + input_sync(l_sensorconfig.input_dev); + l_sensorconfig.test_pass = 1; // for testing + // read next + queue_delayed_work(l_sensorconfig.queue, &l_sensorconfig.work, msecs_to_jiffies(sample_rate_2_memsec(l_sensorconfig.sensor_samp))); +} + + +static int dmard08_probe(struct platform_device *pdev) +{ + int err = 0; + + //register ctrl dev + err = misc_register(&d08_device); + if (err !=0) { + errlog("Can't register d08_device!\n"); + return -1; + } + // register rd/wr proc + l_sensorconfig.sensor_proc = create_proc_entry(GSENSOR_PROC_NAME, 0666, NULL/*&proc_root*/); + if (l_sensorconfig.sensor_proc != NULL) + { + l_sensorconfig.sensor_proc->write_proc = sensor_writeproc; + l_sensorconfig.sensor_proc->read_proc = sensor_readproc; + } + // init work queue + l_sensorconfig.queue = create_singlethread_workqueue("sensor-report"); + INIT_DELAYED_WORK(&l_sensorconfig.work, read_work_func); + mutex_init(&sense_data_mutex); + // init input device + l_sensorconfig.input_dev = input_allocate_device(); + if (!l_sensorconfig.input_dev) { + err = -ENOMEM; + errlog("Failed to allocate input device\n"); + goto exit_input_dev_alloc_failed; + } + l_sensorconfig.input_dev->evbit[0] = BIT(EV_ABS) | BIT_MASK(EV_KEY); + /* x-axis acceleration */ + input_set_abs_params(l_sensorconfig.input_dev, ABS_X, -1024, 1024, 0, 0); + /* y-axis acceleration */ + input_set_abs_params(l_sensorconfig.input_dev, ABS_Y, -1024, 1024, 0, 0); + /* z-axis acceleration */ + input_set_abs_params(l_sensorconfig.input_dev, ABS_Z, -1024, 1024, 0, 0); + + l_sensorconfig.input_dev->name = GSENSOR_INPUT_NAME; + + err = input_register_device(l_sensorconfig.input_dev); + + if (err) { + errlog("Unable to register input device: %s\n", + l_sensorconfig.input_dev->name); + goto exit_input_register_device_failed; + } + + return 0; +exit_input_register_device_failed: + // free inut + input_free_device(l_sensorconfig.input_dev); +exit_input_dev_alloc_failed: + // free queue + destroy_workqueue(l_sensorconfig.queue); + l_sensorconfig.queue = NULL; + // free proc + if (l_sensorconfig.sensor_proc != NULL) + { + remove_proc_entry(GSENSOR_PROC_NAME, NULL); + l_sensorconfig.sensor_proc = NULL; + } + // free work + // unregister ctrl dev + misc_deregister(&d08_device); + return err; +} + +static int dmard08_remove(struct platform_device *pdev) +{ + if (NULL != l_sensorconfig.queue) + { + cancel_delayed_work_sync(&l_sensorconfig.work); + flush_workqueue(l_sensorconfig.queue); + destroy_workqueue(l_sensorconfig.queue); + l_sensorconfig.queue = NULL; + } + if (l_sensorconfig.sensor_proc != NULL) + { + remove_proc_entry(GSENSOR_PROC_NAME, NULL); + l_sensorconfig.sensor_proc = NULL; + } + misc_deregister(&d08_device); + input_unregister_device(l_sensorconfig.input_dev); + return 0; +} + + +static struct platform_driver dmard08_driver = { + .probe = dmard08_probe, + .remove = dmard08_remove, + .suspend = dmard08_suspend, + .resume = dmard08_resume, + .driver = { + .name = GSENSOR_I2C_NAME, + }, +}; + +#if 0 +static void dmard08_early_suspend(struct early_suspend *h) +{ + dbg("start\n"); + cancel_delayed_work_sync(&l_sensorconfig.work); + dbg("exit\n"); +} + +static void dmard08_late_resume(struct early_suspend *h) +{ + dbg("start\n"); + // init + queue_delayed_work(l_sensorconfig.queue, &l_sensorconfig.work, msecs_to_jiffies(sample_rate_2_memsec(l_sensorconfig.sensor_samp))); + dbg("exit\n"); +} +#endif + +static int __init dmard08_init(void) //OK +{ + int ret = 0; + + // parse g-sensor u-boot arg + ret = get_axisset(); + if (ret < 0) + { + printk("<<<<<%s user choose to no sensor chip!\n", __func__); + return ret; + } + /*if ((ret != 0) || !l_sensorconfig.op) + { + dbg("Can't load gsensor dmar08 driver for error u-boot arg!\n"); + return -EINVAL; + }*/ + if (!(this_client = sensor_i2c_register_device(0, GSENSOR_I2C_ADDR, GSENSOR_I2C_NAME))) + { + printk(KERN_EMERG"Can't register gsensor i2c device!\n"); + return -1; + } + // find the device + /*if(i2c_add_driver(&dmard08_i2c_driver) != 0) + { + ret = -1; + dbg("Can't find gsensor dmard08!\n"); + goto err_i2c_add_driver; + }*/ + if(dmard08_hw_init(this_client)) + { + ret = -1; + dbg("Can't find gsensor dmard08!\n"); + goto err_i2c_add_driver; + } + + + // create the platform device + l_dev_class = class_create(THIS_MODULE, GSENSOR_I2C_NAME); + if (IS_ERR(l_dev_class)){ + ret = PTR_ERR(l_dev_class); + printk(KERN_ERR "Can't class_create gsensor device !!\n"); + return ret; + } + if((ret = platform_device_register(&dmard08_device))) + { + klog("Can't register mc3230 platform devcie!!!\n"); + return ret; + } + if ((ret = platform_driver_register(&dmard08_driver)) != 0) + { + errlog("Can't register mc3230 platform driver!!!\n"); + return ret; + } +#ifdef CONFIG_HAS_EARLYSUSPEND + l_sensorconfig.earlysuspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; + l_sensorconfig.earlysuspend.suspend = dmard08_early_suspend; + l_sensorconfig.earlysuspend.resume = dmard08_late_resume; + register_early_suspend(&l_sensorconfig.earlysuspend); +#endif + + queue_delayed_work(l_sensorconfig.queue, &l_sensorconfig.work, msecs_to_jiffies(sample_rate_2_memsec(l_sensorconfig.sensor_samp))); + + return 0; + +err_i2c_add_driver: + sensor_i2c_unregister_device(this_client); + return ret; +} + +static void __exit dmard08_exit(void) //OK +{ + platform_driver_unregister(&dmard08_driver); + platform_device_unregister(&dmard08_device); + class_destroy(l_dev_class); + sensor_i2c_unregister_device(this_client); +} + +//********************************************************************************************************* +// 2011-11-30 +// Add by Steve Huang +// function definition +/* +void gsensor_write_offset_to_file(void) +{ + char data[18]; + unsigned int orgfs; + long lfile=-1; + + //sprintf(data,"%5d %5d %5d",offset.u.x,offset.u.y,offset.u.z); //marked by eason check again + + orgfs = get_fs(); +// Set segment descriptor associated to kernel space + set_fs(KERNEL_DS); + + lfile=sys_open(OffsetFileName,O_WRONLY|O_CREAT, 0777); + if (lfile < 0) + { + printk("sys_open %s error!!. %ld\n",OffsetFileName,lfile); + } + else + { + sys_write(lfile, data,18); + sys_close(lfile); + } + set_fs(orgfs); + + return; +} + +void gsensor_read_offset_from_file(void) +{ + unsigned int orgfs; + char data[18]; + long lfile=-1; + orgfs = get_fs(); +// Set segment descriptor associated to kernel space + set_fs(KERNEL_DS); + + lfile=sys_open(OffsetFileName, O_RDONLY, 0); + if (lfile < 0) + { + printk("sys_open %s error!!. %ld\n",OffsetFileName,lfile); + if(lfile==-2) + { + lfile=sys_open(OffsetFileName,O_WRONLY|O_CREAT, 0777); + if(lfile >=0) + { + strcpy(data,"00000 00000 00000"); + printk("sys_open %s OK!!. %ld\n",OffsetFileName,lfile); + sys_write(lfile,data,18); + sys_read(lfile, data, 18); + sys_close(lfile); + } + else + printk("sys_open %s error!!. %ld\n",OffsetFileName,lfile); + } + + } + else + { + sys_read(lfile, data, 18); + sys_close(lfile); + } + //sscanf(data,"%hd %hd %hd",&offset.u.x,&offset.u.y,&offset.u.z); //marked by eason check again + set_fs(orgfs); + +} +*/ +//********************************************************************************************************* +MODULE_AUTHOR("DMT_RD"); +MODULE_DESCRIPTION("DMARD08 g-sensor Driver"); +MODULE_LICENSE("GPL"); + +module_init(dmard08_init); +module_exit(dmard08_exit); + diff --git a/drivers/input/sensor/dmard08_gsensor/dmard08.h b/drivers/input/sensor/dmard08_gsensor/dmard08.h new file mode 100755 index 00000000..e6a6c935 --- /dev/null +++ b/drivers/input/sensor/dmard08_gsensor/dmard08.h @@ -0,0 +1,75 @@ +/* + * @file include/linux/dmard08.h + * @brief DMT g-sensor Linux device driver + * @author Domintech Technology Co., Ltd (http://www.domintech.com.tw) + * @version 1.2 + * @date 2011/11/14 + * + * @section LICENSE + * + * Copyright 2011 Domintech Technology Co., Ltd + * + * 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. + * + * + */ +#ifndef DMARD08_H +#define DMARD08_H + +#define GSENSOR_I2C_NAME "dmard08" +#define GSENSOR_I2C_ADDR 0x1c +/* +#define DEVICE_I2C_NAME "dmard08" + +//#define DMT_DEBUG_DATA 1 +#define DMT_DEBUG_DATA 0 + +#if DMT_DEBUG_DATA +#define IN_FUNC_MSG printk(KERN_INFO "@DMT@ In %s\n", __func__) +#define PRINT_X_Y_Z(x, y, z) printk(KERN_INFO "@DMT@ X/Y/Z axis: %04d , %04d , %04d\n", (x), (y), (z)) +#define PRINT_OFFSET(x, y, z) printk(KERN_INFO "@offset@ X/Y/Z axis: %04d , %04d , %04d\n",offset.x,offset.y,offset.z); +#else +#define IN_FUNC_MSG +#define PRINT_X_Y_Z(x, y, z) +#define PRINT_OFFSET(x, y, z) +#endif +*/ + +//g-senor layout configuration, choose one of the following configuration +#define CONFIG_GSEN_LAYOUT_PAT_1 +//#define CONFIG_GSEN_LAYOUT_PAT_2 +//#define CONFIG_GSEN_LAYOUT_PAT_3 +//#define CONFIG_GSEN_LAYOUT_PAT_4 +//#define CONFIG_GSEN_LAYOUT_PAT_5 +//#define CONFIG_GSEN_LAYOUT_PAT_6 +//#define CONFIG_GSEN_LAYOUT_PAT_7 +//#define CONFIG_GSEN_LAYOUT_PAT_8 + +#define CONFIG_GSEN_CALIBRATION_GRAVITY_ON_Z_NEGATIVE 1 +#define CONFIG_GSEN_CALIBRATION_GRAVITY_ON_Z_POSITIVE 2 +#define CONFIG_GSEN_CALIBRATION_GRAVITY_ON_Y_NEGATIVE 3 +#define CONFIG_GSEN_CALIBRATION_GRAVITY_ON_Y_POSITIVE 4 +#define CONFIG_GSEN_CALIBRATION_GRAVITY_ON_X_NEGATIVE 5 +#define CONFIG_GSEN_CALIBRATION_GRAVITY_ON_X_POSITIVE 6 + +#define DEFAULT_SENSITIVITY 256 +#define IOCTL_MAGIC 0x09 +#define SENSOR_DATA_SIZE 3 + +#define SENSOR_RESET _IO(IOCTL_MAGIC, 0) +#define SENSOR_CALIBRATION _IOWR(IOCTL_MAGIC, 1, int[SENSOR_DATA_SIZE]) +#define SENSOR_GET_OFFSET _IOR(IOCTL_MAGIC, 2, int[SENSOR_DATA_SIZE]) +#define SENSOR_SET_OFFSET _IOWR(IOCTL_MAGIC, 3, int[SENSOR_DATA_SIZE]) +#define SENSOR_READ_ACCEL_XYZ _IOR(IOCTL_MAGIC, 4, int[SENSOR_DATA_SIZE]) + +#define SENSOR_MAXNR 4 + +#endif + diff --git a/drivers/input/sensor/dmard09_gsensor/Makefile b/drivers/input/sensor/dmard09_gsensor/Makefile new file mode 100755 index 00000000..d9242020 --- /dev/null +++ b/drivers/input/sensor/dmard09_gsensor/Makefile @@ -0,0 +1,35 @@ +KERNELDIR=../../../../ +#KERNELDIR=/home/hangyan/android8850/kernel/ANDROID_3.0.8 +CROSS = arm_1103_le- +CC= $(CROSS)gcc +LD= $(CROSS)ld +STRIP = $(CROSS)strip + +DEBUG = n + +# Add your debugging flag (or not) to EXTRA_CFLAGS +ifeq ($(DEBUG),y) +# DEBFLAGS = -O -g -DSCULL_DEBUG # "-O" is needed to expand inlines +DEBFLAGS = -O0 -g -DSCULL_DEBUG # "-O" is needed to expand inlines + +else + DEBFLAGS = -O2 -Wall +endif + + +EXTRA_CFLAGS += $(DEBFLAGS) + + +MY_MODULE_NAME=s_wmt_gsensor_dmard09 + +obj-m := $(MY_MODULE_NAME).o +$(MY_MODULE_NAME)-objs := dmt09.o + +default: + $(MAKE) -C $(KERNELDIR) SUBDIRS=$(PWD) modules + $(STRIP) --strip-debug $(MY_MODULE_NAME).ko + rm -rf *.o *~ core .depend .*.cmd *.mod.c .tmp_versions + +clean: + rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions *.order *.symvers + diff --git a/drivers/input/sensor/dmard09_gsensor/dmt09.c b/drivers/input/sensor/dmard09_gsensor/dmt09.c new file mode 100755 index 00000000..90b03aa3 --- /dev/null +++ b/drivers/input/sensor/dmard09_gsensor/dmt09.c @@ -0,0 +1,1685 @@ +/* + * @file drivers/misc/dmt09.c + * @brief DMT g-sensor Linux device driver + * @author Domintech Technology Co., Ltd (http://www.domintech.com.tw) + * @version 1.06 + * @date 2013/08/14 + * @section LICENSE + * + * Copyright 2012 Domintech Technology Co., Ltd + * + * 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 "dmt09.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../sensor.h" + +////////////////////////////////////////////////////////// +static struct wmt_gsensor_data l_sensorconfig = { + .op = 0, + .int_gpio = 3, + .samp = 5, + .xyz_axis = { + {ABS_X, -1}, + {ABS_Y, 1}, + {ABS_Z, -1}, + }, + .sensor_proc = NULL, + .isdbg = 0, + .sensor_samp = 10, // 1 sample/second + .sensor_enable = 1, // enable sensor + .test_pass = 0, // for test program + .offset={0,0,0}, +}; + +static struct class* l_dev_class = NULL; +static void update_var(void); + +//////////////////////////////////////////////////////////// + + +static unsigned int interval; +static int D09_write_offset_to_file(struct i2c_client *client); +void D09_read_offset_from_file(struct i2c_client *client); +#define DMT_BROADCAST_APK_ENABLE +char D09_OffsetFileName[] = "/data/misc/gsensor_offset.txt";//"/system/vendor/dmt/gsensor_offset.txt";// /* FILE offset.txt */ +char DmtXXFileName[] = "/data/misc/dmt_sensor.txt";//"/system/vendor/dmt/dmt_sensor.txt";// +static int create_devidfile(void); +static struct dmt_data *s_dmt; +static int device_init(void); +static void device_exit(void); + +static int device_open(struct inode*, struct file*); +static long device_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); +static int device_close(struct inode*, struct file*); + +static int dmard09_suspend(struct platform_device *pdev, pm_message_t state); +static int dmard09_resume(struct platform_device *pdev); + +/*static int device_i2c_suspend(struct i2c_client *client, pm_message_t mesg); +static int device_i2c_resume(struct i2c_client *client); +static int __devinit device_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id); +static int __devexit device_i2c_remove(struct i2c_client *client);*/ +static int D09_i2c_read_xyz(struct i2c_client *client, int *xyz); +static int device_i2c_rxdata(struct i2c_client *client, unsigned char *rxDat, int length); +static int device_i2c_txdata(struct i2c_client *client, unsigned char *txData, int length); + +static int dmt_get_filter(struct i2c_client *client); +static int dmt_set_filter(struct i2c_client *client,int); +static int dmt_get_position(struct i2c_client *client); +static int dmt_set_position(struct i2c_client *client,int); +static int DMT_GetOpenStatus(struct i2c_client *client){ + struct dmt_data *dmt = i2c_get_clientdata(client); + GSE_LOG("start active=%d\n",dmt->active.counter); + wait_event_interruptible(dmt->open_wq, (atomic_read(&dmt->active) != 0)); + return 0; +} + +static int DMT_GetCloseStatus(struct i2c_client *client){ + struct dmt_data *dmt = i2c_get_clientdata(client); + GSE_LOG("start active=%d\n",dmt->active.counter); + wait_event_interruptible(dmt->open_wq, (atomic_read(&dmt->active) <= 0)); + return 0; +} + +static void DMT_sysfs_update_active_status(struct dmt_data *dmt , int en){ + unsigned long dmt_delay; + if(en){ + dmt_delay=msecs_to_jiffies(atomic_read(&dmt->delay)); + if(dmt_delay<1) + dmt_delay=1; + + GSE_LOG("schedule_delayed_work start with delay time=%lu\n",dmt_delay); + schedule_delayed_work(&dmt->delaywork,dmt_delay); + } + else + cancel_delayed_work_sync(&dmt->delaywork); +} + +static bool get_value_as_int(char const *buf, size_t size, int *value){ + long tmp; + if (size == 0) + return false; + /* maybe text format value */ + if ((buf[0] == '0') && (size > 1)) { + if ((buf[1] == 'x') || (buf[1] == 'X')) { + /* hexadecimal format */ + if (0 != strict_strtol(buf, 16, &tmp)) + return false; + } else { + /* octal format */ + if (0 != strict_strtol(buf, 8, &tmp)) + return false; + } + } else { + /* decimal format */ + if (0 != strict_strtol(buf, 10, &tmp)) + return false; + } + + if (tmp > INT_MAX) + return false; + + *value = tmp; + return true; +} +static bool get_value_as_int64(char const *buf, size_t size, long long *value) +{ + long long tmp; + if (size == 0) + return false; + /* maybe text format value */ + if ((buf[0] == '0') && (size > 1)) { + if ((buf[1] == 'x') || (buf[1] == 'X')) { + /* hexadecimal format */ + if (0 != strict_strtoll(buf, 16, &tmp)) + return false; + } else { + /* octal format */ + if (0 != strict_strtoll(buf, 8, &tmp)) + return false; + } + } else { + /* decimal format */ + if (0 != strict_strtoll(buf, 10, &tmp)) + return false; + } + + if (tmp > LLONG_MAX) + return false; + + *value = tmp; + return true; +} +/* sysfs enable show & store */ +static ssize_t dmt_sysfs_enable_show( + struct dmt_data *dmt, char *buf, int pos) +{ + char str[2][16]={"ACC enable OFF","ACC enable ON"}; + int flag; + flag=atomic_read(&dmt->enable); + return sprintf(buf, "%s\n", str[flag]); +} + +static ssize_t dmt_sysfs_enable_store( + struct dmt_data *dmt, char const *buf, size_t count, int pos) +{ + int en = 0; + if (NULL == buf) + return -EINVAL; + //GSE_LOG("buf=%x %x\n", buf[0], buf[1]); + if (0 == count) + return 0; + + if (false == get_value_as_int(buf, count, &en)) + return -EINVAL; + + en = en ? 1 : 0; + + atomic_set(&dmt->enable,en); + DMT_sysfs_update_active_status(dmt , en); + return count; +} + +static ssize_t dmt_enable_show(struct device *dev, struct device_attribute *attr, char *buf){ + return dmt_sysfs_enable_show( dev_get_drvdata(dev), buf, ACC_DATA_FLAG); +} + +static ssize_t dmt_enable_store( struct device *dev, struct device_attribute *attr, char const *buf, size_t count){ + return dmt_sysfs_enable_store( dev_get_drvdata(dev), buf, count, ACC_DATA_FLAG); +} + +/* sysfs delay show & store*/ +static ssize_t dmt_sysfs_delay_show( struct dmt_data *dmt, char *buf, int pos){ + return sprintf(buf, "%d\n", atomic_read(&dmt->delay)); +} + +static ssize_t dmt_sysfs_delay_store( struct dmt_data *dmt, char const *buf, size_t count, int pos){ + long long val = 0; + + if (NULL == buf) + return -EINVAL; + + if (0 == count) + return 0; + + if (false == get_value_as_int64(buf, count, &val)) + return -EINVAL; + + atomic_set(&dmt->delay, (unsigned int) val); + GSE_LOG("Driver attribute set delay =%lld\n", val); + + return count; +} + +static ssize_t dmt_delay_show( struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return dmt_sysfs_delay_show( dev_get_drvdata(dev), buf, ACC_DATA_FLAG); +} + +static ssize_t dmt_delay_store( struct device *dev, + struct device_attribute *attr, + char const *buf, + size_t count) +{ + return dmt_sysfs_delay_store( dev_get_drvdata(dev), buf, count, ACC_DATA_FLAG); +} +/* sysfs position show & store */ +static ssize_t dmt_position_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct input_dev *input = to_input_dev(dev); + struct dmt_data *dmt = input_get_drvdata(input); + + return sprintf(buf, "%d\n", dmt_get_position(dmt->client)); +} + +static ssize_t dmt_position_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct input_dev *input = to_input_dev(dev); + struct dmt_data *dmt = input_get_drvdata(input); + unsigned long position; + int ret; + + ret = strict_strtoul(buf, 10, &position); + if (ret < 0) + return count; + + dmt_set_position(dmt->client, position); + return count; +} +/* sysfs offset show & store */ +static ssize_t dmt_offset_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct input_dev *input = to_input_dev(dev); + struct dmt_data *dmt = input_get_drvdata(input); + return sprintf(buf, "( %d %d %d )\n", dmt->offset.u.x, dmt->offset.u.y, dmt->offset.u.z); +} + +static ssize_t dmt_offset_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct input_dev *input = to_input_dev(dev); + struct dmt_data *dmt = input_get_drvdata(input); + sscanf(buf, "%d %d %d", (int *)&dmt->offset.v[0], (int *)&dmt->offset.v[1], (int *)&dmt->offset.v[2]); + D09_write_offset_to_file(dmt->client); + update_var(); + return count; +} +/* sysfs filter show & store */ +static ssize_t dmt_filter_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct input_dev *input = to_input_dev(dev); + struct dmt_data *dmt = input_get_drvdata(input); + + return sprintf(buf, "%d\n", dmt_get_filter(dmt->client)); +} + +static ssize_t dmt_filter_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct input_dev *input = to_input_dev(dev); + struct dmt_data *dmt = input_get_drvdata(input); + unsigned long filter; + int ret; + + ret = strict_strtoul(buf, 10, &filter); + if (ret < 0) + return count; + + dmt_set_filter(dmt->client, filter); + return count; +} + +/* sysfs data show */ +static ssize_t dmt_acc_private_data_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct input_dev *input = to_input_dev(dev); + struct dmt_data *dmt = input_get_drvdata(input); + raw_data accel; + + mutex_lock(&dmt->data_mutex); + accel = dmt->last; + mutex_unlock(&dmt->data_mutex); + + return sprintf(buf, "( %d %d %d )\n", dmt->last.v[0], dmt->last.v[1], dmt->last.v[2]); +} +/* sysfs id show */ +static ssize_t dmt_id_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + char str[8]={GSENSOR_ID}; + return sprintf(buf, "%s\n", str); +} +/* sysfs debug_suspend show & store */ +#ifdef DMT_DEBUG_DATA +static ssize_t dmt_debug_suspend_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct input_dev *input = to_input_dev(dev); + struct dmt_data *dmt = input_get_drvdata(input); + int suspend = dmt->suspend; + + mutex_lock(&dmt->suspend_mutex); + suspend = sprintf(buf, "%d\n", dmt->suspend); + mutex_unlock(&dmt->suspend_mutex); + return suspend; +} + +static ssize_t dmt_debug_suspend_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct input_dev *input = to_input_dev(dev); + struct dmt_data *dmt = input_get_drvdata(input); + unsigned long suspend; + pm_message_t msg; + int ret; + + ret = strict_strtoul(buf, 10, &suspend); + if (ret < 0) + return count; + + memset(&msg, 0, sizeof(pm_message_t)); + + mutex_lock(&dmt->suspend_mutex); + + if (suspend) { + dmard09_suspend(dmt->pdevice, msg); + dmt->suspend = 1; + } else { + dmard09_resume(dmt->pdevice); + dmt->suspend = 0; + } + + mutex_unlock(&dmt->suspend_mutex); + + return count; +} +/* sysfs reg_read show & store */ +static ssize_t dmt_reg_read_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct dmt_data *dmt = dev_get_drvdata(dev); + int err; + unsigned char i2c[1]; + + i2c[0] = (unsigned char)atomic_read(&dmt->addr); + err = device_i2c_rxdata(dmt->client, i2c, 1); + if (err < 0) + return err; + + return sprintf(buf, "0x%02X\n", i2c[0]); +} + +static ssize_t dmt_reg_read_store(struct device *dev, + struct device_attribute *attr, + char const *buf, + size_t count) +{ + struct dmt_data *dmt = dev_get_drvdata(dev); + int addr = 0; + + if (NULL == buf) + return -EINVAL; + + if (0 == count) + return 0; + + if (false == get_value_as_int(buf, count, &addr)) + return -EINVAL; + + if (addr < 0 || 128 < addr) + return -EINVAL; + + atomic_set(&dmt->addr, addr); + + return 1; +} +#endif /* DEBUG */ +/********************************************************************* + * + * SysFS attribute functions + * + * directory : /sys/class/accelemeter/dmardXX/ + * files : + * - enable_acc [rw] [t] : enable flag for accelerometer + * - delay_acc [rw] [t] : delay in nanosecond for accelerometer + * - position [rw] [t] : chip mounting position + * - offset [rw] [t] : offset + * - data [r] [t] : raw data + * - id [r] [t] : chip id + * + * debug : + * - debug_suspend [w] [t] : suspend test + * - reg_read [rw] [t] : Read register + * - reg_write [rw] [t] : Weite register + * + * [rw]= read/write + * [r] = read only + * [w] = write only + * [b] = binary format + * [t] = text format + */ + +static struct device_attribute DMT_attributes[] = { + __ATTR(enable_acc, 0660, dmt_enable_show, dmt_enable_store), + __ATTR(delay_acc, 0660, dmt_delay_show, dmt_delay_store), + __ATTR(position, 0660, dmt_position_show, dmt_position_store), + __ATTR(offset, 0660, dmt_offset_show, dmt_offset_store), + __ATTR(filter, 0660, dmt_filter_show, dmt_filter_store), + __ATTR(data, 0660, dmt_acc_private_data_show, NULL), + __ATTR(id, 0660, dmt_id_show, NULL), +#ifdef DMT_DEBUG_DATA + __ATTR(debug_suspend, 0660, dmt_debug_suspend_show,dmt_debug_suspend_store), + __ATTR(reg_read, 0660, dmt_reg_read_show, dmt_reg_read_store), + __ATTR(reg_write, 0660, NULL, NULL), +#endif // DEBUG + __ATTR_NULL, +}; + +static char const *const ACCELEMETER_CLASS_NAME = "accelemeter"; +static char const *const GSENSOR_DEVICE_NAME = SENSOR_I2C_NAME; +static char const *const device_link_name = "i2c"; +static dev_t const dmt_device_dev_t = MKDEV(MISC_MAJOR, MISC_DYNAMIC_MINOR); + +// dmt sysfs functions +static int create_device_attributes(struct device *dev, struct device_attribute *attrs){ + int i; + int err = 0; + for (i = 0 ; NULL != attrs[i].attr.name ; ++i) { + err = device_create_file(dev, &attrs[i]); + if (0 != err) + break; + } + + if (0 != err) { + for (; i >= 0 ; --i) + device_remove_file(dev, &attrs[i]); + } + return err; +} + +static void remove_device_attributes( + struct device *dev, + struct device_attribute *attrs) +{ + int i; + + for (i = 0 ; NULL != attrs[i].attr.name ; ++i) + device_remove_file(dev, &attrs[i]); +} + +static int create_sysfs_interfaces(struct dmt_data *dmt) +{ + int err; + + if (NULL == dmt) + return -EINVAL; + + err = 0; + dmt->class = class_create(THIS_MODULE, ACCELEMETER_CLASS_NAME); + if (IS_ERR(dmt->class)) { + err = PTR_ERR(dmt->class); + goto exit_class_create_failed; + } + + dmt->class_dev = device_create( + dmt->class, + NULL, + dmt_device_dev_t, + dmt, + GSENSOR_DEVICE_NAME); + if (IS_ERR(dmt->class_dev)) { + err = PTR_ERR(dmt->class_dev); + goto exit_class_device_create_failed; + } + + err = sysfs_create_link( + &dmt->class_dev->kobj, + &dmt->client->dev.kobj, + device_link_name); + if (0 > err) + goto exit_sysfs_create_link_failed; + + err = create_device_attributes( + dmt->class_dev, + DMT_attributes); + if (0 > err) + goto exit_device_attributes_create_failed; +#if 0 + err = create_device_binary_attributes( + &dmt->class_dev->kobj, + dmt_bin_attributes); + if (0 > err) + goto exit_device_binary_attributes_create_failed; +#endif + + return err; + +#if 0 +exit_device_binary_attributes_create_failed: + remove_device_attributes(dmt->class_dev, dmt_attributes); +#endif +exit_device_attributes_create_failed: + sysfs_remove_link(&dmt->class_dev->kobj, device_link_name); +exit_sysfs_create_link_failed: + device_destroy(dmt->class, dmt_device_dev_t); +exit_class_device_create_failed: + dmt->class_dev = NULL; + class_destroy(dmt->class); +exit_class_create_failed: + dmt->class = NULL; + return err; +} + +static void remove_sysfs_interfaces(struct dmt_data *dmt){ + if (NULL == dmt) + return; + + if (NULL != dmt->class_dev) { + + remove_device_attributes( + dmt->class_dev, + DMT_attributes); + sysfs_remove_link( + &dmt->class_dev->kobj, + device_link_name); + dmt->class_dev = NULL; + } + if (NULL != dmt->class) { + device_destroy( + dmt->class, + dmt_device_dev_t); + class_destroy(dmt->class); + dmt->class = NULL; + } +} + +int D09_input_init(struct i2c_client *client){ + struct dmt_data *dmt = i2c_get_clientdata(client); + int err = 0; + dmt->input = input_allocate_device(); + if (!dmt->input){ + GSE_ERR("input device allocate ERROR !!\n"); + return -ENOMEM; + } + else + GSE_LOG("input device allocate Success !!\n"); + /* Setup input device */ + //dmt->input->name = SENSOR_I2C_NAME; + set_bit(EV_ABS, dmt->input->evbit); + /* Accelerometer [-78.5, 78.5]m/s2 in Q16 */ + input_set_abs_params(dmt->input, ABS_X, ABSMIN, ABSMAX, 0, 0); + input_set_abs_params(dmt->input, ABS_Y, ABSMIN, ABSMAX, 0, 0); + input_set_abs_params(dmt->input, ABS_Z, ABSMIN, ABSMAX, 0, 0); + /* Set InputDevice Name */ + dmt->input->name = INPUT_NAME_ACC; + /* Register */ + err = input_register_device(dmt->input); + if (err) { + GSE_ERR("input_register_device ERROR !!\n"); + input_free_device(dmt->input); + return err; + } + GSE_LOG("input_register_device SUCCESS %d !! \n",err); + + return err; +} + +int D09_calibrate(struct i2c_client *client) +{ + struct dmt_data *dmt = i2c_get_clientdata(client); + raw_data avg; + int i, j; + long xyz_acc[SENSOR_DATA_SIZE]; + int xyz[SENSOR_DATA_SIZE]; + /* initialize the offset value */ + for(i = 0; i < SENSOR_DATA_SIZE; ++i) + dmt->offset.v[i] = 0; + /* initialize the accumulation buffer */ + for(i = 0; i < SENSOR_DATA_SIZE; ++i) + xyz_acc[i] = 0; + + for(i = 0; i < AVG_NUM; i++) { + D09_i2c_read_xyz(client, (int *)&xyz); + for(j = 0; j < SENSOR_DATA_SIZE; ++j) + xyz_acc[j] += xyz[j]; + } + /* calculate averages */ + for(i = 0; i < SENSOR_DATA_SIZE; ++i) + avg.v[i] = xyz_acc[i] / AVG_NUM; + + if(avg.v[2] < 0){ + dmt->offset.u.x = avg.v[0] ; + dmt->offset.u.y = avg.v[1] ; + dmt->offset.u.z = avg.v[2] + DEFAULT_SENSITIVITY; + return CONFIG_GSEN_CALIBRATION_GRAVITY_ON_Z_POSITIVE; + } + else{ + dmt->offset.u.x = avg.v[0] ; + dmt->offset.u.y = avg.v[1] ; + dmt->offset.u.z = avg.v[2] - DEFAULT_SENSITIVITY; + return CONFIG_GSEN_CALIBRATION_GRAVITY_ON_Z_NEGATIVE; + } + return 0; +} + +int dmard09_init(struct i2c_client *client){ + + //struct dmt_data *dmt = i2c_get_clientdata(client); + unsigned char buffer[7]; + /* 1. Active Mode */ + buffer[0] = REG_ACTR; + buffer[1] = MODE_ACTIVE; + device_i2c_txdata(client, buffer, 2); + /* 2. check D09 who am I */ + buffer[0] = REG_DC; + device_i2c_rxdata(client, buffer, 1); + if (buffer[0] == VALUE_WHO_AM_I) + { + printk(KERN_INFO GSE_TAG"D09 WHO_AM_I_VALUE = %d \n", buffer[0]); + GSE_LOG("D09 registered I2C driver!\n"); + } + else + { + GSE_ERR("gsensor I2C err = %d!\n", buffer[0]); + return -1; + } + /* 3. Set Data conversion rate*/ + buffer[0] = REG_CNT_L1; + buffer[1] = VALUE_ODR_100; + buffer[2] = VALUE_CNT_L2; + device_i2c_txdata(client, buffer, 3); + /* 4. open hardware filter */ + buffer[0] = REG_ODF; + buffer[1] = ODF_Ave_4; + buffer[2] = 0x00; + device_i2c_txdata(client, buffer, 3); + /* 5. check hardware filter again */ + buffer[0] = REG_ODF; //0x07 smooth filter 1/8 Bandwidth */ + device_i2c_rxdata(client, buffer, 2); + printk(KERN_INFO GSE_TAG" REG_ODF = %x , %x\n", buffer[0] , buffer[1]); + + return 0; +} + +void D09_set_offset(struct i2c_client *client, int val[3]){ + struct dmt_data *dmt = i2c_get_clientdata(client); + int i; + for(i = 0; i < SENSOR_DATA_SIZE; ++i) + dmt->offset.v[i] = val[i]; +} + +struct file_operations sensor_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = device_ioctl, + .open = device_open, + .release = device_close, +}; + +static struct miscdevice dmt_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = SENSOR_I2C_NAME, + .fops = &sensor_fops, +}; + +static int sensor_close_dev(struct i2c_client *client){ + char buffer[3]; + buffer[0] = REG_ACTR; + device_i2c_rxdata(client, buffer, 2); + buffer[1] = buffer[0] & 0xFE; //Mask off last bit (POWER DOWN MODE) + //buffer[1] = MODE_POWERDOWN; + device_i2c_txdata(client,buffer, 2); + return 0; +} + +static void dmard09_shutdown(struct platform_device *pdev) +{ + flush_delayed_work_sync(&s_dmt->delaywork); + DMT_sysfs_update_active_status(s_dmt , 0); +} + + +//static int device_i2c_suspend(struct i2c_client *client, pm_message_t mesg){ +static int dmard09_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct dmt_data *dmt = i2c_get_clientdata(s_dmt->client); + flush_delayed_work_sync(&dmt->delaywork); + DMT_sysfs_update_active_status(dmt , 0); + return sensor_close_dev(dmt->client); +} + +//static int device_i2c_resume(struct i2c_client *client){ +static int dmard09_resume(struct platform_device *pdev) +{ + struct dmt_data *dmt = i2c_get_clientdata(s_dmt->client); + int en = 1; + GSE_FUN(); + printk("dmt->enable=%d",dmt->enable); + dmard09_init(dmt->client); + atomic_set(&dmt->enable,en); + DMT_sysfs_update_active_status(dmt , en); + return 0; +} +/* +static int __devexit device_i2c_remove(struct i2c_client *client){ + return 0; +} + +static const struct i2c_device_id device_i2c_ids[] = { + { SENSOR_I2C_NAME, 0}, + { } +}; + +static struct i2c_driver device_i2c_driver = { + .driver = { + .owner = THIS_MODULE, + .name = SENSOR_I2C_NAME, + }, + .class = I2C_CLASS_HWMON, + .id_table = device_i2c_ids, + .probe = device_i2c_probe, + .remove = __devexit_p(device_i2c_remove), +#ifdef CONFIG_HAS_EARLYSUSPEND + .suspend = device_i2c_suspend, + .resume = device_i2c_resume, +#endif +}; +*/ +static int device_open(struct inode *inode, struct file *filp){ + return 0; +} + +static long device_ioctl(struct file *file, unsigned int cmd, unsigned long arg){ + //struct i2c_client *client = (struct i2c_client *)file->private_data; + //struct dmt_data *dmt = (struct dmt_data*)i2c_get_clientdata(client); + + int err = 0, ret = 0, i; + int intBuf[SENSOR_DATA_SIZE], xyz[SENSOR_DATA_SIZE]; + /* check type */ + if (_IOC_TYPE(cmd) != IOCTL_MAGIC) return -ENOTTY; + + /* check user space pointer is valid */ + if (_IOC_DIR(cmd) & _IOC_READ) + err = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd)); + else if (_IOC_DIR(cmd) & _IOC_WRITE) + err = !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd)); + if (err) return -EFAULT; + + switch(cmd) { + case SENSOR_RESET: + ret = dmard09_init(s_dmt->client); + return ret; + + case SENSOR_CALIBRATION: + /* get orientation info */ + //if(copy_from_user(&intBuf, (int*)arg, sizeof(intBuf))) return -EFAULT; + D09_calibrate(s_dmt->client); + GSE_LOG("Sensor_calibration:%d %d %d\n", s_dmt->offset.u.x, s_dmt->offset.u.y, s_dmt->offset.u.z); + /* save file */ + D09_write_offset_to_file(s_dmt->client); + update_var(); + + /* return the offset */ + for(i = 0; i < SENSOR_DATA_SIZE; ++i) + intBuf[i] = s_dmt->offset.v[i]; + + ret = copy_to_user((int *)arg, &intBuf, sizeof(intBuf)); + return ret; + + case SENSOR_GET_OFFSET: + /* get data from file */ + D09_read_offset_from_file(s_dmt->client); + for(i = 0; i < SENSOR_DATA_SIZE; ++i) + intBuf[i] = s_dmt->offset.v[i]; + + ret = copy_to_user((int *)arg, &intBuf, sizeof(intBuf)); + return ret; + + case SENSOR_SET_OFFSET: + ret = copy_from_user(&intBuf, (int *)arg, sizeof(intBuf)); + D09_set_offset(s_dmt->client , intBuf); + /* write into file */ + D09_write_offset_to_file(s_dmt->client); + update_var(); + return ret; + + case SENSOR_READ_ACCEL_XYZ: + D09_i2c_read_xyz(s_dmt->client, (int *)&xyz); + for(i = 0; i < SENSOR_DATA_SIZE; ++i) + intBuf[i] = xyz[i] - s_dmt->offset.v[i]; + + ret = copy_to_user((int*)arg, &intBuf, sizeof(intBuf)); + return ret; + + case SENSOR_SETYPR: + if(copy_from_user(&intBuf, (int*)arg, sizeof(intBuf))) { + GSE_LOG("%s: -EFAULT\n",__func__); + return -EFAULT; + } + input_report_abs(s_dmt->input, ABS_X, intBuf[0]); + input_report_abs(s_dmt->input, ABS_Y, intBuf[1]); + input_report_abs(s_dmt->input, ABS_Z, intBuf[2]); + input_sync(s_dmt->input); + GSE_LOG("SENSOR_SETYPR OK! x=%d,y=%d,z=%d\n",intBuf[0],intBuf[1],intBuf[2]); + return ret; + + case SENSOR_GET_OPEN_STATUS: + GSE_LOG("Going into DMT_GetOpenStatus()\n"); + ret = DMT_GetOpenStatus(s_dmt->client); + return ret; + + case SENSOR_GET_CLOSE_STATUS: + GSE_LOG("Going into DMT_GetCloseStatus()\n"); + ret = DMT_GetCloseStatus(s_dmt->client); + return ret; + + case SENSOR_GET_DELAY: + ret = copy_to_user((int*)arg, &interval, sizeof(interval)); + return ret; + + default: /* redundant, as cmd was checked against MAXNR */ + return -ENOTTY; + } + + return 0; +} + +static int device_close(struct inode *inode, struct file *filp){ + return 0; +} + +/***** I2C I/O function ***********************************************/ +static int device_i2c_rxdata( struct i2c_client *client, unsigned char *rxData, int length){ + struct i2c_msg msgs[] = { + {.addr = client->addr, .flags = 0, .len = 1, .buf = rxData,}, + {.addr = client->addr, .flags = I2C_M_RD, .len = length, .buf = rxData,}, + }; + //unsigned char addr = rxData[0]; + if (i2c_transfer(client->adapter, msgs, 2) < 0) { + dev_err(&client->dev, "%s: transfer failed.", __func__); + return -EIO; + } + //DMT_DATA(&client->dev, "RxData: len=%02x, addr=%02x, data=%02x\n", + //length, addr, rxData[0]); + + return 0; +} + +static int device_i2c_txdata( struct i2c_client *client, unsigned char *txData, int length){ + struct i2c_msg msg[] = { + {.addr = client->addr, .flags = 0, .len = length, .buf = txData,}, + }; + + if (i2c_transfer(client->adapter, msg, 1) < 0) { + dev_err(&client->dev, "%s: transfer failed.", __func__); + return -EIO; + } + //DMT_DATA(&client->dev, "TxData: len=%02x, addr=%02x data=%02x\n", + //length, txData[0], txData[1]); + return 0; +} + +static int D09_i2c_read_xyz(struct i2c_client *client, int *xyz_p){ + + struct dmt_data *dmt = i2c_get_clientdata(client); + u8 buffer[11]; + s16 xyzTmp[SENSOR_DATA_SIZE]; + int pos = dmt->position; + int i, j , k; + /* get xyz high/low bytes, 0x0A */ + buffer[0] = REG_STAT; + /* Read acceleration data */ + if (device_i2c_rxdata(client, buffer, 8)!= 0) + for(i = 0; i < SENSOR_DATA_SIZE; ++i) + { + xyz_p[i] = 0; + xyzTmp[i] = 0; + } + else + for(i = 0; i < SENSOR_DATA_SIZE; ++i){ + xyz_p[i] = 0; + xyzTmp[i] = 0; + /* merge xyz high/low bytes & 1g = 128 becomes 1g = 1024 */ + mutex_lock(&dmt->data_mutex); + xyzTmp[i] =(((int16_t)((buffer[2*(i+1)+1] << 8)) | buffer[2*(i+1)] ) >> 3) << 5; + mutex_unlock(&dmt->data_mutex); + } +#ifdef SW_FILTER + if( dmt->aveflag >= dmt->filter){ + for(i = 0; i < SENSOR_DATA_SIZE; ++i){ + dmt->sum[i] = dmt->sum[i] - dmt->bufferave[i][dmt->pointer] + xyzTmp[i]; + } + /* transfer to the default layout */ + for(i = 0; i < SENSOR_DATA_SIZE; ++i){ + for(j = 0; j < SENSOR_DATA_SIZE; j++) + xyz_p[i] += (int)(dmt->sum[j]/dmt->filter * dmt_position_map[pos][i][j]); + } + } + else{ + /* init dmt->sum */ + for(i = 0; i < SENSOR_DATA_SIZE; ++i) + dmt->sum[i] = xyzTmp[i]; +#endif + /* transfer to the default layout */ + for(i = 0; i < SENSOR_DATA_SIZE; ++i){ + for(j = 0; j < SENSOR_DATA_SIZE; j++){ + xyz_p[i] += (int)(xyzTmp[j] * dmt_position_map[pos][i][j]); + //GSE_LOG("%04d, %04d,%d \n", xyz_p[i], xyzTmp[j], dmt_position_map[pos][i][j]); + } + } + //GSE_LOG("xyz_p: %04d , %04d , %04d\n", xyz_p[0], xyz_p[1], xyz_p[2]); +#ifdef SW_FILTER + dmt->aveflag++; + } + /* init dmt->sum */ + for(i = 0; i < SENSOR_DATA_SIZE; ++i){ + dmt->sum[i] = 0; + } + dmt->pointer++; + dmt->pointer %= dmt->filter; + for(i = 0; i < SENSOR_DATA_SIZE; ++i){ + dmt->bufferave[i][dmt->pointer] = xyzTmp[i]; + } + for(i = 0; i < SENSOR_DATA_SIZE; ++i){ + for(k = 0; k < dmt->filter; ++k){ + dmt->sum[i] += dmt->bufferave[i][k]; + } + } +#endif + return 0; +} + +static void DMT_work_func(struct work_struct *delaywork){ + struct dmt_data *dmt = container_of(delaywork, struct dmt_data, delaywork.work); + int i; + //static bool firsttime=true; + raw_data xyz; + unsigned long dmt_delay = msecs_to_jiffies(atomic_read(&dmt->delay)); + + + D09_i2c_read_xyz(dmt->client, (int *)&xyz.v); + /* dmt->last = RawData - Offset */ + for(i = 0; i < SENSOR_DATA_SIZE; ++i) + dmt->last.v[i] = xyz.v[i] - dmt->offset.v[i]; + //GSE_LOG("@DMTRaw @ X/Y/Z axis: %04d , %04d , %04d\n", xyz.v[0], xyz.v[1], xyz.v[2]); + //GSE_LOG("@Offset @ X/Y/Z axis: %04d , %04d , %04d\n", dmt->offset.u.x, dmt->offset.u.y, dmt->offset.u.z); + //GSE_LOG("@Raw-Offset@ X/Y/Z axis: %04d , %04d , %04d ,dmt_delay=%d\n", dmt->last.u.x, dmt->last.u.y, dmt->last.u.z, atomic_read(&dmt->delay)); + +#ifdef STABLE_VALUE_FUNCTION + if(abs(dmt->last.v[0])< RANGE_XYZ){ dmt->last.v[0] = 0;} + if(abs(dmt->last.v[1])< RANGE_XYZ){ dmt->last.v[1] = 0;} + if(abs(dmt->last.v[2])< RANGE_XYZ){ dmt->last.v[2] = 0;} +#endif + + + input_report_abs(dmt->input, ABS_X, -dmt->last.v[l_sensorconfig.xyz_axis[0][0]]*l_sensorconfig.xyz_axis[0][1]);//dmt->last.v[0]); + input_report_abs(dmt->input, ABS_Y, -dmt->last.v[l_sensorconfig.xyz_axis[1][0]]*l_sensorconfig.xyz_axis[1][1]);//dmt->last.v[1]); + input_report_abs(dmt->input, ABS_Z, -dmt->last.v[l_sensorconfig.xyz_axis[2][0]]*l_sensorconfig.xyz_axis[2][1]);//dmt->last.v[2]); + input_sync(dmt->input); + + if(dmt_delay < 1) + dmt_delay = 1; + schedule_delayed_work(&dmt->delaywork, dmt_delay); +} + +static int mma09_open(struct inode *node, struct file *fle) +{ + GSE_LOG("open...\n"); + return 0; +} + +static int mma09_close(struct inode *node, struct file *fle) +{ + GSE_LOG("close...\n"); + return 0; +} + +static long mma09_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + int err = 0; + //unsigned char data[6]; + void __user *argp = (void __user *)arg; + short delay, enable; + unsigned int uval = 0; + + + /* cmd mapping */ + switch(cmd) + { + case ECS_IOCTL_APP_SET_DELAY: + // set the rate of g-sensor + if (copy_from_user(&delay, argp, sizeof(short))) + { + printk(KERN_ALERT "Can't get set delay!!!\n"); + return -EFAULT; + } + klog("Get delay=%d\n", delay); + + if ((delay >=0) && (delay < 20)) + { + delay = 20; + } else if (delay > 200) + { + delay = 200; + } + l_sensorconfig.sensor_samp = 1000/delay; + atomic_set(&s_dmt->delay, 1000/delay); + break; + case ECS_IOCTL_APP_SET_AFLAG: + // enable/disable sensor + if (copy_from_user(&enable, argp, sizeof(short))) + { + printk(KERN_ERR "Can't get enable flag!!!\n"); + return -EFAULT; + } + klog("enable=%d\n",enable); + if ((enable >=0) && (enable <=1)) + { + //KMSGINF("driver: disable/enable(%d) gsensor.\n", enable); + + l_sensorconfig.sensor_enable = enable; + atomic_set(&s_dmt->enable,enable); + DMT_sysfs_update_active_status(s_dmt , enable); + + } else { + printk(KERN_ERR "Wrong enable argument in %s !!!\n", __FUNCTION__); + return -EINVAL; + } + break; + case WMT_IOCTL_SENSOR_GET_DRVID: + uval = DMARD09_DRVID;//; + if (copy_to_user((unsigned int*)arg, &uval, sizeof(unsigned int))) + { + return -EFAULT; + } + GSE_LOG("dmard09_driver_id:%d\n",uval); + break; + case WMT_IOCTL_SENOR_GET_RESOLUTION: + uval = (10<<8) | 1; + if (copy_to_user((unsigned int *)arg, &uval, sizeof(unsigned int))) + { + return -EFAULT; + } + printk("<<<<<< 0) + { + if (sample != l_sensorconfig.sensor_samp) + { + // should do sth + } + //printk(KERN_ALERT "sensor samp=%d(amsr:%d) has been set.\n", sample, amsr); + } else { + klog("Wrong sample argumnet of sensor.\n"); + } + } else if (sscanf(buffer, "enable=%d\n", &enable)) + { + if ((enable < 0) || (enable > 1)) + { + klog("The argument to enable/disable g-sensor should be 0 or 1 !!!\n"); + } else if (enable != l_sensorconfig.sensor_enable) + { + //mma_enable_disable(enable); + l_sensorconfig.sensor_enable = enable; + } + } else if (sscanf(buffer, "sensor_test=%d\n", &test)) + { // for test begin + l_sensorconfig.test_pass = 0; + atomic_set(&s_dmt->enable,1); + DMT_sysfs_update_active_status(s_dmt , 1); + } else if (sscanf(buffer, "sensor_testend=%d\n", &test)) + { // Don nothing only to be compatible the before testing program + atomic_set(&s_dmt->enable,0); + DMT_sysfs_update_active_status(s_dmt , 0); + } + //mutex_unlock(&sense_data_mutex); + return count; +} + +static int sensor_readproc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len = 0; + + len = sprintf(page, + "test_pass=%d\nisdbg=%d\nrate=%d\nenable=%d\n", + l_sensorconfig.test_pass, + l_sensorconfig.isdbg, + l_sensorconfig.sensor_samp, + l_sensorconfig.sensor_enable + ); + return len; +} + + +//static int __devinit device_i2c_probe(struct i2c_client *client,const struct i2c_device_id *id){ +static int __devinit dmard09_probe(struct platform_device *pdev) +{ + int i, k, ret = 0; + //struct dmt_data *s_dmt = i2c_get_clientdata(client); + //struct dmt_data *s_dmt; + GSE_FUN(); +/* + if(!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)){ + GSE_ERR("check_functionality failed.\n"); + ret = -ENODEV; + goto exit0; + } + + // Allocate memory for driver data + s_dmt = kzalloc(sizeof(struct dmt_data), GFP_KERNEL); + memset(s_dmt, 0, sizeof(struct dmt_data)); + if (s_dmt == NULL) { + GSE_ERR("alloc data failed.\n"); + ret = -ENOMEM; + goto exit1; + } +*/ + /*for(i = 0; i < SENSOR_DATA_SIZE; ++i) + s_dmt->offset.v[i] = 0;*/ +#ifdef SW_FILTER + s_dmt->pointer = 0; + s_dmt->aveflag = 0; + for(i = 0; i < SENSOR_DATA_SIZE; ++i){ + s_dmt->sum[i] = 0; + for(k = 0; k < SENSOR_DATA_AVG; ++k){ + s_dmt->bufferave[i][k] = 0; + } + } + s_dmt->filter = SENSOR_DATA_AVG; + GSE_LOG("D09_DEFAULT_FILTER: %d\n", s_dmt->filter); +#endif + /* I2C initialization */ + //s_dmt->client = client; + + /* set client data */ + i2c_set_clientdata(s_dmt->client, s_dmt); + /*ret = dmard09_init(client); + if (ret < 0) + goto exit2; + */ + /* input */ + ret = D09_input_init(s_dmt->client); + if (ret){ + GSE_ERR("D09_input_init fail, error code= %d\n",ret); + goto exit3; + } + + /* initialize variables in dmt_data */ + mutex_init(&s_dmt->data_mutex); + mutex_init(&s_dmt->enable_mutex); +#ifdef DMT_DEBUG_DATA + mutex_init(&s_dmt->suspend_mutex); +#endif + init_waitqueue_head(&s_dmt->open_wq); + atomic_set(&s_dmt->active, 0); + atomic_set(&s_dmt->enable, 0); + atomic_set(&s_dmt->delay, 0); + atomic_set(&s_dmt->addr, 0); + /* DMT Acceleration Sensor Mounting Position on Board */ + s_dmt->position = D09_DEFAULT_POSITION; + GSE_LOG("D09_DEFAULT_POSITION: %d\n", s_dmt->position); + //s_dmt->position = (CONFIG_INPUT_DMT_ACCELEROMETER_POSITION); + //GSE_LOG("CONFIG_INPUT_DMT_ACCELEROMETER_POSITION: %d\n", s_dmt->position); + /* Misc device */ + if (misc_register(&dmt_device) < 0){ + GSE_ERR("dmt_dev register failed"); + goto exit4; + } + + /* Setup sysfs */ + if (create_sysfs_interfaces(s_dmt) < 0){ + GSE_ERR("create sysfs failed."); + goto exit5; + } +#ifdef CONFIG_HAS_EARLYSUSPEND + s_dmt->early_suspend.suspend = device_i2c_suspend; + s_dmt->early_suspend.resume = device_i2c_resume; + register_early_suspend(&s_dmt->early_suspend); +#endif + /* Setup driver interface */ + INIT_DELAYED_WORK(&s_dmt->delaywork, DMT_work_func); + GSE_LOG("DMT: INIT_DELAYED_WORK\n"); + + //register ctrl dev + ret = misc_register(&d09_device); + if (ret !=0) { + errlog("Can't register d09_device!\n"); + return -1; + } + // register rd/wr proc + l_sensorconfig.sensor_proc = create_proc_entry(GSENSOR_PROC_NAME, 0666, NULL/*&proc_root*/); + if (l_sensorconfig.sensor_proc != NULL) + { + l_sensorconfig.sensor_proc->write_proc = sensor_writeproc; + l_sensorconfig.sensor_proc->read_proc = sensor_readproc; + } + + //create offset file after factory reset + D09_read_offset_from_file(s_dmt->client); + + return 0; + +exit5: + misc_deregister(&dmt_device); +exit4: + input_unregister_device(s_dmt->input); +exit3: + kfree(s_dmt); +/*exit2: +exit1: +exit0:*/ + return ret; +} +/* +static struct i2c_board_info dmard09_board_info={ + .type = SENSOR_I2C_NAME, + .addr = SENSOR_I2C_ADDR, +}; +*/ +//static struct i2c_client *client; + +extern int wmt_getsyspara(char *varname, unsigned char *varval, int *varlen); +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.dm09sensor", varbuf, &varlen)) { + errlog("Can't get gsensor config in u-boot!!!!\n"); + return -1; + } else { + n = sscanf(varbuf, "%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d", + &l_sensorconfig.op, + &l_sensorconfig.int_gpio, + &l_sensorconfig.samp, + &(l_sensorconfig.xyz_axis[0][0]), + &(l_sensorconfig.xyz_axis[0][1]), + &(l_sensorconfig.xyz_axis[1][0]), + &(l_sensorconfig.xyz_axis[1][1]), + &(l_sensorconfig.xyz_axis[2][0]), + &(l_sensorconfig.xyz_axis[2][1]), + &(l_sensorconfig.offset[0]), + &(l_sensorconfig.offset[1]), + &(l_sensorconfig.offset[2]) + ); + if (n != 12) { + errlog("gsensor format is error in u-boot!!!\n"); + return -1; + } + l_sensorconfig.sensor_samp = l_sensorconfig.samp; + + dbg("get the sensor config: %d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d\n", + l_sensorconfig.op, + l_sensorconfig.int_gpio, + l_sensorconfig.samp, + l_sensorconfig.xyz_axis[0][0], + l_sensorconfig.xyz_axis[0][1], + l_sensorconfig.xyz_axis[1][0], + l_sensorconfig.xyz_axis[1][1], + l_sensorconfig.xyz_axis[2][0], + l_sensorconfig.xyz_axis[2][1], + l_sensorconfig.offset[0], + l_sensorconfig.offset[1], + l_sensorconfig.offset[2] + ); + } + return 0; +} + +static void dmard09_platform_release(struct device *device) +{ + GSE_LOG("...\n"); + return; +} + +static int __devexit dmard09_remove(struct platform_device *pdev) +{ + if (l_sensorconfig.sensor_proc != NULL) + { + remove_proc_entry(GSENSOR_PROC_NAME, NULL); + l_sensorconfig.sensor_proc = NULL; + } + //misc_deregister(&d09_device); + return 0; +} + + +static struct platform_device dmard09_device = { + .name = SENSOR_I2C_NAME, + .id = 0, + .dev = { + .release = dmard09_platform_release, + }, +}; + +static struct platform_driver dmard09_driver = { + .probe = dmard09_probe, + .remove = dmard09_remove, + .shutdown = dmard09_shutdown, + .suspend = dmard09_suspend, + .resume = dmard09_resume, + .driver = { + .name = SENSOR_I2C_NAME, + }, +}; + + +static int __init device_init(void){ + //struct device *device; + struct i2c_client *this_client; + int ret = 0; + + // parse g-sensor u-boot arg + ret = get_axisset(); + if (ret < 0) + { + printk("<<<<<%s user choose to no sensor chip!\n", __func__); + return ret; + } + GSE_LOG("D09 gsensor driver: initialize.\n"); + + if (!(this_client = sensor_i2c_register_device(0, DEVICE_I2C_ADDR, SENSOR_I2C_NAME))) + { + printk(KERN_ERR"Can't register gsensor i2c device!\n"); + return -1; + } + + if (dmard09_init(this_client)) + { + GSE_ERR("Failed to init dmard09!\n"); + sensor_i2c_unregister_device(this_client); + return -1; + } + + /* Allocate memory for driver data */ + s_dmt = kzalloc(sizeof(struct dmt_data), GFP_KERNEL); + //memset(s_dmt, 0, sizeof(struct dmt_data)); + if (s_dmt == NULL) { + GSE_ERR("alloc data failed.\n"); + return -ENOMEM; + } + + s_dmt->client = this_client; + s_dmt->pdevice = &dmard09_device; + s_dmt->offset.u.x = l_sensorconfig.offset[0]; + s_dmt->offset.u.y = l_sensorconfig.offset[1]; + s_dmt->offset.u.z = l_sensorconfig.offset[2]; + + + // create the platform device + l_dev_class = class_create(THIS_MODULE, SENSOR_I2C_NAME); + if (IS_ERR(l_dev_class)){ + ret = PTR_ERR(l_dev_class); + printk(KERN_ERR "Can't class_create gsensor device !!\n"); + return ret; + } + if((ret = platform_device_register(&dmard09_device))) + { + GSE_ERR("Can't register dmard09 platform devcie!!!\n"); + return ret; + } + if ((ret = platform_driver_register(&dmard09_driver)) != 0) + { + GSE_ERR("Can't register dmard09 platform driver!!!\n"); + return ret; + } + + return 0; +} + +static void __exit device_exit(void){ + //i2c_unregister_device(client); + //i2c_del_driver(&device_i2c_driver); + GSE_LOG("D09 gsensor driver: release.\n"); + + flush_delayed_work_sync(&s_dmt->delaywork); + cancel_delayed_work_sync(&s_dmt->delaywork); + + input_unregister_device(s_dmt->input); + input_free_device(s_dmt->input); + misc_deregister(&dmt_device); + misc_deregister(&d09_device); + platform_driver_unregister(&dmard09_driver); + platform_device_unregister(&dmard09_device); + sensor_i2c_unregister_device(s_dmt->client); + class_destroy(l_dev_class); + + remove_sysfs_interfaces(s_dmt); + kfree(s_dmt); +} + +static int dmt_get_filter(struct i2c_client *client){ + struct dmt_data *dmt = i2c_get_clientdata(client); + return dmt->filter; +} + +static int dmt_set_filter(struct i2c_client *client, int filter){ + struct dmt_data *dmt = i2c_get_clientdata(client); + if (!((filter >= 1) && (filter <= 32))) + return -1; + dmt->filter = filter; + return 0; +} + +static int dmt_get_position(struct i2c_client *client){ + struct dmt_data *dmt = i2c_get_clientdata(client); + return dmt->position; +} + +static int dmt_set_position(struct i2c_client *client, int position){ + struct dmt_data *dmt = i2c_get_clientdata(client); + if (!((position >= 0) && (position <= 7))) + return -1; + dmt->position = position; + return 0; +} + +extern int wmt_setsyspara(char *varname, char *varval); +static void update_var(void) +{ + char varbuf[64]; + int varlen; + + memset(varbuf, 0, sizeof(varbuf)); + varlen = sizeof(varbuf); + + sprintf(varbuf, "%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d", + l_sensorconfig.op, + l_sensorconfig.int_gpio, + l_sensorconfig.samp, + l_sensorconfig.xyz_axis[0][0], + l_sensorconfig.xyz_axis[0][1], + l_sensorconfig.xyz_axis[1][0], + l_sensorconfig.xyz_axis[1][1], + l_sensorconfig.xyz_axis[2][0], + l_sensorconfig.xyz_axis[2][1], + s_dmt->offset.u.x, + s_dmt->offset.u.y, + s_dmt->offset.u.z + ); + + wmt_setsyspara("wmt.io.dm09sensor",varbuf); +} + +static int D09_write_offset_to_file(struct i2c_client *client){ + struct dmt_data *dmt = i2c_get_clientdata(client); + char r_buf[18] = {0}; + char w_buf[18] = {0}; + //unsigned int orgfs; + struct file *fp; + mm_segment_t fs; + ssize_t ret; + //int8_t i; + + sprintf(w_buf,"%5d %5d %5d", dmt->offset.u.x, dmt->offset.u.y, dmt->offset.u.z); + /* Set segment descriptor associated to kernel space */ + fp = filp_open(D09_OffsetFileName, O_RDWR | O_CREAT, 0777); + if(IS_ERR(fp)){ + GSE_ERR("filp_open %s error!!.:%d\n",D09_OffsetFileName,fp); + return -1; + } + else{ + fs = get_fs(); + //set_fs(KERNEL_DS); + set_fs(get_ds()); + GSE_LOG("filp_open %s SUCCESS!!.\n",D09_OffsetFileName); + //fp->f_op->write(fp,data,18, &fp->f_pos); + //filp_close(fp,NULL); + ret = fp->f_op->write(fp,w_buf,18,&fp->f_pos); + if(ret != 18) + { + printk(KERN_ERR "%s: write error!\n", __func__); + filp_close(fp,NULL); + return -EIO; + } + //fp->f_pos=0x00; + ret = fp->f_op->read(fp,r_buf, 18,&fp->f_pos); + if(ret < 0) + { + printk(KERN_ERR "%s: read error!\n", __func__); + filp_close(fp,NULL); + return -EIO; + } + set_fs(fs); + + // + //printk(KERN_INFO "%s: read ret=%d!", __func__, ret); + /* for(i=0; i<18 ;i++) + { + if(r_buf[i] != w_buf[i]) + { + printk(KERN_ERR "%s: read back error, r_buf[%x](0x%x) != w_buf[%x](0x%x)\n", + __func__, i, r_buf[i], i, w_buf[i]); + filp_close(fp,NULL); + return -EIO; + } + } + */ + + } + filp_close(fp,NULL); + return 0; +} + +void D09_read_offset_from_file(struct i2c_client *client){ + struct dmt_data *dmt = i2c_get_clientdata(client); + unsigned int orgfs; + char data[18]; + struct file *fp; + int ux,uy,uz; + orgfs = get_fs(); + /* Set segment descriptor associated to kernel space */ + set_fs(KERNEL_DS); + + fp = filp_open(D09_OffsetFileName, O_RDWR , 0); + GSE_FUN(); + if(IS_ERR(fp)){ + GSE_ERR("Sorry,file open ERROR !\n"); + if(l_sensorconfig.op){ //first time + l_sensorconfig.op=0; +#if AUTO_CALIBRATION + /* get acceleration average reading */ + D09_calibrate(client); + update_var(); + D09_write_offset_to_file(client); +#endif +#ifdef DMT_BROADCAST_APK_ENABLE + create_devidfile(); + return; +#endif + } + D09_write_offset_to_file(client); + } + else{ + GSE_LOG("filp_open %s SUCCESS!!.\n",D09_OffsetFileName); + fp->f_op->read(fp,data,18, &fp->f_pos); + GSE_LOG("filp_read result %s\n",data); + sscanf(data,"%d %d %d",&ux,&uy,&uz); + dmt->offset.u.x=ux; + dmt->offset.u.y=uy; + dmt->offset.u.z=uz; + } + set_fs(orgfs); +} +static int create_devidfile(void) +{ + char data[18]; + unsigned int orgfs; + struct file *fp; + + sprintf(data,"%5d %5d %5d",0,0,0); + orgfs = get_fs(); + /* Set segment descriptor associated to kernel space */ + set_fs(KERNEL_DS); + GSE_FUN(); + fp = filp_open(DmtXXFileName, O_RDWR | O_CREAT, 0777); + if(IS_ERR(fp)){ + GSE_ERR("Sorry,file open ERROR !\n"); + return -1; + } + fp->f_op->write(fp,data,18, &fp->f_pos); + set_fs(orgfs); + filp_close(fp,NULL); + return 0; +} +//********************************************************************************************************* +MODULE_AUTHOR("DMT_RD"); +MODULE_DESCRIPTION("DMT Gsensor Driver"); +MODULE_LICENSE("GPL"); + +module_init(device_init); +module_exit(device_exit); diff --git a/drivers/input/sensor/dmard09_gsensor/dmt09.h b/drivers/input/sensor/dmard09_gsensor/dmt09.h new file mode 100755 index 00000000..d30e606a --- /dev/null +++ b/drivers/input/sensor/dmard09_gsensor/dmt09.h @@ -0,0 +1,183 @@ +/* @version 1.03 + * Copyright 2011 Domintech Technology Co., Ltd + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +#ifndef DMT09_H +#define DMT09_H +#include +#include +#include +#include +#include +#include +#include +#include +//#include +#define AUTO_CALIBRATION 0 +#define SW_FILTER /* Enable or Disable Software filter */ +#define SENSOR_DATA_AVG 4//8 /* AVG sensor data */ + +#define STABLE_VALUE_FUNCTION +#define RANGE_XYZ 40 + +//#define DMT_DEBUG_DATA +#define GSE_TAG "[DMT_Gsensor]" +#ifdef DMT_DEBUG_DATA +#define GSE_ERR(fmt, args...) printk(KERN_ERR GSE_TAG"%s %d : "fmt, __FUNCTION__, __LINE__, ##args) +#define GSE_LOG(fmt, args...) printk(KERN_INFO GSE_TAG fmt, ##args) +#define GSE_FUN(f) printk(KERN_INFO GSE_TAG" %s: %s: %i\n", __FILE__, __func__, __LINE__) +#define DMT_DATA(dev, ...) dev_dbg((dev), ##__VA_ARGS__) +#else +#define GSE_ERR(fmt, args...) +#define GSE_LOG(fmt, args...) +#define GSE_FUN(f) +#define DMT_DATA(dev, format, ...) +#endif + +#define GSENSOR_ID "DMARD09" +#define INPUT_NAME_ACC "g-sensor"//"DMT_accel"//"g-sensor"// /* Input Device Name */ +#define SENSOR_I2C_NAME "dmard09"//"dmt"// /* Device name for DMARD09 misc. device */ +#define DEVICE_I2C_ADDR 0x1d +#define REG_ACTR 0x00 +#define REG_STAT 0x0A +#define REG_DX 0x0C +#define REG_DY 0x0E +#define REG_DZ 0x10 +#define REG_DT 0x12 +#define REG_INL 0x16 +#define REG_DC 0x18 +#define REG_CNT_L1 0x1B +#define REG_CNT_L2 0x1C +#define REG_CNT_L3 0x1D +#define REG_INC 0x1E +#define REG_ODF 0x20 +#define REG_THR1 0x62 +#define REG_THR2 0x64 + +#define MODE_ACTIVE 0x61 /* active */ +#define MODE_POWERDOWN 0x60 /* powerdown */ + +#define VALUE_WHO_AM_I 0x95 /* D09 WMI */ +#define VALUE_ODR_200 0x9C /* conversion rate 200Hz */ +#define VALUE_ODR_100 0x98 /* conversion rate 100Hz */ +#define VALUE_ODR_50 0x94 /* conversion rate 50Hz */ +#define VALUE_ODR_20 0x90 /* conversion rate 20Hz */ +#define VALUE_ODR_10 0x8C /* conversion rate 10Hz */ +#define VALUE_ODR_5 0x88 /* conversion rate 5Hz */ +#define VALUE_ODR_1 0x84 /* conversion rate 1Hz */ +#define VALUE_ODR_0_5 0x80 /* conversion rate 0.5Hz */ +#define VALUE_CNT_L2 0xE4 /* Disable IEN */ +/* Optional Digital Filter [Low Byte and High Byte Order] */ +#define ODF_NoFilter 0x00 /* No filter */ +#define ODF_Ave_4 0x03 /* smooth filter 1/4 Bandwidth */ +#define ODF_Ave_8 0x07 /* smooth filter 1/8 Bandwidth */ +#define ODF_Ave_16 0x0f /* smooth filter 1/16 Bandwidth */ + + +#define CONFIG_GSEN_CALIBRATION_GRAVITY_ON_Z_NEGATIVE 1 +#define CONFIG_GSEN_CALIBRATION_GRAVITY_ON_Z_POSITIVE 2 +#define CONFIG_GSEN_CALIBRATION_GRAVITY_ON_Y_NEGATIVE 3 +#define CONFIG_GSEN_CALIBRATION_GRAVITY_ON_Y_POSITIVE 4 +#define CONFIG_GSEN_CALIBRATION_GRAVITY_ON_X_NEGATIVE 5 +#define CONFIG_GSEN_CALIBRATION_GRAVITY_ON_X_POSITIVE 6 + +#define AVG_NUM 16 +#define SENSOR_DATA_SIZE 3 +#define DEFAULT_SENSITIVITY 1024 + +#define IOCTL_MAGIC 0x09 +#define SENSOR_RESET _IO(IOCTL_MAGIC, 0) +#define SENSOR_CALIBRATION _IOWR(IOCTL_MAGIC, 1, int[SENSOR_DATA_SIZE]) +#define SENSOR_GET_OFFSET _IOR(IOCTL_MAGIC, 2, int[SENSOR_DATA_SIZE]) +#define SENSOR_SET_OFFSET _IOWR(IOCTL_MAGIC, 3, int[SENSOR_DATA_SIZE]) +#define SENSOR_READ_ACCEL_XYZ _IOR(IOCTL_MAGIC, 4, int[SENSOR_DATA_SIZE]) +#define SENSOR_SETYPR _IOW(IOCTL_MAGIC, 5, int[SENSOR_DATA_SIZE]) +#define SENSOR_GET_OPEN_STATUS _IO(IOCTL_MAGIC, 6) +#define SENSOR_GET_CLOSE_STATUS _IO(IOCTL_MAGIC, 7) +#define SENSOR_GET_DELAY _IOR(IOCTL_MAGIC, 8, unsigned int*) +#define SENSOR_MAXNR 8 +/* Default sensorlayout parameters */ +#define D09_DEFAULT_POSITION 6 + +/* Transformation matrix for chip mounting position */ +static const int dmt_position_map[][3][3] = { + { { 1, 0, 0}, { 0,-1, 0}, { 0, 0,-1}, }, /* top/upper-left */ + { { 0, 1, 0}, { 1, 0, 0}, { 0, 0,-1}, }, /* top/lower-left */ + { {-1, 0, 0}, { 0, 1, 0}, { 0, 0,-1}, }, /* top/lower-right */ + { { 0,-1, 0}, {-1, 0, 0}, { 0, 0,-1}, }, /* top/upper-right */ + { {-1, 0, 0}, { 0,-1, 0}, { 0, 0, 1}, }, /* bottom/upper-right*/ + { { 0,-1, 0}, {-1, 0, 0}, { 0, 0, 1}, }, /* bottom/upper-left */ + { { 1, 0, 0}, { 0, 1, 0}, { 0, 0, 1}, }, /* bottom/lower-right*/ + { { 0, 1,0}, { 1, 0, 0}, { 0, 0, 1}, }, /* bottom/lower-left */ +}; + +typedef union { + struct { + int x; + int y; + int z; + } u; + int v[SENSOR_DATA_SIZE]; +} raw_data; + +struct dmt_data { + struct platform_device *pdevice; + struct device *class_dev; + struct class *class; + struct input_dev *input; + struct i2c_client *client; +#ifdef CONFIG_HAS_EARLYSUSPEND + struct early_suspend early_suspend; +#endif + struct delayed_work delaywork; + struct work_struct work; + struct mutex data_mutex; + struct mutex enable_mutex; /* for suspend */ + raw_data last; /* RawData */ + raw_data offset; /* Offset */ +#ifdef SW_FILTER + int sum[SENSOR_DATA_SIZE]; /* SW_FILTER sum */ + int bufferave[3][32]; + s8 aveflag; /* FULL bufferave[][] */ + s8 pointer; /* last update data */ +#endif + wait_queue_head_t open_wq; + atomic_t active; + atomic_t delay; + atomic_t enable; + int filter; + int position; /* must int type ,for Kconfig setup */ + atomic_t addr; +#ifdef DMT_DEBUG_DATA + struct mutex suspend_mutex; + int suspend; +#endif +}; + +#define ACC_DATA_FLAG 0 +#define MAG_DATA_FLAG 1 +#define ORI_DATA_FLAG 2 +#define DMT_NUM_SENSORS 3 + +/* ABS axes parameter range [um/s^2] (for input event) */ +#define GRAVITY_EARTH 9806550 +#define ABSMAX (GRAVITY_EARTH * 2) +#define ABSMIN (-GRAVITY_EARTH * 2) + +#endif diff --git a/drivers/input/sensor/dmard10_gsensor/Makefile b/drivers/input/sensor/dmard10_gsensor/Makefile new file mode 100755 index 00000000..3241f881 --- /dev/null +++ b/drivers/input/sensor/dmard10_gsensor/Makefile @@ -0,0 +1,34 @@ +KERNELDIR=../../../../ +#KERNELDIR=/home/hangyan/android8850/kernel/ANDROID_3.0.8 +CROSS = arm_1103_le- +CC= $(CROSS)gcc +LD= $(CROSS)ld +STRIP = $(CROSS)strip + +DEBUG = n + +# Add your debugging flag (or not) to EXTRA_CFLAGS +ifeq ($(DEBUG),y) +# DEBFLAGS = -O -g -DSCULL_DEBUG # "-O" is needed to expand inlines +DEBFLAGS = -O0 -g -DSCULL_DEBUG # "-O" is needed to expand inlines + +else + DEBFLAGS = -O2 -Wall +endif + +EXTRA_CFLAGS += $(DEBFLAGS) + + +MY_MODULE_NAME=s_wmt_gsensor_dmard10 + +obj-m := $(MY_MODULE_NAME).o +$(MY_MODULE_NAME)-objs := dmt10.o + +default: + $(MAKE) -C $(KERNELDIR) SUBDIRS=$(PWD) modules + $(STRIP) --strip-debug $(MY_MODULE_NAME).ko + rm -rf *.o *~ core .depend .*.cmd *.mod.c .tmp_versions + +clean: + rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions *.order *.symvers + diff --git a/drivers/input/sensor/dmard10_gsensor/dmt10.c b/drivers/input/sensor/dmard10_gsensor/dmt10.c new file mode 100755 index 00000000..9810ea3a --- /dev/null +++ b/drivers/input/sensor/dmard10_gsensor/dmt10.c @@ -0,0 +1,1702 @@ +/* + * @file drivers/misc/dmt10.c + * @brief DMT g-sensor Linux device driver + * @author Domintech Technology Co., Ltd (http://www.domintech.com.tw) + * @version 1.06 + * @date 2013/08/14 + * @section LICENSE + * + * Copyright 2012 Domintech Technology Co., Ltd + * + * 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 "dmt10.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../sensor.h" + +////////////////////////////////////////////////////////// +static struct wmt_gsensor_data l_sensorconfig = { + .op = 0, + .int_gpio = 3, + .samp = 5, + .xyz_axis = { + {ABS_X, -1}, + {ABS_Y, 1}, + {ABS_Z, -1}, + }, + .sensor_proc = NULL, + .isdbg = 0, + .sensor_samp = 10, // 1 sample/second + .sensor_enable = 1, // enable sensor + .test_pass = 0, // for test program + .offset={0,0,0}, +}; + +static struct class* l_dev_class = NULL; +static void update_var(void); + +//////////////////////////////////////////////////////////// + + +static unsigned int interval; +static int D10_write_offset_to_file(struct i2c_client *client); +void D10_read_offset_from_file(struct i2c_client *client); +#define DMT_BROADCAST_APK_ENABLE +char D10_OffsetFileName[] = "/data/misc/gsensor_offset.txt"; /* FILE offset.txt */ +char DmtXXFileName[] = "/data/misc/dmt_sensor.txt"; +static int create_devidfile(void); +static struct dmt_data *s_dmt; +static int device_init(void); +static void device_exit(void); + +static int device_open(struct inode*, struct file*); +static long device_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); +static int device_close(struct inode*, struct file*); + +static int dmard10_suspend(struct platform_device *pdev, pm_message_t state); +static int dmard10_resume(struct platform_device *pdev); + +/*static int device_i2c_suspend(struct i2c_client *client, pm_message_t mesg); +static int device_i2c_resume(struct i2c_client *client); +static int __devinit device_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id); +static int __devexit device_i2c_remove(struct i2c_client *client);*/ +static int D10_i2c_read_xyz(struct i2c_client *client, int *xyz); +static int device_i2c_rxdata(struct i2c_client *client, unsigned char *rxDat, int length); +static int device_i2c_txdata(struct i2c_client *client, unsigned char *txData, int length); + +static int dmt_get_filter(struct i2c_client *client); +static int dmt_set_filter(struct i2c_client *client,int); +static int dmt_get_position(struct i2c_client *client); +static int dmt_set_position(struct i2c_client *client,int); +static int DMT_GetOpenStatus(struct i2c_client *client){ + struct dmt_data *dmt = i2c_get_clientdata(client); + GSE_LOG("start active=%d\n",dmt->active.counter); + wait_event_interruptible(dmt->open_wq, (atomic_read(&dmt->active) != 0)); + return 0; +} + +static int DMT_GetCloseStatus(struct i2c_client *client){ + struct dmt_data *dmt = i2c_get_clientdata(client); + GSE_LOG("start active=%d\n",dmt->active.counter); + wait_event_interruptible(dmt->open_wq, (atomic_read(&dmt->active) <= 0)); + return 0; +} + +static void DMT_sysfs_update_active_status(struct dmt_data *dmt , int en){ + unsigned long dmt_delay; + if(en){ + dmt_delay=msecs_to_jiffies(atomic_read(&dmt->delay)); + if(dmt_delay<1) + dmt_delay=1; + + GSE_LOG("schedule_delayed_work start with delay time=%lu\n",dmt_delay); + schedule_delayed_work(&dmt->delaywork,dmt_delay); + } + else + cancel_delayed_work_sync(&dmt->delaywork); +} + +static bool get_value_as_int(char const *buf, size_t size, int *value){ + long tmp; + if (size == 0) + return false; + /* maybe text format value */ + if ((buf[0] == '0') && (size > 1)) { + if ((buf[1] == 'x') || (buf[1] == 'X')) { + /* hexadecimal format */ + if (0 != strict_strtol(buf, 16, &tmp)) + return false; + } else { + /* octal format */ + if (0 != strict_strtol(buf, 8, &tmp)) + return false; + } + } else { + /* decimal format */ + if (0 != strict_strtol(buf, 10, &tmp)) + return false; + } + + if (tmp > INT_MAX) + return false; + + *value = tmp; + return true; +} +static bool get_value_as_int64(char const *buf, size_t size, long long *value) +{ + long long tmp; + if (size == 0) + return false; + /* maybe text format value */ + if ((buf[0] == '0') && (size > 1)) { + if ((buf[1] == 'x') || (buf[1] == 'X')) { + /* hexadecimal format */ + if (0 != strict_strtoll(buf, 16, &tmp)) + return false; + } else { + /* octal format */ + if (0 != strict_strtoll(buf, 8, &tmp)) + return false; + } + } else { + /* decimal format */ + if (0 != strict_strtoll(buf, 10, &tmp)) + return false; + } + + if (tmp > LLONG_MAX) + return false; + + *value = tmp; + return true; +} +/* sysfs enable show & store */ +static ssize_t dmt_sysfs_enable_show( + struct dmt_data *dmt, char *buf, int pos) +{ + char str[2][16]={"ACC enable OFF","ACC enable ON"}; + int flag; + flag=atomic_read(&dmt->enable); + return sprintf(buf, "%s\n", str[flag]); +} + +static ssize_t dmt_sysfs_enable_store( + struct dmt_data *dmt, char const *buf, size_t count, int pos) +{ + int en = 0; + if (NULL == buf) + return -EINVAL; + //GSE_LOG("buf=%x %x\n", buf[0], buf[1]); + if (0 == count) + return 0; + + if (false == get_value_as_int(buf, count, &en)) + return -EINVAL; + + en = en ? 1 : 0; + + atomic_set(&dmt->enable,en); + DMT_sysfs_update_active_status(dmt , en); + return count; +} + +static ssize_t dmt_enable_show(struct device *dev, struct device_attribute *attr, char *buf){ + return dmt_sysfs_enable_show( dev_get_drvdata(dev), buf, ACC_DATA_FLAG); +} + +static ssize_t dmt_enable_store( struct device *dev, struct device_attribute *attr, char const *buf, size_t count){ + return dmt_sysfs_enable_store( dev_get_drvdata(dev), buf, count, ACC_DATA_FLAG); +} + +/* sysfs delay show & store*/ +static ssize_t dmt_sysfs_delay_show( struct dmt_data *dmt, char *buf, int pos){ + return sprintf(buf, "%d\n", atomic_read(&dmt->delay)); +} + +static ssize_t dmt_sysfs_delay_store( struct dmt_data *dmt, char const *buf, size_t count, int pos){ + long long val = 0; + + if (NULL == buf) + return -EINVAL; + + if (0 == count) + return 0; + + if (false == get_value_as_int64(buf, count, &val)) + return -EINVAL; + + atomic_set(&dmt->delay, (unsigned int) val); + GSE_LOG("Driver attribute set delay =%lld\n", val); + + return count; +} + +static ssize_t dmt_delay_show( struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return dmt_sysfs_delay_show( dev_get_drvdata(dev), buf, ACC_DATA_FLAG); +} + +static ssize_t dmt_delay_store( struct device *dev, + struct device_attribute *attr, + char const *buf, + size_t count) +{ + return dmt_sysfs_delay_store( dev_get_drvdata(dev), buf, count, ACC_DATA_FLAG); +} +/* sysfs position show & store */ +static ssize_t dmt_position_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct input_dev *input = to_input_dev(dev); + struct dmt_data *dmt = input_get_drvdata(input); + + return sprintf(buf, "%d\n", dmt_get_position(dmt->client)); +} + +static ssize_t dmt_position_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct input_dev *input = to_input_dev(dev); + struct dmt_data *dmt = input_get_drvdata(input); + unsigned long position; + int ret; + + ret = strict_strtoul(buf, 10, &position); + if (ret < 0) + return count; + + dmt_set_position(dmt->client, position); + return count; +} +/* sysfs offset show & store */ +static ssize_t dmt_offset_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct input_dev *input = to_input_dev(dev); + struct dmt_data *dmt = input_get_drvdata(input); + return sprintf(buf, "( %d %d %d )\n", dmt->offset.u.x, dmt->offset.u.y, dmt->offset.u.z); +} + +static ssize_t dmt_offset_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct input_dev *input = to_input_dev(dev); + struct dmt_data *dmt = input_get_drvdata(input); + sscanf(buf, "%d %d %d", (int *)&dmt->offset.v[0], (int *)&dmt->offset.v[1], (int *)&dmt->offset.v[2]); + D10_write_offset_to_file(dmt->client); + update_var(); + return count; +} +/* sysfs filter show & store */ +static ssize_t dmt_filter_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct input_dev *input = to_input_dev(dev); + struct dmt_data *dmt = input_get_drvdata(input); + + return sprintf(buf, "%d\n", dmt_get_filter(dmt->client)); +} + +static ssize_t dmt_filter_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct input_dev *input = to_input_dev(dev); + struct dmt_data *dmt = input_get_drvdata(input); + unsigned long filter; + int ret; + + ret = strict_strtoul(buf, 10, &filter); + if (ret < 0) + return count; + + dmt_set_filter(dmt->client, filter); + return count; +} + +/* sysfs data show */ +static ssize_t dmt_acc_private_data_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct input_dev *input = to_input_dev(dev); + struct dmt_data *dmt = input_get_drvdata(input); + raw_data accel; + + mutex_lock(&dmt->data_mutex); + accel = dmt->last; + mutex_unlock(&dmt->data_mutex); + + return sprintf(buf, "( %d %d %d )\n", dmt->last.v[0], dmt->last.v[1], dmt->last.v[2]); +} +/* sysfs id show */ +static ssize_t dmt_id_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + char str[8]={GSENSOR_ID}; + return sprintf(buf, "%s\n", str); +} +/* sysfs debug_suspend show & store */ +#ifdef DMT_DEBUG_DATA +static ssize_t dmt_debug_suspend_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct input_dev *input = to_input_dev(dev); + struct dmt_data *dmt = input_get_drvdata(input); + int suspend = dmt->suspend; + + mutex_lock(&dmt->suspend_mutex); + suspend = sprintf(buf, "%d\n", dmt->suspend); + mutex_unlock(&dmt->suspend_mutex); + return suspend; +} + +static ssize_t dmt_debug_suspend_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct input_dev *input = to_input_dev(dev); + struct dmt_data *dmt = input_get_drvdata(input); + unsigned long suspend; + pm_message_t msg; + int ret; + + ret = strict_strtoul(buf, 10, &suspend); + if (ret < 0) + return count; + + memset(&msg, 0, sizeof(pm_message_t)); + + mutex_lock(&dmt->suspend_mutex); + + if (suspend) { + dmard10_suspend(dmt->pdevice, msg); + dmt->suspend = 1; + } else { + dmard10_resume(dmt->pdevice); + dmt->suspend = 0; + } + + mutex_unlock(&dmt->suspend_mutex); + + return count; +} +/* sysfs reg_read show & store */ +static ssize_t dmt_reg_read_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct dmt_data *dmt = dev_get_drvdata(dev); + int err; + unsigned char i2c[1]; + + i2c[0] = (unsigned char)atomic_read(&dmt->addr); + err = device_i2c_rxdata(dmt->client, i2c, 1); + if (err < 0) + return err; + + return sprintf(buf, "0x%02X\n", i2c[0]); +} + +static ssize_t dmt_reg_read_store(struct device *dev, + struct device_attribute *attr, + char const *buf, + size_t count) +{ + struct dmt_data *dmt = dev_get_drvdata(dev); + int addr = 0; + + if (NULL == buf) + return -EINVAL; + + if (0 == count) + return 0; + + if (false == get_value_as_int(buf, count, &addr)) + return -EINVAL; + + if (addr < 0 || 128 < addr) + return -EINVAL; + + atomic_set(&dmt->addr, addr); + + return 1; +} +#endif /* DEBUG */ +/********************************************************************* + * + * SysFS attribute functions + * + * directory : /sys/class/accelemeter/dmardXX/ + * files : + * - enable_acc [rw] [t] : enable flag for accelerometer + * - delay_acc [rw] [t] : delay in nanosecond for accelerometer + * - position [rw] [t] : chip mounting position + * - offset [rw] [t] : offset + * - data [r] [t] : raw data + * - id [r] [t] : chip id + * + * debug : + * - debug_suspend [w] [t] : suspend test + * - reg_read [rw] [t] : Read register + * - reg_write [rw] [t] : Weite register + * + * [rw]= read/write + * [r] = read only + * [w] = write only + * [b] = binary format + * [t] = text format + */ + +static struct device_attribute DMT_attributes[] = { + __ATTR(enable_acc, 0660, dmt_enable_show, dmt_enable_store), + __ATTR(delay_acc, 0660, dmt_delay_show, dmt_delay_store), + __ATTR(position, 0660, dmt_position_show, dmt_position_store), + __ATTR(offset, 0660, dmt_offset_show, dmt_offset_store), + __ATTR(filter, 0660, dmt_filter_show, dmt_filter_store), + __ATTR(data, 0660, dmt_acc_private_data_show, NULL), + __ATTR(id, 0660, dmt_id_show, NULL), +#ifdef DMT_DEBUG_DATA + __ATTR(debug_suspend, 0660, dmt_debug_suspend_show,dmt_debug_suspend_store), + __ATTR(reg_read, 0660, dmt_reg_read_show, dmt_reg_read_store), + __ATTR(reg_write, 0660, NULL, NULL), +#endif // DEBUG + __ATTR_NULL, +}; + +static char const *const ACCELEMETER_CLASS_NAME = "accelemeter"; +static char const *const GSENSOR_DEVICE_NAME = SENSOR_I2C_NAME; +static char const *const device_link_name = "i2c"; +static dev_t const dmt_device_dev_t = MKDEV(MISC_MAJOR, MISC_DYNAMIC_MINOR); + +// dmt sysfs functions +static int create_device_attributes(struct device *dev, struct device_attribute *attrs){ + int i; + int err = 0; + for (i = 0 ; NULL != attrs[i].attr.name ; ++i) { + err = device_create_file(dev, &attrs[i]); + if (0 != err) + break; + } + + if (0 != err) { + for (; i >= 0 ; --i) + device_remove_file(dev, &attrs[i]); + } + return err; +} + +static void remove_device_attributes( + struct device *dev, + struct device_attribute *attrs) +{ + int i; + + for (i = 0 ; NULL != attrs[i].attr.name ; ++i) + device_remove_file(dev, &attrs[i]); +} + +static int create_sysfs_interfaces(struct dmt_data *dmt) +{ + int err; + + if (NULL == dmt) + return -EINVAL; + + err = 0; + dmt->class = class_create(THIS_MODULE, ACCELEMETER_CLASS_NAME); + if (IS_ERR(dmt->class)) { + err = PTR_ERR(dmt->class); + goto exit_class_create_failed; + } + + dmt->class_dev = device_create( + dmt->class, + NULL, + dmt_device_dev_t, + dmt, + GSENSOR_DEVICE_NAME); + if (IS_ERR(dmt->class_dev)) { + err = PTR_ERR(dmt->class_dev); + goto exit_class_device_create_failed; + } + + err = sysfs_create_link( + &dmt->class_dev->kobj, + &dmt->client->dev.kobj, + device_link_name); + if (0 > err) + goto exit_sysfs_create_link_failed; + + err = create_device_attributes( + dmt->class_dev, + DMT_attributes); + if (0 > err) + goto exit_device_attributes_create_failed; +#if 0 + err = create_device_binary_attributes( + &dmt->class_dev->kobj, + dmt_bin_attributes); + if (0 > err) + goto exit_device_binary_attributes_create_failed; +#endif + + return err; + +#if 0 +exit_device_binary_attributes_create_failed: + remove_device_attributes(dmt->class_dev, dmt_attributes); +#endif +exit_device_attributes_create_failed: + sysfs_remove_link(&dmt->class_dev->kobj, device_link_name); +exit_sysfs_create_link_failed: + device_destroy(dmt->class, dmt_device_dev_t); +exit_class_device_create_failed: + dmt->class_dev = NULL; + class_destroy(dmt->class); +exit_class_create_failed: + dmt->class = NULL; + return err; +} + +static void remove_sysfs_interfaces(struct dmt_data *dmt){ + if (NULL == dmt) + return; + + if (NULL != dmt->class_dev) { + + remove_device_attributes( + dmt->class_dev, + DMT_attributes); + sysfs_remove_link( + &dmt->class_dev->kobj, + device_link_name); + dmt->class_dev = NULL; + } + if (NULL != dmt->class) { + device_destroy( + dmt->class, + dmt_device_dev_t); + class_destroy(dmt->class); + dmt->class = NULL; + } +} + +int D10_input_init(struct i2c_client *client){ + struct dmt_data *dmt = i2c_get_clientdata(client); + int err = 0; + dmt->input = input_allocate_device(); + if (!dmt->input){ + GSE_ERR("input device allocate ERROR !!\n"); + return -ENOMEM; + } + else + GSE_LOG("input device allocate Success !!\n"); + /* Setup input device */ + //dmt->input->name = SENSOR_I2C_NAME; + set_bit(EV_ABS, dmt->input->evbit); + /* Accelerometer [-78.5, 78.5]m/s2 in Q16 */ + input_set_abs_params(dmt->input, ABS_X, ABSMIN, ABSMAX, 0, 0); + input_set_abs_params(dmt->input, ABS_Y, ABSMIN, ABSMAX, 0, 0); + input_set_abs_params(dmt->input, ABS_Z, ABSMIN, ABSMAX, 0, 0); + /* Set InputDevice Name */ + dmt->input->name = INPUT_NAME_ACC; + /* Register */ + err = input_register_device(dmt->input); + if (err) { + GSE_ERR("input_register_device ERROR !!\n"); + input_free_device(dmt->input); + return err; + } + GSE_LOG("input_register_device SUCCESS %d !! \n",err); + + return err; +} + +int D10_calibrate(struct i2c_client *client) +{ + struct dmt_data *dmt = i2c_get_clientdata(client); + raw_data avg; + int i, j; + long xyz_acc[SENSOR_DATA_SIZE]; + int xyz[SENSOR_DATA_SIZE]; + /* initialize the offset value */ + for(i = 0; i < SENSOR_DATA_SIZE; ++i) + dmt->offset.v[i] = 0; + /* initialize the accumulation buffer */ + for(i = 0; i < SENSOR_DATA_SIZE; ++i) + xyz_acc[i] = 0; + + for(i = 0; i < AVG_NUM; i++) { + D10_i2c_read_xyz(client, (int *)&xyz); + for(j = 0; j < SENSOR_DATA_SIZE; ++j) + xyz_acc[j] += xyz[j]; + } + /* calculate averages */ + for(i = 0; i < SENSOR_DATA_SIZE; ++i) + avg.v[i] = xyz_acc[i] / AVG_NUM; + + if(avg.v[2] < 0){ + dmt->offset.u.x = avg.v[0] ; + dmt->offset.u.y = avg.v[1] ; + dmt->offset.u.z = avg.v[2] + DEFAULT_SENSITIVITY; + return CONFIG_GSEN_CALIBRATION_GRAVITY_ON_Z_POSITIVE; + } + else{ + dmt->offset.u.x = avg.v[0] ; + dmt->offset.u.y = avg.v[1] ; + dmt->offset.u.z = avg.v[2] - DEFAULT_SENSITIVITY; + return CONFIG_GSEN_CALIBRATION_GRAVITY_ON_Z_NEGATIVE; + } + return 0; +} + +int dmard10_init(struct i2c_client *client){ + unsigned char buffer[7], buffer2[2]; + /* 1. check D10 , VALUE_STADR = 0x55 , VALUE_STAINT = 0xAA */ + buffer[0] = REG_STADR; + buffer2[0] = REG_STAINT; + + device_i2c_rxdata(client, buffer, 2); + device_i2c_rxdata(client, buffer2, 2); + + if( buffer[0] == VALUE_STADR || buffer2[0] == VALUE_STAINT){ + GSE_LOG(" REG_STADR_VALUE = %d , REG_STAINT_VALUE = %d\n", buffer[0], buffer2[0]); + } + else{ + GSE_LOG(" REG_STADR_VALUE = %d , REG_STAINT_VALUE = %d \n", buffer[0], buffer2[0]); + return -1; + } + /* 2. Powerdown reset */ + buffer[0] = REG_PD; + buffer[1] = VALUE_PD_RST; + device_i2c_txdata(client, buffer, 2); + /* 3. ACTR => Standby mode => Download OTP to parameter reg => Standby mode => Reset data path => Standby mode */ + buffer[0] = REG_ACTR; + buffer[1] = MODE_Standby; + buffer[2] = MODE_ReadOTP; + buffer[3] = MODE_Standby; + buffer[4] = MODE_ResetDataPath; + buffer[5] = MODE_Standby; + device_i2c_txdata(client, buffer, 6); + /* 4. OSCA_EN = 1 ,TSTO = b'000(INT1 = normal, TEST0 = normal) */ + buffer[0] = REG_MISC2; + buffer[1] = VALUE_MISC2_OSCA_EN; + device_i2c_txdata(client, buffer, 2); + /* 5. AFEN = 1(AFE will powerdown after ADC) */ + buffer[0] = REG_AFEM; + buffer[1] = VALUE_AFEM_AFEN_Normal; + buffer[2] = VALUE_CKSEL_ODR_100_204; + buffer[3] = VALUE_INTC; + buffer[4] = VALUE_TAPNS_Ave_4; + buffer[5] = 0x00; // DLYC, no delay timing + buffer[6] = 0x07; // INTD=1 (push-pull), INTA=1 (active high), AUTOT=1 (enable T) + device_i2c_txdata(client, buffer, 7); + /* 6. write TCGYZ & TCGX */ + buffer[0] = REG_WDAL; // REG:0x01 + buffer[1] = 0x00; // set TC of Y,Z gain value + buffer[2] = 0x00; // set TC of X gain value + buffer[3] = 0x03; // Temperature coefficient of X,Y,Z gain + device_i2c_txdata(client, buffer, 4); + + buffer[0] = REG_ACTR; // REG:0x00 + buffer[1] = MODE_Standby; // Standby + buffer[2] = MODE_WriteOTPBuf; // WriteOTPBuf + buffer[3] = MODE_Standby; // Standby + device_i2c_txdata(client, buffer, 4); + //buffer[0] = REG_TCGYZ; + //device_i2c_rxdata(client, buffer, 2); + //GSE_LOG(" TCGYZ = %d, TCGX = %d \n", buffer[0], buffer[1]); + + /* 7. Activation mode */ + buffer[0] = REG_ACTR; + buffer[1] = MODE_Active; + device_i2c_txdata(client, buffer, 2); + return 0; +} + +void D10_set_offset(struct i2c_client *client, int val[3]){ + struct dmt_data *dmt = i2c_get_clientdata(client); + int i; + for(i = 0; i < SENSOR_DATA_SIZE; ++i) + dmt->offset.v[i] = val[i]; +} + +struct file_operations sensor_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = device_ioctl, + .open = device_open, + .release = device_close, +}; + +static struct miscdevice dmt_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = SENSOR_I2C_NAME, + .fops = &sensor_fops, +}; + +static int sensor_close_dev(struct i2c_client *client){ + char buffer[3]; + GSE_FUN(); + buffer[0] = REG_AFEM; + buffer[1] = 0x0f; + device_i2c_txdata(client,buffer, 2); + buffer[0] = REG_ACTR; + buffer[1] = MODE_Standby; + buffer[2] = MODE_Off; + device_i2c_txdata(client,buffer, 3); + return 0; +} + +static void dmard10_shutdown(struct platform_device *pdev) +{ + flush_delayed_work_sync(&s_dmt->delaywork); + DMT_sysfs_update_active_status(s_dmt , 0); +} + + +//static int device_i2c_suspend(struct i2c_client *client, pm_message_t mesg){ +static int dmard10_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct dmt_data *dmt = i2c_get_clientdata(s_dmt->client); + flush_delayed_work_sync(&dmt->delaywork); + DMT_sysfs_update_active_status(dmt , 0); + return sensor_close_dev(dmt->client); +} + +//static int device_i2c_resume(struct i2c_client *client){ +static int dmard10_resume(struct platform_device *pdev) +{ + struct dmt_data *dmt = i2c_get_clientdata(s_dmt->client); + int en = 1; + GSE_FUN(); + printk("dmt->enable=%d",dmt->enable); + dmard10_init(dmt->client); + atomic_set(&dmt->enable,en); + DMT_sysfs_update_active_status(dmt , en); + return 0; +} +/* +static int __devexit device_i2c_remove(struct i2c_client *client){ + return 0; +} + +static const struct i2c_device_id device_i2c_ids[] = { + { SENSOR_I2C_NAME, 0}, + { } +}; + +static struct i2c_driver device_i2c_driver = { + .driver = { + .owner = THIS_MODULE, + .name = SENSOR_I2C_NAME, + }, + .class = I2C_CLASS_HWMON, + .id_table = device_i2c_ids, + .probe = device_i2c_probe, + .remove = __devexit_p(device_i2c_remove), +#ifdef CONFIG_HAS_EARLYSUSPEND + .suspend = device_i2c_suspend, + .resume = device_i2c_resume, +#endif +}; +*/ +static int device_open(struct inode *inode, struct file *filp){ + return 0; +} + +static long device_ioctl(struct file *file, unsigned int cmd, unsigned long arg){ + //struct i2c_client *client = (struct i2c_client *)file->private_data; + //struct dmt_data *dmt = (struct dmt_data*)i2c_get_clientdata(client); + + int err = 0, ret = 0, i; + int intBuf[SENSOR_DATA_SIZE], xyz[SENSOR_DATA_SIZE]; + /* check type */ + if (_IOC_TYPE(cmd) != IOCTL_MAGIC) return -ENOTTY; + + /* check user space pointer is valid */ + if (_IOC_DIR(cmd) & _IOC_READ) + err = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd)); + else if (_IOC_DIR(cmd) & _IOC_WRITE) + err = !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd)); + if (err) return -EFAULT; + + switch(cmd) { + case SENSOR_RESET: + ret = dmard10_init(s_dmt->client); + return ret; + + case SENSOR_CALIBRATION: + /* get orientation info */ + //if(copy_from_user(&intBuf, (int*)arg, sizeof(intBuf))) return -EFAULT; + D10_calibrate(s_dmt->client); + GSE_LOG("Sensor_calibration:%d %d %d\n", s_dmt->offset.u.x, s_dmt->offset.u.y, s_dmt->offset.u.z); + /* save file */ + D10_write_offset_to_file(s_dmt->client); + update_var(); + + /* return the offset */ + for(i = 0; i < SENSOR_DATA_SIZE; ++i) + intBuf[i] = s_dmt->offset.v[i]; + + ret = copy_to_user((int *)arg, &intBuf, sizeof(intBuf)); + return ret; + + case SENSOR_GET_OFFSET: + /* get data from file */ + D10_read_offset_from_file(s_dmt->client); + for(i = 0; i < SENSOR_DATA_SIZE; ++i) + intBuf[i] = s_dmt->offset.v[i]; + + ret = copy_to_user((int *)arg, &intBuf, sizeof(intBuf)); + return ret; + + case SENSOR_SET_OFFSET: + ret = copy_from_user(&intBuf, (int *)arg, sizeof(intBuf)); + D10_set_offset(s_dmt->client , intBuf); + /* write into file */ + D10_write_offset_to_file(s_dmt->client); + update_var(); + return ret; + + case SENSOR_READ_ACCEL_XYZ: + D10_i2c_read_xyz(s_dmt->client, (int *)&xyz); + for(i = 0; i < SENSOR_DATA_SIZE; ++i) + intBuf[i] = xyz[i] - s_dmt->offset.v[i]; + + ret = copy_to_user((int*)arg, &intBuf, sizeof(intBuf)); + return ret; + + case SENSOR_SETYPR: + if(copy_from_user(&intBuf, (int*)arg, sizeof(intBuf))) { + GSE_LOG("%s: -EFAULT\n",__func__); + return -EFAULT; + } + input_report_abs(s_dmt->input, ABS_X, intBuf[0]); + input_report_abs(s_dmt->input, ABS_Y, intBuf[1]); + input_report_abs(s_dmt->input, ABS_Z, intBuf[2]); + input_sync(s_dmt->input); + GSE_LOG("SENSOR_SETYPR OK! x=%d,y=%d,z=%d\n",intBuf[0],intBuf[1],intBuf[2]); + return ret; + + case SENSOR_GET_OPEN_STATUS: + GSE_LOG("Going into DMT_GetOpenStatus()\n"); + ret = DMT_GetOpenStatus(s_dmt->client); + return ret; + + case SENSOR_GET_CLOSE_STATUS: + GSE_LOG("Going into DMT_GetCloseStatus()\n"); + ret = DMT_GetCloseStatus(s_dmt->client); + return ret; + + case SENSOR_GET_DELAY: + ret = copy_to_user((int*)arg, &interval, sizeof(interval)); + return ret; + + default: /* redundant, as cmd was checked against MAXNR */ + return -ENOTTY; + } + + return 0; +} + +static int device_close(struct inode *inode, struct file *filp){ + return 0; +} + +/***** I2C I/O function ***********************************************/ +static int device_i2c_rxdata( struct i2c_client *client, unsigned char *rxData, int length){ + struct i2c_msg msgs[] = { + {.addr = client->addr, .flags = 0, .len = 1, .buf = rxData,}, + {.addr = client->addr, .flags = I2C_M_RD, .len = length, .buf = rxData,}, + }; + //unsigned char addr = rxData[0]; + if (i2c_transfer(client->adapter, msgs, 2) < 0) { + dev_err(&client->dev, "%s: transfer failed.", __func__); + return -EIO; + } + //DMT_DATA(&client->dev, "RxData: len=%02x, addr=%02x, data=%02x\n", + //length, addr, rxData[0]); + + return 0; +} + +static int device_i2c_txdata( struct i2c_client *client, unsigned char *txData, int length){ + struct i2c_msg msg[] = { + {.addr = client->addr, .flags = 0, .len = length, .buf = txData,}, + }; + + if (i2c_transfer(client->adapter, msg, 1) < 0) { + dev_err(&client->dev, "%s: transfer failed.", __func__); + return -EIO; + } + //DMT_DATA(&client->dev, "TxData: len=%02x, addr=%02x data=%02x\n", + //length, txData[0], txData[1]); + return 0; +} + +static int D10_i2c_read_xyz(struct i2c_client *client, int *xyz_p){ + struct dmt_data *dmt = i2c_get_clientdata(client); + u8 buffer[11]; + s16 xyzTmp[SENSOR_DATA_SIZE]; + int pos = dmt->position; + int i, j , k; + /* get xyz high/low bytes, 0x12 */ + buffer[0] = REG_STADR; + /* Read acceleration data */ + if (device_i2c_rxdata(client, buffer, 10)!= 0) + for(i = 0; i < SENSOR_DATA_SIZE; ++i) + xyz_p[i] = 0; + else + for(i = 0; i < SENSOR_DATA_SIZE; ++i){ + xyz_p[i] = 0; + /* merge xyz high/low bytes & 1g = 128 becomes 1g = 1024 */ + mutex_lock(&dmt->data_mutex); + xyzTmp[i] = ((int16_t)((buffer[2*(i+1)+1] << 8)) | buffer[2*(i+1)] ) << 3; + mutex_unlock(&dmt->data_mutex); + } +#ifdef SW_FILTER + if( dmt->aveflag >= dmt->filter){ + for(i = 0; i < SENSOR_DATA_SIZE; ++i){ + dmt->sum[i] = dmt->sum[i] - dmt->bufferave[i][dmt->pointer] + xyzTmp[i]; + } + /* transfer to the default layout */ + for(i = 0; i < SENSOR_DATA_SIZE; ++i){ + for(j = 0; j < SENSOR_DATA_SIZE; j++) + xyz_p[i] += (int)(dmt->sum[j]/dmt->filter * dmt_position_map[pos][i][j]); + } + } + else{ + /* init dmt->sum */ + for(i = 0; i < SENSOR_DATA_SIZE; ++i) + dmt->sum[i] = xyzTmp[i]; +#endif + /* transfer to the default layout */ + for(i = 0; i < SENSOR_DATA_SIZE; ++i){ + for(j = 0; j < SENSOR_DATA_SIZE; j++){ + xyz_p[i] += (int)(xyzTmp[j] * dmt_position_map[pos][i][j]); + //GSE_LOG("%04d, %04d,%d \n", xyz_p[i], xyzTmp[j], dmt_position_map[pos][i][j]); + } + } + //GSE_LOG("xyz_p: %04d , %04d , %04d\n", xyz_p[0], xyz_p[1], xyz_p[2]); +#ifdef SW_FILTER + dmt->aveflag++; + } + /* init dmt->sum */ + for(i = 0; i < SENSOR_DATA_SIZE; ++i){ + dmt->sum[i] = 0; + } + dmt->pointer++; + dmt->pointer %= dmt->filter; + for(i = 0; i < SENSOR_DATA_SIZE; ++i){ + dmt->bufferave[i][dmt->pointer] = xyzTmp[i]; + } + for(i = 0; i < SENSOR_DATA_SIZE; ++i){ + for(k = 0; k < dmt->filter; ++k){ + dmt->sum[i] += dmt->bufferave[i][k]; + } + } +#endif + return 0; +} + +static void DMT_work_func(struct work_struct *delaywork){ + struct dmt_data *dmt = container_of(delaywork, struct dmt_data, delaywork.work); + int i; + //static bool firsttime=true; + raw_data xyz; + unsigned long dmt_delay = msecs_to_jiffies(atomic_read(&dmt->delay)); + + + D10_i2c_read_xyz(dmt->client, (int *)&xyz.v); + /* dmt->last = RawData - Offset */ + for(i = 0; i < SENSOR_DATA_SIZE; ++i) + dmt->last.v[i] = xyz.v[i] - dmt->offset.v[i]; + //GSE_LOG("@DMTRaw @ X/Y/Z axis: %04d , %04d , %04d\n", xyz.v[0], xyz.v[1], xyz.v[2]); + //GSE_LOG("@Offset @ X/Y/Z axis: %04d , %04d , %04d\n", dmt->offset.u.x, dmt->offset.u.y, dmt->offset.u.z); + //GSE_LOG("@Raw-Offset@ X/Y/Z axis: %04d , %04d , %04d ,dmt_delay=%d\n", dmt->last.u.x, dmt->last.u.y, dmt->last.u.z, atomic_read(&dmt->delay)); + + + input_report_abs(dmt->input, ABS_X, dmt->last.v[l_sensorconfig.xyz_axis[0][0]]*l_sensorconfig.xyz_axis[0][1]);//dmt->last.v[0]); + input_report_abs(dmt->input, ABS_Y, dmt->last.v[l_sensorconfig.xyz_axis[1][0]]*l_sensorconfig.xyz_axis[1][1]);//dmt->last.v[1]); + input_report_abs(dmt->input, ABS_Z, dmt->last.v[l_sensorconfig.xyz_axis[2][0]]*l_sensorconfig.xyz_axis[2][1]);//dmt->last.v[2]); + input_sync(dmt->input); + + if(dmt_delay < 1) + dmt_delay = 1; + schedule_delayed_work(&dmt->delaywork, dmt_delay); +} + +static int mma10_open(struct inode *node, struct file *fle) +{ + GSE_LOG("open...\n"); + return 0; +} + +static int mma10_close(struct inode *node, struct file *fle) +{ + GSE_LOG("close...\n"); + return 0; +} + +static long mma10_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + int err = 0; + //unsigned char data[6]; + void __user *argp = (void __user *)arg; + short delay, enable; + unsigned int uval = 0; + + + /* cmd mapping */ + switch(cmd) + { + case ECS_IOCTL_APP_SET_DELAY: + // set the rate of g-sensor + if (copy_from_user(&delay, argp, sizeof(short))) + { + printk(KERN_ALERT "Can't get set delay!!!\n"); + return -EFAULT; + } + klog("Get delay=%d\n", delay); + + if ((delay >=0) && (delay < 20)) + { + delay = 20; + } else if (delay > 200) + { + delay = 200; + } + l_sensorconfig.sensor_samp = 1000/delay; + atomic_set(&s_dmt->delay, 1000/delay); + break; + case ECS_IOCTL_APP_SET_AFLAG: + // enable/disable sensor + if (copy_from_user(&enable, argp, sizeof(short))) + { + printk(KERN_ERR "Can't get enable flag!!!\n"); + return -EFAULT; + } + klog("enable=%d\n",enable); + if ((enable >=0) && (enable <=1)) + { + //KMSGINF("driver: disable/enable(%d) gsensor.\n", enable); + + l_sensorconfig.sensor_enable = enable; + atomic_set(&s_dmt->enable,enable); + DMT_sysfs_update_active_status(s_dmt , enable); + + } else { + printk(KERN_ERR "Wrong enable argument in %s !!!\n", __FUNCTION__); + return -EINVAL; + } + break; + case WMT_IOCTL_SENSOR_GET_DRVID: + uval = DMARD10_DRVID; + if (copy_to_user((unsigned int*)arg, &uval, sizeof(unsigned int))) + { + return -EFAULT; + } + GSE_LOG("dmard10_driver_id:%d\n",uval); + break; + case WMT_IOCTL_SENOR_GET_RESOLUTION: + uval = (10<<8) | 1; + if (copy_to_user((unsigned int *)arg, &uval, sizeof(unsigned int))) + { + return -EFAULT; + } + printk("<<<<<< 0) + { + if (sample != l_sensorconfig.sensor_samp) + { + // should do sth + } + //printk(KERN_ALERT "sensor samp=%d(amsr:%d) has been set.\n", sample, amsr); + } else { + klog("Wrong sample argumnet of sensor.\n"); + } + } else if (sscanf(buffer, "enable=%d\n", &enable)) + { + if ((enable < 0) || (enable > 1)) + { + klog("The argument to enable/disable g-sensor should be 0 or 1 !!!\n"); + } else if (enable != l_sensorconfig.sensor_enable) + { + //mma_enable_disable(enable); + l_sensorconfig.sensor_enable = enable; + } + } else if (sscanf(buffer, "sensor_test=%d\n", &test)) + { // for test begin + l_sensorconfig.test_pass = 0; + atomic_set(&s_dmt->enable,1); + DMT_sysfs_update_active_status(s_dmt , 1); + } else if (sscanf(buffer, "sensor_testend=%d\n", &test)) + { // Don nothing only to be compatible the before testing program + atomic_set(&s_dmt->enable,0); + DMT_sysfs_update_active_status(s_dmt , 0); + } + //mutex_unlock(&sense_data_mutex); + return count; +} + +static int sensor_readproc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len = 0; + + len = sprintf(page, + "test_pass=%d\nisdbg=%d\nrate=%d\nenable=%d\n", + l_sensorconfig.test_pass, + l_sensorconfig.isdbg, + l_sensorconfig.sensor_samp, + l_sensorconfig.sensor_enable + ); + return len; +} + + +//static int __devinit device_i2c_probe(struct i2c_client *client,const struct i2c_device_id *id){ +static int __devinit dmard10_probe(struct platform_device *pdev) +{ + int i, k, ret = 0; + //struct dmt_data *s_dmt = i2c_get_clientdata(client); + //struct dmt_data *s_dmt; + GSE_FUN(); +/* + if(!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)){ + GSE_ERR("check_functionality failed.\n"); + ret = -ENODEV; + goto exit0; + } + + // Allocate memory for driver data + s_dmt = kzalloc(sizeof(struct dmt_data), GFP_KERNEL); + memset(s_dmt, 0, sizeof(struct dmt_data)); + if (s_dmt == NULL) { + GSE_ERR("alloc data failed.\n"); + ret = -ENOMEM; + goto exit1; + } +*/ + /*for(i = 0; i < SENSOR_DATA_SIZE; ++i) + s_dmt->offset.v[i] = 0;*/ +#ifdef SW_FILTER + s_dmt->pointer = 0; + s_dmt->aveflag = 0; + for(i = 0; i < SENSOR_DATA_SIZE; ++i){ + s_dmt->sum[i] = 0; + for(k = 0; k < SENSOR_DATA_AVG; ++k){ + s_dmt->bufferave[i][k] = 0; + } + } + s_dmt->filter = SENSOR_DATA_AVG; + GSE_LOG("D10_DEFAULT_FILTER: %d\n", s_dmt->filter); +#endif + /* I2C initialization */ + //s_dmt->client = client; + + /* set client data */ + i2c_set_clientdata(s_dmt->client, s_dmt); + /*ret = dmard10_init(client); + if (ret < 0) + goto exit2; + */ + /* input */ + ret = D10_input_init(s_dmt->client); + if (ret){ + GSE_ERR("D10_input_init fail, error code= %d\n",ret); + goto exit3; + } + + /* initialize variables in dmt_data */ + mutex_init(&s_dmt->data_mutex); + mutex_init(&s_dmt->enable_mutex); +#ifdef DMT_DEBUG_DATA + mutex_init(&s_dmt->suspend_mutex); +#endif + init_waitqueue_head(&s_dmt->open_wq); + atomic_set(&s_dmt->active, 0); + atomic_set(&s_dmt->enable, 0); + atomic_set(&s_dmt->delay, 0); + atomic_set(&s_dmt->addr, 0); + /* DMT Acceleration Sensor Mounting Position on Board */ + s_dmt->position = D10_DEFAULT_POSITION; + GSE_LOG("D10_DEFAULT_POSITION: %d\n", s_dmt->position); + //s_dmt->position = (CONFIG_INPUT_DMT_ACCELEROMETER_POSITION); + //GSE_LOG("CONFIG_INPUT_DMT_ACCELEROMETER_POSITION: %d\n", s_dmt->position); + /* Misc device */ + if (misc_register(&dmt_device) < 0){ + GSE_ERR("dmt_dev register failed"); + goto exit4; + } + + /* Setup sysfs */ + if (create_sysfs_interfaces(s_dmt) < 0){ + GSE_ERR("create sysfs failed."); + goto exit5; + } +#ifdef CONFIG_HAS_EARLYSUSPEND + s_dmt->early_suspend.suspend = device_i2c_suspend; + s_dmt->early_suspend.resume = device_i2c_resume; + register_early_suspend(&s_dmt->early_suspend); +#endif + /* Setup driver interface */ + INIT_DELAYED_WORK(&s_dmt->delaywork, DMT_work_func); + GSE_LOG("DMT: INIT_DELAYED_WORK\n"); + + //register ctrl dev + ret = misc_register(&d10_device); + if (ret !=0) { + errlog("Can't register d10_device!\n"); + return -1; + } + // register rd/wr proc + l_sensorconfig.sensor_proc = create_proc_entry(GSENSOR_PROC_NAME, 0666, NULL/*&proc_root*/); + if (l_sensorconfig.sensor_proc != NULL) + { + l_sensorconfig.sensor_proc->write_proc = sensor_writeproc; + l_sensorconfig.sensor_proc->read_proc = sensor_readproc; + } + + //create offset file after factory reset + D10_read_offset_from_file(s_dmt->client); + + return 0; + +exit5: + misc_deregister(&dmt_device); +exit4: + input_unregister_device(s_dmt->input); +exit3: + kfree(s_dmt); +/*exit2: +exit1: +exit0:*/ + return ret; +} +/* +static struct i2c_board_info dmard10_board_info={ + .type = SENSOR_I2C_NAME, + .addr = SENSOR_I2C_ADDR, +}; +*/ +//static struct i2c_client *client; + +extern int wmt_getsyspara(char *varname, unsigned char *varval, int *varlen); +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.dm10sensor", varbuf, &varlen)) { + errlog("Can't get gsensor config in u-boot!!!!\n"); + return -1; //open it for no env just,not insmod such module 2014-6-30 + } else { + n = sscanf(varbuf, "%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d", + &l_sensorconfig.op, + &l_sensorconfig.int_gpio, + &l_sensorconfig.samp, + &(l_sensorconfig.xyz_axis[0][0]), + &(l_sensorconfig.xyz_axis[0][1]), + &(l_sensorconfig.xyz_axis[1][0]), + &(l_sensorconfig.xyz_axis[1][1]), + &(l_sensorconfig.xyz_axis[2][0]), + &(l_sensorconfig.xyz_axis[2][1]), + &(l_sensorconfig.offset[0]), + &(l_sensorconfig.offset[1]), + &(l_sensorconfig.offset[2]) + ); + if (n != 12) { + errlog("gsensor format is error in u-boot!!!\n"); + return -1; + } + l_sensorconfig.sensor_samp = l_sensorconfig.samp; + + dbg("get the sensor config: %d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d\n", + l_sensorconfig.op, + l_sensorconfig.int_gpio, + l_sensorconfig.samp, + l_sensorconfig.xyz_axis[0][0], + l_sensorconfig.xyz_axis[0][1], + l_sensorconfig.xyz_axis[1][0], + l_sensorconfig.xyz_axis[1][1], + l_sensorconfig.xyz_axis[2][0], + l_sensorconfig.xyz_axis[2][1], + l_sensorconfig.offset[0], + l_sensorconfig.offset[1], + l_sensorconfig.offset[2] + ); + } + return 0; +} + +static void dmard10_platform_release(struct device *device) +{ + GSE_LOG("...\n"); + return; +} + +static int __devexit dmard10_remove(struct platform_device *pdev) +{ + if (l_sensorconfig.sensor_proc != NULL) + { + remove_proc_entry(GSENSOR_PROC_NAME, NULL); + l_sensorconfig.sensor_proc = NULL; + } + //misc_deregister(&d10_device); + return 0; +} + + +static struct platform_device dmard10_device = { + .name = SENSOR_I2C_NAME, + .id = 0, + .dev = { + .release = dmard10_platform_release, + }, +}; + +static struct platform_driver dmard10_driver = { + .probe = dmard10_probe, + .remove = dmard10_remove, + .shutdown = dmard10_shutdown, + .suspend = dmard10_suspend, + .resume = dmard10_resume, + .driver = { + .name = SENSOR_I2C_NAME, + }, +}; + + +static int __init device_init(void){ + //struct device *device; + struct i2c_client *this_client; + int ret = 0; + + // parse g-sensor u-boot arg + ret = get_axisset(); + if (ret < 0) + { + printk("<<<<<%s user choose to no sensor chip!\n", __func__); + return ret; + } + GSE_LOG("D10 gsensor driver: initialize.\n"); + + if (!(this_client = sensor_i2c_register_device(0, SENSOR_I2C_ADDR, SENSOR_I2C_NAME))) + { + printk(KERN_ERR"Can't register gsensor i2c device!\n"); + return -1; + } + + if (dmard10_init(this_client)) + { + GSE_ERR("Failed to init dmard10!\n"); + sensor_i2c_unregister_device(this_client); + return -1; + } + + /* Allocate memory for driver data */ + s_dmt = kzalloc(sizeof(struct dmt_data), GFP_KERNEL); + //memset(s_dmt, 0, sizeof(struct dmt_data)); + if (s_dmt == NULL) { + GSE_ERR("alloc data failed.\n"); + return -ENOMEM; + } + + s_dmt->client = this_client; + s_dmt->pdevice = &dmard10_device; + s_dmt->offset.u.x = l_sensorconfig.offset[0]; + s_dmt->offset.u.y = l_sensorconfig.offset[1]; + s_dmt->offset.u.z = l_sensorconfig.offset[2]; + + + // create the platform device + l_dev_class = class_create(THIS_MODULE, SENSOR_I2C_NAME); + if (IS_ERR(l_dev_class)){ + ret = PTR_ERR(l_dev_class); + printk(KERN_ERR "Can't class_create gsensor device !!\n"); + return ret; + } + if((ret = platform_device_register(&dmard10_device))) + { + GSE_ERR("Can't register dmard10 platform devcie!!!\n"); + return ret; + } + if ((ret = platform_driver_register(&dmard10_driver)) != 0) + { + GSE_ERR("Can't register dmard10 platform driver!!!\n"); + return ret; + } + + return 0; +} + +static void __exit device_exit(void){ + //i2c_unregister_device(client); + //i2c_del_driver(&device_i2c_driver); + GSE_LOG("D10 gsensor driver: release.\n"); + + flush_delayed_work_sync(&s_dmt->delaywork); + cancel_delayed_work_sync(&s_dmt->delaywork); + + input_unregister_device(s_dmt->input); + input_free_device(s_dmt->input); + misc_deregister(&dmt_device); + misc_deregister(&d10_device); + platform_driver_unregister(&dmard10_driver); + platform_device_unregister(&dmard10_device); + sensor_i2c_unregister_device(s_dmt->client); + class_destroy(l_dev_class); + + remove_sysfs_interfaces(s_dmt); + kfree(s_dmt); +} + +static int dmt_get_filter(struct i2c_client *client){ + struct dmt_data *dmt = i2c_get_clientdata(client); + return dmt->filter; +} + +static int dmt_set_filter(struct i2c_client *client, int filter){ + struct dmt_data *dmt = i2c_get_clientdata(client); + if (!((filter >= 1) && (filter <= 32))) + return -1; + dmt->filter = filter; + return 0; +} + +static int dmt_get_position(struct i2c_client *client){ + struct dmt_data *dmt = i2c_get_clientdata(client); + return dmt->position; +} + +static int dmt_set_position(struct i2c_client *client, int position){ + struct dmt_data *dmt = i2c_get_clientdata(client); + if (!((position >= 0) && (position <= 7))) + return -1; + dmt->position = position; + return 0; +} + +extern int wmt_setsyspara(char *varname, char *varval); +static void update_var(void) +{ + char varbuf[64]; + int varlen; + + memset(varbuf, 0, sizeof(varbuf)); + varlen = sizeof(varbuf); + + sprintf(varbuf, "%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d", + l_sensorconfig.op, + l_sensorconfig.int_gpio, + l_sensorconfig.samp, + l_sensorconfig.xyz_axis[0][0], + l_sensorconfig.xyz_axis[0][1], + l_sensorconfig.xyz_axis[1][0], + l_sensorconfig.xyz_axis[1][1], + l_sensorconfig.xyz_axis[2][0], + l_sensorconfig.xyz_axis[2][1], + s_dmt->offset.u.x, + s_dmt->offset.u.y, + s_dmt->offset.u.z + ); + + wmt_setsyspara("wmt.io.dm10sensor",varbuf); +} + +static int D10_write_offset_to_file(struct i2c_client *client){ + struct dmt_data *dmt = i2c_get_clientdata(client); + char r_buf[18] = {0}; + char w_buf[18] = {0}; + //unsigned int orgfs; + struct file *fp; + mm_segment_t fs; + ssize_t ret; + //int8_t i; + + sprintf(w_buf,"%5d %5d %5d", dmt->offset.u.x, dmt->offset.u.y, dmt->offset.u.z); + /* Set segment descriptor associated to kernel space */ + fp = filp_open(D10_OffsetFileName, O_RDWR | O_CREAT, 0777); + if(IS_ERR(fp)){ + GSE_ERR("filp_open %s error!!.\n",D10_OffsetFileName); + return -1; + } + else{ + fs = get_fs(); + //set_fs(KERNEL_DS); + set_fs(get_ds()); + GSE_LOG("filp_open %s SUCCESS!!.\n",D10_OffsetFileName); + //fp->f_op->write(fp,data,18, &fp->f_pos); + //filp_close(fp,NULL); + ret = fp->f_op->write(fp,w_buf,18,&fp->f_pos); + if(ret != 18) + { + printk(KERN_ERR "%s: write error!\n", __func__); + filp_close(fp,NULL); + return -EIO; + } + //fp->f_pos=0x00; + ret = fp->f_op->read(fp,r_buf, 18,&fp->f_pos); + if(ret < 0) + { + printk(KERN_ERR "%s: read error!\n", __func__); + filp_close(fp,NULL); + return -EIO; + } + set_fs(fs); + + // + //printk(KERN_INFO "%s: read ret=%d!", __func__, ret); + /* for(i=0; i<18 ;i++) + { + if(r_buf[i] != w_buf[i]) + { + printk(KERN_ERR "%s: read back error, r_buf[%x](0x%x) != w_buf[%x](0x%x)\n", + __func__, i, r_buf[i], i, w_buf[i]); + filp_close(fp,NULL); + return -EIO; + } + } + */ + + } + filp_close(fp,NULL); + return 0; +} + +void D10_read_offset_from_file(struct i2c_client *client){ + struct dmt_data *dmt = i2c_get_clientdata(client); + unsigned int orgfs; + char data[18]; + struct file *fp; + int ux,uy,uz; + orgfs = get_fs(); + /* Set segment descriptor associated to kernel space */ + set_fs(KERNEL_DS); + + fp = filp_open(D10_OffsetFileName, O_RDWR , 0); + GSE_FUN(); + if(IS_ERR(fp)){ + GSE_ERR("Sorry,file open ERROR !\n"); + if(l_sensorconfig.op){ //first time + l_sensorconfig.op=0; +#if AUTO_CALIBRATION + /* get acceleration average reading */ + D10_calibrate(client); + update_var(); + D10_write_offset_to_file(client); +#endif +#ifdef DMT_BROADCAST_APK_ENABLE + create_devidfile(); + return; +#endif + } + D10_write_offset_to_file(client); + } + else{ + GSE_LOG("filp_open %s SUCCESS!!.\n",D10_OffsetFileName); + fp->f_op->read(fp,data,18, &fp->f_pos); + GSE_LOG("filp_read result %s\n",data); + sscanf(data,"%d %d %d",&ux,&uy,&uz); + dmt->offset.u.x=ux; + dmt->offset.u.y=uy; + dmt->offset.u.z=uz; + } + set_fs(orgfs); +} +static int create_devidfile(void) +{ + char data[18]; + unsigned int orgfs; + struct file *fp; + + sprintf(data,"%5d %5d %5d",0,0,0); + orgfs = get_fs(); + /* Set segment descriptor associated to kernel space */ + set_fs(KERNEL_DS); + GSE_FUN(); + fp = filp_open(DmtXXFileName, O_RDWR | O_CREAT, 0777); + if(IS_ERR(fp)){ + GSE_ERR("Sorry,file open ERROR !\n"); + return -1; + } + fp->f_op->write(fp,data,18, &fp->f_pos); + set_fs(orgfs); + filp_close(fp,NULL); + return 0; +} +//********************************************************************************************************* +MODULE_AUTHOR("DMT_RD"); +MODULE_DESCRIPTION("DMT Gsensor Driver"); +MODULE_LICENSE("GPL"); + +module_init(device_init); +module_exit(device_exit); diff --git a/drivers/input/sensor/dmard10_gsensor/dmt10.h b/drivers/input/sensor/dmard10_gsensor/dmt10.h new file mode 100755 index 00000000..f77b07e3 --- /dev/null +++ b/drivers/input/sensor/dmard10_gsensor/dmt10.h @@ -0,0 +1,192 @@ +/* @version 1.03 + * Copyright 2011 Domintech Technology Co., Ltd + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +#ifndef DMT10_H +#define DMT10_H +#include +#include +#include +#include +#include +#include +#include +#include +//#include +#define AUTO_CALIBRATION 0 +#define SW_FILTER /* Enable or Disable Software filter */ +#define SENSOR_DATA_AVG 8 /* AVG sensor data */ + +//#define DMT_DEBUG_DATA +#define GSE_TAG "[DMT_Gsensor]" +#ifdef DMT_DEBUG_DATA +#define GSE_ERR(fmt, args...) printk(KERN_ERR GSE_TAG"%s %d : "fmt, __FUNCTION__, __LINE__, ##args) +#define GSE_LOG(fmt, args...) printk(KERN_INFO GSE_TAG fmt, ##args) +#define GSE_FUN(f) printk(KERN_INFO GSE_TAG" %s: %s: %i\n", __FILE__, __func__, __LINE__) +#define DMT_DATA(dev, ...) dev_dbg((dev), ##__VA_ARGS__) +#else +#define GSE_ERR(fmt, args...) +#define GSE_LOG(fmt, args...) +#define GSE_FUN(f) +#define DMT_DATA(dev, format, ...) +#endif + +#define GSENSOR_ID "DMARD10" +#define INPUT_NAME_ACC "g-sensor"//"DMT_accel"//"g-sensor"// /* Input Device Name */ +#define SENSOR_I2C_NAME "dmard10"//"dmt"// /* Device name for DMARD10 misc. device */ +#define SENSOR_I2C_ADDR 0x18 +#define REG_ACTR 0x00 +#define REG_WDAL 0x01 +#define REG_TAPNS 0x0f +#define REG_MISC2 0x1f +#define REG_AFEM 0x0c +#define REG_CKSEL 0x0d +#define REG_INTC 0x0e +#define REG_STADR 0x12 +#define REG_STAINT 0x1C +#define REG_PD 0x21 +#define REG_TCGYZ 0x26 +#define REG_X_OUT 0x41 + +#define MODE_Off 0x00 +#define MODE_ResetAtOff 0x01 +#define MODE_Standby 0x02 +#define MODE_ResetAtStandby 0x03 +#define MODE_Active 0x06 +#define MODE_Trigger 0x0a +#define MODE_ReadOTP 0x12 +#define MODE_WriteOTP 0x22 +#define MODE_WriteOTPBuf 0x42 +#define MODE_ResetDataPath 0x82 + +#define VALUE_STADR 0x55 +#define VALUE_STAINT 0xAA +#define VALUE_AFEM_AFEN_Normal 0x8f// AFEN set 1 , ATM[2:0]=b'000(normal),EN_Z/Y/X/T=1 +#define VALUE_AFEM_Normal 0x0f// AFEN set 0 , ATM[2:0]=b'000(normal),EN_Z/Y/X/T=1 +#define VALUE_INTC 0x00// INTC[6:5]=b'00 +#define VALUE_INTC_Interrupt_En 0x20// INTC[6:5]=b'01 (Data ready interrupt enable, active high at INT0) +#define VALUE_CKSEL_ODR_0_204 0x04// ODR[3:0]=b'0000 (0.78125Hz), CCK[3:0]=b'0100 (204.8kHZ) +#define VALUE_CKSEL_ODR_1_204 0x14// ODR[3:0]=b'0001 (1.5625Hz), CCK[3:0]=b'0100 (204.8kHZ) +#define VALUE_CKSEL_ODR_3_204 0x24// ODR[3:0]=b'0010 (3.125Hz), CCK[3:0]=b'0100 (204.8kHZ) +#define VALUE_CKSEL_ODR_6_204 0x34// ODR[3:0]=b'0011 (6.25Hz), CCK[3:0]=b'0100 (204.8kHZ) +#define VALUE_CKSEL_ODR_12_204 0x44// ODR[3:0]=b'0100 (12.5Hz), CCK[3:0]=b'0100 (204.8kHZ) +#define VALUE_CKSEL_ODR_25_204 0x54// ODR[3:0]=b'0101 (25Hz), CCK[3:0]=b'0100 (204.8kHZ) +#define VALUE_CKSEL_ODR_50_204 0x64// ODR[3:0]=b'0110 (50Hz), CCK[3:0]=b'0100 (204.8kHZ) +#define VALUE_CKSEL_ODR_100_204 0x74// ODR[3:0]=b'0111 (100Hz), CCK[3:0]=b'0100 (204.8kHZ) + +#define VALUE_TAPNS_NoFilter 0x00 // TAP1/TAP2 NO FILTER +#define VALUE_TAPNS_Ave_2 0x11 // TAP1/TAP2 Average 2 +#define VALUE_TAPNS_Ave_4 0x22 // TAP1/TAP2 Average 4 +#define VALUE_TAPNS_Ave_8 0x33 // TAP1/TAP2 Average 8 +#define VALUE_TAPNS_Ave_16 0x44 // TAP1/TAP2 Average 16 +#define VALUE_TAPNS_Ave_32 0x55 // TAP1/TAP2 Average 32 +#define VALUE_MISC2_OSCA_EN 0x08 +#define VALUE_PD_RST 0x52 + +#define CONFIG_GSEN_CALIBRATION_GRAVITY_ON_Z_NEGATIVE 1 +#define CONFIG_GSEN_CALIBRATION_GRAVITY_ON_Z_POSITIVE 2 +#define CONFIG_GSEN_CALIBRATION_GRAVITY_ON_Y_NEGATIVE 3 +#define CONFIG_GSEN_CALIBRATION_GRAVITY_ON_Y_POSITIVE 4 +#define CONFIG_GSEN_CALIBRATION_GRAVITY_ON_X_NEGATIVE 5 +#define CONFIG_GSEN_CALIBRATION_GRAVITY_ON_X_POSITIVE 6 + +#define AVG_NUM 16 +#define SENSOR_DATA_SIZE 3 +#define DEFAULT_SENSITIVITY 1024 + +#define IOCTL_MAGIC 0x09 +#define SENSOR_RESET _IO(IOCTL_MAGIC, 0) +#define SENSOR_CALIBRATION _IOWR(IOCTL_MAGIC, 1, int[SENSOR_DATA_SIZE]) +#define SENSOR_GET_OFFSET _IOR(IOCTL_MAGIC, 2, int[SENSOR_DATA_SIZE]) +#define SENSOR_SET_OFFSET _IOWR(IOCTL_MAGIC, 3, int[SENSOR_DATA_SIZE]) +#define SENSOR_READ_ACCEL_XYZ _IOR(IOCTL_MAGIC, 4, int[SENSOR_DATA_SIZE]) +#define SENSOR_SETYPR _IOW(IOCTL_MAGIC, 5, int[SENSOR_DATA_SIZE]) +#define SENSOR_GET_OPEN_STATUS _IO(IOCTL_MAGIC, 6) +#define SENSOR_GET_CLOSE_STATUS _IO(IOCTL_MAGIC, 7) +#define SENSOR_GET_DELAY _IOR(IOCTL_MAGIC, 8, unsigned int*) +#define SENSOR_MAXNR 8 +/* Default sensorlayout parameters */ +#define D10_DEFAULT_POSITION 6 + +/* Transformation matrix for chip mounting position */ +static const int dmt_position_map[][3][3] = { + { { 1, 0, 0}, { 0,-1, 0}, { 0, 0,-1}, }, /* top/upper-left */ + { { 0, 1, 0}, { 1, 0, 0}, { 0, 0,-1}, }, /* top/lower-left */ + { {-1, 0, 0}, { 0, 1, 0}, { 0, 0,-1}, }, /* top/lower-right */ + { { 0,-1, 0}, {-1, 0, 0}, { 0, 0,-1}, }, /* top/upper-right */ + { {-1, 0, 0}, { 0,-1, 0}, { 0, 0, 1}, }, /* bottom/upper-right*/ + { { 0,-1, 0}, {-1, 0, 0}, { 0, 0, 1}, }, /* bottom/upper-left */ + { { 1, 0, 0}, { 0, 1, 0}, { 0, 0, 1}, }, /* bottom/lower-right*/ + { { 0, 1,0}, { 1, 0, 0}, { 0, 0, 1}, }, /* bottom/lower-left */ +}; + +typedef union { + struct { + int x; + int y; + int z; + } u; + int v[SENSOR_DATA_SIZE]; +} raw_data; + +struct dmt_data { + struct platform_device *pdevice; + struct device *class_dev; + struct class *class; + struct input_dev *input; + struct i2c_client *client; +#ifdef CONFIG_HAS_EARLYSUSPEND + struct early_suspend early_suspend; +#endif + struct delayed_work delaywork; + struct work_struct work; + struct mutex data_mutex; + struct mutex enable_mutex; /* for suspend */ + raw_data last; /* RawData */ + raw_data offset; /* Offset */ +#ifdef SW_FILTER + int sum[SENSOR_DATA_SIZE]; /* SW_FILTER sum */ + int bufferave[3][32]; + s8 aveflag; /* FULL bufferave[][] */ + s8 pointer; /* last update data */ +#endif + wait_queue_head_t open_wq; + atomic_t active; + atomic_t delay; + atomic_t enable; + int filter; + int position; /* must int type ,for Kconfig setup */ + atomic_t addr; +#ifdef DMT_DEBUG_DATA + struct mutex suspend_mutex; + int suspend; +#endif +}; + +#define ACC_DATA_FLAG 0 +#define MAG_DATA_FLAG 1 +#define ORI_DATA_FLAG 2 +#define DMT_NUM_SENSORS 3 + +/* ABS axes parameter range [um/s^2] (for input event) */ +#define GRAVITY_EARTH 9806550 +#define ABSMAX (GRAVITY_EARTH * 2) +#define ABSMIN (-GRAVITY_EARTH * 2) + +#endif diff --git a/drivers/input/sensor/isl29023_lsensor/Makefile b/drivers/input/sensor/isl29023_lsensor/Makefile new file mode 100755 index 00000000..ac959091 --- /dev/null +++ b/drivers/input/sensor/isl29023_lsensor/Makefile @@ -0,0 +1,34 @@ +KERNELDIR=../../../../ +#KERNELDIR=/home/hangyan/android8850/kernel/ANDROID_3.0.8 +CROSS = arm_1103_le- +CC= $(CROSS)gcc +LD= $(CROSS)ld +STRIP = $(CROSS)strip + +DEBUG = n + +# Add your debugging flag (or not) to EXTRA_CFLAGS +ifeq ($(DEBUG),y) +# DEBFLAGS = -O -g -DSCULL_DEBUG # "-O" is needed to expand inlines +DEBFLAGS = -O0 -g -DSCULL_DEBUG # "-O" is needed to expand inlines + +else + DEBFLAGS = -O2 -Wall +endif + +EXTRA_CFLAGS += $(DEBFLAGS) + + +MY_MODULE_NAME=s_wmt_lsensor_isl29023 + +obj-m := $(MY_MODULE_NAME).o +$(MY_MODULE_NAME)-objs := isl29023.o + +default: + $(MAKE) -C $(KERNELDIR) SUBDIRS=$(PWD) modules + $(STRIP) --strip-debug $(MY_MODULE_NAME).ko + rm -rf *.o *~ core .depend .*.cmd *.mod.c .tmp_versions + +clean: + rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions *.order *.symvers + diff --git a/drivers/input/sensor/isl29023_lsensor/isl29023.c b/drivers/input/sensor/isl29023_lsensor/isl29023.c new file mode 100755 index 00000000..3366e92a --- /dev/null +++ b/drivers/input/sensor/isl29023_lsensor/isl29023.c @@ -0,0 +1,1164 @@ +/* + * isl29023.c - Intersil ISL29023 ALS & Proximity Driver + * + * By Intersil Corp + * Michael DiGioia + * + * Based on isl29011.c + * by Mike DiGioia + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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; version 2 of the License. + * + * 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, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include +#include +#include "../sensor.h" + +/* Insmod parameters */ +//I2C_CLIENT_INSMOD_1(isl29023); + +#define MODULE_NAME "isl29023" + +#define SENSOR_I2C_NAME "isl29023" +#define SENSOR_I2C_ADDR 0x44 + +#undef dbg +#define dbg(fmt, args...) + +#undef errlog +#undef klog +#define errlog(fmt, args...) printk(KERN_ERR "[%s]: " fmt, __FUNCTION__, ## args) +#define klog(fmt, args...) printk(KERN_ALERT "[%s]: " fmt, __FUNCTION__, ## args) + +/* ICS932S401 registers */ +#define ISL29023_REG_VENDOR_REV 0x06 +#define ISL29023_VENDOR 1 +#define ISL29023_VENDOR_MASK 0x0F +#define ISL29023_REV 4 +#define ISL29023_REV_SHIFT 4 +#define ISL29023_REG_DEVICE 0x44 +#define ISL29023_DEVICE 44 + + +#define REG_CMD_1 0x00 +#define REG_CMD_2 0x01 +#define REG_DATA_LSB 0x02 +#define REG_DATA_MSB 0x03 +#define ISL_MOD_MASK 0xE0 +#define ISL_MOD_POWERDOWN 0 +#define ISL_MOD_ALS_ONCE 1 +#define ISL_MOD_IR_ONCE 2 +#define ISL_MOD_RESERVED 4 +#define ISL_MOD_ALS_CONT 5 +#define ISL_MOD_IR_CONT 6 +#define IR_CURRENT_MASK 0xC0 +#define IR_FREQ_MASK 0x30 +#define SENSOR_RANGE_MASK 0x03 +#define ISL_RES_MASK 0x0C + +static int last_mod; + +static struct i2c_client *this_client = NULL; +struct isl_device { + struct input_polled_dev* input_poll_dev; + struct i2c_client* client; + int resolution; + int range; + int isdbg; +#ifdef CONFIG_HAS_EARLYSUSPEND + struct early_suspend earlysuspend; +#endif + +}; + +static struct isl_device* l_sensorconfig = NULL; +static struct kobject *android_lsensor_kobj = NULL; +static int l_enable = 1; // 0:don't report data + +static DEFINE_MUTEX(mutex); + +static int isl_set_range(struct i2c_client *client, int range) +{ + int ret_val; + + ret_val = i2c_smbus_read_byte_data(client, REG_CMD_2); + if (ret_val < 0) + return -EINVAL; + ret_val &= ~SENSOR_RANGE_MASK; /*reset the bit */ + ret_val |= range; + ret_val = i2c_smbus_write_byte_data(client, REG_CMD_2, ret_val); + + printk(KERN_INFO MODULE_NAME ": %s isl29023 set_range call, \n", __func__); + if (ret_val < 0) + return ret_val; + return range; +} + +static int isl_set_mod(struct i2c_client *client, int mod) +{ + int ret, val, freq; + + switch (mod) { + case ISL_MOD_POWERDOWN: + case ISL_MOD_RESERVED: + goto setmod; + case ISL_MOD_ALS_ONCE: + case ISL_MOD_ALS_CONT: + freq = 0; + break; + case ISL_MOD_IR_ONCE: + case ISL_MOD_IR_CONT: + freq = 1; + break; + default: + return -EINVAL; + } + /* set IR frequency */ + val = i2c_smbus_read_byte_data(client, REG_CMD_2); + if (val < 0) + return -EINVAL; + val &= ~IR_FREQ_MASK; + if (freq) + val |= IR_FREQ_MASK; + ret = i2c_smbus_write_byte_data(client, REG_CMD_2, val); + if (ret < 0) + return -EINVAL; + +setmod: + /* set operation mod */ + val = i2c_smbus_read_byte_data(client, REG_CMD_1); + if (val < 0) + return -EINVAL; + val &= ~ISL_MOD_MASK; + val |= (mod << 5); + ret = i2c_smbus_write_byte_data(client, REG_CMD_1, val); + if (ret < 0) + return -EINVAL; + + if (mod != ISL_MOD_POWERDOWN) + last_mod = mod; + + return mod; +} + +static int isl_get_res(struct i2c_client *client) +{ + int val; + + printk(KERN_INFO MODULE_NAME ": %s isl29023 get_res call, \n", __func__); + val = i2c_smbus_read_word_data(client, 0)>>8 & 0xff; + + if (val < 0) + return -EINVAL; + + val &= ISL_RES_MASK; + val >>= 2; + + switch (val) { + case 0: + return 65536; + case 1: + return 4096; + case 2: + return 256; + case 3: + return 16; + default: + return -EINVAL; + } +} + +static int isl_get_mod(struct i2c_client *client) +{ + int val; + + val = i2c_smbus_read_byte_data(client, REG_CMD_1); + if (val < 0) + return -EINVAL; + return val >> 5; +} + +static int isl_get_range(struct i2c_client* client) +{ + switch (i2c_smbus_read_word_data(client, 0)>>8 & 0xff & 0x3) { + case 0: return 1000; + case 1: return 4000; + case 2: return 16000; + case 3: return 64000; + default: return -EINVAL; + } +} + +static ssize_t +isl_sensing_range_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + int val; + + mutex_lock(&mutex); + pm_runtime_get_sync(dev); + val = i2c_smbus_read_byte_data(client, REG_CMD_2); + pm_runtime_put_sync(dev); + mutex_unlock(&mutex); + + dev_dbg(dev, "%s: range: 0x%.2x\n", __func__, val); + + if (val < 0) + return val; + return sprintf(buf, "%d000\n", 1 << (2 * (val & 3))); +} + +static ssize_t +ir_current_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + int val; + + mutex_lock(&mutex); + pm_runtime_get_sync(dev); + val = i2c_smbus_read_byte_data(client, REG_CMD_2); + pm_runtime_put_sync(dev); + mutex_unlock(&mutex); + + dev_dbg(dev, "%s: IR current: 0x%.2x\n", __func__, val); + + if (val < 0) + return -EINVAL; + val >>= 6; + + switch (val) { + case 0: + val = 100; + break; + case 1: + val = 50; + break; + case 2: + val = 25; + break; + case 3: + val = 0; + break; + default: + return -EINVAL; + } + + if (val) + val = sprintf(buf, "%d\n", val); + else + val = sprintf(buf, "%s\n", "12.5"); + return val; +} + +static ssize_t +isl_sensing_mod_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +// struct i2c_client *client = to_i2c_client(dev); + + dev_dbg(dev, "%s: mod: 0x%.2x\n", __func__, last_mod); + + switch (last_mod) { + case ISL_MOD_POWERDOWN: + return sprintf(buf, "%s\n", "0-Power-down"); + case ISL_MOD_ALS_ONCE: + return sprintf(buf, "%s\n", "1-ALS once"); + case ISL_MOD_IR_ONCE: + return sprintf(buf, "%s\n", "2-IR once"); + case ISL_MOD_RESERVED: + return sprintf(buf, "%s\n", "4-Reserved"); + case ISL_MOD_ALS_CONT: + return sprintf(buf, "%s\n", "5-ALS continuous"); + case ISL_MOD_IR_CONT: + return sprintf(buf, "%s\n", "6-IR continuous"); + default: + return -EINVAL; + } +} + +static ssize_t +isl_output_data_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + int ret_val, mod; + unsigned long int output = 0; + int temp; + + mutex_lock(&mutex); + pm_runtime_get_sync(dev); + + temp = i2c_smbus_read_byte_data(client, REG_DATA_MSB); + if (temp < 0) + goto err_exit; + ret_val = i2c_smbus_read_byte_data(client, REG_DATA_LSB); + if (ret_val < 0) + goto err_exit; + ret_val |= temp << 8; + + dev_dbg(dev, "%s: Data: %04x\n", __func__, ret_val); + + mod = isl_get_mod(client); + switch (last_mod) { + case ISL_MOD_ALS_CONT: + case ISL_MOD_ALS_ONCE: + case ISL_MOD_IR_ONCE: + case ISL_MOD_IR_CONT: + output = ret_val; + break; + default: + goto err_exit; + } + pm_runtime_put_sync(dev); + mutex_unlock(&mutex); + return sprintf(buf, "%ld\n", output); + +err_exit: + pm_runtime_put_sync(dev); + mutex_unlock(&mutex); + return -EINVAL; +} + +static int isl_get_lux_data(struct i2c_client* client) +{ + struct isl_device* idev = i2c_get_clientdata(client); + + __u16 resH, resL; + //int range; + resL = i2c_smbus_read_word_data(client, 1)>>8; + resH = i2c_smbus_read_word_data(client, 2)&0xff00; + if ((resL < 0) || (resH < 0)) + { + errlog("Error to read lux_data!\n"); + return -1; + } + return (resH | resL) * idev->range / idev->resolution; +} +static ssize_t +isl_lux_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + __u16 resH, resL;// L1, L2, H1, H2, thresL, thresH; + char cmd2; + int res, data, tmp, range, resolution; + + mutex_lock(&mutex); + pm_runtime_get_sync(dev); + +// cmd2 = i2c_smbus_read_word_data(client, 0)>>8 & 0xff; // 01h + resL = i2c_smbus_read_word_data(client, 1)>>8; // 02h + resH = i2c_smbus_read_word_data(client, 2)&0xff00; // 03h +// L1 = i2c_smbus_read_word_data(client, 3)>>8; // 04h +// L2 = i2c_smbus_read_word_data(client, 4)&0xff00; // 05h +// H1 = i2c_smbus_read_word_data(client, 5)>>8; // 06h +// H2 = i2c_smbus_read_word_data(client, 6)&0xff00; // 07h + + res = resH | resL; +// thresL = L2 | L1; +// thresH = H2 | H1; + + cmd2 = i2c_smbus_read_word_data(client, 0)>>8 & 0xff; + resolution = isl_get_res(client); //resolution + pm_runtime_put_sync(dev); + mutex_unlock(&mutex); + tmp = cmd2 & 0x3; //range + switch (tmp) { + case 0: + range = 1000; + break; + case 1: + range = 4000; + break; + case 2: + range = 16000; + break; + case 3: + range = 64000; + break; + default: + return -EINVAL; + } + data = res * range / resolution; + +// printk("Data = 0x%04x [%d]\n", data, data); +// printk("CMD2 = 0x%x\n", cmd2); +// printk("Threshold Low = 0x%04x\n", thresL); +// printk("Threshold High = 0x%04x\n", thresH); + + return sprintf(buf, "%u\n", data); +} + +static ssize_t +isl_cmd2_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + unsigned long cmd2; + mutex_lock(&mutex); + pm_runtime_get_sync(dev); + cmd2 = i2c_smbus_read_word_data(client, 0)>>8 & 0xff; + pm_runtime_put_sync(dev); + mutex_unlock(&mutex); + dbg(" cmd2: 0x%02x\n", cmd2); + switch (cmd2) { + case 0: + return sprintf(buf, "%s\n", "[cmd2 = 0] n = 16, range = 1000"); + case 1: + return sprintf(buf, "%s\n", "[cmd2 = 1] n = 16, range = 4000"); + case 2: + return sprintf(buf, "%s\n", "[cmd2 = 2] n = 16, range = 16000"); + case 3: + return sprintf(buf, "%s\n", "[cmd2 = 3] n = 16, range = 64000"); + + case 4: + return sprintf(buf, "%s\n", "[cmd2 = 4] n = 12, range = 1000"); + case 5: + return sprintf(buf, "%s\n", "[cmd2 = 5] n = 12, range = 4000"); + case 6: + return sprintf(buf, "%s\n", "[cmd2 = 6] n = 12, range = 16000"); + case 7: + return sprintf(buf, "%s\n", "[cmd2 = 7] n = 12, range = 64000"); + + case 8: + return sprintf(buf, "%s\n", "[cmd2 = 8] n = 8, range = 1000"); + case 9: + return sprintf(buf, "%s\n", "[cmd2 = 9] n = 8, range = 4000"); + case 10: + return sprintf(buf, "%s\n", "[cmd2 = 10] n = 8, range = 16000"); + case 11: + return sprintf(buf, "%s\n", "[cmd2 = 11] n = 8, range = 64000"); + + case 12: + return sprintf(buf, "%s\n", "[cmd2 = 12] n = 4, range = 1000"); + case 13: + return sprintf(buf, "%s\n", "[cmd2 = 13] n = 4, range = 4000"); + case 14: + return sprintf(buf, "%s\n", "[cmd2 = 14] n = 4, range = 16000"); + case 15: + return sprintf(buf, "%s\n", "[cmd2 = 15] n = 4, range = 64000"); + + default: + return -EINVAL; + } +} + + +static ssize_t +isl_sensing_range_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + unsigned int ret_val; + unsigned long val; + + if (strict_strtoul(buf, 10, &val)) + return -EINVAL; + + switch (val) { + case 1000: + val = 0; + break; + case 4000: + val = 1; + break; + case 16000: + val = 2; + break; + case 64000: + val = 3; + break; + default: + return -EINVAL; + } + + mutex_lock(&mutex); + pm_runtime_get_sync(dev); + ret_val = isl_set_range(client, val); + pm_runtime_put_sync(dev); + mutex_unlock(&mutex); + + if (ret_val < 0) + return ret_val; + return count; +} + +static ssize_t +ir_current_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + unsigned int ret_val; + unsigned long val; + + if (!strncmp(buf, "12.5", 4)) + val = 3; + else { + if (strict_strtoul(buf, 10, &val)) + return -EINVAL; + switch (val) { + case 100: + val = 0; + break; + case 50: + val = 1; + break; + case 25: + val = 2; + break; + default: + return -EINVAL; + } + } + + mutex_lock(&mutex); + pm_runtime_get_sync(dev); + + ret_val = i2c_smbus_read_byte_data(client, REG_CMD_2); + if (ret_val < 0) + goto err_exit; + + ret_val &= ~IR_CURRENT_MASK; /*reset the bit before setting them */ + ret_val |= (val << 6); + + ret_val = i2c_smbus_write_byte_data(client, REG_CMD_2, ret_val); + if (ret_val < 0) + goto err_exit; + + pm_runtime_put_sync(dev); + mutex_unlock(&mutex); + + return count; + +err_exit: + pm_runtime_put_sync(dev); + mutex_unlock(&mutex); + return -EINVAL; +} + +static ssize_t +isl_sensing_mod_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + int ret_val; + unsigned long val; + + if (strict_strtoul(buf, 10, &val)) + return -EINVAL; + if (val > 7) + return -EINVAL; + + mutex_lock(&mutex); + pm_runtime_get_sync(dev); + ret_val = isl_set_mod(client, val); + pm_runtime_put_sync(dev); + mutex_unlock(&mutex); + + if (ret_val < 0) + return ret_val; + return count; +} + +static ssize_t +isl_cmd2_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct isl_device* idev = i2c_get_clientdata(client); + int res; + unsigned long val; + if (strict_strtoul(buf, 10, &val)) + return -EINVAL; + if (val > 15 || val < 0) + return -EINVAL; + + mutex_lock(&mutex); + pm_runtime_get_sync(dev); + res = i2c_smbus_write_byte_data(client, REG_CMD_2, val); + if (res < 0) + printk("Warning - write failed\n"); + + idev->resolution = isl_get_res(client); + idev->range = isl_get_range(client); + pm_runtime_put_sync(dev); + mutex_unlock(&mutex); + + return count; +} + +static DEVICE_ATTR(range, S_IRUGO | S_IWUSR, + isl_sensing_range_show, isl_sensing_range_store); +static DEVICE_ATTR(mod, S_IRUGO | S_IWUSR, + isl_sensing_mod_show, isl_sensing_mod_store); +static DEVICE_ATTR(ir_current, S_IRUGO | S_IWUSR, + ir_current_show, ir_current_store); +static DEVICE_ATTR(output, S_IRUGO, isl_output_data_show, NULL); +static DEVICE_ATTR(cmd2, S_IRUGO | S_IWUSR, + isl_cmd2_show, isl_cmd2_store); +static DEVICE_ATTR(lux, S_IRUGO, + isl_lux_show, NULL); + +static struct attribute *mid_att_isl[] = { + &dev_attr_range.attr, + &dev_attr_mod.attr, + &dev_attr_ir_current.attr, + &dev_attr_output.attr, + &dev_attr_lux.attr, + &dev_attr_cmd2.attr, + NULL +}; + +static struct attribute_group m_isl_gr = { + .name = "isl29023", + .attrs = mid_att_isl +}; + +static int isl_set_default_config(struct i2c_client *client) +{ + struct isl_device* idev = i2c_get_clientdata(client); + + int ret=0; +/* We don't know what it does ... */ +// ret = i2c_smbus_write_byte_data(client, REG_CMD_1, 0xE0); +// ret = i2c_smbus_write_byte_data(client, REG_CMD_2, 0xC3); +/* Set default to ALS continuous */ + ret = i2c_smbus_write_byte_data(client, REG_CMD_1, 0xA0); + if (ret < 0) + return -EINVAL; +/* Range: 0~16000, number of clock cycles: 65536 */ + ret = i2c_smbus_write_byte_data(client, REG_CMD_2, 0x02); // vivienne + if (ret < 0) + return -EINVAL; + idev->resolution = isl_get_res(client); + idev->range = isl_get_range(client);; + dbg("isl29023 set_default_config call, \n"); + + return 0; +} + +/* Return 0 if detection is successful, -ENODEV otherwise */ +static int isl29023_detect(struct i2c_client *client/*, int kind, + struct i2c_board_info *info*/) +{ + struct i2c_adapter *adapter = client->adapter; + int vendor, device, revision; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + //printk(KERN_INFO MODULE_NAME ": %s isl29023 detact call, kind:%d type:%s addr:%x \n", __func__, kind, info->type, info->addr); + + /* if (kind <= 0)*/ { + + + vendor = i2c_smbus_read_word_data(client, + ISL29023_REG_VENDOR_REV); + dbg("read vendor=%d(0x%x)\n", vendor,vendor); + if (0x0FFFF == vendor) + { + dbg("find isl29023!\n"); + return 0; + } else { + return -ENODEV; + } + vendor >>= 8; + revision = vendor >> ISL29023_REV_SHIFT; + vendor &= ISL29023_VENDOR_MASK; + if (vendor != ISL29023_VENDOR) + { + dbg("real_vendor=0x%x,tvendor=0x%x\n",vendor,ISL29023_VENDOR); + return -ENODEV; + } + + device = i2c_smbus_read_word_data(client, + ISL29023_REG_DEVICE); + dbg("device=%x\n", device); + device >>= 8; + if (device != ISL29023_DEVICE) + { + dbg("real_device=0x%x, tdevice=0x%x\n", device, ISL29023_DEVICE); + return -ENODEV; + } + + if (revision != ISL29023_REV) + { + dbg("Unknown revision %d\n", + revision); + } + } /*else + dev_dbg(&adapter->dev, "detection forced\n");*/ + + // strlcpy(info->type, "isl29023", I2C_NAME_SIZE); + + return 0; +} + +int isl_input_open(struct input_dev* input) +{ + return 0; +} + +void isl_input_close(struct input_dev* input) +{ +} + +static void isl_input_lux_poll(struct input_polled_dev *dev) +{ + struct isl_device* idev = dev->private; + struct input_dev* input = idev->input_poll_dev->input; + struct i2c_client* client = idev->client; + + if (l_enable != 0) + { + mutex_lock(&mutex); + //pm_runtime_get_sync(dev); + input_report_abs(input, ABS_MISC, isl_get_lux_data(client)); + input_sync(input); + //pm_runtime_put_sync(dev); + mutex_unlock(&mutex); + } +} + +static struct i2c_device_id isl29023_id[] = { + {"isl29023", 0}, + {} +}; + +static int isl29023_runtime_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + dev_dbg(dev, "suspend\n"); + + mutex_lock(&mutex); + pm_runtime_get_sync(dev); + isl_set_mod(client, ISL_MOD_POWERDOWN); + pm_runtime_put_sync(dev); + mutex_unlock(&mutex); + + printk(KERN_INFO MODULE_NAME ": %s isl29023 suspend call, \n", __func__); + return 0; +} + +static int isl29023_runtime_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + dev_dbg(dev, "resume\n"); + + mutex_lock(&mutex); + pm_runtime_get_sync(dev); + isl_set_mod(client, last_mod); + pm_runtime_put_sync(dev); + mutex_unlock(&mutex); + + printk(KERN_INFO MODULE_NAME ": %s isl29023 resume call, \n", __func__); + return 0; +} + +MODULE_DEVICE_TABLE(i2c, isl29023_id); + +/*static const struct dev_pm_ops isl29023_pm_ops = { + .runtime_suspend = isl29023_runtime_suspend, + .runtime_resume = isl29023_runtime_resume, +}; + +static struct i2c_board_info isl_info = { + I2C_BOARD_INFO("isl29023", 0x44), +}; + +static struct i2c_driver isl29023_driver = { + .driver = { + .name = "isl29023", + .pm = &isl29023_pm_ops, + }, + .probe = isl29023_probe, + .remove = isl29023_remove, + .id_table = isl29023_id, + .detect = isl29023_detect, + //.address_data = &addr_data, +};*/ + +static int mmad_open(struct inode *inode, struct file *file) +{ + dbg("Open the l-sensor node...\n"); + return 0; +} + +static int mmad_release(struct inode *inode, struct file *file) +{ + dbg("Close the l-sensor node...\n"); + return 0; +} + +static ssize_t mmad_read(struct file *fl, char __user *buf, size_t cnt, loff_t *lf) +{ + int lux_data = 0; + + mutex_lock(&mutex); + lux_data = isl_get_lux_data(l_sensorconfig->client); + mutex_unlock(&mutex); + if (lux_data < 0) + { + errlog("Failed to read lux data!\n"); + return -1; + } + copy_to_user(buf, &lux_data, sizeof(lux_data)); + return sizeof(lux_data); +} + +static long +mmad_ioctl(/*struct inode *inode,*/ struct file *file, unsigned int cmd, + unsigned long arg) +{ + void __user *argp = (void __user *)arg; + //char rwbuf[5]; + short enable; //amsr = -1; + unsigned int uval; + + dbg("l-sensor ioctr...\n"); + //memset(rwbuf, 0, sizeof(rwbuf)); + switch (cmd) { + case LIGHT_IOCTL_SET_ENABLE: + // enable/disable sensor + if (copy_from_user(&enable, argp, sizeof(short))) + { + printk(KERN_ERR "Can't get enable flag!!!\n"); + return -EFAULT; + } + dbg("enable=%d\n",enable); + if ((enable >=0) && (enable <=1)) + { + dbg("driver: disable/enable(%d) gsensor.\n", enable); + + //l_sensorconfig.sensor_enable = enable; + dbg("Should to implement d/e the light sensor!\n"); + l_enable = enable; + + } else { + printk(KERN_ERR "Wrong enable argument in %s !!!\n", __FUNCTION__); + return -EINVAL; + } + break; + case WMT_IOCTL_SENSOR_GET_DRVID: + uval = ISL29023_DRVID; + if (copy_to_user((unsigned int*)arg, &uval, sizeof(unsigned int))) + { + return -EFAULT; + } + dbg("Isl29023_driver_id:%d\n",uval); + default: + break; + } + + return 0; +} + + +static struct file_operations mmad_fops = { + .owner = THIS_MODULE, + .open = mmad_open, + .release = mmad_release, + .read = mmad_read, + .unlocked_ioctl = mmad_ioctl, +}; + + +static struct miscdevice mmad_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "lsensor_ctrl", + .fops = &mmad_fops, +}; + +static void isl29023_early_suspend(struct early_suspend *h) +{ + struct i2c_client *client = l_sensorconfig->client; + + dbg("start\n"); + mutex_lock(&mutex); + //pm_runtime_get_sync(dev); + isl_set_mod(client, ISL_MOD_POWERDOWN); + //pm_runtime_put_sync(dev); + mutex_unlock(&mutex); + dbg("exit\n"); +} + +static void isl29023_late_resume(struct early_suspend *h) +{ + struct i2c_client *client = l_sensorconfig->client; + + dbg("start\n"); + mutex_lock(&mutex); + //pm_runtime_get_sync(dev); + isl_set_mod(client, last_mod); + isl_set_default_config(client); + //pm_runtime_put_sync(dev); + mutex_unlock(&mutex); + dbg("exit\n"); +} + + +static int +isl29023_probe(struct i2c_client *client/*, const struct i2c_device_id *id*/) +{ + int res=0; + + struct isl_device* idev = kzalloc(sizeof(struct isl_device), GFP_KERNEL); + if(!idev) + return -ENOMEM; + + l_sensorconfig = idev; + android_lsensor_kobj = kobject_create_and_add("android_lsensor", NULL); + if (android_lsensor_kobj == NULL) { + errlog( + "lsensor_sysfs_init:"\ + "subsystem_register failed\n"); + res = -ENOMEM; + goto err_kobjetc_create; + } + res = sysfs_create_group(android_lsensor_kobj, &m_isl_gr); + if (res) { + //pr_warn("isl29023: device create file failed!!\n"); + printk(KERN_INFO MODULE_NAME ": %s isl29023 device create file failed\n", __func__); + res = -EINVAL; + goto err_sysfs_create; + } + +/* last mod is ALS continuous */ + last_mod = 5; + //pm_runtime_enable(&client->dev); + idev->input_poll_dev = input_allocate_polled_device(); + if(!idev->input_poll_dev) + { + res = -ENOMEM; + goto err_input_allocate_device; + } + idev->client = client; + idev->input_poll_dev->private = idev; + idev->input_poll_dev->poll = isl_input_lux_poll; + idev->input_poll_dev->poll_interval = 100;//50; + idev->input_poll_dev->input->open = isl_input_open; + idev->input_poll_dev->input->close = isl_input_close; + idev->input_poll_dev->input->name = "lsensor_lux"; + idev->input_poll_dev->input->id.bustype = BUS_I2C; + idev->input_poll_dev->input->dev.parent = &client->dev; + input_set_drvdata(idev->input_poll_dev->input, idev); + input_set_capability(idev->input_poll_dev->input, EV_ABS, ABS_MISC); + input_set_abs_params(idev->input_poll_dev->input, ABS_MISC, 0, 16000, 0, 0); + i2c_set_clientdata(client, idev); + /* set default config after set_clientdata */ + res = isl_set_default_config(client); + res = misc_register(&mmad_device); + if (res) { + errlog("mmad_device register failed\n"); + goto err_misc_register; + } + res = input_register_polled_device(idev->input_poll_dev); + if(res < 0) + goto err_input_register_device; + // suspend/resume register +#ifdef CONFIG_HAS_EARLYSUSPEND + idev->earlysuspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; + idev->earlysuspend.suspend = isl29023_early_suspend; + idev->earlysuspend.resume = isl29023_late_resume; + register_early_suspend(&(idev->earlysuspend)); +#endif + + dbg("isl29023 probe succeed!\n"); + return 0; +err_input_register_device: + misc_deregister(&mmad_device); + input_free_polled_device(idev->input_poll_dev); +err_misc_register: +err_input_allocate_device: + //__pm_runtime_disable(&client->dev, false); +err_sysfs_create: + kobject_del(android_lsensor_kobj); +err_kobjetc_create: + kfree(idev); + return res; +} + +static int isl29023_remove(struct i2c_client *client) +{ + struct isl_device* idev = i2c_get_clientdata(client); + + //unregister_early_suspend(&(idev->earlysuspend)); + misc_deregister(&mmad_device); + input_unregister_polled_device(idev->input_poll_dev); + input_free_polled_device(idev->input_poll_dev); + sysfs_remove_group(android_lsensor_kobj, &m_isl_gr); + kobject_del(android_lsensor_kobj); + //__pm_runtime_disable(&client->dev, false); + kfree(idev); + printk(KERN_INFO MODULE_NAME ": %s isl29023 remove call, \n", __func__); + return 0; +} + +//****************add platform_device & platform_driver for suspend &resume 2013-7-2 +static int ls_probe(struct platform_device *pdev){ + //printk("<<<%s\n", __FUNCTION__); + return 0; +} +static int ls_remove(struct platform_device *pdev){ + //printk("<<<%s\n", __FUNCTION__); + return 0; +} +static int ls_suspend(struct platform_device *pdev, pm_message_t state){ + printk("<<<%s\n", __FUNCTION__); + struct i2c_client *client = l_sensorconfig->client; + + mutex_lock(&mutex); + + isl_set_mod(client, ISL_MOD_POWERDOWN); + + mutex_unlock(&mutex); + + + return 0; +} + +static int ls_resume(struct platform_device *pdev){ + //return 0; + int ret = 0; + int count = 0; + printk("<<<%s\n", __FUNCTION__); + struct i2c_client *client = l_sensorconfig->client; + + + + +RETRY: + mutex_lock(&mutex); + //pm_runtime_get_sync(dev); + isl_set_mod(client, last_mod); + ret = isl_set_default_config(client); + //pm_runtime_put_sync(dev); + mutex_unlock(&mutex); + if (ret < 0){ + printk("%s isl_set_default_config fail!\n", __FUNCTION__); + count++; + if (count < 5){ + mdelay(2); + goto RETRY; + } + else + return ret; + } + return 0; + +} +static void lsdev_release(struct device *dev) +{ + return; +} +static struct platform_device lsdev = { + .name = "lsdevice", + .id = -1, + .dev = { + .release = lsdev_release, + }, +}; +static struct platform_driver lsdrv = { + .probe = ls_probe, + .remove = ls_remove, + .suspend = ls_suspend, + .resume = ls_resume, + .driver = { + .name = "lsdevice", + }, +}; +//******************************************************************** + +static int __init sensor_isl29023_init(void) +{ + printk(KERN_INFO MODULE_NAME ": %s isl29023 init call, \n", __func__); + /* + * Force device to initialize: i2c-15 0x44 + * If i2c_new_device is not called, even isl29023_detect will not run + * TODO: rework to automatically initialize the device + */ + //i2c_new_device(i2c_get_adapter(15), &isl_info); + //return i2c_add_driver(&isl29023_driver); + if (!(this_client = sensor_i2c_register_device(2, SENSOR_I2C_ADDR, SENSOR_I2C_NAME))) + { + printk(KERN_EMERG"Can't register gsensor i2c device!\n"); + return -1; + } + if (isl29023_detect(this_client)) + { + errlog("Can't find light sensor isl29023!\n"); + goto detect_fail; + } + if(isl29023_probe(this_client)) + { + errlog("Erro for probe!\n"); + goto detect_fail; + } + int ret = 0; + ret = platform_device_register(&lsdev); + if (ret){ + printk("<< + * + * 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 2 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 . + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +//#include +#include +#include +#ifdef CONFIG_HAS_EARLYSUSPEND +#include +#endif /* CONFIG_HAS_EARLYSUSPEND */ +#include +#include +#include +#include "kionix_accel.h" +#include "../sensor.h" + + + +/* Debug Message Flags */ +#define KIONIX_KMSG_ERR 1 /* Print kernel debug message for error */ +#define KIONIX_KMSG_INF 1 /* Print kernel debug message for info */ + +#if KIONIX_KMSG_ERR +#define KMSGERR(format, ...) \ + dev_err(format, ## __VA_ARGS__) + //printk(format, ## __VA_ARGS__) +#else +#define KMSGERR(format, ...) +#endif + +#if KIONIX_KMSG_INF +#define KMSGINF(format, ...) \ + dev_info(format, ## __VA_ARGS__) + //printk(format, ## __VA_ARGS__) +#else +#define KMSGINF(format, ...) +#endif + + +/****************************************************************************** + * Accelerometer WHO_AM_I return value + *****************************************************************************/ +#define KIONIX_ACCEL_WHO_AM_I_KXTE9 0x00 +#define KIONIX_ACCEL_WHO_AM_I_KXTF9 0x01 +#define KIONIX_ACCEL_WHO_AM_I_KXTI9_1001 0x04 +#define KIONIX_ACCEL_WHO_AM_I_KXTIK_1004 0x05 +#define KIONIX_ACCEL_WHO_AM_I_KXTJ9_1005 0x07 +#define KIONIX_ACCEL_WHO_AM_I_KXTJ9_1007 0x08 +#define KIONIX_ACCEL_WHO_AM_I_KXCJ9_1008 0x0A +#define KIONIX_ACCEL_WHO_AM_I_KXTJ2_1009 0x09 +#define KIONIX_ACCEL_WHO_AM_I_KXCJK_1013 0x11 + +/****************************************************************************** + * Accelerometer Grouping + *****************************************************************************/ +#define KIONIX_ACCEL_GRP1 1 /* KXTE9 */ +#define KIONIX_ACCEL_GRP2 2 /* KXTF9/I9-1001/J9-1005 */ +#define KIONIX_ACCEL_GRP3 3 /* KXTIK-1004 */ +#define KIONIX_ACCEL_GRP4 4 /* KXTJ9-1007/KXCJ9-1008 */ +#define KIONIX_ACCEL_GRP5 5 /* KXTJ2-1009 */ +#define KIONIX_ACCEL_GRP6 6 /* KXCJK-1013 */ + +/****************************************************************************** + * Registers for Accelerometer Group 1 & 2 & 3 + *****************************************************************************/ +#define ACCEL_WHO_AM_I 0x0F + +/*****************************************************************************/ +/* Registers for Accelerometer Group 1 */ +/*****************************************************************************/ +/* Output Registers */ +#define ACCEL_GRP1_XOUT 0x12 +/* Control Registers */ +#define ACCEL_GRP1_CTRL_REG1 0x1B +/* CTRL_REG1 */ +#define ACCEL_GRP1_PC1_OFF 0x7F +#define ACCEL_GRP1_PC1_ON (1 << 7) +#define ACCEL_GRP1_ODR40 (3 << 3) +#define ACCEL_GRP1_ODR10 (2 << 3) +#define ACCEL_GRP1_ODR3 (1 << 3) +#define ACCEL_GRP1_ODR1 (0 << 3) +#define ACCEL_GRP1_ODR_MASK (3 << 3) + +/*****************************************************************************/ +/* Registers for Accelerometer Group 2 & 3 */ +/*****************************************************************************/ +/* Output Registers */ +#define ACCEL_GRP2_XOUT_L 0x06 +/* Control Registers */ +#define ACCEL_GRP2_INT_REL 0x1A +#define ACCEL_GRP2_CTRL_REG1 0x1B +#define ACCEL_GRP2_INT_CTRL1 0x1E +#define ACCEL_GRP2_DATA_CTRL 0x21 +/* CTRL_REG1 */ +#define ACCEL_GRP2_PC1_OFF 0x7F +#define ACCEL_GRP2_PC1_ON (1 << 7) +#define ACCEL_GRP2_DRDYE (1 << 5) +#define ACCEL_GRP2_G_8G (2 << 3) +#define ACCEL_GRP2_G_4G (1 << 3) +#define ACCEL_GRP2_G_2G (0 << 3) +#define ACCEL_GRP2_G_MASK (3 << 3) +#define ACCEL_GRP2_RES_8BIT (0 << 6) +#define ACCEL_GRP2_RES_12BIT (1 << 6) +#define ACCEL_GRP2_RES_MASK (1 << 6) +/* INT_CTRL1 */ +#define ACCEL_GRP2_IEA (1 << 4) +#define ACCEL_GRP2_IEN (1 << 5) +/* DATA_CTRL_REG */ +#define ACCEL_GRP2_ODR12_5 0x00 +#define ACCEL_GRP2_ODR25 0x01 +#define ACCEL_GRP2_ODR50 0x02 +#define ACCEL_GRP2_ODR100 0x03 +#define ACCEL_GRP2_ODR200 0x04 +#define ACCEL_GRP2_ODR400 0x05 +#define ACCEL_GRP2_ODR800 0x06 +/*****************************************************************************/ + + +/*****************************************************************************/ +/* Registers for Accelerometer Group 4 & 5 & 6 */ +/*****************************************************************************/ +/* Output Registers */ +#define ACCEL_GRP4_XOUT_L 0x06 +/* Control Registers */ +#define ACCEL_GRP4_INT_REL 0x1A +#define ACCEL_GRP4_CTRL_REG1 0x1B +#define ACCEL_GRP4_INT_CTRL1 0x1E +#define ACCEL_GRP4_DATA_CTRL 0x21 +/* CTRL_REG1 */ +#define ACCEL_GRP4_PC1_OFF 0x7F +#define ACCEL_GRP4_PC1_ON (1 << 7) +#define ACCEL_GRP4_DRDYE (1 << 5) +#define ACCEL_GRP4_G_8G (2 << 3) +#define ACCEL_GRP4_G_4G (1 << 3) +#define ACCEL_GRP4_G_2G (0 << 3) +#define ACCEL_GRP4_G_MASK (3 << 3) +#define ACCEL_GRP4_RES_8BIT (0 << 6) +#define ACCEL_GRP4_RES_12BIT (1 << 6) +#define ACCEL_GRP4_RES_MASK (1 << 6) +/* INT_CTRL1 */ +#define ACCEL_GRP4_IEA (1 << 4) +#define ACCEL_GRP4_IEN (1 << 5) +/* DATA_CTRL_REG */ +#define ACCEL_GRP4_ODR0_781 0x08 +#define ACCEL_GRP4_ODR1_563 0x09 +#define ACCEL_GRP4_ODR3_125 0x0A +#define ACCEL_GRP4_ODR6_25 0x0B +#define ACCEL_GRP4_ODR12_5 0x00 +#define ACCEL_GRP4_ODR25 0x01 +#define ACCEL_GRP4_ODR50 0x02 +#define ACCEL_GRP4_ODR100 0x03 +#define ACCEL_GRP4_ODR200 0x04 +#define ACCEL_GRP4_ODR400 0x05 +#define ACCEL_GRP4_ODR800 0x06 +#define ACCEL_GRP4_ODR1600 0x07 +/*****************************************************************************/ + +/* Input Event Constants */ +#define ACCEL_G_MAX 8096 +#define ACCEL_FUZZ 3 +#define ACCEL_FLAT 3 +/* I2C Retry Constants */ +#define KIONIX_I2C_RETRY_COUNT 10 /* Number of times to retry i2c */ +#define KIONIX_I2C_RETRY_TIMEOUT 1 /* Timeout between retry (miliseconds) */ + +/* Earlysuspend Contants */ +#define KIONIX_ACCEL_EARLYSUSPEND_TIMEOUT 5000 /* Timeout (miliseconds) */ + +/* + * The following table lists the maximum appropriate poll interval for each + * available output data rate (ODR). + */ +static const struct { + unsigned int cutoff; + u8 mask; +} kionix_accel_grp1_odr_table[] = { + { 100, ACCEL_GRP1_ODR40 }, + { 334, ACCEL_GRP1_ODR10 }, + { 1000, ACCEL_GRP1_ODR3 }, + { 0, ACCEL_GRP1_ODR1 }, +}; + +static const struct { + unsigned int cutoff; + u8 mask; +} kionix_accel_grp2_odr_table[] = { + { 3, ACCEL_GRP2_ODR800 }, + { 5, ACCEL_GRP2_ODR400 }, + { 10, ACCEL_GRP2_ODR200 }, + { 20, ACCEL_GRP2_ODR100 }, + { 40, ACCEL_GRP2_ODR50 }, + { 80, ACCEL_GRP2_ODR25 }, + { 0, ACCEL_GRP2_ODR12_5}, +}; + +static const struct { + unsigned int cutoff; + u8 mask; +} kionix_accel_grp4_odr_table[] = { + { 2, ACCEL_GRP4_ODR1600 }, + { 3, ACCEL_GRP4_ODR800 }, + { 5, ACCEL_GRP4_ODR400 }, + { 10, ACCEL_GRP4_ODR200 }, + { 20, ACCEL_GRP4_ODR100 }, + { 40, ACCEL_GRP4_ODR50 }, + { 80, ACCEL_GRP4_ODR25 }, + { 160, ACCEL_GRP4_ODR12_5}, + { 320, ACCEL_GRP4_ODR6_25}, + { 640, ACCEL_GRP4_ODR3_125}, + { 1280, ACCEL_GRP4_ODR1_563}, + { 0, ACCEL_GRP4_ODR0_781}, +}; + +enum { + accel_grp1_ctrl_reg1 = 0, + accel_grp1_regs_count, +}; + +enum { + accel_grp2_ctrl_reg1 = 0, + accel_grp2_data_ctrl, + accel_grp2_int_ctrl, + accel_grp2_regs_count, +}; + +enum { + accel_grp4_ctrl_reg1 = 0, + accel_grp4_data_ctrl, + accel_grp4_int_ctrl, + accel_grp4_regs_count, +}; + +#define GSENSOR_PROC_NAME "gsensor_config" +#define GSENSOR_MAJOR 161 +static struct i2c_client *this_client = NULL; +static struct platform_device *this_pdev; +static struct kionix_accel_platform_data kionix_accel_pdata = { + .min_interval = 5, + .poll_interval = 200, + .accel_direction = 7, + .accel_irq_use_drdy = 0, + .accel_res = KIONIX_ACCEL_RES_12BIT, + .accel_g_range = KIONIX_ACCEL_G_4G, +}; +/* +struct kionix_config +{ + int op; + int int_gpio; //0-3 + int xyz_axis[3][2]; // (axis,direction) + int rxyz_axis[3][2]; + int irq; + struct proc_dir_entry* sensor_proc; + int sensorlevel; + int shake_enable; // 1--enable shake, 0--disable shake + int manual_rotation; // 0--landance, 90--vertical + struct input_dev *input_dev; + //struct work_struct work; + struct delayed_work work; // for polling + struct workqueue_struct *queue; + int isdbg; // 0-- no debug log, 1--show debug log + int sensor_samp; // 1,2,4,8,16,32,64,120 + int sensor_enable; // 0 --> disable sensor, 1 --> enable sensor + int test_pass; + spinlock_t spinlock; + int pollcnt; // the counts of polling + int offset[3]; +}; + +static struct kionix_config l_sensorconfig = { + .op = 0, + .int_gpio = 3, + .xyz_axis = { + {ABS_X, -1}, + {ABS_Y, 1}, + {ABS_Z, -1}, + }, + .irq = 6, + .int_gpio = 3, + .sensor_proc = NULL, + //.sensorlevel = SENSOR_GRAVITYGAME_MODE, + .shake_enable = 0, // default enable shake + .isdbg = 0, + .sensor_samp = 10, // 4sample/second + .sensor_enable = 1, // enable sensor + .test_pass = 0, // for test program + .pollcnt = 0, // Don't report the x,y,z when the driver is loaded until 2~3 seconds + .offset = {0,0,0}, +}; +*/ + +struct kionix_accel_driver { + struct i2c_client *client; + struct kionix_accel_platform_data accel_pdata; + struct input_dev *input_dev; + struct delayed_work accel_work; + struct workqueue_struct *accel_workqueue; + wait_queue_head_t wqh_suspend; + + int accel_data[3]; + int accel_cali[3]; + u8 axis_map_x; + u8 axis_map_y; + u8 axis_map_z; + bool negate_x; + bool negate_y; + bool negate_z; + u8 shift; + + unsigned int poll_interval; + unsigned int poll_delay; + unsigned int accel_group; + u8 *accel_registers; + + atomic_t accel_suspended; + atomic_t accel_suspend_continue; + atomic_t accel_enabled; + atomic_t accel_input_event; + atomic_t accel_enable_resume; + struct mutex mutex_earlysuspend; + struct mutex mutex_resume; + struct mutex mutex_subinput; + rwlock_t rwlock_accel_data; + + bool accel_drdy; + + /* Function callback */ + void (*kionix_accel_report_accel_data)(struct kionix_accel_driver *acceld); + int (*kionix_accel_update_odr)(struct kionix_accel_driver *acceld, unsigned int poll_interval); + int (*kionix_accel_power_on_init)(struct kionix_accel_driver *acceld); + int (*kionix_accel_operate)(struct kionix_accel_driver *acceld); + int (*kionix_accel_standby)(struct kionix_accel_driver *acceld); + +#ifdef CONFIG_HAS_EARLYSUSPEND + struct early_suspend early_suspend; +#endif /* CONFIG_HAS_EARLYSUSPEND */ +}; + +extern int wmt_getsyspara(char *varname, unsigned char *varval, int *varlen); +static void kionix_accel_update_direction(struct kionix_accel_driver *acceld); +static int get_axisset(struct kionix_accel_driver *acceld) +{ + char varbuf[64]; + int n; + int ubootvar[3][3]; + int varlen; + int err; + + memset(varbuf, 0, sizeof(varbuf)); + varlen = sizeof(varbuf); + if (wmt_getsyspara("wmt.io.kionixgsensor", varbuf, &varlen)) { + printk(KERN_DEBUG "Can't get gsensor config in u-boot!!!!\n"); + return -1; + kionix_accel_update_direction(acceld);//return -1; + } else { + sscanf(varbuf, "%d:%d:%d:%d:%d:%d", + &ubootvar[0][0], + &ubootvar[0][1], + &ubootvar[1][0], + &ubootvar[1][1], + &ubootvar[2][0], + &ubootvar[2][1]); + + acceld->axis_map_x = ubootvar[0][0]; + acceld->negate_x = ubootvar[0][1]<0?1:0; + acceld->axis_map_y = ubootvar[1][0]; + acceld->negate_y = ubootvar[1][1]<0?1:0; + acceld->axis_map_z = ubootvar[2][0]; + acceld->negate_z = ubootvar[2][1]<0?1:0; + /*kionix_accel_pdata.accel_direction = direction; + printk(KERN_ERR"accel_direction is %d,g_range is %d,res is %d\n",kionix_accel_pdata.accel_direction,kionix_accel_pdata.accel_g_range,kionix_accel_pdata.accel_res);*/ + + } + return 0; +} + +static int kionix_i2c_read(struct i2c_client *client, u8 addr, u8 *data, int len) +{ + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = client->flags, + .len = 1, + .buf = &addr, + }, + { + .addr = client->addr, + .flags = client->flags | I2C_M_RD, + .len = len, + .buf = data, + }, + }; + + return i2c_transfer(client->adapter, msgs, 2); +} + +static int kionix_i2c_write(struct i2c_client *client, u8 addr, u8 *data, int len) +{ + char wrData[12] = {0}; + /* + struct i2c_msg msgs = + {.addr = client->addr, .flags = 0, .len = len+1, .buf = wrData,}; +*/ + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = client->flags, + .len = len+1, + .buf = wrData, + }, + }; + + if (!client || (!data)) + { + printk("%s NULL client!\n", __FUNCTION__); + return -EIO; + } + + wrData[0] = addr; + strncpy(&wrData[1], data, len); + + if (i2c_transfer(client->adapter, &msgs, 1) < 0) { + printk( "%s: transfer failed.", __func__); + return -EIO; + } + + return 0; +} + +static int kionix_i2c_writebyte(struct i2c_client *client, u8 addr, u8 data) +{ + char wrData[2] = {0}; + /* + struct i2c_msg msgs = + {.addr = client->addr, .flags = 0, .len = len+1, .buf = wrData,}; +*/ + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = client->flags, + .len = 2, + .buf = &wrData[0], + }, + }; + + if (!client) + { + printk("%s NULL client!\n", __FUNCTION__); + return -EIO; + } + + wrData[0] = addr; + //strncpy(&wrData[1], data, len); + wrData[1] = data; + + if (i2c_transfer(client->adapter, &msgs, 1) < 0) { + printk( "%s: transfer failed.", __func__); + return -EIO; + } + + return 0; +} + +static int kionix_strtok(const char *buf, size_t count, char **token, const int token_nr) +{ + char *buf2 = (char *)kzalloc((count + 1) * sizeof(char), GFP_KERNEL); + char **token2 = token; + unsigned int num_ptr = 0, num_nr = 0, num_neg = 0; + int i = 0, start = 0, end = (int)count; + + strcpy(buf2, buf); + + /* We need to breakup the string into separate chunks in order for kstrtoint + * or strict_strtol to parse them without returning an error. Stop when the end of + * the string is reached or when enough value is read from the string */ + while((start < end) && (i < token_nr)) { + /* We found a negative sign */ + if(*(buf2 + start) == '-') { + /* Previous char(s) are numeric, so we store their value first before proceed */ + if(num_nr > 0) { + /* If there is a pending negative sign, we adjust the variables to account for it */ + if(num_neg) { + num_ptr--; + num_nr++; + } + *token2 = (char *)kzalloc((num_nr + 2) * sizeof(char), GFP_KERNEL); + strncpy(*token2, (const char *)(buf2 + num_ptr), (size_t) num_nr); + *(*token2+num_nr) = '\n'; + i++; + token2++; + /* Reset */ + num_ptr = num_nr = 0; + } + /* This indicates that there is a pending negative sign in the string */ + num_neg = 1; + } + /* We found a numeric */ + else if((*(buf2 + start) >= '0') && (*(buf2 + start) <= '9')) { + /* If the previous char(s) are not numeric, set num_ptr to current char */ + if(num_nr < 1) + num_ptr = start; + num_nr++; + } + /* We found an unwanted character */ + else { + /* Previous char(s) are numeric, so we store their value first before proceed */ + if(num_nr > 0) { + if(num_neg) { + num_ptr--; + num_nr++; + } + *token2 = (char *)kzalloc((num_nr + 2) * sizeof(char), GFP_KERNEL); + strncpy(*token2, (const char *)(buf2 + num_ptr), (size_t) num_nr); + *(*token2+num_nr) = '\n'; + i++; + token2++; + } + /* Reset all the variables to start afresh */ + num_ptr = num_nr = num_neg = 0; + } + start++; + } + + kfree(buf2); + + return (i == token_nr) ? token_nr : -1; +} + +static int kionix_accel_grp1_power_on_init(struct kionix_accel_driver *acceld) +{ + int err; + + if(atomic_read(&acceld->accel_enabled) > 0) { + err = i2c_smbus_write_byte_data(acceld->client, + ACCEL_GRP1_CTRL_REG1, acceld->accel_registers[accel_grp1_ctrl_reg1] | ACCEL_GRP1_PC1_ON); + if (err < 0) + return err; + } + else { + err = i2c_smbus_write_byte_data(acceld->client, + ACCEL_GRP1_CTRL_REG1, acceld->accel_registers[accel_grp1_ctrl_reg1]); + if (err < 0) + return err; + } + + return 0; +} + +static int kionix_accel_grp1_operate(struct kionix_accel_driver *acceld) +{ + int err; + + err = i2c_smbus_write_byte_data(acceld->client, ACCEL_GRP1_CTRL_REG1, \ + acceld->accel_registers[accel_grp2_ctrl_reg1] | ACCEL_GRP1_PC1_ON); + if (err < 0) + return err; + + queue_delayed_work(acceld->accel_workqueue, &acceld->accel_work, 0); + + return 0; +} + +static int kionix_accel_grp1_standby(struct kionix_accel_driver *acceld) +{ + int err; + + cancel_delayed_work_sync(&acceld->accel_work); + + err = i2c_smbus_write_byte_data(acceld->client, ACCEL_GRP1_CTRL_REG1, 0); + if (err < 0) + return err; + + return 0; +} + +static void kionix_accel_grp1_report_accel_data(struct kionix_accel_driver *acceld) +{ + u8 accel_data[3]; + s16 x, y, z; + int err; + struct input_dev *input_dev = acceld->input_dev; + int loop = KIONIX_I2C_RETRY_COUNT; + + if(atomic_read(&acceld->accel_enabled) > 0) { + if(atomic_read(&acceld->accel_enable_resume) > 0) + { + while(loop) { + //mutex_lock(&input_dev->mutex); + mutex_lock(&acceld->mutex_subinput); + err = kionix_i2c_read(acceld->client, ACCEL_GRP1_XOUT, accel_data, 6); + //mutex_unlock(&input_dev->mutex); + mutex_unlock(&acceld->mutex_subinput); + if(err < 0){ + loop--; + mdelay(KIONIX_I2C_RETRY_TIMEOUT); + } + else + loop = 0; + } + if (err < 0) { + KMSGERR(&acceld->client->dev, "%s: read data output error = %d\n", __func__, err); + } + else { + write_lock(&acceld->rwlock_accel_data); + + x = ((s16) le16_to_cpu(((s16)(accel_data[acceld->axis_map_x] >> 2)) - 32)) << 6; + y = ((s16) le16_to_cpu(((s16)(accel_data[acceld->axis_map_y] >> 2)) - 32)) << 6; + z = ((s16) le16_to_cpu(((s16)(accel_data[acceld->axis_map_z] >> 2)) - 32)) << 6; + + acceld->accel_data[acceld->axis_map_x] = (acceld->negate_x ? -x : x) + acceld->accel_cali[acceld->axis_map_x]; + acceld->accel_data[acceld->axis_map_y] = (acceld->negate_y ? -y : y) + acceld->accel_cali[acceld->axis_map_y]; + acceld->accel_data[acceld->axis_map_z] = (acceld->negate_z ? -z : z) + acceld->accel_cali[acceld->axis_map_z]; + + if(atomic_read(&acceld->accel_input_event) > 0) { + input_report_abs(acceld->input_dev, ABS_X, acceld->accel_data[acceld->axis_map_x]); + input_report_abs(acceld->input_dev, ABS_Y, acceld->accel_data[acceld->axis_map_y]); + input_report_abs(acceld->input_dev, ABS_Z, acceld->accel_data[acceld->axis_map_z]); + input_sync(acceld->input_dev); + } + + write_unlock(&acceld->rwlock_accel_data); + } + } + else + { + atomic_inc(&acceld->accel_enable_resume); + } + } +} + +static int kionix_accel_grp1_update_odr(struct kionix_accel_driver *acceld, unsigned int poll_interval) +{ + int err; + int i; + u8 odr; + + /* Use the lowest ODR that can support the requested poll interval */ + for (i = 0; i < ARRAY_SIZE(kionix_accel_grp1_odr_table); i++) { + odr = kionix_accel_grp1_odr_table[i].mask; + if (poll_interval < kionix_accel_grp1_odr_table[i].cutoff) + break; + } + + /* Do not need to update CTRL_REG1 register if the ODR is not changed */ + if((acceld->accel_registers[accel_grp1_ctrl_reg1] & ACCEL_GRP1_ODR_MASK) == odr) + return 0; + else { + acceld->accel_registers[accel_grp1_ctrl_reg1] &= ~ACCEL_GRP1_ODR_MASK; + acceld->accel_registers[accel_grp1_ctrl_reg1] |= odr; + } + + /* Do not need to update CTRL_REG1 register if the sensor is not currently turn on */ + if(atomic_read(&acceld->accel_enabled) > 0) { + err = i2c_smbus_write_byte_data(acceld->client, ACCEL_GRP1_CTRL_REG1, \ + acceld->accel_registers[accel_grp1_ctrl_reg1] | ACCEL_GRP1_PC1_ON); + if (err < 0) + return err; + } + + return 0; +} + +static int kionix_accel_grp2_power_on_init(struct kionix_accel_driver *acceld) +{ + int err; + + /* ensure that PC1 is cleared before updating control registers */ + err = i2c_smbus_write_byte_data(acceld->client, + ACCEL_GRP2_CTRL_REG1, 0); + if (err < 0) + return err; + + err = i2c_smbus_write_byte_data(acceld->client, + ACCEL_GRP2_DATA_CTRL, acceld->accel_registers[accel_grp2_data_ctrl]); + if (err < 0) + return err; + + /* only write INT_CTRL_REG1 if in irq mode */ + if (acceld->client->irq) { + err = i2c_smbus_write_byte_data(acceld->client, + ACCEL_GRP2_INT_CTRL1, acceld->accel_registers[accel_grp2_int_ctrl]); + if (err < 0) + return err; + } + + if(atomic_read(&acceld->accel_enabled) > 0) { + err = i2c_smbus_write_byte_data(acceld->client, + ACCEL_GRP2_CTRL_REG1, acceld->accel_registers[accel_grp2_ctrl_reg1] | ACCEL_GRP2_PC1_ON); + if (err < 0) + return err; + } + else { + err = i2c_smbus_write_byte_data(acceld->client, + ACCEL_GRP2_CTRL_REG1, acceld->accel_registers[accel_grp2_ctrl_reg1]); + if (err < 0) + return err; + } + + return 0; +} + +static int kionix_accel_grp2_operate(struct kionix_accel_driver *acceld) +{ + int err; + + err = i2c_smbus_write_byte_data(acceld->client, ACCEL_GRP2_CTRL_REG1, \ + acceld->accel_registers[accel_grp2_ctrl_reg1] | ACCEL_GRP2_PC1_ON); + if (err < 0) + return err; + + if(acceld->accel_drdy == 0) + queue_delayed_work(acceld->accel_workqueue, &acceld->accel_work, 0); + + return 0; +} + +static int kionix_accel_grp2_standby(struct kionix_accel_driver *acceld) +{ + int err; + + if(acceld->accel_drdy == 0) + cancel_delayed_work_sync(&acceld->accel_work); + + err = i2c_smbus_write_byte_data(acceld->client, ACCEL_GRP2_CTRL_REG1, 0); + if (err < 0) + return err; + + return 0; +} + +static void kionix_accel_grp2_report_accel_data(struct kionix_accel_driver *acceld) +{ + struct { union { + s16 accel_data_s16[3]; + s8 accel_data_s8[6]; + }; } accel_data; + s16 x, y, z; + int err; + struct input_dev *input_dev = acceld->input_dev; + int loop; + + /* Only read the output registers if enabled */ + if(atomic_read(&acceld->accel_enabled) > 0) { + if(atomic_read(&acceld->accel_enable_resume) > 0) + { + loop = KIONIX_I2C_RETRY_COUNT; + while(loop) { + //mutex_lock(&input_dev->mutex); + mutex_lock(&acceld->mutex_subinput); + err = kionix_i2c_read(acceld->client, ACCEL_GRP2_XOUT_L, (u8 *)accel_data.accel_data_s16, 6); + //mutex_unlock(&input_dev->mutex); + mutex_unlock(&acceld->mutex_subinput); + if(err < 0){ + loop--; + mdelay(KIONIX_I2C_RETRY_TIMEOUT); + } + else + loop = 0; + } + if (err < 0) { + KMSGERR(&acceld->client->dev, "%s: read data output error = %d\n", __func__, err); + } + else { + write_lock(&acceld->rwlock_accel_data); + + x = ((s16) le16_to_cpu(accel_data.accel_data_s16[acceld->axis_map_x])) >> acceld->shift; + y = ((s16) le16_to_cpu(accel_data.accel_data_s16[acceld->axis_map_y])) >> acceld->shift; + z = ((s16) le16_to_cpu(accel_data.accel_data_s16[acceld->axis_map_z])) >> acceld->shift; + + acceld->accel_data[acceld->axis_map_x] = (acceld->negate_x ? -x : x) + acceld->accel_cali[acceld->axis_map_x]; + acceld->accel_data[acceld->axis_map_y] = (acceld->negate_y ? -y : y) + acceld->accel_cali[acceld->axis_map_y]; + acceld->accel_data[acceld->axis_map_z] = (acceld->negate_z ? -z : z) + acceld->accel_cali[acceld->axis_map_z]; + + if(atomic_read(&acceld->accel_input_event) > 0) { + input_report_abs(acceld->input_dev, ABS_X, acceld->accel_data[acceld->axis_map_x]); + input_report_abs(acceld->input_dev, ABS_Y, acceld->accel_data[acceld->axis_map_y]); + input_report_abs(acceld->input_dev, ABS_Z, acceld->accel_data[acceld->axis_map_z]); + input_sync(acceld->input_dev); + } + + write_unlock(&acceld->rwlock_accel_data); + } + } + else + { + atomic_inc(&acceld->accel_enable_resume); + } + } + + /* Clear the interrupt if using drdy */ + if(acceld->accel_drdy == 1) { + loop = KIONIX_I2C_RETRY_COUNT; + while(loop) { + err = i2c_smbus_read_byte_data(acceld->client, ACCEL_GRP2_INT_REL); + if(err < 0){ + loop--; + mdelay(KIONIX_I2C_RETRY_TIMEOUT); + } + else + loop = 0; + } + if (err < 0) + KMSGERR(&acceld->client->dev, "%s: clear interrupt error = %d\n", __func__, err); + } +} + +static void kionix_accel_grp2_update_g_range(struct kionix_accel_driver *acceld) +{ + acceld->accel_registers[accel_grp2_ctrl_reg1] &= ~ACCEL_GRP2_G_MASK; + + switch (acceld->accel_pdata.accel_g_range) { + case KIONIX_ACCEL_G_8G: + case KIONIX_ACCEL_G_6G: + acceld->shift = 2; + acceld->accel_registers[accel_grp2_ctrl_reg1] |= ACCEL_GRP2_G_8G; + break; + case KIONIX_ACCEL_G_4G: + acceld->shift = 3; + acceld->accel_registers[accel_grp2_ctrl_reg1] |= ACCEL_GRP2_G_4G; + break; + case KIONIX_ACCEL_G_2G: + default: + acceld->shift = 4; + acceld->accel_registers[accel_grp2_ctrl_reg1] |= ACCEL_GRP2_G_2G; + break; + } + + return; +} + +static int kionix_accel_grp2_update_odr(struct kionix_accel_driver *acceld, unsigned int poll_interval) +{ + int err; + int i; + u8 odr; + + /* Use the lowest ODR that can support the requested poll interval */ + for (i = 0; i < ARRAY_SIZE(kionix_accel_grp2_odr_table); i++) { + odr = kionix_accel_grp2_odr_table[i].mask; + if (poll_interval < kionix_accel_grp2_odr_table[i].cutoff) + break; + } + + /* Do not need to update DATA_CTRL_REG register if the ODR is not changed */ + if(acceld->accel_registers[accel_grp2_data_ctrl] == odr) + return 0; + else + acceld->accel_registers[accel_grp2_data_ctrl] = odr; + + /* Do not need to update DATA_CTRL_REG register if the sensor is not currently turn on */ + if(atomic_read(&acceld->accel_enabled) > 0) { + err = i2c_smbus_write_byte_data(acceld->client, ACCEL_GRP2_CTRL_REG1, 0); + if (err < 0) + return err; + + err = i2c_smbus_write_byte_data(acceld->client, ACCEL_GRP2_DATA_CTRL, acceld->accel_registers[accel_grp2_data_ctrl]); + if (err < 0) + return err; + + err = i2c_smbus_write_byte_data(acceld->client, ACCEL_GRP2_CTRL_REG1, acceld->accel_registers[accel_grp2_ctrl_reg1] | ACCEL_GRP2_PC1_ON); + if (err < 0) + return err; + } + + return 0; +} + +static int kionix_accel_grp4_power_on_init(struct kionix_accel_driver *acceld) +{ + int err; + char rxData[2] = {0}; + /* ensure that PC1 is cleared before updating control registers */ + /*err = i2c_smbus_write_byte_data(acceld->client, + ACCEL_GRP4_CTRL_REG1, 0);*/ + + err = kionix_i2c_writebyte(acceld->client, + ACCEL_GRP4_CTRL_REG1, 0); + /*kionix_i2c_read(acceld->client,ACCEL_GRP4_CTRL_REG1,rxData,1); + printk(KERN_ERR"%d ,%s: ACCEL_GRP4_CTRL_REG1 is %d",__LINE__,__FUNCTION__,rxData[0]);*/ + + if (err < 0) + return err; + + /*err = i2c_smbus_write_byte_data(acceld->client, + ACCEL_GRP4_DATA_CTRL, acceld->accel_registers[accel_grp4_data_ctrl]);*/ + + err = kionix_i2c_writebyte(acceld->client, + ACCEL_GRP4_DATA_CTRL, acceld->accel_registers[accel_grp4_data_ctrl]); + /*kionix_i2c_read(acceld->client,ACCEL_GRP4_CTRL_REG1,rxData,1); + printk(KERN_ERR"%d,%s: ACCEL_GRP4_CTRL_REG1 now is %d,wanted value is %d",__LINE__,__FUNCTION__,rxData[0],rxData[1]);*/ + + if (err < 0) + return err; + + /* only write INT_CTRL_REG1 if in irq mode */ + if (acceld->client->irq) { + err = i2c_smbus_write_byte_data(acceld->client, + ACCEL_GRP4_INT_CTRL1, acceld->accel_registers[accel_grp4_int_ctrl]); + if (err < 0) + return err; + } + + if(atomic_read(&acceld->accel_enabled) > 0) { + /*err = i2c_smbus_write_byte_data(acceld->client, + ACCEL_GRP4_CTRL_REG1, acceld->accel_registers[accel_grp4_ctrl_reg1] | ACCEL_GRP4_PC1_ON);*/ + + err = kionix_i2c_writebyte(acceld->client, + ACCEL_GRP4_CTRL_REG1, acceld->accel_registers[accel_grp4_ctrl_reg1] | ACCEL_GRP4_PC1_ON); + /*kionix_i2c_read(acceld->client,ACCEL_GRP4_CTRL_REG1,rxData,1); + printk(KERN_ERR"%d,%s: ACCEL_GRP4_CTRL_REG1 now is %d,wanted value is %d",rxData[0],rxData[1]);*/ + + if (err < 0) + return err; + } + else { + /*err = i2c_smbus_write_byte_data(acceld->client, + ACCEL_GRP4_CTRL_REG1, acceld->accel_registers[accel_grp4_ctrl_reg1]);*/ + + err = kionix_i2c_writebyte(acceld->client, + ACCEL_GRP4_CTRL_REG1, acceld->accel_registers[accel_grp4_ctrl_reg1]); + /*kionix_i2c_read(acceld->client,ACCEL_GRP4_CTRL_REG1,rxData,1); + printk(KERN_ERR"%d,%s: ACCEL_GRP4_CTRL_REG1 now is %d,wanted value is %d",__LINE__,__FUNCTION__,rxData[0],acceld->accel_registers[accel_grp4_ctrl_reg1]);*/ + + if (err < 0) + return err; + } + + return 0; +} + +static int kionix_accel_grp4_operate(struct kionix_accel_driver *acceld) +{ + int err; + + /*err = i2c_smbus_write_byte_data(acceld->client, ACCEL_GRP4_CTRL_REG1, \ + acceld->accel_registers[accel_grp4_ctrl_reg1] | ACCEL_GRP4_PC1_ON);*/ + + err = kionix_i2c_writebyte(acceld->client, ACCEL_GRP4_CTRL_REG1, \ + acceld->accel_registers[accel_grp4_ctrl_reg1] | ACCEL_GRP4_PC1_ON); + if (err < 0) + return err; + + if(acceld->accel_drdy == 0) + queue_delayed_work(acceld->accel_workqueue, &acceld->accel_work, 0); + + return 0; +} + +static int kionix_accel_grp4_standby(struct kionix_accel_driver *acceld) +{ + int err; + + if(acceld->accel_drdy == 0) + cancel_delayed_work_sync(&acceld->accel_work); + + //err = i2c_smbus_write_byte_data(acceld->client, ACCEL_GRP4_CTRL_REG1, 0); + err = kionix_i2c_writebyte(acceld->client, ACCEL_GRP4_CTRL_REG1, 0); + if (err < 0) + return err; + + return 0; +} + +static void kionix_accel_grp4_report_accel_data(struct kionix_accel_driver *acceld) +{ + struct { union { + s16 accel_data_s16[3]; + s8 accel_data_s8[6]; + }; } accel_data; + s16 x, y, z; + int err; + struct input_dev *input_dev = acceld->input_dev; + int loop; + + /* Only read the output registers if enabled */ + if(atomic_read(&acceld->accel_enabled) > 0) { + if(atomic_read(&acceld->accel_enable_resume) > 0) + { + loop = KIONIX_I2C_RETRY_COUNT; + while(loop) { + //mutex_lock(&input_dev->mutex); + mutex_lock(&acceld->mutex_subinput); + err = kionix_i2c_read(acceld->client, ACCEL_GRP4_XOUT_L, (u8 *)accel_data.accel_data_s16, 6); + //mutex_unlock(&input_dev->mutex); + mutex_unlock(&acceld->mutex_subinput); + if(err < 0){ + loop--; + mdelay(KIONIX_I2C_RETRY_TIMEOUT); + } + else + loop = 0; + } + if (err < 0) { + KMSGERR(&acceld->client->dev, "%s: read data output error = %d\n", __func__, err); + } + else { + write_lock(&acceld->rwlock_accel_data); + + x = ((s16) le16_to_cpu(accel_data.accel_data_s16[acceld->axis_map_x])) >> acceld->shift; + y = ((s16) le16_to_cpu(accel_data.accel_data_s16[acceld->axis_map_y])) >> acceld->shift; + z = ((s16) le16_to_cpu(accel_data.accel_data_s16[acceld->axis_map_z])) >> acceld->shift; + + acceld->accel_data[acceld->axis_map_x] = (acceld->negate_x ? -x : x) + acceld->accel_cali[acceld->axis_map_x]; + acceld->accel_data[acceld->axis_map_y] = (acceld->negate_y ? -y : y) + acceld->accel_cali[acceld->axis_map_y]; + acceld->accel_data[acceld->axis_map_z] = (acceld->negate_z ? -z : z) + acceld->accel_cali[acceld->axis_map_z]; + + //printk(KERN_ERR"x:%d,y:%d,z:%d",x,y,z); + + if(atomic_read(&acceld->accel_input_event) > 0) { + input_report_abs(acceld->input_dev, ABS_X, acceld->accel_data[acceld->axis_map_x]); + input_report_abs(acceld->input_dev, ABS_Y, acceld->accel_data[acceld->axis_map_y]); + input_report_abs(acceld->input_dev, ABS_Z, acceld->accel_data[acceld->axis_map_z]); + input_sync(acceld->input_dev); + } + + write_unlock(&acceld->rwlock_accel_data); + } + } + else + { + atomic_inc(&acceld->accel_enable_resume); + } + } + + /* Clear the interrupt if using drdy */ + if(acceld->accel_drdy == 1) { + loop = KIONIX_I2C_RETRY_COUNT; + while(loop) { + err = i2c_smbus_read_byte_data(acceld->client, ACCEL_GRP4_INT_REL); + if(err < 0){ + loop--; + mdelay(KIONIX_I2C_RETRY_TIMEOUT); + } + else + loop = 0; + } + if (err < 0) + KMSGERR(&acceld->client->dev, "%s: clear interrupt error = %d\n", __func__, err); + } +} + +static void kionix_accel_grp4_update_g_range(struct kionix_accel_driver *acceld) +{ + acceld->accel_registers[accel_grp4_ctrl_reg1] &= ~ACCEL_GRP4_G_MASK; + //printk(KERN_ERR"kionix_accel_grp4_update_g_range is %d",acceld->accel_pdata.accel_g_range); + switch (acceld->accel_pdata.accel_g_range) { + case KIONIX_ACCEL_G_8G: + case KIONIX_ACCEL_G_6G: + //acceld->shift = 2; + acceld->accel_registers[accel_grp4_ctrl_reg1] |= ACCEL_GRP4_G_8G; + break; + case KIONIX_ACCEL_G_4G: + //acceld->shift = 4;//3; + acceld->accel_registers[accel_grp4_ctrl_reg1] |= ACCEL_GRP4_G_4G; + break; + case KIONIX_ACCEL_G_2G: + default: + //acceld->shift = 4; + acceld->accel_registers[accel_grp4_ctrl_reg1] |= ACCEL_GRP4_G_2G; + break; + } + + return; +} + +static int kionix_accel_grp4_update_odr(struct kionix_accel_driver *acceld, unsigned int poll_interval) +{ + int err; + int i; + u8 odr; + + /* Use the lowest ODR that can support the requested poll interval */ + for (i = 0; i < ARRAY_SIZE(kionix_accel_grp4_odr_table); i++) { + odr = kionix_accel_grp4_odr_table[i].mask; + if (poll_interval < kionix_accel_grp4_odr_table[i].cutoff) + break; + } + + /* Do not need to update DATA_CTRL_REG register if the ODR is not changed */ + if(acceld->accel_registers[accel_grp4_data_ctrl] == odr) + return 0; + else + acceld->accel_registers[accel_grp4_data_ctrl] = odr; + + /* Do not need to update DATA_CTRL_REG register if the sensor is not currently turn on */ + if(atomic_read(&acceld->accel_enabled) > 0) { + //err = i2c_smbus_write_byte_data(acceld->client, ACCEL_GRP4_CTRL_REG1, 0); + err = kionix_i2c_writebyte(acceld->client, ACCEL_GRP4_CTRL_REG1, 0); + + + if (err < 0) + return err; + + //err = i2c_smbus_write_byte_data(acceld->client, ACCEL_GRP4_DATA_CTRL, acceld->accel_registers[accel_grp4_data_ctrl]); + err = kionix_i2c_writebyte(acceld->client, ACCEL_GRP4_DATA_CTRL, acceld->accel_registers[accel_grp4_data_ctrl]); + if (err < 0) + return err; + + //err = i2c_smbus_write_byte_data(acceld->client, ACCEL_GRP4_CTRL_REG1, acceld->accel_registers[accel_grp4_ctrl_reg1] | ACCEL_GRP4_PC1_ON); + err = kionix_i2c_writebyte(acceld->client, ACCEL_GRP4_CTRL_REG1, acceld->accel_registers[accel_grp4_ctrl_reg1] | ACCEL_GRP4_PC1_ON); + if (err < 0) + return err; + //############# + err = i2c_smbus_read_byte_data(acceld->client, ACCEL_GRP4_DATA_CTRL); + if (err < 0) + return err; + switch(err) { + case ACCEL_GRP4_ODR0_781: + dev_info(&acceld->client->dev, "ODR = 0.781 Hz\n"); + break; + case ACCEL_GRP4_ODR1_563: + dev_info(&acceld->client->dev, "ODR = 1.563 Hz\n"); + break; + case ACCEL_GRP4_ODR3_125: + dev_info(&acceld->client->dev, "ODR = 3.125 Hz\n"); + break; + case ACCEL_GRP4_ODR6_25: + dev_info(&acceld->client->dev, "ODR = 6.25 Hz\n"); + break; + case ACCEL_GRP4_ODR12_5: + dev_info(&acceld->client->dev, "ODR = 12.5 Hz\n"); + break; + case ACCEL_GRP4_ODR25: + dev_info(&acceld->client->dev, "ODR = 25 Hz\n"); + break; + case ACCEL_GRP4_ODR50: + dev_info(&acceld->client->dev, "ODR = 50 Hz\n"); + break; + case ACCEL_GRP4_ODR100: + dev_info(&acceld->client->dev, "ODR = 100 Hz\n"); + break; + case ACCEL_GRP4_ODR200: + dev_info(&acceld->client->dev, "ODR = 200 Hz\n"); + break; + case ACCEL_GRP4_ODR400: + dev_info(&acceld->client->dev, "ODR = 400 Hz\n"); + break; + case ACCEL_GRP4_ODR800: + dev_info(&acceld->client->dev, "ODR = 800 Hz\n"); + break; + case ACCEL_GRP4_ODR1600: + dev_info(&acceld->client->dev, "ODR = 1600 Hz\n"); + break; + default: + dev_info(&acceld->client->dev, "Unknown ODR\n"); + break; + } + //############# + } + + return 0; +} + +static int kionix_accel_power_on(struct kionix_accel_driver *acceld) +{ + if (acceld->accel_pdata.power_on) + return acceld->accel_pdata.power_on(); + + return 0; +} + +static void kionix_accel_power_off(struct kionix_accel_driver *acceld) +{ + if (acceld->accel_pdata.power_off) + acceld->accel_pdata.power_off(); +} + +static irqreturn_t kionix_accel_isr(int irq, void *dev) +{ + struct kionix_accel_driver *acceld = dev; + + queue_delayed_work(acceld->accel_workqueue, &acceld->accel_work, 0); + + return IRQ_HANDLED; +} + +static void kionix_accel_work(struct work_struct *work) +{ + struct kionix_accel_driver *acceld = container_of((struct delayed_work *)work, struct kionix_accel_driver, accel_work); + + if(acceld->accel_drdy == 0) + queue_delayed_work(acceld->accel_workqueue, &acceld->accel_work, acceld->poll_delay); + + acceld->kionix_accel_report_accel_data(acceld); +} + +static void kionix_accel_update_direction(struct kionix_accel_driver *acceld) +{ + unsigned int direction = acceld->accel_pdata.accel_direction; + unsigned int accel_group = acceld->accel_group; + + write_lock(&acceld->rwlock_accel_data); + acceld->axis_map_x = ((direction-1)%2); + acceld->axis_map_y = (direction%2); + acceld->axis_map_z = 2; + acceld->negate_z = ((direction-1)/4); + switch(accel_group) { + case KIONIX_ACCEL_GRP3: + case KIONIX_ACCEL_GRP6: + acceld->negate_x = (((direction+2)/2)%2); + acceld->negate_y = (((direction+5)/4)%2); + break; + case KIONIX_ACCEL_GRP5: + acceld->axis_map_x = (direction%2); + acceld->axis_map_y = ((direction-1)%2); + acceld->negate_x = (((direction+1)/2)%2); + acceld->negate_y = (((direction/2)+((direction-1)/4))%2); + break; + default: + acceld->negate_x = ((direction/2)%2); + acceld->negate_y = (((direction+1)/4)%2); + break; + } + write_unlock(&acceld->rwlock_accel_data); + return; +} + +static int kionix_accel_enable(struct kionix_accel_driver *acceld) +{ + int err = 0; + long remaining; + + mutex_lock(&acceld->mutex_earlysuspend); + + atomic_set(&acceld->accel_suspend_continue, 0); + + /* Make sure that the sensor had successfully resumed before enabling it */ + if(atomic_read(&acceld->accel_suspended) == 1) { + KMSGINF(&acceld->client->dev, "%s: waiting for resume\n", __func__); + remaining = wait_event_interruptible_timeout(acceld->wqh_suspend, \ + atomic_read(&acceld->accel_suspended) == 0, \ + msecs_to_jiffies(KIONIX_ACCEL_EARLYSUSPEND_TIMEOUT)); + + if(atomic_read(&acceld->accel_suspended) == 1) { + KMSGERR(&acceld->client->dev, "%s: timeout waiting for resume\n", __func__); + err = -ETIME; + goto exit; + } + } + + err = acceld->kionix_accel_operate(acceld); + + if (err < 0) { + KMSGERR(&acceld->client->dev, \ + "%s: kionix_accel_operate returned err = %d\n", __func__, err); + goto exit; + } + + atomic_inc(&acceld->accel_enabled); + +exit: + mutex_unlock(&acceld->mutex_earlysuspend); + + return err; +} + +static int kionix_accel_disable(struct kionix_accel_driver *acceld) +{ + int err = 0; + + mutex_lock(&acceld->mutex_resume); + + atomic_set(&acceld->accel_suspend_continue, 1); + + if(atomic_read(&acceld->accel_enabled) > 0){ + if(atomic_dec_and_test(&acceld->accel_enabled)) { + if(atomic_read(&acceld->accel_enable_resume) > 0) + atomic_set(&acceld->accel_enable_resume, 0); + err = acceld->kionix_accel_standby(acceld); + if (err < 0) { + KMSGERR(&acceld->client->dev, \ + "%s: kionix_accel_standby returned err = %d\n", __func__, err); + goto exit; + } + wake_up_interruptible(&acceld->wqh_suspend); + } + } + +exit: + mutex_unlock(&acceld->mutex_resume); + + return err; +} + +static int kionix_accel_input_open(struct input_dev *input) +{ + struct kionix_accel_driver *acceld = input_get_drvdata(input); + + atomic_inc(&acceld->accel_input_event); + + return 0; +} + +static void kionix_accel_input_close(struct input_dev *dev) +{ + struct kionix_accel_driver *acceld = input_get_drvdata(dev); + + atomic_dec(&acceld->accel_input_event); +} + +static void __devinit kionix_accel_init_input_device(struct kionix_accel_driver *acceld, + struct input_dev *input_dev) +{ + __set_bit(EV_ABS, input_dev->evbit); + input_set_abs_params(input_dev, ABS_X, -ACCEL_G_MAX, ACCEL_G_MAX, ACCEL_FUZZ, ACCEL_FLAT); + input_set_abs_params(input_dev, ABS_Y, -ACCEL_G_MAX, ACCEL_G_MAX, ACCEL_FUZZ, ACCEL_FLAT); + input_set_abs_params(input_dev, ABS_Z, -ACCEL_G_MAX, ACCEL_G_MAX, ACCEL_FUZZ, ACCEL_FLAT); + + input_dev->name = "g-sensor";//KIONIX_ACCEL_NAME; + input_dev->id.bustype = BUS_I2C; + input_dev->dev.parent = &acceld->client->dev; +} + +static int __devinit kionix_accel_setup_input_device(struct kionix_accel_driver *acceld) +{ + struct input_dev *input_dev; + int err; + + input_dev = input_allocate_device(); + if (!input_dev) { + KMSGERR(&acceld->client->dev, "input_allocate_device failed\n"); + printk("kionix_accel_probe: Failed to allocate input device\n"); + return -ENOMEM; + } + + acceld->input_dev = input_dev; + + input_dev->open = kionix_accel_input_open; + input_dev->close = kionix_accel_input_close; + input_set_drvdata(input_dev, acceld); + + kionix_accel_init_input_device(acceld, input_dev); + + err = input_register_device(acceld->input_dev); + if (err) { + KMSGERR(&acceld->client->dev, \ + "%s: input_register_device returned err = %d\n", __func__, err); + printk("kionix_accel_probe: Failed to register input device\n"); + input_free_device(acceld->input_dev); + return err; + } + + return 0; +} + +/* Returns the enable state of device */ +static ssize_t kionix_accel_get_enable(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct kionix_accel_driver *acceld = i2c_get_clientdata(client); + + return sprintf(buf, "%d\n", atomic_read(&acceld->accel_enabled) > 0 ? 1 : 0); +} + +/* Allow users to enable/disable the device */ +static ssize_t kionix_accel_set_enable(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct kionix_accel_driver *acceld = i2c_get_clientdata(client); + struct input_dev *input_dev = acceld->input_dev; + char *buf2; + const int enable_count = 1; + unsigned long enable; + int err = 0; + + /* Lock the device to prevent races with open/close (and itself) */ + //mutex_lock(&input_dev->mutex); + mutex_lock(&acceld->mutex_subinput); + if(kionix_strtok(buf, count, &buf2, enable_count) < 0) { + KMSGERR(&acceld->client->dev, \ + "%s: No enable data being read. " \ + "No enable data will be updated.\n", __func__); + } + + else { + /* Removes any leading negative sign */ + while(*buf2 == '-') + buf2++; + #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,35)) + err = kstrtouint((const char *)buf2, 10, (unsigned int *)&enable); + if (err < 0) { + KMSGERR(&acceld->client->dev, \ + "%s: kstrtouint returned err = %d\n", __func__, err); + goto exit; + } + #else + err = strict_strtoul((const char *)buf2, 10, &enable); + if (err < 0) { + KMSGERR(&acceld->client->dev, \ + "%s: strict_strtoul returned err = %d\n", __func__, err); + goto exit; + } + #endif + + if(enable) + err = kionix_accel_enable(acceld); + else + err = kionix_accel_disable(acceld); + } + +exit: + //mutex_unlock(&input_dev->mutex); + mutex_unlock(&acceld->mutex_subinput); + return (err < 0) ? err : count; +} + +/* Returns currently selected poll interval (in ms) */ +static ssize_t kionix_accel_get_delay(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct kionix_accel_driver *acceld = i2c_get_clientdata(client); + + return sprintf(buf, "%d\n", acceld->poll_interval); +} + +/* Allow users to select a new poll interval (in ms) */ +static ssize_t kionix_accel_set_delay(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct kionix_accel_driver *acceld = i2c_get_clientdata(client); + struct input_dev *input_dev = acceld->input_dev; + char *buf2; + const int delay_count = 1; + unsigned long interval; + int err = 0; + + /* Lock the device to prevent races with open/close (and itself) */ + //mutex_lock(&input_dev->mutex); + mutex_lock(&acceld->mutex_subinput); + if(kionix_strtok(buf, count, &buf2, delay_count) < 0) { + KMSGERR(&acceld->client->dev, \ + "%s: No delay data being read. " \ + "No delay data will be updated.\n", __func__); + } + + else { + /* Removes any leading negative sign */ + while(*buf2 == '-') + buf2++; + #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,35)) + err = kstrtouint((const char *)buf2, 10, (unsigned int *)&interval); + if (err < 0) { + KMSGERR(&acceld->client->dev, \ + "%s: kstrtouint returned err = %d\n", __func__, err); + goto exit; + } + #else + err = strict_strtoul((const char *)buf2, 10, &interval); + if (err < 0) { + KMSGERR(&acceld->client->dev, \ + "%s: strict_strtoul returned err = %d\n", __func__, err); + goto exit; + } + #endif + + if(acceld->accel_drdy == 1) + disable_irq(client->irq); + + /* + * Set current interval to the greater of the minimum interval or + * the requested interval + */ + acceld->poll_interval = max((unsigned int)interval, acceld->accel_pdata.min_interval); + acceld->poll_delay = msecs_to_jiffies(acceld->poll_interval); + + err = acceld->kionix_accel_update_odr(acceld, acceld->poll_interval); + + if(acceld->accel_drdy == 1) + enable_irq(client->irq); + } + +exit: + //mutex_unlock(&input_dev->mutex); + mutex_unlock(&acceld->mutex_subinput); + + return (err < 0) ? err : count; +} + +/* Returns the direction of device */ +static ssize_t kionix_accel_get_direct(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct kionix_accel_driver *acceld = i2c_get_clientdata(client); + + return sprintf(buf, "%d\n", acceld->accel_pdata.accel_direction); +} + +/* Allow users to change the direction the device */ +static ssize_t kionix_accel_set_direct(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct kionix_accel_driver *acceld = i2c_get_clientdata(client); + struct input_dev *input_dev = acceld->input_dev; + char *buf2; + const int direct_count = 1; + unsigned long direction; + int err = 0; + + /* Lock the device to prevent races with open/close (and itself) */ + //mutex_lock(&input_dev->mutex); + mutex_lock(&acceld->mutex_subinput); + if(kionix_strtok(buf, count, &buf2, direct_count) < 0) { + KMSGERR(&acceld->client->dev, \ + "%s: No direction data being read. " \ + "No direction data will be updated.\n", __func__); + } + + else { + /* Removes any leading negative sign */ + while(*buf2 == '-') + buf2++; + #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,35)) + err = kstrtouint((const char *)buf2, 10, (unsigned int *)&direction); + if (err < 0) { + KMSGERR(&acceld->client->dev, \ + "%s: kstrtouint returned err = %d\n", __func__, err); + goto exit; + } + #else + err = strict_strtoul((const char *)buf2, 10, &direction); + if (err < 0) { + KMSGERR(&acceld->client->dev, \ + "%s: strict_strtoul returned err = %d\n", __func__, err); + goto exit; + } + #endif + + if(direction < 1 || direction > 8) + KMSGERR(&acceld->client->dev, "%s: invalid direction = %d\n", __func__, (unsigned int) direction); + + else { + acceld->accel_pdata.accel_direction = (u8) direction; + kionix_accel_update_direction(acceld); + } + } + +exit: + //mutex_unlock(&input_dev->mutex); + mutex_unlock(&acceld->mutex_subinput); + return (err < 0) ? err : count; +} + +/* Returns the data output of device */ +static ssize_t kionix_accel_get_data(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct kionix_accel_driver *acceld = i2c_get_clientdata(client); + int x, y, z; + + read_lock(&acceld->rwlock_accel_data); + + x = acceld->accel_data[acceld->axis_map_x]; + y = acceld->accel_data[acceld->axis_map_y]; + z = acceld->accel_data[acceld->axis_map_z]; + + read_unlock(&acceld->rwlock_accel_data); + + return sprintf(buf, "%d %d %d\n", x, y, z); +} + +/* Returns the calibration value of the device */ +static ssize_t kionix_accel_get_cali(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct kionix_accel_driver *acceld = i2c_get_clientdata(client); + int calibration[3]; + + read_lock(&acceld->rwlock_accel_data); + + calibration[0] = acceld->accel_cali[acceld->axis_map_x]; + calibration[1] = acceld->accel_cali[acceld->axis_map_y]; + calibration[2] = acceld->accel_cali[acceld->axis_map_z]; + + read_unlock(&acceld->rwlock_accel_data); + + return sprintf(buf, "%d %d %d\n", calibration[0], calibration[1], calibration[2]); +} + +/* Allow users to change the calibration value of the device */ +static ssize_t kionix_accel_set_cali(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct kionix_accel_driver *acceld = i2c_get_clientdata(client); + struct input_dev *input_dev = acceld->input_dev; + const int cali_count = 3; /* How many calibration that we expect to get from the string */ + char **buf2; + long calibration[cali_count]; + int err = 0, i = 0; + + /* Lock the device to prevent races with open/close (and itself) */ + //mutex_lock(&input_dev->mutex); + mutex_lock(&acceld->mutex_subinput); + buf2 = (char **)kzalloc(cali_count * sizeof(char *), GFP_KERNEL); + + if(kionix_strtok(buf, count, buf2, cali_count) < 0) { + KMSGERR(&acceld->client->dev, \ + "%s: Not enough calibration data being read. " \ + "No calibration data will be updated.\n", __func__); + } + else { + /* Convert string to integers */ + for(i = 0 ; i < cali_count ; i++) { + #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,35)) + err = kstrtoint((const char *)*(buf2+i), 10, (int *)&calibration[i]); + if(err < 0) { + KMSGERR(&acceld->client->dev, \ + "%s: kstrtoint returned err = %d." \ + "No calibration data will be updated.\n", __func__ , err); + goto exit; + } + #else + err = strict_strtol((const char *)*(buf2+i), 10, &calibration[i]); + if(err < 0) { + KMSGERR(&acceld->client->dev, \ + "%s: strict_strtol returned err = %d." \ + "No calibration data will be updated.\n", __func__ , err); + goto exit; + } + #endif + } + + write_lock(&acceld->rwlock_accel_data); + + acceld->accel_cali[acceld->axis_map_x] = (int)calibration[0]; + acceld->accel_cali[acceld->axis_map_y] = (int)calibration[1]; + acceld->accel_cali[acceld->axis_map_z] = (int)calibration[2]; + + write_unlock(&acceld->rwlock_accel_data); + } + +exit: + for(i = 0 ; i < cali_count ; i++) + kfree(*(buf2+i)); + + kfree(buf2); + + //mutex_unlock(&input_dev->mutex); + mutex_unlock(&acceld->mutex_subinput); + + return (err < 0) ? err : count; +} + +static DEVICE_ATTR(enable, S_IRUGO|S_IWUSR, kionix_accel_get_enable, kionix_accel_set_enable); +static DEVICE_ATTR(delay, S_IRUGO|S_IWUSR, kionix_accel_get_delay, kionix_accel_set_delay); +static DEVICE_ATTR(direct, S_IRUGO|S_IWUSR, kionix_accel_get_direct, kionix_accel_set_direct); +static DEVICE_ATTR(data, S_IRUGO, kionix_accel_get_data, NULL); +static DEVICE_ATTR(cali, S_IRUGO|S_IWUSR, kionix_accel_get_cali, kionix_accel_set_cali); + +static struct attribute *kionix_accel_attributes[] = { + &dev_attr_enable.attr, + &dev_attr_delay.attr, + &dev_attr_direct.attr, + &dev_attr_data.attr, + &dev_attr_cali.attr, + NULL +}; + +static struct attribute_group kionix_accel_attribute_group = { + .attrs = kionix_accel_attributes +}; + +static int kionix_chip_id[] ={ + KIONIX_ACCEL_WHO_AM_I_KXTE9, + KIONIX_ACCEL_WHO_AM_I_KXTF9, + KIONIX_ACCEL_WHO_AM_I_KXTI9_1001, + KIONIX_ACCEL_WHO_AM_I_KXTIK_1004, + KIONIX_ACCEL_WHO_AM_I_KXTJ9_1005, + KIONIX_ACCEL_WHO_AM_I_KXTJ9_1007, + KIONIX_ACCEL_WHO_AM_I_KXCJ9_1008, + KIONIX_ACCEL_WHO_AM_I_KXTJ2_1009, + KIONIX_ACCEL_WHO_AM_I_KXCJK_1013 +}; + +static int iskionix() +{ + char rxData[2] = {0}; + int ret = 0; + int i = 0; + + ret = kionix_i2c_read(this_client,ACCEL_WHO_AM_I,rxData,1); //maybe should 2 success // -5 ioerror!! + printk(KERN_ERR"<<<<%s ret:%d val 0x%x\n", __FUNCTION__, ret, rxData[0]); + if (ret <= 0) // 2 ? + { + return -1; + } + for(i = 0 ; i < sizeof(kionix_chip_id)/sizeof(kionix_chip_id[0]);i++) + if(rxData[0] == kionix_chip_id[i]) + return 0; + + return -1; +} + +static int __devinit kionix_verify(struct kionix_accel_driver *acceld) +{ + int retval = i2c_smbus_read_byte_data(acceld->client, ACCEL_WHO_AM_I); + +#if KIONIX_KMSG_INF + switch (retval) { + case KIONIX_ACCEL_WHO_AM_I_KXTE9: + KMSGINF(&acceld->client->dev, "this accelerometer is a KXTE9.\n"); + break; + case KIONIX_ACCEL_WHO_AM_I_KXTF9: + KMSGINF(&acceld->client->dev, "this accelerometer is a KXTF9.\n"); + break; + case KIONIX_ACCEL_WHO_AM_I_KXTI9_1001: + KMSGINF(&acceld->client->dev, "this accelerometer is a KXTI9-1001.\n"); + break; + case KIONIX_ACCEL_WHO_AM_I_KXTIK_1004: + KMSGINF(&acceld->client->dev, "this accelerometer is a KXTIK-1004.\n"); + break; + case KIONIX_ACCEL_WHO_AM_I_KXTJ9_1005: + KMSGINF(&acceld->client->dev, "this accelerometer is a KXTJ9-1005.\n"); + break; + case KIONIX_ACCEL_WHO_AM_I_KXTJ9_1007: + KMSGINF(&acceld->client->dev, "this accelerometer is a KXTJ9-1007.\n"); + break; + case KIONIX_ACCEL_WHO_AM_I_KXCJ9_1008: + KMSGINF(&acceld->client->dev, "this accelerometer is a KXCJ9-1008.\n"); + break; + case KIONIX_ACCEL_WHO_AM_I_KXTJ2_1009: + KMSGINF(&acceld->client->dev, "this accelerometer is a KXTJ2-1009.\n"); + break; + case KIONIX_ACCEL_WHO_AM_I_KXCJK_1013: + KMSGINF(&acceld->client->dev, "this accelerometer is a KXCJK-1013.\n"); + break; + default: + break; + } +#endif + + return retval; +} + +#ifdef CONFIG_HAS_EARLYSUSPEND +void kionix_accel_earlysuspend_suspend(struct early_suspend *h) +{ + struct kionix_accel_driver *acceld = container_of(h, struct kionix_accel_driver, early_suspend); + long remaining; + + mutex_lock(&acceld->mutex_earlysuspend); + + /* Only continue to suspend if enable did not intervene */ + if(atomic_read(&acceld->accel_suspend_continue) > 0) { + /* Make sure that the sensor had successfully disabled before suspending it */ + if(atomic_read(&acceld->accel_enabled) > 0) { + KMSGINF(&acceld->client->dev, "%s: waiting for disable\n", __func__); + remaining = wait_event_interruptible_timeout(acceld->wqh_suspend, \ + atomic_read(&acceld->accel_enabled) < 1, \ + msecs_to_jiffies(KIONIX_ACCEL_EARLYSUSPEND_TIMEOUT)); + + if(atomic_read(&acceld->accel_enabled) > 0) { + KMSGERR(&acceld->client->dev, "%s: timeout waiting for disable\n", __func__); + } + } + + kionix_accel_power_off(acceld); + + atomic_set(&acceld->accel_suspended, 1); + } + + mutex_unlock(&acceld->mutex_earlysuspend); + + return; +} + +void kionix_accel_earlysuspend_resume(struct early_suspend *h) +{ + struct kionix_accel_driver *acceld = container_of(h, struct kionix_accel_driver, early_suspend); + int err; + + mutex_lock(&acceld->mutex_resume); + + if(atomic_read(&acceld->accel_suspended) == 1) { + err = kionix_accel_power_on(acceld); + if (err < 0) { + KMSGERR(&acceld->client->dev, "%s: kionix_accel_power_on returned err = %d\n", __func__, err); + goto exit; + } + + /* Only needs to reinitialized the registers if Vdd is pulled low during suspend */ + if(err > 0) { + err = acceld->kionix_accel_power_on_init(acceld); + if (err) { + KMSGERR(&acceld->client->dev, "%s: kionix_accel_power_on_init returned err = %d\n", __func__, err); + goto exit; + } + } + + atomic_set(&acceld->accel_suspended, 0); + } + + wake_up_interruptible(&acceld->wqh_suspend); + +exit: + mutex_unlock(&acceld->mutex_resume); + + return; +} +#endif /* CONFIG_HAS_EARLYSUSPEND */ + +static int kionix_open(struct inode *inode, struct file *file) +{ + struct kionix_accel_driver *acceld = i2c_get_clientdata(this_client); + //KMSGINF("Open the g-sensor node...\n"); + kionix_accel_input_open(acceld->input_dev); + return 0; +} + +static int kionix_release(struct inode *inode, struct file *file) +{ + struct kionix_accel_driver *acceld = i2c_get_clientdata(this_client); + //KMSGINF("Close the g-sensor node...\n"); + kionix_accel_input_close(acceld->input_dev); + return 0; +} + +static long +kionix_ioctl(/*struct inode *inode,*/ struct file *file, unsigned int cmd, + unsigned long arg) +{ + void __user *argp = (void __user *)arg; + char rwbuf[5]; + short delay, enable; //amsr = -1; + unsigned int uval = 0; + + struct kionix_accel_driver *acceld = i2c_get_clientdata(this_client); + + //KMSGINF("g-sensor ioctr...\n"); + memset(rwbuf, 0, sizeof(rwbuf)); + switch (cmd) { + case ECS_IOCTL_APP_SET_DELAY: + // set the rate of g-sensor + if (copy_from_user(&delay, argp, sizeof(short))) + { + printk(KERN_ALERT "Can't get set delay!!!\n"); + return -EFAULT; + } + klog("Get delay=%d\n", delay); + + if ((delay >=0) && (delay < 20)) + { + delay = 20; + } else if (delay > 200) + { + delay = 200; + } + //l_sensorconfig.sensor_samp = 1000/delay; + acceld->poll_interval = 1000/delay; + acceld->poll_delay = msecs_to_jiffies(acceld->poll_interval); + acceld->kionix_accel_update_odr(acceld, acceld->poll_interval); + break; + case ECS_IOCTL_APP_SET_AFLAG: + // enable/disable sensor + if (copy_from_user(&enable, argp, sizeof(short))) + { + printk(KERN_ERR "Can't get enable flag!!!\n"); + return -EFAULT; + } + klog("enable=%d\n",enable); + if ((enable >=0) && (enable <=1)) + { + //KMSGINF("driver: disable/enable(%d) gsensor.\n", enable); + + //l_sensorconfig.sensor_enable = enable; + if(enable) + kionix_accel_enable(acceld); + else + kionix_accel_disable(acceld); + + } else { + printk(KERN_ERR "Wrong enable argument in %s !!!\n", __FUNCTION__); + return -EINVAL; + } + break; + case WMT_IOCTL_SENSOR_GET_DRVID: + uval = KIONIX_DRVID; + if (copy_to_user((unsigned int*)arg, &uval, sizeof(unsigned int))) + { + return -EFAULT; + } + //KMSGINF("kionix_driver_id:%d\n",uval); + break; + case WMT_IOCTL_SENOR_GET_RESOLUTION: + + uval = (12<<8) | 8; // 8bit:4g 0xxx xx //mma8452Q + if (copy_to_user((unsigned int *)arg, &uval, sizeof(unsigned int))) + { + return -EFAULT; + } + printk("<<<<<<dev.platform_data; + struct kionix_accel_driver *acceld; + int err; + struct proc_dir_entry *proc_dir, *proc_entry; +/* + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_I2C | I2C_FUNC_SMBUS_BYTE_DATA)) { + KMSGERR(&client->dev, "client is not i2c capable. Abort.\n"); + return -ENXIO; + } +*/ + if (!accel_pdata) { + KMSGERR(&this_client->dev, "platform data is NULL. Abort.\n"); + return -EINVAL; + } + + acceld = kzalloc(sizeof(*acceld), GFP_KERNEL); + if (acceld == NULL) { + KMSGERR(&this_client->dev, \ + "failed to allocate memory for module data. Abort.\n"); + return -ENOMEM; + } + + acceld->client = this_client; + acceld->accel_pdata = *accel_pdata; + + i2c_set_clientdata(this_client, acceld); + + err = kionix_accel_power_on(acceld); + if (err < 0) + goto err_free_mem; + + if (accel_pdata->init) { + err = accel_pdata->init(); + if (err < 0) + goto err_accel_pdata_power_off; + } + + err = kionix_verify(acceld); + if (err < 0) { + KMSGERR(&acceld->client->dev, "%s: kionix_verify returned err = %d. Abort.\n", __func__, err); + goto err_accel_pdata_exit; + } + + /* Setup group specific configuration and function callback */ + switch (err) { + case KIONIX_ACCEL_WHO_AM_I_KXTE9: + acceld->accel_group = KIONIX_ACCEL_GRP1; + acceld->accel_registers = kzalloc(sizeof(u8)*accel_grp1_regs_count, GFP_KERNEL); + if (acceld->accel_registers == NULL) { + KMSGERR(&this_client->dev, \ + "failed to allocate memory for accel_registers. Abort.\n"); + goto err_accel_pdata_exit; + } + acceld->accel_drdy = 0; + acceld->kionix_accel_report_accel_data = kionix_accel_grp1_report_accel_data; + acceld->kionix_accel_update_odr = kionix_accel_grp1_update_odr; + acceld->kionix_accel_power_on_init = kionix_accel_grp1_power_on_init; + acceld->kionix_accel_operate = kionix_accel_grp1_operate; + acceld->kionix_accel_standby = kionix_accel_grp1_standby; + break; + case KIONIX_ACCEL_WHO_AM_I_KXTF9: + case KIONIX_ACCEL_WHO_AM_I_KXTI9_1001: + case KIONIX_ACCEL_WHO_AM_I_KXTIK_1004: + case KIONIX_ACCEL_WHO_AM_I_KXTJ9_1005: + if(err == KIONIX_ACCEL_WHO_AM_I_KXTIK_1004) + acceld->accel_group = KIONIX_ACCEL_GRP3; + else + acceld->accel_group = KIONIX_ACCEL_GRP2; + acceld->accel_registers = kzalloc(sizeof(u8)*accel_grp2_regs_count, GFP_KERNEL); + if (acceld->accel_registers == NULL) { + KMSGERR(&this_client->dev, \ + "failed to allocate memory for accel_registers. Abort.\n"); + goto err_accel_pdata_exit; + } + switch(acceld->accel_pdata.accel_res) { + case KIONIX_ACCEL_RES_6BIT: + case KIONIX_ACCEL_RES_8BIT: + acceld->accel_registers[accel_grp2_ctrl_reg1] |= ACCEL_GRP2_RES_8BIT; + break; + case KIONIX_ACCEL_RES_12BIT: + default: + acceld->accel_registers[accel_grp2_ctrl_reg1] |= ACCEL_GRP2_RES_12BIT; + break; + } + if(acceld->accel_pdata.accel_irq_use_drdy && this_client->irq) { + acceld->accel_registers[accel_grp2_int_ctrl] |= ACCEL_GRP2_IEN | ACCEL_GRP2_IEA; + acceld->accel_registers[accel_grp2_ctrl_reg1] |= ACCEL_GRP2_DRDYE; + acceld->accel_drdy = 1; + } + else + acceld->accel_drdy = 0; + kionix_accel_grp2_update_g_range(acceld); + acceld->kionix_accel_report_accel_data = kionix_accel_grp2_report_accel_data; + acceld->kionix_accel_update_odr = kionix_accel_grp2_update_odr; + acceld->kionix_accel_power_on_init = kionix_accel_grp2_power_on_init; + acceld->kionix_accel_operate = kionix_accel_grp2_operate; + acceld->kionix_accel_standby = kionix_accel_grp2_standby; + break; + case KIONIX_ACCEL_WHO_AM_I_KXTJ9_1007: + case KIONIX_ACCEL_WHO_AM_I_KXCJ9_1008: + case KIONIX_ACCEL_WHO_AM_I_KXTJ2_1009: + case KIONIX_ACCEL_WHO_AM_I_KXCJK_1013: + if(err == KIONIX_ACCEL_WHO_AM_I_KXTJ2_1009) + acceld->accel_group = KIONIX_ACCEL_GRP5; + else if(err == KIONIX_ACCEL_WHO_AM_I_KXCJK_1013) + acceld->accel_group = KIONIX_ACCEL_GRP6; + else + acceld->accel_group = KIONIX_ACCEL_GRP4; + acceld->accel_registers = kzalloc(sizeof(u8)*accel_grp4_regs_count, GFP_KERNEL); + if (acceld->accel_registers == NULL) { + KMSGERR(&this_client->dev, \ + "failed to allocate memory for accel_registers. Abort.\n"); + goto err_accel_pdata_exit; + } + switch(acceld->accel_pdata.accel_res) { + case KIONIX_ACCEL_RES_6BIT: + case KIONIX_ACCEL_RES_8BIT: + acceld->shift = 0; + acceld->accel_registers[accel_grp4_ctrl_reg1] |= ACCEL_GRP4_RES_8BIT; + break; + case KIONIX_ACCEL_RES_12BIT: + acceld->shift = 4; + default: + acceld->accel_registers[accel_grp4_ctrl_reg1] |= ACCEL_GRP4_RES_12BIT; + break; + } + if(acceld->accel_pdata.accel_irq_use_drdy && this_client->irq) { + acceld->accel_registers[accel_grp4_int_ctrl] |= ACCEL_GRP4_IEN | ACCEL_GRP4_IEA; + acceld->accel_registers[accel_grp4_ctrl_reg1] |= ACCEL_GRP4_DRDYE; + acceld->accel_drdy = 1; + } + else + acceld->accel_drdy = 0; + kionix_accel_grp4_update_g_range(acceld); + acceld->kionix_accel_report_accel_data = kionix_accel_grp4_report_accel_data; + acceld->kionix_accel_update_odr = kionix_accel_grp4_update_odr; + acceld->kionix_accel_power_on_init = kionix_accel_grp4_power_on_init; + acceld->kionix_accel_operate = kionix_accel_grp4_operate; + acceld->kionix_accel_standby = kionix_accel_grp4_standby; + break; + default: + KMSGERR(&acceld->client->dev, \ + "%s: unsupported device, who am i = %d. Abort.\n", __func__, err); + goto err_accel_pdata_exit; + } + + err = kionix_accel_setup_input_device(acceld); + if (err) + goto err_free_accel_registers; + +//add + /*this_pdev = pdev; + l_sensorconfig.input_dev = acceld->input_dev;*/ + + err = misc_register(&kionix_device); + if (err) { + printk(KERN_ERR + "kionix_accel_probe: kionix_device register failed\n"); + goto exit_misc_device_register_failed; + } + + //dev_set_drvdata(&pdev->dev, &l_sensorconfig); +//end add + + + atomic_set(&acceld->accel_suspended, 0); + atomic_set(&acceld->accel_suspend_continue, 1); + atomic_set(&acceld->accel_enabled, 0); + atomic_set(&acceld->accel_input_event, 0); + atomic_set(&acceld->accel_enable_resume, 0); + + mutex_init(&acceld->mutex_earlysuspend); + mutex_init(&acceld->mutex_resume); + + mutex_init(&acceld->mutex_subinput);//add 2014-6-12 + + rwlock_init(&acceld->rwlock_accel_data); + + acceld->poll_interval = acceld->accel_pdata.poll_interval; + acceld->poll_delay = msecs_to_jiffies(acceld->poll_interval); + acceld->kionix_accel_update_odr(acceld, acceld->poll_interval); + get_axisset(acceld);//kionix_accel_update_direction(acceld); + + proc_dir = proc_mkdir("sensors", NULL); + if (proc_dir == NULL) + KMSGERR(&this_client->dev, "failed to create /proc/sensors\n"); + else { + proc_entry = create_proc_entry( "accelinfo", 0644, proc_dir); + if (proc_entry == NULL) + KMSGERR(&this_client->dev, "failed to create /proc/cpu/accelinfo\n"); + } + +/* + proc_dir = create_proc_entry(GSENSOR_PROC_NAME, 0666, NULL);//&proc_root + if (proc_dir != NULL) + { + proc_dir->write_proc = sensor_writeproc; + proc_dir->read_proc = sensor_readproc; + } +*/ + acceld->accel_workqueue = create_singlethread_workqueue("Kionix Accel Workqueue"); + INIT_DELAYED_WORK(&acceld->accel_work, kionix_accel_work); + init_waitqueue_head(&acceld->wqh_suspend); + + if (acceld->accel_drdy) { + err = request_threaded_irq(this_client->irq, NULL, kionix_accel_isr, \ + IRQF_TRIGGER_RISING | IRQF_ONESHOT, \ + KIONIX_ACCEL_IRQ, acceld); + if (err) { + KMSGERR(&acceld->client->dev, "%s: request_threaded_irq returned err = %d\n", __func__, err); + KMSGERR(&acceld->client->dev, "%s: running in software polling mode instead\n", __func__); + acceld->accel_drdy = 0; + } + KMSGINF(&acceld->client->dev, "running in hardware interrupt mode\n"); + } else { + KMSGINF(&acceld->client->dev, "running in software polling mode\n"); + } + + err = acceld->kionix_accel_power_on_init(acceld); + if (err) { + KMSGERR(&acceld->client->dev, "%s: kionix_accel_power_on_init returned err = %d. Abort.\n", __func__, err); + goto err_free_irq; + } + + err = sysfs_create_group(&this_client->dev.kobj, &kionix_accel_attribute_group); + if (err) { + KMSGERR(&acceld->client->dev, "%s: sysfs_create_group returned err = %d. Abort.\n", __func__, err); + goto err_free_irq; + } + +#ifdef CONFIG_HAS_EARLYSUSPEND + /* The higher the level, the earlier it resume, and the later it suspend */ + acceld->early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB + 50; + acceld->early_suspend.suspend = kionix_accel_earlysuspend_suspend; + acceld->early_suspend.resume = kionix_accel_earlysuspend_resume; + register_early_suspend(&acceld->early_suspend); +#endif /* CONFIG_HAS_EARLYSUSPEND */ + + + // satrt the polling work + if(acceld->accel_drdy == 0) + queue_delayed_work(acceld->accel_workqueue, &acceld->accel_work, acceld->poll_delay); + return 0; + +exit_misc_device_register_failed: +err_free_irq: + if (acceld->accel_drdy) + free_irq(this_client->irq, acceld); + destroy_workqueue(acceld->accel_workqueue); + input_unregister_device(acceld->input_dev); +err_free_accel_registers: + kfree(acceld->accel_registers); +err_accel_pdata_exit: + if (accel_pdata->exit) + accel_pdata->exit(); +err_accel_pdata_power_off: + kionix_accel_power_off(acceld); +err_free_mem: + kfree(acceld); +exit_input_dev_alloc_failed: + return err; +} + +static int kionix_accel_remove(struct platform_device *pdev) +{ + struct kionix_accel_driver *acceld = i2c_get_clientdata(this_client); + +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&acceld->early_suspend); +#endif /* CONFIG_HAS_EARLYSUSPEND */ + if (NULL != acceld->accel_workqueue) + { + cancel_delayed_work_sync(&acceld->accel_work); + flush_workqueue(acceld->accel_workqueue); + destroy_workqueue(acceld->accel_workqueue); + acceld->accel_workqueue = NULL; + } + sysfs_remove_group(&this_client->dev.kobj, &kionix_accel_attribute_group); + if (acceld->accel_drdy) + free_irq(this_client->irq, acceld); + //destroy_workqueue(acceld->accel_workqueue); + misc_deregister(&kionix_device); + input_unregister_device(acceld->input_dev); + kfree(acceld->accel_registers); + if (acceld->accel_pdata.exit) + acceld->accel_pdata.exit(); + kionix_accel_power_off(acceld); + kfree(acceld); + + return 0; +} + +static int __devexit kionix_accel_i2cremove(struct i2c_client *client) +{ + return 0; +} + +static const struct i2c_device_id kionix_accel_id[] = { + { KIONIX_ACCEL_NAME, 0 }, + { }, +}; + +MODULE_DEVICE_TABLE(i2c, kionix_accel_id); + +static struct i2c_driver kionix_accel_driver = { + .driver = { + .name = KIONIX_ACCEL_NAME, + .owner = THIS_MODULE, + }, + .probe = kionix_accel_probe, + .remove = __devexit_p(kionix_accel_i2cremove), + .id_table = kionix_accel_id, +}; + + +static struct platform_device kionix_pdevice = { + .name = "kionix", + .id = 0, + /*.dev = { + //.release = mma8452q_platform_release, + },*/ +}; + +//************ +static void kionix_accel_shutdown(struct platform_device *pdev) +{ + struct kionix_accel_driver *acceld = NULL; + acceld = i2c_get_clientdata(this_client); + if (acceld) { + printk("<<<<<%s\n", __func__); + flush_delayed_work_sync(&acceld->accel_work); + cancel_delayed_work_sync(&acceld->accel_work); + } + +} +//****add for resume dpm timeout 2014-6-12 +static int kionix_accel_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct kionix_accel_driver *acceld = NULL; + acceld = i2c_get_clientdata(this_client); + if (acceld) { + printk("<<<<<%s\n", __func__); + flush_delayed_work_sync(&acceld->accel_work); + cancel_delayed_work_sync(&acceld->accel_work); + } +} + +int kionix_accel_resume(struct platform_device *pdev) +{ + struct kionix_accel_driver *acceld = NULL; + acceld = i2c_get_clientdata(this_client); + if (acceld) { + printk("<<<<<%s\n", __func__); + queue_delayed_work(acceld->accel_workqueue, &acceld->accel_work, acceld->poll_delay); + } +} +//*********************** +static struct platform_driver kionix_driver = { + .probe = kionix_accel_probe, + .remove = kionix_accel_remove, + .shutdown = kionix_accel_shutdown, + .suspend = kionix_accel_suspend, + .resume = kionix_accel_resume, + .driver = { + .name = "kionix", + }, +}; + + +static struct class* l_dev_class = NULL; +static struct device *l_clsdevice = NULL; +static int __init kionix_accel_init(void) +{ + //return i2c_add_driver(&kionix_accel_driver); + + int ret = 0; + struct kionix_accel_driver *acceld; + + acceld = kzalloc(sizeof(*acceld), GFP_KERNEL); + if (acceld == NULL) { + printk("%s kzalloc fail!\n", __func__); + return -ENOMEM; + } + + ret = get_axisset(acceld);// + if (ret < 0) + { + printk("%s user choose to no sensor chip!\n", __func__); + kfree(acceld); + return ret; + } + kfree(acceld); + + if (!(this_client = sensor_i2c_register_device2(0, KIONIX_ACCEL_I2C_ADDR, KIONIX_ACCEL_NAME,(void*)(&kionix_accel_pdata)))) + { + printk(KERN_EMERG"Can't register gsensor i2c device!\n"); + return -1; + } + + + if(iskionix()) + { + printk(KERN_ERR "Can't find kionix!!\n"); + sensor_i2c_unregister_device(this_client); + return -1; + } + + //ret = get_axisset(NULL); + + printk("kionix g-sensor driver init\n"); + + //spin_lock_init(&l_sensorconfig.spinlock); + l_dev_class = class_create(THIS_MODULE, KIONIX_ACCEL_NAME); + //for S40 module to judge whether insmod is ok + if (IS_ERR(l_dev_class)){ + ret = PTR_ERR(l_dev_class); + printk(KERN_ERR "Can't class_create gsensor device !!\n"); + return ret; + } + l_clsdevice = device_create(l_dev_class, NULL, MKDEV(GSENSOR_MAJOR, 0), NULL, KIONIX_ACCEL_NAME); + if (IS_ERR(l_clsdevice)){ + ret = PTR_ERR(l_clsdevice); + printk(KERN_ERR "Failed to create device %s !!!",KIONIX_ACCEL_NAME); + return ret; + } + + if((ret = platform_device_register(&kionix_pdevice))) + { + printk(KERN_ERR "%s Can't register kionix platform devcie!!!\n", __FUNCTION__); + return ret; + } + if ((ret = platform_driver_register(&kionix_driver)) != 0) + { + printk(KERN_ERR "%s Can't register kionix platform driver!!!\n", __FUNCTION__); + return ret; + } + + return 0; + +} +module_init(kionix_accel_init); + +static void __exit kionix_accel_exit(void) +{ + //i2c_del_driver(&kionix_accel_driver); + platform_driver_unregister(&kionix_driver); + platform_device_unregister(&kionix_pdevice); + + device_destroy(l_dev_class, MKDEV(GSENSOR_MAJOR, 0)); + + class_destroy(l_dev_class); + sensor_i2c_unregister_device(this_client); +} +module_exit(kionix_accel_exit); + +MODULE_DESCRIPTION("Kionix accelerometer driver"); +MODULE_AUTHOR("Kuching Tan "); +MODULE_LICENSE("GPL"); +MODULE_VERSION("3.3.0"); diff --git a/drivers/input/sensor/kionix_gsensor/kionix_accel.h b/drivers/input/sensor/kionix_gsensor/kionix_accel.h new file mode 100755 index 00000000..b7be9b8f --- /dev/null +++ b/drivers/input/sensor/kionix_gsensor/kionix_accel.h @@ -0,0 +1,85 @@ +/* include/linux/input/kionix_accel.h - Kionix accelerometer driver + * + * Copyright (C) 2012 Kionix, Inc. + * Written by Kuching Tan + * + * 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 2 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 . + * + */ + +#ifndef __KIONIX_ACCEL_H__ +#define __KIONIX_ACCEL_H__ + +#define KIONIX_ACCEL_I2C_ADDR 0x0E +#define KIONIX_ACCEL_NAME "kionix_accel" +#define KIONIX_ACCEL_IRQ "kionix-irq" + +struct kionix_accel_platform_data { + /* Although the accelerometer can perform at high ODR, + * there is a need to keep the maximum ODR to a lower + * value due to power consumption or other concern. + * Use this variable to set the minimum allowable + * interval for data to be reported from the + * accelerometer. Unit is measured in milli- + * seconds. Recommended value is 5ms. */ + unsigned int min_interval; + /* Use this variable to set the default interval for + * data to be reported from the accelerometer. This + * value will be used during driver setup process, + * but can be changed by the system during runtime via + * sysfs control. Recommended value is 200ms.*/ + unsigned int poll_interval; + + /* This variable controls the corresponding direction + * of the accelerometer that is mounted on the board + * of the device. Refer to the porting guide for + * details. Valid value is 1 to 8. */ + u8 accel_direction; + + /* Use this variable to choose whether or not to use + * DRDY hardware interrupt mode to trigger a data + * report event instead of using software polling. + * Note that for those accelerometer model that does + * not support DRDY hardware interrupt, the driver + * will revert to software polling mode automatically. + * Valid value is 0 or 1.*/ + bool accel_irq_use_drdy; + + /* Use this variable to control the number of + * effective bits of the accelerometer output. + * Use the macro definition to select the desired + * number of effective bits. */ + #define KIONIX_ACCEL_RES_12BIT 0 + #define KIONIX_ACCEL_RES_8BIT 1 + #define KIONIX_ACCEL_RES_6BIT 2 + u8 accel_res; + + /* Use this variable to control the G range of + * the accelerometer output. Use the macro definition + * to select the desired G range.*/ + #define KIONIX_ACCEL_G_2G 0 + #define KIONIX_ACCEL_G_4G 1 + #define KIONIX_ACCEL_G_6G 2 + #define KIONIX_ACCEL_G_8G 3 + u8 accel_g_range; + + /* Optional callback functions that can be implemented + * on per product basis. If these callbacks are defined, + * they will be called by the driver. */ + int (*init)(void); + void (*exit)(void); + int (*power_on)(void); + int (*power_off)(void); +}; +#endif /* __KIONIX_ACCEL_H__ */ diff --git a/drivers/input/sensor/kxte9_gsensor/Makefile b/drivers/input/sensor/kxte9_gsensor/Makefile new file mode 100755 index 00000000..23eca917 --- /dev/null +++ b/drivers/input/sensor/kxte9_gsensor/Makefile @@ -0,0 +1,34 @@ +KERNELDIR=../../../../ +#KERNELDIR=/home/hangyan/android8850/kernel/ANDROID_3.0.8 +CROSS = arm_1103_le- +CC= $(CROSS)gcc +LD= $(CROSS)ld +STRIP = $(CROSS)strip + +DEBUG = n + +# Add your debugging flag (or not) to EXTRA_CFLAGS +ifeq ($(DEBUG),y) +# DEBFLAGS = -O -g -DSCULL_DEBUG # "-O" is needed to expand inlines +DEBFLAGS = -O0 -g -DSCULL_DEBUG # "-O" is needed to expand inlines + +else + DEBFLAGS = -O2 -Wall +endif + +EXTRA_CFLAGS += $(DEBFLAGS) + + +MY_MODULE_NAME=s_wmt_gsensor_kxte9 + +obj-m := $(MY_MODULE_NAME).o +$(MY_MODULE_NAME)-objs := kxte9.o + +default: + $(MAKE) -C $(KERNELDIR) SUBDIRS=$(PWD) modules + $(STRIP) --strip-debug $(MY_MODULE_NAME).ko + rm -rf *.o *~ core .depend .*.cmd *.mod.c .tmp_versions + +clean: + rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions *.order *.symvers + 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 + * + * 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 . + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include +#include +#include +#include +#include "kxte9.h" +//#include +#include + +#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 "); +MODULE_LICENSE("GPL"); +MODULE_VERSION(VERSION_DEV); diff --git a/drivers/input/sensor/kxte9_gsensor/kxte9.h b/drivers/input/sensor/kxte9_gsensor/kxte9.h new file mode 100755 index 00000000..6b5141e5 --- /dev/null +++ b/drivers/input/sensor/kxte9_gsensor/kxte9.h @@ -0,0 +1,116 @@ +/* include/linux/kxte9.h - KXTE9 accelerometer driver + * + * Copyright (C) 2010 Kionix, Inc. + * Written by Kuching Tan + * + * 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 . + * + */ + +#ifndef __KXTE9_H__ +#define __KXTE9_H__ + +#define KXTE9_I2C_ADDR 0x0F +/* CONTROL REGISTER 1 BITS */ +#define TPE 0x01 /* tilt position function enable bit */ +#define WUFE 0x02 /* wake-up function enable bit */ +#define B2SE 0x04 /* back-to-sleep function enable bit */ +#define ODR125E 0x20 /* 125Hz ODR mode */ +#define ODR40E 0x18 /* initial ODR masks */ +#define ODR10E 0x10 +#define ODR3E 0x08 +#define ODR1E 0x00 +/* CONTROL REGISTER 3 BITS */ +#define SRST 0x80 /* software reset */ +#define CTC 0x10 /* communication-test function */ +#define OB2S40 0x0C /* back-to-sleep ODR masks */ +#define OB2S10 0x08 +#define OB2S3 0x04 +#define OB2S1 0x00 +#define OWUF40 0x03 /* wake-up ODR masks */ +#define OWUF10 0x02 +#define OWUF3 0x01 +#define OWUF1 0x00 +/* INTERRUPT CONTROL REGISTER 1 BITS */ +#define KXTE9_IEN 0x10 /* interrupt enable */ +#define KXTE9_IEA 0x08 /* interrupt polarity */ +#define KXTE9_IEL 0x04 /* interrupt response */ + +/* Device Meta Data */ +#define DESC_DEV "KXTE9 3-axis Accelerometer" // Device Description +#define VERSION_DEV "1.1.7" +#define VER_MAJOR_DEV 1 +#define VER_MINOR_DEV 1 +#define VER_MAINT_DEV 7 +#define MAX_G_DEV (2.0f) // Maximum G Level +#define MAX_SENS_DEV (1024.0f) // Maximum Sensitivity +#define PWR_DEV (0.03f) // Typical Current + +/* Input Device Name */ +//#define INPUT_NAME_ACC "kxte9_accel" +#define INPUT_NAME_ACC "g-sensor" + +/* Device name for kxte9 misc. device */ +#define NAME_DEV "kxte9" +#define DIR_DEV "/dev/kxte9" + +/* IOCTLs for kxte9 misc. device library */ +#define KXTE9IO 0x92 +#define KXTE9_IOCTL_GET_COUNT _IOR(KXTE9IO, 0x01, int) +#define KXTE9_IOCTL_GET_MG _IOR(KXTE9IO, 0x02, int) +#define KXTE9_IOCTL_ENABLE_OUTPUT _IO(KXTE9IO, 0x03) +#define KXTE9_IOCTL_DISABLE_OUTPUT _IO(KXTE9IO, 0x04) +#define KXTE9_IOCTL_GET_ENABLE _IOR(KXTE9IO, 0x05, int) +#define KXTE9_IOCTL_RESET _IO(KXTE9IO, 0x06) +#define KXTE9_IOCTL_UPDATE_ODR _IOW(KXTE9IO, 0x07, int) +#define KXTE9_IOCTL_ENABLE_CTC _IO(KXTE9IO, 0x08) +#define KXTE9_IOCTL_DISABLE_CTC _IO(KXTE9IO, 0x09) +#define KXTE9_IOCTL_GET_CT_RESP _IOR(KXTE9IO, 0x0A, int) + + +#ifdef __KERNEL__ +struct kxte9_platform_data { + int poll_interval; + int min_interval; + /* the desired g-range, in milli-g (always 2000 for kxte9) */ + u8 g_range; + /* used to compensate for alternate device placement within the host */ +/* + u8 axis_map_x; + u8 axis_map_y; + u8 axis_map_z; + u8 negate_x; + u8 negate_y; + u8 negate_z; +*/ + /* initial configuration values, set during board configuration */ + u8 ctrl_reg1_init; + u8 engine_odr_init; + u8 int_ctrl_init; + u8 tilt_timer_init; + u8 wuf_timer_init; + u8 b2s_timer_init; + u8 wuf_thresh_init; + u8 b2s_thresh_init; + + int (*init)(void); + void (*exit)(void); +//NelsonTmp{ +// int gpio; +//} +}; +#endif /* __KERNEL__ */ + +#endif /* __KXTE9_H__ */ + diff --git a/drivers/input/sensor/kxte9_gsensor/readme.txt b/drivers/input/sensor/kxte9_gsensor/readme.txt new file mode 100755 index 00000000..e4653a08 --- /dev/null +++ b/drivers/input/sensor/kxte9_gsensor/readme.txt @@ -0,0 +1,47 @@ +The back of WM3445 MID + ______________ +| +z | +| \ @ | +| \ | +| ---- +y | +| | | +| | | +| +x | +|______________| +(KXTE9-2050 Accelerometer) + +wmt.io.gsensor +Configure G-Sensor for Enable/Disable,X,Y,Z axis mapping and sampling rate. If driver not detect this variable, then G-sensor driver won¡¦t be loaded. +:::::::::<8bit> +:= Operation mode for the g-sensor +0 : KXTE9-2050 G-Sensor is disabled. +1 : KXTE9-2050 G-Sensor is enabled. +:= G-Sensor sampling rate. The valid values are 1,3,10,and 40. +:= G-Sensor axis-x will map to which axis of device. +0 : X +1 : Y +2 : Z +:= If G-Sensor axis-x direction is the same as device. +1 : Positive direction +-1 : Negative direction +:= G-Sensor axis-y will map to which axis of device. +0 : X +1 : Y +2 : Z +:= If G-Sensor axis-y direction is the same as device. +1 : Positive direction +-1 : Negative direction +:= G-Sensor axis-z will map to which axis of device. +0 : X +1 : Y +2 : Z +:= If G-Sensor axis-z direction is the same as device. +1 : Positive direction +-1 : Negative direction +:= For every new/current axes values stored, add the newly stored axes with previously (avg-count-1) stored axes values and do an average +<8bit>:= Using acceleration data as 8bit +0 : 6bit +1 : 8bit +Ex: +#KXTE9-2050 G-sensor enabled, using 40 sampling rate, X->-Y, Y->X, Z->-Z, 4 average count, using 8bit +setenv wmt.io.gsensor 1:40:1:-1:0:-1:2:-1:4:1 diff --git a/drivers/input/sensor/l3g4200d_gyro/Makefile b/drivers/input/sensor/l3g4200d_gyro/Makefile new file mode 100755 index 00000000..2818c82f --- /dev/null +++ b/drivers/input/sensor/l3g4200d_gyro/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for the kernel device drivers. +# +obj-$(CONFIG_WMT_GYRO_L3G4200D) += s_wmt_gyro_l3g4200d.o +s_wmt_gyro_l3g4200d-objs := l3g4200d_gyr.o \ No newline at end of file diff --git a/drivers/input/sensor/l3g4200d_gyro/l3g4200d.h b/drivers/input/sensor/l3g4200d_gyro/l3g4200d.h new file mode 100755 index 00000000..4cae2bc5 --- /dev/null +++ b/drivers/input/sensor/l3g4200d_gyro/l3g4200d.h @@ -0,0 +1,108 @@ +/******************** (C) COPYRIGHT 2010 STMicroelectronics ******************** +* +* File Name : l3g4200d.h +* Authors : MH - C&I BU - Application Team +* : Carmine Iascone (carmine.iascone@st.com) +* : Matteo Dameno (matteo.dameno@st.com) +* Version : V 1.1 sysfs +* Date : 2010/Aug/19 +* Description : L3G4200D digital output gyroscope sensor API +* +******************************************************************************** +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +* +* THE PRESENT SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES +* OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, FOR THE SOLE +* PURPOSE TO SUPPORT YOUR APPLICATION DEVELOPMENT. +* AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY DIRECT, +* INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM THE +* CONTENT OF SUCH SOFTWARE AND/OR THE USE MADE BY CUSTOMERS OF THE CODING +* INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. +* +******************************************************************************** +* REVISON HISTORY +* +* VERSION | DATE | AUTHORS | DESCRIPTION +* 1.0 | 2010/Aug/19 | Carmine Iascone | First Release +* 1.1 | 2011/02/28 | Matteo Dameno | Self Test Added +* 1.2 | 2013/04/29 | Howay Huo | Android Interface Added +*******************************************************************************/ + +#ifndef __L3G4200D_H__ +#define __L3G4200D_H__ + +#define L3G4200D_GYR_DEV_NAME "l3g4200d_gyr" +#define GYRO_INPUT_NAME "gyroscope" +#define GYRO_MISCDEV_NAME "gyro_ctrl" + +#define L3G4200D_DRVID 0 + +#define L3G4200D_GYR_FS_250DPS 0x00 +#define L3G4200D_GYR_FS_500DPS 0x10 +#define L3G4200D_GYR_FS_2000DPS 0x30 + +#define L3G4200D_GYR_ENABLED 1 +#define L3G4200D_GYR_DISABLED 0 + +#define WMTGSENSOR_IOCTL_MAGIC 0x09 +#define WMT_GYRO_IOCTL_MAGIC 0x11 +#define GYRO_IOCTL_SET_ENABLE _IOW(WMT_GYRO_IOCTL_MAGIC, 0, int) +#define GYRO_IOCTL_GET_ENABLE _IOW(WMT_GYRO_IOCTL_MAGIC, 1, int) +#define GYRO_IOCTL_SET_DELAY _IOW(WMT_GYRO_IOCTL_MAGIC, 2, int) +#define GYRO_IOCTL_GET_DELAY _IOW(WMT_GYRO_IOCTL_MAGIC, 3, int) +#define WMT_IOCTL_SENSOR_GET_DRVID _IOW(WMTGSENSOR_IOCTL_MAGIC, 4, unsigned int) + +#ifdef __KERNEL__ + +struct l3g4200d_triple { + short x, /* x-axis angular rate data. */ + y, /* y-axis angluar rate data. */ + z; /* z-axis angular rate data. */ +}; + + +struct reg_value_t { + u8 ctrl_reg1; + u8 ctrl_reg2; + u8 ctrl_reg3; + u8 ctrl_reg4; + u8 ctrl_reg5; + u8 ref_datacap; + u8 fifo_ctrl_reg; + u8 int1_cfg; + u8 int1_ths_xh; + u8 int1_ths_xl; + u8 int1_ths_yh; + u8 int1_ths_yl; + u8 int1_ths_zh; + u8 int1_ths_zl; + u8 int1_duration; +}; + +struct l3g4200d_gyr_platform_data { + int (*init)(void); + void (*exit)(void); + int (*power_on)(void); + int (*power_off)(void); + int poll_interval; + int min_interval; + + u8 fs_range; + + int axis_map_x; + int axis_map_y; + int axis_map_z; + + int direction_x; + int direction_y; + int direction_z; + + struct reg_value_t init_state; + +}; +#endif /* __KERNEL__ */ + +#endif /* __L3G4200D_H__ */ diff --git a/drivers/input/sensor/l3g4200d_gyro/l3g4200d_gyr.c b/drivers/input/sensor/l3g4200d_gyro/l3g4200d_gyr.c new file mode 100755 index 00000000..a30c00a6 --- /dev/null +++ b/drivers/input/sensor/l3g4200d_gyro/l3g4200d_gyr.c @@ -0,0 +1,1681 @@ +/******************** (C) COPYRIGHT 2010 STMicroelectronics ******************** +* +* File Name : l3g4200d_gyr_sysfs.c +* Authors : MH - C&I BU - Application Team +* : Carmine Iascone (carmine.iascone@st.com) +* : Matteo Dameno (matteo.dameno@st.com) +* : Both authors are willing to be considered the contact +* : and update points for the driver. +* Version : V 1.1 sysfs +* Date : 2011/02/28 +* Description : L3G4200D digital output gyroscope sensor API +* +******************************************************************************** +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +* +* THE PRESENT SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES +* OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, FOR THE SOLE +* PURPOSE TO SUPPORT YOUR APPLICATION DEVELOPMENT. +* AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY DIRECT, +* INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM THE +* CONTENT OF SUCH SOFTWARE AND/OR THE USE MADE BY CUSTOMERS OF THE CODING +* INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. +* +******************************************************************************** +* REVISON HISTORY +* +* VERSION | DATE | AUTHORS | DESCRIPTION +* 1.0 | 2010/11/19 | Carmine Iascone | First Release +* 1.1 | 2011/02/28 | Matteo Dameno | Self Test Added +* 1.2 | 2013/04/29 | Howay Huo | Android Interface Added +*******************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "l3g4200d.h" + +#undef DEBUG +#define DEBUG 1 + +#define L3G4200D_I2C_ADDR 0x69 + +/* WM8880 interrupt pin connect to the DRDY Pin of LL3G4200D */ +#define L3G4200D_IRQ_PIN WMT_PIN_GP2_GPIO17 + +/** Maximum polled-device-reported rot speed value value in dps*/ +#define FS_MAX 32768 + +/* l3g4200d gyroscope registers */ +#define WHO_AM_I 0x0F + +#define CTRL_REG1 0x20 +#define CTRL_REG2 0x21 +#define CTRL_REG3 0x22 +#define CTRL_REG4 0x23 +#define CTRL_REG5 0x24 +#define REF_DATACAP 0x25 +#define OUT_TEMP 0x26 +#define STATUS_REG 0x27 +#define OUT_X_L 0x28 +#define OUT_X_H 0x29 +#define OUT_Y_L 0x2A +#define OUT_Y_H 0x2B +#define OUT_Z_L 0x2C +#define OUT_Z_H 0x2D +#define FIFO_CTRL_REG 0x2E +#define FIFO_SRC_REG 0x2F +#define INT1_CFG 0x30 +#define INT1_SRC 0x31 +#define INT1_THS_XH 0x32 +#define INT1_THS_XL 0x33 +#define INT1_THS_YH 0x34 +#define INT1_THS_YL 0x35 +#define INT1_THS_ZH 0x36 +#define INT1_THS_ZL 0x37 +#define INT1_DURATION 0x38 + +/* CTRL_REG1 */ +#define PM_MASK 0x08 +#define PM_NORMAL 0x08 +#define ENABLE_ALL_AXES 0x07 + +/* CTRL_REG3 */ +#define I2_DRDY 0x08 + +/* CTRL_REG4 bits */ +#define FS_MASK 0x30 + +#define SELFTEST_MASK 0x06 +#define L3G4200D_SELFTEST_DIS 0x00 +#define L3G4200D_SELFTEST_EN_POS 0x02 +#define L3G4200D_SELFTEST_EN_NEG 0x04 + +#define FUZZ 0 +#define FLAT 0 +#define AUTO_INCREMENT 0x80 + +/* STATUS_REG */ +#define ZYXDA 0x08 +#define ZDA 0x04 +#define YDA 0x02 +#define XDA 0x01 + +/** Registers Contents */ +#define WHOAMI_L3G4200D 0x00D3 /* Expected content for WAI register*/ + +#define ODR_MASK 0xF0 +#define ODR100_BW25 0x10 +#define ODR200_BW70 0x70 +#define ODR400_BW110 0xB0 +#define ODR800_BW110 0xF0 + +#define L3G4200D_PU_DELAY 320 //ms + +static struct i2c_client *l3g4200d_i2c_client = NULL; +static int enable_print_log; +static int interrupt_mode = 0; +static int flush_polling_data; + +/* + * L3G4200D gyroscope data + * brief structure containing gyroscope values for yaw, pitch and roll in + * signed short + */ + +struct output_rate{ + u8 odr_mask; + u32 delay_us; +}; + +static const struct output_rate gyro_odr_table[] = { + { ODR800_BW110, 1250 }, + { ODR400_BW110, 2500 }, + { ODR200_BW70, 5000 }, + { ODR100_BW25, 10000}, +}; + +struct l3g4200d_data { + struct i2c_client *client; + struct l3g4200d_gyr_platform_data *pdata; + + struct mutex lock; + + struct delayed_work enable_work; + struct delayed_work input_work; + struct input_dev *input_dev; + int hw_initialized; + int selftest_enabled; + atomic_t enabled; + + u8 reg_addr; + struct reg_value_t resume_state; +}; + +extern int wmt_getsyspara(char *varname, char *varval, int *varlen); + +static int l3g4200d_i2c_read(struct l3g4200d_data *gyro, + u8 *buf, int len) +{ + int err; + + struct i2c_msg msgs[] = { + { + .addr = gyro->client->addr, + .flags = gyro->client->flags & I2C_M_TEN, + .len = 1, + .buf = buf, + }, + { + .addr = gyro->client->addr, + .flags = (gyro->client->flags & I2C_M_TEN) | I2C_M_RD, + .len = len, + .buf = buf, + }, + }; + + err = i2c_transfer(gyro->client->adapter, msgs, 2); + + if (err != 2) { + dev_err(&gyro->client->dev, "read transfer error: %d\n",err); + return -EIO; + } + + return 0; +} + +static int l3g4200d_i2c_write(struct l3g4200d_data *gyro, + u8 *buf, + int len) +{ + int err; + + struct i2c_msg msgs[] = { + { + .addr = gyro->client->addr, + .flags = gyro->client->flags & I2C_M_TEN, + .len = len + 1, + .buf = buf, + }, + }; + + err = i2c_transfer(gyro->client->adapter, msgs, 1); + + if (err != 1) { + dev_err(&gyro->client->dev, "write transfer error\n"); + return -EIO; + } + + return 0; +} + +static int l3g4200d_register_write(struct l3g4200d_data *gyro, u8 *buf, + u8 reg_address, u8 new_value) +{ + int err = -1; + + /* Sets configuration register at reg_address + * NOTE: this is a straight overwrite */ + buf[0] = reg_address; + buf[1] = new_value; + err = l3g4200d_i2c_write(gyro, buf, 1); + if (err < 0) + return err; + + return err; +} + +static int l3g4200d_register_read(struct l3g4200d_data *gyro, u8 *buf, + u8 reg_address) +{ + + int err = -1; + buf[0] = (reg_address); + err = l3g4200d_i2c_read(gyro, buf, 1); + return err; +} + +static int l3g4200d_register_update(struct l3g4200d_data *gyro, u8 *buf, + u8 reg_address, u8 mask, u8 new_bit_values) +{ + int err = -1; + u8 init_val; + u8 updated_val; + err = l3g4200d_register_read(gyro, buf, reg_address); + if (!(err < 0)) { + init_val = buf[0]; + if((new_bit_values & mask) != (init_val & mask)) { + updated_val = ((mask & new_bit_values) | ((~mask) & init_val)); + err = l3g4200d_register_write(gyro, buf, reg_address, + updated_val); + } + else + return 0; + } + return err; +} + +static int l3g4200d_register_store(struct l3g4200d_data *gyro, u8 reg) +{ + int err = -1; + u8 buf[2]; + + err = l3g4200d_register_read(gyro, buf, reg); + if (err) + return err; + + switch (reg) { + case CTRL_REG1: + gyro->resume_state.ctrl_reg1 = buf[0]; + break; + case CTRL_REG2: + gyro->resume_state.ctrl_reg2 = buf[0]; + break; + case CTRL_REG3: + gyro->resume_state.ctrl_reg3 = buf[0]; + break; + case CTRL_REG4: + gyro->resume_state.ctrl_reg4 = buf[0]; + break; + case CTRL_REG5: + gyro->resume_state.ctrl_reg5 = buf[0]; + break; + case REF_DATACAP: + gyro->resume_state.ref_datacap = buf[0]; + break; + case FIFO_CTRL_REG: + gyro->resume_state.fifo_ctrl_reg = buf[0]; + break; + case INT1_CFG: + gyro->resume_state.int1_cfg = buf[0]; + break; + case INT1_THS_XH: + gyro->resume_state.int1_ths_xh = buf[0]; + break; + case INT1_THS_XL: + gyro->resume_state.int1_ths_xl = buf[0]; + break; + case INT1_THS_YH: + gyro->resume_state.int1_ths_yh = buf[0]; + break; + case INT1_THS_YL: + gyro->resume_state.int1_ths_yl = buf[0]; + break; + case INT1_THS_ZH: + gyro->resume_state.int1_ths_zh = buf[0]; + break; + case INT1_THS_ZL: + gyro->resume_state.int1_ths_zl = buf[0]; + break; + case INT1_DURATION: + gyro->resume_state.int1_duration = buf[0]; + break; + default: + pr_err("%s: can't support reg (0x%02X) store\n", L3G4200D_GYR_DEV_NAME, reg); + return -1; + } + + return 0; +} + +static int l3g4200d_update_fs_range(struct l3g4200d_data *gyro, + u8 new_fs) +{ + int res ; + u8 buf[2]; + + buf[0] = CTRL_REG4; + + res = l3g4200d_register_update(gyro, buf, CTRL_REG4, + FS_MASK, new_fs); + + if (res < 0) { + pr_err("%s : failed to update fs:0x%02x\n", + __func__, new_fs); + return res; + } + + l3g4200d_register_store(gyro, CTRL_REG4); + + return 0; +} + + +static int l3g4200d_selftest(struct l3g4200d_data *gyro, u8 enable) +{ + int err = -1; + u8 buf[2] = {0x00,0x00}; + char reg_address, mask, bit_values; + + reg_address = CTRL_REG4; + mask = SELFTEST_MASK; + if (enable > 0) + bit_values = L3G4200D_SELFTEST_EN_POS; + else + bit_values = L3G4200D_SELFTEST_DIS; + if (atomic_read(&gyro->enabled)) { + mutex_lock(&gyro->lock); + err = l3g4200d_register_update(gyro, buf, reg_address, + mask, bit_values); + gyro->selftest_enabled = enable; + mutex_unlock(&gyro->lock); + if (err < 0) + return err; + + l3g4200d_register_store(gyro, CTRL_REG4); + } + return err; +} + + +static int l3g4200d_update_odr(struct l3g4200d_data *gyro, + int poll_interval) +{ + int err = -1; + int i; + u8 config[2]; + + for (i = ARRAY_SIZE(gyro_odr_table) - 1; i >= 0; i--) { + if (gyro_odr_table[i].delay_us <= poll_interval){ + break; + } + } + + gyro->pdata->poll_interval = gyro_odr_table[i].delay_us; + + config[1] = gyro_odr_table[i].odr_mask; + config[1] |= (ENABLE_ALL_AXES + PM_NORMAL); + + /* If device is currently enabled, we need to write new + * configuration out to it */ + if (atomic_read(&gyro->enabled)) { + config[0] = CTRL_REG1; + err = l3g4200d_i2c_write(gyro, config, 1); + if (err < 0) + return err; + + l3g4200d_register_store(gyro, CTRL_REG1); + } + + return err; +} + +/* gyroscope data readout */ +static int l3g4200d_get_data(struct l3g4200d_data *gyro, + struct l3g4200d_triple *data) +{ + int err; + unsigned char gyro_out[6]; + /* y,p,r hardware data */ + s16 hw_d[3] = { 0 }; + + gyro_out[0] = (AUTO_INCREMENT | OUT_X_L); + + err = l3g4200d_i2c_read(gyro, gyro_out, 6); + + if (err < 0) + return err; + + hw_d[0] = (s16) (((gyro_out[1]) << 8) | gyro_out[0]); + hw_d[1] = (s16) (((gyro_out[3]) << 8) | gyro_out[2]); + hw_d[2] = (s16) (((gyro_out[5]) << 8) | gyro_out[4]); + + data->x = ((gyro->pdata->direction_x < 0) ? (-hw_d[gyro->pdata->axis_map_x]) + : (hw_d[gyro->pdata->axis_map_x])); + data->y = ((gyro->pdata->direction_y < 0) ? (-hw_d[gyro->pdata->axis_map_y]) + : (hw_d[gyro->pdata->axis_map_y])); + data->z = ((gyro->pdata->direction_z < 0) ? (-hw_d[gyro->pdata->axis_map_z]) + : (hw_d[gyro->pdata->axis_map_z])); + + if(enable_print_log) + printk("x = %d, y = %d, z = %d\n", data->x, data->y, data->z); + + return err; +} + +static void l3g4200d_report_values(struct l3g4200d_data *l3g, + struct l3g4200d_triple *data) +{ + struct input_dev *input = l3g->input_dev; + + input_report_abs(input, ABS_X, data->x); + input_report_abs(input, ABS_Y, data->y); + input_report_abs(input, ABS_Z, data->z); + input_sync(input); +} + +static int l3g4200d_hw_init(struct l3g4200d_data *gyro) +{ + int err = -1; + u8 buf[8]; + + printk(KERN_INFO "%s hw init\n", L3G4200D_GYR_DEV_NAME); + + buf[0] = (AUTO_INCREMENT | CTRL_REG1); + buf[1] = gyro->resume_state.ctrl_reg1; + buf[2] = gyro->resume_state.ctrl_reg2; + buf[3] = gyro->resume_state.ctrl_reg3; + buf[4] = gyro->resume_state.ctrl_reg4; + buf[5] = gyro->resume_state.ctrl_reg5; + buf[6] = gyro->resume_state.ref_datacap; + err = l3g4200d_i2c_write(gyro, buf, 6); + if(err) + return err; + + buf[0] = FIFO_CTRL_REG; + buf[1] = gyro->resume_state.fifo_ctrl_reg; + err = l3g4200d_i2c_write(gyro, buf, 1); + if(err) + return err; + + buf[0] = INT1_CFG; + buf[1] = gyro->resume_state.int1_cfg; + err = l3g4200d_i2c_write(gyro, buf, 1); + if(err) + return err; + + buf[0] = (AUTO_INCREMENT | INT1_THS_XH); + buf[1] = gyro->resume_state.int1_ths_xh; + buf[2] = gyro->resume_state.int1_ths_xl; + buf[3] = gyro->resume_state.int1_ths_yh; + buf[4] = gyro->resume_state.int1_ths_yl; + buf[5] = gyro->resume_state.int1_ths_zh; + buf[6] = gyro->resume_state.int1_ths_zl; + buf[7] = gyro->resume_state.int1_duration; + err = l3g4200d_i2c_write(gyro, buf, 7); + if(err) + return err; + + gyro->hw_initialized = 1; + + return 0; +} + +static void l3g4200d_gpio_irq_enable(void) +{ + wmt_gpio_unmask_irq(L3G4200D_IRQ_PIN); +} + +static void l3g4200d_gpio_irq_disable(void) +{ + wmt_gpio_mask_irq(L3G4200D_IRQ_PIN); +} + +static int l3g4200d_irq_hw_init(int resume) +{ + int ret; + + if(!resume) { + ret = gpio_request(L3G4200D_IRQ_PIN, "l3g4200d-gyro irq"); //enable gpio + if(ret < 0) { + pr_err("gpio(%d) request fail for l3g4200d-gyro irq\n", L3G4200D_IRQ_PIN); + return ret; + } + }else + gpio_re_enabled(L3G4200D_IRQ_PIN); //re-enable gpio + + gpio_direction_input(L3G4200D_IRQ_PIN); //gpio input + + wmt_gpio_setpull(L3G4200D_IRQ_PIN, WMT_GPIO_PULL_DOWN); //enable pull and pull-down + + wmt_gpio_mask_irq(L3G4200D_IRQ_PIN); //disable interrupt + + wmt_gpio_set_irq_type(L3G4200D_IRQ_PIN, IRQ_TYPE_EDGE_RISING); //rise edge and clear interrupt + + return 0; +} + +static void l3g4200d_irq_hw_free(void) +{ + l3g4200d_gpio_irq_disable(); + gpio_free(L3G4200D_IRQ_PIN); + +} + +static int l3g4200d_gpio_get_value(void) +{ + return (REG8_VAL(__GPIO_BASE + 0x0002) & BIT1); +} + +static int l3g4200d_flush_gyro_data(struct l3g4200d_data *gyro) +{ + struct l3g4200d_triple data; + int i; + + for (i = 0; i < 5; i++) { + if (l3g4200d_gpio_get_value()) { + l3g4200d_get_data(gyro, &data); + pr_info("%s: flush_gyro_data: %d\n", L3G4200D_GYR_DEV_NAME, i); + } + else { + return 0; + } + } + + return -EIO; +} + +static void l3g4200d_device_power_off(struct l3g4200d_data *gyro) +{ + int err; + u8 buf[2]; + + pr_info("%s power off\n", L3G4200D_GYR_DEV_NAME); + + buf[0] = CTRL_REG1; + err = l3g4200d_i2c_read(gyro, buf, 1); + if(err < 0) { + dev_err(&gyro->client->dev, "read ctrl_reg1 failed\n"); + buf[0] = (gyro->resume_state.ctrl_reg1 | PM_MASK); + } + + if(buf[0] & PM_MASK) { + buf[1] = buf[0] & ~PM_MASK; + buf[0] = CTRL_REG1; + + err = l3g4200d_i2c_write(gyro, buf, 1); + if (err) + dev_err(&gyro->client->dev, "soft power off failed\n"); + + l3g4200d_register_store(gyro, CTRL_REG1); + } + + if (gyro->pdata->power_off) { + gyro->pdata->power_off(); +// gyro->hw_initialized = 0; + } + +// if (gyro->hw_initialized) +// gyro->hw_initialized = 0; + +} + +static int l3g4200d_device_power_on(struct l3g4200d_data *gyro) +{ + int err; + u8 buf[2]; + + pr_info("%s power on\n", L3G4200D_GYR_DEV_NAME); + + if (gyro->pdata->power_on) { + err = gyro->pdata->power_on(); + if (err < 0) + return err; + } + + if (!gyro->hw_initialized) { + err = l3g4200d_hw_init(gyro); + if (err) { + l3g4200d_device_power_off(gyro); + return err; + } + } + + buf[0] = CTRL_REG1; + err = l3g4200d_i2c_read(gyro, buf, 1); + if(err) { + dev_err(&gyro->client->dev, "read ctrl_reg1 failed\n"); + buf[0] = (gyro->resume_state.ctrl_reg1 & ~PM_MASK); + } + + if(!(buf[0] & PM_MASK)) { + buf[1] = buf[0] | PM_MASK; + buf[0] = CTRL_REG1; + err = l3g4200d_i2c_write(gyro, buf, 1); + if(err) { + dev_err(&gyro->client->dev, "soft power on failed\n"); + return err; + } + l3g4200d_register_store(gyro, CTRL_REG1); + } + + return 0; +} + +static int l3g4200d_enable(struct l3g4200d_data *gyro) +{ + int err; + + pr_info("%s enable\n", L3G4200D_GYR_DEV_NAME); + + if (!atomic_cmpxchg(&gyro->enabled, 0, 1)) { + err = l3g4200d_device_power_on(gyro); + if (err) { + atomic_set(&gyro->enabled, 0); + return err; + } + + //Android will call l3g4200d_set_delay() after l3g4200d_enable; + } + + return 0; +} + +static int l3g4200d_disable(struct l3g4200d_data *gyro) +{ + pr_info("%s disable\n", L3G4200D_GYR_DEV_NAME); + + if (atomic_cmpxchg(&gyro->enabled, 1, 0)){ + if(interrupt_mode) { + cancel_delayed_work_sync(&gyro->enable_work); + l3g4200d_gpio_irq_disable(); + }else + cancel_delayed_work_sync(&gyro->input_work); + + l3g4200d_device_power_off(gyro); + } + return 0; +} + +static int l3g4200d_set_delay(struct l3g4200d_data *gyro, u32 delay_us) +{ + int odr_value = ODR100_BW25; + int err = -1; + int i; + u8 buf[2] = {CTRL_REG1, 0}; + + pr_info("l3g4200d_set_delay: %d us\n", delay_us); + + /* do not report noise during ODR update */ + if(interrupt_mode) { + cancel_delayed_work_sync(&gyro->enable_work); + l3g4200d_gpio_irq_disable(); + }else + cancel_delayed_work_sync(&gyro->input_work); + + for(i=0; i < ARRAY_SIZE(gyro_odr_table); i++) + if(delay_us <= gyro_odr_table[i].delay_us) { + odr_value = gyro_odr_table[i].odr_mask; + delay_us = gyro_odr_table[i].delay_us; + break; + } + + if(delay_us >= gyro_odr_table[3].delay_us) { + odr_value = gyro_odr_table[3].odr_mask; + delay_us = gyro_odr_table[3].delay_us; + } + + err = l3g4200d_register_update(gyro, buf, CTRL_REG1, ODR_MASK, odr_value); + if(err) { + dev_err(&gyro->client->dev, "update odr failed 0x%x,0x%x: %d\n", + buf[0], odr_value, err); + return err; + } + + l3g4200d_register_store(gyro, CTRL_REG1); + + gyro->pdata->poll_interval = max((int)delay_us, gyro->pdata->min_interval); + + //do not report noise at IC power-up + // flush data before really read + if(interrupt_mode) + schedule_delayed_work(&gyro->enable_work, msecs_to_jiffies( + L3G4200D_PU_DELAY)); + else { + flush_polling_data = 1; + schedule_delayed_work(&gyro->input_work, msecs_to_jiffies( + L3G4200D_PU_DELAY)); + } + + return 0; +} + +static ssize_t attr_polling_rate_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int val; + struct l3g4200d_data *gyro = dev_get_drvdata(dev); + mutex_lock(&gyro->lock); + val = gyro->pdata->poll_interval; + mutex_unlock(&gyro->lock); + return sprintf(buf, "%d\n", val); +} + +static ssize_t attr_polling_rate_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct l3g4200d_data *gyro = dev_get_drvdata(dev); + unsigned long interval_us; + + if (strict_strtoul(buf, 10, &interval_us)) + return -EINVAL; + if (!interval_us) + return -EINVAL; + mutex_lock(&gyro->lock); + gyro->pdata->poll_interval = interval_us; + l3g4200d_update_odr(gyro, interval_us); + mutex_unlock(&gyro->lock); + return size; +} + +static ssize_t attr_range_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct l3g4200d_data *gyro = dev_get_drvdata(dev); + int range = 0; + char val; + mutex_lock(&gyro->lock); + val = gyro->pdata->fs_range; + switch (val) { + case L3G4200D_GYR_FS_250DPS: + range = 250; + break; + case L3G4200D_GYR_FS_500DPS: + range = 500; + break; + case L3G4200D_GYR_FS_2000DPS: + range = 2000; + break; + } + mutex_unlock(&gyro->lock); + return sprintf(buf, "%d\n", range); +} + +static ssize_t attr_range_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct l3g4200d_data *gyro = dev_get_drvdata(dev); + unsigned long val; + if (strict_strtoul(buf, 10, &val)) + return -EINVAL; + mutex_lock(&gyro->lock); + gyro->pdata->fs_range = val; + l3g4200d_update_fs_range(gyro, val); + mutex_unlock(&gyro->lock); + return size; +} + +static ssize_t attr_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct l3g4200d_data *gyro = dev_get_drvdata(dev); + int val = atomic_read(&gyro->enabled); + return sprintf(buf, "%d\n", val); +} + +static ssize_t attr_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct l3g4200d_data *gyro = dev_get_drvdata(dev); + unsigned long val; + + if (strict_strtoul(buf, 10, &val)) + return -EINVAL; + + if (val) { + l3g4200d_enable(gyro); + l3g4200d_set_delay(gyro, gyro->pdata->poll_interval); + } + else + l3g4200d_disable(gyro); + + return size; +} + +static ssize_t attr_get_selftest(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int val; + struct l3g4200d_data *gyro = dev_get_drvdata(dev); + mutex_lock(&gyro->lock); + val = gyro->selftest_enabled; + mutex_unlock(&gyro->lock); + return sprintf(buf, "%d\n", val); +} + +static ssize_t attr_set_selftest(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct l3g4200d_data *gyro = dev_get_drvdata(dev); + unsigned long val; + + if (strict_strtoul(buf, 10, &val)) + return -EINVAL; + + l3g4200d_selftest(gyro, val); + + return size; +} + +#ifdef DEBUG +static ssize_t attr_reg_set(struct device *dev, struct device_attribute *attr, + const char *buf, size_t size) +{ + int rc; + struct l3g4200d_data *gyro = dev_get_drvdata(dev); + u8 x[2]; + unsigned long val; + + if (strict_strtoul(buf, 16, &val)) + return -EINVAL; + mutex_lock(&gyro->lock); + x[0] = gyro->reg_addr; + mutex_unlock(&gyro->lock); + x[1] = val; + rc = l3g4200d_i2c_write(gyro, x, 1); + return size; +} + +static ssize_t attr_reg_get(struct device *dev, struct device_attribute *attr, + char *buf) +{ + ssize_t ret; + struct l3g4200d_data *gyro = dev_get_drvdata(dev); + int rc; + u8 data; + + mutex_lock(&gyro->lock); + data = gyro->reg_addr; + mutex_unlock(&gyro->lock); + rc = l3g4200d_i2c_read(gyro, &data, 1); + ret = sprintf(buf, "0x%02x\n", data); + return ret; +} + +static ssize_t attr_addr_set(struct device *dev, struct device_attribute *attr, + const char *buf, size_t size) +{ + struct l3g4200d_data *gyro = dev_get_drvdata(dev); + unsigned long val; + + if (strict_strtoul(buf, 16, &val)) + return -EINVAL; + + mutex_lock(&gyro->lock); + + gyro->reg_addr = val; + + mutex_unlock(&gyro->lock); + + return size; +} + +static ssize_t attr_get_printlog(struct device * dev,struct device_attribute * attr, + char * buf) +{ + ssize_t ret; + + ret = sprintf(buf, "%d\n", enable_print_log); + + return ret; +} + +static ssize_t attr_set_printlog(struct device *dev, struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long val; + + if (strict_strtoul(buf, 10, &val)) + return -EINVAL; + + enable_print_log = val; + + return size; +} + +#endif /* DEBUG */ + +static struct device_attribute attributes[] = { + __ATTR(pollrate_ms, 0666, attr_polling_rate_show, + attr_polling_rate_store), + __ATTR(range, 0666, attr_range_show, attr_range_store), + __ATTR(enable_device, 0666, attr_enable_show, attr_enable_store), + __ATTR(enable_selftest, 0666, attr_get_selftest, attr_set_selftest), +#ifdef DEBUG + __ATTR(reg_value, 0600, attr_reg_get, attr_reg_set), + __ATTR(reg_addr, 0200, NULL, attr_addr_set), + __ATTR(printlog, 0600, attr_get_printlog, attr_set_printlog), +#endif +}; + +static int create_sysfs_interfaces(struct device *dev) +{ + int i; + for (i = 0; i < ARRAY_SIZE(attributes); i++) + if (device_create_file(dev, attributes + i)) + goto error; + return 0; + +error: + for ( ; i >= 0; i--) + device_remove_file(dev, attributes + i); + dev_err(dev, "%s:Unable to create interface\n", __func__); + return -1; +} + +static int remove_sysfs_interfaces(struct device *dev) +{ + int i; + for (i = 0; i < ARRAY_SIZE(attributes); i++) + device_remove_file(dev, attributes + i); + return 0; +} + +static void l3g4200d_data_report(struct l3g4200d_data *gyro) +{ + struct l3g4200d_triple data_out; + int err; + + err = l3g4200d_get_data(gyro, &data_out); + if (err) + dev_err(&gyro->client->dev, "get_gyroscope_data failed\n"); + else + l3g4200d_report_values(gyro, &data_out); + +} + +static int l3g4200d_validate_pdata(struct l3g4200d_data *gyro) +{ + gyro->pdata->poll_interval = max(gyro->pdata->poll_interval, + gyro->pdata->min_interval); + + if (gyro->pdata->axis_map_x > 2 || + gyro->pdata->axis_map_y > 2 || + gyro->pdata->axis_map_z > 2) { + dev_err(&gyro->client->dev, + "invalid axis_map value x:%u y:%u z%u\n", + gyro->pdata->axis_map_x, + gyro->pdata->axis_map_y, + gyro->pdata->axis_map_z); + return -EINVAL; + } + + /* Only allow 1 and -1 for direction flag */ + if (abs(gyro->pdata->direction_x) != 1 || + abs(gyro->pdata->direction_y) != 1 || + abs(gyro->pdata->direction_z) != 1) { + dev_err(&gyro->client->dev, + "invalid direction value x:%d y:%d z:%d\n", + gyro->pdata->direction_x, + gyro->pdata->direction_y, + gyro->pdata->direction_z); + return -EINVAL; + } + + /* Enforce minimum polling interval */ + if (gyro->pdata->poll_interval < gyro->pdata->min_interval) { + dev_err(&gyro->client->dev, + "minimum poll interval violated\n"); + return -EINVAL; + } + return 0; +} + +static int l3g4200d_misc_open(struct inode *inode, struct file *file) +{ + int err; + + err = nonseekable_open(inode, file); + if(err < 0) + return err; + + file->private_data = i2c_get_clientdata(l3g4200d_i2c_client); + + return 0; +} + +static int l3g4200d_misc_close(struct inode *inode, struct file *filp) +{ + return 0; +} + + +static long l3g4200d_misc_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + void __user *argp = (void __user *)arg; + int interval, val; + int err = -1; + struct l3g4200d_data *gyro = file->private_data; + + switch(cmd){ + case GYRO_IOCTL_GET_DELAY: + interval = gyro->pdata->poll_interval; + if(copy_to_user(argp, &interval, sizeof(interval))) + return -EFAULT; + break; + + case GYRO_IOCTL_SET_DELAY: + if(copy_from_user(&interval, argp, sizeof(interval))) + return -EFAULT; + err = l3g4200d_set_delay(gyro, interval); + if(err < 0) + return err; + break; + + case GYRO_IOCTL_SET_ENABLE: + if(copy_from_user(&val, argp, sizeof(val))) + return -EFAULT; + if(val > 1) + return -EINVAL; + + if(val) + l3g4200d_enable(gyro); + else + l3g4200d_disable(gyro); + break; + + case GYRO_IOCTL_GET_ENABLE: + val = atomic_read(&gyro->enabled); + if(copy_to_user(argp, &val, sizeof(val))) + return -EINVAL; + break; + + case WMT_IOCTL_SENSOR_GET_DRVID: + val = L3G4200D_DRVID; + if (copy_to_user(argp, &val, sizeof(val))) + return -EFAULT; + break; + + default: + return -EINVAL; + } + + return 0; +} + +static const struct file_operations l3g4200d_misc_fops = { + .owner = THIS_MODULE, + .open = l3g4200d_misc_open, + .unlocked_ioctl = l3g4200d_misc_ioctl, + .release = l3g4200d_misc_close, +}; + +static struct miscdevice l3g4200d_misc_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = GYRO_MISCDEV_NAME, + .fops = &l3g4200d_misc_fops, +}; + +static int l3g4200d_input_init(struct l3g4200d_data *gyro) +{ + int err = -1; + struct input_dev *input; + + gyro->input_dev = input_allocate_device(); + if (!gyro->input_dev) { + err = -ENOMEM; + dev_err(&gyro->client->dev, + "input device allocate failed\n"); + goto err0; + } + + input = gyro->input_dev; + + input_set_drvdata(input, gyro); + + set_bit(EV_ABS, input->evbit); + + input_set_abs_params(input, ABS_X, -FS_MAX, FS_MAX, FUZZ, FLAT); + input_set_abs_params(input, ABS_Y, -FS_MAX, FS_MAX, FUZZ, FLAT); + input_set_abs_params(input, ABS_Z, -FS_MAX, FS_MAX, FUZZ, FLAT); + + input->name = GYRO_INPUT_NAME; + + err = input_register_device(input); + if (err) { + dev_err(&gyro->client->dev, + "unable to register input polled device %s\n", + input->name); + goto err1; + } + + return 0; + +err1: + input_free_device(input); +err0: + return err; +} + +static void l3g4200d_input_cleanup(struct l3g4200d_data *gyro) +{ + input_unregister_device(gyro->input_dev); + input_free_device(gyro->input_dev); +} + +static int l3g4300d_detect(struct i2c_client *client) +{ + int ret; + u8 buf[1]; + struct l3g4200d_data gyro; + + gyro.client = client; + + ret = l3g4200d_register_read(&gyro, buf, WHO_AM_I); + if(ret) + return 0; + + if(buf[0] != WHOAMI_L3G4200D){ + dev_err(&client->dev, "chipId = 0x%02X, error", buf[0]); + return 0; + } + + return 1; +} + +static irqreturn_t gyro_irq_handler(int irq, void *dev_id) +{ + if(!gpio_irqstatus(L3G4200D_IRQ_PIN)) + return IRQ_NONE; + + wmt_gpio_ack_irq(L3G4200D_IRQ_PIN); //clear interrupt + + //printk("got gyro interrupt\n"); + if(!is_gpio_irqenable(L3G4200D_IRQ_PIN)) { + //pr_err("l3g4200d irq is disabled\n"); + return IRQ_HANDLED; + }else + return IRQ_WAKE_THREAD; +} + +static irqreturn_t gyro_irq_thread(int irq, void *dev) +{ + struct l3g4200d_data *gyro = dev; + + l3g4200d_data_report(gyro); + + return IRQ_HANDLED; +} + +static void l3g4200d_enable_work_func(struct work_struct *work) +{ + struct delayed_work *dwork = to_delayed_work(work); + struct l3g4200d_data *gyro = + container_of(dwork, struct l3g4200d_data, enable_work); + + l3g4200d_flush_gyro_data(gyro); + l3g4200d_gpio_irq_enable(); +} + +static void l3g4200d_input_poll_func(struct work_struct *work) +{ + struct l3g4200d_triple data; + struct delayed_work *dwork = to_delayed_work(work); + struct l3g4200d_data *gyro = + container_of(dwork, struct l3g4200d_data, input_work); + + if(flush_polling_data == 0) + l3g4200d_data_report(gyro); + else { + l3g4200d_get_data(gyro, &data); //flush the first data + flush_polling_data = 0; + } + + schedule_delayed_work(&gyro->input_work, + usecs_to_jiffies(gyro->pdata->poll_interval)); +} + +static int l3g4200d_param_parse(int *p_int_mode, struct l3g4200d_gyr_platform_data *pdata) +{ + char varbuf[64] = {0}; + int n, ret, varlen; + int tmp[7]; + + varlen = sizeof(varbuf); + + ret = wmt_getsyspara("wmt.io.gyro.l3g4200d", varbuf, &varlen); + if(ret) + return 0; + + n = sscanf(varbuf, "%d:%d:%d:%d:%d:%d:%d", + &tmp[0], &tmp[1], &tmp[2], &tmp[3], &tmp[4], &tmp[5], &tmp[6]); + + if(n != 7) { + pr_err("l3g4200d-gyro param format error\n"); + return -1; + } + + pdata->axis_map_x = tmp[0]; + pdata->direction_x = tmp[1]; + pdata->axis_map_y = tmp[2]; + pdata->direction_y = tmp[3]; + pdata->axis_map_z = tmp[4]; + pdata->direction_z = tmp[5]; + *p_int_mode = tmp[6]; + + return 0; +} + +static int l3g4200d_probe(struct i2c_client *client, + const struct i2c_device_id *devid) +{ + struct l3g4200d_data *gyro; + int err = -1; + u8 buf[2]; + + pr_info("%s: probe start.\n", L3G4200D_GYR_DEV_NAME); + + if (client->dev.platform_data == NULL) { + dev_err(&client->dev, "platform data is NULL. exiting.\n"); + err = -ENODEV; + goto err0; + } + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(&client->dev, "client not i2c capable:1\n"); + err = -ENODEV; + goto err0; + } + + if(l3g4300d_detect(client) == 0){ + dev_err(&client->dev, "not found the gyro\n"); + err = -ENODEV; + goto err0; + } + + gyro = kzalloc(sizeof(*gyro), GFP_KERNEL); + if (gyro == NULL) { + dev_err(&client->dev, + "failed to allocate memory for module data\n"); + err = -ENOMEM; + goto err0; + } + + l3g4200d_param_parse(&interrupt_mode, client->dev.platform_data); + if(interrupt_mode) { + pr_info("l3g4200d-gyro in interrupt mode\n"); + err = l3g4200d_irq_hw_init(0); + if(err) + goto err0; + } + else + pr_info("l3g4200d-gyro in polling mode\n"); + + mutex_init(&gyro->lock); + mutex_lock(&gyro->lock); + gyro->client = client; + + gyro->pdata = kmalloc(sizeof(*gyro->pdata), GFP_KERNEL); + if (gyro->pdata == NULL) { + dev_err(&client->dev, + "failed to allocate memory for pdata: %d\n", err); + goto err1; + } + memcpy(gyro->pdata, client->dev.platform_data, + sizeof(*gyro->pdata)); + + err = l3g4200d_validate_pdata(gyro); + if (err < 0) { + dev_err(&client->dev, "failed to validate platform data\n"); + goto err1_1; + } + + i2c_set_clientdata(client, gyro); + + if (gyro->pdata->init) { + err = gyro->pdata->init(); + if (err < 0) { + dev_err(&client->dev, "init failed: %d\n", err); + goto err1_1; + } + } + + if(interrupt_mode) + gyro->pdata->init_state.ctrl_reg3 |= I2_DRDY; + + //According to the introduction of L3G4200D's datasheet page 32, + //the bit7 and bit6 of CTRL_REG2's value is loaded at boot. This value must not be changed + buf[0] = CTRL_REG2; + err = l3g4200d_i2c_read(gyro, buf, 1); + if(err) + goto err1_1; + + gyro->pdata->init_state.ctrl_reg2 = (gyro->pdata->init_state.ctrl_reg2 & 0x1F) + | (buf[0] & 0xC0); + + gyro->pdata->init_state.ctrl_reg1 &= ~PM_MASK; + + memcpy(&gyro->resume_state, &gyro->pdata->init_state, + sizeof(struct reg_value_t)); + + err = l3g4200d_hw_init(gyro); + if (err) { + dev_err(&client->dev, "hardware init failed: %d\n", err); + goto err2; + } + + err = l3g4200d_input_init(gyro); + if (err < 0) + goto err3; + + err = create_sysfs_interfaces(&client->dev); + if (err < 0) { + dev_err(&client->dev, + "%s device register failed\n", L3G4200D_GYR_DEV_NAME); + goto err4; + } + + /* As default, do not report information */ + atomic_set(&gyro->enabled, 0); + + if(interrupt_mode) + INIT_DELAYED_WORK(&gyro->enable_work, l3g4200d_enable_work_func); + else + INIT_DELAYED_WORK(&gyro->input_work, l3g4200d_input_poll_func); + + err = misc_register(&l3g4200d_misc_device); + if(err){ + dev_err(&client->dev, "l3g4200d_device register failed\n"); + goto err5; + } + + if(interrupt_mode) { + err = request_threaded_irq(gyro->client->irq, gyro_irq_handler, + gyro_irq_thread, IRQF_SHARED, L3G4200D_GYR_DEV_NAME, gyro); + + if(err) { + pr_err("%s: irq request failed: %d\n", __func__, err); + err = -ENODEV; + goto err6; + } + } + + mutex_unlock(&gyro->lock); + +#ifdef DEBUG + pr_info("%s probed: device created successfully\n", + L3G4200D_GYR_DEV_NAME); +#endif + + return 0; + +err6: + misc_deregister(&l3g4200d_misc_device); +err5: + remove_sysfs_interfaces(&client->dev); +err4: + l3g4200d_input_cleanup(gyro); +err3: + l3g4200d_device_power_off(gyro); +err2: + if (gyro->pdata->exit) + gyro->pdata->exit(); +err1_1: + mutex_unlock(&gyro->lock); + kfree(gyro->pdata); +err1: + kfree(gyro); + if(interrupt_mode) + l3g4200d_irq_hw_free(); +err0: + pr_err("%s: Driver Initialization failed\n", + L3G4200D_GYR_DEV_NAME); + return err; +} + +static int l3g4200d_remove(struct i2c_client *client) +{ + struct l3g4200d_data *gyro = i2c_get_clientdata(client); +#ifdef DEBUG + pr_info(KERN_INFO "L3G4200D driver removing\n"); +#endif + + l3g4200d_disable(gyro); + if(interrupt_mode) { + free_irq(gyro->client->irq, gyro); + l3g4200d_irq_hw_free(); + } + misc_deregister(&l3g4200d_misc_device); + l3g4200d_input_cleanup(gyro); + remove_sysfs_interfaces(&client->dev); + kfree(gyro->pdata); + kfree(gyro); + return 0; +} + +static void l3g4200d_shutdown(struct i2c_client *client) +{ + struct l3g4200d_data *gyro = i2c_get_clientdata(client); + + pr_info("l3g4200d_shutdown\n"); + + l3g4200d_disable(gyro); +} + + +static int l3g4200d_suspend(struct device *dev) +{ + struct l3g4200d_data *gyro = i2c_get_clientdata(l3g4200d_i2c_client); + int err; + u8 buf[8]; + + pr_info(KERN_INFO "l3g4200d_suspend\n"); + + if(atomic_read(&gyro->enabled)) { + if(interrupt_mode) { + cancel_delayed_work_sync(&gyro->enable_work); + l3g4200d_gpio_irq_disable(); + } + else + cancel_delayed_work_sync(&gyro->input_work); + + //store register value + buf[0] = (AUTO_INCREMENT | CTRL_REG1); + err = l3g4200d_i2c_read(gyro, buf, 6); + if(err) + goto err1; + gyro->resume_state.ctrl_reg1 = buf[0]; + gyro->resume_state.ctrl_reg2 = buf[1]; + gyro->resume_state.ctrl_reg3 = buf[2]; + gyro->resume_state.ctrl_reg4 = buf[3]; + gyro->resume_state.ctrl_reg5 = buf[4]; + gyro->resume_state.ref_datacap = buf[5]; + + buf[0] = FIFO_CTRL_REG; + err = l3g4200d_i2c_read(gyro, buf, 1); + if(err) + goto err1; + gyro->resume_state.fifo_ctrl_reg = buf[0]; + + buf[0] = INT1_CFG; + err = l3g4200d_i2c_read(gyro, buf, 1); + if(err) + goto err1; + gyro->resume_state.int1_cfg = buf[0]; + + buf[0] = (AUTO_INCREMENT | INT1_THS_XH); + err = l3g4200d_i2c_read(gyro, buf, 7); + if(err) + goto err1; + + gyro->resume_state.int1_ths_xh = buf[0]; + gyro->resume_state.int1_ths_xl = buf[1]; + gyro->resume_state.int1_ths_yh = buf[2]; + gyro->resume_state.int1_ths_yl = buf[3]; + gyro->resume_state.int1_ths_zh = buf[4]; + gyro->resume_state.int1_ths_zl = buf[5]; + gyro->resume_state.int1_duration = buf[6]; + } + + goto exit1; + +err1: + dev_err(&gyro->client->dev, "save register value fail at suspend\n"); + memcpy(&gyro->resume_state, &gyro->pdata->init_state, + sizeof(struct reg_value_t)); +exit1: + l3g4200d_device_power_off(gyro); + + if (gyro->hw_initialized) + gyro->hw_initialized = 0; + + return 0; +} + +static int l3g4200d_resume(struct device *dev) +{ + struct l3g4200d_data *gyro = i2c_get_clientdata(l3g4200d_i2c_client); + int err; + + pr_info(KERN_INFO "l3g4200d_resume\n"); + + if(interrupt_mode) + l3g4200d_irq_hw_init(1); + + if(atomic_read(&gyro->enabled)) { + + err = l3g4200d_device_power_on(gyro); + if(err) + { + dev_err(&gyro->client->dev, "power_on failed at resume\n"); + atomic_set(&gyro->enabled, 0); + + return 0; + } + + //do not report noise at IC power-up + // flush data before really read + if(interrupt_mode) + schedule_delayed_work(&gyro->enable_work, msecs_to_jiffies( + L3G4200D_PU_DELAY)); + else { + flush_polling_data = 1; + schedule_delayed_work(&gyro->input_work, msecs_to_jiffies( + L3G4200D_PU_DELAY)); + } + }else { + memcpy(&gyro->resume_state, &gyro->pdata->init_state, + sizeof(struct reg_value_t)); + + err = l3g4200d_hw_init(gyro); + if (err) + dev_err(&gyro->client->dev, "hardware init failed at resume\n"); + } + + return 0; +} + +static const struct i2c_device_id l3g4200d_id[] = { + { L3G4200D_GYR_DEV_NAME , 0 }, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, l3g4200d_id); + +static struct dev_pm_ops l3g4200d_pm = { + .suspend = l3g4200d_suspend, + .resume = l3g4200d_resume, +}; + +static struct i2c_driver l3g4200d_driver = { + .driver = { + .owner = THIS_MODULE, + .name = L3G4200D_GYR_DEV_NAME, + .pm = &l3g4200d_pm, + }, + .probe = l3g4200d_probe, + .remove = __devexit_p(l3g4200d_remove), + .shutdown = l3g4200d_shutdown, + .id_table = l3g4200d_id, +}; + +static struct l3g4200d_gyr_platform_data gyr_drvr_platform_data={ + .poll_interval = 10000, // us + .min_interval = 1250, // us + + .fs_range = L3G4200D_GYR_FS_2000DPS, + + .axis_map_x = 0, + .axis_map_y = 1, + .axis_map_z = 2, + + .direction_x = 1, + .direction_y = -1, + .direction_z = -1, + + .init_state.ctrl_reg1 = 0x17, //ODR100 + .init_state.ctrl_reg2 = 0x00, + .init_state.ctrl_reg3 = 0x00, //DRDY interrupt + .init_state.ctrl_reg4 = 0xA0, //BDU enable, 2000 dps + .init_state.ctrl_reg5 = 0x00, + .init_state.ref_datacap = 0x00, + .init_state.fifo_ctrl_reg = 0x00, + .init_state.int1_cfg = 0x00, + .init_state.int1_ths_xh = 0x00, + .init_state.int1_ths_xl = 0x00, + .init_state.int1_ths_yh = 0x00, + .init_state.int1_ths_yl = 0x00, + .init_state.int1_ths_zh = 0x00, + .init_state.int1_ths_zl = 0x00, + .init_state.int1_duration = 0x00 +}; + +static struct i2c_board_info __initdata l3g4200d_i2c_board[] = { + { + I2C_BOARD_INFO(L3G4200D_GYR_DEV_NAME, L3G4200D_I2C_ADDR), + .irq = IRQ_GPIO, + .platform_data = &gyr_drvr_platform_data, + }, +}; + +static int __init l3g4200d_init(void) +{ + int ret; + struct i2c_adapter *adapter; + +#ifdef DEBUG + pr_info("%s: gyroscope sysfs driver init\n", L3G4200D_GYR_DEV_NAME); +#endif + + adapter = i2c_get_adapter(0); + if (adapter == NULL) { + pr_err("%s: i2c_get_adapter() error\n", L3G4200D_GYR_DEV_NAME); + return -ENODEV; + } + + l3g4200d_i2c_client = i2c_new_device(adapter, l3g4200d_i2c_board); + if (l3g4200d_i2c_client == NULL) { + pr_err("%s: i2c_new_device() error\n", L3G4200D_GYR_DEV_NAME); + return -ENOMEM; + } + + i2c_put_adapter(adapter); + + ret = i2c_add_driver(&l3g4200d_driver); + if(ret){ + pr_err("%s: i2c_add_driver() failed\n", L3G4200D_GYR_DEV_NAME); + i2c_unregister_device(l3g4200d_i2c_client); + return ret; + } + + return ret; +} + +static void __exit l3g4200d_exit(void) +{ +#ifdef DEBUG + pr_info("L3G4200D exit\n"); +#endif + + i2c_del_driver(&l3g4200d_driver); + i2c_unregister_device(l3g4200d_i2c_client); + + return; +} + +module_init(l3g4200d_init); +module_exit(l3g4200d_exit); + +MODULE_DESCRIPTION("l3g4200d digital gyroscope sysfs driver"); +MODULE_AUTHOR("Matteo Dameno, Carmine Iascone, STMicroelectronics"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/sensor/mc3230_gsensor/Makefile b/drivers/input/sensor/mc3230_gsensor/Makefile new file mode 100755 index 00000000..8a419fc7 --- /dev/null +++ b/drivers/input/sensor/mc3230_gsensor/Makefile @@ -0,0 +1,34 @@ +KERNELDIR=../../../../ +#KERNELDIR=/home/hangyan/android8850/kernel/ANDROID_3.0.8 +CROSS = arm_1103_le- +CC= $(CROSS)gcc +LD= $(CROSS)ld +STRIP = $(CROSS)strip + +DEBUG = n + +# Add your debugging flag (or not) to EXTRA_CFLAGS +ifeq ($(DEBUG),y) +# DEBFLAGS = -O -g -DSCULL_DEBUG # "-O" is needed to expand inlines +DEBFLAGS = -O0 -g -DSCULL_DEBUG # "-O" is needed to expand inlines + +else + DEBFLAGS = -O2 -Wall +endif + +EXTRA_CFLAGS += $(DEBFLAGS) + + +MY_MODULE_NAME=s_wmt_gsensor_mc3230 + +obj-m := $(MY_MODULE_NAME).o +$(MY_MODULE_NAME)-objs := mc32x0.o + +default: + $(MAKE) -C $(KERNELDIR) SUBDIRS=$(PWD) modules + $(STRIP) --strip-debug $(MY_MODULE_NAME).ko + rm -rf *.o *~ core .depend .*.cmd *.mod.c .tmp_versions + +clean: + rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions *.order *.symvers + diff --git a/drivers/input/sensor/mc3230_gsensor/mc32x0.c b/drivers/input/sensor/mc3230_gsensor/mc32x0.c new file mode 100755 index 00000000..4330dbb6 --- /dev/null +++ b/drivers/input/sensor/mc3230_gsensor/mc32x0.c @@ -0,0 +1,2580 @@ +/* Date: 2011/4/8 11:00:00 + * Revision: 2.5 + */ + +/* + * This software program is licensed subject to the GNU General Public License + * (GPL).Version 2,June 1991, available at http://www.fsf.org/copyleft/gpl.html + + * (C) Copyright 2011 Bosch Sensortec GmbH + * All Rights Reserved + */ + + +/* file mc32x0.c + brief This file contains all function implementations for the mc32x0 in linux + +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +//#include +#include +#include +#include +#include + +//#include +#include +#include + +#include "../sensor.h" +#include "mc32x0_driver.h" + + +#if 0 +#define mcprintkreg(x...) printk(x) +#else +#define mcprintkreg(x...) +#endif + +#if 0 +#define mcprintkfunc(x...) printk(x) +#else +#define mcprintkfunc(x...) +#endif + +#if 0 +#define GSE_ERR(x...) printk(x) +#define GSE_LOG(x...) printk(x) +#else +#define GSE_ERR(x...) +#define GSE_LOG(x...) +#endif + +static int g_virtual_z = 0; +#define G_2_REVERSE_VIRTUAL_Z 0 //!!!!! 1 +#define SUPPORT_VIRTUAL_Z_SENSOR //add 2013-10-23 +#define LOW_RESOLUTION 1 +#define HIGH_RESOLUTION 2 +#define RBM_RESOLUTION 3 +#ifdef SUPPORT_VIRTUAL_Z_SENSOR +#define Low_Pos_Max 127 +#define Low_Neg_Max -128 +#define High_Pos_Max 8191 +#define High_Neg_Max -8192 +#define VIRTUAL_Z 1 +static int Railed = 0; +#else +#define VIRTUAL_Z 0 +#endif + + +static struct class* l_dev_class = NULL; + + + + +#define GSENSOR_NAME "mc3230" +#define SENSOR_DATA_SIZE 3 +#define AVG_NUM 16 +/* Addresses to scan */ +//static const unsigned short normal_i2c[2] = {0x00,I2C_CLIENT_END}; + + +//volatile unsigned char mc32x0_on_off=0; +//static int mc32x0_pin_hd; +static char mc32x0_on_off_str[32]; +#define G_0 ABS_X +#define G_1 ABS_Y +#define G_2 ABS_Z +#define G_0_REVERSE 1 +#define G_1_REVERSE 1 +#define G_2_REVERSE -1 + +#define GRAVITY_1G_VALUE 1000 + +#define SENSOR_DMARD_IOCTL_BASE 234 + +#define IOCTL_SENSOR_SET_DELAY_ACCEL _IO(SENSOR_DMARD_IOCTL_BASE, 100) +#define IOCTL_SENSOR_GET_DELAY_ACCEL _IO(SENSOR_DMARD_IOCTL_BASE, 101) +#define IOCTL_SENSOR_GET_STATE_ACCEL _IO(SENSOR_DMARD_IOCTL_BASE, 102) +#define IOCTL_SENSOR_SET_STATE_ACCEL _IO(SENSOR_DMARD_IOCTL_BASE, 103) +#define IOCTL_SENSOR_GET_DATA_ACCEL _IO(SENSOR_DMARD_IOCTL_BASE, 104) + +#define IOCTL_MSENSOR_SET_DELAY_MAGNE _IO(SENSOR_DMARD_IOCTL_BASE, 200) +#define IOCTL_MSENSOR_GET_DATA_MAGNE _IO(SENSOR_DMARD_IOCTL_BASE, 201) +#define IOCTL_MSENSOR_GET_STATE_MAGNE _IO(SENSOR_DMARD_IOCTL_BASE, 202) +#define IOCTL_MSENSOR_SET_STATE_MAGNE _IO(SENSOR_DMARD_IOCTL_BASE, 203) + +#define IOCTL_SENSOR_GET_NAME _IO(SENSOR_DMARD_IOCTL_BASE, 301) +#define IOCTL_SENSOR_GET_VENDOR _IO(SENSOR_DMARD_IOCTL_BASE, 302) + +#define IOCTL_SENSOR_GET_CONVERT_PARA _IO(SENSOR_DMARD_IOCTL_BASE, 401) + +#define SENSOR_CALIBRATION _IOWR(SENSOR_DMARD_IOCTL_BASE, 402, int[SENSOR_DATA_SIZE]) + + +#define mc32x0_CONVERT_PARAMETER (1.5f * (9.80665f) / 256.0f) +//#define mc32x0_DISPLAY_NAME "mc32x0" +//#define mc32x0_DIPLAY_VENDOR "domintech" + +#define X_OUT 0x41 +#define CONTROL_REGISTER 0x44 +#define SW_RESET 0x53 +#define WHO_AM_I 0x0f +#define WHO_AM_I_VALUE 0x06 + +#define MC32X0_AXIS_X 0 +#define MC32X0_AXIS_Y 1 +#define MC32X0_AXIS_Z 2 +#define MC32X0_AXES_NUM 3 +#define MC32X0_DATA_LEN 6 + +#define MC32X0_XOUT_REG 0x00 +#define MC32X0_YOUT_REG 0x01 +#define MC32X0_ZOUT_REG 0x02 +#define MC32X0_Tilt_Status_REG 0x03 +#define MC32X0_Sampling_Rate_Status_REG 0x04 +#define MC32X0_Sleep_Count_REG 0x05 +#define MC32X0_Interrupt_Enable_REG 0x06 +#define MC32X0_Mode_Feature_REG 0x07 +#define MC32X0_Sample_Rate_REG 0x08 +#define MC32X0_Tap_Detection_Enable_REG 0x09 +#define MC32X0_TAP_Dwell_Reject_REG 0x0a +#define MC32X0_DROP_Control_Register_REG 0x0b +#define MC32X0_SHAKE_Debounce_REG 0x0c +#define MC32X0_XOUT_EX_L_REG 0x0d +#define MC32X0_XOUT_EX_H_REG 0x0e +#define MC32X0_YOUT_EX_L_REG 0x0f +#define MC32X0_YOUT_EX_H_REG 0x10 +#define MC32X0_ZOUT_EX_L_REG 0x11 +#define MC32X0_ZOUT_EX_H_REG 0x12 +#define MC32X0_CHIP_ID_REG 0x18 +#define MC32X0_RANGE_Control_REG 0x20 +#define MC32X0_SHAKE_Threshold_REG 0x2B +#define MC32X0_UD_Z_TH_REG 0x2C +#define MC32X0_UD_X_TH_REG 0x2D +#define MC32X0_RL_Z_TH_REG 0x2E +#define MC32X0_RL_Y_TH_REG 0x2F +#define MC32X0_FB_Z_TH_REG 0x30 +#define MC32X0_DROP_Threshold_REG 0x31 +#define MC32X0_TAP_Threshold_REG 0x32 +#define MC32X0_HIGH_END 0x01 +/*******MC3210/20 define this**********/ + + +#define MCUBE_8G_14BIT 0x10 + +#define DOT_CALI + +#define MC32X0_LOW_END 0x02 +/*******mc32x0 define this**********/ + +#define MCUBE_1_5G_8BIT 0x20 +//#define MCUBE_1_5G_8BIT_TAP +//#define MCUBE_1_5G_6BIT +#define MC32X0_MODE_DEF 0x43 + +#define MC32X0ADDRESS 0x4c + +#define mc32x0_I2C_NAME "mc32x0" +#define GSENSOR_DEV_COUNT 1 +#define GSENSOR_DURATION_MAX 200 +#define GSENSOR_DURATION_MIN 10 +#define GSENSOR_DURATION_DEFAULT 20 + +#define MAX_RETRY 20 +#define INPUT_FUZZ 0 +#define INPUT_FLAT 0 + +#define AUTO_CALIBRATION 0 + +static unsigned char is_new_mc34x0 = 0; +static unsigned char is_mc3250 = 0; + +static unsigned char McubeID=0; +#ifdef DOT_CALI +#define CALIB_PATH "/data/data/com.mcube.acc/files/mcube-calib.txt" +//MCUBE_BACKUP_FILE +#define BACKUP_CALIB_PATH "/data/misc/sensors/mcube-calib.txt" +//static char backup_buf[64]; +//MCUBE_BACKUP_FILE +#define DATA_PATH "/sdcard/mcube-register-map.txt" + +typedef struct { + unsigned short x; /**< X axis */ + unsigned short y; /**< Y axis */ + unsigned short z; /**< Z axis */ +} GSENSOR_VECTOR3D; + +static GSENSOR_VECTOR3D gsensor_gain; +static struct miscdevice mc32x0_device; + +//static struct file * fd_file = NULL; + +static mm_segment_t oldfs; +//add by Liang for storage offset data +static unsigned char offset_buf[9]; +static signed int offset_data[3]; +s16 G_RAW_DATA[3]; +static signed int gain_data[3]; +static signed int enable_RBM_calibration = 0; +#endif + +#ifdef DOT_CALI + +#if 1 +#define GSENSOR 0xA1//0x95 + +#define GSENSOR_IOCTL_INIT _IO(GSENSOR, 0x01) +#define GSENSOR_IOCTL_READ_CHIPINFO _IOR(GSENSOR, 0x02, int) +#define GSENSOR_IOCTL_READ_SENSORDATA _IOR(GSENSOR, 0x17, int) +#define GSENSOR_IOCTL_READ_OFFSET _IOR(GSENSOR, 0x04, GSENSOR_VECTOR3D) +#define GSENSOR_IOCTL_READ_GAIN _IOR(GSENSOR, 0x05, GSENSOR_VECTOR3D) +#define GSENSOR_IOCTL_READ_RAW_DATA _IOR(GSENSOR, 0x30, int) +//#define GSENSOR_IOCTL_SET_CALI _IOW(GSENSOR, 0x06, SENSOR_DATA) +#define GSENSOR_IOCTL_GET_CALI _IOW(GSENSOR, 0x22, SENSOR_DATA) +#define GSENSOR_IOCTL_CLR_CALI _IO(GSENSOR, 0x23) +#define GSENSOR_MCUBE_IOCTL_READ_RBM_DATA _IOR(GSENSOR, 0x24, SENSOR_DATA) +#define GSENSOR_MCUBE_IOCTL_SET_RBM_MODE _IO(GSENSOR, 0x25) +#define GSENSOR_MCUBE_IOCTL_CLEAR_RBM_MODE _IO(GSENSOR, 0x26) +#define GSENSOR_MCUBE_IOCTL_SET_CALI _IOW(GSENSOR, 0x27, SENSOR_DATA) +#define GSENSOR_MCUBE_IOCTL_REGISTER_MAP _IO(GSENSOR, 0x28) +#define GSENSOR_IOCTL_SET_CALI_MODE _IOW(GSENSOR, 0x29,int) +#else + +#define GSENSOR_IOCTL_INIT 0xa1 +#define GSENSOR_IOCTL_READ_CHIPINFO 0xa2 +#define GSENSOR_IOCTL_READ_SENSORDATA 0xa3 +#define GSENSOR_IOCTL_READ_OFFSET 0xa4 +#define GSENSOR_IOCTL_READ_GAIN 0xa5 +#define GSENSOR_IOCTL_READ_RAW_DATA 0xa6 +#define GSENSOR_IOCTL_SET_CALI 0xa7 +#define GSENSOR_IOCTL_GET_CALI 0xa8 +#define GSENSOR_IOCTL_CLR_CALI 0xa9 + +#define GSENSOR_MCUBE_IOCTL_READ_RBM_DATA 0xaa +#define GSENSOR_MCUBE_IOCTL_SET_RBM_MODE 0xab +#define GSENSOR_MCUBE_IOCTL_CLEAR_RBM_MODE 0xac +#define GSENSOR_MCUBE_IOCTL_SET_CALI 0xad +#define GSENSOR_MCUBE_IOCTL_REGISTER_MAP 0xae +#define GSENSOR_IOCTL_SET_CALI_MODE 0xaf +#endif + +typedef struct{ + int x; + int y; + int z; +}SENSOR_DATA; + +static int load_cali_flg = 0; +//MCUBE_BACKUP_FILE +//static bool READ_FROM_BACKUP = false; +//MCUBE_BACKUP_FILE + +#endif + +#define MC32X0_WAKE 1 +#define MC32X0_SNIFF 2 +#define MC32X0_STANDBY 3 + +struct dev_data { + struct i2c_client *client; +}; +static struct dev_data dev; + +/* Addresses to scan */ +static const unsigned short normal_i2c[2] = {MC32X0ADDRESS, I2C_CLIENT_END}; + +/* +typedef union { + struct { + s16 x; + s16 y; + s16 z; + } u; + s16 v[SENSOR_DATA_SIZE]; +} raw_data; +static raw_data offset; +*/ + +struct acceleration { + int x; + int y; + int z; +}; + +//void gsensor_write_offset_to_file(void); +//void gsensor_read_offset_from_file(void); +//char OffsetFileName[] = "/data/misc/dmt/offset.txt"; +/*static struct sensor_config_info gsensor_info = { + .input_type = GSENSOR_TYPE, +};*/ + +static u32 debug_mask = 0; +#define dprintk(level_mask, fmt, arg...) if (unlikely(debug_mask & level_mask)) \ + printk(KERN_DEBUG fmt , ## arg) + +module_param_named(debug_mask, debug_mask, int, 0644); + + +enum { + DEBUG_INIT = 1U << 0, + DEBUG_CONTROL_INFO = 1U << 1, + DEBUG_DATA_INFO = 1U << 2, + DEBUG_SUSPEND = 1U << 3, +}; + +struct mc32x0_data { + struct mutex lock; + struct i2c_client *client; + struct delayed_work work; + struct workqueue_struct *mc32x0_wq; + struct hrtimer timer; + struct device *device; + struct input_dev *input_dev; + int use_count; + int enabled; + volatile unsigned int duration; + int use_irq; + int irq; + unsigned long irqflags; + int gpio; + unsigned int map[3]; + int inv[3]; +#ifdef CONFIG_HAS_EARLYSUSPEND + struct early_suspend early_suspend; +#endif + // for control + int int_gpio; //0-3 + int op; + int samp; + //int xyz_axis[3][3]; // (axis,direction) + struct proc_dir_entry* sensor_proc; + int isdbg; + int sensor_samp; // + int sensor_enable; // 0 --> disable sensor, 1 --> enable sensor + int test_pass; + int offset[MC32X0_AXES_NUM+1]; /*+1: for 4-byte alignment*/ + s16 data[MC32X0_AXES_NUM+1]; +}; + +static struct mc32x0_data l_sensorconfig = { + .op = 0, + .int_gpio = 3, + .samp = 16, + /*.xyz_axis = { + {ABS_X, -1}, + {ABS_Y, 1}, + {ABS_Z, -1}, + },*/ + .sensor_proc = NULL, + .isdbg = 1, + .sensor_samp = 1, // 1 sample/second + .sensor_enable = 1, // enable sensor + .test_pass = 0, // for test program + //.offset={0,0,0}, +}; + + +//============================================================================= +enum mc3xx0_orientation +{ + MC3XX0_TOP_LEFT_DOWN = 0, + MC3XX0_TOP_RIGHT_DOWN, + MC3XX0_TOP_RIGHT_UP, + MC3XX0_TOP_LEFT_UP, + MC3XX0_BOTTOM_LEFT_DOWN, + MC3XX0_BOTTOM_RIGHT_DOWN, + MC3XX0_BOTTOM_RIGHT_UP, + MC3XX0_BOTTOM_LEFT_UP +}; + +enum mc3xx0_axis +{ + MC3XX0_AXIS_X = 0, + MC3XX0_AXIS_Y, + MC3XX0_AXIS_Z, + MC3XX0_AXIS_NUM +}; + +struct mc3xx0_hwmsen_convert +{ + signed int sign[3]; + unsigned int map[3]; +}; + +// Transformation matrix for chip mounting position +static const struct mc3xx0_hwmsen_convert mc3xx0_cvt[] = +{ + {{ 1, 1, 1}, {MC3XX0_AXIS_X, MC3XX0_AXIS_Y, MC3XX0_AXIS_Z}}, // 0: top , left-down + {{-1, 1, 1}, {MC3XX0_AXIS_Y, MC3XX0_AXIS_X, MC3XX0_AXIS_Z}}, // 1: top , right-down + {{-1, -1, 1}, {MC3XX0_AXIS_X, MC3XX0_AXIS_Y, MC3XX0_AXIS_Z}}, // 2: top , right-up + {{ 1, -1, 1}, {MC3XX0_AXIS_Y, MC3XX0_AXIS_X, MC3XX0_AXIS_Z}}, // 3: top , left-up + {{-1, 1, -1}, {MC3XX0_AXIS_X, MC3XX0_AXIS_Y, MC3XX0_AXIS_Z}}, // 4: bottom, left-down + {{ 1, 1, -1}, {MC3XX0_AXIS_Y, MC3XX0_AXIS_X, MC3XX0_AXIS_Z}}, // 5: bottom, right-down + {{ 1, -1, -1}, {MC3XX0_AXIS_X, MC3XX0_AXIS_Y, MC3XX0_AXIS_Z}}, // 6: bottom, right-up + {{-1, -1, -1}, {MC3XX0_AXIS_Y, MC3XX0_AXIS_X, MC3XX0_AXIS_Z}}, // 7: bottom, left-up +}; + +//static unsigned char mc3xx0_current_placement = MC3XX0_BOTTOM_LEFT_DOWN; // current soldered placement +static struct mc3xx0_hwmsen_convert *pCvt; + +//volatile static short sensor_duration = SENSOR_DURATION_DEFAULT;//delay +//volatile static short sensor_state_flag = 1; + +#ifdef SUPPORT_VIRTUAL_Z_SENSOR //add 2013-10-23 +int Verify_Z_Railed(int AccData, int resolution) +{ + int status = 0; + GSE_LOG("%s: AccData = %d",__func__, AccData); + if(resolution == 1) // Low resolution + { + if((AccData >= Low_Pos_Max && AccData >=0)|| (AccData <= Low_Neg_Max && AccData < 0)) + { + status = 1; + GSE_LOG("%s: Railed at Low Resolution",__func__); + } + } + else if (resolution == 2) //High resolution + { + if((AccData >= High_Pos_Max && AccData >=0) || (AccData <= High_Neg_Max && AccData < 0)) + { + status = 1; + GSE_LOG("%s: Railed at High Resolution",__func__); + } + } + else if (resolution == 3) //High resolution + { + if((AccData >= Low_Pos_Max*3 && AccData >=0) || (AccData <= Low_Neg_Max*3 && AccData < 0)) + { + status = 1; + GSE_LOG("%s: Railed at High Resolution",__func__); + } + } + else + GSE_LOG("%s, Wrong resolution",__func__); + + return status; +} + +int SquareRoot(int x) +{ + int lowerbound; + int upperbound; + int root; + + if(x < 0) return -1; + if(x == 0 || x == 1) return x; + lowerbound = 1; + upperbound = x; + root = lowerbound + (upperbound - lowerbound)/2; + + while(root > x/root || root+1 <= x/(root+1)) + { + if(root > x/root) + { + upperbound = root; + } + else + { + lowerbound = root; + } + root = lowerbound + (upperbound - lowerbound)/2; + } + GSE_LOG("%s: Sqrt root is %d",__func__, root); + return root; +} +#endif + + +unsigned int sample_rate_2_memsec(unsigned int rate) +{ + return (1000/rate); +} + +static ssize_t mc32x0_map_show(struct device *dev, struct device_attribute *attr,char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct mc32x0_data *data; + int i; + data = i2c_get_clientdata(client); + for (i = 0; i< 3; i++) + { + if(data->inv[i] == 1) + { + switch(data->map[i]) + { + case ABS_X: + buf[i] = 'x'; + break; + case ABS_Y: + buf[i] = 'y'; + break; + case ABS_Z: + buf[i] = 'z'; + break; + default: + buf[i] = '_'; + break; + } + } + else + { + switch(data->map[i]) + { + case ABS_X: + buf[i] = 'X'; + break; + case ABS_Y: + buf[i] = 'Y'; + break; + case ABS_Z: + buf[i] = 'Z'; + break; + default: + buf[i] = '-'; + break; + } + } + } + sprintf(buf+3,"\r\n"); + return 5; +} +/* +//Function as i2c_master_send, and return 1 if operation is successful. +static int i2c_write_bytes(struct i2c_client *client, uint8_t *data, uint16_t len) +{ + struct i2c_msg msg; + int ret=-1; + + msg.flags = !I2C_M_RD; + msg.addr = client->addr; + msg.len = len; + msg.buf = data; + + ret=i2c_transfer(client->adapter, &msg,1); + return ret; +} + +static bool gsensor_i2c_test(struct i2c_client * client) +{ + int ret, retry; + uint8_t test_data[1] = { 0 }; //only write a data address. + + for(retry=0; retry < 2; retry++) + { + ret =i2c_write_bytes(client, test_data, 1); //Test i2c. + if (ret == 1) + break; + msleep(5); + } + + return ret==1 ? true : false; +} +*/ +/** + * gsensor_detect - Device detection callback for automatic device creation + * return value: + * = 0; success; + * < 0; err + */ + /* +static int gsensor_detect(struct i2c_client *client, struct i2c_board_info *info) +{ + struct i2c_adapter *adapter = client->adapter; + int ret; + + dprintk(DEBUG_INIT, "%s enter \n", __func__); + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + if(twi_id == adapter->nr){ + pr_info("%s: addr= %x\n",__func__,client->addr); + + ret = gsensor_i2c_test(client); + if(!ret){ + pr_info("%s:I2C connection might be something wrong or maybe the other gsensor equipment! \n",__func__); + return -ENODEV; + }else{ + pr_info("I2C connection sucess!\n"); + strlcpy(info->type, SENSOR_NAME, I2C_NAME_SIZE); + return 0; + } + + }else{ + return -ENODEV; + } +} +*/ +int mc32x0_set_image (struct i2c_client *client) +{ + int comres = 0; + unsigned char data; + + + data = i2c_smbus_read_byte_data(client, 0x3B); + //comres = p_mc32x0->MC32X0_BUS_READ_FUNC(p_mc32x0->dev_addr, 0x3B, &data, 1 ); + if((data == 0x19)||(data == 0x29)) + { + McubeID = 0x22; + } + else if((data == 0x90)||(data == 0xA8)) + { + McubeID = 0x11; + } + else + { + McubeID = 0; + } + + if (0x88 == data) + { + McubeID = 0x11; + is_mc3250 = 1; + } + + if (0x39 == data) + { + McubeID = 0x22; + is_new_mc34x0 = 1; + } + else if (0xB8 == data) + { + McubeID = 0x11; + is_new_mc34x0 = 1; + } + + + if(McubeID &MCUBE_8G_14BIT) + { + //#ifdef MCUBE_8G_14BIT + data = MC32X0_MODE_DEF; + //comres += p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, MC32X0_Mode_Feature_REG, &data, 1 ); + i2c_smbus_write_byte_data(client, MC32X0_Mode_Feature_REG,data); + data = 0x00; + //comres += p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, MC32X0_Sleep_Count_REG, &data, 1 ); + i2c_smbus_write_byte_data(client, MC32X0_Sleep_Count_REG,data); + data = 0x00; + //comres += p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, MC32X0_Sample_Rate_REG, &data, 1 ); + i2c_smbus_write_byte_data(client, MC32X0_Sample_Rate_REG,data); + data = 0x00; + //comres += p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, MC32X0_Tap_Detection_Enable_REG, &data, 1 ); + i2c_smbus_write_byte_data(client, MC32X0_Tap_Detection_Enable_REG,data); + data = 0x3F; + //comres += p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, MC32X0_RANGE_Control_REG, &data, 1 ); + i2c_smbus_write_byte_data(client, MC32X0_RANGE_Control_REG,data); + data = 0x00; + //comres += p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, MC32X0_Interrupt_Enable_REG, &data, 1 ); + i2c_smbus_write_byte_data(client, MC32X0_Interrupt_Enable_REG,data); +#ifdef DOT_CALI + gsensor_gain.x = gsensor_gain.y = gsensor_gain.z = 1024; +#endif + //#endif + } + else if(McubeID &MCUBE_1_5G_8BIT) + { + #ifdef MCUBE_1_5G_8BIT + data = MC32X0_MODE_DEF; + //comres += p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, MC32X0_Mode_Feature_REG, &data, 1 ); + i2c_smbus_write_byte_data(client, MC32X0_Mode_Feature_REG,data); + data = 0x00; + //comres += p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, MC32X0_Sleep_Count_REG, &data, 1 ); + i2c_smbus_write_byte_data(client, MC32X0_Sleep_Count_REG,data); + data = 0x00; + //comres += p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, MC32X0_Sample_Rate_REG, &data, 1 ); + i2c_smbus_write_byte_data(client, MC32X0_Sample_Rate_REG,data); + data = 0x02; + //comres += p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, MC32X0_RANGE_Control_REG, &data, 1 ); + i2c_smbus_write_byte_data(client, MC32X0_RANGE_Control_REG,data); + data = 0x00; + //comres += p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, MC32X0_Tap_Detection_Enable_REG, &data, 1 ); + i2c_smbus_write_byte_data(client, MC32X0_Tap_Detection_Enable_REG,data); + data = 0x00; + //comres += p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, MC32X0_Interrupt_Enable_REG, &data, 1 ); + i2c_smbus_write_byte_data(client, MC32X0_Interrupt_Enable_REG,data); +#ifdef DOT_CALI + gsensor_gain.x = gsensor_gain.y = gsensor_gain.z = 86; +#endif + #endif + } + + data = 0x41; + //comres += p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, MC32X0_Mode_Feature_REG, &data, 1 ); + i2c_smbus_write_byte_data(client, MC32X0_Mode_Feature_REG,data); + //MC32X0_rbm(0,0); + return comres; +} + +static ssize_t mc32x0_map_store(struct device *dev, struct device_attribute *attr,const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct mc32x0_data *data; + int i; + data = i2c_get_clientdata(client); + + if(count < 3) return -EINVAL; + + for(i = 0; i< 3; i++) + { + switch(buf[i]) + { + case 'x': + data->map[i] = ABS_X; + data->inv[i] = 1; + break; + case 'y': + data->map[i] = ABS_Y; + data->inv[i] = 1; + break; + case 'z': + data->map[i] = ABS_Z; + data->inv[i] = 1; + break; + case 'X': + data->map[i] = ABS_X; + data->inv[i] = -1; + break; + case 'Y': + data->map[i] = ABS_Y; + data->inv[i] = -1; + break; + case 'Z': + data->map[i] = ABS_Z; + data->inv[i] = -1; + break; + default: + return -EINVAL; + } + } + + return count; +} + +static int mc32x0_enable(struct mc32x0_data *data, int enable); + +static ssize_t mc32x0_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = container_of(mc32x0_device.parent, struct i2c_client, dev); + + struct mc32x0_data *mc32x0 = i2c_get_clientdata(client); + + return sprintf(buf, "%d\n", mc32x0->enabled); +} + +static ssize_t mc32x0_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + bool new_enable; + + struct i2c_client *client = container_of(mc32x0_device.parent, struct i2c_client, dev); + + struct mc32x0_data *mc32x0 = i2c_get_clientdata(client); + + if (sysfs_streq(buf, "1")) + new_enable = true; + else if (sysfs_streq(buf, "0")) + new_enable = false; + else { + pr_debug("%s: invalid value %d\n", __func__, *buf); + return -EINVAL; + } + + mc32x0_enable(mc32x0, new_enable); + + return count; +} + +static ssize_t mc32x0_delay_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", 1000/l_sensorconfig.sensor_samp); +} + +static ssize_t mc32x0_delay_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long data; + int error; + + error = strict_strtoul(buf, 10, &data); + if (error) + return error; + if (data > GSENSOR_DURATION_MAX) + data = GSENSOR_DURATION_MAX; + if (data < GSENSOR_DURATION_MIN) + data = GSENSOR_DURATION_MIN; + l_sensorconfig.sensor_samp = 1000/data; + + return count; +} + +static DEVICE_ATTR(map, 0660, mc32x0_map_show, mc32x0_map_store); +static DEVICE_ATTR(enable, 0660, mc32x0_enable_show, mc32x0_enable_store); +static DEVICE_ATTR(delay, 0660, mc32x0_delay_show, mc32x0_delay_store); + +static struct attribute* mc32x0_attrs[] = +{ + &dev_attr_map.attr, + &dev_attr_enable.attr, + &dev_attr_delay.attr, + NULL +}; + +static const struct attribute_group mc32x0_group = +{ + .attrs = mc32x0_attrs, +}; + +static int mc32x0_chip_init(struct i2c_client *client) +{ + + mc32x0_set_image(client); + + return McubeID?0:-1; +} + +int mc32x0_set_mode(struct i2c_client *client, unsigned char mode) +{ + + int comres=0; + unsigned char data; + + + if (mode<4) { + data = 0x40|mode; + i2c_smbus_write_byte_data(client, MC32X0_Mode_Feature_REG,data); + } + return comres; + +} + + + +#ifdef DOT_CALI +struct file *openFile(const char *path,int flag,int mode) +{ + struct file *fp; + + fp=filp_open(path, flag, mode); + if (IS_ERR(fp) || !fp->f_op) + { + GSE_LOG("Calibration File filp_open return NULL\n"); + return NULL; + } + else + { + + return fp; + } +} + +int readFile(struct file *fp,char *buf,int readlen) +{ + if (fp->f_op && fp->f_op->read) + return fp->f_op->read(fp,buf,readlen, &fp->f_pos); + else + return -1; +} + +int writeFile(struct file *fp,char *buf,int writelen) +{ + if (fp->f_op && fp->f_op->write) + return fp->f_op->write(fp,buf,writelen, &fp->f_pos); + else + return -1; +} + +int closeFile(struct file *fp) +{ + filp_close(fp,NULL); + return 0; +} + +void initKernelEnv(void) +{ + oldfs = get_fs(); + set_fs(KERNEL_DS); + printk(KERN_INFO "initKernelEnv\n"); +} + + int MC32X0_WriteCalibration(struct i2c_client *client, int dat[MC32X0_AXES_NUM]) +{ + int err; + u8 buf[9]; + s16 tmp, x_gain, y_gain, z_gain ; + s32 x_off, y_off, z_off; + int temp_cali_dat[MC32X0_AXES_NUM] = { 0 }; + //const struct mc3xx0_hwmsen_convert *pCvt = NULL; + + //pCvt = &mc3xx0_cvt[mc3xx0_current_placement]; + + temp_cali_dat[pCvt->map[MC3XX0_AXIS_X]] = pCvt->sign[MC3XX0_AXIS_X] * dat[MC3XX0_AXIS_X]; + temp_cali_dat[pCvt->map[MC3XX0_AXIS_Y]] = pCvt->sign[MC3XX0_AXIS_Y] * dat[MC3XX0_AXIS_Y]; + temp_cali_dat[pCvt->map[MC3XX0_AXIS_Z]] = pCvt->sign[MC3XX0_AXIS_Z] * dat[MC3XX0_AXIS_Z]; +/* + temp_cali_dat[MC3XX0_AXIS_X] = ((temp_cali_dat[MC3XX0_AXIS_X] * gsensor_gain.x) / GRAVITY_1G_VALUE); + temp_cali_dat[MC3XX0_AXIS_Y] = ((temp_cali_dat[MC3XX0_AXIS_Y] * gsensor_gain.y) / GRAVITY_1G_VALUE); + temp_cali_dat[MC3XX0_AXIS_Z] = ((temp_cali_dat[MC3XX0_AXIS_Z] * gsensor_gain.z) / GRAVITY_1G_VALUE); +*/ + if (is_new_mc34x0) + { + temp_cali_dat[MC3XX0_AXIS_X] = -temp_cali_dat[MC3XX0_AXIS_X]; + temp_cali_dat[MC3XX0_AXIS_Y] = -temp_cali_dat[MC3XX0_AXIS_Y]; + } + else if (is_mc3250) + { + s16 temp = 0; + + temp = temp_cali_dat[MC3XX0_AXIS_X]; + + temp_cali_dat[MC3XX0_AXIS_X] = -temp_cali_dat[MC3XX0_AXIS_Y]; + temp_cali_dat[MC3XX0_AXIS_Y] = temp; + } + + dat[MC3XX0_AXIS_X] = temp_cali_dat[MC3XX0_AXIS_X]; + dat[MC3XX0_AXIS_Y] = temp_cali_dat[MC3XX0_AXIS_Y]; + dat[MC3XX0_AXIS_Z] = temp_cali_dat[MC3XX0_AXIS_Z]; + +#if 0 //modify by zwx + + GSE_LOG("UPDATE dat: (%+3d %+3d %+3d)\n", + dat[MC32X0_AXIS_X], dat[MC32X0_AXIS_Y], dat[MC32X0_AXIS_Z]); + + /*calculate the real offset expected by caller*/ + //cali_temp[MC32X0_AXIS_X] = dat[MC32X0_AXIS_X]; + //cali_temp[MC32X0_AXIS_Y] = dat[MC32X0_AXIS_Y]; + //cali_temp[MC32X0_AXIS_Z] = dat[MC32X0_AXIS_Z]; + //cali[MC32X0_AXIS_Z]= cali[MC32X0_AXIS_Z]-gsensor_gain.z; + + +#endif +// read register 0x21~0x28 +#if 1 //zwx + //if ((err = mc32x0_read_block(client, 0x21, buf, 3))) + //if ((err = p_mc32x0->MC32X0_BUS_READ_FUNC(p_mc32x0->dev_addr, 0x21, &buf[0],3))) + err = i2c_smbus_read_i2c_block_data(client , 0x21 , 3 , &buf[0]); + + //if ((err = mc32x0_read_block(client, 0x24, &buf[3], 3))) + //if ((err = p_mc32x0->MC32X0_BUS_READ_FUNC(p_mc32x0->dev_addr, 0x24, &buf[3],3))) + err = i2c_smbus_read_i2c_block_data(client , 0x24 , 3 , &buf[3]); + + //if ((err = mc32x0_read_block(client, 0x27, &buf[6], 3))) + //if ((err = p_mc32x0->MC32X0_BUS_READ_FUNC(p_mc32x0->dev_addr, 0x27, &buf[6],3))) + err = i2c_smbus_read_i2c_block_data(client , 0x27 , 3 , &buf[6]); + +#else + buf[0] = 0x21; + err = mc32x0_rx_data(client, &buf[0], 3); + buf[3] = 0x24; + err = mc32x0_rx_data(client, &buf[3], 3); + buf[6] = 0x27; + err = mc32x0_rx_data(client, &buf[6], 3); +#endif +#if 1 + // get x,y,z offset + tmp = ((buf[1] & 0x3f) << 8) + buf[0]; + if (tmp & 0x2000) + tmp |= 0xc000; + x_off = tmp; + + tmp = ((buf[3] & 0x3f) << 8) + buf[2]; + if (tmp & 0x2000) + tmp |= 0xc000; + y_off = tmp; + + tmp = ((buf[5] & 0x3f) << 8) + buf[4]; + if (tmp & 0x2000) + tmp |= 0xc000; + z_off = tmp; + + // get x,y,z gain + x_gain = ((buf[1] >> 7) << 8) + buf[6]; + y_gain = ((buf[3] >> 7) << 8) + buf[7]; + z_gain = ((buf[5] >> 7) << 8) + buf[8]; + + // prepare new offset + x_off = x_off + 16 * dat[MC32X0_AXIS_X] * 256 * 128 / 3 / gsensor_gain.x / (40 + x_gain); + y_off = y_off + 16 * dat[MC32X0_AXIS_Y] * 256 * 128 / 3 / gsensor_gain.y / (40 + y_gain); + z_off = z_off + 16 * dat[MC32X0_AXIS_Z] * 256 * 128 / 3 / gsensor_gain.z / (40 + z_gain); + + //storege the cerrunt offset data with DOT format + offset_data[0] = x_off; + offset_data[1] = y_off; + offset_data[2] = z_off; + + //storege the cerrunt Gain data with GOT format + gain_data[0] = 256*8*128/3/(40+x_gain); + gain_data[1] = 256*8*128/3/(40+y_gain); + gain_data[2] = 256*8*128/3/(40+z_gain); + printk("%d %d ======================\n\n ",gain_data[0],x_gain); +#endif + buf[0]=0x43; + //mc32x0_write_block(client, 0x07, buf, 1); + //mc32x0_write_reg(client,0x07,0x43); + //p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, 0x07, &buf[0], 1 ); + i2c_smbus_write_byte_data(client, 0x07,buf[0]); + buf[0] = x_off & 0xff; + buf[1] = ((x_off >> 8) & 0x3f) | (x_gain & 0x0100 ? 0x80 : 0); + buf[2] = y_off & 0xff; + buf[3] = ((y_off >> 8) & 0x3f) | (y_gain & 0x0100 ? 0x80 : 0); + buf[4] = z_off & 0xff; + buf[5] = ((z_off >> 8) & 0x3f) | (z_gain & 0x0100 ? 0x80 : 0); + + + //mc32x0_write_block(client, 0x21, buf, 6); + //p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, 0x21, &buf[0], 6 ); + i2c_smbus_write_i2c_block_data(client, 0x21, 2,&buf[0]); + i2c_smbus_write_i2c_block_data(client, 0x21+2, 2,&buf[2]); + i2c_smbus_write_i2c_block_data(client, 0x21+4, 2,&buf[4]); + + + buf[0]=0x41; + //mc32x0_write_block(client, 0x07, buf, 1); + //p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, 0x07, &buf[0], 1 ); + i2c_smbus_write_byte_data(client, 0x07,buf[0]); + //mc32x0_write_reg(client,0x07,0x41); + + return err; + +} +/* +int mcube_read_cali_file(struct i2c_client *client) +{ + int cali_data[3]; + int err =0; + + printk("%s %d\n",__func__,__LINE__); + //MCUBE_BACKUP_FILE + READ_FROM_BACKUP = false; + //MCUBE_BACKUP_FILE + initKernelEnv(); + fd_file = openFile(CALIB_PATH,O_RDONLY,0); + //MCUBE_BACKUP_FILE + if (fd_file == NULL) + { + fd_file = openFile(BACKUP_CALIB_PATH, O_RDONLY, 0); + if(fd_file != NULL) + { + READ_FROM_BACKUP = true; + } + } + //MCUBE_BACKUP_FILE + if (fd_file == NULL) + { + GSE_LOG("fail to open\n"); + cali_data[0] = 0; + cali_data[1] = 0; + cali_data[2] = 0; + return 1; + } + else + { + printk("%s %d\n",__func__,__LINE__); + memset(backup_buf,0,64); + if ((err = readFile(fd_file,backup_buf,128))>0) + GSE_LOG("buf:%s\n",backup_buf); + else + GSE_LOG("read file error %d\n",err); + printk("%s %d\n",__func__,__LINE__); + + set_fs(oldfs); + closeFile(fd_file); + + sscanf(backup_buf, "%d %d %d",&cali_data[MC32X0_AXIS_X], &cali_data[MC32X0_AXIS_Y], &cali_data[MC32X0_AXIS_Z]); + GSE_LOG("cali_data: %d %d %d\n", cali_data[MC32X0_AXIS_X], cali_data[MC32X0_AXIS_Y], cali_data[MC32X0_AXIS_Z]); + + //cali_data1[MC32X0_AXIS_X] = cali_data[MC32X0_AXIS_X] * gsensor_gain.x / GRAVITY_EARTH_1000; + //cali_data1[MC32X0_AXIS_Y] = cali_data[MC32X0_AXIS_Y] * gsensor_gain.y / GRAVITY_EARTH_1000; + //cali_data1[MC32X0_AXIS_Z] = cali_data[MC32X0_AXIS_Z] * gsensor_gain.z / GRAVITY_EARTH_1000; + //cali_data[MC32X0_AXIS_X]=-cali_data[MC32X0_AXIS_X]; + //cali_data[MC32X0_AXIS_Y]=-cali_data[MC32X0_AXIS_Y]; + //cali_data[MC32X0_AXIS_Z]=-cali_data[MC32X0_AXIS_Z]; + + //GSE_LOG("cali_data1: %d %d %d\n", cali_data1[MC32X0_AXIS_X], cali_data1[MC32X0_AXIS_Y], cali_data1[MC32X0_AXIS_Z]); + printk("%s %d\n",__func__,__LINE__); + MC32X0_WriteCalibration(client,cali_data); + } + + return 0; +} +*/ + +void MC32X0_rbm(struct i2c_client *client, int enable) +{ + //int err; + char buf1[3]; + if(enable == 1 ) + { +#if 1 + buf1[0] = 0x43; + //err = mc32x0_write_block(client, 0x07, buf1, 0x01); + //err = p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, 0x07, &buf1[0], 1 ); + i2c_smbus_write_byte_data(client, 0x07,buf1[0]); + buf1[0] = 0x02; + //err = mc32x0_write_block(client, 0x14, buf1, 0x01); + //err = p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, 0x14, &buf1[0], 1 ); + i2c_smbus_write_byte_data(client, 0x14,buf1[0]); + buf1[0] = 0x41; + //err = mc32x0_write_block(client, 0x07, buf1, 0x01); + //err = p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, 0x07, &buf1[0], 1 ); + i2c_smbus_write_byte_data(client, 0x07,buf1[0]); +#else + err = mc32x0_write_reg(client,0x07,0x43); + err = mc32x0_write_reg(client,0x14,0x02); + err = mc32x0_write_reg(client,0x07,0x41); +#endif + enable_RBM_calibration =1; + + GSE_LOG("set rbm!!\n"); + + msleep(10); + } + else if(enable == 0 ) + { +#if 1 + buf1[0] = 0x43; + //err = mc32x0_write_block(client, 0x07, buf1, 0x01); + //err = p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, 0x07, &buf1[0], 1 ); + i2c_smbus_write_byte_data(client, 0x07,buf1[0]); + + buf1[0] = 0x00; + //err = mc32x0_write_block(client, 0x14, buf1, 0x01); + //err = p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, 0x14, &buf1[0], 1 ); + i2c_smbus_write_byte_data(client, 0x14,buf1[0]); + buf1[0] = 0x41; + //err = mc32x0_write_block(client, 0x07, buf1, 0x01); + //err = p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, 0x07, &buf1[0], 1 ); + i2c_smbus_write_byte_data(client, 0x07,buf1[0]); +#else + err = mc32x0_write_reg(client,0x07,0x43); + err = mc32x0_write_reg(client,0x14,0x00); + err = mc32x0_write_reg(client,0x07,0x41); +#endif + enable_RBM_calibration =0; + + GSE_LOG("clear rbm!!\n"); + + msleep(10); + } +} + +/*----------------------------------------------------------------------------*/ + int MC32X0_ReadData_RBM(struct i2c_client *client,int data[MC32X0_AXES_NUM]) +{ + //u8 uData; + u8 addr = 0x0d; + u8 rbm_buf[MC32X0_DATA_LEN] = {0}; + int err = 0; + + + //err = p_mc32x0->MC32X0_BUS_READ_FUNC(p_mc32x0->dev_addr, addr, &rbm_buf[0],6); + err = i2c_smbus_read_i2c_block_data(client , addr , 6 , rbm_buf); + //err = mc32x0_read_block(client, addr, rbm_buf, 0x06); + + data[MC32X0_AXIS_X] = (s16)((rbm_buf[0]) | (rbm_buf[1] << 8)); + data[MC32X0_AXIS_Y] = (s16)((rbm_buf[2]) | (rbm_buf[3] << 8)); + data[MC32X0_AXIS_Z] = (s16)((rbm_buf[4]) | (rbm_buf[5] << 8)); + + GSE_LOG("rbm_buf<<<<<[%02x %02x %02x %02x %02x %02x]\n",rbm_buf[0], rbm_buf[2], rbm_buf[2], rbm_buf[3], rbm_buf[4], rbm_buf[5]); + GSE_LOG("RBM<<<<<[%04x %04x %04x]\n", data[MC32X0_AXIS_X], data[MC32X0_AXIS_Y], data[MC32X0_AXIS_Z]); + GSE_LOG("RBM<<<<<[%04d %04d %04d]\n", data[MC32X0_AXIS_X], data[MC32X0_AXIS_Y], data[MC32X0_AXIS_Z]); + return err; +} + + + int MC32X0_ReadRBMData(struct i2c_client *client, char *buf) +{ + //struct mc32x0_data *mc32x0 = i2c_get_clientdata(client); + int res = 0; + int data[3]; + + if (!buf) + { + return EINVAL; + } + + mc32x0_set_mode(client,MC32X0_WAKE); +/* + if(mc32x0->status == mc32x0_CLOSE) + { + res = mc32x0_start(client, 0); + if(res) + { + GSE_ERR("Power on mc32x0 error %d!\n", res); + } + } +*/ + if((res = MC32X0_ReadData_RBM(client,data))) + { + GSE_ERR("%s I2C error: ret value=%d",__func__, res); + return EIO; + } + else + { + sprintf(buf, "%04x %04x %04x", data[MC32X0_AXIS_X], + data[MC32X0_AXIS_Y], data[MC32X0_AXIS_Z]); + + } + + return 0; +} + int MC32X0_ReadOffset(struct i2c_client *client,s16 ofs[MC32X0_AXES_NUM]) +{ + int err; + u8 off_data[6]; + + + if(McubeID &MCUBE_8G_14BIT) + { + + //if ((err = mc32x0_read_block(client, MC32X0_XOUT_EX_L_REG, off_data, MC32X0_DATA_LEN))) + //if ((err = p_mc32x0->MC32X0_BUS_READ_FUNC(p_mc32x0->dev_addr, MC32X0_XOUT_EX_L_REG, &off_data[0],MC32X0_DATA_LEN))) + err = i2c_smbus_read_i2c_block_data(client , MC32X0_XOUT_EX_L_REG , MC32X0_DATA_LEN , off_data); + + ofs[MC32X0_AXIS_X] = ((s16)(off_data[0]))|((s16)(off_data[1])<<8); + ofs[MC32X0_AXIS_Y] = ((s16)(off_data[2]))|((s16)(off_data[3])<<8); + ofs[MC32X0_AXIS_Z] = ((s16)(off_data[4]))|((s16)(off_data[5])<<8); + } + else if(McubeID &MCUBE_1_5G_8BIT) + { + //if ((err = mc32x0_read_block(client, 0, off_data, 3))) + //if ((err = p_mc32x0->MC32X0_BUS_READ_FUNC(p_mc32x0->dev_addr, 0, &off_data[0],3))) + err = i2c_smbus_read_i2c_block_data(client , 0 , 3 , off_data); + + ofs[MC32X0_AXIS_X] = (s8)off_data[0]; + ofs[MC32X0_AXIS_Y] = (s8)off_data[1]; + ofs[MC32X0_AXIS_Z] = (s8)off_data[2]; + } + + GSE_LOG("MC32X0_ReadOffset %d %d %d \n",ofs[MC32X0_AXIS_X] ,ofs[MC32X0_AXIS_Y],ofs[MC32X0_AXIS_Z]); + + return 0; +} +/*----------------------------------------------------------------------------*/ + int MC32X0_ResetCalibration(struct i2c_client *client) +{ + + u8 buf[6]; + s16 tmp; + int err; +#if 1 //zwx + buf[0] = 0x43; + //if(err = mc32x0_write_block(client, 0x07, buf, 1)) + //if(err = p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, 0x07, &buf[0], 1 )) + if((err = i2c_smbus_write_byte_data(client, 0x07,buf[0]))) + { + GSE_ERR("error 0x07: %d\n", err); + } + + + //if(err = mc32x0_write_block(client, 0x21, offset_buf, 6)) // add by liang for writing offset register as OTP value + //if(err = p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, 0x21, &offset_buf[0], 6 )) + if((err = i2c_smbus_write_i2c_block_data(client, 0x21, 6,offset_buf))) + { + GSE_ERR("error: %d\n", err); + } + + buf[0] = 0x41; + //if(err = mc32x0_write_block(client, 0x07, buf, 1)) + //if(err = p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, 0x07, &buf[0], 1 )) + if((err = i2c_smbus_write_byte_data(client, 0x07,buf[0]))) + { + GSE_ERR("error: %d\n", err); + } +#else + mc32x0_write_reg(client,0x07,0x43); + + mc32x0_write_block(client, 0x21, offset_buf, 6); + + mc32x0_write_reg(client,0x07,0x41); +#endif + msleep(20); + + tmp = ((offset_buf[1] & 0x3f) << 8) + offset_buf[0]; // add by Liang for set offset_buf as OTP value + if (tmp & 0x2000) + tmp |= 0xc000; + offset_data[0] = tmp; + + tmp = ((offset_buf[3] & 0x3f) << 8) + offset_buf[2]; // add by Liang for set offset_buf as OTP value + if (tmp & 0x2000) + tmp |= 0xc000; + offset_data[1] = tmp; + + tmp = ((offset_buf[5] & 0x3f) << 8) + offset_buf[4]; // add by Liang for set offset_buf as OTP value + if (tmp & 0x2000) + tmp |= 0xc000; + offset_data[2] = tmp; + + //memset(mc32x0->cali_sw, 0x00, sizeof(mc32x0->cali_sw)); + return 0; + +} +/*----------------------------------------------------------------------------*/ + int MC32X0_ReadCalibration(struct i2c_client *client,int dat[MC32X0_AXES_NUM]) +{ + + signed short MC_offset[MC32X0_AXES_NUM+1]; /*+1: for 4-byte alignment*/ + int err; + memset(MC_offset, 0, sizeof(MC_offset)); + if ((err = MC32X0_ReadOffset(client, MC_offset))) { + GSE_ERR("read offset fail, %d\n", err); + return err; + } + + dat[MC32X0_AXIS_X] = MC_offset[MC32X0_AXIS_X]; + dat[MC32X0_AXIS_Y] = MC_offset[MC32X0_AXIS_Y]; + dat[MC32X0_AXIS_Z] = MC_offset[MC32X0_AXIS_Z]; + //modify by zwx + //GSE_LOG("MC32X0_ReadCalibration %d %d %d \n",dat[mc32x0->cvt.map[MC32X0_AXIS_X]] ,dat[mc32x0->cvt.map[MC32X0_AXIS_Y]],dat[mc32x0->cvt.map[MC32X0_AXIS_Z]]); + + return 0; +} + +/*----------------------------------------------------------------------------*/ + + int MC32X0_ReadData(struct i2c_client *client, s16 buffer[MC32X0_AXES_NUM]) +{ + unsigned char buf[6]; + signed char buf1[6]; + char rbm_buf[6]; + int ret; + //int err = 0; + + #ifdef SUPPORT_VIRTUAL_Z_SENSOR + int tempX=0; + int tempY=0; + int tempZ=0; + #endif + + if ( enable_RBM_calibration == 0) + { + //err = hwmsen_read_block(client, addr, buf, 0x06); + } + else if (enable_RBM_calibration == 1) + { + memset(rbm_buf, 0, 6); + //rbm_buf[0] = mc32x0_REG_RBM_DATA; + //ret = mc32x0_rx_data(client, &rbm_buf[0], 6); + //ret = p_mc32x0->MC32X0_BUS_READ_FUNC(p_mc32x0->dev_addr, 0x0d, &rbm_buf[0],6); + i2c_smbus_read_i2c_block_data(client , 0x0d , 2 , &rbm_buf[0]); + i2c_smbus_read_i2c_block_data(client , 0x0d+2 , 2 , &rbm_buf[2]); + i2c_smbus_read_i2c_block_data(client , 0x0d+4 , 2 , &rbm_buf[4]); + } + + if ( enable_RBM_calibration == 0) + { + + if(McubeID &MC32X0_HIGH_END) + { + #ifdef MC32X0_HIGH_END + ret = i2c_smbus_read_i2c_block_data(client , MC32X0_XOUT_EX_L_REG , 6 , buf); + //ret = p_mc32x0->MC32X0_BUS_READ_FUNC(p_mc32x0->dev_addr, MC32X0_XOUT_EX_L_REG, &buf[0],6); + + buffer[0] = (signed short)((buf[0])|(buf[1]<<8)); + buffer[1] = (signed short)((buf[2])|(buf[3]<<8)); + buffer[2] = (signed short)((buf[4])|(buf[5]<<8)); + #endif + } + else if(McubeID &MC32X0_LOW_END) + { + #ifdef MC32X0_LOW_END + ret = i2c_smbus_read_i2c_block_data(client , MC32X0_XOUT_REG , 3 , buf1); + //ret = p_mc32x0->MC32X0_BUS_READ_FUNC(p_mc32x0->dev_addr, MC32X0_XOUT_REG, &buf[0],3); + + buffer[0] = (signed short)buf1[0]; + buffer[1] = (signed short)buf1[1]; + buffer[2] = (signed short)buf1[2]; + #endif + } + #ifdef SUPPORT_VIRTUAL_Z_SENSOR //add 2013-10-23 + if (g_virtual_z) + { + //printk("%s 1\n", __FUNCTION__); + + tempX = buffer[MC32X0_AXIS_X]; + tempY = buffer[MC32X0_AXIS_Y]; + tempZ = buffer[MC32X0_AXIS_Z]; + //printk(" %d:Verify_Z_Railed() %d\n", (int)buffer[MC32X0_AXIS_Z], Verify_Z_Railed((int)buffer[MC32X0_AXIS_Z], LOW_RESOLUTION)); + if(1 == Verify_Z_Railed((int)buffer[MC32X0_AXIS_Z], LOW_RESOLUTION)) // z-railed + { + Railed = 1; + + GSE_LOG("%s: Z railed", __func__); + //printk("%s: Z railed \n", __func__); + if (G_2_REVERSE_VIRTUAL_Z == 1) + buffer[MC32X0_AXIS_Z] = (s8) ( gsensor_gain.z - (abs(tempX) + abs(tempY))); + else + buffer[MC32X0_AXIS_Z] = (s8) -( gsensor_gain.z - (abs(tempX) + abs(tempY))); + } + else + { + Railed = 0; + } + } + #endif + mcprintkreg("MC32X0_ReadData : %d %d %d \n",buffer[0],buffer[1],buffer[2]); + } + else if (enable_RBM_calibration == 1) + { + buffer[MC32X0_AXIS_X] = (s16)((rbm_buf[0]) | (rbm_buf[1] << 8)); + buffer[MC32X0_AXIS_Y] = (s16)((rbm_buf[2]) | (rbm_buf[3] << 8)); + buffer[MC32X0_AXIS_Z] = (s16)((rbm_buf[4]) | (rbm_buf[5] << 8)); + + GSE_LOG("%s RBM<<<<<[%08d %08d %08d]\n", __func__,buffer[MC32X0_AXIS_X], buffer[MC32X0_AXIS_Y], buffer[MC32X0_AXIS_Z]); + if(gain_data[0] == 0) + { + buffer[MC32X0_AXIS_X] = 0; + buffer[MC32X0_AXIS_Y] = 0; + buffer[MC32X0_AXIS_Z] = 0; + return 0; + } + buffer[MC32X0_AXIS_X] = (buffer[MC32X0_AXIS_X] + offset_data[0]/2)*gsensor_gain.x/gain_data[0]; + buffer[MC32X0_AXIS_Y] = (buffer[MC32X0_AXIS_Y] + offset_data[1]/2)*gsensor_gain.y/gain_data[1]; + buffer[MC32X0_AXIS_Z] = (buffer[MC32X0_AXIS_Z] + offset_data[2]/2)*gsensor_gain.z/gain_data[2]; + + #ifdef SUPPORT_VIRTUAL_Z_SENSOR // add 2013-10-23 + if (g_virtual_z) + { + tempX = buffer[MC32X0_AXIS_X]; + tempY = buffer[MC32X0_AXIS_Y]; + tempZ = buffer[MC32X0_AXIS_Z]; + //printk("%s 2\n", __FUNCTION__); + GSE_LOG("Original RBM<<<<<[%08d %08d %08d]\n", buffer[MC32X0_AXIS_X], buffer[MC32X0_AXIS_Y], buffer[MC32X0_AXIS_Z]); + printk("Verify_Z_Railed() %d\n", Verify_Z_Railed((int)buffer[MC32X0_AXIS_Z], RBM_RESOLUTION)); + if(1 == Verify_Z_Railed(buffer[MC32X0_AXIS_Z], RBM_RESOLUTION)) // z-railed + { + GSE_LOG("%s: Z Railed in RBM mode",__FUNCTION__); + //printk("%s: Z Railed in RBM mode\n",__FUNCTION__); + if (G_2_REVERSE_VIRTUAL_Z == 1) + buffer[MC32X0_AXIS_Z] = (s16) ( gsensor_gain.z - (abs(tempX) + abs(tempY))); + else + buffer[MC32X0_AXIS_Z] = (s16) -( gsensor_gain.z - (abs(tempX) + abs(tempY))); + } + GSE_LOG("RBM<<<<<[%08d %08d %08d]\n", buffer[MC32X0_AXIS_X], buffer[MC32X0_AXIS_Y], buffer[MC32X0_AXIS_Z]); + } + #endif + + GSE_LOG("%s offset_data <<<<<[%d %d %d]\n", __func__,offset_data[0], offset_data[1], offset_data[2]); + + GSE_LOG("%s gsensor_gain <<<<<[%d %d %d]\n", __func__,gsensor_gain.x, gsensor_gain.y, gsensor_gain.z); + + GSE_LOG("%s gain_data <<<<<[%d %d %d]\n", __func__,gain_data[0], gain_data[1], gain_data[2]); + + GSE_LOG("%s RBM->RAW <<<<<[%d %d %d]\n", __func__,buffer[MC32X0_AXIS_X], buffer[MC32X0_AXIS_Y], buffer[MC32X0_AXIS_Z]); + } + + return 0; +} + +int MC32X0_ReadRawData(struct i2c_client *client, char * buf) +{ + + + int res = 0; + s16 raw_buf[3]; + + if (!buf) + { + return EINVAL; + } + + //mc32x0_power_up(mc32x0); + mc32x0_set_mode(client, MC32X0_WAKE); + if((res = MC32X0_ReadData(client,&raw_buf[0]))) + { + printk("%s %d\n",__FUNCTION__, __LINE__); + GSE_ERR("I2C error: ret value=%d", res); + return EIO; + } + else + { + //const struct mc3xx0_hwmsen_convert *pCvt = &mc3xx0_cvt[mc3xx0_current_placement]; + GSE_LOG("UPDATE dat: (%+3d %+3d %+3d)\n", + raw_buf[MC32X0_AXIS_X], raw_buf[MC32X0_AXIS_Y], raw_buf[MC32X0_AXIS_Z]); + + //G_RAW_DATA[MC32X0_AXIS_X] = raw_buf[0]; + //G_RAW_DATA[MC32X0_AXIS_Y] = raw_buf[1]; + //G_RAW_DATA[MC32X0_AXIS_Z] = raw_buf[2]; + //G_RAW_DATA[MC32X0_AXIS_Z] = G_RAW_DATA[MC32X0_AXIS_Z] + gsensor_gain.z; +/* + raw_buf[MC3XX0_AXIS_X] = ((raw_buf[MC3XX0_AXIS_X] * GRAVITY_1G_VALUE) / gsensor_gain.x); + raw_buf[MC3XX0_AXIS_Y] = ((raw_buf[MC3XX0_AXIS_Y] * GRAVITY_1G_VALUE) / gsensor_gain.y); + raw_buf[MC3XX0_AXIS_Z] = ((raw_buf[MC3XX0_AXIS_Z] * GRAVITY_1G_VALUE) / gsensor_gain.z); +*/ + if (is_new_mc34x0) + { + raw_buf[MC3XX0_AXIS_X] = -raw_buf[MC3XX0_AXIS_X]; + raw_buf[MC3XX0_AXIS_Y] = -raw_buf[MC3XX0_AXIS_Y]; + } + else if (is_mc3250) + { + s16 temp = 0; + + temp = raw_buf[MC3XX0_AXIS_X]; + + raw_buf[MC3XX0_AXIS_X] = raw_buf[MC3XX0_AXIS_Y]; + raw_buf[MC3XX0_AXIS_Y] = -temp; + } + + G_RAW_DATA[MC3XX0_AXIS_X] = pCvt->sign[MC3XX0_AXIS_X] * raw_buf[pCvt->map[MC3XX0_AXIS_X]]; + G_RAW_DATA[MC3XX0_AXIS_Y] = pCvt->sign[MC3XX0_AXIS_Y] * raw_buf[pCvt->map[MC3XX0_AXIS_Y]]; + G_RAW_DATA[MC3XX0_AXIS_Z] = pCvt->sign[MC3XX0_AXIS_Z] * raw_buf[pCvt->map[MC3XX0_AXIS_Z]]; + + G_RAW_DATA[MC32X0_AXIS_Z] += gsensor_gain.z*(pCvt->sign[MC3XX0_AXIS_Z])*(1);//-=GRAVITY_1G_VALUE; + + //printk("%s %d\n",__FUNCTION__, __LINE__); + sprintf(buf, "%04x %04x %04x", G_RAW_DATA[MC32X0_AXIS_X], + G_RAW_DATA[MC32X0_AXIS_Y], G_RAW_DATA[MC32X0_AXIS_Z]); + GSE_LOG("G_RAW_DATA: (%+3d %+3d %+3d)\n", + G_RAW_DATA[MC32X0_AXIS_X], G_RAW_DATA[MC32X0_AXIS_Y], G_RAW_DATA[MC32X0_AXIS_Z]); + } + return 0; +} + +int mc32x0_reset (struct i2c_client *client) +{ + + s16 tmp, x_gain, y_gain, z_gain ; + s32 x_off, y_off, z_off; + u8 buf[3]; + int err; + //mc32x0_write_reg(client,0x1b,0x6d); + buf[0]=0x6d; + //p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, 0x1b, &buf[0], 1 ); + i2c_smbus_write_byte_data(client, 0x1b,buf[0]); + + //mc32x0_write_reg(client,0x1b,0x43); + buf[0]=0x43; + //p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, 0x1b, &buf[0], 1 ); + i2c_smbus_write_byte_data(client, 0x1b,buf[0]); + msleep(5); + + //mc32x0_write_reg(client,0x07,0x43); + buf[0]=0x43; + //p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, 0x07, &buf[0], 1 ); + i2c_smbus_write_byte_data(client, 0x07,buf[0]); + //mc32x0_write_reg(client,0x1C,0x80); + buf[0]=0x80; + //p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, 0x1C, &buf[0], 1 ); + i2c_smbus_write_byte_data(client, 0x1c,buf[0]); + //mc32x0_write_reg(client,0x17,0x80); + buf[0]=0x80; + //p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, 0x17, &buf[0], 1 ); + i2c_smbus_write_byte_data(client, 0x17,buf[0]); + msleep(5); + //mc32x0_write_reg(client,0x1C,0x00); + buf[0]=0x00; + //p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, 0x1C, &buf[0], 1 ); + i2c_smbus_write_byte_data(client, 0x1c,buf[0]); + //mc32x0_write_reg(client,0x17,0x00); + buf[0]=0x00; + //p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, 0x17, &buf[0], 1 ); + i2c_smbus_write_byte_data(client, 0x17,buf[0]); + msleep(5); + + +/* + if ((err = mc32x0_read_block(new_client, 0x21, offset_buf, 6))) //add by Liang for storeage OTP offsef register value + { + GSE_ERR("error: %d\n", err); + return err; + } +*/ + memset(offset_buf, 0, 9); + //offset_buf[0] = 0x21; + //err = mc32x0_rx_data(client, offset_buf, 9); + //err = p_mc32x0->MC32X0_BUS_READ_FUNC(p_mc32x0->dev_addr, 0x21, &offset_buf[0],9); + err = i2c_smbus_read_i2c_block_data(client , 0x21 , 9 , offset_buf); + + + tmp = ((offset_buf[1] & 0x3f) << 8) + offset_buf[0]; + if (tmp & 0x2000) + tmp |= 0xc000; + x_off = tmp; + + tmp = ((offset_buf[3] & 0x3f) << 8) + offset_buf[2]; + if (tmp & 0x2000) + tmp |= 0xc000; + y_off = tmp; + + tmp = ((offset_buf[5] & 0x3f) << 8) + offset_buf[4]; + if (tmp & 0x2000) + tmp |= 0xc000; + z_off = tmp; + + // get x,y,z gain + x_gain = ((offset_buf[1] >> 7) << 8) + offset_buf[6]; + y_gain = ((offset_buf[3] >> 7) << 8) + offset_buf[7]; + z_gain = ((offset_buf[5] >> 7) << 8) + offset_buf[8]; + + + //storege the cerrunt offset data with DOT format + offset_data[0] = x_off; + offset_data[1] = y_off; + offset_data[2] = z_off; + + //storege the cerrunt Gain data with GOT format + gain_data[0] = 256*8*128/3/(40+x_gain); + gain_data[1] = 256*8*128/3/(40+y_gain); + gain_data[2] = 256*8*128/3/(40+z_gain); + printk("offser gain = %d %d %d %d %d %d======================\n\n ", + gain_data[0],gain_data[1],gain_data[2],offset_data[0],offset_data[1],offset_data[2]); + + return 0; +} +#endif + +int mc32x0_read_accel_xyz(struct i2c_client *client, s16 * acc) +{ + int comres; + s16 raw_data[MC3XX0_AXIS_NUM] = { 0 }; + //const struct mc3xx0_hwmsen_convert *pCvt = &mc3xx0_cvt[mc3xx0_current_placement]; +#ifdef DOT_CALI + s16 raw_buf[6]; + + + comres = MC32X0_ReadData(client,&raw_buf[0]); + + acc[0] = raw_buf[0]; + acc[1] = raw_buf[1]; + acc[2] = raw_buf[2]; +#else + unsigned char raw_buf[6]; + signed char raw_buf1[3]; + if(McubeID &MC32X0_HIGH_END) + { + #ifdef MC32X0_HIGH_END + comres = i2c_smbus_read_i2c_block_data(client , MC32X0_XOUT_EX_L_REG , 6 , raw_buf); + //comres = p_mc32x0->MC32X0_BUS_READ_FUNC(p_mc32x0->dev_addr, MC32X0_XOUT_EX_L_REG, &data[0],6); + + acc[0] = (signed short)((raw_buf[0])|(raw_buf[1]<<8)); + acc[1] = (signed short)((raw_buf[2])|(raw_buf[3]<<8)); + acc[2] = (signed short)((raw_buf[4])|(raw_buf[5]<<8)); + #endif + } + else if(McubeID &MC32X0_LOW_END) + { + #ifdef MC32X0_LOW_END + comres = i2c_smbus_read_i2c_block_data(client , MC32X0_XOUT_REG , 3 , raw_buf1); + //comres = p_mc32x0->MC32X0_BUS_READ_FUNC(p_mc32x0->dev_addr, MC32X0_XOUT_REG, &data[0],3); + + acc[0] = (signed short)raw_buf1[0]; + acc[1] = (signed short)raw_buf1[1]; + acc[2] = (signed short)raw_buf1[2]; + #endif + } +#endif + + raw_data[MC3XX0_AXIS_X] = acc[MC3XX0_AXIS_X]; + raw_data[MC3XX0_AXIS_Y] = acc[MC3XX0_AXIS_Y]; + raw_data[MC3XX0_AXIS_Z] = acc[MC3XX0_AXIS_Z]; +/* + raw_data[MC3XX0_AXIS_X] = ((raw_data[MC3XX0_AXIS_X] * GRAVITY_1G_VALUE) / gsensor_gain.x); + raw_data[MC3XX0_AXIS_Y] = ((raw_data[MC3XX0_AXIS_Y] * GRAVITY_1G_VALUE) / gsensor_gain.y); + raw_data[MC3XX0_AXIS_Z] = ((raw_data[MC3XX0_AXIS_Z] * GRAVITY_1G_VALUE) / gsensor_gain.z); +*/ + if (is_new_mc34x0) + { + raw_data[MC3XX0_AXIS_X] = -raw_data[MC3XX0_AXIS_X]; + raw_data[MC3XX0_AXIS_Y] = -raw_data[MC3XX0_AXIS_Y]; + } + else if (is_mc3250) + { + s16 temp = 0; + + temp = raw_data[MC3XX0_AXIS_X]; + + raw_data[MC3XX0_AXIS_X] = raw_data[MC3XX0_AXIS_Y]; + raw_data[MC3XX0_AXIS_Y] = -temp; + } + + acc[MC3XX0_AXIS_X] = pCvt->sign[MC3XX0_AXIS_X] * raw_data[pCvt->map[MC3XX0_AXIS_X]]; + acc[MC3XX0_AXIS_Y] = pCvt->sign[MC3XX0_AXIS_Y] * raw_data[pCvt->map[MC3XX0_AXIS_Y]]; + acc[MC3XX0_AXIS_Z] = pCvt->sign[MC3XX0_AXIS_Z] * raw_data[pCvt->map[MC3XX0_AXIS_Z]]; + + return comres; + +} + +static int mc32x0_measure(struct i2c_client *client, struct acceleration *accel) +{ + + s16 raw[3]; + +#ifdef DOT_CALI + //int ret; +#endif + + +#ifdef DOT_CALI + if( load_cali_flg > 0) + { + /*ret =mcube_read_cali_file(client); + if(ret == 0) + load_cali_flg = ret; + else + load_cali_flg --; + GSE_LOG("load_cali %d\n",ret); */ + MC32X0_WriteCalibration(client,l_sensorconfig.offset); + load_cali_flg = 0; + } +#endif + /* read acceleration data */ + mc32x0_read_accel_xyz(client,&raw[0]); + + accel->x = raw[0] ; + accel->y = raw[1] ; + accel->z = raw[2] ; + return 0; +} + +static void mc32x0_work_func(struct work_struct *work) +{ + struct mc32x0_data *data = container_of(work, struct mc32x0_data, work); + struct acceleration accel = {0}; + + mc32x0_measure(data->client, &accel); + + //printk(KERN_ERR"mc32x0_measure: acc.x=%d, acc.y=%d, acc.z=%d\n", data->inv[0]*accel.x,data->inv[1]*accel.y, data->inv[2]*accel.z); + + //input_report_abs(data->input_dev, data->map[0], data->inv[0]*accel.x); + //input_report_abs(data->input_dev, data->map[1], data->inv[1]*accel.y); + //input_report_abs(data->input_dev, data->map[2], data->inv[2]*accel.z); + + //accel.x = (accel.x&0x00FF) | ((accel.y&0xFF)<<8) | ((accel.z&0xFF)<<16); + + input_report_abs(data->input_dev, ABS_X, accel.x); + input_report_abs(data->input_dev, ABS_Y, accel.y); + input_report_abs(data->input_dev, ABS_Z, accel.z); + input_sync(data->input_dev); + + queue_delayed_work(data->mc32x0_wq, &data->work, msecs_to_jiffies(sample_rate_2_memsec(data->sensor_samp))); +} +/* +static enum hrtimer_restart mc32x0_timer_func(struct hrtimer *timer) +{ + struct mc32x0_data *data = container_of(timer, struct mc32x0_data, timer); + + queue_work(data->mc32x0_wq, &data->work); + hrtimer_start(&data->timer, ktime_set(0, sensor_duration*1000000), HRTIMER_MODE_REL); + + return HRTIMER_NORESTART; +} +*/ +static int mc32x0_enable(struct mc32x0_data *data, int enable) +{ + if(enable){ + msleep(10); + mc32x0_chip_init(data->client); + queue_delayed_work(data->mc32x0_wq, &data->work, msecs_to_jiffies(sample_rate_2_memsec(data->sensor_samp)));//hrtimer_start(&data->timer, ktime_set(0, asensor_duration*1000000), HRTIMER_MODE_REL); + data->enabled = true; + }else{ + cancel_delayed_work_sync(&l_sensorconfig.work);//hrtimer_cancel(&data->timer); + data->enabled = false; + } + return 0; +} +/* +//MCUBE_BACKUP_FILE +static void mcube_copy_file(const char *dstFilePath) +{ + + int err =0; + initKernelEnv(); + + fd_file = openFile(dstFilePath,O_RDWR,0); + if (fd_file == NULL) + { + GSE_LOG("open %s fail\n",dstFilePath); + return; + } + + if ((err = writeFile(fd_file,backup_buf,64))>0) + GSE_LOG("buf:%s\n",backup_buf); + else + GSE_LOG("write file error %d\n",err); + + set_fs(oldfs); ; + closeFile(fd_file); + +} +//MCUBE_BACKUP_FILE +*/ + +extern int wmt_setsyspara(char *varname, char *varval); +static void update_var(void) +{ + char varbuf[64]; + int varlen; + + memset(varbuf, 0, sizeof(varbuf)); + varlen = sizeof(varbuf); + + sprintf(varbuf, "%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d", + l_sensorconfig.op, + l_sensorconfig.int_gpio, + l_sensorconfig.samp, + (pCvt->map[MC3XX0_AXIS_X]), + (pCvt->sign[MC3XX0_AXIS_X]), + (pCvt->map[MC3XX0_AXIS_Y]), + (pCvt->sign[MC3XX0_AXIS_Y]), + (pCvt->map[MC3XX0_AXIS_Z]), + (pCvt->sign[MC3XX0_AXIS_Z]), + l_sensorconfig.offset[0], + l_sensorconfig.offset[1], + l_sensorconfig.offset[2] + ); + + wmt_setsyspara("wmt.io.mc3230sensor",varbuf); +} + +static long mc32x0_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + //int intBuf[SENSOR_DATA_SIZE]; + int ret = 0; + //float convert_para=0.0f; + short enable = 0; + short delay = 0; + //short val=1000/l_sensorconfig.sensor_samp; + unsigned int uval ; +#ifdef DOT_CALI + void __user *data1; + char strbuf[256]; + //int cali[3]; + SENSOR_DATA sensor_data; + struct i2c_client *client = container_of(mc32x0_device.parent, struct i2c_client, dev); + //struct mc32x0_data* this = (struct mc32x0_data *)i2c_get_clientdata(client); /* ?õô????????????. */ +#endif + + switch (cmd) { + case ECS_IOCTL_APP_SET_AFLAG: + // enable/disable sensor + + if (copy_from_user(&enable, (short*)arg, sizeof(short))) + { + errlog("Can't get enable flag!!!\n"); + ret = -EFAULT; + goto errioctl; + } + if ((enable >=0) && (enable <=1)) + { + dbg("driver: disable/enable(%d) gsensor. l_sensorconfig.sensor_samp=%d\n", enable, l_sensorconfig.sensor_samp); + + if (enable != l_sensorconfig.sensor_enable) + { + + l_sensorconfig.sensor_enable = enable; + + } + } else { + errlog("Wrong enable argument!!!\n"); + ret = -EFAULT; + goto errioctl; + } + break; + case ECS_IOCTL_APP_SET_DELAY://IOCTL_SENSOR_SET_DELAY_ACCEL: + /*if(copy_from_user((void *)&sensor_duration, (void __user *) arg, sizeof(short))!=0){ + printk("copy from error in %s.\n",__func__); + }*/ + + // set the rate of g-sensor + if (copy_from_user(&delay,(short*)arg, sizeof(short))) + { + errlog("Can't get set delay!!!\n"); + ret = -EFAULT; + goto errioctl; + } + dbg("Get delay=%d \n", delay); + + if ((delay >=0) && (delay < 20)) + { + delay = 20; + } else if (delay > 200) + { + delay = 200; + } + if (delay > 0) + { + l_sensorconfig.sensor_samp = 1000/delay; + } else { + errlog("error delay argument(delay=%d)!!!\n",delay); + ret = -EFAULT; + goto errioctl; + } + + break; +/* + case IOCTL_SENSOR_GET_DELAY_ACCEL: + + if(copy_to_user((void __user *) arg, (const void *)&val, sizeof(short))!=0){ + printk("copy to error in %s.\n",__func__); + } + + break;*/ + case WMT_IOCTL_SENSOR_GET_DRVID: + uval = MC3230_DRVID; + if (copy_to_user((unsigned int*)arg, &uval, sizeof(unsigned int))) + { + return -EFAULT; + } + dbg("mc32x0_driver_id:%d\n",uval); + break; + case WMT_IOCTL_SENOR_GET_RESOLUTION: + if(McubeID &MCUBE_1_5G_8BIT) + uval = (8<<8) | 3; //mc3230:8 bit ,+/-1.5g + if (copy_to_user((unsigned int *)arg, &uval, sizeof(unsigned int))) + { + return -EFAULT; + } + printk("<<<<<<suspend)) + //{ + // GSE_ERR("Perform calibration in suspend state!!\n"); + // err = -EINVAL; + //} + else + { + //this->cali_sw[MC32X0_AXIS_X] += sensor_data.x; + //this->cali_sw[MC32X0_AXIS_Y] += sensor_data.y; + //this->cali_sw[MC32X0_AXIS_Z] += sensor_data.z; + + l_sensorconfig.offset[MC32X0_AXIS_X] = sensor_data.x; + l_sensorconfig.offset[MC32X0_AXIS_Y] = sensor_data.y; + l_sensorconfig.offset[MC32X0_AXIS_Z] = sensor_data.z; + + GSE_LOG("GSENSOR_MCUBE_IOCTL_SET_CALI %d %d %d %d %d %d!!\n", l_sensorconfig.offset[MC32X0_AXIS_X], l_sensorconfig.offset[MC32X0_AXIS_Y],l_sensorconfig.offset[MC32X0_AXIS_Z] ,sensor_data.x, sensor_data.y ,sensor_data.z); + + update_var(); + ret = MC32X0_WriteCalibration(client, l_sensorconfig.offset); + } + + break; + + case GSENSOR_IOCTL_CLR_CALI: + GSE_LOG("fwq GSENSOR_IOCTL_CLR_CALI!!\n"); + l_sensorconfig.offset[0] = 0; + l_sensorconfig.offset[1] = 0; + l_sensorconfig.offset[2] = 0; + + update_var(); + ret = MC32X0_ResetCalibration(client); + break; + + case GSENSOR_IOCTL_GET_CALI: + GSE_LOG("fwq mc32x0 GSENSOR_IOCTL_GET_CALI\n"); + + data1 = (unsigned char*)arg; + + if(data1 == NULL) + { + ret = -EINVAL; + break; + } + + if((ret = MC32X0_ReadCalibration(client,l_sensorconfig.offset))) + { + GSE_LOG("fwq mc32x0 MC32X0_ReadCalibration error!!!!\n"); + break; + } + + sensor_data.x = l_sensorconfig.offset[0];//this->cali_sw[MC32X0_AXIS_X]; + sensor_data.y = l_sensorconfig.offset[1];//this->cali_sw[MC32X0_AXIS_Y]; + sensor_data.z = l_sensorconfig.offset[2];//this->cali_sw[MC32X0_AXIS_Z]; + // if(copy_to_user(data, &sensor_data, sizeof(sensor_data))) + + if(copy_to_user(data1, &sensor_data, sizeof(sensor_data))) + { + ret = -EFAULT; + break; + } + break; + // add by liang **** + //add in Sensors_io.h + //#define GSENSOR_IOCTL_SET_CALI_MODE _IOW(GSENSOR, 0x0e, int) + case GSENSOR_IOCTL_SET_CALI_MODE: + GSE_LOG("fwq mc32x0 GSENSOR_IOCTL_SET_CALI_MODE\n"); + break; + + case GSENSOR_MCUBE_IOCTL_READ_RBM_DATA: + GSE_LOG("fwq GSENSOR_MCUBE_IOCTL_READ_RBM_DATA\n"); + data1 = (void __user *) arg; + if(data1 == NULL) + { + ret = -EINVAL; + break; + } + MC32X0_ReadRBMData(client,(char *)&strbuf); + if(copy_to_user(data1, &strbuf, strlen(strbuf)+1)) + { + ret = -EFAULT; + break; + } + break; + + case GSENSOR_MCUBE_IOCTL_SET_RBM_MODE: + GSE_LOG("fwq GSENSOR_MCUBE_IOCTL_SET_RBM_MODE\n"); + //MCUBE_BACKUP_FILE + /*if(READ_FROM_BACKUP==true) + { + + mcube_copy_file(CALIB_PATH); + + READ_FROM_BACKUP = false; + }*/ + //MCUBE_BACKUP_FILE + MC32X0_rbm(client, 1); + + break; + + case GSENSOR_MCUBE_IOCTL_CLEAR_RBM_MODE: + GSE_LOG("fwq GSENSOR_MCUBE_IOCTL_CLEAR_RBM_MODE\n"); + + MC32X0_rbm(client, 0); + + break; + + case GSENSOR_MCUBE_IOCTL_REGISTER_MAP: + GSE_LOG("fwq GSENSOR_MCUBE_IOCTL_REGISTER_MAP\n"); + + //MC32X0_Read_Reg_Map(client); + + break; +#endif + + + + default: + ret = -EINVAL; + break; + } + +errioctl: + + return ret; +} + + +static int mc32x0_open(struct inode *inode, struct file *filp) +{ + /*int ret; + ret = nonseekable_open(inode, filp);*/ + return 0; +} + +static int mc32x0_release(struct inode *inode, struct file *filp) +{ + return 0; +} + +static struct file_operations sensor_fops = +{ + .owner = THIS_MODULE, + .open = mc32x0_open, + .release = mc32x0_release, + .unlocked_ioctl = mc32x0_ioctl, +}; + +//#ifdef CONFIG_HAS_EARLYSUSPEND +static int mc32x0_i2c_suspend(struct platform_device *pdev, pm_message_t state) +{ + /*struct mc32x0_data *data; + char mc32x0_address; + char mc32x0_data; + + //printk("mc32x0_early_suspend 2 \n"); + + data = container_of(handler, struct mc32x0_data, early_suspend); + + hrtimer_cancel(&data->timer); + */ + cancel_delayed_work_sync(&l_sensorconfig.work); + mc32x0_set_mode(l_sensorconfig.client,MC32X0_STANDBY); + return 0; +} + +static int mc32x0_i2c_resume(struct platform_device *pdev) +{ + struct mc32x0_data *data = &l_sensorconfig; + //char mc32x0_address; + //char mc32x0_data; + + //printk("mc32x0_early_resume 2\n"); + + //data = container_of(handler, struct mc32x0_data, early_suspend); + + //Add 20130722 + mc32x0_chip_init(data->client); + MC32X0_ResetCalibration(data->client); + MC32X0_WriteCalibration(data->client,l_sensorconfig.offset);//mcube_read_cali_file(data->client); + //before + + mc32x0_set_mode(data->client,MC32X0_WAKE); + + queue_delayed_work(data->mc32x0_wq, &data->work, msecs_to_jiffies(sample_rate_2_memsec(data->sensor_samp))); + //hrtimer_start(&data->timer, ktime_set(1, 0), HRTIMER_MODE_REL); + return 0; +} +//#endif + +static struct miscdevice mc32x0_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "sensor_ctrl", + .fops = &sensor_fops, +}; + +static int sensor_writeproc( struct file *file, + const char *buffer, + unsigned long count, + void *data ) +{ + + //int inputval = -1; + int enable, sample = -1; + char tembuf[8]; + //unsigned int amsr = 0; + int test = 0; + + mutex_lock(&l_sensorconfig.lock); + memset(tembuf, 0, sizeof(tembuf)); + // get sensor level and set sensor level + if (sscanf(buffer, "isdbg=%d\n", &l_sensorconfig.isdbg)) + { + // only set the dbg flag + } else if (sscanf(buffer, "samp=%d\n", &sample)) + { + if (sample > 0) + { + if (sample != l_sensorconfig.sensor_samp) + { + // should do sth + } + //printk(KERN_ALERT "sensor samp=%d(amsr:%d) has been set.\n", sample, amsr); + } else { + klog("Wrong sample argumnet of sensor.\n"); + } + } else if (sscanf(buffer, "enable=%d\n", &enable)) + { + if ((enable < 0) || (enable > 1)) + { + dbg("The argument to enable/disable g-sensor should be 0 or 1 !!!\n"); + } else if (enable != l_sensorconfig.sensor_enable) + { + //mma_enable_disable(enable); + l_sensorconfig.sensor_enable = enable; + } + } else if (sscanf(buffer, "sensor_test=%d\n", &test)) + { // for test begin + l_sensorconfig.test_pass = 0; + } else if (sscanf(buffer, "sensor_testend=%d\n", &test)) + { // Don nothing only to be compatible the before testing program + } + mutex_unlock(&l_sensorconfig.lock); + return count; +} + +static int sensor_readproc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len = 0; + + len = sprintf(page, + "test_pass=%d\nisdbg=%d\nrate=%d\nenable=%d\n", + l_sensorconfig.test_pass, + l_sensorconfig.isdbg, + l_sensorconfig.sensor_samp, + l_sensorconfig.sensor_enable + ); + return len; +} + +static int mc32x0_probe(struct platform_device *pdev) +{ + int ret = 0; + struct mc32x0_data *data = &l_sensorconfig; + struct i2c_client *client = data->client; + +#ifdef DOT_CALI + load_cali_flg = 30; +#endif + data->sensor_proc = create_proc_entry(GSENSOR_PROC_NAME, 0666, NULL/*&proc_root*/); + if (data->sensor_proc != NULL) + { + data->sensor_proc->write_proc = sensor_writeproc; + data->sensor_proc->read_proc = sensor_readproc; + } + + data->mc32x0_wq = create_singlethread_workqueue("mc32x0_wq"); + if (!data->mc32x0_wq ) + { + ret = -ENOMEM; + goto err_create_workqueue_failed; + } + + INIT_DELAYED_WORK(&data->work, mc32x0_work_func); + //INIT_WORK(&data->work, mc32x0_work_func); + mutex_init(&data->lock); + + + data->input_dev = input_allocate_device(); + if (!data->input_dev) { + ret = -ENOMEM; + goto exit_input_dev_alloc_failed; + } + + + dev.client=client; + + i2c_set_clientdata(client, data); + #ifdef DOT_CALI + mc32x0_reset(client); + #endif + + set_bit(EV_ABS, data->input_dev->evbit); + data->map[0] = G_0; + data->map[1] = G_1; + data->map[2] = G_2; + data->inv[0] = G_0_REVERSE; + data->inv[1] = G_1_REVERSE; + data->inv[2] = G_2_REVERSE; + + input_set_abs_params(data->input_dev, ABS_X, -32*8, 32*8, INPUT_FUZZ, INPUT_FLAT); + input_set_abs_params(data->input_dev, ABS_Y, -32*8, 32*8, INPUT_FUZZ, INPUT_FLAT); + input_set_abs_params(data->input_dev, ABS_Z, -32*8, 32*8, INPUT_FUZZ, INPUT_FLAT); + + data->input_dev->name = "g-sensor"; + + + ret = input_register_device(data->input_dev); + if (ret) { + goto exit_input_register_device_failed; + } + mc32x0_device.parent = &client->dev; + ret = misc_register(&mc32x0_device); + if (ret) { + goto exit_misc_device_register_failed; + } + + ret = sysfs_create_group(&client->dev.kobj, &mc32x0_group); +/* + if (!data->use_irq){ + hrtimer_init(&data->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + data->timer.function = mc32x0_timer_func; + hrtimer_start(&data->timer, ktime_set(1, 0), HRTIMER_MODE_REL); + } +*/ + +#ifdef CONFIG_HAS_EARLYSUSPEND + data->early_suspend.suspend = mc32x0_early_suspend; + data->early_suspend.resume = mc32x0_early_resume; + register_early_suspend(&data->early_suspend); +#endif + data->enabled = true; + queue_delayed_work(data->mc32x0_wq, &data->work, msecs_to_jiffies(sample_rate_2_memsec(data->sensor_samp))); + strcpy(mc32x0_on_off_str,"gsensor_int2"); + dprintk(DEBUG_INIT,"mc32x0 probe ok \n"); + + return 0; +exit_misc_device_register_failed: +exit_input_register_device_failed: + input_free_device(data->input_dev); +exit_input_dev_alloc_failed: + destroy_workqueue(data->mc32x0_wq); +err_create_workqueue_failed: + kfree(data); + printk("mc32x0 probe failed \n"); + return ret; + +} + +static int mc32x0_remove(struct platform_device *pdev) +{ + /*struct mc32x0_data *data = i2c_get_clientdata(client); + + hrtimer_cancel(&data->timer); + input_unregister_device(data->input_dev); + //gpio_release(mc32x0_pin_hd, 2); + misc_deregister(&mc32x0_device); + sysfs_remove_group(&client->dev.kobj, &mc32x0_group); + kfree(data);*/ + + if (NULL != l_sensorconfig.mc32x0_wq) + { + cancel_delayed_work_sync(&l_sensorconfig.work); + flush_workqueue(l_sensorconfig.mc32x0_wq); + destroy_workqueue(l_sensorconfig.mc32x0_wq); + l_sensorconfig.mc32x0_wq = NULL; + } + if (l_sensorconfig.sensor_proc != NULL) + { + remove_proc_entry(GSENSOR_PROC_NAME, NULL); + l_sensorconfig.sensor_proc = NULL; + } + misc_deregister(&mc32x0_device); + input_unregister_device(l_sensorconfig.input_dev); + sysfs_remove_group(&l_sensorconfig.client->dev.kobj, &mc32x0_group); + return 0; +} + +static void mc32x0_shutdown(struct platform_device *pdev) +{ + struct mc32x0_data *data = &l_sensorconfig;//i2c_get_clientdata(client); + if(data->enabled) + mc32x0_enable(data,0); +} +/* +static const struct i2c_device_id mc32x0_id[] = { + { SENSOR_NAME, 0 }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, mc32x0_id); + +static struct i2c_driver mc32x0_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .owner = THIS_MODULE, + .name = SENSOR_NAME, + }, + .id_table = mc32x0_id, + .probe = mc32x0_probe, + .remove = mc32x0_remove, + .shutdown = mc32x0_shutdown, + .detect = gsensor_detect, + .address_list = normal_i2c, +}; +*/ +extern int wmt_getsyspara(char *varname, unsigned char *varval, int *varlen); +static int get_axisset(void) +{ + char varbuf[64]; + int n; + int varlen; + //int tmpoff[3] = {0}; + memset(varbuf, 0, sizeof(varbuf)); + varlen = sizeof(varbuf); + + pCvt = (struct mc3xx0_hwmsen_convert *)kzalloc(sizeof(struct mc3xx0_hwmsen_convert), GFP_KERNEL); + + if (wmt_getsyspara("wmt.io.mc3230.virtualz", varbuf, &varlen)) { + errlog("Can't get gsensor config in u-boot!!!!\n"); + //return -1; + } else { + sscanf(varbuf, "%d", &g_virtual_z); + + } + printk("%s g_virtual_z %d\n", __FUNCTION__, g_virtual_z); + memset(varbuf, 0, sizeof(varbuf)); + if (wmt_getsyspara("wmt.io.mc3230sensor", varbuf, &varlen)) { + errlog("Can't get gsensor config in u-boot!!!!\n"); + return -1; //open it for no env just,not insmod such module 2014-6-30 + } else { + n = sscanf(varbuf, "%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d", + &l_sensorconfig.op, + &l_sensorconfig.int_gpio, + &l_sensorconfig.samp, + &(pCvt->map[MC3XX0_AXIS_X]), + &(pCvt->sign[MC3XX0_AXIS_X]), + &(pCvt->map[MC3XX0_AXIS_Y]), + &(pCvt->sign[MC3XX0_AXIS_Y]), + &(pCvt->map[MC3XX0_AXIS_Z]), + &(pCvt->sign[MC3XX0_AXIS_Z]), + &(l_sensorconfig.offset[0]), + &(l_sensorconfig.offset[1]), + &(l_sensorconfig.offset[2]) + ); + if (n != 12) { + printk(KERN_ERR "gsensor format is error in u-boot!!!\n"); + return -1; + } + l_sensorconfig.sensor_samp = l_sensorconfig.samp; + + + dbg("get the sensor config: %d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d\n", + l_sensorconfig.op, + l_sensorconfig.int_gpio, + l_sensorconfig.samp, + pCvt->map[MC3XX0_AXIS_X], + pCvt->sign[MC3XX0_AXIS_X], + pCvt->map[MC3XX0_AXIS_Y], + pCvt->sign[MC3XX0_AXIS_Y], + pCvt->map[MC3XX0_AXIS_Z], + pCvt->sign[MC3XX0_AXIS_Z], + l_sensorconfig.offset[0], + l_sensorconfig.offset[1], + l_sensorconfig.offset[2] + ); + } + return 0; +} + +static void mc32x0_platform_release(struct device *device) +{ + return; +} + +static struct platform_device mc32x0_pdevice = { + .name = GSENSOR_NAME, + .id = 0, + .dev = { + .release = mc32x0_platform_release, + }, +}; + +static struct platform_driver mc32x0_pdriver = { + .probe = mc32x0_probe, + .remove = mc32x0_remove, + .suspend = mc32x0_i2c_suspend, + .resume = mc32x0_i2c_resume, + .shutdown = mc32x0_shutdown, + .driver = { + .name = GSENSOR_NAME, + }, +}; + + +static int __init mc32x0_init(void) +{ + struct i2c_client *this_client; + int ret = 0; + + dprintk(DEBUG_INIT, "======%s=========. \n", __func__); + // parse g-sensor u-boot arg + ret = get_axisset(); + if (ret < 0) + { + printk("<<<<<%s user choose to no sensor chip!\n", __func__); + return ret; + } + if (!(this_client = sensor_i2c_register_device(0, MC32X0_I2C_ADDR, GSENSOR_NAME))) + { + printk(KERN_ERR"Can't register gsensor i2c device!\n"); + return -1; + } + + if (mc32x0_chip_init(this_client)) + { + printk(KERN_ERR"Failed to init MC32X0!\n"); + sensor_i2c_unregister_device(this_client); + return -1; + } + + //printk(KERN_ERR"McubeID:%d\n",McubeID); + l_sensorconfig.client = this_client; + + l_dev_class = class_create(THIS_MODULE, GSENSOR_NAME); + if (IS_ERR(l_dev_class)){ + ret = PTR_ERR(l_dev_class); + printk(KERN_ERR "Can't class_create gsensor device !!\n"); + return ret; + } + if((ret = platform_device_register(&mc32x0_pdevice))) + { + klog("Can't register mc3230 platform devcie!!!\n"); + return ret; + } + if ((ret = platform_driver_register(&mc32x0_pdriver)) != 0) + { + errlog("Can't register mc3230 platform driver!!!\n"); + return ret; + } + return ret; +} + +static void __exit mc32x0_exit(void) +{ +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&l_sensorconfig.earlysuspend); +#endif + platform_driver_unregister(&mc32x0_pdriver); + platform_device_unregister(&mc32x0_pdevice); + sensor_i2c_unregister_device(l_sensorconfig.client); + class_destroy(l_dev_class); +} + +//********************************************************************************************************* +MODULE_AUTHOR("Long Chen "); +MODULE_DESCRIPTION("mc32x0 driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("1.1"); + +module_init(mc32x0_init); +module_exit(mc32x0_exit); + diff --git a/drivers/input/sensor/mc3230_gsensor/mc32x0_driver.c b/drivers/input/sensor/mc3230_gsensor/mc32x0_driver.c new file mode 100755 index 00000000..19c81b97 --- /dev/null +++ b/drivers/input/sensor/mc3230_gsensor/mc32x0_driver.c @@ -0,0 +1,505 @@ +/* + * Copyright (C) 2011 MCUBE, Inc. + * + * Initial Code: + * Tan Liang + */ + + + + + +#include + +#include "mc32x0_driver.h" + + +mc32x0_t *p_mc32x0; /**< pointer to MC32X0 device structure */ + + +/** API Initialization routine + \param *mc32x0 pointer to MC32X0 structured type + \return result of communication routines + */ + +int mcube_mc32x0_init(mc32x0_t *mc32x0) +{ + int comres=0; + unsigned char data; + + p_mc32x0 = mc32x0; /* assign mc32x0 ptr */ + p_mc32x0->dev_addr = MC32X0_I2C_ADDR; /* preset I2C_addr */ + comres += p_mc32x0->MC32X0_BUS_READ_FUNC(p_mc32x0->dev_addr, MC32X0_CHIP_ID, &data, 1); /* read Chip Id */ + + p_mc32x0->chip_id = data; + + // init other reg + data = 0x63; + comres += p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, 0x1b, &data, 1); + data = 0x43; + comres += p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, 0x1b, &data, 1); + msleep(5); + + data = 0x43; + comres += p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, 0x07, &data, 1); + data = 0x80; + comres += p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, 0x1c, &data, 1); + data = 0x80; + comres += p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, 0x17, &data, 1); + msleep(5); + data = 0x00; + comres += p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, 0x1c, &data, 1); + data = 0x00; + comres += p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, 0x17, &data, 1); + + return comres; +} + +int mc32x0_set_image (void) +{ + int comres; + unsigned char data; + if (p_mc32x0==0) + return E_NULL_PTR; + +#ifdef MCUBE_2G_10BIT_TAP + data = MC32X0_MODE_DEF; + comres += p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, MC32X0_Mode_Feature_REG, &data, 1 ); + + data = 0x00; + comres += p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, MC32X0_Sleep_Count_REG, &data, 1 ); + + data = 0x00; + comres += p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, MC32X0_Sample_Rate_REG, &data, 1 ); + + data = 0x80; + comres += p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, MC32X0_Tap_Detection_Enable_REG, &data, 1 ); + + data = 0x05; + comres += p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, MC32X0_TAP_Dwell_Reject_REG, &data, 1 ); + + data = 0x33; + comres += p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, MC32X0_RANGE_Control_REG, &data, 1 ); + + data = 0x07; + comres += p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, MC32X0_TAP_Threshold_REG, &data, 1 ); + + data = 0x04; + comres += p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, MC32X0_Interrupt_Enable_REG, &data, 1 ); + +#endif + +#ifdef MCUBE_2G_10BIT + data = MC32X0_MODE_DEF; + comres += p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, MC32X0_Mode_Feature_REG, &data, 1 ); + + data = 0x00; + comres += p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, MC32X0_Sleep_Count_REG, &data, 1 ); + + data = 0x00; + comres += p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, MC32X0_Sample_Rate_REG, &data, 1 ); + + data = 0x00; + comres += p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, MC32X0_Tap_Detection_Enable_REG, &data, 1 ); + + data = 0x33; + comres += p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, MC32X0_RANGE_Control_REG, &data, 1 ); + + data = 0x00; + comres += p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, MC32X0_Interrupt_Enable_REG, &data, 1 ); + +#endif + +#ifdef MCUBE_8G_14BIT_TAP + data = MC32X0_MODE_DEF; + comres += p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, MC32X0_Mode_Feature_REG, &data, 1 ); + + data = 0x00; + comres += p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, MC32X0_Sleep_Count_REG, &data, 1 ); + + data = 0x00; + comres += p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, MC32X0_Sample_Rate_REG, &data, 1 ); + + data = 0x80; + comres += p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, MC32X0_Tap_Detection_Enable_REG, &data, 1 ); + + data = 0x05; + comres += p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, MC32X0_TAP_Dwell_Reject_REG, &data, 1 ); + + data = 0x3F; + comres += p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, MC32X0_RANGE_Control_REG, &data, 1 ); + + data = 0x07; + comres += p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, MC32X0_TAP_Threshold_REG, &data, 1 ); + + data = 0x04; + comres += p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, MC32X0_Interrupt_Enable_REG, &data, 1 ); + +#endif + +#ifdef MCUBE_8G_14BIT + data = MC32X0_MODE_DEF; + comres += p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, MC32X0_Mode_Feature_REG, &data, 1 ); + + data = 0x00; + comres += p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, MC32X0_Sleep_Count_REG, &data, 1 ); + + data = 0x00; + comres += p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, MC32X0_Sample_Rate_REG, &data, 1 ); + + data = 0x00; + comres += p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, MC32X0_Tap_Detection_Enable_REG, &data, 1 ); + + data = 0x3F; + comres += p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, MC32X0_RANGE_Control_REG, &data, 1 ); + + data = 0x00; + comres += p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, MC32X0_Interrupt_Enable_REG, &data, 1 ); + +#endif + + + +#ifdef MCUBE_1_5G_8BIT + data = MC32X0_MODE_DEF; + comres += p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, MC32X0_Mode_Feature_REG, &data, 1 ); + + data = 0x00; + comres += p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, MC32X0_Sleep_Count_REG, &data, 1 ); + + data = 0x00; + comres += p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, MC32X0_Sample_Rate_REG, &data, 1 ); + + data = 0x02; + comres += p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, MC32X0_RANGE_Control_REG, &data, 1 ); + + data = 0x00; + comres += p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, MC32X0_Tap_Detection_Enable_REG, &data, 1 ); + + data = 0x00; + comres += p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, MC32X0_Interrupt_Enable_REG, &data, 1 ); + +#endif + + +#ifdef MCUBE_1_5G_8BIT_TAP + data = MC32X0_MODE_DEF; + comres += p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, MC32X0_Mode_Feature_REG, &data, 1 ); + + data = 0x00; + comres += p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, MC32X0_Sleep_Count_REG, &data, 1 ); + + data = 0x00; + comres += p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, MC32X0_Sample_Rate_REG, &data, 1 ); + + data = 0x80; + comres += p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, MC32X0_Tap_Detection_Enable_REG, &data, 1 ); + + data = 0x02; + comres += p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, MC32X0_RANGE_Control_REG, &data, 1 ); + + data = 0x03; + comres += p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, MC32X0_TAP_Dwell_Reject_REG, &data, 1 ); + + data = 0x07; + comres += p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, MC32X0_TAP_Threshold_REG, &data, 1 ); + + data = 0x04; + comres += p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, MC32X0_Interrupt_Enable_REG, &data, 1 ); + +#endif + + +#ifdef MCUBE_1_5G_6BIT + + data = MC32X0_MODE_DEF; + comres += p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, MC32X0_Mode_Feature_REG, &data, 1 ); + + data = 0x00; + comres += p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, MC32X0_Sleep_Count_REG, &data, 1 ); + + data = 0x00; + comres += p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, MC32X0_Sample_Rate_REG, &data, 1 ); + + data = 0x00; + comres += p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, MC32X0_RANGE_Control_REG, &data, 1 ); + + data = 0x00; + comres += p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, MC32X0_Tap_Detection_Enable_REG, &data, 1 ); + + data = 0x00; + comres += p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, MC32X0_Interrupt_Enable_REG, &data, 1 ); + + +#endif + + + + return comres; +} + + +int mc32x0_get_offset(unsigned char *offset) +{ + return 0; +} + + +int mc32x0_set_offset(unsigned char offset) +{ + return 0; +} + + + + +/** set mc32x0s range + \param range + + \see MC32X0_RANGE_2G + \see MC32X0_RANGE_4G + \see MC32X0_RANGE_8G +*/ +int mc32x0_set_range(char range) +{ + int comres = 0; + unsigned char data; + + if (p_mc32x0==0) + return E_NULL_PTR; + + if (range<3) { + comres = p_mc32x0->MC32X0_BUS_READ_FUNC(p_mc32x0->dev_addr, MC32X0_RANGE__REG, &data, 1); + data = MC32X0_SET_BITSLICE(data, MC32X0_RANGE, range); + comres += p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, MC32X0_RANGE__REG, &data, 1); + + } + return comres; + +} + + +/* readout select range from MC32X0 + \param *range pointer to range setting + \return result of bus communication function + \see MC32X0_RANGE_2G, MC32X0_RANGE_4G, MC32X0_RANGE_8G + \see mc32x0_set_range() +*/ +int mc32x0_get_range(unsigned char *range) +{ + + int comres = 0; + unsigned char data; + + if (p_mc32x0==0) + return E_NULL_PTR; + comres = p_mc32x0->MC32X0_BUS_READ_FUNC(p_mc32x0->dev_addr, MC32X0_RANGE__REG, &data, 1); + data = MC32X0_GET_BITSLICE(data, MC32X0_RANGE); + + *range = data; + + + return comres; + +} + + + +int mc32x0_set_mode(unsigned char mode) { + + int comres=0; + unsigned char data; + + if (p_mc32x0==0) + return E_NULL_PTR; + + if (mode<4) { + data = MC32X0_MODE_DEF; + data = MC32X0_SET_BITSLICE(data, MC32X0_MODE, mode); + comres += p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, MC32X0_MODE__REG, &data, 1 ); + + p_mc32x0->mode = mode; + } + return comres; + +} + + + +int mc32x0_get_mode(unsigned char *mode) +{ + if (p_mc32x0==0) + return E_NULL_PTR; + *mode = p_mc32x0->mode; + return 0; +} + + +int mc32x0_set_bandwidth(char bw) +{ + int comres = 0; + unsigned char data; + + + if (p_mc32x0==0) + return E_NULL_PTR; + + if (bw<7) { + + comres = p_mc32x0->MC32X0_BUS_READ_FUNC(p_mc32x0->dev_addr, MC32X0_BANDWIDTH__REG, &data, 1 ); + data = MC32X0_SET_BITSLICE(data, MC32X0_BANDWIDTH, bw); + comres += p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, MC32X0_BANDWIDTH__REG, &data, 1 ); + + } + + return comres; + + +} + +int mc32x0_get_bandwidth(unsigned char *bw) { + int comres = 1; + if (p_mc32x0==0) + return E_NULL_PTR; + + comres = p_mc32x0->MC32X0_BUS_READ_FUNC(p_mc32x0->dev_addr, MC32X0_BANDWIDTH__REG, bw, 1 ); + + *bw = MC32X0_GET_BITSLICE(*bw, MC32X0_BANDWIDTH); + + return comres; + +} + + +int mc32x0_read_accel_x(short *a_x) +{ + int comres; + unsigned char data[2]; + + + if (p_mc32x0==0) + return E_NULL_PTR; + + comres = p_mc32x0->MC32X0_BUS_READ_FUNC(p_mc32x0->dev_addr, MC32X0_XOUT_EX_L_REG, &data[0],2); + + *a_x = ((short)data[0])|(((short)data[1])<<8); + + return comres; + +} + + + +int mc32x0_read_accel_y(short *a_y) +{ + int comres; + unsigned char data[2]; + + + if (p_mc32x0==0) + return E_NULL_PTR; + + comres = p_mc32x0->MC32X0_BUS_READ_FUNC(p_mc32x0->dev_addr, MC32X0_YOUT_EX_L_REG, &data[0],2); + + *a_y = ((short)data[0])|(((short)data[1])<<8); + + return comres; +} + + +int mc32x0_read_accel_z(short *a_z) +{ + int comres; + unsigned char data[2]; + + if (p_mc32x0==0) + return E_NULL_PTR; + + comres = p_mc32x0->MC32X0_BUS_READ_FUNC(p_mc32x0->dev_addr, MC32X0_ZOUT_EX_L_REG, &data[0],2); + + *a_z = ((short)data[0])|(((short)data[1])<<8); + + return comres; +} + + +int mc32x0_read_accel_xyz(mc32x0acc_t * acc) +{ + int comres; + unsigned char data[6]; + + + if (p_mc32x0==0) + return E_NULL_PTR; + +#ifdef MC32X0_HIGH_END + comres = p_mc32x0->MC32X0_BUS_READ_FUNC(p_mc32x0->dev_addr, MC32X0_XOUT_EX_L_REG, &data[0],6); + + acc->x = ((signed short)data[0])|(((signed short)data[1])<<8); + acc->y = ((signed short)data[2])|(((signed short)data[3])<<8); + acc->z = ((signed short)data[4])|(((signed short)data[5])<<8); +#endif + +#ifdef MC32X0_LOW_END + comres = p_mc32x0->MC32X0_BUS_READ_FUNC(p_mc32x0->dev_addr, MC32X0_XOUT_REG, &data[0],3); + +#ifndef MCUBE_1_5G_6BIT + acc->x = (signed char)data[0]; + acc->y = (signed char)data[1]; + acc->z = (signed char)data[2]; +#else + acc->x = (signed short)GET_REAL_VALUE(data[0],6); + acc->y = (signed short)GET_REAL_VALUE(data[1],6); + acc->z = (signed short)GET_REAL_VALUE(data[2],6); +#endif + +#endif + + + + + return comres; + +} + + + +int mc32x0_get_interrupt_status(unsigned char * ist) +{ + + int comres=0; + if (p_mc32x0==0) + return E_NULL_PTR; + comres = p_mc32x0->MC32X0_BUS_READ_FUNC(p_mc32x0->dev_addr, MC32X0_Tilt_Status_REG, ist, 1); + return comres; +} + + + +int mc32x0_read_reg(unsigned char addr, unsigned char *data, unsigned char len) +{ + + int comres; + if (p_mc32x0==0) + return E_NULL_PTR; + + comres = p_mc32x0->MC32X0_BUS_READ_FUNC(p_mc32x0->dev_addr, addr, data, len); + return comres; + +} + + +int mc32x0_write_reg(unsigned char addr, unsigned char *data, unsigned char len) +{ + + int comres; + + if (p_mc32x0==0) + return E_NULL_PTR; + + comres = p_mc32x0->MC32X0_BUS_WRITE_FUNC(p_mc32x0->dev_addr, addr, data, len); + + return comres; + +} + diff --git a/drivers/input/sensor/mc3230_gsensor/mc32x0_driver.h b/drivers/input/sensor/mc3230_gsensor/mc32x0_driver.h new file mode 100755 index 00000000..0aca80e8 --- /dev/null +++ b/drivers/input/sensor/mc3230_gsensor/mc32x0_driver.h @@ -0,0 +1,219 @@ +/* + * Copyright (C) 2011 MCUBE, Inc. + * + * Initial Code: + * Tan Liang + */ + + + +#ifndef __MC32X0_H__ +#define __MC32X0_H__ + + + +#define MC32X0_WR_FUNC_PTR char (* bus_write)(unsigned char, unsigned char *, unsigned char) + +#define MC32X0_BUS_WRITE_FUNC(dev_addr, reg_addr, reg_data, wr_len)\ + bus_write(reg_addr, reg_data, wr_len) + +#define MC32X0_RD_FUNC_PTR char (* bus_read)( unsigned char, unsigned char *, unsigned char) + +#define MC32X0_BUS_READ_FUNC(dev_addr, reg_addr, reg_data, r_len)\ + bus_read(reg_addr, reg_data, r_len) + +#define GET_REAL_VALUE(rv, bn) \ + ((rv & (0x01 << (bn - 1))) ? (- (rv & ~(0xffff << (bn - 1)))) : (rv & ~(0xffff << (bn - 1)))) + + + +//#define MC32X0_HIGH_END +/*******MC3210/20 define this**********/ + +//#define MCUBE_2G_10BIT_TAP +//#define MCUBE_2G_10BIT +//#define MCUBE_8G_14BIT_TAP +//#define MCUBE_8G_14BIT + + + +//#define MC32X0_LOW_END +/*******MC3230 define this**********/ + +//#define MCUBE_1_5G_8BIT +//#define MCUBE_1_5G_8BIT_TAP + + + + + +/** MC32X0 I2C Address +*/ + +#define MC32X0_I2C_ADDR 0x4c // 0x98 >> 1 + + + +/* + MC32X0 API error codes +*/ + +#define E_NULL_PTR (char)-127 + +/* + * + * register definitions + * + */ + +#define MC32X0_XOUT_REG 0x00 +#define MC32X0_YOUT_REG 0x01 +#define MC32X0_ZOUT_REG 0x02 +#define MC32X0_Tilt_Status_REG 0x03 +#define MC32X0_Sampling_Rate_Status_REG 0x04 +#define MC32X0_Sleep_Count_REG 0x05 +#define MC32X0_Interrupt_Enable_REG 0x06 +#define MC32X0_Mode_Feature_REG 0x07 +#define MC32X0_Sample_Rate_REG 0x08 +#define MC32X0_Tap_Detection_Enable_REG 0x09 +#define MC32X0_TAP_Dwell_Reject_REG 0x0a +#define MC32X0_DROP_Control_Register_REG 0x0b +#define MC32X0_SHAKE_Debounce_REG 0x0c +#define MC32X0_XOUT_EX_L_REG 0x0d +#define MC32X0_XOUT_EX_H_REG 0x0e +#define MC32X0_YOUT_EX_L_REG 0x0f +#define MC32X0_YOUT_EX_H_REG 0x10 +#define MC32X0_ZOUT_EX_L_REG 0x11 +#define MC32X0_ZOUT_EX_H_REG 0x12 +#define MC32X0_CHIP_ID 0x18 +#define MC32X0_RANGE_Control_REG 0x20 +#define MC32X0_SHAKE_Threshold_REG 0x2B +#define MC32X0_UD_Z_TH_REG 0x2C +#define MC32X0_UD_X_TH_REG 0x2D +#define MC32X0_RL_Z_TH_REG 0x2E +#define MC32X0_RL_Y_TH_REG 0x2F +#define MC32X0_FB_Z_TH_REG 0x30 +#define MC32X0_DROP_Threshold_REG 0x31 +#define MC32X0_TAP_Threshold_REG 0x32 + + + + +/** MC32X0 acceleration data + \brief Structure containing acceleration values for x,y and z-axis in signed short + +*/ + +typedef struct { + short x, /**< holds x-axis acceleration data sign extended. Range -512 to 511. */ + y, /**< holds y-axis acceleration data sign extended. Range -512 to 511. */ + z; /**< holds z-axis acceleration data sign extended. Range -512 to 511. */ +} mc32x0acc_t; + +/* RANGE */ + +#define MC32X0_RANGE__POS 2 +#define MC32X0_RANGE__LEN 2 +#define MC32X0_RANGE__MSK 0x0c +#define MC32X0_RANGE__REG MC32X0_RANGE_Control_REG + +/* MODE */ + +#define MC32X0_MODE__POS 0 +#define MC32X0_MODE__LEN 2 +#define MC32X0_MODE__MSK 0x03 +#define MC32X0_MODE__REG MC32X0_Mode_Feature_REG + +#define MC32X0_MODE_DEF 0x43 + + +/* BANDWIDTH */ + +#define MC32X0_BANDWIDTH__POS 4 +#define MC32X0_BANDWIDTH__LEN 3 +#define MC32X0_BANDWIDTH__MSK 0x70 +#define MC32X0_BANDWIDTH__REG MC32X0_RANGE_Control_REG + + +#define MC32X0_GET_BITSLICE(regvar, bitname)\ + (regvar & bitname##__MSK) >> bitname##__POS + + +#define MC32X0_SET_BITSLICE(regvar, bitname, val)\ + (regvar & ~bitname##__MSK) | ((val< +#include +#include +#include +#include +#include +#include +#include +//#include +#include +#include +#include +#include + + +#include +#include + +#include "../sensor.h" + + +//#include + +//=== CONFIGURATIONS ========================================================== +//#define _MC3XXX_DEBUG_ON_ + +//============================================================================= +#ifdef _MC3XXX_DEBUG_ON_ + #define mcprintkreg(x...) printk(x) + #define mcprintkfunc(x...) printk(x) + #define GSE_ERR(x...) printk(x) + #define GSE_LOG(x...) printk(x) +#else + #define mcprintkreg(x...) + #define mcprintkfunc(x...) + #define GSE_ERR(x...) + #define GSE_LOG(x...) +#endif + +static int g_virtual_z = 0; +#define G_2_REVERSE_VIRTUAL_Z 0 //!!!!! 1 +#define SUPPORT_VIRTUAL_Z_SENSOR //add 2013-10-23 +#define LOW_RESOLUTION 1 +#define HIGH_RESOLUTION 2 +#define RBM_RESOLUTION 3 +#ifdef SUPPORT_VIRTUAL_Z_SENSOR +#define Low_Pos_Max 127 +#define Low_Neg_Max -128 +#define High_Pos_Max 8191 +#define High_Neg_Max -8192 +#define VIRTUAL_Z 1 +static int Railed = 0; +#else +#define VIRTUAL_Z 0 +#endif + + +static struct class* l_dev_class = NULL; + + +#define SENSOR_NAME "mc3xxx" +#define SENSOR_DRIVER_VERSION "1.0.0" +#define SENSOR_DATA_SIZE 3 + +//static int mc3xxx_pin_hd; +//static char mc3xxx_on_off_str[32]; +#define G_0 ABS_Y +#define G_1 ABS_X +#define G_2 ABS_Z +#define G_0_REVERSE 1 +#define G_1_REVERSE 1 +#define G_2_REVERSE 1 + +//static unsigned char s_bResolution = 0x00; +static unsigned char s_bPCODE = 0x00; +static unsigned short mc3xxx_i2c_auto_probe_addr[] = { 0x4C, 0x6C, 0x4E, 0x6D, 0x6E, 0x6F }; + +//============================================================================= +#define SENSOR_DMARD_IOCTL_BASE 234 +#define IOCTL_SENSOR_SET_DELAY_ACCEL _IO(SENSOR_DMARD_IOCTL_BASE, 100) +#define IOCTL_SENSOR_GET_DELAY_ACCEL _IO(SENSOR_DMARD_IOCTL_BASE, 101) +#define IOCTL_SENSOR_GET_STATE_ACCEL _IO(SENSOR_DMARD_IOCTL_BASE, 102) +#define IOCTL_SENSOR_SET_STATE_ACCEL _IO(SENSOR_DMARD_IOCTL_BASE, 103) +#define IOCTL_SENSOR_GET_DATA_ACCEL _IO(SENSOR_DMARD_IOCTL_BASE, 104) + +#define IOCTL_MSENSOR_SET_DELAY_MAGNE _IO(SENSOR_DMARD_IOCTL_BASE, 200) +#define IOCTL_MSENSOR_GET_DATA_MAGNE _IO(SENSOR_DMARD_IOCTL_BASE, 201) +#define IOCTL_MSENSOR_GET_STATE_MAGNE _IO(SENSOR_DMARD_IOCTL_BASE, 202) +#define IOCTL_MSENSOR_SET_STATE_MAGNE _IO(SENSOR_DMARD_IOCTL_BASE, 203) + +#define IOCTL_SENSOR_GET_NAME _IO(SENSOR_DMARD_IOCTL_BASE, 301) +#define IOCTL_SENSOR_GET_VENDOR _IO(SENSOR_DMARD_IOCTL_BASE, 302) +#define IOCTL_SENSOR_GET_CONVERT_PARA _IO(SENSOR_DMARD_IOCTL_BASE, 401) +//#define SENSOR_CALIBRATION _IOWR(SENSOR_DMARD_IOCTL_BASE, 402, int[SENSOR_DATA_SIZE]) + +//============================================================================= +#define MC3XXX_CONVERT_PARAMETER (1.5f * (9.80665f) / 256.0f) +#define MC3XXX_DISPLAY_NAME SENSOR_NAME +#define MC3XXX_DIPLAY_VENDOR "mCube" + +//============================================================================= +#define MC3XXX_AXIS_X 0 +#define MC3XXX_AXIS_Y 1 +#define MC3XXX_AXIS_Z 2 +#define MC3XXX_AXIS_NUM 3 +#define MC3XXX_DATA_LEN 6 + + +/*********************************************** + *** REGISTER MAP + ***********************************************/ +#define MC3XXX_REG_XOUT 0x00 +#define MC3XXX_REG_YOUT 0x01 +#define MC3XXX_REG_ZOUT 0x02 +#define MC3XXX_REG_TILT_STATUS 0x03 +#define MC3XXX_REG_SAMPLE_RATE_STATUS 0x04 +#define MC3XXX_REG_SLEEP_COUNT 0x05 +#define MC3XXX_REG_INTERRUPT_ENABLE 0x06 +#define MC3XXX_REG_MODE_FEATURE 0x07 +#define MC3XXX_REG_SAMPLE_RATE 0x08 +#define MC3XXX_REG_TAP_DETECTION_ENABLE 0x09 +#define MC3XXX_REG_TAP_DWELL_REJECT 0x0A +#define MC3XXX_REG_DROP_CONTROL 0x0B +#define MC3XXX_REG_SHAKE_DEBOUNCE 0x0C +#define MC3XXX_REG_XOUT_EX_L 0x0D +#define MC3XXX_REG_XOUT_EX_H 0x0E +#define MC3XXX_REG_YOUT_EX_L 0x0F +#define MC3XXX_REG_YOUT_EX_H 0x10 +#define MC3XXX_REG_ZOUT_EX_L 0x11 +#define MC3XXX_REG_ZOUT_EX_H 0x12 +#define MC3XXX_REG_RANGE_CONTROL 0x20 +#define MC3XXX_REG_SHAKE_THRESHOLD 0x2B +#define MC3XXX_REG_UD_Z_TH 0x2C +#define MC3XXX_REG_UD_X_TH 0x2D +#define MC3XXX_REG_RL_Z_TH 0x2E +#define MC3XXX_REG_RL_Y_TH 0x2F +#define MC3XXX_REG_FB_Z_TH 0x30 +#define MC3XXX_REG_DROP_THRESHOLD 0x31 +#define MC3XXX_REG_TAP_THRESHOLD 0x32 +#define MC3XXX_REG_PRODUCT_CODE 0x3B + +/*********************************************** + *** RETURN CODE + ***********************************************/ +#define MC3XXX_RETCODE_SUCCESS (0) +#define MC3XXX_RETCODE_ERROR_I2C (-1) +#define MC3XXX_RETCODE_ERROR_NULL_POINTER (-2) +#define MC3XXX_RETCODE_ERROR_STATUS (-3) +#define MC3XXX_RETCODE_ERROR_SETUP (-4) +#define MC3XXX_RETCODE_ERROR_GET_DATA (-5) +#define MC3XXX_RETCODE_ERROR_IDENTIFICATION (-6) + + +/*********************************************** + *** CONFIGURATION + ***********************************************/ +#define MC3XXX_BUF_SIZE 256 + +#define MCUBE_1_5G_8BIT 0x01 //MC3XXX_LOW_END +#define MCUBE_8G_14BIT 0x02 //MC3XXX_HIGH_END + +#define DOT_CALI + + +#define SENSOR_DURATION_DEFAULT 20 + +#define INPUT_FUZZ 0 +#define INPUT_FLAT 0 + +/*********************************************** + *** PRODUCT ID + ***********************************************/ +#define MC3XXX_PCODE_3210 0x90 +#define MC3XXX_PCODE_3230 0x19 +#define MC3XXX_PCODE_3250 0x88 +#define MC3XXX_PCODE_3410 0xA8 +#define MC3XXX_PCODE_3410N 0xB8 +#define MC3XXX_PCODE_3430 0x29 +#define MC3XXX_PCODE_3430N 0x39 +#define MC3XXX_PCODE_3510B 0x40 +#define MC3XXX_PCODE_3530B 0x30 +#define MC3XXX_PCODE_3510C 0x10 +#define MC3XXX_PCODE_3530C 0x6E + +//============================================================================= +static unsigned char is_new_mc34x0 = 0; +static unsigned char is_mc3250 = 0; +static unsigned char is_mc35xx = 0; +static unsigned char Sensor_Accuracy = 0; + + +//============================================================================= +#ifdef DOT_CALI +#define CALIB_PATH "/data/data/com.mcube.acc/files/mcube-calib.txt" +//MCUBE_BACKUP_FILE +#define BACKUP_CALIB_PATH "/data/misc/mcube-calib.txt" +static char backup_buf[64]; +//MCUBE_BACKUP_FILE +#define DATA_PATH "/sdcard/mcube-register-map.txt" + +typedef struct { + unsigned short x; /**< X axis */ + unsigned short y; /**< Y axis */ + unsigned short z; /**< Z axis */ +} GSENSOR_VECTOR3D; + +static GSENSOR_VECTOR3D gsensor_gain = { 0 }; +static struct miscdevice mc3xxx_device; + +static struct file * fd_file = NULL; + +static mm_segment_t oldfs = { 0 }; +static unsigned char offset_buf[6] = { 0 }; +static signed int offset_data[3] = { 0 }; +s16 G_RAW_DATA[3] = { 0 }; +static signed int gain_data[3] = { 0 }; +static signed int enable_RBM_calibration = 0; + +#define GSENSOR 0x95 +#define GSENSOR_IOCTL_INIT _IO(GSENSOR, 0x01) +#define GSENSOR_IOCTL_READ_CHIPINFO _IOR(GSENSOR, 0x02, int) +#define GSENSOR_IOCTL_READ_SENSORDATA _IOR(GSENSOR, 0x03, int) +#define GSENSOR_IOCTL_READ_OFFSET _IOR(GSENSOR, 0x04, GSENSOR_VECTOR3D) +#define GSENSOR_IOCTL_READ_GAIN _IOR(GSENSOR, 0x05, GSENSOR_VECTOR3D) +#define GSENSOR_IOCTL_READ_RAW_DATA _IOR(GSENSOR, 0x06, int) +//#define GSENSOR_IOCTL_SET_CALI _IOW(GSENSOR, 0x06, SENSOR_DATA) +#define GSENSOR_IOCTL_GET_CALI _IOW(GSENSOR, 0x07, SENSOR_DATA) +#define GSENSOR_IOCTL_CLR_CALI _IO(GSENSOR, 0x08) +#define GSENSOR_MCUBE_IOCTL_READ_RBM_DATA _IOR(GSENSOR, 0x09, SENSOR_DATA) +#define GSENSOR_MCUBE_IOCTL_SET_RBM_MODE _IO(GSENSOR, 0x0a) +#define GSENSOR_MCUBE_IOCTL_CLEAR_RBM_MODE _IO(GSENSOR, 0x0b) +#define GSENSOR_MCUBE_IOCTL_SET_CALI _IOW(GSENSOR, 0x0c, SENSOR_DATA) +#define GSENSOR_MCUBE_IOCTL_REGISTER_MAP _IO(GSENSOR, 0x0d) +#define GSENSOR_IOCTL_SET_CALI_MODE _IOW(GSENSOR, 0x0e,int) +#define GSENSOR_MCUBE_IOCTL_READ_PRODUCT_ID _IOR(GSENSOR, 0x0f, int) +#define GSENSOR_MCUBE_IOCTL_READ_FILEPATH _IOR(GSENSOR, 0x10, char[256]) + + +static int MC3XXX_ReadRegMap(struct i2c_client *client, u8 *pbUserBuf); +static int mc3xxx_chip_init(struct i2c_client *client); +static int MC3XXX_ResetCalibration(struct i2c_client *client); +static int MC3XX0_ValidateSensorIC(unsigned char bPCode); + + +typedef struct{ + int x; + int y; + int z; +}SENSOR_DATA; + +static int load_cali_flg = 0; +static int wake_mc3xxx_flg = 0; + +//MCUBE_BACKUP_FILE +static bool READ_FROM_BACKUP = false; +//MCUBE_BACKUP_FILE + +#endif + +#define MC3XXX_WAKE 1 +#define MC3XXX_SNIFF 2 +#define MC3XXX_STANDBY 3 + +struct dev_data { + struct i2c_client *client; +}; + +static struct dev_data dev = { 0 }; + +struct acceleration { + int x; + int y; + int z; +}; + +struct mc3xxx_data { + struct mutex lock; + struct i2c_client *client; + struct delayed_work work; + struct workqueue_struct *mc3xxx_wq; + struct hrtimer timer; + struct device *device; + struct input_dev *input_dev; + int use_count; + int enabled; + volatile unsigned int duration; + int use_irq; + int irq; + unsigned long irqflags; + int gpio; + unsigned int map[3]; + int inv[3]; +#ifdef CONFIG_HAS_EARLYSUSPEND + struct early_suspend early_suspend; +#endif + // for control + int int_gpio; //0-3 + int op; + int samp; + //int xyz_axis[3][3]; // (axis,direction) + struct proc_dir_entry* sensor_proc; + int isdbg; + int sensor_samp; // + int sensor_enable; // 0 --> disable sensor, 1 --> enable sensor + int test_pass; + int offset[MC3XXX_AXIS_NUM+1]; /*+1: for 4-byte alignment*/ + s16 data[MC3XXX_AXIS_NUM+1]; +}; + +static struct mc3xxx_data l_sensorconfig = { + .op = 0, + .int_gpio = 3, + .samp = 16, + /*.xyz_axis = { + {ABS_X, -1}, + {ABS_Y, 1}, + {ABS_Z, -1}, + },*/ + .sensor_proc = NULL, + .isdbg = 1, + .sensor_samp = 1, // 1 sample/second + .sensor_enable = 1, // enable sensor + .test_pass = 0, // for test program + //.offset={0,0,0}, +}; + + +//============================================================================= +enum mc3xx0_orientation +{ + MC3XX0_TOP_LEFT_DOWN = 0, + MC3XX0_TOP_RIGHT_DOWN, + MC3XX0_TOP_RIGHT_UP, + MC3XX0_TOP_LEFT_UP, + MC3XX0_BOTTOM_LEFT_DOWN, + MC3XX0_BOTTOM_RIGHT_DOWN, + MC3XX0_BOTTOM_RIGHT_UP, + MC3XX0_BOTTOM_LEFT_UP +}; + + +struct mc3xx0_hwmsen_convert +{ + signed int sign[3]; + unsigned int map[3]; +}; + +// Transformation matrix for chip mounting position +static const struct mc3xx0_hwmsen_convert mc3xx0_cvt[] = +{ + {{ 1, 1, 1}, {MC3XXX_AXIS_X, MC3XXX_AXIS_Y, MC3XXX_AXIS_Z}}, // 0: top , left-down + {{-1, 1, 1}, {MC3XXX_AXIS_Y, MC3XXX_AXIS_X, MC3XXX_AXIS_Z}}, // 1: top , right-down + {{-1, -1, 1}, {MC3XXX_AXIS_X, MC3XXX_AXIS_Y, MC3XXX_AXIS_Z}}, // 2: top , right-up + {{ 1, -1, 1}, {MC3XXX_AXIS_Y, MC3XXX_AXIS_X, MC3XXX_AXIS_Z}}, // 3: top , left-up + {{-1, 1, -1}, {MC3XXX_AXIS_X, MC3XXX_AXIS_Y, MC3XXX_AXIS_Z}}, // 4: bottom, left-down + {{ 1, 1, -1}, {MC3XXX_AXIS_Y, MC3XXX_AXIS_X, MC3XXX_AXIS_Z}}, // 5: bottom, right-down + {{ 1, -1, -1}, {MC3XXX_AXIS_X, MC3XXX_AXIS_Y, MC3XXX_AXIS_Z}}, // 6: bottom, right-up + {{-1, -1, -1}, {MC3XXX_AXIS_Y, MC3XXX_AXIS_X, MC3XXX_AXIS_Z}}, // 7: bottom, left-up +}; + +//static unsigned char mc3xx0_current_placement = MC3XX0_TOP_RIGHT_UP; // current soldered placement +static struct mc3xx0_hwmsen_convert *pCvt; + +#ifdef SUPPORT_VIRTUAL_Z_SENSOR //add 2013-10-23 +int Verify_Z_Railed(int AccData, int resolution) +{ + int status = 0; + GSE_LOG("%s: AccData = %d",__func__, AccData); + if(resolution == 1) // Low resolution + { + if((AccData >= Low_Pos_Max && AccData >=0)|| (AccData <= Low_Neg_Max && AccData < 0)) + { + status = 1; + GSE_LOG("%s: Railed at Low Resolution",__func__); + } + } + else if (resolution == 2) //High resolution + { + if((AccData >= High_Pos_Max && AccData >=0) || (AccData <= High_Neg_Max && AccData < 0)) + { + status = 1; + GSE_LOG("%s: Railed at High Resolution",__func__); + } + } + else if (resolution == 3) //High resolution + { + if((AccData >= Low_Pos_Max*3 && AccData >=0) || (AccData <= Low_Neg_Max*3 && AccData < 0)) + { + status = 1; + GSE_LOG("%s: Railed at High Resolution",__func__); + } + } + else + GSE_LOG("%s, Wrong resolution",__func__); + + return status; +} + +int SquareRoot(int x) +{ + int lowerbound; + int upperbound; + int root; + + if(x < 0) return -1; + if(x == 0 || x == 1) return x; + lowerbound = 1; + upperbound = x; + root = lowerbound + (upperbound - lowerbound)/2; + + while(root > x/root || root+1 <= x/(root+1)) + { + if(root > x/root) + { + upperbound = root; + } + else + { + lowerbound = root; + } + root = lowerbound + (upperbound - lowerbound)/2; + } + GSE_LOG("%s: Sqrt root is %d",__func__, root); + return root; +} +#endif + + +unsigned int sample_rate_2_memsec(unsigned int rate) +{ + return (1000/rate); +} + + +//============================================================================= +//volatile static short sensor_duration = SENSOR_DURATION_DEFAULT; +//volatile static short sensor_state_flag = 1; + +//============================================================================= +static ssize_t mc3xxx_map_show(struct device *dev, struct device_attribute *attr,char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct mc3xxx_data *data = NULL; + int i = 0; + data = i2c_get_clientdata(client); + for (i = 0; i< 3; i++) + { + if(data->inv[i] == 1) + { + switch(data->map[i]) + { + case ABS_X: + buf[i] = 'x'; + break; + case ABS_Y: + buf[i] = 'y'; + break; + case ABS_Z: + buf[i] = 'z'; + break; + default: + buf[i] = '_'; + break; + } + } + else + { + switch(data->map[i]) + { + case ABS_X: + buf[i] = 'X'; + break; + case ABS_Y: + buf[i] = 'Y'; + break; + case ABS_Z: + buf[i] = 'Z'; + break; + default: + buf[i] = '-'; + break; + } + } + } + sprintf(buf+3,"\r\n"); + return 5; +} + +/***************************************** + *** show_regiter_map + *****************************************/ +static ssize_t show_regiter_map(struct device *dev, struct device_attribute *attr, char *buf) +{ + u8 _bIndex = 0; + u8 _baRegMap[64] = { 0 }; + ssize_t _tLength = 0; + + struct i2c_client *client = to_i2c_client(dev); + + MC3XXX_ReadRegMap(client, _baRegMap); + + for (_bIndex = 0; _bIndex < 64; _bIndex++) + _tLength += snprintf((buf + _tLength), (PAGE_SIZE - _tLength), "Reg[0x%02X]: 0x%02X\n", _bIndex, _baRegMap[_bIndex]); + + return (_tLength); +} + +static ssize_t mc3xxx_map_store(struct device *dev, struct device_attribute *attr,const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct mc3xxx_data *data = NULL; + int i = 0; + data = i2c_get_clientdata(client); + + if(count < 3) return -EINVAL; + + for(i = 0; i< 3; i++) + { + switch(buf[i]) + { + case 'x': + data->map[i] = ABS_X; + data->inv[i] = 1; + break; + case 'y': + data->map[i] = ABS_Y; + data->inv[i] = 1; + break; + case 'z': + data->map[i] = ABS_Z; + data->inv[i] = 1; + break; + case 'X': + data->map[i] = ABS_X; + data->inv[i] = -1; + break; + case 'Y': + data->map[i] = ABS_Y; + data->inv[i] = -1; + break; + case 'Z': + data->map[i] = ABS_Z; + data->inv[i] = -1; + break; + default: + return -EINVAL; + } + } + + return count; +} + +//============================================================================= +static ssize_t mc3xxx_version_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%s\n", SENSOR_DRIVER_VERSION); +} + +//============================================================================= +static ssize_t mc3xxx_chip_id_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + unsigned char bChipID[4] = { 0 }; + struct i2c_client *client = to_i2c_client(dev); + + i2c_smbus_read_i2c_block_data(client, 0x3C, 4, bChipID); + + return sprintf(buf, "%02X-%02X-%02X-%02X\n", bChipID[0], bChipID[1], bChipID[2], bChipID[3]); +} +/* +//============================================================================= +static ssize_t mc3xxx_position_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + printk("%s called\n", __func__); + return sprintf(buf, "%d\n", mc3xx0_current_placement); +} + +//============================================================================= +static ssize_t mc3xxx_position_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned long position = 0; + + printk("%s called\n", __func__); + + position = simple_strtoul(buf, NULL,10); + + if (position < 8) + mc3xx0_current_placement = position; + + return count; +} +*/ + +static int mc3xxx_enable(struct mc3xxx_data *data, int enable) +{ + if(enable) + { + msleep(10); + //mutex_lock(&data->lock); + mc3xxx_chip_init(data->client); + //mutex_unlock(&data->lock); + queue_delayed_work(data->mc3xxx_wq, &data->work, msecs_to_jiffies(sample_rate_2_memsec(data->sensor_samp)));//hrtimer_start(&data->timer, ktime_set(0, sensor_duration*1000000), HRTIMER_MODE_REL); + data->enabled = true; + } + else + { + cancel_delayed_work_sync(&l_sensorconfig.work);//hrtimer_cancel(&data->timer); + data->enabled = false; + } + return 0; +} + +static ssize_t mc3xxx_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = container_of(mc3xxx_device.parent, struct i2c_client, dev); + + struct mc3xxx_data *mc3xxx = i2c_get_clientdata(client); + + + return sprintf(buf, "%d\n", mc3xxx->enabled); +} + +static ssize_t mc3xxx_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + + bool new_enable; + + struct i2c_client *client = container_of(mc3xxx_device.parent, struct i2c_client, dev); + + struct mc3xxx_data *mc3xxx = i2c_get_clientdata(client); + + if (sysfs_streq(buf, "1")) + new_enable = true; + else if (sysfs_streq(buf, "0")) + new_enable = false; + else + { + pr_debug("%s: invalid value %d\n", __func__, *buf); + return -EINVAL; + } + + mc3xxx_enable(mc3xxx, new_enable); + + return count; +} + + +static DRIVER_ATTR(regmap , S_IRUGO, show_regiter_map, NULL ); +static DEVICE_ATTR(map, S_IWUSR | S_IRUGO, mc3xxx_map_show, mc3xxx_map_store); +static DEVICE_ATTR(version , S_IRUGO , mc3xxx_version_show , NULL ); +static DEVICE_ATTR(chipid , S_IRUGO , mc3xxx_chip_id_show , NULL ); +//static DEVICE_ATTR(position, S_IRUGO | S_IWUSR | S_IWGRP, mc3xxx_position_show, mc3xxx_position_store); +static DEVICE_ATTR(enable, S_IRUGO|S_IWUSR|S_IWGRP|S_IWOTH, mc3xxx_enable_show, mc3xxx_enable_store); +static struct attribute* mc3xxx_attrs[] = +{ + &driver_attr_regmap, + &dev_attr_map.attr, + &dev_attr_version.attr, + &dev_attr_chipid.attr, + //&dev_attr_position.attr, + &dev_attr_enable.attr, + NULL +}; + +static const struct attribute_group mc3xxx_group = +{ + .attrs = mc3xxx_attrs, +}; + +//============================================================================= +static int mc3xxx_chip_init(struct i2c_client *client) +{ + unsigned char data = 0; + + data = i2c_smbus_read_byte_data(client, MC3XXX_REG_PRODUCT_CODE); + s_bPCODE =data; + if((data == MC3XXX_PCODE_3230)||(data == MC3XXX_PCODE_3430) + ||(data == MC3XXX_PCODE_3430N)||(data == MC3XXX_PCODE_3530B) + ||((data|0x0E) == MC3XXX_PCODE_3530C)) + Sensor_Accuracy = MCUBE_1_5G_8BIT; //8bit + else if((data == MC3XXX_PCODE_3210)||(data == MC3XXX_PCODE_3410) + ||(data == MC3XXX_PCODE_3250)||(data == MC3XXX_PCODE_3410N) + ||(data == MC3XXX_PCODE_3510B)||(data == MC3XXX_PCODE_3510C)) + Sensor_Accuracy = MCUBE_8G_14BIT; //14bit + else + Sensor_Accuracy = 0; + + if (data == MC3XXX_PCODE_3250) + is_mc3250 = 1; + + if ((data == MC3XXX_PCODE_3430N)||(data == MC3XXX_PCODE_3410N)) + is_new_mc34x0 = 1; + + if((MC3XXX_PCODE_3510B == data) || (MC3XXX_PCODE_3510C == data) + ||(data == MC3XXX_PCODE_3530B)||((data|0x0E) == MC3XXX_PCODE_3530C)) + is_mc35xx = 1; + + + if(MCUBE_8G_14BIT == Sensor_Accuracy) + { + data = 0x43; + i2c_smbus_write_byte_data(client, MC3XXX_REG_MODE_FEATURE, data); + data = 0x00; + i2c_smbus_write_byte_data(client, MC3XXX_REG_SLEEP_COUNT, data); + + data = 0x00; + if (is_mc35xx) + { + data = 0x0A; + } + + i2c_smbus_write_byte_data(client, MC3XXX_REG_SAMPLE_RATE, data); + + data = 0x3F; + if ((MC3XXX_PCODE_3510B == s_bPCODE) || (MC3XXX_PCODE_3510C == s_bPCODE)) + data = 0x25; + else if ((MC3XXX_PCODE_3530B == s_bPCODE) || (MC3XXX_PCODE_3530C == (s_bPCODE|0x0E))) + data = 0x02; + + i2c_smbus_write_byte_data(client, MC3XXX_REG_RANGE_CONTROL, data); + data = 0x00; + i2c_smbus_write_byte_data(client, MC3XXX_REG_TAP_DETECTION_ENABLE, data); + data = 0x00; + i2c_smbus_write_byte_data(client, MC3XXX_REG_INTERRUPT_ENABLE, data); + + #ifdef DOT_CALI + gsensor_gain.x = gsensor_gain.y = gsensor_gain.z = 1024; + #endif + } + else if(MCUBE_1_5G_8BIT == Sensor_Accuracy) + { + data = 0x43; + i2c_smbus_write_byte_data(client, MC3XXX_REG_MODE_FEATURE, data); + data = 0x00; + i2c_smbus_write_byte_data(client, MC3XXX_REG_SLEEP_COUNT, data); + + data = 0x00; + if (is_mc35xx) + { + data = 0x0A; + } + + i2c_smbus_write_byte_data(client, MC3XXX_REG_SAMPLE_RATE, data); + + data = 0x32; + if ((MC3XXX_PCODE_3510B == s_bPCODE) || (MC3XXX_PCODE_3510C == s_bPCODE)) + data = 0x25; + else if ((MC3XXX_PCODE_3530B == s_bPCODE) || (MC3XXX_PCODE_3530C == (s_bPCODE|0x0E))) + data = 0x02; + + i2c_smbus_write_byte_data(client, MC3XXX_REG_RANGE_CONTROL,data); + data = 0x00; + i2c_smbus_write_byte_data(client, MC3XXX_REG_TAP_DETECTION_ENABLE, data); + data = 0x00; + i2c_smbus_write_byte_data(client, MC3XXX_REG_INTERRUPT_ENABLE, data); + + #ifdef DOT_CALI + gsensor_gain.x = gsensor_gain.y = gsensor_gain.z = 86; + if (is_mc35xx) + { + gsensor_gain.x = gsensor_gain.y = gsensor_gain.z = 64; + } + #endif + } + + data = 0x41; + i2c_smbus_write_byte_data(client, MC3XXX_REG_MODE_FEATURE, data); + + return 0; +} + +//============================================================================= +int mc3xxx_set_mode(struct i2c_client *client, unsigned char mode) +{ + int comres = 0; + unsigned char data = 0; + + if (mode < 4) + { + data = (0x40 | mode); + comres = i2c_smbus_write_byte_data(client, MC3XXX_REG_MODE_FEATURE, data); + } + + return comres; +} + + + +#ifdef DOT_CALI +//============================================================================= +struct file *openFile(char *path,int flag,int mode) +{ + struct file *fp = NULL; + + fp = filp_open(path, flag, mode); + + if (IS_ERR(fp) || !fp->f_op) + { + GSE_LOG("Calibration File filp_open return NULL\n"); + return NULL; + } + + return fp; +} + +//============================================================================= +int readFile(struct file *fp,char *buf,int readlen) +{ + if (fp->f_op && fp->f_op->read) + return fp->f_op->read(fp,buf,readlen, &fp->f_pos); + else + return -1; +} + +//============================================================================= +int writeFile(struct file *fp,char *buf,int writelen) +{ + if (fp->f_op && fp->f_op->write) + return fp->f_op->write(fp,buf,writelen, &fp->f_pos); + else + return -1; +} + +//============================================================================= +int closeFile(struct file *fp) +{ + filp_close(fp,NULL); + + return 0; +} + +//============================================================================= +void initKernelEnv(void) +{ + oldfs = get_fs(); + set_fs(KERNEL_DS); + printk(KERN_INFO "initKernelEnv\n"); +} + +//============================================================================= + int MC3XXX_WriteCalibration(struct i2c_client *client, int dat[MC3XXX_AXIS_NUM]) +{ + int err = 0; + u8 buf[9] = { 0 }; + s16 tmp = 0, x_gain = 0, y_gain = 0, z_gain = 0; + s32 x_off = 0, y_off = 0, z_off = 0; + int temp_cali_dat[MC3XXX_AXIS_NUM] = { 0 }; + //const struct mc3xx0_hwmsen_convert *pCvt = NULL; + + u8 bMsbFilter = 0x3F; + s16 wSignBitMask = 0x2000; + s16 wSignPaddingBits = 0xC000; + s32 dwRangePosLimit = 0x1FFF; + s32 dwRangeNegLimit = -0x2000; + + if (is_mc35xx) + { + bMsbFilter = 0x7F; + wSignBitMask = 0x4000; + wSignPaddingBits = 0x8000; + dwRangePosLimit = 0x3FFF; + dwRangeNegLimit = -0x4000; + } + + //pCvt = &mc3xx0_cvt[mc3xx0_current_placement]; + + temp_cali_dat[pCvt->map[MC3XXX_AXIS_X]] = pCvt->sign[MC3XXX_AXIS_X] * dat[MC3XXX_AXIS_X]; + temp_cali_dat[pCvt->map[MC3XXX_AXIS_Y]] = pCvt->sign[MC3XXX_AXIS_Y] * dat[MC3XXX_AXIS_Y]; + temp_cali_dat[pCvt->map[MC3XXX_AXIS_Z]] = pCvt->sign[MC3XXX_AXIS_Z] * dat[MC3XXX_AXIS_Z]; + + if ((is_new_mc34x0)||(is_mc35xx)) + { + temp_cali_dat[MC3XXX_AXIS_X] = -temp_cali_dat[MC3XXX_AXIS_X]; + temp_cali_dat[MC3XXX_AXIS_Y] = -temp_cali_dat[MC3XXX_AXIS_Y]; + } + else if (is_mc3250) + { + s16 temp = 0; + + temp = temp_cali_dat[MC3XXX_AXIS_X]; + + temp_cali_dat[MC3XXX_AXIS_X] = -temp_cali_dat[MC3XXX_AXIS_Y]; + temp_cali_dat[MC3XXX_AXIS_Y] = temp; + } + + dat[MC3XXX_AXIS_X] = temp_cali_dat[MC3XXX_AXIS_X]; + dat[MC3XXX_AXIS_Y] = temp_cali_dat[MC3XXX_AXIS_Y]; + dat[MC3XXX_AXIS_Z] = temp_cali_dat[MC3XXX_AXIS_Z]; + + GSE_LOG("UPDATE dat: (%+3d %+3d %+3d)\n", + dat[MC3XXX_AXIS_X], dat[MC3XXX_AXIS_Y], dat[MC3XXX_AXIS_Z]); + + // read register 0x21~0x29 + err = i2c_smbus_read_i2c_block_data(client , 0x21 , 3 , &buf[0]); + err |= i2c_smbus_read_i2c_block_data(client , 0x24 , 3 , &buf[3]); + err |= i2c_smbus_read_i2c_block_data(client , 0x27 , 3 , &buf[6]); + + + // get x,y,z offset + tmp = ((buf[1] & bMsbFilter) << 8) + buf[0]; + if (tmp & wSignBitMask) + tmp |= wSignPaddingBits; + x_off = tmp; + + tmp = ((buf[3] & bMsbFilter) << 8) + buf[2]; + if (tmp & wSignBitMask) + tmp |= wSignPaddingBits; + y_off = tmp; + + tmp = ((buf[5] & bMsbFilter) << 8) + buf[4]; + if (tmp & wSignBitMask) + tmp |= wSignPaddingBits; + z_off = tmp; + + // get x,y,z gain + x_gain = ((buf[1] >> 7) << 8) + buf[6]; + y_gain = ((buf[3] >> 7) << 8) + buf[7]; + z_gain = ((buf[5] >> 7) << 8) + buf[8]; + + // prepare new offset + x_off = x_off + 16 * dat[MC3XXX_AXIS_X] * 256 * 128 / 3 / gsensor_gain.x / (40 + x_gain); + y_off = y_off + 16 * dat[MC3XXX_AXIS_Y] * 256 * 128 / 3 / gsensor_gain.y / (40 + y_gain); + z_off = z_off + 16 * dat[MC3XXX_AXIS_Z] * 256 * 128 / 3 / gsensor_gain.z / (40 + z_gain); + + //add for over range + if( x_off > dwRangePosLimit) + { + x_off = dwRangePosLimit; + } + else if( x_off < dwRangeNegLimit) + { + x_off = dwRangeNegLimit; + } + + if( y_off > dwRangePosLimit) + { + y_off = dwRangePosLimit; + } + else if( y_off < dwRangeNegLimit) + { + y_off = dwRangeNegLimit; + } + + if( z_off > dwRangePosLimit) + { + z_off = dwRangePosLimit; + } + else if( z_off < dwRangeNegLimit) + { + z_off = dwRangeNegLimit; + } + + //storege the cerrunt offset data with DOT format + offset_data[0] = x_off; + offset_data[1] = y_off; + offset_data[2] = z_off; + + //storege the cerrunt Gain data with GOT format + gain_data[0] = 256*8*128/3/(40+x_gain); + gain_data[1] = 256*8*128/3/(40+y_gain); + gain_data[2] = 256*8*128/3/(40+z_gain); + printk("%d %d ======================\n\n ",gain_data[0],x_gain); + + buf[0] = 0x43; + i2c_smbus_write_byte_data(client, 0x07, buf[0]); + + buf[0] = x_off & 0xff; + buf[1] = ((x_off >> 8) & bMsbFilter) | (x_gain & 0x0100 ? 0x80 : 0); + buf[2] = y_off & 0xff; + buf[3] = ((y_off >> 8) & bMsbFilter) | (y_gain & 0x0100 ? 0x80 : 0); + buf[4] = z_off & 0xff; + buf[5] = ((z_off >> 8) & bMsbFilter) | (z_gain & 0x0100 ? 0x80 : 0); + + i2c_smbus_write_i2c_block_data(client, 0x21, 2, &buf[0]); + i2c_smbus_write_i2c_block_data(client, 0x21+2, 2, &buf[2]); + i2c_smbus_write_i2c_block_data(client, 0x21+4, 2, &buf[4]); + + buf[0] = 0x41; + i2c_smbus_write_byte_data(client, 0x07,buf[0]); + + msleep(50); + + return err; + +} + +int mcube_read_cali_file(struct i2c_client *client) +{ + int cali_data[3] = { 0 }; + int err =0; + //char buf[64]; + printk("%s %d\n",__func__,__LINE__); + //MCUBE_BACKUP_FILE + READ_FROM_BACKUP = false; + //MCUBE_BACKUP_FILE + initKernelEnv(); + fd_file = openFile(CALIB_PATH,O_RDONLY,0); + //MCUBE_BACKUP_FILE + if (fd_file == NULL) + { + fd_file = openFile(BACKUP_CALIB_PATH, O_RDONLY, 0); + if(fd_file != NULL) + { + READ_FROM_BACKUP = true; + } + } + //MCUBE_BACKUP_FILE + if (fd_file == NULL) + { + GSE_LOG("fail to open\n"); + cali_data[0] = 0; + cali_data[1] = 0; + cali_data[2] = 0; + + return -1; + } + else + { + printk("%s %d\n",__func__,__LINE__); + memset(backup_buf,0,64); + if ((err = readFile(fd_file,backup_buf,128))>0) + GSE_LOG("buf:%s\n",backup_buf); + else + GSE_LOG("read file error %d\n",err); + printk("%s %d\n",__func__,__LINE__); + + set_fs(oldfs); + closeFile(fd_file); + + sscanf(backup_buf, "%d %d %d",&cali_data[MC3XXX_AXIS_X], &cali_data[MC3XXX_AXIS_Y], &cali_data[MC3XXX_AXIS_Z]); + GSE_LOG("cali_data: %d %d %d\n", cali_data[MC3XXX_AXIS_X], cali_data[MC3XXX_AXIS_Y], cali_data[MC3XXX_AXIS_Z]); + + MC3XXX_WriteCalibration(client, cali_data); + } + return 0; +} + +//============================================================================= +static int mcube_write_log_data(struct i2c_client *client, u8 data[0x3f]) +{ + #define _WRT_LOG_DATA_BUFFER_SIZE (66 * 50) + + s16 rbm_data[3]={0}, raw_data[3]={0}; + int err =0; + char *_pszBuffer = NULL; + int n=0,i=0; + + initKernelEnv(); + fd_file = openFile(DATA_PATH ,O_RDWR | O_CREAT,0); + if (fd_file == NULL) + { + GSE_LOG("mcube_write_log_data fail to open\n"); + } + else + { + rbm_data[MC3XXX_AXIS_X] = (s16)((data[0x0d]) | (data[0x0e] << 8)); + rbm_data[MC3XXX_AXIS_Y] = (s16)((data[0x0f]) | (data[0x10] << 8)); + rbm_data[MC3XXX_AXIS_Z] = (s16)((data[0x11]) | (data[0x12] << 8)); + + raw_data[MC3XXX_AXIS_X] = (rbm_data[MC3XXX_AXIS_X] + offset_data[0]/2)*gsensor_gain.x/gain_data[0]; + raw_data[MC3XXX_AXIS_Y] = (rbm_data[MC3XXX_AXIS_Y] + offset_data[1]/2)*gsensor_gain.y/gain_data[1]; + raw_data[MC3XXX_AXIS_Z] = (rbm_data[MC3XXX_AXIS_Z] + offset_data[2]/2)*gsensor_gain.z/gain_data[2]; + + _pszBuffer = kzalloc(_WRT_LOG_DATA_BUFFER_SIZE, GFP_KERNEL); + if (NULL == _pszBuffer) + { + GSE_ERR("fail to allocate memory for buffer\n"); + closeFile(fd_file); + return -1; + } + memset(_pszBuffer, 0, _WRT_LOG_DATA_BUFFER_SIZE); + + n += sprintf(_pszBuffer+n, "G-sensor RAW X = %d Y = %d Z = %d\n", raw_data[0] ,raw_data[1] ,raw_data[2]); + n += sprintf(_pszBuffer+n, "G-sensor RBM X = %d Y = %d Z = %d\n", rbm_data[0] ,rbm_data[1] ,rbm_data[2]); + for(i=0; i<64; i++) + { + n += sprintf(_pszBuffer+n, "mCube register map Register[%x] = 0x%x\n",i,data[i]); + } + msleep(50); + if ((err = writeFile(fd_file,_pszBuffer,n))>0) + GSE_LOG("buf:%s\n",_pszBuffer); + else + GSE_LOG("write file error %d\n",err); + + kfree(_pszBuffer); + + set_fs(oldfs); + closeFile(fd_file); + } + return 0; +} + +//============================================================================= +void MC3XXX_rbm(struct i2c_client *client, int enable) +{ + char buf1[3] = { 0 }; + if(enable == 1 ) + { + buf1[0] = 0x43; + i2c_smbus_write_byte_data(client, 0x07, buf1[0]); + + buf1[0] = 0x6D; + i2c_smbus_write_byte_data(client, 0x1B, buf1[0]); + + buf1[0] = 0x43; + i2c_smbus_write_byte_data(client, 0x1B, buf1[0]); + + buf1[0] = 0x00; + i2c_smbus_write_byte_data(client, 0x3B, buf1[0]); + + buf1[0] = 0x02; + i2c_smbus_write_byte_data(client, 0x14, buf1[0]); + + buf1[0] = 0x41; + i2c_smbus_write_byte_data(client, 0x07, buf1[0]); + + enable_RBM_calibration = 1; + + GSE_LOG("set rbm!!\n"); + + msleep(10); + } + else if(enable == 0 ) + { + buf1[0] = 0x43; + i2c_smbus_write_byte_data(client, 0x07, buf1[0]); + + buf1[0] = 0x00; + i2c_smbus_write_byte_data(client, 0x14, buf1[0]); + GSE_LOG("set rbm!! %x @@@@\n",s_bPCODE); + + buf1[0] = s_bPCODE; + i2c_smbus_write_byte_data(client, 0x3B, buf1[0]); + + buf1[0] = 0x6D; + i2c_smbus_write_byte_data(client, 0x1B, buf1[0]); + + buf1[0] = 0x43; + i2c_smbus_write_byte_data(client, 0x1B, buf1[0]); + + buf1[0] = 0x41; + i2c_smbus_write_byte_data(client, 0x07, buf1[0]); + + enable_RBM_calibration = 0; + + GSE_LOG("clear rbm!!\n"); + + msleep(10); + } +} + +/*----------------------------------------------------------------------------*/ + int MC3XXX_ReadData_RBM(struct i2c_client *client,int data[MC3XXX_AXIS_NUM]) +{ + u8 addr = 0x0d; + u8 rbm_buf[MC3XXX_DATA_LEN] = {0}; + int err = 0; + + //err = p_mc3xxx->MC3XXX_BUS_READ_FUNC(p_mc3xxx->dev_addr, addr, &rbm_buf[0],6); + err = i2c_smbus_read_i2c_block_data(client , addr , 6 , rbm_buf); + //err = mc3xxx_read_block(client, addr, rbm_buf, 0x06); + + data[MC3XXX_AXIS_X] = (s16)((rbm_buf[0]) | (rbm_buf[1] << 8)); + data[MC3XXX_AXIS_Y] = (s16)((rbm_buf[2]) | (rbm_buf[3] << 8)); + data[MC3XXX_AXIS_Z] = (s16)((rbm_buf[4]) | (rbm_buf[5] << 8)); + + GSE_LOG("rbm_buf<<<<<[%02x %02x %02x %02x %02x %02x]\n",rbm_buf[0], rbm_buf[2], rbm_buf[2], rbm_buf[3], rbm_buf[4], rbm_buf[5]); + GSE_LOG("RBM<<<<<[%04x %04x %04x]\n", data[MC3XXX_AXIS_X], data[MC3XXX_AXIS_Y], data[MC3XXX_AXIS_Z]); + GSE_LOG("RBM<<<<<[%04d %04d %04d]\n", data[MC3XXX_AXIS_X], data[MC3XXX_AXIS_Y], data[MC3XXX_AXIS_Z]); + return err; +} + + + int MC3XXX_ReadRBMData(struct i2c_client *client, char *buf) +{ + int res = 0; + int data[3]; + + if (!buf) + { + return EINVAL; + } + + mc3xxx_set_mode(client,MC3XXX_WAKE); + + res = MC3XXX_ReadData_RBM(client,data); + + if(res) + { + GSE_ERR("%s I2C error: ret value=%d",__func__, res); + return EIO; + } + else + { + sprintf(buf, "%04x %04x %04x", data[MC3XXX_AXIS_X], + data[MC3XXX_AXIS_Y], data[MC3XXX_AXIS_Z]); + + } + + return 0; +} + int MC3XXX_ReadOffset(struct i2c_client *client,s16 ofs[MC3XXX_AXIS_NUM]) +{ + int err = 0; + u8 off_data[6] = { 0 }; + + if(Sensor_Accuracy == MCUBE_8G_14BIT) + { + err = i2c_smbus_read_i2c_block_data(client, MC3XXX_REG_XOUT_EX_L, MC3XXX_DATA_LEN, off_data); + + ofs[MC3XXX_AXIS_X] = ((s16)(off_data[0]))|((s16)(off_data[1])<<8); + ofs[MC3XXX_AXIS_Y] = ((s16)(off_data[2]))|((s16)(off_data[3])<<8); + ofs[MC3XXX_AXIS_Z] = ((s16)(off_data[4]))|((s16)(off_data[5])<<8); + } + else if(Sensor_Accuracy == MCUBE_1_5G_8BIT) + { + err = i2c_smbus_read_i2c_block_data(client, 0, 3, off_data); + + ofs[MC3XXX_AXIS_X] = (s8)off_data[0]; + ofs[MC3XXX_AXIS_Y] = (s8)off_data[1]; + ofs[MC3XXX_AXIS_Z] = (s8)off_data[2]; + } + + GSE_LOG("MC3XXX_ReadOffset %d %d %d\n", ofs[MC3XXX_AXIS_X], ofs[MC3XXX_AXIS_Y], ofs[MC3XXX_AXIS_Z]); + + return err; +} +/*----------------------------------------------------------------------------*/ + static int MC3XXX_ResetCalibration(struct i2c_client *client) +{ + u8 buf[6] = { 0 }; + s16 tmp = 0; + int err = 0; + + u8 bMsbFilter = 0x3F; + s16 wSignBitMask = 0x2000; + s16 wSignPaddingBits = 0xC000; + + buf[0] = 0x43; + err = i2c_smbus_write_byte_data(client, 0x07, buf[0]); + if(err) + { + GSE_ERR("error 0x07: %d\n", err); + } + + err = i2c_smbus_write_i2c_block_data(client, 0x21, 6, offset_buf); + if(err) + { + GSE_ERR("error: %d\n", err); + } + + buf[0] = 0x41; + err = i2c_smbus_write_byte_data(client, 0x07, buf[0]); + if(err) + { + GSE_ERR("error: %d\n", err); + } + + msleep(20); + + + if (is_mc35xx) + { + bMsbFilter = 0x7F; + wSignBitMask = 0x4000; + wSignPaddingBits = 0x8000; + } + + tmp = ((offset_buf[1] & bMsbFilter) << 8) + offset_buf[0]; + if (tmp & wSignBitMask) + tmp |= wSignPaddingBits; + offset_data[0] = tmp; + + tmp = ((offset_buf[3] & bMsbFilter) << 8) + offset_buf[2]; + if (tmp & wSignBitMask) + tmp |= wSignPaddingBits; + offset_data[1] = tmp; + + tmp = ((offset_buf[5] & bMsbFilter) << 8) + offset_buf[4]; + if (tmp & wSignBitMask) + tmp |= wSignPaddingBits; + offset_data[2] = tmp; + + return 0; +} + +//============================================================================= + int MC3XXX_ReadCalibration(struct i2c_client *client,int dat[MC3XXX_AXIS_NUM]) +{ + signed short MC_offset[MC3XXX_AXIS_NUM + 1] = { 0 }; // +1: for 4-byte alignment + int err = 0; + + memset(MC_offset, 0, sizeof(MC_offset)); + + err = MC3XXX_ReadOffset(client, MC_offset); + + if (err) + { + GSE_ERR("read offset fail, %d\n", err); + return err; + } + + dat[MC3XXX_AXIS_X] = MC_offset[MC3XXX_AXIS_X]; + dat[MC3XXX_AXIS_Y] = MC_offset[MC3XXX_AXIS_Y]; + dat[MC3XXX_AXIS_Z] = MC_offset[MC3XXX_AXIS_Z]; + + return 0; +} + +//============================================================================= +int MC3XXX_ReadData(struct i2c_client *client, s16 buffer[MC3XXX_AXIS_NUM]) +{ + unsigned char buf[6] = { 0 }; + signed char buf1[6] = { 0 }; + char rbm_buf[6] = { 0 }; + int ret = 0; + + #ifdef SUPPORT_VIRTUAL_Z_SENSOR + int tempX=0; + int tempY=0; + int tempZ=0; + #endif + + if (enable_RBM_calibration == 0) + { + //err = hwmsen_read_block(client, addr, buf, 0x06); + } + else if (enable_RBM_calibration == 1) + { + memset(rbm_buf, 0, 6); + i2c_smbus_read_i2c_block_data(client, 0x0d , 2, &rbm_buf[0]); + i2c_smbus_read_i2c_block_data(client, 0x0d+2, 2, &rbm_buf[2]); + i2c_smbus_read_i2c_block_data(client, 0x0d+4, 2, &rbm_buf[4]); + } + + if (enable_RBM_calibration == 0) + { + if(Sensor_Accuracy == MCUBE_8G_14BIT) + { + ret = i2c_smbus_read_i2c_block_data(client, MC3XXX_REG_XOUT_EX_L, 6, buf); + + buffer[0] = (signed short)((buf[0])|(buf[1]<<8)); + buffer[1] = (signed short)((buf[2])|(buf[3]<<8)); + buffer[2] = (signed short)((buf[4])|(buf[5]<<8)); + } + else if(Sensor_Accuracy == MCUBE_1_5G_8BIT) + { + ret = i2c_smbus_read_i2c_block_data(client, MC3XXX_REG_XOUT, 3, buf1); + + buffer[0] = (signed short)buf1[0]; + buffer[1] = (signed short)buf1[1]; + buffer[2] = (signed short)buf1[2]; + } + + #ifdef SUPPORT_VIRTUAL_Z_SENSOR //add 2013-10-23 + if (g_virtual_z) + { + //printk("%s 1\n", __FUNCTION__); + + tempX = buffer[MC3XXX_AXIS_X]; + tempY = buffer[MC3XXX_AXIS_Y]; + tempZ = buffer[MC3XXX_AXIS_Z]; + //printk(" %d:Verify_Z_Railed() %d\n", (int)buffer[MC32X0_AXIS_Z], Verify_Z_Railed((int)buffer[MC32X0_AXIS_Z], LOW_RESOLUTION)); + if(1 == Verify_Z_Railed((int)buffer[MC3XXX_AXIS_Z], LOW_RESOLUTION)) // z-railed + { + Railed = 1; + + GSE_LOG("%s: Z railed", __func__); + //printk("%s: Z railed \n", __func__); + if (G_2_REVERSE_VIRTUAL_Z == 1) + buffer[MC3XXX_AXIS_Z] = (s8) ( gsensor_gain.z - (abs(tempX) + abs(tempY))); + else + buffer[MC3XXX_AXIS_Z] = (s8) -( gsensor_gain.z - (abs(tempX) + abs(tempY))); + } + else + { + Railed = 0; + } + } + #endif + mcprintkreg("MC3XXX_ReadData: %d %d %d\n", buffer[0], buffer[1], buffer[2]); + } + else if (enable_RBM_calibration == 1) + { + buffer[MC3XXX_AXIS_X] = (s16)((rbm_buf[0]) | (rbm_buf[1] << 8)); + buffer[MC3XXX_AXIS_Y] = (s16)((rbm_buf[2]) | (rbm_buf[3] << 8)); + buffer[MC3XXX_AXIS_Z] = (s16)((rbm_buf[4]) | (rbm_buf[5] << 8)); + + GSE_LOG("%s RBM<<<<<[%08d %08d %08d]\n", __func__, buffer[MC3XXX_AXIS_X], buffer[MC3XXX_AXIS_Y], buffer[MC3XXX_AXIS_Z]); + + if(gain_data[0] == 0) + { + buffer[MC3XXX_AXIS_X] = 0; + buffer[MC3XXX_AXIS_Y] = 0; + buffer[MC3XXX_AXIS_Z] = 0; + + return 0; + } + + buffer[MC3XXX_AXIS_X] = (buffer[MC3XXX_AXIS_X] + offset_data[0]/2)*gsensor_gain.x/gain_data[0]; + buffer[MC3XXX_AXIS_Y] = (buffer[MC3XXX_AXIS_Y] + offset_data[1]/2)*gsensor_gain.y/gain_data[1]; + buffer[MC3XXX_AXIS_Z] = (buffer[MC3XXX_AXIS_Z] + offset_data[2]/2)*gsensor_gain.z/gain_data[2]; + + #ifdef SUPPORT_VIRTUAL_Z_SENSOR // add 2013-10-23 + if (g_virtual_z) + { + tempX = buffer[MC3XXX_AXIS_X]; + tempY = buffer[MC3XXX_AXIS_Y]; + tempZ = buffer[MC3XXX_AXIS_Z]; + //printk("%s 2\n", __FUNCTION__); + GSE_LOG("Original RBM<<<<<[%08d %08d %08d]\n", buffer[MC3XXX_AXIS_X], buffer[MC3XXX_AXIS_Y], buffer[MC3XXX_AXIS_Z]); + printk("Verify_Z_Railed() %d\n", Verify_Z_Railed((int)buffer[MC3XXX_AXIS_Z], RBM_RESOLUTION)); + if(1 == Verify_Z_Railed(buffer[MC3XXX_AXIS_Z], RBM_RESOLUTION)) // z-railed + { + GSE_LOG("%s: Z Railed in RBM mode",__FUNCTION__); + //printk("%s: Z Railed in RBM mode\n",__FUNCTION__); + if (G_2_REVERSE_VIRTUAL_Z == 1) + buffer[MC3XXX_AXIS_Z] = (s16) ( gsensor_gain.z - (abs(tempX) + abs(tempY))); + else + buffer[MC3XXX_AXIS_Z] = (s16) -( gsensor_gain.z - (abs(tempX) + abs(tempY))); + } + GSE_LOG("RBM<<<<<[%08d %08d %08d]\n", buffer[MC3XXX_AXIS_X], buffer[MC3XXX_AXIS_Y], buffer[MC3XXX_AXIS_Z]); + } + #endif + + GSE_LOG("%s offset_data <<<<<[%d %d %d]\n", __func__, offset_data[0], offset_data[1], offset_data[2]); + GSE_LOG("%s gsensor_gain <<<<<[%d %d %d]\n", __func__, gsensor_gain.x, gsensor_gain.y, gsensor_gain.z); + GSE_LOG("%s gain_data <<<<<[%d %d %d]\n", __func__, gain_data[0], gain_data[1], gain_data[2]); + GSE_LOG("%s RBM->RAW <<<<<[%d %d %d]\n", __func__, buffer[MC3XXX_AXIS_X], buffer[MC3XXX_AXIS_Y], buffer[MC3XXX_AXIS_Z]); + } + + return 0; +} + +//============================================================================= +int MC3XXX_ReadRawData(struct i2c_client *client, char * buf) +{ + int res = 0; + s16 raw_buf[3] = { 0 }; + + if (!buf || !client) + { + return -EINVAL; + } + + mc3xxx_set_mode(client, MC3XXX_WAKE); + res = MC3XXX_ReadData(client,&raw_buf[0]); + if(res) + { + printk("%s %d\n",__FUNCTION__, __LINE__); + GSE_ERR("I2C error: ret value=%d", res); + return -EIO; + } + else + { + //const struct mc3xx0_hwmsen_convert *pCvt = &mc3xx0_cvt[mc3xx0_current_placement]; + + GSE_LOG("UPDATE dat: (%+3d %+3d %+3d)\n", + raw_buf[MC3XXX_AXIS_X], raw_buf[MC3XXX_AXIS_Y], raw_buf[MC3XXX_AXIS_Z]); + + if ((is_new_mc34x0)||(is_mc35xx)) + { + raw_buf[MC3XXX_AXIS_X] = -raw_buf[MC3XXX_AXIS_X]; + raw_buf[MC3XXX_AXIS_Y] = -raw_buf[MC3XXX_AXIS_Y]; + } + else if (is_mc3250) + { + s16 temp = 0; + + temp = raw_buf[MC3XXX_AXIS_X]; + + raw_buf[MC3XXX_AXIS_X] = raw_buf[MC3XXX_AXIS_Y]; + raw_buf[MC3XXX_AXIS_Y] = -temp; + } + + G_RAW_DATA[MC3XXX_AXIS_X] = pCvt->sign[MC3XXX_AXIS_X] * raw_buf[pCvt->map[MC3XXX_AXIS_X]]; + G_RAW_DATA[MC3XXX_AXIS_Y] = pCvt->sign[MC3XXX_AXIS_Y] * raw_buf[pCvt->map[MC3XXX_AXIS_Y]]; + G_RAW_DATA[MC3XXX_AXIS_Z] = pCvt->sign[MC3XXX_AXIS_Z] * raw_buf[pCvt->map[MC3XXX_AXIS_Z]]; + + G_RAW_DATA[MC3XXX_AXIS_Z] += gsensor_gain.z*(pCvt->sign[MC3XXX_AXIS_Z])*(1);//G_RAW_DATA[MC3XXX_AXIS_Z]+gsensor_gain.z; + + sprintf(buf, "%04x %04x %04x", G_RAW_DATA[MC3XXX_AXIS_X], + G_RAW_DATA[MC3XXX_AXIS_Y], G_RAW_DATA[MC3XXX_AXIS_Z]); + + GSE_LOG("G_RAW_DATA: (%+3d %+3d %+3d)\n", + G_RAW_DATA[MC3XXX_AXIS_X], G_RAW_DATA[MC3XXX_AXIS_Y], G_RAW_DATA[MC3XXX_AXIS_Z]); + } + + return 0; +} + +//============================================================================= +static int MC3XXX_ReadRegMap(struct i2c_client *client, u8 *pbUserBuf) +{ + u8 data[128] = {0}; + //u8 addr = 0x00; + int err = 0; + int i = 0; + + if(NULL == client) + { + err = -EINVAL; + return err; + } + + + for(i = 0; i < 64; i++) + { + data[i] = i2c_smbus_read_byte_data(client, i); + printk(KERN_INFO "mcube register map Register[%x] = 0x%x\n", i ,data[i]); + } + + msleep(50); + + mcube_write_log_data(client, data); + + msleep(50); + + if (NULL != pbUserBuf) + { + printk(KERN_INFO "copy to user buffer\n"); + memcpy(pbUserBuf, data, 64); + } + + return err; +} + +//============================================================================= +void MC3XXX_Reset(struct i2c_client *client) +{ + //s16 tmp = 0, x_gain = 0, y_gain = 0, z_gain = 0; + u8 buf[3] = { 0 }; + int err = 0; + + buf[0] = 0x43; + i2c_smbus_write_byte_data(client, 0x07, buf[0]); + + i2c_smbus_read_i2c_block_data(client, 0x04, 1, buf); + + if (0x00 == (buf[0] & 0x40)) + { + buf[0] = 0x6d; + i2c_smbus_write_byte_data(client, 0x1b, buf[0]); + + buf[0] = 0x43; + i2c_smbus_write_byte_data(client, 0x1b, buf[0]); + } + + msleep(5); + + buf[0] = 0x43; + i2c_smbus_write_byte_data(client, 0x07, buf[0]); + + buf[0] = 0x80; + i2c_smbus_write_byte_data(client, 0x1c, buf[0]); + + buf[0] = 0x80; + i2c_smbus_write_byte_data(client, 0x17, buf[0]); + + msleep(5); + + buf[0] = 0x00; + i2c_smbus_write_byte_data(client, 0x1c, buf[0]); + + buf[0] = 0x00; + i2c_smbus_write_byte_data(client, 0x17, buf[0]); + + msleep(5); + + memset(offset_buf, 0, sizeof(offset_buf)); + + err = i2c_smbus_read_i2c_block_data(client, 0x21, 6, offset_buf); + + i2c_smbus_read_i2c_block_data(client, 0x04, 1, buf); + + if (0x00 == (buf[0] & 0x40)) + { + buf[0] = 0x6d; + i2c_smbus_write_byte_data(client, 0x1b, buf[0]); + + buf[0] = 0x43; + i2c_smbus_write_byte_data(client, 0x1b, buf[0]); + } + + buf[0] = 0x41; + i2c_smbus_write_byte_data(client, 0x07, buf[0]); + +} +#endif + +int mc3xxx_read_accel_xyz(struct i2c_client *client, s16 * acc) +{ + int comres = 0; + s16 raw_data[MC3XXX_AXIS_NUM] = { 0 }; + //const struct mc3xx0_hwmsen_convert *pCvt = &mc3xx0_cvt[mc3xx0_current_placement]; + +#ifdef DOT_CALI + s16 raw_buf[6] = { 0 }; + + comres = MC3XXX_ReadData(client, &raw_buf[0]); + + raw_data[MC3XXX_AXIS_X] = raw_buf[0]; + raw_data[MC3XXX_AXIS_Y] = raw_buf[1]; + raw_data[MC3XXX_AXIS_Z] = raw_buf[2]; +#else + unsigned char raw_buf[6] = { 0 }; + signed char raw_buf1[3] = { 0 }; + + if(Sensor_Accuracy == MCUBE_8G_14BIT) + { + comres = i2c_smbus_read_i2c_block_data(client, MC3XXX_REG_XOUT_EX_L, 6, raw_buf); + + raw_data[MC3XXX_AXIS_X] = (signed short)((raw_buf[0])|(raw_buf[1]<<8)); + raw_data[MC3XXX_AXIS_Y] = (signed short)((raw_buf[2])|(raw_buf[3]<<8)); + raw_data[MC3XXX_AXIS_Z] = (signed short)((raw_buf[4])|(raw_buf[5]<<8)); + } + else if(Sensor_Accuracy == MCUBE_1_5G_8BIT) + { + comres = i2c_smbus_read_i2c_block_data(client, MC3XXX_REG_XOUT, 3, raw_buf1); + + raw_data[MC3XXX_AXIS_X] = (signed short)raw_buf1[0]; + raw_data[MC3XXX_AXIS_Y] = (signed short)raw_buf1[1]; + raw_data[MC3XXX_AXIS_Z] = (signed short)raw_buf1[2]; + } +#endif + + if((is_new_mc34x0)||(is_mc35xx)) + { + raw_data[MC3XXX_AXIS_X] = -raw_data[MC3XXX_AXIS_X]; + raw_data[MC3XXX_AXIS_Y] = -raw_data[MC3XXX_AXIS_Y]; + } + else if (is_mc3250) + { + s16 temp = 0; + + temp = raw_data[MC3XXX_AXIS_X]; + + raw_data[MC3XXX_AXIS_X] = raw_data[MC3XXX_AXIS_Y]; + raw_data[MC3XXX_AXIS_Y] = -temp; + } + //printk("%s:%d %d %d\n",__FUNCTION__,raw_data[0],raw_data[1],raw_data[2]); + acc[MC3XXX_AXIS_X] = pCvt->sign[MC3XXX_AXIS_X] * raw_data[pCvt->map[MC3XXX_AXIS_X]]; + acc[MC3XXX_AXIS_Y] = pCvt->sign[MC3XXX_AXIS_Y] * raw_data[pCvt->map[MC3XXX_AXIS_Y]]; + acc[MC3XXX_AXIS_Z] = pCvt->sign[MC3XXX_AXIS_Z] * raw_data[pCvt->map[MC3XXX_AXIS_Z]]; + + return comres; +} + +//============================================================================= +static void mc3xxx_work_func(struct work_struct *work) +{ + struct mc3xxx_data *data = &l_sensorconfig;//container_of(work, struct mc3xxx_data, work); + //int ret = 0; + s16 raw[3] = { 0 }; + +#ifdef DOT_CALI + if( load_cali_flg > 0) + { + /* ret = mcube_read_cali_file(data->client); + + if(ret == 0) + load_cali_flg = ret; + else + load_cali_flg--; + + GSE_LOG("load_cali %d\n",ret); */ + MC3XXX_WriteCalibration(data->client,l_sensorconfig.offset); + load_cali_flg = 0; + } +#endif +#if 1 +//gsensor not use when resume + if(wake_mc3xxx_flg==1){ + wake_mc3xxx_flg=0; + + + mc3xxx_chip_init(data->client); + MC3XXX_ResetCalibration(data->client); + + MC3XXX_WriteCalibration(data->client,l_sensorconfig.offset); + /*ret =mcube_read_cali_file(data->client); + if(ret !=0) + printk("*load_cali %d\n",ret); */ + } +#endif + + mc3xxx_read_accel_xyz(data->client, &raw[0]); + //printk("%s:%d %d %d\n",__FUNCTION__,raw[0],raw[1],raw[2]); + input_report_abs(data->input_dev, ABS_X, raw[0]); + input_report_abs(data->input_dev, ABS_Y, raw[1]); + input_report_abs(data->input_dev, ABS_Z, raw[2]); + input_sync(data->input_dev); + + queue_delayed_work(data->mc3xxx_wq, &data->work, msecs_to_jiffies(sample_rate_2_memsec(data->sensor_samp))); +} +/* +//============================================================================= +static enum hrtimer_restart mc3xxx_timer_func(struct hrtimer *timer) +{ + struct mc3xxx_data *data = container_of(timer, struct mc3xxx_data, timer); + + queue_work(data->mc3xxx_wq, &data->work); + + hrtimer_start(&data->timer, ktime_set(0, sensor_duration*1000000), HRTIMER_MODE_REL); + + return HRTIMER_NORESTART; +} +*/ +//MCUBE_BACKUP_FILE +static void mcube_copy_file(const char *dstFilePath) +{ + int err =0; + initKernelEnv(); + + fd_file = openFile(dstFilePath,O_RDWR,0); + if (fd_file == NULL) + { + GSE_LOG("open %s fail\n",dstFilePath); + return; + } + + if ((err = writeFile(fd_file,backup_buf,64))>0) + GSE_LOG("buf:%s\n",backup_buf); + else + GSE_LOG("write file error %d\n",err); + + set_fs(oldfs); ; + closeFile(fd_file); + +} +//MCUBE_BACKUP_FILE + +extern int wmt_setsyspara(char *varname, char *varval); +static void update_var(void) +{ + char varbuf[64]; + int varlen; + + memset(varbuf, 0, sizeof(varbuf)); + varlen = sizeof(varbuf); + + sprintf(varbuf, "%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d", + l_sensorconfig.op, + l_sensorconfig.int_gpio, + l_sensorconfig.samp, + (pCvt->map[MC3XXX_AXIS_X]), + (pCvt->sign[MC3XXX_AXIS_X]), + (pCvt->map[MC3XXX_AXIS_Y]), + (pCvt->sign[MC3XXX_AXIS_Y]), + (pCvt->map[MC3XXX_AXIS_Z]), + (pCvt->sign[MC3XXX_AXIS_Z]), + l_sensorconfig.offset[0], + l_sensorconfig.offset[1], + l_sensorconfig.offset[2] + ); + + wmt_setsyspara("wmt.io.mc3230sensor",varbuf); +} + +static long wmt_mc3xxx_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + short enable = 0; + short delay = 0; + unsigned int temp; + + switch (cmd){ + + case ECS_IOCTL_APP_SET_AFLAG: + // enable/disable sensor + if (copy_from_user(&enable, (short*)arg, sizeof(short))) + { + errlog("Can't get enable flag!!!\n"); + return -EFAULT; + } + if ((enable >=0) && (enable <=1)) + { + dbg("driver: disable/enable(%d) gsensor. l_sensorconfig.sensor_samp=%d\n", enable, l_sensorconfig.sensor_samp); + + if (enable != l_sensorconfig.sensor_enable) + { + + l_sensorconfig.sensor_enable = enable; + + } + } else { + errlog("Wrong enable argument!!!\n"); + return -EFAULT; + } + break; + case ECS_IOCTL_APP_SET_DELAY://IOCTL_SENSOR_SET_DELAY_ACCEL: + // set the rate of g-sensor + if (copy_from_user(&delay,(short*)arg, sizeof(short))) + { + errlog("Can't get set delay!!!\n"); + return -EFAULT; + } + dbg("Get delay=%d \n", delay); + + if ((delay >=0) && (delay < 20)) + { + delay = 20; + } else if (delay > 200) + { + delay = 200; + } + if (delay > 0) + { + l_sensorconfig.sensor_samp = 1000/delay; + } else { + errlog("error delay argument(delay=%d)!!!\n",delay); + return -EFAULT; + } + + break; + case WMT_IOCTL_SENSOR_GET_DRVID: + temp = MC3230_DRVID; + if (copy_to_user((unsigned int*)arg, &temp, sizeof(unsigned int))) + { + return -EFAULT; + } + dbg("mc32x0_driver_id:%d\n",temp); + break; + case WMT_IOCTL_SENOR_GET_RESOLUTION: + if(Sensor_Accuracy &MCUBE_1_5G_8BIT){ + if(is_mc35xx) //mc3236:8 bit ,+/-2g + temp = (8<<8) | 4; + else + temp = (8<<8) | 3; //mc3230:8 bit ,+/-1.5g + + } + if (copy_to_user((unsigned int *)arg, &temp, sizeof(unsigned int))) + { + return -EFAULT; + } + printk("<<<<<<lock); + MC3XXX_ReadRawData(client, strbuf); + //mutex_unlock(&data->lock); + + if (copy_to_user((void __user *) arg, &strbuf, strlen(strbuf)+1)) + { + printk("failed to copy sense data to user space."); + return -EFAULT; + } + break; + + case GSENSOR_MCUBE_IOCTL_SET_CALI: + GSE_LOG("fwq GSENSOR_MCUBE_IOCTL_SET_CALI!!\n"); + data1 = (void __user *)arg; + + if(data1 == NULL) + { + ret = -EINVAL; + break; + } + if(copy_from_user(&sensor_data, data1, sizeof(sensor_data))) + { + ret = -EFAULT; + break; + } + else + { + l_sensorconfig.offset[MC3XXX_AXIS_X] = sensor_data.x; + l_sensorconfig.offset[MC3XXX_AXIS_Y] = sensor_data.y; + l_sensorconfig.offset[MC3XXX_AXIS_Z] = sensor_data.z; + update_var(); + GSE_LOG("GSENSOR_MCUBE_IOCTL_SET_CALI %d %d %d %d %d %d!!\n", l_sensorconfig.offset[MC3XXX_AXIS_X], l_sensorconfig.offset[MC3XXX_AXIS_Y],l_sensorconfig.offset[MC3XXX_AXIS_Z] ,sensor_data.x, sensor_data.y ,sensor_data.z); + + //mutex_lock(&data->lock); + ret = MC3XXX_WriteCalibration(client, l_sensorconfig.offset); + //mutex_unlock(&data->lock); + } + break; + + case GSENSOR_IOCTL_CLR_CALI: + GSE_LOG("fwq GSENSOR_IOCTL_CLR_CALI!!\n"); + //mutex_lock(&data->lock); + l_sensorconfig.offset[0] = 0; + l_sensorconfig.offset[1] = 0; + l_sensorconfig.offset[2] = 0; + + update_var(); + ret = MC3XXX_ResetCalibration(client); + //mutex_unlock(&data->lock); + break; + + case GSENSOR_IOCTL_GET_CALI: + GSE_LOG("fwq mc3xxx GSENSOR_IOCTL_GET_CALI\n"); + + data1 = (unsigned char*)arg; + + if(data1 == NULL) + { + ret = -EINVAL; + break; + } + + if((ret = MC3XXX_ReadCalibration(client,l_sensorconfig.offset))) + { + GSE_LOG("fwq mc3xxx MC3XXX_ReadCalibration error!!!!\n"); + break; + } + + sensor_data.x = l_sensorconfig.offset[MC3XXX_AXIS_X]; + sensor_data.y = l_sensorconfig.offset[MC3XXX_AXIS_Y]; + sensor_data.z = l_sensorconfig.offset[MC3XXX_AXIS_Z]; + + if(copy_to_user(data1, &sensor_data, sizeof(sensor_data))) + { + ret = -EFAULT; + break; + } + break; + + case GSENSOR_IOCTL_SET_CALI_MODE: + GSE_LOG("fwq mc3xxx GSENSOR_IOCTL_SET_CALI_MODE\n"); + break; + + case GSENSOR_MCUBE_IOCTL_READ_RBM_DATA: + GSE_LOG("fwq GSENSOR_MCUBE_IOCTL_READ_RBM_DATA\n"); + data1 = (void __user *) arg; + if(data1 == NULL) + { + ret = -EINVAL; + break; + } + MC3XXX_ReadRBMData(client,(char *)&strbuf); + if(copy_to_user(data1, &strbuf, strlen(strbuf)+1)) + { + ret = -EFAULT; + break; + } + break; + case GSENSOR_MCUBE_IOCTL_SET_RBM_MODE: + GSE_LOG("fwq GSENSOR_MCUBE_IOCTL_SET_RBM_MODE\n"); + //MCUBE_BACKUP_FILE + if(READ_FROM_BACKUP==true) + { + + //mcube_copy_file(CALIB_PATH); + + READ_FROM_BACKUP = false; + } + //MCUBE_BACKUP_FILE + //mutex_lock(&data->lock); + MC3XXX_rbm(client, 1); + //mutex_unlock(&data->lock); + break; + + case GSENSOR_MCUBE_IOCTL_CLEAR_RBM_MODE: + GSE_LOG("fwq GSENSOR_MCUBE_IOCTL_CLEAR_RBM_MODE\n"); + //mutex_lock(&data->lock); + MC3XXX_rbm(client, 0); + //mutex_unlock(&data->lock); + break; + + case GSENSOR_MCUBE_IOCTL_REGISTER_MAP: + GSE_LOG("fwq GSENSOR_MCUBE_IOCTL_REGISTER_MAP\n"); + MC3XXX_ReadRegMap(client, NULL); + break; + + case GSENSOR_MCUBE_IOCTL_READ_PRODUCT_ID: + GSE_LOG("fwq GSENSOR_MCUBE_IOCTL_READ_PRODUCT_ID\n"); + data1 = (void __user *) arg; + if(data1 == NULL) + { + ret = -EINVAL; + break; + } + + if (MC3XXX_RETCODE_SUCCESS != (prod = MC3XX0_ValidateSensorIC(s_bPCODE))) + GSE_LOG("Not mCube accelerometers!\n"); + + if(copy_to_user(data1, &prod, sizeof(prod))) + { + GSE_LOG("%s: read pcode fail to copy!\n", __func__); + return -EFAULT; + } + break; +#endif + + default: + ret = -EINVAL; + break; + } + + return ret; +} + + +static int mc3xxx_open(struct inode *inode, struct file *filp) +{ + return nonseekable_open(inode, filp); +} + +static int mc3xxx_release(struct inode *inode, struct file *filp) +{ + return 0; +} + +static int wmt_mc3xxx_open(struct inode *inode, struct file *filp) +{ + return 0; +} + +static int wmt_mc3xxx_release(struct inode *inode, struct file *filp) +{ + return 0; +} + + +//============================================================================= +static struct file_operations sensor_fops = +{ + .owner = THIS_MODULE, + .open = mc3xxx_open, + .release = mc3xxx_release, + .unlocked_ioctl = mc3xxx_ioctl, +}; + +static struct file_operations wmt_sensor_fops = +{ + .owner = THIS_MODULE, + .open = wmt_mc3xxx_open, + .release = wmt_mc3xxx_release, + .unlocked_ioctl = wmt_mc3xxx_ioctl, +}; + + + +static int sensor_writeproc( struct file *file, + const char *buffer, + unsigned long count, + void *data ) +{ + + //int inputval = -1; + int enable, sample = -1; + char tembuf[8]; + //unsigned int amsr = 0; + int test = 0; + + mutex_lock(&l_sensorconfig.lock); + memset(tembuf, 0, sizeof(tembuf)); + // get sensor level and set sensor level + if (sscanf(buffer, "isdbg=%d\n", &l_sensorconfig.isdbg)) + { + // only set the dbg flag + } else if (sscanf(buffer, "samp=%d\n", &sample)) + { + if (sample > 0) + { + if (sample != l_sensorconfig.sensor_samp) + { + // should do sth + } + //printk(KERN_ALERT "sensor samp=%d(amsr:%d) has been set.\n", sample, amsr); + } else { + klog("Wrong sample argumnet of sensor.\n"); + } + } else if (sscanf(buffer, "enable=%d\n", &enable)) + { + if ((enable < 0) || (enable > 1)) + { + dbg("The argument to enable/disable g-sensor should be 0 or 1 !!!\n"); + } else if (enable != l_sensorconfig.sensor_enable) + { + //mma_enable_disable(enable); + l_sensorconfig.sensor_enable = enable; + } + } else if (sscanf(buffer, "sensor_test=%d\n", &test)) + { // for test begin + l_sensorconfig.test_pass = 0; + } else if (sscanf(buffer, "sensor_testend=%d\n", &test)) + { // Don nothing only to be compatible the before testing program + } + mutex_unlock(&l_sensorconfig.lock); + return count; +} + +static int sensor_readproc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len = 0; + + len = sprintf(page, + "test_pass=%d\nisdbg=%d\nrate=%d\nenable=%d\n", + l_sensorconfig.test_pass, + l_sensorconfig.isdbg, + l_sensorconfig.sensor_samp, + l_sensorconfig.sensor_enable + ); + return len; +} + + +//#ifdef CONFIG_HAS_EARLYSUSPEND +static void mc3xxx_early_suspend(struct platform_device *pdev, pm_message_t state) +{ + /*struct mc3xxx_data *data = NULL; + + data = container_of(handler, struct mc3xxx_data, early_suspend); + + hrtimer_cancel(&data->timer);*/ + + cancel_delayed_work_sync(&l_sensorconfig.work); + mc3xxx_set_mode(l_sensorconfig.client,MC3XXX_STANDBY); +} + +//============================================================================= +static void mc3xxx_early_resume(struct platform_device *pdev) +{ + struct mc3xxx_data *data = &l_sensorconfig; + + wake_mc3xxx_flg =1; + //data = container_of(handler, struct mc3xxx_data, early_suspend); + + mc3xxx_set_mode(data->client,MC3XXX_WAKE); + + queue_delayed_work(data->mc3xxx_wq, &data->work, msecs_to_jiffies(sample_rate_2_memsec(data->sensor_samp))); + //hrtimer_start(&data->timer, ktime_set(1, 0), HRTIMER_MODE_REL); +} +//#endif + +//============================================================================= +static struct miscdevice mc3xxx_device = +{ + .minor = MISC_DYNAMIC_MINOR, + .name = SENSOR_NAME, + .fops = &sensor_fops, +}; + +static struct miscdevice mc3xxx_wmt_device = +{ + .minor = MISC_DYNAMIC_MINOR, + .name = "sensor_ctrl", + .fops = &wmt_sensor_fops, +}; + + + +/***************************************** + *** MC3XX0_ValidateSensorIC + *****************************************/ +static int MC3XX0_ValidateSensorIC(unsigned char bPCode) +{ + unsigned char _baOurSensorList[] = { MC3XXX_PCODE_3210 , MC3XXX_PCODE_3230 , + MC3XXX_PCODE_3250 , + MC3XXX_PCODE_3410 , MC3XXX_PCODE_3430 , + MC3XXX_PCODE_3410N, MC3XXX_PCODE_3430N, + MC3XXX_PCODE_3510B, MC3XXX_PCODE_3530B, + MC3XXX_PCODE_3510C, MC3XXX_PCODE_3530C + }; + + int _nSensorCount = (sizeof(_baOurSensorList) / sizeof(_baOurSensorList[0])); + int _nCheckIndex = 0; + + GSE_LOG("[%s] code to be verified: 0x%X, _nSensorCount: %d\n", __FUNCTION__, bPCode, _nSensorCount); + + for (_nCheckIndex = 0; _nCheckIndex < _nSensorCount; _nCheckIndex++) + { + if (_baOurSensorList[_nCheckIndex] == bPCode) + return (MC3XXX_RETCODE_SUCCESS); + } + + if (MC3XXX_PCODE_3530C == (bPCode | 0x0E)) + return (MC3XXX_RETCODE_SUCCESS); + + return (MC3XXX_RETCODE_ERROR_IDENTIFICATION); +} + +/***************************************** + *** _mc3xxx_i2c_auto_probe + *****************************************/ +static int _mc3xxx_i2c_auto_probe(struct i2c_client *client) +{ + unsigned char _baDataBuf[2] = {0}; + int _nProbeAddrCount = (sizeof(mc3xxx_i2c_auto_probe_addr) / sizeof(mc3xxx_i2c_auto_probe_addr[0])); + int _nCount = 0; + int _nCheckCount = 0; + + //GSE_FUN(); + + s_bPCODE = 0x00; + + for (_nCount = 0; _nCount < _nProbeAddrCount; _nCount++) + { + _nCheckCount = 0; + client->addr = mc3xxx_i2c_auto_probe_addr[_nCount]; + + //GSE_LOG("[%s] probing addr: 0x%X\n", __FUNCTION__, client->addr); + +_I2C_AUTO_PROBE_RECHECK_: + _baDataBuf[0] = MC3XXX_REG_PRODUCT_CODE; + if (0 > i2c_master_send(client, &(_baDataBuf[0]), 1)) + { + //GSE_ERR("ERR: addr: 0x%X fail to communicate-2!\n", client->addr); + continue; + } + + if (0 > i2c_master_recv(client, &(_baDataBuf[0]), 1)) + { + //GSE_ERR("ERR: addr: 0x%X fail to communicate-3!\n", client->addr); + continue; + } + + _nCheckCount++; + + //GSE_LOG("[%s][%d] addr: 0x%X ok to read REG(0x3B): 0x%X\n", __FUNCTION__, _nCheckCount, client->addr, _baDataBuf[0]); + + if (0x00 == _baDataBuf[0]) + { + if (1 == _nCheckCount) + { + MC3XXX_Reset(client); + goto _I2C_AUTO_PROBE_RECHECK_; + } + } + + if (MC3XXX_RETCODE_SUCCESS == MC3XX0_ValidateSensorIC(_baDataBuf[0])) + { + //GSE_LOG("[%s] addr: 0x%X confirmed ok to use.\n", __FUNCTION__, client->addr); + + s_bPCODE = _baDataBuf[0]; + + return (MC3XXX_RETCODE_SUCCESS); + } + } + + return (MC3XXX_RETCODE_ERROR_I2C); +} + + +//============================================================================= +//static int mc3xxx_probe(struct i2c_client *client, +// const struct i2c_device_id *id) +static int mc3xxx_probe(struct platform_device *pdev) +{ + int ret = 0; + //int product_code = 0; + struct mc3xxx_data *data = &l_sensorconfig; + struct i2c_client *client = l_sensorconfig.client; + + #ifdef DOT_CALI + load_cali_flg = 30; + #endif +/* + if (MC3XXX_RETCODE_SUCCESS != _mc3xxx_i2c_auto_probe(client)) + { + GSE_ERR("ERR: fail to probe mCube sensor!\n"); + goto err_check_functionality_failed; + } + + data = kzalloc(sizeof(struct mc3xxx_data), GFP_KERNEL); + if(data == NULL) + { + ret = -ENOMEM; + goto err_alloc_data_failed; + } +*/ + data->sensor_proc = create_proc_entry(GSENSOR_PROC_NAME, 0666, NULL/*&proc_root*/); + if (data->sensor_proc != NULL) + { + data->sensor_proc->write_proc = sensor_writeproc; + data->sensor_proc->read_proc = sensor_readproc; + } + + data->mc3xxx_wq = create_singlethread_workqueue("mc3xxx_wq"); + if (!data->mc3xxx_wq ) + { + ret = -ENOMEM; + goto err_create_workqueue_failed; + } + INIT_DELAYED_WORK(&data->work, mc3xxx_work_func); + mutex_init(&data->lock); + + //sensor_duration = SENSOR_DURATION_DEFAULT; + //sensor_state_flag = 1; + + + data->client = client; + dev.client=client; + + i2c_set_clientdata(client, data); + + data->input_dev = input_allocate_device(); + if (!data->input_dev) { + ret = -ENOMEM; + goto exit_input_dev_alloc_failed; + } + + #ifdef DOT_CALI + MC3XXX_Reset(client); + #endif + + ret = mc3xxx_chip_init(client); + if (ret < 0) { + goto err_chip_init_failed; + } + + set_bit(EV_ABS, data->input_dev->evbit); + data->map[0] = G_0; + data->map[1] = G_1; + data->map[2] = G_2; + data->inv[0] = G_0_REVERSE; + data->inv[1] = G_1_REVERSE; + data->inv[2] = G_2_REVERSE; + + input_set_abs_params(data->input_dev, ABS_X, -32*8, 32*8, INPUT_FUZZ, INPUT_FLAT); + input_set_abs_params(data->input_dev, ABS_Y, -32*8, 32*8, INPUT_FUZZ, INPUT_FLAT); + input_set_abs_params(data->input_dev, ABS_Z, -32*8, 32*8, INPUT_FUZZ, INPUT_FLAT); + + data->input_dev->name = "g-sensor"; + + ret = input_register_device(data->input_dev); + if (ret) { + goto exit_input_register_device_failed; + } + + mc3xxx_device.parent = &client->dev; + + ret = misc_register(&mc3xxx_device); + if (ret) { + goto exit_misc_device_register_failed; + } + + ret = misc_register(&mc3xxx_wmt_device); + if (ret) { + goto exit_misc_device_register_failed; + } + + ret = sysfs_create_group(&data->input_dev->dev.kobj, &mc3xxx_group); +/* + if (!data->use_irq){ + hrtimer_init(&data->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + data->timer.function = mc3xxx_timer_func; + //hrtimer_start(&data->timer, ktime_set(1, 0), HRTIMER_MODE_REL); + } +*/ + +#ifdef CONFIG_HAS_EARLYSUSPEND + data->early_suspend.suspend = mc3xxx_early_suspend; + data->early_suspend.resume = mc3xxx_early_resume; + register_early_suspend(&data->early_suspend); +#endif + data->enabled = 1; + queue_delayed_work(data->mc3xxx_wq, &data->work, msecs_to_jiffies(sample_rate_2_memsec(data->sensor_samp))); + //strcpy(mc3xxx_on_off_str,"gsensor_int2"); + //gpio_set_one_pin_io_status(mc3xxx_pin_hd,0,mc3xxx_on_off_str); + + printk("mc3xxx probe ok \n"); + + return 0; +exit_misc_device_register_failed: +exit_input_register_device_failed: + input_free_device(data->input_dev); +err_chip_init_failed: +exit_input_dev_alloc_failed: + destroy_workqueue(data->mc3xxx_wq); +err_create_workqueue_failed: + kfree(data); +//err_alloc_data_failed: +//err_check_functionality_failed: + printk("mc3xxx probe failed \n"); + return ret; + +} + +//static int mc3xxx_remove(struct i2c_client *client) +static int mc3xxx_remove(struct platform_device *pdev) +{ + /*struct mc3xxx_data *data = i2c_get_clientdata(client); + + hrtimer_cancel(&data->timer); + input_unregister_device(data->input_dev); +// gpio_release(mc3xxx_pin_hd, 2); + misc_deregister(&mc3xxx_device); + sysfs_remove_group(&data->input_dev->dev.kobj, &mc3xxx_group); + kfree(data); + return 0;*/ + + if (NULL != l_sensorconfig.mc3xxx_wq) + { + cancel_delayed_work_sync(&l_sensorconfig.work); + flush_workqueue(l_sensorconfig.mc3xxx_wq); + destroy_workqueue(l_sensorconfig.mc3xxx_wq); + l_sensorconfig.mc3xxx_wq = NULL; + } + if (l_sensorconfig.sensor_proc != NULL) + { + remove_proc_entry(GSENSOR_PROC_NAME, NULL); + l_sensorconfig.sensor_proc = NULL; + } + misc_deregister(&mc3xxx_device); + misc_deregister(&mc3xxx_wmt_device); + sysfs_remove_group(&l_sensorconfig.input_dev->dev.kobj, &mc3xxx_group); + input_unregister_device(l_sensorconfig.input_dev); + return 0; +} + +//============================================================================= +/* +static void mc3xxx_shutdown(struct i2c_client *client) +{ + struct mc3xxx_data *data = i2c_get_clientdata(client); + + if(data->enabled) + mc3xxx_enable(data, 0); +} +*/ + +//============================================================================= + +static const struct i2c_device_id mc3xxx_id[] = +{ + { SENSOR_NAME, 0 }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, mc3xxx_id); +/* +static struct i2c_driver mc3xxx_driver = +{ + .class = I2C_CLASS_HWMON, + + .driver = { + .owner = THIS_MODULE, + .name = SENSOR_NAME, + }, + .id_table = mc3xxx_id, + .probe = mc3xxx_probe, + .remove = mc3xxx_remove, + //.shutdown = mc3xxx_shutdown, +}; +*/ +extern int wmt_getsyspara(char *varname, unsigned char *varval, int *varlen); +static int get_axisset(void) +{ + char varbuf[64]; + int n; + int varlen; + //int tmpoff[3] = {0}; + memset(varbuf, 0, sizeof(varbuf)); + varlen = sizeof(varbuf); + + pCvt = (struct mc3xx0_hwmsen_convert *)kzalloc(sizeof(struct mc3xx0_hwmsen_convert), GFP_KERNEL); + + if (wmt_getsyspara("wmt.io.mc3230.virtualz", varbuf, &varlen)) { + errlog("Can't get gsensor config in u-boot!!!!\n"); + //return -1; + } else { + sscanf(varbuf, "%d", &g_virtual_z); + + } + printk("%s g_virtual_z %d\n", __FUNCTION__, g_virtual_z); + memset(varbuf, 0, sizeof(varbuf)); + if (wmt_getsyspara("wmt.io.mc3230sensor", varbuf, &varlen)) { + errlog("Can't get gsensor config in u-boot!!!!\n"); + return -1; //open it for no env just,not insmod such module 2014-6-30 + } else { + n = sscanf(varbuf, "%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d", + &l_sensorconfig.op, + &l_sensorconfig.int_gpio, + &l_sensorconfig.samp, + &(pCvt->map[MC3XXX_AXIS_X]), + &(pCvt->sign[MC3XXX_AXIS_X]), + &(pCvt->map[MC3XXX_AXIS_Y]), + &(pCvt->sign[MC3XXX_AXIS_Y]), + &(pCvt->map[MC3XXX_AXIS_Z]), + &(pCvt->sign[MC3XXX_AXIS_Z]), + &(l_sensorconfig.offset[0]), + &(l_sensorconfig.offset[1]), + &(l_sensorconfig.offset[2]) + ); + if (n != 12) { + printk(KERN_ERR "gsensor format is error in u-boot!!!\n"); + return -1; + } + l_sensorconfig.sensor_samp = l_sensorconfig.samp; + + + dbg("get the sensor config: %d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d\n", + l_sensorconfig.op, + l_sensorconfig.int_gpio, + l_sensorconfig.samp, + pCvt->map[MC3XXX_AXIS_X], + pCvt->sign[MC3XXX_AXIS_X], + pCvt->map[MC3XXX_AXIS_Y], + pCvt->sign[MC3XXX_AXIS_Y], + pCvt->map[MC3XXX_AXIS_Z], + pCvt->sign[MC3XXX_AXIS_Z], + l_sensorconfig.offset[0], + l_sensorconfig.offset[1], + l_sensorconfig.offset[2] + ); + } + return 0; +} + +static void mc3xxx_platform_release(struct device *device) +{ + return; +} + + +static struct platform_device mc3xxx_pdevice = { + .name = SENSOR_NAME, + .id = 0, + .dev = { + .release = mc3xxx_platform_release, + }, +}; + +static struct platform_driver mc3xxx_pdriver = { + .probe = mc3xxx_probe, + .remove = mc3xxx_remove, + .suspend = mc3xxx_early_suspend, + .resume = mc3xxx_early_resume, + //.shutdown = mc3xxx_shutdown, + .driver = { + .name = SENSOR_NAME, + }, +}; + + +static int __init mc3xxx_init(void) +{ + int ret = -1; + struct i2c_client *this_client; + printk("mc3xxx: init\n"); + + //ret = i2c_add_driver(&mc3xxx_driver); + + // parse g-sensor u-boot arg + ret = get_axisset(); + if (ret < 0) + { + printk("<<<<<%s user choose to no sensor chip!\n", __func__); + return ret; + } + + if (!(this_client = sensor_i2c_register_device(0, mc3xxx_i2c_auto_probe_addr[0], SENSOR_NAME))) + { + printk(KERN_ERR"Can't register gsensor i2c device!\n"); + return -1; + } + + if (MC3XXX_RETCODE_SUCCESS != _mc3xxx_i2c_auto_probe(this_client)) + { + GSE_ERR("ERR: fail to auto_probe mCube sensor!\n"); + sensor_i2c_unregister_device(this_client); + return -1; + } + + + l_sensorconfig.client = this_client; + + l_dev_class = class_create(THIS_MODULE, SENSOR_NAME); + if (IS_ERR(l_dev_class)){ + ret = PTR_ERR(l_dev_class); + printk(KERN_ERR "Can't class_create gsensor device !!\n"); + return ret; + } + if((ret = platform_device_register(&mc3xxx_pdevice))) + { + klog("Can't register mc3xxx platform devcie!!!\n"); + return ret; + } + if ((ret = platform_driver_register(&mc3xxx_pdriver)) != 0) + { + errlog("Can't register mc3xxx platform driver!!!\n"); + return ret; + } + + + return ret; +} + +static void __exit mc3xxx_exit(void) +{ + //i2c_del_driver(&mc3xxx_driver); +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&l_sensorconfig.earlysuspend); +#endif + platform_driver_unregister(&mc3xxx_pdriver); + platform_device_unregister(&mc3xxx_pdevice); + sensor_i2c_unregister_device(l_sensorconfig.client); + class_destroy(l_dev_class); +} + +//============================================================================= +module_init(mc3xxx_init); +module_exit(mc3xxx_exit); + +MODULE_DESCRIPTION("mc3xxx accelerometer driver"); +MODULE_AUTHOR("mCube-inc"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(SENSOR_DRIVER_VERSION); + diff --git a/drivers/input/sensor/mma7660_gsensor/Makefile b/drivers/input/sensor/mma7660_gsensor/Makefile new file mode 100755 index 00000000..99ecc6a1 --- /dev/null +++ b/drivers/input/sensor/mma7660_gsensor/Makefile @@ -0,0 +1,34 @@ +KERNELDIR=../../../../ +#KERNELDIR=/home/hangyan/android8850/kernel/ANDROID_3.0.8 +CROSS = arm_1103_le- +CC= $(CROSS)gcc +LD= $(CROSS)ld +STRIP = $(CROSS)strip + +DEBUG = n + +# Add your debugging flag (or not) to EXTRA_CFLAGS +ifeq ($(DEBUG),y) +# DEBFLAGS = -O -g -DSCULL_DEBUG # "-O" is needed to expand inlines +DEBFLAGS = -O0 -g -DSCULL_DEBUG # "-O" is needed to expand inlines + +else + DEBFLAGS = -O2 -Wall +endif + +EXTRA_CFLAGS += $(DEBFLAGS) + + +MY_MODULE_NAME=s_wmt_gsensor_mma7660 + +obj-m := $(MY_MODULE_NAME).o +$(MY_MODULE_NAME)-objs := mma7660.o + +default: + $(MAKE) -C $(KERNELDIR) SUBDIRS=$(PWD) modules + $(STRIP) --strip-debug $(MY_MODULE_NAME).ko + rm -rf *.o *~ core .depend .*.cmd *.mod.c .tmp_versions + +clean: + rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions *.order *.symvers + diff --git a/drivers/input/sensor/mma7660_gsensor/mma7660.c b/drivers/input/sensor/mma7660_gsensor/mma7660.c new file mode 100755 index 00000000..f0a0b1ee --- /dev/null +++ b/drivers/input/sensor/mma7660_gsensor/mma7660.c @@ -0,0 +1,1052 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mma7660.h" + + +//#define DEBUG 1 + +#undef dbg + +//#if 0 + #define dbg(fmt, args...) if (l_sensorconfig.isdbg) printk(KERN_ALERT "[%s]: " fmt, __FUNCTION__, ## args) + //#define dbg(fmt, args...) printk(KERN_ALERT "[%s]: " fmt, __FUNCTION__, ## args) + +#define klog(fmt, args...) printk(KERN_ALERT "[%s]: " fmt, __FUNCTION__, ## args) + + //#define dbg(format, arg...) printk(KERN_ALERT format, ## arg) + + +//#else +// #define dbg(format, arg...) +//#endif + +/////////////////////Macro constant +#define SENSOR_POLL_WAIT_TIME 1837 +#define MAX_FAILURE_COUNT 10 +#define MMA7660_ADDR 0x4C +#define LAND_PORT_MASK 0x1C +#define LAND_LEFT 0x1 +#define LAND_RIGHT 0x2 +#define PORT_INVERT 0x5 +#define PORT_NORMAL 0x6 + +#define LANDSCAPE_LOCATION 0 +#define PORTRAIT_LOCATION 1 + +#define SENSOR_UI_MODE 0 +#define SENSOR_GRAVITYGAME_MODE 1 + +#define UI_SAMPLE_RATE 0xFC + +#define GSENSOR_PROC_NAME "gsensor_config" +#define GSENSOR_MAJOR 161 +#define GSENSOR_NAME "mma7660" +#define GSENSOR_DRIVER_NAME "mma7660_drv" + + +#define sin30_1000 500 +#define cos30_1000 866 + +#define DISABLE 0 +#define ENABLE 1 + +////////////////////////the rate of g-sensor///////////////////////////////////////////// +#define SENSOR_DELAY_FASTEST 0 +#define SENSOR_DELAY_GAME 20 +#define SENSOR_DELAY_UI 60 +#define SENSOR_DELAY_NORMAL 200 + +#define FASTEST_MMA_AMSR 0 // 120 samples/sec +#define GAME_MMA_AMSR 1 // 1, (64, samples/sec) +#define UI_MMA_AMSR 3 // 2, 3,4, (16, 8,32 samples/sec) +#define NORMAL_MMA_AMSR 5 // 5, 6, 7 (4, 2, 1 samples/sec) + +///////////////////////////////////////////////////////////////////////// +static int xyz_g_table[64] = { +0,47,94,141,188,234,281,328, +375,422,469,516,563,609,656,703, +750,797,844,891,938,984,1031,1078, +1125,1172,1219,1266,1313,1359,1406,1453, +-1500,-1453,-1406,-1359,-1313,-1266,-1219,-1172, +-1125,-1078,-1031,-984,-938,-891,-844,-797, +-750,-703,-656,-609,-563,-516,-469,-422, +-375,-328,-281,-234,-188,-141,-94,-47 +}; + +static struct platform_device *this_pdev; + +static struct class* l_dev_class = NULL; +static struct device *l_clsdevice = NULL; + + + +struct mma7660_config +{ + int op; + int int_gpio; //0-3 + int xyz_axis[3][2]; // (axis,direction) + int rxyz_axis[3][2]; + int irq; + struct proc_dir_entry* sensor_proc; + int sensorlevel; + int shake_enable; // 1--enable shake, 0--disable shake + int manual_rotation; // 0--landance, 90--vertical + struct input_dev *input_dev; + //struct work_struct work; + struct delayed_work work; // for polling + struct workqueue_struct *queue; + int isdbg; // 0-- no debug log, 1--show debug log + int sensor_samp; // 1,2,4,8,16,32,64,120 + int sensor_enable; // 0 --> disable sensor, 1 --> enable sensor + int test_pass; + spinlock_t spinlock; + int pollcnt; // the counts of polling + int offset[3]; +}; + +static struct mma7660_config l_sensorconfig = { + .op = 0, + .int_gpio = 3, + .xyz_axis = { + {ABS_X, -1}, + {ABS_Y, 1}, + {ABS_Z, -1}, + }, + .irq = 6, + .int_gpio = 3, + .sensor_proc = NULL, + .sensorlevel = SENSOR_GRAVITYGAME_MODE, + .shake_enable = 0, // default enable shake + .isdbg = 0, + .sensor_samp = 10, // 4sample/second + .sensor_enable = 1, // enable sensor + .test_pass = 0, // for test program + .pollcnt = 0, // Don't report the x,y,z when the driver is loaded until 2~3 seconds + .offset = {0,0,0}, +}; + + +static struct timer_list l_polltimer; // for shaking +static int is_sensor_good = 0; // 1-- work well, 0 -- exception +struct work_struct poll_work; +static struct mutex sense_data_mutex; +static int revision = -1; +static int l_resumed = 0; // 1: suspend --> resume;2: suspend but not resumed; other values have no meaning + +//////////////////Macro function////////////////////////////////////////////////////// + +#define SET_MMA_SAMPLE(buf,samp) { \ + buf[0] = 0; \ + sensor_i2c_write(MMA7660_ADDR,7,buf,1); \ + buf[0] = samp; \ + sensor_i2c_write(MMA7660_ADDR,8,buf,1); \ + buf[0] = 0x01; \ + sensor_i2c_write(MMA7660_ADDR,7,buf,1); \ +} + +////////////////////////Function define///////////////////////////////////////////////////////// + +static unsigned int mma_sample2AMSR(unsigned int samp); + +////////////////////////Function implement///////////////////////////////////////////////// +// rate: 1,2,4,8,16,32,64,120 +static unsigned int sample_rate_2_memsec(unsigned int rate) +{ + return (1000/rate); +} + + + +static ssize_t gsensor_vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret = 0; + + sprintf(buf, "MMA7660_%#x\n", revision); + ret = strlen(buf) + 1; + + return ret; +} + +static DEVICE_ATTR(vendor, 0444, gsensor_vendor_show, NULL); + +static struct kobject *android_gsensor_kobj; +static int gsensor_sysfs_init(void) +{ + int ret ; + + android_gsensor_kobj = kobject_create_and_add("android_gsensor", NULL); + if (android_gsensor_kobj == NULL) { + printk(KERN_ERR + "mma7660 gsensor_sysfs_init:"\ + "subsystem_register failed\n"); + ret = -ENOMEM; + goto err; + } + + ret = sysfs_create_file(android_gsensor_kobj, &dev_attr_vendor.attr); + if (ret) { + printk(KERN_ERR + "mma7660 gsensor_sysfs_init:"\ + "sysfs_create_group failed\n"); + goto err4; + } + + return 0 ; +err4: + kobject_del(android_gsensor_kobj); +err: + return ret ; +} + +static int gsensor_sysfs_exit(void) +{ + sysfs_remove_file(android_gsensor_kobj, &dev_attr_vendor.attr); + kobject_del(android_gsensor_kobj); + return 0; +} + +//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); + +int sensor_i2c_write(unsigned int addr,unsigned int index,char *pdata,int len) +{ + /*struct i2c_msg msg[1]; + unsigned char buf[len+1]; + + //addr = (addr >> 1); + buf[0] = index; + memcpy(&buf[1],pdata,len); + msg[0].addr = addr; + msg[0].flags = 0 ; + msg[0].flags &= ~(I2C_M_RD); + msg[0].len = len+1; + msg[0].buf = buf; +//tmp sensor_i2c_do_xfer(msg,1); + if (wmt_i2c_xfer_continue_if_4(msg,1,0) <= 0) + { + klog("write error!\n"); + return -1; + }*/ + int ret; + ret = i2c_api_do_send(0, addr, index, pdata, len); + if (ret <= 0) { + klog("i2c_api_do_send error!\n"); + return -1; + } + +#ifdef DEBUG +{ + int i; + + printk("sensor_i2c_write(addr 0x%x,index 0x%x,len %d\n",addr,index,len); + for(i=0;i> 1); + memset(buf,0x55,len+1); + buf[0] = index; + buf[1] = 0x0; + + msg[0].addr = addr; + msg[0].flags = 0 ; + msg[0].flags &= ~(I2C_M_RD); + msg[0].len = 1; + msg[0].buf = buf; + + msg[1].addr = addr; + msg[1].flags = 0 ; + msg[1].flags |= (I2C_M_RD); + msg[1].len = len; + msg[1].buf = buf; + + //tmp sensor_i2c_do_xfer(msg, 2); + ret = wmt_i2c_xfer_continue_if_4(msg, 2,0); + if (ret < 0) + { + klog("read error!\n"); + return ret; + } + + memcpy(pdata,buf,len);*/ + int ret; + ret = i2c_api_do_recv(0, addr, index, pdata, len); + if (ret <= 0) { + klog("i2c_api_do_recv error!\n"); + return -1; + } +#ifdef DEBUG +{ + int i; + + printk("sensor_i2c_read(addr 0x%x,index 0x%x,len %d\n",addr,index,len); + for(i=0;i= 120) + { + return 0; + } + while (samp) + { + samp = samp >> 1; + i++; + } + amsr = 8 - i; + return amsr; +} + +static int mma_enable_disable(int enable) +{ + char buf[1]; + + // disable all interrupt of g-sensor + memset(buf, 0, sizeof(buf)); + if ((enable < 0) || (enable > 1)) + { + return -1; + } + buf[0] = 0; + sensor_i2c_write(MMA7660_ADDR,7,buf,1); + if (enable != 0) + { + buf[0] = (1 == l_sensorconfig.shake_enable) ? 0xF0:0x10; + } else { + buf[0] = 0; + } + sensor_i2c_write(MMA7660_ADDR,6,buf,1); + buf[0] = 0xf9; + sensor_i2c_write(MMA7660_ADDR,7,buf,1); + return 0; +} + +// To contol the g-sensor for UI +static int mmad_open(struct inode *inode, struct file *file) +{ + dbg("Open the g-sensor node...\n"); + return 0; +} + +static int mmad_release(struct inode *inode, struct file *file) +{ + dbg("Close the g-sensor node...\n"); + return 0; +} + +static /*int*/ long +mmad_ioctl(/*struct inode *inode,*/ struct file *file, unsigned int cmd, + unsigned long arg) +{ + void __user *argp = (void __user *)arg; + char rwbuf[5]; + short delay, enable; //amsr = -1; + unsigned int uval = 0; + int i = 0; + unsigned char rxData[3] = {0}; + int intBuf[3] = {0}; + dbg("g-sensor ioctr...\n"); + memset(rwbuf, 0, sizeof(rwbuf)); + switch (cmd) { + case ECS_IOCTL_APP_SET_DELAY: + // set the rate of g-sensor + if (copy_from_user(&delay, argp, sizeof(short))) + { + printk(KERN_ALERT "Can't get set delay!!!\n"); + return -EFAULT; + } + klog("Get delay=%d\n", delay); + //klog("before change sensor sample:%d...\n", l_sensorconfig.sensor_samp); + if ((delay >=0) && (delay < 20)) + { + delay = 20; + } else if (delay > 200) + { + delay = 200; + } + l_sensorconfig.sensor_samp = 1000/delay; + + break; + case ECS_IOCTL_APP_SET_AFLAG: + // enable/disable sensor + if (copy_from_user(&enable, argp, sizeof(short))) + { + printk(KERN_ERR "Can't get enable flag!!!\n"); + return -EFAULT; + } + klog("enable=%d\n",enable); + if ((enable >=0) && (enable <=1)) + { + dbg("driver: disable/enable(%d) gsensor.\n", enable); + + l_sensorconfig.sensor_enable = enable; + + } else { + printk(KERN_ERR "Wrong enable argument in %s !!!\n", __FUNCTION__); + return -EINVAL; + } + break; + case WMT_IOCTL_SENSOR_GET_DRVID: + uval = MMA7660_DRVID; + if (copy_to_user((unsigned int*)arg, &uval, sizeof(unsigned int))) + { + return -EFAULT; + } + dbg("mma7660_driver_id:%d\n",uval); + break; + case ECS_IOCTL_APP_READ_XYZ: + sensor_i2c_read(MMA7660_ADDR,0,rxData,3); + + for (i=0; i < 3; i++) + { + if (rxData[i]&0x40) break; + } + //if (l_sensorconfig.pollcnt >= 20) + if (3 == i) + { + intBuf[0] = xyz_g_table[rxData[l_sensorconfig.xyz_axis[0][0]]]*l_sensorconfig.xyz_axis[0][1]; + intBuf[1] = xyz_g_table[rxData[l_sensorconfig.xyz_axis[1][0]]]*l_sensorconfig.xyz_axis[1][1]; + intBuf[2] = xyz_g_table[rxData[l_sensorconfig.xyz_axis[2][0]]]*l_sensorconfig.xyz_axis[2][1]; + + if (copy_to_user((unsigned int*)arg, intBuf, sizeof(intBuf))) + { + return -EFAULT; + } + } + break; + } + + + + return 0; +} + + +static void mma_work_func(struct work_struct *work) +{ + struct mma7660_config *data; + unsigned char rxData[3]; + //unsigned char tiltval = 0; + int i = 0; + int x,y,z; + + dbg("enter...\n"); + data = dev_get_drvdata(&this_pdev->dev); + if (!l_sensorconfig.sensor_enable) + { + queue_delayed_work(l_sensorconfig.queue, &l_sensorconfig.work, msecs_to_jiffies(sample_rate_2_memsec(l_sensorconfig.sensor_samp))); + return; + } + mutex_lock(&sense_data_mutex); + is_sensor_good = 1; // for watchdog + l_sensorconfig.test_pass = 1; // for testing + + /*sensor_i2c_read(MMA7660_ADDR,3,rxData,1); + tiltval = rxData[0]; + if (tiltval & 0x80) { + if (1 == l_sensorconfig.shake_enable) { + printk(KERN_NOTICE "shake!!!\n"); + input_report_key(data->input_dev, KEY_NEXTSONG, 1); + input_report_key(data->input_dev, KEY_NEXTSONG, 0); + input_sync(data->input_dev); + + } + } else*/ { + sensor_i2c_read(MMA7660_ADDR,0,rxData,3); + /* dbg(KERN_DEBUG "Angle: x=%d, y=%d, z=%d\n", + xy_degree_table[rxData[0]], + xy_degree_table[rxData[1]], + z_degree_table[rxData[2]]); + dbg(KERN_DEBUG "G value: x=%d, y=%d, z=%d\n", + xyz_g_table[rxData[0]], + xyz_g_table[rxData[1]], + xyz_g_table[rxData[2]]); + */ + for (i=0; i < 3; i++) + { + if (rxData[i]&0x40) break; + } + //if (l_sensorconfig.pollcnt >= 20) + if (3 == i) + { + x = xyz_g_table[rxData[l_sensorconfig.xyz_axis[0][0]]]*l_sensorconfig.xyz_axis[0][1]; + y = xyz_g_table[rxData[l_sensorconfig.xyz_axis[1][0]]]*l_sensorconfig.xyz_axis[1][1]; + z = xyz_g_table[rxData[l_sensorconfig.xyz_axis[2][0]]]*l_sensorconfig.xyz_axis[2][1]; + input_report_abs(data->input_dev, ABS_X, x); + input_report_abs(data->input_dev, ABS_Y, y); + input_report_abs(data->input_dev, ABS_Z, z); + input_sync(data->input_dev); + dbg("gx=%x,gy=%x,gz=%x\n",x,y,z); + } + } + //gsensor_int_ctrl(ENABLE); + mutex_unlock(&sense_data_mutex); + queue_delayed_work(l_sensorconfig.queue, &l_sensorconfig.work, msecs_to_jiffies(sample_rate_2_memsec(l_sensorconfig.sensor_samp))); +} + +static int sensor_writeproc( struct file *file, + const char *buffer, + unsigned long count, + void *data ) +{ + + int inputval = -1; + int enable, sample = -1; + char tembuf[8]; + unsigned int amsr = 0; + int test = 0; + + mutex_lock(&sense_data_mutex); + // disable int + //gsensor_int_ctrl(DISABLE); + memset(tembuf, 0, sizeof(tembuf)); + // get sensor level and set sensor level + if (sscanf(buffer, "level=%d\n", &l_sensorconfig.sensorlevel)) + { + } else if (sscanf(buffer, "shakenable=%d\n", &l_sensorconfig.shake_enable)) + { + /* + regval[0] = 0; + sensor_i2c_write(MMA7660_ADDR,7,regval,1); // standard mode + sensor_i2c_read(MMA7660_ADDR,6,regval,1); + switch (l_sensorconfig.shake_enable) + { + case 0: // disable shake + regval[0] &= 0x1F; + sensor_i2c_write(MMA7660_ADDR,6, regval,1); + dbg("Shake disable!!\n"); + break; + case 1: // enable shake + regval[0] |= 0xE0; + sensor_i2c_write(MMA7660_ADDR,6, regval,1); + dbg("Shake enable!!\n"); + break; + }; + sensor_i2c_write(MMA7660_ADDR,7,&oldval,1); + */ + } /*else if (sscanf(buffer, "rotation=%d\n", &l_sensorconfig.manual_rotation)) + { + switch (l_sensorconfig.manual_rotation) + { + case 90: + // portrait + input_report_abs(mma7660data->input_dev, ABS_X, cos30_1000); + input_report_abs(mma7660data->input_dev, ABS_Y, 0); + input_report_abs(mma7660data->input_dev, ABS_Z, sin30_1000); + input_sync(mma7660data->input_dev); + break; + case 0: + // landscape + input_report_abs(mma7660data->input_dev, ABS_X, 0); + input_report_abs(mma7660data->input_dev, ABS_Y, cos30_1000); + input_report_abs(mma7660data->input_dev, ABS_Z, sin30_1000); + input_sync(mma7660data->input_dev); + break; + }; + }*/ else if (sscanf(buffer, "isdbg=%d\n", &l_sensorconfig.isdbg)) + { + // only set the dbg flag + } else if (sscanf(buffer, "init=%d\n", &inputval)) + { + mma7660_chip_init(); + dbg("Has reinit sensor !!!\n"); + } else if (sscanf(buffer, "samp=%d\n", &sample)) + { + if (sample > 0) + { + if (sample != l_sensorconfig.sensor_samp) + { + amsr = mma_sample2AMSR(sample); + SET_MMA_SAMPLE(tembuf, amsr); + klog("sample:%d ,amsr:%d \n", sample, amsr); + l_sensorconfig.sensor_samp = sample; + } + printk(KERN_ALERT "sensor samp=%d(amsr:%d) has been set.\n", sample, amsr); + } else { + printk(KERN_ALERT "Wrong sample argumnet of sensor.\n"); + } + } else if (sscanf(buffer, "enable=%d\n", &enable)) + { + if ((enable < 0) || (enable > 1)) + { + printk(KERN_ERR "The argument to enable/disable g-sensor should be 0 or 1 !!!\n"); + } else if (enable != l_sensorconfig.sensor_enable) + { + mma_enable_disable(enable); + l_sensorconfig.sensor_enable = enable; + } + } else if (sscanf(buffer, "sensor_test=%d\n", &test)) + { // for test begin + l_sensorconfig.test_pass = 0; + } else if (sscanf(buffer, "sensor_testend=%d\n", &test)) + { // Don nothing only to be compatible the before testing program + } + mutex_unlock(&sense_data_mutex); + return count; +} + +static int sensor_readproc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len = 0; + + len = sprintf(page, + "test_pass=%d\nisdbg=%d\nrate=%d\nenable=%d\n", + l_sensorconfig.test_pass, + l_sensorconfig.isdbg, + l_sensorconfig.sensor_samp, + l_sensorconfig.sensor_enable + ); + return len; +} + + + +static int mma7660_init_client(struct platform_device *pdev) +{ + struct mma7660_config *data; +// int ret; + + data = dev_get_drvdata(&pdev->dev); + mutex_init(&sense_data_mutex); + /*Only for polling, not interrupt*/ + l_sensorconfig.sensor_proc = create_proc_entry(GSENSOR_PROC_NAME, 0666, NULL/*&proc_root*/); + if (l_sensorconfig.sensor_proc != NULL) + { + l_sensorconfig.sensor_proc->write_proc = sensor_writeproc; + l_sensorconfig.sensor_proc->read_proc = sensor_readproc; + } + + + return 0; + +//err: +// return ret; +} + +static struct file_operations mmad_fops = { + .owner = THIS_MODULE, + .open = mmad_open, + .release = mmad_release, + .unlocked_ioctl = mmad_ioctl, +}; + + +static struct miscdevice mmad_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "sensor_ctrl", + .fops = &mmad_fops, +}; + +static int mma7660_probe( + struct platform_device *pdev) +{ + int err; + + this_pdev = pdev; + l_sensorconfig.queue = create_singlethread_workqueue("sensor-intterupt-handle"); + INIT_DELAYED_WORK(&l_sensorconfig.work, mma_work_func); + + l_sensorconfig.input_dev = input_allocate_device(); + if (!l_sensorconfig.input_dev) { + err = -ENOMEM; + printk(KERN_ERR + "mma7660_probe: Failed to allocate input device\n"); + goto exit_input_dev_alloc_failed; + } + l_sensorconfig.input_dev->evbit[0] = BIT(EV_ABS) | BIT_MASK(EV_KEY); + set_bit(KEY_NEXTSONG, l_sensorconfig.input_dev->keybit); + + /* x-axis acceleration */ + input_set_abs_params(l_sensorconfig.input_dev, ABS_X, -65532, 65532, 0, 0); + /* y-axis acceleration */ + input_set_abs_params(l_sensorconfig.input_dev, ABS_Y, -65532, 65532, 0, 0); + /* z-axis acceleration */ + input_set_abs_params(l_sensorconfig.input_dev, ABS_Z, -65532, 65532, 0, 0); + + l_sensorconfig.input_dev->name = "g-sensor"; + + err = input_register_device(l_sensorconfig.input_dev); + + if (err) { + printk(KERN_ERR + "mma7660_probe: Unable to register input device: %s\n", + l_sensorconfig.input_dev->name); + goto exit_input_register_device_failed; + } + + err = misc_register(&mmad_device); + if (err) { + printk(KERN_ERR + "mma7660_probe: mmad_device register failed\n"); + goto exit_misc_device_register_failed; + } + + dev_set_drvdata(&pdev->dev, &l_sensorconfig); + mma7660_chip_init(); + mma7660_init_client(pdev); + gsensor_sysfs_init(); + + // satrt the polling work + l_sensorconfig.sensor_samp = 10; + queue_delayed_work(l_sensorconfig.queue, &l_sensorconfig.work, msecs_to_jiffies(sample_rate_2_memsec(l_sensorconfig.sensor_samp))); + return 0; + +exit_misc_device_register_failed: +exit_input_register_device_failed: + input_free_device(l_sensorconfig.input_dev); +//exit_alloc_data_failed: +exit_input_dev_alloc_failed: +//exit_check_functionality_failed: + return err; +} + +static int mma7660_remove(struct platform_device *pdev) +{ + if (NULL != l_sensorconfig.queue) + { + cancel_delayed_work_sync(&l_sensorconfig.work); + flush_workqueue(l_sensorconfig.queue); + destroy_workqueue(l_sensorconfig.queue); + l_sensorconfig.queue = NULL; + } + gsensor_sysfs_exit(); + misc_deregister(&mmad_device); + input_unregister_device(l_sensorconfig.input_dev); + if (l_sensorconfig.sensor_proc != NULL) + { + remove_proc_entry(GSENSOR_PROC_NAME, NULL); + l_sensorconfig.sensor_proc = NULL; + } + //free_irq(l_sensorconfig.irq, &l_sensorconfig); + return 0; +} + +static int mma7660_suspend(struct platform_device *pdev, pm_message_t state) +{ + //gsensor_int_ctrl(DISABLE); + cancel_delayed_work_sync(&l_sensorconfig.work); + del_timer(&l_polltimer); + dbg("...ok\n"); + //l_resumed = 2; + + return 0; +} + +static int mma7660_resume(struct platform_device *pdev) +{ + + //mma7660_chip_init(); + //gsensor_int_ctrl(ENABLE); + //mma_enable_disable(1); + l_resumed = 1; + mod_timer(&l_polltimer, jiffies + msecs_to_jiffies(SENSOR_POLL_WAIT_TIME)); + dbg("...ok\n"); + + return 0; +} + +static void mma7660_platform_release(struct device *device) +{ + return; +} + +static void mma7660_shutdown(struct platform_device *pdev) +{ + + flush_delayed_work_sync(&l_sensorconfig.work); + cancel_delayed_work_sync(&l_sensorconfig.work); + +} + +static struct platform_device mma7660_device = { + .name = "mma7660", + .id = 0, + .dev = { + .release = mma7660_platform_release, + }, +}; + +static struct platform_driver mma7660_driver = { + .probe = mma7660_probe, + .remove = mma7660_remove, + .suspend = mma7660_suspend, + .resume = mma7660_resume, + .shutdown = mma7660_shutdown, + .driver = { + .name = "mma7660", + }, +}; + +/* + * Brief: + * Get the configure of sensor from u-boot. + * Input: + * no use. + * Output: + * no use. + * Return: + * 0--success, -1--error. + * History: + * Created by HangYan on 2010-4-19 + * Author: + * Hang Yan in ShenZhen. + */ +extern int wmt_getsyspara(char *varname, unsigned char *varval, int *varlen); +static int get_axisset(void* param) +{ + char varbuf[64]; + int n; + int varlen; + + memset(varbuf, 0, sizeof(varbuf)); + varlen = sizeof(varbuf); + if (wmt_getsyspara("wmt.io.mma7660gsensor", varbuf, &varlen)) { + printk(KERN_DEBUG "Can't get gsensor config in u-boot!!!!\n"); + return -1; + } else { + n = sscanf(varbuf, "%d:%d:%d:%d:%d:%d:%d:%d:%d:%d", + &l_sensorconfig.op, + &(l_sensorconfig.xyz_axis[0][0]), + &(l_sensorconfig.xyz_axis[0][1]), + &(l_sensorconfig.xyz_axis[1][0]), + &(l_sensorconfig.xyz_axis[1][1]), + &(l_sensorconfig.xyz_axis[2][0]), + &(l_sensorconfig.xyz_axis[2][1]), + &(l_sensorconfig.offset[0]), + &(l_sensorconfig.offset[1]), + &(l_sensorconfig.offset[2])); + if (n != 10) { + printk(KERN_ERR "gsensor format is error in u-boot!!!\n"); + return -1; + } + + dbg("get the sensor config: %d:%d:%d:%d:%d:%d:%d:%d:%d:%d\n", + l_sensorconfig.op, + l_sensorconfig.xyz_axis[0][0], + l_sensorconfig.xyz_axis[0][1], + l_sensorconfig.xyz_axis[1][0], + l_sensorconfig.xyz_axis[1][1], + l_sensorconfig.xyz_axis[2][0], + l_sensorconfig.xyz_axis[2][1], + l_sensorconfig.offset[0], + l_sensorconfig.offset[1], + l_sensorconfig.offset[2] + ); + } + return 0; +} + + +// Add one timer to reinit sensor every 5s +static void sensor_polltimer_timeout(unsigned long timeout) +{ + schedule_work(&poll_work); +} + +static void poll_work_func(struct work_struct *work) +{ + char rxData[1]; + + //mutex_lock(&sense_data_mutex); + if (l_sensorconfig.pollcnt != 20) + { + l_sensorconfig.pollcnt++; + } + dbg("read to work!\n"); + //spin_lock(&l_sensorconfig.spinlock); + mutex_lock(&sense_data_mutex); + if (1 == l_resumed) + { + mma7660_chip_init(); + //gsensor_int_ctrl(ENABLE); + //mma_enable_disable(1); + dbg("reinit for resume...\n"); + l_resumed = 0; + //spin_unlock(&l_sensorconfig.spinlock); + mutex_unlock(&sense_data_mutex); + queue_delayed_work(l_sensorconfig.queue, &l_sensorconfig.work, msecs_to_jiffies(sample_rate_2_memsec(l_sensorconfig.sensor_samp))); + + return; + } + //spin_unlock(&l_sensorconfig.spinlock); + mutex_unlock(&sense_data_mutex); + if (!is_sensor_good) + { + //mma7660_chip_init(); + // if ic is blocked then wake g-sensor + sensor_i2c_read(MMA7660_ADDR,3,rxData,1); + } else { + is_sensor_good = 0; + } + + //mutex_unlock(&sense_data_mutex); + mod_timer(&l_polltimer, jiffies + msecs_to_jiffies(SENSOR_POLL_WAIT_TIME)); + +} + +static int mma7660_open(struct inode *inode, struct file *file) +{ + return 0; +} + +static int mma7660_release(struct inode *inode, struct file *file) +{ + return 0; +} + + +static struct file_operations mma7660_fops = { + .owner = THIS_MODULE, + .open = mma7660_open, + .release = mma7660_release, +}; + +static int is_mma7660(void) +{ + char txData[2]; + int i = 0; + + txData[1] = 0; + for (i = 0; i < 3; i++) + { + if(sensor_i2c_write(MMA7660_ADDR,7,txData,1) == 0) + { + return 0; + } + } + return -1; +} + +static int __init mma7660_init(void) +{ + int ret = 0; + + if (is_mma7660()) + { + printk(KERN_ERR "Can't find mma7660!!\n"); + return -1; + } + ret = get_axisset(NULL); // get gsensor config from u-boot + if (ret < 0) + { + printk("<<<<<%s user choose to no sensor chip!\n", __func__); + return ret; + } + /*if ((ret != 0) || !l_sensorconfig.op) + return -EINVAL;*/ + + + printk(KERN_INFO "mma7660fc g-sensor driver init\n"); + + spin_lock_init(&l_sensorconfig.spinlock); + INIT_WORK(&poll_work, poll_work_func); + // Create device node + if (register_chrdev (GSENSOR_MAJOR, GSENSOR_NAME, &mma7660_fops)) { + printk (KERN_ERR "unable to get major %d\n", GSENSOR_MAJOR); + return -EIO; + } + + l_dev_class = class_create(THIS_MODULE, GSENSOR_NAME); + if (IS_ERR(l_dev_class)){ + ret = PTR_ERR(l_dev_class); + printk(KERN_ERR "Can't class_create gsensor device !!\n"); + return ret; + } + l_clsdevice = device_create(l_dev_class, NULL, MKDEV(GSENSOR_MAJOR, 0), NULL, GSENSOR_NAME); + if (IS_ERR(l_clsdevice)){ + ret = PTR_ERR(l_clsdevice); + printk(KERN_ERR "Failed to create device %s !!!",GSENSOR_NAME); + return ret; + } + + if((ret = platform_device_register(&mma7660_device))) + { + printk(KERN_ERR "%s Can't register mma7660 platform devcie!!!\n", __FUNCTION__); + return ret; + } + if ((ret = platform_driver_register(&mma7660_driver)) != 0) + { + printk(KERN_ERR "%s Can't register mma7660 platform driver!!!\n", __FUNCTION__); + return ret; + } + + setup_timer(&l_polltimer, sensor_polltimer_timeout, 0); + mod_timer(&l_polltimer, jiffies + msecs_to_jiffies(SENSOR_POLL_WAIT_TIME)); + is_sensor_good = 1; + + return 0; +} + +static void __exit mma7660_exit(void) +{ + del_timer(&l_polltimer); + platform_driver_unregister(&mma7660_driver); + platform_device_unregister(&mma7660_device); + device_destroy(l_dev_class, MKDEV(GSENSOR_MAJOR, 0)); + unregister_chrdev(GSENSOR_MAJOR, GSENSOR_NAME); + class_destroy(l_dev_class); + +} + +module_init(mma7660_init); +module_exit(mma7660_exit); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/sensor/mma7660_gsensor/mma7660.h b/drivers/input/sensor/mma7660_gsensor/mma7660.h new file mode 100755 index 00000000..db6c06c0 --- /dev/null +++ b/drivers/input/sensor/mma7660_gsensor/mma7660.h @@ -0,0 +1,106 @@ +/* + * Definitions for akm8976 compass chip. + */ +#ifndef AKM8976_H +#define AKM8976_H + +#include + +/* Compass device dependent definition */ +#define AKECS_MODE_MEASURE 0x00 /* Starts measurement. Please use AKECS_MODE_MEASURE_SNG */ + /* or AKECS_MODE_MEASURE_SEQ instead of this. */ +#define AKECS_MODE_PFFD 0x01 /* Start pedometer and free fall detect. */ +#define AKECS_MODE_E2P_READ 0x02 /* E2P access mode (read). */ +#define AKECS_MODE_POWERDOWN 0x03 /* Power down mode */ + +#define AKECS_MODE_MEASURE_SNG 0x10 /* Starts single measurement */ +#define AKECS_MODE_MEASURE_SEQ 0x11 /* Starts sequential measurement */ + +/* Default register settings */ +#define CSPEC_AINT 0x01 /* Amplification for acceleration sensor */ +#define CSPEC_SNG_NUM 0x01 /* Single measurement mode */ +#define CSPEC_SEQ_NUM 0x02 /* Sequential measurement mode */ +#define CSPEC_SFRQ_32 0x00 /* Measurement frequency: 32Hz */ +#define CSPEC_SFRQ_64 0x01 /* Measurement frequency: 64Hz */ +#define CSPEC_MCS 0x07 /* Clock frequency */ +#define CSPEC_MKS 0x01 /* Clock type: CMOS level */ +#define CSPEC_INTEN 0x01 /* Interruption pin enable: Enable */ + +#define RBUFF_SIZE 31 /* Rx buffer size */ +#define MAX_CALI_SIZE 0x1000U /* calibration buffer size */ + +/* AK8976A register address */ +#define AKECS_REG_ST 0xC0 +#define AKECS_REG_TMPS 0xC1 +#define AKECS_REG_MS1 0xE0 +#define AKECS_REG_MS2 0xE1 +#define AKECS_REG_MS3 0xE2 + +#define AKMIO 0xA1 + +/* IOCTLs for AKM library */ +#define ECS_IOCTL_RESET _IO(AKMIO, 0x04) +#define ECS_IOCTL_INT_STATUS _IO(AKMIO, 0x05) +#define ECS_IOCTL_FFD_STATUS _IO(AKMIO, 0x06) +#define ECS_IOCTL_SET_MODE _IOW(AKMIO, 0x07, short) +#define ECS_IOCTL_GETDATA _IOR(AKMIO, 0x08, char[RBUFF_SIZE+1]) +#define ECS_IOCTL_GET_NUMFRQ _IOR(AKMIO, 0x09, char[2]) +#define ECS_IOCTL_SET_PERST _IO(AKMIO, 0x0A) +#define ECS_IOCTL_SET_G0RST _IO(AKMIO, 0x0B) +#define ECS_IOCTL_SET_YPR _IOW(AKMIO, 0x0C, short[12]) +#define ECS_IOCTL_GET_OPEN_STATUS _IOR(AKMIO, 0x0D, int) +#define ECS_IOCTL_GET_CLOSE_STATUS _IOR(AKMIO, 0x0E, int) +#define ECS_IOCTL_GET_CALI_DATA _IOR(AKMIO, 0x0F, char[MAX_CALI_SIZE]) +#define ECS_IOCTL_GET_DELAY _IOR(AKMIO, 0x30, short) + +/* IOCTLs for APPs */ +#define ECS_IOCTL_APP_SET_MODE _IOW(AKMIO, 0x10, short) +#define ECS_IOCTL_APP_SET_MFLAG _IOW(AKMIO, 0x11, short) +#define ECS_IOCTL_APP_GET_MFLAG _IOW(AKMIO, 0x12, short) +#define ECS_IOCTL_APP_GET_AFLAG _IOR(AKMIO, 0x14, short) +#define ECS_IOCTL_APP_SET_TFLAG _IOR(AKMIO, 0x15, short) +#define ECS_IOCTL_APP_GET_TFLAG _IOR(AKMIO, 0x16, short) +#define ECS_IOCTL_APP_RESET_PEDOMETER _IO(AKMIO, 0x17) +#define ECS_IOCTL_APP_GET_DELAY ECS_IOCTL_GET_DELAY +#define ECS_IOCTL_APP_SET_MVFLAG _IOW(AKMIO, 0x19, short) /* Set raw magnetic vector flag */ +#define ECS_IOCTL_APP_GET_MVFLAG _IOR(AKMIO, 0x1A, short) /* Get raw magnetic vector flag */ + +/* IOCTLs for pedometer */ +#define ECS_IOCTL_SET_STEP_CNT _IOW(AKMIO, 0x20, short) +#define SENSOR_DATA_SIZE 3 +#define WMTGSENSOR_IOCTL_MAGIC 0x09 +#define ECS_IOCTL_APP_SET_AFLAG _IOW(WMTGSENSOR_IOCTL_MAGIC, 0x02, short) +#define ECS_IOCTL_APP_SET_DELAY _IOW(WMTGSENSOR_IOCTL_MAGIC, 0x03, short) +#define WMT_IOCTL_SENSOR_GET_DRVID _IOW(WMTGSENSOR_IOCTL_MAGIC, 0x04, unsigned int) +#define ECS_IOCTL_APP_READ_XYZ _IOW(WMTGSENSOR_IOCTL_MAGIC, 0x05, int[SENSOR_DATA_SIZE]) + +#define MMA7660_DRVID 0 + + + +/* Default GPIO setting */ +#define ECS_RST 146 /*MISC4, bit2 */ +#define ECS_CLK_ON 155 /*MISC5, bit3 */ +#define ECS_INTR 161 /*INT2, bit1 */ + +/* MISC */ +#define MMA7660_ADDR 0x4C +#define SENSOR_UI_MODE 0 +#define SENSOR_GRAVITYGAME_MODE 1 +#define UI_SAMPLE_RATE 0xFC +#define GSENSOR_PROC_NAME "gsensor_config" +#define sin30_1000 500 +#define cos30_1000 866 +#define DISABLE 0 +#define ENABLE 1 + +struct mma7660_platform_data { + int reset; + int clk_on; + int intr; +}; + +extern char *get_mma_cal_ram(void); + +#endif + diff --git a/drivers/input/sensor/mma8452q_gsensor/Makefile b/drivers/input/sensor/mma8452q_gsensor/Makefile new file mode 100755 index 00000000..4dcceb42 --- /dev/null +++ b/drivers/input/sensor/mma8452q_gsensor/Makefile @@ -0,0 +1,34 @@ +KERNELDIR=../../../../ +#KERNELDIR=/home/hangyan/android8850/kernel/ANDROID_3.0.8 +CROSS = arm_1103_le- +CC= $(CROSS)gcc +LD= $(CROSS)ld +STRIP = $(CROSS)strip + +DEBUG = n + +# Add your debugging flag (or not) to EXTRA_CFLAGS +ifeq ($(DEBUG),y) +# DEBFLAGS = -O -g -DSCULL_DEBUG # "-O" is needed to expand inlines +DEBFLAGS = -O0 -g -DSCULL_DEBUG # "-O" is needed to expand inlines + +else + DEBFLAGS = -O2 -Wall +endif + +EXTRA_CFLAGS += $(DEBFLAGS) + + +MY_MODULE_NAME=s_wmt_gsensor_mma8452q + +obj-m := $(MY_MODULE_NAME).o +$(MY_MODULE_NAME)-objs := mma8x5x.o + +default: + $(MAKE) -C $(KERNELDIR) SUBDIRS=$(PWD) modules + $(STRIP) --strip-debug $(MY_MODULE_NAME).ko + rm -rf *.o *~ core .depend .*.cmd *.mod.c .tmp_versions + +clean: + rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions *.order *.symvers + diff --git a/drivers/input/sensor/mma8452q_gsensor/mma8x5x.c b/drivers/input/sensor/mma8452q_gsensor/mma8x5x.c new file mode 100755 index 00000000..443d3b18 --- /dev/null +++ b/drivers/input/sensor/mma8452q_gsensor/mma8x5x.c @@ -0,0 +1,982 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mma8x5x.h" //"mma8452q.h" +#include "../sensor.h" + +//#define DEBUG 1 + +#undef dbg + +//#if 0 + #define dbg(fmt, args...) if (l_sensorconfig.isdbg) printk(KERN_ALERT "[%s]: " fmt, __FUNCTION__, ## args) + //#define dbg(fmt, args...) printk(KERN_ALERT "[%s]: " fmt, __FUNCTION__, ## args) + +#define klog(fmt, args...) printk(KERN_ALERT "[%s]: " fmt, __FUNCTION__, ## args) + + //#define dbg(format, arg...) printk(KERN_ALERT format, ## arg) + + +//#else +// #define dbg(format, arg...) +//#endif + +/////////////////////Macro constant +#define SENSOR_POLL_WAIT_TIME 1837 +#define MAX_FAILURE_COUNT 10 +#define MMA8452_ADDR 0x1C //slave 0x1D??high +#define LAND_PORT_MASK 0x1C +#define LAND_LEFT 0x1 +#define LAND_RIGHT 0x2 +#define PORT_INVERT 0x5 +#define PORT_NORMAL 0x6 + +#define LANDSCAPE_LOCATION 0 +#define PORTRAIT_LOCATION 1 + +#define SENSOR_UI_MODE 0 +#define SENSOR_GRAVITYGAME_MODE 1 + +#define UI_SAMPLE_RATE 0xFC + +#define GSENSOR_PROC_NAME "gsensor_config" +#define GSENSOR_MAJOR 161 +#define GSENSOR_NAME "mma8452q" +#define GSENSOR_DRIVER_NAME "mma8452q_drv" + + +#define sin30_1000 500 +#define cos30_1000 866 + +#define DISABLE 0 +#define ENABLE 1 + +////////////////////////the rate of g-sensor///////////////////////////////////////////// +#define SENSOR_DELAY_FASTEST 0 +#define SENSOR_DELAY_GAME 20 +#define SENSOR_DELAY_UI 60 +#define SENSOR_DELAY_NORMAL 200 + +#define FASTEST_MMA_AMSR 0 // 120 samples/sec +#define GAME_MMA_AMSR 1 // 1, (64, samples/sec) +#define UI_MMA_AMSR 3 // 2, 3,4, (16, 8,32 samples/sec) +#define NORMAL_MMA_AMSR 5 // 5, 6, 7 (4, 2, 1 samples/sec) + +#define MMA8451_ID 0x1A +#define MMA8452_ID 0x2A +#define MMA8453_ID 0x3A +#define MMA8652_ID 0x4A +#define MMA8653_ID 0x5A +#define MODE_CHANGE_DELAY_MS 100 +/* register enum for mma8x5x registers */ +enum { + MMA8X5X_STATUS = 0x00, + MMA8X5X_OUT_X_MSB, + MMA8X5X_OUT_X_LSB, + MMA8X5X_OUT_Y_MSB, + MMA8X5X_OUT_Y_LSB, + MMA8X5X_OUT_Z_MSB, + MMA8X5X_OUT_Z_LSB, + + MMA8X5X_F_SETUP = 0x09, + MMA8X5X_TRIG_CFG, + MMA8X5X_SYSMOD, + MMA8X5X_INT_SOURCE, + MMA8X5X_WHO_AM_I, + MMA8X5X_XYZ_DATA_CFG, + MMA8X5X_HP_FILTER_CUTOFF, + + MMA8X5X_PL_STATUS, + MMA8X5X_PL_CFG, + MMA8X5X_PL_COUNT, + MMA8X5X_PL_BF_ZCOMP, + MMA8X5X_P_L_THS_REG, + + MMA8X5X_FF_MT_CFG, + MMA8X5X_FF_MT_SRC, + MMA8X5X_FF_MT_THS, + MMA8X5X_FF_MT_COUNT, + + MMA8X5X_TRANSIENT_CFG = 0x1D, + MMA8X5X_TRANSIENT_SRC, + MMA8X5X_TRANSIENT_THS, + MMA8X5X_TRANSIENT_COUNT, + + MMA8X5X_PULSE_CFG, + MMA8X5X_PULSE_SRC, + MMA8X5X_PULSE_THSX, + MMA8X5X_PULSE_THSY, + MMA8X5X_PULSE_THSZ, + MMA8X5X_PULSE_TMLT, + MMA8X5X_PULSE_LTCY, + MMA8X5X_PULSE_WIND, + + MMA8X5X_ASLP_COUNT, + MMA8X5X_CTRL_REG1, + MMA8X5X_CTRL_REG2, + MMA8X5X_CTRL_REG3, + MMA8X5X_CTRL_REG4, + MMA8X5X_CTRL_REG5, + + MMA8X5X_OFF_X, + MMA8X5X_OFF_Y, + MMA8X5X_OFF_Z, + + MMA8X5X_REG_END, +}; + +enum { + MODE_2G = 0, + MODE_4G, + MODE_8G, +}; + +static int mma8x5x_chip_id[] ={ + MMA8451_ID, + MMA8452_ID, + MMA8453_ID, + MMA8652_ID, + MMA8653_ID, +}; + +static struct i2c_client *this_client = NULL; +///////////////////////////////////////////////////////////////////////// + +static struct platform_device *this_pdev; + +static struct class* l_dev_class = NULL; +static struct device *l_clsdevice = NULL; + + + +struct mma8452q_config +{ + int op; + int int_gpio; //0-3 + int xyz_axis[3][2]; // (axis,direction) + int rxyz_axis[3][2]; + int irq; + struct proc_dir_entry* sensor_proc; + int sensorlevel; + int shake_enable; // 1--enable shake, 0--disable shake + int manual_rotation; // 0--landance, 90--vertical + struct input_dev *input_dev; + //struct work_struct work; + struct delayed_work work; // for polling + struct workqueue_struct *queue; + int isdbg; // 0-- no debug log, 1--show debug log + int sensor_samp; // 1,2,4,8,16,32,64,120 + int sensor_enable; // 0 --> disable sensor, 1 --> enable sensor + int test_pass; + spinlock_t spinlock; + int pollcnt; // the counts of polling + int offset[3]; +}; + +static struct mma8452q_config l_sensorconfig = { + .op = 0, + .int_gpio = 3, + .xyz_axis = { + {ABS_X, -1}, + {ABS_Y, 1}, + {ABS_Z, -1}, + }, + .irq = 6, + .int_gpio = 3, + .sensor_proc = NULL, + .sensorlevel = SENSOR_GRAVITYGAME_MODE, + .shake_enable = 0, // default enable shake + .isdbg = 0, + .sensor_samp = 10, // 4sample/second + .sensor_enable = 1, // enable sensor + .test_pass = 0, // for test program + .pollcnt = 0, // Don't report the x,y,z when the driver is loaded until 2~3 seconds + .offset = {0,0,0}, +}; + + + +struct work_struct poll_work; +static struct mutex sense_data_mutex; +static int revision = -1; +static int l_resumed = 0; // 1: suspend --> resume;2: suspend but not resumed; other values have no meaning + +//////////////////Macro function////////////////////////////////////////////////////// + +#define SET_MMA_SAMPLE(buf,samp) { \ + buf[0] = 0; \ + sensor_i2c_write(/*MMA8452_ADDR,*/7,buf,1); \ + buf[0] = samp; \ + sensor_i2c_write(/*MMA8452_ADDR,*/8,buf,1); \ + buf[0] = 0x01; \ + sensor_i2c_write(/*MMA8452_ADDR,*/7,buf,1); \ +} + +////////////////////////Function define///////////////////////////////////////////////////////// + +static unsigned int mma_sample2AMSR(unsigned int samp); + +////////////////////////Function implement///////////////////////////////////////////////// +// rate: 1,2,4,8,16,32,64,120 +static unsigned int sample_rate_2_memsec(unsigned int rate) +{ + return (1000/rate); +} + + + +static ssize_t gsensor_vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret = 0; + + sprintf(buf, "MMA8452_%#x\n", revision); + ret = strlen(buf) + 1; + + return ret; +} + +static DEVICE_ATTR(vendor, 0444, gsensor_vendor_show, NULL); + +static struct kobject *android_gsensor_kobj; +static int gsensor_sysfs_init(void) +{ + int ret ; + + android_gsensor_kobj = kobject_create_and_add("android_gsensor", NULL); + if (android_gsensor_kobj == NULL) { + printk(KERN_ERR + "mma8452q gsensor_sysfs_init:"\ + "subsystem_register failed\n"); + ret = -ENOMEM; + goto err; + } + + ret = sysfs_create_file(android_gsensor_kobj, &dev_attr_vendor.attr); + if (ret) { + printk(KERN_ERR + "mma8452q gsensor_sysfs_init:"\ + "sysfs_create_group failed\n"); + goto err4; + } + + return 0 ; +err4: + kobject_del(android_gsensor_kobj); +err: + return ret ; +} + +static int gsensor_sysfs_exit(void) +{ + sysfs_remove_file(android_gsensor_kobj, &dev_attr_vendor.attr); + kobject_del(android_gsensor_kobj); + return 0; +} + +//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); + +int sensor_i2c_write(/*unsigned int addr,*/unsigned int index,char *pdata,int len) +{ + + char wrData[12] = {0}; + struct i2c_client *client = this_client; + + struct i2c_msg msgs = + {.addr = client->addr, .flags = 0, .len = len+1, .buf = wrData,}; + + + if (!client || (!pdata)) + { + printk("%s NULL client!\n", __FUNCTION__); + return -EIO; + } + + wrData[0] = index; + strncpy(&wrData[1], pdata, len); + + if (i2c_transfer(client->adapter, &msgs, 1) < 0) { + printk( "%s: transfer failed.", __func__); + return -EIO; + } + + return 0; +} /* End of sensor_i2c_write */ + +int sensor_i2c_read(/*unsigned int addr,*/unsigned int index,char *pdata,int len) +{ + char rdData[2] = {0}; + struct i2c_client *client = this_client; + + struct i2c_msg msgs[2] = + { + {.addr = client->addr, .flags = 0|I2C_M_NOSTART, .len = 1, .buf = rdData,}, + {.addr = client->addr, .flags = I2C_M_RD, .len = len, .buf = pdata,}, + }; + rdData[0] = index; + if (!client || (!pdata)) + { + printk("%s NULL client!\n", __FUNCTION__); + return -EIO; + } + if (i2c_transfer(client->adapter, msgs, 2) < 0) { + printk( "%s: transfer failed.", __func__); + return -EIO; + } + + return 0;//rdData[0]; i2c read ok!! + +} /* End of sensor_i2c_read */ + +//****************add for mma8452q****************************** + +static int mma8452q_chip_init(void) +{ + char txData[1] = {0}; + + int result; + + txData[0] = 0x1; //active mode + result = sensor_i2c_write(/*MMA8452_ADDR,*/MMA8X5X_CTRL_REG1,txData,1); + if (result < 0) + goto out; + + txData[0] = MODE_2G; + result = sensor_i2c_write(/*MMA8452_ADDR,*/MMA8X5X_XYZ_DATA_CFG,txData,1); + if (result < 0) + goto out; + + txData[0] = 0x1; //wake mode + result = sensor_i2c_write(/*MMA8452_ADDR,*/MMA8X5X_SYSMOD,txData,1); + if (result < 0) + goto out; + + msleep(MODE_CHANGE_DELAY_MS); + return 0; +out: + + return result; + +} + +static unsigned int mma_sample2AMSR(unsigned int samp) +{ + int i = 0; + unsigned int amsr; + + if (samp >= 120) + { + return 0; + } + while (samp) + { + samp = samp >> 1; + i++; + } + amsr = 8 - i; + return amsr; +} + +static int mma_enable_disable(int enable) +{ + char buf[1]; + + // disable all interrupt of g-sensor + memset(buf, 0, sizeof(buf)); + if ((enable < 0) || (enable > 1)) + { + return -1; + } + buf[0] = 0; + sensor_i2c_write(/*MMA8452_ADDR,*/7,buf,1); + if (enable != 0) + { + buf[0] = (1 == l_sensorconfig.shake_enable) ? 0xF0:0x10; + } else { + buf[0] = 0; + } + sensor_i2c_write(/*MMA8452_ADDR,*/6,buf,1); + buf[0] = 0xf9; + sensor_i2c_write(/*MMA8452_ADDR,*/7,buf,1); + return 0; +} + +// To contol the g-sensor for UI +static int mmad_open(struct inode *inode, struct file *file) +{ + dbg("Open the g-sensor node...\n"); + return 0; +} + +static int mmad_release(struct inode *inode, struct file *file) +{ + dbg("Close the g-sensor node...\n"); + return 0; +} + +static long +mmad_ioctl(/*struct inode *inode,*/ struct file *file, unsigned int cmd, + unsigned long arg) +{ + void __user *argp = (void __user *)arg; + char rwbuf[5]; + short delay, enable; //amsr = -1; + unsigned int uval = 0; + + dbg("g-sensor ioctr...\n"); + memset(rwbuf, 0, sizeof(rwbuf)); + switch (cmd) { + case ECS_IOCTL_APP_SET_DELAY: + // set the rate of g-sensor + if (copy_from_user(&delay, argp, sizeof(short))) + { + printk(KERN_ALERT "Can't get set delay!!!\n"); + return -EFAULT; + } + klog("Get delay=%d\n", delay); + //klog("before change sensor sample:%d...\n", l_sensorconfig.sensor_samp); + if ((delay >=0) && (delay < 20)) + { + delay = 20; + } else if (delay > 200) + { + delay = 200; + } + l_sensorconfig.sensor_samp = 1000/delay; + + break; + case ECS_IOCTL_APP_SET_AFLAG: + // enable/disable sensor + if (copy_from_user(&enable, argp, sizeof(short))) + { + printk(KERN_ERR "Can't get enable flag!!!\n"); + return -EFAULT; + } + klog("enable=%d\n",enable); + if ((enable >=0) && (enable <=1)) + { + dbg("driver: disable/enable(%d) gsensor.\n", enable); + + l_sensorconfig.sensor_enable = enable; + + } else { + printk(KERN_ERR "Wrong enable argument in %s !!!\n", __FUNCTION__); + return -EINVAL; + } + break; + case WMT_IOCTL_SENSOR_GET_DRVID: + uval = MMA8452Q_DRVID; + if (copy_to_user((unsigned int*)arg, &uval, sizeof(unsigned int))) + { + return -EFAULT; + } + dbg("mma8452q_driver_id:%d\n",uval); + break; + case WMT_IOCTL_SENOR_GET_RESOLUTION: + + uval = (16<<8) | 4; // 16bit:4g 0xxx xx //mma8452Q + if (copy_to_user((unsigned int *)arg, &uval, sizeof(unsigned int))) + { + return -EFAULT; + } + printk("<<<<<<dev); + if (!l_sensorconfig.sensor_enable) + { + queue_delayed_work(l_sensorconfig.queue, &l_sensorconfig.work, msecs_to_jiffies(sample_rate_2_memsec(l_sensorconfig.sensor_samp))); + return; + } + mutex_lock(&sense_data_mutex); + + + i = sensor_i2c_read(/*MMA8452_ADDR,*/MMA8X5X_OUT_X_MSB,tmp, sizeof(tmp)); +#if 0 + for (i=0; i<6; i++) + sensor_i2c_read(/*MMA8452_ADDR,*/MMA8X5X_OUT_X_MSB+i,&tmp[i], 1); + i = 0; +#endif + + if (0 == i) + { + rxData[0] = ((tmp[0] << 8 )&0xff00 ) | (tmp[1] ); + rxData[1] = ((tmp[2] << 8) &0xff00 ) | (tmp[3] ); + rxData[2] = ((tmp[4] << 8) &0xff00 ) | (tmp[5] ); + x = rxData[l_sensorconfig.xyz_axis[0][0]]*l_sensorconfig.xyz_axis[0][1]; + y = rxData[l_sensorconfig.xyz_axis[1][0]]*l_sensorconfig.xyz_axis[1][1]; + z = rxData[l_sensorconfig.xyz_axis[2][0]]*l_sensorconfig.xyz_axis[2][1]; + #if 0 + x = (x*9800) >> 14; + y = (y*9800) >> 14; + z = (z*9800) >> 14; + #endif + //printk("x,y,z (%d, %d, %d) \n", rxData[0], rxData[1], rxData[2]); + //printk("x,y,z (%d, %d, %d) \n", x, y, z); + //printk("x 0x%x\n", tmp[0]); + input_report_abs(data->input_dev, ABS_X, x); + input_report_abs(data->input_dev, ABS_Y, y); + input_report_abs(data->input_dev, ABS_Z, z); + input_sync(data->input_dev); + } + + + mutex_unlock(&sense_data_mutex); + queue_delayed_work(l_sensorconfig.queue, &l_sensorconfig.work, msecs_to_jiffies(sample_rate_2_memsec(l_sensorconfig.sensor_samp))); +} + +static int sensor_writeproc( struct file *file, + const char *buffer, + unsigned long count, + void *data ) +{ + + int inputval = -1; + int enable, sample = -1; + char tembuf[8]; + unsigned int amsr = 0; + int test = 0; + + mutex_lock(&sense_data_mutex); + // disable int + //gsensor_int_ctrl(DISABLE); + memset(tembuf, 0, sizeof(tembuf)); + // get sensor level and set sensor level + if (sscanf(buffer, "level=%d\n", &l_sensorconfig.sensorlevel)) + { + } else if (sscanf(buffer, "shakenable=%d\n", &l_sensorconfig.shake_enable)) + { + + } + else if (sscanf(buffer, "isdbg=%d\n", &l_sensorconfig.isdbg)) + { + // only set the dbg flag + } else if (sscanf(buffer, "init=%d\n", &inputval)) + { + mma8452q_chip_init(); + dbg("Has reinit sensor !!!\n"); + } else if (sscanf(buffer, "samp=%d\n", &sample)) + { + if (sample > 0) + { + if (sample != l_sensorconfig.sensor_samp) + { + amsr = mma_sample2AMSR(sample); + SET_MMA_SAMPLE(tembuf, amsr); + klog("sample:%d ,amsr:%d \n", sample, amsr); + l_sensorconfig.sensor_samp = sample; + } + printk(KERN_ALERT "sensor samp=%d(amsr:%d) has been set.\n", sample, amsr); + } else { + printk(KERN_ALERT "Wrong sample argumnet of sensor.\n"); + } + } else if (sscanf(buffer, "enable=%d\n", &enable)) + { + if ((enable < 0) || (enable > 1)) + { + printk(KERN_ERR "The argument to enable/disable g-sensor should be 0 or 1 !!!\n"); + } else if (enable != l_sensorconfig.sensor_enable) + { + mma_enable_disable(enable); + l_sensorconfig.sensor_enable = enable; + } + } else if (sscanf(buffer, "sensor_test=%d\n", &test)) + { // for test begin + l_sensorconfig.test_pass = 0; + } else if (sscanf(buffer, "sensor_testend=%d\n", &test)) + { // Don nothing only to be compatible the before testing program + } + mutex_unlock(&sense_data_mutex); + return count; +} + +static int sensor_readproc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len = 0; + + len = sprintf(page, + "test_pass=%d\nisdbg=%d\nrate=%d\nenable=%d\n", + l_sensorconfig.test_pass, + l_sensorconfig.isdbg, + l_sensorconfig.sensor_samp, + l_sensorconfig.sensor_enable + ); + return len; +} + + + +static int mma8452q_init_client(struct platform_device *pdev) +{ + struct mma8452q_config *data; +// int ret; + + data = dev_get_drvdata(&pdev->dev); + mutex_init(&sense_data_mutex); + /*Only for polling, not interrupt*/ + l_sensorconfig.sensor_proc = create_proc_entry(GSENSOR_PROC_NAME, 0666, NULL/*&proc_root*/); + if (l_sensorconfig.sensor_proc != NULL) + { + l_sensorconfig.sensor_proc->write_proc = sensor_writeproc; + l_sensorconfig.sensor_proc->read_proc = sensor_readproc; + } + + + return 0; + +//err: +// return ret; +} + +static struct file_operations mmad_fops = { + .owner = THIS_MODULE, + .open = mmad_open, + .release = mmad_release, + .unlocked_ioctl = mmad_ioctl, +}; + + +static struct miscdevice mmad_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "sensor_ctrl", + .fops = &mmad_fops, +}; + +static int mma8452q_probe(struct platform_device *pdev) +{ + int err; + + this_pdev = pdev; + l_sensorconfig.queue = create_singlethread_workqueue("sensor-intterupt-handle"); + INIT_DELAYED_WORK(&l_sensorconfig.work, mma_work_func); + + l_sensorconfig.input_dev = input_allocate_device(); + if (!l_sensorconfig.input_dev) { + err = -ENOMEM; + printk(KERN_ERR + "mma8452q_probe: Failed to allocate input device\n"); + goto exit_input_dev_alloc_failed; + } + l_sensorconfig.input_dev->evbit[0] = BIT(EV_ABS) | BIT_MASK(EV_KEY); + set_bit(KEY_NEXTSONG, l_sensorconfig.input_dev->keybit); + + /* x-axis acceleration */ + input_set_abs_params(l_sensorconfig.input_dev, ABS_X, -65532, 65532, 0, 0); + /* y-axis acceleration */ + input_set_abs_params(l_sensorconfig.input_dev, ABS_Y, -65532, 65532, 0, 0); + /* z-axis acceleration */ + input_set_abs_params(l_sensorconfig.input_dev, ABS_Z, -65532, 65532, 0, 0); + + l_sensorconfig.input_dev->name = "g-sensor"; + + err = input_register_device(l_sensorconfig.input_dev); + + if (err) { + printk(KERN_ERR + "mma8452q_probe: Unable to register input device: %s\n", + l_sensorconfig.input_dev->name); + goto exit_input_register_device_failed; + } + + err = misc_register(&mmad_device); + if (err) { + printk(KERN_ERR + "mma8452q_probe: mmad_device register failed\n"); + goto exit_misc_device_register_failed; + } + + dev_set_drvdata(&pdev->dev, &l_sensorconfig); + mma8452q_chip_init(); + mma8452q_init_client(pdev); + gsensor_sysfs_init(); + + // satrt the polling work + l_sensorconfig.sensor_samp = 10; + queue_delayed_work(l_sensorconfig.queue, &l_sensorconfig.work, msecs_to_jiffies(sample_rate_2_memsec(l_sensorconfig.sensor_samp))); + return 0; + +exit_misc_device_register_failed: +exit_input_register_device_failed: + input_free_device(l_sensorconfig.input_dev); + +exit_input_dev_alloc_failed: + + return err; +} + +static int mma8452q_remove(struct platform_device *pdev) +{ + if (NULL != l_sensorconfig.queue) + { + cancel_delayed_work_sync(&l_sensorconfig.work); + flush_workqueue(l_sensorconfig.queue); + destroy_workqueue(l_sensorconfig.queue); + l_sensorconfig.queue = NULL; + } + gsensor_sysfs_exit(); + misc_deregister(&mmad_device); + input_unregister_device(l_sensorconfig.input_dev); + if (l_sensorconfig.sensor_proc != NULL) + { + remove_proc_entry(GSENSOR_PROC_NAME, NULL); + l_sensorconfig.sensor_proc = NULL; + } + + return 0; +} + +static int mma8452q_suspend(struct platform_device *pdev, pm_message_t state) +{ + //gsensor_int_ctrl(DISABLE); + cancel_delayed_work_sync(&l_sensorconfig.work); + + dbg("...ok\n"); + //l_resumed = 2; + + return 0; +} + +static int mma8452q_resume(struct platform_device *pdev) +{ + + l_resumed = 1; + + mma8452q_chip_init(); + queue_delayed_work(l_sensorconfig.queue, \ + &l_sensorconfig.work, msecs_to_jiffies(sample_rate_2_memsec(l_sensorconfig.sensor_samp))); + dbg("...ok\n"); + + return 0; +} + +static void mma8452q_platform_release(struct device *device) +{ + return; +} + +static void mma8452q_shutdown(struct platform_device *pdev) +{ + flush_delayed_work_sync(&l_sensorconfig.work); + cancel_delayed_work_sync(&l_sensorconfig.work); + +} + +static struct platform_device mma8452q_device = { + .name = "mma8452q", + .id = 0, + .dev = { + .release = mma8452q_platform_release, + }, +}; + +static struct platform_driver mma8452q_driver = { + .probe = mma8452q_probe, + .remove = mma8452q_remove, + .suspend = mma8452q_suspend, + .resume = mma8452q_resume, + .shutdown = mma8452q_shutdown, + .driver = { + .name = "mma8452q", + }, +}; + +/* + * Brief: + * Get the configure of sensor from u-boot. + * Input: + * no use. + * Output: + * no use. + * Return: + * 0--success, -1--error. + * History: + * Created by HangYan on 2010-4-19 + * Author: + * Hang Yan in ShenZhen. + */ +extern int wmt_getsyspara(char *varname, unsigned char *varval, int *varlen); +static int get_axisset(void* param) +{ + char varbuf[64]; + int n; + int varlen; + + memset(varbuf, 0, sizeof(varbuf)); + varlen = sizeof(varbuf); + if (wmt_getsyspara("wmt.io.mma8452qgsensor", varbuf, &varlen)) { + printk(KERN_DEBUG "Can't get gsensor config in u-boot!!!!\n"); + return -1; + } else { + n = sscanf(varbuf, "%d:%d:%d:%d:%d:%d:%d:%d:%d:%d", + &l_sensorconfig.op, + &(l_sensorconfig.xyz_axis[0][0]), + &(l_sensorconfig.xyz_axis[0][1]), + &(l_sensorconfig.xyz_axis[1][0]), + &(l_sensorconfig.xyz_axis[1][1]), + &(l_sensorconfig.xyz_axis[2][0]), + &(l_sensorconfig.xyz_axis[2][1]), + &(l_sensorconfig.offset[0]), + &(l_sensorconfig.offset[1]), + &(l_sensorconfig.offset[2])); + if (n != 10) { + printk(KERN_ERR "gsensor format is error in u-boot!!!\n"); + return -1; + } + + printk("get the sensor config: %d:%d:%d:%d:%d:%d:%d:%d:%d:%d\n", + l_sensorconfig.op, + l_sensorconfig.xyz_axis[0][0], + l_sensorconfig.xyz_axis[0][1], + l_sensorconfig.xyz_axis[1][0], + l_sensorconfig.xyz_axis[1][1], + l_sensorconfig.xyz_axis[2][0], + l_sensorconfig.xyz_axis[2][1], + l_sensorconfig.offset[0], + l_sensorconfig.offset[1], + l_sensorconfig.offset[2] + ); + } + return 0; +} + +static int is_mma8452q(void) +{ + char rxData[2] = {0}; + int ret = 0; + int i = 0; + //char rdData[10] = {0}; + //char wbuf[6] = {0}; + //char rbuf[0x33] = {0}; + + ret = sensor_i2c_read(MMA8X5X_WHO_AM_I,rxData,1); + //printk("<<<<%s ret %d, val 0x%x\n", __FUNCTION__, ret, rxData[0]); + for(i = 0 ; i < sizeof(mma8x5x_chip_id)/sizeof(mma8x5x_chip_id[0]);i++) + if(rxData[0] == mma8x5x_chip_id[i]) + return 0; +#if 0 + struct i2c_client* client = gsensor_get_i2c_client(); + if (!client){ + printk("client NULL!\n"); + return -1; + } + + for (i=0; i<6; i++) + wbuf[i] = i+2; + //sensor_i2c_write(0x25, wbuf, 6); + //rbuf[0] = 0x11; + //sensor_i2c_read(0xb, rbuf, 0x1); + mma8452q_chip_init(); + for (i=0; i<0x12; i++) { + sensor_i2c_read(0xb+i, rbuf, 0x1); + printk("<<<<%s reg 0x%x val 0x%x\n", __FUNCTION__, 0xb+i, rbuf[0]); + } +#endif + return -1; +} + +static int __init mma8452q_init(void) +{ + int ret = 0; + + ret = get_axisset(NULL); + if (ret < 0) + { + printk("<<<<<%s user choose to no sensor chip!\n", __func__); + return ret; + } + + if (!(this_client = sensor_i2c_register_device(0, GSENSOR_I2C_ADDR, GSENSOR_I2C_NAME))) + { + printk(KERN_EMERG"Can't register gsensor i2c device!\n"); + return -1; + } + + if (is_mma8452q()) + { + printk(KERN_ERR "Can't find mma8452q!!\n"); + sensor_i2c_unregister_device(this_client); + return -1; + } + + + + printk(KERN_INFO "mma8452qfc g-sensor driver init\n"); + + spin_lock_init(&l_sensorconfig.spinlock); + + // Create device node + + l_dev_class = class_create(THIS_MODULE, GSENSOR_NAME); + //for S40 module to judge whether insmod is ok + if (IS_ERR(l_dev_class)){ + ret = PTR_ERR(l_dev_class); + printk(KERN_ERR "Can't class_create gsensor device !!\n"); + return ret; + } + l_clsdevice = device_create(l_dev_class, NULL, MKDEV(GSENSOR_MAJOR, 0), NULL, GSENSOR_NAME); + if (IS_ERR(l_clsdevice)){ + ret = PTR_ERR(l_clsdevice); + printk(KERN_ERR "Failed to create device %s !!!",GSENSOR_NAME); + return ret; + } + + if((ret = platform_device_register(&mma8452q_device))) + { + printk(KERN_ERR "%s Can't register mma8452q platform devcie!!!\n", __FUNCTION__); + return ret; + } + if ((ret = platform_driver_register(&mma8452q_driver)) != 0) + { + printk(KERN_ERR "%s Can't register mma8452q platform driver!!!\n", __FUNCTION__); + return ret; + } + + return 0; +} + +static void __exit mma8452q_exit(void) +{ + platform_driver_unregister(&mma8452q_driver); + platform_device_unregister(&mma8452q_device); + device_destroy(l_dev_class, MKDEV(GSENSOR_MAJOR, 0)); + + class_destroy(l_dev_class); + sensor_i2c_unregister_device(this_client); + +} + +module_init(mma8452q_init); +module_exit(mma8452q_exit); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/sensor/mma8452q_gsensor/mma8x5x.h b/drivers/input/sensor/mma8452q_gsensor/mma8x5x.h new file mode 100755 index 00000000..d8d386b2 --- /dev/null +++ b/drivers/input/sensor/mma8452q_gsensor/mma8x5x.h @@ -0,0 +1,107 @@ +/* + * Definitions for akm8976 compass chip. + */ +#ifndef AKM8976_H +#define AKM8976_H + +#include + +#define GSENSOR_I2C_NAME "mma8452q" +#define GSENSOR_I2C_ADDR 0x1c +/* Compass device dependent definition */ +#define AKECS_MODE_MEASURE 0x00 /* Starts measurement. Please use AKECS_MODE_MEASURE_SNG */ + /* or AKECS_MODE_MEASURE_SEQ instead of this. */ +#define AKECS_MODE_PFFD 0x01 /* Start pedometer and free fall detect. */ +#define AKECS_MODE_E2P_READ 0x02 /* E2P access mode (read). */ +#define AKECS_MODE_POWERDOWN 0x03 /* Power down mode */ + +#define AKECS_MODE_MEASURE_SNG 0x10 /* Starts single measurement */ +#define AKECS_MODE_MEASURE_SEQ 0x11 /* Starts sequential measurement */ + +/* Default register settings */ +#define CSPEC_AINT 0x01 /* Amplification for acceleration sensor */ +#define CSPEC_SNG_NUM 0x01 /* Single measurement mode */ +#define CSPEC_SEQ_NUM 0x02 /* Sequential measurement mode */ +#define CSPEC_SFRQ_32 0x00 /* Measurement frequency: 32Hz */ +#define CSPEC_SFRQ_64 0x01 /* Measurement frequency: 64Hz */ +#define CSPEC_MCS 0x07 /* Clock frequency */ +#define CSPEC_MKS 0x01 /* Clock type: CMOS level */ +#define CSPEC_INTEN 0x01 /* Interruption pin enable: Enable */ + +#define RBUFF_SIZE 31 /* Rx buffer size */ +#define MAX_CALI_SIZE 0x1000U /* calibration buffer size */ + +/* AK8976A register address */ +#define AKECS_REG_ST 0xC0 +#define AKECS_REG_TMPS 0xC1 +#define AKECS_REG_MS1 0xE0 +#define AKECS_REG_MS2 0xE1 +#define AKECS_REG_MS3 0xE2 + +#define AKMIO 0xA1 + +/* IOCTLs for AKM library */ +#define ECS_IOCTL_RESET _IO(AKMIO, 0x04) +#define ECS_IOCTL_INT_STATUS _IO(AKMIO, 0x05) +#define ECS_IOCTL_FFD_STATUS _IO(AKMIO, 0x06) +#define ECS_IOCTL_SET_MODE _IOW(AKMIO, 0x07, short) +#define ECS_IOCTL_GETDATA _IOR(AKMIO, 0x08, char[RBUFF_SIZE+1]) +#define ECS_IOCTL_GET_NUMFRQ _IOR(AKMIO, 0x09, char[2]) +#define ECS_IOCTL_SET_PERST _IO(AKMIO, 0x0A) +#define ECS_IOCTL_SET_G0RST _IO(AKMIO, 0x0B) +#define ECS_IOCTL_SET_YPR _IOW(AKMIO, 0x0C, short[12]) +#define ECS_IOCTL_GET_OPEN_STATUS _IOR(AKMIO, 0x0D, int) +#define ECS_IOCTL_GET_CLOSE_STATUS _IOR(AKMIO, 0x0E, int) +#define ECS_IOCTL_GET_CALI_DATA _IOR(AKMIO, 0x0F, char[MAX_CALI_SIZE]) +#define ECS_IOCTL_GET_DELAY _IOR(AKMIO, 0x30, short) + +/* IOCTLs for APPs */ +#define ECS_IOCTL_APP_SET_MODE _IOW(AKMIO, 0x10, short) +#define ECS_IOCTL_APP_SET_MFLAG _IOW(AKMIO, 0x11, short) +#define ECS_IOCTL_APP_GET_MFLAG _IOW(AKMIO, 0x12, short) +#define ECS_IOCTL_APP_GET_AFLAG _IOR(AKMIO, 0x14, short) +#define ECS_IOCTL_APP_SET_TFLAG _IOR(AKMIO, 0x15, short) +#define ECS_IOCTL_APP_GET_TFLAG _IOR(AKMIO, 0x16, short) +#define ECS_IOCTL_APP_RESET_PEDOMETER _IO(AKMIO, 0x17) +#define ECS_IOCTL_APP_GET_DELAY ECS_IOCTL_GET_DELAY +#define ECS_IOCTL_APP_SET_MVFLAG _IOW(AKMIO, 0x19, short) /* Set raw magnetic vector flag */ +#define ECS_IOCTL_APP_GET_MVFLAG _IOR(AKMIO, 0x1A, short) /* Get raw magnetic vector flag */ + +/* IOCTLs for pedometer */ +#define ECS_IOCTL_SET_STEP_CNT _IOW(AKMIO, 0x20, short) + +#define WMTGSENSOR_IOCTL_MAGIC 0x09 +#define ECS_IOCTL_APP_SET_AFLAG _IOW(WMTGSENSOR_IOCTL_MAGIC, 0x02, short) +#define ECS_IOCTL_APP_SET_DELAY _IOW(WMTGSENSOR_IOCTL_MAGIC, 0x03, short) +#define WMT_IOCTL_SENSOR_GET_DRVID _IOW(WMTGSENSOR_IOCTL_MAGIC, 0x04, unsigned int) + +//#define MMA8452_DRVID 6 + + + +/* Default GPIO setting */ +#define ECS_RST 146 /*MISC4, bit2 */ +#define ECS_CLK_ON 155 /*MISC5, bit3 */ +#define ECS_INTR 161 /*INT2, bit1 */ + +/* MISC */ +#define MMA8452_ADDR 0x1C +#define SENSOR_UI_MODE 0 +#define SENSOR_GRAVITYGAME_MODE 1 +#define UI_SAMPLE_RATE 0xFC +#define GSENSOR_PROC_NAME "gsensor_config" +#define sin30_1000 500 +#define cos30_1000 866 +#define DISABLE 0 +#define ENABLE 1 + +struct mma7660_platform_data { + int reset; + int clk_on; + int intr; +}; + +extern char *get_mma_cal_ram(void); + +#endif + diff --git a/drivers/input/sensor/mmc328x_msensor/Makefile b/drivers/input/sensor/mmc328x_msensor/Makefile new file mode 100755 index 00000000..bdf4598d --- /dev/null +++ b/drivers/input/sensor/mmc328x_msensor/Makefile @@ -0,0 +1,4 @@ +s_wmt_msensor_mmc328x-objs += mmc328x.o +obj-m += s_wmt_msensor_mmc328x.o +s_wmt_generic_mecs-objs += mecs.o +obj-m += s_wmt_generic_mecs.o \ No newline at end of file diff --git a/drivers/input/sensor/mmc328x_msensor/mecs.c b/drivers/input/sensor/mmc328x_msensor/mecs.c new file mode 100755 index 00000000..335ad266 --- /dev/null +++ b/drivers/input/sensor/mmc328x_msensor/mecs.c @@ -0,0 +1,433 @@ +/* + * Copyright (C) 2010 MEMSIC, Inc. + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include +#include +#include "mecs.h" + +#define DEBUG 0 + +#define ECS_DATA_DEV_NAME "ecompass_data" +#define ECS_CTRL_DEV_NAME "ecompass_ctrl" + +static int ecs_ctrl_open(struct inode *inode, struct file *file); +static int ecs_ctrl_release(struct inode *inode, struct file *file); +static int ecs_ctrl_ioctl(struct file *file, unsigned int cmd, unsigned long arg); + +static DECLARE_WAIT_QUEUE_HEAD(open_wq); + +static atomic_t open_count; +static atomic_t open_flag; +static atomic_t reserve_open_flag; + +static atomic_t a_flag; +static atomic_t m_flag; +static atomic_t o_flag; + +static short ecompass_delay = 0; + + +static struct input_dev *ecs_data_device; + +static struct file_operations ecs_ctrl_fops = { + .owner = THIS_MODULE, + .open = ecs_ctrl_open, + .release = ecs_ctrl_release, + .unlocked_ioctl = ecs_ctrl_ioctl, +}; + +static struct miscdevice ecs_ctrl_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = ECS_CTRL_DEV_NAME, + .fops = &ecs_ctrl_fops, +}; + +static int ecs_ctrl_open(struct inode *inode, struct file *file) +{ +#if 1 + atomic_set(&reserve_open_flag, 1); + atomic_set(&open_flag, 1); + atomic_set(&open_count, 1); + wake_up(&open_wq); + + return 0; +#else + int ret = -1; + + if (atomic_cmpxchg(&open_count, 0, 1) == 0) { + if (atomic_cmpxchg(&open_flag, 0, 1) == 0) { + atomic_set(&reserve_open_flag, 1); + wake_up(&open_wq); + ret = 0; + } + } + + return ret; +#endif +} + +static int ecs_ctrl_release(struct inode *inode, struct file *file) +{ + atomic_set(&reserve_open_flag, 0); + atomic_set(&open_flag, 0); + atomic_set(&open_count, 0); + wake_up(&open_wq); + + return 0; +} + +static int ecs_ctrl_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + void __user *pa = (void __user *)arg; + short flag; + short delay; + int parms[4]; + int ypr[12]; + unsigned int uval = 0; + + switch (cmd) { + case ECOMPASS_IOC_SET_MODE: + break; + case ECOMPASS_IOC_SET_DELAY: + if (copy_from_user(&delay, pa, sizeof(delay))) + return -EFAULT; + ecompass_delay = delay; + break; + case ECOMPASS_IOC_GET_DELAY: + delay = ecompass_delay; + if (copy_to_user(pa, &delay, sizeof(delay))) + return -EFAULT; + break; + + case ECOMPASS_IOC_SET_AFLAG: + if (copy_from_user(&flag, pa, sizeof(flag))) + return -EFAULT; + if (flag < 0 || flag > 1) + return -EINVAL; + atomic_set(&a_flag, flag); + break; + case ECOMPASS_IOC_GET_AFLAG: + flag = atomic_read(&a_flag); + if (copy_to_user(pa, &flag, sizeof(flag))) + return -EFAULT; + break; + case ECOMPASS_IOC_SET_MFLAG: + if (copy_from_user(&flag, pa, sizeof(flag))) + return -EFAULT; + if (flag < 0 || flag > 1) + return -EINVAL; + atomic_set(&m_flag, flag); + break; + case ECOMPASS_IOC_GET_MFLAG: + flag = atomic_read(&m_flag); + if (copy_to_user(pa, &flag, sizeof(flag))) + return -EFAULT; + break; + case ECOMPASS_IOC_SET_OFLAG: + if (copy_from_user(&flag, pa, sizeof(flag))) + return -EFAULT; + if (flag < 0 || flag > 1) + return -EINVAL; + atomic_set(&o_flag, flag); + break; + case ECOMPASS_IOC_GET_OFLAG: + flag = atomic_read(&o_flag); + if (copy_to_user(pa, &flag, sizeof(flag))) + return -EFAULT; + break; + + case ECOMPASS_IOC_SET_APARMS: + if (copy_from_user(parms, pa, sizeof(parms))) + return -EFAULT; + /* acceleration x-axis */ + input_set_abs_params(ecs_data_device, ABS_X, + parms[0], parms[1], parms[2], parms[3]); + /* acceleration y-axis */ + input_set_abs_params(ecs_data_device, ABS_Y, + parms[0], parms[1], parms[2], parms[3]); + /* acceleration z-axis */ + input_set_abs_params(ecs_data_device, ABS_Z, + parms[0], parms[1], parms[2], parms[3]); + break; + case ECOMPASS_IOC_GET_APARMS: + break; + case ECOMPASS_IOC_SET_MPARMS: + if (copy_from_user(parms, pa, sizeof(parms))) + return -EFAULT; + /* magnetic raw x-axis */ + input_set_abs_params(ecs_data_device, ABS_HAT0X, + parms[0], parms[1], parms[2], parms[3]); + /* magnetic raw y-axis */ + input_set_abs_params(ecs_data_device, ABS_HAT0Y, + parms[0], parms[1], parms[2], parms[3]); + /* magnetic raw z-axis */ + input_set_abs_params(ecs_data_device, ABS_BRAKE, + parms[0], parms[1], parms[2], parms[3]); + break; + case ECOMPASS_IOC_GET_MPARMS: + break; + case ECOMPASS_IOC_SET_OPARMS_YAW: + if (copy_from_user(parms, pa, sizeof(parms))) + return -EFAULT; + /* orientation yaw */ + input_set_abs_params(ecs_data_device, ABS_RX, + parms[0], parms[1], parms[2], parms[3]); + break; + case ECOMPASS_IOC_GET_OPARMS_YAW: + break; + case ECOMPASS_IOC_SET_OPARMS_PITCH: + if (copy_from_user(parms, pa, sizeof(parms))) + return -EFAULT; + /* orientation pitch */ + input_set_abs_params(ecs_data_device, ABS_RY, + parms[0], parms[1], parms[2], parms[3]); + break; + case ECOMPASS_IOC_GET_OPARMS_PITCH: + break; + case ECOMPASS_IOC_SET_OPARMS_ROLL: + if (copy_from_user(parms, pa, sizeof(parms))) + return -EFAULT; + /* orientation roll */ + input_set_abs_params(ecs_data_device, ABS_RZ, + parms[0], parms[1], parms[2], parms[3]); + break; + case ECOMPASS_IOC_GET_OPARMS_ROLL: + break; + + case ECOMPASS_IOC_SET_YPR: + if (copy_from_user(ypr, pa, sizeof(ypr))) + return -EFAULT; + /* Report acceleration sensor information */ + if (atomic_read(&a_flag)) { + input_report_abs(ecs_data_device, ABS_X, ypr[0]); + input_report_abs(ecs_data_device, ABS_Y, ypr[1]); + input_report_abs(ecs_data_device, ABS_Z, ypr[2]); + input_report_abs(ecs_data_device, ABS_WHEEL, ypr[3]); + } + + /* Report magnetic sensor information */ + if (atomic_read(&m_flag)) { + input_report_abs(ecs_data_device, ABS_HAT0X, ypr[4]); + input_report_abs(ecs_data_device, ABS_HAT0Y, ypr[5]); + input_report_abs(ecs_data_device, ABS_BRAKE, ypr[6]); + input_report_abs(ecs_data_device, ABS_GAS, ypr[7]); + } + + /* Report orientation information */ + if (atomic_read(&o_flag)) { + input_report_abs(ecs_data_device, ABS_RX, ypr[8]); + input_report_abs(ecs_data_device, ABS_RY, ypr[9]); + input_report_abs(ecs_data_device, ABS_RZ, ypr[10]); + input_report_abs(ecs_data_device, ABS_RUDDER, ypr[11]); + } + + input_sync(ecs_data_device); + break; + + case WMT_IOCTL_SENSOR_GET_DRVID: + uval = 0; + if (copy_to_user((unsigned int*)arg, &uval, sizeof(unsigned int))) + { + return -EFAULT; + } + + default: + break; + } + + return 0; +} + +static ssize_t ecs_ctrl_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + ssize_t ret = 0; + + sprintf(buf, "ecompass_ctrl");//!!! + ret = strlen(buf) + 1; + + return ret; +} + +static DEVICE_ATTR(ecs_ctrl, S_IRUGO, ecs_ctrl_show, NULL); + +//********************** +static struct input_dev *g_input; +struct timer_list mytimer; +static void rep_pwer(long var) +{ + static int i = 0; + i++; +#if 0 + input_report_abs(g_input, ABS_X,i); + input_report_abs(g_input, ABS_Y, i); + input_report_abs(g_input, ABS_Z, i); + //input_report_abs(ecs_data_device, ABS_GAS, 2); + input_sync(g_input); +#endif + + input_report_abs(ecs_data_device, ABS_HAT0X,i); + input_report_abs(ecs_data_device, ABS_HAT0Y, i); + input_report_abs(ecs_data_device, ABS_BRAKE, i); + input_report_abs(ecs_data_device, ABS_GAS, 2); + input_sync(ecs_data_device); + //printk("<<<evbit); + /* Accelerometer [-78.5, 78.5]m/s2 in Q16 */ + input_set_abs_params(g_input, ABS_HAT0X, -5144576, 5144576, 0, 0); + input_set_abs_params(g_input, ABS_HAT0Y, -5144576, 5144576, 0, 0); + input_set_abs_params(g_input, ABS_BRAKE, -5144576, 5144576, 0, 0); + + /* Set InputDevice Name */ + g_input->name = "ecompass_data"; + + /* Register */ + //err = input_register_device(g_input); + //*****************************************2013-4-13 + + ecs_data_device = input_allocate_device(); + if (!ecs_data_device) { + res = -ENOMEM; + pr_err("%s: failed to allocate input device\n", __FUNCTION__); + goto out; + } + + set_bit(EV_ABS, ecs_data_device->evbit); + + /* 32768 == 1g, range -4g ~ +4g */ + /* acceleration x-axis */ + input_set_abs_params(ecs_data_device, ABS_X, + -32768*4, 32768*4, 0, 0); + /* acceleration y-axis */ + input_set_abs_params(ecs_data_device, ABS_Y, + -32768*4, 32768*4, 0, 0); + /* acceleration z-axis */ + input_set_abs_params(ecs_data_device, ABS_Z, + -32768*4, 32768*4, 0, 0); + /* acceleration status, 0 ~ 3 */ + input_set_abs_params(ecs_data_device, ABS_WHEEL, + 0, 100, 0, 0); + + /* 32768 == 1gauss, range -4gauss ~ +4gauss */ + /* magnetic raw x-axis */ + input_set_abs_params(ecs_data_device, ABS_HAT0X, + -32768*4, 32768*4, 0, 0); + /* magnetic raw y-axis */ + input_set_abs_params(ecs_data_device, ABS_HAT0Y, + -32768*4, 32768*4, 0, 0); + /* magnetic raw z-axis */ + input_set_abs_params(ecs_data_device, ABS_BRAKE, + -32768*4, 32768*4, 0, 0); + /* magnetic raw status, 0 ~ 3 */ + input_set_abs_params(ecs_data_device, ABS_GAS, + 0, 100, 0, 0); + + /* 65536 == 360degree */ + /* orientation yaw, 0 ~ 360 */ + input_set_abs_params(ecs_data_device, ABS_RX, + 0, 65536, 0, 0); + /* orientation pitch, -180 ~ 180 */ + input_set_abs_params(ecs_data_device, ABS_RY, + -65536/2, 65536/2, 0, 0); + /* orientation roll, -90 ~ 90 */ + input_set_abs_params(ecs_data_device, ABS_RZ, + -65536/4, 65536/4, 0, 0); + /* orientation status, 0 ~ 3 */ + input_set_abs_params(ecs_data_device, ABS_RUDDER, + 0, 100, 0, 0); + + ecs_data_device->name = ECS_DATA_DEV_NAME; +#if 1 + res = input_register_device(ecs_data_device); + if (res) { + pr_err("%s: unable to register input device: %s\n", + __FUNCTION__, ecs_data_device->name); + goto out_free_input; + } +#endif + + res = misc_register(&ecs_ctrl_device); + if (res) { + pr_err("%s: ecs_ctrl_device register failed\n", __FUNCTION__); + goto out_free_input; + } + res = device_create_file(ecs_ctrl_device.this_device, &dev_attr_ecs_ctrl); + if (res) { + pr_err("%s: device_create_file failed\n", __FUNCTION__); + goto out_deregister_misc; + } + + return 0; + +out_deregister_misc: + misc_deregister(&ecs_ctrl_device); +out_free_input: + input_free_device(ecs_data_device); +out: + return res; +} + +static void __exit ecompass_exit(void) +{ + pr_info("ecompass driver: exit\n"); + device_remove_file(ecs_ctrl_device.this_device, &dev_attr_ecs_ctrl); + misc_deregister(&ecs_ctrl_device); + input_free_device(ecs_data_device); +} + +module_init(ecompass_init); +module_exit(ecompass_exit); + +MODULE_DESCRIPTION("MEMSIC eCompass Driver"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/input/sensor/mmc328x_msensor/mecs.h b/drivers/input/sensor/mmc328x_msensor/mecs.h new file mode 100755 index 00000000..c328e585 --- /dev/null +++ b/drivers/input/sensor/mmc328x_msensor/mecs.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2010 MEMSIC, Inc. + * + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* + * Definitions for ECOMPASS magnetic sensor chip. + */ +#ifndef __ECOMPASS_H__ +#define __ECOMPASS_H__ + +#include + +/* Use 'e' as magic number */ +#define ECOMPASS_IOM 'e' + +/* IOCTLs for ECOMPASS device */ +#define ECOMPASS_IOC_SET_MODE _IOW(ECOMPASS_IOM, 0x00, short) +#define ECOMPASS_IOC_SET_DELAY _IOW(ECOMPASS_IOM, 0x01, short) +#define ECOMPASS_IOC_GET_DELAY _IOR(ECOMPASS_IOM, 0x02, short) + +#define ECOMPASS_IOC_SET_AFLAG _IOW(ECOMPASS_IOM, 0x10, short) +#define ECOMPASS_IOC_GET_AFLAG _IOR(ECOMPASS_IOM, 0x11, short) +#define ECOMPASS_IOC_SET_MFLAG _IOW(ECOMPASS_IOM, 0x12, short) +#define ECOMPASS_IOC_GET_MFLAG _IOR(ECOMPASS_IOM, 0x13, short) +#define ECOMPASS_IOC_SET_OFLAG _IOW(ECOMPASS_IOM, 0x14, short) +#define ECOMPASS_IOC_GET_OFLAG _IOR(ECOMPASS_IOM, 0x15, short) + +#define ECOMPASS_IOC_SET_APARMS _IOW(ECOMPASS_IOM, 0x20, int[4]) +#define ECOMPASS_IOC_GET_APARMS _IOR(ECOMPASS_IOM, 0x21, int[4]) +#define ECOMPASS_IOC_SET_MPARMS _IOW(ECOMPASS_IOM, 0x22, int[4]) +#define ECOMPASS_IOC_GET_MPARMS _IOR(ECOMPASS_IOM, 0x23, int[4]) +#define ECOMPASS_IOC_SET_OPARMS_YAW _IOW(ECOMPASS_IOM, 0x24, int[4]) +#define ECOMPASS_IOC_GET_OPARMS_YAW _IOR(ECOMPASS_IOM, 0x25, int[4]) +#define ECOMPASS_IOC_SET_OPARMS_PITCH _IOW(ECOMPASS_IOM, 0x26, int[4]) +#define ECOMPASS_IOC_GET_OPARMS_PITCH _IOR(ECOMPASS_IOM, 0x27, int[4]) +#define ECOMPASS_IOC_SET_OPARMS_ROLL _IOW(ECOMPASS_IOM, 0x28, int[4]) +#define ECOMPASS_IOC_GET_OPARMS_ROLL _IOR(ECOMPASS_IOM, 0x29, int[4]) + +#define ECOMPASS_IOC_SET_YPR _IOW(ECOMPASS_IOM, 0x30, int[12]) + +#define WMTGSENSOR_IOCTL_MAGIC 0x09 +#define WMT_IOCTL_SENSOR_GET_DRVID _IOW(WMTGSENSOR_IOCTL_MAGIC, 0x04, unsigned int) +#endif /* __ECOMPASS_H__ */ + diff --git a/drivers/input/sensor/mmc328x_msensor/mmc328x.c b/drivers/input/sensor/mmc328x_msensor/mmc328x.c new file mode 100755 index 00000000..4148b5f1 --- /dev/null +++ b/drivers/input/sensor/mmc328x_msensor/mmc328x.c @@ -0,0 +1,505 @@ +/* + * Copyright (C) 2011 MEMSIC, Inc. + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include +#include "mmc328x.h" +#define DEBUG 0 +#define MAX_FAILURE_COUNT 3 +#define READMD 0 + +#define MMC328X_DELAY_TM 10 /* ms */ +#define MMC328X_DELAY_RM 10 /* ms */ +#define MMC328X_DELAY_STDN 1 /* ms */ +#define MMC328X_DELAY_RRM 1 /* ms */ + +#define MMC328X_RETRY_COUNT 3 +#define MMC328X_RRM_INTV 100 + + +//******************************* move from memsicd 2013-4-26 +#define MMC328X_OFFSET_X 4096 +#define MMC328X_OFFSET_Y 4096 +#define MMC328X_OFFSET_Z 4096 +//****************************************** + +#define MMC328X_DEV_NAME "mmc328x" +#define CONFIG_SENSORS_MMC328xMA_MAG //add rambo 2013-4-20 +struct i2c_client *g_client; + +extern int wmt_getsyspara(char *varname, unsigned char *varval, int *varlen); +static u32 read_idx = 0; + +static struct i2c_client *this_client; + +static struct wmt_msensor_data l_sensorconfig = { + .op = 0, + .int_gpio = 3, + .samp = 5, + .xyz_axis = { + {ABS_X, -1}, + {ABS_Y, 1}, + {ABS_Z, -1}, + }, + .sensor_proc = NULL, + .isdbg = 0, + .sensor_samp = 10, // 1 sample/second + .sensor_enable = 1, // enable sensor + .test_pass = 0, // for test program + .offset={0,0,0}, +}; + +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.msensor328x", varbuf, &varlen)) { + printk("Can't get gsensor config in u-boot!!!!\n"); + //return -1; + } else { + n = sscanf(varbuf, "%d:%d:%d:%d:%d:%d", + + &(l_sensorconfig.xyz_axis[0][0]), + &(l_sensorconfig.xyz_axis[0][1]), + &(l_sensorconfig.xyz_axis[1][0]), + &(l_sensorconfig.xyz_axis[1][1]), + &(l_sensorconfig.xyz_axis[2][0]), + &(l_sensorconfig.xyz_axis[2][1]) + + ); + if (n != 6) { + printk("gsensor format is error in u-boot!!!\n"); + return -1; + } + l_sensorconfig.sensor_samp = l_sensorconfig.samp; + + printk("get the sensor config: %d:%d:%d:%d:%d:%d\n", + + l_sensorconfig.xyz_axis[0][0], + l_sensorconfig.xyz_axis[0][1], + l_sensorconfig.xyz_axis[1][0], + l_sensorconfig.xyz_axis[1][1], + l_sensorconfig.xyz_axis[2][0], + l_sensorconfig.xyz_axis[2][1] + ); + } + return 0; +} + + +static int mmc328x_i2c_rx_data(char *buf, int len) +{ + uint8_t i; + struct i2c_msg msgs[] = { + { + .addr = this_client->addr, + .flags = 0, + .len = 1, + .buf = buf, + }, + { + .addr = this_client->addr, + .flags = I2C_M_RD, + .len = len, + .buf = buf, + } + }; + + for (i = 0; i < MMC328X_RETRY_COUNT; i++) { + if (i2c_transfer(this_client->adapter, msgs, 2) >= 0) { + break; + } + mdelay(10); + } + + if (i >= MMC328X_RETRY_COUNT) { + pr_err("%s: retry over %d\n", __FUNCTION__, MMC328X_RETRY_COUNT); + return -EIO; + } + + return 0; +} + +static int mmc328x_i2c_tx_data(char *buf, int len) +{ + uint8_t i; + struct i2c_msg msg[] = { + { + .addr = this_client->addr, + .flags = 0, + .len = len, + .buf = buf, + } + }; + + for (i = 0; i < MMC328X_RETRY_COUNT; i++) { + if (i2c_transfer(this_client->adapter, msg, 1) >= 0) { + break; + } + mdelay(10); + } + + if (i >= MMC328X_RETRY_COUNT) { + pr_err("%s: retry over %d\n", __FUNCTION__, MMC328X_RETRY_COUNT); + return -EIO; + } + return 0; +} + +static int mmc328x_open(struct inode *inode, struct file *file) +{ + return nonseekable_open(inode, file); +} + +static int mmc328x_release(struct inode *inode, struct file *file) +{ + return 0; +} + +static int mmc328x_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + void __user *pa = (void __user *)arg; + unsigned char data[16] = {0}; + int vec[3] = {0}; + int tmp[3] = {0}; + + int MD_times = 0; + + switch (cmd) { + case MMC328X_IOC_TM: + data[0] = MMC328X_REG_CTRL; + data[1] = MMC328X_CTRL_TM; + if (mmc328x_i2c_tx_data(data, 2) < 0) { + return -EFAULT; + } + /* wait TM done for coming data read */ + msleep(MMC328X_DELAY_TM); + break; + case MMC328X_IOC_RM: + data[0] = MMC328X_REG_CTRL; + data[1] = MMC328X_CTRL_RM; + if (mmc328x_i2c_tx_data(data, 2) < 0) { + return -EFAULT; + } + /* wait external capacitor charging done for next SET*/ + msleep(MMC328X_DELAY_RM); + break; + case MMC328X_IOC_RRM: + data[0] = MMC328X_REG_CTRL; + data[1] = MMC328X_CTRL_RRM; + if (mmc328x_i2c_tx_data(data, 2) < 0) { + return -EFAULT; + } + /* wait external capacitor charging done for next RRM */ + msleep(MMC328X_DELAY_RM); + break; + case MMC328X_IOC_READ: + data[0] = MMC328X_REG_DATA; + if (mmc328x_i2c_rx_data(data, 6) < 0) { + return -EFAULT; + } + tmp[0] = data[1] << 8 | data[0]; + tmp[1] = data[3] << 8 | data[2]; + tmp[2] = data[5] << 8 | data[4]; + tmp[2] = 8192 - tmp[2] ; + //add 2013-4-26 + tmp[0] -= MMC328X_OFFSET_X; + tmp[1] -= MMC328X_OFFSET_Y; + tmp[2] -= MMC328X_OFFSET_Z; + //add end + vec[0] = tmp[l_sensorconfig.xyz_axis[0][0]]*l_sensorconfig.xyz_axis[0][1]; + + vec[1] = tmp[l_sensorconfig.xyz_axis[1][0]]*l_sensorconfig.xyz_axis[1][1]; + + vec[2] = tmp[l_sensorconfig.xyz_axis[2][0]]*l_sensorconfig.xyz_axis[2][1]; + + #if DEBUG + printk("[X - %04x] [Y - %04x] [Z - %04x]\n", + vec[0], vec[1], vec[2]); + #endif + if (copy_to_user(pa, vec, sizeof(vec))) { + return -EFAULT; + } + break; + case MMC328X_IOC_READXYZ: + /* do RM every MMC328X_RRM_INTV times read */ + if (!(read_idx % MMC328X_RRM_INTV)) { +#ifdef CONFIG_SENSORS_MMC328xMA_MAG + data[0] = MMC328X_REG_CTRL; + data[1] = MMC328X_CTRL_RRM; + mmc328x_i2c_tx_data(data, 2); + msleep(MMC328X_DELAY_RRM); +#endif + /* RM */ + data[0] = MMC328X_REG_CTRL; + data[1] = MMC328X_CTRL_RM; + /* not check return value here, assume it always OK */ + mmc328x_i2c_tx_data(data, 2); + /* wait external capacitor charging done for next RM */ + msleep(MMC328X_DELAY_RM); + } + read_idx++; + + /* send TM cmd before read */ + data[0] = MMC328X_REG_CTRL; + data[1] = MMC328X_CTRL_TM; + /* not check return value here, assume it always OK */ + mmc328x_i2c_tx_data(data, 2); + /* wait TM done for coming data read */ + msleep(MMC328X_DELAY_TM); +#if READMD + /* Read MD */ + data[0] = MMC328X_REG_DS; + if (mmc328x_i2c_rx_data(data, 1) < 0) { + return -EFAULT; + } + while (!(data[0] & 0x01)) { + msleep(1); + /* Read MD again*/ + data[0] = MMC328X_REG_DS; + if (mmc328x_i2c_rx_data(data, 1) < 0) { + return -EFAULT; + } + + if (data[0] & 0x01) break; + MD_times++; + if (MD_times > 2) { + #if DEBUG + printk("TM not work!!"); + #endif + return -EFAULT; + } + } +#endif + /* read xyz raw data */ + data[0] = MMC328X_REG_DATA; + if (mmc328x_i2c_rx_data(data, 6) < 0) { + return -EFAULT; + } + tmp[0] = data[1] << 8 | data[0]; + tmp[1] = data[3] << 8 | data[2]; + tmp[2] = data[5] << 8 | data[4]; + tmp[2] = 8192 - tmp[2]; + //add 2013-4-26 + tmp[0] -= MMC328X_OFFSET_X; + tmp[1] -= MMC328X_OFFSET_Y; + tmp[2] -= MMC328X_OFFSET_Z; + //add + vec[0] = tmp[l_sensorconfig.xyz_axis[0][0]]*l_sensorconfig.xyz_axis[0][1]; + + vec[1] = tmp[l_sensorconfig.xyz_axis[1][0]]*l_sensorconfig.xyz_axis[1][1]; + + vec[2] = tmp[l_sensorconfig.xyz_axis[2][0]]*l_sensorconfig.xyz_axis[2][1]; + + + + #if DEBUG + printk("[X - %04x] [Y - %04x] [Z - %04x]\n", + vec[0], vec[1], vec[2]); + #endif + if (copy_to_user(pa, vec, sizeof(vec))) { + return -EFAULT; + } + + break; + default: + break; + } + + return 0; +} + +static ssize_t mmc328x_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + ssize_t ret = 0; + + sprintf(buf, "MMC328X");//!!! + ret = strlen(buf) + 1; + + return ret; +} + +static DEVICE_ATTR(mmc328x, S_IRUGO, mmc328x_show, NULL); + +static struct file_operations mmc328x_fops = { + .owner = THIS_MODULE, + .open = mmc328x_open, + .release = mmc328x_release, + .unlocked_ioctl = mmc328x_ioctl, +}; + +static struct miscdevice mmc328x_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = MMC328X_DEV_NAME, + .fops = &mmc328x_fops, +}; + +static int mmc328x_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + unsigned char data[16] = {0}; + int res = 0; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + pr_err("%s: functionality check failed\n", __FUNCTION__); + res = -ENODEV; + goto out; + } + this_client = client; + + res = misc_register(&mmc328x_device);// + if (res) { + pr_err("%s: mmc328x_device register failed\n", __FUNCTION__); + goto out; + } + res = device_create_file(&client->dev, &dev_attr_mmc328x);// + if (res) { + pr_err("%s: device_create_file failed\n", __FUNCTION__); + goto out_deregister; + } + + /* send RM/RRM cmd to mag sensor first of all */ +#ifdef CONFIG_SENSORS_MMC328xMA_MAG + data[0] = MMC328X_REG_CTRL; + data[1] = MMC328X_CTRL_RRM; + if (mmc328x_i2c_tx_data(data, 2) < 0) { + } + msleep(MMC328X_DELAY_RRM); + data[0] = MMC328X_REG_CTRL; + data[1] = MMC328X_CTRL_TM; + if (mmc328x_i2c_tx_data(data, 2) < 0) { + } + msleep(5*MMC328X_DELAY_TM); +#endif + + data[0] = MMC328X_REG_CTRL; + data[1] = MMC328X_CTRL_RM; + if (mmc328x_i2c_tx_data(data, 2) < 0) { + /* assume RM always success */ + } +#ifndef CONFIG_SENSORS_MMC328xMA_MAG + /* wait external capacitor charging done for next RM */ + msleep(MMC328X_DELAY_RM); +#else + msleep(10*MMC328X_DELAY_RM); + data[0] = MMC328X_REG_CTRL; + data[1] = MMC328X_CTRL_TM; + if (mmc328x_i2c_tx_data(data, 2) < 0) { + } +#endif + + return 0; + +out_deregister: + misc_deregister(&mmc328x_device); +out: + return res; +} + +static int mmc328x_remove(struct i2c_client *client) +{ + device_remove_file(&client->dev, &dev_attr_mmc328x); + misc_deregister(&mmc328x_device); + + return 0; +} + +static const struct i2c_device_id mmc328x_id[] = { + { MMC328X_I2C_NAME, 0 }, + { } +}; + +static struct i2c_driver mmc328x_driver = { + .probe = mmc328x_probe, + .remove = mmc328x_remove, + .id_table = mmc328x_id, + .driver = { + .owner = THIS_MODULE, + .name = MMC328X_I2C_NAME, + }, +}; + +//****************************add by rambo to create i2c client 2013-4-18 +static struct i2c_board_info mmc328x_board_info = +{ + .type = MMC328X_I2C_NAME, + .addr = MMC328X_I2C_ADDR, +}; + +//****************************************** + +static int __init mmc328x_init(void) +{ + int ret; + pr_info("mmc328x driver: init\n"); + + ret = get_axisset(); + struct i2c_adapter *adapter; + adapter = i2c_get_adapter(0); + if (!adapter) + { + printk("<<<<<%s i2c get adapter fail!\n", __FUNCTION__); + return -1; + } + g_client = i2c_new_device(adapter, &mmc328x_board_info); + if (!g_client) + { + printk("<<<<%s i2c new device fail!\n", __FUNCTION__); + i2c_put_adapter(adapter); + return -1; + } + i2c_put_adapter(adapter); + + return i2c_add_driver(&mmc328x_driver); +} + +static void __exit mmc328x_exit(void) +{ + if (g_client != NULL) + { + i2c_unregister_device(g_client); + } + pr_info("mmc328x driver: exit\n"); + i2c_del_driver(&mmc328x_driver); +} + +module_init(mmc328x_init); +module_exit(mmc328x_exit); + +MODULE_DESCRIPTION("MEMSIC MMC328X Magnetic Sensor Driver"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/input/sensor/mmc328x_msensor/mmc328x.h b/drivers/input/sensor/mmc328x_msensor/mmc328x.h new file mode 100755 index 00000000..f272ea1d --- /dev/null +++ b/drivers/input/sensor/mmc328x_msensor/mmc328x.h @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2010 MEMSIC, Inc. + * + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* + * Definitions for mmc328x magnetic sensor chip. + */ +#ifndef __MMC328X_H__ +#define __MMC328X_H__ + +#include + +#define MMC328X_I2C_NAME "mmc328x" + +/* + * This address comes must match the part# on your target. + * Address to the sensor part# support as following list: + * MMC3280MS - 0110000b + * MMC3281MS - 0110001b + * MMC3282MS - 0110010b + * MMC3283MS - 0110011b + * MMC3284MS - 0110100b + * MMC3285MS - 0110101b + * MMC3286MS - 0110110b + * MMC3287MS - 0110111b + * Please refer to sensor datasheet for detail. + */ + struct wmt_msensor_data{ + // for control + int int_gpio; //0-3 + int op; + int samp; + int xyz_axis[3][2]; // (axis,direction) + struct proc_dir_entry* sensor_proc; + struct input_dev *input_dev; + //struct work_struct work; + struct delayed_work work; // for polling + struct workqueue_struct *queue; + int isdbg; + int sensor_samp; // + int sensor_enable; // 0 --> disable sensor, 1 --> enable sensor + int test_pass; + int offset[3]; + struct i2c_client *client; +#ifdef CONFIG_HAS_EARLYSUSPEND + struct early_suspend earlysuspend; +#endif + +}; +#define MMC328X_I2C_ADDR 0x30 + +/* MMC328X register address */ +#define MMC328X_REG_CTRL 0x07 +#define MMC328X_REG_DATA 0x00 +#define MMC328X_REG_DS 0x06 + +/* MMC328X control bit */ +#define MMC328X_CTRL_TM 0x01 +#define MMC328X_CTRL_RM 0x20 +#define MMC328X_CTRL_RRM 0x40 +#define MMC328X_CTRL_NOBOOST 0x10 + +/* Use 'm' as magic number */ +#define MMC328X_IOM 'm' + +/* IOCTLs for MMC328X device */ +#define MMC328X_IOC_TM _IO (MMC328X_IOM, 0x00) +#define MMC328X_IOC_RM _IO (MMC328X_IOM, 0x01) +#define MMC328X_IOC_READ _IOR(MMC328X_IOM, 0x02, int[3]) +#define MMC328X_IOC_READXYZ _IOR(MMC328X_IOM, 0x03, int[3]) +#define MMC328X_IOC_RRM _IO (MMC328X_IOM, 0x04) +#define MMC328X_IOC_NOBOOST _IO (MMC328X_IOM, 0x05) + +#endif /* __MMC328X_H__ */ + diff --git a/drivers/input/sensor/mxc622x_gsensor/Makefile b/drivers/input/sensor/mxc622x_gsensor/Makefile new file mode 100755 index 00000000..16baea79 --- /dev/null +++ b/drivers/input/sensor/mxc622x_gsensor/Makefile @@ -0,0 +1,34 @@ +KERNELDIR=../../../../ +#KERNELDIR=/home/hangyan/android8850/kernel/ANDROID_3.0.8 +CROSS = arm_1103_le- +CC= $(CROSS)gcc +LD= $(CROSS)ld +STRIP = $(CROSS)strip + +DEBUG = n + +# Add your debugging flag (or not) to EXTRA_CFLAGS +ifeq ($(DEBUG),y) +# DEBFLAGS = -O -g -DSCULL_DEBUG # "-O" is needed to expand inlines +DEBFLAGS = -O0 -g -DSCULL_DEBUG # "-O" is needed to expand inlines + +else + DEBFLAGS = -O2 -Wall +endif + +EXTRA_CFLAGS += $(DEBFLAGS) + + +MY_MODULE_NAME=s_wmt_gsensor_mxc622x + +obj-m := $(MY_MODULE_NAME).o +$(MY_MODULE_NAME)-objs := mxc622x.o + +default: + $(MAKE) -C $(KERNELDIR) SUBDIRS=$(PWD) modules + $(STRIP) --strip-debug $(MY_MODULE_NAME).ko + rm -rf *.o *~ core .depend .*.cmd *.mod.c .tmp_versions + +clean: + rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions *.order *.symvers + diff --git a/drivers/input/sensor/mxc622x_gsensor/mxc622x.c b/drivers/input/sensor/mxc622x_gsensor/mxc622x.c new file mode 100755 index 00000000..9c94b6ed --- /dev/null +++ b/drivers/input/sensor/mxc622x_gsensor/mxc622x.c @@ -0,0 +1,1151 @@ +/******************** (C) COPYRIGHT 2010 STMicroelectronics ******************** + * + * File Name : mxc622x_acc.c + * Description : MXC622X accelerometer sensor API + * + ******************************************************************************* + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * THE PRESENT SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES + * OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, FOR THE SOLE + * PURPOSE TO SUPPORT YOUR APPLICATION DEVELOPMENT. + * AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY DIRECT, + * INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM THE + * CONTENT OF SUCH SOFTWARE AND/OR THE USE MADE BY CUSTOMERS OF THE CODING + * INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + * THIS SOFTWARE IS SPECIFICALLY DESIGNED FOR EXCLUSIVE USE WITH ST PARTS. + * + + ******************************************************************************/ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#ifdef CONFIG_HAS_EARLYSUSPEND +#include +#endif +#include + +#include "mxc622x.h" +#include "../sensor.h" +#ifdef CONFIG_ARCH_SC8810 +#include +#endif + +#define G_MAX 16000 /** Maximum polled-device-reported g value */ +#define WHOAMI_MXC622X_ACC 0x25 /* Expctd content for WAI */ + +/* CONTROL REGISTERS */ +#define WHO_AM_I 0x08 /* WhoAmI register */ + +#define FUZZ 32 +#define FLAT 32 +#define I2C_RETRY_DELAY 5 +#define I2C_RETRIES 5 +#define I2C_AUTO_INCREMENT 0x80 + +/* RESUME STATE INDICES */ + +#define RESUME_ENTRIES 20 +#define DEVICE_INFO "Memsic, MXC622X" +#define DEVICE_INFO_LEN 32 + +/* end RESUME STATE INDICES */ + +#define DEBUG +//#define MXC622X_DEBUG + +#define MAX_INTERVAL 50 + +#ifdef __KERNEL__ +static struct mxc622x_acc_platform_data mxc622x_plat_data = { + .poll_interval = 20, + .min_interval = 10, +}; +#endif + +#ifdef I2C_BUS_NUM_STATIC_ALLOC +static struct i2c_board_info mxc622x_i2c_boardinfo = { + I2C_BOARD_INFO(MXC622X_ACC_I2C_NAME, MXC622X_ACC_I2C_ADDR), +#ifdef __KERNEL__ + .platform_data = &mxc622x_plat_data +#endif +}; +#endif + +struct mxc622x_acc_data { + struct i2c_client *client; + struct mxc622x_acc_platform_data *pdata; + + struct mutex lock; + struct delayed_work input_work; + + struct input_dev *input_dev; + + int hw_initialized; + /* hw_working=-1 means not tested yet */ + int hw_working; + atomic_t enabled; + int on_before_suspend; + + u8 resume_state[RESUME_ENTRIES]; + +#ifdef CONFIG_HAS_EARLYSUSPEND + struct early_suspend early_suspend; +#endif +}; + +/* + * Because misc devices can not carry a pointer from driver register to + * open, we keep this global. This limits the driver to a single instance. + */ +struct mxc622x_acc_data *mxc622x_acc_misc_data; +struct i2c_client *mxc622x_i2c_client = NULL; +static struct class* l_dev_class = NULL; +struct i2c_client *this_client = NULL; +////////////////////////////////////////////////////// +struct mx622x_sensordata{ + // for control + int int_gpio; //0-3 + int op; + int samp; + int xyz_axis[3][3]; // (axis,direction) + struct proc_dir_entry* sensor_proc; + struct input_dev *input_dev; + //struct work_struct work; + struct delayed_work work; // for polling + struct workqueue_struct *queue; + int isdbg; + int sensor_samp; // + int sensor_enable; // 0 --> disable sensor, 1 --> enable sensor + int test_pass; + //int offset[3]; +#ifdef CONFIG_HAS_EARLYSUSPEND + struct early_suspend earlysuspend; +#endif + s16 offset[3+1]; /*+1: for 4-byte alignment*/ + + +}; + +static struct mx622x_sensordata l_sensorconfig = { + .op = 0, + .int_gpio = 3, + .samp = 16, + .xyz_axis = { + {ABS_X, -1}, + {ABS_Y, 1}, + {ABS_Z, -1}, + }, + .sensor_proc = NULL, + .isdbg = 0, + .sensor_samp = 1, // 1 sample/second + .sensor_enable = 1, // enable sensor + .test_pass = 0, // for test program + //.offset={0,0,0}, +}; + +////////////////////////////////////////////////////// + + +static int mxc622x_acc_i2c_read(struct mxc622x_acc_data *acc, u8 * buf, int len) +{ + int err; + int tries = 0; + + struct i2c_msg msgs[] = { + { + .addr = acc->client->addr, + .flags = acc->client->flags & I2C_M_TEN, + .len = 1, + .buf = buf, }, + { + .addr = acc->client->addr, + .flags = (acc->client->flags & I2C_M_TEN) | I2C_M_RD, + .len = len, + .buf = buf, }, + }; + + do { + err = i2c_transfer(acc->client->adapter, msgs, 2); + if (err != 2) + msleep_interruptible(I2C_RETRY_DELAY); + } while ((err != 2) && (++tries < I2C_RETRIES)); + + if (err != 2) { + dev_err(&acc->client->dev, "read transfer error\n"); + err = -EIO; + } else { + err = 0; + } + + return err; +} + +static int mxc622x_acc_i2c_write(struct mxc622x_acc_data *acc, u8 * buf, int len) +{ + int err; + int tries = 0; + + struct i2c_msg msgs[] = { { .addr = acc->client->addr, + .flags = acc->client->flags & I2C_M_TEN, + .len = len + 1, .buf = buf, }, }; + do { + err = i2c_transfer(acc->client->adapter, msgs, 1); + if (err != 1) + msleep_interruptible(I2C_RETRY_DELAY); + } while ((err != 1) && (++tries < I2C_RETRIES)); + + if (err != 1) { + dev_err(&acc->client->dev, "write transfer error\n"); + err = -EIO; + } else { + err = 0; + } + + return err; +} + +static int mxc622x_acc_hw_init(struct mxc622x_acc_data *acc) +{ + int err = -1; + u8 buf[7]; + + printk(KERN_INFO "%s: hw init start\n", MXC622X_ACC_DEV_NAME); + + buf[0] = WHO_AM_I; + err = mxc622x_acc_i2c_read(acc, buf, 1); + if (err < 0) + goto error_firstread; + else + acc->hw_working = 1; + if ((buf[0] & 0x3F) != WHOAMI_MXC622X_ACC) { + err = -1; /* choose the right coded error */ + goto error_unknown_device; + } + + acc->hw_initialized = 1; + printk(KERN_INFO "%s: hw init done\n", MXC622X_ACC_DEV_NAME); + return 0; + +error_firstread: + acc->hw_working = 0; + dev_warn(&acc->client->dev, "Error reading WHO_AM_I: is device " + "available/working?\n"); + goto error1; +error_unknown_device: + dev_err(&acc->client->dev, + "device unknown. Expected: 0x%x," + " Replies: 0x%x\n", WHOAMI_MXC622X_ACC, buf[0]); +error1: + acc->hw_initialized = 0; + dev_err(&acc->client->dev, "hw init error 0x%x,0x%x: %d\n", buf[0], + buf[1], err); + return err; +} + +static void mxc622x_acc_device_power_off(struct mxc622x_acc_data *acc) +{ + int err; + u8 buf[2] = { MXC622X_REG_CTRL, MXC622X_CTRL_PWRDN }; + + err = mxc622x_acc_i2c_write(acc, buf, 1); + if (err < 0) + dev_err(&acc->client->dev, "soft power off failed: %d\n", err); +} + +static int mxc622x_acc_device_power_on(struct mxc622x_acc_data *acc) +{ + int err = -1; + u8 buf[2] = { MXC622X_REG_CTRL, MXC622X_CTRL_PWRON }; + + err = mxc622x_acc_i2c_write(acc, buf, 1); + if (err < 0) + dev_err(&acc->client->dev, "soft power on failed: %d\n", err); + + if (!acc->hw_initialized) { + err = mxc622x_acc_hw_init(acc); + if (acc->hw_working == 1 && err < 0) { + mxc622x_acc_device_power_off(acc); + return err; + } + } + + return 0; +} + + +/* */ + +static int mxc622x_acc_register_write(struct mxc622x_acc_data *acc, u8 *buf, + u8 reg_address, u8 new_value) +{ + int err = -1; + + if (atomic_read(&acc->enabled)) { + /* Sets configuration register at reg_address + * NOTE: this is a straight overwrite */ + buf[0] = reg_address; + buf[1] = new_value; + err = mxc622x_acc_i2c_write(acc, buf, 1); + if (err < 0) + return err; + } + return err; +} + +static int mxc622x_acc_register_read(struct mxc622x_acc_data *acc, u8 *buf, + u8 reg_address) +{ + + int err = -1; + buf[0] = (reg_address); + err = mxc622x_acc_i2c_read(acc, buf, 1); + return err; +} + +static int mxc622x_acc_register_update(struct mxc622x_acc_data *acc, u8 *buf, + u8 reg_address, u8 mask, u8 new_bit_values) +{ + int err = -1; + u8 init_val; + u8 updated_val; + err = mxc622x_acc_register_read(acc, buf, reg_address); + if (!(err < 0)) { + init_val = buf[1]; + updated_val = ((mask & new_bit_values) | ((~mask) & init_val)); + err = mxc622x_acc_register_write(acc, buf, reg_address, + updated_val); + } + return err; +} + +/* */ + +static int mxc622x_acc_get_acceleration_data(struct mxc622x_acc_data *acc, + int *xyz) +{ + int err = -1; + /* Data bytes from hardware x, y */ + u8 acc_data[2]; + + acc_data[0] = MXC622X_REG_DATA; + err = mxc622x_acc_i2c_read(acc, acc_data, 2); + + if (err < 0) + { + #ifdef DEBUG + printk(KERN_INFO "%s I2C read error %d\n", MXC622X_ACC_I2C_NAME, err); + #endif + return err; + } + + xyz[0] = (signed char)acc_data[0]; + xyz[1] = (signed char)acc_data[1]; + xyz[2] = 8; //32; + + #ifdef MXC622X_DEBUG + printk("x = %d, y = %d\n", xyz[0], xyz[1]); + #endif + + #ifdef MXC622X_DEBUG + + printk(KERN_INFO "%s read x=%d, y=%d, z=%d\n", + MXC622X_ACC_DEV_NAME, xyz[0], xyz[1], xyz[2]); + printk(KERN_INFO "%s poll interval %d\n", MXC622X_ACC_DEV_NAME, acc->pdata->poll_interval); + + #endif + return err; +} + +static void mxc622x_acc_report_values(struct mxc622x_acc_data *acc, int *xyz) +{ + int txyz,tx,ty,tz = 0; + static int suf=0; // To report every merging value + + tx = xyz[l_sensorconfig.xyz_axis[0][0]]*l_sensorconfig.xyz_axis[0][1]; + ty = xyz[l_sensorconfig.xyz_axis[1][0]]*l_sensorconfig.xyz_axis[1][1]; + tz = xyz[l_sensorconfig.xyz_axis[2][0]]*l_sensorconfig.xyz_axis[2][1]; + suf++; + if (suf > 5) + { + suf = 0; + } + + // packet the x,y,z + txyz = (tx&0x00FF) | ((ty&0xFF)<<8) | ((tz&0xFF)<<16) | ((suf&0xFF)<<24); + input_report_abs(acc->input_dev, ABS_X, txyz); + /*input_report_abs(acc->input_dev, ABS_Y, xyz[1]); + input_report_abs(acc->input_dev, ABS_Z, xyz[2]);*/ + input_sync(acc->input_dev); +} + +static int mxc622x_acc_enable(struct mxc622x_acc_data *acc) +{ + int err; + + if (!atomic_cmpxchg(&acc->enabled, 0, 1)) { + err = mxc622x_acc_device_power_on(acc); + if (err < 0) { + atomic_set(&acc->enabled, 0); + return err; + } + + schedule_delayed_work(&acc->input_work, msecs_to_jiffies( + acc->pdata->poll_interval)); + } + + return 0; +} + +static int mxc622x_acc_disable(struct mxc622x_acc_data *acc) +{ + if (atomic_cmpxchg(&acc->enabled, 1, 0)) { + cancel_delayed_work_sync(&acc->input_work); + mxc622x_acc_device_power_off(acc); + } + + return 0; +} + +static int mxc622x_acc_misc_open(struct inode *inode, struct file *file) +{ + int err; + err = nonseekable_open(inode, file); + if (err < 0) + return err; + + file->private_data = mxc622x_acc_misc_data; + + return 0; +} + +static /*int*/long mxc622x_acc_misc_ioctl(/*struct inode *inode, */struct file *file, + unsigned int cmd, unsigned long arg) +{ + void __user *argp = (void __user *)arg; + //u8 buf[4]; + //u8 mask; + //u8 reg_address; + //u8 bit_values; + int err; + int interval; + int xyz[3] = {0}; + struct mxc622x_acc_data *acc = file->private_data; + unsigned int uval = 0; + +// printk(KERN_INFO "%s: %s call with cmd 0x%x and arg 0x%x\n", +// MXC622X_ACC_DEV_NAME, __func__, cmd, (unsigned int)arg); + + switch (cmd) { + case MXC622X_ACC_IOCTL_GET_DELAY: + interval = acc->pdata->poll_interval; + if (copy_to_user(argp, &interval, sizeof(interval))) + return -EFAULT; + break; + + //case MXC622X_ACC_IOCTL_SET_DELAY: + case ECS_IOCTL_APP_SET_DELAY: + if (copy_from_user(&interval, argp, sizeof(interval))) + return -EFAULT; + if (interval < 0 || interval > 1000) + return -EINVAL; + //if(interval > MAX_INTERVAL) + //interval = MAX_INTERVAL; + acc->pdata->poll_interval = max(interval, + acc->pdata->min_interval); + break; + + //case MXC622X_ACC_IOCTL_SET_ENABLE: + case ECS_IOCTL_APP_SET_AFLAG: + if (copy_from_user(&interval, argp, sizeof(interval))) + return -EFAULT; + if (interval > 1) + return -EINVAL; + if (interval) + err = mxc622x_acc_enable(acc); + else + err = mxc622x_acc_disable(acc); + return err; + break; + + case MXC622X_ACC_IOCTL_GET_ENABLE: + interval = atomic_read(&acc->enabled); + if (copy_to_user(argp, &interval, sizeof(interval))) + return -EINVAL; + break; + case MXC622X_ACC_IOCTL_GET_COOR_XYZ: + err = mxc622x_acc_get_acceleration_data(acc, xyz); + if (err < 0) + return err; + #ifdef DEBUG + // printk(KERN_ALERT "%s Get coordinate xyz:[%d, %d, %d]\n", + // __func__, xyz[0], xyz[1], xyz[2]); + #endif + if (copy_to_user(argp, xyz, sizeof(xyz))) { + printk(KERN_ERR " %s %d error in copy_to_user \n", + __func__, __LINE__); + return -EINVAL; + } + break; + case MXC622X_ACC_IOCTL_GET_CHIP_ID: + { + u8 devid = 0; + u8 devinfo[DEVICE_INFO_LEN] = {0}; + err = mxc622x_acc_register_read(acc, &devid, WHO_AM_I); + if (err < 0) { + printk("%s, error read register WHO_AM_I\n", __func__); + return -EAGAIN; + } + sprintf(devinfo, "%s, %#x", DEVICE_INFO, devid); + + if (copy_to_user(argp, devinfo, sizeof(devinfo))) { + printk("%s error in copy_to_user(IOCTL_GET_CHIP_ID)\n", __func__); + return -EINVAL; + } + } + break; + case WMT_IOCTL_SENSOR_GET_DRVID: + uval = MXC622X_DRVID; + if (copy_to_user((unsigned int*)arg, &uval, sizeof(unsigned int))) + { + return -EFAULT; + } + dbg("mxc622x_driver_id:%d\n",uval); + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int mxc622x_acc_misc_close(struct inode *inode, struct file *filp) +{ + return 0; +} + + +static const struct file_operations mxc622x_acc_misc_fops = { + .owner = THIS_MODULE, + .open = mxc622x_acc_misc_open, + .unlocked_ioctl = mxc622x_acc_misc_ioctl, + .release = mxc622x_acc_misc_close, +}; + +static struct miscdevice mxc622x_acc_misc_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = GSENSOR_DEV_NODE,//MXC622X_ACC_DEV_NAME, + .fops = &mxc622x_acc_misc_fops, +}; + +static void mxc622x_acc_input_work_func(struct work_struct *work) +{ + struct mxc622x_acc_data *acc; + + int xyz[3] = { 0 }; + int err; + + acc = mxc622x_acc_misc_data; /*container_of((struct delayed_work *)work, + struct mxc622x_acc_data, input_work); */ + + mutex_lock(&acc->lock); + err = mxc622x_acc_get_acceleration_data(acc, xyz); + if (err < 0) + dev_err(&acc->client->dev, "get_acceleration_data failed\n"); + else + mxc622x_acc_report_values(acc, xyz); + + schedule_delayed_work(&acc->input_work, msecs_to_jiffies( + acc->pdata->poll_interval)); + mutex_unlock(&acc->lock); +} + +#ifdef MXC622X_OPEN_ENABLE +int mxc622x_acc_input_open(struct input_dev *input) +{ + struct mxc622x_acc_data *acc = input_get_drvdata(input); + + return mxc622x_acc_enable(acc); +} + +void mxc622x_acc_input_close(struct input_dev *dev) +{ + struct mxc622x_acc_data *acc = input_get_drvdata(dev); + + mxc622x_acc_disable(acc); +} +#endif + +static int mxc622x_acc_validate_pdata(struct mxc622x_acc_data *acc) +{ + acc->pdata->poll_interval = max(acc->pdata->poll_interval, + acc->pdata->min_interval); + + /* Enforce minimum polling interval */ + if (acc->pdata->poll_interval < acc->pdata->min_interval) { + dev_err(&acc->client->dev, "minimum poll interval violated\n"); + return -EINVAL; + } + + return 0; +} + +static int mxc622x_acc_input_init(struct mxc622x_acc_data *acc) +{ + int err; + // Polling rx data when the interrupt is not used. + if (1/*acc->irq1 == 0 && acc->irq1 == 0*/) { + INIT_DELAYED_WORK(&acc->input_work, mxc622x_acc_input_work_func); + } + + acc->input_dev = input_allocate_device(); + if (!acc->input_dev) { + err = -ENOMEM; + dev_err(&acc->client->dev, "input device allocate failed\n"); + goto err0; + } + +#ifdef MXC622X_ACC_OPEN_ENABLE + acc->input_dev->open = mxc622x_acc_input_open; + acc->input_dev->close = mxc622x_acc_input_close; +#endif + + input_set_drvdata(acc->input_dev, acc); + + set_bit(EV_ABS, acc->input_dev->evbit); + + input_set_abs_params(acc->input_dev, ABS_X, -G_MAX, G_MAX, FUZZ, FLAT); + input_set_abs_params(acc->input_dev, ABS_Y, -G_MAX, G_MAX, FUZZ, FLAT); + input_set_abs_params(acc->input_dev, ABS_Z, -G_MAX, G_MAX, FUZZ, FLAT); + + acc->input_dev->name = GSENSOR_INPUT_NAME;//MXC622X_ACC_INPUT_NAME; + + err = input_register_device(acc->input_dev); + if (err) { + dev_err(&acc->client->dev, + "unable to register input polled device %s\n", + acc->input_dev->name); + goto err1; + } + + return 0; + +err1: + input_free_device(acc->input_dev); +err0: + return err; +} + +static void mxc622x_acc_input_cleanup(struct mxc622x_acc_data *acc) +{ + input_unregister_device(acc->input_dev); + input_free_device(acc->input_dev); +} + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void mxc622x_early_suspend (struct early_suspend* es); +static void mxc622x_early_resume (struct early_suspend* es); +#endif + +static int is_mxc622x(struct i2c_client *client) +{ + int tempvalue; + + /* read chip id */ + tempvalue = i2c_smbus_read_word_data(client, WHO_AM_I); + if ((tempvalue & 0x003F) == WHOAMI_MXC622X_ACC) { + //printk(KERN_INFO "%s I2C driver registered!\n", + // MXC622X_ACC_DEV_NAME); + return 1; + } + return 0; +} + +static int mxc622x_acc_probe(struct i2c_client *client) +{ + + struct mxc622x_acc_data *acc; + + int err = -1; + int tempvalue; + + pr_info("%s: probe start.\n", MXC622X_ACC_DEV_NAME); + + /*if (client->dev.platform_data == NULL) { + dev_err(&client->dev, "platform data is NULL. exiting.\n"); + err = -ENODEV; + goto exit_check_functionality_failed; + }*/ + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(&client->dev, "client not i2c capable\n"); + err = -ENODEV; + goto exit_check_functionality_failed; + } + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA)) { + dev_err(&client->dev, "client not smb-i2c capable:2\n"); + err = -EIO; + goto exit_check_functionality_failed; + } + + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_I2C_BLOCK)){ + dev_err(&client->dev, "client not smb-i2c capable:3\n"); + err = -EIO; + goto exit_check_functionality_failed; + } + /* + * OK. From now, we presume we have a valid client. We now create the + * client structure, even though we cannot fill it completely yet. + */ + + acc = kzalloc(sizeof(struct mxc622x_acc_data), GFP_KERNEL); + if (acc == NULL) { + err = -ENOMEM; + dev_err(&client->dev, + "failed to allocate memory for module data: " + "%d\n", err); + goto exit_alloc_data_failed; + } + + mutex_init(&acc->lock); + mutex_lock(&acc->lock); + + acc->client = client; + mxc622x_i2c_client = client; + i2c_set_clientdata(client, acc); + + /* read chip id */ + tempvalue = i2c_smbus_read_word_data(client, WHO_AM_I); + + if ((tempvalue & 0x003F) == WHOAMI_MXC622X_ACC) { + printk(KERN_INFO "%s I2C driver registered!\n", + MXC622X_ACC_DEV_NAME); + } else { + acc->client = NULL; + printk(KERN_INFO "I2C driver not registered!" + " Device unknown 0x%x\n", tempvalue); + goto err_mutexunlockfreedata; + } + acc->pdata = kzalloc(sizeof(struct mxc622x_acc_platform_data), GFP_KERNEL); + if (acc->pdata == NULL) { + err = -ENOMEM; + dev_err(&client->dev, + "failed to allocate memory for pdata: %d\n", + err); + goto exit_kfree_pdata; + } + + //memcpy(acc->pdata, client->dev.platform_data, sizeof(*acc->pdata)); + acc->pdata->poll_interval = 20; + acc->pdata->min_interval = 10; + + err = mxc622x_acc_validate_pdata(acc); + if (err < 0) { + dev_err(&client->dev, "failed to validate platform data\n"); + goto exit_kfree_pdata; + } + + i2c_set_clientdata(client, acc); + + + /*if (acc->pdata->init) { + err = acc->pdata->init(); + if (err < 0) { + dev_err(&client->dev, "init failed: %d\n", err); + goto err2; + } + }*/ + + err = mxc622x_acc_device_power_on(acc); + if (err < 0) { + dev_err(&client->dev, "power on failed: %d\n", err); + goto err2; + } + + atomic_set(&acc->enabled, 1); + + err = mxc622x_acc_input_init(acc); + if (err < 0) { + dev_err(&client->dev, "input init failed\n"); + goto err_power_off; + } + mxc622x_acc_misc_data = acc; + + err = misc_register(&mxc622x_acc_misc_device); + if (err < 0) { + dev_err(&client->dev, + "misc MXC622X_ACC_DEV_NAME register failed\n"); + goto err_input_cleanup; + } + + mxc622x_acc_device_power_off(acc); + + /* As default, do not report information */ + atomic_set(&acc->enabled, 0); + + acc->on_before_suspend = 0; + + #ifdef CONFIG_HAS_EARLYSUSPEND + acc->early_suspend.suspend = mxc622x_early_suspend; + acc->early_suspend.resume = mxc622x_early_resume; + acc->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN; + register_early_suspend(&acc->early_suspend); +#endif + + mutex_unlock(&acc->lock); + + dev_info(&client->dev, "%s: probed\n", MXC622X_ACC_DEV_NAME); + + return 0; + +err_input_cleanup: + mxc622x_acc_input_cleanup(acc); +err_power_off: + mxc622x_acc_device_power_off(acc); +err2: + if (acc->pdata->exit) acc->pdata->exit(); +exit_kfree_pdata: + kfree(acc->pdata); +err_mutexunlockfreedata: + kfree(acc); + mutex_unlock(&acc->lock); + i2c_set_clientdata(client, NULL); + mxc622x_acc_misc_data = NULL; +exit_alloc_data_failed: +exit_check_functionality_failed: + printk(KERN_ERR "%s: Driver Init failed\n", MXC622X_ACC_DEV_NAME); + return err; +} + +static int __devexit mxc622x_acc_remove(struct i2c_client *client) +{ + /* TODO: revisit ordering here once _probe order is finalized */ + struct mxc622x_acc_data *acc = mxc622x_acc_misc_data;//i2c_get_clientdata(client); + + misc_deregister(&mxc622x_acc_misc_device); + mxc622x_acc_input_cleanup(acc); + mxc622x_acc_device_power_off(acc); + if (acc->pdata->exit) + acc->pdata->exit(); + kfree(acc->pdata); + kfree(acc); + + return 0; +} + +static int mxc622x_acc_resume(struct platform_device *pdev) +{ + struct mxc622x_acc_data *acc = mxc622x_acc_misc_data; + + if (acc != NULL && acc->on_before_suspend) { + acc->on_before_suspend = 0; + acc->hw_initialized = 0; + return mxc622x_acc_enable(acc); + } + return 0; +} + +static int mxc622x_acc_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct mxc622x_acc_data *acc = mxc622x_acc_misc_data; + if (acc != NULL) { + if (atomic_read(&acc->enabled)) { + acc->on_before_suspend = 1; + return mxc622x_acc_disable(acc); + } + } + return 0; +} + +static int mxc622x_probe(struct platform_device *pdev) +{ + return 0; +} + +static int mxc622x_remove(struct platform_device *pdev) +{ + return 0; +} + +static struct platform_driver mxc622x_driver = { + .probe = mxc622x_probe, + .remove = mxc622x_remove, + .suspend = mxc622x_acc_suspend, + .resume = mxc622x_acc_resume, + .driver = { + .name = GSENSOR_I2C_NAME, + }, +}; + + +#ifdef CONFIG_HAS_EARLYSUSPEND + +static void mxc622x_early_suspend (struct early_suspend* es) +{ + struct mxc622x_acc_data *acc = mxc622x_acc_misc_data; //i2c_get_clientdata(client); +#ifdef MXC622X_DEBUG + printk("%s.\n", __func__); +#endif + if (acc != NULL) { + if (atomic_read(&acc->enabled)) { + acc->on_before_suspend = 1; + return mxc622x_acc_disable(acc); + } + } +} + +static void mxc622x_early_resume (struct early_suspend* es) +{ + struct mxc622x_acc_data *acc = mxc622x_acc_misc_data; //i2c_get_clientdata(client); +#ifdef MXC622X_DEBUG + printk("%s.\n", __func__); +#endif + + if (acc != NULL && acc->on_before_suspend) { + acc->on_before_suspend = 0; + acc->hw_initialized = 0; + return mxc622x_acc_enable(acc); + } + +} + +#endif /* CONFIG_HAS_EARLYSUSPEND */ + +static const struct i2c_device_id mxc622x_acc_id[] + = { { MXC622X_ACC_DEV_NAME, 0 }, { }, }; + +MODULE_DEVICE_TABLE(i2c, mxc622x_acc_id); + +#if 0 +static struct i2c_driver mxc622x_acc_driver = { + .driver = { + .name = MXC622X_ACC_I2C_NAME, + }, + .probe = mxc622x_acc_probe, + .remove = __devexit_p(mxc622x_acc_remove), + .resume = mxc622x_acc_resume, + .suspend = mxc622x_acc_suspend, + .id_table = mxc622x_acc_id, +}; +#endif + +#ifdef I2C_BUS_NUM_STATIC_ALLOC + +int i2c_static_add_device(struct i2c_board_info *info) +{ + struct i2c_adapter *adapter; + struct i2c_client *client; + int err; + + adapter = i2c_get_adapter(I2C_STATIC_BUS_NUM); + if (!adapter) { + pr_err("%s: can't get i2c adapter\n", __FUNCTION__); + err = -ENODEV; + goto i2c_err; + } + + client = i2c_new_device(adapter, info); + if (!client) { + pr_err("%s: can't add i2c device at 0x%x\n", + __FUNCTION__, (unsigned int)info->addr); + err = -ENODEV; + goto i2c_err; + } + + i2c_put_adapter(adapter); + + return 0; + +i2c_err: + return err; +} + +#endif /*I2C_BUS_NUM_STATIC_ALLOC*/ + +static void mxc622x_platform_release(struct device *device) +{ + dbg("...\n"); + return; +} + + +static struct platform_device mxc622x_device = { + .name = GSENSOR_I2C_NAME, + .id = 0, + .dev = { + .release = mxc622x_platform_release, + }, +}; + +extern int wmt_getsyspara(char *varname, unsigned char *varval, int *varlen); +static int get_axisset(void* param) +{ + char varbuf[64]; + int n; + int varlen; + + int tmp_offset[3] = {0}; + memset(varbuf, 0, sizeof(varbuf)); + varlen = sizeof(varbuf); + if (wmt_getsyspara("wmt.io.mxc622xsensor", varbuf, &varlen)) { + errlog("Can't get gsensor config in u-boot!!!!\n"); + return -1; + } else { + n = sscanf(varbuf, "%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d", + &l_sensorconfig.op, + &l_sensorconfig.int_gpio, + &l_sensorconfig.samp, + &(l_sensorconfig.xyz_axis[0][0]), + &(l_sensorconfig.xyz_axis[0][1]), + &(l_sensorconfig.xyz_axis[1][0]), + &(l_sensorconfig.xyz_axis[1][1]), + &(l_sensorconfig.xyz_axis[2][0]), + &(l_sensorconfig.xyz_axis[2][1]), + &tmp_offset[0], + &tmp_offset[1], + &tmp_offset[2] + ); + if (n != 12) { + printk(KERN_ERR "gsensor format is error in u-boot!!!\n"); + return -1; + } + l_sensorconfig.offset[0] = tmp_offset[0]; + l_sensorconfig.offset[1] = tmp_offset[1]; + l_sensorconfig.offset[2] = tmp_offset[2]; + l_sensorconfig.sensor_samp = l_sensorconfig.samp; + + dbg("get the sensor config: %d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d\n", + l_sensorconfig.op, + l_sensorconfig.int_gpio, + l_sensorconfig.samp, + l_sensorconfig.xyz_axis[0][0], + l_sensorconfig.xyz_axis[0][1], + l_sensorconfig.xyz_axis[1][0], + l_sensorconfig.xyz_axis[1][1], + l_sensorconfig.xyz_axis[2][0], + l_sensorconfig.xyz_axis[2][1], + l_sensorconfig.offset[0], + l_sensorconfig.offset[1], + l_sensorconfig.offset[2] + ); + } + return 0; +} + + +static int __init mxc622x_acc_init(void) +{ + int ret = 0; + + printk(KERN_INFO "%s accelerometer driver: init\n", + MXC622X_ACC_I2C_NAME); + ret = get_axisset(NULL); + if (ret < 0) + { + printk("<<<<<%s user choose to no sensor chip!\n", __func__); + return ret; + } + + if (!(this_client = sensor_i2c_register_device(0, GSENSOR_I2C_ADDR, GSENSOR_I2C_NAME))) + { + printk(KERN_EMERG"Can't register gsensor i2c device!\n"); + return -1; + } + if(!is_mxc622x(this_client)) + { + dbg("isn't mxc622x gsensor!\n"); + return -1; + } + // parse g-sensor u-boot arg + + /*if (ret) + { + errlog("only for test!\n"); + return -1; + }*/ +#ifdef I2C_BUS_NUM_STATIC_ALLOC + ret = i2c_static_add_device(&mxc622x_i2c_boardinfo); + if (ret < 0) { + pr_err("%s: add i2c device error %d\n", __FUNCTION__, ret); + goto init_err; + } +#endif + ret = mxc622x_acc_probe(this_client); + if (ret) + { + sensor_i2c_unregister_device(this_client); + return -1; + } + // create the platform device + l_dev_class = class_create(THIS_MODULE, GSENSOR_I2C_NAME); + if (IS_ERR(l_dev_class)){ + ret = PTR_ERR(l_dev_class); + printk(KERN_ERR "Can't class_create gsensor device !!\n"); + return ret; + } + if((ret = platform_device_register(&mxc622x_device))) + { + klog("Can't register mc3230 platform devcie!!!\n"); + return ret; + } + if ((ret = platform_driver_register(&mxc622x_driver)) != 0) + { + errlog("Can't register mc3230 platform driver!!!\n"); + return ret; + } + + //return i2c_add_driver(&mxc622x_acc_driver); + +init_err: + return ret; +} + +static void __exit mxc622x_acc_exit(void) +{ + //printk(KERN_INFO "%s accelerometer driver exit\n", MXC622X_ACC_DEV_NAME); + + platform_driver_unregister(&mxc622x_driver); + platform_device_unregister(&mxc622x_device); + class_destroy(l_dev_class); + mxc622x_acc_remove(mxc622x_i2c_client); + sensor_i2c_unregister_device(this_client); + #ifdef I2C_BUS_NUM_STATIC_ALLOC + i2c_unregister_device(mxc622x_i2c_client); + #endif + + //i2c_del_driver(&mxc622x_acc_driver); + return; +} + +module_init(mxc622x_acc_init); +module_exit(mxc622x_acc_exit); + +MODULE_DESCRIPTION("mxc622x accelerometer misc driver"); +MODULE_AUTHOR("Memsic"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/input/sensor/mxc622x_gsensor/mxc622x.h b/drivers/input/sensor/mxc622x_gsensor/mxc622x.h new file mode 100755 index 00000000..2b83bb7b --- /dev/null +++ b/drivers/input/sensor/mxc622x_gsensor/mxc622x.h @@ -0,0 +1,83 @@ + +/******************** (C) COPYRIGHT 2010 STMicroelectronics ******************** +* +* File Name : mxc622x.h +* Authors : MH - C&I BU - Application Team +* +******************************************************************************** +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +* +* THE PRESENT SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES +* OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, FOR THE SOLE +* PURPOSE TO SUPPORT YOUR APPLICATION DEVELOPMENT. +* AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY DIRECT, +* INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM THE +* CONTENT OF SUCH SOFTWARE AND/OR THE USE MADE BY CUSTOMERS OF THE CODING +* INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. +* +* THIS SOFTWARE IS SPECIFICALLY DESIGNED FOR EXCLUSIVE USE WITH ST PARTS. +* +*******************************************************************************/ + + +#ifndef __MXC622X_H__ +#define __MXC622X_H__ + +#include /* For IOCTL macros */ +#include + +#ifndef DEBUG +#define DEBUG +#endif + +#define GSENSOR_I2C_NAME "mxc622x" +#define GSENSOR_I2C_ADDR 0x15 + +#define MXC622X_ACC_IOCTL_BASE 77 +/** The following define the IOCTL command values via the ioctl macros */ +#define MXC622X_ACC_IOCTL_SET_DELAY _IOW(MXC622X_ACC_IOCTL_BASE, 0, int) +#define MXC622X_ACC_IOCTL_GET_DELAY _IOR(MXC622X_ACC_IOCTL_BASE, 1, int) +#define MXC622X_ACC_IOCTL_SET_ENABLE _IOW(MXC622X_ACC_IOCTL_BASE, 2, int) +#define MXC622X_ACC_IOCTL_GET_ENABLE _IOR(MXC622X_ACC_IOCTL_BASE, 3, int) +#define MXC622X_ACC_IOCTL_GET_COOR_XYZ _IOW(MXC622X_ACC_IOCTL_BASE, 22, int) +#define MXC622X_ACC_IOCTL_GET_CHIP_ID _IOR(MXC622X_ACC_IOCTL_BASE, 255, char[32]) + +/************************************************/ +/* Accelerometer defines section */ +/************************************************/ +#define MXC622X_ACC_DEV_NAME "mxc622x" +#define MXC622X_ACC_INPUT_NAME "accelerometer" +#define MXC622X_ACC_I2C_ADDR 0x15 +#define MXC622X_ACC_I2C_NAME MXC622X_ACC_DEV_NAME + +/* MXC622X register address */ +#define MXC622X_REG_CTRL 0x04 +#define MXC622X_REG_DATA 0x00 + +/* MXC622X control bit */ +#define MXC622X_CTRL_PWRON 0x00 /* power on */ +#define MXC622X_CTRL_PWRDN 0x80 /* power donw */ + +//#if defined(CONFIG_MACH_SP6810A) +//#define I2C_BUS_NUM_STATIC_ALLOC +#define I2C_STATIC_BUS_NUM ( 0) // Need to be modified according to actual setting +//#endif + +struct mxc622x_acc_platform_data { + int poll_interval; + int min_interval; + + int (*init)(void); + void (*exit)(void); + int (*power_on)(void); + int (*power_off)(void); + +}; + +#endif /* __MXC622X_H__ */ + + + diff --git a/drivers/input/sensor/sensor.c b/drivers/input/sensor/sensor.c new file mode 100755 index 00000000..5a82a9fb --- /dev/null +++ b/drivers/input/sensor/sensor.c @@ -0,0 +1,114 @@ +#include +#include +//#include +#include "sensor.h" +//DEFINE_MUTEX(mutex_client); +//static struct i2c_client *sensor_client=NULL; +struct i2c_client *sensor_i2c_register_device(int bus_no, int client_addr, const char *client_name) +{ + struct i2c_adapter *adapter = NULL; + struct i2c_client *sensor_client=NULL; + + struct i2c_board_info sensor_i2c_board_info = { + .type = "unused", + .flags = 0x00, + .addr = 0xff, + .platform_data = NULL, + .archdata = NULL, + .irq = -1, + }; + + if ((bus_no<0) || (client_addr>0x7f) || (client_addr<0)|| (!client_name)) + { + printk(KERN_ERR "%s param error! pls check out!\n", __FUNCTION__); + return NULL; + } + printk(KERN_INFO "%s busno %d client_addr 0x%x client_name %s \n", __FUNCTION__, \ + bus_no, client_addr, client_name); + + sensor_i2c_board_info.addr = client_addr; + //sensor_i2c_board_info.type = client_name; + strcpy(sensor_i2c_board_info.type, client_name); + + adapter = i2c_get_adapter(bus_no);/*in bus NR*/ + + if (NULL == adapter) { + printk("can not get i2c adapter, client address error\n"); + return NULL; + } + + //mutex_lock(&mutex_client); + sensor_client = i2c_new_device(adapter, &sensor_i2c_board_info); + + + if (sensor_client == NULL) { + printk("allocate i2c client failed\n"); + //mutex_unlock(&mutex_client); + return NULL; + } + i2c_put_adapter(adapter); + //mutex_unlock(&mutex_client); + + return sensor_client; +} +EXPORT_SYMBOL(sensor_i2c_register_device); + +struct i2c_client *sensor_i2c_register_device2(int bus_no, int client_addr, const char *client_name,void *pdata) +{ + struct i2c_adapter *adapter = NULL; + struct i2c_client *sensor_client=NULL; + + struct i2c_board_info sensor_i2c_board_info = { + .type = "unused", + .flags = 0x00, + .addr = 0xff, + .platform_data = NULL, + .archdata = NULL, + .irq = -1, + }; + + if ((bus_no<0) || (client_addr>0x7f) || (client_addr<0)|| (!client_name)) + { + printk(KERN_ERR "%s param error! pls check out!\n", __FUNCTION__); + return NULL; + } + printk(KERN_INFO "%s busno %d client_addr 0x%x client_name %s \n", __FUNCTION__, \ + bus_no, client_addr, client_name); + + sensor_i2c_board_info.addr = client_addr; + sensor_i2c_board_info.platform_data = pdata; + //sensor_i2c_board_info.type = client_name; + strcpy(sensor_i2c_board_info.type, client_name); + + adapter = i2c_get_adapter(bus_no);/*in bus NR*/ + + if (NULL == adapter) { + printk("can not get i2c adapter, client address error\n"); + return NULL; + } + + //mutex_lock(&mutex_client); + sensor_client = i2c_new_device(adapter, &sensor_i2c_board_info); + + + if (sensor_client == NULL) { + printk("allocate i2c client failed\n"); + //mutex_unlock(&mutex_client); + return NULL; + } + i2c_put_adapter(adapter); + //mutex_unlock(&mutex_client); + + return sensor_client; +} +EXPORT_SYMBOL(sensor_i2c_register_device2); + +void sensor_i2c_unregister_device(struct i2c_client *client) +{ + if (client != NULL) + { + i2c_unregister_device(client); + } +} +EXPORT_SYMBOL(sensor_i2c_unregister_device); + diff --git a/drivers/input/sensor/sensor.h b/drivers/input/sensor/sensor.h new file mode 100755 index 00000000..9a51433d --- /dev/null +++ b/drivers/input/sensor/sensor.h @@ -0,0 +1,91 @@ +#ifndef __SENSOR_H__ +#define __SENSOR_H__ +#include +#include +#include +#include +#include + +#ifdef CONFIG_HAS_EARLYSUSPEND +#include +#endif + + +//#define GSENSOR_I2C_NAME "unused" +//#define GSENSOR_I2C_ADDR 0xff + + +#define GSENSOR_PROC_NAME "gsensor_config" +#define GSENSOR_INPUT_NAME "g-sensor" +#define GSENSOR_DEV_NODE "sensor_ctrl" + +#define SENSOR_PROC_NAME "lsensor_config" +#define SENSOR_INPUT_NAME "l-sensor" +#define SENSOR_DEV_NODE "lsensor_ctrl" + +#undef dbg +#define dbg(fmt, args...) if (l_sensorconfig.isdbg) printk(KERN_ALERT "[%s]: " fmt, __FUNCTION__, ## args) + +#undef errlog +#undef klog +#define errlog(fmt, args...) printk(KERN_ERR "[%s]: " fmt, __FUNCTION__, ## args) +#define klog(fmt, args...) printk(KERN_ALERT "[%s]: " fmt, __FUNCTION__, ## args) + +enum gsensor_id +{ + MMA7660_DRVID = 0, + MC3230_DRVID , + DMARD08_DRVID , + DMARD06_DRVID , + DMARD10_DRVID , + MXC622X_DRVID , + MMA8452Q_DRVID , + STK8312_DRVID , + KIONIX_DRVID, + DMARD09_DRVID , + //add new gsensor id here, must be in order +}; + +#define ISL29023_DRVID 0 + +struct wmt_gsensor_data{ + // for control + int int_gpio; //0-3 + int op; + int samp; + int xyz_axis[3][2]; // (axis,direction) + struct proc_dir_entry* sensor_proc; + struct input_dev *input_dev; + //struct work_struct work; + struct delayed_work work; // for polling + struct workqueue_struct *queue; + int isdbg; + int sensor_samp; // + int sensor_enable; // 0 --> disable sensor, 1 --> enable sensor + int test_pass; + int offset[3]; + struct i2c_client *client; +#ifdef CONFIG_HAS_EARLYSUSPEND + struct early_suspend earlysuspend; +#endif + +}; + +///////////////////////// ioctrl cmd //////////////////////// +#define WMTGSENSOR_IOCTL_MAGIC 0x09 +#define WMT_IOCTL_SENSOR_CAL_OFFSET _IOW(WMTGSENSOR_IOCTL_MAGIC, 0x01, int) //offset calibration +#define ECS_IOCTL_APP_SET_AFLAG _IOW(WMTGSENSOR_IOCTL_MAGIC, 0x02, short) +#define ECS_IOCTL_APP_SET_DELAY _IOW(WMTGSENSOR_IOCTL_MAGIC, 0x03, short) +#define WMT_IOCTL_SENSOR_GET_DRVID _IOW(WMTGSENSOR_IOCTL_MAGIC, 0x04, unsigned int) +#define WMT_IOCTL_SENOR_GET_RESOLUTION _IOR(WMTGSENSOR_IOCTL_MAGIC, 0x05, short) + +#define WMT_LSENSOR_IOCTL_MAGIC 0x10 +#define LIGHT_IOCTL_SET_ENABLE _IOW(WMT_LSENSOR_IOCTL_MAGIC, 0x01, short) + +/* Function prototypes */ +extern struct i2c_client *sensor_i2c_register_device (int bus_no, int client_addr, const char *client_name); +extern struct i2c_client *sensor_i2c_register_device2(int bus_no, int client_addr, const char *client_name,void *pdata); +extern void sensor_i2c_unregister_device(struct i2c_client *client); + + +#endif diff --git a/drivers/input/sensor/stk3310/Makefile b/drivers/input/sensor/stk3310/Makefile new file mode 100755 index 00000000..19a79f84 --- /dev/null +++ b/drivers/input/sensor/stk3310/Makefile @@ -0,0 +1,34 @@ +KERNELDIR=../../../../ +#KERNELDIR=/home/hangyan/android8850/kernel/ANDROID_3.0.8 +CROSS = arm_1103_le- +CC= $(CROSS)gcc +LD= $(CROSS)ld +STRIP = $(CROSS)strip + +DEBUG = n + +# Add your debugging flag (or not) to EXTRA_CFLAGS +ifeq ($(DEBUG),y) +# DEBFLAGS = -O -g -DSCULL_DEBUG # "-O" is needed to expand inlines +DEBFLAGS = -O0 -g -DSCULL_DEBUG # "-O" is needed to expand inlines + +else + DEBFLAGS = -O2 -Wall +endif + +EXTRA_CFLAGS += $(DEBFLAGS) + + +MY_MODULE_NAME=s_wmt_lsensor_stk3310 + +obj-m := $(MY_MODULE_NAME).o +$(MY_MODULE_NAME)-objs := stk3310.o + +default: + $(MAKE) -C $(KERNELDIR) SUBDIRS=$(PWD) modules + $(STRIP) --strip-debug $(MY_MODULE_NAME).ko + rm -rf *.o *~ core .depend .*.cmd *.mod.c .tmp_versions + +clean: + rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions *.order *.symvers + diff --git a/drivers/input/sensor/stk3310/stk3310.c b/drivers/input/sensor/stk3310/stk3310.c new file mode 100755 index 00000000..4e9fde10 --- /dev/null +++ b/drivers/input/sensor/stk3310/stk3310.c @@ -0,0 +1,644 @@ +/* + * stk3310.c - stk3310 ALS & Proximity Driver + * + * By Intersil Corp + * Michael DiGioia + * + * Based on isl29011.c + * by Mike DiGioia + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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; version 2 of the License. + * + * 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, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include +#include +#include "../sensor.h" +#define SENSOR_I2C_NAME "stk3310" +#define SENSOR_I2C_ADDR 0x48 + +#undef dbg +#define dbg(fmt, args...) +#undef errlog +#undef klog +#define errlog(fmt, args...) printk(KERN_ERR "[%s]: " fmt, __FUNCTION__, ## args) +#define klog(fmt, args...) printk(KERN_ALERT "[%s]: " fmt, __FUNCTION__, ## args) +/* Insmod parameters */ +//I2C_CLIENT_INSMOD_1(stk3310); + +#define MODULE_NAME "stk3310" + + +struct stk_device { + struct input_polled_dev* input_poll_devl; + struct input_polled_dev* input_poll_devp; + struct i2c_client* client; +#ifdef CONFIG_HAS_EARLYSUSPEND + struct early_suspend earlysuspend; +#endif + +}; + +static struct stk_device* l_sensorconfig = NULL; +static int l_enable = 1; // 0:don't report data +static int p_enable = 1; // 0:don't report data + +static struct i2c_client *this_client = NULL; + +static DEFINE_MUTEX(mutex); + +static int isl_get_lux_datal(struct i2c_client* client) +{ + __u16 resH, resL; + resL = i2c_smbus_read_byte_data(client, 0x14); + resH = i2c_smbus_read_byte_data(client, 0x13); + if ((resL < 0) || (resH < 0)) + { + errlog("Error to read lux_data!\n"); + return -1; + } + return (resH <<8 | resL) ;//* idev->range / idev->resolution; +} + + +static int isl_get_lux_datap(struct i2c_client* client) +{ + __u16 resH, resL; + resL = i2c_smbus_read_byte_data(client, 0x12); + resH = i2c_smbus_read_byte_data(client, 0x11); + if ((resL < 0) || (resH < 0)) + { + errlog("Error to read lux_data!\n"); + return -1; + } + //return (resH <<8 | resL) ;//* idev->range / idev->resolution; + if(resH>0) + return 0; + else + return 6; +} + +//#define PXM 0 +static int isl_set_default_config(struct i2c_client *client) +{ + int ret=0; + unsigned char regval; +//#if PXM + ret = i2c_smbus_write_byte_data(client, 0, (1 << 1)); + if(p_enable) + { + regval = i2c_smbus_read_byte_data(l_sensorconfig->client, 0); + regval |= (1 << 0); + i2c_smbus_write_byte_data(l_sensorconfig->client, 0, regval); + } + //ret = i2c_smbus_write_byte_data(client, 0, (1 << 0)); +//#else +//#endif + if (ret < 0) + return -EINVAL; + return 0; +} + +/* Return 0 if detection is successful, -ENODEV otherwise */ +static int stk3310_detect(struct i2c_client *client/*, int kind, + struct i2c_board_info *info*/) +{ + int device; + + device= i2c_smbus_read_byte_data(client, 0x3e); + if(0x13==device) + { + printk(KERN_ALERT "stk3310 detected OK\n"); + return 0; + } + else + return -1; +} + +int isl_input_open(struct input_dev* input) +{ + return 0; +} + +void isl_input_close(struct input_dev* input) +{ +} + +static void isl_input_lux_poll_l(struct input_polled_dev *dev) +{ + struct stk_device* idev = dev->private; + struct input_dev* input = idev->input_poll_devl->input; + struct i2c_client* client = idev->client; + if (l_enable != 0) + { + mutex_lock(&mutex); + //printk(KERN_ALERT "by flashchen val is %x",val); + input_report_abs(input, ABS_MISC, isl_get_lux_datal(client)); + input_sync(input); + mutex_unlock(&mutex); + } +} + +static void isl_input_lux_poll_p(struct input_polled_dev *dev) +{ + struct stk_device* idev = dev->private; + struct input_dev* input = idev->input_poll_devp->input; + struct i2c_client* client = idev->client; + if (p_enable != 0) + { + mutex_lock(&mutex); + //printk(KERN_ALERT "by flashchen val is %x",val); + input_report_abs(input, ABS_MISC, isl_get_lux_datap(client)); + input_sync(input); + mutex_unlock(&mutex); + } +} + +static struct i2c_device_id stk3310_id[] = { + {"stk3310", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, stk3310_id); + + +static int mmad_open(struct inode *inode, struct file *file) +{ + dbg("Open the l-sensor node...\n"); + return 0; +} + +static int mmad_release(struct inode *inode, struct file *file) +{ + dbg("Close the l-sensor node...\n"); + return 0; +} + +static ssize_t mmadl_read(struct file *fl, char __user *buf, size_t cnt, loff_t *lf) +{ + int lux_data = 0; + + mutex_lock(&mutex); + lux_data = isl_get_lux_datal(l_sensorconfig->client); + mutex_unlock(&mutex); + if (lux_data < 0) + { + errlog("Failed to read lux data!\n"); + return -1; + } + printk(KERN_ALERT "lux_data is %x\n",lux_data); + return 0; + copy_to_user(buf, &lux_data, sizeof(lux_data)); + return sizeof(lux_data); +} + + +static ssize_t mmadp_read(struct file *fl, char __user *buf, size_t cnt, loff_t *lf) +{ + int lux_data = 0; + + mutex_lock(&mutex); + lux_data = isl_get_lux_datap(l_sensorconfig->client); + mutex_unlock(&mutex); + if (lux_data < 0) + { + errlog("Failed to read lux data!\n"); + return -1; + } + printk(KERN_ALERT "lux_data is %x\n",lux_data); + return 0; + copy_to_user(buf, &lux_data, sizeof(lux_data)); + return sizeof(lux_data); +} + +static long +mmadl_ioctl(/*struct inode *inode,*/ struct file *file, unsigned int cmd, + unsigned long arg) +{ + void __user *argp = (void __user *)arg; + //char rwbuf[5]; + short enable; //amsr = -1; + unsigned int uval; + + dbg("l-sensor ioctr...\n"); + //memset(rwbuf, 0, sizeof(rwbuf)); + switch (cmd) { + case LIGHT_IOCTL_SET_ENABLE: + // enable/disable sensor + if (copy_from_user(&enable, argp, sizeof(short))) + { + printk(KERN_ERR "Can't get enable flag!!!\n"); + return -EFAULT; + } + dbg("enable=%d\n",enable); + if ((enable >=0) && (enable <=1)) + { + dbg("driver: disable/enable(%d) gsensor.\n", enable); + + //l_sensorconfig.sensor_enable = enable; + dbg("Should to implement d/e the light sensor!\n"); + l_enable = enable; + + } else { + printk(KERN_ERR "Wrong enable argument in %s !!!\n", __FUNCTION__); + return -EINVAL; + } + break; + case WMT_IOCTL_SENSOR_GET_DRVID: +#define DRVID 0 + uval = DRVID ; + if (copy_to_user((unsigned int*)arg, &uval, sizeof(unsigned int))) + { + return -EFAULT; + } + dbg("stk3310_driver_id:%d\n",uval); + default: + break; + } + + return 0; +} + + +static long +mmadp_ioctl(/*struct inode *inode,*/ struct file *file, unsigned int cmd, + unsigned long arg) +{ + void __user *argp = (void __user *)arg; + //char rwbuf[5]; + short enable; //amsr = -1; + unsigned int uval; + unsigned char regval; + + dbg("l-sensor ioctr...\n"); + //memset(rwbuf, 0, sizeof(rwbuf)); + switch (cmd) { + case LIGHT_IOCTL_SET_ENABLE: + // enable/disable sensor + if (copy_from_user(&enable, argp, sizeof(short))) + { + printk(KERN_ERR "Can't get enable flag!!!\n"); + return -EFAULT; + } + dbg("enable=%d\n",enable); + if ((enable >=0) && (enable <=1)) + { + dbg("driver: disable/enable(%d) gsensor.\n", enable); + + //l_sensorconfig.sensor_enable = enable; + dbg("Should to implement d/e the light sensor!\n"); + p_enable = enable; + if(p_enable) + { + regval = i2c_smbus_read_byte_data(l_sensorconfig->client, 0); + regval |= (1 << 0); + i2c_smbus_write_byte_data(l_sensorconfig->client, 0, regval); + } + else + { + regval = i2c_smbus_read_byte_data(l_sensorconfig->client, 0); + regval &= ~(1 << 0); + i2c_smbus_write_byte_data(l_sensorconfig->client, 0, regval); + } + + } else { + printk(KERN_ERR "Wrong enable argument in %s !!!\n", __FUNCTION__); + return -EINVAL; + } + break; + case WMT_IOCTL_SENSOR_GET_DRVID: +#define DRVID 0 + uval = DRVID ; + if (copy_to_user((unsigned int*)arg, &uval, sizeof(unsigned int))) + { + return -EFAULT; + } + dbg("stk3310_driver_id:%d\n",uval); + default: + break; + } + + return 0; +} + + +static struct file_operations mmadl_fops = { + .owner = THIS_MODULE, + .open = mmad_open, + .release = mmad_release, + .read = mmadl_read, + .unlocked_ioctl = mmadl_ioctl, +}; + +static struct miscdevice mmadl_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "lsensor_ctrl", + .fops = &mmadl_fops, +}; + +static struct file_operations mmadp_fops = { + .owner = THIS_MODULE, + .open = mmad_open, + .release = mmad_release, + .read = mmadp_read, + .unlocked_ioctl = mmadp_ioctl, +}; + +static struct miscdevice mmadp_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "psensor_ctrl", + .fops = &mmadp_fops, +}; +#if 0 +static void stk3310_early_suspend(struct early_suspend *h) +{ + dbg("start\n"); + mutex_lock(&mutex); + //pm_runtime_get_sync(dev); + //isl_set_mod(client, ISL_MOD_POWERDOWN); + //pm_runtime_put_sync(dev); + mutex_unlock(&mutex); + dbg("exit\n"); +} + +static void stk3310_late_resume(struct early_suspend *h) +{ + struct i2c_client *client = l_sensorconfig->client; + + dbg("start\n"); + mutex_lock(&mutex); + //pm_runtime_get_sync(dev); + //isl_set_mod(client, last_mod); + isl_set_default_config(client); + //pm_runtime_put_sync(dev); + mutex_unlock(&mutex); + dbg("exit\n"); +} +#endif + +static int +stk3310_probe(struct i2c_client *client/*, const struct i2c_device_id *id*/) +{ + int res=0; + + struct stk_device* idev = kzalloc(sizeof(struct stk_device), GFP_KERNEL); + if(!idev) + return -ENOMEM; + + l_sensorconfig = idev; + +/* last mod is ALS continuous */ + //pm_runtime_enable(&client->dev); + idev->input_poll_devl = input_allocate_polled_device(); + if(!idev->input_poll_devl) + { + res = -ENOMEM; + goto err_input_allocate_device; + } + idev->input_poll_devp = input_allocate_polled_device(); + if(!idev->input_poll_devp) + { + res = -ENOMEM; + goto err_input_allocate_device; + } + idev->client = client; + + idev->input_poll_devl->private = idev; + idev->input_poll_devl->poll = isl_input_lux_poll_l; + idev->input_poll_devl->poll_interval = 100;//50; + idev->input_poll_devl->input->open = isl_input_open; + idev->input_poll_devl->input->close = isl_input_close; + idev->input_poll_devl->input->name = "lsensor_lux"; + idev->input_poll_devl->input->id.bustype = BUS_I2C; + idev->input_poll_devl->input->dev.parent = &client->dev; + + input_set_drvdata(idev->input_poll_devl->input, idev); + input_set_capability(idev->input_poll_devl->input, EV_ABS, ABS_MISC); + input_set_abs_params(idev->input_poll_devl->input, ABS_MISC, 0, 16000, 0, 0); + + idev->input_poll_devp->private = idev; + idev->input_poll_devp->poll = isl_input_lux_poll_p; + idev->input_poll_devp->poll_interval = 100;//50; + idev->input_poll_devp->input->open = isl_input_open; + idev->input_poll_devp->input->close = isl_input_close; + idev->input_poll_devp->input->name = "psensor_lux"; + idev->input_poll_devp->input->id.bustype = BUS_I2C; + idev->input_poll_devp->input->dev.parent = &client->dev; + + input_set_drvdata(idev->input_poll_devp->input, idev); + input_set_capability(idev->input_poll_devp->input, EV_ABS, ABS_MISC); + input_set_abs_params(idev->input_poll_devp->input, ABS_MISC, 0, 16000, 0, 0); + i2c_set_clientdata(client, idev); + /* set default config after set_clientdata */ + res = isl_set_default_config(client); + res = misc_register(&mmadl_device); + if (res) { + errlog("mmad_device register failed\n"); + goto err_misc_registerl; + } + res = misc_register(&mmadp_device); + if (res) { + errlog("mmad_device register failed\n"); + goto err_misc_registerp; + } + res = input_register_polled_device(idev->input_poll_devl); + if(res < 0) + goto err_input_register_devicel; + res = input_register_polled_device(idev->input_poll_devp); + if(res < 0) + goto err_input_register_devicep; + // suspend/resume register +#ifdef CONFIG_HAS_EARLYSUSPEND + idev->earlysuspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; + idev->earlysuspend.suspend = stk3310_early_suspend; + idev->earlysuspend.resume = stk3310_late_resume; + register_early_suspend(&(idev->earlysuspend)); +#endif + + dbg("stk3310 probe succeed!\n"); + return 0; +err_input_register_devicep: + input_free_polled_device(idev->input_poll_devp); +err_input_register_devicel: + input_free_polled_device(idev->input_poll_devl); +err_misc_registerp: + misc_deregister(&mmadp_device); +err_misc_registerl: + misc_deregister(&mmadl_device); +err_input_allocate_device: + //__pm_runtime_disable(&client->dev, false); + kfree(idev); + return res; +} + +static int stk3310_remove(struct i2c_client *client) +{ + struct stk_device* idev = i2c_get_clientdata(client); + + //unregister_early_suspend(&(idev->earlysuspend)); + misc_deregister(&mmadl_device); + misc_deregister(&mmadp_device); + input_unregister_polled_device(idev->input_poll_devl); + input_unregister_polled_device(idev->input_poll_devp); + input_free_polled_device(idev->input_poll_devl); + input_free_polled_device(idev->input_poll_devp); + //__pm_runtime_disable(&client->dev, false); + kfree(idev); + printk(KERN_INFO MODULE_NAME ": %s stk3310 remove call, \n", __func__); + return 0; +} + +//****************add platform_device & platform_driver for suspend &resume 2013-7-2 +static int ls_probe(struct platform_device *pdev){ + //printk("<<<%s\n", __FUNCTION__); + return 0; +} +static int ls_remove(struct platform_device *pdev){ + //printk("<<<%s\n", __FUNCTION__); + return 0; +} +static int ls_suspend(struct platform_device *pdev, pm_message_t state){ + printk("<<<%s\n", __FUNCTION__); + + return 0; +} + +static int ls_resume(struct platform_device *pdev){ + //return 0; + int ret = 0; + int count = 0; + + struct i2c_client *client = l_sensorconfig->client; + printk("<<<%s\n", __FUNCTION__); + +RETRY: + mutex_lock(&mutex); + + ret = isl_set_default_config(client); + + mutex_unlock(&mutex); + if (ret < 0){ + printk("%s isl_set_default_config fail!\n", __FUNCTION__); + count++; + if (count < 5){ + mdelay(2); + goto RETRY; + } + else + return ret; + } + return 0; + +} +static void lsdev_release(struct device *dev) +{ + return; +} +static struct platform_device lsdev = { + .name = "lsdevice", + .id = -1, + .dev = { + .release = lsdev_release, + }, +}; +static struct platform_driver lsdrv = { + .probe = ls_probe, + .remove = ls_remove, + .suspend = ls_suspend, + .resume = ls_resume, + .driver = { + .name = "lsdevice", + }, +}; +//******************************************************************** + + +static int __init sensor_stk3310_init(void) +{ + int ret = 0; + printk(KERN_INFO MODULE_NAME ": %s stk3310 init call, \n", __func__); + /* + * Force device to initialize: i2c-15 0x44 + * If i2c_new_device is not called, even stk3310_detect will not run + * TODO: rework to automatically initialize the device + */ + //i2c_new_device(i2c_get_adapter(15), &isl_info); + //return i2c_add_driver(&stk3310_driver); + if (!(this_client = sensor_i2c_register_device(2, SENSOR_I2C_ADDR, SENSOR_I2C_NAME))) + { + printk(KERN_EMERG"Can't register gsensor i2c device!\n"); + return -1; + } + if (stk3310_detect(this_client)) + { + errlog("Can't find light sensor stk3310!\n"); + goto detect_fail; + } + if(stk3310_probe(this_client)) + { + errlog("Erro for probe!\n"); + goto detect_fail; + } + + ret = platform_device_register(&lsdev); + if (ret){ + printk("<< +#define STK831X_I2C_NAME "stk831x" +#define ACC_IDEVICE_NAME "sensor_ctrl" +#define STKDIR 0x3D +#define STK_LSB_1G 21 +/* registers for stk8312 registers */ + +#define STK831X_XOUT 0x00 /* x-axis acceleration*/ +#define STK831X_YOUT 0x01 /* y-axis acceleration*/ +#define STK831X_ZOUT 0x02 /* z-axis acceleration*/ +#define STK831X_TILT 0x03 /* Tilt Status */ +#define STK831X_SRST 0x04 /* Sampling Rate Status */ +#define STK831X_SPCNT 0x05 /* Sleep Count */ +#define STK831X_INTSU 0x06 /* Interrupt setup*/ +#define STK831X_MODE 0x07 +#define STK831X_SR 0x08 /* Sample rate */ +#define STK831X_PDET 0x09 /* Tap Detection */ +#define STK831X_DEVID 0x0B /* Device ID */ +#define STK831X_OFSX 0x0C /* X-Axis offset */ +#define STK831X_OFSY 0x0D /* Y-Axis offset */ +#define STK831X_OFSZ 0x0E /* Z-Axis offset */ +#define STK831X_PLAT 0x0F /* Tap Latency */ +#define STK831X_PWIN 0x10 /* Tap Window */ +#define STK831X_FTH 0x11 /* Free-Fall Threshold */ +#define STK831X_FTM 0x12 /* Free-Fall Time */ +#define STK831X_STH 0x13 /* Shake Threshold */ +#define STK831X_CTRL 0x14 /* Control Register */ +#define STK831X_RESET 0x20 /*software reset*/ + +/* IOCTLs*/ +#define STK_IOCTL_WRITE _IOW(STKDIR, 0x01, char[8]) +#define STK_IOCTL_READ _IOWR(STKDIR, 0x02, char[8]) +#define STK_IOCTL_SET_ENABLE _IOW(STKDIR, 0x03, char) +#define STK_IOCTL_GET_ENABLE _IOR(STKDIR, 0x04, char) +#define STK_IOCTL_SET_DELAY _IOW(STKDIR, 0x05, char) +#define STK_IOCTL_GET_DELAY _IOR(STKDIR, 0x06, char) +#define STK_IOCTL_SET_OFFSET _IOW(STKDIR, 0x07, char[3]) +#define STK_IOCTL_GET_OFFSET _IOR(STKDIR, 0x08, char[3]) +#define STK_IOCTL_GET_ACCELERATION _IOR(STKDIR, 0x09, int[3]) +#define STK_IOCTL_SET_RANGE _IOW(STKDIR, 0x10, char) +#define STK_IOCTL_GET_RANGE _IOR(STKDIR, 0x11, char) +#define STK_IOCTL_SET_CALI _IOW(STKDIR, 0x12, char) + + +#endif diff --git a/drivers/input/sensor/stk8312_gsensor/stk8313.h b/drivers/input/sensor/stk8312_gsensor/stk8313.h new file mode 100755 index 00000000..66536ac7 --- /dev/null +++ b/drivers/input/sensor/stk8312_gsensor/stk8313.h @@ -0,0 +1,52 @@ +/* + * Definitions for Sensortek stk8313 accelerometer + */ +#ifndef _STK831X_H_ +#define _STK831X_H_ + +#include +#define STK831X_I2C_NAME "stk831x" +#define ACC_IDEVICE_NAME "accelerometer" +#define STKDIR 0x3D +#define STK_LSB_1G 256 +/* register for stk8313 registers */ + +#define STK831X_XOUT 0x00 +#define STK831X_YOUT 0x02 +#define STK831X_ZOUT 0x04 +#define STK831X_TILT 0x06 /* Tilt Status */ +#define STK831X_SRST 0x07 /* Sampling Rate Status */ +#define STK831X_SPCNT 0x08 /* Sleep Count */ +#define STK831X_INTSU 0x09 /* Interrupt setup*/ +#define STK831X_MODE 0x0A +#define STK831X_SR 0x0B /* Sample rate */ +#define STK831X_PDET 0x0C /* Tap Detection */ +#define STK831X_DEVID 0x0E /* Device ID */ +#define STK831X_OFSX 0x0F /* X-Axis offset */ +#define STK831X_OFSY 0x10 /* Y-Axis offset */ +#define STK831X_OFSZ 0x11 /* Z-Axis offset */ +#define STK831X_PLAT 0x12 /* Tap Latency */ +#define STK831X_PWIN 0x13 /* Tap Window */ +#define STK831X_FTH 0x14 /* Fre e-Fall Threshold */ +#define STK831X_FTM 0x15 /* Free-Fall Time */ +#define STK831X_STH 0x16 /* Shake Threshold */ +#define STK831X_ISTMP 0x17 /* Interrupt Setup */ +#define STK831X_INTMAP 0x18 /*Interrupt Map*/ +#define STK831X_RESET 0x20 /*software reset*/ + +/* IOCTLs*/ +#define STK_IOCTL_WRITE _IOW(STKDIR, 0x01, char[8]) +#define STK_IOCTL_READ _IOWR(STKDIR, 0x02, char[8]) +#define STK_IOCTL_SET_ENABLE _IOW(STKDIR, 0x03, char) +#define STK_IOCTL_GET_ENABLE _IOR(STKDIR, 0x04, char) +#define STK_IOCTL_SET_DELAY _IOW(STKDIR, 0x05, char) +#define STK_IOCTL_GET_DELAY _IOR(STKDIR, 0x06, char) +#define STK_IOCTL_SET_OFFSET _IOW(STKDIR, 0x07, char[3]) +#define STK_IOCTL_GET_OFFSET _IOR(STKDIR, 0x08, char[3]) +#define STK_IOCTL_GET_ACCELERATION _IOR(STKDIR, 0x09, int[3]) +#define STK_IOCTL_SET_RANGE _IOW(STKDIR, 0x10, char) +#define STK_IOCTL_GET_RANGE _IOR(STKDIR, 0x11, char) +#define STK_IOCTL_SET_CALI _IOW(STKDIR, 0x12, char) + + +#endif \ No newline at end of file diff --git a/drivers/input/sensor/stk8312_gsensor/stk831x.c b/drivers/input/sensor/stk8312_gsensor/stk831x.c new file mode 100755 index 00000000..68952f9c --- /dev/null +++ b/drivers/input/sensor/stk8312_gsensor/stk831x.c @@ -0,0 +1,3590 @@ +/* + * stk831x.c - Linux kernel modules for sensortek stk8311/stk8312/stk8313 accelerometer + * + * Copyright (C) 2011~2013 Lex Hsieh / sensortek + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include "../sensor.h" + + +//#define STK_ALLWINNER_PLATFORM +#define STK_ACC_DRIVER_VERSION "1.6.1" +/*choose polling or interrupt mode*/ +#define STK_ACC_POLLING_MODE 1 +#if (!STK_ACC_POLLING_MODE) + #define ADDITIONAL_GPIO_CFG 1 + #define STK_INT_PIN 39 +#endif +//#define STK_PERMISSION_THREAD +#define STK_RESUME_RE_INIT +//#define STK_DEBUG_PRINT +//#define STK_DEBUG_RAWDATA +//#define STK_LOWPASS +#define STK_FIR_LEN 4 + +/////////////////////////////////////// +#define CONFIG_SENSORS_STK8312//////// +///////////////////////////////////// +#define STK_ZG_FILTER +#ifdef CONFIG_SENSORS_STK8312 + #define STK_ZG_COUNT 1 +#elif defined (CONFIG_SENSORS_STK8313) + #define STK_ZG_COUNT 4 +#endif + +#define STK_TUNE +#ifdef CONFIG_SENSORS_STK8312 + #define STK_TUNE_XYOFFSET 3 + #define STK_TUNE_ZOFFSET 6 + #define STK_TUNE_NOISE 5 +#elif defined (CONFIG_SENSORS_STK8313) + #define STK_TUNE_XYOFFSET 35 + #define STK_TUNE_ZOFFSET 75 + #define STK_TUNE_NOISE 20 +#endif +#define STK_TUNE_NUM 125 +#define STK_TUNE_DELAY 125 +//Flourtise - Kevin +#define STK_WMT_PLATFORM +#ifdef STK_WMT_PLATFORM + //#define STK8312_DRVID 7 + ///////////////////////// ioctrl cmd ////////////////////////^M + #define WMTGSENSOR_IOCTL_MAGIC 0x09 + #define WMT_IOCTL_SENSOR_CAL_OFFSET _IOW(WMTGSENSOR_IOCTL_MAGIC, 0x01, int) //offset calibration^M + #define ECS_IOCTL_APP_SET_AFLAG _IOW(WMTGSENSOR_IOCTL_MAGIC, 0x02, short) + #define ECS_IOCTL_APP_SET_DELAY _IOW(WMTGSENSOR_IOCTL_MAGIC, 0x03, short) + #define WMT_IOCTL_SENSOR_GET_DRVID _IOW(WMTGSENSOR_IOCTL_MAGIC, 0x04, unsigned int) + #define WMT_IOCTL_SENOR_GET_RESOLUTION _IOR(WMTGSENSOR_IOCTL_MAGIC, 0x05, short) + +#endif +//Flourise - Kevin +#ifndef STK_WMT_PLATFORM + #ifdef CONFIG_SENSORS_STK8313 + #include + #elif defined CONFIG_SENSORS_STK8312 + #include + #else + #error "What's your stk accelerometer?" + #endif +#else + #ifdef CONFIG_SENSORS_STK8313 + #include "stk8313.h" + #elif defined CONFIG_SENSORS_STK8312 + #include "stk8312.h" + #else + #error "What's your stk accelerometer?" + #endif +#endif /* #ifndef STK_ALLWINNER_PLATFORM */ + +SYSCALL_DEFINE3(fchmodat, int, dfd, const char __user *, filename, mode_t, mode); + +static struct i2c_client *this_client = NULL; + +//add 2013-6-24 +#define GSENSOR_NAME "stk8312" +static struct class* l_dev_class = NULL; +struct stk8312_config +{ + int op; + int int_gpio; //0-3 + int xyz_axis[3][2]; // (axis,direction) + int rxyz_axis[3][2]; + int irq; + struct proc_dir_entry* sensor_proc; + int sensorlevel; + int shake_enable; // 1--enable shake, 0--disable shake + int manual_rotation; // 0--landance, 90--vertical + struct input_dev *input_dev; + //struct work_struct work; + struct delayed_work work; // for polling + struct workqueue_struct *queue; + int isdbg; // 0-- no debug log, 1--show debug log + int sensor_samp; // 1,2,4,8,16,32,64,120 + int sensor_enable; // 0 --> disable sensor, 1 --> enable sensor + int test_pass; + spinlock_t spinlock; + int pollcnt; // the counts of polling + int offset[3]; +}; + +static struct stk8312_config l_sensorconfig = { + .op = 0, + .int_gpio = 3, + .xyz_axis = { + {ABS_X, -1}, + {ABS_Y, 1}, + {ABS_Z, -1}, + }, + .irq = 6, + .int_gpio = 3, + .sensor_proc = NULL, + .sensorlevel = 0, + .shake_enable = 0, // default enable shake + .isdbg = 0, + .sensor_samp = 10, // 4sample/second + .sensor_enable = 1, // enable sensor + .test_pass = 0, // for test program + .pollcnt = 0, // Don't report the x,y,z when the driver is loaded until 2~3 seconds + .offset = {0,0,0}, +}; +//****************************************** + +#if defined(STK_LOWPASS) +#define MAX_FIR_LEN 32 +struct data_filter { + s16 raw[MAX_FIR_LEN][3]; + int sum[3]; + int num; + int idx; +}; +#endif + +struct stk831x_data +{ + struct input_dev *input_dev; + struct work_struct stk_work; + int irq; + int raw_data[3]; + atomic_t enabled; + unsigned char delay; + struct mutex write_lock; + bool first_enable; + bool re_enable; + char recv_reg; +#if STK_ACC_POLLING_MODE + struct hrtimer acc_timer; + struct work_struct stk_acc_work; + struct workqueue_struct *stk_acc_wq; + ktime_t acc_poll_delay; +#endif //#if STK_ACC_POLLING_MODE + atomic_t cali_status; +#if defined(STK_LOWPASS) + atomic_t firlength; + atomic_t fir_en; + struct data_filter fir; +#endif +}; + +#define STK831X_HOLD_ODR +#define STK831X_INIT_ODR 1//2 //2:100Hz, 3:50Hz, 4:25Hz +#define STK831X_SAMPLE_TIME_MIN_NO 2 +#define STK831X_SAMPLE_TIME_NO 5 +const static int STK831X_SAMPLE_TIME[STK831X_SAMPLE_TIME_NO] = {2500, 5000, 10000, 20000, 40000}; +static struct stk831x_data *stk831x_data_ptr; +static int event_since_en = 0; +static int event_since_en_limit = 20; +#if (!STK_ACC_POLLING_MODE) +static struct workqueue_struct *stk_mems_work_queue = NULL; +#endif //#if STK_ACC_POLLING_MODE + +#define STK_DEBUG_CALI +#define STK_SAMPLE_NO 10 +#define STK_ACC_CALI_VER0 0x3D +#define STK_ACC_CALI_VER1 0x01 +#define STK_ACC_CALI_FILE "/data/misc/stkacccali.conf" +#define STK_ACC_CALI_FILE_SIZE 10 + +#define STK_K_SUCCESS_TUNE 0x04 +#define STK_K_SUCCESS_FT2 0x03 +#define STK_K_SUCCESS_FT1 0x02 +#define STK_K_SUCCESS_FILE 0x01 +#define STK_K_NO_CALI 0xFF +#define STK_K_RUNNING 0xFE +#define STK_K_FAIL_LRG_DIFF 0xFD +#define STK_K_FAIL_OPEN_FILE 0xFC +#define STK_K_FAIL_W_FILE 0xFB +#define STK_K_FAIL_R_BACK 0xFA +#define STK_K_FAIL_R_BACK_COMP 0xF9 +#define STK_K_FAIL_I2C 0xF8 +#define STK_K_FAIL_K_PARA 0xF7 +#define STK_K_FAIL_OTP_OUT_RG 0xF6 +#define STK_K_FAIL_ENG_I2C 0xF5 +#define STK_K_FAIL_FT1_USD 0xF4 +#define STK_K_FAIL_FT2_USD 0xF3 +#define STK_K_FAIL_WRITE_NOFST 0xF2 +#define STK_K_FAIL_OTP_5T 0xF1 +#define STK_K_FAIL_PLACEMENT 0xF0 + + +#define POSITIVE_Z_UP 0 +#define NEGATIVE_Z_UP 1 +#define POSITIVE_X_UP 2 +#define NEGATIVE_X_UP 3 +#define POSITIVE_Y_UP 4 +#define NEGATIVE_Y_UP 5 +static unsigned char stk831x_placement = POSITIVE_Z_UP; +#ifdef STK_TUNE +static char stk_tune_offset_record[3] = {0}; +static int stk_tune_offset[3] = {0}; +static int stk_tune_sum[3] = {0}; +static int stk_tune_max[3] = {0}; +static int stk_tune_min[3] = {0}; +static int stk_tune_index = 0; +static int stk_tune_done = 0; +#endif + +static int stk_store_in_ic( struct stk831x_data *stk, char otp_offset[], char FT_index, uint32_t delay_ms); +static int32_t stk_get_file_content(char * r_buf, int8_t buf_size); +static int stk_store_in_file(char offset[], char mode); +static int STK831x_ReadByteOTP(char rReg, char *value); +static int STK831x_SetEnable(struct stk831x_data *stk, char en); +static int STK831x_SetCali(struct stk831x_data *stk, char sstate); +static int32_t stk_get_ic_content(struct stk831x_data *stk); +static int STK831x_SetOffset(char buf[]); +static void stk_handle_first_en(struct stk831x_data *stk); +static int STK831x_GetDelay(struct stk831x_data *stk, uint32_t* gdelay_ns); +static int STK831x_SetDelay(struct stk831x_data *stk, uint32_t sdelay_ns); + +#ifdef STK_ALLWINNER_PLATFORM +static int gsensor_direct_x = 0; +static int gsensor_direct_y = 0; +static int gsensor_direct_z = 0; +static int gsensor_xy_revert = 0; + +enum { + DEBUG_INIT = 1U << 0, + DEBUG_CONTROL_INFO = 1U << 1, + DEBUG_DATA_INFO = 1U << 2, + DEBUG_SUSPEND = 1U << 3, +}; +static u32 debug_mask = 0; +#define dprintk(level_mask, fmt, arg...) if (unlikely(debug_mask & level_mask)) \ + printk(KERN_DEBUG fmt , ## arg) + +module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP); +#endif /* #ifdef STK_ALLWINNER_PLATFORM */ + +#ifdef STK_ALLWINNER_PLATFORM +/* Addresses to scan */ +static union +{ + unsigned short dirty_addr_buf[2]; + const unsigned short normal_i2c[2]; +}u_i2c_addr = {{0x00},}; +static __u32 twi_id = 1; +#endif /* #ifdef STK_ALLWINNER_PLATFORM */ + +/** + * gsensor_fetch_sysconfig_para - get config info from sysconfig.fex file. + * return value: + * = 0; success; + * < 0; err + */ +#ifdef STK_ALLWINNER_A20_A31 +static int gsensor_fetch_sysconfig_para(void) +{ + int ret = -1; + int device_used = -1; + script_item_u val; + script_item_value_type_e type; + + dprintk(DEBUG_INIT, "========%s===================\n", __func__); + + type = script_get_item("gsensor_para", "gsensor_used", &val); + + if (SCIRPT_ITEM_VALUE_TYPE_INT != type) { + pr_err("%s: type err device_used = %d. \n", __func__, val.val); + goto script_get_err; + } + device_used = val.val; + + if (1 == device_used) { + type = script_get_item("gsensor_para", "gsensor_twi_id", &val); + if(SCIRPT_ITEM_VALUE_TYPE_INT != type){ + pr_err("%s: type err twi_id = %d. \n", __func__, val.val); + goto script_get_err; + } + twi_id = val.val; + + dprintk(DEBUG_INIT, "%s: twi_id is %d. \n", __func__, twi_id); + + if(SCIRPT_ITEM_VALUE_TYPE_INT != script_get_item("gsensor_para", "gsensor_direct_x", &val)){ + pr_err("%s: line: %d: script_get_item err. \n", __FILE__, __LINE__); + goto script_get_err; + } + gsensor_direct_x = val.val; + + if(SCIRPT_ITEM_VALUE_TYPE_INT != script_get_item("gsensor_para", "gsensor_direct_y", &val)){ + pr_err("%s: line: %d: script_get_item err. \n", __FILE__, __LINE__); + goto script_get_err; + } + gsensor_direct_y = val.val; + if(SCIRPT_ITEM_VALUE_TYPE_INT != script_get_item("gsensor_para", "gsensor_direct_z", &val)){ + pr_err("%s: line: %d: script_get_item err. \n", __FILE__, __LINE__); + goto script_get_err; + } + gsensor_direct_z = val.val; + + if(SCIRPT_ITEM_VALUE_TYPE_INT != script_get_item("gsensor_para", "gsensor_xy_revert", &val)){ + pr_err("%s: line: %d: script_get_item err. \n", __FILE__, __LINE__); + goto script_get_err; + } + gsensor_xy_revert = val.val; + + ret = 0; + + } else { + pr_err("%s: gsensor_unused. \n", __func__); + ret = -1; + } + + return ret; + +script_get_err: + pr_notice("=========script_get_err============\n"); + return ret; +} +#endif /* #ifdef STK_ALLWINNER_A20_A31 */ + +#ifdef STK_ALLWINNER_A13 +static int gsensor_fetch_sysconfig_para(void) +{ + int ret = -1; + int device_used = -1; + + printk("========%s===================\n", __func__); + + if(SCRIPT_PARSER_OK != (ret = script_parser_fetch("gsensor_para", "gsensor_used", &device_used, 1))){ + pr_err("%s: script_parser_fetch err.ret = %d. \n", __func__, ret); + goto script_parser_fetch_err; + } + if(1 == device_used){ + if(SCRIPT_PARSER_OK != script_parser_fetch("gsensor_para", "gsensor_twi_id", &twi_id, 1)){ + pr_err("%s: script_parser_fetch err. \n",__func__); + goto script_parser_fetch_err; + } + printk("%s: twi_id is %d. \n", __func__, twi_id); + + stk8313_pin_hd = gpio_request_ex("gsensor_para",NULL); + if (stk8313_pin_hd==-1) { + printk("stk8313_pin_hd pin request error!\n"); + } + ret = 0; + + }else{ + pr_err("%s: gsensor_unused. \n", __func__); + ret = -1; + } + + return ret; + + script_parser_fetch_err: + pr_notice("=========script_parser_fetch_err============\n"); + return ret; +} +#endif /* #ifdef STK_ALLWINNER_A13 */ + + + +static int STK_i2c_Rx(char *rxData, int length) +{ + uint8_t retry; + struct i2c_msg msgs[] = + { + { + .addr = this_client->addr, + .flags = 0, + .len = 1, + .buf = rxData, + }, + { + .addr = this_client->addr, + .flags = I2C_M_RD, + .len = length, + .buf = rxData, + }, + }; + + for (retry = 0; retry <= 3; retry++) + { + if (i2c_transfer(this_client->adapter, msgs, 2) > 0) + break; + else + mdelay(10); + } + + if (retry > 3) + { + printk(KERN_ERR "%s: retry over 3\n", __func__); + return -EIO; + } + else + return 0; +} + +static int STK_i2c_Tx(char *txData, int length) +{ + int retry; + struct i2c_msg msg[] = + { + { + .addr = this_client->addr, + .flags = 0, + .len = length, + .buf = txData, + }, + }; + + for (retry = 0; retry <= 3; retry++) + { + if (i2c_transfer(this_client->adapter, msg, 1) > 0) + break; + else + mdelay(10); + } + + if(*txData >= 0x21 && *txData <= 0x3E) + { + for (retry = 0; retry <= 3; retry++) + { + if (i2c_transfer(this_client->adapter, msg, 1) > 0) + break; + else + mdelay(10); + } + } + + if (retry > 3) + { + printk(KERN_ERR "%s: i2c error, retry over 3\n", __func__); + return -EIO; + } + else + return 0; +} + + +static int STK831X_SetVD(struct stk831x_data *stk) +{ + int result; + char buffer[2] = ""; + char reg24; + + msleep(2); + result = STK831x_ReadByteOTP(0x70, ®24); + if(result < 0) + { + printk(KERN_ERR "%s: read back error, result=%d\n", __func__, result); + return result; + } + + if(reg24 != 0) + { + buffer[0] = 0x24; + buffer[1] = reg24; + //printk(KERN_INFO "%s:write 0x%x to 0x24\n", __func__, buffer[1]); + result = STK_i2c_Tx(buffer, 2); + if (result < 0) + { + printk(KERN_ERR "%s:failed\n", __func__); + return result; + } + } + else + { + //printk(KERN_INFO "%s: reg24=0, do nothing\n", __func__); + return 0; + } + + buffer[0] = 0x24; + result = STK_i2c_Rx(buffer, 1); + if (result < 0) + { + printk(KERN_ERR "%s:failed\n", __func__); + return result; + } + if(buffer[0] != reg24) + { + printk(KERN_ERR "%s: error, reg24=0x%x, read=0x%x\n", __func__, reg24, buffer[0]); + return -1; + } + //printk(KERN_INFO "%s: successfully", __func__); + return 0; +} + +#ifdef STK_TUNE +static void STK831x_ResetPara(void) +{ + int ii; + for(ii=0;ii<3;ii++) + { + stk_tune_sum[ii] = 0; + stk_tune_min[ii] = 4096; + stk_tune_max[ii] = -4096; + } + return; +} + +static void STK831x_Tune(struct stk831x_data *stk, int acc[]) +{ + int ii; + char offset[3]; + char mode_reg; + int result; + char buffer[2] = ""; + + if (stk_tune_done==0) + { + if( event_since_en >= STK_TUNE_DELAY) + { + if ((abs(acc[0]) <= STK_TUNE_XYOFFSET) && (abs(acc[1]) <= STK_TUNE_XYOFFSET) + && (abs(abs(acc[2])-STK_LSB_1G) <= STK_TUNE_ZOFFSET)) + stk_tune_index++; + else + stk_tune_index = 0; + + if (stk_tune_index==0) + STK831x_ResetPara(); + else + { + for(ii=0;ii<3;ii++) + { + stk_tune_sum[ii] += acc[ii]; + if(acc[ii] > stk_tune_max[ii]) + stk_tune_max[ii] = acc[ii]; + if(acc[ii] < stk_tune_min[ii]) + stk_tune_min[ii] = acc[ii]; + } + } + + if(stk_tune_index == STK_TUNE_NUM) + { + for(ii=0;ii<3;ii++) + { + if((stk_tune_max[ii] - stk_tune_min[ii]) > STK_TUNE_NOISE) + { + stk_tune_index = 0; + STK831x_ResetPara(); + return; + } + } + buffer[0] = STK831X_MODE; + result = STK_i2c_Rx(buffer, 1); + if (result < 0) + { + printk(KERN_ERR "%s:failed, result=0x%x\n", __func__, result); + return; + } + mode_reg = buffer[0]; + buffer[1] = mode_reg & 0xF8; + buffer[0] = STK831X_MODE; + result = STK_i2c_Tx(buffer, 2); + if (result < 0) + { + printk(KERN_ERR "%s:failed, result=0x%x\n", __func__, result); + return; + } + + stk_tune_offset[0] = stk_tune_sum[0]/STK_TUNE_NUM; + stk_tune_offset[1] = stk_tune_sum[1]/STK_TUNE_NUM; + if (acc[2] > 0) + stk_tune_offset[2] = stk_tune_sum[2]/STK_TUNE_NUM - STK_LSB_1G; + else + stk_tune_offset[2] = stk_tune_sum[2]/STK_TUNE_NUM - (-STK_LSB_1G); + + offset[0] = (char) (-stk_tune_offset[0]); + offset[1] = (char) (-stk_tune_offset[1]); + offset[2] = (char) (-stk_tune_offset[2]); + STK831x_SetOffset(offset); + stk_tune_offset_record[0] = offset[0]; + stk_tune_offset_record[1] = offset[1]; + stk_tune_offset_record[2] = offset[2]; + + buffer[1] = mode_reg | 0x1; + buffer[0] = STK831X_MODE; + result = STK_i2c_Tx(buffer, 2); + if (result < 0) + { + printk(KERN_ERR "%s:failed, result=0x%x\n", __func__, result); + return; + } + + STK831X_SetVD(stk); + stk_store_in_file(offset, STK_K_SUCCESS_TUNE); + stk_tune_done = 1; + atomic_set(&stk->cali_status, STK_K_SUCCESS_TUNE); + event_since_en = 0; + printk(KERN_INFO "%s:TUNE done, %d,%d,%d\n", __func__, offset[0], offset[1],offset[2]); + } + } + } + /* + else + { + if(atomic_read(&stk->enabled)) + { + acc[0] -= stk_tune_offset[0]; + acc[1] -= stk_tune_offset[1]; + acc[2] -= stk_tune_offset[2]; + } + } + */ + // printk(KERN_INFO "%s:TUNE %4d,%4d,%4d [%d,%d,%d] %d\n", __func__, acc[0], acc[1], acc[2], stk_tune_offset[0], stk_tune_offset[1],stk_tune_offset[2],stk_tune_done); + return; +} +#endif + +#ifdef CONFIG_SENSORS_STK8312 +static int STK831x_CheckReading(int acc[], bool clear) +{ + static int check_result = 0; + + if(acc[0] == 127 || acc[0] == -128 || acc[1] == 127 || acc[1] == -128 || + acc[2] == 127 || acc[2] == -128) + { + printk(KERN_INFO "%s: acc:%o,%o,%o\n", __func__, acc[0], acc[1], acc[2]); + check_result++; + } + if(clear) + { + if(check_result == 3) + { + event_since_en_limit = 10000; + printk(KERN_INFO "%s: incorrect reading\n", __func__); + check_result = 0; + return 1; + } + check_result = 0; + } + return 0; +} +static inline int STK831x_ReadSensorData(struct stk831x_data *stk) +{ + int result; + char buffer[3] = {0}; + int acc_xyz[3] = {0}; +#ifdef STK_ZG_FILTER + s16 zero_fir = 0; +#endif +#ifdef STK_LOWPASS + int idx, firlength = atomic_read(&stk->firlength); +#endif + int k_status = atomic_read(&stk->cali_status); + memset(buffer, 0, 3); + + buffer[0] = STK831X_XOUT; + result = STK_i2c_Rx(buffer, 3); + if (result < 0) + { + printk(KERN_ERR "%s:i2c transfer error\n", __func__); + return result; + } + + if (buffer[0] & 0x80) + acc_xyz[0] = buffer[0] - 256; + else + acc_xyz[0] = buffer[0]; + if (buffer[1] & 0x80) + acc_xyz[1] = buffer[1] - 256; + else + acc_xyz[1] = buffer[1]; + if (buffer[2] & 0x80) + acc_xyz[2] = buffer[2] - 256; + else + acc_xyz[2] = buffer[2]; + +#ifdef STK_DEBUG_RAWDATA + printk(KERN_INFO "%s:RAW %4d,%4d,%4d\n", __func__, stk->raw_data[0], stk->raw_data[1], stk->raw_data[2]); +#endif + + if(event_since_en == 16 || event_since_en == 17) + STK831x_CheckReading(acc_xyz, false); + else if(event_since_en == 18) + STK831x_CheckReading(acc_xyz, true); + + if(k_status == STK_K_RUNNING) + { + stk->raw_data[0] = acc_xyz[0]; + stk->raw_data[1] = acc_xyz[1]; + stk->raw_data[2] = acc_xyz[2]; + return 0; + } + +#ifdef STK_LOWPASS //not define 2013-7-12 + if(atomic_read(&stk->fir_en)) + { + if(stk->fir.num < firlength) + { + stk->fir.raw[stk->fir.num][0] = acc_xyz[0]; + stk->fir.raw[stk->fir.num][1] = acc_xyz[1]; + stk->fir.raw[stk->fir.num][2] = acc_xyz[2]; + stk->fir.sum[0] += acc_xyz[0]; + stk->fir.sum[1] += acc_xyz[1]; + stk->fir.sum[2] += acc_xyz[2]; + stk->fir.num++; + stk->fir.idx++; + } + else + { + idx = stk->fir.idx % firlength; + stk->fir.sum[0] -= stk->fir.raw[idx][0]; + stk->fir.sum[1] -= stk->fir.raw[idx][1]; + stk->fir.sum[2] -= stk->fir.raw[idx][2]; + stk->fir.raw[idx][0] = acc_xyz[0]; + stk->fir.raw[idx][1] = acc_xyz[1]; + stk->fir.raw[idx][2] = acc_xyz[2]; + stk->fir.sum[0] += acc_xyz[0]; + stk->fir.sum[1] += acc_xyz[1]; + stk->fir.sum[2] += acc_xyz[2]; + stk->fir.idx++; + acc_xyz[0] = stk->fir.sum[0]/firlength; + acc_xyz[1] = stk->fir.sum[1]/firlength; + acc_xyz[2] = stk->fir.sum[2]/firlength; + } + } +#ifdef STK_DEBUG_RAWDATA + printk(KERN_INFO "%s:After FIR %4d,%4d,%4d\n", __func__, stk->raw_data[0], stk->raw_data[1], stk->raw_data[2]); +#endif + +#endif /* #ifdef STK_LOWPASS */ + + + +#ifdef STK_TUNE //define + if((k_status&0xF0) != 0) + STK831x_Tune(stk, acc_xyz); +#endif + +#ifdef STK_ZG_FILTER //define + if( abs(acc_xyz[0]) <= STK_ZG_COUNT) // 1 + acc_xyz[0] = (acc_xyz[0]*zero_fir); + if( abs(acc_xyz[1]) <= STK_ZG_COUNT) + acc_xyz[1] = (acc_xyz[1]*zero_fir); + if( abs(acc_xyz[2]) <= STK_ZG_COUNT) + acc_xyz[2] = (acc_xyz[2]*zero_fir); +#endif /* #ifdef STK_ZG_FILTER */ + + stk->raw_data[0] = acc_xyz[0]; + stk->raw_data[1] = acc_xyz[1]; + stk->raw_data[2] = acc_xyz[2]; + + return 0; +} + +#elif defined CONFIG_SENSORS_STK8313 +static int STK831x_CheckReading(int acc[], bool clear) +{ + static int check_result = 0; + + if(acc[0] == 2047 || acc[0] == -2048 || acc[1] == 2047 || acc[1] == -2048 || + acc[2] == 2047 || acc[2] == -2048) + { + printk(KERN_INFO "%s: acc:%o,%o,%o\n", __func__, acc[0], acc[1], acc[2]); + check_result++; + } + if(clear) + { + if(check_result == 3) + { + event_since_en_limit = 10000; + printk(KERN_INFO "%s: incorrect reading\n", __func__); + check_result = 0; + return 1; + } + check_result = 0; + } + return 0; +} +static int STK831x_ReadSensorData(struct stk831x_data *stk) +{ + int result; + char buffer[6] = ""; + int acc_xyz[3] = {0}; +#ifdef STK_ZG_FILTER + s16 zero_fir = 0; +#endif +#ifdef STK_LOWPASS + int idx, firlength = atomic_read(&stk->firlength); +#endif + int k_status = atomic_read(&stk->cali_status); + + memset(buffer, 0, 6); + buffer[0] = STK831X_XOUT; + result = STK_i2c_Rx(buffer, 6); + if (result < 0) + { + printk(KERN_ERR "%s:i2c transfer error\n", __func__); + return result; + } + + if (buffer[0] & 0x80) + acc_xyz[0] = ((int)buffer[0]<<4) + (buffer[1]>>4) - 4096; + else + acc_xyz[0] = ((int)buffer[0]<<4) + (buffer[1]>>4); + if (buffer[2] & 0x80) + acc_xyz[1] = ((int)buffer[2]<<4) + (buffer[3]>>4) - 4096; + else + acc_xyz[1] = ((int)buffer[2]<<4) + (buffer[3]>>4); + if (buffer[4] & 0x80) + acc_xyz[2] = ((int)buffer[4]<<4) + (buffer[5]>>4) - 4096; + else + acc_xyz[2] = ((int)buffer[4]<<4) + (buffer[5]>>4); + +#ifdef STK_DEBUG_RAWDATA + printk(KERN_INFO "%s:RAW %4d,%4d,%4d\n", __func__, stk->raw_data[0], stk->raw_data[1], stk->raw_data[2]); +#endif + + if(event_since_en == 16 || event_since_en == 17) + STK831x_CheckReading(acc_xyz, false); + else if(event_since_en == 18) + STK831x_CheckReading(acc_xyz, true); + if(k_status == STK_K_RUNNING) + { + stk->raw_data[0] = acc_xyz[0]; + stk->raw_data[1] = acc_xyz[1]; + stk->raw_data[2] = acc_xyz[2]; + return 0; + } + +#ifdef STK_LOWPASS + if(atomic_read(&stk->fir_en)) + { + if(stk->fir.num < firlength) + { + stk->fir.raw[stk->fir.num][0] = acc_xyz[0]; + stk->fir.raw[stk->fir.num][1] = acc_xyz[1]; + stk->fir.raw[stk->fir.num][2] = acc_xyz[2]; + stk->fir.sum[0] += acc_xyz[0]; + stk->fir.sum[1] += acc_xyz[1]; + stk->fir.sum[2] += acc_xyz[2]; + stk->fir.num++; + stk->fir.idx++; + } + else + { + idx = stk->fir.idx % firlength; + stk->fir.sum[0] -= stk->fir.raw[idx][0]; + stk->fir.sum[1] -= stk->fir.raw[idx][1]; + stk->fir.sum[2] -= stk->fir.raw[idx][2]; + stk->fir.raw[idx][0] = acc_xyz[0]; + stk->fir.raw[idx][1] = acc_xyz[1]; + stk->fir.raw[idx][2] = acc_xyz[2]; + stk->fir.sum[0] += acc_xyz[0]; + stk->fir.sum[1] += acc_xyz[1]; + stk->fir.sum[2] += acc_xyz[2]; + stk->fir.idx++; + acc_xyz[0] = stk->fir.sum[0]/firlength; + acc_xyz[1] = stk->fir.sum[1]/firlength; + acc_xyz[2] = stk->fir.sum[2]/firlength; + } + } +#ifdef STK_DEBUG_RAWDATA + printk(KERN_INFO "%s:After FIR %4d,%4d,%4d\n", __func__, stk->raw_data[0], stk->raw_data[1], stk->raw_data[2]); +#endif + +#endif /* #ifdef STK_LOWPASS */ + + +#ifdef STK_TUNE + if((k_status&0xF0) != 0) + STK831x_Tune(stk, acc_xyz); +#endif + +#ifdef STK_ZG_FILTER + if( abs(acc_xyz[0]) <= STK_ZG_COUNT) + acc_xyz[0] = (acc_xyz[0]*zero_fir); + if( abs(acc_xyz[1]) <= STK_ZG_COUNT) + acc_xyz[1] = (acc_xyz[1]*zero_fir); + if( abs(acc_xyz[2]) <= STK_ZG_COUNT) + acc_xyz[2] = (acc_xyz[2]*zero_fir); +#endif /* #ifdef STK_ZG_FILTER */ + + stk->raw_data[0] = acc_xyz[0]; + stk->raw_data[1] = acc_xyz[1]; + stk->raw_data[2] = acc_xyz[2]; + + return 0; +} +#endif + +static int STK831x_ReportValue(struct stk831x_data *stk) +{ + //int tmp = 0; + int rxyz[3] = {0}; +#if 1//comment 2013-8-15 + if(event_since_en < 1200) + { + event_since_en++; + if(event_since_en < 12) + return 0; + } +#endif +//add by gandy + //gsensor_direct_x = 0; +#if 0 + if (gsensor_direct_x == 1) + stk->raw_data[0] = -stk->raw_data[0]; + + //gsensor_direct_y = 1; + if (gsensor_direct_y == 1) + stk->raw_data[1] = -stk->raw_data[1]; + + gsensor_direct_z = 1; + if (gsensor_direct_z == 1) + stk->raw_data[2] = -stk->raw_data[2]; + + if (gsensor_xy_revert == 1) + { + tmp = stk->raw_data[0]; + stk->raw_data[0] = stk->raw_data[1]; + stk->raw_data[1] = tmp; + } +#endif +//end add + //add coord + //printk("x,y,z(%d,%d,%d)\n", stk->raw_data[0], stk->raw_data[1], stk->raw_data[2]); + rxyz[0] = stk->raw_data[0]; + rxyz[1] = stk->raw_data[1]; + rxyz[2] = stk->raw_data[2]; + stk->raw_data[0] = rxyz[l_sensorconfig.xyz_axis[0][0]]*l_sensorconfig.xyz_axis[0][1]; + stk->raw_data[1] = rxyz[l_sensorconfig.xyz_axis[1][0]]*l_sensorconfig.xyz_axis[1][1]; + stk->raw_data[2] = rxyz[l_sensorconfig.xyz_axis[2][0]]*l_sensorconfig.xyz_axis[2][1]; + + +#if 0 + stk->raw_data[0] = stk->raw_data[0]*9800*100/2133; //add for stk8132 21.34 + stk->raw_data[1] = stk->raw_data[1]*9800*100/2133; + stk->raw_data[2] = stk->raw_data[2]*9800*100/2133; +#endif + + +#ifdef STK_DEBUG_PRINT + printk(KERN_INFO "%s:%4d,%4d,%4d\n", __func__, stk->raw_data[0], stk->raw_data[1], stk->raw_data[2]); +#endif + input_report_abs(stk->input_dev, ABS_X, stk->raw_data[0]); + input_report_abs(stk->input_dev, ABS_Y, stk->raw_data[1]); + input_report_abs(stk->input_dev, ABS_Z, stk->raw_data[2]); + + //printk(" after x,y,z(%d,%d,%d)\n", stk->raw_data[0], stk->raw_data[1], stk->raw_data[2]); + input_sync(stk->input_dev); + return 0; +} + +static int STK831x_SetOffset(char buf[]) +{ + int result; + char buffer[4] = ""; + + buffer[0] = STK831X_OFSX; + buffer[1] = buf[0]; + buffer[2] = buf[1]; + buffer[3] = buf[2]; + result = STK_i2c_Tx(buffer, 4); + if (result < 0) + { + printk(KERN_ERR "%s:failed\n", __func__); + return result; + } + return 0; +} + +static int STK831x_GetOffset(char buf[]) +{ + int result; + char buffer[3] = ""; + + buffer[0] = STK831X_OFSX; + result = STK_i2c_Rx(buffer, 3); + if (result < 0) + { + printk(KERN_ERR "%s:failed\n", __func__); + return result; + } + buf[0] = buffer[0]; + buf[1] = buffer[1]; + buf[2] = buffer[2]; + return 0; +} + +static int STK831x_SetEnable(struct stk831x_data *stk, char en) +{ + int result; + char buffer[2] = ""; + int new_enabled = (en)?1:0; + int k_status = atomic_read(&stk->cali_status); + + if(new_enabled == atomic_read(&stk->enabled)) + return 0; + printk(KERN_INFO "%s:%x\n", __func__, en); + + //mutex_lock(&stk->write_lock); + if(stk->first_enable && k_status != STK_K_RUNNING) + stk_handle_first_en(stk); + + mutex_lock(&stk->write_lock); + buffer[0] = STK831X_MODE; + result = STK_i2c_Rx(buffer, 1); + if (result < 0) + { + printk(KERN_ERR "%s:failed\n", __func__); + goto e_err_i2c; + } + if(en) + { + buffer[1] = (buffer[0] & 0xF8) | 0x01; + event_since_en = 0; +#ifdef STK_TUNE + if((k_status&0xF0) != 0 && stk_tune_done == 0) + { + stk_tune_index = 0; + STK831x_ResetPara(); + } +#endif + } + else + buffer[1] = (buffer[0] & 0xF8); + + buffer[0] = STK831X_MODE; + result = STK_i2c_Tx(buffer, 2); + if (result < 0) + { + printk(KERN_ERR "%s:failed\n", __func__); + goto e_err_i2c; + } + mutex_unlock(&stk->write_lock); + + if(stk->first_enable && k_status != STK_K_RUNNING) + { + stk->first_enable = false; + msleep(2); + result = stk_get_ic_content(stk); + } + if(en) + { + STK831X_SetVD(stk); +#if STK_ACC_POLLING_MODE + hrtimer_start(&stk->acc_timer, stk->acc_poll_delay, HRTIMER_MODE_REL); +#else + enable_irq((unsigned int)stk->irq); +#endif //#if STK_ACC_POLLING_MODE + } + else + { +#if STK_ACC_POLLING_MODE + hrtimer_cancel(&stk->acc_timer); + cancel_work_sync(&stk->stk_acc_work); +#else + disable_irq((unsigned int)stk->irq); +#endif //#if STK_ACC_POLLING_MODE + } + //mutex_unlock(&stk->write_lock); + atomic_set(&stk->enabled, new_enabled); + return 0; + +e_err_i2c: + mutex_unlock(&stk->write_lock); + return result; +} + +static int STK831x_GetEnable(struct stk831x_data *stk, char* gState) +{ + *gState = atomic_read(&stk->enabled); + return 0; +} + +static int STK831x_SetDelay(struct stk831x_data *stk, uint32_t sdelay_ns) +{ + unsigned char sr_no; + int result; + char buffer[2] = ""; + uint32_t sdelay_us = sdelay_ns / 1000; + + for(sr_no=(STK831X_SAMPLE_TIME_NO-1);sr_no>0;sr_no--) + { + if(sdelay_us >= STK831X_SAMPLE_TIME[sr_no]) + break; + } + if(sr_no < STK831X_SAMPLE_TIME_MIN_NO) + sr_no = STK831X_SAMPLE_TIME_MIN_NO; + +#ifdef STK831X_HOLD_ODR + sr_no = STK831X_INIT_ODR; +#endif + +#ifdef STK_DEBUG_PRINT +#ifdef STK831X_HOLD_ODR + printk(KERN_INFO "%s:sdelay_us=%d, Hold delay = %d\n", __func__, sdelay_us, STK831X_SAMPLE_TIME[STK831X_INIT_ODR]); +#else + printk(KERN_INFO "%s:sdelay_us=%d\n", __func__, sdelay_us); +#endif +#endif + mutex_lock(&stk->write_lock); + if(stk->delay == sr_no) + { + mutex_unlock(&stk->write_lock); + return 0; + } + buffer[0] = STK831X_SR; + result = STK_i2c_Rx(buffer, 1); + if (result < 0) + { + printk(KERN_ERR "%s:failed\n", __func__); + goto d_err_i2c; + } + + buffer[1] = (buffer[0] & 0xF8) | ((sr_no & 0x07)); + buffer[0] = STK831X_SR; + result = STK_i2c_Tx(buffer, 2); + if (result < 0) + { + printk(KERN_ERR "%s:failed\n", __func__); + goto d_err_i2c; + } + stk->delay = sr_no; +#if STK_ACC_POLLING_MODE + stk->acc_poll_delay = ns_to_ktime(STK831X_SAMPLE_TIME[sr_no]*USEC_PER_MSEC); +#endif + +#if defined(STK_LOWPASS) + stk->fir.num = 0; + stk->fir.idx = 0; + stk->fir.sum[0] = 0; + stk->fir.sum[1] = 0; + stk->fir.sum[2] = 0; +#endif + mutex_unlock(&stk->write_lock); + + return 0; +d_err_i2c: + mutex_unlock(&stk->write_lock); + return result; +} + +static int STK831x_GetDelay(struct stk831x_data *stk, uint32_t *gdelay_ns) +{ + int result; + char buffer[2] = ""; + + mutex_lock(&stk->write_lock); + buffer[0] = STK831X_SR; + result = STK_i2c_Rx(buffer, 1); + if (result < 0) + { + mutex_unlock(&stk->write_lock); + printk(KERN_ERR "%s:failed\n", __func__); + return result; + } + mutex_unlock(&stk->write_lock); + *gdelay_ns = (uint32_t) STK831X_SAMPLE_TIME[(int)buffer[0]] * 1000; + return 0; +} + + +static int STK831x_SetRange(char srange) +{ + int result; + char buffer[2] = ""; +#ifdef STK_DEBUG_PRINT + printk(KERN_INFO "%s:range=0x%x\n", __func__, srange); +#endif + + if(srange >= 3) + { + printk(KERN_ERR "%s:parameter out of range\n", __func__); + return -1; + } + + buffer[0] = STK831X_STH; + result = STK_i2c_Rx(buffer, 1); + if (result < 0) + { + printk(KERN_ERR "%s:failed\n", __func__); + return result; + } + + buffer[1] = (buffer[0] & 0x3F) | srange<<6; + buffer[0] = STK831X_STH; + result = STK_i2c_Tx(buffer, 2); + if (result < 0) + { + printk(KERN_ERR "%s:failed\n", __func__); + return result; + } + return 0; +} + +static int STK831x_GetRange(char* grange) +{ + int result; + char buffer = 0; + + buffer = STK831X_STH; + result = STK_i2c_Rx(&buffer, 1); + if (result < 0) + { + printk(KERN_ERR "%s:failed\n", __func__); + return result; + } + *grange = buffer >> 6; + return 0; +} + +static int STK831x_ReadByteOTP(char rReg, char *value) +{ + int redo = 0; + int result; + char buffer[2] = ""; + *value = 0; + + buffer[0] = 0x3D; + buffer[1] = rReg; + result = STK_i2c_Tx(buffer, 2); + if (result < 0) + { + printk(KERN_ERR "%s:failed\n", __func__); + goto eng_i2c_r_err; + } + buffer[0] = 0x3F; + buffer[1] = 0x02; + result = STK_i2c_Tx(buffer, 2); + if (result < 0) + { + printk(KERN_ERR "%s:failed\n", __func__); + goto eng_i2c_r_err; + } + + do { + msleep(2); + buffer[0] = 0x3F; + result = STK_i2c_Rx(buffer, 1); + if (result < 0) + { + printk(KERN_ERR "%s:failed\n", __func__); + goto eng_i2c_r_err; + } + if(buffer[0]& 0x80) + { + break; + } + redo++; + }while(redo < 10); + + if(redo == 10) + { + printk(KERN_ERR "%s:OTP read repeat read 10 times! Failed!\n", __func__); + return -STK_K_FAIL_OTP_5T; + } + buffer[0] = 0x3E; + result = STK_i2c_Rx(buffer, 1); + if (result < 0) + { + printk(KERN_ERR "%s:failed\n", __func__); + goto eng_i2c_r_err; + } + *value = buffer[0]; +#ifdef STK_DEBUG_CALI + printk(KERN_INFO "%s: read 0x%x=0x%x\n", __func__, rReg, *value); +#endif + return 0; + +eng_i2c_r_err: + return -STK_K_FAIL_ENG_I2C; +} + +static int STK831x_WriteByteOTP(char wReg, char value) +{ + int finish_w_check = 0; + int result; + char buffer[2] = ""; + char read_back, value_xor = value; + int re_write = 0; + + do + { + finish_w_check = 0; + buffer[0] = 0x3D; + buffer[1] = wReg; + result = STK_i2c_Tx(buffer, 2); + if (result < 0) + { + printk(KERN_ERR "%s:failed, err=0x%x\n", __func__, result); + goto eng_i2c_w_err; + } + buffer[0] = 0x3E; + buffer[1] = value_xor; + result = STK_i2c_Tx(buffer, 2); + if (result < 0) + { + printk(KERN_ERR "%s:failed, err=0x%x\n", __func__, result); + goto eng_i2c_w_err; + } + buffer[0] = 0x3F; + buffer[1] = 0x01; + result = STK_i2c_Tx(buffer, 2); + if (result < 0) + { + printk(KERN_ERR "%s:failed, err=0x%x\n", __func__, result); + goto eng_i2c_w_err; + } + + do + { + msleep(1); + buffer[0] = 0x3F; + result = STK_i2c_Rx(buffer, 1); + if (result < 0) + { + printk(KERN_ERR "%s:failed, err=0x%x\n", __func__, result); + goto eng_i2c_w_err; + } + if(buffer[0]& 0x80) + { + result = STK831x_ReadByteOTP(wReg, &read_back); + if(result < 0) + { + printk(KERN_ERR "%s: read back error, result=%d\n", __func__, result); + goto eng_i2c_w_err; + } + + if(read_back == value) + { +#ifdef STK_DEBUG_CALI + printk(KERN_INFO "%s: write 0x%x=0x%x successfully\n", __func__, wReg, value); +#endif + re_write = 0xFF; + break; + } + else + { + printk(KERN_ERR "%s: write 0x%x=0x%x, read 0x%x=0x%x, try again\n", __func__, wReg, value_xor, wReg, read_back); + value_xor = read_back ^ value; + re_write++; + break; + } + } + finish_w_check++; + } while (finish_w_check < 5); + } while(re_write < 10); + + if(re_write == 10) + { + printk(KERN_ERR "%s: write 0x%x fail, read=0x%x, write=0x%x, target=0x%x\n", __func__, wReg, read_back, value_xor, value); + return -STK_K_FAIL_OTP_5T; + } + + return 0; + +eng_i2c_w_err: + return -STK_K_FAIL_ENG_I2C; +} + +static int STK831x_WriteOffsetOTP(struct stk831x_data *stk, int FT, char offsetData[]) +{ + char regR[6], reg_comp[3]; + char mode; + int result; + char buffer[2] = ""; + int ft_pre_trim = 0; + + if(FT==1) + { + result = STK831x_ReadByteOTP(0x7F, ®R[0]); + if(result < 0) + goto eng_i2c_err; + + if(regR[0]&0x10) + { + printk(KERN_ERR "%s: 0x7F=0x%x\n", __func__, regR[0]); + return -STK_K_FAIL_FT1_USD; + } + } + else if (FT == 2) + { + result = STK831x_ReadByteOTP(0x7F, ®R[0]); + if(result < 0) + goto eng_i2c_err; + + if(regR[0]&0x20) + { + printk(KERN_ERR "%s: 0x7F=0x%x\n", __func__, regR[0]); + return -STK_K_FAIL_FT2_USD; + } + } +//Check End + + buffer[0] = STK831X_MODE; + result = STK_i2c_Rx(buffer, 1); + if (result < 0) + { + goto common_i2c_error; + } + mode = buffer[0]; + buffer[1] = (mode | 0x01); + buffer[0] = STK831X_MODE; + result = STK_i2c_Tx(buffer, 2); + if (result < 0) + { + goto common_i2c_error; + } + msleep(2); + + + if(FT == 1) + { + result = STK831x_ReadByteOTP(0x40, ®_comp[0]); + if(result < 0) + goto eng_i2c_err; + result = STK831x_ReadByteOTP(0x41, ®_comp[1]); + if(result < 0) + goto eng_i2c_err; + result = STK831x_ReadByteOTP(0x42, ®_comp[2]); + if(result < 0) + goto eng_i2c_err; + } + else if (FT == 2) + { + result = STK831x_ReadByteOTP(0x50, ®_comp[0]); + if(result < 0) + goto eng_i2c_err; + result = STK831x_ReadByteOTP(0x51, ®_comp[1]); + if(result < 0) + goto eng_i2c_err; + result = STK831x_ReadByteOTP(0x52, ®_comp[2]); + if(result < 0) + goto eng_i2c_err; + } + + result = STK831x_ReadByteOTP(0x30, ®R[0]); + if(result < 0) + goto eng_i2c_err; + result = STK831x_ReadByteOTP(0x31, ®R[1]); + if(result < 0) + goto eng_i2c_err; + result = STK831x_ReadByteOTP(0x32, ®R[2]); + if(result < 0) + goto eng_i2c_err; + + if(reg_comp[0] == regR[0] && reg_comp[1] == regR[1] && reg_comp[2] == regR[2]) + { + printk(KERN_INFO "%s: ft pre-trimmed\n", __func__); + ft_pre_trim = 1; + } + + if(!ft_pre_trim) + { + if(FT == 1) + { + result = STK831x_WriteByteOTP(0x40, regR[0]); + if(result < 0) + goto eng_i2c_err; + result = STK831x_WriteByteOTP(0x41, regR[1]); + if(result < 0) + goto eng_i2c_err; + result = STK831x_WriteByteOTP(0x42, regR[2]); + if(result < 0) + goto eng_i2c_err; + } + else if (FT == 2) + { + result = STK831x_WriteByteOTP(0x50, regR[0]); + if(result < 0) + goto eng_i2c_err; + result = STK831x_WriteByteOTP(0x51, regR[1]); + if(result < 0) + goto eng_i2c_err; + result = STK831x_WriteByteOTP(0x52, regR[2]); + if(result < 0) + goto eng_i2c_err; + } + } +#ifdef STK_DEBUG_CALI + printk(KERN_INFO "%s:OTP step1 Success!\n", __func__); +#endif + buffer[0] = 0x2A; + result = STK_i2c_Rx(buffer, 1); + if (result < 0) + { + goto common_i2c_error; + } + else + { + regR[0] = buffer[0]; + } + buffer[0] = 0x2B; + result = STK_i2c_Rx(buffer, 1); + if (result < 0) + { + goto common_i2c_error; + } + else + { + regR[1] = buffer[0]; + } + buffer[0] = 0x2E; + result = STK_i2c_Rx(buffer, 1); + if (result < 0) + { + goto common_i2c_error; + } + else + { + regR[2] = buffer[0]; + } + buffer[0] = 0x2F; + result = STK_i2c_Rx(buffer, 1); + if (result < 0) + { + goto common_i2c_error; + } + else + { + regR[3] = buffer[0]; + } + buffer[0] = 0x32; + result = STK_i2c_Rx(buffer, 1); + if (result < 0) + { + goto common_i2c_error; + } + else + { + regR[4] = buffer[0]; + } + buffer[0] = 0x33; + result = STK_i2c_Rx(buffer, 1); + if (result < 0) + { + goto common_i2c_error; + } + else + { + regR[5] = buffer[0]; + } + + regR[1] = offsetData[0]; + regR[3] = offsetData[2]; + regR[5] = offsetData[1]; + if(FT==1) + { + result = STK831x_WriteByteOTP(0x44, regR[1]); + if(result < 0) + goto eng_i2c_err; + result = STK831x_WriteByteOTP(0x46, regR[3]); + if(result < 0) + goto eng_i2c_err; + result = STK831x_WriteByteOTP(0x48, regR[5]); + if(result < 0) + goto eng_i2c_err; + + if(!ft_pre_trim) + { + result = STK831x_WriteByteOTP(0x43, regR[0]); + if(result < 0) + goto eng_i2c_err; + result = STK831x_WriteByteOTP(0x45, regR[2]); + if(result < 0) + goto eng_i2c_err; + result = STK831x_WriteByteOTP(0x47, regR[4]); + if(result < 0) + goto eng_i2c_err; + } + } + else if (FT == 2) + { + result = STK831x_WriteByteOTP(0x54, regR[1]); + if(result < 0) + goto eng_i2c_err; + result = STK831x_WriteByteOTP(0x56, regR[3]); + if(result < 0) + goto eng_i2c_err; + result = STK831x_WriteByteOTP(0x58, regR[5]); + if(result < 0) + goto eng_i2c_err; + + if(!ft_pre_trim) + { + result = STK831x_WriteByteOTP(0x53, regR[0]); + if(result < 0) + goto eng_i2c_err; + result = STK831x_WriteByteOTP(0x55, regR[2]); + if(result < 0) + goto eng_i2c_err; + result = STK831x_WriteByteOTP(0x57, regR[4]); + if(result < 0) + goto eng_i2c_err; + } + } +#ifdef STK_DEBUG_CALI + printk(KERN_INFO "%s:OTP step2 Success!\n", __func__); +#endif + result = STK831x_ReadByteOTP(0x7F, ®R[0]); + if(result < 0) + goto eng_i2c_err; + + if(FT==1) + regR[0] = regR[0]|0x10; + else if(FT==2) + regR[0] = regR[0]|0x20; + + result = STK831x_WriteByteOTP(0x7F, regR[0]); + if(result < 0) + goto eng_i2c_err; +#ifdef STK_DEBUG_CALI + printk(KERN_INFO "%s:OTP step3 Success!\n", __func__); +#endif + return 0; + +eng_i2c_err: + printk(KERN_ERR "%s: read/write eng i2c error, result=0x%x\n", __func__, result); + return result; + +common_i2c_error: + printk(KERN_ERR "%s: read/write common i2c error, result=0x%x\n", __func__, result); + return result; +} + +static int STK831X_VerifyCali(struct stk831x_data *stk, unsigned char en_dis, uint32_t delay_ms) +{ + unsigned char axis, state; + int acc_ave[3] = {0, 0, 0}; + const unsigned char verify_sample_no = 3; +#ifdef CONFIG_SENSORS_STK8313 + const unsigned char verify_diff = 25; +#elif defined CONFIG_SENSORS_STK8312 + const unsigned char verify_diff = 2; +#endif + int result; + char buffer[2] = ""; + int ret = 0; + + if(en_dis) + { + STK831x_SetDelay(stk, 10000000); + buffer[0] = STK831X_MODE; + result = STK_i2c_Rx(buffer, 1); + if (result < 0) + { + printk(KERN_ERR "%s:failed, result=0x%x\n", __func__, result); + return -STK_K_FAIL_I2C; + } + buffer[1] = (buffer[0] & 0xF8) | 0x01; + buffer[0] = STK831X_MODE; + result = STK_i2c_Tx(buffer, 2); + if (result < 0) + { + printk(KERN_ERR "%s:failed, result=0x%x\n", __func__, result); + return -STK_K_FAIL_I2C; + } + STK831X_SetVD(stk); + msleep(delay_ms*15); + } + + for(state=0;stateraw_data[axis]; +#ifdef STK_DEBUG_CALI + printk(KERN_INFO "%s: acc=%d,%d,%d\n", __func__, stk->raw_data[0], stk->raw_data[1], stk->raw_data[2]); +#endif + msleep(delay_ms); + } + + for(axis=0;axis<3;axis++) + acc_ave[axis] /= verify_sample_no; + + switch(stk831x_placement) + { + case POSITIVE_X_UP: + acc_ave[0] -= STK_LSB_1G; + break; + case NEGATIVE_X_UP: + acc_ave[0] += STK_LSB_1G; + break; + case POSITIVE_Y_UP: + acc_ave[1] -= STK_LSB_1G; + break; + case NEGATIVE_Y_UP: + acc_ave[1] += STK_LSB_1G; + break; + case POSITIVE_Z_UP: + acc_ave[2] -= STK_LSB_1G; + break; + case NEGATIVE_Z_UP: + acc_ave[2] += STK_LSB_1G; + break; + default: + printk("%s: invalid stk831x_placement=%d\n", __func__, stk831x_placement); + ret = -STK_K_FAIL_PLACEMENT; + break; + } + if(abs(acc_ave[0]) > verify_diff || abs(acc_ave[1]) > verify_diff || abs(acc_ave[2]) > verify_diff) + { + printk(KERN_INFO "%s:Check data x:%d, y:%d, z:%d\n", __func__,acc_ave[0],acc_ave[1],acc_ave[2]); + printk(KERN_ERR "%s:Check Fail, Calibration Fail\n", __func__); + ret = -STK_K_FAIL_LRG_DIFF; + } +#ifdef STK_DEBUG_CALI + else + printk(KERN_INFO "%s:Check data pass\n", __func__); +#endif + if(en_dis) + { + buffer[0] = STK831X_MODE; + result = STK_i2c_Rx(buffer, 1); + if (result < 0) + { + printk(KERN_ERR "%s:failed, result=0x%x\n", __func__, result); + return -STK_K_FAIL_I2C; + } + buffer[1] = (buffer[0] & 0xF8); + buffer[0] = STK831X_MODE; + result = STK_i2c_Tx(buffer, 2); + if (result < 0) + { + printk(KERN_ERR "%s:failed, result=0x%x\n", __func__, result); + return -STK_K_FAIL_I2C; + } + } + + return ret; +} + + +static int STK831x_SetCali(struct stk831x_data *stk, char sstate) +{ + char org_enable; + int acc_ave[3] = {0, 0, 0}; + int state, axis; + int new_offset[3]; + char char_offset[3] = {0}; + int result; + char buffer[2] = ""; + char reg_offset[3] = {0}; + char store_location = sstate; + uint32_t gdelay_ns, real_delay_ms; + char offset[3]; + + atomic_set(&stk->cali_status, STK_K_RUNNING); + //sstate=1, STORE_OFFSET_IN_FILE + //sstate=2, STORE_OFFSET_IN_IC +#ifdef STK_DEBUG_CALI + printk(KERN_INFO "%s:store_location=%d\n", __func__, store_location); +#endif + if((store_location != 3 && store_location != 2 && store_location != 1) || (stk831x_placement < 0 || stk831x_placement > 5) ) + { + printk(KERN_ERR "%s, invalid parameters\n", __func__); + atomic_set(&stk->cali_status, STK_K_FAIL_K_PARA); + return -STK_K_FAIL_K_PARA; + } + STK831x_GetDelay(stk, &gdelay_ns); + STK831x_GetEnable(stk, &org_enable); + if(org_enable) + STK831x_SetEnable(stk, 0); + STK831x_SetDelay(stk, 10000000); + msleep(1); + STK831x_GetDelay(stk, &real_delay_ms); + real_delay_ms = (real_delay_ms + (NSEC_PER_MSEC / 2)) / NSEC_PER_MSEC; + printk(KERN_INFO "%s: delay =%d ms\n", __func__, real_delay_ms); + + STK831x_SetOffset(reg_offset); + buffer[0] = STK831X_MODE; + result = STK_i2c_Rx(buffer, 1); + if (result < 0) + { + goto err_i2c_rw; + } + buffer[1] = (buffer[0] & 0xF8) | 0x01; + buffer[0] = STK831X_MODE; + result = STK_i2c_Tx(buffer, 2); + if (result < 0) + { + goto err_i2c_rw; + } + + STK831X_SetVD(stk); + if(store_location >= 2) + { + buffer[0] = 0x2B; + buffer[1] = 0x0; + result = STK_i2c_Tx(buffer, 2); + if (result < 0) + { + goto err_i2c_rw; + } + buffer[0] = 0x2F; + buffer[1] = 0x0; + result = STK_i2c_Tx(buffer, 2); + if (result < 0) + { + goto err_i2c_rw; + } + buffer[0] = 0x33; + buffer[1] = 0x0; + result = STK_i2c_Tx(buffer, 2); + if (result < 0) + { + goto err_i2c_rw; + } + } + + msleep(real_delay_ms*20); + for(state=0;stateraw_data[axis]; +#ifdef STK_DEBUG_CALI + printk(KERN_INFO "%s: acc=%d,%d,%d\n", __func__, stk->raw_data[0], stk->raw_data[1], stk->raw_data[2]); +#endif + msleep(real_delay_ms); + } + buffer[0] = STK831X_MODE; + result = STK_i2c_Rx(buffer, 1); + if (result < 0) + { + goto err_i2c_rw; + } + buffer[1] = (buffer[0] & 0xF8); + buffer[0] = STK831X_MODE; + result = STK_i2c_Tx(buffer, 2); + if (result < 0) + { + goto err_i2c_rw; + } + + for(axis=0;axis<3;axis++) + { + if(acc_ave[axis] >= 0) + acc_ave[axis] = (acc_ave[axis] + STK_SAMPLE_NO / 2) / STK_SAMPLE_NO; + else + acc_ave[axis] = (acc_ave[axis] - STK_SAMPLE_NO / 2) / STK_SAMPLE_NO; + + } + if(acc_ave[2] <= -1) + stk831x_placement = NEGATIVE_Z_UP; + else if((acc_ave[2] >= 1)) + stk831x_placement = POSITIVE_Z_UP; +#ifdef STK_DEBUG_CALI + printk(KERN_INFO "%s:stk831x_placement=%d\n", __func__, stk831x_placement); +#endif + + switch(stk831x_placement) + { + case POSITIVE_X_UP: + acc_ave[0] -= STK_LSB_1G; + break; + case NEGATIVE_X_UP: + acc_ave[0] += STK_LSB_1G; + break; + case POSITIVE_Y_UP: + acc_ave[1] -= STK_LSB_1G; + break; + case NEGATIVE_Y_UP: + acc_ave[1] += STK_LSB_1G; + break; + case POSITIVE_Z_UP: + acc_ave[2] -= STK_LSB_1G; + break; + case NEGATIVE_Z_UP: + acc_ave[2] += STK_LSB_1G; + break; + default: + printk("%s: invalid stk831x_placement=%d\n", __func__, stk831x_placement); + atomic_set(&stk->cali_status, STK_K_FAIL_PLACEMENT); + return -STK_K_FAIL_K_PARA; + break; + } + + for(axis=0;axis<3;axis++) + { + acc_ave[axis] = -acc_ave[axis]; + new_offset[axis] = acc_ave[axis]; + char_offset[axis] = new_offset[axis]; + } +#ifdef STK_DEBUG_CALI + printk(KERN_INFO "%s: New offset:%d,%d,%d\n", __func__, new_offset[0], new_offset[1], new_offset[2]); +#endif + if(store_location == 1) + { + STK831x_SetOffset(char_offset); + msleep(1); + STK831x_GetOffset(reg_offset); + for(axis=0;axis<3;axis++) + { + if(char_offset[axis] != reg_offset[axis]) + { + printk(KERN_ERR "%s: set offset to register fail!, char_offset[%d]=%d,reg_offset[%d]=%d\n", + __func__, axis,char_offset[axis], axis, reg_offset[axis]); + atomic_set(&stk->cali_status, STK_K_FAIL_WRITE_NOFST); + return -STK_K_FAIL_WRITE_NOFST; + } + } + + + result = STK831X_VerifyCali(stk, 1, real_delay_ms); + if(result) + { + printk(KERN_ERR "%s: calibration check fail, result=0x%x\n", __func__, result); + atomic_set(&stk->cali_status, -result); + } + else + { + result = stk_store_in_file(char_offset, STK_K_SUCCESS_FILE); + if(result) + { + printk(KERN_INFO "%s:write calibration failed\n", __func__); + atomic_set(&stk->cali_status, -result); + } + else + { + printk(KERN_INFO "%s successfully\n", __func__); + atomic_set(&stk->cali_status, STK_K_SUCCESS_FILE); + } + + } + } + else if(store_location >= 2) + { + for(axis=0; axis<3; axis++) + { +#ifdef CONFIG_SENSORS_STK8313 + new_offset[axis]>>=2; +#endif + char_offset[axis] = (char)new_offset[axis]; + if( (char_offset[axis]>>7)==0) + { + if(char_offset[axis] >= 0x20 ) + { + printk(KERN_ERR "%s: offset[%d]=0x%x is too large, limit to 0x1f\n", + __func__, axis, char_offset[axis] ); + char_offset[axis] = 0x1F; + //atomic_set(&stk->cali_status, STK_K_FAIL_OTP_OUT_RG); + //return -STK_K_FAIL_OTP_OUT_RG; + } + } + else + { + if(char_offset[axis] <= 0xDF) + { + printk(KERN_ERR "%s: offset[%d]=0x%x is too large, limit to 0x20\n", + __func__, axis, char_offset[axis]); + char_offset[axis] = 0x20; + //atomic_set(&stk->cali_status, STK_K_FAIL_OTP_OUT_RG); + //return -STK_K_FAIL_OTP_OUT_RG; + } + else + char_offset[axis] = char_offset[axis] & 0x3f; + } + } + + printk(KERN_INFO "%s: OTP offset:0x%x,0x%x,0x%x\n", __func__, char_offset[0], char_offset[1], char_offset[2]); + if(store_location == 2) + { + result = stk_store_in_ic( stk, char_offset, 1, real_delay_ms); + if(result == 0) + { + printk(KERN_INFO "%s successfully\n", __func__); + atomic_set(&stk->cali_status, STK_K_SUCCESS_FT1); + } + else + { + printk(KERN_ERR "%s fail, result=%d\n", __func__, result); + } + } + else if(store_location == 3) + { + result = stk_store_in_ic( stk, char_offset, 2, real_delay_ms); + if(result == 0) + { + printk(KERN_INFO "%s successfully\n", __func__); + atomic_set(&stk->cali_status, STK_K_SUCCESS_FT2); + } + else + { + printk(KERN_ERR "%s fail, result=%d\n", __func__, result); + } + } + offset[0] = offset[1] = offset[2] = 0; + stk_store_in_file(offset, store_location); + } +#ifdef STK_TUNE + stk_tune_offset_record[0] = 0; + stk_tune_offset_record[1] = 0; + stk_tune_offset_record[2] = 0; + stk_tune_done = 1; +#endif + stk->first_enable = false; + STK831x_SetDelay(stk, gdelay_ns); + + if(org_enable) + STK831x_SetEnable(stk, 1); + return 0; + +err_i2c_rw: + stk->first_enable = false; + if(org_enable) + STK831x_SetEnable(stk, 1); + printk(KERN_ERR "%s: i2c read/write error, err=0x%x\n", __func__, result); + atomic_set(&stk->cali_status, STK_K_FAIL_I2C); + return result; +} + + +static int STK831x_GetCali(struct stk831x_data *stk) +{ + char r_buf[STK_ACC_CALI_FILE_SIZE] = {0}; + char offset[3], mode; + int cnt, result; + char regR[6]; + +#ifdef STK_TUNE + printk(KERN_INFO "%s: stk_tune_done=%d, stk_tune_index=%d, stk_tune_offset=%d,%d,%d\n", __func__, + stk_tune_done, stk_tune_index, stk_tune_offset_record[0], stk_tune_offset_record[1], + stk_tune_offset_record[2]); +#endif + if ((stk_get_file_content(r_buf, STK_ACC_CALI_FILE_SIZE)) == 0) + { + if(r_buf[0] == STK_ACC_CALI_VER0 && r_buf[1] == STK_ACC_CALI_VER1) + { + offset[0] = r_buf[2]; + offset[1] = r_buf[3]; + offset[2] = r_buf[4]; + mode = r_buf[5]; + printk(KERN_INFO "%s:file offset:%#02x,%#02x,%#02x,%#02x\n", + __func__, offset[0], offset[1], offset[2], mode); + } + else + { + printk(KERN_ERR "%s: cali version number error! r_buf=0x%x,0x%x,0x%x,0x%x,0x%x\n", + __func__, r_buf[0], r_buf[1], r_buf[2], r_buf[3], r_buf[4]); + } + } + else + printk(KERN_INFO "%s: No file offset\n", __func__); + + for(cnt=0x43;cnt<0x49;cnt++) + { + result = STK831x_ReadByteOTP(cnt, &(regR[cnt-0x43])); + if(result < 0) + printk(KERN_ERR "%s: STK831x_ReadByteOTP failed, ret=%d\n", __func__, result); + } + printk(KERN_INFO "%s: OTP 0x43-0x49:%#02x,%#02x,%#02x,%#02x,%#02x,%#02x\n", __func__, regR[0], + regR[1], regR[2],regR[3], regR[4], regR[5]); + + for(cnt=0x53;cnt<0x59;cnt++) + { + result = STK831x_ReadByteOTP(cnt, &(regR[cnt-0x53])); + if(result < 0) + printk(KERN_ERR "%s: STK831x_ReadByteOTP failed, ret=%d\n", __func__, result); + } + printk(KERN_INFO "%s: OTP 0x53-0x59:%#02x,%#02x,%#02x,%#02x,%#02x,%#02x\n", __func__, regR[0], + regR[1], regR[2],regR[3], regR[4], regR[5]); + + return 0; +} + +static int STK831x_Init(struct stk831x_data *stk, struct i2c_client *client) +{ + int result; + char buffer[2] = ""; + +#ifdef CONFIG_SENSORS_STK8312 + printk(KERN_INFO "%s: Initialize stk8312\n", __func__); +#elif defined CONFIG_SENSORS_STK8313 + printk(KERN_INFO "%s: Initialize stk8313\n", __func__); +#endif + + buffer[0] = STK831X_RESET; + buffer[1] = 0x00; + result = STK_i2c_Tx(buffer, 2); + if (result < 0) + { + printk(KERN_ERR "%s:failed\n", __func__); + return result; + } + + /* int pin is active high, psuh-pull */ + buffer[0] = STK831X_MODE; + buffer[1] = 0xC0; + result = STK_i2c_Tx(buffer, 2); + if (result < 0) + { + printk(KERN_ERR "%s:failed\n", __func__); + return result; + } + + /* 50 Hz ODR */ + stk->delay = STK831X_INIT_ODR; + buffer[0] = STK831X_SR; + buffer[1] = stk->delay /*+ STK831X_SAMPLE_TIME_BASE*//*2*/; //debug for rotate slowly 2013-8-15 + result = STK_i2c_Tx(buffer, 2); + if (result < 0) + { + printk(KERN_ERR "%s:failed\n", __func__); + return result; + } + +#if (!STK_ACC_POLLING_MODE) + /* enable GINT, int after every measurement */ + buffer[0] = STK831X_INTSU; + buffer[1] = 0x10; + result = STK_i2c_Tx(buffer, 2); + if (result < 0) + { + printk(KERN_ERR "%s:interrupt init failed\n", __func__); + return result; + } +#endif + /* +- 6g mode */ + buffer[0] = STK831X_STH; +#ifdef CONFIG_SENSORS_STK8312 + buffer[1] = 0x42; +#elif defined CONFIG_SENSORS_STK8313 + buffer[1] = 0x82; +#endif + result = STK_i2c_Tx(buffer, 2); + if (result < 0) + { + printk(KERN_ERR "%s:set range failed\n", __func__); + return result; + } + + atomic_set(&stk->enabled, 0); + event_since_en = 0; + +#ifdef STK_LOWPASS + memset(&stk->fir, 0x00, sizeof(stk->fir)); + atomic_set(&stk->firlength, STK_FIR_LEN); + atomic_set(&stk->fir_en, 1); +#endif + +#ifdef STK_TUNE + stk_tune_offset[0] = 0; + stk_tune_offset[1] = 0; + stk_tune_offset[2] = 0; + stk_tune_done = 0; +#endif + return 0; +} + +static void stk_handle_first_en(struct stk831x_data *stk) +{ + char r_buf[STK_ACC_CALI_FILE_SIZE] = {0}; + char offset[3]; + char mode; + + if ((stk_get_file_content(r_buf, STK_ACC_CALI_FILE_SIZE)) == 0) + { + if(r_buf[0] == STK_ACC_CALI_VER0 && r_buf[1] == STK_ACC_CALI_VER1) + { + offset[0] = r_buf[2]; + offset[1] = r_buf[3]; + offset[2] = r_buf[4]; + mode = r_buf[5]; + STK831x_SetOffset(offset); +#ifdef STK_TUNE + stk_tune_offset_record[0] = offset[0]; + stk_tune_offset_record[1] = offset[1]; + stk_tune_offset_record[2] = offset[2]; +#endif + printk(KERN_INFO "%s: set offset:%d,%d,%d, mode=%d\n", __func__, offset[0], offset[1], offset[2], mode); + atomic_set(&stk->cali_status, mode); + } + else + { + printk(KERN_ERR "%s: cali version number error! r_buf=0x%x,0x%x,0x%x,0x%x,0x%x\n", + __func__, r_buf[0], r_buf[1], r_buf[2], r_buf[3], r_buf[4]); + //return -EINVAL; + } + } +#ifdef STK_TUNE + else if(stk_tune_offset_record[0]!=0 || stk_tune_offset_record[1]!=0 || stk_tune_offset_record[2]!=0) + { + STK831x_SetOffset(stk_tune_offset_record); + stk_tune_done = 1; + atomic_set(&stk->cali_status, STK_K_SUCCESS_TUNE); + printk(KERN_INFO "%s: set offset:%d,%d,%d\n", __func__, stk_tune_offset_record[0], + stk_tune_offset_record[1],stk_tune_offset_record[2]); + } +#endif + else + { + offset[0] = offset[1] = offset[2] = 0; + stk_store_in_file(offset, STK_K_NO_CALI); + atomic_set(&stk->cali_status, STK_K_NO_CALI); + } + printk(KERN_INFO "%s: finish, cali_status = 0x%x\n", __func__, atomic_read(&stk->cali_status)); + return; +} + +static int32_t stk_get_ic_content(struct stk831x_data *stk) +{ + int result; + char regR; + + result = STK831x_ReadByteOTP(0x7F, ®R); + if(result < 0) + { + printk(KERN_ERR "%s: read/write eng i2c error, result=0x%x\n", __func__, result); + return result; + } + + if(regR&0x20) + { + atomic_set(&stk->cali_status, STK_K_SUCCESS_FT2); + printk(KERN_INFO "%s: OTP 2 used\n", __func__); + return 2; + } + if(regR&0x10) + { + atomic_set(&stk->cali_status, STK_K_SUCCESS_FT1); + printk(KERN_INFO "%s: OTP 1 used\n", __func__); + return 1; + } + return 0; +} + +static int stk_store_in_ic( struct stk831x_data *stk, char otp_offset[], char FT_index, uint32_t delay_ms) +{ + int result; + char buffer[2] = ""; + + buffer[0] = STK831X_MODE; + result = STK_i2c_Rx(buffer, 1); + if (result < 0) + { + goto ic_err_i2c_rw; + } + buffer[1] = (buffer[0] & 0xF8) | 0x01; + buffer[0] = STK831X_MODE; + result = STK_i2c_Tx(buffer, 2); + if (result < 0) + { + goto ic_err_i2c_rw; + } + STK831X_SetVD(stk); + + buffer[0] = 0x2B; + buffer[1] = otp_offset[0]; + result = STK_i2c_Tx(buffer, 2); + if (result < 0) + { + goto ic_err_i2c_rw; + } + buffer[0] = 0x2F; + buffer[1] = otp_offset[2]; + result = STK_i2c_Tx(buffer, 2); + if (result < 0) + { + goto ic_err_i2c_rw; + } + buffer[0] = 0x33; + buffer[1] = otp_offset[1]; + result = STK_i2c_Tx(buffer, 2); + if (result < 0) + { + goto ic_err_i2c_rw; + } + + +#ifdef STK_DEBUG_CALI + //printk(KERN_INFO "%s:Check All OTP Data after write 0x2B 0x2F 0x33\n", __func__); + //STK831x_ReadAllOTP(); +#endif + + msleep(delay_ms*15); + result = STK831X_VerifyCali(stk, 0, 0); + if(result) + { + printk(KERN_ERR "%s: calibration check1 fail, FT_index=%d\n", __func__, FT_index); + goto ic_err_misc; + } +#ifdef STK_DEBUG_CALI + //printk(KERN_INFO "\n%s:Check All OTP Data before write OTP\n", __func__); + +#endif + //Write OTP + printk(KERN_INFO "\n%s:Write offset data to FT%d OTP\n", __func__, FT_index); + result = STK831x_WriteOffsetOTP(stk, FT_index, otp_offset); + if(result < 0) + { + printk(KERN_INFO "%s: write OTP%d fail\n", __func__, FT_index); + goto ic_err_misc; + } + + buffer[0] = STK831X_MODE; + result = STK_i2c_Rx(buffer, 1); + if (result < 0) + { + goto ic_err_i2c_rw; + } + buffer[1] = (buffer[0] & 0xF8); + buffer[0] = STK831X_MODE; + result = STK_i2c_Tx(buffer, 2); + if (result < 0) + { + goto ic_err_i2c_rw; + } + + msleep(1); + STK831x_Init(stk, this_client); +#ifdef STK_DEBUG_CALI + //printk(KERN_INFO "\n%s:Check All OTP Data after write OTP and reset\n", __func__); +#endif + + result = STK831X_VerifyCali(stk, 1, delay_ms); + if(result) + { + printk(KERN_ERR "%s: calibration check2 fail\n", __func__); + goto ic_err_misc; + } + return 0; + +ic_err_misc: + STK831x_Init(stk, this_client); + msleep(1); + atomic_set(&stk->cali_status, -result); + return result; + +ic_err_i2c_rw: + printk(KERN_ERR "%s: i2c read/write error, err=0x%x\n", __func__, result); + msleep(1); + STK831x_Init(stk, this_client); + atomic_set(&stk->cali_status, STK_K_FAIL_I2C); + return result; +} + +static int32_t stk_get_file_content(char * r_buf, int8_t buf_size) +{ + struct file *cali_file; + mm_segment_t fs; + ssize_t ret; + + cali_file = filp_open(STK_ACC_CALI_FILE, O_RDONLY,0); + if(IS_ERR(cali_file)) + { + printk(KERN_ERR "%s: filp_open error, no offset file!\n", __func__); + return -ENOENT; + } + else + { + fs = get_fs(); + set_fs(get_ds()); + ret = cali_file->f_op->read(cali_file,r_buf, STK_ACC_CALI_FILE_SIZE,&cali_file->f_pos); + if(ret < 0) + { + printk(KERN_ERR "%s: read error, ret=%d\n", __func__, ret); + filp_close(cali_file,NULL); + return -EIO; + } + set_fs(fs); + } + + filp_close(cali_file,NULL); + return 0; +} + +#ifdef STK_PERMISSION_THREAD +static struct task_struct *STKPermissionThread = NULL; + +static int stk_permission_thread(void *data) +{ + int ret = 0; + int retry = 0; + mm_segment_t fs = get_fs(); + set_fs(KERNEL_DS); + msleep(20000); + do{ + msleep(5000); + ret = sys_fchmodat(AT_FDCWD, "/sys/class/input/input0/driver/cali" , 0666); + ret = sys_fchmodat(AT_FDCWD, "/sys/class/input/input1/driver/cali" , 0666); + ret = sys_fchmodat(AT_FDCWD, "/sys/class/input/input2/driver/cali" , 0666); + ret = sys_fchmodat(AT_FDCWD, "/sys/class/input/input3/driver/cali" , 0666); + ret = sys_fchmodat(AT_FDCWD, "/sys/class/input/input4/driver/cali" , 0666); + ret = sys_fchmodat(AT_FDCWD, "/sys/class/input/input0/cali" , 0666); + ret = sys_fchmodat(AT_FDCWD, "/sys/class/input/input1/cali" , 0666); + ret = sys_fchmodat(AT_FDCWD, "/sys/class/input/input2/cali" , 0666); + ret = sys_fchmodat(AT_FDCWD, "/sys/class/input/input3/cali" , 0666); + ret = sys_fchmodat(AT_FDCWD, "/sys/class/input/input4/cali" , 0666); + ret = sys_chmod(STK_ACC_CALI_FILE , 0666); + ret = sys_fchmodat(AT_FDCWD, STK_ACC_CALI_FILE , 0666); + //if(ret < 0) + // printk("fail to execute sys_fchmodat, ret = %d\n", ret); + if(retry++ > 10) + break; + }while(ret == -ENOENT); + set_fs(fs); + printk(KERN_INFO "%s exit, retry=%d\n", __func__, retry); + return 0; +} +#endif /* #ifdef STK_PERMISSION_THREAD */ + +static int stk_store_in_file(char offset[], char mode) +{ + struct file *cali_file; + char r_buf[STK_ACC_CALI_FILE_SIZE] = {0}; + char w_buf[STK_ACC_CALI_FILE_SIZE] = {0}; + mm_segment_t fs; + ssize_t ret; + int8_t i; + + w_buf[0] = STK_ACC_CALI_VER0; + w_buf[1] = STK_ACC_CALI_VER1; + w_buf[2] = offset[0]; + w_buf[3] = offset[1]; + w_buf[4] = offset[2]; + w_buf[5] = mode; + + cali_file = filp_open(STK_ACC_CALI_FILE, O_CREAT | O_RDWR,0666); + + if(IS_ERR(cali_file)) + { + printk(KERN_ERR "%s: filp_open error!\n", __func__); + return -STK_K_FAIL_OPEN_FILE; + } + else + { + fs = get_fs(); + set_fs(get_ds()); + + ret = cali_file->f_op->write(cali_file,w_buf,STK_ACC_CALI_FILE_SIZE,&cali_file->f_pos); + if(ret != STK_ACC_CALI_FILE_SIZE) + { + printk(KERN_ERR "%s: write error!\n", __func__); + filp_close(cali_file,NULL); + return -STK_K_FAIL_W_FILE; + } + cali_file->f_pos=0x00; + ret = cali_file->f_op->read(cali_file,r_buf, STK_ACC_CALI_FILE_SIZE,&cali_file->f_pos); + if(ret < 0) + { + printk(KERN_ERR "%s: read error!\n", __func__); + filp_close(cali_file,NULL); + return -STK_K_FAIL_R_BACK; + } + set_fs(fs); + + //printk(KERN_INFO "%s: read ret=%d!\n", __func__, ret); + for(i=0;iprivate_data = stk831x_data_ptr; + return 0; +} + +static int stk_release(struct inode *inode, struct file *file) +{ + return 0; +} + +#if (LINUX_VERSION_CODE>=KERNEL_VERSION(2,6,36)) +static long stk_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +#else +static int stk_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +#endif +{ + void __user *argp = (void __user *)arg; + int retval = 0; + char state = 0, restore_state = 0; + char rwbuf[8] = ""; + uint32_t delay_ns; + char char3_buffer[3]; + int result; + int int3_buffer[3]; + struct stk831x_data *stk = file->private_data; + unsigned int uval = -1; +/* printk(KERN_INFO "%s: cmd = 0x%x\n", __func__, cmd); */ + + if(cmd == ECS_IOCTL_APP_SET_DELAY || cmd == STK_IOCTL_SET_DELAY || cmd == STK_IOCTL_SET_OFFSET || cmd == STK_IOCTL_SET_RANGE || cmd == STK_IOCTL_WRITE || cmd == STK_IOCTL_SET_CALI) + { + STK831x_GetEnable(stk, &restore_state); + if(restore_state) + STK831x_SetEnable(stk, 0); + } + + switch (cmd) + { + case STK_IOCTL_SET_OFFSET: + if(copy_from_user(&char3_buffer, argp, sizeof(char3_buffer))) + return -EFAULT; + break; + case ECS_IOCTL_APP_SET_DELAY: + case STK_IOCTL_SET_DELAY: + if(copy_from_user(&delay_ns, argp, sizeof(uint32_t))) + return -EFAULT; + break; + case STK_IOCTL_WRITE: + case STK_IOCTL_READ: + if (copy_from_user(&rwbuf, argp, sizeof(rwbuf))) + return -EFAULT; + break; + case ECS_IOCTL_APP_SET_AFLAG: + case STK_IOCTL_SET_ENABLE: + case STK_IOCTL_SET_RANGE: + case STK_IOCTL_SET_CALI: + if(copy_from_user(&state, argp, sizeof(char))) + return -EFAULT; + break; + case WMT_IOCTL_SENSOR_GET_DRVID: + + uval = STK8312_DRVID; + if (copy_to_user((unsigned int*)arg, &uval, sizeof(unsigned int))) + { + return -EFAULT; + } + printk("<<<<<<raw_data[0]; + int3_buffer[1] = stk->raw_data[1]; + int3_buffer[2] = stk->raw_data[2]; + break; + case ECS_IOCTL_APP_SET_AFLAG: + case STK_IOCTL_SET_ENABLE: + STK831x_SetEnable(stk, state); + break; + case STK_IOCTL_GET_ENABLE: + STK831x_GetEnable(stk, &state); + break; + case STK_IOCTL_SET_RANGE: + STK831x_SetRange(state); + break; + case STK_IOCTL_GET_RANGE: + STK831x_GetRange(&state); + break; + case STK_IOCTL_SET_CALI: + STK831x_SetCali(stk, state); + break; + default: + //retval = -ENOTTY; + break; + } + + if(cmd == ECS_IOCTL_APP_SET_DELAY || cmd == STK_IOCTL_SET_DELAY || cmd == STK_IOCTL_SET_OFFSET || cmd == STK_IOCTL_SET_RANGE || cmd == STK_IOCTL_WRITE || cmd == STK_IOCTL_SET_CALI) + { + if(restore_state) + STK831x_SetEnable(stk, restore_state); + } + switch (cmd) + { + case STK_IOCTL_GET_ACCELERATION: + if(copy_to_user(argp, &int3_buffer, sizeof(int3_buffer))) + return -EFAULT; + break; + case STK_IOCTL_READ: + if(copy_to_user(argp, &rwbuf, sizeof(rwbuf))) + return -EFAULT; + break; + case STK_IOCTL_GET_DELAY: + if(copy_to_user(argp, &delay_ns, sizeof(delay_ns))) + return -EFAULT; + break; + case STK_IOCTL_GET_OFFSET: + if(copy_to_user(argp, &char3_buffer, sizeof(char3_buffer))) + return -EFAULT; + break; + case STK_IOCTL_GET_RANGE: + case STK_IOCTL_GET_ENABLE: + if(copy_to_user(argp, &state, sizeof(char))) + return -EFAULT; + break; + default: + break; + } + + return retval; +} + + +static struct file_operations stk_fops = { + .owner = THIS_MODULE, + .open = stk_open, + .release = stk_release, +#if (LINUX_VERSION_CODE>=KERNEL_VERSION(2,6,36)) + .unlocked_ioctl = stk_ioctl, +#else + .ioctl = stk_ioctl, +#endif +}; + +static struct miscdevice stk_device = { + .minor = MISC_DYNAMIC_MINOR, +#ifndef STK_WMT_PLATFORM + .name = "stk831x", +#else + .name = "sensor_ctrl", +#endif + .fops = &stk_fops, +}; + + +#if STK_ACC_POLLING_MODE +static enum hrtimer_restart stk_acc_timer_func(struct hrtimer *timer) +{ + struct stk831x_data *stk = container_of(timer, struct stk831x_data, acc_timer); + queue_work(stk->stk_acc_wq, &stk->stk_acc_work); + hrtimer_forward_now(&stk->acc_timer, stk->acc_poll_delay); + return HRTIMER_RESTART; +} + +static void stk_acc_poll_work_func(struct work_struct *work) +{ + struct stk831x_data *stk = container_of(work, struct stk831x_data, stk_acc_work); + STK831x_ReadSensorData(stk); + STK831x_ReportValue(stk); + return; +} + +#else + +static irqreturn_t stk_mems_irq_handler(int irq, void *data) +{ + struct stk831x_data *pData = data; + disable_irq_nosync(pData->irq); + queue_work(stk_mems_work_queue,&pData->stk_work); + return IRQ_HANDLED; +} + + +static void stk_mems_wq_function(struct work_struct *work) +{ + struct stk831x_data *stk = container_of(work, struct stk831x_data, stk_work); + STK831x_ReadSensorData(stk); + STK831x_ReportValue(stk); + enable_irq(stk->irq); +} + +static int stk831x_irq_setup(struct i2c_client *client, struct stk831x_data *stk_int) +{ + int error; + int irq= -1; +#if ADDITIONAL_GPIO_CFG + if (gpio_request(STK_INT_PIN, "EINT")) + { + printk(KERN_ERR "%s:gpio_request() failed\n",__func__); + return -1; + } + gpio_direction_input(STK_INT_PIN); + + irq = gpio_to_irq(STK_INT_PIN); + if ( irq < 0 ) + { + printk(KERN_ERR "%s:gpio_to_irq() failed\n",__func__); + return -1; + } + client->irq = irq; + stk_int->irq = irq; +#endif //#if ADDITIONAL_GPIO_CFG + printk(KERN_INFO "%s: irq # = %d\n", __func__, irq); + if(irq < 0) + printk(KERN_ERR "%s: irq number was not specified!\n", __func__); + error = request_irq(client->irq, stk_mems_irq_handler, IRQF_TRIGGER_RISING , "stk-mems", stk_int); + if (error < 0) + { + printk(KERN_ERR "%s: request_irq(%d) failed for (%d)\n", __func__, client->irq, error); + return -1; + } + disable_irq(irq); + return irq; +} + +#endif //#if STK_ACC_POLLING_MODE + +static ssize_t stk831x_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct stk831x_data *stk = i2c_get_clientdata(this_client); + return scnprintf(buf, PAGE_SIZE, "%d\n", atomic_read(&stk->enabled)); +} + +static ssize_t stk831x_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long data; + int error; + + struct stk831x_data *stk = i2c_get_clientdata(this_client); + + error = strict_strtoul(buf, 10, &data); + if (error) + { + printk(KERN_ERR "%s: strict_strtoul failed, error=0x%x\n", __func__, error); + return error; + } + if ((data == 0)||(data==1)) + STK831x_SetEnable(stk,data); + else + printk(KERN_ERR "%s: invalud argument, data=%ld\n", __func__, data); + return count; +} + +static ssize_t stk831x_value_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct stk831x_data *stk = i2c_get_clientdata(this_client); + int ddata[3]; + + printk(KERN_INFO "driver version:%s\n",STK_ACC_DRIVER_VERSION); + STK831x_ReadSensorData(stk); + ddata[0]= stk->raw_data[0]; + ddata[1]= stk->raw_data[1]; + ddata[2]= stk->raw_data[2]; + return scnprintf(buf, PAGE_SIZE, "%d %d %d\n", ddata[0], ddata[1], ddata[2]); +} + +static ssize_t stk831x_delay_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct stk831x_data *stk = i2c_get_clientdata(this_client); + uint32_t gdelay_ns; + + STK831x_GetDelay(stk, &gdelay_ns); + return scnprintf(buf, PAGE_SIZE, "%d\n", gdelay_ns/1000000); +} + +static ssize_t stk831x_delay_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long data; + int error; + struct stk831x_data *stk = i2c_get_clientdata(this_client); + char restore_state = 0; + + error = strict_strtoul(buf, 10, &data); + if (error) + { + printk(KERN_ERR "%s: strict_strtoul failed, error=0x%x\n", __func__, error); + return error; + } + + STK831x_GetEnable(stk, &restore_state); + if(restore_state) + STK831x_SetEnable(stk, 0); + + STK831x_SetDelay(stk, data*1000000); // ms to ns + + if(restore_state) + STK831x_SetEnable(stk, restore_state); + return count; +} + +static ssize_t stk831x_cali_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct stk831x_data *stk = i2c_get_clientdata(this_client); + int status = atomic_read(&stk->cali_status); + + if(status != STK_K_RUNNING) + STK831x_GetCali(stk); + return scnprintf(buf, PAGE_SIZE, "%02x\n", status); +} + +static ssize_t stk831x_cali_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long data; + int error; + struct stk831x_data *stk = i2c_get_clientdata(this_client); + error = strict_strtoul(buf, 10, &data); + if (error) + { + printk(KERN_ERR "%s: strict_strtoul failed, error=0x%x\n", __func__, error); + return error; + } + STK831x_SetCali(stk, data); + return count; +} + +static ssize_t stk831x_send_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int error, i; + char *token[2]; + int w_reg[2]; + char buffer[2] = ""; + + for (i = 0; i < 2; i++) + token[i] = strsep((char **)&buf, " "); + if((error = strict_strtoul(token[0], 16, (unsigned long *)&(w_reg[0]))) < 0) + { + printk(KERN_ERR "%s:strict_strtoul failed, error=0x%x\n", __func__, error); + return error; + } + if((error = strict_strtoul(token[1], 16, (unsigned long *)&(w_reg[1]))) < 0) + { + printk(KERN_ERR "%s:strict_strtoul failed, error=0x%x\n", __func__, error); + return error; + } + printk(KERN_INFO "%s: reg[0x%x]=0x%x\n", __func__, w_reg[0], w_reg[1]); + buffer[0] = w_reg[0]; + buffer[1] = w_reg[1]; + error = STK_i2c_Tx(buffer, 2); + if (error < 0) + { + printk(KERN_ERR "%s:failed\n", __func__); + return error; + } + return count; +} + +static ssize_t stk831x_recv_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct stk831x_data *stk = i2c_get_clientdata(this_client); + return scnprintf(buf, PAGE_SIZE, "%02x\n", stk->recv_reg); +} + +static ssize_t stk831x_recv_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + char buffer[2] = ""; + unsigned long data; + int error; + struct stk831x_data *stk = i2c_get_clientdata(this_client); + + error = strict_strtoul(buf, 16, &data); + if (error) + { + printk(KERN_ERR "%s: strict_strtoul failed, error=0x%x\n", __func__, error); + return error; + } + + buffer[0] = data; + error = STK_i2c_Rx(buffer, 2); + if (error < 0) + { + printk(KERN_ERR "%s:failed\n", __func__); + return error; + } + stk->recv_reg = buffer[0]; + printk(KERN_INFO "%s: reg[0x%x]=0x%x\n", __func__, (int)data , (int)buffer[0]); + return count; +} + +static ssize_t stk831x_allreg_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int error; + char buffer[16] = ""; + char show_buffer[14] = ""; + int aa,bb, no, show_no = 0; + + for(bb=0;bb<4;bb++) + { + buffer[0] = bb * 0x10; + error = STK_i2c_Rx(buffer, 16); + if (error < 0) + { + printk(KERN_ERR "%s:failed\n", __func__); + return error; + } + for(aa=0;aa<16;aa++) + { + no = bb*0x10+aa; + printk(KERN_INFO "stk reg[0x%x]=0x%x\n", no, buffer[aa]); + switch(no) + { + case 0x0: + case 0x1: + case 0x2: + case 0x3: + case 0x4: + case 0x5: + case STK831X_INTSU: + case STK831X_MODE: + case STK831X_SR: + case STK831X_OFSX: + case STK831X_OFSY: + case STK831X_OFSZ: + case STK831X_STH: + case 0x24: + show_buffer[show_no] = buffer[aa]; + show_no++; + break; + default: + break; + } + } + } + return scnprintf(buf, PAGE_SIZE, "0x0=%02x,0x1=%02x,0x2=%02x,0x3=%02x,0x4=%02x,0x5=%02x,INTSU=%02x,MODE=%02x,SR=%02x,OFSX=%02x,OFSY=%02x,OFSZ=%02x,STH=%02x,0x24=%02x\n", + show_buffer[0], show_buffer[1], show_buffer[2], show_buffer[3], show_buffer[4], + show_buffer[5], show_buffer[6], show_buffer[7], show_buffer[8], show_buffer[9], + show_buffer[10], show_buffer[11], show_buffer[12], show_buffer[13]); +} + +static ssize_t stk831x_sendo_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int error, i; + char *token[2]; + int w_reg[2]; + char buffer[2] = ""; + + for (i = 0; i < 2; i++) + token[i] = strsep((char **)&buf, " "); + if((error = strict_strtoul(token[0], 16, (unsigned long *)&(w_reg[0]))) < 0) + { + printk(KERN_ERR "%s:strict_strtoul failed, error=0x%x\n", __func__, error); + return error; + } + if((error = strict_strtoul(token[1], 16, (unsigned long *)&(w_reg[1]))) < 0) + { + printk(KERN_ERR "%s:strict_strtoul failed, error=0x%x\n", __func__, error); + return error; + } + printk(KERN_INFO "%s: reg[0x%x]=0x%x\n", __func__, w_reg[0], w_reg[1]); + + buffer[0] = w_reg[0]; + buffer[1] = w_reg[1]; + error = STK831x_WriteByteOTP(buffer[0], buffer[1]); + if (error < 0) + { + printk(KERN_ERR "%s:failed\n", __func__); + return error; + } + return count; +} + + +static ssize_t stk831x_recvo_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + char buffer[2] = ""; + unsigned long data; + int error; + + error = strict_strtoul(buf, 16, &data); + if (error) + { + printk(KERN_ERR "%s: strict_strtoul failed, error=0x%x\n", __func__, error); + return error; + } + + buffer[0] = data; + error = STK831x_ReadByteOTP(buffer[0], &buffer[1]); + if (error < 0) + { + printk(KERN_ERR "%s:failed\n", __func__); + return error; + } + printk(KERN_INFO "%s: reg[0x%x]=0x%x\n", __func__, buffer[0] , buffer[1]); + return count; +} + +static ssize_t stk831x_firlen_show(struct device *dev, +struct device_attribute *attr, char *buf) +{ +#ifdef STK_LOWPASS + struct stk831x_data *stk = i2c_get_clientdata(this_client); + int len = atomic_read(&stk->firlength); + + if(atomic_read(&stk->firlength)) + { + printk(KERN_INFO "len = %2d, idx = %2d\n", stk->fir.num, stk->fir.idx); + printk(KERN_INFO "sum = [%5d %5d %5d]\n", stk->fir.sum[0], stk->fir.sum[1], stk->fir.sum[2]); + printk(KERN_INFO "avg = [%5d %5d %5d]\n", stk->fir.sum[0]/len, stk->fir.sum[1]/len, stk->fir.sum[2]/len); + } + return snprintf(buf, PAGE_SIZE, "%d\n", atomic_read(&stk->firlength)); +#else + return snprintf(buf, PAGE_SIZE, "not support\n"); +#endif +} + +static ssize_t stk831x_firlen_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ +#ifdef STK_LOWPASS + struct stk831x_data *stk = i2c_get_clientdata(this_client); + int error; + unsigned long data; + + error = strict_strtoul(buf, 10, &data); + if (error) + { + printk(KERN_ERR "%s: strict_strtoul failed, error=%d\n", __func__, error); + return error; + } + + if(data > MAX_FIR_LEN) + { + printk(KERN_ERR "%s: firlen exceed maximum filter length\n", __func__); + } + else if (data < 1) + { + atomic_set(&stk->firlength, 1); + atomic_set(&stk->fir_en, 0); + memset(&stk->fir, 0x00, sizeof(stk->fir)); + } + else + { + atomic_set(&stk->firlength, data); + memset(&stk->fir, 0x00, sizeof(stk->fir)); + atomic_set(&stk->fir_en, 1); + } +#else + printk(KERN_ERR "%s: firlen is not supported\n", __func__); +#endif + return count; +} + + +static DEVICE_ATTR(enable, 0644, stk831x_enable_show, stk831x_enable_store); +static DEVICE_ATTR(value, 0444, stk831x_value_show, NULL); +static DEVICE_ATTR(delay, 0644, stk831x_delay_show, stk831x_delay_store); +static DEVICE_ATTR(cali, 0644, stk831x_cali_show, stk831x_cali_store); +static DEVICE_ATTR(send, 0200, NULL, stk831x_send_store); +static DEVICE_ATTR(recv, 0644, stk831x_recv_show, stk831x_recv_store); +static DEVICE_ATTR(allreg, 0444, stk831x_allreg_show, NULL); +static DEVICE_ATTR(sendo, 0200, NULL, stk831x_sendo_store); +static DEVICE_ATTR(recvo, 0200, NULL, stk831x_recvo_store); +static DEVICE_ATTR(firlen, 0644, stk831x_firlen_show, stk831x_firlen_store); + +static struct attribute *stk831x_attributes[] = { + &dev_attr_enable.attr, + &dev_attr_value.attr, + &dev_attr_delay.attr, + &dev_attr_cali.attr, + &dev_attr_send.attr, + &dev_attr_recv.attr, + &dev_attr_allreg.attr, + &dev_attr_sendo.attr, + &dev_attr_recvo.attr, + &dev_attr_firlen.attr, + NULL +}; + +static struct attribute_group stk831x_attribute_group = { +#ifndef STK_ALLWINNER_PLATFORM + .name = "driver", +#endif + .attrs = stk831x_attributes, +}; +static int stk831x_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + int error; + struct stk831x_data *stk; + + printk(KERN_INFO "stk831x_probe: driver version:%s\n",STK_ACC_DRIVER_VERSION); + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + { + printk(KERN_ERR "%s:i2c_check_functionality error\n", __func__); + error = -ENODEV; + goto exit_i2c_check_functionality_error; + } + + stk = kzalloc(sizeof(struct stk831x_data),GFP_KERNEL); + if (!stk) + { + printk(KERN_ERR "%s:memory allocation error\n", __func__); + error = -ENOMEM; + goto exit_kzalloc_error; + } + stk831x_data_ptr = stk; + mutex_init(&stk->write_lock); + +#if (STK_ACC_POLLING_MODE) + stk->stk_acc_wq = create_singlethread_workqueue("stk_acc_wq"); + INIT_WORK(&stk->stk_acc_work, stk_acc_poll_work_func); + hrtimer_init(&stk->acc_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); +// stk->acc_poll_delay = ns_to_ktime(40 * NSEC_PER_MSEC); + stk->acc_poll_delay = ns_to_ktime(STK831X_SAMPLE_TIME[STK831X_INIT_ODR]*USEC_PER_MSEC); + stk->acc_timer.function = stk_acc_timer_func; +#else + stk_mems_work_queue = create_workqueue("stk_mems_wq"); + if(stk_mems_work_queue) + INIT_WORK(&stk->stk_work, stk_mems_wq_function); + else + { + printk(KERN_ERR "%s:create_workqueue error\n", __func__); + error = -EPERM; + goto exit_create_workqueue_error; + } + + error = stk831x_irq_setup(client, stk); + if(!error) + { + goto exit_irq_setup_error; + } +#endif //#if STK_ACC_POLLING_MODE + + i2c_set_clientdata(client, stk); + this_client = client; + + error = STK831x_Init(stk, client); + if (error) + { + printk(KERN_ERR "%s:stk831x initialization failed\n", __func__); + goto exit_stk_init_error; + } + atomic_set(&stk->cali_status, STK_K_NO_CALI); + stk->first_enable = true; + stk->re_enable = false; + event_since_en_limit = 20; + + stk->input_dev = input_allocate_device(); + if (!stk->input_dev) + { + error = -ENOMEM; + printk(KERN_ERR "%s:input_allocate_device failed\n", __func__); + goto exit_input_dev_alloc_error; + } +#ifndef STK_WMT_PLATFORM + stk->input_dev->name = ACC_IDEVICE_NAME; +#else + stk->input_dev->name = "g-sensor"; +#endif + set_bit(EV_ABS, stk->input_dev->evbit); + + input_set_abs_params(stk->input_dev, ABS_X, -65532, 65532, 0, 0); + input_set_abs_params(stk->input_dev, ABS_Y, -65532, 65532, 0, 0); + input_set_abs_params(stk->input_dev, ABS_Z, -65532, 65532, 0, 0); + + + error = input_register_device(stk->input_dev); + if (error) + { + printk(KERN_ERR "%s:Unable to register input device: %s\n", __func__, stk->input_dev->name); + goto exit_input_register_device_error; + } + + error = misc_register(&stk_device); + if (error) + { + printk(KERN_ERR "%s: misc_register failed\n", __func__); + goto exit_misc_device_register_error; + } + error = sysfs_create_group(&stk_device.this_device->kobj, &stk831x_attribute_group); + if (error) + { + printk(KERN_ERR "%s: sysfs_create_group failed\n", __func__); + goto exit_sysfs_create_group_error; + } + + printk(KERN_INFO "%s successfully\n", __func__); + return 0; +exit_sysfs_create_group_error: + //sysfs_remove_group(&stk->input_dev->dev.kobj, &stk831x_attribute_group); + sysfs_remove_group(&stk_device.this_device->kobj, &stk831x_attribute_group); +exit_misc_device_register_error: + misc_deregister(&stk_device); +exit_input_register_device_error: + input_unregister_device(stk->input_dev); +exit_input_dev_alloc_error: +exit_stk_init_error: +#if (STK_ACC_POLLING_MODE) + hrtimer_try_to_cancel(&stk->acc_timer); + destroy_workqueue(stk->stk_acc_wq); +#else + free_irq(client->irq, stk); +#if ADDITIONAL_GPIO_CFG +exit_irq_setup_error: + gpio_free( STK_INT_PIN ); +#endif //#if ADDITIONAL_GPIO_CFG + destroy_workqueue(stk_mems_work_queue); +exit_create_workqueue_error: +#endif //#if (!STK_ACC_POLLING_MODE) + mutex_destroy(&stk->write_lock); + kfree(stk); + stk = NULL; +exit_kzalloc_error: +exit_i2c_check_functionality_error: + return error; +} + +static int stk831x_remove(struct i2c_client *client) +{ + struct stk831x_data *stk = i2c_get_clientdata(client); + + //sysfs_remove_group(&stk->input_dev->dev.kobj, &stk831x_attribute_group); + sysfs_remove_group(&stk_device.this_device->kobj, &stk831x_attribute_group); + misc_deregister(&stk_device); + input_unregister_device(stk->input_dev); + cancel_work_sync(&stk->stk_work); +#if (STK_ACC_POLLING_MODE) + hrtimer_try_to_cancel(&stk->acc_timer); + destroy_workqueue(stk->stk_acc_wq); +#else + free_irq(client->irq, stk); +#if ADDITIONAL_GPIO_CFG + gpio_free( STK_INT_PIN ); +#endif //#if ADDITIONAL_GPIO_CFG + if (stk_mems_work_queue) + destroy_workqueue(stk_mems_work_queue); +#endif //#if (!STK_ACC_POLLING_MODE) + mutex_destroy(&stk->write_lock); + kfree(stk); + stk = NULL; + return 0; +} + +static const struct i2c_device_id stk831x[] = { + { STK831X_I2C_NAME, 0 }, + { } +}; + +#ifdef CONFIG_PM_SLEEP +static int stk831x_suspend(struct device *dev) +{ + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct stk831x_data *stk = i2c_get_clientdata(client); + printk(KERN_INFO "%s\n", __func__); + if(atomic_read(&stk->enabled)) + { + STK831x_SetEnable(stk, 0); + stk->re_enable = true; + } + return 0; +} + + +static int stk831x_resume(struct device *dev) +{ + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct stk831x_data *stk = i2c_get_clientdata(client); +#ifdef STK_RESUME_RE_INIT + int error; +#endif + + printk(KERN_INFO "%s\n", __func__); +#ifdef STK_RESUME_RE_INIT + error = STK831x_Init(stk, this_client); + if (error) + { + printk(KERN_ERR "%s:stk831x initialization failed\n", __func__); + return error; + } + stk->first_enable = true; +#endif + if(stk->re_enable) + { + stk->re_enable = false; + STK831x_SetEnable(stk, 1); + } + return 0; +} +#endif /* CONFIG_PM_SLEEP */ + + +#ifdef CONFIG_PM_RUNTIME +static int stk831x_runtime_suspend(struct device *dev) +{ + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct stk831x_data *stk = i2c_get_clientdata(client); + printk(KERN_INFO "%s\n", __func__); + if(atomic_read(&stk->enabled)) + { + STK831x_SetEnable(stk, 0); + stk->re_enable = true; + } + return 0; +} + + +static int stk831x_runtime_resume(struct device *dev) +{ + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct stk831x_data *stk = i2c_get_clientdata(client); + printk(KERN_INFO "%s\n", __func__); + stk->first_enable = true; + if(stk->re_enable) + { + stk->re_enable = false; + STK831x_SetEnable(stk, 1); + } + return 0; +} +#endif /* CONFIG_PM_RUNTIME */ + +static const struct dev_pm_ops stk831x_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(stk831x_suspend, stk831x_resume) + SET_RUNTIME_PM_OPS(stk831x_runtime_suspend, stk831x_runtime_resume, NULL) +}; + +static void stk831x_shutdown(struct i2c_client *client) +{ + struct stk831x_data *stk = NULL; + + stk = i2c_get_clientdata(client); + hrtimer_cancel(&stk->acc_timer); + cancel_work_sync(&stk->stk_acc_work); +} + +static struct i2c_driver stk831x_driver = { + .probe = stk831x_probe, + .remove = stk831x_remove, + .id_table = stk831x, + .shutdown = stk831x_shutdown, +// .suspend = stk831x_suspend, +// .resume = stk831x_resume, + .driver = { + .name = STK831X_I2C_NAME, + .pm = &stk831x_pm_ops, + }, +}; + + +extern int wmt_getsyspara(char *varname, unsigned char *varval, int *varlen); +static int get_axisset(void* param) +{ + char varbuf[64]; + int n; + int varlen; + //int tmpoff[3] = {0}; + memset(varbuf, 0, sizeof(varbuf)); + varlen = sizeof(varbuf); + if (wmt_getsyspara("wmt.io.stk8312sensor", varbuf, &varlen)) { + errlog("Can't get gsensor config in u-boot!!!!\n"); + return -1; + } else { + n = sscanf(varbuf, "%d:%d:%d:%d:%d:%d:%d:%d:%d:%d", + &l_sensorconfig.op, + &(l_sensorconfig.xyz_axis[0][0]), + &(l_sensorconfig.xyz_axis[0][1]), + &(l_sensorconfig.xyz_axis[1][0]), + &(l_sensorconfig.xyz_axis[1][1]), + &(l_sensorconfig.xyz_axis[2][0]), + &(l_sensorconfig.xyz_axis[2][1]), + &(l_sensorconfig.offset[0]), + &(l_sensorconfig.offset[1]), + &(l_sensorconfig.offset[2])); + if (n != 10) { + printk(KERN_ERR "gsensor format is error in u-boot!!!\n"); + return -1; + } + + printk("get the sensor config: %d:%d:%d:%d:%d:%d:%d:%d:%d:%d\n", + l_sensorconfig.op, + l_sensorconfig.xyz_axis[0][0], + l_sensorconfig.xyz_axis[0][1], + l_sensorconfig.xyz_axis[1][0], + l_sensorconfig.xyz_axis[1][1], + l_sensorconfig.xyz_axis[2][0], + l_sensorconfig.xyz_axis[2][1], + l_sensorconfig.offset[0], + l_sensorconfig.offset[1], + l_sensorconfig.offset[2] + ); + } + return 0; +} + +static int is_stk8312(void) +{ + struct i2c_client *client = NULL; + int ret = 0; + char wbuf[1] = {0x0b}; + char rbuf[1] = {0x0}; + //int devid = 0; + client = this_client; + if (!client) + { + return 0; + } +#if 0 + + devid = i2c_smbus_read_byte_data(client, 0x0b); //fail!!! +#endif + + ret = i2c_master_send(client, wbuf, 1); + ret = i2c_master_recv(client, rbuf, 1); + printk("<<<%s devid 0x%x\n", __FUNCTION__ ,rbuf[0]); +#if 0 //also ok!! + struct i2c_msg msg[2] = { + {.addr = client->addr, + .flags = 0, + .len = 1, + .buf = wbuf, + }, + { .addr = client->addr, + .flags = I2C_M_RD, + .len = 1, + .buf = rbuf, + }, + }; + + ret = i2c_transfer(client->adapter, msg, 2); + + printk("ret %d, id 0x%x\n", ret, rbuf[0]); +#endif + if (rbuf[0] == 0x58) + return 1; + else + return 0; + + +} + +static int __init stk831x_init(void) +{ + int ret = 0; + printk(KERN_EMERG"%d:%s",__LINE__,__func__); + + ret = get_axisset(NULL); + if (ret < 0) + { + printk("<<<<<%s user choose to no sensor chip!\n", __func__); + return ret; + } + + if (!(this_client = sensor_i2c_register_device(0, STKDIR, STK831X_I2C_NAME))) + { + printk(KERN_EMERG"Can't register gsensor i2c device!\n"); + return -1; + } + ret = is_stk8312(); + if (!ret) + { + printk("%s not find stk8312\n", __FUNCTION__); + sensor_i2c_unregister_device(this_client); + return -1; + + } + + + l_dev_class = class_create(THIS_MODULE, GSENSOR_NAME); + //for S40 module to judge whether insmod is ok + if (IS_ERR(l_dev_class)){ + ret = PTR_ERR(l_dev_class); + printk(KERN_ERR "Can't class_create gsensor device !!\n"); + return ret; + } + + ret = i2c_add_driver(&stk831x_driver); + if (ret!=0) + { + printk(KERN_EMERG"======stk831x init fail, ret=0x%x======\n", ret); + i2c_del_driver(&stk831x_driver); + return ret; + } +#ifdef STK_PERMISSION_THREAD + STKPermissionThread = kthread_run( + ,"stk","Permissionthread"); + if(IS_ERR(STKPermissionThread)) + STKPermissionThread = NULL; +#endif // STK_PERMISSION_THREAD + printk(KERN_EMERG"%d:%s",__LINE__,__func__); + + return ret; +} + +static void __exit stk831x_exit(void) +{ + //sensor_i2c_unregister_device(this_client); + i2c_del_driver(&stk831x_driver); + class_destroy(l_dev_class); +#ifdef STK_PERMISSION_THREAD + if(STKPermissionThread) + STKPermissionThread = NULL; +#endif // STK_PERMISSION_THREAD + sensor_i2c_unregister_device(this_client); +} + +module_init(stk831x_init); +module_exit(stk831x_exit); + +MODULE_AUTHOR("Lex Hsieh / Sensortek"); +MODULE_DESCRIPTION("stk831x 3-Axis accelerometer driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(STK_ACC_DRIVER_VERSION); diff --git a/drivers/input/sensor/us5182_lpsensor/Makefile b/drivers/input/sensor/us5182_lpsensor/Makefile new file mode 100755 index 00000000..1f89e698 --- /dev/null +++ b/drivers/input/sensor/us5182_lpsensor/Makefile @@ -0,0 +1,34 @@ +KERNELDIR=../../../../ +#KERNELDIR=/home/hangyan/android8850/kernel/ANDROID_3.0.8 +CROSS = arm_1103_le- +CC= $(CROSS)gcc +LD= $(CROSS)ld +STRIP = $(CROSS)strip + +DEBUG = n + +# Add your debugging flag (or not) to EXTRA_CFLAGS +ifeq ($(DEBUG),y) +# DEBFLAGS = -O -g -DSCULL_DEBUG # "-O" is needed to expand inlines +DEBFLAGS = -O0 -g -DSCULL_DEBUG # "-O" is needed to expand inlines + +else + DEBFLAGS = -O2 -Wall +endif + +EXTRA_CFLAGS += $(DEBFLAGS) + + +MY_MODULE_NAME=s_wmt_lsensor_us5182 + +obj-m := $(MY_MODULE_NAME).o +$(MY_MODULE_NAME)-objs := us5182.o + +default: + $(MAKE) -C $(KERNELDIR) SUBDIRS=$(PWD) modules + $(STRIP) --strip-debug $(MY_MODULE_NAME).ko + rm -rf *.o *~ core .depend .*.cmd *.mod.c .tmp_versions + +clean: + rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions *.order *.symvers + diff --git a/drivers/input/sensor/us5182_lpsensor/us5182.c b/drivers/input/sensor/us5182_lpsensor/us5182.c new file mode 100755 index 00000000..b251fb83 --- /dev/null +++ b/drivers/input/sensor/us5182_lpsensor/us5182.c @@ -0,0 +1,1098 @@ +/* + * us5182.c - us5182 ALS & Proximity Driver + * + * By Intersil Corp + * Michael DiGioia + * + * Based on isl29011.c + * by Mike DiGioia + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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; version 2 of the License. + * + * 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, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include +#include +#include "../sensor.h" +#include "us5182.h" +/* Insmod parameters */ +//I2C_CLIENT_INSMOD_1(us5182); + +#define MODULE_NAME "us5182" + +#undef dbg +#define dbg(fmt, args...) + +#undef errlog +#undef klog +#define errlog(fmt, args...) printk(KERN_ERR "[%s]: " fmt, __FUNCTION__, ## args) +#define klog(fmt, args...) printk(KERN_ALERT "[%s]: " fmt, __FUNCTION__, ## args) + +struct us_device { + struct input_polled_dev* input_poll_devl; + struct input_polled_dev* input_poll_devp; + struct i2c_client* client; + struct class* class; + struct device *lsdev; + dev_t devno; +#ifdef CONFIG_HAS_EARLYSUSPEND + struct early_suspend earlysuspend; +#endif + u8 enable_id; + +}; +static int psh_l8th = 90; +static int psh_h8th = 0; + +static int psl_l8th = 50; +static int psl_h8th = 0; + +static struct i2c_client *this_client = NULL; +/*=====Global variable===============================*/ +static u8 error_flag, debounces; +static int previous_value, this_value; +static struct i2c_client *gclient = NULL; +/*===================================================*/ +static u8 reg_cache[us5182_NUM_CACHABLE_REGS]; + +static struct us_device* l_sensorconfig = NULL; +static int l_enable = 0; // 0:don't report data +static int p_enable = 0; // 0:don't report data + +extern int wmt_getsyspara(char *varname, unsigned char *varval, int *varlen); +static int no_adc_map = 1; + +static DEFINE_MUTEX(mutex); + +static int us5182_i2c_read(struct i2c_client *client,u8 reg) +{ +#if 0 + int val; + val = i2c_smbus_read_byte_data(client, reg); + if (val < 0) + printk("%s %d i2c transfer error\n", __func__, __LINE__); + return val; +#endif +//in default our i2c controller, will not send repeatStart signal in read process.(stop-start) +//well this sensor must have the repeatStart signal to work normally +//so we have to pass I2C_M_NOSTART flag to controller 2013-7-5 + char rdData[2] = {0}; + + struct i2c_msg msgs[2] = + { + {.addr = client->addr, .flags = 0|I2C_M_NOSTART, .len = 1, .buf = rdData,}, + {.addr = client->addr, .flags = I2C_M_RD, .len = 1, .buf = rdData,}, + }; + rdData[0] = reg; + + if (i2c_transfer(client->adapter, msgs, 2) < 0) { + printk( "%s: transfer failed.", __func__); + return -EIO; + } + + return rdData[0]; +} + +static int get_als_resolution(struct i2c_client *client) +{ + return (us5182_i2c_read(client,REGS_CR01) & 0x18) >> 3; +} + + +static int isl_get_lux_datal(struct i2c_client* client) +{ + + int lsb, msb, bitdepth; + + mutex_lock(&mutex); + lsb = us5182_i2c_read(client, REGS_LSB_SENSOR);// + + if (lsb < 0) { + mutex_unlock(&mutex); + return lsb; + } + + msb = us5182_i2c_read(client, REGS_MSB_SENSOR);// + mutex_unlock(&mutex); + + if (msb < 0) + return msb; + + bitdepth = get_als_resolution(client);//????? + switch(bitdepth){ + case 0: + lsb &= 0xF0; // 12bit?? + lsb >>= 4; //add + return ((msb << 4) | lsb); + break; + case 1: + lsb &= 0xFC; //?? 14bit + lsb >>= 2; + return ((msb << 6) | lsb); + break; + } + + return ((msb << 8) | lsb); +} + + +static int get_ps_resolution(struct i2c_client *client) +{ + u8 data; + + data = (us5182_i2c_read(client,REGS_CR02) & 0x18) >> 3; + + return data; +} + +static int isl_get_lux_datap(struct i2c_client* client) +{ + + int lsb, msb, bitdepth; + + mutex_lock(&mutex); + lsb = us5182_i2c_read(client, REGS_LSB_SENSOR_PS); + + if (lsb < 0) { + mutex_unlock(&mutex); + return lsb; + } + + msb = us5182_i2c_read(client, REGS_MSB_SENSOR_PS); + mutex_unlock(&mutex); + + if (msb < 0) + return msb; + + bitdepth = get_ps_resolution(client); + switch(bitdepth){ + case 0: + lsb &= 0xF0; // 12bit ?? + lsb >>= 4; + return ((msb << 4) | lsb); + break; + case 1: + lsb &= 0xFC; // 14bit ?? + lsb >>= 2; + return ((msb << 6) | lsb); + break; + } + + return ((msb << 8) | lsb); //we use 16bit now +} + + + +/* Return 0 if detection is successful, -ENODEV otherwise */ +static int us5182_detect(struct i2c_client *client/*, int kind, + struct i2c_board_info *info*/) +{ + + char rxData[2] = {0xb2, 0}; + int ret = 0; + +#if 1 + //rxData[0] = 0xb2; + + struct i2c_msg msgs[2] = { + + {.addr = client->addr, .flags = 0|I2C_M_NOSTART, .len = 1, .buf = rxData,}, + {.addr = client->addr, .flags = I2C_M_RD, .len = 1, .buf = rxData,} + }; + ret = i2c_transfer(client->adapter, msgs, 2); + if (ret < 0){ + printk(KERN_ERR "%s i2c_transfer error!\n", __FUNCTION__); + return -EIO; + } + +#endif + + if(0x26 == rxData[0]) + { + printk(KERN_ALERT "us5182 detected OK\n"); + return 0; + } + else + return -1; +} + +int isl_input_open(struct input_dev* input) +{ + return 0; +} + +void isl_input_close(struct input_dev* input) +{ +} + +//Fixme plan to transfer the adc value to the config.xml lux 2013-5-10 +static __u16 uadc[8] = {2, 8, 100, 400, 900, 1000, 1500, 1900};//customize +static __u16 ulux[9] = {128, 200, 1300, 2000, 3000, 4000, 5000, 6000, 7000}; +static __u16 adc_to_lux(__u16 adc) +{ + static long long var = 0; + int i = 0; //length of array is 8,9 + for (i=0; i<8; i++) { + if ( adc < uadc[i]){ + break; + } + } + if ( i<9) + { + var++; + if (var%2) + return ulux[i]+0; + else + return ulux[i]-1; + } + return ulux[4]; +} + +static void isl_input_lux_poll_l(struct input_polled_dev *dev) +{ + struct us_device* idev = dev->private; + struct input_dev* input = idev->input_poll_devl->input; + struct i2c_client* client = idev->client; + int ret_val = 0; + + if (client == NULL){ + printk("%s client NULL!\n", __FUNCTION__); + return; + } + + //printk("%s\n", __FUNCTION__); + if (l_enable != 0) + { + //mutex_lock(&mutex); //dead lock!! 2013-7-9!!! + //printk(KERN_ALERT "by flashchen val is %x",val); + ret_val = isl_get_lux_datal(client); //adc + if (ret_val < 0) + return; + if (!no_adc_map) + ret_val = adc_to_lux(ret_val); + + input_report_abs(input, ABS_MISC, ret_val); + //printk("%s %d\n", __FUNCTION__, ret_val); + input_sync(input); + //mutex_unlock(&mutex); + } +} + +static void isl_input_lux_poll_p(struct input_polled_dev *dev) +{ + struct us_device* idev = dev->private; + struct input_dev* input = idev->input_poll_devp->input; + struct i2c_client* client = idev->client; + + int tmp_val = 0, debounce = 0; + int ret_val = 0; + + //printk("%s\n", __FUNCTION__); + if (p_enable != 0) + { + //mutex_lock(&mutex); + //printk(KERN_ALERT "by flashchen val is %x",val); + + #if 0 //just read raw data out 2013-7-18 + for (debounce=0; debounce<10; debounce++){ + ret_val = isl_get_lux_datap(client); + if (ret_val < 0) + return; + + tmp_val += ret_val; + msleep(1); + } + tmp_val /= 10; + //add for near/far detection! + if (tmp_val > 0x00ff) + tmp_val = 6; + else + tmp_val = 0; + input_report_abs(input, ABS_MISC, tmp_val); + input_sync(input); + #endif + + tmp_val = us5182_i2c_read(client, REGS_CR00); + + if (tmp_val & CR0_PROX_MASK) //approach + input_report_abs(input, ABS_MISC, 0); + else + input_report_abs(input, ABS_MISC, 6); + + input_sync(input); + //printk("%s %d\n", __FUNCTION__, tmp_val); + //mutex_unlock(&mutex); + } +} + +#if 0 +static struct i2c_device_id us5182_id[] = { + {"us5182", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, us5182_id); +#endif // 2013-7-9 + +static int mmad_open(struct inode *inode, struct file *file) +{ + dbg("Open the l-sensor node...\n"); + return 0; +} + +static int mmad_release(struct inode *inode, struct file *file) +{ + dbg("Close the l-sensor node...\n"); + return 0; +} + +static ssize_t mmadl_read(struct file *fl, char __user *buf, size_t cnt, loff_t *lf) +{ + int lux_data = 0; + //printk("%s try to mutex_lock \n", __FUNCTION__); + //mutex_lock(&mutex); + //printk("lock ok!\n"); + lux_data = isl_get_lux_datal(l_sensorconfig->client); + //mutex_unlock(&mutex); + if (lux_data < 0) + { + printk("Failed to read lux data!\n"); + return -1; + } + printk(KERN_ALERT "lux_data is %x\n",lux_data); + //return 0; + copy_to_user(buf, &lux_data, sizeof(lux_data)); + return sizeof(lux_data); +} + + +static ssize_t mmadp_read(struct file *fl, char __user *buf, size_t cnt, loff_t *lf) +{ + int lux_data = 0; + + //mutex_lock(&mutex); + lux_data = isl_get_lux_datap(l_sensorconfig->client); + //mutex_unlock(&mutex); + if (lux_data < 0) + { + errlog("Failed to read lux data!\n"); + return -1; + } + printk(KERN_ALERT "lux_data is %x\n",lux_data); + //return 0; + copy_to_user(buf, &lux_data, sizeof(lux_data)); + return sizeof(lux_data); +} + +static long +mmadl_ioctl(/*struct inode *inode,*/ struct file *file, unsigned int cmd, + unsigned long arg) +{ + void __user *argp = (void __user *)arg; + //char rwbuf[5]; + short enable; //amsr = -1; + unsigned int uval; + + //printk("l-sensor ioctr... cmd 0x%x arg %d\n", cmd, arg); + //memset(rwbuf, 0, sizeof(rwbuf)); + switch (cmd) { + case LIGHT_IOCTL_SET_ENABLE: + // enable/disable sensor + if (copy_from_user(&enable, argp, sizeof(short))) + { + printk(KERN_ERR "Can't get enable flag!!!\n"); + return -EFAULT; + } + dbg("enable=%d\n",enable); + if ((enable >=0) && (enable <=1)) + { + dbg("driver: disable/enable(%d) gsensor.\n", enable); + + //l_sensorconfig.sensor_enable = enable; + dbg("Should to implement d/e the light sensor!\n"); + l_enable = enable; + + } else { + printk(KERN_ERR "Wrong enable argument in %s !!!\n", __FUNCTION__); + return -EINVAL; + } + break; + case WMT_IOCTL_SENSOR_GET_DRVID: +#define DRVID 0 + uval = DRVID ; + if (copy_to_user((unsigned int*)arg, &uval, sizeof(unsigned int))) + { + return -EFAULT; + } + dbg("us5182_driver_id:%d\n",uval); + default: + break; + } + + return 0; +} + + +static long +mmadp_ioctl(/*struct inode *inode,*/ struct file *file, unsigned int cmd, + unsigned long arg) +{ + void __user *argp = (void __user *)arg; + //char rwbuf[5]; + short enable; //amsr = -1; + unsigned int uval; + unsigned char regval; + + dbg("l-sensor ioctr...\n"); + //memset(rwbuf, 0, sizeof(rwbuf)); + switch (cmd) { + case LIGHT_IOCTL_SET_ENABLE: + // enable/disable sensor + if (copy_from_user(&enable, argp, sizeof(short))) + { + printk(KERN_ERR "Can't get enable flag!!!\n"); + return -EFAULT; + } + dbg("enable=%d\n",enable); + if ((enable >=0) && (enable <=1)) + { + dbg("driver: disable/enable(%d) gsensor.\n", enable); + + //l_sensorconfig.sensor_enable = enable; + dbg("Should to implement d/e the light sensor!\n"); + p_enable = enable; + #if 1 + if(p_enable) + { + regval = us5182_i2c_read(l_sensorconfig->client, 0); + regval &= ~(3 << 4); + i2c_smbus_write_byte_data(l_sensorconfig->client, 0, regval); + } + else + { + regval = us5182_i2c_read(l_sensorconfig->client, 0); + regval &= ~(3 << 4); + regval |= (1 << 4); + i2c_smbus_write_byte_data(l_sensorconfig->client, 0, regval); + } + #endif + + } else { + printk(KERN_ERR "Wrong enable argument in %s !!!\n", __FUNCTION__); + return -EINVAL; + } + break; + case WMT_IOCTL_SENSOR_GET_DRVID: +#define DRVID 0 + uval = DRVID ; + if (copy_to_user((unsigned int*)arg, &uval, sizeof(unsigned int))) + { + return -EFAULT; + } + dbg("us5182_driver_id:%d\n",uval); + default: + break; + } + + return 0; +} + + +static struct file_operations mmadl_fops = { + .owner = THIS_MODULE, + .open = mmad_open, + .release = mmad_release, + .read = mmadl_read, + .unlocked_ioctl = mmadl_ioctl, +}; + +static struct miscdevice mmadl_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "lsensor_ctrl", + .fops = &mmadl_fops, +}; + +static struct file_operations mmadp_fops = { + .owner = THIS_MODULE, + .open = mmad_open, + .release = mmad_release, + .read = mmadp_read, + .unlocked_ioctl = mmadp_ioctl, +}; + +static struct miscdevice mmadp_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "psensor_ctrl", + .fops = &mmadp_fops, +}; + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void us5182_early_suspend(struct early_suspend *h) +{ + dbg("start\n"); + mutex_lock(&mutex); + //pm_runtime_get_sync(dev); + //isl_set_mod(client, ISL_MOD_POWERDOWN); + //pm_runtime_put_sync(dev); + mutex_unlock(&mutex); + dbg("exit\n"); +} + +static void us5182_late_resume(struct early_suspend *h) +{ + struct i2c_client *client = l_sensorconfig->client; + + dbg("start\n"); + mutex_lock(&mutex); + //pm_runtime_get_sync(dev); + //isl_set_mod(client, last_mod); + //isl_set_default_config(client); + //pm_runtime_put_sync(dev); + mutex_unlock(&mutex); + dbg("exit\n"); +} +#endif + +int us5182_i2c_write(struct i2c_client *client, u8 reg,u8 mask, u8 shift, int val ) { + //struct us5182_data *data = i2c_get_clientdata(client); + int err; + u8 tmp; + mutex_lock(&mutex); + + tmp = reg_cache[reg]; + tmp &= ~mask; + tmp |= val << shift; + + err = i2c_smbus_write_byte_data(client, reg, tmp); + if (!err) + reg_cache[reg] = tmp; + + mutex_unlock(&mutex); + if (err >= 0) return 0; + + printk("%s %d i2c transfer error\n", __func__, __LINE__); + return err; +} + + +static int set_word_mode(struct i2c_client *client, int mode) +{ + // + return us5182_i2c_write(client, REGS_CR00, + CR0_WORD_MASK, CR0_WORD_SHIFT, mode); +} + + +static int set_oneshotmode(struct i2c_client *client, int mode) +{ + return us5182_i2c_write(client,REGS_CR00,CR0_ONESHOT_MASK, CR0_ONESHOT_SHIFT, mode); +} + + +static int set_opmode(struct i2c_client *client, int mode) +{ + return us5182_i2c_write(client,REGS_CR00,CR0_OPMODE_MASK, + CR0_OPMODE_SHIFT, mode); +} + +/* power_status */ +static int set_power_status(struct i2c_client *client, int status) +{ + if(status == CR0_SHUTDOWN_EN ) + return us5182_i2c_write(client,REGS_CR00,CR0_SHUTDOWN_MASK, + CR0_SHUTDOWN_SHIFT,CR0_SHUTDOWN_EN); + else + return us5182_i2c_write(client,REGS_CR00,CR0_SHUTDOWN_MASK, + CR0_SHUTDOWN_SHIFT, CR0_OPERATION); +} + + +static int us5182_init_client(struct i2c_client *client) +{ + + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + int i = 0; + int v = -1; + if ( !i2c_check_functionality(adapter,I2C_FUNC_SMBUS_BYTE_DATA) ) { + printk(KERN_INFO "byte op is not permited.\n"); + return -EIO; + } + + /* read all the registers once to fill the cache. + * if one of the reads fails, we consider the init failed */ + + for (i = 0; i < ARRAY_SIZE(reg_cache); i++) { + v = us5182_i2c_read(client, i); + printk("reg 0x%x value 0x%x \n", i, v); + if (v < 0) + return -ENODEV; + reg_cache[i] = v; + } + + /*Set Default*/ + set_word_mode(client, 0);//word enable? //just byte one time + set_power_status(client,CR0_OPERATION); //power on? + set_opmode(client,CR0_OPMODE_ALSONLY); //CR0_OPMODE_ALSANDPS CR0_OPMODE_ALSONLY + set_oneshotmode(client, CR0_ONESHOT_DIS); + + us5182_i2c_write(client, REGS_CR03, CR3_LEDDR_MASK, CR3_LEDDR_SHIFT, CR3_LEDDR_50); + + //set als gain + us5182_i2c_write(client, REGS_CR01, CR1_ALS_GAIN_MASK, CR1_ALS_GAIN_SHIFT, CR1_ALS_GAIN_X8); + + //set ps threshold --> lth, hth + us5182_i2c_write(client, REGS_INT_LSB_TH_HI_PS, 0xff, 0, psh_l8th); // + us5182_i2c_write(client, REGS_INT_MSB_TH_HI_PS, 0xff, 0, psh_h8th); //0 default + + us5182_i2c_write(client, REGS_INT_LSB_TH_LO_PS, 0xff, 0, psl_l8th); // + us5182_i2c_write(client, REGS_INT_MSB_TH_LO_PS, 0xff, 0, psl_h8th); // 0 default + //set_resolution(client,us5182_RES_16); + //set_range(client,3); + //set_int_ht(client,0x3E8); //1000 lux + //set_int_lt(client,0x8); //8 lux + //dev_info(&data->client->dev, "us5182 ver. %s found.\n",DRIVER_VERSION); + + /*init global variable*/ + error_flag = 0; + previous_value = 0; + this_value = 0; + debounces = 2;//debounces 5 times + return 0; +} +//******add for sys debug devic_attribute +static ssize_t reg_show(struct device *dev, struct device_attribute *attr, char *buf) { + + int i = 0, val = 0; + printk("<<<<<<<<0x%x\n", reg, res, val); + + return count; +} +//struct device_attribute dev_attr_reg = __ATTR(reg, 0644, reg_show, reg_store); +//DEVICE_ATTR(reg, 0644, reg_show, reg_store); + +static ssize_t adc_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + + int i; + int size = sizeof(uadc)/sizeof(uadc[0]); + printk("<<<%s\n", __FUNCTION__); + for (i=0; i>>\n", buf); + n = sscanf(buf, "%d:%d", &index, &tmp); + printk("<<<=0 && index=0; i--) + device_remove_file(dev, &attr[i]);//&attr[i].attr + } + return err; +} + +static void device_remove_attribute(struct device *dev, struct device_attribute *attr) +{ + int i; + for (i=0; attr[i].attr.name != NULL; i++) + device_remove_file(dev, &attr[i]); //&attr[i].attr +} +//add end +static int +us5182_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + int res=0; + + struct us_device* idev = kzalloc(sizeof(struct us_device), GFP_KERNEL); + if(!idev) + return -ENOMEM; + + l_sensorconfig = idev; + + /*initial enable device id*/ + idev->enable_id = 0x00; + + gclient = client; + /* initialize the us5182 chip */ + res = us5182_init_client(client);// + + if (res != 0) + goto err_input_allocate_device; +/* last mod is ALS continuous */ + //pm_runtime_enable(&client->dev); + idev->input_poll_devl = input_allocate_polled_device(); + if(!idev->input_poll_devl) + { + res = -ENOMEM; + goto err_input_allocate_device; + } + idev->input_poll_devp = input_allocate_polled_device(); + if(!idev->input_poll_devp) + { + res = -ENOMEM; + goto err_input_allocate_device; + } + idev->client = client; + + idev->input_poll_devl->private = idev; + idev->input_poll_devl->poll = isl_input_lux_poll_l; + idev->input_poll_devl->poll_interval = 100;//50; + idev->input_poll_devl->input->open = isl_input_open; + idev->input_poll_devl->input->close = isl_input_close; + idev->input_poll_devl->input->name = "lsensor_lux"; + idev->input_poll_devl->input->id.bustype = BUS_I2C; + idev->input_poll_devl->input->dev.parent = &client->dev; + + input_set_drvdata(idev->input_poll_devl->input, idev); + input_set_capability(idev->input_poll_devl->input, EV_ABS, ABS_MISC); + input_set_abs_params(idev->input_poll_devl->input, ABS_MISC, 0, 16000, 0, 0); + + idev->input_poll_devp->private = idev; + idev->input_poll_devp->poll = isl_input_lux_poll_p; + idev->input_poll_devp->poll_interval = 10;//100; 50ms + idev->input_poll_devp->input->open = isl_input_open; + idev->input_poll_devp->input->close = isl_input_close; + idev->input_poll_devp->input->name = "psensor_lux"; + idev->input_poll_devp->input->id.bustype = BUS_I2C; + idev->input_poll_devp->input->dev.parent = &client->dev; + + input_set_drvdata(idev->input_poll_devp->input, idev); + input_set_capability(idev->input_poll_devp->input, EV_ABS, ABS_MISC); + input_set_abs_params(idev->input_poll_devp->input, ABS_MISC, 0, 16000, 0, 0); + i2c_set_clientdata(client, idev); + /* set default config after set_clientdata */ + //res = isl_set_default_config(client); + res = misc_register(&mmadl_device); + if (res) { + errlog("mmad_device register failed\n"); + goto err_misc_registerl; + } + res = misc_register(&mmadp_device); + if (res) { + errlog("mmad_device register failed\n"); + goto err_misc_registerp; + } + res = input_register_polled_device(idev->input_poll_devl); + if(res < 0) + goto err_input_register_devicel; + res = input_register_polled_device(idev->input_poll_devp); + if(res < 0) + goto err_input_register_devicep; + // suspend/resume register +#ifdef CONFIG_HAS_EARLYSUSPEND + idev->earlysuspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; + idev->earlysuspend.suspend = us5182_early_suspend; + idev->earlysuspend.resume = us5182_late_resume; + register_early_suspend(&(idev->earlysuspend)); +#endif + + dbg("us5182 probe succeed!\n"); + res = alloc_chrdev_region(&idev->devno, 0, 1, "us5182"); + if(res) + { + printk("can't allocate chrdev\n"); + return 0; + } + idev->class = class_create(THIS_MODULE, "us5182-lsensor"); + if (IS_ERR(idev->class)) { + printk("<<< %s class_create() error!\n", __FUNCTION__); + return 0; + } + idev->lsdev = device_create(idev->class, NULL, idev->devno, NULL, "us5182"); + if (IS_ERR(idev->lsdev)) { + printk("<<< %s device_create() error!\n", __FUNCTION__); + return 0; + } + res = device_create_attribute(idev->lsdev, us5182_attr); + return 0; +err_input_register_devicep: + input_free_polled_device(idev->input_poll_devp); +err_input_register_devicel: + input_free_polled_device(idev->input_poll_devl); +err_misc_registerp: + misc_deregister(&mmadp_device); +err_misc_registerl: + misc_deregister(&mmadl_device); +err_input_allocate_device: + //__pm_runtime_disable(&client->dev, false); + kfree(idev); + return res; +} + +static int us5182_remove(struct i2c_client *client) +{ + int i = 0; + struct us_device* idev = i2c_get_clientdata(client); +#if 1 + //device_remove_file(idev->lsdev, &dev_attr_reg); + device_remove_attribute(idev->lsdev, us5182_attr); + unregister_chrdev_region(idev->devno, 1); + device_destroy(idev->class, idev->devno); + class_destroy(idev->class); +#endif + printk("%s %d\n", __FUNCTION__, i++); // 0 + //unregister_early_suspend(&(idev->earlysuspend)); + misc_deregister(&mmadl_device); + printk("%s %d\n", __FUNCTION__, i++); + misc_deregister(&mmadp_device); + printk("%s %d\n", __FUNCTION__, i++); + input_unregister_polled_device(idev->input_poll_devl);//here block?? + printk("%s %d\n", __FUNCTION__, i++); + input_unregister_polled_device(idev->input_poll_devp); + printk("%s %d\n", __FUNCTION__, i++); + input_free_polled_device(idev->input_poll_devl); + printk("%s %d\n", __FUNCTION__, i++); + input_free_polled_device(idev->input_poll_devp); + printk("%s %d\n", __FUNCTION__, i++); + //__pm_runtime_disable(&client->dev, false); + + kfree(idev); + printk(KERN_INFO MODULE_NAME ": %s us5182 remove call, \n", __func__); + return 0; +} +static void us5182_shutdown(struct i2c_client *client) +{ + l_enable = 0; + p_enable = 0; +} + +static int us5182_suspend(struct i2c_client *client, pm_message_t message) +{ + return 0; +} +static int us5182_resume(struct i2c_client *client) +{ + int res = 0; + res = us5182_init_client(client);// + return 0; +} + +static const struct i2c_device_id us5182_id[] = { + { SENSOR_I2C_NAME , 0 }, + {}, +}; + + +static struct i2c_driver us5182_i2c_driver = +{ + .probe = us5182_probe, + .remove = us5182_remove, + .suspend = us5182_suspend, + .resume = us5182_resume, + .shutdown = us5182_shutdown, + .driver = { + .name = SENSOR_I2C_NAME, + .owner = THIS_MODULE, + }, + .id_table = us5182_id, +}; + +static int get_adc_val(void) +{ + int i=0, varlen=0, n=0; + __u32 buf[8] = {0}; + char varbuf[50] ={0}; + char *name = "wmt.io.lsensor"; + char *psth = "wmt.io.psensor"; + int thbuf[4] = {0}; + + varlen = sizeof(varbuf); + if (wmt_getsyspara(psth, varbuf, &varlen)) + { + printk("<<<. All Rights Reserved. + * us5182 Light Sensor Driver for Linux 2.6 + */ + +/* + * 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 2 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef __us5182_H__ +#define __us5182_H__ + +#include + +#define SENSOR_I2C_NAME "us5182" +#define SENSOR_I2C_ADDR (0x72>>1) //0x72 //7bit 0x39 +/*us5182 Slave Addr*/ +#define LIGHT_ADDR 0x39 //0x72 //7bit 0x39 + +/*Interrupt PIN for S3C6410*/ +//#define IRQ_LIGHT_INT IRQ_EINT(6) //comment for compile error + +/*Register Set*/ +#define REGS_CR00 0x00 +#define REGS_CR01 0x01 +#define REGS_CR02 0x02 +#define REGS_CR03 0x03 +//ALS +#define REGS_INT_LSB_TH_LO 0x04 +#define REGS_INT_MSB_TH_LO 0x05 +#define REGS_INT_LSB_TH_HI 0x06 +#define REGS_INT_MSB_TH_HI 0x07 +//PS +#define REGS_INT_LSB_TH_LO_PS 0x08 +#define REGS_INT_MSB_TH_LO_PS 0x09 +#define REGS_INT_LSB_TH_HI_PS 0x0A +#define REGS_INT_MSB_TH_HI_PS 0x0B +//ALS data +#define REGS_LSB_SENSOR 0x0C +#define REGS_MSB_SENSOR 0x0D +//PS data +#define REGS_LSB_SENSOR_PS 0x0E +#define REGS_MSB_SENSOR_PS 0x0F + +#define REGS_CR10 0x10 +#define REGS_CR11 0x11 +#define REGS_CR16 0x16 +#define REGS_CR20 0x20 +#define REGS_CR21 0x21 +#define REGS_CR22 0x22 +#define REGS_CR29 0x29 +#define REGS_CR2A 0x2A +#define REGS_CR2B 0x2B +#define REGS_VERSION_ID 0x1F +#define REGS_CHIP_ID 0xB2 + +/*ShutDown_EN*/ +#define CR0_OPERATION 0x0 +#define CR0_SHUTDOWN_EN 0x1 + +#define CR0_SHUTDOWN_SHIFT (7) +#define CR0_SHUTDOWN_MASK (0x1 << CR0_SHUTDOWN_SHIFT) + +/*OneShot_EN*/ +#define CR0_ONESHOT_EN 0x01 +#define CR0_ONESHOT_DIS 0x00 +#define CR0_ONESHOT_SHIFT (6) +#define CR0_ONESHOT_MASK (0x1 << CR0_ONESHOT_SHIFT) + +/*Operation Mode*/ +#define CR0_OPMODE_ALSANDPS 0x0 +#define CR0_OPMODE_ALSONLY 0x1 +#define CR0_OPMODE_IRONLY 0x2 + +#define CR0_OPMODE_SHIFT (4) +#define CR0_OPMODE_MASK (0x3 << CR0_OPMODE_SHIFT) + +/*all int flag (PROX, INT_A, INT_P)*/ +#define CR0_ALL_INT_CLEAR 0x0 + +#define CR0_ALL_INT_SHIFT (1) +#define CR0_ALL_INT_MASK (0x7 << CR0_ALL_INT_SHIFT) + + +/*indicator of object proximity detection*/ +#define CR0_PROX_CLEAR 0x0 + +#define CR0_PROX_SHIFT (3) +#define CR0_PROX_MASK (0x1 << CR0_PROX_SHIFT) + +/*interrupt status of proximity sensor*/ +#define CR0_INTP_CLEAR 0x0 + +#define CR0_INTP_SHIFT (2) +#define CR0_INTP_MASK (0x1 << CR0_INTP_SHIFT) + +/*interrupt status of ambient sensor*/ +#define CR0_INTA_CLEAR 0x0 + +#define CR0_INTA_SHIFT (1) +#define CR0_INTA_MASK (0x1 << CR0_INTA_SHIFT) + +/*Word mode enable*/ +#define CR0_WORD_EN 0x1 + +#define CR0_WORD_SHIFT (0) +#define CR0_WORD_MASK (0x1 << CR0_WORD_SHIFT) + + +/*ALS fault queue depth for interrupt enent output*/ +#define CR1_ALS_FQ_1 0x0 +#define CR1_ALS_FQ_4 0x1 +#define CR1_ALS_FQ_8 0x2 +#define CR1_ALS_FQ_16 0x3 +#define CR1_ALS_FQ_24 0x4 +#define CR1_ALS_FQ_32 0x5 +#define CR1_ALS_FQ_48 0x6 +#define CR1_ALS_FQ_63 0x7 + +#define CR1_ALS_FQ_SHIFT (5) +#define CR1_ALS_FQ_MASK (0x7 << CR1_ALS_FQ_SHIFT) + +/*resolution for ALS*/ +#define CR1_ALS_RES_12BIT 0x0 +#define CR1_ALS_RES_14BIT 0x1 +#define CR1_ALS_RES_16BIT 0x2 +#define CR1_ALS_RES_16BIT_2 0x3 + +#define CR1_ALS_RES_SHIFT (3) +#define CR1_ALS_RES_MASK (0x3 << CR1_ALS_RES_SHIFT) + +/*sensing amplifier selection for ALS*/ +#define CR1_ALS_GAIN_X1 0x0 +#define CR1_ALS_GAIN_X2 0x1 +#define CR1_ALS_GAIN_X4 0x2 +#define CR1_ALS_GAIN_X8 0x3 +#define CR1_ALS_GAIN_X16 0x4 +#define CR1_ALS_GAIN_X32 0x5 +#define CR1_ALS_GAIN_X64 0x6 +#define CR1_ALS_GAIN_X128 0x7 + +#define CR1_ALS_GAIN_SHIFT (0) +#define CR1_ALS_GAIN_MASK (0x7 << CR1_ALS_GAIN_SHIFT) + + +/*PS fault queue depth for interrupt event output*/ +#define CR2_PS_FQ_1 0x0 +#define CR2_PS_FQ_4 0x1 +#define CR2_PS_FQ_8 0x2 +#define CR2_PS_FQ_15 0x3 + +#define CR2_PS_FQ_SHIFT (6) +#define CR2_PS_FQ_MASK (0x3 << CR2_PS_FQ_SHIFT) + +/*interrupt type setting */ +/*low active*/ +#define CR2_INT_LEVEL 0x0 +/*low pulse*/ +#define CR2_INT_PULSE 0x1 + +#define CR2_INT_SHIFT (5) +#define CR2_INT_MASK (0x1 << CR2_INT_SHIFT) + +/*resolution for PS*/ +#define CR2_PS_RES_12 0x0 +#define CR2_PS_RES_14 0x1 +#define CR2_PS_RES_16 0x2 +#define CR2_PS_RES_16_2 0x3 + +#define CR2_PS_RES_SHIFT (3) +#define CR2_PS_RES_MASK (0x3 << CR2_PS_RES_SHIFT) + +/*sensing amplifier selection for PS*/ +#define CR2_PS_GAIN_1 0x0 +#define CR2_PS_GAIN_2 0x1 +#define CR2_PS_GAIN_4 0x2 +#define CR2_PS_GAIN_8 0x3 +#define CR2_PS_GAIN_16 0x4 +#define CR2_PS_GAIN_32 0x5 +#define CR2_PS_GAIN_64 0x6 +#define CR2_PS_GAIN_128 0x7 + +#define CR2_PS_GAIN_SHIFT (0) +#define CR2_PS_GAIN_MASK (0x7 << CR2_PS_GAIN_SHIFT) + +/*wait-time slot selection*/ +#define CR3_WAIT_SEL_0 0x0 +#define CR3_WAIT_SEL_4 0x1 +#define CR3_WAIT_SEL_8 0x2 +#define CR3_WAIT_SEL_16 0x3 + +#define CR3_WAIT_SEL_SHIFT (6) +#define CR3_WAIT_SEL_MASK (0x3 << CR3_WAIT_SEL_SHIFT) + +/*IR-LED drive peak current setting*/ +#define CR3_LEDDR_12_5 0x0 +#define CR3_LEDDR_25 0x1 +#define CR3_LEDDR_50 0x2 +#define CR3_LEDDR_100 0x3 + +#define CR3_LEDDR_SHIFT (4) +#define CR3_LEDDR_MASK (0x3 << CR3_LEDDR_SHIFT) + +/*INT pin source selection*/ +#define CR3_INT_SEL_BATH 0x0 +#define CR3_INT_SEL_ALS 0x1 +#define CR3_INT_SEL_PS 0x2 +#define CR3_INT_SEL_PSAPP 0x3 + +#define CR3_INT_SEL_SHIFT (2) +#define CR3_INT_SEL_MASK (0x3 << CR3_INT_SEL_SHIFT) + +/*software reset for register and core*/ +#define CR3_SOFTRST_EN 0x1 + +#define CR3_SOFTRST_SHIFT (0) +#define CR3_SOFTRST_MASK (0x1 << CR3_SOFTRST_SHIFT) + +/*modulation frequency of LED driver*/ +#define CR10_FREQ_DIV2 0x0 +#define CR10_FREQ_DIV4 0x1 +#define CR10_FREQ_DIV8 0x2 +#define CR10_FREQ_DIV16 0x3 + +#define CR10_FREQ_SHIFT (1) +#define CR10_FREQ_MASK (0x3 << CR10_FREQ_SHIFT) + +/*50/60 Rejection enable*/ +#define CR10_REJ_5060_DIS 0x00 +#define CR10_REJ_5060_EN 0x01 + +#define CR10_REJ_5060_SHIFT (0) +#define CR10_REJ_5060_MASK (0x1 << CR10_REJ_5060_SHIFT) + +#define us5182_NUM_CACHABLE_REGS 0x12 + +/*enable sensor*/ +#define ID_LIGHT 0 +#define ID_PROXIMITY 1 + +#define DEVICE_LIGHT (1 << ID_LIGHT) +#define DEVICE_PROXIMITY (1 << ID_PROXIMITY) +#endif -- cgit