if (s3cmci_card_present(mmc) == 0) { dbg(host, dbg_err, "%s: no medium present\n", __func__); host->mrq->cmd->error = -ENOMEDIUM; mmc_request_done(mmc, mrq);//如果卡不存在,就终止请求 } else s3cmci_send_request(mmc); } 接下来看s3cmci_send_request(mmc): 这个函数先判断一下请求时传输数据还是命令,如果是数据的话: 先调用s3cmci_setup_data来对S3C2410_SDIDCON寄存器进行设置,然后设置SDITIMER寄存器这就设置好了总线宽度,是否使用DMA,,并启动了数据传输模式,并且使能了下面这些中断: imsk = S3C2410_SDIIMSK_FIFOFAIL | S3C2410_SDIIMSK_DATACRC | S3C2410_SDIIMSK_DATATIMEOUT | S3C2410_SDIIMSK_DATAFINISH; 解析来判断是否是采用DMA进行数据传输还是采用FIFO进行数据传输 if (host->dodma) / because host->dodma = 0,so we don't use it res = s3cmci_prepare_dma(host, cmd->data);//准备DMA传输, else res = s3cmci_prepare_pio(host, cmd->data);.//准备FIFO传输 如果是命令的话:则调用s3cmci_send_command()这个函数是命令发送的函数,和datesheet上描述的过程差不多,关于SD规范中命令的格式,请参考参考资料1. writel(cmd->arg, host->base + S3C2410_SDICMDARG);/*先写参数寄存器 ccon = cmd->opcode & S3C2410_SDICMDCON_INDEX;//确定命令种类 ccon |= S3C2410_SDICMDCON_SENDERHOST | S3C2410_SDICMDCON_CMDSTART; /*with start 2bits*/ if (cmd->flags & MMC_RSP_PRESENT) ccon |= S3C2410_SDICMDCON_WAITRSP; /*wait rsp*/ if (cmd->flags & MMC_RSP_136) ccon |= S3C2410_SDICMDCON_LONGRSP; //确定respose的种类 writel(ccon, host->base + S3C2410_SDICMDCON);
命令通道分析完了,我们分析数据通道,先分析采用FIFO方式传输是怎么样实现的。 先分析s3cmci_prepare_pio(host, cmd->data) 根据rw来判断是读还是写 if (rw) { do_pio_write(host); /* Determines SDI generate an interrupt if Tx FIFO fills half*/ enable_imask(host, S3C2410_SDIIMSK_TXFIFOHALF); } else { enable_imask(host, S3C2410_SDIIMSK_RXFIFOHALF | S3C2410_SDIIMSK_RXFIFOLAST); } 如果是写数据到SD的话,会调用do_pio_write,往FIFO中填充数据。当64字节的FIFO少于33字节时就会产生中断。如果是从SD读数据,则先使能中断,当FIFO多于31字节时时,则会调用中断服务程序,中断服务程序中将会调用do_pio_read FIFO的数据读出。 接下来分析do_pio_write: to_ptr = host->base + host->sdidata; fifo_free(host)用来检测fifo剩余空间 while ((fifo = fifo_free(host)) > 3) { if (!host->pio_bytes) { res = get_data_buffer(host, &host->pio_bytes, /* If we have reached the end of the block, we have to * write exactly the remaining number of bytes. If we * in the middle of the block, we have to write full * words, so round down to an even multiple of 4. */ if (fifo >= host->pio_bytes)//fifo的空间比pio_bytes大,表明这是读这个块的最后一次 fifo = host->pio_bytes; /* because the volume of FIFO can contain the remaning block*/ else fifo -= fifo & 3;/*round down to an even multiple of 4*/
host->pio_bytes -= fifo;//更新还剩余的没有写完的字 host->pio_count += fifo;/*chang the value of pio_bytes*/
fifo = (fifo + 3) >> 2;//将字节数转化为字数 /*how many words fifo contain,every time we just writ one word*/ ptr = host->pio_ptr; while (fifo--) writel(*ptr++, to_ptr);//写往FIFO. host->pio_ptr = ptr; } 注释一:注意,MMC核心为mrq->data成员分配了一个struct scatterlist的表,用来支持分散聚集,使用这种方法,这样使物理上不一致的内存页,被组装成一个连续的数组,避免了分配大的缓冲区的问题 我们看代码 if (host->pio_sgptr >= host->mrq->data->sg_len) { dbg(host, dbg_debug, "no more buffers (%i/%i)\n", host->pio_sgptr, host->mrq->data->sg_len); return -EBUSY; } sg = &host->mrq->data->sg[host->pio_sgptr]; *bytes = sg->length;//页缓冲区中的长度 *pointer = sg_virt(sg);将页地址映射为虚拟地址 host->pio_sgptr++;这里表明我们的程序又完成了一次映射 这样,每一个mmc请求,我们只能处理scatterlist表中的一个页(块)。因此,完成一次完整的请求需要映射sg_len次 再来总结一下一个mmc写设备请求的过程: 在s3cmci_prepare_pio中我们第一次先调用do_pio_write,如果FIFO空间大于3,且能够获取到scatterlist,则我们就开始往FIFO写数据,当FIFO空间小于3,则使能TXFIFOHALF中断,在中断服务程序中,如果检测到TFDET表明又有FIFO空间了,则关闭TXFIFOHALF中断,并调用do_pio_write进行写。 数据流向如下:scatterlist-------->fifo---------->sdcard 一个mmc读设备请求的过程数据流向如下:sdcard --------> fifo ---------->scatterlist, ????关于读数据的过程,中断的触发不是很清楚,s3cmci_prepare_pio中enable_imask(host, S3C2410_SDIIMSK_RXFIFOHALF,S3C2410_SDIIMSK_RXFIFOLAST);但如果没从SD卡中读数据,怎么会引发这个中断呢?是由S3C2410_SDIIMSK_RXFIFOLAST引起的吗 接下来我们分析一下中断服务程序: static irqreturn_t s3cmci_irq(int irq, void *dev_id) 该程序先获取所有的状态寄存器: mci_csta = readl(host->base + S3C2410_SDICMDSTAT); mci_dsta = readl(host->base + S3C2410_SDIDSTA); mci_dcnt = readl(host->base + S3C2410_SDIDCNT); mci_fsta = readl(host->base + S3C2410_SDIFSTA); mci_imsk = readl(host->base + host->sdiimsk); 这些将作为中断处理的依据。 如果不是DMA模式,则处理数据的收发 if (!host->dodma) { if ((host->pio_active == XFER_WRITE) && (mci_fsta & S3C2410_SDIFSTA_TFDET)) { /*This bit indicates that FIFO data is available for transmit when DatMode is data transmit mode. If DMA mode is enable, sd host requests DMA operation.*/ disable_imask(host, S3C2410_SDIIMSK_TXFIFOHALF); tasklet_schedule(&host->pio_tasklet); 注意我们采用tasklet这种延时机制来减少中断服务的时间,延时函数pio_tasklet中调用了do_pio_write和了do_pio_read host->status = "pio tx"; }
if ((host->pio_active == XFER_READ) && (mci_fsta & S3C2410_SDIFSTA_RFDET)) {