Merge branch 'sensor/next' into next

* sensor/next: (29 commits)
  LF-99 hwmon: mag3110: correct processing order after probe error
  MLK-22296-4 misc: mpl3115: Fix build warning when CONFIG_PM_SLEEP=n
  MLK-17061-1 sensor: set sensor interrupt pins as open-drain
  hwmon: mma8451: Add regulator_disable to avoid WARN_ON
  hwmon: mag3110: Add regulator_disable to avoid WARN_ON
  ...
This commit is contained in:
Dong Aisheng 2019-12-02 18:05:28 +08:00
commit 7b8618cba8
18 changed files with 5909 additions and 17 deletions

View File

@ -1959,4 +1959,19 @@ config SENSORS_ATK0110
endif # ACPI
config SENSORS_MAG3110
tristate "Freescale MAG3110 e-compass sensor"
depends on I2C && SYSFS
help
If you say yes here you get support for the Freescale MAG3110
e-compass sensor.
This driver can also be built as a module. If so, the module
will be called mag3110.
config MXC_MMA8451
tristate "MMA8451 device driver"
depends on I2C
select INPUT_POLLDEV
default y
endif # HWMON

View File

@ -178,6 +178,8 @@ obj-$(CONFIG_SENSORS_W83L786NG) += w83l786ng.o
obj-$(CONFIG_SENSORS_WM831X) += wm831x-hwmon.o
obj-$(CONFIG_SENSORS_WM8350) += wm8350-hwmon.o
obj-$(CONFIG_SENSORS_XGENE) += xgene-hwmon.o
obj-$(CONFIG_SENSORS_MAG3110) += mag3110.o
obj-$(CONFIG_MXC_MMA8451) += mxc_mma8451.o
obj-$(CONFIG_SENSORS_OCC) += occ/
obj-$(CONFIG_PMBUS) += pmbus/

665
drivers/hwmon/mag3110.c Normal file
View File

@ -0,0 +1,665 @@
/*
*
* Copyright (C) 2011-2014 Freescale Semiconductor, Inc.
*
* 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.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/irq.h>
#include <linux/platform_device.h>
#include <linux/input-polldev.h>
#include <linux/hwmon.h>
#include <linux/input.h>
#include <linux/wait.h>
#include <linux/workqueue.h>
#include <linux/of.h>
#include <linux/regulator/consumer.h>
#define MAG3110_DRV_NAME "mag3110"
#define MAG3110_ID (0xC4)
#define MAG3110_XYZ_DATA_LEN (6)
#define MAG3110_STATUS_ZYXDR (0x08)
#define MAG3110_AC_MASK (0x01)
#define MAG3110_AC_OFFSET (0)
#define MAG3110_DR_MODE_MASK (0x7 << 5)
#define MAG3110_DR_MODE_OFFSET (5)
#define POLL_INTERVAL_MAX (500)
#define POLL_INTERVAL (100)
#define INT_TIMEOUT (1000)
#define DEFAULT_POSITION (2)
/* register enum for mag3110 registers */
enum {
MAG3110_DR_STATUS = 0x00,
MAG3110_OUT_X_MSB,
MAG3110_OUT_X_LSB,
MAG3110_OUT_Y_MSB,
MAG3110_OUT_Y_LSB,
MAG3110_OUT_Z_MSB,
MAG3110_OUT_Z_LSB,
MAG3110_WHO_AM_I,
MAG3110_OFF_X_MSB,
MAG3110_OFF_X_LSB,
MAG3110_OFF_Y_MSB,
MAG3110_OFF_Y_LSB,
MAG3110_OFF_Z_MSB,
MAG3110_OFF_Z_LSB,
MAG3110_DIE_TEMP,
MAG3110_CTRL_REG1 = 0x10,
MAG3110_CTRL_REG2,
};
enum {
MAG_STANDBY,
MAG_ACTIVED
};
struct mag3110_data {
struct i2c_client *client;
struct input_polled_dev *poll_dev;
struct device *hwmon_dev;
wait_queue_head_t waitq;
bool data_ready;
u8 ctl_reg1;
int active;
int position;
int use_irq;
};
static short MAGHAL[8][3][3] = {
{ {0, 1, 0}, {-1, 0, 0}, {0, 0, 1} },
{ {1, 0, 0}, {0, 1, 0}, {0, 0, 1} },
{ {0, -1, 0}, {1, 0, 0}, {0, 0, 1} },
{ {-1, 0, 0}, {0, -1, 0}, {0, 0, 1} },
{ {0, 1, 0}, {1, 0, 0}, {0, 0, -1} },
{ {1, 0, 0}, {0, -1, 0}, {0, 0, -1} },
{ {0, -1, 0}, {-1, 0, 0}, {0, 0, -1} },
{ {-1, 0, 0}, {0, 1, 0}, {0, 0, -1} },
};
static struct mag3110_data *mag3110_pdata;
static DEFINE_MUTEX(mag3110_lock);
/*
* This function do one mag3110 register read.
*/
static int mag3110_adjust_position(short *x, short *y, short *z)
{
short rawdata[3], data[3];
int i, j;
int position = mag3110_pdata->position;
if (position < 0 || position > 7)
position = 0;
rawdata[0] = *x;
rawdata[1] = *y;
rawdata[2] = *z;
for (i = 0; i < 3; i++) {
data[i] = 0;
for (j = 0; j < 3; j++)
data[i] += rawdata[j] * MAGHAL[position][i][j];
}
*x = data[0];
*y = data[1];
*z = data[2];
return 0;
}
static int mag3110_read_reg(struct i2c_client *client, u8 reg)
{
return i2c_smbus_read_byte_data(client, reg);
}
/*
* This function do one mag3110 register write.
*/
static int mag3110_write_reg(struct i2c_client *client, u8 reg, char value)
{
int ret;
ret = i2c_smbus_write_byte_data(client, reg, value);
if (ret < 0)
dev_err(&client->dev, "i2c write failed\n");
return ret;
}
/*
* This function do multiple mag3110 registers read.
*/
static int mag3110_read_block_data(struct i2c_client *client, u8 reg,
int count, u8 *addr)
{
if (i2c_smbus_read_i2c_block_data(client, reg, count, addr) < count) {
dev_err(&client->dev, "i2c block read failed\n");
return -1;
}
return count;
}
/*
* Initialization function
*/
static int mag3110_init_client(struct i2c_client *client)
{
int val, ret;
/* enable automatic resets */
val = 0x80;
ret = mag3110_write_reg(client, MAG3110_CTRL_REG2, val);
/* set default data rate to 10HZ */
val = mag3110_read_reg(client, MAG3110_CTRL_REG1);
val |= (0x0 << MAG3110_DR_MODE_OFFSET);
ret = mag3110_write_reg(client, MAG3110_CTRL_REG1, val);
return ret;
}
/*
* read sensor data from mag3110
*/
static int mag3110_read_data(short *x, short *y, short *z)
{
struct mag3110_data *data;
u8 tmp_data[MAG3110_XYZ_DATA_LEN];
int retry = 3;
int result;
if (!mag3110_pdata || mag3110_pdata->active == MAG_STANDBY)
return -EINVAL;
data = mag3110_pdata;
if (data->use_irq && !wait_event_interruptible_timeout
(data->waitq, data->data_ready != 0,
msecs_to_jiffies(INT_TIMEOUT))) {
dev_dbg(&data->client->dev, "interrupt not received\n");
return -ETIME;
}
do {
msleep(1);
result = i2c_smbus_read_byte_data(data->client,
MAG3110_DR_STATUS);
retry--;
} while (!(result & MAG3110_STATUS_ZYXDR) && retry > 0);
/* Clear data_ready flag after data is read out */
if (retry == 0)
return -EINVAL;
data->data_ready = 0;
while (i2c_smbus_read_byte_data(data->client, MAG3110_DR_STATUS)) {
if (mag3110_read_block_data(data->client,
MAG3110_OUT_X_MSB, MAG3110_XYZ_DATA_LEN,
tmp_data) < 0)
return -1;
}
*x = ((tmp_data[0] << 8) & 0xff00) | tmp_data[1];
*y = ((tmp_data[2] << 8) & 0xff00) | tmp_data[3];
*z = ((tmp_data[4] << 8) & 0xff00) | tmp_data[5];
return 0;
}
static void report_abs(void)
{
struct input_dev *idev;
short x, y, z;
mutex_lock(&mag3110_lock);
if (mag3110_read_data(&x, &y, &z) != 0)
goto out;
mag3110_adjust_position(&x, &y, &z);
idev = mag3110_pdata->poll_dev->input;
input_report_abs(idev, ABS_X, x);
input_report_abs(idev, ABS_Y, y);
input_report_abs(idev, ABS_Z, z);
input_sync(idev);
out:
mutex_unlock(&mag3110_lock);
}
static void mag3110_dev_poll(struct input_polled_dev *dev)
{
report_abs();
}
static irqreturn_t mag3110_irq_handler(int irq, void *dev_id)
{
int result;
u8 tmp_data[MAG3110_XYZ_DATA_LEN];
result = i2c_smbus_read_byte_data(mag3110_pdata->client,
MAG3110_DR_STATUS);
if (!(result & MAG3110_STATUS_ZYXDR))
return IRQ_NONE;
mag3110_pdata->data_ready = 1;
if (mag3110_pdata->active == MAG_STANDBY)
/*
* Since the mode will be changed, sometimes irq will
* be handled in StandBy mode because of interrupt latency.
* So just clear the interrutp flag via reading block data.
*/
mag3110_read_block_data(mag3110_pdata->client,
MAG3110_OUT_X_MSB,
MAG3110_XYZ_DATA_LEN, tmp_data);
else
wake_up_interruptible(&mag3110_pdata->waitq);
return IRQ_HANDLED;
}
static ssize_t mag3110_enable_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct i2c_client *client;
int val;
mutex_lock(&mag3110_lock);
client = mag3110_pdata->client;
val = mag3110_read_reg(client, MAG3110_CTRL_REG1) & MAG3110_AC_MASK;
mutex_unlock(&mag3110_lock);
return sprintf(buf, "%d\n", val);
}
static ssize_t mag3110_enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct i2c_client *client;
int reg, ret;
long enable;
u8 tmp_data[MAG3110_XYZ_DATA_LEN];
ret = kstrtol(buf, 10, &enable);
if (ret) {
dev_err(dev, "string to long error\n");
return ret;
}
mutex_lock(&mag3110_lock);
client = mag3110_pdata->client;
reg = mag3110_read_reg(client, MAG3110_CTRL_REG1);
if (enable && mag3110_pdata->active == MAG_STANDBY) {
reg |= MAG3110_AC_MASK;
ret = mag3110_write_reg(client, MAG3110_CTRL_REG1, reg);
if (!ret)
mag3110_pdata->active = MAG_ACTIVED;
} else if (!enable && mag3110_pdata->active == MAG_ACTIVED) {
reg &= ~MAG3110_AC_MASK;
ret = mag3110_write_reg(client, MAG3110_CTRL_REG1, reg);
if (!ret)
mag3110_pdata->active = MAG_STANDBY;
}
/* Read out MSB data to clear interrupt flag */
msleep(100);
mag3110_read_block_data(mag3110_pdata->client, MAG3110_OUT_X_MSB,
MAG3110_XYZ_DATA_LEN, tmp_data);
mutex_unlock(&mag3110_lock);
return count;
}
static DEVICE_ATTR(enable, S_IWUSR | S_IRUGO,
mag3110_enable_show, mag3110_enable_store);
static ssize_t mag3110_dr_mode_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct i2c_client *client;
int val;
client = mag3110_pdata->client;
val = (mag3110_read_reg(client, MAG3110_CTRL_REG1)
& MAG3110_DR_MODE_MASK) >> MAG3110_DR_MODE_OFFSET;
return sprintf(buf, "%d\n", val);
}
static ssize_t mag3110_dr_mode_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct i2c_client *client;
int reg, ret;
unsigned long val;
/* This must be done when mag3110 is disabled */
if ((kstrtoul(buf, 10, &val) < 0) || (val > 7))
return -EINVAL;
client = mag3110_pdata->client;
reg = mag3110_read_reg(client, MAG3110_CTRL_REG1) &
~MAG3110_DR_MODE_MASK;
reg |= (val << MAG3110_DR_MODE_OFFSET);
/* MAG3110_CTRL_REG1 bit 5-7: data rate mode */
ret = mag3110_write_reg(client, MAG3110_CTRL_REG1, reg);
if (ret < 0)
return ret;
return count;
}
static DEVICE_ATTR(dr_mode, S_IWUSR | S_IRUGO,
mag3110_dr_mode_show, mag3110_dr_mode_store);
static ssize_t mag3110_position_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int val;
mutex_lock(&mag3110_lock);
val = mag3110_pdata->position;
mutex_unlock(&mag3110_lock);
return sprintf(buf, "%d\n", val);
}
static ssize_t mag3110_position_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
long position;
int ret;
ret = kstrtol(buf, 10, &position);
if (ret) {
dev_err(dev, "string to long error\n");
return ret;
}
mutex_lock(&mag3110_lock);
mag3110_pdata->position = (int)position;
mutex_unlock(&mag3110_lock);
return count;
}
static DEVICE_ATTR(position, S_IWUSR | S_IRUGO,
mag3110_position_show, mag3110_position_store);
static struct attribute *mag3110_attributes[] = {
&dev_attr_enable.attr,
&dev_attr_dr_mode.attr,
&dev_attr_position.attr,
NULL
};
static const struct attribute_group mag3110_attr_group = {
.attrs = mag3110_attributes,
};
static int mag3110_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct i2c_adapter *adapter;
struct input_dev *idev = NULL;
struct mag3110_data *data;
int ret = 0;
struct regulator *vdd, *vdd_io;
u32 pos = 0;
struct device_node *of_node = client->dev.of_node;
u32 irq_flag;
struct irq_data *irq_data = NULL;
bool shared_irq = of_property_read_bool(of_node, "shared-interrupt");
vdd = NULL;
vdd_io = NULL;
vdd = devm_regulator_get(&client->dev, "vdd");
if (!IS_ERR(vdd)) {
ret = regulator_enable(vdd);
if (ret) {
dev_err(&client->dev, "vdd set voltage error\n");
return ret;
}
}
vdd_io = devm_regulator_get(&client->dev, "vddio");
if (!IS_ERR(vdd_io)) {
ret = regulator_enable(vdd_io);
if (ret) {
dev_err(&client->dev, "vddio set voltage error\n");
return ret;
}
}
adapter = to_i2c_adapter(client->dev.parent);
if (!i2c_check_functionality(adapter,
I2C_FUNC_SMBUS_BYTE |
I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_SMBUS_I2C_BLOCK)) {
ret = -EIO;
goto error_disable_reg;
}
dev_info(&client->dev, "check mag3110 chip ID\n");
ret = mag3110_read_reg(client, MAG3110_WHO_AM_I);
if (MAG3110_ID != ret) {
dev_err(&client->dev,
"read chip ID 0x%x is not equal to 0x%x!\n", ret,
MAG3110_ID);
ret = -EINVAL;
goto error_disable_reg;
}
data = kzalloc(sizeof(struct mag3110_data), GFP_KERNEL);
if (!data) {
ret = -ENOMEM;
goto error_disable_reg;
}
data->client = client;
i2c_set_clientdata(client, data);
/* Init queue */
init_waitqueue_head(&data->waitq);
data->hwmon_dev = hwmon_device_register(&client->dev);
if (IS_ERR(data->hwmon_dev)) {
dev_err(&client->dev, "hwmon register failed!\n");
ret = PTR_ERR(data->hwmon_dev);
goto error_rm_dev_sysfs;
}
if (client->irq > 0) {
data->use_irq = 1;
irq_data = irq_get_irq_data(client->irq);
}
/*input poll device register */
data->poll_dev = input_allocate_polled_device();
if (!data->poll_dev) {
dev_err(&client->dev, "alloc poll device failed!\n");
ret = -ENOMEM;
goto error_rm_hwmon_dev;
}
data->poll_dev->poll = mag3110_dev_poll;
data->poll_dev->poll_interval = POLL_INTERVAL;
data->poll_dev->poll_interval_max = POLL_INTERVAL_MAX;
idev = data->poll_dev->input;
idev->name = MAG3110_DRV_NAME;
idev->id.bustype = BUS_I2C;
idev->evbit[0] = BIT_MASK(EV_ABS);
input_set_abs_params(idev, ABS_X, -15000, 15000, 0, 0);
input_set_abs_params(idev, ABS_Y, -15000, 15000, 0, 0);
input_set_abs_params(idev, ABS_Z, -15000, 15000, 0, 0);
ret = input_register_polled_device(data->poll_dev);
if (ret) {
dev_err(&client->dev, "register poll device failed!\n");
goto error_free_poll_dev;
}
/*create device group in sysfs as user interface */
ret = sysfs_create_group(&idev->dev.kobj, &mag3110_attr_group);
if (ret) {
dev_err(&client->dev, "create device file failed!\n");
ret = -EINVAL;
goto error_rm_poll_dev;
}
if (data->use_irq) {
irq_flag = irqd_get_trigger_type(irq_data);
irq_flag |= IRQF_ONESHOT;
if (shared_irq)
irq_flag |= IRQF_SHARED;
ret = request_threaded_irq(client->irq, NULL, mag3110_irq_handler,
irq_flag, client->dev.driver->name, idev);
if (ret < 0) {
dev_err(&client->dev, "failed to register irq %d!\n",
client->irq);
goto error_rm_dev_sysfs;
}
}
/* Initialize mag3110 chip */
mag3110_init_client(client);
mag3110_pdata = data;
mag3110_pdata->active = MAG_STANDBY;
ret = of_property_read_u32(of_node, "position", &pos);
if (ret)
pos = DEFAULT_POSITION;
mag3110_pdata->position = (int)pos;
dev_info(&client->dev, "mag3110 is probed\n");
return 0;
error_rm_dev_sysfs:
sysfs_remove_group(&client->dev.kobj, &mag3110_attr_group);
error_rm_poll_dev:
input_unregister_polled_device(data->poll_dev);
error_free_poll_dev:
input_free_polled_device(data->poll_dev);
error_rm_hwmon_dev:
hwmon_device_unregister(data->hwmon_dev);
kfree(data);
mag3110_pdata = NULL;
error_disable_reg:
if (!IS_ERR(vdd))
regulator_disable(vdd);
if (!IS_ERR(vdd_io))
regulator_disable(vdd_io);
return ret;
}
static int mag3110_remove(struct i2c_client *client)
{
struct mag3110_data *data;
int ret;
data = i2c_get_clientdata(client);
data->ctl_reg1 = mag3110_read_reg(client, MAG3110_CTRL_REG1);
ret = mag3110_write_reg(client, MAG3110_CTRL_REG1,
data->ctl_reg1 & ~MAG3110_AC_MASK);
free_irq(client->irq, data);
input_unregister_polled_device(data->poll_dev);
input_free_polled_device(data->poll_dev);
hwmon_device_unregister(data->hwmon_dev);
sysfs_remove_group(&client->dev.kobj, &mag3110_attr_group);
kfree(data);
mag3110_pdata = NULL;
return ret;
}
#ifdef CONFIG_PM
static int mag3110_suspend(struct device *dev)
{
int ret = 0;
struct i2c_client *client = to_i2c_client(dev);
struct mag3110_data *data = i2c_get_clientdata(client);
if (data->active == MAG_ACTIVED) {
data->ctl_reg1 = mag3110_read_reg(client, MAG3110_CTRL_REG1);
ret = mag3110_write_reg(client, MAG3110_CTRL_REG1,
data->ctl_reg1 & ~MAG3110_AC_MASK);
}
return ret;
}
static int mag3110_resume(struct device *dev)
{
int ret = 0;
struct i2c_client *client = to_i2c_client(dev);
struct mag3110_data *data = i2c_get_clientdata(client);
u8 tmp_data[MAG3110_XYZ_DATA_LEN];
if (data->active == MAG_ACTIVED) {
ret = mag3110_write_reg(client, MAG3110_CTRL_REG1,
data->ctl_reg1);
if (data->ctl_reg1 & MAG3110_AC_MASK) {
/* Read out MSB data to clear interrupt
flag automatically */
mag3110_read_block_data(client, MAG3110_OUT_X_MSB,
MAG3110_XYZ_DATA_LEN, tmp_data);
}
}
return ret;
}
static const struct dev_pm_ops mag3110_dev_pm_ops = {
.suspend = mag3110_suspend,
.resume = mag3110_resume,
};
#define MAG3110_DEV_PM_OPS (&mag3110_dev_pm_ops)
#else
#define MAG3110_DEV_PM_OPS NULL
#endif /* CONFIG_PM */
static const struct i2c_device_id mag3110_id[] = {
{MAG3110_DRV_NAME, 0},
{}
};
MODULE_DEVICE_TABLE(i2c, mag3110_id);
static struct i2c_driver mag3110_driver = {
.driver = {
.name = MAG3110_DRV_NAME,
.owner = THIS_MODULE,
.pm = MAG3110_DEV_PM_OPS,
},
.probe = mag3110_probe,
.remove = mag3110_remove,
.id_table = mag3110_id,
};
static int __init mag3110_init(void)
{
return i2c_add_driver(&mag3110_driver);
}
static void __exit mag3110_exit(void)
{
i2c_del_driver(&mag3110_driver);
}
module_init(mag3110_init);
module_exit(mag3110_exit);
MODULE_AUTHOR("Freescale Semiconductor, Inc.");
MODULE_DESCRIPTION("Freescale mag3110 3-axis magnetometer driver");
MODULE_LICENSE("GPL");

