Linux下sdio设备扫描过程

前言

本文基于Linux version 3.10.52版本代码分析sdio设备的扫描过程,同时选择sdio wifi设备作为分析对象,在分析过程中,附带上sdio的协议内容,帮助初学人员学习sdio协议基本内容及sdio的扫描过程。

1 SDIO设备接口及内部结构

本节介绍SDIO设备的硬件接口及内部寄存器等。

1.1 SDIO设备硬件接口

SDIO设备的硬件操作接口有3种方式:
A、 SPI mode;
B、 SD 1-bit mode;
C、 SD 4-bit mode;
这3种方式的硬件接口及管脚定义如下面图、表:
这里写图片描述
图1 SDIO硬件接口(连接了2组sdio设备)
这里写图片描述
表1 SDIO各种模式管脚对应关系
本文不对SPI mode的相关内容介绍,有关SPI mode的内容不做过多说明。

1.2 SDIO设备内部寄存器

下图为SDIO设备内部操作空间的映射表。
这里写图片描述
图2 sdio internal map
CIA: Common I/O Area
CCCR:Card Common Control Registers
FBR :Function Basic Registers
CIS:Card Information Structure
RFU: Reserved for Future Use
图中两个蓝色框表示的是一个东西,只是一个图更详细描述内部各个地址段的用途。CIS区域除了保存CIS信息外,sdio设备商也把设备使用的寄存器定义在这个区域,或作为RAM存储运行固件(fw)等。CSA区域的读写,需要借助FBR中的寄存器0xn0Ch—0xn0Eh、0xn0Fh(n等1~7)。
其中function number的个数随着不同设备而不同,并不一定都需要7个function,如后面介绍的扫描设备就只有function1、function2两个function。
CCCR、FBR、CIS区域的寄存器或数据存储格式如下面各个表,各个寄存器bit的意义在后面介绍扫描过程时再结合实际说明:
这里写图片描述
表2 Card Common Control Registers (CCCR)
这里写图片描述
表3 Function Basic Information Registers (这里为function1的FBR)
在FBR寄存器中,0Ch—0Eh、0Fh用于读写CSA区域,操作方式如下面描述:
In order for the host to access a function’s CSA, it first shall determine if that function supports a CSA. The host reads the FBR register at address 00n00h where n is the function number (1 to 7). If bit 6=1, then the function supports a CSA and the host enables access by writing bit 7=1. The next step is for the host to load the 24 bit address to start reading or writing. This is accomplished by writing the 24 bits (A23-0) to registers
00n0Ch to 00n0Eh where n is the function number (1 to 7). Once the start address is written, data can be read or written by accessing register 00n0Fh, the CSA data window register. If more than 1 byte needs to be read or written, an extended I/O command (byte or block) can be performed with an OP code of 0 (fixed address). The address pointer shall be automatically incremented with each access to the window register, so the access will be to sequential addresses within the CSA. Once the operation is complete, the address of the NEXT operation shall be held in the 24 bit address register for the host to read.

CIS区域从0x001000h~0x017FFFh,其中CIS信息采用一种结构化的信息存储,每一条信息作为一个tuple结构体,该结构体中第一个字节为tuple code,用于表示该tuple存储的信息类型,第二个字节表示该tuple的长度n,同时也指明下一个tuple的开始位置,跟在长度后面就是n bytes的tuple内容,如下表所示。整个CIS信息最后是以一个0xFF字节作为结束符。
这里写图片描述
表4 Basic Tuple Format
SDIO支持的tuple code如下表:
这里写图片描述
表5 Tuples Supported by SDIO Cards
这里写图片描述
表6 CISTPL_MANFID: Manufacturer Identification Tuple
如上表,sdio设备的id就存储在tuple code(0x20)的tuple中,就是后续扫描中说到的vendor id与device id。
这里写图片描述
表7 CISTPL_FUNCID Tuple

在tuple code为0x22的CISTPL_FUNCE中,有3中不同类型的tuple,所在在tuple结构体中增加了一个TYPE字节,但又为了与其它tuple的兼容,把TYPE字段放在了tuple的第3个字节,同时这个字节是包含在长度n内的。如下表:
这里写图片描述
表8 CISTPL_FUNCE Tuple General Structure
这里写图片描述
表9 TPLFID_FUNCTION Tuple for Function 0 (Common:type 0x00)
这里写图片描述
表10 TPLFID_FUNCTION Tuple for Function 1-7
(High Power Tuple:type 0x01)
这里写图片描述
表11 TPLFID_FUNCTION Tuple for Function 1-7
(Power State Tuple:type 0x02)
这里写图片描述
表12 CISTPL_SDIO_STD: Tuple Reserved for SDIO Cards
这里写图片描述
表13 CISTPL_SDIO_EXT: Tuple Reserved for SDIO Cards

2 SDIO状态、命令及应答

本节介绍SDIO总线状态,各个命令及命令的应答。

2.1 SDIO总线状态

SDIO总线有各种状态,在不同的状态下,响应的命令也不相同。若在一种状态下,接收到了一个该状态下不响应的命令,就不会回应该命令,也不进行任何处理。如在“Initialization State”接收到CMD52,就不会回应,也不会执行任何动作。
这里写图片描述
图3 总线状态图及可接收的命令

2.2 SDIO命令

 SDIO的命令、数据收发几种方式(如下图):
A、 单命令没有回应;
B、 命令加回应;
C、 命令+回应+数据(1个block,指定发送1个block);
D、 命令+回应+数据(多个block,指定发送n个block);
E、 命令+回应+数据+命令+回应(后一个命令+回应用于结束数据传输,发送n个block,直到发送停止传输命令为止);
这里写图片描述
图5 (Multiple) Block Read Operation
这里写图片描述
图6 (Multiple) Block Write Operation

 命令、应答、data之间的时间关系
这里写图片描述
这里写图片描述
图7 timing value

 个别命令、应答格式
CMD3(SEND_RELATIVE_ADDR ):
CMD3用于读取卡的RCA(Relative Card Address register ),该命令不需要参数,如下图,Command index为0x03,Argument为0x00。
这里写图片描述
CMD3的Response(R6):
这里写图片描述
CMD5(IO_SEND_OP_COND):
CMD5用于获取OCR(Operation Conditions Register )及设备配置;
这里写图片描述
CMD5的Response(R4)
这里写图片描述
其它位定义如下:
Stuff Bits: Not used, shall be set to 0.
S18R: Switching to 1.8V Request
C: Set to 1 if Card is ready to operate after initialization
Number of I/O Functions:
Indicates the total number of I/O functions supported by this card. The range is
0-7. Note that the common area present on all I/O cards at Function 0 is not
included in this count. The I/O functions shall be implemented sequentially
beginning at function 1.
Memory Present:
Set to 1 if the card also contains SD memory. Set to 0 if the card is I/O only.
OCR的定义如下:
这里写图片描述
表14 OCR Values for CMD5
CMD52(IO_RW_DIRECT):
The IO_RW_DIRECT is the simplest means to access a single register within the total 128K of register space in any I/O function, including the common I/O area (CIA).
这里写图片描述
CMD52的response(R5):
这里写图片描述
其它位定义如下:
R/W Flag: 读写控制为,0:读,1:写;
RAW Flag: 写后回读,当R/W Flag为1时,RAW Flag为1时,写发送完成后,在response中返回写入的数据;
Response Flags:
8 Bits of flag data indicating the status of the SDIO card. Table 5-1 shows the format of these flag bits.

这里写图片描述
表15 Flag Data for IO_RW_DIRECT SD Response

CMD53(IO_RW_EXTENDED):
In order to read and write multiple I/O registers with a single command, a new command,IO_RW_EXTENDED is defined.
这里写图片描述
CMD53的response(R5):
与CMD52的response一样,只是8-bit data field shall be stuff bits and shall be read as 00h。
其它位定义:
Block Mode:表示块读写还是字节读写,0:字节读写;1:块读写;
OP code:读写时地址的变化模式
0:Multi byte R/W to fixed address;
1:Multi byte R/W to incrementing address;
Byte/Block Count:读写的字节或块数;
这里写图片描述

3 SDIO设备的扫描

本文对sdio设备的扫描过程主要是结合sdio命令进行,对于sdio控制器的上电,复位,clock的初始化等不做详细介绍。Linux内核中扫描sdio设备涉及的代码包含在下面目录中:kernel\drivers\mmc\core。

A、 sdio设备扫描从mmc_rescan函数开始,mmc_rescan函数中分别使用400k、300k、200k,100k的速率调用mmc_rescan_try_freq进行扫描,只要扫描到了设备,就会退出扫描。所以如果在400k速率时扫描到了sdio���备,后面3种速率的扫描就不需要再执行。

