linux下SDIO设备扫描分析

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,默认{BANNED}最佳高速率25MHz;

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,{BANNED}最佳高速率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;

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->

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
SDIO (Secure Digital Input/Output) 是一种用于将外部设备连接到计算机系统的接口标准。Linux提供了SDIO设备驱动程序,用于管理和控制与系统连接的SDIO设备SDIO设备驱动在Linux内核中的实现基于统一的设备模型,以提供设备发现、配置和控制功能。驱动程序负责与硬件通信,并且为用户空间程序提供与SDIO设备交互的接口。 SDIO设备驱动程序的核心功能包括: 1. 设备识别和初始化:驱动程序通过检测SDIO设备的插入和拔出事件来进行设备识别。在插入新的SDIO设备时,驱动程序会通过与设备通信来获取设备的识别信息,并进行必要的初始化设置。 2. 数据传输:驱动程序负责管理和控制从SDIO设备读取数据或向设备写入数据。它处理数据传输的请求和缓冲区管理,以确保数据的正确传输和处理。驱动程序还为用户空间程序提供接口,以便它们可以通过系统调用与SDIO设备进行数据交互。 3. 中断处理:SDIO设备可能会产生中断信号,以通知系统有新的数据可用或发生了特定事件。驱动程序负责处理这些中断,并触发适当的操作或通知用户空间程序。 4. 功耗管理:SDIO设备驱动程序还可以实现功耗管理功能,以控制和优化设备的功耗消耗。它可以根据系统的需求来控制设备的电源状态,例如睡眠模式和唤醒模式之间的切换。 5. 错误处理和调试:驱动程序应具备可靠的错误处理和调试功能,以便在发生错误或异常情况时能够及时识别和处理问题。例如,驱动程序可能需要处理数据传输错误、设备通信失败或设备故障等情况。 总之,LinuxSDIO设备驱动程序提供了一个统一的接口和框架,以便方便地管理和控制与系统连接的SDIO设备。它允许用户空间程序与SDIO设备进行数据交互,并负责处理设备的初始化、数据传输、中断处理、功耗管理和错误处理等功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值