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> 15e51df6ceSBen Chuang #include "sdhci.h" 16e51df6ceSBen Chuang #include "sdhci-pci.h" 17e51df6ceSBen Chuang 18e51df6ceSBen Chuang /* Genesys Logic extra registers */ 19e51df6ceSBen Chuang #define SDHCI_GLI_9750_WT 0x800 20e51df6ceSBen Chuang #define SDHCI_GLI_9750_WT_EN BIT(0) 21e51df6ceSBen Chuang #define GLI_9750_WT_EN_ON 0x1 22e51df6ceSBen Chuang #define GLI_9750_WT_EN_OFF 0x0 23e51df6ceSBen Chuang 24e51df6ceSBen Chuang #define SDHCI_GLI_9750_DRIVING 0x860 25e51df6ceSBen Chuang #define SDHCI_GLI_9750_DRIVING_1 GENMASK(11, 0) 26e51df6ceSBen Chuang #define SDHCI_GLI_9750_DRIVING_2 GENMASK(27, 26) 27e51df6ceSBen Chuang #define GLI_9750_DRIVING_1_VALUE 0xFFF 28e51df6ceSBen Chuang #define GLI_9750_DRIVING_2_VALUE 0x3 29b56ff195SBen Chuang #define SDHCI_GLI_9750_SEL_1 BIT(29) 30b56ff195SBen Chuang #define SDHCI_GLI_9750_SEL_2 BIT(31) 31b56ff195SBen Chuang #define SDHCI_GLI_9750_ALL_RST (BIT(24)|BIT(25)|BIT(28)|BIT(30)) 32e51df6ceSBen Chuang 33e51df6ceSBen Chuang #define SDHCI_GLI_9750_PLL 0x864 34e51df6ceSBen Chuang #define SDHCI_GLI_9750_PLL_TX2_INV BIT(23) 35e51df6ceSBen Chuang #define SDHCI_GLI_9750_PLL_TX2_DLY GENMASK(22, 20) 36e51df6ceSBen Chuang #define GLI_9750_PLL_TX2_INV_VALUE 0x1 37e51df6ceSBen Chuang #define GLI_9750_PLL_TX2_DLY_VALUE 0x0 38e51df6ceSBen Chuang 39e51df6ceSBen Chuang #define SDHCI_GLI_9750_SW_CTRL 0x874 40e51df6ceSBen Chuang #define SDHCI_GLI_9750_SW_CTRL_4 GENMASK(7, 6) 41e51df6ceSBen Chuang #define GLI_9750_SW_CTRL_4_VALUE 0x3 42e51df6ceSBen Chuang 43e51df6ceSBen Chuang #define SDHCI_GLI_9750_MISC 0x878 44e51df6ceSBen Chuang #define SDHCI_GLI_9750_MISC_TX1_INV BIT(2) 45e51df6ceSBen Chuang #define SDHCI_GLI_9750_MISC_RX_INV BIT(3) 46e51df6ceSBen Chuang #define SDHCI_GLI_9750_MISC_TX1_DLY GENMASK(6, 4) 47e51df6ceSBen Chuang #define GLI_9750_MISC_TX1_INV_VALUE 0x0 48e51df6ceSBen Chuang #define GLI_9750_MISC_RX_INV_ON 0x1 49e51df6ceSBen Chuang #define GLI_9750_MISC_RX_INV_OFF 0x0 50e51df6ceSBen Chuang #define GLI_9750_MISC_RX_INV_VALUE GLI_9750_MISC_RX_INV_OFF 51e51df6ceSBen Chuang #define GLI_9750_MISC_TX1_DLY_VALUE 0x5 52e51df6ceSBen Chuang 53e51df6ceSBen Chuang #define SDHCI_GLI_9750_TUNING_CONTROL 0x540 54e51df6ceSBen Chuang #define SDHCI_GLI_9750_TUNING_CONTROL_EN BIT(4) 55e51df6ceSBen Chuang #define GLI_9750_TUNING_CONTROL_EN_ON 0x1 56e51df6ceSBen Chuang #define GLI_9750_TUNING_CONTROL_EN_OFF 0x0 57e51df6ceSBen Chuang #define SDHCI_GLI_9750_TUNING_CONTROL_GLITCH_1 BIT(16) 58e51df6ceSBen Chuang #define SDHCI_GLI_9750_TUNING_CONTROL_GLITCH_2 GENMASK(20, 19) 59e51df6ceSBen Chuang #define GLI_9750_TUNING_CONTROL_GLITCH_1_VALUE 0x1 60e51df6ceSBen Chuang #define GLI_9750_TUNING_CONTROL_GLITCH_2_VALUE 0x2 61e51df6ceSBen Chuang 62e51df6ceSBen Chuang #define SDHCI_GLI_9750_TUNING_PARAMETERS 0x544 63e51df6ceSBen Chuang #define SDHCI_GLI_9750_TUNING_PARAMETERS_RX_DLY GENMASK(2, 0) 64e51df6ceSBen Chuang #define GLI_9750_TUNING_PARAMETERS_RX_DLY_VALUE 0x1 65e51df6ceSBen Chuang 661ae1d2d6SBen Chuang #define SDHCI_GLI_9763E_CTRL_HS400 0x7 671ae1d2d6SBen Chuang 681ae1d2d6SBen Chuang #define SDHCI_GLI_9763E_HS400_ES_REG 0x52C 691ae1d2d6SBen Chuang #define SDHCI_GLI_9763E_HS400_ES_BIT BIT(8) 701ae1d2d6SBen Chuang 711ae1d2d6SBen Chuang #define PCIE_GLI_9763E_VHS 0x884 721ae1d2d6SBen Chuang #define GLI_9763E_VHS_REV GENMASK(19, 16) 731ae1d2d6SBen Chuang #define GLI_9763E_VHS_REV_R 0x0 741ae1d2d6SBen Chuang #define GLI_9763E_VHS_REV_M 0x1 751ae1d2d6SBen Chuang #define GLI_9763E_VHS_REV_W 0x2 761ae1d2d6SBen Chuang #define PCIE_GLI_9763E_SCR 0x8E0 771ae1d2d6SBen Chuang #define GLI_9763E_SCR_AXI_REQ BIT(9) 781ae1d2d6SBen Chuang 79e51df6ceSBen Chuang #define GLI_MAX_TUNING_LOOP 40 80e51df6ceSBen Chuang 81e51df6ceSBen Chuang /* Genesys Logic chipset */ 82e51df6ceSBen Chuang static inline void gl9750_wt_on(struct sdhci_host *host) 83e51df6ceSBen Chuang { 84e51df6ceSBen Chuang u32 wt_value; 85e51df6ceSBen Chuang u32 wt_enable; 86e51df6ceSBen Chuang 87e51df6ceSBen Chuang wt_value = sdhci_readl(host, SDHCI_GLI_9750_WT); 88e51df6ceSBen Chuang wt_enable = FIELD_GET(SDHCI_GLI_9750_WT_EN, wt_value); 89e51df6ceSBen Chuang 90e51df6ceSBen Chuang if (wt_enable == GLI_9750_WT_EN_ON) 91e51df6ceSBen Chuang return; 92e51df6ceSBen Chuang 93e51df6ceSBen Chuang wt_value &= ~SDHCI_GLI_9750_WT_EN; 94e51df6ceSBen Chuang wt_value |= FIELD_PREP(SDHCI_GLI_9750_WT_EN, GLI_9750_WT_EN_ON); 95e51df6ceSBen Chuang 96e51df6ceSBen Chuang sdhci_writel(host, wt_value, SDHCI_GLI_9750_WT); 97e51df6ceSBen Chuang } 98e51df6ceSBen Chuang 99e51df6ceSBen Chuang static inline void gl9750_wt_off(struct sdhci_host *host) 100e51df6ceSBen Chuang { 101e51df6ceSBen Chuang u32 wt_value; 102e51df6ceSBen Chuang u32 wt_enable; 103e51df6ceSBen Chuang 104e51df6ceSBen Chuang wt_value = sdhci_readl(host, SDHCI_GLI_9750_WT); 105e51df6ceSBen Chuang wt_enable = FIELD_GET(SDHCI_GLI_9750_WT_EN, wt_value); 106e51df6ceSBen Chuang 107e51df6ceSBen Chuang if (wt_enable == GLI_9750_WT_EN_OFF) 108e51df6ceSBen Chuang return; 109e51df6ceSBen Chuang 110e51df6ceSBen Chuang wt_value &= ~SDHCI_GLI_9750_WT_EN; 111e51df6ceSBen Chuang wt_value |= FIELD_PREP(SDHCI_GLI_9750_WT_EN, GLI_9750_WT_EN_OFF); 112e51df6ceSBen Chuang 113e51df6ceSBen Chuang sdhci_writel(host, wt_value, SDHCI_GLI_9750_WT); 114e51df6ceSBen Chuang } 115e51df6ceSBen Chuang 116e51df6ceSBen Chuang static void gli_set_9750(struct sdhci_host *host) 117e51df6ceSBen Chuang { 118e51df6ceSBen Chuang u32 driving_value; 119e51df6ceSBen Chuang u32 pll_value; 120e51df6ceSBen Chuang u32 sw_ctrl_value; 121e51df6ceSBen Chuang u32 misc_value; 122e51df6ceSBen Chuang u32 parameter_value; 123e51df6ceSBen Chuang u32 control_value; 124e51df6ceSBen Chuang u16 ctrl2; 125e51df6ceSBen Chuang 126e51df6ceSBen Chuang gl9750_wt_on(host); 127e51df6ceSBen Chuang 128e51df6ceSBen Chuang driving_value = sdhci_readl(host, SDHCI_GLI_9750_DRIVING); 129e51df6ceSBen Chuang pll_value = sdhci_readl(host, SDHCI_GLI_9750_PLL); 130e51df6ceSBen Chuang sw_ctrl_value = sdhci_readl(host, SDHCI_GLI_9750_SW_CTRL); 131e51df6ceSBen Chuang misc_value = sdhci_readl(host, SDHCI_GLI_9750_MISC); 132e51df6ceSBen Chuang parameter_value = sdhci_readl(host, SDHCI_GLI_9750_TUNING_PARAMETERS); 133e51df6ceSBen Chuang control_value = sdhci_readl(host, SDHCI_GLI_9750_TUNING_CONTROL); 134e51df6ceSBen Chuang 135e51df6ceSBen Chuang driving_value &= ~(SDHCI_GLI_9750_DRIVING_1); 136e51df6ceSBen Chuang driving_value &= ~(SDHCI_GLI_9750_DRIVING_2); 137e51df6ceSBen Chuang driving_value |= FIELD_PREP(SDHCI_GLI_9750_DRIVING_1, 138e51df6ceSBen Chuang GLI_9750_DRIVING_1_VALUE); 139e51df6ceSBen Chuang driving_value |= FIELD_PREP(SDHCI_GLI_9750_DRIVING_2, 140e51df6ceSBen Chuang GLI_9750_DRIVING_2_VALUE); 141b56ff195SBen Chuang driving_value &= ~(SDHCI_GLI_9750_SEL_1|SDHCI_GLI_9750_SEL_2|SDHCI_GLI_9750_ALL_RST); 142b56ff195SBen Chuang driving_value |= SDHCI_GLI_9750_SEL_2; 143e51df6ceSBen Chuang sdhci_writel(host, driving_value, SDHCI_GLI_9750_DRIVING); 144e51df6ceSBen Chuang 145e51df6ceSBen Chuang sw_ctrl_value &= ~SDHCI_GLI_9750_SW_CTRL_4; 146e51df6ceSBen Chuang sw_ctrl_value |= FIELD_PREP(SDHCI_GLI_9750_SW_CTRL_4, 147e51df6ceSBen Chuang GLI_9750_SW_CTRL_4_VALUE); 148e51df6ceSBen Chuang sdhci_writel(host, sw_ctrl_value, SDHCI_GLI_9750_SW_CTRL); 149e51df6ceSBen Chuang 150e51df6ceSBen Chuang /* reset the tuning flow after reinit and before starting tuning */ 151e51df6ceSBen Chuang pll_value &= ~SDHCI_GLI_9750_PLL_TX2_INV; 152e51df6ceSBen Chuang pll_value &= ~SDHCI_GLI_9750_PLL_TX2_DLY; 153e51df6ceSBen Chuang pll_value |= FIELD_PREP(SDHCI_GLI_9750_PLL_TX2_INV, 154e51df6ceSBen Chuang GLI_9750_PLL_TX2_INV_VALUE); 155e51df6ceSBen Chuang pll_value |= FIELD_PREP(SDHCI_GLI_9750_PLL_TX2_DLY, 156e51df6ceSBen Chuang GLI_9750_PLL_TX2_DLY_VALUE); 157e51df6ceSBen Chuang 158e51df6ceSBen Chuang misc_value &= ~SDHCI_GLI_9750_MISC_TX1_INV; 159e51df6ceSBen Chuang misc_value &= ~SDHCI_GLI_9750_MISC_RX_INV; 160e51df6ceSBen Chuang misc_value &= ~SDHCI_GLI_9750_MISC_TX1_DLY; 161e51df6ceSBen Chuang misc_value |= FIELD_PREP(SDHCI_GLI_9750_MISC_TX1_INV, 162e51df6ceSBen Chuang GLI_9750_MISC_TX1_INV_VALUE); 163e51df6ceSBen Chuang misc_value |= FIELD_PREP(SDHCI_GLI_9750_MISC_RX_INV, 164e51df6ceSBen Chuang GLI_9750_MISC_RX_INV_VALUE); 165e51df6ceSBen Chuang misc_value |= FIELD_PREP(SDHCI_GLI_9750_MISC_TX1_DLY, 166e51df6ceSBen Chuang GLI_9750_MISC_TX1_DLY_VALUE); 167e51df6ceSBen Chuang 168e51df6ceSBen Chuang parameter_value &= ~SDHCI_GLI_9750_TUNING_PARAMETERS_RX_DLY; 169e51df6ceSBen Chuang parameter_value |= FIELD_PREP(SDHCI_GLI_9750_TUNING_PARAMETERS_RX_DLY, 170e51df6ceSBen Chuang GLI_9750_TUNING_PARAMETERS_RX_DLY_VALUE); 171e51df6ceSBen Chuang 172e51df6ceSBen Chuang control_value &= ~SDHCI_GLI_9750_TUNING_CONTROL_GLITCH_1; 173e51df6ceSBen Chuang control_value &= ~SDHCI_GLI_9750_TUNING_CONTROL_GLITCH_2; 174e51df6ceSBen Chuang control_value |= FIELD_PREP(SDHCI_GLI_9750_TUNING_CONTROL_GLITCH_1, 175e51df6ceSBen Chuang GLI_9750_TUNING_CONTROL_GLITCH_1_VALUE); 176e51df6ceSBen Chuang control_value |= FIELD_PREP(SDHCI_GLI_9750_TUNING_CONTROL_GLITCH_2, 177e51df6ceSBen Chuang GLI_9750_TUNING_CONTROL_GLITCH_2_VALUE); 178e51df6ceSBen Chuang 179e51df6ceSBen Chuang sdhci_writel(host, pll_value, SDHCI_GLI_9750_PLL); 180e51df6ceSBen Chuang sdhci_writel(host, misc_value, SDHCI_GLI_9750_MISC); 181e51df6ceSBen Chuang 182e51df6ceSBen Chuang /* disable tuned clk */ 183e51df6ceSBen Chuang ctrl2 = sdhci_readw(host, SDHCI_HOST_CONTROL2); 184e51df6ceSBen Chuang ctrl2 &= ~SDHCI_CTRL_TUNED_CLK; 185e51df6ceSBen Chuang sdhci_writew(host, ctrl2, SDHCI_HOST_CONTROL2); 186e51df6ceSBen Chuang 187e51df6ceSBen Chuang /* enable tuning parameters control */ 188e51df6ceSBen Chuang control_value &= ~SDHCI_GLI_9750_TUNING_CONTROL_EN; 189e51df6ceSBen Chuang control_value |= FIELD_PREP(SDHCI_GLI_9750_TUNING_CONTROL_EN, 190e51df6ceSBen Chuang GLI_9750_TUNING_CONTROL_EN_ON); 191e51df6ceSBen Chuang sdhci_writel(host, control_value, SDHCI_GLI_9750_TUNING_CONTROL); 192e51df6ceSBen Chuang 193e51df6ceSBen Chuang /* write tuning parameters */ 194e51df6ceSBen Chuang sdhci_writel(host, parameter_value, SDHCI_GLI_9750_TUNING_PARAMETERS); 195e51df6ceSBen Chuang 196e51df6ceSBen Chuang /* disable tuning parameters control */ 197e51df6ceSBen Chuang control_value &= ~SDHCI_GLI_9750_TUNING_CONTROL_EN; 198e51df6ceSBen Chuang control_value |= FIELD_PREP(SDHCI_GLI_9750_TUNING_CONTROL_EN, 199e51df6ceSBen Chuang GLI_9750_TUNING_CONTROL_EN_OFF); 200e51df6ceSBen Chuang sdhci_writel(host, control_value, SDHCI_GLI_9750_TUNING_CONTROL); 201e51df6ceSBen Chuang 202e51df6ceSBen Chuang /* clear tuned clk */ 203e51df6ceSBen Chuang ctrl2 = sdhci_readw(host, SDHCI_HOST_CONTROL2); 204e51df6ceSBen Chuang ctrl2 &= ~SDHCI_CTRL_TUNED_CLK; 205e51df6ceSBen Chuang sdhci_writew(host, ctrl2, SDHCI_HOST_CONTROL2); 206e51df6ceSBen Chuang 207e51df6ceSBen Chuang gl9750_wt_off(host); 208e51df6ceSBen Chuang } 209e51df6ceSBen Chuang 210e51df6ceSBen Chuang static void gli_set_9750_rx_inv(struct sdhci_host *host, bool b) 211e51df6ceSBen Chuang { 212e51df6ceSBen Chuang u32 misc_value; 213e51df6ceSBen Chuang 214e51df6ceSBen Chuang gl9750_wt_on(host); 215e51df6ceSBen Chuang 216e51df6ceSBen Chuang misc_value = sdhci_readl(host, SDHCI_GLI_9750_MISC); 217e51df6ceSBen Chuang misc_value &= ~SDHCI_GLI_9750_MISC_RX_INV; 218e51df6ceSBen Chuang if (b) { 219e51df6ceSBen Chuang misc_value |= FIELD_PREP(SDHCI_GLI_9750_MISC_RX_INV, 220e51df6ceSBen Chuang GLI_9750_MISC_RX_INV_ON); 221e51df6ceSBen Chuang } else { 222e51df6ceSBen Chuang misc_value |= FIELD_PREP(SDHCI_GLI_9750_MISC_RX_INV, 223e51df6ceSBen Chuang GLI_9750_MISC_RX_INV_OFF); 224e51df6ceSBen Chuang } 225e51df6ceSBen Chuang sdhci_writel(host, misc_value, SDHCI_GLI_9750_MISC); 226e51df6ceSBen Chuang 227e51df6ceSBen Chuang gl9750_wt_off(host); 228e51df6ceSBen Chuang } 229e51df6ceSBen Chuang 230e51df6ceSBen Chuang static int __sdhci_execute_tuning_9750(struct sdhci_host *host, u32 opcode) 231e51df6ceSBen Chuang { 232e51df6ceSBen Chuang int i; 233e51df6ceSBen Chuang int rx_inv; 234e51df6ceSBen Chuang 235e51df6ceSBen Chuang for (rx_inv = 0; rx_inv < 2; rx_inv++) { 236e51df6ceSBen Chuang gli_set_9750_rx_inv(host, !!rx_inv); 237e51df6ceSBen Chuang sdhci_start_tuning(host); 238e51df6ceSBen Chuang 239e51df6ceSBen Chuang for (i = 0; i < GLI_MAX_TUNING_LOOP; i++) { 240e51df6ceSBen Chuang u16 ctrl; 241e51df6ceSBen Chuang 242e51df6ceSBen Chuang sdhci_send_tuning(host, opcode); 243e51df6ceSBen Chuang 244e51df6ceSBen Chuang if (!host->tuning_done) { 245e51df6ceSBen Chuang sdhci_abort_tuning(host, opcode); 246e51df6ceSBen Chuang break; 247e51df6ceSBen Chuang } 248e51df6ceSBen Chuang 249e51df6ceSBen Chuang ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); 250e51df6ceSBen Chuang if (!(ctrl & SDHCI_CTRL_EXEC_TUNING)) { 251e51df6ceSBen Chuang if (ctrl & SDHCI_CTRL_TUNED_CLK) 252e51df6ceSBen Chuang return 0; /* Success! */ 253e51df6ceSBen Chuang break; 254e51df6ceSBen Chuang } 255e51df6ceSBen Chuang } 256e51df6ceSBen Chuang } 257e51df6ceSBen Chuang if (!host->tuning_done) { 258e51df6ceSBen Chuang pr_info("%s: Tuning timeout, falling back to fixed sampling clock\n", 259e51df6ceSBen Chuang mmc_hostname(host->mmc)); 260e51df6ceSBen Chuang return -ETIMEDOUT; 261e51df6ceSBen Chuang } 262e51df6ceSBen Chuang 263e51df6ceSBen Chuang pr_info("%s: Tuning failed, falling back to fixed sampling clock\n", 264e51df6ceSBen Chuang mmc_hostname(host->mmc)); 265e51df6ceSBen Chuang sdhci_reset_tuning(host); 266e51df6ceSBen Chuang 267e51df6ceSBen Chuang return -EAGAIN; 268e51df6ceSBen Chuang } 269e51df6ceSBen Chuang 270e51df6ceSBen Chuang static int gl9750_execute_tuning(struct sdhci_host *host, u32 opcode) 271e51df6ceSBen Chuang { 272e51df6ceSBen Chuang host->mmc->retune_period = 0; 273e51df6ceSBen Chuang if (host->tuning_mode == SDHCI_TUNING_MODE_1) 274e51df6ceSBen Chuang host->mmc->retune_period = host->tuning_count; 275e51df6ceSBen Chuang 276e51df6ceSBen Chuang gli_set_9750(host); 277e51df6ceSBen Chuang host->tuning_err = __sdhci_execute_tuning_9750(host, opcode); 278e51df6ceSBen Chuang sdhci_end_tuning(host); 279e51df6ceSBen Chuang 280e51df6ceSBen Chuang return 0; 281e51df6ceSBen Chuang } 282e51df6ceSBen Chuang 28331e43f31SBen Chuang static void gli_pcie_enable_msi(struct sdhci_pci_slot *slot) 28431e43f31SBen Chuang { 28531e43f31SBen Chuang int ret; 28631e43f31SBen Chuang 28731e43f31SBen Chuang ret = pci_alloc_irq_vectors(slot->chip->pdev, 1, 1, 28831e43f31SBen Chuang PCI_IRQ_MSI | PCI_IRQ_MSIX); 28931e43f31SBen Chuang if (ret < 0) { 29031e43f31SBen Chuang pr_warn("%s: enable PCI MSI failed, error=%d\n", 29131e43f31SBen Chuang mmc_hostname(slot->host->mmc), ret); 29231e43f31SBen Chuang return; 29331e43f31SBen Chuang } 29431e43f31SBen Chuang 29531e43f31SBen Chuang slot->host->irq = pci_irq_vector(slot->chip->pdev, 0); 29631e43f31SBen Chuang } 29731e43f31SBen Chuang 298e51df6ceSBen Chuang static int gli_probe_slot_gl9750(struct sdhci_pci_slot *slot) 299e51df6ceSBen Chuang { 300e51df6ceSBen Chuang struct sdhci_host *host = slot->host; 301e51df6ceSBen Chuang 30231e43f31SBen Chuang gli_pcie_enable_msi(slot); 303e51df6ceSBen Chuang slot->host->mmc->caps2 |= MMC_CAP2_NO_SDIO; 304e51df6ceSBen Chuang sdhci_enable_v4_mode(host); 305e51df6ceSBen Chuang 306e51df6ceSBen Chuang return 0; 307e51df6ceSBen Chuang } 308e51df6ceSBen Chuang 309e51df6ceSBen Chuang static int gli_probe_slot_gl9755(struct sdhci_pci_slot *slot) 310e51df6ceSBen Chuang { 311e51df6ceSBen Chuang struct sdhci_host *host = slot->host; 312e51df6ceSBen Chuang 31331e43f31SBen Chuang gli_pcie_enable_msi(slot); 314e51df6ceSBen Chuang slot->host->mmc->caps2 |= MMC_CAP2_NO_SDIO; 315e51df6ceSBen Chuang sdhci_enable_v4_mode(host); 316e51df6ceSBen Chuang 317e51df6ceSBen Chuang return 0; 318e51df6ceSBen Chuang } 319e51df6ceSBen Chuang 320e51df6ceSBen Chuang static void sdhci_gli_voltage_switch(struct sdhci_host *host) 321e51df6ceSBen Chuang { 322e51df6ceSBen Chuang /* 323e51df6ceSBen Chuang * According to Section 3.6.1 signal voltage switch procedure in 324e51df6ceSBen Chuang * SD Host Controller Simplified Spec. 4.20, steps 6~8 are as 325e51df6ceSBen Chuang * follows: 326e51df6ceSBen Chuang * (6) Set 1.8V Signal Enable in the Host Control 2 register. 327e51df6ceSBen Chuang * (7) Wait 5ms. 1.8V voltage regulator shall be stable within this 328e51df6ceSBen Chuang * period. 329e51df6ceSBen Chuang * (8) If 1.8V Signal Enable is cleared by Host Controller, go to 330e51df6ceSBen Chuang * step (12). 331e51df6ceSBen Chuang * 332e51df6ceSBen Chuang * Wait 5ms after set 1.8V signal enable in Host Control 2 register 333e51df6ceSBen Chuang * to ensure 1.8V signal enable bit is set by GL9750/GL9755. 334e51df6ceSBen Chuang */ 335e51df6ceSBen Chuang usleep_range(5000, 5500); 336e51df6ceSBen Chuang } 337e51df6ceSBen Chuang 338e51df6ceSBen Chuang static void sdhci_gl9750_reset(struct sdhci_host *host, u8 mask) 339e51df6ceSBen Chuang { 340e51df6ceSBen Chuang sdhci_reset(host, mask); 341e51df6ceSBen Chuang gli_set_9750(host); 342e51df6ceSBen Chuang } 343e51df6ceSBen Chuang 344e51df6ceSBen Chuang static u32 sdhci_gl9750_readl(struct sdhci_host *host, int reg) 345e51df6ceSBen Chuang { 346e51df6ceSBen Chuang u32 value; 347e51df6ceSBen Chuang 348e51df6ceSBen Chuang value = readl(host->ioaddr + reg); 349e51df6ceSBen Chuang if (unlikely(reg == SDHCI_MAX_CURRENT && !(value & 0xff))) 350e51df6ceSBen Chuang value |= 0xc8; 351e51df6ceSBen Chuang 352e51df6ceSBen Chuang return value; 353e51df6ceSBen Chuang } 354e51df6ceSBen Chuang 355282ede76SBen Chuang #ifdef CONFIG_PM_SLEEP 356282ede76SBen Chuang static int sdhci_pci_gli_resume(struct sdhci_pci_chip *chip) 357282ede76SBen Chuang { 358282ede76SBen Chuang struct sdhci_pci_slot *slot = chip->slots[0]; 359282ede76SBen Chuang 360282ede76SBen Chuang pci_free_irq_vectors(slot->chip->pdev); 361282ede76SBen Chuang gli_pcie_enable_msi(slot); 362282ede76SBen Chuang 363282ede76SBen Chuang return sdhci_pci_resume_host(chip); 364282ede76SBen Chuang } 365282ede76SBen Chuang #endif 366282ede76SBen Chuang 3671ae1d2d6SBen Chuang static void gl9763e_hs400_enhanced_strobe(struct mmc_host *mmc, 3681ae1d2d6SBen Chuang struct mmc_ios *ios) 3691ae1d2d6SBen Chuang { 3701ae1d2d6SBen Chuang struct sdhci_host *host = mmc_priv(mmc); 3711ae1d2d6SBen Chuang u32 val; 3721ae1d2d6SBen Chuang 3731ae1d2d6SBen Chuang val = sdhci_readl(host, SDHCI_GLI_9763E_HS400_ES_REG); 3741ae1d2d6SBen Chuang if (ios->enhanced_strobe) 3751ae1d2d6SBen Chuang val |= SDHCI_GLI_9763E_HS400_ES_BIT; 3761ae1d2d6SBen Chuang else 3771ae1d2d6SBen Chuang val &= ~SDHCI_GLI_9763E_HS400_ES_BIT; 3781ae1d2d6SBen Chuang 3791ae1d2d6SBen Chuang sdhci_writel(host, val, SDHCI_GLI_9763E_HS400_ES_REG); 3801ae1d2d6SBen Chuang } 3811ae1d2d6SBen Chuang 3821ae1d2d6SBen Chuang static void sdhci_set_gl9763e_signaling(struct sdhci_host *host, 3831ae1d2d6SBen Chuang unsigned int timing) 3841ae1d2d6SBen Chuang { 3851ae1d2d6SBen Chuang u16 ctrl_2; 3861ae1d2d6SBen Chuang 3871ae1d2d6SBen Chuang ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2); 3881ae1d2d6SBen Chuang ctrl_2 &= ~SDHCI_CTRL_UHS_MASK; 3891ae1d2d6SBen Chuang if (timing == MMC_TIMING_MMC_HS200) 3901ae1d2d6SBen Chuang ctrl_2 |= SDHCI_CTRL_UHS_SDR104; 3911ae1d2d6SBen Chuang else if (timing == MMC_TIMING_MMC_HS) 3921ae1d2d6SBen Chuang ctrl_2 |= SDHCI_CTRL_UHS_SDR25; 3931ae1d2d6SBen Chuang else if (timing == MMC_TIMING_MMC_DDR52) 3941ae1d2d6SBen Chuang ctrl_2 |= SDHCI_CTRL_UHS_DDR50; 3951ae1d2d6SBen Chuang else if (timing == MMC_TIMING_MMC_HS400) 3961ae1d2d6SBen Chuang ctrl_2 |= SDHCI_GLI_9763E_CTRL_HS400; 3971ae1d2d6SBen Chuang 3981ae1d2d6SBen Chuang sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2); 3991ae1d2d6SBen Chuang } 4001ae1d2d6SBen Chuang 4011ae1d2d6SBen Chuang static void gli_set_gl9763e(struct sdhci_pci_slot *slot) 4021ae1d2d6SBen Chuang { 4031ae1d2d6SBen Chuang struct pci_dev *pdev = slot->chip->pdev; 4041ae1d2d6SBen Chuang u32 value; 4051ae1d2d6SBen Chuang 4061ae1d2d6SBen Chuang pci_read_config_dword(pdev, PCIE_GLI_9763E_VHS, &value); 4071ae1d2d6SBen Chuang value &= ~GLI_9763E_VHS_REV; 4081ae1d2d6SBen Chuang value |= FIELD_PREP(GLI_9763E_VHS_REV, GLI_9763E_VHS_REV_W); 4091ae1d2d6SBen Chuang pci_write_config_dword(pdev, PCIE_GLI_9763E_VHS, value); 4101ae1d2d6SBen Chuang 4111ae1d2d6SBen Chuang pci_read_config_dword(pdev, PCIE_GLI_9763E_SCR, &value); 4121ae1d2d6SBen Chuang value |= GLI_9763E_SCR_AXI_REQ; 4131ae1d2d6SBen Chuang pci_write_config_dword(pdev, PCIE_GLI_9763E_SCR, value); 4141ae1d2d6SBen Chuang 4151ae1d2d6SBen Chuang pci_read_config_dword(pdev, PCIE_GLI_9763E_VHS, &value); 4161ae1d2d6SBen Chuang value &= ~GLI_9763E_VHS_REV; 4171ae1d2d6SBen Chuang value |= FIELD_PREP(GLI_9763E_VHS_REV, GLI_9763E_VHS_REV_R); 4181ae1d2d6SBen Chuang pci_write_config_dword(pdev, PCIE_GLI_9763E_VHS, value); 4191ae1d2d6SBen Chuang } 4201ae1d2d6SBen Chuang 4211ae1d2d6SBen Chuang static int gli_probe_slot_gl9763e(struct sdhci_pci_slot *slot) 4221ae1d2d6SBen Chuang { 4231ae1d2d6SBen Chuang struct sdhci_host *host = slot->host; 4241ae1d2d6SBen Chuang 4251ae1d2d6SBen Chuang host->mmc->caps |= MMC_CAP_8_BIT_DATA | 4261ae1d2d6SBen Chuang MMC_CAP_1_8V_DDR | 4271ae1d2d6SBen Chuang MMC_CAP_NONREMOVABLE; 4281ae1d2d6SBen Chuang host->mmc->caps2 |= MMC_CAP2_HS200_1_8V_SDR | 4291ae1d2d6SBen Chuang MMC_CAP2_HS400_1_8V | 4301ae1d2d6SBen Chuang MMC_CAP2_HS400_ES | 4311ae1d2d6SBen Chuang MMC_CAP2_NO_SDIO | 4321ae1d2d6SBen Chuang MMC_CAP2_NO_SD; 4331ae1d2d6SBen Chuang gli_pcie_enable_msi(slot); 4341ae1d2d6SBen Chuang host->mmc_host_ops.hs400_enhanced_strobe = 4351ae1d2d6SBen Chuang gl9763e_hs400_enhanced_strobe; 4361ae1d2d6SBen Chuang gli_set_gl9763e(slot); 4371ae1d2d6SBen Chuang sdhci_enable_v4_mode(host); 4381ae1d2d6SBen Chuang 4391ae1d2d6SBen Chuang return 0; 4401ae1d2d6SBen Chuang } 4411ae1d2d6SBen Chuang 442e51df6ceSBen Chuang static const struct sdhci_ops sdhci_gl9755_ops = { 443e51df6ceSBen Chuang .set_clock = sdhci_set_clock, 444e51df6ceSBen Chuang .enable_dma = sdhci_pci_enable_dma, 445e51df6ceSBen Chuang .set_bus_width = sdhci_set_bus_width, 446e51df6ceSBen Chuang .reset = sdhci_reset, 447e51df6ceSBen Chuang .set_uhs_signaling = sdhci_set_uhs_signaling, 448e51df6ceSBen Chuang .voltage_switch = sdhci_gli_voltage_switch, 449e51df6ceSBen Chuang }; 450e51df6ceSBen Chuang 451e51df6ceSBen Chuang const struct sdhci_pci_fixes sdhci_gl9755 = { 452e51df6ceSBen Chuang .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, 453e51df6ceSBen Chuang .quirks2 = SDHCI_QUIRK2_BROKEN_DDR50, 454e51df6ceSBen Chuang .probe_slot = gli_probe_slot_gl9755, 455e51df6ceSBen Chuang .ops = &sdhci_gl9755_ops, 456282ede76SBen Chuang #ifdef CONFIG_PM_SLEEP 457282ede76SBen Chuang .resume = sdhci_pci_gli_resume, 458282ede76SBen Chuang #endif 459e51df6ceSBen Chuang }; 460e51df6ceSBen Chuang 461e51df6ceSBen Chuang static const struct sdhci_ops sdhci_gl9750_ops = { 462e51df6ceSBen Chuang .read_l = sdhci_gl9750_readl, 463e51df6ceSBen Chuang .set_clock = sdhci_set_clock, 464e51df6ceSBen Chuang .enable_dma = sdhci_pci_enable_dma, 465e51df6ceSBen Chuang .set_bus_width = sdhci_set_bus_width, 466e51df6ceSBen Chuang .reset = sdhci_gl9750_reset, 467e51df6ceSBen Chuang .set_uhs_signaling = sdhci_set_uhs_signaling, 468e51df6ceSBen Chuang .voltage_switch = sdhci_gli_voltage_switch, 469e51df6ceSBen Chuang .platform_execute_tuning = gl9750_execute_tuning, 470e51df6ceSBen Chuang }; 471e51df6ceSBen Chuang 472e51df6ceSBen Chuang const struct sdhci_pci_fixes sdhci_gl9750 = { 473e51df6ceSBen Chuang .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, 474e51df6ceSBen Chuang .quirks2 = SDHCI_QUIRK2_BROKEN_DDR50, 475e51df6ceSBen Chuang .probe_slot = gli_probe_slot_gl9750, 476e51df6ceSBen Chuang .ops = &sdhci_gl9750_ops, 477282ede76SBen Chuang #ifdef CONFIG_PM_SLEEP 478282ede76SBen Chuang .resume = sdhci_pci_gli_resume, 479282ede76SBen Chuang #endif 480e51df6ceSBen Chuang }; 4811ae1d2d6SBen Chuang 4821ae1d2d6SBen Chuang static const struct sdhci_ops sdhci_gl9763e_ops = { 4831ae1d2d6SBen Chuang .set_clock = sdhci_set_clock, 4841ae1d2d6SBen Chuang .enable_dma = sdhci_pci_enable_dma, 4851ae1d2d6SBen Chuang .set_bus_width = sdhci_set_bus_width, 4861ae1d2d6SBen Chuang .reset = sdhci_reset, 4871ae1d2d6SBen Chuang .set_uhs_signaling = sdhci_set_gl9763e_signaling, 4881ae1d2d6SBen Chuang .voltage_switch = sdhci_gli_voltage_switch, 4891ae1d2d6SBen Chuang }; 4901ae1d2d6SBen Chuang 4911ae1d2d6SBen Chuang const struct sdhci_pci_fixes sdhci_gl9763e = { 4921ae1d2d6SBen Chuang .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, 4931ae1d2d6SBen Chuang .probe_slot = gli_probe_slot_gl9763e, 4941ae1d2d6SBen Chuang .ops = &sdhci_gl9763e_ops, 4951ae1d2d6SBen Chuang #ifdef CONFIG_PM_SLEEP 4961ae1d2d6SBen Chuang .resume = sdhci_pci_gli_resume, 4971ae1d2d6SBen Chuang #endif 4981ae1d2d6SBen Chuang }; 499