Created
May 24, 2017 05:57
-
-
Save alexforsale/2af550aef0f5c6b11796d7e09a0edfdd to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
From a67f8c0ca3bad08a7d4ecf26df6a11c2757a9aee Mon Sep 17 00:00:00 2001 | |
From: alexforsale <[email protected]> | |
Date: Sun, 21 May 2017 00:54:00 +0700 | |
Subject: [PATCH 1/6] A16C3H: drivers:input:misc: add kionix_accel | |
--- | |
arch/arm/boot/dts/qcom/msm8909-qrd-skuc.dtsi | 16 + | |
drivers/input/misc/Kconfig | 9 + | |
drivers/input/misc/Makefile | 1 + | |
drivers/input/misc/kionix_accel.c | 2518 ++++++++++++++++++++++++++ | |
include/linux/input/kionix_accel.h | 100 + | |
5 files changed, 2644 insertions(+) | |
create mode 100644 drivers/input/misc/kionix_accel.c | |
create mode 100644 include/linux/input/kionix_accel.h | |
diff --git a/arch/arm/boot/dts/qcom/msm8909-qrd-skuc.dtsi b/arch/arm/boot/dts/qcom/msm8909-qrd-skuc.dtsi | |
index 1fdfbf59d22a..bccd5fa2b87a 100644 | |
--- a/arch/arm/boot/dts/qcom/msm8909-qrd-skuc.dtsi | |
+++ b/arch/arm/boot/dts/qcom/msm8909-qrd-skuc.dtsi | |
@@ -133,6 +133,22 @@ | |
bosch,gpio-int1 = <&msm_gpio 96 0x2002>; | |
bosch,gpio-int2 = <&msm_gpio 65 0x2002>; | |
}; | |
+ | |
+ kionix@e { | |
+ compatible = "kionix,kxtj2-1009"; | |
+ reg = <0x0e>; | |
+ interrupt-parent = <&msm_gpio>; | |
+ interrupts = <112 0x2002>; | |
+ vdd-supply = <&pm8909_l17>; | |
+ vio-supply = <&pm8909_l6>; | |
+ kionix,min_interval = <5>; | |
+ kionix,poll_interval = <100>; | |
+ kionix,accel_direction = <4>; | |
+ kionix,accel_irq_use_drdy = <0>; | |
+ kionix,accel_res = <12>; | |
+ kionix,accel_g_range = <2>; | |
+ }; | |
+ | |
}; | |
gen-vkeys { | |
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig | |
index 99ceadfca66a..f0952a4c5b2d 100644 | |
--- a/drivers/input/misc/Kconfig | |
+++ b/drivers/input/misc/Kconfig | |
@@ -228,6 +228,15 @@ config SENSORS_LIS3DH | |
To compile this driver as a module, choose M here: the | |
module will be called lis3dh_acc. | |
+config SENSORS_KIONIX_ACCEL | |
+ tristate "KIONIX_ACCEL" | |
+ depends on I2C | |
+ help | |
+ If you say yes here you get support for the Kionix digital 3-axis | |
+ accelerometer. | |
+ This driver can also be built as a module. If so, the module will be | |
+ called kionix_accel. | |
+ | |
config INPUT_APANEL | |
tristate "Fujitsu Lifebook Application Panel buttons" | |
depends on X86 && I2C && LEDS_CLASS | |
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile | |
index 99a162f88810..4a71dc16f475 100644 | |
--- a/drivers/input/misc/Makefile | |
+++ b/drivers/input/misc/Makefile | |
@@ -74,6 +74,7 @@ obj-$(CONFIG_SENSORS_AKM8963) += akm8963.o | |
obj-$(CONFIG_SENSORS_AKM09911) += akm09911.o | |
obj-$(CONFIG_SENSORS_LIS3DH) += lis3dh_acc.o | |
obj-$(CONFIG_SENSORS_AP3426) += ap3426.o | |
+obj-$(CONFIG_SENSORS_KIONIX_ACCEL) += kionix_accel.o | |
obj-$(CONFIG_SENSORS_BMA2X2) += bstclass.o | |
obj-$(CONFIG_SENSORS_BMA2X2) += bma2x2.o | |
diff --git a/drivers/input/misc/kionix_accel.c b/drivers/input/misc/kionix_accel.c | |
new file mode 100644 | |
index 000000000000..c4ca918e462d | |
--- /dev/null | |
+++ b/drivers/input/misc/kionix_accel.c | |
@@ -0,0 +1,2518 @@ | |
+/* drivers/input/misc/kionix_accel.c - Kionix accelerometer driver | |
+ * | |
+ * Copyright (C) 2012-2015 Kionix, Inc. | |
+ * Written by Kuching Tan <[email protected]> | |
+ * | |
+ * This program is free software: you can redistribute it and/or modify | |
+ * it under the terms of the GNU General Public License as published by | |
+ * the Free Software Foundation, either version 2 of the License, or | |
+ * (at your option) any later version. | |
+ * | |
+ * This program is distributed in the hope that it will be useful, | |
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
+ * GNU General Public License for more details. | |
+ * | |
+ * You should have received a copy of the GNU General Public License | |
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
+ * | |
+ */ | |
+ | |
+#include <linux/kernel.h> | |
+#include <linux/delay.h> | |
+#include <linux/i2c.h> | |
+#include <linux/input.h> | |
+#include <linux/interrupt.h> | |
+#include <linux/workqueue.h> | |
+#include <linux/module.h> | |
+#include <linux/slab.h> | |
+#include <linux/input/kionix_accel.h> | |
+#include <linux/version.h> | |
+#include <linux/proc_fs.h> | |
+#include <linux/regulator/consumer.h> | |
+#include <linux/sensors.h> | |
+#ifdef CONFIG_HAS_EARLYSUSPEND | |
+#include <linux/earlysuspend.h> | |
+#endif /* CONFIG_HAS_EARLYSUSPEND */ | |
+ | |
+#ifdef CONFIG_OF | |
+#include <linux/of_gpio.h> | |
+#endif /* CONFIG_OF */ | |
+ | |
+/* Debug Message Flags */ | |
+#define KIONIX_KMSG_ERR 1 /* Print kernel debug message for error */ | |
+#define KIONIX_KMSG_INF 1 /* Print kernel debug message for info */ | |
+ | |
+#if KIONIX_KMSG_ERR | |
+#define KMSGERR(format, ...) \ | |
+ dev_err(format, ## __VA_ARGS__) | |
+#else | |
+#define KMSGERR(format, ...) | |
+#endif | |
+ | |
+#if KIONIX_KMSG_INF | |
+#define KMSGINF(format, ...) \ | |
+ dev_info(format, ## __VA_ARGS__) | |
+#else | |
+#define KMSGINF(format, ...) | |
+#endif | |
+ | |
+/****************************************************************************** | |
+ * Accelerometer WHO_AM_I return value | |
+ *****************************************************************************/ | |
+#define KIONIX_ACCEL_WHO_AM_I_KXTE9 0x00 | |
+#define KIONIX_ACCEL_WHO_AM_I_KXTF9 0x01 | |
+#define KIONIX_ACCEL_WHO_AM_I_KXTI9_1001 0x04 | |
+#define KIONIX_ACCEL_WHO_AM_I_KXTIK_1004 0x05 | |
+#define KIONIX_ACCEL_WHO_AM_I_KXTJ9_1005 0x07 | |
+#define KIONIX_ACCEL_WHO_AM_I_KXTJ9_1007 0x08 | |
+#define KIONIX_ACCEL_WHO_AM_I_KXCJ9_1008 0x0A | |
+#define KIONIX_ACCEL_WHO_AM_I_KXTJ2_1009 0x09 | |
+#define KIONIX_ACCEL_WHO_AM_I_KXCJK_1013 0x11 | |
+ | |
+/****************************************************************************** | |
+ * Accelerometer Grouping | |
+ *****************************************************************************/ | |
+#define KIONIX_ACCEL_GRP1 1 /* KXTE9 */ | |
+#define KIONIX_ACCEL_GRP2 2 /* KXTF9/I9-1001/J9-1005 */ | |
+#define KIONIX_ACCEL_GRP3 3 /* KXTIK-1004 */ | |
+#define KIONIX_ACCEL_GRP4 4 /* KXTJ9-1007/KXCJ9-1008 */ | |
+#define KIONIX_ACCEL_GRP5 5 /* KXTJ2-1009 */ | |
+#define KIONIX_ACCEL_GRP6 6 /* KXCJK-1013 */ | |
+ | |
+/****************************************************************************** | |
+ * Registers for Accelerometer Group 1 & 2 & 3 | |
+ *****************************************************************************/ | |
+#define ACCEL_WHO_AM_I 0x0F | |
+ | |
+/*****************************************************************************/ | |
+/* Registers for Accelerometer Group 1 */ | |
+/*****************************************************************************/ | |
+/* Output Registers */ | |
+#define ACCEL_GRP1_XOUT 0x12 | |
+/* Control Registers */ | |
+#define ACCEL_GRP1_CTRL_REG1 0x1B | |
+/* CTRL_REG1 */ | |
+#define ACCEL_GRP1_PC1_OFF 0x7F | |
+#define ACCEL_GRP1_PC1_ON (1 << 7) | |
+#define ACCEL_GRP1_ODR40 (3 << 3) | |
+#define ACCEL_GRP1_ODR10 (2 << 3) | |
+#define ACCEL_GRP1_ODR3 (1 << 3) | |
+#define ACCEL_GRP1_ODR1 (0 << 3) | |
+#define ACCEL_GRP1_ODR_MASK (3 << 3) | |
+ | |
+/*****************************************************************************/ | |
+/* Registers for Accelerometer Group 2 & 3 */ | |
+/*****************************************************************************/ | |
+/* Output Registers */ | |
+#define ACCEL_GRP2_XOUT_L 0x06 | |
+/* Control Registers */ | |
+#define ACCEL_GRP2_INT_REL 0x1A | |
+#define ACCEL_GRP2_CTRL_REG1 0x1B | |
+#define ACCEL_GRP2_INT_CTRL1 0x1E | |
+#define ACCEL_GRP2_DATA_CTRL 0x21 | |
+/* CTRL_REG1 */ | |
+#define ACCEL_GRP2_PC1_OFF 0x7F | |
+#define ACCEL_GRP2_PC1_ON (1 << 7) | |
+#define ACCEL_GRP2_DRDYE (1 << 5) | |
+#define ACCEL_GRP2_G_8G (2 << 3) | |
+#define ACCEL_GRP2_G_4G (1 << 3) | |
+#define ACCEL_GRP2_G_2G (0 << 3) | |
+#define ACCEL_GRP2_G_MASK (3 << 3) | |
+#define ACCEL_GRP2_RES_8BIT (0 << 6) | |
+#define ACCEL_GRP2_RES_12BIT (1 << 6) | |
+#define ACCEL_GRP2_RES_MASK (1 << 6) | |
+/* INT_CTRL1 */ | |
+#define ACCEL_GRP2_IEA (1 << 4) | |
+#define ACCEL_GRP2_IEN (1 << 5) | |
+/* DATA_CTRL_REG */ | |
+#define ACCEL_GRP2_ODR12_5 0x00 | |
+#define ACCEL_GRP2_ODR25 0x01 | |
+#define ACCEL_GRP2_ODR50 0x02 | |
+#define ACCEL_GRP2_ODR100 0x03 | |
+#define ACCEL_GRP2_ODR200 0x04 | |
+#define ACCEL_GRP2_ODR400 0x05 | |
+#define ACCEL_GRP2_ODR800 0x06 | |
+/*****************************************************************************/ | |
+ | |
+/*****************************************************************************/ | |
+/* Registers for Accelerometer Group 4 & 5 & 6 */ | |
+/*****************************************************************************/ | |
+/* Output Registers */ | |
+#define ACCEL_GRP4_XOUT_L 0x06 | |
+/* Control Registers */ | |
+#define ACCEL_GRP4_INT_REL 0x1A | |
+#define ACCEL_GRP4_CTRL_REG1 0x1B | |
+#define ACCEL_GRP4_INT_CTRL1 0x1E | |
+#define ACCEL_GRP4_DATA_CTRL 0x21 | |
+/* CTRL_REG1 */ | |
+#define ACCEL_GRP4_PC1_OFF 0x7F | |
+#define ACCEL_GRP4_PC1_ON (1 << 7) | |
+#define ACCEL_GRP4_DRDYE (1 << 5) | |
+#define ACCEL_GRP4_G_8G (2 << 3) | |
+#define ACCEL_GRP4_G_4G (1 << 3) | |
+#define ACCEL_GRP4_G_2G (0 << 3) | |
+#define ACCEL_GRP4_G_MASK (3 << 3) | |
+#define ACCEL_GRP4_RES_8BIT (0 << 6) | |
+#define ACCEL_GRP4_RES_12BIT (1 << 6) | |
+#define ACCEL_GRP4_RES_MASK (1 << 6) | |
+/* INT_CTRL1 */ | |
+#define ACCEL_GRP4_IEA (1 << 4) | |
+#define ACCEL_GRP4_IEN (1 << 5) | |
+/* DATA_CTRL_REG */ | |
+#define ACCEL_GRP4_ODR0_781 0x08 | |
+#define ACCEL_GRP4_ODR1_563 0x09 | |
+#define ACCEL_GRP4_ODR3_125 0x0A | |
+#define ACCEL_GRP4_ODR6_25 0x0B | |
+#define ACCEL_GRP4_ODR12_5 0x00 | |
+#define ACCEL_GRP4_ODR25 0x01 | |
+#define ACCEL_GRP4_ODR50 0x02 | |
+#define ACCEL_GRP4_ODR100 0x03 | |
+#define ACCEL_GRP4_ODR200 0x04 | |
+#define ACCEL_GRP4_ODR400 0x05 | |
+#define ACCEL_GRP4_ODR800 0x06 | |
+#define ACCEL_GRP4_ODR1600 0x07 | |
+/*****************************************************************************/ | |
+/* Input Event Constants */ | |
+#define ACCEL_G_MAX 8096 | |
+#define ACCEL_FUZZ 3 | |
+#define ACCEL_FLAT 3 | |
+/* I2C Retry Constants */ | |
+/* Number of times to retry i2c */ | |
+#define KIONIX_I2C_RETRY_COUNT 10 | |
+/* Timeout between retry (miliseconds) */ | |
+#define KIONIX_I2C_RETRY_TIMEOUT 1 | |
+ | |
+/* Earlysuspend Contants */ | |
+/* Timeout (miliseconds) */ | |
+#define KIONIX_ACCEL_EARLYSUSPEND_TIMEOUT 5000 | |
+ | |
+/* | |
+ * The following table lists the maximum appropriate poll interval for each | |
+ * available output data rate (ODR). | |
+ */ | |
+static const struct { | |
+ unsigned int cutoff; | |
+ u8 mask; | |
+} kionix_accel_grp1_odr_table[] = { | |
+ { | |
+ 100, ACCEL_GRP1_ODR40}, { | |
+ 334, ACCEL_GRP1_ODR10}, { | |
+ 1000, ACCEL_GRP1_ODR3}, { | |
+0, ACCEL_GRP1_ODR1},}; | |
+ | |
+static const struct { | |
+ unsigned int cutoff; | |
+ u8 mask; | |
+} kionix_accel_grp2_odr_table[] = { | |
+ { | |
+ 3, ACCEL_GRP2_ODR800}, { | |
+ 5, ACCEL_GRP2_ODR400}, { | |
+ 10, ACCEL_GRP2_ODR200}, { | |
+ 20, ACCEL_GRP2_ODR100}, { | |
+ 40, ACCEL_GRP2_ODR50}, { | |
+ 80, ACCEL_GRP2_ODR25}, { | |
+0, ACCEL_GRP2_ODR12_5},}; | |
+ | |
+static const struct { | |
+ unsigned int cutoff; | |
+ u8 mask; | |
+} kionix_accel_grp4_odr_table[] = { | |
+ { | |
+ 2, ACCEL_GRP4_ODR1600}, { | |
+ 3, ACCEL_GRP4_ODR800}, { | |
+ 5, ACCEL_GRP4_ODR400}, { | |
+ 10, ACCEL_GRP4_ODR200}, { | |
+ 20, ACCEL_GRP4_ODR100}, { | |
+ 40, ACCEL_GRP4_ODR50}, { | |
+ 80, ACCEL_GRP4_ODR25}, { | |
+ 160, ACCEL_GRP4_ODR12_5}, { | |
+ 320, ACCEL_GRP4_ODR6_25}, { | |
+ 640, ACCEL_GRP4_ODR3_125}, { | |
+ 1280, ACCEL_GRP4_ODR1_563}, { | |
+0, ACCEL_GRP4_ODR0_781},}; | |
+ | |
+enum { | |
+ accel_grp1_ctrl_reg1 = 0, | |
+ accel_grp1_regs_count, | |
+}; | |
+ | |
+enum { | |
+ accel_grp2_ctrl_reg1 = 0, | |
+ accel_grp2_data_ctrl, | |
+ accel_grp2_int_ctrl, | |
+ accel_grp2_regs_count, | |
+}; | |
+ | |
+enum { | |
+ accel_grp4_ctrl_reg1 = 0, | |
+ accel_grp4_data_ctrl, | |
+ accel_grp4_int_ctrl, | |
+ accel_grp4_regs_count, | |
+}; | |
+ | |
+struct kionix_accel_driver { | |
+ /* regulator data */ | |
+ bool power_on; | |
+ struct regulator *vdd; | |
+ struct regulator *vio; | |
+ | |
+ struct i2c_client *client; | |
+ struct sensors_classdev cdev; | |
+ struct kionix_accel_platform_data accel_pdata; | |
+ struct input_dev *input_dev; | |
+ struct delayed_work accel_work; | |
+ struct workqueue_struct *accel_workqueue; | |
+ wait_queue_head_t wqh_suspend; | |
+ | |
+ int accel_data[3]; | |
+ int accel_cali[3]; | |
+ u8 axis_map_x; | |
+ u8 axis_map_y; | |
+ u8 axis_map_z; | |
+ bool negate_x; | |
+ bool negate_y; | |
+ bool negate_z; | |
+ u8 shift; | |
+ | |
+ atomic_t delay; | |
+ | |
+ unsigned int poll_interval; | |
+ unsigned int poll_delay; | |
+ unsigned int accel_group; | |
+ u8 *accel_registers; | |
+ | |
+ atomic_t accel_suspended; | |
+ atomic_t accel_suspend_continue; | |
+ atomic_t accel_enabled; | |
+ atomic_t accel_input_event; | |
+ atomic_t accel_enable_resume; | |
+ struct mutex mutex_earlysuspend; | |
+ struct mutex mutex_resume; | |
+ rwlock_t rwlock_accel_data; | |
+ | |
+ bool accel_drdy; | |
+ | |
+ /* Function callback */ | |
+ void (*kionix_accel_report_accel_data) (struct kionix_accel_driver | |
+ *acceld); | |
+ int (*kionix_accel_update_odr) (struct kionix_accel_driver *acceld, | |
+ unsigned int poll_interval); | |
+ int (*kionix_accel_power_on_init) (struct kionix_accel_driver *acceld); | |
+ int (*kionix_accel_operate) (struct kionix_accel_driver *acceld); | |
+ int (*kionix_accel_standby) (struct kionix_accel_driver *acceld); | |
+ | |
+#ifdef CONFIG_HAS_EARLYSUSPEND | |
+ struct early_suspend early_suspend; | |
+#endif /* CONFIG_HAS_EARLYSUSPEND */ | |
+}; | |
+ | |
+/* | |
+ * Global data | |
+ */ | |
+static struct kionix_accel_driver *kionix_data; | |
+ | |
+static struct sensors_classdev sensors_cdev = { | |
+ .name = "kionix-accel", | |
+ .vendor = "kionix", | |
+ .version = 1, | |
+ .handle = SENSORS_ACCELERATION_HANDLE, | |
+ .type = SENSOR_TYPE_ACCELEROMETER, | |
+ .max_range = "156.8", /* 16g */ | |
+ .resolution = "0.156", /* 15.63mg */ | |
+ .sensor_power = "0.13", /* typical value */ | |
+ .min_delay = POLL_INTERVAL_MIN_MS * 1000, /* in microseconds */ | |
+ .fifo_reserved_event_count = 0, | |
+ .fifo_max_event_count = 0, | |
+ .enabled = 0, | |
+ .delay_msec = POLL_DEFAULT_INTERVAL_MS, /* in millisecond */ | |
+ .sensors_enable = NULL, | |
+ .sensors_poll_delay = NULL, | |
+}; | |
+ | |
+static int kionix_i2c_read(struct i2c_client *client, | |
+ u8 addr, u8 *data, int len) | |
+{ | |
+ struct i2c_msg msgs[] = { | |
+ { | |
+ .addr = client->addr, | |
+ .flags = client->flags, | |
+ .len = 1, | |
+ .buf = &addr, | |
+ }, | |
+ { | |
+ .addr = client->addr, | |
+ .flags = client->flags | I2C_M_RD, | |
+ .len = len, | |
+ .buf = data, | |
+ }, | |
+ }; | |
+ | |
+ return i2c_transfer(client->adapter, msgs, 2); | |
+} | |
+ | |
+static int kionix_strtok(const char *buf, | |
+ size_t count, char **token, const int token_nr) | |
+{ | |
+ char *buf2 = kzalloc((count + 1) * sizeof(char), GFP_KERNEL); | |
+ char **token2 = token; | |
+ unsigned int num_ptr = 0, num_nr = 0, num_neg = 0; | |
+ int i = 0, start = 0, end = (int)count; | |
+ | |
+ strlcpy(buf2, buf, sizeof(buf2)); | |
+ | |
+ while ((start < end) && (i < token_nr)) { | |
+ /* We found a negative sign */ | |
+ if (*(buf2 + start) == '-') { | |
+ /* Previous char(s) are numeric, so | |
+ we store their value first before proceed */ | |
+ if (num_nr > 0) { | |
+ /* If there is a pending negative sign, | |
+ we adjust the variables to account for it */ | |
+ if (num_neg) { | |
+ num_ptr--; | |
+ num_nr++; | |
+ } | |
+ *token2 = | |
+ kzalloc((num_nr + 2) * sizeof(char), | |
+ GFP_KERNEL); | |
+ strlcpy(*token2, (const char *)(buf2 + num_ptr), | |
+ (size_t) num_nr); | |
+ *(*token2 + num_nr) = '\n'; | |
+ i++; | |
+ token2++; | |
+ /* Reset */ | |
+ num_ptr = num_nr = 0; | |
+ } | |
+ /* This indicates that there is a pending | |
+ negative sign in the string */ | |
+ num_neg = 1; | |
+ } | |
+ /* We found a numeric */ | |
+ else if ((*(buf2 + start) >= '0') && (*(buf2 + start) <= '9')) { | |
+ /* If the previous char(s) are not numeric, | |
+ set num_ptr to current char */ | |
+ if (num_nr < 1) | |
+ num_ptr = start; | |
+ num_nr++; | |
+ } | |
+ /* We found an unwanted character */ | |
+ else { | |
+ /* Previous char(s) are numeric, so we | |
+ store their value first before proceed */ | |
+ if (num_nr > 0) { | |
+ if (num_neg) { | |
+ num_ptr--; | |
+ num_nr++; | |
+ } | |
+ *token2 = | |
+ kzalloc((num_nr + 2) * sizeof(char), | |
+ GFP_KERNEL); | |
+ strlcpy(*token2, (const char *)(buf2 + num_ptr), | |
+ (size_t) num_nr); | |
+ *(*token2 + num_nr) = '\n'; | |
+ i++; | |
+ token2++; | |
+ } | |
+ /* Reset all the variables to start afresh */ | |
+ num_ptr = num_nr = num_neg = 0; | |
+ } | |
+ start++; | |
+ } | |
+ | |
+ kfree(buf2); | |
+ | |
+ return (i == token_nr) ? token_nr : -1; | |
+} | |
+ | |
+static int kionix_accel_grp1_power_on_init(struct kionix_accel_driver *acceld) | |
+{ | |
+ int err; | |
+ | |
+ if (atomic_read(&acceld->accel_enabled) > 0) { | |
+ err = i2c_smbus_write_byte_data(acceld->client, | |
+ ACCEL_GRP1_CTRL_REG1, | |
+ acceld->accel_registers | |
+ [accel_grp1_ctrl_reg1] | | |
+ ACCEL_GRP1_PC1_ON); | |
+ if (err < 0) | |
+ return err; | |
+ } else { | |
+ err = i2c_smbus_write_byte_data(acceld->client, | |
+ ACCEL_GRP1_CTRL_REG1, | |
+ acceld->accel_registers | |
+ [accel_grp1_ctrl_reg1]); | |
+ if (err < 0) | |
+ return err; | |
+ } | |
+ | |
+ return 0; | |
+} | |
+ | |
+static int kionix_accel_grp1_operate(struct kionix_accel_driver *acceld) | |
+{ | |
+ int err; | |
+ | |
+ err = i2c_smbus_write_byte_data(acceld->client, ACCEL_GRP1_CTRL_REG1, | |
+ acceld->accel_registers | |
+ [accel_grp2_ctrl_reg1] | | |
+ ACCEL_GRP1_PC1_ON); | |
+ if (err < 0) | |
+ return err; | |
+ | |
+ queue_delayed_work(acceld->accel_workqueue, &acceld->accel_work, 0); | |
+ | |
+ return 0; | |
+} | |
+ | |
+static int kionix_accel_grp1_standby(struct kionix_accel_driver *acceld) | |
+{ | |
+ int err; | |
+ | |
+ cancel_delayed_work_sync(&acceld->accel_work); | |
+ | |
+ err = | |
+ i2c_smbus_write_byte_data(acceld->client, ACCEL_GRP1_CTRL_REG1, 0); | |
+ if (err < 0) | |
+ return err; | |
+ | |
+ return 0; | |
+} | |
+ | |
+static void kionix_accel_grp1_report_accel_data(struct kionix_accel_driver | |
+ *acceld) | |
+{ | |
+ u8 accel_data[3]; | |
+ s16 x, y, z; | |
+ int err; | |
+ struct input_dev *input_dev = acceld->input_dev; | |
+ int loop = KIONIX_I2C_RETRY_COUNT; | |
+ | |
+ if (atomic_read(&acceld->accel_enabled) > 0) { | |
+ if (atomic_read(&acceld->accel_enable_resume) > 0) { | |
+ while (loop) { | |
+ mutex_lock(&input_dev->mutex); | |
+ err = | |
+ kionix_i2c_read(acceld->client, | |
+ ACCEL_GRP1_XOUT, accel_data, | |
+ 6); | |
+ mutex_unlock(&input_dev->mutex); | |
+ if (err < 0) { | |
+ loop--; | |
+ msleep(KIONIX_I2C_RETRY_TIMEOUT); | |
+ } else | |
+ loop = 0; | |
+ } | |
+ if (err < 0) { | |
+ KMSGERR(&acceld->client->dev, | |
+ "%s: read data output error = %d\n", | |
+ __func__, err); | |
+ } else { | |
+ write_lock(&acceld->rwlock_accel_data); | |
+ | |
+ x = ((s16) | |
+ le16_to_cpu(((s16) | |
+ (accel_data | |
+ [acceld->axis_map_x] >> 2)) - | |
+ 32)) << 6; | |
+ y = ((s16) | |
+ le16_to_cpu(((s16) | |
+ (accel_data | |
+ [acceld->axis_map_y] >> 2)) - | |
+ 32)) << 6; | |
+ z = ((s16) | |
+ le16_to_cpu(((s16) | |
+ (accel_data | |
+ [acceld->axis_map_z] >> 2)) - | |
+ 32)) << 6; | |
+ | |
+ acceld->accel_data[acceld->axis_map_x] = | |
+ (acceld->negate_x ? -x : x) + | |
+ acceld->accel_cali[acceld->axis_map_x]; | |
+ acceld->accel_data[acceld->axis_map_y] = | |
+ (acceld->negate_y ? -y : y) + | |
+ acceld->accel_cali[acceld->axis_map_y]; | |
+ acceld->accel_data[acceld->axis_map_z] = | |
+ (acceld->negate_z ? -z : z) + | |
+ acceld->accel_cali[acceld->axis_map_z]; | |
+ | |
+ if (atomic_read(&acceld->accel_input_event) | |
+ > 0) { | |
+ input_report_abs(acceld->input_dev, | |
+ ABS_X, | |
+ acceld->accel_data | |
+ [acceld->axis_map_x]); | |
+ input_report_abs(acceld->input_dev, | |
+ ABS_Y, | |
+ acceld->accel_data | |
+ [acceld->axis_map_y]); | |
+ input_report_abs(acceld->input_dev, | |
+ ABS_Z, | |
+ acceld->accel_data | |
+ [acceld->axis_map_z]); | |
+ input_sync(acceld->input_dev); | |
+ } | |
+ | |
+ write_unlock(&acceld->rwlock_accel_data); | |
+ } | |
+ } else | |
+ atomic_inc(&acceld->accel_enable_resume); | |
+ } | |
+} | |
+ | |
+static int kionix_accel_grp1_update_odr(struct kionix_accel_driver *acceld, | |
+ unsigned int poll_interval) | |
+{ | |
+ int err; | |
+ int i; | |
+ u8 odr; | |
+ | |
+ /* Use the lowest ODR that can support the requested poll interval */ | |
+ for (i = 0; i < ARRAY_SIZE(kionix_accel_grp1_odr_table); i++) { | |
+ odr = kionix_accel_grp1_odr_table[i].mask; | |
+ if (poll_interval < kionix_accel_grp1_odr_table[i].cutoff) | |
+ break; | |
+ } | |
+ | |
+/* Do not need to update CTRL_REG1 register if the ODR is not changed */ | |
+ if ((acceld->accel_registers[accel_grp1_ctrl_reg1] & | |
+ ACCEL_GRP1_ODR_MASK) == odr) | |
+ return 0; | |
+ else { | |
+ acceld->accel_registers[accel_grp1_ctrl_reg1] &= | |
+ ~ACCEL_GRP1_ODR_MASK; | |
+ acceld->accel_registers[accel_grp1_ctrl_reg1] |= odr; | |
+ } | |
+ | |
+/* Do not need to update CTRL_REG1 register if | |
+the sensor is not currently turn on */ | |
+ if (atomic_read(&acceld->accel_enabled) > 0) { | |
+ err = | |
+ i2c_smbus_write_byte_data(acceld->client, | |
+ ACCEL_GRP1_CTRL_REG1, | |
+ acceld->accel_registers | |
+ [accel_grp1_ctrl_reg1] | | |
+ ACCEL_GRP1_PC1_ON); | |
+ if (err < 0) | |
+ return err; | |
+ } | |
+ | |
+ return 0; | |
+} | |
+ | |
+static int kionix_accel_grp2_power_on_init(struct kionix_accel_driver *acceld) | |
+{ | |
+ int err; | |
+ | |
+/* ensure that PC1 is cleared before updating control registers */ | |
+ err = i2c_smbus_write_byte_data(acceld->client, | |
+ ACCEL_GRP2_CTRL_REG1, 0); | |
+ if (err < 0) | |
+ return err; | |
+ | |
+ err = i2c_smbus_write_byte_data(acceld->client, | |
+ ACCEL_GRP2_DATA_CTRL, | |
+ acceld->accel_registers | |
+ [accel_grp2_data_ctrl]); | |
+ if (err < 0) | |
+ return err; | |
+ | |
+/* only write INT_CTRL_REG1 if in irq mode */ | |
+ if (acceld->client->irq) { | |
+ err = i2c_smbus_write_byte_data(acceld->client, | |
+ ACCEL_GRP2_INT_CTRL1, | |
+ acceld->accel_registers | |
+ [accel_grp2_int_ctrl]); | |
+ if (err < 0) | |
+ return err; | |
+ } | |
+ | |
+ if (atomic_read(&acceld->accel_enabled) > 0) { | |
+ err = i2c_smbus_write_byte_data(acceld->client, | |
+ ACCEL_GRP2_CTRL_REG1, | |
+ acceld->accel_registers | |
+ [accel_grp2_ctrl_reg1] | | |
+ ACCEL_GRP2_PC1_ON); | |
+ if (err < 0) | |
+ return err; | |
+ } else { | |
+ err = i2c_smbus_write_byte_data(acceld->client, | |
+ ACCEL_GRP2_CTRL_REG1, | |
+ acceld->accel_registers | |
+ [accel_grp2_ctrl_reg1]); | |
+ if (err < 0) | |
+ return err; | |
+ } | |
+ | |
+ return 0; | |
+} | |
+ | |
+static int kionix_accel_grp2_operate(struct kionix_accel_driver *acceld) | |
+{ | |
+ int err; | |
+ | |
+ err = i2c_smbus_write_byte_data(acceld->client, ACCEL_GRP2_CTRL_REG1, | |
+ acceld->accel_registers | |
+ [accel_grp2_ctrl_reg1] | | |
+ ACCEL_GRP2_PC1_ON); | |
+ if (err < 0) | |
+ return err; | |
+ | |
+ if (acceld->accel_drdy == 0) | |
+ queue_delayed_work(acceld->accel_workqueue, &acceld->accel_work, | |
+ 0); | |
+ | |
+ return 0; | |
+} | |
+ | |
+static int kionix_accel_grp2_standby(struct kionix_accel_driver *acceld) | |
+{ | |
+ int err; | |
+ | |
+ if (acceld->accel_drdy == 0) | |
+ cancel_delayed_work_sync(&acceld->accel_work); | |
+ | |
+ err = | |
+ i2c_smbus_write_byte_data(acceld->client, ACCEL_GRP2_CTRL_REG1, 0); | |
+ if (err < 0) | |
+ return err; | |
+ | |
+ return 0; | |
+} | |
+ | |
+static void kionix_accel_grp2_report_accel_data(struct kionix_accel_driver | |
+ *acceld) | |
+{ | |
+ struct { | |
+ union { | |
+ s16 accel_data_s16[3]; | |
+ s8 accel_data_s8[6]; | |
+ }; | |
+ } accel_data; | |
+ s16 x, y, z; | |
+ int err; | |
+ struct input_dev *input_dev = acceld->input_dev; | |
+ int loop; | |
+ | |
+ /* Only read the output registers if enabled */ | |
+ if (atomic_read(&acceld->accel_enabled) > 0) { | |
+ if (atomic_read(&acceld->accel_enable_resume) > 0) { | |
+ loop = KIONIX_I2C_RETRY_COUNT; | |
+ while (loop) { | |
+ mutex_lock(&input_dev->mutex); | |
+ err = | |
+ kionix_i2c_read(acceld->client, | |
+ ACCEL_GRP2_XOUT_L, (u8 *) | |
+ accel_data.accel_data_s16, | |
+ 6); | |
+ mutex_unlock(&input_dev->mutex); | |
+ if (err < 0) { | |
+ loop--; | |
+ msleep(KIONIX_I2C_RETRY_TIMEOUT); | |
+ } else | |
+ loop = 0; | |
+ } | |
+ if (err < 0) { | |
+ KMSGERR(&acceld->client->dev, | |
+ "%s: read data output error = %d\n", | |
+ __func__, err); | |
+ } else { | |
+ write_lock(&acceld->rwlock_accel_data); | |
+ | |
+ x = ((s16) | |
+ le16_to_cpu(accel_data.accel_data_s16 | |
+ [acceld->axis_map_x])) >> | |
+ acceld->shift; | |
+ y = ((s16) | |
+ le16_to_cpu(accel_data.accel_data_s16 | |
+ [acceld->axis_map_y])) >> | |
+ acceld->shift; | |
+ z = ((s16) | |
+ le16_to_cpu(accel_data.accel_data_s16 | |
+ [acceld->axis_map_z])) >> | |
+ acceld->shift; | |
+ | |
+ acceld->accel_data[acceld->axis_map_x] = | |
+ (acceld->negate_x ? -x : x) + | |
+ acceld->accel_cali[acceld->axis_map_x]; | |
+ acceld->accel_data[acceld->axis_map_y] = | |
+ (acceld->negate_y ? -y : y) + | |
+ acceld->accel_cali[acceld->axis_map_y]; | |
+ acceld->accel_data[acceld->axis_map_z] = | |
+ (acceld->negate_z ? -z : z) + | |
+ acceld->accel_cali[acceld->axis_map_z]; | |
+ | |
+ if (atomic_read(&acceld->accel_input_event) | |
+ > 0) { | |
+ input_report_abs(acceld->input_dev, | |
+ ABS_X, | |
+ acceld->accel_data | |
+ [acceld->axis_map_x]); | |
+ input_report_abs(acceld->input_dev, | |
+ ABS_Y, | |
+ acceld->accel_data | |
+ [acceld->axis_map_y]); | |
+ input_report_abs(acceld->input_dev, | |
+ ABS_Z, | |
+ acceld->accel_data | |
+ [acceld->axis_map_z]); | |
+ input_sync(acceld->input_dev); | |
+ } | |
+ | |
+ write_unlock(&acceld->rwlock_accel_data); | |
+ } | |
+ } else | |
+ atomic_inc(&acceld->accel_enable_resume); | |
+ } | |
+ | |
+ /* Clear the interrupt if using drdy */ | |
+ if (acceld->accel_drdy == 1) { | |
+ loop = KIONIX_I2C_RETRY_COUNT; | |
+ while (loop) { | |
+ err = | |
+ i2c_smbus_read_byte_data(acceld->client, | |
+ ACCEL_GRP2_INT_REL); | |
+ if (err < 0) { | |
+ loop--; | |
+ msleep(KIONIX_I2C_RETRY_TIMEOUT); | |
+ } else | |
+ loop = 0; | |
+ } | |
+ if (err < 0) | |
+ KMSGERR(&acceld->client->dev, | |
+ "%s: clear interrupt error = %d\n", __func__, | |
+ err); | |
+ } | |
+} | |
+ | |
+static void kionix_accel_grp2_update_g_range(struct kionix_accel_driver *acceld) | |
+{ | |
+ acceld->accel_registers[accel_grp2_ctrl_reg1] &= ~ACCEL_GRP2_G_MASK; | |
+ | |
+ switch (acceld->accel_pdata.accel_g_range) { | |
+ case KIONIX_ACCEL_G_8G: | |
+ case KIONIX_ACCEL_G_6G: | |
+ acceld->shift = 2; | |
+ acceld->accel_registers[accel_grp2_ctrl_reg1] |= | |
+ ACCEL_GRP2_G_8G; | |
+ break; | |
+ case KIONIX_ACCEL_G_4G: | |
+ acceld->shift = 3; | |
+ acceld->accel_registers[accel_grp2_ctrl_reg1] |= | |
+ ACCEL_GRP2_G_4G; | |
+ break; | |
+ case KIONIX_ACCEL_G_2G: | |
+ default: | |
+ acceld->shift = 4; | |
+ acceld->accel_registers[accel_grp2_ctrl_reg1] |= | |
+ ACCEL_GRP2_G_2G; | |
+ break; | |
+ } | |
+ | |
+ return; | |
+} | |
+ | |
+static int kionix_accel_grp2_update_odr(struct kionix_accel_driver *acceld, | |
+ unsigned int poll_interval) | |
+{ | |
+ int err; | |
+ int i; | |
+ u8 odr; | |
+ | |
+ /* Use the lowest ODR that can support the requested poll interval */ | |
+ for (i = 0; i < ARRAY_SIZE(kionix_accel_grp2_odr_table); i++) { | |
+ odr = kionix_accel_grp2_odr_table[i].mask; | |
+ if (poll_interval < kionix_accel_grp2_odr_table[i].cutoff) | |
+ break; | |
+ } | |
+ | |
+ /* Do not need to update DATA_CTRL_REG | |
+ register if the ODR is not changed */ | |
+ if (acceld->accel_registers[accel_grp2_data_ctrl] == odr) | |
+ return 0; | |
+ else | |
+ acceld->accel_registers[accel_grp2_data_ctrl] = odr; | |
+ | |
+ if (atomic_read(&acceld->accel_enabled) > 0) { | |
+ err = | |
+ i2c_smbus_write_byte_data(acceld->client, | |
+ ACCEL_GRP2_CTRL_REG1, 0); | |
+ if (err < 0) | |
+ return err; | |
+ | |
+ err = | |
+ i2c_smbus_write_byte_data(acceld->client, | |
+ ACCEL_GRP2_DATA_CTRL, | |
+ acceld->accel_registers | |
+ [accel_grp2_data_ctrl]); | |
+ if (err < 0) | |
+ return err; | |
+ | |
+ err = | |
+ i2c_smbus_write_byte_data(acceld->client, | |
+ ACCEL_GRP2_CTRL_REG1, | |
+ acceld->accel_registers | |
+ [accel_grp2_ctrl_reg1] | | |
+ ACCEL_GRP2_PC1_ON); | |
+ if (err < 0) | |
+ return err; | |
+ } | |
+ | |
+ return 0; | |
+} | |
+ | |
+static int kionix_accel_grp4_power_on_init(struct kionix_accel_driver *acceld) | |
+{ | |
+ int err; | |
+ | |
+ /* ensure that PC1 is cleared before updating control registers */ | |
+ err = i2c_smbus_write_byte_data(acceld->client, | |
+ ACCEL_GRP4_CTRL_REG1, 0); | |
+ if (err < 0) | |
+ return err; | |
+ | |
+ err = i2c_smbus_write_byte_data(acceld->client, | |
+ ACCEL_GRP4_DATA_CTRL, | |
+ acceld->accel_registers | |
+ [accel_grp4_data_ctrl]); | |
+ if (err < 0) | |
+ return err; | |
+ | |
+ /* only write INT_CTRL_REG1 if in irq mode */ | |
+ if (acceld->client->irq) { | |
+ err = i2c_smbus_write_byte_data(acceld->client, | |
+ ACCEL_GRP4_INT_CTRL1, | |
+ acceld->accel_registers | |
+ [accel_grp4_int_ctrl]); | |
+ if (err < 0) | |
+ return err; | |
+ } | |
+ | |
+ if (atomic_read(&acceld->accel_enabled) > 0) { | |
+ err = i2c_smbus_write_byte_data(acceld->client, | |
+ ACCEL_GRP4_CTRL_REG1, | |
+ acceld->accel_registers | |
+ [accel_grp4_ctrl_reg1] | | |
+ ACCEL_GRP4_PC1_ON); | |
+ if (err < 0) | |
+ return err; | |
+ } else { | |
+ err = i2c_smbus_write_byte_data(acceld->client, | |
+ ACCEL_GRP4_CTRL_REG1, | |
+ acceld->accel_registers | |
+ [accel_grp4_ctrl_reg1]); | |
+ if (err < 0) | |
+ return err; | |
+ } | |
+ | |
+ return 0; | |
+} | |
+ | |
+static int kionix_accel_grp4_operate(struct kionix_accel_driver *acceld) | |
+{ | |
+ int err; | |
+ | |
+ err = i2c_smbus_write_byte_data(acceld->client, ACCEL_GRP4_CTRL_REG1, | |
+ acceld->accel_registers | |
+ [accel_grp4_ctrl_reg1] | | |
+ ACCEL_GRP4_PC1_ON); | |
+ if (err < 0) | |
+ return err; | |
+ | |
+ if (acceld->accel_drdy == 0) | |
+ queue_delayed_work(acceld->accel_workqueue, &acceld->accel_work, | |
+ 0); | |
+ | |
+ return 0; | |
+} | |
+ | |
+static int kionix_accel_grp4_standby(struct kionix_accel_driver *acceld) | |
+{ | |
+ int err; | |
+ | |
+ if (acceld->accel_drdy == 0) | |
+ cancel_delayed_work_sync(&acceld->accel_work); | |
+ | |
+ err = | |
+ i2c_smbus_write_byte_data(acceld->client, ACCEL_GRP4_CTRL_REG1, 0); | |
+ if (err < 0) | |
+ return err; | |
+ | |
+ return 0; | |
+} | |
+ | |
+static void kionix_accel_grp4_report_accel_data(struct kionix_accel_driver | |
+ *acceld) | |
+{ | |
+ struct { | |
+ union { | |
+ s16 accel_data_s16[3]; | |
+ s8 accel_data_s8[6]; | |
+ }; | |
+ } accel_data; | |
+ s16 x, y, z; | |
+ int err; | |
+ struct input_dev *input_dev = acceld->input_dev; | |
+ int loop; | |
+ | |
+ /* Only read the output registers if enabled */ | |
+ if (atomic_read(&acceld->accel_enabled) > 0) { | |
+ if (atomic_read(&acceld->accel_enable_resume) > 0) { | |
+ | |
+ loop = KIONIX_I2C_RETRY_COUNT; | |
+ while (loop) { | |
+ mutex_lock(&input_dev->mutex); | |
+ err = | |
+ kionix_i2c_read(acceld->client, | |
+ ACCEL_GRP4_XOUT_L, (u8 *) | |
+ accel_data.accel_data_s16, | |
+ 6); | |
+ mutex_unlock(&input_dev->mutex); | |
+ if (err < 0) { | |
+ loop--; | |
+ msleep(KIONIX_I2C_RETRY_TIMEOUT); | |
+ } else | |
+ loop = 0; | |
+ } | |
+ if (err < 0) { | |
+ KMSGERR(&acceld->client->dev, | |
+ "%s: read data output error = %d\n", | |
+ __func__, err); | |
+ } else { | |
+ | |
+ write_lock(&acceld->rwlock_accel_data); | |
+ | |
+ x = ((s16) | |
+ le16_to_cpu(accel_data.accel_data_s16 | |
+ [acceld->axis_map_x])) >> | |
+ acceld->shift; | |
+ y = ((s16) | |
+ le16_to_cpu(accel_data.accel_data_s16 | |
+ [acceld->axis_map_y])) >> | |
+ acceld->shift; | |
+ z = ((s16) | |
+ le16_to_cpu(accel_data.accel_data_s16 | |
+ [acceld->axis_map_z])) >> | |
+ acceld->shift; | |
+ | |
+ acceld->accel_data[acceld->axis_map_x] = | |
+ (acceld->negate_x ? -x : x) + | |
+ acceld->accel_cali[acceld->axis_map_x]; | |
+ acceld->accel_data[acceld->axis_map_y] = | |
+ (acceld->negate_y ? -y : y) + | |
+ acceld->accel_cali[acceld->axis_map_y]; | |
+ acceld->accel_data[acceld->axis_map_z] = | |
+ (acceld->negate_z ? -z : z) + | |
+ acceld->accel_cali[acceld->axis_map_z]; | |
+ | |
+ if (atomic_read(&acceld->accel_input_event) | |
+ > 0) { | |
+ input_report_abs(acceld->input_dev, | |
+ ABS_X, | |
+ acceld->accel_data | |
+ [acceld->axis_map_x]<<acceld->shift); | |
+ input_report_abs(acceld->input_dev, | |
+ ABS_Y, | |
+ acceld->accel_data | |
+ [acceld->axis_map_y]<<acceld->shift); | |
+ input_report_abs(acceld->input_dev, | |
+ ABS_Z, | |
+ acceld->accel_data | |
+ [acceld->axis_map_z]<<acceld->shift); | |
+ input_sync(acceld->input_dev); | |
+ } | |
+ write_unlock(&acceld->rwlock_accel_data); | |
+ } | |
+ } else { | |
+ atomic_inc(&acceld->accel_enable_resume); | |
+ } | |
+ } | |
+ | |
+ /* Clear the interrupt if using drdy */ | |
+ if (acceld->accel_drdy == 1) { | |
+ | |
+ loop = KIONIX_I2C_RETRY_COUNT; | |
+ while (loop) { | |
+ err = | |
+ i2c_smbus_read_byte_data(acceld->client, | |
+ ACCEL_GRP4_INT_REL); | |
+ if (err < 0) { | |
+ loop--; | |
+ msleep(KIONIX_I2C_RETRY_TIMEOUT); | |
+ } else | |
+ loop = 0; | |
+ } | |
+ if (err < 0) | |
+ KMSGERR(&acceld->client->dev, | |
+ "%s: clear interrupt error = %d\n", __func__, | |
+ err); | |
+ } | |
+} | |
+ | |
+static void kionix_accel_grp4_update_g_range(struct kionix_accel_driver *acceld) | |
+{ | |
+ acceld->accel_registers[accel_grp4_ctrl_reg1] &= ~ACCEL_GRP4_G_MASK; | |
+ | |
+ switch (acceld->accel_pdata.accel_g_range) { | |
+ case KIONIX_ACCEL_G_8G: | |
+ case KIONIX_ACCEL_G_6G: | |
+ acceld->shift = 2; | |
+ acceld->accel_registers[accel_grp4_ctrl_reg1] |= | |
+ ACCEL_GRP4_G_8G; | |
+ break; | |
+ case KIONIX_ACCEL_G_4G: | |
+ acceld->shift = 3; | |
+ acceld->accel_registers[accel_grp4_ctrl_reg1] |= | |
+ ACCEL_GRP4_G_4G; | |
+ break; | |
+ case KIONIX_ACCEL_G_2G: | |
+ default: | |
+ acceld->shift = 4; | |
+ acceld->accel_registers[accel_grp4_ctrl_reg1] |= | |
+ ACCEL_GRP4_G_2G; | |
+ break; | |
+ } | |
+ | |
+ return; | |
+} | |
+ | |
+static int kionix_accel_grp4_update_odr(struct kionix_accel_driver *acceld, | |
+ unsigned int poll_interval) | |
+{ | |
+ int err; | |
+ int i; | |
+ u8 odr; | |
+ | |
+ /* Use the lowest ODR that can support the requested poll interval */ | |
+ for (i = 0; i < ARRAY_SIZE(kionix_accel_grp4_odr_table); i++) { | |
+ odr = kionix_accel_grp4_odr_table[i].mask; | |
+ if (poll_interval < kionix_accel_grp4_odr_table[i].cutoff) | |
+ break; | |
+ } | |
+ | |
+ if (acceld->accel_registers[accel_grp4_data_ctrl] == odr) | |
+ return 0; | |
+ else | |
+ acceld->accel_registers[accel_grp4_data_ctrl] = odr; | |
+ | |
+ if (atomic_read(&acceld->accel_enabled) > 0) { | |
+ err = | |
+ i2c_smbus_write_byte_data(acceld->client, | |
+ ACCEL_GRP4_CTRL_REG1, 0); | |
+ if (err < 0) | |
+ return err; | |
+ | |
+ err = | |
+ i2c_smbus_write_byte_data(acceld->client, | |
+ ACCEL_GRP4_DATA_CTRL, | |
+ acceld->accel_registers | |
+ [accel_grp4_data_ctrl]); | |
+ if (err < 0) | |
+ return err; | |
+ | |
+ err = | |
+ i2c_smbus_write_byte_data(acceld->client, | |
+ ACCEL_GRP4_CTRL_REG1, | |
+ acceld->accel_registers | |
+ [accel_grp4_ctrl_reg1] | | |
+ ACCEL_GRP4_PC1_ON); | |
+ if (err < 0) | |
+ return err; | |
+ | |
+ err = | |
+ i2c_smbus_read_byte_data(acceld->client, | |
+ ACCEL_GRP4_DATA_CTRL); | |
+ if (err < 0) | |
+ return err; | |
+ switch (err) { | |
+ case ACCEL_GRP4_ODR0_781: | |
+ dev_info(&acceld->client->dev, "ODR = 0.781 Hz\n"); | |
+ break; | |
+ case ACCEL_GRP4_ODR1_563: | |
+ dev_info(&acceld->client->dev, "ODR = 1.563 Hz\n"); | |
+ break; | |
+ case ACCEL_GRP4_ODR3_125: | |
+ dev_info(&acceld->client->dev, "ODR = 3.125 Hz\n"); | |
+ break; | |
+ case ACCEL_GRP4_ODR6_25: | |
+ dev_info(&acceld->client->dev, "ODR = 6.25 Hz\n"); | |
+ break; | |
+ case ACCEL_GRP4_ODR12_5: | |
+ dev_info(&acceld->client->dev, "ODR = 12.5 Hz\n"); | |
+ break; | |
+ case ACCEL_GRP4_ODR25: | |
+ dev_info(&acceld->client->dev, "ODR = 25 Hz\n"); | |
+ break; | |
+ case ACCEL_GRP4_ODR50: | |
+ dev_info(&acceld->client->dev, "ODR = 50 Hz\n"); | |
+ break; | |
+ case ACCEL_GRP4_ODR100: | |
+ dev_info(&acceld->client->dev, "ODR = 100 Hz\n"); | |
+ break; | |
+ case ACCEL_GRP4_ODR200: | |
+ dev_info(&acceld->client->dev, "ODR = 200 Hz\n"); | |
+ break; | |
+ case ACCEL_GRP4_ODR400: | |
+ dev_info(&acceld->client->dev, "ODR = 400 Hz\n"); | |
+ break; | |
+ case ACCEL_GRP4_ODR800: | |
+ dev_info(&acceld->client->dev, "ODR = 800 Hz\n"); | |
+ break; | |
+ case ACCEL_GRP4_ODR1600: | |
+ dev_info(&acceld->client->dev, "ODR = 1600 Hz\n"); | |
+ break; | |
+ default: | |
+ dev_info(&acceld->client->dev, "Unknown ODR\n"); | |
+ break; | |
+ } | |
+ } | |
+ | |
+ return 0; | |
+} | |
+/* | |
+static int kionix_accel_power_on(struct kionix_accel_driver *acceld) | |
+{ | |
+ if (acceld->accel_pdata.power_on) | |
+ return acceld->accel_pdata.power_on(); | |
+ return 0; | |
+} | |
+*/ | |
+static void kionix_accel_power_off(struct kionix_accel_driver *acceld) | |
+{ | |
+ if (acceld->accel_pdata.power_off) | |
+ acceld->accel_pdata.power_off(); | |
+} | |
+ | |
+static irqreturn_t kionix_accel_isr(int irq, void *dev) | |
+{ | |
+ struct kionix_accel_driver *acceld = dev; | |
+ | |
+ queue_delayed_work(acceld->accel_workqueue, &acceld->accel_work, 0); | |
+ | |
+ return IRQ_HANDLED; | |
+} | |
+ | |
+static void kionix_accel_work(struct work_struct *work) | |
+{ | |
+ struct kionix_accel_driver *acceld = | |
+ container_of((struct delayed_work *)work, | |
+ struct kionix_accel_driver, accel_work); | |
+ | |
+ if (acceld->accel_drdy == 0) | |
+ queue_delayed_work(acceld->accel_workqueue, &acceld->accel_work, | |
+ msecs_to_jiffies(atomic_read | |
+ (&acceld->delay))); | |
+ acceld->kionix_accel_report_accel_data(acceld); | |
+} | |
+ | |
+static void kionix_accel_update_direction(struct kionix_accel_driver *acceld) | |
+{ | |
+ unsigned int direction = acceld->accel_pdata.accel_direction; | |
+ unsigned int accel_group = acceld->accel_group; | |
+ | |
+ write_lock(&acceld->rwlock_accel_data); | |
+ acceld->axis_map_x = ((direction - 1) % 2); | |
+ acceld->axis_map_y = (direction % 2); | |
+ acceld->axis_map_z = 2; | |
+ acceld->negate_z = ((direction - 1) / 4); | |
+ switch (accel_group) { | |
+ case KIONIX_ACCEL_GRP3: | |
+ case KIONIX_ACCEL_GRP6: | |
+ acceld->negate_x = (((direction + 2) / 2) % 2); | |
+ acceld->negate_y = (((direction + 5) / 4) % 2); | |
+ break; | |
+ case KIONIX_ACCEL_GRP5: | |
+ acceld->axis_map_x = (direction % 2); | |
+ acceld->axis_map_y = ((direction - 1) % 2); | |
+ acceld->negate_x = (((direction + 1) / 2) % 2); | |
+ acceld->negate_y = | |
+ (((direction / 2) + ((direction - 1) / 4)) % 2); | |
+ break; | |
+ default: | |
+ acceld->negate_x = ((direction / 2) % 2); | |
+ acceld->negate_y = (((direction + 1) / 4) % 2); | |
+ break; | |
+ } | |
+ write_unlock(&acceld->rwlock_accel_data); | |
+ return; | |
+} | |
+ | |
+static int kionix_accel_enable(struct kionix_accel_driver *acceld) | |
+{ | |
+ int err = 0; | |
+ long remaining; | |
+ struct kionix_accel_platform_data pdata = acceld->accel_pdata; | |
+ | |
+ if (pdata.acc_power_on) | |
+ pdata.acc_power_on(true); | |
+ | |
+ mutex_lock(&acceld->mutex_earlysuspend); | |
+ | |
+ atomic_set(&acceld->accel_suspend_continue, 0); | |
+ | |
+ if (atomic_read(&acceld->accel_suspended) == 1) { | |
+ KMSGINF(&acceld->client->dev, "%s: waiting for resume\n", | |
+ __func__); | |
+ remaining = | |
+ wait_event_interruptible_timeout(acceld->wqh_suspend, | |
+ atomic_read(&acceld->accel_suspended) == 0, | |
+ msecs_to_jiffies | |
+ (KIONIX_ACCEL_EARLYSUSPEND_TIMEOUT)); | |
+ | |
+ if (atomic_read(&acceld->accel_suspended) == 1) { | |
+ KMSGERR(&acceld->client->dev, | |
+ "%s: timeout waiting for resume\n", __func__); | |
+ err = -ETIME; | |
+ goto exit; | |
+ } | |
+ } | |
+ | |
+ err = acceld->kionix_accel_operate(acceld); | |
+ | |
+ if (err < 0) { | |
+ KMSGERR(&acceld->client->dev, | |
+ "%s: kionix_accel_operate returned err = %d\n", | |
+ __func__, err); | |
+ goto exit; | |
+ } | |
+ | |
+ /* atomic_inc(&acceld->accel_enabled); */ | |
+ | |
+exit: | |
+ mutex_unlock(&acceld->mutex_earlysuspend); | |
+ pr_info("%s: ++enable_sensor\n", __func__); | |
+ return err; | |
+} | |
+ | |
+static int kionix_accel_disable(struct kionix_accel_driver *acceld) | |
+{ | |
+ int err = 0; | |
+ struct kionix_accel_platform_data pdata = acceld->accel_pdata; | |
+ | |
+ mutex_lock(&acceld->mutex_resume); | |
+ | |
+ atomic_set(&acceld->accel_suspend_continue, 1); | |
+ | |
+ if (atomic_read(&acceld->accel_enabled) > 0) { | |
+ if (atomic_dec_and_test(&acceld->accel_enabled)) { | |
+ if (atomic_read(&acceld->accel_enable_resume) > 0) | |
+ atomic_set(&acceld->accel_enable_resume, 0); | |
+ err = acceld->kionix_accel_standby(acceld); | |
+ if (err < 0) { | |
+ KMSGERR(&acceld->client->dev, | |
+ "%s: kionix_accel_standby returned err = %d\n", | |
+ __func__, err); | |
+ goto exit; | |
+ } | |
+ wake_up_interruptible(&acceld->wqh_suspend); | |
+ } | |
+ } | |
+ | |
+ /* Vote off regulators */ | |
+ if (pdata.acc_power_on) | |
+ pdata.acc_power_on(false); | |
+ | |
+exit: | |
+ mutex_unlock(&acceld->mutex_resume); | |
+ | |
+ return err; | |
+} | |
+ | |
+static int kionix_accel_input_open(struct input_dev *input) | |
+{ | |
+ struct kionix_accel_driver *acceld = input_get_drvdata(input); | |
+ | |
+ atomic_inc(&acceld->accel_input_event); | |
+ | |
+ return 0; | |
+} | |
+ | |
+static void kionix_accel_input_close(struct input_dev *dev) | |
+{ | |
+ struct kionix_accel_driver *acceld = input_get_drvdata(dev); | |
+ | |
+ atomic_dec(&acceld->accel_input_event); | |
+} | |
+ | |
+static void kionix_accel_init_input_device(struct kionix_accel_driver *acceld, | |
+ struct input_dev *input_dev) | |
+{ | |
+ __set_bit(EV_ABS, input_dev->evbit); | |
+ input_set_abs_params(input_dev, ABS_X, -ACCEL_G_MAX, ACCEL_G_MAX, | |
+ ACCEL_FUZZ, ACCEL_FLAT); | |
+ input_set_abs_params(input_dev, ABS_Y, -ACCEL_G_MAX, ACCEL_G_MAX, | |
+ ACCEL_FUZZ, ACCEL_FLAT); | |
+ input_set_abs_params(input_dev, ABS_Z, -ACCEL_G_MAX, ACCEL_G_MAX, | |
+ ACCEL_FUZZ, ACCEL_FLAT); | |
+ | |
+ input_dev->name = KIONIX_ACCEL_NAME; | |
+ input_dev->id.bustype = BUS_I2C; | |
+ /*input_dev->dev.parent = &acceld->client->dev; */ | |
+} | |
+ | |
+static int kionix_accel_setup_input_device(struct kionix_accel_driver *acceld) | |
+{ | |
+ struct input_dev *input_dev; | |
+ int err; | |
+ | |
+ input_dev = input_allocate_device(); | |
+ if (!input_dev) { | |
+ KMSGERR(&acceld->client->dev, "input_allocate_device failed\n"); | |
+ return -ENOMEM; | |
+ } | |
+ | |
+ acceld->input_dev = input_dev; | |
+ | |
+ input_dev->open = kionix_accel_input_open; | |
+ input_dev->close = kionix_accel_input_close; | |
+ input_set_drvdata(input_dev, acceld); | |
+ | |
+ kionix_accel_init_input_device(acceld, input_dev); | |
+ | |
+ err = input_register_device(acceld->input_dev); | |
+ if (err) { | |
+ KMSGERR(&acceld->client->dev, | |
+ "%s: input_register_device returned err = %d\n", | |
+ __func__, err); | |
+ input_free_device(acceld->input_dev); | |
+ return err; | |
+ } | |
+ | |
+ return 0; | |
+} | |
+ | |
+/* Returns the enable state of device */ | |
+static ssize_t kionix_accel_get_enable(struct device *dev, | |
+ struct device_attribute *attr, char *buf) | |
+{ | |
+ struct i2c_client *client = to_i2c_client(dev); | |
+ struct kionix_accel_driver *acceld = i2c_get_clientdata(client); | |
+ size_t size = sizeof(buf); | |
+ return snprintf(buf, size, "%d\n", | |
+ atomic_read(&acceld->accel_enabled) > 0 ? 1 : 0); | |
+} | |
+ | |
+/* Allow users to enable/disable the device */ | |
+static ssize_t kionix_accel_set_enable(struct device *dev, | |
+ struct device_attribute *attr, | |
+ const char *buf, size_t count) | |
+{ | |
+ struct i2c_client *client = to_i2c_client(dev); | |
+ struct kionix_accel_driver *acceld = i2c_get_clientdata(client); | |
+ struct input_dev *input_dev = acceld->input_dev; | |
+ char *buf2; | |
+ const int enable_count = 1; | |
+ unsigned long enable; | |
+ int err = 0; | |
+ | |
+ /* Lock the device to prevent races with open/close (and itself) */ | |
+ mutex_lock(&input_dev->mutex); | |
+ | |
+ if (kionix_strtok(buf, count, &buf2, enable_count) < 0) { | |
+ KMSGERR(&acceld->client->dev, | |
+ "%s: No enable data being read. " | |
+ "No enable data will be updated.\n", __func__); | |
+ } else { | |
+ /* Removes any leading negative sign */ | |
+ while (*buf2 == '-') | |
+ buf2++; | |
+ | |
+ err = | |
+ kstrtouint((const char *)buf2, 10, (unsigned int *)&enable); | |
+ if (err < 0) { | |
+ KMSGERR(&acceld->client->dev, | |
+ "%s: kstrtouint returned err = %d\n", __func__, | |
+ err); | |
+ goto exit; | |
+ } | |
+ | |
+ if (enable) | |
+ err = kionix_accel_enable(acceld); | |
+ else | |
+ err = kionix_accel_disable(acceld); | |
+ | |
+ } | |
+ | |
+exit: | |
+ mutex_unlock(&input_dev->mutex); | |
+ | |
+ return (err < 0) ? err : count; | |
+} | |
+ | |
+/* Returns currently selected poll interval (in ms) */ | |
+static ssize_t kionix_accel_get_delay(struct device *dev, | |
+ struct device_attribute *attr, char *buf) | |
+{ | |
+ struct i2c_client *client = to_i2c_client(dev); | |
+ struct kionix_accel_driver *acceld = i2c_get_clientdata(client); | |
+ size_t size = sizeof(buf); | |
+ return snprintf(buf, size, "%d\n", acceld->poll_interval); | |
+} | |
+ | |
+/* Allow users to select a new poll interval (in ms) */ | |
+static ssize_t kionix_accel_set_delay(struct device *dev, | |
+ struct device_attribute *attr, | |
+ const char *buf, size_t count) | |
+{ | |
+ struct i2c_client *client = to_i2c_client(dev); | |
+ struct kionix_accel_driver *acceld = i2c_get_clientdata(client); | |
+ struct input_dev *input_dev = acceld->input_dev; | |
+ char *buf2; | |
+ const int delay_count = 1; | |
+ unsigned long interval; | |
+ int err = 0; | |
+ | |
+ /* Lock the device to prevent races with open/close (and itself) */ | |
+ mutex_lock(&input_dev->mutex); | |
+ | |
+ if (kionix_strtok(buf, count, &buf2, delay_count) < 0) { | |
+ KMSGERR(&acceld->client->dev, | |
+ "%s: No delay data being read. " | |
+ "No delay data will be updated.\n", __func__); | |
+ } | |
+ | |
+ else { | |
+ /* Removes any leading negative sign */ | |
+ while (*buf2 == '-') | |
+ buf2++; | |
+ | |
+ err = | |
+ kstrtouint((const char *)buf2, 10, | |
+ (unsigned int *)&interval); | |
+ if (err < 0) { | |
+ KMSGERR(&acceld->client->dev, | |
+ "%s: kstrtouint returned err = %d\n", __func__, | |
+ err); | |
+ goto exit; | |
+ } | |
+ | |
+ if (acceld->accel_drdy == 1) | |
+ disable_irq(client->irq); | |
+ | |
+ /* | |
+ * Set current interval to the greater of | |
+ * the minimum interval or | |
+ * the requested interval | |
+ */ | |
+ acceld->poll_interval = | |
+ max((unsigned int)interval, | |
+ acceld->accel_pdata.min_interval); | |
+ acceld->poll_delay = msecs_to_jiffies(acceld->poll_interval); | |
+ | |
+ err = | |
+ acceld->kionix_accel_update_odr(acceld, | |
+ acceld->poll_interval); | |
+ | |
+ if (acceld->accel_drdy == 1) | |
+ enable_irq(client->irq); | |
+ } | |
+ | |
+exit: | |
+ mutex_unlock(&input_dev->mutex); | |
+ | |
+ return (err < 0) ? err : count; | |
+} | |
+ | |
+/* Returns the direction of device */ | |
+static ssize_t kionix_accel_get_direct(struct device *dev, | |
+ struct device_attribute *attr, char *buf) | |
+{ | |
+ struct i2c_client *client = to_i2c_client(dev); | |
+ struct kionix_accel_driver *acceld = i2c_get_clientdata(client); | |
+ size_t size = sizeof(buf); | |
+ return snprintf(buf, size, "%d\n", | |
+ acceld->accel_pdata.accel_direction); | |
+} | |
+ | |
+/* Allow users to change the direction the device */ | |
+static ssize_t kionix_accel_set_direct(struct device *dev, | |
+ struct device_attribute *attr, | |
+ const char *buf, size_t count) | |
+{ | |
+ struct i2c_client *client = to_i2c_client(dev); | |
+ struct kionix_accel_driver *acceld = i2c_get_clientdata(client); | |
+ struct input_dev *input_dev = acceld->input_dev; | |
+ char *buf2; | |
+ const int direct_count = 1; | |
+ unsigned long direction; | |
+ int err = 0; | |
+ | |
+ /* Lock the device to prevent races with open/close (and itself) */ | |
+ mutex_lock(&input_dev->mutex); | |
+ | |
+ if (kionix_strtok(buf, count, &buf2, direct_count) < 0) { | |
+ KMSGERR(&acceld->client->dev, | |
+ "%s: No direction data being read. " | |
+ "No direction data will be updated.\n", __func__); | |
+ } | |
+ | |
+ else { | |
+ /* Removes any leading negative sign */ | |
+ while (*buf2 == '-') | |
+ buf2++; | |
+ | |
+ err = | |
+ kstrtouint((const char *)buf2, 10, | |
+ (unsigned int *)&direction); | |
+ if (err < 0) { | |
+ KMSGERR(&acceld->client->dev, | |
+ "%s: kstrtouint returned err = %d\n", __func__, | |
+ err); | |
+ goto exit; | |
+ } | |
+ | |
+ if (direction < 1 || direction > 8) | |
+ KMSGERR(&acceld->client->dev, | |
+ "%s: invalid direction = %d\n", __func__, | |
+ (unsigned int)direction); | |
+ | |
+ else { | |
+ acceld->accel_pdata.accel_direction = (u8) direction; | |
+ kionix_accel_update_direction(acceld); | |
+ } | |
+ } | |
+ | |
+exit: | |
+ mutex_unlock(&input_dev->mutex); | |
+ | |
+ return (err < 0) ? err : count; | |
+} | |
+ | |
+/* Returns the data output of device */ | |
+static ssize_t kionix_accel_get_data(struct device *dev, | |
+ struct device_attribute *attr, char *buf) | |
+{ | |
+ struct i2c_client *client = to_i2c_client(dev); | |
+ struct kionix_accel_driver *acceld = i2c_get_clientdata(client); | |
+ int x, y, z; | |
+ size_t size = sizeof(buf); | |
+ read_lock(&acceld->rwlock_accel_data); | |
+ | |
+ x = acceld->accel_data[acceld->axis_map_x]; | |
+ y = acceld->accel_data[acceld->axis_map_y]; | |
+ z = acceld->accel_data[acceld->axis_map_z]; | |
+ | |
+ read_unlock(&acceld->rwlock_accel_data); | |
+ | |
+ return snprintf(buf, size, "%d %d %d\n", x, y, z); | |
+} | |
+ | |
+/* Returns the calibration value of the device */ | |
+static ssize_t kionix_accel_get_cali(struct device *dev, | |
+ struct device_attribute *attr, char *buf) | |
+{ | |
+ struct i2c_client *client = to_i2c_client(dev); | |
+ struct kionix_accel_driver *acceld = i2c_get_clientdata(client); | |
+ int calibration[3]; | |
+ size_t size = sizeof(buf); | |
+ read_lock(&acceld->rwlock_accel_data); | |
+ | |
+ calibration[0] = acceld->accel_cali[acceld->axis_map_x]; | |
+ calibration[1] = acceld->accel_cali[acceld->axis_map_y]; | |
+ calibration[2] = acceld->accel_cali[acceld->axis_map_z]; | |
+ | |
+ read_unlock(&acceld->rwlock_accel_data); | |
+ | |
+ return snprintf(buf, size, "%d %d %d\n", calibration[0], | |
+ calibration[1], | |
+ calibration[2]); | |
+} | |
+ | |
+/* Allow users to change the calibration value of the device */ | |
+static ssize_t kionix_accel_set_cali(struct device *dev, | |
+ struct device_attribute *attr, | |
+ const char *buf, size_t count) | |
+{ | |
+ struct i2c_client *client = to_i2c_client(dev); | |
+ struct kionix_accel_driver *acceld = i2c_get_clientdata(client); | |
+ struct input_dev *input_dev = acceld->input_dev; | |
+/* How many calibration that we expect to get from the string */ | |
+ const int cali_count = 3; | |
+ char **buf2; | |
+ long calibration[cali_count]; | |
+ int err = 0, i = 0; | |
+ | |
+ /* Lock the device to prevent races with open/close (and itself) */ | |
+ mutex_lock(&input_dev->mutex); | |
+ | |
+ buf2 = kzalloc(cali_count * sizeof(char *), GFP_KERNEL); | |
+ | |
+ if (kionix_strtok(buf, count, buf2, cali_count) < 0) { | |
+ KMSGERR(&acceld->client->dev, | |
+ "%s: Not enough calibration data being read. " | |
+ "No calibration data will be updated.\n", __func__); | |
+ } else { | |
+ /* Convert string to integers */ | |
+ for (i = 0; i < cali_count; i++) { | |
+ | |
+ err = | |
+ kstrtoint((const char *)*(buf2 + i), 10, | |
+ (int *)&calibration[i]); | |
+ if (err < 0) { | |
+ KMSGERR(&acceld->client->dev, | |
+ "%s: kstrtoint returned err = %d." | |
+ "No calibration data will be updated.\n", | |
+ __func__, err); | |
+ goto exit; | |
+ } | |
+ } | |
+ | |
+ write_lock(&acceld->rwlock_accel_data); | |
+ | |
+ acceld->accel_cali[acceld->axis_map_x] = (int)calibration[0]; | |
+ acceld->accel_cali[acceld->axis_map_y] = (int)calibration[1]; | |
+ acceld->accel_cali[acceld->axis_map_z] = (int)calibration[2]; | |
+ | |
+ write_unlock(&acceld->rwlock_accel_data); | |
+ } | |
+ | |
+exit: | |
+ for (i = 0; i < cali_count; i++) | |
+ kfree(*(buf2 + i)); | |
+ | |
+ kfree(buf2); | |
+ | |
+ mutex_unlock(&input_dev->mutex); | |
+ | |
+ return (err < 0) ? err : count; | |
+} | |
+ | |
+static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR | S_IWGRP | S_IWOTH, | |
+ kionix_accel_get_enable, kionix_accel_set_enable); | |
+static DEVICE_ATTR(poll_delay, S_IRUGO | S_IWUSR | S_IWGRP | S_IWOTH, | |
+ kionix_accel_get_delay, kionix_accel_set_delay); | |
+static DEVICE_ATTR(direct, S_IRUGO | S_IWUSR | S_IWGRP | S_IWOTH, | |
+ kionix_accel_get_direct, kionix_accel_set_direct); | |
+static DEVICE_ATTR(data, S_IRUGO, kionix_accel_get_data, NULL); | |
+static DEVICE_ATTR(cali, S_IRUGO | S_IWUSR | S_IWGRP | S_IWOTH, | |
+ kionix_accel_get_cali, kionix_accel_set_cali); | |
+ | |
+static struct attribute *kionix_accel_attributes[] = { | |
+ &dev_attr_enable.attr, | |
+ &dev_attr_poll_delay.attr, | |
+ &dev_attr_direct.attr, | |
+ &dev_attr_data.attr, | |
+ &dev_attr_cali.attr, | |
+ NULL | |
+}; | |
+ | |
+static struct attribute_group kionix_accel_attribute_group = { | |
+ .attrs = kionix_accel_attributes | |
+}; | |
+ | |
+static int kionix_verify(struct kionix_accel_driver *acceld) | |
+{ | |
+ int retval = i2c_smbus_read_byte_data(acceld->client, ACCEL_WHO_AM_I); | |
+ pr_info("%s: devices id is %d\n", __func__, retval); | |
+#if KIONIX_KMSG_INF | |
+ switch (retval) { | |
+ case KIONIX_ACCEL_WHO_AM_I_KXTE9: | |
+ KMSGINF(&acceld->client->dev, | |
+ "this accelerometer is a KXTE9.\n"); | |
+ break; | |
+ case KIONIX_ACCEL_WHO_AM_I_KXTF9: | |
+ KMSGINF(&acceld->client->dev, | |
+ "this accelerometer is a KXTF9.\n"); | |
+ break; | |
+ case KIONIX_ACCEL_WHO_AM_I_KXTI9_1001: | |
+ KMSGINF(&acceld->client->dev, | |
+ "this accelerometer is a KXTI9-1001.\n"); | |
+ break; | |
+ case KIONIX_ACCEL_WHO_AM_I_KXTIK_1004: | |
+ KMSGINF(&acceld->client->dev, | |
+ "this accelerometer is a KXTIK-1004.\n"); | |
+ break; | |
+ case KIONIX_ACCEL_WHO_AM_I_KXTJ9_1005: | |
+ KMSGINF(&acceld->client->dev, | |
+ "this accelerometer is a KXTJ9-1005.\n"); | |
+ break; | |
+ case KIONIX_ACCEL_WHO_AM_I_KXTJ9_1007: | |
+ KMSGINF(&acceld->client->dev, | |
+ "this accelerometer is a KXTJ9-1007.\n"); | |
+ break; | |
+ case KIONIX_ACCEL_WHO_AM_I_KXCJ9_1008: | |
+ KMSGINF(&acceld->client->dev, | |
+ "this accelerometer is a KXCJ9-1008.\n"); | |
+ break; | |
+ case KIONIX_ACCEL_WHO_AM_I_KXTJ2_1009: | |
+ KMSGINF(&acceld->client->dev, | |
+ "this accelerometer is a KXTJ2-1009.\n"); | |
+ break; | |
+ case KIONIX_ACCEL_WHO_AM_I_KXCJK_1013: | |
+ KMSGINF(&acceld->client->dev, | |
+ "this accelerometer is a KXCJK-1013.\n"); | |
+ break; | |
+ default: | |
+ break; | |
+ } | |
+#endif | |
+ | |
+ return retval; | |
+} | |
+ | |
+#ifdef CONFIG_HAS_EARLYSUSPEND | |
+void kionix_accel_earlysuspend_suspend(struct early_suspend *h) | |
+{ | |
+ struct kionix_accel_driver *acceld = | |
+ container_of(h, struct kionix_accel_driver, early_suspend); | |
+ long remaining; | |
+ | |
+ mutex_lock(&acceld->mutex_earlysuspend); | |
+ | |
+ /* Only continue to suspend if enable did not intervene */ | |
+ if (atomic_read(&acceld->accel_suspend_continue) > 0) { | |
+ if (atomic_read(&acceld->accel_enabled) > 0) { | |
+ KMSGINF(&acceld->client->dev, | |
+ "%s: waiting for disable\n", __func__); | |
+ remaining = | |
+ wait_event_interruptible_timeout | |
+ (acceld->wqh_suspend, | |
+ atomic_read(&acceld->accel_enabled) < 1, | |
+ msecs_to_jiffies | |
+ (KIONIX_ACCEL_EARLYSUSPEND_TIMEOUT)); | |
+ | |
+ if (atomic_read(&acceld->accel_enabled) > 0) | |
+ KMSGERR(&acceld->client->dev, | |
+ "%s: timeout waiting for disable\n", | |
+ __func__); | |
+ } | |
+ | |
+ kionix_accel_power_off(acceld); | |
+ | |
+ atomic_set(&acceld->accel_suspended, 1); | |
+ } | |
+ | |
+ mutex_unlock(&acceld->mutex_earlysuspend); | |
+ | |
+ return; | |
+} | |
+ | |
+void kionix_accel_earlysuspend_resume(struct early_suspend *h) | |
+{ | |
+ struct kionix_accel_driver *acceld = | |
+ container_of(h, struct kionix_accel_driver, early_suspend); | |
+ int err; | |
+ | |
+ mutex_lock(&acceld->mutex_resume); | |
+ | |
+ if (atomic_read(&acceld->accel_suspended) == 1) { | |
+ err = kionix_accel_power_on(acceld); | |
+ if (err < 0) { | |
+ KMSGERR(&acceld->client->dev, | |
+ "%s: kionix_accel_power_on returned err = %d\n", | |
+ __func__, err); | |
+ goto exit; | |
+ } | |
+ | |
+ if (err > 0) { | |
+ err = acceld->kionix_accel_power_on_init(acceld); | |
+ if (err) { | |
+ KMSGERR(&acceld->client->dev, | |
+ "%s: kionix_accel_power_on_init returned err = %d\n", | |
+ __func__, err); | |
+ goto exit; | |
+ } | |
+ } | |
+ | |
+ atomic_set(&acceld->accel_suspended, 0); | |
+ } | |
+ | |
+ wake_up_interruptible(&acceld->wqh_suspend); | |
+ | |
+exit: | |
+ mutex_unlock(&acceld->mutex_resume); | |
+ | |
+ return; | |
+} | |
+#endif /* CONFIG_HAS_EARLYSUSPEND */ | |
+ | |
+static int kionix_accel_regulator_configure( | |
+ struct kionix_accel_driver *data, bool on) | |
+{ | |
+ int rc; | |
+ | |
+ if (!on) { | |
+ | |
+ if (regulator_count_voltages(data->vdd) > 0) | |
+ regulator_set_voltage(data->vdd, 0, | |
+ KIONIX_VDD_MAX_UV); | |
+ | |
+ regulator_put(data->vdd); | |
+ | |
+ if (regulator_count_voltages(data->vio) > 0) | |
+ regulator_set_voltage(data->vio, 0, | |
+ KIONIX_VIO_MAX_UV); | |
+ | |
+ regulator_put(data->vio); | |
+ } else { | |
+ data->vdd = regulator_get(&data->client->dev, "vdd"); | |
+ if (IS_ERR(data->vdd)) { | |
+ rc = PTR_ERR(data->vdd); | |
+ dev_err(&data->client->dev, | |
+ "Regulator get failed vdd rc=%d\n", rc); | |
+ return rc; | |
+ } | |
+ | |
+ if (regulator_count_voltages(data->vdd) > 0) { | |
+ rc = regulator_set_voltage(data->vdd, | |
+ KIONIX_VDD_MIN_UV, KIONIX_VDD_MAX_UV); | |
+ if (rc) { | |
+ dev_err(&data->client->dev, | |
+ "Regulator set failed vdd rc=%d\n", | |
+ rc); | |
+ goto reg_vdd_put; | |
+ } | |
+ } | |
+ | |
+ data->vio = regulator_get(&data->client->dev, "vio"); | |
+ if (IS_ERR(data->vio)) { | |
+ rc = PTR_ERR(data->vio); | |
+ dev_err(&data->client->dev, | |
+ "Regulator get failed vio rc=%d\n", rc); | |
+ goto reg_vdd_set; | |
+ } | |
+ | |
+ if (regulator_count_voltages(data->vio) > 0) { | |
+ rc = regulator_set_voltage(data->vio, | |
+ KIONIX_VIO_MIN_UV, KIONIX_VIO_MAX_UV); | |
+ if (rc) { | |
+ dev_err(&data->client->dev, | |
+ "Regulator set failed vio rc=%d\n", rc); | |
+ goto reg_vio_put; | |
+ } | |
+ } | |
+ } | |
+ | |
+ return 0; | |
+reg_vio_put: | |
+ regulator_put(data->vio); | |
+ | |
+reg_vdd_set: | |
+ if (regulator_count_voltages(data->vdd) > 0) | |
+ regulator_set_voltage(data->vdd, 0, KIONIX_VDD_MAX_UV); | |
+reg_vdd_put: | |
+ regulator_put(data->vdd); | |
+ return rc; | |
+} | |
+ | |
+static int kionix_accel_regulator_power_on( | |
+ struct kionix_accel_driver *data, bool on) | |
+{ | |
+ int rc = 0; | |
+ | |
+ if (!on) { | |
+ rc = regulator_disable(data->vdd); | |
+ if (rc) { | |
+ dev_err(&data->client->dev, | |
+ "Regulator vdd disable failed rc=%d\n", rc); | |
+ return rc; | |
+ } | |
+ | |
+ rc = regulator_disable(data->vio); | |
+ if (rc) { | |
+ dev_err(&data->client->dev, | |
+ "Regulator vio disable failed rc=%d\n", rc); | |
+ rc = regulator_enable(data->vdd); | |
+ dev_err(&data->client->dev, | |
+ "Regulator vio re-enabled rc=%d\n", rc); | |
+ /* | |
+ * Successfully re-enable regulator. | |
+ * Enter poweron delay and returns error. | |
+ */ | |
+ if (!rc) { | |
+ rc = -EBUSY; | |
+ goto enable_delay; | |
+ } | |
+ } | |
+ return rc; | |
+ } else { | |
+ rc = regulator_enable(data->vdd); | |
+ if (rc) { | |
+ dev_err(&data->client->dev, | |
+ "Regulator vdd enable failed rc=%d\n", rc); | |
+ return rc; | |
+ } | |
+ | |
+ rc = regulator_enable(data->vio); | |
+ if (rc) { | |
+ dev_err(&data->client->dev, | |
+ "Regulator vio enable failed rc=%d\n", rc); | |
+ regulator_disable(data->vdd); | |
+ return rc; | |
+ } | |
+ } | |
+ | |
+enable_delay: | |
+ msleep(130); | |
+ dev_dbg(&data->client->dev, | |
+ "Sensor regulator power on =%d\n", on); | |
+ return rc; | |
+} | |
+ | |
+static int kionix_accel_platform_hw_power_on(bool on) | |
+{ | |
+ struct kionix_accel_driver *data; | |
+ int err = 0; | |
+ | |
+ if (kionix_data == NULL) | |
+ return -ENODEV; | |
+ | |
+ data = kionix_data; | |
+ if (data->power_on != on) { | |
+ err = kionix_accel_regulator_power_on(data, on); | |
+ if (err) | |
+ dev_err(&data->client->dev, | |
+ "Can't configure regulator!\n"); | |
+ else | |
+ data->power_on = on; | |
+ } | |
+ | |
+ return err; | |
+} | |
+ | |
+static int kionix_accel_platform_hw_init(void) | |
+{ | |
+ struct i2c_client *client; | |
+ struct kionix_accel_driver *data; | |
+ int error; | |
+ | |
+ if (kionix_data == NULL) | |
+ return -ENODEV; | |
+ | |
+ data = kionix_data; | |
+ client = data->client; | |
+ | |
+ error = kionix_accel_regulator_configure(data, true); | |
+ if (error < 0) { | |
+ dev_err(&client->dev, "unable to configure regulator\n"); | |
+ return error; | |
+ } | |
+ | |
+ return 0; | |
+} | |
+ | |
+static void kionix_accel_platform_hw_exit(void) | |
+{ | |
+ struct kionix_accel_driver *data = kionix_data; | |
+ | |
+ if (data == NULL) | |
+ return; | |
+ | |
+ kionix_accel_regulator_configure(data, false); | |
+ | |
+} | |
+ | |
+#ifdef CONFIG_OF | |
+static int kionix_accel_parse_dt(struct device *dev, | |
+ struct kionix_accel_platform_data *accel_pdata) | |
+{ | |
+ struct device_node *np = dev->of_node; | |
+ u32 temp_val; | |
+ int rc; | |
+ | |
+ /* set functions of platform data */ | |
+ accel_pdata->acc_init = kionix_accel_platform_hw_init; | |
+ accel_pdata->acc_exit = kionix_accel_platform_hw_exit; | |
+ accel_pdata->acc_power_on = kionix_accel_platform_hw_power_on; | |
+ | |
+ rc = of_property_read_u32(np, "kionix,min_interval", &temp_val); | |
+ if (rc && (rc != -EINVAL)) { | |
+ dev_err(dev, "Unable to read min-interval\n"); | |
+ return rc; | |
+ } else { | |
+ accel_pdata->min_interval = temp_val; | |
+ } | |
+ | |
+ rc = of_property_read_u32(np, "kionix,poll_interval", &temp_val); | |
+ if (rc && (rc != -EINVAL)) { | |
+ dev_err(dev, "Unable to read poll-interval\n"); | |
+ return rc; | |
+ } else { | |
+ accel_pdata->poll_interval = temp_val; | |
+ } | |
+ | |
+ rc = of_property_read_u32(np, "kionix,accel_direction", &temp_val); | |
+ if (rc && (rc != -EINVAL)) { | |
+ dev_err(dev, "Unable to read accel_direction\n"); | |
+ return rc; | |
+ } else { | |
+ accel_pdata->accel_direction = (u8) temp_val; | |
+ } | |
+ | |
+ rc = of_property_read_u32(np, "kionix,accel_irq_use_drdy", &temp_val); | |
+ if (rc && (rc != -EINVAL)) { | |
+ dev_err(dev, "Unable to read accel_irq_use_drdy\n"); | |
+ return rc; | |
+ } else { | |
+ accel_pdata->accel_irq_use_drdy = temp_val; | |
+ } | |
+ | |
+ rc = of_property_read_u32(np, "kionix,accel_res", &temp_val); | |
+ if (rc && (rc != -EINVAL)) { | |
+ dev_err(dev, "Unable to read accel_res\n"); | |
+ return rc; | |
+ } else { | |
+ accel_pdata->accel_res = (u8) temp_val; | |
+ } | |
+ | |
+ rc = of_property_read_u32(np, "kionix,accel_g_range", &temp_val); | |
+ if (rc && (rc != -EINVAL)) { | |
+ dev_err(dev, "Unable to read g-range\n"); | |
+ return rc; | |
+ } else { | |
+ switch (temp_val) { | |
+ case 2: | |
+ accel_pdata->accel_g_range = KIONIX_ACCEL_G_2G; | |
+ break; | |
+ case 4: | |
+ accel_pdata->accel_g_range = KIONIX_ACCEL_G_4G; | |
+ break; | |
+ case 6: | |
+ accel_pdata->accel_g_range = KIONIX_ACCEL_G_6G; | |
+ break; | |
+ case 8: | |
+ accel_pdata->accel_g_range = KIONIX_ACCEL_G_8G; | |
+ break; | |
+ default: | |
+ accel_pdata->accel_g_range = KIONIX_ACCEL_G_2G; | |
+ break; | |
+ } | |
+ } | |
+ | |
+ return 0; | |
+} | |
+#else | |
+static int kionix_accel_parse_dt(struct device *dev, | |
+ struct kionix_accel_platform_data *accel_pdata) | |
+{ | |
+ return -ENODEV; | |
+} | |
+#endif /* !CONFIG_OF */ | |
+ | |
+static int kionix_accel_cdev_enable(struct sensors_classdev *sensors_cdev, | |
+ unsigned int enable) | |
+{ | |
+ struct kionix_accel_driver *acceld = container_of(sensors_cdev, | |
+ struct | |
+ kionix_accel_driver, | |
+ cdev); | |
+ int pre_enable = atomic_read(&acceld->accel_enabled); | |
+ | |
+ if (enable) { | |
+ if (pre_enable == 0) { | |
+ kionix_accel_enable(acceld); | |
+ atomic_set(&acceld->accel_enabled, 1); | |
+ } | |
+ } else { | |
+ if (pre_enable == 1) { | |
+ kionix_accel_disable(acceld); | |
+ atomic_set(&acceld->accel_enabled, 0); | |
+ } | |
+ } | |
+ return 0; | |
+} | |
+ | |
+static int kionix_accel_cdev_poll_delay(struct sensors_classdev *sensors_cdev, | |
+ unsigned int delay_ms) | |
+{ | |
+ struct kionix_accel_driver *acceld = container_of(sensors_cdev, | |
+ struct | |
+ kionix_accel_driver, | |
+ cdev); | |
+ | |
+ if (delay_ms < KIONIX_ACCEL_MIN_DELAY) | |
+ delay_ms = KIONIX_ACCEL_MIN_DELAY; | |
+ if (delay_ms > KIONIX_ACCEL_MAX_DELAY) | |
+ delay_ms = KIONIX_ACCEL_MAX_DELAY; | |
+ atomic_set(&acceld->delay, (unsigned int)delay_ms); | |
+ acceld->kionix_accel_update_odr(acceld, atomic_read(&acceld->delay)); | |
+ return 0; | |
+} | |
+ | |
+static int kionix_accel_probe(struct i2c_client *client, | |
+ const struct i2c_device_id *id) | |
+{ | |
+ struct kionix_accel_platform_data *accel_pdata; | |
+ struct kionix_accel_driver *acceld; | |
+ int err; | |
+KMSGERR(&client->dev, "kionix_accel_probe.\n"); | |
+ if (!i2c_check_functionality(client->adapter, | |
+ I2C_FUNC_I2C | I2C_FUNC_SMBUS_BYTE_DATA)) { | |
+ KMSGERR(&client->dev, "client is not i2c capable. Abort.\n"); | |
+ return -ENXIO; | |
+ } | |
+ accel_pdata = kzalloc(sizeof(*accel_pdata), GFP_KERNEL); | |
+ if (accel_pdata == NULL) { | |
+ KMSGERR(&client->dev, | |
+ "failed to allocate memory for module data. Abort.\n"); | |
+ return -ENOMEM; | |
+ } | |
+ if (client->dev.of_node) { | |
+ memset(accel_pdata, 0, | |
+ sizeof(struct kionix_accel_platform_data)); | |
+ err = kionix_accel_parse_dt(&client->dev, accel_pdata); | |
+ if (err) { | |
+ dev_err(&client->dev, | |
+ "Unable to parse platfrom data err=%d\n", err); | |
+ goto err_free_pdata; | |
+ } | |
+ } else { | |
+ if (client->dev.platform_data) { | |
+ accel_pdata = | |
+ (struct kionix_accel_platform_data *)client-> | |
+ dev.platform_data; | |
+ } else { | |
+ KMSGERR(&client->dev, | |
+ "platform data is NULL. Abortk.\n"); | |
+ return -EINVAL; | |
+ } | |
+ } | |
+ acceld = kzalloc(sizeof(*acceld), GFP_KERNEL); | |
+ if (acceld == NULL) { | |
+ KMSGERR(&client->dev, | |
+ "failed to allocate memory for module data. Abort.\n"); | |
+ goto err_free_mem; | |
+ } | |
+ kionix_data = acceld; | |
+ acceld->client = client; | |
+ acceld->accel_pdata = *accel_pdata; | |
+ | |
+ i2c_set_clientdata(client, acceld); | |
+ | |
+ /* h/w initialization */ | |
+ if (accel_pdata->acc_init) | |
+ err = accel_pdata->acc_init(); | |
+ | |
+ if (accel_pdata->acc_power_on) | |
+ err = accel_pdata->acc_power_on(true); | |
+ | |
+ err = kionix_verify(acceld); | |
+ if (err < 0) { | |
+ KMSGERR(&acceld->client->dev, | |
+ "%s: kionix_verify returned err = %d. Abort.\n", | |
+ __func__, err); | |
+ goto err_accel_pdata_power_off; | |
+ } | |
+ | |
+ /* Setup group specific configuration and function callback */ | |
+ switch (err) { | |
+ case KIONIX_ACCEL_WHO_AM_I_KXTE9: | |
+ acceld->accel_group = KIONIX_ACCEL_GRP1; | |
+ acceld->accel_registers = | |
+ kzalloc(sizeof(u8) * accel_grp1_regs_count, GFP_KERNEL); | |
+ if (acceld->accel_registers == NULL) { | |
+ KMSGERR(&client->dev, | |
+ "failed to allocate memory for accel_registers. Abort.\n"); | |
+ goto err_accel_pdata_power_off; | |
+ } | |
+ acceld->accel_drdy = 0; | |
+ acceld->kionix_accel_report_accel_data = | |
+ kionix_accel_grp1_report_accel_data; | |
+ acceld->kionix_accel_update_odr = kionix_accel_grp1_update_odr; | |
+ acceld->kionix_accel_power_on_init = | |
+ kionix_accel_grp1_power_on_init; | |
+ acceld->kionix_accel_operate = kionix_accel_grp1_operate; | |
+ acceld->kionix_accel_standby = kionix_accel_grp1_standby; | |
+ break; | |
+ case KIONIX_ACCEL_WHO_AM_I_KXTF9: | |
+ case KIONIX_ACCEL_WHO_AM_I_KXTI9_1001: | |
+ case KIONIX_ACCEL_WHO_AM_I_KXTIK_1004: | |
+ case KIONIX_ACCEL_WHO_AM_I_KXTJ9_1005: | |
+ if (err == KIONIX_ACCEL_WHO_AM_I_KXTIK_1004) | |
+ acceld->accel_group = KIONIX_ACCEL_GRP3; | |
+ else | |
+ acceld->accel_group = KIONIX_ACCEL_GRP2; | |
+ acceld->accel_registers = | |
+ kzalloc(sizeof(u8) * accel_grp2_regs_count, GFP_KERNEL); | |
+ if (acceld->accel_registers == NULL) { | |
+ KMSGERR(&client->dev, | |
+ "failed to allocate memory for accel_registers. Abort.\n"); | |
+ goto err_accel_pdata_power_off; | |
+ } | |
+ switch (acceld->accel_pdata.accel_res) { | |
+ case KIONIX_ACCEL_RES_6BIT: | |
+ case KIONIX_ACCEL_RES_8BIT: | |
+ acceld->accel_registers[accel_grp2_ctrl_reg1] |= | |
+ ACCEL_GRP2_RES_8BIT; | |
+ break; | |
+ case KIONIX_ACCEL_RES_12BIT: | |
+ default: | |
+ acceld->accel_registers[accel_grp2_ctrl_reg1] |= | |
+ ACCEL_GRP2_RES_12BIT; | |
+ break; | |
+ } | |
+ if (acceld->accel_pdata.accel_irq_use_drdy && client->irq) { | |
+ acceld->accel_registers[accel_grp2_int_ctrl] |= | |
+ ACCEL_GRP2_IEN | ACCEL_GRP2_IEA; | |
+ acceld->accel_registers[accel_grp2_ctrl_reg1] |= | |
+ ACCEL_GRP2_DRDYE; | |
+ acceld->accel_drdy = 1; | |
+ } else | |
+ acceld->accel_drdy = 0; | |
+ kionix_accel_grp2_update_g_range(acceld); | |
+ acceld->kionix_accel_report_accel_data = | |
+ kionix_accel_grp2_report_accel_data; | |
+ acceld->kionix_accel_update_odr = kionix_accel_grp2_update_odr; | |
+ acceld->kionix_accel_power_on_init = | |
+ kionix_accel_grp2_power_on_init; | |
+ acceld->kionix_accel_operate = kionix_accel_grp2_operate; | |
+ acceld->kionix_accel_standby = kionix_accel_grp2_standby; | |
+ break; | |
+ case KIONIX_ACCEL_WHO_AM_I_KXTJ9_1007: | |
+ case KIONIX_ACCEL_WHO_AM_I_KXCJ9_1008: | |
+ case KIONIX_ACCEL_WHO_AM_I_KXTJ2_1009: | |
+ case KIONIX_ACCEL_WHO_AM_I_KXCJK_1013: | |
+ if (err == KIONIX_ACCEL_WHO_AM_I_KXTJ2_1009) | |
+ acceld->accel_group = KIONIX_ACCEL_GRP5; | |
+ else if (err == KIONIX_ACCEL_WHO_AM_I_KXCJK_1013) | |
+ acceld->accel_group = KIONIX_ACCEL_GRP6; | |
+ else | |
+ acceld->accel_group = KIONIX_ACCEL_GRP4; | |
+ acceld->accel_registers = | |
+ kzalloc(sizeof(u8) * accel_grp4_regs_count, GFP_KERNEL); | |
+ if (acceld->accel_registers == NULL) { | |
+ KMSGERR(&client->dev, | |
+ "failed to allocate memory for accel_registers. Abort.\n"); | |
+ goto err_accel_pdata_power_off; | |
+ } | |
+ switch (acceld->accel_pdata.accel_res) { | |
+ case KIONIX_ACCEL_RES_6BIT: | |
+ case KIONIX_ACCEL_RES_8BIT: | |
+ acceld->accel_registers[accel_grp4_ctrl_reg1] |= | |
+ ACCEL_GRP4_RES_8BIT; | |
+ break; | |
+ case KIONIX_ACCEL_RES_12BIT: | |
+ default: | |
+ acceld->accel_registers[accel_grp4_ctrl_reg1] |= | |
+ ACCEL_GRP4_RES_12BIT; | |
+ break; | |
+ } | |
+ if (acceld->accel_pdata.accel_irq_use_drdy && client->irq) { | |
+ acceld->accel_registers[accel_grp4_int_ctrl] |= | |
+ ACCEL_GRP4_IEN | ACCEL_GRP4_IEA; | |
+ acceld->accel_registers[accel_grp4_ctrl_reg1] |= | |
+ ACCEL_GRP4_DRDYE; | |
+ acceld->accel_drdy = 1; | |
+ } else | |
+ acceld->accel_drdy = 0; | |
+ kionix_accel_grp4_update_g_range(acceld); | |
+ acceld->kionix_accel_report_accel_data = | |
+ kionix_accel_grp4_report_accel_data; | |
+ acceld->kionix_accel_update_odr = kionix_accel_grp4_update_odr; | |
+ acceld->kionix_accel_power_on_init = | |
+ kionix_accel_grp4_power_on_init; | |
+ acceld->kionix_accel_operate = kionix_accel_grp4_operate; | |
+ acceld->kionix_accel_standby = kionix_accel_grp4_standby; | |
+ break; | |
+ default: | |
+ KMSGERR(&acceld->client->dev, | |
+ "%s: unsupported device, who am i = %d. Abort.\n", | |
+ __func__, err); | |
+ goto err_accel_pdata_power_off; | |
+ } | |
+ | |
+ err = kionix_accel_setup_input_device(acceld); | |
+ if (err) | |
+ goto err_free_accel_registers; | |
+ | |
+ atomic_set(&acceld->accel_suspended, 0); | |
+ atomic_set(&acceld->accel_suspend_continue, 1); | |
+ atomic_set(&acceld->accel_enabled, 0); | |
+ /*atomic_set(&acceld->accel_input_event, 0); */ | |
+ atomic_set(&acceld->accel_input_event, 1); | |
+ atomic_set(&acceld->accel_enable_resume, 0); | |
+ atomic_set(&acceld->delay, accel_pdata->poll_interval); | |
+ | |
+ mutex_init(&acceld->mutex_earlysuspend); | |
+ mutex_init(&acceld->mutex_resume); | |
+ rwlock_init(&acceld->rwlock_accel_data); | |
+ | |
+ acceld->poll_delay = msecs_to_jiffies(100); | |
+ acceld->kionix_accel_update_odr(acceld, 100); | |
+ kionix_accel_update_direction(acceld); | |
+ | |
+ acceld->accel_workqueue = create_workqueue("Kionix Accel Workqueue"); | |
+ INIT_DELAYED_WORK(&acceld->accel_work, kionix_accel_work); | |
+ init_waitqueue_head(&acceld->wqh_suspend); | |
+ | |
+ if (acceld->accel_drdy) { | |
+ err = request_threaded_irq(client->irq, NULL, kionix_accel_isr, | |
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT, | |
+ KIONIX_ACCEL_IRQ, acceld); | |
+ if (err) { | |
+ KMSGERR(&acceld->client->dev, | |
+ "%s: request_threaded_irq returned err = %d\n", | |
+ __func__, err); | |
+ KMSGERR(&acceld->client->dev, | |
+ "%s: running in software polling mode instead\n", | |
+ __func__); | |
+ acceld->accel_drdy = 0; | |
+ } | |
+ KMSGINF(&acceld->client->dev, | |
+ "running in hardware interrupt mode\n"); | |
+ } else { | |
+ KMSGINF(&acceld->client->dev, | |
+ "running in software polling mode\n"); | |
+ } | |
+ | |
+ err = acceld->kionix_accel_power_on_init(acceld); | |
+ if (err) { | |
+ KMSGERR(&acceld->client->dev, | |
+ "%s: kionix_accel_power_on_init returned err = %d. Abort.\n", | |
+ __func__, err); | |
+ goto err_free_irq; | |
+ } | |
+ | |
+ err = | |
+ sysfs_create_group(&acceld->input_dev->dev.kobj, | |
+ &kionix_accel_attribute_group); | |
+ if (err) { | |
+ KMSGERR(&acceld->client->dev, | |
+ "%s: sysfs_create_group returned err = %d. Abort.\n", | |
+ __func__, err); | |
+ goto err_free_irq; | |
+ } | |
+#ifdef CONFIG_HAS_EARLYSUSPEND | |
+ acceld->early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB + 50; | |
+ acceld->early_suspend.suspend = kionix_accel_earlysuspend_suspend; | |
+ acceld->early_suspend.resume = kionix_accel_earlysuspend_resume; | |
+ register_early_suspend(&acceld->early_suspend); | |
+#endif /* CONFIG_HAS_EARLYSUSPEND */ | |
+ | |
+ /* Register to sensors class */ | |
+ acceld->cdev = sensors_cdev; | |
+ acceld->cdev.sensors_enable = kionix_accel_cdev_enable; | |
+ acceld->cdev.sensors_poll_delay = kionix_accel_cdev_poll_delay; | |
+ err = sensors_classdev_register(&client->dev, &acceld->cdev); | |
+ if (err) { | |
+ dev_err(&client->dev, "create class device file failed\n"); | |
+ err = -EINVAL; | |
+ goto exit_remove_sysfs_group; | |
+ } | |
+ | |
+ return 0; | |
+ | |
+/*exit_unregister_acc_class: | |
+ sensors_classdev_unregister(&acceld->cdev);*/ | |
+exit_remove_sysfs_group: | |
+ sysfs_remove_group(&acceld->input_dev->dev.kobj, | |
+ &kionix_accel_attribute_group); | |
+err_free_irq: | |
+ if (acceld->accel_drdy) | |
+ free_irq(client->irq, acceld); | |
+ destroy_workqueue(acceld->accel_workqueue); | |
+ input_unregister_device(acceld->input_dev); | |
+err_free_accel_registers: | |
+ kfree(acceld->accel_registers); | |
+err_accel_pdata_power_off: | |
+ if (accel_pdata->acc_power_on) | |
+ accel_pdata->acc_power_on(false); | |
+ if (accel_pdata->acc_exit) | |
+ accel_pdata->acc_exit(); | |
+err_free_mem: | |
+ kionix_data = NULL; | |
+ kfree(acceld); | |
+err_free_pdata: | |
+kfree(accel_pdata); | |
+ return err; | |
+} | |
+ | |
+static int kionix_accel_remove(struct i2c_client *client) | |
+{ | |
+ struct kionix_accel_driver *acceld = i2c_get_clientdata(client); | |
+ struct kionix_accel_platform_data accel_pdata = acceld->accel_pdata; | |
+ | |
+#ifdef CONFIG_HAS_EARLYSUSPEND | |
+ unregister_early_suspend(&acceld->early_suspend); | |
+#endif /* CONFIG_HAS_EARLYSUSPEND */ | |
+ sysfs_remove_group(&acceld->input_dev->dev.kobj, | |
+ &kionix_accel_attribute_group); | |
+ if (acceld->accel_drdy) | |
+ free_irq(client->irq, acceld); | |
+ destroy_workqueue(acceld->accel_workqueue); | |
+ input_unregister_device(acceld->input_dev); | |
+ kfree(acceld->accel_registers); | |
+ if (acceld->accel_pdata.exit) | |
+ acceld->accel_pdata.exit(); | |
+ kionix_accel_power_off(acceld); | |
+ | |
+ if (accel_pdata.acc_power_on) | |
+ accel_pdata.acc_power_on(false); | |
+ if (accel_pdata.acc_exit) | |
+ accel_pdata.acc_exit(); | |
+ kionix_data = NULL; | |
+ kfree(acceld); | |
+ | |
+ return 0; | |
+} | |
+ | |
+#ifdef CONFIG_PM | |
+static int kionix_accel_suspend(struct i2c_client *client, pm_message_t mesg) | |
+{ | |
+ struct kionix_accel_driver *acceld = i2c_get_clientdata(client); | |
+ | |
+ kionix_accel_cdev_enable(&acceld->cdev, 0); | |
+ | |
+ return 0; | |
+} | |
+ | |
+static int kionix_accel_resume(struct i2c_client *client) | |
+{ | |
+ struct kionix_accel_driver *acceld = i2c_get_clientdata(client); | |
+ | |
+ kionix_accel_cdev_enable(&acceld->cdev, 1); | |
+ | |
+ return 0; | |
+} | |
+ | |
+#else | |
+ | |
+#define kionix_accel_suspend NULL | |
+#define kionix_accel_resume NULL | |
+ | |
+#endif /* CONFIG_PM */ | |
+ | |
+static const struct i2c_device_id kionix_accel_id[] = { | |
+ {KIONIX_ACCEL_NAME, 0}, | |
+ {}, | |
+}; | |
+ | |
+static struct of_device_id kionix_accel_match_table[] = { | |
+ {.compatible = "kionix,kxtj2-1009",}, | |
+ {}, | |
+}; | |
+ | |
+MODULE_DEVICE_TABLE(i2c, kionix_accel_id); | |
+ | |
+static struct i2c_driver kionix_accel_driver = { | |
+ .driver = { | |
+ .name = KIONIX_ACCEL_NAME, | |
+ .owner = THIS_MODULE, | |
+ .of_match_table = kionix_accel_match_table, | |
+ }, | |
+ .suspend = kionix_accel_suspend, | |
+ .resume = kionix_accel_resume, | |
+ .probe = kionix_accel_probe, | |
+ .remove = kionix_accel_remove, | |
+ .id_table = kionix_accel_id, | |
+}; | |
+ | |
+static int __init kionix_accel_init(void) | |
+{ | |
+ printk(KERN_ERR "kionix_accel_init.\n"); | |
+ return i2c_add_driver(&kionix_accel_driver); | |
+} | |
+ | |
+module_init(kionix_accel_init); | |
+ | |
+static void __exit kionix_accel_exit(void) | |
+{ | |
+ i2c_del_driver(&kionix_accel_driver); | |
+} | |
+ | |
+module_exit(kionix_accel_exit); | |
+ | |
+MODULE_DESCRIPTION("Kionix accelerometer driver"); | |
+MODULE_AUTHOR("Kuching Tan <[email protected]>"); | |
+MODULE_LICENSE("GPL"); | |
+MODULE_VERSION("3.3.0"); | |
diff --git a/include/linux/input/kionix_accel.h b/include/linux/input/kionix_accel.h | |
new file mode 100644 | |
index 000000000000..25926fc89545 | |
--- /dev/null | |
+++ b/include/linux/input/kionix_accel.h | |
@@ -0,0 +1,100 @@ | |
+/* include/linux/input/kionix_accel.h - Kionix accelerometer driver | |
+ * | |
+ * Copyright (C) 2012-2015 Kionix, Inc. | |
+ * Written by Kuching Tan <[email protected]> | |
+ * | |
+ * This program is free software: you can redistribute it and/or modify | |
+ * it under the terms of the GNU General Public License as published by | |
+ * the Free Software Foundation, either version 2 of the License, or | |
+ * (at your option) any later version. | |
+ * | |
+ * This program is distributed in the hope that it will be useful, | |
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
+ * GNU General Public License for more details. | |
+ * | |
+ * You should have received a copy of the GNU General Public License | |
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
+ * | |
+ */ | |
+#ifndef __KIONIX_ACCEL_H__ | |
+#define __KIONIX_ACCEL_H__ | |
+ | |
+/* POWER SUPPLY VOLTAGE RANGE */ | |
+#define KIONIX_VDD_MIN_UV 2000000 | |
+#define KIONIX_VDD_MAX_UV 3300000 | |
+#define KIONIX_VIO_MIN_UV 1750000 | |
+#define KIONIX_VIO_MAX_UV 1950000 | |
+/* Polling delay in msecs */ | |
+#define POLL_INTERVAL_MIN_MS 1 | |
+#define POLL_INTERVAL_MAX_MS 10000 | |
+#define POLL_DEFAULT_INTERVAL_MS 200 | |
+#define KIONIX_ACCEL_MAX_DELAY 1000 | |
+#define KIONIX_ACCEL_MIN_DELAY 1 | |
+ | |
+#define KIONIX_ACCEL_I2C_ADDR 0x0e | |
+#define KIONIX_ACCEL_NAME "kionix-accel" | |
+#define KIONIX_ACCEL_IRQ "kionix-irq" | |
+ | |
+struct kionix_accel_platform_data { | |
+ int (*acc_power)(unsigned char onoff); | |
+ int (*acc_init)(void); | |
+ void (*acc_exit)(void); | |
+ int (*acc_power_on)(bool); | |
+ /* Although the accelerometer can perform at high ODR, | |
+ * there is a need to keep the maximum ODR to a lower | |
+ * value due to power consumption or other concern. | |
+ * Use this variable to set the minimum allowable | |
+ * interval for data to be reported from the | |
+ * accelerometer. Unit is measured in milli- | |
+ * seconds. Recommended value is 5ms. */ | |
+ unsigned int min_interval; | |
+ /* Use this variable to set the default interval for | |
+ * data to be reported from the accelerometer. This | |
+ * value will be used during driver setup process, | |
+ * but can be changed by the system during runtime via | |
+ * sysfs control. Recommended value is 200ms.*/ | |
+ unsigned int poll_interval; | |
+ | |
+ /* This variable controls the corresponding direction | |
+ * of the accelerometer that is mounted on the board | |
+ * of the device. Refer to the porting guide for | |
+ * details. Valid value is 1 to 8. */ | |
+ u8 accel_direction; | |
+ | |
+ /* Use this variable to choose whether or not to use | |
+ * DRDY hardware interrupt mode to trigger a data | |
+ * report event instead of using software polling. | |
+ * Note that for those accelerometer model that does | |
+ * not support DRDY hardware interrupt, the driver | |
+ * will revert to software polling mode automatically. | |
+ * Valid value is 0 or 1.*/ | |
+ bool accel_irq_use_drdy; | |
+ | |
+ /* Use this variable to control the number of | |
+ * effective bits of the accelerometer output. | |
+ * Use the macro definition to select the desired | |
+ * number of effective bits. */ | |
+ #define KIONIX_ACCEL_RES_12BIT 0 | |
+ #define KIONIX_ACCEL_RES_8BIT 1 | |
+ #define KIONIX_ACCEL_RES_6BIT 2 | |
+ u8 accel_res; | |
+ | |
+ /* Use this variable to control the G range of | |
+ * the accelerometer output. Use the macro definition | |
+ * to select the desired G range.*/ | |
+ #define KIONIX_ACCEL_G_2G 0 | |
+ #define KIONIX_ACCEL_G_4G 1 | |
+ #define KIONIX_ACCEL_G_6G 2 | |
+ #define KIONIX_ACCEL_G_8G 3 | |
+ u8 accel_g_range; | |
+ | |
+ /* Optional callback functions that can be implemented | |
+ * on per product basis. If these callbacks are defined, | |
+ * they will be called by the driver. */ | |
+ int (*init)(void); | |
+ void (*exit)(void); | |
+ int (*power_on)(void); | |
+ int (*power_off)(void); | |
+}; | |
+#endif /* __KIONIX_ACCEL_H__ */ | |
-- | |
2.13.0 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment