From 4ec6de1f59d1f0b5ed9cfebf3c1b4080d7215dc2 Mon Sep 17 00:00:00 2001
From: popcornmix <popcornmix@gmail.com>
Date: Sun, 12 May 2013 12:27:48 +0100
Subject: [PATCH 026/196] Add low-latency mode to sdcard driver. Disable with
 sdhci-bcm2708.enable_llm=0. Thanks ddv2005.

---
 drivers/mmc/host/sdhci-bcm2708.c |  17 ++--
 drivers/mmc/host/sdhci.c         | 165 ++++++++++++++++++++++++++++++---------
 drivers/mmc/host/sdhci.h         |   6 ++
 include/linux/mmc/sdhci.h        |   1 +
 4 files changed, 145 insertions(+), 44 deletions(-)

diff --git a/drivers/mmc/host/sdhci-bcm2708.c b/drivers/mmc/host/sdhci-bcm2708.c
index 7a703c2..7ce2829 100644
--- a/drivers/mmc/host/sdhci-bcm2708.c
+++ b/drivers/mmc/host/sdhci-bcm2708.c
@@ -135,6 +135,7 @@ static bool allow_highspeed = 1;
 static int emmc_clock_freq = BCM2708_EMMC_CLOCK_FREQ;
 static bool sync_after_dma = 1;
 static bool missing_status = 1;
+bool enable_llm = 1;
 
 #if 0
 static void hptime_test(void)
@@ -871,12 +872,11 @@ static irqreturn_t sdhci_bcm2708_dma_irq(int irq, void *dev_id)
 	struct sdhci_host *host = dev_id;
 	struct sdhci_bcm2708_priv *host_priv = SDHCI_HOST_PRIV(host);
 	u32 dma_cs; /* control and status register */
-	unsigned long flags;
 
 	BUG_ON(NULL == dev_id);
 	BUG_ON(NULL == host_priv->dma_chan_base);
 
-	spin_lock_irqsave(&host->lock, flags);
+	sdhci_spin_lock(host);
 
 	dma_cs = readl(host_priv->dma_chan_base + BCM2708_DMA_CS);
 
@@ -917,8 +917,7 @@ static irqreturn_t sdhci_bcm2708_dma_irq(int irq, void *dev_id)
 
 		result = IRQ_HANDLED;
 	}
-
-	spin_unlock_irqrestore(&host->lock, flags);
+	sdhci_spin_unlock(host);
 
 	return result;
 }
@@ -1193,9 +1192,12 @@ static int sdhci_bcm2708_probe(struct platform_device *pdev)
 		sdhci_bcm2708_ops.missing_status = sdhci_bcm2708_missing_status;
 	}
 
+	printk("sdhci: %s low-latency mode\n",enable_llm?"Enable":"Disable");
+
 	host->hw_name = "BCM2708_Arasan";
 	host->ops = &sdhci_bcm2708_ops;
 	host->irq = platform_get_irq(pdev, 0);
+	host->second_irq = 0;
 
 	host->quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION |
 		       SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
@@ -1256,12 +1258,13 @@ static int sdhci_bcm2708_probe(struct platform_device *pdev)
 	}
 	host_priv->dma_chan = ret;
 
-	ret = request_irq(host_priv->dma_irq, sdhci_bcm2708_dma_irq,
-			  IRQF_SHARED, DRIVER_NAME " (dma)", host);
+	ret = request_irq(host_priv->dma_irq, sdhci_bcm2708_dma_irq,0,//IRQF_SHARED,
+			  DRIVER_NAME " (dma)", host);
 	if (ret) {
 		dev_err(&pdev->dev, "cannot set DMA IRQ\n");
 		goto err_add_dma_irq;
 	}
