input: bma250: Add HTC variant of BMA250 I2C accelerometer
HTC kernel version: villeu-jb-crc-3.4.10-ae8b65e
Change-Id: I4b7ad69386bb99ad72f165669648728c068ac4ab
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index f6c647c..99075b0 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -675,3 +675,9 @@
module will be called bmp18x-i2c.
endif
+
+config SENSORS_BMA250
+ tristate "BMA250 accelerometer sensor support"
+ depends on I2C
+ help
+ BMA250 G-sensor driver for HTC
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index 97d9782..8cb5d50 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -63,3 +63,4 @@
obj-$(CONFIG_STM_LIS3DH) += lis3dh_acc.o
obj-$(CONFIG_BMP18X) += bmp18x-core.o
obj-$(CONFIG_BMP18X_I2C) += bmp18x-i2c.o
+obj-$(CONFIG_SENSORS_BMA250) += bma250.o
diff --git a/drivers/input/misc/bma250.c b/drivers/input/misc/bma250.c
new file mode 100644
index 0000000..902618b
--- /dev/null
+++ b/drivers/input/misc/bma250.c
@@ -0,0 +1,676 @@
+/* drivers/i2c/chips/bma250.c - bma250 G-sensor driver
+ *
+ * Copyright (C) 2008-2009 HTC Corporation.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/input.h>
+#include <linux/bma250.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include<linux/earlysuspend.h>
+#include <linux/export.h>
+#include <linux/module.h>
+
+
+#define D(x...) pr_info("[GSNR][BMA250] " x)
+#define E(x...) printk(KERN_ERR "[GSNR][BMA250 ERROR] " x)
+#define DIF(x...) {\
+ if (debug_flag)\
+ printk(KERN_DEBUG "[GSNR][BMA250 DEBUG] " x); }
+
+#define DEFAULT_RANGE BMA_RANGE_2G
+#define DEFAULT_BW BMA_BW_31_25HZ
+
+#define RETRY_TIMES 10
+
+static struct i2c_client *this_client;
+
+struct bma250_data {
+ struct input_dev *input_dev;
+ struct work_struct work;
+ struct early_suspend early_suspend;
+};
+
+static struct bma250_platform_data *pdata;
+static atomic_t PhoneOn_flag = ATOMIC_INIT(0);
+#define DEVICE_ACCESSORY_ATTR(_name, _mode, _show, _store) \
+struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store)
+
+static int debug_flag;
+static char update_user_calibrate_data;
+
+static int BMA_I2C_RxData(char *rxData, int length)
+{
+ int retry;
+ struct i2c_msg msgs[] = {
+ {
+ .addr = this_client->addr,
+ .flags = 0,
+ .len = 1,
+ .buf = rxData,
+ },
+ {
+ .addr = this_client->addr,
+ .flags = I2C_M_RD,
+ .len = length,
+ .buf = rxData,
+ },
+ };
+
+ for (retry = 0; retry <= RETRY_TIMES; retry++) {
+ if (i2c_transfer(this_client->adapter, msgs, 2) > 0)
+ break;
+ else
+ mdelay(10);
+ }
+
+ if (retry > RETRY_TIMES) {
+ E("%s: retry over %d\n", __func__, RETRY_TIMES);
+ return -EIO;
+ } else
+ return 0;
+}
+
+static int BMA_I2C_TxData(char *txData, int length)
+{
+ int retry;
+ struct i2c_msg msg[] = {
+ {
+ .addr = this_client->addr,
+ .flags = 0,
+ .len = length,
+ .buf = txData,
+ },
+ };
+
+ for (retry = 0; retry <= RETRY_TIMES; retry++) {
+ if (i2c_transfer(this_client->adapter, msg, 1) > 0)
+ break;
+ else
+ mdelay(10);
+ }
+
+ if (retry > RETRY_TIMES) {
+ E("%s: retry over %d\n", __func__, RETRY_TIMES);
+ return -EIO;
+ } else
+ return 0;
+}
+
+static int BMA_Init(void)
+{
+ char buffer[4] = "";
+ int ret;
+ unsigned char range = 0, bw = 0;
+
+ memset(buffer, 0, 4);
+
+ buffer[0] = bma250_RANGE_SEL_REG;
+ ret = BMA_I2C_RxData(buffer, 2);
+ if (ret < 0)
+ return -1;
+ D("%s: bma250_RANGE_SEL_REG++: range = 0x%02x, bw = 0x%02x\n",
+ __func__, buffer[0], buffer[1]);
+ range = (buffer[0] & 0xF0) | DEFAULT_RANGE;
+ bw = (buffer[1] & 0xE0) | DEFAULT_BW;
+
+
+
+ buffer[1] = bw;
+ buffer[0] = bma250_BW_SEL_REG;
+ ret = BMA_I2C_TxData(buffer, 2);
+ if (ret < 0) {
+ E("%s: Write bma250_BW_SEL_REG fail\n", __func__);
+ return -1;
+ }
+
+ buffer[1] = range;
+ buffer[0] = bma250_RANGE_SEL_REG;
+ ret = BMA_I2C_TxData(buffer, 2);
+ if (ret < 0) {
+ E("%s: Write bma250_BW_SEL_REG fail\n", __func__);
+ return -1;
+ }
+
+
+ buffer[0] = bma250_RANGE_SEL_REG;
+ ret = BMA_I2C_RxData(buffer, 2);
+ if (ret < 0)
+ return -1;
+
+ D("%s: bma250_RANGE_SEL_REG:use single write--: range = 0x%02x, bw = 0x%02x\n",
+ __func__, buffer[0], buffer[1]);
+
+ return 0;
+
+}
+
+static int BMA_TransRBuff(short *rbuf)
+{
+ char buffer[6];
+ int ret;
+
+ memset(buffer, 0, 6);
+
+ buffer[0] = bma250_X_AXIS_LSB_REG;
+ ret = BMA_I2C_RxData(buffer, 6);
+ if (ret < 0)
+ return ret;
+
+
+ rbuf[0] = (short)(buffer[1] << 8 | buffer[0]);
+ rbuf[0] >>= 6;
+ rbuf[1] = (short)(buffer[3] << 8 | buffer[2]);
+ rbuf[1] >>= 6;
+ rbuf[2] = (short)(buffer[5] << 8 | buffer[4]);
+ rbuf[2] >>= 6;
+
+ DIF("%s: (x, y, z) = (%d, %d, %d)\n",
+ __func__, rbuf[0], rbuf[1], rbuf[2]);
+
+ return 1;
+}
+
+static int BMA_set_mode(unsigned char mode)
+{
+ char buffer[2] = "";
+ int ret = 0;
+ unsigned char data1 = 0;
+
+ printk(KERN_INFO "[GSNR] Gsensor %s\n", mode ? "disable" : "enable");
+
+ memset(buffer, 0, 2);
+
+ D("%s: mode = 0x%02x\n", __func__, mode);
+ if (mode < 2) {
+ buffer[0] = bma250_MODE_CTRL_REG;
+ ret = BMA_I2C_RxData(buffer, 1);
+ if (ret < 0)
+ return -1;
+
+
+ switch (mode) {
+ case bma250_MODE_NORMAL:
+ data1 = buffer[0] & 0x7F;
+ break;
+ case bma250_MODE_SUSPEND:
+ data1 = buffer[0] | 0x80;
+ break;
+ default:
+ break;
+ }
+
+
+ buffer[0] = bma250_MODE_CTRL_REG;
+ buffer[1] = data1;
+ ret = BMA_I2C_TxData(buffer, 2);
+ } else
+ ret = E_OUT_OF_RANGE;
+
+ if (mode == bma250_MODE_NORMAL)
+ usleep(2000);
+
+
+ return ret;
+}
+
+static int BMA_GET_INT(void)
+{
+ int ret;
+ ret = gpio_get_value(pdata->intr);
+ return ret;
+}
+
+static int bma_open(struct inode *inode, struct file *file)
+{
+ return nonseekable_open(inode, file);
+}
+
+static int bma_release(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+static long bma_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+
+ void __user *argp = (void __user *)arg;
+
+ char rwbuf[8] = "";
+ int ret = -1;
+ short buf[8], temp;
+ int kbuf = 0;
+
+ DIF("%s: cmd = 0x%x\n", __func__, cmd);
+
+ switch (cmd) {
+ case BMA_IOCTL_READ:
+ case BMA_IOCTL_WRITE:
+ case BMA_IOCTL_SET_MODE:
+ case BMA_IOCTL_SET_CALI_MODE:
+ case BMA_IOCTL_SET_UPDATE_USER_CALI_DATA:
+ if (copy_from_user(&rwbuf, argp, sizeof(rwbuf)))
+ return -EFAULT;
+ break;
+ case BMA_IOCTL_READ_ACCELERATION:
+ if (copy_from_user(&buf, argp, sizeof(buf)))
+ return -EFAULT;
+ break;
+ case BMA_IOCTL_WRITE_CALI_VALUE:
+ if (copy_from_user(&kbuf, argp, sizeof(kbuf)))
+ return -EFAULT;
+ break;
+ default:
+ break;
+ }
+
+ switch (cmd) {
+ case BMA_IOCTL_INIT:
+ ret = BMA_Init();
+ if (ret < 0)
+ return ret;
+ break;
+ case BMA_IOCTL_READ:
+ if (rwbuf[0] < 1)
+ return -EINVAL;
+ break;
+ case BMA_IOCTL_WRITE:
+ if (rwbuf[0] < 2)
+ return -EINVAL;
+ break;
+ case BMA_IOCTL_WRITE_CALI_VALUE:
+ pdata->gs_kvalue = kbuf;
+ printk(KERN_INFO "%s: Write calibration value: 0x%X\n",
+ __func__, pdata->gs_kvalue);
+ break;
+ case BMA_IOCTL_READ_ACCELERATION:
+ ret = BMA_TransRBuff(&buf[0]);
+ if (ret < 0)
+ return ret;
+ break;
+ case BMA_IOCTL_READ_CALI_VALUE:
+ if ((pdata->gs_kvalue & (0x67 << 24)) != (0x67 << 24)) {
+ rwbuf[0] = 0;
+ rwbuf[1] = 0;
+ rwbuf[2] = 0;
+ } else {
+ rwbuf[0] = (pdata->gs_kvalue >> 16) & 0xFF;
+ rwbuf[1] = (pdata->gs_kvalue >> 8) & 0xFF;
+ rwbuf[2] = pdata->gs_kvalue & 0xFF;
+ }
+ DIF("%s: CALI(x, y, z) = (%d, %d, %d)\n",
+ __func__, rwbuf[0], rwbuf[1], rwbuf[2]);
+ break;
+ case BMA_IOCTL_SET_MODE:
+ BMA_set_mode(rwbuf[0]);
+ break;
+ case BMA_IOCTL_GET_INT:
+ temp = BMA_GET_INT();
+ break;
+ case BMA_IOCTL_GET_CHIP_LAYOUT:
+ if (pdata)
+ temp = pdata->chip_layout;
+ break;
+ case BMA_IOCTL_GET_CALI_MODE:
+ if (pdata)
+ temp = pdata->calibration_mode;
+ break;
+ case BMA_IOCTL_SET_CALI_MODE:
+ if (pdata)
+ pdata->calibration_mode = rwbuf[0];
+ break;
+ case BMA_IOCTL_GET_UPDATE_USER_CALI_DATA:
+ temp = update_user_calibrate_data;
+ break;
+ case BMA_IOCTL_SET_UPDATE_USER_CALI_DATA:
+ update_user_calibrate_data = rwbuf[0];
+ break;
+
+ default:
+ return -ENOTTY;
+ }
+
+ switch (cmd) {
+ case BMA_IOCTL_READ:
+ break;
+ case BMA_IOCTL_READ_ACCELERATION:
+ if (copy_to_user(argp, &buf, sizeof(buf)))
+ return -EFAULT;
+ break;
+ case BMA_IOCTL_READ_CALI_VALUE:
+ if (copy_to_user(argp, &rwbuf, sizeof(rwbuf)))
+ return -EFAULT;
+ break;
+ case BMA_IOCTL_GET_INT:
+ if (copy_to_user(argp, &temp, sizeof(temp)))
+ return -EFAULT;
+ break;
+ case BMA_IOCTL_GET_CHIP_LAYOUT:
+ if (copy_to_user(argp, &temp, sizeof(temp)))
+ return -EFAULT;
+ break;
+ case BMA_IOCTL_GET_CALI_MODE:
+ if (copy_to_user(argp, &temp, sizeof(temp)))
+ return -EFAULT;
+ break;
+ case BMA_IOCTL_GET_UPDATE_USER_CALI_DATA:
+ if (copy_to_user(argp, &temp, sizeof(temp)))
+ return -EFAULT;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+#ifdef EARLY_SUSPEND_BMA
+
+static void bma250_early_suspend(struct early_suspend *handler)
+{
+ if (!atomic_read(&PhoneOn_flag))
+ BMA_set_mode(bma250_MODE_SUSPEND);
+ else
+ printk(KERN_DEBUG "bma250_early_suspend: PhoneOn_flag is set\n");
+}
+
+static void bma250_late_resume(struct early_suspend *handler)
+{
+ BMA_set_mode(bma250_MODE_NORMAL);
+}
+
+#else
+
+static int bma250_suspend(struct i2c_client *client, pm_message_t mesg)
+{
+ BMA_set_mode(bma250_MODE_SUSPEND);
+
+ return 0;
+}
+
+static int bma250_resume(struct i2c_client *client)
+{
+ BMA_set_mode(bma250_MODE_NORMAL);
+ return 0;
+}
+#endif
+
+static ssize_t bma250_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ char *s = buf;
+ s += sprintf(s, "%d\n", atomic_read(&PhoneOn_flag));
+ return s - buf;
+}
+
+static ssize_t bma250_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ if (count == (strlen("enable") + 1) &&
+ strncmp(buf, "enable", strlen("enable")) == 0) {
+ atomic_set(&PhoneOn_flag, 1);
+ printk(KERN_DEBUG "bma250_store: PhoneOn_flag=%d\n",
+ atomic_read(&PhoneOn_flag));
+ return count;
+ }
+ if (count == (strlen("disable") + 1) &&
+ strncmp(buf, "disable", strlen("disable")) == 0) {
+ atomic_set(&PhoneOn_flag, 0);
+ printk(KERN_DEBUG "bma250_store: PhoneOn_flag=%d\n",
+ atomic_read(&PhoneOn_flag));
+ return count;
+ }
+ E("bma250_store: invalid argument\n");
+ return -EINVAL;
+
+}
+
+static DEVICE_ACCESSORY_ATTR(PhoneOnOffFlag, 0664, \
+ bma250_show, bma250_store);
+
+static ssize_t debug_flag_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ char *s = buf;
+ char buffer, range = -1, bandwidth = -1, mode = -1;
+ int ret;
+
+ buffer = bma250_BW_SEL_REG;
+ ret = BMA_I2C_RxData(&buffer, 1);
+ if (ret < 0)
+ return -1;
+ bandwidth = (buffer & 0x1F);
+
+ buffer = bma250_RANGE_SEL_REG;
+ ret = BMA_I2C_RxData(&buffer, 1);
+ if (ret < 0)
+ return -1;
+ range = (buffer & 0xF);
+
+ buffer = bma250_MODE_CTRL_REG;
+ ret = BMA_I2C_RxData(&buffer, 1);
+ if (ret < 0)
+ return -1;
+ mode = ((buffer & 0x80) >> 7);
+
+ s += sprintf(s, "debug_flag = %d, range = 0x%x, bandwidth = 0x%x, "
+ "mode = 0x%x\n", debug_flag, range, bandwidth, mode);
+
+ return s - buf;
+}
+static ssize_t debug_flag_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ debug_flag = -1;
+ sscanf(buf, "%d", &debug_flag);
+
+ return count;
+
+}
+
+static DEVICE_ACCESSORY_ATTR(debug_en, 0664, \
+ debug_flag_show, debug_flag_store);
+
+int bma250_registerAttr(void)
+{
+ int ret;
+ struct class *htc_accelerometer_class;
+ struct device *accelerometer_dev;
+
+ htc_accelerometer_class = class_create(THIS_MODULE,
+ "htc_accelerometer");
+ if (IS_ERR(htc_accelerometer_class)) {
+ ret = PTR_ERR(htc_accelerometer_class);
+ htc_accelerometer_class = NULL;
+ goto err_create_class;
+ }
+
+ accelerometer_dev = device_create(htc_accelerometer_class,
+ NULL, 0, "%s", "accelerometer");
+ if (unlikely(IS_ERR(accelerometer_dev))) {
+ ret = PTR_ERR(accelerometer_dev);
+ accelerometer_dev = NULL;
+ goto err_create_accelerometer_device;
+ }
+
+
+ ret = device_create_file(accelerometer_dev, &dev_attr_PhoneOnOffFlag);
+ if (ret)
+ goto err_create_accelerometer_device_file;
+
+
+ ret = device_create_file(accelerometer_dev, &dev_attr_debug_en);
+ if (ret)
+ goto err_create_accelerometer_debug_en_device_file;
+
+ return 0;
+
+err_create_accelerometer_debug_en_device_file:
+ device_remove_file(accelerometer_dev, &dev_attr_PhoneOnOffFlag);
+err_create_accelerometer_device_file:
+ device_unregister(accelerometer_dev);
+err_create_accelerometer_device:
+ class_destroy(htc_accelerometer_class);
+err_create_class:
+
+ return ret;
+}
+
+static const struct file_operations bma_fops = {
+ .owner = THIS_MODULE,
+ .open = bma_open,
+ .release = bma_release,
+
+#if HAVE_COMPAT_IOCTL
+ .compat_ioctl = bma_ioctl,
+#endif
+#if HAVE_UNLOCKED_IOCTL
+ .unlocked_ioctl = bma_ioctl,
+#endif
+};
+
+static struct miscdevice bma_device = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "bma150",
+ .fops = &bma_fops,
+};
+
+int bma250_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ struct bma250_data *bma;
+ char buffer[2];
+ int err = 0;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ err = -ENODEV;
+ goto exit_check_functionality_failed;
+ }
+
+ bma = kzalloc(sizeof(struct bma250_data), GFP_KERNEL);
+ if (!bma) {
+ err = -ENOMEM;
+ goto exit_alloc_data_failed;
+ }
+
+ i2c_set_clientdata(client, bma);
+
+ pdata = client->dev.platform_data;
+ if (pdata == NULL) {
+ E("bma250_init_client: platform data is NULL\n");
+ goto exit_platform_data_null;
+ }
+
+ pdata->gs_kvalue = gs_kvalue;
+ printk(KERN_INFO "BMA250 G-sensor I2C driver: gs_kvalue = 0x%X\n",
+ pdata->gs_kvalue);
+
+ this_client = client;
+
+ buffer[0] = bma250_CHIP_ID_REG;
+ err = BMA_I2C_RxData(buffer, 1);
+ if (err < 0)
+ goto exit_wrong_ID;
+ D("%s: CHIP ID = 0x%02x\n", __func__, buffer[0]);
+ if ((buffer[0] != 0x3) && (buffer[0] != 0xF9)) {
+ E("Wrong chip ID of BMA250 or BMA250E!!\n");
+ goto exit_wrong_ID;
+ }
+
+ err = BMA_Init();
+ if (err < 0) {
+ E("bma250_probe: bma_init failed\n");
+ goto exit_init_failed;
+ }
+
+ err = misc_register(&bma_device);
+ if (err) {
+ E("bma250_probe: device register failed\n");
+ goto exit_misc_device_register_failed;
+ }
+
+#ifdef EARLY_SUSPEND_BMA
+ bma->early_suspend.suspend = bma250_early_suspend;
+ bma->early_suspend.resume = bma250_late_resume;
+ register_early_suspend(&bma->early_suspend);
+#endif
+
+ err = bma250_registerAttr();
+ if (err) {
+ E("%s: set spi_bma150_registerAttr fail!\n", __func__);
+ goto err_registerAttr;
+ }
+ D("%s: I2C retry 10 times version. OK\n", __func__);
+ debug_flag = 0;
+
+ return 0;
+
+err_registerAttr:
+exit_misc_device_register_failed:
+exit_init_failed:
+exit_wrong_ID:
+exit_platform_data_null:
+ kfree(bma);
+exit_alloc_data_failed:
+exit_check_functionality_failed:
+ return err;
+}
+
+static int bma250_remove(struct i2c_client *client)
+{
+ struct bma250_data *bma = i2c_get_clientdata(client);
+ kfree(bma);
+ return 0;
+}
+
+static const struct i2c_device_id bma250_id[] = {
+ { BMA250_I2C_NAME, 0 },
+ { }
+};
+
+static struct i2c_driver bma250_driver = {
+ .probe = bma250_probe,
+ .remove = bma250_remove,
+ .id_table = bma250_id,
+
+#ifndef EARLY_SUSPEND_BMA
+ .suspend = bma250_suspend,
+ .resume = bma250_resume,
+#endif
+ .driver = {
+ .name = BMA250_I2C_NAME,
+ },
+};
+
+static int __init bma250_init(void)
+{
+ printk(KERN_INFO "BMA250 G-sensor driver: init\n");
+ return i2c_add_driver(&bma250_driver);
+}
+
+static void __exit bma250_exit(void)
+{
+ i2c_del_driver(&bma250_driver);
+}
+
+module_init(bma250_init);
+module_exit(bma250_exit);
+
+MODULE_DESCRIPTION("BMA250 G-sensor driver");
+MODULE_LICENSE("GPL");
+