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