+	host->second_irq = host_priv->dma_irq;
 	DBG("DMA CBs %p handle %08X DMA%d %p DMA IRQ %d\n",
 	    host_priv->cb_base, (unsigned)host_priv->cb_handle,
 	    host_priv->dma_chan, host_priv->dma_chan_base,
@@ -1384,6 +1387,7 @@ module_param(allow_highspeed, bool, 0444);
 module_param(emmc_clock_freq, int, 0444);
 module_param(sync_after_dma, bool, 0444);
 module_param(missing_status, bool, 0444);
+module_param(enable_llm, bool, 0444);
 
 MODULE_DESCRIPTION("Secure Digital Host Controller Interface platform driver");
 MODULE_AUTHOR("Broadcom <info@broadcom.com>");
@@ -1394,5 +1398,6 @@ MODULE_PARM_DESC(allow_highspeed, "Allow high speed transfers modes");
 MODULE_PARM_DESC(emmc_clock_freq, "Specify the speed of emmc clock");
 MODULE_PARM_DESC(sync_after_dma, "Block in driver until dma complete");
 MODULE_PARM_DESC(missing_status, "Use the missing status quirk");
+MODULE_PARM_DESC(enable_llm, "Enable low-latency mode");
 
 
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 179e83e..470860b 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -124,6 +124,91 @@ static void sdhci_dumpregs(struct sdhci_host *host)
  * Low level functions                                                       *
  *                                                                           *
 \*****************************************************************************/
+extern bool enable_llm;
+static int sdhci_locked=0;
+void sdhci_spin_lock(struct sdhci_host *host)
+{
+	spin_lock(&host->lock);
+#ifdef CONFIG_PREEMPT
+	if(enable_llm)
+	{
+		disable_irq_nosync(host->irq);
+		if(host->second_irq)
+			disable_irq_nosync(host->second_irq);
+		local_irq_enable();
+	}
+#endif
+}
+
+void sdhci_spin_unlock(struct sdhci_host *host)
+{
+#ifdef CONFIG_PREEMPT
+	if(enable_llm)
+	{
+		local_irq_disable();
+		if(host->second_irq)
+			enable_irq(host->second_irq);
+		enable_irq(host->irq);
+	}
+#endif
+	spin_unlock(&host->lock);
+}
+
+void sdhci_spin_lock_irqsave(struct sdhci_host *host,unsigned long *flags)
+{
+#ifdef CONFIG_PREEMPT
+	if(enable_llm)
+	{
+		while(sdhci_locked)
+		{
+			preempt_schedule();
+		}
+		spin_lock_irqsave(&host->lock,*flags);
+		disable_irq(host->irq);
+		if(host->second_irq)
+			disable_irq(host->second_irq);
+		local_irq_enable();
+	}
+	else
+#endif
+		spin_lock_irqsave(&host->lock,*flags);
+}
+
+void sdhci_spin_unlock_irqrestore(struct sdhci_host *host,unsigned long flags)
+{
+#ifdef CONFIG_PREEMPT
+	if(enable_llm)
+	{
+		local_irq_disable();
+		if(host->second_irq)
+			enable_irq(host->second_irq);
+		enable_irq(host->irq);
+	}
+#endif
+	spin_unlock_irqrestore(&host->lock,flags);
+}
+
+static void sdhci_spin_enable_schedule(struct sdhci_host *host)
+{
+#ifdef CONFIG_PREEMPT
+	if(enable_llm)
+	{
+		sdhci_locked = 1;
+		preempt_enable();
+	}
+#endif
+}
+
+static void sdhci_spin_disable_schedule(struct sdhci_host *host)
+{
+#ifdef CONFIG_PREEMPT
+	if(enable_llm)
+	{
+		preempt_disable();
+		sdhci_locked = 0;
+	}
+#endif
+}
 
 static void sdhci_clear_set_irqs(struct sdhci_host *host, u32 clear, u32 set)
 {
@@ -289,7 +374,7 @@ static void sdhci_led_control(struct led_classdev *led,
 	struct sdhci_host *host = container_of(led, struct sdhci_host, led);
 	unsigned long flags;
 
-	spin_lock_irqsave(&host->lock, flags);
+	sdhci_spin_lock_irqsave(host, &flags);
 
 	if (host->runtime_suspended)
 		goto out;
@@ -299,7 +384,7 @@ static void sdhci_led_control(struct led_classdev *led,
 	else
 		sdhci_activate_led(host);
 out:
-	spin_unlock_irqrestore(&host->lock, flags);
+	sdhci_spin_unlock_irqrestore(host, flags);
 }
 #endif
 
@@ -1007,7 +1092,9 @@ static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
 			return;
 		}
 		timeout--;
+		sdhci_spin_enable_schedule(host);
 		mdelay(1);
+		sdhci_spin_disable_schedule(host);
 	}
 	DBG("send cmd %d - wait 0x%X irq 0x%x\n", cmd->opcode, mask,
 	    sdhci_readl(host, SDHCI_INT_STATUS));
@@ -1234,7 +1321,9 @@ clock_set:
 			return;
 		}
 		timeout--;
