linux reset框架

本文深入探讨Linux内核的Reset框架,旨在简化设备驱动程序的复位控制。文章详细介绍了Reset框架的Consumer和Provider角色,以及如何在设备树中定义Reset资源。通过具体的代码示例,展示了Consumer如何使用API进行复位操作,以及Provider如何实现复位逻辑。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

文章参考蜗壳科技

1 前言

Linux kernel为了方便设备驱动的编写,抽象出一个简单的软件框架----reset framework,为reset的provider提供统一的reset资源管理手段,并为reset的consumer(各个硬件模块)提供便捷、统一的复位控制API。reset framework可以分为consumer和provider。provider也就是具体reset动作的提供者,实现了某个模块的具体的reset过程,provider是reset框架代码主要部分;consumer也就是reset framework的使用者,就如在某个外设模块的驱动中,我们需要调用reset函数,驱动模块就是一个consumer

2 consumer

从某一个硬件模块的驱动设计者来看,他的要求很简单:我只是想复位我的硬件,而不想知道到底用什么手段才能复位(例如控制哪个寄存器的哪个bit位,等等)。
这个要求其实体现了软件设计(甚至是任何设计)中的一个最最质朴的设计理念:封装和抽象。对设备驱动来说,它期望看到是“reset”这个通用概念,用这个通用概念去发号施令的话,这个驱动就具备了通用性和可移植性(无论在周围的环境如何变化,“reset”本身不会变化)。而至于怎么reset,是通过寄存器A的bit m,还是寄存器B的bit n,则是平台维护者需要关心的事情(就是本文的reset provider)。
看到这样的要求,Linux kernel说:OK,于是reset framework出场,提供了如下的机制(基于device tree):

1)首先,提供描述系统中reset资源的方法(参考下面第3章的介绍),这样consumer可以基于这种描述在自己的dts node中引用所需的reset信号。

2)然后,consumer设备在自己的dts node中使用“resets”、“reset-names”等关键字声明所需的reset的资源,例如[1](“resets”字段的具体格式由reset provider决定”):

device {                                                                
        resets = <&rst 20>;                                             
        reset-names = "reset";                                          
};

3)最后,consumer driver在需要的时候,可以调用下面的API复位自己(具体可参考“include/linux/reset.h“):

3-a)只有一个reset信号的话,可以使用最简单的device_reset API

int device_reset(struct device *dev);

3-b)如果需要更为复杂的控制(例如有多个reset信号、需要控制处于reset状态的长度的等),可以使用稍微复杂的API

/* 通过reset_control_get或者devm_reset_control_get获得reset句柄 */ 
struct reset_control *reset_control_get(struct device *dev, const char *id);    
void reset_control_put(struct reset_control *rstc);                             
struct reset_control *devm_reset_control_get(struct device *dev, const char *id);

/* 通过reset_control_reset进行复位,或者通过reset_control_assert使设备处于复位生效状态,通过reset_control_deassert使复位失效 */ 
int reset_control_reset(struct reset_control *rstc);                            
int reset_control_assert(struct reset_control *rstc);                           
int reset_control_deassert(struct reset_control *rstc);

3 provider

kernel为reset provider提供的API位于“include/linux/reset-controller.h”中,很简单,无非就是:创建并填充reset controller设备(struct reset_controller_dev),并调用相应的接口(reset_controller_register/reset_controller_unregister)注册或者注销之。

reset controller的抽象也很简单:

struct reset_controller_dev {                                                   
        struct reset_control_ops *ops;                                          
        struct module *owner;                                                   
        struct list_head list;                                                  
        struct device_node *of_node;                                            
        int of_reset_n_cells;                                                   
        int (*of_xlate)(struct reset_controller_dev *rcdev,                     
                        const struct of_phandle_args *reset_spec);              
        unsigned int nr_resets; 
};

ops提供reset操作的实现,基本上是reset provider的所有工作量。

