GNU Linux-libre 6.9.1-gnu
[releases.git] / drivers / mmc / host / dw_mmc-exynos.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Exynos Specific Extensions for Synopsys DW Multimedia Card Interface driver
4  *
5  * Copyright (C) 2012, Samsung Electronics Co., Ltd.
6  */
7
8 #include <linux/module.h>
9 #include <linux/platform_device.h>
10 #include <linux/clk.h>
11 #include <linux/mmc/host.h>
12 #include <linux/mmc/mmc.h>
13 #include <linux/of.h>
14 #include <linux/pm_runtime.h>
15 #include <linux/slab.h>
16
17 #include "dw_mmc.h"
18 #include "dw_mmc-pltfm.h"
19 #include "dw_mmc-exynos.h"
20
21 /* Variations in Exynos specific dw-mshc controller */
22 enum dw_mci_exynos_type {
23         DW_MCI_TYPE_EXYNOS4210,
24         DW_MCI_TYPE_EXYNOS4412,
25         DW_MCI_TYPE_EXYNOS5250,
26         DW_MCI_TYPE_EXYNOS5420,
27         DW_MCI_TYPE_EXYNOS5420_SMU,
28         DW_MCI_TYPE_EXYNOS7,
29         DW_MCI_TYPE_EXYNOS7_SMU,
30         DW_MCI_TYPE_ARTPEC8,
31 };
32
33 /* Exynos implementation specific driver private data */
34 struct dw_mci_exynos_priv_data {
35         enum dw_mci_exynos_type         ctrl_type;
36         u8                              ciu_div;
37         u32                             sdr_timing;
38         u32                             ddr_timing;
39         u32                             hs400_timing;
40         u32                             tuned_sample;
41         u32                             cur_speed;
42         u32                             dqs_delay;
43         u32                             saved_dqs_en;
44         u32                             saved_strobe_ctrl;
45 };
46
47 static struct dw_mci_exynos_compatible {
48         char                            *compatible;
49         enum dw_mci_exynos_type         ctrl_type;
50 } exynos_compat[] = {
51         {
52                 .compatible     = "samsung,exynos4210-dw-mshc",
53                 .ctrl_type      = DW_MCI_TYPE_EXYNOS4210,
54         }, {
55                 .compatible     = "samsung,exynos4412-dw-mshc",
56                 .ctrl_type      = DW_MCI_TYPE_EXYNOS4412,
57         }, {
58                 .compatible     = "samsung,exynos5250-dw-mshc",
59                 .ctrl_type      = DW_MCI_TYPE_EXYNOS5250,
60         }, {
61                 .compatible     = "samsung,exynos5420-dw-mshc",
62                 .ctrl_type      = DW_MCI_TYPE_EXYNOS5420,
63         }, {
64                 .compatible     = "samsung,exynos5420-dw-mshc-smu",
65                 .ctrl_type      = DW_MCI_TYPE_EXYNOS5420_SMU,
66         }, {
67                 .compatible     = "samsung,exynos7-dw-mshc",
68                 .ctrl_type      = DW_MCI_TYPE_EXYNOS7,
69         }, {
70                 .compatible     = "samsung,exynos7-dw-mshc-smu",
71                 .ctrl_type      = DW_MCI_TYPE_EXYNOS7_SMU,
72         }, {
73                 .compatible     = "axis,artpec8-dw-mshc",
74                 .ctrl_type      = DW_MCI_TYPE_ARTPEC8,
75         },
76 };
77
78 static inline u8 dw_mci_exynos_get_ciu_div(struct dw_mci *host)
79 {
80         struct dw_mci_exynos_priv_data *priv = host->priv;
81
82         if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4412)
83                 return EXYNOS4412_FIXED_CIU_CLK_DIV;
84         else if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4210)
85                 return EXYNOS4210_FIXED_CIU_CLK_DIV;
86         else if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
87                         priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU ||
88                         priv->ctrl_type == DW_MCI_TYPE_ARTPEC8)
89                 return SDMMC_CLKSEL_GET_DIV(mci_readl(host, CLKSEL64)) + 1;
90         else
91                 return SDMMC_CLKSEL_GET_DIV(mci_readl(host, CLKSEL)) + 1;
92 }
93
94 static void dw_mci_exynos_config_smu(struct dw_mci *host)
95 {
96         struct dw_mci_exynos_priv_data *priv = host->priv;
97
98         /*
99          * If Exynos is provided the Security management,
100          * set for non-ecryption mode at this time.
101          */
102         if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS5420_SMU ||
103                 priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU) {
104                 mci_writel(host, MPSBEGIN0, 0);
105                 mci_writel(host, MPSEND0, SDMMC_ENDING_SEC_NR_MAX);
106                 mci_writel(host, MPSCTRL0, SDMMC_MPSCTRL_SECURE_WRITE_BIT |
107                            SDMMC_MPSCTRL_NON_SECURE_READ_BIT |
108                            SDMMC_MPSCTRL_VALID |
109                            SDMMC_MPSCTRL_NON_SECURE_WRITE_BIT);
110         }
111 }
112
113 static int dw_mci_exynos_priv_init(struct dw_mci *host)
114 {
115         struct dw_mci_exynos_priv_data *priv = host->priv;
116
117         dw_mci_exynos_config_smu(host);
118
119         if (priv->ctrl_type >= DW_MCI_TYPE_EXYNOS5420) {
120                 priv->saved_strobe_ctrl = mci_readl(host, HS400_DLINE_CTRL);
121                 priv->saved_dqs_en = mci_readl(host, HS400_DQS_EN);
122                 priv->saved_dqs_en |= AXI_NON_BLOCKING_WR;
123                 mci_writel(host, HS400_DQS_EN, priv->saved_dqs_en);
124                 if (!priv->dqs_delay)
125                         priv->dqs_delay =
126                                 DQS_CTRL_GET_RD_DELAY(priv->saved_strobe_ctrl);
127         }
128
129         if (priv->ctrl_type == DW_MCI_TYPE_ARTPEC8) {
130                 /* Quirk needed for the ARTPEC-8 SoC */
131                 host->quirks |= DW_MMC_QUIRK_EXTENDED_TMOUT;
132         }
133
134         host->bus_hz /= (priv->ciu_div + 1);
135
136         return 0;
137 }
138
139 static void dw_mci_exynos_set_clksel_timing(struct dw_mci *host, u32 timing)
140 {
141         struct dw_mci_exynos_priv_data *priv = host->priv;
142         u32 clksel;
143
144         if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
145                 priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU ||
146                 priv->ctrl_type == DW_MCI_TYPE_ARTPEC8)
147                 clksel = mci_readl(host, CLKSEL64);
148         else
149                 clksel = mci_readl(host, CLKSEL);
150
151         clksel = (clksel & ~SDMMC_CLKSEL_TIMING_MASK) | timing;
152
153         if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
154                 priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU ||
155                 priv->ctrl_type == DW_MCI_TYPE_ARTPEC8)
156                 mci_writel(host, CLKSEL64, clksel);
157         else
158                 mci_writel(host, CLKSEL, clksel);
159
160         /*
161          * Exynos4412 and Exynos5250 extends the use of CMD register with the
162          * use of bit 29 (which is reserved on standard MSHC controllers) for
163          * optionally bypassing the HOLD register for command and data. The
164          * HOLD register should be bypassed in case there is no phase shift
165          * applied on CMD/DATA that is sent to the card.
166          */
167         if (!SDMMC_CLKSEL_GET_DRV_WD3(clksel) && host->slot)
168                 set_bit(DW_MMC_CARD_NO_USE_HOLD, &host->slot->flags);
169 }
170
171 #ifdef CONFIG_PM
172 static int dw_mci_exynos_runtime_resume(struct device *dev)
173 {
174         struct dw_mci *host = dev_get_drvdata(dev);
175         int ret;
176
177         ret = dw_mci_runtime_resume(dev);
178         if (ret)
179                 return ret;
180
181         dw_mci_exynos_config_smu(host);
182
183         return ret;
184 }
185 #endif /* CONFIG_PM */
186
187 #ifdef CONFIG_PM_SLEEP
188 /**
189  * dw_mci_exynos_suspend_noirq - Exynos-specific suspend code
190  * @dev: Device to suspend (this device)
191  *
192  * This ensures that device will be in runtime active state in
193  * dw_mci_exynos_resume_noirq after calling pm_runtime_force_resume()
194  */
195 static int dw_mci_exynos_suspend_noirq(struct device *dev)
196 {
197         pm_runtime_get_noresume(dev);
198         return pm_runtime_force_suspend(dev);
199 }
200
201 /**
202  * dw_mci_exynos_resume_noirq - Exynos-specific resume code
203  * @dev: Device to resume (this device)
204  *
205  * On exynos5420 there is a silicon errata that will sometimes leave the
206  * WAKEUP_INT bit in the CLKSEL register asserted.  This bit is 1 to indicate
207  * that it fired and we can clear it by writing a 1 back.  Clear it to prevent
208  * interrupts from going off constantly.
209  *
210  * We run this code on all exynos variants because it doesn't hurt.
211  */
212 static int dw_mci_exynos_resume_noirq(struct device *dev)
213 {
214         struct dw_mci *host = dev_get_drvdata(dev);
215         struct dw_mci_exynos_priv_data *priv = host->priv;
216         u32 clksel;
217         int ret;
218
219         ret = pm_runtime_force_resume(dev);
220         if (ret)
221                 return ret;
222
223         if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
224                 priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU ||
225                 priv->ctrl_type == DW_MCI_TYPE_ARTPEC8)
226                 clksel = mci_readl(host, CLKSEL64);
227         else
228                 clksel = mci_readl(host, CLKSEL);
229
230         if (clksel & SDMMC_CLKSEL_WAKEUP_INT) {
231                 if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
232                         priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU ||
233                         priv->ctrl_type == DW_MCI_TYPE_ARTPEC8)
234                         mci_writel(host, CLKSEL64, clksel);
235                 else
236                         mci_writel(host, CLKSEL, clksel);
237         }
238
239         pm_runtime_put(dev);
240
241         return 0;
242 }
243 #endif /* CONFIG_PM_SLEEP */
244
245 static void dw_mci_exynos_config_hs400(struct dw_mci *host, u32 timing)
246 {
247         struct dw_mci_exynos_priv_data *priv = host->priv;
248         u32 dqs, strobe;
249
250         /*
251          * Not supported to configure register
252          * related to HS400
253          */
254         if ((priv->ctrl_type < DW_MCI_TYPE_EXYNOS5420) ||
255                 (priv->ctrl_type == DW_MCI_TYPE_ARTPEC8)) {
256                 if (timing == MMC_TIMING_MMC_HS400)
257                         dev_warn(host->dev,
258                                  "cannot configure HS400, unsupported chipset\n");
259                 return;
260         }
261
262         dqs = priv->saved_dqs_en;
263         strobe = priv->saved_strobe_ctrl;
264
265         if (timing == MMC_TIMING_MMC_HS400) {
266                 dqs |= DATA_STROBE_EN;
267                 strobe = DQS_CTRL_RD_DELAY(strobe, priv->dqs_delay);
268         } else if (timing == MMC_TIMING_UHS_SDR104) {
269                 dqs &= 0xffffff00;
270         } else {
271                 dqs &= ~DATA_STROBE_EN;
272         }
273
274         mci_writel(host, HS400_DQS_EN, dqs);
275         mci_writel(host, HS400_DLINE_CTRL, strobe);
276 }
277
278 static void dw_mci_exynos_adjust_clock(struct dw_mci *host, unsigned int wanted)
279 {
280         struct dw_mci_exynos_priv_data *priv = host->priv;
281         unsigned long actual;
282         u8 div;
283         int ret;
284         /*
285          * Don't care if wanted clock is zero or
286          * ciu clock is unavailable
287          */
288         if (!wanted || IS_ERR(host->ciu_clk))
289                 return;
290
291         /* Guaranteed minimum frequency for cclkin */
292         if (wanted < EXYNOS_CCLKIN_MIN)
293                 wanted = EXYNOS_CCLKIN_MIN;
294
295         if (wanted == priv->cur_speed)
296                 return;
297
298         div = dw_mci_exynos_get_ciu_div(host);
299         ret = clk_set_rate(host->ciu_clk, wanted * div);
300         if (ret)
301                 dev_warn(host->dev,
302                         "failed to set clk-rate %u error: %d\n",
303                         wanted * div, ret);
304         actual = clk_get_rate(host->ciu_clk);
305         host->bus_hz = actual / div;
306         priv->cur_speed = wanted;
307         host->current_speed = 0;
308 }
309
310 static void dw_mci_exynos_set_ios(struct dw_mci *host, struct mmc_ios *ios)
311 {
312         struct dw_mci_exynos_priv_data *priv = host->priv;
313         unsigned int wanted = ios->clock;
314         u32 timing = ios->timing, clksel;
315
316         switch (timing) {
317         case MMC_TIMING_MMC_HS400:
318                 /* Update tuned sample timing */
319                 clksel = SDMMC_CLKSEL_UP_SAMPLE(
320                                 priv->hs400_timing, priv->tuned_sample);
321                 wanted <<= 1;
322                 break;
323         case MMC_TIMING_MMC_DDR52:
324                 clksel = priv->ddr_timing;
325                 /* Should be double rate for DDR mode */
326                 if (ios->bus_width == MMC_BUS_WIDTH_8)
327                         wanted <<= 1;
328                 break;
329         case MMC_TIMING_UHS_SDR104:
330         case MMC_TIMING_UHS_SDR50:
331                 clksel = (priv->sdr_timing & 0xfff8ffff) |
332                         (priv->ciu_div << 16);
333                 break;
334         case MMC_TIMING_UHS_DDR50:
335                 clksel = (priv->ddr_timing & 0xfff8ffff) |
336                         (priv->ciu_div << 16);
337                 break;
338         default:
339                 clksel = priv->sdr_timing;
340         }
341
342         /* Set clock timing for the requested speed mode*/
343         dw_mci_exynos_set_clksel_timing(host, clksel);
344
345         /* Configure setting for HS400 */
346         dw_mci_exynos_config_hs400(host, timing);
347
348         /* Configure clock rate */
349         dw_mci_exynos_adjust_clock(host, wanted);
350 }
351
352 static int dw_mci_exynos_parse_dt(struct dw_mci *host)
353 {
354         struct dw_mci_exynos_priv_data *priv;
355         struct device_node *np = host->dev->of_node;
356         u32 timing[2];
357         u32 div = 0;
358         int idx;
359         int ret;
360
361         priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL);
362         if (!priv)
363                 return -ENOMEM;
364
365         for (idx = 0; idx < ARRAY_SIZE(exynos_compat); idx++) {
366                 if (of_device_is_compatible(np, exynos_compat[idx].compatible))
367                         priv->ctrl_type = exynos_compat[idx].ctrl_type;
368         }
369
370         if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4412)
371                 priv->ciu_div = EXYNOS4412_FIXED_CIU_CLK_DIV - 1;
372         else if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4210)
373                 priv->ciu_div = EXYNOS4210_FIXED_CIU_CLK_DIV - 1;
374         else {
375                 of_property_read_u32(np, "samsung,dw-mshc-ciu-div", &div);
376                 priv->ciu_div = div;
377         }
378
379         ret = of_property_read_u32_array(np,
380                         "samsung,dw-mshc-sdr-timing", timing, 2);
381         if (ret)
382                 return ret;
383
384         priv->sdr_timing = SDMMC_CLKSEL_TIMING(timing[0], timing[1], div);
385
386         ret = of_property_read_u32_array(np,
387                         "samsung,dw-mshc-ddr-timing", timing, 2);
388         if (ret)
389                 return ret;
390
391         priv->ddr_timing = SDMMC_CLKSEL_TIMING(timing[0], timing[1], div);
392
393         ret = of_property_read_u32_array(np,
394                         "samsung,dw-mshc-hs400-timing", timing, 2);
395         if (!ret && of_property_read_u32(np,
396                                 "samsung,read-strobe-delay", &priv->dqs_delay))
397                 dev_dbg(host->dev,
398                         "read-strobe-delay is not found, assuming usage of default value\n");
399
400         priv->hs400_timing = SDMMC_CLKSEL_TIMING(timing[0], timing[1],
401                                                 HS400_FIXED_CIU_CLK_DIV);
402         host->priv = priv;
403         return 0;
404 }
405
406 static inline u8 dw_mci_exynos_get_clksmpl(struct dw_mci *host)
407 {
408         struct dw_mci_exynos_priv_data *priv = host->priv;
409
410         if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
411                 priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU ||
412                 priv->ctrl_type == DW_MCI_TYPE_ARTPEC8)
413                 return SDMMC_CLKSEL_CCLK_SAMPLE(mci_readl(host, CLKSEL64));
414         else
415                 return SDMMC_CLKSEL_CCLK_SAMPLE(mci_readl(host, CLKSEL));
416 }
417
418 static inline void dw_mci_exynos_set_clksmpl(struct dw_mci *host, u8 sample)
419 {
420         u32 clksel;
421         struct dw_mci_exynos_priv_data *priv = host->priv;
422
423         if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
424                 priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU ||
425                 priv->ctrl_type == DW_MCI_TYPE_ARTPEC8)
426                 clksel = mci_readl(host, CLKSEL64);
427         else
428                 clksel = mci_readl(host, CLKSEL);
429         clksel = SDMMC_CLKSEL_UP_SAMPLE(clksel, sample);
430         if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
431                 priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU ||
432                 priv->ctrl_type == DW_MCI_TYPE_ARTPEC8)
433                 mci_writel(host, CLKSEL64, clksel);
434         else
435                 mci_writel(host, CLKSEL, clksel);
436 }
437
438 static inline u8 dw_mci_exynos_move_next_clksmpl(struct dw_mci *host)
439 {
440         struct dw_mci_exynos_priv_data *priv = host->priv;
441         u32 clksel;
442         u8 sample;
443
444         if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
445                 priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU ||
446                 priv->ctrl_type == DW_MCI_TYPE_ARTPEC8)
447                 clksel = mci_readl(host, CLKSEL64);
448         else
449                 clksel = mci_readl(host, CLKSEL);
450
451         sample = (clksel + 1) & 0x7;
452         clksel = SDMMC_CLKSEL_UP_SAMPLE(clksel, sample);
453
454         if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
455                 priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU ||
456                 priv->ctrl_type == DW_MCI_TYPE_ARTPEC8)
457                 mci_writel(host, CLKSEL64, clksel);
458         else
459                 mci_writel(host, CLKSEL, clksel);
460
461         return sample;
462 }
463
464 static s8 dw_mci_exynos_get_best_clksmpl(u8 candidates)
465 {
466         const u8 iter = 8;
467         u8 __c;
468         s8 i, loc = -1;
469
470         for (i = 0; i < iter; i++) {
471                 __c = ror8(candidates, i);
472                 if ((__c & 0xc7) == 0xc7) {
473                         loc = i;
474                         goto out;
475                 }
476         }
477
478         for (i = 0; i < iter; i++) {
479                 __c = ror8(candidates, i);
480                 if ((__c & 0x83) == 0x83) {
481                         loc = i;
482                         goto out;
483                 }
484         }
485
486         /*
487          * If there is no cadiates value, then it needs to return -EIO.
488          * If there are candidates values and don't find bset clk sample value,
489          * then use a first candidates clock sample value.
490          */
491         for (i = 0; i < iter; i++) {
492                 __c = ror8(candidates, i);
493                 if ((__c & 0x1) == 0x1) {
494                         loc = i;
495                         goto out;
496                 }
497         }
498 out:
499         return loc;
500 }
501
502 static int dw_mci_exynos_execute_tuning(struct dw_mci_slot *slot, u32 opcode)
503 {
504         struct dw_mci *host = slot->host;
505         struct dw_mci_exynos_priv_data *priv = host->priv;
506         struct mmc_host *mmc = slot->mmc;
507         u8 start_smpl, smpl, candidates = 0;
508         s8 found;
509         int ret = 0;
510
511         start_smpl = dw_mci_exynos_get_clksmpl(host);
512
513         do {
514                 mci_writel(host, TMOUT, ~0);
515                 smpl = dw_mci_exynos_move_next_clksmpl(host);
516
517                 if (!mmc_send_tuning(mmc, opcode, NULL))
518                         candidates |= (1 << smpl);
519
520         } while (start_smpl != smpl);
521
522         found = dw_mci_exynos_get_best_clksmpl(candidates);
523         if (found >= 0) {
524                 dw_mci_exynos_set_clksmpl(host, found);
525                 priv->tuned_sample = found;
526         } else {
527                 ret = -EIO;
528                 dev_warn(&mmc->class_dev,
529                         "There is no candidates value about clksmpl!\n");
530         }
531
532         return ret;
533 }
534
535 static int dw_mci_exynos_prepare_hs400_tuning(struct dw_mci *host,
536                                         struct mmc_ios *ios)
537 {
538         struct dw_mci_exynos_priv_data *priv = host->priv;
539
540         dw_mci_exynos_set_clksel_timing(host, priv->hs400_timing);
541         dw_mci_exynos_adjust_clock(host, (ios->clock) << 1);
542
543         return 0;
544 }
545
546 static void dw_mci_exynos_set_data_timeout(struct dw_mci *host,
547                                            unsigned int timeout_ns)
548 {
549         u32 clk_div, tmout;
550         u64 tmp;
551         unsigned int tmp2;
552
553         clk_div = (mci_readl(host, CLKDIV) & 0xFF) * 2;
554         if (clk_div == 0)
555                 clk_div = 1;
556
557         tmp = DIV_ROUND_UP_ULL((u64)timeout_ns * host->bus_hz, NSEC_PER_SEC);
558         tmp = DIV_ROUND_UP_ULL(tmp, clk_div);
559
560         /* TMOUT[7:0] (RESPONSE_TIMEOUT) */
561         tmout = 0xFF; /* Set maximum */
562
563         /*
564          * Extended HW timer (max = 0x6FFFFF2):
565          * ((TMOUT[10:8] - 1) * 0xFFFFFF + TMOUT[31:11] * 8)
566          */
567         if (!tmp || tmp > 0x6FFFFF2)
568                 tmout |= (0xFFFFFF << 8);
569         else {
570                 /* TMOUT[10:8] */
571                 tmp2 = (((unsigned int)tmp / 0xFFFFFF) + 1) & 0x7;
572                 tmout |= tmp2 << 8;
573
574                 /* TMOUT[31:11] */
575                 tmp = tmp - ((tmp2 - 1) * 0xFFFFFF);
576                 tmout |= (tmp & 0xFFFFF8) << 8;
577         }
578
579         mci_writel(host, TMOUT, tmout);
580         dev_dbg(host->dev, "timeout_ns: %u => TMOUT[31:8]: %#08x",
581                 timeout_ns, tmout >> 8);
582 }
583
584 static u32 dw_mci_exynos_get_drto_clks(struct dw_mci *host)
585 {
586         u32 drto_clks;
587
588         drto_clks = mci_readl(host, TMOUT) >> 8;
589
590         return (((drto_clks & 0x7) - 1) * 0xFFFFFF) + ((drto_clks & 0xFFFFF8));
591 }
592
593 /* Common capabilities of Exynos4/Exynos5 SoC */
594 static unsigned long exynos_dwmmc_caps[4] = {
595         MMC_CAP_1_8V_DDR | MMC_CAP_8_BIT_DATA,
596         0,
597         0,
598         0,
599 };
600
601 static const struct dw_mci_drv_data exynos_drv_data = {
602         .caps                   = exynos_dwmmc_caps,
603         .num_caps               = ARRAY_SIZE(exynos_dwmmc_caps),
604         .common_caps            = MMC_CAP_CMD23,
605         .init                   = dw_mci_exynos_priv_init,
606         .set_ios                = dw_mci_exynos_set_ios,
607         .parse_dt               = dw_mci_exynos_parse_dt,
608         .execute_tuning         = dw_mci_exynos_execute_tuning,
609         .prepare_hs400_tuning   = dw_mci_exynos_prepare_hs400_tuning,
610 };
611
612 static const struct dw_mci_drv_data artpec_drv_data = {
613         .common_caps            = MMC_CAP_CMD23,
614         .init                   = dw_mci_exynos_priv_init,
615         .set_ios                = dw_mci_exynos_set_ios,
616         .parse_dt               = dw_mci_exynos_parse_dt,
617         .execute_tuning         = dw_mci_exynos_execute_tuning,
618         .set_data_timeout               = dw_mci_exynos_set_data_timeout,
619         .get_drto_clks          = dw_mci_exynos_get_drto_clks,
620 };
621
622 static const struct of_device_id dw_mci_exynos_match[] = {
623         { .compatible = "samsung,exynos4412-dw-mshc",
624                         .data = &exynos_drv_data, },
625         { .compatible = "samsung,exynos5250-dw-mshc",
626                         .data = &exynos_drv_data, },
627         { .compatible = "samsung,exynos5420-dw-mshc",
628                         .data = &exynos_drv_data, },
629         { .compatible = "samsung,exynos5420-dw-mshc-smu",
630                         .data = &exynos_drv_data, },
631         { .compatible = "samsung,exynos7-dw-mshc",
632                         .data = &exynos_drv_data, },
633         { .compatible = "samsung,exynos7-dw-mshc-smu",
634                         .data = &exynos_drv_data, },
635         { .compatible = "axis,artpec8-dw-mshc",
636                         .data = &artpec_drv_data, },
637         {},
638 };
639 MODULE_DEVICE_TABLE(of, dw_mci_exynos_match);
640
641 static int dw_mci_exynos_probe(struct platform_device *pdev)
642 {
643         const struct dw_mci_drv_data *drv_data;
644         const struct of_device_id *match;
645         int ret;
646
647         match = of_match_node(dw_mci_exynos_match, pdev->dev.of_node);
648         drv_data = match->data;
649
650         pm_runtime_get_noresume(&pdev->dev);
651         pm_runtime_set_active(&pdev->dev);
652         pm_runtime_enable(&pdev->dev);
653
654         ret = dw_mci_pltfm_register(pdev, drv_data);
655         if (ret) {
656                 pm_runtime_disable(&pdev->dev);
657                 pm_runtime_set_suspended(&pdev->dev);
658                 pm_runtime_put_noidle(&pdev->dev);
659
660                 return ret;
661         }
662
663         return 0;
664 }
665
666 static void dw_mci_exynos_remove(struct platform_device *pdev)
667 {
668         pm_runtime_disable(&pdev->dev);
669         pm_runtime_set_suspended(&pdev->dev);
670         pm_runtime_put_noidle(&pdev->dev);
671
672         dw_mci_pltfm_remove(pdev);
673 }
674
675 static const struct dev_pm_ops dw_mci_exynos_pmops = {
676         SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(dw_mci_exynos_suspend_noirq,
677                                       dw_mci_exynos_resume_noirq)
678         SET_RUNTIME_PM_OPS(dw_mci_runtime_suspend,
679                            dw_mci_exynos_runtime_resume,
680                            NULL)
681 };
682
683 static struct platform_driver dw_mci_exynos_pltfm_driver = {
684         .probe          = dw_mci_exynos_probe,
685         .remove_new     = dw_mci_exynos_remove,
686         .driver         = {
687                 .name           = "dwmmc_exynos",
688                 .probe_type     = PROBE_PREFER_ASYNCHRONOUS,
689                 .of_match_table = dw_mci_exynos_match,
690                 .pm             = &dw_mci_exynos_pmops,
691         },
692 };
693
694 module_platform_driver(dw_mci_exynos_pltfm_driver);
695
696 MODULE_DESCRIPTION("Samsung Specific DW-MSHC Driver Extension");
697 MODULE_AUTHOR("Thomas Abraham <thomas.ab@samsung.com");
698 MODULE_LICENSE("GPL v2");
699 MODULE_ALIAS("platform:dwmmc_exynos");