您的位置:首页 > 运维架构 > Linux

linux设备模型之mmc,sd子系统<二>

2012-02-01 18:31 399 查看
-----------------------------------------------------------

本文系本站原创,欢迎转载!

转载请注明出处:http://blog.csdn.net/gdt_a20

-----------------------------------------------------------

继续上一篇文章,先看一个重点结构,平台相关,真正对host的设置都会回调到这里

static struct mmc_host_ops s3cmci_ops = {

.request = s3cmci_request, //用于命令和数据的发送接收

.set_ios = s3cmci_set_ios, //用于设置io

.get_ro = s3cmci_get_ro, //用于判断写保护

.get_cd = s3cmci_card_present, //判断卡是否存在

.enable_sdio_irq = s3cmci_enable_sdio_irq,

};

####先看最复杂的命令请求

static void s3cmci_request(struct mmc_host *mmc, struct mmc_request *mrq)

{

struct s3cmci_host *host = mmc_priv(mmc);

host->status = "mmc request"; //设置状态

host->cmd_is_stop = 0;

host->mrq = mrq; //请求结构描述

if (s3cmci_card_present(mmc) == 0) { //卡是否存在

dbg(host, dbg_err, "%s: no medium present\n", __func__);

host->mrq->cmd->error = -ENOMEDIUM;

mmc_request_done(mmc, mrq); //无卡结束

} else

s3cmci_send_request(mmc); //有卡发送

}

@继续

static void s3cmci_send_request(struct mmc_host *mmc)

{

struct s3cmci_host *host = mmc_priv(mmc);

struct mmc_request *mrq = host->mrq;

struct mmc_command *cmd = host->cmd_is_stop ? mrq->stop : mrq->cmd;

//只有stop和命令请求?

host->ccnt++;

prepare_dbgmsg(host, cmd, host->cmd_is_stop);

/* Clear command, data and fifo status registers

Fifo clear only necessary on 2440, but doesn't hurt on 2410

请求状态

*/

writel(0xFFFFFFFF, host->base + S3C2410_SDICMDSTAT);

writel(0xFFFFFFFF, host->base + S3C2410_SDIDSTA);

writel(0xFFFFFFFF, host->base + S3C2410_SDIFSTA);

if (cmd->data) {

int res = s3cmci_setup_data(host, cmd->data);

host->dcnt++;

if (res) {

dbg(host, dbg_err, "setup data error %d\n", res);

cmd->error = res;

cmd->data->error = res;

mmc_request_done(mmc, mrq);

return;

}

if (s3cmci_host_usedma(host)) //是否用dma

res = s3cmci_prepare_dma(host, cmd->data);

else

res = s3cmci_prepare_pio(host, cmd->data);

if (res) {

dbg(host, dbg_err, "data prepare error %d\n", res);

cmd->error = res;

cmd->data->error = res;

mmc_request_done(mmc, mrq);

return;

}

}

/* Send command */

s3cmci_send_command(host, cmd); //发送命令

/* Enable Interrupt */

s3cmci_enable_irq(host, true); //使能中断

}

###设置命令数据到相应寄存器

static int s3cmci_setup_data(struct s3cmci_host *host, struct mmc_data *data)

{

u32 dcon, imsk, stoptries = 3;

/* write DCON register */

if (!data) {

writel(0, host->base + S3C2410_SDIDCON);

return 0;

}

if ((data->blksz & 3) != 0) {

/* We cannot deal with unaligned blocks with more than

* one block being transferred. */

if (data->blocks > 1) {

pr_warning("%s: can't do non-word sized block transfers (blksz %d)\n", __func__, data->blksz);

return -EINVAL;

}

}

while (readl(host->base + S3C2410_SDIDSTA) &

(S3C2410_SDIDSTA_TXDATAON | S3C2410_SDIDSTA_RXDATAON)) {

dbg(host, dbg_err,

"mci_setup_data() transfer stillin progress.\n");

writel(S3C2410_SDIDCON_STOP, host->base + S3C2410_SDIDCON);

s3cmci_reset(host);

if ((stoptries--) == 0) {

dbg_dumpregs(host, "DRF");

return -EINVAL;

}

}

dcon = data->blocks & S3C2410_SDIDCON_BLKNUM_MASK;

if (s3cmci_host_usedma(host))

dcon |= S3C2410_SDIDCON_DMAEN;

if (host->bus_width == MMC_BUS_WIDTH_4)

dcon |= S3C2410_SDIDCON_WIDEBUS;

if (!(data->flags & MMC_DATA_STREAM))

dcon |= S3C2410_SDIDCON_BLOCKMODE;

if (data->flags & MMC_DATA_WRITE) {

dcon |= S3C2410_SDIDCON_TXAFTERRESP;

dcon |= S3C2410_SDIDCON_XFER_TXSTART;

}

if (data->flags & MMC_DATA_READ) {

dcon |= S3C2410_SDIDCON_RXAFTERCMD;

dcon |= S3C2410_SDIDCON_XFER_RXSTART;

}

if (host->is2440) {

dcon |= S3C2440_SDIDCON_DS_WORD;

dcon |= S3C2440_SDIDCON_DATSTART;

}

writel(dcon, host->base + S3C2410_SDIDCON);

/* write BSIZE register */

writel(data->blksz, host->base + S3C2410_SDIBSIZE);

/* add to IMASK register */

imsk = S3C2410_SDIIMSK_FIFOFAIL | S3C2410_SDIIMSK_DATACRC |

S3C2410_SDIIMSK_DATATIMEOUT | S3C2410_SDIIMSK_DATAFINISH;

enable_imask(host, imsk);

/* write TIMER register */

if (host->is2440) {

writel(0x007FFFFF, host->base + S3C2410_SDITIMER);

} else {

writel(0x0000FFFF, host->base + S3C2410_SDITIMER);

/* FIX: set slow clock to prevent timeouts on read */

if (data->flags & MMC_DATA_READ)

writel(0xFF, host->base + S3C2410_SDIPRE);

}

return 0;

}

