Linux I2C Watchdog驱动开发调试

本章以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进行填充。

struct i2c_driver mcu_watchdog_driver = {
    .driver = {
                .name   = "I2C MCU Watchdog",
                .owner = THIS_MODULE,
                .of_match_table = advwdt_dt_ids,
    },
    .probe = i2c_wtd_probe,
    .remove = i2c_wtd_remove,
    .id_table = advwdt_id,

};

static int __init watchdog_driver_init(void)
{
    return i2c_add_driver(&mcu_watchdog_driver);
}

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

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

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

 hdr40_i2c1: i2c@c250000 {
                i2cmcu@58 {
                     compatible = "stm,i2c-watchdog";
                     reg = <0x58>;
                     #address-cells = <1>;
                     #size-cells = <0>;
                     skip_mux_detect = "yes";
                     vcc-supply = <&p3737_vdd_1v8_sys>;
                };
        };

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

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

主要函数:注册,注销。

extern int watchdog_register_device(struct watchdog_device *);
extern void watchdog_unregister_device(struct watchdog_device *);

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

struct mcu_wtd_data {
        struct watchdog_device wdd;
        struct i2c_client *client;
};

实现探测probe函数。

static int i2c_wtd_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
        struct mcu_wtd_data *wd_data;
        int ret = 0;

        if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
                return -ENODEV;

        wd_data = devm_kzalloc(&client->dev, sizeof(struct mcu_wtd_data), GFP_KERNEL);
        if (!wd_data)
                return -ENOMEM;

        wd_data->client = client;
        watchdog_set_drvdata(&wd_data->wdd, wd_data);
        i2c_set_clientdata(client, wd_data);

        wd_data->wdd.info = &jetson_mcu_watchdog_info;
        wd_data->wdd.ops = &jetson_mcu_watchdog_ops;
        wd_data->wdd.max_timeout = MAX_TIMEOUT;
        wd_data->wdd.min_timeout = MIN_TIMEOUT;
        wd_data->wdd.timeout = DEFAULT_TIMEOUT;
        wd_data->wdd.parent = &client->dev;

        ret = watchdog_register_device(&wd_data->wdd);
        if (ret) {
                dev_err(&client->dev, "failed to register watchdog device\n");
                return ret;
        }
        return 0;
}

定义watchdog_ops操作函数。

static const struct watchdog_ops jetson_mcu_watchdog_ops = {
        .owner = THIS_MODULE,
        .start = jetson_mcu_watchdog_start,
        .stop = jetson_mcu_watchdog_stop,
        .ping = jetson_mcu_watchdog_ping,
        .set_timeout = jetson_mcu_watchdog_set_timeout,
};

实现start,stop,ping,set_timeout函数等。

static int jetson_mcu_watchdog_start(struct watchdog_device *wdd)
{
        struct mcu_wtd_data *wd_data = watchdog_get_drvdata(wdd);
        int ret = 0;

        ret = i2c_smbus_write_byte_data(wd_data->client, WATCHDOG_CONTROL_REG, WATCHDOG_ENABLE);
        if (ret < 0) {
                dev_err(&wd_data->client->dev, "failed to start watchdog: %d\n", ret);
                return ret;
        }
        return 0;
}

static int jetson_mcu_watchdog_stop(struct watchdog_device *wdd)
{
        struct mcu_wtd_data *wd_data = watchdog_get_drvdata(wdd);
        int ret = 0;

        ret = i2c_smbus_write_byte_data(wd_data->client, WATCHDOG_CONTROL_REG, WATCHDOG_DISABLE);
        if (ret < 0) {
                dev_err(&wd_data->client->dev, "failed to stop watchdog: %d\n", ret);
                return ret;
        }
        return 0;
}

static int jetson_mcu_watchdog_ping(struct watchdog_device *wdd)
{
        struct mcu_wtd_data *wd_data = watchdog_get_drvdata(wdd);
        int ret = 0;

        ret = i2c_smbus_write_byte_data(wd_data->client, WATCHDOG_TIMEOUT_CLEAR_CONTROL_REG, WATCHDOG_CLEAR);
        if (ret < 0) {
                dev_err(&wd_data->client->dev, "failed to ping watchdog: %d\n", ret);
                return ret;
        }
        return 0;
}
static int jetson_mcu_watchdog_set_timeout(struct watchdog_device *wdd, unsigned int timeout)
{
        struct mcu_wtd_data *wd_data = watchdog_get_drvdata(wdd);
        int ret = 0;

        ret = i2c_smbus_write_byte_data(wd_data->client, WATCHDOG_TIMEOUT_RELOAD_REG, timeout);
        if (ret < 0) {
                dev_err(&wd_data->client->dev, "failed to set timeout watchdog: %d\n", ret);
                return ret;
        }
        wdd->timeout = timeout;
        return 0;
}

注销watchdog函数

static int i2c_wtd_remove(struct i2c_client *client)
{
        struct mcu_wtd_data *wd_data = i2c_get_clientdata(client);

        watchdog_unregister_device(&wd_data->wdd);

        return 0;
}

驱动卸载函数

static void __exit watchdog_driver_exit(void)
{
        i2c_del_driver(&mcu_watchdog_driver);
}

至此,我们的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

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值