+		sdhci_spin_enable_schedule(host);
 		mdelay(1);
+		sdhci_spin_disable_schedule(host);
 	}
 
 	clk |= SDHCI_CLOCK_CARD_EN;
@@ -1330,7 +1419,7 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
 
 	sdhci_runtime_pm_get(host);
 
-	spin_lock_irqsave(&host->lock, flags);
+	sdhci_spin_lock_irqsave(host, &flags);
 
 	WARN_ON(host->mrq != NULL);
 
@@ -1388,9 +1477,9 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
 					mmc->card->type == MMC_TYPE_MMC ?
 					MMC_SEND_TUNING_BLOCK_HS200 :
 					MMC_SEND_TUNING_BLOCK;
-				spin_unlock_irqrestore(&host->lock, flags);
+				sdhci_spin_unlock_irqrestore(host, flags);
 				sdhci_execute_tuning(mmc, tuning_opcode);
-				spin_lock_irqsave(&host->lock, flags);
+				sdhci_spin_lock_irqsave(host, &flags);
 
 				/* Restore original mmc_request structure */
 				host->mrq = mrq;
@@ -1404,7 +1493,7 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
 	}
 
 	mmiowb();
-	spin_unlock_irqrestore(&host->lock, flags);
+	sdhci_spin_unlock_irqrestore(host, flags);
 }
 
 static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
@@ -1413,10 +1502,10 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
 	int vdd_bit = -1;
 	u8 ctrl;
 
-	spin_lock_irqsave(&host->lock, flags);
+	sdhci_spin_lock_irqsave(host, &flags);
 
 	if (host->flags & SDHCI_DEVICE_DEAD) {
-		spin_unlock_irqrestore(&host->lock, flags);
+		sdhci_spin_unlock_irqrestore(host, flags);
 		if (host->vmmc && ios->power_mode == MMC_POWER_OFF)
 			mmc_regulator_set_ocr(host->mmc, host->vmmc, 0);
 		return;
@@ -1443,9 +1532,9 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
 		vdd_bit = sdhci_set_power(host, ios->vdd);
 
 	if (host->vmmc && vdd_bit != -1) {
-		spin_unlock_irqrestore(&host->lock, flags);
+		sdhci_spin_unlock_irqrestore(host, flags);
 		mmc_regulator_set_ocr(host->mmc, host->vmmc, vdd_bit);
-		spin_lock_irqsave(&host->lock, flags);
+		sdhci_spin_lock_irqsave(host, &flags);
 	}
 
 	if (host->ops->platform_send_init_74_clocks)
@@ -1583,7 +1672,7 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
 		sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
 
 	mmiowb();
-	spin_unlock_irqrestore(&host->lock, flags);
+	sdhci_spin_unlock_irqrestore(host, flags);
 }
 
 static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
@@ -1631,7 +1720,7 @@ static int sdhci_check_ro(struct sdhci_host *host)
 	unsigned long flags;
 	int is_readonly;
 
-	spin_lock_irqsave(&host->lock, flags);
+	sdhci_spin_lock_irqsave(host, &flags);
 
 	if (host->flags & SDHCI_DEVICE_DEAD)
 		is_readonly = 0;
@@ -1641,7 +1730,7 @@ static int sdhci_check_ro(struct sdhci_host *host)
 		is_readonly = !(sdhci_readl(host, SDHCI_PRESENT_STATE)
 				& SDHCI_WRITE_PROTECT);
 
-	spin_unlock_irqrestore(&host->lock, flags);
+	sdhci_spin_unlock_irqrestore(host, flags);
 
 	/* This quirk needs to be replaced by a callback-function later */
 	return host->quirks & SDHCI_QUIRK_INVERTED_WRITE_PROTECT ?
@@ -1714,9 +1803,9 @@ static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable)
 	struct sdhci_host *host = mmc_priv(mmc);
 	unsigned long flags;
 
-	spin_lock_irqsave(&host->lock, flags);
+	sdhci_spin_lock_irqsave(host, &flags);
 	sdhci_enable_sdio_irq_nolock(host, enable);
