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 void mmc_omap_cmd_done(struct mmc_omap_host *host, struct mmc_command *cmd) { host->cmd = NULL; del_timer(&host->cmd_abort_timer); if (cmd->flags & MMC_RSP_PRESENT) { if (cmd->flags & MMC_RSP_136) { /* response type 2 */ cmd->resp[3] = OMAP_MMC_READ(host, RSP0) | (OMAP_MMC_READ(host, RSP1) << 16); cmd->resp[2] = OMAP_MMC_READ(host, RSP2) | (OMAP_MMC_READ(host, RSP3) << 16); cmd->resp[1] = OMAP_MMC_READ(host, RSP4) | (OMAP_MMC_READ(host, RSP5) << 16); cmd->resp[0] = OMAP_MMC_READ(host, RSP6) | (OMAP_MMC_READ(host, RSP7) << 16); } else { /* response types 1, 1b, 3, 4, 5, 6 */ cmd->resp[0] = OMAP_MMC_READ(host, RSP6) | (OMAP_MMC_READ(host, RSP7) << 16); } } if (host->data == NULL || cmd->error) { struct mmc_host *mmc; if (host->data != NULL) mmc_omap_abort_xfer(host, host->data); host->mrq = NULL; mmc = host->mmc; mmc_omap_release_slot(host->current_slot, 1); mmc_request_done(mmc, cmd->mrq); } }
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 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_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); }
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_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); }