mmc: core: Add support for notifying host driver while scaling clocks

Host drivers can participate in clock scaling by registering
->notify_load host operation, which allows host driver to carry out
platform specific operations for further power savings or increasing
throughput based on whether load is LOW or HIGH respectively. This
can be applicable to non-ultra high speed cards as well so remove
the check for ultra high speed cards to initialize clock scaling.

Change-Id: Icaab9520135e384f5470db68b2f25c5cdce5663a
Signed-off-by: Sujit Reddy Thumma <sthumma@codeaurora.org>
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 73a1b41..9fc599b 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -2864,6 +2864,40 @@
 	return ret;
 }
 
+static int mmc_clk_update_freq(struct mmc_host *host,
+		unsigned long freq, enum mmc_load state)
+{
+	int err = 0;
+
+	if (host->ops->notify_load) {
+		err = host->ops->notify_load(host, state);
+		if (err)
+			goto out;
+	}
+
+	if (freq != host->clk_scaling.curr_freq) {
+		if (!mmc_is_vaild_state_for_clk_scaling(host)) {
+			err = -EAGAIN;
+			goto error;
+		}
+
+		err = host->bus_ops->change_bus_speed(host, &freq);
+		if (!err)
+			host->clk_scaling.curr_freq = freq;
+		else
+			pr_err("%s: %s: failed (%d) at freq=%lu\n",
+				mmc_hostname(host), __func__, err, freq);
+	}
+error:
+	if (err) {
+		/* restore previous state */
+		if (host->ops->notify_load)
+			host->ops->notify_load(host, host->clk_scaling.state);
+	}
+out:
+	return err;
+}
+
 /**
  * mmc_clk_scaling() - clock scaling decision algorithm
  * @host:	pointer to mmc host structure
@@ -2889,6 +2923,7 @@
 	unsigned int up_threshold = host->clk_scaling.up_threshold;
 	unsigned int down_threshold = host->clk_scaling.down_threshold;
 	bool queue_scale_down_work = false;
+	enum mmc_load state;
 
 	if (!card || !host->bus_ops || !host->bus_ops->change_bus_speed) {
 		pr_err("%s: %s: invalid entry\n", mmc_hostname(host), __func__);
@@ -2916,6 +2951,7 @@
 	busy_time_ms = host->clk_scaling.busy_time_us / USEC_PER_MSEC;
 
 	freq = host->clk_scaling.curr_freq;
+	state = host->clk_scaling.state;
 
 	/*
 	 * Note that the max. and min. frequency should be based
@@ -2924,28 +2960,24 @@
 	 */
 	if ((busy_time_ms * 100 > total_time_ms * up_threshold)) {
 		freq = mmc_get_max_frequency(host);
+		state = MMC_LOAD_HIGH;
 	} else if ((busy_time_ms * 100 < total_time_ms * down_threshold)) {
 		if (!from_wq)
 			queue_scale_down_work = true;
 		freq = mmc_get_min_frequency(host);
+		state = MMC_LOAD_LOW;
 	}
 
