分享

DW的SD卡驱动分析(三)

 dwlinux_gs 2014-08-14
    这一节我们看下探测函数tasklet中的两个函数,首先看下发现卡函数dw_mci_tasklet_card:
static void dw_mci_tasklet_card(unsigned long data)
{
    struct dw_mci *host = (struct dw_mci *)data;
    int i;
    //每个slot都执行一次,本例只有一个
    for (i = 0; i < host->num_slots; i++) {
        struct dw_mci_slot *slot = host->slot[i];
        struct mmc_host *mmc = slot->mmc;
        struct mmc_request *mrq;
        int present;
        u32 ctrl;
        present = dw_mci_get_cd(mmc);    //获取卡状态,后面讲
        while (present != slot->last_detect_state) {
            spin_lock(&host->lock);
            dev_dbg(&slot->mmc->class_dev, "card %s\n",
                present ? "inserted" : "removed");
            /* Card change detected */
            slot->last_detect_state = present;   
            /* Power up slot */
            if (present != 0) {
                if (host->pdata->setpower)
                    host->pdata->setpower(slot->id,
                                  mmc->ocr_avail);
                set_bit(DW_MMC_CARD_PRESENT, &slot->flags);    //设置卡存在标志
            }
            /* Clean up queue if present */
            mrq = slot->mrq;
            if (mrq) {
                if (mrq == host->mrq) {
                    host->data = NULL;
                    host->cmd = NULL;
                    switch (host->state) {
                    case STATE_IDLE:
                        break;
                    case STATE_SENDING_CMD:
                        mrq->cmd->error = -ENOMEDIUM;
                        if (!mrq->data)
                            break;
                        /* fall through */
                    case STATE_SENDING_DATA:
                        mrq->data->error = -ENOMEDIUM;
                        dw_mci_stop_dma(host);    //停止DMA,后面讲
                        break;
                    case STATE_DATA_BUSY:
                    case STATE_DATA_ERROR:
                        if (mrq->data->error == -EINPROGRESS)
                            mrq->data->error = -ENOMEDIUM;
                        if (!mrq->stop)
                            break;
                        /* fall through */
                    case STATE_SENDING_STOP:
                        mrq->stop->error = -ENOMEDIUM;
                        break;
                    }
                    dw_mci_request_end(host, mrq);    //停止请求,后面讲
                } else {
                    list_del(&slot->queue_node);
                    mrq->cmd->error = -ENOMEDIUM;
                    if (mrq->data)
                        mrq->data->error = -ENOMEDIUM;
                    if (mrq->stop)
                        mrq->stop->error = -ENOMEDIUM;
                    spin_unlock(&host->lock);
                    mmc_request_done(slot->mmc, mrq);
                    spin_lock(&host->lock);
                }
            }
            /* Power down slot */
            if (present == 0) {
                if (host->pdata->setpower)
                    host->pdata->setpower(slot->id, 0);
                clear_bit(DW_MMC_CARD_PRESENT, &slot->flags);
                /*
                 * Clear down the FIFO - doing so generates a
                 * block interrupt, hence setting the
                 * scatter-gather pointer to NULL.
                 */
                host->sg = NULL;
                ctrl = mci_readl(host, CTRL);
                ctrl |= SDMMC_CTRL_FIFO_RESET;
                mci_writel(host, CTRL, ctrl);    //复位fifo
#ifdef CONFIG_MMC_DW_IDMAC
                ctrl = mci_readl(host, BMOD);
                ctrl |= 0x01; /* Software reset of DMA */
                mci_writel(host, BMOD, ctrl);
#endif
            }
            spin_unlock(&host->lock);
            present = dw_mci_get_cd(mmc);
        }
        mmc_detect_change(slot->mmc,
            msecs_to_jiffies(host->pdata->detect_delay_ms));
    }
}
获取卡状态
static int dw_mci_get_cd(struct mmc_host *mmc)
{
    int present;
    struct dw_mci_slot *slot = mmc_priv(mmc);
    struct dw_mci_board *brd = slot->host->pdata;
    /* Use platform get_cd function, else try onboard card detect */
    if (brd->quirks & DW_MCI_QUIRK_BROKEN_CARD_DETECTION)
        present = 1;
    else if (brd->get_cd)
        present = !brd->get_cd(slot->id);
    else
        present = (mci_readl(slot->host, CDETECT) & (1 << slot->id))
            == 0 ? 1 : 0;    //读取寄存器获取卡状态
    if (present)
        dev_dbg(&mmc->class_dev, "card is present\n");
    else
        dev_dbg(&mmc->class_dev, "card is not present\n");
    return present;
}
停止DMA
/* DMA interface functions */
static void dw_mci_stop_dma(struct dw_mci *host)
{
    //执行if,因为我们在内部DMA初始化时,已经将host->use_dma置1了
    if (host->use_dma) {
        host->dma_ops->stop(host);
        host->dma_ops->cleanup(host);
    } else {
        /* Data transfer was stopped by the interrupt handler */
        set_bit(EVENT_XFER_COMPLETE, &host->pending_events);
    }
}
停止请求
static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq)
    __releases(&host->lock)
    __acquires(&host->lock)
{
    struct dw_mci_slot *slot;
    struct mmc_host    *prev_mmc = host->cur_slot->mmc;
    WARN_ON(host->cmd || host->data);
    host->cur_slot->mrq = NULL;
    host->mrq = NULL;
    if (!list_empty(&host->queue)) {
        slot = list_entry(host->queue.next,
                  struct dw_mci_slot, queue_node);
        list_del(&slot->queue_node);
        dev_vdbg(&host->pdev->dev, "list not empty: %s is next\n",
             mmc_hostname(slot->mmc));
        host->state = STATE_SENDING_CMD;
        dw_mci_start_request(host, slot);    //在请求中讲述    
    } else {
        dev_vdbg(&host->pdev->dev, "list empty\n");
        host->state = STATE_IDLE;
    }
    spin_unlock(&host->lock);
    mmc_request_done(prev_mmc, mrq);
    spin_lock(&host->lock);
}

    接下来讲述tasklet另外一个函数dw_mci_tasklet_func:
static void dw_mci_tasklet_func(unsigned long priv)
{
    struct dw_mci *host = (struct dw_mci *)priv;
    struct mmc_data    *data;
    struct mmc_command *cmd;
    enum dw_mci_state state;
    enum dw_mci_state prev_state;
    u32 status;
    spin_lock(&host->lock);
    state = host->state;
    data = host->data;
    do {
        prev_state = state;
        //host->pending_events的值都是在中断中被置的
        switch (state) {
        case STATE_IDLE:
            break;
        case STATE_SENDING_CMD:
            if (!test_and_clear_bit(EVENT_CMD_COMPLETE,
                        &host->pending_events))
                break;
            cmd = host->cmd;
            host->cmd = NULL;
            set_bit(EVENT_CMD_COMPLETE, &host->completed_events);
            dw_mci_command_complete(host, host->mrq->cmd);    //命令完成,后面讲
            if (!host->mrq->data || cmd->error) {
                dw_mci_request_end(host, host->mrq);   
                goto unlock;
            }
            prev_state = state = STATE_SENDING_DATA;
            /* fall through */
        case STATE_SENDING_DATA:
            if (test_and_clear_bit(EVENT_DATA_ERROR,
                           &host->pending_events)) {
                dw_mci_stop_dma(host);
                if (data->stop)
                    send_stop_cmd(host, data);    //发送结束命令,后面讲述
                state = STATE_DATA_ERROR;
                break;
            }
            if (!test_and_clear_bit(EVENT_XFER_COMPLETE,
                        &host->pending_events))
                break;
            set_bit(EVENT_XFER_COMPLETE, &host->completed_events);
            prev_state = state = STATE_DATA_BUSY;
            /* fall through */
        case STATE_DATA_BUSY:
            if (!test_and_clear_bit(EVENT_DATA_COMPLETE,
                        &host->pending_events))
                break;
            host->data = NULL;
            set_bit(EVENT_DATA_COMPLETE, &host->completed_events);
            status = host->data_status;
            if (status & DW_MCI_DATA_ERROR_FLAGS) {
                if (status & SDMMC_INT_DTO) {
                    dev_err(&host->pdev->dev,
                        "data timeout error\n");
                    data->error = -ETIMEDOUT;
                } else if (status & SDMMC_INT_DCRC) {
                    dev_err(&host->pdev->dev,
                        "data CRC error\n");
                    data->error = -EILSEQ;
                } else {
                    dev_err(&host->pdev->dev,
                        "data FIFO error "
                        "(status=%08x)\n",
                        status);
                    data->error = -EIO;
                }
            } else {
                data->bytes_xfered = data->blocks * data->blksz;
                data->error = 0;
            }
            if (!data->stop) {
                dw_mci_request_end(host, host->mrq);
                goto unlock;
            }
            prev_state = state = STATE_SENDING_STOP;
            if (!data->error)
                send_stop_cmd(host, data);
            /* fall through */
        case STATE_SENDING_STOP:
            if (!test_and_clear_bit(EVENT_CMD_COMPLETE,
                        &host->pending_events))
                break;
            host->cmd = NULL;
            dw_mci_command_complete(host, host->mrq->stop);
            dw_mci_request_end(host, host->mrq);
            goto unlock;
        case STATE_DATA_ERROR:
            if (!test_and_clear_bit(EVENT_XFER_COMPLETE,
                        &host->pending_events))
                break;
            state = STATE_DATA_BUSY;
            break;
        }
    } while (state != prev_state);
    host->state = state;
unlock:
    spin_unlock(&host->lock);
}
    命令完成
