您的位置:首页 > 其它

SDIO驱动(13)card的初始化

2017-05-29 17:24 190 查看
/*
* 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行,做同样的事情。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: