找回密码
 立即注册
首页 业界区 业界 龙芯2k0300 - 走马观碑组MPU6050驱动移植

龙芯2k0300 - 走马观碑组MPU6050驱动移植

判涔 6 小时前
在《龙芯2k0300 - 走马观碑组第21届智能汽车竞赛软硬件设计》中我们介绍到我们的开发板使用了MPU6050 陀螺仪,MPU6050是一个六轴姿态传感器,内部集成了三轴加速度计和三轴陀螺仪:


  • 加速度计:检测X、Y、Z三个轴上的加速度(比如前进、后退、倾斜);
  • 陀螺仪:检测绕X、Y、Z轴的旋转速度(比如左转、右转、翻滚);
1.png
MPU6050陀螺仪采用I2C通信方式:
2.png
3.png
MPU6050陀螺仪采用I2C通信方式,与龙芯2K0300开发板的连接关系需严格对应,确保I2C通信与GPIO控制正常,连接表如下:
  屏幕引脚 功能 连接的龙芯GPIO 说明     VCC 为MPU6050提供工作电压 3.3V 接3.3V   GND 电源共地,确保电压稳定性 GND 必须可靠接地,否则可能出现显示异常   SCL I2C 时钟引脚 I2C1_SCL (GPIO50) 提供I2C同步通信时钟   SDA SDA数据引脚 I2C1_SDA(GPIO51) 传输I2C命令与显示数据   XDA  ——    XCL  ——    AD0 决定 I2C 地址 —— 接 GND 地址为 0x68,接 3.3V 为 0x69   INT 中断引脚 —— 中断引脚,暂时不用接        一、MPU6050设备驱动

MPU6050由于采用了I2C通讯协议,所以涉及到了I2C设备驱动。如果对I2C驱动源码移植感兴趣,可参考:


  • 《通信协议-I2C》;
  • 《linux驱动移植-I2C总线设备驱动》;
  • 《linux驱动移植-I2C适配器驱动移植》;
  • 《linux驱动移植-I2C驱动移植(OLED SSD1306)》。
龙邱科技提供了MPU6050设备驱动实现,位于: i2c_mpu6050_driver,只不过这个是基于linux 4.19的,如果移植这个改动的相对较多。
1.1 内核配置

实际上我们下载的内核linux 6.12已经内置了MPU6050的驱动,其使用了IIO框架,这里我们就直接使用内核驱动即可,无需从零开发驱动,需开启内核I2C及IIO相关配置,并添加MPU6050驱动代码。
进入内核配置界面:
  1. zhengyang@ubuntu:~$ cd /opt/2k0300/build-2k0300/workspace/linux-6.12
  2. zhengyang@ubuntu:/opt/2k0300/build-2k0300/workspace/linux-6.12$ source ../set_env.sh && make menuconfig
复制代码
依次进入以下菜单:
  1. Device Drivers  →
  2.     I2C support   →
  3.         I2C Hardware Bus support →
  4.             <*> Loongson fast speed I2C adapter    # 龙芯 2K0300 I2C控制器驱动
  5.         <*>  I2C device interface   
  6.      [*]  Industrial I/O support  →
  7.                     Inertial measurement units  →
  8.                             <*> Invensense MPU6050 devices (I2C)
复制代码
默认会生成配置:
  1. CONFIG_I2C_LSFS=y  # 这个已经配置,不用追加
  2. CONFIG_I2C_CHARDEV=y
  3. CONFIG_IIO=y       # 这个已经配置,不用追加
  4. CONFIG_INV_MPU6050_IIO=y
  5. CONFIG_INV_MPU6050_I2C=y
复制代码
我们直接修改arch/loongarch/configs/loongson_2k300_defconfig文件,加入这几个配置。
1.2 MPU6050驱动

驱动源码位于drivers/iio/imu/inv_mpu6050/目录下,基于内核IIO框架开发,无需修改内核核心代码,只需编译进内核即可;
  1. zhengyang@ubuntu:/opt/2k0300/build-2k0300/workspace/linux-6.12$ ll drivers/iio/imu/inv_mpu6050/
  2. -rw-rw-r-- 1 zhengyang zhengyang  4620  3月 19 19:48 inv_mpu_acpi.c
  3. -rw-rw-r-- 1 zhengyang zhengyang  5522  3月 19 19:48 inv_mpu_aux.c
  4. -rw-rw-r-- 1 zhengyang zhengyang   479  3月 19 19:48 inv_mpu_aux.h
  5. -rw-rw-r-- 1 zhengyang zhengyang 62807  3月 19 19:48 inv_mpu_core.c
  6. -rw-rw-r-- 1 zhengyang zhengyang  6467  3月 19 19:48 inv_mpu_i2c.c
  7. -rw-rw-r-- 1 zhengyang zhengyang 16370  3月 19 19:48 inv_mpu_iio.h
  8. -rw-rw-r-- 1 zhengyang zhengyang  8654  3月 19 19:48 inv_mpu_magn.c
  9. -rw-rw-r-- 1 zhengyang zhengyang   931  3月 19 19:48 inv_mpu_magn.h
  10. -rw-rw-r-- 1 zhengyang zhengyang  3830  3月 19 19:48 inv_mpu_ring.c
  11. -rw-rw-r-- 1 zhengyang zhengyang  3883  3月 19 19:48 inv_mpu_spi.c
  12. -rw-rw-r-- 1 zhengyang zhengyang  8786  3月 19 19:48 inv_mpu_trigger.c
  13. -rw-rw-r-- 1 zhengyang zhengyang  1042  3月 19 19:48 Kconfig
  14. -rw-rw-r-- 1 zhengyang zhengyang   417  3月 19 19:48 Makefile
复制代码
我们重点关注inv_mpu_i2c.c和inv_mpu_core.c。
1.2.1 inv_mpu_i2c.c

drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c源码如下:
  点击查看详情
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Copyright (C) 2012 Invensense, Inc.
  4. */
  5. #include <linux/delay.h>
  6. #include <linux/err.h>
  7. #include <linux/i2c.h>
  8. #include <linux/iio/iio.h>
  9. #include <linux/mod_devicetable.h>
  10. #include <linux/module.h>
  11. #include <linux/property.h>
  12. #include "inv_mpu_iio.h"
  13. static const struct regmap_config inv_mpu_regmap_config = {
  14.         .reg_bits = 8,
  15.         .val_bits = 8,
  16. };
  17. static int inv_mpu6050_select_bypass(struct i2c_mux_core *muxc, u32 chan_id)
  18. {
  19.         return 0;
  20. }
  21. static bool inv_mpu_i2c_aux_bus(struct device *dev)
  22. {
  23.         struct inv_mpu6050_state *st = iio_priv(dev_get_drvdata(dev));
  24.         switch (st->chip_type) {
  25.         case INV_ICM20608:
  26.         case INV_ICM20608D:
  27.         case INV_ICM20609:
  28.         case INV_ICM20689:
  29.         case INV_ICM20600:
  30.         case INV_ICM20602:
  31.         case INV_IAM20680:
  32.                 /* no i2c auxiliary bus on the chip */
  33.                 return false;
  34.         case INV_MPU9150:
  35.         case INV_MPU9250:
  36.         case INV_MPU9255:
  37.                 if (st->magn_disabled)
  38.                         return true;
  39.                 else
  40.                         return false;
  41.         default:
  42.                 return true;
  43.         }
  44. }
  45. static int inv_mpu_i2c_aux_setup(struct iio_dev *indio_dev)
  46. {
  47.         struct inv_mpu6050_state *st = iio_priv(indio_dev);
  48.         struct device *dev = indio_dev->dev.parent;
  49.         struct fwnode_handle *mux_node;
  50.         int ret;
  51.         /*
  52.          * MPU9xxx magnetometer support requires to disable i2c auxiliary bus.
  53.          * To ensure backward compatibility with existing setups, do not disable
  54.          * i2c auxiliary bus if it used.
  55.          * Check for i2c-gate node in devicetree and set magnetometer disabled.
  56.          * Only MPU6500 is supported by ACPI, no need to check.
  57.          */
  58.         switch (st->chip_type) {
  59.         case INV_MPU9150:
  60.         case INV_MPU9250:
  61.         case INV_MPU9255:
  62.                 mux_node = device_get_named_child_node(dev, "i2c-gate");
  63.                 if (mux_node != NULL) {
  64.                         st->magn_disabled = true;
  65.                         dev_warn(dev, "disable internal use of magnetometer\n");
  66.                 }
  67.                 fwnode_handle_put(mux_node);
  68.                 break;
  69.         default:
  70.                 break;
  71.         }
  72.         /* enable i2c bypass when using i2c auxiliary bus */
  73.         if (inv_mpu_i2c_aux_bus(dev)) {
  74.                 ret = regmap_write(st->map, st->reg->int_pin_cfg,
  75.                                    st->irq_mask | INV_MPU6050_BIT_BYPASS_EN);
  76.                 if (ret)
  77.                         return ret;
  78.         }
  79.         return 0;
  80. }
  81. /**
  82. *  inv_mpu_probe() - probe function.
  83. *  @client:          i2c client.
  84. *
  85. *  Returns 0 on success, a negative error code otherwise.
  86. */
  87. static int inv_mpu_probe(struct i2c_client *client)
  88. {
  89.         const struct i2c_device_id *id = i2c_client_get_device_id(client);
  90.         const void *match;
  91.         struct inv_mpu6050_state *st;
  92.         int result;
  93.         enum inv_devices chip_type;
  94.         struct regmap *regmap;
  95.         const char *name;
  96.         if (!i2c_check_functionality(client->adapter,
  97.                                      I2C_FUNC_SMBUS_I2C_BLOCK))
  98.                 return -EOPNOTSUPP;
  99.         match = device_get_match_data(&client->dev);
  100.         if (match) {
  101.                 chip_type = (uintptr_t)match;
  102.                 name = client->name;
  103.         } else if (id) {
  104.                 chip_type = (enum inv_devices)
  105.                         id->driver_data;
  106.                 name = id->name;
  107.         } else {
  108.                 return -ENOSYS;
  109.         }
  110.         regmap = devm_regmap_init_i2c(client, &inv_mpu_regmap_config);
  111.         if (IS_ERR(regmap)) {
  112.                 dev_err(&client->dev, "Failed to register i2c regmap: %pe\n",
  113.                         regmap);
  114.                 return PTR_ERR(regmap);
  115.         }
  116.         result = inv_mpu_core_probe(regmap, client->irq, name,
  117.                                     inv_mpu_i2c_aux_setup, chip_type);
  118.         if (result < 0)
  119.                 return result;
  120.         st = iio_priv(dev_get_drvdata(&client->dev));
  121.         if (inv_mpu_i2c_aux_bus(&client->dev)) {
  122.                 /* declare i2c auxiliary bus */
  123.                 st->muxc = i2c_mux_alloc(client->adapter, &client->dev,
  124.                                          1, 0, I2C_MUX_LOCKED | I2C_MUX_GATE,
  125.                                          inv_mpu6050_select_bypass, NULL);
  126.                 if (!st->muxc)
  127.                         return -ENOMEM;
  128.                 st->muxc->priv = dev_get_drvdata(&client->dev);
  129.                 result = i2c_mux_add_adapter(st->muxc, 0, 0);
  130.                 if (result)
  131.                         return result;
  132.                 result = inv_mpu_acpi_create_mux_client(client);
  133.                 if (result)
  134.                         goto out_del_mux;
  135.         }
  136.         return 0;
  137. out_del_mux:
  138.         i2c_mux_del_adapters(st->muxc);
  139.         return result;
  140. }
  141. static void inv_mpu_remove(struct i2c_client *client)
  142. {
  143.         struct iio_dev *indio_dev = i2c_get_clientdata(client);
  144.         struct inv_mpu6050_state *st = iio_priv(indio_dev);
  145.         if (st->muxc) {
  146.                 inv_mpu_acpi_delete_mux_client(client);
  147.                 i2c_mux_del_adapters(st->muxc);
  148.         }
  149. }
  150. /*
  151. * device id table is used to identify what device can be
  152. * supported by this driver
  153. */
  154. static const struct i2c_device_id inv_mpu_id[] = {
  155.         {"mpu6050", INV_MPU6050},
  156.         {"mpu6500", INV_MPU6500},
  157.         {"mpu6515", INV_MPU6515},
  158.         {"mpu6880", INV_MPU6880},
  159.         {"mpu9150", INV_MPU9150},
  160.         {"mpu9250", INV_MPU9250},
  161.         {"mpu9255", INV_MPU9255},
  162.         {"icm20608", INV_ICM20608},
  163.         {"icm20608d", INV_ICM20608D},
  164.         {"icm20609", INV_ICM20609},
  165.         {"icm20689", INV_ICM20689},
  166.         {"icm20600", INV_ICM20600},
  167.         {"icm20602", INV_ICM20602},
  168.         {"icm20690", INV_ICM20690},
  169.         {"iam20680", INV_IAM20680},
  170.         {}
  171. };
  172. MODULE_DEVICE_TABLE(i2c, inv_mpu_id);
  173. static const struct of_device_id inv_of_match[] = {
  174.         {
  175.                 .compatible = "invensense,mpu6050",
  176.                 .data = (void *)INV_MPU6050
  177.         },
  178.         {
  179.                 .compatible = "invensense,mpu6500",
  180.                 .data = (void *)INV_MPU6500
  181.         },
  182.         {
  183.                 .compatible = "invensense,mpu6515",
  184.                 .data = (void *)INV_MPU6515
  185.         },
  186.         {
  187.                 .compatible = "invensense,mpu6880",
  188.                 .data = (void *)INV_MPU6880
  189.         },
  190.         {
  191.                 .compatible = "invensense,mpu9150",
  192.                 .data = (void *)INV_MPU9150
  193.         },
  194.         {
  195.                 .compatible = "invensense,mpu9250",
  196.                 .data = (void *)INV_MPU9250
  197.         },
  198.         {
  199.                 .compatible = "invensense,mpu9255",
  200.                 .data = (void *)INV_MPU9255
  201.         },
  202.         {
  203.                 .compatible = "invensense,icm20608",
  204.                 .data = (void *)INV_ICM20608
  205.         },
  206.         {
  207.                 .compatible = "invensense,icm20608d",
  208.                 .data = (void *)INV_ICM20608D
  209.         },
  210.         {
  211.                 .compatible = "invensense,icm20609",
  212.                 .data = (void *)INV_ICM20609
  213.         },
  214.         {
  215.                 .compatible = "invensense,icm20689",
  216.                 .data = (void *)INV_ICM20689
  217.         },
  218.         {
  219.                 .compatible = "invensense,icm20600",
  220.                 .data = (void *)INV_ICM20600
  221.         },
  222.         {
  223.                 .compatible = "invensense,icm20602",
  224.                 .data = (void *)INV_ICM20602
  225.         },
  226.         {
  227.                 .compatible = "invensense,icm20690",
  228.                 .data = (void *)INV_ICM20690
  229.         },
  230.         {
  231.                 .compatible = "invensense,iam20680",
  232.                 .data = (void *)INV_IAM20680
  233.         },
  234.         { }
  235. };
  236. MODULE_DEVICE_TABLE(of, inv_of_match);
  237. static const struct acpi_device_id inv_acpi_match[] = {
  238.         {"INVN6500", INV_MPU6500},
  239.         { },
  240. };
  241. MODULE_DEVICE_TABLE(acpi, inv_acpi_match);
  242. static struct i2c_driver inv_mpu_driver = {
  243.         .probe                =        inv_mpu_probe,
  244.         .remove                =        inv_mpu_remove,
  245.         .id_table        =        inv_mpu_id,
  246.         .driver = {
  247.                 .of_match_table = inv_of_match,
  248.                 .acpi_match_table = inv_acpi_match,
  249.                 .name        =        "inv-mpu6050-i2c",
  250.                 .pm     =       pm_ptr(&inv_mpu_pmops),
  251.         },
  252. };
  253. module_i2c_driver(inv_mpu_driver);
  254. MODULE_AUTHOR("Invensense Corporation");
  255. MODULE_DESCRIPTION("Invensense device MPU6050 driver");
  256. MODULE_LICENSE("GPL");
  257. MODULE_IMPORT_NS(IIO_MPU6050);
复制代码
1.2.2  inv_mpu_core.c

