1. 程式人生 > >Linux裝置驅動程式架構分析之MMC/SD(二)

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函式我們也就分析完了。