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