drivers/iio/imu/inv_mpu6050/inv_mpu_core.c源码如下:
  点击查看详情
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Copyright (C) 2012 Invensense, Inc.
  4. */
  5. #include <linux/module.h>
  6. #include <linux/slab.h>
  7. #include <linux/i2c.h>
  8. #include <linux/err.h>
  9. #include <linux/delay.h>
  10. #include <linux/sysfs.h>
  11. #include <linux/jiffies.h>
  12. #include <linux/irq.h>
  13. #include <linux/interrupt.h>
  14. #include <linux/acpi.h>
  15. #include <linux/platform_device.h>
  16. #include <linux/regulator/consumer.h>
  17. #include <linux/math64.h>
  18. #include <linux/minmax.h>
  19. #include <linux/pm.h>
  20. #include <linux/pm_runtime.h>
  21. #include <linux/property.h>
  22. #include <linux/iio/common/inv_sensors_timestamp.h>
  23. #include <linux/iio/iio.h>
  24. #include "inv_mpu_iio.h"
  25. #include "inv_mpu_magn.h"
  26. /*
  27. * this is the gyro scale translated from dynamic range plus/minus
  28. * {250, 500, 1000, 2000} to rad/s
  29. */
  30. static const int gyro_scale_6050[] = {133090, 266181, 532362, 1064724};
  31. /*
  32. * this is the accel scale translated from dynamic range plus/minus
  33. * {2, 4, 8, 16} to m/s^2
  34. */
  35. static const int accel_scale[] = {598, 1196, 2392, 4785};
  36. static const struct inv_mpu6050_reg_map reg_set_icm20602 = {
  37.         .sample_rate_div        = INV_MPU6050_REG_SAMPLE_RATE_DIV,
  38.         .lpf                    = INV_MPU6050_REG_CONFIG,
  39.         .accel_lpf              = INV_MPU6500_REG_ACCEL_CONFIG_2,
  40.         .user_ctrl              = INV_MPU6050_REG_USER_CTRL,
  41.         .fifo_en                = INV_MPU6050_REG_FIFO_EN,
  42.         .gyro_config            = INV_MPU6050_REG_GYRO_CONFIG,
  43.         .accl_config            = INV_MPU6050_REG_ACCEL_CONFIG,
  44.         .fifo_count_h           = INV_MPU6050_REG_FIFO_COUNT_H,
  45.         .fifo_r_w               = INV_MPU6050_REG_FIFO_R_W,
  46.         .raw_gyro               = INV_MPU6050_REG_RAW_GYRO,
  47.         .raw_accl               = INV_MPU6050_REG_RAW_ACCEL,
  48.         .temperature            = INV_MPU6050_REG_TEMPERATURE,
  49.         .int_enable             = INV_MPU6050_REG_INT_ENABLE,
  50.         .int_status             = INV_MPU6050_REG_INT_STATUS,
  51.         .pwr_mgmt_1             = INV_MPU6050_REG_PWR_MGMT_1,
  52.         .pwr_mgmt_2             = INV_MPU6050_REG_PWR_MGMT_2,
  53.         .int_pin_cfg            = INV_MPU6050_REG_INT_PIN_CFG,
  54.         .accl_offset            = INV_MPU6500_REG_ACCEL_OFFSET,
  55.         .gyro_offset            = INV_MPU6050_REG_GYRO_OFFSET,
  56.         .i2c_if                 = INV_ICM20602_REG_I2C_IF,
  57. };
  58. static const struct inv_mpu6050_reg_map reg_set_6500 = {
  59.         .sample_rate_div        = INV_MPU6050_REG_SAMPLE_RATE_DIV,
  60.         .lpf                    = INV_MPU6050_REG_CONFIG,
  61.         .accel_lpf              = INV_MPU6500_REG_ACCEL_CONFIG_2,
  62.         .user_ctrl              = INV_MPU6050_REG_USER_CTRL,
  63.         .fifo_en                = INV_MPU6050_REG_FIFO_EN,
  64.         .gyro_config            = INV_MPU6050_REG_GYRO_CONFIG,
  65.         .accl_config            = INV_MPU6050_REG_ACCEL_CONFIG,
  66.         .fifo_count_h           = INV_MPU6050_REG_FIFO_COUNT_H,
  67.         .fifo_r_w               = INV_MPU6050_REG_FIFO_R_W,
  68.         .raw_gyro               = INV_MPU6050_REG_RAW_GYRO,
  69.         .raw_accl               = INV_MPU6050_REG_RAW_ACCEL,
  70.         .temperature            = INV_MPU6050_REG_TEMPERATURE,
  71.         .int_enable             = INV_MPU6050_REG_INT_ENABLE,
  72.         .int_status             = INV_MPU6050_REG_INT_STATUS,
  73.         .pwr_mgmt_1             = INV_MPU6050_REG_PWR_MGMT_1,
  74.         .pwr_mgmt_2             = INV_MPU6050_REG_PWR_MGMT_2,
  75.         .int_pin_cfg                = INV_MPU6050_REG_INT_PIN_CFG,
  76.         .accl_offset                = INV_MPU6500_REG_ACCEL_OFFSET,
  77.         .gyro_offset                = INV_MPU6050_REG_GYRO_OFFSET,
  78.         .i2c_if                 = 0,
  79. };
  80. static const struct inv_mpu6050_reg_map reg_set_6050 = {
  81.         .sample_rate_div        = INV_MPU6050_REG_SAMPLE_RATE_DIV,
  82.         .lpf                    = INV_MPU6050_REG_CONFIG,
  83.         .user_ctrl              = INV_MPU6050_REG_USER_CTRL,
  84.         .fifo_en                = INV_MPU6050_REG_FIFO_EN,
  85.         .gyro_config            = INV_MPU6050_REG_GYRO_CONFIG,
  86.         .accl_config            = INV_MPU6050_REG_ACCEL_CONFIG,
  87.         .fifo_count_h           = INV_MPU6050_REG_FIFO_COUNT_H,
  88.         .fifo_r_w               = INV_MPU6050_REG_FIFO_R_W,
  89.         .raw_gyro               = INV_MPU6050_REG_RAW_GYRO,
  90.         .raw_accl               = INV_MPU6050_REG_RAW_ACCEL,
  91.         .temperature            = INV_MPU6050_REG_TEMPERATURE,
  92.         .int_enable             = INV_MPU6050_REG_INT_ENABLE,
  93.         .pwr_mgmt_1             = INV_MPU6050_REG_PWR_MGMT_1,
  94.         .pwr_mgmt_2             = INV_MPU6050_REG_PWR_MGMT_2,
  95.         .int_pin_cfg                = INV_MPU6050_REG_INT_PIN_CFG,
  96.         .accl_offset                = INV_MPU6050_REG_ACCEL_OFFSET,
  97.         .gyro_offset                = INV_MPU6050_REG_GYRO_OFFSET,
  98.         .i2c_if                 = 0,
  99. };
  100. static const struct inv_mpu6050_chip_config chip_config_6050 = {
  101.         .clk = INV_CLK_INTERNAL,
  102.         .fsr = INV_MPU6050_FSR_2000DPS,
  103.         .lpf = INV_MPU6050_FILTER_20HZ,
  104.         .divider = INV_MPU6050_FIFO_RATE_TO_DIVIDER(50),
  105.         .gyro_en = true,
  106.         .accl_en = true,
  107.         .temp_en = true,
  108.         .magn_en = false,
  109.         .gyro_fifo_enable = false,
  110.         .accl_fifo_enable = false,
  111.         .temp_fifo_enable = false,
  112.         .magn_fifo_enable = false,
  113.         .accl_fs = INV_MPU6050_FS_02G,
  114.         .user_ctrl = 0,
  115. };
  116. static const struct inv_mpu6050_chip_config chip_config_6500 = {
  117.         .clk = INV_CLK_PLL,
  118.         .fsr = INV_MPU6050_FSR_2000DPS,
  119.         .lpf = INV_MPU6050_FILTER_20HZ,
  120.         .divider = INV_MPU6050_FIFO_RATE_TO_DIVIDER(50),
  121.         .gyro_en = true,
  122.         .accl_en = true,
  123.         .temp_en = true,
  124.         .magn_en = false,
  125.         .gyro_fifo_enable = false,
  126.         .accl_fifo_enable = false,
  127.         .temp_fifo_enable = false,
  128.         .magn_fifo_enable = false,
  129.         .accl_fs = INV_MPU6050_FS_02G,
  130.         .user_ctrl = 0,
  131. };
  132. /* Indexed by enum inv_devices */
  133. static const struct inv_mpu6050_hw hw_info[] = {
  134.         {
  135.                 .whoami = INV_MPU6050_WHOAMI_VALUE,
  136.                 .name = "MPU6050",
  137.                 .reg = &reg_set_6050,
  138.                 .config = &chip_config_6050,
  139.                 .fifo_size = 1024,
  140.                 .temp = {INV_MPU6050_TEMP_OFFSET, INV_MPU6050_TEMP_SCALE},
  141.                 .startup_time = {INV_MPU6050_GYRO_STARTUP_TIME, INV_MPU6050_ACCEL_STARTUP_TIME},
  142.         },
  143.         {
  144.                 .whoami = INV_MPU6500_WHOAMI_VALUE,
  145.                 .name = "MPU6500",
  146.                 .reg = &reg_set_6500,
  147.                 .config = &chip_config_6500,
  148.                 .fifo_size = 512,
  149.                 .temp = {INV_MPU6500_TEMP_OFFSET, INV_MPU6500_TEMP_SCALE},
  150.                 .startup_time = {INV_MPU6500_GYRO_STARTUP_TIME, INV_MPU6500_ACCEL_STARTUP_TIME},
  151.         },
  152.         {
  153.                 .whoami = INV_MPU6515_WHOAMI_VALUE,
  154.                 .name = "MPU6515",
  155.                 .reg = &reg_set_6500,
  156.                 .config = &chip_config_6500,
  157.                 .fifo_size = 512,
  158.                 .temp = {INV_MPU6500_TEMP_OFFSET, INV_MPU6500_TEMP_SCALE},
  159.                 .startup_time = {INV_MPU6500_GYRO_STARTUP_TIME, INV_MPU6500_ACCEL_STARTUP_TIME},
  160.         },
  161.         {
  162.                 .whoami = INV_MPU6880_WHOAMI_VALUE,
  163.                 .name = "MPU6880",
  164.                 .reg = &reg_set_6500,
  165.                 .config = &chip_config_6500,
  166.                 .fifo_size = 4096,
  167.                 .temp = {INV_MPU6500_TEMP_OFFSET, INV_MPU6500_TEMP_SCALE},
  168.                 .startup_time = {INV_MPU6500_GYRO_STARTUP_TIME, INV_MPU6500_ACCEL_STARTUP_TIME},
  169.         },
  170.         {
  171.                 .whoami = INV_MPU6000_WHOAMI_VALUE,
  172.                 .name = "MPU6000",
  173.                 .reg = &reg_set_6050,
  174.                 .config = &chip_config_6050,
  175.                 .fifo_size = 1024,
  176.                 .temp = {INV_MPU6050_TEMP_OFFSET, INV_MPU6050_TEMP_SCALE},
  177.                 .startup_time = {INV_MPU6050_GYRO_STARTUP_TIME, INV_MPU6050_ACCEL_STARTUP_TIME},
  178.         },
  179.         {
  180.                 .whoami = INV_MPU9150_WHOAMI_VALUE,
  181.                 .name = "MPU9150",
  182.                 .reg = &reg_set_6050,
  183.                 .config = &chip_config_6050,
  184.                 .fifo_size = 1024,
  185.                 .temp = {INV_MPU6050_TEMP_OFFSET, INV_MPU6050_TEMP_SCALE},
  186.                 .startup_time = {INV_MPU6050_GYRO_STARTUP_TIME, INV_MPU6050_ACCEL_STARTUP_TIME},
  187.         },
  188.         {
  189.                 .whoami = INV_MPU9250_WHOAMI_VALUE,
  190.                 .name = "MPU9250",
  191.                 .reg = &reg_set_6500,
  192.                 .config = &chip_config_6500,
  193.                 .fifo_size = 512,
  194.                 .temp = {INV_MPU6500_TEMP_OFFSET, INV_MPU6500_TEMP_SCALE},
  195.                 .startup_time = {INV_MPU6500_GYRO_STARTUP_TIME, INV_MPU6500_ACCEL_STARTUP_TIME},
  196.         },
  197.         {
  198.                 .whoami = INV_MPU9255_WHOAMI_VALUE,
  199.                 .name = "MPU9255",
  200.                 .reg = &reg_set_6500,
  201.                 .config = &chip_config_6500,
  202.                 .fifo_size = 512,
  203.                 .temp = {INV_MPU6500_TEMP_OFFSET, INV_MPU6500_TEMP_SCALE},
  204.                 .startup_time = {INV_MPU6500_GYRO_STARTUP_TIME, INV_MPU6500_ACCEL_STARTUP_TIME},
  205.         },
  206.         {
  207.                 .whoami = INV_ICM20608_WHOAMI_VALUE,
  208.                 .name = "ICM20608",
  209.                 .reg = &reg_set_6500,
  210.                 .config = &chip_config_6500,
  211.                 .fifo_size = 512,
  212.                 .temp = {INV_ICM20608_TEMP_OFFSET, INV_ICM20608_TEMP_SCALE},
  213.                 .startup_time = {INV_MPU6500_GYRO_STARTUP_TIME, INV_MPU6500_ACCEL_STARTUP_TIME},
  214.         },
  215.         {
  216.                 .whoami = INV_ICM20608D_WHOAMI_VALUE,
  217.                 .name = "ICM20608D",
  218.                 .reg = &reg_set_6500,
  219.                 .config = &chip_config_6500,
  220.                 .fifo_size = 512,
  221.                 .temp = {INV_ICM20608_TEMP_OFFSET, INV_ICM20608_TEMP_SCALE},
  222.                 .startup_time = {INV_MPU6500_GYRO_STARTUP_TIME, INV_MPU6500_ACCEL_STARTUP_TIME},
  223.         },
  224.         {
  225.                 .whoami = INV_ICM20609_WHOAMI_VALUE,
  226.                 .name = "ICM20609",
  227.                 .reg = &reg_set_6500,
  228.                 .config = &chip_config_6500,
  229.                 .fifo_size = 4 * 1024,
  230.                 .temp = {INV_ICM20608_TEMP_OFFSET, INV_ICM20608_TEMP_SCALE},
  231.                 .startup_time = {INV_MPU6500_GYRO_STARTUP_TIME, INV_MPU6500_ACCEL_STARTUP_TIME},
  232.         },
  233.         {
  234.                 .whoami = INV_ICM20689_WHOAMI_VALUE,
  235.                 .name = "ICM20689",
  236.                 .reg = &reg_set_6500,
  237.                 .config = &chip_config_6500,
  238.                 .fifo_size = 4 * 1024,
  239.                 .temp = {INV_ICM20608_TEMP_OFFSET, INV_ICM20608_TEMP_SCALE},
  240.                 .startup_time = {INV_MPU6500_GYRO_STARTUP_TIME, INV_MPU6500_ACCEL_STARTUP_TIME},
  241.         },
  242.         {
  243.                 .whoami = INV_ICM20600_WHOAMI_VALUE,
  244.                 .name = "ICM20600",
  245.                 .reg = &reg_set_icm20602,
  246.                 .config = &chip_config_6500,
  247.                 .fifo_size = 1008,
  248.                 .temp = {INV_ICM20608_TEMP_OFFSET, INV_ICM20608_TEMP_SCALE},
  249.                 .startup_time = {INV_ICM20602_GYRO_STARTUP_TIME, INV_ICM20602_ACCEL_STARTUP_TIME},
  250.         },
  251.         {
  252.                 .whoami = INV_ICM20602_WHOAMI_VALUE,
  253.                 .name = "ICM20602",
  254.                 .reg = &reg_set_icm20602,
  255.                 .config = &chip_config_6500,
  256.                 .fifo_size = 1008,
  257.                 .temp = {INV_ICM20608_TEMP_OFFSET, INV_ICM20608_TEMP_SCALE},
  258.                 .startup_time = {INV_ICM20602_GYRO_STARTUP_TIME, INV_ICM20602_ACCEL_STARTUP_TIME},
  259.         },
  260.         {
  261.                 .whoami = INV_ICM20690_WHOAMI_VALUE,
  262.                 .name = "ICM20690",
  263.                 .reg = &reg_set_6500,
  264.                 .config = &chip_config_6500,
  265.                 .fifo_size = 1024,
  266.                 .temp = {INV_ICM20608_TEMP_OFFSET, INV_ICM20608_TEMP_SCALE},
  267.                 .startup_time = {INV_ICM20690_GYRO_STARTUP_TIME, INV_ICM20690_ACCEL_STARTUP_TIME},
  268.         },
  269.         {
  270.                 .whoami = INV_IAM20680_WHOAMI_VALUE,
  271.                 .name = "IAM20680",
  272.                 .reg = &reg_set_6500,
  273.                 .config = &chip_config_6500,
  274.                 .fifo_size = 512,
  275.                 .temp = {INV_ICM20608_TEMP_OFFSET, INV_ICM20608_TEMP_SCALE},
  276.                 .startup_time = {INV_MPU6500_GYRO_STARTUP_TIME, INV_MPU6500_ACCEL_STARTUP_TIME},
  277.         },
  278. };
  279. static int inv_mpu6050_pwr_mgmt_1_write(struct inv_mpu6050_state *st, bool sleep,
  280.                                         bool cycle, int clock, int temp_dis)
  281. {
  282.         u8 val;
  283.         if (clock < 0)
  284.                 clock = st->chip_config.clk;
  285.         if (temp_dis < 0)
  286.                 temp_dis = !st->chip_config.temp_en;
  287.         val = clock & INV_MPU6050_BIT_CLK_MASK;
  288.         if (temp_dis)
  289.                 val |= INV_MPU6050_BIT_TEMP_DIS;
  290.         if (sleep)
  291.                 val |= INV_MPU6050_BIT_SLEEP;
  292.         if (cycle)
  293.                 val |= INV_MPU6050_BIT_CYCLE;
  294.         dev_dbg(regmap_get_device(st->map), "pwr_mgmt_1: 0x%x\n", val);
  295.         return regmap_write(st->map, st->reg->pwr_mgmt_1, val);
  296. }
  297. static int inv_mpu6050_clock_switch(struct inv_mpu6050_state *st,
  298.                                     unsigned int clock)
  299. {
  300.         int ret;
  301.         switch (st->chip_type) {
  302.         case INV_MPU6050:
  303.         case INV_MPU6000:
  304.         case INV_MPU9150:
  305.                 /* old chips: switch clock manually */
  306.                 ret = inv_mpu6050_pwr_mgmt_1_write(st, false, false, clock, -1);
  307.                 if (ret)
  308.                         return ret;
  309.                 st->chip_config.clk = clock;
  310.                 break;
  311.         default:
  312.                 /* automatic clock switching, nothing to do */
  313.                 break;
  314.         }
  315.         return 0;
  316. }
  317. int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en,
  318.                               unsigned int mask)
  319. {
  320.         unsigned int sleep, val;
  321.         u8 pwr_mgmt2, user_ctrl;
  322.         int ret;
  323.         /* delete useless requests */
  324.         if (mask & INV_MPU6050_SENSOR_ACCL && en == st->chip_config.accl_en)
  325.                 mask &= ~INV_MPU6050_SENSOR_ACCL;
  326.         if (mask & INV_MPU6050_SENSOR_GYRO && en == st->chip_config.gyro_en)
  327.                 mask &= ~INV_MPU6050_SENSOR_GYRO;
  328.         if (mask & INV_MPU6050_SENSOR_TEMP && en == st->chip_config.temp_en)
  329.                 mask &= ~INV_MPU6050_SENSOR_TEMP;
  330.         if (mask & INV_MPU6050_SENSOR_MAGN && en == st->chip_config.magn_en)
  331.                 mask &= ~INV_MPU6050_SENSOR_MAGN;
  332.         if (mask & INV_MPU6050_SENSOR_WOM && en == st->chip_config.wom_en)
  333.                 mask &= ~INV_MPU6050_SENSOR_WOM;
  334.         /* force accel on if WoM is on and not going off */
  335.         if (!en && (mask & INV_MPU6050_SENSOR_ACCL) && st->chip_config.wom_en &&
  336.                         !(mask & INV_MPU6050_SENSOR_WOM))
  337.                 mask &= ~INV_MPU6050_SENSOR_ACCL;
  338.         if (mask == 0)
  339.                 return 0;
  340.         /* turn on/off temperature sensor */
  341.         if (mask & INV_MPU6050_SENSOR_TEMP) {
  342.                 ret = inv_mpu6050_pwr_mgmt_1_write(st, false, false, -1, !en);
  343.                 if (ret)
  344.                         return ret;
  345.                 st->chip_config.temp_en = en;
  346.         }
  347.         /* update user_crtl for driving magnetometer */
  348.         if (mask & INV_MPU6050_SENSOR_MAGN) {
  349.                 user_ctrl = st->chip_config.user_ctrl;
  350.                 if (en)
  351.                         user_ctrl |= INV_MPU6050_BIT_I2C_MST_EN;
  352.                 else
  353.                         user_ctrl &= ~INV_MPU6050_BIT_I2C_MST_EN;
  354.                 ret = regmap_write(st->map, st->reg->user_ctrl, user_ctrl);
  355.                 if (ret)
  356.                         return ret;
  357.                 st->chip_config.user_ctrl = user_ctrl;
  358.                 st->chip_config.magn_en = en;
  359.         }
  360.         /* manage accel & gyro engines */
  361.         if (mask & (INV_MPU6050_SENSOR_ACCL | INV_MPU6050_SENSOR_GYRO)) {
  362.                 /* compute power management 2 current value */
  363.                 pwr_mgmt2 = 0;
  364.                 if (!st->chip_config.accl_en)
  365.                         pwr_mgmt2 |= INV_MPU6050_BIT_PWR_ACCL_STBY;
  366.                 if (!st->chip_config.gyro_en)
  367.                         pwr_mgmt2 |= INV_MPU6050_BIT_PWR_GYRO_STBY;
  368.                 /* update to new requested value */
  369.                 if (mask & INV_MPU6050_SENSOR_ACCL) {
  370.                         if (en)
  371.                                 pwr_mgmt2 &= ~INV_MPU6050_BIT_PWR_ACCL_STBY;
  372.                         else
  373.                                 pwr_mgmt2 |= INV_MPU6050_BIT_PWR_ACCL_STBY;
  374.                 }
  375.                 if (mask & INV_MPU6050_SENSOR_GYRO) {
  376.                         if (en)
  377.                                 pwr_mgmt2 &= ~INV_MPU6050_BIT_PWR_GYRO_STBY;
  378.                         else
  379.                                 pwr_mgmt2 |= INV_MPU6050_BIT_PWR_GYRO_STBY;
  380.                 }
  381.                 /* switch clock to internal when turning gyro off */
  382.                 if (mask & INV_MPU6050_SENSOR_GYRO && !en) {
  383.                         ret = inv_mpu6050_clock_switch(st, INV_CLK_INTERNAL);
  384.                         if (ret)
  385.                                 return ret;
  386.                 }
  387.                 /* update sensors engine */
  388.                 dev_dbg(regmap_get_device(st->map), "pwr_mgmt_2: 0x%x\n",
  389.                         pwr_mgmt2);
  390.                 ret = regmap_write(st->map, st->reg->pwr_mgmt_2, pwr_mgmt2);
  391.                 if (ret)
  392.                         return ret;
  393.                 if (mask & INV_MPU6050_SENSOR_ACCL)
  394.                         st->chip_config.accl_en = en;
  395.                 if (mask & INV_MPU6050_SENSOR_GYRO)
  396.                         st->chip_config.gyro_en = en;
  397.                 /* compute required time to have sensors stabilized */
  398.                 sleep = 0;
  399.                 if (en) {
  400.                         if (mask & INV_MPU6050_SENSOR_ACCL) {
  401.                                 if (sleep < st->hw->startup_time.accel)
  402.                                         sleep = st->hw->startup_time.accel;
  403.                         }
  404.                         if (mask & INV_MPU6050_SENSOR_GYRO) {
  405.                                 if (sleep < st->hw->startup_time.gyro)
  406.                                         sleep = st->hw->startup_time.gyro;
  407.                         }
  408.                 } else {
  409.                         if (mask & INV_MPU6050_SENSOR_GYRO) {
  410.                                 if (sleep < INV_MPU6050_GYRO_DOWN_TIME)
  411.                                         sleep = INV_MPU6050_GYRO_DOWN_TIME;
  412.                         }
  413.                 }
  414.                 if (sleep)
  415.                         msleep(sleep);
  416.                 /* switch clock to PLL when turning gyro on */
  417.                 if (mask & INV_MPU6050_SENSOR_GYRO && en) {
  418.                         ret = inv_mpu6050_clock_switch(st, INV_CLK_PLL);
  419.                         if (ret)
  420.                                 return ret;
  421.                 }
  422.         }
  423.         /* enable/disable accel intelligence control */
  424.         if (mask & INV_MPU6050_SENSOR_WOM) {
  425.                 val = en ? INV_MPU6500_BIT_ACCEL_INTEL_EN |
  426.                            INV_MPU6500_BIT_ACCEL_INTEL_MODE : 0;
  427.                 ret = regmap_write(st->map, INV_MPU6500_REG_ACCEL_INTEL_CTRL, val);
  428.                 if (ret)
  429.                         return ret;
  430.                 st->chip_config.wom_en = en;
  431.         }
  432.         return 0;
  433. }
  434. static int inv_mpu6050_set_power_itg(struct inv_mpu6050_state *st,
  435.                                      bool power_on)
  436. {
  437.         int result;
  438.         result = inv_mpu6050_pwr_mgmt_1_write(st, !power_on, false, -1, -1);
  439.         if (result)
  440.                 return result;
  441.         if (power_on)
  442.                 usleep_range(INV_MPU6050_REG_UP_TIME_MIN,
  443.                              INV_MPU6050_REG_UP_TIME_MAX);
  444.         return 0;
  445. }
  446. static int inv_mpu6050_set_gyro_fsr(struct inv_mpu6050_state *st,
  447.                                     enum inv_mpu6050_fsr_e val)
  448. {
  449.         unsigned int gyro_shift;
  450.         u8 data;
  451.         switch (st->chip_type) {
  452.         case INV_ICM20690:
  453.                 gyro_shift = INV_ICM20690_GYRO_CONFIG_FSR_SHIFT;
  454.                 break;
  455.         default:
  456.                 gyro_shift = INV_MPU6050_GYRO_CONFIG_FSR_SHIFT;
  457.                 break;
  458.         }
  459.         data = val << gyro_shift;
  460.         return regmap_write(st->map, st->reg->gyro_config, data);
  461. }
  462. static int inv_mpu6050_set_accel_lpf_regs(struct inv_mpu6050_state *st,
  463.                                           enum inv_mpu6050_filter_e val)
  464. {
  465.         switch (st->chip_type) {
  466.         case INV_MPU6050:
  467.         case INV_MPU6000:
  468.         case INV_MPU9150:
  469.                 /* old chips, nothing to do */
  470.                 return 0;
  471.         case INV_ICM20689:
  472.         case INV_ICM20690:
  473.                 /* set FIFO size to maximum value */
  474.                 val |= INV_ICM20689_BITS_FIFO_SIZE_MAX;
  475.                 break;
  476.         default:
  477.                 break;
  478.         }
  479.         return regmap_write(st->map, st->reg->accel_lpf, val);
  480. }
  481. /*
  482. *  inv_mpu6050_set_lpf_regs() - set low pass filter registers, chip dependent
  483. *
  484. *  MPU60xx/MPU9150 use only 1 register for accelerometer + gyroscope
  485. *  MPU6500 and above have a dedicated register for accelerometer
  486. */
  487. static int inv_mpu6050_set_lpf_regs(struct inv_mpu6050_state *st,
  488.                                     enum inv_mpu6050_filter_e val)
  489. {
  490.         int result;
  491.         result = regmap_write(st->map, st->reg->lpf, val);
  492.         if (result)
  493.                 return result;
  494.         /* set accel lpf */
  495.         return inv_mpu6050_set_accel_lpf_regs(st, val);
  496. }
  497. /*
  498. *  inv_mpu6050_init_config() - Initialize hardware, disable FIFO.
  499. *
  500. *  Initial configuration:
  501. *  FSR: ± 2000DPS
  502. *  DLPF: 20Hz
  503. *  FIFO rate: 50Hz
  504. *  Clock source: Gyro PLL
  505. */
  506. static int inv_mpu6050_init_config(struct iio_dev *indio_dev)
  507. {
  508.         int result;
  509.         u8 d;
  510.         struct inv_mpu6050_state *st = iio_priv(indio_dev);
  511.         struct inv_sensors_timestamp_chip timestamp;
  512.         result = inv_mpu6050_set_gyro_fsr(st, st->chip_config.fsr);
  513.         if (result)
  514.                 return result;
  515.         result = inv_mpu6050_set_lpf_regs(st, st->chip_config.lpf);
  516.         if (result)
  517.                 return result;
  518.         d = st->chip_config.divider;
  519.         result = regmap_write(st->map, st->reg->sample_rate_div, d);
  520.         if (result)
  521.                 return result;
  522.         d = (st->chip_config.accl_fs << INV_MPU6050_ACCL_CONFIG_FSR_SHIFT);
  523.         result = regmap_write(st->map, st->reg->accl_config, d);
  524.         if (result)
  525.                 return result;
  526.         result = regmap_write(st->map, st->reg->int_pin_cfg, st->irq_mask);
  527.         if (result)
  528.                 return result;
  529.         /* clock jitter is +/- 2% */
  530.         timestamp.clock_period = NSEC_PER_SEC / INV_MPU6050_INTERNAL_FREQ_HZ;
  531.         timestamp.jitter = 20;
  532.         timestamp.init_period =
  533.                         NSEC_PER_SEC / INV_MPU6050_DIVIDER_TO_FIFO_RATE(st->chip_config.divider);
  534.         inv_sensors_timestamp_init(&st->timestamp, &timestamp);
  535.         /* magn chip init, noop if not present in the chip */
  536.         result = inv_mpu_magn_probe(st);
  537.         if (result)
  538.                 return result;
  539.         return 0;
  540. }
  541. static int inv_mpu6050_sensor_set(struct inv_mpu6050_state  *st, int reg,
  542.                                 int axis, int val)
  543. {
  544.         int ind;
  545.         __be16 d = cpu_to_be16(val);
  546.         ind = (axis - IIO_MOD_X) * 2;
  547.         return regmap_bulk_write(st->map, reg + ind, &d, sizeof(d));
  548. }
  549. static int inv_mpu6050_sensor_show(struct inv_mpu6050_state  *st, int reg,
  550.                                    int axis, int *val)
  551. {
  552.         int ind, result;
  553.         __be16 d;
  554.         ind = (axis - IIO_MOD_X) * 2;
  555.         result = regmap_bulk_read(st->map, reg + ind, &d, sizeof(d));
  556.         if (result)
  557.                 return result;
  558.         *val = (short)be16_to_cpup(&d);
  559.         return IIO_VAL_INT;
  560. }
  561. static int inv_mpu6050_read_channel_data(struct iio_dev *indio_dev,
  562.                                          struct iio_chan_spec const *chan,
  563.                                          int *val)
  564. {
  565.         struct inv_mpu6050_state *st = iio_priv(indio_dev);
  566.         struct device *pdev = regmap_get_device(st->map);
  567.         unsigned int freq_hz, period_us, min_sleep_us, max_sleep_us;
  568.         int result;
  569.         int ret;
  570.         /* compute sample period */
  571.         freq_hz = INV_MPU6050_DIVIDER_TO_FIFO_RATE(st->chip_config.divider);
  572.         period_us = 1000000 / freq_hz;
  573.         result = pm_runtime_resume_and_get(pdev);
  574.         if (result)
  575.                 return result;
  576.         switch (chan->type) {
  577.         case IIO_ANGL_VEL:
  578.                 if (!st->chip_config.gyro_en) {
  579.                         result = inv_mpu6050_switch_engine(st, true,
  580.                                         INV_MPU6050_SENSOR_GYRO);
  581.                         if (result)
  582.                                 goto error_power_off;
  583.                         /* need to wait 2 periods to have first valid sample */
  584.                         min_sleep_us = 2 * period_us;
  585.                         max_sleep_us = 2 * (period_us + period_us / 2);
  586.                         usleep_range(min_sleep_us, max_sleep_us);
  587.                 }
  588.                 ret = inv_mpu6050_sensor_show(st, st->reg->raw_gyro,
  589.                                               chan->channel2, val);
  590.                 break;
  591.         case IIO_ACCEL:
  592.                 if (!st->chip_config.accl_en) {
  593.                         result = inv_mpu6050_switch_engine(st, true,
  594.                                         INV_MPU6050_SENSOR_ACCL);
  595.                         if (result)
  596.                                 goto error_power_off;
  597.                         /* wait 1 period for first sample availability */
  598.                         min_sleep_us = period_us;
  599.                         max_sleep_us = period_us + period_us / 2;
  600.                         usleep_range(min_sleep_us, max_sleep_us);
  601.                 }
  602.                 ret = inv_mpu6050_sensor_show(st, st->reg->raw_accl,
  603.                                               chan->channel2, val);
  604.                 break;
  605.         case IIO_TEMP:
  606.                 /* temperature sensor work only with accel and/or gyro */
  607.                 if (!st->chip_config.accl_en && !st->chip_config.gyro_en) {
  608.                         result = -EBUSY;
  609.                         goto error_power_off;
  610.                 }
  611.                 if (!st->chip_config.temp_en) {
  612.                         result = inv_mpu6050_switch_engine(st, true,
  613.                                         INV_MPU6050_SENSOR_TEMP);
  614.                         if (result)
  615.                                 goto error_power_off;
  616.                         /* wait 1 period for first sample availability */
  617.                         min_sleep_us = period_us;
  618.                         max_sleep_us = period_us + period_us / 2;
  619.                         usleep_range(min_sleep_us, max_sleep_us);
  620.                 }
  621.                 ret = inv_mpu6050_sensor_show(st, st->reg->temperature,
  622.                                               IIO_MOD_X, val);
  623.                 break;
  624.         case IIO_MAGN:
  625.                 if (!st->chip_config.magn_en) {
  626.                         result = inv_mpu6050_switch_engine(st, true,
  627.                                         INV_MPU6050_SENSOR_MAGN);
  628.                         if (result)
  629.                                 goto error_power_off;
  630.                         /* frequency is limited for magnetometer */
  631.                         if (freq_hz > INV_MPU_MAGN_FREQ_HZ_MAX) {
  632.                                 freq_hz = INV_MPU_MAGN_FREQ_HZ_MAX;
  633.                                 period_us = 1000000 / freq_hz;
  634.                         }
  635.                         /* need to wait 2 periods to have first valid sample */
  636.                         min_sleep_us = 2 * period_us;
  637.                         max_sleep_us = 2 * (period_us + period_us / 2);
  638.                         usleep_range(min_sleep_us, max_sleep_us);
  639.                 }
  640.                 ret = inv_mpu_magn_read(st, chan->channel2, val);
  641.                 break;
  642.         default:
  643.                 ret = -EINVAL;
  644.                 break;
  645.         }
  646.         pm_runtime_mark_last_busy(pdev);
  647.         pm_runtime_put_autosuspend(pdev);
  648.         return ret;
  649. error_power_off:
  650.         pm_runtime_put_autosuspend(pdev);
  651.         return result;
  652. }
  653. static int
  654. inv_mpu6050_read_raw(struct iio_dev *indio_dev,
  655.                      struct iio_chan_spec const *chan,
  656.                      int *val, int *val2, long mask)
  657. {
  658.         struct inv_mpu6050_state  *st = iio_priv(indio_dev);
  659.         int ret = 0;
  660.         switch (mask) {
  661.         case IIO_CHAN_INFO_RAW:
  662.                 ret = iio_device_claim_direct_mode(indio_dev);
  663.                 if (ret)
  664.                         return ret;
  665.                 mutex_lock(&st->lock);
  666.                 ret = inv_mpu6050_read_channel_data(indio_dev, chan, val);
  667.                 mutex_unlock(&st->lock);
  668.                 iio_device_release_direct_mode(indio_dev);
  669.                 return ret;
  670.         case IIO_CHAN_INFO_SCALE:
  671.                 switch (chan->type) {
  672.                 case IIO_ANGL_VEL:
  673.                         mutex_lock(&st->lock);
  674.                         *val  = 0;
  675.                         *val2 = gyro_scale_6050[st->chip_config.fsr];
  676.                         mutex_unlock(&st->lock);
  677.                         return IIO_VAL_INT_PLUS_NANO;
  678.                 case IIO_ACCEL:
  679.                         mutex_lock(&st->lock);
  680.                         *val = 0;
  681.                         *val2 = accel_scale[st->chip_config.accl_fs];
  682.                         mutex_unlock(&st->lock);
  683.                         return IIO_VAL_INT_PLUS_MICRO;
  684.                 case IIO_TEMP:
  685.                         *val = st->hw->temp.scale / 1000000;
  686.                         *val2 = st->hw->temp.scale % 1000000;
  687.                         return IIO_VAL_INT_PLUS_MICRO;
  688.                 case IIO_MAGN:
  689.                         return inv_mpu_magn_get_scale(st, chan, val, val2);
  690.                 default:
  691.                         return -EINVAL;
  692.                 }
  693.         case IIO_CHAN_INFO_OFFSET:
  694.                 switch (chan->type) {
  695.                 case IIO_TEMP:
  696.                         *val = st->hw->temp.offset;
  697.                         return IIO_VAL_INT;
  698.                 default:
  699.                         return -EINVAL;
  700.                 }
  701.         case IIO_CHAN_INFO_CALIBBIAS:
  702.                 switch (chan->type) {
  703.                 case IIO_ANGL_VEL:
  704.                         mutex_lock(&st->lock);
  705.                         ret = inv_mpu6050_sensor_show(st, st->reg->gyro_offset,
  706.                                                 chan->channel2, val);
  707.                         mutex_unlock(&st->lock);
  708.                         return ret;
  709.                 case IIO_ACCEL:
  710.                         mutex_lock(&st->lock);
  711.                         ret = inv_mpu6050_sensor_show(st, st->reg->accl_offset,
  712.                                                 chan->channel2, val);
  713.                         mutex_unlock(&st->lock);
  714.                         return ret;
  715.                 default:
  716.                         return -EINVAL;
  717.                 }
  718.         default:
  719.                 return -EINVAL;
  720.         }
  721. }
  722. static int inv_mpu6050_write_gyro_scale(struct inv_mpu6050_state *st, int val,
  723.                                         int val2)
  724. {
  725.         int result, i;
  726.         if (val != 0)
  727.                 return -EINVAL;
  728.         for (i = 0; i < ARRAY_SIZE(gyro_scale_6050); ++i) {
  729.                 if (gyro_scale_6050[i] == val2) {
  730.                         result = inv_mpu6050_set_gyro_fsr(st, i);
  731.                         if (result)
  732.                                 return result;
  733.                         st->chip_config.fsr = i;
  734.                         return 0;
  735.                 }
  736.         }
  737.         return -EINVAL;
  738. }
  739. static int inv_write_raw_get_fmt(struct iio_dev *indio_dev,
  740.                                  struct iio_chan_spec const *chan, long mask)
  741. {
  742.         switch (mask) {
  743.         case IIO_CHAN_INFO_SCALE:
  744.                 switch (chan->type) {
  745.                 case IIO_ANGL_VEL:
  746.                         return IIO_VAL_INT_PLUS_NANO;
  747.                 default:
  748.                         return IIO_VAL_INT_PLUS_MICRO;
  749.                 }
  750.         default:
  751.                 return IIO_VAL_INT_PLUS_MICRO;
  752.         }
  753.         return -EINVAL;
  754. }
  755. static int inv_mpu6050_write_accel_scale(struct inv_mpu6050_state *st, int val,
  756.                                          int val2)
  757. {
  758.         int result, i;
  759.         u8 d;
  760.         if (val != 0)
  761.                 return -EINVAL;
  762.         for (i = 0; i < ARRAY_SIZE(accel_scale); ++i) {
  763.                 if (accel_scale[i] == val2) {
  764.                         d = (i << INV_MPU6050_ACCL_CONFIG_FSR_SHIFT);
  765.                         result = regmap_write(st->map, st->reg->accl_config, d);
  766.                         if (result)
  767.                                 return result;
  768.                         st->chip_config.accl_fs = i;
  769.                         return 0;
  770.                 }
  771.         }
  772.         return -EINVAL;
  773. }
  774. static int inv_mpu6050_write_raw(struct iio_dev *indio_dev,
  775.                                  struct iio_chan_spec const *chan,
  776.                                  int val, int val2, long mask)
  777. {
  778.         struct inv_mpu6050_state  *st = iio_priv(indio_dev);
  779.         struct device *pdev = regmap_get_device(st->map);
  780.         int result;
  781.         /*
  782.          * we should only update scale when the chip is disabled, i.e.
  783.          * not running
  784.          */
  785.         result = iio_device_claim_direct_mode(indio_dev);
  786.         if (result)
  787.                 return result;
  788.         mutex_lock(&st->lock);
  789.         result = pm_runtime_resume_and_get(pdev);
  790.         if (result)
  791.                 goto error_write_raw_unlock;
  792.         switch (mask) {
  793.         case IIO_CHAN_INFO_SCALE:
  794.                 switch (chan->type) {
  795.                 case IIO_ANGL_VEL:
  796.                         result = inv_mpu6050_write_gyro_scale(st, val, val2);
  797.                         break;
  798.                 case IIO_ACCEL:
  799.                         result = inv_mpu6050_write_accel_scale(st, val, val2);
  800.                         break;
  801.                 default:
  802.                         result = -EINVAL;
  803.                         break;
  804.                 }
  805.                 break;
  806.         case IIO_CHAN_INFO_CALIBBIAS:
  807.                 switch (chan->type) {
  808.                 case IIO_ANGL_VEL:
  809.                         result = inv_mpu6050_sensor_set(st,
  810.                                                         st->reg->gyro_offset,
  811.                                                         chan->channel2, val);
  812.                         break;
  813.                 case IIO_ACCEL:
  814.                         result = inv_mpu6050_sensor_set(st,
  815.                                                         st->reg->accl_offset,
  816.                                                         chan->channel2, val);
  817.                         break;
  818.                 default:
  819.                         result = -EINVAL;
  820.                         break;
  821.                 }
  822.                 break;
  823.         default:
  824.                 result = -EINVAL;
  825.                 break;
  826.         }
  827.         pm_runtime_mark_last_busy(pdev);
  828.         pm_runtime_put_autosuspend(pdev);
  829. error_write_raw_unlock:
  830.         mutex_unlock(&st->lock);
  831.         iio_device_release_direct_mode(indio_dev);
  832.         return result;
  833. }
  834. static u64 inv_mpu6050_convert_wom_to_roc(unsigned int threshold, unsigned int freq_div)
  835. {
  836.         /* 4mg per LSB converted in m/s² in micro (1000000) */
  837.         const unsigned int convert = 4U * 9807U;
  838.         u64 value;
  839.         value = threshold * convert;
  840.         /* compute the differential by multiplying by the frequency */
  841.         return div_u64(value * INV_MPU6050_INTERNAL_FREQ_HZ, freq_div);
  842. }
  843. static unsigned int inv_mpu6050_convert_roc_to_wom(u64 roc, unsigned int freq_div)
  844. {
  845.         /* 4mg per LSB converted in m/s² in micro (1000000) */
  846.         const unsigned int convert = 4U * 9807U;
  847.         u64 value;
  848.         /* return 0 only if roc is 0 */
  849.         if (roc == 0)
  850.                 return 0;
  851.         value = div_u64(roc * freq_div, convert * INV_MPU6050_INTERNAL_FREQ_HZ);
  852.         /* limit value to 8 bits and prevent 0 */
  853.         return min(255, max(1, value));
  854. }
  855. static int inv_mpu6050_set_wom_int(struct inv_mpu6050_state *st, bool on)
  856. {
  857.         unsigned int reg_val, val;
  858.         switch (st->chip_type) {
  859.         case INV_MPU6050:
  860.         case INV_MPU6500:
  861.         case INV_MPU6515:
  862.         case INV_MPU6880:
  863.         case INV_MPU6000:
  864.         case INV_MPU9150:
  865.         case INV_MPU9250:
  866.         case INV_MPU9255:
  867.                 reg_val = INV_MPU6500_BIT_WOM_INT_EN;
  868.                 break;
  869.         default:
  870.                 reg_val = INV_ICM20608_BIT_WOM_INT_EN;
  871.                 break;
  872.         }
  873.         val = on ? reg_val : 0;
  874.         return regmap_update_bits(st->map, st->reg->int_enable, reg_val, val);
  875. }
  876. static int inv_mpu6050_set_wom_threshold(struct inv_mpu6050_state *st, u64 value,
  877.                                          unsigned int freq_div)
  878. {
  879.         unsigned int threshold;
  880.         int result;
  881.         /* convert roc to wom threshold and convert back to handle clipping */
  882.         threshold = inv_mpu6050_convert_roc_to_wom(value, freq_div);
  883.         value = inv_mpu6050_convert_wom_to_roc(threshold, freq_div);
  884.         dev_dbg(regmap_get_device(st->map), "wom_threshold: 0x%x\n", threshold);
  885.         switch (st->chip_type) {
  886.         case INV_ICM20609:
  887.         case INV_ICM20689:
  888.         case INV_ICM20600:
  889.         case INV_ICM20602:
  890.         case INV_ICM20690:
  891.                 st->data[0] = threshold;
  892.                 st->data[1] = threshold;
  893.                 st->data[2] = threshold;
  894.                 result = regmap_bulk_write(st->map, INV_ICM20609_REG_ACCEL_WOM_X_THR,
  895.                                            st->data, 3);
  896.                 break;
  897.         default:
  898.                 result = regmap_write(st->map, INV_MPU6500_REG_WOM_THRESHOLD, threshold);
  899.                 break;
  900.         }
  901.         if (result)
  902.                 return result;
  903.         st->chip_config.roc_threshold = value;
  904.         return 0;
  905. }
  906. static int inv_mpu6050_set_lp_odr(struct inv_mpu6050_state *st, unsigned int freq_div,
  907.                                   unsigned int *lp_div)
  908. {
  909.         static const unsigned int freq_dividers[] = {2, 4, 8, 16, 32, 64, 128, 256};
  910.         static const unsigned int reg_values[] = {
  911.                 INV_MPU6050_LPOSC_500HZ, INV_MPU6050_LPOSC_250HZ,
  912.                 INV_MPU6050_LPOSC_125HZ, INV_MPU6050_LPOSC_62HZ,
  913.                 INV_MPU6050_LPOSC_31HZ, INV_MPU6050_LPOSC_16HZ,
  914.                 INV_MPU6050_LPOSC_8HZ, INV_MPU6050_LPOSC_4HZ,
  915.         };
  916.         unsigned int val, i;
  917.         switch (st->chip_type) {
  918.         case INV_ICM20609:
  919.         case INV_ICM20689:
  920.         case INV_ICM20600:
  921.         case INV_ICM20602:
  922.         case INV_ICM20690:
  923.                 /* nothing to do */
  924.                 *lp_div = INV_MPU6050_FREQ_DIVIDER(st);
  925.                 return 0;
  926.         default:
  927.                 break;
  928.         }
  929.         /* found the nearest superior frequency divider */
  930.         i = ARRAY_SIZE(reg_values) - 1;
  931.         val = reg_values[i];
  932.         *lp_div = freq_dividers[i];
  933.         for (i = 0; i < ARRAY_SIZE(freq_dividers); ++i) {
  934.                 if (freq_div <= freq_dividers[i]) {
  935.                         val = reg_values[i];
  936.                         *lp_div = freq_dividers[i];
  937.                         break;
  938.                 }
  939.         }
  940.         dev_dbg(regmap_get_device(st->map), "lp_odr: 0x%x\n", val);
  941.         return regmap_write(st->map, INV_MPU6500_REG_LP_ODR, val);
  942. }
  943. static int inv_mpu6050_set_wom_lp(struct inv_mpu6050_state *st, bool on)
  944. {
  945.         unsigned int lp_div;
  946.         int result;
  947.         if (on) {
  948.                 /* set low power ODR */
  949.                 result = inv_mpu6050_set_lp_odr(st, INV_MPU6050_FREQ_DIVIDER(st), &lp_div);
  950.                 if (result)
  951.                         return result;
  952.                 /* disable accel low pass filter */
  953.                 result = inv_mpu6050_set_accel_lpf_regs(st, INV_MPU6050_FILTER_NOLPF);
  954.                 if (result)
  955.                         return result;
  956.                 /* update wom threshold with new low-power frequency divider */
  957.                 result = inv_mpu6050_set_wom_threshold(st, st->chip_config.roc_threshold, lp_div);
  958.                 if (result)
  959.                         return result;
  960.                 /* set cycle mode */
  961.                 result = inv_mpu6050_pwr_mgmt_1_write(st, false, true, -1, -1);
  962.         } else {
  963.                 /* disable cycle mode */
  964.                 result = inv_mpu6050_pwr_mgmt_1_write(st, false, false, -1, -1);
  965.                 if (result)
  966.                         return result;
  967.                 /* restore wom threshold */
  968.                 result = inv_mpu6050_set_wom_threshold(st, st->chip_config.roc_threshold,
  969.                                                        INV_MPU6050_FREQ_DIVIDER(st));
  970.                 if (result)
  971.                         return result;
  972.                 /* restore accel low pass filter */
  973.                 result = inv_mpu6050_set_accel_lpf_regs(st, st->chip_config.lpf);
  974.         }
  975.         return result;
  976. }
  977. static int inv_mpu6050_enable_wom(struct inv_mpu6050_state *st, bool en)
  978. {
  979.         struct device *pdev = regmap_get_device(st->map);
  980.         unsigned int mask;
  981.         int result;
  982.         if (en) {
  983.                 result = pm_runtime_resume_and_get(pdev);
  984.                 if (result)
  985.                         return result;
  986.                 mask = INV_MPU6050_SENSOR_ACCL | INV_MPU6050_SENSOR_WOM;
  987.                 result = inv_mpu6050_switch_engine(st, true, mask);
  988.                 if (result)
  989.                         goto error_suspend;
  990.                 result = inv_mpu6050_set_wom_int(st, true);
  991.                 if (result)
  992.                         goto error_suspend;
  993.         } else {
  994.                 result = inv_mpu6050_set_wom_int(st, false);
  995.                 if (result)
  996.                         dev_err(pdev, "error %d disabling WoM interrupt bit", result);
  997.                 /* disable only WoM and let accel be disabled by autosuspend */
  998.                 result = inv_mpu6050_switch_engine(st, false, INV_MPU6050_SENSOR_WOM);
  999.                 if (result) {
  1000.                         dev_err(pdev, "error %d disabling WoM force off", result);
  1001.                         /* force WoM off */
  1002.                         st->chip_config.wom_en = false;
  1003.                 }
  1004.                 pm_runtime_mark_last_busy(pdev);
  1005.                 pm_runtime_put_autosuspend(pdev);
  1006.         }
  1007.         return result;
  1008. error_suspend:
  1009.         pm_runtime_mark_last_busy(pdev);
  1010.         pm_runtime_put_autosuspend(pdev);
  1011.         return result;
  1012. }
  1013. static int inv_mpu6050_read_event_config(struct iio_dev *indio_dev,
  1014.                                          const struct iio_chan_spec *chan,
  1015.                                          enum iio_event_type type,
  1016.                                          enum iio_event_direction dir)
  1017. {
  1018.         struct inv_mpu6050_state *st = iio_priv(indio_dev);
  1019.         /* support only WoM (accel roc rising) event */
  1020.         if (chan->type != IIO_ACCEL || type != IIO_EV_TYPE_ROC ||
  1021.             dir != IIO_EV_DIR_RISING)
  1022.                 return -EINVAL;
  1023.         guard(mutex)(&st->lock);
  1024.         return st->chip_config.wom_en ? 1 : 0;
  1025. }
  1026. static int inv_mpu6050_write_event_config(struct iio_dev *indio_dev,
  1027.                                           const struct iio_chan_spec *chan,
  1028.                                           enum iio_event_type type,
  1029.                                           enum iio_event_direction dir,
  1030.                                           int state)
  1031. {
  1032.         struct inv_mpu6050_state *st = iio_priv(indio_dev);
  1033.         int enable;
  1034.         /* support only WoM (accel roc rising) event */
  1035.         if (chan->type != IIO_ACCEL || type != IIO_EV_TYPE_ROC ||
  1036.             dir != IIO_EV_DIR_RISING)
  1037.                 return -EINVAL;
  1038.         enable = !!state;
  1039.         guard(mutex)(&st->lock);
  1040.         if (st->chip_config.wom_en == enable)
  1041.                 return 0;
  1042.         return inv_mpu6050_enable_wom(st, enable);
  1043. }
  1044. static int inv_mpu6050_read_event_value(struct iio_dev *indio_dev,
  1045.                                         const struct iio_chan_spec *chan,
  1046.                                         enum iio_event_type type,
  1047.                                         enum iio_event_direction dir,
  1048.                                         enum iio_event_info info,
  1049.                                         int *val, int *val2)
  1050. {
  1051.         struct inv_mpu6050_state *st = iio_priv(indio_dev);
  1052.         u32 rem;
  1053.         /* support only WoM (accel roc rising) event value */
  1054.         if (chan->type != IIO_ACCEL || type != IIO_EV_TYPE_ROC ||
  1055.             dir != IIO_EV_DIR_RISING || info != IIO_EV_INFO_VALUE)
  1056.                 return -EINVAL;
  1057.         guard(mutex)(&st->lock);
  1058.         /* return value in micro */
  1059.         *val = div_u64_rem(st->chip_config.roc_threshold, 1000000U, &rem);
  1060.         *val2 = rem;
  1061.         return IIO_VAL_INT_PLUS_MICRO;
  1062. }
  1063. static int inv_mpu6050_write_event_value(struct iio_dev *indio_dev,
  1064.                                          const struct iio_chan_spec *chan,
  1065.                                          enum iio_event_type type,
  1066.                                          enum iio_event_direction dir,
  1067.                                          enum iio_event_info info,
  1068.                                          int val, int val2)
  1069. {
  1070.         struct inv_mpu6050_state *st = iio_priv(indio_dev);
  1071.         struct device *pdev = regmap_get_device(st->map);
  1072.         u64 value;
  1073.         int result;
  1074.         /* support only WoM (accel roc rising) event value */
  1075.         if (chan->type != IIO_ACCEL || type != IIO_EV_TYPE_ROC ||
  1076.             dir != IIO_EV_DIR_RISING || info != IIO_EV_INFO_VALUE)
  1077.                 return -EINVAL;
  1078.         if (val < 0 || val2 < 0)
  1079.                 return -EINVAL;
  1080.         guard(mutex)(&st->lock);
  1081.         result = pm_runtime_resume_and_get(pdev);
  1082.         if (result)
  1083.                 return result;
  1084.         value = (u64)val * 1000000ULL + (u64)val2;
  1085.         result = inv_mpu6050_set_wom_threshold(st, value, INV_MPU6050_FREQ_DIVIDER(st));
  1086.         pm_runtime_mark_last_busy(pdev);
  1087.         pm_runtime_put_autosuspend(pdev);
  1088.         return result;
  1089. }
  1090. /*
  1091. *  inv_mpu6050_set_lpf() - set low pass filer based on fifo rate.
  1092. *
  1093. *                  Based on the Nyquist principle, the bandwidth of the low
  1094. *                  pass filter must not exceed the signal sampling rate divided
  1095. *                  by 2, or there would be aliasing.
  1096. *                  This function basically search for the correct low pass
  1097. *                  parameters based on the fifo rate, e.g, sampling frequency.
  1098. *
  1099. *  lpf is set automatically when setting sampling rate to avoid any aliases.
  1100. */
  1101. static int inv_mpu6050_set_lpf(struct inv_mpu6050_state *st, int rate)
  1102. {
  1103.         static const int hz[] = {400, 200, 90, 40, 20, 10};
  1104.         static const int d[] = {
  1105.                 INV_MPU6050_FILTER_200HZ, INV_MPU6050_FILTER_100HZ,
  1106.                 INV_MPU6050_FILTER_45HZ, INV_MPU6050_FILTER_20HZ,
  1107.                 INV_MPU6050_FILTER_10HZ, INV_MPU6050_FILTER_5HZ
  1108.         };
  1109.         int i, result;
  1110.         u8 data;
  1111.         data = INV_MPU6050_FILTER_5HZ;
  1112.         for (i = 0; i < ARRAY_SIZE(hz); ++i) {
  1113.                 if (rate >= hz[i]) {
  1114.                         data = d[i];
  1115.                         break;
  1116.                 }
  1117.         }
  1118.         result = inv_mpu6050_set_lpf_regs(st, data);
  1119.         if (result)
  1120.                 return result;
  1121.         st->chip_config.lpf = data;
  1122.         return 0;
  1123. }
  1124. /*
  1125. * inv_mpu6050_fifo_rate_store() - Set fifo rate.
  1126. */
  1127. static ssize_t
  1128. inv_mpu6050_fifo_rate_store(struct device *dev, struct device_attribute *attr,
  1129.                             const char *buf, size_t count)
  1130. {
  1131.         int fifo_rate;
  1132.         u32 fifo_period;
  1133.         bool fifo_on;
  1134.         u8 d;
  1135.         int result;
  1136.         struct iio_dev *indio_dev = dev_to_iio_dev(dev);
  1137.         struct inv_mpu6050_state *st = iio_priv(indio_dev);
  1138.         struct device *pdev = regmap_get_device(st->map);
  1139.         if (kstrtoint(buf, 10, &fifo_rate))
  1140.                 return -EINVAL;
  1141.         if (fifo_rate < INV_MPU6050_MIN_FIFO_RATE ||
  1142.             fifo_rate > INV_MPU6050_MAX_FIFO_RATE)
  1143.                 return -EINVAL;
  1144.         /* compute the chip sample rate divider */
  1145.         d = INV_MPU6050_FIFO_RATE_TO_DIVIDER(fifo_rate);
  1146.         /* compute back the fifo rate to handle truncation cases */
  1147.         fifo_rate = INV_MPU6050_DIVIDER_TO_FIFO_RATE(d);
  1148.         fifo_period = NSEC_PER_SEC / fifo_rate;
  1149.         mutex_lock(&st->lock);
  1150.         if (d == st->chip_config.divider) {
  1151.                 result = 0;
  1152.                 goto fifo_rate_fail_unlock;
  1153.         }
  1154.         fifo_on = st->chip_config.accl_fifo_enable ||
  1155.                   st->chip_config.gyro_fifo_enable ||
  1156.                   st->chip_config.magn_fifo_enable;
  1157.         result = inv_sensors_timestamp_update_odr(&st->timestamp, fifo_period, fifo_on);
  1158.         if (result)
  1159.                 goto fifo_rate_fail_unlock;
  1160.         result = pm_runtime_resume_and_get(pdev);
  1161.         if (result)
  1162.                 goto fifo_rate_fail_unlock;
  1163.         result = regmap_write(st->map, st->reg->sample_rate_div, d);
  1164.         if (result)
  1165.                 goto fifo_rate_fail_power_off;
  1166.         st->chip_config.divider = d;
  1167.         result = inv_mpu6050_set_lpf(st, fifo_rate);
  1168.         if (result)
  1169.                 goto fifo_rate_fail_power_off;
  1170.         /* update rate for magn, noop if not present in chip */
  1171.         result = inv_mpu_magn_set_rate(st, fifo_rate);
  1172.         if (result)
  1173.                 goto fifo_rate_fail_power_off;
  1174.         /* update wom threshold since roc is dependent on sampling frequency */
  1175.         result = inv_mpu6050_set_wom_threshold(st, st->chip_config.roc_threshold,
  1176.                                                INV_MPU6050_FREQ_DIVIDER(st));
  1177.         if (result)
  1178.                 goto fifo_rate_fail_power_off;
  1179.         pm_runtime_mark_last_busy(pdev);
  1180. fifo_rate_fail_power_off:
  1181.         pm_runtime_put_autosuspend(pdev);
  1182. fifo_rate_fail_unlock:
  1183.         mutex_unlock(&st->lock);
  1184.         if (result)
  1185.                 return result;
  1186.         return count;
  1187. }
  1188. /*
  1189. * inv_fifo_rate_show() - Get the current sampling rate.
  1190. */
  1191. static ssize_t
  1192. inv_fifo_rate_show(struct device *dev, struct device_attribute *attr,
  1193.                    char *buf)
  1194. {
  1195.         struct inv_mpu6050_state *st = iio_priv(dev_to_iio_dev(dev));
  1196.         unsigned fifo_rate;
  1197.         mutex_lock(&st->lock);
  1198.         fifo_rate = INV_MPU6050_DIVIDER_TO_FIFO_RATE(st->chip_config.divider);
  1199.         mutex_unlock(&st->lock);
  1200.         return scnprintf(buf, PAGE_SIZE, "%u\n", fifo_rate);
  1201. }
  1202. /*
  1203. * inv_attr_show() - calling this function will show current
  1204. *                    parameters.
  1205. *
  1206. * Deprecated in favor of IIO mounting matrix API.
  1207. *
  1208. * See inv_get_mount_matrix()
  1209. */
  1210. static ssize_t inv_attr_show(struct device *dev, struct device_attribute *attr,
  1211.                              char *buf)
  1212. {
  1213.         struct inv_mpu6050_state *st = iio_priv(dev_to_iio_dev(dev));
  1214.         struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
  1215.         s8 *m;
  1216.         switch (this_attr->address) {
  1217.         /*
  1218.          * In MPU6050, the two matrix are the same because gyro and accel
  1219.          * are integrated in one chip
  1220.          */
  1221.         case ATTR_GYRO_MATRIX:
  1222.         case ATTR_ACCL_MATRIX:
  1223.                 m = st->plat_data.orientation;
  1224.                 return scnprintf(buf, PAGE_SIZE,
  1225.                         "%d, %d, %d; %d, %d, %d; %d, %d, %d\n",
  1226.                         m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8]);
  1227.         default:
  1228.                 return -EINVAL;
  1229.         }
  1230. }
  1231. /**
  1232. * inv_mpu6050_validate_trigger() - validate_trigger callback for invensense
  1233. *                                  MPU6050 device.
  1234. * @indio_dev: The IIO device
  1235. * @trig: The new trigger
  1236. *
  1237. * Returns: 0 if the 'trig' matches the trigger registered by the MPU6050
  1238. * device, -EINVAL otherwise.
  1239. */
  1240. static int inv_mpu6050_validate_trigger(struct iio_dev *indio_dev,
  1241.                                         struct iio_trigger *trig)
  1242. {
  1243.         struct inv_mpu6050_state *st = iio_priv(indio_dev);
  1244.         if (st->trig != trig)
  1245.                 return -EINVAL;
  1246.         return 0;
  1247. }
  1248. static const struct iio_mount_matrix *
  1249. inv_get_mount_matrix(const struct iio_dev *indio_dev,
  1250.                      const struct iio_chan_spec *chan)
  1251. {
  1252.         struct inv_mpu6050_state *data = iio_priv(indio_dev);
  1253.         const struct iio_mount_matrix *matrix;
  1254.         if (chan->type == IIO_MAGN)
  1255.                 matrix = &data->magn_orient;
  1256.         else
  1257.                 matrix = &data->orientation;
  1258.         return matrix;
  1259. }
  1260. static const struct iio_chan_spec_ext_info inv_ext_info[] = {
  1261.         IIO_MOUNT_MATRIX(IIO_SHARED_BY_TYPE, inv_get_mount_matrix),
  1262.         { }
  1263. };
  1264. static const struct iio_event_spec inv_wom_events[] = {
  1265.         {
  1266.                 .type = IIO_EV_TYPE_ROC,
  1267.                 .dir = IIO_EV_DIR_RISING,
  1268.                 .mask_separate = BIT(IIO_EV_INFO_ENABLE) |
  1269.                                  BIT(IIO_EV_INFO_VALUE),
  1270.         },
  1271. };
  1272. #define INV_MPU6050_CHAN(_type, _channel2, _index)                    \
  1273.         {                                                             \
  1274.                 .type = _type,                                        \
  1275.                 .modified = 1,                                        \
  1276.                 .channel2 = _channel2,                                \
  1277.                 .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
  1278.                 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |              \
  1279.                                       BIT(IIO_CHAN_INFO_CALIBBIAS),   \
  1280.                 .scan_index = _index,                                 \
  1281.                 .scan_type = {                                        \
  1282.                                 .sign = 's',                          \
  1283.                                 .realbits = 16,                       \
  1284.                                 .storagebits = 16,                    \
  1285.                                 .shift = 0,                           \
  1286.                                 .endianness = IIO_BE,                 \
  1287.                              },                                       \
  1288.                 .ext_info = inv_ext_info,                             \
  1289.         }
  1290. #define INV_MPU6050_TEMP_CHAN(_index)                                \
  1291.         {                                                        \
  1292.                 .type = IIO_TEMP,                                \
  1293.                 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW)        \
  1294.                                 | BIT(IIO_CHAN_INFO_OFFSET)        \
  1295.                                 | BIT(IIO_CHAN_INFO_SCALE),        \
  1296.                 .scan_index = _index,                                \
  1297.                 .scan_type = {                                        \
  1298.                         .sign = 's',                                \
  1299.                         .realbits = 16,                                \
  1300.                         .storagebits = 16,                        \
  1301.                         .shift = 0,                                \
  1302.                         .endianness = IIO_BE,                        \
  1303.                 },                                                \
  1304.         }
  1305. #define INV_MPU6050_EVENT_CHAN(_type, _channel2, _events, _events_nb)        \
  1306. {                                                                        \
  1307.         .type = _type,                                                        \
  1308.         .modified = 1,                                                        \
  1309.         .channel2 = _channel2,                                                \
  1310.         .event_spec = _events,                                                \
  1311.         .num_event_specs = _events_nb,                                        \
  1312.         .scan_index = -1,                                                \
  1313. }
  1314. static const struct iio_chan_spec inv_mpu6050_channels[] = {
  1315.         IIO_CHAN_SOFT_TIMESTAMP(INV_MPU6050_SCAN_TIMESTAMP),
  1316.         INV_MPU6050_TEMP_CHAN(INV_MPU6050_SCAN_TEMP),
  1317.         INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_X, INV_MPU6050_SCAN_GYRO_X),
  1318.         INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_Y, INV_MPU6050_SCAN_GYRO_Y),
  1319.         INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_Z, INV_MPU6050_SCAN_GYRO_Z),
  1320.         INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_X, INV_MPU6050_SCAN_ACCL_X),
  1321.         INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_Y, INV_MPU6050_SCAN_ACCL_Y),
  1322.         INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_Z, INV_MPU6050_SCAN_ACCL_Z),
  1323. };
  1324. static const struct iio_chan_spec inv_mpu6500_channels[] = {
  1325.         IIO_CHAN_SOFT_TIMESTAMP(INV_MPU6050_SCAN_TIMESTAMP),
  1326.         INV_MPU6050_TEMP_CHAN(INV_MPU6050_SCAN_TEMP),
  1327.         INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_X, INV_MPU6050_SCAN_GYRO_X),
  1328.         INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_Y, INV_MPU6050_SCAN_GYRO_Y),
  1329.         INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_Z, INV_MPU6050_SCAN_GYRO_Z),
  1330.         INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_X, INV_MPU6050_SCAN_ACCL_X),
  1331.         INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_Y, INV_MPU6050_SCAN_ACCL_Y),
  1332.         INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_Z, INV_MPU6050_SCAN_ACCL_Z),
  1333.         INV_MPU6050_EVENT_CHAN(IIO_ACCEL, IIO_MOD_X_OR_Y_OR_Z,
  1334.                                inv_wom_events, ARRAY_SIZE(inv_wom_events)),
  1335. };
  1336. #define INV_MPU6050_SCAN_MASK_3AXIS_ACCEL        \
  1337.         (BIT(INV_MPU6050_SCAN_ACCL_X)                \
  1338.         | BIT(INV_MPU6050_SCAN_ACCL_Y)                \
  1339.         | BIT(INV_MPU6050_SCAN_ACCL_Z))
  1340. #define INV_MPU6050_SCAN_MASK_3AXIS_GYRO        \
  1341.         (BIT(INV_MPU6050_SCAN_GYRO_X)                \
  1342.         | BIT(INV_MPU6050_SCAN_GYRO_Y)                \
  1343.         | BIT(INV_MPU6050_SCAN_GYRO_Z))
  1344. #define INV_MPU6050_SCAN_MASK_TEMP                (BIT(INV_MPU6050_SCAN_TEMP))
  1345. static const unsigned long inv_mpu_scan_masks[] = {
  1346.         /* 3-axis accel */
  1347.         INV_MPU6050_SCAN_MASK_3AXIS_ACCEL,
  1348.         INV_MPU6050_SCAN_MASK_3AXIS_ACCEL | INV_MPU6050_SCAN_MASK_TEMP,
  1349.         /* 3-axis gyro */
  1350.         INV_MPU6050_SCAN_MASK_3AXIS_GYRO,
  1351.         INV_MPU6050_SCAN_MASK_3AXIS_GYRO | INV_MPU6050_SCAN_MASK_TEMP,
  1352.         /* 6-axis accel + gyro */
  1353.         INV_MPU6050_SCAN_MASK_3AXIS_ACCEL | INV_MPU6050_SCAN_MASK_3AXIS_GYRO,
  1354.         INV_MPU6050_SCAN_MASK_3AXIS_ACCEL | INV_MPU6050_SCAN_MASK_3AXIS_GYRO
  1355.                 | INV_MPU6050_SCAN_MASK_TEMP,
  1356.         0,
  1357. };
  1358. #define INV_MPU9X50_MAGN_CHAN(_chan2, _bits, _index)                        \
  1359.         {                                                                \
  1360.                 .type = IIO_MAGN,                                        \
  1361.                 .modified = 1,                                                \
  1362.                 .channel2 = _chan2,                                        \
  1363.                 .info_mask_separate = BIT(IIO_CHAN_INFO_SCALE) |        \
  1364.                                       BIT(IIO_CHAN_INFO_RAW),                \
  1365.                 .scan_index = _index,                                        \
  1366.                 .scan_type = {                                                \
  1367.                         .sign = 's',                                        \
  1368.                         .realbits = _bits,                                \
  1369.                         .storagebits = 16,                                \
  1370.                         .shift = 0,                                        \
  1371.                         .endianness = IIO_BE,                                \
  1372.                 },                                                        \
  1373.                 .ext_info = inv_ext_info,                                \
  1374.         }
  1375. static const struct iio_chan_spec inv_mpu9150_channels[] = {
  1376.         IIO_CHAN_SOFT_TIMESTAMP(INV_MPU9X50_SCAN_TIMESTAMP),
  1377.         INV_MPU6050_TEMP_CHAN(INV_MPU6050_SCAN_TEMP),
  1378.         INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_X, INV_MPU6050_SCAN_GYRO_X),
  1379.         INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_Y, INV_MPU6050_SCAN_GYRO_Y),
  1380.         INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_Z, INV_MPU6050_SCAN_GYRO_Z),
  1381.         INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_X, INV_MPU6050_SCAN_ACCL_X),
  1382.         INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_Y, INV_MPU6050_SCAN_ACCL_Y),
  1383.         INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_Z, INV_MPU6050_SCAN_ACCL_Z),
  1384.         /* Magnetometer resolution is 13 bits */
  1385.         INV_MPU9X50_MAGN_CHAN(IIO_MOD_X, 13, INV_MPU9X50_SCAN_MAGN_X),
  1386.         INV_MPU9X50_MAGN_CHAN(IIO_MOD_Y, 13, INV_MPU9X50_SCAN_MAGN_Y),
  1387.         INV_MPU9X50_MAGN_CHAN(IIO_MOD_Z, 13, INV_MPU9X50_SCAN_MAGN_Z),
  1388. };
  1389. static const struct iio_chan_spec inv_mpu9250_channels[] = {
  1390.         IIO_CHAN_SOFT_TIMESTAMP(INV_MPU9X50_SCAN_TIMESTAMP),
  1391.         INV_MPU6050_TEMP_CHAN(INV_MPU6050_SCAN_TEMP),
  1392.         INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_X, INV_MPU6050_SCAN_GYRO_X),
  1393.         INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_Y, INV_MPU6050_SCAN_GYRO_Y),
  1394.         INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_Z, INV_MPU6050_SCAN_GYRO_Z),
  1395.         INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_X, INV_MPU6050_SCAN_ACCL_X),
  1396.         INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_Y, INV_MPU6050_SCAN_ACCL_Y),
  1397.         INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_Z, INV_MPU6050_SCAN_ACCL_Z),
  1398.         /* Magnetometer resolution is 16 bits */
  1399.         INV_MPU9X50_MAGN_CHAN(IIO_MOD_X, 16, INV_MPU9X50_SCAN_MAGN_X),
  1400.         INV_MPU9X50_MAGN_CHAN(IIO_MOD_Y, 16, INV_MPU9X50_SCAN_MAGN_Y),
  1401.         INV_MPU9X50_MAGN_CHAN(IIO_MOD_Z, 16, INV_MPU9X50_SCAN_MAGN_Z),
  1402. };
  1403. #define INV_MPU9X50_SCAN_MASK_3AXIS_MAGN        \
  1404.         (BIT(INV_MPU9X50_SCAN_MAGN_X)                \
  1405.         | BIT(INV_MPU9X50_SCAN_MAGN_Y)                \
  1406.         | BIT(INV_MPU9X50_SCAN_MAGN_Z))
  1407. static const unsigned long inv_mpu9x50_scan_masks[] = {
  1408.         /* 3-axis accel */
  1409.         INV_MPU6050_SCAN_MASK_3AXIS_ACCEL,
  1410.         INV_MPU6050_SCAN_MASK_3AXIS_ACCEL | INV_MPU6050_SCAN_MASK_TEMP,
  1411.         /* 3-axis gyro */
  1412.         INV_MPU6050_SCAN_MASK_3AXIS_GYRO,
  1413.         INV_MPU6050_SCAN_MASK_3AXIS_GYRO | INV_MPU6050_SCAN_MASK_TEMP,
  1414.         /* 3-axis magn */
  1415.         INV_MPU9X50_SCAN_MASK_3AXIS_MAGN,
  1416.         INV_MPU9X50_SCAN_MASK_3AXIS_MAGN | INV_MPU6050_SCAN_MASK_TEMP,
  1417.         /* 6-axis accel + gyro */
  1418.         INV_MPU6050_SCAN_MASK_3AXIS_ACCEL | INV_MPU6050_SCAN_MASK_3AXIS_GYRO,
  1419.         INV_MPU6050_SCAN_MASK_3AXIS_ACCEL | INV_MPU6050_SCAN_MASK_3AXIS_GYRO
  1420.                 | INV_MPU6050_SCAN_MASK_TEMP,
  1421.         /* 6-axis accel + magn */
  1422.         INV_MPU6050_SCAN_MASK_3AXIS_ACCEL | INV_MPU9X50_SCAN_MASK_3AXIS_MAGN,
  1423.         INV_MPU6050_SCAN_MASK_3AXIS_ACCEL | INV_MPU9X50_SCAN_MASK_3AXIS_MAGN
  1424.                 | INV_MPU6050_SCAN_MASK_TEMP,
  1425.         /* 6-axis gyro + magn */
  1426.         INV_MPU6050_SCAN_MASK_3AXIS_GYRO | INV_MPU9X50_SCAN_MASK_3AXIS_MAGN,
  1427.         INV_MPU6050_SCAN_MASK_3AXIS_GYRO | INV_MPU9X50_SCAN_MASK_3AXIS_MAGN
  1428.                 | INV_MPU6050_SCAN_MASK_TEMP,
  1429.         /* 9-axis accel + gyro + magn */
  1430.         INV_MPU6050_SCAN_MASK_3AXIS_ACCEL | INV_MPU6050_SCAN_MASK_3AXIS_GYRO
  1431.                 | INV_MPU9X50_SCAN_MASK_3AXIS_MAGN,
  1432.         INV_MPU6050_SCAN_MASK_3AXIS_ACCEL | INV_MPU6050_SCAN_MASK_3AXIS_GYRO
  1433.                 | INV_MPU9X50_SCAN_MASK_3AXIS_MAGN
  1434.                 | INV_MPU6050_SCAN_MASK_TEMP,
  1435.         0,
  1436. };
  1437. static const unsigned long inv_icm20602_scan_masks[] = {
  1438.         /* 3-axis accel + temp (mandatory) */
  1439.         INV_MPU6050_SCAN_MASK_3AXIS_ACCEL | INV_MPU6050_SCAN_MASK_TEMP,
  1440.         /* 3-axis gyro + temp (mandatory) */
  1441.         INV_MPU6050_SCAN_MASK_3AXIS_GYRO | INV_MPU6050_SCAN_MASK_TEMP,
  1442.         /* 6-axis accel + gyro + temp (mandatory) */
  1443.         INV_MPU6050_SCAN_MASK_3AXIS_ACCEL | INV_MPU6050_SCAN_MASK_3AXIS_GYRO
  1444.                 | INV_MPU6050_SCAN_MASK_TEMP,
  1445.         0,
  1446. };
  1447. /*
  1448. * The user can choose any frequency between INV_MPU6050_MIN_FIFO_RATE and
  1449. * INV_MPU6050_MAX_FIFO_RATE, but only these frequencies are matched by the
  1450. * low-pass filter. Specifically, each of these sampling rates are about twice
  1451. * the bandwidth of a corresponding low-pass filter, which should eliminate
  1452. * aliasing following the Nyquist principle. By picking a frequency different
  1453. * from these, the user risks aliasing effects.
  1454. */
  1455. static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("10 20 50 100 200 500");
  1456. static IIO_CONST_ATTR(in_anglvel_scale_available,
  1457.                                           "0.000133090 0.000266181 0.000532362 0.001064724");
  1458. static IIO_CONST_ATTR(in_accel_scale_available,
  1459.                                           "0.000598 0.001196 0.002392 0.004785");
  1460. static IIO_DEV_ATTR_SAMP_FREQ(S_IRUGO | S_IWUSR, inv_fifo_rate_show,
  1461.         inv_mpu6050_fifo_rate_store);
  1462. /* Deprecated: kept for userspace backward compatibility. */
  1463. static IIO_DEVICE_ATTR(in_gyro_matrix, S_IRUGO, inv_attr_show, NULL,
  1464.         ATTR_GYRO_MATRIX);
  1465. static IIO_DEVICE_ATTR(in_accel_matrix, S_IRUGO, inv_attr_show, NULL,
  1466.         ATTR_ACCL_MATRIX);
  1467. static struct attribute *inv_attributes[] = {
  1468.         &iio_dev_attr_in_gyro_matrix.dev_attr.attr,  /* deprecated */
  1469.         &iio_dev_attr_in_accel_matrix.dev_attr.attr, /* deprecated */
  1470.         &iio_dev_attr_sampling_frequency.dev_attr.attr,
  1471.         &iio_const_attr_sampling_frequency_available.dev_attr.attr,
  1472.         &iio_const_attr_in_accel_scale_available.dev_attr.attr,
  1473.         &iio_const_attr_in_anglvel_scale_available.dev_attr.attr,
  1474.         NULL,
  1475. };
  1476. static const struct attribute_group inv_attribute_group = {
  1477.         .attrs = inv_attributes
  1478. };
  1479. static int inv_mpu6050_reg_access(struct iio_dev *indio_dev,
  1480.                                   unsigned int reg,
  1481.                                   unsigned int writeval,
  1482.                                   unsigned int *readval)
  1483. {
  1484.         struct inv_mpu6050_state *st = iio_priv(indio_dev);
  1485.         int ret;
  1486.         mutex_lock(&st->lock);
  1487.         if (readval)
  1488.                 ret = regmap_read(st->map, reg, readval);
  1489.         else
  1490.                 ret = regmap_write(st->map, reg, writeval);
  1491.         mutex_unlock(&st->lock);
  1492.         return ret;
  1493. }
  1494. static const struct iio_info mpu_info = {
  1495.         .read_raw = &inv_mpu6050_read_raw,
  1496.         .write_raw = &inv_mpu6050_write_raw,
  1497.         .write_raw_get_fmt = &inv_write_raw_get_fmt,
  1498.         .attrs = &inv_attribute_group,
  1499.         .read_event_config = inv_mpu6050_read_event_config,
  1500.         .write_event_config = inv_mpu6050_write_event_config,
  1501.         .read_event_value = inv_mpu6050_read_event_value,
  1502.         .write_event_value = inv_mpu6050_write_event_value,
  1503.         .validate_trigger = inv_mpu6050_validate_trigger,
  1504.         .debugfs_reg_access = &inv_mpu6050_reg_access,
  1505. };
  1506. /*
  1507. *  inv_check_and_setup_chip() - check and setup chip.
  1508. */
  1509. static int inv_check_and_setup_chip(struct inv_mpu6050_state *st)
  1510. {
  1511.         int result;
  1512.         unsigned int regval, mask;
  1513.         int i;
  1514.         st->hw  = &hw_info[st->chip_type];
  1515.         st->reg = hw_info[st->chip_type].reg;
  1516.         memcpy(&st->chip_config, hw_info[st->chip_type].config,
  1517.                sizeof(st->chip_config));
  1518.         st->data = devm_kzalloc(regmap_get_device(st->map), st->hw->fifo_size, GFP_KERNEL);
  1519.         if (st->data == NULL)
  1520.                 return -ENOMEM;
  1521.         /* check chip self-identification */
  1522.         result = regmap_read(st->map, INV_MPU6050_REG_WHOAMI, &regval);
  1523.         if (result)
  1524.                 return result;
  1525.         if (regval != st->hw->whoami) {
  1526.                 /* check whoami against all possible values */
  1527.                 for (i = 0; i < INV_NUM_PARTS; ++i) {
  1528.                         if (regval == hw_info[i].whoami) {
  1529.                                 dev_warn(regmap_get_device(st->map),
  1530.                                         "whoami mismatch got 0x%02x (%s) expected 0x%02x (%s)\n",
  1531.                                         regval, hw_info[i].name,
  1532.                                         st->hw->whoami, st->hw->name);
  1533.                                 break;
  1534.                         }
  1535.                 }
  1536.                 if (i >= INV_NUM_PARTS) {
  1537.                         dev_err(regmap_get_device(st->map),
  1538.                                 "invalid whoami 0x%02x expected 0x%02x (%s)\n",
  1539.                                 regval, st->hw->whoami, st->hw->name);
  1540.                         return -ENODEV;
  1541.                 }
  1542.         }
  1543.         /* reset to make sure previous state are not there */
  1544.         result = regmap_write(st->map, st->reg->pwr_mgmt_1,
  1545.                               INV_MPU6050_BIT_H_RESET);
  1546.         if (result)
  1547.                 return result;
  1548.         msleep(INV_MPU6050_POWER_UP_TIME);
  1549.         switch (st->chip_type) {
  1550.         case INV_MPU6000:
  1551.         case INV_MPU6500:
  1552.         case INV_MPU6515:
  1553.         case INV_MPU6880:
  1554.         case INV_MPU9250:
  1555.         case INV_MPU9255:
  1556.                 /* reset signal path (required for spi connection) */
  1557.                 regval = INV_MPU6050_BIT_TEMP_RST | INV_MPU6050_BIT_ACCEL_RST |
  1558.                          INV_MPU6050_BIT_GYRO_RST;
  1559.                 result = regmap_write(st->map, INV_MPU6050_REG_SIGNAL_PATH_RESET,
  1560.                                       regval);
  1561.                 if (result)
  1562.                         return result;
  1563.                 msleep(INV_MPU6050_POWER_UP_TIME);
  1564.                 break;
  1565.         default:
  1566.                 break;
  1567.         }
  1568.         /*
  1569.          * Turn power on. After reset, the sleep bit could be on
  1570.          * or off depending on the OTP settings. Turning power on
  1571.          * make it in a definite state as well as making the hardware
  1572.          * state align with the software state
  1573.          */
  1574.         result = inv_mpu6050_set_power_itg(st, true);
  1575.         if (result)
  1576.                 return result;
  1577.         mask = INV_MPU6050_SENSOR_ACCL | INV_MPU6050_SENSOR_GYRO |
  1578.                         INV_MPU6050_SENSOR_TEMP | INV_MPU6050_SENSOR_MAGN;
  1579.         result = inv_mpu6050_switch_engine(st, false, mask);
  1580.         if (result)
  1581.                 goto error_power_off;
  1582.         return 0;
  1583. error_power_off:
  1584.         inv_mpu6050_set_power_itg(st, false);
  1585.         return result;
  1586. }
  1587. static int inv_mpu_core_enable_regulator_vddio(struct inv_mpu6050_state *st)
  1588. {
  1589.         int result;
  1590.         result = regulator_enable(st->vddio_supply);
  1591.         if (result) {
  1592.                 dev_err(regmap_get_device(st->map),
  1593.                         "Failed to enable vddio regulator: %d\n", result);
  1594.         } else {
  1595.                 /* Give the device a little bit of time to start up. */
  1596.                 usleep_range(3000, 5000);
  1597.         }
  1598.         return result;
  1599. }
  1600. static int inv_mpu_core_disable_regulator_vddio(struct inv_mpu6050_state *st)
  1601. {
  1602.         int result;
  1603.         result = regulator_disable(st->vddio_supply);
  1604.         if (result)
  1605.                 dev_err(regmap_get_device(st->map),
  1606.                         "Failed to disable vddio regulator: %d\n", result);
  1607.         return result;
  1608. }
  1609. static void inv_mpu_core_disable_regulator_action(void *_data)
  1610. {
  1611.         struct inv_mpu6050_state *st = _data;
  1612.         int result;
  1613.         result = regulator_disable(st->vdd_supply);
  1614.         if (result)
  1615.                 dev_err(regmap_get_device(st->map),
  1616.                         "Failed to disable vdd regulator: %d\n", result);
  1617.         inv_mpu_core_disable_regulator_vddio(st);
  1618. }
  1619. static void inv_mpu_pm_disable(void *data)
  1620. {
  1621.         struct device *dev = data;
  1622.         pm_runtime_disable(dev);
  1623. }
  1624. int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name,
  1625.                 int (*inv_mpu_bus_setup)(struct iio_dev *), int chip_type)
  1626. {
  1627.         struct inv_mpu6050_state *st;
  1628.         struct iio_dev *indio_dev;
  1629.         struct inv_mpu6050_platform_data *pdata;
  1630.         struct device *dev = regmap_get_device(regmap);
  1631.         int result;
  1632.         struct irq_data *desc;
  1633.         int irq_type;
  1634.         indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
  1635.         if (!indio_dev)
  1636.                 return -ENOMEM;
  1637.         BUILD_BUG_ON(ARRAY_SIZE(hw_info) != INV_NUM_PARTS);
  1638.         if (chip_type < 0 || chip_type >= INV_NUM_PARTS) {
  1639.                 dev_err(dev, "Bad invensense chip_type=%d name=%s\n",
  1640.                                 chip_type, name);
  1641.                 return -ENODEV;
  1642.         }
  1643.         st = iio_priv(indio_dev);
  1644.         mutex_init(&st->lock);
  1645.         st->chip_type = chip_type;
  1646.         st->irq = irq;
  1647.         st->map = regmap;
  1648.         st->level_shifter = device_property_read_bool(dev,
  1649.                                                       "invensense,level-shifter");
  1650.         pdata = dev_get_platdata(dev);
  1651.         if (!pdata) {
  1652.                 result = iio_read_mount_matrix(dev, &st->orientation);
  1653.                 if (result) {
  1654.                         dev_err(dev, "Failed to retrieve mounting matrix %d\n",
  1655.                                 result);
  1656.                         return result;
  1657.                 }
  1658.         } else {
  1659.                 st->plat_data = *pdata;
  1660.         }
  1661.         if (irq > 0) {
  1662.                 desc = irq_get_irq_data(irq);
  1663.                 if (!desc) {
  1664.                         dev_err(dev, "Could not find IRQ %d\n", irq);
  1665.                         return -EINVAL;
  1666.                 }
  1667.                 irq_type = irqd_get_trigger_type(desc);
  1668.                 if (!irq_type)
  1669.                         irq_type = IRQF_TRIGGER_RISING;
  1670.         } else {
  1671.                 /* Doesn't really matter, use the default */
  1672.                 irq_type = IRQF_TRIGGER_RISING;
  1673.         }
  1674.         if (irq_type & IRQF_TRIGGER_RISING)        // rising or both-edge
  1675.                 st->irq_mask = INV_MPU6050_ACTIVE_HIGH;
  1676.         else if (irq_type == IRQF_TRIGGER_FALLING)
  1677.                 st->irq_mask = INV_MPU6050_ACTIVE_LOW;
  1678.         else if (irq_type == IRQF_TRIGGER_HIGH)
  1679.                 st->irq_mask = INV_MPU6050_ACTIVE_HIGH |
  1680.                         INV_MPU6050_LATCH_INT_EN;
  1681.         else if (irq_type == IRQF_TRIGGER_LOW)
  1682.                 st->irq_mask = INV_MPU6050_ACTIVE_LOW |
  1683.                         INV_MPU6050_LATCH_INT_EN;
  1684.         else {
  1685.                 dev_err(dev, "Invalid interrupt type 0x%x specified\n",
  1686.                         irq_type);
  1687.                 return -EINVAL;
  1688.         }
  1689.         device_set_wakeup_capable(dev, true);
  1690.         st->vdd_supply = devm_regulator_get(dev, "vdd");
  1691.         if (IS_ERR(st->vdd_supply))
  1692.                 return dev_err_probe(dev, PTR_ERR(st->vdd_supply),
  1693.                                      "Failed to get vdd regulator\n");
  1694.         st->vddio_supply = devm_regulator_get(dev, "vddio");
  1695.         if (IS_ERR(st->vddio_supply))
  1696.                 return dev_err_probe(dev, PTR_ERR(st->vddio_supply),
  1697.                                      "Failed to get vddio regulator\n");
  1698.         result = regulator_enable(st->vdd_supply);
  1699.         if (result) {
  1700.                 dev_err(dev, "Failed to enable vdd regulator: %d\n", result);
  1701.                 return result;
  1702.         }
  1703.         msleep(INV_MPU6050_POWER_UP_TIME);
  1704.         result = inv_mpu_core_enable_regulator_vddio(st);
  1705.         if (result) {
  1706.                 regulator_disable(st->vdd_supply);
  1707.                 return result;
  1708.         }
  1709.         result = devm_add_action_or_reset(dev, inv_mpu_core_disable_regulator_action,
  1710.                                  st);
  1711.         if (result) {
  1712.                 dev_err(dev, "Failed to setup regulator cleanup action %d\n",
  1713.                         result);
  1714.                 return result;
  1715.         }
  1716.         /* fill magnetometer orientation */
  1717.         result = inv_mpu_magn_set_orient(st);
  1718.         if (result)
  1719.                 return result;
  1720.         /* power is turned on inside check chip type*/
  1721.         result = inv_check_and_setup_chip(st);
  1722.         if (result)
  1723.                 return result;
  1724.         result = inv_mpu6050_init_config(indio_dev);
  1725.         if (result) {
  1726.                 dev_err(dev, "Could not initialize device.\n");
  1727.                 goto error_power_off;
  1728.         }
  1729.         dev_set_drvdata(dev, indio_dev);
  1730.         /* name will be NULL when enumerated via ACPI */
  1731.         if (name)
  1732.                 indio_dev->name = name;
  1733.         else
  1734.                 indio_dev->name = dev_name(dev);
  1735.         /* requires parent device set in indio_dev */
  1736.         if (inv_mpu_bus_setup) {
  1737.                 result = inv_mpu_bus_setup(indio_dev);
  1738.                 if (result)
  1739.                         goto error_power_off;
  1740.         }
  1741.         /* chip init is done, turning on runtime power management */
  1742.         result = pm_runtime_set_active(dev);
  1743.         if (result)
  1744.                 goto error_power_off;
  1745.         pm_runtime_get_noresume(dev);
  1746.         pm_runtime_enable(dev);
  1747.         pm_runtime_set_autosuspend_delay(dev, INV_MPU6050_SUSPEND_DELAY_MS);
  1748.         pm_runtime_use_autosuspend(dev);
  1749.         pm_runtime_put(dev);
  1750.         result = devm_add_action_or_reset(dev, inv_mpu_pm_disable, dev);
  1751.         if (result)
  1752.                 return result;
  1753.         switch (chip_type) {
  1754.         case INV_MPU6000:
  1755.         case INV_MPU6050:
  1756.                 indio_dev->channels = inv_mpu6050_channels;
  1757.                 indio_dev->num_channels = ARRAY_SIZE(inv_mpu6050_channels);
  1758.                 indio_dev->available_scan_masks = inv_mpu_scan_masks;
  1759.                 break;
  1760.         case INV_MPU9150:
  1761.                 indio_dev->channels = inv_mpu9150_channels;
  1762.                 indio_dev->num_channels = ARRAY_SIZE(inv_mpu9150_channels);
  1763.                 indio_dev->available_scan_masks = inv_mpu9x50_scan_masks;
  1764.                 break;
  1765.         case INV_MPU9250:
  1766.         case INV_MPU9255:
  1767.                 indio_dev->channels = inv_mpu9250_channels;
  1768.                 indio_dev->num_channels = ARRAY_SIZE(inv_mpu9250_channels);
  1769.                 indio_dev->available_scan_masks = inv_mpu9x50_scan_masks;
  1770.                 break;
  1771.         case INV_ICM20600:
  1772.         case INV_ICM20602:
  1773.                 indio_dev->channels = inv_mpu6500_channels;
  1774.                 indio_dev->num_channels = ARRAY_SIZE(inv_mpu6500_channels);
  1775.                 indio_dev->available_scan_masks = inv_icm20602_scan_masks;
  1776.                 break;
  1777.         default:
  1778.                 indio_dev->channels = inv_mpu6500_channels;
  1779.                 indio_dev->num_channels = ARRAY_SIZE(inv_mpu6500_channels);
  1780.                 indio_dev->available_scan_masks = inv_mpu_scan_masks;
  1781.                 break;
  1782.         }
  1783.         /*
  1784.          * Use magnetometer inside the chip only if there is no i2c
  1785.          * auxiliary device in use. Otherwise Going back to 6-axis only.
  1786.          */
  1787.         if (st->magn_disabled) {
  1788.                 switch (chip_type) {
  1789.                 case INV_MPU9150:
  1790.                         indio_dev->channels = inv_mpu6050_channels;
  1791.                         indio_dev->num_channels = ARRAY_SIZE(inv_mpu6050_channels);
  1792.                         indio_dev->available_scan_masks = inv_mpu_scan_masks;
  1793.                         break;
  1794.                 default:
  1795.                         indio_dev->channels = inv_mpu6500_channels;
  1796.                         indio_dev->num_channels = ARRAY_SIZE(inv_mpu6500_channels);
  1797.                         indio_dev->available_scan_masks = inv_mpu_scan_masks;
  1798.                         break;
  1799.                 }
  1800.         }
  1801.         indio_dev->info = &mpu_info;
  1802.         if (irq > 0) {
  1803.                 /*
  1804.                  * The driver currently only supports buffered capture with its
  1805.                  * own trigger. So no IRQ, no trigger, no buffer
  1806.                  */
  1807.                 result = devm_iio_triggered_buffer_setup(dev, indio_dev,
  1808.                                                          iio_pollfunc_store_time,
  1809.                                                          inv_mpu6050_read_fifo,
  1810.                                                          NULL);
  1811.                 if (result) {
  1812.                         dev_err(dev, "configure buffer fail %d\n", result);
  1813.                         return result;
  1814.                 }
  1815.                 result = inv_mpu6050_probe_trigger(indio_dev, irq_type);
  1816.                 if (result) {
  1817.                         dev_err(dev, "trigger probe fail %d\n", result);
  1818.                         return result;
  1819.                 }
  1820.         }
  1821.         result = devm_iio_device_register(dev, indio_dev);
  1822.         if (result) {
  1823.                 dev_err(dev, "IIO register fail %d\n", result);
  1824.                 return result;
  1825.         }
  1826.         return 0;
  1827. error_power_off:
  1828.         inv_mpu6050_set_power_itg(st, false);
  1829.         return result;
  1830. }
  1831. EXPORT_SYMBOL_NS_GPL(inv_mpu_core_probe, IIO_MPU6050);
  1832. static int inv_mpu_resume(struct device *dev)
  1833. {
  1834.         struct iio_dev *indio_dev = dev_get_drvdata(dev);
  1835.         struct inv_mpu6050_state *st = iio_priv(indio_dev);
  1836.         bool wakeup;
  1837.         int result;
  1838.         guard(mutex)(&st->lock);
  1839.         wakeup = device_may_wakeup(dev) && st->chip_config.wom_en;
  1840.         if (wakeup) {
  1841.                 enable_irq(st->irq);
  1842.                 disable_irq_wake(st->irq);
  1843.                 result = inv_mpu6050_set_wom_lp(st, false);
  1844.                 if (result)
  1845.                         return result;
  1846.         } else {
  1847.                 result = inv_mpu_core_enable_regulator_vddio(st);
  1848.                 if (result)
  1849.                         return result;
  1850.                 result = inv_mpu6050_set_power_itg(st, true);
  1851.                 if (result)
  1852.                         return result;
  1853.         }
  1854.         pm_runtime_disable(dev);
  1855.         pm_runtime_set_active(dev);
  1856.         pm_runtime_enable(dev);
  1857.         result = inv_mpu6050_switch_engine(st, true, st->suspended_sensors);
  1858.         if (result)
  1859.                 return result;
  1860.         if (st->chip_config.wom_en && !wakeup) {
  1861.                 result = inv_mpu6050_set_wom_int(st, true);
  1862.                 if (result)
  1863.                         return result;
  1864.         }
  1865.         if (iio_buffer_enabled(indio_dev))
  1866.                 result = inv_mpu6050_prepare_fifo(st, true);
  1867.         return result;
  1868. }
  1869. static int inv_mpu_suspend(struct device *dev)
  1870. {
  1871.         struct iio_dev *indio_dev = dev_get_drvdata(dev);
  1872.         struct inv_mpu6050_state *st = iio_priv(indio_dev);
  1873.         bool wakeup;
  1874.         int result;
  1875.         guard(mutex)(&st->lock);
  1876.         st->suspended_sensors = 0;
  1877.         if (pm_runtime_suspended(dev))
  1878.                 return 0;
  1879.         if (iio_buffer_enabled(indio_dev)) {
  1880.                 result = inv_mpu6050_prepare_fifo(st, false);
  1881.                 if (result)
  1882.                         return result;
  1883.         }
  1884.         wakeup = device_may_wakeup(dev) && st->chip_config.wom_en;
  1885.         if (st->chip_config.wom_en && !wakeup) {
  1886.                 result = inv_mpu6050_set_wom_int(st, false);
  1887.                 if (result)
  1888.                         return result;
  1889.         }
  1890.         if (st->chip_config.accl_en && !wakeup)
  1891.                 st->suspended_sensors |= INV_MPU6050_SENSOR_ACCL;
  1892.         if (st->chip_config.gyro_en)
  1893.                 st->suspended_sensors |= INV_MPU6050_SENSOR_GYRO;
  1894.         if (st->chip_config.temp_en)
  1895.                 st->suspended_sensors |= INV_MPU6050_SENSOR_TEMP;
  1896.         if (st->chip_config.magn_en)
  1897.                 st->suspended_sensors |= INV_MPU6050_SENSOR_MAGN;
  1898.         if (st->chip_config.wom_en && !wakeup)
  1899.                 st->suspended_sensors |= INV_MPU6050_SENSOR_WOM;
  1900.         result = inv_mpu6050_switch_engine(st, false, st->suspended_sensors);
  1901.         if (result)
  1902.                 return result;
  1903.         if (wakeup) {
  1904.                 result = inv_mpu6050_set_wom_lp(st, true);
  1905.                 if (result)
  1906.                         return result;
  1907.                 enable_irq_wake(st->irq);
  1908.                 disable_irq(st->irq);
  1909.         } else {
  1910.                 result = inv_mpu6050_set_power_itg(st, false);
  1911.                 if (result)
  1912.                         return result;
  1913.                 inv_mpu_core_disable_regulator_vddio(st);
  1914.         }
  1915.         return 0;
  1916. }
  1917. static int inv_mpu_runtime_suspend(struct device *dev)
  1918. {
  1919.         struct inv_mpu6050_state *st = iio_priv(dev_get_drvdata(dev));
  1920.         unsigned int sensors;
  1921.         int ret;
  1922.         mutex_lock(&st->lock);
  1923.         sensors = INV_MPU6050_SENSOR_ACCL | INV_MPU6050_SENSOR_GYRO |
  1924.                         INV_MPU6050_SENSOR_TEMP | INV_MPU6050_SENSOR_MAGN |
  1925.                         INV_MPU6050_SENSOR_WOM;
  1926.         ret = inv_mpu6050_switch_engine(st, false, sensors);
  1927.         if (ret)
  1928.                 goto out_unlock;
  1929.         ret = inv_mpu6050_set_power_itg(st, false);
  1930.         if (ret)
  1931.                 goto out_unlock;
  1932.         inv_mpu_core_disable_regulator_vddio(st);
  1933. out_unlock:
  1934.         mutex_unlock(&st->lock);
  1935.         return ret;
  1936. }
  1937. static int inv_mpu_runtime_resume(struct device *dev)
  1938. {
  1939.         struct inv_mpu6050_state *st = iio_priv(dev_get_drvdata(dev));
  1940.         int ret;
  1941.         ret = inv_mpu_core_enable_regulator_vddio(st);
  1942.         if (ret)
  1943.                 return ret;
  1944.         return inv_mpu6050_set_power_itg(st, true);
  1945. }
  1946. EXPORT_NS_GPL_DEV_PM_OPS(inv_mpu_pmops, IIO_MPU6050) = {
  1947.         SYSTEM_SLEEP_PM_OPS(inv_mpu_suspend, inv_mpu_resume)
  1948.         RUNTIME_PM_OPS(inv_mpu_runtime_suspend, inv_mpu_runtime_resume, NULL)
  1949. };
  1950. MODULE_AUTHOR("Invensense Corporation");
  1951. MODULE_DESCRIPTION("Invensense device MPU6050 driver");
  1952. MODULE_LICENSE("GPL");
  1953. MODULE_IMPORT_NS(IIO_INV_SENSORS_TIMESTAMP);
