diff options
author | Dan Murphy <dmurphy@ti.com> | 2011-07-20 12:10:55 -0500 |
---|---|---|
committer | Ziyann <jaraidaniel@gmail.com> | 2014-10-01 13:00:39 +0200 |
commit | 0dba7fb462c5a0505d4d210f8e4df6f4d859991c (patch) | |
tree | e484a3f818f5fa326ac947cbd50b3261f5e0d280 | |
parent | dc8948c9f032c4d734b3de1017ddca7f8043f6cb (diff) | |
download | kernel_samsung_tuna-0dba7fb462c5a0505d4d210f8e4df6f4d859991c.zip kernel_samsung_tuna-0dba7fb462c5a0505d4d210f8e4df6f4d859991c.tar.gz kernel_samsung_tuna-0dba7fb462c5a0505d4d210f8e4df6f4d859991c.tar.bz2 |
[Tablet] BMA180 Accelerometer:Port BMA180 k35 driver to k3.0
Port BMA180 k35 driver to k3.0
Change-Id: Ic018de4ae42db15ff50834fe726d2a77e9618678
Signed-off-by: Arthur Philpott <arthur.philpott@ti.com>
Conflicts:
drivers/input/misc/Kconfig
-rwxr-xr-x | drivers/input/misc/Kconfig | 7 | ||||
-rwxr-xr-x | drivers/input/misc/Makefile | 1 | ||||
-rw-r--r-- | drivers/input/misc/bma180.c | 831 | ||||
-rw-r--r-- | include/linux/i2c/bma180.h | 72 |
4 files changed, 911 insertions, 0 deletions
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 436ca93..a822b61 100755 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -541,6 +541,13 @@ config OPTICAL_GP2A help This option enables proximity & light sensors using gp2a driver. +config INPUT_BMA180 + bool "BMA-180 Tri-axis accelerometer" + depends on I2C && SYSFS + help + Say Y here if you want to use BMA-180 Tri-axis accelerometer + through I2C interface. + config INPUT_MPU3050 bool "MPU-3050 Tri-axis gyroscope" depends on I2C && SYSFS diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 182cbd5..93d86b7 100755 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -53,5 +53,6 @@ obj-$(CONFIG_INPUT_YEALINK) += yealink.o obj-$(CONFIG_OPTICAL_GP2A) += gp2a.o obj-$(CONFIG_INPUT_HMC5843) += hmc5843.o obj-$(CONFIG_INPUT_SFH7741) += sfh7741.o +obj-$(CONFIG_INPUT_BMA180) += bma180.o obj-$(CONFIG_INPUT_MPU3050) += mpu3050.o obj-$(CONFIG_INPUT_TSL2771) += tsl2771.o diff --git a/drivers/input/misc/bma180.c b/drivers/input/misc/bma180.c new file mode 100644 index 0000000..1b8f86c --- /dev/null +++ b/drivers/input/misc/bma180.c @@ -0,0 +1,831 @@ +/* + * bma180.c + * BMA-180 Accelerometer driver + * + * Copyright (C) 2010 Texas Instruments + * Author: Dan Murphy <Dmurphy@ti.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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/>. + * + * Derived work from bma180_accl.c from Jorge Bustamante <jbustamante@ti.com> + */ + +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/input.h> +#include <linux/workqueue.h> +#include <linux/i2c.h> +#include <linux/i2c/bma180.h> +#include <linux/gpio.h> + +#define BMA180_DEBUG 1 + +#define DEVICE_NAME "bma180" +#define DRIVER_NAME "bma180_accel" + +#define BMA180_OFFSET_Z 0x3A +#define BMA180_OFFSET_Y 0x39 +#define BMA180_OFFSET_X 0x38 +#define BMA180_OFFSET_T 0x37 +#define BMA180_OFFSET_LSB2 0x36 +#define BMA180_OFFSET_LSB1 0x35 +#define BMA180_GAIN_Z 0x34 +#define BMA180_GAIN_Y 0x33 +#define BMA180_GAIN_X 0x32 +#define BMA180_GAIN_T 0x31 +#define BMA180_TCO_Z 0x30 +#define BMA180_TCO_Y 0x2F +#define BMA180_TCO_X 0x2E +#define BMA180_CD2 0x2D +#define BMA180_CD1 0x2C +#define BMA180_SLOPE_TH 0x2B +#define BMA180_HIGH_TH 0x2A +#define BMA180_LOW_TH 0x29 +#define BMA180_TAPSENS_TH 0x28 +#define BMA180_HIGH_DUR 0x27 +#define BMA180_LOW_DUR 0x26 +#define BMA180_HIGH_LOW_INFO 0x25 +#define BMA180_SLOPE_TAPSENS_INFO 0x24 +#define BMA180_HY 0x23 +#define BMA180_CTRL_REG4 0x22 +#define BMA180_CTRL_REG3 0x21 +#define BMA180_BW_TCS 0x20 +#define BMA180_RESET 0x10 +#define BMA180_CTRL_REG2 0x0F +#define BMA180_CTRL_REG1 0x0E +#define BMA180_CTRL_REG0 0x0D +#define BMA180_STATUS_REG4 0x0C +#define BMA180_STATUS_REG3 0x0B +#define BMA180_STATUS_REG2 0x0A +#define BMA180_STATUS_REG1 0x09 +#define BMA180_TEMP 0x08 +#define BMA180_ACC_Z_MSB 0x07 +#define BMA180_ACC_Z_LSB 0x06 +#define BMA180_ACC_Y_MSB 0x05 +#define BMA180_ACC_Y_LSB 0x04 +#define BMA180_ACC_X_MSB 0x03 +#define BMA180_ACC_X_LSB 0x02 +#define BMA180_VERSION 0x01 +#define BMA180_CHIP_ID 0x00 + +#define SLEEP 0x00 +#define WAKEUP 0x01 + +struct bma180_accel_data { + struct bma180accel_platform_data *pdata; + struct i2c_client *client; + struct input_dev *input_dev; + struct delayed_work wq; + struct mutex mutex; + + uint32_t def_poll_rate; + int wakeup_flag; +}; + +static uint32_t accl_debug; +module_param_named(bma180_debug, accl_debug, uint, 0664); + +static int g_range_table[7] = { + 1000, + 1500, + 2000, + 3000, + 4000, + 8000, + 16000, +}; + +/* interval between samples for the different rates, in msecs */ +static const unsigned int bma180_measure_interval[] = { + 1000 / 10, 1000 / 20, 1000 / 40, 1000 / 75, + 1000 / 150, 1000 / 300, 1000 / 600, 1000 / 1200 +}; + +#ifdef BMA180_DEBUG +struct bma180_reg { + const char *name; + uint8_t reg; + int writeable; +} bma180_regs[] = { + { "CHIP_ID", BMA180_CHIP_ID, 0 }, + { "VERSION", BMA180_VERSION, 0 }, + { "X_LSB", BMA180_ACC_X_LSB, 0 }, + { "X_MSB", BMA180_ACC_X_MSB, 0 }, + { "Y_LSB", BMA180_ACC_Y_LSB, 0 }, + { "Y_MSB", BMA180_ACC_Y_MSB, 0 }, + { "Z_LSB", BMA180_ACC_Z_LSB, 0 }, + { "Z_MSB", BMA180_ACC_Z_MSB, 0 }, + { "TEMP", BMA180_TEMP, 0 }, + { "STATUS1", BMA180_STATUS_REG1, 0 }, + { "STATUS2", BMA180_STATUS_REG2, 0 }, + { "STATUS3", BMA180_STATUS_REG3, 0 }, + { "STATUS4", BMA180_STATUS_REG4, 0 }, + { "CTRL0", BMA180_CTRL_REG0, 1 }, + { "CTRL1", BMA180_CTRL_REG1, 1 }, + { "CTRL2", BMA180_CTRL_REG2, 1 }, + { "RESET", BMA180_RESET, 1 }, + { "BW_TCS", BMA180_BW_TCS, 1 }, + { "CTRL3", BMA180_CTRL_REG3, 1 }, + { "CTRL4", BMA180_CTRL_REG4, 1 }, + { "HY", BMA180_HY, 1 }, + { "TAP_INFO", BMA180_SLOPE_TAPSENS_INFO, 1 }, + { "HI_LOW_INFO", BMA180_HIGH_LOW_INFO, 1 }, + { "LOW_DUR", BMA180_LOW_DUR, 1 }, + { "HIGH_DUR", BMA180_HIGH_DUR, 1 }, + { "TAP_THRESH", BMA180_TAPSENS_TH, 1 }, + { "LOW_THRESH", BMA180_LOW_TH, 1 }, + { "HIGH_THRESH", BMA180_HIGH_TH, 1 }, + { "SLOPE_THRESH", BMA180_SLOPE_TH, 1 }, + { "CD1", BMA180_CD1, 1 }, + { "CD2", BMA180_CD2, 1 }, + { "TCO_X", BMA180_TCO_X, 1 }, + { "TCO_Y", BMA180_TCO_Y, 1 }, + { "TCO_Z", BMA180_TCO_Z, 1 }, + { "GAIN_T", BMA180_GAIN_T, 1 }, + { "GAIN_X", BMA180_GAIN_X, 1 }, + { "GAIN_Y", BMA180_GAIN_Y, 1 }, + { "GAIN_Z", BMA180_GAIN_Z, 1 }, + { "OFFSET_LSB1", BMA180_OFFSET_LSB1, 1 }, + { "OFFSET_LSB2", BMA180_OFFSET_LSB2, 1 }, + { "OFFSET_T", BMA180_OFFSET_T, 1 }, + { "OFFSET_X", BMA180_OFFSET_X, 1 }, + { "OFFSET_Y", BMA180_OFFSET_Y, 1 }, + { "OFFSET_Z", BMA180_OFFSET_Z, 1 }, +}; +#endif + +static int bma180_write(struct bma180_accel_data *data, u8 reg, u8 val) +{ + int ret = 0; + mutex_lock(&data->mutex); + ret = i2c_smbus_write_byte_data(data->client, reg, val); + if (ret < 0) + dev_err(&data->client->dev, + "i2c_smbus_write_byte_data failed\n"); + mutex_unlock(&data->mutex); + + return ret; +} + +static int bma180_read_transfer(struct bma180_accel_data *data, + unsigned short data_addr, char *data_buf, int count) +{ + int ret; + int counter = 5; + char *data_buffer = data_buf; + + struct i2c_msg msgs[] = { + { + .addr = data->client->addr, + .flags = data->client->flags & I2C_M_TEN, + .len = 1, + .buf = data_buffer, + }, + { + .addr = data->client->addr, + .flags = (data->client->flags & I2C_M_TEN) | I2C_M_RD, + .len = count, + .buf = data_buffer, + }, + }; + + data_buffer[0] = data_addr; + msgs->buf = data_buffer; + + do { + ret = i2c_transfer(data->client->adapter, msgs, 2); + if (ret != 2) { + dev_err(&data->client->dev, + "i2c_transfer failed\n"); + counter--; + msleep(1); + } else { + return 0; + } + } while (counter >= 0); + + return -1; +} + +static int bma180_accel_device_hw_set_bandwidth(struct bma180_accel_data *data, + int bandwidth) +{ + uint8_t reg_val; + uint8_t int_val; + + bma180_read_transfer(data, BMA180_CTRL_REG3, &int_val, 1); + bma180_write(data, BMA180_CTRL_REG3, 0x00); + bma180_read_transfer(data, BMA180_BW_TCS, ®_val, 1); + reg_val = (reg_val & 0x0F) | ((bandwidth << 4) & 0xF0); + bma180_write(data, BMA180_BW_TCS, reg_val); + msleep(10); + bma180_write(data, BMA180_CTRL_REG3, int_val); + + return 0; +} + +static void bma180_device_sleep_wakeup(struct bma180_accel_data *data, + int do_wakeup) +{ + int ret; + uint8_t reg_val; + + bma180_read_transfer(data, BMA180_CTRL_REG0, ®_val, 1); + + if (do_wakeup) { + reg_val &= ~BMA180_SLEEP; + ret = bma180_write(data, BMA180_CTRL_REG0, reg_val); + if (ret < 0) + dev_err(&data->client->dev, + "can't wakeup device\n"); + msleep(10); + } else { + reg_val |= BMA180_SLEEP; + ret = bma180_write(data, BMA180_CTRL_REG0, reg_val); + if (ret < 0) + dev_err(&data->client->dev, + "sleep mode can't be set\n"); + } +} + +static irqreturn_t bma180_accel_thread_irq(int irq, void *dev_data) +{ + struct bma180_accel_data *data = (struct bma180_accel_data *) dev_data; + + if (!data->client->irq) + schedule_delayed_work(&data->wq, 0); + + return IRQ_HANDLED; +} + +static int bma180_accel_data_ready(struct bma180_accel_data *data) +{ + int ret; + uint8_t data_val_h, data_val_l; + short int x = 0; + short int y = 0; + short int z = 0; + uint8_t data_buffer[6]; + + ret = bma180_read_transfer(data, BMA180_ACC_X_LSB, data_buffer, 6); + if (ret != 0) { + dev_err(&data->client->dev, + "bma180_read_data_ready failed\n"); + return -1; + } + data_val_l = data_buffer[0]; + data_val_h = data_buffer[1]; + if (accl_debug) + pr_info("%s: X low 0x%X X high 0x%X\n", + __func__, data_val_l, data_val_h); + x = ((data_val_h << 8) | data_val_l); + x = (x >> 2); + x = x * g_range_table[data->pdata->g_range]/data->pdata->bit_mode; + + data_val_l = data_buffer[2]; + data_val_h = data_buffer[3]; + if (accl_debug) + pr_info("%s: Y low 0x%X Y high 0x%X\n", + __func__, data_val_l, data_val_h); + + y = ((data_val_h << 8) | data_val_l); + y = (y >> 2); + y = y * g_range_table[data->pdata->g_range]/data->pdata->bit_mode; + + data_val_l = data_buffer[4]; + data_val_h = data_buffer[5]; + if (accl_debug) + pr_info("%s: Z low 0x%X Z high 0x%X\n", + __func__, data_val_l, data_val_h); + + z = ((data_val_h << 8) | data_val_l); + z = (z >> 2); + z = z * g_range_table[data->pdata->g_range]/data->pdata->bit_mode; + + if (accl_debug) + pr_info("%s: X: 0x%X Y: 0x%X Z: 0x%X\n", + __func__, x, y, z); + + input_report_abs(data->input_dev, ABS_X, x); + input_report_abs(data->input_dev, ABS_Y, y); + input_report_abs(data->input_dev, ABS_Z, z); + input_sync(data->input_dev); + + return 0; +} + +static void bma180_accel_device_worklogic(struct work_struct *work) +{ + struct bma180_accel_data *data = container_of((struct delayed_work *)work, + struct bma180_accel_data, wq); + + bma180_accel_data_ready(data); + if (!data->client->irq) + schedule_delayed_work(&data->wq, + msecs_to_jiffies(data->def_poll_rate)); + +} + +static ssize_t bma180_show_attr_enable(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct bma180_accel_data *data = platform_get_drvdata(pdev); + + return sprintf(buf, "%d\n", data->pdata->mode); +} + +static ssize_t bma180_store_attr_enable(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct platform_device *pdev = to_platform_device(dev); + struct bma180_accel_data *data = platform_get_drvdata(pdev); + unsigned long val; + int error, enable; + + error = strict_strtoul(buf, 0, &val); + if (error) + return error; + enable = !!val; + + mutex_lock(&data->mutex); + if (data->pdata->mode == enable) { + mutex_unlock(&data->mutex); + return count; + } + + data->pdata->mode = enable; + mutex_unlock(&data->mutex); + + if (enable) { + data->wakeup_flag = 0; + bma180_device_sleep_wakeup(data, WAKEUP); + schedule_delayed_work(&data->wq, 0); + } else { + cancel_delayed_work_sync(&data->wq); + bma180_device_sleep_wakeup(data, SLEEP); + } + + data->pdata->mode = enable; + + return count; +} + +static ssize_t bma180_show_attr_delay(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct bma180_accel_data *data = platform_get_drvdata(pdev); + + return sprintf(buf, "%d\n", data->def_poll_rate); +} + +static ssize_t bma180_store_attr_delay(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct platform_device *pdev = to_platform_device(dev); + struct bma180_accel_data *data = platform_get_drvdata(pdev); + unsigned long interval; + int error; + int i = 0; + + error = strict_strtoul(buf, 0, &interval); + if (error) + return error; + + if (interval < 0) + return -EINVAL; + + cancel_delayed_work_sync(&data->wq); + + if (interval >= + bma180_measure_interval[BMA_BW_10HZ]) + i = BMA_BW_10HZ; + else if (interval >= + bma180_measure_interval[BMA_BW_20HZ]) + i = BMA_BW_20HZ; + else if (interval >= + bma180_measure_interval[BMA_BW_40HZ]) + i = BMA_BW_40HZ; + else if (interval >= + bma180_measure_interval[BMA_BW_75HZ]) + i = BMA_BW_75HZ; + else if (interval >= + bma180_measure_interval[BMA_BW_150HZ]) + i = BMA_BW_150HZ; + else if (interval >= + bma180_measure_interval[BMA_BW_300HZ]) + i = BMA_BW_300HZ; + else if (interval >= + bma180_measure_interval[BMA_BW_600HZ]) + i = BMA_BW_600HZ; + else + i = BMA_BW_1200HZ; + + data->def_poll_rate = interval; + bma180_accel_device_hw_set_bandwidth(data, i); + schedule_delayed_work(&data->wq, 0); + + return count; + +} +#ifdef BMA180_DEBUG +static ssize_t bma180_registers_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct bma180_accel_data *data = platform_get_drvdata(pdev); + unsigned i, n, reg_count; + uint8_t value; + + reg_count = sizeof(bma180_regs) / sizeof(bma180_regs[0]); + for (i = 0, n = 0; i < reg_count; i++) { + bma180_read_transfer(data, bma180_regs[i].reg, &value, 1); + n += scnprintf(buf + n, PAGE_SIZE - n, + "%-20s = 0x%02X\n", + bma180_regs[i].name, + value); + } + + return n; +} + +static ssize_t bma180_registers_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct platform_device *pdev = to_platform_device(dev); + struct bma180_accel_data *data = platform_get_drvdata(pdev); + unsigned i, reg_count, value; + int error = 0; + char name[30]; + + if (count >= 30) { + pr_err("%s:input too long\n", __func__); + return -1; + } + + if (sscanf(buf, "%s %x", name, &value) != 2) { + pr_err("%s:unable to parse input\n", __func__); + return -1; + } + + reg_count = sizeof(bma180_regs) / sizeof(bma180_regs[0]); + for (i = 0; i < reg_count; i++) { + if (!strcmp(name, bma180_regs[i].name)) { + if (bma180_regs[i].writeable) { + error = bma180_write(data, + bma180_regs[i].reg, + value); + if (error) { + pr_err("%s:Failed to write %s\n", + __func__, name); + return -1; + } + } else { + pr_err("%s:Register %s is not writeable\n", + __func__, name); + return -1; + } + return count; + } + } + + pr_err("%s:no such register %s\n", __func__, name); + return -1; +} + +static DEVICE_ATTR(registers, S_IWUSR | S_IRUGO, + bma180_registers_show, bma180_registers_store); +#endif +static DEVICE_ATTR(enable, S_IWUSR | S_IRUGO, + bma180_show_attr_enable, bma180_store_attr_enable); + +static DEVICE_ATTR(delay, S_IWUSR | S_IRUGO, + bma180_show_attr_delay, bma180_store_attr_delay); + +static struct attribute *bma180_accel_attrs[] = { + &dev_attr_enable.attr, + &dev_attr_delay.attr, +#ifdef BMA180_DEBUG + &dev_attr_registers.attr, +#endif + NULL +}; + +static const struct attribute_group bma180_accel_attr_group = { + .attrs = bma180_accel_attrs, +}; + +static int bma180_accel_device_hw_reset(struct bma180_accel_data *data) +{ + /* write 0xB6 to this register to do a soft-reset */ + bma180_write(data, BMA180_RESET, 0xB6); + + return 0; +} + +static int bma180_accel_device_hw_reset_int(struct bma180_accel_data *data) +{ + uint8_t reg_val; + + bma180_read_transfer(data, BMA180_CTRL_REG0, ®_val, 1); + reg_val |= 0x40; + bma180_write(data, BMA180_CTRL_REG0, reg_val); + + return 0; +} + +static int bma180_accel_device_hw_set_grange(struct bma180_accel_data *data, + int grange) +{ + uint8_t reg_val; + + bma180_read_transfer(data, BMA180_OFFSET_LSB1, ®_val, 1); + reg_val = (reg_val & 0xF1) | ((grange << 1) & 0x0E); + bma180_write(data, BMA180_OFFSET_LSB1, reg_val); + + return 0; +} + +static int bma180_accel_device_hw_set_smp_skip(struct bma180_accel_data *data, + int smp_skip) +{ + uint8_t reg_val; + + bma180_read_transfer(data, BMA180_OFFSET_LSB1, ®_val, 1); + reg_val = (reg_val & 0xFE) | ((smp_skip << 0) & 0x01); + bma180_write(data, BMA180_OFFSET_LSB1, reg_val); + + return 0; +} + +static int bma180_accel_device_hw_set_mode(struct bma180_accel_data *data, + int mode) +{ + uint8_t reg_val; + + bma180_read_transfer(data, BMA180_TCO_Z, ®_val, 1); + reg_val = (reg_val & 0xFC) | ((mode << 0) & 0x03); + bma180_write(data, BMA180_TCO_Z, reg_val); + + return 0; +} + +static int bma180_accel_device_hw_set_12bits(struct bma180_accel_data *data, + int mode) +{ + uint8_t reg_val; + + bma180_read_transfer(data, BMA180_OFFSET_T, ®_val, 1); + reg_val = (reg_val & 0xFE) | ((mode << 0) & 0x01); + bma180_write(data, BMA180_OFFSET_T, reg_val); + + return 0; +} + +static int bma180_accel_device_hw_init(struct bma180_accel_data *data) +{ + int ret = 0; + + bma180_accel_device_hw_reset(data); + msleep(1); + bma180_write(data, BMA180_CTRL_REG0, data->pdata->ctrl_reg0); + bma180_write(data, BMA180_CTRL_REG3, 0x00); + bma180_write(data, BMA180_HIGH_LOW_INFO, 0x00); + bma180_write(data, BMA180_SLOPE_TAPSENS_INFO, 0x00); + bma180_write(data, BMA180_GAIN_Y, 0xA9); + bma180_write(data, BMA180_HIGH_DUR, 0x00); + bma180_accel_device_hw_set_grange(data, data->pdata->g_range); + bma180_accel_device_hw_set_bandwidth(data, data->pdata->bandwidth); + bma180_accel_device_hw_set_mode(data, data->pdata->mode); + bma180_accel_device_hw_set_smp_skip(data, data->pdata->smp_skip); + bma180_accel_device_hw_set_12bits(data, data->pdata->bit_mode); + bma180_write(data, BMA180_CTRL_REG3, 0x02); + + bma180_accel_device_hw_reset_int(data); + + return ret; +} + + +static int __devinit bma180_accel_driver_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct bma180accel_platform_data *pdata = client->dev.platform_data; + struct bma180_accel_data *data; + int ret = 0; + + pr_info("%s: Enter\n", __func__); + + if (pdata == NULL) { + pr_err("%s: Platform data not found\n", __func__); + return -ENODEV; + } + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + pr_err("%s: need I2C_FUNC_I2C\n", __func__); + return -ENODEV; + } + + /* alloc memory for data structure */ + data = kzalloc(sizeof(struct bma180_accel_data), GFP_KERNEL); + if (!data) { + ret = -ENOMEM; + goto error; + } + data->pdata = pdata; + data->client = client; + data->def_poll_rate = pdata->def_poll_rate; + i2c_set_clientdata(client, data); + + data->input_dev = input_allocate_device(); + if (data->input_dev == NULL) { + ret = -ENOMEM; + dev_err(&data->client->dev, + "Failed to allocate input device\n"); + goto error; + } + + INIT_DELAYED_WORK(&data->wq, bma180_accel_device_worklogic); + + mutex_init(&data->mutex); + + data->input_dev->name = "bma180"; + data->input_dev->id.bustype = BUS_I2C; + + __set_bit(EV_ABS, data->input_dev->evbit); + input_set_abs_params(data->input_dev, ABS_X, + -g_range_table[data->pdata->g_range], + g_range_table[data->pdata->g_range], + data->pdata->fuzz_x, 0); + input_set_abs_params(data->input_dev, ABS_Y, + -g_range_table[data->pdata->g_range], + g_range_table[data->pdata->g_range], + data->pdata->fuzz_y, 0); + input_set_abs_params(data->input_dev, ABS_Z, + -g_range_table[data->pdata->g_range], + g_range_table[data->pdata->g_range], + data->pdata->fuzz_z, 0); + + data->input_dev->dev.parent = &data->client->dev; + input_set_drvdata(data->input_dev, data); + + ret = input_register_device(data->input_dev); + if (ret) { + dev_err(&data->client->dev, + "Unable to register input device\n"); + } + + if (data->client->irq) { + ret = request_threaded_irq(data->client->irq, NULL, + bma180_accel_thread_irq, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + data->client->name, data); + if (ret < 0) { + dev_err(&data->client->dev, + "request_threaded_irq failed\n"); + goto error_1; + } + } + + ret = bma180_accel_device_hw_init(data); + if (ret) + goto error_1; + + ret = sysfs_create_group(&client->dev.kobj, &bma180_accel_attr_group); + if (ret) + goto error_1; + + return 0; + +error_1: + input_free_device(data->input_dev); + mutex_destroy(&data->mutex); + kfree(data); +error: + return ret; +} + +static int __devexit bma180_accel_driver_remove(struct i2c_client *client) +{ + struct bma180_accel_data *data = i2c_get_clientdata(client); + int ret = 0; + + sysfs_remove_group(&client->dev.kobj, &bma180_accel_attr_group); + + if (data->client->irq) + free_irq(data->client->irq, data); + + cancel_delayed_work_sync(&data->wq); + + if (data->input_dev) + input_free_device(data->input_dev); + + i2c_set_clientdata(client, NULL); + kfree(data); + + return ret; +} + + +#ifdef CONFIG_PM +static int bma180_accel_driver_suspend(struct i2c_client *client, + pm_message_t mesg) +{ + struct bma180_accel_data *data = i2c_get_clientdata(client); + + mutex_lock(&data->mutex); + if (!data->pdata->mode) { + mutex_unlock(&data->mutex); + return 0; + } + + data->pdata->mode = 0; + mutex_unlock(&data->mutex); + data->wakeup_flag = 1; + + cancel_delayed_work_sync(&data->wq); + bma180_device_sleep_wakeup(data, SLEEP); + + return 0; +} + +static int bma180_accel_driver_resume(struct i2c_client *client) +{ + struct bma180_accel_data *data = i2c_get_clientdata(client); + + mutex_lock(&data->mutex); + if (data->pdata->mode) { + mutex_unlock(&data->mutex); + return 0; + } + + if (!data->wakeup_flag) { + mutex_unlock(&data->mutex); + return 0; + } + + data->pdata->mode = 1; + mutex_unlock(&data->mutex); + data->wakeup_flag = 0; + + bma180_device_sleep_wakeup(data, WAKEUP); + schedule_delayed_work(&data->wq, 0); + + return 0; +} +#endif + +static const struct i2c_device_id bma180_accel_idtable[] = { + { DRIVER_NAME, 0 }, + { }, +}; + +MODULE_DEVICE_TABLE(i2c, bma180_accel_idtable); + +static struct i2c_driver bma180_accel_driver = { + .probe = bma180_accel_driver_probe, + .remove = bma180_accel_driver_remove, + .id_table = bma180_accel_idtable, +#ifdef CONFIG_PM + .suspend = bma180_accel_driver_suspend, + .resume = bma180_accel_driver_resume, +#endif + .driver = { + .name = DRIVER_NAME + }, +}; + +static int __init bma180_accel_driver_init(void) +{ + return i2c_add_driver(&bma180_accel_driver); +} + +static void __exit bma180_accel_driver_exit(void) +{ + i2c_del_driver(&bma180_accel_driver); +} + +module_init(bma180_accel_driver_init); +module_exit(bma180_accel_driver_exit); + +MODULE_DESCRIPTION("BMA-180 Accelerometer Driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Dan Murphy <DMurphy@ti.com>"); diff --git a/include/linux/i2c/bma180.h b/include/linux/i2c/bma180.h new file mode 100644 index 0000000..2d5bafe --- /dev/null +++ b/include/linux/i2c/bma180.h @@ -0,0 +1,72 @@ +/* + * bma180.h + * BMA-180 Accelerometer driver + * + * Copyright (C) 2010 Texas Instruments + * Author: Jorge Bustamante <jbustamante@ti.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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 _LINUX_BMA180_I2C_H +#define _LINUX_BMA180_I2C_H + +#define BMA_GRANGE_1G 0x0 +#define BMA_GRANGE_1_5G 0x1 +#define BMA_GRANGE_2G 0x2 +#define BMA_GRANGE_3G 0x3 +#define BMA_GRANGE_4G 0x4 +#define BMA_GRANGE_8G 0x5 +#define BMA_GRANGE_16G 0x6 + +#define BMA_BW_10HZ 0x0 +#define BMA_BW_20HZ 0x1 +#define BMA_BW_40HZ 0x2 +#define BMA_BW_75HZ 0x3 +#define BMA_BW_150HZ 0x4 +#define BMA_BW_300HZ 0x5 +#define BMA_BW_600HZ 0x6 +#define BMA_BW_1200HZ 0x7 +#define BMA_BW_HP_1HZ 0x8 +#define BMA_BW_BP_0_2HZ_300HZ 0x9 + +#define BMA_MODE_LOW_NOISE 0x0 +#define BMA_MODE_SUPER_LOW_NOISE 0x1 +#define BMA_MODE_ULTRA_LOW_NOISE 0x2 +#define BMA_MODE_LOW_POWER 0x3 + +#define BMA_BITMODE_14BITS 0x2000 +#define BMA_BITMODE_12BITS 0x0800 + +#define BMA180_SLEEP 0x02 + +struct bma180accel_platform_data { + uint8_t method; + uint32_t def_poll_rate; + uint16_t fuzz_x; + uint16_t fuzz_y; + uint16_t fuzz_z; + uint8_t ctrl_reg0; + uint8_t ctrl_reg1; + uint8_t ctrl_reg2; + uint8_t ctrl_reg3; + uint8_t ctrl_reg4; + + uint16_t bandwidth; + uint8_t g_range; + uint8_t mode; + int bit_mode; + uint8_t smp_skip; +}; + +#endif |