@@ -131,7 +131,10 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
if (mrq->done)
mrq->done(mrq);
- mmc_host_clk_gate(host);
+#ifdef CONFIG_MMC_CLKGATE
+ if ((host->caps & MMC_CAP_CLOCK_GATING_HW) == 0)
+ mmc_host_clk_gate(host);
+#endif
}
}
@@ -192,7 +195,12 @@ mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
mrq->stop->mrq = mrq;
}
}
- mmc_host_clk_ungate(host);
+
+#ifdef CONFIG_MMC_CLKGATE
+ if ((host->caps & MMC_CAP_CLOCK_GATING_HW) == 0)
+ mmc_host_clk_ungate(host);
+#endif
+
host->ops->request(host, mrq);
}
@@ -622,8 +630,10 @@ static inline void mmc_set_ios(struct mmc_host *host)
* We've been given a new frequency while the clock is gated,
* so make sure we regard this as ungating it.
*/
- if (ios->clock > 0 && host->clk_gated)
- host->clk_gated = false;
+ if ((host->caps & MMC_CAP_CLOCK_GATING_HW) == 0
+ && ios->clock > 0
+ && host->clk_gated)
+ host->clk_gated = MMC_CLOCK_GATED_OFF;
#endif
host->ops->set_ios(host, ios);
@@ -657,11 +667,11 @@ void mmc_set_clock(struct mmc_host *host, unsigned int hz)
/*
* This gates the clock by setting it to 0 Hz.
*/
-void mmc_gate_clock(struct mmc_host *host)
+void mmc_gate_clock(struct mmc_host *host, int clk_gating)
{
host->clk_old = host->ios.clock;
host->ios.clock = 0;
- host->clk_gated = true;
+ host->clk_gated = clk_gating;
mmc_set_ios(host);
}
@@ -682,7 +692,37 @@ void mmc_ungate_clock(struct mmc_host *host)
BUG_ON(host->ios.clock);
mmc_set_clock(host, host->clk_old);
}
- host->clk_gated = false;
+ host->clk_gated = MMC_CLOCK_GATED_OFF;
+}
+
+/*
+ * This gates the clock by enabling driver h/w
+ */
+void mmc_hwgate_clock(struct mmc_host *host, int clk_gating)
+{
+ host->clk_old = host->ios.clock;
+ host->ios.clock = 0;
+ host->clk_gated = clk_gating;
+ mmc_set_ios(host);
+}
+
+/*
+ * This ungates the clock by turning off h/w gating
+ */
+void mmc_hwungate_clock(struct mmc_host *host)
+{
+ /*
+ * We should previously have gated the clock, so the clock shall
+ * be 0 here! The clock may however be 0 during initialization,
+ * when some request operations are performed before setting
+ * the frequency. When ungate is requested in that situation
+ * we just ignore the call.
+ */
+ if (host->clk_old) {
+ BUG_ON(host->ios.clock);
+ mmc_set_clock(host, host->clk_old);
+ }
+ host->clk_gated = MMC_CLOCK_GATED_OFF;
}
#endif
@@ -33,8 +33,10 @@ void mmc_init_erase(struct mmc_card *card);
void mmc_set_chip_select(struct mmc_host *host, int mode);
void mmc_set_clock(struct mmc_host *host, unsigned int hz);
-void mmc_gate_clock(struct mmc_host *host);
+void mmc_gate_clock(struct mmc_host *host, int clk_gating);
+void mmc_hwgate_clock(struct mmc_host *host, int clk_gating);
void mmc_ungate_clock(struct mmc_host *host);
+void mmc_hwungate_clock(struct mmc_host *host);
void mmc_set_bus_mode(struct mmc_host *host, unsigned int mode);
void mmc_set_bus_width(struct mmc_host *host, unsigned int width);
void mmc_set_bus_width_ddr(struct mmc_host *host, unsigned int width,
@@ -101,7 +101,7 @@ static void mmc_host_clk_gate_delayed(struct mmc_host *host)
if (!host->clk_requests) {
spin_unlock_irqrestore(&host->clk_lock, flags);
/* This will set host->ios.clock to 0 */
- mmc_gate_clock(host);
+ mmc_gate_clock(host, MMC_CLOCK_GATED_SW_ON);
spin_lock_irqsave(&host->clk_lock, flags);
pr_debug("%s: gated MCI clock\n", mmc_hostname(host));
}
@@ -217,7 +217,7 @@ static inline void mmc_host_clk_init(struct mmc_host *host)
host->clk_requests = 0;
/* Hold MCI clock for 8 cycles by default */
host->clk_delay = 8;
- host->clk_gated = false;
+ host->clk_gated = MMC_CLOCK_GATED_OFF;
host->clk_pending_gate = false;
INIT_WORK(&host->clk_disable_work, mmc_host_clk_gate_work);
spin_lock_init(&host->clk_lock);
@@ -284,7 +284,10 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
host->class_dev.class = &mmc_host_class;
device_initialize(&host->class_dev);
- mmc_host_clk_init(host);
+#ifdef CONFIG_MMC_CLKGATE
+ if ((host->caps & MMC_CAP_CLOCK_GATING_HW) == 0)
+ mmc_host_clk_init(host);
+#endif
spin_lock_init(&host->lock);
init_waitqueue_head(&host->wq);
@@ -368,7 +371,10 @@ void mmc_remove_host(struct mmc_host *host)
led_trigger_unregister_simple(host->led);
- mmc_host_clk_exit(host);
+#ifdef CONFIG_MMC_CLKGATE
+ if ((host->caps & MMC_CAP_CLOCK_GATING_HW) == 0)
+ mmc_host_clk_exit(host);
+#endif
}
EXPORT_SYMBOL(mmc_remove_host);
@@ -516,6 +516,10 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
}
mmc_set_clock(host, max_dtr);
+#ifdef CONFIG_MMC_CLKGATE
+ if (host->caps & MMC_CAP_CLOCK_GATING_HW)
+ mmc_hwgate_clock(host, MMC_CLOCK_GATED_HW_ON);
+#endif
/*
* Indicate DDR mode (if supported).
@@ -592,6 +596,10 @@ static void mmc_remove(struct mmc_host *host)
BUG_ON(!host);
BUG_ON(!host->card);
+#ifdef CONFIG_MMC_CLKGATE
+ if (host->caps & MMC_CAP_CLOCK_GATING_HW)
+ mmc_hwungate_clock(host);
+#endif
mmc_remove_card(host->card);
host->card = NULL;
}
@@ -622,6 +622,10 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
* Set bus speed.
*/
mmc_set_clock(host, mmc_sd_get_max_clock(card));
+#ifdef CONFIG_MMC_CLKGATE
+ if (host->caps & MMC_CAP_CLOCK_GATING_HW)
+ mmc_hwgate_clock(host, MMC_CLOCK_GATED_HW_ON);
+#endif
/*
* Switch to wider bus (if supported).
@@ -425,6 +425,10 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
* It's host's responsibility to fill cccr and cis
* structures in init_card().
*/
+#ifdef CONFIG_MMC_CLKGATE
+ if (host->caps & MMC_CAP_CLOCK_GATING_HW)
+ mmc_hwgate_clock(host, MMC_CLOCK_GATED_OFF);
+#endif
mmc_set_clock(host, card->cis.max_dtr);
if (card->cccr.high_speed) {
@@ -491,6 +495,10 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
/*
* Change to the card's maximum speed.
*/
+#ifdef CONFIG_MMC_CLKGATE
+ if (host->caps & MMC_CAP_CLOCK_GATING_HW)
+ mmc_hwgate_clock(host, MMC_CLOCK_GATED_OFF);
+#endif
mmc_set_clock(host, mmc_sdio_get_max_clock(card));
/*
@@ -1185,6 +1185,9 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
if (host->ops->platform_send_init_74_clocks)
host->ops->platform_send_init_74_clocks(host, ios->power_mode);
+ if ((mmc->caps & MMC_CAP_CLOCK_GATING_HW) && host->ops->hw_clk_gate)
+ host->ops->hw_clk_gate(host);
+
ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
if (ios->bus_width == MMC_BUS_WIDTH_8)
@@ -218,6 +218,7 @@ struct sdhci_ops {
void (*platform_send_init_74_clocks)(struct sdhci_host *host,
u8 power_mode);
unsigned int (*get_ro)(struct sdhci_host *host);
+ void (*hw_clk_gate)(struct sdhci_host *host);
};
#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
@@ -168,13 +168,18 @@ struct mmc_host {
/* DDR mode at 1.8V */
#define MMC_CAP_1_2V_DDR (1 << 12) /* can support */
/* DDR mode at 1.2V */
+#define MMC_CAP_CLOCK_GATING_HW (1 << 13) /* h/w supports clock gating */
mmc_pm_flag_t pm_caps; /* supported pm features */
#ifdef CONFIG_MMC_CLKGATE
int clk_requests; /* internal reference counter */
unsigned int clk_delay; /* number of MCI clk hold cycles */
- bool clk_gated; /* clock gated */
+
+#define MMC_CLOCK_GATED_OFF 0
+#define MMC_CLOCK_GATED_SW_ON 1
+#define MMC_CLOCK_GATED_HW_ON 2
+ int clk_gated; /* clock gated */
bool clk_pending_gate; /* pending clock gating */
struct work_struct clk_disable_work; /* delayed clock disable */
unsigned int clk_old; /* old clock value cache */