复制代码
1.3 新增设备节点

这里我们将MPU6050接到i2c1接口,因此需要适当调整设备树。
1.3.1 i2c1

i2c1节点定义在arch/loongarch/boot/dts/loongson-2k0300.dtsi:
  1. i2c1: i2c1@0x16109000 {
  2.         compatible = "loongson,lsfs-i2c";
  3.         reg = <0 0x16109000 0 0x1000>;
  4.         #address-cells = <1>;
  5.         #size-cells = <0>;
  6.         interrupt-parent = <&liointc0>;
  7.         interrupts = <4 IRQ_TYPE_LEVEL_HIGH>;
  8.         pinctrl-0 = <&i2c1_pins>;
  9.         pinctrl-names = "default";
  10.         clock-frequency = <200000000>;
  11.         status = "disabled";
  12. };
复制代码
其中:


  • i2c1::标签,用于在设备树中其他地方通过&i2c1引用这个节点,方便添加属性或修改状态;
  • i2c1@0x16109000:节点名,格式为 设备名@寄存器基地址。这里i2c1是功能名,0x16109000是该I2C控制器的物理寄存器基地址。
  • compatible:驱动匹配字符串。内核通过该属性寻找能驱动此设备的驱动程序;
  • reg:寄存器地址范围。格式为 ,由于龙芯采用64位寻址,这里用两个32位数表示64位地址;
  • #address-cells和#size-cells:定义该节点下子节点(即挂载的I2C从设备)的地址和长度格式。

    • #address-cells = :子节点的 reg 属性中,地址部分占用1个32位单元。对于I2C设备,这通常是7位从设备地址;
    • #size-cells = :子节点的 reg 属性中没有长度字段(因为I2C设备地址不涉及地址范围);
      
  • interrupt-parent:指定该设备的中断路由到哪个中断控制器。&liointc0 是龙芯2K0300内部的中断控制器节点(即龙芯I/O中断控制器)的标签;
  • interrupts:描述中断线的具体信息;

    • :硬件中断编号(对应I2C1控制器在中断控制器中的编号);
    • IRQ_TYPE_LEVEL_HIGH:中断触发类型,这里定义为高电平触发。该宏在  中定义。
      
  • pinctrl-0:指定设备使用的第一组引脚配置,&i2c1_pins 是另一个设备树节点的标签,该节点描述了I2C1的SCL和SDA引脚应复用为I2C功能,并可能包含上拉等电气属性;
  • pinctrl-names:为引脚的配置状态命名,与 pinctrl-0 对应。"default" 是默认状态,驱动在probe时会自动应用 pinctrl-0 的配置,引脚配置设置为&i2c1_pins;
  • clock-frequency:I2C控制器的输入时钟频率(单位Hz);
  • status:设备状态,此时处于禁用状态。