####发送命令

static void s3cmci_send_command(struct s3cmci_host *host,

struct mmc_command *cmd)

{

u32 ccon, imsk;

imsk = S3C2410_SDIIMSK_CRCSTATUS | S3C2410_SDIIMSK_CMDTIMEOUT |

S3C2410_SDIIMSK_RESPONSEND | S3C2410_SDIIMSK_CMDSENT |

S3C2410_SDIIMSK_RESPONSECRC;

enable_imask(host, imsk);

if (cmd->data) //完成标志

host->complete_what = COMPLETION_XFERFINISH_RSPFIN;

else if (cmd->flags & MMC_RSP_PRESENT)

host->complete_what = COMPLETION_RSPFIN;

else

host->complete_what = COMPLETION_CMDSENT;

writel(cmd->arg, host->base + S3C2410_SDICMDARG);

ccon = cmd->opcode & S3C2410_SDICMDCON_INDEX;

ccon |= S3C2410_SDICMDCON_SENDERHOST | S3C2410_SDICMDCON_CMDSTART;

if (cmd->flags & MMC_RSP_PRESENT)

ccon |= S3C2410_SDICMDCON_WAITRSP;

if (cmd->flags & MMC_RSP_136)

ccon |= S3C2410_SDICMDCON_LONGRSP;

writel(ccon, host->base + S3C2410_SDICMDCON);

}

####使能中断,对接上前一篇文章的irq申请部分,

/**

* s3cmci_enable_irq - enable IRQ, after having disabled it.

* @host: The device state.

* @more: True if more IRQs are expected from transfer.

*

* Enable the main IRQ if needed after it has been disabled.

*

* The IRQ can be one of the following states:

* - disabled during IDLE

* - disabled whilst processing data

* - enabled during transfer

* - enabled whilst awaiting SDIO interrupt detection

*/

static void s3cmci_enable_irq(struct s3cmci_host *host, bool more)

{

unsigned long flags;

bool enable = false;

local_irq_save(flags);

host->irq_enabled = more;

host->irq_disabled = false;

enable = more | host->sdio_irqen;

if (host->irq_state != enable) {

host->irq_state = enable;

if (enable)

enable_irq(host->irq);

else

disable_irq(host->irq);

}

local_irq_restore(flags);

}

#####set_ios函数,

static void s3cmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)

