blob: bbae5996954cf54e5bd73d7921520272d8ef5a9e [file] [log] [blame]
/*
* 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.
*
*/
#define DEBUG
#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>
#include <linux/slab.h>
#include <linux/irq.h>
#include <linux/gpio.h>
#include <asm/io.h>
#include <mach/hardware.h>
#include <mach/rdb/brcm_rdb_chipreg.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_ENABLE_ADV_INT_SHIFT 6
#define SMB380_ENABLE_ADV_INT_MASK (0x1 << 6)
#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_ANY_MOTION_DUR_SHIFT 6
#define SMB380_ANY_MOTION_DUR_MASK (0x3 << 6)
#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_ANY_MOTION_THRES_SHIFT 0
#define SMB380_ANY_MOTION_THRES_MASK (0xFF << 0)
#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_ANY_MOTION_SHIFT 6
#define SMB380_ENABLE_ANY_MOTION_MASK (0x1 << 6)
#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_ENABLE_ADV_INT 0
#define SMB380_DEFAULT_NEW_DATA_INT 0
#define SMB380_DEFAULT_ANY_MOTION_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_ANY_MOTION_DUR 0
#define SMB380_DEFAULT_ANY_MOTION_THRES 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 enable_adv_int;
u8 new_data_int;
u8 hg_int;
u8 lg_int;
u8 lg_dur;
u8 lg_thres;
u8 lg_hyst;
u8 hg_dur;
u8 hg_thres;
u8 hg_hyst;
u8 any_motion_dur;
u8 any_motion_thres;
u8 any_motion_int;
};
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);
}
/* enable_adv_int interrupt */
static int smb380_set_enable_adv_int(struct i2c_client *client, u8 val)
{
return smb380_set_reg_bits(client, val, SMB380_ENABLE_ADV_INT_SHIFT,
SMB380_ENABLE_ADV_INT_MASK, SMB380_CONF2_REG);
}
static u8 smb380_get_enable_adv_int(struct i2c_client *client)
{
return smb380_get_reg_bits(client, SMB380_ENABLE_ADV_INT_SHIFT,
SMB380_ENABLE_ADV_INT_MASK, SMB380_CONF2_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);
}
/* any_motion interrupt */
static int smb380_set_any_motion_int(struct i2c_client *client, u8 val)
{
return smb380_set_reg_bits(client, val, SMB380_ENABLE_ANY_MOTION_SHIFT,
SMB380_ENABLE_ANY_MOTION_MASK, SMB380_CTRL2_REG);
}
static u8 smb380_get_any_motion_int(struct i2c_client *client)
{
return smb380_get_reg_bits(client, SMB380_ENABLE_ANY_MOTION_SHIFT,
SMB380_ENABLE_ANY_MOTION_MASK, SMB380_CTRL2_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);
}
/* any_motion_threshold value */
static int smb380_set_any_motion_thres(struct i2c_client *client, u8 any_motion_thres)
{
return smb380_set_reg_bits(client, any_motion_thres, SMB380_ANY_MOTION_THRES_SHIFT,
SMB380_ANY_MOTION_THRES_MASK, SMB380_SETTINGS5_REG);
}
static u8 smb380_get_any_motion_thres(struct i2c_client *client)
{
return smb380_get_reg_bits(client, SMB380_ANY_MOTION_THRES_SHIFT,
SMB380_ANY_MOTION_THRES_MASK, SMB380_SETTINGS5_REG);
}
/* any_motion_dur criterion */
static int smb380_set_any_motion_dur(struct i2c_client *client, u8 any_motion_dur)
{
return smb380_set_reg_bits(client, any_motion_dur, SMB380_ANY_MOTION_DUR_SHIFT,
SMB380_ANY_MOTION_DUR_MASK, SMB380_SETTINGS6_REG);
}
static u8 smb380_get_any_motion_dur(struct i2c_client *client)
{
return smb380_get_reg_bits(client, SMB380_ANY_MOTION_DUR_SHIFT,
SMB380_ANY_MOTION_DUR_MASK, SMB380_SETTINGS6_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(enable_adv_int);
SMB380_ADJUST(new_data_int);
SMB380_ADJUST(hg_int);
SMB380_ADJUST(any_motion_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);
SMB380_ADJUST(any_motion_dur);
SMB380_ADJUST(any_motion_thres);
static struct attribute *smb380_attributes[] = {
&dev_attr_xyz.attr,
&dev_attr_temperature.attr,
&dev_attr_range.attr,
&dev_attr_bandwidth.attr,
&dev_attr_enable_adv_int.attr,
&dev_attr_new_data_int.attr,
&dev_attr_hg_int.attr,
&dev_attr_any_motion_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,
&dev_attr_any_motion_dur.attr,
&dev_attr_any_motion_thres.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);
#if 1
smb380_read_temperature(sensor->client, &sensor->data.temp);
#endif
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_enable_adv_int(sensor->client, sensor->enable_adv_int);
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_any_motion_dur(sensor->client, sensor->any_motion_dur);
smb380_set_any_motion_thres(sensor->client, sensor->any_motion_thres);
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_any_motion_int(sensor->client, sensor->any_motion_int);
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;
}
/* Hack:
* Since an unknown source is changing this pin mux after the Uboot,
* without knowing where it came from, we will set up the right pin mux
* right before the usage.
*/
writel(0x303, KONA_CHIPREG_VA + CHIPREG_ULPI1_DATA_6_OFFSET);
if (client->irq > 0) {
/*
* Setup GPIO properties for interrupt from sensor.
*/
ret = set_irq_type(client->irq, IRQ_TYPE_EDGE_FALLING);
if (ret < 0) {
printk(KERN_ERR "set_irq_type failed with irq %d\n", client->irq);
goto failed_reg;
}
ret = gpio_request(irq_to_gpio(client->irq), "bma150 sensor int");
if (ret < 0) {
printk(KERN_ERR "unable to request GPIO pin %d\n", irq_to_gpio(client->irq));
goto failed_reg;
}
gpio_direction_input(irq_to_gpio(client->irq));
ret = request_irq(client->irq, smb380_irq, IRQF_TRIGGER_FALLING,
"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->enable_adv_int = pdata->enable_adv_int;
sensor->new_data_int = pdata->new_data_int;
sensor->any_motion_int = pdata->any_motion_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->any_motion_dur = pdata->any_motion_dur;
sensor->any_motion_thres = pdata->any_motion_thres;
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->enable_adv_int = SMB380_DEFAULT_ENABLE_ADV_INT;
sensor->new_data_int = SMB380_DEFAULT_NEW_DATA_INT;
sensor->any_motion_int = SMB380_DEFAULT_ANY_MOTION_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->any_motion_dur = SMB380_DEFAULT_ANY_MOTION_DUR;
sensor->any_motion_thres = SMB380_DEFAULT_ANY_MOTION_THRES;
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");