本文介绍如何三招搞定Linux SD卡驱动调试。
一、SD卡介绍
SD Card(Secure Digital Memory Card):即安全数码卡,是一种基于半导体快闪存储器的新一代高速存储设备。
SD卡架构如下:

下面简单整理下SD卡的外部引脚、内部寄存器、速度等级和容量等级。
1、外部引脚
SD卡支持三种传输模式:SPI模式、1bit SD模式和4bit SD模式。
在实际项目中,自己主要调试后面两种模式,SD卡共有9个引脚,汇总如下:
| 引脚名称 | 引脚描述 | 备注 |
|---|---|---|
| CLK | 时钟信号 | |
| CMD | 命令/回复引脚 | |
| DATA0 ~ 3 | 数据线 |
1bit SD模式:只使用DATA0,CD:卡检测。4bit SD模式:CD/DATA3:卡检测/数据3 |
| VDD | 电源 | |
| VSS1/2 | 地 |
2、内部寄存器
SD卡相关寄存器整理如下:
| 寄存器名称 | 寄存器描述 |
|---|---|
| OCR(Operating Conditions Register) | 运行条件寄存器 |
| CID(Card IDentification Register) | 卡识号寄存器,每张卡都有唯一的识别号 |
| CSD(Card Specific Data Register) | 描述数据寄存器 |
| SCR(SD Card Configuration Register) | SD卡配置寄存器 |
| RCA(Relative Card Address) | 卡地址寄存器 |
| DSR(Driver Stage Register) | 驱动级寄存器 |
3、速度等级
根据数据传输速度,SD卡有不同的速度等级表示方法。
| 协议规范 | 简介 |
|---|---|
| SD1.0 | 使用X表示不同的速度等级,较少使用。 |
| SD2.0 | 普通卡(Class2、Class4、Class6)和高速卡(Class10)。 |
| SD3.0 | 使用UHS速度等级1和3。 |
| SD4.0 | 使用UHS-II |
4、容量等级
SD卡容量目前支持:SD、SDHC和SDXC。
| SD卡类型 | 协议规范 | 容量大小 | 支持文件格式 |
|---|---|---|---|
| SD | SD1.0 | ~2GB | FAT 12,16 |
| SDHC(SD High Capacity) | SD2.0 | 2GB ~ 32GB | FAT 32 |
| SDXC(SD eXtended Capacity) | SD3.0 | 32GB ~ 2TB | exFAT |
二、SD卡调试
1、原理图
预调驱动,先看原理图。
下图是RK3568和microSD卡槽之间的连接方式。

