static irqreturn_t sh_mmcif_intr(int irq, void *dev_id) { struct sh_mmcif_host *host = dev_id; u32 state = 0; int err = 0; state = sh_mmcif_readl(host->addr, MMCIF_CE_INT); if (state & INT_RBSYE) { sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~(INT_RBSYE | INT_CRSPE)); sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MRBSYE); } else if (state & INT_CRSPE) { sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~INT_CRSPE); sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MCRSPE); } else if (state & INT_BUFREN) { sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~INT_BUFREN); sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MBUFREN); } else if (state & INT_BUFWEN) { sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~INT_BUFWEN); sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MBUFWEN); } else if (state & INT_CMD12DRE) { sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~(INT_CMD12DRE | INT_CMD12RBE | INT_CMD12CRE | INT_BUFRE)); sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MCMD12DRE); } else if (state & INT_BUFRE) { sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~INT_BUFRE); sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MBUFRE); } else if (state & INT_DTRANE) { sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~INT_DTRANE); sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MDTRANE); } else if (state & INT_CMD12RBE) { sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~(INT_CMD12RBE | INT_CMD12CRE)); sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MCMD12RBE); } else if (state & INT_ERR_STS) { /* err interrupts */ sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~state); sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, state); err = 1; } else { pr_debug("%s: Not support int\n", DRIVER_NAME); sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~state); sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, state); err = 1; } if (err) { host->sd_error = 1; pr_debug("%s: int err state = %08x\n", DRIVER_NAME, state); } host->wait_int = 1; wake_up(&host->intr_wait); return IRQ_HANDLED; }
static int sh_mmcif_intr(void *dev_id) { struct sh_mmcif_host *host = dev_id; u32 state = 0; state = sh_mmcif_read(&host->regs->ce_int); state &= sh_mmcif_read(&host->regs->ce_int_mask); if (state & INT_RBSYE) { sh_mmcif_write(~(INT_RBSYE | INT_CRSPE), &host->regs->ce_int); sh_mmcif_bitclr(MASK_MRBSYE, &host->regs->ce_int_mask); goto end; } else if (state & INT_CRSPE) { sh_mmcif_write(~INT_CRSPE, &host->regs->ce_int); sh_mmcif_bitclr(MASK_MCRSPE, &host->regs->ce_int_mask); /* one more interrupt (INT_RBSYE) */ if (sh_mmcif_read(&host->regs->ce_cmd_set) & CMD_SET_RBSY) return -EAGAIN; goto end; } else if (state & INT_BUFREN) { sh_mmcif_write(~INT_BUFREN, &host->regs->ce_int); sh_mmcif_bitclr(MASK_MBUFREN, &host->regs->ce_int_mask); goto end; } else if (state & INT_BUFWEN) { sh_mmcif_write(~INT_BUFWEN, &host->regs->ce_int); sh_mmcif_bitclr(MASK_MBUFWEN, &host->regs->ce_int_mask); goto end; } else if (state & INT_CMD12DRE) { sh_mmcif_write(~(INT_CMD12DRE | INT_CMD12RBE | INT_CMD12CRE | INT_BUFRE), &host->regs->ce_int); sh_mmcif_bitclr(MASK_MCMD12DRE, &host->regs->ce_int_mask); goto end; } else if (state & INT_BUFRE) { sh_mmcif_write(~INT_BUFRE, &host->regs->ce_int); sh_mmcif_bitclr(MASK_MBUFRE, &host->regs->ce_int_mask); goto end; } else if (state & INT_DTRANE) { sh_mmcif_write(~INT_DTRANE, &host->regs->ce_int); sh_mmcif_bitclr(MASK_MDTRANE, &host->regs->ce_int_mask); goto end; } else if (state & INT_CMD12RBE) { sh_mmcif_write(~(INT_CMD12RBE | INT_CMD12CRE), &host->regs->ce_int); sh_mmcif_bitclr(MASK_MCMD12RBE, &host->regs->ce_int_mask); goto end; } else if (state & INT_ERR_STS) { /* err interrupts */ sh_mmcif_write(~state, &host->regs->ce_int); sh_mmcif_bitclr(state, &host->regs->ce_int_mask); goto err; } else return -EAGAIN; err: host->sd_error = 1; debug("%s: int err state = %08x\n", DRIVER_NAME, state); end: host->wait_int = 1; return 0; }
static irqreturn_t sh_mmcif_intr(int irq, void *dev_id) { struct sh_mmcif_host *host = dev_id; u32 state, mask; state = sh_mmcif_readl(host->addr, MMCIF_CE_INT); mask = sh_mmcif_readl(host->addr, MMCIF_CE_INT_MASK); if (host->ccs_enable) sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~(state & mask)); else sh_mmcif_writel(host->addr, MMCIF_CE_INT, INT_CCS | ~(state & mask)); sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, state & MASK_CLEAN); if (state & ~MASK_CLEAN) dev_dbg(&host->pd->dev, "IRQ state = 0x%08x incompletely cleared\n", state); if (state & INT_ERR_STS || state & ~INT_ALL) { host->sd_error = true; dev_dbg(&host->pd->dev, "int err state = 0x%08x\n", state); } if (state & ~(INT_CMD12RBE | INT_CMD12CRE)) { if (!host->mrq) dev_dbg(&host->pd->dev, "NULL IRQ state = 0x%08x\n", state); if (!host->dma_active) return IRQ_WAKE_THREAD; else if (host->sd_error) mmcif_dma_complete(host); } else { dev_dbg(&host->pd->dev, "Unexpected IRQ 0x%x\n", state); } return IRQ_HANDLED; }
static void sh_mmcif_clock_control(struct sh_mmcif_host *host, unsigned int clk) { struct sh_mmcif_plat_data *p = host->pd->dev.platform_data; sh_mmcif_bitclr(host, MMCIF_CE_CLK_CTRL, CLK_ENABLE); sh_mmcif_bitclr(host, MMCIF_CE_CLK_CTRL, CLK_CLEAR); if (!clk) return; if (p->sup_pclk && clk == host->clk) sh_mmcif_bitset(host, MMCIF_CE_CLK_CTRL, CLK_SUP_PCLK); else sh_mmcif_bitset(host, MMCIF_CE_CLK_CTRL, CLK_CLEAR & (ilog2(__rounddown_pow_of_two(host->clk / clk)) << 16)); sh_mmcif_bitset(host, MMCIF_CE_CLK_CTRL, CLK_ENABLE); }
static void sh_mmcif_clock_control(struct sh_mmcif_host *host, unsigned int clk) { int i; sh_mmcif_bitclr(CLK_ENABLE, &host->regs->ce_clk_ctrl); sh_mmcif_bitclr(CLK_CLEAR, &host->regs->ce_clk_ctrl); if (!clk) return; if (clk == CLKDEV_EMMC_DATA) { sh_mmcif_bitset(CLK_PCLK, &host->regs->ce_clk_ctrl); } else { for (i = 1; (unsigned int)host->clk / (1 << i) >= clk; i++) ; sh_mmcif_bitset((i - 1) << 16, &host->regs->ce_clk_ctrl); } sh_mmcif_bitset(CLK_ENABLE, &host->regs->ce_clk_ctrl); }
static void sh_mmcif_clock_control(struct sh_mmcif_host *host, unsigned int clk) { struct sh_mmcif_plat_data *p = host->pd->dev.platform_data; bool sup_pclk = p ? p->sup_pclk : false; sh_mmcif_bitclr(host, MMCIF_CE_CLK_CTRL, CLK_ENABLE); sh_mmcif_bitclr(host, MMCIF_CE_CLK_CTRL, CLK_CLEAR); if (!clk) return; if (sup_pclk && clk == host->clk) sh_mmcif_bitset(host, MMCIF_CE_CLK_CTRL, CLK_SUP_PCLK); else sh_mmcif_bitset(host, MMCIF_CE_CLK_CTRL, CLK_CLEAR & ((fls(DIV_ROUND_UP(host->clk, clk) - 1) - 1) << 16)); sh_mmcif_bitset(host, MMCIF_CE_CLK_CTRL, CLK_ENABLE); }
static void sh_mmcif_start_dma_tx(struct sh_mmcif_host *host) { struct mmc_data *data = host->mrq->data; struct scatterlist *sg = data->sg; struct dma_async_tx_descriptor *desc = NULL; struct dma_chan *chan = host->chan_tx; dma_cookie_t cookie = -EINVAL; int ret; ret = dma_map_sg(chan->device->dev, sg, data->sg_len, DMA_TO_DEVICE); if (ret > 0) { host->dma_active = true; desc = dmaengine_prep_slave_sg(chan, sg, ret, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); } if (desc) { desc->callback = mmcif_dma_complete; desc->callback_param = host; cookie = dmaengine_submit(desc); sh_mmcif_bitset(host, MMCIF_CE_BUF_ACC, BUF_ACC_DMAWEN); dma_async_issue_pending(chan); } dev_dbg(&host->pd->dev, "%s(): mapped %d -> %d, cookie %d\n", __func__, data->sg_len, ret, cookie); if (!desc) { /* DMA failed, fall back to PIO */ if (ret >= 0) ret = -EIO; host->chan_tx = NULL; host->dma_active = false; dma_release_channel(chan); /* Free the Rx channel too */ chan = host->chan_rx; if (chan) { host->chan_rx = NULL; dma_release_channel(chan); } dev_warn(&host->pd->dev, "DMA failed: %d, falling back to PIO\n", ret); sh_mmcif_bitclr(host, MMCIF_CE_BUF_ACC, BUF_ACC_DMAREN | BUF_ACC_DMAWEN); } dev_dbg(&host->pd->dev, "%s(): desc %p, cookie %d\n", __func__, desc, cookie); }
static void sh_mmcif_release_dma(struct sh_mmcif_host *host) { sh_mmcif_bitclr(host, MMCIF_CE_BUF_ACC, BUF_ACC_DMAREN | BUF_ACC_DMAWEN); /* Descriptors are freed automatically */ if (host->chan_tx) { struct dma_chan *chan = host->chan_tx; host->chan_tx = NULL; dma_release_channel(chan); } if (host->chan_rx) { struct dma_chan *chan = host->chan_rx; host->chan_rx = NULL; dma_release_channel(chan); } host->dma_active = false; }
static bool sh_mmcif_end_cmd(struct sh_mmcif_host *host) { struct mmc_command *cmd = host->mrq->cmd; struct mmc_data *data = host->mrq->data; long time; if (host->sd_error) { switch (cmd->opcode) { case MMC_ALL_SEND_CID: case MMC_SELECT_CARD: case MMC_APP_CMD: cmd->error = -ETIMEDOUT; break; default: cmd->error = sh_mmcif_error_manage(host); break; } dev_dbg(&host->pd->dev, "CMD%d error %d\n", cmd->opcode, cmd->error); host->sd_error = false; return false; } if (!(cmd->flags & MMC_RSP_PRESENT)) { cmd->error = 0; return false; } sh_mmcif_get_response(host, cmd); if (!data) return false; /* * Completion can be signalled from DMA callback and error, so, have to * reset here, before setting .dma_active */ init_completion(&host->dma_complete); if (data->flags & MMC_DATA_READ) { if (host->chan_rx) sh_mmcif_start_dma_rx(host); } else { if (host->chan_tx) sh_mmcif_start_dma_tx(host); } if (!host->dma_active) { data->error = sh_mmcif_data_trans(host, host->mrq, cmd->opcode); return !data->error; } /* Running in the IRQ thread, can sleep */ time = wait_for_completion_interruptible_timeout(&host->dma_complete, host->timeout); if (data->flags & MMC_DATA_READ) dma_unmap_sg(host->chan_rx->device->dev, data->sg, data->sg_len, DMA_FROM_DEVICE); else dma_unmap_sg(host->chan_tx->device->dev, data->sg, data->sg_len, DMA_TO_DEVICE); if (host->sd_error) { dev_err(host->mmc->parent, "Error IRQ while waiting for DMA completion!\n"); /* Woken up by an error IRQ: abort DMA */ data->error = sh_mmcif_error_manage(host); } else if (!time) { dev_err(host->mmc->parent, "DMA timeout!\n"); data->error = -ETIMEDOUT; } else if (time < 0) { dev_err(host->mmc->parent, "wait_for_completion_...() error %ld!\n", time); data->error = time; } sh_mmcif_bitclr(host, MMCIF_CE_BUF_ACC, BUF_ACC_DMAREN | BUF_ACC_DMAWEN); host->dma_active = false; if (data->error) { data->bytes_xfered = 0; /* Abort DMA */ if (data->flags & MMC_DATA_READ) dmaengine_terminate_all(host->chan_rx); else dmaengine_terminate_all(host->chan_tx); } return false; }
static irqreturn_t sh_mmcif_intr(int irq, void *dev_id) { struct sh_mmcif_host *host = dev_id; u32 state; int err = 0; state = sh_mmcif_readl(host->addr, MMCIF_CE_INT); if (state & INT_ERR_STS) { /* error interrupts - process first */ sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~state); sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, state); err = 1; } else if (state & INT_RBSYE) { sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~(INT_RBSYE | INT_CRSPE)); sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MRBSYE); } else if (state & INT_CRSPE) { sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~INT_CRSPE); sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MCRSPE); } else if (state & INT_BUFREN) { sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~INT_BUFREN); sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MBUFREN); } else if (state & INT_BUFWEN) { sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~INT_BUFWEN); sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MBUFWEN); } else if (state & INT_CMD12DRE) { sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~(INT_CMD12DRE | INT_CMD12RBE | INT_CMD12CRE | INT_BUFRE)); sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MCMD12DRE); } else if (state & INT_BUFRE) { sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~INT_BUFRE); sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MBUFRE); } else if (state & INT_DTRANE) { sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~(INT_CMD12DRE | INT_CMD12RBE | INT_CMD12CRE | INT_DTRANE)); sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MDTRANE); } else if (state & INT_CMD12RBE) { sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~(INT_CMD12RBE | INT_CMD12CRE)); sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MCMD12RBE); } else { dev_dbg(&host->pd->dev, "Unsupported interrupt: 0x%x\n", state); sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~state); sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, state); err = 1; } if (err) { host->sd_error = true; dev_dbg(&host->pd->dev, "int err state = %08x\n", state); } if (state & ~(INT_CMD12RBE | INT_CMD12CRE)) { if (!host->dma_active) return IRQ_WAKE_THREAD; else if (host->sd_error) mmcif_dma_complete(host); } else { dev_dbg(&host->pd->dev, "Unexpected IRQ 0x%x\n", state); } return IRQ_HANDLED; }
static bool sh_mmcif_end_cmd(struct sh_mmcif_host *host) { struct mmc_command *cmd = host->mrq->cmd; struct mmc_data *data = host->mrq->data; long time; if (host->sd_error) { switch (cmd->opcode) { case MMC_ALL_SEND_CID: case MMC_SELECT_CARD: case MMC_APP_CMD: cmd->error = -ETIMEDOUT; host->sd_error = false; break; default: cmd->error = sh_mmcif_error_manage(host); dev_dbg(&host->pd->dev, "Cmd(d'%d) error %d\n", cmd->opcode, cmd->error); break; } return false; } if (!(cmd->flags & MMC_RSP_PRESENT)) { cmd->error = 0; return false; } sh_mmcif_get_response(host, cmd); if (!data) return false; if (data->flags & MMC_DATA_READ) { if (host->chan_rx) sh_mmcif_start_dma_rx(host); } else { if (host->chan_tx) sh_mmcif_start_dma_tx(host); } if (!host->dma_active) { data->error = sh_mmcif_data_trans(host, host->mrq, cmd->opcode); if (!data->error) return true; return false; } /* Running in the IRQ thread, can sleep */ time = wait_for_completion_interruptible_timeout(&host->dma_complete, host->timeout); if (host->sd_error) { dev_err(host->mmc->parent, "Error IRQ while waiting for DMA completion!\n"); /* Woken up by an error IRQ: abort DMA */ if (data->flags & MMC_DATA_READ) dmaengine_terminate_all(host->chan_rx); else dmaengine_terminate_all(host->chan_tx); data->error = sh_mmcif_error_manage(host); } else if (!time) { data->error = -ETIMEDOUT; } else if (time < 0) { data->error = time; } sh_mmcif_bitclr(host, MMCIF_CE_BUF_ACC, BUF_ACC_DMAREN | BUF_ACC_DMAWEN); host->dma_active = false; if (data->error) data->bytes_xfered = 0; return false; }