603
drivers/hwmon/mxc_mma8451.c Normal file
View File

@ -0,0 +1,603 @@
/*
* mma8451.c - Linux kernel modules for 3-Axis Orientation/Motion
* Detection Sensor
*
* Copyright (C) 2010-2014 Freescale Semiconductor, 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>
#include <linux/of.h>
#include <linux/regulator/consumer.h>
#define MMA8451_I2C_ADDR 0x1C
#define MMA8451_ID 0x1A
#define MMA8452_ID 0x2A
#define MMA8453_ID 0x3A
#define POLL_INTERVAL_MIN 1
#define POLL_INTERVAL_MAX 500
#define POLL_INTERVAL 100 /* msecs */
#define INPUT_FUZZ 32
#define INPUT_FLAT 32
#define MODE_CHANGE_DELAY_MS 100
#define MMA8451_STATUS_ZYXDR 0x08
#define MMA8451_BUF_SIZE 7
#define DEFAULT_POSITION 0
/* register enum for mma8451 registers */
enum {
MMA8451_STATUS = 0x00,
MMA8451_OUT_X_MSB,
MMA8451_OUT_X_LSB,
MMA8451_OUT_Y_MSB,
MMA8451_OUT_Y_LSB,
MMA8451_OUT_Z_MSB,
MMA8451_OUT_Z_LSB,
MMA8451_F_SETUP = 0x09,
MMA8451_TRIG_CFG,
MMA8451_SYSMOD,
MMA8451_INT_SOURCE,
MMA8451_WHO_AM_I,
MMA8451_XYZ_DATA_CFG,
MMA8451_HP_FILTER_CUTOFF,
MMA8451_PL_STATUS,
MMA8451_PL_CFG,
MMA8451_PL_COUNT,
MMA8451_PL_BF_ZCOMP,
MMA8451_P_L_THS_REG,
MMA8451_FF_MT_CFG,
MMA8451_FF_MT_SRC,
MMA8451_FF_MT_THS,
MMA8451_FF_MT_COUNT,
MMA8451_TRANSIENT_CFG = 0x1D,
MMA8451_TRANSIENT_SRC,
MMA8451_TRANSIENT_THS,
MMA8451_TRANSIENT_COUNT,
MMA8451_PULSE_CFG,
MMA8451_PULSE_SRC,
MMA8451_PULSE_THSX,
MMA8451_PULSE_THSY,
MMA8451_PULSE_THSZ,
MMA8451_PULSE_TMLT,
MMA8451_PULSE_LTCY,
MMA8451_PULSE_WIND,
MMA8451_ASLP_COUNT,
MMA8451_CTRL_REG1,
MMA8451_CTRL_REG2,
MMA8451_CTRL_REG3,
MMA8451_CTRL_REG4,
MMA8451_CTRL_REG5,
MMA8451_OFF_X,
MMA8451_OFF_Y,
MMA8451_OFF_Z,
MMA8451_REG_END,
};
/* The sensitivity is represented in counts/g. In 2g mode the
sensitivity is 1024 counts/g. In 4g mode the sensitivity is 512
counts/g and in 8g mode the sensitivity is 256 counts/g.
*/
enum {
MODE_2G = 0,
MODE_4G,
MODE_8G,
};
enum {
MMA_STANDBY = 0,
MMA_ACTIVED,
};
/* mma8451 status */
struct mma8451_status {
u8 mode;
u8 ctl_reg1;
int active;
int position;
};
static struct mma8451_status mma_status;
static struct input_polled_dev *mma8451_idev;
static struct device *hwmon_dev;
static struct i2c_client *mma8451_i2c_client;
static int senstive_mode = MODE_2G;
static int ACCHAL[8][3][3] = {
{ {0, -1, 0}, {1, 0, 0}, {0, 0, 1} },
{ {-1, 0, 0}, {0, -1, 0}, {0, 0, 1} },
{ {0, 1, 0}, {-1, 0, 0}, {0, 0, 1} },
{ {1, 0, 0}, {0, 1, 0}, {0, 0, 1} },
{ {0, -1, 0}, {-1, 0, 0}, {0, 0, -1} },
{ {-1, 0, 0}, {0, 1, 0}, {0, 0, -1} },
{ {0, 1, 0}, {1, 0, 0}, {0, 0, -1} },
{ {1, 0, 0}, {0, -1, 0}, {0, 0, -1} },
};
static DEFINE_MUTEX(mma8451_lock);
static int mma8451_adjust_position(short *x, short *y, short *z)
{
short rawdata[3], data[3];
int i, j;
int position = mma_status.position;
if (position < 0 || position > 7)
position = 0;
rawdata[0] = *x;
rawdata[1] = *y;
rawdata[2] = *z;
for (i = 0; i < 3; i++) {
data[i] = 0;
for (j = 0; j < 3; j++)
data[i] += rawdata[j] * ACCHAL[position][i][j];
}
*x = data[0];
*y = data[1];
*z = data[2];
return 0;
}
static int mma8451_change_mode(struct i2c_client *client, int mode)
{
int result;
mma_status.ctl_reg1 = 0;
result = i2c_smbus_write_byte_data(client, MMA8451_CTRL_REG1, 0);
if (result < 0)
goto out;
mma_status.active = MMA_STANDBY;
result = i2c_smbus_write_byte_data(client, MMA8451_XYZ_DATA_CFG,
mode);
if (result < 0)
goto out;
mdelay(MODE_CHANGE_DELAY_MS);
mma_status.mode = mode;
return 0;
out:
dev_err(&client->dev, "error when init mma8451:(%d)", result);
return result;
}
static int mma8451_read_data(short *x, short *y, short *z)
{
u8 tmp_data[MMA8451_BUF_SIZE];
int ret;
ret = i2c_smbus_read_i2c_block_data(mma8451_i2c_client,
MMA8451_OUT_X_MSB, 7, tmp_data);
if (ret < MMA8451_BUF_SIZE) {
dev_err(&mma8451_i2c_client->dev, "i2c block read failed\n");
return -EIO;
}
*x = ((tmp_data[0] << 8) & 0xff00) | tmp_data[1];
*y = ((tmp_data[2] << 8) & 0xff00) | tmp_data[3];
*z = ((tmp_data[4] << 8) & 0xff00) | tmp_data[5];
return 0;
}
static void report_abs(void)
{
short x, y, z;
int result;
int retry = 3;
mutex_lock(&mma8451_lock);
if (mma_status.active == MMA_STANDBY)
goto out;
/* wait for the data ready */
do {
result = i2c_smbus_read_byte_data(mma8451_i2c_client,
MMA8451_STATUS);
retry--;
msleep(1);
} while (!(result & MMA8451_STATUS_ZYXDR) && retry > 0);
if (retry == 0)
goto out;
if (mma8451_read_data(&x, &y, &z) != 0)
goto out;
mma8451_adjust_position(&x, &y, &z);
input_report_abs(mma8451_idev->input, ABS_X, x);
input_report_abs(mma8451_idev->input, ABS_Y, y);
input_report_abs(mma8451_idev->input, ABS_Z, z);
input_sync(mma8451_idev->input);
out:
mutex_unlock(&mma8451_lock);
}
static void mma8451_dev_poll(struct input_polled_dev *dev)
{
report_abs();
}
static ssize_t mma8451_enable_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct i2c_client *client;
u8 val;
int enable;
mutex_lock(&mma8451_lock);
client = mma8451_i2c_client;
val = i2c_smbus_read_byte_data(client, MMA8451_CTRL_REG1);
if ((val & 0x01) && mma_status.active == MMA_ACTIVED)
enable = 1;
else
enable = 0;
mutex_unlock(&mma8451_lock);
return sprintf(buf, "%d\n", enable);
}
static ssize_t mma8451_enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct i2c_client *client;
int ret;
unsigned long enable;
u8 val = 0;
ret = kstrtoul(buf, 10, &enable);
if (ret) {
dev_err(dev, "string transform error\n");
return ret;
}
mutex_lock(&mma8451_lock);
client = mma8451_i2c_client;
enable = (enable > 0) ? 1 : 0;
if (enable && mma_status.active == MMA_STANDBY) {
val = i2c_smbus_read_byte_data(client, MMA8451_CTRL_REG1);
ret =
i2c_smbus_write_byte_data(client, MMA8451_CTRL_REG1,
val | 0x01);
if (!ret)
mma_status.active = MMA_ACTIVED;
} else if (enable == 0 && mma_status.active == MMA_ACTIVED) {
val = i2c_smbus_read_byte_data(client, MMA8451_CTRL_REG1);
ret =
i2c_smbus_write_byte_data(client, MMA8451_CTRL_REG1,
val & 0xFE);
if (!ret)
mma_status.active = MMA_STANDBY;
}
mutex_unlock(&mma8451_lock);
return count;
}
static ssize_t mma8451_position_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int position = 0;
mutex_lock(&mma8451_lock);
position = mma_status.position;
mutex_unlock(&mma8451_lock);
return sprintf(buf, "%d\n", position);
}
static ssize_t mma8451_position_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
unsigned long position;
int ret;
ret = kstrtoul(buf, 10, &position);
if (ret) {
dev_err(dev, "string transform error\n");
return ret;
}
mutex_lock(&mma8451_lock);
mma_status.position = (int)position;
mutex_unlock(&mma8451_lock);
return count;
}
static ssize_t mma8451_scalemode_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
int mode = 0;
mutex_lock(&mma8451_lock);
mode = (int)mma_status.mode;
mutex_unlock(&mma8451_lock);
return sprintf(buf, "%d\n", mode);
}
static ssize_t mma8451_scalemode_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
unsigned long mode;
int ret, active_save;
struct i2c_client *client = mma8451_i2c_client;
ret = kstrtoul(buf, 10, &mode);
if (ret) {
dev_err(dev, "string transform error\n");
goto out;
}
if (mode > MODE_8G) {
dev_warn(dev, "not supported mode\n");
ret = count;
goto out;
}
mutex_lock(&mma8451_lock);
if (mode == mma_status.mode) {
ret = count;
goto out_unlock;
}
active_save = mma_status.active;
ret = mma8451_change_mode(client, mode);
if (ret)
goto out_unlock;
if (active_save == MMA_ACTIVED) {
ret = i2c_smbus_write_byte_data(client, MMA8451_CTRL_REG1, 1);
if (ret)
goto out_unlock;
mma_status.active = active_save;
}
out_unlock:
mutex_unlock(&mma8451_lock);
out:
return ret;
}
static DEVICE_ATTR(enable, S_IWUSR | S_IRUGO,
mma8451_enable_show, mma8451_enable_store);
static DEVICE_ATTR(position, S_IWUSR | S_IRUGO,
mma8451_position_show, mma8451_position_store);
static DEVICE_ATTR(scalemode, S_IWUSR | S_IRUGO,
mma8451_scalemode_show, mma8451_scalemode_store);
static struct attribute *mma8451_attributes[] = {
&dev_attr_enable.attr,
&dev_attr_position.attr,
&dev_attr_scalemode.attr,
NULL
};
static const struct attribute_group mma8451_attr_group = {
.attrs = mma8451_attributes,
};
static int mma8451_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int result, client_id;
struct input_dev *idev;
struct i2c_adapter *adapter;
u32 pos;
struct device_node *of_node = client->dev.of_node;
struct regulator *vdd, *vdd_io;
mma8451_i2c_client = client;
vdd = devm_regulator_get(&client->dev, "vdd");
if (!IS_ERR(vdd)) {
result = regulator_enable(vdd);
if (result) {
dev_err(&client->dev, "vdd set voltage error\n");
return result;
}
}
vdd_io = devm_regulator_get(&client->dev, "vddio");
if (!IS_ERR(vdd_io)) {
result = regulator_enable(vdd_io);
if (result) {
dev_err(&client->dev, "vddio set voltage error\n");
return result;
}
}
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, MMA8451_WHO_AM_I);
if (client_id != MMA8451_ID && client_id != MMA8452_ID
&& client_id != MMA8453_ID) {
dev_err(&client->dev,
"read chip ID 0x%x is not equal to 0x%x or 0x%x!\n",
result, MMA8451_ID, MMA8452_ID);
result = -EINVAL;
goto err_out;
}
/* Initialize the MMA8451 chip */
result = mma8451_change_mode(client, senstive_mode);
if (result) {
dev_err(&client->dev,
"error when init mma8451 chip:(%d)\n", result);
goto err_out;
}
hwmon_dev = hwmon_device_register(&client->dev);
if (!hwmon_dev) {
result = -ENOMEM;
dev_err(&client->dev, "error when register hwmon device\n");
goto err_out;
}
mma8451_idev = input_allocate_polled_device();
if (!mma8451_idev) {
result = -ENOMEM;
dev_err(&client->dev, "alloc poll device failed!\n");
goto err_alloc_poll_device;
}
mma8451_idev->poll = mma8451_dev_poll;
mma8451_idev->poll_interval = POLL_INTERVAL;
mma8451_idev->poll_interval_min = POLL_INTERVAL_MIN;
mma8451_idev->poll_interval_max = POLL_INTERVAL_MAX;
idev = mma8451_idev->input;
idev->name = "mma845x";
idev->id.bustype = BUS_I2C;
idev->evbit[0] = BIT_MASK(EV_ABS);
input_set_abs_params(idev, ABS_X, -8192, 8191, INPUT_FUZZ, INPUT_FLAT);
input_set_abs_params(idev, ABS_Y, -8192, 8191, INPUT_FUZZ, INPUT_FLAT);
input_set_abs_params(idev, ABS_Z, -8192, 8191, INPUT_FUZZ, INPUT_FLAT);
result = input_register_polled_device(mma8451_idev);
if (result) {
dev_err(&client->dev, "register poll device failed!\n");
goto err_register_polled_device;
}
result = sysfs_create_group(&idev->dev.kobj, &mma8451_attr_group);
if (result) {
dev_err(&client->dev, "create device file failed!\n");
result = -EINVAL;
goto err_register_polled_device;
}
result = of_property_read_u32(of_node, "position", &pos);
if (result)
pos = DEFAULT_POSITION;
mma_status.position = (int)pos;
return 0;
err_register_polled_device:
input_free_polled_device(mma8451_idev);
err_alloc_poll_device:
hwmon_device_unregister(&client->dev);
err_out:
if (!IS_ERR(vdd))
regulator_disable(vdd);
if (!IS_ERR(vdd_io))
regulator_disable(vdd_io);
return result;
}
static int mma8451_stop_chip(struct i2c_client *client)
{
int ret = 0;
if (mma_status.active == MMA_ACTIVED) {
mma_status.ctl_reg1 = i2c_smbus_read_byte_data(client,
MMA8451_CTRL_REG1);
ret = i2c_smbus_write_byte_data(client, MMA8451_CTRL_REG1,
mma_status.ctl_reg1 & 0xFE);
}
return ret;
}
static int mma8451_remove(struct i2c_client *client)
{
int ret;
ret = mma8451_stop_chip(client);
hwmon_device_unregister(hwmon_dev);
return ret;
}
#ifdef CONFIG_PM_SLEEP
static int mma8451_suspend(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
return mma8451_stop_chip(client);
}
static int mma8451_resume(struct device *dev)
{
int ret = 0;
struct i2c_client *client = to_i2c_client(dev);
if (mma_status.active == MMA_ACTIVED)
ret = i2c_smbus_write_byte_data(client, MMA8451_CTRL_REG1,
mma_status.ctl_reg1);
return ret;
}
#endif
static const struct i2c_device_id mma8451_id[] = {
{"mma8451", 0},
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(i2c, mma8451_id);
static SIMPLE_DEV_PM_OPS(mma8451_pm_ops, mma8451_suspend, mma8451_resume);
static struct i2c_driver mma8451_driver = {
.driver = {
.name = "mma8451",
.owner = THIS_MODULE,
.pm = &mma8451_pm_ops,
},
.probe = mma8451_probe,
.remove = mma8451_remove,
.id_table = mma8451_id,
};
static int __init mma8451_init(void)
{
/* register driver */
int res;
res = i2c_add_driver(&mma8451_driver);
if (res < 0) {
printk(KERN_INFO "add mma8451 i2c driver failed\n");
return -ENODEV;
}
return res;
}
static void __exit mma8451_exit(void)
{
i2c_del_driver(&mma8451_driver);
}
MODULE_AUTHOR("Freescale Semiconductor, Inc.");
MODULE_DESCRIPTION("MMA8451 3-Axis Orientation/Motion Detection Sensor driver");
MODULE_LICENSE("GPL");
module_init(mma8451_init);
module_exit(mma8451_exit);

View File

@ -894,4 +894,39 @@ config INPUT_STPMIC1_ONKEY
To compile this driver as a module, choose M here: the
module will be called stpmic1_onkey.
config INPUT_MPL3115
tristate "MPL3115 pressure temperature sensor"
depends on I2C && SYSFS
help
If you say yes here you get support for the Freescale MPL3115
pressure temperature sensor.
This driver can also be built as a module. If so, the module
will be called mpl3115
config SENSOR_FXLS8471
tristate "FXLS8471 motion sensor device driver"
depends on I2C
default n
config SENSOR_IMX_RPMSG
tristate "NXP IMX rpmsg virtual sensor device driver"
depends on (SOC_IMX7ULP)
depends on RPMSG && SYSFS
depends on OF
default n
help
This is rpmsg virtual sensor driver on i.mx7ulp, because some
sensors connect with M4 core.
config INPUT_ISL29023
tristate "Intersil ISL29023 ambient light sensor"
depends on I2C && SYSFS
help
If you say yes here you get support for the Intersil ISL29023
ambient light sensor.
This driver can also be built as a module. If so, the module
will be called isl29023.
endif

View File

@ -85,4 +85,7 @@ obj-$(CONFIG_INPUT_WM831X_ON) += wm831x-on.o
obj-$(CONFIG_INPUT_XEN_KBDDEV_FRONTEND) += xen-kbdfront.o
obj-$(CONFIG_INPUT_YEALINK) += yealink.o
obj-$(CONFIG_INPUT_IDEAPAD_SLIDEBAR) += ideapad_slidebar.o
obj-$(CONFIG_INPUT_MPL3115) += mpl3115.o
obj-$(CONFIG_SENSOR_FXLS8471) += fxls8471.o fxls8471_i2c.o
obj-$(CONFIG_INPUT_ISL29023) += isl29023.o
obj-$(CONFIG_SENSOR_IMX_RPMSG) += rpmsg_input.o

View File

@ -0,0 +1,583 @@
/*
* fxls8471.c - Linux kernel modules for 3-Axis Accel sensor
* Copyright (C) 2014-2015 Freescale Semiconductor, 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/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>
#include <linux/miscdevice.h>
#include <linux/poll.h>
#include "fxls8471.h"
#define SENSOR_IOCTL_BASE 'S'
#define SENSOR_GET_MODEL_NAME _IOR(SENSOR_IOCTL_BASE, 0, char *)
#define SENSOR_GET_POWER_STATUS _IOR(SENSOR_IOCTL_BASE, 2, int)
#define SENSOR_SET_POWER_STATUS _IOR(SENSOR_IOCTL_BASE, 3, int)
#define SENSOR_GET_DELAY_TIME _IOR(SENSOR_IOCTL_BASE, 4, int)
#define SENSOR_SET_DELAY_TIME _IOR(SENSOR_IOCTL_BASE, 5, int)
#define SENSOR_GET_RAW_DATA _IOR(SENSOR_IOCTL_BASE, 6, short[3])
#define FXLS8471_POSITION_DEFAULT 2
#define FXLS8471_DELAY_DEFAULT 200
#define FXLS8471_STATUS_ZYXDR 0x08
#define FXLS8471_BUF_SIZE 6
struct fxls8471_data fxls8471_dev;
EXPORT_SYMBOL(fxls8471_dev);
static int fxls8471_position_setting[8][3][3] = {
{{0, -1, 0}, {1, 0, 0}, {0, 0, 1} },
{{-1, 0, 0}, {0, -1, 0}, {0, 0, 1} },
{{0, 1, 0}, {-1, 0, 0}, {0, 0, 1} },
{{1, 0, 0}, {0, 1, 0}, {0, 0, 1} },
{{0, -1, 0}, {-1, 0, 0}, {0, 0, -1} },
{{-1, 0, 0}, {0, 1, 0}, {0, 0, -1} },
{{0, 1, 0}, {1, 0, 0}, {0, 0, -1} },
{{1, 0, 0}, {0, -1, 0}, {0, 0, -1} },
};
static int fxls8471_bus_write(struct fxls8471_data *pdata, u8 reg, u8 val)
{
if (pdata && pdata->write)
return pdata->write(pdata, reg, val);
return -EIO;
}
static int fxls8471_bus_read(struct fxls8471_data *pdata, u8 reg)
{
if (pdata && pdata->read)
return pdata->read(pdata, reg);
return -EIO;
}
static int fxls8471_bus_read_block(struct fxls8471_data *pdata, u8 reg, u8 len,
u8 *val)
{
if (pdata && pdata->read_block)
return pdata->read_block(pdata, reg, len, val);
return -EIO;
}
static int fxls8471_data_convert(struct fxls8471_data *pdata,
struct fxls8471_data_axis *axis_data)
{
short rawdata[3], data[3];
int i, j;
int position = atomic_read(&pdata->position);
if (position < 0 || position > 7)
position = 0;
rawdata[0] = axis_data->x;
rawdata[1] = axis_data->y;
rawdata[2] = axis_data->z;
for (i = 0; i < 3; i++) {
data[i] = 0;
for (j = 0; j < 3; j++)
data[i] +=
rawdata[j] *
fxls8471_position_setting[position][i][j];
}
axis_data->x = data[0];
axis_data->y = data[1];
axis_data->z = data[2];
return 0;
}
static int fxls8471_device_init(struct fxls8471_data *pdata)
{
int result;
result = fxls8471_bus_write(pdata, FXLS8471_CTRL_REG1, 0);
if (result < 0)
goto out;
result = fxls8471_bus_write(pdata, FXLS8471_XYZ_DATA_CFG, MODE_2G);
if (result < 0)
goto out;
if (pdata->irq) {
result = fxls8471_bus_write(pdata, FXLS8471_CTRL_REG5, 0x01);
if (result < 0)
goto out;
result = fxls8471_bus_write(pdata, FXLS8471_CTRL_REG4, 0x01);
if (result < 0)
goto out;
}
atomic_set(&pdata->active, STANDBY);
return 0;
out:
printk("FXLS8471 device init error\n");
return result;
}
static int fxls8471_change_mode(struct fxls8471_data *pdata, int mode)
{
u8 val;
int ret;
val = fxls8471_bus_read(pdata, FXLS8471_CTRL_REG1);
if (mode == ACTIVED)
val |= 0x01;
else
val &= (~0x01);
ret = fxls8471_bus_write(pdata, FXLS8471_CTRL_REG1, val);
return ret;
}
static int fxls8471_set_delay(struct fxls8471_data *pdata, int delay)
{
u8 val;
val = fxls8471_bus_read(pdata, FXLS8471_CTRL_REG1);
/* set sensor standby */
fxls8471_bus_write(pdata, FXLS8471_CTRL_REG1, (val & ~0x01));
val &= ~(0x7 << 3);
if (delay <= 10)
val |= 0x02 << 3;
else if (delay <= 20)
val |= 0x03 << 3;
else if (delay <= 67)
val |= 0x04 << 3;
else
val |= 0x05 << 3;
/* set sensor standby */
fxls8471_bus_write(pdata, FXLS8471_CTRL_REG1, val);
return 0;
}
static int fxls8471_change_range(struct fxls8471_data *pdata, int range)
{
int ret;
ret = fxls8471_bus_write(pdata, FXLS8471_XYZ_DATA_CFG, range);
return ret;
}
static int fxls8471_read_data(struct fxls8471_data *pdata,
struct fxls8471_data_axis *data)
{
u8 tmp_data[FXLS8471_BUF_SIZE];
int ret;
ret = fxls8471_bus_read_block(pdata, FXLS8471_OUT_X_MSB,
FXLS8471_BUF_SIZE, tmp_data);
if (ret < FXLS8471_BUF_SIZE) {
printk(KERN_ERR "FXLS8471 read sensor block data error\n");
return -EIO;
}
data->x = ((tmp_data[0] << 8) & 0xff00) | tmp_data[1];
data->y = ((tmp_data[2] << 8) & 0xff00) | tmp_data[3];
data->z = ((tmp_data[4] << 8) & 0xff00) | tmp_data[5];
return 0;
}
/* fxls8471 miscdevice */
static long fxls8471_ioctl(struct file *file, unsigned int reg,
unsigned long arg)
{
struct fxls8471_data *pdata = file->private_data;
void __user *argp = (void __user *)arg;
long ret = 0;
short sdata[3];
int enable;
int delay;
struct fxls8471_data_axis data;
if (!pdata) {
printk(KERN_ERR "FXLS8471 struct datt point is NULL.");
return -EFAULT;
}
switch (reg) {
case SENSOR_GET_MODEL_NAME:
if (copy_to_user(argp, "fxls8471", strlen("fxls8471") + 1)) {
printk(KERN_ERR
"SENSOR_GET_MODEL_NAME copy_to_user failed.");
ret = -EFAULT;
}
break;
case SENSOR_GET_POWER_STATUS:
enable = atomic_read(&pdata->active);
if (copy_to_user(argp, &enable, sizeof(int))) {
printk(KERN_ERR
"SENSOR_SET_POWER_STATUS copy_to_user failed.");
ret = -EFAULT;
}
break;
case SENSOR_SET_POWER_STATUS:
if (copy_from_user(&enable, argp, sizeof(int))) {
printk(KERN_ERR
"SENSOR_SET_POWER_STATUS copy_to_user failed.");
ret = -EFAULT;
}
if (pdata) {
ret =
fxls8471_change_mode(pdata,
enable ? ACTIVED : STANDBY);
if (!ret)
atomic_set(&pdata->active, enable);
}
break;
case SENSOR_GET_DELAY_TIME:
delay = atomic_read(&pdata->delay);
if (copy_to_user(argp, &delay, sizeof(delay))) {
printk(KERN_ERR
"SENSOR_GET_DELAY_TIME copy_to_user failed.");
return -EFAULT;
}
break;
case SENSOR_SET_DELAY_TIME:
if (copy_from_user(&delay, argp, sizeof(int))) {
printk(KERN_ERR
"SENSOR_GET_DELAY_TIME copy_to_user failed.");
ret = -EFAULT;
}
if (pdata && delay > 0 && delay <= 500) {
ret = fxls8471_set_delay(pdata, delay);
if (!ret)
atomic_set(&pdata->delay, delay);
}
break;
case SENSOR_GET_RAW_DATA:
ret = fxls8471_read_data(pdata, &data);
if (!ret) {
fxls8471_data_convert(pdata, &data);
sdata[0] = data.x;
sdata[1] = data.y;
sdata[2] = data.z;
if (copy_to_user(argp, sdata, sizeof(sdata))) {
printk(KERN_ERR
"SENSOR_GET_RAW_DATA copy_to_user failed.");
ret = -EFAULT;
}
}
break;
default:
ret = -1;
}
return ret;
}
static int fxls8471_open(struct inode *inode, struct file *file)
{
file->private_data = &fxls8471_dev;
return nonseekable_open(inode, file);
}
static int fxls8471_release(struct inode *inode, struct file *file)
{
/* note: releasing the wdt in NOWAYOUT-mode does not stop it */
return 0;
}
static const struct file_operations fxls8471_fops = {
.owner = THIS_MODULE,
.open = fxls8471_open,
.release = fxls8471_release,
.unlocked_ioctl = fxls8471_ioctl,
};
static struct miscdevice fxls8471_device = {
.minor = MISC_DYNAMIC_MINOR,
.name = "FreescaleAccelerometer",
.fops = &fxls8471_fops,
};
static ssize_t fxls8471_enable_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct fxls8471_data *pdata = &fxls8471_dev;
int enable = 0;
enable = atomic_read(&pdata->active);
return sprintf(buf, "%d\n", enable);
}
static ssize_t fxls8471_enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct fxls8471_data *pdata = &fxls8471_dev;
int ret;
unsigned long enable;
if (kstrtoul(buf, 10, &enable) < 0)
return -EINVAL;
enable = (enable > 0) ? 1 : 0;
ret = fxls8471_change_mode(pdata, (enable > 0 ? ACTIVED : STANDBY));
if (!ret) {
atomic_set(&pdata->active, enable);
if (enable)
printk(KERN_INFO "mma enable setting actived\n");
else
printk(KERN_INFO "mma enable setting standby\n");
}
return count;
}
static ssize_t fxls8471_poll_delay_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct fxls8471_data *pdata = &fxls8471_dev;
int delay = 0;
delay = atomic_read(&pdata->delay);
return sprintf(buf, "%d\n", delay);
}
static ssize_t fxls8471_poll_delay_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct fxls8471_data *pdata = &fxls8471_dev;
int ret;
unsigned long delay;
if (kstrtoul(buf, 10, &delay) < 0)
return -EINVAL;
ret = fxls8471_set_delay(pdata, delay);
if (!ret)
atomic_set(&pdata->delay, delay);
return count;
}
static ssize_t fxls8471_position_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct fxls8471_data *pdata = &fxls8471_dev;
int position = 0;
position = atomic_read(&pdata->position);
return sprintf(buf, "%d\n", position);
}
static ssize_t fxls8471_position_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct fxls8471_data *pdata = &fxls8471_dev;
unsigned long position;
if (kstrtoul(buf, 10, &position) < 0)
return -EINVAL;
atomic_set(&pdata->position, position);
return count;
}
static ssize_t fxls8471_data_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct fxls8471_data *pdata = &fxls8471_dev;
int ret = 0;
struct fxls8471_data_axis data;
ret = fxls8471_read_data(pdata, &data);
if (!ret)
fxls8471_data_convert(pdata, &data);
return sprintf(buf, "%d,%d,%d\n", data.x, data.y, data.z);
}
static ssize_t fxls8471_range_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct fxls8471_data *pdata = &fxls8471_dev;
int range = 0;
range = atomic_read(&pdata->range);
return sprintf(buf, "%d\n", range);
}
static ssize_t fxls8471_range_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct fxls8471_data *pdata = &fxls8471_dev;
int ret;
unsigned long range;
if (kstrtoul(buf, 10, &range) < 0)
return -EINVAL;
if (range == atomic_read(&pdata->range))
return count;
if (atomic_read(&pdata->active))
printk(KERN_INFO "Pls set the sensor standby and then actived\n");
ret = fxls8471_change_range(pdata, range);
if (!ret)
atomic_set(&pdata->range, range);
return count;
}
static DEVICE_ATTR(enable, S_IWUSR | S_IRUGO, fxls8471_enable_show, fxls8471_enable_store);
static DEVICE_ATTR(poll_delay, S_IWUSR | S_IRUGO, fxls8471_poll_delay_show,
fxls8471_poll_delay_store);
static DEVICE_ATTR(position, S_IWUSR | S_IRUGO, fxls8471_position_show,
fxls8471_position_store);
static DEVICE_ATTR(data, S_IWUSR | S_IRUGO, fxls8471_data_show, NULL);
static DEVICE_ATTR(range, S_IWUSR | S_IRUGO, fxls8471_range_show, fxls8471_range_store);
static struct attribute *fxls8471_attributes[] = {
&dev_attr_enable.attr,
&dev_attr_poll_delay.attr,
&dev_attr_position.attr,
&dev_attr_data.attr,
&dev_attr_range.attr,
NULL
};
static const struct attribute_group fxls8471_attr_group = {
.attrs = fxls8471_attributes,
};
static irqreturn_t fxls8471_irq_handler(int irq, void *dev)
{
int ret;
u8 int_src;
struct fxls8471_data *pdata = (struct fxls8471_data *)dev;
struct fxls8471_data_axis data;
int_src = fxls8471_bus_read(pdata, FXLS8471_INT_SOURCE);
/* data ready interrupt */
if (int_src & 0x01) {
ret = fxls8471_read_data(pdata, &data);
if (!ret) {
fxls8471_data_convert(pdata, &data);
input_report_abs(pdata->idev, ABS_X, data.x);
input_report_abs(pdata->idev, ABS_Y, data.y);
input_report_abs(pdata->idev, ABS_Z, data.z);
input_sync(pdata->idev);
}
}
return IRQ_HANDLED;
}
int fxls8471_driver_init(struct fxls8471_data *pdata)
{
int result, chip_id;
chip_id = fxls8471_bus_read(pdata, FXLS8471_WHO_AM_I);
if (chip_id != FXSL8471_ID) {
printk(KERN_ERR "read sensor who am i (0x%x)error !\n",
chip_id);
result = -EINVAL;
goto err_out;
}
/* Initialize the FXLS8471 chip */
pdata->chip_id = chip_id;
atomic_set(&pdata->delay, FXLS8471_DELAY_DEFAULT);
atomic_set(&pdata->position, FXLS8471_POSITION_DEFAULT);
result = misc_register(&fxls8471_device);
if (result != 0) {
printk(KERN_ERR "register acc miscdevice error");
goto err_out;
}
result =
sysfs_create_group(&fxls8471_device.this_device->kobj,
&fxls8471_attr_group);
if (result) {
printk(KERN_ERR "create device file failed!\n");
result = -EINVAL;
goto err_create_sysfs;
}
/*create data input device */
pdata->idev = input_allocate_device();
if (!pdata->idev) {
result = -ENOMEM;
printk(KERN_ERR "alloc fxls8471 input device failed!\n");
goto err_alloc_input_device;
}
pdata->idev->name = "FreescaleAccelerometer";
pdata->idev->id.bustype = BUS_I2C;
pdata->idev->evbit[0] = BIT_MASK(EV_ABS);
input_set_abs_params(pdata->idev, ABS_X, -0x7fff, 0x7fff, 0, 0);
input_set_abs_params(pdata->idev, ABS_Y, -0x7fff, 0x7fff, 0, 0);
input_set_abs_params(pdata->idev, ABS_Z, -0x7fff, 0x7fff, 0, 0);
result = input_register_device(pdata->idev);
if (result) {
printk(KERN_ERR "register fxls8471 input device failed!\n");
goto err_register_input_device;
}
if (pdata->irq) {
result =
request_threaded_irq(pdata->irq, NULL, fxls8471_irq_handler,
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
pdata->idev->name, pdata);
if (result < 0) {
printk(KERN_ERR "failed to register MMA8x5x irq %d!\n",
pdata->irq);
goto err_register_irq;
}
}
fxls8471_device_init(pdata);
printk("fxls8471 device driver probe successfully\n");
return 0;
err_register_irq:
input_unregister_device(pdata->idev);
err_register_input_device:
input_free_device(pdata->idev);
err_alloc_input_device:
sysfs_remove_group(&fxls8471_device.this_device->kobj,
&fxls8471_attr_group);
err_create_sysfs:
misc_deregister(&fxls8471_device);
err_out:
return result;
}
EXPORT_SYMBOL_GPL(fxls8471_driver_init);
int fxls8471_driver_remove(struct fxls8471_data *pdata)
{
fxls8471_change_mode(pdata, STANDBY);
misc_deregister(&fxls8471_device);
return 0;
}
EXPORT_SYMBOL_GPL(fxls8471_driver_remove);
#ifdef CONFIG_PM_SLEEP
int fxls8471_driver_suspend(struct fxls8471_data *pdata)
{
if (atomic_read(&pdata->active))
fxls8471_change_mode(pdata, STANDBY);
return 0;
}
EXPORT_SYMBOL_GPL(fxls8471_driver_suspend);
int fxls8471_driver_resume(struct fxls8471_data *pdata)
{
if (atomic_read(&pdata->active))
fxls8471_change_mode(pdata, ACTIVED);
return 0;
}
EXPORT_SYMBOL_GPL(fxls8471_driver_resume);
#endif

