Linux reset子系统和驱动实例

上篇讲了Linux clock驱动,今天说说Linux的reset驱动。

时钟和复位是两个不同的驱动,但通常都是由负责clock驱动的人,把reset驱动完成。同样,reset驱动也是由芯片厂商去完成的。

Linux reset子系统


reset子系统非常简单,与clock子系统非常类似,但在驱动实现上,reset驱动更简单。

因为clock驱动主要是时钟的实现,涉及到固定时钟、分频、门控等一些时钟的分级关系,需要弄清楚时钟树里每个时钟的关系。

而reset驱动有点相当于clock驱动的门控,它只有复位和解复位两个功能。

类似于clock子系统,reset子系统也分为了consumerprovider,结构体关系如下:

img

consumer

​ reset API接口的使用者,内核提供了统一的reset接口:

devm_reset_control_get(struct device *dev, const char *id)//获取reset句柄
reset_control_deassert(struct reset_control *rstc)//解复位
reset_control_assert(struct reset_control *rstc)//复位
reset_control_reset(struct reset_control *rstc)//先复位,延迟一会,然后解复位

struct reset_control结构体表示一个reset句柄,驱动中使用reset API,需要先获取reset句柄

provider

​ reset提供者,即reset驱动。struct reset_controller_dev结构体代表一个reset控制器,内部包含了reset操作函数集合struct reset_control_ops,注册reset驱动时,需要分配一个struct reset_controller_dev结构体,然后填充成员,最后将该结构体注册。

struct reset_controller_dev{
    const struct reset_control_ops *ops;//复位控制操作函数
    struct list_head list;//全局链表,复位控制器注册后挂载到全局链表
    struct list_head reset_control_head;//各个模块复位的链表头
    struct device *dev;
    int of_reset_n_cells;//dts中引用时,需要几个参数
        
    //通过dts引用的参数,解析复位控制器中相应的参数
    int (*of_xlate)(struct reset_controller_dev *rcdev, const struct of_phandle_args *reset_spec)unsigned int nr_resets;//复位设备个数
}

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);//解复位
    int (*status)(struct reset_controller_dev *rcdev, unsigned long id);//复位状态查询
}

reset复位API说明


devm_reset_control_get

struct reset_control *devm_reset_control_get(struct device *dev, const char *id)
  • 作用:获取相应的reset句柄
  • 参数
    • ​ dev:指向申请reset资源的设备句柄
    • ​ id:指向要申请的reset资源名(字符串),可以为NULL
  • 返回
    • ​ 成功:返回reset句柄
    • ​ 失败:返回NULL

reset_control_deassert

int reset_control_deassert(struct reset_control *rstc)
  • 作用:对传入的reset资源进行解复位操作
  • 参数
    • ​ rstc:指向申请reset资源的设备句柄
  • 返回
    • ​ 成功:返回0
    • ​ 失败:返回错误码

reset_control_assert

int reset_control_assert(struct reset_control *rstc)
  • 作用:对传入的reset资源进行复位操作。

参数和返回值与reset_control_deassert相同

reset_control_reset

int reset_control_reset(struct reset_control *rstc)
  • 作用:对传入的reset资源先进行复位操作,然后等待5us,再进行解复位操作。
  • 相当于执行了一遍reset_control_assert后,然后delay一会,再调用reset_control_deassert

reset API使用示例


基本步骤:

1、调用devm_reset_control_get()获取reset句柄

2、调用reset_control_assert()进行复位操作

3、调用reset_control_deassert()进行解复位操作

static int xx_probe(struct platform_device *pdev)
{

 struct device_node* np = pdev->dev.of_node;
 ......

    /* 1、获取reset句柄 */
 host->rstc = devm_reset_control_get(&pdev->dev, np->name);
 if (IS_ERR(host->rstc)) {
  dev_err(&pdev->dev, "No reset controller specified\n");
  return PTR_ERR(host->rstc);
 }

 if (host->rstc) {
        /* 2、复位 */
  ret = reset_control_assert(host->rstc);
  if (ret) {
   dev_err(&pdev->dev, "unable to reset_control_assert\n");
   return ret;
  }
  udelay(1);
 
        /* 3、解复位 */
  ret = reset_control_deassert(host->rstc);
  if (ret) {
   dev_err(&pdev->dev, "unable to reset_control_deassert\n");
   return ret;
  }
 }
 ......
}