B、 mmc_rescan_try_freq函数中,先进行sdio_reset,命令如下:
cmd,Arg:0xc00, Cmd:52 /* 读取寄存器06h */
cmd,Arg:0x80000c08, Cmd:52 /* 寄存器06h RES (复位)bit写1 */
可以看到这两个命令都没有回应,这是因为sdio设备刚上电时处于initialization state,对于cmd52命令是不响应的,在这种情况下,这两个命令对sdio设备也是没有任何作用的。如果当前sdio设备处于commond state或transfer state,需要重新扫描sido设备,这两个命令就起作用了,会对sdio设备进行复位;

C、 mmc_go_idle把卡从sd mode切换到spi mode;
cmd,Arg:0x0, Cmd:0 /* 该命令不需要应答 */
这个命令对sd卡来说,作用是切换到idle状态,但对sdio设备来说,就是用于从sd mode切换到spi mode。同时这个命令要起作用,传输时CS管脚还必须要拉低,但在sd mode传输时,CS(对应DATA[3])管脚没有拉低,所以该命令在sd mode传输时也是不起作用的,只有在SPI mode传输时才会起作用。

D、 mmc_send_if_cond
cmd,Arg:0x1aa, Cmd:8
该命令对sdio设备为可选命令,可以不用实现。

从初始化开始到这个命令,对于重新上电了并使用sd mode的sdio设备来说,都是不起作用的,但这些命令的目的是为了兼容sd卡、emmc的扫描,及sdio设备在不同状态、不同模式时的扫描使用。
接下来就是调用mmc_attach_sdio进行实际的sdio设备扫描及初始化。

E、 mmc_attach_sdio函数中,先进行mmc_send_io_op_cond获取配置;
cmd,Arg:0x0, Cmd:5 /* 获取OCR及配置 */
cmd resp, 0:0x20ffff00, 1:0x3f
/* resp中已经把所有数据右移8bit(去掉CRC7和E bit) */
从response中,可以知道,该sdio设备配置:
Functions: 2个(function1, function2);
Memory Present::0 该设备不包含sd memory,sdio设备类型为MMC_TYPE_SDIO;
OCR: ffff00(详细定义见“表14 OCR Values for CMD5”)
获取完配置后,接着调用mmc_sdio_init_card进行sdio设备初始化;

F、 mmc_sdio_init_card函数中,先mmc_send_io_op_cond对sdio设备配置;
cmd,Arg:0x1018000, Cmd:5 /* 设置OCR及请求切换到1.8V */
cmd resp, 0:0xa0ffff00, 1:0x3f /* 回应Card is ready to operate,并不支持切换到1.8V */
response发现sdio设备不支持1.8V,后面不会进行1.8V的切换;同时该sdio设备不支持sd memory,也不需要进行sd memory的相关初始化。

G、 mmc_send_relative_addr获取sdio设备的RCA;
cmd,Arg:0x0, Cmd:3 /* cmd3获取RCA */
0:0x10000, 1:0x3 /* RCA值为1 */

H、 mmc_select_card通过RCA选择sdio设备;
cmd,Arg:0x10000, Cmd:7 /* cmd7的参数只有[31:16]为RCA,其它无效 */
cmd resp, 0:0x1e00, 1:0x7 /* response为Status Register */
response bit[12:9]值为0Fh:CURRENT_STATE,
For an I/O only card, the current state shall be fixed at a value of 0Fh.

I、 sdio_read_cccr读取cccr、Card Capability寄存器:
cmd,Arg:0x0, Cmd:52 /* 读取寄存器cccr */
cmd resp, 0:0x1032, 1:0x34
response bit[13:12] IO_CURRENT_STATE: 0x01,cmd state
response bit[7:4] sdio version: 0x3,version 2.00
response bit[3:0] CCCR Format Version: 0x2 version 2.00

cmd,Arg:0x1000, Cmd:52 /* 读取Card Capability寄存器 */
cmd resp, 0:0x1002, 1:0x34
response bit[1] Support Multiple Block Transfer: 0x1 支持

根据CCCR Format Version >= 2.0,再读取12h(Power Control),13h(Bus Speed Select)寄存器;
cmd,Arg:0x2400, Cmd:52 /* 读取12h(Power Control) */
cmd resp, 0:0x1001, 1:0x34
response bit[0] Support Master Power Control:
The total card power may exceed 720mW

