[PATCH] input: add driver for Bosch Sensortec's BMA150 accelerometer

来源:互联网 发布:知乎app ios7 下载 编辑:程序博客网 时间:2024/05/22 06:18
 Signed-off-by: Albert Zhang Signed-off-by: Eric Andersson --- drivers/input/misc/Kconfig  |   10 + drivers/input/misc/Makefile |    1 + drivers/input/misc/bma150.c |  554 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 565 insertions(+), 0 deletions(-) create mode 100644 drivers/input/misc/bma150.cdiff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfigindex 45dc6aa..9da47fd 100644--- a/drivers/input/misc/Kconfig+++ b/drivers/input/misc/Kconfig@@ -478,4 +478,14 @@ config INPUT_XEN_KBDDEV_FRONTEND   To compile this driver as a module, choose M here: the   module will be called xen-kbdfront. +config INPUT_BMA150+tristate "BMA150 acceleration sensor support"+depends on I2C+help+  Say Y here if you have Bosch Sensortec's BMA150 acceleration+  sensor hooked to an I2C bus.++  To compile this driver as a module, choose M here: the+  module will be called bma150.+ endifdiff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefileindex 38efb2c..9b13e0e 100644--- a/drivers/input/misc/Makefile+++ b/drivers/input/misc/Makefile@@ -45,4 +45,5 @@ obj-$(CONFIG_INPUT_WISTRON_BTNS)+= wistron_btns.o obj-$(CONFIG_INPUT_WM831X_ON)+= wm831x-on.o obj-$(CONFIG_INPUT_XEN_KBDDEV_FRONTEND)+= xen-kbdfront.o obj-$(CONFIG_INPUT_YEALINK)+= yealink.o+obj-$(CONFIG_INPUT_BMA150)+= bma150.o diff --git a/drivers/input/misc/bma150.c b/drivers/input/misc/bma150.cnew file mode 100644index 0000000..1a176b2--- /dev/null+++ b/drivers/input/misc/bma150.c@@ -0,0 +1,554 @@+/*+ * Copyright (c) 2011 Bosch Sensortec GmbH+ * Copyright (c) 2011 Unixphere+ *+ * This driver adds support for Bosch Sensortec's digital acceleration+ * sensor BMA150.+ * The datasheet for the chip can be found here:+ * http://www.bosch-sensortec.com/content/language1/downloads/BST-BMA150-DS000-07.pdf+ *+ * This program is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License as published by+ * the Free Software Foundation; either version 2 of the License, or+ * (at your option) any later version.+ *+ * This program is distributed in the hope that it will be useful,+ * but WITHOUT ANY WARRANTY; without even the implied warranty of+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+ * GNU General Public License for more details.+ *+ * You should have received a copy of the GNU General Public License+ * along with this program; if not, write to the Free Software+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.+ */+#include +#include +#include +#include +#include +#include +#include ++#define SENSOR_NAME"bma150"+#define ABSMAX_ACC_VAL0x01FF+#define ABSMIN_ACC_VAL-(ABSMAX_ACC_VAL)++#define BMA150_MAX_DELAY200+#define BMA150_RANGE_2G0+#define BMA150_RANGE_8G2+#define BMA150_BW_50HZ1+#define BMA150_BW_1500HZ6++#define BMA150_MODE_NORMAL0+#define BMA150_MODE_SLEEP2+#define BMA150_MODE_WAKE_UP3++#define BMA150_CHIP_ID2+#define BMA150_CHIP_ID_REG0x00++#define BMA150_ACC_X_LSB_REG0x02++#define BMA150_SLEEP_POS0+#define BMA150_SLEEP_MSK0x01+#define BMA150_SLEEP_REG0x0a++#define BMA150_BANDWIDTH_POS0+#define BMA150_BANDWIDTH_MSK0x07+#define BMA150_BANDWIDTH_REG0x14++#define BMA150_RANGE_POS3+#define BMA150_RANGE_MSK0x18+#define BMA150_RANGE_REG0x14++#define BMA150_WAKE_UP_POS0+#define BMA150_WAKE_UP_MSK0x01+#define BMA150_WAKE_UP_REG0x15++struct bma150acc {+s16x,+y,+z;+};++struct bma150_data {+struct i2c_client *bma150_client;+atomic_t delay;+unsigned char mode;+struct input_dev *input;+struct bma150acc value;+struct mutex mutex;+struct delayed_work work;+};++static int bma150_set_mode(struct i2c_client *client, unsigned char mode)+{+int ret;+unsigned char data1, data2;+struct bma150_data *bma150 = i2c_get_clientdata(client);++if ((mode != BMA150_MODE_NORMAL) &&+    (mode != BMA150_MODE_SLEEP) &&+    (mode != BMA150_MODE_WAKE_UP))+return -EINVAL;++data1 = i2c_smbus_read_byte_data(client, BMA150_WAKE_UP_REG);+if (data1 < 0)+return ret;++data1 = (data1 & ~BMA150_WAKE_UP_MSK) |+((mode << BMA150_WAKE_UP_POS) & BMA150_WAKE_UP_MSK);++data2 = i2c_smbus_read_byte_data(client, BMA150_SLEEP_REG);+if (data2 < 0)+return ret;++data2 = (data2 & ~BMA150_SLEEP_MSK) |+(((mode>>1) << BMA150_SLEEP_POS) & BMA150_SLEEP_MSK);++ret = i2c_smbus_write_byte_data(client, BMA150_WAKE_UP_REG, data1);+if (ret < 0)+return ret;++ret = i2c_smbus_write_byte_data(client, BMA150_SLEEP_REG, data2);+if (ret < 0)+return ret;++mutex_lock(&bma150->mutex);+bma150->mode = (unsigned char) mode;+mutex_unlock(&bma150->mutex);+return 0;+}+++static int bma150_set_range(struct i2c_client *client, unsigned char range)+{+int ret;+unsigned char data;++if (range > BMA150_RANGE_8G)+return -EINVAL;++data = i2c_smbus_read_byte_data(client, BMA150_RANGE_REG);+if (data < 0)+return ret;++data = (data & ~BMA150_RANGE_MSK) |+((range << BMA150_RANGE_POS) & BMA150_RANGE_MSK);++ret = i2c_smbus_write_byte_data(client, BMA150_RANGE_REG, data);+if (ret < 0)+return ret;++return 0;+}++static int bma150_get_range(struct i2c_client *client, unsigned char *range)+{+int ret;+unsigned char data;++data = i2c_smbus_read_byte_data(client, BMA150_RANGE_REG);+if (data < 0)+return ret;++*range = (data & BMA150_RANGE_MSK) >> BMA150_RANGE_POS;+return 0;+}++static int bma150_set_bandwidth(struct i2c_client *client, unsigned char bw)+{+int ret;+unsigned char data;++if (bw > BMA150_BW_1500HZ)+return -EINVAL;++data = i2c_smbus_read_byte_data(client, BMA150_BANDWIDTH_REG);+if (data < 0)+return ret;++data = (data & ~BMA150_BANDWIDTH_MSK) |+((bw << BMA150_BANDWIDTH_POS) & BMA150_BANDWIDTH_MSK);++ret = i2c_smbus_write_byte_data(client, BMA150_BANDWIDTH_REG, data);+if (ret < 0)+return ret;++return 0;+}++static int bma150_get_bandwidth(struct i2c_client *client, unsigned char *bw)+{+int ret;+unsigned char data;++data = i2c_smbus_read_byte_data(client, BMA150_BANDWIDTH_REG);+if (data < 0)+return ret;++*bw = (data & BMA150_BANDWIDTH_MSK) >> BMA150_BANDWIDTH_POS;+return 0;+}++static int bma150_read_accel_xyz(struct i2c_client *client,+struct bma150acc *acc)+{+unsigned char data[6];+int ret = i2c_smbus_read_i2c_block_data(client,+BMA150_ACC_X_LSB_REG, 6, data);+if (ret != 6)+return -EIO;++acc->x = ((0xC0 & data[0]) >> 6) | (data[1] << 2);+acc->y = ((0xC0 & data[2]) >> 6) | (data[3] << 2);+acc->z = ((0xC0 & data[4]) >> 6) | (data[5] << 2);++/* sign extension */+acc->x = (s16) (acc->x << 6) >> 6;+acc->y = (s16) (acc->y << 6) >> 6;+acc->z = (s16) (acc->z << 6) >> 6;++return 0;+}++static void bma150_work_func(struct work_struct *work)+{+int ret;+struct bma150_data *bma150 = container_of((struct delayed_work *)work,+struct bma150_data, work);+struct bma150acc acc;+unsigned long delay = msecs_to_jiffies(atomic_read(&bma150->delay));++ret = bma150_read_accel_xyz(bma150->bma150_client, &acc);+if (ret < 0)+return;++input_report_abs(bma150->input, ABS_X, acc.x);+input_report_abs(bma150->input, ABS_Y, acc.y);+input_report_abs(bma150->input, ABS_Z, acc.z);+input_sync(bma150->input);++mutex_lock(&bma150->mutex);+bma150->value = acc;+mutex_unlock(&bma150->mutex);++schedule_delayed_work(&bma150->work, delay);+}++static ssize_t bma150_mode_show(struct device *dev,+struct device_attribute *attr, char *buf)+{+struct i2c_client *client = to_i2c_client(dev);+struct bma150_data *bma150 = i2c_get_clientdata(client);++return sprintf(buf, "%d\n", bma150->mode);+}++static ssize_t bma150_mode_store(struct device *dev,+struct device_attribute *attr,+const char *buf, size_t count)+{+unsigned long data;+int error;+struct i2c_client *client = to_i2c_client(dev);+struct bma150_data *bma150 = i2c_get_clientdata(client);++error = strict_strtoul(buf, 10, &data);+if (error)+return error;++if (bma150_set_mode(bma150->bma150_client, (unsigned char) data) < 0)+return -EINVAL;++return count;+}+static ssize_t bma150_range_show(struct device *dev,+struct device_attribute *attr, char *buf)+{+unsigned char data;+struct i2c_client *client = to_i2c_client(dev);+struct bma150_data *bma150 = i2c_get_clientdata(client);++if (bma150_get_range(bma150->bma150_client, &data) < 0)+return sprintf(buf, "Read error\n");++return sprintf(buf, "%d\n", data);+}++static ssize_t bma150_range_store(struct device *dev,+struct device_attribute *attr,+const char *buf, size_t count)+{+unsigned long data;+int error;+struct i2c_client *client = to_i2c_client(dev);+struct bma150_data *bma150 = i2c_get_clientdata(client);++error = strict_strtoul(buf, 10, &data);+if (error)+return error;+if (bma150_set_range(bma150->bma150_client, (unsigned char) data) < 0)+return -EINVAL;++return count;+}++static ssize_t bma150_bandwidth_show(struct device *dev,+struct device_attribute *attr, char *buf)+{+unsigned char data;+struct i2c_client *client = to_i2c_client(dev);+struct bma150_data *bma150 = i2c_get_clientdata(client);++if (bma150_get_bandwidth(bma150->bma150_client, &data) < 0)+return sprintf(buf, "Read error\n");++return sprintf(buf, "%d\n", data);++}++static ssize_t bma150_bandwidth_store(struct device *dev,+struct device_attribute *attr,+const char *buf, size_t count)+{+unsigned long data;+int error;+struct i2c_client *client = to_i2c_client(dev);+struct bma150_data *bma150 = i2c_get_clientdata(client);++error = strict_strtoul(buf, 10, &data);+if (error)+return error;++if (bma150_set_bandwidth(bma150->bma150_client,+(unsigned char) data) < 0)+return -EINVAL;++return count;+}++static ssize_t bma150_value_show(struct device *dev,+struct device_attribute *attr, char *buf)+{+struct input_dev *input = to_input_dev(dev);+struct bma150_data *bma150 = input_get_drvdata(input);++return sprintf(buf, "%d %d %d\n", bma150->value.x, bma150->value.y,+bma150->value.z);+}++static ssize_t bma150_delay_show(struct device *dev,+struct device_attribute *attr, char *buf)+{+struct i2c_client *client = to_i2c_client(dev);+struct bma150_data *bma150 = i2c_get_clientdata(client);++return sprintf(buf, "%d\n", atomic_read(&bma150->delay));++}++static ssize_t bma150_delay_store(struct device *dev,+struct device_attribute *attr,+const char *buf, size_t count)+{+unsigned long data;+int error;+struct i2c_client *client = to_i2c_client(dev);+struct bma150_data *bma150 = i2c_get_clientdata(client);++error = strict_strtoul(buf, 10, &data);+if (error)+return error;++if (data > BMA150_MAX_DELAY)+data = BMA150_MAX_DELAY;++atomic_set(&bma150->delay, (unsigned int) data);++return count;+}++static DEVICE_ATTR(range, S_IRUGO|S_IWUSR|S_IWGRP,+bma150_range_show, bma150_range_store);+static DEVICE_ATTR(bandwidth, S_IRUGO|S_IWUSR|S_IWGRP,+bma150_bandwidth_show, bma150_bandwidth_store);+static DEVICE_ATTR(mode, S_IRUGO|S_IWUSR|S_IWGRP,+bma150_mode_show, bma150_mode_store);+static DEVICE_ATTR(value, S_IRUGO, bma150_value_show, NULL);+static DEVICE_ATTR(delay, S_IRUGO|S_IWUSR|S_IWGRP,+bma150_delay_show, bma150_delay_store);++static struct attribute *bma150_attributes[] = {+&dev_attr_range.attr,+&dev_attr_bandwidth.attr,+&dev_attr_mode.attr,+&dev_attr_value.attr,+&dev_attr_delay.attr,+NULL+};++static struct attribute_group bma150_attribute_group = {+.attrs = bma150_attributes+};++static int bma150_open(struct input_dev *inputdev)+{+struct bma150_data *dev = input_get_drvdata(inputdev);+int ret = bma150_set_mode(dev->bma150_client, BMA150_MODE_NORMAL);+if (ret < 0)+return ret;+schedule_delayed_work(&dev->work,+msecs_to_jiffies(atomic_read(&dev->delay)));+return 0;+}++static void bma150_close(struct input_dev *inputdev)+{+struct bma150_data *dev = input_get_drvdata(inputdev);+cancel_delayed_work_sync(&dev->work);+bma150_set_mode(dev->bma150_client, BMA150_MODE_SLEEP);+}++static int __devinit bma150_probe(struct i2c_client *client,+  const struct i2c_device_id *id)+{+struct bma150_data *data;+struct input_dev *dev;+int tempvalue;+int err = 0;++if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {+dev_err(&client->dev, "i2c_check_functionality error\n");+return -EIO;+}++data = kzalloc(sizeof(struct bma150_data), GFP_KERNEL);+if (!data)+return -ENOMEM;++tempvalue = i2c_smbus_read_byte_data(client, BMA150_CHIP_ID_REG);+if (tempvalue != BMA150_CHIP_ID) {+dev_err(&client->dev, "BMA150 chip id error: %d\n", tempvalue);+err = -EINVAL;+goto kfree_exit;+}+i2c_set_clientdata(client, data);+data->bma150_client = client;++bma150_set_bandwidth(client, BMA150_BW_50HZ);+bma150_set_range(client, BMA150_RANGE_2G);++INIT_DELAYED_WORK(&data->work, bma150_work_func);+atomic_set(&data->delay, BMA150_MAX_DELAY);++dev = input_allocate_device();+if (!dev) {+err = -ENOMEM;+goto kfree_exit;+}++dev->name = SENSOR_NAME;+dev->id.bustype = BUS_I2C;+dev->open = bma150_open;+dev->close = bma150_close;++input_set_capability(dev, EV_ABS, ABS_MISC);+input_set_abs_params(dev, ABS_X, ABSMIN_ACC_VAL, ABSMAX_ACC_VAL, 0, 0);+input_set_abs_params(dev, ABS_Y, ABSMIN_ACC_VAL, ABSMAX_ACC_VAL, 0, 0);+input_set_abs_params(dev, ABS_Z, ABSMIN_ACC_VAL, ABSMAX_ACC_VAL, 0, 0);+input_set_drvdata(dev, data);++err = input_register_device(dev);+if (err < 0) {+input_free_device(dev);+goto kfree_exit;+}++data->input = dev;++err = sysfs_create_group(&data->input->dev.kobj,+&bma150_attribute_group);+if (err < 0)+goto error_sysfs;++mutex_init(&data->mutex);++schedule_delayed_work(&data->work,+msecs_to_jiffies(atomic_read(&data->delay)));++dev_info(&client->dev, "Registered BMA150 I2C driver\n");+return 0;++error_sysfs:+input_unregister_device(dev);+kfree_exit:+kfree(data);+return err;+}++#ifdef CONFIG_PM+static int bma150_suspend(struct device *dev)+{+struct i2c_client *client = to_i2c_client(dev);++return bma150_set_mode(client, BMA150_MODE_SLEEP);+}++static int bma150_resume(struct device *dev)+{+struct i2c_client *client = to_i2c_client(dev);++return bma150_set_mode(client, BMA150_MODE_NORMAL);+}+#endif++static int bma150_remove(struct i2c_client *client)+{+struct bma150_data *data = i2c_get_clientdata(client);++sysfs_remove_group(&data->input->dev.kobj, &bma150_attribute_group);+input_unregister_device(data->input);+kfree(data);++return 0;+}++static SIMPLE_DEV_PM_OPS(bma150_pm, bma150_suspend,+ bma150_resume);++static const struct i2c_device_id bma150_id[] = {+{ SENSOR_NAME, 0 },+{ }+};++MODULE_DEVICE_TABLE(i2c, bma150_id);++static struct i2c_driver bma150_driver = {+.driver = {+.owner= THIS_MODULE,+.name= SENSOR_NAME,+.pm= &bma150_pm,+},+.class          = I2C_CLASS_HWMON,+.id_table= bma150_id,+.probe= bma150_probe,+.remove= __devexit_p(bma150_remove),+};++static int __init BMA150_init(void)+{+return i2c_add_driver(&bma150_driver);+}++static void __exit BMA150_exit(void)+{+i2c_del_driver(&bma150_driver);+}++MODULE_AUTHOR("Albert Zhang ");+MODULE_DESCRIPTION("BMA150 driver");+MODULE_LICENSE("GPL");++module_init(BMA150_init);+module_exit(BMA150_exit);+-- 1.7.3.4--To unsubscribe from this list: send the line "unsubscribe linux-kernel" inthe body of a message to majordomo@vger.kernel.orgMore majordomo info at  http://vger.kernel.org/majordomo-info.htmlPlease read the FAQ at  http://www.tux.org/lkml/


原创粉丝点击