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

来源:互联网 发布:美国东密西根大学知乎 编辑:程序博客网 时间:2024/05/30 13:41
-----------------------------------------------------------
本文系本站原创,欢迎转载!
转载请注明出处: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
原创粉丝点击