Linux裝置驅動程式架構分析之MMC/SD(二)
作者:劉昊昱
核心版本: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函式我們也就分析完了。