This patch supports driver for BOSCH BMA023 and SMB380 which are accelerometers.

Basically, this driver will read x, y, and z coordinate registers
from the device and report the values to users through sysfs interface.

In further, this driver can also report user's oscillation via input event,
when the device is used as an input device which is like gaming joystick.

The driver allows to set or get the device's properties by
the sysfs attributes.

Could this patch be accepted into mid-ref tree by meego?


sysfs attributes
----------------
range: indicate the full scale acceleration range
        0: +/-2g (default)
        1: +/-4g
        2: +/-8g
bandwidth: indicate the digital filtering of ADC output data
           to obtain the desired bandwidth
        0: 25Hz
        1: 50Hz
        2: 100Hz
        3: 190Hz (default)
        4: 375Hz
        5: 750Hz
        6: 1500Hz
new_data_int: generate an interrupt
              when all three axes acceleration values are new
        0: disable (default)
        1: enable
hg_int: generate an interrupt when the high-g threshold criteria are met
        0: disable (default)
        1: enable
lg_int: generate an interrupt when the low-g threshold criteria are met
        0: disable (default)
        1: enable
hg_dur:
        0 - 255: define a high-g interrupt criterion for duration
        150: default
hg_hyst:
        0 - 7: define a high-g interrupt criterion
        0: default
hg_thres:
        0 - 255: define a high-g interrupt criterion
        160: default
lg_dur:
        0 - 255: define a low-g interrupt criterion for duration
        150: default
lg_hyst:
        0 - 7: define a high-g interrupt criterion
        0: default
lg_thres:
        0 - 255: define a high-g interrupt criterion
        20: default
power_mode: indicate the current power mode
        0: low power mode (default)
        1: normal power mode
accel_data: print the values of x, y, and z coordinate register
temperature: print the value of temperature register

Signed-off-by: Joseph Lai <[email protected]> 

drivers/input/misc/Kconfig  |   10 +
 drivers/input/misc/Makefile |    1 +
 drivers/input/misc/bma023.c |  719 +++++++++++++++++++++++++++++++++++++++++++
 include/linux/bma023.h      |   47 +++
 4 files changed, 777 insertions(+), 0 deletions(-)
 create mode 100644 drivers/input/misc/bma023.c
 create mode 100644 include/linux/bma023.h

Index: linux-2.6.35/drivers/input/misc/Kconfig
===================================================================
--- linux-2.6.35.orig/drivers/input/misc/Kconfig        2011-01-07 
20:10:35.000000000 +0800
+++ linux-2.6.35/drivers/input/misc/Kconfig     2011-01-07 20:14:14.000000000 
+0800
@@ -390,4 +390,14 @@
          To compile this driver as a module, choose M here: the
          module will be called pcap_keys.
 
+config INPUT_BMA023
+       tristate "BMA023/SMB380 Triaxial acceleration sensor"
+       depends on I2C
+       help
+         Say Y here if you want to support Bosch BMA023/SMB380
+         connected via an I2C bus.
+
+         To compile this driver as a module, choose M here: the
+         module will be called bma023.
+
 endif
Index: linux-2.6.35/drivers/input/misc/Makefile
===================================================================
--- linux-2.6.35.orig/drivers/input/misc/Makefile       2011-01-07 
20:10:35.000000000 +0800
+++ linux-2.6.35/drivers/input/misc/Makefile    2011-01-07 21:48:39.000000000 
+0800
@@ -29,6 +29,7 @@
 obj-$(CONFIG_INPUT_RB532_BUTTON)       += rb532_button.o
 obj-$(CONFIG_INPUT_GPIO_ROTARY_ENCODER)        += rotary_encoder.o
 obj-$(CONFIG_INPUT_SGI_BTNS)           += sgi_btns.o
+obj-$(CONFIG_INPUT_BMA023)                     += bma023.o
 obj-$(CONFIG_INPUT_SPARCSPKR)          += sparcspkr.o
 obj-$(CONFIG_INPUT_TWL4030_PWRBUTTON)  += twl4030-pwrbutton.o
 obj-$(CONFIG_INPUT_TWL4030_VIBRA)      += twl4030-vibra.o
