图片
/
图片
/
//扫描总线上设备mmc设备
void mmc_rescan(struct work_struct *work)
{
struct mmc_host *host =
container_of(work, struct mmc_host, detect.work);
u32 ocr;
int err;
mmc_bus_get(host);
//如果为第一次扫描总线上设备
if (host->bus_ops == NULL) {
/*
* Only we can add a new handler, so it’s safe to
* release the lock here.
*/
mmc_bus_put(host);
if (host->ops->get_cd && host->ops->get_cd(host) == 0)
goto out;
/* 驱动中使用mmc_claim_host(host);来得知,当前mmc控制器是否被占用,当前mmc控制器如果被占用,那么 host->claimed = 1;否则为0,如果为1,那么会在for(;;)循环中调用schedule切换出自己,当占用mmc控制器的操作完成之后,执行 mmc_release_host()的时候,会激活登记到等待队列&host->wq中的其他程序获得mmc主控制器的物理使用权*/
//这个函数和mmc_release_host(host);配对使用,相当于一把锁,就是在同一个时间只有一个sd卡可以保持和主控制器通讯;
mmc_claim_host(host);
最开始,卡是power_off
//开启host电源
mmc_power_up(host);
设置host为idle模式
mmc_go_idle(host);
///是用于验证SD卡接口操作状态的有效性命令(CMD8)。
//如果 SD_SEND_IF_COND指示为符合SD2.0标准的卡,
//则设置操作状态寄存器ocrbit30指示能 够处理块地址SDHC卡
//在SD子系统中,所有的数据传输均是由host发起。
//Host发送完一命令则会等待中断的产生,在这里采用完成量来实现进程的阻塞。
mmc_send_if_cond(host, host->ocr_avail);
/*
* First we search for SDIO...
*/
//判断是否是SDIO协议卡
//通过发送命令CMD5
//CMD5协议与SD接口中的ACMD41类似,用于检查是否支持SDIO的电压。
//CMD5的回应中,MP为0,表示是SDIO卡;如果MP为1,表示不但是SDIO卡,并且是SD卡。
err = mmc_send_io_op_cond(host, 0, &ocr);
if (!err) {
if (mmc_attach_sdio(host, ocr))
mmc_power_off(host);
goto out;
}
/*
* ...then normal SD...
*/
//判断是否是SD协议卡
//ocr 是指 card 內部的 Operation Condition Register (OCR) 讀出來的值
//發送 CMD41 CMD55 讀取 OCR 的值
//#define SD_APP_OP_COND 41 /* bcr [31:0] OCR R3 */
err = mmc_send_app_op_cond(host, 0, &ocr);//本函数后面有详解
if (!err) {
//装载 绑定 SD卡设备
if (mmc_attach_sd(host, ocr))
mmc_power_off(host);
goto out;
}
/*
* ...and finally MMC.
*/
//判断是否是MMC协议卡
err = mmc_send_op_cond(host, 0, &ocr);
if (!err) {
if (mmc_attach_mmc(host, ocr))
mmc_power_off(host);
goto out;
}
释放卡对主机的持有权
mmc_release_host(host);
mmc_power_off(host);
} else {
if (host->bus_ops->detect && !host->bus_dead)
host->bus_ops->detect(host);
mmc_bus_put(host);
}
out:
if (host->caps & MMC_CAP_NEEDS_POLL)
mmc_schedule_delayed_work(&host->detect, HZ);
}
/
/**
- mmc_wait_for_cmd - start a command and wait for completion
- @host: MMC host to start command
- @cmd: MMC command to start
- @retries: maximum number of retries
- Start a new MMC command for a host, and wait for the command
- to complete. Return any error that occurred while the command
- was executing. Do not attempt to parse the response.
*/
//这个是SDIO卡的
int mmc_wait_for_cmd(struct mmc_host *host, struct mmc_command *cmd, int retries)
{
struct mmc_request mrq;
cmd->retries = retries;
mrq.cmd = cmd;
cmd->data = NULL;
mmc_wait_for_req(host, &mrq);
return cmd->error;
}
/**
- mmc_wait_for_app_cmd - start an application command and wait for
completion - @host: MMC host to start command
- @card: Card to send MMC_APP_CMD to
- @cmd: MMC command to start
- @retries: maximum number of retries
- Sends a MMC_APP_CMD, checks the card response, sends the command
- in the parameter and waits for it to complete. Return any error
- that occurred while the command was executing. Do not attempt to
- parse the response.
*/
//这个是SD协议卡的
int mmc_wait_for_app_cmd(struct mmc_host *host, struct mmc_card *card,
struct mmc_command *cmd, int retries)
{
struct mmc_request mrq;
int i, err;
BUG_ON(!cmd);
BUG_ON(retries < 0);
err = -EIO;
/*
* We have to resend MMC_APP_CMD for each attempt so
* we cannot use the retries field in mmc_command.
*/
for (i = 0;i <= retries;i++) {
memset(&mrq, 0, sizeof(struct mmc_request));
err = mmc_app_cmd(host, card);
if (err) {
/* no point in retrying; no APP commands allowed */
if (mmc_host_is_spi(host)) {
if (cmd->resp[0] & R1_SPI_ILLEGAL_COMMAND)
break;
}
continue;
}
memset(&mrq, 0, sizeof(struct mmc_request));
memset(cmd->resp, 0, sizeof(cmd->resp));
cmd->retries = 0;
mrq.cmd = cmd;
cmd->data = NULL;
mmc_wait_for_req(host, &mrq);
err = cmd->error;
if (!cmd->error)
break;
/* no point in retrying illegal APP commands */
if (mmc_host_is_spi(host)) {
if (cmd->resp[0] & R1_SPI_ILLEGAL_COMMAND)
break;
}
}
return err;
}
/
//判断是否是SD协议卡
int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
{
struct mmc_command cmd;
int i, err = 0;
BUG_ON(!host);
memset(&cmd, 0, sizeof(struct mmc_command));
//判断是否是SD协议卡
//ocr 是指 card 內部的 Operation Condition Register (OCR) 讀出來的值
//發送 CMD41 CMD55 讀取 OCR 的值
//#define SD_APP_OP_COND 41 /* bcr [31:0] OCR R3 */
//见SD卡手册
图片
图片
图片
图片
cmd.opcode = SD_APP_OP_COND;
if (mmc_host_is_spi(host))
cmd.arg = ocr & (1 << 30); /* SPI only defines one bit */
else
cmd.arg = ocr;
cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R3 | MMC_CMD_BCR;
for (i = 100; i; i--) {
//MMC_CMD_RETRIES 重复的次数?
err = mmc_wait_for_app_cmd(host, NULL, &cmd, MMC_CMD_RETRIES);
if (err)
break;
/* if we're just probing, do a single pass */
if (ocr == 0)
break;
/* otherwise wait until reset completes */
if (mmc_host_is_spi(host)) {
if (!(cmd.resp[0] & R1_SPI_IDLE))
break;
} else {
if (cmd.resp[0] & MMC_CARD_BUSY)
break;
}
err = -ETIMEDOUT;
mmc_delay(10);
}
if (rocr && !mmc_host_is_spi(host))
*rocr = cmd.resp[0];
return err;
}
/
/**
- mmc_wait_for_req - start a request and wait for completion
- @host: MMC host to start command
- @mrq: MMC request to start
- Start a new MMC custom command request for a host, and wait
- for the command to complete. Does not attempt to parse the
- response.
*/
void mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq)
{
DECLARE_COMPLETION_ONSTACK(complete);
mrq->done_data = &complete;
mrq->done = mmc_wait_done;
//开始request
//mmc_start_request->host->ops->request(即sdhci_request)
mmc_start_request(host, mrq);
//这里采用完成量来实现进程的阻塞。
wait_for_completion(&complete);
}
/
mmc_send_if_cond实现,从字面意思上看该函数就是发送一个SD标准命令,亦如usb的标准命令一样。
在这里不得不先说明一点就是在SD子系统中,所有的数据传输均是由host发起。Host发送完一命令则会等待中断的产生,在这里采用完成量来实现进程的阻塞。
int mmc_send_if_cond(struct mmc_host *host, u32 ocr)
{
struct mmc_command cmd;
int err;
static const u8 test_pattern = 0xAA;
u8 result_pattern;
/*
* To support SD 2.0 cards, we must always invoke SD_SEND_IF_COND
* before SD_APP_OP_COND. This command will harmlessly fail for
* SD 1.0 cards.
*/
cmd.opcode = SD_SEND_IF_COND;
cmd.arg = ((ocr & 0xFF8000) != 0) << 8 | test_pattern;
cmd.flags = MMC_RSP_SPI_R7 | MMC_RSP_R7 | MMC_CMD_BCR;
//发送指令,并等待complete 完成量完成
err = mmc_wait_for_cmd(host, &cmd, 0);
if (err)
return err;
if (mmc_host_is_spi(host))
result_pattern = cmd.resp[1] & 0xFF;
else
result_pattern = cmd.resp[0] & 0xFF;
if (result_pattern != test_pattern)
return -EIO;
return 0;
}
/
/
/*
-
Starting point for SD card init.
*/
int mmc_attach_sd(struct mmc_host *host, u32 ocr)
{
int err;BUG_ON(!host);
WARN_ON(!host->claimed);mmc_attach_bus(host, &mmc_sd_ops);
/*
-
We need to get OCR a different way for SPI.
*/
if (mmc_host_is_spi(host)) {
mmc_go_idle(host);err = mmc_spi_read_ocr(host, 0, &ocr);
if (err)
goto err;
}
/*
- Sanity check the voltages that the card claims to
- support.
*/
if (ocr & 0x7F) {
printk(KERN_WARNING "%s: card claims to support voltages "
“below the defined range. These will be ignored.\n”,
mmc_hostname(host));
ocr &= ~0x7F;
}
if (ocr & MMC_VDD_165_195) {
printk(KERN_WARNING "%s: SD card claims to support the "
"incompletely defined ‘low voltage range’. This "
“will be ignored.\n”, mmc_hostname(host));
ocr &= ~MMC_VDD_165_195;
}host->ocr = mmc_select_voltage(host, ocr);
/*
- Can we support the voltage(s) of the card(s)?
*/
if (!host->ocr) {
err = -EINVAL;
goto err;
}
/*
- Detect and init the card.
*/
err = mmc_sd_init_card(host, host->ocr, NULL);
if (err)
goto err;
mmc_release_host(host);
err = mmc_add_card(host->card);
if (err)
goto remove_card;return 0;
-
remove_card:
mmc_remove_card(host->card);
host->card = NULL;
mmc_claim_host(host);
err:
mmc_detach_bus(host);
mmc_release_host(host);
printk(KERN_ERR "%s: error %d whilst initialising SD card\n",
mmc_hostname(host), err);
return err;
}
/
/*
-
Starting point for MMC card init.
*/
int mmc_attach_mmc(struct mmc_host *host, u32 ocr)
{
int err;BUG_ON(!host);
WARN_ON(!host->claimed);mmc_attach_bus(host, &mmc_ops);
/*
- We need to get OCR a different way for SPI.
*/
if (mmc_host_is_spi(host)) {
err = mmc_spi_read_ocr(host, 1, &ocr);
if (err)
goto err;
}
/*
- Sanity check the voltages that the card claims to
- support.
*/
if (ocr & 0x7F) {
printk(KERN_WARNING "%s: card claims to support voltages "
“below the defined range. These will be ignored.\n”,
mmc_hostname(host));
ocr &= ~0x7F;
}
host->ocr = mmc_select_voltage(host, ocr);
/*
- Can we support the voltage of the card?
*/
if (!host->ocr) {
err = -EINVAL;
goto err;
}
/*
- Detect and init the card.
*/
err = mmc_init_card(host, host->ocr, NULL);
if (err)
goto err;
mmc_release_host(host);
err = mmc_add_card(host->card);
if (err)
goto remove_card;return 0;
- We need to get OCR a different way for SPI.
remove_card:
mmc_remove_card(host->card);
host->card = NULL;
mmc_claim_host(host);
err:
mmc_detach_bus(host);
mmc_release_host(host);
printk(KERN_ERR "%s: error %d whilst initialising MMC card\n",
mmc_hostname(host), err);
return err;
}
/*
-
Starting point for SDIO card init.
*/
int mmc_attach_sdio(struct mmc_host *host, u32 ocr)
{
int err;
int i, funcs;
struct mmc_card *card;BUG_ON(!host);
WARN_ON(!host->claimed);mmc_attach_bus(host, &mmc_sdio_ops);
/*
- Sanity check the voltages that the card claims to
- support.
*/
if (ocr & 0x7F) {
printk(KERN_WARNING "%s: card claims to support voltages "
“below the defined range. These will be ignored.\n”,
mmc_hostname(host));
ocr &= ~0x7F;
}
if (ocr & MMC_VDD_165_195) {
printk(KERN_WARNING "%s: SDIO card claims to support the "
"incompletely defined ‘low voltage range’. This "
“will be ignored.\n”, mmc_hostname(host));
ocr &= ~MMC_VDD_165_195;
}host->ocr = mmc_select_voltage(host, ocr);
/*
- Can we support the voltage(s) of the card(s)?
*/
if (!host->ocr) {
err = -EINVAL;
goto err;
}
/*
- Inform the card of the voltage
*/
err = mmc_send_io_op_cond(host, host->ocr, &ocr);
if (err)
goto err;
/*
- For SPI, enable CRC as appropriate.
*/
if (mmc_host_is_spi(host)) {
err = mmc_spi_set_crc(host, use_spi_crc);
if (err)
goto err;
}
/*
- The number of functions on the card is encoded inside
- the ocr.
*/
funcs = (ocr & 0x70000000) >> 28;
/*
- Allocate card structure.
*/
card = mmc_alloc_card(host, NULL);
if (IS_ERR(card)) {
err = PTR_ERR(card);
goto err;
}
card->type = MMC_TYPE_SDIO;
card->sdio_funcs = funcs;host->card = card;
/*
-
For native busses: set card RCA and quit open drain mode.
*/
if (!mmc_host_is_spi(host)) {
err = mmc_send_relative_addr(host, &card->rca);
if (err)
goto remove;mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
}
/*
- Select card, as all following commands rely on that.
*/
if (!mmc_host_is_spi(host)) {
err = mmc_select_card(card);
if (err)
goto remove;
}
/*
- Read the common registers.
*/
err = sdio_read_cccr(card);
if (err)
goto remove;
/*
- Read the common CIS tuples.
*/
err = sdio_read_common_cis(card);
if (err)
goto remove;
/*
- Switch to high-speed (if supported).
*/
err = sdio_enable_hs(card);
if (err)
goto remove;
/*
- Change to the card’s maximum speed.
/
if (mmc_card_highspeed(card)) {
/- The SDIO specification doesn’t mention how
- the CIS transfer speed register relates to
- high-speed, but it seems that 50 MHz is
- mandatory.
*/
mmc_set_clock(host, 50000000);
} else {
mmc_set_clock(host, card->cis.max_dtr);
}
/*
- Switch to wider bus (if supported).
*/
err = sdio_enable_wide(card);
if (err)
goto remove;
/*
- Initialize (but don’t add) all present functions.
*/
for (i = 0;i < funcs;i++) {
err = sdio_init_func(host->card, i + 1);
if (err)
goto remove;
}
mmc_release_host(host);
/*
- First add the card to the driver model…
*/
err = mmc_add_card(host->card);
if (err)
goto remove_added;
/*
- …then the SDIO functions.
*/
for (i = 0;i < funcs;i++) {
err = sdio_add_func(host->card->sdio_func[i]);
if (err)
goto remove_added;
}
return 0;
remove_added:
/* Remove without lock if the device has been added. /
mmc_sdio_remove(host);
mmc_claim_host(host);
remove:
/ And with lock if it hasn’t been added. */
if (host->card)
mmc_sdio_remove(host);
err:
mmc_detach_bus(host);
mmc_release_host(host);
printk(KERN_ERR "%s: error %d whilst initialising SDIO card\n",
mmc_hostname(host), err);
return err;
}
观察传递的参数,是mrq。可以知道mmc最终命令的承载都是用struct mmc_request *mrq 这样的结构完成
static void
mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
{
if (mrq->data) {
mmc_hostname(host), mrq->data->blksz,
mrq->data->blocks, mrq->data->flags,
mrq->data->timeout_ns / 1000000,
mrq->data->timeout_clks);
}
if (mrq->stop) {
pr_debug("%s: CMD%u arg %08x flags %08x\n",
mmc_hostname(host), mrq->stop->opcode,
mrq->stop->arg, mrq->stop->flags);
}
led_trigger_event(host->led, LED_FULL);
mrq->cmd->error = 0;
mrq->cmd->mrq = mrq;
if (mrq->data) {
host->max_req_size);
mrq->cmd->data = mrq->data;
mrq->data->error = 0;
mrq->data->mrq = mrq;
if (mrq->stop) {
mrq->data->stop = mrq->stop;
mrq->stop->error = 0;
mrq->stop->mrq = mrq;
}
}
mmc_start_request->host->ops->request(即sdhci_request)
///mmc_start_request要交给各个host完成处理了。Mmc驱动是一个通用框架驱动,
//不同的host对应的命令处理必定有所差别。针对sdhci标准的host mmc驱动。
//host->ops->request(host, mrq);的执行将交给,sdhci_request()完成。
host->ops->request(host, mrq);
}
/
static const struct mmc_host_ops sdhci_ops = {
//request函数指针指向的函数用来处理host向从设备发送命令的请求,
.request = sdhci_request,
//set_ios用来设置电源、时钟等等之类(需要重点关注),
.set_ios = sdhci_set_ios,
//get_ro用来判断是否写保护
.get_ro = sdhci_get_ro,
//使能SD/MMC IRQ中断
.enable_sdio_irq = sdhci_enable_sdio_irq,
};
///
//static const struct mmc_host_ops sdhci_ops = {
// .request = sdhci_request,
//request函数指针指向的函数用来处理host向从设备发送命令的请求,
/*****************************************************************************\
-
*
- MMC callbacks *
-
*
*****************************************************************************/
static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
{
//注意函数一进来,host结构体发生变化,已经不再是mmc_host结构,
//而是各具体的厂商的host,如这里的struct sdhci_host *host;
//其实是host = mmc_priv(mmc);这么的得来的。
struct sdhci_host *host;
unsigned long flags;
host = mmc_priv(mmc);
spin_lock_irqsave(&host->lock, flags);
#ifndef CONFIG_LEDS_CLASS
sdhci_activate_led(host);
#endif
//保存起mrq结构
host->mrq = mrq;
if ((mmc->caps & MMC_CAP_ON_BOARD) || (host->flags & SDHCI_DEVICE_ALIVE))
//当命令传输完成系统调用中断处理函数sdhci_irq。在其中进行中断完成后的处理???。
//sdhci_add_host 中
// tasklet_init(&host->finish_tasklet, sdhci_tasklet_finish, (unsigned long)host);
sdhci_send_command(host, mrq->cmd);//发送命令
else {
if (!(readl(host->ioaddr + SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT)
|| (host->flags & SDHCI_DEVICE_DEAD)) {
host->mrq->cmd->error = -ENOMEDIUM;
tasklet_schedule(&host->finish_tasklet);
} else
sdhci_send_command(host, mrq->cmd);
}
//为了保证编译器顺序编译,防止编译器优化打乱执行顺序
mmiowb();
spin_unlock_irqrestore(&host->lock, flags);
}
//
static void sdhci_send_command(struct sdhci_host *host, struct mmc_command cmd)
{
int flags;
u32 mask;
unsigned long timeout;
/ Wait max 10 ms */
timeout = 10;
//SDHCI_PRESENT_STATE 寄存器的第0位
图片
mask = SDHCI_CMD_INHIBIT;
//SDHCI_PRESENT_STATE 寄存器的第1位
图片
if ((cmd->data != NULL) || (cmd->flags & MMC_RSP_BUSY))
mask |= SDHCI_DATA_INHIBIT;
/* We shouldn't wait for data inihibit for stop commands, even
though they might use busy signaling */
if (host->mrq->data && (cmd == host->mrq->data->stop))
mask &= ~SDHCI_DATA_INHIBIT;
//
//#define SDHCI_PRESENT_STATE 0x24
//读一个长整形数据(8字节)当前状态寄存器
while (readl(host->ioaddr + SDHCI_PRESENT_STATE) & mask) {
if (timeout == 0) {
printk(KERN_ERR "%s: Controller never released "
“inhibit bit(s).\n”, mmc_hostname(host->mmc));
sdhci_dumpregs(host);
cmd->error = -EIO;
//
//int sdhci_add_host(struct sdhci_host *host)
// tasklet_init(&host->finish_tasklet, sdhci_tasklet_finish, (unsigned long)host);
finish_tasklet用于命令传输完成后的处理
tasklet_schedule(&host->finish_tasklet);
return;
}
timeout–;
mdelay(1);
}
//改变定时器时间,设置定时时间
mod_timer(&host->timer, jiffies + 10 * HZ);
//命令保存到host起来
host->cmd = cmd;
//准备数据
//里面的关键就是准备DMA,dma_map_sg(),
//从data->sg里面获取到信息,填充到DMA控制器里面。
sdhci_prepare_data(host, cmd->data);
//#define SDHCI_ARGUMENT 0x08
//SD命令参数寄存器
图片
writel(cmd->arg, host->ioaddr + SDHCI_ARGUMENT);
sdhci_set_transfer_mode(host, cmd->data);
if ((cmd->flags & MMC_RSP_136) && (cmd->flags & MMC_RSP_BUSY)) {
printk(KERN_ERR "%s: Unsupported response type!\n",
mmc_hostname(host->mmc));
cmd->error = -EINVAL;
//启动finish_tasklet完成后面的事情。finish_tasklet的定义是sdhci_tasklet_finish
tasklet_schedule(&host->finish_tasklet);
return;
}
if (!(cmd->flags & MMC_RSP_PRESENT))
flags = SDHCI_CMD_RESP_NONE;
else if (cmd->flags & MMC_RSP_136)
flags = SDHCI_CMD_RESP_LONG;
else if (cmd->flags & MMC_RSP_BUSY)
flags = SDHCI_CMD_RESP_SHORT_BUSY;
else
flags = SDHCI_CMD_RESP_SHORT;
if (cmd->flags & MMC_RSP_CRC)
flags |= SDHCI_CMD_CRC;
if (cmd->flags & MMC_RSP_OPCODE)
flags |= SDHCI_CMD_INDEX;
if (cmd->data)
flags |= SDHCI_CMD_DATA;
//数据发送,这才是真正的控制host发送命令的操作,到这类,mmc控制器才开始跟sd卡做交互
//#define SDHCI_COMMAND 0x0E
图片
writew(SDHCI_MAKE_CMD(cmd->opcode, flags),
host->ioaddr + SDHCI_COMMAND);
}
///