From: Donggeun Kim <dg77.kim@samsung.com>
To: linux-input@vger.kernel.org
Cc: kyungmin.park@samsung.com
Subject: [PATCH] input: smb380 accelerometer driver
Date: Wed, 14 Jul 2010 18:31:02 +0900 [thread overview]
Message-ID: <4C3D83D6.9090309@samsung.com> (raw)
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
next reply other threads:[~2010-07-14 9:31 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2010-07-14 9:31 Donggeun Kim [this message]
2010-07-14 13:54 ` [PATCH] input: smb380 accelerometer driver Christoph Fritz
2010-07-15 8:58 ` [PATCH v2] " Donggeun Kim
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=4C3D83D6.9090309@samsung.com \
--to=dg77.kim@samsung.com \
--cc=kyungmin.park@samsung.com \
--cc=linux-input@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.