Ben Chuang | e51df6c | 2019-09-11 15:23:44 +0800 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0+ |
| 2 | /* |
| 3 | * Copyright (C) 2019 Genesys Logic, Inc. |
| 4 | * |
| 5 | * Authors: Ben Chuang <ben.chuang@genesyslogic.com.tw> |
| 6 | * |
| 7 | * Version: v0.9.0 (2019-08-08) |
| 8 | */ |
| 9 | |
| 10 | #include <linux/bitfield.h> |
| 11 | #include <linux/bits.h> |
| 12 | #include <linux/pci.h> |
| 13 | #include <linux/mmc/mmc.h> |
| 14 | #include <linux/delay.h> |
| 15 | #include "sdhci.h" |
| 16 | #include "sdhci-pci.h" |
| 17 | |
| 18 | /* Genesys Logic extra registers */ |
| 19 | #define SDHCI_GLI_9750_WT 0x800 |
| 20 | #define SDHCI_GLI_9750_WT_EN BIT(0) |
| 21 | #define GLI_9750_WT_EN_ON 0x1 |
| 22 | #define GLI_9750_WT_EN_OFF 0x0 |
| 23 | |
| 24 | #define SDHCI_GLI_9750_DRIVING 0x860 |
| 25 | #define SDHCI_GLI_9750_DRIVING_1 GENMASK(11, 0) |
| 26 | #define SDHCI_GLI_9750_DRIVING_2 GENMASK(27, 26) |
| 27 | #define GLI_9750_DRIVING_1_VALUE 0xFFF |
| 28 | #define GLI_9750_DRIVING_2_VALUE 0x3 |
| 29 | |
| 30 | #define SDHCI_GLI_9750_PLL 0x864 |
| 31 | #define SDHCI_GLI_9750_PLL_TX2_INV BIT(23) |
| 32 | #define SDHCI_GLI_9750_PLL_TX2_DLY GENMASK(22, 20) |
| 33 | #define GLI_9750_PLL_TX2_INV_VALUE 0x1 |
| 34 | #define GLI_9750_PLL_TX2_DLY_VALUE 0x0 |
| 35 | |
| 36 | #define SDHCI_GLI_9750_SW_CTRL 0x874 |
| 37 | #define SDHCI_GLI_9750_SW_CTRL_4 GENMASK(7, 6) |
| 38 | #define GLI_9750_SW_CTRL_4_VALUE 0x3 |
| 39 | |
| 40 | #define SDHCI_GLI_9750_MISC 0x878 |
| 41 | #define SDHCI_GLI_9750_MISC_TX1_INV BIT(2) |
| 42 | #define SDHCI_GLI_9750_MISC_RX_INV BIT(3) |
| 43 | #define SDHCI_GLI_9750_MISC_TX1_DLY GENMASK(6, 4) |
| 44 | #define GLI_9750_MISC_TX1_INV_VALUE 0x0 |
| 45 | #define GLI_9750_MISC_RX_INV_ON 0x1 |
| 46 | #define GLI_9750_MISC_RX_INV_OFF 0x0 |
| 47 | #define GLI_9750_MISC_RX_INV_VALUE GLI_9750_MISC_RX_INV_OFF |
| 48 | #define GLI_9750_MISC_TX1_DLY_VALUE 0x5 |
| 49 | |
| 50 | #define SDHCI_GLI_9750_TUNING_CONTROL 0x540 |
| 51 | #define SDHCI_GLI_9750_TUNING_CONTROL_EN BIT(4) |
| 52 | #define GLI_9750_TUNING_CONTROL_EN_ON 0x1 |
| 53 | #define GLI_9750_TUNING_CONTROL_EN_OFF 0x0 |
| 54 | #define SDHCI_GLI_9750_TUNING_CONTROL_GLITCH_1 BIT(16) |
| 55 | #define SDHCI_GLI_9750_TUNING_CONTROL_GLITCH_2 GENMASK(20, 19) |
| 56 | #define GLI_9750_TUNING_CONTROL_GLITCH_1_VALUE 0x1 |
| 57 | #define GLI_9750_TUNING_CONTROL_GLITCH_2_VALUE 0x2 |
| 58 | |
| 59 | #define SDHCI_GLI_9750_TUNING_PARAMETERS 0x544 |
| 60 | #define SDHCI_GLI_9750_TUNING_PARAMETERS_RX_DLY GENMASK(2, 0) |
| 61 | #define GLI_9750_TUNING_PARAMETERS_RX_DLY_VALUE 0x1 |
| 62 | |
| 63 | #define GLI_MAX_TUNING_LOOP 40 |
| 64 | |
| 65 | /* Genesys Logic chipset */ |
| 66 | static inline void gl9750_wt_on(struct sdhci_host *host) |
| 67 | { |
| 68 | u32 wt_value; |
| 69 | u32 wt_enable; |
| 70 | |
| 71 | wt_value = sdhci_readl(host, SDHCI_GLI_9750_WT); |
| 72 | wt_enable = FIELD_GET(SDHCI_GLI_9750_WT_EN, wt_value); |
| 73 | |
| 74 | if (wt_enable == GLI_9750_WT_EN_ON) |
| 75 | return; |
| 76 | |
| 77 | wt_value &= ~SDHCI_GLI_9750_WT_EN; |
| 78 | wt_value |= FIELD_PREP(SDHCI_GLI_9750_WT_EN, GLI_9750_WT_EN_ON); |
| 79 | |
| 80 | sdhci_writel(host, wt_value, SDHCI_GLI_9750_WT); |
| 81 | } |
| 82 | |
| 83 | static inline void gl9750_wt_off(struct sdhci_host *host) |
| 84 | { |
| 85 | u32 wt_value; |
| 86 | u32 wt_enable; |
| 87 | |
| 88 | wt_value = sdhci_readl(host, SDHCI_GLI_9750_WT); |
| 89 | wt_enable = FIELD_GET(SDHCI_GLI_9750_WT_EN, wt_value); |
| 90 | |
| 91 | if (wt_enable == GLI_9750_WT_EN_OFF) |
| 92 | return; |
| 93 | |
| 94 | wt_value &= ~SDHCI_GLI_9750_WT_EN; |
| 95 | wt_value |= FIELD_PREP(SDHCI_GLI_9750_WT_EN, GLI_9750_WT_EN_OFF); |
| 96 | |
| 97 | sdhci_writel(host, wt_value, SDHCI_GLI_9750_WT); |
| 98 | } |
| 99 | |
| 100 | static void gli_set_9750(struct sdhci_host *host) |
| 101 | { |
| 102 | u32 driving_value; |
| 103 | u32 pll_value; |
| 104 | u32 sw_ctrl_value; |
| 105 | u32 misc_value; |
| 106 | u32 parameter_value; |
| 107 | u32 control_value; |
| 108 | u16 ctrl2; |
| 109 | |
| 110 | gl9750_wt_on(host); |
| 111 | |
| 112 | driving_value = sdhci_readl(host, SDHCI_GLI_9750_DRIVING); |
| 113 | pll_value = sdhci_readl(host, SDHCI_GLI_9750_PLL); |
| 114 | sw_ctrl_value = sdhci_readl(host, SDHCI_GLI_9750_SW_CTRL); |
| 115 | misc_value = sdhci_readl(host, SDHCI_GLI_9750_MISC); |
| 116 | parameter_value = sdhci_readl(host, SDHCI_GLI_9750_TUNING_PARAMETERS); |
| 117 | control_value = sdhci_readl(host, SDHCI_GLI_9750_TUNING_CONTROL); |
| 118 | |
| 119 | driving_value &= ~(SDHCI_GLI_9750_DRIVING_1); |
| 120 | driving_value &= ~(SDHCI_GLI_9750_DRIVING_2); |
| 121 | driving_value |= FIELD_PREP(SDHCI_GLI_9750_DRIVING_1, |
| 122 | GLI_9750_DRIVING_1_VALUE); |
| 123 | driving_value |= FIELD_PREP(SDHCI_GLI_9750_DRIVING_2, |
| 124 | GLI_9750_DRIVING_2_VALUE); |
| 125 | sdhci_writel(host, driving_value, SDHCI_GLI_9750_DRIVING); |
| 126 | |
| 127 | sw_ctrl_value &= ~SDHCI_GLI_9750_SW_CTRL_4; |
| 128 | sw_ctrl_value |= FIELD_PREP(SDHCI_GLI_9750_SW_CTRL_4, |
| 129 | GLI_9750_SW_CTRL_4_VALUE); |
| 130 | sdhci_writel(host, sw_ctrl_value, SDHCI_GLI_9750_SW_CTRL); |
| 131 | |
| 132 | /* reset the tuning flow after reinit and before starting tuning */ |
| 133 | pll_value &= ~SDHCI_GLI_9750_PLL_TX2_INV; |
| 134 | pll_value &= ~SDHCI_GLI_9750_PLL_TX2_DLY; |
| 135 | pll_value |= FIELD_PREP(SDHCI_GLI_9750_PLL_TX2_INV, |
| 136 | GLI_9750_PLL_TX2_INV_VALUE); |
| 137 | pll_value |= FIELD_PREP(SDHCI_GLI_9750_PLL_TX2_DLY, |
| 138 | GLI_9750_PLL_TX2_DLY_VALUE); |
| 139 | |
| 140 | misc_value &= ~SDHCI_GLI_9750_MISC_TX1_INV; |
| 141 | misc_value &= ~SDHCI_GLI_9750_MISC_RX_INV; |
| 142 | misc_value &= ~SDHCI_GLI_9750_MISC_TX1_DLY; |
| 143 | misc_value |= FIELD_PREP(SDHCI_GLI_9750_MISC_TX1_INV, |
| 144 | GLI_9750_MISC_TX1_INV_VALUE); |
| 145 | misc_value |= FIELD_PREP(SDHCI_GLI_9750_MISC_RX_INV, |
| 146 | GLI_9750_MISC_RX_INV_VALUE); |
| 147 | misc_value |= FIELD_PREP(SDHCI_GLI_9750_MISC_TX1_DLY, |
| 148 | GLI_9750_MISC_TX1_DLY_VALUE); |
| 149 | |
| 150 | parameter_value &= ~SDHCI_GLI_9750_TUNING_PARAMETERS_RX_DLY; |
| 151 | parameter_value |= FIELD_PREP(SDHCI_GLI_9750_TUNING_PARAMETERS_RX_DLY, |
| 152 | GLI_9750_TUNING_PARAMETERS_RX_DLY_VALUE); |
| 153 | |
| 154 | control_value &= ~SDHCI_GLI_9750_TUNING_CONTROL_GLITCH_1; |
| 155 | control_value &= ~SDHCI_GLI_9750_TUNING_CONTROL_GLITCH_2; |
| 156 | control_value |= FIELD_PREP(SDHCI_GLI_9750_TUNING_CONTROL_GLITCH_1, |
| 157 | GLI_9750_TUNING_CONTROL_GLITCH_1_VALUE); |
| 158 | control_value |= FIELD_PREP(SDHCI_GLI_9750_TUNING_CONTROL_GLITCH_2, |
| 159 | GLI_9750_TUNING_CONTROL_GLITCH_2_VALUE); |
| 160 | |
| 161 | sdhci_writel(host, pll_value, SDHCI_GLI_9750_PLL); |
| 162 | sdhci_writel(host, misc_value, SDHCI_GLI_9750_MISC); |
| 163 | |
| 164 | /* disable tuned clk */ |
| 165 | ctrl2 = sdhci_readw(host, SDHCI_HOST_CONTROL2); |
| 166 | ctrl2 &= ~SDHCI_CTRL_TUNED_CLK; |
| 167 | sdhci_writew(host, ctrl2, SDHCI_HOST_CONTROL2); |
| 168 | |
| 169 | /* enable tuning parameters control */ |
| 170 | control_value &= ~SDHCI_GLI_9750_TUNING_CONTROL_EN; |
| 171 | control_value |= FIELD_PREP(SDHCI_GLI_9750_TUNING_CONTROL_EN, |
| 172 | GLI_9750_TUNING_CONTROL_EN_ON); |
| 173 | sdhci_writel(host, control_value, SDHCI_GLI_9750_TUNING_CONTROL); |
| 174 | |
| 175 | /* write tuning parameters */ |
| 176 | sdhci_writel(host, parameter_value, SDHCI_GLI_9750_TUNING_PARAMETERS); |
| 177 | |
| 178 | /* disable tuning parameters control */ |
| 179 | control_value &= ~SDHCI_GLI_9750_TUNING_CONTROL_EN; |
| 180 | control_value |= FIELD_PREP(SDHCI_GLI_9750_TUNING_CONTROL_EN, |
| 181 | GLI_9750_TUNING_CONTROL_EN_OFF); |
| 182 | sdhci_writel(host, control_value, SDHCI_GLI_9750_TUNING_CONTROL); |
| 183 | |
| 184 | /* clear tuned clk */ |
| 185 | ctrl2 = sdhci_readw(host, SDHCI_HOST_CONTROL2); |
| 186 | ctrl2 &= ~SDHCI_CTRL_TUNED_CLK; |
| 187 | sdhci_writew(host, ctrl2, SDHCI_HOST_CONTROL2); |
| 188 | |
| 189 | gl9750_wt_off(host); |
| 190 | } |
| 191 | |
| 192 | static void gli_set_9750_rx_inv(struct sdhci_host *host, bool b) |
| 193 | { |
| 194 | u32 misc_value; |
| 195 | |
| 196 | gl9750_wt_on(host); |
| 197 | |
| 198 | misc_value = sdhci_readl(host, SDHCI_GLI_9750_MISC); |
| 199 | misc_value &= ~SDHCI_GLI_9750_MISC_RX_INV; |
| 200 | if (b) { |
| 201 | misc_value |= FIELD_PREP(SDHCI_GLI_9750_MISC_RX_INV, |
| 202 | GLI_9750_MISC_RX_INV_ON); |
| 203 | } else { |
| 204 | misc_value |= FIELD_PREP(SDHCI_GLI_9750_MISC_RX_INV, |
| 205 | GLI_9750_MISC_RX_INV_OFF); |
| 206 | } |
| 207 | sdhci_writel(host, misc_value, SDHCI_GLI_9750_MISC); |
| 208 | |
| 209 | gl9750_wt_off(host); |
| 210 | } |
| 211 | |
| 212 | static int __sdhci_execute_tuning_9750(struct sdhci_host *host, u32 opcode) |
| 213 | { |
| 214 | int i; |
| 215 | int rx_inv; |
| 216 | |
| 217 | for (rx_inv = 0; rx_inv < 2; rx_inv++) { |
| 218 | gli_set_9750_rx_inv(host, !!rx_inv); |
| 219 | sdhci_start_tuning(host); |
| 220 | |
| 221 | for (i = 0; i < GLI_MAX_TUNING_LOOP; i++) { |
| 222 | u16 ctrl; |
| 223 | |
| 224 | sdhci_send_tuning(host, opcode); |
| 225 | |
| 226 | if (!host->tuning_done) { |
| 227 | sdhci_abort_tuning(host, opcode); |
| 228 | break; |
| 229 | } |
| 230 | |
| 231 | ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); |
| 232 | if (!(ctrl & SDHCI_CTRL_EXEC_TUNING)) { |
| 233 | if (ctrl & SDHCI_CTRL_TUNED_CLK) |
| 234 | return 0; /* Success! */ |
| 235 | break; |
| 236 | } |
| 237 | } |
| 238 | } |
| 239 | if (!host->tuning_done) { |
| 240 | pr_info("%s: Tuning timeout, falling back to fixed sampling clock\n", |
| 241 | mmc_hostname(host->mmc)); |
| 242 | return -ETIMEDOUT; |
| 243 | } |
| 244 | |
| 245 | pr_info("%s: Tuning failed, falling back to fixed sampling clock\n", |
| 246 | mmc_hostname(host->mmc)); |
| 247 | sdhci_reset_tuning(host); |
| 248 | |
| 249 | return -EAGAIN; |
| 250 | } |
| 251 | |
| 252 | static int gl9750_execute_tuning(struct sdhci_host *host, u32 opcode) |
| 253 | { |
| 254 | host->mmc->retune_period = 0; |
| 255 | if (host->tuning_mode == SDHCI_TUNING_MODE_1) |
| 256 | host->mmc->retune_period = host->tuning_count; |
| 257 | |
| 258 | gli_set_9750(host); |
| 259 | host->tuning_err = __sdhci_execute_tuning_9750(host, opcode); |
| 260 | sdhci_end_tuning(host); |
| 261 | |
| 262 | return 0; |
| 263 | } |
| 264 | |
Ben Chuang | 31e43f3 | 2020-02-19 17:29:00 +0800 | [diff] [blame^] | 265 | static void gli_pcie_enable_msi(struct sdhci_pci_slot *slot) |
| 266 | { |
| 267 | int ret; |
| 268 | |
| 269 | ret = pci_alloc_irq_vectors(slot->chip->pdev, 1, 1, |
| 270 | PCI_IRQ_MSI | PCI_IRQ_MSIX); |
| 271 | if (ret < 0) { |
| 272 | pr_warn("%s: enable PCI MSI failed, error=%d\n", |
| 273 | mmc_hostname(slot->host->mmc), ret); |
| 274 | return; |
| 275 | } |
| 276 | |
| 277 | slot->host->irq = pci_irq_vector(slot->chip->pdev, 0); |
| 278 | } |
| 279 | |
Ben Chuang | e51df6c | 2019-09-11 15:23:44 +0800 | [diff] [blame] | 280 | static int gli_probe_slot_gl9750(struct sdhci_pci_slot *slot) |
| 281 | { |
| 282 | struct sdhci_host *host = slot->host; |
| 283 | |
Ben Chuang | 31e43f3 | 2020-02-19 17:29:00 +0800 | [diff] [blame^] | 284 | gli_pcie_enable_msi(slot); |
Ben Chuang | e51df6c | 2019-09-11 15:23:44 +0800 | [diff] [blame] | 285 | slot->host->mmc->caps2 |= MMC_CAP2_NO_SDIO; |
| 286 | sdhci_enable_v4_mode(host); |
| 287 | |
| 288 | return 0; |
| 289 | } |
| 290 | |
| 291 | static int gli_probe_slot_gl9755(struct sdhci_pci_slot *slot) |
| 292 | { |
| 293 | struct sdhci_host *host = slot->host; |
| 294 | |
Ben Chuang | 31e43f3 | 2020-02-19 17:29:00 +0800 | [diff] [blame^] | 295 | gli_pcie_enable_msi(slot); |
Ben Chuang | e51df6c | 2019-09-11 15:23:44 +0800 | [diff] [blame] | 296 | slot->host->mmc->caps2 |= MMC_CAP2_NO_SDIO; |
| 297 | sdhci_enable_v4_mode(host); |
| 298 | |
| 299 | return 0; |
| 300 | } |
| 301 | |
| 302 | static void sdhci_gli_voltage_switch(struct sdhci_host *host) |
| 303 | { |
| 304 | /* |
| 305 | * According to Section 3.6.1 signal voltage switch procedure in |
| 306 | * SD Host Controller Simplified Spec. 4.20, steps 6~8 are as |
| 307 | * follows: |
| 308 | * (6) Set 1.8V Signal Enable in the Host Control 2 register. |
| 309 | * (7) Wait 5ms. 1.8V voltage regulator shall be stable within this |
| 310 | * period. |
| 311 | * (8) If 1.8V Signal Enable is cleared by Host Controller, go to |
| 312 | * step (12). |
| 313 | * |
| 314 | * Wait 5ms after set 1.8V signal enable in Host Control 2 register |
| 315 | * to ensure 1.8V signal enable bit is set by GL9750/GL9755. |
| 316 | */ |
| 317 | usleep_range(5000, 5500); |
| 318 | } |
| 319 | |
| 320 | static void sdhci_gl9750_reset(struct sdhci_host *host, u8 mask) |
| 321 | { |
| 322 | sdhci_reset(host, mask); |
| 323 | gli_set_9750(host); |
| 324 | } |
| 325 | |
| 326 | static u32 sdhci_gl9750_readl(struct sdhci_host *host, int reg) |
| 327 | { |
| 328 | u32 value; |
| 329 | |
| 330 | value = readl(host->ioaddr + reg); |
| 331 | if (unlikely(reg == SDHCI_MAX_CURRENT && !(value & 0xff))) |
| 332 | value |= 0xc8; |
| 333 | |
| 334 | return value; |
| 335 | } |
| 336 | |
| 337 | static const struct sdhci_ops sdhci_gl9755_ops = { |
| 338 | .set_clock = sdhci_set_clock, |
| 339 | .enable_dma = sdhci_pci_enable_dma, |
| 340 | .set_bus_width = sdhci_set_bus_width, |
| 341 | .reset = sdhci_reset, |
| 342 | .set_uhs_signaling = sdhci_set_uhs_signaling, |
| 343 | .voltage_switch = sdhci_gli_voltage_switch, |
| 344 | }; |
| 345 | |
| 346 | const struct sdhci_pci_fixes sdhci_gl9755 = { |
| 347 | .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, |
| 348 | .quirks2 = SDHCI_QUIRK2_BROKEN_DDR50, |
| 349 | .probe_slot = gli_probe_slot_gl9755, |
| 350 | .ops = &sdhci_gl9755_ops, |
| 351 | }; |
| 352 | |
| 353 | static const struct sdhci_ops sdhci_gl9750_ops = { |
| 354 | .read_l = sdhci_gl9750_readl, |
| 355 | .set_clock = sdhci_set_clock, |
| 356 | .enable_dma = sdhci_pci_enable_dma, |
| 357 | .set_bus_width = sdhci_set_bus_width, |
| 358 | .reset = sdhci_gl9750_reset, |
| 359 | .set_uhs_signaling = sdhci_set_uhs_signaling, |
| 360 | .voltage_switch = sdhci_gli_voltage_switch, |
| 361 | .platform_execute_tuning = gl9750_execute_tuning, |
| 362 | }; |
| 363 | |
| 364 | const struct sdhci_pci_fixes sdhci_gl9750 = { |
| 365 | .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, |
| 366 | .quirks2 = SDHCI_QUIRK2_BROKEN_DDR50, |
| 367 | .probe_slot = gli_probe_slot_gl9750, |
| 368 | .ops = &sdhci_gl9750_ops, |
| 369 | }; |