{

struct s3cmci_host *host = mmc_priv(mmc);

u32 mci_con;

/* Set the power state */

mci_con = readl(host->base + S3C2410_SDICON);

switch (ios->power_mode) { //设置模式选择

case MMC_POWER_ON:

case MMC_POWER_UP: //打开或者增强都对应于配置gpio

s3c2410_gpio_cfgpin(S3C2410_GPE(5), S3C2410_GPE5_SDCLK);

s3c2410_gpio_cfgpin(S3C2410_GPE(6), S3C2410_GPE6_SDCMD);

s3c2410_gpio_cfgpin(S3C2410_GPE(7), S3C2410_GPE7_SDDAT0);

s3c2410_gpio_cfgpin(S3C2410_GPE(8), S3C2410_GPE8_SDDAT1);

s3c2410_gpio_cfgpin(S3C2410_GPE(9), S3C2410_GPE9_SDDAT2);

s3c2410_gpio_cfgpin(S3C2410_GPE(10), S3C2410_GPE10_SDDAT3);

if (host->pdata->set_power) //2440比较简单,没有verg对应设置

host->pdata->set_power(ios->power_mode, ios->vdd);

if (!host->is2440)

mci_con |= S3C2410_SDICON_FIFORESET;

break;

case MMC_POWER_OFF: //关闭部分

default:

gpio_direction_output(S3C2410_GPE(5), 0);

if (host->is2440)

mci_con |= S3C2440_SDICON_SDRESET;

if (host->pdata->set_power)

host->pdata->set_power(ios->power_mode, ios->vdd);

break;

}

s3cmci_set_clk(host, ios); //设置clk

/* Set CLOCK_ENABLE */

if (ios->clock)

mci_con |= S3C2410_SDICON_CLOCKTYPE;

else

mci_con &= ~S3C2410_SDICON_CLOCKTYPE;

writel(mci_con, host->base + S3C2410_SDICON);

if ((ios->power_mode == MMC_POWER_ON) ||

(ios->power_mode == MMC_POWER_UP)) {

dbg(host, dbg_conf, "running at %lukHz (requested: %ukHz).\n",

host->real_rate/1000, ios->clock/1000);

} else {

dbg(host, dbg_conf, "powered down.\n");

}

host->bus_width = ios->bus_width;

}

####设置clock函数

static void s3cmci_set_clk(struct s3cmci_host *host, struct mmc_ios *ios)

{

u32 mci_psc;

/* Set clock */

for (mci_psc = 0; mci_psc < 255; mci_psc++) {

host->real_rate = host->clk_rate / (host->clk_div*(mci_psc+1));

if (host->real_rate <= ios->clock)

break;

}

if (mci_psc > 255)

mci_psc = 255;

host->prescaler = mci_psc;

writel(host->prescaler, host->base + S3C2410_SDIPRE);

/* If requested clock is 0, real_rate will be 0, too */

if (ios->clock == 0)

host->real_rate = 0;

}

######写保护函数:

static int s3cmci_get_ro(struct mmc_host *mmc)

{

struct s3cmci_host *host = mmc_priv(mmc);

struct s3c24xx_mci_pdata *pdata = host->pdata;

int ret;

if (pdata->no_wprotect)

return 0;

ret = gpio_get_value(pdata->gpio_wprotect) ? 1 : 0;

ret ^= pdata->wprotect_invert;

return ret;

}

######探测卡函数

static int s3cmci_card_present(struct mmc_host *mmc)

{

struct s3cmci_host *host = mmc_priv(mmc);

struct s3c24xx_mci_pdata *pdata = host->pdata;

int ret;

if (pdata->no_detect) //没有探测函数

return -ENOSYS;

ret = gpio_get_value(pdata->gpio_detect) ? 0 : 1; //直接读取gpio值

return ret ^ pdata->detect_invert;

}

#####下面说一下上文遗留几个函数,

/drivers/mmc/core/core.c

void mmc_start_host(struct mmc_host *host)

{

mmc_power_off(host); //host power off

mmc_detect_change(host, 0); //detect

}

#####首先是mmc_power_off(host),


#####同文件下的

void mmc_power_off(struct mmc_host *host)

{

mmc_host_clk_hold(host); //clk可能恢复到先前的一个值,

host->ios.clock = 0; //强制设置成0?

host->ios.vdd = 0;

mmc_poweroff_notify(host);

/*

* Reset ocr mask to be the highest possible voltage supported for

* this mmc host. This value will be used at next power up.

*/

host->ocr = 1 << (fls(host->ocr_avail) - 1); //支持的最大电压



if (!mmc_host_is_spi(host)) { //不是spi

host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN; //漏极开路式

host->ios.chip_select = MMC_CS_DONTCARE; //不关心cs

}

host->ios.power_mode = MMC_POWER_OFF; //状态power_off

host->ios.bus_width = MMC_BUS_WIDTH_1;

host->ios.timing = MMC_TIMING_LEGACY;

mmc_set_ios(host); //关闭电压,真正设置成0

/*

* Some configurations, such as the 802.11 SDIO card in the OLPC

* XO-1.5, require a short delay after poweroff before the card

* can be successfully turned on again.

*/

mmc_delay(1); //短暂延迟

mmc_host_clk_release(host); //disable

}

######同文件夹下

/**

* mmc_host_clk_hold - ungate hardware MCI clocks

* @host: host to ungate.

* //gate用于限制保证有最低clk???

* Makes sure the host ios.clock is restored to a non-zero value

* past this call. Increase clock reference count and ungate clock

* if we're the first user.

*/

void mmc_host_clk_hold(struct mmc_host *host)

