diff options
Diffstat (limited to 'drivers/input/misc/kr3dh.c')
-rw-r--r-- | drivers/input/misc/kr3dh.c | 1352 |
1 files changed, 1352 insertions, 0 deletions
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"); |