static int sh_mmcif_single_write(struct sh_mmcif_host *host, struct mmc_request *mrq) { struct mmc_data *data = mrq->data; long time; u32 blocksize, i, *p = sg_virt(data->sg); host->wait_int = 0; sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFWEN); /* buf write enable */ 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 || host->sd_error != 0)) return sh_mmcif_error_manage(host); host->wait_int = 0; blocksize = (BLOCK_SIZE_MASK & sh_mmcif_readl(host->addr, MMCIF_CE_BLOCK_SET)) + 3; for (i = 0; i < blocksize / 4; i++) sh_mmcif_writel(host->addr, MMCIF_CE_DATA, *p++); /* buffer write end */ sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MDTRANE); 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 || host->sd_error != 0)) return sh_mmcif_error_manage(host); host->wait_int = 0; return 0; }
static int sh_mmcif_single_write(struct sh_mmcif_host *host, struct mmc_data *data) { long time; u32 blocksize, i; const unsigned long *p = (unsigned long *)data->dest; if ((unsigned long)p & 0x00000001) { printf("%s: The data pointer is unaligned.", __func__); return -EIO; } host->wait_int = 0; sh_mmcif_bitset(MASK_MBUFWEN, &host->regs->ce_int_mask); time = mmcif_wait_interrupt_flag(host); if (time == 0 || host->sd_error != 0) return sh_mmcif_error_manage(host); host->wait_int = 0; blocksize = (BLOCK_SIZE_MASK & sh_mmcif_read(&host->regs->ce_block_set)) + 3; for (i = 0; i < blocksize / 4; i++) sh_mmcif_write(*p++, &host->regs->ce_data); /* buffer write end */ sh_mmcif_bitset(MASK_MDTRANE, &host->regs->ce_int_mask); time = mmcif_wait_interrupt_flag(host); if (time == 0 || host->sd_error != 0) return sh_mmcif_error_manage(host); host->wait_int = 0; return 0; }
static void sh_mmcif_stop_cmd(struct sh_mmcif_host *host, struct mmc_request *mrq, struct mmc_command *cmd) { long time; if (mrq->cmd->opcode == MMC_READ_MULTIPLE_BLOCK) sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MCMD12DRE); else if (mrq->cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK) sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MCMD12RBE); else { pr_err(DRIVER_NAME": not support stop cmd\n"); cmd->error = sh_mmcif_error_manage(host); return; } 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 || host->sd_error != 0)) { cmd->error = sh_mmcif_error_manage(host); return; } sh_mmcif_get_cmd12response(host, cmd); host->wait_int = 0; cmd->error = 0; }
static void mmcif_timeout_work(struct work_struct *work) { struct delayed_work *d = container_of(work, struct delayed_work, work); struct sh_mmcif_host *host = container_of(d, struct sh_mmcif_host, timeout_work); struct mmc_request *mrq = host->mrq; unsigned long flags; if (host->dying) /* Don't run after mmc_remove_host() */ return; dev_err(&host->pd->dev, "Timeout waiting for %u on CMD%u\n", host->wait_for, mrq->cmd->opcode); spin_lock_irqsave(&host->lock, flags); if (host->state == STATE_IDLE) { spin_unlock_irqrestore(&host->lock, flags); return; } host->state = STATE_TIMEOUT; spin_unlock_irqrestore(&host->lock, flags); /* * Handle races with cancel_delayed_work(), unless * cancel_delayed_work_sync() is used */ switch (host->wait_for) { case MMCIF_WAIT_FOR_CMD: mrq->cmd->error = sh_mmcif_error_manage(host); break; case MMCIF_WAIT_FOR_STOP: mrq->stop->error = sh_mmcif_error_manage(host); break; case MMCIF_WAIT_FOR_MREAD: case MMCIF_WAIT_FOR_MWRITE: case MMCIF_WAIT_FOR_READ: case MMCIF_WAIT_FOR_WRITE: case MMCIF_WAIT_FOR_READ_END: case MMCIF_WAIT_FOR_WRITE_END: mrq->data->error = sh_mmcif_error_manage(host); break; default: BUG(); } host->state = STATE_IDLE; host->wait_for = MMCIF_WAIT_FOR_REQUEST; host->mrq = NULL; mmc_request_done(host->mmc, mrq); }
static int sh_mmcif_multi_write(struct sh_mmcif_host *host, struct mmc_request *mrq) { struct mmc_data *data = mrq->data; long time; u32 i, sec, j, blocksize, *p; blocksize = BLOCK_SIZE_MASK & sh_mmcif_readl(host->addr, MMCIF_CE_BLOCK_SET); for (j = 0; j < data->sg_len; j++) { p = sg_virt(data->sg); host->wait_int = 0; for (sec = 0; sec < data->sg->length / blocksize; sec++) { sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFWEN); /* buf write enable*/ 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 || host->sd_error != 0)) return sh_mmcif_error_manage(host); host->wait_int = 0; for (i = 0; i < blocksize / 4; i++) sh_mmcif_writel(host->addr, MMCIF_CE_DATA, *p++); } if (j < data->sg_len - 1) data->sg++; } return 0; }
static int sh_mmcif_multi_write(struct sh_mmcif_host *host, struct mmc_data *data) { long time; u32 i, j, blocksize; const unsigned long *p = (unsigned long *)data->dest; if ((unsigned long)p & 0x00000001) { printf("%s: The data pointer is unaligned.", __func__); return -EIO; } host->wait_int = 0; blocksize = BLOCK_SIZE_MASK & sh_mmcif_read(&host->regs->ce_block_set); for (j = 0; j < data->blocks; j++) { sh_mmcif_bitset(MASK_MBUFWEN, &host->regs->ce_int_mask); time = mmcif_wait_interrupt_flag(host); if (time == 0 || host->sd_error != 0) return sh_mmcif_error_manage(host); host->wait_int = 0; for (i = 0; i < blocksize / 4; i++) sh_mmcif_write(*p++, &host->regs->ce_data); WATCHDOG_RESET(); } return 0; }
static void mmcif_timeout_work(struct work_struct *work) { struct delayed_work *d = container_of(work, struct delayed_work, work); struct sh_mmcif_host *host = container_of(d, struct sh_mmcif_host, timeout_work); struct mmc_request *mrq = host->mrq; if (host->dying) /* Don't run after mmc_remove_host() */ return; /* * Handle races with cancel_delayed_work(), unless * cancel_delayed_work_sync() is used */ switch (host->wait_for) { case MMCIF_WAIT_FOR_CMD: mrq->cmd->error = sh_mmcif_error_manage(host); break; case MMCIF_WAIT_FOR_STOP: mrq->stop->error = sh_mmcif_error_manage(host); break; case MMCIF_WAIT_FOR_MREAD: case MMCIF_WAIT_FOR_MWRITE: case MMCIF_WAIT_FOR_READ: case MMCIF_WAIT_FOR_WRITE: case MMCIF_WAIT_FOR_READ_END: case MMCIF_WAIT_FOR_WRITE_END: mrq->data->error = sh_mmcif_error_manage(host); break; default: BUG(); } host->state = STATE_IDLE; host->wait_for = MMCIF_WAIT_FOR_REQUEST; host->mrq = NULL; mmc_request_done(host->mmc, mrq); }
static void sh_mmcif_stop_cmd(struct sh_mmcif_host *host, struct mmc_request *mrq) { switch (mrq->cmd->opcode) { case MMC_READ_MULTIPLE_BLOCK: sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MCMD12DRE); break; case MMC_WRITE_MULTIPLE_BLOCK: sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MCMD12RBE); break; default: dev_err(&host->pd->dev, "unsupported stop cmd\n"); mrq->stop->error = sh_mmcif_error_manage(host); return; } host->wait_for = MMCIF_WAIT_FOR_STOP; }
static bool sh_mmcif_write_block(struct sh_mmcif_host *host) { struct mmc_data *data = host->mrq->data; u32 *p = sg_virt(data->sg); int i; if (host->sd_error) { data->error = sh_mmcif_error_manage(host); return false; } for (i = 0; i < host->blocksize / 4; i++) sh_mmcif_writel(host->addr, MMCIF_CE_DATA, *p++); /* buffer write end */ sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MDTRANE); host->wait_for = MMCIF_WAIT_FOR_WRITE_END; return true; }
static bool sh_mmcif_read_block(struct sh_mmcif_host *host) { struct mmc_data *data = host->mrq->data; u32 *p = sg_virt(data->sg); int i; if (host->sd_error) { data->error = sh_mmcif_error_manage(host); return false; } for (i = 0; i < host->blocksize / 4; i++) *p++ = sh_mmcif_readl(host->addr, MMCIF_CE_DATA); /* buffer read end */ sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFRE); host->wait_for = MMCIF_WAIT_FOR_READ_END; return true; }
static bool sh_mmcif_mwrite_block(struct sh_mmcif_host *host) { struct mmc_data *data = host->mrq->data; u32 *p = host->pio_ptr; int i; if (host->sd_error) { data->error = sh_mmcif_error_manage(host); dev_dbg(&host->pd->dev, "%s(): %d\n", __func__, data->error); return false; } BUG_ON(!data->sg->length); for (i = 0; i < host->blocksize / 4; i++) sh_mmcif_writel(host->addr, MMCIF_CE_DATA, *p++); if (!sh_mmcif_next_block(host, p)) return false; sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFWEN); return true; }
static bool sh_mmcif_mwrite_block(struct sh_mmcif_host *host) { struct mmc_data *data = host->mrq->data; u32 *p = host->pio_ptr; int i; if (host->sd_error) { data->error = sh_mmcif_error_manage(host); return false; } BUG_ON(!data->sg->length); for (i = 0; i < host->blocksize / 4; i++) sh_mmcif_writel(host->addr, MMCIF_CE_DATA, *p++); if (!sh_mmcif_next_block(host, p)) return false; schedule_delayed_work(&host->timeout_work, host->timeout); sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFWEN); return true; }
static irqreturn_t sh_mmcif_irqt(int irq, void *dev_id) { struct sh_mmcif_host *host = dev_id; struct mmc_request *mrq; bool wait = false; cancel_delayed_work_sync(&host->timeout_work); mutex_lock(&host->thread_lock); mrq = host->mrq; if (!mrq) { dev_dbg(&host->pd->dev, "IRQ thread state %u, wait %u: NULL mrq!\n", host->state, host->wait_for); mutex_unlock(&host->thread_lock); return IRQ_HANDLED; } /* * All handlers return true, if processing continues, and false, if the * request has to be completed - successfully or not */ switch (host->wait_for) { case MMCIF_WAIT_FOR_REQUEST: /* We're too late, the timeout has already kicked in */ mutex_unlock(&host->thread_lock); return IRQ_HANDLED; case MMCIF_WAIT_FOR_CMD: /* Wait for data? */ wait = sh_mmcif_end_cmd(host); break; case MMCIF_WAIT_FOR_MREAD: /* Wait for more data? */ wait = sh_mmcif_mread_block(host); break; case MMCIF_WAIT_FOR_READ: /* Wait for data end? */ wait = sh_mmcif_read_block(host); break; case MMCIF_WAIT_FOR_MWRITE: /* Wait data to write? */ wait = sh_mmcif_mwrite_block(host); break; case MMCIF_WAIT_FOR_WRITE: /* Wait for data end? */ wait = sh_mmcif_write_block(host); break; case MMCIF_WAIT_FOR_STOP: if (host->sd_error) { mrq->stop->error = sh_mmcif_error_manage(host); dev_dbg(&host->pd->dev, "%s(): %d\n", __func__, mrq->stop->error); break; } sh_mmcif_get_cmd12response(host, mrq->stop); mrq->stop->error = 0; break; case MMCIF_WAIT_FOR_READ_END: case MMCIF_WAIT_FOR_WRITE_END: if (host->sd_error) { mrq->data->error = sh_mmcif_error_manage(host); dev_dbg(&host->pd->dev, "%s(): %d\n", __func__, mrq->data->error); } break; default: BUG(); } if (wait) { schedule_delayed_work(&host->timeout_work, host->timeout); /* Wait for more data */ mutex_unlock(&host->thread_lock); return IRQ_HANDLED; } if (host->wait_for != MMCIF_WAIT_FOR_STOP) { struct mmc_data *data = mrq->data; if (!mrq->cmd->error && data && !data->error) data->bytes_xfered = data->blocks * data->blksz; if (mrq->stop && !mrq->cmd->error && (!data || !data->error)) { sh_mmcif_stop_cmd(host, mrq); if (!mrq->stop->error) { schedule_delayed_work(&host->timeout_work, host->timeout); mutex_unlock(&host->thread_lock); return IRQ_HANDLED; } } } host->wait_for = MMCIF_WAIT_FOR_REQUEST; host->state = STATE_IDLE; host->mrq = NULL; mmc_request_done(host->mmc, mrq); mutex_unlock(&host->thread_lock); return IRQ_HANDLED; }
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 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 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; }