of_xlate和of_reset_n_cells用于解析consumer device dts node中的“resets = ; ”节点,如果reset controller比较简单(仅仅是线性的索引),可以不实现,使用reset framework提供的简单版本----of_reset_simple_xlate即可。

nr_resets,该reset controller所控制的reset信号的个数。

其它字段内部使用,provider不需要关心。
struct reset_control_ops也比较单纯,如下:

struct reset_control_ops {                                                      
        int (*reset)(struct reset_controller_dev *rcdev, unsigned long id);     
        int (*assert)(struct reset_controller_dev *rcdev, unsigned long id);    
        int (*deassert)(struct reset_controller_dev *rcdev, unsigned long id); 
};

reset可控制设备完成一次完整的复位过程。

assert和deassert分别控制设备reset状态的生效和失效。

4 代码实例

有了上述的基本知识之后,我们就可以来完成一个reset framework了

4.1 设备树

首先是device tree中需要对reset进行定义

        rst: reset-controller {
                compatible = "arch64,a10-reset";
                #reset-cells = <2>;
                reg = <0x0 0x91000000 0x0 0x1000>;
        };
                i2c0: i2c@0xA1006000 {
                        compatible = "arch64,a10-i2c";
                        reg = <0 0xA1006000 0 0x100>;
                        interrupt-parent = <&gic>;
                        interrupts = <0 32 4>;
                        clock-frequency = <24000000>;
                        resets = <&rst 0x50 11>;
                        reset-names = "i2c0";
                        status = "disabled";
                };

上面就是一个简单的reset framework 的device tree。
1-5行是reset-controller的内容,第二行是reset controller的匹配字符串,第四行是reset controller的寄存器和大小.
6-15行则是i2c0的device tree描述,其中13行表示的i2c0的reset的名称,便于在驱动中获取,第12行则表示i2c0使用的reset controller ,寄存器的偏移以及那个bit。

4.2 consumer

接下来我们看看代码中如何使用这些信息的
在驱动的probe代码中,驱动需要获取reset的信息

		i2c_dev->i2c_rst =
			devm_reset_control_get(i2c_dev->dev, "i2c0");

i2c_rst是一个reset_control的结构体

struct reset_control *cnn_rst;

对于provider来讲,使用reset framework很简单

static int i2c_reset_assert(struct reset_control *rstc)
{
	int rc = 0;
	rc = reset_control_assert(rstc);
	if (rc < 0) {
		pr_err("%s: failed\n", __func__);
		return rc;
	}

	return rc;
}
static int i2c_reset_assert(struct reset_control *rstc)
{
	int rc = 0;
	rc = reset_control_assert(rstc);
	if (rc < 0) {
		pr_err("%s: failed\n", __func__);
		return rc;
	}

	return rc;
}

static int i2c_hw_reset(struct i2c_dev *i2c_dev)
{
		i2c_reset_assert(i2c_dev->i2c_rst );
		udelay(1);
		i2c_reset_release(i2c_dev->i2c_rst );

}

4.3 provider

reset framework的主要工作由provider来完成,将provider也作为一个驱动来完成,

static const struct of_device_id a10_reset_dt_ids[] = {
        { .compatible = "hobot,a10-reset", },
        { },
};

static struct platform_driver a10_reset_driver = {
        .probe  = a10_reset_probe,
        .driver = {
                .name       = KBUILD_MODNAME,
                .of_match_table = a10_reset_dt_ids,
        },
};

static int __init a10_reset_init(void)
{
    return platform_driver_register(&a10_reset_driver);
}

首先将reset驱动作为一个platform设备驱动进行注册。

