diff options
Diffstat (limited to 'drivers/input/misc/mma8450.c')
-rw-r--r-- | drivers/input/misc/mma8450.c | 173 |
1 files changed, 157 insertions, 16 deletions
diff --git a/drivers/input/misc/mma8450.c b/drivers/input/misc/mma8450.c index 49f5242bc54c..2174d2bdbb9e 100644 --- a/drivers/input/misc/mma8450.c +++ b/drivers/input/misc/mma8450.c @@ -2,7 +2,7 @@ /* * Driver for Freescale's 3-Axis Accelerometer MMA8450 * - * Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright (C) 2011-2015 Freescale Semiconductor, Inc. All Rights Reserved. */ #include <linux/kernel.h> @@ -12,6 +12,7 @@ #include <linux/i2c.h> #include <linux/input-polldev.h> #include <linux/of_device.h> +#include <linux/mutex.h> #define MMA8450_DRV_NAME "mma8450" @@ -38,11 +39,22 @@ #define MMA8450_CTRL_REG1 0x38 #define MMA8450_CTRL_REG2 0x39 +#define MMA8450_ID 0xC6 +#define MMA8450_WHO_AM_I 0x0F + +enum { + MODE_STANDBY = 0, + MODE_2G, + MODE_4G, + MODE_8G, +}; /* mma8450 status */ struct mma8450 { struct i2c_client *client; struct input_polled_dev *idev; + struct mutex mma8450_lock; + u8 mode; }; static int mma8450_read(struct mma8450 *m, unsigned off) @@ -99,16 +111,19 @@ static void mma8450_poll(struct input_polled_dev *dev) int ret; u8 buf[6]; - ret = mma8450_read(m, MMA8450_STATUS); - if (ret < 0) - return; + mutex_lock(&m->mma8450_lock); - if (!(ret & MMA8450_STATUS_ZXYDR)) + ret = mma8450_read(m, MMA8450_STATUS); + if (ret < 0 || !(ret & MMA8450_STATUS_ZXYDR)) { + mutex_unlock(&m->mma8450_lock); return; + } ret = mma8450_read_block(m, MMA8450_OUT_X_LSB, buf, sizeof(buf)); - if (ret < 0) + if (ret < 0) { + mutex_unlock(&m->mma8450_lock); return; + } x = ((int)(s8)buf[1] << 4) | (buf[0] & 0xf); y = ((int)(s8)buf[3] << 4) | (buf[2] & 0xf); @@ -118,10 +133,12 @@ static void mma8450_poll(struct input_polled_dev *dev) input_report_abs(dev->input, ABS_Y, y); input_report_abs(dev->input, ABS_Z, z); input_sync(dev->input); + + mutex_unlock(&m->mma8450_lock); } /* Initialize the MMA8450 chip */ -static void mma8450_open(struct input_polled_dev *dev) +static s32 mma8450_open(struct input_polled_dev *dev) { struct mma8450 *m = dev->private; int err; @@ -129,18 +146,20 @@ static void mma8450_open(struct input_polled_dev *dev) /* enable all events from X/Y/Z, no FIFO */ err = mma8450_write(m, MMA8450_XYZ_DATA_CFG, 0x07); if (err) - return; + return err; /* * Sleep mode poll rate - 50Hz * System output data rate - 400Hz - * Full scale selection - Active, +/- 2G + * Standby mode */ - err = mma8450_write(m, MMA8450_CTRL_REG1, 0x01); - if (err < 0) - return; - + err = mma8450_write(m, MMA8450_CTRL_REG1, MODE_STANDBY); + if (err) + return err; + m->mode = MODE_STANDBY; msleep(MODE_CHANGE_DELAY_MS); + + return 0; } static void mma8450_close(struct input_polled_dev *dev) @@ -151,6 +170,76 @@ static void mma8450_close(struct input_polled_dev *dev) mma8450_write(m, MMA8450_CTRL_REG2, 0x01); } +static ssize_t mma8450_scalemode_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int mode = 0; + struct mma8450 *m; + struct i2c_client *client = to_i2c_client(dev); + + m = i2c_get_clientdata(client); + + mutex_lock(&m->mma8450_lock); + mode = (int)m->mode; + mutex_unlock(&m->mma8450_lock); + + return sprintf(buf, "%d\n", mode); +} + +static ssize_t mma8450_scalemode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long mode; + int ret; + struct mma8450 *m = NULL; + struct i2c_client *client = to_i2c_client(dev); + + ret = kstrtoul(buf, 10, &mode); + if (ret) { + dev_err(dev, "string transform error\n"); + return ret; + } + + if (mode > MODE_8G) { + dev_warn(dev, "not supported mode %d\n", (int)mode); + return count; + } + + m = i2c_get_clientdata(client); + + mutex_lock(&m->mma8450_lock); + if (mode == m->mode) { + mutex_unlock(&m->mma8450_lock); + return count; + } + + ret = mma8450_write(m, MMA8450_CTRL_REG1, mode); + if (ret < 0) { + mutex_unlock(&m->mma8450_lock); + return ret; + } + + msleep(MODE_CHANGE_DELAY_MS); + m->mode = (u8)mode; + mutex_unlock(&m->mma8450_lock); + + return count; +} + +static DEVICE_ATTR(scalemode, S_IWUSR | S_IRUGO, + mma8450_scalemode_show, mma8450_scalemode_store); + +static struct attribute *mma8450_attributes[] = { + &dev_attr_scalemode.attr, + NULL +}; + +static const struct attribute_group mma8450_attr_group = { + .attrs = mma8450_attributes, +}; + /* * I2C init/probing/exit functions */ @@ -159,7 +248,24 @@ static int mma8450_probe(struct i2c_client *c, { struct input_polled_dev *idev; struct mma8450 *m; - int err; + int err, client_id; + struct i2c_adapter *adapter = NULL; + + adapter = to_i2c_adapter(c->dev.parent); + err = i2c_check_functionality(adapter, + I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA); + if (!err) + return err; + + client_id = i2c_smbus_read_byte_data(c, MMA8450_WHO_AM_I); + + if (MMA8450_ID != client_id) { + dev_err(&c->dev, + "read chip ID 0x%x is not equal to 0x%x!\n", client_id, + MMA8450_ID); + return -EINVAL; + } m = devm_kzalloc(&c->dev, sizeof(*m), GFP_KERNEL); if (!m) @@ -171,6 +277,7 @@ static int mma8450_probe(struct i2c_client *c, m->client = c; m->idev = idev; + i2c_set_clientdata(c, m); idev->private = m; idev->input->name = MMA8450_DRV_NAME; @@ -178,8 +285,6 @@ static int mma8450_probe(struct i2c_client *c, idev->poll = mma8450_poll; idev->poll_interval = POLL_INTERVAL; idev->poll_interval_max = POLL_INTERVAL_MAX; - idev->open = mma8450_open; - idev->close = mma8450_close; __set_bit(EV_ABS, idev->input->evbit); input_set_abs_params(idev->input, ABS_X, -2048, 2047, 32, 32); @@ -192,6 +297,41 @@ static int mma8450_probe(struct i2c_client *c, return err; } + mutex_init(&m->mma8450_lock); + + err = mma8450_open(idev); + if (err) { + dev_err(&c->dev, "failed to initialize mma8450\n"); + goto err_unreg_dev; + } + + err = sysfs_create_group(&c->dev.kobj, &mma8450_attr_group); + if (err) { + dev_err(&c->dev, "create device file failed!\n"); + err = -EINVAL; + goto err_close; + } + + return 0; + +err_close: + mma8450_close(idev); +err_unreg_dev: + mutex_destroy(&m->mma8450_lock); + input_unregister_polled_device(idev); + return err; +} + +static int mma8450_remove(struct i2c_client *c) +{ + struct mma8450 *m = i2c_get_clientdata(c); + struct input_polled_dev *idev = m->idev; + + sysfs_remove_group(&c->dev.kobj, &mma8450_attr_group); + mma8450_close(idev); + mutex_destroy(&m->mma8450_lock); + input_unregister_polled_device(idev); + return 0; } @@ -213,6 +353,7 @@ static struct i2c_driver mma8450_driver = { .of_match_table = mma8450_dt_ids, }, .probe = mma8450_probe, + .remove = mma8450_remove, .id_table = mma8450_id, }; |