diff options
author | codeworkx <codeworkx@cyanogenmod.com> | 2012-09-22 09:48:20 +0200 |
---|---|---|
committer | codeworkx <codeworkx@cyanogenmod.com> | 2012-09-22 14:02:16 +0200 |
commit | 2489007e7d740ccbc3e0a202914e243ad5178787 (patch) | |
tree | b8e6380ea7b1da63474ad68a5dba997e01146043 /drivers/input/misc | |
parent | 5f67568eb31e3a813c7c52461dcf66ade15fc2e7 (diff) | |
download | kernel_samsung_smdk4412-2489007e7d740ccbc3e0a202914e243ad5178787.zip kernel_samsung_smdk4412-2489007e7d740ccbc3e0a202914e243ad5178787.tar.gz kernel_samsung_smdk4412-2489007e7d740ccbc3e0a202914e243ad5178787.tar.bz2 |
merge opensource jb u5
Change-Id: I1aaec157aa196f3448eff8636134fce89a814cf2
Diffstat (limited to 'drivers/input/misc')
-rwxr-xr-x[-rw-r--r--] | drivers/input/misc/Kconfig | 13 | ||||
-rwxr-xr-x[-rw-r--r--] | drivers/input/misc/Makefile | 4 | ||||
-rw-r--r--[-rwxr-xr-x] | drivers/input/misc/ak8975.c | 5 | ||||
-rw-r--r-- | drivers/input/misc/flip.c | 408 | ||||
-rw-r--r--[-rwxr-xr-x] | drivers/input/misc/gp2a.c | 66 | ||||
-rw-r--r-- | drivers/input/misc/kr3dh.c | 1352 |
6 files changed, 1787 insertions, 61 deletions
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 8a356ef..9b75c47 100644..100755 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -515,4 +515,17 @@ config INPUT_XEN_KBDDEV_FRONTEND To compile this driver as a module, choose M here: the module will be called xen-kbdfront. +config INPUT_FLIP + tristate "INPUT_FLIP" + help + Say Y here if you want to support gpio based Flip... + +config INPUT_KR3DH + tristate "ST KR3DH accelerometer sensor" + depends on I2C + help + Say Y here if you want to support for ST KR3DH accelerometer sensor. + + To compile this driver as a module, choose M here: the + module will be called kr3dh. endif diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 1d6cd26..549156c 100644..100755 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_HP_SDC_RTC) += hp_sdc_rtc.o obj-$(CONFIG_INPUT_IXP4XX_BEEPER) += ixp4xx-beeper.o obj-$(CONFIG_INPUT_KEYCHORD) += keychord.o obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o +obj-$(CONFIG_INPUT_KR3DH) += kr3dh.o obj-$(CONFIG_INPUT_M68K_BEEP) += m68kspkr.o obj-$(CONFIG_INPUT_MAX8925_ONKEY) += max8925_onkey.o obj-$(CONFIG_INPUT_PCAP) += pcap_keys.o @@ -49,4 +50,5 @@ obj-$(CONFIG_INPUT_XEN_KBDDEV_FRONTEND) += xen-kbdfront.o obj-$(CONFIG_INPUT_YEALINK) += yealink.o obj-$(CONFIG_SENSORS_BH1721FVC) += bh1721fvc.o obj-$(CONFIG_MPU_SENSORS_AK8975) += ak8975.o -obj-$(CONFIG_OPTICAL_GP2A) += gp2a.o
\ No newline at end of file +obj-$(CONFIG_OPTICAL_GP2A) += gp2a.o +obj-$(CONFIG_INPUT_FLIP) += flip.o
\ No newline at end of file diff --git a/drivers/input/misc/ak8975.c b/drivers/input/misc/ak8975.c index d95f229..027c2df 100755..100644 --- a/drivers/input/misc/ak8975.c +++ b/drivers/input/misc/ak8975.c @@ -12,7 +12,9 @@ * GNU General Public License for more details. * */ - +#ifdef CONFIG_MPU_SENSORS_MPU3050 +#define FACTORY_TEST +#endif #include <linux/i2c.h> #include <linux/interrupt.h> #include <linux/irq.h> @@ -803,6 +805,7 @@ return 0; exit_class_create_failed: exit_i2c_failed: + misc_deregister(&akm->akmd_device); exit_akmd_device_register_failed: #if USING_IRQ free_irq(akm->irq, akm); diff --git a/drivers/input/misc/flip.c b/drivers/input/misc/flip.c new file mode 100644 index 0000000..769268d --- /dev/null +++ b/drivers/input/misc/flip.c @@ -0,0 +1,408 @@ + +/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/platform_device.h> +#include <linux/log2.h> +#include <linux/spinlock.h> +#include <linux/hrtimer.h> +#include <linux/sched.h> +#include <linux/pm.h> +#include <linux/slab.h> +#include <linux/pm_runtime.h> +#include <linux/gpio.h> +#include <linux/interrupt.h> +#include <linux/switch.h> +#include <linux/workqueue.h> +#include <linux/irqdesc.h> +#include <linux/fs.h> +#include <linux/sysctl.h> +#include <linux/proc_fs.h> +#include <linux/delay.h> +#include <linux/input.h> +#include <linux/workqueue.h> +#include <linux/irqdesc.h> +#include <linux/wakelock.h> + +#define REPORT_KEY 0 +#define FLIP_NOTINIT -1 +#define FLIP_OPEN 1 +#define FLIP_CLOSE 0 + +#define FLIP_SCAN_INTERVAL (50) /* ms */ +#define FLIP_STABLE_COUNT (1) +#define GPIO_FLIP GPIO_HALL_SW + +extern struct class *sec_class; + +#if 0 /* DEBUG */ +#define dbg_printk(fmt, ...) \ + printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__) +#else +#define dbg_printk(fmt, ...) \ + ({ if (0) printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__); 0; }) +#endif + +///////////////////////////////////////////////////////////////////// +struct sec_flip_pdata { + int wakeup; + int debounce_interval; + unsigned int rep:1; /* enable input subsystem auto repeat */ +}; + +struct sec_flip { + struct input_dev *input; + struct wake_lock wlock; + struct workqueue_struct *wq; /* The actuak work queue */ + struct work_struct flip_id_det; /* The work being queued */ + struct timer_list flip_timer; + struct device *sec_flip; + int timer_debounce; + int gpio; + int irq; +}; + + +///////////////////////////////////////////////////////////////////// +#ifdef CONFIG_S5P_DSIM_SWITCHABLE_DUAL_LCD +extern void s3cfb_switch_dual_lcd(int lcd_sel); +#endif +extern void samsung_switching_tsp(int flip); +extern void samsung_switching_tkey(int flip); +///////////////////////////////////////////////////////////////////// + +static int flip_status; +static int flip_status_before; + +/* 0 : open, 1: close */ +int Is_folder_state(void) +{ + printk("%s: flip_status = %d\n", __func__,flip_status); + return !flip_status; +} +EXPORT_SYMBOL(Is_folder_state); + + +static void sec_report_flip_key(struct sec_flip *flip) +{ +#if REPORT_KEY + if (flip_status) { + input_report_key(flip->input, KEY_FOLDER_OPEN, 1); + input_report_key(flip->input, KEY_FOLDER_OPEN, 0); + input_sync(flip->input); + dbg_printk("[FLIP] %s: input flip key : open\n", __FUNCTION__); + } else { + input_report_key(flip->input, KEY_FOLDER_CLOSE, 1); + input_report_key(flip->input, KEY_FOLDER_CLOSE, 0); + input_sync(flip->input); + dbg_printk ("[FLIP] %s: input flip key : close\n", __FUNCTION__); + } +#else + if (flip_status) { + input_report_switch(flip->input, SW_LID, 0); + input_sync(flip->input); + dbg_printk("[FLIP] %s: input flip key : open\n", __FUNCTION__); + } else { + input_report_switch(flip->input, SW_LID, 1); + input_sync(flip->input); + dbg_printk ("[FLIP] %s: input flip key : close\n", __FUNCTION__); + } +#endif +} + +static void set_flip_status(struct sec_flip *flip) +{ + int val = 0; + + val = gpio_get_value_cansleep(flip->gpio); + dbg_printk("%s, val:%d, flip_status:%d\n", __FUNCTION__, val, flip_status); + if (flip_status != val) { + flip_status_before = flip_status; + flip_status = val ? FLIP_OPEN : FLIP_CLOSE; + } + +} + +static void sec_flip_work_func(struct work_struct *work) +{ + struct sec_flip* flip = container_of( work, struct sec_flip, flip_id_det); + + //enable_irq(flip->irq); + + set_flip_status(flip); + printk("%s: %s, before:%d \n", __func__, (flip_status) ? "OPEN 1" : "CLOSE 0", flip_status_before); + + sec_report_flip_key(flip); + + if (flip_status != flip_status_before) { +#ifdef CONFIG_S5P_DSIM_SWITCHABLE_DUAL_LCD + s3cfb_switch_dual_lcd(!flip_status); +#endif + samsung_switching_tsp(flip_status); + samsung_switching_tkey(flip_status); + } +} + +static irqreturn_t sec_flip_irq_handler(int irq, void *_flip) +{ + struct sec_flip *flip = _flip; + + dbg_printk("%s\n", __FUNCTION__); + +/* + val = gpio_get_value_cansleep(flip->gpio); + + if(val){ // OPEN + flip_status = 1; + } else{ // CLOSE + flip_status = 0; + samsung_switching_tsp_pre(0, flip_status); + } + printk("[FLIP] %s: val=%d (1:open, 0:close)\n", __func__, val); + + wake_lock_timeout(&flip->wlock, 1 * HZ); +*/ + if (flip->timer_debounce) + mod_timer(&flip->flip_timer, + jiffies + msecs_to_jiffies(flip->timer_debounce)); + else + queue_work(flip->wq, &flip->flip_id_det ); + + return IRQ_HANDLED; +} + + +void sec_flip_timer(unsigned long data) +{ + struct sec_flip* flip = (struct sec_flip*)data; + + dbg_printk("%s\n", __FUNCTION__); + queue_work(flip->wq, &flip->flip_id_det); +} + +#if 1 /*CONFIG_PM*/ +static int sec_flip_suspend(struct device *dev) +{ + struct sec_flip *flip = dev_get_drvdata(dev); + + dbg_printk(KERN_INFO "[FLIP]%s:\n", __func__); + + if (device_may_wakeup(dev)) { + enable_irq_wake(flip->irq); + printk(KERN_INFO "[FLIP]%s: enable_irq_wake\n", __func__); + } + + return 0; +} + +static int sec_flip_resume(struct device *dev) +{ + struct sec_flip *flip = dev_get_drvdata(dev); + + dbg_printk("[FLIP]%s:\n", __func__); + + if (device_may_wakeup(dev)) { + disable_irq_wake(flip->irq); + printk(KERN_INFO "[FLIP]%s: disable_irq_wake\n", __func__); + } + + return 0; +} + +static struct dev_pm_ops pm8058_flip_pm_ops = { + .suspend = sec_flip_suspend, + .resume = sec_flip_resume, +}; +#endif + +static ssize_t status_check(struct device *dev, struct device_attribute *attr, char *buf) +{ + printk("flip status check : %d\n", flip_status); + return sprintf(buf, "%d\n", flip_status); +} + +static DEVICE_ATTR(flipStatus, S_IRUGO | S_IWUSR | S_IWGRP , status_check, NULL); + +static int __devinit sec_flip_probe(struct platform_device *pdev) +{ + struct input_dev *input; + int err; + struct sec_flip *flip; + struct sec_flip_pdata *pdata = pdev->dev.platform_data; + + dev_info(&pdev->dev, "probe\n"); + + if (!pdata) { + dev_err(&pdev->dev, "power key platform data not supplied\n"); + return -EINVAL; + } + + flip = kzalloc(sizeof(*flip), GFP_KERNEL); + if (!flip) + return -ENOMEM; + +/* INPUT DEVICE */ + input = input_allocate_device(); + if (!input) { + dev_err(&pdev->dev, "Can't allocate power button\n"); + err = -ENOMEM; + goto free_flip; + } + + if (pdata->rep) + __set_bit(EV_REP, input->evbit); + +#if REPORT_KEY + input_set_capability(input, EV_KEY, KEY_FOLDER_OPEN); + input_set_capability(input, EV_KEY, KEY_FOLDER_CLOSE); +#else + input_set_capability(input, EV_SW, SW_LID); +#endif + + input->name = "sec_flip"; + input->phys = "sec_flip/input0"; + input->dev.parent = &pdev->dev; + + err = input_register_device(input); + if (err) { + dev_err(&pdev->dev, "Can't register power key: %d\n", err); + goto free_input_dev; + } + flip->input = input; + + platform_set_drvdata(pdev, flip); + wake_lock_init(&flip->wlock, WAKE_LOCK_SUSPEND, "sec_flip"); + setup_timer(&flip->flip_timer, sec_flip_timer, (unsigned long)flip); + +/* Initialize the static variable*/ + flip_status = FLIP_NOTINIT; + flip_status_before = FLIP_NOTINIT; + +/* INTERRUPT */ + flip->gpio = GPIO_FLIP; + flip->irq = gpio_to_irq(GPIO_FLIP); + + if (pdata->debounce_interval) { + err = gpio_set_debounce(flip->gpio, + pdata->debounce_interval * 1000); + /* use timer if gpiolib doesn't provide debounce */ + if (err < 0) + flip->timer_debounce = pdata->debounce_interval; + + printk("%s: error:%d, timer_debounce=%d\n", __func__, err, flip->timer_debounce); + } + + + flip->wq = create_singlethread_workqueue("sec_flip_wq"); + if (!flip->wq) { + dev_err(&pdev->dev, "create_workqueue failed.\n"); + } + INIT_WORK(&flip->flip_id_det, sec_flip_work_func); + + device_init_wakeup(&pdev->dev, pdata->wakeup); + + + err = request_threaded_irq(flip->irq, NULL, sec_flip_irq_handler, (IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING), "flip_det_irq", flip); + if (err < 0) { + dev_err(&pdev->dev, "Can't get %d IRQ for flip: %d\n", + flip->irq, err); + goto unreg_input_dev; + } + /*enable_irq_wake(flip->irq);*/ + /*disable_irq_nosync(flip->irq);*/ + + printk("%s: gpio=%d irq=%d\n", __func__, flip->gpio, flip->irq); + +/* SYSFS for FACTORY TEST */ + flip->sec_flip = device_create(sec_class, NULL, 0, NULL, "sec_flip"); + err = device_create_file(flip->sec_flip , &dev_attr_flipStatus); + if(err<0){ + printk("flip status check cannot create file : %d\n", flip_status); + goto free_flip; + } + + mod_timer(&flip->flip_timer, jiffies + msecs_to_jiffies(5000)); + return 0; + + +unreg_input_dev: + input_unregister_device(input); + input = NULL; +free_input_dev: + input_free_device(input); +free_flip: + + kfree(flip); + + return err; +} + +static int __devexit sec_flip_remove(struct platform_device *pdev) +{ + struct sec_flip *flip = platform_get_drvdata(pdev); + + printk("%s:\n", __func__); + + if (flip!=NULL) + del_timer_sync(&flip->flip_timer); + + device_init_wakeup(&pdev->dev, 0); + + if (flip!=NULL) { + free_irq(flip->irq, NULL); + destroy_workqueue(flip->wq); + input_unregister_device(flip->input); + kfree(flip); + } + + return 0; +} + +static struct platform_driver sec_flip_driver = { + .probe = sec_flip_probe, + .remove = __devexit_p(sec_flip_remove), + .driver = { + .name = "sec_flip", + .owner = THIS_MODULE, +#if 1 /* CONFIG_PM */ + .pm = &pm8058_flip_pm_ops, +#endif + }, +}; + +static int __init sec_flip_init(void) +{ + return platform_driver_register(&sec_flip_driver); +} +module_init(sec_flip_init); + +static void __exit sec_flip_exit(void) +{ + platform_driver_unregister(&sec_flip_driver); +} +module_exit(sec_flip_exit); + +MODULE_ALIAS("platform:sec_flip"); +MODULE_DESCRIPTION("Flip Key with GPIO"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/misc/gp2a.c b/drivers/input/misc/gp2a.c index 05564cb..5da7ec0 100755..100644 --- a/drivers/input/misc/gp2a.c +++ b/drivers/input/misc/gp2a.c @@ -58,8 +58,6 @@ #define VENDOR_NAME "SHARP" #define CHIP_NAME "GP2A0002" -#define ADC_SAMPLE_NUM 5 - /* ADDSEL is LOW */ #define REGS_PROX 0x0 /* Read Only */ #define REGS_GAIN 0x1 /* Write Only */ @@ -121,34 +119,6 @@ static u8 reg_defaults[5] = { /* light sensor adc channel */ #define ALS_IOUT_ADC 9 -#if defined(CONFIG_MACH_P8) - -static const int adc_table[4] = { - 320, - 840, - 1400, - 1950, -}; - -#elif defined(CONFIG_MACH_P2) - -static const int adc_table[4] = { - 480, - 975, - 1535, - 2090, -}; - -#else - -static const int adc_table[4] = { - 430, - 925, - 1485, - 1950, -}; -#endif - struct gp2a_data; enum { @@ -182,7 +152,6 @@ struct gp2a_data { struct mutex adc_mutex; struct wake_lock prx_wake_lock; struct workqueue_struct *wq; - unsigned int adc_total; struct s3c_adc_client *padc; bool enable_wakeup; int prox_value; @@ -386,7 +355,6 @@ static int lightsensor_get_adcvalue(struct gp2a_data *gp2a) { int value = 0; int fake_value; - unsigned int adc_avr_value; /* get ADC */ /* value = gp2a->pdata->light_adc_value(); */ @@ -402,28 +370,16 @@ static int lightsensor_get_adcvalue(struct gp2a_data *gp2a) value = 0; } - gp2a->adc_total += value; - - adc_avr_value = gp2a->adc_total/ADC_SAMPLE_NUM; - - gp2a->adc_total -= adc_avr_value; - /* Cut off ADC Value */ -#if 1 - if (adc_avr_value < LIGHT_FAKE_LIMIT) { + if (value < LIGHT_FAKE_LIMIT) { fake_value = - (adc_avr_value < LIGHT_FAKE_THRESHOLD) ? - 0 : 2 * (adc_avr_value) - LIGHT_FAKE_LIMIT; - adc_avr_value = fake_value; + (value < LIGHT_FAKE_THRESHOLD) ? + 0 : 2 * (value) - LIGHT_FAKE_LIMIT; + value = fake_value; } -#else - if (adc_avr_value < 10) { - gp2a->adc_total = 0; - adc_avr_value = 0; - } -#endif - return adc_avr_value; + + return value; } static ssize_t lightsensor_file_state_show(struct device *dev, @@ -521,7 +477,6 @@ static struct device_attribute *proximity_sensor_attrs[] = { static void gp2a_work_func_light(struct work_struct *work) { - int i; struct gp2a_data *gp2a = container_of(work, struct gp2a_data, work_light); int adc = lightsensor_get_adcvalue(gp2a); @@ -705,12 +660,7 @@ static int gp2a_i2c_probe(struct i2c_client *client, pr_err("%s: missing pdata!\n", __func__); return ret; } - /* - if (!pdata->power || !pdata->light_adc_value) { - pr_err("%s: incomplete pdata!\n", __func__); - return ret; - } - */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { pr_err("%s: i2c functionality check failed!\n", __func__); return ret; @@ -892,8 +842,6 @@ static int gp2a_i2c_probe(struct i2c_client *client, input_report_abs(gp2a->proximity_input_dev, ABS_DISTANCE, 1); input_sync(gp2a->proximity_input_dev); - gp2a->adc_total = 0; - goto done; /* error, unwind it all */ diff --git a/drivers/input/misc/kr3dh.c b/drivers/input/misc/kr3dh.c new file mode 100644 index 0000000..0ef6543 --- /dev/null +++ b/drivers/input/misc/kr3dh.c @@ -0,0 +1,1352 @@ +/* + * kr3dh.c - ST Microelectronics three-axes accelerometer + * + * Copyright (C) 2010 Samsung Electronics + * Donggeun Kim <dg77.kim@samsung.com> + * + * 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. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/workqueue.h> +#include <linux/mutex.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/delay.h> +#include <linux/gpio.h> +#include <linux/input.h> +#include <linux/slab.h> +#include <linux/pm_runtime.h> +#include <linux/kr3dh.h> + +#define KR3DH_AUTO_INCREASE 0x80 +#define KR3DH_WHO_AM_I 0x0f +#define KR3DH_CTRL_REG1 0x20 +#define KR3DH_CTRL_REG2 0x21 +#define KR3DH_CTRL_REG3 0x22 +#define KR3DH_CTRL_REG4 0x23 +#define KR3DH_CTRL_REG5 0x24 +#define KR3DH_HP_FILTER_RESET 0x25 +#define KR3DH_REFERENCE 0x26 +#define KR3DH_STATUS_REG 0x27 +#define KR3DH_OUT_X_L 0x28 +#define KR3DH_OUT_X_H 0x29 +#define KR3DH_OUT_Y_L 0x2a +#define KR3DH_OUT_Y_H 0x2b +#define KR3DH_OUT_Z_L 0x2c +#define KR3DH_OUT_Z_H 0x2d +#define KR3DH_INT1_CFG_REG 0x30 +#define KR3DH_INT1_SRC_REG 0x31 +#define KR3DH_INT1_THS 0x32 +#define KR3DH_INT1_DURATION 0x33 +#define KR3DH_INT2_CFG_REG 0x34 +#define KR3DH_INT2_SRC_REG 0x35 +#define KR3DH_INT2_THS 0x36 +#define KR3DH_INT2_DURATION 0x37 +#define KR3DH_MULTI_OUT_X_L (KR3DH_AUTO_INCREASE | KR3DH_OUT_X_L) + +#define KR3DH_PM_SHIFT 5 +#define KR3DH_PM_MASK ((0x7) << KR3DH_PM_SHIFT) +#define KR3DH_DATA_RATE_SHIFT 3 +#define KR3DH_DATA_RATE_MASK ((0x3) << KR3DH_DATA_RATE_SHIFT) +#define KR3DH_ZEN_SHIFT 2 +#define KR3DH_ZEN_MASK ((0x1) << KR3DH_ZEN_SHIFT) +#define KR3DH_YEN_SHIFT 1 +#define KR3DH_YEN_MASK ((0x1) << KR3DH_YEN_SHIFT) +#define KR3DH_XEN_SHIFT 0 +#define KR3DH_XEN_MASK ((0x1) << KR3DH_XEN_SHIFT) + +#define KR3DH_BOOT_SHIFT 7 +#define KR3DH_BOOT_MASK ((0x1) << KR3DH_BOOT_SHIFT) +#define KR3DH_HPM_SHIFT 5 +#define KR3DH_HPM_MASK ((0x3) << KR3DH_HPM_SHIFT) +#define KR3DH_FDS_SHIFT 4 +#define KR3DH_FDS_MASK ((0x1) << KR3DH_FDS_SHIFT) +#define KR3DH_HPEN2_SHIFT 3 +#define KR3DH_HPEN2_MASK ((0x1) << KR3DH_HPEN2_SHIFT) +#define KR3DH_HPEN1_SHIFT 2 +#define KR3DH_HPEN1_MASK ((0x1) << KR3DH_HPEN1_SHIFT) +#define KR3DH_HPCF_SHIFT 0 +#define KR3DH_HPCF_MASK ((0x3) << KR3DH_HPCF_SHIFT) + +#define KR3DH_INT_ACTIVE_SHIFT 7 +#define KR3DH_INT_ACTIVE_MASK ((0x1) << KR3DH_INT_ACTIVE_SHIFT) +#define KR3DH_INT_PPOD_SHIFT 6 +#define KR3DH_INT_PPOD_MASK ((0x1) << KR3DH_INT_PPOD_SHIFT) +#define KR3DH_LIR2_SHIFT 5 +#define KR3DH_LIR2_MASK ((0x1) << KR3DH_LIR2_SHIFT) +#define KR3DH_INT2_CFG_SHIFT 3 +#define KR3DH_INT2_CFG_MASK ((0x3) << KR3DH_INT2_CFG_SHIFT) +#define KR3DH_LIR1_SHIFT 2 +#define KR3DH_LIR1_MASK ((0x1) << KR3DH_LIR1_SHIFT) +#define KR3DH_INT1_CFG_SHIFT 0 +#define KR3DH_INT1_CFG_MASK ((0x3) << KR3DH_INT1_CFG_SHIFT) + +#define KR3DH_BDU_SHIFT 7 +#define KR3DH_BDU_MASK ((0x1) << KR3DH_BDU_SHIFT) +#define KR3DH_BLE_SHIFT 6 +#define KR3DH_BLE_MASK ((0x1) << KR3DH_BLE_SHIFT) +#define KR3DH_FS_SHIFT 4 +#define KR3DH_FS_MASK ((0x3) << KR3DH_FS_SHIFT) +#define KR3DH_ST_SIGN_SHIFT 3 +#define KR3DH_ST_SIGN_MASK ((0x1) << KR3DH_ST_SIGN_SHIFT) +#define KR3DH_ST_SHIFT 1 +#define KR3DH_ST_MASK ((0x3) << KR3DH_ST_SHIFT) +#define KR3DH_SIM_SHIFT 0 +#define KR3DH_SIM_MASK ((0x1) << KR3DH_SIM_SHIFT) + +#define KR3DH_TURNON_SHIFT 0 +#define KR3DH_TURNON_MASK ((0x3) << KR3DH_BOOT_SHIFT) + +#define KR3DH_ZYXOR_SHIFT 7 +#define KR3DH_ZYXOR_MASK ((0x1) << KR3DH_ZYXOR_SHIFT) +#define KR3DH_ZOR_SHIFT 6 +#define KR3DH_ZOR_MASK ((0x1) << KR3DH_ZOR_SHIFT) +#define KR3DH_YOR_SHIFT 5 +#define KR3DH_YOR_MASK ((0x1) << KR3DH_YOR_SHIFT) +#define KR3DH_XOR_SHIFT 4 +#define KR3DH_XOR_MASK ((0x1) << KR3DH_XOR_SHIFT) +#define KR3DH_ZYXDA_SHIFT 3 +#define KR3DH_ZYXDA_MASK ((0x1) << KR3DH_ZYXDA_SHIFT) +#define KR3DH_ZDA_SHIFT 2 +#define KR3DH_ZDA_MASK ((0x1) << KR3DH_ZDA_SHIFT) +#define KR3DH_YDA_SHIFT 1 +#define KR3DH_YDA_MASK ((0x1) << KR3DH_YDA_SHIFT) +#define KR3DH_XDA_SHIFT 0 +#define KR3DH_XDA_MASK ((0x1) << KR3DH_XDA_SHIFT) + +#define KR3DH_AOR_SHIFT 7 +#define KR3DH_AOR_MASK ((0x1) << KR3DH_AOR_SHIFT) +#define KR3DH_6D_SHIFT 6 +#define KR3DH_6D_MASK ((0x1) << KR3DH_6D_SHIFT) +#define KR3DH_ZHIE_SHIFT 5 +#define KR3DH_ZHIE_MASK ((0x1) << KR3DH_ZHIE_SHIFT) +#define KR3DH_ZLIE_SHIFT 4 +#define KR3DH_ZLIE_MASK ((0x1) << KR3DH_ZLIE_SHIFT) +#define KR3DH_YHIE_SHIFT 3 +#define KR3DH_YHIE_MASK ((0x1) << KR3DH_YHIE_SHIFT) +#define KR3DH_YLIE_SHIFT 2 +#define KR3DH_YLIE_MASK ((0x1) << KR3DH_YLIE_SHIFT) +#define KR3DH_XHIE_SHIFT 1 +#define KR3DH_XHIE_MASK ((0x1) << KR3DH_XHIE_SHIFT) +#define KR3DH_XLIE_SHIFT 0 +#define KR3DH_XLIE_MASK ((0x1) << KR3DH_XLIE_SHIFT) + +#define KR3DH_IA_SHIFT 6 +#define KR3DH_IA_MASK ((0x1) << KR3DH_IA_SHIFT) +#define KR3DH_ZH_SHIFT 5 +#define KR3DH_ZH_MASK ((0x1) << KR3DH_ZH_SHIFT) +#define KR3DH_ZL_SHIFT 4 +#define KR3DH_ZL_MASK ((0x1) << KR3DH_ZL_SHIFT) +#define KR3DH_YH_SHIFT 3 +#define KR3DH_YH_MASK ((0x1) << KR3DH_YH_SHIFT) +#define KR3DH_YL_SHIFT 2 +#define KR3DH_YL_MASK ((0x1) << KR3DH_YL_SHIFT) +#define KR3DH_XH_SHIFT 1 +#define KR3DH_XH_MASK ((0x1) << KR3DH_XH_SHIFT) +#define KR3DH_XL_SHIFT 0 +#define KR3DH_XL_MASK ((0x1) << KR3DH_XL_SHIFT) + +#define KR3DH_INT_THS_MASK 0x7f +#define KR3DH_INT_DURATION_MASK 0x7f + +#define KR3DH_DEV_ID 0x32 +#define K3DH_DEV_ID 0x33 + +#define KR3DH_OUT_MIN_VALUE (-32768) +#define KR3DH_OUT_MAX_VALUE (32767) + +#define KR3DH_ON 1 +#define KR3DH_OFF 0 + +struct kr3dh_chip { + struct i2c_client *client; + struct device *dev; + struct input_dev *idev; + struct work_struct work1; + struct work_struct work2; + struct mutex lock; + struct kr3dh_platform_data *pdata; + + s16 x; + s16 y; + s16 z; + + int irq2; + u8 power_mode; + u8 data_rate; + u8 zen; + u8 yen; + u8 xen; + u8 reboot; + u8 hpmode; + u8 filter_sel; + u8 hp_enable_1; + u8 hp_enable_2; + u8 hpcf; + u8 int_hl_active; + u8 int_pp_od; + u8 int2_latch; + u8 int2_cfg; + u8 int1_latch; + u8 int1_cfg; + u8 block_data_update; + u8 endian; + u8 fullscale; + u8 selftest_sign; + u8 selftest; + u8 spi_mode; + u8 turn_on_mode; + u8 int1_combination; + u8 int1_6d_enable; + u8 int1_z_high_enable; + u8 int1_z_low_enable; + u8 int1_y_high_enable; + u8 int1_y_low_enable; + u8 int1_x_high_enable; + u8 int1_x_low_enable; + u8 int1_threshold; + u8 int1_duration; + u8 int2_combination; + u8 int2_6d_enable; + u8 int2_z_high_enable; + u8 int2_z_low_enable; + u8 int2_y_high_enable; + u8 int2_y_low_enable; + u8 int2_x_high_enable; + u8 int2_x_low_enable; + u8 int2_threshold; + u8 int2_duration; + u8 resume_power_mode; +}; + +static int kr3dh_write_reg(struct i2c_client *client, u8 reg, + u8 *value, u8 length) +{ + int ret; + + if (length == 1) + ret = i2c_smbus_write_byte_data(client, reg, *value); + else + ret = i2c_smbus_write_i2c_block_data(client, reg, + length, value); + if (ret < 0) + dev_err(&client->dev, "%s: reg 0x%x, err %d\n", + __func__, reg, ret); + + return ret; +} + +static int kr3dh_read_reg(struct i2c_client *client, u8 reg) +{ + int ret; + + ret = i2c_smbus_read_byte_data(client, reg); + if (ret < 0) + dev_err(&client->dev, "%s: reg 0x%x, err %d\n", + __func__, reg, ret); + + return ret; +} + +static void kr3dh_set_power_mode(struct kr3dh_chip *chip, u8 val) +{ + u8 value, temp; + + temp = (val << KR3DH_PM_SHIFT) & KR3DH_PM_MASK; + value = temp | chip->data_rate | chip->zen | + chip->yen | chip->xen; + + mutex_lock(&chip->lock); + chip->power_mode = temp; + kr3dh_write_reg(chip->client, KR3DH_CTRL_REG1, &value, 1); + mutex_unlock(&chip->lock); +} + +static u8 kr3dh_get_power_mode(struct kr3dh_chip *chip) +{ + return chip->power_mode >> KR3DH_PM_SHIFT; +} + +static void kr3dh_set_zen(struct kr3dh_chip *chip, u8 val) +{ + u8 value, temp; + + temp = (val << KR3DH_ZEN_SHIFT) & KR3DH_ZEN_MASK; + value = temp | chip->power_mode | chip->data_rate | + chip->yen | chip->xen; + mutex_lock(&chip->lock); + chip->zen = temp; + kr3dh_write_reg(chip->client, KR3DH_CTRL_REG1, &value, 1); + mutex_unlock(&chip->lock); +} + +static u8 kr3dh_get_zen(struct kr3dh_chip *chip) +{ + return chip->zen >> KR3DH_ZEN_SHIFT; +} + +static void kr3dh_set_yen(struct kr3dh_chip *chip, u8 val) +{ + u8 value, temp; + + temp = (val << KR3DH_YEN_SHIFT) & KR3DH_YEN_MASK; + value = temp | chip->power_mode | chip->data_rate | + chip->zen | chip->xen; + mutex_lock(&chip->lock); + chip->yen = temp; + kr3dh_write_reg(chip->client, KR3DH_CTRL_REG1, &value, 1); + mutex_unlock(&chip->lock); +} + +static u8 kr3dh_get_yen(struct kr3dh_chip *chip) +{ + return chip->yen >> KR3DH_YEN_SHIFT; +} + +static void kr3dh_set_xen(struct kr3dh_chip *chip, u8 val) +{ + u8 value, temp; + + temp = (val << KR3DH_XEN_SHIFT) & KR3DH_XEN_MASK; + value = temp | chip->power_mode | chip->data_rate | + chip->zen | chip->yen; + mutex_lock(&chip->lock); + chip->xen = temp; + kr3dh_write_reg(chip->client, KR3DH_CTRL_REG1, &value, 1); + mutex_unlock(&chip->lock); +} + +static u8 kr3dh_get_xen(struct kr3dh_chip *chip) +{ + return chip->xen >> KR3DH_XEN_SHIFT; +} + +static void kr3dh_set_reboot(struct kr3dh_chip *chip, u8 val) +{ + u8 value, temp; + + temp = (val << KR3DH_BOOT_SHIFT) & KR3DH_BOOT_MASK; + value = temp | chip->hpmode | chip->filter_sel | chip->hp_enable_2 | + chip->hp_enable_1 | chip->hpcf; + mutex_lock(&chip->lock); + chip->reboot = temp; + kr3dh_write_reg(chip->client, KR3DH_CTRL_REG2, &value, 1); + mutex_unlock(&chip->lock); +} + +static u8 kr3dh_get_reboot(struct kr3dh_chip *chip) +{ + return chip->reboot >> KR3DH_BOOT_SHIFT; +} + +static void kr3dh_set_int1_6d_enable(struct kr3dh_chip *chip, u8 val) +{ + u8 value, temp; + + temp = (val << KR3DH_6D_SHIFT) & KR3DH_6D_MASK; + value = temp | chip->int1_combination | chip->int1_z_high_enable | + chip->int1_z_low_enable | chip->int1_y_high_enable | + chip->int1_y_low_enable | chip->int1_x_high_enable | + chip->int1_x_low_enable; + mutex_lock(&chip->lock); + chip->int1_6d_enable = temp; + kr3dh_write_reg(chip->client, KR3DH_INT1_CFG_REG, &value, 1); + mutex_unlock(&chip->lock); +} + +static u8 kr3dh_get_int1_6d_enable(struct kr3dh_chip *chip) +{ + return chip->int1_z_high_enable >> KR3DH_6D_SHIFT; +} + +static void kr3dh_set_int1_z_high_enable(struct kr3dh_chip *chip, u8 val) +{ + u8 value, temp; + + temp = (val << KR3DH_ZHIE_SHIFT) & KR3DH_ZHIE_MASK; + value = temp | chip->int1_combination | chip->int1_6d_enable | + chip->int1_z_low_enable | chip->int1_y_high_enable | + chip->int1_y_low_enable | chip->int1_x_high_enable | + chip->int1_x_low_enable; + mutex_lock(&chip->lock); + chip->int1_z_high_enable = temp; + kr3dh_write_reg(chip->client, KR3DH_INT1_CFG_REG, &value, 1); + mutex_unlock(&chip->lock); +} + +static u8 kr3dh_get_int1_z_high_enable(struct kr3dh_chip *chip) +{ + return chip->int1_z_high_enable >> KR3DH_ZHIE_SHIFT; +} + +static void kr3dh_set_int1_z_low_enable(struct kr3dh_chip *chip, u8 val) +{ + u8 value, temp; + + temp = (val << KR3DH_ZLIE_SHIFT) & KR3DH_ZLIE_MASK; + value = temp | chip->int1_combination | chip->int1_6d_enable | + chip->int1_z_high_enable | chip->int1_y_high_enable | + chip->int1_y_low_enable | chip->int1_x_high_enable | + chip->int1_x_low_enable; + mutex_lock(&chip->lock); + chip->int1_z_low_enable = temp; + kr3dh_write_reg(chip->client, KR3DH_INT1_CFG_REG, &value, 1); + mutex_unlock(&chip->lock); +} + +static u8 kr3dh_get_int1_z_low_enable(struct kr3dh_chip *chip) +{ + return chip->int1_z_low_enable >> KR3DH_ZLIE_SHIFT; +} + +static void kr3dh_set_int1_y_high_enable(struct kr3dh_chip *chip, u8 val) +{ + u8 value, temp; + + temp = (val << KR3DH_YHIE_SHIFT) & KR3DH_YHIE_MASK; + value = temp | chip->int1_combination | chip->int1_6d_enable | + chip->int1_z_high_enable | chip->int1_z_low_enable | + chip->int1_y_low_enable | chip->int1_x_high_enable | + chip->int1_x_low_enable; + mutex_lock(&chip->lock); + chip->int1_y_high_enable = temp; + kr3dh_write_reg(chip->client, KR3DH_INT1_CFG_REG, &value, 1); + mutex_unlock(&chip->lock); +} + +static u8 kr3dh_get_int1_y_high_enable(struct kr3dh_chip *chip) +{ + return chip->int1_y_high_enable >> KR3DH_YHIE_SHIFT; +} + +static void kr3dh_set_int1_y_low_enable(struct kr3dh_chip *chip, u8 val) +{ + u8 value, temp; + + temp = (val << KR3DH_YLIE_SHIFT) & KR3DH_YLIE_MASK; + value = temp | chip->int1_combination | chip->int1_6d_enable | + chip->int1_z_high_enable | chip->int1_z_low_enable | + chip->int1_y_high_enable | chip->int1_x_high_enable | + chip->int1_x_low_enable; + mutex_lock(&chip->lock); + chip->int1_y_low_enable = temp; + kr3dh_write_reg(chip->client, KR3DH_INT1_CFG_REG, &value, 1); + mutex_unlock(&chip->lock); +} + +static u8 kr3dh_get_int1_y_low_enable(struct kr3dh_chip *chip) +{ + return chip->int1_y_low_enable >> KR3DH_YLIE_SHIFT; +} + +static void kr3dh_set_int1_x_high_enable(struct kr3dh_chip *chip, u8 val) +{ + u8 value, temp; + + temp = (val << KR3DH_XHIE_SHIFT) & KR3DH_XHIE_MASK; + value = temp | chip->int1_combination | chip->int1_6d_enable | + chip->int1_z_high_enable | chip->int1_z_low_enable | + chip->int1_y_high_enable | chip->int1_y_low_enable | + chip->int1_x_low_enable; + mutex_lock(&chip->lock); + chip->int1_x_high_enable = temp; + kr3dh_write_reg(chip->client, KR3DH_INT1_CFG_REG, &value, 1); + mutex_unlock(&chip->lock); +} + +static u8 kr3dh_get_int1_x_high_enable(struct kr3dh_chip *chip) +{ + return chip->int1_x_high_enable >> KR3DH_XHIE_SHIFT; +} + +static void kr3dh_set_int1_x_low_enable(struct kr3dh_chip *chip, u8 val) +{ + u8 value, temp; + + temp = (val << KR3DH_XLIE_SHIFT) & KR3DH_XLIE_MASK; + value = temp | chip->int1_combination | chip->int1_6d_enable | + chip->int1_z_high_enable | chip->int1_z_low_enable | + chip->int1_y_high_enable | chip->int1_y_low_enable | + chip->int1_x_high_enable; + mutex_lock(&chip->lock); + chip->int1_x_low_enable = temp; + kr3dh_write_reg(chip->client, KR3DH_INT1_CFG_REG, &value, 1); + mutex_unlock(&chip->lock); +} + +static u8 kr3dh_get_int1_x_low_enable(struct kr3dh_chip *chip) +{ + return chip->int1_x_low_enable >> KR3DH_XLIE_SHIFT; +} + +static void kr3dh_set_int1_threshold(struct kr3dh_chip *chip, u8 val) +{ + u8 value = val; + + mutex_lock(&chip->lock); + chip->int1_threshold = val & KR3DH_INT_THS_MASK; + kr3dh_write_reg(chip->client, KR3DH_INT1_THS, &value, 1); + mutex_unlock(&chip->lock); +} + +static u8 kr3dh_get_int1_threshold(struct kr3dh_chip *chip) +{ + return chip->int1_threshold; +} + +static void kr3dh_set_int1_duration(struct kr3dh_chip *chip, u8 val) +{ + u8 value = val; + + mutex_lock(&chip->lock); + chip->int1_duration = val & KR3DH_INT_DURATION_MASK; + kr3dh_write_reg(chip->client, KR3DH_INT1_DURATION, &value, 1); + mutex_unlock(&chip->lock); +} + +static u8 kr3dh_get_int1_duration(struct kr3dh_chip *chip) +{ + return chip->int1_duration; +} + +static void kr3dh_set_int2_6d_enable(struct kr3dh_chip *chip, u8 val) +{ + u8 value, temp; + + temp = (val << KR3DH_6D_SHIFT) & KR3DH_6D_MASK; + value = temp | chip->int2_combination | chip->int2_z_high_enable | + chip->int2_z_low_enable | chip->int2_y_high_enable | + chip->int2_y_low_enable | chip->int2_x_high_enable | + chip->int2_x_low_enable; + mutex_lock(&chip->lock); + chip->int2_6d_enable = temp; + kr3dh_write_reg(chip->client, KR3DH_INT2_CFG_REG, &value, 1); + mutex_unlock(&chip->lock); +} + +static u8 kr3dh_get_int2_6d_enable(struct kr3dh_chip *chip) +{ + return chip->int2_6d_enable >> KR3DH_6D_SHIFT; +} + +static void kr3dh_set_int2_z_high_enable(struct kr3dh_chip *chip, u8 val) +{ + u8 value, temp; + + temp = (val << KR3DH_ZHIE_SHIFT) & KR3DH_ZHIE_MASK; + value = temp | chip->int2_combination | chip->int2_6d_enable | + chip->int2_z_low_enable | chip->int2_y_high_enable | + chip->int2_y_low_enable | chip->int2_x_high_enable | + chip->int2_x_low_enable; + mutex_lock(&chip->lock); + chip->int2_z_high_enable = temp; + kr3dh_write_reg(chip->client, KR3DH_INT2_CFG_REG, &value, 1); + mutex_unlock(&chip->lock); +} + +static u8 kr3dh_get_int2_z_high_enable(struct kr3dh_chip *chip) +{ + return chip->int2_z_high_enable >> KR3DH_ZHIE_SHIFT; +} + +static void kr3dh_set_int2_z_low_enable(struct kr3dh_chip *chip, u8 val) +{ + u8 value, temp; + + temp = (val << KR3DH_ZLIE_SHIFT) & KR3DH_ZLIE_MASK; + value = temp | chip->int2_combination | chip->int2_6d_enable | + chip->int2_z_high_enable | chip->int2_y_high_enable | + chip->int2_y_low_enable | chip->int2_x_high_enable | + chip->int2_x_low_enable; + mutex_lock(&chip->lock); + chip->int2_z_low_enable = temp; + kr3dh_write_reg(chip->client, KR3DH_INT2_CFG_REG, &value, 1); + mutex_unlock(&chip->lock); +} + +static u8 kr3dh_get_int2_z_low_enable(struct kr3dh_chip *chip) +{ + return chip->int2_z_low_enable >> KR3DH_ZLIE_SHIFT; +} + +static void kr3dh_set_int2_y_high_enable(struct kr3dh_chip *chip, u8 val) +{ + u8 value, temp; + + temp = (val << KR3DH_YHIE_SHIFT) & KR3DH_YHIE_MASK; + value = temp | chip->int2_combination | chip->int2_6d_enable | + chip->int2_z_high_enable | chip->int2_z_low_enable | + chip->int2_y_low_enable | chip->int2_x_high_enable | + chip->int2_x_low_enable; + mutex_lock(&chip->lock); + chip->int2_y_high_enable = temp; + kr3dh_write_reg(chip->client, KR3DH_INT2_CFG_REG, &value, 1); + mutex_unlock(&chip->lock); +} + +static u8 kr3dh_get_int2_y_high_enable(struct kr3dh_chip *chip) +{ + return chip->int2_y_high_enable >> KR3DH_YHIE_SHIFT; +} + +static void kr3dh_set_int2_y_low_enable(struct kr3dh_chip *chip, u8 val) +{ + u8 value, temp; + + temp = (val << KR3DH_YLIE_SHIFT) & KR3DH_YLIE_MASK; + value = temp | chip->int2_combination | chip->int2_6d_enable | + chip->int2_z_high_enable | chip->int2_z_low_enable | + chip->int2_y_high_enable | chip->int2_x_high_enable | + chip->int2_x_low_enable; + mutex_lock(&chip->lock); + chip->int2_y_low_enable = temp; + kr3dh_write_reg(chip->client, KR3DH_INT2_CFG_REG, &value, 1); + mutex_unlock(&chip->lock); +} + +static u8 kr3dh_get_int2_y_low_enable(struct kr3dh_chip *chip) +{ + return chip->int2_y_low_enable >> KR3DH_YLIE_SHIFT; +} + +static void kr3dh_set_int2_x_high_enable(struct kr3dh_chip *chip, u8 val) +{ + u8 value, temp; + + temp = (val << KR3DH_XHIE_SHIFT) & KR3DH_XHIE_MASK; + value = temp | chip->int2_combination | chip->int2_6d_enable | + chip->int2_z_high_enable | chip->int2_z_low_enable | + chip->int2_y_high_enable | chip->int2_y_low_enable | + chip->int2_x_low_enable; + mutex_lock(&chip->lock); + chip->int2_x_high_enable = temp; + kr3dh_write_reg(chip->client, KR3DH_INT2_CFG_REG, &value, 1); + mutex_unlock(&chip->lock); +} + +static u8 kr3dh_get_int2_x_high_enable(struct kr3dh_chip *chip) +{ + return chip->int2_x_high_enable >> KR3DH_XHIE_SHIFT; +} + +static void kr3dh_set_int2_x_low_enable(struct kr3dh_chip *chip, u8 val) +{ + u8 value, temp; + + temp = (val << KR3DH_XLIE_SHIFT) & KR3DH_XLIE_MASK; + value = temp | chip->int2_combination | chip->int2_6d_enable | + chip->int2_z_high_enable | chip->int2_z_low_enable | + chip->int2_y_high_enable | chip->int2_y_low_enable | + chip->int2_x_high_enable; + mutex_lock(&chip->lock); + chip->int2_x_low_enable = temp; + kr3dh_write_reg(chip->client, KR3DH_INT2_CFG_REG, &value, 1); + mutex_unlock(&chip->lock); +} + +static u8 kr3dh_get_int2_x_low_enable(struct kr3dh_chip *chip) +{ + return chip->int2_x_low_enable >> KR3DH_XLIE_SHIFT; +} + +static void kr3dh_set_int2_threshold(struct kr3dh_chip *chip, u8 val) +{ + u8 value = val; + + mutex_lock(&chip->lock); + chip->int2_threshold = val & KR3DH_INT_THS_MASK; + kr3dh_write_reg(chip->client, KR3DH_INT2_THS, &value, 1); + mutex_unlock(&chip->lock); +} + +static u8 kr3dh_get_int2_threshold(struct kr3dh_chip *chip) +{ + return chip->int2_threshold; +} + +static void kr3dh_set_int2_duration(struct kr3dh_chip *chip, u8 val) +{ + u8 value = val; + + mutex_lock(&chip->lock); + chip->int2_duration = val & KR3DH_INT_DURATION_MASK; + kr3dh_write_reg(chip->client, KR3DH_INT2_DURATION, &value, 1); + mutex_unlock(&chip->lock); +} + +static u8 kr3dh_get_int2_duration(struct kr3dh_chip *chip) +{ + return chip->int2_duration; +} + +static void kr3dh_get_xyz(struct kr3dh_chip *chip) +{ + u8 xyz_values[6]; + s16 temp; + int ret; + + mutex_lock(&chip->lock); + + ret = i2c_smbus_read_i2c_block_data(chip->client, + KR3DH_MULTI_OUT_X_L, 6, xyz_values); + if (ret < 0) + goto out; + + temp = ((xyz_values[1] << BITS_PER_BYTE) | xyz_values[0]); + chip->x = chip->pdata->negate_x ? -(temp >> 4) : temp >> 4; + temp = ((xyz_values[3] << BITS_PER_BYTE) | xyz_values[2]); + chip->y = chip->pdata->negate_y ? -(temp >> 4) : temp >> 4; + temp = ((xyz_values[5] << BITS_PER_BYTE) | xyz_values[4]); + chip->z = chip->pdata->negate_z ? -(temp >> 4) : temp >> 4; + + if (chip->pdata->change_xy) { + temp = chip->x; + chip->x = chip->y; + chip->y = temp; + } +out: + mutex_unlock(&chip->lock); +} + +static void kr3dh_work1(struct work_struct *work) +{ + struct kr3dh_chip *chip = container_of(work, + struct kr3dh_chip, work1); + u8 int1_src = 0; + + int1_src = kr3dh_read_reg(chip->client, KR3DH_INT1_SRC_REG); + if (int1_src || + ((chip->int1_cfg & KR3DH_INT1_CFG_MASK) == KR3DH_DATA_READY)) { + kr3dh_get_xyz(chip); + mutex_lock(&chip->lock); + input_report_abs(chip->idev, ABS_MISC, 1); + input_report_abs(chip->idev, ABS_MISC, int1_src); + input_report_abs(chip->idev, ABS_X, chip->x); + input_report_abs(chip->idev, ABS_Y, chip->y); + input_report_abs(chip->idev, ABS_Z, chip->z); + input_sync(chip->idev); + mutex_unlock(&chip->lock); + } + + enable_irq(chip->client->irq); +} + +static void kr3dh_work2(struct work_struct *work) +{ + struct kr3dh_chip *chip = container_of(work, + struct kr3dh_chip, work2); + u8 int2_src = 0; + + int2_src = kr3dh_read_reg(chip->client, KR3DH_INT2_SRC_REG); + if (int2_src || + ((chip->int2_cfg & KR3DH_INT2_CFG_MASK) == KR3DH_DATA_READY)) { + kr3dh_get_xyz(chip); + mutex_lock(&chip->lock); + input_report_abs(chip->idev, ABS_MISC, 2); + input_report_abs(chip->idev, ABS_MISC, int2_src); + input_report_abs(chip->idev, ABS_X, chip->x); + input_report_abs(chip->idev, ABS_Y, chip->y); + input_report_abs(chip->idev, ABS_Z, chip->z); + input_sync(chip->idev); + mutex_unlock(&chip->lock); + } + + enable_irq(chip->irq2); +} + +static irqreturn_t kr3dh_irq1(int irq, void *data) +{ + struct kr3dh_chip *chip = data; + + if (!work_pending(&chip->work1)) { + disable_irq_nosync(irq); + schedule_work(&chip->work1); + } else { + dev_err(&chip->client->dev, "work pending\n"); + } + + return IRQ_HANDLED; +} + +static irqreturn_t kr3dh_irq2(int irq, void *data) +{ + struct kr3dh_chip *chip = data; + + if (!work_pending(&chip->work2)) { + disable_irq_nosync(irq); + schedule_work(&chip->work2); + } else { + dev_err(&chip->client->dev, "work pending\n"); + } + + return IRQ_HANDLED; +} + +static ssize_t kr3dh_show_xyz(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct kr3dh_chip *chip = dev_get_drvdata(dev); + kr3dh_get_xyz(chip); + return sprintf(buf, "%d %d %d\n", chip->x, chip->y, chip->z); +} +static DEVICE_ATTR(xyz, S_IRUGO, kr3dh_show_xyz, NULL); + +static ssize_t kr3dh_show_reg_state(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct kr3dh_chip *chip = dev_get_drvdata(dev); + u8 ctrl_reg[5], int1_reg[4]; + int ret; + + ret = i2c_smbus_read_i2c_block_data(chip->client, + (KR3DH_CTRL_REG1 | KR3DH_AUTO_INCREASE), 5, ctrl_reg); + + if (ret < 0) { + dev_err(&chip->client->dev, "%s: reg 0x%x, err %d\n", + __func__, KR3DH_CTRL_REG1, ret); + return ret; + } + + ret = i2c_smbus_read_i2c_block_data(chip->client, + (KR3DH_INT1_CFG_REG | KR3DH_AUTO_INCREASE), 4, int1_reg); + + if (ret < 0) { + dev_err(&chip->client->dev, "%s: reg 0x%x, err %d\n", + __func__, KR3DH_INT1_CFG_REG, ret); + return ret; + } + + ret = i2c_smbus_read_byte_data(chip->client, KR3DH_STATUS_REG); + if (ret < 0) { + dev_err(&chip->client->dev, "%s: reg 0x%x, err %d\n", + __func__, KR3DH_STATUS_REG, ret); + return ret; + } + + return sprintf(buf, "ctrl1:0x%x ctrl2:0x%x ctrl3:0x%x\n" + "ctrl4:0x%x ctrl5:0x%x\n" + "int1_cfg:0x%x int1_src:0x%x int1_ths:0x%x\n" + "int1_dur:0x%x status_reg:0x%x\n", + ctrl_reg[0], ctrl_reg[1], ctrl_reg[2], + ctrl_reg[3], ctrl_reg[4], + int1_reg[0], int1_reg[1], int1_reg[2], + int1_reg[3], ret); +} +static DEVICE_ATTR(reg_state, S_IRUGO, kr3dh_show_reg_state, NULL); + +#define KR3DH_INPUT(name) \ +static ssize_t kr3dh_store_##name(struct device *dev, \ + struct device_attribute *attr, const char *buf, size_t count) \ +{ \ + struct kr3dh_chip *chip = dev_get_drvdata(dev); \ + unsigned long val; \ + int ret; \ + \ + if (!count) \ + return -EINVAL; \ + ret = strict_strtoul(buf, 10, &val); \ + if (ret) { \ + dev_err(dev, "fail: conversion %s 5o number\n", buf); \ + return count; \ + } \ + kr3dh_set_##name(chip, (u8) val); \ + return count; \ +} \ +static ssize_t kr3dh_show_##name(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + struct kr3dh_chip *chip = dev_get_drvdata(dev); \ + u16 ret = kr3dh_get_##name(chip); \ + return sprintf(buf, "%d\n", ret); \ +} \ +static DEVICE_ATTR(name, S_IRUGO | S_IWUSR, \ + kr3dh_show_##name, kr3dh_store_##name); + +KR3DH_INPUT(power_mode); +KR3DH_INPUT(zen); +KR3DH_INPUT(yen); +KR3DH_INPUT(xen); +KR3DH_INPUT(reboot); +KR3DH_INPUT(int1_6d_enable); +KR3DH_INPUT(int1_z_high_enable); +KR3DH_INPUT(int1_z_low_enable); +KR3DH_INPUT(int1_y_high_enable); +KR3DH_INPUT(int1_y_low_enable); +KR3DH_INPUT(int1_x_high_enable); +KR3DH_INPUT(int1_x_low_enable); +KR3DH_INPUT(int1_threshold); +KR3DH_INPUT(int1_duration); +KR3DH_INPUT(int2_6d_enable); +KR3DH_INPUT(int2_z_high_enable); +KR3DH_INPUT(int2_z_low_enable); +KR3DH_INPUT(int2_y_high_enable); +KR3DH_INPUT(int2_y_low_enable); +KR3DH_INPUT(int2_x_high_enable); +KR3DH_INPUT(int2_x_low_enable); +KR3DH_INPUT(int2_threshold); +KR3DH_INPUT(int2_duration); + +static struct attribute *kr3dh_attributes[] = { + &dev_attr_power_mode.attr, + &dev_attr_zen.attr, + &dev_attr_yen.attr, + &dev_attr_xen.attr, + &dev_attr_reboot.attr, + &dev_attr_int1_6d_enable.attr, + &dev_attr_int1_z_high_enable.attr, + &dev_attr_int1_z_low_enable.attr, + &dev_attr_int1_y_high_enable.attr, + &dev_attr_int1_y_low_enable.attr, + &dev_attr_int1_x_high_enable.attr, + &dev_attr_int1_x_low_enable.attr, + &dev_attr_int1_threshold.attr, + &dev_attr_int1_duration.attr, + &dev_attr_int2_6d_enable.attr, + &dev_attr_int2_z_high_enable.attr, + &dev_attr_int2_z_low_enable.attr, + &dev_attr_int2_y_high_enable.attr, + &dev_attr_int2_y_low_enable.attr, + &dev_attr_int2_x_high_enable.attr, + &dev_attr_int2_x_low_enable.attr, + &dev_attr_int2_threshold.attr, + &dev_attr_int2_duration.attr, + &dev_attr_xyz.attr, + &dev_attr_reg_state.attr, + NULL +}; + +static const struct attribute_group kr3dh_group = { + .attrs = kr3dh_attributes, +}; + +static void kr3dh_unregister_input_device(struct kr3dh_chip *chip) +{ + struct i2c_client *client = chip->client; + + if (client->irq > 0) + free_irq(client->irq, chip); + if (chip->irq2 > 0) + free_irq(chip->irq2, chip); + + input_unregister_device(chip->idev); + chip->idev = NULL; +} + +static int kr3dh_register_input_device(struct kr3dh_chip *chip) +{ + struct i2c_client *client = chip->client; + struct input_dev *idev; + int ret; + + idev = chip->idev = input_allocate_device(); + if (!idev) { + dev_err(&client->dev, "allocating input device failed\n"); + ret = -ENOMEM; + goto error_alloc; + } + + idev->name = "KR3DH accelerometer"; + idev->id.bustype = BUS_I2C; + idev->dev.parent = &client->dev; + idev->evbit[0] = BIT_MASK(EV_ABS); + + input_set_abs_params(idev, ABS_MISC, 0, 255, 0, 0); + input_set_abs_params(idev, ABS_X, KR3DH_OUT_MIN_VALUE, + KR3DH_OUT_MAX_VALUE, 0, 0); + input_set_abs_params(idev, ABS_Y, KR3DH_OUT_MIN_VALUE, + KR3DH_OUT_MAX_VALUE, 0, 0); + input_set_abs_params(idev, ABS_Z, KR3DH_OUT_MIN_VALUE, + KR3DH_OUT_MAX_VALUE, 0, 0); + + input_set_drvdata(idev, chip); + + ret = input_register_device(idev); + if (ret) { + dev_err(&client->dev, "registering input device failed\n"); + goto error_reg; + } + + if (client->irq > 0) { + unsigned long irq_flag = IRQF_DISABLED; + + irq_flag |= IRQF_TRIGGER_RISING; + ret = request_irq(client->irq, kr3dh_irq1, irq_flag, + "KR3DH accelerometer int1", chip); + if (ret) { + dev_err(&client->dev, "can't get IRQ %d, ret %d\n", + client->irq, ret); + goto error_irq1; + } + } + + if (chip->irq2 > 0) { + unsigned long irq_flag = IRQF_DISABLED; + + irq_flag |= IRQF_TRIGGER_RISING; + ret = request_irq(chip->irq2, kr3dh_irq2, irq_flag, + "KR3DH accelerometer int2", chip); + if (ret) { + dev_err(&client->dev, "can't get IRQ %d, ret %d\n", + chip->irq2, ret); + goto error_irq2; + } + } + + return 0; + +error_irq2: + if (client->irq > 0) + free_irq(client->irq, chip); +error_irq1: + input_unregister_device(idev); +error_reg: + input_free_device(idev); +error_alloc: + return ret; +} + +static void kr3dh_initialize_chip(struct kr3dh_chip *chip) +{ + u8 value; + + /* CTRL_REG1 */ + value = chip->power_mode | chip->data_rate | chip->zen | chip->yen | + chip->xen; + kr3dh_write_reg(chip->client, KR3DH_CTRL_REG1, &value, 1); + + /* CTRL_REG2 */ + value = chip->reboot | chip->hpmode | chip->filter_sel | + chip->hp_enable_2 | chip->hp_enable_1 | chip->hpcf; + kr3dh_write_reg(chip->client, KR3DH_CTRL_REG2, &value, 1); + + /* CTRL_REG3 */ + value = chip->int_hl_active | chip->int_pp_od | chip->int2_latch | + chip->int2_cfg | chip->int1_latch | chip->int1_cfg; + kr3dh_write_reg(chip->client, KR3DH_CTRL_REG3, &value, 1); + + /* CTRL_REG4 */ + value = chip->block_data_update | chip->endian | chip->fullscale | + chip->selftest_sign | chip->selftest | chip->spi_mode; + kr3dh_write_reg(chip->client, KR3DH_CTRL_REG4, &value, 1); + + /* INT1_THS_REG */ + kr3dh_write_reg(chip->client, KR3DH_INT1_THS, + &chip->int1_threshold, 1); + + /* INT1_DURATION_REG */ + kr3dh_write_reg(chip->client, KR3DH_INT1_DURATION, + &chip->int1_duration, 1); + + /* INT2_THS_REG */ + kr3dh_write_reg(chip->client, KR3DH_INT2_THS, + &chip->int2_threshold, 1); + + /* INT2_DURATION_REG */ + kr3dh_write_reg(chip->client, KR3DH_INT2_DURATION, + &chip->int2_duration, 1); + + /* INT1_CFG_REG */ + value = chip->int1_combination | chip->int1_6d_enable | + chip->int1_z_high_enable | chip->int1_z_low_enable | + chip->int1_y_high_enable | chip->int1_y_low_enable | + chip->int1_x_high_enable | chip->int1_x_low_enable; + kr3dh_write_reg(chip->client, KR3DH_INT1_CFG_REG, &value, 1); + + /* INT2_CFG_REG */ + value = chip->int2_combination | chip->int2_6d_enable | + chip->int2_z_high_enable | chip->int2_z_low_enable | + chip->int2_y_high_enable | chip->int2_y_low_enable | + chip->int2_x_high_enable | chip->int2_x_low_enable; + kr3dh_write_reg(chip->client, KR3DH_INT2_CFG_REG, &value, 1); + + /* CTRL_REG5 */ + kr3dh_write_reg(chip->client, KR3DH_CTRL_REG5, &chip->turn_on_mode, 1); + +} + +static void kr3dh_format_chip_data(struct kr3dh_chip *chip, + struct kr3dh_platform_data *pdata) +{ + chip->irq2 = pdata->irq2; + + /* CTRL_REG1 */ + chip->power_mode = (pdata->power_mode << KR3DH_PM_SHIFT) & + KR3DH_PM_MASK; + chip->resume_power_mode = chip->power_mode; + chip->data_rate = (pdata->data_rate << KR3DH_DATA_RATE_SHIFT) & + KR3DH_DATA_RATE_MASK; + chip->zen = (pdata->zen << KR3DH_ZEN_SHIFT) & KR3DH_ZEN_MASK; + chip->yen = (pdata->yen << KR3DH_YEN_SHIFT) & KR3DH_YEN_MASK; + chip->xen = (pdata->xen << KR3DH_XEN_SHIFT) & KR3DH_XEN_MASK; + + /* CTRL_REG2 */ + chip->reboot = (pdata->reboot << KR3DH_BOOT_SHIFT) & KR3DH_BOOT_MASK; + chip->hpmode = (pdata->hpmode << KR3DH_HPM_SHIFT) & KR3DH_HPM_MASK; + chip->filter_sel = (pdata->filter_sel << KR3DH_FDS_SHIFT) & + KR3DH_FDS_MASK; + chip->hp_enable_2 = (pdata->hp_enable_2 << KR3DH_HPEN2_SHIFT) & + KR3DH_HPEN2_MASK; + chip->hp_enable_1 = (pdata->hp_enable_1 << KR3DH_HPEN1_SHIFT) & + KR3DH_HPEN1_MASK; + chip->hpcf = (pdata->hpcf << KR3DH_HPCF_SHIFT) & KR3DH_HPCF_MASK; + + /* CTRL_REG3 */ + chip->int_hl_active = (pdata->int_hl_active << KR3DH_INT_ACTIVE_SHIFT) & + KR3DH_INT_ACTIVE_MASK; + chip->int_pp_od = (pdata->int_pp_od << KR3DH_INT_PPOD_SHIFT) & + KR3DH_INT_PPOD_MASK; + chip->int2_latch = (pdata->int2_latch << KR3DH_LIR2_SHIFT) & + KR3DH_LIR2_MASK; + chip->int2_cfg = (pdata->int2_cfg << KR3DH_INT2_CFG_SHIFT) & + KR3DH_INT2_CFG_MASK; + chip->int1_latch = (pdata->int1_latch << KR3DH_LIR1_SHIFT) & + KR3DH_LIR1_MASK; + chip->int1_cfg = (pdata->int1_cfg << KR3DH_INT1_CFG_SHIFT) & + KR3DH_INT1_CFG_MASK; + + /* CTRL_REG4 */ + chip->block_data_update = + ((pdata->block_data_update << KR3DH_BDU_SHIFT) + & KR3DH_BDU_MASK); + chip->endian = (pdata->endian << KR3DH_BLE_SHIFT) & KR3DH_BLE_MASK; + chip->fullscale = (pdata->fullscale << KR3DH_FS_SHIFT) & KR3DH_FS_MASK; + chip->selftest_sign = (pdata->selftest_sign << KR3DH_ST_SIGN_SHIFT) & + KR3DH_ST_SIGN_MASK; + chip->selftest = (pdata->selftest << KR3DH_ST_SHIFT) & KR3DH_ST_MASK; + chip->spi_mode = (pdata->spi_mode << KR3DH_SIM_SHIFT) & KR3DH_SIM_MASK; + + /* CTRL_REG5 */ + chip->turn_on_mode = pdata->turn_on_mode & KR3DH_TURNON_MASK; + + /* INT1_CFG_REG */ + chip->int1_combination = (pdata->int1_combination << KR3DH_AOR_SHIFT) & + KR3DH_AOR_MASK; + chip->int1_6d_enable = (pdata->int1_6d_enable << KR3DH_6D_SHIFT) & + KR3DH_6D_MASK; + chip->int1_z_high_enable = (pdata->int1_z_high_enable << + KR3DH_ZHIE_SHIFT) & KR3DH_ZHIE_MASK; + chip->int1_z_low_enable = (pdata->int1_z_low_enable << + KR3DH_ZLIE_SHIFT) & KR3DH_ZLIE_MASK; + chip->int1_y_high_enable = (pdata->int1_y_high_enable << + KR3DH_YHIE_SHIFT) & KR3DH_YHIE_MASK; + chip->int1_y_low_enable = (pdata->int1_y_low_enable << + KR3DH_YLIE_SHIFT) & KR3DH_YLIE_MASK; + chip->int1_x_high_enable = (pdata->int1_x_high_enable << + KR3DH_XHIE_SHIFT) & KR3DH_XHIE_MASK; + chip->int1_x_low_enable = (pdata->int1_x_low_enable << + KR3DH_XLIE_SHIFT) & KR3DH_XLIE_MASK; + + /* INT1_THS_REG */ + chip->int1_threshold = pdata->int1_threshold & KR3DH_INT_THS_MASK; + + /* INT1_DURATION_REG */ + chip->int1_duration = pdata->int1_duration & KR3DH_INT_DURATION_MASK; + + /* INT2_CFG_REG */ + chip->int2_combination = (pdata->int2_combination << KR3DH_AOR_SHIFT) & + KR3DH_AOR_MASK; + chip->int2_6d_enable = (pdata->int2_6d_enable << KR3DH_6D_SHIFT) & + KR3DH_6D_MASK; + chip->int2_z_high_enable = (pdata->int2_z_high_enable << + KR3DH_ZHIE_SHIFT) & KR3DH_ZHIE_MASK; + chip->int2_z_low_enable = (pdata->int2_z_low_enable << + KR3DH_ZLIE_SHIFT) & KR3DH_ZLIE_MASK; + chip->int2_y_high_enable = (pdata->int2_y_high_enable << + KR3DH_YHIE_SHIFT) & KR3DH_YHIE_MASK; + chip->int2_y_low_enable = (pdata->int2_y_low_enable << + KR3DH_YLIE_SHIFT) & KR3DH_YLIE_MASK; + chip->int2_x_high_enable = (pdata->int2_x_high_enable << + KR3DH_XHIE_SHIFT) & KR3DH_XHIE_MASK; + chip->int2_x_low_enable = (pdata->int2_x_low_enable << + KR3DH_XLIE_SHIFT) & KR3DH_XLIE_MASK; + + /* INT2_THS_REG */ + chip->int2_threshold = pdata->int2_threshold & KR3DH_INT_THS_MASK; + + /* INT2_DURATION_REG */ + chip->int2_duration = pdata->int2_duration & KR3DH_INT_DURATION_MASK; +} + +static int __devinit kr3dh_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct kr3dh_chip *chip; + struct kr3dh_platform_data *pdata; + u8 value; + int ret = -1; + + chip = kzalloc(sizeof(struct kr3dh_chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + pdata = client->dev.platform_data; + chip->pdata = pdata; + + /* Detect device id */ + value = kr3dh_read_reg(client, KR3DH_WHO_AM_I); + if ((value != KR3DH_DEV_ID) && (value != K3DH_DEV_ID)) { + dev_err(&client->dev, "failed to detect device id\n"); + goto error_devid_detect; + } + + chip->client = client; + + i2c_set_clientdata(client, chip); + INIT_WORK(&chip->work1, kr3dh_work1); + INIT_WORK(&chip->work2, kr3dh_work2); + mutex_init(&chip->lock); + + ret = sysfs_create_group(&client->dev.kobj, &kr3dh_group); + if (ret) { + dev_err(&client->dev, + "creating attribute group failed\n"); + goto error_sysfs; + } + + if (pdata) + kr3dh_format_chip_data(chip, pdata); + + ret = kr3dh_register_input_device(chip); + if (ret) { + dev_err(&client->dev, "registering input device failed\n"); + goto error_input; + } + + kr3dh_initialize_chip(chip); + + pm_runtime_set_active(&client->dev); + + dev_info(&client->dev, "%s registered\n", id->name); + + return 0; + +error_input: + sysfs_remove_group(&client->dev.kobj, &kr3dh_group); +error_devid_detect: +error_sysfs: + kfree(chip); + return ret; +} + +static int __devexit kr3dh_remove(struct i2c_client *client) +{ + struct kr3dh_chip *chip = i2c_get_clientdata(client); + + disable_irq_nosync(client->irq); + disable_irq_nosync(chip->irq2); + cancel_work_sync(&chip->work1); + cancel_work_sync(&chip->work2); + + kr3dh_unregister_input_device(chip); + sysfs_remove_group(&client->dev.kobj, &kr3dh_group); + kfree(chip); + + return 0; +} + +#ifdef CONFIG_PM +static int kr3dh_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct kr3dh_chip *chip = i2c_get_clientdata(client); + + disable_irq_nosync(client->irq); + disable_irq_nosync(chip->irq2); + cancel_work_sync(&chip->work1); + cancel_work_sync(&chip->work2); + + chip->resume_power_mode = chip->power_mode; + kr3dh_set_power_mode(chip, KR3DH_POWER_DOWN << KR3DH_PM_SHIFT); + + return 0; +} + +static int kr3dh_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct kr3dh_chip *chip = i2c_get_clientdata(client); + + kr3dh_set_power_mode(chip, chip->resume_power_mode >> KR3DH_PM_SHIFT); + + enable_irq(client->irq); + enable_irq(chip->irq2); + + return 0; +} + +static int kr3dh_freeze(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct kr3dh_chip *chip = i2c_get_clientdata(client); + + disable_irq_nosync(client->irq); + disable_irq_nosync(chip->irq2); + cancel_work_sync(&chip->work1); + cancel_work_sync(&chip->work2); + + return 0; +} + +static int kr3dh_restore(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct kr3dh_chip *chip = i2c_get_clientdata(client); + + kr3dh_initialize_chip(chip); + + enable_irq(client->irq); + enable_irq(chip->irq2); + + return 0; +} + + +static const struct dev_pm_ops kr3dh_dev_pm_ops = { + .suspend = kr3dh_suspend, + .resume = kr3dh_resume, + .freeze = kr3dh_freeze, + .restore = kr3dh_restore, +}; + +#define KR3DH_DEV_PM_OPS (&kr3dh_dev_pm_ops) +#else +#define KR3DH_DEV_PM_OPS NULL +#endif + +static const struct i2c_device_id kr3dh_id[] = { + { "KR3DH", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, kr3dh_id); + +static struct i2c_driver kr3dh_i2c_driver = { + .driver = { + .name = "KR3DH", + .pm = KR3DH_DEV_PM_OPS, + }, + .probe = kr3dh_probe, + .remove = __exit_p(kr3dh_remove), + .id_table = kr3dh_id, +}; + +static int __init kr3dh_init(void) +{ + return i2c_add_driver(&kr3dh_i2c_driver); +} +module_init(kr3dh_init); + +static void __exit kr3dh_exit(void) +{ + i2c_del_driver(&kr3dh_i2c_driver); +} +module_exit(kr3dh_exit); + +MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>"); +MODULE_DESCRIPTION("KR3DH accelerometer driver"); +MODULE_LICENSE("GPL"); |