View File

@ -0,0 +1,94 @@
/*
* fxls8471.h - Linux kernel modules for 3-Axis Accel sensor
* Copyright (C) 2014-2015 Freescale Semiconductor, 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.
*
*/
#ifndef _FXSL8471_H
#define _FXSL8471_H
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/i2c.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/input.h>
#define FXSL8471_ID 0x6a
/* register enum for fxls8471 registers */
enum { FXLS8471_STATUS = 0x00, FXLS8471_OUT_X_MSB, FXLS8471_OUT_X_LSB,
FXLS8471_OUT_Y_MSB, FXLS8471_OUT_Y_LSB, FXLS8471_OUT_Z_MSB,
FXLS8471_OUT_Z_LSB, FXLS8471_F_SETUP =
0x09, FXLS8471_TRIG_CFG, FXLS8471_SYSMOD,
FXLS8471_INT_SOURCE, FXLS8471_WHO_AM_I,
FXLS8471_XYZ_DATA_CFG, FXLS8471_HP_FILTER_CUTOFF,
FXLS8471_PL_STATUS, FXLS8471_PL_CFG, FXLS8471_PL_COUNT,
FXLS8471_PL_BF_ZCOMP, FXLS8471_P_L_THS_REG,
FXLS8471_FF_MT_CFG, FXLS8471_FF_MT_SRC, FXLS8471_FF_MT_THS,
FXLS8471_FF_MT_COUNT, FXLS8471_TRANSIENT_CFG =
0x1D, FXLS8471_TRANSIENT_SRC, FXLS8471_TRANSIENT_THS,
FXLS8471_TRANSIENT_COUNT, FXLS8471_PULSE_CFG,
FXLS8471_PULSE_SRC, FXLS8471_PULSE_THSX, FXLS8471_PULSE_THSY,
FXLS8471_PULSE_THSZ, FXLS8471_PULSE_TMLT,
FXLS8471_PULSE_LTCY, FXLS8471_PULSE_WIND,
FXLS8471_ASLP_COUNT, FXLS8471_CTRL_REG1, FXLS8471_CTRL_REG2,
FXLS8471_CTRL_REG3, FXLS8471_CTRL_REG4, FXLS8471_CTRL_REG5,
FXLS8471_OFF_X, FXLS8471_OFF_Y, FXLS8471_OFF_Z,
FXLS8471_REG_END,
};
enum { STANDBY = 0, ACTIVED,
};
enum { MODE_2G = 0, MODE_4G, MODE_8G,
};
struct fxls8471_data_axis {
short x;
short y;
short z;
};
struct fxls8471_data {
void *bus_priv;
u16 bus_type;
int irq;
s32 (*write)(struct fxls8471_data *pdata, u8 reg, u8 val);
s32 (*read)(struct fxls8471_data *pdata, u8 reg);
s32 (*read_block)(struct fxls8471_data *pdata, u8 reg, u8 len,
u8 *val);
struct input_dev *idev;
atomic_t active;
atomic_t delay;
atomic_t position;
atomic_t range;
u8 chip_id;
};
extern struct fxls8471_data fxls8471_dev;
int fxls8471_driver_init(struct fxls8471_data *pdata);
int fxls8471_driver_remove(struct fxls8471_data *pdata);
int fxls8471_driver_suspend(struct fxls8471_data *pdata);
int fxls8471_driver_resume(struct fxls8471_data *pdata);
#endif /* */

