SDIO驅動(13)card的初始化

/*
 * Starting point for SDIO card init.
 */
int mmc_attach_sdio(struct mmc_host *host)
{
	int err, i, funcs;
	u32 ocr;
	struct mmc_card *card;

	BUG_ON(!host);
	WARN_ON(!host->claimed);

	err = mmc_send_io_op_cond(host, 0, &ocr);
	if (err)
		return err;

	mmc_attach_bus(host, &mmc_sdio_ops);
	if (host->ocr_avail_sdio)
		host->ocr_avail = host->ocr_avail_sdio;

	/*
	 * 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(s) of the card(s)?
	 */
	if (!host->ocr) {
		err = -EINVAL;
		goto err;
	}

	/*
	 * Detect and init the card.
	 */
	err = mmc_sdio_init_card(host, host->ocr, NULL, 0);
	if (err)
		goto err;
	card = host->card;

	/*
	 * Enable runtime PM only if supported by host+card+board
	 */
	if (host->caps & MMC_CAP_POWER_OFF_CARD) {
		/*
		 * Let runtime PM core know our card is active
		 */
		err = pm_runtime_set_active(&card->dev);
		if (err)
			goto remove;

		/*
		 * Enable runtime PM for this card
		 */
		pm_runtime_enable(&card->dev);
	}

	/*
	 * The number of functions on the card is encoded inside
	 * the ocr.
	 */
	funcs = (ocr & 0x70000000) >> 28;
	card->sdio_funcs = 0;

	/*
	 * Initialize (but don't add) all present functions.
	 */
	for (i = 0; i < funcs; i++, card->sdio_funcs++) {
		err = sdio_init_func(host->card, i + 1);
		if (err)
			goto remove;

		/*
		 * Enable Runtime PM for this func (if supported)
		 */
		if (host->caps & MMC_CAP_POWER_OFF_CARD)
			pm_runtime_enable(&card->sdio_func[i]->dev);
	}

	/*
	 * First add the card to the driver model...
	 */
	mmc_release_host(host);
	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;
	}

	mmc_claim_host(host);
	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. */
	mmc_release_host(host);
	if (host->card)
		mmc_sdio_remove(host);
	mmc_claim_host(host);
err:
	mmc_detach_bus(host);

	printk(KERN_ERR "%s: error %d whilst initialising SDIO card\n",
		mmc_hostname(host), err);

	return err;
}

13行mmc_send_io_op_cond,通過CMD5查詢card支持的操作電壓,CMD5是sdio特有的,所以如果對該命令沒有反應說明卡槽裏的是sd/mmc卡。由於命令發送函數結構大體差不離,所以就不列出mmc_send_io_op_cond函數的具體內容,來看CMD5


OCR-Operation Conditions Register,card支持的最小和最低電壓,這24bits的定義在“SDIO驅動(12)card的掃描流程”有詳細列出。mmc_send_io_op_cond執行過後,變量ocr的內容爲


C:如果爲1的話,說明卡已經準備就緒ready。

Number of I/O functions:card支持的I/O總數,範圍0~7。

Memory Present:如果爲0說明card僅僅是一個I/O卡;1說明card還支持SD memory。

Stuff bits:保留,值爲0。

I/O OCR:card返回的Voltage support list。


17行mmc_attach_bus,爲每一個host分配獨有的總線handler:

/*
 * Assign a mmc bus handler to a host. Only one bus handler may control a
 * host at any given time.
 */
void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops)
{
	unsigned long flags;

	BUG_ON(!host);
	BUG_ON(!ops);

	WARN_ON(!host->claimed);

	spin_lock_irqsave(&host->lock, flags);

	BUG_ON(host->bus_ops);
	BUG_ON(host->bus_refs);

	host->bus_ops = ops;
	host->bus_refs = 1;
	host->bus_dead = 0;

	spin_unlock_irqrestore(&host->lock, flags);
}
9、10行的檢測是防止NULL指針,16、17行防止對host重複執行attach bus,之前說過如果檢測出現異常系統會panic。

12行,記得吧在進入之前可是執行過獨佔訪問設置的:

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);
關於sdio總線的mmc_sdio_ops,之前分析過它的detect函數實現,其他成員實現就略過不提了。


25~30行,card返回的ocr參數檢測,防止出現不支持的電壓值。不過按照spec,保留位佔8個bits,難道不是應該if (ocr & 0xFF) {}嗎?
32行mmc_select_voltage,選擇host和card都支持的最低操作電壓:

/*
 * Mask off any voltages we don't support and select
 * the lowest voltage
 */