-	if (freq != host->clk_scaling.curr_freq) {
+	if (state != host->clk_scaling.state) {
 		if (!queue_scale_down_work) {
 			if (!from_wq)
 				cancel_delayed_work_sync(
 						&host->clk_scaling.work);
-
-			if (!mmc_is_vaild_state_for_clk_scaling(host))
-				goto bypass_scaling;
-
-			err = host->bus_ops->change_bus_speed(host, &freq);
+			err = mmc_clk_update_freq(host, freq, state);
 			if (!err)
-				host->clk_scaling.curr_freq = freq;
-			else
-				pr_err("%s: %s: failed (%d) at freq=%lu\n",
-					mmc_hostname(host), __func__, err,
-					freq);
+				host->clk_scaling.state = state;
+			else if (err == -EAGAIN)
+				goto no_reset_stats;
 		} else {
 			/*
 			 * We hold claim host while queueing the scale down
@@ -2954,13 +2986,12 @@
 			 */
 			queue_delayed_work(system_nrt_wq,
 					&host->clk_scaling.work, 1);
-			host->clk_scaling.in_progress = false;
-			goto out;
+			goto no_reset_stats;
 		}
 	}
 
 	mmc_reset_clk_scale_stats(host);
-bypass_scaling:
+no_reset_stats:
 	host->clk_scaling.in_progress = false;
 out:
 	return;
@@ -3007,6 +3038,9 @@
 
 	INIT_DELAYED_WORK(&host->clk_scaling.work, mmc_clk_scale_work);
 	host->clk_scaling.curr_freq = mmc_get_max_frequency(host);
+	if (host->ops->notify_load)
+		host->ops->notify_load(host, MMC_LOAD_HIGH);
+	host->clk_scaling.state = MMC_LOAD_HIGH;
 	mmc_reset_clk_scale_stats(host);
 	host->clk_scaling.enable = true;
 	host->clk_scaling.initialized = true;
diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c
index c0a4cef..6c03bfc 100644
--- a/drivers/mmc/core/host.c
+++ b/drivers/mmc/core/host.c
@@ -466,12 +466,8 @@
 		goto err;
 
 	if (value && !mmc_can_scale_clk(host)) {
-		if (mmc_card_ddr_mode(host->card) ||
-				mmc_card_hs200(host->card) ||
-				mmc_card_uhs(host->card)) {
-			host->caps2 |= MMC_CAP2_CLK_SCALE;
-			mmc_init_clk_scaling(host);
-		}
+		host->caps2 |= MMC_CAP2_CLK_SCALE;
+		mmc_init_clk_scaling(host);
 
 		if (!mmc_can_scale_clk(host)) {
 			host->caps2 &= ~MMC_CAP2_CLK_SCALE;
@@ -487,6 +483,10 @@
 			if (host->bus_ops->change_bus_speed(host, &freq))
 				goto err;
 		}
+		if (host->ops->notify_load &&
+				host->ops->notify_load(host, MMC_LOAD_HIGH))
+			goto err;
+		host->clk_scaling.state = MMC_LOAD_HIGH;
 		host->clk_scaling.initialized = false;
 	}
 	retval = count;
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 8a866cf..89f8c91 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -1734,9 +1734,7 @@
 	if (err)
 		goto remove_card;
 
-	/* Initialize clock scaling only for high frequency modes */
-	if (mmc_card_hs200(host->card) || mmc_card_ddr_mode(host->card))
-		mmc_init_clk_scaling(host);
+	mmc_init_clk_scaling(host);
 
 	return 0;
 
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index 60e0640..ddf9a87 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -1415,9 +1415,7 @@
 	if (err)
 		goto remove_card;
 
-	/* Initialize clock scaling only for high frequency modes */
-	if (mmc_card_uhs(host->card))
-		mmc_init_clk_scaling(host);
+	mmc_init_clk_scaling(host);
 
 	return 0;
 
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 9eef3a0..48be19a 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -82,6 +82,12 @@
 #define MMC_SET_DRIVER_TYPE_D	3
 };
 
+/* states to represent load on the host */
+enum mmc_load {
+	MMC_LOAD_HIGH,
+	MMC_LOAD_LOW,
+};
+
 struct mmc_host_ops {
 	/*
 	 * 'enable' is called when the host is claimed and 'disable' is called
@@ -140,6 +146,7 @@
 	void	(*hw_reset)(struct mmc_host *host);
 	unsigned long (*get_max_frequency)(struct mmc_host *host);
 	unsigned long (*get_min_frequency)(struct mmc_host *host);
+	int	(*notify_load)(struct mmc_host *, enum mmc_load);
 	int	(*stop_request)(struct mmc_host *host);
 	unsigned int	(*get_xfer_remain)(struct mmc_host *host);
 };
@@ -401,6 +408,7 @@
 		bool		initialized;
 		bool		in_progress;
 		struct delayed_work work;
+		enum mmc_load	state;
 	} clk_scaling;
 	unsigned long		private[0] ____cacheline_aligned;
 };