View File

@ -0,0 +1,111 @@
/*
* fxls8471-i2c.c - Linux kernel modules for 3-Axis Smart Orientation
* /Motion Sensor
* Version : 01.00
* Time : Dec.26, 2012
* Author : rick zhang <rick.zhang@freescale.com>
*
* Copyright (C) 2010-2011 Freescale Semiconductor.
*
* 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/kernel.h>
#include <linux/init.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include "fxls8471.h"
static s32 fxls8471_i2c_write(struct fxls8471_data *pdata, u8 reg, u8 val)
{
struct i2c_client *client = (struct i2c_client *)pdata->bus_priv;
return i2c_smbus_write_byte_data(client, reg, val);
}
static int fxls8471_i2c_read(struct fxls8471_data *pdata, u8 reg)
{
struct i2c_client *client = (struct i2c_client *)pdata->bus_priv;
return i2c_smbus_read_byte_data(client, reg);
}
static int fxls8471_i2c_read_block(struct fxls8471_data *pdata, u8 reg, u8 len,
u8 *val)
{
struct i2c_client *client = (struct i2c_client *)pdata->bus_priv;
return i2c_smbus_read_i2c_block_data(client, reg, len, val);
}
static int fxls8471_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
fxls8471_dev.bus_priv = client;
fxls8471_dev.bus_type = BUS_I2C;
fxls8471_dev.irq = client->irq;
fxls8471_dev.read = fxls8471_i2c_read;
fxls8471_dev.write = fxls8471_i2c_write;
fxls8471_dev.read_block = fxls8471_i2c_read_block;
i2c_set_clientdata(client, &fxls8471_dev);
return fxls8471_driver_init(&fxls8471_dev);
}
static int fxls8471_i2c_remove(struct i2c_client *client)
{
return fxls8471_driver_remove(&fxls8471_dev);
}
#ifdef CONFIG_PM_SLEEP
static int fxls8471_i2c_suspend(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
return fxls8471_driver_suspend(i2c_get_clientdata(client));
}
static int fxls8471_i2c_resume(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
return fxls8471_driver_resume(i2c_get_clientdata(client));
}
#else /* */
#define fxls8471_i2c_suspend NULL
#define fxls8471_i2c_resume NULL
#endif /* */
static const struct i2c_device_id fxls8471_i2c_id[] = {
{"fxls8471", 0},
{}
};
MODULE_DEVICE_TABLE(i2c, fxls8471_i2c_id);
static SIMPLE_DEV_PM_OPS(fxls8471_pm_ops, fxls8471_i2c_suspend,
fxls8471_i2c_resume);
static struct i2c_driver fxls8471_i2c_driver = {
.driver = {
.name = "fxls8471",
.owner = THIS_MODULE,
.pm = &fxls8471_pm_ops,
},
.probe = fxls8471_i2c_probe,
.remove = fxls8471_i2c_remove,
.id_table = fxls8471_i2c_id,
};
module_i2c_driver(fxls8471_i2c_driver);
MODULE_AUTHOR("Freescale Semiconductor, Inc.");
MODULE_DESCRIPTION("FXLS8471 3-Axis Acc Sensor driver");
MODULE_LICENSE("GPL");

File diff suppressed because it is too large Load Diff

View File

@ -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,
};

View File

