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