| /* | |
| * 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; | |
| } | |
| 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; | |
| } | |
| 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"); |