1a6e7e407SFabio Estevam // SPDX-License-Identifier: GPL-2.0
295f25efeSWolfram Sang /*
395f25efeSWolfram Sang  * Freescale eSDHC i.MX controller driver for the platform bus.
495f25efeSWolfram Sang  *
595f25efeSWolfram Sang  * derived from the OF-version.
695f25efeSWolfram Sang  *
795f25efeSWolfram Sang  * Copyright (c) 2010 Pengutronix e.K.
8035ff831SWolfram Sang  *   Author: Wolfram Sang <kernel@pengutronix.de>
995f25efeSWolfram Sang  */
1095f25efeSWolfram Sang 
11a8e809ecSMasahiro Yamada #include <linux/bitfield.h>
1295f25efeSWolfram Sang #include <linux/io.h>
13f581e909SHaibo Chen #include <linux/iopoll.h>
1495f25efeSWolfram Sang #include <linux/delay.h>
1595f25efeSWolfram Sang #include <linux/err.h>
1695f25efeSWolfram Sang #include <linux/clk.h>
1766506f76SShawn Guo #include <linux/module.h>
18e149860dSRichard Zhu #include <linux/slab.h>
191c4989b0SBOUGH CHEN #include <linux/pm_qos.h>
2095f25efeSWolfram Sang #include <linux/mmc/host.h>
2158ac8177SRichard Zhu #include <linux/mmc/mmc.h>
2258ac8177SRichard Zhu #include <linux/mmc/sdio.h>
23fbe5fdd1SShawn Guo #include <linux/mmc/slot-gpio.h>
24abfafc2dSShawn Guo #include <linux/of.h>
25abfafc2dSShawn Guo #include <linux/of_device.h>
26e62d8b8fSDong Aisheng #include <linux/pinctrl/consumer.h>
2782906b13SArnd Bergmann #include <linux/platform_data/mmc-esdhc-imx.h>
2889d7e5c1SDong Aisheng #include <linux/pm_runtime.h>
2995f25efeSWolfram Sang #include "sdhci-pltfm.h"
3095f25efeSWolfram Sang #include "sdhci-esdhc.h"
31bb6e3581SBOUGH CHEN #include "cqhci.h"
3295f25efeSWolfram Sang 
33a215186dSHaibo Chen #define ESDHC_SYS_CTRL_DTOCV_MASK	0x0f
3460bf6396SShawn Guo #define	ESDHC_CTRL_D3CD			0x08
35fd44954eSHaibo Chen #define ESDHC_BURST_LEN_EN_INCR		(1 << 27)
3658ac8177SRichard Zhu /* VENDOR SPEC register */
3760bf6396SShawn Guo #define ESDHC_VENDOR_SPEC		0xc0
3860bf6396SShawn Guo #define  ESDHC_VENDOR_SPEC_SDIO_QUIRK	(1 << 1)
390322191eSDong Aisheng #define  ESDHC_VENDOR_SPEC_VSELECT	(1 << 1)
40fed2f6e2SDong Aisheng #define  ESDHC_VENDOR_SPEC_FRC_SDCLK_ON	(1 << 8)
413722c74cSHaibo Chen #define ESDHC_DEBUG_SEL_AND_STATUS_REG		0xc2
423722c74cSHaibo Chen #define ESDHC_DEBUG_SEL_REG			0xc3
433722c74cSHaibo Chen #define ESDHC_DEBUG_SEL_MASK			0xf
443722c74cSHaibo Chen #define ESDHC_DEBUG_SEL_CMD_STATE		1
453722c74cSHaibo Chen #define ESDHC_DEBUG_SEL_DATA_STATE		2
463722c74cSHaibo Chen #define ESDHC_DEBUG_SEL_TRANS_STATE		3
473722c74cSHaibo Chen #define ESDHC_DEBUG_SEL_DMA_STATE		4
483722c74cSHaibo Chen #define ESDHC_DEBUG_SEL_ADMA_STATE		5
493722c74cSHaibo Chen #define ESDHC_DEBUG_SEL_FIFO_STATE		6
503722c74cSHaibo Chen #define ESDHC_DEBUG_SEL_ASYNC_FIFO_STATE	7
5160bf6396SShawn Guo #define ESDHC_WTMK_LVL			0x44
52cc17e129SDong Aisheng #define  ESDHC_WTMK_DEFAULT_VAL		0x10401040
533fbd4322SAndrew Gabbasov #define  ESDHC_WTMK_LVL_RD_WML_MASK	0x000000FF
543fbd4322SAndrew Gabbasov #define  ESDHC_WTMK_LVL_RD_WML_SHIFT	0
553fbd4322SAndrew Gabbasov #define  ESDHC_WTMK_LVL_WR_WML_MASK	0x00FF0000
563fbd4322SAndrew Gabbasov #define  ESDHC_WTMK_LVL_WR_WML_SHIFT	16
573fbd4322SAndrew Gabbasov #define  ESDHC_WTMK_LVL_WML_VAL_DEF	64
583fbd4322SAndrew Gabbasov #define  ESDHC_WTMK_LVL_WML_VAL_MAX	128
5960bf6396SShawn Guo #define ESDHC_MIX_CTRL			0x48
60de5bdbffSDong Aisheng #define  ESDHC_MIX_CTRL_DDREN		(1 << 3)
612a15f981SShawn Guo #define  ESDHC_MIX_CTRL_AC23EN		(1 << 7)
620322191eSDong Aisheng #define  ESDHC_MIX_CTRL_EXE_TUNE	(1 << 22)
630322191eSDong Aisheng #define  ESDHC_MIX_CTRL_SMPCLK_SEL	(1 << 23)
640b330e38SDong Aisheng #define  ESDHC_MIX_CTRL_AUTO_TUNE_EN	(1 << 24)
650322191eSDong Aisheng #define  ESDHC_MIX_CTRL_FBCLK_SEL	(1 << 25)
6628b07674SHaibo Chen #define  ESDHC_MIX_CTRL_HS400_EN	(1 << 26)
67029e2476SBOUGH CHEN #define  ESDHC_MIX_CTRL_HS400_ES_EN	(1 << 27)
682a15f981SShawn Guo /* Bits 3 and 6 are not SDHCI standard definitions */
692a15f981SShawn Guo #define  ESDHC_MIX_CTRL_SDHCI_MASK	0xb7
70d131a71cSDong Aisheng /* Tuning bits */
71d131a71cSDong Aisheng #define  ESDHC_MIX_CTRL_TUNING_MASK	0x03c00000
7258ac8177SRichard Zhu 
73602519b2SDong Aisheng /* dll control register */
74602519b2SDong Aisheng #define ESDHC_DLL_CTRL			0x60
75602519b2SDong Aisheng #define ESDHC_DLL_OVERRIDE_VAL_SHIFT	9
76602519b2SDong Aisheng #define ESDHC_DLL_OVERRIDE_EN_SHIFT	8
77602519b2SDong Aisheng 
780322191eSDong Aisheng /* tune control register */
790322191eSDong Aisheng #define ESDHC_TUNE_CTRL_STATUS		0x68
800322191eSDong Aisheng #define  ESDHC_TUNE_CTRL_STEP		1
810322191eSDong Aisheng #define  ESDHC_TUNE_CTRL_MIN		0
820322191eSDong Aisheng #define  ESDHC_TUNE_CTRL_MAX		((1 << 7) - 1)
830322191eSDong Aisheng 
8428b07674SHaibo Chen /* strobe dll register */
8528b07674SHaibo Chen #define ESDHC_STROBE_DLL_CTRL		0x70
8628b07674SHaibo Chen #define ESDHC_STROBE_DLL_CTRL_ENABLE	(1 << 0)
8728b07674SHaibo Chen #define ESDHC_STROBE_DLL_CTRL_RESET	(1 << 1)
885bd2acdcSHaibo Chen #define ESDHC_STROBE_DLL_CTRL_SLV_DLY_TARGET_DEFAULT	0x7
8928b07674SHaibo Chen #define ESDHC_STROBE_DLL_CTRL_SLV_DLY_TARGET_SHIFT	3
902eaf5a53SBOUGH CHEN #define ESDHC_STROBE_DLL_CTRL_SLV_UPDATE_INT_DEFAULT	(4 << 20)
9128b07674SHaibo Chen 
9228b07674SHaibo Chen #define ESDHC_STROBE_DLL_STATUS		0x74
9328b07674SHaibo Chen #define ESDHC_STROBE_DLL_STS_REF_LOCK	(1 << 1)
9428b07674SHaibo Chen #define ESDHC_STROBE_DLL_STS_SLV_LOCK	0x1
9528b07674SHaibo Chen 
96bcdb5301SBOUGH CHEN #define ESDHC_VEND_SPEC2		0xc8
97bcdb5301SBOUGH CHEN #define ESDHC_VEND_SPEC2_EN_BUSY_IRQ	(1 << 8)
98bcdb5301SBOUGH CHEN 
996e9fd28eSDong Aisheng #define ESDHC_TUNING_CTRL		0xcc
1006e9fd28eSDong Aisheng #define ESDHC_STD_TUNING_EN		(1 << 24)
1016e9fd28eSDong Aisheng /* NOTE: the minimum valid tuning start tap for mx6sl is 1 */
102d87fc966SDong Aisheng #define ESDHC_TUNING_START_TAP_DEFAULT	0x1
1031194be8cSHaibo Chen #define ESDHC_TUNING_START_TAP_MASK	0x7f
10416e40e5bSHaibo Chen #define ESDHC_TUNING_CMD_CRC_CHECK_DISABLE	(1 << 7)
105260ecb3cSHaibo Chen #define ESDHC_TUNING_STEP_MASK		0x00070000
106d407e30bSHaibo Chen #define ESDHC_TUNING_STEP_SHIFT		16
1076e9fd28eSDong Aisheng 
108ad93220dSDong Aisheng /* pinctrl state */
109ad93220dSDong Aisheng #define ESDHC_PINCTRL_STATE_100MHZ	"state_100mhz"
110ad93220dSDong Aisheng #define ESDHC_PINCTRL_STATE_200MHZ	"state_200mhz"
111ad93220dSDong Aisheng 
11258ac8177SRichard Zhu /*
113af51079eSSascha Hauer  * Our interpretation of the SDHCI_HOST_CONTROL register
114af51079eSSascha Hauer  */
115af51079eSSascha Hauer #define ESDHC_CTRL_4BITBUS		(0x1 << 1)
116af51079eSSascha Hauer #define ESDHC_CTRL_8BITBUS		(0x2 << 1)
117af51079eSSascha Hauer #define ESDHC_CTRL_BUSWIDTH_MASK	(0x3 << 1)
118af51079eSSascha Hauer 
119af51079eSSascha Hauer /*
120d04f8d5bSBenoît Thébaudeau  * There is an INT DMA ERR mismatch between eSDHC and STD SDHC SPEC:
12197e4ba6aSRichard Zhu  * Bit25 is used in STD SPEC, and is reserved in fsl eSDHC design,
12297e4ba6aSRichard Zhu  * but bit28 is used as the INT DMA ERR in fsl eSDHC design.
12397e4ba6aSRichard Zhu  * Define this macro DMA error INT for fsl eSDHC
12497e4ba6aSRichard Zhu  */
12560bf6396SShawn Guo #define ESDHC_INT_VENDOR_SPEC_DMA_ERR	(1 << 28)
12697e4ba6aSRichard Zhu 
127bb6e3581SBOUGH CHEN /* the address offset of CQHCI */
128bb6e3581SBOUGH CHEN #define ESDHC_CQHCI_ADDR_OFFSET		0x100
129bb6e3581SBOUGH CHEN 
13097e4ba6aSRichard Zhu /*
13158ac8177SRichard Zhu  * The CMDTYPE of the CMD register (offset 0xE) should be set to
13258ac8177SRichard Zhu  * "11" when the STOP CMD12 is issued on imx53 to abort one
13358ac8177SRichard Zhu  * open ended multi-blk IO. Otherwise the TC INT wouldn't
13458ac8177SRichard Zhu  * be generated.
13558ac8177SRichard Zhu  * In exact block transfer, the controller doesn't complete the
13658ac8177SRichard Zhu  * operations automatically as required at the end of the
13758ac8177SRichard Zhu  * transfer and remains on hold if the abort command is not sent.
13858ac8177SRichard Zhu  * As a result, the TC flag is not asserted and SW received timeout
139d04f8d5bSBenoît Thébaudeau  * exception. Bit1 of Vendor Spec register is used to fix it.
14058ac8177SRichard Zhu  */
14131fbb301SShawn Guo #define ESDHC_FLAG_MULTIBLK_NO_INT	BIT(1)
14231fbb301SShawn Guo /*
1439d61c009SShawn Guo  * The flag tells that the ESDHC controller is an USDHC block that is
1449d61c009SShawn Guo  * integrated on the i.MX6 series.
1459d61c009SShawn Guo  */
1469d61c009SShawn Guo #define ESDHC_FLAG_USDHC		BIT(3)
1476e9fd28eSDong Aisheng /* The IP supports manual tuning process */
1486e9fd28eSDong Aisheng #define ESDHC_FLAG_MAN_TUNING		BIT(4)
1496e9fd28eSDong Aisheng /* The IP supports standard tuning process */
1506e9fd28eSDong Aisheng #define ESDHC_FLAG_STD_TUNING		BIT(5)
1516e9fd28eSDong Aisheng /* The IP has SDHCI_CAPABILITIES_1 register */
1526e9fd28eSDong Aisheng #define ESDHC_FLAG_HAVE_CAP1		BIT(6)
15318094430SDong Aisheng /*
154d04f8d5bSBenoît Thébaudeau  * The IP has erratum ERR004536
15518094430SDong Aisheng  * uSDHC: ADMA Length Mismatch Error occurs if the AHB read access is slow,
15618094430SDong Aisheng  * when reading data from the card
157667123f6SBenoît Thébaudeau  * This flag is also set for i.MX25 and i.MX35 in order to get
158667123f6SBenoît Thébaudeau  * SDHCI_QUIRK_BROKEN_ADMA, but for different reasons (ADMA capability bits).
15918094430SDong Aisheng  */
16018094430SDong Aisheng #define ESDHC_FLAG_ERR004536		BIT(7)
1614245afffSDong Aisheng /* The IP supports HS200 mode */
1624245afffSDong Aisheng #define ESDHC_FLAG_HS200		BIT(8)
16328b07674SHaibo Chen /* The IP supports HS400 mode */
16428b07674SHaibo Chen #define ESDHC_FLAG_HS400		BIT(9)
165af6a50d4SBOUGH CHEN /*
166af6a50d4SBOUGH CHEN  * The IP has errata ERR010450
167af6a50d4SBOUGH CHEN  * uSDHC: Due to the I/O timing limit, for SDR mode, SD card clock can't
168af6a50d4SBOUGH CHEN  * exceed 150MHz, for DDR mode, SD card clock can't exceed 45MHz.
169af6a50d4SBOUGH CHEN  */
170af6a50d4SBOUGH CHEN #define ESDHC_FLAG_ERR010450		BIT(10)
171029e2476SBOUGH CHEN /* The IP supports HS400ES mode */
172029e2476SBOUGH CHEN #define ESDHC_FLAG_HS400_ES		BIT(11)
173bb6e3581SBOUGH CHEN /* The IP has Host Controller Interface for Command Queuing */
174bb6e3581SBOUGH CHEN #define ESDHC_FLAG_CQHCI		BIT(12)
1751c4989b0SBOUGH CHEN /* need request pmqos during low power */
1761c4989b0SBOUGH CHEN #define ESDHC_FLAG_PMQOS		BIT(13)
177a26a4f1bSHaibo Chen /* The IP state got lost in low power mode */
178a26a4f1bSHaibo Chen #define ESDHC_FLAG_STATE_LOST_IN_LPMODE		BIT(14)
1795c11f1ffSHaibo Chen /* The IP lost clock rate in PM_RUNTIME */
1805c11f1ffSHaibo Chen #define ESDHC_FLAG_CLK_RATE_LOST_IN_PM_RUNTIME	BIT(15)
18174898cbcSHaibo Chen /*
18274898cbcSHaibo Chen  * The IP do not support the ACMD23 feature completely when use ADMA mode.
18374898cbcSHaibo Chen  * In ADMA mode, it only use the 16 bit block count of the register 0x4
18474898cbcSHaibo Chen  * (BLOCK_ATT) as the CMD23's argument for ACMD23 mode, which means it will
18574898cbcSHaibo Chen  * ignore the upper 16 bit of the CMD23's argument. This will block the reliable
18674898cbcSHaibo Chen  * write operation in RPMB, because RPMB reliable write need to set the bit31
18774898cbcSHaibo Chen  * of the CMD23's argument.
18874898cbcSHaibo Chen  * imx6qpdl/imx6sx/imx6sl/imx7d has this limitation only for ADMA mode, SDMA
18974898cbcSHaibo Chen  * do not has this limitation. so when these SoC use ADMA mode, it need to
19074898cbcSHaibo Chen  * disable the ACMD23 feature.
19174898cbcSHaibo Chen  */
19274898cbcSHaibo Chen #define ESDHC_FLAG_BROKEN_AUTO_CMD23	BIT(16)
193e149860dSRichard Zhu 
194f47c4bbfSShawn Guo struct esdhc_soc_data {
195f47c4bbfSShawn Guo 	u32 flags;
196f47c4bbfSShawn Guo };
197f47c4bbfSShawn Guo 
1984f100012SAndrey Smirnov static const struct esdhc_soc_data esdhc_imx25_data = {
199667123f6SBenoît Thébaudeau 	.flags = ESDHC_FLAG_ERR004536,
200f47c4bbfSShawn Guo };
201f47c4bbfSShawn Guo 
2024f100012SAndrey Smirnov static const struct esdhc_soc_data esdhc_imx35_data = {
203667123f6SBenoît Thébaudeau 	.flags = ESDHC_FLAG_ERR004536,
204f47c4bbfSShawn Guo };
205f47c4bbfSShawn Guo 
2064f100012SAndrey Smirnov static const struct esdhc_soc_data esdhc_imx51_data = {
207f47c4bbfSShawn Guo 	.flags = 0,
208f47c4bbfSShawn Guo };
209f47c4bbfSShawn Guo 
2104f100012SAndrey Smirnov static const struct esdhc_soc_data esdhc_imx53_data = {
211f47c4bbfSShawn Guo 	.flags = ESDHC_FLAG_MULTIBLK_NO_INT,
212f47c4bbfSShawn Guo };
213f47c4bbfSShawn Guo 
2144f100012SAndrey Smirnov static const struct esdhc_soc_data usdhc_imx6q_data = {
21574898cbcSHaibo Chen 	.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_MAN_TUNING
21674898cbcSHaibo Chen 			| ESDHC_FLAG_BROKEN_AUTO_CMD23,
2176e9fd28eSDong Aisheng };
2186e9fd28eSDong Aisheng 
2194f100012SAndrey Smirnov static const struct esdhc_soc_data usdhc_imx6sl_data = {
2206e9fd28eSDong Aisheng 	.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
2214245afffSDong Aisheng 			| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_ERR004536
22274898cbcSHaibo Chen 			| ESDHC_FLAG_HS200
22374898cbcSHaibo Chen 			| ESDHC_FLAG_BROKEN_AUTO_CMD23,
22474898cbcSHaibo Chen };
22574898cbcSHaibo Chen 
22674898cbcSHaibo Chen static const struct esdhc_soc_data usdhc_imx6sll_data = {
22774898cbcSHaibo Chen 	.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
22874898cbcSHaibo Chen 			| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
22986b59671SHaibo Chen 			| ESDHC_FLAG_HS400
23074898cbcSHaibo Chen 			| ESDHC_FLAG_STATE_LOST_IN_LPMODE,
23157ed3314SShawn Guo };
23257ed3314SShawn Guo 
2334f100012SAndrey Smirnov static const struct esdhc_soc_data usdhc_imx6sx_data = {
234913d4951SDong Aisheng 	.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
235a26a4f1bSHaibo Chen 			| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
23674898cbcSHaibo Chen 			| ESDHC_FLAG_STATE_LOST_IN_LPMODE
23774898cbcSHaibo Chen 			| ESDHC_FLAG_BROKEN_AUTO_CMD23,
238913d4951SDong Aisheng };
239913d4951SDong Aisheng 
240af6a50d4SBOUGH CHEN static const struct esdhc_soc_data usdhc_imx6ull_data = {
241af6a50d4SBOUGH CHEN 	.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
242af6a50d4SBOUGH CHEN 			| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
243a26a4f1bSHaibo Chen 			| ESDHC_FLAG_ERR010450
244a26a4f1bSHaibo Chen 			| ESDHC_FLAG_STATE_LOST_IN_LPMODE,
245af6a50d4SBOUGH CHEN };
246af6a50d4SBOUGH CHEN 
2474f100012SAndrey Smirnov static const struct esdhc_soc_data usdhc_imx7d_data = {
24828b07674SHaibo Chen 	.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
24928b07674SHaibo Chen 			| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
250a26a4f1bSHaibo Chen 			| ESDHC_FLAG_HS400
25174898cbcSHaibo Chen 			| ESDHC_FLAG_STATE_LOST_IN_LPMODE
25274898cbcSHaibo Chen 			| ESDHC_FLAG_BROKEN_AUTO_CMD23,
25328b07674SHaibo Chen };
25428b07674SHaibo Chen 
2551c4989b0SBOUGH CHEN static struct esdhc_soc_data usdhc_imx7ulp_data = {
2561c4989b0SBOUGH CHEN 	.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
2571c4989b0SBOUGH CHEN 			| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
258a26a4f1bSHaibo Chen 			| ESDHC_FLAG_PMQOS | ESDHC_FLAG_HS400
259a26a4f1bSHaibo Chen 			| ESDHC_FLAG_STATE_LOST_IN_LPMODE,
2601c4989b0SBOUGH CHEN };
2611c4989b0SBOUGH CHEN 
262029e2476SBOUGH CHEN static struct esdhc_soc_data usdhc_imx8qxp_data = {
263029e2476SBOUGH CHEN 	.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
264029e2476SBOUGH CHEN 			| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
265bb6e3581SBOUGH CHEN 			| ESDHC_FLAG_HS400 | ESDHC_FLAG_HS400_ES
266a26a4f1bSHaibo Chen 			| ESDHC_FLAG_CQHCI
2675c11f1ffSHaibo Chen 			| ESDHC_FLAG_STATE_LOST_IN_LPMODE
2685c11f1ffSHaibo Chen 			| ESDHC_FLAG_CLK_RATE_LOST_IN_PM_RUNTIME,
269029e2476SBOUGH CHEN };
270029e2476SBOUGH CHEN 
271cde5e8e9SHaibo Chen static struct esdhc_soc_data usdhc_imx8mm_data = {
272cde5e8e9SHaibo Chen 	.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
273cde5e8e9SHaibo Chen 			| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
274cde5e8e9SHaibo Chen 			| ESDHC_FLAG_HS400 | ESDHC_FLAG_HS400_ES
275cde5e8e9SHaibo Chen 			| ESDHC_FLAG_CQHCI
276cde5e8e9SHaibo Chen 			| ESDHC_FLAG_STATE_LOST_IN_LPMODE,
277e149860dSRichard Zhu };
278e149860dSRichard Zhu 
279e149860dSRichard Zhu struct pltfm_imx_data {
280e149860dSRichard Zhu 	u32 scratchpad;
281e62d8b8fSDong Aisheng 	struct pinctrl *pinctrl;
282ad93220dSDong Aisheng 	struct pinctrl_state *pins_100mhz;
283ad93220dSDong Aisheng 	struct pinctrl_state *pins_200mhz;
284f47c4bbfSShawn Guo 	const struct esdhc_soc_data *socdata;
285842afc02SShawn Guo 	struct esdhc_platform_data boarddata;
28652dac615SSascha Hauer 	struct clk *clk_ipg;
28752dac615SSascha Hauer 	struct clk *clk_ahb;
28852dac615SSascha Hauer 	struct clk *clk_per;
2893602785bSMichael Trimarchi 	unsigned int actual_clock;
290361b8482SLucas Stach 	enum {
291361b8482SLucas Stach 		NO_CMD_PENDING,      /* no multiblock command pending */
292361b8482SLucas Stach 		MULTIBLK_IN_PROCESS, /* exact multiblock cmd in process */
293361b8482SLucas Stach 		WAIT_FOR_INT,        /* sent CMD12, waiting for response INT */
294361b8482SLucas Stach 	} multiblock_status;
295de5bdbffSDong Aisheng 	u32 is_ddr;
2961c4989b0SBOUGH CHEN 	struct pm_qos_request pm_qos_req;
297e149860dSRichard Zhu };
298e149860dSRichard Zhu 
299abfafc2dSShawn Guo static const struct of_device_id imx_esdhc_dt_ids[] = {
300f47c4bbfSShawn Guo 	{ .compatible = "fsl,imx25-esdhc", .data = &esdhc_imx25_data, },
301f47c4bbfSShawn Guo 	{ .compatible = "fsl,imx35-esdhc", .data = &esdhc_imx35_data, },
302f47c4bbfSShawn Guo 	{ .compatible = "fsl,imx51-esdhc", .data = &esdhc_imx51_data, },
303f47c4bbfSShawn Guo 	{ .compatible = "fsl,imx53-esdhc", .data = &esdhc_imx53_data, },
304913d4951SDong Aisheng 	{ .compatible = "fsl,imx6sx-usdhc", .data = &usdhc_imx6sx_data, },
3056e9fd28eSDong Aisheng 	{ .compatible = "fsl,imx6sl-usdhc", .data = &usdhc_imx6sl_data, },
30674898cbcSHaibo Chen 	{ .compatible = "fsl,imx6sll-usdhc", .data = &usdhc_imx6sll_data, },
307f47c4bbfSShawn Guo 	{ .compatible = "fsl,imx6q-usdhc", .data = &usdhc_imx6q_data, },
308af6a50d4SBOUGH CHEN 	{ .compatible = "fsl,imx6ull-usdhc", .data = &usdhc_imx6ull_data, },
30928b07674SHaibo Chen 	{ .compatible = "fsl,imx7d-usdhc", .data = &usdhc_imx7d_data, },
3101c4989b0SBOUGH CHEN 	{ .compatible = "fsl,imx7ulp-usdhc", .data = &usdhc_imx7ulp_data, },
311029e2476SBOUGH CHEN 	{ .compatible = "fsl,imx8qxp-usdhc", .data = &usdhc_imx8qxp_data, },
312cde5e8e9SHaibo Chen 	{ .compatible = "fsl,imx8mm-usdhc", .data = &usdhc_imx8mm_data, },
313abfafc2dSShawn Guo 	{ /* sentinel */ }
314abfafc2dSShawn Guo };
315abfafc2dSShawn Guo MODULE_DEVICE_TABLE(of, imx_esdhc_dt_ids);
316abfafc2dSShawn Guo 
31757ed3314SShawn Guo static inline int is_imx25_esdhc(struct pltfm_imx_data *data)
31857ed3314SShawn Guo {
319f47c4bbfSShawn Guo 	return data->socdata == &esdhc_imx25_data;
32057ed3314SShawn Guo }
32157ed3314SShawn Guo 
32257ed3314SShawn Guo static inline int is_imx53_esdhc(struct pltfm_imx_data *data)
32357ed3314SShawn Guo {
324f47c4bbfSShawn Guo 	return data->socdata == &esdhc_imx53_data;
32557ed3314SShawn Guo }
32657ed3314SShawn Guo 
32795a2482aSShawn Guo static inline int is_imx6q_usdhc(struct pltfm_imx_data *data)
32895a2482aSShawn Guo {
329f47c4bbfSShawn Guo 	return data->socdata == &usdhc_imx6q_data;
33095a2482aSShawn Guo }
33195a2482aSShawn Guo 
3329d61c009SShawn Guo static inline int esdhc_is_usdhc(struct pltfm_imx_data *data)
3339d61c009SShawn Guo {
334f47c4bbfSShawn Guo 	return !!(data->socdata->flags & ESDHC_FLAG_USDHC);
3359d61c009SShawn Guo }
3369d61c009SShawn Guo 
33795f25efeSWolfram Sang static inline void esdhc_clrset_le(struct sdhci_host *host, u32 mask, u32 val, int reg)
33895f25efeSWolfram Sang {
33995f25efeSWolfram Sang 	void __iomem *base = host->ioaddr + (reg & ~0x3);
34095f25efeSWolfram Sang 	u32 shift = (reg & 0x3) * 8;
34195f25efeSWolfram Sang 
34295f25efeSWolfram Sang 	writel(((readl(base) & ~(mask << shift)) | (val << shift)), base);
34395f25efeSWolfram Sang }
34495f25efeSWolfram Sang 
3453722c74cSHaibo Chen #define DRIVER_NAME "sdhci-esdhc-imx"
3463722c74cSHaibo Chen #define ESDHC_IMX_DUMP(f, x...) \
3473722c74cSHaibo Chen 	pr_err("%s: " DRIVER_NAME ": " f, mmc_hostname(host->mmc), ## x)
3483722c74cSHaibo Chen static void esdhc_dump_debug_regs(struct sdhci_host *host)
3493722c74cSHaibo Chen {
3503722c74cSHaibo Chen 	int i;
3513722c74cSHaibo Chen 	char *debug_status[7] = {
3523722c74cSHaibo Chen 				 "cmd debug status",
3533722c74cSHaibo Chen 				 "data debug status",
3543722c74cSHaibo Chen 				 "trans debug status",
3553722c74cSHaibo Chen 				 "dma debug status",
3563722c74cSHaibo Chen 				 "adma debug status",
3573722c74cSHaibo Chen 				 "fifo debug status",
3583722c74cSHaibo Chen 				 "async fifo debug status"
3593722c74cSHaibo Chen 	};
3603722c74cSHaibo Chen 
3613722c74cSHaibo Chen 	ESDHC_IMX_DUMP("========= ESDHC IMX DEBUG STATUS DUMP =========\n");
3623722c74cSHaibo Chen 	for (i = 0; i < 7; i++) {
3633722c74cSHaibo Chen 		esdhc_clrset_le(host, ESDHC_DEBUG_SEL_MASK,
3643722c74cSHaibo Chen 			ESDHC_DEBUG_SEL_CMD_STATE + i, ESDHC_DEBUG_SEL_REG);
3653722c74cSHaibo Chen 		ESDHC_IMX_DUMP("%s:  0x%04x\n", debug_status[i],
3663722c74cSHaibo Chen 			readw(host->ioaddr + ESDHC_DEBUG_SEL_AND_STATUS_REG));
3673722c74cSHaibo Chen 	}
3683722c74cSHaibo Chen 
3693722c74cSHaibo Chen 	esdhc_clrset_le(host, ESDHC_DEBUG_SEL_MASK, 0, ESDHC_DEBUG_SEL_REG);
3703722c74cSHaibo Chen 
3713722c74cSHaibo Chen }
3723722c74cSHaibo Chen 
373f581e909SHaibo Chen static inline void esdhc_wait_for_card_clock_gate_off(struct sdhci_host *host)
374f581e909SHaibo Chen {
375f581e909SHaibo Chen 	u32 present_state;
376f581e909SHaibo Chen 	int ret;
377f581e909SHaibo Chen 
378f581e909SHaibo Chen 	ret = readl_poll_timeout(host->ioaddr + ESDHC_PRSSTAT, present_state,
379f581e909SHaibo Chen 				(present_state & ESDHC_CLOCK_GATE_OFF), 2, 100);
380f581e909SHaibo Chen 	if (ret == -ETIMEDOUT)
381f581e909SHaibo Chen 		dev_warn(mmc_dev(host->mmc), "%s: card clock still not gate off in 100us!.\n", __func__);
382f581e909SHaibo Chen }
383f581e909SHaibo Chen 
3847e29c306SWolfram Sang static u32 esdhc_readl_le(struct sdhci_host *host, int reg)
3857e29c306SWolfram Sang {
386361b8482SLucas Stach 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
387070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
388913413c3SShawn Guo 	u32 val = readl(host->ioaddr + reg);
389913413c3SShawn Guo 
3900322191eSDong Aisheng 	if (unlikely(reg == SDHCI_PRESENT_STATE)) {
3910322191eSDong Aisheng 		u32 fsl_prss = val;
3920322191eSDong Aisheng 		/* save the least 20 bits */
3930322191eSDong Aisheng 		val = fsl_prss & 0x000FFFFF;
3940322191eSDong Aisheng 		/* move dat[0-3] bits */
3950322191eSDong Aisheng 		val |= (fsl_prss & 0x0F000000) >> 4;
3960322191eSDong Aisheng 		/* move cmd line bit */
3970322191eSDong Aisheng 		val |= (fsl_prss & 0x00800000) << 1;
3980322191eSDong Aisheng 	}
3990322191eSDong Aisheng 
40097e4ba6aSRichard Zhu 	if (unlikely(reg == SDHCI_CAPABILITIES)) {
4016b4fb671SDong Aisheng 		/* ignore bit[0-15] as it stores cap_1 register val for mx6sl */
4026b4fb671SDong Aisheng 		if (imx_data->socdata->flags & ESDHC_FLAG_HAVE_CAP1)
4036b4fb671SDong Aisheng 			val &= 0xffff0000;
4046b4fb671SDong Aisheng 
40597e4ba6aSRichard Zhu 		/* In FSL esdhc IC module, only bit20 is used to indicate the
40697e4ba6aSRichard Zhu 		 * ADMA2 capability of esdhc, but this bit is messed up on
40797e4ba6aSRichard Zhu 		 * some SOCs (e.g. on MX25, MX35 this bit is set, but they
40897e4ba6aSRichard Zhu 		 * don't actually support ADMA2). So set the BROKEN_ADMA
409d04f8d5bSBenoît Thébaudeau 		 * quirk on MX25/35 platforms.
41097e4ba6aSRichard Zhu 		 */
41197e4ba6aSRichard Zhu 
41297e4ba6aSRichard Zhu 		if (val & SDHCI_CAN_DO_ADMA1) {
41397e4ba6aSRichard Zhu 			val &= ~SDHCI_CAN_DO_ADMA1;
41497e4ba6aSRichard Zhu 			val |= SDHCI_CAN_DO_ADMA2;
41597e4ba6aSRichard Zhu 		}
41697e4ba6aSRichard Zhu 	}
41797e4ba6aSRichard Zhu 
4186e9fd28eSDong Aisheng 	if (unlikely(reg == SDHCI_CAPABILITIES_1)) {
4196e9fd28eSDong Aisheng 		if (esdhc_is_usdhc(imx_data)) {
4206e9fd28eSDong Aisheng 			if (imx_data->socdata->flags & ESDHC_FLAG_HAVE_CAP1)
4216e9fd28eSDong Aisheng 				val = readl(host->ioaddr + SDHCI_CAPABILITIES) & 0xFFFF;
4226e9fd28eSDong Aisheng 			else
4236e9fd28eSDong Aisheng 				/* imx6q/dl does not have cap_1 register, fake one */
4240322191eSDong Aisheng 				val = SDHCI_SUPPORT_DDR50 | SDHCI_SUPPORT_SDR104
425888824bbSDong Aisheng 					| SDHCI_SUPPORT_SDR50
426da0295ffSDong Aisheng 					| SDHCI_USE_SDR50_TUNING
427a8e809ecSMasahiro Yamada 					| FIELD_PREP(SDHCI_RETUNING_MODE_MASK,
428a8e809ecSMasahiro Yamada 						     SDHCI_TUNING_MODE_3);
42928b07674SHaibo Chen 
43028b07674SHaibo Chen 			if (imx_data->socdata->flags & ESDHC_FLAG_HS400)
43128b07674SHaibo Chen 				val |= SDHCI_SUPPORT_HS400;
43292748beaSStefan Agner 
43392748beaSStefan Agner 			/*
43492748beaSStefan Agner 			 * Do not advertise faster UHS modes if there are no
43592748beaSStefan Agner 			 * pinctrl states for 100MHz/200MHz.
43692748beaSStefan Agner 			 */
437*25e8b9ebSShawn Guo 			if (IS_ERR_OR_NULL(imx_data->pins_100mhz))
438*25e8b9ebSShawn Guo 				val &= ~(SDHCI_SUPPORT_SDR50 | SDHCI_SUPPORT_DDR50);
439*25e8b9ebSShawn Guo 			if (IS_ERR_OR_NULL(imx_data->pins_200mhz))
440*25e8b9ebSShawn Guo 				val &= ~(SDHCI_SUPPORT_SDR104 | SDHCI_SUPPORT_HS400);
4416e9fd28eSDong Aisheng 		}
4426e9fd28eSDong Aisheng 	}
4430322191eSDong Aisheng 
4449d61c009SShawn Guo 	if (unlikely(reg == SDHCI_MAX_CURRENT) && esdhc_is_usdhc(imx_data)) {
4450322191eSDong Aisheng 		val = 0;
446804a65b3SMasahiro Yamada 		val |= FIELD_PREP(SDHCI_MAX_CURRENT_330_MASK, 0xFF);
447804a65b3SMasahiro Yamada 		val |= FIELD_PREP(SDHCI_MAX_CURRENT_300_MASK, 0xFF);
448804a65b3SMasahiro Yamada 		val |= FIELD_PREP(SDHCI_MAX_CURRENT_180_MASK, 0xFF);
4490322191eSDong Aisheng 	}
4500322191eSDong Aisheng 
45197e4ba6aSRichard Zhu 	if (unlikely(reg == SDHCI_INT_STATUS)) {
45260bf6396SShawn Guo 		if (val & ESDHC_INT_VENDOR_SPEC_DMA_ERR) {
45360bf6396SShawn Guo 			val &= ~ESDHC_INT_VENDOR_SPEC_DMA_ERR;
45497e4ba6aSRichard Zhu 			val |= SDHCI_INT_ADMA_ERROR;
45597e4ba6aSRichard Zhu 		}
456361b8482SLucas Stach 
457361b8482SLucas Stach 		/*
458361b8482SLucas Stach 		 * mask off the interrupt we get in response to the manually
459361b8482SLucas Stach 		 * sent CMD12
460361b8482SLucas Stach 		 */
461361b8482SLucas Stach 		if ((imx_data->multiblock_status == WAIT_FOR_INT) &&
462361b8482SLucas Stach 		    ((val & SDHCI_INT_RESPONSE) == SDHCI_INT_RESPONSE)) {
463361b8482SLucas Stach 			val &= ~SDHCI_INT_RESPONSE;
464361b8482SLucas Stach 			writel(SDHCI_INT_RESPONSE, host->ioaddr +
465361b8482SLucas Stach 						   SDHCI_INT_STATUS);
466361b8482SLucas Stach 			imx_data->multiblock_status = NO_CMD_PENDING;
467361b8482SLucas Stach 		}
46897e4ba6aSRichard Zhu 	}
46997e4ba6aSRichard Zhu 
4707e29c306SWolfram Sang 	return val;
4717e29c306SWolfram Sang }
4727e29c306SWolfram Sang 
4737e29c306SWolfram Sang static void esdhc_writel_le(struct sdhci_host *host, u32 val, int reg)
4747e29c306SWolfram Sang {
475e149860dSRichard Zhu 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
476070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
4770d58864bSTony Lin 	u32 data;
478e149860dSRichard Zhu 
47977da3da0SAaron Brice 	if (unlikely(reg == SDHCI_INT_ENABLE || reg == SDHCI_SIGNAL_ENABLE ||
48077da3da0SAaron Brice 			reg == SDHCI_INT_STATUS)) {
481b7321042SDong Aisheng 		if ((val & SDHCI_INT_CARD_INT) && !esdhc_is_usdhc(imx_data)) {
4820d58864bSTony Lin 			/*
4830d58864bSTony Lin 			 * Clear and then set D3CD bit to avoid missing the
484d04f8d5bSBenoît Thébaudeau 			 * card interrupt. This is an eSDHC controller problem
4850d58864bSTony Lin 			 * so we need to apply the following workaround: clear
4860d58864bSTony Lin 			 * and set D3CD bit will make eSDHC re-sample the card
4870d58864bSTony Lin 			 * interrupt. In case a card interrupt was lost,
4880d58864bSTony Lin 			 * re-sample it by the following steps.
4890d58864bSTony Lin 			 */
4900d58864bSTony Lin 			data = readl(host->ioaddr + SDHCI_HOST_CONTROL);
49160bf6396SShawn Guo 			data &= ~ESDHC_CTRL_D3CD;
4920d58864bSTony Lin 			writel(data, host->ioaddr + SDHCI_HOST_CONTROL);
49360bf6396SShawn Guo 			data |= ESDHC_CTRL_D3CD;
4940d58864bSTony Lin 			writel(data, host->ioaddr + SDHCI_HOST_CONTROL);
4950d58864bSTony Lin 		}
496915be485SDong Aisheng 
497915be485SDong Aisheng 		if (val & SDHCI_INT_ADMA_ERROR) {
498915be485SDong Aisheng 			val &= ~SDHCI_INT_ADMA_ERROR;
499915be485SDong Aisheng 			val |= ESDHC_INT_VENDOR_SPEC_DMA_ERR;
500915be485SDong Aisheng 		}
5010d58864bSTony Lin 	}
5020d58864bSTony Lin 
503f47c4bbfSShawn Guo 	if (unlikely((imx_data->socdata->flags & ESDHC_FLAG_MULTIBLK_NO_INT)
50458ac8177SRichard Zhu 				&& (reg == SDHCI_INT_STATUS)
50558ac8177SRichard Zhu 				&& (val & SDHCI_INT_DATA_END))) {
50658ac8177SRichard Zhu 			u32 v;
50760bf6396SShawn Guo 			v = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
50860bf6396SShawn Guo 			v &= ~ESDHC_VENDOR_SPEC_SDIO_QUIRK;
50960bf6396SShawn Guo 			writel(v, host->ioaddr + ESDHC_VENDOR_SPEC);
510361b8482SLucas Stach 
511361b8482SLucas Stach 			if (imx_data->multiblock_status == MULTIBLK_IN_PROCESS)
512361b8482SLucas Stach 			{
513361b8482SLucas Stach 				/* send a manual CMD12 with RESPTYP=none */
514361b8482SLucas Stach 				data = MMC_STOP_TRANSMISSION << 24 |
515361b8482SLucas Stach 				       SDHCI_CMD_ABORTCMD << 16;
516361b8482SLucas Stach 				writel(data, host->ioaddr + SDHCI_TRANSFER_MODE);
517361b8482SLucas Stach 				imx_data->multiblock_status = WAIT_FOR_INT;
518361b8482SLucas Stach 			}
51958ac8177SRichard Zhu 	}
52058ac8177SRichard Zhu 
5217e29c306SWolfram Sang 	writel(val, host->ioaddr + reg);
5227e29c306SWolfram Sang }
5237e29c306SWolfram Sang 
52495f25efeSWolfram Sang static u16 esdhc_readw_le(struct sdhci_host *host, int reg)
52595f25efeSWolfram Sang {
526ef4d0888SShawn Guo 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
527070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
5280322191eSDong Aisheng 	u16 ret = 0;
5290322191eSDong Aisheng 	u32 val;
530ef4d0888SShawn Guo 
53195a2482aSShawn Guo 	if (unlikely(reg == SDHCI_HOST_VERSION)) {
532ef4d0888SShawn Guo 		reg ^= 2;
5339d61c009SShawn Guo 		if (esdhc_is_usdhc(imx_data)) {
53495a2482aSShawn Guo 			/*
535ef4d0888SShawn Guo 			 * The usdhc register returns a wrong host version.
536ef4d0888SShawn Guo 			 * Correct it here.
53795a2482aSShawn Guo 			 */
538ef4d0888SShawn Guo 			return SDHCI_SPEC_300;
539ef4d0888SShawn Guo 		}
54095a2482aSShawn Guo 	}
54195f25efeSWolfram Sang 
5420322191eSDong Aisheng 	if (unlikely(reg == SDHCI_HOST_CONTROL2)) {
5430322191eSDong Aisheng 		val = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
5440322191eSDong Aisheng 		if (val & ESDHC_VENDOR_SPEC_VSELECT)
5450322191eSDong Aisheng 			ret |= SDHCI_CTRL_VDD_180;
5460322191eSDong Aisheng 
5479d61c009SShawn Guo 		if (esdhc_is_usdhc(imx_data)) {
5486e9fd28eSDong Aisheng 			if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING)
5490322191eSDong Aisheng 				val = readl(host->ioaddr + ESDHC_MIX_CTRL);
5506e9fd28eSDong Aisheng 			else if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING)
5516e9fd28eSDong Aisheng 				/* the std tuning bits is in ACMD12_ERR for imx6sl */
552869f8a69SAdrian Hunter 				val = readl(host->ioaddr + SDHCI_AUTO_CMD_STATUS);
5536e9fd28eSDong Aisheng 		}
5546e9fd28eSDong Aisheng 
5550322191eSDong Aisheng 		if (val & ESDHC_MIX_CTRL_EXE_TUNE)
5560322191eSDong Aisheng 			ret |= SDHCI_CTRL_EXEC_TUNING;
5570322191eSDong Aisheng 		if (val & ESDHC_MIX_CTRL_SMPCLK_SEL)
5580322191eSDong Aisheng 			ret |= SDHCI_CTRL_TUNED_CLK;
5590322191eSDong Aisheng 
5600322191eSDong Aisheng 		ret &= ~SDHCI_CTRL_PRESET_VAL_ENABLE;
5610322191eSDong Aisheng 
5620322191eSDong Aisheng 		return ret;
5630322191eSDong Aisheng 	}
5640322191eSDong Aisheng 
5657dd109efSDong Aisheng 	if (unlikely(reg == SDHCI_TRANSFER_MODE)) {
5667dd109efSDong Aisheng 		if (esdhc_is_usdhc(imx_data)) {
5677dd109efSDong Aisheng 			u32 m = readl(host->ioaddr + ESDHC_MIX_CTRL);
5687dd109efSDong Aisheng 			ret = m & ESDHC_MIX_CTRL_SDHCI_MASK;
5697dd109efSDong Aisheng 			/* Swap AC23 bit */
5707dd109efSDong Aisheng 			if (m & ESDHC_MIX_CTRL_AC23EN) {
5717dd109efSDong Aisheng 				ret &= ~ESDHC_MIX_CTRL_AC23EN;
5727dd109efSDong Aisheng 				ret |= SDHCI_TRNS_AUTO_CMD23;
5737dd109efSDong Aisheng 			}
5747dd109efSDong Aisheng 		} else {
5757dd109efSDong Aisheng 			ret = readw(host->ioaddr + SDHCI_TRANSFER_MODE);
5767dd109efSDong Aisheng 		}
5777dd109efSDong Aisheng 
5787dd109efSDong Aisheng 		return ret;
5797dd109efSDong Aisheng 	}
5807dd109efSDong Aisheng 
58195f25efeSWolfram Sang 	return readw(host->ioaddr + reg);
58295f25efeSWolfram Sang }
58395f25efeSWolfram Sang 
58495f25efeSWolfram Sang static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg)
58595f25efeSWolfram Sang {
58695f25efeSWolfram Sang 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
587070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
5880322191eSDong Aisheng 	u32 new_val = 0;
58995f25efeSWolfram Sang 
59095f25efeSWolfram Sang 	switch (reg) {
5910322191eSDong Aisheng 	case SDHCI_CLOCK_CONTROL:
5920322191eSDong Aisheng 		new_val = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
5930322191eSDong Aisheng 		if (val & SDHCI_CLOCK_CARD_EN)
5940322191eSDong Aisheng 			new_val |= ESDHC_VENDOR_SPEC_FRC_SDCLK_ON;
5950322191eSDong Aisheng 		else
5960322191eSDong Aisheng 			new_val &= ~ESDHC_VENDOR_SPEC_FRC_SDCLK_ON;
5970322191eSDong Aisheng 		writel(new_val, host->ioaddr + ESDHC_VENDOR_SPEC);
598f581e909SHaibo Chen 		if (!(new_val & ESDHC_VENDOR_SPEC_FRC_SDCLK_ON))
599f581e909SHaibo Chen 			esdhc_wait_for_card_clock_gate_off(host);
6000322191eSDong Aisheng 		return;
6010322191eSDong Aisheng 	case SDHCI_HOST_CONTROL2:
6020322191eSDong Aisheng 		new_val = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
6030322191eSDong Aisheng 		if (val & SDHCI_CTRL_VDD_180)
6040322191eSDong Aisheng 			new_val |= ESDHC_VENDOR_SPEC_VSELECT;
6050322191eSDong Aisheng 		else
6060322191eSDong Aisheng 			new_val &= ~ESDHC_VENDOR_SPEC_VSELECT;
6070322191eSDong Aisheng 		writel(new_val, host->ioaddr + ESDHC_VENDOR_SPEC);
6086e9fd28eSDong Aisheng 		if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING) {
6090322191eSDong Aisheng 			new_val = readl(host->ioaddr + ESDHC_MIX_CTRL);
610da0295ffSDong Aisheng 			if (val & SDHCI_CTRL_TUNED_CLK) {
6110322191eSDong Aisheng 				new_val |= ESDHC_MIX_CTRL_SMPCLK_SEL;
612da0295ffSDong Aisheng 				new_val |= ESDHC_MIX_CTRL_AUTO_TUNE_EN;
613da0295ffSDong Aisheng 			} else {
6140322191eSDong Aisheng 				new_val &= ~ESDHC_MIX_CTRL_SMPCLK_SEL;
615da0295ffSDong Aisheng 				new_val &= ~ESDHC_MIX_CTRL_AUTO_TUNE_EN;
616da0295ffSDong Aisheng 			}
6170322191eSDong Aisheng 			writel(new_val , host->ioaddr + ESDHC_MIX_CTRL);
6186e9fd28eSDong Aisheng 		} else if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) {
619869f8a69SAdrian Hunter 			u32 v = readl(host->ioaddr + SDHCI_AUTO_CMD_STATUS);
6206e9fd28eSDong Aisheng 			u32 m = readl(host->ioaddr + ESDHC_MIX_CTRL);
6218b2bb0adSDong Aisheng 			if (val & SDHCI_CTRL_TUNED_CLK) {
6228b2bb0adSDong Aisheng 				v |= ESDHC_MIX_CTRL_SMPCLK_SEL;
6236e9fd28eSDong Aisheng 			} else {
6248b2bb0adSDong Aisheng 				v &= ~ESDHC_MIX_CTRL_SMPCLK_SEL;
6256e9fd28eSDong Aisheng 				m &= ~ESDHC_MIX_CTRL_FBCLK_SEL;
6260b330e38SDong Aisheng 				m &= ~ESDHC_MIX_CTRL_AUTO_TUNE_EN;
6276e9fd28eSDong Aisheng 			}
6286e9fd28eSDong Aisheng 
6298b2bb0adSDong Aisheng 			if (val & SDHCI_CTRL_EXEC_TUNING) {
6308b2bb0adSDong Aisheng 				v |= ESDHC_MIX_CTRL_EXE_TUNE;
6318b2bb0adSDong Aisheng 				m |= ESDHC_MIX_CTRL_FBCLK_SEL;
6320b330e38SDong Aisheng 				m |= ESDHC_MIX_CTRL_AUTO_TUNE_EN;
6338b2bb0adSDong Aisheng 			} else {
6348b2bb0adSDong Aisheng 				v &= ~ESDHC_MIX_CTRL_EXE_TUNE;
6358b2bb0adSDong Aisheng 			}
6366e9fd28eSDong Aisheng 
637869f8a69SAdrian Hunter 			writel(v, host->ioaddr + SDHCI_AUTO_CMD_STATUS);
6386e9fd28eSDong Aisheng 			writel(m, host->ioaddr + ESDHC_MIX_CTRL);
6396e9fd28eSDong Aisheng 		}
6400322191eSDong Aisheng 		return;
64195f25efeSWolfram Sang 	case SDHCI_TRANSFER_MODE:
642f47c4bbfSShawn Guo 		if ((imx_data->socdata->flags & ESDHC_FLAG_MULTIBLK_NO_INT)
64358ac8177SRichard Zhu 				&& (host->cmd->opcode == SD_IO_RW_EXTENDED)
64458ac8177SRichard Zhu 				&& (host->cmd->data->blocks > 1)
64558ac8177SRichard Zhu 				&& (host->cmd->data->flags & MMC_DATA_READ)) {
64658ac8177SRichard Zhu 			u32 v;
64760bf6396SShawn Guo 			v = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
64860bf6396SShawn Guo 			v |= ESDHC_VENDOR_SPEC_SDIO_QUIRK;
64960bf6396SShawn Guo 			writel(v, host->ioaddr + ESDHC_VENDOR_SPEC);
65058ac8177SRichard Zhu 		}
65169f54698SShawn Guo 
6529d61c009SShawn Guo 		if (esdhc_is_usdhc(imx_data)) {
6533fbd4322SAndrew Gabbasov 			u32 wml;
65469f54698SShawn Guo 			u32 m = readl(host->ioaddr + ESDHC_MIX_CTRL);
6552a15f981SShawn Guo 			/* Swap AC23 bit */
6562a15f981SShawn Guo 			if (val & SDHCI_TRNS_AUTO_CMD23) {
6572a15f981SShawn Guo 				val &= ~SDHCI_TRNS_AUTO_CMD23;
6582a15f981SShawn Guo 				val |= ESDHC_MIX_CTRL_AC23EN;
6592a15f981SShawn Guo 			}
6602a15f981SShawn Guo 			m = val | (m & ~ESDHC_MIX_CTRL_SDHCI_MASK);
66169f54698SShawn Guo 			writel(m, host->ioaddr + ESDHC_MIX_CTRL);
6623fbd4322SAndrew Gabbasov 
6633fbd4322SAndrew Gabbasov 			/* Set watermark levels for PIO access to maximum value
6643fbd4322SAndrew Gabbasov 			 * (128 words) to accommodate full 512 bytes buffer.
6653fbd4322SAndrew Gabbasov 			 * For DMA access restore the levels to default value.
6663fbd4322SAndrew Gabbasov 			 */
6673fbd4322SAndrew Gabbasov 			m = readl(host->ioaddr + ESDHC_WTMK_LVL);
668e534b82fSHaibo Chen 			if (val & SDHCI_TRNS_DMA) {
6693fbd4322SAndrew Gabbasov 				wml = ESDHC_WTMK_LVL_WML_VAL_DEF;
670e534b82fSHaibo Chen 			} else {
671e534b82fSHaibo Chen 				u8 ctrl;
6723fbd4322SAndrew Gabbasov 				wml = ESDHC_WTMK_LVL_WML_VAL_MAX;
673e534b82fSHaibo Chen 
674e534b82fSHaibo Chen 				/*
675e534b82fSHaibo Chen 				 * Since already disable DMA mode, so also need
676e534b82fSHaibo Chen 				 * to clear the DMASEL. Otherwise, for standard
677e534b82fSHaibo Chen 				 * tuning, when send tuning command, usdhc will
678e534b82fSHaibo Chen 				 * still prefetch the ADMA script from wrong
679e534b82fSHaibo Chen 				 * DMA address, then we will see IOMMU report
680e534b82fSHaibo Chen 				 * some error which show lack of TLB mapping.
681e534b82fSHaibo Chen 				 */
682e534b82fSHaibo Chen 				ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
683e534b82fSHaibo Chen 				ctrl &= ~SDHCI_CTRL_DMA_MASK;
684e534b82fSHaibo Chen 				sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
685e534b82fSHaibo Chen 			}
6863fbd4322SAndrew Gabbasov 			m &= ~(ESDHC_WTMK_LVL_RD_WML_MASK |
6873fbd4322SAndrew Gabbasov 			       ESDHC_WTMK_LVL_WR_WML_MASK);
6883fbd4322SAndrew Gabbasov 			m |= (wml << ESDHC_WTMK_LVL_RD_WML_SHIFT) |
6893fbd4322SAndrew Gabbasov 			     (wml << ESDHC_WTMK_LVL_WR_WML_SHIFT);
6903fbd4322SAndrew Gabbasov 			writel(m, host->ioaddr + ESDHC_WTMK_LVL);
69169f54698SShawn Guo 		} else {
69269f54698SShawn Guo 			/*
69369f54698SShawn Guo 			 * Postpone this write, we must do it together with a
69469f54698SShawn Guo 			 * command write that is down below.
69569f54698SShawn Guo 			 */
696e149860dSRichard Zhu 			imx_data->scratchpad = val;
69769f54698SShawn Guo 		}
69895f25efeSWolfram Sang 		return;
69995f25efeSWolfram Sang 	case SDHCI_COMMAND:
700361b8482SLucas Stach 		if (host->cmd->opcode == MMC_STOP_TRANSMISSION)
70158ac8177SRichard Zhu 			val |= SDHCI_CMD_ABORTCMD;
70295a2482aSShawn Guo 
703361b8482SLucas Stach 		if ((host->cmd->opcode == MMC_SET_BLOCK_COUNT) &&
704f47c4bbfSShawn Guo 		    (imx_data->socdata->flags & ESDHC_FLAG_MULTIBLK_NO_INT))
705361b8482SLucas Stach 			imx_data->multiblock_status = MULTIBLK_IN_PROCESS;
706361b8482SLucas Stach 
7079d61c009SShawn Guo 		if (esdhc_is_usdhc(imx_data))
70895a2482aSShawn Guo 			writel(val << 16,
70995a2482aSShawn Guo 			       host->ioaddr + SDHCI_TRANSFER_MODE);
71069f54698SShawn Guo 		else
711e149860dSRichard Zhu 			writel(val << 16 | imx_data->scratchpad,
71295f25efeSWolfram Sang 			       host->ioaddr + SDHCI_TRANSFER_MODE);
71395f25efeSWolfram Sang 		return;
71495f25efeSWolfram Sang 	case SDHCI_BLOCK_SIZE:
71595f25efeSWolfram Sang 		val &= ~SDHCI_MAKE_BLKSZ(0x7, 0);
71695f25efeSWolfram Sang 		break;
71795f25efeSWolfram Sang 	}
71895f25efeSWolfram Sang 	esdhc_clrset_le(host, 0xffff, val, reg);
71995f25efeSWolfram Sang }
72095f25efeSWolfram Sang 
72177da3da0SAaron Brice static u8 esdhc_readb_le(struct sdhci_host *host, int reg)
72277da3da0SAaron Brice {
72377da3da0SAaron Brice 	u8 ret;
72477da3da0SAaron Brice 	u32 val;
72577da3da0SAaron Brice 
72677da3da0SAaron Brice 	switch (reg) {
72777da3da0SAaron Brice 	case SDHCI_HOST_CONTROL:
72877da3da0SAaron Brice 		val = readl(host->ioaddr + reg);
72977da3da0SAaron Brice 
73077da3da0SAaron Brice 		ret = val & SDHCI_CTRL_LED;
73177da3da0SAaron Brice 		ret |= (val >> 5) & SDHCI_CTRL_DMA_MASK;
73277da3da0SAaron Brice 		ret |= (val & ESDHC_CTRL_4BITBUS);
73377da3da0SAaron Brice 		ret |= (val & ESDHC_CTRL_8BITBUS) << 3;
73477da3da0SAaron Brice 		return ret;
73577da3da0SAaron Brice 	}
73677da3da0SAaron Brice 
73777da3da0SAaron Brice 	return readb(host->ioaddr + reg);
73877da3da0SAaron Brice }
73977da3da0SAaron Brice 
74095f25efeSWolfram Sang static void esdhc_writeb_le(struct sdhci_host *host, u8 val, int reg)
74195f25efeSWolfram Sang {
7429a0985b7SWilson Callan 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
743070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
74481a0a8bcSBenoît Thébaudeau 	u32 new_val = 0;
745af51079eSSascha Hauer 	u32 mask;
74695f25efeSWolfram Sang 
74795f25efeSWolfram Sang 	switch (reg) {
74895f25efeSWolfram Sang 	case SDHCI_POWER_CONTROL:
74995f25efeSWolfram Sang 		/*
75095f25efeSWolfram Sang 		 * FSL put some DMA bits here
75195f25efeSWolfram Sang 		 * If your board has a regulator, code should be here
75295f25efeSWolfram Sang 		 */
75395f25efeSWolfram Sang 		return;
75495f25efeSWolfram Sang 	case SDHCI_HOST_CONTROL:
7556b40d182SShawn Guo 		/* FSL messed up here, so we need to manually compose it. */
756af51079eSSascha Hauer 		new_val = val & SDHCI_CTRL_LED;
7577122bbb0SMasanari Iida 		/* ensure the endianness */
75895f25efeSWolfram Sang 		new_val |= ESDHC_HOST_CONTROL_LE;
7599a0985b7SWilson Callan 		/* bits 8&9 are reserved on mx25 */
7609a0985b7SWilson Callan 		if (!is_imx25_esdhc(imx_data)) {
76195f25efeSWolfram Sang 			/* DMA mode bits are shifted */
76295f25efeSWolfram Sang 			new_val |= (val & SDHCI_CTRL_DMA_MASK) << 5;
7639a0985b7SWilson Callan 		}
76495f25efeSWolfram Sang 
765af51079eSSascha Hauer 		/*
766af51079eSSascha Hauer 		 * Do not touch buswidth bits here. This is done in
767af51079eSSascha Hauer 		 * esdhc_pltfm_bus_width.
768f6825748SMartin Fuzzey 		 * Do not touch the D3CD bit either which is used for the
769d04f8d5bSBenoît Thébaudeau 		 * SDIO interrupt erratum workaround.
770af51079eSSascha Hauer 		 */
771f6825748SMartin Fuzzey 		mask = 0xffff & ~(ESDHC_CTRL_BUSWIDTH_MASK | ESDHC_CTRL_D3CD);
772af51079eSSascha Hauer 
773af51079eSSascha Hauer 		esdhc_clrset_le(host, mask, new_val, reg);
77495f25efeSWolfram Sang 		return;
77581a0a8bcSBenoît Thébaudeau 	case SDHCI_SOFTWARE_RESET:
77681a0a8bcSBenoît Thébaudeau 		if (val & SDHCI_RESET_DATA)
77781a0a8bcSBenoît Thébaudeau 			new_val = readl(host->ioaddr + SDHCI_HOST_CONTROL);
77881a0a8bcSBenoît Thébaudeau 		break;
77995f25efeSWolfram Sang 	}
78095f25efeSWolfram Sang 	esdhc_clrset_le(host, 0xff, val, reg);
781913413c3SShawn Guo 
78281a0a8bcSBenoît Thébaudeau 	if (reg == SDHCI_SOFTWARE_RESET) {
78381a0a8bcSBenoît Thébaudeau 		if (val & SDHCI_RESET_ALL) {
784913413c3SShawn Guo 			/*
78581a0a8bcSBenoît Thébaudeau 			 * The esdhc has a design violation to SDHC spec which
78681a0a8bcSBenoît Thébaudeau 			 * tells that software reset should not affect card
78781a0a8bcSBenoît Thébaudeau 			 * detection circuit. But esdhc clears its SYSCTL
78881a0a8bcSBenoît Thébaudeau 			 * register bits [0..2] during the software reset. This
78981a0a8bcSBenoît Thébaudeau 			 * will stop those clocks that card detection circuit
79081a0a8bcSBenoît Thébaudeau 			 * relies on. To work around it, we turn the clocks on
79181a0a8bcSBenoît Thébaudeau 			 * back to keep card detection circuit functional.
792913413c3SShawn Guo 			 */
793913413c3SShawn Guo 			esdhc_clrset_le(host, 0x7, 0x7, ESDHC_SYSTEM_CONTROL);
79458c8c4fbSShawn Guo 			/*
79558c8c4fbSShawn Guo 			 * The reset on usdhc fails to clear MIX_CTRL register.
79658c8c4fbSShawn Guo 			 * Do it manually here.
79758c8c4fbSShawn Guo 			 */
798de5bdbffSDong Aisheng 			if (esdhc_is_usdhc(imx_data)) {
79981a0a8bcSBenoît Thébaudeau 				/*
80081a0a8bcSBenoît Thébaudeau 				 * the tuning bits should be kept during reset
80181a0a8bcSBenoît Thébaudeau 				 */
802d131a71cSDong Aisheng 				new_val = readl(host->ioaddr + ESDHC_MIX_CTRL);
803d131a71cSDong Aisheng 				writel(new_val & ESDHC_MIX_CTRL_TUNING_MASK,
804d131a71cSDong Aisheng 						host->ioaddr + ESDHC_MIX_CTRL);
805de5bdbffSDong Aisheng 				imx_data->is_ddr = 0;
806de5bdbffSDong Aisheng 			}
80781a0a8bcSBenoît Thébaudeau 		} else if (val & SDHCI_RESET_DATA) {
80881a0a8bcSBenoît Thébaudeau 			/*
80981a0a8bcSBenoît Thébaudeau 			 * The eSDHC DAT line software reset clears at least the
81081a0a8bcSBenoît Thébaudeau 			 * data transfer width on i.MX25, so make sure that the
81181a0a8bcSBenoît Thébaudeau 			 * Host Control register is unaffected.
81281a0a8bcSBenoît Thébaudeau 			 */
81381a0a8bcSBenoît Thébaudeau 			esdhc_clrset_le(host, 0xff, new_val,
81481a0a8bcSBenoît Thébaudeau 					SDHCI_HOST_CONTROL);
81581a0a8bcSBenoît Thébaudeau 		}
81658c8c4fbSShawn Guo 	}
81795f25efeSWolfram Sang }
81895f25efeSWolfram Sang 
8190ddf03c9SLucas Stach static unsigned int esdhc_pltfm_get_max_clock(struct sdhci_host *host)
8200ddf03c9SLucas Stach {
8210ddf03c9SLucas Stach 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
8220ddf03c9SLucas Stach 
823a974862fSDong Aisheng 	return pltfm_host->clock;
8240ddf03c9SLucas Stach }
8250ddf03c9SLucas Stach 
82695f25efeSWolfram Sang static unsigned int esdhc_pltfm_get_min_clock(struct sdhci_host *host)
82795f25efeSWolfram Sang {
82895f25efeSWolfram Sang 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
82995f25efeSWolfram Sang 
830a974862fSDong Aisheng 	return pltfm_host->clock / 256 / 16;
83195f25efeSWolfram Sang }
83295f25efeSWolfram Sang 
8338ba9580aSLucas Stach static inline void esdhc_pltfm_set_clock(struct sdhci_host *host,
8348ba9580aSLucas Stach 					 unsigned int clock)
8358ba9580aSLucas Stach {
8368ba9580aSLucas Stach 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
837070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
838a974862fSDong Aisheng 	unsigned int host_clock = pltfm_host->clock;
8395143c953SBenoît Thébaudeau 	int ddr_pre_div = imx_data->is_ddr ? 2 : 1;
8405143c953SBenoît Thébaudeau 	int pre_div = 1;
841d31fc00aSDong Aisheng 	int div = 1;
842f581e909SHaibo Chen 	int ret;
843fed2f6e2SDong Aisheng 	u32 temp, val;
8448ba9580aSLucas Stach 
8459d61c009SShawn Guo 	if (esdhc_is_usdhc(imx_data)) {
846fed2f6e2SDong Aisheng 		val = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
847fed2f6e2SDong Aisheng 		writel(val & ~ESDHC_VENDOR_SPEC_FRC_SDCLK_ON,
848fed2f6e2SDong Aisheng 			host->ioaddr + ESDHC_VENDOR_SPEC);
849f581e909SHaibo Chen 		esdhc_wait_for_card_clock_gate_off(host);
850fed2f6e2SDong Aisheng 	}
85173e736f8SStefan Agner 
85273e736f8SStefan Agner 	if (clock == 0) {
85373e736f8SStefan Agner 		host->mmc->actual_clock = 0;
854373073efSRussell King 		return;
855fed2f6e2SDong Aisheng 	}
856d31fc00aSDong Aisheng 
857499ed50fSBenoît Thébaudeau 	/* For i.MX53 eSDHCv3, SYSCTL.SDCLKFS may not be set to 0. */
858499ed50fSBenoît Thébaudeau 	if (is_imx53_esdhc(imx_data)) {
859499ed50fSBenoît Thébaudeau 		/*
860499ed50fSBenoît Thébaudeau 		 * According to the i.MX53 reference manual, if DLLCTRL[10] can
861499ed50fSBenoît Thébaudeau 		 * be set, then the controller is eSDHCv3, else it is eSDHCv2.
862499ed50fSBenoît Thébaudeau 		 */
863499ed50fSBenoît Thébaudeau 		val = readl(host->ioaddr + ESDHC_DLL_CTRL);
864499ed50fSBenoît Thébaudeau 		writel(val | BIT(10), host->ioaddr + ESDHC_DLL_CTRL);
865499ed50fSBenoît Thébaudeau 		temp = readl(host->ioaddr + ESDHC_DLL_CTRL);
866499ed50fSBenoît Thébaudeau 		writel(val, host->ioaddr + ESDHC_DLL_CTRL);
867499ed50fSBenoît Thébaudeau 		if (temp & BIT(10))
868499ed50fSBenoît Thébaudeau 			pre_div = 2;
869499ed50fSBenoît Thébaudeau 	}
870499ed50fSBenoît Thébaudeau 
871d31fc00aSDong Aisheng 	temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
872d31fc00aSDong Aisheng 	temp &= ~(ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN
873d31fc00aSDong Aisheng 		| ESDHC_CLOCK_MASK);
874d31fc00aSDong Aisheng 	sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
875d31fc00aSDong Aisheng 
876af6a50d4SBOUGH CHEN 	if (imx_data->socdata->flags & ESDHC_FLAG_ERR010450) {
877af6a50d4SBOUGH CHEN 		unsigned int max_clock;
878af6a50d4SBOUGH CHEN 
879af6a50d4SBOUGH CHEN 		max_clock = imx_data->is_ddr ? 45000000 : 150000000;
880af6a50d4SBOUGH CHEN 
881af6a50d4SBOUGH CHEN 		clock = min(clock, max_clock);
882af6a50d4SBOUGH CHEN 	}
883af6a50d4SBOUGH CHEN 
8845143c953SBenoît Thébaudeau 	while (host_clock / (16 * pre_div * ddr_pre_div) > clock &&
8855143c953SBenoît Thébaudeau 			pre_div < 256)
886d31fc00aSDong Aisheng 		pre_div *= 2;
887d31fc00aSDong Aisheng 
8885143c953SBenoît Thébaudeau 	while (host_clock / (div * pre_div * ddr_pre_div) > clock && div < 16)
889d31fc00aSDong Aisheng 		div++;
890d31fc00aSDong Aisheng 
8915143c953SBenoît Thébaudeau 	host->mmc->actual_clock = host_clock / (div * pre_div * ddr_pre_div);
892d31fc00aSDong Aisheng 	dev_dbg(mmc_dev(host->mmc), "desired SD clock: %d, actual: %d\n",
893e76b8559SDong Aisheng 		clock, host->mmc->actual_clock);
894d31fc00aSDong Aisheng 
895d31fc00aSDong Aisheng 	pre_div >>= 1;
896d31fc00aSDong Aisheng 	div--;
897d31fc00aSDong Aisheng 
898d31fc00aSDong Aisheng 	temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
899d31fc00aSDong Aisheng 	temp |= (ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN
900d31fc00aSDong Aisheng 		| (div << ESDHC_DIVIDER_SHIFT)
901d31fc00aSDong Aisheng 		| (pre_div << ESDHC_PREDIV_SHIFT));
902d31fc00aSDong Aisheng 	sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
903fed2f6e2SDong Aisheng 
904f581e909SHaibo Chen 	/* need to wait the bit 3 of the PRSSTAT to be set, make sure card clock is stable */
905f581e909SHaibo Chen 	ret = readl_poll_timeout(host->ioaddr + ESDHC_PRSSTAT, temp,
906f581e909SHaibo Chen 				(temp & ESDHC_CLOCK_STABLE), 2, 100);
907f581e909SHaibo Chen 	if (ret == -ETIMEDOUT)
908f581e909SHaibo Chen 		dev_warn(mmc_dev(host->mmc), "card clock still not stable in 100us!.\n");
909f581e909SHaibo Chen 
9109d61c009SShawn Guo 	if (esdhc_is_usdhc(imx_data)) {
911fed2f6e2SDong Aisheng 		val = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
912fed2f6e2SDong Aisheng 		writel(val | ESDHC_VENDOR_SPEC_FRC_SDCLK_ON,
913fed2f6e2SDong Aisheng 			host->ioaddr + ESDHC_VENDOR_SPEC);
914fed2f6e2SDong Aisheng 	}
915fed2f6e2SDong Aisheng 
9168ba9580aSLucas Stach }
9178ba9580aSLucas Stach 
918913413c3SShawn Guo static unsigned int esdhc_pltfm_get_ro(struct sdhci_host *host)
919913413c3SShawn Guo {
920842afc02SShawn Guo 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
921070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
922842afc02SShawn Guo 	struct esdhc_platform_data *boarddata = &imx_data->boarddata;
923913413c3SShawn Guo 
924913413c3SShawn Guo 	switch (boarddata->wp_type) {
925913413c3SShawn Guo 	case ESDHC_WP_GPIO:
926fbe5fdd1SShawn Guo 		return mmc_gpio_get_ro(host->mmc);
927913413c3SShawn Guo 	case ESDHC_WP_CONTROLLER:
928913413c3SShawn Guo 		return !(readl(host->ioaddr + SDHCI_PRESENT_STATE) &
929913413c3SShawn Guo 			       SDHCI_WRITE_PROTECT);
930913413c3SShawn Guo 	case ESDHC_WP_NONE:
931913413c3SShawn Guo 		break;
932913413c3SShawn Guo 	}
933913413c3SShawn Guo 
934913413c3SShawn Guo 	return -ENOSYS;
935913413c3SShawn Guo }
936913413c3SShawn Guo 
9372317f56cSRussell King static void esdhc_pltfm_set_bus_width(struct sdhci_host *host, int width)
938af51079eSSascha Hauer {
939af51079eSSascha Hauer 	u32 ctrl;
940af51079eSSascha Hauer 
941af51079eSSascha Hauer 	switch (width) {
942af51079eSSascha Hauer 	case MMC_BUS_WIDTH_8:
943af51079eSSascha Hauer 		ctrl = ESDHC_CTRL_8BITBUS;
944af51079eSSascha Hauer 		break;
945af51079eSSascha Hauer 	case MMC_BUS_WIDTH_4:
946af51079eSSascha Hauer 		ctrl = ESDHC_CTRL_4BITBUS;
947af51079eSSascha Hauer 		break;
948af51079eSSascha Hauer 	default:
949af51079eSSascha Hauer 		ctrl = 0;
950af51079eSSascha Hauer 		break;
951af51079eSSascha Hauer 	}
952af51079eSSascha Hauer 
953af51079eSSascha Hauer 	esdhc_clrset_le(host, ESDHC_CTRL_BUSWIDTH_MASK, ctrl,
954af51079eSSascha Hauer 			SDHCI_HOST_CONTROL);
955af51079eSSascha Hauer }
956af51079eSSascha Hauer 
957de3e1dd0SBOUGH CHEN static int usdhc_execute_tuning(struct mmc_host *mmc, u32 opcode)
958de3e1dd0SBOUGH CHEN {
959de3e1dd0SBOUGH CHEN 	struct sdhci_host *host = mmc_priv(mmc);
960de3e1dd0SBOUGH CHEN 
961de3e1dd0SBOUGH CHEN 	/*
962de3e1dd0SBOUGH CHEN 	 * i.MX uSDHC internally already uses a fixed optimized timing for
963de3e1dd0SBOUGH CHEN 	 * DDR50, normally does not require tuning for DDR50 mode.
964de3e1dd0SBOUGH CHEN 	 */
965de3e1dd0SBOUGH CHEN 	if (host->timing == MMC_TIMING_UHS_DDR50)
966de3e1dd0SBOUGH CHEN 		return 0;
967de3e1dd0SBOUGH CHEN 
968de3e1dd0SBOUGH CHEN 	return sdhci_execute_tuning(mmc, opcode);
969de3e1dd0SBOUGH CHEN }
970de3e1dd0SBOUGH CHEN 
9710322191eSDong Aisheng static void esdhc_prepare_tuning(struct sdhci_host *host, u32 val)
9720322191eSDong Aisheng {
9730322191eSDong Aisheng 	u32 reg;
9740ac4f496SHaibo Chen 	u8 sw_rst;
9750ac4f496SHaibo Chen 	int ret;
9760322191eSDong Aisheng 
9770322191eSDong Aisheng 	/* FIXME: delay a bit for card to be ready for next tuning due to errors */
9780322191eSDong Aisheng 	mdelay(1);
9790322191eSDong Aisheng 
9800ac4f496SHaibo Chen 	/* IC suggest to reset USDHC before every tuning command */
9810ac4f496SHaibo Chen 	esdhc_clrset_le(host, 0xff, SDHCI_RESET_ALL, SDHCI_SOFTWARE_RESET);
9820ac4f496SHaibo Chen 	ret = readb_poll_timeout(host->ioaddr + SDHCI_SOFTWARE_RESET, sw_rst,
9830ac4f496SHaibo Chen 				!(sw_rst & SDHCI_RESET_ALL), 10, 100);
9840ac4f496SHaibo Chen 	if (ret == -ETIMEDOUT)
9850ac4f496SHaibo Chen 		dev_warn(mmc_dev(host->mmc),
9860ac4f496SHaibo Chen 		"warning! RESET_ALL never complete before sending tuning command\n");
9870ac4f496SHaibo Chen 
9880322191eSDong Aisheng 	reg = readl(host->ioaddr + ESDHC_MIX_CTRL);
9890322191eSDong Aisheng 	reg |= ESDHC_MIX_CTRL_EXE_TUNE | ESDHC_MIX_CTRL_SMPCLK_SEL |
9900322191eSDong Aisheng 			ESDHC_MIX_CTRL_FBCLK_SEL;
9910322191eSDong Aisheng 	writel(reg, host->ioaddr + ESDHC_MIX_CTRL);
9920322191eSDong Aisheng 	writel(val << 8, host->ioaddr + ESDHC_TUNE_CTRL_STATUS);
9930322191eSDong Aisheng 	dev_dbg(mmc_dev(host->mmc),
994d04f8d5bSBenoît Thébaudeau 		"tuning with delay 0x%x ESDHC_TUNE_CTRL_STATUS 0x%x\n",
9950322191eSDong Aisheng 			val, readl(host->ioaddr + ESDHC_TUNE_CTRL_STATUS));
9960322191eSDong Aisheng }
9970322191eSDong Aisheng 
9980322191eSDong Aisheng static void esdhc_post_tuning(struct sdhci_host *host)
9990322191eSDong Aisheng {
10000322191eSDong Aisheng 	u32 reg;
10010322191eSDong Aisheng 
10020322191eSDong Aisheng 	reg = readl(host->ioaddr + ESDHC_MIX_CTRL);
10030322191eSDong Aisheng 	reg &= ~ESDHC_MIX_CTRL_EXE_TUNE;
1004da0295ffSDong Aisheng 	reg |= ESDHC_MIX_CTRL_AUTO_TUNE_EN;
10050322191eSDong Aisheng 	writel(reg, host->ioaddr + ESDHC_MIX_CTRL);
10060322191eSDong Aisheng }
10070322191eSDong Aisheng 
10080322191eSDong Aisheng static int esdhc_executing_tuning(struct sdhci_host *host, u32 opcode)
10090322191eSDong Aisheng {
10100322191eSDong Aisheng 	int min, max, avg, ret;
10110322191eSDong Aisheng 
10120322191eSDong Aisheng 	/* find the mininum delay first which can pass tuning */
10130322191eSDong Aisheng 	min = ESDHC_TUNE_CTRL_MIN;
10140322191eSDong Aisheng 	while (min < ESDHC_TUNE_CTRL_MAX) {
10150322191eSDong Aisheng 		esdhc_prepare_tuning(host, min);
10169979dbe5SChaotian Jing 		if (!mmc_send_tuning(host->mmc, opcode, NULL))
10170322191eSDong Aisheng 			break;
10180322191eSDong Aisheng 		min += ESDHC_TUNE_CTRL_STEP;
10190322191eSDong Aisheng 	}
10200322191eSDong Aisheng 
10210322191eSDong Aisheng 	/* find the maxinum delay which can not pass tuning */
10220322191eSDong Aisheng 	max = min + ESDHC_TUNE_CTRL_STEP;
10230322191eSDong Aisheng 	while (max < ESDHC_TUNE_CTRL_MAX) {
10240322191eSDong Aisheng 		esdhc_prepare_tuning(host, max);
10259979dbe5SChaotian Jing 		if (mmc_send_tuning(host->mmc, opcode, NULL)) {
10260322191eSDong Aisheng 			max -= ESDHC_TUNE_CTRL_STEP;
10270322191eSDong Aisheng 			break;
10280322191eSDong Aisheng 		}
10290322191eSDong Aisheng 		max += ESDHC_TUNE_CTRL_STEP;
10300322191eSDong Aisheng 	}
10310322191eSDong Aisheng 
10320322191eSDong Aisheng 	/* use average delay to get the best timing */
10330322191eSDong Aisheng 	avg = (min + max) / 2;
10340322191eSDong Aisheng 	esdhc_prepare_tuning(host, avg);
10359979dbe5SChaotian Jing 	ret = mmc_send_tuning(host->mmc, opcode, NULL);
10360322191eSDong Aisheng 	esdhc_post_tuning(host);
10370322191eSDong Aisheng 
1038d04f8d5bSBenoît Thébaudeau 	dev_dbg(mmc_dev(host->mmc), "tuning %s at 0x%x ret %d\n",
10390322191eSDong Aisheng 		ret ? "failed" : "passed", avg, ret);
10400322191eSDong Aisheng 
10410322191eSDong Aisheng 	return ret;
10420322191eSDong Aisheng }
10430322191eSDong Aisheng 
1044029e2476SBOUGH CHEN static void esdhc_hs400_enhanced_strobe(struct mmc_host *mmc, struct mmc_ios *ios)
1045029e2476SBOUGH CHEN {
1046029e2476SBOUGH CHEN 	struct sdhci_host *host = mmc_priv(mmc);
1047029e2476SBOUGH CHEN 	u32 m;
1048029e2476SBOUGH CHEN 
1049029e2476SBOUGH CHEN 	m = readl(host->ioaddr + ESDHC_MIX_CTRL);
1050029e2476SBOUGH CHEN 	if (ios->enhanced_strobe)
1051029e2476SBOUGH CHEN 		m |= ESDHC_MIX_CTRL_HS400_ES_EN;
1052029e2476SBOUGH CHEN 	else
1053029e2476SBOUGH CHEN 		m &= ~ESDHC_MIX_CTRL_HS400_ES_EN;
1054029e2476SBOUGH CHEN 	writel(m, host->ioaddr + ESDHC_MIX_CTRL);
1055029e2476SBOUGH CHEN }
1056029e2476SBOUGH CHEN 
1057ad93220dSDong Aisheng static int esdhc_change_pinstate(struct sdhci_host *host,
1058ad93220dSDong Aisheng 						unsigned int uhs)
1059ad93220dSDong Aisheng {
1060ad93220dSDong Aisheng 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1061070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
1062ad93220dSDong Aisheng 	struct pinctrl_state *pinctrl;
1063ad93220dSDong Aisheng 
1064ad93220dSDong Aisheng 	dev_dbg(mmc_dev(host->mmc), "change pinctrl state for uhs %d\n", uhs);
1065ad93220dSDong Aisheng 
1066ad93220dSDong Aisheng 	if (IS_ERR(imx_data->pinctrl) ||
1067ad93220dSDong Aisheng 		IS_ERR(imx_data->pins_100mhz) ||
1068ad93220dSDong Aisheng 		IS_ERR(imx_data->pins_200mhz))
1069ad93220dSDong Aisheng 		return -EINVAL;
1070ad93220dSDong Aisheng 
1071ad93220dSDong Aisheng 	switch (uhs) {
1072ad93220dSDong Aisheng 	case MMC_TIMING_UHS_SDR50:
10739f327845SHaibo Chen 	case MMC_TIMING_UHS_DDR50:
1074ad93220dSDong Aisheng 		pinctrl = imx_data->pins_100mhz;
1075ad93220dSDong Aisheng 		break;
1076ad93220dSDong Aisheng 	case MMC_TIMING_UHS_SDR104:
1077429a5b45SDong Aisheng 	case MMC_TIMING_MMC_HS200:
107828b07674SHaibo Chen 	case MMC_TIMING_MMC_HS400:
1079ad93220dSDong Aisheng 		pinctrl = imx_data->pins_200mhz;
1080ad93220dSDong Aisheng 		break;
1081ad93220dSDong Aisheng 	default:
1082ad93220dSDong Aisheng 		/* back to default state for other legacy timing */
10832480b720SUlf Hansson 		return pinctrl_select_default_state(mmc_dev(host->mmc));
1084ad93220dSDong Aisheng 	}
1085ad93220dSDong Aisheng 
1086ad93220dSDong Aisheng 	return pinctrl_select_state(imx_data->pinctrl, pinctrl);
1087ad93220dSDong Aisheng }
1088ad93220dSDong Aisheng 
108928b07674SHaibo Chen /*
1090d04f8d5bSBenoît Thébaudeau  * For HS400 eMMC, there is a data_strobe line. This signal is generated
109128b07674SHaibo Chen  * by the device and used for data output and CRC status response output
109228b07674SHaibo Chen  * in HS400 mode. The frequency of this signal follows the frequency of
1093d04f8d5bSBenoît Thébaudeau  * CLK generated by host. The host receives the data which is aligned to the
109428b07674SHaibo Chen  * edge of data_strobe line. Due to the time delay between CLK line and
109528b07674SHaibo Chen  * data_strobe line, if the delay time is larger than one clock cycle,
1096d04f8d5bSBenoît Thébaudeau  * then CLK and data_strobe line will be misaligned, read error shows up.
109728b07674SHaibo Chen  */
109828b07674SHaibo Chen static void esdhc_set_strobe_dll(struct sdhci_host *host)
109928b07674SHaibo Chen {
11005bd2acdcSHaibo Chen 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
11015bd2acdcSHaibo Chen 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
11025bd2acdcSHaibo Chen 	u32 strobe_delay;
110328b07674SHaibo Chen 	u32 v;
1104373e800bSHaibo Chen 	int ret;
110528b07674SHaibo Chen 
11067ac6da26SDong Aisheng 	/* disable clock before enabling strobe dll */
11077ac6da26SDong Aisheng 	writel(readl(host->ioaddr + ESDHC_VENDOR_SPEC) &
11087ac6da26SDong Aisheng 		~ESDHC_VENDOR_SPEC_FRC_SDCLK_ON,
11097ac6da26SDong Aisheng 		host->ioaddr + ESDHC_VENDOR_SPEC);
1110f581e909SHaibo Chen 	esdhc_wait_for_card_clock_gate_off(host);
11117ac6da26SDong Aisheng 
111228b07674SHaibo Chen 	/* force a reset on strobe dll */
111328b07674SHaibo Chen 	writel(ESDHC_STROBE_DLL_CTRL_RESET,
111428b07674SHaibo Chen 		host->ioaddr + ESDHC_STROBE_DLL_CTRL);
11152eaf5a53SBOUGH CHEN 	/* clear the reset bit on strobe dll before any setting */
11162eaf5a53SBOUGH CHEN 	writel(0, host->ioaddr + ESDHC_STROBE_DLL_CTRL);
11172eaf5a53SBOUGH CHEN 
111828b07674SHaibo Chen 	/*
111928b07674SHaibo Chen 	 * enable strobe dll ctrl and adjust the delay target
112028b07674SHaibo Chen 	 * for the uSDHC loopback read clock
112128b07674SHaibo Chen 	 */
11225bd2acdcSHaibo Chen 	if (imx_data->boarddata.strobe_dll_delay_target)
11235bd2acdcSHaibo Chen 		strobe_delay = imx_data->boarddata.strobe_dll_delay_target;
11245bd2acdcSHaibo Chen 	else
11255bd2acdcSHaibo Chen 		strobe_delay = ESDHC_STROBE_DLL_CTRL_SLV_DLY_TARGET_DEFAULT;
112628b07674SHaibo Chen 	v = ESDHC_STROBE_DLL_CTRL_ENABLE |
11272eaf5a53SBOUGH CHEN 		ESDHC_STROBE_DLL_CTRL_SLV_UPDATE_INT_DEFAULT |
11285bd2acdcSHaibo Chen 		(strobe_delay << ESDHC_STROBE_DLL_CTRL_SLV_DLY_TARGET_SHIFT);
112928b07674SHaibo Chen 	writel(v, host->ioaddr + ESDHC_STROBE_DLL_CTRL);
1130373e800bSHaibo Chen 
1131373e800bSHaibo Chen 	/* wait max 50us to get the REF/SLV lock */
1132373e800bSHaibo Chen 	ret = readl_poll_timeout(host->ioaddr + ESDHC_STROBE_DLL_STATUS, v,
1133373e800bSHaibo Chen 		((v & ESDHC_STROBE_DLL_STS_REF_LOCK) && (v & ESDHC_STROBE_DLL_STS_SLV_LOCK)), 1, 50);
1134373e800bSHaibo Chen 	if (ret == -ETIMEDOUT)
113528b07674SHaibo Chen 		dev_warn(mmc_dev(host->mmc),
1136373e800bSHaibo Chen 		"warning! HS400 strobe DLL status REF/SLV not lock in 50us, STROBE DLL status is %x!\n", v);
113728b07674SHaibo Chen }
113828b07674SHaibo Chen 
1139d9370424SHaibo Chen static void esdhc_reset_tuning(struct sdhci_host *host)
1140d9370424SHaibo Chen {
1141d9370424SHaibo Chen 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1142d9370424SHaibo Chen 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
1143d9370424SHaibo Chen 	u32 ctrl;
1144d9370424SHaibo Chen 
1145d04f8d5bSBenoît Thébaudeau 	/* Reset the tuning circuit */
1146d9370424SHaibo Chen 	if (esdhc_is_usdhc(imx_data)) {
1147d9370424SHaibo Chen 		if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING) {
1148d9370424SHaibo Chen 			ctrl = readl(host->ioaddr + ESDHC_MIX_CTRL);
1149d9370424SHaibo Chen 			ctrl &= ~ESDHC_MIX_CTRL_SMPCLK_SEL;
1150d9370424SHaibo Chen 			ctrl &= ~ESDHC_MIX_CTRL_FBCLK_SEL;
1151d9370424SHaibo Chen 			writel(ctrl, host->ioaddr + ESDHC_MIX_CTRL);
1152d9370424SHaibo Chen 			writel(0, host->ioaddr + ESDHC_TUNE_CTRL_STATUS);
1153d9370424SHaibo Chen 		} else if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) {
1154869f8a69SAdrian Hunter 			ctrl = readl(host->ioaddr + SDHCI_AUTO_CMD_STATUS);
1155d9370424SHaibo Chen 			ctrl &= ~ESDHC_MIX_CTRL_SMPCLK_SEL;
1156869f8a69SAdrian Hunter 			writel(ctrl, host->ioaddr + SDHCI_AUTO_CMD_STATUS);
1157d9370424SHaibo Chen 		}
1158d9370424SHaibo Chen 	}
1159d9370424SHaibo Chen }
1160d9370424SHaibo Chen 
1161850a29b8SRussell King static void esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned timing)
1162ad93220dSDong Aisheng {
116328b07674SHaibo Chen 	u32 m;
1164ad93220dSDong Aisheng 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1165070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
1166602519b2SDong Aisheng 	struct esdhc_platform_data *boarddata = &imx_data->boarddata;
1167ad93220dSDong Aisheng 
116828b07674SHaibo Chen 	/* disable ddr mode and disable HS400 mode */
116928b07674SHaibo Chen 	m = readl(host->ioaddr + ESDHC_MIX_CTRL);
117028b07674SHaibo Chen 	m &= ~(ESDHC_MIX_CTRL_DDREN | ESDHC_MIX_CTRL_HS400_EN);
117128b07674SHaibo Chen 	imx_data->is_ddr = 0;
117228b07674SHaibo Chen 
1173850a29b8SRussell King 	switch (timing) {
1174ad93220dSDong Aisheng 	case MMC_TIMING_UHS_SDR12:
1175ad93220dSDong Aisheng 	case MMC_TIMING_UHS_SDR25:
1176ad93220dSDong Aisheng 	case MMC_TIMING_UHS_SDR50:
1177ad93220dSDong Aisheng 	case MMC_TIMING_UHS_SDR104:
1178de0a0decSBOUGH CHEN 	case MMC_TIMING_MMC_HS:
1179429a5b45SDong Aisheng 	case MMC_TIMING_MMC_HS200:
118028b07674SHaibo Chen 		writel(m, host->ioaddr + ESDHC_MIX_CTRL);
1181ad93220dSDong Aisheng 		break;
1182ad93220dSDong Aisheng 	case MMC_TIMING_UHS_DDR50:
118369f5bf38SAisheng Dong 	case MMC_TIMING_MMC_DDR52:
118428b07674SHaibo Chen 		m |= ESDHC_MIX_CTRL_DDREN;
118528b07674SHaibo Chen 		writel(m, host->ioaddr + ESDHC_MIX_CTRL);
1186de5bdbffSDong Aisheng 		imx_data->is_ddr = 1;
1187602519b2SDong Aisheng 		if (boarddata->delay_line) {
1188602519b2SDong Aisheng 			u32 v;
1189602519b2SDong Aisheng 			v = boarddata->delay_line <<
1190602519b2SDong Aisheng 				ESDHC_DLL_OVERRIDE_VAL_SHIFT |
1191602519b2SDong Aisheng 				(1 << ESDHC_DLL_OVERRIDE_EN_SHIFT);
1192602519b2SDong Aisheng 			if (is_imx53_esdhc(imx_data))
1193602519b2SDong Aisheng 				v <<= 1;
1194602519b2SDong Aisheng 			writel(v, host->ioaddr + ESDHC_DLL_CTRL);
1195602519b2SDong Aisheng 		}
1196ad93220dSDong Aisheng 		break;
119728b07674SHaibo Chen 	case MMC_TIMING_MMC_HS400:
119828b07674SHaibo Chen 		m |= ESDHC_MIX_CTRL_DDREN | ESDHC_MIX_CTRL_HS400_EN;
119928b07674SHaibo Chen 		writel(m, host->ioaddr + ESDHC_MIX_CTRL);
120028b07674SHaibo Chen 		imx_data->is_ddr = 1;
12017ac6da26SDong Aisheng 		/* update clock after enable DDR for strobe DLL lock */
12027ac6da26SDong Aisheng 		host->ops->set_clock(host, host->clock);
120328b07674SHaibo Chen 		esdhc_set_strobe_dll(host);
120428b07674SHaibo Chen 		break;
1205d9370424SHaibo Chen 	case MMC_TIMING_LEGACY:
1206d9370424SHaibo Chen 	default:
1207d9370424SHaibo Chen 		esdhc_reset_tuning(host);
1208d9370424SHaibo Chen 		break;
1209ad93220dSDong Aisheng 	}
1210ad93220dSDong Aisheng 
1211850a29b8SRussell King 	esdhc_change_pinstate(host, timing);
1212ad93220dSDong Aisheng }
1213ad93220dSDong Aisheng 
12140718e59aSRussell King static void esdhc_reset(struct sdhci_host *host, u8 mask)
12150718e59aSRussell King {
12160718e59aSRussell King 	sdhci_reset(host, mask);
12170718e59aSRussell King 
12180718e59aSRussell King 	sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
12190718e59aSRussell King 	sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
12200718e59aSRussell King }
12210718e59aSRussell King 
122210fd0ad9SAisheng Dong static unsigned int esdhc_get_max_timeout_count(struct sdhci_host *host)
122310fd0ad9SAisheng Dong {
122410fd0ad9SAisheng Dong 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1225070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
122610fd0ad9SAisheng Dong 
1227d04f8d5bSBenoît Thébaudeau 	/* Doc Erratum: the uSDHC actual maximum timeout count is 1 << 29 */
12282fb0b02bSHaibo Chen 	return esdhc_is_usdhc(imx_data) ? 1 << 29 : 1 << 27;
122910fd0ad9SAisheng Dong }
123010fd0ad9SAisheng Dong 
1231e33eb8e2SAisheng Dong static void esdhc_set_timeout(struct sdhci_host *host, struct mmc_command *cmd)
1232e33eb8e2SAisheng Dong {
1233e33eb8e2SAisheng Dong 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1234070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
1235e33eb8e2SAisheng Dong 
1236e33eb8e2SAisheng Dong 	/* use maximum timeout counter */
1237a215186dSHaibo Chen 	esdhc_clrset_le(host, ESDHC_SYS_CTRL_DTOCV_MASK,
1238a215186dSHaibo Chen 			esdhc_is_usdhc(imx_data) ? 0xF : 0xE,
1239e33eb8e2SAisheng Dong 			SDHCI_TIMEOUT_CONTROL);
1240e33eb8e2SAisheng Dong }
1241e33eb8e2SAisheng Dong 
1242bb6e3581SBOUGH CHEN static u32 esdhc_cqhci_irq(struct sdhci_host *host, u32 intmask)
1243bb6e3581SBOUGH CHEN {
1244bb6e3581SBOUGH CHEN 	int cmd_error = 0;
1245bb6e3581SBOUGH CHEN 	int data_error = 0;
1246bb6e3581SBOUGH CHEN 
1247bb6e3581SBOUGH CHEN 	if (!sdhci_cqe_irq(host, intmask, &cmd_error, &data_error))
1248bb6e3581SBOUGH CHEN 		return intmask;
1249bb6e3581SBOUGH CHEN 
1250bb6e3581SBOUGH CHEN 	cqhci_irq(host->mmc, intmask, cmd_error, data_error);
1251bb6e3581SBOUGH CHEN 
1252bb6e3581SBOUGH CHEN 	return 0;
1253bb6e3581SBOUGH CHEN }
1254bb6e3581SBOUGH CHEN 
12556e9fd28eSDong Aisheng static struct sdhci_ops sdhci_esdhc_ops = {
1256e149860dSRichard Zhu 	.read_l = esdhc_readl_le,
12570c6d49ceSWolfram Sang 	.read_w = esdhc_readw_le,
125877da3da0SAaron Brice 	.read_b = esdhc_readb_le,
1259e149860dSRichard Zhu 	.write_l = esdhc_writel_le,
12600c6d49ceSWolfram Sang 	.write_w = esdhc_writew_le,
12610c6d49ceSWolfram Sang 	.write_b = esdhc_writeb_le,
12628ba9580aSLucas Stach 	.set_clock = esdhc_pltfm_set_clock,
12630ddf03c9SLucas Stach 	.get_max_clock = esdhc_pltfm_get_max_clock,
12640c6d49ceSWolfram Sang 	.get_min_clock = esdhc_pltfm_get_min_clock,
126510fd0ad9SAisheng Dong 	.get_max_timeout_count = esdhc_get_max_timeout_count,
1266913413c3SShawn Guo 	.get_ro = esdhc_pltfm_get_ro,
1267e33eb8e2SAisheng Dong 	.set_timeout = esdhc_set_timeout,
12682317f56cSRussell King 	.set_bus_width = esdhc_pltfm_set_bus_width,
1269ad93220dSDong Aisheng 	.set_uhs_signaling = esdhc_set_uhs_signaling,
12700718e59aSRussell King 	.reset = esdhc_reset,
1271bb6e3581SBOUGH CHEN 	.irq = esdhc_cqhci_irq,
12723722c74cSHaibo Chen 	.dump_vendor_regs = esdhc_dump_debug_regs,
12730c6d49ceSWolfram Sang };
12740c6d49ceSWolfram Sang 
12751db5eebfSLars-Peter Clausen static const struct sdhci_pltfm_data sdhci_esdhc_imx_pdata = {
127697e4ba6aSRichard Zhu 	.quirks = ESDHC_DEFAULT_QUIRKS | SDHCI_QUIRK_NO_HISPD_BIT
127797e4ba6aSRichard Zhu 			| SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC
127897e4ba6aSRichard Zhu 			| SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC
127985d6509dSShawn Guo 			| SDHCI_QUIRK_BROKEN_CARD_DETECTION,
128085d6509dSShawn Guo 	.ops = &sdhci_esdhc_ops,
128185d6509dSShawn Guo };
128285d6509dSShawn Guo 
1283f3f5cf3dSDong Aisheng static void sdhci_esdhc_imx_hwinit(struct sdhci_host *host)
1284f3f5cf3dSDong Aisheng {
1285f3f5cf3dSDong Aisheng 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1286f3f5cf3dSDong Aisheng 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
1287982cf37dSHaibo Chen 	struct cqhci_host *cq_host = host->mmc->cqe_private;
12882b16cf32SDong Aisheng 	int tmp;
1289f3f5cf3dSDong Aisheng 
1290f3f5cf3dSDong Aisheng 	if (esdhc_is_usdhc(imx_data)) {
1291f3f5cf3dSDong Aisheng 		/*
1292f3f5cf3dSDong Aisheng 		 * The imx6q ROM code will change the default watermark
1293f3f5cf3dSDong Aisheng 		 * level setting to something insane.  Change it back here.
1294f3f5cf3dSDong Aisheng 		 */
1295f3f5cf3dSDong Aisheng 		writel(ESDHC_WTMK_DEFAULT_VAL, host->ioaddr + ESDHC_WTMK_LVL);
1296f3f5cf3dSDong Aisheng 
1297f3f5cf3dSDong Aisheng 		/*
1298f3f5cf3dSDong Aisheng 		 * ROM code will change the bit burst_length_enable setting
1299d04f8d5bSBenoît Thébaudeau 		 * to zero if this usdhc is chosen to boot system. Change
1300f3f5cf3dSDong Aisheng 		 * it back here, otherwise it will impact the performance a
1301f3f5cf3dSDong Aisheng 		 * lot. This bit is used to enable/disable the burst length
1302d04f8d5bSBenoît Thébaudeau 		 * for the external AHB2AXI bridge. It's useful especially
1303f3f5cf3dSDong Aisheng 		 * for INCR transfer because without burst length indicator,
1304f3f5cf3dSDong Aisheng 		 * the AHB2AXI bridge does not know the burst length in
1305f3f5cf3dSDong Aisheng 		 * advance. And without burst length indicator, AHB INCR
1306f3f5cf3dSDong Aisheng 		 * transfer can only be converted to singles on the AXI side.
1307f3f5cf3dSDong Aisheng 		 */
1308f3f5cf3dSDong Aisheng 		writel(readl(host->ioaddr + SDHCI_HOST_CONTROL)
1309f3f5cf3dSDong Aisheng 			| ESDHC_BURST_LEN_EN_INCR,
1310f3f5cf3dSDong Aisheng 			host->ioaddr + SDHCI_HOST_CONTROL);
1311e30be063SBOUGH CHEN 
1312f3f5cf3dSDong Aisheng 		/*
1313d04f8d5bSBenoît Thébaudeau 		 * erratum ESDHC_FLAG_ERR004536 fix for MX6Q TO1.2 and MX6DL
1314f3f5cf3dSDong Aisheng 		 * TO1.1, it's harmless for MX6SL
1315f3f5cf3dSDong Aisheng 		 */
1316e30be063SBOUGH CHEN 		writel(readl(host->ioaddr + 0x6c) & ~BIT(7),
1317f3f5cf3dSDong Aisheng 			host->ioaddr + 0x6c);
1318f3f5cf3dSDong Aisheng 
1319f3f5cf3dSDong Aisheng 		/* disable DLL_CTRL delay line settings */
1320f3f5cf3dSDong Aisheng 		writel(0x0, host->ioaddr + ESDHC_DLL_CTRL);
13212b16cf32SDong Aisheng 
1322bcdb5301SBOUGH CHEN 		/*
1323bcdb5301SBOUGH CHEN 		 * For the case of command with busy, if set the bit
1324bcdb5301SBOUGH CHEN 		 * ESDHC_VEND_SPEC2_EN_BUSY_IRQ, USDHC will generate a
1325bcdb5301SBOUGH CHEN 		 * transfer complete interrupt when busy is deasserted.
1326bcdb5301SBOUGH CHEN 		 * When CQHCI use DCMD to send a CMD need R1b respons,
1327bcdb5301SBOUGH CHEN 		 * CQHCI require to set ESDHC_VEND_SPEC2_EN_BUSY_IRQ,
1328bcdb5301SBOUGH CHEN 		 * otherwise DCMD will always meet timeout waiting for
1329bcdb5301SBOUGH CHEN 		 * hardware interrupt issue.
1330bcdb5301SBOUGH CHEN 		 */
1331bcdb5301SBOUGH CHEN 		if (imx_data->socdata->flags & ESDHC_FLAG_CQHCI) {
1332bcdb5301SBOUGH CHEN 			tmp = readl(host->ioaddr + ESDHC_VEND_SPEC2);
1333bcdb5301SBOUGH CHEN 			tmp |= ESDHC_VEND_SPEC2_EN_BUSY_IRQ;
1334bcdb5301SBOUGH CHEN 			writel(tmp, host->ioaddr + ESDHC_VEND_SPEC2);
1335bcdb5301SBOUGH CHEN 
1336bcdb5301SBOUGH CHEN 			host->quirks &= ~SDHCI_QUIRK_NO_BUSY_IRQ;
1337bcdb5301SBOUGH CHEN 		}
1338bcdb5301SBOUGH CHEN 
13392b16cf32SDong Aisheng 		if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) {
13402b16cf32SDong Aisheng 			tmp = readl(host->ioaddr + ESDHC_TUNING_CTRL);
13412b16cf32SDong Aisheng 			tmp |= ESDHC_STD_TUNING_EN |
13422b16cf32SDong Aisheng 				ESDHC_TUNING_START_TAP_DEFAULT;
13432b16cf32SDong Aisheng 			if (imx_data->boarddata.tuning_start_tap) {
13442b16cf32SDong Aisheng 				tmp &= ~ESDHC_TUNING_START_TAP_MASK;
13452b16cf32SDong Aisheng 				tmp |= imx_data->boarddata.tuning_start_tap;
13462b16cf32SDong Aisheng 			}
13472b16cf32SDong Aisheng 
13482b16cf32SDong Aisheng 			if (imx_data->boarddata.tuning_step) {
13492b16cf32SDong Aisheng 				tmp &= ~ESDHC_TUNING_STEP_MASK;
13502b16cf32SDong Aisheng 				tmp |= imx_data->boarddata.tuning_step
13512b16cf32SDong Aisheng 					<< ESDHC_TUNING_STEP_SHIFT;
13522b16cf32SDong Aisheng 			}
135316e40e5bSHaibo Chen 
135416e40e5bSHaibo Chen 			/* Disable the CMD CRC check for tuning, if not, need to
135516e40e5bSHaibo Chen 			 * add some delay after every tuning command, because
135616e40e5bSHaibo Chen 			 * hardware standard tuning logic will directly go to next
135716e40e5bSHaibo Chen 			 * step once it detect the CMD CRC error, will not wait for
135816e40e5bSHaibo Chen 			 * the card side to finally send out the tuning data, trigger
135916e40e5bSHaibo Chen 			 * the buffer read ready interrupt immediately. If usdhc send
136016e40e5bSHaibo Chen 			 * the next tuning command some eMMC card will stuck, can't
136116e40e5bSHaibo Chen 			 * response, block the tuning procedure or the first command
136216e40e5bSHaibo Chen 			 * after the whole tuning procedure always can't get any response.
136316e40e5bSHaibo Chen 			 */
136416e40e5bSHaibo Chen 			tmp |= ESDHC_TUNING_CMD_CRC_CHECK_DISABLE;
13652b16cf32SDong Aisheng 			writel(tmp, host->ioaddr + ESDHC_TUNING_CTRL);
1366a98c557eSBOUGH CHEN 		} else if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING) {
1367a98c557eSBOUGH CHEN 			/*
1368a98c557eSBOUGH CHEN 			 * ESDHC_STD_TUNING_EN may be configed in bootloader
1369a98c557eSBOUGH CHEN 			 * or ROM code, so clear this bit here to make sure
1370a98c557eSBOUGH CHEN 			 * the manual tuning can work.
1371a98c557eSBOUGH CHEN 			 */
1372a98c557eSBOUGH CHEN 			tmp = readl(host->ioaddr + ESDHC_TUNING_CTRL);
1373a98c557eSBOUGH CHEN 			tmp &= ~ESDHC_STD_TUNING_EN;
1374a98c557eSBOUGH CHEN 			writel(tmp, host->ioaddr + ESDHC_TUNING_CTRL);
13752b16cf32SDong Aisheng 		}
1376982cf37dSHaibo Chen 
1377982cf37dSHaibo Chen 		/*
1378982cf37dSHaibo Chen 		 * On i.MX8MM, we are running Dual Linux OS, with 1st Linux using SD Card
1379982cf37dSHaibo Chen 		 * as rootfs storage, 2nd Linux using eMMC as rootfs storage. We let the
1380982cf37dSHaibo Chen 		 * the 1st linux configure power/clock for the 2nd Linux.
1381982cf37dSHaibo Chen 		 *
1382982cf37dSHaibo Chen 		 * When the 2nd Linux is booting into rootfs stage, we let the 1st Linux
1383982cf37dSHaibo Chen 		 * to destroy the 2nd linux, then restart the 2nd linux, we met SDHCI dump.
1384982cf37dSHaibo Chen 		 * After we clear the pending interrupt and halt CQCTL, issue gone.
1385982cf37dSHaibo Chen 		 */
1386982cf37dSHaibo Chen 		if (cq_host) {
1387982cf37dSHaibo Chen 			tmp = cqhci_readl(cq_host, CQHCI_IS);
1388982cf37dSHaibo Chen 			cqhci_writel(cq_host, tmp, CQHCI_IS);
1389982cf37dSHaibo Chen 			cqhci_writel(cq_host, CQHCI_HALT, CQHCI_CTL);
1390982cf37dSHaibo Chen 		}
1391f3f5cf3dSDong Aisheng 	}
1392f3f5cf3dSDong Aisheng }
1393f3f5cf3dSDong Aisheng 
1394bb6e3581SBOUGH CHEN static void esdhc_cqe_enable(struct mmc_host *mmc)
1395bb6e3581SBOUGH CHEN {
1396bb6e3581SBOUGH CHEN 	struct sdhci_host *host = mmc_priv(mmc);
139785236d2bSBOUGH CHEN 	struct cqhci_host *cq_host = mmc->cqe_private;
1398bb6e3581SBOUGH CHEN 	u32 reg;
1399bb6e3581SBOUGH CHEN 	u16 mode;
1400bb6e3581SBOUGH CHEN 	int count = 10;
1401bb6e3581SBOUGH CHEN 
1402bb6e3581SBOUGH CHEN 	/*
1403bb6e3581SBOUGH CHEN 	 * CQE gets stuck if it sees Buffer Read Enable bit set, which can be
1404bb6e3581SBOUGH CHEN 	 * the case after tuning, so ensure the buffer is drained.
1405bb6e3581SBOUGH CHEN 	 */
1406bb6e3581SBOUGH CHEN 	reg = sdhci_readl(host, SDHCI_PRESENT_STATE);
1407bb6e3581SBOUGH CHEN 	while (reg & SDHCI_DATA_AVAILABLE) {
1408bb6e3581SBOUGH CHEN 		sdhci_readl(host, SDHCI_BUFFER);
1409bb6e3581SBOUGH CHEN 		reg = sdhci_readl(host, SDHCI_PRESENT_STATE);
1410bb6e3581SBOUGH CHEN 		if (count-- == 0) {
1411bb6e3581SBOUGH CHEN 			dev_warn(mmc_dev(host->mmc),
1412bb6e3581SBOUGH CHEN 				"CQE may get stuck because the Buffer Read Enable bit is set\n");
1413bb6e3581SBOUGH CHEN 			break;
1414bb6e3581SBOUGH CHEN 		}
1415bb6e3581SBOUGH CHEN 		mdelay(1);
1416bb6e3581SBOUGH CHEN 	}
1417bb6e3581SBOUGH CHEN 
1418bb6e3581SBOUGH CHEN 	/*
1419bb6e3581SBOUGH CHEN 	 * Runtime resume will reset the entire host controller, which
1420bb6e3581SBOUGH CHEN 	 * will also clear the DMAEN/BCEN of register ESDHC_MIX_CTRL.
1421bb6e3581SBOUGH CHEN 	 * Here set DMAEN and BCEN when enable CMDQ.
1422bb6e3581SBOUGH CHEN 	 */
1423bb6e3581SBOUGH CHEN 	mode = sdhci_readw(host, SDHCI_TRANSFER_MODE);
1424bb6e3581SBOUGH CHEN 	if (host->flags & SDHCI_REQ_USE_DMA)
1425bb6e3581SBOUGH CHEN 		mode |= SDHCI_TRNS_DMA;
1426bb6e3581SBOUGH CHEN 	if (!(host->quirks2 & SDHCI_QUIRK2_SUPPORT_SINGLE))
1427bb6e3581SBOUGH CHEN 		mode |= SDHCI_TRNS_BLK_CNT_EN;
1428bb6e3581SBOUGH CHEN 	sdhci_writew(host, mode, SDHCI_TRANSFER_MODE);
1429bb6e3581SBOUGH CHEN 
143085236d2bSBOUGH CHEN 	/*
143185236d2bSBOUGH CHEN 	 * Though Runtime resume reset the entire host controller,
143285236d2bSBOUGH CHEN 	 * but do not impact the CQHCI side, need to clear the
143385236d2bSBOUGH CHEN 	 * HALT bit, avoid CQHCI stuck in the first request when
143485236d2bSBOUGH CHEN 	 * system resume back.
143585236d2bSBOUGH CHEN 	 */
143685236d2bSBOUGH CHEN 	cqhci_writel(cq_host, 0, CQHCI_CTL);
143785236d2bSBOUGH CHEN 	if (cqhci_readl(cq_host, CQHCI_CTL) && CQHCI_HALT)
143885236d2bSBOUGH CHEN 		dev_err(mmc_dev(host->mmc),
143985236d2bSBOUGH CHEN 			"failed to exit halt state when enable CQE\n");
144085236d2bSBOUGH CHEN 
144185236d2bSBOUGH CHEN 
1442bb6e3581SBOUGH CHEN 	sdhci_cqe_enable(mmc);
1443bb6e3581SBOUGH CHEN }
1444bb6e3581SBOUGH CHEN 
1445bb6e3581SBOUGH CHEN static void esdhc_sdhci_dumpregs(struct mmc_host *mmc)
1446bb6e3581SBOUGH CHEN {
1447bb6e3581SBOUGH CHEN 	sdhci_dumpregs(mmc_priv(mmc));
1448bb6e3581SBOUGH CHEN }
1449bb6e3581SBOUGH CHEN 
1450bb6e3581SBOUGH CHEN static const struct cqhci_host_ops esdhc_cqhci_ops = {
1451bb6e3581SBOUGH CHEN 	.enable		= esdhc_cqe_enable,
1452bb6e3581SBOUGH CHEN 	.disable	= sdhci_cqe_disable,
1453bb6e3581SBOUGH CHEN 	.dumpregs	= esdhc_sdhci_dumpregs,
1454bb6e3581SBOUGH CHEN };
1455bb6e3581SBOUGH CHEN 
1456c3be1efdSBill Pemberton static int
1457abfafc2dSShawn Guo sdhci_esdhc_imx_probe_dt(struct platform_device *pdev,
145807bf2b54SSascha Hauer 			 struct sdhci_host *host,
145991fa4252SDong Aisheng 			 struct pltfm_imx_data *imx_data)
1460abfafc2dSShawn Guo {
1461abfafc2dSShawn Guo 	struct device_node *np = pdev->dev.of_node;
146291fa4252SDong Aisheng 	struct esdhc_platform_data *boarddata = &imx_data->boarddata;
14634800e87aSDong Aisheng 	int ret;
1464abfafc2dSShawn Guo 
1465abfafc2dSShawn Guo 	if (of_get_property(np, "fsl,wp-controller", NULL))
1466abfafc2dSShawn Guo 		boarddata->wp_type = ESDHC_WP_CONTROLLER;
1467abfafc2dSShawn Guo 
146874ff81e1SLinus Walleij 	/*
146974ff81e1SLinus Walleij 	 * If we have this property, then activate WP check.
147074ff81e1SLinus Walleij 	 * Retrieveing and requesting the actual WP GPIO will happen
147174ff81e1SLinus Walleij 	 * in the call to mmc_of_parse().
147274ff81e1SLinus Walleij 	 */
147374ff81e1SLinus Walleij 	if (of_property_read_bool(np, "wp-gpios"))
1474abfafc2dSShawn Guo 		boarddata->wp_type = ESDHC_WP_GPIO;
1475abfafc2dSShawn Guo 
1476d407e30bSHaibo Chen 	of_property_read_u32(np, "fsl,tuning-step", &boarddata->tuning_step);
1477d87fc966SDong Aisheng 	of_property_read_u32(np, "fsl,tuning-start-tap",
1478d87fc966SDong Aisheng 			     &boarddata->tuning_start_tap);
1479d407e30bSHaibo Chen 
14805bd2acdcSHaibo Chen 	of_property_read_u32(np, "fsl,strobe-dll-delay-target",
14815bd2acdcSHaibo Chen 				&boarddata->strobe_dll_delay_target);
1482ad93220dSDong Aisheng 	if (of_find_property(np, "no-1-8-v", NULL))
148386f495c5SStefan Agner 		host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V;
1484ad93220dSDong Aisheng 
1485602519b2SDong Aisheng 	if (of_property_read_u32(np, "fsl,delay-line", &boarddata->delay_line))
1486602519b2SDong Aisheng 		boarddata->delay_line = 0;
1487602519b2SDong Aisheng 
148807bf2b54SSascha Hauer 	mmc_of_parse_voltage(np, &host->ocr_mask);
148907bf2b54SSascha Hauer 
1490f410ee0aSPeng Fan 	if (esdhc_is_usdhc(imx_data) && !IS_ERR(imx_data->pinctrl)) {
149191fa4252SDong Aisheng 		imx_data->pins_100mhz = pinctrl_lookup_state(imx_data->pinctrl,
149291fa4252SDong Aisheng 						ESDHC_PINCTRL_STATE_100MHZ);
149391fa4252SDong Aisheng 		imx_data->pins_200mhz = pinctrl_lookup_state(imx_data->pinctrl,
149491fa4252SDong Aisheng 						ESDHC_PINCTRL_STATE_200MHZ);
149591fa4252SDong Aisheng 	}
149691fa4252SDong Aisheng 
149715064119SFabio Estevam 	/* call to generic mmc_of_parse to support additional capabilities */
14984800e87aSDong Aisheng 	ret = mmc_of_parse(host->mmc);
14994800e87aSDong Aisheng 	if (ret)
15004800e87aSDong Aisheng 		return ret;
15014800e87aSDong Aisheng 
1502287980e4SArnd Bergmann 	if (mmc_gpio_get_cd(host->mmc) >= 0)
15034800e87aSDong Aisheng 		host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION;
15044800e87aSDong Aisheng 
15054800e87aSDong Aisheng 	return 0;
1506abfafc2dSShawn Guo }
1507abfafc2dSShawn Guo 
1508c3be1efdSBill Pemberton static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
150995f25efeSWolfram Sang {
151085d6509dSShawn Guo 	struct sdhci_pltfm_host *pltfm_host;
151185d6509dSShawn Guo 	struct sdhci_host *host;
1512bb6e3581SBOUGH CHEN 	struct cqhci_host *cq_host;
15130c6d49ceSWolfram Sang 	int err;
1514e149860dSRichard Zhu 	struct pltfm_imx_data *imx_data;
151595f25efeSWolfram Sang 
1516070e6d3fSJisheng Zhang 	host = sdhci_pltfm_init(pdev, &sdhci_esdhc_imx_pdata,
1517070e6d3fSJisheng Zhang 				sizeof(*imx_data));
151885d6509dSShawn Guo 	if (IS_ERR(host))
151985d6509dSShawn Guo 		return PTR_ERR(host);
152085d6509dSShawn Guo 
152185d6509dSShawn Guo 	pltfm_host = sdhci_priv(host);
152285d6509dSShawn Guo 
1523070e6d3fSJisheng Zhang 	imx_data = sdhci_pltfm_priv(pltfm_host);
152457ed3314SShawn Guo 
152591b3d2e5SFabio Estevam 	imx_data->socdata = device_get_match_data(&pdev->dev);
152685d6509dSShawn Guo 
15271c4989b0SBOUGH CHEN 	if (imx_data->socdata->flags & ESDHC_FLAG_PMQOS)
1528d1b98305SRafael J. Wysocki 		cpu_latency_qos_add_request(&imx_data->pm_qos_req, 0);
15291c4989b0SBOUGH CHEN 
153052dac615SSascha Hauer 	imx_data->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
153152dac615SSascha Hauer 	if (IS_ERR(imx_data->clk_ipg)) {
153252dac615SSascha Hauer 		err = PTR_ERR(imx_data->clk_ipg);
1533e3af31c6SShawn Guo 		goto free_sdhci;
153495f25efeSWolfram Sang 	}
153552dac615SSascha Hauer 
153652dac615SSascha Hauer 	imx_data->clk_ahb = devm_clk_get(&pdev->dev, "ahb");
153752dac615SSascha Hauer 	if (IS_ERR(imx_data->clk_ahb)) {
153852dac615SSascha Hauer 		err = PTR_ERR(imx_data->clk_ahb);
1539e3af31c6SShawn Guo 		goto free_sdhci;
154052dac615SSascha Hauer 	}
154152dac615SSascha Hauer 
154252dac615SSascha Hauer 	imx_data->clk_per = devm_clk_get(&pdev->dev, "per");
154352dac615SSascha Hauer 	if (IS_ERR(imx_data->clk_per)) {
154452dac615SSascha Hauer 		err = PTR_ERR(imx_data->clk_per);
1545e3af31c6SShawn Guo 		goto free_sdhci;
154652dac615SSascha Hauer 	}
154752dac615SSascha Hauer 
154852dac615SSascha Hauer 	pltfm_host->clk = imx_data->clk_per;
1549a974862fSDong Aisheng 	pltfm_host->clock = clk_get_rate(pltfm_host->clk);
155017b1eb7fSFabio Estevam 	err = clk_prepare_enable(imx_data->clk_per);
155117b1eb7fSFabio Estevam 	if (err)
155217b1eb7fSFabio Estevam 		goto free_sdhci;
155317b1eb7fSFabio Estevam 	err = clk_prepare_enable(imx_data->clk_ipg);
155417b1eb7fSFabio Estevam 	if (err)
155517b1eb7fSFabio Estevam 		goto disable_per_clk;
155617b1eb7fSFabio Estevam 	err = clk_prepare_enable(imx_data->clk_ahb);
155717b1eb7fSFabio Estevam 	if (err)
155817b1eb7fSFabio Estevam 		goto disable_ipg_clk;
155995f25efeSWolfram Sang 
1560ad93220dSDong Aisheng 	imx_data->pinctrl = devm_pinctrl_get(&pdev->dev);
15619e70ff99SHaibo Chen 	if (IS_ERR(imx_data->pinctrl))
1562b62eee9fSHaibo Chen 		dev_warn(mmc_dev(host->mmc), "could not get pinctrl\n");
1563e62d8b8fSDong Aisheng 
156469ed60e0SDong Aisheng 	if (esdhc_is_usdhc(imx_data)) {
156569ed60e0SDong Aisheng 		host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN;
156609c8192bSStefan Agner 		host->mmc->caps |= MMC_CAP_1_8V_DDR | MMC_CAP_3_3V_DDR;
1567f6140462SHaibo Chen 
1568f6140462SHaibo Chen 		/* GPIO CD can be set as a wakeup source */
1569f6140462SHaibo Chen 		host->mmc->caps |= MMC_CAP_CD_WAKE;
1570f6140462SHaibo Chen 
15714245afffSDong Aisheng 		if (!(imx_data->socdata->flags & ESDHC_FLAG_HS200))
15724245afffSDong Aisheng 			host->quirks2 |= SDHCI_QUIRK2_BROKEN_HS200;
1573a75dcbf4SDong Aisheng 
1574a75dcbf4SDong Aisheng 		/* clear tuning bits in case ROM has set it already */
1575a75dcbf4SDong Aisheng 		writel(0x0, host->ioaddr + ESDHC_MIX_CTRL);
1576869f8a69SAdrian Hunter 		writel(0x0, host->ioaddr + SDHCI_AUTO_CMD_STATUS);
1577a75dcbf4SDong Aisheng 		writel(0x0, host->ioaddr + ESDHC_TUNE_CTRL_STATUS);
1578de3e1dd0SBOUGH CHEN 
1579de3e1dd0SBOUGH CHEN 		/*
1580de3e1dd0SBOUGH CHEN 		 * Link usdhc specific mmc_host_ops execute_tuning function,
1581de3e1dd0SBOUGH CHEN 		 * to replace the standard one in sdhci_ops.
1582de3e1dd0SBOUGH CHEN 		 */
1583de3e1dd0SBOUGH CHEN 		host->mmc_host_ops.execute_tuning = usdhc_execute_tuning;
158469ed60e0SDong Aisheng 	}
1585f750ba9bSShawn Guo 
15866e9fd28eSDong Aisheng 	if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING)
15876e9fd28eSDong Aisheng 		sdhci_esdhc_ops.platform_execute_tuning =
15886e9fd28eSDong Aisheng 					esdhc_executing_tuning;
15898b2bb0adSDong Aisheng 
159018094430SDong Aisheng 	if (imx_data->socdata->flags & ESDHC_FLAG_ERR004536)
159118094430SDong Aisheng 		host->quirks |= SDHCI_QUIRK_BROKEN_ADMA;
159218094430SDong Aisheng 
159328b07674SHaibo Chen 	if (imx_data->socdata->flags & ESDHC_FLAG_HS400)
159428b07674SHaibo Chen 		host->quirks2 |= SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400;
159528b07674SHaibo Chen 
159674898cbcSHaibo Chen 	if (imx_data->socdata->flags & ESDHC_FLAG_BROKEN_AUTO_CMD23)
159774898cbcSHaibo Chen 		host->quirks2 |= SDHCI_QUIRK2_ACMD23_BROKEN;
159874898cbcSHaibo Chen 
1599029e2476SBOUGH CHEN 	if (imx_data->socdata->flags & ESDHC_FLAG_HS400_ES) {
1600029e2476SBOUGH CHEN 		host->mmc->caps2 |= MMC_CAP2_HS400_ES;
1601029e2476SBOUGH CHEN 		host->mmc_host_ops.hs400_enhanced_strobe =
1602029e2476SBOUGH CHEN 					esdhc_hs400_enhanced_strobe;
1603029e2476SBOUGH CHEN 	}
1604029e2476SBOUGH CHEN 
1605bb6e3581SBOUGH CHEN 	if (imx_data->socdata->flags & ESDHC_FLAG_CQHCI) {
1606bcdb5301SBOUGH CHEN 		host->mmc->caps2 |= MMC_CAP2_CQE | MMC_CAP2_CQE_DCMD;
1607bb6e3581SBOUGH CHEN 		cq_host = devm_kzalloc(&pdev->dev, sizeof(*cq_host), GFP_KERNEL);
16089a633f3bSWei Yongjun 		if (!cq_host) {
16099a633f3bSWei Yongjun 			err = -ENOMEM;
1610bb6e3581SBOUGH CHEN 			goto disable_ahb_clk;
1611bb6e3581SBOUGH CHEN 		}
1612bb6e3581SBOUGH CHEN 
1613bb6e3581SBOUGH CHEN 		cq_host->mmio = host->ioaddr + ESDHC_CQHCI_ADDR_OFFSET;
1614bb6e3581SBOUGH CHEN 		cq_host->ops = &esdhc_cqhci_ops;
1615bb6e3581SBOUGH CHEN 
1616bb6e3581SBOUGH CHEN 		err = cqhci_init(cq_host, host->mmc, false);
1617bb6e3581SBOUGH CHEN 		if (err)
1618bb6e3581SBOUGH CHEN 			goto disable_ahb_clk;
1619bb6e3581SBOUGH CHEN 	}
1620bb6e3581SBOUGH CHEN 
162191fa4252SDong Aisheng 	err = sdhci_esdhc_imx_probe_dt(pdev, host, imx_data);
162291fa4252SDong Aisheng 	if (err)
162317b1eb7fSFabio Estevam 		goto disable_ahb_clk;
1624ad93220dSDong Aisheng 
1625f3f5cf3dSDong Aisheng 	sdhci_esdhc_imx_hwinit(host);
1626f3f5cf3dSDong Aisheng 
162785d6509dSShawn Guo 	err = sdhci_add_host(host);
162885d6509dSShawn Guo 	if (err)
162917b1eb7fSFabio Estevam 		goto disable_ahb_clk;
163085d6509dSShawn Guo 
163189d7e5c1SDong Aisheng 	pm_runtime_set_active(&pdev->dev);
163289d7e5c1SDong Aisheng 	pm_runtime_set_autosuspend_delay(&pdev->dev, 50);
163389d7e5c1SDong Aisheng 	pm_runtime_use_autosuspend(&pdev->dev);
163489d7e5c1SDong Aisheng 	pm_suspend_ignore_children(&pdev->dev, 1);
163577903c01SUlf Hansson 	pm_runtime_enable(&pdev->dev);
163689d7e5c1SDong Aisheng 
16377e29c306SWolfram Sang 	return 0;
16387e29c306SWolfram Sang 
163917b1eb7fSFabio Estevam disable_ahb_clk:
164052dac615SSascha Hauer 	clk_disable_unprepare(imx_data->clk_ahb);
164117b1eb7fSFabio Estevam disable_ipg_clk:
164217b1eb7fSFabio Estevam 	clk_disable_unprepare(imx_data->clk_ipg);
164317b1eb7fSFabio Estevam disable_per_clk:
164417b1eb7fSFabio Estevam 	clk_disable_unprepare(imx_data->clk_per);
1645e3af31c6SShawn Guo free_sdhci:
16461c4989b0SBOUGH CHEN 	if (imx_data->socdata->flags & ESDHC_FLAG_PMQOS)
1647d1b98305SRafael J. Wysocki 		cpu_latency_qos_remove_request(&imx_data->pm_qos_req);
164885d6509dSShawn Guo 	sdhci_pltfm_free(pdev);
164985d6509dSShawn Guo 	return err;
165095f25efeSWolfram Sang }
165195f25efeSWolfram Sang 
16526e0ee714SBill Pemberton static int sdhci_esdhc_imx_remove(struct platform_device *pdev)
165395f25efeSWolfram Sang {
165485d6509dSShawn Guo 	struct sdhci_host *host = platform_get_drvdata(pdev);
165595f25efeSWolfram Sang 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1656070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
1657a56f4413SFrank Li 	int dead;
165885d6509dSShawn Guo 
16590b414368SUlf Hansson 	pm_runtime_get_sync(&pdev->dev);
1660a56f4413SFrank Li 	dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff);
16610b414368SUlf Hansson 	pm_runtime_disable(&pdev->dev);
16620b414368SUlf Hansson 	pm_runtime_put_noidle(&pdev->dev);
16630b414368SUlf Hansson 
166485d6509dSShawn Guo 	sdhci_remove_host(host, dead);
16650c6d49ceSWolfram Sang 
166652dac615SSascha Hauer 	clk_disable_unprepare(imx_data->clk_per);
166752dac615SSascha Hauer 	clk_disable_unprepare(imx_data->clk_ipg);
166852dac615SSascha Hauer 	clk_disable_unprepare(imx_data->clk_ahb);
166952dac615SSascha Hauer 
16701c4989b0SBOUGH CHEN 	if (imx_data->socdata->flags & ESDHC_FLAG_PMQOS)
1671d1b98305SRafael J. Wysocki 		cpu_latency_qos_remove_request(&imx_data->pm_qos_req);
16721c4989b0SBOUGH CHEN 
167385d6509dSShawn Guo 	sdhci_pltfm_free(pdev);
167485d6509dSShawn Guo 
167585d6509dSShawn Guo 	return 0;
167695f25efeSWolfram Sang }
167795f25efeSWolfram Sang 
16782788ed42SUlf Hansson #ifdef CONFIG_PM_SLEEP
167904143fbaSDong Aisheng static int sdhci_esdhc_suspend(struct device *dev)
168004143fbaSDong Aisheng {
16813e3274abSUlf Hansson 	struct sdhci_host *host = dev_get_drvdata(dev);
1682a26a4f1bSHaibo Chen 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1683a26a4f1bSHaibo Chen 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
1684bb6e3581SBOUGH CHEN 	int ret;
1685bb6e3581SBOUGH CHEN 
1686bb6e3581SBOUGH CHEN 	if (host->mmc->caps2 & MMC_CAP2_CQE) {
1687bb6e3581SBOUGH CHEN 		ret = cqhci_suspend(host->mmc);
1688bb6e3581SBOUGH CHEN 		if (ret)
1689bb6e3581SBOUGH CHEN 			return ret;
1690bb6e3581SBOUGH CHEN 	}
16913e3274abSUlf Hansson 
1692a26a4f1bSHaibo Chen 	if ((imx_data->socdata->flags & ESDHC_FLAG_STATE_LOST_IN_LPMODE) &&
1693a26a4f1bSHaibo Chen 		(host->tuning_mode != SDHCI_TUNING_MODE_1)) {
1694a26a4f1bSHaibo Chen 		mmc_retune_timer_stop(host->mmc);
1695a26a4f1bSHaibo Chen 		mmc_retune_needed(host->mmc);
1696a26a4f1bSHaibo Chen 	}
1697a26a4f1bSHaibo Chen 
1698d38dcad4SAdrian Hunter 	if (host->tuning_mode != SDHCI_TUNING_MODE_3)
1699d38dcad4SAdrian Hunter 		mmc_retune_needed(host->mmc);
1700d38dcad4SAdrian Hunter 
1701af8fade4SHaibo Chen 	ret = sdhci_suspend_host(host);
1702f6140462SHaibo Chen 	if (ret)
1703f6140462SHaibo Chen 		return ret;
1704f6140462SHaibo Chen 
1705f6140462SHaibo Chen 	ret = pinctrl_pm_select_sleep_state(dev);
1706f6140462SHaibo Chen 	if (ret)
1707f6140462SHaibo Chen 		return ret;
1708f6140462SHaibo Chen 
1709f6140462SHaibo Chen 	ret = mmc_gpio_set_cd_wake(host->mmc, true);
1710af8fade4SHaibo Chen 
1711af8fade4SHaibo Chen 	return ret;
171204143fbaSDong Aisheng }
171304143fbaSDong Aisheng 
171404143fbaSDong Aisheng static int sdhci_esdhc_resume(struct device *dev)
171504143fbaSDong Aisheng {
1716cc17e129SDong Aisheng 	struct sdhci_host *host = dev_get_drvdata(dev);
1717bb6e3581SBOUGH CHEN 	int ret;
1718cc17e129SDong Aisheng 
1719af8fade4SHaibo Chen 	ret = pinctrl_pm_select_default_state(dev);
1720af8fade4SHaibo Chen 	if (ret)
1721af8fade4SHaibo Chen 		return ret;
1722af8fade4SHaibo Chen 
172319dbfdd3SDong Aisheng 	/* re-initialize hw state in case it's lost in low power mode */
172419dbfdd3SDong Aisheng 	sdhci_esdhc_imx_hwinit(host);
1725cc17e129SDong Aisheng 
1726bb6e3581SBOUGH CHEN 	ret = sdhci_resume_host(host);
1727bb6e3581SBOUGH CHEN 	if (ret)
1728bb6e3581SBOUGH CHEN 		return ret;
1729bb6e3581SBOUGH CHEN 
1730bb6e3581SBOUGH CHEN 	if (host->mmc->caps2 & MMC_CAP2_CQE)
1731bb6e3581SBOUGH CHEN 		ret = cqhci_resume(host->mmc);
1732bb6e3581SBOUGH CHEN 
1733f6140462SHaibo Chen 	if (!ret)
1734f6140462SHaibo Chen 		ret = mmc_gpio_set_cd_wake(host->mmc, false);
1735f6140462SHaibo Chen 
1736bb6e3581SBOUGH CHEN 	return ret;
173704143fbaSDong Aisheng }
17382788ed42SUlf Hansson #endif
173904143fbaSDong Aisheng 
17402788ed42SUlf Hansson #ifdef CONFIG_PM
174189d7e5c1SDong Aisheng static int sdhci_esdhc_runtime_suspend(struct device *dev)
174289d7e5c1SDong Aisheng {
174389d7e5c1SDong Aisheng 	struct sdhci_host *host = dev_get_drvdata(dev);
174489d7e5c1SDong Aisheng 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1745070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
174689d7e5c1SDong Aisheng 	int ret;
174789d7e5c1SDong Aisheng 
1748bb6e3581SBOUGH CHEN 	if (host->mmc->caps2 & MMC_CAP2_CQE) {
1749bb6e3581SBOUGH CHEN 		ret = cqhci_suspend(host->mmc);
1750bb6e3581SBOUGH CHEN 		if (ret)
1751bb6e3581SBOUGH CHEN 			return ret;
1752bb6e3581SBOUGH CHEN 	}
1753bb6e3581SBOUGH CHEN 
175489d7e5c1SDong Aisheng 	ret = sdhci_runtime_suspend_host(host);
1755371d39faSMichael Trimarchi 	if (ret)
1756371d39faSMichael Trimarchi 		return ret;
175789d7e5c1SDong Aisheng 
1758d38dcad4SAdrian Hunter 	if (host->tuning_mode != SDHCI_TUNING_MODE_3)
1759d38dcad4SAdrian Hunter 		mmc_retune_needed(host->mmc);
1760d38dcad4SAdrian Hunter 
17613602785bSMichael Trimarchi 	imx_data->actual_clock = host->mmc->actual_clock;
17623602785bSMichael Trimarchi 	esdhc_pltfm_set_clock(host, 0);
176389d7e5c1SDong Aisheng 	clk_disable_unprepare(imx_data->clk_per);
176489d7e5c1SDong Aisheng 	clk_disable_unprepare(imx_data->clk_ipg);
176589d7e5c1SDong Aisheng 	clk_disable_unprepare(imx_data->clk_ahb);
176689d7e5c1SDong Aisheng 
17671c4989b0SBOUGH CHEN 	if (imx_data->socdata->flags & ESDHC_FLAG_PMQOS)
1768d1b98305SRafael J. Wysocki 		cpu_latency_qos_remove_request(&imx_data->pm_qos_req);
17691c4989b0SBOUGH CHEN 
177089d7e5c1SDong Aisheng 	return ret;
177189d7e5c1SDong Aisheng }
177289d7e5c1SDong Aisheng 
177389d7e5c1SDong Aisheng static int sdhci_esdhc_runtime_resume(struct device *dev)
177489d7e5c1SDong Aisheng {
177589d7e5c1SDong Aisheng 	struct sdhci_host *host = dev_get_drvdata(dev);
177689d7e5c1SDong Aisheng 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1777070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
177817b1eb7fSFabio Estevam 	int err;
177989d7e5c1SDong Aisheng 
17801c4989b0SBOUGH CHEN 	if (imx_data->socdata->flags & ESDHC_FLAG_PMQOS)
1781d1b98305SRafael J. Wysocki 		cpu_latency_qos_add_request(&imx_data->pm_qos_req, 0);
17821c4989b0SBOUGH CHEN 
17835c11f1ffSHaibo Chen 	if (imx_data->socdata->flags & ESDHC_FLAG_CLK_RATE_LOST_IN_PM_RUNTIME)
17845c11f1ffSHaibo Chen 		clk_set_rate(imx_data->clk_per, pltfm_host->clock);
17855c11f1ffSHaibo Chen 
1786a0ad3087SMichael Trimarchi 	err = clk_prepare_enable(imx_data->clk_ahb);
1787a0ad3087SMichael Trimarchi 	if (err)
17881c4989b0SBOUGH CHEN 		goto remove_pm_qos_request;
1789a0ad3087SMichael Trimarchi 
179017b1eb7fSFabio Estevam 	err = clk_prepare_enable(imx_data->clk_per);
179117b1eb7fSFabio Estevam 	if (err)
1792a0ad3087SMichael Trimarchi 		goto disable_ahb_clk;
1793af5d2b7bSUlf Hansson 
179417b1eb7fSFabio Estevam 	err = clk_prepare_enable(imx_data->clk_ipg);
179517b1eb7fSFabio Estevam 	if (err)
179617b1eb7fSFabio Estevam 		goto disable_per_clk;
1797af5d2b7bSUlf Hansson 
17983602785bSMichael Trimarchi 	esdhc_pltfm_set_clock(host, imx_data->actual_clock);
1799a0ad3087SMichael Trimarchi 
1800c6303c5dSBaolin Wang 	err = sdhci_runtime_resume_host(host, 0);
180117b1eb7fSFabio Estevam 	if (err)
1802a0ad3087SMichael Trimarchi 		goto disable_ipg_clk;
180389d7e5c1SDong Aisheng 
1804bb6e3581SBOUGH CHEN 	if (host->mmc->caps2 & MMC_CAP2_CQE)
1805bb6e3581SBOUGH CHEN 		err = cqhci_resume(host->mmc);
1806bb6e3581SBOUGH CHEN 
1807bb6e3581SBOUGH CHEN 	return err;
180817b1eb7fSFabio Estevam 
180917b1eb7fSFabio Estevam disable_ipg_clk:
181017b1eb7fSFabio Estevam 	clk_disable_unprepare(imx_data->clk_ipg);
181117b1eb7fSFabio Estevam disable_per_clk:
181217b1eb7fSFabio Estevam 	clk_disable_unprepare(imx_data->clk_per);
1813a0ad3087SMichael Trimarchi disable_ahb_clk:
1814a0ad3087SMichael Trimarchi 	clk_disable_unprepare(imx_data->clk_ahb);
18151c4989b0SBOUGH CHEN remove_pm_qos_request:
18161c4989b0SBOUGH CHEN 	if (imx_data->socdata->flags & ESDHC_FLAG_PMQOS)
1817d1b98305SRafael J. Wysocki 		cpu_latency_qos_remove_request(&imx_data->pm_qos_req);
181817b1eb7fSFabio Estevam 	return err;
181989d7e5c1SDong Aisheng }
182089d7e5c1SDong Aisheng #endif
182189d7e5c1SDong Aisheng 
182289d7e5c1SDong Aisheng static const struct dev_pm_ops sdhci_esdhc_pmops = {
182304143fbaSDong Aisheng 	SET_SYSTEM_SLEEP_PM_OPS(sdhci_esdhc_suspend, sdhci_esdhc_resume)
182489d7e5c1SDong Aisheng 	SET_RUNTIME_PM_OPS(sdhci_esdhc_runtime_suspend,
182589d7e5c1SDong Aisheng 				sdhci_esdhc_runtime_resume, NULL)
182689d7e5c1SDong Aisheng };
182789d7e5c1SDong Aisheng 
182885d6509dSShawn Guo static struct platform_driver sdhci_esdhc_imx_driver = {
182985d6509dSShawn Guo 	.driver		= {
183085d6509dSShawn Guo 		.name	= "sdhci-esdhc-imx",
183121b2cec6SDouglas Anderson 		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
1832abfafc2dSShawn Guo 		.of_match_table = imx_esdhc_dt_ids,
183389d7e5c1SDong Aisheng 		.pm	= &sdhci_esdhc_pmops,
183485d6509dSShawn Guo 	},
183585d6509dSShawn Guo 	.probe		= sdhci_esdhc_imx_probe,
18360433c143SBill Pemberton 	.remove		= sdhci_esdhc_imx_remove,
183795f25efeSWolfram Sang };
183885d6509dSShawn Guo 
1839d1f81a64SAxel Lin module_platform_driver(sdhci_esdhc_imx_driver);
184085d6509dSShawn Guo 
184185d6509dSShawn Guo MODULE_DESCRIPTION("SDHCI driver for Freescale i.MX eSDHC");
1842035ff831SWolfram Sang MODULE_AUTHOR("Wolfram Sang <kernel@pengutronix.de>");
184385d6509dSShawn Guo MODULE_LICENSE("GPL v2");
1844