]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/mmc/host/dw_mmc-exynos.c
Merge branch 'akpm-current/current'
[karo-tx-linux.git] / drivers / mmc / host / dw_mmc-exynos.c
1 /*
2  * Exynos Specific Extensions for Synopsys DW Multimedia Card Interface driver
3  *
4  * Copyright (C) 2012, Samsung Electronics Co., Ltd.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  */
11
12 #include <linux/module.h>
13 #include <linux/platform_device.h>
14 #include <linux/clk.h>
15 #include <linux/mmc/host.h>
16 #include <linux/mmc/dw_mmc.h>
17 #include <linux/mmc/mmc.h>
18 #include <linux/of.h>
19 #include <linux/of_gpio.h>
20 #include <linux/slab.h>
21
22 #include "dw_mmc.h"
23 #include "dw_mmc-pltfm.h"
24 #include "dw_mmc-exynos.h"
25
26 /* Variations in Exynos specific dw-mshc controller */
27 enum dw_mci_exynos_type {
28         DW_MCI_TYPE_EXYNOS4210,
29         DW_MCI_TYPE_EXYNOS4412,
30         DW_MCI_TYPE_EXYNOS5250,
31         DW_MCI_TYPE_EXYNOS5420,
32         DW_MCI_TYPE_EXYNOS5420_SMU,
33         DW_MCI_TYPE_EXYNOS7,
34         DW_MCI_TYPE_EXYNOS7_SMU,
35 };
36
37 /* Exynos implementation specific driver private data */
38 struct dw_mci_exynos_priv_data {
39         enum dw_mci_exynos_type         ctrl_type;
40         u8                              ciu_div;
41         u32                             sdr_timing;
42         u32                             ddr_timing;
43         u32                             hs400_timing;
44         u32                             tuned_sample;
45         u32                             cur_speed;
46         u32                             dqs_delay;
47         u32                             saved_dqs_en;
48         u32                             saved_strobe_ctrl;
49 };
50
51 static struct dw_mci_exynos_compatible {
52         char                            *compatible;
53         enum dw_mci_exynos_type         ctrl_type;
54 } exynos_compat[] = {
55         {
56                 .compatible     = "samsung,exynos4210-dw-mshc",
57                 .ctrl_type      = DW_MCI_TYPE_EXYNOS4210,
58         }, {
59                 .compatible     = "samsung,exynos4412-dw-mshc",
60                 .ctrl_type      = DW_MCI_TYPE_EXYNOS4412,
61         }, {
62                 .compatible     = "samsung,exynos5250-dw-mshc",
63                 .ctrl_type      = DW_MCI_TYPE_EXYNOS5250,
64         }, {
65                 .compatible     = "samsung,exynos5420-dw-mshc",
66                 .ctrl_type      = DW_MCI_TYPE_EXYNOS5420,
67         }, {
68                 .compatible     = "samsung,exynos5420-dw-mshc-smu",
69                 .ctrl_type      = DW_MCI_TYPE_EXYNOS5420_SMU,
70         }, {
71                 .compatible     = "samsung,exynos7-dw-mshc",
72                 .ctrl_type      = DW_MCI_TYPE_EXYNOS7,
73         }, {
74                 .compatible     = "samsung,exynos7-dw-mshc-smu",
75                 .ctrl_type      = DW_MCI_TYPE_EXYNOS7_SMU,
76         },
77 };
78
79 static inline u8 dw_mci_exynos_get_ciu_div(struct dw_mci *host)
80 {
81         struct dw_mci_exynos_priv_data *priv = host->priv;
82
83         if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4412)
84                 return EXYNOS4412_FIXED_CIU_CLK_DIV;
85         else if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4210)
86                 return EXYNOS4210_FIXED_CIU_CLK_DIV;
87         else if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
88                         priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
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 int dw_mci_exynos_priv_init(struct dw_mci *host)
95 {
96         struct dw_mci_exynos_priv_data *priv = host->priv;
97
98         if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS5420_SMU ||
99                 priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU) {
100                 mci_writel(host, MPSBEGIN0, 0);
101                 mci_writel(host, MPSEND0, SDMMC_ENDING_SEC_NR_MAX);
102                 mci_writel(host, MPSCTRL0, SDMMC_MPSCTRL_SECURE_WRITE_BIT |
103                            SDMMC_MPSCTRL_NON_SECURE_READ_BIT |
104                            SDMMC_MPSCTRL_VALID |
105                            SDMMC_MPSCTRL_NON_SECURE_WRITE_BIT);
106         }
107
108         if (priv->ctrl_type >= DW_MCI_TYPE_EXYNOS5420) {
109                 priv->saved_strobe_ctrl = mci_readl(host, HS400_DLINE_CTRL);
110                 priv->saved_dqs_en = mci_readl(host, HS400_DQS_EN);
111                 priv->saved_dqs_en |= AXI_NON_BLOCKING_WR;
112                 mci_writel(host, HS400_DQS_EN, priv->saved_dqs_en);
113                 if (!priv->dqs_delay)
114                         priv->dqs_delay =
115                                 DQS_CTRL_GET_RD_DELAY(priv->saved_strobe_ctrl);
116         }
117
118         return 0;
119 }
120
121 static int dw_mci_exynos_setup_clock(struct dw_mci *host)
122 {
123         struct dw_mci_exynos_priv_data *priv = host->priv;
124
125         host->bus_hz /= (priv->ciu_div + 1);
126
127         return 0;
128 }
129
130 static void dw_mci_exynos_set_clksel_timing(struct dw_mci *host, u32 timing)
131 {
132         struct dw_mci_exynos_priv_data *priv = host->priv;
133         u32 clksel;
134
135         if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
136                 priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
137                 clksel = mci_readl(host, CLKSEL64);
138         else
139                 clksel = mci_readl(host, CLKSEL);
140
141         clksel = (clksel & ~SDMMC_CLKSEL_TIMING_MASK) | timing;
142
143         if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
144                 priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
145                 mci_writel(host, CLKSEL64, clksel);
146         else
147                 mci_writel(host, CLKSEL, clksel);
148
149         /*
150          * Exynos4412 and Exynos5250 extends the use of CMD register with the
151          * use of bit 29 (which is reserved on standard MSHC controllers) for
152          * optionally bypassing the HOLD register for command and data. The
153          * HOLD register should be bypassed in case there is no phase shift
154          * applied on CMD/DATA that is sent to the card.
155          */
156         if (!SDMMC_CLKSEL_GET_DRV_WD3(clksel))
157                 set_bit(DW_MMC_CARD_NO_USE_HOLD, &host->cur_slot->flags);
158 }
159
160 #ifdef CONFIG_PM_SLEEP
161 static int dw_mci_exynos_suspend(struct device *dev)
162 {
163         struct dw_mci *host = dev_get_drvdata(dev);
164
165         return dw_mci_suspend(host);
166 }
167
168 static int dw_mci_exynos_resume(struct device *dev)
169 {
170         struct dw_mci *host = dev_get_drvdata(dev);
171
172         dw_mci_exynos_priv_init(host);
173         return dw_mci_resume(host);
174 }
175
176 /**
177  * dw_mci_exynos_resume_noirq - Exynos-specific resume code
178  *
179  * On exynos5420 there is a silicon errata that will sometimes leave the
180  * WAKEUP_INT bit in the CLKSEL register asserted.  This bit is 1 to indicate
181  * that it fired and we can clear it by writing a 1 back.  Clear it to prevent
182  * interrupts from going off constantly.
183  *
184  * We run this code on all exynos variants because it doesn't hurt.
185  */
186
187 static int dw_mci_exynos_resume_noirq(struct device *dev)
188 {
189         struct dw_mci *host = dev_get_drvdata(dev);
190         struct dw_mci_exynos_priv_data *priv = host->priv;
191         u32 clksel;
192
193         if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
194                 priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
195                 clksel = mci_readl(host, CLKSEL64);
196         else
197                 clksel = mci_readl(host, CLKSEL);
198
199         if (clksel & SDMMC_CLKSEL_WAKEUP_INT) {
200                 if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
201                         priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
202                         mci_writel(host, CLKSEL64, clksel);
203                 else
204                         mci_writel(host, CLKSEL, clksel);
205         }
206
207         return 0;
208 }
209 #else
210 #define dw_mci_exynos_suspend           NULL
211 #define dw_mci_exynos_resume            NULL
212 #define dw_mci_exynos_resume_noirq      NULL
213 #endif /* CONFIG_PM_SLEEP */
214
215 static void dw_mci_exynos_config_hs400(struct dw_mci *host, u32 timing)
216 {
217         struct dw_mci_exynos_priv_data *priv = host->priv;
218         u32 dqs, strobe;
219
220         /*
221          * Not supported to configure register
222          * related to HS400
223          */
224         if (priv->ctrl_type < DW_MCI_TYPE_EXYNOS5420)
225                 return;
226
227         dqs = priv->saved_dqs_en;
228         strobe = priv->saved_strobe_ctrl;
229
230         if (timing == MMC_TIMING_MMC_HS400) {
231                 dqs |= DATA_STROBE_EN;
232                 strobe = DQS_CTRL_RD_DELAY(strobe, priv->dqs_delay);
233         } else {
234                 dqs &= ~DATA_STROBE_EN;
235         }
236
237         mci_writel(host, HS400_DQS_EN, dqs);
238         mci_writel(host, HS400_DLINE_CTRL, strobe);
239 }
240
241 static void dw_mci_exynos_adjust_clock(struct dw_mci *host, unsigned int wanted)
242 {
243         struct dw_mci_exynos_priv_data *priv = host->priv;
244         unsigned long actual;
245         u8 div;
246         int ret;
247         /*
248          * Don't care if wanted clock is zero or
249          * ciu clock is unavailable
250          */
251         if (!wanted || IS_ERR(host->ciu_clk))
252                 return;
253
254         /* Guaranteed minimum frequency for cclkin */
255         if (wanted < EXYNOS_CCLKIN_MIN)
256                 wanted = EXYNOS_CCLKIN_MIN;
257
258         if (wanted == priv->cur_speed)
259                 return;
260
261         div = dw_mci_exynos_get_ciu_div(host);
262         ret = clk_set_rate(host->ciu_clk, wanted * div);
263         if (ret)
264                 dev_warn(host->dev,
265                         "failed to set clk-rate %u error: %d\n",
266                         wanted * div, ret);
267         actual = clk_get_rate(host->ciu_clk);
268         host->bus_hz = actual / div;
269         priv->cur_speed = wanted;
270         host->current_speed = 0;
271 }
272
273 static void dw_mci_exynos_set_ios(struct dw_mci *host, struct mmc_ios *ios)
274 {
275         struct dw_mci_exynos_priv_data *priv = host->priv;
276         unsigned int wanted = ios->clock;
277         u32 timing = ios->timing, clksel;
278
279         switch (timing) {
280         case MMC_TIMING_MMC_HS400:
281                 /* Update tuned sample timing */
282                 clksel = SDMMC_CLKSEL_UP_SAMPLE(
283                                 priv->hs400_timing, priv->tuned_sample);
284                 wanted <<= 1;
285                 break;
286         case MMC_TIMING_MMC_DDR52:
287                 clksel = priv->ddr_timing;
288                 /* Should be double rate for DDR mode */
289                 if (ios->bus_width == MMC_BUS_WIDTH_8)
290                         wanted <<= 1;
291                 break;
292         default:
293                 clksel = priv->sdr_timing;
294         }
295
296         /* Set clock timing for the requested speed mode*/
297         dw_mci_exynos_set_clksel_timing(host, clksel);
298
299         /* Configure setting for HS400 */
300         dw_mci_exynos_config_hs400(host, timing);
301
302         /* Configure clock rate */
303         dw_mci_exynos_adjust_clock(host, wanted);
304 }
305
306 static int dw_mci_exynos_parse_dt(struct dw_mci *host)
307 {
308         struct dw_mci_exynos_priv_data *priv;
309         struct device_node *np = host->dev->of_node;
310         u32 timing[2];
311         u32 div = 0;
312         int idx;
313         int ret;
314
315         priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL);
316         if (!priv)
317                 return -ENOMEM;
318
319         for (idx = 0; idx < ARRAY_SIZE(exynos_compat); idx++) {
320                 if (of_device_is_compatible(np, exynos_compat[idx].compatible))
321                         priv->ctrl_type = exynos_compat[idx].ctrl_type;
322         }
323
324         if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4412)
325                 priv->ciu_div = EXYNOS4412_FIXED_CIU_CLK_DIV - 1;
326         else if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4210)
327                 priv->ciu_div = EXYNOS4210_FIXED_CIU_CLK_DIV - 1;
328         else {
329                 of_property_read_u32(np, "samsung,dw-mshc-ciu-div", &div);
330                 priv->ciu_div = div;
331         }
332
333         ret = of_property_read_u32_array(np,
334                         "samsung,dw-mshc-sdr-timing", timing, 2);
335         if (ret)
336                 return ret;
337
338         priv->sdr_timing = SDMMC_CLKSEL_TIMING(timing[0], timing[1], div);
339
340         ret = of_property_read_u32_array(np,
341                         "samsung,dw-mshc-ddr-timing", timing, 2);
342         if (ret)
343                 return ret;
344
345         priv->ddr_timing = SDMMC_CLKSEL_TIMING(timing[0], timing[1], div);
346
347         ret = of_property_read_u32_array(np,
348                         "samsung,dw-mshc-hs400-timing", timing, 2);
349         if (!ret && of_property_read_u32(np,
350                                 "samsung,read-strobe-delay", &priv->dqs_delay))
351                 dev_dbg(host->dev,
352                         "read-strobe-delay is not found, assuming usage of default value\n");
353
354         priv->hs400_timing = SDMMC_CLKSEL_TIMING(timing[0], timing[1],
355                                                 HS400_FIXED_CIU_CLK_DIV);
356         host->priv = priv;
357         return 0;
358 }
359
360 static inline u8 dw_mci_exynos_get_clksmpl(struct dw_mci *host)
361 {
362         struct dw_mci_exynos_priv_data *priv = host->priv;
363
364         if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
365                 priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
366                 return SDMMC_CLKSEL_CCLK_SAMPLE(mci_readl(host, CLKSEL64));
367         else
368                 return SDMMC_CLKSEL_CCLK_SAMPLE(mci_readl(host, CLKSEL));
369 }
370
371 static inline void dw_mci_exynos_set_clksmpl(struct dw_mci *host, u8 sample)
372 {
373         u32 clksel;
374         struct dw_mci_exynos_priv_data *priv = host->priv;
375
376         if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
377                 priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
378                 clksel = mci_readl(host, CLKSEL64);
379         else
380                 clksel = mci_readl(host, CLKSEL);
381         clksel = SDMMC_CLKSEL_UP_SAMPLE(clksel, sample);
382         if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
383                 priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
384                 mci_writel(host, CLKSEL64, clksel);
385         else
386                 mci_writel(host, CLKSEL, clksel);
387 }
388
389 static inline u8 dw_mci_exynos_move_next_clksmpl(struct dw_mci *host)
390 {
391         struct dw_mci_exynos_priv_data *priv = host->priv;
392         u32 clksel;
393         u8 sample;
394
395         if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
396                 priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
397                 clksel = mci_readl(host, CLKSEL64);
398         else
399                 clksel = mci_readl(host, CLKSEL);
400
401         sample = (clksel + 1) & 0x7;
402         clksel = SDMMC_CLKSEL_UP_SAMPLE(clksel, sample);
403
404         if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
405                 priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
406                 mci_writel(host, CLKSEL64, clksel);
407         else
408                 mci_writel(host, CLKSEL, clksel);
409
410         return sample;
411 }
412
413 static s8 dw_mci_exynos_get_best_clksmpl(u8 candiates)
414 {
415         const u8 iter = 8;
416         u8 __c;
417         s8 i, loc = -1;
418
419         for (i = 0; i < iter; i++) {
420                 __c = ror8(candiates, i);
421                 if ((__c & 0xc7) == 0xc7) {
422                         loc = i;
423                         goto out;
424                 }
425         }
426
427         for (i = 0; i < iter; i++) {
428                 __c = ror8(candiates, i);
429                 if ((__c & 0x83) == 0x83) {
430                         loc = i;
431                         goto out;
432                 }
433         }
434
435 out:
436         return loc;
437 }
438
439 static int dw_mci_exynos_execute_tuning(struct dw_mci_slot *slot, u32 opcode)
440 {
441         struct dw_mci *host = slot->host;
442         struct dw_mci_exynos_priv_data *priv = host->priv;
443         struct mmc_host *mmc = slot->mmc;
444         u8 start_smpl, smpl, candiates = 0;
445         s8 found = -1;
446         int ret = 0;
447
448         start_smpl = dw_mci_exynos_get_clksmpl(host);
449
450         do {
451                 mci_writel(host, TMOUT, ~0);
452                 smpl = dw_mci_exynos_move_next_clksmpl(host);
453
454                 if (!mmc_send_tuning(mmc, opcode, NULL))
455                         candiates |= (1 << smpl);
456
457         } while (start_smpl != smpl);
458
459         found = dw_mci_exynos_get_best_clksmpl(candiates);
460         if (found >= 0) {
461                 dw_mci_exynos_set_clksmpl(host, found);
462                 priv->tuned_sample = found;
463         } else {
464                 ret = -EIO;
465         }
466
467         return ret;
468 }
469
470 static int dw_mci_exynos_prepare_hs400_tuning(struct dw_mci *host,
471                                         struct mmc_ios *ios)
472 {
473         struct dw_mci_exynos_priv_data *priv = host->priv;
474
475         dw_mci_exynos_set_clksel_timing(host, priv->hs400_timing);
476         dw_mci_exynos_adjust_clock(host, (ios->clock) << 1);
477
478         return 0;
479 }
480
481 /* Common capabilities of Exynos4/Exynos5 SoC */
482 static unsigned long exynos_dwmmc_caps[4] = {
483         MMC_CAP_1_8V_DDR | MMC_CAP_8_BIT_DATA | MMC_CAP_CMD23,
484         MMC_CAP_CMD23,
485         MMC_CAP_CMD23,
486         MMC_CAP_CMD23,
487 };
488
489 static const struct dw_mci_drv_data exynos_drv_data = {
490         .caps                   = exynos_dwmmc_caps,
491         .init                   = dw_mci_exynos_priv_init,
492         .setup_clock            = dw_mci_exynos_setup_clock,
493         .set_ios                = dw_mci_exynos_set_ios,
494         .parse_dt               = dw_mci_exynos_parse_dt,
495         .execute_tuning         = dw_mci_exynos_execute_tuning,
496         .prepare_hs400_tuning   = dw_mci_exynos_prepare_hs400_tuning,
497 };
498
499 static const struct of_device_id dw_mci_exynos_match[] = {
500         { .compatible = "samsung,exynos4412-dw-mshc",
501                         .data = &exynos_drv_data, },
502         { .compatible = "samsung,exynos5250-dw-mshc",
503                         .data = &exynos_drv_data, },
504         { .compatible = "samsung,exynos5420-dw-mshc",
505                         .data = &exynos_drv_data, },
506         { .compatible = "samsung,exynos5420-dw-mshc-smu",
507                         .data = &exynos_drv_data, },
508         { .compatible = "samsung,exynos7-dw-mshc",
509                         .data = &exynos_drv_data, },
510         { .compatible = "samsung,exynos7-dw-mshc-smu",
511                         .data = &exynos_drv_data, },
512         {},
513 };
514 MODULE_DEVICE_TABLE(of, dw_mci_exynos_match);
515
516 static int dw_mci_exynos_probe(struct platform_device *pdev)
517 {
518         const struct dw_mci_drv_data *drv_data;
519         const struct of_device_id *match;
520
521         match = of_match_node(dw_mci_exynos_match, pdev->dev.of_node);
522         drv_data = match->data;
523         return dw_mci_pltfm_register(pdev, drv_data);
524 }
525
526 static const struct dev_pm_ops dw_mci_exynos_pmops = {
527         SET_SYSTEM_SLEEP_PM_OPS(dw_mci_exynos_suspend, dw_mci_exynos_resume)
528         .resume_noirq = dw_mci_exynos_resume_noirq,
529         .thaw_noirq = dw_mci_exynos_resume_noirq,
530         .restore_noirq = dw_mci_exynos_resume_noirq,
531 };
532
533 static struct platform_driver dw_mci_exynos_pltfm_driver = {
534         .probe          = dw_mci_exynos_probe,
535         .remove         = dw_mci_pltfm_remove,
536         .driver         = {
537                 .name           = "dwmmc_exynos",
538                 .of_match_table = dw_mci_exynos_match,
539                 .pm             = &dw_mci_exynos_pmops,
540         },
541 };
542
543 module_platform_driver(dw_mci_exynos_pltfm_driver);
544
545 MODULE_DESCRIPTION("Samsung Specific DW-MSHC Driver Extension");
546 MODULE_AUTHOR("Thomas Abraham <thomas.ab@samsung.com");
547 MODULE_LICENSE("GPL v2");
548 MODULE_ALIAS("platform:dwmmc_exynos");