Jan 19, 2024
本文阅读量次【MMC子系统】 一、MMC/SD/SDIO介绍 # 1、前言 # 该节学习Linux Kernel的MMC子系统,也称为块设备驱动,正如其名,与字符驱动相比,MMC子系统以块为单位进行操作。
同时,由于MMC Card、SD Card、SDIO Card等设备协议基本大同小异,所以在Linux Kernel中使用MMC子系统来统一管理!
2、MMC/SD/SDIO介绍 # 上面我们了解到,Linux Kernel使用统一的子系统模型来管理MMC、SD、SDIO等设备,那么为什么要这样设计呢?
答案当然是:三者协议有一定的共通性。
MMC(MultiMediaCard)多媒体卡设备,从本质上看,它是一种用于固态非易失性存储的内存卡(memory card)规范,定义了诸如卡的形态、尺寸、容量、电气信号、和主机之间的通信协议等方方面面的内容。
1997年,MMC规范正式发布,至今已经进化出了SD、MicroSD、SDIO、EMMC等多种不同的规范,虽然眼花缭乱,但是追其根源,都源于MMC规范,所以Linux Kernel可以将其统一管理!
MMC:强调的是多媒体存储(MM:MultiMedia)
SD:强调的是安全数据(SD:Secure Digital)
SDIO:强调的是IO接口(IO:Input/Output)
更多干货可见:高级工程师聚集地,助力大家更上一层楼!
3、总线接口 # MMC、SD、SDIO其物理接口也十分相似,我们以MMC为例进行分析。
我们的MMC卡如上图所示,内部我们不展开分析,直接将其作为一个完整的设备来分析。
其通过CLK、CMD、DATA等管脚与我们的SOC通信,两者之间当然少不了Controller了。
把通信总线部分,拿出来看:
CLK:提供SOC和设备之间的通信时钟,常用的通信频率为400KHz(识卡)、25MHz,50MHz
CMD:提供SOC和设备之间的通信命令,标识不同的命令编号,类型多达50多种。
DATA:提供SOC和设备之间的数据通信,其通信总线有8根,可自定义设置,一般默认的是1-bit (默认)模式、4-bit模式和8-bit模式。当然数据线越多,传输越快嘛,但是处理起来也稍微繁琐。
除了上面的一些管脚,当然还少不了VCC、GND等管脚喽,与其它外设不同的是,MMC类的设备,还会有一个检测引脚DET,用于检测是否存在卡设备(热插拔)。
好啦,上面我们对MMC、SD、SDIO进行简单了解,也知道了通信的常用方式与物理接口,当然其最核心在于通信的协议啦!由于协议过于复杂,我们放到后面了解。
4、参考文章 # [1]:http://www.wowotech.net/basic_tech/mmc_sd_sdio_intro.html
欢迎关注【嵌入式艺术】,董哥原创!
Jan 19, 2024
本文阅读量次【LED子系统深度剖析】一、开篇词|Linux驱动开发新手必读 # 1、前言 # 大家好,我是董哥!
俗话说:“万丈高楼平地起”,对于我们刚学习Linux驱动开发的小伙伴,Linux驱动开发的基础至关重要,无论我们是学习51单片机、STM32还是ARM,点灯的地位还是毋庸置疑的。
在Linux驱动开发的学习过程中,点灯对于大多数人来说,对着教程照葫芦画瓢,还是能快速点亮一颗LED灯的,但是你真的明白,一颗小小LED灯的背后,到底执行了哪些动作吗,Linux内核是如何管理的呢?
今天,作为在芯片原厂工作的我,有义务带着大家,深入扒一扒LED子系统的工作原理!
总结系列文章,花费时间较长,希望大家尊重原创!
2、LED子系统开发详细介绍 # 该系列文章整体预览如下:
3、LED子系统开发文章汇总 # 为了方便大家快速找到文章,这里按照学习流程进行汇总,点击即可访问!
章节 内容 1、开篇词 1. 文章总览 2、LED子系统框架分析 2.1 裸机处理 2.2 LED子系统框架 2.3 目录结构及核心文件 3、硬件驱动层详解 3.1 gpio_led_probe分析 3.2 gpio_leds_create分析 3.3 create_gpio_led分析 3.4 数据结构之间的关系,以及实现流程 4、核心层——led-class.c详解 4.1 leds_init分析 4.2 leds_class_dev_pm_ops分析 4.3 led_groups分析 4.4 led class的注册注销分析 5、核心层——led-core.c详解 5.1 led_init_core分析 5.2 led_timer_function分析 5.3 set_brightness_delayed分析 5.4 代码实现流程分析 6、核心层——led-triggers.c详解 6.1 触发器设置相关函数分析 6.2 触发器注册注销函数分析 6.3 闪烁功能相关函数分析 6.4 调用流程分析 7、触发器的实现 7.1 触发器介绍 7.2 heartbeat触发器的注册注销流程 7.
...
Jan 19, 2024
本文阅读量次【MMC子系统】 二、EMMC协议 # 1、前言 # 在上一节,我们知道EMMC、SD、SDIO三种规范都是在MMC规范之上发展而来,协议相差不大,所以Linux Kernel才能使用MMC子系统来统一管理!
下面,我们以MMC协议为例,来了解一下相关协议!
2、EMMC基本了解 # 2.1 物理线路 # 物理接口 接口含义 CLK 时钟线,此信号的每一周期控制命令线上的 1 bit 传输,以及所有数据线上 1 bit(1x) 或 2 bit(2x)传输。 CMD 命令线,此信号是双向命令通道,用于设备初始化和命令传输。CMD信号有两种工 作模式:用于初始化模式开漏模式和快速命令传输推拉模式。 DAT0-7 这些是双向的数据通道。DAT 信号以推拉模式工作。缺省状态,只有DAT0处于推拉模式,DAT1-7处于上拉(内含上拉),进入4bit后,DAT0-3处于推拉 2.2 EMMC相关寄存器了解 # 2.3 其他特性了解 # 读写模式:单块读写,多块读写
寻址方式:字节寻址和扇区寻址,字节寻址允许最大2GB,容量超过2GB的,使用扇区(512B)寻址
电压模式:支持高电压和双电压模式
支持增强分区模式等
3、总线协议 # 3.1 基础了解 # 命令:启动一种操作的Token,命令从主机发往设备,在CMD线路上串行传输。 应答:从设备发往主机作为对上一命令的回答的Token,在CMD线路上串行传输。 数据:在主从机之间双向传输,总线宽度可以是1-bit(缺省)、4-bit 和 8-bit 3.2 命令格式 # 每一个Token,都是由一个起始位(’0’)前导,以一个停止位(’1’)终止。总长度是 48 比特。每一个 Token 都用CRC保护,因此可以检测到传输错误,可重复操作。
命令索引:也就是前面CMDX的0,1,2,3等命令编号。
命令参数:有些命令需要参数,例如地址信息等。
3.3 命令格式 # ① 无应答广播命令(bc)
② 有应答广播命令(bcr)
③ 点对点寻址命令(ac),无DAT数据
...
Jan 19, 2024
本文阅读量次【LED子系统深度剖析】二、LED子系统框架分析 # 1、前言 # 我们学习嵌入式,无论是C51、STM32或者是ARM,都是从点灯开始的,点灯在嵌入式中的地位等同于Hello World在各大语言中的地位!
虽然LED功能简单,但是其麻雀虽小,五脏俱全,在学习Linux驱动开发的过程中,学习LED子系统,往往也能够起到牵一发而动全身的作用,也更有益于大家熟悉驱动开发的框架!
2、LED裸机处理 # 我们在学习Linux驱动框架的时候,第一步要做的就是去掉子系统的面纱,先弄明白裸机处理的流程!
有嵌入式经验的朋友,对LED的裸机在清楚不过了,上面是LED的硬件电路,通常一端接到VCC,一端接到GPIO,当GPIO拉低时,LED亮;当GPIO拉高时,LED灭。
在这里裸机我们不过多了解了,目的在于窥探LED子系统。
3、LED子系统框架 # 框架是什么?
框架是一个规范,为我们开发者增加限制的同时,也是为了更好的开发新的程序,新的功能,其目的主要是:将不变的成分剥离开来,固化进框架,让开发者做最少的事情!
框架所处的位置,正如上图所示,由下往上看:
Hardware:我们的硬件设备,指的是LED 硬件驱动层:是直接操作硬件的实现,用于驱动硬件,实现相应的功能,并且将硬件设备注册进框架之中。 核心层:将LED进行统一管理,提供注册,注销,管理LED等相关接口,起到呈上启下的作用,方便上层调用。 用户层:用户通过sysfs文件系统中对应的文件节点,能够直接控制LED的亮灭。 4、LED子系统目录结构及核心文件 # 了解完LED子系统框架之后,我们来分析一下其相关的目录结构!
ketnel │ └── driver │ │ └── leds │ │ │ ├── Makefile │ │ │ ├── led-core.c │ │ │ ├── led-gpio.c │ │ │ ├── led-class.c │ │ │ ├── led-triggers.c │ │ │ ├── ...... │ │ │ └── trigger │ │ │ │ ├── ledtrig-cpu.
...
Jan 19, 2024
本文阅读量次【MMC子系统】三、MMC子系统框架 # 上章,我们简单了解了EMMC协议,感兴趣的可以查阅一下SD和SDIO的协议,之所以Linux内核能够对SD、SDIO、EMMC进行统一管理,根本原因就是三者协议上的相似性,我们该系列文章均以EMMC为剑,一层层划开包裹着的盔甲。
本系列文章,均以Linux 4.19为参考
1、MMC子系统框架 # 如上图所示,MMC子系统的整体框架包括:MMC Host、MMC Core、MMC Block。我们从下网上看:
MMC HOST:即MMC控制器驱动层,正如其名,该层主要是为了实现MMC控制器的初始化,以及MMC底层的数据收发操作,其直接控制的是底层寄存器,用以产生相应的通信时序。 MMC CORE:即MMC核心层,该层主要起到了承上启下的作用。对下,主要体现在注册MMC总线,实现对MMC device和MMC driver的统一管理;对上,体现在实现MMC通信协议,并向上提供相应的读写操作接口。 MMC BLOCK:即MMC块设备驱动层,其主要作用是屏蔽底层的实现逻辑,将底层抽象为卡设备,并且与虚拟文件系统关联,负责块设备请求的处理以及请求队列的管理,又称为card卡驱动。 哈哈,简单吧,我们刚开始对MMC子系统框架就先了解这么多,不着急,慢慢来。
2、MMC子系统文件结构 # 了解完MMC子系统后,我们看一下MMC驱动在Linux下的目录结构,我们进入到drivers/mmc目录
drivers/mmc/ ├── core ├── block.c ├── bus.c ├── core.c ├── mmc.c ├── mmc_ops.c ├── ...... ├── host ├── sunxi-mmc.c ├── ...... 这里介绍一个方法
如果刚接触的朋友,不知道文件之间的关系是怎么样的,可以通过Makefile和Kconfig文件来大致看一下。
obj-$(CONFIG_MMC) += mmc_core.o mmc_core-y := core.o bus.o host.o \ mmc.o mmc_ops.o sd.o sd_ops.o \ sdio.o sdio_ops.o sdio_bus.o \ sdio_cis.o sdio_io.o sdio_irq.o \ slot-gpio.o 由上面可知,MMC CORE核心层,包括的文件有:core.
...
Jan 19, 2024
本文阅读量次【LED子系统深度剖析】三、硬件驱动层详解 # 上篇文章我们了解了子系统的框架,下面我们来分析驱动框架中每层的实现以及作用。
在LED子系统中,硬件驱动层相关文件在包括:kernel/drivers/leds/ 目录下,其主要的函数有:led-gpio.c、led-xxx.c,其中led-gpio.c为通用的平台驱动程序,led-xxx.c为不同厂家提供的平台驱动程序。
我们在这里主要分析led-gpio.c
1、gpio_led_probe分析 # 打开该文件,直接找到加载驱动的入口函数gpio_led_probe
1.1 相关数据结构 # 1.1.1 gpio_led_platform_data # struct gpio_led_platform_data { int num_leds; const struct gpio_led *leds; #define GPIO_LED_NO_BLINK_LOW 0 /* No blink GPIO state low */ #define GPIO_LED_NO_BLINK_HIGH 1 /* No blink GPIO state high */ #define GPIO_LED_BLINK 2 /* Please, blink */ gpio_blink_set_t gpio_blink_set; }; 结构体名称:gpio_led_platform_data
文件位置:include/linux/leds.h
主要作用:LED的平台数据,用于对LED硬件设备的统一管理
这个结构体用于父节点向子节点传递的数据时使用
1.1.2 gpio_leds_priv # struct gpio_leds_priv { int num_leds; struct gpio_led_data leds[]; }; 结构体名称:gpio_leds_priv
...
Jan 19, 2024
本文阅读量次【MMC子系统】四、MMC控制器驱动层 # 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", .
...
Jan 19, 2024
本文阅读量次【LED子系统深度剖析】四、核心层详解(一) # 1、前言 # 上篇文章我们了解了子系统的硬件驱动层,下面我们来分析驱动框架中核心层的实现以及作用。
在LED子系统框架中,核心层包括几个部分:核心层的实现部分(led-core.c)、sysfs文件节点创建(led-class.c)、触发功能实现(led-triggers.c、driver/leds/triggers/led-xxx.c)
其中,触发功能部分较为独立,我们暂且先不去分析。
我们先从led-class.c文件开始分析
2、leds_init分析 # 该函数其主要是为了创建LED设备文件节点,方便用户通过节点直接访问。
该文件,我们直接拉下底部,我们直接看入口函数:leds_init
2.1 相关数据结构 # 2.1.1 class # /** * struct class - device classes * @name: Name of the class. * @owner: The module owner. * @class_groups: Default attributes of this class. * @dev_groups: Default attributes of the devices that belong to the class. * @dev_kobj: The kobject that represents this class and links it into the hierarchy. * @dev_uevent: Called when a device is added, removed from this class, or a * few other things that generate uevents to add the environment * variables.
...
Jan 19, 2024
本文阅读量次我的圈子: 高级工程师聚集地 创作理念:专注分享高质量嵌入式文章,让大家读有所得! 亲爱的读者,你好: 感谢你对我的专栏的关注和支持,我很高兴能和你分享我的知识和经验。如果你喜欢我的内容,想要阅读更多的精彩技术文章,可以扫码加入我的社群。
欢迎关注【嵌入式艺术】,董哥原创!
Jan 19, 2024
本文阅读量次【LED子系统深度剖析】五、核心层详解(二) # 1、前言 # 上篇文章我们了解了子系统的核心层led-class.c,下面我们来分析驱动框架中核心层的led-core.c实现以及作用。
我们接着从led-core.c文件开始分析
2、led_init_core分析 # 上一篇文章,我们知道在将leds_classdev注册进入子系统后,会调用led_init_core函数,初始化核心层,下面我们以led_init_core该函数为突破口分析。
2.1 相关数据结构 # 2.1.1 work_struct # struct work_struct { atomic_long_t data; struct list_head entry; work_func_t func; #ifdef CONFIG_LOCKDEP struct lockdep_map lockdep_map; #endif }; 结构体名称:work_struct
文件位置:include/linux/workqueue.h.h
主要作用:定义一个工作队列,包括了工作项的状态和数据,以及处理工作项的函数指针,用于实现异步执行任务的功能。在工作队列中,每个工作项都是一个work_struct结构体的实例,通过将工作项添加到工作队列中,可以实现后台执行任务的功能。
2.1.2 timer_list # struct timer_list { /* * All fields that change during normal runtime grouped to the * same cacheline */ struct hlist_node entry; unsigned long expires; void (*function)(struct timer_list *); u32 flags; #ifdef CONFIG_LOCKDEP struct lockdep_map lockdep_map; #endif }; 结构体名称:work_struct
...