static void mmc_omap_send_abort(struct mmc_omap_host *host, int maxloops) { struct mmc_omap_slot *slot = host->current_slot; unsigned int restarts, passes, timeout; u16 stat = 0; /* Sending abort takes 80 clocks. Have some extra and round up */ timeout = (120*1000000 + slot->fclk_freq - 1)/slot->fclk_freq; restarts = 0; while (restarts < maxloops) { OMAP_MMC_WRITE(host, STAT, 0xFFFF); OMAP_MMC_WRITE(host, CMD, (3 << 12) | (1 << 7)); passes = 0; while (passes < timeout) { stat = OMAP_MMC_READ(host, STAT); if (stat & OMAP_MMC_STAT_END_OF_CMD) goto out; udelay(1); passes++; } restarts++; } out: OMAP_MMC_WRITE(host, STAT, stat); }
static inline void set_cmd_timeout(struct mmc_omap_host *host, struct mmc_request *req) { u16 reg; reg = OMAP_MMC_READ(host, SDIO); reg &= ~(1 << 5); OMAP_MMC_WRITE(host, SDIO, reg); /* Set maximum timeout */ OMAP_MMC_WRITE(host, CTO, 0xff); }
static inline void set_data_timeout(struct mmc_omap_host *host, struct mmc_request *req) { unsigned int timeout, cycle_ns; u16 reg; cycle_ns = 1000000000 / host->current_slot->fclk_freq; timeout = req->data->timeout_ns / cycle_ns; timeout += req->data->timeout_clks; /* Check if we need to use timeout multiplier register */ reg = OMAP_MMC_READ(host, SDIO); if (timeout > 0xffff) { reg |= (1 << 5); timeout /= 1024; } else reg &= ~(1 << 5); OMAP_MMC_WRITE(host, SDIO, reg); OMAP_MMC_WRITE(host, DTO, timeout); }
static void mmc_omap_select_slot(struct mmc_omap_slot *slot, int claimed) { struct mmc_omap_host *host = slot->host; unsigned long flags; if (claimed) goto no_claim; spin_lock_irqsave(&host->slot_lock, flags); while (host->mmc != NULL) { spin_unlock_irqrestore(&host->slot_lock, flags); wait_event(host->slot_wq, host->mmc == NULL); spin_lock_irqsave(&host->slot_lock, flags); } host->mmc = slot->mmc; spin_unlock_irqrestore(&host->slot_lock, flags); no_claim: del_timer(&host->clk_timer); if (host->current_slot != slot || !claimed) mmc_omap_fclk_offdelay(host->current_slot); if (host->current_slot != slot) { OMAP_MMC_WRITE(host, CON, slot->saved_con & 0xFC00); if (host->pdata->switch_slot != NULL) host->pdata->switch_slot(mmc_dev(slot->mmc), slot->id); host->current_slot = slot; } if (claimed) { mmc_omap_fclk_enable(host, 1); /* Doing the dummy read here seems to work around some bug * at least in OMAP24xx silicon where the command would not * start after writing the CMD register. Sigh. */ OMAP_MMC_READ(host, CON); OMAP_MMC_WRITE(host, CON, slot->saved_con); } else mmc_omap_fclk_enable(host, 0); }
static void mmc_omap_set_power(struct mmc_omap_slot *slot, int power_on, int vdd) { struct mmc_omap_host *host; host = slot->host; if (slot->pdata->set_power != NULL) slot->pdata->set_power(mmc_dev(slot->mmc), slot->id, power_on, vdd); if (mmc_omap2()) { u16 w; if (power_on) { w = OMAP_MMC_READ(host, CON); OMAP_MMC_WRITE(host, CON, w | (1 << 11)); } else { w = OMAP_MMC_READ(host, CON); OMAP_MMC_WRITE(host, CON, w & ~(1 << 11)); } } }
static void mmc_omap_cmd_timer(unsigned long data) { struct mmc_omap_host *host = (struct mmc_omap_host *) data; unsigned long flags; spin_lock_irqsave(&host->slot_lock, flags); if (host->cmd != NULL && !host->abort) { OMAP_MMC_WRITE(host, IE, 0); disable_irq(host->irq); host->abort = 1; schedule_work(&host->cmd_abort_work); } spin_unlock_irqrestore(&host->slot_lock, flags); }
static void mmc_omap_select_slot(struct mmc_omap_slot *slot, int claimed) { struct mmc_omap_host *host = slot->host; unsigned long flags; if (claimed) goto no_claim; spin_lock_irqsave(&host->slot_lock, flags); while (host->mmc != NULL) { spin_unlock_irqrestore(&host->slot_lock, flags); wait_event(host->slot_wq, host->mmc == NULL); spin_lock_irqsave(&host->slot_lock, flags); } host->mmc = slot->mmc; spin_unlock_irqrestore(&host->slot_lock, flags); no_claim: del_timer(&host->clk_timer); if (host->current_slot != slot || !claimed) mmc_omap_fclk_offdelay(host->current_slot); if (host->current_slot != slot) { OMAP_MMC_WRITE(host, CON, slot->saved_con & 0xFC00); if (host->pdata->switch_slot != NULL) host->pdata->switch_slot(mmc_dev(slot->mmc), slot->id); host->current_slot = slot; } if (claimed) { mmc_omap_fclk_enable(host, 1); OMAP_MMC_READ(host, CON); OMAP_MMC_WRITE(host, CON, slot->saved_con); } else mmc_omap_fclk_enable(host, 0); }
/* Prepare to transfer the next segment of a scatterlist */ static void mmc_omap_prepare_dma(struct mmc_omap_host *host, struct mmc_data *data) { int dma_ch = host->dma_ch; unsigned long data_addr; u16 buf, frame; u32 count; struct scatterlist *sg = &data->sg[host->sg_idx]; int src_port = 0; int dst_port = 0; int sync_dev = 0; data_addr = host->phys_base + OMAP_MMC_REG_DATA; frame = data->blksz; count = sg_dma_len(sg); if ((data->blocks == 1) && (count > data->blksz)) count = frame; host->dma_len = count; /* FIFO is 16x2 bytes on 15xx, and 32x2 bytes on 16xx and 24xx. * Use 16 or 32 word frames when the blocksize is at least that large. * Blocksize is usually 512 bytes; but not for some SD reads. */ if (cpu_is_omap15xx() && frame > 32) frame = 32; else if (frame > 64) frame = 64; count /= frame; frame >>= 1; if (!(data->flags & MMC_DATA_WRITE)) { buf = 0x800f | ((frame - 1) << 8); if (cpu_class_is_omap1()) { src_port = OMAP_DMA_PORT_TIPB; dst_port = OMAP_DMA_PORT_EMIFF; } if (cpu_is_omap24xx()) sync_dev = OMAP24XX_DMA_MMC1_RX; omap_set_dma_src_params(dma_ch, src_port, OMAP_DMA_AMODE_CONSTANT, data_addr, 0, 0); omap_set_dma_dest_params(dma_ch, dst_port, OMAP_DMA_AMODE_POST_INC, sg_dma_address(sg), 0, 0); omap_set_dma_dest_data_pack(dma_ch, 1); omap_set_dma_dest_burst_mode(dma_ch, OMAP_DMA_DATA_BURST_4); } else { buf = 0x0f80 | ((frame - 1) << 0); if (cpu_class_is_omap1()) { src_port = OMAP_DMA_PORT_EMIFF; dst_port = OMAP_DMA_PORT_TIPB; } if (cpu_is_omap24xx()) sync_dev = OMAP24XX_DMA_MMC1_TX; omap_set_dma_dest_params(dma_ch, dst_port, OMAP_DMA_AMODE_CONSTANT, data_addr, 0, 0); omap_set_dma_src_params(dma_ch, src_port, OMAP_DMA_AMODE_POST_INC, sg_dma_address(sg), 0, 0); omap_set_dma_src_data_pack(dma_ch, 1); omap_set_dma_src_burst_mode(dma_ch, OMAP_DMA_DATA_BURST_4); } /* Max limit for DMA frame count is 0xffff */ BUG_ON(count > 0xffff); OMAP_MMC_WRITE(host, BUF, buf); omap_set_dma_transfer_params(dma_ch, OMAP_DMA_DATA_TYPE_S16, frame, count, OMAP_DMA_SYNC_FRAME, sync_dev, 0); }
static irqreturn_t mmc_omap_irq(int irq, void *dev_id) { struct mmc_omap_host * host = (struct mmc_omap_host *)dev_id; u16 status; int end_command; int end_transfer; int transfer_error, cmd_error; if (host->cmd == NULL && host->data == NULL) { status = OMAP_MMC_READ(host, STAT); dev_info(mmc_dev(host->slots[0]->mmc), "Spurious IRQ 0x%04x\n", status); if (status != 0) { OMAP_MMC_WRITE(host, STAT, status); OMAP_MMC_WRITE(host, IE, 0); } return IRQ_HANDLED; } end_command = 0; end_transfer = 0; transfer_error = 0; cmd_error = 0; while ((status = OMAP_MMC_READ(host, STAT)) != 0) { int cmd; OMAP_MMC_WRITE(host, STAT, status); if (host->cmd != NULL) cmd = host->cmd->opcode; else cmd = -1; #ifdef CONFIG_MMC_DEBUG dev_dbg(mmc_dev(host->mmc), "MMC IRQ %04x (CMD %d): ", status, cmd); mmc_omap_report_irq(status); printk("\n"); #endif if (host->total_bytes_left) { if ((status & OMAP_MMC_STAT_A_FULL) || (status & OMAP_MMC_STAT_END_OF_DATA)) mmc_omap_xfer_data(host, 0); if (status & OMAP_MMC_STAT_A_EMPTY) mmc_omap_xfer_data(host, 1); } if (status & OMAP_MMC_STAT_END_OF_DATA) end_transfer = 1; if (status & OMAP_MMC_STAT_DATA_TOUT) { dev_dbg(mmc_dev(host->mmc), "data timeout (CMD%d)\n", cmd); if (host->data) { host->data->error = -ETIMEDOUT; transfer_error = 1; } } if (status & OMAP_MMC_STAT_DATA_CRC) { if (host->data) { host->data->error = -EILSEQ; dev_dbg(mmc_dev(host->mmc), "data CRC error, bytes left %d\n", host->total_bytes_left); transfer_error = 1; } else { dev_dbg(mmc_dev(host->mmc), "data CRC error\n"); } } if (status & OMAP_MMC_STAT_CMD_TOUT) { /* Timeouts are routine with some commands */ if (host->cmd) { struct mmc_omap_slot *slot = host->current_slot; if (slot == NULL || !mmc_omap_cover_is_open(slot)) dev_err(mmc_dev(host->mmc), "command timeout (CMD%d)\n", cmd); host->cmd->error = -ETIMEDOUT; end_command = 1; cmd_error = 1; } } if (status & OMAP_MMC_STAT_CMD_CRC) { if (host->cmd) { dev_err(mmc_dev(host->mmc), "command CRC error (CMD%d, arg 0x%08x)\n", cmd, host->cmd->arg); host->cmd->error = -EILSEQ; end_command = 1; cmd_error = 1; } else dev_err(mmc_dev(host->mmc), "command CRC error without cmd?\n"); } if (status & OMAP_MMC_STAT_CARD_ERR) { dev_dbg(mmc_dev(host->mmc), "ignoring card status error (CMD%d)\n", cmd); end_command = 1; } /* * NOTE: On 1610 the END_OF_CMD may come too early when * starting a write */ if ((status & OMAP_MMC_STAT_END_OF_CMD) && (!(status & OMAP_MMC_STAT_A_EMPTY))) { end_command = 1; } } if (cmd_error && host->data) { del_timer(&host->cmd_abort_timer); host->abort = 1; OMAP_MMC_WRITE(host, IE, 0); disable_irq_nosync(host->irq); schedule_work(&host->cmd_abort_work); return IRQ_HANDLED; } if (end_command) mmc_omap_cmd_done(host, host->cmd); if (host->data != NULL) { if (transfer_error) mmc_omap_xfer_done(host, host->data); else if (end_transfer) mmc_omap_end_of_data(host, host->data); } return IRQ_HANDLED; }
static void mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd) { u32 cmdreg; u32 resptype; u32 cmdtype; host->cmd = cmd; resptype = 0; cmdtype = 0; /* Our hardware needs to know exact type */ switch (mmc_resp_type(cmd)) { case MMC_RSP_NONE: break; case MMC_RSP_R1: case MMC_RSP_R1B: /* resp 1, 1b, 6, 7 */ resptype = 1; break; case MMC_RSP_R2: resptype = 2; break; case MMC_RSP_R3: resptype = 3; break; default: dev_err(mmc_dev(host->mmc), "Invalid response type: %04x\n", mmc_resp_type(cmd)); break; } if (mmc_cmd_type(cmd) == MMC_CMD_ADTC) { cmdtype = OMAP_MMC_CMDTYPE_ADTC; } else if (mmc_cmd_type(cmd) == MMC_CMD_BC) { cmdtype = OMAP_MMC_CMDTYPE_BC; } else if (mmc_cmd_type(cmd) == MMC_CMD_BCR) { cmdtype = OMAP_MMC_CMDTYPE_BCR; } else { cmdtype = OMAP_MMC_CMDTYPE_AC; } cmdreg = cmd->opcode | (resptype << 8) | (cmdtype << 12); if (host->current_slot->bus_mode == MMC_BUSMODE_OPENDRAIN) cmdreg |= 1 << 6; if (cmd->flags & MMC_RSP_BUSY) cmdreg |= 1 << 11; if (host->data && !(host->data->flags & MMC_DATA_WRITE)) cmdreg |= 1 << 15; mod_timer(&host->cmd_abort_timer, jiffies + HZ/2); OMAP_MMC_WRITE(host, CTO, 200); OMAP_MMC_WRITE(host, ARGL, cmd->arg & 0xffff); OMAP_MMC_WRITE(host, ARGH, cmd->arg >> 16); OMAP_MMC_WRITE(host, IE, OMAP_MMC_STAT_A_EMPTY | OMAP_MMC_STAT_A_FULL | OMAP_MMC_STAT_CMD_CRC | OMAP_MMC_STAT_CMD_TOUT | OMAP_MMC_STAT_DATA_CRC | OMAP_MMC_STAT_DATA_TOUT | OMAP_MMC_STAT_END_OF_CMD | OMAP_MMC_STAT_CARD_ERR | OMAP_MMC_STAT_END_OF_DATA); OMAP_MMC_WRITE(host, CMD, cmdreg); }
static void mmc_omap_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) { struct mmc_omap_slot *slot = mmc_priv(mmc); struct mmc_omap_host *host = slot->host; int i, dsor; int clk_enabled; mmc_omap_select_slot(slot, 0); dsor = mmc_omap_calc_divisor(mmc, ios); if (ios->vdd != slot->vdd) slot->vdd = ios->vdd; clk_enabled = 0; switch (ios->power_mode) { case MMC_POWER_OFF: mmc_omap_set_power(slot, 0, ios->vdd); break; case MMC_POWER_UP: /* Cannot touch dsor yet, just power up MMC */ mmc_omap_set_power(slot, 1, ios->vdd); goto exit; case MMC_POWER_ON: mmc_omap_fclk_enable(host, 1); clk_enabled = 1; dsor |= 1 << 11; break; } if (slot->bus_mode != ios->bus_mode) { if (slot->pdata->set_bus_mode != NULL) slot->pdata->set_bus_mode(mmc_dev(mmc), slot->id, ios->bus_mode); slot->bus_mode = ios->bus_mode; } /* On insanely high arm_per frequencies something sometimes * goes somehow out of sync, and the POW bit is not being set, * which results in the while loop below getting stuck. * Writing to the CON register twice seems to do the trick. */ for (i = 0; i < 2; i++) OMAP_MMC_WRITE(host, CON, dsor); slot->saved_con = dsor; if (ios->power_mode == MMC_POWER_ON) { /* worst case at 400kHz, 80 cycles makes 200 microsecs */ int usecs = 250; /* Send clock cycles, poll completion */ OMAP_MMC_WRITE(host, IE, 0); OMAP_MMC_WRITE(host, STAT, 0xffff); OMAP_MMC_WRITE(host, CMD, 1 << 7); while (usecs > 0 && (OMAP_MMC_READ(host, STAT) & 1) == 0) { udelay(1); usecs--; } OMAP_MMC_WRITE(host, STAT, 1); } exit: mmc_omap_release_slot(slot, clk_enabled); }
static void mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req) { struct mmc_data *data = req->data; int i, use_dma, block_size; unsigned sg_len; host->data = data; if (data == NULL) { OMAP_MMC_WRITE(host, BLEN, 0); OMAP_MMC_WRITE(host, NBLK, 0); OMAP_MMC_WRITE(host, BUF, 0); host->dma_in_use = 0; set_cmd_timeout(host, req); return; } block_size = data->blksz; OMAP_MMC_WRITE(host, NBLK, data->blocks - 1); OMAP_MMC_WRITE(host, BLEN, block_size - 1); set_data_timeout(host, req); /* cope with calling layer confusion; it issues "single * block" writes using multi-block scatterlists. */ sg_len = (data->blocks == 1) ? 1 : data->sg_len; /* Only do DMA for entire blocks */ use_dma = host->use_dma; if (use_dma) { for (i = 0; i < sg_len; i++) { if ((data->sg[i].length % block_size) != 0) { use_dma = 0; break; } } } host->sg_idx = 0; if (use_dma) { if (mmc_omap_get_dma_channel(host, data) == 0) { enum dma_data_direction dma_data_dir; if (data->flags & MMC_DATA_WRITE) dma_data_dir = DMA_TO_DEVICE; else dma_data_dir = DMA_FROM_DEVICE; host->sg_len = dma_map_sg(mmc_dev(host->mmc), data->sg, sg_len, dma_data_dir); host->total_bytes_left = 0; mmc_omap_prepare_dma(host, req->data); host->brs_received = 0; host->dma_done = 0; host->dma_in_use = 1; } else use_dma = 0; } /* Revert to PIO? */ if (!use_dma) { OMAP_MMC_WRITE(host, BUF, 0x1f1f); host->total_bytes_left = data->blocks * block_size; host->sg_len = sg_len; mmc_omap_sg_to_buf(host); host->dma_in_use = 0; } }
static void mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req) { struct mmc_data *data = req->data; int i, use_dma, block_size; unsigned sg_len; host->data = data; if (data == NULL) { OMAP_MMC_WRITE(host, BLEN, 0); OMAP_MMC_WRITE(host, NBLK, 0); OMAP_MMC_WRITE(host, BUF, 0); host->dma_in_use = 0; set_cmd_timeout(host, req); return; } block_size = data->blksz; OMAP_MMC_WRITE(host, NBLK, data->blocks - 1); OMAP_MMC_WRITE(host, BLEN, block_size - 1); set_data_timeout(host, req); /* cope with calling layer confusion; it issues "single * block" writes using multi-block scatterlists. */ sg_len = (data->blocks == 1) ? 1 : data->sg_len; /* Only do DMA for entire blocks */ use_dma = host->use_dma; if (use_dma) { for (i = 0; i < sg_len; i++) { if ((data->sg[i].length % block_size) != 0) { use_dma = 0; break; } } } host->sg_idx = 0; if (use_dma) { enum dma_data_direction dma_data_dir; struct dma_async_tx_descriptor *tx; struct dma_chan *c; u32 burst, *bp; u16 buf; /* * FIFO is 16x2 bytes on 15xx, and 32x2 bytes on 16xx * and 24xx. Use 16 or 32 word frames when the * blocksize is at least that large. Blocksize is * usually 512 bytes; but not for some SD reads. */ burst = cpu_is_omap15xx() ? 32 : 64; if (burst > data->blksz) burst = data->blksz; burst >>= 1; if (data->flags & MMC_DATA_WRITE) { c = host->dma_tx; bp = &host->dma_tx_burst; buf = 0x0f80 | (burst - 1) << 0; dma_data_dir = DMA_TO_DEVICE; } else { c = host->dma_rx; bp = &host->dma_rx_burst; buf = 0x800f | (burst - 1) << 8; dma_data_dir = DMA_FROM_DEVICE; } if (!c) goto use_pio; /* Only reconfigure if we have a different burst size */ if (*bp != burst) { struct dma_slave_config cfg; cfg.src_addr = host->phys_base + OMAP_MMC_REG(host, DATA); cfg.dst_addr = host->phys_base + OMAP_MMC_REG(host, DATA); cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; cfg.src_maxburst = burst; cfg.dst_maxburst = burst; if (dmaengine_slave_config(c, &cfg)) goto use_pio; *bp = burst; } host->sg_len = dma_map_sg(c->device->dev, data->sg, sg_len, dma_data_dir); if (host->sg_len == 0) goto use_pio; tx = dmaengine_prep_slave_sg(c, data->sg, host->sg_len, data->flags & MMC_DATA_WRITE ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!tx) goto use_pio; OMAP_MMC_WRITE(host, BUF, buf); tx->callback = mmc_omap_dma_callback; tx->callback_param = host; dmaengine_submit(tx); host->brs_received = 0; host->dma_done = 0; host->dma_in_use = 1; return; } use_pio: /* Revert to PIO? */ OMAP_MMC_WRITE(host, BUF, 0x1f1f); host->total_bytes_left = data->blocks * block_size; host->sg_len = sg_len; mmc_omap_sg_to_buf(host); host->dma_in_use = 0; }