979 lines
28 KiB
C
979 lines
28 KiB
C
/*
|
|
* 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");
|