u32 mmc_select_voltage(struct mmc_host *host, u32 ocr)
{
	int bit;

	ocr &= host->ocr_avail;

	bit = ffs(ocr);
	if (bit) {
		bit -= 1;

		ocr &= 3 << bit;

		host->ios.vdd = bit;
		mmc_set_ios(host);
	} else {
		pr_warning("%s: host doesn't support card's voltages\n",
				mmc_hostname(host));
		ocr = 0;
	}

	return ocr;
}
9行與過之後就是host、card都支持的電壓設置,11行返回ocr中第一個bit是1的位置,注意範圍計算方式[1,32]所以需要減去1,15行過後將mask off其他電壓,不過這裏3<<bit的話,如果ocr[bit+1]不爲零,那麼ocr[bit+1, bit]=0b11即bit+1位不被mask off。這是基於何種考慮?


45行正式開始card的初始化,複雜的函數:

/*
 * Handle the detection and initialisation of a card.
 *
 * In the case of a resume, "oldcard" will contain the card
 * we're trying to reinitialise.
 */
static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
			      struct mmc_card *oldcard, int powered_resume)
{
	struct mmc_card *card;
	int err;

	BUG_ON(!host);
	WARN_ON(!host->claimed);

	/*
	 * Inform the card of the voltage
	 */
	if (!powered_resume) {
		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;
	}

	/*
	 * Allocate card structure.
	 */
	card = mmc_alloc_card(host, NULL);
	if (IS_ERR(card)) {
		err = PTR_ERR(card);
		goto err;
	}

	if (ocr & R4_MEMORY_PRESENT
	    && mmc_sd_get_cid(host, host->ocr & ocr, card->raw_cid) == 0) {
		card->type = MMC_TYPE_SD_COMBO;

		if (oldcard && (oldcard->type != MMC_TYPE_SD_COMBO ||
		    memcmp(card->raw_cid, oldcard->raw_cid, sizeof(card->raw_cid)) != 0)) {
			mmc_remove_card(card);
			return -ENOENT;
		}
	} else {
		card->type = MMC_TYPE_SDIO;

		if (oldcard && oldcard->type != MMC_TYPE_SDIO) {
			mmc_remove_card(card);
			return -ENOENT;
		}
	}

	/*
	 * Call the optional HC's init_card function to handle quirks.
	 */
	if (host->ops->init_card)
		host->ops->init_card(host, card);

	/*
	 * For native busses:  set card RCA and quit open drain mode.
	 */
	if (!powered_resume && !mmc_host_is_spi(host)) {
		err = mmc_send_relative_addr(host, &card->rca);
		if (err)
			goto remove;

		/*
		 * Update oldcard with the new RCA received from the SDIO
		 * device -- we're doing this so that it's updated in the
		 * "card" struct when oldcard overwrites that later.
		 */
		if (oldcard)
			oldcard->rca = card->rca;

		mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
	}

	/*
	 * Read CSD, before selecting the card
	 */
	if (!oldcard && card->type == MMC_TYPE_SD_COMBO) {
		err = mmc_sd_get_csd(host, card);
		if (err)
			return err;

		mmc_decode_cid(card);
	}

	/*
	 * Select card, as all following commands rely on that.
	 */
	if (!powered_resume && !mmc_host_is_spi(host)) {
		err = mmc_select_card(card);
		if (err)
			goto remove;
	}

	if (card->quirks & MMC_QUIRK_NONSTD_SDIO) {
		/*
		 * This is non-standard SDIO device, meaning it doesn't
		 * have any CIA (Common I/O area) registers present.
		 * It's host's responsibility to fill cccr and cis
		 * structures in init_card().
		 */
		mmc_set_clock(host, card->cis.max_dtr);

		if (card->cccr.high_speed) {
			mmc_card_set_highspeed(card);
			mmc_set_timing(card->host, MMC_TIMING_SD_HS);
		}

		goto finish;
	}

	/*
	 * 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;

	if (oldcard) {
		int same = (card->cis.vendor == oldcard->cis.vendor &&
			    card->cis.device == oldcard->cis.device);
		mmc_remove_card(card);
		if (!same)
			return -ENOENT;

		card = oldcard;
	}

	if (card->type == MMC_TYPE_SD_COMBO) {
		err = mmc_sd_setup_card(host, card, oldcard != NULL);
		/* handle as SDIO-only card if memory init failed */
		if (err) {
			mmc_go_idle(host);
			if (mmc_host_is_spi(host))
				/* should not fail, as it worked previously */
				mmc_spi_set_crc(host, use_spi_crc);
			card->type = MMC_TYPE_SDIO;
		} else
			card->dev.type = &sd_type;
	}

	/*
	 * If needed, disconnect card detection pull-up resistor.
	 */
	err = sdio_disable_cd(card);
	if (err)
		goto remove;

	/*
	 * Switch to high-speed (if supported).
	 */
	err = sdio_enable_hs(card);
	if (err > 0)
		mmc_sd_go_highspeed(card);
	else if (err)
		goto remove;

	/*
	 * Change to the card's maximum speed.
	 */
	mmc_set_clock(host, mmc_sdio_get_max_clock(card));

	/*
	 * Switch to wider bus (if supported).
	 */
	err = sdio_enable_4bit_bus(card);
	if (err > 0)
		mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4);
	else if (err)
		goto remove;

