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