static void dw_mci_command_complete(struct dw_mci *host, struct mmc_command *cmd)
{
    u32 status = host->cmd_status;
    host->cmd_status = 0;
    /* Read the response from the card (up to 16 bytes) */
    if (cmd->flags & MMC_RSP_PRESENT) {
        if (cmd->flags & MMC_RSP_136) {
            cmd->resp[3] = mci_readl(host, RESP0);
            cmd->resp[2] = mci_readl(host, RESP1);
            cmd->resp[1] = mci_readl(host, RESP2);
            cmd->resp[0] = mci_readl(host, RESP3);
        } else {
            cmd->resp[0] = mci_readl(host, RESP0);
            cmd->resp[1] = 0;
            cmd->resp[2] = 0;
            cmd->resp[3] = 0;
        }
    }
    if (status & SDMMC_INT_RTO)
        cmd->error = -ETIMEDOUT;
    else if ((cmd->flags & MMC_RSP_CRC) && (status & SDMMC_INT_RCRC))
        cmd->error = -EILSEQ;
    else if (status & SDMMC_INT_RESP_ERR)
        cmd->error = -EIO;
    else
        cmd->error = 0;
    if (cmd->error) {
        /* newer ip versions need a delay between retries */
        if (host->quirks & DW_MCI_QUIRK_RETRY_DELAY)
            mdelay(20);
        if (cmd->data) {
            host->data = NULL;
            dw_mci_stop_dma(host);
        }
    }
}
    发送结束命令
static void send_stop_cmd(struct dw_mci *host, struct mmc_data *data)
{
    dw_mci_start_command(host, data->stop, host->stop_cmdr);
}
static void dw_mci_start_command(struct dw_mci *host,
                 struct mmc_command *cmd, u32 cmd_flags)
{
    host->cmd = cmd;
    dev_vdbg(&host->pdev->dev,
         "start command: ARGR=0x%08x CMDR=0x%08x\n",
         cmd->arg, cmd_flags);
    mci_writel(host, CMDARG, cmd->arg);
    wmb();
    if (cmd->opcode == SD_APP_SEND_SCR)
        mci_writel(host, CMD, cmd_flags | SDMMC_CMD_START | SDMMC_CMD_SEND_STOP);
    else
        mci_writel(host, CMD, cmd_flags | SDMMC_CMD_START);
}

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多