Re: [PATCH V2 13/16] OMAP: hsmmc: implement clock switcher

2011-05-12 Thread Tony Lindgren
* Adrian Hunter adrian.hun...@nokia.com [110506 02:13]:
 From: Andy Shevchenko ext-andriy.shevche...@nokia.com
 
 There are 3 new platform data methods which should help us to do a clock
 switching when notification is happened or request is started.
 
 The purpose of the patch is to avoid high frequency of MMC controller on low
 OPPs due to an HW bug in OMAP 3630.
...

 +static int hsmmc_clk_notifier(struct notifier_block *nb, unsigned long event,
 +   void *data)
 +{
 + struct cpufreq_freqs *freqs = data;
 + unsigned int threshold = 40; /* between opp1 and opp2 */
 +
 + switch (event) {
 + case CPUFREQ_PRECHANGE:
 + if (freqs-new  threshold  freqs-old = threshold) {
 + /* opp2 - opp1 */
 + hsmmc_max_freq = HSMMC_MAX_FREQ  1;
 +
 + /* Timeout is 1 sec */
 + if (!wait_event_timeout(hsmmc_max_freq_wq,
 + hsmmc_max_freq_ok(),
 + msecs_to_jiffies(1000)))
 + pr_err(MMC violates maximum frequency 
 +constraint\n);
 + }
 + break;
 + case CPUFREQ_POSTCHANGE:
 + if (freqs-old  threshold  freqs-new = threshold) {
 + /* opp1 - opp2 */
 + hsmmc_max_freq = HSMMC_MAX_FREQ;
 + }
 + break;
 + default:
 + break;
 + }
 +
 + return NOTIFY_DONE;
 +}

I think the cpufreq notifier code should be in the driver, the platform
init code should just prepare things for the driver.

Regards,

Tony
--
To unsubscribe from this list: send the line unsubscribe linux-omap in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH V2 13/16] OMAP: hsmmc: implement clock switcher

2011-05-06 Thread Adrian Hunter
From: Andy Shevchenko ext-andriy.shevche...@nokia.com

There are 3 new platform data methods which should help us to do a clock
switching when notification is happened or request is started.

The purpose of the patch is to avoid high frequency of MMC controller on low
OPPs due to an HW bug in OMAP 3630.

The algorithm:
 - the PM governor switches the clock of L3 (and therefore L4) bus on demand
 - the MMC controller clock should follow the change

We have considered two OPPs for L3/L4 bus. Thus, we have corresponding flow:
 - OPP1 - OPP2 (in case of abort - OPP1)
 - OPP2 - OPP1 (in case of abort - OPP2)

During idle the MMC gates functional clock and we don't care about the
frequency. Most important to get proper solution when notification comes during
request. Then:
 - in case of OPP1 - OPP2 we update frequency limit and it will be used for
   new coming requests (it assumes the current frequency of the controller is
   lower then new one)
 - otherwise we wait until no device has used higher frequency then upcoming
   one

Known issues and limitations:
 - originally a clock notifier was used for the core L4 iclk but for upstream
   Adrian changed to a cpufreq notifier where we assume OPP1 below 400MHz and
   OPP2 above 400MHz

Signed-off-by: Andy Shevchenko ext-andriy.shevche...@nokia.com
Signed-off-by: Adrian Hunter adrian.hun...@nokia.com
---
 arch/arm/mach-omap2/hsmmc.c   |  180 -
 arch/arm/plat-omap/include/plat/mmc.h |8 ++
 2 files changed, 187 insertions(+), 1 deletions(-)

diff --git a/arch/arm/mach-omap2/hsmmc.c b/arch/arm/mach-omap2/hsmmc.c
index 6b97fae..c37ba4f 100644
--- a/arch/arm/mach-omap2/hsmmc.c
+++ b/arch/arm/mach-omap2/hsmmc.c
@@ -10,10 +10,15 @@
  * published by the Free Software Foundation.
  */
 #include linux/kernel.h
