The following patch is on top of v8 to support hardware clock gating. I do not have hardware to test this code until next week. Comments welcome. Will revise once it is tested. Philip diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 8bf542c..5339fa8 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -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 diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index 9972808..da3ba94 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -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, diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index aacd9c5..4a73d4d 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -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); diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 77f93c3..2191553 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -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; } diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 49da4df..fa4254a 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -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). diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index c3ad105..d305ac0 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -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)); /* diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 154cbf8..00a4720 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -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) diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index d52a716..47d081d 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -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 diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index f108cee..1878cfb 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -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 */ -- To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html