* [PATCH] input: smb380 accelerometer driver
@ 2010-07-14 9:31 Donggeun Kim
2010-07-14 13:54 ` Christoph Fritz
0 siblings, 1 reply; 3+ messages in thread
From: Donggeun Kim @ 2010-07-14 9:31 UTC (permalink / raw)
To: linux-input; +Cc: kyungmin.park
This patch supports driver for BOSCH SMB380 and BMA023 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.
The result values range from -512 to 511 respectively.
The driver allows to set or get the device's properties by the sysfs attributes.
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 (default)
1: 50Hz
2: 100Hz
3: 190Hz
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
1: enable (default)
lg_int: generate an interrupt when the low-g threshold criteria are met
0: disable
1: enable (default)
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
Thank you.
- Donggeun
Signed-off-by: Donggeun Kim <dg77.kim@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
---
drivers/input/misc/Kconfig | 8 +
drivers/input/misc/Makefile | 1 +
drivers/input/misc/smb380.c | 710 +++++++++++++++++++++++++++++++++++++++++++
include/linux/smb380.h | 47 +++
4 files changed, 766 insertions(+), 0 deletions(-)
create mode 100644 drivers/input/misc/smb380.c
create mode 100644 include/linux/smb380.h
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index c44b9ea..f709790 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -390,4 +390,12 @@ config INPUT_PCAP
To compile this driver as a module, choose M here: the
module will be called pcap_keys.
+config INPUT_SMB380
+ tristate "SMB380 Triaxial acceleration sensor"
+ depends on I2C
+ help
+ This driver provides support for the Bosche Sensortec Triaxial
+ Acceleration Sensor IC, which provides measurements of acceleration
+ in prependicular axes as well as absolute temperature measurement.
+
endif
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index 71fe57d..bb0eaba 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -37,4 +37,5 @@ obj-$(CONFIG_INPUT_WINBOND_CIR) += winbond-cir.o
obj-$(CONFIG_INPUT_WISTRON_BTNS) += wistron_btns.o
obj-$(CONFIG_INPUT_WM831X_ON) += wm831x-on.o
obj-$(CONFIG_INPUT_YEALINK) += yealink.o
+obj-$(CONFIG_INPUT_SMB380) += smb380.o
diff --git a/drivers/input/misc/smb380.c b/drivers/input/misc/smb380.c
new file mode 100644
index 0000000..5a61ee7
--- /dev/null
+++ b/drivers/input/misc/smb380.c
@@ -0,0 +1,710 @@
+/*
+ * smb380.c - SMB380 Tri-axis accelerometer driver
+ *
+ * Copyright (C) 2010 Samsung Eletronics Co.Ltd
+ * Kim Kyuwon <q1.kim@samsung.com>
+ * Kyungmin Park <kyungmin.park@samsung.com>
+ * Donggeun Kim <dg77.kim@samsung.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.
+ *
+ */
+
+#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/smb380.h>
+
+#define SMB380_CHIP_ID_REG 0x00
+#define SMB380_X_LSB_REG 0x02
+#define SMB380_X_MSB_REG 0x03
+#define SMB380_Y_LSB_REG 0x04
+#define SMB380_Y_MSB_REG 0x05
+#define SMB380_Z_LSB_REG 0x06
+#define SMB380_Z_MSB_REG 0x07
+#define SMB380_TEMP_REG 0x08
+#define SMB380_CTRL1_REG 0x0a
+#define SMB380_CTRL2_REG 0x0b
+#define SMB380_SETTINGS1_REG 0x0c
+#define SMB380_SETTINGS2_REG 0x0d
+#define SMB380_SETTINGS3_REG 0x0e
+#define SMB380_SETTINGS4_REG 0x0f
+#define SMB380_SETTINGS5_REG 0x10
+#define SMB380_SETTINGS6_REG 0x11
+#define SMB380_RANGE_BW_REG 0x14
+#define SMB380_CONF2_REG 0x15
+
+#define SMB380_CHIP_ID 0x2
+
+#define SMB380_NEW_DATA_INT_SHIFT 5
+#define SMB380_NEW_DATA_INT_MASK (0x1 << 5)
+
+#define SMB380_RANGE_SHIFT 3
+#define SMB380_RANGE_MASK (0x3 << 3)
+#define SMB380_BANDWIDTH_SHIFT 0
+#define SMB380_BANDWIDTH_MASK (0x7)
+
+#define SMB380_HG_HYST_SHIFT 3
+#define SMB380_HG_HYST_MASK (0x7 << 3)
+#define SMB380_LG_HYST_SHIFT 0
+#define SMB380_LG_HYST_MASK (0x7)
+
+#define SMB380_HG_DUR_SHIFT (0x0)
+#define SMB380_HG_DUR_MASK (0xff)
+#define SMB380_HG_THRES_SHIFT (0x0)
+#define SMB380_HG_THRES_MASK (0xff)
+#define SMB380_LG_DUR_SHIFT (0x0)
+#define SMB380_LG_DUR_MASK (0xff)
+#define SMB380_LG_THRES_SHIFT (0x0)
+#define SMB380_LG_THRES_MASK (0xff)
+
+#define SMB380_ENABLE_HG_SHIFT 1
+#define SMB380_ENABLE_HG_MASK (0x1 << 1)
+#define SMB380_ENABLE_LG_SHIFT 0
+#define SMB380_ENABLE_LG_MASK (0x1)
+
+#define SMB380_SLEEP_SHIFT 0
+#define SMB380_SLEEP_MASK (0x1)
+
+#define SMB380_ACCEL_BITS 10
+#define SMB380_MAX_VALUE ((1 << ((SMB380_ACCEL_BITS) - 1)) - 1)
+#define SMB380_MIN_VALUE (-(1 << ((SMB380_ACCEL_BITS) - 1)))
+
+#define SMB380_DEFAULT_RANGE RANGE_2G
+#define SMB380_DEFAULT_BANDWIDTH BW_25HZ
+#define SMB380_DEFAULT_NEW_DATA_INT 0
+#define SMB380_DEFAULT_HG_INT 1
+#define SMB380_DEFAULT_LG_INT 1
+#define SMB380_DEFAULT_HG_DURATION 0x96
+#define SMB380_DEFAULT_HG_THRESHOLD 0xa0
+#define SMB380_DEFAULT_HG_HYST 0
+#define SMB380_DEFAULT_LG_DURATION 0x96
+#define SMB380_DEFAULT_LG_THRESHOLD 0x14
+#define SMB380_DEFAULT_LG_HYST 0
+
+struct smb380_data {
+ s16 x;
+ s16 y;
+ s16 z;
+ u8 temp;
+};
+
+struct smb380_sensor {
+ struct i2c_client *client;
+ struct device *dev;
+ struct input_dev *idev;
+ struct work_struct work;
+ struct mutex lock;
+
+ struct smb380_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;
+};
+
+static int smb380_write_reg(struct i2c_client *client, u8 reg, u8 val)
+{
+ int ret;
+
+ /*
+ * Accorting to the datasheet, the interrupt should be deactivated
+ * on the microprocessor 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 smb380_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 smb380_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 smb380_set_reg_bits(struct i2c_client *client,
+ int val, int shift, u8 mask, u8 reg)
+{
+ u8 data = smb380_read_reg(client, reg);
+
+ data = (data & ~mask) | ((val << shift) & mask);
+ return smb380_write_reg(client, reg, data);
+}
+
+static u8 smb380_get_reg_bits(struct i2c_client *client, int shift,
+ u8 mask, u8 reg)
+{
+ u8 data = smb380_read_reg(client, reg);
+
+ data = (data & mask) >> shift;
+ return data;
+}
+
+static int smb380_set_range(struct i2c_client *client, enum scale_range range)
+{
+ return smb380_set_reg_bits(client, range, SMB380_RANGE_SHIFT,
+ SMB380_RANGE_MASK, SMB380_RANGE_BW_REG);
+}
+
+static u8 smb380_get_range(struct i2c_client *client)
+{
+ return smb380_get_reg_bits(client, SMB380_RANGE_SHIFT,
+ SMB380_RANGE_MASK, SMB380_RANGE_BW_REG);
+}
+
+static int smb380_set_bandwidth(struct i2c_client *client, enum filter_bw bw)
+{
+ return smb380_set_reg_bits(client, bw, SMB380_BANDWIDTH_SHIFT,
+ SMB380_BANDWIDTH_MASK, SMB380_RANGE_BW_REG);
+}
+
+static u8 smb380_get_bandwidth(struct i2c_client *client)
+{
+ return smb380_get_reg_bits(client, SMB380_BANDWIDTH_SHIFT,
+ SMB380_BANDWIDTH_MASK, SMB380_RANGE_BW_REG);
+}
+
+static int smb380_set_new_data_int(struct i2c_client *client, u8 val)
+{
+ return smb380_set_reg_bits(client, val, SMB380_NEW_DATA_INT_SHIFT,
+ SMB380_NEW_DATA_INT_MASK, SMB380_CONF2_REG);
+}
+
+static u8 smb380_get_new_data_int(struct i2c_client *client)
+{
+ return smb380_get_reg_bits(client, SMB380_NEW_DATA_INT_SHIFT,
+ SMB380_NEW_DATA_INT_MASK, SMB380_CONF2_REG);
+}
+
+static int smb380_set_hg_int(struct i2c_client *client, u8 val)
+{
+ return smb380_set_reg_bits(client, val, SMB380_ENABLE_HG_SHIFT,
+ SMB380_ENABLE_HG_MASK, SMB380_CTRL2_REG);
+}
+
+static u8 smb380_get_hg_int(struct i2c_client *client)
+{
+ return smb380_get_reg_bits(client, SMB380_ENABLE_HG_SHIFT,
+ SMB380_ENABLE_HG_MASK, SMB380_CTRL2_REG);
+}
+
+static int smb380_set_lg_int(struct i2c_client *client, u8 val)
+{
+ return smb380_set_reg_bits(client, val, SMB380_ENABLE_LG_SHIFT,
+ SMB380_ENABLE_LG_MASK, SMB380_CTRL2_REG);
+}
+
+static u8 smb380_get_lg_int(struct i2c_client *client)
+{
+ return smb380_get_reg_bits(client, SMB380_ENABLE_LG_SHIFT,
+ SMB380_ENABLE_LG_MASK, SMB380_CTRL2_REG);
+}
+
+static int smb380_set_lg_dur(struct i2c_client *client, u8 dur)
+{
+ return smb380_set_reg_bits(client, dur, SMB380_LG_DUR_SHIFT,
+ SMB380_LG_DUR_MASK, SMB380_SETTINGS2_REG);
+}
+
+static u8 smb380_get_lg_dur(struct i2c_client *client)
+{
+ return smb380_get_reg_bits(client, SMB380_LG_DUR_SHIFT,
+ SMB380_LG_DUR_MASK, SMB380_SETTINGS2_REG);
+}
+
+static int smb380_set_lg_thres(struct i2c_client *client, u8 thres)
+{
+ return smb380_set_reg_bits(client, thres, SMB380_LG_THRES_SHIFT,
+ SMB380_LG_THRES_MASK, SMB380_SETTINGS1_REG);
+}
+
+static u8 smb380_get_lg_thres(struct i2c_client *client)
+{
+ return smb380_get_reg_bits(client, SMB380_LG_THRES_SHIFT,
+ SMB380_LG_THRES_MASK, SMB380_SETTINGS1_REG);
+}
+
+static int smb380_set_lg_hyst(struct i2c_client *client, u8 hyst)
+{
+ return smb380_set_reg_bits(client, hyst, SMB380_LG_HYST_SHIFT,
+ SMB380_LG_HYST_MASK, SMB380_SETTINGS6_REG);
+}
+
+static u8 smb380_get_lg_hyst(struct i2c_client *client)
+{
+ return smb380_get_reg_bits(client, SMB380_LG_HYST_SHIFT,
+ SMB380_LG_HYST_MASK, SMB380_SETTINGS6_REG);
+}
+
+static int smb380_set_hg_dur(struct i2c_client *client, u8 dur)
+{
+ return smb380_set_reg_bits(client, dur, SMB380_HG_DUR_SHIFT,
+ SMB380_HG_DUR_MASK, SMB380_SETTINGS4_REG);
+}
+
+static u8 smb380_get_hg_dur(struct i2c_client *client)
+{
+ return smb380_get_reg_bits(client, SMB380_HG_DUR_SHIFT,
+ SMB380_HG_DUR_MASK, SMB380_SETTINGS4_REG);
+}
+
+static int smb380_set_hg_thres(struct i2c_client *client, u8 thres)
+{
+ return smb380_set_reg_bits(client, thres, SMB380_HG_THRES_SHIFT,
+ SMB380_HG_THRES_MASK, SMB380_SETTINGS3_REG);
+}
+
+static u8 smb380_get_hg_thres(struct i2c_client *client)
+{
+ return smb380_get_reg_bits(client, SMB380_HG_THRES_SHIFT,
+ SMB380_HG_THRES_MASK, SMB380_SETTINGS3_REG);
+}
+
+static int smb380_set_hg_hyst(struct i2c_client *client, u8 hyst)
+{
+ return smb380_set_reg_bits(client, hyst, SMB380_HG_HYST_SHIFT,
+ SMB380_HG_HYST_MASK, SMB380_SETTINGS6_REG);
+}
+
+static u8 smb380_get_hg_hyst(struct i2c_client *client)
+{
+ return smb380_get_reg_bits(client, SMB380_HG_HYST_SHIFT,
+ SMB380_HG_HYST_MASK, SMB380_SETTINGS6_REG);
+}
+
+static int smb380_set_sleep(struct i2c_client *client, u8 val)
+{
+ return smb380_set_reg_bits(client, val, SMB380_SLEEP_SHIFT,
+ SMB380_SLEEP_MASK, SMB380_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 inline void smb380_correct_accel_sign(s16 *val)
+{
+ *val <<= (sizeof(s16) * BITS_PER_BYTE - SMB380_ACCEL_BITS);
+ *val >>= (sizeof(s16) * BITS_PER_BYTE - SMB380_ACCEL_BITS);
+}
+
+static void smb380_merge_register_values(struct i2c_client *client, s16 *val,
+ u8 lsb, u8 msb)
+{
+ *val = (msb << 2) | (lsb >> 6);
+ smb380_correct_accel_sign(val);
+}
+
+/*
+ * Read 8 bit temperature.
+ * An output of 0 equals -30C, 1 LSB equals 0.5C
+ */
+static void smb380_read_temperature(struct i2c_client *client, u8 *temper)
+{
+ *temper = smb380_read_reg(client, SMB380_TEMP_REG);
+
+ dev_dbg(&client->dev, "%s: %d\n", __func__, *temper);
+}
+
+static void smb380_read_xyz(struct i2c_client *client,
+ s16 *x, s16 *y, s16 *z)
+{
+ u8 buffer[6];
+ buffer[0] = SMB380_X_LSB_REG;
+ smb380_xyz_read_reg(client, buffer, 6);
+
+ smb380_merge_register_values(client, x, buffer[0], buffer[1]);
+ smb380_merge_register_values(client, y, buffer[2], buffer[3]);
+ smb380_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 smb380_show_xyz(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct smb380_sensor *sensor = dev_get_drvdata(dev);
+
+ mutex_lock(&sensor->lock);
+ smb380_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(xyz, S_IRUGO, smb380_show_xyz, NULL);
+
+static ssize_t smb380_show_temper(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct smb380_sensor *sensor = dev_get_drvdata(dev);
+
+ mutex_lock(&sensor->lock);
+ smb380_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, smb380_show_temper, NULL);
+
+#define SMB380_ADJUST(name) \
+static ssize_t smb380_show_##name(struct device *dev, \
+ struct device_attribute *att, char *buf) \
+{ \
+ struct smb380_sensor *sensor = dev_get_drvdata(dev); \
+ \
+ return sprintf(buf, "%d\n", sensor->name); \
+} \
+static ssize_t smb380_store_##name(struct device *dev, \
+ struct device_attribute *attr, const char *buf, size_t count) \
+{ \
+ struct smb380_sensor *sensor = dev_get_drvdata(dev); \
+ unsigned long val; \
+ int ret; \
+ u8 result; \
+ \
+ ret = strict_strtoul(buf, 10, &val); \
+ if (!ret) { \
+ smb380_set_##name(sensor->client, val); \
+ result = smb380_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, \
+ smb380_show_##name, smb380_store_##name);
+
+SMB380_ADJUST(range);
+SMB380_ADJUST(bandwidth);
+SMB380_ADJUST(new_data_int);
+SMB380_ADJUST(hg_int);
+SMB380_ADJUST(lg_int);
+SMB380_ADJUST(lg_dur);
+SMB380_ADJUST(lg_thres);
+SMB380_ADJUST(lg_hyst);
+SMB380_ADJUST(hg_dur);
+SMB380_ADJUST(hg_thres);
+SMB380_ADJUST(hg_hyst);
+
+static struct attribute *smb380_attributes[] = {
+ &dev_attr_xyz.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 smb380_group = {
+ .attrs = smb380_attributes,
+};
+
+static void smb380_work(struct work_struct *work)
+{
+ struct smb380_sensor *sensor =
+ container_of(work, struct smb380_sensor, work);
+
+ smb380_read_xyz(sensor->client,
+ &sensor->data.x, &sensor->data.y, &sensor->data.z);
+ smb380_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 smb380_irq(int irq, void *dev_id)
+{
+ struct smb380_sensor *sensor = dev_id;
+
+ if (!work_pending(&sensor->work)) {
+ disable_irq_nosync(irq);
+ schedule_work(&sensor->work);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void smb380_initialize(struct smb380_sensor *sensor)
+{
+ smb380_set_range(sensor->client, sensor->range);
+ smb380_set_bandwidth(sensor->client, sensor->bandwidth);
+ smb380_set_new_data_int(sensor->client, sensor->new_data_int);
+ smb380_set_hg_dur(sensor->client, sensor->hg_dur);
+ smb380_set_hg_thres(sensor->client, sensor->hg_thres);
+ smb380_set_hg_hyst(sensor->client, sensor->hg_hyst);
+ smb380_set_lg_dur(sensor->client, sensor->lg_dur);
+ smb380_set_lg_thres(sensor->client, sensor->lg_thres);
+ smb380_set_lg_hyst(sensor->client, sensor->lg_hyst);
+ smb380_set_hg_int(sensor->client, sensor->hg_int);
+ smb380_set_lg_int(sensor->client, sensor->lg_int);
+}
+
+static void smb380_unregister_input_device(struct smb380_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 smb380_register_input_device(struct smb380_sensor *sensor)
+{
+ struct i2c_client *client = sensor->client;
+ struct input_dev *idev;
+ int ret;
+
+ idev = sensor->idev = input_allocate_device();
+ if (!idev) {
+ dev_err(&client->dev, "allocating input device is failed\n");
+ ret = -ENOMEM;
+ goto failed_alloc;
+ }
+
+ idev->name = "SMB380 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, SMB380_MIN_VALUE,
+ SMB380_MAX_VALUE, 0, 0);
+ input_set_abs_params(idev, ABS_Y, SMB380_MIN_VALUE,
+ SMB380_MAX_VALUE, 0, 0);
+ input_set_abs_params(idev, ABS_Z, SMB380_MIN_VALUE,
+ SMB380_MAX_VALUE, 0, 0);
+
+ input_set_drvdata(idev, sensor);
+
+ ret = input_register_device(idev);
+ if (ret) {
+ dev_err(&client->dev, "registering input device is failed\n");
+ goto failed_reg;
+ }
+
+ if (client->irq > 0) {
+ ret = request_irq(client->irq, smb380_irq, IRQF_TRIGGER_RISING,
+ "smb380 accelerometer", 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 smb380_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct smb380_sensor *sensor;
+ struct smb380_platform_data *pdata;
+ int ret;
+
+ sensor = kzalloc(sizeof(struct smb380_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 = smb380_read_reg(client, SMB380_CHIP_ID_REG);
+ if (ret < 0) {
+ dev_err(&client->dev, "failed to detect device\n");
+ goto failed_free;
+ }
+ if (ret != SMB380_CHIP_ID) {
+ dev_err(&client->dev, "the chip id is missmatched\n");
+ goto failed_free;
+ }
+
+ INIT_WORK(&sensor->work, smb380_work);
+ mutex_init(&sensor->lock);
+
+ ret = sysfs_create_group(&client->dev.kobj, &smb380_group);
+ if (ret) {
+ dev_err(&client->dev, "creating attribute group is failed\n");
+ goto failed_free;
+ }
+
+ ret = smb380_register_input_device(sensor);
+ if (ret) {
+ dev_err(&client->dev, "registering input device is failed\n");
+ goto failed_remove_sysfs;
+ }
+
+ 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 = SMB380_DEFAULT_RANGE;
+ sensor->bandwidth = SMB380_DEFAULT_BANDWIDTH;
+ sensor->new_data_int = SMB380_DEFAULT_NEW_DATA_INT;
+ sensor->hg_int = SMB380_DEFAULT_HG_INT;
+ sensor->lg_int = SMB380_DEFAULT_LG_INT;
+ sensor->hg_dur = SMB380_DEFAULT_HG_DURATION;
+ sensor->hg_thres = SMB380_DEFAULT_HG_THRESHOLD;
+ sensor->hg_hyst = SMB380_DEFAULT_HG_HYST;
+ sensor->lg_dur = SMB380_DEFAULT_LG_DURATION;
+ sensor->lg_thres = SMB380_DEFAULT_LG_THRESHOLD;
+ sensor->lg_hyst = SMB380_DEFAULT_LG_HYST;
+ }
+
+ smb380_initialize(sensor);
+
+ dev_info(&client->dev, "%s registered\n", id->name);
+ return 0;
+
+failed_remove_sysfs:
+ sysfs_remove_group(&client->dev.kobj, &smb380_group);
+failed_free:
+ kfree(sensor);
+ return ret;
+}
+
+static int __devexit smb380_remove(struct i2c_client *client)
+{
+ struct smb380_sensor *sensor = i2c_get_clientdata(client);
+
+ smb380_unregister_input_device(sensor);
+ sysfs_remove_group(&client->dev.kobj, &smb380_group);
+ kfree(sensor);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int smb380_suspend(struct i2c_client *client, pm_message_t mesg)
+{
+ smb380_set_sleep(client, 1);
+ return 0;
+}
+
+static int smb380_resume(struct i2c_client *client)
+{
+ smb380_set_sleep(client, 0);
+ return 0;
+}
+
+#else
+#define smb380_suspend NULL
+#define smb380_resume NULL
+#endif
+
+static const struct i2c_device_id smb380_ids[] = {
+ { "smb380", 0 },
+ { "bma023", 1 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, smb380_ids);
+
+static struct i2c_driver smb380_i2c_driver = {
+ .driver = {
+ .name = "smb380",
+ },
+ .probe = smb380_probe,
+ .remove = __devexit_p(smb380_remove),
+ .suspend = smb380_suspend,
+ .resume = smb380_resume,
+ .id_table = smb380_ids,
+};
+
+static int __init smb380_init(void)
+{
+ return i2c_add_driver(&smb380_i2c_driver);
+}
+module_init(smb380_init);
+
+static void __exit smb380_exit(void)
+{
+ i2c_del_driver(&smb380_i2c_driver);
+}
+module_exit(smb380_exit);
+
+MODULE_AUTHOR("Kim Kyuwon <q1.kim@samsung.com>");
+MODULE_DESCRIPTION("SMB380/BMA023 Tri-axis accelerometer driver");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/smb380.h b/include/linux/smb380.h
new file mode 100644
index 0000000..e93c52d
--- /dev/null
+++ b/include/linux/smb380.h
@@ -0,0 +1,47 @@
+/*
+ * smb380.h - SMB380 Tri-axis accelerometer driver
+ *
+ * Copyright (c) 2010 Samsung Eletronics
+ * Kyungmin Park <kyungmin.park@samsung.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.
+ *
+ */
+
+#ifndef _SMB380_H_
+#define _SMB380_H_
+
+enum scale_range {
+ RANGE_2G,
+ RANGE_4G,
+ RANGE_8G,
+};
+
+/* Used to setup the digital filtering bandwitdh of ADC output */
+enum filter_bw {
+ BW_25HZ,
+ BW_50HZ,
+ BW_100HZ,
+ BW_190HZ,
+ BW_375HZ,
+ BW_750HZ,
+ BW_1500HZ,
+};
+
+struct smb380_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 /* _SMB380_H_ */
--
1.6.3.3
^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [PATCH] input: smb380 accelerometer driver
2010-07-14 9:31 [PATCH] input: smb380 accelerometer driver Donggeun Kim
@ 2010-07-14 13:54 ` Christoph Fritz
2010-07-15 8:58 ` [PATCH v2] " Donggeun Kim
0 siblings, 1 reply; 3+ messages in thread
From: Christoph Fritz @ 2010-07-14 13:54 UTC (permalink / raw)
To: Donggeun Kim
Cc: linux-input, kyungmin.park, Dmitry Torokhov, Mike Frysinger,
Samuel Ortiz, Bryan Wu, Haojian Zhuang
Hi Kim,
with scripts/get_maintainer.pl <patch> you get email-ads to all whom it
may concern.
On Wed, 2010-07-14 at 18:31 +0900, Donggeun Kim wrote:
> This patch supports driver for BOSCH SMB380 and BMA023 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.
> The result values range from -512 to 511 respectively.
>
> The driver allows to set or get the device's properties by the sysfs attributes.
there are pretty long lines for a git comment
>
> 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 (default)
> 1: 50Hz
> 2: 100Hz
> 3: 190Hz
> 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
> 1: enable (default)
> lg_int: generate an interrupt when the low-g threshold criteria are met
> 0: disable
> 1: enable (default)
> 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
>
> Thank you.
> - Donggeun
>
> Signed-off-by: Donggeun Kim <dg77.kim@samsung.com>
> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
> ---
> drivers/input/misc/Kconfig | 8 +
> drivers/input/misc/Makefile | 1 +
> drivers/input/misc/smb380.c | 710 +++++++++++++++++++++++++++++++++++++++++++
> include/linux/smb380.h | 47 +++
> 4 files changed, 766 insertions(+), 0 deletions(-)
> create mode 100644 drivers/input/misc/smb380.c
> create mode 100644 include/linux/smb380.h
>
> diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
> index c44b9ea..f709790 100644
> --- a/drivers/input/misc/Kconfig
> +++ b/drivers/input/misc/Kconfig
> @@ -390,4 +390,12 @@ config INPUT_PCAP
> To compile this driver as a module, choose M here: the
> module will be called pcap_keys.
>
> +config INPUT_SMB380
> + tristate "SMB380 Triaxial acceleration sensor"
> + depends on I2C
> + help
> + This driver provides support for the Bosche Sensortec Triaxial
I live near Feuerbach, the company is definitely called "Bosch".
you could also mention the device ids (SMB380 and BMA023) and it's bus
type (i2c)
> + Acceleration Sensor IC, which provides measurements of acceleration
> + in prependicular axes as well as absolute temperature measurement.
typo: perpendicular
add:
"To compile this driver as a module, choose M here: the
module will be called smb380"
> +
> endif
> diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
> index 71fe57d..bb0eaba 100644
> --- a/drivers/input/misc/Makefile
> +++ b/drivers/input/misc/Makefile
> @@ -37,4 +37,5 @@ obj-$(CONFIG_INPUT_WINBOND_CIR) += winbond-cir.o
> obj-$(CONFIG_INPUT_WISTRON_BTNS) += wistron_btns.o
> obj-$(CONFIG_INPUT_WM831X_ON) += wm831x-on.o
> obj-$(CONFIG_INPUT_YEALINK) += yealink.o
> +obj-$(CONFIG_INPUT_SMB380) += smb380.o
please keep the alphabetic order in this file
>
> diff --git a/drivers/input/misc/smb380.c b/drivers/input/misc/smb380.c
> new file mode 100644
> index 0000000..5a61ee7
> --- /dev/null
> +++ b/drivers/input/misc/smb380.c
> @@ -0,0 +1,710 @@
> +/*
> + * smb380.c - SMB380 Tri-axis accelerometer driver
> + *
> + * Copyright (C) 2010 Samsung Eletronics Co.Ltd
> + * Kim Kyuwon <q1.kim@samsung.com>
> + * Kyungmin Park <kyungmin.park@samsung.com>
> + * Donggeun Kim <dg77.kim@samsung.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.
> + *
> + */
> +
> +#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/smb380.h>
> +
> +#define SMB380_CHIP_ID_REG 0x00
> +#define SMB380_X_LSB_REG 0x02
> +#define SMB380_X_MSB_REG 0x03
> +#define SMB380_Y_LSB_REG 0x04
> +#define SMB380_Y_MSB_REG 0x05
> +#define SMB380_Z_LSB_REG 0x06
> +#define SMB380_Z_MSB_REG 0x07
> +#define SMB380_TEMP_REG 0x08
> +#define SMB380_CTRL1_REG 0x0a
> +#define SMB380_CTRL2_REG 0x0b
> +#define SMB380_SETTINGS1_REG 0x0c
> +#define SMB380_SETTINGS2_REG 0x0d
> +#define SMB380_SETTINGS3_REG 0x0e
> +#define SMB380_SETTINGS4_REG 0x0f
> +#define SMB380_SETTINGS5_REG 0x10
> +#define SMB380_SETTINGS6_REG 0x11
> +#define SMB380_RANGE_BW_REG 0x14
> +#define SMB380_CONF2_REG 0x15
> +
> +#define SMB380_CHIP_ID 0x2
> +
> +#define SMB380_NEW_DATA_INT_SHIFT 5
> +#define SMB380_NEW_DATA_INT_MASK (0x1 << 5)
> +
> +#define SMB380_RANGE_SHIFT 3
> +#define SMB380_RANGE_MASK (0x3 << 3)
> +#define SMB380_BANDWIDTH_SHIFT 0
> +#define SMB380_BANDWIDTH_MASK (0x7)
> +
> +#define SMB380_HG_HYST_SHIFT 3
> +#define SMB380_HG_HYST_MASK (0x7 << 3)
> +#define SMB380_LG_HYST_SHIFT 0
> +#define SMB380_LG_HYST_MASK (0x7)
> +
> +#define SMB380_HG_DUR_SHIFT (0x0)
> +#define SMB380_HG_DUR_MASK (0xff)
> +#define SMB380_HG_THRES_SHIFT (0x0)
> +#define SMB380_HG_THRES_MASK (0xff)
> +#define SMB380_LG_DUR_SHIFT (0x0)
> +#define SMB380_LG_DUR_MASK (0xff)
> +#define SMB380_LG_THRES_SHIFT (0x0)
> +#define SMB380_LG_THRES_MASK (0xff)
> +
> +#define SMB380_ENABLE_HG_SHIFT 1
> +#define SMB380_ENABLE_HG_MASK (0x1 << 1)
> +#define SMB380_ENABLE_LG_SHIFT 0
> +#define SMB380_ENABLE_LG_MASK (0x1)
> +
> +#define SMB380_SLEEP_SHIFT 0
> +#define SMB380_SLEEP_MASK (0x1)
> +
> +#define SMB380_ACCEL_BITS 10
> +#define SMB380_MAX_VALUE ((1 << ((SMB380_ACCEL_BITS) - 1)) - 1)
> +#define SMB380_MIN_VALUE (-(1 << ((SMB380_ACCEL_BITS) - 1)))
> +
> +#define SMB380_DEFAULT_RANGE RANGE_2G
> +#define SMB380_DEFAULT_BANDWIDTH BW_25HZ
> +#define SMB380_DEFAULT_NEW_DATA_INT 0
> +#define SMB380_DEFAULT_HG_INT 1
> +#define SMB380_DEFAULT_LG_INT 1
> +#define SMB380_DEFAULT_HG_DURATION 0x96
> +#define SMB380_DEFAULT_HG_THRESHOLD 0xa0
> +#define SMB380_DEFAULT_HG_HYST 0
> +#define SMB380_DEFAULT_LG_DURATION 0x96
> +#define SMB380_DEFAULT_LG_THRESHOLD 0x14
> +#define SMB380_DEFAULT_LG_HYST 0
> +
> +struct smb380_data {
> + s16 x;
> + s16 y;
> + s16 z;
> + u8 temp;
> +};
> +
> +struct smb380_sensor {
> + struct i2c_client *client;
> + struct device *dev;
> + struct input_dev *idev;
> + struct work_struct work;
> + struct mutex lock;
> +
> + struct smb380_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;
> +};
> +
> +static int smb380_write_reg(struct i2c_client *client, u8 reg, u8 val)
> +{
> + int ret;
> +
> + /*
> + * Accorting to the datasheet, the interrupt should be deactivated
typo: According
> + * on the microprocessor side when write sequences operate
> + */
I would prefer: ... on the host-side ...
> + 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 smb380_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 smb380_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 smb380_set_reg_bits(struct i2c_client *client,
> + int val, int shift, u8 mask, u8 reg)
> +{
> + u8 data = smb380_read_reg(client, reg);
> +
> + data = (data & ~mask) | ((val << shift) & mask);
> + return smb380_write_reg(client, reg, data);
> +}
> +
> +static u8 smb380_get_reg_bits(struct i2c_client *client, int shift,
> + u8 mask, u8 reg)
> +{
> + u8 data = smb380_read_reg(client, reg);
> +
> + data = (data & mask) >> shift;
> + return data;
> +}
> +
> +static int smb380_set_range(struct i2c_client *client, enum scale_range range)
> +{
> + return smb380_set_reg_bits(client, range, SMB380_RANGE_SHIFT,
> + SMB380_RANGE_MASK, SMB380_RANGE_BW_REG);
> +}
> +
> +static u8 smb380_get_range(struct i2c_client *client)
> +{
> + return smb380_get_reg_bits(client, SMB380_RANGE_SHIFT,
> + SMB380_RANGE_MASK, SMB380_RANGE_BW_REG);
> +}
> +
> +static int smb380_set_bandwidth(struct i2c_client *client, enum filter_bw bw)
> +{
> + return smb380_set_reg_bits(client, bw, SMB380_BANDWIDTH_SHIFT,
> + SMB380_BANDWIDTH_MASK, SMB380_RANGE_BW_REG);
> +}
> +
> +static u8 smb380_get_bandwidth(struct i2c_client *client)
> +{
> + return smb380_get_reg_bits(client, SMB380_BANDWIDTH_SHIFT,
> + SMB380_BANDWIDTH_MASK, SMB380_RANGE_BW_REG);
> +}
> +
> +static int smb380_set_new_data_int(struct i2c_client *client, u8 val)
> +{
> + return smb380_set_reg_bits(client, val, SMB380_NEW_DATA_INT_SHIFT,
> + SMB380_NEW_DATA_INT_MASK, SMB380_CONF2_REG);
> +}
> +
> +static u8 smb380_get_new_data_int(struct i2c_client *client)
> +{
> + return smb380_get_reg_bits(client, SMB380_NEW_DATA_INT_SHIFT,
> + SMB380_NEW_DATA_INT_MASK, SMB380_CONF2_REG);
> +}
> +
> +static int smb380_set_hg_int(struct i2c_client *client, u8 val)
> +{
> + return smb380_set_reg_bits(client, val, SMB380_ENABLE_HG_SHIFT,
> + SMB380_ENABLE_HG_MASK, SMB380_CTRL2_REG);
> +}
> +
> +static u8 smb380_get_hg_int(struct i2c_client *client)
> +{
> + return smb380_get_reg_bits(client, SMB380_ENABLE_HG_SHIFT,
> + SMB380_ENABLE_HG_MASK, SMB380_CTRL2_REG);
> +}
> +
> +static int smb380_set_lg_int(struct i2c_client *client, u8 val)
> +{
> + return smb380_set_reg_bits(client, val, SMB380_ENABLE_LG_SHIFT,
> + SMB380_ENABLE_LG_MASK, SMB380_CTRL2_REG);
> +}
> +
> +static u8 smb380_get_lg_int(struct i2c_client *client)
> +{
> + return smb380_get_reg_bits(client, SMB380_ENABLE_LG_SHIFT,
> + SMB380_ENABLE_LG_MASK, SMB380_CTRL2_REG);
> +}
> +
> +static int smb380_set_lg_dur(struct i2c_client *client, u8 dur)
> +{
> + return smb380_set_reg_bits(client, dur, SMB380_LG_DUR_SHIFT,
> + SMB380_LG_DUR_MASK, SMB380_SETTINGS2_REG);
> +}
> +
> +static u8 smb380_get_lg_dur(struct i2c_client *client)
> +{
> + return smb380_get_reg_bits(client, SMB380_LG_DUR_SHIFT,
> + SMB380_LG_DUR_MASK, SMB380_SETTINGS2_REG);
> +}
> +
> +static int smb380_set_lg_thres(struct i2c_client *client, u8 thres)
> +{
> + return smb380_set_reg_bits(client, thres, SMB380_LG_THRES_SHIFT,
> + SMB380_LG_THRES_MASK, SMB380_SETTINGS1_REG);
> +}
> +
> +static u8 smb380_get_lg_thres(struct i2c_client *client)
> +{
> + return smb380_get_reg_bits(client, SMB380_LG_THRES_SHIFT,
> + SMB380_LG_THRES_MASK, SMB380_SETTINGS1_REG);
> +}
> +
> +static int smb380_set_lg_hyst(struct i2c_client *client, u8 hyst)
> +{
> + return smb380_set_reg_bits(client, hyst, SMB380_LG_HYST_SHIFT,
> + SMB380_LG_HYST_MASK, SMB380_SETTINGS6_REG);
> +}
> +
> +static u8 smb380_get_lg_hyst(struct i2c_client *client)
> +{
> + return smb380_get_reg_bits(client, SMB380_LG_HYST_SHIFT,
> + SMB380_LG_HYST_MASK, SMB380_SETTINGS6_REG);
> +}
> +
> +static int smb380_set_hg_dur(struct i2c_client *client, u8 dur)
> +{
> + return smb380_set_reg_bits(client, dur, SMB380_HG_DUR_SHIFT,
> + SMB380_HG_DUR_MASK, SMB380_SETTINGS4_REG);
> +}
> +
> +static u8 smb380_get_hg_dur(struct i2c_client *client)
> +{
> + return smb380_get_reg_bits(client, SMB380_HG_DUR_SHIFT,
> + SMB380_HG_DUR_MASK, SMB380_SETTINGS4_REG);
> +}
> +
> +static int smb380_set_hg_thres(struct i2c_client *client, u8 thres)
> +{
> + return smb380_set_reg_bits(client, thres, SMB380_HG_THRES_SHIFT,
> + SMB380_HG_THRES_MASK, SMB380_SETTINGS3_REG);
> +}
> +
> +static u8 smb380_get_hg_thres(struct i2c_client *client)
> +{
> + return smb380_get_reg_bits(client, SMB380_HG_THRES_SHIFT,
> + SMB380_HG_THRES_MASK, SMB380_SETTINGS3_REG);
> +}
> +
> +static int smb380_set_hg_hyst(struct i2c_client *client, u8 hyst)
> +{
> + return smb380_set_reg_bits(client, hyst, SMB380_HG_HYST_SHIFT,
> + SMB380_HG_HYST_MASK, SMB380_SETTINGS6_REG);
> +}
> +
> +static u8 smb380_get_hg_hyst(struct i2c_client *client)
> +{
> + return smb380_get_reg_bits(client, SMB380_HG_HYST_SHIFT,
> + SMB380_HG_HYST_MASK, SMB380_SETTINGS6_REG);
> +}
> +
> +static int smb380_set_sleep(struct i2c_client *client, u8 val)
> +{
> + return smb380_set_reg_bits(client, val, SMB380_SLEEP_SHIFT,
> + SMB380_SLEEP_MASK, SMB380_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 inline void smb380_correct_accel_sign(s16 *val)
> +{
> + *val <<= (sizeof(s16) * BITS_PER_BYTE - SMB380_ACCEL_BITS);
> + *val >>= (sizeof(s16) * BITS_PER_BYTE - SMB380_ACCEL_BITS);
> +}
to quote Documentation/CodingStyle:
"Generally, inline functions are preferable to macros resembling
functions."
> +
> +static void smb380_merge_register_values(struct i2c_client *client, s16 *val,
> + u8 lsb, u8 msb)
> +{
> + *val = (msb << 2) | (lsb >> 6);
> + smb380_correct_accel_sign(val);
> +}
maybe this too?
> +
> +/*
> + * Read 8 bit temperature.
> + * An output of 0 equals -30C, 1 LSB equals 0.5C
> + */
> +static void smb380_read_temperature(struct i2c_client *client, u8 *temper)
> +{
> + *temper = smb380_read_reg(client, SMB380_TEMP_REG);
> +
> + dev_dbg(&client->dev, "%s: %d\n", __func__, *temper);
> +}
> +
> +static void smb380_read_xyz(struct i2c_client *client,
> + s16 *x, s16 *y, s16 *z)
> +{
> + u8 buffer[6];
> + buffer[0] = SMB380_X_LSB_REG;
> + smb380_xyz_read_reg(client, buffer, 6);
> +
> + smb380_merge_register_values(client, x, buffer[0], buffer[1]);
> + smb380_merge_register_values(client, y, buffer[2], buffer[3]);
> + smb380_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 smb380_show_xyz(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + struct smb380_sensor *sensor = dev_get_drvdata(dev);
> +
> + mutex_lock(&sensor->lock);
> + smb380_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(xyz, S_IRUGO, smb380_show_xyz, NULL);
> +
> +static ssize_t smb380_show_temper(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + struct smb380_sensor *sensor = dev_get_drvdata(dev);
> +
> + mutex_lock(&sensor->lock);
> + smb380_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, smb380_show_temper, NULL);
> +
> +#define SMB380_ADJUST(name) \
> +static ssize_t smb380_show_##name(struct device *dev, \
> + struct device_attribute *att, char *buf) \
> +{ \
> + struct smb380_sensor *sensor = dev_get_drvdata(dev); \
> + \
> + return sprintf(buf, "%d\n", sensor->name); \
> +} \
> +static ssize_t smb380_store_##name(struct device *dev, \
> + struct device_attribute *attr, const char *buf, size_t count) \
> +{ \
> + struct smb380_sensor *sensor = dev_get_drvdata(dev); \
> + unsigned long val; \
> + int ret; \
> + u8 result; \
> + \
> + ret = strict_strtoul(buf, 10, &val); \
> + if (!ret) { \
> + smb380_set_##name(sensor->client, val); \
> + result = smb380_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, \
> + smb380_show_##name, smb380_store_##name);
> +
> +SMB380_ADJUST(range);
> +SMB380_ADJUST(bandwidth);
> +SMB380_ADJUST(new_data_int);
> +SMB380_ADJUST(hg_int);
> +SMB380_ADJUST(lg_int);
> +SMB380_ADJUST(lg_dur);
> +SMB380_ADJUST(lg_thres);
> +SMB380_ADJUST(lg_hyst);
> +SMB380_ADJUST(hg_dur);
> +SMB380_ADJUST(hg_thres);
> +SMB380_ADJUST(hg_hyst);
> +
> +static struct attribute *smb380_attributes[] = {
> + &dev_attr_xyz.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 smb380_group = {
> + .attrs = smb380_attributes,
> +};
> +
> +static void smb380_work(struct work_struct *work)
> +{
> + struct smb380_sensor *sensor =
> + container_of(work, struct smb380_sensor, work);
> +
> + smb380_read_xyz(sensor->client,
> + &sensor->data.x, &sensor->data.y, &sensor->data.z);
> + smb380_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 smb380_irq(int irq, void *dev_id)
> +{
> + struct smb380_sensor *sensor = dev_id;
> +
> + if (!work_pending(&sensor->work)) {
> + disable_irq_nosync(irq);
> + schedule_work(&sensor->work);
> + }
> +
> + return IRQ_HANDLED;
> +}
> +
> +static void smb380_initialize(struct smb380_sensor *sensor)
> +{
> + smb380_set_range(sensor->client, sensor->range);
> + smb380_set_bandwidth(sensor->client, sensor->bandwidth);
> + smb380_set_new_data_int(sensor->client, sensor->new_data_int);
> + smb380_set_hg_dur(sensor->client, sensor->hg_dur);
> + smb380_set_hg_thres(sensor->client, sensor->hg_thres);
> + smb380_set_hg_hyst(sensor->client, sensor->hg_hyst);
> + smb380_set_lg_dur(sensor->client, sensor->lg_dur);
> + smb380_set_lg_thres(sensor->client, sensor->lg_thres);
> + smb380_set_lg_hyst(sensor->client, sensor->lg_hyst);
> + smb380_set_hg_int(sensor->client, sensor->hg_int);
> + smb380_set_lg_int(sensor->client, sensor->lg_int);
> +}
by refactoring you could merge the set-functions into this one
and comment each line
> +
> +static void smb380_unregister_input_device(struct smb380_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 smb380_register_input_device(struct smb380_sensor *sensor)
> +{
> + struct i2c_client *client = sensor->client;
> + struct input_dev *idev;
> + int ret;
> +
> + idev = sensor->idev = input_allocate_device();
> + if (!idev) {
> + dev_err(&client->dev, "allocating input device is failed\n");
typo: -is
> + ret = -ENOMEM;
> + goto failed_alloc;
> + }
> +
> + idev->name = "SMB380 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, SMB380_MIN_VALUE,
> + SMB380_MAX_VALUE, 0, 0);
> + input_set_abs_params(idev, ABS_Y, SMB380_MIN_VALUE,
> + SMB380_MAX_VALUE, 0, 0);
> + input_set_abs_params(idev, ABS_Z, SMB380_MIN_VALUE,
> + SMB380_MAX_VALUE, 0, 0);
> +
> + input_set_drvdata(idev, sensor);
> +
> + ret = input_register_device(idev);
> + if (ret) {
> + dev_err(&client->dev, "registering input device is failed\n");
> + goto failed_reg;
> + }
> +
> + if (client->irq > 0) {
> + ret = request_irq(client->irq, smb380_irq, IRQF_TRIGGER_RISING,
> + "smb380 accelerometer", 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 smb380_probe(struct i2c_client *client,
> + const struct i2c_device_id *id)
> +{
> + struct smb380_sensor *sensor;
> + struct smb380_platform_data *pdata;
> + int ret;
> +
> + sensor = kzalloc(sizeof(struct smb380_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 = smb380_read_reg(client, SMB380_CHIP_ID_REG);
> + if (ret < 0) {
> + dev_err(&client->dev, "failed to detect device\n");
> + goto failed_free;
> + }
> + if (ret != SMB380_CHIP_ID) {
> + dev_err(&client->dev, "the chip id is missmatched\n");
typo: mismatched
> + goto failed_free;
> + }
> +
> + INIT_WORK(&sensor->work, smb380_work);
> + mutex_init(&sensor->lock);
> +
> + ret = sysfs_create_group(&client->dev.kobj, &smb380_group);
> + if (ret) {
> + dev_err(&client->dev, "creating attribute group is failed\n");
typo: -is
> + goto failed_free;
> + }
> +
> + ret = smb380_register_input_device(sensor);
> + if (ret) {
> + dev_err(&client->dev, "registering input device is failed\n");
typo: -is
> + goto failed_remove_sysfs;
> + }
> +
> + 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 = SMB380_DEFAULT_RANGE;
> + sensor->bandwidth = SMB380_DEFAULT_BANDWIDTH;
> + sensor->new_data_int = SMB380_DEFAULT_NEW_DATA_INT;
> + sensor->hg_int = SMB380_DEFAULT_HG_INT;
> + sensor->lg_int = SMB380_DEFAULT_LG_INT;
> + sensor->hg_dur = SMB380_DEFAULT_HG_DURATION;
> + sensor->hg_thres = SMB380_DEFAULT_HG_THRESHOLD;
> + sensor->hg_hyst = SMB380_DEFAULT_HG_HYST;
> + sensor->lg_dur = SMB380_DEFAULT_LG_DURATION;
> + sensor->lg_thres = SMB380_DEFAULT_LG_THRESHOLD;
> + sensor->lg_hyst = SMB380_DEFAULT_LG_HYST;
> + }
> +
> + smb380_initialize(sensor);
> +
> + dev_info(&client->dev, "%s registered\n", id->name);
> + return 0;
> +
> +failed_remove_sysfs:
> + sysfs_remove_group(&client->dev.kobj, &smb380_group);
> +failed_free:
> + kfree(sensor);
> + return ret;
> +}
> +
> +static int __devexit smb380_remove(struct i2c_client *client)
> +{
> + struct smb380_sensor *sensor = i2c_get_clientdata(client);
> +
> + smb380_unregister_input_device(sensor);
> + sysfs_remove_group(&client->dev.kobj, &smb380_group);
> + kfree(sensor);
> + return 0;
> +}
> +
> +#ifdef CONFIG_PM
> +static int smb380_suspend(struct i2c_client *client, pm_message_t mesg)
> +{
> + smb380_set_sleep(client, 1);
> + return 0;
> +}
> +
> +static int smb380_resume(struct i2c_client *client)
> +{
> + smb380_set_sleep(client, 0);
> + return 0;
> +}
> +
> +#else
> +#define smb380_suspend NULL
> +#define smb380_resume NULL
> +#endif
> +
> +static const struct i2c_device_id smb380_ids[] = {
> + { "smb380", 0 },
> + { "bma023", 1 },
> + { }
> +};
> +MODULE_DEVICE_TABLE(i2c, smb380_ids);
> +
> +static struct i2c_driver smb380_i2c_driver = {
> + .driver = {
> + .name = "smb380",
> + },
> + .probe = smb380_probe,
> + .remove = __devexit_p(smb380_remove),
> + .suspend = smb380_suspend,
> + .resume = smb380_resume,
> + .id_table = smb380_ids,
> +};
> +
> +static int __init smb380_init(void)
> +{
> + return i2c_add_driver(&smb380_i2c_driver);
> +}
> +module_init(smb380_init);
> +
> +static void __exit smb380_exit(void)
> +{
> + i2c_del_driver(&smb380_i2c_driver);
> +}
> +module_exit(smb380_exit);
> +
> +MODULE_AUTHOR("Kim Kyuwon <q1.kim@samsung.com>");
> +MODULE_DESCRIPTION("SMB380/BMA023 Tri-axis accelerometer driver");
> +MODULE_LICENSE("GPL");
> diff --git a/include/linux/smb380.h b/include/linux/smb380.h
> new file mode 100644
> index 0000000..e93c52d
> --- /dev/null
> +++ b/include/linux/smb380.h
> @@ -0,0 +1,47 @@
> +/*
> + * smb380.h - SMB380 Tri-axis accelerometer driver
> + *
> + * Copyright (c) 2010 Samsung Eletronics
> + * Kyungmin Park <kyungmin.park@samsung.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.
> + *
> + */
> +
> +#ifndef _SMB380_H_
> +#define _SMB380_H_
> +
> +enum scale_range {
> + RANGE_2G,
> + RANGE_4G,
> + RANGE_8G,
> +};
> +
> +/* Used to setup the digital filtering bandwitdh of ADC output */
typo bandwidth
> +enum filter_bw {
> + BW_25HZ,
> + BW_50HZ,
> + BW_100HZ,
> + BW_190HZ,
> + BW_375HZ,
> + BW_750HZ,
> + BW_1500HZ,
> +};
> +
> +struct smb380_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 /* _SMB380_H_ */
^ permalink raw reply [flat|nested] 3+ messages in thread
* [PATCH v2] input: smb380 accelerometer driver
2010-07-14 13:54 ` Christoph Fritz
@ 2010-07-15 8:58 ` Donggeun Kim
0 siblings, 0 replies; 3+ messages in thread
From: Donggeun Kim @ 2010-07-15 8:58 UTC (permalink / raw)
To: linux-input; +Cc: dmitry.torokhov, kyungmin.park
Changes from v1 to v2:
- clean up the code based on v1 feedback
This patch supports driver for BOSCH SMB380 and BMA023 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.
The driver allows to set or get the device's properties by
the sysfs attributes.
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 (default)
1: 50Hz
2: 100Hz
3: 190Hz
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
1: enable (default)
lg_int: generate an interrupt when the low-g threshold criteria are met
0: disable
1: enable (default)
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
xyz: print the values of x, y, and z coordinate register
temperature: print the value of temperature register
Thank you.
- Donggeun
Signed-off-by: Donggeun Kim <dg77.kim@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
---
drivers/input/misc/Kconfig | 10 +
drivers/input/misc/Makefile | 1 +
drivers/input/misc/smb380.c | 719 +++++++++++++++++++++++++++++++++++++++++++
include/linux/smb380.h | 47 +++
4 files changed, 777 insertions(+), 0 deletions(-)
create mode 100644 drivers/input/misc/smb380.c
create mode 100644 include/linux/smb380.h
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index c44b9ea..ea9cfae 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -390,4 +390,14 @@ config INPUT_PCAP
To compile this driver as a module, choose M here: the
module will be called pcap_keys.
+config INPUT_SMB380
+ tristate "SMB380/BMA023 Triaxial acceleration sensor"
+ depends on I2C
+ help
+ Say Y here if you want to support Bosch SMB380/BMA023
+ connected via an I2C bus.
+
+ To compile this driver as a module, choose M here: the
+ module will be called smb380.
+
endif
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index 71fe57d..4633b69 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -29,6 +29,7 @@ obj-$(CONFIG_INPUT_POWERMATE) += powermate.o
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_SMB380) += smb380.o
obj-$(CONFIG_INPUT_SPARCSPKR) += sparcspkr.o
obj-$(CONFIG_INPUT_TWL4030_PWRBUTTON) += twl4030-pwrbutton.o
obj-$(CONFIG_INPUT_TWL4030_VIBRA) += twl4030-vibra.o
diff --git a/drivers/input/misc/smb380.c b/drivers/input/misc/smb380.c
new file mode 100644
index 0000000..b13fe1c
--- /dev/null
+++ b/drivers/input/misc/smb380.c
@@ -0,0 +1,719 @@
+/*
+ * smb380.c - SMB380 Tri-axis accelerometer driver
+ *
+ * Copyright (C) 2010 Samsung Eletronics Co.Ltd
+ * Kim Kyuwon <q1.kim@samsung.com>
+ * Kyungmin Park <kyungmin.park@samsung.com>
+ * Donggeun Kim <dg77.kim@samsung.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.
+ *
+ */
+
+#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/smb380.h>
+
+#define SMB380_CHIP_ID_REG 0x00
+#define SMB380_X_LSB_REG 0x02
+#define SMB380_X_MSB_REG 0x03
+#define SMB380_Y_LSB_REG 0x04
+#define SMB380_Y_MSB_REG 0x05
+#define SMB380_Z_LSB_REG 0x06
+#define SMB380_Z_MSB_REG 0x07
+#define SMB380_TEMP_REG 0x08
+#define SMB380_CTRL1_REG 0x0a
+#define SMB380_CTRL2_REG 0x0b
+#define SMB380_SETTINGS1_REG 0x0c
+#define SMB380_SETTINGS2_REG 0x0d
+#define SMB380_SETTINGS3_REG 0x0e
+#define SMB380_SETTINGS4_REG 0x0f
+#define SMB380_SETTINGS5_REG 0x10
+#define SMB380_SETTINGS6_REG 0x11
+#define SMB380_RANGE_BW_REG 0x14
+#define SMB380_CONF2_REG 0x15
+
+#define SMB380_CHIP_ID 0x2
+
+#define SMB380_NEW_DATA_INT_SHIFT 5
+#define SMB380_NEW_DATA_INT_MASK (0x1 << 5)
+
+#define SMB380_RANGE_SHIFT 3
+#define SMB380_RANGE_MASK (0x3 << 3)
+#define SMB380_BANDWIDTH_SHIFT 0
+#define SMB380_BANDWIDTH_MASK (0x7)
+
+#define SMB380_HG_HYST_SHIFT 3
+#define SMB380_HG_HYST_MASK (0x7 << 3)
+#define SMB380_LG_HYST_SHIFT 0
+#define SMB380_LG_HYST_MASK (0x7)
+
+#define SMB380_HG_DUR_SHIFT (0x0)
+#define SMB380_HG_DUR_MASK (0xff)
+#define SMB380_HG_THRES_SHIFT (0x0)
+#define SMB380_HG_THRES_MASK (0xff)
+#define SMB380_LG_DUR_SHIFT (0x0)
+#define SMB380_LG_DUR_MASK (0xff)
+#define SMB380_LG_THRES_SHIFT (0x0)
+#define SMB380_LG_THRES_MASK (0xff)
+
+#define SMB380_ENABLE_HG_SHIFT 1
+#define SMB380_ENABLE_HG_MASK (0x1 << 1)
+#define SMB380_ENABLE_LG_SHIFT 0
+#define SMB380_ENABLE_LG_MASK (0x1)
+
+#define SMB380_SLEEP_SHIFT 0
+#define SMB380_SLEEP_MASK (0x1)
+
+#define SMB380_ACCEL_BITS 10
+#define SMB380_MAX_VALUE ((1 << ((SMB380_ACCEL_BITS) - 1)) - 1)
+#define SMB380_MIN_VALUE (-(1 << ((SMB380_ACCEL_BITS) - 1)))
+
+#define SMB380_DEFAULT_RANGE RANGE_2G
+#define SMB380_DEFAULT_BANDWIDTH BW_25HZ
+#define SMB380_DEFAULT_NEW_DATA_INT 0
+#define SMB380_DEFAULT_HG_INT 1
+#define SMB380_DEFAULT_LG_INT 1
+#define SMB380_DEFAULT_HG_DURATION 0x96
+#define SMB380_DEFAULT_HG_THRESHOLD 0xa0
+#define SMB380_DEFAULT_HG_HYST 0
+#define SMB380_DEFAULT_LG_DURATION 0x96
+#define SMB380_DEFAULT_LG_THRESHOLD 0x14
+#define SMB380_DEFAULT_LG_HYST 0
+
+struct smb380_data {
+ s16 x;
+ s16 y;
+ s16 z;
+ u8 temp;
+};
+
+struct smb380_sensor {
+ struct i2c_client *client;
+ struct device *dev;
+ struct input_dev *idev;
+ struct work_struct work;
+ struct mutex lock;
+
+ struct smb380_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;
+};
+
+static int smb380_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 smb380_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 smb380_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 smb380_set_reg_bits(struct i2c_client *client,
+ int val, int shift, u8 mask, u8 reg)
+{
+ u8 data = smb380_read_reg(client, reg);
+
+ data = (data & ~mask) | ((val << shift) & mask);
+ return smb380_write_reg(client, reg, data);
+}
+
+static u8 smb380_get_reg_bits(struct i2c_client *client, int shift,
+ u8 mask, u8 reg)
+{
+ u8 data = smb380_read_reg(client, reg);
+
+ data = (data & mask) >> shift;
+ return data;
+}
+
+/* range */
+static int smb380_set_range(struct i2c_client *client, enum scale_range range)
+{
+ return smb380_set_reg_bits(client, range, SMB380_RANGE_SHIFT,
+ SMB380_RANGE_MASK, SMB380_RANGE_BW_REG);
+}
+
+static u8 smb380_get_range(struct i2c_client *client)
+{
+ return smb380_get_reg_bits(client, SMB380_RANGE_SHIFT,
+ SMB380_RANGE_MASK, SMB380_RANGE_BW_REG);
+}
+
+/* bandwidth */
+static int smb380_set_bandwidth(struct i2c_client *client, enum filter_bw bw)
+{
+ return smb380_set_reg_bits(client, bw, SMB380_BANDWIDTH_SHIFT,
+ SMB380_BANDWIDTH_MASK, SMB380_RANGE_BW_REG);
+}
+
+static u8 smb380_get_bandwidth(struct i2c_client *client)
+{
+ return smb380_get_reg_bits(client, SMB380_BANDWIDTH_SHIFT,
+ SMB380_BANDWIDTH_MASK, SMB380_RANGE_BW_REG);
+}
+
+/* new data interrupt */
+static int smb380_set_new_data_int(struct i2c_client *client, u8 val)
+{
+ return smb380_set_reg_bits(client, val, SMB380_NEW_DATA_INT_SHIFT,
+ SMB380_NEW_DATA_INT_MASK, SMB380_CONF2_REG);
+}
+
+static u8 smb380_get_new_data_int(struct i2c_client *client)
+{
+ return smb380_get_reg_bits(client, SMB380_NEW_DATA_INT_SHIFT,
+ SMB380_NEW_DATA_INT_MASK, SMB380_CONF2_REG);
+}
+
+/* high-g interrupt */
+static int smb380_set_hg_int(struct i2c_client *client, u8 val)
+{
+ return smb380_set_reg_bits(client, val, SMB380_ENABLE_HG_SHIFT,
+ SMB380_ENABLE_HG_MASK, SMB380_CTRL2_REG);
+}
+
+static u8 smb380_get_hg_int(struct i2c_client *client)
+{
+ return smb380_get_reg_bits(client, SMB380_ENABLE_HG_SHIFT,
+ SMB380_ENABLE_HG_MASK, SMB380_CTRL2_REG);
+}
+
+/* low-g interrupt */
+static int smb380_set_lg_int(struct i2c_client *client, u8 val)
+{
+ return smb380_set_reg_bits(client, val, SMB380_ENABLE_LG_SHIFT,
+ SMB380_ENABLE_LG_MASK, SMB380_CTRL2_REG);
+}
+
+static u8 smb380_get_lg_int(struct i2c_client *client)
+{
+ return smb380_get_reg_bits(client, SMB380_ENABLE_LG_SHIFT,
+ SMB380_ENABLE_LG_MASK, SMB380_CTRL2_REG);
+}
+
+/* low-g interrupt criterion for duration */
+static int smb380_set_lg_dur(struct i2c_client *client, u8 dur)
+{
+ return smb380_set_reg_bits(client, dur, SMB380_LG_DUR_SHIFT,
+ SMB380_LG_DUR_MASK, SMB380_SETTINGS2_REG);
+}
+
+static u8 smb380_get_lg_dur(struct i2c_client *client)
+{
+ return smb380_get_reg_bits(client, SMB380_LG_DUR_SHIFT,
+ SMB380_LG_DUR_MASK, SMB380_SETTINGS2_REG);
+}
+
+/* low-g interrupt criterion for threshold */
+static int smb380_set_lg_thres(struct i2c_client *client, u8 thres)
+{
+ return smb380_set_reg_bits(client, thres, SMB380_LG_THRES_SHIFT,
+ SMB380_LG_THRES_MASK, SMB380_SETTINGS1_REG);
+}
+
+static u8 smb380_get_lg_thres(struct i2c_client *client)
+{
+ return smb380_get_reg_bits(client, SMB380_LG_THRES_SHIFT,
+ SMB380_LG_THRES_MASK, SMB380_SETTINGS1_REG);
+}
+
+/* low-g interrupt criterion for hysteresis */
+static int smb380_set_lg_hyst(struct i2c_client *client, u8 hyst)
+{
+ return smb380_set_reg_bits(client, hyst, SMB380_LG_HYST_SHIFT,
+ SMB380_LG_HYST_MASK, SMB380_SETTINGS6_REG);
+}
+
+static u8 smb380_get_lg_hyst(struct i2c_client *client)
+{
+ return smb380_get_reg_bits(client, SMB380_LG_HYST_SHIFT,
+ SMB380_LG_HYST_MASK, SMB380_SETTINGS6_REG);
+}
+
+/* high-g interrupt criterion for duration */
+static int smb380_set_hg_dur(struct i2c_client *client, u8 dur)
+{
+ return smb380_set_reg_bits(client, dur, SMB380_HG_DUR_SHIFT,
+ SMB380_HG_DUR_MASK, SMB380_SETTINGS4_REG);
+}
+
+static u8 smb380_get_hg_dur(struct i2c_client *client)
+{
+ return smb380_get_reg_bits(client, SMB380_HG_DUR_SHIFT,
+ SMB380_HG_DUR_MASK, SMB380_SETTINGS4_REG);
+}
+
+/* high-g interrupt criterion for threshold */
+static int smb380_set_hg_thres(struct i2c_client *client, u8 thres)
+{
+ return smb380_set_reg_bits(client, thres, SMB380_HG_THRES_SHIFT,
+ SMB380_HG_THRES_MASK, SMB380_SETTINGS3_REG);
+}
+
+static u8 smb380_get_hg_thres(struct i2c_client *client)
+{
+ return smb380_get_reg_bits(client, SMB380_HG_THRES_SHIFT,
+ SMB380_HG_THRES_MASK, SMB380_SETTINGS3_REG);
+}
+
+/* high-g interrupt criterion for hysteresis */
+static int smb380_set_hg_hyst(struct i2c_client *client, u8 hyst)
+{
+ return smb380_set_reg_bits(client, hyst, SMB380_HG_HYST_SHIFT,
+ SMB380_HG_HYST_MASK, SMB380_SETTINGS6_REG);
+}
+
+static u8 smb380_get_hg_hyst(struct i2c_client *client)
+{
+ return smb380_get_reg_bits(client, SMB380_HG_HYST_SHIFT,
+ SMB380_HG_HYST_MASK, SMB380_SETTINGS6_REG);
+}
+
+static int smb380_set_sleep(struct i2c_client *client, u8 val)
+{
+ return smb380_set_reg_bits(client, val, SMB380_SLEEP_SHIFT,
+ SMB380_SLEEP_MASK, SMB380_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 smb380_correct_accel_sign(s16 *val)
+{
+ *val <<= (sizeof(s16) * BITS_PER_BYTE - SMB380_ACCEL_BITS);
+ *val >>= (sizeof(s16) * BITS_PER_BYTE - SMB380_ACCEL_BITS);
+}
+
+static void smb380_merge_register_values(struct i2c_client *client, s16 *val,
+ u8 lsb, u8 msb)
+{
+ *val = (msb << 2) | (lsb >> 6);
+ smb380_correct_accel_sign(val);
+}
+
+/*
+ * Read 8 bit temperature.
+ * An output of 0 equals -30C, 1 LSB equals 0.5C
+ */
+static void smb380_read_temperature(struct i2c_client *client, u8 *temper)
+{
+ *temper = smb380_read_reg(client, SMB380_TEMP_REG);
+
+ dev_dbg(&client->dev, "%s: %d\n", __func__, *temper);
+}
+
+static void smb380_read_xyz(struct i2c_client *client,
+ s16 *x, s16 *y, s16 *z)
+{
+ u8 buffer[6];
+ buffer[0] = SMB380_X_LSB_REG;
+ smb380_xyz_read_reg(client, buffer, 6);
+
+ smb380_merge_register_values(client, x, buffer[0], buffer[1]);
+ smb380_merge_register_values(client, y, buffer[2], buffer[3]);
+ smb380_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 smb380_show_xyz(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct smb380_sensor *sensor = dev_get_drvdata(dev);
+
+ mutex_lock(&sensor->lock);
+ smb380_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(xyz, S_IRUGO, smb380_show_xyz, NULL);
+
+static ssize_t smb380_show_temper(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct smb380_sensor *sensor = dev_get_drvdata(dev);
+
+ mutex_lock(&sensor->lock);
+ smb380_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, smb380_show_temper, NULL);
+
+#define SMB380_ADJUST(name) \
+static ssize_t smb380_show_##name(struct device *dev, \
+ struct device_attribute *att, char *buf) \
+{ \
+ struct smb380_sensor *sensor = dev_get_drvdata(dev); \
+ \
+ return sprintf(buf, "%d\n", sensor->name); \
+} \
+static ssize_t smb380_store_##name(struct device *dev, \
+ struct device_attribute *attr, const char *buf, size_t count) \
+{ \
+ struct smb380_sensor *sensor = dev_get_drvdata(dev); \
+ unsigned long val; \
+ int ret; \
+ u8 result; \
+ \
+ ret = strict_strtoul(buf, 10, &val); \
+ if (!ret) { \
+ smb380_set_##name(sensor->client, val); \
+ result = smb380_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, \
+ smb380_show_##name, smb380_store_##name);
+
+SMB380_ADJUST(range);
+SMB380_ADJUST(bandwidth);
+SMB380_ADJUST(new_data_int);
+SMB380_ADJUST(hg_int);
+SMB380_ADJUST(lg_int);
+SMB380_ADJUST(lg_dur);
+SMB380_ADJUST(lg_thres);
+SMB380_ADJUST(lg_hyst);
+SMB380_ADJUST(hg_dur);
+SMB380_ADJUST(hg_thres);
+SMB380_ADJUST(hg_hyst);
+
+static struct attribute *smb380_attributes[] = {
+ &dev_attr_xyz.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 smb380_group = {
+ .attrs = smb380_attributes,
+};
+
+static void smb380_work(struct work_struct *work)
+{
+ struct smb380_sensor *sensor =
+ container_of(work, struct smb380_sensor, work);
+
+ smb380_read_xyz(sensor->client,
+ &sensor->data.x, &sensor->data.y, &sensor->data.z);
+ smb380_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 smb380_irq(int irq, void *dev_id)
+{
+ struct smb380_sensor *sensor = dev_id;
+
+ if (!work_pending(&sensor->work)) {
+ disable_irq_nosync(irq);
+ schedule_work(&sensor->work);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void smb380_initialize(struct smb380_sensor *sensor)
+{
+ smb380_set_range(sensor->client, sensor->range);
+ smb380_set_bandwidth(sensor->client, sensor->bandwidth);
+ smb380_set_new_data_int(sensor->client, sensor->new_data_int);
+ smb380_set_hg_dur(sensor->client, sensor->hg_dur);
+ smb380_set_hg_thres(sensor->client, sensor->hg_thres);
+ smb380_set_hg_hyst(sensor->client, sensor->hg_hyst);
+ smb380_set_lg_dur(sensor->client, sensor->lg_dur);
+ smb380_set_lg_thres(sensor->client, sensor->lg_thres);
+ smb380_set_lg_hyst(sensor->client, sensor->lg_hyst);
+ smb380_set_hg_int(sensor->client, sensor->hg_int);
+ smb380_set_lg_int(sensor->client, sensor->lg_int);
+}
+
+static void smb380_unregister_input_device(struct smb380_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 smb380_register_input_device(struct smb380_sensor *sensor)
+{
+ struct i2c_client *client = sensor->client;
+ struct input_dev *idev;
+ int ret;
+
+ idev = sensor->idev = input_allocate_device();
+ if (!idev) {
+ dev_err(&client->dev, "failed to allocate input device\n");
+ ret = -ENOMEM;
+ goto failed_alloc;
+ }
+
+ idev->name = "SMB380 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, SMB380_MIN_VALUE,
+ SMB380_MAX_VALUE, 0, 0);
+ input_set_abs_params(idev, ABS_Y, SMB380_MIN_VALUE,
+ SMB380_MAX_VALUE, 0, 0);
+ input_set_abs_params(idev, ABS_Z, SMB380_MIN_VALUE,
+ SMB380_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, smb380_irq, IRQF_TRIGGER_RISING,
+ "smb380 accelerometer", 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 smb380_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct smb380_sensor *sensor;
+ struct smb380_platform_data *pdata;
+ int ret;
+
+ sensor = kzalloc(sizeof(struct smb380_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 = smb380_read_reg(client, SMB380_CHIP_ID_REG);
+ if (ret < 0) {
+ dev_err(&client->dev, "failed to detect device\n");
+ goto failed_free;
+ }
+ if (ret != SMB380_CHIP_ID) {
+ dev_err(&client->dev, "unsupported chip id\n");
+ goto failed_free;
+ }
+
+ INIT_WORK(&sensor->work, smb380_work);
+ mutex_init(&sensor->lock);
+
+ ret = sysfs_create_group(&client->dev.kobj, &smb380_group);
+ if (ret) {
+ dev_err(&client->dev, "failed to create attribute group\n");
+ goto failed_free;
+ }
+
+ ret = smb380_register_input_device(sensor);
+ if (ret)
+ goto failed_remove_sysfs;
+
+ 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 = SMB380_DEFAULT_RANGE;
+ sensor->bandwidth = SMB380_DEFAULT_BANDWIDTH;
+ sensor->new_data_int = SMB380_DEFAULT_NEW_DATA_INT;
+ sensor->hg_int = SMB380_DEFAULT_HG_INT;
+ sensor->lg_int = SMB380_DEFAULT_LG_INT;
+ sensor->hg_dur = SMB380_DEFAULT_HG_DURATION;
+ sensor->hg_thres = SMB380_DEFAULT_HG_THRESHOLD;
+ sensor->hg_hyst = SMB380_DEFAULT_HG_HYST;
+ sensor->lg_dur = SMB380_DEFAULT_LG_DURATION;
+ sensor->lg_thres = SMB380_DEFAULT_LG_THRESHOLD;
+ sensor->lg_hyst = SMB380_DEFAULT_LG_HYST;
+ }
+
+ smb380_initialize(sensor);
+
+ dev_info(&client->dev, "%s registered\n", id->name);
+ return 0;
+
+failed_remove_sysfs:
+ sysfs_remove_group(&client->dev.kobj, &smb380_group);
+failed_free:
+ kfree(sensor);
+ return ret;
+}
+
+static int __devexit smb380_remove(struct i2c_client *client)
+{
+ struct smb380_sensor *sensor = i2c_get_clientdata(client);
+
+ smb380_unregister_input_device(sensor);
+ sysfs_remove_group(&client->dev.kobj, &smb380_group);
+ kfree(sensor);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int smb380_suspend(struct i2c_client *client, pm_message_t mesg)
+{
+ smb380_set_sleep(client, 1);
+ return 0;
+}
+
+static int smb380_resume(struct i2c_client *client)
+{
+ smb380_set_sleep(client, 0);
+ return 0;
+}
+
+#else
+#define smb380_suspend NULL
+#define smb380_resume NULL
+#endif
+
+static const struct i2c_device_id smb380_ids[] = {
+ { "smb380", 0 },
+ { "bma023", 1 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, smb380_ids);
+
+static struct i2c_driver smb380_i2c_driver = {
+ .driver = {
+ .name = "smb380",
+ },
+ .probe = smb380_probe,
+ .remove = __devexit_p(smb380_remove),
+ .suspend = smb380_suspend,
+ .resume = smb380_resume,
+ .id_table = smb380_ids,
+};
+
+static int __init smb380_init(void)
+{
+ return i2c_add_driver(&smb380_i2c_driver);
+}
+module_init(smb380_init);
+
+static void __exit smb380_exit(void)
+{
+ i2c_del_driver(&smb380_i2c_driver);
+}
+module_exit(smb380_exit);
+
+MODULE_AUTHOR("Kim Kyuwon <q1.kim@samsung.com>");
+MODULE_DESCRIPTION("SMB380/BMA023 Tri-axis accelerometer driver");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/smb380.h b/include/linux/smb380.h
new file mode 100644
index 0000000..a6f971b
--- /dev/null
+++ b/include/linux/smb380.h
@@ -0,0 +1,47 @@
+/*
+ * smb380.h - SMB380 Tri-axis accelerometer driver
+ *
+ * Copyright (c) 2010 Samsung Eletronics
+ * Kyungmin Park <kyungmin.park@samsung.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.
+ *
+ */
+
+#ifndef _SMB380_H_
+#define _SMB380_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 smb380_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 /* _SMB380_H_ */
--
1.6.3.3
^ permalink raw reply related [flat|nested] 3+ messages in thread
end of thread, other threads:[~2010-07-15 8:58 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-07-14 9:31 [PATCH] input: smb380 accelerometer driver Donggeun Kim
2010-07-14 13:54 ` Christoph Fritz
2010-07-15 8:58 ` [PATCH v2] " Donggeun Kim
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).