IT技术互动交流平台

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

来源:IT165收集  发布日期:2016-04-05 22:43:37

前面对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,它主要存放两大数据结构的指针,分别是cmd和data,顾名思意,一个为指令,一个为数据

也就是说,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方式的数据传输。

 

Tag标签: 卡驱动   控制器   硬件  
  • 专题推荐

About IT165 - 广告服务 - 隐私声明 - 版权申明 - 免责条款 - 网站地图 - 网友投稿 - 联系方式
本站内容来自于互联网,仅供用于网络技术学习,学习中请遵循相关法律法规