Linux SD卡驱动开发(四) —— SD 控制器之真正的硬件操作

前面对SD卡控制器有了一个基本的介绍。其实SD控制器层更过的意义是为core层提供一种操作SD卡硬件的一种方法,当然不同的控制器对硬件控制的方法不尽相同,但是他们最终都能像core层提交一个统一的封装有操作方法的数据结构,那便是即将闪亮登场的struct mmc_host_ops....对应的host文件为s3cmci.c。

    接下来就来揭开与之对应的struct mmc_host_ops结构的神秘面纱....

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. static struct mmc_host_ops s3cmci_ops = {    
  2.     .request    = s3cmci_request,    
  3.     .set_ios    = s3cmci_set_ios,    
  4.     .get_ro     = s3cmci_get_ro,    
  5.     .get_cd     = s3cmci_card_present,    
  6.     .enable_sdio_irq = s3cmci_enable_sdio_irq,    
  7. };    

       在讲述每个方法具体的实现之前,先来对struct mmc_host_ops结构中的各个成员有个简单的认识。

request方法:无论是前面所说的单纯的命令传输,还是带有数据的传输过程,无一例外最终都是调用request来实现的,那么如您所想,他也将成为这个舞台万众瞩目的焦点。

set_ios方法:用于设置SD卡控制器,前面我们所见到的设置控制器时钟,数据线宽度等等一系列操作最终就是通过他来实现的。

get_ro方法:获取卡的写保护状态,前面所过,SD卡初始化完成以后,我们进行的一个最后的工作便是检测卡的写保护状态,其实就是调用get_ro方法。

get_cd方法:检测卡是否在卡槽之中,它所对应的函数前面已经在初始化中分析过了,这里不再单独列来。

enable_sdio_irq方法:就是使能SDIO卡的中断,这个是对sdio卡而言的,这里将不做重点分析。

       有了一个初步的了解之后,接下来的时间就来各个击破了,本着由浅入深的原则我们先来看看s3cmci_get_ro。


一、s3cmci_get_ro

      从SD卡结构上来说有个写保护的开关,这就使得判断SD卡是否写保护可以从其机械特征上入手,从而特殊设计的SD卡槽为我们提供了方便。在这里采用的方法正是利用了这种特殊设计的SD卡槽带来的优势,因此只需要读取SD卡槽的SD写保护引脚的状态就能判定卡写保护的情况。实现的代码如下:

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. static int s3cmci_get_ro(struct mmc_host *mmc)  
  2. {  
  3.     struct s3cmci_host *host = mmc_priv(mmc);  
  4.     struct s3c24xx_mci_pdata *pdata = host->pdata;  
  5.     int ret;  
  6.   
  7.     if (pdata->no_wprotect)  
  8.         return 0;  
  9.   
  10.     ret = gpio_get_value(pdata->gpio_wprotect) ? 1 : 0;  
  11.     ret ^= pdata->wprotect_invert;  
  12.   
  13.     return ret;  
  14. }  

第10行正是获取SD写保护引脚的值,当然由于硬件设计上的不同可能带来状态上的取反,所以这里有个pdata->wprotect_invert标记决定是否应该反相。对于只读来说应该返回1,否则该方法的返回值为0。


二、s3cmci_set_ios

      根据我们前面所见到的种种设置,这里的ioset可能会相对烦锁一些,具体的代码如下:

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. static void s3cmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)  
  2. {  
  3.     struct s3cmci_host *host = mmc_priv(mmc);  
  4.     u32 mci_con;  
  5.   
  6.     /* Set the power state */  
  7.   
  8.     mci_con = readl(host->base + S3C2410_SDICON);  
  9.   
  10.     switch (ios->power_mode) {  
  11.     case MMC_POWER_ON:  
  12.     case MMC_POWER_UP:  
  13.         /* Configure GPE5...GPE10 pins in SD mode */  
  14.         s3c_gpio_cfgall_range(S3C2410_GPE(5), 6, S3C_GPIO_SFN(2),  
  15.                       S3C_GPIO_PULL_NONE);  
  16.   
  17.         if (host->pdata->set_power)  
  18.             host->pdata->set_power(ios->power_mode, ios->vdd);  
  19.   
  20.         if (!host->is2440)  
  21.             mci_con |= S3C2410_SDICON_FIFORESET;  
  22.   
  23.         break;  
  24.   
  25.     case MMC_POWER_OFF:  
  26.     default:  
  27.         gpio_direction_output(S3C2410_GPE(5), 0);  
  28.   
  29.         if (host->is2440)  
  30.             mci_con |= S3C2440_SDICON_SDRESET;  
  31.   
  32.         if (host->pdata->set_power)  
  33.             host->pdata->set_power(ios->power_mode, ios->vdd);  
  34.   
  35.         break;  
  36.     }  
  37.   
  38.     s3cmci_set_clk(host, ios);  
  39.   
  40.     /* Set CLOCK_ENABLE */  
  41.     if (ios->clock)  
  42.         mci_con |= S3C2410_SDICON_CLOCKTYPE;  
  43.     else  
  44.         mci_con &= ~S3C2410_SDICON_CLOCKTYPE;  
  45.   
  46.     writel(mci_con, host->base + S3C2410_SDICON);  
  47.   
  48.     if ((ios->power_mode == MMC_POWER_ON) ||  
  49.         (ios->power_mode == MMC_POWER_UP)) {  
  50.         dbg(host, dbg_conf, "running at %lukHz (requested: %ukHz).\n",  
  51.             host->real_rate/1000, ios->clock/1000);  
  52.     } else {  
  53.         dbg(host, dbg_conf, "powered down.\n");  
  54.     }  
  55.   
  56.     host->bus_width = ios->bus_width;  
  57. }  

