赞
踩
版权声明:本文为博主原创文章,转载请注明出处:https://blog.csdn.net/huang_165/article/details/86550606
环境:
sd卡:罗果 128MB class 6
内核:Linux4.4
class 6是sd卡数据速率标准
我们先看看硬件拓扑图:图片转自http://www.wowotech.net/basic_tech/mmc_sd_sdio_intro.html
MMC的本质是提供一套可以访问固态非易失性存储介质的通信协议(以sdio为接口),从产业化的角度看,这些存储介质一般集成在一个独立的外部模块中(卡、WIFI模组等),通过物理总线和mmc主机控制器、CPU连接。也就是说,WiFi模组也可以模拟为MMC设备。
看下sd卡内部图:图片转自:http://www.wowotech.net/basic_tech/mmc_sd_sdio_intro.html
Memory core,存储介质,一般是NAND flash、NOR flash等;
Memory core interface,管理存储介质的接口,用于访问(读、写、擦出等操作)存储介质;
Card interface(CMD、CLK、DATA),总线接口,外界访问卡内部存储介质的接口,和具体的管脚相连;
Card interface controller,将总线接口上的协议转换为Memory core interface的形式,用于访问内部存储介质;
Power模块,提供reset、上电检测等功能;
寄存器(图中位于Card interface controller的左侧,那些小矩形),用于提供卡的信息、参数、访问控制等功能。
mmc框架在Linux之中体现为:图片转自:http://www.wowotech.net/basic_tech/mmc_sd_sdio_intro.html
对于sd卡这种存储设备来说,在Linux上被归类为块设备,sd卡块设备在Linux中被规划为card level <--> core level <--> host level。文件系统直接访问的是card level,card level通过请求队列来缓存(参考本人写的《编写一个ramdisk块设备驱动...》)。
core level封装存储卡的识别、设置、挂载、读写通用api。
host level则对应于主板上mmc卡控制器。
而card level、core level是由Linux社区维护的,我们不需要修改。只有host level才是我们关心的。host level和具体soc平台相关,soc原厂会提供可用的驱动源码配合相应设备树节点实现sd卡热插拔、读写操作。
也就是说,如果主板上的sd卡的sdio电路、卡检测电路、供电电路和原厂一致的话,我们需要做的是“配置内核、编译驱动“就可以实现任意sd卡的正常挂载、读写了。
card level:代码位于:drivers/mmc/card/
core level:代码位于:drivers/mmc/core/
host level:代码位于:drivers/mmc/host/rk_sdmmc.*、dw_mmc-rockchip.*
所以,只要确保设备树配置正确,通常能正常操作sd卡,下面结合设备树说明文档将设备树相关节点走一遍,然后编译驱动加载到板端运行。附录有调试sd卡信息。
参考文档:Documentation/devicetree/bindings下的rockchip-dw-mshc.txt、mmc.txt、mmc-card.txt、clock-bindings.txt、power_domain.txt。
查找设备树时应该进到Documentation/devicetree/bindings/mmc下查找相关文档说明,比如rk平台下有rockchip-dw-mshc.txt,它告诉我们需要查找的compatible为:
rockchip,rk3399-dw-mshc 位于rk3399.dtsi:308
- sdmmc: dwmmc@fe320000 {
- compatible = "rockchip,rk3399-dw-mshc",
- "rockchip,rk3288-dw-mshc";
- reg = <0x0 0xfe320000 0x0 0x4000>; //由上一层在这里是根节点的#address-cells、#size-cells决定,很明显是支持rk soc的mmc卡控制器的寄存器信息。
- interrupts = <GIC_SPI 65 IRQ_TYPE_LEVEL_HIGH 0>; //mmc卡控制器的中断信息。
- max-frequency = <150000000>; //由mmc.txt知道,这是供给mmc控制器的最大时钟
- assigned-clocks = <&cru HCLK_SD>; //时钟列表,&cru是一个phandle用来列出《cru: clock-controller@ff760000》时钟控制器提供的能力,
- //一般来说,需要 跟进一个assigned-clock-parents来使用,这里列表只有一个可忽略。参考附录
- assigned-clock-rates = <200000000>; //设定的频率,需要和assigned-clocks一一对应,单位hz
- clocks = <&cru HCLK_SDMMC>, <&cru SCLK_SDMMC>, //列出时钟控制器phandle和时钟id对。其中时钟id来源于rk3399-cru.h
- <&cru SCLK_SDMMC_DRV>, <&cru SCLK_SDMMC_SAMPLE>;
- clock-names = "biu", "ciu", "ciu-drive", "ciu-sample"; //时钟对的名字
- fifo-depth = <0x100>; //host层驱动和core层互传数据的缓存深度-256B
- power-domains = <&power RK3399_PD_SD>; //和时钟对类似,&power是一个phandle用来列出《power: power-controller》rk芯片提供的电源管理能力,
- //同样的电源id也来自于rk3399-power.h
- resets = <&cru SRST_SDMMC>; //软复位时钟,用来重置所有块设备(dw_mmc.c--dw_mci_probe函数中有调用)
- reset-names = "reset";
- status = "okay";
- };
第一行的sdmmc是dwmmc@fe320000的一个符号(label)其它节点引用sdmmc就相当于引用dwmmc@fe320000
好的,我们分析下那些节点引用了sdmmc,可以grep &sdmmc查下。同时,给大家分享一个技巧:要是搜出来的文件很多不用怕。
我们先按照文件名排除一些,然后打开arch\arm64\boot\dts\rockchip\.rk3399-sapphire-excavator-linux.dtb.dts.tmp因为我设备树是rk3399-sapphire-excavator-linux。搜下该文件名如:rk3399-sapphire.dtsi看能不能搜到。
引用sdmmc节点位于rk3399-sapphire.dtsi:565
- &sdmmc {
- clock-frequency = <150000000>; //sd卡控制器的工作时钟大小
- clock-freq-min-max = <100000 150000000>; //最大最小值
- supports-sd; //支持sd卡标记
- bus-width = <4>; //sd卡总线(sdio)传输一个word的大小--4B
- cap-mmc-highspeed; //sd卡控制器支持mmc高速卡标记
- cap-sd-highspeed; //卡控制器支持sd高速卡标记
- disable-wp; //禁用写保护标记
- num-slots = <1>; //在主板上接入sd卡控制器的sd卡卡槽数目
- //sd-uhs-sdr104;
- vqmmc-supply = <&vcc_sd>; //电源管理芯片(rk808)到sd卡控制器的电源regulator(可理解为可控通路)
- pinctrl-names = "default";
- pinctrl-0 = <&sdmmc_clk &sdmmc_cmd &sdmmc_cd &sdmmc_bus4>; //pinctrl是一个重要概念,用来指示这个节点引用了那些pin并且那些pin都具有什么功能。
- //在驱动代码中调用devm_pinctrl_get_select、pinctrl_select_state就可实现某组pin的复用设置。可理解为不同pinctrl对应不同方案。
- status = "okay";
- };
到这里,我们发现设置了sd卡控制器的时钟、word widht、控制能力、电源管理。剩下就是我们最直接关系的sdio接口了,也就是sdmmc节点下的pinctrl-0方案。
老套路,搜索下。sdmmc节点下的pinctrl-0方案位于rk3399.dtsi文件中。
再分享一个技巧,pinctrl规定了<n m>为 (0+n)_(A+m/8)(m%8);比如<1 9>极为gpio1_B1
- sdmmc_clk: sdmmc-clk {
- rockchip,pins =
- <4 12 RK_FUNC_1 &pcfg_pull_none>;//gpio4_B3 复用为RK_FUNC_1(根据原理图可知为第一个复用) 默认开漏
- };
-
- sdmmc_cmd: sdmmc-cmd {
- rockchip,pins =
- <4 13 RK_FUNC_1 &pcfg_pull_up>;
- };
-
- sdmmc_cd: sdmcc-cd {
- rockchip,pins =
- <0 7 RK_FUNC_1 &pcfg_pull_up>;
- };
-
- sdmmc_bus4: sdmmc-bus4 {
- rockchip,pins =
- <4 8 RK_FUNC_1 &pcfg_pull_up>, //gpio4_B0 复用为RK_FUNC_1(根据原理图可知为第一个复用) 默认上拉
- <4 9 RK_FUNC_1 &pcfg_pull_up>, //gpio4_B1
- <4 10 RK_FUNC_1 &pcfg_pull_up>, //gpio4_B2
- <4 11 RK_FUNC_1 &pcfg_pull_up>; //gpio4_B3
- };
sd卡原理图:
上面设备树节点描述和我的原理图是匹配的。
好了,其实分析到这里sd卡就能用了,我们貌似没改过设备树,甚至驱动源码都没碰过就弄完了。大家不要觉得奇怪,我本身是一位汽车维修员。。(Linux fan)
所以,我深知Linux底层的发展,为了更多东西模块化、标准化。在这种要求下Linux就会越来越结构化,可以改动的东西很少,绝大部分都由Linux社区那边的大佬维护。我们这些小白就看看代码理解理解代码就好了。
不过,万一程序出问题了或者需要增强、适配一些功能,那么底层开发人员将要在纷繁复杂的源码中理代码,这是一件有挑战的工作。
时钟来源设置:参考clock-bindings.txt
- uart@a000 {
- ...
- clocks = <&osc 0>, <&pll 1>;
- clock-names = "baud", "register";
- assigned-clocks = <&clkcon 0>, <&pll 2>;
- assigned-clock-parents = <&pll 2>; //指定使用上面列表中的哪一个作为时钟来源
- assigned-clock-rates = <0>, <460800>; //设置时钟列表的时钟大小,单位hz
- };
调试sd卡
插卡打印:
- [ 82.959940] rockchip-iodomain ff770000.syscon:io-domains: Setting to 3300000 done //设置vcc电源为3.3v
- [ 82.960632] rockchip-iodomain ff770000.syscon:io-domains: Setting to 3300000 done
- [ 82.971961] mmc_host mmc0: Bus speed (slot 0) = 400000Hz (slot req 400000Hz, actual 400000HZ div = 0)
- [ 83.232097] mmc_host mmc0: Bus speed (slot 0) = 50000000Hz (slot req 50000000Hz, actual 50000000HZ div = 0)
- [ 83.233080] mmc0: new high speed SD card at address 211b //mmc控制器为刚插入的sd卡分配一个地址并告知sd卡,以后将用这个地址和sd卡通信。
- [ 83.235930] mmcblk0: mmc0:211b APPSD 119 MiB
- [ 83.240962] mmcblk0: p1
- [ 83.521626] FAT-fs (mmcblk0p1): utf8 is not a recommended IO charset for FAT filesystems, filesystem will be case sensitive!
- [ 83.538985] FAT-fs (mmcblk0p1): Volume was not properly unmounted. Some data may be corrupt. Please run fsck.
拔卡打印:
[ 76.957384] mmc0: card 211b removed
生成块设备节点:/dev/mmcblk0p1
将sd卡挂载到/sdcard目录
# mkdir /run/sdcard
# mount /dev/mmcblk0p1 /run/sdcard/
# df -h
/dev/mmcblk0p1 118.7M 2.0K 118.7M 0% /run/sdcard
进入/run/sdcard/目录下
# echo 123 > 123.txt
取消挂载,再挂载。
# umount /run/sdcard/
# mount /dev/mmcblk0p1 /run/sdcard/
# cat sdcard/123.txt
123
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。