static int __devexit mmc_omap_remove(struct platform_device *pdev) { struct mmc_omap_host *host = platform_get_drvdata(pdev); int i; platform_set_drvdata(pdev, NULL); BUG_ON(host == NULL); for (i = 0; i < host->nr_slots; i++) mmc_omap_remove_slot(host->slots[i]); if (host->pdata->cleanup) host->pdata->cleanup(&pdev->dev); mmc_omap_fclk_enable(host, 0); free_irq(host->irq, host); clk_put(host->fclk); clk_disable(host->iclk); clk_put(host->iclk); if (host->dma_tx) dma_release_channel(host->dma_tx); if (host->dma_rx) dma_release_channel(host->dma_rx); iounmap(host->virt_base); release_mem_region(pdev->resource[0].start, pdev->resource[0].end - pdev->resource[0].start + 1); destroy_workqueue(host->mmc_omap_wq); kfree(host); return 0; }
static void mmc_omap_clk_timer(unsigned long data) { struct mmc_omap_host *host = (struct mmc_omap_host *) data; mmc_omap_fclk_enable(host, 0); }
static int mmc_omap_remove(struct platform_device *pdev) { struct mmc_omap_host *host = platform_get_drvdata(pdev); int i; BUG_ON(host == NULL); for (i = 0; i < host->nr_slots; i++) mmc_omap_remove_slot(host->slots[i]); if (host->pdata->cleanup) host->pdata->cleanup(&pdev->dev); mmc_omap_fclk_enable(host, 0); free_irq(host->irq, host); clk_put(host->fclk); clk_disable(host->iclk); clk_put(host->iclk); if (host->dma_tx) dma_release_channel(host->dma_tx); if (host->dma_rx) dma_release_channel(host->dma_rx); destroy_workqueue(host->mmc_omap_wq); return 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); /* 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 void mmc_omap_release_slot(struct mmc_omap_slot *slot, int clk_enabled) { struct mmc_omap_host *host = slot->host; unsigned long flags; int i; BUG_ON(slot == NULL || host->mmc == NULL); if (clk_enabled) /* Keeps clock running for at least 8 cycles on valid freq */ mod_timer(&host->clk_timer, jiffies + HZ/10); else { del_timer(&host->clk_timer); mmc_omap_fclk_offdelay(slot); mmc_omap_fclk_enable(host, 0); } spin_lock_irqsave(&host->slot_lock, flags); /* Check for any pending requests */ for (i = 0; i < host->nr_slots; i++) { struct mmc_omap_slot *new_slot; if (host->slots[i] == NULL || host->slots[i]->mrq == NULL) continue; BUG_ON(host->next_slot != NULL); new_slot = host->slots[i]; /* The current slot should not have a request in queue */ BUG_ON(new_slot == host->current_slot); host->next_slot = new_slot; host->mmc = new_slot->mmc; spin_unlock_irqrestore(&host->slot_lock, flags); schedule_work(&host->slot_release_work); return; } host->mmc = NULL; wake_up(&host->slot_wq); spin_unlock_irqrestore(&host->slot_lock, flags); }
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); }