作者:刘昊昱
博客:http://blog.csdn.net/liuhaoyutz
内核版本:3.10.1
一、s3cmci_ops分析
在上一篇文章中我们分析了Mini2440 MMC/SD驱动的probe函数s3cmci_probe。在该函数中初始化了struct 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层与host层的接口,mmc_host.ops是控制host完成用户请求的接口函数集,其类型是struct mmc_host_ops,该结构体定义在include/linux/mmc/host.h文件中:
83structmmc_host_ops { 84 /* 85 *'enable' is called when the host is claimed and 'disable' is called 86 *when the host is released. 'enable' and 'disable' are deprecated. 87 */ 88 int (*enable)(struct mmc_host *host); 89 int (*disable)(struct mmc_host *host); 90 /* 91 *It is optional for the host to implement pre_req and post_req in 92 *order to support double buffering of requests (prepare one 93 *request while another request is active). 94 *pre_req() must always be followed by a post_req(). 95 *To undo a call made to pre_req(), call post_req() with 96 *a nonzero err condition. 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 * Avoid calling these three functions too often or in a "fastpath", 105 * since underlaying controller might implement them in an expensive 106 * and/or slow way. 107 * 108 * Also note that these functions might sleep, so don't call them 109 * in the atomic contexts! 110 * 111 * Return values for the get_ro callback should be: 112 * 0 for a read/write card 113 * 1 for a read-only card 114 * -ENOSYS when not supported(equal to NULL callback) 115 * or a negative errno value whensomething bad happened 116 * 117 * Return values for the get_cd callback should be: 118 * 0 for a absent card 119 * 1 for a present card 120 * -ENOSYS when not supported(equal to NULL callback) 121 * or a negative errno value whensomething bad happened 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 /* optional callback for HC quirks */ 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 /* Check if the card is pulling dat[0:3] low */ 135 int (*card_busy)(struct mmc_host *host); 136 137 /* The tuning command opcode value is different for SD and eMMC cards */ 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};
request函数用于处理用户的请求。
set_ios函数用于设置SDI的控制参数,如时钟、总线宽度等等。
get_ro函数用于探测SD卡是否有写保护。
get_cd函数用于探测卡是否已插入插槽。
enable_sdio_irq函数用于启动或禁用SDI中断。
需要注意的是,为什么没有对MMC/SD进行读写的read和write函数呢?这是因为Linux块设备的读写操作是通过request函数完成的。
那么对于Mini2440,它的s3cmci_ops中的成员函数在什么时候会被调用呢?举例如下:
在drivers/mmc/core/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->claimed); 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行,调用了host->ops->request函数,即s3cmci_request函数。
再比如,在drivers/mmc/core/core.c文件中:
954/* 955* Internal function that does the actual ios call to the host driver, 956* optionally printing some debug output. 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行,调用了host->ops->set_ios函数,即s3cmci_set_ios函数。
下面我们就来看一下s3cmci_ops的各个成员函数的实现。
s3cmci_get_ro函数定义在drivers/mmc/host/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 return 0; 1380 1381 ret = gpio_get_value(pdata->gpio_wprotect) ? 1 : 0; 1382 ret ^= pdata->wprotect_invert; 1383 1384 return 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函数定义在drivers/mmc/host/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 return -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函数定义在drivers/mmc/host/s3cmci.c文件中:
1387static voids3cmci_enable_sdio_irq(struct mmc_host *mmc, int enable) 1388{ 1389 struct s3cmci_host *host = mmc_priv(mmc); 1390 unsigned long flags; 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行,我觉得这一行不应该存在,因为这一行将参数enable的值赋值给host->sdio_irqen,但是1398行又接着判断enable与host->sdio_irqen是否相等,如果相等就退出了。
1401-1408行,enable为1,使能SDIO中断。
1402行,S3C2410_SDICON_SDIOIRQ定义在drivers/mmc/host/s3cmci.c文件中:
57#define S3C2410_SDICON_SDIOIRQ (1 << 3)
对照S3C2440 Datasheet,可知这个宏用来设置SDICON寄存器的第3位,该位决定是否接收SDIO中断。
1403行,S3C2410_SDIIMSK_SDIOIRQ定义在drivers/mmc/host/s3cmci.c文件中:
105#define S3C2410_SDIIMSK_SDIOIRQ (1 << 12)
对照S3C2440 Datasheet,可知这个宏用来设置SDIIntMsk寄存器的第13位,该位决定当read wait request发生时,SDI是否产生一个中断。
enable_imask函数定义在drivers/mmc/host/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 return newmask; 289}
该函数用来设置SDIIntMsk寄存器。
1409-1416行,enable为0,禁用SDIO中断。
1419行,用新的con设置SDICON即SDI控制寄存器。
s3cmci_set_ios函数定义在drivers/mmc/host/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 /* Set the power state */ 1312 1313 mci_con = readl(host->base + S3C2410_SDICON); 1314 1315 switch (ios->power_mode) { 1316 case MMC_POWER_ON: 1317 case MMC_POWER_UP: 1318 /* Configure GPE5...GPE10 pins in SD mode */ 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 break; 1329 1330 case MMC_POWER_OFF: 1331 default: 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 break; 1341 } 1342 1343 s3cmci_set_clk(host, ios); 1344 1345 /* Set CLOCK_ENABLE */ 1346 if (ios->clock) 1347 mci_con |= S3C2410_SDICON_CLOCKTYPE; 1348 else 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, "running at %lukHz (requested: %ukHz).\n", 1356 host->real_rate/1000,ios->clock/1000); 1357 } else { 1358 dbg(host, dbg_conf, "powered down.\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; /* clock rate */ 24 unsigned short vdd; 25 26/*vdd stores the bit number of the selected voltage range from below. */ 27 28 unsigned char bus_mode; /* command output mode */ 29 30#define MMC_BUSMODE_OPENDRAIN 1 31#define MMC_BUSMODE_PUSHPULL 2 32 33 unsigned char chip_select; /* SPI chip select */ 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; /* power supply 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; /* data 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 unsigned char timing; /* timing specification used */ 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; /* signallingvoltage (1.8V or 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; /* driver 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宏定义在arch/arm/plat-samsung/include/plat/gpio-cfg.h文件中:
66#defineS3C_GPIO_SPECIAL_MARK (0xfffffff0) 67#define S3C_GPIO_SPECIAL(x)(S3C_GPIO_SPECIAL_MARK | (x)) 68 69/*Defines for generic pin configurations */ 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宏定义在arch/arm/plat-samsung/include/plat/gpio-cfg.h文件中:
126/* Define values for the pull-{up,down}available for each gpio pin. 127 * 128 * These values control the state of theweak pull-{up,down} resistors 129 * available on most pins on the S3Cseries. Not all chips support both 130 * up or down settings, and it may bedependent on the chip that is being 131 * used to whether the particular modeis available. 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函数定义在drivers/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 return ret; 3160 } 3161 3162 return 0; 3163}
可以看到,s3c_gpio_cfgall_range函数设置从GPE5开始的6个GPIO,即GPE5、GPE6、GPE7、GPE8、GPE9、GPE10。使用参数cfg设置GPECON寄存器,使用参数pull设置GPEUP寄存器。
GPECON寄存器对应的位置被设置为01,即使能相关SDI功能。
1330-1341行,如果ios->power_mode为MMC_POWER_OFF或者default,则执行这个分支。
1332行,调用gpio_direction_output(S3C2410_GPE(5),0)关闭SDI时钟。
1335行,mci_con |=S3C2440_SDICON_SDRESET,根据S3C2440数据手册,这句用于reset整个sd/mmc模块。
1343行,调用s3cmci_set_clk(host,ios)设置时钟,s3cmci_set_clk定义在drivers/mmc/host/s3cmci.c文件中:
1283static void s3cmci_set_clk(structs3cmci_host *host, struct mmc_ios *ios) 1284{ 1285 u32 mci_psc; 1286 1287 /* Set clock */ 1288 for (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 break; 1293 } 1294 1295 if (mci_psc > 255) 1296 mci_psc = 255; 1297 1298 host->prescaler = mci_psc; 1299 writel(host->prescaler, host->base + S3C2410_SDIPRE); 1300 1301 /* If requested clock is 0, real_rate will be 0, too */ 1302 if (ios->clock == 0) 1303 host->real_rate = 0; 1304}
1346-1349行,如果ios->clock不为0,使能时钟,否则禁用时钟。
1351行,将mci_con写回SDICON寄存器。
1361行,用ios->bus_width设置数据总线宽度host->bus_width。
s3cmci_request函数定义在drivers/mmc/host/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代表一个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 struct completion completion; 134 void (*done)(struct mmc_request *);/*completion function */ 135 struct mmc_host *host; 136};
1271行,将host->status设置为"mmcrequest",host->status主要用于记录request处理所处的阶段及状态,方便调试使用。
1272行,设置host->cmd_is_stop为0,从字面上理解,我认为host->cmd_is_stop代表command是否是stop命令(即有一个command是stop),0表示不是stop命令。
1273行,将mmc_requestmrp保存在host->mrq中,方便以后使用。
1275-1280行,如果卡不存在,则调用mmc_request_done(mmc,mrq)结束这次request处理,否则,调用s3cmci_send_request(mmc)。
s3cmci_send_request函数定义在drivers/mmc/host/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 /* Clear command, data and fifo statusregisters 1212 Fifo clear only necessary on 2440, butdoesn't hurt on 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 return; 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 return; 1244 } 1245 } 1246 1247 /* Send command */ 1248 s3cmci_send_command(host, cmd); 1249 1250 /* Enable Interrupt */ 1251 s3cmci_enable_irq(host, true); 1252}
1204行,由structmmc_host得到struct s3cmci_host。
1205行,从host->mrq取出mmc_request以便使用。
1206行,因为host->cmd_is_stop被设置为0,所以cmd被设置为mrq->cmd。
1214-1216行,清空SDICmdSta寄存器、SDIDatSta寄存器和SDIFSTA寄存器。
1216-1245行,如果cmd->data不为0,即当前command带有要处理的数据,则执行这个if语句块,进行数据处理的准备工作。
1219行,调用s3cmci_setup_data(host,cmd->data),该函数定义在drivers/mmc/host/s3cmci.c文件中:
1048static ints3cmci_setup_data(struct s3cmci_host *host, struct mmc_data *data) 1049{ 1050 u32 dcon, imsk, stoptries = 3; 1051 1052 /* write DCON register */ 1053 1054 if (!data) { 1055 writel(0, host->base +S3C2410_SDIDCON); 1056 return 0; 1057 } 1058 1059 if ((data->blksz & 3) != 0) { 1060 /* We cannot deal with unaligned blockswith more than 1061 * one block being transferred. */ 1062 1063 if (data->blocks > 1) { 1064 pr_warning("%s: can't donon-word sized block transfers (blksz %d)\n", __func__, data->blksz); 1065 return -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 return -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 /* write BSIZE register */ 1113 1114 writel(data->blksz, host->base +S3C2410_SDIBSIZE); 1115 1116 /* add to IMASK register */ 1117 imsk = S3C2410_SDIIMSK_FIFOFAIL |S3C2410_SDIIMSK_DATACRC | 1118 S3C2410_SDIIMSK_DATATIMEOUT | S3C2410_SDIIMSK_DATAFINISH; 1119 1120 enable_imask(host, imsk); 1121 1122 /* write TIMER register */ 1123 1124 if (host->is2440) { 1125 writel(0x007FFFFF, host->base +S3C2410_SDITIMER); 1126 } else { 1127 writel(0x0000FFFF, host->base +S3C2410_SDITIMER); 1128 1129 /* FIX: set slow clock to preventtimeouts on read */ 1130 if (data->flags & MMC_DATA_READ) 1131 writel(0xFF, host->base +S3C2410_SDIPRE); 1132 } 1133 1134 return 0; 1135}
1054-1057行,如果命令数据为空,则清零SDIDatCon寄存器。
1059-1067行,根据Datasheet的描述,如果在多模块下必须分配字大小,即BlkSize[1:0]=00,所以这里“与”3来判断是不是单模块。如果在单模块处理的情况下,模块数大于1,则出错退出。
1069-1082行,循环判断是否有数据正在发送或接收,如果有,则停止传输,并复位时钟。最多循环3次。
1086-1087行,如果使用DMA传输,则使能SDIDatCon寄存器的第15位DMA功能。
1089-1090行,如果数据总线宽度为4线,则使能SDIDatCon寄存器的第16位宽总线WideBus功能。
1092-1093行,配置SDIDatCon寄存器的第17位,数据传输模式为块传输模式。
1095-1098行,如果是写数据,配置SDIDatCon寄存器的第20位,收到回应后开始写数据。然后配置SDIDatCon寄存器的第12、13位,设置为写模式。
1100-1103行,如果是读数据,配置SDIDatCon寄存器的第19位,命令发送后开始读数据。
然后配置SDIDatCon寄存器的第12、13位,设置为读模式。
1105-1108行,如果是S3C2440,配置SDIDatCon寄存器的第22、23位为10,即传输单位为word。然后配置SDIDatCon寄存器的第14位,开始数据传输。
1110行,将dcon写入SDIDatCon寄存器。
1114行,将data->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函数,该函数定义在drivers/mmc/host/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函数定义在drivers/mmc/host/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 return 0; 1159}
1141行,根据data->flags确定是读还是写。
1151行,如果是写,则调用do_pio_write函数。do_pio_write函数定义在drivers/mmc/host/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 return; 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 /* If we have reached the end of the block, we have to 548 * write exactly the remainingnumber of bytes. If we 549 * in the middle of the block, we have to write full 550 * words, so round down to an even multiple of 4. */ 551 if (fifo >= host->pio_bytes) 552 fifo = host->pio_bytes; 553 else 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 while (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(host)函数取得FIFO的剩余可用空间的字节数保存在fifo变量中,这里,fifo变量代表这次while循环最多可写的字节个数。如果剩余空间大于3个字节,即最小写一个字,则循环条件成立。
530-532行,如果host->pio_bytes为0,则调用get_data_buffer从分散聚集列表中取得保存要写的数据的缓冲区,缓冲区的长度和起始地址分别保存在get_data_buffer的2个参数host->pio_bytes和第3个参数host->pio_ptr中。
533-539行,如果get_data_buffer出错,打印信息退出。
551-552行,如果fifo大于等于host->pio_bytes,即FIFO的可用空间大于等于保存要写数据的缓冲区长度,则将fifo设置为host->pio_bytes。
554行,如果fifo小于host->pio_bytes,即FIFO的可用空间小于要写数据的缓冲区长度,则将fifo设置为fifo – (fifo & 3)。从注释可以看到,这是为了保证以字为单位进行写操作。
556行,host->pio_bytes-= fifo,保存这次写操作后,剩余的要写的字节数。
557行,host->pio_count+= fifo,保存已经写了多少个字节。
559行,将字节数转化为字数。
561-562行,写数据到SDIDAT寄存器。
563行,host->pio_ptr= ptr,保存当前还剩余要写数据的位置。
564行,结束这次while循环,回到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(host,cmd)发送命令。
该函数定义在drivers/mmc/host/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 else 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}
1021-1025行,出现CRC状态错误、命令响应超时、接收命令响应、命令发送、响应CRC校验失败时,将产生SDI中断。
1027-1032行,设置host->complete_what。
在drivers/mmc/host/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};
另外,在drivers/mmc/host/s3cmci.c文件的s3cmci_irq函数的注释中,有如下内容:
603 * host->complete_what Indicates when the request is considered done 604 * COMPLETION_CMDSENT when thecommand was sent 605 * COMPLETION_RSPFIN when aresponse was received 606 * COMPLETION_XFERFINISH whenthe data transfer is finished 607 * COMPLETION_XFERFINISH_RSPFIN both of the above.
1034行,用cmd->arg设置设置SDICmdArg寄存器。
1036-1045行,配置SDICmdCon寄存器。
1036行,取得命令索引。
1037行,命令启动。
1039-1040行,配置host等待响应。
1042-1043行,配置host接收136位长响应。
回到s3cmci_send_request函数:
1251行,使能中断。
至此,s3cmci_send_request函数我们就分析完了。
s3cmci_request函数我们也就分析完了。
s3cmci_ops结构体我们也就分析完了。
二、中断处理函数s3cmci_irq分析
SDI中断处理函数s3cmci_irq定义在drivers/mmc/host/s3cmci.c文件中:
599/* 600 * ISR for SDI Interface IRQ 601 * Communication between driver and ISRworks as follows: 602 * host->mrq points tocurrent request 603 * host->complete_what Indicates when the request is considered done 604 * COMPLETION_CMDSENT when thecommand was sent 605 * COMPLETION_RSPFIN when aresponse was received 606 * COMPLETION_XFERFINISH whenthe data transfer is finished 607 * COMPLETION_XFERFINISH_RSPFIN both of the above. 608 * host->complete_request is the completion-object the driver waits for 609 * 610 * 1) Driver sets up host->mrq andhost->complete_what 611 * 2) Driver prepares the transfer 612 * 3) Driver enables interrupts 613 * 4) Driver starts transfer 614 * 5) Driver waits forhost->complete_rquest 615 * 6) ISR checks for request status (errorsand success) 616 * 6) ISR setshost->mrq->cmd->error and host->mrq->data->error 617 * 7) ISR completeshost->complete_request 618 * 8) ISR disables interrupts 619 * 9) Driver wakes up and takes care of therequest 620 * 621 * Note: "->error"-fields areexpected to be set to 0 before the request 622 * was issued by mmc.c - therefore they are only set, when an error 623 * contition comes up 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 return 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: ignore CRCfail with long rsp\n"); 717 } else { 718 /* note, we used to fail thetransfer 719 * here, but it seems that thisis just 720 * the hardware getting itwrong. 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: command response received"; 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 /* errors handled after this point are only relevant 745 when a data transfer is in progress */ 746 747 if (!cmd->data) 748 goto clear_status_bits; 749 750 /* Check for FIFO failure */ 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 return 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行,根据Datasheet,这两句的作用是清零SDIDatSta寄存器的第9位。
642行,调用mmc_signal_sdio_irq函数处理SDIO中断,该函数定义在include/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,则执行这个if分支。
676-677行,如果host->pio_active为XFER_WRITE,并且SDIFSTA寄存器的第13位被置位,表明FIFO可以用于写操作。
679行,禁用TFHalf中断。
680行,调用host->pio_tasklet。
681行,设置host->status为"piotx"。
684-685行,如果host->pio_active为XFER_READ,并且SDIFSTA寄存器的第12位被置位,表明FIFO可以用于读操作。
687-689行,禁用Rx FIFO相关中断。
691行,调用host->pio_tasklet。
692行,设置host->status为"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行,处理数据传输结束。
下面我们来看host->pio_tasklet,在s3cmci_probe函数中,有如下语句:
1662 tasklet_init(&host->pio_tasklet,pio_tasklet, (unsigned long) host);
可以看到,host->pio_tasklet对应的tasklet函数为pio_tasklet,并将host做为参数传递给该函数。pio_tasklet函数定义在drivers/mmc/host/s3cmci.c文件中:
569static void pio_tasklet(unsigned long data) 570{ 571 struct s3cmci_host *host = (struct s3cmci_host *) data; 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, "unfinished %s " 585 "- pio_count:[%u]pio_bytes:[%u]\n", 586 (host->pio_active ==XFER_READ) ? "read" : "write", 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行,如果host->pio_active为XFER_WRITE,即写数据,则调用do_pio_write(host)函数,该函数我们前面已经分析过了。
578-579行,如果host->pio_active为XFER_READ,即读数据,则调用do_pio_read(host)函数,该函数定义在drivers/mmc/host/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 /* write real prescaler to host, it might be set slow to fix */ 446 writel(host->prescaler, host->base + S3C2410_SDIPRE); 447 448 from_ptr = host->base + host->sdidata; 449 450 while ((fifo = fifo_count(host))) { 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 return; 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] buffer:[%03i] dcnt:[%08X]\n", 470 fifo, host->pio_bytes, 471 readl(host->base + S3C2410_SDIDCNT)); 472 473 /* If we have reached the end of the block, we can 474 * read a word and get 1 to 3 bytes. If we in the 475 * middle of the block, we have to read full words, 476 * otherwise we will write garbage, so round down to 477 * an even multiple of 4. */ 478 if (fifo >= host->pio_bytes) 479 fifo = host->pio_bytes; 480 else 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 while (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 while (n--) { 498 *p++ = data; 499 data >>= 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 return; 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函数,从分散聚集列表中获取用于存放被读取数据的缓冲区的相关信息,缓冲区的长度保存在host->pio_bytes中,缓冲区的起始地址保存在host->pio_ptr中。如果get_data_buffer函数返回非0值,表示read操作完成。
478-481行,如果FIFO中可读取数据的字节数大于host->pio_bytes(即缓冲区的大小),则将fifo设置为host->pio_bytes,否则fifo -=fifo & 3。从473-477行的注释可以看出,这样做是为了按字来读取数据。
483行,修改host->pio_bytes的值,缓冲区还有多少字节的空间。
484行,已经读取的数据的字节数保存在host->pio_count变量中。
486行,以字为单位,要读取的数据个数保存在fifo_words变量中。
488-489行,循环读取数据。
490行,保存下次要读取的数据的起始位置到host->pio_ptr中。
492-501行,读取剩余的非字节对齐部分。
502行,结束这次while循环,回到450行,判断FIFO中是否还有可读的数据,如果有的话,继续进行读取操作。
504-514行,如果host->pio_bytes为0,并且get_data_buffer函数返回非0值,表示没有可用的缓冲区空间,read结束。
516-517行,便能read中断。
至此,do_pio_read函数我们就分析完了。
回到pio_tasklet函数:
581-596行,如果命令处理结束,则调用finalize_request进行最后的处理。否则,打开中断,继续监听中断。finalize_request函数定义在drivers/mmc/host/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 return; 906 907 if (!mrq) 908 return; 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 return; 917 } 918 } 919 920 /* Read response from controller. */ 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->prescaler, 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 /* Cleanup controller */ 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 return; 949 } 950 951 /* If we have no data transfer we are finished here */ 952 if (!mrq->data) 953 goto request_done; 954 955 /* Calculate the amout of bytes transfer if there was no error */ 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 /* If we had an error while transferring data we flush the 964 * DMA channel and the fifo to clear out any garbage. */ 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 /* Clear failure register and reset 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行,将host->prescaler写入SDIPRE寄存器。
937行,清零SDICmdArg寄存器。
938行,清零SDIDatCon寄存器,除了第14位设置为1,表示启动数据传输。
939行,清零SDICmdCon寄存器。
940行,清零SDIIntMsk寄存器,只允许SDIO中断。
945-949行,发送stop命令。
955-961行,计算传输的字节总数。
965-983行,如果数据传输过程出错,刷新DMA通道和FIFO,清除垃圾数据。
至此,finalize_request函数我们就分析完了,pio_tasklet函数我们也就分析完了,同时中断处理函数s3cmci_irq函数我们也就分析完了。