cmd,Arg:0x2600, Cmd:52 /* 读取13h(Bus Speed Select) */
cmd resp, 0:0x1001, 1:0x34
response bit[0] Support High-Speed: 0x1,支持;
response bit[4:2] Bus Speed Select: 0x0,默认最高速率25MHz;
High-speed下速率值:
这里写图片描述
J、 sdio_read_common_cis读取CIS;
先读取Common CIS Pointer
cmd,Arg:0x1200, Cmd:52 /* 读寄存器9 */
cmd resp, 0:0x1070, 1:0x34
cmd,Arg:0x1400, Cmd:52 /* 读寄存器A */
cmd resp, 0:0x1010, 1:0x34
cmd,Arg:0x1600, Cmd:52 /* 读寄存器B */
cmd resp, 0:0x1000, 1:0x34
从3个response中得到Common CIS Pointer:0x1070;
接着从地址0x1070读取CIS数据。
内核代码只解析tuple code为0x15(cistpl_vers_1),0x20(cistpl_manfid), 0x22(cistpl_funce),其中0x22(cistpl_funce)中只解析type为0x0(cistpl_funce_common)、type为0x1(cistpl_funce_func)的数据。至于其它的CIS数据,在后续设备驱动代码中回重新再读一次,再由设备驱动使用。在这里只列出0x20(cistpl_manfid)的读取。
cmd,Arg:0x20e000, Cmd:52 /* 读取寄存器0x1070 */
cmd resp, 0:0x1020, 1:0x34 /* 返回tuple code:0x20 */
cmd,Arg:0x20e200, Cmd:52 /* 读取寄存器0x1071 */
cmd resp, 0:0x1004, 1:0x34 /* 返回tuple 长度:0x04 */
cmd,Arg:0x20e400, Cmd:52 /* 读取寄存器0x1072 */
cmd resp, 0:0x10d0, 1:0x34 /* 返回vendor id低8bit:0xd0 */
cmd,Arg:0x20e600, Cmd:52 /* 读取寄存器0x1073 */
cmd resp, 0:0x1002, 1:0x34 /* 返回vendor id高8bit:0x02 */
cmd,Arg:0x20e800, Cmd:52 /* 读取寄存器0x1074 */
cmd resp, 0:0x10a6, 1:0x34 /* 返回device id低8bit:0xa6 */
cmd,Arg:0x20ea00, Cmd:52 /* 读取寄存器0x1075 */
cmd resp, 0:0x10a9, 1:0x34 /* 返回device id高8bit:0xa9 */
从上面的读取值,vendor id:0x02d0, device id:0xa9a6;
接着读取function 0的cis.blksize,cis.max_dtr。
当读到tuple code为0x00时,表示没有tuple内容,继续往下读,当读到tuple code为0xFF时,表示整个CIS内容结束了。

K、 接着sdio_enable_hs切换到high-speed;
cmd,Arg:0x2600, Cmd:52 /* 读取寄存器0x13 */
cmd resp, 0:0x1001, 1:0x34 /* 支持high-speed */
cmd,Arg:0x80002603, Cmd:52 /* 设置寄存器0x13,最高速率50MHz */
cmd resp, 0:0x1003, 1:0x34

L、 设置控制器clock,调用sdio_enable_4bit_bus设置sdio设备bus width,再设置控制器的bus width;
cmd,Arg:0xe00, Cmd:52 /* 读取寄存器0x7 */
cmd resp, 0:0x1040, 1:0x34 /* 支持8bit模式,当前为1bit模式 */
cmd,Arg:0x80000e42, Cmd:52 /* 写寄存器0x7,设置为4bit模式 */
cmd resp, 0:0x1042, 1:0x34

到这里,mmc_sdio_init_card初始化完成,接下来进行sdio设备 function的初始化,function的初始化由sdio_init_func函数完成,每一个function调用一次。这里扫描的sdio设备有2个function,会调用2次sdio_init_func。

M、 sdio_init_func函数中先sdio_read_fbr,读取fbr;
cmd,Arg:0x20000, Cmd:52
/* 读取寄存器0x100,即function 1的fbr寄存器0 */
cmd resp, 0:0x1000, 1:0x34
response bit[6] 0x0:不支持CSA;
response bit[3:0] 0x0:
No SDIO standard interface supported by this function;
bit[3:0]各个值对应的含义;
这里写图片描述
N、 接着调用sdio_read_func_cis读取function的CIS;
读取function的CIS与前面sdio_read_common_cis读取CIS是一样的流程,只是读取CIS的地址不同,CIS的内容也不同而已。
其中一个很重要的参数是:func->max_blksize;
同时如果读到vendor id,device id,就保存在function的结构中,若没读到,就从card->cis.vendor、card->cis.device(sdio_read_common_cis读出来的)copy过来,