8行对SD卡控制器的设置最直接的莫过于对寄存器S3C2410_SDICON的访问了,为了保证后面不改变其他无关位的值,这里先读取S3C2410_SDICON中的当前值保存。

10-39行是SD控制器工作状态的设定,对ioset来说,swith无疑是他最好的朋友,当MMC_POWER_UP时,SD控制器的相应管脚会得到正确的初始化。其他的如fifo也将被正确复位。

41-47行就是对sd控制器时钟的设置,最终一切ioset的成功归功于49行将重新设置的S3C2410_SDICON状态写入寄存器,从此新的控制器状态生效。



        那么在主机驱动层中的一个请求处理是怎么通过核心层提交到块设备请求层的呢?

在网上找到一副图来说明他们之间的关联和处理流程,如下图:


三、s3cmci_request

       结构体mmc_request定义于/include/linux/mmc/core.h,它主要存放两大数据结构的指针,分别是cmddata,顾名思意,一个为指令,一个为数据

       也就是说,mmc_request结构体存放了进行主控制器与sd卡间通信所需要的指令和数据

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. static void s3cmci_request(struct mmc_host *mmc, struct mmc_request *mrq)  
  2. {  
  3.     struct s3cmci_host *host = mmc_priv(mmc);  
  4.   
  5.     host->status = "mmc request";  
  6.     host->cmd_is_stop = 0;  
  7.     host->mrq = mrq;  
  8.   
  9.     if (s3cmci_card_present(mmc) == 0) {  
  10.         dbg(host, dbg_err, "%s: no medium present\n", __func__);  
  11.         host->mrq->cmd->error = -ENOMEDIUM;  
  12.         mmc_request_done(mmc, mrq);  
  13.     } else  
  14.         s3cmci_send_request(mmc);  
  15. }  

第9行判断SD卡是否还在卡槽之中,如果已经拔出,那不客气mmc_request_done将帮您结束这个请求。怎么个解决法还是先看看mmc_request_done的代码:

mmc_request_done

[core/core.c]

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. /** 
  2.  *  mmc_request_done - finish processing an MMC request 
  3.  *  @host: MMC host which completed request 
  4.  *  @mrq: MMC request which request 
  5.  * 
  6.  *  MMC drivers should call this function when they have completed 
  7.  *  their processing of a request. 
  8.  */  
  9. void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)  
  10. {  
  11.     struct mmc_command *cmd = mrq->cmd;  
  12.     int err = cmd->error;  
  13.   
  14.     if (err && cmd->retries && mmc_host_is_spi(host)) {  
  15.         if (cmd->resp[0] & R1_SPI_ILLEGAL_COMMAND)  
  16.             cmd->retries = 0;  
  17.     }  
  18.   
  19.     if (err && cmd->retries && !mmc_card_removed(host->card)) {  
  20.         /* 
  21.          * Request starter must handle retries - see 
  22.          * mmc_wait_for_req_done(). 
  23.          */  
  24.         if (mrq->done)  
  25.             mrq->done(mrq);  
  26.     } else {  
  27.         mmc_should_fail_request(host, mrq);  
  28.   
  29.         led_trigger_event(host->led, LED_OFF);  
  30.   
  31.         pr_debug("%s: req done (CMD%u): %d: %08x %08x %08x %08x\n",  
  32.             mmc_hostname(host), cmd->opcode, err,  
  33.             cmd->resp[0], cmd->resp[1],  
  34.             cmd->resp[2], cmd->resp[3]);  
  35.   
  36.         if (mrq->data) {  
  37.             pr_debug("%s:     %d bytes transferred: %d\n",  
  38.                 mmc_hostname(host),  
  39.                 mrq->data->bytes_xfered, mrq->data->error);  
  40.         }  
  41.   
  42.         if (mrq->stop) {  
  43.             pr_debug("%s:     (CMD%u): %d: %08x %08x %08x %08x\n",  
  44.                 mmc_hostname(host), mrq->stop->opcode,  
  45.                 mrq->stop->error,  
  46.                 mrq->stop->resp[0], mrq->stop->resp[1],  
  47.                 mrq->stop->resp[2], mrq->stop->resp[3]);  
  48.         }  
  49.   
  50.         if (mrq->done)  
  51.             mrq->done(mrq);  
  52.   
  53.         mmc_host_clk_release(host);  
  54.     }  
  55. }  

14行如果是SPI传输出现错误,而且还有重试的机会,那么只要SPI不忽略这个命令,那么就还是给他重试的机会,也就到了85-91行继续调用host->ops->request(host, mrq);提交请求,否则既然是SPI忽略了这个命令,无论重试多少次都不会有结果,那么就干脆一不做二不休cmd->retries = 0;

14-16行就是只要设备有重生的机会就还是继续拯救...

26-51行如果传输无误或者重试次数到了,就会执行。其中多半是调试信息。

50-51行许下的承诺就好比欠下的债,前面我们讨论mmc_wait_for_req的时候有这么两句:

 mrq->done_data = &complete;
 mrq->done = mmc_wait_done;

然后我们说N年以后的某一天我们会和mmc_wait_done 再聚首,这里51 行便是调用的mmc_wait_done。内容如下:

mmc_wait_done

