|
今天看帖子看到一个关于MMC的问题,然后自己对照了一些最新的android4.1.2的内核源代码,发现问题依然存在。我将内容发布在这里供大家浏览,并希望TI的FAE可以尽快修复这个问题。
最近在測試 整個 kernel 功能的時候發現 , SD Card 移除的過程中 kernel 會"卡機" 約800mS , 這 800 mS 中 , 連 kernel driver 中的 add_timer() function 都停滯 !!
初步 判別 , 應該卡在 IRQ 中.
尋找了半天 ,發現 , Card remove 過程 , 會呼叫
int _mmc_detect_card_removed(struct mmc_host *host)
在這個 function 中 會呼叫 ret = host->bus_ops->alive(host);
alive() ; 在core/sd.c 中有註冊 , 內容是
static int mmc_sd_alive(struct mmc_host *host)
{
return mmc_send_status(host->card, NULL);
}
好玩的事情來了 , Card 已經拔除 , 還送 status command 給 Host !!
mmc_send_status() 定義在 mmc_ops.c 中
int mmc_send_status(struct mmc_card *card, u32 *status)
{
int err;
struct mmc_command cmd = {0};
BUG_ON(!card);
BUG_ON(!card->host);
cmd.opcode = MMC_SEND_STATUS;
if (!mmc_host_is_spi(card->host))
cmd.arg = card->rca << 16;
cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC;
err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
if (err)
return err;
/* NOTE: callers are required to understand the difference
* between "native" and SPI format status words!
*/
if (status)
*status = cmd.resp[0];
return 0;
}
送出 command 後要等 cmd done. cmd done 需要 mmc controller 的irq 中斷 .
AM335x BSP 的 mmc 中斷流程如下:
omap_hsmmc_irq()
omap_hsmmc_do_irq()
## if have error.
omap_hsmmc_reset_controller_fsm()
主要卡在 omap_hsmmc_reset_controller_fsm() 中.
首先 , 我不喜歡 IRQ 執行太多事情 , 所以先用 tasklet 分開 ,這時候要注意 , MMC 的 IRQ 會一直送 ,直到IRQ 事件處理完畢 , tasklet 只能排程一次 , 所以要先關閉 IRQ , 等 tasklit 完成後在打開.
修改後如下:
static void omap_hsmmc_irq_tasklet(unsigned long arg)
{
struct omap_hsmmc_host *host = (struct omap_hsmmc_host *) arg;
omap_hsmmc_do_irq(host, host->irq_status,host->irq);
//---- reenable mmc irq signal.
OMAP_HSMMC_WRITE(host->base, ISE, INT_EN_MASK);
spin_unlock(&host->irq_lock);
}
static irqreturn_t omap_hsmmc_irq(int irq, void *dev_id)
{
struct omap_hsmmc_host *host = dev_id;
#if defined(USING_TASKLET)
//---- disable mmc irq signal.
spin_lock(&host->irq_lock);
OMAP_HSMMC_WRITE(host->base, ISE, 0);
host->irq_status = OMAP_HSMMC_READ(host->base, STAT);
tasklet_schedule(&host->omap_irq_tasklet);
#else
int status;
status = OMAP_HSMMC_READ(host->base, STAT);
do {
omap_hsmmc_do_irq(host, status, irq);
/* Flush posted write */
status = OMAP_HSMMC_READ(host->base, STAT);
} while (status & INT_EN_MASK);
#endif
return IRQ_HANDLED;
}
這樣後還是會 "卡機" , 看一下主要卡住的地方 omap_hsmmc_reset_controller_fsm()
unsigned long limit = (loops_per_jiffy *
msecs_to_jiffies(MMC_TIMEOUT_MS));
..............
...............
while ((!(OMAP_HSMMC_READ(host->base, SYSCTL) & bit)) &&
(i++ < limit))
cpu_relax();
首先 , cpu_relax() 其實應該是 一堆 NOP 指令(我之前沒有用過@@) , 主要是 loops_per_jiffy 這個參數 ,這個參數應該是每秒執行多少個 nop 指令 , 所以上面這樣的 code 感覺怪怪的 , 這個 cpu_relax() + OMAP_HSMMC_READ + 其他判別 的 code 會執行 數百萬次 ....當然會卡住 !!
都改成 tacklet 了 , 就想說換成一般 mdelay 的方式 , 這個 limit 只是 timeout 的變數, 沒有太大影響. 修改後如下:
static inline void omap_hsmmc_reset_controller_fsm(struct omap_hsmmc_host *host,
unsigned long bit)
{
unsigned long i = 0;
#if !defined(USING_TASKLET)
unsigned long limit = (loops_per_jiffy *
msecs_to_jiffies(MMC_TIMEOUT_MS));
#endif
OMAP_HSMMC_WRITE(host->base, SYSCTL,
OMAP_HSMMC_READ(host->base, SYSCTL) | bit);
/*
* OMAP4 ES2 and greater has an updated reset logic.
* Monitor a 0->1 transition first
*/
if (mmc_slot(host).features & HSMMC_HAS_UPDATED_RESET)
{
#if defined(USING_TASKLET)
while ((!(OMAP_HSMMC_READ(host->base, SYSCTL) & bit)) &&
(i++ < MMC_TIMEOUT_MS))
mdelay(1);
#else
while ((!(OMAP_HSMMC_READ(host->base, SYSCTL) & bit)) &&
(i++ < limit))
cpu_relax();
#endif
......................
}
|
|