]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - drivers/mmc/host/dw_mmc-exynos.c
Merge branch 'locking-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[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 #ifdef CONFIG_PM_SLEEP
151 static int dw_mci_exynos_suspend(struct device *dev)
152 {
153         struct dw_mci *host = dev_get_drvdata(dev);
154
155         return dw_mci_suspend(host);
156 }
157
158 static int dw_mci_exynos_resume(struct device *dev)
159 {
160         struct dw_mci *host = dev_get_drvdata(dev);
161
162         dw_mci_exynos_priv_init(host);
163         return dw_mci_resume(host);
164 }
165
166 /**
167  * dw_mci_exynos_resume_noirq - Exynos-specific resume code
168  *
169  * On exynos5420 there is a silicon errata that will sometimes leave the
170  * WAKEUP_INT bit in the CLKSEL register asserted.  This bit is 1 to indicate
171  * that it fired and we can clear it by writing a 1 back.  Clear it to prevent
172  * interrupts from going off constantly.
173  *
174  * We run this code on all exynos variants because it doesn't hurt.
175  */
176
177 static int dw_mci_exynos_resume_noirq(struct device *dev)
178 {
179         struct dw_mci *host = dev_get_drvdata(dev);
180         struct dw_mci_exynos_priv_data *priv = host->priv;
181         u32 clksel;
182
183         if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
184                 priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
185                 clksel = mci_readl(host, CLKSEL64);
186         else
187                 clksel = mci_readl(host, CLKSEL);
188
189         if (clksel & SDMMC_CLKSEL_WAKEUP_INT) {
190                 if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
191                         priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
192                         mci_writel(host, CLKSEL64, clksel);
193                 else
194                         mci_writel(host, CLKSEL, clksel);
195         }
196
197         return 0;
198 }
199 #else
200 #define dw_mci_exynos_suspend           NULL
201 #define dw_mci_exynos_resume            NULL
202 #define dw_mci_exynos_resume_noirq      NULL
203 #endif /* CONFIG_PM_SLEEP */
204
205 static void dw_mci_exynos_prepare_command(struct dw_mci *host, u32 *cmdr)
206 {
207         struct dw_mci_exynos_priv_data *priv = host->priv;
208         /*
209          * Exynos4412 and Exynos5250 extends the use of CMD register with the
210          * use of bit 29 (which is reserved on standard MSHC controllers) for
211          * optionally bypassing the HOLD register for command and data. The
212          * HOLD register should be bypassed in case there is no phase shift
213          * applied on CMD/DATA that is sent to the card.
214          */
215         if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
216                 priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU) {
217                 if (SDMMC_CLKSEL_GET_DRV_WD3(mci_readl(host, CLKSEL64)))
218                         *cmdr |= SDMMC_CMD_USE_HOLD_REG;
219          } else {
220                 if (SDMMC_CLKSEL_GET_DRV_WD3(mci_readl(host, CLKSEL)))
221                         *cmdr |= SDMMC_CMD_USE_HOLD_REG;
222         }
223 }
224
225 static void dw_mci_exynos_config_hs400(struct dw_mci *host, u32 timing)
226 {
227         struct dw_mci_exynos_priv_data *priv = host->priv;
228         u32 dqs, strobe;
229
230         /*
231          * Not supported to configure register
232          * related to HS400
233          */
234         if (priv->ctrl_type < DW_MCI_TYPE_EXYNOS5420)
235                 return;
236
237         dqs = priv->saved_dqs_en;
238         strobe = priv->saved_strobe_ctrl;
239
240         if (timing == MMC_TIMING_MMC_HS400) {
241                 dqs |= DATA_STROBE_EN;
242                 strobe = DQS_CTRL_RD_DELAY(strobe, priv->dqs_delay);
243         } else {
244                 dqs &= ~DATA_STROBE_EN;
245         }
246
247         mci_writel(host, HS400_DQS_EN, dqs);
248         mci_writel(host, HS400_DLINE_CTRL, strobe);
249 }
250
251 static void dw_mci_exynos_adjust_clock(struct dw_mci *host, unsigned int wanted)
252 {
253         struct dw_mci_exynos_priv_data *priv = host->priv;
254         unsigned long actual;
255         u8 div;
256         int ret;
257         /*
258          * Don't care if wanted clock is zero or
259          * ciu clock is unavailable
260          */
261         if (!wanted || IS_ERR(host->ciu_clk))
262                 return;
263
264         /* Guaranteed minimum frequency for cclkin */
265         if (wanted < EXYNOS_CCLKIN_MIN)
266                 wanted = EXYNOS_CCLKIN_MIN;
267
268         if (wanted == priv->cur_speed)
269                 return;
270
271         div = dw_mci_exynos_get_ciu_div(host);
272         ret = clk_set_rate(host->ciu_clk, wanted * div);
273         if (ret)
274                 dev_warn(host->dev,
275                         "failed to set clk-rate %u error: %d\n",
276                         wanted * div, ret);
277         actual = clk_get_rate(host->ciu_clk);
278         host->bus_hz = actual / div;
279         priv->cur_speed = wanted;
280         host->current_speed = 0;
281 }
282
283 static void dw_mci_exynos_set_ios(struct dw_mci *host, struct mmc_ios *ios)
284 {
285         struct dw_mci_exynos_priv_data *priv = host->priv;
286         unsigned int wanted = ios->clock;
287         u32 timing = ios->timing, clksel;
288
289         switch (timing) {
290         case MMC_TIMING_MMC_HS400:
291                 /* Update tuned sample timing */
292                 clksel = SDMMC_CLKSEL_UP_SAMPLE(
293                                 priv->hs400_timing, priv->tuned_sample);
294                 wanted <<= 1;
295                 break;
296         case MMC_TIMING_MMC_DDR52:
297                 clksel = priv->ddr_timing;
298                 /* Should be double rate for DDR mode */
299                 if (ios->bus_width == MMC_BUS_WIDTH_8)
300                         wanted <<= 1;
301                 break;
302         default:
303                 clksel = priv->sdr_timing;
304         }
305
306         /* Set clock timing for the requested speed mode*/
307         dw_mci_exynos_set_clksel_timing(host, clksel);
308
309         /* Configure setting for HS400 */
310         dw_mci_exynos_config_hs400(host, timing);
311
312         /* Configure clock rate */
313         dw_mci_exynos_adjust_clock(host, wanted);
314 }
315
316 static int dw_mci_exynos_parse_dt(struct dw_mci *host)
317 {
318         struct dw_mci_exynos_priv_data *priv;
319         struct device_node *np = host->dev->of_node;
320         u32 timing[2];
321         u32 div = 0;
322         int idx;
323         int ret;
324
325         priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL);
326         if (!priv)
327                 return -ENOMEM;
328
329         for (idx = 0; idx < ARRAY_SIZE(exynos_compat); idx++) {
330                 if (of_device_is_compatible(np, exynos_compat[idx].compatible))
331                         priv->ctrl_type = exynos_compat[idx].ctrl_type;
332         }
333
334         if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4412)
335                 priv->ciu_div = EXYNOS4412_FIXED_CIU_CLK_DIV - 1;
336         else if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4210)
337                 priv->ciu_div = EXYNOS4210_FIXED_CIU_CLK_DIV - 1;
338         else {
339                 of_property_read_u32(np, "samsung,dw-mshc-ciu-div", &div);
340                 priv->ciu_div = div;
341         }
342
343         ret = of_property_read_u32_array(np,
344                         "samsung,dw-mshc-sdr-timing", timing, 2);
345         if (ret)
346                 return ret;
347
348         priv->sdr_timing = SDMMC_CLKSEL_TIMING(timing[0], timing[1], div);
349
350         ret = of_property_read_u32_array(np,
351                         "samsung,dw-mshc-ddr-timing", timing, 2);
352         if (ret)
353                 return ret;
354
355         priv->ddr_timing = SDMMC_CLKSEL_TIMING(timing[0], timing[1], div);
356
357         ret = of_property_read_u32_array(np,
358                         "samsung,dw-mshc-hs400-timing", timing, 2);
359         if (!ret && of_property_read_u32(np,
360                                 "samsung,read-strobe-delay", &priv->dqs_delay))
361                 dev_dbg(host->dev,
362                         "read-strobe-delay is not found, assuming usage of default value\n");
363
364         priv->hs400_timing = SDMMC_CLKSEL_TIMING(timing[0], timing[1],
365                                                 HS400_FIXED_CIU_CLK_DIV);
366         host->priv = priv;
367         return 0;
368 }
369
370 static inline u8 dw_mci_exynos_get_clksmpl(struct dw_mci *host)
371 {
372         struct dw_mci_exynos_priv_data *priv = host->priv;
373
374         if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
375                 priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
376                 return SDMMC_CLKSEL_CCLK_SAMPLE(mci_readl(host, CLKSEL64));
377         else
378                 return SDMMC_CLKSEL_CCLK_SAMPLE(mci_readl(host, CLKSEL));
379 }
380
381 static inline void dw_mci_exynos_set_clksmpl(struct dw_mci *host, u8 sample)
382 {
383         u32 clksel;
384         struct dw_mci_exynos_priv_data *priv = host->priv;
385
386         if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
387                 priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
388                 clksel = mci_readl(host, CLKSEL64);
389         else
390                 clksel = mci_readl(host, CLKSEL);
391         clksel = SDMMC_CLKSEL_UP_SAMPLE(clksel, sample);
392         if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
393                 priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
394                 mci_writel(host, CLKSEL64, clksel);
395         else
396                 mci_writel(host, CLKSEL, clksel);
397 }
398
399 static inline u8 dw_mci_exynos_move_next_clksmpl(struct dw_mci *host)
400 {
401         struct dw_mci_exynos_priv_data *priv = host->priv;
402         u32 clksel;
403         u8 sample;
404
405         if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
406                 priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
407                 clksel = mci_readl(host, CLKSEL64);
408         else
409                 clksel = mci_readl(host, CLKSEL);
410
411         sample = (clksel + 1) & 0x7;
412         clksel = SDMMC_CLKSEL_UP_SAMPLE(clksel, sample);
413
414         if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
415                 priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
416                 mci_writel(host, CLKSEL64, clksel);
417         else
418                 mci_writel(host, CLKSEL, clksel);
419
420         return sample;
421 }
422
423 static s8 dw_mci_exynos_get_best_clksmpl(u8 candiates)
424 {
425         const u8 iter = 8;
426         u8 __c;
427         s8 i, loc = -1;
428
429         for (i = 0; i < iter; i++) {
430                 __c = ror8(candiates, i);
431                 if ((__c & 0xc7) == 0xc7) {
432                         loc = i;
433                         goto out;
434                 }
435         }
436
437         for (i = 0; i < iter; i++) {
438                 __c = ror8(candiates, i);
439                 if ((__c & 0x83) == 0x83) {
440                         loc = i;
441                         goto out;
442                 }
443         }
444
445 out:
446         return loc;
447 }
448
449 static int dw_mci_exynos_execute_tuning(struct dw_mci_slot *slot, u32 opcode)
450 {
451         struct dw_mci *host = slot->host;
452         struct dw_mci_exynos_priv_data *priv = host->priv;
453         struct mmc_host *mmc = slot->mmc;
454         u8 start_smpl, smpl, candiates = 0;
455         s8 found = -1;
456         int ret = 0;
457
458         start_smpl = dw_mci_exynos_get_clksmpl(host);
459
460         do {
461                 mci_writel(host, TMOUT, ~0);
462                 smpl = dw_mci_exynos_move_next_clksmpl(host);
463
464                 if (!mmc_send_tuning(mmc, opcode, NULL))
465                         candiates |= (1 << smpl);
466
467         } while (start_smpl != smpl);
468
469         found = dw_mci_exynos_get_best_clksmpl(candiates);
470         if (found >= 0) {
471                 dw_mci_exynos_set_clksmpl(host, found);
472                 priv->tuned_sample = found;
473         } else {
474                 ret = -EIO;
475         }
476
477         return ret;
478 }
479
480 static int dw_mci_exynos_prepare_hs400_tuning(struct dw_mci *host,
481                                         struct mmc_ios *ios)
482 {
483         struct dw_mci_exynos_priv_data *priv = host->priv;
484
485         dw_mci_exynos_set_clksel_timing(host, priv->hs400_timing);
486         dw_mci_exynos_adjust_clock(host, (ios->clock) << 1);
487
488         return 0;
489 }
490
491 /* Common capabilities of Exynos4/Exynos5 SoC */
492 static unsigned long exynos_dwmmc_caps[4] = {
493         MMC_CAP_1_8V_DDR | MMC_CAP_8_BIT_DATA | MMC_CAP_CMD23,
494         MMC_CAP_CMD23,
495         MMC_CAP_CMD23,
496         MMC_CAP_CMD23,
497 };
498
499 static const struct dw_mci_drv_data exynos_drv_data = {
500         .caps                   = exynos_dwmmc_caps,
501         .init                   = dw_mci_exynos_priv_init,
502         .setup_clock            = dw_mci_exynos_setup_clock,
503         .prepare_command        = dw_mci_exynos_prepare_command,
504         .set_ios                = dw_mci_exynos_set_ios,
505         .parse_dt               = dw_mci_exynos_parse_dt,
506         .execute_tuning         = dw_mci_exynos_execute_tuning,
507         .prepare_hs400_tuning   = dw_mci_exynos_prepare_hs400_tuning,
508 };
509
510 static const struct of_device_id dw_mci_exynos_match[] = {
511         { .compatible = "samsung,exynos4412-dw-mshc",
512                         .data = &exynos_drv_data, },
513         { .compatible = "samsung,exynos5250-dw-mshc",
514                         .data = &exynos_drv_data, },
515         { .compatible = "samsung,exynos5420-dw-mshc",
516                         .data = &exynos_drv_data, },
517         { .compatible = "samsung,exynos5420-dw-mshc-smu",
518                         .data = &exynos_drv_data, },
519         { .compatible = "samsung,exynos7-dw-mshc",
520                         .data = &exynos_drv_data, },
521         { .compatible = "samsung,exynos7-dw-mshc-smu",
522                         .data = &exynos_drv_data, },
523         {},
524 };
525 MODULE_DEVICE_TABLE(of, dw_mci_exynos_match);
526
527 static int dw_mci_exynos_probe(struct platform_device *pdev)
528 {
529         const struct dw_mci_drv_data *drv_data;
530         const struct of_device_id *match;
531
532         match = of_match_node(dw_mci_exynos_match, pdev->dev.of_node);
533         drv_data = match->data;
534         return dw_mci_pltfm_register(pdev, drv_data);
535 }
536
537 static const struct dev_pm_ops dw_mci_exynos_pmops = {
538         SET_SYSTEM_SLEEP_PM_OPS(dw_mci_exynos_suspend, dw_mci_exynos_resume)
539         .resume_noirq = dw_mci_exynos_resume_noirq,
540         .thaw_noirq = dw_mci_exynos_resume_noirq,
541         .restore_noirq = dw_mci_exynos_resume_noirq,
542 };
543
544 static struct platform_driver dw_mci_exynos_pltfm_driver = {
545         .probe          = dw_mci_exynos_probe,
546         .remove         = dw_mci_pltfm_remove,
547         .driver         = {
548                 .name           = "dwmmc_exynos",
549                 .of_match_table = dw_mci_exynos_match,
550                 .pm             = &dw_mci_exynos_pmops,
551         },
552 };
553
554 module_platform_driver(dw_mci_exynos_pltfm_driver);
555
556 MODULE_DESCRIPTION("Samsung Specific DW-MSHC Driver Extension");
557 MODULE_AUTHOR("Thomas Abraham <thomas.ab@samsung.com");
558 MODULE_LICENSE("GPL v2");
559 MODULE_ALIAS("platform:dwmmc_exynos");