作者:刘昊昱
博客: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函数我们也就分析完了。