-	spin_unlock_irqrestore(&host->lock, flags);
+	sdhci_spin_unlock_irqrestore(host, flags);
 }
 
 static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host,
@@ -2060,7 +2149,7 @@ static void sdhci_card_event(struct mmc_host *mmc)
 	struct sdhci_host *host = mmc_priv(mmc);
 	unsigned long flags;
 
-	spin_lock_irqsave(&host->lock, flags);
+	sdhci_spin_lock_irqsave(host, &flags);
 
 	/* Check host->mrq first in case we are runtime suspended */
 	if (host->mrq &&
@@ -2077,7 +2166,7 @@ static void sdhci_card_event(struct mmc_host *mmc)
 		tasklet_schedule(&host->finish_tasklet);
 	}
 
-	spin_unlock_irqrestore(&host->lock, flags);
+	sdhci_spin_unlock_irqrestore(host, flags);
 }
 
 static const struct mmc_host_ops sdhci_ops = {
@@ -2116,14 +2205,14 @@ static void sdhci_tasklet_finish(unsigned long param)
 
 	host = (struct sdhci_host*)param;
 
-	spin_lock_irqsave(&host->lock, flags);
+	sdhci_spin_lock_irqsave(host, &flags);
 
         /*
          * If this tasklet gets rescheduled while running, it will
          * be run again afterwards but without any active request.
          */
 	if (!host->mrq) {
-		spin_unlock_irqrestore(&host->lock, flags);
+		sdhci_spin_unlock_irqrestore(host, flags);
 		return;
 	}
 
@@ -2161,7 +2250,7 @@ static void sdhci_tasklet_finish(unsigned long param)
 #endif
 
 	mmiowb();
-	spin_unlock_irqrestore(&host->lock, flags);
+	sdhci_spin_unlock_irqrestore(host, flags);
 
 	mmc_request_done(host->mmc, mrq);
 	sdhci_runtime_pm_put(host);
@@ -2174,7 +2263,7 @@ static void sdhci_timeout_timer(unsigned long data)
 
 	host = (struct sdhci_host*)data;
 
-	spin_lock_irqsave(&host->lock, flags);
+	sdhci_spin_lock_irqsave(host, &flags);
 
 	if (host->mrq) {
 		pr_err("%s: Timeout waiting for hardware "
@@ -2195,7 +2284,7 @@ static void sdhci_timeout_timer(unsigned long data)
 	}
 
 	mmiowb();
-	spin_unlock_irqrestore(&host->lock, flags);
+	sdhci_spin_unlock_irqrestore(host, flags);
 }
 
 static void sdhci_tuning_timer(unsigned long data)
@@ -2205,11 +2294,11 @@ static void sdhci_tuning_timer(unsigned long data)
 
 	host = (struct sdhci_host *)data;
 
-	spin_lock_irqsave(&host->lock, flags);
+	sdhci_spin_lock_irqsave(host, &flags);
 
 	host->flags |= SDHCI_NEEDS_RETUNING;
 
-	spin_unlock_irqrestore(&host->lock, flags);
+	sdhci_spin_unlock_irqrestore(host, flags);
 }
 
 /*****************************************************************************\
@@ -2433,10 +2522,10 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
 	u32 intmask, unexpected = 0;
 	int cardint = 0, max_loops = 16;
 
-	spin_lock(&host->lock);
+	sdhci_spin_lock(host);
 
 	if (host->runtime_suspended) {
-		spin_unlock(&host->lock);
+		sdhci_spin_unlock(host);
 		pr_warning("%s: got irq while runtime suspended\n",
 		       mmc_hostname(host->mmc));
 		return IRQ_HANDLED;
@@ -2540,7 +2629,7 @@ again:
 	if (intmask && --max_loops)
 		goto again;
 out:
-	spin_unlock(&host->lock);
+	sdhci_spin_unlock(host);
 
 	if (unexpected) {
 		pr_err("%s: Unexpected interrupt 0x%08x.\n",
@@ -2702,15 +2791,15 @@ int sdhci_runtime_suspend_host(struct sdhci_host *host)
 		host->flags &= ~SDHCI_NEEDS_RETUNING;
 	}
 
-	spin_lock_irqsave(&host->lock, flags);
+	sdhci_spin_lock_irqsave(host, &flags);
 	sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK);
-	spin_unlock_irqrestore(&host->lock, flags);
+	sdhci_spin_unlock_irqrestore(host, flags);
 
 	synchronize_irq(host->irq);
 
-	spin_lock_irqsave(&host->lock, flags);
+	sdhci_spin_lock_irqsave(host, &flags);
 	host->runtime_suspended = true;
-	spin_unlock_irqrestore(&host->lock, flags);
+	sdhci_spin_unlock_irqrestore(host, flags);
 
 	return ret;
 }
@@ -2736,16 +2825,16 @@ int sdhci_runtime_resume_host(struct sdhci_host *host)
 	sdhci_do_start_signal_voltage_switch(host, &host->mmc->ios);
 	if ((host_flags & SDHCI_PV_ENABLED) &&
 		!(host->quirks2 & SDHCI_QUIRK2_PRESET_VALUE_BROKEN)) {
-		spin_lock_irqsave(&host->lock, flags);
+		sdhci_spin_lock_irqsave(host, &flags);
 		sdhci_enable_preset_value(host, true);
-		spin_unlock_irqrestore(&host->lock, flags);
+		sdhci_spin_unlock_irqrestore(host, flags);
 	}
 
 	/* Set the re-tuning expiration flag */
 	if (host->flags & SDHCI_USING_RETUNING_TIMER)
 		host->flags |= SDHCI_NEEDS_RETUNING;
 