[core/core.c]

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. static void mmc_wait_done(struct mmc_request *mrq)  
  2. {  
  3.     complete(mrq->done_data);  
  4. }  

     还记得mmc_wait_for_req中为了你苦苦等待的那个wait_for_completion(&complete),因为等待,所以她进入了睡眠。现在事情做完了,他重新回来调用complete(mrq->done_data)唤醒这个沉睡的内核精灵。说到这好奇的人难免会问,那要是一直出错又该是谁来唤醒他呢?带着疑问我们继续向前....   

    回到s3cmci_request,如果卡还存在的话s3cmci_send_request将真正开始这个请求的处理。

s3cmci_send_request

[host/s3cmci.c]

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. static void s3cmci_send_request(struct mmc_host *mmc)  
  2. {  
  3.     struct s3cmci_host *host = mmc_priv(mmc);  
  4.     struct mmc_request *mrq = host->mrq;  
  5.     struct mmc_command *cmd = host->cmd_is_stop ? mrq->stop : mrq->cmd;  
  6.   
  7.     host->ccnt++;  
  8.     prepare_dbgmsg(host, cmd, host->cmd_is_stop);  
  9.   
  10.     /* Clear command, data and fifo status registers 
  11.        Fifo clear only necessary on 2440, but doesn't hurt on 2410 
  12.     */  
  13.     writel(0xFFFFFFFF, host->base + S3C2410_SDICMDSTAT);  
  14.     writel(0xFFFFFFFF, host->base + S3C2410_SDIDSTA);  
  15.     writel(0xFFFFFFFF, host->base + S3C2410_SDIFSTA);  
  16.   
  17.     if (cmd->data) {  
  18.         int res = s3cmci_setup_data(host, cmd->data);  
  19.   
  20.         host->dcnt++;  
  21.   
  22.         if (res) {  
  23.             dbg(host, dbg_err, "setup data error %d\n", res);  
  24.             cmd->error = res;  
  25.             cmd->data->error = res;  
  26.   
  27.             mmc_request_done(mmc, mrq);  
  28.             return;  
  29.         }  
  30.   
  31.         if (s3cmci_host_usedma(host))  
  32.             res = s3cmci_prepare_dma(host, cmd->data);  
  33.         else  
  34.             res = s3cmci_prepare_pio(host, cmd->data);  
  35.   
  36.         if (res) {  
  37.             dbg(host, dbg_err, "data prepare error %d\n", res);  
  38.             cmd->error = res;  
  39.             cmd->data->error = res;  
  40.   
  41.             mmc_request_done(mmc, mrq);  
  42.             return;  
  43.         }  
  44.     }  
  45.   
  46.     /* Send command */  
  47.     s3cmci_send_command(host, cmd);  
  48.   
  49.     /* Enable Interrupt */  
  50.     s3cmci_enable_irq(host, true);  
  51. }  

13-15行全部写入1,是为了清除之前传输的SDI命令状态寄存器、SDI数据状态寄存器以及SDI FIFO状态寄存器。这是在一次新的传输之前所必须有的初始化工作,否则可能出现未知的状态错误。

17行cmd->data实际上就是mmc_request->data,前面没少对他进行介绍。与之相类似的还有stop->data。这里我们姑且不说带有数据的传输过程,先来看看SD卡命令的实现。也就是1171行s3cmci_send_command(host, cmd);


命令、数据发送流程如下图:


1、发送命令

s3cmci_send_command(host, cmd)

[host/s3cmci.c]

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. static void s3cmci_send_command(struct s3cmci_host *host,  
  2.                     struct mmc_command *cmd)  
  3. {  
  4.     u32 ccon, imsk;  
  5.   
  6.     imsk  = S3C2410_SDIIMSK_CRCSTATUS | S3C2410_SDIIMSK_CMDTIMEOUT |  
  7.         S3C2410_SDIIMSK_RESPONSEND | S3C2410_SDIIMSK_CMDSENT |  
  8.         S3C2410_SDIIMSK_RESPONSECRC;  
  9.   
  10.     enable_imask(host, imsk);  
  11.   
  12.     if (cmd->data)  
  13.         host->complete_what = COMPLETION_XFERFINISH_RSPFIN;  
  14.     else if (cmd->flags & MMC_RSP_PRESENT)  
  15.         host->complete_what = COMPLETION_RSPFIN;  
  16.     else  
  17.         host->complete_what = COMPLETION_CMDSENT;  
  18.   
  19.     writel(cmd->arg, host->base + S3C2410_SDICMDARG);  
  20.   
  21.     ccon  = cmd->opcode & S3C2410_SDICMDCON_INDEX;  
  22.     ccon |= S3C2410_SDICMDCON_SENDERHOST | S3C2410_SDICMDCON_CMDSTART;  
  23.   
  24.     if (cmd->flags & MMC_RSP_PRESENT)  
  25.         ccon |= S3C2410_SDICMDCON_WAITRSP;  
  26.   
  27.     if (cmd->flags & MMC_RSP_136)  
  28.         ccon |= S3C2410_SDICMDCON_LONGRSP;  
  29.   
  30.     writel(ccon, host->base + S3C2410_SDICMDCON);  
  31. }  

6-8行是使能相应的中断,其中包括CRC校验错误、命令超时、收到命令响应等等。具体的中断屏蔽寄存器的内容可以参考S3C2440用户手册。

