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

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.mzph.cn/news/612520.shtml

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

相关文章

Keil编译生成的bin文件自动以版本号命名

Keil编译程序生成bin文件时&#xff0c;如何自动以版本号命名bin文件 一、目的二、方法三、实现过程1、脚本形式2、可执行文件形式 一、目的 Keil编译程序时&#xff0c;生成的Hex/Bin文件名字是根据Keil中工程配置里定的名字命名。通常代码里会有一个字段专门用来定义软件版本…

权限维持篇

一、Windows 1、 不死马权限维持 1.1 概述 <?php ignore_user_abort(); //关掉浏览器&#xff0c;PHP脚本也可以继续执行. set_time_limit(0);//通过set_time_limit(0)可以让程序无限制的执行下去 $interval 5; // 每隔*秒运行 do { $filename test.php; if(file_exi…

vue3.2引用unplugin-vue-components插入,解放开发中import组件

目录 前言引用unplugin-vue-components插件的优缺点优点缺点 unplugin-vue-components插件引入安装插件配置vite配置更新TypeScript配置使用代码位置 总结Q&A 前言 unplugin-vue-components是一个用于Vue.js项目的插件&#xff0c;特别适用于Vite和Webpack构建工具。它的主…

Java零基础教学文档第三篇:JDBC

今日新篇章 【JDBC】 【主要内容】 JDBC概述 使用JDBC完成添加操作 使用JDBC完成更新和删除 DBUtils的简单封装 使用JDBC完成查询 使用JDBC完成分页查询 常用接口详解 JDBC批处理 SQL注入问题 事务处理解决转账问题 连接池 使用反射对DBUtils再次的封装 BaseDAO的封…

Django报错处理

django.template.exceptions.TemplateDoesNotExist: django/forms/widgets/text.html django.template.exceptions.TemplateDoesNotExist: django/forms/widgets/number.html以上报错是pycharm中创建虚拟环境之后把原本自带的templates文件删除&#xff0c;重新在app01下面创建…

Unity Delaunay三角剖分算法 动态生成

Unity Delaunay三角剖分算法 动态生成 Delaunay三角剖分Delaunay三角剖分 定义Delaunay 边Delaunay 空圆特性 Delaunay 三角形Delaunay 最大化最小角特性 Delaunay 三角形特征Delaunay 算法Delaunay Lawson算法Delaunay Bowyer-Watson算法 Unity Delaunay三角剖分 应用Unity 工…

Linux——firewalld防火墙(二)

一、firewalld高级配置 1、IP地址伪装 地址伪装&#xff08;masquerade):通过地址伪装&#xff0c;NAT设备将经过设备的包转发到指定接收方&#xff0c;同时将通过的数据包的源地址更改为其自己的接口地址。当返回的数据包到达时&#xff0c;会将目的地址修改为原始主机的地址…

CMU15-445-Spring-2023-Project #2 - 前置知识(lec07-010)

Lecture #07_ Hash Tables Data Structures Hash Table 哈希表将键映射到值。它提供平均 O (1) 的操作复杂度&#xff08;最坏情况下为 O (n)&#xff09;和 O (n) 的存储复杂度。 由两部分组成&#xff1a; Hash Function和Hashing Scheme&#xff08;发生冲突后的处理&…

神经辐射场(NeRF)概述

神经辐射场&#xff08;NeRF&#xff09;是一种用于三维场景重建的深度学习算法。它能够从一组稀疏的二维图片中重建出高质量的三维场景。 以下是对NeRF算法的原理和实现方法的详细解释&#xff1a; NeRF算法原理&#xff1a; 基本概念&#xff1a; NeRF算法基于光线追踪的原理…

Unified-IO 2 模型: 通过视觉、语言、音频和动作扩展自回归多模态模型。给大家提前预演了GPT5?

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

从0到1实现html文件转换为markdown文档(进度0.1)

Spider-Man 前言准备环境1、node.js2、git 执行指令顺序报错及其解决方案一、npm 错误&#xff01;可以在以下位置找到此运行的完整日志解决方案 二、没有修改权限解决方案&#xff1a; 注意事项总结 前言 当我们处理文档时&#xff0c;常常会遇到将HTML文档转换为Markdown文档…

残疾人聋哑人专用起床叫醒器震动起床提醒器

残疾人聋人专用起床叫醒器震动起床提醒器是为特殊教育学校提供的一种安全防护设施&#xff0c;符合特教行業標準8.7電教、信息網路設備的規定&#xff0c;系统采用了全自动IP网络控制、每个设备内带有IP地扯能独立控制每一个宿舍和教室&#xff0c;在同一时间内&#xff0c;多功…

TypeScript进阶(二)深入理解装饰器

✨ 专栏介绍 TypeScript是一种由微软开发的开源编程语言&#xff0c;它是JavaScript的超集&#xff0c;意味着任何有效的JavaScript代码都是有效的TypeScript代码。TypeScript通过添加静态类型和其他特性来增强JavaScript&#xff0c;使其更适合大型项目和团队开发。 在TypeS…

【解决】Unity Project 面板资源显示丢失的异常问题处理

开发平台&#xff1a;Unity 2021.3.7f1c1   一、问题描述 在开发过程中&#xff0c;遭遇 Project 面板资源显示丢失、不全的问题。但 Unity Console 并未发出错误提示。   二、解决方案&#xff1a;删除 Library 目录 前往 “工程目录/Library” 删除内部所有文件并重打开该…

【JVM】本地方法接口 Native Interface

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

Python教程16:使用海龟画图turtle画会动的时钟

---------------turtle源码集合--------------- Python教程36&#xff1a;海龟画图turtle写春联 Python源码35&#xff1a;海龟画图turtle画中国结 Python源码31&#xff1a;海龟画图turtle画七道彩虹 Python源码30&#xff1a;海龟画图turtle画紫色的小熊 Python源码29&a…

CRMEB多商户短信开发

在使用CRMEB多商户系统的时候&#xff0c;想要二开使用其他平台的短信&#xff0c;这里以阿里云短信为例的具体实现方法。 一、加载阿里云短信的SDK&#xff0c;执行命令&#xff1a;composer require alibabacloud/dysmsapi-20170525 二、增加阿里云短信的驱动 1.在 crmeb\…

【REST2SQL】07 GO 操作 Mysql 数据库

【REST2SQL】01RDB关系型数据库REST初设计 【REST2SQL】02 GO连接Oracle数据库 【REST2SQL】03 GO读取JSON文件 【REST2SQL】04 REST2SQL第一版Oracle版实现 【REST2SQL】05 GO 操作 达梦 数据库 【REST2SQL】06 GO 跨包接口重构代码 MySQL是一个关系型数据库管理系统&#xf…

什么是编程思路?如何训练提升自己的编程思路?

哈喽&#xff0c;大家上午好呀&#xff01;又和大家如期见面了&#xff01; 今天给大家分享改变编程思路的9条技巧。 1.拆分项目&#xff0c;再编程 先按大类写子程序&#xff0c;例如自动&#xff0c;手动&#xff0c;报警&#xff0c;然后子程序中写FB块&#xff0c;FC程序&…

el-upload实现可替换、删除、预览的图片上传。js 往返缓存(可判断当前页面是不是返回的页面)

el-upload实现可替换、删除、预览的图片上传 组件使用&#xff1a; <template><div><UploadImage sendUrl :limit"1" :size"size" :gifSize"gifSize" v-model"images"></UploadImage> </div> </tem…