static int a10_reset_probe(struct platform_device *pdev)
{
        struct a10_reset_data *data;
        struct resource *res;
        struct device *dev = &pdev->dev;
        struct device_node *np = dev->of_node;
        u32 modrst_offset;

        /*
         * The binding was mainlined without the required property.
         * Do not continue, when we encounter an old DT.
         */
        if (!of_find_property(pdev->dev.of_node, "#reset-cells", NULL)) {
                dev_err(&pdev->dev, "%s missing #reset-cells property\n",
                        pdev->dev.of_node->full_name);
                return -EINVAL;
        }

        data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
        if (!data)
                return -ENOMEM;

        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        data->membase = devm_ioremap_resource(&pdev->dev, res);
        if (IS_ERR(data->membase))
                return PTR_ERR(data->membase);

        spin_lock_init(&data->lock);

        data->rcdev.owner = THIS_MODULE;
        data->rcdev.nr_resets = a10_MAX_NR_RESETS;
        data->rcdev.ops = &a10_reset_ops;
        data->rcdev.of_node = pdev->dev.of_node;
        data->rcdev.of_xlate = a10_reset_of_xlate;
        data->rcdev.of_reset_n_cells = 2;

        return devm_reset_controller_register(dev, &data->rcdev);

}

接下来是驱动的probe代码,获取设备树中的信息。接下来最重要的就是实现第32行的a10_reset_ops结构体

static int a10_reset_assert(struct reset_controller_dev *rcdev,
        unsigned long id)
{
        void __iomem    *regaddr;
        uint32_t reg_val, offset;
        unsigned long flags;
        u8 bit;
        struct a10_reset_data *data = to_a10_reset_data(rcdev);

        if (rcdev == NULL || id < 0)
                return -EINVAL;

        spin_lock_irqsave(&data->lock, flags);
        offset = (id & RESET_REG_OFFSET_MASK) >> RESET_REG_OFFSET_SHIFT;
        regaddr = data->membase + offset;

        reg_val = readl(regaddr);
        bit = (id & RESET_REG_BIT_MASK);
        reg_val |= BIT(bit);
        writel(reg_val, regaddr);

        spin_unlock_irqrestore(&data->lock, flags);

        return 0;
}

static int a10_reset_deassert(struct reset_controller_dev *rcdev,
        unsigned long id)
{
        void __iomem    *regaddr;
        uint32_t reg_val, offset;
        unsigned long flags;
        u8 bit;
        struct a10_reset_data *data = to_a10_reset_data(rcdev);

        if (rcdev == NULL || id < 0)
                return -EINVAL;

        spin_lock_irqsave(&data->lock, flags);
        offset = (id & RESET_REG_OFFSET_MASK) >> RESET_REG_OFFSET_SHIFT;
        regaddr = data->membase + offset;

        reg_val = readl(regaddr);
        bit = (id & RESET_REG_BIT_MASK);
        reg_val &= ~(BIT(bit));
        writel(reg_val, regaddr);

        spin_unlock_irqrestore(&data->lock, flags);
        return 0;
}
static int a10_reset_status(struct reset_controller_dev *rcdev,
        unsigned long id)
{
        return 0;
}
static const struct reset_control_ops a10_reset_ops = {
        .assert     = a10_reset_assert,
        .deassert   = a10_reset_deassert,
        .status     = a10_reset_status,
};

assert和deassert就是对设备树中指定的寄存器和bit位进行操作,以实现模块的reset

