Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.5.6.
  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 */
 23enum 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};
 32
 33/* Exynos implementation specific driver private data */
 34struct 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
 47static 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};
 74
 75static inline u8 dw_mci_exynos_get_ciu_div(struct dw_mci *host)
 76{
 77	struct dw_mci_exynos_priv_data *priv = host->priv;
 78
 79	if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4412)
 80		return EXYNOS4412_FIXED_CIU_CLK_DIV;
 81	else if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4210)
 82		return EXYNOS4210_FIXED_CIU_CLK_DIV;
 83	else if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
 84			priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
 85		return SDMMC_CLKSEL_GET_DIV(mci_readl(host, CLKSEL64)) + 1;
 86	else
 87		return SDMMC_CLKSEL_GET_DIV(mci_readl(host, CLKSEL)) + 1;
 88}
 89
 90static void dw_mci_exynos_config_smu(struct dw_mci *host)
 91{
 92	struct dw_mci_exynos_priv_data *priv = host->priv;
 93
 94	/*
 95	 * If Exynos is provided the Security management,
 96	 * set for non-ecryption mode at this time.
 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
109static int dw_mci_exynos_priv_init(struct dw_mci *host)
110{
111	struct dw_mci_exynos_priv_data *priv = host->priv;
112
113	dw_mci_exynos_config_smu(host);
114
115	if (priv->ctrl_type >= DW_MCI_TYPE_EXYNOS5420) {
116		priv->saved_strobe_ctrl = mci_readl(host, HS400_DLINE_CTRL);
117		priv->saved_dqs_en = mci_readl(host, HS400_DQS_EN);
118		priv->saved_dqs_en |= AXI_NON_BLOCKING_WR;
119		mci_writel(host, HS400_DQS_EN, priv->saved_dqs_en);
120		if (!priv->dqs_delay)
121			priv->dqs_delay =
122				DQS_CTRL_GET_RD_DELAY(priv->saved_strobe_ctrl);
123	}
124
125	host->bus_hz /= (priv->ciu_div + 1);
126
127	return 0;
128}
129
130static 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) && host->slot)
157		set_bit(DW_MMC_CARD_NO_USE_HOLD, &host->slot->flags);
158}
159
160#ifdef CONFIG_PM
161static int dw_mci_exynos_runtime_resume(struct device *dev)
162{
163	struct dw_mci *host = dev_get_drvdata(dev);
164	int ret;
165
166	ret = dw_mci_runtime_resume(dev);
167	if (ret)
168		return ret;
169
170	dw_mci_exynos_config_smu(host);
171
172	return ret;
173}
174#endif /* CONFIG_PM */
175
176#ifdef CONFIG_PM_SLEEP
177/**
178 * dw_mci_exynos_suspend_noirq - Exynos-specific suspend code
179 *
180 * This ensures that device will be in runtime active state in
181 * dw_mci_exynos_resume_noirq after calling pm_runtime_force_resume()
182 */
183static int dw_mci_exynos_suspend_noirq(struct device *dev)
184{
185	pm_runtime_get_noresume(dev);
186	return pm_runtime_force_suspend(dev);
187}
188
189/**
190 * dw_mci_exynos_resume_noirq - Exynos-specific resume code
191 *
192 * On exynos5420 there is a silicon errata that will sometimes leave the
193 * WAKEUP_INT bit in the CLKSEL register asserted.  This bit is 1 to indicate
194 * that it fired and we can clear it by writing a 1 back.  Clear it to prevent
195 * interrupts from going off constantly.
196 *
197 * We run this code on all exynos variants because it doesn't hurt.
198 */
199static int dw_mci_exynos_resume_noirq(struct device *dev)
200{
201	struct dw_mci *host = dev_get_drvdata(dev);
202	struct dw_mci_exynos_priv_data *priv = host->priv;
203	u32 clksel;
204	int ret;
205
206	ret = pm_runtime_force_resume(dev);
207	if (ret)
208		return ret;
209
210	if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
211		priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
212		clksel = mci_readl(host, CLKSEL64);
213	else
214		clksel = mci_readl(host, CLKSEL);
215
216	if (clksel & SDMMC_CLKSEL_WAKEUP_INT) {
217		if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
218			priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
219			mci_writel(host, CLKSEL64, clksel);
220		else
221			mci_writel(host, CLKSEL, clksel);
222	}
223
224	pm_runtime_put(dev);
225
226	return 0;
227}
228#endif /* CONFIG_PM_SLEEP */
229
230static void dw_mci_exynos_config_hs400(struct dw_mci *host, u32 timing)
231{
232	struct dw_mci_exynos_priv_data *priv = host->priv;
233	u32 dqs, strobe;
234
235	/*
236	 * Not supported to configure register
237	 * related to HS400
238	 */
239	if (priv->ctrl_type < DW_MCI_TYPE_EXYNOS5420) {
240		if (timing == MMC_TIMING_MMC_HS400)
241			dev_warn(host->dev,
242				 "cannot configure HS400, unsupported chipset\n");
243		return;
244	}
245
246	dqs = priv->saved_dqs_en;
247	strobe = priv->saved_strobe_ctrl;
248
249	if (timing == MMC_TIMING_MMC_HS400) {
250		dqs |= DATA_STROBE_EN;
251		strobe = DQS_CTRL_RD_DELAY(strobe, priv->dqs_delay);
252	} else if (timing == MMC_TIMING_UHS_SDR104) {
253		dqs &= 0xffffff00;
254	} else {
255		dqs &= ~DATA_STROBE_EN;
256	}
257
258	mci_writel(host, HS400_DQS_EN, dqs);
259	mci_writel(host, HS400_DLINE_CTRL, strobe);
260}
261
262static void dw_mci_exynos_adjust_clock(struct dw_mci *host, unsigned int wanted)
263{
264	struct dw_mci_exynos_priv_data *priv = host->priv;
265	unsigned long actual;
266	u8 div;
267	int ret;
268	/*
269	 * Don't care if wanted clock is zero or
270	 * ciu clock is unavailable
271	 */
272	if (!wanted || IS_ERR(host->ciu_clk))
273		return;
274
275	/* Guaranteed minimum frequency for cclkin */
276	if (wanted < EXYNOS_CCLKIN_MIN)
277		wanted = EXYNOS_CCLKIN_MIN;
278
279	if (wanted == priv->cur_speed)
280		return;
281
282	div = dw_mci_exynos_get_ciu_div(host);
283	ret = clk_set_rate(host->ciu_clk, wanted * div);
284	if (ret)
285		dev_warn(host->dev,
286			"failed to set clk-rate %u error: %d\n",
287			wanted * div, ret);
288	actual = clk_get_rate(host->ciu_clk);
289	host->bus_hz = actual / div;
290	priv->cur_speed = wanted;
291	host->current_speed = 0;
292}
293
294static void dw_mci_exynos_set_ios(struct dw_mci *host, struct mmc_ios *ios)
295{
296	struct dw_mci_exynos_priv_data *priv = host->priv;
297	unsigned int wanted = ios->clock;
298	u32 timing = ios->timing, clksel;
299
300	switch (timing) {
301	case MMC_TIMING_MMC_HS400:
302		/* Update tuned sample timing */
303		clksel = SDMMC_CLKSEL_UP_SAMPLE(
304				priv->hs400_timing, priv->tuned_sample);
305		wanted <<= 1;
306		break;
307	case MMC_TIMING_MMC_DDR52:
308		clksel = priv->ddr_timing;
309		/* Should be double rate for DDR mode */
310		if (ios->bus_width == MMC_BUS_WIDTH_8)
311			wanted <<= 1;
312		break;
313	case MMC_TIMING_UHS_SDR104:
314	case MMC_TIMING_UHS_SDR50:
315		clksel = (priv->sdr_timing & 0xfff8ffff) |
316			(priv->ciu_div << 16);
317		break;
318	case MMC_TIMING_UHS_DDR50:
319		clksel = (priv->ddr_timing & 0xfff8ffff) |
320			(priv->ciu_div << 16);
321		break;
322	default:
323		clksel = priv->sdr_timing;
324	}
325
326	/* Set clock timing for the requested speed mode*/
327	dw_mci_exynos_set_clksel_timing(host, clksel);
328
329	/* Configure setting for HS400 */
330	dw_mci_exynos_config_hs400(host, timing);
331
332	/* Configure clock rate */
333	dw_mci_exynos_adjust_clock(host, wanted);
334}
335
336static int dw_mci_exynos_parse_dt(struct dw_mci *host)
337{
338	struct dw_mci_exynos_priv_data *priv;
339	struct device_node *np = host->dev->of_node;
340	u32 timing[2];
341	u32 div = 0;
342	int idx;
343	int ret;
344
345	priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL);
346	if (!priv)
347		return -ENOMEM;
348
349	for (idx = 0; idx < ARRAY_SIZE(exynos_compat); idx++) {
350		if (of_device_is_compatible(np, exynos_compat[idx].compatible))
351			priv->ctrl_type = exynos_compat[idx].ctrl_type;
352	}
353
354	if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4412)
355		priv->ciu_div = EXYNOS4412_FIXED_CIU_CLK_DIV - 1;
356	else if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4210)
357		priv->ciu_div = EXYNOS4210_FIXED_CIU_CLK_DIV - 1;
358	else {
359		of_property_read_u32(np, "samsung,dw-mshc-ciu-div", &div);
360		priv->ciu_div = div;
361	}
362
363	ret = of_property_read_u32_array(np,
364			"samsung,dw-mshc-sdr-timing", timing, 2);
365	if (ret)
366		return ret;
367
368	priv->sdr_timing = SDMMC_CLKSEL_TIMING(timing[0], timing[1], div);
369
370	ret = of_property_read_u32_array(np,
371			"samsung,dw-mshc-ddr-timing", timing, 2);
372	if (ret)
373		return ret;
374
375	priv->ddr_timing = SDMMC_CLKSEL_TIMING(timing[0], timing[1], div);
376
377	ret = of_property_read_u32_array(np,
378			"samsung,dw-mshc-hs400-timing", timing, 2);
379	if (!ret && of_property_read_u32(np,
380				"samsung,read-strobe-delay", &priv->dqs_delay))
381		dev_dbg(host->dev,
382			"read-strobe-delay is not found, assuming usage of default value\n");
383
384	priv->hs400_timing = SDMMC_CLKSEL_TIMING(timing[0], timing[1],
385						HS400_FIXED_CIU_CLK_DIV);
386	host->priv = priv;
387	return 0;
388}
389
390static inline u8 dw_mci_exynos_get_clksmpl(struct dw_mci *host)
391{
392	struct dw_mci_exynos_priv_data *priv = host->priv;
393
394	if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
395		priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
396		return SDMMC_CLKSEL_CCLK_SAMPLE(mci_readl(host, CLKSEL64));
397	else
398		return SDMMC_CLKSEL_CCLK_SAMPLE(mci_readl(host, CLKSEL));
399}
400
401static inline void dw_mci_exynos_set_clksmpl(struct dw_mci *host, u8 sample)
402{
403	u32 clksel;
404	struct dw_mci_exynos_priv_data *priv = host->priv;
405
406	if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
407		priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
408		clksel = mci_readl(host, CLKSEL64);
409	else
410		clksel = mci_readl(host, CLKSEL);
411	clksel = SDMMC_CLKSEL_UP_SAMPLE(clksel, sample);
412	if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
413		priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
414		mci_writel(host, CLKSEL64, clksel);
415	else
416		mci_writel(host, CLKSEL, clksel);
417}
418
419static inline u8 dw_mci_exynos_move_next_clksmpl(struct dw_mci *host)
420{
421	struct dw_mci_exynos_priv_data *priv = host->priv;
422	u32 clksel;
423	u8 sample;
424
425	if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
426		priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
427		clksel = mci_readl(host, CLKSEL64);
428	else
429		clksel = mci_readl(host, CLKSEL);
430
431	sample = (clksel + 1) & 0x7;
432	clksel = SDMMC_CLKSEL_UP_SAMPLE(clksel, sample);
433
434	if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
435		priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
436		mci_writel(host, CLKSEL64, clksel);
437	else
438		mci_writel(host, CLKSEL, clksel);
439
440	return sample;
441}
442
443static s8 dw_mci_exynos_get_best_clksmpl(u8 candiates)
444{
445	const u8 iter = 8;
446	u8 __c;
447	s8 i, loc = -1;
448
449	for (i = 0; i < iter; i++) {
450		__c = ror8(candiates, i);
451		if ((__c & 0xc7) == 0xc7) {
452			loc = i;
453			goto out;
454		}
455	}
456
457	for (i = 0; i < iter; i++) {
458		__c = ror8(candiates, i);
459		if ((__c & 0x83) == 0x83) {
460			loc = i;
461			goto out;
462		}
463	}
464
465out:
466	return loc;
467}
468
469static int dw_mci_exynos_execute_tuning(struct dw_mci_slot *slot, u32 opcode)
470{
471	struct dw_mci *host = slot->host;
472	struct dw_mci_exynos_priv_data *priv = host->priv;
473	struct mmc_host *mmc = slot->mmc;
474	u8 start_smpl, smpl, candiates = 0;
475	s8 found = -1;
476	int ret = 0;
477
478	start_smpl = dw_mci_exynos_get_clksmpl(host);
479
480	do {
481		mci_writel(host, TMOUT, ~0);
482		smpl = dw_mci_exynos_move_next_clksmpl(host);
483
484		if (!mmc_send_tuning(mmc, opcode, NULL))
485			candiates |= (1 << smpl);
486
487	} while (start_smpl != smpl);
488
489	found = dw_mci_exynos_get_best_clksmpl(candiates);
490	if (found >= 0) {
491		dw_mci_exynos_set_clksmpl(host, found);
492		priv->tuned_sample = found;
493	} else {
494		ret = -EIO;
495	}
496
497	return ret;
498}
499
500static int dw_mci_exynos_prepare_hs400_tuning(struct dw_mci *host,
501					struct mmc_ios *ios)
502{
503	struct dw_mci_exynos_priv_data *priv = host->priv;
504
505	dw_mci_exynos_set_clksel_timing(host, priv->hs400_timing);
506	dw_mci_exynos_adjust_clock(host, (ios->clock) << 1);
507
508	return 0;
509}
510
511/* Common capabilities of Exynos4/Exynos5 SoC */
512static unsigned long exynos_dwmmc_caps[4] = {
513	MMC_CAP_1_8V_DDR | MMC_CAP_8_BIT_DATA | MMC_CAP_CMD23,
514	MMC_CAP_CMD23,
515	MMC_CAP_CMD23,
516	MMC_CAP_CMD23,
517};
518
519static const struct dw_mci_drv_data exynos_drv_data = {
520	.caps			= exynos_dwmmc_caps,
521	.num_caps		= ARRAY_SIZE(exynos_dwmmc_caps),
522	.init			= dw_mci_exynos_priv_init,
523	.set_ios		= dw_mci_exynos_set_ios,
524	.parse_dt		= dw_mci_exynos_parse_dt,
525	.execute_tuning		= dw_mci_exynos_execute_tuning,
526	.prepare_hs400_tuning	= dw_mci_exynos_prepare_hs400_tuning,
527};
528
529static const struct of_device_id dw_mci_exynos_match[] = {
530	{ .compatible = "samsung,exynos4412-dw-mshc",
531			.data = &exynos_drv_data, },
532	{ .compatible = "samsung,exynos5250-dw-mshc",
533			.data = &exynos_drv_data, },
534	{ .compatible = "samsung,exynos5420-dw-mshc",
535			.data = &exynos_drv_data, },
536	{ .compatible = "samsung,exynos5420-dw-mshc-smu",
537			.data = &exynos_drv_data, },
538	{ .compatible = "samsung,exynos7-dw-mshc",
539			.data = &exynos_drv_data, },
540	{ .compatible = "samsung,exynos7-dw-mshc-smu",
541			.data = &exynos_drv_data, },
542	{},
543};
544MODULE_DEVICE_TABLE(of, dw_mci_exynos_match);
545
546static int dw_mci_exynos_probe(struct platform_device *pdev)
547{
548	const struct dw_mci_drv_data *drv_data;
549	const struct of_device_id *match;
550	int ret;
551
552	match = of_match_node(dw_mci_exynos_match, pdev->dev.of_node);
553	drv_data = match->data;
554
555	pm_runtime_get_noresume(&pdev->dev);
556	pm_runtime_set_active(&pdev->dev);
557	pm_runtime_enable(&pdev->dev);
558
559	ret = dw_mci_pltfm_register(pdev, drv_data);
560	if (ret) {
561		pm_runtime_disable(&pdev->dev);
562		pm_runtime_set_suspended(&pdev->dev);
563		pm_runtime_put_noidle(&pdev->dev);
564
565		return ret;
566	}
567
568	return 0;
569}
570
571static int dw_mci_exynos_remove(struct platform_device *pdev)
572{
573	pm_runtime_disable(&pdev->dev);
574	pm_runtime_set_suspended(&pdev->dev);
575	pm_runtime_put_noidle(&pdev->dev);
576
577	return dw_mci_pltfm_remove(pdev);
578}
579
580static const struct dev_pm_ops dw_mci_exynos_pmops = {
581	SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(dw_mci_exynos_suspend_noirq,
582				      dw_mci_exynos_resume_noirq)
583	SET_RUNTIME_PM_OPS(dw_mci_runtime_suspend,
584			   dw_mci_exynos_runtime_resume,
585			   NULL)
586};
587
588static struct platform_driver dw_mci_exynos_pltfm_driver = {
589	.probe		= dw_mci_exynos_probe,
590	.remove		= dw_mci_exynos_remove,
591	.driver		= {
592		.name		= "dwmmc_exynos",
593		.of_match_table	= dw_mci_exynos_match,
594		.pm		= &dw_mci_exynos_pmops,
595	},
596};
597
598module_platform_driver(dw_mci_exynos_pltfm_driver);
599
600MODULE_DESCRIPTION("Samsung Specific DW-MSHC Driver Extension");
601MODULE_AUTHOR("Thomas Abraham <thomas.ab@samsung.com");
602MODULE_LICENSE("GPL v2");
603MODULE_ALIAS("platform:dwmmc_exynos");