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