<think>好的,用户想了解Linux摄像头驱动框架的学习内容。首先,我需要确定用户的基础水平。可能他已经有一定的Linux驱动开发经验,或者只是刚开始接触。这会影响我回答的深度和侧重点。 接下来,我应该考虑用户的需求是什么。他可能正在开发摄像头相关的项目,或者在学习嵌入式系统时遇到了摄像头驱动的问题。了解他的实际应用场景有助于提供更有针对性的信息。 Linux摄像头驱动框架涉及多个子系统,比如V4L2、媒体控制器、设备树等。我需要分模块讲解,结构清晰,便于用户逐步理解。可能需要从基础概念开始,再深入到各个组件的作用和交互方式。 用户可能需要具体的例子或者学习路径,比如推荐参考资料、代码示例或者调试方法。我应该包括这些内容,帮助他实际动手操作。 另外,常见的问题和调试技巧也是用户关心的部分。比如如何排查摄像头无法识别的问题,或者图像显示异常的处理方法。这些实战经验能增加回答的实用性。 还要注意使用中文,避免专业术语的过度堆砌,用更易懂的语言解释复杂概念。同时,保持回答的结构化,使用标题和列表,让用户更容易浏览和理解。 最后,确认是否覆盖了用户可能关心的所有方面,比如内核版本的影响、社区资源、相关工具等。确保信息全面,帮助用户建立系统的学习框架。</think>Linux摄像头驱动框架学习指南(基于V4L2框架) 一、核心框架组成 1. V4L2(Video4Linux2)架构 - 用户空间接口:通过/dev/videoX设备节点提供ioctl调用 - 核心层:v4l2-core模块(v4l2-dev.c/v4l2-ioctl.c等) - 驱动层:具体摄像头控制器驱动实现 2. 关键数据结构 - struct video_device:代表视频设备实例 - struct v4l2_file_operations:文件操作接口 - struct v4l2_ioctl_ops:控制接口实现 二、驱动开发核心步骤 1. 设备注册流程 ```c struct video_device *vdev = video_device_alloc(); vdev->fops = &my_fops; vdev->ioctl_ops = &my_ioctl_ops; video_register_device(vdev, VFL_TYPE_VIDEO, -1); ``` 2. 实现关键操作 - open/release:设备打开/关闭管理 - querycap:设备能力查询 - enum_fmt:枚举支持的像素格式 - try_fmt/s_fmt:设置视频格式 - reqbufs/qbuf/dqbuf:缓冲区管理 - streamon/streamoff:启停数据流 三、关键子系统集成 1. 与媒体控制器框架交互 - 建立entities间连接(sensor -> CSI -> ISP) - 配置pipeline拓扑结构 2. 时钟与电源管理 - 实现v4l2_subdev_pad_ops - 控制sensor的reset/power引脚 - 配置MIPI时钟参数 四、典型开发流程示例 1. 硬件初始化 - 配置I2C总线(sensor寄存器访问) - 初始化MIPI CSI接口 - 设置DMA传输通道 2. 数据传输实现 ```c struct vb2_queue *q = &my_dev->queue; q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; q->io_modes = VB2_MMAP | VB2_USERPTR; q->ops = &my_vb2_ops; vb2_queue_init(q); ``` 五、调试与测试工具 1. 用户空间工具链 - v4l2-ctl:参数配置与查询 - yavta:RAW数据捕获 - qv4l2:图形化控制工具 2. 内核调试手段 - CONFIG_VIDEO_ADV_DEBUG 编译选项 - v4l2跟踪点(tracepoints) - 动态日志等级控制(videobuf2-core模块) 六、学习路径建议 1. 基础准备 - 掌握Linux设备驱动模型 - 理解字符设备工作原理 - 熟悉设备树绑定方法 2. 推荐学习资料 - 内核文档:Documentation/media/v4l-drivers/ - V4L2规范文档(v4l2-spec.pdf) - 典型驱动参考(如ov5640.c) 七、常见问题解决方案 1. 设备未识别问题 - 检查I2C地址配置 - 验证时钟信号与电源 - 确认设备树节点正确性 2. 图像异常处理 - 验证MIPI时序配置 - 检查DMA缓冲区对齐 - 调试CSI错误中断 最新注意事项(2024年): 1. 新内核趋势:逐步采用V4L2框架的异步子设备注册 2. 硬件加速:关注V4L2 MEM2MEM设备开发模式 3. 安全要求:强化用户空间缓冲区访问验证 建议实践项目: 开发USB摄像头驱动 → 实现MIPI-CSI摄像头支持 → 集成ISP图像处理管线
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值