static void sh_mmcif_start_cmd(struct sh_mmcif_host *host, struct mmc_request *mrq, struct mmc_command *cmd) { long time; int ret = 0, mask = 0; u32 opc = cmd->opcode; host->cmd = cmd; switch (opc) { /* respons busy check */ case MMC_SWITCH: case MMC_STOP_TRANSMISSION: case MMC_SET_WRITE_PROT: case MMC_CLR_WRITE_PROT: case MMC_ERASE: case MMC_GEN_CMD: mask = MASK_MRBSYE; break; default: mask = MASK_MCRSPE; break; } mask |= MASK_MCMDVIO | MASK_MBUFVIO | MASK_MWDATERR | MASK_MRDATERR | MASK_MRIDXERR | MASK_MRSPERR | MASK_MCCSTO | MASK_MCRCSTO | MASK_MWDATTO | MASK_MRDATTO | MASK_MRBSYTO | MASK_MRSPTO; if (host->data) { sh_mmcif_writel(host->addr, MMCIF_CE_BLOCK_SET, 0); sh_mmcif_writel(host->addr, MMCIF_CE_BLOCK_SET, mrq->data->blksz); } opc = sh_mmcif_set_cmd(host, mrq, cmd, opc); sh_mmcif_writel(host->addr, MMCIF_CE_INT, 0xD80430C0); sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, mask); /* set arg */ sh_mmcif_writel(host->addr, MMCIF_CE_ARG, cmd->arg); host->wait_int = 0; /* set cmd */ sh_mmcif_writel(host->addr, MMCIF_CE_CMD_SET, opc); time = wait_event_interruptible_timeout(host->intr_wait, host->wait_int == 1 || host->sd_error == 1, host->timeout); if (host->wait_int != 1 && time == 0) { cmd->error = sh_mmcif_error_manage(host); return; } 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: pr_debug("%s: Cmd(d'%d) err\n", DRIVER_NAME, cmd->opcode); cmd->error = sh_mmcif_error_manage(host); break; } host->sd_error = 0; host->wait_int = 0; return; } if (!(cmd->flags & MMC_RSP_PRESENT)) { cmd->error = ret; host->wait_int = 0; return; } if (host->wait_int == 1) { sh_mmcif_get_response(host, cmd); host->wait_int = 0; } if (host->data) { ret = sh_mmcif_data_trans(host, mrq, cmd->opcode); if (ret < 0) mrq->data->bytes_xfered = 0; else mrq->data->bytes_xfered = mrq->data->blocks * mrq->data->blksz; } cmd->error = ret; }
static int sh_mmcif_start_cmd(struct sh_mmcif_host *host, struct mmc_data *data, struct mmc_cmd *cmd) { long time; int ret = 0, mask = 0; u32 opc = cmd->cmdidx; if (opc == MMC_CMD_STOP_TRANSMISSION) { /* MMCIF sends the STOP command automatically */ if (host->last_cmd == MMC_CMD_READ_MULTIPLE_BLOCK) sh_mmcif_bitset(MASK_MCMD12DRE, &host->regs->ce_int_mask); else sh_mmcif_bitset(MASK_MCMD12RBE, &host->regs->ce_int_mask); time = mmcif_wait_interrupt_flag(host); if (time == 0 || host->sd_error != 0) return sh_mmcif_error_manage(host); sh_mmcif_get_cmd12response(host, cmd); return 0; } if (opc == MMC_CMD_SWITCH) mask = MASK_MRBSYE; else mask = MASK_MCRSPE; mask |= MASK_MCMDVIO | MASK_MBUFVIO | MASK_MWDATERR | MASK_MRDATERR | MASK_MRIDXERR | MASK_MRSPERR | MASK_MCCSTO | MASK_MCRCSTO | MASK_MWDATTO | MASK_MRDATTO | MASK_MRBSYTO | MASK_MRSPTO; if (host->data) { sh_mmcif_write(0, &host->regs->ce_block_set); sh_mmcif_write(data->blocksize, &host->regs->ce_block_set); } opc = sh_mmcif_set_cmd(host, data, cmd); sh_mmcif_write(INT_START_MAGIC, &host->regs->ce_int); sh_mmcif_write(mask, &host->regs->ce_int_mask); debug("CMD%d ARG:%08x\n", cmd->cmdidx, cmd->cmdarg); /* set arg */ sh_mmcif_write(cmd->cmdarg, &host->regs->ce_arg); host->wait_int = 0; /* set cmd */ sh_mmcif_write(opc, &host->regs->ce_cmd_set); time = mmcif_wait_interrupt_flag(host); if (time == 0) return sh_mmcif_error_manage(host); if (host->sd_error) { switch (cmd->cmdidx) { case MMC_CMD_ALL_SEND_CID: case MMC_CMD_SELECT_CARD: case MMC_CMD_APP_CMD: ret = TIMEOUT; break; default: printf(DRIVER_NAME": Cmd(d'%d) err\n", cmd->cmdidx); ret = sh_mmcif_error_manage(host); break; } host->sd_error = 0; host->wait_int = 0; return ret; } /* if no response */ if (!(opc & 0x00C00000)) return 0; if (host->wait_int == 1) { sh_mmcif_get_response(host, cmd); host->wait_int = 0; } if (host->data) ret = sh_mmcif_data_trans(host, data, cmd->cmdidx); host->last_cmd = cmd->cmdidx; return ret; }
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 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; }