SDIO驱动(12)card的扫描流程

在"SDIO驱动(9)Host注册"中提到,一旦host设备注册成功,接下来会启动一次扫描好获取并管理挂在该host上的card。

如果是一张可以热插拔的card,当card插入/拔出时触发中断,同样也将启动扫描流程。

以中断触发为例:

/*
 * ISR for the CardDetect Pin
*/
static irqreturn_t s3cmci_irq_cd(int irq, void *dev_id)
{
	struct s3cmci_host *host = (struct s3cmci_host *)dev_id;

	dbg(host, dbg_irq, "card detect\n");

	mmc_detect_change(host->mmc, msecs_to_jiffies(500));

	return IRQ_HANDLED;
}
中断到来,s3cmci_irq_cd函数运行,还记得dev_id是创建中断请求时传进来的,类型就是s3cmci_host:

static int __devinit s3cmci_probe(struct platform_device *pdev)
{
	struct s3cmci_host *host;
	if (request_irq(host->irq, s3cmci_irq, 0, DRIVER_NAME, host)) {}
}
mmc_detect_change是一个"为他人作嫁衣裳"的函数,它启动一个delayed_work:host->detect。还记得它的初始化?

struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
{
	INIT_DELAYED_WORK(&host->detect, mmc_rescan);
}

根据参数msecs_to_jiffies(500)-convert milliseconds to jiffies,它将在500毫秒后运行:

void mmc_rescan(struct work_struct *work)
{
	static const unsigned freqs[] = { 400000, 300000, 200000, 100000 };
	struct mmc_host *host =
		container_of(work, struct mmc_host, detect.work);
	int i;

	if (host->rescan_disable)
		return;

	mmc_bus_get(host);

	/*
	 * if there is a _removable_ card registered, check whether it is
	 * still present
	 */
	if (host->bus_ops && host->bus_ops->detect && !host->bus_dead
	    && !(host->caps & MMC_CAP_NONREMOVABLE))
		host->bus_ops->detect(host);

	/*
	 * Let mmc_bus_put() free the bus/bus_ops if we've found that
	 * the card is no longer present.
	 */
	mmc_bus_put(host);
	mmc_bus_get(host);

	/* if there still is a card present, stop here */
	if (host->bus_ops != NULL) {
		mmc_bus_put(host);
		goto out;
	}

	/*
	 * 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);
	for (i = 0; i < ARRAY_SIZE(freqs); i++) {
		if (!mmc_rescan_try_freq(host, max(freqs[i], host->f_min)))
			break;
		if (freqs[i] < host->f_min)
			break;
	}
	mmc_release_host(host);

 out:
	if (host->caps & MMC_CAP_NEEDS_POLL)
		mmc_schedule_delayed_work(&host->detect, HZ);
}
11~25行,检测一个removable性质的card是否还存在。mmc_bus_get,增加总线的引用计数,类似使用方式之前也提到过,就是防止“正在桥上走着,桥却被抽走”的情况发生。19行调用bus提供的detect回调进行检测,以"sdio"总线为例,就是mmc_sdio_detect函数:

/*
 * Card detection callback from host.
 */
static void mmc_sdio_detect(struct mmc_host *host)
-->int mmc_select_card(struct mmc_card *card)
---->_mmc_select_card(struct mmc_host *host, struct mmc_card *card)
{
	int err;
	struct mmc_command cmd;

	BUG_ON(!host);

	memset(&cmd, 0, sizeof(struct mmc_command));

	cmd.opcode = MMC_SELECT_CARD;

	if (card) {
		cmd.arg = card->rca << 16;
		cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
	} else {
		cmd.arg = 0;
		cmd.flags = MMC_RSP_NONE | MMC_CMD_AC;
	}

	err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
	if (err)
		return err;

	return 0;
}
detect的实现很简单,就是向card发送一个选中命令(CMD7),参数就是card的地址(RCA,Relative Card Address)。
40行,调用我们在“ SDIO驱动(10)Host的operations实现”提到的get_cd回调,检测card是否存在。返回0表明无卡,那就go out吧,out标识的作用是:如果我们设置了循环检测,在1秒(HZ)后重新开始检测。 有卡返回值为1,于是就用400000, 300000, 200000, 100000频率依次探测:

static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
{
	host->f_init = freq;

#ifdef CONFIG_MMC_DEBUG
	pr_info("%s: %s: trying to init card at %u Hz\n",
		mmc_hostname(host), __func__, host->f_init);
#endif
	mmc_power_up(host);
	sdio_reset(host);
	mmc_go_idle(host);

	mmc_send_if_cond(host, host->ocr_avail);

	/* Order's important: probe SDIO, then SD, then MMC */
	if (!mmc_attach_sdio(host))
		return 0;
	if (!mmc_attach_sd(host))
		return 0;
	if (!mmc_attach_mmc(host))
		return 0;

	mmc_power_off(host);
	return -EIO;
}
mmc_power_up给卡上电,这个函数还是有的说的:

/*
 * Apply power to the MMC stack.  This is a two-stage process.
 * First, we enable power to the card without the clock running.
 * We then wait a bit for the power to stabilise.  Finally,
 * enable the bus drivers and clock to the card.
 *
 * We must _NOT_ enable the clock prior to power stablising.
 *
 * If a host does all the power sequencing itself, ignore the
 * initial MMC_POWER_UP stage.
 */
static void mmc_power_up(struct mmc_host *host)
{
	int bit;

	/* If ocr is set, we use it */
	if (host->ocr)
		bit = ffs(host->ocr) - 1;
	else
		bit = fls(host->ocr_avail) - 1;

	host->ios.vdd = bit;
	if (mmc_host_is_spi(host)) {
		host->ios.chip_select = MMC_CS_HIGH;
		host->ios.bus_mode = MMC_BUSMODE_PUSHPULL;
	} else {
		host->ios.chip_select = MMC_CS_DONTCARE;
		host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
	}
	host->ios.power_mode = MMC_POWER_UP;
	host->ios.bus_width = MMC_BUS_WIDTH_1;
	host->ios.timing = MMC_TIMING_LEGACY;
	mmc_set_ios(host);

	/*
	 * This delay should be sufficient to allow the power supply
	 * to reach the minimum voltage.
	 */
	mmc_delay(10);

	host->ios.clock = host->f_init;

	host->ios.power_mode = MMC_POWER_ON;
	mmc_set_ios(host);

	/*
	 * This delay must be at least 74 clock sizes, or 1 ms, or the
	 * time required to reach a stable voltage.
	 */
	mmc_delay(10);
}
上电流程注释说的很清楚,要先给power,等power稳定了在给clock。
18行ffs-find first bit set,返回32位整数中第一个为1的bit的位置,比如ffs(0x3)=1。相应的fls(0x3)=2即返回最后一个为1的bit的位置,参照标准规定的电压表就明白这两个函数的作用,区别在于ffs取最低电压,fls取最高电压:

SDIO驱动(12)card的扫描流程_第1张图片

33行mmc_set_ios(host),最终调用分析过的set_ios回调,即s3cmci_set_ios函数,至此,该函数就没什么可说的了。


第10行,sdio_reset(host)复位sdio卡,按照spec规定:

In order to reset an I/O only card or the I/O portion of a combo card, use CMD52 to write  
a 1 to the RES bit in the CCCR (bit 3 of register 6)
代码实现:

int sdio_reset(struct mmc_host *host)
{
	int ret;
	u8 abort;

	/* SDIO Simplified Specification V2.0, 4.4 Reset for SDIO */

	ret = mmc_io_rw_direct_host(host, 0, 0, SDIO_CCCR_ABORT, 0, &abort);
	if (ret)
		abort = 0x08;
	else
		abort |= 0x08;

	ret = mmc_io_rw_direct_host(host, 1, 0, SDIO_CCCR_ABORT, abort, NULL);
	return ret;
}
8行,读取CCCR(0x06地址)寄存器内容,读取成功就直设置bit 3(12行),否则直接设置整个寄存器的值(10行)。10行数据写入寄存器,进而card被复位。


11行,mmc_go_idle(host)设置卡进入idle状态,发送命令CMD0即可。此处的关键是防止卡进入SPI模式,方式就是把片选的pin(对应card detect/DAT[3]引脚)拉高。

13行,mmc_send_if_cond(host, host->ocr_avail),通过cmd8检测card是否支持host提供的操作电压(ocr_avail),执行该命令的前提是card处于IDLE状态,所以11行的函数有了解释。mmc_send_if_cond实现:

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;

	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;
}
参照CMD8命令的说明,mmc_send_if_cond就很好理解了:
SDIO驱动(12)card的扫描流程_第2张图片

其中voltage supplied字段:

SDIO驱动(12)card的扫描流程_第3张图片

When the card is in idle state, the host shall issue CMD8 before ACMD41. In the argument, 'voltage supplied' is set to the host supply voltage and 'check pattern' is set to any 8-bit pattern.

The card checks whether it can operate on the host's supply voltage. The card that accepted the supplied voltage returns R7 response.In the response , the card echoes back both the voltage range and check pattern set in the argument. If the card does not support the host supply voltage, it shall not return response and stays in Idle state.
It is recommeded to use '10101010b'(0xAA) for the 'check pattern'.

上面解释了整个函数的用意。


16行,mmc_attach_sdio初始化sdio card。

你可能感兴趣的:(MMC子系统)