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


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

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

    static struct mmc_host_ops s3cmci_ops = {  
        .request    = s3cmci_request,  
        .set_ios    = s3cmci_set_ios,  
        .get_ro     = s3cmci_get_ro,  
        .get_cd     = s3cmci_card_present,  
        .enable_sdio_irq = s3cmci_enable_sdio_irq,  
    };  

           在讲述每一个方法详细的实现之前。先来对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写保护引脚的状态就能判定卡写保护的情况。

    实现的代码例如以下:

    static int s3cmci_get_ro(struct mmc_host *mmc)
    {
    	struct s3cmci_host *host = mmc_priv(mmc);
    	struct s3c24xx_mci_pdata *pdata = host->pdata;
    	int ret;
    
    	if (pdata->no_wprotect)
    		return 0;
    
    	ret = gpio_get_value(pdata->gpio_wprotect) ?

    1 : 0; ret ^= pdata->wprotect_invert; return ret; }

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


    二、s3cmci_set_ios

          依据我们前面所见到的种种设置,这里的ioset可能会相对烦锁一些,详细的代码例如以下:

    static void s3cmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
    {
    	struct s3cmci_host *host = mmc_priv(mmc);
    	u32 mci_con;
    
    	/* Set the power state */
    
    	mci_con = readl(host->base + S3C2410_SDICON);
    
    	switch (ios->power_mode) {
    	case MMC_POWER_ON:
    	case MMC_POWER_UP:
    		/* Configure GPE5...GPE10 pins in SD mode */
    		s3c_gpio_cfgall_range(S3C2410_GPE(5), 6, S3C_GPIO_SFN(2),
    				      S3C_GPIO_PULL_NONE);
    
    		if (host->pdata->set_power)
    			host->pdata->set_power(ios->power_mode, ios->vdd);
    
    		if (!host->is2440)
    			mci_con |= S3C2410_SDICON_FIFORESET;
    
    		break;
    
    	case MMC_POWER_OFF:
    	default:
    		gpio_direction_output(S3C2410_GPE(5), 0);
    
    		if (host->is2440)
    			mci_con |= S3C2440_SDICON_SDRESET;
    
    		if (host->pdata->set_power)
    			host->pdata->set_power(ios->power_mode, ios->vdd);
    
    		break;
    	}
    
    	s3cmci_set_clk(host, ios);
    
    	/* Set CLOCK_ENABLE */
    	if (ios->clock)
    		mci_con |= S3C2410_SDICON_CLOCKTYPE;
    	else
    		mci_con &= ~S3C2410_SDICON_CLOCKTYPE;
    
    	writel(mci_con, host->base + S3C2410_SDICON);
    
    	if ((ios->power_mode == MMC_POWER_ON) ||
    	    (ios->power_mode == MMC_POWER_UP)) {
    		dbg(host, dbg_conf, "running at %lukHz (requested: %ukHz).
    ",
    			host->real_rate/1000, ios->clock/1000);
    	} else {
    		dbg(host, dbg_conf, "powered down.
    ");
    	}
    
    	host->bus_width = ios->bus_width;
    }
    

    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卡间通信所须要的指令和数据

    static void s3cmci_request(struct mmc_host *mmc, struct mmc_request *mrq)
    {
    	struct s3cmci_host *host = mmc_priv(mmc);
    
    	host->status = "mmc request";
    	host->cmd_is_stop = 0;
    	host->mrq = mrq;
    
    	if (s3cmci_card_present(mmc) == 0) {
    		dbg(host, dbg_err, "%s: no medium present
    ", __func__);
    		host->mrq->cmd->error = -ENOMEDIUM;
    		mmc_request_done(mmc, mrq);
    	} else
    		s3cmci_send_request(mmc);
    }

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

    mmc_request_done

    [core/core.c]

    /**
     *	mmc_request_done - finish processing an MMC request
     *	@host: MMC host which completed request
     *	@mrq: MMC request which request
     *
     *	MMC drivers should call this function when they have completed
     *	their processing of a request.
     */
    void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
    {
    	struct mmc_command *cmd = mrq->cmd;
    	int err = cmd->error;
    
    	if (err && cmd->retries && mmc_host_is_spi(host)) {
    		if (cmd->resp[0] & R1_SPI_ILLEGAL_COMMAND)
    			cmd->retries = 0;
    	}
    
    	if (err && cmd->retries && !mmc_card_removed(host->card)) {
    		/*
    		 * Request starter must handle retries - see
    		 * mmc_wait_for_req_done().
    		 */
    		if (mrq->done)
    			mrq->done(mrq);
    	} else {
    		mmc_should_fail_request(host, mrq);
    
    		led_trigger_event(host->led, LED_OFF);
    
    		pr_debug("%s: req done (CMD%u): %d: %08x %08x %08x %08x
    ",
    			mmc_hostname(host), cmd->opcode, err,
    			cmd->resp[0], cmd->resp[1],
    			cmd->resp[2], cmd->resp[3]);
    
    		if (mrq->data) {
    			pr_debug("%s:     %d bytes transferred: %d
    ",
    				mmc_hostname(host),
    				mrq->data->bytes_xfered, mrq->data->error);
    		}
    
    		if (mrq->stop) {
    			pr_debug("%s:     (CMD%u): %d: %08x %08x %08x %08x
    ",
    				mmc_hostname(host), mrq->stop->opcode,
    				mrq->stop->error,
    				mrq->stop->resp[0], mrq->stop->resp[1],
    				mrq->stop->resp[2], mrq->stop->resp[3]);
    		}
    
    		if (mrq->done)
    			mrq->done(mrq);
    
    		mmc_host_clk_release(host);
    	}
    }
    

    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]

    static void mmc_wait_done(struct mmc_request *mrq)
    {
    	complete(mrq->done_data);
    }

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

        回到s3cmci_request,假设卡还存在的话s3cmci_send_request将真正開始这个请求的处理。

    s3cmci_send_request

    [host/s3cmci.c]

    static void s3cmci_send_request(struct mmc_host *mmc)
    {
    	struct s3cmci_host *host = mmc_priv(mmc);
    	struct mmc_request *mrq = host->mrq;
    	struct mmc_command *cmd = host->cmd_is_stop ?

    mrq->stop : mrq->cmd; host->ccnt++; prepare_dbgmsg(host, cmd, host->cmd_is_stop); /* Clear command, data and fifo status registers Fifo clear only necessary on 2440, but doesn't hurt on 2410 */ writel(0xFFFFFFFF, host->base + S3C2410_SDICMDSTAT); writel(0xFFFFFFFF, host->base + S3C2410_SDIDSTA); writel(0xFFFFFFFF, host->base + S3C2410_SDIFSTA); if (cmd->data) { int res = s3cmci_setup_data(host, cmd->data); host->dcnt++; if (res) { dbg(host, dbg_err, "setup data error %d ", res); cmd->error = res; cmd->data->error = res; mmc_request_done(mmc, mrq); return; } if (s3cmci_host_usedma(host)) res = s3cmci_prepare_dma(host, cmd->data); else res = s3cmci_prepare_pio(host, cmd->data); if (res) { dbg(host, dbg_err, "data prepare error %d ", res); cmd->error = res; cmd->data->error = res; mmc_request_done(mmc, mrq); return; } } /* Send command */ s3cmci_send_command(host, cmd); /* Enable Interrupt */ s3cmci_enable_irq(host, true); }

    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]

    static void s3cmci_send_command(struct s3cmci_host *host,
    					struct mmc_command *cmd)
    {
    	u32 ccon, imsk;
    
    	imsk  = S3C2410_SDIIMSK_CRCSTATUS | S3C2410_SDIIMSK_CMDTIMEOUT |
    		S3C2410_SDIIMSK_RESPONSEND | S3C2410_SDIIMSK_CMDSENT |
    		S3C2410_SDIIMSK_RESPONSECRC;
    
    	enable_imask(host, imsk);
    
    	if (cmd->data)
    		host->complete_what = COMPLETION_XFERFINISH_RSPFIN;
    	else if (cmd->flags & MMC_RSP_PRESENT)
    		host->complete_what = COMPLETION_RSPFIN;
    	else
    		host->complete_what = COMPLETION_CMDSENT;
    
    	writel(cmd->arg, host->base + S3C2410_SDICMDARG);
    
    	ccon  = cmd->opcode & S3C2410_SDICMDCON_INDEX;
    	ccon |= S3C2410_SDICMDCON_SENDERHOST | S3C2410_SDICMDCON_CMDSTART;
    
    	if (cmd->flags & MMC_RSP_PRESENT)
    		ccon |= S3C2410_SDICMDCON_WAITRSP;
    
    	if (cmd->flags & MMC_RSP_136)
    		ccon |= S3C2410_SDICMDCON_LONGRSP;
    
    	writel(ccon, host->base + S3C2410_SDICMDCON);
    }
    

    6-8行是使能对应的中断,当中包含CRC校验错误、命令超时、收到命令响应等等。

    详细的中断屏蔽寄存器的内容能够參考S3C2440用户手冊。

    12-17行实际上指当前的这个命令结束时候应该所处的状态。中断处理函数将实际硬件的完毕情况与这个状态相比較。终于得到这个命令运行的结果。而cmd->flags正是前面提交命令之前依据不同命令的实际情况来设置的,比方具有应答数据的命令可能须要设置cmd->flags |= MMC_RSP_PRESENT。然后相应的结束状态也就应该是COMPLETION_RSPFIN收到应答。

    前面说过host->complete_what是个枚举类型的变量包括了整个命令过程的各个阶段,内容例如以下:

    enum s3cmci_waitfor {  
        COMPLETION_NONE,  
        COMPLETION_FINALIZE,  
        COMPLETION_CMDSENT,  
        COMPLETION_RSPFIN,  
        COMPLETION_XFERFINISH,  
        COMPLETION_XFERFINISH_RSPFIN,  
    };  

    一般的命令可能无应答阶段,我们默认传输数据正确完毕以后即觉得命令运行完毕也就是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]

    /*
     * ISR for SDI Interface IRQ
     * Communication between driver and ISR works as follows:
     *   host->mrq 			points to current request
     *   host->complete_what	Indicates when the request is considered done
     *     COMPLETION_CMDSENT	  when the command was sent
     *     COMPLETION_RSPFIN          when a response was received
     *     COMPLETION_XFERFINISH	  when the data transfer is finished
     *     COMPLETION_XFERFINISH_RSPFIN both of the above.
     *   host->complete_request	is the completion-object the driver waits for
     *
     * 1) Driver sets up host->mrq and host->complete_what
     * 2) Driver prepares the transfer
     * 3) Driver enables interrupts
     * 4) Driver starts transfer
     * 5) Driver waits for host->complete_rquest
     * 6) ISR checks for request status (errors and success)
     * 6) ISR sets host->mrq->cmd->error and host->mrq->data->error
     * 7) ISR completes host->complete_request
     * 8) ISR disables interrupts
     * 9) Driver wakes up and takes care of the request
     *
     * Note: "->error"-fields are expected to be set to 0 before the request
     *       was issued by mmc.c - therefore they are only set, when an error
     *       contition comes up
     */
    
    static irqreturn_t s3cmci_irq(int irq, void *dev_id)
    {
    	struct s3cmci_host *host = dev_id;
    	struct mmc_command *cmd;
    	u32 mci_csta, mci_dsta, mci_fsta, mci_dcnt, mci_imsk;
    	u32 mci_cclear = 0, mci_dclear;
    	unsigned long iflags;
    
    	mci_dsta = readl(host->base + S3C2410_SDIDSTA);
    	mci_imsk = readl(host->base + host->sdiimsk);
    
    	if (mci_dsta & S3C2410_SDIDSTA_SDIOIRQDETECT) {
    		if (mci_imsk & S3C2410_SDIIMSK_SDIOIRQ) {
    			mci_dclear = S3C2410_SDIDSTA_SDIOIRQDETECT;
    			writel(mci_dclear, host->base + S3C2410_SDIDSTA);
    
    			mmc_signal_sdio_irq(host->mmc);
    			return IRQ_HANDLED;
    		}
    	}
    
    	spin_lock_irqsave(&host->complete_lock, iflags);
    
    	mci_csta = readl(host->base + S3C2410_SDICMDSTAT);
    	mci_dcnt = readl(host->base + S3C2410_SDIDCNT);
    	mci_fsta = readl(host->base + S3C2410_SDIFSTA);
    	mci_dclear = 0;
    
    	if ((host->complete_what == COMPLETION_NONE) ||
    	    (host->complete_what == COMPLETION_FINALIZE)) {
    		host->status = "nothing to complete";
    		clear_imask(host);
    		goto irq_out;
    	}
    
    	if (!host->mrq) {
    		host->status = "no active mrq";
    		clear_imask(host);
    		goto irq_out;
    	}
    
    	cmd = host->cmd_is_stop ?

    host->mrq->stop : host->mrq->cmd; if (!cmd) { host->status = "no active cmd"; clear_imask(host); goto irq_out; } if (!s3cmci_host_usedma(host)) { if ((host->pio_active == XFER_WRITE) && (mci_fsta & S3C2410_SDIFSTA_TFDET)) { disable_imask(host, S3C2410_SDIIMSK_TXFIFOHALF); tasklet_schedule(&host->pio_tasklet); host->status = "pio tx"; } if ((host->pio_active == XFER_READ) && (mci_fsta & S3C2410_SDIFSTA_RFDET)) { disable_imask(host, S3C2410_SDIIMSK_RXFIFOHALF | S3C2410_SDIIMSK_RXFIFOLAST); tasklet_schedule(&host->pio_tasklet); host->status = "pio rx"; } } if (mci_csta & S3C2410_SDICMDSTAT_CMDTIMEOUT) { dbg(host, dbg_err, "CMDSTAT: error CMDTIMEOUT "); cmd->error = -ETIMEDOUT; host->status = "error: command timeout"; goto fail_transfer; } if (mci_csta & S3C2410_SDICMDSTAT_CMDSENT) { if (host->complete_what == COMPLETION_CMDSENT) { host->status = "ok: command sent"; goto close_transfer; } mci_cclear |= S3C2410_SDICMDSTAT_CMDSENT; } if (mci_csta & S3C2410_SDICMDSTAT_CRCFAIL) { if (cmd->flags & MMC_RSP_CRC) { if (host->mrq->cmd->flags & MMC_RSP_136) { dbg(host, dbg_irq, "fixup: ignore CRC fail with long rsp "); } else { /* note, we used to fail the transfer * here, but it seems that this is just * the hardware getting it wrong. * * cmd->error = -EILSEQ; * host->status = "error: bad command crc"; * goto fail_transfer; */ } } mci_cclear |= S3C2410_SDICMDSTAT_CRCFAIL; } if (mci_csta & S3C2410_SDICMDSTAT_RSPFIN) { if (host->complete_what == COMPLETION_RSPFIN) { host->status = "ok: command response received"; goto close_transfer; } if (host->complete_what == COMPLETION_XFERFINISH_RSPFIN) host->complete_what = COMPLETION_XFERFINISH; mci_cclear |= S3C2410_SDICMDSTAT_RSPFIN; } /* errors handled after this point are only relevant when a data transfer is in progress */ if (!cmd->data) goto clear_status_bits; /* Check for FIFO failure */ if (host->is2440) { if (mci_fsta & S3C2440_SDIFSTA_FIFOFAIL) { dbg(host, dbg_err, "FIFO failure "); host->mrq->data->error = -EILSEQ; host->status = "error: 2440 fifo failure"; goto fail_transfer; } } else { if (mci_dsta & S3C2410_SDIDSTA_FIFOFAIL) { dbg(host, dbg_err, "FIFO failure "); cmd->data->error = -EILSEQ; host->status = "error: fifo failure"; goto fail_transfer; } } if (mci_dsta & S3C2410_SDIDSTA_RXCRCFAIL) { dbg(host, dbg_err, "bad data crc (outgoing) "); cmd->data->error = -EILSEQ; host->status = "error: bad data crc (outgoing)"; goto fail_transfer; } if (mci_dsta & S3C2410_SDIDSTA_CRCFAIL) { dbg(host, dbg_err, "bad data crc (incoming) "); cmd->data->error = -EILSEQ; host->status = "error: bad data crc (incoming)"; goto fail_transfer; } if (mci_dsta & S3C2410_SDIDSTA_DATATIMEOUT) { dbg(host, dbg_err, "data timeout "); cmd->data->error = -ETIMEDOUT; host->status = "error: data timeout"; goto fail_transfer; } if (mci_dsta & S3C2410_SDIDSTA_XFERFINISH) { if (host->complete_what == COMPLETION_XFERFINISH) { host->status = "ok: data transfer completed"; goto close_transfer; } if (host->complete_what == COMPLETION_XFERFINISH_RSPFIN) host->complete_what = COMPLETION_RSPFIN; mci_dclear |= S3C2410_SDIDSTA_XFERFINISH; } clear_status_bits: writel(mci_cclear, host->base + S3C2410_SDICMDSTAT); writel(mci_dclear, host->base + S3C2410_SDIDSTA); goto irq_out; fail_transfer: host->pio_active = XFER_NONE; close_transfer: host->complete_what = COMPLETION_FINALIZE; clear_imask(host); tasklet_schedule(&host->pio_tasklet); goto irq_out; irq_out: dbg(host, dbg_irq, "csta:0x%08x dsta:0x%08x fsta:0x%08x dcnt:0x%08x status:%s. ", mci_csta, mci_dsta, mci_fsta, mci_dcnt, host->status); spin_unlock_irqrestore(&host->complete_lock, iflags); return IRQ_HANDLED; }

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

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

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


    static inline void clear_imask(struct s3cmci_host *host)
    {
    	u32 mask = readl(host->base + host->sdiimsk);
    
    	/* preserve the SDIO IRQ mask state */
    	mask &= S3C2410_SDIIMSK_SDIOIRQ;
    	writel(mask, host->base + host->sdiimsk);
    }
    

    上面的代码仅仅保留了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

    static int s3cmci_setup_data(struct s3cmci_host *host, struct mmc_data *data)
    {
    	u32 dcon, imsk, stoptries = 3;
    
    	/* write DCON register */
    
    	if (!data) {
    		writel(0, host->base + S3C2410_SDIDCON);
    		return 0;
    	}
    
    	if ((data->blksz & 3) != 0) {
    		/* We cannot deal with unaligned blocks with more than
    		 * one block being transferred. */
    
    		if (data->blocks > 1) {
    			pr_warning("%s: can't do non-word sized block transfers (blksz %d)
    ", __func__, data->blksz);
    			return -EINVAL;
    		}
    	}
    
    	while (readl(host->base + S3C2410_SDIDSTA) &
    	       (S3C2410_SDIDSTA_TXDATAON | S3C2410_SDIDSTA_RXDATAON)) {
    
    		dbg(host, dbg_err,
    		    "mci_setup_data() transfer stillin progress.
    ");
    
    		writel(S3C2410_SDIDCON_STOP, host->base + S3C2410_SDIDCON);
    		s3cmci_reset(host);
    
    		if ((stoptries--) == 0) {
    			dbg_dumpregs(host, "DRF");
    			return -EINVAL;
    		}
    	}
    
    	dcon  = data->blocks & S3C2410_SDIDCON_BLKNUM_MASK;
    
    	if (s3cmci_host_usedma(host))
    		dcon |= S3C2410_SDIDCON_DMAEN;
    
    	if (host->bus_width == MMC_BUS_WIDTH_4)
    		dcon |= S3C2410_SDIDCON_WIDEBUS;
    
    	if (!(data->flags & MMC_DATA_STREAM))
    		dcon |= S3C2410_SDIDCON_BLOCKMODE;
    
    	if (data->flags & MMC_DATA_WRITE) {
    		dcon |= S3C2410_SDIDCON_TXAFTERRESP;
    		dcon |= S3C2410_SDIDCON_XFER_TXSTART;
    	}
    
    	if (data->flags & MMC_DATA_READ) {
    		dcon |= S3C2410_SDIDCON_RXAFTERCMD;
    		dcon |= S3C2410_SDIDCON_XFER_RXSTART;
    	}
    
    	if (host->is2440) {
    		dcon |= S3C2440_SDIDCON_DS_WORD;
    		dcon |= S3C2440_SDIDCON_DATSTART;
    	}
    
    	writel(dcon, host->base + S3C2410_SDIDCON);
    
    	/* write BSIZE register */
    
    	writel(data->blksz, host->base + S3C2410_SDIBSIZE);
    
    	/* add to IMASK register */
    	imsk = S3C2410_SDIIMSK_FIFOFAIL | S3C2410_SDIIMSK_DATACRC |
    	       S3C2410_SDIIMSK_DATATIMEOUT | S3C2410_SDIIMSK_DATAFINISH;
    
    	enable_imask(host, imsk);
    
    	/* write TIMER register */
    
    	if (host->is2440) {
    		writel(0x007FFFFF, host->base + S3C2410_SDITIMER);
    	} else {
    		writel(0x0000FFFF, host->base + S3C2410_SDITIMER);
    
    		/* FIX: set slow clock to prevent timeouts on read */
    		if (data->flags & MMC_DATA_READ)
    			writel(0xFF, host->base + S3C2410_SDIPRE);
    	}
    
    	return 0;
    }
    

    7-10行假设data不存在。接下来就无事可做了。

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

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

    static void s3cmci_reset(struct s3cmci_host *host)
    {
    	u32 con = readl(host->base + S3C2410_SDICON);
    	
    	con |= S3C2440_SDICON_SDRESET;
    	writel(con, host->base + S3C2410_SDICON);
    }

    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方式的传输数据。


    
  • 相关阅读:
    【转】Maven实战(九)---模块聚合和继承
    【转】Maven实战(八)---模块划分
    Oracle ORA-01034,ORA-27101,ORA-00600
    【spring源代码分析】--Bean的解析与注冊
    ArcGIS For Flex报错
    江西省委常委赵智勇被免 无被查字眼 媒体推測窝案
    软考——(4)数据库
    matlab 2014a 改为英文版本号
    POJ 2299 Ultra-QuickSort (求序列的逆序对数)
    工厂方法模式(factory method pattern)
  • 原文地址:https://www.cnblogs.com/tlnshuju/p/7360326.html
Copyright © 2020-2023  润新知