{

unsigned long flags;

mutex_lock(&host->clk_gate_mutex);

spin_lock_irqsave(&host->clk_lock, flags);

if (host->clk_gated) {

spin_unlock_irqrestore(&host->clk_lock, flags);

mmc_ungate_clock(host);

spin_lock_irqsave(&host->clk_lock, flags);

pr_debug("%s: ungated MCI clock\n", mmc_hostname(host));

}

host->clk_requests++;

spin_unlock_irqrestore(&host->clk_lock, flags);

mutex_unlock(&host->clk_gate_mutex);

}

########位于drivers/mmc/core/core.c

/*

* This restores the clock from gating by using the cached

* clock value.

*/

void mmc_ungate_clock(struct mmc_host *host)

{

/*

* We should previously have gated the clock, so the clock shall

* be 0 here! The clock may however be 0 during initialization,

* when some request operations are performed before setting

* the frequency. When ungate is requested in that situation

* we just ignore the call.

*/

if (host->clk_old) {

BUG_ON(host->ios.clock);

/* This call will also set host->clk_gated to false */

__mmc_set_clock(host, host->clk_old);

}

}

#####看样子gate缓存了clk值,可以恢复到上一个clk值.


#####跟进去



/*

* Sets the host clock to the highest possible frequency that

* is below "hz".

*/

static void __mmc_set_clock(struct mmc_host *host, unsigned int hz)

{

WARN_ON(hz < host->f_min);

if (hz > host->f_max)

hz = host->f_max;

host->ios.clock = hz;

mmc_set_ios(host);

}

######把clock设置成传进的值

/*

* Internal function that does the actual ios call to the host driver,

* optionally printing some debug output.

*/

static inline void mmc_set_ios(struct mmc_host *host)

{

struct mmc_ios *ios = &host->ios;

pr_debug("%s: clock %uHz busmode %u powermode %u cs %u Vdd %u "

"width %u timing %u\n",

mmc_hostname(host), ios->clock, ios->bus_mode,

ios->power_mode, ios->chip_select, ios->vdd,

ios->bus_width, ios->timing);

if (ios->clock > 0) //此时的clock如果不是0,那么clk_gate = false,

mmc_set_ungated(host); //只有0时才能再gate?

host->ops->set_ios(host, ios); //调用平台相关函数真正设置io

}



#####另外一个函数mmc_detect_change(host, 0);


#####在core.c中,

/**

* mmc_detect_change - process change of state on a MMC socket

* @host: host which changed state.

* @delay: optional delay to wait before detection (jiffies)

*

* MMC drivers should call this when they detect a card has been

* inserted or removed. The MMC layer will confirm that any

* present card is still functional, and initialize any newly

* inserted.

*/

void mmc_detect_change(struct mmc_host *host, unsigned long delay)

{

#ifdef CONFIG_MMC_DEBUG

unsigned long flags;

spin_lock_irqsave(&host->lock, flags);

WARN_ON(host->removed);

spin_unlock_irqrestore(&host->lock, flags);

#endif

mmc_schedule_delayed_work(&host->detect, delay);

}

#####这个地方对应于上面alloc_host时候的mmc_rescan,

#####注册完毕执行一次探测,确认默认是否sd卡被插入,

#####而插入卡后中断触发也会执行到这里,下面直接看插卡后isr函数函数了,


#####在s3cmci.c中,

/*

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

}

#####都走到mmc_detect_change,只不过多了个延迟,


#####继续追下去,core.c中,

/**

* mmc_detect_change - process change of state on a MMC socket

* @host: host which changed state.

* @delay: optional delay to wait before detection (jiffies)

*

* MMC drivers should call this when they detect a card has been

* inserted or removed. The MMC layer will confirm that any

* present card is still functional, and initialize any newly

* inserted.

*/

void mmc_detect_change(struct mmc_host *host, unsigned long delay)

{

#ifdef CONFIG_MMC_DEBUG

unsigned long flags;

spin_lock_irqsave(&host->lock, flags);

WARN_ON(host->removed);

spin_unlock_irqrestore(&host->lock, flags);

#endif

mmc_schedule_delayed_work(&host->detect, delay);

}

#####会调用mmc_schedule_delayed_work(&host->detect, delay);

#####host->detect这个延迟队列的初始化位于/drivers/mmc/core/host.c中,

#####INIT_DELAYED_WORK(&host->detect, mmc_rescan);

#####探测函数都会调用mmc_rescan这个系统提供的扫描sd卡函数,

#####核心扫描函数,下面就是重点的扫描卡函数了,放到下篇单独来说;


总结,概览了平台最相关的几个函数,接下来的都通用函数了,写的比较粗糙,还望见谅^.^!



Thanks
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: