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