更多有关设备树相关的内容可以参考:《linux设备树-基础介绍》。
1.3.2 mpu6050@68

  1. zhengyang@ubuntu:~$ cd /opt/2k0300/build-2k0300/workspace/linux-6.12
  2. zhengyang@ubuntu:/opt/2k0300/build-2k0300/workspace/linux-6.12$ vim arch/loongarch/boot/dts/ls2k300_99pi.dtsi
复制代码
修改arch/loongarch/boot/dts/ls2k300_99pi.dtsi:
  1. // 在根节点或合适位置(通常是 &soc 内)引用并启用 I2C1
  2. &i2c1 {
  3.     status = "okay";
  4.         ......
  5.        
  6.     // MPU6050 传感器
  7.     mpu6050@68 {
  8.         compatible = "invensense,mpu6050";
  9.         reg = <0x68>;
  10.         status = "okay";
  11.     };
  12. };
复制代码
通过 &i2c1 引用这个节点,将 status 改为 "okay",同时添加了I2C从设备节点。
其中mpu6050@68设备节点下的compatible名称要和inv_mpu_i2c.c驱动中的inv_mpu_driver.driver.of_match_table里的名称匹配。
  1. /*
  2. * device id table is used to identify what device can be
  3. * supported by this driver
  4. */
  5. static const struct i2c_device_id inv_mpu_id[] = {
  6.         {"mpu6050", INV_MPU6050},
  7.         {"mpu6500", INV_MPU6500},
  8.         {"mpu6515", INV_MPU6515},
  9.         {"mpu6880", INV_MPU6880},
  10.         {"mpu9150", INV_MPU9150},
  11.         {"mpu9250", INV_MPU9250},
  12.         {"mpu9255", INV_MPU9255},
  13.         {"icm20608", INV_ICM20608},
  14.         {"icm20608d", INV_ICM20608D},
  15.         {"icm20609", INV_ICM20609},
  16.         {"icm20689", INV_ICM20689},
  17.         {"icm20600", INV_ICM20600},
  18.         {"icm20602", INV_ICM20602},
  19.         {"icm20690", INV_ICM20690},
  20.         {"iam20680", INV_IAM20680},
  21.         {}
  22. };
  23. MODULE_DEVICE_TABLE(i2c, inv_mpu_id);
  24. static const struct of_device_id inv_of_match[] = {
  25.         {
  26.                 .compatible = "invensense,mpu6050",
  27.                 .data = (void *)INV_MPU6050
  28.         },
  29.         {
  30.                 .compatible = "invensense,mpu6500",
  31.                 .data = (void *)INV_MPU6500
  32.         },
  33.         {
  34.                 .compatible = "invensense,mpu6515",
  35.                 .data = (void *)INV_MPU6515
  36.         },
  37.     ......
  38. }
  39. MODULE_DEVICE_TABLE(of, inv_of_match);
  40. static const struct acpi_device_id inv_acpi_match[] = {
  41.         {"INVN6500", INV_MPU6500},
  42.         { },
  43. };
  44. MODULE_DEVICE_TABLE(acpi, inv_acpi_match);
  45. static struct i2c_driver inv_mpu_driver = {
  46.         .probe                =        inv_mpu_probe,
  47.         .remove                =        inv_mpu_remove,
  48.         .id_table        =        inv_mpu_id,
  49.         .driver = {
  50.                 .of_match_table = inv_of_match,
  51.                 .acpi_match_table = inv_acpi_match,
  52.                 .name        =        "inv-mpu6050-i2c",
  53.                 .pm     =       pm_ptr(&inv_mpu_pmops),
  54.         },
  55. };