12-17行实际上指当前的这个命令结束时候应该所处的状态,中断处理函数将实际硬件的完成情况与这个状态相比较,最终得到这个命令执行的结果。而cmd->flags正是前面提交命令之前根据不同命令的实际情况来设置的,比如具有应答数据的命令可能需要设置cmd->flags |= MMC_RSP_PRESENT。然后对应的结束状态也就应该是COMPLETION_RSPFIN收到应答。前面说过host->complete_what是个枚举类型的变量包含了整个命令过程的各个阶段,内容如下:

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. enum s3cmci_waitfor {    
  2.     COMPLETION_NONE,    
  3.     COMPLETION_FINALIZE,    
  4.     COMPLETION_CMDSENT,    
  5.     COMPLETION_RSPFIN,    
  6.     COMPLETION_XFERFINISH,    
  7.     COMPLETION_XFERFINISH_RSPFIN,    
  8. };    

一般的命令可能无应答阶段,我们默认数据传输正确完成以后即认为命令执行完成也就是17行对应的host->complete_what = COMPLETION_CMDSENT;

19行是对命令命令参数寄存器的设置,cmd->arg是一个32bit的整数,这里如实填写即可。

21行之后的内容就是对控制寄存器的设置了,由于控制寄存器比较重要,这里列出他寄存器位的信息如下:对照上表应该不难分析函数中所设置的每一位的具体含义,这里就不再一一解释了。SDICmdCon[8]的置位使得SD控制器开始发送命令。回到s3cmci_send_request....

前一段第50行s3cmci_enable_irq(host, true); 就是使能SDI 控制器的中断。然而s3cmci_send_command 中间的944 行设置imr|=S3C2410_SDIIMSK_CMDSENT,命中注定命令发出以后产生一个相应的中断,接下来就进入probe 阶段所注册的那个SDI 中断request_irq(host->irq, s3cmci_irq, 0, DRIVER_NAME, host)

request_irq(host->irq, s3cmci_irq, 0, DRIVER_NAME, host)

