Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1190053
  • 博文数量: 221
  • 博客积分: 10
  • 博客等级: 民兵
  • 技术积分: 2139
  • 用 户 组: 普通用户
  • 注册时间: 2012-11-27 19:53
个人简介

JustForFun

文章分类

全部博文(221)

文章存档

2024年(6)

2023年(8)

2022年(2)

2021年(2)

2020年(29)

2019年(11)

2018年(23)

2017年(41)

2016年(76)

2015年(23)

我的朋友
最近访客

分类: LINUX

2016-11-07 14:54:05

 图片
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

图片
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
图片
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

//扫描总线上设备mmc设备
void mmc_rescan(struct work_struct *work)
{
    struct mmc_host *host =
        container_of(work, struct mmc_host, detect.work);
    u32 ocr;
    int err;

    mmc_bus_get(host);
//如果为第一次扫描总线上设备
    if (host->bus_ops == NULL) {
        /*
         * Only we can add a new handler, so it's safe to
         * release the lock here.
         */
        mmc_bus_put(host);

        if (host->ops->get_cd && host->ops->get_cd(host) == 0)
            goto out;
/* 驱动中使用mmc_claim_host(host);来得知,当前mmc控制器是否被占用,当前mmc控制器如果被占用,那么 host->claimed = 1;否则为0,如果为1,那么会在for(;;)循环中调用schedule切换出自己,当占用mmc控制器的操作完成之后,执行 mmc_release_host()的时候,会激活登记到等待队列&host->wq中的其他程序获得mmc主控制器的物理使用权*/
//这个函数和mmc_release_host(host);配对使用,相当于一把锁,就是在同一个时间只有一个sd卡可以保持和主控制器通讯;
        mmc_claim_host(host);
//
//最开始,卡是power_off
//
开启host电源
        mmc_power_up(host);
设置host为idle模式
        mmc_go_idle(host);
///是用于验证SD卡接口操作状态的有效性命令(CMD8)。
//如果 SD_SEND_IF_COND指示为符合SD2.0标准的卡,
//则设置操作状态寄存器ocrbit30指示能 够处理块地址SDHC卡
//在SD子系统中,所有的数据传输均是由host发起。
//Host发送完一命令则会等待中断的产生,在这里采用完成量来实现进程的阻塞。
        mmc_send_if_cond(host, host->ocr_avail);

        /*
         * First we search for SDIO...
         */
//判断是否是SDIO协议卡
//通过发送命令CMD5
//CMD5协议与SD接口中的ACMD41类似,用于检查是否支持SDIO的电压。
//CMD5的回应中,MP为0,表示是SDIO卡;如果MP为1,表示不但是SDIO卡,并且是SD卡。

        err = mmc_send_io_op_cond(host, 0, &ocr);
        if (!err) {
            if (mmc_attach_sdio(host, ocr))
                mmc_power_off(host);
            goto out;
        }

        /*
         * ...then normal SD...
         */
//判断是否是SD协议卡
//ocr 是指 card 內部的 Operation Condition Register (OCR) 讀出來的值
//發送 CMD41 CMD55 讀取 OCR 的值
//#define SD_APP_OP_COND           41   /* bcr  [31:0] OCR         R3  */
        err = mmc_send_app_op_cond(host, 0, &ocr);//本函数后面有详解
        if (!err) {
//装载 绑定 SD卡设备
            if (mmc_attach_sd(host, ocr))
                mmc_power_off(host);
            goto out;
        }

        /*
         * ...and finally MMC.
         */
//判断是否是MMC协议卡
        err = mmc_send_op_cond(host, 0, &ocr);
        if (!err) {
            if (mmc_attach_mmc(host, ocr))
                mmc_power_off(host);
            goto out;
        }
////释放卡对主机的持有权
        mmc_release_host(host);
        mmc_power_off(host);
    } else {
        if (host->bus_ops->detect && !host->bus_dead)
            host->bus_ops->detect(host);

        mmc_bus_put(host);
    }
out:
    if (host->caps & MMC_CAP_NEEDS_POLL)
        mmc_schedule_delayed_work(&host->detect, HZ);
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/**
 *    mmc_wait_for_cmd - start a command and wait for completion
 *    @host: MMC host to start command
 *    @cmd: MMC command to start
 *    @retries: maximum number of retries
 *
 *    Start a new MMC command for a host, and wait for the command
 *    to complete.  Return any error that occurred while the command
 *    was executing.  Do not attempt to parse the response.
 */
//这个是SDIO卡的
int mmc_wait_for_cmd(struct mmc_host *host, struct mmc_command *cmd, int retries)
{
    struct mmc_request mrq;
    cmd->retries = retries;
    mrq.cmd = cmd;
    cmd->data = NULL;
    mmc_wait_for_req(host, &mrq);
    return cmd->error;
}
////////////////////////////////////////////////////////////////////////////////////

/**
 *    mmc_wait_for_app_cmd - start an application command and wait for
                    completion
 *    @host: MMC host to start command
 *    @card: Card to send MMC_APP_CMD to
 *    @cmd: MMC command to start
 *    @retries: maximum number of retries
 *
 *    Sends a MMC_APP_CMD, checks the card response, sends the command
 *    in the parameter and waits for it to complete. Return any error
 *    that occurred while the command was executing.  Do not attempt to
 *    parse the response.
 */
//这个是SD协议卡的
int mmc_wait_for_app_cmd(struct mmc_host *host, struct mmc_card *card,
    struct mmc_command *cmd, int retries)
{
    struct mmc_request mrq;

    int i, err;

    BUG_ON(!cmd);
    BUG_ON(retries < 0);

    err = -EIO;

    /*
     * We have to resend MMC_APP_CMD for each attempt so
     * we cannot use the retries field in mmc_command.
     */
    for (i = 0;i <= retries;i++) {
        memset(&mrq, 0, sizeof(struct mmc_request));

        err = mmc_app_cmd(host, card);
        if (err) {
            /* no point in retrying; no APP commands allowed */
            if (mmc_host_is_spi(host)) {
                if (cmd->resp[0] & R1_SPI_ILLEGAL_COMMAND)
                    break;
            }
            continue;
        }

        memset(&mrq, 0, sizeof(struct mmc_request));

        memset(cmd->resp, 0, sizeof(cmd->resp));
        cmd->retries = 0;

        mrq.cmd = cmd;
        cmd->data = NULL;

        mmc_wait_for_req(host, &mrq);

        err = cmd->error;
        if (!cmd->error)
            break;

        /* no point in retrying illegal APP commands */
        if (mmc_host_is_spi(host)) {
            if (cmd->resp[0] & R1_SPI_ILLEGAL_COMMAND)
                break;
        }
    }

    return err;
}
/////////////////////////////////////////////////////////////////////////////////

//判断是否是SD协议卡
int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
{
    struct mmc_command cmd;
    int i, err = 0;

    BUG_ON(!host);

    memset(&cmd, 0, sizeof(struct mmc_command));
//判断是否是SD协议卡
//ocr 是指 card 內部的 Operation Condition Register (OCR) 讀出來的值
//發送 CMD41 CMD55 讀取 OCR 的值
//#define SD_APP_OP_COND           41   /* bcr  [31:0] OCR         R3  */
//见SD卡手册

图片

图片

图片

图片



    cmd.opcode = SD_APP_OP_COND;
    if (mmc_host_is_spi(host))
        cmd.arg = ocr & (1 << 30); /* SPI only defines one bit */
    else
        cmd.arg = ocr;
    cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R3 | MMC_CMD_BCR;

    for (i = 100; i; i--) {
//MMC_CMD_RETRIES 重复的次数?
        err = mmc_wait_for_app_cmd(host, NULL, &cmd, MMC_CMD_RETRIES);
        if (err)
            break;

        /* if we're just probing, do a single pass */
        if (ocr == 0)
            break;

        /* otherwise wait until reset completes */
        if (mmc_host_is_spi(host)) {
            if (!(cmd.resp[0] & R1_SPI_IDLE))
                break;
        } else {
            if (cmd.resp[0] & MMC_CARD_BUSY)
                break;
        }

        err = -ETIMEDOUT;

        mmc_delay(10);
    }

    if (rocr && !mmc_host_is_spi(host))
        *rocr = cmd.resp[0];

    return err;
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


/**
 *    mmc_wait_for_req - start a request and wait for completion
 *    @host: MMC host to start command
 *    @mrq: MMC request to start
 *
 *    Start a new MMC custom command request for a host, and wait
 *    for the command to complete. Does not attempt to parse the
 *    response.
 */
void mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq)
{
    DECLARE_COMPLETION_ONSTACK(complete);

    mrq->done_data = &complete;
    mrq->done = mmc_wait_done;
//开始request
//mmc_start_request->host->ops->request(即sdhci_request)
    mmc_start_request(host, mrq);
//这里采用完成量来实现进程的阻塞。
    wait_for_completion(&complete);
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

mmc_send_if_cond实现,从字面意思上看该函数就是发送一个SD标准命令,亦如usb的标准命令一样。

在这里不得不先说明一点就是在SD子系统中,所有的数据传输均是由host发起。Host发送完一命令则会等待中断的产生,在这里采用完成量来实现进程的阻塞。

int mmc_send_if_cond(struct mmc_host *host, u32 ocr)
{
    struct mmc_command cmd;
    int err;
    static const u8 test_pattern = 0xAA;
    u8 result_pattern;

    /*
     * To support SD 2.0 cards, we must always invoke SD_SEND_IF_COND
     * before SD_APP_OP_COND. This command will harmlessly fail for
     * SD 1.0 cards.
     */
    cmd.opcode = SD_SEND_IF_COND;
    cmd.arg = ((ocr & 0xFF8000) != 0) << 8 | test_pattern;
    cmd.flags = MMC_RSP_SPI_R7 | MMC_RSP_R7 | MMC_CMD_BCR;
//发送指令,并等待complete 完成量完成
    err = mmc_wait_for_cmd(host, &cmd, 0);
    if (err)
        return err;

    if (mmc_host_is_spi(host))
        result_pattern = cmd.resp[1] & 0xFF;
    else
        result_pattern = cmd.resp[0] & 0xFF;

    if (result_pattern != test_pattern)
        return -EIO;

    return 0;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/*
 * Starting point for SD card init.
 */
int mmc_attach_sd(struct mmc_host *host, u32 ocr)
{
    int err;

    BUG_ON(!host);
    WARN_ON(!host->claimed);

    mmc_attach_bus(host, &mmc_sd_ops);

    /*
     * We need to get OCR a different way for SPI.
     */
    if (mmc_host_is_spi(host)) {
        mmc_go_idle(host);

        err = mmc_spi_read_ocr(host, 0, &ocr);
        if (err)
            goto err;
    }

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

    if (ocr & MMC_VDD_165_195) {
        printk(KERN_WARNING "%s: SD card claims to support the "
               "incompletely defined 'low voltage range'. This "
               "will be ignored.\n", mmc_hostname(host));
        ocr &= ~MMC_VDD_165_195;
    }

    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_sd_init_card(host, host->ocr, NULL);
    if (err)
        goto err;

    mmc_release_host(host);

    err = mmc_add_card(host->card);
    if (err)
        goto remove_card;

    return 0;

remove_card:
    mmc_remove_card(host->card);
    host->card = NULL;
    mmc_claim_host(host);
err:
    mmc_detach_bus(host);
    mmc_release_host(host);

    printk(KERN_ERR "%s: error %d whilst initialising SD card\n",
        mmc_hostname(host), err);

    return err;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/*
 * Starting point for MMC card init.
 */
int mmc_attach_mmc(struct mmc_host *host, u32 ocr)
{
    int err;

    BUG_ON(!host);
    WARN_ON(!host->claimed);

    mmc_attach_bus(host, &mmc_ops);

    /*
     * We need to get OCR a different way for SPI.
     */
    if (mmc_host_is_spi(host)) {
        err = mmc_spi_read_ocr(host, 1, &ocr);
        if (err)
            goto err;
    }

    /*
     * 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 of the card?
     */
    if (!host->ocr) {
        err = -EINVAL;
        goto err;
    }

    /*
     * Detect and init the card.
     */
    err = mmc_init_card(host, host->ocr, NULL);
    if (err)
        goto err;

    mmc_release_host(host);

    err = mmc_add_card(host->card);
    if (err)
        goto remove_card;

    return 0;

remove_card:
    mmc_remove_card(host->card);
    host->card = NULL;
    mmc_claim_host(host);
err:
    mmc_detach_bus(host);
    mmc_release_host(host);

    printk(KERN_ERR "%s: error %d whilst initialising MMC card\n",
        mmc_hostname(host), err);

    return err;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/*
 * Starting point for SDIO card init.
 */
int mmc_attach_sdio(struct mmc_host *host, u32 ocr)
{
    int err;
    int i, funcs;
    struct mmc_card *card;

    BUG_ON(!host);
    WARN_ON(!host->claimed);

    mmc_attach_bus(host, &mmc_sdio_ops);

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

    if (ocr & MMC_VDD_165_195) {
        printk(KERN_WARNING "%s: SDIO card claims to support the "
               "incompletely defined 'low voltage range'. This "
               "will be ignored.\n", mmc_hostname(host));
        ocr &= ~MMC_VDD_165_195;
    }

    host->ocr = mmc_select_voltage(host, ocr);

    /*
     * Can we support the voltage(s) of the card(s)?
     */
    if (!host->ocr) {
        err = -EINVAL;
        goto err;
    }

    /*
     * Inform the card of the voltage
     */
    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;
    }

    /*
     * The number of functions on the card is encoded inside
     * the ocr.
     */
    funcs = (ocr & 0x70000000) >> 28;

    /*
     * Allocate card structure.
     */
    card = mmc_alloc_card(host, NULL);
    if (IS_ERR(card)) {
        err = PTR_ERR(card);
        goto err;
    }

    card->type = MMC_TYPE_SDIO;
    card->sdio_funcs = funcs;

    host->card = card;

    /*
     * For native busses:  set card RCA and quit open drain mode.
     */
    if (!mmc_host_is_spi(host)) {
        err = mmc_send_relative_addr(host, &card->rca);
        if (err)
            goto remove;

        mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
    }

    /*
     * Select card, as all following commands rely on that.
     */
    if (!mmc_host_is_spi(host)) {
        err = mmc_select_card(card);
        if (err)
            goto remove;
    }

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

    /*
     * Switch to high-speed (if supported).
     */
    err = sdio_enable_hs(card);
    if (err)
        goto remove;

    /*
     * Change to the card's maximum speed.
     */
    if (mmc_card_highspeed(card)) {
        /*
         * The SDIO specification doesn't mention how
         * the CIS transfer speed register relates to
         * high-speed, but it seems that 50 MHz is
         * mandatory.
         */
        mmc_set_clock(host, 50000000);
    } else {
        mmc_set_clock(host, card->cis.max_dtr);
    }

    /*
     * Switch to wider bus (if supported).
     */
    err = sdio_enable_wide(card);
    if (err)
        goto remove;

    /*
     * Initialize (but don't add) all present functions.
     */
    for (i = 0;i < funcs;i++) {
        err = sdio_init_func(host->card, i + 1);
        if (err)
            goto remove;
    }

    mmc_release_host(host);

    /*
     * First add the card to the driver model...
     */
    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;
    }

    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. */
    if (host->card)
        mmc_sdio_remove(host);
err:
    mmc_detach_bus(host);
    mmc_release_host(host);

    printk(KERN_ERR "%s: error %d whilst initialising SDIO card\n",
        mmc_hostname(host), err);

    return err;
}

观察传递的参数,是mrq。可以知道mmc最终命令的承载都是用struct mmc_request *mrq 这样的结构完成
static void
mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
{
    if (mrq->data) {
            mmc_hostname(host), mrq->data->blksz,
            mrq->data->blocks, mrq->data->flags,
            mrq->data->timeout_ns / 1000000,
            mrq->data->timeout_clks);
    }

    if (mrq->stop) {
        pr_debug("%s:     CMD%u arg %08x flags %08x\n",
             mmc_hostname(host), mrq->stop->opcode,
             mrq->stop->arg, mrq->stop->flags);
    }

    led_trigger_event(host->led, LED_FULL);

    mrq->cmd->error = 0;
    mrq->cmd->mrq = mrq;
    if (mrq->data) {
            host->max_req_size);

        mrq->cmd->data = mrq->data;
        mrq->data->error = 0;
        mrq->data->mrq = mrq;
        if (mrq->stop) {
            mrq->data->stop = mrq->stop;
            mrq->stop->error = 0;
            mrq->stop->mrq = mrq;
        }
    }
////mmc_start_request->host->ops->request(即sdhci_request)
///mmc_start_request要交给各个host完成处理了。Mmc驱动是一个通用框架驱动,
//不同的host对应的命令处理必定有所差别。针对sdhci标准的host mmc驱动。
//host->ops->request(host, mrq);的执行将交给,sdhci_request()完成。
    host->ops->request(host, mrq);
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


 
static const struct mmc_host_ops sdhci_ops = {
//
request函数指针指向的函数用来处理host向从设备发送命令的请求,
 
    .request    = sdhci_request,
//
set_ios用来设置电源、时钟等等之类(需要重点关注),   
    .set_ios    = sdhci_set_ios,
//
get_ro用来判断是否写保护 
    .get_ro        = sdhci_get_ro,
//使能SD/MMC IRQ中断
    .enable_sdio_irq = sdhci_enable_sdio_irq,
};
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

//static const struct mmc_host_ops sdhci_ops = {
//    .request    = sdhci_request,
//request函数指针指向的函数用来处理host向从设备发送命令的请求,
/*****************************************************************************\
 *                                                                           *
 * MMC callbacks                                                             *
 *                                                                           *
\*****************************************************************************/

static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
{
//注意函数一进来,host结构体发生变化,已经不再是mmc_host结构,
//而是各具体的厂商的host,如这里的struct sdhci_host *host;
//其实是host = mmc_priv(mmc);这么的得来的。
    struct sdhci_host *host;
    unsigned long flags;

    host = mmc_priv(mmc);

    spin_lock_irqsave(&host->lock, flags);
#ifndef CONFIG_LEDS_CLASS
    sdhci_activate_led(host);
#endif
//保存起mrq结构
    host->mrq = mrq;

    if ((mmc->caps & MMC_CAP_ON_BOARD) || (host->flags & SDHCI_DEVICE_ALIVE))
//当命令传输完成系统调用中断处理函数sdhci_irq。在其中进行中断完成后的处理???。
//sdhci_add_host 中
// tasklet_init(&host->finish_tasklet, sdhci_tasklet_finish, (unsigned long)host);
        sdhci_send_command(host, mrq->cmd);//发送命令
    else {
        if (!(readl(host->ioaddr + SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT)
            || (host->flags & SDHCI_DEVICE_DEAD)) {
            host->mrq->cmd->error = -ENOMEDIUM;
            tasklet_schedule(&host->finish_tasklet);
        } else
            sdhci_send_command(host, mrq->cmd);
    }
//为了保证编译器顺序编译,防止编译器优化打乱执行顺序
    mmiowb();
    spin_unlock_irqrestore(&host->lock, flags);
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
{
    int flags;
    u32 mask;
    unsigned long timeout;
    /* Wait max 10 ms */
    timeout = 10;
//SDHCI_PRESENT_STATE 寄存器的第0位
图片
    mask = SDHCI_CMD_INHIBIT;
//SDHCI_PRESENT_STATE 寄存器的第1位
图片
    if ((cmd->data != NULL) || (cmd->flags & MMC_RSP_BUSY))
        mask |= SDHCI_DATA_INHIBIT;

    /* We shouldn't wait for data inihibit for stop commands, even
       though they might use busy signaling */
    if (host->mrq->data && (cmd == host->mrq->data->stop))
        mask &= ~SDHCI_DATA_INHIBIT;


//
//#define SDHCI_PRESENT_STATE    0x24
//读一个长整形数据(8字节)当前状态寄存器
    while (readl(host->ioaddr + SDHCI_PRESENT_STATE) & mask) {
        if (timeout == 0) {
            printk(KERN_ERR "%s: Controller never released "
                "inhibit bit(s).\n", mmc_hostname(host->mmc));
            sdhci_dumpregs(host);
            cmd->error = -EIO;
//
//int sdhci_add_host(struct sdhci_host *host)
// tasklet_init(&host->finish_tasklet,  sdhci_tasklet_finish, (unsigned long)host);
////finish_tasklet用于命令传输完成后的处理 
            tasklet_schedule(&host->finish_tasklet);
            return;
        }
        timeout--;
        mdelay(1);
    }
//改变定时器时间,设置定时时间
    mod_timer(&host->timer, jiffies + 10 * HZ);
//命令保存到host起来
    host->cmd = cmd;
//准备数据
//里面的关键就是准备DMA,dma_map_sg(),
//从data->sg里面获取到信息,填充到DMA控制器里面。
    sdhci_prepare_data(host, cmd->data);
////
//#define SDHCI_ARGUMENT        0x08
//SD命令参数寄存器
图片
    writel(cmd->arg, host->ioaddr + SDHCI_ARGUMENT);

    sdhci_set_transfer_mode(host, cmd->data);

    if ((cmd->flags & MMC_RSP_136) && (cmd->flags & MMC_RSP_BUSY)) {
        printk(KERN_ERR "%s: Unsupported response type!\n",
            mmc_hostname(host->mmc));
        cmd->error = -EINVAL;
//启动finish_tasklet完成后面的事情。finish_tasklet的定义是sdhci_tasklet_finish
        tasklet_schedule(&host->finish_tasklet);
        return;
    }

    if (!(cmd->flags & MMC_RSP_PRESENT))
        flags = SDHCI_CMD_RESP_NONE;
    else if (cmd->flags & MMC_RSP_136)
        flags = SDHCI_CMD_RESP_LONG;
    else if (cmd->flags & MMC_RSP_BUSY)
        flags = SDHCI_CMD_RESP_SHORT_BUSY;
    else
        flags = SDHCI_CMD_RESP_SHORT;

    if (cmd->flags & MMC_RSP_CRC)
        flags |= SDHCI_CMD_CRC;
    if (cmd->flags & MMC_RSP_OPCODE)
        flags |= SDHCI_CMD_INDEX;
    if (cmd->data)
        flags |= SDHCI_CMD_DATA;
//数据发送,这才是真正的控制host发送命令的操作,到这类,mmc控制器才开始跟sd卡做交互
//#define SDHCI_COMMAND        0x0E
图片
    writew(SDHCI_MAKE_CMD(cmd->opcode, flags),
        host->ioaddr + SDHCI_COMMAND);

}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 





阅读(5373) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~