xref: /OK3568_Linux_fs/kernel/drivers/mmc/host/sdhci-xenon.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * Driver for Marvell Xenon SDHC as a platform device
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Copyright (C) 2016 Marvell, All Rights Reserved.
6*4882a593Smuzhiyun  *
7*4882a593Smuzhiyun  * Author:	Hu Ziji <huziji@marvell.com>
8*4882a593Smuzhiyun  * Date:	2016-8-24
9*4882a593Smuzhiyun  *
10*4882a593Smuzhiyun  * Inspired by Jisheng Zhang <jszhang@marvell.com>
11*4882a593Smuzhiyun  * Special thanks to Video BG4 project team.
12*4882a593Smuzhiyun  */
13*4882a593Smuzhiyun 
14*4882a593Smuzhiyun #include <linux/delay.h>
15*4882a593Smuzhiyun #include <linux/ktime.h>
16*4882a593Smuzhiyun #include <linux/module.h>
17*4882a593Smuzhiyun #include <linux/of.h>
18*4882a593Smuzhiyun #include <linux/pm.h>
19*4882a593Smuzhiyun #include <linux/pm_runtime.h>
20*4882a593Smuzhiyun 
21*4882a593Smuzhiyun #include "sdhci-pltfm.h"
22*4882a593Smuzhiyun #include "sdhci-xenon.h"
23*4882a593Smuzhiyun 
xenon_enable_internal_clk(struct sdhci_host * host)24*4882a593Smuzhiyun static int xenon_enable_internal_clk(struct sdhci_host *host)
25*4882a593Smuzhiyun {
26*4882a593Smuzhiyun 	u32 reg;
27*4882a593Smuzhiyun 	ktime_t timeout;
28*4882a593Smuzhiyun 
29*4882a593Smuzhiyun 	reg = sdhci_readl(host, SDHCI_CLOCK_CONTROL);
30*4882a593Smuzhiyun 	reg |= SDHCI_CLOCK_INT_EN;
31*4882a593Smuzhiyun 	sdhci_writel(host, reg, SDHCI_CLOCK_CONTROL);
32*4882a593Smuzhiyun 	/* Wait max 20 ms */
33*4882a593Smuzhiyun 	timeout = ktime_add_ms(ktime_get(), 20);
34*4882a593Smuzhiyun 	while (1) {
35*4882a593Smuzhiyun 		bool timedout = ktime_after(ktime_get(), timeout);
36*4882a593Smuzhiyun 
37*4882a593Smuzhiyun 		reg = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
38*4882a593Smuzhiyun 		if (reg & SDHCI_CLOCK_INT_STABLE)
39*4882a593Smuzhiyun 			break;
40*4882a593Smuzhiyun 		if (timedout) {
41*4882a593Smuzhiyun 			dev_err(mmc_dev(host->mmc), "Internal clock never stabilised.\n");
42*4882a593Smuzhiyun 			return -ETIMEDOUT;
43*4882a593Smuzhiyun 		}
44*4882a593Smuzhiyun 		usleep_range(900, 1100);
45*4882a593Smuzhiyun 	}
46*4882a593Smuzhiyun 
47*4882a593Smuzhiyun 	return 0;
48*4882a593Smuzhiyun }
49*4882a593Smuzhiyun 
50*4882a593Smuzhiyun /* Set SDCLK-off-while-idle */
xenon_set_sdclk_off_idle(struct sdhci_host * host,unsigned char sdhc_id,bool enable)51*4882a593Smuzhiyun static void xenon_set_sdclk_off_idle(struct sdhci_host *host,
52*4882a593Smuzhiyun 				     unsigned char sdhc_id, bool enable)
53*4882a593Smuzhiyun {
54*4882a593Smuzhiyun 	u32 reg;
55*4882a593Smuzhiyun 	u32 mask;
56*4882a593Smuzhiyun 
57*4882a593Smuzhiyun 	reg = sdhci_readl(host, XENON_SYS_OP_CTRL);
58*4882a593Smuzhiyun 	/* Get the bit shift basing on the SDHC index */
59*4882a593Smuzhiyun 	mask = (0x1 << (XENON_SDCLK_IDLEOFF_ENABLE_SHIFT + sdhc_id));
60*4882a593Smuzhiyun 	if (enable)
61*4882a593Smuzhiyun 		reg |= mask;
62*4882a593Smuzhiyun 	else
63*4882a593Smuzhiyun 		reg &= ~mask;
64*4882a593Smuzhiyun 
65*4882a593Smuzhiyun 	sdhci_writel(host, reg, XENON_SYS_OP_CTRL);
66*4882a593Smuzhiyun }
67*4882a593Smuzhiyun 
68*4882a593Smuzhiyun /* Enable/Disable the Auto Clock Gating function */
xenon_set_acg(struct sdhci_host * host,bool enable)69*4882a593Smuzhiyun static void xenon_set_acg(struct sdhci_host *host, bool enable)
70*4882a593Smuzhiyun {
71*4882a593Smuzhiyun 	u32 reg;
72*4882a593Smuzhiyun 
73*4882a593Smuzhiyun 	reg = sdhci_readl(host, XENON_SYS_OP_CTRL);
74*4882a593Smuzhiyun 	if (enable)
75*4882a593Smuzhiyun 		reg &= ~XENON_AUTO_CLKGATE_DISABLE_MASK;
76*4882a593Smuzhiyun 	else
77*4882a593Smuzhiyun 		reg |= XENON_AUTO_CLKGATE_DISABLE_MASK;
78*4882a593Smuzhiyun 	sdhci_writel(host, reg, XENON_SYS_OP_CTRL);
79*4882a593Smuzhiyun }
80*4882a593Smuzhiyun 
81*4882a593Smuzhiyun /* Enable this SDHC */
xenon_enable_sdhc(struct sdhci_host * host,unsigned char sdhc_id)82*4882a593Smuzhiyun static void xenon_enable_sdhc(struct sdhci_host *host,
83*4882a593Smuzhiyun 			      unsigned char sdhc_id)
84*4882a593Smuzhiyun {
85*4882a593Smuzhiyun 	u32 reg;
86*4882a593Smuzhiyun 
87*4882a593Smuzhiyun 	reg = sdhci_readl(host, XENON_SYS_OP_CTRL);
88*4882a593Smuzhiyun 	reg |= (BIT(sdhc_id) << XENON_SLOT_ENABLE_SHIFT);
89*4882a593Smuzhiyun 	sdhci_writel(host, reg, XENON_SYS_OP_CTRL);
90*4882a593Smuzhiyun 
91*4882a593Smuzhiyun 	host->mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY;
92*4882a593Smuzhiyun 	/*
93*4882a593Smuzhiyun 	 * Force to clear BUS_TEST to
94*4882a593Smuzhiyun 	 * skip bus_test_pre and bus_test_post
95*4882a593Smuzhiyun 	 */
96*4882a593Smuzhiyun 	host->mmc->caps &= ~MMC_CAP_BUS_WIDTH_TEST;
97*4882a593Smuzhiyun }
98*4882a593Smuzhiyun 
99*4882a593Smuzhiyun /* Disable this SDHC */
xenon_disable_sdhc(struct sdhci_host * host,unsigned char sdhc_id)100*4882a593Smuzhiyun static void xenon_disable_sdhc(struct sdhci_host *host,
101*4882a593Smuzhiyun 			       unsigned char sdhc_id)
102*4882a593Smuzhiyun {
103*4882a593Smuzhiyun 	u32 reg;
104*4882a593Smuzhiyun 
105*4882a593Smuzhiyun 	reg = sdhci_readl(host, XENON_SYS_OP_CTRL);
106*4882a593Smuzhiyun 	reg &= ~(BIT(sdhc_id) << XENON_SLOT_ENABLE_SHIFT);
107*4882a593Smuzhiyun 	sdhci_writel(host, reg, XENON_SYS_OP_CTRL);
108*4882a593Smuzhiyun }
109*4882a593Smuzhiyun 
110*4882a593Smuzhiyun /* Enable Parallel Transfer Mode */
xenon_enable_sdhc_parallel_tran(struct sdhci_host * host,unsigned char sdhc_id)111*4882a593Smuzhiyun static void xenon_enable_sdhc_parallel_tran(struct sdhci_host *host,
112*4882a593Smuzhiyun 					    unsigned char sdhc_id)
113*4882a593Smuzhiyun {
114*4882a593Smuzhiyun 	u32 reg;
115*4882a593Smuzhiyun 
116*4882a593Smuzhiyun 	reg = sdhci_readl(host, XENON_SYS_EXT_OP_CTRL);
117*4882a593Smuzhiyun 	reg |= BIT(sdhc_id);
118*4882a593Smuzhiyun 	sdhci_writel(host, reg, XENON_SYS_EXT_OP_CTRL);
119*4882a593Smuzhiyun }
120*4882a593Smuzhiyun 
121*4882a593Smuzhiyun /* Mask command conflict error */
xenon_mask_cmd_conflict_err(struct sdhci_host * host)122*4882a593Smuzhiyun static void xenon_mask_cmd_conflict_err(struct sdhci_host *host)
123*4882a593Smuzhiyun {
124*4882a593Smuzhiyun 	u32  reg;
125*4882a593Smuzhiyun 
126*4882a593Smuzhiyun 	reg = sdhci_readl(host, XENON_SYS_EXT_OP_CTRL);
127*4882a593Smuzhiyun 	reg |= XENON_MASK_CMD_CONFLICT_ERR;
128*4882a593Smuzhiyun 	sdhci_writel(host, reg, XENON_SYS_EXT_OP_CTRL);
129*4882a593Smuzhiyun }
130*4882a593Smuzhiyun 
xenon_retune_setup(struct sdhci_host * host)131*4882a593Smuzhiyun static void xenon_retune_setup(struct sdhci_host *host)
132*4882a593Smuzhiyun {
133*4882a593Smuzhiyun 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
134*4882a593Smuzhiyun 	struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
135*4882a593Smuzhiyun 	u32 reg;
136*4882a593Smuzhiyun 
137*4882a593Smuzhiyun 	/* Disable the Re-Tuning Request functionality */
138*4882a593Smuzhiyun 	reg = sdhci_readl(host, XENON_SLOT_RETUNING_REQ_CTRL);
139*4882a593Smuzhiyun 	reg &= ~XENON_RETUNING_COMPATIBLE;
140*4882a593Smuzhiyun 	sdhci_writel(host, reg, XENON_SLOT_RETUNING_REQ_CTRL);
141*4882a593Smuzhiyun 
142*4882a593Smuzhiyun 	/* Disable the Re-tuning Interrupt */
143*4882a593Smuzhiyun 	reg = sdhci_readl(host, SDHCI_SIGNAL_ENABLE);
144*4882a593Smuzhiyun 	reg &= ~SDHCI_INT_RETUNE;
145*4882a593Smuzhiyun 	sdhci_writel(host, reg, SDHCI_SIGNAL_ENABLE);
146*4882a593Smuzhiyun 	reg = sdhci_readl(host, SDHCI_INT_ENABLE);
147*4882a593Smuzhiyun 	reg &= ~SDHCI_INT_RETUNE;
148*4882a593Smuzhiyun 	sdhci_writel(host, reg, SDHCI_INT_ENABLE);
149*4882a593Smuzhiyun 
150*4882a593Smuzhiyun 	/* Force to use Tuning Mode 1 */
151*4882a593Smuzhiyun 	host->tuning_mode = SDHCI_TUNING_MODE_1;
152*4882a593Smuzhiyun 	/* Set re-tuning period */
153*4882a593Smuzhiyun 	host->tuning_count = 1 << (priv->tuning_count - 1);
154*4882a593Smuzhiyun }
155*4882a593Smuzhiyun 
156*4882a593Smuzhiyun /*
157*4882a593Smuzhiyun  * Operations inside struct sdhci_ops
158*4882a593Smuzhiyun  */
159*4882a593Smuzhiyun /* Recover the Register Setting cleared during SOFTWARE_RESET_ALL */
xenon_reset_exit(struct sdhci_host * host,unsigned char sdhc_id,u8 mask)160*4882a593Smuzhiyun static void xenon_reset_exit(struct sdhci_host *host,
161*4882a593Smuzhiyun 			     unsigned char sdhc_id, u8 mask)
162*4882a593Smuzhiyun {
163*4882a593Smuzhiyun 	/* Only SOFTWARE RESET ALL will clear the register setting */
164*4882a593Smuzhiyun 	if (!(mask & SDHCI_RESET_ALL))
165*4882a593Smuzhiyun 		return;
166*4882a593Smuzhiyun 
167*4882a593Smuzhiyun 	/* Disable tuning request and auto-retuning again */
168*4882a593Smuzhiyun 	xenon_retune_setup(host);
169*4882a593Smuzhiyun 
170*4882a593Smuzhiyun 	/*
171*4882a593Smuzhiyun 	 * The ACG should be turned off at the early init time, in order
172*4882a593Smuzhiyun 	 * to solve a possible issues with the 1.8V regulator stabilization.
173*4882a593Smuzhiyun 	 * The feature is enabled in later stage.
174*4882a593Smuzhiyun 	 */
175*4882a593Smuzhiyun 	xenon_set_acg(host, false);
176*4882a593Smuzhiyun 
177*4882a593Smuzhiyun 	xenon_set_sdclk_off_idle(host, sdhc_id, false);
178*4882a593Smuzhiyun 
179*4882a593Smuzhiyun 	xenon_mask_cmd_conflict_err(host);
180*4882a593Smuzhiyun }
181*4882a593Smuzhiyun 
xenon_reset(struct sdhci_host * host,u8 mask)182*4882a593Smuzhiyun static void xenon_reset(struct sdhci_host *host, u8 mask)
183*4882a593Smuzhiyun {
184*4882a593Smuzhiyun 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
185*4882a593Smuzhiyun 	struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
186*4882a593Smuzhiyun 
187*4882a593Smuzhiyun 	sdhci_reset(host, mask);
188*4882a593Smuzhiyun 	xenon_reset_exit(host, priv->sdhc_id, mask);
189*4882a593Smuzhiyun }
190*4882a593Smuzhiyun 
191*4882a593Smuzhiyun /*
192*4882a593Smuzhiyun  * Xenon defines different values for HS200 and HS400
193*4882a593Smuzhiyun  * in Host_Control_2
194*4882a593Smuzhiyun  */
xenon_set_uhs_signaling(struct sdhci_host * host,unsigned int timing)195*4882a593Smuzhiyun static void xenon_set_uhs_signaling(struct sdhci_host *host,
196*4882a593Smuzhiyun 				    unsigned int timing)
197*4882a593Smuzhiyun {
198*4882a593Smuzhiyun 	u16 ctrl_2;
199*4882a593Smuzhiyun 
200*4882a593Smuzhiyun 	ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
201*4882a593Smuzhiyun 	/* Select Bus Speed Mode for host */
202*4882a593Smuzhiyun 	ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
203*4882a593Smuzhiyun 	if (timing == MMC_TIMING_MMC_HS200)
204*4882a593Smuzhiyun 		ctrl_2 |= XENON_CTRL_HS200;
205*4882a593Smuzhiyun 	else if (timing == MMC_TIMING_UHS_SDR104)
206*4882a593Smuzhiyun 		ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
207*4882a593Smuzhiyun 	else if (timing == MMC_TIMING_UHS_SDR12)
208*4882a593Smuzhiyun 		ctrl_2 |= SDHCI_CTRL_UHS_SDR12;
209*4882a593Smuzhiyun 	else if (timing == MMC_TIMING_UHS_SDR25)
210*4882a593Smuzhiyun 		ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
211*4882a593Smuzhiyun 	else if (timing == MMC_TIMING_UHS_SDR50)
212*4882a593Smuzhiyun 		ctrl_2 |= SDHCI_CTRL_UHS_SDR50;
213*4882a593Smuzhiyun 	else if ((timing == MMC_TIMING_UHS_DDR50) ||
214*4882a593Smuzhiyun 		 (timing == MMC_TIMING_MMC_DDR52))
215*4882a593Smuzhiyun 		ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
216*4882a593Smuzhiyun 	else if (timing == MMC_TIMING_MMC_HS400)
217*4882a593Smuzhiyun 		ctrl_2 |= XENON_CTRL_HS400;
218*4882a593Smuzhiyun 	sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
219*4882a593Smuzhiyun }
220*4882a593Smuzhiyun 
xenon_set_power(struct sdhci_host * host,unsigned char mode,unsigned short vdd)221*4882a593Smuzhiyun static void xenon_set_power(struct sdhci_host *host, unsigned char mode,
222*4882a593Smuzhiyun 		unsigned short vdd)
223*4882a593Smuzhiyun {
224*4882a593Smuzhiyun 	struct mmc_host *mmc = host->mmc;
225*4882a593Smuzhiyun 	u8 pwr = host->pwr;
226*4882a593Smuzhiyun 
227*4882a593Smuzhiyun 	sdhci_set_power_noreg(host, mode, vdd);
228*4882a593Smuzhiyun 
229*4882a593Smuzhiyun 	if (host->pwr == pwr)
230*4882a593Smuzhiyun 		return;
231*4882a593Smuzhiyun 
232*4882a593Smuzhiyun 	if (host->pwr == 0)
233*4882a593Smuzhiyun 		vdd = 0;
234*4882a593Smuzhiyun 
235*4882a593Smuzhiyun 	if (!IS_ERR(mmc->supply.vmmc))
236*4882a593Smuzhiyun 		mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, vdd);
237*4882a593Smuzhiyun }
238*4882a593Smuzhiyun 
xenon_voltage_switch(struct sdhci_host * host)239*4882a593Smuzhiyun static void xenon_voltage_switch(struct sdhci_host *host)
240*4882a593Smuzhiyun {
241*4882a593Smuzhiyun 	/* Wait for 5ms after set 1.8V signal enable bit */
242*4882a593Smuzhiyun 	usleep_range(5000, 5500);
243*4882a593Smuzhiyun }
244*4882a593Smuzhiyun 
245*4882a593Smuzhiyun static const struct sdhci_ops sdhci_xenon_ops = {
246*4882a593Smuzhiyun 	.voltage_switch		= xenon_voltage_switch,
247*4882a593Smuzhiyun 	.set_clock		= sdhci_set_clock,
248*4882a593Smuzhiyun 	.set_power		= xenon_set_power,
249*4882a593Smuzhiyun 	.set_bus_width		= sdhci_set_bus_width,
250*4882a593Smuzhiyun 	.reset			= xenon_reset,
251*4882a593Smuzhiyun 	.set_uhs_signaling	= xenon_set_uhs_signaling,
252*4882a593Smuzhiyun 	.get_max_clock		= sdhci_pltfm_clk_get_max_clock,
253*4882a593Smuzhiyun };
254*4882a593Smuzhiyun 
255*4882a593Smuzhiyun static const struct sdhci_pltfm_data sdhci_xenon_pdata = {
256*4882a593Smuzhiyun 	.ops = &sdhci_xenon_ops,
257*4882a593Smuzhiyun 	.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC |
258*4882a593Smuzhiyun 		  SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER |
259*4882a593Smuzhiyun 		  SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
260*4882a593Smuzhiyun };
261*4882a593Smuzhiyun 
262*4882a593Smuzhiyun /*
263*4882a593Smuzhiyun  * Xenon Specific Operations in mmc_host_ops
264*4882a593Smuzhiyun  */
xenon_set_ios(struct mmc_host * mmc,struct mmc_ios * ios)265*4882a593Smuzhiyun static void xenon_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
266*4882a593Smuzhiyun {
267*4882a593Smuzhiyun 	struct sdhci_host *host = mmc_priv(mmc);
268*4882a593Smuzhiyun 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
269*4882a593Smuzhiyun 	struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
270*4882a593Smuzhiyun 	u32 reg;
271*4882a593Smuzhiyun 
272*4882a593Smuzhiyun 	/*
273*4882a593Smuzhiyun 	 * HS400/HS200/eMMC HS doesn't have Preset Value register.
274*4882a593Smuzhiyun 	 * However, sdhci_set_ios will read HS400/HS200 Preset register.
275*4882a593Smuzhiyun 	 * Disable Preset Value register for HS400/HS200.
276*4882a593Smuzhiyun 	 * eMMC HS with preset_enabled set will trigger a bug in
277*4882a593Smuzhiyun 	 * get_preset_value().
278*4882a593Smuzhiyun 	 */
279*4882a593Smuzhiyun 	if ((ios->timing == MMC_TIMING_MMC_HS400) ||
280*4882a593Smuzhiyun 	    (ios->timing == MMC_TIMING_MMC_HS200) ||
281*4882a593Smuzhiyun 	    (ios->timing == MMC_TIMING_MMC_HS)) {
282*4882a593Smuzhiyun 		host->preset_enabled = false;
283*4882a593Smuzhiyun 		host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN;
284*4882a593Smuzhiyun 		host->flags &= ~SDHCI_PV_ENABLED;
285*4882a593Smuzhiyun 
286*4882a593Smuzhiyun 		reg = sdhci_readw(host, SDHCI_HOST_CONTROL2);
287*4882a593Smuzhiyun 		reg &= ~SDHCI_CTRL_PRESET_VAL_ENABLE;
288*4882a593Smuzhiyun 		sdhci_writew(host, reg, SDHCI_HOST_CONTROL2);
289*4882a593Smuzhiyun 	} else {
290*4882a593Smuzhiyun 		host->quirks2 &= ~SDHCI_QUIRK2_PRESET_VALUE_BROKEN;
291*4882a593Smuzhiyun 	}
292*4882a593Smuzhiyun 
293*4882a593Smuzhiyun 	sdhci_set_ios(mmc, ios);
294*4882a593Smuzhiyun 	xenon_phy_adj(host, ios);
295*4882a593Smuzhiyun 
296*4882a593Smuzhiyun 	if (host->clock > XENON_DEFAULT_SDCLK_FREQ)
297*4882a593Smuzhiyun 		xenon_set_sdclk_off_idle(host, priv->sdhc_id, true);
298*4882a593Smuzhiyun }
299*4882a593Smuzhiyun 
xenon_start_signal_voltage_switch(struct mmc_host * mmc,struct mmc_ios * ios)300*4882a593Smuzhiyun static int xenon_start_signal_voltage_switch(struct mmc_host *mmc,
301*4882a593Smuzhiyun 					     struct mmc_ios *ios)
302*4882a593Smuzhiyun {
303*4882a593Smuzhiyun 	struct sdhci_host *host = mmc_priv(mmc);
304*4882a593Smuzhiyun 
305*4882a593Smuzhiyun 	/*
306*4882a593Smuzhiyun 	 * Before SD/SDIO set signal voltage, SD bus clock should be
307*4882a593Smuzhiyun 	 * disabled. However, sdhci_set_clock will also disable the Internal
308*4882a593Smuzhiyun 	 * clock in mmc_set_signal_voltage().
309*4882a593Smuzhiyun 	 * If Internal clock is disabled, the 3.3V/1.8V bit can not be updated.
310*4882a593Smuzhiyun 	 * Thus here manually enable internal clock.
311*4882a593Smuzhiyun 	 *
312*4882a593Smuzhiyun 	 * After switch completes, it is unnecessary to disable internal clock,
313*4882a593Smuzhiyun 	 * since keeping internal clock active obeys SD spec.
314*4882a593Smuzhiyun 	 */
315*4882a593Smuzhiyun 	xenon_enable_internal_clk(host);
316*4882a593Smuzhiyun 
317*4882a593Smuzhiyun 	xenon_soc_pad_ctrl(host, ios->signal_voltage);
318*4882a593Smuzhiyun 
319*4882a593Smuzhiyun 	/*
320*4882a593Smuzhiyun 	 * If Vqmmc is fixed on platform, vqmmc regulator should be unavailable.
321*4882a593Smuzhiyun 	 * Thus SDHCI_CTRL_VDD_180 bit might not work then.
322*4882a593Smuzhiyun 	 * Skip the standard voltage switch to avoid any issue.
323*4882a593Smuzhiyun 	 */
324*4882a593Smuzhiyun 	if (PTR_ERR(mmc->supply.vqmmc) == -ENODEV)
325*4882a593Smuzhiyun 		return 0;
326*4882a593Smuzhiyun 
327*4882a593Smuzhiyun 	return sdhci_start_signal_voltage_switch(mmc, ios);
328*4882a593Smuzhiyun }
329*4882a593Smuzhiyun 
330*4882a593Smuzhiyun /*
331*4882a593Smuzhiyun  * Update card type.
332*4882a593Smuzhiyun  * priv->init_card_type will be used in PHY timing adjustment.
333*4882a593Smuzhiyun  */
xenon_init_card(struct mmc_host * mmc,struct mmc_card * card)334*4882a593Smuzhiyun static void xenon_init_card(struct mmc_host *mmc, struct mmc_card *card)
335*4882a593Smuzhiyun {
336*4882a593Smuzhiyun 	struct sdhci_host *host = mmc_priv(mmc);
337*4882a593Smuzhiyun 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
338*4882a593Smuzhiyun 	struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
339*4882a593Smuzhiyun 
340*4882a593Smuzhiyun 	/* Update card type*/
341*4882a593Smuzhiyun 	priv->init_card_type = card->type;
342*4882a593Smuzhiyun }
343*4882a593Smuzhiyun 
xenon_execute_tuning(struct mmc_host * mmc,u32 opcode)344*4882a593Smuzhiyun static int xenon_execute_tuning(struct mmc_host *mmc, u32 opcode)
345*4882a593Smuzhiyun {
346*4882a593Smuzhiyun 	struct sdhci_host *host = mmc_priv(mmc);
347*4882a593Smuzhiyun 
348*4882a593Smuzhiyun 	if (host->timing == MMC_TIMING_UHS_DDR50 ||
349*4882a593Smuzhiyun 		host->timing == MMC_TIMING_MMC_DDR52)
350*4882a593Smuzhiyun 		return 0;
351*4882a593Smuzhiyun 
352*4882a593Smuzhiyun 	/*
353*4882a593Smuzhiyun 	 * Currently force Xenon driver back to support mode 1 only,
354*4882a593Smuzhiyun 	 * even though Xenon might claim to support mode 2 or mode 3.
355*4882a593Smuzhiyun 	 * It requires more time to test mode 2/mode 3 on more platforms.
356*4882a593Smuzhiyun 	 */
357*4882a593Smuzhiyun 	if (host->tuning_mode != SDHCI_TUNING_MODE_1)
358*4882a593Smuzhiyun 		xenon_retune_setup(host);
359*4882a593Smuzhiyun 
360*4882a593Smuzhiyun 	return sdhci_execute_tuning(mmc, opcode);
361*4882a593Smuzhiyun }
362*4882a593Smuzhiyun 
xenon_enable_sdio_irq(struct mmc_host * mmc,int enable)363*4882a593Smuzhiyun static void xenon_enable_sdio_irq(struct mmc_host *mmc, int enable)
364*4882a593Smuzhiyun {
365*4882a593Smuzhiyun 	struct sdhci_host *host = mmc_priv(mmc);
366*4882a593Smuzhiyun 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
367*4882a593Smuzhiyun 	struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
368*4882a593Smuzhiyun 	u32 reg;
369*4882a593Smuzhiyun 	u8 sdhc_id = priv->sdhc_id;
370*4882a593Smuzhiyun 
371*4882a593Smuzhiyun 	sdhci_enable_sdio_irq(mmc, enable);
372*4882a593Smuzhiyun 
373*4882a593Smuzhiyun 	if (enable) {
374*4882a593Smuzhiyun 		/*
375*4882a593Smuzhiyun 		 * Set SDIO Card Inserted indication
376*4882a593Smuzhiyun 		 * to enable detecting SDIO async irq.
377*4882a593Smuzhiyun 		 */
378*4882a593Smuzhiyun 		reg = sdhci_readl(host, XENON_SYS_CFG_INFO);
379*4882a593Smuzhiyun 		reg |= (1 << (sdhc_id + XENON_SLOT_TYPE_SDIO_SHIFT));
380*4882a593Smuzhiyun 		sdhci_writel(host, reg, XENON_SYS_CFG_INFO);
381*4882a593Smuzhiyun 	} else {
382*4882a593Smuzhiyun 		/* Clear SDIO Card Inserted indication */
383*4882a593Smuzhiyun 		reg = sdhci_readl(host, XENON_SYS_CFG_INFO);
384*4882a593Smuzhiyun 		reg &= ~(1 << (sdhc_id + XENON_SLOT_TYPE_SDIO_SHIFT));
385*4882a593Smuzhiyun 		sdhci_writel(host, reg, XENON_SYS_CFG_INFO);
386*4882a593Smuzhiyun 	}
387*4882a593Smuzhiyun }
388*4882a593Smuzhiyun 
xenon_replace_mmc_host_ops(struct sdhci_host * host)389*4882a593Smuzhiyun static void xenon_replace_mmc_host_ops(struct sdhci_host *host)
390*4882a593Smuzhiyun {
391*4882a593Smuzhiyun 	host->mmc_host_ops.set_ios = xenon_set_ios;
392*4882a593Smuzhiyun 	host->mmc_host_ops.start_signal_voltage_switch =
393*4882a593Smuzhiyun 			xenon_start_signal_voltage_switch;
394*4882a593Smuzhiyun 	host->mmc_host_ops.init_card = xenon_init_card;
395*4882a593Smuzhiyun 	host->mmc_host_ops.execute_tuning = xenon_execute_tuning;
396*4882a593Smuzhiyun 	host->mmc_host_ops.enable_sdio_irq = xenon_enable_sdio_irq;
397*4882a593Smuzhiyun }
398*4882a593Smuzhiyun 
399*4882a593Smuzhiyun /*
400*4882a593Smuzhiyun  * Parse Xenon specific DT properties:
401*4882a593Smuzhiyun  * sdhc-id: the index of current SDHC.
402*4882a593Smuzhiyun  *	    Refer to XENON_SYS_CFG_INFO register
403*4882a593Smuzhiyun  * tun-count: the interval between re-tuning
404*4882a593Smuzhiyun  */
xenon_probe_dt(struct platform_device * pdev)405*4882a593Smuzhiyun static int xenon_probe_dt(struct platform_device *pdev)
406*4882a593Smuzhiyun {
407*4882a593Smuzhiyun 	struct device_node *np = pdev->dev.of_node;
408*4882a593Smuzhiyun 	struct sdhci_host *host = platform_get_drvdata(pdev);
409*4882a593Smuzhiyun 	struct mmc_host *mmc = host->mmc;
410*4882a593Smuzhiyun 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
411*4882a593Smuzhiyun 	struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
412*4882a593Smuzhiyun 	u32 sdhc_id, nr_sdhc;
413*4882a593Smuzhiyun 	u32 tuning_count;
414*4882a593Smuzhiyun 
415*4882a593Smuzhiyun 	/* Disable HS200 on Armada AP806 */
416*4882a593Smuzhiyun 	if (of_device_is_compatible(np, "marvell,armada-ap806-sdhci"))
417*4882a593Smuzhiyun 		host->quirks2 |= SDHCI_QUIRK2_BROKEN_HS200;
418*4882a593Smuzhiyun 
419*4882a593Smuzhiyun 	sdhc_id = 0x0;
420*4882a593Smuzhiyun 	if (!of_property_read_u32(np, "marvell,xenon-sdhc-id", &sdhc_id)) {
421*4882a593Smuzhiyun 		nr_sdhc = sdhci_readl(host, XENON_SYS_CFG_INFO);
422*4882a593Smuzhiyun 		nr_sdhc &= XENON_NR_SUPPORTED_SLOT_MASK;
423*4882a593Smuzhiyun 		if (unlikely(sdhc_id > nr_sdhc)) {
424*4882a593Smuzhiyun 			dev_err(mmc_dev(mmc), "SDHC Index %d exceeds Number of SDHCs %d\n",
425*4882a593Smuzhiyun 				sdhc_id, nr_sdhc);
426*4882a593Smuzhiyun 			return -EINVAL;
427*4882a593Smuzhiyun 		}
428*4882a593Smuzhiyun 	}
429*4882a593Smuzhiyun 	priv->sdhc_id = sdhc_id;
430*4882a593Smuzhiyun 
431*4882a593Smuzhiyun 	tuning_count = XENON_DEF_TUNING_COUNT;
432*4882a593Smuzhiyun 	if (!of_property_read_u32(np, "marvell,xenon-tun-count",
433*4882a593Smuzhiyun 				  &tuning_count)) {
434*4882a593Smuzhiyun 		if (unlikely(tuning_count >= XENON_TMR_RETUN_NO_PRESENT)) {
435*4882a593Smuzhiyun 			dev_err(mmc_dev(mmc), "Wrong Re-tuning Count. Set default value %d\n",
436*4882a593Smuzhiyun 				XENON_DEF_TUNING_COUNT);
437*4882a593Smuzhiyun 			tuning_count = XENON_DEF_TUNING_COUNT;
438*4882a593Smuzhiyun 		}
439*4882a593Smuzhiyun 	}
440*4882a593Smuzhiyun 	priv->tuning_count = tuning_count;
441*4882a593Smuzhiyun 
442*4882a593Smuzhiyun 	return xenon_phy_parse_dt(np, host);
443*4882a593Smuzhiyun }
444*4882a593Smuzhiyun 
xenon_sdhc_prepare(struct sdhci_host * host)445*4882a593Smuzhiyun static int xenon_sdhc_prepare(struct sdhci_host *host)
446*4882a593Smuzhiyun {
447*4882a593Smuzhiyun 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
448*4882a593Smuzhiyun 	struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
449*4882a593Smuzhiyun 	u8 sdhc_id = priv->sdhc_id;
450*4882a593Smuzhiyun 
451*4882a593Smuzhiyun 	/* Enable SDHC */
452*4882a593Smuzhiyun 	xenon_enable_sdhc(host, sdhc_id);
453*4882a593Smuzhiyun 
454*4882a593Smuzhiyun 	/* Enable ACG */
455*4882a593Smuzhiyun 	xenon_set_acg(host, true);
456*4882a593Smuzhiyun 
457*4882a593Smuzhiyun 	/* Enable Parallel Transfer Mode */
458*4882a593Smuzhiyun 	xenon_enable_sdhc_parallel_tran(host, sdhc_id);
459*4882a593Smuzhiyun 
460*4882a593Smuzhiyun 	/* Disable SDCLK-Off-While-Idle before card init */
461*4882a593Smuzhiyun 	xenon_set_sdclk_off_idle(host, sdhc_id, false);
462*4882a593Smuzhiyun 
463*4882a593Smuzhiyun 	xenon_mask_cmd_conflict_err(host);
464*4882a593Smuzhiyun 
465*4882a593Smuzhiyun 	return 0;
466*4882a593Smuzhiyun }
467*4882a593Smuzhiyun 
xenon_sdhc_unprepare(struct sdhci_host * host)468*4882a593Smuzhiyun static void xenon_sdhc_unprepare(struct sdhci_host *host)
469*4882a593Smuzhiyun {
470*4882a593Smuzhiyun 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
471*4882a593Smuzhiyun 	struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
472*4882a593Smuzhiyun 	u8 sdhc_id = priv->sdhc_id;
473*4882a593Smuzhiyun 
474*4882a593Smuzhiyun 	/* disable SDHC */
475*4882a593Smuzhiyun 	xenon_disable_sdhc(host, sdhc_id);
476*4882a593Smuzhiyun }
477*4882a593Smuzhiyun 
xenon_probe(struct platform_device * pdev)478*4882a593Smuzhiyun static int xenon_probe(struct platform_device *pdev)
479*4882a593Smuzhiyun {
480*4882a593Smuzhiyun 	struct sdhci_pltfm_host *pltfm_host;
481*4882a593Smuzhiyun 	struct sdhci_host *host;
482*4882a593Smuzhiyun 	struct xenon_priv *priv;
483*4882a593Smuzhiyun 	int err;
484*4882a593Smuzhiyun 
485*4882a593Smuzhiyun 	host = sdhci_pltfm_init(pdev, &sdhci_xenon_pdata,
486*4882a593Smuzhiyun 				sizeof(struct xenon_priv));
487*4882a593Smuzhiyun 	if (IS_ERR(host))
488*4882a593Smuzhiyun 		return PTR_ERR(host);
489*4882a593Smuzhiyun 
490*4882a593Smuzhiyun 	pltfm_host = sdhci_priv(host);
491*4882a593Smuzhiyun 	priv = sdhci_pltfm_priv(pltfm_host);
492*4882a593Smuzhiyun 
493*4882a593Smuzhiyun 	/*
494*4882a593Smuzhiyun 	 * Link Xenon specific mmc_host_ops function,
495*4882a593Smuzhiyun 	 * to replace standard ones in sdhci_ops.
496*4882a593Smuzhiyun 	 */
497*4882a593Smuzhiyun 	xenon_replace_mmc_host_ops(host);
498*4882a593Smuzhiyun 
499*4882a593Smuzhiyun 	pltfm_host->clk = devm_clk_get(&pdev->dev, "core");
500*4882a593Smuzhiyun 	if (IS_ERR(pltfm_host->clk)) {
501*4882a593Smuzhiyun 		err = PTR_ERR(pltfm_host->clk);
502*4882a593Smuzhiyun 		dev_err(&pdev->dev, "Failed to setup input clk: %d\n", err);
503*4882a593Smuzhiyun 		goto free_pltfm;
504*4882a593Smuzhiyun 	}
505*4882a593Smuzhiyun 	err = clk_prepare_enable(pltfm_host->clk);
506*4882a593Smuzhiyun 	if (err)
507*4882a593Smuzhiyun 		goto free_pltfm;
508*4882a593Smuzhiyun 
509*4882a593Smuzhiyun 	priv->axi_clk = devm_clk_get(&pdev->dev, "axi");
510*4882a593Smuzhiyun 	if (IS_ERR(priv->axi_clk)) {
511*4882a593Smuzhiyun 		err = PTR_ERR(priv->axi_clk);
512*4882a593Smuzhiyun 		if (err == -EPROBE_DEFER)
513*4882a593Smuzhiyun 			goto err_clk;
514*4882a593Smuzhiyun 	} else {
515*4882a593Smuzhiyun 		err = clk_prepare_enable(priv->axi_clk);
516*4882a593Smuzhiyun 		if (err)
517*4882a593Smuzhiyun 			goto err_clk;
518*4882a593Smuzhiyun 	}
519*4882a593Smuzhiyun 
520*4882a593Smuzhiyun 	err = mmc_of_parse(host->mmc);
521*4882a593Smuzhiyun 	if (err)
522*4882a593Smuzhiyun 		goto err_clk_axi;
523*4882a593Smuzhiyun 
524*4882a593Smuzhiyun 	sdhci_get_of_property(pdev);
525*4882a593Smuzhiyun 
526*4882a593Smuzhiyun 	xenon_set_acg(host, false);
527*4882a593Smuzhiyun 
528*4882a593Smuzhiyun 	/* Xenon specific dt parse */
529*4882a593Smuzhiyun 	err = xenon_probe_dt(pdev);
530*4882a593Smuzhiyun 	if (err)
531*4882a593Smuzhiyun 		goto err_clk_axi;
532*4882a593Smuzhiyun 
533*4882a593Smuzhiyun 	err = xenon_sdhc_prepare(host);
534*4882a593Smuzhiyun 	if (err)
535*4882a593Smuzhiyun 		goto err_clk_axi;
536*4882a593Smuzhiyun 
537*4882a593Smuzhiyun 	pm_runtime_get_noresume(&pdev->dev);
538*4882a593Smuzhiyun 	pm_runtime_set_active(&pdev->dev);
539*4882a593Smuzhiyun 	pm_runtime_set_autosuspend_delay(&pdev->dev, 50);
540*4882a593Smuzhiyun 	pm_runtime_use_autosuspend(&pdev->dev);
541*4882a593Smuzhiyun 	pm_runtime_enable(&pdev->dev);
542*4882a593Smuzhiyun 	pm_suspend_ignore_children(&pdev->dev, 1);
543*4882a593Smuzhiyun 
544*4882a593Smuzhiyun 	err = sdhci_add_host(host);
545*4882a593Smuzhiyun 	if (err)
546*4882a593Smuzhiyun 		goto remove_sdhc;
547*4882a593Smuzhiyun 
548*4882a593Smuzhiyun 	pm_runtime_put_autosuspend(&pdev->dev);
549*4882a593Smuzhiyun 
550*4882a593Smuzhiyun 	return 0;
551*4882a593Smuzhiyun 
552*4882a593Smuzhiyun remove_sdhc:
553*4882a593Smuzhiyun 	pm_runtime_disable(&pdev->dev);
554*4882a593Smuzhiyun 	pm_runtime_put_noidle(&pdev->dev);
555*4882a593Smuzhiyun 	xenon_sdhc_unprepare(host);
556*4882a593Smuzhiyun err_clk_axi:
557*4882a593Smuzhiyun 	clk_disable_unprepare(priv->axi_clk);
558*4882a593Smuzhiyun err_clk:
559*4882a593Smuzhiyun 	clk_disable_unprepare(pltfm_host->clk);
560*4882a593Smuzhiyun free_pltfm:
561*4882a593Smuzhiyun 	sdhci_pltfm_free(pdev);
562*4882a593Smuzhiyun 	return err;
563*4882a593Smuzhiyun }
564*4882a593Smuzhiyun 
xenon_remove(struct platform_device * pdev)565*4882a593Smuzhiyun static int xenon_remove(struct platform_device *pdev)
566*4882a593Smuzhiyun {
567*4882a593Smuzhiyun 	struct sdhci_host *host = platform_get_drvdata(pdev);
568*4882a593Smuzhiyun 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
569*4882a593Smuzhiyun 	struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
570*4882a593Smuzhiyun 
571*4882a593Smuzhiyun 	pm_runtime_get_sync(&pdev->dev);
572*4882a593Smuzhiyun 	pm_runtime_disable(&pdev->dev);
573*4882a593Smuzhiyun 	pm_runtime_put_noidle(&pdev->dev);
574*4882a593Smuzhiyun 
575*4882a593Smuzhiyun 	sdhci_remove_host(host, 0);
576*4882a593Smuzhiyun 
577*4882a593Smuzhiyun 	xenon_sdhc_unprepare(host);
578*4882a593Smuzhiyun 	clk_disable_unprepare(priv->axi_clk);
579*4882a593Smuzhiyun 	clk_disable_unprepare(pltfm_host->clk);
580*4882a593Smuzhiyun 
581*4882a593Smuzhiyun 	sdhci_pltfm_free(pdev);
582*4882a593Smuzhiyun 
583*4882a593Smuzhiyun 	return 0;
584*4882a593Smuzhiyun }
585*4882a593Smuzhiyun 
586*4882a593Smuzhiyun #ifdef CONFIG_PM_SLEEP
xenon_suspend(struct device * dev)587*4882a593Smuzhiyun static int xenon_suspend(struct device *dev)
588*4882a593Smuzhiyun {
589*4882a593Smuzhiyun 	struct sdhci_host *host = dev_get_drvdata(dev);
590*4882a593Smuzhiyun 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
591*4882a593Smuzhiyun 	struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
592*4882a593Smuzhiyun 	int ret;
593*4882a593Smuzhiyun 
594*4882a593Smuzhiyun 	ret = pm_runtime_force_suspend(dev);
595*4882a593Smuzhiyun 
596*4882a593Smuzhiyun 	priv->restore_needed = true;
597*4882a593Smuzhiyun 	return ret;
598*4882a593Smuzhiyun }
599*4882a593Smuzhiyun #endif
600*4882a593Smuzhiyun 
601*4882a593Smuzhiyun #ifdef CONFIG_PM
xenon_runtime_suspend(struct device * dev)602*4882a593Smuzhiyun static int xenon_runtime_suspend(struct device *dev)
603*4882a593Smuzhiyun {
604*4882a593Smuzhiyun 	struct sdhci_host *host = dev_get_drvdata(dev);
605*4882a593Smuzhiyun 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
606*4882a593Smuzhiyun 	struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
607*4882a593Smuzhiyun 	int ret;
608*4882a593Smuzhiyun 
609*4882a593Smuzhiyun 	ret = sdhci_runtime_suspend_host(host);
610*4882a593Smuzhiyun 	if (ret)
611*4882a593Smuzhiyun 		return ret;
612*4882a593Smuzhiyun 
613*4882a593Smuzhiyun 	if (host->tuning_mode != SDHCI_TUNING_MODE_3)
614*4882a593Smuzhiyun 		mmc_retune_needed(host->mmc);
615*4882a593Smuzhiyun 
616*4882a593Smuzhiyun 	clk_disable_unprepare(pltfm_host->clk);
617*4882a593Smuzhiyun 	/*
618*4882a593Smuzhiyun 	 * Need to update the priv->clock here, or when runtime resume
619*4882a593Smuzhiyun 	 * back, phy don't aware the clock change and won't adjust phy
620*4882a593Smuzhiyun 	 * which will cause cmd err
621*4882a593Smuzhiyun 	 */
622*4882a593Smuzhiyun 	priv->clock = 0;
623*4882a593Smuzhiyun 	return 0;
624*4882a593Smuzhiyun }
625*4882a593Smuzhiyun 
xenon_runtime_resume(struct device * dev)626*4882a593Smuzhiyun static int xenon_runtime_resume(struct device *dev)
627*4882a593Smuzhiyun {
628*4882a593Smuzhiyun 	struct sdhci_host *host = dev_get_drvdata(dev);
629*4882a593Smuzhiyun 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
630*4882a593Smuzhiyun 	struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
631*4882a593Smuzhiyun 	int ret;
632*4882a593Smuzhiyun 
633*4882a593Smuzhiyun 	ret = clk_prepare_enable(pltfm_host->clk);
634*4882a593Smuzhiyun 	if (ret) {
635*4882a593Smuzhiyun 		dev_err(dev, "can't enable mainck\n");
636*4882a593Smuzhiyun 		return ret;
637*4882a593Smuzhiyun 	}
638*4882a593Smuzhiyun 
639*4882a593Smuzhiyun 	if (priv->restore_needed) {
640*4882a593Smuzhiyun 		ret = xenon_sdhc_prepare(host);
641*4882a593Smuzhiyun 		if (ret)
642*4882a593Smuzhiyun 			goto out;
643*4882a593Smuzhiyun 		priv->restore_needed = false;
644*4882a593Smuzhiyun 	}
645*4882a593Smuzhiyun 
646*4882a593Smuzhiyun 	ret = sdhci_runtime_resume_host(host, 0);
647*4882a593Smuzhiyun 	if (ret)
648*4882a593Smuzhiyun 		goto out;
649*4882a593Smuzhiyun 	return 0;
650*4882a593Smuzhiyun out:
651*4882a593Smuzhiyun 	clk_disable_unprepare(pltfm_host->clk);
652*4882a593Smuzhiyun 	return ret;
653*4882a593Smuzhiyun }
654*4882a593Smuzhiyun #endif /* CONFIG_PM */
655*4882a593Smuzhiyun 
656*4882a593Smuzhiyun static const struct dev_pm_ops sdhci_xenon_dev_pm_ops = {
657*4882a593Smuzhiyun 	SET_SYSTEM_SLEEP_PM_OPS(xenon_suspend,
658*4882a593Smuzhiyun 				pm_runtime_force_resume)
659*4882a593Smuzhiyun 	SET_RUNTIME_PM_OPS(xenon_runtime_suspend,
660*4882a593Smuzhiyun 			   xenon_runtime_resume,
661*4882a593Smuzhiyun 			   NULL)
662*4882a593Smuzhiyun };
663*4882a593Smuzhiyun 
664*4882a593Smuzhiyun static const struct of_device_id sdhci_xenon_dt_ids[] = {
665*4882a593Smuzhiyun 	{ .compatible = "marvell,armada-ap806-sdhci",},
666*4882a593Smuzhiyun 	{ .compatible = "marvell,armada-cp110-sdhci",},
667*4882a593Smuzhiyun 	{ .compatible = "marvell,armada-3700-sdhci",},
668*4882a593Smuzhiyun 	{}
669*4882a593Smuzhiyun };
670*4882a593Smuzhiyun MODULE_DEVICE_TABLE(of, sdhci_xenon_dt_ids);
671*4882a593Smuzhiyun 
672*4882a593Smuzhiyun static struct platform_driver sdhci_xenon_driver = {
673*4882a593Smuzhiyun 	.driver	= {
674*4882a593Smuzhiyun 		.name	= "xenon-sdhci",
675*4882a593Smuzhiyun 		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
676*4882a593Smuzhiyun 		.of_match_table = sdhci_xenon_dt_ids,
677*4882a593Smuzhiyun 		.pm = &sdhci_xenon_dev_pm_ops,
678*4882a593Smuzhiyun 	},
679*4882a593Smuzhiyun 	.probe	= xenon_probe,
680*4882a593Smuzhiyun 	.remove	= xenon_remove,
681*4882a593Smuzhiyun };
682*4882a593Smuzhiyun 
683*4882a593Smuzhiyun module_platform_driver(sdhci_xenon_driver);
684*4882a593Smuzhiyun 
685*4882a593Smuzhiyun MODULE_DESCRIPTION("SDHCI platform driver for Marvell Xenon SDHC");
686*4882a593Smuzhiyun MODULE_AUTHOR("Hu Ziji <huziji@marvell.com>");
687*4882a593Smuzhiyun MODULE_LICENSE("GPL v2");
688