Linux的设备驱动程序架构分析之MMC / SD(二)
转自:http : //blog.csdn.net/liuhaoyutz
内核版本:3.10.1
一,s3cmci_ops分析
在上一篇文章中我们分析了Mini2440 MMC / SD驱动的探针函数s3cmci_probe。在该函数中初始化了结构mmc_host指针变量mmc,其中,设置mmc-> ops为s3cmci_ops,s3cmci_ops定义在drivers / mmc / host / s3cmci.c文件中:
- 1427static struct mmc_host_ops s3cmci_ops = {
- 1428 .request = s3cmci_request,
- 1429.set_ios = s3cmci_set_ios,
- 1430 .get_ro = s3cmci_get_ro,
- 1431 .get_cd = s3cmci_card_present,
- 1432.enable_sdio_irq = s3cmci_enable_sdio_irq,
- 1433};
struct mmc_host是mmc core层与主机层的接口,mmc_host.ops是控制主机完成用户请求的接口函数集,其类型是struct mmc_host_ops,该结构体定义在include / linux / mmc / host.h文件中:
- 83structmmc_host_ops {
- 84 / *
- 85 *“启用”在主机被声明时被调用,并且“禁用”被调用
- 86 *当主机被释放时。“启用”和“禁用”已被弃用。
- 87 * /
- 88 int (* enable)(struct mmc_host * host);
- 89 int (* disable)(struct mmc_host * host);
- 90 / *
- 91 *主机可以实现pre_req和post_req
- 92 *命令来支持请求的双缓冲(准备一个
- 93 *请求,而另一个请求是活动的)。
- 94 * pre_req()必须后跟post_req()。
- 95 *撤消对pre_req()的调用,调用post_req()
- 96 *非零错误条件。
- 97 * /
- 98 void (* post_req)(structmmc_host * host, struct mmc_request * req,
- 99 int err);
- 100 void (* pre_req)(struct mmc_host * host, struct mmc_request * req,
- 101 bool is_first_req);
- 102 void (* request)(struct mmc_host * host, struct mmc_request * req);
- 103 / *
- 104 *避免频繁地调用这三个函数,或者在“fastpath”中,
- 105 *因为底层控制器可能会以昂贵的方式实施它们
- 106 *和/或缓慢的方式。
- 107 *
- 108 *另请注意,这些功能可能会休眠,所以不要打电话给他们
- 109 *在原子语境中!
- 110 *
- 111 * get_ro回调函数的返回值应为:
- 112 * 0用于读/写卡
- 113 * 1用于只读卡
- 114 * -ENOSYS不支持时(等于NULL回调)
- 115 *或错误发生错误的负errno值
- 116 *
- 117 * get_cd回调的返回值应为:
- 118 * 0为缺席卡
- 119 * 1为现在的卡
- 不支持120 * -ENOSYS(等于NULL回调)
- 121 *或错误发生错误的负errno值
- 122 * /
- 123 void (* set_ios)(struct mmc_host * host, struct mmc_ios * ios);
- 124 int (* get_ro)(struct mmc_host * host);
- 125 int (* get_cd)(struct mmc_host * host);
- 126
- 127 void (* enable_sdio_irq)(structmmc_host * host, int enable);
- 128
- 129 / *可选回调HC怪癖* /
- 130 void (* init_card)(struct mmc_host * host, struct mmc_card * card);
- 131
- 132 int (* start_signal_voltage_switch)(struct mmc_host * host, struct mmc_ios * ios);
- 133
- 134 / *检查卡是否正在拉dat [0:3]低*
- 135 int (* card_busy)(struct mmc_host * host);
- 136
- 137 / * SD和eMMC卡的调谐命令操作码值不同* /
- 138 int (* execute_tuning)(struct mmc_host * host,u32 opcode);
- 139 int (* select_drive_strength)(unsigned int max_dtr, int host_drv,intcard_drv);
- 140 void (* hw_reset)(struct mmc_host * host);
- 141 void (* card_event)(structmmc_host * host);
- 142};
请求函数用于处理用户的请求。
set_ios函数用于设置SDI的控制参数,如时钟,总线宽度等等。
get_ro函数用于探测SD卡是否有写保护。
get_cd函数用于探测卡是否已插入插槽。
enable_sdio_irq函数用于启动或禁用SDI中断。
需要注意的是,为什么没有对MMC / SD进行读写的读取和写入函数呢?这是因为Linux的块设备的读写操作是通过请求函数完成的。
那么对于mini2440的,它的s3cmci_ops中的成员函数在什么时候会被调用呢举例如下?
在驱动器/ MMC /核心/ core.c文件中:
- 194staticvoid
- 195mmc_start_request(struct mmc_host * host,struct mmc_request * mrq)
- 196 {
- 197#ifdef CONFIG_MMC_DEBUG
- 198 unsigned int i,sz;
- 199 struct scatterlist * sg;
- 200#ENDIF
- 201
- 202 if (mrq-> sbc){
- 203 pr_debug(“<%s:starting CMD%u arg%08x flags%08x> \ n” ,
- 204 mmc_hostname(host),mrq-> sbc-> opcode,
- 205 mrq-> sbc-> arg,mrq-> sbc-> flags);
- 206}
- 207
- 208 pr_debug(“%s:starting CMD%u arg%08x flags%08x \ n” ,
- 209 mmc_hostname(host),mrq-> cmd-> opcode,
- 210 mrq-> cmd-> arg,mrq-> cmd-> flags);
- 211
- 212 if (mrq-> data){
- 213 pr_debug(“%s:blksz%dblocks%d flags%08x”
- 214 “tsac%d ms nsac%d \ n” ,
- 215 mmc_hostname(host),mrq-> data-> blksz,
- 216 mrq-> data-> blocks,mrq-> data-> flags,
- 217 mrq-> data-> timeout_ns / 1000000,
- 218 mrq-> data-> timeout_clks);
- 219}
- 220 221 if (mrq-> stop){
- 222 pr_debug(“%s:CMD%u arg%08x flags%08x \ n” ,
- 223 mmc_hostname(host),mrq-> stop-> opcode,
- 224 mrq-> stop-> arg,mrq-> stop-> flags);
- 225}
- 226
- 227 WARN_ON(!host-> claim);
- 228
- 229 mrq-> cmd-> error = 0;
- 230 mrq-> cmd-> mrq = mrq;
- 231 if (mrq-> data){
- 232 BUG_ON(mrq-> data-> blksz> host-> max_blk_size);
- 233 BUG_ON(mrq-> data-> blocks> host-> max_blk_count);
- 234 BUG_ON(mrq-> data-> blocks * mrq-> data-> blksz>
- 235 host-> max_req_size);
- 236
- 237#ifdef CONFIG_MMC_DEBUG
- 238 sz = 0;
- 239 for_each_sg(mrq-> data-> sg,sg,mrq-> data-> sg_len,i)
- 240 sz + = sg-> length;
- 241 BUG_ON(sz!= mrq-> data-> blocks * mrq-> data-> blksz);
- 242#ENDIF
- 243
- 244 mrq-> cmd-> data = mrq-> data;
- 245 mrq-> data-> error = 0;
- 246 mrq-> data-> mrq = mrq;
- 247 if (mrq-> stop){
- 248 mrq-> data-> stop = mrq-> stop;
- 249 mrq-> stop-> error = 0;
- 250 mrq-> stop-> mrq = mrq;
- 251}
- 252}
- 253 mmc_host_clk_hold(host);
- 254 led_trigger_event(host-> led,LED_FULL);
- 255 host-> ops-> request(host,mrq);
- 256}
可以看到255行,调用了宿主 - > ops->请求函数,即s3cmci_request函数。
再比如,在驱动器/ MMC /核心/ core.c文件中:
- 954 / *
- 955 *内部功能,实际的ios调用主机驱动程序,
- 956 *可选择打印一些调试输出。
- 957 * /
- 958static inline void mmc_set_ios(structmmc_host * host)
- 959 {
- 960 struct mmc_ios * ios =&host-> ios;
- 961
- 962 pr_debug(“%s:clock%uHz busmode%u powermode%u cs%u Vdd%u”
- 963 “width%u timing%u \ n” ,
- 964 mmc_hostname(host),ios-> clock,ios-> bus_mode,
- 965 ios-> power_mode,ios-> chip_select,ios-> vdd,
- 966 ios-> bus_width,ios-> timing);
- 967
- 968 if (ios-> clock> 0)
- 969 mmc_set_ungated(host);
- 970 host-> ops-> set_ios(host,ios);
- 971}
可以看到,970行,调用了宿主 - > ops-> set_ios函数,即s3cmci_set_ios函数。
下面我们就来看一下s3cmci_ops的各个成员函数的实现。
s3cmci_get_ro函数定义在驱动程序/ MMC /主机/ s3cmci.c文件中:
- 1372static int s3cmci_get_ro(structmmc_host * mmc)
- 1373 {
- 1374 struct s3cmci_host * host = mmc_priv(mmc);
- 1375 struct s3c24xx_mci_pdata * pdata = host-> pdata;
- 1376 int ret;
- 1377
- 1378 if (pdata-> no_wprotect)
- 1379 返回 0;
- 1380
- 1381 ret = gpio_get_value(pdata-> gpio_wprotect)?1:0;
- 1382 ret ^ = pdata-> wprotect_invert;
- 1383
- 1384 返回 RET;
- 1385}
1374行,由mmc_host取得s3cmci_host。
1375行,取得s3c24xx_mci_pdata,其它保存着SDI的平台数据。
1378行,如果s3c24xx_mci_pdata.no_wprotect为1,表明没有写保护开关,直接退出。例如MMC卡就没有写保护开关,只有SD卡才有写保护开关。
1381行,读取gpio_wprotect引脚电平,对于MINI2440,即GPH8引脚。
1382行,与pdata-> wprotect_invert执行异或操作,即反转上步得到GPH8引脚电平值。
s3cmci_card_present函数定义在驱动程序/ MMC /主机/ s3cmci.c文件中:
- 1254static int s3cmci_card_present(structmmc_host * mmc)
- 1255 {
- 1256 struct s3cmci_host * host = mmc_priv(mmc);
- 1257 struct s3c24xx_mci_pdata * pdata = host-> pdata;
- 1258 int ret;
- 1259
- 1260 if (pdata-> no_detect)
- 1261 返回 -ENOSYS;
- 1262
- 1263 ret = gpio_get_value(pdata-> gpio_detect)?0:1;
- 1264 return ret ^ pdata-> detect_invert;
- 1265}
1256行,由mmc_host取得s3cmci_host。
1257行,取得s3c24xx_mci_pdata,其它保存着SDI的平台数据。
1260行,如果s3c24xx_mci_pdata.no_detect为1,表明没有卡探测引脚,直接退出。
1263行,读取gpio_detect引脚电平值,对于MINI2440,即GPG8引脚。
1264行,与pdata-> detect_invert进行异或操作,即反转上步得到的GPG8引脚电平值。
s3cmci_enable_sdio_irq函数定义在驱动程序/ MMC /主机/ s3cmci.c文件中:
- 1387static voids3cmci_enable_sdio_irq(struct mmc_host * mmc, int enable)
- 1388 {
- 1389 struct s3cmci_host * host = mmc_priv(mmc);
- 1390无符号 长 旗
- 1391 u32 con
- 1392
- 1393 local_irq_save(flags);
- 1394
- 1395 con = readl(host-> base + S3C2410_SDICON);
- 1396 host-> sdio_irqen = enable;
- 1397
- 1398 if (enable == host-> sdio_irqen)
- 1399 goto same_state;
- 1400
- 1401 if (enable){
- 1402 con | = S3C2410_SDICON_SDIOIRQ;
- 1403 enable_imask(host,S3C2410_SDIIMSK_SDIOIRQ);
- 1404
- 1405 if (!host-> irq_state &&!host-> irq_disabled){
- 1406 host-> irq_state = true ;
- 1407 enable_irq(host-> irq);
- 1408}
- 1409} else {
- 1410 disable_imask(host,S3C2410_SDIIMSK_SDIOIRQ);
- 1411 con&=〜S3C2410_SDICON_SDIOIRQ;
- 1412
- 1413 if (!host-> irq_enabled && host-> irq_state){
- 1414 disable_irq_nosync(host-> irq);
- 1415 host-> irq_state = false ;
- 1416}
- 1417}
- 1418
- 1419 writel(con,host-> base + S3C2410_SDICON);
- 1420
- 1421 same_state:
- 1422 local_irq_restore(flags);
- 1423
- 1424 s3cmci_check_sdio_irq(host);
- 1425}
1389行,由mmc_host取得s3cmci_host。
1395行,读取SDICON即SDI控制寄存器的内容,保存在CON中。
1396行,我觉得这一行不应该存在,因为这一行将参数启用的值赋值给宿主> sdio_irqen,但是1398行又接着判断启用与主机 - > sdio_irqen是否相等,如果相等就退出了。
1401年至1408年行,启用为1,使能SDIO中断。
1402行,S3C2410_SDICON_SDIOIRQ定义在驱动程序/ MMC /主机/ s3cmci.c文件中:
57#define S3C2410_SDICON_SDIOIRQ(1 << 3)
对照S3C2440数据手册,可知这个宏用来设置SDICON寄存器的第3位,该位决定是否接收SDIO中断。
1403行,S3C2410_SDIIMSK_SDIOIRQ定义在驱动程序/ MMC /主机/ s3cmci.c文件中:
105#define S3C2410_SDIIMSK_SDIOIRQ(1 << 12)
对照S3C2440数据手册,可知这个宏用来设置SDIIntMsk寄存器的第13位,该位决定当读等待请求发生时,SDI是否产生一个中断。
enable_imask函数定义在驱动程序/ MMC /主机/ s3cmci.c文件中:
- 279staticinline u32 enable_imask(struct s3cmci_host * host,u32 imask)
- 280 {
- 281 u32 newmask;
- 282
- 283 newmask = readl(host-> base + host-> sdiimsk);
- 284 newmask | = imask;
- 285
- 286 writel(newmask,host-> base + host-> sdiimsk);
- 287
- 288 返回 newmask;
- 289}
该函数用来设置SDIIntMsk寄存器。
1409至1416年行,使为0,禁用SDIO中断。
1419行,用新的骗子设置SDICON即SDI控制寄存器。
s3cmci_set_ios函数定义在驱动程序/ MMC /主机/ s3cmci.c文件中:
- 1306static void s3cmci_set_ios(structmmc_host * mmc, struct mmc_ios * ios)
- 1307 {
- 1308 struct s3cmci_host * host = mmc_priv(mmc);
- 1309 u32 mci_con;
- 1310
- 1311 / *设置电源状态* /
- 1312
- 1313 mci_con = readl(host-> base + S3C2410_SDICON);
- 1314
- 1315 开关 (ios-> power_mode){
- 1316 案例 MMC_POWER_ON:
- 1317 箱 MMC_POWER_UP:
- 1318 / *在SD模式下配置GPE5 ... GPE10引脚* /
- 1319 s3c_gpio_cfgall_range(S3C2410_GPE(5),6,S3C_GPIO_SFN(2),
- 1320 S3C_GPIO_PULL_NONE);
- 1321
- 1322 if (host-> pdata-> set_power)
- 1323 host-> pdata-> set_power(ios-> power_mode,ios-> vdd);
- 1324
- 1325 if (!host-> is2440)
- 1326 mci_con | = S3C2410_SDICON_FIFORESET;
- 1327
- 1328 突破;
- 1329
- 1330 箱 MMC_POWER_OFF:
- 1331 默认值:
- 1332 gpio_direction_output(S3C2410_GPE(5),0);
- 1333
- 1334 if (host-> is2440)
- 1335 mci_con | = S3C2440_SDICON_SDRESET;
- 1336
- 1337 if (host-> pdata-> set_power)
- 1338 host-> pdata-> set_power(ios-> power_mode,ios-> vdd);
- 1339
- 1340 突破;
- 1341}
- 1342
- 1343 s3cmci_set_clk(host,ios);
- 1344
- 1345 / *设置CLOCK_ENABLE * /
- 1346 if (ios-> clock)
- 1347 mci_con | = S3C2410_SDICON_CLOCKTYPE;
- 1348 其他
- 1349 mci_con&=〜S3C2410_SDICON_CLOCKTYPE;
- 1350
- 1351 writel(mci_con,host-> base + S3C2410_SDICON);
- 1352
- 1353 if ((ios-> power_mode == MMC_POWER_ON)||
- 1354(ios-> power_mode == MMC_POWER_UP)){
- 1355 dbg(host,dbg_conf, “以%lukHz运行(请求:%ukHz)\ n” ,
- 1356 host-> real_rate / 1000,ios-> clock / 1000);
- 1357} else {
- 1358 dbg(host,dbg_conf, “掉电。\ n” );
- 1359}
- 1360
- 1361 host-> bus_width = ios-> bus_width;
- 1362}
1306行,参数ios是structmmc_ios类型指针.struct mmc_ios定义在include / linux / mmc / host.h文件中:
- 22structmmc_ios {
- 23 unsigned int clock; / *时钟频率* /
- 24 unsigned short vdd;
- 25
- 26 / * vdd从下方存储所选电压范围的位数。* /
- 27
- 28 unsigned char bus_mode; / *命令输出模式* /
- 29
- 30#define MMC_BUSMODE_OPENDRAIN 1
- 31#define MMC_BUSMODE_PUSHPULL 2
- 32
- 33 unsigned char chip_select; / * SPI芯片选择* /
- 34
- 35#define MMC_CS_DONTCARE 0
- 36#define MMC_CS_HIGH 1
- 37#define MMC_CS_LOW 2
- 38
- 39 unsigned char power_mode; / *电源模式* /
- 40
- 41#define MMC_POWER_OFF 0
- 42#define MMC_POWER_UP 1
- 43#define MMC_POWER_ON 2
- 44
- 45 unsigned char bus_width; / *数据总线宽度* /
- 46
- 47#define MMC_BUS_WIDTH_1 0
- 48#define MMC_BUS_WIDTH_4 2
- 49#define MMC_BUS_WIDTH_8 3
- 50
- 51无符号 字符 时序 / *使用时间规格* /
- 52
- 53#define MMC_TIMING_LEGACY 0
- 54#define MMC_TIMING_MMC_HS 1
- 55#define MMC_TIMING_SD_HS 2
- 56#define MMC_TIMING_UHS_SDR12 3
- 57#define MMC_TIMING_UHS_SDR25 4
- 58#define MMC_TIMING_UHS_SDR50 5
- 59#define MMC_TIMING_UHS_SDR104 6
- 60#define MMC_TIMING_UHS_DDR50 7
- 61#define MMC_TIMING_MMC_HS200 8
- 62
- 63#define MMC_SDR_MODE 0
- 64#define MMC_1_2V_DDR_MODE 1
- 65#define MMC_1_8V_DDR_MODE 2
- 66#define MMC_1_2V_SDR_MODE 3
- 67#define MMC_1_8V_SDR_MODE 4
- 68
- 69 unsigned char signal_voltage; / *信号电压(1.8V或3.3V)* /
- 70
- 71#define MMC_SIGNAL_VOLTAGE_330 0
- 72#defineMMC_SIGNAL_VOLTAGE_180 1
- 73#define MMC_SIGNAL_VOLTAGE_120 2
- 74
- 75 unsigned char drv_type; / *驱动程序类型(A,B,C,D)* /
- 76
- 77#define MMC_SET_DRIVER_TYPE_B 0
- 78#define MMC_SET_DRIVER_TYPE_A 1
- 79#define MMC_SET_DRIVER_TYPE_C 2
- 80#define MMC_SET_DRIVER_TYPE_D 3
- 81};
1308行,由mmc_host取得s3cmci_host。
1313行,读取SDICON即SDI控制寄存器的值,保存在mci_con中。
1316至1328年行,如果ios-> power_mode为MMC_POWER_ON或MMC_POWER_UP,则执行这个分支。
S3C_GPIO_SFN宏定义在拱/臂/高原三星/包含/高原/ GPIO-cfg.h文件中:
- 66#defineS3C_GPIO_SPECIAL_MARK(0xfffffff0)
- 67#define S3C_GPIO_SPECIAL(x)(S3C_GPIO_SPECIAL_MARK |(x))
- 68
- 69 / *定义通用引脚配置* /
- 70#define S3C_GPIO_INPUT(S3C_GPIO_SPECIAL(0))
- 71#define S3C_GPIO_OUTPUT(S3C_GPIO_SPECIAL(1))
- 72#defineS3C_GPIO_SFN(x)(S3C_GPIO_SPECIAL(x))
S3C_GPIO_PULL_NONE宏定义在拱/臂/高原三星/包含/高原/ GPIO-cfg.h文件中:
- 126 / *定义每个gpio引脚可用的pull-{up,down}的值。
- 127 *
- 128 *这些值控制弱上拉电阻的状态
- 129 *可用于S3C系列的大多数引脚。并不是所有的芯片都支持
- 130 *上或下设置,它可能依赖于正在使用的芯片
- 131 *用于特定模式是否可用。
- 132 * /
- 133#define S3C_GPIO_PULL_NONE((__force samsung_gpio_pull_t)0x00)
- 134#define S3C_GPIO_PULL_DOWN((__force samsung_gpio_pull_t)0x01)
- 135#define S3C_GPIO_PULL_UP((__force samsung_gpio_pull_t)0x02)
s3c_gpio_cfgall_range函数定义在驱动器/ GPIO / GPIO-samsung.c文件中:
- 3150int s3c_gpio_cfgall_range(unsigned intstart,unsigned int nr,
- 3151 unsigned int cfg,samsung_gpio_pull_t pull)
- 3152 {
- 3153 int ret;
- 3154
- 3155 for (; nr> 0; nr--,start ++){
- 3156 s3c_gpio_setpull(start,pull);
- 3157 ret = s3c_gpio_cfgpin(start,cfg);
- 3158 if (ret!= 0)
- 3159 返回 RET;
- 3160}
- 3161
- 3162 返回 0;
- 3163}
可以看到,s3c_gpio_cfgall_range函数设置从GPE5开始的6个GPIO,即GPE5,GPE6,GPE7,GPE8,GPE9,GPE10。使用参数CFG设置GPECON寄存器,使用参数拉设置GPEUP寄存器。
GPECON寄存器对应的位置被设置为01,即使能相关SDI功能。
一三三○年至1341年行,如果ios-> power_mode为MMC_POWER_OFF或者默认情况下,则执行这个分支。
1332行,调用gpio_direction_output(S3C2410_GPE(5),0)关闭SDI时钟。
1335行,mci_con | = S3C2440_SDICON_SDRESET,根据S3C2440数据手册,这句用于重置整个sd / mmc模块。
1343行,调用s3cmci_set_clk(主机,IOS)设置时钟,s3cmci_set_clk定义在驱动程序/ MMC /主机/ s3cmci.c文件中:
- 1283static void s3cmci_set_clk(structs3cmci_host * host, struct mmc_ios * ios)
- 1284 {
- 1285 u32 mci_psc;
- 1286
- 1287 / *设置时钟* /
- 1288 为 (mci_psc = 0; mci_psc <255; mci_psc ++){
- 1289 host-> real_rate = host-> clk_rate /(host-> clk_div *(mci_psc + 1));
- 1290
- 1291 if (host-> real_rate <= ios-> clock)
- 1292 突破;
- 1293}
- 1294
- 1295 if (mci_psc> 255)
- 1296 mci_psc = 255;
- 1297
- 1298 host-> prescaler = mci_psc;
- 1299 writel(host->预分频器,host-> base + S3C2410_SDIPRE);
- 1300
- 1301 / *如果请求的时钟为0,real_rate也将为0 *
- 1302 if (ios-> clock == 0)
- 1303 host-> real_rate = 0;
- 1304}
1346至1349年行,如果ios->时钟不为0时,使能时钟,否则禁用时钟。
1351行,将mci_con写回SDICON寄存器。
1361行,用ios-> bus_width设置数据总线宽度宿主 - > bus_width。
s3cmci_request函数定义在驱动程序/ MMC /主机/ s3cmci.c文件中:
- 1267static voids3cmci_request(struct mmc_host * mmc, struct mmc_request * mrq)
- 1268 {
- 1269 struct s3cmci_host * host = mmc_priv(mmc);
- 1270
- 1271 host-> status = “mmc request” ;
- 1272 host-> cmd_is_stop = 0;
- 1273 host-> mrq = mrq;
- 1274
- 1275 if (s3cmci_card_present(mmc)== 0){
- 1276 dbg(host,dbg_err, “%s:no mediumpresent \ n” ,__func__);
- 1277 host-> mrq-> cmd-> error = -ENOMEDIUM;
- 1278 mmc_request_done(mmc,mrq);
- 1279} else
- 1280 s3cmci_send_request(mmc);
- 1281}
struct mmc_request代表一个请求,该结构体定义在include / linux / mmc / core.h文件中:
- 127structmmc_request {
- 128 struct mmc_command * sbc; / * SET_BLOCK_COUNT for multiblock * /
- 129 struct mmc_command * cmd;
- 130 struct mmc_data * data;
- 131 struct mmc_command * stop;
- 132
- 133 结构 完成;
- 134 void (* done)(struct mmc_request *); / *完成功能* /
- 135 struct mmc_host * host;
- 136};
1271行,将宿主>状态设置为 “mmcrequest”,主机 - >状态主要用于记录请求处理所处的阶段及状态,方便调试使用。
1272行,设置宿主> cmd_is_stop为0,从字面上理解,我认为宿主> cmd_is_stop代表命令是否是停止命令(即有一个命令是停止),0表示不是停止命令。
1273行,将mmc_requestmrp保存在主机 - > MRQ中,方便以后使用。
1275年至1280年行,如果卡不存在,则调用mmc_request_done(MMC,MRQ)结束这次请求处理,否则,调用s3cmci_send_request(MMC)。
s3cmci_send_request函数定义在驱动程序/ MMC /主机/ s3cmci.c文件中:
- 1202static voids3cmci_send_request(struct mmc_host * mmc)
- 1203 {
- 1204 struct s3cmci_host * host = mmc_priv(mmc);
- 1205 struct mmc_request * mrq = host-> mrq;
- 1206 struct mmc_command * cmd = host-> cmd_is_stop?mrq-> stop:mrq-> cmd;
- 1207
- 1208 host-> ccnt ++;
- 1209 prepare_dbgmsg(host,cmd,host-> cmd_is_stop);
- 1210
- 1211 / *清除命令,数据和fifo状态注册
- 1212五十目清除只有在2440,但不会在2410受伤
- 1213 * /
- 1214 writel(0xFFFFFFFF,host-> base + S3C2410_SDICMDSTAT);
- 1215 writel(0xFFFFFFFF,host-> base + S3C2410_SDIDSTA);
- 1216 writel(0xFFFFFFFF,host-> base + S3C2410_SDIFSTA);
- 1217
- 1218 if (cmd-> data){
- 1219 int res = s3cmci_setup_data(host,cmd-> data);
- 1220
- 1221 host-> dcnt ++;
- 1222
- 1223 if (res){
- 1224 dbg(host,dbg_err, “setup dataerror%d \ n” ,res);
- 1225 cmd-> error = res;
- 1226 cmd-> data-> error = res;
- 1227
- 1228 mmc_request_done(mmc,mrq);
- 1229 回报;
- 1230}
- 1231
- 1232 if (s3cmci_host_usedma(host))
- 1233 res = s3cmci_prepare_dma(host,cmd-> data);
- 1234 else
- 1235 res = s3cmci_prepare_pio(host,cmd-> data);
- 1236
- 1237 if (res){
- 1238 dbg(host,dbg_err,“data prepareerror %d \ n” ,res);
- 1239 cmd-> error = res;
- 1240 cmd-> data-> error = res;
- 1241
- 1242 mmc_request_done(mmc,mrq);
- 1243 回报;
- 1244}
- 1245}
- 1246
- 1247 / *发送命令* /
- 1248 s3cmci_send_command(host,cmd);
- 1249
- 1250 / *使能中断* /
- 1251 s3cmci_enable_irq(host, true );
- 1252}
1204行,由structmmc_host得到结构s3cmci_host。
1205行,从宿主> MRQ取出mmc_request以便使用。
1206行,因为宿主> cmd_is_stop被设置为0,所以CMD被设置为mrq-> CMD。
1214-1216行,清空SDICmdSta寄存器,SDIDatSta寄存器和SDIFSTA寄存器。
1216至1245年行,如果CMD->数据不为0,即当前命令带有要处理的数据,则执行这个如果语句块,进行数据处理的准备工作。
1219行,调用s3cmci_setup_data(主机,CMD->数据),该函数定义在驱动/ MMC /主机/ s3cmci.c文件中:
- 1048static ints3cmci_setup_data(struct s3cmci_host * host, struct mmc_data * data)
- 1049 {
- 1050 u32 dcon,imsk,stoptries = 3;
- 1051
- 1052 / *写DCON寄存器* /
- 1053
- 1054 if (!data){
- 1055 writel(0,host-> base + S3C2410_SDIDCON);
- 1056 返回 0;
- 1057}
- 1058
- 1059 if ((data-> blksz&3)!= 0){
- 1060 / *我们不能处理不对齐的块
- 1061 *一个块被转移。* /
- 1062
- 1063 if (data-> blocks> 1){
- 1064 pr_warning(“%s:can not donon-word sizes block transfer(blksz%d)\ n” ,__func__,data-> blksz);
- 1065 返回 -EINVAL;
- 1066}
- 1067}
- 1068
- 1069 while (readl(host-> base + S3C2410_SDIDSTA)&
- 1070(S3C2410_SDIDSTA_TXDATAON | S3C2410_SDIDSTA_RXDATAON)){
- 1071
- 1072 dbg(host,dbg_err,
- 1073 “mci_setup_data()transferstillin progress。\ n” );
- 1074
- 1075 writel(S3C2410_SDIDCON_STOP,host-> base + S3C2410_SDIDCON);
- 1076 s3cmci_reset(host);
- 1077
- 1078 if ((stoptries--)== 0){
- 1079 dbg_dumpregs(host,“DRF” );
- 1080 返回 -EINVAL;
- 1081}
- 1082}
- 1083
- 1084 dcon = data-> blocks&S3C2410_SDIDCON_BLKNUM_MASK;
- 1085
- 1086 if (s3cmci_host_usedma(host))
- 1087 dcon | = S3C2410_SDIDCON_DMAEN;
- 1088
- 1089 if (host-> bus_width == MMC_BUS_WIDTH_4)
- 1090 dcon | = S3C2410_SDIDCON_WIDEBUS;
- 1091
- 1092 if (!(data-> flags&MMC_DATA_STREAM))
- 1093 dcon | = S3C2410_SDIDCON_BLOCKMODE;
- 1094
- 1095 if (data-> flags&MMC_DATA_WRITE){
- 1096 dcon | = S3C2410_SDIDCON_TXAFTERRESP;
- 1097 dcon | = S3C2410_SDIDCON_XFER_TXSTART;
- 1098}
- 1099
- 1100 if (data-> flags&MMC_DATA_READ){
- 1101 dcon | = S3C2410_SDIDCON_RXAFTERCMD;
- 1102 dcon | = S3C2410_SDIDCON_XFER_RXSTART;
- 1103}
- 1104
- 1105 if (host-> is2440){
- 1106 dcon | = S3C2440_SDIDCON_DS_WORD;
- 1107 dcon | = S3C2440_SDIDCON_DATSTART;
- 1108}
- 1109
- 1110 writel(dcon,host-> base + S3C2410_SDIDCON);
- 1111
- 1112 / *写入BSIZE寄存器* /
- 1113
- 1114 writel(data-> blksz,host-> base + S3C2410_SDIBSIZE);
- 1115
- 1116 / *加入IMASK注册* /
- 1117 imsk = S3C2410_SDIIMSK_FIFOFAIL | S3C2410_SDIIMSK_DATACRC |
- 1118 S3C2410_SDIIMSK_DATATIMEOUT | S3C2410_SDIIMSK_DATAFINISH;
- 1119
- 1120 enable_imask(host,imsk);
- 1121
- 1122 / *写入TIMER寄存器* /
- 1123
- 1124 if (host-> is2440){
- 1125 writel(0x007FFFFF,host-> base + S3C2410_SDITIMER);
- 1126} else {
- 1127 writel(0x0000FFFF,host-> base + S3C2410_SDITIMER);
- 1128
- 1129 / * FIX:设置缓慢的时钟,以防止超时读*
- 1130 if (data-> flags&MMC_DATA_READ)
- 1131 writel(0xFF,host-> base + S3C2410_SDIPRE);
- 1132}
- 1133
- 1134 返回 0;
- 1135}
1054-1057行,如果命令数据为空,则清零SDIDatCon寄存器。
1059至1067年行,根据数据表的描述,如果在多模块下必须分配字大小,即BLKSIZE [1:0] = 00,所以这里“与” 3来判断是不是单模块如果在单模块处理的情况下,模块数大于1,则出错退出。
1069至1082年行,循环判断是否有数据正在发送或接收,如果有,则停止传输,并复位时钟。最多循环3次。
1086年至1087年行,如果使用DMA传输,则使能SDIDatCon寄存器的第15位的DMA功能。
1089至1090年行,如果数据总线宽度为4线,则使能SDIDatCon寄存器的第16位宽总线WideBus功能。
一〇九二年至1093年行,配置SDIDatCon寄存器的第17位,数据传输模式为块传输模式。
1095至1098年行,如果是写数据,配置SDIDatCon寄存器的第20位,收到回应后开始写数据。然后配置SDIDatCon寄存器的第12,13位,设置为写模式。
一一〇〇年至1103年行,如果是读数据,配置SDIDatCon寄存器的第19位,命令发送后开始读数据。
然后配置SDIDatCon寄存器的第12,13位,设置为读模式。
1105至1108年行,如果是S3C2440,配置SDIDatCon寄存器的第22,23位为10,即传输单位为字。然后配置SDIDatCon寄存器的第14位,开始数据传输。
1110行,将DCON写入SDIDatCon寄存器。
1114行,将数据 - > blksz写入SDIBSize寄存器。
1117至1118年行,设置出现FIFO失败SDI中断使能;数据接收CRC错误SDI中断使能;数据接收超时SDI中断使能;数据计时为0SDI中断使能。
1120行,调用enable_imask使能设置的中断。
1124年至1132年行,设置SDIDTimer寄存器。
回到s3cmci_send_request函数:
1223-1230行,如果s3cmci_setup_data出错,则打印信息并退出。
1232年至1233年行,如果使用DMA,则调用s3cmci_prepare_dma函数,该函数定义在驱动程序/ MMC /主机/ s3cmci.c文件中,关于DMA相关的函数,我们不再详细跟踪了。
1235行,如果没有使用DMA数据传输方式,则调用s3cmci_prepare_pio函数,即使用FIFO数据传输方式,具体来说,就是调用do_pio_write向FIFO中填充数据,当64字节的FIFO少于33字节时就会产生中断;或者从SD读数据,则先使能中断,当FIFO多于31字节时,则会调用中断服务程序,中断服务程序会调用do_pio_read读出FIFO的数据。
s3cmci_prepare_pio函数定义在驱动程序/ MMC /主机/ s3cmci.c文件中:
- 1139static ints3cmci_prepare_pio(struct s3cmci_host * host, struct mmc_data * data)
- 1140 {
- 1141 int rw =(data-> flags&MMC_DATA_WRITE)?1:0;
- 1142
- 1143 BUG_ON((data-> flags&BOTH_DIR)== BOTH_DIR);
- 1144
- 1145 host-> pio_sgptr = 0;
- 1146 host-> pio_bytes = 0;
- 1147 host-> pio_count = 0;
- 1148 host-> pio_active = rw?XFER_WRITE:XFER_READ;
- 1149
- 1150 if (rw){
- 1151 do_pio_write(host);
- 1152 enable_imask(host,S3C2410_SDIIMSK_TXFIFOHALF);
- 1153} else {
- 1154 enable_imask(host,S3C2410_SDIIMSK_RXFIFOHALF
- 1155 | S3C2410_SDIIMSK_RXFIFOLAST);
- 1156}
- 1157
- 1158 返回 0;
- 1159}
1141行,根据数据 - >标志确定是读还是写。
1151行,如果是写,则调用do_pio_write函数.do_pio_write函数定义在驱动程序/ MMC /主机/ s3cmci.c文件中:
- 520static void do_pio_write(struct s3cmci_host * host)
- 521 {
- 522 void __iomem * to_ptr;
- 523 int res
- 524 u32 fifo;
- 525 u32 * ptr;
- 526
- 527 to_ptr = host-> base + host-> sdidata;
- 528
- 529 while ((fifo = fifo_free(host))> 3){
- 530 if (!host-> pio_bytes){
- 531 res = get_data_buffer(host,&host-> pio_bytes,
- 532&host-> pio_ptr);
- 533 if (res){
- 534 dbg(host,dbg_pio,
- 535 “pio_write():complete(no more data)。\ n” );
- 536 host-> pio_active = XFER_NONE;
- 537
- 538 回;
- 539}
- 540
- 541 dbg(host,dbg_pio,
- 542 “pio_write():new source:[%i] @ [%p] \ n” ,
- 543 host-> pio_bytes,host-> pio_ptr);
- 544
- 545}
- 546
- 547 / *如果我们已经到了最后,我们必须
- 548 *写入剩余的字节数。要是我们
- 549 *在块的中间,我们必须写满
- 550 *字,所以四舍五入到4. * /
- 551 if (fifo> = host-> pio_bytes)
- 552 fifo = host-> pio_bytes;
- 553 其他
- 554 fifo - = fifo&3;
- 555
- 556 host-> pio_bytes - = fifo;
- 557 host-> pio_count + = fifo;
- 558
- 559 fifo =(fifo + 3)>> 2;
- 560 ptr = host-> pio_ptr;
- 561 而 (fifo--)
- 562 writel(* ptr ++,to_ptr);
- 563 host-> pio_ptr = ptr;
- 564}
- 565
- 566 enable_imask(host,S3C2410_SDIIMSK_TXFIFOHALF);
- 567}
527行,取得SDIDAT寄存器的物理地址保存在to_ptr变量中。
529行,调用fifo_free(主机)函数取得FIFO的剩余可用空间的字节数保存在FIFO变量中,这里,FIFO变量代表这次而循环最多可写的字节个数。如果剩余空间大于3个字节,即最小写一个字,则循环条件成立。
530-532行,如果宿主> pio_bytes为0,则调用get_data_buffer从分散聚集列表中取得保存要写的数据的缓冲区,缓冲区的长度和起始地址分别保存在get_data_buffer的2个参数宿主> pio_bytes和第3个参数宿主> pio_ptr中。
533-539行,如果get_data_buffer出错,打印信息退出。
551-552行,如果FIFO大于等于宿主> pio_bytes,即FIFO的可用空间大于等于保存要写数据的缓冲区长度,则将FIFO设置为主机 - > pio_bytes。
554行,如果fifo小于主机 - > pio_bytes,即FIFO的可用空间小于要写数据的缓冲区长度,则将fifo设置为fifo - (fifo&3)。从注释可以看到,这是为了保证以字为单位进行写操作。
556行,主机 - > pio_bytes- = fifo,保存这次写操作后,剩余的要写的字节数。
557行,主机 - > pio_count + = fifo,保存已经写了多个个字节。
559行,将字节数转化为字数。
561-562行,写数据到SDIDAT寄存器。
563行,主机 - > pio_ptr = ptr,保存当前还剩余要写数据的位置。
564行,结束这次虽然循环,回到529行重新执行上述过程。
566行,使能SDIIntMsk第4位,如果Tx FIFO填充满半,就产生中断。
回到s3cmci_prepare_pio函数中:
1152行,使能SDIIntMsk第4位,如果Tx FIFO填充满半,就产生中断。
1154行,使能SDIIntMsk第0位,如果Rx FIFO填充满半,就生产中断。
1155行,使能SDIIntMsk第2位,如果Rx FIFO读取了最后的数据,就产生中断。
回到s3cmci_send_request函数:
1248行,调用s3cmci_send_command(主机,CMD)发送命令。
该函数定义在驱动程序/ MMC /主机/ s3cmci.c文件中:
- 1016static voids3cmci_send_command(struct s3cmci_host * host,
- 1017 struct mmc_command * cmd)
- 1018 {
- 1019 u32 ccon,imsk;
- 1020
- 1021 imsk = S3C2410_SDIIMSK_CRCSTATUS | S3C2410_SDIIMSK_CMDTIMEOUT |
- 1022 S3C2410_SDIIMSK_RESPONSEND | S3C2410_SDIIMSK_CMDSENT |
- 1023 S3C2410_SDIIMSK_RESPONSECRC;
- 1024
- 1025 enable_imask(host,imsk);
- 1026
- 1027 if (cmd-> data)
- 1028 host-> complete_what = COMPLETION_XFERFINISH_RSPFIN;
- 1029 else if (cmd-> flags&MMC_RSP_PRESENT)
- 1030 host-> complete_what = COMPLETION_RSPFIN;
- 1031 其他
- 1032 host-> complete_what = COMPLETION_CMDSENT;
- 1033
- 1034 writel(cmd-> arg,host-> base + S3C2410_SDICMDARG);
- 1035
- 1036 ccon = cmd-> opcode&S3C2410_SDICMDCON_INDEX;
- 1037 ccon | = S3C2410_SDICMDCON_SENDERHOST | S3C2410_SDICMDCON_CMDSTART;
- 1038
- 1039 if (cmd-> flags&MMC_RSP_PRESENT)
- 1040 ccon | = S3C2410_SDICMDCON_WAITRSP;
- 1041
- 1042 if (cmd-> flags&MMC_RSP_136)
- 1043 ccon | = S3C2410_SDICMDCON_LONGRSP;
- 1044
- 1045 writel(ccon,host-> base + S3C2410_SDICMDCON);
- 1046}
一○二一年至1025年行,出现CRC状态错误,命令响应超时,接收命令响应,命令发送,响应CRC校验失败时,将产生SDI中断。
一零二七年至1032年行,设置宿主> complete_what。
在驱动器/ MMC /主机/ s3cmci.h文件中,有如下定义:
- 11enum s3cmci_waitfor {
- 12 COMPLETION_NONE,
- 13 COMPLETION_FINALIZE,
- 14 COMPLETION_CMDSENT,
- 15 COMPLETION_RSPFIN,
- 16 COMPLETION_XFERFINISH,
- 17 COMPLETION_XFERFINISH_RSPFIN,
- 18};
另外,在驱动器/ MMC /主机/ s3cmci.c文件的s3cmci_irq函数的注释中,有如下内容:
- 603 * host-> complete_what指示请求何时被认为完成
- 604 * COMPLETION_CMDSENT当命令发送时
- 605 * COMPLETION_RSPFIN当接收到响应
- 606 * COMPLETION_XFERFINISH数据传输完成
- 607 * COMPLETION_XFERFINISH_RSPFIN上述两者。
1034行,用CMD-> ARG设置设置SDICmdArg寄存器。
1036年至1045年行,配置SDICmdCon寄存器。
1036行,取得命令索引。
1037行,命令启动。
1039年至1040年行,配置主机等待响应。
1042至1043年行,配置主机接收136位长响应。
回到s3cmci_send_request函数:
1251行,使能中断。
至此,s3cmci_send_request函数我们就分析完了。
s3cmci_request函数我们也就分析完了。
s3cmci_ops结构体我们也就分析完了。
二,中断处理函数s3cmci_irq分析
SDI中断处理函数s3cmci_irq定义在驱动程序/ MMC /主机/ s3cmci.c文件中:
- 599 / *
- 600 * ISR用于SDI接口IRQ
- 601 *驱动程序与ISRworks之间的通讯如下:
- 602 * host-> mrq指向电流请求
- 603 * host-> complete_what指示请求何时被认为完成
- 604 * COMPLETION_CMDSENT当命令发送时
- 605 * COMPLETION_RSPFIN当接收到响应
- 606 * COMPLETION_XFERFINISH数据传输完成
- 607 * COMPLETION_XFERFINISH_RSPFIN上述两者。
- 608 * host-> complete_request是驱动程序等待的完成对象
- 609 *
- 610 * 1)驱动程序设置host-> mrq andhost-> complete_what
- 611 * 2)司机准备转移
- 612 * 3)驱动器使能中断
- 613 * 4)驱动程序开始转移
- 614 * 5)驱动程序等待forhost-> complete_rquest
- 615 * 6)ISR检查请求状态(错误和成功)
- 616 * 6)ISR setshost-> mrq-> cmd-> error and host-> mrq-> data-> error
- 617 * 7)ISR completedhost-> complete_request
- 618 * 8)ISR禁止中断
- 619 * 9)司机醒来,照顾好了
- 620 *
- 621 *注意:“ - >错误”-fields在请求之前要设置为0
- 622 *由mmc.c发布 - 因此它们只能设置一个错误
- 623 *争执出现
- 624 * /
- 625
- 626static irqreturn_t s3cmci_irq(int irq, void * dev_id)
- 627 {
- 628 struct s3cmci_host * host = dev_id;
- 629 struct mmc_command * cmd;
- 630 u32 mci_csta,mci_dsta,mci_fsta,mci_dcnt,mci_imsk;
- 631 u32 mci_cclear = 0,mci_dclear;
- 632 unsigned long iflags;
- 633
- 634 mci_dsta = readl(host-> base + S3C2410_SDIDSTA);
- 635 mci_imsk = readl(host-> base + host-> sdiimsk);
- 636
- 637 if (mci_dsta&S3C2410_SDIDSTA_SDIOIRQDETECT){
- 638 if (mci_imsk&S3C2410_SDIIMSK_SDIOIRQ){
- 639 mci_dclear = S3C2410_SDIDSTA_SDIOIRQDETECT;
- 640 writel(mci_dclear,host-> base + S3C2410_SDIDSTA);
- 641
- 642 mmc_signal_sdio_irq(host-> mmc);
- 643 返回 IRQ_HANDLED;
- 644}
- 645}
- 646
- 647 spin_lock_irqsave(&host-> complete_lock,iflags);
- 648
- 649 mci_csta = readl(host-> base + S3C2410_SDICMDSTAT);
- 650 mci_dcnt = readl(host-> base + S3C2410_SDIDCNT);
- 651 mci_fsta = readl(host-> base + S3C2410_SDIFSTA);
- 652 mci_dclear = 0;
- 653
- 654 if ((host-> complete_what == COMPLETION_NONE)||
- 655(host-> complete_what == COMPLETION_FINALIZE)){
- 656 host-> status = “nothing to complete” ;
- 657 clear_imask(host);
- 658 goto irq_out;
- 659}
- 660
- 661 if (!host-> mrq){
- 662 host-> status = “no active mrq” ;
- 663 clear_imask(host);
- 664 goto irq_out;
- 665}
- 666
- 667 cmd = host-> cmd_is_stop?host-> mrq-> stop:host-> mrq-> cmd;
- 668
- 669 if (!cmd){
- 670 host-> status = “no active cmd” ;
- 671 clear_imask(host);
- 672 goto irq_out;
- 673}
- 674
- 675 if (!s3cmci_host_usedma(host)){
- 676 if ((host-> pio_active == XFER_WRITE)&&
- 677(mci_fsta&S3C2410_SDIFSTA_TFDET)){
- 678
- 679 disable_imask(host,S3C2410_SDIIMSK_TXFIFOHALF);
- 680 tasklet_schedule(&host-> pio_tasklet);
- 681 host-> status = “pio tx” ;
- 682}
- 683
- 684 if ((host-> pio_active == XFER_READ)&&
- 685(mci_fsta&S3C2410_SDIFSTA_RFDET)){
- 686
- 687 disable_imask(host,
- 688 S3C2410_SDIIMSK_RXFIFOHALF |
- 689 S3C2410_SDIIMSK_RXFIFOLAST);
- 690
- 691 tasklet_schedule(&host-> pio_tasklet);
- 692 host-> status = “pio rx” ;
- 693}
- 694}
- 695
- 696 if (mci_csta&S3C2410_SDICMDSTAT_CMDTIMEOUT){
- 697 dbg(host,dbg_err, “CMDSTAT:error CMDTIMEOUT \ n” );
- 698 cmd-> error = -ETIMEDOUT;
- 699 host-> status = “error:command timeout” ;
- 700 goto fail_transfer;
- 701}
- 702
- 703 if (mci_csta&S3C2410_SDICMDSTAT_CMDSENT){
- 704 if (host-> complete_what == COMPLETION_CMDSENT){
- 705 host-> status = “ok:command sent” ;
- 706 goto close_transfer;
- 707}
- 708
- 709 mci_cclear | = S3C2410_SDICMDSTAT_CMDSENT;
- 710}
- 711
- 712 if (mci_csta&S3C2410_SDICMDSTAT_CRCFAIL){
- 713 if (cmd-> flags&MMC_RSP_CRC){
- 714 if (host-> mrq-> cmd-> flags&MMC_RSP_136){
- 715 dbg(host,dbg_irq,
- 716 “fixup:忽略具有长rsp \ n的CRCfail” );
- 717} else {
- 718 / *注意,我们以前是转载失败
- 719 *在这里,但似乎这只是
- 720 *硬件得到它。
- 721 *
- 722 * cmd-> error = -EILSEQ;
- 723 * host-> status =“error:bad command crc”;
- 724 * goto fail_transfer;
- 725 * /
- 726}
- 727}
- 728
- 729 mci_cclear | = S3C2410_SDICMDSTAT_CRCFAIL;
- 730}
- 731
- 732 if (mci_csta&S3C2410_SDICMDSTAT_RSPFIN){
- 733 if (host-> complete_what == COMPLETION_RSPFIN){
- 734 host-> status = “ok:收到命令响应” ;
- 735 goto close_transfer;
- 736}
- 737
- 738 if (host-> complete_what == COMPLETION_XFERFINISH_RSPFIN)
- 739 host-> complete_what = COMPLETION_XFERFINISH;
- 740
- 741 mci_cclear | = S3C2410_SDICMDSTAT_RSPFIN;
- 742}
- 743
- 744 / *之后处理的错误只是相关的
- 745当数据传输正在进行时* /
- 746
- 747 if (!cmd-> data)
- 748 goto clear_status_bits;
- 749
- 750 / *检查FIFO故障* /
- 751 if (host-> is2440){
- 752 if (mci_fsta&S3C2440_SDIFSTA_FIFOFAIL){
- 753 dbg(host,dbg_err, “FIFO failure \ n” );
- 754 host-> mrq-> data-> error = -EILSEQ;
- 755 host-> status = “error:2440 fifo failure” ;
- 756 goto fail_transfer;
- 757}
- 758} else {
- 759 if (mci_dsta&S3C2410_SDIDSTA_FIFOFAIL){
- 760 dbg(host,dbg_err, “FIFOfailure \ n” );
- 761 cmd-> data-> error = -EILSEQ;
- 762 host-> status = “error:fifo failure” ;
- 763 goto fail_transfer;
- 764}
- 765}
- 766
- 767 if (mci_dsta&S3C2410_SDIDSTA_RXCRCFAIL){
- 768 dbg(host,dbg_err, “bad data crc(outgoing)\ n” );
- 769 cmd-> data-> error = -EILSEQ;
- 770 host-> status = “error:bad data crc(outgoing)” ;
- 771 goto fail_transfer;
- 772}
- 773
- 774 if (mci_dsta&S3C2410_SDIDSTA_CRCFAIL){
- 775 dbg(host,dbg_err, “bad data crc(incoming)\ n” );
- 776 cmd-> data-> error = -EILSEQ;
- 777 host-> status = “error:bad data crc(incoming)” ;
- 778 goto fail_transfer;
- 779}
- 780
- 781 if (mci_dsta&S3C2410_SDIDSTA_DATATIMEOUT){
- 782 dbg(host,dbg_err, “data timeout \ n” );
- 783 cmd-> data-> error = -ETIMEDOUT;
- 784 host-> status = “error:data timeout” ;
- 785 goto fail_transfer;
- 786}
- 787
- 788 if (mci_dsta&S3C2410_SDIDSTA_XFERFINISH){
- 789 if (host-> complete_what == COMPLETION_XFERFINISH){
- 790 host-> status = “ok:data transfer completed” ;
- 791 goto close_transfer;
- 792}
- 793
- 794 if (host-> complete_what == COMPLETION_XFERFINISH_RSPFIN)
- 795 host-> complete_what = COMPLETION_RSPFIN;
- 796
- 797 mci_dclear | = S3C2410_SDIDSTA_XFERFINISH;
- 798}
- 799
- 800clear_status_bits:
- 801 writel(mci_cclear,host-> base + S3C2410_SDICMDSTAT);
- 802 writel(mci_dclear,host-> base + S3C2410_SDIDSTA);
- 803
- 804 goto irq_out;
- 805
- 806fail_transfer:
- 807 host-> pio_active = XFER_NONE;
- 808
- 809close_transfer:
- 810 host-> complete_what = COMPLETION_FINALIZE;
- 811
- 812 clear_imask(host);
- 813 tasklet_schedule(&host-> pio_tasklet);
- 814
- 815 goto irq_out;
- 816
- 817irq_out:
- 818 dbg(host,dbg_irq,
- 819 “CSTA:0X%08X DSTA:0X%08X FSTA:0X%08X DCNT:0X%08xstatus:%S \ n”个,
- 820 mci_csta,mci_dsta,mci_fsta,mci_dcnt,host-> status);
- 821
- 822 spin_unlock_irqrestore(&host-> complete_lock,iflags);
- 823 返回 IRQ_HANDLED;
- 824
- 825}
在分析这个函数之前,请先看一下599-624行的注释。
628行,的dev_id是中断处理函数传递过来的structs3cmci_host指针。
634行,读取SDIDatSta寄存器,保存在mci_dsta变量中。
635行,读取SDIIntMsk寄存器,保存在mci_imsk变量中。
637行,S3C2410_SDIDSTA_SDIOIRQDETECT宏标志着SDIDatSta寄存器的第9位被置位,说明有SDIO中断被检测到。
638行,S3C2410_SDIIMSK_SDIOIRQ宏标志着SDIIntMsk寄存器的第12位被置位,表示使能SDI产生SDIO中断。
行639-640,根据数据表,这两句的作用是清零SDIDatSta寄存器的第9位。
642行,调用mmc_signal_sdio_irq函数处理SDIO中断,该函数定义在包括/ LINUX / MMC / host.h文件中:
- 396static inlinevoid mmc_signal_sdio_irq(struct mmc_host * host)
- 397 {
- 398 host-> ops-> enable_sdio_irq(host,0);
- 399 host-> sdio_irq_pending = true ;
- 400 wake_up_process(host-> sdio_irq_thread);
- 401}
649行,读取SDICmdSta寄存器,保存在mci_csta变量中。
650行,读取SDIDatCnt寄存器,保存在mci_dcnt变量中。
651行,读取SDIFSTA寄存器,保存在mci_fsta变量中。
654-665行,做一些检查工作。
667行,设置CMD。
675-694行,如果没有使用DMA,则执行这个如果分支。
676-677行,如果宿主> pio_active为XFER_WRITE,并且SDIFSTA寄存器的第13位被置位,表明FIFO可以用于写操作。
679行,禁用TFHalf中断。
680行,调用主机 - > pio_tasklet。
681行,设置主机 - >状态为 “piotx”。
684-685行,如果宿主> pio_active为XFER_READ,并且SDIFSTA寄存器的第12位被置位,表明FIFO可以用于读操作。
687-689行,禁用Rx FIFO相关中断。
691行,调用主机 - > pio_tasklet。
692行,设置主机 - >状态为 “piorx”。
696-701行,处理命令超时。
703-710行,命令发送完成(不论是否得到应答)。
712-730行,处理CRC校验错误。
732-742行,处理收到命令应答。
750-798行,处理数据传输相关错误。
751-765行,处理FIFO相关错误。
767-772行,处理读数据CRC校验错误。
774-779行,处理发送数据时CRC校验错误。
781-786行,处理数据超时。
788-798行,处理数据传输结束。
下面我们来看宿主> pio_tasklet,在s3cmci_probe函数中,有如下语句:
1662 tasklet_init(&host-> pio_tasklet,pio_tasklet,(unsigned long)host);
可以看到,宿主> pio_tasklet对应的微进程函数为pio_tasklet,并将主机做为参数传递给该函数.pio_tasklet函数定义在驱动/ MMC /主机/ s3cmci.c文件中:
- 569static void pio_tasklet(unsigned long data)
- 570 {
- 571 struct s3cmci_host * host =(struct s3cmci_host *)数据;
- 572
- 573 s3cmci_disable_irq(host, true );
- 574
- 575 if (host-> pio_active == XFER_WRITE)
- 576 do_pio_write(host);
- 577
- 578 if (host-> pio_active == XFER_READ)
- 579 do_pio_read(host);
- 580
- 581 if (host-> complete_what == COMPLETION_FINALIZE){
- 582 clear_imask(host);
- 583 if (host-> pio_active!= XFER_NONE){
- 584 dbg(host,dbg_err, “未完成%s”
- 585 “ - pio_count:[%u] pio_bytes:[%u] \ n” ,
- 586(host-> pio_active == XFER_READ)? “读” : “写” ,
- 587 host-> pio_count,host-> pio_bytes);
- 588
- 589 if (host-> mrq-> data)
- 590 host-> mrq-> data-> error = -EINVAL;
- 591}
- 592
- 593 s3cmci_enable_irq(host, false );
- 594 finalize_request(host);
- 595} else
- 596 s3cmci_enable_irq(host, true );
- 597}
575-576行,如果宿主> pio_active为XFER_WRITE,即写数据,则调用do_pio_write(主机)函数,该函数我们前面已经分析过了。
578-579行,如果宿主> pio_active为XFER_READ,即读数据,则调用do_pio_read(主机)函数,该函数定义在驱动/ MMC /主机/ s3cmci.c文件中:
- 437static void do_pio_read(struct s3cmci_host * host)
- 438 {
- 439 int res
- 440 u32 fifo;
- 441 u32 * ptr;
- 442 u32 fifo_words;
- 443 void __iomem * from_ptr;
- 444
- 445 / *写真实的预分频器到主机,它可能设置缓慢修复* /
- 446 writel(host->预分频器,host-> base + S3C2410_SDIPRE);
- 447
- 448 from_ptr = host-> base + host-> sdidata;
- 449
- 450 而 ((FIFO = fifo_count(主机))){
- 451 if (!host-> pio_bytes){
- 452 res = get_data_buffer(host,&host-> pio_bytes,
- 453&host-> pio_ptr);
- 454 if (res){
- 455 host-> pio_active = XFER_NONE;
- 456 host-> complete_what = COMPLETION_FINALIZE;
- 457
- 458 dbg(host,dbg_pio,“pio_read():”
- 459 “complete(no moredata)。\ n” );
- 460 回;
- 461}
- 462
- 463 dbg(host,dbg_pio,
- 464 “pio_read():new target:[%i] @ [%p] \ n” ,
- 465 host-> pio_bytes,host-> pio_ptr);
- 466}
- 467
- 468 dbg(host,dbg_pio,
- 469 “pio_read():FIFO:[%02i]缓冲液:[%03i] DCNT:[%08X] \ n”个,
- 470 fifo,host-> pio_bytes,
- 471 readl(host-> base + S3C2410_SDIDCNT));
- 472
- 473 / *如果我们到了最后的块,我们可以
- 474 *读一个字,得到1到3个字节。如果我们在
- 475 *中间的块,我们必须阅读全文,
- 476 *否则我们会写垃圾,这样一来就可以了
- 477 *甚至倍数4. * /
- 478 if (fifo> = host-> pio_bytes)
- 479 fifo = host-> pio_bytes;
- 480 其他
- 481 fifo - = fifo&3;
- 482
- 483 host-> pio_bytes - = fifo;
- 484 host-> pio_count + = fifo;
- 485
- 486 fifo_words = fifo >> 2;
- 487 ptr = host-> pio_ptr;
- 488 而 (fifo_words--)
- 489 * ptr ++ = readl(from_ptr);
- 490 host-> pio_ptr = ptr;
- 491
- 492 if (fifo&3){
- 493 u32 n = fifo&3;
- 494 u32 data = readl(from_ptr);
- 495 u8 * p =(u8 *)host-> pio_ptr;
- 496
- 497 而 (N--){
- 498 * p ++ = data;
- 499数据>> = 8;
- 500}
- 501}
- 502}
- 503
- 504 if (!host-> pio_bytes){
- 505 res = get_data_buffer(host,&host-> pio_bytes,&host-> pio_ptr);
- 506 if (res){
- 507 dbg(host,dbg_pio,
- 508 “pio_read():complete(no more buffers)。\ n” );
- 509 host-> pio_active = XFER_NONE;
- 510 host-> complete_what = COMPLETION_FINALIZE;
- 511
- 512 回;
- 513}
- 514}
- 515
- 516 enable_imask(host,
- 517 S3C2410_SDIIMSK_RXFIFOHALF | S3C2410_SDIIMSK_RXFIFOLAST);
- 518}
446行,设置波特率预分频器寄存器SDIPRE。
448行,将SDI数据寄存器SDIDAT的虚拟地址保存在from_ptr变量中。
450行,调用fifo_count得到FIFO中可读取数据的字节数,保存在FIFO变量中。
451-466行,调用get_data_buffer函数,从分散聚集列表中获取用于存放被读取数据的缓冲区的相关信息,缓冲区的长度保存在主机 - > pio_bytes中,缓冲区的起始地址保存在主机 - > pio_ptr中如果get_data_buffer函数返回非0值,表示读操作完成。
478-481行,如果FIFO中可读取数据的字节数大于host-> pio_bytes(即缓冲区的大小),则将fifo设置为host-> pio_bytes,否则fifo - = fifo&3.从473- 477行的注释可以看出,这样做是为了按字来读取数据。
483行,修改主机 - > pio_bytes的值,缓冲区还有多少字节的空间。
484行,已经读取的数据的字节数保存在主机 - > pio_count变量中。
486行,以字为单位,要读取的数据个数保存在fifo_words变量中。
488-489行,循环读取数据。
490行,保存下次要读取的数据的起始位置到宿主> pio_ptr中。
492-501行,读取剩余的非字节对齐部分。
502行,结束这次而循环,回到450行,判断FIFO中是否还有可读的数据,如果有的话,继续进行读取操作。
504-514行,如果宿主> pio_bytes为0,并且get_data_buffer函数返回非0值,表示没有可用的缓冲区空间,读结束。
516-517行,便能读取中断。
至此,do_pio_read函数我们就分析完了。
回到pio_tasklet函数:
581-596行,如果命令处理结束,则调用finalize_request进行最后的处理否则,打开中断,继续监听中断.finalize_request函数定义在驱动程序/ MMC /主机/ s3cmci.c文件中:
- 898static void finalize_request(structs3cmci_host * host)
- 899 {
- 900 struct mmc_request * mrq = host-> mrq;
- 901 struct mmc_command * cmd;
- 902 intdebug_as_failure = 0;
- 903
- 904 if (host-> complete_what!= COMPLETION_FINALIZE)
- 905 回;
- 906
- 907 if (!mrq)
- 908 回;
- 909 cmd = host-> cmd_is_stop?mrq-> stop:mrq-> cmd;
- 910
- 911 if (cmd-> data &&(cmd-> error == 0)&&
- 912(cmd-> data-> error == 0)){
- 913 if (s3cmci_host_usedma(host)&&(!host-> dma_complete)){
- 914 dbg(host,dbg_dma, “DMA Missing(%d)!\ n” ,
- 915 host-> dma_complete);
- 916 回;
- 917}
- 918}
- 919
- 920 / *从控制器读取响应。* /
- 921 cmd-> resp [0] = readl(host-> base + S3C2410_SDIRSP0);
- 922 cmd-> resp [1] = readl(host-> base + S3C2410_SDIRSP1);
- 923 cmd-> resp [2] = readl(host-> base + S3C2410_SDIRSP2);
- 924 cmd-> resp [3] = readl(host-> base + S3C2410_SDIRSP3);
- 925
- 926 writel(host->预分频器,host-> base + S3C2410_SDIPRE);
- 927
- 928 if (cmd-> error)
- 929 debug_as_failure = 1;
- 930
- 931 if (cmd-> data && cmd-> data-> error)
- 932 debug_as_failure = 1;
- 933
- 934 dbg_dumpcmd(host,cmd,debug_as_failure);
- 935
- 936 / *清理控制器* /
- 937 writel(0,host-> base + S3C2410_SDICMDARG);
- 938 writel(S3C2410_SDIDCON_STOP,host-> base + S3C2410_SDIDCON);
- 939 writel(0,host-> base + S3C2410_SDICMDCON);
- 940 clear_imask(host);
- 941
- 942 if (cmd-> data && cmd-> error)
- 943 cmd-> data-> error = cmd-> error;
- 944
- 945 if (cmd-> data && cmd-> data-> stop &&(!host-> cmd_is_stop)){
- 946 host-> cmd_is_stop = 1;
- 947 s3cmci_send_request(host-> mmc);
- 948 回;
- 949}
- 950
- 951 / *如果我们没有数据传输,我们在这里完成* /
- 952 if (!mrq-> data)
- 953 goto request_done;
- 954
- 955 / *如果没有错误,则计算字节传输的数量* /
- 956 if (mrq-> data-> error == 0){
- 957 mrq-> data-> bytes_xfered =
- 958(mrq-> data-> blocks * mrq-> data-> blksz);
- 959} else {
- 960 mrq-> data-> bytes_xfered = 0;
- 961}
- 962
- 963 / *如果我们在传输数据时发生错误,我们刷新
- 964 * DMA通道和fifo清除任何垃圾。* /
- 965 if (mrq-> data-> error!= 0){
- 966 if (s3cmci_host_usedma(host))
- 967 s3c2410_dma_ctrl(host-> dma,S3C2410_DMAOP_FLUSH);
- 968
- 969 if (host-> is2440){
- 970 / *清除故障寄存器并复位fifo。* /
- 971 writel(S3C2440_SDIFSTA_FIFORESET |
- 972 S3C2440_SDIFSTA_FIFOFAIL,
- 973 host-> base + S3C2410_SDIFSTA);
- 974} else {
- 975 u32 mci_con;
- 976
- 977 / * reset fifo * /
- 978 mci_con = readl(host-> base + S3C2410_SDICON);
- 979 mci_con | = S3C2410_SDICON_FIFORESET;
- 980
- 981 writel(mci_con,host-> base + S3C2410_SDICON);
- 982}
- 983}
- 984
- 985request_done:
- 986 host-> complete_what = COMPLETION_NONE;
- 987 host-> mrq = NULL;
- 988
- 989 s3cmci_check_sdio_irq(host);
- 990 mmc_request_done(host-> mmc,mrq);
- 991}
920-924行,读取SDIRSP0 -SDIRSP3寄存器,保存在cmd-> resp中。
926行,将宿主>预分频器写入SDIPRE寄存器。
937行,清零SDICmdArg寄存器。
938行,清零SDIDatCon寄存器,除了第14位设置为1,表示启动数据传输。
939行,清零SDICmdCon寄存器。
940行,清零SDIIntMsk寄存器,只允许SDIO中断。
945-949行,发送停止命令。
955-961行,计算传输的字节总数。
965-983行,如果数据传输过程出错,刷新DMA通道和FIFO,清除垃圾数据。
至此,finalize_request函数我们就分析完了,pio_tasklet函数我们也就分析完了,同时中断处理函数s3cmci_irq函数我们也就分析完了。
Linux的设备驱动程序架构分析之MMC / SD(二)相关推荐
- Linux设备驱动程序架构分析之一个I2C驱动实例
作者:刘昊昱 博客:http://blog.csdn.net/liuhaoyutz 内核版本:3.10.1 编写一个I2C设备驱动程序的工作可分为两部分,一是定义和注册I2C设备,即i2c_clien ...
- Linux设备驱动程序架构分析之I2C架构(基于3.10.1内核)
作者:刘昊昱 博客:http://blog.csdn.net/liuhaoyutz 内核版本:3.10.1 I2C体系架构的硬件实体包括两部分: 硬件I2C Adapter:硬件I2C Adapter ...
- Linux设备驱动程序架构分析之SD Spec摘要
作者:刘昊昱 博客:http://blog.csdn.net/liuhaoyutz 本文是对SDSpecifications Part 1 Physical Layer Simplified Spec ...
- linux设备驱动程序架构的研究,Linux设备驱动程序学习(12)-Linux设备模型(底层原理简介)...
Linux设备驱动程序学习(12) -Linux设备模型(底层原理简介) 以<LDD3>的说法:Linux设备模型这部分内容可以认为是高级教材,对于多数程序作者来说是不必要的.但是我个人认 ...
- linux 卸载 usbmouse,8 Linux usbmouse设备驱动程序
前一章节对linux内核中USB驱动程序的框架进行了分析,这一节以USB鼠标为对象,编写USB鼠标驱动程序. 实验内容:编写USB鼠标设备驱动程序.并将USB鼠标左键定义为"L"功 ...
- Linux字符设备驱动程序开发(1)-使用字符设备驱动
1.编译/安装驱动 在Linux系统中,驱动程序通常采用内核模块的程序结构来进行编码.因此,编译/安装一个驱动程序,其实质就是编译/安装一个内核模块.把下面的范例代码拷贝到Linux系统中: memd ...
- 一张图掌握 Linux 字符设备驱动架构!【建议收藏】
目录 一. Linux 中字符设备驱动简介 二. 字符设备驱动快速入门(超简单demo) 1. demo 2. 代码编译 3. 加载驱动模块 4. 创建设备节点文件 5. APP设备文件操作 6. 卸 ...
- Linux块设备驱动程序原理
顾名思义,块设备驱动程序就是支持以块的方式进行读写的设备.块设备和字符设备最大的区别在于读写数据的基本单元不同.块设备读写数据的基本单元为块,例如磁盘通常为一个sector,而字符设备的基本单元为字节 ...
- linux 查看 pci 设备驱动,如何写linux pci设备驱动程序
PCI总线应用领域及其广泛并且令人惊奇.不同的pci设备有不同的需求以及不同的问题.因此,在linux内核中pci层支持就非常重要啦.本文档就是想为驱动程序设计开发人员解决pci处理中的各种问题. 0 ...
最新文章
- Python基础——PyCharm版本——第三章、数据类型和变量(超详细)
- Centos7下,简单DOCKER 使用.映射SSH端口到宿主主机.
- IDEA 热部署 仅支持jdk1.6,1.7
- Win7系统不能录音怎么办
- linux新硬盘分区及格式化和挂载分区到目录的方法
- ubuntu18.04+nvidia显卡安装+cuda9.0+cudnn7+pycharm2018.2专业版激活+anaconda3+tensorflow-gpu1.6.0+keras+opencv3
- 无需共享存储发布高性能的虚拟桌面
- Mandriva小记
- 每个人都应该具备点批判性思维
- 免费U盘数据恢复软件有哪些,如何免费恢复U盘的数据
- WP模板常用调用函数
- 程序员女盆友的学习日记(没内容)
- 阿里云-邮件推送 配置 购买域名 配置域名
- 常用正则表达式(手机,邮箱,身份证号,昵称,用户名,车型,车牌号)
- idea 使用mybatis generator生成代码时 报错 The specified target project directory src/main/java/ does not exis
- multiboot之ICAP
- zuk android系统耗电高,联想ZUK手机出现大规模卡顿耗电问题 解决办法让人忍俊不禁...
- Git GitHub 简明教程
- 推荐几本与领导力相关的书,这些书可以帮助你培养领导力
- 工业互联网的破局之道:存储资源盘活系统
热门文章
- 阿伏法机器人_深圳自动捷克钻不良QC机器人操作
- 一女生上厕所忘带纸了。
- mapreduce剖析气象站平均气温
- Windows: 如何配置IPv6隧道
- 变压器噪音分贝测试软件,一体机变压器绕组变形测试仪测试软件界面
- Dota2参议院(LeetCode)
- 如何设计一份优秀的 PPT 文档?
- asp怎么循环增加字段和字段对应的值_MySQL数据类型和字段属性原理与用法详解
- spark写入hbase任务报错:NoClassDefFoundError: com/yammer/metrics/Metrics
- java在圆上画刻度线_Java – 绘制标尺(带有90度角刻度线的线)