How to setup the clock for SD/MMC in Linux Driver
1. set up clock
Driversmmccoremmc.c
(for SD, similar is Driversmmccoresd.c)
static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
struct mmc_card *oldcard)
{
………..
//1.Setup to high speed if hw support
/*
* Activate high speed (if supported)
*/
if ((card->ext_csd.hs_max_dtr != 0) &&
(host->caps & MMC_CAP_MMC_HIGHSPEED)) {
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_HS_TIMING, 1);
if (err != MMC_ERR_NONE)
goto free_card;
mmc_card_set_highspeed(card);
mmc_set_timing(card->host, MMC_TIMING_MMC_HS);
}
//2.set the allowable clock
/*
* Compute bus speed.
*/
max_dtr = (unsigned int)-1;
if (mmc_card_highspeed(card)) {
if (max_dtr > card->ext_csd.hs_max_dtr)
max_dtr = card->ext_csd.hs_max_dtr;
} else if (max_dtr > card->csd.max_dtr) {
max_dtr = card->csd.max_dtr;
}
mmc_set_clock(host, max_dtr);
………..
}
2.do set up clock
Driversmmccorecore.c
void mmc_set_clock(struct mmc_host *host, unsigned int hz)
{
………..
host->ios.clock = hz;
mmc_set_ios(host);
………..
}
3. set io related info, include clock
static inline void mmc_set_ios(struct mmc_host *host)
{
………..
host->ops->set_ios(host, ios);
}
4.call driver’s set_ios function
Here , host->ops->set_ios is
DriversmmchostYOUR_ARCH_NAME_mmc.c
static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
{
if (ios->clock) {
if (ios->clock >= host->mclk) {
//for bypass mode, the MCLK(input clock/main clock/mclk)
//directly driven into the MCLCLK(card clock/cclk)
//that is MCLK=MCLCLK, mclk=cclk
clk = MCI_CLK_BYPASS;
host->cclk = host->mclk;
} else {
//calculate the clkDiv in follow picture :
//(in SD/MMC datasheet )
if ( host->mclk < (2 * ios->clock) )
{
clk = 2;
}
else
{
clk = (host->mclk -(2 * ios->clock)) / (2 * ios->clock) + 1;
if (clk > 255)
clk = 255;
}
if ( clk == 1)
clk = 16;
//clk = 255;
//according to Mclclk(card clock)= Mclk(input clock/main clock)/(2x (clkDiv + 1)),
//calculate out the real cclk(card clock)
host->cclk = host->mclk / (2 * (clk + 1));
}
clk |= MCI_CLK_ENABLE;
}
if (host->plat->translate_vdd)
pwr |= host->plat->translate_vdd(mmc_dev(mmc), ios->vdd);
switch (ios->power_mode) {
case MMC_POWER_OFF:
break;
case MMC_POWER_UP:
pwr |= MCI_PWR_UP;
break;
case MMC_POWER_ON:
pwr |= MCI_PWR_ON;
break;
}
if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN)
pwr |= MCI_ROD;
if (ios->bus_width == MMC_BUS_WIDTH_4 ) {
DBG(host,"MMC: Setting controller bus width to %dn",4);
clk |= MCI_CLK_WIDEBUS;
}
//write the setting value into the reg
writel(clk, host->base + MMCICLOCK);
……….
}
5.Note:
In which the host->mclk is get in:
static int mmci_probe(struct amba_device *dev, void *id)
{
host->mclk = clk_get_rate(host->clk);
}
\archarmmach-YOUR_ARCH_NAMEClock.c
unsigned long clk_get_rate(struct clk *clk)
{
…….
return clk->rate;
}
【Suffix】
Related articles:
【转】mmci控制器驱动(2.6.22.5) — (1) 驱动注册
http://hi.baidu.com/serial_story/blog/item/24bb1a2dd050fd3c359bf701.html
转载请注明:在路上 » How to setup the clock for SD/MMC in Linux Driver