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


继续上一篇文章(见  ),先看一个重点结构,平台相关,真正对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;
}
  • 1
  • 2
  • 3
  • 4
  • 下一页

相关内容