Linux设备驱动程序架构分析之MMC/SD(二)

作者:刘昊昱 

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

你可能感兴趣的:(Linux设备驱动程序架构分析之MMC/SD(二))