复制代码
这样在适配器注册的时候会遍历i2c设备树节点下的所有子设备节点,从而实现注册该控制器下的所有I2C从设备,具体可以参考《I2C适配器注册》源码部分。
1.3.3 i2c1_pins

i2c1_pins定义在arch/loongarch/boot/dts/loongson-2k0300.dtsi:
  1. pinmux: pinmux@16000490 {
  2.         ......
  3.         i2c1_pins: pinmux_G50_G51_as_i2c1 {
  4.                 pinctrl-single,bits = <0xc 0x000000f0 0x000000f0>;
  5.         };
  6.         ......
  7. }
复制代码
这个设备树节点是使用pinctrl-single驱动来配置引脚复用功能的典型写法,这行代码会在寄存器0x1600049c的7:4位写入 0xf,而其他位保持不变,用于将芯片的GPIO50和GPIO51这两个引脚设置为I2C1功能。
4.png
注:pinctrl-single 是一个通用的引脚控制驱动,适用于那些引脚复用寄存器是“单寄存器位域”的芯片。当SoC没有提供复杂的pinctrl框架时,可以直接用这种方式“裸写”寄存器。
二、应用程序

接下来我们在example目录下创建子目录mpu6050_app;
  1. zhengyang@ubuntu:~$ cd /opt/2k0300/loongson_2k300_lib/example
  2. zhengyang@ubuntu:/opt/2k0300/loongson_2k300_lib/example$ mkdir mpu6050_app
