.. | .. |
---|
39 | 39 | #define DWCMSHC_EMMC_DLL_STRBIN 0x80c |
---|
40 | 40 | #define DECMSHC_EMMC_DLL_CMDOUT 0x810 |
---|
41 | 41 | #define DWCMSHC_EMMC_DLL_STATUS0 0x840 |
---|
| 42 | +#define DWCMSHC_EMMC_DLL_STATUS1 0x844 |
---|
42 | 43 | |
---|
43 | 44 | #define DWCMSHC_EMMC_DLL_START BIT(0) |
---|
44 | 45 | #define DWCMSHC_EMMC_DLL_LOCKED BIT(8) |
---|
.. | .. |
---|
48 | 49 | #define DWCMSHC_EMMC_DLL_BYPASS BIT(24) |
---|
49 | 50 | #define DWCMSHC_EMMC_DLL_DLYENA BIT(27) |
---|
50 | 51 | |
---|
51 | | -#define DLL_TXCLK_TAPNUM_DEFAULT 0x10 |
---|
52 | | -#define DLL_TXCLK_TAPNUM_90_DEGREES 0x9 |
---|
| 52 | +#define DLL_TAP_VALUE_SEL BIT(25) |
---|
| 53 | +#define DLL_TAP_VALUE_OFFSET 8 |
---|
| 54 | + |
---|
53 | 55 | #define DLL_TXCLK_TAPNUM_FROM_SW BIT(24) |
---|
54 | 56 | #define DLL_TXCLK_NO_INVERTER BIT(29) |
---|
55 | 57 | |
---|
56 | | -#define DLL_STRBIN_TAPNUM_DEFAULT 0x4 |
---|
57 | 58 | #define DLL_STRBIN_TAPNUM_FROM_SW BIT(24) |
---|
58 | 59 | #define DLL_STRBIN_DELAY_NUM_SEL BIT(26) |
---|
59 | 60 | #define DLL_STRBIN_DELAY_NUM_OFFSET 16 |
---|
60 | | -#define DLL_STRBIN_DELAY_NUM_DEFAULT 0x10 |
---|
61 | 61 | |
---|
62 | 62 | #define DLL_RXCLK_NO_INVERTER BIT(29) |
---|
| 63 | +#define DLL_RXCLK_ORI_GATE BIT(31) |
---|
63 | 64 | |
---|
64 | 65 | #define DWCMSHC_CARD_IS_EMMC BIT(0) |
---|
65 | 66 | #define DWCMSHC_ENHANCED_STROBE BIT(8) |
---|
66 | 67 | |
---|
67 | | -#define DLL_CMDOUT_TAPNUM_90_DEGREES 0x8 |
---|
68 | 68 | #define DLL_CMDOUT_TAPNUM_FROM_SW BIT(24) |
---|
69 | 69 | #define DLL_CMDOUT_SRC_CLK_NEG BIT(28) |
---|
70 | 70 | #define DLL_CMDOUT_EN_SRC_CLK_NEG BIT(29) |
---|
| 71 | +#define DLL_CMDOUT_BOTH_CLK_EDGE BIT(30) |
---|
71 | 72 | |
---|
72 | 73 | #define DLL_LOCK_WO_TMOUT(x) \ |
---|
73 | 74 | ((((x) & DWCMSHC_EMMC_DLL_LOCKED) == DWCMSHC_EMMC_DLL_LOCKED) && \ |
---|
.. | .. |
---|
77 | 78 | #define BOUNDARY_OK(addr, len) \ |
---|
78 | 79 | ((addr | (SZ_128M - 1)) == ((addr + len - 1) | (SZ_128M - 1))) |
---|
79 | 80 | |
---|
| 81 | +struct dwcmshc_driver_data { |
---|
| 82 | + const struct sdhci_pltfm_data *pdata; |
---|
| 83 | + u32 flags; |
---|
| 84 | +#define RK_PLATFROM BIT(0) |
---|
| 85 | +#define RK_DLL_CMD_OUT BIT(1) |
---|
| 86 | +#define RK_RXCLK_NO_INVERTER BIT(2) |
---|
| 87 | +#define RK_TAP_VALUE_SEL BIT(3) |
---|
| 88 | + |
---|
| 89 | + u8 hs200_tx_tap; |
---|
| 90 | + u8 hs400_tx_tap; |
---|
| 91 | + u8 hs400_cmd_tap; |
---|
| 92 | + u8 ddr50_strbin_delay_num; |
---|
| 93 | + u8 hs400_strbin_tap; |
---|
| 94 | +}; |
---|
| 95 | + |
---|
80 | 96 | struct dwcmshc_priv { |
---|
81 | 97 | struct clk *bus_clk; |
---|
82 | 98 | u32 cclk_rate; |
---|
.. | .. |
---|
84 | 100 | /* Rockchip specified optional clocks */ |
---|
85 | 101 | struct clk_bulk_data rockchip_clks[ROCKCHIP_MAX_CLKS]; |
---|
86 | 102 | struct reset_control *reset; |
---|
87 | | - int txclk_tapnum; |
---|
88 | 103 | unsigned int actual_clk; |
---|
89 | | - u32 flags; |
---|
90 | | -}; |
---|
91 | | - |
---|
92 | | -struct dwcmshc_driver_data { |
---|
93 | | - const struct sdhci_pltfm_data *pdata; |
---|
94 | | - u32 flags; |
---|
95 | | -#define RK_PLATFROM BIT(0) |
---|
96 | | -#define RK_DLL_CMD_OUT BIT(1) |
---|
97 | | -#define RK_RXCLK_NO_INVERTER BIT(2) |
---|
| 104 | + const struct dwcmshc_driver_data *drv_data; |
---|
98 | 105 | }; |
---|
99 | 106 | |
---|
100 | 107 | /* |
---|
.. | .. |
---|
195 | 202 | { |
---|
196 | 203 | struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); |
---|
197 | 204 | struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host); |
---|
198 | | - u32 txclk_tapnum, extra; |
---|
| 205 | + const struct dwcmshc_driver_data *drv_data = priv->drv_data; |
---|
| 206 | + u32 txclk_tapnum, extra, dll_lock_value; |
---|
199 | 207 | int err; |
---|
200 | 208 | |
---|
201 | 209 | host->mmc->actual_clock = 0; |
---|
.. | .. |
---|
221 | 229 | extra &= ~BIT(0); |
---|
222 | 230 | sdhci_writel(host, extra, DWCMSHC_HOST_CTRL3); |
---|
223 | 231 | |
---|
| 232 | + /* Disable output clock while config DLL */ |
---|
| 233 | + sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); |
---|
| 234 | + |
---|
224 | 235 | if (clock <= 52000000) { |
---|
| 236 | + /* Disable DLL */ |
---|
| 237 | + sdhci_writel(host, 0, DWCMSHC_EMMC_DLL_CTRL); |
---|
225 | 238 | /* |
---|
226 | | - * Disable DLL and reset both of sample and drive clock. |
---|
| 239 | + * Config DLL BYPASS and Reset both of sample and drive clock. |
---|
227 | 240 | * The bypass bit and start bit need to set if DLL is not locked. |
---|
228 | 241 | */ |
---|
229 | 242 | sdhci_writel(host, DWCMSHC_EMMC_DLL_BYPASS | DWCMSHC_EMMC_DLL_START, DWCMSHC_EMMC_DLL_CTRL); |
---|
230 | | - sdhci_writel(host, 0, DWCMSHC_EMMC_DLL_RXCLK); |
---|
| 243 | + sdhci_writel(host, DLL_RXCLK_ORI_GATE, DWCMSHC_EMMC_DLL_RXCLK); |
---|
231 | 244 | sdhci_writel(host, 0, DWCMSHC_EMMC_DLL_TXCLK); |
---|
232 | 245 | sdhci_writel(host, 0, DECMSHC_EMMC_DLL_CMDOUT); |
---|
233 | 246 | /* |
---|
.. | .. |
---|
237 | 250 | */ |
---|
238 | 251 | extra = DWCMSHC_EMMC_DLL_DLYENA | |
---|
239 | 252 | DLL_STRBIN_DELAY_NUM_SEL | |
---|
240 | | - DLL_STRBIN_DELAY_NUM_DEFAULT << DLL_STRBIN_DELAY_NUM_OFFSET; |
---|
| 253 | + drv_data->ddr50_strbin_delay_num << DLL_STRBIN_DELAY_NUM_OFFSET; |
---|
241 | 254 | sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_STRBIN); |
---|
242 | | - return; |
---|
| 255 | + goto exit; |
---|
243 | 256 | } |
---|
244 | 257 | |
---|
245 | 258 | /* Reset DLL */ |
---|
246 | 259 | sdhci_writel(host, BIT(1), DWCMSHC_EMMC_DLL_CTRL); |
---|
247 | 260 | udelay(1); |
---|
248 | 261 | sdhci_writel(host, 0x0, DWCMSHC_EMMC_DLL_CTRL); |
---|
249 | | - |
---|
250 | | - /* |
---|
251 | | - * We shouldn't set DLL_RXCLK_NO_INVERTER for identify mode but |
---|
252 | | - * we must set it in higher speed mode. |
---|
253 | | - */ |
---|
254 | | - extra = DWCMSHC_EMMC_DLL_DLYENA; |
---|
255 | | - if (priv->flags & RK_RXCLK_NO_INVERTER) |
---|
256 | | - extra |= DLL_RXCLK_NO_INVERTER; |
---|
257 | | - sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_RXCLK); |
---|
258 | 262 | |
---|
259 | 263 | /* Init DLL settings, clean start bit before resetting */ |
---|
260 | 264 | sdhci_writel(host, 0, DWCMSHC_EMMC_DLL_CTRL); |
---|
.. | .. |
---|
267 | 271 | 500 * USEC_PER_MSEC); |
---|
268 | 272 | if (err) { |
---|
269 | 273 | dev_err(mmc_dev(host->mmc), "DLL lock timeout!\n"); |
---|
270 | | - return; |
---|
| 274 | + goto exit; |
---|
271 | 275 | } |
---|
272 | 276 | |
---|
| 277 | + dll_lock_value = ((sdhci_readl(host, DWCMSHC_EMMC_DLL_STATUS0) & 0xFF) * 2) & 0xFF; |
---|
| 278 | + |
---|
273 | 279 | extra = 0x1 << 16 | /* tune clock stop en */ |
---|
274 | | - 0x2 << 17 | /* pre-change delay */ |
---|
| 280 | + 0x3 << 17 | /* pre-change delay */ |
---|
275 | 281 | 0x3 << 19; /* post-change delay */ |
---|
276 | 282 | sdhci_writel(host, extra, DWCMSHC_EMMC_ATCTRL); |
---|
277 | 283 | |
---|
278 | | - txclk_tapnum = priv->txclk_tapnum; |
---|
| 284 | + extra = DWCMSHC_EMMC_DLL_DLYENA | DLL_RXCLK_ORI_GATE; |
---|
| 285 | + if (drv_data->flags & RK_RXCLK_NO_INVERTER) |
---|
| 286 | + extra |= DLL_RXCLK_NO_INVERTER; |
---|
| 287 | + if (drv_data->flags & RK_TAP_VALUE_SEL) |
---|
| 288 | + extra |= DLL_TAP_VALUE_SEL | dll_lock_value << DLL_TAP_VALUE_OFFSET; |
---|
| 289 | + sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_RXCLK); |
---|
279 | 290 | |
---|
280 | | - if ((priv->flags & RK_DLL_CMD_OUT) && |
---|
281 | | - host->mmc->ios.timing == MMC_TIMING_MMC_HS400) { |
---|
282 | | - txclk_tapnum = DLL_TXCLK_TAPNUM_90_DEGREES; |
---|
| 291 | + txclk_tapnum = drv_data->hs200_tx_tap; |
---|
| 292 | + if (host->mmc->ios.timing == MMC_TIMING_MMC_HS400) { |
---|
| 293 | + txclk_tapnum = drv_data->hs400_tx_tap; |
---|
283 | 294 | |
---|
284 | | - extra = DLL_CMDOUT_SRC_CLK_NEG | |
---|
285 | | - DLL_CMDOUT_EN_SRC_CLK_NEG | |
---|
286 | | - DWCMSHC_EMMC_DLL_DLYENA | |
---|
287 | | - DLL_CMDOUT_TAPNUM_90_DEGREES | |
---|
288 | | - DLL_CMDOUT_TAPNUM_FROM_SW; |
---|
289 | | - sdhci_writel(host, extra, DECMSHC_EMMC_DLL_CMDOUT); |
---|
| 295 | + if (drv_data->flags & RK_DLL_CMD_OUT) { |
---|
| 296 | + extra = DLL_CMDOUT_SRC_CLK_NEG | |
---|
| 297 | + DLL_CMDOUT_BOTH_CLK_EDGE | |
---|
| 298 | + DWCMSHC_EMMC_DLL_DLYENA | |
---|
| 299 | + drv_data->hs400_cmd_tap | |
---|
| 300 | + DLL_CMDOUT_TAPNUM_FROM_SW; |
---|
| 301 | + if (drv_data->flags & RK_TAP_VALUE_SEL) |
---|
| 302 | + extra |= DLL_TAP_VALUE_SEL | dll_lock_value << DLL_TAP_VALUE_OFFSET; |
---|
| 303 | + sdhci_writel(host, extra, DECMSHC_EMMC_DLL_CMDOUT); |
---|
| 304 | + } |
---|
290 | 305 | } |
---|
291 | | - |
---|
292 | 306 | extra = DWCMSHC_EMMC_DLL_DLYENA | |
---|
293 | 307 | DLL_TXCLK_TAPNUM_FROM_SW | |
---|
294 | 308 | DLL_RXCLK_NO_INVERTER | |
---|
295 | 309 | txclk_tapnum; |
---|
| 310 | + if (drv_data->flags & RK_TAP_VALUE_SEL) |
---|
| 311 | + extra |= DLL_TAP_VALUE_SEL | dll_lock_value << DLL_TAP_VALUE_OFFSET; |
---|
296 | 312 | sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_TXCLK); |
---|
297 | 313 | |
---|
298 | 314 | extra = DWCMSHC_EMMC_DLL_DLYENA | |
---|
299 | | - DLL_STRBIN_TAPNUM_DEFAULT | |
---|
| 315 | + drv_data->hs400_strbin_tap | |
---|
300 | 316 | DLL_STRBIN_TAPNUM_FROM_SW; |
---|
| 317 | + if (drv_data->flags & RK_TAP_VALUE_SEL) |
---|
| 318 | + extra |= DLL_TAP_VALUE_SEL | dll_lock_value << DLL_TAP_VALUE_OFFSET; |
---|
301 | 319 | sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_STRBIN); |
---|
| 320 | + |
---|
| 321 | +exit: |
---|
| 322 | + /* enable output clock */ |
---|
| 323 | + sdhci_enable_clk(host, 0); |
---|
302 | 324 | } |
---|
303 | 325 | |
---|
304 | 326 | static void rockchip_sdhci_reset(struct sdhci_host *host, u8 mask) |
---|
.. | .. |
---|
367 | 389 | static const struct dwcmshc_driver_data rk3568_drvdata = { |
---|
368 | 390 | .pdata = &sdhci_dwcmshc_rk_pdata, |
---|
369 | 391 | .flags = RK_PLATFROM | RK_RXCLK_NO_INVERTER, |
---|
| 392 | + .hs200_tx_tap = 16, |
---|
| 393 | + .hs400_tx_tap = 8, |
---|
| 394 | + .hs400_cmd_tap = 8, |
---|
| 395 | + .hs400_strbin_tap = 4, |
---|
| 396 | + .ddr50_strbin_delay_num = 16, |
---|
370 | 397 | }; |
---|
371 | 398 | |
---|
372 | 399 | static const struct dwcmshc_driver_data rk3588_drvdata = { |
---|
373 | 400 | .pdata = &sdhci_dwcmshc_rk_pdata, |
---|
374 | 401 | .flags = RK_PLATFROM | RK_DLL_CMD_OUT, |
---|
| 402 | + .hs200_tx_tap = 16, |
---|
| 403 | + .hs400_tx_tap = 9, |
---|
| 404 | + .hs400_cmd_tap = 8, |
---|
| 405 | + .hs400_strbin_tap = 4, |
---|
| 406 | + .ddr50_strbin_delay_num = 16, |
---|
| 407 | +}; |
---|
| 408 | + |
---|
| 409 | +static const struct dwcmshc_driver_data rk3528_drvdata = { |
---|
| 410 | + .pdata = &sdhci_dwcmshc_rk_pdata, |
---|
| 411 | + .flags = RK_PLATFROM | RK_DLL_CMD_OUT | RK_TAP_VALUE_SEL, |
---|
| 412 | + .hs200_tx_tap = 12, |
---|
| 413 | + .hs400_tx_tap = 6, |
---|
| 414 | + .hs400_cmd_tap = 6, |
---|
| 415 | + .hs400_strbin_tap = 3, |
---|
| 416 | + .ddr50_strbin_delay_num = 10, |
---|
375 | 417 | }; |
---|
376 | 418 | |
---|
377 | 419 | static int rockchip_pltf_init(struct sdhci_host *host, struct dwcmshc_priv *priv) |
---|
.. | .. |
---|
393 | 435 | dev_err(mmc_dev(host->mmc), "failed to enable clocks %d\n", err); |
---|
394 | 436 | return err; |
---|
395 | 437 | } |
---|
396 | | - |
---|
397 | | - if (of_property_read_u32(mmc_dev(host->mmc)->of_node, "rockchip,txclk-tapnum", |
---|
398 | | - &priv->txclk_tapnum)) |
---|
399 | | - priv->txclk_tapnum = DLL_TXCLK_TAPNUM_DEFAULT; |
---|
400 | 438 | |
---|
401 | 439 | /* Disable cmd conflict check */ |
---|
402 | 440 | sdhci_writel(host, 0x0, DWCMSHC_HOST_CTRL3); |
---|
.. | .. |
---|
424 | 462 | { |
---|
425 | 463 | .compatible = "rockchip,dwcmshc-sdhci", |
---|
426 | 464 | .data = &rk3568_drvdata, |
---|
| 465 | + }, |
---|
| 466 | + { |
---|
| 467 | + .compatible = "rockchip,rk3528-dwcmshc", |
---|
| 468 | + .data = &rk3528_drvdata, |
---|
427 | 469 | }, |
---|
428 | 470 | { |
---|
429 | 471 | .compatible = "rockchip,rk3588-dwcmshc", |
---|
.. | .. |
---|
463 | 505 | |
---|
464 | 506 | pltfm_host = sdhci_priv(host); |
---|
465 | 507 | priv = sdhci_pltfm_priv(pltfm_host); |
---|
466 | | - |
---|
| 508 | + priv->drv_data = drv_data; |
---|
467 | 509 | priv->reset = devm_reset_control_array_get_exclusive(&pdev->dev); |
---|
468 | 510 | pltfm_host->clk = devm_clk_get(&pdev->dev, "core"); |
---|
469 | 511 | if (IS_ERR(pltfm_host->clk)) { |
---|
.. | .. |
---|
502 | 544 | if (err) |
---|
503 | 545 | goto err_clk; |
---|
504 | 546 | |
---|
505 | | - priv->flags = drv_data->flags; |
---|
506 | 547 | if (drv_data->flags & RK_PLATFROM) { |
---|
507 | 548 | err = rockchip_pltf_init(host, priv); |
---|
508 | 549 | if (err) |
---|