【MMC子系统】四、MMC控制器驱动层

news2025/2/25 13:34:09
img
我的圈子: 高级工程师聚集地
我是董哥,高级嵌入式软件开发工程师,从事嵌入式Linux驱动开发和系统开发,曾就职于世界500强企业!
创作理念:专注分享高质量嵌入式文章,让大家读有所得!
img

文章目录

    • 4.1 通用驱动框架
    • 4.2 注册与注销函数
      • 4.2.1 probe函数
      • 4.2.2 remove函数
    • 4.3 ops函数实现
    • 4.4 PM接口

MMC控制器驱动层一般为chip manufacturer做的事,不同的芯片实现方式不尽相同。

Linux内核源码,相当大的一部分都是由Device Drivers程序代码组成,其次另一大部分就是那些你从来都没有听说过的Filesystem Format组成,真正核心的代码非常短小精悍的。

当然,设备驱动程序也有一套既定的框架,按照框架来编写,实现对应的接口就可以了,在这里,我们主要分析一下MMC控制器驱动的实现框架,不拘泥于细节。

下文以sunxi-mmc.c为例来分析,基于Linux4.19

 

4.1 通用驱动框架

static int sunxi_mmc_probe(struct platform_device *pdev) {
    .....
}

static const struct of_device_id sunxi_mmc_of_match[] = {
    { .compatible = "allwinner,sun4i-a10-mmc", .data = &sun4i_a10_cfg },
    { .compatible = "allwinner,sun5i-a13-mmc", .data = &sun5i_a13_cfg },
    { .compatible = "allwinner,sun7i-a20-mmc", .data = &sun7i_a20_cfg },
    { .compatible = "allwinner,sun8i-a83t-emmc", .data = &sun8i_a83t_emmc_cfg },
    { .compatible = "allwinner,sun9i-a80-mmc", .data = &sun9i_a80_cfg },
    { .compatible = "allwinner,sun50i-a64-mmc", .data = &sun50i_a64_cfg },
    { .compatible = "allwinner,sun50i-a64-emmc", .data = &sun50i_a64_emmc_cfg },
    { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, sunxi_mmc_of_match);


static const struct dev_pm_ops sunxi_mmc_pm_ops = {
    SET_RUNTIME_PM_OPS(sunxi_mmc_runtime_suspend,
               sunxi_mmc_runtime_resume,
               NULL)
};

static struct platform_driver sunxi_mmc_driver = {
    .driver = {
        .name	= "sunxi-mmc",
        .of_match_table = of_match_ptr(sunxi_mmc_of_match),
        .pm = &sunxi_mmc_pm_ops,
    },
    .probe		= sunxi_mmc_probe,
    .remove		= sunxi_mmc_remove,
};
module_platform_driver(sunxi_mmc_driver);

MODULE_DESCRIPTION("Allwinner's SD/MMC Card Controller Driver");
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("David Lanzendörfer <david.lanzendoerfer@o2s.ch>");
MODULE_ALIAS("platform:sunxi-mmc");

这套基本的框架,老生常谈,其主要功能就是:按照of_match_table匹配表,来实现platform_deviceplatform_driver的匹配,然后执行probe函数。

 

4.2 注册与注销函数

static int sunxi_mmc_probe(struct platform_device *pdev) {
    .....
}

static int sunxi_mmc_remove(struct platform_device *pdev) {
    ......
}

比较重要的两个函数,我们一般insmod xxx.ko后,执行完_init函数后,最终如果设备树和驱动匹配成功,会调用probe函数,相同,卸载驱动时,也会调用到remove函数。

4.2.1 probe函数

probe函数很长,我们挑重点来了解

static int sunxi_mmc_probe(struct platform_device *pdev)
{
    struct sunxi_mmc_host *host;
    struct mmc_host *mmc;
    int ret;

    mmc = mmc_alloc_host(sizeof(struct sunxi_mmc_host), &pdev->dev);
    if (!mmc) {
        dev_err(&pdev->dev, "mmc alloc host failed\n");
        return -ENOMEM;
    }
    platform_set_drvdata(pdev, mmc);

    host = mmc_priv(mmc);
    host->dev = &pdev->dev;
    host->mmc = mmc;
    spin_lock_init(&host->lock);

    // 1. 获取设备树资源
    ret = sunxi_mmc_resource_request(host, pdev);
    if (ret)
        goto error_free_host;

    ......

    // 2. 初始化MMC控制器
    mmc->ops		= &sunxi_mmc_ops;
    mmc->max_blk_count	= 8192;
    mmc->max_blk_size	= 4096;
    mmc->max_segs		= PAGE_SIZE / sizeof(struct sunxi_idma_des);
    mmc->max_seg_size	= (1 << host->cfg->idma_des_size_bits);
    mmc->max_req_size	= mmc->max_seg_size * mmc->max_segs;
    /* 400kHz ~ 52MHz */
    mmc->f_min		=   400000;
    mmc->f_max		= 52000000;
    mmc->caps	       |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED |
                  MMC_CAP_ERASE | MMC_CAP_SDIO_IRQ;

    if (host->cfg->clk_delays || host->use_new_timings)
        mmc->caps      |= MMC_CAP_1_8V_DDR | MMC_CAP_3_3V_DDR;

    ret = mmc_of_parse(mmc);
    if (ret)
        goto error_free_dma;

    /* TODO: This driver doesn't support HS400 mode yet */
    mmc->caps2 &= ~MMC_CAP2_HS400;

    ret = sunxi_mmc_init_host(host);
    if (ret)
        goto error_free_dma;
    
    .......

    // 3. 将mmc控制器加入到子系统中
    ret = mmc_add_host(mmc);
    if (ret)
        goto error_free_dma;

    dev_info(&pdev->dev, "initialized, max. request size: %u KB%s\n",
         mmc->max_req_size >> 10,
         host->use_new_timings ? ", uses new timings mode" : "");

    return 0;

error_free_dma:
    dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
error_free_host:
    mmc_free_host(mmc);
    return ret;
}


函数作用:从设备树获取配置信息,并初始化mmc控制器,最后将mmc加入到子系统中。

上面代码已经作了简单注释

4.2.2 remove函数

remove函数看起来就比较简单了,就是probe函数的反操作

static int sunxi_mmc_remove(struct platform_device *pdev)
{
    struct mmc_host	*mmc = platform_get_drvdata(pdev);
    struct sunxi_mmc_host *host = mmc_priv(mmc);

    // 1. 移除子系统
    mmc_remove_host(mmc);
    pm_runtime_force_suspend(&pdev->dev);
    disable_irq(host->irq);
    sunxi_mmc_disable(host);
    dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
    
    // 2. 释放mmc内存
    mmc_free_host(mmc);

    return 0;
}

函数作用:将mmc移除子系统,并释放内存。

 

更多干货可见:高级工程师聚集地,助力大家更上一层楼!

 

4.3 ops函数实现

了解过基本驱动框架的都知道,最为核心的就是ops相关的接口了,上层调用底层代码,全靠它。

probe函数中,我们看到mmc->ops = &sunxi_mmc_ops的代码,就是注册了ops结构体,最后通过mmc_add_host接口,打通核心层与MMC控制器驱动层的界限。

static const struct mmc_host_ops sunxi_mmc_ops = {
    .request	 = sunxi_mmc_request,
    .set_ios	 = sunxi_mmc_set_ios,
    .get_ro		 = mmc_gpio_get_ro,
    .get_cd		 = mmc_gpio_get_cd,
    .enable_sdio_irq = sunxi_mmc_enable_sdio_irq,
    .start_signal_voltage_switch = sunxi_mmc_volt_switch,
    .hw_reset	 = sunxi_mmc_hw_reset,
    .card_busy	 = sunxi_mmc_card_busy,
};

  • .request:上层发送命令请求
  • .set_ios:上层设置时钟频率,总线数量的接口
  • .get_ro:表示卡的读写状态
  • .get_cd:检测卡是否存在的接口
  • .enable_sdio_irq:提供给上层打开sdio中断的接口
  • .hw_reset:硬件重置接口
  • .card_busy:反映卡的状态接口

具体怎么实现,就是chip manufacturer做的事情,我们这里只需要知道,上层通过封装的接口,最终通过ops->xxx函数来将控制寄存器进行数据传输。

4.4 PM接口

PM就是我们说的Power Manager电源管理,用于功耗控制。

#ifdef CONFIG_PM
static int sunxi_mmc_runtime_resume(struct device *dev)
{
    struct mmc_host	*mmc = dev_get_drvdata(dev);
    struct sunxi_mmc_host *host = mmc_priv(mmc);
    int ret;

    ret = sunxi_mmc_enable(host);
    if (ret)
        return ret;

    sunxi_mmc_init_host(host);
    sunxi_mmc_set_bus_width(host, mmc->ios.bus_width);
    sunxi_mmc_set_clk(host, &mmc->ios);
    enable_irq(host->irq);

    return 0;
}

static int sunxi_mmc_runtime_suspend(struct device *dev)
{
    struct mmc_host	*mmc = dev_get_drvdata(dev);
    struct sunxi_mmc_host *host = mmc_priv(mmc);

    /*
     * When clocks are off, it's possible receiving
     * fake interrupts, which will stall the system.
     * Disabling the irq  will prevent this.
     */
    disable_irq(host->irq);
    sunxi_mmc_reset_host(host);
    sunxi_mmc_disable(host);

    return 0;
}
#endif

其主要功能就是:确保休眠时,所有外设的时钟使能需要关闭,来确保功耗最低。

 

MMC控制器驱动就是就是这么简单,不需要过多了解的,咱们就先不关心,聚焦于整个框架。

img
欢迎关注 公号&星球【嵌入式艺术】,董哥原创!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1373403.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

网络服务DHCP与DNS

一 DHCP的工作原理&#xff08;租约过程&#xff09; 分类 1&#xff09;自动分配&#xff1a;分配到一个IP地址后永久使用 &#xff08;2&#xff09;手动分配&#xff1a;由DHCP服务器管理员指定IP&#xff08;打印机、报销系统&#xff09;把mac地址和ip地址做一个一一对…

猫粮对比:性价比高的主食冻干猫粮推荐

虽然很多铲屎官可能认为给猫咪喂猫粮就足够了&#xff0c;但实际上猫咪对蛋白质的需求很高&#xff0c;并且作为肉食动物&#xff0c;它们更喜欢肉的味道。而冻干猫粮是采用低温和真空干燥处理技术将鲜肉制成&#xff0c;去除水分并保持蛋白质等营养物质不变性&#xff0c;同时…

办公自动应用,HR大屏可视化模板

大家可以进行资料下载。 完整的案例。 AIGC ChatGPT 职场案例 AI 绘画 与 短视频制作 PowerBI 商业智能 68集 数据库Mysql 8.0 54集 数据库Oracle 21C 142集 Office 2021实战应用 Python 数据分析实战&#xff0c; ETL Informatica 数据仓库案例实战 Excel 2021实操 100集&a…

premiere简约大气3D动画logo片头Pr模板Mogrt免费下载

Premiere简约大气3D动画logo片头pr模板mogrt下载&#xff0c;无需插件&#xff0c;高清分辨率&#xff0c;易于自定义&#xff0c;包括教程&#xff0c;不包括音频和图像。免费下载&#xff1a;https://prmuban.com/37065.html

路由黑洞和黑洞路由的区别

路由黑洞&#xff1a; 路由黑洞是一种现象&#xff0c;一般是在网络边界做汇总回程路由的时候产生的一种不太愿意出现的现象&#xff0c;就是汇总的时候有时会有一些不在内网中存在的网段&#xff0c;但是又包含在汇总后的网段中&#xff0c;如果在这个汇总的边界设备上同时还配…

【JVM】本地方法接口 Native Interface

一、JNI简介 JVM本地方法接口&#xff08;Java Native Interface&#xff0c;JNI&#xff09;是一种允许Java代码调用本地方法&#xff08;如C或C编写的方法&#xff09;的机制。这种技术通常用于实现高性能的计算密集型任务&#xff0c;或者与底层系统库进行交互。 二、JNI组…

西门子S7-1200与S7-300PLC的九大不同点

S7-1200作为新推出的紧凑型控制器&#xff0c;其产品定位在原有的SIMATIC S7-200和S7-300之间&#xff0c;它与S7-300的区别主要体现在硬件、通信、工程、存储器、功能块、计数器、定时器、工艺功能等方面。 一、硬件的区别 在硬件扩展方面&#xff0c;S7-300的主机架多支持八…

现代密码学 考点复盘

现代密码学 考点汇总&#xff08;上&#xff09; 写在最前面考试范围一、给一个简单的方案&#xff0c;判断是否cca安全二、随机预言机模型之下的简单应用 考试题目1.证明CBC方案是CPA安全的2. 证明哈希函数的抗碰撞性3. CBC-MAC安全&#xff1a;证明CPA安全的对称密钥加密方案…

Digital Audio (HDMI)未插入 用Hdmi连接电脑 显示高清数字音频未插入 win10电脑没声音,喇叭上一个叉❌

先说结论&#xff0c;出现这些问题的原因&#xff1a; 未插入音频设备或者硬件问题&#xff08;10%&#xff09;设置错误&#xff0c;未使用显示器音频 &#xff08;30%&#xff09;音频驱动不兼容或者没有驱动&#xff08;50%&#xff09;其他驱动有问题 &#xff08;10%&…

YOLOv8改进 | 二次创新篇 | 结合iRMB和EMA形成全新的iEMA机制(全网独家创新)

一、本文介绍 本文给大家带来的改进机制是二次创新的机制,二次创新是我们发表论文中关键的一环,为什么这么说,从去年的三月份开始对于图像领域的论文发表其实是变难的了,在那之前大家可能搭搭积木的情况下就可以简单的发表一篇论文,但是从去年开始单纯的搭积木其实发表论…

自动化测试框架pytest系列之基础概念介绍(一)

如果你要打算学习自动化测试 &#xff0c;无论是web自动化、app自动化还是接口自动化 &#xff0c;在学习的道路上&#xff0c;你几乎会遇到pytest这个测试框架&#xff0c;因为自动化编写没有测试框架&#xff0c;根本玩不了 。 如果你已经是一位自动化测试人员 &#xff0c;…

效率交响曲:AIOps 协调卓越运营

作者&#xff1a;来自 Elastic Priscilla_Parodi ​ 在我们探索 AIOps 之前&#xff0c;让我们先澄清一些与不同 Ops 的一些单并非全部相关的关键概念&#xff1a; 1&#xff09;DevOps&#xff1a;开发运维 你可能已经听说过 DevOps。 它是一种通过协作和自动化促进交付来集…

Apollo计算几何算法(一)

Planning模块&#xff0c;路径和速度曲线抽象成折线&#xff08;Polyline&#xff09;&#xff0c;障碍物抽象成多边形&#xff08;Polygon&#xff09;。在碰撞检测、投影计算距离、平滑曲线等方面应用广泛。 1 几何算法 1.1 线段 moudles/common/math/line_segment2d.h n…

怎么投稿各大媒体网站?

怎么投稿各大媒体网站&#xff1f;这是很多写作者及自媒体从业者经常面临的问题。在信息爆炸的时代&#xff0c;如何将自己的文章推送到广大读者面前&#xff0c;成为了一个不可避免的挑战。本文将为大家介绍一种简单有效的投稿方法——媒介库发稿平台发稿&#xff0c;帮助大家…

rke2 Online Deploy Rancher v2.8.0 latest (helm 在线部署 rancher v2.8.0)

文章目录 1. 简介2. 预备条件3. 安装 helm4. 安装 cert-manager4.1 yaml 安装4.2 helm 安装 5. 安装 rancher6. 验证7. 界面预览 1. 简介 Rancher 是一个 Kubernetes 管理工具&#xff0c;让你能在任何地方和任何提供商上部署和运行集群。 Rancher 可以创建来自 Kubernetes 托…

常用的网站

PIXEL MOTION 注册-YesPMP平台 模型下载 - Ourblender - 专业的三维素材库 Vega AI 创作平台 夏沫的AI小站 Tripo AI B站视频下载工具 | 极简纯净

如何通过 Prompt 优化大模型 Text2SQL 的效果

前言 在上篇文章中「大模型LLM在Text2SQL上的应用实践」介绍了基于SQLDatabaseChain的Text2SQL实践&#xff0c;但对于逻辑复杂的查询在稳定性、可靠性、安全性方面可能无法达到预期&#xff0c;比如输出幻觉、数据安全、用户输入错误等问题。 本文将从以下4个方面探讨通过Pr…

gem5学习(11):将缓存添加到配置脚本中——Adding cache to the configuration script

目录 一、Creating cache objects 1、Classic caches and Ruby 二、Cache 1、导入SimObject(s) 2、创建L1Cache 3、创建L1Cache子类 4、创建L2Cache 5、L1Cache添加连接函数 6、为L1ICache和L1DCache添加连接函数 7、为L2Cache添加内存侧和CPU侧的连接函数 完整代码…

大模型学习之书生·浦语大模型4——基于Xtuner大模型微调实战

基于Xtuner大模型微调实战 Fintune简介 海量数据训练的base model指令微调Instructed LLM 增量预训练微调 增量数据不需要问题&#xff0c;只需要答案&#xff0c;只需要陈述类的数据 指令跟随微调 指定角色指定问题给对应的user指定答案给assistant LIaMa2InternLM 不同的模…

【大数据】NiFi 中的处理器(二):PutDatabaseRecord

NiFi 中的处理器&#xff08;二&#xff09;&#xff1a;PutDatabaseRecord 1.基本介绍2.属性配置3.连接关系4.应用场景 1.基本介绍 PutDatabaseRecord 处理器使用指定的 RecordReader 从传入的流文件中读取&#xff08;可能是多个&#xff0c;说数组也成&#xff09;记录。这…