reset驱动实例


类似于clock驱动,reset驱动也是编进内核的,在Linux启动时,完成reset驱动的加载。

设备树

reset:reset-controller{
 compatible = "xx,xx-reset";
 reg = <0x0 0xc0000000 0x0 0x1000>;
 #reset-cells = <1>;
};

上述是一个reset控制器的节点,0xc0000000是寄存器基址,0x1000是映射大小。#reset-cells代表引用该reset时需要的cells个数。

例如,#reset-cells = <1>; 则正确引用为:

mmc:mmc@0x12345678{
    ......
    resets = <&reset  0>;//0代表reset设备id,id是自定义的,但是不能超过reset驱动中指定的设备个数
    ......
};

驱动编写

reset驱动编写的基本步骤:

1、实现struct reset_control_ops结构体中的.reset.assert.deassert.status函数

2、分配struct reset_controller_dev结构体,填充opsownernr_resets等成员内容

3、调用reset_controller_register函数注册reset设备

以下是从实际项目中分离出来的reset驱动代码:

#include <linux/of.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/reset-controller.h>
#include <linux/io.h>
#include <linux/delay.h>

// 自定义芯片厂的结构体,保存寄存器基址等信息
struct xx_reset{
 struct reset_controller_dev rcdev;
 void __iomem *base;
    //......
};


static int xx_reset(struct reset_controller_dev *rcdev, unsigned long id)
{
 //操作寄存器:先复位,延迟一会,然后解复位
 return 0;
}

static int xx_reset_assert(struct reset_controller_dev *rcdev, unsigned long id)
{
 //操作寄存器:复位
 return 0;
}

static int xx_reset_deassert(struct reset_controller_dev *rcdev, unsigned long id)
{
 //操作寄存器:解复位
 return  0;
}

static int xx_reset_status(struct reset_controller_dev *rcdev, unsigned long id)
{
 //操作寄存器:获取复位状态
 return 0; 
}

static struct reset_control_ops xx_reset_ops = {
 .rest = xx_rest,
 .assert = xx_reset_asser,
 .deassert = xx_reset_deassert,
 .status = xx_rest_status,
};

static int xx_reset_probe(struct platform_device *pdev)
{
 struct xx_reset *xx_reset;
 struct resource *res;
 
 xx_reset = devm_kzalloc(&pdev->dev, sizeof(*xx_reset), GFP_KERNEL);
 if (!xx_reset)
  return -ENOMEM;

 platform_set_drvdata(pdev, xx_reset);

 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 xx_reset->base = devm_ioremap_resource(&pdev->dev, res);//映射寄存器基址
 if (IS_ERR(xx_reset->base))
  return PTR_ERR(xx_reset->base);

 xx_reset->rcdev.ops = &xx_reset_ops;//reset_ops操作函数集合
 xx_reset->rcdev.owner = THIS_MODULE;
 xx_reset->rcdev.of_node = pdev->dev.of_node;
 xx_reset->rcdev.of_reset_n_cells = 1;  
 xx_reset->rcdev.nr_resets = BITS_PER_LONG;//reset设备个数

 return reset_controller_register(&xx_reset->rcdev);//注册reset controller
 
}


static int xx_reset_remove(struct platform_device *pdev)
{
 struct xx_reste *xx_reset = platform_get_drvdata(pdev);
 
 reset_controller_unregister(&xx_reset->rcdev);
 return 0;
}

static const struct of_device_id ak_reset_of_match[]={
 {.compatible = "xx,xx-reset"},
 {},
};

MODULE_DEVICE_TABLE(of, xx_reset_of_match);

static struct platform_driver xx_reset_driver = {
 .probe = xx_reset_probe,
 .remove = xx_reset_remove,
 .driver = {
  .name = "xx-reset",
  .of_match_table = ak_reset_of_match,
 },
};


module_platorm_driver(xx_reset_driver);

MODULE_LICENSE("GPL");
MODULE_DESCPRIPTION("xx reset controller driver");
MODULE_AUTHOR("xx Microelectronic");
MODULE_VERSION("v1.0.00");
<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
发出的红包

打赏作者

嵌入式Linux充电站

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值