static void mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd, unsigned int status) { void __iomem *base = host->base; host->cmd = NULL; cmd->resp[0] = readl(base + MMCIRESPONSE0); cmd->resp[1] = readl(base + MMCIRESPONSE1); cmd->resp[2] = readl(base + MMCIRESPONSE2); cmd->resp[3] = readl(base + MMCIRESPONSE3); if (status & MCI_CMDTIMEOUT) { cmd->error = -ETIMEDOUT; } else if (status & MCI_CMDCRCFAIL && cmd->flags & MMC_RSP_CRC) { cmd->error = -EILSEQ; } if (!cmd->data || cmd->error) { if (host->data) mmci_stop_data(host); mmci_request_end(host, cmd->mrq); } else if (!(cmd->data->flags & MMC_DATA_READ)) { mmci_start_data(host, cmd->data); } }
static void mmci_data_irq(struct mmci_host *host, struct mmc_data *data, unsigned int status) { if (status & MCI_DATABLOCKEND) { host->data_xfered += data->blksz; } if (status & (MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_TXUNDERRUN|MCI_RXOVERRUN)) { if (status & MCI_DATACRCFAIL) data->error = -EILSEQ; else if (status & MCI_DATATIMEOUT) data->error = -ETIMEDOUT; else if (status & (MCI_TXUNDERRUN|MCI_RXOVERRUN)) data->error = -EIO; status |= MCI_DATAEND; /* * We hit an error condition. Ensure that any data * partially written to a page is properly coherent. */ if (host->sg_len && data->flags & MMC_DATA_READ) flush_dcache_page(sg_page(host->sg_ptr)); } if (status & MCI_DATAEND) { mmci_stop_data(host); if (!data->stop) { mmci_request_end(host, data->mrq); } else { mmci_start_command(host, data->stop, 0); } } }
static void mmci_data_irq(struct mmci_host *host, struct mmc_data *data, unsigned int status) { if (status & MCI_DATABLOCKEND) { host->data_xfered += 1 << data->blksz_bits; } if (status & (MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_TXUNDERRUN|MCI_RXOVERRUN)) { if (status & MCI_DATACRCFAIL) data->error = MMC_ERR_BADCRC; else if (status & MCI_DATATIMEOUT) data->error = MMC_ERR_TIMEOUT; else if (status & (MCI_TXUNDERRUN|MCI_RXOVERRUN)) data->error = MMC_ERR_FIFO; status |= MCI_DATAEND; } if (status & MCI_DATAEND) { mmci_stop_data(host); if (!data->stop) { mmci_request_end(host, data->mrq); } else { mmci_start_command(host, data->stop, 0); } } }
static void mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd, unsigned int status) { void __iomem *base = host->base; bool sbc; if (!cmd) return; sbc = (cmd == host->mrq->sbc); /* * We need to be one of these interrupts to be considered worth * handling. Note that we tag on any latent IRQs postponed * due to waiting for busy status. */ if (!((status|host->busy_status) & (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT|MCI_CMDSENT|MCI_CMDRESPEND))) return; /* * ST Micro variant: handle busy detection. */ if (host->variant->busy_detect) { bool busy_resp = !!(cmd->flags & MMC_RSP_BUSY); /* We are busy with a command, return */ if (host->busy_status && (status & host->variant->busy_detect_flag)) return; /* * We were not busy, but we now got a busy response on * something that was not an error, and we double-check * that the special busy status bit is still set before * proceeding. */ if (!host->busy_status && busy_resp && !(status & (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT)) && (readl(base + MMCISTATUS) & host->variant->busy_detect_flag)) { /* Clear the busy start IRQ */ writel(host->variant->busy_detect_mask, host->base + MMCICLEAR); /* Unmask the busy end IRQ */ writel(readl(base + MMCIMASK0) | host->variant->busy_detect_mask, base + MMCIMASK0); /* * Now cache the last response status code (until * the busy bit goes low), and return. */ host->busy_status = status & (MCI_CMDSENT|MCI_CMDRESPEND); return; } /* * At this point we are not busy with a command, we have * not received a new busy request, clear and mask the busy * end IRQ and fall through to process the IRQ. */ if (host->busy_status) { writel(host->variant->busy_detect_mask, host->base + MMCICLEAR); writel(readl(base + MMCIMASK0) & ~host->variant->busy_detect_mask, base + MMCIMASK0); host->busy_status = 0; } } host->cmd = NULL; if (status & MCI_CMDTIMEOUT) { cmd->error = -ETIMEDOUT; } else if (status & MCI_CMDCRCFAIL && cmd->flags & MMC_RSP_CRC) { cmd->error = -EILSEQ; } else { cmd->resp[0] = readl(base + MMCIRESPONSE0); cmd->resp[1] = readl(base + MMCIRESPONSE1); cmd->resp[2] = readl(base + MMCIRESPONSE2); cmd->resp[3] = readl(base + MMCIRESPONSE3); } if ((!sbc && !cmd->data) || cmd->error) { if (host->data) { /* Terminate the DMA transfer */ if (dma_inprogress(host)) { mmci_dma_data_error(host); mmci_dma_unmap(host, host->data); } mmci_stop_data(host); } mmci_request_end(host, host->mrq); } else if (sbc) { mmci_start_command(host, host->mrq->cmd, 0); } else if (!(cmd->data->flags & MMC_DATA_READ)) { mmci_start_data(host, cmd->data); } }
static void mmci_data_irq(struct mmci_host *host, struct mmc_data *data, unsigned int status) { /* Make sure we have data to handle */ if (!data) return; /* First check for errors */ if (status & (MCI_DATACRCFAIL | MCI_DATATIMEOUT | host->variant->start_err | MCI_TXUNDERRUN | MCI_RXOVERRUN)) { u32 remain, success; /* Terminate the DMA transfer */ if (dma_inprogress(host)) { mmci_dma_data_error(host); mmci_dma_unmap(host, data); } /* * Calculate how far we are into the transfer. Note that * the data counter gives the number of bytes transferred * on the MMC bus, not on the host side. On reads, this * can be as much as a FIFO-worth of data ahead. This * matters for FIFO overruns only. */ remain = readl(host->base + MMCIDATACNT); success = data->blksz * data->blocks - remain; dev_dbg(mmc_dev(host->mmc), "MCI ERROR IRQ, status 0x%08x at 0x%08x\n", status, success); if (status & MCI_DATACRCFAIL) { /* Last block was not successful */ success -= 1; data->error = -EILSEQ; } else if (status & MCI_DATATIMEOUT) { data->error = -ETIMEDOUT; } else if (status & MCI_STARTBITERR) { data->error = -ECOMM; } else if (status & MCI_TXUNDERRUN) { data->error = -EIO; } else if (status & MCI_RXOVERRUN) { if (success > host->variant->fifosize) success -= host->variant->fifosize; else success = 0; data->error = -EIO; } data->bytes_xfered = round_down(success, data->blksz); } if (status & MCI_DATABLOCKEND) dev_err(mmc_dev(host->mmc), "stray MCI_DATABLOCKEND interrupt\n"); if (status & MCI_DATAEND || data->error) { if (dma_inprogress(host)) mmci_dma_finalize(host, data); mmci_stop_data(host); if (!data->error) /* The error clause is handled above, success! */ data->bytes_xfered = data->blksz * data->blocks; if (!data->stop || host->mrq->sbc) { mmci_request_end(host, data->mrq); } else { mmci_start_command(host, data->stop, 0); } } }