[host/s3cmci.c]

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. /* 
  2.  * ISR for SDI Interface IRQ 
  3.  * Communication between driver and ISR works as follows: 
  4.  *   host->mrq           points to current request 
  5.  *   host->complete_what Indicates when the request is considered done 
  6.  *     COMPLETION_CMDSENT     when the command was sent 
  7.  *     COMPLETION_RSPFIN          when a response was received 
  8.  *     COMPLETION_XFERFINISH      when the data transfer is finished 
  9.  *     COMPLETION_XFERFINISH_RSPFIN both of the above. 
  10.  *   host->complete_request  is the completion-object the driver waits for 
  11.  * 
  12.  * 1) Driver sets up host->mrq and host->complete_what 
  13.  * 2) Driver prepares the transfer 
  14.  * 3) Driver enables interrupts 
  15.  * 4) Driver starts transfer 
  16.  * 5) Driver waits for host->complete_rquest 
  17.  * 6) ISR checks for request status (errors and success) 
  18.  * 6) ISR sets host->mrq->cmd->error and host->mrq->data->error 
  19.  * 7) ISR completes host->complete_request 
  20.  * 8) ISR disables interrupts 
  21.  * 9) Driver wakes up and takes care of the request 
  22.  * 
  23.  * Note: "->error"-fields are expected to be set to 0 before the request 
  24.  *       was issued by mmc.c - therefore they are only set, when an error 
  25.  *       contition comes up 
  26.  */  
  27.   
  28. static irqreturn_t s3cmci_irq(int irq, void *dev_id)  
  29. {  
  30.     struct s3cmci_host *host = dev_id;  
  31.     struct mmc_command *cmd;  
  32.     u32 mci_csta, mci_dsta, mci_fsta, mci_dcnt, mci_imsk;  
  33.     u32 mci_cclear = 0, mci_dclear;  
  34.     unsigned long iflags;  
  35.   
  36.     mci_dsta = readl(host->base + S3C2410_SDIDSTA);  
  37.     mci_imsk = readl(host->base + host->sdiimsk);  
  38.   
  39.     if (mci_dsta & S3C2410_SDIDSTA_SDIOIRQDETECT) {  
  40.         if (mci_imsk & S3C2410_SDIIMSK_SDIOIRQ) {  
  41.             mci_dclear = S3C2410_SDIDSTA_SDIOIRQDETECT;  
  42.             writel(mci_dclear, host->base + S3C2410_SDIDSTA);  
  43.   
  44.             mmc_signal_sdio_irq(host->mmc);  
  45.             return IRQ_HANDLED;  
  46.         }  
  47.     }  
  48.   
  49.     spin_lock_irqsave(&host->complete_lock, iflags);  
  50.   
  51.     mci_csta = readl(host->base + S3C2410_SDICMDSTAT);  
  52.     mci_dcnt = readl(host->base + S3C2410_SDIDCNT);  
  53.     mci_fsta = readl(host->base + S3C2410_SDIFSTA);  
  54.     mci_dclear = 0;  
  55.   
  56.     if ((host->complete_what == COMPLETION_NONE) ||  
  57.         (host->complete_what == COMPLETION_FINALIZE)) {  
  58.         host->status = "nothing to complete";  
  59.         clear_imask(host);  
  60.         goto irq_out;  
  61.     }  
  62.   
  63.     if (!host->mrq) {  
  64.         host->status = "no active mrq";  
  65.         clear_imask(host);  
  66.         goto irq_out;  
  67.     }  
  68.   
  69.     cmd = host->cmd_is_stop ? host->mrq->stop : host->mrq->cmd;  
  70.   
  71.     if (!cmd) {  
  72.         host->status = "no active cmd";  
  73.         clear_imask(host);  
  74.         goto irq_out;  
  75.     }  
  76.   
  77.     if (!s3cmci_host_usedma(host)) {  
  78.         if ((host->pio_active == XFER_WRITE) &&  
  79.             (mci_fsta & S3C2410_SDIFSTA_TFDET)) {  
  80.   
  81.             disable_imask(host, S3C2410_SDIIMSK_TXFIFOHALF);  
  82.             tasklet_schedule(&host->pio_tasklet);  
  83.             host->status = "pio tx";  
  84.         }  
  85.   
  86.         if ((host->pio_active == XFER_READ) &&  
  87.             (mci_fsta & S3C2410_SDIFSTA_RFDET)) {  
  88.   
  89.             disable_imask(host,  
  90.                       S3C2410_SDIIMSK_RXFIFOHALF |  
  91.                       S3C2410_SDIIMSK_RXFIFOLAST);  
  92.   
  93.             tasklet_schedule(&host->pio_tasklet);  
  94.             host->status = "pio rx";  
  95.         }  
  96.     }  
  97.   
  98.     if (mci_csta & S3C2410_SDICMDSTAT_CMDTIMEOUT) {  
  99.         dbg(host, dbg_err, "CMDSTAT: error CMDTIMEOUT\n");  
  100.         cmd->error = -ETIMEDOUT;  
  101.         host->status = "error: command timeout";  
  102.         goto fail_transfer;  
  103.     }  
  104.   
  105.     if (mci_csta & S3C2410_SDICMDSTAT_CMDSENT) {  
  106.         if (host->complete_what == COMPLETION_CMDSENT) {  
  107.             host->status = "ok: command sent";  
  108.             goto close_transfer;  
  109.         }  
  110.   
  111.         mci_cclear |= S3C2410_SDICMDSTAT_CMDSENT;  
  112.     }  
  113.   
  114.     if (mci_csta & S3C2410_SDICMDSTAT_CRCFAIL) {  
  115.         if (cmd->flags & MMC_RSP_CRC) {  
  116.             if (host->mrq->cmd->flags & MMC_RSP_136) {  
  117.                 dbg(host, dbg_irq,  
  118.                     "fixup: ignore CRC fail with long rsp\n");  
  119.             } else {  
  120.                 /* note, we used to fail the transfer 
  121.                  * here, but it seems that this is just 
  122.                  * the hardware getting it wrong. 
  123.                  * 
  124.                  * cmd->error = -EILSEQ; 
  125.                  * host->status = "error: bad command crc"; 
  126.                  * goto fail_transfer; 
  127.                 */  
  128.             }  
  129.         }  
  130.   
  131.         mci_cclear |= S3C2410_SDICMDSTAT_CRCFAIL;  
  132.     }  
  133.   
  134.     if (mci_csta & S3C2410_SDICMDSTAT_RSPFIN) {  
  135.         if (host->complete_what == COMPLETION_RSPFIN) {  
  136.             host->status = "ok: command response received";  
  137.             goto close_transfer;  
  138.         }  
  139.   
  140.         if (host->complete_what == COMPLETION_XFERFINISH_RSPFIN)  
  141.             host->complete_what = COMPLETION_XFERFINISH;  
  142.   
  143.         mci_cclear |= S3C2410_SDICMDSTAT_RSPFIN;  
  144.     }  
  145.   
  146.     /* errors handled after this point are only relevant 
  147.        when a data transfer is in progress */  
  148.   
  149.     if (!cmd->data)  
  150.         goto clear_status_bits;  
  151.   
  152.     /* Check for FIFO failure */  
  153.     if (host->is2440) {  
  154.         if (mci_fsta & S3C2440_SDIFSTA_FIFOFAIL) {  
  155.             dbg(host, dbg_err, "FIFO failure\n");  
  156.             host->mrq->data->error = -EILSEQ;  
  157.             host->status = "error: 2440 fifo failure";  
  158.             goto fail_transfer;  
  159.         }  
  160.     } else {  
  161.         if (mci_dsta & S3C2410_SDIDSTA_FIFOFAIL) {  
  162.             dbg(host, dbg_err, "FIFO failure\n");  
  163.             cmd->data->error = -EILSEQ;  
  164.             host->status = "error:  fifo failure";  
  165.             goto fail_transfer;  
  166.         }  
  167.     }  
  168.   
  169.     if (mci_dsta & S3C2410_SDIDSTA_RXCRCFAIL) {  
  170.         dbg(host, dbg_err, "bad data crc (outgoing)\n");  
  171.         cmd->data->error = -EILSEQ;  
  172.         host->status = "error: bad data crc (outgoing)";  
  173.         goto fail_transfer;  
  174.     }  
  175.   
  176.     if (mci_dsta & S3C2410_SDIDSTA_CRCFAIL) {  
  177.         dbg(host, dbg_err, "bad data crc (incoming)\n");  
  178.         cmd->data->error = -EILSEQ;  
  179.         host->status = "error: bad data crc (incoming)";  
  180.         goto fail_transfer;  
  181.     }  
  182.   
  183.     if (mci_dsta & S3C2410_SDIDSTA_DATATIMEOUT) {  
  184.         dbg(host, dbg_err, "data timeout\n");  
  185.         cmd->data->error = -ETIMEDOUT;  
  186.         host->status = "error: data timeout";  
  187.         goto fail_transfer;  
  188.     }  
  189.   
  190.     if (mci_dsta & S3C2410_SDIDSTA_XFERFINISH) {  
  191.         if (host->complete_what == COMPLETION_XFERFINISH) {  
  192.             host->status = "ok: data transfer completed";  
  193.             goto close_transfer;  
  194.         }  
  195.   
  196.         if (host->complete_what == COMPLETION_XFERFINISH_RSPFIN)  
  197.             host->complete_what = COMPLETION_RSPFIN;  
  198.   
  199.         mci_dclear |= S3C2410_SDIDSTA_XFERFINISH;  
  200.     }  
  201.   
  202. clear_status_bits:  
  203.     writel(mci_cclear, host->base + S3C2410_SDICMDSTAT);  
  204.     writel(mci_dclear, host->base + S3C2410_SDIDSTA);  
  205.   
  206.     goto irq_out;  
  207.   
  208. fail_transfer:  
  209.     host->pio_active = XFER_NONE;  
  210.   
  211. close_transfer:  
  212.     host->complete_what = COMPLETION_FINALIZE;  
  213.   
  214.     clear_imask(host);  
  215.     tasklet_schedule(&host->pio_tasklet);  
  216.   
  217.     goto irq_out;  
  218.   
  219. irq_out:  
  220.     dbg(host, dbg_irq,  
  221.         "csta:0x%08x dsta:0x%08x fsta:0x%08x dcnt:0x%08x status:%s.\n",  
  222.         mci_csta, mci_dsta, mci_fsta, mci_dcnt, host->status);  
  223.   
  224.     spin_unlock_irqrestore(&host->complete_lock, iflags);  
  225.     return IRQ_HANDLED;  
  226.   
  227. }  