+#include linux/err.h
 #include linux/slab.h
 #include linux/string.h
 #include linux/delay.h
+#include linux/platform_device.h
+#include linux/clk.h
+#include linux/mmc/card.h
 #include mach/hardware.h
+#include plat/clock.h
 #include plat/mmc.h
 #include plat/omap-pm.h
 #include plat/mux.h
@@ -23,6 +28,8 @@
 #include hsmmc.h
 #include control.h
 
+#define HSMMC_MAX_FREQ 4800
+
 #if defined(CONFIG_MMC_OMAP_HS) || defined(CONFIG_MMC_OMAP_HS_MODULE)
 
 static u16 control_pbias_offset;
@@ -203,6 +210,155 @@ static int nop_mmc_set_power(struct device *dev, int 
slot, int power_on,
return 0;
 }
 
+#ifdef CONFIG_ARCH_OMAP3
+static struct hsmmc_max_freq_info {
+   struct device *dev;
+   int freq;
+   int high_speed;
+} hsmmc_max_freq_info[OMAP34XX_NR_MMC];
+
+static unsigned int hsmmc_max_freq = HSMMC_MAX_FREQ;
+static DEFINE_SPINLOCK(hsmmc_max_freq_lock);
+
+static DECLARE_WAIT_QUEUE_HEAD(hsmmc_max_freq_wq);
+
+static int hsmmc_high_speed(struct device *dev)
+{
+   void *drvdata = platform_get_drvdata(to_platform_device(dev));
+   struct mmc_host *mmc = container_of(drvdata, struct mmc_host, private);
+
+   return mmc-card ? mmc_card_highspeed(mmc-card) : 0;
+}
+
+static unsigned int hsmmc_get_max_freq_hs(struct device *dev, int high_speed)
+{
+   return high_speed ? hsmmc_max_freq : hsmmc_max_freq  1;
+}
+
+static unsigned int hsmmc_get_max_freq(struct device *dev)
+{
+   return hsmmc_get_max_freq_hs(dev, hsmmc_high_speed(dev));
+}
+
+static unsigned int hsmmc_active(struct device *dev, unsigned int target_freq)
+{
+   int high_speed = hsmmc_high_speed(dev);
+   int i;
+   unsigned int max_freq, freq;
+   unsigned long flags;
+
+   spin_lock_irqsave(hsmmc_max_freq_lock, flags);
+   max_freq = hsmmc_get_max_freq_hs(dev, high_speed);
+   freq = min(target_freq, max_freq);
+   for (i = 0; i  ARRAY_SIZE(hsmmc_max_freq_info); i++) {
+   if (!hsmmc_max_freq_info[i].dev) {
+   hsmmc_max_freq_info[i].dev = dev;
+   hsmmc_max_freq_info[i].freq = freq;
+   hsmmc_max_freq_info[i].high_speed = high_speed;
+   break;
+   }
+   }
+   spin_unlock_irqrestore(hsmmc_max_freq_lock, flags);
+   return freq;
+}
+
+static void hsmmc_inactive(struct device *dev)
+{
+   int i;
+   unsigned long flags;
+
+   spin_lock_irqsave(hsmmc_max_freq_lock, flags);
+   for (i = 0; i  ARRAY_SIZE(hsmmc_max_freq_info); i++) {
+   if (hsmmc_max_freq_info[i].dev == dev) {
+   hsmmc_max_freq_info[i].dev = NULL;
+   spin_unlock_irqrestore(hsmmc_max_freq_lock, flags);
+   /*
+* Wake up the queue only in case we deactivated a
+* device.
+*/
+   wake_up(hsmmc_max_freq_wq);
+   return;
+   }
+   }
+   spin_unlock_irqrestore(hsmmc_max_freq_lock, flags);
+}
+
+static bool hsmmc_max_freq_ok(void)
+{
+   int i;
+   bool ret = true;
+   unsigned long flags;
+
+