复制代码
目录结构如下:
  1. zhengyang@ubuntu:/opt/2k0300/loongson_2k300_lib/example/mpu6050_app$ tree .
  2. .
  3. ├── main.c
  4. └── Makefile
复制代码
2.1 main.c

内核自带驱动把MPU6050注册成IIO传感器,应用层直接读文件即可获取距离。下面编写一个简单的测试应用程序,用于读取数据并打印;
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <fcntl.h>
  4. #include <unistd.h>
  5. #include <string.h>
  6. #include <math.h>
  7. #include <time.h>
  8. #include <sys/time.h>
  9. // 根据你的实际设备路径修改,通常是 iio:device0 或 iio:device1
  10. #define IIO_DEVICE_PATH "/sys/bus/iio/devices/iio:device1"
  11. // 灵敏度配置 (MPU6050 默认配置)
  12. #define ACC_SENSITIVITY 16384.0f
  13. #define GYRO_SENSITIVITY 16.4f
  14. #define GRAVITY 9.8f
  15. #define PI 3.1415926f
  16. // 互补滤波系数
  17. #define ALPHA 0.98f
  18. // --- 全局变量 ---
  19. float gyro_zero_offset_x = 0.0f;
  20. float gyro_zero_offset_y = 0.0f;
  21. float gyro_zero_offset_z = 0.0f;
  22. // 姿态角 (弧度)
  23. float pitch = 0.0f; // 俯仰角
  24. float roll = 0.0f;  // 横滚角
  25. // --- 辅助函数:读取 sysfs 文件 ---
  26. int read_sysfs_int(const char *path)
  27. {
  28.     int fd;
  29.     char buf[32];
  30.     int val;
  31.    
  32.     // 拼接完整路径
  33.     char full_path[256];
  34.     snprintf(full_path, sizeof(full_path), "%s/%s", IIO_DEVICE_PATH, path);
  35.     fd = open(full_path, O_RDONLY);
  36.     if (fd < 0) return 0; // 读取失败返回0
  37.     memset(buf, 0, sizeof(buf));
  38.     read(fd, buf, sizeof(buf) - 1);
  39.     close(fd);
  40.     val = atoi(buf);
  41.     return val;
  42. }
  43. // --- 辅助函数:获取时间差 (秒) ---
  44. double get_time_diff(struct timeval *last, struct timeval *now)
  45. {
  46.     return (now->tv_sec - last->tv_sec) + (now->tv_usec - last->tv_usec) / 1000000.0;
  47. }
  48. // --- 1. 校准陀螺仪 ---
  49. void calibrate_gyro()
  50. {
  51.     printf("正在校准陀螺仪,请保持开发板绝对静止...\n");
  52.     int samples = 500; // 采样次数
  53.     long sum_x = 0, sum_y = 0, sum_z = 0;
  54.     for (int i = 0; i < samples; i++) {
  55.         sum_x += read_sysfs_int("in_anglvel_x_raw");
  56.         sum_y += read_sysfs_int("in_anglvel_y_raw");
  57.         sum_z += read_sysfs_int("in_anglvel_z_raw");
  58.         usleep(2000); // 2ms 间隔
  59.     }
  60.     // 计算平均值作为零偏 (转换为 rad/s)
  61.     gyro_zero_offset_x = (sum_x / (float)samples) / GYRO_SENSITIVITY * (PI / 180.0f);
  62.     gyro_zero_offset_y = (sum_y / (float)samples) / GYRO_SENSITIVITY * (PI / 180.0f);
  63.     gyro_zero_offset_z = (sum_z / (float)samples) / GYRO_SENSITIVITY * (PI / 180.0f);
  64.     printf("校准完成!零偏: X=%.4f, Y=%.4f, Z=%.4f (rad/s)\n",
  65.            gyro_zero_offset_x, gyro_zero_offset_y, gyro_zero_offset_z);
  66. }
  67. // --- 主函数 ---
  68. int main()
  69. {
  70.     struct timeval last_time, current_time;
  71.     double dt;
  72.    
  73.     // 1. 初始化时间
  74.     gettimeofday(&last_time, NULL);
  75.     // 2. 执行校准
  76.     calibrate_gyro();
  77.     printf("开始输出姿态数据 (Pitch: 前后倾, Roll: 左右倾)\n");
  78.     printf("单位: 度 (°)\n");
  79.     while (1) {
  80.         // --- A. 读取原始数据 ---
  81.         int acc_x_raw = read_sysfs_int("in_accel_x_raw");
  82.         int acc_y_raw = read_sysfs_int("in_accel_y_raw");
  83.         int acc_z_raw = read_sysfs_int("in_accel_z_raw");
  84.         int gyro_x_raw = read_sysfs_int("in_anglvel_x_raw");
  85.         int gyro_y_raw = read_sysfs_int("in_anglvel_y_raw");
  86.         int gyro_z_raw = read_sysfs_int("in_anglvel_z_raw");
  87.         // --- B. 单位转换 ---
  88.         // 加速度转 m/s^2
  89.         float acc_x = (acc_x_raw / ACC_SENSITIVITY) * GRAVITY;
  90.         float acc_y = (acc_y_raw / ACC_SENSITIVITY) * GRAVITY;
  91.         float acc_z = (acc_z_raw / ACC_SENSITIVITY) * GRAVITY;
  92.         // 角速度转 rad/s 并减去零偏
  93.         float gyro_x = (gyro_x_raw / GYRO_SENSITIVITY) * (PI / 180.0f) - gyro_zero_offset_x;
  94.         float gyro_y = (gyro_y_raw / GYRO_SENSITIVITY) * (PI / 180.0f) - gyro_zero_offset_y;
  95.         float gyro_z = (gyro_z_raw / GYRO_SENSITIVITY) * (PI / 180.0f) - gyro_zero_offset_z;
  96.         // --- C. 计算时间间隔 dt ---
  97.         gettimeofday(&current_time, NULL);
  98.         dt = get_time_diff(&last_time, &current_time);
  99.         last_time = current_time;
  100.         // 防止 dt 过大导致积分发散(比如调试暂停过)
  101.         if (dt > 0.1) dt = 0.01;
  102.         // --- D. 姿态解算 ---
  103.         
  104.         // 1. 加速度计计算角度 (参考角度)
  105.         // Pitch (绕Y轴旋转)
  106.         float acc_pitch = atan2(acc_x, sqrt(acc_y*acc_y + acc_z*acc_z)) * (180.0f / PI);
  107.         // Roll (绕X轴旋转)
  108.         float acc_roll = atan2(acc_y, sqrt(acc_x*acc_x + acc_z*acc_z)) * (180.0f / PI);
  109.         // 2. 陀螺仪积分 (变化量)
  110.         // 注意:这里简化了坐标系转换,仅适用于小角度或特定安装方向
  111.         float gyro_pitch = gyro_y * (180.0f / PI) * dt;
  112.         float gyro_roll = gyro_x * (180.0f / PI) * dt;
  113.         // 3. 互补滤波融合
  114.         // 新角度 = 0.98 * (旧角度 + 陀螺仪变化) + 0.02 * 加速度计角度
  115.         pitch = ALPHA * (pitch + gyro_pitch) + (1.0f - ALPHA) * acc_pitch;
  116.         roll  = ALPHA * (roll + gyro_roll)  + (1.0f - ALPHA) * acc_roll;
  117.         // --- E. 打印结果 ---
  118.         // \r 用于覆盖当前行,实现刷新效果
  119.         printf("\rPitch: %6.2f° | Roll: %6.2f° | AccZ: %.2f m/s^2", pitch, roll, acc_z);
  120.         fflush(stdout);
  121.         usleep(10000); // 10ms 刷新率 (100Hz)
  122.     }
  123.     return 0;
  124. }
