361 lines
10 KiB
C
361 lines
10 KiB
C
/*
|
|
* Copyright (C) 2010-2015 Freescale , Inc. All Rights Reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/init.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/i2c.h>
|
|
#include <linux/pm.h>
|
|
#include <linux/mutex.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/irq.h>
|
|
#include <linux/hwmon-sysfs.h>
|
|
#include <linux/err.h>
|
|
#include <linux/hwmon.h>
|
|
#include <linux/input-polldev.h>
|
|
|
|
#define MPL3115_DRV_NAME "mpl3115"
|
|
#define ABS_TEMPTERAURE ABS_MISC
|
|
|
|
#define INPUT_FUZZ 32
|
|
#define INPUT_FLAT 32
|
|
#define MPL_ACTIVED 1
|
|
#define MPL_STANDBY 0
|
|
#define POLL_INTERVAL_MAX 500
|
|
#define POLL_INTERVAL 100
|
|
#define POLL_INTERVAL_MIN 1
|
|
#define MPL3115_ID 0xc4
|
|
#define MPLL_ACTIVE_MASK 0x01
|
|
#define MPL3115_STATUS_DR 0x08
|
|
|
|
/* MPL3115A register address */
|
|
#define MPL3115_STATUS 0x00
|
|
#define MPL3115_PRESSURE_DATA 0x01
|
|
#define MPL3115_DR_STATUS 0x06
|
|
#define MPL3115_DELTA_DATA 0x07
|
|
#define MPL3115_WHO_AM_I 0x0c
|
|
#define MPL3115_FIFO_STATUS 0x0d
|
|
#define MPL3115_FIFO_DATA 0x0e
|
|
#define MPL3115_FIFO_SETUP 0x0e
|
|
#define MPL3115_TIME_DELAY 0x10
|
|
#define MPL3115_SYS_MODE 0x11
|
|
#define MPL3115_INT_SORCE 0x12
|
|
#define MPL3115_PT_DATA_CFG 0x13
|
|
#define MPL3115_BAR_IN_MSB 0x14
|
|
#define MPL3115_P_ARLARM_MSB 0x16
|
|
#define MPL3115_T_ARLARM 0x18
|
|
#define MPL3115_P_ARLARM_WND_MSB 0x19
|
|
#define MPL3115_T_ARLARM_WND 0x1b
|
|
#define MPL3115_P_MIN_DATA 0x1c
|
|
#define MPL3115_T_MIN_DATA 0x1f
|
|
#define MPL3115_P_MAX_DATA 0x21
|
|
#define MPL3115_T_MAX_DATA 0x24
|
|
#define MPL3115_CTRL_REG1 0x26
|
|
#define MPL3115_CTRL_REG2 0x27
|
|
#define MPL3115_CTRL_REG3 0x28
|
|
#define MPL3115_CTRL_REG4 0x29
|
|
#define MPL3115_CTRL_REG5 0x2a
|
|
#define MPL3115_OFFSET_P 0x2b
|
|
#define MPL3115_OFFSET_T 0x2c
|
|
#define MPL3115_OFFSET_H 0x2d
|
|
|
|
#define DATA_SHIFT_BIT(data, bit) ((data << bit) & (0xff << bit))
|
|
|
|
struct mpl3115_data {
|
|
struct i2c_client *client;
|
|
struct input_polled_dev *poll_dev;
|
|
struct mutex data_lock;
|
|
int active;
|
|
};
|
|
|
|
static int mpl3115_i2c_read(struct i2c_client *client, u8 reg, u8 *values, u8 length)
|
|
{
|
|
return i2c_smbus_read_i2c_block_data(client, reg, length, values);
|
|
}
|
|
|
|
static int mpl3115_i2c_write(struct i2c_client *client, u8 reg, const u8 *values, u8 length)
|
|
{
|
|
return i2c_smbus_write_i2c_block_data(client, reg, length, values);
|
|
}
|
|
|
|
static ssize_t mpl3115_enable_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
int val;
|
|
u8 sysmode;
|
|
|
|
struct input_polled_dev *poll_dev = dev_get_drvdata(dev);
|
|
struct mpl3115_data *pdata = (struct mpl3115_data *)(poll_dev->private);
|
|
struct i2c_client *client = pdata->client;
|
|
mutex_lock(&pdata->data_lock);
|
|
mpl3115_i2c_read(client, MPL3115_CTRL_REG1, &sysmode, 1);
|
|
sysmode &= MPLL_ACTIVE_MASK;
|
|
val = (sysmode ? 1 : 0);
|
|
mutex_unlock(&pdata->data_lock);
|
|
|
|
return sprintf(buf, "%d\n", val);
|
|
}
|
|
|
|
static ssize_t mpl3115_enable_store(struct device *dev,
|
|
struct device_attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
int ret, enable;
|
|
u8 val;
|
|
struct input_polled_dev *poll_dev = dev_get_drvdata(dev);
|
|
struct mpl3115_data *pdata = (struct mpl3115_data *)(poll_dev->private);
|
|
struct i2c_client *client = pdata->client;
|
|
|
|
enable = simple_strtoul(buf, NULL, 10);
|
|
mutex_lock(&pdata->data_lock);
|
|
mpl3115_i2c_read(client, MPL3115_CTRL_REG1, &val, 1);
|
|
if (enable && pdata->active == MPL_STANDBY) {
|
|
val |= MPLL_ACTIVE_MASK;
|
|
ret = mpl3115_i2c_write(client, MPL3115_CTRL_REG1, &val, 1);
|
|
if (!ret)
|
|
pdata->active = MPL_ACTIVED;
|
|
printk("mpl3115 set active\n");
|
|
} else if (!enable && pdata->active == MPL_ACTIVED) {
|
|
val &= ~MPLL_ACTIVE_MASK;
|
|
ret = mpl3115_i2c_write(client, MPL3115_CTRL_REG1, &val, 1);
|
|
if (!ret)
|
|
pdata->active = MPL_STANDBY;
|
|
printk("mpl3115 set inactive\n");
|
|
}
|
|
mutex_unlock(&pdata->data_lock);
|
|
|
|
return count;
|
|
}
|
|
|
|
static DEVICE_ATTR(enable, S_IWUSR | S_IRUGO, mpl3115_enable_show, mpl3115_enable_store);
|
|
|
|
static struct attribute *mpl3115_attributes[] = {
|
|
&dev_attr_enable.attr,
|
|
NULL
|
|
};
|
|
|
|
static const struct attribute_group mpl3115_attr_group = {
|
|
.attrs = mpl3115_attributes,
|
|
};
|
|
|
|
static void mpl3115_device_init(struct i2c_client *client)
|
|
{
|
|
u8 val[8];
|
|
struct device_node *np = client->dev.of_node;
|
|
|
|
/* set interrupt pin as open-drain */
|
|
if (of_get_property(np, "interrupt-open-drain", NULL)) {
|
|
val[0] = 0x11;
|
|
mpl3115_i2c_write(client, MPL3115_CTRL_REG3, val, 1);
|
|
}
|
|
|
|
val[0] = 0x28;
|
|
mpl3115_i2c_write(client, MPL3115_CTRL_REG1, val, 1);
|
|
}
|
|
|
|
static int mpl3115_read_data(struct i2c_client *client, int *pres, short *temp)
|
|
{
|
|
u8 tmp[5];
|
|
|
|
mpl3115_i2c_read(client, MPL3115_PRESSURE_DATA, tmp, 5);
|
|
*pres = (DATA_SHIFT_BIT(tmp[0], 24) | DATA_SHIFT_BIT(tmp[1], 16) | DATA_SHIFT_BIT(tmp[2], 8)) >> 12;
|
|
*temp = (DATA_SHIFT_BIT(tmp[3], 8) | DATA_SHIFT_BIT(tmp[4], 0)) >> 4;
|
|
return 0;
|
|
}
|
|
|
|
static void report_abs(struct mpl3115_data *pdata)
|
|
{
|
|
struct input_dev *idev;
|
|
int pressure = 0;
|
|
short temperature = 0;
|
|
|
|
mutex_lock(&pdata->data_lock);
|
|
if (pdata->active == MPL_STANDBY)
|
|
goto out;
|
|
idev = pdata->poll_dev->input;
|
|
mpl3115_read_data(pdata->client, &pressure, &temperature);
|
|
input_report_abs(idev, ABS_PRESSURE, pressure);
|
|
input_report_abs(idev, ABS_TEMPTERAURE, temperature);
|
|
input_sync(idev);
|
|
out:
|
|
mutex_unlock(&pdata->data_lock);
|
|
}
|
|
|
|
static void mpl3115_dev_poll(struct input_polled_dev *dev)
|
|
{
|
|
struct mpl3115_data *pdata = (struct mpl3115_data *)dev->private;
|
|
|
|
report_abs(pdata);
|
|
}
|
|
|
|
static int mpl3115_probe(struct i2c_client *client,
|
|
const struct i2c_device_id *id)
|
|
{
|
|
int result, client_id;
|
|
struct input_dev *idev;
|
|
struct i2c_adapter *adapter;
|
|
struct mpl3115_data *pdata;
|
|
|
|
adapter = to_i2c_adapter(client->dev.parent);
|
|
result = i2c_check_functionality(adapter,
|
|
I2C_FUNC_SMBUS_BYTE |
|
|
I2C_FUNC_SMBUS_BYTE_DATA);
|
|
if (!result)
|
|
goto err_out;
|
|
|
|
client_id = i2c_smbus_read_byte_data(client, MPL3115_WHO_AM_I);
|
|
printk("read mpl3115 chip id 0x%x\n", client_id);
|
|
if (client_id != MPL3115_ID) {
|
|
dev_err(&client->dev,
|
|
"read chip ID 0x%x is not equal to 0x%x!\n",
|
|
result, MPL3115_ID);
|
|
result = -EINVAL;
|
|
goto err_out;
|
|
}
|
|
pdata = kzalloc(sizeof(struct mpl3115_data), GFP_KERNEL);
|
|
if (!pdata)
|
|
goto err_out;
|
|
pdata->client = client;
|
|
i2c_set_clientdata(client, pdata);
|
|
mutex_init(&pdata->data_lock);
|
|
pdata->poll_dev = input_allocate_polled_device();
|
|
if (!pdata->poll_dev) {
|
|
result = -ENOMEM;
|
|
dev_err(&client->dev, "alloc poll device failed!\n");
|
|
goto err_alloc_data;
|
|
}
|
|
pdata->poll_dev->poll = mpl3115_dev_poll;
|
|
pdata->poll_dev->private = pdata;
|
|
pdata->poll_dev->poll_interval = POLL_INTERVAL;
|
|
pdata->poll_dev->poll_interval_min = POLL_INTERVAL_MIN;
|
|
pdata->poll_dev->poll_interval_max = POLL_INTERVAL_MAX;
|
|
idev = pdata->poll_dev->input;
|
|
idev->name = MPL3115_DRV_NAME;
|
|
idev->id.bustype = BUS_I2C;
|
|
idev->evbit[0] = BIT_MASK(EV_ABS);
|
|
|
|
input_set_abs_params(idev, ABS_PRESSURE, -0x7FFFFFFF, 0x7FFFFFFF, 0, 0);
|
|
input_set_abs_params(idev, ABS_TEMPTERAURE, -0x7FFFFFFF, 0x7FFFFFFF, 0, 0);
|
|
result = input_register_polled_device(pdata->poll_dev);
|
|
if (result) {
|
|
dev_err(&client->dev, "register poll device failed!\n");
|
|
goto error_free_poll_dev;
|
|
}
|
|
result = sysfs_create_group(&idev->dev.kobj, &mpl3115_attr_group);
|
|
if (result) {
|
|
dev_err(&client->dev, "create device file failed!\n");
|
|
result = -EINVAL;
|
|
goto error_register_polled_device;
|
|
}
|
|
mpl3115_device_init(client);
|
|
printk("mpl3115 device driver probe successfully");
|
|
return 0;
|
|
error_register_polled_device:
|
|
input_unregister_polled_device(pdata->poll_dev);
|
|
error_free_poll_dev:
|
|
input_free_polled_device(pdata->poll_dev);
|
|
err_alloc_data:
|
|
kfree(pdata);
|
|
err_out:
|
|
return result;
|
|
}
|
|
|
|
static int mpl3115_stop_chip(struct i2c_client *client)
|
|
{
|
|
u8 val;
|
|
int ret;
|
|
|
|
mpl3115_i2c_read(client, MPL3115_CTRL_REG1, &val, 1);
|
|
val &= ~MPLL_ACTIVE_MASK;
|
|
ret = mpl3115_i2c_write(client, MPL3115_CTRL_REG1, &val, 1);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_PM_SLEEP
|
|
static int mpl3115_start_chip(struct i2c_client *client)
|
|
{
|
|
u8 val;
|
|
int ret;
|
|
|
|
mpl3115_i2c_read(client, MPL3115_CTRL_REG1, &val, 1);
|
|
val |= MPLL_ACTIVE_MASK;
|
|
ret = mpl3115_i2c_write(client, MPL3115_CTRL_REG1, &val, 1);
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static int mpl3115_remove(struct i2c_client *client)
|
|
{
|
|
struct mpl3115_data *pdata = i2c_get_clientdata(client);
|
|
struct input_dev *idev = pdata->poll_dev->input;
|
|
|
|
mpl3115_stop_chip(client);
|
|
sysfs_remove_group(&idev->dev.kobj, &mpl3115_attr_group);
|
|
input_unregister_polled_device(pdata->poll_dev);
|
|
kfree(pdata);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_PM_SLEEP
|
|
static int mpl3115_suspend(struct device *dev)
|
|
{
|
|
struct i2c_client *client = to_i2c_client(dev);
|
|
struct mpl3115_data *pdata = i2c_get_clientdata(client);
|
|
if (pdata->active == MPL_ACTIVED)
|
|
mpl3115_stop_chip(client);
|
|
return 0;
|
|
}
|
|
|
|
static int mpl3115_resume(struct device *dev)
|
|
{
|
|
struct i2c_client *client = to_i2c_client(dev);
|
|
struct mpl3115_data *pdata = i2c_get_clientdata(client);
|
|
if (pdata->active == MPL_ACTIVED)
|
|
mpl3115_start_chip(client);
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static const struct i2c_device_id mpl3115_id[] = {
|
|
{MPL3115_DRV_NAME, 0},
|
|
{ /* sentinel */ }
|
|
};
|
|
MODULE_DEVICE_TABLE(i2c, mpl3115_id);
|
|
|
|
static SIMPLE_DEV_PM_OPS(mpl3115_pm_ops, mpl3115_suspend, mpl3115_resume);
|
|
static struct i2c_driver mpl3115_driver = {
|
|
.driver = {
|
|
.name = MPL3115_DRV_NAME,
|
|
.owner = THIS_MODULE,
|
|
.pm = &mpl3115_pm_ops,
|
|
},
|
|
.probe = mpl3115_probe,
|
|
.remove = mpl3115_remove,
|
|
.id_table = mpl3115_id,
|
|
};
|
|
|
|
module_i2c_driver(mpl3115_driver);
|
|
|
|
MODULE_AUTHOR("Freescale Semiconductor, Inc.");
|
|
MODULE_DESCRIPTION("MPL3115 Smart Pressure Sensor driver");
|
|
MODULE_LICENSE("GPL");
|