到这里,sdio设备的扫描就完成了,接着会调用mmc_add_card增加sdio设备,调用sdio_add_func增加function设备,这样整个扫描过程就完成了。

O、 在sdio_add_func的时候,会触发设置function的block size;
这里只是初始化设置function的block size,在sdio设备驱动实际运行时,还会重新设置该值。
cmd,Arg:0x80022040, Cmd:52 /* 写寄存器110,function1 fbr的0x10 */
cmd resp, 0:0x1040, 1:0x34
cmd,Arg:0x80022200, Cmd:52 /* 写寄存器110,function1 fbr的0x11 */
cmd resp, 0:0x1000, 1:0x34
上面命令设置function1的block size大小为64byte;
cmd,Arg:0x80042000, Cmd:52 /* 写寄存器210,function2 fbr的0x10 */
cmd resp, 0:0x1000, 1:0x34
cmd,Arg:0x80042202, Cmd:52 /* 写寄存器210,function2 fbr的0x10 */
cmd resp, 0:0x1002, 1:0x34
上面命令设置function2的block size大小为512byte;

到此,linux内核中整个sdio设备的扫描就完成了,如果已经注册了对应sdio id的驱动,就会调用驱动的probe进行设备的初始化。

从整个扫描过程看,没有使用到data线,所以如果sdio设备初始化的时候,能检测到设备,但初始化失败,很大可能那就是data线出问题了。

附:
 sdio扫描函数调用过程:
mmc_rescan->
mmc_rescan_try_freq->
sdio_reset->(cmd52,cmd52)
mmc_go_idle->(cmd0)
mmc_send_if_cond->(cmd8)
mmc_attach_sdio->
mmc_send_io_op_cond->(cmd5)
mmc_sdio_init_card->
mmc_send_io_op_cond->(cmd5)
mmc_send_relative_addr->(cmd3)
mmc_select_card->(cmd7)
sdio_read_cccr->(cmd52…)
sdio_read_common_cis->(cmd52…)
sdio_enable_hs->
mmc_sdio_switch_hs->(cmd52…)
sdio_enable_4bit_bus->
sdio_enable_wide->(cmd52…)
sdio_init_func->
sdio_read_fbr->(cmd52…)
sdio_read_func_cis->(cmd52…)
mmc_add_card->
sdio_add_func->

 sd扫描函数调用过程:
mmc_rescan->
mmc_rescan_try_freq->
sdio_reset->(cmd52…)
mmc_go_idle->(cmd0)
mmc_send_if_cond->(cmd8)
mmc_attach_sdio->
mmc_send_io_op_cond->(cmd5)
mmc_attach_sd->
mmc_send_app_op_cond->(cmd55,acmd41)
mmc_sd_init_card->
mmc_sd_get_cid->
mmc_go_idle->(cmd0)
mmc_send_if_cond->(cmd8)
mmc_send_app_op_cond->(cmd55,acmd41 4次)
mmc_all_send_cid->(cmd2)
mmc_send_relative_addr->(cmd3)
mmc_sd_get_csd->
mmc_send_csd->
mmc_send_cxd_native->(cmd9)
mmc_select_card->(cmd7)
mmc_sd_setup_card->
mmc_app_send_scr->(cmd55,acmd51)
mmc_read_ssr->
mmc_app_sd_status->(cmd55,acmd13)
mmc_read_switch->
mmc_sd_switch->(cmd6)
mmc_sd_switch_hs->
mmc_sd_switch->(cmd6)
mmc_app_set_bus_width->(cmd55,acmd6)
mmc_add_card->

 emmc扫描函数调用过程(没抓到数据包,根据代码流程整理,仅供参考):
mmc_rescan->
mmc_rescan_try_freq->
sdio_reset->(cmd52…)
mmc_go_idle->(cmd0)
mmc_send_if_cond->(cmd8)
mmc_attach_sdio->
mmc_send_io_op_cond->(cmd5)
mmc_attach_sd->
mmc_send_app_op_cond->(cmd55,acmd41)
mmc_attach_mmc->
mmc_send_op_cond->(cmd1)
mmc_init_card->
mmc_go_idle->(cmd0)
mmc_send_op_cond->(cmd1)
mmc_all_send_cid->(cmd2)
mmc_set_relative_addr->(cmd3)
mmc_send_csd->(cmd9)
mmc_select_card->(cmd7)
mmc_get_ext_csd->(cmd8)
……?
mmc_add_card->

猜你喜欢

转载自www.linuxidc.com/Linux/2018-08/153459.htm