复制代码
2.2 Makefile

  1. all:
  2.         loongarch64-linux-gnu-gcc -o main main.c
  3. clean:
  4.         rm -rf *.o main
复制代码
2.3 编译应用程序

  1. zhengyang@ubuntu:/opt/2k0300/loongson_2k300_lib/example/mpu6050_app$ make
  2. loongarch64-linux-gnu-gcc -o main main.c
  3. zhengyang@ubuntu:/opt/2k0300/loongson_2k300_lib/example/mpu6050_app$ ll
复制代码
三、测试

3.1 烧录设备树

3.1.1 编译设备树

如果需要单独编译设备树,在linux内核根目录执行如下命令:
  1. zhengyang@ubuntu:~$ cd /opt/2k0300/build-2k0300/workspace/linux-6.12
  2. zhengyang@ubuntu:/opt/2k0300/build-2k0300/workspace/linux-6.12$ source ../set_env.sh && make  arch/loongarch/boot/dts/ls2k300_99pi_wifi.dts  dtbs V=1
复制代码
3.1.2 更新设备树

将设备树拷贝到久久派的/opt目录;
  1. zhengyang@ubuntu:/opt/2k0300/build-2k0300/workspace/linux-6.12$ scp arch/loongarch/boot/dts/ls2k300_99pi_wifi.dtb root@172.23.34.188:/opt
