|
@@ -17,6 +17,7 @@
|
|
|
|
|
|
#include <linux/module.h>
|
|
|
#include <linux/init.h>
|
|
|
+#include <linux/kernel.h>
|
|
|
#include <linux/debugfs.h>
|
|
|
#include <linux/seq_file.h>
|
|
|
#include <linux/interrupt.h>
|
|
@@ -33,6 +34,7 @@
|
|
|
#include <linux/semaphore.h>
|
|
|
#include <linux/gpio.h>
|
|
|
#include <linux/regulator/consumer.h>
|
|
|
+#include <linux/pm_runtime.h>
|
|
|
#include <plat/dma.h>
|
|
|
#include <mach/hardware.h>
|
|
|
#include <plat/board.h>
|
|
@@ -116,15 +118,13 @@
|
|
|
#define OMAP_MMC4_DEVID 3
|
|
|
#define OMAP_MMC5_DEVID 4
|
|
|
|
|
|
+#define MMC_AUTOSUSPEND_DELAY 100
|
|
|
#define MMC_TIMEOUT_MS 20
|
|
|
#define OMAP_MMC_MASTER_CLOCK 96000000
|
|
|
+#define OMAP_MMC_MIN_CLOCK 400000
|
|
|
+#define OMAP_MMC_MAX_CLOCK 52000000
|
|
|
#define DRIVER_NAME "omap_hsmmc"
|
|
|
|
|
|
-/* Timeouts for entering power saving states on inactivity, msec */
|
|
|
-#define OMAP_MMC_DISABLED_TIMEOUT 100
|
|
|
-#define OMAP_MMC_SLEEP_TIMEOUT 1000
|
|
|
-#define OMAP_MMC_OFF_TIMEOUT 8000
|
|
|
-
|
|
|
/*
|
|
|
* One controller can have multiple slots, like on some omap boards using
|
|
|
* omap.c controller driver. Luckily this is not currently done on any known
|
|
@@ -141,6 +141,11 @@
|
|
|
#define OMAP_HSMMC_WRITE(base, reg, val) \
|
|
|
__raw_writel((val), (base) + OMAP_HSMMC_##reg)
|
|
|
|
|
|
+struct omap_hsmmc_next {
|
|
|
+ unsigned int dma_len;
|
|
|
+ s32 cookie;
|
|
|
+};
|
|
|
+
|
|
|
struct omap_hsmmc_host {
|
|
|
struct device *dev;
|
|
|
struct mmc_host *mmc;
|
|
@@ -148,7 +153,6 @@ struct omap_hsmmc_host {
|
|
|
struct mmc_command *cmd;
|
|
|
struct mmc_data *data;
|
|
|
struct clk *fclk;
|
|
|
- struct clk *iclk;
|
|
|
struct clk *dbclk;
|
|
|
/*
|
|
|
* vcc == configured supply
|
|
@@ -184,6 +188,7 @@ struct omap_hsmmc_host {
|
|
|
int reqs_blocked;
|
|
|
int use_reg;
|
|
|
int req_in_progress;
|
|
|
+ struct omap_hsmmc_next next_data;
|
|
|
|
|
|
struct omap_mmc_platform_data *pdata;
|
|
|
};
|
|
@@ -547,6 +552,15 @@ static void omap_hsmmc_gpio_free(struct omap_mmc_platform_data *pdata)
|
|
|
gpio_free(pdata->slots[0].switch_pin);
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * Start clock to the card
|
|
|
+ */
|
|
|
+static void omap_hsmmc_start_clock(struct omap_hsmmc_host *host)
|
|
|
+{
|
|
|
+ OMAP_HSMMC_WRITE(host->base, SYSCTL,
|
|
|
+ OMAP_HSMMC_READ(host->base, SYSCTL) | CEN);
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* Stop clock to the card
|
|
|
*/
|
|
@@ -584,6 +598,81 @@ static void omap_hsmmc_disable_irq(struct omap_hsmmc_host *host)
|
|
|
OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
|
|
|
}
|
|
|
|
|
|
+/* Calculate divisor for the given clock frequency */
|
|
|
+static u16 calc_divisor(struct mmc_ios *ios)
|
|
|
+{
|
|
|
+ u16 dsor = 0;
|
|
|
+
|
|
|
+ if (ios->clock) {
|
|
|
+ dsor = DIV_ROUND_UP(OMAP_MMC_MASTER_CLOCK, ios->clock);
|
|
|
+ if (dsor > 250)
|
|
|
+ dsor = 250;
|
|
|
+ }
|
|
|
+
|
|
|
+ return dsor;
|
|
|
+}
|
|
|
+
|
|
|
+static void omap_hsmmc_set_clock(struct omap_hsmmc_host *host)
|
|
|
+{
|
|
|
+ struct mmc_ios *ios = &host->mmc->ios;
|
|
|
+ unsigned long regval;
|
|
|
+ unsigned long timeout;
|
|
|
+
|
|
|
+ dev_dbg(mmc_dev(host->mmc), "Set clock to %uHz\n", ios->clock);
|
|
|
+
|
|
|
+ omap_hsmmc_stop_clock(host);
|
|
|
+
|
|
|
+ regval = OMAP_HSMMC_READ(host->base, SYSCTL);
|
|
|
+ regval = regval & ~(CLKD_MASK | DTO_MASK);
|
|
|
+ regval = regval | (calc_divisor(ios) << 6) | (DTO << 16);
|
|
|
+ OMAP_HSMMC_WRITE(host->base, SYSCTL, regval);
|
|
|
+ OMAP_HSMMC_WRITE(host->base, SYSCTL,
|
|
|
+ OMAP_HSMMC_READ(host->base, SYSCTL) | ICE);
|
|
|
+
|
|
|
+ /* Wait till the ICS bit is set */
|
|
|
+ timeout = jiffies + msecs_to_jiffies(MMC_TIMEOUT_MS);
|
|
|
+ while ((OMAP_HSMMC_READ(host->base, SYSCTL) & ICS) != ICS
|
|
|
+ && time_before(jiffies, timeout))
|
|
|
+ cpu_relax();
|
|
|
+
|
|
|
+ omap_hsmmc_start_clock(host);
|
|
|
+}
|
|
|
+
|
|
|
+static void omap_hsmmc_set_bus_width(struct omap_hsmmc_host *host)
|
|
|
+{
|
|
|
+ struct mmc_ios *ios = &host->mmc->ios;
|
|
|
+ u32 con;
|
|
|
+
|
|
|
+ con = OMAP_HSMMC_READ(host->base, CON);
|
|
|
+ switch (ios->bus_width) {
|
|
|
+ case MMC_BUS_WIDTH_8:
|
|
|
+ OMAP_HSMMC_WRITE(host->base, CON, con | DW8);
|
|
|
+ break;
|
|
|
+ case MMC_BUS_WIDTH_4:
|
|
|
+ OMAP_HSMMC_WRITE(host->base, CON, con & ~DW8);
|
|
|
+ OMAP_HSMMC_WRITE(host->base, HCTL,
|
|
|
+ OMAP_HSMMC_READ(host->base, HCTL) | FOUR_BIT);
|
|
|
+ break;
|
|
|
+ case MMC_BUS_WIDTH_1:
|
|
|
+ OMAP_HSMMC_WRITE(host->base, CON, con & ~DW8);
|
|
|
+ OMAP_HSMMC_WRITE(host->base, HCTL,
|
|
|
+ OMAP_HSMMC_READ(host->base, HCTL) & ~FOUR_BIT);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static void omap_hsmmc_set_bus_mode(struct omap_hsmmc_host *host)
|
|
|
+{
|
|
|
+ struct mmc_ios *ios = &host->mmc->ios;
|
|
|
+ u32 con;
|
|
|
+
|
|
|
+ con = OMAP_HSMMC_READ(host->base, CON);
|
|
|
+ if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN)
|
|
|
+ OMAP_HSMMC_WRITE(host->base, CON, con | OD);
|
|
|
+ else
|
|
|
+ OMAP_HSMMC_WRITE(host->base, CON, con & ~OD);
|
|
|
+}
|
|
|
+
|
|
|
#ifdef CONFIG_PM
|
|
|
|
|
|
/*
|
|
@@ -595,8 +684,7 @@ static int omap_hsmmc_context_restore(struct omap_hsmmc_host *host)
|
|
|
struct mmc_ios *ios = &host->mmc->ios;
|
|
|
struct omap_mmc_platform_data *pdata = host->pdata;
|
|
|
int context_loss = 0;
|
|
|
- u32 hctl, capa, con;
|
|
|
- u16 dsor = 0;
|
|
|
+ u32 hctl, capa;
|
|
|
unsigned long timeout;
|
|
|
|
|
|
if (pdata->get_context_loss_count) {
|
|
@@ -658,54 +746,12 @@ static int omap_hsmmc_context_restore(struct omap_hsmmc_host *host)
|
|
|
if (host->power_mode == MMC_POWER_OFF)
|
|
|
goto out;
|
|
|
|
|
|
- con = OMAP_HSMMC_READ(host->base, CON);
|
|
|
- switch (ios->bus_width) {
|
|
|
- case MMC_BUS_WIDTH_8:
|
|
|
- OMAP_HSMMC_WRITE(host->base, CON, con | DW8);
|
|
|
- break;
|
|
|
- case MMC_BUS_WIDTH_4:
|
|
|
- OMAP_HSMMC_WRITE(host->base, CON, con & ~DW8);
|
|
|
- OMAP_HSMMC_WRITE(host->base, HCTL,
|
|
|
- OMAP_HSMMC_READ(host->base, HCTL) | FOUR_BIT);
|
|
|
- break;
|
|
|
- case MMC_BUS_WIDTH_1:
|
|
|
- OMAP_HSMMC_WRITE(host->base, CON, con & ~DW8);
|
|
|
- OMAP_HSMMC_WRITE(host->base, HCTL,
|
|
|
- OMAP_HSMMC_READ(host->base, HCTL) & ~FOUR_BIT);
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- if (ios->clock) {
|
|
|
- dsor = OMAP_MMC_MASTER_CLOCK / ios->clock;
|
|
|
- if (dsor < 1)
|
|
|
- dsor = 1;
|
|
|
-
|
|
|
- if (OMAP_MMC_MASTER_CLOCK / dsor > ios->clock)
|
|
|
- dsor++;
|
|
|
-
|
|
|
- if (dsor > 250)
|
|
|
- dsor = 250;
|
|
|
- }
|
|
|
-
|
|
|
- OMAP_HSMMC_WRITE(host->base, SYSCTL,
|
|
|
- OMAP_HSMMC_READ(host->base, SYSCTL) & ~CEN);
|
|
|
- OMAP_HSMMC_WRITE(host->base, SYSCTL, (dsor << 6) | (DTO << 16));
|
|
|
- OMAP_HSMMC_WRITE(host->base, SYSCTL,
|
|
|
- OMAP_HSMMC_READ(host->base, SYSCTL) | ICE);
|
|
|
+ omap_hsmmc_set_bus_width(host);
|
|
|
|
|
|
- timeout = jiffies + msecs_to_jiffies(MMC_TIMEOUT_MS);
|
|
|
- while ((OMAP_HSMMC_READ(host->base, SYSCTL) & ICS) != ICS
|
|
|
- && time_before(jiffies, timeout))
|
|
|
- ;
|
|
|
+ omap_hsmmc_set_clock(host);
|
|
|
|
|
|
- OMAP_HSMMC_WRITE(host->base, SYSCTL,
|
|
|
- OMAP_HSMMC_READ(host->base, SYSCTL) | CEN);
|
|
|
+ omap_hsmmc_set_bus_mode(host);
|
|
|
|
|
|
- con = OMAP_HSMMC_READ(host->base, CON);
|
|
|
- if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN)
|
|
|
- OMAP_HSMMC_WRITE(host->base, CON, con | OD);
|
|
|
- else
|
|
|
- OMAP_HSMMC_WRITE(host->base, CON, con & ~OD);
|
|
|
out:
|
|
|
host->context_loss = context_loss;
|
|
|
|
|
@@ -973,14 +1019,14 @@ static void omap_hsmmc_dma_cleanup(struct omap_hsmmc_host *host, int errno)
|
|
|
* Readable error output
|
|
|
*/
|
|
|
#ifdef CONFIG_MMC_DEBUG
|
|
|
-static void omap_hsmmc_report_irq(struct omap_hsmmc_host *host, u32 status)
|
|
|
+static void omap_hsmmc_dbg_report_irq(struct omap_hsmmc_host *host, u32 status)
|
|
|
{
|
|
|
/* --- means reserved bit without definition at documentation */
|
|
|
static const char *omap_hsmmc_status_bits[] = {
|
|
|
- "CC", "TC", "BGE", "---", "BWR", "BRR", "---", "---", "CIRQ",
|
|
|
- "OBI", "---", "---", "---", "---", "---", "ERRI", "CTO", "CCRC",
|
|
|
- "CEB", "CIE", "DTO", "DCRC", "DEB", "---", "ACE", "---",
|
|
|
- "---", "---", "---", "CERR", "CERR", "BADA", "---", "---", "---"
|
|
|
+ "CC" , "TC" , "BGE", "---", "BWR" , "BRR" , "---" , "---" ,
|
|
|
+ "CIRQ", "OBI" , "---", "---", "---" , "---" , "---" , "ERRI",
|
|
|
+ "CTO" , "CCRC", "CEB", "CIE", "DTO" , "DCRC", "DEB" , "---" ,
|
|
|
+ "ACE" , "---" , "---", "---", "CERR", "BADA", "---" , "---"
|
|
|
};
|
|
|
char res[256];
|
|
|
char *buf = res;
|
|
@@ -997,6 +1043,11 @@ static void omap_hsmmc_report_irq(struct omap_hsmmc_host *host, u32 status)
|
|
|
|
|
|
dev_dbg(mmc_dev(host->mmc), "%s\n", res);
|
|
|
}
|
|
|
+#else
|
|
|
+static inline void omap_hsmmc_dbg_report_irq(struct omap_hsmmc_host *host,
|
|
|
+ u32 status)
|
|
|
+{
|
|
|
+}
|
|
|
#endif /* CONFIG_MMC_DEBUG */
|
|
|
|
|
|
/*
|
|
@@ -1055,9 +1106,7 @@ static void omap_hsmmc_do_irq(struct omap_hsmmc_host *host, int status)
|
|
|
dev_dbg(mmc_dev(host->mmc), "IRQ Status is %x\n", status);
|
|
|
|
|
|
if (status & ERR) {
|
|
|
-#ifdef CONFIG_MMC_DEBUG
|
|
|
- omap_hsmmc_report_irq(host, status);
|
|
|
-#endif
|
|
|
+ omap_hsmmc_dbg_report_irq(host, status);
|
|
|
if ((status & CMD_TIMEOUT) ||
|
|
|
(status & CMD_CRC)) {
|
|
|
if (host->cmd) {
|
|
@@ -1155,8 +1204,7 @@ static int omap_hsmmc_switch_opcond(struct omap_hsmmc_host *host, int vdd)
|
|
|
int ret;
|
|
|
|
|
|
/* Disable the clocks */
|
|
|
- clk_disable(host->fclk);
|
|
|
- clk_disable(host->iclk);
|
|
|
+ pm_runtime_put_sync(host->dev);
|
|
|
if (host->got_dbclk)
|
|
|
clk_disable(host->dbclk);
|
|
|
|
|
@@ -1167,8 +1215,7 @@ static int omap_hsmmc_switch_opcond(struct omap_hsmmc_host *host, int vdd)
|
|
|
if (!ret)
|
|
|
ret = mmc_slot(host).set_power(host->dev, host->slot_id, 1,
|
|
|
vdd);
|
|
|
- clk_enable(host->iclk);
|
|
|
- clk_enable(host->fclk);
|
|
|
+ pm_runtime_get_sync(host->dev);
|
|
|
if (host->got_dbclk)
|
|
|
clk_enable(host->dbclk);
|
|
|
|
|
@@ -1322,7 +1369,7 @@ static void omap_hsmmc_config_dma_params(struct omap_hsmmc_host *host,
|
|
|
static void omap_hsmmc_dma_cb(int lch, u16 ch_status, void *cb_data)
|
|
|
{
|
|
|
struct omap_hsmmc_host *host = cb_data;
|
|
|
- struct mmc_data *data = host->mrq->data;
|
|
|
+ struct mmc_data *data;
|
|
|
int dma_ch, req_in_progress;
|
|
|
|
|
|
if (!(ch_status & OMAP_DMA_BLOCK_IRQ)) {
|
|
@@ -1337,6 +1384,7 @@ static void omap_hsmmc_dma_cb(int lch, u16 ch_status, void *cb_data)
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
+ data = host->mrq->data;
|
|
|
host->dma_sg_idx++;
|
|
|
if (host->dma_sg_idx < host->dma_len) {
|
|
|
/* Fire up the next transfer. */
|
|
@@ -1346,8 +1394,9 @@ static void omap_hsmmc_dma_cb(int lch, u16 ch_status, void *cb_data)
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
|
|
|
- omap_hsmmc_get_dma_dir(host, data));
|
|
|
+ if (!data->host_cookie)
|
|
|
+ dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
|
|
|
+ omap_hsmmc_get_dma_dir(host, data));
|
|
|
|
|
|
req_in_progress = host->req_in_progress;
|
|
|
dma_ch = host->dma_ch;
|
|
@@ -1365,6 +1414,45 @@ static void omap_hsmmc_dma_cb(int lch, u16 ch_status, void *cb_data)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+static int omap_hsmmc_pre_dma_transfer(struct omap_hsmmc_host *host,
|
|
|
+ struct mmc_data *data,
|
|
|
+ struct omap_hsmmc_next *next)
|
|
|
+{
|
|
|
+ int dma_len;
|
|
|
+
|
|
|
+ if (!next && data->host_cookie &&
|
|
|
+ data->host_cookie != host->next_data.cookie) {
|
|
|
+ printk(KERN_WARNING "[%s] invalid cookie: data->host_cookie %d"
|
|
|
+ " host->next_data.cookie %d\n",
|
|
|
+ __func__, data->host_cookie, host->next_data.cookie);
|
|
|
+ data->host_cookie = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Check if next job is already prepared */
|
|
|
+ if (next ||
|
|
|
+ (!next && data->host_cookie != host->next_data.cookie)) {
|
|
|
+ dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg,
|
|
|
+ data->sg_len,
|
|
|
+ omap_hsmmc_get_dma_dir(host, data));
|
|
|
+
|
|
|
+ } else {
|
|
|
+ dma_len = host->next_data.dma_len;
|
|
|
+ host->next_data.dma_len = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ if (dma_len == 0)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ if (next) {
|
|
|
+ next->dma_len = dma_len;
|
|
|
+ data->host_cookie = ++next->cookie < 0 ? 1 : next->cookie;
|
|
|
+ } else
|
|
|
+ host->dma_len = dma_len;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* Routine to configure and start DMA for the MMC card
|
|
|
*/
|
|
@@ -1398,9 +1486,10 @@ static int omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host,
|
|
|
mmc_hostname(host->mmc), ret);
|
|
|
return ret;
|
|
|
}
|
|
|
+ ret = omap_hsmmc_pre_dma_transfer(host, data, NULL);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
|
|
|
- host->dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg,
|
|
|
- data->sg_len, omap_hsmmc_get_dma_dir(host, data));
|
|
|
host->dma_ch = dma_ch;
|
|
|
host->dma_sg_idx = 0;
|
|
|
|
|
@@ -1480,6 +1569,35 @@ omap_hsmmc_prepare_data(struct omap_hsmmc_host *host, struct mmc_request *req)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static void omap_hsmmc_post_req(struct mmc_host *mmc, struct mmc_request *mrq,
|
|
|
+ int err)
|
|
|
+{
|
|
|
+ struct omap_hsmmc_host *host = mmc_priv(mmc);
|
|
|
+ struct mmc_data *data = mrq->data;
|
|
|
+
|
|
|
+ if (host->use_dma) {
|
|
|
+ dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
|
|
|
+ omap_hsmmc_get_dma_dir(host, data));
|
|
|
+ data->host_cookie = 0;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static void omap_hsmmc_pre_req(struct mmc_host *mmc, struct mmc_request *mrq,
|
|
|
+ bool is_first_req)
|
|
|
+{
|
|
|
+ struct omap_hsmmc_host *host = mmc_priv(mmc);
|
|
|
+
|
|
|
+ if (mrq->data->host_cookie) {
|
|
|
+ mrq->data->host_cookie = 0;
|
|
|
+ return ;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (host->use_dma)
|
|
|
+ if (omap_hsmmc_pre_dma_transfer(host, mrq->data,
|
|
|
+ &host->next_data))
|
|
|
+ mrq->data->host_cookie = 0;
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* Request function. for read/write operation
|
|
|
*/
|
|
@@ -1528,13 +1646,9 @@ static void omap_hsmmc_request(struct mmc_host *mmc, struct mmc_request *req)
|
|
|
static void omap_hsmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
|
|
{
|
|
|
struct omap_hsmmc_host *host = mmc_priv(mmc);
|
|
|
- u16 dsor = 0;
|
|
|
- unsigned long regval;
|
|
|
- unsigned long timeout;
|
|
|
- u32 con;
|
|
|
int do_send_init_stream = 0;
|
|
|
|
|
|
- mmc_host_enable(host->mmc);
|
|
|
+ pm_runtime_get_sync(host->dev);
|
|
|
|
|
|
if (ios->power_mode != host->power_mode) {
|
|
|
switch (ios->power_mode) {
|
|
@@ -1557,22 +1671,7 @@ static void omap_hsmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
|
|
|
|
|
/* FIXME: set registers based only on changes to ios */
|
|
|
|
|
|
- con = OMAP_HSMMC_READ(host->base, CON);
|
|
|
- switch (mmc->ios.bus_width) {
|
|
|
- case MMC_BUS_WIDTH_8:
|
|
|
- OMAP_HSMMC_WRITE(host->base, CON, con | DW8);
|
|
|
- break;
|
|
|
- case MMC_BUS_WIDTH_4:
|
|
|
- OMAP_HSMMC_WRITE(host->base, CON, con & ~DW8);
|
|
|
- OMAP_HSMMC_WRITE(host->base, HCTL,
|
|
|
- OMAP_HSMMC_READ(host->base, HCTL) | FOUR_BIT);
|
|
|
- break;
|
|
|
- case MMC_BUS_WIDTH_1:
|
|
|
- OMAP_HSMMC_WRITE(host->base, CON, con & ~DW8);
|
|
|
- OMAP_HSMMC_WRITE(host->base, HCTL,
|
|
|
- OMAP_HSMMC_READ(host->base, HCTL) & ~FOUR_BIT);
|
|
|
- break;
|
|
|
- }
|
|
|
+ omap_hsmmc_set_bus_width(host);
|
|
|
|
|
|
if (host->pdata->controller_flags & OMAP_HSMMC_SUPPORTS_DUAL_VOLT) {
|
|
|
/* Only MMC1 can interface at 3V without some flavor
|
|
@@ -1592,47 +1691,14 @@ static void omap_hsmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- if (ios->clock) {
|
|
|
- dsor = OMAP_MMC_MASTER_CLOCK / ios->clock;
|
|
|
- if (dsor < 1)
|
|
|
- dsor = 1;
|
|
|
-
|
|
|
- if (OMAP_MMC_MASTER_CLOCK / dsor > ios->clock)
|
|
|
- dsor++;
|
|
|
-
|
|
|
- if (dsor > 250)
|
|
|
- dsor = 250;
|
|
|
- }
|
|
|
- omap_hsmmc_stop_clock(host);
|
|
|
- regval = OMAP_HSMMC_READ(host->base, SYSCTL);
|
|
|
- regval = regval & ~(CLKD_MASK);
|
|
|
- regval = regval | (dsor << 6) | (DTO << 16);
|
|
|
- OMAP_HSMMC_WRITE(host->base, SYSCTL, regval);
|
|
|
- OMAP_HSMMC_WRITE(host->base, SYSCTL,
|
|
|
- OMAP_HSMMC_READ(host->base, SYSCTL) | ICE);
|
|
|
-
|
|
|
- /* Wait till the ICS bit is set */
|
|
|
- timeout = jiffies + msecs_to_jiffies(MMC_TIMEOUT_MS);
|
|
|
- while ((OMAP_HSMMC_READ(host->base, SYSCTL) & ICS) != ICS
|
|
|
- && time_before(jiffies, timeout))
|
|
|
- msleep(1);
|
|
|
-
|
|
|
- OMAP_HSMMC_WRITE(host->base, SYSCTL,
|
|
|
- OMAP_HSMMC_READ(host->base, SYSCTL) | CEN);
|
|
|
+ omap_hsmmc_set_clock(host);
|
|
|
|
|
|
if (do_send_init_stream)
|
|
|
send_init_stream(host);
|
|
|
|
|
|
- con = OMAP_HSMMC_READ(host->base, CON);
|
|
|
- if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN)
|
|
|
- OMAP_HSMMC_WRITE(host->base, CON, con | OD);
|
|
|
- else
|
|
|
- OMAP_HSMMC_WRITE(host->base, CON, con & ~OD);
|
|
|
+ omap_hsmmc_set_bus_mode(host);
|
|
|
|
|
|
- if (host->power_mode == MMC_POWER_OFF)
|
|
|
- mmc_host_disable(host->mmc);
|
|
|
- else
|
|
|
- mmc_host_lazy_disable(host->mmc);
|
|
|
+ pm_runtime_put_autosuspend(host->dev);
|
|
|
}
|
|
|
|
|
|
static int omap_hsmmc_get_cd(struct mmc_host *mmc)
|
|
@@ -1688,230 +1754,12 @@ static void omap_hsmmc_conf_bus_power(struct omap_hsmmc_host *host)
|
|
|
set_sd_bus_power(host);
|
|
|
}
|
|
|
|
|
|
-/*
|
|
|
- * Dynamic power saving handling, FSM:
|
|
|
- * ENABLED -> DISABLED -> CARDSLEEP / REGSLEEP -> OFF
|
|
|
- * ^___________| | |
|
|
|
- * |______________________|______________________|
|
|
|
- *
|
|
|
- * ENABLED: mmc host is fully functional
|
|
|
- * DISABLED: fclk is off
|
|
|
- * CARDSLEEP: fclk is off, card is asleep, voltage regulator is asleep
|
|
|
- * REGSLEEP: fclk is off, voltage regulator is asleep
|
|
|
- * OFF: fclk is off, voltage regulator is off
|
|
|
- *
|
|
|
- * Transition handlers return the timeout for the next state transition
|
|
|
- * or negative error.
|
|
|
- */
|
|
|
-
|
|
|
-enum {ENABLED = 0, DISABLED, CARDSLEEP, REGSLEEP, OFF};
|
|
|
-
|
|
|
-/* Handler for [ENABLED -> DISABLED] transition */
|
|
|
-static int omap_hsmmc_enabled_to_disabled(struct omap_hsmmc_host *host)
|
|
|
-{
|
|
|
- omap_hsmmc_context_save(host);
|
|
|
- clk_disable(host->fclk);
|
|
|
- host->dpm_state = DISABLED;
|
|
|
-
|
|
|
- dev_dbg(mmc_dev(host->mmc), "ENABLED -> DISABLED\n");
|
|
|
-
|
|
|
- if (host->power_mode == MMC_POWER_OFF)
|
|
|
- return 0;
|
|
|
-
|
|
|
- return OMAP_MMC_SLEEP_TIMEOUT;
|
|
|
-}
|
|
|
-
|
|
|
-/* Handler for [DISABLED -> REGSLEEP / CARDSLEEP] transition */
|
|
|
-static int omap_hsmmc_disabled_to_sleep(struct omap_hsmmc_host *host)
|
|
|
-{
|
|
|
- int err, new_state;
|
|
|
-
|
|
|
- if (!mmc_try_claim_host(host->mmc))
|
|
|
- return 0;
|
|
|
-
|
|
|
- clk_enable(host->fclk);
|
|
|
- omap_hsmmc_context_restore(host);
|
|
|
- if (mmc_card_can_sleep(host->mmc)) {
|
|
|
- err = mmc_card_sleep(host->mmc);
|
|
|
- if (err < 0) {
|
|
|
- clk_disable(host->fclk);
|
|
|
- mmc_release_host(host->mmc);
|
|
|
- return err;
|
|
|
- }
|
|
|
- new_state = CARDSLEEP;
|
|
|
- } else {
|
|
|
- new_state = REGSLEEP;
|
|
|
- }
|
|
|
- if (mmc_slot(host).set_sleep)
|
|
|
- mmc_slot(host).set_sleep(host->dev, host->slot_id, 1, 0,
|
|
|
- new_state == CARDSLEEP);
|
|
|
- /* FIXME: turn off bus power and perhaps interrupts too */
|
|
|
- clk_disable(host->fclk);
|
|
|
- host->dpm_state = new_state;
|
|
|
-
|
|
|
- mmc_release_host(host->mmc);
|
|
|
-
|
|
|
- dev_dbg(mmc_dev(host->mmc), "DISABLED -> %s\n",
|
|
|
- host->dpm_state == CARDSLEEP ? "CARDSLEEP" : "REGSLEEP");
|
|
|
-
|
|
|
- if (mmc_slot(host).no_off)
|
|
|
- return 0;
|
|
|
-
|
|
|
- if ((host->mmc->caps & MMC_CAP_NONREMOVABLE) ||
|
|
|
- mmc_slot(host).card_detect ||
|
|
|
- (mmc_slot(host).get_cover_state &&
|
|
|
- mmc_slot(host).get_cover_state(host->dev, host->slot_id)))
|
|
|
- return OMAP_MMC_OFF_TIMEOUT;
|
|
|
-
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-/* Handler for [REGSLEEP / CARDSLEEP -> OFF] transition */
|
|
|
-static int omap_hsmmc_sleep_to_off(struct omap_hsmmc_host *host)
|
|
|
-{
|
|
|
- if (!mmc_try_claim_host(host->mmc))
|
|
|
- return 0;
|
|
|
-
|
|
|
- if (mmc_slot(host).no_off)
|
|
|
- return 0;
|
|
|
-
|
|
|
- if (!((host->mmc->caps & MMC_CAP_NONREMOVABLE) ||
|
|
|
- mmc_slot(host).card_detect ||
|
|
|
- (mmc_slot(host).get_cover_state &&
|
|
|
- mmc_slot(host).get_cover_state(host->dev, host->slot_id)))) {
|
|
|
- mmc_release_host(host->mmc);
|
|
|
- return 0;
|
|
|
- }
|
|
|
-
|
|
|
- mmc_slot(host).set_power(host->dev, host->slot_id, 0, 0);
|
|
|
- host->vdd = 0;
|
|
|
- host->power_mode = MMC_POWER_OFF;
|
|
|
-
|
|
|
- dev_dbg(mmc_dev(host->mmc), "%s -> OFF\n",
|
|
|
- host->dpm_state == CARDSLEEP ? "CARDSLEEP" : "REGSLEEP");
|
|
|
-
|
|
|
- host->dpm_state = OFF;
|
|
|
-
|
|
|
- mmc_release_host(host->mmc);
|
|
|
-
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-/* Handler for [DISABLED -> ENABLED] transition */
|
|
|
-static int omap_hsmmc_disabled_to_enabled(struct omap_hsmmc_host *host)
|
|
|
-{
|
|
|
- int err;
|
|
|
-
|
|
|
- err = clk_enable(host->fclk);
|
|
|
- if (err < 0)
|
|
|
- return err;
|
|
|
-
|
|
|
- omap_hsmmc_context_restore(host);
|
|
|
- host->dpm_state = ENABLED;
|
|
|
-
|
|
|
- dev_dbg(mmc_dev(host->mmc), "DISABLED -> ENABLED\n");
|
|
|
-
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-/* Handler for [SLEEP -> ENABLED] transition */
|
|
|
-static int omap_hsmmc_sleep_to_enabled(struct omap_hsmmc_host *host)
|
|
|
-{
|
|
|
- if (!mmc_try_claim_host(host->mmc))
|
|
|
- return 0;
|
|
|
-
|
|
|
- clk_enable(host->fclk);
|
|
|
- omap_hsmmc_context_restore(host);
|
|
|
- if (mmc_slot(host).set_sleep)
|
|
|
- mmc_slot(host).set_sleep(host->dev, host->slot_id, 0,
|
|
|
- host->vdd, host->dpm_state == CARDSLEEP);
|
|
|
- if (mmc_card_can_sleep(host->mmc))
|
|
|
- mmc_card_awake(host->mmc);
|
|
|
-
|
|
|
- dev_dbg(mmc_dev(host->mmc), "%s -> ENABLED\n",
|
|
|
- host->dpm_state == CARDSLEEP ? "CARDSLEEP" : "REGSLEEP");
|
|
|
-
|
|
|
- host->dpm_state = ENABLED;
|
|
|
-
|
|
|
- mmc_release_host(host->mmc);
|
|
|
-
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-/* Handler for [OFF -> ENABLED] transition */
|
|
|
-static int omap_hsmmc_off_to_enabled(struct omap_hsmmc_host *host)
|
|
|
-{
|
|
|
- clk_enable(host->fclk);
|
|
|
-
|
|
|
- omap_hsmmc_context_restore(host);
|
|
|
- omap_hsmmc_conf_bus_power(host);
|
|
|
- mmc_power_restore_host(host->mmc);
|
|
|
-
|
|
|
- host->dpm_state = ENABLED;
|
|
|
-
|
|
|
- dev_dbg(mmc_dev(host->mmc), "OFF -> ENABLED\n");
|
|
|
-
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
- * Bring MMC host to ENABLED from any other PM state.
|
|
|
- */
|
|
|
-static int omap_hsmmc_enable(struct mmc_host *mmc)
|
|
|
-{
|
|
|
- struct omap_hsmmc_host *host = mmc_priv(mmc);
|
|
|
-
|
|
|
- switch (host->dpm_state) {
|
|
|
- case DISABLED:
|
|
|
- return omap_hsmmc_disabled_to_enabled(host);
|
|
|
- case CARDSLEEP:
|
|
|
- case REGSLEEP:
|
|
|
- return omap_hsmmc_sleep_to_enabled(host);
|
|
|
- case OFF:
|
|
|
- return omap_hsmmc_off_to_enabled(host);
|
|
|
- default:
|
|
|
- dev_dbg(mmc_dev(host->mmc), "UNKNOWN state\n");
|
|
|
- return -EINVAL;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
- * Bring MMC host in PM state (one level deeper).
|
|
|
- */
|
|
|
-static int omap_hsmmc_disable(struct mmc_host *mmc, int lazy)
|
|
|
-{
|
|
|
- struct omap_hsmmc_host *host = mmc_priv(mmc);
|
|
|
-
|
|
|
- switch (host->dpm_state) {
|
|
|
- case ENABLED: {
|
|
|
- int delay;
|
|
|
-
|
|
|
- delay = omap_hsmmc_enabled_to_disabled(host);
|
|
|
- if (lazy || delay < 0)
|
|
|
- return delay;
|
|
|
- return 0;
|
|
|
- }
|
|
|
- case DISABLED:
|
|
|
- return omap_hsmmc_disabled_to_sleep(host);
|
|
|
- case CARDSLEEP:
|
|
|
- case REGSLEEP:
|
|
|
- return omap_hsmmc_sleep_to_off(host);
|
|
|
- default:
|
|
|
- dev_dbg(mmc_dev(host->mmc), "UNKNOWN state\n");
|
|
|
- return -EINVAL;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
static int omap_hsmmc_enable_fclk(struct mmc_host *mmc)
|
|
|
{
|
|
|
struct omap_hsmmc_host *host = mmc_priv(mmc);
|
|
|
- int err;
|
|
|
|
|
|
- err = clk_enable(host->fclk);
|
|
|
- if (err)
|
|
|
- return err;
|
|
|
- dev_dbg(mmc_dev(host->mmc), "mmc_fclk: enabled\n");
|
|
|
- omap_hsmmc_context_restore(host);
|
|
|
+ pm_runtime_get_sync(host->dev);
|
|
|
+
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -1919,26 +1767,17 @@ static int omap_hsmmc_disable_fclk(struct mmc_host *mmc, int lazy)
|
|
|
{
|
|
|
struct omap_hsmmc_host *host = mmc_priv(mmc);
|
|
|
|
|
|
- omap_hsmmc_context_save(host);
|
|
|
- clk_disable(host->fclk);
|
|
|
- dev_dbg(mmc_dev(host->mmc), "mmc_fclk: disabled\n");
|
|
|
+ pm_runtime_mark_last_busy(host->dev);
|
|
|
+ pm_runtime_put_autosuspend(host->dev);
|
|
|
+
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
static const struct mmc_host_ops omap_hsmmc_ops = {
|
|
|
.enable = omap_hsmmc_enable_fclk,
|
|
|
.disable = omap_hsmmc_disable_fclk,
|
|
|
- .request = omap_hsmmc_request,
|
|
|
- .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 */
|
|
|
-};
|
|
|
-
|
|
|
-static const struct mmc_host_ops omap_hsmmc_ps_ops = {
|
|
|
- .enable = omap_hsmmc_enable,
|
|
|
- .disable = omap_hsmmc_disable,
|
|
|
+ .post_req = omap_hsmmc_post_req,
|
|
|
+ .pre_req = omap_hsmmc_pre_req,
|
|
|
.request = omap_hsmmc_request,
|
|
|
.set_ios = omap_hsmmc_set_ios,
|
|
|
.get_cd = omap_hsmmc_get_cd,
|
|
@@ -1968,15 +1807,12 @@ static int omap_hsmmc_regs_show(struct seq_file *s, void *data)
|
|
|
host->dpm_state, mmc->nesting_cnt,
|
|
|
host->context_loss, context_loss);
|
|
|
|
|
|
- if (host->suspended || host->dpm_state == OFF) {
|
|
|
+ if (host->suspended) {
|
|
|
seq_printf(s, "host suspended, can't read registers\n");
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
- if (clk_enable(host->fclk) != 0) {
|
|
|
- seq_printf(s, "can't read the regs\n");
|
|
|
- return 0;
|
|
|
- }
|
|
|
+ pm_runtime_get_sync(host->dev);
|
|
|
|
|
|
seq_printf(s, "SYSCONFIG:\t0x%08x\n",
|
|
|
OMAP_HSMMC_READ(host->base, SYSCONFIG));
|
|
@@ -1993,7 +1829,8 @@ static int omap_hsmmc_regs_show(struct seq_file *s, void *data)
|
|
|
seq_printf(s, "CAPA:\t\t0x%08x\n",
|
|
|
OMAP_HSMMC_READ(host->base, CAPA));
|
|
|
|
|
|
- clk_disable(host->fclk);
|
|
|
+ pm_runtime_mark_last_busy(host->dev);
|
|
|
+ pm_runtime_put_autosuspend(host->dev);
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
@@ -2077,14 +1914,12 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
|
|
|
host->mapbase = res->start;
|
|
|
host->base = ioremap(host->mapbase, SZ_4K);
|
|
|
host->power_mode = MMC_POWER_OFF;
|
|
|
+ host->next_data.cookie = 1;
|
|
|
|
|
|
platform_set_drvdata(pdev, host);
|
|
|
INIT_WORK(&host->mmc_carddetect_work, omap_hsmmc_detect);
|
|
|
|
|
|
- if (mmc_slot(host).power_saving)
|
|
|
- mmc->ops = &omap_hsmmc_ps_ops;
|
|
|
- else
|
|
|
- mmc->ops = &omap_hsmmc_ops;
|
|
|
+ mmc->ops = &omap_hsmmc_ops;
|
|
|
|
|
|
/*
|
|
|
* If regulator_disable can only put vcc_aux to sleep then there is
|
|
@@ -2093,44 +1928,26 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
|
|
|
if (mmc_slot(host).vcc_aux_disable_is_sleep)
|
|
|
mmc_slot(host).no_off = 1;
|
|
|
|
|
|
- mmc->f_min = 400000;
|
|
|
- mmc->f_max = 52000000;
|
|
|
+ mmc->f_min = OMAP_MMC_MIN_CLOCK;
|
|
|
+ mmc->f_max = OMAP_MMC_MAX_CLOCK;
|
|
|
|
|
|
spin_lock_init(&host->irq_lock);
|
|
|
|
|
|
- host->iclk = clk_get(&pdev->dev, "ick");
|
|
|
- if (IS_ERR(host->iclk)) {
|
|
|
- ret = PTR_ERR(host->iclk);
|
|
|
- host->iclk = NULL;
|
|
|
- goto err1;
|
|
|
- }
|
|
|
host->fclk = clk_get(&pdev->dev, "fck");
|
|
|
if (IS_ERR(host->fclk)) {
|
|
|
ret = PTR_ERR(host->fclk);
|
|
|
host->fclk = NULL;
|
|
|
- clk_put(host->iclk);
|
|
|
goto err1;
|
|
|
}
|
|
|
|
|
|
omap_hsmmc_context_save(host);
|
|
|
|
|
|
mmc->caps |= MMC_CAP_DISABLE;
|
|
|
- mmc_set_disable_delay(mmc, OMAP_MMC_DISABLED_TIMEOUT);
|
|
|
- /* we start off in DISABLED state */
|
|
|
- host->dpm_state = DISABLED;
|
|
|
|
|
|
- if (clk_enable(host->iclk) != 0) {
|
|
|
- clk_put(host->iclk);
|
|
|
- clk_put(host->fclk);
|
|
|
- goto err1;
|
|
|
- }
|
|
|
-
|
|
|
- if (mmc_host_enable(host->mmc) != 0) {
|
|
|
- clk_disable(host->iclk);
|
|
|
- clk_put(host->iclk);
|
|
|
- clk_put(host->fclk);
|
|
|
- goto err1;
|
|
|
- }
|
|
|
+ pm_runtime_enable(host->dev);
|
|
|
+ pm_runtime_get_sync(host->dev);
|
|
|
+ pm_runtime_set_autosuspend_delay(host->dev, MMC_AUTOSUSPEND_DELAY);
|
|
|
+ pm_runtime_use_autosuspend(host->dev);
|
|
|
|
|
|
if (cpu_is_omap2430()) {
|
|
|
host->dbclk = clk_get(&pdev->dev, "mmchsdb_fck");
|
|
@@ -2240,8 +2057,6 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
|
|
|
|
|
|
omap_hsmmc_disable_irq(host);
|
|
|
|
|
|
- mmc_host_lazy_disable(host->mmc);
|
|
|
-
|
|
|
omap_hsmmc_protect_card(host);
|
|
|
|
|
|
mmc_add_host(mmc);
|
|
@@ -2259,6 +2074,8 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
|
|
|
}
|
|
|
|
|
|
omap_hsmmc_debugfs(mmc);
|
|
|
+ pm_runtime_mark_last_busy(host->dev);
|
|
|
+ pm_runtime_put_autosuspend(host->dev);
|
|
|
|
|
|
return 0;
|
|
|
|
|
@@ -2274,10 +2091,9 @@ err_reg:
|
|
|
err_irq_cd_init:
|
|
|
free_irq(host->irq, host);
|
|
|
err_irq:
|
|
|
- mmc_host_disable(host->mmc);
|
|
|
- clk_disable(host->iclk);
|
|
|
+ pm_runtime_mark_last_busy(host->dev);
|
|
|
+ pm_runtime_put_autosuspend(host->dev);
|
|
|
clk_put(host->fclk);
|
|
|
- clk_put(host->iclk);
|
|
|
if (host->got_dbclk) {
|
|
|
clk_disable(host->dbclk);
|
|
|
clk_put(host->dbclk);
|
|
@@ -2299,7 +2115,7 @@ static int omap_hsmmc_remove(struct platform_device *pdev)
|
|
|
struct resource *res;
|
|
|
|
|
|
if (host) {
|
|
|
- mmc_host_enable(host->mmc);
|
|
|
+ pm_runtime_get_sync(host->dev);
|
|
|
mmc_remove_host(host->mmc);
|
|
|
if (host->use_reg)
|
|
|
omap_hsmmc_reg_put(host);
|
|
@@ -2310,10 +2126,9 @@ static int omap_hsmmc_remove(struct platform_device *pdev)
|
|
|
free_irq(mmc_slot(host).card_detect_irq, host);
|
|
|
flush_work_sync(&host->mmc_carddetect_work);
|
|
|
|
|
|
- mmc_host_disable(host->mmc);
|
|
|
- clk_disable(host->iclk);
|
|
|
+ pm_runtime_put_sync(host->dev);
|
|
|
+ pm_runtime_disable(host->dev);
|
|
|
clk_put(host->fclk);
|
|
|
- clk_put(host->iclk);
|
|
|
if (host->got_dbclk) {
|
|
|
clk_disable(host->dbclk);
|
|
|
clk_put(host->dbclk);
|
|
@@ -2343,6 +2158,7 @@ static int omap_hsmmc_suspend(struct device *dev)
|
|
|
return 0;
|
|
|
|
|
|
if (host) {
|
|
|
+ pm_runtime_get_sync(host->dev);
|
|
|
host->suspended = 1;
|
|
|
if (host->pdata->suspend) {
|
|
|
ret = host->pdata->suspend(&pdev->dev,
|
|
@@ -2357,13 +2173,11 @@ static int omap_hsmmc_suspend(struct device *dev)
|
|
|
}
|
|
|
cancel_work_sync(&host->mmc_carddetect_work);
|
|
|
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,
|
|
|
OMAP_HSMMC_READ(host->base, HCTL) & ~SDBP);
|
|
|
- mmc_host_disable(host->mmc);
|
|
|
- clk_disable(host->iclk);
|
|
|
if (host->got_dbclk)
|
|
|
clk_disable(host->dbclk);
|
|
|
} else {
|
|
@@ -2375,9 +2189,8 @@ static int omap_hsmmc_suspend(struct device *dev)
|
|
|
dev_dbg(mmc_dev(host->mmc),
|
|
|
"Unmask interrupt failed\n");
|
|
|
}
|
|
|
- mmc_host_disable(host->mmc);
|
|
|
}
|
|
|
-
|
|
|
+ pm_runtime_put_sync(host->dev);
|
|
|
}
|
|
|
return ret;
|
|
|
}
|
|
@@ -2393,14 +2206,7 @@ static int omap_hsmmc_resume(struct device *dev)
|
|
|
return 0;
|
|
|
|
|
|
if (host) {
|
|
|
- ret = clk_enable(host->iclk);
|
|
|
- if (ret)
|
|
|
- goto clk_en_err;
|
|
|
-
|
|
|
- if (mmc_host_enable(host->mmc) != 0) {
|
|
|
- clk_disable(host->iclk);
|
|
|
- goto clk_en_err;
|
|
|
- }
|
|
|
+ pm_runtime_get_sync(host->dev);
|
|
|
|
|
|
if (host->got_dbclk)
|
|
|
clk_enable(host->dbclk);
|
|
@@ -2421,15 +2227,12 @@ static int omap_hsmmc_resume(struct device *dev)
|
|
|
if (ret == 0)
|
|
|
host->suspended = 0;
|
|
|
|
|
|
- mmc_host_lazy_disable(host->mmc);
|
|
|
+ pm_runtime_mark_last_busy(host->dev);
|
|
|
+ pm_runtime_put_autosuspend(host->dev);
|
|
|
}
|
|
|
|
|
|
return ret;
|
|
|
|
|
|
-clk_en_err:
|
|
|
- dev_dbg(mmc_dev(host->mmc),
|
|
|
- "Failed to enable MMC clocks during resume\n");
|
|
|
- return ret;
|
|
|
}
|
|
|
|
|
|
#else
|
|
@@ -2437,9 +2240,33 @@ clk_en_err:
|
|
|
#define omap_hsmmc_resume NULL
|
|
|
#endif
|
|
|
|
|
|
+static int omap_hsmmc_runtime_suspend(struct device *dev)
|
|
|
+{
|
|
|
+ struct omap_hsmmc_host *host;
|
|
|
+
|
|
|
+ host = platform_get_drvdata(to_platform_device(dev));
|
|
|
+ omap_hsmmc_context_save(host);
|
|
|
+ dev_dbg(mmc_dev(host->mmc), "disabled\n");
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int omap_hsmmc_runtime_resume(struct device *dev)
|
|
|
+{
|
|
|
+ struct omap_hsmmc_host *host;
|
|
|
+
|
|
|
+ host = platform_get_drvdata(to_platform_device(dev));
|
|
|
+ omap_hsmmc_context_restore(host);
|
|
|
+ dev_dbg(mmc_dev(host->mmc), "enabled\n");
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
static struct dev_pm_ops omap_hsmmc_dev_pm_ops = {
|
|
|
.suspend = omap_hsmmc_suspend,
|
|
|
.resume = omap_hsmmc_resume,
|
|
|
+ .runtime_suspend = omap_hsmmc_runtime_suspend,
|
|
|
+ .runtime_resume = omap_hsmmc_runtime_resume,
|
|
|
};
|
|
|
|
|
|
static struct platform_driver omap_hsmmc_driver = {
|