-	spin_lock_irqsave(&host->lock, flags);
+	sdhci_spin_lock_irqsave(host, &flags);
 
 	host->runtime_suspended = false;
 
@@ -2756,7 +2845,7 @@ int sdhci_runtime_resume_host(struct sdhci_host *host)
 	/* Enable Card Detection */
 	sdhci_enable_card_detection(host);
 
-	spin_unlock_irqrestore(&host->lock, flags);
+	sdhci_spin_unlock_irqrestore(host, flags);
 
 	return ret;
 }
@@ -3248,7 +3337,7 @@ int sdhci_add_host(struct sdhci_host *host)
 		host->tuning_timer.function = sdhci_tuning_timer;
 	}
 
-	ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED,
+	ret = request_irq(host->irq, sdhci_irq, 0,//IRQF_SHARED,
 		mmc_hostname(mmc), host);
 	if (ret) {
 		pr_err("%s: Failed to request IRQ %d: %d\n",
@@ -3312,7 +3401,7 @@ void sdhci_remove_host(struct sdhci_host *host, int dead)
 	unsigned long flags;
 
 	if (dead) {
-		spin_lock_irqsave(&host->lock, flags);
+		sdhci_spin_lock_irqsave(host, &flags);
 
 		host->flags |= SDHCI_DEVICE_DEAD;
 
@@ -3324,7 +3413,7 @@ void sdhci_remove_host(struct sdhci_host *host, int dead)
 			tasklet_schedule(&host->finish_tasklet);
 		}
 
-		spin_unlock_irqrestore(&host->lock, flags);
+		sdhci_spin_unlock_irqrestore(host, flags);
 	}
 
 	sdhci_disable_card_detection(host);
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index f90574e..cc393af 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -441,4 +441,10 @@ extern int sdhci_runtime_suspend_host(struct sdhci_host *host);
 extern int sdhci_runtime_resume_host(struct sdhci_host *host);
 #endif
 
+extern void sdhci_spin_lock_irqsave(struct sdhci_host *host,unsigned long *flags);
+extern void sdhci_spin_unlock_irqrestore(struct sdhci_host *host,unsigned long flags);
+extern void sdhci_spin_lock(struct sdhci_host *host);
+extern void sdhci_spin_unlock(struct sdhci_host *host);
+
+
 #endif /* __SDHCI_HW_H */
diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h
index f3a39c1..58bfab0 100644
--- a/include/linux/mmc/sdhci.h
+++ b/include/linux/mmc/sdhci.h
@@ -97,6 +97,7 @@ struct sdhci_host {
 #define SDHCI_QUIRK2_PRESET_VALUE_BROKEN		(1<<3)
 
 	int irq;		/* Device IRQ */
+	int second_irq; 	/* Additional IRQ to disable/enable in low-latency mode */
 	void __iomem *ioaddr;	/* Mapped address */
 
 	const struct sdhci_ops *ops;	/* Low level hw interface */
-- 
1.9.1