finish:
	if (!oldcard)
		host->card = card;
	return 0;

remove:
	if (!oldcard)
		mmc_remove_card(card);

err:
	return err;
}
20行,之前的mmc_send_io_op_cond動作第二個參數爲0,所以僅僅是探測一下;這裏傳入了ocr參數,那麼將等待card狀態ready後才返回。
28~32行的SPI接口操作方式不予關注。

37~41行,mmc_alloc_card創建一個card設備

43~51行,即上面提到的sdio+memory混合的卡,MMC框架對其定義的類型爲MMC_TYPE_SD_COMBO。純粹的sdio卡的類型爲MMC_TYPE_SDIO。

64、65行,host controller提供了init_card回調的話則調用之,用來執行controller特定的card初始話。如果硬件廠商按照標準來的話自然不需要,但林子這麼大難免有不按套路出牌的,無論怎麼說該支持還得支持。

71行,通過CMD3獲取card的地址(RCA),CMD3的響應爲R6:


83行,bus有兩種模式:

MMC_BUSMODE_OPENDRAIN: 該模式用於card的初始化。

MMC_BUSMODE_PUSHPULL: 進入傳輸狀態前,設置bus模式爲pushpull模式。

89~95行,sdio卡不支持該操作。

101行,發送CMD7選中card,在standby和transfer狀態之間進行轉換,此處轉換到transfer狀態。

106~121行,說的就是那些不按套路設計硬件的廠商的產品,由host填充時序參數。

126行,sdio_read_cccr讀取CCCR(Card Common Control Registers)寄存器的值,這裏有個關係需要知道:


1、CCCR位於卡的CIA區域;

2、CIA區域訪問方式:host通過sdio專屬命令CMD52、功能號0訪問。

所以,看sdio_read_cccr函數:

static int sdio_read_cccr(struct mmc_card *card)
{
	int ret;
	int cccr_vsn;
	unsigned char data;

	memset(&card->cccr, 0, sizeof(struct sdio_cccr));
	// SDIO_CCCR_CCCR = 0
	ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_CCCR, 0, &data);
	if (ret)
		goto out;

	cccr_vsn = data & 0x0f;

	if (cccr_vsn > SDIO_CCCR_REV_1_20) {
		printk(KERN_ERR "%s: unrecognised CCCR structure version %d\n",
			mmc_hostname(card->host), cccr_vsn);
		return -EINVAL;
	}

	card->cccr.sdio_vsn = (data & 0xf0) >> 4;

	ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_CAPS, 0, &data);
	if (ret)
		goto out;

	if (data & SDIO_CCCR_CAP_SMB)
		card->cccr.multi_block = 1;
	if (data & SDIO_CCCR_CAP_LSC)
		card->cccr.low_speed = 1;
	if (data & SDIO_CCCR_CAP_4BLS)
		card->cccr.wide_bus = 1;

	if (cccr_vsn >= SDIO_CCCR_REV_1_10) {
		ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_POWER, 0, &data);
		if (ret)
			goto out;

		if (data & SDIO_POWER_SMPC)
			card->cccr.high_power = 1;
	}

	if (cccr_vsn >= SDIO_CCCR_REV_1_20) {
		ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_SPEED, 0, &data);
		if (ret)
			goto out;

		if (data & SDIO_SPEED_SHS)
			card->cccr.high_speed = 1;
	}

out:
	return ret;
}
9行,mmc_io_rw_direct是CMD52的實現函數,其原型:

int mmc_io_rw_direct(struct mmc_card *card, int write, unsigned fn, unsigned addr, u8 in, u8 *out)

通過設置不同addr參數就可以讀取相應寄存器信息。這部分內容完全就是spec的實現,參照spec即可理解。


133行,sdio_read_common_cis讀取CIS(Card Information Structure)信息。這部分內容較多,只能單獨列出,參見“SDIO驅動(14)card的CIS讀取”。

137~145行,參數oldcard傳進來的是NULL,所以不予關心。147~158行,MMC_TYPE_SD_COMBO混合卡也不關心。

163~185行,這部分函數作用及原理參考spec都很容易理解。

至此,mmc_sdio_init_card函數就分析完了。


接着看mmc_attach_sdio函數,53~65行,又是PM的東西,71~72行,解析出function numbers。

77~87行,每一個function作爲一個device,由結構體sdio_func表徵,掛在sdio總線下面;sdio_init_func函數用來完成創建並初始化它。

93行,添加card到系統中(device_add)並標誌card爲Present狀態;101行,做同樣的事情。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章