1
0
This repository has been archived on 2025-07-31. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
orange_kernel/drivers/misc/icm42607.c
2025-03-18 10:29:27 +08:00

698 lines
19 KiB
C

/* drivers/misc/icm42607.c - icm42607 driver
*
* Copyright (C) 2003-2015 Invensense Corporation.
* Author: Mingyang Li <myli@invensense.com>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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 "icm42607.h"
#define MPU_DEVICE_NAME "icm42607"
#define ICM42607_POLL_INTERVAL 100
#define ICM42607_POLL_INTERVAL_MIN 5
#define ICM42607_POLL_INTERVAL_MAX 500
#define ICM42607_CAPTURE_TIMES 10
#define ICM42607_ACCEL_SENSITIVE 16384
int inv_debug = 0;
#define inv_info(fmt, arg...) \
do{ \
if (inv_debug) \
printk("%s "fmt, MPU_DEVICE_NAME, ##arg); \
}while(0)
static int icm42607_read_accel_xyz(struct icm42607_device *dev);
static int icm42607_read_gyro_xyz(struct icm42607_device *dev);
static int icm42607_read(struct i2c_client *client, unsigned char addr)
{
int ret = 0;
ret = i2c_smbus_read_byte_data(client, addr);
if (ret < 0) {
printk("failed to read register 0x%02x, error %d\n", addr, ret);
}
return ret;
}
static int icm42607_write(struct i2c_client *client, unsigned char addr, unsigned char data)
{
int ret = 0;
ret = i2c_smbus_write_byte_data(client, addr, data);
if (ret < 0) {
printk("failed to write register 0x%02x, error %d\n", addr, ret);
return ret;
}
return 0;
}
static void icm42607_accel_enable(struct icm42607_device *dev)
{
struct i2c_client *client = dev->client;
unsigned char data = 0;
int res = 0;
data = icm42607_read(client, ICM42607_PWR_MGMT0);
data &= 0xFC;
data |= 0x03;
res = icm42607_write(client, ICM42607_PWR_MGMT0, data);
if (res) {
printk("icm42607_accel_enable error res=%d\n", res);
}
msleep(10);
}
static void icm42607_accel_disable(struct icm42607_device *dev)
{
struct i2c_client *client = dev->client;
unsigned char data = 0;
int res = 0;
data = icm42607_read(client, ICM42607_PWR_MGMT0);
data &= 0xFC;
res = icm42607_write(client, ICM42607_PWR_MGMT0, data);
if (res) {
printk("icm42607_accel_disable error res=%d\n", res);
}
msleep(1);
}
static void icm42607_gyro_enable(struct icm42607_device *dev)
{
struct i2c_client *client = dev->client;
unsigned char data = 0;
int res = 0;
data = icm42607_read(client, ICM42607_PWR_MGMT0);
data &= 0xF3;
data |= 0x0C;
res = icm42607_write(client, ICM42607_PWR_MGMT0, data);
if (res) {
printk("icm42607_gyro_enable error res=%d\n", res);
}
msleep(80);
}
static void icm42607_gyro_disable(struct icm42607_device *dev)
{
struct i2c_client *client = dev->client;
unsigned char data = 0;
int res = 0;
data = icm42607_read(client, ICM42607_PWR_MGMT0);
data &= 0xF3;
res = icm42607_write(client, ICM42607_PWR_MGMT0, data);
if (res) {
printk("icm42607_gyro_disable error res=%d\n", res);
}
msleep(10);
}
static void icm42607_reset_delay(struct icm42607_device *dev)
{
struct icm42607_device *mpu =dev;
mutex_lock(&mpu->mutex);
mpu->poll_time = (mpu->accel_poll) < (mpu->gyro_poll) ? (mpu->accel_poll) : (mpu->gyro_poll);
mpu->input_dev->poll_interval = mpu->poll_time;
mutex_unlock(&mpu->mutex);
}
static void icm42607_accel_gyro_calibration(struct icm42607_device *dev)
{
int i = 0;
int err = 0;
long int sum_accel[3] = {0, 0, 0};
long int sum_gyro[3] = {0, 0, 0};
for (i = 0; i < ICM42607_CAPTURE_TIMES; i++) {
err = icm42607_read_accel_xyz(dev);
if (err < 0) {
printk("in %s read accel data error\n", __func__);
}
err = icm42607_read_gyro_xyz(dev);
if (err < 0) {
printk("in %s read gyro data error\n", __func__);
}
sum_accel[0] += dev->accel_data[0];
sum_accel[1] += dev->accel_data[1];
sum_accel[2] += dev->accel_data[2];
sum_gyro[0] += dev->gyro_data[0];
sum_gyro[1] += dev->gyro_data[1];
sum_gyro[2] += dev->gyro_data[2];
inv_info("%d times, read accel data is %d %d %d, gyro data is %d %d %d \n",
i, dev->accel_data[0], dev->accel_data[1], dev->accel_data[2],
dev->gyro_data[0], dev->gyro_data[1], dev->gyro_data[2]);
}
dev->accel_offset[0] = sum_accel[0]/ICM42607_CAPTURE_TIMES;
dev->accel_offset[1] = sum_accel[1]/ICM42607_CAPTURE_TIMES;
dev->accel_offset[2] = ICM42607_ACCEL_SENSITIVE - sum_accel[2]/ICM42607_CAPTURE_TIMES;
dev->gyro_offset[0] = sum_gyro[0]/ICM42607_CAPTURE_TIMES;
dev->gyro_offset[1] = sum_gyro[1]/ICM42607_CAPTURE_TIMES;
dev->gyro_offset[2] = sum_gyro[2]/ICM42607_CAPTURE_TIMES;
inv_info("accel offset is %d %d %d, gyro offset is %d %d %d \n",
dev->accel_offset[0], dev->accel_offset[1], dev->accel_offset[2],
dev->gyro_offset[0], dev->gyro_offset[1], dev->gyro_offset[2]);
}
static ssize_t icm42607_accel_enable_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct input_polled_dev *polldev = dev_get_drvdata(dev);
struct icm42607_device *mpu = (struct icm42607_device *)polldev->private;
inv_info("%s: called\n", __func__);
return sprintf(buf, "%d\n", mpu->accel_status);
}
static ssize_t icm42607_accel_enable_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct input_polled_dev *polldev = dev_get_drvdata(dev);
struct icm42607_device *mpu = (struct icm42607_device *)polldev->private;
int changed = 0;
unsigned long val;
val = simple_strtoul(buf, NULL, 10);
inv_info("in %s: data is %ld\n", __func__, val);
changed = (val == mpu->accel_status) ? 0 : 1;
if (changed) {
printk("accel status changed, now state is %s\n", (val > 0) ? "active" : "disable");
if (val) {
icm42607_accel_enable(mpu);
mpu->accel_status = 1;
} else {
icm42607_accel_disable(mpu);
mpu->accel_status = 0;
}
}
return count;
}
static ssize_t icm42607_accel_delay_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct input_polled_dev *polldev = dev_get_drvdata(dev);
struct icm42607_device *mpu = (struct icm42607_device *)polldev->private;
inv_info("%s: called\n", __func__);
return sprintf(buf, "%d\n", mpu->accel_poll);
}
static ssize_t icm42607_accel_delay_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct input_polled_dev *polldev = dev_get_drvdata(dev);
struct icm42607_device *mpu = (struct icm42607_device *)polldev->private;
unsigned long val;
val = simple_strtoul(buf, NULL, 10);
inv_info("in %s: data is %ld\n", __func__, val);
mpu->accel_poll = val;
icm42607_reset_delay(mpu);
return count;
}
static ssize_t icm42607_gyro_enable_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct input_polled_dev *polldev = dev_get_drvdata(dev);
struct icm42607_device *mpu = (struct icm42607_device *)polldev->private;
inv_info("%s : called name %s\n", __func__, mpu->input_dev->input->name);
return sprintf(buf, "%d\n", mpu->gyro_status);
}
static ssize_t icm42607_gyro_enable_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct input_polled_dev *polldev = dev_get_drvdata(dev);
struct icm42607_device *mpu = (struct icm42607_device *)polldev->private;
int changed = 0;
unsigned long val;
val = simple_strtoul(buf, NULL, 10);
inv_info("in %s: data is %ld\n", __func__, val);
changed = (val == mpu->gyro_status) ? 0 : 1;
if (changed) {
printk("gyro status changed, now state is %s\n", (val > 0) ? "active" : "disable");
if (val) {
icm42607_gyro_enable(mpu);
mpu->gyro_status = 1;
} else {
icm42607_gyro_disable(mpu);
mpu->gyro_status = 0;
}
}
return count;
}
static ssize_t icm42607_gyro_delay_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct input_polled_dev *polldev = dev_get_drvdata(dev);
struct icm42607_device *mpu = (struct icm42607_device *)polldev->private;
inv_info("%s : called name %s\n", __func__, mpu->input_dev->input->name);
return sprintf(buf, "%d\n", mpu->gyro_poll);
}
static ssize_t icm42607_gyro_delay_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct input_polled_dev *polldev = dev_get_drvdata(dev);
struct icm42607_device *mpu = (struct icm42607_device *)polldev->private;
unsigned long val;
val = simple_strtoul(buf, NULL, 10);
inv_info("in %s: data is %ld\n",__func__,val);
mpu->gyro_poll = val;
icm42607_reset_delay(mpu);
return count;
}
static ssize_t icm42607_device_delay_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct input_polled_dev *polldev = dev_get_drvdata(dev);
struct icm42607_device *mpu = (struct icm42607_device *)polldev->private;
inv_info("%s : called name %s (delay: %d)\n", __func__, mpu->input_dev->input->name, mpu->poll_time);
return sprintf(buf, "%d\n", mpu->input_dev->poll_interval);
}
static ssize_t icm42607_device_delay_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct input_polled_dev *polldev = dev_get_drvdata(dev);
struct icm42607_device *mpu = (struct icm42607_device *)polldev->private;
unsigned long val;
val = simple_strtoul(buf, NULL, 10);
inv_info("in %s: data is %ld\n",__func__,val);
mpu->poll_time = val;
mpu->input_dev->poll_interval = mpu->poll_time;
return count;
}
static ssize_t icm42607_calibration_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct input_polled_dev *polldev = dev_get_drvdata(dev);
struct icm42607_device *mpu = (struct icm42607_device *)polldev->private;
inv_info("%s : called name %s\n", __func__, mpu->input_dev->input->name);
return sprintf(buf, "AX%d AY%d AZ%d GX%d GY%d GZ%d\n", mpu->accel_offset[0], mpu->accel_offset[1], mpu->accel_offset[2],
mpu->gyro_offset[0], mpu->gyro_offset[1], mpu->gyro_offset[2]);
}
static ssize_t icm42607_calibration_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct input_polled_dev *polldev = dev_get_drvdata(dev);
struct icm42607_device *mpu = (struct icm42607_device *)polldev->private;
unsigned long val;
//before calibration, save accel and gyro status
int pre_accel = mpu->accel_status;
int pre_gyro = mpu->gyro_status;
val = simple_strtoul(buf, NULL, 10);
if (val != 1) {
return count;
}
//close accel and gyro to calibration
mpu->accel_status = 0;
mpu->gyro_status = 0;
if (pre_accel == 0) {
icm42607_accel_enable(mpu);
}
if (pre_gyro == 0) {
icm42607_gyro_enable(mpu);
}
icm42607_accel_gyro_calibration(mpu);
//after calibration restore previous accel and gyro status
mpu->accel_status = pre_accel;
mpu->gyro_status = pre_gyro;
if (pre_accel == 0) {
icm42607_accel_disable(mpu);
}
if (pre_gyro == 0) {
icm42607_gyro_disable(mpu);
}
return count;
}
static ssize_t icm42607_debug_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
inv_info("%s : called inv_debug is %d\n", __func__, inv_debug);
return sprintf(buf, "%d\n", inv_debug);
}
static ssize_t icm42607_debug_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
unsigned long val;
val = simple_strtoul(buf, NULL, 10);
inv_info("in %s: data is %ld\n",__func__, val);
inv_debug = val;
return count;
}
/* S_IWUGO | S_IRUGO -> 0644 */
static DEVICE_ATTR(accel_enable, 0644, icm42607_accel_enable_show, icm42607_accel_enable_store);
static DEVICE_ATTR(accel_delay, 0644, icm42607_accel_delay_show, icm42607_accel_delay_store);
static DEVICE_ATTR(gyro_enable, 0644, icm42607_gyro_enable_show, icm42607_gyro_enable_store);
static DEVICE_ATTR(gyro_delay, 0644, icm42607_gyro_delay_show, icm42607_gyro_delay_store);
static DEVICE_ATTR(device_delay, 0644, icm42607_device_delay_show, icm42607_device_delay_store);
static DEVICE_ATTR(calibration, 0644, icm42607_calibration_show, icm42607_calibration_store);
static DEVICE_ATTR(debug, 0644, icm42607_debug_show, icm42607_debug_store);
static int icm42607_read_accel_xyz(struct icm42607_device *dev)
{
struct i2c_client *client = dev->client;
u8 buff[6];
int err = 0;
err = i2c_smbus_read_i2c_block_data(client, ICM42607_ACCEL_DATA_X1, 6, buff);
if (err < 0) {
printk("failed to read acc data, err=%d\n", err);
return err;
}
dev->accel_data[0] = (short)(buff[0]<<8 | buff[1]);
dev->accel_data[1] = (short)(buff[2]<<8 | buff[3]);
dev->accel_data[2] = (short)(buff[4]<<8 | buff[5]);
return 0;
}
static int icm42607_read_gyro_xyz(struct icm42607_device *dev)
{
struct i2c_client *client = dev->client;
u8 buff[6];
int err = 0;
err = i2c_smbus_read_i2c_block_data(client, ICM42607_GYRO_DATA_X1, 6, buff);
if (err < 0) {
printk("failed to read gyro data error=%d\n", err);
return err;
}
dev->gyro_data[0] = (short)(buff[0]<<8 | buff[1]);
dev->gyro_data[1] = (short)(buff[2]<<8 | buff[3]);
dev->gyro_data[2] = (short)(buff[4]<<8 | buff[5]);
return 0;
}
static void icm42607_read_sensors_data(struct icm42607_device *device)
{
struct icm42607_device *dev = device;
if (dev->accel_status) {
icm42607_read_accel_xyz(dev);
inv_info("accel data is read finished, %d %d %d\n", dev->accel_data[0],
dev->accel_data[1], dev->accel_data[2]);
}
if (dev->gyro_status) {
icm42607_read_gyro_xyz(dev);
inv_info("gyro data is read finished, %d %d %d\n", dev->gyro_data[0],
dev->gyro_data[1], dev->gyro_data[2]);
}
}
static void icm42607_report_value(struct icm42607_device *device)
{
struct icm42607_device *dev = device;
if (dev->accel_status) {
input_report_abs(dev->input_dev->input,ABS_X, dev->accel_data[0]);
input_report_abs(dev->input_dev->input,ABS_Y, dev->accel_data[1]);
input_report_abs(dev->input_dev->input,ABS_Z, dev->accel_data[2]);
input_sync(dev->input_dev->input);
}
if (dev->gyro_status) {
input_report_abs(dev->input_dev->input,ABS_RX, dev->gyro_data[0]);
input_report_abs(dev->input_dev->input,ABS_RY, dev->gyro_data[1]);
input_report_abs(dev->input_dev->input,ABS_RZ, dev->gyro_data[2]);
input_sync(dev->input_dev->input);
}
}
static void icm42607_poll(struct input_polled_dev *dev)
{
struct icm42607_device *mpu = (struct icm42607_device *)dev->private;
icm42607_read_sensors_data(mpu);
icm42607_report_value(mpu);
}
static int icm42607_device_init(struct i2c_client *client)
{
int res = 0;
int val = 0;
res = icm42607_write(client, ICM42607_SIGNAL_PATH_RESET, 0x10);
if (res) {
printk("ICM42607 soft reset error res=%d\n", res);
return res;
}
msleep(100);
val = icm42607_read(client, ICM42607_INT_STATUS);
printk("ICM42607_INT_STATUS=%d\n", val);
val = icm42607_read(client, ICM42607_WHO_AM_I);
printk("ICM42607 whoami=%d\n", val);
res = icm42607_write(client, ICM42607_INTF_CONFIG1, 0x01); //disable i3c sdr/ddr
if (res) {
printk("ICM42607 config gyro error res=%d!\n", res);
return res;
}
res = icm42607_write(client, ICM42607_GYRO_CONFIG0, 0x0B); //config gyro for 2000dps 25Hz
if (res) {
printk("ICM42607 config gyro error res=%d!\n", res);
return res;
}
res = icm42607_write(client, ICM42607_ACCEL_CONFIG0, 0x6B); //config Accel for 2g 25Hz
if (res) {
printk("ICM42607 config accel error res=%d!\n", res);
return res;
}
val = icm42607_read(client, ICM42607_GYRO_CONFIG0);
printk("ICM42607_GYRO_CONFIG0=%d\n", val);
val = icm42607_read(client, ICM42607_ACCEL_CONFIG0);
printk("ICM42607_ACCEL_CONFIG0=%d\n", val);
return 0;
}
static struct attribute *icm42607_sysfs_attrs[] = {
&dev_attr_accel_enable.attr,
&dev_attr_accel_delay.attr,
&dev_attr_gyro_enable.attr,
&dev_attr_gyro_delay.attr,
&dev_attr_device_delay.attr,
&dev_attr_calibration.attr,
&dev_attr_debug.attr,
NULL
};
struct attribute_group icm42607_attrs = {
.attrs = icm42607_sysfs_attrs,
};
int icm42607_probe(struct i2c_client *client, const struct i2c_device_id *devid)
{
struct icm42607_device *mpu;
int err;
printk("icm42607_probe start\n");
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
printk("platform data is NULL. exiting.\n");
err = -ENODEV;
goto exit_check_functionality_failed;
}
mpu = kzalloc(sizeof(struct icm42607_device), GFP_KERNEL);
if (!mpu) {
printk("failed to allocate memory for module data\n");
err = -ENOMEM;
goto exit_alloc_data_failed;
}
mutex_init(&mpu->mutex);
i2c_set_clientdata(client, mpu);
err = icm42607_device_init(client);
if (err) {
printk("icm42607_device_init fail\n");
goto exit_input_dev_alloc_failed;
}
mpu->client = client;
mpu->input_dev = input_allocate_polled_device();
if (!mpu->input_dev) {
err = -ENOMEM;
printk("input_allocate_polled_device failed\n");
goto exit_input_dev_alloc_failed;
}
set_bit(EV_ABS, mpu->input_dev->input->evbit);
/* x-axis acceleration */
input_set_abs_params(mpu->input_dev->input, ABS_X, -32768, 32767, 0, 0);
/* y-axis acceleration */
input_set_abs_params(mpu->input_dev->input, ABS_Y, -32768, 32767, 0, 0);
/* z-axis acceleration */
input_set_abs_params(mpu->input_dev->input, ABS_Z, -32768, 32767, 0, 0);
/* Gyro X-axis */
input_set_abs_params(mpu->input_dev->input, ABS_RX, -32768, 32767, 0, 0);
/* Gyro Y-axis */
input_set_abs_params(mpu->input_dev->input, ABS_RY, -32768, 32767, 0, 0);
/* Gyro Z-axis */
input_set_abs_params(mpu->input_dev->input, ABS_RZ, -32768, 32767, 0, 0);
mpu->input_dev->private = mpu;
mpu->input_dev->input->name = MPU_DEVICE_NAME;
mpu->input_dev->input->id.bustype = BUS_I2C;
mpu->input_dev->poll = icm42607_poll;
mpu->input_dev->poll_interval = ICM42607_POLL_INTERVAL;
/* diff */
/*
mpu->input_dev->poll_interval_max = ICM42607_POLL_INTERVAL_MAX;
mpu->input_dev->poll_interval_min = ICM42607_POLL_INTERVAL_MIN;*/
mpu->input_dev->poll_interval = ICM42607_POLL_INTERVAL;
mpu->accel_poll = ICM42607_POLL_INTERVAL;
mpu->gyro_poll = ICM42607_POLL_INTERVAL;
err = input_register_polled_device(mpu->input_dev);
if (err) {
printk("icm42607_probe: Unable to register input device: %s\n",
mpu->input_dev->input->name);
goto exit_input_register_device_failed;
}
//mpu->input_dev->private = mpu;
//mpu->input_dev->close(mpu->input_dev);
if (sysfs_create_group(&mpu->input_dev->input->dev.kobj, &icm42607_attrs)) {
printk("icm42607_probe create sysfile error\n");
goto exit_input_register_device_failed;
}
printk("icm42607 probe successfully\n");
return 0;
exit_input_register_device_failed:
input_unregister_polled_device(mpu->input_dev);
exit_input_dev_alloc_failed:
kfree(mpu);
exit_alloc_data_failed:
exit_check_functionality_failed:
return err;
}
/* diff */
static void icm42607_remove(struct i2c_client *client)
{
struct icm42607_device *mpu = i2c_get_clientdata(client);
input_unregister_polled_device(mpu->input_dev);
kfree(mpu);
}
static const struct i2c_device_id icm42607_id[] = {
{"icm42607",0},
{}
};
MODULE_DEVICE_TABLE(i2c, icm42607_id);
static const struct of_device_id icm42607_match[] = {
{.compatible = "invn,icm42607",},
{}
};
MODULE_DEVICE_TABLE(of, icm42607_match);
static struct i2c_driver icm42607_driver = {
.probe = icm42607_probe,
.remove = icm42607_remove,
.id_table = icm42607_id,
.driver = {
.owner = THIS_MODULE,
.of_match_table = icm42607_match,
.name = "icm42607",
},
};
static int __init icm42607_init(void)
{
printk("icm42607 driver: init\n");
return i2c_add_driver(&icm42607_driver);
}
static void __exit icm42607_exit(void)
{
printk("icm42607 driver: exit\n");
i2c_del_driver(&icm42607_driver);
}
module_init(icm42607_init);
module_exit(icm42607_exit);
MODULE_AUTHOR("Invensense Corporation");
MODULE_DESCRIPTION("Invensense I2C device driver");
MODULE_LICENSE("GPL");