@ -0,0 +1,360 @@
/*
* 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");

View File

@ -0,0 +1,528 @@
/*
* Copyright 2018 NXP
*
* 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.
*
*/
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/imx_rpmsg.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/hwmon-sysfs.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_qos.h>
#include <linux/rpmsg.h>
#include <linux/uaccess.h>
#include <linux/virtio.h>
#define RPMSG_INPUT_DEV_STANDBY 0x00
#define RPMSG_INPUT_DEV_ACTIVED 0x01
#define DETECTOR_DEVICE "step_detector"
#define COUNTER_DEVICE "step_counter"
#define PEDOMETER_TYPE 0x0
#define PEDOMETER_IDX 0x0
#define RPMSG_TIMEOUT 1000
enum rpmsg_input_header_type {
RPMSG_INPUT_SETUP,
RPMSG_INPUT_REPLY,
RPMSG_INPUT_NOTIFY,
};
enum rpmsg_input_header_cmd {
RPMSG_INPUT_DETECTOR_CMD,
RPMSG_INPUT_COUNTER_CMD,
RPMSG_INPUT_POLL_DELAY_CMD,
};
struct rpmsg_input_msg {
struct imx_rpmsg_head header;
u8 sensor_type;
u8 sensor_index;
union {
union {
u8 enable;
u8 retcode;
} inout;
u32 val;
} instruct;
} __packed __aligned(8);
struct rpmsg_input_data {
struct device *dev;
struct miscdevice *detector_miscdev;
struct miscdevice *counter_miscdev;
struct input_dev *detector_input;
struct input_dev *counter_input;
atomic_t detector_active;
atomic_t counter_active;
atomic_t counter_delay;
struct rpmsg_device *rpdev;
struct rpmsg_input_msg *reply_msg;
struct rpmsg_input_msg *notify_msg;
struct pm_qos_request pm_qos_req;
struct completion cmd_complete;
struct mutex lock;
};
static struct rpmsg_input_data *input_rpmsg;
static int input_send_message(struct rpmsg_input_msg *msg,
struct rpmsg_input_data *info)
{
int err;
if (!info->rpdev) {
dev_dbg(info->dev,
"rpmsg channel not ready, m4 image ready?\n");
return -EINVAL;
}
mutex_lock(&info->lock);
pm_qos_add_request(&info->pm_qos_req,
PM_QOS_CPU_DMA_LATENCY, 0);
reinit_completion(&info->cmd_complete);
err = rpmsg_send(info->rpdev->ept, (void *)msg,
sizeof(struct rpmsg_input_msg));
if (err) {
dev_err(&info->rpdev->dev, "rpmsg_send failed: %d\n", err);
goto err_out;
}
err = wait_for_completion_timeout(&info->cmd_complete,
msecs_to_jiffies(RPMSG_TIMEOUT));
if (!err) {
dev_err(&info->rpdev->dev, "rpmsg_send timeout!\n");
err = -ETIMEDOUT;
goto err_out;
}
err = info->reply_msg->instruct.inout.retcode;
if (err != 0) {
dev_err(&info->rpdev->dev, "rpmsg not ack %d!\n", err);
err = -EINVAL;
goto err_out;
}
err = 0;
err_out:
pm_qos_remove_request(&info->pm_qos_req);
mutex_unlock(&info->lock);
return err;
}
static inline void rpmsg_input_evt_report(struct rpmsg_input_msg *msg)
{
int val = 0x1;
if (msg->header.cmd == RPMSG_INPUT_DETECTOR_CMD) {
input_report_rel(input_rpmsg->detector_input, REL_MISC, val);
input_sync(input_rpmsg->detector_input);
} else if (msg->header.cmd == RPMSG_INPUT_COUNTER_CMD) {
val = msg->instruct.val;
input_report_abs(input_rpmsg->counter_input, ABS_MISC, val);
input_sync(input_rpmsg->counter_input);
}
}
static int rpmsg_input_cb(struct rpmsg_device *rpdev,
void *data, int len, void *priv, u32 src)
{
struct rpmsg_input_msg *msg = (struct rpmsg_input_msg *)data;
if (msg->header.type == RPMSG_INPUT_REPLY) {
input_rpmsg->reply_msg = msg;
complete(&input_rpmsg->cmd_complete);
return 0;
} else if (msg->header.type == RPMSG_INPUT_NOTIFY) {
input_rpmsg->notify_msg = msg;
rpmsg_input_evt_report(msg);
} else
dev_err(&input_rpmsg->rpdev->dev, "wrong command type!\n");
return 0;
}
static int rpmsg_input_probe(struct rpmsg_device *rpdev)
{
input_rpmsg->rpdev = rpdev;
dev_info(&rpdev->dev, "new channel: 0x%x -> 0x%x!\n",
rpdev->src, rpdev->dst);
init_completion(&input_rpmsg->cmd_complete);
mutex_init(&input_rpmsg->lock);
return 0;
}
static struct rpmsg_device_id rpmsg_input_id_table[] = {
{ .name = "rpmsg-sensor-channel" },
{ },
};
static struct rpmsg_driver rpmsg_input_driver = {
.drv.name = "input_rpmsg",
.drv.owner = THIS_MODULE,
.id_table = rpmsg_input_id_table,
.probe = rpmsg_input_probe,
.callback = rpmsg_input_cb,
};
static int rpmsg_input_open(struct inode *inode, struct file *file)
{
file->private_data = input_rpmsg;
return nonseekable_open(inode, file);
}
static int rpmsg_input_release(struct inode *inode, struct file *file)
{
return 0;
}
static long
rpmsg_input_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
/* TBD: for future sensor interfaces support */
return 0;
}
static const struct file_operations rpmsg_input_fops = {
.owner = THIS_MODULE,
.open = rpmsg_input_open,
.release = rpmsg_input_release,
.unlocked_ioctl = rpmsg_input_ioctl,
};
static struct miscdevice step_detector_device = {
.minor = MISC_DYNAMIC_MINOR,
.name = "step_detector",
.fops = &rpmsg_input_fops,
};
static struct miscdevice step_counter_device = {
.minor = MISC_DYNAMIC_MINOR,
.name = "step_counter",
.fops = &rpmsg_input_fops,
};
static int rpmsg_input_change_mode(int cmd, int enable)
{
struct rpmsg_input_msg msg;
memset(&msg, 0, sizeof(struct rpmsg_input_msg));
msg.header.cate = IMX_RPMSG_SENSOR;
msg.header.major = IMX_RMPSG_MAJOR;
msg.header.minor = IMX_RMPSG_MINOR;
msg.header.type = RPMSG_INPUT_SETUP;
msg.header.cmd = cmd;
msg.sensor_type = PEDOMETER_TYPE;
msg.sensor_index = PEDOMETER_IDX;
msg.instruct.inout.enable = enable;
return input_send_message(&msg, input_rpmsg);
}
static int rpmsg_input_set_poll_delay(int cmd, int delay)
{
struct rpmsg_input_msg msg;
memset(&msg, 0, sizeof(struct rpmsg_input_msg));
msg.header.cate = IMX_RPMSG_SENSOR;
msg.header.major = IMX_RMPSG_MAJOR;
msg.header.minor = IMX_RMPSG_MINOR;
msg.header.type = RPMSG_INPUT_SETUP;
msg.header.cmd = cmd;
msg.sensor_type = PEDOMETER_TYPE;
msg.sensor_index = PEDOMETER_IDX;
msg.instruct.val = delay;
return input_send_message(&msg, input_rpmsg);
}
static ssize_t rpmsg_input_enable_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct miscdevice *misc_dev = dev_get_drvdata(dev);
struct rpmsg_input_data *pdata = input_rpmsg;
int enable = 0;
if (pdata->detector_miscdev == misc_dev)
enable = atomic_read(&pdata->detector_active);
if (pdata->counter_miscdev == misc_dev)
enable = atomic_read(&pdata->counter_active);
return sprintf(buf, "%d\n", enable);
}
static ssize_t rpmsg_input_enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct miscdevice *misc_dev = dev_get_drvdata(dev);
struct rpmsg_input_data *pdata = input_rpmsg;
unsigned long enable;
int cmd;
int ret;
if (kstrtoul(buf, 10, &enable) < 0)
return -EINVAL;
if (misc_dev == pdata->detector_miscdev)
cmd = RPMSG_INPUT_DETECTOR_CMD;
if (misc_dev == pdata->counter_miscdev)
cmd = RPMSG_INPUT_COUNTER_CMD;
enable = enable > 0 ? RPMSG_INPUT_DEV_ACTIVED :
RPMSG_INPUT_DEV_STANDBY;
ret = rpmsg_input_change_mode(cmd, enable);
if (!ret) {
if (cmd == RPMSG_INPUT_DETECTOR_CMD)
atomic_set(&pdata->detector_active, enable);
else if (cmd == RPMSG_INPUT_COUNTER_CMD)
atomic_set(&pdata->counter_active, enable);
}
return count;
}
static ssize_t rpmsg_input_poll_delay_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct miscdevice *misc_dev = dev_get_drvdata(dev);
struct rpmsg_input_data *pdata = input_rpmsg;
int poll_delay = 0;
if (pdata->detector_miscdev == misc_dev)
return -ENOTSUPP;
if (pdata->counter_miscdev == misc_dev)
poll_delay = atomic_read(&pdata->counter_delay);
return sprintf(buf, "%d\n", poll_delay);
}
static ssize_t rpmsg_input_poll_delay_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct miscdevice *misc_dev = dev_get_drvdata(dev);
struct rpmsg_input_data *pdata = input_rpmsg;
unsigned long delay;
int cmd;
int ret;
if (kstrtoul(buf, 10, &delay) < 0)
return -EINVAL;
if (pdata->detector_miscdev == misc_dev)
return -ENOTSUPP;
if (pdata->counter_miscdev == misc_dev)
cmd = RPMSG_INPUT_POLL_DELAY_CMD;
ret = rpmsg_input_set_poll_delay(cmd, delay);
if (!ret) {
if (cmd == RPMSG_INPUT_POLL_DELAY_CMD)
atomic_set(&pdata->counter_delay, delay);
}
return count;
}
static DEVICE_ATTR(enable, S_IWUSR | S_IRUGO,
rpmsg_input_enable_show,
rpmsg_input_enable_store);
static DEVICE_ATTR(poll_delay, S_IWUSR | S_IRUGO,
rpmsg_input_poll_delay_show,
rpmsg_input_poll_delay_store);
static struct attribute *rpmsg_input_attributes[] = {
&dev_attr_enable.attr,
&dev_attr_poll_delay.attr,
NULL
};
static const struct attribute_group rpmsg_input_attr_group = {
.attrs = rpmsg_input_attributes,
};
static int rpmsg_input_register_sysfs_device(struct rpmsg_input_data *pdata)
{
struct miscdevice *misc_dev = NULL;
int err = 0;
/* register sysfs for detector */
misc_dev = pdata->detector_miscdev;
err = sysfs_create_group(&misc_dev->this_device->kobj,
&rpmsg_input_attr_group);
if (err)
goto out;
/* register sysfs for counter */
misc_dev = pdata->counter_miscdev;
err = sysfs_create_group(&misc_dev->this_device->kobj,
&rpmsg_input_attr_group);
if (err)
goto err_register_sysfs;
return 0;
err_register_sysfs:
misc_dev = pdata->detector_miscdev;
sysfs_remove_group(&misc_dev->this_device->kobj, &rpmsg_input_attr_group);
out:
return err;
}
static int rpmsg_input_device_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct rpmsg_input_data *pdata;
struct input_dev *detector_input;
struct input_dev *counter_input;
int ret = 0;
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
return -ENOMEM;
detector_input = devm_input_allocate_device(dev);
if (!detector_input) {
dev_err(dev, "failed to allocate detector device\n");
return -ENOMEM;
}
counter_input = devm_input_allocate_device(dev);
if (!counter_input) {
dev_err(dev, "failed to allocate counter device\n");
return -ENOMEM;
}
detector_input->name = DETECTOR_DEVICE;
detector_input->phys = "rpmsg-input/input0";
detector_input->dev.parent = &pdev->dev;
detector_input->id.bustype = BUS_HOST;
detector_input->evbit[0] = BIT_MASK(EV_REL);
detector_input->relbit[0] = BIT_MASK(REL_MISC);
pdata->detector_input = detector_input;
counter_input->name = COUNTER_DEVICE;
counter_input->phys = "rpmsg-input/input1";
counter_input->dev.parent = &pdev->dev;
counter_input->id.bustype = BUS_HOST;
counter_input->evbit[0] = BIT_MASK(EV_ABS);
input_set_abs_params(counter_input, ABS_MISC, 0, 0xFFFFFFFF, 0, 0);
pdata->counter_input = counter_input;
input_rpmsg = pdata;
platform_set_drvdata(pdev, pdata);
ret = misc_register(&step_detector_device);
if (ret != 0) {
dev_err(dev, "register acc miscdevice error");
goto err_register_detector_misc;
}
pdata->detector_miscdev = &step_detector_device;
ret = misc_register(&step_counter_device);
if (ret != 0) {
dev_err(dev, "register acc miscdevice error");
goto err_register_counter_misc;
}
pdata->counter_miscdev = &step_counter_device;
ret = rpmsg_input_register_sysfs_device(pdata);
if (ret) {
dev_err(dev, "Unable to register input sysfs, error: %d\n",
ret);
goto err_register_sysfs;
}
ret = input_register_device(detector_input);
if (ret) {
dev_err(dev, "Unable to register detector device, error: %d\n",
ret);
goto err_register_detector_input;
}
ret = input_register_device(counter_input);
if (ret) {
dev_err(dev, "Unable to register counter device, error: %d\n",
ret);
goto err_register_counter_input;
}
ret = register_rpmsg_driver(&rpmsg_input_driver);
if (ret) {
dev_err(dev, "Unable to register rpmsg driver, error: %d\n",
ret);
goto err_register_rmpsg_driver;
}
return ret;
err_register_rmpsg_driver:
input_unregister_device(counter_input);
err_register_counter_input:
input_unregister_device(detector_input);
err_register_detector_input:
err_register_sysfs:
misc_deregister(&step_counter_device);
pdata->counter_miscdev = NULL;
err_register_counter_misc:
misc_deregister(&step_detector_device);
pdata->detector_miscdev = NULL;
err_register_detector_misc:
pdata->detector_input = NULL;
pdata->counter_input = NULL;
platform_set_drvdata(pdev, NULL);
return ret;
}
static const struct of_device_id rpmsg_input_of_match[] = {
{ .compatible = "fsl,rpmsg-input", },
{ },
};
MODULE_DEVICE_TABLE(of, rpmsg_input_of_match);
static struct platform_driver rpmsg_input_device_driver = {
.probe = rpmsg_input_device_probe,
.driver = {
.name = "rpmsg-input",
.of_match_table = of_match_ptr(rpmsg_input_of_match)
}
};
module_platform_driver(rpmsg_input_device_driver);
MODULE_AUTHOR("NXP Semiconductor");
MODULE_DESCRIPTION("NXP i.MX7ULP rpmsg sensor input driver");
MODULE_LICENSE("GPL v2");

View File

@ -315,6 +315,26 @@ config ISL29020
This driver can also be built as a module. If so, the module
will be called isl29020.
config SENSORS_FXOS8700
tristate "Freescale FXOS8700 M+G combo sensor"
depends on I2C && SYSFS
help
If you say yes here you get support for the Freescale FXOS8700
m+g combo sensor.
This driver can also be built as a module. If so, the module
will be called fxos8700.
config SENSORS_FXAS2100X
tristate "Freescale FXAS2100X gyroscope sensor"
depends on I2C && SYSFS
help
If you say yes here you get support for the Freescale FXAS2100X
gyroscope sensor.
This driver can also be built as a module. If so, the module
will be called fxas2100x.
config SENSORS_TSL2550
tristate "Taos TSL2550 ambient light sensor"
depends on I2C && SYSFS

View File

@ -19,6 +19,8 @@ obj-$(CONFIG_TIFM_7XX1) += tifm_7xx1.o
obj-$(CONFIG_PHANTOM) += phantom.o
obj-$(CONFIG_QCOM_COINCELL) += qcom-coincell.o
obj-$(CONFIG_QCOM_FASTRPC) += fastrpc.o
obj-$(CONFIG_SENSORS_FXOS8700) += fxos8700.o
obj-$(CONFIG_SENSORS_FXAS2100X) += fxas2100x.o
obj-$(CONFIG_SENSORS_BH1770) += bh1770glc.o
obj-$(CONFIG_SENSORS_APDS990X) += apds990x.o
obj-$(CONFIG_ENCLOSURE_SERVICES) += enclosure.o

628
drivers/misc/fxas2100x.c Normal file
View File