36-48行是判断SDIO所触发的中断,与我们说说的无关。飘过....

30-34行分别读取命令状态、尚未完成传输的数据大小以及FIFO的状态的值。

56-61行就是之前所分析的host->complete_what,如果设备无欲无求host->complete_what == COMPLETION_NONE,即使连最基本的命令发送也不要求完成的话,那就没什么意义了,直接清除IMASK,返回。

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. static inline void clear_imask(struct s3cmci_host *host)  
  2. {  
  3.     u32 mask = readl(host->base + host->sdiimsk);  
  4.   
  5.     /* preserve the SDIO IRQ mask state */  
  6.     mask &= S3C2410_SDIIMSK_SDIOIRQ;  
  7.     writel(mask, host->base + host->sdiimsk);  
  8. }  

上面的代码只保留了SDIO IRQ状态,其他的中断都是被屏蔽了的。由此足见其对SDIO设备的偏心程度。

585-589 行尽然玩丢了host->mrq,无论是命令还是数据请求,我们都是递交了struct mmc_request结构的,所以驱动很气愤,直接返回。

591行前面我们看到struct mmc_request中包含了两种类型的struct mmc_cmd一个是所谓的cmd另外一个就是stop了,当然选择哪一个也不是他自己说来算了,当然有主机host-
>cmd_is_stop来决定了。

63-67行是PIO模式下数据传输的,我们姑且先放着,等说完CMD回头再看。

98-103行命令超时以后就会跳转到fail_transfer,至于fail_transfer又干了些啥好事我们走到那里了再说,继续前进...

105-109行命令发送成功以后所产生的中断,如果host->complete_what也正好只要求传输成功即COMPLETION_CMDSENT,那正好完成工作,goto close_transfer。

114-129行是CRC错误,忽略。

134-138行命令相应接收成功,那么依旧goto close_transfer。

140-143行至今尚未发现一个所谓的COMPLETION_XFERFINISH_RSPFIN最多也就数据传输成功那么修改一下这个脑残host->complete_what =COMPLETION_XFERFINISH;

149-150行如果没有数据传输,那么接下来就可以进行状态清理工作了。

153-200行是检查FIFO信息的,回头说到PIO传输的时候在来分析它。

203-204行意图很明确,显然是毁尸灭迹,清除状态。最后可以看到无论是先前的fail_transfer:还是后来的close_transfer,最总都会去调用

215行的tasklet_schedule(&host->pio_tasklet),是什么赋予这个函数如此强大的魅力,且听下回分解...


2、数据传输 s3cmci_setup_data

        是时候该看点实际的数据传输了,前面说过s3cmci_send_request中的if (cmd->data)是区分命令是否有数据阶段的关键标志。如果有数据传输的,那么就到了
s3cmci_setup_data

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. static int s3cmci_setup_data(struct s3cmci_host *host, struct mmc_data *data)  
  2. {  
  3.     u32 dcon, imsk, stoptries = 3;  
  4.   
  5.     /* write DCON register */  
  6.   
  7.     if (!data) {  
  8.         writel(0, host->base + S3C2410_SDIDCON);  
  9.         return 0;  
  10.     }  
  11.   
  12.     if ((data->blksz & 3) != 0) {  
  13.         /* We cannot deal with unaligned blocks with more than 
  14.          * one block being transferred. */  
  15.   
  16.         if (data->blocks > 1) {  
  17.             pr_warning("%s: can't do non-word sized block transfers (blksz %d)\n", __func__, data->blksz);  
  18.             return -EINVAL;  
  19.         }  
  20.     }  
  21.   
  22.     while (readl(host->base + S3C2410_SDIDSTA) &  
  23.            (S3C2410_SDIDSTA_TXDATAON | S3C2410_SDIDSTA_RXDATAON)) {  
  24.   
  25.         dbg(host, dbg_err,  
  26.             "mci_setup_data() transfer stillin progress.\n");  
  27.   
  28.         writel(S3C2410_SDIDCON_STOP, host->base + S3C2410_SDIDCON);  
  29.         s3cmci_reset(host);  
  30.   
  31.         if ((stoptries--) == 0) {  
  32.             dbg_dumpregs(host, "DRF");  
  33.             return -EINVAL;  
  34.         }  
  35.     }  
  36.   
  37.     dcon  = data->blocks & S3C2410_SDIDCON_BLKNUM_MASK;  
  38.   
  39.     if (s3cmci_host_usedma(host))  
  40.         dcon |= S3C2410_SDIDCON_DMAEN;  
  41.   
  42.     if (host->bus_width == MMC_BUS_WIDTH_4)  
  43.         dcon |= S3C2410_SDIDCON_WIDEBUS;  
  44.   
  45.     if (!(data->flags & MMC_DATA_STREAM))  
  46.         dcon |= S3C2410_SDIDCON_BLOCKMODE;  
  47.   
  48.     if (data->flags & MMC_DATA_WRITE) {  
  49.         dcon |= S3C2410_SDIDCON_TXAFTERRESP;  
  50.         dcon |= S3C2410_SDIDCON_XFER_TXSTART;  
  51.     }  
  52.   
  53.     if (data->flags & MMC_DATA_READ) {  
  54.         dcon |= S3C2410_SDIDCON_RXAFTERCMD;  
  55.         dcon |= S3C2410_SDIDCON_XFER_RXSTART;  
  56.     }  
  57.   
  58.     if (host->is2440) {  
  59.         dcon |= S3C2440_SDIDCON_DS_WORD;  
  60.         dcon |= S3C2440_SDIDCON_DATSTART;  
  61.     }  
  62.   
  63.     writel(dcon, host->base + S3C2410_SDIDCON);  
  64.   
  65.     /* write BSIZE register */  
  66.   
  67.     writel(data->blksz, host->base + S3C2410_SDIBSIZE);  
  68.   
  69.     /* add to IMASK register */  
  70.     imsk = S3C2410_SDIIMSK_FIFOFAIL | S3C2410_SDIIMSK_DATACRC |  
  71.            S3C2410_SDIIMSK_DATATIMEOUT | S3C2410_SDIIMSK_DATAFINISH;  
  72.   
  73.     enable_imask(host, imsk);  
  74.   
  75.     /* write TIMER register */  
  76.   
  77.     if (host->is2440) {  
  78.         writel(0x007FFFFF, host->base + S3C2410_SDITIMER);  
  79.     } else {  
  80.         writel(0x0000FFFF, host->base + S3C2410_SDITIMER);  
  81.   
  82.         /* FIX: set slow clock to prevent timeouts on read */  
  83.         if (data->flags & MMC_DATA_READ)  
  84.             writel(0xFF, host->base + S3C2410_SDIPRE);  
  85.     }  
  86.   
  87.     return 0;  
  88. }  

7-10行如果data不存在,接下来就无事可做了。

12-20行块大小是4字节对齐的,如果data->blksz不满足,那么返回错误。

22-35行读取数据状态寄存器,如果正在发送或接收数据,则s3cmci_reset(host);复位SD控制器。

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. static void s3cmci_reset(struct s3cmci_host *host)  
  2. {  
  3.     u32 con = readl(host->base + S3C2410_SDICON);  
  4.       
  5.     con |= S3C2440_SDICON_SDRESET;  
  6.     writel(con, host->base + S3C2410_SDICON);  
  7. }  

37-63行根据数据特征、主机总线宽度等信息设置数据控制寄存器。

67行设置SD控制器块大小寄存器。这是上层设置下来的值,一般为512。

70-73行设置中断屏蔽寄存器,使能数据传输完成中断、超时等。

77-84行是关于读写超时的处理。接着返回到s3cmci_send_request....

如果不出什么问题,应该就到了85行。

s3cmci_prepare_dma(host, cmd->data);是DMA传输的处理,

s3cmci_prepare_pio(host, cmd->data);是PIO方式的处理,下面我们先来关注PIO方式的数据传输。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/401908.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux SD卡驱动开发(五) —— SD 卡驱动分析Core补充篇

Core层中有两个重要函数 mmc_alloc_host 用于构造host,前面已经学习过,这里不再阐述;另一个就是 mmc_add_host,用于注册host 前面探测函数s3cmci_probe,现在就来回顾一下这个函数的作用。先简要的概括一下这个函数的功能&#xff…

navicat连接oracle 报 ORA-12737 set CHS16GBK

