Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1680900
  • 博文数量: 584
  • 博客积分: 13857
  • 博客等级: 上将
  • 技术积分: 11883
  • 用 户 组: 普通用户
  • 注册时间: 2009-12-16 09:34

分类: LINUX

2010-08-19 15:12:10

8. s3cmci_irq SDI的中断服务功能。我们从第6小节中对MMC/SD各种请求处理的代码中和(一)中“命令、数据发送流程图”中可以看出,在这个中断服务中将要处理很 多请求相关的事情。但对于中断服务来说,这样会严重影响系统的性能,所以这正是为什么要在驱动中实现中断的底半部机制。下面看代码进行分析。

//MMC/SD卡中断服务程序
static irqreturn_t s3cmci_irq(int irq, void *dev_id)
{
    //dev_id参数是申请中断的时候传递过来的s3cmci_host结构 体,void类型的指针可以存放任何的数据类型
    struct s3cmci_host *host = dev_id;
    struct mmc_command *cmd;
    u32 mci_csta, mci_dsta, mci_fsta, mci_dcnt, mci_imsk;
    u32 mci_cclear, mci_dclear;
    unsigned long iflags;

    //关中断并保持状态字
    spin_lock_irqsave(&host->complete_lock, iflags);

    //分别读命令状态、数据状态、数据保留计数器、FIFO状态、中断屏蔽寄存器的 值
    mci_csta = readl(host->base + S3C2410_SDICMDSTAT);
    mci_dsta = readl(host->base + S3C2410_SDIDSTA);
    mci_dcnt = readl(host->base + S3C2410_SDIDCNT);
    mci_fsta = readl(host->base + S3C2410_SDIFSTA);
    mci_imsk = readl(host->base + host->sdiimsk);
    mci_cclear = 0;
    mci_dclear = 0;

    //如果当前没有请求状态或者请求已经完成了,则恢复中断什么都不做
    if ((host->complete_what == COMPLETION_NONE) || (host->complete_what == COMPLETION_FINALIZE))
    {
        host->status = "nothing to complete";
        clear_imask(host);
        goto irq_out;
    }

    //如果核心层无MMC/SD请求,则恢复中断什么都不做
    if (!host->mrq)
    {
        host->status = "no active mrq";
        clear_imask(host);
        goto irq_out;
    }

    //获取当前发送命令有无完成
    cmd = host->cmd_is_stop ? host->mrq->stop : host->mrq->cmd;

    // 如果发送命令完成了,则恢复中断什么都不做
    if (!cmd)
    {
        host->status = "no active cmd";
        clear_imask(host);
        goto irq_out;
    }

    //判断在数据传输状态时使用的传输方式
    if (!host->dodma)
    {
        //不是DMA传输。如果是FIFO写,则切换到底半部去进行FIFO的写操作
        if ((host->pio_active == XFER_WRITE) && (mci_fsta & S3C2410_SDIFSTA_TFDET))
        {
            disable_imask(host, S3C2410_SDIIMSK_TXFIFOHALF);
            tasklet_schedule(&host->pio_tasklet);
            host->status = "pio tx";
        }

        //如果是FIFO读,则切换到底半部去进行FIFO的读操作
        if ((host->pio_active == XFER_READ) && (mci_fsta & S3C2410_SDIFSTA_RFDET))
        {
            disable_imask(host, S3C2410_SDIIMSK_RXFIFOHALF | S3C2410_SDIIMSK_RXFIFOLAST);
            tasklet_schedule(&host->pio_tasklet);
            host->status = "pio rx";
        }
    }

    //命令响应超时
    if (mci_csta & S3C2410_SDICMDSTAT_CMDTIMEOUT)
    {
        dbg(host, dbg_err, "CMDSTAT: error CMDTIMEOUT\n");
        cmd->error = -ETIMEDOUT;
        host->status = "error: command timeout";
        goto fail_transfer;
    }

    //命令发送结束
    if (mci_csta & S3C2410_SDICMDSTAT_CMDSENT)
    {
        if (host->complete_what == COMPLETION_CMDSENT)
        {
            host->status = "ok: command sent";
            goto close_transfer;
        }

        mci_cclear |= S3C2410_SDICMDSTAT_CMDSENT;
    }

    //收到命令响应,CRC校验失败
    if (mci_csta & S3C2410_SDICMDSTAT_CRCFAIL)
    {
        if (cmd->flags & MMC_RSP_CRC)
        {
            if (host->mrq->cmd->flags & MMC_RSP_136)
            {
                dbg(host, dbg_irq, "fixup: ignore CRC fail with long rsp\n");
            } else {
                
/* note, we used to fail the transfer
                 * here, but it seems that this is just
                 * the hardware getting it wrong.
                 *
                 * cmd->error = -EILSEQ;
                 * host->status = "error: bad command crc";
                 * goto fail_transfer;
                */

            }
        }

        mci_cclear |= S3C2410_SDICMDSTAT_CRCFAIL;
    }

    //收到命令响应,响应结束
    if (mci_csta & S3C2410_SDICMDSTAT_RSPFIN)
    {
        //如果当前任务是完成,接收命令响应
        if (host->complete_what == COMPLETION_RSPFIN)
        {
            host->status = "ok: command response received";
            goto close_transfer;
//停止传输

        }
        
        //当前任务是完成数据传输和接收命令响应
        if (host->complete_what == COMPLETION_XFERFINISH_RSPFIN)
            // 标记当前任务为完成数据传输
            host->complete_what = COMPLETION_XFERFINISH;

        //清除收到命令响应标志
        mci_cclear |= S3C2410_SDICMDSTAT_RSPFIN;
    }

    if (!cmd->data)
        goto clear_status_bits;

    //FIFO失败
    if (mci_fsta & S3C2440_SDIFSTA_FIFOFAIL)
    {
        dbg(host, dbg_err, "FIFO failure\n");
        host->mrq->data->error = -EILSEQ;
        host->status = "error: 2440 fifo failure";
        goto fail_transfer;
    }

    //接收CRC错误
    if (mci_dsta & S3C2410_SDIDSTA_RXCRCFAIL)
    {
        dbg(host, dbg_err, "bad data crc (outgoing)\n");
        cmd->data->error = -EILSEQ;
        host->status = "error: bad data crc (outgoing)";
        goto fail_transfer;
    }

    //发送数据后,CRC状态错误
    if (mci_dsta & S3C2410_SDIDSTA_CRCFAIL)
    {
        dbg(host, dbg_err, "bad data crc (incoming)\n");
        cmd->data->error = -EILSEQ;
        host->status = "error: bad data crc (incoming)";
        goto fail_transfer;
    }

    //数据/忙接收超时
    if (mci_dsta & S3C2410_SDIDSTA_DATATIMEOUT)
    {
        dbg(host, dbg_err, "data timeout\n");
        cmd->data->error = -ETIMEDOUT;
        host->status = "error: data timeout";
        goto fail_transfer;
    }

    //数据计数器为0,和本次请求的全部数据传输结束
    if (mci_dsta & S3C2410_SDIDSTA_XFERFINISH)
    {
        //如果当前任务是完成数据传输则结束数据传输
        if (host->complete_what == COMPLETION_XFERFINISH)
        {
            host->status = "ok: data transfer completed";
            goto close_transfer;
        }

        //如果当前任务是完成数据传输和接收命令响应
        if (host->complete_what == COMPLETION_XFERFINISH_RSPFIN)
            //标记当前任务为完成 接收命令响应
            host->complete_what = COMPLETION_RSPFIN;

        //清除数据传输完标志
        mci_dclear |= S3C2410_SDIDSTA_XFERFINISH;
    }

 //清除状态字
clear_status_bits:
    writel(mci_cclear, host->base + S3C2410_SDICMDSTAT);
    writel(mci_dclear, host->base + S3C2410_SDIDSTA);

    goto irq_out;

// 传输失败
fail_transfer:
    host->pio_active = XFER_NONE;

//传输结束
close_transfer:
    host->complete_what = COMPLETION_FINALIZE;

    clear_imask(host);
    tasklet_schedule(&host->pio_tasklet);

    goto irq_out;

irq_out:
    dbg(host, dbg_irq, "csta:0x%08x dsta:0x%08x fsta:0x%08x dcnt:0x%08x status:%s.\n",
     mci_csta, mci_dsta, mci_fsta, mci_dcnt, host->status);

    //开中断并恢复状态字
    spin_unlock_irqrestore(&host->complete_lock, iflags);
    return IRQ_HANDLED;
}

//MMC/SD卡 中断底半部程序
static void pio_tasklet(unsigned long data)
{
    //data 参数是在s3cmci_probe中的tasklet_init的时候传递过来的
    struct s3cmci_host *host = (struct s3cmci_host *) data;

    //在 执行底半部程序的时候屏蔽中断
    disable_irq(host->irq);

    //判断如果当前存在FIFO的写状态,则进行FIFO的写操作
    if (host->pio_active == XFER_WRITE)
        do_pio_write(host);

    //判断如果当前存在FIFO的读状态,则进行FIFO的读操作
    if (host->pio_active == XFER_READ)
        do_pio_read(host);

    //判断如果当前的请求状态为完成状态,则准备进行完成请求处理
    if (host->complete_what == COMPLETION_FINALIZE)
    {
        //清空中断屏蔽寄存器
        clear_imask(host);
        
        //FIFO状态验证
        if (host->pio_active != XFER_NONE)
        {
            if (host->mrq->data)
                host->mrq->data->error = -EINVAL;
        }

        //完成请求处理
        finalize_request(host);
    }
    else
        //当前请求状态为其他,则使能中断继续请求处理
        enable_irq(host->irq);
}

//完成请求处理
static void finalize_request(struct s3cmci_host *host)
{
    struct mmc_request *mrq = host->mrq;
    struct mmc_command *cmd = host->cmd_is_stop ? mrq->stop : mrq->cmd;
    int debug_as_failure = 0;

    //如果当前请求状态不为完成状态,则为错误
    if (host->complete_what != COMPLETION_FINALIZE)
        return;

    if (!mrq)
        return;

    if (cmd->data && (cmd->error == 0) && (cmd->data->error == 0))
    {
        if (host->dodma && (!host->dma_complete))
        {
            dbg(host, dbg_dma, "DMA Missing!\n");
            return;
        }
    }

    //读响应寄存器
    cmd->resp[0] = readl(host->base + S3C2410_SDIRSP0);
    cmd->resp[1] = readl(host->base + S3C2410_SDIRSP1);
    cmd->resp[2] = readl(host->base + S3C2410_SDIRSP2);
    cmd->resp[3] = readl(host->base + S3C2410_SDIRSP3);

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

    if (cmd->error)
        debug_as_failure = 1;

    if (cmd->data && cmd->data->error)
        debug_as_failure = 1;

    dbg_dumpcmd(host, cmd, debug_as_failure);

    //清空命令参数、数据配置、命令配置、中断屏蔽寄存器
    writel(0, host->base + S3C2410_SDICMDARG);
    writel(S3C2410_SDIDCON_STOP, host->base + S3C2410_SDIDCON);
    writel(0, host->base + S3C2410_SDICMDCON);
    writel(0, host->base + host->sdiimsk);

    if (cmd->data && cmd->error)
        cmd->data->error = cmd->error;

    //有数据请求,有传输停止命令,数据传输命令已发送
    if (cmd->data && cmd->data->stop && (!host->cmd_is_stop))
    {
        host->cmd_is_stop = 1;
        s3cmci_send_request(host->mmc);
//传输停止命令

        return;
    }

    if (!mrq->data)
        goto request_done;

    //计算已传 输的数据量
    if (mrq->data->error == 0)
    {
        mrq->data->bytes_xfered = (mrq->data->blocks * mrq->data->blksz);
    }
    else
    {
        mrq->data->bytes_xfered = 0;
    }

    if (mrq->data->error != 0)
    {
        if (host->dodma)
            s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_FLUSH);

        // 清除和复位FIFO状态寄存器
        writel(S3C2440_SDIFSTA_FIFORESET | S3C2440_SDIFSTA_FIFOFAIL, host->base + S3C2410_SDIFSTA);
    }

//完成请求
request_done:
    host->complete_what = COMPLETION_NONE;
    host->mrq = NULL;
    mmc_request_done(host->mmc, mrq);
}

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