复制代码
在久久派使用dd命令烧写设备树到SPI Nor Flash的dtb分区;
  1. [root@LS-GD opt]# dd if=/opt/ls2k300_99pi_wifi.dtb of=/dev/mtdblock3 bs=1
  2. 22124+0 records in
  3. 22124+0 records out
  4. 22124 bytes (22 kB, 22 KiB) copied, 0.500342 s, 44.2 kB/s
复制代码
3.2 安装驱动

由于我们在内核配置环节将驱动配置到内核中了,因此需要重新编译内核并烧录内核。
3.2.1 编译内核

ubuntu宿主接重新编译内核:
  1. zhengyang@ubuntu:/opt/2k0300/build-2k0300/workspace/linux-6.12$ source ../set_env.sh && make uImage -j$(nproc)
复制代码
3.2.2 烧录内核

久久派烧录内核,即将编译生成的uImage(内核镜像)拷贝到/boot目录;
  1. [root@LS-GD ~]# scp zhengyang@172.23.34.187:/opt/2k0300/build-2k0300/workspace/linux-6.12/arch/loongarch/boot/uImage /boot/
  2. [root@LS-GD ~]# reboot
复制代码
3.2.3 查看i2c信息

我们将龙邱MPU6050陀螺仪接入开发板的I2C1接口处,进入I2C总线的控制目录:
  1. #  I2C 总线的控制目录
  2. [root@LS-GD ~]# cd /sys/bus/i2c/
  3. [root@LS-GD i2c]# ls -l
  4. drwxr-xr-x  2 root root     0 Jul 24 20:49 devices
  5. drwxr-xr-x 10 root root     0 Jul 24 20:49 drivers
  6. -rw-r--r--  1 root root 16384 Jul 24 20:51 drivers_autoprobe
  7. --w-------  1 root root 16384 Jul 24 20:51 drivers_probe
  8. --w-------  1 root root 16384 Jul 24 20:49 uevent
复制代码
其中:


  • devices:列出了所有连接在I2C总线上的设备;
  • drivers:列出了所有已注册的I2C驱动程序;
  • drivers_autoprobe、drivers_probe:这些是控制文件,用于手动触发驱动探测或设置自动探测。
进入设备列表目录,查看具体挂载了哪些硬件:
  1. [root@LS-GD i2c]# cd devices/
  2. [root@LS-GD devices]# ls -l
  3. # i2c-1:代表I2C控制器 1-0068:代表连接在 i2c-1 总线上的设备。
  4. lrwxrwxrwx 1 root root 0 Jul 24 20:49 1-0029 -> ../../../devices/platform/2k300-soc/16109000.i2c1/i2c-1/1-0029
  5. lrwxrwxrwx 1 root root 0 Jul 24 20:49 1-0068 -> ../../../devices/platform/2k300-soc/16109000.i2c1/i2c-1/1-0068
  6. lrwxrwxrwx 1 root root 0 Jul 24 20:49 i2c-1 -> ../../../devices/platform/2k300-soc/16109000.i2c1/i2c-1
  7. lrwxrwxrwx 1 root root 0 Jul 24 20:49 i2c-4 -> ../../../devices/platform/2k300-soc/16109000.i2c1/i2c-1/i2c-4
复制代码
进入了这个地址为0x68的设备目录:
  1. [root@LS-GD devices]# cd 1-0068
  2. [root@LS-GD 1-0068]# ls -l
  3. lrwxrwxrwx 1 root root     0 Jul 24 20:54 channel-0 -> ../i2c-4
  4. lrwxrwxrwx 1 root root     0 Jul 24 20:54 driver -> ../../../../../../bus/i2c/drivers/inv-mpu6050-i2c
  5. drwxr-xr-x 3 root root     0 Jul 24 20:49 iio:device1
  6. -r--r--r-- 1 root root 16384 Jul 24 20:49 modalias
  7. -r--r--r-- 1 root root 16384 Jul 24 20:49 name
  8. lrwxrwxrwx 1 root root     0 Jul 24 20:54 of_node -> ../../../../../../firmware/devicetree/base/2k300-soc/i2c1@0x16109000/mpu6050@68
  9. drwxr-xr-x 2 root root     0 Jul 24 20:54 power
  10. lrwxrwxrwx 1 root root     0 Jul 24 20:49 subsystem -> ../../../../../../bus/i2c
  11. -rw-r--r-- 1 root root 16384 Jul 24 20:49 uevent
复制代码
其中:


  • driver:绑定的驱动:内核已经设备和inv-mpu6050-i2c驱动绑定在了一起;
  • iio:device1:生成的输入设备节点;
  • of_node:设备树节点匹配。
3.3 查看iio信息

我们将龙邱MPU6050陀螺仪接入开发板的I2C1接口处, 查看IIO设备相关文件:
  1. [root@LS-GD i2c]# ls  /sys/bus/iio/devices/iio\:device1/ -l
  2. -rw-r--r-- 1 root root 16384 Jul 24 20:56 current_timestamp_clock
  3. -r--r--r-- 1 root root 16384 Jul 24 20:56 in_accel_matrix
  4. -r--r--r-- 1 root root 16384 Jul 24 20:56 in_accel_mount_matrix
  5. -rw-r--r-- 1 root root 16384 Jul 24 20:56 in_accel_scale
  6. -r--r--r-- 1 root root 16384 Jul 24 20:56 in_accel_scale_available
  7. -rw-r--r-- 1 root root 16384 Jul 24 20:56 in_accel_x_calibbias
  8. -rw-r--r-- 1 root root 16384 Jul 24 20:56 in_accel_x_raw
  9. -rw-r--r-- 1 root root 16384 Jul 24 20:56 in_accel_y_calibbias
  10. -rw-r--r-- 1 root root 16384 Jul 24 20:56 in_accel_y_raw
  11. -rw-r--r-- 1 root root 16384 Jul 24 20:56 in_accel_z_calibbias
  12. -rw-r--r-- 1 root root 16384 Jul 24 20:56 in_accel_z_raw
  13. -r--r--r-- 1 root root 16384 Jul 24 20:56 in_anglvel_mount_matrix
  14. -rw-r--r-- 1 root root 16384 Jul 24 20:56 in_anglvel_scale
  15. -r--r--r-- 1 root root 16384 Jul 24 20:56 in_anglvel_scale_available
  16. -rw-r--r-- 1 root root 16384 Jul 24 20:56 in_anglvel_x_calibbias
  17. -rw-r--r-- 1 root root 16384 Jul 24 20:56 in_anglvel_x_raw
  18. -rw-r--r-- 1 root root 16384 Jul 24 20:56 in_anglvel_y_calibbias
  19. -rw-r--r-- 1 root root 16384 Jul 24 20:56 in_anglvel_y_raw
  20. -rw-r--r-- 1 root root 16384 Jul 24 20:56 in_anglvel_z_calibbias
  21. -rw-r--r-- 1 root root 16384 Jul 24 20:56 in_anglvel_z_raw
  22. -r--r--r-- 1 root root 16384 Jul 24 20:56 in_gyro_matrix
  23. -rw-r--r-- 1 root root 16384 Jul 24 20:56 in_temp_offset
  24. -rw-r--r-- 1 root root 16384 Jul 24 20:56 in_temp_raw
  25. -rw-r--r-- 1 root root 16384 Jul 24 20:56 in_temp_scale
  26. -r--r--r-- 1 root root 16384 Jul 24 20:49 name
  27. lrwxrwxrwx 1 root root     0 Jul 24 20:56 of_node -> ../../../../../../../firmware/devicetree/base/2k300-soc/i2c1@0x16109000/mpu6050@68
  28. drwxr-xr-x 2 root root     0 Jul 24 20:56 power
  29. -rw-r--r-- 1 root root 16384 Jul 24 20:56 sampling_frequency
  30. -r--r--r-- 1 root root 16384 Jul 24 20:56 sampling_frequency_available
  31. lrwxrwxrwx 1 root root     0 Jul 24 20:49 subsystem -> ../../../../../../../bus/iio
  32. -rw-r--r-- 1 root root 16384 Jul 24 20:49 uevent
  33. -r--r--r-- 1 root root 16384 Jul 24 20:56 waiting_for_supplier
复制代码
查看传感器名称:
  1. [root@LS-GD i2c]# cat /sys/bus/iio/devices/iio\:device1/name
  2. mpu6050
复制代码
3.3.1 加速度计相关文件

  文件 说明 示例值     in_accel_x_raw X轴加速度原始值 16384   in_accel_y_raw Y轴加速度原始值 -2048   in_accel_z_raw Z轴加速度原始值 16384   in_accel_scale 缩放系数(将原始值转换为g/s^2) 0.000598   in_accel_scale_available 支持的缩放系数 --   in_accel_x_calibbias X轴加速度校准偏置(可写,用于零点校准) 0   in_accel_y_calibbias Y轴加速度校准偏置(可写,用于零点校准) 0   in_accel_z_calibbias Z轴加速度校准偏置(可写,用于零点校准) 0  读取加速度计x轴原始值:
  1. [root@LS-GD i2c]# cat /sys/bus/iio/devices/iio\:device1/in_accel_x_raw
  2. -788
复制代码
读取加速度计y轴原始值:
  1. [root@LS-GD i2c]# cat /sys/bus/iio/devices/iio\:device1/in_accel_y_raw
  2. -206
复制代码
读取加速度计z轴原始值:
  1. [root@LS-GD i2c]# cat /sys/bus/iio/devices/iio\:device1/in_accel_z_raw
  2. 15616
复制代码
查看缩放系数(每一个原始数据LSB代表多少\(m/s^2\))):
  1. [root@LS-GD i2c]# cat /sys/bus/iio/devices/iio\:device1/in_accel_scale
  2. 0.000598
复制代码
查看支持的缩放系数:
  1. [root@LS-GD i2c]# cat /sys/bus/iio/devices/iio\:device1/in_accel_scale_available
  2. 0.000598 0.001196 0.002392 0.004785
复制代码
如何将将读取的原始值转换为实际值呢?

\[Accel(m/s^2)=\frac{Raw_{Value}}{16384.0}×9.8 = Raw_Value \times 0.000598 \]
注意:静止时,Z轴应该约为9.8,X/Y轴约为0。
3.3.2 陀螺仪相关文件

  文件 说明 示例值     in_anglvel_x_raw X轴角速度原始值 -256   in_anglvel_y_raw Y轴角速度原始值 128   in_anglvel_z_raw Z轴角速度原始值 64   in_anglvel_scale 缩放系数(原始值→rad/s) 0.001064724   in_anglvel_scale_available 支持的缩放系数 --   in_anglvel_x_calibbias X轴角速度校准偏置(可写) 0   in_anglvel_y_calibbias Y轴角速度校准偏置(可写) 0   in_anglvel_z_calibbias Z轴角速度校准偏置(可写) 0  读取角速度计x轴原始值:
  1. [root@LS-GD i2c]# cat /sys/bus/iio/devices/iio\:device1/in_anglvel_x_raw
  2. -62
复制代码
读取角速度计y轴原始值:
  1. [root@LS-GD i2c]# cat /sys/bus/iio/devices/iio\:device1/in_anglvel_y_raw
  2. 6
复制代码
读取角速度计z轴原始值:
  1. [root@LS-GD i2c]# cat /sys/bus/iio/devices/iio\:device1/in_anglvel_z_raw
  2. -20
复制代码
查看缩放系数(每一个原始数据LSB代表多少弧度/秒(rad/s)):
  1. [root@LS-GD i2c]# cat /sys/bus/iio/devices/iio\:device1/in_anglvel_scale
  2. 0.001064724
复制代码
查看支持的缩放系数:
  1. [root@LS-GD i2c]# cat /sys/bus/iio/devices/iio\:device1/in_anglvel_scale_available
  2. 0.000133090 0.000266181 0.000532362 0.001064724
复制代码
如何将将读取的原始值转换为实际值呢?

\[Accel(m/s2) = \frac {Raw_{Value}} {16.4} × \frac{\pi}{180} = Raw_Value \times 0.001064724 \]
3.3.3 温度传感器文件

  文件 说明 示例值     in_temp_raw 温度原始值 1638   in_temp_offset 温度偏移 0   in_temp_scale 温度缩放系数 1  3.3.4 采样频率控制

  文件 说明     sampling_frequency 当前采样频率(Hz)   sampling_frequency_available 支持的采样频率列表  当前采样频率:
  1. [root@LS-GD i2c]# cat /sys/bus/iio/devices/iio\:device1/sampling_frequency
  2. 50
复制代码
支持的采样频率列表:
  1. [root@LS-GD i2c]# cat /sys/bus/iio/devices/iio\:device1/sampling_frequency_available
  2. 10 20 50 100 200 500
复制代码
3.3 应用程序测试

3.3.1 校准

校准的核心目的是算出零偏。加速度计校准:


  • 把MPU6050水平静止放在桌面上;
  • 理论上Z轴应该是1g (9.8 m/s² ),X/Y轴是0;
  • 但在代码中,我们通常先不校准加速度计,因为它受重力影响,计算偏置比较麻烦。对于简单的倾角计算,直接用原始数据即可。
陀螺仪校准:


  • 陀螺仪非常容易“漂移”,即使不动,它也会输出比如 10 或 -5 这种小数值,积分后角度会乱飞;
  • 方法:程序启动时,保持开发板绝对静止1-2秒。程序采集几百次数据取平均值,这个平均值就是零偏。以后每次读取数据都要减去这个零偏。
校准程序在我们的main.c文件中已经实现。
3.3.2 测试

我们将龙邱MPU6050陀螺仪接入开发板的I2C1接口处。
在久久派开发板执行如下命令:
  1. [root@LS-GD opt]# scp zhengyang@172.23.34.187:/opt/2k0300/loongson_2k300_lib/example/mpu6050_app/main ./
  2. zhengyang@172.23.34.187's password:
  3. main                                                                          100%   20KB   1.4MB/s   00:00
  4. [root@LS-GD opt]# ./main
复制代码
参考文章
[1] LS2K0300久久派_V1.1板卡使用手册v1.2_20240705.pdf
[2] [Linux IIO驱动
[3] Linux下IIO子系统的使用与源码分析
[4] Linux下IIO子系统驱动
[5] linux IIO驱动框架开发流程说明

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册