2、SDCard配置
RK3568 SD卡配置文件:
1)arch/arm64/boot/dts/rockchip/rk3568.dtsi
sdmmc0: dwmmc@fe2b0000 {
compatible = "rockchip,rk3568-dw-mshc",
"rockchip,rk3288-dw-mshc";
reg = <0x0 0xfe2b0000 0x0 0x4000>;
interrupts = <GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>;
max-frequency = <150000000>;
clocks = <&cru HCLK_SDMMC0>, <&cru CLK_SDMMC0>,
<&cru SCLK_SDMMC0_DRV>, <&cru SCLK_SDMMC0_SAMPLE>;
clock-names = "biu", "ciu", "ciu-drive", "ciu-sample";
fifo-depth = <0x100>;
resets = <&cru SRST_SDMMC0>;
reset-names = "reset";
status = "disabled";
};
2)arch/arm64/boot/dts/rockchip/rk3568-firefly-core.dtsi
&sdmmc0 {
max-frequency = <150000000>;
supports-sd;
bus-width = <4>;
cap-mmc-highspeed;
cap-sd-highspeed;
disable-wp;
sd-uhs-sdr104;
vmmc-supply = <&vcc3v3_sd>;
vqmmc-supply = <&vccio_sd>;
pinctrl-names = "default";
pinctrl-0 = <&sdmmc0_bus4 &sdmmc0_clk &sdmmc0_cmd &sdmmc0_det>;
status = "okay";
};
其中:
clocks:表示SD卡控制器时钟、driver和sample时钟。
max-frequency:SD卡的最大运行频率,根据不同的模式进行调整。
supports-sd:表示为SD卡功能,必须添加。否则无法初始化SD卡。
bus-width:SD卡使用4线模式。如果不配置,默认1线模式。
cap-mmc-highspeed/cap-sd-highspeed:支持highspeed的SD卡。
vmmc-supply、vqmmc-supply:SD卡电源域。
SD3.0速度模式:
sd-uhs-sdr12:时钟频率不超过24M,信号电压1.8V
sd-uhs-sdr25:时钟频率不超过50M,信号电压1.8V
sd-uhs-sdr50:时钟频率不超过100M,信号电压1.8V
sd-uhs-ddr50:时钟频率不超过50M,采用双沿采样,信号电压1.8V
sd-uhs-sdr104:时钟频率不超过208M,信号电压1.8V
pinctrl-0:SD卡pinmux配置。
3、SDCard驱动
RK3568驱动文件:drivers/mmc/host/dw_mmc-rockchip.c,主要关注:
static const struct dw_mci_drv_data rk3288_drv_data = {
.caps = dw_mci_rk3288_dwmmc_caps,
.num_caps = ARRAY_SIZE(dw_mci_rk3288_dwmmc_caps),
.set_ios = dw_mci_rk3288_set_ios, ## 配置时钟、总线、电源、片选、时序等
.execute_tuning = dw_mci_rk3288_execute_tuning, ## 调节driver clk和sample clk phase
.parse_dt = dw_mci_rk3288_parse_dt,
.init = dw_mci_rockchip_init,
};
RK3568 SD控制器使用Synopsys IP,主要关注:dw_mci_rk3288_set_ios()和dw_mci_rk3288_execute_tuning()。
系统启动后,可通过如下命令查看SD卡属性(驱动实现:drivers/mmc/core/debugfs.c):
[root@xiaotianbsp:/]# cat /sys/kernel/debug/mmc1/ios
clock: 150000000 Hz
actual clock: 148500000 Hz
vdd: 21 (3.3 ~ 3.4 V)
bus mode: 2 (push-pull)
chip select: 0 (don't care)
power mode: 2 (on)
bus width: 2 (4 bits)
timing spec: 6 (sd uhs SDR104)
signal voltage: 1 (1.80 V)
driver type: 0 (driver type B)
三、SDCard测试
下面测试基于SanDisk Ultra 1 32G SD卡完成。
1、SD卡检测
系统启动后,SD卡启动日志如下:
[ 1.225398] dwmmc_rockchip fe2b0000.dwmmc: Successfully tuned phase to 266
[ 1.225420] mmc1: new ultra high speed SDR104 SDHC card at address aaaa
[ 1.226456] mmcblk1: mmc1:aaaa SD32G 29.7 GiB
[ 1.227643] mmcblk1: p1
如果SD卡没有正常初始化,出现如下日志:
[ 182.501273] mmc_host mmc1: Bus speed (slot 0) = 375000Hz (slot req 400000Hz, actual 375000HZ div = 0)
[ 182.672282] mmc1: error -123 whilst initialising SD card
[ 182.686489] mmc_host mmc1: Bus speed (slot 0) = 375000Hz (slot req 300000Hz, actual 187500HZ div = 1)
[ 182.699318] mmc_host mmc1: Bus speed (slot 0) = 375000Hz (slot req 375000Hz, actual 375000HZ div = 0)
[ 182.717513] mmc_host mmc1: Bus speed (slot 0) = 375000Hz (slot req 200000Hz, actual 187500HZ div = 1)
原因:mmc_sd_init_card()失败。出错信息定义(include/uapi/asm-generic/errno.h):
#define ETIMEDOUT 110 /* Connection timed out */
...
#define ENOMEDIUM 123 /* No medium found */
2、SD卡寄存器
通过如下命令,可以查看SD卡相关寄存器的值。
[root@xiaotianbsp:/]# cd /sys/class/mmc_host/mmc1/mmc1:aaaa
[root@xiaotianbsp:/sys/devices/platform/fe2b0000.dwmmc/mmc_host/mmc1/mmc1:aaaa]# ls
block driver hwrev oemid scr type
cid dsr manfid power serial uevent
csd erase_size name preferred_erase_size ssr
date fwrev ocr rca subsystem
[root@xiaotianbsp:/sys/devices/platform/fe2b0000.dwmmc/mmc_host/mmc1/mmc1:aaaa]# cat cid
03534453443332478554c496d501636f
## 其它寄存器查看方式类似
SD卡其它参数的查看方法如下:
[root@xiaotianbsp:/sys/devices/platform/fe2b0000.dwmmc/mmc_host/mmc1/mmc1:aaaa]# cat erase_size
512
[root@xiaotianbsp:/sys/devices/platform/fe2b0000.dwmmc/mmc_host/mmc1/mmc1:aaaa]# cat fwrev
0x5
[root@xiaotianbsp:/sys/devices/platform/fe2b0000.dwmmc/mmc_host/mmc1/mmc1:aaaa]# cat hwrev
0x8
[root@xiaotianbsp:/sys/devices/platform/fe2b0000.dwmmc/mmc_host/mmc1/mmc1:aaaa]# cat manfid
0x000003
[root@xiaotianbsp:/sys/devices/platform/fe2b0000.dwmmc/mmc_host/mmc1/mmc1:aaaa]# cat name
SD32G
[root@xiaotianbsp:/sys/devices/platform/fe2b0000.dwmmc/mmc_host/mmc1/mmc1:aaaa]# cat oemid
0x5344
[root@xiaotianbsp:/sys/devices/platform/fe2b0000.dwmmc/mmc_host/mmc1/mmc1:aaaa]# cat serial
0x54c496d5
[root@xiaotianbsp:/sys/devices/platform/fe2b0000.dwmmc/mmc_host/mmc1/mmc1:aaaa]# cat type
SD
3、挂载/卸载
SD卡分区挂载命令:
[root@xiaotianbsp:/]# mount -t vfat /dev/mmcblk1p1 /tmp/
[ 244.746983] FAT-fs (mmcblk1p1): utf8 is not a recommended IO charset for FAT filesystems, filesystem will be case sensitive!
[ 244.749331] FAT-fs (mmcblk1p1): Volume was not properly unmounted. Some data may be corrupt. Please run fsck.
[root@xiaotianbsp:/]# mount
...
/dev/mmcblk1p1 on /tmp type vfat (rw,relatime,fmask=0022,dmask=0022,codepage=936,iocharset=utf8,shortname=mixed,errors=remount-ro)
SD卡分区卸载命令:
umount /dev/mmcblk1p1
4、创建分区
使用fdisk命令,可在SD卡上重新创建分区。
[root@xiaotianbsp:/]# fdisk /dev/mmcblk1
...
Command (m for help): p ## 1、查看现有分区
Disk /dev/mmcblk1: 30 GB, 31914983424 bytes, 62333952 sectors
3880 cylinders, 255 heads, 63 sectors/track
Units: cylinders of 16065 * 512 = 8225280 bytes
Device Boot StartCHS EndCHS StartLBA EndLBA Sectors Size Id Type
/dev/mmcblk1p1 0,0,17 1023,254,63 16 62333951 62333936 29.7G c Win95 FAT32 (LBA)
Command (m for help): d ## 2、删除分区
Selected partition 1
Command (m for help): p
Disk /dev/mmcblk1: 30 GB, 31914983424 bytes, 62333952 sectors
3880 cylinders, 255 heads, 63 sectors/track
Units: cylinders of 16065 * 512 = 8225280 bytes
Device Boot StartCHS EndCHS StartLBA EndLBA Sectors Size Id Type
Command (m for help): n ## 3、创建新分区,共创建了2个分区
Command action
e extended
p primary partition (1-4)
p
Partition number (1-4): 1
First cylinder (1-3880, default 1): Using default value 1
Last cylinder or +size or +sizeM or +sizeK (1-3880, default 3880): 1940
Command (m for help): p
Disk /dev/mmcblk1: 30 GB, 31914983424 bytes, 62333952 sectors
3880 cylinders, 255 heads, 63 sectors/track
Units: cylinders of 16065 * 512 = 8225280 bytes
Device Boot StartCHS EndCHS StartLBA EndLBA Sectors Size Id Type
/dev/mmcblk1p1 0,1,1 1023,254,63 63 31166099 31166037 14.8G 83 Linux
Command (m for help): n
Command action
e extended
p primary partition (1-4)
p
Partition number (1-4): 2
First cylinder (1941-3880, default 1941): Using default value 1941
Last cylinder or +size or +sizeM or +sizeK (1941-3880, default 3880): Using default value 3880
Command (m for help): p
Disk /dev/mmcblk1: 30 GB, 31914983424 bytes, 62333952 sectors
3880 cylinders, 255 heads, 63 sectors/track
Units: cylinders of 16065 * 512 = 8225280 bytes
Device Boot StartCHS EndCHS StartLBA EndLBA Sectors Size Id Type
/dev/mmcblk1p1 0,1,1 1023,254,63 63 31166099 31166037 14.8G 83 Linux
/dev/mmcblk1p2 1023,254,63 1023,254,63 31166100 62332199 31166100 14.8G 83 Linux
Command (m for help): w ## 4、保存新分区
5、读写测试
测试SD卡写命令:
[root@RK356X:/]# time dd oflag=direct,nonblock if=/dev/zero of=/dev/mmcblk1p1 bs=1M count=100
100+0 records in
100+0 records out
104857600 bytes (105 MB, 100 MiB) copied, 3.4844 s, 30.1 MB/s
real 0m 3.49s
user 0m 0.00s
sys 0m 0.16s
测试SD卡读命令:
[root@RK356X:/]# time dd iflag=direct,nonblock if=/dev/mmcblk1p1 of=/dev/null bs=1M count=100
100+0 records in
100+0 records out
104857600 bytes (105 MB, 100 MiB) copied, 1.65236 s, 63.5 MB/s
real 0m 1.65s
user 0m 0.00s
sys 0m 0.03s
上面命令中配置的iflag和oflag属性,可以规避文件系统cache,直接读写,不使用buffer cache。
注:转载请标注作者和出处。