summaryrefslogtreecommitdiffstats
path: root/drivers/mmc/host/omap_hsmmc.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mmc/host/omap_hsmmc.c')
-rw-r--r--drivers/mmc/host/omap_hsmmc.c91
1 files changed, 69 insertions, 22 deletions
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index b032828c6126..82a1079bbdc7 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -28,6 +28,7 @@
#include <linux/clk.h>
#include <linux/mmc/host.h>
#include <linux/mmc/core.h>
+#include <linux/mmc/mmc.h>
#include <linux/io.h>
#include <linux/semaphore.h>
#include <linux/gpio.h>
@@ -78,6 +79,7 @@
#define INT_EN_MASK 0x307F0033
#define BWR_ENABLE (1 << 4)
#define BRR_ENABLE (1 << 5)
+#define DTO_ENABLE (1 << 20)
#define INIT_STREAM (1 << 1)
#define DP_SELECT (1 << 21)
#define DDIR (1 << 4)
@@ -248,9 +250,9 @@ static int omap_hsmmc_1_set_power(struct device *dev, int slot, int power_on,
mmc_slot(host).before_set_reg(dev, slot, power_on, vdd);
if (power_on)
- ret = mmc_regulator_set_ocr(host->vcc, vdd);
+ ret = mmc_regulator_set_ocr(host->mmc, host->vcc, vdd);
else
- ret = mmc_regulator_set_ocr(host->vcc, 0);
+ ret = mmc_regulator_set_ocr(host->mmc, host->vcc, 0);
if (mmc_slot(host).after_set_reg)
mmc_slot(host).after_set_reg(dev, slot, power_on, vdd);
@@ -289,18 +291,23 @@ static int omap_hsmmc_23_set_power(struct device *dev, int slot, int power_on,
* chips/cards need an interface voltage rail too.
*/
if (power_on) {
- ret = mmc_regulator_set_ocr(host->vcc, vdd);
+ ret = mmc_regulator_set_ocr(host->mmc, host->vcc, vdd);
/* Enable interface voltage rail, if needed */
if (ret == 0 && host->vcc_aux) {
ret = regulator_enable(host->vcc_aux);
if (ret < 0)
- ret = mmc_regulator_set_ocr(host->vcc, 0);
+ ret = mmc_regulator_set_ocr(host->mmc,
+ host->vcc, 0);
}
} else {
+ /* Shut down the rail */
if (host->vcc_aux)
ret = regulator_disable(host->vcc_aux);
- if (ret == 0)
- ret = mmc_regulator_set_ocr(host->vcc, 0);
+ if (!ret) {
+ /* Then proceed to shut down the local regulator */
+ ret = mmc_regulator_set_ocr(host->mmc,
+ host->vcc, 0);
+ }
}
if (mmc_slot(host).after_set_reg)
@@ -341,9 +348,9 @@ static int omap_hsmmc_23_set_sleep(struct device *dev, int slot, int sleep,
if (cardsleep) {
/* VCC can be turned off if card is asleep */
if (sleep)
- err = mmc_regulator_set_ocr(host->vcc, 0);
+ err = mmc_regulator_set_ocr(host->mmc, host->vcc, 0);
else
- err = mmc_regulator_set_ocr(host->vcc, vdd);
+ err = mmc_regulator_set_ocr(host->mmc, host->vcc, vdd);
} else
err = regulator_set_mode(host->vcc, mode);
if (err)
@@ -362,6 +369,7 @@ static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host)
{
struct regulator *reg;
int ret = 0;
+ int ocr_value = 0;
switch (host->id) {
case OMAP_MMC1_DEVID:
@@ -394,6 +402,17 @@ static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host)
}
} else {
host->vcc = reg;
+ ocr_value = mmc_regulator_get_ocrmask(reg);
+ if (!mmc_slot(host).ocr_mask) {
+ mmc_slot(host).ocr_mask = ocr_value;
+ } else {
+ if (!(mmc_slot(host).ocr_mask & ocr_value)) {
+ pr_err("MMC%d ocrmask %x is not supported\n",
+ host->id, mmc_slot(host).ocr_mask);
+ mmc_slot(host).ocr_mask = 0;
+ return -EINVAL;
+ }
+ }
mmc_slot(host).ocr_mask = mmc_regulator_get_ocrmask(reg);
/* Allow an aux regulator */
@@ -464,8 +483,6 @@ static int omap_hsmmc_gpio_init(struct omap_mmc_platform_data *pdata)
int ret;
if (gpio_is_valid(pdata->slots[0].switch_pin)) {
- pdata->suspend = omap_hsmmc_suspend_cdirq;
- pdata->resume = omap_hsmmc_resume_cdirq;
if (pdata->slots[0].cover)
pdata->slots[0].get_cover_state =
omap_hsmmc_get_cover_state;
@@ -523,7 +540,8 @@ static void omap_hsmmc_stop_clock(struct omap_hsmmc_host *host)
dev_dbg(mmc_dev(host->mmc), "MMC Clock is not stoped\n");
}
-static void omap_hsmmc_enable_irq(struct omap_hsmmc_host *host)
+static void omap_hsmmc_enable_irq(struct omap_hsmmc_host *host,
+ struct mmc_command *cmd)
{
unsigned int irq_mask;
@@ -532,6 +550,10 @@ static void omap_hsmmc_enable_irq(struct omap_hsmmc_host *host)
else
irq_mask = INT_EN_MASK;
+ /* Disable timeout for erases */
+ if (cmd->opcode == MMC_ERASE)
+ irq_mask &= ~DTO_ENABLE;
+
OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
OMAP_HSMMC_WRITE(host->base, ISE, irq_mask);
OMAP_HSMMC_WRITE(host->base, IE, irq_mask);
@@ -782,7 +804,7 @@ omap_hsmmc_start_command(struct omap_hsmmc_host *host, struct mmc_command *cmd,
mmc_hostname(host->mmc), cmd->opcode, cmd->arg);
host->cmd = cmd;
- omap_hsmmc_enable_irq(host);
+ omap_hsmmc_enable_irq(host, cmd);
host->response_busy = 0;
if (cmd->flags & MMC_RSP_PRESENT) {
@@ -975,6 +997,17 @@ static inline void omap_hsmmc_reset_controller_fsm(struct omap_hsmmc_host *host,
OMAP_HSMMC_WRITE(host->base, SYSCTL,
OMAP_HSMMC_READ(host->base, SYSCTL) | bit);
+ /*
+ * OMAP4 ES2 and greater has an updated reset logic.
+ * Monitor a 0->1 transition first
+ */
+ if (mmc_slot(host).features & HSMMC_HAS_UPDATED_RESET) {
+ while ((!(OMAP_HSMMC_READ(host, SYSCTL) & bit))
+ && (i++ < limit))
+ cpu_relax();
+ }
+ i = 0;
+
while ((OMAP_HSMMC_READ(host->base, SYSCTL) & bit) &&
(i++ < limit))
cpu_relax();
@@ -1273,8 +1306,11 @@ static void omap_hsmmc_dma_cb(int lch, u16 ch_status, void *cb_data)
struct mmc_data *data = host->mrq->data;
int dma_ch, req_in_progress;
- if (ch_status & OMAP2_DMA_MISALIGNED_ERR_IRQ)
- dev_dbg(mmc_dev(host->mmc), "MISALIGNED_ADRS_ERR\n");
+ if (!(ch_status & OMAP_DMA_BLOCK_IRQ)) {
+ dev_warn(mmc_dev(host->mmc), "unexpected dma status %x\n",
+ ch_status);
+ return;
+ }
spin_lock(&host->irq_lock);
if (host->dma_ch < 0) {
@@ -1598,6 +1634,14 @@ static int omap_hsmmc_get_ro(struct mmc_host *mmc)
return mmc_slot(host).get_ro(host->dev, 0);
}
+static void omap_hsmmc_init_card(struct mmc_host *mmc, struct mmc_card *card)
+{
+ struct omap_hsmmc_host *host = mmc_priv(mmc);
+
+ if (mmc_slot(host).init_card)
+ mmc_slot(host).init_card(card);
+}
+
static void omap_hsmmc_conf_bus_power(struct omap_hsmmc_host *host)
{
u32 hctl, capa, value;
@@ -1869,6 +1913,7 @@ static const struct mmc_host_ops omap_hsmmc_ops = {
.set_ios = omap_hsmmc_set_ios,
.get_cd = omap_hsmmc_get_cd,
.get_ro = omap_hsmmc_get_ro,
+ .init_card = omap_hsmmc_init_card,
/* NYET -- enable_sdio_irq */
};
@@ -1879,6 +1924,7 @@ static const struct mmc_host_ops omap_hsmmc_ps_ops = {
.set_ios = omap_hsmmc_set_ios,
.get_cd = omap_hsmmc_get_cd,
.get_ro = omap_hsmmc_get_ro,
+ .init_card = omap_hsmmc_init_card,
/* NYET -- enable_sdio_irq */
};
@@ -1983,6 +2029,8 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
if (res == NULL || irq < 0)
return -ENXIO;
+ res->start += pdata->reg_offset;
+ res->end += pdata->reg_offset;
res = request_mem_region(res->start, res->end - res->start + 1,
pdev->name);
if (res == NULL)
@@ -2085,8 +2133,7 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
/* Since we do only SG emulation, we can have as many segs
* as we want. */
- mmc->max_phys_segs = 1024;
- mmc->max_hw_segs = 1024;
+ mmc->max_segs = 1024;
mmc->max_blk_size = 512; /* Block Length at max can be 1024 */
mmc->max_blk_count = 0xFFFF; /* No. of Blocks is 16 bits */
@@ -2094,11 +2141,10 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
mmc->max_seg_size = mmc->max_req_size;
mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED |
- MMC_CAP_WAIT_WHILE_BUSY;
+ MMC_CAP_WAIT_WHILE_BUSY | MMC_CAP_ERASE;
- if (mmc_slot(host).wires >= 8)
- mmc->caps |= MMC_CAP_8_BIT_DATA;
- else if (mmc_slot(host).wires >= 4)
+ mmc->caps |= mmc_slot(host).caps;
+ if (mmc->caps & MMC_CAP_8_BIT_DATA)
mmc->caps |= MMC_CAP_4_BIT_DATA;
if (mmc_slot(host).nonremovable)
@@ -2170,6 +2216,8 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
"Unable to grab MMC CD IRQ\n");
goto err_irq_cd;
}
+ pdata->suspend = omap_hsmmc_suspend_cdirq;
+ pdata->resume = omap_hsmmc_resume_cdirq;
}
omap_hsmmc_disable_irq(host);
@@ -2272,7 +2320,6 @@ static int omap_hsmmc_suspend(struct device *dev)
int ret = 0;
struct platform_device *pdev = to_platform_device(dev);
struct omap_hsmmc_host *host = platform_get_drvdata(pdev);
- pm_message_t state = PMSG_SUSPEND; /* unused by MMC core */
if (host && host->suspended)
return 0;
@@ -2291,8 +2338,8 @@ static int omap_hsmmc_suspend(struct device *dev)
}
}
cancel_work_sync(&host->mmc_carddetect_work);
- mmc_host_enable(host->mmc);
ret = mmc_suspend_host(host->mmc);
+ mmc_host_enable(host->mmc);
if (ret == 0) {
omap_hsmmc_disable_irq(host);
OMAP_HSMMC_WRITE(host->base, HCTL,