1e51df6ceSBen Chuang // SPDX-License-Identifier: GPL-2.0+ 2e51df6ceSBen Chuang /* 3e51df6ceSBen Chuang * Copyright (C) 2019 Genesys Logic, Inc. 4e51df6ceSBen Chuang * 5e51df6ceSBen Chuang * Authors: Ben Chuang <ben.chuang@genesyslogic.com.tw> 6e51df6ceSBen Chuang * 7e51df6ceSBen Chuang * Version: v0.9.0 (2019-08-08) 8e51df6ceSBen Chuang */ 9e51df6ceSBen Chuang 10e51df6ceSBen Chuang #include <linux/bitfield.h> 11e51df6ceSBen Chuang #include <linux/bits.h> 12e51df6ceSBen Chuang #include <linux/pci.h> 13e51df6ceSBen Chuang #include <linux/mmc/mmc.h> 14e51df6ceSBen Chuang #include <linux/delay.h> 15189f1d9bSHector Martin #include <linux/of.h> 16d607667bSBen Chuang #include <linux/iopoll.h> 17e51df6ceSBen Chuang #include "sdhci.h" 18e51df6ceSBen Chuang #include "sdhci-pci.h" 19347f6be1SBen Chuang #include "cqhci.h" 20e51df6ceSBen Chuang 21e51df6ceSBen Chuang /* Genesys Logic extra registers */ 22e51df6ceSBen Chuang #define SDHCI_GLI_9750_WT 0x800 23e51df6ceSBen Chuang #define SDHCI_GLI_9750_WT_EN BIT(0) 24e51df6ceSBen Chuang #define GLI_9750_WT_EN_ON 0x1 25e51df6ceSBen Chuang #define GLI_9750_WT_EN_OFF 0x0 26e51df6ceSBen Chuang 279751baccSBen Chuang #define SDHCI_GLI_9750_CFG2 0x848 289751baccSBen Chuang #define SDHCI_GLI_9750_CFG2_L1DLY GENMASK(28, 24) 299751baccSBen Chuang #define GLI_9750_CFG2_L1DLY_VALUE 0x1F 309751baccSBen Chuang 31e51df6ceSBen Chuang #define SDHCI_GLI_9750_DRIVING 0x860 32e51df6ceSBen Chuang #define SDHCI_GLI_9750_DRIVING_1 GENMASK(11, 0) 33e51df6ceSBen Chuang #define SDHCI_GLI_9750_DRIVING_2 GENMASK(27, 26) 34e51df6ceSBen Chuang #define GLI_9750_DRIVING_1_VALUE 0xFFF 35e51df6ceSBen Chuang #define GLI_9750_DRIVING_2_VALUE 0x3 36b56ff195SBen Chuang #define SDHCI_GLI_9750_SEL_1 BIT(29) 37b56ff195SBen Chuang #define SDHCI_GLI_9750_SEL_2 BIT(31) 38b56ff195SBen Chuang #define SDHCI_GLI_9750_ALL_RST (BIT(24)|BIT(25)|BIT(28)|BIT(30)) 39e51df6ceSBen Chuang 40e51df6ceSBen Chuang #define SDHCI_GLI_9750_PLL 0x864 41786d33c8SBen Chuang #define SDHCI_GLI_9750_PLL_LDIV GENMASK(9, 0) 42786d33c8SBen Chuang #define SDHCI_GLI_9750_PLL_PDIV GENMASK(14, 12) 43786d33c8SBen Chuang #define SDHCI_GLI_9750_PLL_DIR BIT(15) 44e51df6ceSBen Chuang #define SDHCI_GLI_9750_PLL_TX2_INV BIT(23) 45e51df6ceSBen Chuang #define SDHCI_GLI_9750_PLL_TX2_DLY GENMASK(22, 20) 46e51df6ceSBen Chuang #define GLI_9750_PLL_TX2_INV_VALUE 0x1 47e51df6ceSBen Chuang #define GLI_9750_PLL_TX2_DLY_VALUE 0x0 48786d33c8SBen Chuang #define SDHCI_GLI_9750_PLLSSC_STEP GENMASK(28, 24) 49786d33c8SBen Chuang #define SDHCI_GLI_9750_PLLSSC_EN BIT(31) 50786d33c8SBen Chuang 51786d33c8SBen Chuang #define SDHCI_GLI_9750_PLLSSC 0x86C 52786d33c8SBen Chuang #define SDHCI_GLI_9750_PLLSSC_PPM GENMASK(31, 16) 53e51df6ceSBen Chuang 54e51df6ceSBen Chuang #define SDHCI_GLI_9750_SW_CTRL 0x874 55e51df6ceSBen Chuang #define SDHCI_GLI_9750_SW_CTRL_4 GENMASK(7, 6) 56e51df6ceSBen Chuang #define GLI_9750_SW_CTRL_4_VALUE 0x3 57e51df6ceSBen Chuang 58e51df6ceSBen Chuang #define SDHCI_GLI_9750_MISC 0x878 59e51df6ceSBen Chuang #define SDHCI_GLI_9750_MISC_TX1_INV BIT(2) 60e51df6ceSBen Chuang #define SDHCI_GLI_9750_MISC_RX_INV BIT(3) 61e51df6ceSBen Chuang #define SDHCI_GLI_9750_MISC_TX1_DLY GENMASK(6, 4) 62e51df6ceSBen Chuang #define GLI_9750_MISC_TX1_INV_VALUE 0x0 63e51df6ceSBen Chuang #define GLI_9750_MISC_RX_INV_ON 0x1 64e51df6ceSBen Chuang #define GLI_9750_MISC_RX_INV_OFF 0x0 65e51df6ceSBen Chuang #define GLI_9750_MISC_RX_INV_VALUE GLI_9750_MISC_RX_INV_OFF 66e51df6ceSBen Chuang #define GLI_9750_MISC_TX1_DLY_VALUE 0x5 6708df1a50SBen Chuang #define SDHCI_GLI_9750_MISC_SSC_OFF BIT(26) 68e51df6ceSBen Chuang 69e51df6ceSBen Chuang #define SDHCI_GLI_9750_TUNING_CONTROL 0x540 70e51df6ceSBen Chuang #define SDHCI_GLI_9750_TUNING_CONTROL_EN BIT(4) 71e51df6ceSBen Chuang #define GLI_9750_TUNING_CONTROL_EN_ON 0x1 72e51df6ceSBen Chuang #define GLI_9750_TUNING_CONTROL_EN_OFF 0x0 73e51df6ceSBen Chuang #define SDHCI_GLI_9750_TUNING_CONTROL_GLITCH_1 BIT(16) 74e51df6ceSBen Chuang #define SDHCI_GLI_9750_TUNING_CONTROL_GLITCH_2 GENMASK(20, 19) 75e51df6ceSBen Chuang #define GLI_9750_TUNING_CONTROL_GLITCH_1_VALUE 0x1 76e51df6ceSBen Chuang #define GLI_9750_TUNING_CONTROL_GLITCH_2_VALUE 0x2 77e51df6ceSBen Chuang 78e51df6ceSBen Chuang #define SDHCI_GLI_9750_TUNING_PARAMETERS 0x544 79e51df6ceSBen Chuang #define SDHCI_GLI_9750_TUNING_PARAMETERS_RX_DLY GENMASK(2, 0) 80e51df6ceSBen Chuang #define GLI_9750_TUNING_PARAMETERS_RX_DLY_VALUE 0x1 81e51df6ceSBen Chuang 821ae1d2d6SBen Chuang #define SDHCI_GLI_9763E_CTRL_HS400 0x7 831ae1d2d6SBen Chuang 841ae1d2d6SBen Chuang #define SDHCI_GLI_9763E_HS400_ES_REG 0x52C 851ae1d2d6SBen Chuang #define SDHCI_GLI_9763E_HS400_ES_BIT BIT(8) 861ae1d2d6SBen Chuang 871ae1d2d6SBen Chuang #define PCIE_GLI_9763E_VHS 0x884 881ae1d2d6SBen Chuang #define GLI_9763E_VHS_REV GENMASK(19, 16) 891ae1d2d6SBen Chuang #define GLI_9763E_VHS_REV_R 0x0 901ae1d2d6SBen Chuang #define GLI_9763E_VHS_REV_M 0x1 911ae1d2d6SBen Chuang #define GLI_9763E_VHS_REV_W 0x2 92347f6be1SBen Chuang #define PCIE_GLI_9763E_MB 0x888 93347f6be1SBen Chuang #define GLI_9763E_MB_CMDQ_OFF BIT(19) 9415f908faSRenius Chen #define GLI_9763E_MB_ERP_ON BIT(7) 951ae1d2d6SBen Chuang #define PCIE_GLI_9763E_SCR 0x8E0 961ae1d2d6SBen Chuang #define GLI_9763E_SCR_AXI_REQ BIT(9) 971ae1d2d6SBen Chuang 98f9e5b339SJason Lai #define PCIE_GLI_9763E_CFG 0x8A0 99f9e5b339SJason Lai #define GLI_9763E_CFG_LPSN_DIS BIT(12) 100f9e5b339SJason Lai 101edee82f7SRenius Chen #define PCIE_GLI_9763E_CFG2 0x8A4 102edee82f7SRenius Chen #define GLI_9763E_CFG2_L1DLY GENMASK(28, 19) 10334dd3cccSBen Chuang #define GLI_9763E_CFG2_L1DLY_MID 0x54 104edee82f7SRenius Chen 10598991b18SBen Chuang #define PCIE_GLI_9763E_MMC_CTRL 0x960 10698991b18SBen Chuang #define GLI_9763E_HS400_SLOW BIT(3) 10798991b18SBen Chuang 108c58c5950SRenius Chen #define PCIE_GLI_9763E_CLKRXDLY 0x934 109c58c5950SRenius Chen #define GLI_9763E_HS400_RXDLY GENMASK(31, 28) 110c58c5950SRenius Chen #define GLI_9763E_HS400_RXDLY_5 0x5 111c58c5950SRenius Chen 112347f6be1SBen Chuang #define SDHCI_GLI_9763E_CQE_BASE_ADDR 0x200 113347f6be1SBen Chuang #define GLI_9763E_CQE_TRNS_MODE (SDHCI_TRNS_MULTI | \ 114347f6be1SBen Chuang SDHCI_TRNS_BLK_CNT_EN | \ 115347f6be1SBen Chuang SDHCI_TRNS_DMA) 116347f6be1SBen Chuang 117786d33c8SBen Chuang #define PCI_GLI_9755_WT 0x800 118786d33c8SBen Chuang #define PCI_GLI_9755_WT_EN BIT(0) 119786d33c8SBen Chuang #define GLI_9755_WT_EN_ON 0x1 120786d33c8SBen Chuang #define GLI_9755_WT_EN_OFF 0x0 121786d33c8SBen Chuang 1220f1d9961SBen Chuang #define PCI_GLI_9755_PECONF 0x44 1230f1d9961SBen Chuang #define PCI_GLI_9755_LFCLK GENMASK(14, 12) 1240f1d9961SBen Chuang #define PCI_GLI_9755_DMACLK BIT(29) 125189f1d9bSHector Martin #define PCI_GLI_9755_INVERT_CD BIT(30) 126189f1d9bSHector Martin #define PCI_GLI_9755_INVERT_WP BIT(31) 1270f1d9961SBen Chuang 1289751baccSBen Chuang #define PCI_GLI_9755_CFG2 0x48 1299751baccSBen Chuang #define PCI_GLI_9755_CFG2_L1DLY GENMASK(28, 24) 1309751baccSBen Chuang #define GLI_9755_CFG2_L1DLY_VALUE 0x1F 1319751baccSBen Chuang 132786d33c8SBen Chuang #define PCI_GLI_9755_PLL 0x64 133786d33c8SBen Chuang #define PCI_GLI_9755_PLL_LDIV GENMASK(9, 0) 134786d33c8SBen Chuang #define PCI_GLI_9755_PLL_PDIV GENMASK(14, 12) 135786d33c8SBen Chuang #define PCI_GLI_9755_PLL_DIR BIT(15) 136786d33c8SBen Chuang #define PCI_GLI_9755_PLLSSC_STEP GENMASK(28, 24) 137786d33c8SBen Chuang #define PCI_GLI_9755_PLLSSC_EN BIT(31) 138786d33c8SBen Chuang 139786d33c8SBen Chuang #define PCI_GLI_9755_PLLSSC 0x68 140786d33c8SBen Chuang #define PCI_GLI_9755_PLLSSC_PPM GENMASK(15, 0) 141786d33c8SBen Chuang 142f46b54ccSRenius Chen #define PCI_GLI_9755_SerDes 0x70 143f46b54ccSRenius Chen #define PCI_GLI_9755_SCP_DIS BIT(19) 144f46b54ccSRenius Chen 14508df1a50SBen Chuang #define PCI_GLI_9755_MISC 0x78 14608df1a50SBen Chuang #define PCI_GLI_9755_MISC_SSC_OFF BIT(26) 14708df1a50SBen Chuang 14836ed2fd3SBen Chuang #define PCI_GLI_9755_PM_CTRL 0xFC 14936ed2fd3SBen Chuang #define PCI_GLI_9755_PM_STATE GENMASK(1, 0) 15036ed2fd3SBen Chuang 151e51df6ceSBen Chuang #define GLI_MAX_TUNING_LOOP 40 152e51df6ceSBen Chuang 153e51df6ceSBen Chuang /* Genesys Logic chipset */ 154e51df6ceSBen Chuang static inline void gl9750_wt_on(struct sdhci_host *host) 155e51df6ceSBen Chuang { 156e51df6ceSBen Chuang u32 wt_value; 157e51df6ceSBen Chuang u32 wt_enable; 158e51df6ceSBen Chuang 159e51df6ceSBen Chuang wt_value = sdhci_readl(host, SDHCI_GLI_9750_WT); 160e51df6ceSBen Chuang wt_enable = FIELD_GET(SDHCI_GLI_9750_WT_EN, wt_value); 161e51df6ceSBen Chuang 162e51df6ceSBen Chuang if (wt_enable == GLI_9750_WT_EN_ON) 163e51df6ceSBen Chuang return; 164e51df6ceSBen Chuang 165e51df6ceSBen Chuang wt_value &= ~SDHCI_GLI_9750_WT_EN; 166e51df6ceSBen Chuang wt_value |= FIELD_PREP(SDHCI_GLI_9750_WT_EN, GLI_9750_WT_EN_ON); 167e51df6ceSBen Chuang 168e51df6ceSBen Chuang sdhci_writel(host, wt_value, SDHCI_GLI_9750_WT); 169e51df6ceSBen Chuang } 170e51df6ceSBen Chuang 171e51df6ceSBen Chuang static inline void gl9750_wt_off(struct sdhci_host *host) 172e51df6ceSBen Chuang { 173e51df6ceSBen Chuang u32 wt_value; 174e51df6ceSBen Chuang u32 wt_enable; 175e51df6ceSBen Chuang 176e51df6ceSBen Chuang wt_value = sdhci_readl(host, SDHCI_GLI_9750_WT); 177e51df6ceSBen Chuang wt_enable = FIELD_GET(SDHCI_GLI_9750_WT_EN, wt_value); 178e51df6ceSBen Chuang 179e51df6ceSBen Chuang if (wt_enable == GLI_9750_WT_EN_OFF) 180e51df6ceSBen Chuang return; 181e51df6ceSBen Chuang 182e51df6ceSBen Chuang wt_value &= ~SDHCI_GLI_9750_WT_EN; 183e51df6ceSBen Chuang wt_value |= FIELD_PREP(SDHCI_GLI_9750_WT_EN, GLI_9750_WT_EN_OFF); 184e51df6ceSBen Chuang 185e51df6ceSBen Chuang sdhci_writel(host, wt_value, SDHCI_GLI_9750_WT); 186e51df6ceSBen Chuang } 187e51df6ceSBen Chuang 188e51df6ceSBen Chuang static void gli_set_9750(struct sdhci_host *host) 189e51df6ceSBen Chuang { 190e51df6ceSBen Chuang u32 driving_value; 191e51df6ceSBen Chuang u32 pll_value; 192e51df6ceSBen Chuang u32 sw_ctrl_value; 193e51df6ceSBen Chuang u32 misc_value; 194e51df6ceSBen Chuang u32 parameter_value; 195e51df6ceSBen Chuang u32 control_value; 196e51df6ceSBen Chuang u16 ctrl2; 197e51df6ceSBen Chuang 198e51df6ceSBen Chuang gl9750_wt_on(host); 199e51df6ceSBen Chuang 200e51df6ceSBen Chuang driving_value = sdhci_readl(host, SDHCI_GLI_9750_DRIVING); 201e51df6ceSBen Chuang pll_value = sdhci_readl(host, SDHCI_GLI_9750_PLL); 202e51df6ceSBen Chuang sw_ctrl_value = sdhci_readl(host, SDHCI_GLI_9750_SW_CTRL); 203e51df6ceSBen Chuang misc_value = sdhci_readl(host, SDHCI_GLI_9750_MISC); 204e51df6ceSBen Chuang parameter_value = sdhci_readl(host, SDHCI_GLI_9750_TUNING_PARAMETERS); 205e51df6ceSBen Chuang control_value = sdhci_readl(host, SDHCI_GLI_9750_TUNING_CONTROL); 206e51df6ceSBen Chuang 207e51df6ceSBen Chuang driving_value &= ~(SDHCI_GLI_9750_DRIVING_1); 208e51df6ceSBen Chuang driving_value &= ~(SDHCI_GLI_9750_DRIVING_2); 209e51df6ceSBen Chuang driving_value |= FIELD_PREP(SDHCI_GLI_9750_DRIVING_1, 210e51df6ceSBen Chuang GLI_9750_DRIVING_1_VALUE); 211e51df6ceSBen Chuang driving_value |= FIELD_PREP(SDHCI_GLI_9750_DRIVING_2, 212e51df6ceSBen Chuang GLI_9750_DRIVING_2_VALUE); 213b56ff195SBen Chuang driving_value &= ~(SDHCI_GLI_9750_SEL_1|SDHCI_GLI_9750_SEL_2|SDHCI_GLI_9750_ALL_RST); 214b56ff195SBen Chuang driving_value |= SDHCI_GLI_9750_SEL_2; 215e51df6ceSBen Chuang sdhci_writel(host, driving_value, SDHCI_GLI_9750_DRIVING); 216e51df6ceSBen Chuang 217e51df6ceSBen Chuang sw_ctrl_value &= ~SDHCI_GLI_9750_SW_CTRL_4; 218e51df6ceSBen Chuang sw_ctrl_value |= FIELD_PREP(SDHCI_GLI_9750_SW_CTRL_4, 219e51df6ceSBen Chuang GLI_9750_SW_CTRL_4_VALUE); 220e51df6ceSBen Chuang sdhci_writel(host, sw_ctrl_value, SDHCI_GLI_9750_SW_CTRL); 221e51df6ceSBen Chuang 222e51df6ceSBen Chuang /* reset the tuning flow after reinit and before starting tuning */ 223e51df6ceSBen Chuang pll_value &= ~SDHCI_GLI_9750_PLL_TX2_INV; 224e51df6ceSBen Chuang pll_value &= ~SDHCI_GLI_9750_PLL_TX2_DLY; 225e51df6ceSBen Chuang pll_value |= FIELD_PREP(SDHCI_GLI_9750_PLL_TX2_INV, 226e51df6ceSBen Chuang GLI_9750_PLL_TX2_INV_VALUE); 227e51df6ceSBen Chuang pll_value |= FIELD_PREP(SDHCI_GLI_9750_PLL_TX2_DLY, 228e51df6ceSBen Chuang GLI_9750_PLL_TX2_DLY_VALUE); 229e51df6ceSBen Chuang 230e51df6ceSBen Chuang misc_value &= ~SDHCI_GLI_9750_MISC_TX1_INV; 231e51df6ceSBen Chuang misc_value &= ~SDHCI_GLI_9750_MISC_RX_INV; 232e51df6ceSBen Chuang misc_value &= ~SDHCI_GLI_9750_MISC_TX1_DLY; 233e51df6ceSBen Chuang misc_value |= FIELD_PREP(SDHCI_GLI_9750_MISC_TX1_INV, 234e51df6ceSBen Chuang GLI_9750_MISC_TX1_INV_VALUE); 235e51df6ceSBen Chuang misc_value |= FIELD_PREP(SDHCI_GLI_9750_MISC_RX_INV, 236e51df6ceSBen Chuang GLI_9750_MISC_RX_INV_VALUE); 237e51df6ceSBen Chuang misc_value |= FIELD_PREP(SDHCI_GLI_9750_MISC_TX1_DLY, 238e51df6ceSBen Chuang GLI_9750_MISC_TX1_DLY_VALUE); 239e51df6ceSBen Chuang 240e51df6ceSBen Chuang parameter_value &= ~SDHCI_GLI_9750_TUNING_PARAMETERS_RX_DLY; 241e51df6ceSBen Chuang parameter_value |= FIELD_PREP(SDHCI_GLI_9750_TUNING_PARAMETERS_RX_DLY, 242e51df6ceSBen Chuang GLI_9750_TUNING_PARAMETERS_RX_DLY_VALUE); 243e51df6ceSBen Chuang 244e51df6ceSBen Chuang control_value &= ~SDHCI_GLI_9750_TUNING_CONTROL_GLITCH_1; 245e51df6ceSBen Chuang control_value &= ~SDHCI_GLI_9750_TUNING_CONTROL_GLITCH_2; 246e51df6ceSBen Chuang control_value |= FIELD_PREP(SDHCI_GLI_9750_TUNING_CONTROL_GLITCH_1, 247e51df6ceSBen Chuang GLI_9750_TUNING_CONTROL_GLITCH_1_VALUE); 248e51df6ceSBen Chuang control_value |= FIELD_PREP(SDHCI_GLI_9750_TUNING_CONTROL_GLITCH_2, 249e51df6ceSBen Chuang GLI_9750_TUNING_CONTROL_GLITCH_2_VALUE); 250e51df6ceSBen Chuang 251e51df6ceSBen Chuang sdhci_writel(host, pll_value, SDHCI_GLI_9750_PLL); 252e51df6ceSBen Chuang sdhci_writel(host, misc_value, SDHCI_GLI_9750_MISC); 253e51df6ceSBen Chuang 254e51df6ceSBen Chuang /* disable tuned clk */ 255e51df6ceSBen Chuang ctrl2 = sdhci_readw(host, SDHCI_HOST_CONTROL2); 256e51df6ceSBen Chuang ctrl2 &= ~SDHCI_CTRL_TUNED_CLK; 257e51df6ceSBen Chuang sdhci_writew(host, ctrl2, SDHCI_HOST_CONTROL2); 258e51df6ceSBen Chuang 259e51df6ceSBen Chuang /* enable tuning parameters control */ 260e51df6ceSBen Chuang control_value &= ~SDHCI_GLI_9750_TUNING_CONTROL_EN; 261e51df6ceSBen Chuang control_value |= FIELD_PREP(SDHCI_GLI_9750_TUNING_CONTROL_EN, 262e51df6ceSBen Chuang GLI_9750_TUNING_CONTROL_EN_ON); 263e51df6ceSBen Chuang sdhci_writel(host, control_value, SDHCI_GLI_9750_TUNING_CONTROL); 264e51df6ceSBen Chuang 265e51df6ceSBen Chuang /* write tuning parameters */ 266e51df6ceSBen Chuang sdhci_writel(host, parameter_value, SDHCI_GLI_9750_TUNING_PARAMETERS); 267e51df6ceSBen Chuang 268e51df6ceSBen Chuang /* disable tuning parameters control */ 269e51df6ceSBen Chuang control_value &= ~SDHCI_GLI_9750_TUNING_CONTROL_EN; 270e51df6ceSBen Chuang control_value |= FIELD_PREP(SDHCI_GLI_9750_TUNING_CONTROL_EN, 271e51df6ceSBen Chuang GLI_9750_TUNING_CONTROL_EN_OFF); 272e51df6ceSBen Chuang sdhci_writel(host, control_value, SDHCI_GLI_9750_TUNING_CONTROL); 273e51df6ceSBen Chuang 274e51df6ceSBen Chuang /* clear tuned clk */ 275e51df6ceSBen Chuang ctrl2 = sdhci_readw(host, SDHCI_HOST_CONTROL2); 276e51df6ceSBen Chuang ctrl2 &= ~SDHCI_CTRL_TUNED_CLK; 277e51df6ceSBen Chuang sdhci_writew(host, ctrl2, SDHCI_HOST_CONTROL2); 278e51df6ceSBen Chuang 279e51df6ceSBen Chuang gl9750_wt_off(host); 280e51df6ceSBen Chuang } 281e51df6ceSBen Chuang 282e51df6ceSBen Chuang static void gli_set_9750_rx_inv(struct sdhci_host *host, bool b) 283e51df6ceSBen Chuang { 284e51df6ceSBen Chuang u32 misc_value; 285e51df6ceSBen Chuang 286e51df6ceSBen Chuang gl9750_wt_on(host); 287e51df6ceSBen Chuang 288e51df6ceSBen Chuang misc_value = sdhci_readl(host, SDHCI_GLI_9750_MISC); 289e51df6ceSBen Chuang misc_value &= ~SDHCI_GLI_9750_MISC_RX_INV; 290e51df6ceSBen Chuang if (b) { 291e51df6ceSBen Chuang misc_value |= FIELD_PREP(SDHCI_GLI_9750_MISC_RX_INV, 292e51df6ceSBen Chuang GLI_9750_MISC_RX_INV_ON); 293e51df6ceSBen Chuang } else { 294e51df6ceSBen Chuang misc_value |= FIELD_PREP(SDHCI_GLI_9750_MISC_RX_INV, 295e51df6ceSBen Chuang GLI_9750_MISC_RX_INV_OFF); 296e51df6ceSBen Chuang } 297e51df6ceSBen Chuang sdhci_writel(host, misc_value, SDHCI_GLI_9750_MISC); 298e51df6ceSBen Chuang 299e51df6ceSBen Chuang gl9750_wt_off(host); 300e51df6ceSBen Chuang } 301e51df6ceSBen Chuang 302e51df6ceSBen Chuang static int __sdhci_execute_tuning_9750(struct sdhci_host *host, u32 opcode) 303e51df6ceSBen Chuang { 304e51df6ceSBen Chuang int i; 305e51df6ceSBen Chuang int rx_inv; 306e51df6ceSBen Chuang 307e51df6ceSBen Chuang for (rx_inv = 0; rx_inv < 2; rx_inv++) { 308e51df6ceSBen Chuang gli_set_9750_rx_inv(host, !!rx_inv); 309e51df6ceSBen Chuang sdhci_start_tuning(host); 310e51df6ceSBen Chuang 311e51df6ceSBen Chuang for (i = 0; i < GLI_MAX_TUNING_LOOP; i++) { 312e51df6ceSBen Chuang u16 ctrl; 313e51df6ceSBen Chuang 314e51df6ceSBen Chuang sdhci_send_tuning(host, opcode); 315e51df6ceSBen Chuang 316e51df6ceSBen Chuang if (!host->tuning_done) { 317e51df6ceSBen Chuang sdhci_abort_tuning(host, opcode); 318e51df6ceSBen Chuang break; 319e51df6ceSBen Chuang } 320e51df6ceSBen Chuang 321e51df6ceSBen Chuang ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); 322e51df6ceSBen Chuang if (!(ctrl & SDHCI_CTRL_EXEC_TUNING)) { 323e51df6ceSBen Chuang if (ctrl & SDHCI_CTRL_TUNED_CLK) 324e51df6ceSBen Chuang return 0; /* Success! */ 325e51df6ceSBen Chuang break; 326e51df6ceSBen Chuang } 327e51df6ceSBen Chuang } 328e51df6ceSBen Chuang } 329e51df6ceSBen Chuang if (!host->tuning_done) { 330e51df6ceSBen Chuang pr_info("%s: Tuning timeout, falling back to fixed sampling clock\n", 331e51df6ceSBen Chuang mmc_hostname(host->mmc)); 332e51df6ceSBen Chuang return -ETIMEDOUT; 333e51df6ceSBen Chuang } 334e51df6ceSBen Chuang 335e51df6ceSBen Chuang pr_info("%s: Tuning failed, falling back to fixed sampling clock\n", 336e51df6ceSBen Chuang mmc_hostname(host->mmc)); 337e51df6ceSBen Chuang sdhci_reset_tuning(host); 338e51df6ceSBen Chuang 339e51df6ceSBen Chuang return -EAGAIN; 340e51df6ceSBen Chuang } 341e51df6ceSBen Chuang 342e51df6ceSBen Chuang static int gl9750_execute_tuning(struct sdhci_host *host, u32 opcode) 343e51df6ceSBen Chuang { 344e51df6ceSBen Chuang host->mmc->retune_period = 0; 345e51df6ceSBen Chuang if (host->tuning_mode == SDHCI_TUNING_MODE_1) 346e51df6ceSBen Chuang host->mmc->retune_period = host->tuning_count; 347e51df6ceSBen Chuang 348e51df6ceSBen Chuang gli_set_9750(host); 349e51df6ceSBen Chuang host->tuning_err = __sdhci_execute_tuning_9750(host, opcode); 350e51df6ceSBen Chuang sdhci_end_tuning(host); 351e51df6ceSBen Chuang 352e51df6ceSBen Chuang return 0; 353e51df6ceSBen Chuang } 354e51df6ceSBen Chuang 355786d33c8SBen Chuang static void gl9750_disable_ssc_pll(struct sdhci_host *host) 356786d33c8SBen Chuang { 357786d33c8SBen Chuang u32 pll; 358786d33c8SBen Chuang 359786d33c8SBen Chuang gl9750_wt_on(host); 360786d33c8SBen Chuang pll = sdhci_readl(host, SDHCI_GLI_9750_PLL); 361786d33c8SBen Chuang pll &= ~(SDHCI_GLI_9750_PLL_DIR | SDHCI_GLI_9750_PLLSSC_EN); 362786d33c8SBen Chuang sdhci_writel(host, pll, SDHCI_GLI_9750_PLL); 363786d33c8SBen Chuang gl9750_wt_off(host); 364786d33c8SBen Chuang } 365786d33c8SBen Chuang 366786d33c8SBen Chuang static void gl9750_set_pll(struct sdhci_host *host, u8 dir, u16 ldiv, u8 pdiv) 367786d33c8SBen Chuang { 368786d33c8SBen Chuang u32 pll; 369786d33c8SBen Chuang 370786d33c8SBen Chuang gl9750_wt_on(host); 371786d33c8SBen Chuang pll = sdhci_readl(host, SDHCI_GLI_9750_PLL); 372786d33c8SBen Chuang pll &= ~(SDHCI_GLI_9750_PLL_LDIV | 373786d33c8SBen Chuang SDHCI_GLI_9750_PLL_PDIV | 374786d33c8SBen Chuang SDHCI_GLI_9750_PLL_DIR); 375786d33c8SBen Chuang pll |= FIELD_PREP(SDHCI_GLI_9750_PLL_LDIV, ldiv) | 376786d33c8SBen Chuang FIELD_PREP(SDHCI_GLI_9750_PLL_PDIV, pdiv) | 377786d33c8SBen Chuang FIELD_PREP(SDHCI_GLI_9750_PLL_DIR, dir); 378786d33c8SBen Chuang sdhci_writel(host, pll, SDHCI_GLI_9750_PLL); 379786d33c8SBen Chuang gl9750_wt_off(host); 380786d33c8SBen Chuang 381786d33c8SBen Chuang /* wait for pll stable */ 382786d33c8SBen Chuang mdelay(1); 383786d33c8SBen Chuang } 384786d33c8SBen Chuang 38508df1a50SBen Chuang static bool gl9750_ssc_enable(struct sdhci_host *host) 38608df1a50SBen Chuang { 38708df1a50SBen Chuang u32 misc; 38808df1a50SBen Chuang u8 off; 38908df1a50SBen Chuang 39008df1a50SBen Chuang gl9750_wt_on(host); 39108df1a50SBen Chuang misc = sdhci_readl(host, SDHCI_GLI_9750_MISC); 39208df1a50SBen Chuang off = FIELD_GET(SDHCI_GLI_9750_MISC_SSC_OFF, misc); 39308df1a50SBen Chuang gl9750_wt_off(host); 39408df1a50SBen Chuang 39508df1a50SBen Chuang return !off; 39608df1a50SBen Chuang } 39708df1a50SBen Chuang 398786d33c8SBen Chuang static void gl9750_set_ssc(struct sdhci_host *host, u8 enable, u8 step, u16 ppm) 399786d33c8SBen Chuang { 400786d33c8SBen Chuang u32 pll; 401786d33c8SBen Chuang u32 ssc; 402786d33c8SBen Chuang 403786d33c8SBen Chuang gl9750_wt_on(host); 404786d33c8SBen Chuang pll = sdhci_readl(host, SDHCI_GLI_9750_PLL); 405786d33c8SBen Chuang ssc = sdhci_readl(host, SDHCI_GLI_9750_PLLSSC); 406786d33c8SBen Chuang pll &= ~(SDHCI_GLI_9750_PLLSSC_STEP | 407786d33c8SBen Chuang SDHCI_GLI_9750_PLLSSC_EN); 408786d33c8SBen Chuang ssc &= ~SDHCI_GLI_9750_PLLSSC_PPM; 409786d33c8SBen Chuang pll |= FIELD_PREP(SDHCI_GLI_9750_PLLSSC_STEP, step) | 410786d33c8SBen Chuang FIELD_PREP(SDHCI_GLI_9750_PLLSSC_EN, enable); 411786d33c8SBen Chuang ssc |= FIELD_PREP(SDHCI_GLI_9750_PLLSSC_PPM, ppm); 412786d33c8SBen Chuang sdhci_writel(host, ssc, SDHCI_GLI_9750_PLLSSC); 413786d33c8SBen Chuang sdhci_writel(host, pll, SDHCI_GLI_9750_PLL); 414786d33c8SBen Chuang gl9750_wt_off(host); 415786d33c8SBen Chuang } 416786d33c8SBen Chuang 417786d33c8SBen Chuang static void gl9750_set_ssc_pll_205mhz(struct sdhci_host *host) 418786d33c8SBen Chuang { 41908df1a50SBen Chuang bool enable = gl9750_ssc_enable(host); 42008df1a50SBen Chuang 42108df1a50SBen Chuang /* set pll to 205MHz and ssc */ 42208df1a50SBen Chuang gl9750_set_ssc(host, enable, 0xF, 0x5A1D); 423786d33c8SBen Chuang gl9750_set_pll(host, 0x1, 0x246, 0x0); 424786d33c8SBen Chuang } 425786d33c8SBen Chuang 426d3c6bdb6SBen Chuang static void gl9750_set_ssc_pll_100mhz(struct sdhci_host *host) 427d3c6bdb6SBen Chuang { 42808df1a50SBen Chuang bool enable = gl9750_ssc_enable(host); 42908df1a50SBen Chuang 43008df1a50SBen Chuang /* set pll to 100MHz and ssc */ 43108df1a50SBen Chuang gl9750_set_ssc(host, enable, 0xE, 0x51EC); 432d3c6bdb6SBen Chuang gl9750_set_pll(host, 0x1, 0x244, 0x1); 433d3c6bdb6SBen Chuang } 434d3c6bdb6SBen Chuang 435d3c6bdb6SBen Chuang static void gl9750_set_ssc_pll_50mhz(struct sdhci_host *host) 436d3c6bdb6SBen Chuang { 43708df1a50SBen Chuang bool enable = gl9750_ssc_enable(host); 43808df1a50SBen Chuang 43908df1a50SBen Chuang /* set pll to 50MHz and ssc */ 44008df1a50SBen Chuang gl9750_set_ssc(host, enable, 0xE, 0x51EC); 441d3c6bdb6SBen Chuang gl9750_set_pll(host, 0x1, 0x244, 0x3); 442d3c6bdb6SBen Chuang } 443d3c6bdb6SBen Chuang 444786d33c8SBen Chuang static void sdhci_gl9750_set_clock(struct sdhci_host *host, unsigned int clock) 445786d33c8SBen Chuang { 446786d33c8SBen Chuang struct mmc_ios *ios = &host->mmc->ios; 447786d33c8SBen Chuang u16 clk; 448786d33c8SBen Chuang 449786d33c8SBen Chuang host->mmc->actual_clock = 0; 450786d33c8SBen Chuang 451786d33c8SBen Chuang gl9750_disable_ssc_pll(host); 452786d33c8SBen Chuang sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); 453786d33c8SBen Chuang 454786d33c8SBen Chuang if (clock == 0) 455786d33c8SBen Chuang return; 456786d33c8SBen Chuang 457786d33c8SBen Chuang clk = sdhci_calc_clk(host, clock, &host->mmc->actual_clock); 458786d33c8SBen Chuang if (clock == 200000000 && ios->timing == MMC_TIMING_UHS_SDR104) { 459786d33c8SBen Chuang host->mmc->actual_clock = 205000000; 460786d33c8SBen Chuang gl9750_set_ssc_pll_205mhz(host); 461d3c6bdb6SBen Chuang } else if (clock == 100000000) { 462d3c6bdb6SBen Chuang gl9750_set_ssc_pll_100mhz(host); 463d3c6bdb6SBen Chuang } else if (clock == 50000000) { 464d3c6bdb6SBen Chuang gl9750_set_ssc_pll_50mhz(host); 465786d33c8SBen Chuang } 466786d33c8SBen Chuang 467786d33c8SBen Chuang sdhci_enable_clk(host, clk); 468786d33c8SBen Chuang } 469786d33c8SBen Chuang 4709751baccSBen Chuang static void gl9750_hw_setting(struct sdhci_host *host) 4719751baccSBen Chuang { 4729751baccSBen Chuang u32 value; 4739751baccSBen Chuang 4749751baccSBen Chuang gl9750_wt_on(host); 4759751baccSBen Chuang 4769751baccSBen Chuang value = sdhci_readl(host, SDHCI_GLI_9750_CFG2); 4779751baccSBen Chuang value &= ~SDHCI_GLI_9750_CFG2_L1DLY; 4789751baccSBen Chuang /* set ASPM L1 entry delay to 7.9us */ 4799751baccSBen Chuang value |= FIELD_PREP(SDHCI_GLI_9750_CFG2_L1DLY, 4809751baccSBen Chuang GLI_9750_CFG2_L1DLY_VALUE); 4819751baccSBen Chuang sdhci_writel(host, value, SDHCI_GLI_9750_CFG2); 4829751baccSBen Chuang 4839751baccSBen Chuang gl9750_wt_off(host); 4849751baccSBen Chuang } 4859751baccSBen Chuang 48631e43f31SBen Chuang static void gli_pcie_enable_msi(struct sdhci_pci_slot *slot) 48731e43f31SBen Chuang { 48831e43f31SBen Chuang int ret; 48931e43f31SBen Chuang 49031e43f31SBen Chuang ret = pci_alloc_irq_vectors(slot->chip->pdev, 1, 1, 49131e43f31SBen Chuang PCI_IRQ_MSI | PCI_IRQ_MSIX); 49231e43f31SBen Chuang if (ret < 0) { 49331e43f31SBen Chuang pr_warn("%s: enable PCI MSI failed, error=%d\n", 49431e43f31SBen Chuang mmc_hostname(slot->host->mmc), ret); 49531e43f31SBen Chuang return; 49631e43f31SBen Chuang } 49731e43f31SBen Chuang 49831e43f31SBen Chuang slot->host->irq = pci_irq_vector(slot->chip->pdev, 0); 49931e43f31SBen Chuang } 50031e43f31SBen Chuang 501786d33c8SBen Chuang static inline void gl9755_wt_on(struct pci_dev *pdev) 502786d33c8SBen Chuang { 503786d33c8SBen Chuang u32 wt_value; 504786d33c8SBen Chuang u32 wt_enable; 505786d33c8SBen Chuang 506786d33c8SBen Chuang pci_read_config_dword(pdev, PCI_GLI_9755_WT, &wt_value); 507786d33c8SBen Chuang wt_enable = FIELD_GET(PCI_GLI_9755_WT_EN, wt_value); 508786d33c8SBen Chuang 509786d33c8SBen Chuang if (wt_enable == GLI_9755_WT_EN_ON) 510786d33c8SBen Chuang return; 511786d33c8SBen Chuang 512786d33c8SBen Chuang wt_value &= ~PCI_GLI_9755_WT_EN; 513786d33c8SBen Chuang wt_value |= FIELD_PREP(PCI_GLI_9755_WT_EN, GLI_9755_WT_EN_ON); 514786d33c8SBen Chuang 515786d33c8SBen Chuang pci_write_config_dword(pdev, PCI_GLI_9755_WT, wt_value); 516786d33c8SBen Chuang } 517786d33c8SBen Chuang 518786d33c8SBen Chuang static inline void gl9755_wt_off(struct pci_dev *pdev) 519786d33c8SBen Chuang { 520786d33c8SBen Chuang u32 wt_value; 521786d33c8SBen Chuang u32 wt_enable; 522786d33c8SBen Chuang 523786d33c8SBen Chuang pci_read_config_dword(pdev, PCI_GLI_9755_WT, &wt_value); 524786d33c8SBen Chuang wt_enable = FIELD_GET(PCI_GLI_9755_WT_EN, wt_value); 525786d33c8SBen Chuang 526786d33c8SBen Chuang if (wt_enable == GLI_9755_WT_EN_OFF) 527786d33c8SBen Chuang return; 528786d33c8SBen Chuang 529786d33c8SBen Chuang wt_value &= ~PCI_GLI_9755_WT_EN; 530786d33c8SBen Chuang wt_value |= FIELD_PREP(PCI_GLI_9755_WT_EN, GLI_9755_WT_EN_OFF); 531786d33c8SBen Chuang 532786d33c8SBen Chuang pci_write_config_dword(pdev, PCI_GLI_9755_WT, wt_value); 533786d33c8SBen Chuang } 534786d33c8SBen Chuang 535786d33c8SBen Chuang static void gl9755_disable_ssc_pll(struct pci_dev *pdev) 536786d33c8SBen Chuang { 537786d33c8SBen Chuang u32 pll; 538786d33c8SBen Chuang 539786d33c8SBen Chuang gl9755_wt_on(pdev); 540786d33c8SBen Chuang pci_read_config_dword(pdev, PCI_GLI_9755_PLL, &pll); 541786d33c8SBen Chuang pll &= ~(PCI_GLI_9755_PLL_DIR | PCI_GLI_9755_PLLSSC_EN); 542786d33c8SBen Chuang pci_write_config_dword(pdev, PCI_GLI_9755_PLL, pll); 543786d33c8SBen Chuang gl9755_wt_off(pdev); 544786d33c8SBen Chuang } 545786d33c8SBen Chuang 546786d33c8SBen Chuang static void gl9755_set_pll(struct pci_dev *pdev, u8 dir, u16 ldiv, u8 pdiv) 547786d33c8SBen Chuang { 548786d33c8SBen Chuang u32 pll; 549786d33c8SBen Chuang 550786d33c8SBen Chuang gl9755_wt_on(pdev); 551786d33c8SBen Chuang pci_read_config_dword(pdev, PCI_GLI_9755_PLL, &pll); 552786d33c8SBen Chuang pll &= ~(PCI_GLI_9755_PLL_LDIV | 553786d33c8SBen Chuang PCI_GLI_9755_PLL_PDIV | 554786d33c8SBen Chuang PCI_GLI_9755_PLL_DIR); 555786d33c8SBen Chuang pll |= FIELD_PREP(PCI_GLI_9755_PLL_LDIV, ldiv) | 556786d33c8SBen Chuang FIELD_PREP(PCI_GLI_9755_PLL_PDIV, pdiv) | 557786d33c8SBen Chuang FIELD_PREP(PCI_GLI_9755_PLL_DIR, dir); 558786d33c8SBen Chuang pci_write_config_dword(pdev, PCI_GLI_9755_PLL, pll); 559786d33c8SBen Chuang gl9755_wt_off(pdev); 560786d33c8SBen Chuang 561786d33c8SBen Chuang /* wait for pll stable */ 562786d33c8SBen Chuang mdelay(1); 563786d33c8SBen Chuang } 564786d33c8SBen Chuang 56508df1a50SBen Chuang static bool gl9755_ssc_enable(struct pci_dev *pdev) 56608df1a50SBen Chuang { 56708df1a50SBen Chuang u32 misc; 56808df1a50SBen Chuang u8 off; 56908df1a50SBen Chuang 57008df1a50SBen Chuang gl9755_wt_on(pdev); 57108df1a50SBen Chuang pci_read_config_dword(pdev, PCI_GLI_9755_MISC, &misc); 57208df1a50SBen Chuang off = FIELD_GET(PCI_GLI_9755_MISC_SSC_OFF, misc); 57308df1a50SBen Chuang gl9755_wt_off(pdev); 57408df1a50SBen Chuang 57508df1a50SBen Chuang return !off; 57608df1a50SBen Chuang } 57708df1a50SBen Chuang 578786d33c8SBen Chuang static void gl9755_set_ssc(struct pci_dev *pdev, u8 enable, u8 step, u16 ppm) 579786d33c8SBen Chuang { 580786d33c8SBen Chuang u32 pll; 581786d33c8SBen Chuang u32 ssc; 582786d33c8SBen Chuang 583786d33c8SBen Chuang gl9755_wt_on(pdev); 584786d33c8SBen Chuang pci_read_config_dword(pdev, PCI_GLI_9755_PLL, &pll); 585786d33c8SBen Chuang pci_read_config_dword(pdev, PCI_GLI_9755_PLLSSC, &ssc); 586786d33c8SBen Chuang pll &= ~(PCI_GLI_9755_PLLSSC_STEP | 587786d33c8SBen Chuang PCI_GLI_9755_PLLSSC_EN); 588786d33c8SBen Chuang ssc &= ~PCI_GLI_9755_PLLSSC_PPM; 589786d33c8SBen Chuang pll |= FIELD_PREP(PCI_GLI_9755_PLLSSC_STEP, step) | 590786d33c8SBen Chuang FIELD_PREP(PCI_GLI_9755_PLLSSC_EN, enable); 591786d33c8SBen Chuang ssc |= FIELD_PREP(PCI_GLI_9755_PLLSSC_PPM, ppm); 592786d33c8SBen Chuang pci_write_config_dword(pdev, PCI_GLI_9755_PLLSSC, ssc); 593786d33c8SBen Chuang pci_write_config_dword(pdev, PCI_GLI_9755_PLL, pll); 594786d33c8SBen Chuang gl9755_wt_off(pdev); 595786d33c8SBen Chuang } 596786d33c8SBen Chuang 597786d33c8SBen Chuang static void gl9755_set_ssc_pll_205mhz(struct pci_dev *pdev) 598786d33c8SBen Chuang { 59908df1a50SBen Chuang bool enable = gl9755_ssc_enable(pdev); 60008df1a50SBen Chuang 60108df1a50SBen Chuang /* set pll to 205MHz and ssc */ 60208df1a50SBen Chuang gl9755_set_ssc(pdev, enable, 0xF, 0x5A1D); 603786d33c8SBen Chuang gl9755_set_pll(pdev, 0x1, 0x246, 0x0); 604786d33c8SBen Chuang } 605786d33c8SBen Chuang 606d3c6bdb6SBen Chuang static void gl9755_set_ssc_pll_100mhz(struct pci_dev *pdev) 607d3c6bdb6SBen Chuang { 60808df1a50SBen Chuang bool enable = gl9755_ssc_enable(pdev); 60908df1a50SBen Chuang 61008df1a50SBen Chuang /* set pll to 100MHz and ssc */ 61108df1a50SBen Chuang gl9755_set_ssc(pdev, enable, 0xE, 0x51EC); 612d3c6bdb6SBen Chuang gl9755_set_pll(pdev, 0x1, 0x244, 0x1); 613d3c6bdb6SBen Chuang } 614d3c6bdb6SBen Chuang 615d3c6bdb6SBen Chuang static void gl9755_set_ssc_pll_50mhz(struct pci_dev *pdev) 616d3c6bdb6SBen Chuang { 61708df1a50SBen Chuang bool enable = gl9755_ssc_enable(pdev); 61808df1a50SBen Chuang 61908df1a50SBen Chuang /* set pll to 50MHz and ssc */ 62008df1a50SBen Chuang gl9755_set_ssc(pdev, enable, 0xE, 0x51EC); 621d3c6bdb6SBen Chuang gl9755_set_pll(pdev, 0x1, 0x244, 0x3); 622d3c6bdb6SBen Chuang } 623d3c6bdb6SBen Chuang 624786d33c8SBen Chuang static void sdhci_gl9755_set_clock(struct sdhci_host *host, unsigned int clock) 625786d33c8SBen Chuang { 626786d33c8SBen Chuang struct sdhci_pci_slot *slot = sdhci_priv(host); 627786d33c8SBen Chuang struct mmc_ios *ios = &host->mmc->ios; 628786d33c8SBen Chuang struct pci_dev *pdev; 629786d33c8SBen Chuang u16 clk; 630786d33c8SBen Chuang 631786d33c8SBen Chuang pdev = slot->chip->pdev; 632786d33c8SBen Chuang host->mmc->actual_clock = 0; 633786d33c8SBen Chuang 634786d33c8SBen Chuang gl9755_disable_ssc_pll(pdev); 635786d33c8SBen Chuang sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); 636786d33c8SBen Chuang 637786d33c8SBen Chuang if (clock == 0) 638786d33c8SBen Chuang return; 639786d33c8SBen Chuang 640786d33c8SBen Chuang clk = sdhci_calc_clk(host, clock, &host->mmc->actual_clock); 641786d33c8SBen Chuang if (clock == 200000000 && ios->timing == MMC_TIMING_UHS_SDR104) { 642786d33c8SBen Chuang host->mmc->actual_clock = 205000000; 643786d33c8SBen Chuang gl9755_set_ssc_pll_205mhz(pdev); 644d3c6bdb6SBen Chuang } else if (clock == 100000000) { 645d3c6bdb6SBen Chuang gl9755_set_ssc_pll_100mhz(pdev); 646d3c6bdb6SBen Chuang } else if (clock == 50000000) { 647d3c6bdb6SBen Chuang gl9755_set_ssc_pll_50mhz(pdev); 648786d33c8SBen Chuang } 649786d33c8SBen Chuang 650786d33c8SBen Chuang sdhci_enable_clk(host, clk); 651786d33c8SBen Chuang } 652786d33c8SBen Chuang 6530f1d9961SBen Chuang static void gl9755_hw_setting(struct sdhci_pci_slot *slot) 6540f1d9961SBen Chuang { 6550f1d9961SBen Chuang struct pci_dev *pdev = slot->chip->pdev; 6560f1d9961SBen Chuang u32 value; 6570f1d9961SBen Chuang 6580f1d9961SBen Chuang gl9755_wt_on(pdev); 6590f1d9961SBen Chuang 6600f1d9961SBen Chuang pci_read_config_dword(pdev, PCI_GLI_9755_PECONF, &value); 661189f1d9bSHector Martin /* 662189f1d9bSHector Martin * Apple ARM64 platforms using these chips may have 663189f1d9bSHector Martin * inverted CD/WP detection. 664189f1d9bSHector Martin */ 665189f1d9bSHector Martin if (of_property_read_bool(pdev->dev.of_node, "cd-inverted")) 666189f1d9bSHector Martin value |= PCI_GLI_9755_INVERT_CD; 667189f1d9bSHector Martin if (of_property_read_bool(pdev->dev.of_node, "wp-inverted")) 668189f1d9bSHector Martin value |= PCI_GLI_9755_INVERT_WP; 6690f1d9961SBen Chuang value &= ~PCI_GLI_9755_LFCLK; 6700f1d9961SBen Chuang value &= ~PCI_GLI_9755_DMACLK; 6710f1d9961SBen Chuang pci_write_config_dword(pdev, PCI_GLI_9755_PECONF, value); 6720f1d9961SBen Chuang 673f46b54ccSRenius Chen /* enable short circuit protection */ 674f46b54ccSRenius Chen pci_read_config_dword(pdev, PCI_GLI_9755_SerDes, &value); 675f46b54ccSRenius Chen value &= ~PCI_GLI_9755_SCP_DIS; 676f46b54ccSRenius Chen pci_write_config_dword(pdev, PCI_GLI_9755_SerDes, value); 677f46b54ccSRenius Chen 6789751baccSBen Chuang pci_read_config_dword(pdev, PCI_GLI_9755_CFG2, &value); 6799751baccSBen Chuang value &= ~PCI_GLI_9755_CFG2_L1DLY; 6809751baccSBen Chuang /* set ASPM L1 entry delay to 7.9us */ 6819751baccSBen Chuang value |= FIELD_PREP(PCI_GLI_9755_CFG2_L1DLY, 6829751baccSBen Chuang GLI_9755_CFG2_L1DLY_VALUE); 6839751baccSBen Chuang pci_write_config_dword(pdev, PCI_GLI_9755_CFG2, value); 6849751baccSBen Chuang 68536ed2fd3SBen Chuang /* toggle PM state to allow GL9755 to enter ASPM L1.2 */ 68636ed2fd3SBen Chuang pci_read_config_dword(pdev, PCI_GLI_9755_PM_CTRL, &value); 68736ed2fd3SBen Chuang value |= PCI_GLI_9755_PM_STATE; 68836ed2fd3SBen Chuang pci_write_config_dword(pdev, PCI_GLI_9755_PM_CTRL, value); 68936ed2fd3SBen Chuang value &= ~PCI_GLI_9755_PM_STATE; 69036ed2fd3SBen Chuang pci_write_config_dword(pdev, PCI_GLI_9755_PM_CTRL, value); 69136ed2fd3SBen Chuang 6920f1d9961SBen Chuang gl9755_wt_off(pdev); 6930f1d9961SBen Chuang } 6940f1d9961SBen Chuang 695e51df6ceSBen Chuang static int gli_probe_slot_gl9750(struct sdhci_pci_slot *slot) 696e51df6ceSBen Chuang { 697e51df6ceSBen Chuang struct sdhci_host *host = slot->host; 698e51df6ceSBen Chuang 6999751baccSBen Chuang gl9750_hw_setting(host); 70031e43f31SBen Chuang gli_pcie_enable_msi(slot); 701e51df6ceSBen Chuang slot->host->mmc->caps2 |= MMC_CAP2_NO_SDIO; 702e51df6ceSBen Chuang sdhci_enable_v4_mode(host); 703e51df6ceSBen Chuang 704e51df6ceSBen Chuang return 0; 705e51df6ceSBen Chuang } 706e51df6ceSBen Chuang 707e51df6ceSBen Chuang static int gli_probe_slot_gl9755(struct sdhci_pci_slot *slot) 708e51df6ceSBen Chuang { 709e51df6ceSBen Chuang struct sdhci_host *host = slot->host; 710e51df6ceSBen Chuang 7110f1d9961SBen Chuang gl9755_hw_setting(slot); 71231e43f31SBen Chuang gli_pcie_enable_msi(slot); 713e51df6ceSBen Chuang slot->host->mmc->caps2 |= MMC_CAP2_NO_SDIO; 714e51df6ceSBen Chuang sdhci_enable_v4_mode(host); 715e51df6ceSBen Chuang 716e51df6ceSBen Chuang return 0; 717e51df6ceSBen Chuang } 718e51df6ceSBen Chuang 719e51df6ceSBen Chuang static void sdhci_gli_voltage_switch(struct sdhci_host *host) 720e51df6ceSBen Chuang { 721e51df6ceSBen Chuang /* 722e51df6ceSBen Chuang * According to Section 3.6.1 signal voltage switch procedure in 723e51df6ceSBen Chuang * SD Host Controller Simplified Spec. 4.20, steps 6~8 are as 724e51df6ceSBen Chuang * follows: 725e51df6ceSBen Chuang * (6) Set 1.8V Signal Enable in the Host Control 2 register. 726e51df6ceSBen Chuang * (7) Wait 5ms. 1.8V voltage regulator shall be stable within this 727e51df6ceSBen Chuang * period. 728e51df6ceSBen Chuang * (8) If 1.8V Signal Enable is cleared by Host Controller, go to 729e51df6ceSBen Chuang * step (12). 730e51df6ceSBen Chuang * 731e51df6ceSBen Chuang * Wait 5ms after set 1.8V signal enable in Host Control 2 register 732e51df6ceSBen Chuang * to ensure 1.8V signal enable bit is set by GL9750/GL9755. 733a1149a6cSDaniel Beer * 734a1149a6cSDaniel Beer * ...however, the controller in the NUC10i3FNK4 (a 9755) requires 735a1149a6cSDaniel Beer * slightly longer than 5ms before the control register reports that 736a1149a6cSDaniel Beer * 1.8V is ready, and far longer still before the card will actually 737a1149a6cSDaniel Beer * work reliably. 738e51df6ceSBen Chuang */ 739a1149a6cSDaniel Beer usleep_range(100000, 110000); 740e51df6ceSBen Chuang } 741e51df6ceSBen Chuang 742e51df6ceSBen Chuang static void sdhci_gl9750_reset(struct sdhci_host *host, u8 mask) 743e51df6ceSBen Chuang { 744e51df6ceSBen Chuang sdhci_reset(host, mask); 745e51df6ceSBen Chuang gli_set_9750(host); 746e51df6ceSBen Chuang } 747e51df6ceSBen Chuang 748e51df6ceSBen Chuang static u32 sdhci_gl9750_readl(struct sdhci_host *host, int reg) 749e51df6ceSBen Chuang { 750e51df6ceSBen Chuang u32 value; 751e51df6ceSBen Chuang 752e51df6ceSBen Chuang value = readl(host->ioaddr + reg); 753e51df6ceSBen Chuang if (unlikely(reg == SDHCI_MAX_CURRENT && !(value & 0xff))) 754e51df6ceSBen Chuang value |= 0xc8; 755e51df6ceSBen Chuang 756e51df6ceSBen Chuang return value; 757e51df6ceSBen Chuang } 758e51df6ceSBen Chuang 759282ede76SBen Chuang #ifdef CONFIG_PM_SLEEP 760282ede76SBen Chuang static int sdhci_pci_gli_resume(struct sdhci_pci_chip *chip) 761282ede76SBen Chuang { 762282ede76SBen Chuang struct sdhci_pci_slot *slot = chip->slots[0]; 763282ede76SBen Chuang 764282ede76SBen Chuang pci_free_irq_vectors(slot->chip->pdev); 765282ede76SBen Chuang gli_pcie_enable_msi(slot); 766282ede76SBen Chuang 767282ede76SBen Chuang return sdhci_pci_resume_host(chip); 768282ede76SBen Chuang } 769347f6be1SBen Chuang 770347f6be1SBen Chuang static int sdhci_cqhci_gli_resume(struct sdhci_pci_chip *chip) 771347f6be1SBen Chuang { 772347f6be1SBen Chuang struct sdhci_pci_slot *slot = chip->slots[0]; 773347f6be1SBen Chuang int ret; 774347f6be1SBen Chuang 775347f6be1SBen Chuang ret = sdhci_pci_gli_resume(chip); 776347f6be1SBen Chuang if (ret) 777347f6be1SBen Chuang return ret; 778347f6be1SBen Chuang 779347f6be1SBen Chuang return cqhci_resume(slot->host->mmc); 780347f6be1SBen Chuang } 781347f6be1SBen Chuang 782347f6be1SBen Chuang static int sdhci_cqhci_gli_suspend(struct sdhci_pci_chip *chip) 783347f6be1SBen Chuang { 784347f6be1SBen Chuang struct sdhci_pci_slot *slot = chip->slots[0]; 785347f6be1SBen Chuang int ret; 786347f6be1SBen Chuang 787347f6be1SBen Chuang ret = cqhci_suspend(slot->host->mmc); 788347f6be1SBen Chuang if (ret) 789347f6be1SBen Chuang return ret; 790347f6be1SBen Chuang 791347f6be1SBen Chuang return sdhci_suspend_host(slot->host); 792347f6be1SBen Chuang } 793282ede76SBen Chuang #endif 794282ede76SBen Chuang 7951ae1d2d6SBen Chuang static void gl9763e_hs400_enhanced_strobe(struct mmc_host *mmc, 7961ae1d2d6SBen Chuang struct mmc_ios *ios) 7971ae1d2d6SBen Chuang { 7981ae1d2d6SBen Chuang struct sdhci_host *host = mmc_priv(mmc); 7991ae1d2d6SBen Chuang u32 val; 8001ae1d2d6SBen Chuang 8011ae1d2d6SBen Chuang val = sdhci_readl(host, SDHCI_GLI_9763E_HS400_ES_REG); 8021ae1d2d6SBen Chuang if (ios->enhanced_strobe) 8031ae1d2d6SBen Chuang val |= SDHCI_GLI_9763E_HS400_ES_BIT; 8041ae1d2d6SBen Chuang else 8051ae1d2d6SBen Chuang val &= ~SDHCI_GLI_9763E_HS400_ES_BIT; 8061ae1d2d6SBen Chuang 8071ae1d2d6SBen Chuang sdhci_writel(host, val, SDHCI_GLI_9763E_HS400_ES_REG); 8081ae1d2d6SBen Chuang } 8091ae1d2d6SBen Chuang 8101ae1d2d6SBen Chuang static void sdhci_set_gl9763e_signaling(struct sdhci_host *host, 8111ae1d2d6SBen Chuang unsigned int timing) 8121ae1d2d6SBen Chuang { 8131ae1d2d6SBen Chuang u16 ctrl_2; 8141ae1d2d6SBen Chuang 8151ae1d2d6SBen Chuang ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2); 8161ae1d2d6SBen Chuang ctrl_2 &= ~SDHCI_CTRL_UHS_MASK; 8171ae1d2d6SBen Chuang if (timing == MMC_TIMING_MMC_HS200) 8181ae1d2d6SBen Chuang ctrl_2 |= SDHCI_CTRL_UHS_SDR104; 8191ae1d2d6SBen Chuang else if (timing == MMC_TIMING_MMC_HS) 8201ae1d2d6SBen Chuang ctrl_2 |= SDHCI_CTRL_UHS_SDR25; 8211ae1d2d6SBen Chuang else if (timing == MMC_TIMING_MMC_DDR52) 8221ae1d2d6SBen Chuang ctrl_2 |= SDHCI_CTRL_UHS_DDR50; 8231ae1d2d6SBen Chuang else if (timing == MMC_TIMING_MMC_HS400) 8241ae1d2d6SBen Chuang ctrl_2 |= SDHCI_GLI_9763E_CTRL_HS400; 8251ae1d2d6SBen Chuang 8261ae1d2d6SBen Chuang sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2); 8271ae1d2d6SBen Chuang } 8281ae1d2d6SBen Chuang 829347f6be1SBen Chuang static void sdhci_gl9763e_dumpregs(struct mmc_host *mmc) 830347f6be1SBen Chuang { 831347f6be1SBen Chuang sdhci_dumpregs(mmc_priv(mmc)); 832347f6be1SBen Chuang } 833347f6be1SBen Chuang 834347f6be1SBen Chuang static void sdhci_gl9763e_cqe_pre_enable(struct mmc_host *mmc) 835347f6be1SBen Chuang { 836347f6be1SBen Chuang struct cqhci_host *cq_host = mmc->cqe_private; 837347f6be1SBen Chuang u32 value; 838347f6be1SBen Chuang 839347f6be1SBen Chuang value = cqhci_readl(cq_host, CQHCI_CFG); 840347f6be1SBen Chuang value |= CQHCI_ENABLE; 841347f6be1SBen Chuang cqhci_writel(cq_host, value, CQHCI_CFG); 842347f6be1SBen Chuang } 843347f6be1SBen Chuang 844347f6be1SBen Chuang static void sdhci_gl9763e_cqe_enable(struct mmc_host *mmc) 845347f6be1SBen Chuang { 846347f6be1SBen Chuang struct sdhci_host *host = mmc_priv(mmc); 847347f6be1SBen Chuang 848347f6be1SBen Chuang sdhci_writew(host, GLI_9763E_CQE_TRNS_MODE, SDHCI_TRANSFER_MODE); 849347f6be1SBen Chuang sdhci_cqe_enable(mmc); 850347f6be1SBen Chuang } 851347f6be1SBen Chuang 852347f6be1SBen Chuang static u32 sdhci_gl9763e_cqhci_irq(struct sdhci_host *host, u32 intmask) 853347f6be1SBen Chuang { 854347f6be1SBen Chuang int cmd_error = 0; 855347f6be1SBen Chuang int data_error = 0; 856347f6be1SBen Chuang 857347f6be1SBen Chuang if (!sdhci_cqe_irq(host, intmask, &cmd_error, &data_error)) 858347f6be1SBen Chuang return intmask; 859347f6be1SBen Chuang 860347f6be1SBen Chuang cqhci_irq(host->mmc, intmask, cmd_error, data_error); 861347f6be1SBen Chuang 862347f6be1SBen Chuang return 0; 863347f6be1SBen Chuang } 864347f6be1SBen Chuang 865347f6be1SBen Chuang static void sdhci_gl9763e_cqe_post_disable(struct mmc_host *mmc) 866347f6be1SBen Chuang { 867347f6be1SBen Chuang struct sdhci_host *host = mmc_priv(mmc); 868347f6be1SBen Chuang struct cqhci_host *cq_host = mmc->cqe_private; 869347f6be1SBen Chuang u32 value; 870347f6be1SBen Chuang 871347f6be1SBen Chuang value = cqhci_readl(cq_host, CQHCI_CFG); 872347f6be1SBen Chuang value &= ~CQHCI_ENABLE; 873347f6be1SBen Chuang cqhci_writel(cq_host, value, CQHCI_CFG); 874347f6be1SBen Chuang sdhci_writew(host, 0x0, SDHCI_TRANSFER_MODE); 875347f6be1SBen Chuang } 876347f6be1SBen Chuang 877347f6be1SBen Chuang static const struct cqhci_host_ops sdhci_gl9763e_cqhci_ops = { 878347f6be1SBen Chuang .enable = sdhci_gl9763e_cqe_enable, 879347f6be1SBen Chuang .disable = sdhci_cqe_disable, 880347f6be1SBen Chuang .dumpregs = sdhci_gl9763e_dumpregs, 881347f6be1SBen Chuang .pre_enable = sdhci_gl9763e_cqe_pre_enable, 882347f6be1SBen Chuang .post_disable = sdhci_gl9763e_cqe_post_disable, 883347f6be1SBen Chuang }; 884347f6be1SBen Chuang 885347f6be1SBen Chuang static int gl9763e_add_host(struct sdhci_pci_slot *slot) 886347f6be1SBen Chuang { 887347f6be1SBen Chuang struct device *dev = &slot->chip->pdev->dev; 888347f6be1SBen Chuang struct sdhci_host *host = slot->host; 889347f6be1SBen Chuang struct cqhci_host *cq_host; 890347f6be1SBen Chuang bool dma64; 891347f6be1SBen Chuang int ret; 892347f6be1SBen Chuang 893347f6be1SBen Chuang ret = sdhci_setup_host(host); 894347f6be1SBen Chuang if (ret) 895347f6be1SBen Chuang return ret; 896347f6be1SBen Chuang 897347f6be1SBen Chuang cq_host = devm_kzalloc(dev, sizeof(*cq_host), GFP_KERNEL); 898347f6be1SBen Chuang if (!cq_host) { 899347f6be1SBen Chuang ret = -ENOMEM; 900347f6be1SBen Chuang goto cleanup; 901347f6be1SBen Chuang } 902347f6be1SBen Chuang 903347f6be1SBen Chuang cq_host->mmio = host->ioaddr + SDHCI_GLI_9763E_CQE_BASE_ADDR; 904347f6be1SBen Chuang cq_host->ops = &sdhci_gl9763e_cqhci_ops; 905347f6be1SBen Chuang 906347f6be1SBen Chuang dma64 = host->flags & SDHCI_USE_64_BIT_DMA; 907347f6be1SBen Chuang if (dma64) 908347f6be1SBen Chuang cq_host->caps |= CQHCI_TASK_DESC_SZ_128; 909347f6be1SBen Chuang 910347f6be1SBen Chuang ret = cqhci_init(cq_host, host->mmc, dma64); 911347f6be1SBen Chuang if (ret) 912347f6be1SBen Chuang goto cleanup; 913347f6be1SBen Chuang 914347f6be1SBen Chuang ret = __sdhci_add_host(host); 915347f6be1SBen Chuang if (ret) 916347f6be1SBen Chuang goto cleanup; 917347f6be1SBen Chuang 918347f6be1SBen Chuang return 0; 919347f6be1SBen Chuang 920347f6be1SBen Chuang cleanup: 921347f6be1SBen Chuang sdhci_cleanup_host(host); 922347f6be1SBen Chuang return ret; 923347f6be1SBen Chuang } 924347f6be1SBen Chuang 925347f6be1SBen Chuang static void sdhci_gl9763e_reset(struct sdhci_host *host, u8 mask) 926347f6be1SBen Chuang { 927347f6be1SBen Chuang if ((host->mmc->caps2 & MMC_CAP2_CQE) && (mask & SDHCI_RESET_ALL) && 928347f6be1SBen Chuang host->mmc->cqe_private) 929347f6be1SBen Chuang cqhci_deactivate(host->mmc); 930347f6be1SBen Chuang sdhci_reset(host, mask); 931347f6be1SBen Chuang } 932347f6be1SBen Chuang 9331ae1d2d6SBen Chuang static void gli_set_gl9763e(struct sdhci_pci_slot *slot) 9341ae1d2d6SBen Chuang { 9351ae1d2d6SBen Chuang struct pci_dev *pdev = slot->chip->pdev; 9361ae1d2d6SBen Chuang u32 value; 9371ae1d2d6SBen Chuang 9381ae1d2d6SBen Chuang pci_read_config_dword(pdev, PCIE_GLI_9763E_VHS, &value); 9391ae1d2d6SBen Chuang value &= ~GLI_9763E_VHS_REV; 9401ae1d2d6SBen Chuang value |= FIELD_PREP(GLI_9763E_VHS_REV, GLI_9763E_VHS_REV_W); 9411ae1d2d6SBen Chuang pci_write_config_dword(pdev, PCIE_GLI_9763E_VHS, value); 9421ae1d2d6SBen Chuang 9431ae1d2d6SBen Chuang pci_read_config_dword(pdev, PCIE_GLI_9763E_SCR, &value); 9441ae1d2d6SBen Chuang value |= GLI_9763E_SCR_AXI_REQ; 9451ae1d2d6SBen Chuang pci_write_config_dword(pdev, PCIE_GLI_9763E_SCR, value); 9461ae1d2d6SBen Chuang 94798991b18SBen Chuang pci_read_config_dword(pdev, PCIE_GLI_9763E_MMC_CTRL, &value); 94898991b18SBen Chuang value &= ~GLI_9763E_HS400_SLOW; 94998991b18SBen Chuang pci_write_config_dword(pdev, PCIE_GLI_9763E_MMC_CTRL, value); 95098991b18SBen Chuang 951edee82f7SRenius Chen pci_read_config_dword(pdev, PCIE_GLI_9763E_CFG2, &value); 952edee82f7SRenius Chen value &= ~GLI_9763E_CFG2_L1DLY; 95334dd3cccSBen Chuang /* set ASPM L1 entry delay to 21us */ 954baaaf55dSBen Chuang value |= FIELD_PREP(GLI_9763E_CFG2_L1DLY, GLI_9763E_CFG2_L1DLY_MID); 955edee82f7SRenius Chen pci_write_config_dword(pdev, PCIE_GLI_9763E_CFG2, value); 956edee82f7SRenius Chen 957c58c5950SRenius Chen pci_read_config_dword(pdev, PCIE_GLI_9763E_CLKRXDLY, &value); 958c58c5950SRenius Chen value &= ~GLI_9763E_HS400_RXDLY; 959c58c5950SRenius Chen value |= FIELD_PREP(GLI_9763E_HS400_RXDLY, GLI_9763E_HS400_RXDLY_5); 960c58c5950SRenius Chen pci_write_config_dword(pdev, PCIE_GLI_9763E_CLKRXDLY, value); 961c58c5950SRenius Chen 9621ae1d2d6SBen Chuang pci_read_config_dword(pdev, PCIE_GLI_9763E_VHS, &value); 9631ae1d2d6SBen Chuang value &= ~GLI_9763E_VHS_REV; 9641ae1d2d6SBen Chuang value |= FIELD_PREP(GLI_9763E_VHS_REV, GLI_9763E_VHS_REV_R); 9651ae1d2d6SBen Chuang pci_write_config_dword(pdev, PCIE_GLI_9763E_VHS, value); 9661ae1d2d6SBen Chuang } 9671ae1d2d6SBen Chuang 968d607667bSBen Chuang #ifdef CONFIG_PM 969*1c5fd973SRen Zhijie static void gl9763e_set_low_power_negotiation(struct sdhci_pci_slot *slot, bool enable) 970*1c5fd973SRen Zhijie { 971*1c5fd973SRen Zhijie struct pci_dev *pdev = slot->chip->pdev; 972*1c5fd973SRen Zhijie u32 value; 973*1c5fd973SRen Zhijie 974*1c5fd973SRen Zhijie pci_read_config_dword(pdev, PCIE_GLI_9763E_VHS, &value); 975*1c5fd973SRen Zhijie value &= ~GLI_9763E_VHS_REV; 976*1c5fd973SRen Zhijie value |= FIELD_PREP(GLI_9763E_VHS_REV, GLI_9763E_VHS_REV_W); 977*1c5fd973SRen Zhijie pci_write_config_dword(pdev, PCIE_GLI_9763E_VHS, value); 978*1c5fd973SRen Zhijie 979*1c5fd973SRen Zhijie pci_read_config_dword(pdev, PCIE_GLI_9763E_CFG, &value); 980*1c5fd973SRen Zhijie 981*1c5fd973SRen Zhijie if (enable) 982*1c5fd973SRen Zhijie value &= ~GLI_9763E_CFG_LPSN_DIS; 983*1c5fd973SRen Zhijie else 984*1c5fd973SRen Zhijie value |= GLI_9763E_CFG_LPSN_DIS; 985*1c5fd973SRen Zhijie 986*1c5fd973SRen Zhijie pci_write_config_dword(pdev, PCIE_GLI_9763E_CFG, value); 987*1c5fd973SRen Zhijie 988*1c5fd973SRen Zhijie pci_read_config_dword(pdev, PCIE_GLI_9763E_VHS, &value); 989*1c5fd973SRen Zhijie value &= ~GLI_9763E_VHS_REV; 990*1c5fd973SRen Zhijie value |= FIELD_PREP(GLI_9763E_VHS_REV, GLI_9763E_VHS_REV_R); 991*1c5fd973SRen Zhijie pci_write_config_dword(pdev, PCIE_GLI_9763E_VHS, value); 992*1c5fd973SRen Zhijie } 993*1c5fd973SRen Zhijie 994d607667bSBen Chuang static int gl9763e_runtime_suspend(struct sdhci_pci_chip *chip) 995d607667bSBen Chuang { 996d607667bSBen Chuang struct sdhci_pci_slot *slot = chip->slots[0]; 997d607667bSBen Chuang struct sdhci_host *host = slot->host; 998d607667bSBen Chuang u16 clock; 999d607667bSBen Chuang 1000f9e5b339SJason Lai /* Enable LPM negotiation to allow entering L1 state */ 1001f9e5b339SJason Lai gl9763e_set_low_power_negotiation(slot, true); 1002f9e5b339SJason Lai 1003d607667bSBen Chuang clock = sdhci_readw(host, SDHCI_CLOCK_CONTROL); 1004d607667bSBen Chuang clock &= ~(SDHCI_CLOCK_PLL_EN | SDHCI_CLOCK_CARD_EN); 1005d607667bSBen Chuang sdhci_writew(host, clock, SDHCI_CLOCK_CONTROL); 1006d607667bSBen Chuang 1007d607667bSBen Chuang return 0; 1008d607667bSBen Chuang } 1009d607667bSBen Chuang 1010d607667bSBen Chuang static int gl9763e_runtime_resume(struct sdhci_pci_chip *chip) 1011d607667bSBen Chuang { 1012d607667bSBen Chuang struct sdhci_pci_slot *slot = chip->slots[0]; 1013d607667bSBen Chuang struct sdhci_host *host = slot->host; 1014d607667bSBen Chuang u16 clock; 1015d607667bSBen Chuang 1016291e7d52SBen Chuang if (host->mmc->ios.power_mode != MMC_POWER_ON) 1017291e7d52SBen Chuang return 0; 1018291e7d52SBen Chuang 1019d607667bSBen Chuang clock = sdhci_readw(host, SDHCI_CLOCK_CONTROL); 1020d607667bSBen Chuang 1021d607667bSBen Chuang clock |= SDHCI_CLOCK_PLL_EN; 1022d607667bSBen Chuang clock &= ~SDHCI_CLOCK_INT_STABLE; 1023d607667bSBen Chuang sdhci_writew(host, clock, SDHCI_CLOCK_CONTROL); 1024d607667bSBen Chuang 1025d607667bSBen Chuang /* Wait max 150 ms */ 1026d607667bSBen Chuang if (read_poll_timeout(sdhci_readw, clock, (clock & SDHCI_CLOCK_INT_STABLE), 1027d607667bSBen Chuang 1000, 150000, false, host, SDHCI_CLOCK_CONTROL)) { 1028d607667bSBen Chuang pr_err("%s: PLL clock never stabilised.\n", 1029d607667bSBen Chuang mmc_hostname(host->mmc)); 1030d607667bSBen Chuang sdhci_dumpregs(host); 1031d607667bSBen Chuang } 1032d607667bSBen Chuang 1033d607667bSBen Chuang clock |= SDHCI_CLOCK_CARD_EN; 1034d607667bSBen Chuang sdhci_writew(host, clock, SDHCI_CLOCK_CONTROL); 1035d607667bSBen Chuang 1036f9e5b339SJason Lai /* Disable LPM negotiation to avoid entering L1 state. */ 1037f9e5b339SJason Lai gl9763e_set_low_power_negotiation(slot, false); 1038f9e5b339SJason Lai 1039d607667bSBen Chuang return 0; 1040d607667bSBen Chuang } 1041d607667bSBen Chuang #endif 1042d607667bSBen Chuang 10431ae1d2d6SBen Chuang static int gli_probe_slot_gl9763e(struct sdhci_pci_slot *slot) 10441ae1d2d6SBen Chuang { 1045347f6be1SBen Chuang struct pci_dev *pdev = slot->chip->pdev; 10461ae1d2d6SBen Chuang struct sdhci_host *host = slot->host; 1047347f6be1SBen Chuang u32 value; 10481ae1d2d6SBen Chuang 10491ae1d2d6SBen Chuang host->mmc->caps |= MMC_CAP_8_BIT_DATA | 10501ae1d2d6SBen Chuang MMC_CAP_1_8V_DDR | 10511ae1d2d6SBen Chuang MMC_CAP_NONREMOVABLE; 10521ae1d2d6SBen Chuang host->mmc->caps2 |= MMC_CAP2_HS200_1_8V_SDR | 10531ae1d2d6SBen Chuang MMC_CAP2_HS400_1_8V | 10541ae1d2d6SBen Chuang MMC_CAP2_HS400_ES | 10551ae1d2d6SBen Chuang MMC_CAP2_NO_SDIO | 10561ae1d2d6SBen Chuang MMC_CAP2_NO_SD; 1057347f6be1SBen Chuang 1058347f6be1SBen Chuang pci_read_config_dword(pdev, PCIE_GLI_9763E_MB, &value); 1059347f6be1SBen Chuang if (!(value & GLI_9763E_MB_CMDQ_OFF)) 106015f908faSRenius Chen if (value & GLI_9763E_MB_ERP_ON) 1061347f6be1SBen Chuang host->mmc->caps2 |= MMC_CAP2_CQE | MMC_CAP2_CQE_DCMD; 1062347f6be1SBen Chuang 10631ae1d2d6SBen Chuang gli_pcie_enable_msi(slot); 10641ae1d2d6SBen Chuang host->mmc_host_ops.hs400_enhanced_strobe = 10651ae1d2d6SBen Chuang gl9763e_hs400_enhanced_strobe; 10661ae1d2d6SBen Chuang gli_set_gl9763e(slot); 10671ae1d2d6SBen Chuang sdhci_enable_v4_mode(host); 10681ae1d2d6SBen Chuang 10691ae1d2d6SBen Chuang return 0; 10701ae1d2d6SBen Chuang } 10711ae1d2d6SBen Chuang 1072c064bb5cSHector Martin #define REG_OFFSET_IN_BITS(reg) ((reg) << 3 & 0x18) 1073c064bb5cSHector Martin 1074c064bb5cSHector Martin static u16 sdhci_gli_readw(struct sdhci_host *host, int reg) 1075c064bb5cSHector Martin { 1076c064bb5cSHector Martin u32 val = readl(host->ioaddr + (reg & ~3)); 1077c064bb5cSHector Martin u16 word; 1078c064bb5cSHector Martin 1079c064bb5cSHector Martin word = (val >> REG_OFFSET_IN_BITS(reg)) & 0xffff; 1080c064bb5cSHector Martin return word; 1081c064bb5cSHector Martin } 1082c064bb5cSHector Martin 1083c064bb5cSHector Martin static u8 sdhci_gli_readb(struct sdhci_host *host, int reg) 1084c064bb5cSHector Martin { 1085c064bb5cSHector Martin u32 val = readl(host->ioaddr + (reg & ~3)); 1086c064bb5cSHector Martin u8 byte = (val >> REG_OFFSET_IN_BITS(reg)) & 0xff; 1087c064bb5cSHector Martin 1088c064bb5cSHector Martin return byte; 1089c064bb5cSHector Martin } 1090c064bb5cSHector Martin 1091e51df6ceSBen Chuang static const struct sdhci_ops sdhci_gl9755_ops = { 1092c064bb5cSHector Martin .read_w = sdhci_gli_readw, 1093c064bb5cSHector Martin .read_b = sdhci_gli_readb, 1094786d33c8SBen Chuang .set_clock = sdhci_gl9755_set_clock, 1095e51df6ceSBen Chuang .enable_dma = sdhci_pci_enable_dma, 1096e51df6ceSBen Chuang .set_bus_width = sdhci_set_bus_width, 1097e51df6ceSBen Chuang .reset = sdhci_reset, 1098e51df6ceSBen Chuang .set_uhs_signaling = sdhci_set_uhs_signaling, 1099e51df6ceSBen Chuang .voltage_switch = sdhci_gli_voltage_switch, 1100e51df6ceSBen Chuang }; 1101e51df6ceSBen Chuang 1102e51df6ceSBen Chuang const struct sdhci_pci_fixes sdhci_gl9755 = { 1103e51df6ceSBen Chuang .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, 1104e51df6ceSBen Chuang .quirks2 = SDHCI_QUIRK2_BROKEN_DDR50, 1105e51df6ceSBen Chuang .probe_slot = gli_probe_slot_gl9755, 1106e51df6ceSBen Chuang .ops = &sdhci_gl9755_ops, 1107282ede76SBen Chuang #ifdef CONFIG_PM_SLEEP 1108282ede76SBen Chuang .resume = sdhci_pci_gli_resume, 1109282ede76SBen Chuang #endif 1110e51df6ceSBen Chuang }; 1111e51df6ceSBen Chuang 1112e51df6ceSBen Chuang static const struct sdhci_ops sdhci_gl9750_ops = { 1113c064bb5cSHector Martin .read_w = sdhci_gli_readw, 1114c064bb5cSHector Martin .read_b = sdhci_gli_readb, 1115e51df6ceSBen Chuang .read_l = sdhci_gl9750_readl, 1116786d33c8SBen Chuang .set_clock = sdhci_gl9750_set_clock, 1117e51df6ceSBen Chuang .enable_dma = sdhci_pci_enable_dma, 1118e51df6ceSBen Chuang .set_bus_width = sdhci_set_bus_width, 1119e51df6ceSBen Chuang .reset = sdhci_gl9750_reset, 1120e51df6ceSBen Chuang .set_uhs_signaling = sdhci_set_uhs_signaling, 1121e51df6ceSBen Chuang .voltage_switch = sdhci_gli_voltage_switch, 1122e51df6ceSBen Chuang .platform_execute_tuning = gl9750_execute_tuning, 1123e51df6ceSBen Chuang }; 1124e51df6ceSBen Chuang 1125e51df6ceSBen Chuang const struct sdhci_pci_fixes sdhci_gl9750 = { 1126e51df6ceSBen Chuang .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, 1127e51df6ceSBen Chuang .quirks2 = SDHCI_QUIRK2_BROKEN_DDR50, 1128e51df6ceSBen Chuang .probe_slot = gli_probe_slot_gl9750, 1129e51df6ceSBen Chuang .ops = &sdhci_gl9750_ops, 1130282ede76SBen Chuang #ifdef CONFIG_PM_SLEEP 1131282ede76SBen Chuang .resume = sdhci_pci_gli_resume, 1132282ede76SBen Chuang #endif 1133e51df6ceSBen Chuang }; 11341ae1d2d6SBen Chuang 11351ae1d2d6SBen Chuang static const struct sdhci_ops sdhci_gl9763e_ops = { 11361ae1d2d6SBen Chuang .set_clock = sdhci_set_clock, 11371ae1d2d6SBen Chuang .enable_dma = sdhci_pci_enable_dma, 11381ae1d2d6SBen Chuang .set_bus_width = sdhci_set_bus_width, 1139347f6be1SBen Chuang .reset = sdhci_gl9763e_reset, 11401ae1d2d6SBen Chuang .set_uhs_signaling = sdhci_set_gl9763e_signaling, 11411ae1d2d6SBen Chuang .voltage_switch = sdhci_gli_voltage_switch, 1142347f6be1SBen Chuang .irq = sdhci_gl9763e_cqhci_irq, 11431ae1d2d6SBen Chuang }; 11441ae1d2d6SBen Chuang 11451ae1d2d6SBen Chuang const struct sdhci_pci_fixes sdhci_gl9763e = { 11461ae1d2d6SBen Chuang .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, 11471ae1d2d6SBen Chuang .probe_slot = gli_probe_slot_gl9763e, 11481ae1d2d6SBen Chuang .ops = &sdhci_gl9763e_ops, 11491ae1d2d6SBen Chuang #ifdef CONFIG_PM_SLEEP 1150347f6be1SBen Chuang .resume = sdhci_cqhci_gli_resume, 1151347f6be1SBen Chuang .suspend = sdhci_cqhci_gli_suspend, 11521ae1d2d6SBen Chuang #endif 1153d607667bSBen Chuang #ifdef CONFIG_PM 1154d607667bSBen Chuang .runtime_suspend = gl9763e_runtime_suspend, 1155d607667bSBen Chuang .runtime_resume = gl9763e_runtime_resume, 1156d607667bSBen Chuang .allow_runtime_pm = true, 1157d607667bSBen Chuang #endif 1158347f6be1SBen Chuang .add_host = gl9763e_add_host, 11591ae1d2d6SBen Chuang }; 1160