2019独角兽企业重金招聘Python工程师标准>>> 1首 先,我们打开“工具”-->"选项"菜单,见到如下界面,依据OCI library(oci.dll) 路径,导航到 navicat oci 目录下,备份里面的文件(通过…

Linux SD卡驱动开发(六) —— SD卡启动过程总体分析

一、工作流程 mmc驱动主要文件包括 drivers/mmc/card/block.c drivers/mmc/card/queue.c drivers/mmc/core/core.c drivers/mmc/core/host.c drivers/mmc/core/ 内核启动时,首先执行core/core.c的mmc_init,注册mmc、sd总线,以及一个host clas…

svn怎么上传文件 — 百度经验无耻推广

2019独角兽企业重金招聘Python工程师标准>>> svn怎么上传文件 — 欢乐地点进去捧场 PS:觉得笔者太无耻,直接在下方评论抨击 转载于:https://my.oschina.net/cenqingbo/blog/212284

apache 重写和虚拟目录配置

要求:假如我请求一个地址:www.lxy.com/news-sport-id123.html转成:www.lxy.com/show.php?catenews&classsport&id123步骤:①首先我们需要在apache中启用rewrite模块打开apache的httpd.conf文件,找到#LoadModu…

JavaScript代码片段

简介:本文收集了我常用的JavaScript代码片段,欢迎提意见! 大灰狼边敲门边说:“小兔子乖乖,把门儿开开!” 小兔子听到后,连忙去开门:“来喽!” 兔妈妈对小兔子喊道&#x…

路由器开发(一)—— 路由器硬件结构及软件体系

一、路由器的硬件构成 路由器主要由以下几个部分组成:输入/输出接口部分、包转发或交换结构部分(switching fabric)、路由计算或处理部分。如图所示 图1 路由器的基本组成 输入端口是物理链路和输入包的进口处。端口通常由线卡提供&#…

路由器开发(二)—— 路由器工作原理

当信息需要在两个网络之间传输时,常用路由器这种互连设备来负责数据的传输。路由器的主要工作是:路径的决定和数据包的转发(从路由器一个接口输入,然后选择合适接口输出);维护路由表。 路由器工作的方式非常…

Android颜色渐变的分隔线(ListView)

2019独角兽企业重金招聘Python工程师标准>>> shape.xml xx <?xml version"1.0" encoding"utf-8"?><shape xmlns:android"http://schemas.android.com/apk/res/android" > <gradient android:startColor&qu…

项目实践中Linux集群的总结和思考

2019独角兽企业重金招聘Python工程师标准>>> 前言&#xff1a;作为一名Linux/unix系统工程师、项目实施工程师&#xff0c;这几年一直在涉及到对外项目&#xff0c;经手过许多小中型网站的架构&#xff0c;F5、LVS及Nginx接触的都比较多&#xff0c;我想一种比较通俗…

路由器基础知识详解

第一章 网络互联 网络的根本目的非常简单&#xff1a;方便人们交换所获得的信息。但是网络的应用需求非常复杂&#xff1a;有的用户希望高带宽&#xff0c;但并不要求很长的传输距离&#xff1b;有的用户要求很长的距离&#xff0c;但对带宽要求很低&#xff1b;有的对网络的…

事务与锁机制

2019独角兽企业重金招聘Python工程师标准>>> 事务定义&#xff1a; 访问并可能更新数据库&#xff1a;一句或一组SQL&#xff0c;或者是一段程序&#xff0c;反正update了就是事务 ACID的4原则&#xff1a; 原子性&#xff1a; 一致性&#xff1a; 隔离性&#xff1…

路由器 VS OSI七层模型

OSI Open Source Initiative&#xff08;简称OSI&#xff0c;有译作开放源代码促进会、开放原始码组织&#xff09;是一个旨在推动开源软件发展的非盈利组织。OSI参考模型&#xff08;OSI/RM&#xff09;的全称是开放系统互连参考模型&#xff08;Open System Interconnection …

Linux Wireless架构总结

1、无线网络驱动(ath9k_htc) ath9k_htc是一个基于USB接口的SoftMAC无线网络适配器。为了其驱动能正常工作&#xff0c;首先必须调用usb_register来注册驱动定义的usb_driver&#xff0c;以借助USB Core的力量来处理与USB协议相关的事件。其代码如下&#xff1a;[cpp] view plai…

MySQL 日志文件 说明

MySQL 5.5 官方文档上有关日志的分类&#xff1a;By default, nologs are enabled. The following log-specific sections provide information about the server options that enable logging.--默认情况下&#xff0c;没有启动任何log&#xff0c;可以通过如下log 选项来启动…

Linux 下wifi 驱动开发(四)—— USB接口WiFi驱动浅析

前面学习了SDIO接口的WiFi驱动&#xff0c;现在我们来学习一下USB接口的WiFi驱动&#xff0c;二者的区别在于接口不同。而USB接口的设备驱动&#xff0c;我们前面也有学习&#xff0c;比如USB摄像头驱动、USB鼠标驱动&#xff0c;同样都符合LinuxUSB驱动结构&#xff1a; USB设…

Linux 下wifi 驱动开发(三)—— SDIO接口WiFi驱动浅析

SDIO-Wifi模块是基于SDIO接口的符合wifi无线网络标准的嵌入式模块&#xff0c;内置无线网络协议IEEE802.11协议栈以及TCP/IP协议栈&#xff0c;能够实现用户主平台数据通过SDIO口到无线网络之间的转换。SDIO具有传输数据快&#xff0c;兼容SD、MMC接口等特点。 对于SDIO接口的w…

Erlang并发机制 –进程调度

2019独角兽企业重金招聘Python工程师标准>>> Erlang调度器主要完成对Erlang进程的调度&#xff0c;它是Erlang实现软件实时和进程之间公平使用CPU的关键。Erlang运行时&#xff0c;有4种任务需要被调度&#xff1a;进程&#xff0c;Port&#xff0c;Linked-in drive…

Linux 下wifi 驱动开发(二)—— WiFi模块浅析

一、什么是wifi 模块 百度百科上这样定义&#xff1a; Wi-Fi模块又名串口Wi-Fi模块&#xff0c;属于物联网传输层&#xff0c;功能是将串口或TTL电平转为符合Wi-Fi无线网络通信标准的嵌入式模块&#xff0c;内置无线网络协议IEEE802.11b.g.n协议栈以及TCP/IP协议栈。传统的硬件…

Linux 下wifi 驱动开发(一)—— WiFi基础知识解析

一、WiFi相关基础概念 1、什么是wifi 我们看一下百度百科是如何定义的&#xff1a; Wi-Fi是一种可以将个人电脑、手持设备&#xff08;如pad、手机&#xff09;等终端以无线方式互相连接的技术&#xff0c;事实上它是一个高频无线电信号。[1] 无线保真是一个无线网络通信技术…