Hi Alan, The sdhci runtime pm patch depends on the following patch to work. It is already upstream and I backported it to meego kernel. Could you please help queue it up?
Thanks -Yong > -----Original Message----- > From: [email protected] > [mailto:[email protected]] On Behalf Of Yong Wang > Sent: Friday, April 01, 2011 3:11 PM > To: [email protected] > Subject: [Meego-kernel] [PATCH] mmc: Aggressive clock gating framework > > This code is already upstream, but is needed to be backported into > 37 for meego. > > This patch modifies the MMC core code to optionally call the set_ios() > operation on the driver with the clock frequency set to 0 (gate) after > a grace period of at least 8 MCLK cycles, then restore it (ungate) > before any new request. This gives the driver the option to shut down > the MCI clock to the MMC/SD card when the clock frequency is 0, i.e. > the core has stated that the MCI clock does not need to be generated. > > It is inspired by existing clock gating code found in the OMAP and > Atmel drivers and brings this up to the host abstraction. Gating is > performed before and after any MMC request. > > This patchset implements this for the MMCI/PL180 MMC/SD host controller, > but it should be simple to switch OMAP/Atmel over to using this instead. > > mmc_set_{gated,ungated}() add variable protection to the state holders > for the clock gating code. This is particularly important when ordinary > .set_ios() calls would race with the .set_ios() call resulting from a > delayed gate operation. > > Signed-off-by: Linus Walleij <[email protected]> > Reviewed-by: Chris Ball <[email protected]> > Tested-by: Chris Ball <[email protected]> > Signed-off-by: Chris Ball <[email protected]> > --- > drivers/mmc/core/Kconfig | 11 +++ > drivers/mmc/core/core.c | 62 +++++++++++++- > drivers/mmc/core/core.h | 3 + > drivers/mmc/core/debugfs.c | 5 + > drivers/mmc/core/host.c | 205 > +++++++++++++++++++++++++++++++++++++++++++- > drivers/mmc/core/host.h | 21 +++++ > include/linux/mmc/host.h | 10 ++ > 7 files changed, 315 insertions(+), 2 deletions(-) > > diff --git a/drivers/mmc/core/Kconfig b/drivers/mmc/core/Kconfig > index bb22ffd..ef10387 100644 > --- a/drivers/mmc/core/Kconfig > +++ b/drivers/mmc/core/Kconfig > @@ -16,3 +16,14 @@ config MMC_UNSAFE_RESUME > > This option sets a default which can be overridden by the > module parameter "removable=0" or "removable=1". > + > +config MMC_CLKGATE > + bool "MMC host clock gating (EXPERIMENTAL)" > + depends on EXPERIMENTAL > + help > + This will attempt to aggressively gate the clock to the MMC card. > + This is done to save power due to gating off the logic and bus > + noise when the MMC card is not in use. Your host driver has to > + support handling this in order for it to be of any use. > + > + If unsure, say N. > diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c > index a3a780f..722af2d 100644 > --- a/drivers/mmc/core/core.c > +++ b/drivers/mmc/core/core.c > @@ -130,6 +130,8 @@ void mmc_request_done(struct mmc_host *host, > struct mmc_request *mrq) > > if (mrq->done) > mrq->done(mrq); > + > + mmc_host_clk_gate(host); > } > } > > @@ -190,6 +192,7 @@ mmc_start_request(struct mmc_host *host, struct > mmc_request *mrq) > mrq->stop->mrq = mrq; > } > } > + mmc_host_clk_ungate(host); > host->ops->request(host, mrq); > } > > @@ -296,7 +299,7 @@ void mmc_set_data_timeout(struct mmc_data *data, > const struct mmc_card *card) > > timeout_us = data->timeout_ns / 1000; > timeout_us += data->timeout_clks * 1000 / > - (card->host->ios.clock / 1000); > + (mmc_host_clk_rate(card->host) / 1000); > > if (data->flags & MMC_DATA_WRITE) > /* > @@ -614,6 +617,8 @@ static inline void mmc_set_ios(struct mmc_host *host) > ios->power_mode, ios->chip_select, ios->vdd, > ios->bus_width, ios->timing); > > + if (ios->clock > 0) > + mmc_set_ungated(host); > host->ops->set_ios(host, ios); > } > > @@ -641,6 +646,61 @@ void mmc_set_clock(struct mmc_host *host, > unsigned int hz) > mmc_set_ios(host); > } > > +#ifdef CONFIG_MMC_CLKGATE > +/* > + * This gates the clock by setting it to 0 Hz. > + */ > +void mmc_gate_clock(struct mmc_host *host) > +{ > + unsigned long flags; > + > + spin_lock_irqsave(&host->clk_lock, flags); > + host->clk_old = host->ios.clock; > + host->ios.clock = 0; > + host->clk_gated = true; > + spin_unlock_irqrestore(&host->clk_lock, flags); > + mmc_set_ios(host); > +} > + > +/* > + * This restores the clock from gating by using the cached > + * clock value. > + */ > +void mmc_ungate_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); > + /* This call will also set host->clk_gated to false */ > + mmc_set_clock(host, host->clk_old); > + } > +} > + > +void mmc_set_ungated(struct mmc_host *host) > +{ > + unsigned long flags; > + > + /* > + * We've been given a new frequency while the clock is gated, > + * so make sure we regard this as ungating it. > + */ > + spin_lock_irqsave(&host->clk_lock, flags); > + host->clk_gated = false; > + spin_unlock_irqrestore(&host->clk_lock, flags); > +} > + > +#else > +void mmc_set_ungated(struct mmc_host *host) > +{ > +} > +#endif > + > /* > * Change the bus mode (open drain/push-pull) of a host. > */ > diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h > index 77240cd..026c975 100644 > --- a/drivers/mmc/core/core.h > +++ b/drivers/mmc/core/core.h > @@ -33,6 +33,9 @@ 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_ungate_clock(struct mmc_host *host); > +void mmc_set_ungated(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/debugfs.c b/drivers/mmc/core/debugfs.c > index eed1405..998797e 100644 > --- a/drivers/mmc/core/debugfs.c > +++ b/drivers/mmc/core/debugfs.c > @@ -183,6 +183,11 @@ void mmc_add_host_debugfs(struct mmc_host > *host) > &mmc_clock_fops)) > goto err_node; > > +#ifdef CONFIG_MMC_CLKGATE > + if (!debugfs_create_u32("clk_delay", (S_IRUSR | S_IWUSR), > + root, &host->clk_delay)) > + goto err_node; > +#endif > return; > > err_node: > diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c > index 10b8af2..92e3370 100644 > --- a/drivers/mmc/core/host.c > +++ b/drivers/mmc/core/host.c > @@ -3,6 +3,7 @@ > * > * Copyright (C) 2003 Russell King, All Rights Reserved. > * Copyright (C) 2007-2008 Pierre Ossman > + * Copyright (C) 2010 Linus Walleij > * > * This program is free software; you can redistribute it and/or modify > * it under the terms of the GNU General Public License version 2 as > @@ -20,6 +21,7 @@ > #include <linux/suspend.h> > > #include <linux/mmc/host.h> > +#include <linux/mmc/card.h> > > #include "core.h" > #include "host.h" > @@ -50,6 +52,204 @@ void mmc_unregister_host_class(void) > static DEFINE_IDR(mmc_host_idr); > static DEFINE_SPINLOCK(mmc_host_lock); > > +#ifdef CONFIG_MMC_CLKGATE > + > +/* > + * Enabling clock gating will make the core call out to the host > + * once up and once down when it performs a request or card operation > + * intermingled in any fashion. The driver will see this through > + * set_ios() operations with ios.clock field set to 0 to gate (disable) > + * the block clock, and to the old frequency to enable it again. > + */ > +static void mmc_host_clk_gate_delayed(struct mmc_host *host) > +{ > + unsigned long tick_ns; > + unsigned long freq = host->ios.clock; > + unsigned long flags; > + > + if (!freq) { > + pr_debug("%s: frequency set to 0 in disable function, " > + "this means the clock is already disabled.\n", > + mmc_hostname(host)); > + return; > + } > + /* > + * New requests may have appeared while we were scheduling, > + * then there is no reason to delay the check before > + * clk_disable(). > + */ > + spin_lock_irqsave(&host->clk_lock, flags); > + > + /* > + * Delay n bus cycles (at least 8 from MMC spec) before attempting > + * to disable the MCI block clock. The reference count may have > + * gone up again after this delay due to rescheduling! > + */ > + if (!host->clk_requests) { > + spin_unlock_irqrestore(&host->clk_lock, flags); > + tick_ns = DIV_ROUND_UP(1000000000, freq); > + ndelay(host->clk_delay * tick_ns); > + } else { > + /* New users appeared while waiting for this work */ > + spin_unlock_irqrestore(&host->clk_lock, flags); > + return; > + } > + mutex_lock(&host->clk_gate_mutex); > + spin_lock_irqsave(&host->clk_lock, flags); > + if (!host->clk_requests) { > + spin_unlock_irqrestore(&host->clk_lock, flags); > + /* This will set host->ios.clock to 0 */ > + mmc_gate_clock(host); > + spin_lock_irqsave(&host->clk_lock, flags); > + pr_debug("%s: gated MCI clock\n", mmc_hostname(host)); > + } > + spin_unlock_irqrestore(&host->clk_lock, flags); > + mutex_unlock(&host->clk_gate_mutex); > +} > + > +/* > + * Internal work. Work to disable the clock at some later point. > + */ > +static void mmc_host_clk_gate_work(struct work_struct *work) > +{ > + struct mmc_host *host = container_of(work, struct mmc_host, > + clk_gate_work); > + > + mmc_host_clk_gate_delayed(host); > +} > + > +/** > + * mmc_host_clk_ungate - ungate hardware MCI clocks > + * @host: host to ungate. > + * > + * Makes sure the host ios.clock is restored to a non-zero value > + * past this call. Increase clock reference count and ungate clock > + * if we're the first user. > + */ > +void mmc_host_clk_ungate(struct mmc_host *host) > +{ > + unsigned long flags; > + > + mutex_lock(&host->clk_gate_mutex); > + spin_lock_irqsave(&host->clk_lock, flags); > + if (host->clk_gated) { > + spin_unlock_irqrestore(&host->clk_lock, flags); > + mmc_ungate_clock(host); > + spin_lock_irqsave(&host->clk_lock, flags); > + pr_debug("%s: ungated MCI clock\n", mmc_hostname(host)); > + } > + host->clk_requests++; > + spin_unlock_irqrestore(&host->clk_lock, flags); > + mutex_unlock(&host->clk_gate_mutex); > +} > + > +/** > + * mmc_host_may_gate_card - check if this card may be gated > + * @card: card to check. > + */ > +static bool mmc_host_may_gate_card(struct mmc_card *card) > +{ > + /* If there is no card we may gate it */ > + if (!card) > + return true; > + /* > + * Don't gate SDIO cards! These need to be clocked at all times > + * since they may be independent systems generating interrupts > + * and other events. The clock requests counter from the core will > + * go down to zero since the core does not need it, but we will not > + * gate the clock, because there is somebody out there that may still > + * be using it. > + */ > + if (mmc_card_sdio(card)) > + return false; > + > + return true; > +} > + > +/** > + * mmc_host_clk_gate - gate off hardware MCI clocks > + * @host: host to gate. > + * > + * Calls the host driver with ios.clock set to zero as often as possible > + * in order to gate off hardware MCI clocks. Decrease clock reference > + * count and schedule disabling of clock. > + */ > +void mmc_host_clk_gate(struct mmc_host *host) > +{ > + unsigned long flags; > + > + spin_lock_irqsave(&host->clk_lock, flags); > + host->clk_requests--; > + if (mmc_host_may_gate_card(host->card) && > + !host->clk_requests) > + schedule_work(&host->clk_gate_work); > + spin_unlock_irqrestore(&host->clk_lock, flags); > +} > + > +/** > + * mmc_host_clk_rate - get current clock frequency setting > + * @host: host to get the clock frequency for. > + * > + * Returns current clock frequency regardless of gating. > + */ > +unsigned int mmc_host_clk_rate(struct mmc_host *host) > +{ > + unsigned long freq; > + unsigned long flags; > + > + spin_lock_irqsave(&host->clk_lock, flags); > + if (host->clk_gated) > + freq = host->clk_old; > + else > + freq = host->ios.clock; > + spin_unlock_irqrestore(&host->clk_lock, flags); > + return freq; > +} > + > +/** > + * mmc_host_clk_init - set up clock gating code > + * @host: host with potential clock to control > + */ > +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; > + INIT_WORK(&host->clk_gate_work, mmc_host_clk_gate_work); > + spin_lock_init(&host->clk_lock); > + mutex_init(&host->clk_gate_mutex); > +} > + > +/** > + * mmc_host_clk_exit - shut down clock gating code > + * @host: host with potential clock to control > + */ > +static inline void mmc_host_clk_exit(struct mmc_host *host) > +{ > + /* > + * Wait for any outstanding gate and then make sure we're > + * ungated before exiting. > + */ > + if (cancel_work_sync(&host->clk_gate_work)) > + mmc_host_clk_gate_delayed(host); > + if (host->clk_gated) > + mmc_host_clk_ungate(host); > + BUG_ON(host->clk_requests > 0); > +} > + > +#else > + > +static inline void mmc_host_clk_init(struct mmc_host *host) > +{ > +} > + > +static inline void mmc_host_clk_exit(struct mmc_host *host) > +{ > +} > + > +#endif > + > /** > * mmc_alloc_host - initialise the per-host structure. > * @extra: sizeof private data structure > @@ -82,6 +282,8 @@ 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); > + > spin_lock_init(&host->lock); > init_waitqueue_head(&host->wq); > INIT_DELAYED_WORK(&host->detect, mmc_rescan); > @@ -163,6 +365,8 @@ void mmc_remove_host(struct mmc_host *host) > device_del(&host->class_dev); > > led_trigger_unregister_simple(host->led); > + > + mmc_host_clk_exit(host); > } > > EXPORT_SYMBOL(mmc_remove_host); > @@ -183,4 +387,3 @@ void mmc_free_host(struct mmc_host *host) > } > > EXPORT_SYMBOL(mmc_free_host); > - > diff --git a/drivers/mmc/core/host.h b/drivers/mmc/core/host.h > index 8c87e11..de199f9 100644 > --- a/drivers/mmc/core/host.h > +++ b/drivers/mmc/core/host.h > @@ -10,10 +10,31 @@ > */ > #ifndef _MMC_CORE_HOST_H > #define _MMC_CORE_HOST_H > +#include <linux/mmc/host.h> > > int mmc_register_host_class(void); > void mmc_unregister_host_class(void); > > +#ifdef CONFIG_MMC_CLKGATE > +void mmc_host_clk_ungate(struct mmc_host *host); > +void mmc_host_clk_gate(struct mmc_host *host); > +unsigned int mmc_host_clk_rate(struct mmc_host *host); > + > +#else > +static inline void mmc_host_clk_ungate(struct mmc_host *host) > +{ > +} > + > +static inline void mmc_host_clk_gate(struct mmc_host *host) > +{ > +} > + > +static inline unsigned int mmc_host_clk_rate(struct mmc_host *host) > +{ > + return host->ios.clock; > +} > +#endif > + > void mmc_host_deeper_disable(struct work_struct *work); > > #endif > diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h > index 30f6fad..381c77f 100644 > --- a/include/linux/mmc/host.h > +++ b/include/linux/mmc/host.h > @@ -172,6 +172,16 @@ struct mmc_host { > > 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 */ > + struct work_struct clk_gate_work; /* delayed clock gate */ > + unsigned int clk_old; /* old clock value cache */ > + spinlock_t clk_lock; /* lock for clk fields */ > + struct mutex clk_gate_mutex; /* mutex for clock gating */ > +#endif > + > /* host specific block data */ > unsigned int max_seg_size; /* see > blk_queue_max_segment_size */ > unsigned short max_segs; /* see blk_queue_max_segments > */ > -- > 1.5.5.1 > > _______________________________________________ > MeeGo-kernel mailing list > [email protected] > http://lists.meego.com/listinfo/meego-kernel _______________________________________________ MeeGo-kernel mailing list [email protected] http://lists.meego.com/listinfo/meego-kernel
