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