cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

dw_mmc-exynos.c (18684B)


      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	DW_MCI_TYPE_ARTPEC8,
     32};
     33
     34/* Exynos implementation specific driver private data */
     35struct 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
     48static 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
     79static 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
     95static 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
    114static 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
    140static 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
    173static 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 */
    196static 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 */
    213static 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
    246static 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
    279static 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
    311static 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
    353static 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
    407static 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
    419static 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
    439static 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
    465static 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	}
    499out:
    500	return loc;
    501}
    502
    503static 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
    536static 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
    547static 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
    585static 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 */
    595static 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
    602static 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
    613static 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
    623static 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};
    640MODULE_DEVICE_TABLE(of, dw_mci_exynos_match);
    641
    642static 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
    667static 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
    676static 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
    684static 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
    695module_platform_driver(dw_mci_exynos_pltfm_driver);
    696
    697MODULE_DESCRIPTION("Samsung Specific DW-MSHC Driver Extension");
    698MODULE_AUTHOR("Thomas Abraham <thomas.ab@samsung.com");
    699MODULE_LICENSE("GPL v2");
    700MODULE_ALIAS("platform:dwmmc_exynos");