Index: linux-2.6.35/drivers/input/misc/bma023.c
===================================================================
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.35/drivers/input/misc/bma023.c    2011-01-10 21:47:52.000000000 
+0800
@@ -0,0 +1,736 @@
+/*
+ * bma023.c - BMA023 Tri-axis accelerometer driver
+ *
+ * Copyright (C) 2010 Samsung Eletronics Co.Ltd
+ * Kim Kyuwon <[email protected]>
+ * Kyungmin Park <[email protected]>
+ * Donggeun Kim <[email protected]>
+ *
+ * Copyright (C) 2011 Wistron Co.Ltd
+ * Joseph Lai <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/workqueue.h>
+#include <linux/mutex.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/bma023.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+
+#define BMA023_CHIP_ID_REG     0x00
+#define BMA023_X_LSB_REG       0x02
+#define BMA023_X_MSB_REG       0x03
+#define BMA023_Y_LSB_REG       0x04
+#define BMA023_Y_MSB_REG       0x05
+#define BMA023_Z_LSB_REG       0x06
+#define BMA023_Z_MSB_REG       0x07
+#define BMA023_TEMP_REG                0x08
+#define BMA023_CTRL1_REG       0x0a
+#define BMA023_CTRL2_REG       0x0b
+#define BMA023_SETTINGS1_REG   0x0c
+#define BMA023_SETTINGS2_REG   0x0d
+#define BMA023_SETTINGS3_REG   0x0e
+#define BMA023_SETTINGS4_REG   0x0f
+#define BMA023_SETTINGS5_REG   0x10
+#define BMA023_SETTINGS6_REG   0x11
+#define BMA023_RANGE_BW_REG    0x14
+#define BMA023_CONF2_REG       0x15
+
+#define BMA023_CHIP_ID         0x02
+
+#define BMA023_NEW_DATA_INT_SHIFT      5
+#define BMA023_NEW_DATA_INT_MASK       (0x1 << 5)
+
+#define BMA023_RANGE_SHIFT             3
+#define BMA023_RANGE_MASK              (0x3 << 3)
+#define BMA023_BANDWIDTH_SHIFT 0
+#define BMA023_BANDWIDTH_MASK  (0x7)
+
+#define BMA023_HG_HYST_SHIFT   3
+#define BMA023_HG_HYST_MASK            (0x7 << 3)
+#define BMA023_LG_HYST_SHIFT   0
+#define BMA023_LG_HYST_MASK            (0x7)
+
+#define BMA023_HG_DUR_SHIFT            (0x0)
+#define BMA023_HG_DUR_MASK             (0xff)
+#define BMA023_HG_THRES_SHIFT  (0x0)
+#define BMA023_HG_THRES_MASK   (0xff)
+#define BMA023_LG_DUR_SHIFT            (0x0)
+#define BMA023_LG_DUR_MASK             (0xff)
+#define BMA023_LG_THRES_SHIFT  (0x0)
+#define BMA023_LG_THRES_MASK   (0xff)
+
+#define BMA023_ENABLE_HG_SHIFT 1
+#define BMA023_ENABLE_HG_MASK  (0x1 << 1)
+#define BMA023_ENABLE_LG_SHIFT 0
+#define BMA023_ENABLE_LG_MASK  (0x1)
+
+#define BMA023_SLEEP_SHIFT             0
+#define BMA023_SLEEP_MASK              (0x1)
+
+#define BMA023_ACCEL_BITS              10
+#define BMA023_MAX_VALUE               ((1 << ((BMA023_ACCEL_BITS) - 1)) - 1)
+#define BMA023_MIN_VALUE               (-(1 << ((BMA023_ACCEL_BITS) - 1)))
+
+#define BMA023_DEFAULT_RANGE           RANGE_2G
+#define BMA023_DEFAULT_BANDWIDTH       BW_190HZ
+#define BMA023_DEFAULT_NEW_DATA_INT    0
+#define BMA023_DEFAULT_HG_INT          0
+#define BMA023_DEFAULT_LG_INT          0
+#define BMA023_DEFAULT_HG_DURATION     0x96
+#define BMA023_DEFAULT_HG_THRESHOLD    0xa0
+#define BMA023_DEFAULT_HG_HYST         0
+#define BMA023_DEFAULT_LG_DURATION     0x96
+#define BMA023_DEFAULT_LG_THRESHOLD    0x14
+#define BMA023_DEFAULT_LG_HYST         0
+
+struct bma023_data {
+       s16 x;
+       s16 y;
+       s16 z;
+       u8 temp;
+};
+
+struct bma023_sensor {
+       struct i2c_client       *client;
+       struct device           *dev;
+       struct input_dev        *idev;
+       struct work_struct      work;
+       struct mutex            lock;
+       struct bma023_data data;
+       enum scale_range range;
+       enum filter_bw bandwidth;
+       u8 new_data_int;
+       u8 hg_int;
+       u8 lg_int;
+       u8 lg_dur;
+       u8 lg_thres;
+       u8 lg_hyst;
+       u8 hg_dur;
+       u8 hg_thres;
+       u8 hg_hyst;
+       u8 power_mode;
+};
+
+static int bma023_write_reg(struct i2c_client *client, u8 reg, u8 val)
+{
+       int ret;
+       /*
+        * According to the datasheet, the interrupt should be deactivated
+        * on the host side when write sequences operate
+        */
+       disable_irq_nosync(client->irq);
+       ret = i2c_smbus_write_byte_data(client, reg, val);
+       enable_irq(client->irq);
+       if (ret < 0)
+               dev_err(&client->dev, "%s: reg 0x%x, val 0x%x, err %d\n",
+                       __func__, reg, val, ret);
+       return ret;
+}
+
+static int bma023_read_reg(struct i2c_client *client, u8 reg)
+{
+       int ret = i2c_smbus_read_byte_data(client, reg);
+       if (ret < 0)
+               dev_err(&client->dev, "%s: reg 0x%x, err %d\n",
+                       __func__, reg, ret);
+       return ret;
+}
+
+static int bma023_xyz_read_reg(struct i2c_client *client,
+                              u8 *buffer, int length)
+{
+       struct i2c_msg msg[] = {
+               {
+                       .addr = client->addr,
+                       .flags = 0,
+                       .len = 1,
+                       .buf = buffer,
+               },
+               {
+                       .addr = client->addr,
+                       .flags = I2C_M_RD,
+                       .len = length,
+                       .buf = buffer,
+               },
+       };
+       return i2c_transfer(client->adapter, msg, 2);
+}
+
+static int bma023_set_reg_bits(struct i2c_client *client,
+                                       int val, int shift, u8 mask, u8 reg)
+{
+       u8 data = bma023_read_reg(client, reg);
+
+       data = (data & ~mask) | ((val << shift) & mask);
+       return bma023_write_reg(client, reg, data);
+}
+
+static u8 bma023_get_reg_bits(struct i2c_client *client, int shift,
+                                       u8 mask, u8 reg)
+{
+       u8 data = bma023_read_reg(client, reg);
+       data = (data & mask) >> shift;
+       return data;
+}
+
+/* range */
+static int bma023_set_range(struct i2c_client *client, enum scale_range range)
+{
+       return bma023_set_reg_bits(client, range, BMA023_RANGE_SHIFT,
+                                  BMA023_RANGE_MASK, BMA023_RANGE_BW_REG);
+}
+
+static u8 bma023_get_range(struct i2c_client *client)
+{
+       return bma023_get_reg_bits(client, BMA023_RANGE_SHIFT,
+                                  BMA023_RANGE_MASK, BMA023_RANGE_BW_REG);
+}
+
+/* bandwidth */
+static int bma023_set_bandwidth(struct i2c_client *client, enum filter_bw bw)
+{
+       return bma023_set_reg_bits(client, bw, BMA023_BANDWIDTH_SHIFT,
+                                  BMA023_BANDWIDTH_MASK, BMA023_RANGE_BW_REG);
+}
+
+static u8 bma023_get_bandwidth(struct i2c_client *client)
+{
+       return bma023_get_reg_bits(client, BMA023_BANDWIDTH_SHIFT,
+                                  BMA023_BANDWIDTH_MASK, BMA023_RANGE_BW_REG);
+}
+
+/* new data interrupt */
+static int bma023_set_new_data_int(struct i2c_client *client, u8 val)
+{
+       return bma023_set_reg_bits(client, val, BMA023_NEW_DATA_INT_SHIFT,
+                                  BMA023_NEW_DATA_INT_MASK, BMA023_CONF2_REG);
+}
+
+static u8 bma023_get_new_data_int(struct i2c_client *client)
+{
+       return bma023_get_reg_bits(client, BMA023_NEW_DATA_INT_SHIFT,
+                                  BMA023_NEW_DATA_INT_MASK, BMA023_CONF2_REG);
+}
+
+/* high-g interrupt */
+static int bma023_set_hg_int(struct i2c_client *client, u8 val)
+{
+       return bma023_set_reg_bits(client, val, BMA023_ENABLE_HG_SHIFT,
+                                  BMA023_ENABLE_HG_MASK, BMA023_CTRL2_REG);
+}
+
+static u8 bma023_get_hg_int(struct i2c_client *client)
+{
+       return bma023_get_reg_bits(client, BMA023_ENABLE_HG_SHIFT,
+                                  BMA023_ENABLE_HG_MASK, BMA023_CTRL2_REG);
+}
+
+/* low-g interrupt */
+static int bma023_set_lg_int(struct i2c_client *client, u8 val)
+{
+       return bma023_set_reg_bits(client, val, BMA023_ENABLE_LG_SHIFT,
+                                  BMA023_ENABLE_LG_MASK, BMA023_CTRL2_REG);
+}
+
+static u8 bma023_get_lg_int(struct i2c_client *client)
+{
+       return bma023_get_reg_bits(client, BMA023_ENABLE_LG_SHIFT,
+                                  BMA023_ENABLE_LG_MASK, BMA023_CTRL2_REG);
+}
+
+/* low-g interrupt criterion for duration */
+static int bma023_set_lg_dur(struct i2c_client *client, u8 dur)
+{
+       return bma023_set_reg_bits(client, dur, BMA023_LG_DUR_SHIFT,
+                                  BMA023_LG_DUR_MASK, BMA023_SETTINGS2_REG);
+}
+
+static u8 bma023_get_lg_dur(struct i2c_client *client)
+{
+       return bma023_get_reg_bits(client, BMA023_LG_DUR_SHIFT,
+                                  BMA023_LG_DUR_MASK, BMA023_SETTINGS2_REG);
+}
+
+/* low-g interrupt criterion for threshold */
+static int bma023_set_lg_thres(struct i2c_client *client, u8 thres)
+{
+       return bma023_set_reg_bits(client, thres, BMA023_LG_THRES_SHIFT,
+                                  BMA023_LG_THRES_MASK, BMA023_SETTINGS1_REG);
+}
+
+static u8 bma023_get_lg_thres(struct i2c_client *client)
+{
+       return bma023_get_reg_bits(client, BMA023_LG_THRES_SHIFT,
+                                  BMA023_LG_THRES_MASK, BMA023_SETTINGS1_REG);
+}
+
+/* low-g interrupt criterion for hysteresis */
+static int bma023_set_lg_hyst(struct i2c_client *client, u8 hyst)
+{
+       return bma023_set_reg_bits(client, hyst, BMA023_LG_HYST_SHIFT,
+                                  BMA023_LG_HYST_MASK, BMA023_SETTINGS6_REG);
+}
+
+static u8 bma023_get_lg_hyst(struct i2c_client *client)
+{
+       return bma023_get_reg_bits(client, BMA023_LG_HYST_SHIFT,
+                                  BMA023_LG_HYST_MASK, BMA023_SETTINGS6_REG);
+}
+
+/* high-g interrupt criterion for duration */
+static int bma023_set_hg_dur(struct i2c_client *client, u8 dur)
+{
+       return bma023_set_reg_bits(client, dur, BMA023_HG_DUR_SHIFT,
+                                  BMA023_HG_DUR_MASK, BMA023_SETTINGS4_REG);
+}
+
+static u8 bma023_get_hg_dur(struct i2c_client *client)
+{
+       return bma023_get_reg_bits(client, BMA023_HG_DUR_SHIFT,
+                                  BMA023_HG_DUR_MASK, BMA023_SETTINGS4_REG);
+}
+
+/* high-g interrupt criterion for threshold */
+static int bma023_set_hg_thres(struct i2c_client *client, u8 thres)
+{
+       return bma023_set_reg_bits(client, thres, BMA023_HG_THRES_SHIFT,
+                                  BMA023_HG_THRES_MASK, BMA023_SETTINGS3_REG);
+}
+
+static u8 bma023_get_hg_thres(struct i2c_client *client)
+{
+       return bma023_get_reg_bits(client, BMA023_HG_THRES_SHIFT,
+                                  BMA023_HG_THRES_MASK, BMA023_SETTINGS3_REG);
+}
+
+/* high-g interrupt criterion for hysteresis */
+static int bma023_set_hg_hyst(struct i2c_client *client, u8 hyst)
+{
+       return bma023_set_reg_bits(client, hyst, BMA023_HG_HYST_SHIFT,
+                                  BMA023_HG_HYST_MASK, BMA023_SETTINGS6_REG);
+}
+
+static u8 bma023_get_hg_hyst(struct i2c_client *client)
+{
+       return bma023_get_reg_bits(client, BMA023_HG_HYST_SHIFT,
+                                  BMA023_HG_HYST_MASK, BMA023_SETTINGS6_REG);
+}
+
+/* power mode for device suspend/resume */
+static int bma023_set_power_mode(struct i2c_client *client, u8 val)
+{
+       return bma023_set_reg_bits(client, ~val, BMA023_SLEEP_SHIFT,
+                                  BMA023_SLEEP_MASK, BMA023_CTRL1_REG);
+}
+
+static int bma023_get_power_mode(struct i2c_client *client)
+{
+       return !bma023_get_reg_bits(client, BMA023_SLEEP_SHIFT,
+                                  BMA023_SLEEP_MASK, BMA023_CTRL1_REG);
+}
+
+/*
+ * The description of the digital signals x, y and z is "2' complement".
+ * So we need to correct the sign of data read by i2c.
+ */
+static void bma023_correct_accel_sign(s16 *val)
+{
+       *val <<= (sizeof(s16) * BITS_PER_BYTE - BMA023_ACCEL_BITS);
+       *val >>= (sizeof(s16) * BITS_PER_BYTE - BMA023_ACCEL_BITS);
+}
+
+static void bma023_merge_register_values(struct i2c_client *client, s16 *val,
+                                        u8 lsb, u8 msb)
+{
+       *val = (msb << 2) | (lsb >> 6);
+       bma023_correct_accel_sign(val);
+}
+
+/*
+ * Read 8 bit temperature.
+ * An output of 0 equals -30C, 1 LSB equals 0.5C
+ */
+static void bma023_read_temperature(struct i2c_client *client, u8 *temper)
+{
+       *temper = bma023_read_reg(client, BMA023_TEMP_REG);
+       dev_dbg(&client->dev, "%s: %d\n", __func__, *temper);
+}
+
+static void bma023_read_xyz(struct i2c_client *client,
+                           s16 *x, s16 *y, s16 *z)
+{
+       u8 buffer[6];
+       buffer[0] = BMA023_X_LSB_REG;
+       bma023_xyz_read_reg(client, buffer, 6);
+       bma023_merge_register_values(client, x, buffer[0], buffer[1]);
+       bma023_merge_register_values(client, y, buffer[2], buffer[3]);
+       bma023_merge_register_values(client, z, buffer[4], buffer[5]);
+       dev_dbg(&client->dev, "%s: x %d, y %d, z %d\n", __func__, *x, *y, *z);
+}
+
+static ssize_t bma023_show_xyz(struct device *dev,
+                              struct device_attribute *attr, char *buf)
+{
+       struct bma023_sensor *sensor = dev_get_drvdata(dev);
+       if(bma023_get_power_mode(sensor->client) == 0){
+               bma023_set_power_mode(sensor->client, 1);
+               msleep(4);  /* wait for accel chip resume */
+       }
+       mutex_lock(&sensor->lock);
+       bma023_read_xyz(sensor->client,
+               &sensor->data.x, &sensor->data.y, &sensor->data.z);
+       mutex_unlock(&sensor->lock);
+       return sprintf(buf, "%d,%d,%d\n",
+               sensor->data.x, sensor->data.y, sensor->data.z);
+}
+static DEVICE_ATTR(accel_data, S_IRUGO, bma023_show_xyz, NULL);
+
+static ssize_t bma023_show_temper(struct device *dev,
+                                 struct device_attribute *attr, char *buf)
+{
+       struct bma023_sensor *sensor = dev_get_drvdata(dev);
+       mutex_lock(&sensor->lock);
+       bma023_read_temperature(sensor->client, &sensor->data.temp);
+       mutex_unlock(&sensor->lock);
+       return sprintf(buf, "%d\n", sensor->data.temp);
+}
+static DEVICE_ATTR(temperature, S_IRUGO, bma023_show_temper, NULL);
+
+#define BMA023_SYSFS(name) \
+static ssize_t bma023_show_##name(struct device *dev, \
+               struct device_attribute *att, char *buf) \
+{ \
+       struct bma023_sensor *sensor = dev_get_drvdata(dev); \
+       return sprintf(buf, "%d\n", sensor->name); \
+} \
+static ssize_t bma023_store_##name(struct device *dev, \
+               struct device_attribute *attr, const char *buf, size_t count) \
+{ \
+       struct bma023_sensor *sensor = dev_get_drvdata(dev); \
+       unsigned long val; \
+       int ret; \
+       u8 result; \
+       if(!count) \
+               return count;\
+       ret = strict_strtoul(buf, 10, &val); \
+       if (!ret) { \
+               bma023_set_##name(sensor->client, val); \
+               result = bma023_get_##name(sensor->client); \
+               mutex_lock(&sensor->lock); \
+               sensor->name = result; \
+               mutex_unlock(&sensor->lock); \
+               return count; \
+       } \
+       else \
+               return ret; \
+} \
+static DEVICE_ATTR(name, S_IRUGO | S_IWUSR, \
+               bma023_show_##name, bma023_store_##name);
+
+BMA023_SYSFS(power_mode);
+BMA023_SYSFS(range);
+BMA023_SYSFS(bandwidth);
+BMA023_SYSFS(new_data_int);
+BMA023_SYSFS(hg_int);
+BMA023_SYSFS(lg_int);
+BMA023_SYSFS(lg_dur);
+BMA023_SYSFS(lg_thres);
+BMA023_SYSFS(lg_hyst);
+BMA023_SYSFS(hg_dur);
+BMA023_SYSFS(hg_thres);
+BMA023_SYSFS(hg_hyst);
+
+static struct attribute *bma023_attributes[] = {
+       &dev_attr_accel_data.attr,
+       &dev_attr_power_mode.attr,
+       &dev_attr_temperature.attr,
+       &dev_attr_range.attr,
+       &dev_attr_bandwidth.attr,
+       &dev_attr_new_data_int.attr,
+       &dev_attr_hg_int.attr,
+       &dev_attr_lg_int.attr,
+       &dev_attr_lg_dur.attr,
+       &dev_attr_lg_thres.attr,
+       &dev_attr_lg_hyst.attr,
+       &dev_attr_hg_dur.attr,
+       &dev_attr_hg_thres.attr,
+       &dev_attr_hg_hyst.attr,
+       NULL
+};
+
+static const struct attribute_group bma023_group = {
+       .attrs  = bma023_attributes,
+};
+
+static void bma023_work(struct work_struct *work)
+{
+       struct bma023_sensor *sensor =
+                       container_of(work, struct bma023_sensor, work);
+       bma023_read_xyz(sensor->client,
+               &sensor->data.x, &sensor->data.y, &sensor->data.z);
+       bma023_read_temperature(sensor->client, &sensor->data.temp);
+       mutex_lock(&sensor->lock);
+       input_report_abs(sensor->idev, ABS_X, sensor->data.x);
+       input_report_abs(sensor->idev, ABS_Y, sensor->data.y);
+       input_report_abs(sensor->idev, ABS_Z, sensor->data.z);
+       input_sync(sensor->idev);
+       mutex_unlock(&sensor->lock);
+       enable_irq(sensor->client->irq);
+}
+
+static irqreturn_t bma023_irq(int irq, void *dev_id)
+{
+       struct bma023_sensor *sensor = dev_id;
+       if (!work_pending(&sensor->work)) {
+               disable_irq_nosync(irq);
+               schedule_work(&sensor->work);
+       }
+       return IRQ_HANDLED;
+}
+
+static void bma023_initialize(struct bma023_sensor *sensor)
+{
+       bma023_set_range(sensor->client, sensor->range);
+       bma023_set_bandwidth(sensor->client, sensor->bandwidth);
+       bma023_set_new_data_int(sensor->client, sensor->new_data_int);
+       bma023_set_hg_dur(sensor->client, sensor->hg_dur);
+       bma023_set_hg_thres(sensor->client, sensor->hg_thres);
+       bma023_set_hg_hyst(sensor->client, sensor->hg_hyst);
+       bma023_set_lg_dur(sensor->client, sensor->lg_dur);
+       bma023_set_lg_thres(sensor->client, sensor->lg_thres);
+       bma023_set_lg_hyst(sensor->client, sensor->lg_hyst);
+       bma023_set_hg_int(sensor->client, sensor->hg_int);
+       bma023_set_lg_int(sensor->client, sensor->lg_int);
+       bma023_set_power_mode(sensor->client, sensor->power_mode);
+}
+
+static void bma023_unregister_input_device(struct bma023_sensor *sensor)
+{
+       struct i2c_client *client = sensor->client;
+       if (client->irq > 0)
+               free_irq(client->irq, sensor);
+       input_unregister_device(sensor->idev);
+       sensor->idev = NULL;
+}
+
+static int bma023_register_input_device(struct bma023_sensor *sensor)
+{
+       struct i2c_client *client = sensor->client;
+       struct input_dev *idev;
+       int ret;
+       sensor->idev = input_allocate_device();
+       idev = sensor->idev;
+       if (!idev) {
+               dev_err(&client->dev, "failed to allocate input device\n");
+               ret = -ENOMEM;
+               goto failed_alloc;
+       }
+       idev->name = "BMA023 Sensor";
+       idev->id.bustype = BUS_I2C;
+       idev->dev.parent = &client->dev;
+       idev->evbit[0] = BIT_MASK(EV_ABS);
+       input_set_abs_params(idev, ABS_X, BMA023_MIN_VALUE,
+                       BMA023_MAX_VALUE, 0, 0);
+       input_set_abs_params(idev, ABS_Y, BMA023_MIN_VALUE,
+                       BMA023_MAX_VALUE, 0, 0);
+       input_set_abs_params(idev, ABS_Z, BMA023_MIN_VALUE,
+                       BMA023_MAX_VALUE, 0, 0);
+       input_set_drvdata(idev, sensor);
+       ret = input_register_device(idev);
+       if (ret) {
+               dev_err(&client->dev, "failed to register input device\n");
+               goto failed_reg;
+       }
+       if (client->irq > 0) {
+               ret = request_irq(client->irq, bma023_irq, IRQF_TRIGGER_RISING,
+                               "bma023_int", sensor);
+               if (ret) {
+                       dev_err(&client->dev, "can't get IRQ %d, ret %d\n",
+                                       client->irq, ret);
+                       goto failed_irq;
+               }
+       }
+       return 0;
+failed_irq:
+       input_unregister_device(idev);
+       idev = NULL;
+failed_reg:
+       if (idev)
+               input_free_device(idev);
+failed_alloc:
+       return ret;
+}
+
+static int __devinit bma023_probe(struct i2c_client *client,
+                       const struct i2c_device_id *id)
+{
+       struct bma023_sensor *sensor;
+       struct bma023_platform_data *pdata;
+       int ret;
+       sensor = kzalloc(sizeof(struct bma023_sensor), GFP_KERNEL);
+       if (!sensor) {
+               dev_err(&client->dev, "failed to allocate driver data\n");
+               return -ENOMEM;
+       }
+       pdata = client->dev.platform_data;
+
+       sensor->client = client;
+       i2c_set_clientdata(client, sensor);
+
+       ret = bma023_read_reg(client, BMA023_CHIP_ID_REG);
+       if (ret < 0) {
+               dev_err(&client->dev, "failed to detect device\n");
+               goto failed_free;
+       }
+       if (ret != BMA023_CHIP_ID) {
+               dev_err(&client->dev, "unsupported chip id\n");
+               goto failed_free;
+       }
+
+       INIT_WORK(&sensor->work, bma023_work);
+       mutex_init(&sensor->lock);
+
+       ret = sysfs_create_group(&client->dev.kobj, &bma023_group);
+       if (ret) {
+               dev_err(&client->dev, "failed to create attribute group\n");
+               goto failed_free;
+       }
+
+       ret = bma023_register_input_device(sensor);
+       if (ret)
+               dev_err(&client->dev, "only provide sysfs\n");
+
+       if (pdata) {
+               sensor->range = pdata->range;
+               sensor->bandwidth = pdata->bandwidth;
+               sensor->new_data_int = pdata->new_data_int;
+               sensor->hg_int = pdata->hg_int;
+               sensor->lg_int = pdata->lg_int;
+               sensor->hg_dur = pdata->hg_dur;
+               sensor->hg_thres = pdata->hg_thres;
+               sensor->hg_hyst = pdata->hg_hyst;
+               sensor->lg_dur = pdata->lg_dur;
+               sensor->lg_thres = pdata->lg_thres;
+               sensor->lg_hyst = pdata->lg_hyst;
+       } else {
+               sensor->range = BMA023_DEFAULT_RANGE;
+               sensor->bandwidth = BMA023_DEFAULT_BANDWIDTH;
+               sensor->new_data_int = BMA023_DEFAULT_NEW_DATA_INT;
+               sensor->hg_int = BMA023_DEFAULT_HG_INT;
+               sensor->lg_int = BMA023_DEFAULT_LG_INT;
+               sensor->hg_dur = BMA023_DEFAULT_HG_DURATION;
+               sensor->hg_thres = BMA023_DEFAULT_HG_THRESHOLD;
+               sensor->hg_hyst = BMA023_DEFAULT_HG_HYST;
+               sensor->lg_dur = BMA023_DEFAULT_LG_DURATION;
+               sensor->lg_thres = BMA023_DEFAULT_LG_THRESHOLD;
+               sensor->lg_hyst = BMA023_DEFAULT_LG_HYST;
+       }
+       sensor->power_mode = 0;         /* sleep */
+
+       bma023_initialize(sensor);
+
+       dev_info(&client->dev, "%s registered\n", id->name);
+       return 0;
+
+failed_free:
+       kfree(sensor);
+       return ret;
+}
+
+static int __devexit bma023_remove(struct i2c_client *client)
+{
+       struct bma023_sensor *sensor = i2c_get_clientdata(client);
+       if(sensor->idev)
+               bma023_unregister_input_device(sensor);
+       sysfs_remove_group(&client->dev.kobj, &bma023_group);
+       kfree(sensor);
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int bma023_suspend(struct i2c_client *client, pm_message_t mesg)
+{
+       bma023_set_power_mode(client, 1);
+       return 0;
+}
+
+static int bma023_resume(struct i2c_client *client)
+{
+       bma023_set_power_mode(client, 0);
+       return 0;
+}
+
+#else
+#define bma023_suspend NULL
+#define bma023_resume NULL
+#endif
+
+#ifdef CONFIG_PM_RUNTIME
+static int bma023_runtime_suspend(struct device *dev)
+{
+       struct bma023_sensor *sensor = dev_get_drvdata(dev);
+       bma023_set_power_mode(sensor->client, 1);
+       return 0;
+}
+
+static int bma023_runtime_resume(struct device *dev)
+{
+       struct bma023_sensor *sensor = dev_get_drvdata(dev);
+       bma023_set_power_mode(sensor->client, 0);
+       return 0;
+}
+
+static const struct dev_pm_ops bma023_pm = {
+       .runtime_suspend = bma023_runtime_suspend,
+       .runtime_resume  = bma023_runtime_resume,
+};
+#endif
+
+static const struct i2c_device_id bma023_ids[] = {
+       { "bma023", 0 },
+       { "smb380", 1 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, bma023_ids);
+
+static struct i2c_driver bma023_i2c_driver = {
+       .driver = {
+               .name   = "bma023",
+#ifdef CONFIG_PM_RUNTIME
+               .pm             = &bma023_pm,
+#endif
+       },
+       .probe          = bma023_probe,
+       .remove         = __devexit_p(bma023_remove),
+       .suspend        = bma023_suspend,
+       .resume         = bma023_resume,
+       .id_table       = bma023_ids,
+};
+
+static int __init bma023_init(void)
+{
+       return i2c_add_driver(&bma023_i2c_driver);
+}
+module_init(bma023_init);
+
+static void __exit bma023_exit(void)
+{
+       i2c_del_driver(&bma023_i2c_driver);
+}
+module_exit(bma023_exit);
+
+MODULE_AUTHOR("Wistron Corp.");
+MODULE_DESCRIPTION("BMA023/SMB380 Tri-axis accelerometer driver");
+MODULE_LICENSE("GPL");
Index: linux-2.6.35/include/linux/bma023.h
===================================================================
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.35/include/linux/bma023.h 2011-01-07 20:13:10.000000000 +0800
@@ -0,0 +1,47 @@
+/*
+ * bma023.h - BMA023 Tri-axis accelerometer driver
+ *
+ * Copyright (c) 2010 Samsung Eletronics
+ * Kyungmin Park <[email protected]>
+ *
+ * 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.
+ *
+ */
+
+#ifndef _BMA023_H_
+#define _BMA023_H_
+
+enum scale_range {
+       RANGE_2G,
+       RANGE_4G,
+       RANGE_8G,
+};
+
+/* Used to setup the digital filtering bandwidth of ADC output */
+enum filter_bw {
+       BW_25HZ,
+       BW_50HZ,
+       BW_100HZ,
+       BW_190HZ,
+       BW_375HZ,
+       BW_750HZ,
+       BW_1500HZ,
+};
+
+struct bma023_platform_data {
+       enum scale_range range;
+       enum filter_bw bandwidth;
+       u8 new_data_int;
+       u8 hg_int;
+       u8 lg_int;
+       u8 lg_dur;
+       u8 lg_thres;
+       u8 lg_hyst;
+       u8 hg_dur;
+       u8 hg_thres;
+       u8 hg_hyst;
+};
+
+#endif /* _BMA023_H_ */
_______________________________________________
MeeGo-kernel mailing list
[email protected]
http://lists.meego.com/listinfo/meego-kernel

Reply via email to