[PATCH] mmc: sdhci-pci-gli: Improve Random 4K Read Performance of GL9763E

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This patch is based on patch [1] and remove data transfer length
checking.

Due to flaws in hardware design, GL9763E takes long time to exit from L1
state. The I/O performance will suffer severe impact if it often enter
and exit L1 state.

Unfortunately, entering and exiting L1 state is signal handshake in
physical layer, software knows nothiong about it. The only way to stop
entering L1 state is to disable hardware LPM negotiation on GL9763E.

To improve read performance and take battery life into account, we reject
L1 negotiation while executing MMC_READ_MULTIPLE_BLOCK command and enable L1
negotiation again when receiving non-MMC_READ_MULTIPLE_BLOCK command.

[1]
https://patchwork.kernel.org/project/linux-mmc/list/?series=510801&archive
=both

Signed-off-by: Renius Chen <reniuschengl@xxxxxxxxx>
Signed-off-by: Jason Lai <jason.lai@xxxxxxxxxxxxxxxxxxx>
---
 drivers/mmc/host/sdhci-pci-gli.c | 193 ++++++++++---------------------
 1 file changed, 63 insertions(+), 130 deletions(-)

diff --git a/drivers/mmc/host/sdhci-pci-gli.c b/drivers/mmc/host/sdhci-pci-gli.c
index d09728c37d03..0c5aac8047f8 100644
--- a/drivers/mmc/host/sdhci-pci-gli.c
+++ b/drivers/mmc/host/sdhci-pci-gli.c
@@ -13,7 +13,6 @@
 #include <linux/mmc/mmc.h>
 #include <linux/delay.h>
 #include <linux/of.h>
-#include <linux/iopoll.h>
 #include "sdhci.h"
 #include "sdhci-pci.h"
 #include "cqhci.h"
@@ -64,7 +63,6 @@
 #define   GLI_9750_MISC_RX_INV_OFF       0x0
 #define   GLI_9750_MISC_RX_INV_VALUE     GLI_9750_MISC_RX_INV_OFF
 #define   GLI_9750_MISC_TX1_DLY_VALUE    0x5
-#define   SDHCI_GLI_9750_MISC_SSC_OFF    BIT(26)
 
 #define SDHCI_GLI_9750_TUNING_CONTROL	          0x540
 #define   SDHCI_GLI_9750_TUNING_CONTROL_EN          BIT(4)
@@ -95,9 +93,12 @@
 #define PCIE_GLI_9763E_SCR	 0x8E0
 #define   GLI_9763E_SCR_AXI_REQ	   BIT(9)
 
+#define PCIE_GLI_9763E_CFG       0x8A0
+#define   GLI_9763E_CFG_LPSN_DIS   BIT(12)
+
 #define PCIE_GLI_9763E_CFG2      0x8A4
 #define   GLI_9763E_CFG2_L1DLY     GENMASK(28, 19)
-#define   GLI_9763E_CFG2_L1DLY_MID 0x54
+#define   GLI_9763E_CFG2_L1DLY_MID 0x54		// Set L1 entry delay time to 21us
 
 #define PCIE_GLI_9763E_MMC_CTRL  0x960
 #define   GLI_9763E_HS400_SLOW     BIT(3)
@@ -139,11 +140,12 @@
 #define PCI_GLI_9755_SerDes  0x70
 #define PCI_GLI_9755_SCP_DIS   BIT(19)
 
-#define PCI_GLI_9755_MISC	    0x78
-#define   PCI_GLI_9755_MISC_SSC_OFF    BIT(26)
-
 #define GLI_MAX_TUNING_LOOP 40
 
+struct gli_host {
+	bool lpm_negotiation_enabled;
+};
+
 /* Genesys Logic chipset */
 static inline void gl9750_wt_on(struct sdhci_host *host)
 {
@@ -376,19 +378,6 @@ static void gl9750_set_pll(struct sdhci_host *host, u8 dir, u16 ldiv, u8 pdiv)
 	mdelay(1);
 }
 
