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