@ -0,0 +1,628 @@
/*
* Copyright (C) 2012-2015 Freescale Semiconductor, 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.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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>
#include <linux/miscdevice.h>
#include <linux/poll.h>
#define SENSOR_IOCTL_BASE 'S'
#define SENSOR_GET_MODEL_NAME _IOR(SENSOR_IOCTL_BASE, 0, char *)
#define SENSOR_GET_POWER_STATUS _IOR(SENSOR_IOCTL_BASE, 2, int)
#define SENSOR_SET_POWER_STATUS _IOR(SENSOR_IOCTL_BASE, 3, int)
#define SENSOR_GET_DELAY_TIME _IOR(SENSOR_IOCTL_BASE, 4, int)
#define SENSOR_SET_DELAY_TIME _IOR(SENSOR_IOCTL_BASE, 5, int)
#define SENSOR_GET_RAW_DATA _IOR(SENSOR_IOCTL_BASE, 6, short[3])
#define FXAS2100X_I2C_ADDR 0x20
#define FXAS21000_CHIP_ID 0xD1
#define FXAS21002_CHID_ID_1 0xD6
#define FXAS21002_CHID_ID_2 0xD7
#define FXAS2100X_POSITION_DEFAULT 2
#define FXAS2100X_DELAY_DEFAULT 200
#define FXAS2100X_STATUS_ZYXDR 0x08
#define FXAS2100X_BUF_SIZE 6
#define FXAS2100X_POLL_INTERVAL 400
#define FXAS2100X_POLL_MAX 800
#define FXAS2100X_POLL_MIN 200
#define ABSMIN_GYRO_VAL -32768
#define ABSMAX_GYRO_VAL 32768
#define FXAS2100X_DRIVER "fxas2100x"
/* register enum for fxas2100x registers */
enum {
FXAS2100X_STATUS = 0x00,
FXAS2100X_OUT_X_MSB,
FXAS2100X_OUT_X_LSB,
FXAS2100X_OUT_Y_MSB,
FXAS2100X_OUT_Y_LSB,
FXAS2100X_OUT_Z_MSB,
FXAS2100X_OUT_Z_LSB,
FXAS2100X_DR_STATUS,
FXAS2100X_F_STATUS,
FXAS2100X_F_SETUP,
FXAS2100X_F_EVENT,
FXAS2100X_INT_SRC_FLAG,
FXAS2100X_WHO_AM_I,
FXAS2100X_CTRL_REG0,
FXAS2100X_RT_CFG,
FXAS2100X_RT_SRC,
FXAS2100X_RT_THS,
FXAS2100X_RT_COUNT,
FXAS2100X_TEMP,
FXAS2100X_CTRL_REG1,
FXAS2100X_CTRL_REG2,
FXAS2100X_CTRL_REG3, /* fxos21002 special */
FXAS2100X_REG_END,
};
enum {
STANDBY = 0,
ACTIVED,
};
struct fxas2100x_data_axis {
short x;
short y;
short z;
};
struct fxas2100x_data {
struct i2c_client *client;
struct input_polled_dev *input_polled;
atomic_t active;
atomic_t active_poll;
atomic_t delay;
atomic_t position;
u8 chip_id;
};
static struct fxas2100x_data *g_fxas2100x_data;
static int fxas2100x_position_setting[8][3][3] = {
{ {0, -1, 0}, {1, 0, 0}, {0, 0, 1} },
{ {-1, 0, 0}, {0, -1, 0}, {0, 0, 1} },
{ {0, 1, 0}, {-1, 0, 0}, {0, 0, 1} },
{ {1, 0, 0}, {0, 1, 0}, {0, 0, 1} },
{ {0, -1, 0}, {-1, 0, 0}, {0, 0, -1} },
{ {-1, 0, 0}, {0, 1, 0}, {0, 0, -1} },
{ {0, 1, 0}, {1, 0, 0}, {0, 0, -1} },
{ {1, 0, 0}, {0, -1, 0}, {0, 0, -1} },
};
static int fxas2100x_data_convert(struct fxas2100x_data *pdata,
struct fxas2100x_data_axis *axis_data)
{
short rawdata[3], data[3];
int i, j;
int position = atomic_read(&pdata->position);
if (position < 0 || position > 7)
position = 0;
rawdata[0] = axis_data->x;
rawdata[1] = axis_data->y;
rawdata[2] = axis_data->z;
for (i = 0; i < 3; i++) {
data[i] = 0;
for (j = 0; j < 3; j++)
data[i] += rawdata[j] * fxas2100x_position_setting[position][i][j];
}
axis_data->x = data[0];
axis_data->y = data[1];
axis_data->z = data[2];
return 0;
}
static int fxas2100x_device_init(struct i2c_client *client)
{
int result;
u8 val;
struct device_node *np = client->dev.of_node;
struct fxas2100x_data *pdata = i2c_get_clientdata(client);
if (pdata->chip_id == FXAS21000_CHIP_ID)
val = (0x01 << 2); /* fxas21000 dr 200HZ */
else
val = (0x02 << 2); /* fxas21002 dr 200HZ */
result = i2c_smbus_write_byte_data(client, FXAS2100X_CTRL_REG1, val);
if (result < 0)
goto out;
/* set interrupt pin as open-drain */
if (of_get_property(np, "interrupt-open-drain", NULL)) {
result = i2c_smbus_write_byte_data(client, FXAS2100X_CTRL_REG2, 0x01);
if (result < 0)
goto out;
}
atomic_set(&pdata->active, STANDBY);
return 0;
out:
dev_err(&client->dev, "error when init fxas2100x:(%d)", result);
return result;
}
static int fxas2100x_change_mode(struct i2c_client *client, int mode)
{
u8 val;
int ret;
if (mode == ACTIVED) {
val = i2c_smbus_read_byte_data(client, FXAS2100X_CTRL_REG1);
val &= ~0x03;
val |= 0x02;
/* set bit 1 */
ret = i2c_smbus_write_byte_data(client, FXAS2100X_CTRL_REG1, val);
} else {
val = i2c_smbus_read_byte_data(client, FXAS2100X_CTRL_REG1);
val &= (~0x03);
/* clear bit 0,1 */
ret = i2c_smbus_write_byte_data(client, FXAS2100X_CTRL_REG1, val);
}
return ret;
}
static int fxas2100x_set_delay(struct i2c_client *client, int delay)
{
return 0;
}
static int fxas2100x_device_stop(struct i2c_client *client)
{
u8 val;
val = i2c_smbus_read_byte_data(client, FXAS2100X_CTRL_REG1);
val &= ~0x03;
i2c_smbus_write_byte_data(client, FXAS2100X_CTRL_REG1, val);
return 0;
}
static int fxas2100x_read_data(struct fxas2100x_data *pdata,
struct fxas2100x_data_axis *data)
{
struct i2c_client * client = pdata->client;
int x, y, z;
u8 tmp_data[FXAS2100X_BUF_SIZE];
int ret;
ret = i2c_smbus_read_i2c_block_data(client, FXAS2100X_OUT_X_MSB,
FXAS2100X_BUF_SIZE, tmp_data);
if (ret < FXAS2100X_BUF_SIZE) {
dev_err(&client->dev, "i2c block read failed\n");
return -EIO;
}
data->x = ((tmp_data[0] << 8) & 0xff00) | tmp_data[1];
data->y = ((tmp_data[2] << 8) & 0xff00) | tmp_data[3];
data->z = ((tmp_data[4] << 8) & 0xff00) | tmp_data[5];
if (pdata->chip_id == FXAS21000_CHIP_ID) {
x = data->x;
y = data->y;
z = data->z;
x = x * 4 / 5;
y = y * 4 / 5;
z = z * 4 / 5;
data->x = x;
data->y = y;
data->z = z;
}
return 0;
}
/* fxas2100x miscdevice */
static long fxas2100x_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct fxas2100x_data *pdata = file->private_data;
void __user *argp = (void __user *)arg;
struct fxas2100x_data_axis data;
long ret = 0;
short sdata[3];
int enable;
int delay;
if (!pdata) {
printk(KERN_ERR "FXAS2100X struct datt point is NULL.");
return -EFAULT;
}
switch (cmd) {
case SENSOR_GET_MODEL_NAME:
if (copy_to_user(argp, "FXAS2100X GYRO", strlen("FXAS2100X GYRO") + 1)) {
printk(KERN_ERR "SENSOR_GET_MODEL_NAME copy_to_user failed.");
ret = -EFAULT;
}
break;
case SENSOR_GET_POWER_STATUS:
enable = atomic_read(&pdata->active);
if (copy_to_user(argp, &enable, sizeof(int))) {
printk(KERN_ERR "SENSOR_SET_POWER_STATUS copy_to_user failed.");
ret = -EFAULT;
}
break;
case SENSOR_SET_POWER_STATUS:
if (copy_from_user(&enable, argp, sizeof(int))) {
printk(KERN_ERR "SENSOR_SET_POWER_STATUS copy_to_user failed.");
ret = -EFAULT;
}
if (pdata->client) {
ret = fxas2100x_change_mode(pdata->client, enable ? ACTIVED : STANDBY);
if (!ret)
atomic_set(&pdata->active, enable);
}
break;
case SENSOR_GET_DELAY_TIME:
delay = atomic_read(&pdata->delay);
if (copy_to_user(argp, &delay, sizeof(delay))) {
printk(KERN_ERR "SENSOR_GET_DELAY_TIME copy_to_user failed.");
return -EFAULT;
}
break;
case SENSOR_SET_DELAY_TIME:
if (copy_from_user(&delay, argp, sizeof(int))) {
printk(KERN_ERR "SENSOR_GET_DELAY_TIME copy_to_user failed.");
ret = -EFAULT;
}
if (pdata->client && delay > 0 && delay <= 500) {
ret = fxas2100x_set_delay(pdata->client, delay);
if (!ret)
atomic_set(&pdata->delay, delay);
}
break;
case SENSOR_GET_RAW_DATA:
ret = fxas2100x_read_data(pdata, &data);
if (!ret) {
fxas2100x_data_convert(pdata, &data);
sdata[0] = data.x;
sdata[1] = data.y;
sdata[2] = data.z;
if (copy_to_user(argp, sdata, sizeof(sdata))) {
printk(KERN_ERR "SENSOR_GET_RAW_DATA copy_to_user failed.");
ret = -EFAULT;
}
}
break;
default:
ret = -1;
}
return ret;
}
static int fxas2100x_open(struct inode *inode, struct file *file)
{
file->private_data = g_fxas2100x_data;
return nonseekable_open(inode, file);
}
static int fxas2100x_release(struct inode *inode, struct file *file)
{
/* note: releasing the wdt in NOWAYOUT-mode does not stop it */
return 0;
}
static const struct file_operations fxas2100x_fops = {
.owner = THIS_MODULE,
.open = fxas2100x_open,
.release = fxas2100x_release,
.unlocked_ioctl = fxas2100x_ioctl,
};
static struct miscdevice fxas2100x_device = {
.minor = MISC_DYNAMIC_MINOR,
.name = "FreescaleGyroscope",
.fops = &fxas2100x_fops,
};
static ssize_t fxas2100x_enable_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct fxas2100x_data *pdata = g_fxas2100x_data;
int enable = 0;
enable = atomic_read(&pdata->active);
return sprintf(buf, "%d\n", enable);
}
static ssize_t fxas2100x_enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct fxas2100x_data *pdata = g_fxas2100x_data;
struct i2c_client *client = pdata->client;
int ret;
unsigned long enable;
if (kstrtoul(buf, 10, &enable) < 0)
return -EINVAL;
enable = (enable > 0) ? 1 : 0;
ret = fxas2100x_change_mode(client, (enable > 0 ? ACTIVED : STANDBY));
if (!ret) {
atomic_set(&pdata->active, enable);
atomic_set(&pdata->active_poll, enable);
dev_err(dev, "mma enable setting active \n");
}
return count;
}
static ssize_t fxas2100x_poll_delay_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct fxas2100x_data *pdata = g_fxas2100x_data;
int delay = 0;
delay = atomic_read(&pdata->delay);
return sprintf(buf, "%d\n", delay);
}
static ssize_t fxas2100x_poll_delay_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct fxas2100x_data *pdata = g_fxas2100x_data;
struct i2c_client *client = pdata->client;
int ret;
int delay;
delay = simple_strtoul(buf, NULL, 10);
ret = fxas2100x_set_delay(client, delay);
if (!ret)
atomic_set(&pdata->delay, delay);
return count;
}
static ssize_t fxas2100x_position_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct fxas2100x_data *pdata = g_fxas2100x_data;
int position = 0;
position = atomic_read(&pdata->position);
return sprintf(buf, "%d\n", position);
}
static ssize_t fxas2100x_position_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct fxas2100x_data *pdata = g_fxas2100x_data;
int position;
position = simple_strtoul(buf, NULL, 10);
atomic_set(&pdata->position, position);
return count;
}
static DEVICE_ATTR(enable, S_IWUSR | S_IRUGO, fxas2100x_enable_show, fxas2100x_enable_store);
static DEVICE_ATTR(poll_delay, S_IWUSR | S_IRUGO, fxas2100x_poll_delay_show, fxas2100x_poll_delay_store);
static DEVICE_ATTR(position, S_IWUSR | S_IRUGO, fxas2100x_position_show, fxas2100x_position_store);
static struct attribute *fxas2100x_attributes[] = {
&dev_attr_enable.attr,
&dev_attr_poll_delay.attr,
&dev_attr_position.attr,
NULL
};
static const struct attribute_group fxas2100x_attr_group = {
.attrs = fxas2100x_attributes,
};
static void fxas2100x_poll(struct input_polled_dev *dev)
{
struct fxas2100x_data *pdata = g_fxas2100x_data;
struct input_dev *idev = pdata->input_polled->input;
struct fxas2100x_data_axis data;
int ret;
if (!(atomic_read(&pdata->active_poll)))
return;
ret = fxas2100x_read_data(pdata, &data);
if (!ret) {
fxas2100x_data_convert(pdata, &data);
input_report_abs(idev, ABS_X, data.x);
input_report_abs(idev, ABS_Y, data.y);
input_report_abs(idev, ABS_Z, data.z);
input_sync(idev);
}
}
static int fxas2100x_register_polled_device(struct fxas2100x_data *pdata)
{
struct input_polled_dev *ipoll_dev;
struct input_dev *idev;
int error;
ipoll_dev = input_allocate_polled_device();
if (!ipoll_dev)
return -ENOMEM;
ipoll_dev->private = pdata;
ipoll_dev->poll = fxas2100x_poll;
ipoll_dev->poll_interval = FXAS2100X_POLL_INTERVAL;
ipoll_dev->poll_interval_min = FXAS2100X_POLL_MIN;
ipoll_dev->poll_interval_max = FXAS2100X_POLL_MAX;
idev = ipoll_dev->input;
idev->name = FXAS2100X_DRIVER;
idev->id.bustype = BUS_I2C;
idev->dev.parent = &pdata->client->dev;
idev->evbit[0] = BIT_MASK(EV_ABS);
input_set_abs_params(idev, ABS_X, ABSMIN_GYRO_VAL, ABSMAX_GYRO_VAL, 0, 0);
input_set_abs_params(idev, ABS_Y, ABSMIN_GYRO_VAL, ABSMAX_GYRO_VAL, 0, 0);
input_set_abs_params(idev, ABS_Z, ABSMIN_GYRO_VAL, ABSMAX_GYRO_VAL, 0, 0);
error = input_register_polled_device(ipoll_dev);
if (error) {
input_free_polled_device(ipoll_dev);
return error;
}
pdata->input_polled = ipoll_dev;
return 0;
}
static int fxas2100x_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int result, chip_id;
struct fxas2100x_data *pdata;
struct i2c_adapter *adapter;
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;
chip_id = i2c_smbus_read_byte_data(client, FXAS2100X_WHO_AM_I);
if (chip_id != FXAS21000_CHIP_ID && chip_id != FXAS21002_CHID_ID_1 &&
chip_id != FXAS21002_CHID_ID_2) {
dev_err(&client->dev,
"read chip ID 0x%x is not equal to 0x%x for fxas21000 or 0x%x/0x%x fxas21002!\n",
chip_id, FXAS21000_CHIP_ID, FXAS21002_CHID_ID_1, FXAS21002_CHID_ID_2);
result = -EINVAL;
goto err_out;
}
pdata = kzalloc(sizeof(struct fxas2100x_data), GFP_KERNEL);
if (!pdata) {
result = -ENOMEM;
dev_err(&client->dev, "alloc data memory error!\n");
goto err_out;
}
/* Initialize the FXAS2100X chip */
g_fxas2100x_data = pdata;
pdata->client = client;
pdata->chip_id = chip_id;
atomic_set(&pdata->delay, FXAS2100X_DELAY_DEFAULT);
atomic_set(&pdata->position, FXAS2100X_POSITION_DEFAULT);
i2c_set_clientdata(client, pdata);
result = misc_register(&fxas2100x_device);
if (result != 0) {
dev_err(&client->dev, "register acc miscdevice error");
goto err_regsiter_misc;
}
/* for debug */
if (client->irq <= 0) {
result = fxas2100x_register_polled_device(g_fxas2100x_data);
if (result)
dev_err(&client->dev,
"IRQ GPIO conf. error %d, error %d\n",
client->irq, result);
}
result = sysfs_create_group(&fxas2100x_device.this_device->kobj,
&fxas2100x_attr_group);
if (result) {
dev_err(&client->dev, "create device file failed!\n");
result = -EINVAL;
goto err_create_sysfs;
}
fxas2100x_device_init(client);
dev_info(&client->dev, "fxas2100x device driver probe successfully\n");
return 0;
err_create_sysfs:
misc_deregister(&fxas2100x_device);
err_regsiter_misc:
kfree(pdata);
err_out:
return result;
}
static int fxas2100x_remove(struct i2c_client *client)
{
struct fxas2100x_data *pdata = i2c_get_clientdata(client);
fxas2100x_device_stop(client);
if (client->irq <= 0) {
input_unregister_polled_device(pdata->input_polled);
input_free_polled_device(pdata->input_polled);
}
misc_deregister(&fxas2100x_device);
if (pdata != NULL)
kfree(pdata);
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int fxas2100x_suspend(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct fxas2100x_data *pdata = i2c_get_clientdata(client);
if (atomic_read(&pdata->active))
fxas2100x_device_stop(client);
return 0;
}
static int fxas2100x_resume(struct device *dev)
{
int val = 0;
struct i2c_client *client = to_i2c_client(dev);
struct fxas2100x_data *pdata = i2c_get_clientdata(client);
if (atomic_read(&pdata->active)) {
val = i2c_smbus_read_byte_data(client, FXAS2100X_CTRL_REG1);
val &= ~0x03;
val |= 0x02;
i2c_smbus_write_byte_data(client, FXAS2100X_CTRL_REG1, val);
}
return 0;
}
#endif
static const struct i2c_device_id fxas2100x_id[] = {
{ "fxas2100x", 0 },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(i2c, fxas2100x_id);
static SIMPLE_DEV_PM_OPS(fxas2100x_pm_ops, fxas2100x_suspend, fxas2100x_resume);
static struct i2c_driver fxas2100x_driver = {
.driver = {
.name = FXAS2100X_DRIVER,
.owner = THIS_MODULE,
.pm = &fxas2100x_pm_ops,
},
.probe = fxas2100x_probe,
.remove = fxas2100x_remove,
.id_table = fxas2100x_id,
};
module_i2c_driver(fxas2100x_driver);
MODULE_AUTHOR("Freescale Semiconductor, Inc.");
MODULE_DESCRIPTION("FXAS2100X 3-Axis Gyrosope Sensor driver");
MODULE_LICENSE("GPL");

978
drivers/misc/fxos8700.c Normal file
View File

@ -0,0 +1,978 @@
/*
* Copyright (C) 2012-2013 Freescale Semiconductor, 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>
#include <linux/miscdevice.h>
#include <linux/poll.h>
/*register define*/
#define FXOS8700_STATUS 0x00
#define FXOS8700_OUT_X_MSB 0x01
#define FXOS8700_OUT_X_LSB 0x02
#define FXOS8700_OUT_Y_MSB 0x03
#define FXOS8700_OUT_Y_LSB 0x04
#define FXOS8700_OUT_Z_MSB 0x05
#define FXOS8700_OUT_Z_LSB 0x06
#define FXOS8700_F_SETUP 0x09
#define FXOS8700_TRIG_CFG 0x0a
#define FXOS8700_SYSMOD 0x0B
#define FXOS8700_INT_SOURCE 0x0c
#define FXOS8700_WHO_AM_I 0x0d
#define FXOS8700_XYZ_DATA_CFG 0x0e
#define FXOS8700_HP_FILTER_CUTOFF 0x0f
#define FXOS8700_PL_STATUS 0x10
#define FXOS8700_PL_CFG 0x11
#define FXOS8700_PL_COUNT 0x12
#define FXOS8700_PL_BF_ZCOMP 0x13
#define FXOS8700_PL_P_L_THS_REG 0x14
#define FXOS8700_FFMT_CFG 0x15
#define FXOS8700_FFMT_SRC 0x16
#define FXOS8700_FFMT_THS 0x17
#define FXOS8700_FFMT_COUNT 0x18
#define FXOS8700_TRANSIDENT1_CFG 0x1d
#define FXOS8700_TRANSIDENT1_SRC 0x1e
#define FXOS8700_TRANSIDENT1_THS 0x1f
#define FXOS8700_TRANSIDENT1_COUNT 0x20
#define FXOS8700_PULSE_CFG 0x21
#define FXOS8700_PULSE_SRC 0x22
#define FXOS8700_PULSE_THSX 0x23
#define FXOS8700_PULSE_THSY 0x24
#define FXOS8700_PULSE_THSZ 0x25
#define FXOS8700_PULSE_TMLT 0x26
#define FXOS8700_PULSE_LTCY 0x27
#define FXOS8700_PULSE_WIND 0x28
#define FXOS8700_ATSLP_COUNT 0x29
#define FXOS8700_CTRL_REG1 0x2a
#define FXOS8700_CTRL_REG2 0x2b
#define FXOS8700_CTRL_REG3 0x2c
#define FXOS8700_CTRL_REG4 0x2d
#define FXOS8700_CTRL_REG5 0x2e
#define FXOS8700_OFF_X 0x2f
#define FXOS8700_OFF_Y 0x30
#define FXOS8700_OFF_Z 0x31
#define FXOS8700_M_DR_STATUS 0x32
#define FXOS8700_M_OUT_X_MSB 0x33
#define FXOS8700_M_OUT_X_LSB 0x34
#define FXOS8700_M_OUT_Y_MSB 0x35
#define FXOS8700_M_OUT_Y_LSB 0x36
#define FXOS8700_M_OUT_Z_MSB 0x37
#define FXOS8700_M_OUT_Z_LSB 0x38
#define FXOS8700_CMP_X_MSB 0x39
#define FXOS8700_CMP_X_LSB 0x3a
#define FXOS8700_CMP_Y_MSB 0x3b
#define FXOS8700_CMP_Y_LSB 0x3c
#define FXOS8700_CMP_Z_MSB 0x3d
#define FXOS8700_CMP_Z_LSB 0x3e
#define FXOS8700_M_OFF_X_MSB 0x3f
#define FXOS8700_M_OFF_X_LSB 0x40
#define FXOS8700_M_OFF_Y_MSB 0x41
#define FXOS8700_M_OFF_Y_LSB 0x42
#define FXOS8700_M_OFF_Z_MSB 0x43
#define FXOS8700_M_OFF_Z_LSB 0x44
#define FXOS8700_MAX_X_MSB 0x45
#define FXOS8700_MAX_X_LSB 0x46
#define FXOS8700_MAX_Y_MSB 0x47
#define FXOS8700_MAX_Y_LSB 0x48
#define FXOS8700_MAX_Z_MSB 0x49
#define FXOS8700_MAX_Z_LSB 0x4a
#define FXOS8700_MIN_X_MSB 0x4b
#define FXOS8700_MIN_X_LSB 0x4c
#define FXOS8700_MIN_Y_MSB 0x4d
#define FXOS8700_MIN_Y_LSB 0x4e
#define FXOS8700_MIN_Z_MSB 0x4f
#define FXOS8700_MIN_Z_LSB 0x50
#define FXOS8700_M_TEMP 0x51
#define FXOS8700_MAG_THS_CFG 0x52
#define FXOS8700_MAG_THS_SRC 0x53
#define FXOS8700_MAG_THS_THS_X1 0x54
#define FXOS8700_MAG_THS_THS_X0 0x55
#define FXOS8700_MAG_THS_THS_Y1 0x56
#define FXOS8700_MAG_THS_THS_Y0 0x57
#define FXOS8700_MAG_THS_THS_Z1 0x58
#define FXOS8700_MAG_THS_THS_Z0 0x59
#define FXOS8700_MAG_THS_CUNT 0x5a
#define FXOS8700_M_CTRL_REG1 0x5b
#define FXOS8700_M_CTRL_REG2 0x5c
#define FXOS8700_M_CTRL_REG3 0x5d
#define FXOS8700_M_INT_SOURCE 0x5e
#define FXOS8700_G_VECM_CFG 0x5f
#define FXOS8700_G_VECM_THS_MSB 0x60
#define FXOS8700_G_VECM_THS_LSB 0x61
#define FXOS8700_G_VECM_CNT 0x62
#define FXOS8700_G_VECM_INITX_MSB 0x63
#define FXOS8700_G_VECM_INITX_LSB 0x64
#define FXOS8700_G_VECM_INITY_MSB 0x65
#define FXOS8700_G_VECM_INITY_LSB 0x66
#define FXOS8700_G_VECM_INITZ_MSB 0x67
#define FXOS8700_G_VECM_INITZ_LSB 0x68
#define FXOS8700_M_VECM_CFG 0x69
#define FXOS8700_M_VECM_THS_MSB 0x6a
#define FXOS8700_M_VECM_THS_LSB 0x6b
#define FXOS8700_M_VECM_CNT 0x6d
#define FXOS8700_M_VECM_INITX_MSB 0x6d
#define FXOS8700_M_VECM_INITX_LSB 0x6e
#define FXOS8700_M_VECM_INITY_MSB 0x6f
#define FXOS8700_M_VECM_INITY_LSB 0x70
#define FXOS8700_M_VECM_INITZ_MSB 0x71
#define FXOS8700_M_VECM_INITZ_LSB 0x72
#define FXOS8700_G_FFMT_THS_X1 0x73
#define FXOS8700_G_FFMT_THS_X0 0x74
#define FXOS8700_G_FFMT_THS_Y1 0x75
#define FXOS8700_G_FFMT_THS_Y0 0x76
#define FXOS8700_G_FFMT_THS_Z1 0x77
#define FXOS8700_G_FFMT_THS_Z0 0x78
#define FXOS8700_G_TRAN_INIT_MSB 0x79
#define FXOS8700_G_TRAN_INIT_LSB_X 0x7a
#define FXOS8700_G_TRAN_INIT_LSB_Y 0x7b
#define FXOS8700_G_TRAN_INIT_LSB_Z 0x7d
#define FXOS8700_TM_NVM_LOCK 0x7e
#define FXOS8700_NVM_DATA0_35 0x80
#define FXOS8700_NVM_DATA_BNK3 0xa4
#define FXOS8700_NVM_DATA_BNK2 0xa5
#define FXOS8700_NVM_DATA_BNK1 0xa6
#define FXOS8700_NVM_DATA_BNK0 0xa7
#define SENSOR_IOCTL_BASE 'S'
#define SENSOR_GET_MODEL_NAME _IOR(SENSOR_IOCTL_BASE, 0, char *)
#define SENSOR_GET_POWER_STATUS _IOR(SENSOR_IOCTL_BASE, 2, int)
#define SENSOR_SET_POWER_STATUS _IOR(SENSOR_IOCTL_BASE, 3, int)
#define SENSOR_GET_DELAY_TIME _IOR(SENSOR_IOCTL_BASE, 4, int)
#define SENSOR_SET_DELAY_TIME _IOR(SENSOR_IOCTL_BASE, 5, int)
#define SENSOR_GET_RAW_DATA _IOR(SENSOR_IOCTL_BASE, 6, short[3])
#define FXOS8700_I2C_ADDR 0x1E
#define FXOS8700_DEVICE_ID 0xC7
#define FXOS8700_PRE_DEVICE_ID 0xC4
#define FXOS8700_DATA_BUF_SIZE 6
#define FXOS8700_DELAY_DEFAULT 200 /* msecs */
#define FXOS8700_POSITION_DEFAULT 1 /* msecs */
#define FXOS8700_TYPE_ACC 0x00
#define FXOS8700_TYPE_MAG 0x01
#define FXOS8700_STANDBY 0x00
#define FXOS8700_ACTIVED 0x01
#define ABS_STATUS ABS_WHEEL
#define FXOS8700_DRIVER "fxos8700"
#define ABSMAX_ACC_VAL 0x01FF
#define ABSMIN_ACC_VAL -(ABSMAX_ACC_VAL)
#define FXOS8700_POLL_INTERVAL 400
#define FXOS8700_POLL_MAX 800
#define FXOS8700_POLL_MIN 100
enum { MODE_2G = 0, MODE_4G, MODE_8G,
};
struct fxos8700_data_axis {
short x;
short y;
short z;
};
struct fxos8700_data {
struct i2c_client *client;
struct input_polled_dev *input_polled;
struct miscdevice *acc_miscdev;
struct miscdevice *mag_miscdev;
atomic_t acc_delay;
atomic_t mag_delay;
atomic_t acc_active;
atomic_t acc_active_poll;
atomic_t mag_active_poll;
atomic_t mag_active;
atomic_t position;
atomic_t range;
};
static struct fxos8700_data *g_fxos8700_data;
static int fxos8700_position_settings[8][3][3] = {
{ { 0, -1, 0}, { 1, 0, 0}, {0, 0, 1} },
{ {-1, 0, 0}, { 0, -1, 0}, {0, 0, 1} },
{ { 0, 1, 0}, {-1, 0, 0}, {0, 0, 1} },
{ { 1, 0, 0}, { 0, 1, 0}, {0, 0, 1} },
{ { 0, -1, 0}, {-1, 0, 0}, {0, 0, -1} },
{ {-1, 0, 0}, { 0, 1, 0}, {0, 0, -1} },
{ { 0, 1, 0}, { 1, 0, 0}, {0, 0, -1} },
{ { 1, 0, 0}, { 0, -1, 0}, {0, 0, -1} },
};
static int fxos8700_data_convert(struct fxos8700_data_axis *axis_data, int position)
{
short rawdata[3], data[3];
int i, j;
if (position < 0 || position > 7)
position = 0;
rawdata[0] = axis_data->x;
rawdata[1] = axis_data->y;
rawdata[2] = axis_data->z;
for (i = 0; i < 3 ; i++) {
data[i] = 0;
for (j = 0; j < 3; j++)
data[i] += rawdata[j] * fxos8700_position_settings[position][i][j];
}
axis_data->x = data[0];
axis_data->y = data[1];
axis_data->z = data[2];
return 0;
}
static int fxos8700_change_mode(struct i2c_client *client, int type, int active)
{
u8 data;
int acc_act, mag_act;
struct fxos8700_data *pdata = i2c_get_clientdata(client);
acc_act = atomic_read(&pdata->acc_active);
mag_act = atomic_read(&pdata->mag_active);
data = i2c_smbus_read_byte_data(client, FXOS8700_CTRL_REG1);
if (type == FXOS8700_TYPE_ACC)
acc_act = active;
else
mag_act = active;
if (acc_act == FXOS8700_ACTIVED || mag_act == FXOS8700_ACTIVED)
data |= 0x01;
else
data &= ~0x01;
i2c_smbus_write_byte_data(client, FXOS8700_CTRL_REG1, data);
return 0;
}
static int fxos8700_change_range(struct i2c_client *client, int range)
{
int ret;
ret = i2c_smbus_write_byte_data(client, FXOS8700_XYZ_DATA_CFG, range);
return ret;
}
static int fxos8700_set_odr(struct i2c_client *client, int type, int delay)
{
return 0;
}
static int fxos8700_device_init(struct i2c_client *client)
{
int result;
struct device_node *np = client->dev.of_node;
struct fxos8700_data *pdata = i2c_get_clientdata(client);
/* set interrupt pin as open-drain */
if (of_get_property(np, "interrupt-open-drain", NULL)) {
result = i2c_smbus_write_byte_data(client, FXOS8700_CTRL_REG3, 0x01);
if (result < 0)
goto out;
}
/* standby mode */
result = i2c_smbus_write_byte_data(client, FXOS8700_CTRL_REG1, 0x00);
if (result < 0)
goto out;
result = i2c_smbus_write_byte_data(client, FXOS8700_M_CTRL_REG1, 0x1F);
if (result < 0)
goto out;
result = i2c_smbus_write_byte_data(client, FXOS8700_M_CTRL_REG2, 0x5c);
if (result < 0)
goto out;
result = i2c_smbus_write_byte_data(client, FXOS8700_CTRL_REG1, 0x03 << 3);
if (result < 0)
goto out;
result = i2c_smbus_write_byte_data(client, FXOS8700_XYZ_DATA_CFG,
MODE_2G);
if (result < 0)
goto out;
atomic_set(&pdata->acc_active, FXOS8700_STANDBY);
atomic_set(&pdata->mag_active, FXOS8700_STANDBY);
atomic_set(&pdata->position, FXOS8700_POSITION_DEFAULT);
atomic_set(&pdata->range, MODE_2G);
return 0;
out:
dev_err(&client->dev, "Error when init fxos8700 device:(%d)", result);
return result;
}
static int fxos8700_device_stop(struct i2c_client *client)
{
i2c_smbus_write_byte_data(client, FXOS8700_CTRL_REG1, 0x00);
return 0;
}
static int
fxos8700_read_data(struct i2c_client *client, struct fxos8700_data_axis *data, int type)
{
u8 tmp_data[FXOS8700_DATA_BUF_SIZE];
int ret;
u8 reg;
if (type == FXOS8700_TYPE_ACC)
reg = FXOS8700_OUT_X_MSB;
else
reg = FXOS8700_M_OUT_X_MSB;
ret = i2c_smbus_read_i2c_block_data(client, reg, FXOS8700_DATA_BUF_SIZE, tmp_data);
if (ret < FXOS8700_DATA_BUF_SIZE) {
dev_err(&client->dev, "i2c block read %s failed\n",
(type == FXOS8700_TYPE_ACC ? "acc" : "mag"));
return -EIO;
}
data->x = ((tmp_data[0] << 8) & 0xff00) | tmp_data[1];
data->y = ((tmp_data[2] << 8) & 0xff00) | tmp_data[3];
data->z = ((tmp_data[4] << 8) & 0xff00) | tmp_data[5];
return 0;
}
static long fxos8700_acc_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct fxos8700_data *pdata = file->private_data;
void __user *argp = (void __user *)arg;
struct fxos8700_data_axis data;
long ret = 0;
short sdata[3];
int enable;
int delay;
int position;
if (!pdata) {
printk(KERN_ERR "fxos8700 struct datt point is NULL.");
return -EFAULT;
}
switch (cmd) {
case SENSOR_GET_MODEL_NAME:
if (copy_to_user(argp, "FXOS8700 ACC", strlen("FXOS8700 ACC") + 1)) {
printk(KERN_ERR "SENSOR_GET_MODEL_NAME copy_to_user failed.");
ret = -EFAULT;
}
break;
case SENSOR_GET_POWER_STATUS:
enable = atomic_read(&pdata->acc_active);
if (copy_to_user(argp, &enable, sizeof(int))) {
printk(KERN_ERR "SENSOR_SET_POWER_STATUS copy_to_user failed.");
ret = -EFAULT;
}
break;
case SENSOR_SET_POWER_STATUS:
if (copy_from_user(&enable, argp, sizeof(int))) {
printk(KERN_ERR "SENSOR_SET_POWER_STATUS copy_to_user failed.");
ret = -EFAULT;
}
if (pdata->client) {
ret = fxos8700_change_mode(pdata->client, FXOS8700_TYPE_ACC,
enable ? FXOS8700_ACTIVED : FXOS8700_STANDBY);
if (!ret)
atomic_set(&pdata->acc_active, enable);
}
break;
case SENSOR_GET_DELAY_TIME:
delay = atomic_read(&pdata->acc_delay);
if (copy_to_user(argp, &delay, sizeof(delay))) {
printk(KERN_ERR "SENSOR_GET_DELAY_TIME copy_to_user failed.");
return -EFAULT;
}
break;
case SENSOR_SET_DELAY_TIME:
if (copy_from_user(&delay, argp, sizeof(int))) {
printk(KERN_ERR "SENSOR_SET_DELAY_TIME copy_to_user failed.");
ret = -EFAULT;
}
if (pdata->client && delay > 0 && delay <= 500) {
ret = fxos8700_set_odr(pdata->client, FXOS8700_TYPE_ACC, delay);
if (!ret)
atomic_set(&pdata->acc_delay, delay);
}
break;
case SENSOR_GET_RAW_DATA:
position = atomic_read(&pdata->position);
ret = fxos8700_read_data(pdata->client, &data, FXOS8700_TYPE_ACC);
if (!ret) {
fxos8700_data_convert(&data, position);
sdata[0] = data.x;
sdata[1] = data.y;
sdata[2] = data.z;
if (copy_to_user(argp, sdata, sizeof(sdata))) {
printk(KERN_ERR "SENSOR_GET_RAW_DATA copy_to_user failed.");
ret = -EFAULT;
}
}
break;
default:
ret = -1;
}
return ret;
}
static int fxos8700_acc_open(struct inode *inode, struct file *file)
{
file->private_data = g_fxos8700_data;
return nonseekable_open(inode, file);
}
static int fxos8700_acc_release(struct inode *inode, struct file *file)
{
/* note: releasing the wdt in NOWAYOUT-mode does not stop it */
return 0;
}
static const struct file_operations fxos8700_acc_fops = {
.owner = THIS_MODULE,
.open = fxos8700_acc_open,
.release = fxos8700_acc_release,
.unlocked_ioctl = fxos8700_acc_ioctl,
};
/* mag char miscdevice */
static long fxos8700_mag_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct fxos8700_data *pdata = file->private_data;
void __user *argp = (void __user *)arg;
struct fxos8700_data_axis data;
long ret = 0;
short sdata[3];
int enable;
int delay;
int position;
if (!pdata) {
printk(KERN_ERR "fxos8700 struct datt point is NULL.");
return -EFAULT;
}
switch (cmd) {
case SENSOR_GET_MODEL_NAME:
if (copy_to_user(argp, "FXOS8700 MAG", strlen("FXOS8700 MAG") + 1)) {
printk(KERN_ERR "SENSOR_GET_MODEL_NAME copy_to_user failed.");
ret = -EFAULT;
}
break;
case SENSOR_GET_POWER_STATUS:
enable = atomic_read(&pdata->mag_active);
if (copy_to_user(argp, &enable, sizeof(int))) {
printk(KERN_ERR "SENSOR_SET_POWER_STATUS copy_to_user failed.");
ret = -EFAULT;
}
break;
case SENSOR_SET_POWER_STATUS:
if (copy_from_user(&enable, argp, sizeof(int))) {
printk(KERN_ERR "SENSOR_SET_POWER_STATUS copy_to_user failed.");
ret = -EFAULT;
}
if (pdata->client) {
ret = fxos8700_change_mode(pdata->client, FXOS8700_TYPE_MAG,
enable ? FXOS8700_ACTIVED : FXOS8700_STANDBY);
if (!ret)
atomic_set(&pdata->mag_active, enable);
}
break;
case SENSOR_GET_DELAY_TIME:
delay = atomic_read(&pdata->mag_delay);
if (copy_to_user(argp, &delay, sizeof(delay))) {
printk(KERN_ERR "SENSOR_GET_DELAY_TIME copy_to_user failed.");
return -EFAULT;
}
break;
case SENSOR_SET_DELAY_TIME:
if (copy_from_user(&delay, argp, sizeof(int))) {
printk(KERN_ERR "SENSOR_SET_DELAY_TIME copy_to_user failed.");
ret = -EFAULT;
}
if (pdata->client && delay > 0 && delay <= 500) {
ret = fxos8700_set_odr(pdata->client, FXOS8700_TYPE_MAG, delay);
if (!ret)
atomic_set(&pdata->mag_delay, delay);
}
break;
case SENSOR_GET_RAW_DATA:
position = atomic_read(&pdata->position);
ret = fxos8700_read_data(pdata->client, &data, FXOS8700_TYPE_MAG);
if (!ret) {
fxos8700_data_convert(&data, position);
sdata[0] = data.x;
sdata[1] = data.y;
sdata[2] = data.z;
if (copy_to_user(argp, sdata, sizeof(sdata))) {
printk(KERN_ERR "SENSOR_GET_RAW_DATA copy_to_user failed.");
ret = -EFAULT;
}
}
break;
default:
ret = -1;
}
return ret;
}
static int fxos8700_mag_open(struct inode *inode, struct file *file)
{
file->private_data = g_fxos8700_data;
return nonseekable_open(inode, file);
}
static int fxos8700_mag_release(struct inode *inode, struct file *file)
{
/* note: releasing the wdt in NOWAYOUT-mode does not stop it */
return 0;
}
static const struct file_operations fxos8700_mag_fops = {
.owner = THIS_MODULE,
.open = fxos8700_mag_open,
.release = fxos8700_mag_release,
.unlocked_ioctl = fxos8700_mag_ioctl,
};
static struct miscdevice fxos8700_acc_device = {
.minor = MISC_DYNAMIC_MINOR,
.name = "FreescaleAccelerometer",
.fops = &fxos8700_acc_fops,
};
static struct miscdevice fxos8700_mag_device = {
.minor = MISC_DYNAMIC_MINOR,
.name = "FreescaleMagnetometer",
.fops = &fxos8700_mag_fops,
};
static ssize_t fxos8700_enable_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct miscdevice *misc_dev = dev_get_drvdata(dev);
struct fxos8700_data *pdata = g_fxos8700_data;
int enable = 0;
if (pdata->acc_miscdev == misc_dev)
enable = atomic_read(&pdata->acc_active);
if (pdata->mag_miscdev == misc_dev)
enable = atomic_read(&pdata->mag_active);
return sprintf(buf, "%d\n", enable);
}
static ssize_t fxos8700_enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct miscdevice *misc_dev = dev_get_drvdata(dev);
struct fxos8700_data *pdata = g_fxos8700_data;
struct i2c_client *client = pdata->client;
unsigned long enable;
int type;
int ret;
if (kstrtoul(buf, 10, &enable) < 0)
return -EINVAL;
if (misc_dev == pdata->acc_miscdev)
type = FXOS8700_TYPE_ACC;
if (misc_dev == pdata->mag_miscdev)
type = FXOS8700_TYPE_MAG;
enable = (enable > 0 ? FXOS8700_ACTIVED : FXOS8700_STANDBY);
ret = fxos8700_change_mode(client, type, enable);
if (!ret) {
if (type == FXOS8700_TYPE_ACC) {
atomic_set(&pdata->acc_active, enable);
atomic_set(&pdata->acc_active_poll, enable);
} else {
atomic_set(&pdata->mag_active, enable);
atomic_set(&pdata->mag_active_poll, enable);
}
}
return count;
}
static ssize_t fxos8700_poll_delay_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct miscdevice *misc_dev = dev_get_drvdata(dev);
struct fxos8700_data *pdata = g_fxos8700_data;
int poll_delay = 0;
if (pdata->acc_miscdev == misc_dev)
poll_delay = atomic_read(&pdata->acc_delay);
if (pdata->mag_miscdev == misc_dev)
poll_delay = atomic_read(&pdata->mag_delay);
return sprintf(buf, "%d\n", poll_delay);
}
static ssize_t fxos8700_poll_delay_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct miscdevice *misc_dev = dev_get_drvdata(dev);
struct fxos8700_data *pdata = g_fxos8700_data;
struct i2c_client *client = pdata->client;
unsigned long delay;
int type;
int ret;
if (kstrtoul(buf, 10, &delay) < 0)
return -EINVAL;
if (misc_dev == pdata->acc_miscdev)
type = FXOS8700_TYPE_ACC;
if (misc_dev == pdata->mag_miscdev)
type = FXOS8700_TYPE_MAG;
ret = fxos8700_set_odr(client, type, delay);
if (!ret) {
if (type == FXOS8700_TYPE_ACC)
atomic_set(&pdata->acc_delay, delay);
else
atomic_set(&pdata->mag_delay, delay);
}
return count;
}
static ssize_t fxos8700_position_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct fxos8700_data *pdata = g_fxos8700_data;
unsigned long position = atomic_read(&pdata->position);
return sprintf(buf, "%ld\n", position);
}
static ssize_t fxos8700_position_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
unsigned long position;
struct fxos8700_data *pdata = g_fxos8700_data;
if (kstrtoul(buf, 10, &position) < 0)
return -EINVAL;
atomic_set(&pdata->position, position);
return count;
}
static ssize_t fxos8700_range_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct fxos8700_data *pdata = g_fxos8700_data;
unsigned long range = atomic_read(&pdata->range);
return sprintf(buf, "%ld\n", range);
}
static ssize_t fxos8700_range_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
unsigned long range;
struct fxos8700_data *pdata = g_fxos8700_data;
struct i2c_client *client = pdata->client;
int ret;
if (kstrtoul(buf, 10, &range) < 0)
return -EINVAL;
if (range == atomic_read(&pdata->range))
return count;
if (atomic_read(&pdata->acc_active) | atomic_read(&pdata->mag_active))
printk(KERN_INFO "Pls set the sensor standby and then actived\n");
ret = fxos8700_change_range(client, range);
if (!ret)
atomic_set(&pdata->range, range);
return count;
}
static DEVICE_ATTR(enable, S_IWUSR | S_IRUGO, fxos8700_enable_show, fxos8700_enable_store);
static DEVICE_ATTR(poll_delay, S_IWUSR | S_IRUGO, fxos8700_poll_delay_show, fxos8700_poll_delay_store);
static DEVICE_ATTR(position, S_IWUSR | S_IRUGO, fxos8700_position_show, fxos8700_position_store);
static DEVICE_ATTR(range, S_IWUSR | S_IRUGO, fxos8700_range_show, fxos8700_range_store);
static struct attribute *fxos8700_attributes[] = {
&dev_attr_enable.attr,
&dev_attr_poll_delay.attr,
&dev_attr_position.attr,
&dev_attr_range.attr,
NULL
};
static const struct attribute_group fxos8700_attr_group = {
.attrs = fxos8700_attributes,
};
static int fxos8700_register_sysfs_device(struct fxos8700_data *pdata)
{
struct miscdevice *misc_dev = NULL;
int err = -1;
/* register sysfs for acc */
misc_dev = pdata->acc_miscdev;
err = sysfs_create_group(&misc_dev->this_device->kobj, &fxos8700_attr_group);
if (err)
goto out;
/* register sysfs for mag */
misc_dev = pdata->mag_miscdev;
err = sysfs_create_group(&misc_dev->this_device->kobj, &fxos8700_attr_group);
if (err)
goto err_register_sysfs;
return 0;
err_register_sysfs:
misc_dev = pdata->acc_miscdev;
sysfs_remove_group(&misc_dev->this_device->kobj, &fxos8700_attr_group);
printk("reigster mag sysfs error\n");
out:
printk("reigster acc sysfs error\n");
return err;
}
static int fxos8700_unregister_sysfs_device(struct fxos8700_data *pdata)
{
struct miscdevice *misc_dev;
misc_dev = pdata->acc_miscdev;
sysfs_remove_group(&misc_dev->this_device->kobj, &fxos8700_attr_group);
misc_dev = pdata->mag_miscdev;
sysfs_remove_group(&misc_dev->this_device->kobj, &fxos8700_attr_group);
return 0;
}
static void fxos8700_report(struct input_polled_dev *dev, int type)
{
struct fxos8700_data_axis data;
struct fxos8700_data *pdata = g_fxos8700_data;
struct input_dev *idev = pdata->input_polled->input;
int position;
int ret;
position = atomic_read(&pdata->position);
ret = fxos8700_read_data(pdata->client, &data, type);
if (!ret) {
fxos8700_data_convert(&data, position);
input_report_abs(idev, ABS_X, data.x);
input_report_abs(idev, ABS_Y, data.y);
input_report_abs(idev, ABS_Z, data.z);
input_sync(idev);
}
}
static void fxos8700_poll(struct input_polled_dev *dev)
{
struct fxos8700_data *pdata = g_fxos8700_data;
int type;
if (!(atomic_read(&pdata->acc_active_poll) ||
atomic_read(&pdata->mag_active_poll)))
return;
if (atomic_read(&pdata->acc_active_poll))
type = FXOS8700_TYPE_ACC;
if (atomic_read(&pdata->mag_active_poll))
type =FXOS8700_TYPE_MAG;
fxos8700_report(dev, type);
}
static int fxo8700_register_polled_device(struct fxos8700_data *pdata)
{
struct input_polled_dev *ipoll_dev;
struct input_dev *idev;
int error;
ipoll_dev = input_allocate_polled_device();
if (!ipoll_dev)
return -ENOMEM;
ipoll_dev->private = pdata;
ipoll_dev->poll = fxos8700_poll;
ipoll_dev->poll_interval = FXOS8700_POLL_INTERVAL;
ipoll_dev->poll_interval_min = FXOS8700_POLL_MIN;
ipoll_dev->poll_interval_max = FXOS8700_POLL_MAX;
idev = ipoll_dev->input;
idev->name = FXOS8700_DRIVER;
idev->id.bustype = BUS_I2C;
idev->dev.parent = &pdata->client->dev;
idev->evbit[0] = BIT_MASK(EV_ABS);
input_set_abs_params(idev, ABS_X, ABSMIN_ACC_VAL, ABSMAX_ACC_VAL, 0, 0);
input_set_abs_params(idev, ABS_Y, ABSMIN_ACC_VAL, ABSMAX_ACC_VAL, 0, 0);
input_set_abs_params(idev, ABS_Z, ABSMIN_ACC_VAL, ABSMAX_ACC_VAL, 0, 0);
error = input_register_polled_device(ipoll_dev);
if (error) {
input_free_polled_device(ipoll_dev);
return error;
}
pdata->input_polled = ipoll_dev;
return 0;
}
static int fxos8700_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int result, client_id;
struct fxos8700_data *pdata;
struct i2c_adapter *adapter;
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, FXOS8700_WHO_AM_I);
if (client_id != FXOS8700_DEVICE_ID && client_id != FXOS8700_PRE_DEVICE_ID) {
dev_err(&client->dev,
"read chip ID 0x%x is not equal to 0x%x or 0x%x\n",
result, FXOS8700_DEVICE_ID, FXOS8700_PRE_DEVICE_ID);
result = -EINVAL;
goto err_out;
}
pdata = kzalloc(sizeof(struct fxos8700_data), GFP_KERNEL);
if (!pdata) {
result = -ENOMEM;
dev_err(&client->dev, "alloc data memory error!\n");
goto err_out;
}
g_fxos8700_data = pdata;
pdata->client = client;
atomic_set(&pdata->acc_delay, FXOS8700_DELAY_DEFAULT);
atomic_set(&pdata->mag_delay, FXOS8700_DELAY_DEFAULT);
i2c_set_clientdata(client, pdata);
result = misc_register(&fxos8700_acc_device);
if (result != 0) {
printk(KERN_ERR "register acc miscdevice error");
goto err_regsiter_acc_misc;
}
pdata->acc_miscdev = &fxos8700_acc_device;
result = misc_register(&fxos8700_mag_device);
if (result != 0) {
printk(KERN_ERR "register acc miscdevice error");
goto err_regsiter_mag_misc;
}
pdata->mag_miscdev = &fxos8700_mag_device;
/* for debug */
if (client->irq <= 0) {
result = fxo8700_register_polled_device(g_fxos8700_data);
if (result)
dev_err(&client->dev,
"IRQ GPIO conf. error %d, error %d\n",
client->irq, result);
}
result = fxos8700_register_sysfs_device(pdata);
if (result) {
dev_err(&client->dev, "create device file failed!\n");
result = -EINVAL;
goto err_register_sys;
}
fxos8700_device_init(client);
printk("fxos8700 device driver probe successfully");
return 0;
err_register_sys:
misc_deregister(&fxos8700_mag_device);
pdata->mag_miscdev = NULL;
err_regsiter_mag_misc:
misc_deregister(&fxos8700_acc_device);
pdata->acc_miscdev = NULL;
err_regsiter_acc_misc:
i2c_set_clientdata(client, NULL);
kfree(pdata);
err_out:
return result;
}
static int fxos8700_remove(struct i2c_client *client)
{
struct fxos8700_data *pdata = i2c_get_clientdata(client);
if (!pdata)
return 0;
fxos8700_device_stop(client);
if (client->irq <= 0) {
input_unregister_polled_device(pdata->input_polled);
input_free_polled_device(pdata->input_polled);
}
fxos8700_unregister_sysfs_device(pdata);
misc_deregister(&fxos8700_acc_device);
misc_deregister(&fxos8700_mag_device);
kfree(pdata);
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int fxos8700_suspend(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct fxos8700_data *pdata = i2c_get_clientdata(client);
if (atomic_read(&pdata->acc_active) || atomic_read(&pdata->mag_active))
fxos8700_device_stop(client);
return 0;
}
static int fxos8700_resume(struct device *dev)
{
int ret = 0;
struct i2c_client *client = to_i2c_client(dev);
struct fxos8700_data *pdata = i2c_get_clientdata(client);
if (atomic_read(&pdata->acc_active))
fxos8700_change_mode(client, FXOS8700_TYPE_ACC, FXOS8700_ACTIVED);
if (atomic_read(&pdata->mag_active))
fxos8700_change_mode(client, FXOS8700_TYPE_MAG, FXOS8700_ACTIVED);
return ret;
}
#endif
static const struct i2c_device_id fxos8700_id[] = {
{"fxos8700", 0},
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(i2c, fxos8700_id);
static SIMPLE_DEV_PM_OPS(fxos8700_pm_ops, fxos8700_suspend, fxos8700_resume);
static struct i2c_driver fxos8700_driver = {
.driver = {
.name = FXOS8700_DRIVER,
.owner = THIS_MODULE,
.pm = &fxos8700_pm_ops,
},
.probe = fxos8700_probe,
.remove = fxos8700_remove,
.id_table = fxos8700_id,
};
module_i2c_driver(fxos8700_driver);
MODULE_AUTHOR("Freescale Semiconductor, Inc.");
MODULE_DESCRIPTION("FXOS8700 6-Axis Acc and Mag Combo Sensor driver");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,47 @@
/*
* Copyright (C) 2011-2014 Freescale Semiconductor, 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.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef __UAPI_LINUX_ISL29023_H__
#define __UAPI_LINUX_ISL29023_H__
#include <linux/types.h>
#define ISL29023_PD_MODE 0x0
#define ISL29023_ALS_ONCE_MODE 0x1
#define ISL29023_IR_ONCE_MODE 0x2
#define ISL29023_ALS_CONT_MODE 0x5
#define ISL29023_IR_CONT_MODE 0x6
#define ISL29023_INT_PERSISTS_1 0x0
#define ISL29023_INT_PERSISTS_4 0x1
#define ISL29023_INT_PERSISTS_8 0x2
#define ISL29023_INT_PERSISTS_16 0x3
#define ISL29023_RES_16 0x0
#define ISL29023_RES_12 0x1
#define ISL29023_RES_8 0x2
#define ISL29023_RES_4 0x3
#define ISL29023_RANGE_1K 0x0
#define ISL29023_RANGE_4K 0x1
#define ISL29023_RANGE_16K 0x2
#define ISL29023_RANGE_64K 0x3
#endif