当前位置:   article > 正文

Linux I2C Watchdog驱动开发调试_i2c驱动开发mcu

i2c驱动开发mcu

本章以Jetson AGX Orin为例讲解I2C watchdog驱动程序的编程,主要分为以下三个部分。

硬件原理图

Block Diagram

SOC端的#pin脚定义

MCU端#pin脚定义

Linux下的I2C驱动框架

上图描述了linux I2C驱动框架,体系结构在linux中实现相当复杂。如何编写一个linux驱动程序呢?所谓Linux驱动个人认为主要是起承上启下的作用,对上提供接口API给内核,再由内核间接将接口提供给应用层,比如应用层访问/dev/watchdogX文件等;对下控制硬件设备。具体实现的流程套路如下:

1. 确定驱动结构:根据硬件设计结合分层/分离思想确定驱动的基本结构。

2. 确定驱动实例:驱动定义,初始化,注册,注销。

3. 向上提供接口:实现i2c设备的i2c_driver接口以及i2c设备对应的watchdog驱动

4. 向下控制硬件:根据寄存器配置方式实现控制逻辑。

具体代码分析

驱动大致流程:

1) 加载驱动(int函数)

2) 添加i2c驱动

3) 匹配目标硬件设备

4) 探测probe函数

5) 注册watchdog设备

6) 实现watchdog操作集(start,stop,set_timeout等)

7) 注销watchdog设备

8) 卸载驱动

i2c_driver: 代表的一个i2c设备驱动,类似于platform_driver,在i2c_driver注册到内核且名称与设备树匹配一致就会进入到probe函数,驱动卸载时要进入remove函数。所以编写驱动需要将probe,remove和用于匹配硬件的driver进行填充。

  1. struct i2c_driver mcu_watchdog_driver = {
  2. .driver = {
  3. .name = "I2C MCU Watchdog",
  4. .owner = THIS_MODULE,
  5. .of_match_table = advwdt_dt_ids,
  6. },
  7. .probe = i2c_wtd_probe,
  8. .remove = i2c_wtd_remove,
  9. .id_table = advwdt_id,
  10. };
  11. static int __init watchdog_driver_init(void)
  12. {
  13. return i2c_add_driver(&mcu_watchdog_driver);
  14. }

of_match_table:用于匹配设备树信息,匹配的顺序of_match_table> acpi_match_table> id_table>name。

  1. static const struct of_device_id advwdt_dt_ids[] = {
  2. {.compatible = "stm,i2c-watchdog"},
  3. {}
  4. };

设备树:添加硬件信息,slave地址等

  1. hdr40_i2c1: i2c@c250000 {
  2. i2cmcu@58 {
  3. compatible = "stm,i2c-watchdog";
  4. reg = <0x58>;
  5. #address-cells = <1>;
  6. #size-cells = <0>;
  7. skip_mux_detect = "yes";
  8. vcc-supply = <&p3737_vdd_1v8_sys>;
  9. };
  10. };

i2c_client描述挂接在硬件i2c总线上的设备的设备信息,即i2c设备的设备对象。

watchdog_device: watchdog子系统提供了一系列操作,不需要用户再次编写,只需要向相应的结构体填写设备信息。

主要函数:注册,注销。

  1. extern int watchdog_register_device(struct watchdog_device *);
  2. extern void watchdog_unregister_device(struct watchdog_device *);

自定义驱动私有数据结构体。

  1. struct mcu_wtd_data {
  2. struct watchdog_device wdd;
  3. struct i2c_client *client;
  4. };

实现探测probe函数。

  1. static int i2c_wtd_probe(struct i2c_client *client, const struct i2c_device_id *id)
  2. {
  3. struct mcu_wtd_data *wd_data;
  4. int ret = 0;
  5. if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
  6. return -ENODEV;
  7. wd_data = devm_kzalloc(&client->dev, sizeof(struct mcu_wtd_data), GFP_KERNEL);
  8. if (!wd_data)
  9. return -ENOMEM;
  10. wd_data->client = client;
  11. watchdog_set_drvdata(&wd_data->wdd, wd_data);
  12. i2c_set_clientdata(client, wd_data);
  13. wd_data->wdd.info = &jetson_mcu_watchdog_info;
  14. wd_data->wdd.ops = &jetson_mcu_watchdog_ops;
  15. wd_data->wdd.max_timeout = MAX_TIMEOUT;
  16. wd_data->wdd.min_timeout = MIN_TIMEOUT;
  17. wd_data->wdd.timeout = DEFAULT_TIMEOUT;
  18. wd_data->wdd.parent = &client->dev;
  19. ret = watchdog_register_device(&wd_data->wdd);
  20. if (ret) {
  21. dev_err(&client->dev, "failed to register watchdog device\n");
  22. return ret;
  23. }
  24. return 0;
  25. }

定义watchdog_ops操作函数。

  1. static const struct watchdog_ops jetson_mcu_watchdog_ops = {
  2. .owner = THIS_MODULE,
  3. .start = jetson_mcu_watchdog_start,
  4. .stop = jetson_mcu_watchdog_stop,
  5. //.ping = jetson_mcu_watchdog_ping,
  6. .set_timeout = jetson_mcu_watchdog_set_timeout,
  7. };

实现start,stop,set_timeout函数等。

  1. static int jetson_mcu_watchdog_start(struct watchdog_device *wdd)
  2. {
  3. struct mcu_wtd_data *wd_data = watchdog_get_drvdata(wdd);
  4. int ret = 0;
  5. ret = i2c_smbus_write_byte_data(wd_data->client, WATCHDOG_CONTROL_REG, WATCHDOG_ENABLE);
  6. if (ret < 0) {
  7. dev_err(&wd_data->client->dev, "failed to start watchdog: %d\n", ret);
  8. return ret;
  9. }
  10. return 0;
  11. }
  12. static int jetson_mcu_watchdog_stop(struct watchdog_device *wdd)
  13. {
  14. struct mcu_wtd_data *wd_data = watchdog_get_drvdata(wdd);
  15. int ret = 0;
  16. ret = i2c_smbus_write_byte_data(wd_data->client, WATCHDOG_CONTROL_REG, WATCHDOG_DISABLE);
  17. if (ret < 0) {
  18. dev_err(&wd_data->client->dev, "failed to stop watchdog: %d\n", ret);
  19. return ret;
  20. }
  21. return 0;
  22. }

注销watchdog函数

  1. static int i2c_wtd_remove(struct i2c_client *client)
  2. {
  3. struct mcu_wtd_data *wd_data = i2c_get_clientdata(client);
  4. watchdog_unregister_device(&wd_data->wdd);
  5. return 0;
  6. }

驱动卸载函数

  1. static void __exit watchdog_driver_exit(void)
  2. {
  3. i2c_del_driver(&mcu_watchdog_driver);
  4. }

至此,我们的i2c watchdog驱动开发流程已经完毕,接下来就是通过用户层程序使用ioctl()进行访问/dev/watchdog节点来调试。

参考链接:

https://blog.csdn.net/changqing1990/article/details/127511793?spm=1001.2014.3001.5502

https://www.kernel.org/doc/Documentation/watchdog/watchdog-kernel-api.txt

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/羊村懒王/article/detail/713097
推荐阅读
相关标签
  

闽ICP备14008679号