-static bool gl9750_ssc_enable(struct sdhci_host *host)
-{
-	u32 misc;
-	u8 off;
-
-	gl9750_wt_on(host);
-	misc = sdhci_readl(host, SDHCI_GLI_9750_MISC);
-	off = FIELD_GET(SDHCI_GLI_9750_MISC_SSC_OFF, misc);
-	gl9750_wt_off(host);
-
-	return !off;
-}
-
 static void gl9750_set_ssc(struct sdhci_host *host, u8 enable, u8 step, u16 ppm)
 {
 	u32 pll;
@@ -410,31 +399,11 @@ static void gl9750_set_ssc(struct sdhci_host *host, u8 enable, u8 step, u16 ppm)
 
 static void gl9750_set_ssc_pll_205mhz(struct sdhci_host *host)
 {
-	bool enable = gl9750_ssc_enable(host);
-
-	/* set pll to 205MHz and ssc */
-	gl9750_set_ssc(host, enable, 0xF, 0x5A1D);
+	/* set pll to 205MHz and enable ssc */
+	gl9750_set_ssc(host, 0x1, 0x1F, 0xFFE7);
 	gl9750_set_pll(host, 0x1, 0x246, 0x0);
 }
 
-static void gl9750_set_ssc_pll_100mhz(struct sdhci_host *host)
-{
-	bool enable = gl9750_ssc_enable(host);
-
-	/* set pll to 100MHz and ssc */
-	gl9750_set_ssc(host, enable, 0xE, 0x51EC);
-	gl9750_set_pll(host, 0x1, 0x244, 0x1);
-}
-
-static void gl9750_set_ssc_pll_50mhz(struct sdhci_host *host)
-{
-	bool enable = gl9750_ssc_enable(host);
-
-	/* set pll to 50MHz and ssc */
-	gl9750_set_ssc(host, enable, 0xE, 0x51EC);
-	gl9750_set_pll(host, 0x1, 0x244, 0x3);
-}
-
 static void sdhci_gl9750_set_clock(struct sdhci_host *host, unsigned int clock)
 {
 	struct mmc_ios *ios = &host->mmc->ios;
@@ -452,10 +421,6 @@ static void sdhci_gl9750_set_clock(struct sdhci_host *host, unsigned int clock)
 	if (clock == 200000000 && ios->timing == MMC_TIMING_UHS_SDR104) {
 		host->mmc->actual_clock = 205000000;
 		gl9750_set_ssc_pll_205mhz(host);
-	} else if (clock == 100000000) {
-		gl9750_set_ssc_pll_100mhz(host);
-	} else if (clock == 50000000) {
-		gl9750_set_ssc_pll_50mhz(host);
 	}
 
 	sdhci_enable_clk(host, clk);
@@ -556,19 +521,6 @@ static void gl9755_set_pll(struct pci_dev *pdev, u8 dir, u16 ldiv, u8 pdiv)
 	mdelay(1);
 }
 
-static bool gl9755_ssc_enable(struct pci_dev *pdev)
-{
-	u32 misc;
-	u8 off;
-
-	gl9755_wt_on(pdev);
-	pci_read_config_dword(pdev, PCI_GLI_9755_MISC, &misc);
-	off = FIELD_GET(PCI_GLI_9755_MISC_SSC_OFF, misc);
-	gl9755_wt_off(pdev);
-
-	return !off;
-}
-
 static void gl9755_set_ssc(struct pci_dev *pdev, u8 enable, u8 step, u16 ppm)
 {
 	u32 pll;
@@ -590,31 +542,11 @@ static void gl9755_set_ssc(struct pci_dev *pdev, u8 enable, u8 step, u16 ppm)
 
 static void gl9755_set_ssc_pll_205mhz(struct pci_dev *pdev)
 {
-	bool enable = gl9755_ssc_enable(pdev);
-
-	/* set pll to 205MHz and ssc */
-	gl9755_set_ssc(pdev, enable, 0xF, 0x5A1D);
+	/* set pll to 205MHz and enable ssc */
+	gl9755_set_ssc(pdev, 0x1, 0x1F, 0xFFE7);
 	gl9755_set_pll(pdev, 0x1, 0x246, 0x0);
 }
 
-static void gl9755_set_ssc_pll_100mhz(struct pci_dev *pdev)
-{
-	bool enable = gl9755_ssc_enable(pdev);
-
-	/* set pll to 100MHz and ssc */
-	gl9755_set_ssc(pdev, enable, 0xE, 0x51EC);
-	gl9755_set_pll(pdev, 0x1, 0x244, 0x1);
-}
-
-static void gl9755_set_ssc_pll_50mhz(struct pci_dev *pdev)
-{
-	bool enable = gl9755_ssc_enable(pdev);
-
-	/* set pll to 50MHz and ssc */
-	gl9755_set_ssc(pdev, enable, 0xE, 0x51EC);
-	gl9755_set_pll(pdev, 0x1, 0x244, 0x3);
-}
-
 static void sdhci_gl9755_set_clock(struct sdhci_host *host, unsigned int clock)
 {
 	struct sdhci_pci_slot *slot = sdhci_priv(host);
@@ -635,10 +567,6 @@ static void sdhci_gl9755_set_clock(struct sdhci_host *host, unsigned int clock)
 	if (clock == 200000000 && ios->timing == MMC_TIMING_UHS_SDR104) {
 		host->mmc->actual_clock = 205000000;
 		gl9755_set_ssc_pll_205mhz(pdev);
-	} else if (clock == 100000000) {
-		gl9755_set_ssc_pll_100mhz(pdev);
-	} else if (clock == 50000000) {
-		gl9755_set_ssc_pll_50mhz(pdev);
 	}
 
 	sdhci_enable_clk(host, clk);
@@ -818,6 +746,53 @@ static void sdhci_gl9763e_dumpregs(struct mmc_host *mmc)
 	sdhci_dumpregs(mmc_priv(mmc));
 }
 
+static void gl9763e_set_low_power_negotiation(struct sdhci_pci_slot *slot, bool enable)
+{
+	struct pci_dev *pdev = slot->chip->pdev;
+	u32 value;
+
+	pci_read_config_dword(pdev, PCIE_GLI_9763E_VHS, &value);
+	value &= ~GLI_9763E_VHS_REV;
+	value |= FIELD_PREP(GLI_9763E_VHS_REV, GLI_9763E_VHS_REV_W);
+	pci_write_config_dword(pdev, PCIE_GLI_9763E_VHS, value);
+
+	pci_read_config_dword(pdev, PCIE_GLI_9763E_CFG, &value);
+
+	if (enable)
+		value &= ~GLI_9763E_CFG_LPSN_DIS;
+	else
+		value |= GLI_9763E_CFG_LPSN_DIS;
+
+	pci_write_config_dword(pdev, PCIE_GLI_9763E_CFG, value);
+
+	pci_read_config_dword(pdev, PCIE_GLI_9763E_VHS, &value);
+	value &= ~GLI_9763E_VHS_REV;
+	value |= FIELD_PREP(GLI_9763E_VHS_REV, GLI_9763E_VHS_REV_R);
+	pci_write_config_dword(pdev, PCIE_GLI_9763E_VHS, value);
+}
+
+static void gl9763e_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+	struct sdhci_host *host = mmc_priv(mmc);
+	struct mmc_command *cmd;
+	struct sdhci_pci_slot *slot = sdhci_priv(host);
+	struct gli_host *gli_host = sdhci_pci_priv(slot);
+
+	cmd = mrq->cmd;
+
+	if (cmd && (cmd->opcode == MMC_READ_MULTIPLE_BLOCK) && gli_host->lpm_negotiation_enabled) {
+		gl9763e_set_low_power_negotiation(slot, false);
+		gli_host->lpm_negotiation_enabled = false;
+	} else {
+		if (gli_host->lpm_negotiation_enabled == false) {
+			gl9763e_set_low_power_negotiation(slot, true);
+			gli_host->lpm_negotiation_enabled = true;
+		}
+	}
+
+	sdhci_request(mmc, mrq);
+}
+
 static void sdhci_gl9763e_cqe_pre_enable(struct mmc_host *mmc)
 {
 	struct cqhci_host *cq_host = mmc->cqe_private;
@@ -952,47 +927,6 @@ static void gli_set_gl9763e(struct sdhci_pci_slot *slot)
 	pci_write_config_dword(pdev, PCIE_GLI_9763E_VHS, value);
 }
 
-#ifdef CONFIG_PM
-static int gl9763e_runtime_suspend(struct sdhci_pci_chip *chip)
-{
-	struct sdhci_pci_slot *slot = chip->slots[0];
-	struct sdhci_host *host = slot->host;
-	u16 clock;
-
-	clock = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
-	clock &= ~(SDHCI_CLOCK_PLL_EN | SDHCI_CLOCK_CARD_EN);
-	sdhci_writew(host, clock, SDHCI_CLOCK_CONTROL);
-
-	return 0;
-}
-
-static int gl9763e_runtime_resume(struct sdhci_pci_chip *chip)
-{
-	struct sdhci_pci_slot *slot = chip->slots[0];
-	struct sdhci_host *host = slot->host;
-	u16 clock;
-
-	clock = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
-
-	clock |= SDHCI_CLOCK_PLL_EN;
-	clock &= ~SDHCI_CLOCK_INT_STABLE;
-	sdhci_writew(host, clock, SDHCI_CLOCK_CONTROL);
-
-	/* Wait max 150 ms */
-	if (read_poll_timeout(sdhci_readw, clock, (clock & SDHCI_CLOCK_INT_STABLE),
-			      1000, 150000, false, host, SDHCI_CLOCK_CONTROL)) {
-		pr_err("%s: PLL clock never stabilised.\n",
-		       mmc_hostname(host->mmc));
-		sdhci_dumpregs(host);
-	}
-
-	clock |= SDHCI_CLOCK_CARD_EN;
-	sdhci_writew(host, clock, SDHCI_CLOCK_CONTROL);
-
-	return 0;
-}
-#endif
-
 static int gli_probe_slot_gl9763e(struct sdhci_pci_slot *slot)
 {
 	struct pci_dev *pdev = slot->chip->pdev;
@@ -1016,6 +950,9 @@ static int gli_probe_slot_gl9763e(struct sdhci_pci_slot *slot)
 	gli_pcie_enable_msi(slot);
 	host->mmc_host_ops.hs400_enhanced_strobe =
 					gl9763e_hs400_enhanced_strobe;
+
+	host->mmc_host_ops.request = gl9763e_request;
+
 	gli_set_gl9763e(slot);
 	sdhci_enable_v4_mode(host);
 
@@ -1102,11 +1039,7 @@ const struct sdhci_pci_fixes sdhci_gl9763e = {
 #ifdef CONFIG_PM_SLEEP
 	.resume		= sdhci_cqhci_gli_resume,
 	.suspend	= sdhci_cqhci_gli_suspend,
-#endif
-#ifdef CONFIG_PM
-	.runtime_suspend = gl9763e_runtime_suspend,
-	.runtime_resume  = gl9763e_runtime_resume,
-	.allow_runtime_pm = true,
 #endif
 	.add_host       = gl9763e_add_host,
+	.priv_size      = sizeof(struct gli_host),
 };
-- 
2.36.1




[Index of Archives]     [Linux Memonry Technology]     [Linux USB Devel]     [Linux Media]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux