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>
2789d7e5c1SDong Aisheng #include <linux/pm_runtime.h>
2895f25efeSWolfram Sang #include "sdhci-pltfm.h"
2995f25efeSWolfram Sang #include "sdhci-esdhc.h"
30bb6e3581SBOUGH CHEN #include "cqhci.h"
3195f25efeSWolfram Sang 
32a215186dSHaibo Chen #define ESDHC_SYS_CTRL_DTOCV_MASK	0x0f
3360bf6396SShawn Guo #define	ESDHC_CTRL_D3CD			0x08
34fd44954eSHaibo Chen #define ESDHC_BURST_LEN_EN_INCR		(1 << 27)
3558ac8177SRichard Zhu /* VENDOR SPEC register */
3660bf6396SShawn Guo #define ESDHC_VENDOR_SPEC		0xc0
3760bf6396SShawn Guo #define  ESDHC_VENDOR_SPEC_SDIO_QUIRK	(1 << 1)
380322191eSDong Aisheng #define  ESDHC_VENDOR_SPEC_VSELECT	(1 << 1)
39fed2f6e2SDong Aisheng #define  ESDHC_VENDOR_SPEC_FRC_SDCLK_ON	(1 << 8)
403722c74cSHaibo Chen #define ESDHC_DEBUG_SEL_AND_STATUS_REG		0xc2
413722c74cSHaibo Chen #define ESDHC_DEBUG_SEL_REG			0xc3
423722c74cSHaibo Chen #define ESDHC_DEBUG_SEL_MASK			0xf
433722c74cSHaibo Chen #define ESDHC_DEBUG_SEL_CMD_STATE		1
443722c74cSHaibo Chen #define ESDHC_DEBUG_SEL_DATA_STATE		2
453722c74cSHaibo Chen #define ESDHC_DEBUG_SEL_TRANS_STATE		3
463722c74cSHaibo Chen #define ESDHC_DEBUG_SEL_DMA_STATE		4
473722c74cSHaibo Chen #define ESDHC_DEBUG_SEL_ADMA_STATE		5
483722c74cSHaibo Chen #define ESDHC_DEBUG_SEL_FIFO_STATE		6
493722c74cSHaibo Chen #define ESDHC_DEBUG_SEL_ASYNC_FIFO_STATE	7
5060bf6396SShawn Guo #define ESDHC_WTMK_LVL			0x44
51cc17e129SDong Aisheng #define  ESDHC_WTMK_DEFAULT_VAL		0x10401040
523fbd4322SAndrew Gabbasov #define  ESDHC_WTMK_LVL_RD_WML_MASK	0x000000FF
533fbd4322SAndrew Gabbasov #define  ESDHC_WTMK_LVL_RD_WML_SHIFT	0
543fbd4322SAndrew Gabbasov #define  ESDHC_WTMK_LVL_WR_WML_MASK	0x00FF0000
553fbd4322SAndrew Gabbasov #define  ESDHC_WTMK_LVL_WR_WML_SHIFT	16
563fbd4322SAndrew Gabbasov #define  ESDHC_WTMK_LVL_WML_VAL_DEF	64
573fbd4322SAndrew Gabbasov #define  ESDHC_WTMK_LVL_WML_VAL_MAX	128
5860bf6396SShawn Guo #define ESDHC_MIX_CTRL			0x48
59de5bdbffSDong Aisheng #define  ESDHC_MIX_CTRL_DDREN		(1 << 3)
602a15f981SShawn Guo #define  ESDHC_MIX_CTRL_AC23EN		(1 << 7)
610322191eSDong Aisheng #define  ESDHC_MIX_CTRL_EXE_TUNE	(1 << 22)
620322191eSDong Aisheng #define  ESDHC_MIX_CTRL_SMPCLK_SEL	(1 << 23)
630b330e38SDong Aisheng #define  ESDHC_MIX_CTRL_AUTO_TUNE_EN	(1 << 24)
640322191eSDong Aisheng #define  ESDHC_MIX_CTRL_FBCLK_SEL	(1 << 25)
6528b07674SHaibo Chen #define  ESDHC_MIX_CTRL_HS400_EN	(1 << 26)
66029e2476SBOUGH CHEN #define  ESDHC_MIX_CTRL_HS400_ES_EN	(1 << 27)
672a15f981SShawn Guo /* Bits 3 and 6 are not SDHCI standard definitions */
682a15f981SShawn Guo #define  ESDHC_MIX_CTRL_SDHCI_MASK	0xb7
69d131a71cSDong Aisheng /* Tuning bits */
70d131a71cSDong Aisheng #define  ESDHC_MIX_CTRL_TUNING_MASK	0x03c00000
7158ac8177SRichard Zhu 
72602519b2SDong Aisheng /* dll control register */
73602519b2SDong Aisheng #define ESDHC_DLL_CTRL			0x60
74602519b2SDong Aisheng #define ESDHC_DLL_OVERRIDE_VAL_SHIFT	9
75602519b2SDong Aisheng #define ESDHC_DLL_OVERRIDE_EN_SHIFT	8
76602519b2SDong Aisheng 
770322191eSDong Aisheng /* tune control register */
780322191eSDong Aisheng #define ESDHC_TUNE_CTRL_STATUS		0x68
790322191eSDong Aisheng #define  ESDHC_TUNE_CTRL_STEP		1
800322191eSDong Aisheng #define  ESDHC_TUNE_CTRL_MIN		0
810322191eSDong Aisheng #define  ESDHC_TUNE_CTRL_MAX		((1 << 7) - 1)
820322191eSDong Aisheng 
8328b07674SHaibo Chen /* strobe dll register */
8428b07674SHaibo Chen #define ESDHC_STROBE_DLL_CTRL		0x70
8528b07674SHaibo Chen #define ESDHC_STROBE_DLL_CTRL_ENABLE	(1 << 0)
8628b07674SHaibo Chen #define ESDHC_STROBE_DLL_CTRL_RESET	(1 << 1)
875bd2acdcSHaibo Chen #define ESDHC_STROBE_DLL_CTRL_SLV_DLY_TARGET_DEFAULT	0x7
8828b07674SHaibo Chen #define ESDHC_STROBE_DLL_CTRL_SLV_DLY_TARGET_SHIFT	3
892eaf5a53SBOUGH CHEN #define ESDHC_STROBE_DLL_CTRL_SLV_UPDATE_INT_DEFAULT	(4 << 20)
9028b07674SHaibo Chen 
9128b07674SHaibo Chen #define ESDHC_STROBE_DLL_STATUS		0x74
9228b07674SHaibo Chen #define ESDHC_STROBE_DLL_STS_REF_LOCK	(1 << 1)
9328b07674SHaibo Chen #define ESDHC_STROBE_DLL_STS_SLV_LOCK	0x1
9428b07674SHaibo Chen 
95bcdb5301SBOUGH CHEN #define ESDHC_VEND_SPEC2		0xc8
96bcdb5301SBOUGH CHEN #define ESDHC_VEND_SPEC2_EN_BUSY_IRQ	(1 << 8)
97*45334ee1SHaibo Chen #define ESDHC_VEND_SPEC2_AUTO_TUNE_8BIT_EN	(1 << 4)
98*45334ee1SHaibo Chen #define ESDHC_VEND_SPEC2_AUTO_TUNE_4BIT_EN	(0 << 4)
99*45334ee1SHaibo Chen #define ESDHC_VEND_SPEC2_AUTO_TUNE_1BIT_EN	(2 << 4)
100*45334ee1SHaibo Chen #define ESDHC_VEND_SPEC2_AUTO_TUNE_CMD_EN	(1 << 6)
101*45334ee1SHaibo Chen #define ESDHC_VEND_SPEC2_AUTO_TUNE_MODE_MASK	(7 << 4)
102bcdb5301SBOUGH CHEN 
1036e9fd28eSDong Aisheng #define ESDHC_TUNING_CTRL		0xcc
1046e9fd28eSDong Aisheng #define ESDHC_STD_TUNING_EN		(1 << 24)
1056e9fd28eSDong Aisheng /* NOTE: the minimum valid tuning start tap for mx6sl is 1 */
106d87fc966SDong Aisheng #define ESDHC_TUNING_START_TAP_DEFAULT	0x1
1071194be8cSHaibo Chen #define ESDHC_TUNING_START_TAP_MASK	0x7f
10816e40e5bSHaibo Chen #define ESDHC_TUNING_CMD_CRC_CHECK_DISABLE	(1 << 7)
109260ecb3cSHaibo Chen #define ESDHC_TUNING_STEP_MASK		0x00070000
110d407e30bSHaibo Chen #define ESDHC_TUNING_STEP_SHIFT		16
1116e9fd28eSDong Aisheng 
112ad93220dSDong Aisheng /* pinctrl state */
113ad93220dSDong Aisheng #define ESDHC_PINCTRL_STATE_100MHZ	"state_100mhz"
114ad93220dSDong Aisheng #define ESDHC_PINCTRL_STATE_200MHZ	"state_200mhz"
115ad93220dSDong Aisheng 
11658ac8177SRichard Zhu /*
117af51079eSSascha Hauer  * Our interpretation of the SDHCI_HOST_CONTROL register
118af51079eSSascha Hauer  */
119af51079eSSascha Hauer #define ESDHC_CTRL_4BITBUS		(0x1 << 1)
120af51079eSSascha Hauer #define ESDHC_CTRL_8BITBUS		(0x2 << 1)
121af51079eSSascha Hauer #define ESDHC_CTRL_BUSWIDTH_MASK	(0x3 << 1)
122*45334ee1SHaibo Chen #define USDHC_GET_BUSWIDTH(c) (c & ESDHC_CTRL_BUSWIDTH_MASK)
123af51079eSSascha Hauer 
124af51079eSSascha Hauer /*
125d04f8d5bSBenoît Thébaudeau  * There is an INT DMA ERR mismatch between eSDHC and STD SDHC SPEC:
12697e4ba6aSRichard Zhu  * Bit25 is used in STD SPEC, and is reserved in fsl eSDHC design,
12797e4ba6aSRichard Zhu  * but bit28 is used as the INT DMA ERR in fsl eSDHC design.
12897e4ba6aSRichard Zhu  * Define this macro DMA error INT for fsl eSDHC
12997e4ba6aSRichard Zhu  */
13060bf6396SShawn Guo #define ESDHC_INT_VENDOR_SPEC_DMA_ERR	(1 << 28)
13197e4ba6aSRichard Zhu 
132bb6e3581SBOUGH CHEN /* the address offset of CQHCI */
133bb6e3581SBOUGH CHEN #define ESDHC_CQHCI_ADDR_OFFSET		0x100
134bb6e3581SBOUGH CHEN 
13597e4ba6aSRichard Zhu /*
13658ac8177SRichard Zhu  * The CMDTYPE of the CMD register (offset 0xE) should be set to
13758ac8177SRichard Zhu  * "11" when the STOP CMD12 is issued on imx53 to abort one
13858ac8177SRichard Zhu  * open ended multi-blk IO. Otherwise the TC INT wouldn't
13958ac8177SRichard Zhu  * be generated.
14058ac8177SRichard Zhu  * In exact block transfer, the controller doesn't complete the
14158ac8177SRichard Zhu  * operations automatically as required at the end of the
14258ac8177SRichard Zhu  * transfer and remains on hold if the abort command is not sent.
14358ac8177SRichard Zhu  * As a result, the TC flag is not asserted and SW received timeout
144d04f8d5bSBenoît Thébaudeau  * exception. Bit1 of Vendor Spec register is used to fix it.
14558ac8177SRichard Zhu  */
14631fbb301SShawn Guo #define ESDHC_FLAG_MULTIBLK_NO_INT	BIT(1)
14731fbb301SShawn Guo /*
1489d61c009SShawn Guo  * The flag tells that the ESDHC controller is an USDHC block that is
1499d61c009SShawn Guo  * integrated on the i.MX6 series.
1509d61c009SShawn Guo  */
1519d61c009SShawn Guo #define ESDHC_FLAG_USDHC		BIT(3)
1526e9fd28eSDong Aisheng /* The IP supports manual tuning process */
1536e9fd28eSDong Aisheng #define ESDHC_FLAG_MAN_TUNING		BIT(4)
1546e9fd28eSDong Aisheng /* The IP supports standard tuning process */
1556e9fd28eSDong Aisheng #define ESDHC_FLAG_STD_TUNING		BIT(5)
1566e9fd28eSDong Aisheng /* The IP has SDHCI_CAPABILITIES_1 register */
1576e9fd28eSDong Aisheng #define ESDHC_FLAG_HAVE_CAP1		BIT(6)
15818094430SDong Aisheng /*
159d04f8d5bSBenoît Thébaudeau  * The IP has erratum ERR004536
16018094430SDong Aisheng  * uSDHC: ADMA Length Mismatch Error occurs if the AHB read access is slow,
16118094430SDong Aisheng  * when reading data from the card
162667123f6SBenoît Thébaudeau  * This flag is also set for i.MX25 and i.MX35 in order to get
163667123f6SBenoît Thébaudeau  * SDHCI_QUIRK_BROKEN_ADMA, but for different reasons (ADMA capability bits).
16418094430SDong Aisheng  */
16518094430SDong Aisheng #define ESDHC_FLAG_ERR004536		BIT(7)
1664245afffSDong Aisheng /* The IP supports HS200 mode */
1674245afffSDong Aisheng #define ESDHC_FLAG_HS200		BIT(8)
16828b07674SHaibo Chen /* The IP supports HS400 mode */
16928b07674SHaibo Chen #define ESDHC_FLAG_HS400		BIT(9)
170af6a50d4SBOUGH CHEN /*
171af6a50d4SBOUGH CHEN  * The IP has errata ERR010450
172af6a50d4SBOUGH CHEN  * uSDHC: Due to the I/O timing limit, for SDR mode, SD card clock can't
173af6a50d4SBOUGH CHEN  * exceed 150MHz, for DDR mode, SD card clock can't exceed 45MHz.
174af6a50d4SBOUGH CHEN  */
175af6a50d4SBOUGH CHEN #define ESDHC_FLAG_ERR010450		BIT(10)
176029e2476SBOUGH CHEN /* The IP supports HS400ES mode */
177029e2476SBOUGH CHEN #define ESDHC_FLAG_HS400_ES		BIT(11)
178bb6e3581SBOUGH CHEN /* The IP has Host Controller Interface for Command Queuing */
179bb6e3581SBOUGH CHEN #define ESDHC_FLAG_CQHCI		BIT(12)
1801c4989b0SBOUGH CHEN /* need request pmqos during low power */
1811c4989b0SBOUGH CHEN #define ESDHC_FLAG_PMQOS		BIT(13)
182a26a4f1bSHaibo Chen /* The IP state got lost in low power mode */
183a26a4f1bSHaibo Chen #define ESDHC_FLAG_STATE_LOST_IN_LPMODE		BIT(14)
1845c11f1ffSHaibo Chen /* The IP lost clock rate in PM_RUNTIME */
1855c11f1ffSHaibo Chen #define ESDHC_FLAG_CLK_RATE_LOST_IN_PM_RUNTIME	BIT(15)
18674898cbcSHaibo Chen /*
18774898cbcSHaibo Chen  * The IP do not support the ACMD23 feature completely when use ADMA mode.
18874898cbcSHaibo Chen  * In ADMA mode, it only use the 16 bit block count of the register 0x4
18974898cbcSHaibo Chen  * (BLOCK_ATT) as the CMD23's argument for ACMD23 mode, which means it will
19074898cbcSHaibo Chen  * ignore the upper 16 bit of the CMD23's argument. This will block the reliable
19174898cbcSHaibo Chen  * write operation in RPMB, because RPMB reliable write need to set the bit31
19274898cbcSHaibo Chen  * of the CMD23's argument.
19374898cbcSHaibo Chen  * imx6qpdl/imx6sx/imx6sl/imx7d has this limitation only for ADMA mode, SDMA
19474898cbcSHaibo Chen  * do not has this limitation. so when these SoC use ADMA mode, it need to
19574898cbcSHaibo Chen  * disable the ACMD23 feature.
19674898cbcSHaibo Chen  */
19774898cbcSHaibo Chen #define ESDHC_FLAG_BROKEN_AUTO_CMD23	BIT(16)
198e149860dSRichard Zhu 
1994a11cc64SFabio Estevam enum wp_types {
2004a11cc64SFabio Estevam 	ESDHC_WP_NONE,		/* no WP, neither controller nor gpio */
2014a11cc64SFabio Estevam 	ESDHC_WP_CONTROLLER,	/* mmc controller internal WP */
2024a11cc64SFabio Estevam 	ESDHC_WP_GPIO,		/* external gpio pin for WP */
2034a11cc64SFabio Estevam };
2044a11cc64SFabio Estevam 
2054a11cc64SFabio Estevam enum cd_types {
2064a11cc64SFabio Estevam 	ESDHC_CD_NONE,		/* no CD, neither controller nor gpio */
2074a11cc64SFabio Estevam 	ESDHC_CD_CONTROLLER,	/* mmc controller internal CD */
2084a11cc64SFabio Estevam 	ESDHC_CD_GPIO,		/* external gpio pin for CD */
2094a11cc64SFabio Estevam 	ESDHC_CD_PERMANENT,	/* no CD, card permanently wired to host */
2104a11cc64SFabio Estevam };
2114a11cc64SFabio Estevam 
2124a11cc64SFabio Estevam /*
2134a11cc64SFabio Estevam  * struct esdhc_platform_data - platform data for esdhc on i.MX
2144a11cc64SFabio Estevam  *
2154a11cc64SFabio Estevam  * ESDHC_WP(CD)_CONTROLLER type is not available on i.MX25/35.
2164a11cc64SFabio Estevam  *
2174a11cc64SFabio Estevam  * @wp_type:	type of write_protect method (see wp_types enum above)
2184a11cc64SFabio Estevam  * @cd_type:	type of card_detect method (see cd_types enum above)
2194a11cc64SFabio Estevam  */
2204a11cc64SFabio Estevam 
2214a11cc64SFabio Estevam struct esdhc_platform_data {
2224a11cc64SFabio Estevam 	enum wp_types wp_type;
2234a11cc64SFabio Estevam 	enum cd_types cd_type;
2244a11cc64SFabio Estevam 	int max_bus_width;
2254a11cc64SFabio Estevam 	unsigned int delay_line;
2264a11cc64SFabio Estevam 	unsigned int tuning_step;       /* The delay cell steps in tuning procedure */
2274a11cc64SFabio Estevam 	unsigned int tuning_start_tap;	/* The start delay cell point in tuning procedure */
2284a11cc64SFabio Estevam 	unsigned int strobe_dll_delay_target;	/* The delay cell for strobe pad (read clock) */
2294a11cc64SFabio Estevam };
2304a11cc64SFabio Estevam 
231f47c4bbfSShawn Guo struct esdhc_soc_data {
232f47c4bbfSShawn Guo 	u32 flags;
233f47c4bbfSShawn Guo };
234f47c4bbfSShawn Guo 
2354f100012SAndrey Smirnov static const struct esdhc_soc_data esdhc_imx25_data = {
236667123f6SBenoît Thébaudeau 	.flags = ESDHC_FLAG_ERR004536,
237f47c4bbfSShawn Guo };
238f47c4bbfSShawn Guo 
2394f100012SAndrey Smirnov static const struct esdhc_soc_data esdhc_imx35_data = {
240667123f6SBenoît Thébaudeau 	.flags = ESDHC_FLAG_ERR004536,
241f47c4bbfSShawn Guo };
242f47c4bbfSShawn Guo 
2434f100012SAndrey Smirnov static const struct esdhc_soc_data esdhc_imx51_data = {
244f47c4bbfSShawn Guo 	.flags = 0,
245f47c4bbfSShawn Guo };
246f47c4bbfSShawn Guo 
2474f100012SAndrey Smirnov static const struct esdhc_soc_data esdhc_imx53_data = {
248f47c4bbfSShawn Guo 	.flags = ESDHC_FLAG_MULTIBLK_NO_INT,
249f47c4bbfSShawn Guo };
250f47c4bbfSShawn Guo 
2514f100012SAndrey Smirnov static const struct esdhc_soc_data usdhc_imx6q_data = {
25274898cbcSHaibo Chen 	.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_MAN_TUNING
25374898cbcSHaibo Chen 			| ESDHC_FLAG_BROKEN_AUTO_CMD23,
2546e9fd28eSDong Aisheng };
2556e9fd28eSDong Aisheng 
2564f100012SAndrey Smirnov static const struct esdhc_soc_data usdhc_imx6sl_data = {
2576e9fd28eSDong Aisheng 	.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
2584245afffSDong Aisheng 			| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_ERR004536
25974898cbcSHaibo Chen 			| ESDHC_FLAG_HS200
26074898cbcSHaibo Chen 			| ESDHC_FLAG_BROKEN_AUTO_CMD23,
26174898cbcSHaibo Chen };
26274898cbcSHaibo Chen 
26374898cbcSHaibo Chen static const struct esdhc_soc_data usdhc_imx6sll_data = {
26474898cbcSHaibo Chen 	.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
26574898cbcSHaibo Chen 			| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
26686b59671SHaibo Chen 			| ESDHC_FLAG_HS400
26774898cbcSHaibo Chen 			| ESDHC_FLAG_STATE_LOST_IN_LPMODE,
26857ed3314SShawn Guo };
26957ed3314SShawn Guo 
2704f100012SAndrey Smirnov static const struct esdhc_soc_data usdhc_imx6sx_data = {
271913d4951SDong Aisheng 	.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
272a26a4f1bSHaibo Chen 			| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
27374898cbcSHaibo Chen 			| ESDHC_FLAG_STATE_LOST_IN_LPMODE
27474898cbcSHaibo Chen 			| ESDHC_FLAG_BROKEN_AUTO_CMD23,
275913d4951SDong Aisheng };
276913d4951SDong Aisheng 
277af6a50d4SBOUGH CHEN static const struct esdhc_soc_data usdhc_imx6ull_data = {
278af6a50d4SBOUGH CHEN 	.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
279af6a50d4SBOUGH CHEN 			| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
280a26a4f1bSHaibo Chen 			| ESDHC_FLAG_ERR010450
281a26a4f1bSHaibo Chen 			| ESDHC_FLAG_STATE_LOST_IN_LPMODE,
282af6a50d4SBOUGH CHEN };
283af6a50d4SBOUGH CHEN 
2844f100012SAndrey Smirnov static const struct esdhc_soc_data usdhc_imx7d_data = {
28528b07674SHaibo Chen 	.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
28628b07674SHaibo Chen 			| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
287a26a4f1bSHaibo Chen 			| ESDHC_FLAG_HS400
28874898cbcSHaibo Chen 			| ESDHC_FLAG_STATE_LOST_IN_LPMODE
28974898cbcSHaibo Chen 			| ESDHC_FLAG_BROKEN_AUTO_CMD23,
29028b07674SHaibo Chen };
29128b07674SHaibo Chen 
2921c4989b0SBOUGH CHEN static struct esdhc_soc_data usdhc_imx7ulp_data = {
2931c4989b0SBOUGH CHEN 	.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
2941c4989b0SBOUGH CHEN 			| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
295a26a4f1bSHaibo Chen 			| ESDHC_FLAG_PMQOS | ESDHC_FLAG_HS400
296a26a4f1bSHaibo Chen 			| ESDHC_FLAG_STATE_LOST_IN_LPMODE,
2971c4989b0SBOUGH CHEN };
2981c4989b0SBOUGH CHEN 
299029e2476SBOUGH CHEN static struct esdhc_soc_data usdhc_imx8qxp_data = {
300029e2476SBOUGH CHEN 	.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
301029e2476SBOUGH CHEN 			| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
302bb6e3581SBOUGH CHEN 			| ESDHC_FLAG_HS400 | ESDHC_FLAG_HS400_ES
303a26a4f1bSHaibo Chen 			| ESDHC_FLAG_CQHCI
3045c11f1ffSHaibo Chen 			| ESDHC_FLAG_STATE_LOST_IN_LPMODE
3055c11f1ffSHaibo Chen 			| ESDHC_FLAG_CLK_RATE_LOST_IN_PM_RUNTIME,
306029e2476SBOUGH CHEN };
307029e2476SBOUGH CHEN 
308cde5e8e9SHaibo Chen static struct esdhc_soc_data usdhc_imx8mm_data = {
309cde5e8e9SHaibo Chen 	.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
310cde5e8e9SHaibo Chen 			| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
311cde5e8e9SHaibo Chen 			| ESDHC_FLAG_HS400 | ESDHC_FLAG_HS400_ES
312cde5e8e9SHaibo Chen 			| ESDHC_FLAG_CQHCI
313cde5e8e9SHaibo Chen 			| ESDHC_FLAG_STATE_LOST_IN_LPMODE,
314e149860dSRichard Zhu };
315e149860dSRichard Zhu 
316e149860dSRichard Zhu struct pltfm_imx_data {
317e149860dSRichard Zhu 	u32 scratchpad;
318e62d8b8fSDong Aisheng 	struct pinctrl *pinctrl;
319ad93220dSDong Aisheng 	struct pinctrl_state *pins_100mhz;
320ad93220dSDong Aisheng 	struct pinctrl_state *pins_200mhz;
321f47c4bbfSShawn Guo 	const struct esdhc_soc_data *socdata;
322842afc02SShawn Guo 	struct esdhc_platform_data boarddata;
32352dac615SSascha Hauer 	struct clk *clk_ipg;
32452dac615SSascha Hauer 	struct clk *clk_ahb;
32552dac615SSascha Hauer 	struct clk *clk_per;
3263602785bSMichael Trimarchi 	unsigned int actual_clock;
327361b8482SLucas Stach 	enum {
328361b8482SLucas Stach 		NO_CMD_PENDING,      /* no multiblock command pending */
329361b8482SLucas Stach 		MULTIBLK_IN_PROCESS, /* exact multiblock cmd in process */
330361b8482SLucas Stach 		WAIT_FOR_INT,        /* sent CMD12, waiting for response INT */
331361b8482SLucas Stach 	} multiblock_status;
332de5bdbffSDong Aisheng 	u32 is_ddr;
3331c4989b0SBOUGH CHEN 	struct pm_qos_request pm_qos_req;
334e149860dSRichard Zhu };
335e149860dSRichard Zhu 
336abfafc2dSShawn Guo static const struct of_device_id imx_esdhc_dt_ids[] = {
337f47c4bbfSShawn Guo 	{ .compatible = "fsl,imx25-esdhc", .data = &esdhc_imx25_data, },
338f47c4bbfSShawn Guo 	{ .compatible = "fsl,imx35-esdhc", .data = &esdhc_imx35_data, },
339f47c4bbfSShawn Guo 	{ .compatible = "fsl,imx51-esdhc", .data = &esdhc_imx51_data, },
340f47c4bbfSShawn Guo 	{ .compatible = "fsl,imx53-esdhc", .data = &esdhc_imx53_data, },
341913d4951SDong Aisheng 	{ .compatible = "fsl,imx6sx-usdhc", .data = &usdhc_imx6sx_data, },
3426e9fd28eSDong Aisheng 	{ .compatible = "fsl,imx6sl-usdhc", .data = &usdhc_imx6sl_data, },
34374898cbcSHaibo Chen 	{ .compatible = "fsl,imx6sll-usdhc", .data = &usdhc_imx6sll_data, },
344f47c4bbfSShawn Guo 	{ .compatible = "fsl,imx6q-usdhc", .data = &usdhc_imx6q_data, },
345af6a50d4SBOUGH CHEN 	{ .compatible = "fsl,imx6ull-usdhc", .data = &usdhc_imx6ull_data, },
34628b07674SHaibo Chen 	{ .compatible = "fsl,imx7d-usdhc", .data = &usdhc_imx7d_data, },
3471c4989b0SBOUGH CHEN 	{ .compatible = "fsl,imx7ulp-usdhc", .data = &usdhc_imx7ulp_data, },
348029e2476SBOUGH CHEN 	{ .compatible = "fsl,imx8qxp-usdhc", .data = &usdhc_imx8qxp_data, },
349cde5e8e9SHaibo Chen 	{ .compatible = "fsl,imx8mm-usdhc", .data = &usdhc_imx8mm_data, },
350abfafc2dSShawn Guo 	{ /* sentinel */ }
351abfafc2dSShawn Guo };
352abfafc2dSShawn Guo MODULE_DEVICE_TABLE(of, imx_esdhc_dt_ids);
353abfafc2dSShawn Guo 
35457ed3314SShawn Guo static inline int is_imx25_esdhc(struct pltfm_imx_data *data)
35557ed3314SShawn Guo {
356f47c4bbfSShawn Guo 	return data->socdata == &esdhc_imx25_data;
35757ed3314SShawn Guo }
35857ed3314SShawn Guo 
35957ed3314SShawn Guo static inline int is_imx53_esdhc(struct pltfm_imx_data *data)
36057ed3314SShawn Guo {
361f47c4bbfSShawn Guo 	return data->socdata == &esdhc_imx53_data;
36257ed3314SShawn Guo }
36357ed3314SShawn Guo 
3649d61c009SShawn Guo static inline int esdhc_is_usdhc(struct pltfm_imx_data *data)
3659d61c009SShawn Guo {
366f47c4bbfSShawn Guo 	return !!(data->socdata->flags & ESDHC_FLAG_USDHC);
3679d61c009SShawn Guo }
3689d61c009SShawn Guo 
36995f25efeSWolfram Sang static inline void esdhc_clrset_le(struct sdhci_host *host, u32 mask, u32 val, int reg)
37095f25efeSWolfram Sang {
37195f25efeSWolfram Sang 	void __iomem *base = host->ioaddr + (reg & ~0x3);
37295f25efeSWolfram Sang 	u32 shift = (reg & 0x3) * 8;
37395f25efeSWolfram Sang 
37495f25efeSWolfram Sang 	writel(((readl(base) & ~(mask << shift)) | (val << shift)), base);
37595f25efeSWolfram Sang }
37695f25efeSWolfram Sang 
3773722c74cSHaibo Chen #define DRIVER_NAME "sdhci-esdhc-imx"
3783722c74cSHaibo Chen #define ESDHC_IMX_DUMP(f, x...) \
3793722c74cSHaibo Chen 	pr_err("%s: " DRIVER_NAME ": " f, mmc_hostname(host->mmc), ## x)
3803722c74cSHaibo Chen static void esdhc_dump_debug_regs(struct sdhci_host *host)
3813722c74cSHaibo Chen {
3823722c74cSHaibo Chen 	int i;
3833722c74cSHaibo Chen 	char *debug_status[7] = {
3843722c74cSHaibo Chen 				 "cmd debug status",
3853722c74cSHaibo Chen 				 "data debug status",
3863722c74cSHaibo Chen 				 "trans debug status",
3873722c74cSHaibo Chen 				 "dma debug status",
3883722c74cSHaibo Chen 				 "adma debug status",
3893722c74cSHaibo Chen 				 "fifo debug status",
3903722c74cSHaibo Chen 				 "async fifo debug status"
3913722c74cSHaibo Chen 	};
3923722c74cSHaibo Chen 
3933722c74cSHaibo Chen 	ESDHC_IMX_DUMP("========= ESDHC IMX DEBUG STATUS DUMP =========\n");
3943722c74cSHaibo Chen 	for (i = 0; i < 7; i++) {
3953722c74cSHaibo Chen 		esdhc_clrset_le(host, ESDHC_DEBUG_SEL_MASK,
3963722c74cSHaibo Chen 			ESDHC_DEBUG_SEL_CMD_STATE + i, ESDHC_DEBUG_SEL_REG);
3973722c74cSHaibo Chen 		ESDHC_IMX_DUMP("%s:  0x%04x\n", debug_status[i],
3983722c74cSHaibo Chen 			readw(host->ioaddr + ESDHC_DEBUG_SEL_AND_STATUS_REG));
3993722c74cSHaibo Chen 	}
4003722c74cSHaibo Chen 
4013722c74cSHaibo Chen 	esdhc_clrset_le(host, ESDHC_DEBUG_SEL_MASK, 0, ESDHC_DEBUG_SEL_REG);
4023722c74cSHaibo Chen 
4033722c74cSHaibo Chen }
4043722c74cSHaibo Chen 
405f581e909SHaibo Chen static inline void esdhc_wait_for_card_clock_gate_off(struct sdhci_host *host)
406f581e909SHaibo Chen {
407f581e909SHaibo Chen 	u32 present_state;
408f581e909SHaibo Chen 	int ret;
409f581e909SHaibo Chen 
410f581e909SHaibo Chen 	ret = readl_poll_timeout(host->ioaddr + ESDHC_PRSSTAT, present_state,
411f581e909SHaibo Chen 				(present_state & ESDHC_CLOCK_GATE_OFF), 2, 100);
412f581e909SHaibo Chen 	if (ret == -ETIMEDOUT)
413f581e909SHaibo Chen 		dev_warn(mmc_dev(host->mmc), "%s: card clock still not gate off in 100us!.\n", __func__);
414f581e909SHaibo Chen }
415f581e909SHaibo Chen 
416*45334ee1SHaibo Chen /* Enable the auto tuning circuit to check the CMD line and BUS line */
417*45334ee1SHaibo Chen static inline void usdhc_auto_tuning_mode_sel(struct sdhci_host *host)
418*45334ee1SHaibo Chen {
419*45334ee1SHaibo Chen 	u32 buswidth, auto_tune_buswidth;
420*45334ee1SHaibo Chen 
421*45334ee1SHaibo Chen 	buswidth = USDHC_GET_BUSWIDTH(readl(host->ioaddr + SDHCI_HOST_CONTROL));
422*45334ee1SHaibo Chen 
423*45334ee1SHaibo Chen 	switch (buswidth) {
424*45334ee1SHaibo Chen 	case ESDHC_CTRL_8BITBUS:
425*45334ee1SHaibo Chen 		auto_tune_buswidth = ESDHC_VEND_SPEC2_AUTO_TUNE_8BIT_EN;
426*45334ee1SHaibo Chen 		break;
427*45334ee1SHaibo Chen 	case ESDHC_CTRL_4BITBUS:
428*45334ee1SHaibo Chen 		auto_tune_buswidth = ESDHC_VEND_SPEC2_AUTO_TUNE_4BIT_EN;
429*45334ee1SHaibo Chen 		break;
430*45334ee1SHaibo Chen 	default:	/* 1BITBUS */
431*45334ee1SHaibo Chen 		auto_tune_buswidth = ESDHC_VEND_SPEC2_AUTO_TUNE_1BIT_EN;
432*45334ee1SHaibo Chen 		break;
433*45334ee1SHaibo Chen 	}
434*45334ee1SHaibo Chen 
435*45334ee1SHaibo Chen 	esdhc_clrset_le(host, ESDHC_VEND_SPEC2_AUTO_TUNE_MODE_MASK,
436*45334ee1SHaibo Chen 			auto_tune_buswidth | ESDHC_VEND_SPEC2_AUTO_TUNE_CMD_EN,
437*45334ee1SHaibo Chen 			ESDHC_VEND_SPEC2);
438*45334ee1SHaibo Chen }
439*45334ee1SHaibo Chen 
4407e29c306SWolfram Sang static u32 esdhc_readl_le(struct sdhci_host *host, int reg)
4417e29c306SWolfram Sang {
442361b8482SLucas Stach 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
443070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
444913413c3SShawn Guo 	u32 val = readl(host->ioaddr + reg);
445913413c3SShawn Guo 
4460322191eSDong Aisheng 	if (unlikely(reg == SDHCI_PRESENT_STATE)) {
4470322191eSDong Aisheng 		u32 fsl_prss = val;
4480322191eSDong Aisheng 		/* save the least 20 bits */
4490322191eSDong Aisheng 		val = fsl_prss & 0x000FFFFF;
4500322191eSDong Aisheng 		/* move dat[0-3] bits */
4510322191eSDong Aisheng 		val |= (fsl_prss & 0x0F000000) >> 4;
4520322191eSDong Aisheng 		/* move cmd line bit */
4530322191eSDong Aisheng 		val |= (fsl_prss & 0x00800000) << 1;
4540322191eSDong Aisheng 	}
4550322191eSDong Aisheng 
45697e4ba6aSRichard Zhu 	if (unlikely(reg == SDHCI_CAPABILITIES)) {
4576b4fb671SDong Aisheng 		/* ignore bit[0-15] as it stores cap_1 register val for mx6sl */
4586b4fb671SDong Aisheng 		if (imx_data->socdata->flags & ESDHC_FLAG_HAVE_CAP1)
4596b4fb671SDong Aisheng 			val &= 0xffff0000;
4606b4fb671SDong Aisheng 
46197e4ba6aSRichard Zhu 		/* In FSL esdhc IC module, only bit20 is used to indicate the
46297e4ba6aSRichard Zhu 		 * ADMA2 capability of esdhc, but this bit is messed up on
46397e4ba6aSRichard Zhu 		 * some SOCs (e.g. on MX25, MX35 this bit is set, but they
46497e4ba6aSRichard Zhu 		 * don't actually support ADMA2). So set the BROKEN_ADMA
465d04f8d5bSBenoît Thébaudeau 		 * quirk on MX25/35 platforms.
46697e4ba6aSRichard Zhu 		 */
46797e4ba6aSRichard Zhu 
46897e4ba6aSRichard Zhu 		if (val & SDHCI_CAN_DO_ADMA1) {
46997e4ba6aSRichard Zhu 			val &= ~SDHCI_CAN_DO_ADMA1;
47097e4ba6aSRichard Zhu 			val |= SDHCI_CAN_DO_ADMA2;
47197e4ba6aSRichard Zhu 		}
47297e4ba6aSRichard Zhu 	}
47397e4ba6aSRichard Zhu 
4746e9fd28eSDong Aisheng 	if (unlikely(reg == SDHCI_CAPABILITIES_1)) {
4756e9fd28eSDong Aisheng 		if (esdhc_is_usdhc(imx_data)) {
4766e9fd28eSDong Aisheng 			if (imx_data->socdata->flags & ESDHC_FLAG_HAVE_CAP1)
4776e9fd28eSDong Aisheng 				val = readl(host->ioaddr + SDHCI_CAPABILITIES) & 0xFFFF;
4786e9fd28eSDong Aisheng 			else
4796e9fd28eSDong Aisheng 				/* imx6q/dl does not have cap_1 register, fake one */
4800322191eSDong Aisheng 				val = SDHCI_SUPPORT_DDR50 | SDHCI_SUPPORT_SDR104
481888824bbSDong Aisheng 					| SDHCI_SUPPORT_SDR50
482da0295ffSDong Aisheng 					| SDHCI_USE_SDR50_TUNING
483a8e809ecSMasahiro Yamada 					| FIELD_PREP(SDHCI_RETUNING_MODE_MASK,
484a8e809ecSMasahiro Yamada 						     SDHCI_TUNING_MODE_3);
48528b07674SHaibo Chen 
48692748beaSStefan Agner 			/*
48792748beaSStefan Agner 			 * Do not advertise faster UHS modes if there are no
48892748beaSStefan Agner 			 * pinctrl states for 100MHz/200MHz.
48992748beaSStefan Agner 			 */
49025e8b9ebSShawn Guo 			if (IS_ERR_OR_NULL(imx_data->pins_100mhz))
49125e8b9ebSShawn Guo 				val &= ~(SDHCI_SUPPORT_SDR50 | SDHCI_SUPPORT_DDR50);
49225e8b9ebSShawn Guo 			if (IS_ERR_OR_NULL(imx_data->pins_200mhz))
49325e8b9ebSShawn Guo 				val &= ~(SDHCI_SUPPORT_SDR104 | SDHCI_SUPPORT_HS400);
4946e9fd28eSDong Aisheng 		}
4956e9fd28eSDong Aisheng 	}
4960322191eSDong Aisheng 
4979d61c009SShawn Guo 	if (unlikely(reg == SDHCI_MAX_CURRENT) && esdhc_is_usdhc(imx_data)) {
4980322191eSDong Aisheng 		val = 0;
499804a65b3SMasahiro Yamada 		val |= FIELD_PREP(SDHCI_MAX_CURRENT_330_MASK, 0xFF);
500804a65b3SMasahiro Yamada 		val |= FIELD_PREP(SDHCI_MAX_CURRENT_300_MASK, 0xFF);
501804a65b3SMasahiro Yamada 		val |= FIELD_PREP(SDHCI_MAX_CURRENT_180_MASK, 0xFF);
5020322191eSDong Aisheng 	}
5030322191eSDong Aisheng 
50497e4ba6aSRichard Zhu 	if (unlikely(reg == SDHCI_INT_STATUS)) {
50560bf6396SShawn Guo 		if (val & ESDHC_INT_VENDOR_SPEC_DMA_ERR) {
50660bf6396SShawn Guo 			val &= ~ESDHC_INT_VENDOR_SPEC_DMA_ERR;
50797e4ba6aSRichard Zhu 			val |= SDHCI_INT_ADMA_ERROR;
50897e4ba6aSRichard Zhu 		}
509361b8482SLucas Stach 
510361b8482SLucas Stach 		/*
511361b8482SLucas Stach 		 * mask off the interrupt we get in response to the manually
512361b8482SLucas Stach 		 * sent CMD12
513361b8482SLucas Stach 		 */
514361b8482SLucas Stach 		if ((imx_data->multiblock_status == WAIT_FOR_INT) &&
515361b8482SLucas Stach 		    ((val & SDHCI_INT_RESPONSE) == SDHCI_INT_RESPONSE)) {
516361b8482SLucas Stach 			val &= ~SDHCI_INT_RESPONSE;
517361b8482SLucas Stach 			writel(SDHCI_INT_RESPONSE, host->ioaddr +
518361b8482SLucas Stach 						   SDHCI_INT_STATUS);
519361b8482SLucas Stach 			imx_data->multiblock_status = NO_CMD_PENDING;
520361b8482SLucas Stach 		}
52197e4ba6aSRichard Zhu 	}
52297e4ba6aSRichard Zhu 
5237e29c306SWolfram Sang 	return val;
5247e29c306SWolfram Sang }
5257e29c306SWolfram Sang 
5267e29c306SWolfram Sang static void esdhc_writel_le(struct sdhci_host *host, u32 val, int reg)
5277e29c306SWolfram Sang {
528e149860dSRichard Zhu 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
529070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
5300d58864bSTony Lin 	u32 data;
531e149860dSRichard Zhu 
53277da3da0SAaron Brice 	if (unlikely(reg == SDHCI_INT_ENABLE || reg == SDHCI_SIGNAL_ENABLE ||
53377da3da0SAaron Brice 			reg == SDHCI_INT_STATUS)) {
534b7321042SDong Aisheng 		if ((val & SDHCI_INT_CARD_INT) && !esdhc_is_usdhc(imx_data)) {
5350d58864bSTony Lin 			/*
5360d58864bSTony Lin 			 * Clear and then set D3CD bit to avoid missing the
537d04f8d5bSBenoît Thébaudeau 			 * card interrupt. This is an eSDHC controller problem
5380d58864bSTony Lin 			 * so we need to apply the following workaround: clear
5390d58864bSTony Lin 			 * and set D3CD bit will make eSDHC re-sample the card
5400d58864bSTony Lin 			 * interrupt. In case a card interrupt was lost,
5410d58864bSTony Lin 			 * re-sample it by the following steps.
5420d58864bSTony Lin 			 */
5430d58864bSTony Lin 			data = readl(host->ioaddr + SDHCI_HOST_CONTROL);
54460bf6396SShawn Guo 			data &= ~ESDHC_CTRL_D3CD;
5450d58864bSTony Lin 			writel(data, host->ioaddr + SDHCI_HOST_CONTROL);
54660bf6396SShawn Guo 			data |= ESDHC_CTRL_D3CD;
5470d58864bSTony Lin 			writel(data, host->ioaddr + SDHCI_HOST_CONTROL);
5480d58864bSTony Lin 		}
549915be485SDong Aisheng 
550915be485SDong Aisheng 		if (val & SDHCI_INT_ADMA_ERROR) {
551915be485SDong Aisheng 			val &= ~SDHCI_INT_ADMA_ERROR;
552915be485SDong Aisheng 			val |= ESDHC_INT_VENDOR_SPEC_DMA_ERR;
553915be485SDong Aisheng 		}
5540d58864bSTony Lin 	}
5550d58864bSTony Lin 
556f47c4bbfSShawn Guo 	if (unlikely((imx_data->socdata->flags & ESDHC_FLAG_MULTIBLK_NO_INT)
55758ac8177SRichard Zhu 				&& (reg == SDHCI_INT_STATUS)
55858ac8177SRichard Zhu 				&& (val & SDHCI_INT_DATA_END))) {
55958ac8177SRichard Zhu 			u32 v;
56060bf6396SShawn Guo 			v = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
56160bf6396SShawn Guo 			v &= ~ESDHC_VENDOR_SPEC_SDIO_QUIRK;
56260bf6396SShawn Guo 			writel(v, host->ioaddr + ESDHC_VENDOR_SPEC);
563361b8482SLucas Stach 
564361b8482SLucas Stach 			if (imx_data->multiblock_status == MULTIBLK_IN_PROCESS)
565361b8482SLucas Stach 			{
566361b8482SLucas Stach 				/* send a manual CMD12 with RESPTYP=none */
567361b8482SLucas Stach 				data = MMC_STOP_TRANSMISSION << 24 |
568361b8482SLucas Stach 				       SDHCI_CMD_ABORTCMD << 16;
569361b8482SLucas Stach 				writel(data, host->ioaddr + SDHCI_TRANSFER_MODE);
570361b8482SLucas Stach 				imx_data->multiblock_status = WAIT_FOR_INT;
571361b8482SLucas Stach 			}
57258ac8177SRichard Zhu 	}
57358ac8177SRichard Zhu 
5747e29c306SWolfram Sang 	writel(val, host->ioaddr + reg);
5757e29c306SWolfram Sang }
5767e29c306SWolfram Sang 
57795f25efeSWolfram Sang static u16 esdhc_readw_le(struct sdhci_host *host, int reg)
57895f25efeSWolfram Sang {
579ef4d0888SShawn Guo 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
580070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
5810322191eSDong Aisheng 	u16 ret = 0;
5820322191eSDong Aisheng 	u32 val;
583ef4d0888SShawn Guo 
58495a2482aSShawn Guo 	if (unlikely(reg == SDHCI_HOST_VERSION)) {
585ef4d0888SShawn Guo 		reg ^= 2;
5869d61c009SShawn Guo 		if (esdhc_is_usdhc(imx_data)) {
58795a2482aSShawn Guo 			/*
588ef4d0888SShawn Guo 			 * The usdhc register returns a wrong host version.
589ef4d0888SShawn Guo 			 * Correct it here.
59095a2482aSShawn Guo 			 */
591ef4d0888SShawn Guo 			return SDHCI_SPEC_300;
592ef4d0888SShawn Guo 		}
59395a2482aSShawn Guo 	}
59495f25efeSWolfram Sang 
5950322191eSDong Aisheng 	if (unlikely(reg == SDHCI_HOST_CONTROL2)) {
5960322191eSDong Aisheng 		val = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
5970322191eSDong Aisheng 		if (val & ESDHC_VENDOR_SPEC_VSELECT)
5980322191eSDong Aisheng 			ret |= SDHCI_CTRL_VDD_180;
5990322191eSDong Aisheng 
6009d61c009SShawn Guo 		if (esdhc_is_usdhc(imx_data)) {
6016e9fd28eSDong Aisheng 			if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING)
6020322191eSDong Aisheng 				val = readl(host->ioaddr + ESDHC_MIX_CTRL);
6036e9fd28eSDong Aisheng 			else if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING)
6046e9fd28eSDong Aisheng 				/* the std tuning bits is in ACMD12_ERR for imx6sl */
605869f8a69SAdrian Hunter 				val = readl(host->ioaddr + SDHCI_AUTO_CMD_STATUS);
6066e9fd28eSDong Aisheng 		}
6076e9fd28eSDong Aisheng 
6080322191eSDong Aisheng 		if (val & ESDHC_MIX_CTRL_EXE_TUNE)
6090322191eSDong Aisheng 			ret |= SDHCI_CTRL_EXEC_TUNING;
6100322191eSDong Aisheng 		if (val & ESDHC_MIX_CTRL_SMPCLK_SEL)
6110322191eSDong Aisheng 			ret |= SDHCI_CTRL_TUNED_CLK;
6120322191eSDong Aisheng 
6130322191eSDong Aisheng 		ret &= ~SDHCI_CTRL_PRESET_VAL_ENABLE;
6140322191eSDong Aisheng 
6150322191eSDong Aisheng 		return ret;
6160322191eSDong Aisheng 	}
6170322191eSDong Aisheng 
6187dd109efSDong Aisheng 	if (unlikely(reg == SDHCI_TRANSFER_MODE)) {
6197dd109efSDong Aisheng 		if (esdhc_is_usdhc(imx_data)) {
6207dd109efSDong Aisheng 			u32 m = readl(host->ioaddr + ESDHC_MIX_CTRL);
6217dd109efSDong Aisheng 			ret = m & ESDHC_MIX_CTRL_SDHCI_MASK;
6227dd109efSDong Aisheng 			/* Swap AC23 bit */
6237dd109efSDong Aisheng 			if (m & ESDHC_MIX_CTRL_AC23EN) {
6247dd109efSDong Aisheng 				ret &= ~ESDHC_MIX_CTRL_AC23EN;
6257dd109efSDong Aisheng 				ret |= SDHCI_TRNS_AUTO_CMD23;
6267dd109efSDong Aisheng 			}
6277dd109efSDong Aisheng 		} else {
6287dd109efSDong Aisheng 			ret = readw(host->ioaddr + SDHCI_TRANSFER_MODE);
6297dd109efSDong Aisheng 		}
6307dd109efSDong Aisheng 
6317dd109efSDong Aisheng 		return ret;
6327dd109efSDong Aisheng 	}
6337dd109efSDong Aisheng 
63495f25efeSWolfram Sang 	return readw(host->ioaddr + reg);
63595f25efeSWolfram Sang }
63695f25efeSWolfram Sang 
63795f25efeSWolfram Sang static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg)
63895f25efeSWolfram Sang {
63995f25efeSWolfram Sang 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
640070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
6410322191eSDong Aisheng 	u32 new_val = 0;
64295f25efeSWolfram Sang 
64395f25efeSWolfram Sang 	switch (reg) {
6440322191eSDong Aisheng 	case SDHCI_CLOCK_CONTROL:
6450322191eSDong Aisheng 		new_val = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
6460322191eSDong Aisheng 		if (val & SDHCI_CLOCK_CARD_EN)
6470322191eSDong Aisheng 			new_val |= ESDHC_VENDOR_SPEC_FRC_SDCLK_ON;
6480322191eSDong Aisheng 		else
6490322191eSDong Aisheng 			new_val &= ~ESDHC_VENDOR_SPEC_FRC_SDCLK_ON;
6500322191eSDong Aisheng 		writel(new_val, host->ioaddr + ESDHC_VENDOR_SPEC);
651f581e909SHaibo Chen 		if (!(new_val & ESDHC_VENDOR_SPEC_FRC_SDCLK_ON))
652f581e909SHaibo Chen 			esdhc_wait_for_card_clock_gate_off(host);
6530322191eSDong Aisheng 		return;
6540322191eSDong Aisheng 	case SDHCI_HOST_CONTROL2:
6550322191eSDong Aisheng 		new_val = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
6560322191eSDong Aisheng 		if (val & SDHCI_CTRL_VDD_180)
6570322191eSDong Aisheng 			new_val |= ESDHC_VENDOR_SPEC_VSELECT;
6580322191eSDong Aisheng 		else
6590322191eSDong Aisheng 			new_val &= ~ESDHC_VENDOR_SPEC_VSELECT;
6600322191eSDong Aisheng 		writel(new_val, host->ioaddr + ESDHC_VENDOR_SPEC);
661a0dbbdc2SHaibo Chen 		if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) {
662869f8a69SAdrian Hunter 			u32 v = readl(host->ioaddr + SDHCI_AUTO_CMD_STATUS);
6636e9fd28eSDong Aisheng 			u32 m = readl(host->ioaddr + ESDHC_MIX_CTRL);
6648b2bb0adSDong Aisheng 			if (val & SDHCI_CTRL_TUNED_CLK) {
6658b2bb0adSDong Aisheng 				v |= ESDHC_MIX_CTRL_SMPCLK_SEL;
6666e9fd28eSDong Aisheng 			} else {
6678b2bb0adSDong Aisheng 				v &= ~ESDHC_MIX_CTRL_SMPCLK_SEL;
6686e9fd28eSDong Aisheng 				m &= ~ESDHC_MIX_CTRL_FBCLK_SEL;
6690b330e38SDong Aisheng 				m &= ~ESDHC_MIX_CTRL_AUTO_TUNE_EN;
6706e9fd28eSDong Aisheng 			}
6716e9fd28eSDong Aisheng 
6728b2bb0adSDong Aisheng 			if (val & SDHCI_CTRL_EXEC_TUNING) {
6738b2bb0adSDong Aisheng 				v |= ESDHC_MIX_CTRL_EXE_TUNE;
6748b2bb0adSDong Aisheng 				m |= ESDHC_MIX_CTRL_FBCLK_SEL;
6750b330e38SDong Aisheng 				m |= ESDHC_MIX_CTRL_AUTO_TUNE_EN;
676*45334ee1SHaibo Chen 				usdhc_auto_tuning_mode_sel(host);
6778b2bb0adSDong Aisheng 			} else {
6788b2bb0adSDong Aisheng 				v &= ~ESDHC_MIX_CTRL_EXE_TUNE;
6798b2bb0adSDong Aisheng 			}
6806e9fd28eSDong Aisheng 
681869f8a69SAdrian Hunter 			writel(v, host->ioaddr + SDHCI_AUTO_CMD_STATUS);
6826e9fd28eSDong Aisheng 			writel(m, host->ioaddr + ESDHC_MIX_CTRL);
6836e9fd28eSDong Aisheng 		}
6840322191eSDong Aisheng 		return;
68595f25efeSWolfram Sang 	case SDHCI_TRANSFER_MODE:
686f47c4bbfSShawn Guo 		if ((imx_data->socdata->flags & ESDHC_FLAG_MULTIBLK_NO_INT)
68758ac8177SRichard Zhu 				&& (host->cmd->opcode == SD_IO_RW_EXTENDED)
68858ac8177SRichard Zhu 				&& (host->cmd->data->blocks > 1)
68958ac8177SRichard Zhu 				&& (host->cmd->data->flags & MMC_DATA_READ)) {
69058ac8177SRichard Zhu 			u32 v;
69160bf6396SShawn Guo 			v = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
69260bf6396SShawn Guo 			v |= ESDHC_VENDOR_SPEC_SDIO_QUIRK;
69360bf6396SShawn Guo 			writel(v, host->ioaddr + ESDHC_VENDOR_SPEC);
69458ac8177SRichard Zhu 		}
69569f54698SShawn Guo 
6969d61c009SShawn Guo 		if (esdhc_is_usdhc(imx_data)) {
6973fbd4322SAndrew Gabbasov 			u32 wml;
69869f54698SShawn Guo 			u32 m = readl(host->ioaddr + ESDHC_MIX_CTRL);
6992a15f981SShawn Guo 			/* Swap AC23 bit */
7002a15f981SShawn Guo 			if (val & SDHCI_TRNS_AUTO_CMD23) {
7012a15f981SShawn Guo 				val &= ~SDHCI_TRNS_AUTO_CMD23;
7022a15f981SShawn Guo 				val |= ESDHC_MIX_CTRL_AC23EN;
7032a15f981SShawn Guo 			}
7042a15f981SShawn Guo 			m = val | (m & ~ESDHC_MIX_CTRL_SDHCI_MASK);
70569f54698SShawn Guo 			writel(m, host->ioaddr + ESDHC_MIX_CTRL);
7063fbd4322SAndrew Gabbasov 
7073fbd4322SAndrew Gabbasov 			/* Set watermark levels for PIO access to maximum value
7083fbd4322SAndrew Gabbasov 			 * (128 words) to accommodate full 512 bytes buffer.
7093fbd4322SAndrew Gabbasov 			 * For DMA access restore the levels to default value.
7103fbd4322SAndrew Gabbasov 			 */
7113fbd4322SAndrew Gabbasov 			m = readl(host->ioaddr + ESDHC_WTMK_LVL);
712e534b82fSHaibo Chen 			if (val & SDHCI_TRNS_DMA) {
7133fbd4322SAndrew Gabbasov 				wml = ESDHC_WTMK_LVL_WML_VAL_DEF;
714e534b82fSHaibo Chen 			} else {
715e534b82fSHaibo Chen 				u8 ctrl;
7163fbd4322SAndrew Gabbasov 				wml = ESDHC_WTMK_LVL_WML_VAL_MAX;
717e534b82fSHaibo Chen 
718e534b82fSHaibo Chen 				/*
719e534b82fSHaibo Chen 				 * Since already disable DMA mode, so also need
720e534b82fSHaibo Chen 				 * to clear the DMASEL. Otherwise, for standard
721e534b82fSHaibo Chen 				 * tuning, when send tuning command, usdhc will
722e534b82fSHaibo Chen 				 * still prefetch the ADMA script from wrong
723e534b82fSHaibo Chen 				 * DMA address, then we will see IOMMU report
724e534b82fSHaibo Chen 				 * some error which show lack of TLB mapping.
725e534b82fSHaibo Chen 				 */
726e534b82fSHaibo Chen 				ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
727e534b82fSHaibo Chen 				ctrl &= ~SDHCI_CTRL_DMA_MASK;
728e534b82fSHaibo Chen 				sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
729e534b82fSHaibo Chen 			}
7303fbd4322SAndrew Gabbasov 			m &= ~(ESDHC_WTMK_LVL_RD_WML_MASK |
7313fbd4322SAndrew Gabbasov 			       ESDHC_WTMK_LVL_WR_WML_MASK);
7323fbd4322SAndrew Gabbasov 			m |= (wml << ESDHC_WTMK_LVL_RD_WML_SHIFT) |
7333fbd4322SAndrew Gabbasov 			     (wml << ESDHC_WTMK_LVL_WR_WML_SHIFT);
7343fbd4322SAndrew Gabbasov 			writel(m, host->ioaddr + ESDHC_WTMK_LVL);
73569f54698SShawn Guo 		} else {
73669f54698SShawn Guo 			/*
73769f54698SShawn Guo 			 * Postpone this write, we must do it together with a
73869f54698SShawn Guo 			 * command write that is down below.
73969f54698SShawn Guo 			 */
740e149860dSRichard Zhu 			imx_data->scratchpad = val;
74169f54698SShawn Guo 		}
74295f25efeSWolfram Sang 		return;
74395f25efeSWolfram Sang 	case SDHCI_COMMAND:
744361b8482SLucas Stach 		if (host->cmd->opcode == MMC_STOP_TRANSMISSION)
74558ac8177SRichard Zhu 			val |= SDHCI_CMD_ABORTCMD;
74695a2482aSShawn Guo 
747361b8482SLucas Stach 		if ((host->cmd->opcode == MMC_SET_BLOCK_COUNT) &&
748f47c4bbfSShawn Guo 		    (imx_data->socdata->flags & ESDHC_FLAG_MULTIBLK_NO_INT))
749361b8482SLucas Stach 			imx_data->multiblock_status = MULTIBLK_IN_PROCESS;
750361b8482SLucas Stach 
7519d61c009SShawn Guo 		if (esdhc_is_usdhc(imx_data))
75295a2482aSShawn Guo 			writel(val << 16,
75395a2482aSShawn Guo 			       host->ioaddr + SDHCI_TRANSFER_MODE);
75469f54698SShawn Guo 		else
755e149860dSRichard Zhu 			writel(val << 16 | imx_data->scratchpad,
75695f25efeSWolfram Sang 			       host->ioaddr + SDHCI_TRANSFER_MODE);
75795f25efeSWolfram Sang 		return;
75895f25efeSWolfram Sang 	case SDHCI_BLOCK_SIZE:
75995f25efeSWolfram Sang 		val &= ~SDHCI_MAKE_BLKSZ(0x7, 0);
76095f25efeSWolfram Sang 		break;
76195f25efeSWolfram Sang 	}
76295f25efeSWolfram Sang 	esdhc_clrset_le(host, 0xffff, val, reg);
76395f25efeSWolfram Sang }
76495f25efeSWolfram Sang 
76577da3da0SAaron Brice static u8 esdhc_readb_le(struct sdhci_host *host, int reg)
76677da3da0SAaron Brice {
76777da3da0SAaron Brice 	u8 ret;
76877da3da0SAaron Brice 	u32 val;
76977da3da0SAaron Brice 
77077da3da0SAaron Brice 	switch (reg) {
77177da3da0SAaron Brice 	case SDHCI_HOST_CONTROL:
77277da3da0SAaron Brice 		val = readl(host->ioaddr + reg);
77377da3da0SAaron Brice 
77477da3da0SAaron Brice 		ret = val & SDHCI_CTRL_LED;
77577da3da0SAaron Brice 		ret |= (val >> 5) & SDHCI_CTRL_DMA_MASK;
77677da3da0SAaron Brice 		ret |= (val & ESDHC_CTRL_4BITBUS);
77777da3da0SAaron Brice 		ret |= (val & ESDHC_CTRL_8BITBUS) << 3;
77877da3da0SAaron Brice 		return ret;
77977da3da0SAaron Brice 	}
78077da3da0SAaron Brice 
78177da3da0SAaron Brice 	return readb(host->ioaddr + reg);
78277da3da0SAaron Brice }
78377da3da0SAaron Brice 
78495f25efeSWolfram Sang static void esdhc_writeb_le(struct sdhci_host *host, u8 val, int reg)
78595f25efeSWolfram Sang {
7869a0985b7SWilson Callan 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
787070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
78881a0a8bcSBenoît Thébaudeau 	u32 new_val = 0;
789af51079eSSascha Hauer 	u32 mask;
79095f25efeSWolfram Sang 
79195f25efeSWolfram Sang 	switch (reg) {
79295f25efeSWolfram Sang 	case SDHCI_POWER_CONTROL:
79395f25efeSWolfram Sang 		/*
79495f25efeSWolfram Sang 		 * FSL put some DMA bits here
79595f25efeSWolfram Sang 		 * If your board has a regulator, code should be here
79695f25efeSWolfram Sang 		 */
79795f25efeSWolfram Sang 		return;
79895f25efeSWolfram Sang 	case SDHCI_HOST_CONTROL:
7996b40d182SShawn Guo 		/* FSL messed up here, so we need to manually compose it. */
800af51079eSSascha Hauer 		new_val = val & SDHCI_CTRL_LED;
8017122bbb0SMasanari Iida 		/* ensure the endianness */
80295f25efeSWolfram Sang 		new_val |= ESDHC_HOST_CONTROL_LE;
8039a0985b7SWilson Callan 		/* bits 8&9 are reserved on mx25 */
8049a0985b7SWilson Callan 		if (!is_imx25_esdhc(imx_data)) {
80595f25efeSWolfram Sang 			/* DMA mode bits are shifted */
80695f25efeSWolfram Sang 			new_val |= (val & SDHCI_CTRL_DMA_MASK) << 5;
8079a0985b7SWilson Callan 		}
80895f25efeSWolfram Sang 
809af51079eSSascha Hauer 		/*
810af51079eSSascha Hauer 		 * Do not touch buswidth bits here. This is done in
811af51079eSSascha Hauer 		 * esdhc_pltfm_bus_width.
812f6825748SMartin Fuzzey 		 * Do not touch the D3CD bit either which is used for the
813d04f8d5bSBenoît Thébaudeau 		 * SDIO interrupt erratum workaround.
814af51079eSSascha Hauer 		 */
815f6825748SMartin Fuzzey 		mask = 0xffff & ~(ESDHC_CTRL_BUSWIDTH_MASK | ESDHC_CTRL_D3CD);
816af51079eSSascha Hauer 
817af51079eSSascha Hauer 		esdhc_clrset_le(host, mask, new_val, reg);
81895f25efeSWolfram Sang 		return;
81981a0a8bcSBenoît Thébaudeau 	case SDHCI_SOFTWARE_RESET:
82081a0a8bcSBenoît Thébaudeau 		if (val & SDHCI_RESET_DATA)
82181a0a8bcSBenoît Thébaudeau 			new_val = readl(host->ioaddr + SDHCI_HOST_CONTROL);
82281a0a8bcSBenoît Thébaudeau 		break;
82395f25efeSWolfram Sang 	}
82495f25efeSWolfram Sang 	esdhc_clrset_le(host, 0xff, val, reg);
825913413c3SShawn Guo 
82681a0a8bcSBenoît Thébaudeau 	if (reg == SDHCI_SOFTWARE_RESET) {
82781a0a8bcSBenoît Thébaudeau 		if (val & SDHCI_RESET_ALL) {
828913413c3SShawn Guo 			/*
82981a0a8bcSBenoît Thébaudeau 			 * The esdhc has a design violation to SDHC spec which
83081a0a8bcSBenoît Thébaudeau 			 * tells that software reset should not affect card
83181a0a8bcSBenoît Thébaudeau 			 * detection circuit. But esdhc clears its SYSCTL
83281a0a8bcSBenoît Thébaudeau 			 * register bits [0..2] during the software reset. This
83381a0a8bcSBenoît Thébaudeau 			 * will stop those clocks that card detection circuit
83481a0a8bcSBenoît Thébaudeau 			 * relies on. To work around it, we turn the clocks on
83581a0a8bcSBenoît Thébaudeau 			 * back to keep card detection circuit functional.
836913413c3SShawn Guo 			 */
837913413c3SShawn Guo 			esdhc_clrset_le(host, 0x7, 0x7, ESDHC_SYSTEM_CONTROL);
83858c8c4fbSShawn Guo 			/*
83958c8c4fbSShawn Guo 			 * The reset on usdhc fails to clear MIX_CTRL register.
84058c8c4fbSShawn Guo 			 * Do it manually here.
84158c8c4fbSShawn Guo 			 */
842de5bdbffSDong Aisheng 			if (esdhc_is_usdhc(imx_data)) {
84381a0a8bcSBenoît Thébaudeau 				/*
84481a0a8bcSBenoît Thébaudeau 				 * the tuning bits should be kept during reset
84581a0a8bcSBenoît Thébaudeau 				 */
846d131a71cSDong Aisheng 				new_val = readl(host->ioaddr + ESDHC_MIX_CTRL);
847d131a71cSDong Aisheng 				writel(new_val & ESDHC_MIX_CTRL_TUNING_MASK,
848d131a71cSDong Aisheng 						host->ioaddr + ESDHC_MIX_CTRL);
849de5bdbffSDong Aisheng 				imx_data->is_ddr = 0;
850de5bdbffSDong Aisheng 			}
85181a0a8bcSBenoît Thébaudeau 		} else if (val & SDHCI_RESET_DATA) {
85281a0a8bcSBenoît Thébaudeau 			/*
85381a0a8bcSBenoît Thébaudeau 			 * The eSDHC DAT line software reset clears at least the
85481a0a8bcSBenoît Thébaudeau 			 * data transfer width on i.MX25, so make sure that the
85581a0a8bcSBenoît Thébaudeau 			 * Host Control register is unaffected.
85681a0a8bcSBenoît Thébaudeau 			 */
85781a0a8bcSBenoît Thébaudeau 			esdhc_clrset_le(host, 0xff, new_val,
85881a0a8bcSBenoît Thébaudeau 					SDHCI_HOST_CONTROL);
85981a0a8bcSBenoît Thébaudeau 		}
86058c8c4fbSShawn Guo 	}
86195f25efeSWolfram Sang }
86295f25efeSWolfram Sang 
8630ddf03c9SLucas Stach static unsigned int esdhc_pltfm_get_max_clock(struct sdhci_host *host)
8640ddf03c9SLucas Stach {
8650ddf03c9SLucas Stach 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
8660ddf03c9SLucas Stach 
867a974862fSDong Aisheng 	return pltfm_host->clock;
8680ddf03c9SLucas Stach }
8690ddf03c9SLucas Stach 
87095f25efeSWolfram Sang static unsigned int esdhc_pltfm_get_min_clock(struct sdhci_host *host)
87195f25efeSWolfram Sang {
87295f25efeSWolfram Sang 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
87395f25efeSWolfram Sang 
874a974862fSDong Aisheng 	return pltfm_host->clock / 256 / 16;
87595f25efeSWolfram Sang }
87695f25efeSWolfram Sang 
8778ba9580aSLucas Stach static inline void esdhc_pltfm_set_clock(struct sdhci_host *host,
8788ba9580aSLucas Stach 					 unsigned int clock)
8798ba9580aSLucas Stach {
8808ba9580aSLucas Stach 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
881070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
882a974862fSDong Aisheng 	unsigned int host_clock = pltfm_host->clock;
8835143c953SBenoît Thébaudeau 	int ddr_pre_div = imx_data->is_ddr ? 2 : 1;
8845143c953SBenoît Thébaudeau 	int pre_div = 1;
885d31fc00aSDong Aisheng 	int div = 1;
886f581e909SHaibo Chen 	int ret;
887fed2f6e2SDong Aisheng 	u32 temp, val;
8888ba9580aSLucas Stach 
8899d61c009SShawn Guo 	if (esdhc_is_usdhc(imx_data)) {
890fed2f6e2SDong Aisheng 		val = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
891fed2f6e2SDong Aisheng 		writel(val & ~ESDHC_VENDOR_SPEC_FRC_SDCLK_ON,
892fed2f6e2SDong Aisheng 			host->ioaddr + ESDHC_VENDOR_SPEC);
893f581e909SHaibo Chen 		esdhc_wait_for_card_clock_gate_off(host);
894fed2f6e2SDong Aisheng 	}
89573e736f8SStefan Agner 
89673e736f8SStefan Agner 	if (clock == 0) {
89773e736f8SStefan Agner 		host->mmc->actual_clock = 0;
898373073efSRussell King 		return;
899fed2f6e2SDong Aisheng 	}
900d31fc00aSDong Aisheng 
901499ed50fSBenoît Thébaudeau 	/* For i.MX53 eSDHCv3, SYSCTL.SDCLKFS may not be set to 0. */
902499ed50fSBenoît Thébaudeau 	if (is_imx53_esdhc(imx_data)) {
903499ed50fSBenoît Thébaudeau 		/*
904499ed50fSBenoît Thébaudeau 		 * According to the i.MX53 reference manual, if DLLCTRL[10] can
905499ed50fSBenoît Thébaudeau 		 * be set, then the controller is eSDHCv3, else it is eSDHCv2.
906499ed50fSBenoît Thébaudeau 		 */
907499ed50fSBenoît Thébaudeau 		val = readl(host->ioaddr + ESDHC_DLL_CTRL);
908499ed50fSBenoît Thébaudeau 		writel(val | BIT(10), host->ioaddr + ESDHC_DLL_CTRL);
909499ed50fSBenoît Thébaudeau 		temp = readl(host->ioaddr + ESDHC_DLL_CTRL);
910499ed50fSBenoît Thébaudeau 		writel(val, host->ioaddr + ESDHC_DLL_CTRL);
911499ed50fSBenoît Thébaudeau 		if (temp & BIT(10))
912499ed50fSBenoît Thébaudeau 			pre_div = 2;
913499ed50fSBenoît Thébaudeau 	}
914499ed50fSBenoît Thébaudeau 
915d31fc00aSDong Aisheng 	temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
916d31fc00aSDong Aisheng 	temp &= ~(ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN
917d31fc00aSDong Aisheng 		| ESDHC_CLOCK_MASK);
918d31fc00aSDong Aisheng 	sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
919d31fc00aSDong Aisheng 
920af6a50d4SBOUGH CHEN 	if (imx_data->socdata->flags & ESDHC_FLAG_ERR010450) {
921af6a50d4SBOUGH CHEN 		unsigned int max_clock;
922af6a50d4SBOUGH CHEN 
923af6a50d4SBOUGH CHEN 		max_clock = imx_data->is_ddr ? 45000000 : 150000000;
924af6a50d4SBOUGH CHEN 
925af6a50d4SBOUGH CHEN 		clock = min(clock, max_clock);
926af6a50d4SBOUGH CHEN 	}
927af6a50d4SBOUGH CHEN 
9285143c953SBenoît Thébaudeau 	while (host_clock / (16 * pre_div * ddr_pre_div) > clock &&
9295143c953SBenoît Thébaudeau 			pre_div < 256)
930d31fc00aSDong Aisheng 		pre_div *= 2;
931d31fc00aSDong Aisheng 
9325143c953SBenoît Thébaudeau 	while (host_clock / (div * pre_div * ddr_pre_div) > clock && div < 16)
933d31fc00aSDong Aisheng 		div++;
934d31fc00aSDong Aisheng 
9355143c953SBenoît Thébaudeau 	host->mmc->actual_clock = host_clock / (div * pre_div * ddr_pre_div);
936d31fc00aSDong Aisheng 	dev_dbg(mmc_dev(host->mmc), "desired SD clock: %d, actual: %d\n",
937e76b8559SDong Aisheng 		clock, host->mmc->actual_clock);
938d31fc00aSDong Aisheng 
939d31fc00aSDong Aisheng 	pre_div >>= 1;
940d31fc00aSDong Aisheng 	div--;
941d31fc00aSDong Aisheng 
942d31fc00aSDong Aisheng 	temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
943d31fc00aSDong Aisheng 	temp |= (ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN
944d31fc00aSDong Aisheng 		| (div << ESDHC_DIVIDER_SHIFT)
945d31fc00aSDong Aisheng 		| (pre_div << ESDHC_PREDIV_SHIFT));
946d31fc00aSDong Aisheng 	sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
947fed2f6e2SDong Aisheng 
948f581e909SHaibo Chen 	/* need to wait the bit 3 of the PRSSTAT to be set, make sure card clock is stable */
949f581e909SHaibo Chen 	ret = readl_poll_timeout(host->ioaddr + ESDHC_PRSSTAT, temp,
950f581e909SHaibo Chen 				(temp & ESDHC_CLOCK_STABLE), 2, 100);
951f581e909SHaibo Chen 	if (ret == -ETIMEDOUT)
952f581e909SHaibo Chen 		dev_warn(mmc_dev(host->mmc), "card clock still not stable in 100us!.\n");
953f581e909SHaibo Chen 
9549d61c009SShawn Guo 	if (esdhc_is_usdhc(imx_data)) {
955fed2f6e2SDong Aisheng 		val = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
956fed2f6e2SDong Aisheng 		writel(val | ESDHC_VENDOR_SPEC_FRC_SDCLK_ON,
957fed2f6e2SDong Aisheng 			host->ioaddr + ESDHC_VENDOR_SPEC);
958fed2f6e2SDong Aisheng 	}
959fed2f6e2SDong Aisheng 
9608ba9580aSLucas Stach }
9618ba9580aSLucas Stach 
962913413c3SShawn Guo static unsigned int esdhc_pltfm_get_ro(struct sdhci_host *host)
963913413c3SShawn Guo {
964842afc02SShawn Guo 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
965070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
966842afc02SShawn Guo 	struct esdhc_platform_data *boarddata = &imx_data->boarddata;
967913413c3SShawn Guo 
968913413c3SShawn Guo 	switch (boarddata->wp_type) {
969913413c3SShawn Guo 	case ESDHC_WP_GPIO:
970fbe5fdd1SShawn Guo 		return mmc_gpio_get_ro(host->mmc);
971913413c3SShawn Guo 	case ESDHC_WP_CONTROLLER:
972913413c3SShawn Guo 		return !(readl(host->ioaddr + SDHCI_PRESENT_STATE) &
973913413c3SShawn Guo 			       SDHCI_WRITE_PROTECT);
974913413c3SShawn Guo 	case ESDHC_WP_NONE:
975913413c3SShawn Guo 		break;
976913413c3SShawn Guo 	}
977913413c3SShawn Guo 
978913413c3SShawn Guo 	return -ENOSYS;
979913413c3SShawn Guo }
980913413c3SShawn Guo 
9812317f56cSRussell King static void esdhc_pltfm_set_bus_width(struct sdhci_host *host, int width)
982af51079eSSascha Hauer {
983af51079eSSascha Hauer 	u32 ctrl;
984af51079eSSascha Hauer 
985af51079eSSascha Hauer 	switch (width) {
986af51079eSSascha Hauer 	case MMC_BUS_WIDTH_8:
987af51079eSSascha Hauer 		ctrl = ESDHC_CTRL_8BITBUS;
988af51079eSSascha Hauer 		break;
989af51079eSSascha Hauer 	case MMC_BUS_WIDTH_4:
990af51079eSSascha Hauer 		ctrl = ESDHC_CTRL_4BITBUS;
991af51079eSSascha Hauer 		break;
992af51079eSSascha Hauer 	default:
993af51079eSSascha Hauer 		ctrl = 0;
994af51079eSSascha Hauer 		break;
995af51079eSSascha Hauer 	}
996af51079eSSascha Hauer 
997af51079eSSascha Hauer 	esdhc_clrset_le(host, ESDHC_CTRL_BUSWIDTH_MASK, ctrl,
998af51079eSSascha Hauer 			SDHCI_HOST_CONTROL);
999af51079eSSascha Hauer }
1000af51079eSSascha Hauer 
1001de3e1dd0SBOUGH CHEN static int usdhc_execute_tuning(struct mmc_host *mmc, u32 opcode)
1002de3e1dd0SBOUGH CHEN {
1003de3e1dd0SBOUGH CHEN 	struct sdhci_host *host = mmc_priv(mmc);
1004de3e1dd0SBOUGH CHEN 
1005de3e1dd0SBOUGH CHEN 	/*
1006de3e1dd0SBOUGH CHEN 	 * i.MX uSDHC internally already uses a fixed optimized timing for
1007de3e1dd0SBOUGH CHEN 	 * DDR50, normally does not require tuning for DDR50 mode.
1008de3e1dd0SBOUGH CHEN 	 */
1009de3e1dd0SBOUGH CHEN 	if (host->timing == MMC_TIMING_UHS_DDR50)
1010de3e1dd0SBOUGH CHEN 		return 0;
1011de3e1dd0SBOUGH CHEN 
1012de3e1dd0SBOUGH CHEN 	return sdhci_execute_tuning(mmc, opcode);
1013de3e1dd0SBOUGH CHEN }
1014de3e1dd0SBOUGH CHEN 
10150322191eSDong Aisheng static void esdhc_prepare_tuning(struct sdhci_host *host, u32 val)
10160322191eSDong Aisheng {
10170322191eSDong Aisheng 	u32 reg;
10180ac4f496SHaibo Chen 	u8 sw_rst;
10190ac4f496SHaibo Chen 	int ret;
10200322191eSDong Aisheng 
10210322191eSDong Aisheng 	/* FIXME: delay a bit for card to be ready for next tuning due to errors */
10220322191eSDong Aisheng 	mdelay(1);
10230322191eSDong Aisheng 
10240ac4f496SHaibo Chen 	/* IC suggest to reset USDHC before every tuning command */
10250ac4f496SHaibo Chen 	esdhc_clrset_le(host, 0xff, SDHCI_RESET_ALL, SDHCI_SOFTWARE_RESET);
10260ac4f496SHaibo Chen 	ret = readb_poll_timeout(host->ioaddr + SDHCI_SOFTWARE_RESET, sw_rst,
10270ac4f496SHaibo Chen 				!(sw_rst & SDHCI_RESET_ALL), 10, 100);
10280ac4f496SHaibo Chen 	if (ret == -ETIMEDOUT)
10290ac4f496SHaibo Chen 		dev_warn(mmc_dev(host->mmc),
10300ac4f496SHaibo Chen 		"warning! RESET_ALL never complete before sending tuning command\n");
10310ac4f496SHaibo Chen 
10320322191eSDong Aisheng 	reg = readl(host->ioaddr + ESDHC_MIX_CTRL);
10330322191eSDong Aisheng 	reg |= ESDHC_MIX_CTRL_EXE_TUNE | ESDHC_MIX_CTRL_SMPCLK_SEL |
10340322191eSDong Aisheng 			ESDHC_MIX_CTRL_FBCLK_SEL;
10350322191eSDong Aisheng 	writel(reg, host->ioaddr + ESDHC_MIX_CTRL);
10360322191eSDong Aisheng 	writel(val << 8, host->ioaddr + ESDHC_TUNE_CTRL_STATUS);
10370322191eSDong Aisheng 	dev_dbg(mmc_dev(host->mmc),
1038d04f8d5bSBenoît Thébaudeau 		"tuning with delay 0x%x ESDHC_TUNE_CTRL_STATUS 0x%x\n",
10390322191eSDong Aisheng 			val, readl(host->ioaddr + ESDHC_TUNE_CTRL_STATUS));
10400322191eSDong Aisheng }
10410322191eSDong Aisheng 
10420322191eSDong Aisheng static void esdhc_post_tuning(struct sdhci_host *host)
10430322191eSDong Aisheng {
10440322191eSDong Aisheng 	u32 reg;
10450322191eSDong Aisheng 
1046*45334ee1SHaibo Chen 	usdhc_auto_tuning_mode_sel(host);
1047*45334ee1SHaibo Chen 
10480322191eSDong Aisheng 	reg = readl(host->ioaddr + ESDHC_MIX_CTRL);
10490322191eSDong Aisheng 	reg &= ~ESDHC_MIX_CTRL_EXE_TUNE;
1050da0295ffSDong Aisheng 	reg |= ESDHC_MIX_CTRL_AUTO_TUNE_EN;
10510322191eSDong Aisheng 	writel(reg, host->ioaddr + ESDHC_MIX_CTRL);
10520322191eSDong Aisheng }
10530322191eSDong Aisheng 
10540322191eSDong Aisheng static int esdhc_executing_tuning(struct sdhci_host *host, u32 opcode)
10550322191eSDong Aisheng {
10560322191eSDong Aisheng 	int min, max, avg, ret;
10570322191eSDong Aisheng 
10580322191eSDong Aisheng 	/* find the mininum delay first which can pass tuning */
10590322191eSDong Aisheng 	min = ESDHC_TUNE_CTRL_MIN;
10600322191eSDong Aisheng 	while (min < ESDHC_TUNE_CTRL_MAX) {
10610322191eSDong Aisheng 		esdhc_prepare_tuning(host, min);
10629979dbe5SChaotian Jing 		if (!mmc_send_tuning(host->mmc, opcode, NULL))
10630322191eSDong Aisheng 			break;
10640322191eSDong Aisheng 		min += ESDHC_TUNE_CTRL_STEP;
10650322191eSDong Aisheng 	}
10660322191eSDong Aisheng 
10670322191eSDong Aisheng 	/* find the maxinum delay which can not pass tuning */
10680322191eSDong Aisheng 	max = min + ESDHC_TUNE_CTRL_STEP;
10690322191eSDong Aisheng 	while (max < ESDHC_TUNE_CTRL_MAX) {
10700322191eSDong Aisheng 		esdhc_prepare_tuning(host, max);
10719979dbe5SChaotian Jing 		if (mmc_send_tuning(host->mmc, opcode, NULL)) {
10720322191eSDong Aisheng 			max -= ESDHC_TUNE_CTRL_STEP;
10730322191eSDong Aisheng 			break;
10740322191eSDong Aisheng 		}
10750322191eSDong Aisheng 		max += ESDHC_TUNE_CTRL_STEP;
10760322191eSDong Aisheng 	}
10770322191eSDong Aisheng 
10780322191eSDong Aisheng 	/* use average delay to get the best timing */
10790322191eSDong Aisheng 	avg = (min + max) / 2;
10800322191eSDong Aisheng 	esdhc_prepare_tuning(host, avg);
10819979dbe5SChaotian Jing 	ret = mmc_send_tuning(host->mmc, opcode, NULL);
10820322191eSDong Aisheng 	esdhc_post_tuning(host);
10830322191eSDong Aisheng 
1084d04f8d5bSBenoît Thébaudeau 	dev_dbg(mmc_dev(host->mmc), "tuning %s at 0x%x ret %d\n",
10850322191eSDong Aisheng 		ret ? "failed" : "passed", avg, ret);
10860322191eSDong Aisheng 
10870322191eSDong Aisheng 	return ret;
10880322191eSDong Aisheng }
10890322191eSDong Aisheng 
1090029e2476SBOUGH CHEN static void esdhc_hs400_enhanced_strobe(struct mmc_host *mmc, struct mmc_ios *ios)
1091029e2476SBOUGH CHEN {
1092029e2476SBOUGH CHEN 	struct sdhci_host *host = mmc_priv(mmc);
1093029e2476SBOUGH CHEN 	u32 m;
1094029e2476SBOUGH CHEN 
1095029e2476SBOUGH CHEN 	m = readl(host->ioaddr + ESDHC_MIX_CTRL);
1096029e2476SBOUGH CHEN 	if (ios->enhanced_strobe)
1097029e2476SBOUGH CHEN 		m |= ESDHC_MIX_CTRL_HS400_ES_EN;
1098029e2476SBOUGH CHEN 	else
1099029e2476SBOUGH CHEN 		m &= ~ESDHC_MIX_CTRL_HS400_ES_EN;
1100029e2476SBOUGH CHEN 	writel(m, host->ioaddr + ESDHC_MIX_CTRL);
1101029e2476SBOUGH CHEN }
1102029e2476SBOUGH CHEN 
1103ad93220dSDong Aisheng static int esdhc_change_pinstate(struct sdhci_host *host,
1104ad93220dSDong Aisheng 						unsigned int uhs)
1105ad93220dSDong Aisheng {
1106ad93220dSDong Aisheng 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1107070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
1108ad93220dSDong Aisheng 	struct pinctrl_state *pinctrl;
1109ad93220dSDong Aisheng 
1110ad93220dSDong Aisheng 	dev_dbg(mmc_dev(host->mmc), "change pinctrl state for uhs %d\n", uhs);
1111ad93220dSDong Aisheng 
1112ad93220dSDong Aisheng 	if (IS_ERR(imx_data->pinctrl) ||
1113ad93220dSDong Aisheng 		IS_ERR(imx_data->pins_100mhz) ||
1114ad93220dSDong Aisheng 		IS_ERR(imx_data->pins_200mhz))
1115ad93220dSDong Aisheng 		return -EINVAL;
1116ad93220dSDong Aisheng 
1117ad93220dSDong Aisheng 	switch (uhs) {
1118ad93220dSDong Aisheng 	case MMC_TIMING_UHS_SDR50:
11199f327845SHaibo Chen 	case MMC_TIMING_UHS_DDR50:
1120ad93220dSDong Aisheng 		pinctrl = imx_data->pins_100mhz;
1121ad93220dSDong Aisheng 		break;
1122ad93220dSDong Aisheng 	case MMC_TIMING_UHS_SDR104:
1123429a5b45SDong Aisheng 	case MMC_TIMING_MMC_HS200:
112428b07674SHaibo Chen 	case MMC_TIMING_MMC_HS400:
1125ad93220dSDong Aisheng 		pinctrl = imx_data->pins_200mhz;
1126ad93220dSDong Aisheng 		break;
1127ad93220dSDong Aisheng 	default:
1128ad93220dSDong Aisheng 		/* back to default state for other legacy timing */
11292480b720SUlf Hansson 		return pinctrl_select_default_state(mmc_dev(host->mmc));
1130ad93220dSDong Aisheng 	}
1131ad93220dSDong Aisheng 
1132ad93220dSDong Aisheng 	return pinctrl_select_state(imx_data->pinctrl, pinctrl);
1133ad93220dSDong Aisheng }
1134ad93220dSDong Aisheng 
113528b07674SHaibo Chen /*
1136d04f8d5bSBenoît Thébaudeau  * For HS400 eMMC, there is a data_strobe line. This signal is generated
113728b07674SHaibo Chen  * by the device and used for data output and CRC status response output
113828b07674SHaibo Chen  * in HS400 mode. The frequency of this signal follows the frequency of
1139d04f8d5bSBenoît Thébaudeau  * CLK generated by host. The host receives the data which is aligned to the
114028b07674SHaibo Chen  * edge of data_strobe line. Due to the time delay between CLK line and
114128b07674SHaibo Chen  * data_strobe line, if the delay time is larger than one clock cycle,
1142d04f8d5bSBenoît Thébaudeau  * then CLK and data_strobe line will be misaligned, read error shows up.
114328b07674SHaibo Chen  */
114428b07674SHaibo Chen static void esdhc_set_strobe_dll(struct sdhci_host *host)
114528b07674SHaibo Chen {
11465bd2acdcSHaibo Chen 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
11475bd2acdcSHaibo Chen 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
11485bd2acdcSHaibo Chen 	u32 strobe_delay;
114928b07674SHaibo Chen 	u32 v;
1150373e800bSHaibo Chen 	int ret;
115128b07674SHaibo Chen 
11527ac6da26SDong Aisheng 	/* disable clock before enabling strobe dll */
11537ac6da26SDong Aisheng 	writel(readl(host->ioaddr + ESDHC_VENDOR_SPEC) &
11547ac6da26SDong Aisheng 		~ESDHC_VENDOR_SPEC_FRC_SDCLK_ON,
11557ac6da26SDong Aisheng 		host->ioaddr + ESDHC_VENDOR_SPEC);
1156f581e909SHaibo Chen 	esdhc_wait_for_card_clock_gate_off(host);
11577ac6da26SDong Aisheng 
115828b07674SHaibo Chen 	/* force a reset on strobe dll */
115928b07674SHaibo Chen 	writel(ESDHC_STROBE_DLL_CTRL_RESET,
116028b07674SHaibo Chen 		host->ioaddr + ESDHC_STROBE_DLL_CTRL);
11612eaf5a53SBOUGH CHEN 	/* clear the reset bit on strobe dll before any setting */
11622eaf5a53SBOUGH CHEN 	writel(0, host->ioaddr + ESDHC_STROBE_DLL_CTRL);
11632eaf5a53SBOUGH CHEN 
116428b07674SHaibo Chen 	/*
116528b07674SHaibo Chen 	 * enable strobe dll ctrl and adjust the delay target
116628b07674SHaibo Chen 	 * for the uSDHC loopback read clock
116728b07674SHaibo Chen 	 */
11685bd2acdcSHaibo Chen 	if (imx_data->boarddata.strobe_dll_delay_target)
11695bd2acdcSHaibo Chen 		strobe_delay = imx_data->boarddata.strobe_dll_delay_target;
11705bd2acdcSHaibo Chen 	else
11715bd2acdcSHaibo Chen 		strobe_delay = ESDHC_STROBE_DLL_CTRL_SLV_DLY_TARGET_DEFAULT;
117228b07674SHaibo Chen 	v = ESDHC_STROBE_DLL_CTRL_ENABLE |
11732eaf5a53SBOUGH CHEN 		ESDHC_STROBE_DLL_CTRL_SLV_UPDATE_INT_DEFAULT |
11745bd2acdcSHaibo Chen 		(strobe_delay << ESDHC_STROBE_DLL_CTRL_SLV_DLY_TARGET_SHIFT);
117528b07674SHaibo Chen 	writel(v, host->ioaddr + ESDHC_STROBE_DLL_CTRL);
1176373e800bSHaibo Chen 
1177373e800bSHaibo Chen 	/* wait max 50us to get the REF/SLV lock */
1178373e800bSHaibo Chen 	ret = readl_poll_timeout(host->ioaddr + ESDHC_STROBE_DLL_STATUS, v,
1179373e800bSHaibo Chen 		((v & ESDHC_STROBE_DLL_STS_REF_LOCK) && (v & ESDHC_STROBE_DLL_STS_SLV_LOCK)), 1, 50);
1180373e800bSHaibo Chen 	if (ret == -ETIMEDOUT)
118128b07674SHaibo Chen 		dev_warn(mmc_dev(host->mmc),
1182373e800bSHaibo Chen 		"warning! HS400 strobe DLL status REF/SLV not lock in 50us, STROBE DLL status is %x!\n", v);
118328b07674SHaibo Chen }
118428b07674SHaibo Chen 
1185d9370424SHaibo Chen static void esdhc_reset_tuning(struct sdhci_host *host)
1186d9370424SHaibo Chen {
1187d9370424SHaibo Chen 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1188d9370424SHaibo Chen 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
1189d9370424SHaibo Chen 	u32 ctrl;
1190d9370424SHaibo Chen 
1191d04f8d5bSBenoît Thébaudeau 	/* Reset the tuning circuit */
1192d9370424SHaibo Chen 	if (esdhc_is_usdhc(imx_data)) {
1193d9370424SHaibo Chen 		if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING) {
1194d9370424SHaibo Chen 			ctrl = readl(host->ioaddr + ESDHC_MIX_CTRL);
1195d9370424SHaibo Chen 			ctrl &= ~ESDHC_MIX_CTRL_SMPCLK_SEL;
1196d9370424SHaibo Chen 			ctrl &= ~ESDHC_MIX_CTRL_FBCLK_SEL;
1197d9370424SHaibo Chen 			writel(ctrl, host->ioaddr + ESDHC_MIX_CTRL);
1198d9370424SHaibo Chen 			writel(0, host->ioaddr + ESDHC_TUNE_CTRL_STATUS);
1199d9370424SHaibo Chen 		} else if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) {
1200869f8a69SAdrian Hunter 			ctrl = readl(host->ioaddr + SDHCI_AUTO_CMD_STATUS);
1201d9370424SHaibo Chen 			ctrl &= ~ESDHC_MIX_CTRL_SMPCLK_SEL;
1202869f8a69SAdrian Hunter 			writel(ctrl, host->ioaddr + SDHCI_AUTO_CMD_STATUS);
1203d9370424SHaibo Chen 		}
1204d9370424SHaibo Chen 	}
1205d9370424SHaibo Chen }
1206d9370424SHaibo Chen 
1207850a29b8SRussell King static void esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned timing)
1208ad93220dSDong Aisheng {
120928b07674SHaibo Chen 	u32 m;
1210ad93220dSDong Aisheng 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1211070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
1212602519b2SDong Aisheng 	struct esdhc_platform_data *boarddata = &imx_data->boarddata;
1213ad93220dSDong Aisheng 
121428b07674SHaibo Chen 	/* disable ddr mode and disable HS400 mode */
121528b07674SHaibo Chen 	m = readl(host->ioaddr + ESDHC_MIX_CTRL);
121628b07674SHaibo Chen 	m &= ~(ESDHC_MIX_CTRL_DDREN | ESDHC_MIX_CTRL_HS400_EN);
121728b07674SHaibo Chen 	imx_data->is_ddr = 0;
121828b07674SHaibo Chen 
1219850a29b8SRussell King 	switch (timing) {
1220ad93220dSDong Aisheng 	case MMC_TIMING_UHS_SDR12:
1221ad93220dSDong Aisheng 	case MMC_TIMING_UHS_SDR25:
1222ad93220dSDong Aisheng 	case MMC_TIMING_UHS_SDR50:
1223ad93220dSDong Aisheng 	case MMC_TIMING_UHS_SDR104:
1224de0a0decSBOUGH CHEN 	case MMC_TIMING_MMC_HS:
1225429a5b45SDong Aisheng 	case MMC_TIMING_MMC_HS200:
122628b07674SHaibo Chen 		writel(m, host->ioaddr + ESDHC_MIX_CTRL);
1227ad93220dSDong Aisheng 		break;
1228ad93220dSDong Aisheng 	case MMC_TIMING_UHS_DDR50:
122969f5bf38SAisheng Dong 	case MMC_TIMING_MMC_DDR52:
123028b07674SHaibo Chen 		m |= ESDHC_MIX_CTRL_DDREN;
123128b07674SHaibo Chen 		writel(m, host->ioaddr + ESDHC_MIX_CTRL);
1232de5bdbffSDong Aisheng 		imx_data->is_ddr = 1;
1233602519b2SDong Aisheng 		if (boarddata->delay_line) {
1234602519b2SDong Aisheng 			u32 v;
1235602519b2SDong Aisheng 			v = boarddata->delay_line <<
1236602519b2SDong Aisheng 				ESDHC_DLL_OVERRIDE_VAL_SHIFT |
1237602519b2SDong Aisheng 				(1 << ESDHC_DLL_OVERRIDE_EN_SHIFT);
1238602519b2SDong Aisheng 			if (is_imx53_esdhc(imx_data))
1239602519b2SDong Aisheng 				v <<= 1;
1240602519b2SDong Aisheng 			writel(v, host->ioaddr + ESDHC_DLL_CTRL);
1241602519b2SDong Aisheng 		}
1242ad93220dSDong Aisheng 		break;
124328b07674SHaibo Chen 	case MMC_TIMING_MMC_HS400:
124428b07674SHaibo Chen 		m |= ESDHC_MIX_CTRL_DDREN | ESDHC_MIX_CTRL_HS400_EN;
124528b07674SHaibo Chen 		writel(m, host->ioaddr + ESDHC_MIX_CTRL);
124628b07674SHaibo Chen 		imx_data->is_ddr = 1;
12477ac6da26SDong Aisheng 		/* update clock after enable DDR for strobe DLL lock */
12487ac6da26SDong Aisheng 		host->ops->set_clock(host, host->clock);
124928b07674SHaibo Chen 		esdhc_set_strobe_dll(host);
125028b07674SHaibo Chen 		break;
1251d9370424SHaibo Chen 	case MMC_TIMING_LEGACY:
1252d9370424SHaibo Chen 	default:
1253d9370424SHaibo Chen 		esdhc_reset_tuning(host);
1254d9370424SHaibo Chen 		break;
1255ad93220dSDong Aisheng 	}
1256ad93220dSDong Aisheng 
1257850a29b8SRussell King 	esdhc_change_pinstate(host, timing);
1258ad93220dSDong Aisheng }
1259ad93220dSDong Aisheng 
12600718e59aSRussell King static void esdhc_reset(struct sdhci_host *host, u8 mask)
12610718e59aSRussell King {
12620718e59aSRussell King 	sdhci_reset(host, mask);
12630718e59aSRussell King 
12640718e59aSRussell King 	sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
12650718e59aSRussell King 	sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
12660718e59aSRussell King }
12670718e59aSRussell King 
126810fd0ad9SAisheng Dong static unsigned int esdhc_get_max_timeout_count(struct sdhci_host *host)
126910fd0ad9SAisheng Dong {
127010fd0ad9SAisheng Dong 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1271070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
127210fd0ad9SAisheng Dong 
1273d04f8d5bSBenoît Thébaudeau 	/* Doc Erratum: the uSDHC actual maximum timeout count is 1 << 29 */
12742fb0b02bSHaibo Chen 	return esdhc_is_usdhc(imx_data) ? 1 << 29 : 1 << 27;
127510fd0ad9SAisheng Dong }
127610fd0ad9SAisheng Dong 
1277e33eb8e2SAisheng Dong static void esdhc_set_timeout(struct sdhci_host *host, struct mmc_command *cmd)
1278e33eb8e2SAisheng Dong {
1279e33eb8e2SAisheng Dong 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1280070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
1281e33eb8e2SAisheng Dong 
1282e33eb8e2SAisheng Dong 	/* use maximum timeout counter */
1283a215186dSHaibo Chen 	esdhc_clrset_le(host, ESDHC_SYS_CTRL_DTOCV_MASK,
1284a215186dSHaibo Chen 			esdhc_is_usdhc(imx_data) ? 0xF : 0xE,
1285e33eb8e2SAisheng Dong 			SDHCI_TIMEOUT_CONTROL);
1286e33eb8e2SAisheng Dong }
1287e33eb8e2SAisheng Dong 
1288bb6e3581SBOUGH CHEN static u32 esdhc_cqhci_irq(struct sdhci_host *host, u32 intmask)
1289bb6e3581SBOUGH CHEN {
1290bb6e3581SBOUGH CHEN 	int cmd_error = 0;
1291bb6e3581SBOUGH CHEN 	int data_error = 0;
1292bb6e3581SBOUGH CHEN 
1293bb6e3581SBOUGH CHEN 	if (!sdhci_cqe_irq(host, intmask, &cmd_error, &data_error))
1294bb6e3581SBOUGH CHEN 		return intmask;
1295bb6e3581SBOUGH CHEN 
1296bb6e3581SBOUGH CHEN 	cqhci_irq(host->mmc, intmask, cmd_error, data_error);
1297bb6e3581SBOUGH CHEN 
1298bb6e3581SBOUGH CHEN 	return 0;
1299bb6e3581SBOUGH CHEN }
1300bb6e3581SBOUGH CHEN 
13016e9fd28eSDong Aisheng static struct sdhci_ops sdhci_esdhc_ops = {
1302e149860dSRichard Zhu 	.read_l = esdhc_readl_le,
13030c6d49ceSWolfram Sang 	.read_w = esdhc_readw_le,
130477da3da0SAaron Brice 	.read_b = esdhc_readb_le,
1305e149860dSRichard Zhu 	.write_l = esdhc_writel_le,
13060c6d49ceSWolfram Sang 	.write_w = esdhc_writew_le,
13070c6d49ceSWolfram Sang 	.write_b = esdhc_writeb_le,
13088ba9580aSLucas Stach 	.set_clock = esdhc_pltfm_set_clock,
13090ddf03c9SLucas Stach 	.get_max_clock = esdhc_pltfm_get_max_clock,
13100c6d49ceSWolfram Sang 	.get_min_clock = esdhc_pltfm_get_min_clock,
131110fd0ad9SAisheng Dong 	.get_max_timeout_count = esdhc_get_max_timeout_count,
1312913413c3SShawn Guo 	.get_ro = esdhc_pltfm_get_ro,
1313e33eb8e2SAisheng Dong 	.set_timeout = esdhc_set_timeout,
13142317f56cSRussell King 	.set_bus_width = esdhc_pltfm_set_bus_width,
1315ad93220dSDong Aisheng 	.set_uhs_signaling = esdhc_set_uhs_signaling,
13160718e59aSRussell King 	.reset = esdhc_reset,
1317bb6e3581SBOUGH CHEN 	.irq = esdhc_cqhci_irq,
13183722c74cSHaibo Chen 	.dump_vendor_regs = esdhc_dump_debug_regs,
13190c6d49ceSWolfram Sang };
13200c6d49ceSWolfram Sang 
13211db5eebfSLars-Peter Clausen static const struct sdhci_pltfm_data sdhci_esdhc_imx_pdata = {
132297e4ba6aSRichard Zhu 	.quirks = ESDHC_DEFAULT_QUIRKS | SDHCI_QUIRK_NO_HISPD_BIT
132397e4ba6aSRichard Zhu 			| SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC
132497e4ba6aSRichard Zhu 			| SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC
132585d6509dSShawn Guo 			| SDHCI_QUIRK_BROKEN_CARD_DETECTION,
132685d6509dSShawn Guo 	.ops = &sdhci_esdhc_ops,
132785d6509dSShawn Guo };
132885d6509dSShawn Guo 
1329f3f5cf3dSDong Aisheng static void sdhci_esdhc_imx_hwinit(struct sdhci_host *host)
1330f3f5cf3dSDong Aisheng {
1331f3f5cf3dSDong Aisheng 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1332f3f5cf3dSDong Aisheng 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
1333982cf37dSHaibo Chen 	struct cqhci_host *cq_host = host->mmc->cqe_private;
13342b16cf32SDong Aisheng 	int tmp;
1335f3f5cf3dSDong Aisheng 
1336f3f5cf3dSDong Aisheng 	if (esdhc_is_usdhc(imx_data)) {
1337f3f5cf3dSDong Aisheng 		/*
1338f3f5cf3dSDong Aisheng 		 * The imx6q ROM code will change the default watermark
1339f3f5cf3dSDong Aisheng 		 * level setting to something insane.  Change it back here.
1340f3f5cf3dSDong Aisheng 		 */
1341f3f5cf3dSDong Aisheng 		writel(ESDHC_WTMK_DEFAULT_VAL, host->ioaddr + ESDHC_WTMK_LVL);
1342f3f5cf3dSDong Aisheng 
1343f3f5cf3dSDong Aisheng 		/*
1344f3f5cf3dSDong Aisheng 		 * ROM code will change the bit burst_length_enable setting
1345d04f8d5bSBenoît Thébaudeau 		 * to zero if this usdhc is chosen to boot system. Change
1346f3f5cf3dSDong Aisheng 		 * it back here, otherwise it will impact the performance a
1347f3f5cf3dSDong Aisheng 		 * lot. This bit is used to enable/disable the burst length
1348d04f8d5bSBenoît Thébaudeau 		 * for the external AHB2AXI bridge. It's useful especially
1349f3f5cf3dSDong Aisheng 		 * for INCR transfer because without burst length indicator,
1350f3f5cf3dSDong Aisheng 		 * the AHB2AXI bridge does not know the burst length in
1351f3f5cf3dSDong Aisheng 		 * advance. And without burst length indicator, AHB INCR
1352f3f5cf3dSDong Aisheng 		 * transfer can only be converted to singles on the AXI side.
1353f3f5cf3dSDong Aisheng 		 */
1354f3f5cf3dSDong Aisheng 		writel(readl(host->ioaddr + SDHCI_HOST_CONTROL)
1355f3f5cf3dSDong Aisheng 			| ESDHC_BURST_LEN_EN_INCR,
1356f3f5cf3dSDong Aisheng 			host->ioaddr + SDHCI_HOST_CONTROL);
1357e30be063SBOUGH CHEN 
1358f3f5cf3dSDong Aisheng 		/*
1359d04f8d5bSBenoît Thébaudeau 		 * erratum ESDHC_FLAG_ERR004536 fix for MX6Q TO1.2 and MX6DL
1360f3f5cf3dSDong Aisheng 		 * TO1.1, it's harmless for MX6SL
1361f3f5cf3dSDong Aisheng 		 */
1362e30be063SBOUGH CHEN 		writel(readl(host->ioaddr + 0x6c) & ~BIT(7),
1363f3f5cf3dSDong Aisheng 			host->ioaddr + 0x6c);
1364f3f5cf3dSDong Aisheng 
1365f3f5cf3dSDong Aisheng 		/* disable DLL_CTRL delay line settings */
1366f3f5cf3dSDong Aisheng 		writel(0x0, host->ioaddr + ESDHC_DLL_CTRL);
13672b16cf32SDong Aisheng 
1368bcdb5301SBOUGH CHEN 		/*
1369bcdb5301SBOUGH CHEN 		 * For the case of command with busy, if set the bit
1370bcdb5301SBOUGH CHEN 		 * ESDHC_VEND_SPEC2_EN_BUSY_IRQ, USDHC will generate a
1371bcdb5301SBOUGH CHEN 		 * transfer complete interrupt when busy is deasserted.
1372bcdb5301SBOUGH CHEN 		 * When CQHCI use DCMD to send a CMD need R1b respons,
1373bcdb5301SBOUGH CHEN 		 * CQHCI require to set ESDHC_VEND_SPEC2_EN_BUSY_IRQ,
1374bcdb5301SBOUGH CHEN 		 * otherwise DCMD will always meet timeout waiting for
1375bcdb5301SBOUGH CHEN 		 * hardware interrupt issue.
1376bcdb5301SBOUGH CHEN 		 */
1377bcdb5301SBOUGH CHEN 		if (imx_data->socdata->flags & ESDHC_FLAG_CQHCI) {
1378bcdb5301SBOUGH CHEN 			tmp = readl(host->ioaddr + ESDHC_VEND_SPEC2);
1379bcdb5301SBOUGH CHEN 			tmp |= ESDHC_VEND_SPEC2_EN_BUSY_IRQ;
1380bcdb5301SBOUGH CHEN 			writel(tmp, host->ioaddr + ESDHC_VEND_SPEC2);
1381bcdb5301SBOUGH CHEN 
1382bcdb5301SBOUGH CHEN 			host->quirks &= ~SDHCI_QUIRK_NO_BUSY_IRQ;
1383bcdb5301SBOUGH CHEN 		}
1384bcdb5301SBOUGH CHEN 
13852b16cf32SDong Aisheng 		if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) {
13862b16cf32SDong Aisheng 			tmp = readl(host->ioaddr + ESDHC_TUNING_CTRL);
13872b16cf32SDong Aisheng 			tmp |= ESDHC_STD_TUNING_EN |
13882b16cf32SDong Aisheng 				ESDHC_TUNING_START_TAP_DEFAULT;
13892b16cf32SDong Aisheng 			if (imx_data->boarddata.tuning_start_tap) {
13902b16cf32SDong Aisheng 				tmp &= ~ESDHC_TUNING_START_TAP_MASK;
13912b16cf32SDong Aisheng 				tmp |= imx_data->boarddata.tuning_start_tap;
13922b16cf32SDong Aisheng 			}
13932b16cf32SDong Aisheng 
13942b16cf32SDong Aisheng 			if (imx_data->boarddata.tuning_step) {
13952b16cf32SDong Aisheng 				tmp &= ~ESDHC_TUNING_STEP_MASK;
13962b16cf32SDong Aisheng 				tmp |= imx_data->boarddata.tuning_step
13972b16cf32SDong Aisheng 					<< ESDHC_TUNING_STEP_SHIFT;
13982b16cf32SDong Aisheng 			}
139916e40e5bSHaibo Chen 
140016e40e5bSHaibo Chen 			/* Disable the CMD CRC check for tuning, if not, need to
140116e40e5bSHaibo Chen 			 * add some delay after every tuning command, because
140216e40e5bSHaibo Chen 			 * hardware standard tuning logic will directly go to next
140316e40e5bSHaibo Chen 			 * step once it detect the CMD CRC error, will not wait for
140416e40e5bSHaibo Chen 			 * the card side to finally send out the tuning data, trigger
140516e40e5bSHaibo Chen 			 * the buffer read ready interrupt immediately. If usdhc send
140616e40e5bSHaibo Chen 			 * the next tuning command some eMMC card will stuck, can't
140716e40e5bSHaibo Chen 			 * response, block the tuning procedure or the first command
140816e40e5bSHaibo Chen 			 * after the whole tuning procedure always can't get any response.
140916e40e5bSHaibo Chen 			 */
141016e40e5bSHaibo Chen 			tmp |= ESDHC_TUNING_CMD_CRC_CHECK_DISABLE;
14112b16cf32SDong Aisheng 			writel(tmp, host->ioaddr + ESDHC_TUNING_CTRL);
1412a98c557eSBOUGH CHEN 		} else if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING) {
1413a98c557eSBOUGH CHEN 			/*
1414a98c557eSBOUGH CHEN 			 * ESDHC_STD_TUNING_EN may be configed in bootloader
1415a98c557eSBOUGH CHEN 			 * or ROM code, so clear this bit here to make sure
1416a98c557eSBOUGH CHEN 			 * the manual tuning can work.
1417a98c557eSBOUGH CHEN 			 */
1418a98c557eSBOUGH CHEN 			tmp = readl(host->ioaddr + ESDHC_TUNING_CTRL);
1419a98c557eSBOUGH CHEN 			tmp &= ~ESDHC_STD_TUNING_EN;
1420a98c557eSBOUGH CHEN 			writel(tmp, host->ioaddr + ESDHC_TUNING_CTRL);
14212b16cf32SDong Aisheng 		}
1422982cf37dSHaibo Chen 
1423982cf37dSHaibo Chen 		/*
1424982cf37dSHaibo Chen 		 * On i.MX8MM, we are running Dual Linux OS, with 1st Linux using SD Card
1425982cf37dSHaibo Chen 		 * as rootfs storage, 2nd Linux using eMMC as rootfs storage. We let the
1426982cf37dSHaibo Chen 		 * the 1st linux configure power/clock for the 2nd Linux.
1427982cf37dSHaibo Chen 		 *
1428982cf37dSHaibo Chen 		 * When the 2nd Linux is booting into rootfs stage, we let the 1st Linux
1429982cf37dSHaibo Chen 		 * to destroy the 2nd linux, then restart the 2nd linux, we met SDHCI dump.
1430982cf37dSHaibo Chen 		 * After we clear the pending interrupt and halt CQCTL, issue gone.
1431982cf37dSHaibo Chen 		 */
1432982cf37dSHaibo Chen 		if (cq_host) {
1433982cf37dSHaibo Chen 			tmp = cqhci_readl(cq_host, CQHCI_IS);
1434982cf37dSHaibo Chen 			cqhci_writel(cq_host, tmp, CQHCI_IS);
1435982cf37dSHaibo Chen 			cqhci_writel(cq_host, CQHCI_HALT, CQHCI_CTL);
1436982cf37dSHaibo Chen 		}
1437f3f5cf3dSDong Aisheng 	}
1438f3f5cf3dSDong Aisheng }
1439f3f5cf3dSDong Aisheng 
1440bb6e3581SBOUGH CHEN static void esdhc_cqe_enable(struct mmc_host *mmc)
1441bb6e3581SBOUGH CHEN {
1442bb6e3581SBOUGH CHEN 	struct sdhci_host *host = mmc_priv(mmc);
144385236d2bSBOUGH CHEN 	struct cqhci_host *cq_host = mmc->cqe_private;
1444bb6e3581SBOUGH CHEN 	u32 reg;
1445bb6e3581SBOUGH CHEN 	u16 mode;
1446bb6e3581SBOUGH CHEN 	int count = 10;
1447bb6e3581SBOUGH CHEN 
1448bb6e3581SBOUGH CHEN 	/*
1449bb6e3581SBOUGH CHEN 	 * CQE gets stuck if it sees Buffer Read Enable bit set, which can be
1450bb6e3581SBOUGH CHEN 	 * the case after tuning, so ensure the buffer is drained.
1451bb6e3581SBOUGH CHEN 	 */
1452bb6e3581SBOUGH CHEN 	reg = sdhci_readl(host, SDHCI_PRESENT_STATE);
1453bb6e3581SBOUGH CHEN 	while (reg & SDHCI_DATA_AVAILABLE) {
1454bb6e3581SBOUGH CHEN 		sdhci_readl(host, SDHCI_BUFFER);
1455bb6e3581SBOUGH CHEN 		reg = sdhci_readl(host, SDHCI_PRESENT_STATE);
1456bb6e3581SBOUGH CHEN 		if (count-- == 0) {
1457bb6e3581SBOUGH CHEN 			dev_warn(mmc_dev(host->mmc),
1458bb6e3581SBOUGH CHEN 				"CQE may get stuck because the Buffer Read Enable bit is set\n");
1459bb6e3581SBOUGH CHEN 			break;
1460bb6e3581SBOUGH CHEN 		}
1461bb6e3581SBOUGH CHEN 		mdelay(1);
1462bb6e3581SBOUGH CHEN 	}
1463bb6e3581SBOUGH CHEN 
1464bb6e3581SBOUGH CHEN 	/*
1465bb6e3581SBOUGH CHEN 	 * Runtime resume will reset the entire host controller, which
1466bb6e3581SBOUGH CHEN 	 * will also clear the DMAEN/BCEN of register ESDHC_MIX_CTRL.
1467bb6e3581SBOUGH CHEN 	 * Here set DMAEN and BCEN when enable CMDQ.
1468bb6e3581SBOUGH CHEN 	 */
1469bb6e3581SBOUGH CHEN 	mode = sdhci_readw(host, SDHCI_TRANSFER_MODE);
1470bb6e3581SBOUGH CHEN 	if (host->flags & SDHCI_REQ_USE_DMA)
1471bb6e3581SBOUGH CHEN 		mode |= SDHCI_TRNS_DMA;
1472bb6e3581SBOUGH CHEN 	if (!(host->quirks2 & SDHCI_QUIRK2_SUPPORT_SINGLE))
1473bb6e3581SBOUGH CHEN 		mode |= SDHCI_TRNS_BLK_CNT_EN;
1474bb6e3581SBOUGH CHEN 	sdhci_writew(host, mode, SDHCI_TRANSFER_MODE);
1475bb6e3581SBOUGH CHEN 
147685236d2bSBOUGH CHEN 	/*
147785236d2bSBOUGH CHEN 	 * Though Runtime resume reset the entire host controller,
147885236d2bSBOUGH CHEN 	 * but do not impact the CQHCI side, need to clear the
147985236d2bSBOUGH CHEN 	 * HALT bit, avoid CQHCI stuck in the first request when
148085236d2bSBOUGH CHEN 	 * system resume back.
148185236d2bSBOUGH CHEN 	 */
148285236d2bSBOUGH CHEN 	cqhci_writel(cq_host, 0, CQHCI_CTL);
148385236d2bSBOUGH CHEN 	if (cqhci_readl(cq_host, CQHCI_CTL) && CQHCI_HALT)
148485236d2bSBOUGH CHEN 		dev_err(mmc_dev(host->mmc),
148585236d2bSBOUGH CHEN 			"failed to exit halt state when enable CQE\n");
148685236d2bSBOUGH CHEN 
148785236d2bSBOUGH CHEN 
1488bb6e3581SBOUGH CHEN 	sdhci_cqe_enable(mmc);
1489bb6e3581SBOUGH CHEN }
1490bb6e3581SBOUGH CHEN 
1491bb6e3581SBOUGH CHEN static void esdhc_sdhci_dumpregs(struct mmc_host *mmc)
1492bb6e3581SBOUGH CHEN {
1493bb6e3581SBOUGH CHEN 	sdhci_dumpregs(mmc_priv(mmc));
1494bb6e3581SBOUGH CHEN }
1495bb6e3581SBOUGH CHEN 
1496bb6e3581SBOUGH CHEN static const struct cqhci_host_ops esdhc_cqhci_ops = {
1497bb6e3581SBOUGH CHEN 	.enable		= esdhc_cqe_enable,
1498bb6e3581SBOUGH CHEN 	.disable	= sdhci_cqe_disable,
1499bb6e3581SBOUGH CHEN 	.dumpregs	= esdhc_sdhci_dumpregs,
1500bb6e3581SBOUGH CHEN };
1501bb6e3581SBOUGH CHEN 
1502c3be1efdSBill Pemberton static int
1503abfafc2dSShawn Guo sdhci_esdhc_imx_probe_dt(struct platform_device *pdev,
150407bf2b54SSascha Hauer 			 struct sdhci_host *host,
150591fa4252SDong Aisheng 			 struct pltfm_imx_data *imx_data)
1506abfafc2dSShawn Guo {
1507abfafc2dSShawn Guo 	struct device_node *np = pdev->dev.of_node;
150891fa4252SDong Aisheng 	struct esdhc_platform_data *boarddata = &imx_data->boarddata;
15094800e87aSDong Aisheng 	int ret;
1510abfafc2dSShawn Guo 
1511abfafc2dSShawn Guo 	if (of_get_property(np, "fsl,wp-controller", NULL))
1512abfafc2dSShawn Guo 		boarddata->wp_type = ESDHC_WP_CONTROLLER;
1513abfafc2dSShawn Guo 
151474ff81e1SLinus Walleij 	/*
151574ff81e1SLinus Walleij 	 * If we have this property, then activate WP check.
151674ff81e1SLinus Walleij 	 * Retrieveing and requesting the actual WP GPIO will happen
151774ff81e1SLinus Walleij 	 * in the call to mmc_of_parse().
151874ff81e1SLinus Walleij 	 */
151974ff81e1SLinus Walleij 	if (of_property_read_bool(np, "wp-gpios"))
1520abfafc2dSShawn Guo 		boarddata->wp_type = ESDHC_WP_GPIO;
1521abfafc2dSShawn Guo 
1522d407e30bSHaibo Chen 	of_property_read_u32(np, "fsl,tuning-step", &boarddata->tuning_step);
1523d87fc966SDong Aisheng 	of_property_read_u32(np, "fsl,tuning-start-tap",
1524d87fc966SDong Aisheng 			     &boarddata->tuning_start_tap);
1525d407e30bSHaibo Chen 
15265bd2acdcSHaibo Chen 	of_property_read_u32(np, "fsl,strobe-dll-delay-target",
15275bd2acdcSHaibo Chen 				&boarddata->strobe_dll_delay_target);
1528ad93220dSDong Aisheng 	if (of_find_property(np, "no-1-8-v", NULL))
152986f495c5SStefan Agner 		host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V;
1530ad93220dSDong Aisheng 
1531602519b2SDong Aisheng 	if (of_property_read_u32(np, "fsl,delay-line", &boarddata->delay_line))
1532602519b2SDong Aisheng 		boarddata->delay_line = 0;
1533602519b2SDong Aisheng 
15346dab809bSAndy Shevchenko 	mmc_of_parse_voltage(host->mmc, &host->ocr_mask);
153507bf2b54SSascha Hauer 
1536f410ee0aSPeng Fan 	if (esdhc_is_usdhc(imx_data) && !IS_ERR(imx_data->pinctrl)) {
153791fa4252SDong Aisheng 		imx_data->pins_100mhz = pinctrl_lookup_state(imx_data->pinctrl,
153891fa4252SDong Aisheng 						ESDHC_PINCTRL_STATE_100MHZ);
153991fa4252SDong Aisheng 		imx_data->pins_200mhz = pinctrl_lookup_state(imx_data->pinctrl,
154091fa4252SDong Aisheng 						ESDHC_PINCTRL_STATE_200MHZ);
154191fa4252SDong Aisheng 	}
154291fa4252SDong Aisheng 
154315064119SFabio Estevam 	/* call to generic mmc_of_parse to support additional capabilities */
15444800e87aSDong Aisheng 	ret = mmc_of_parse(host->mmc);
15454800e87aSDong Aisheng 	if (ret)
15464800e87aSDong Aisheng 		return ret;
15474800e87aSDong Aisheng 
1548287980e4SArnd Bergmann 	if (mmc_gpio_get_cd(host->mmc) >= 0)
15494800e87aSDong Aisheng 		host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION;
15504800e87aSDong Aisheng 
15514800e87aSDong Aisheng 	return 0;
1552abfafc2dSShawn Guo }
1553abfafc2dSShawn Guo 
1554c3be1efdSBill Pemberton static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
155595f25efeSWolfram Sang {
155685d6509dSShawn Guo 	struct sdhci_pltfm_host *pltfm_host;
155785d6509dSShawn Guo 	struct sdhci_host *host;
1558bb6e3581SBOUGH CHEN 	struct cqhci_host *cq_host;
15590c6d49ceSWolfram Sang 	int err;
1560e149860dSRichard Zhu 	struct pltfm_imx_data *imx_data;
156195f25efeSWolfram Sang 
1562070e6d3fSJisheng Zhang 	host = sdhci_pltfm_init(pdev, &sdhci_esdhc_imx_pdata,
1563070e6d3fSJisheng Zhang 				sizeof(*imx_data));
156485d6509dSShawn Guo 	if (IS_ERR(host))
156585d6509dSShawn Guo 		return PTR_ERR(host);
156685d6509dSShawn Guo 
156785d6509dSShawn Guo 	pltfm_host = sdhci_priv(host);
156885d6509dSShawn Guo 
1569070e6d3fSJisheng Zhang 	imx_data = sdhci_pltfm_priv(pltfm_host);
157057ed3314SShawn Guo 
157191b3d2e5SFabio Estevam 	imx_data->socdata = device_get_match_data(&pdev->dev);
157285d6509dSShawn Guo 
15731c4989b0SBOUGH CHEN 	if (imx_data->socdata->flags & ESDHC_FLAG_PMQOS)
1574d1b98305SRafael J. Wysocki 		cpu_latency_qos_add_request(&imx_data->pm_qos_req, 0);
15751c4989b0SBOUGH CHEN 
157652dac615SSascha Hauer 	imx_data->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
157752dac615SSascha Hauer 	if (IS_ERR(imx_data->clk_ipg)) {
157852dac615SSascha Hauer 		err = PTR_ERR(imx_data->clk_ipg);
1579e3af31c6SShawn Guo 		goto free_sdhci;
158095f25efeSWolfram Sang 	}
158152dac615SSascha Hauer 
158252dac615SSascha Hauer 	imx_data->clk_ahb = devm_clk_get(&pdev->dev, "ahb");
158352dac615SSascha Hauer 	if (IS_ERR(imx_data->clk_ahb)) {
158452dac615SSascha Hauer 		err = PTR_ERR(imx_data->clk_ahb);
1585e3af31c6SShawn Guo 		goto free_sdhci;
158652dac615SSascha Hauer 	}
158752dac615SSascha Hauer 
158852dac615SSascha Hauer 	imx_data->clk_per = devm_clk_get(&pdev->dev, "per");
158952dac615SSascha Hauer 	if (IS_ERR(imx_data->clk_per)) {
159052dac615SSascha Hauer 		err = PTR_ERR(imx_data->clk_per);
1591e3af31c6SShawn Guo 		goto free_sdhci;
159252dac615SSascha Hauer 	}
159352dac615SSascha Hauer 
159452dac615SSascha Hauer 	pltfm_host->clk = imx_data->clk_per;
1595a974862fSDong Aisheng 	pltfm_host->clock = clk_get_rate(pltfm_host->clk);
159617b1eb7fSFabio Estevam 	err = clk_prepare_enable(imx_data->clk_per);
159717b1eb7fSFabio Estevam 	if (err)
159817b1eb7fSFabio Estevam 		goto free_sdhci;
159917b1eb7fSFabio Estevam 	err = clk_prepare_enable(imx_data->clk_ipg);
160017b1eb7fSFabio Estevam 	if (err)
160117b1eb7fSFabio Estevam 		goto disable_per_clk;
160217b1eb7fSFabio Estevam 	err = clk_prepare_enable(imx_data->clk_ahb);
160317b1eb7fSFabio Estevam 	if (err)
160417b1eb7fSFabio Estevam 		goto disable_ipg_clk;
160595f25efeSWolfram Sang 
1606ad93220dSDong Aisheng 	imx_data->pinctrl = devm_pinctrl_get(&pdev->dev);
16079e70ff99SHaibo Chen 	if (IS_ERR(imx_data->pinctrl))
1608b62eee9fSHaibo Chen 		dev_warn(mmc_dev(host->mmc), "could not get pinctrl\n");
1609e62d8b8fSDong Aisheng 
161069ed60e0SDong Aisheng 	if (esdhc_is_usdhc(imx_data)) {
161169ed60e0SDong Aisheng 		host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN;
161209c8192bSStefan Agner 		host->mmc->caps |= MMC_CAP_1_8V_DDR | MMC_CAP_3_3V_DDR;
1613f6140462SHaibo Chen 
1614f6140462SHaibo Chen 		/* GPIO CD can be set as a wakeup source */
1615f6140462SHaibo Chen 		host->mmc->caps |= MMC_CAP_CD_WAKE;
1616f6140462SHaibo Chen 
16174245afffSDong Aisheng 		if (!(imx_data->socdata->flags & ESDHC_FLAG_HS200))
16184245afffSDong Aisheng 			host->quirks2 |= SDHCI_QUIRK2_BROKEN_HS200;
1619a75dcbf4SDong Aisheng 
1620a75dcbf4SDong Aisheng 		/* clear tuning bits in case ROM has set it already */
1621a75dcbf4SDong Aisheng 		writel(0x0, host->ioaddr + ESDHC_MIX_CTRL);
1622869f8a69SAdrian Hunter 		writel(0x0, host->ioaddr + SDHCI_AUTO_CMD_STATUS);
1623a75dcbf4SDong Aisheng 		writel(0x0, host->ioaddr + ESDHC_TUNE_CTRL_STATUS);
1624de3e1dd0SBOUGH CHEN 
1625de3e1dd0SBOUGH CHEN 		/*
1626de3e1dd0SBOUGH CHEN 		 * Link usdhc specific mmc_host_ops execute_tuning function,
1627de3e1dd0SBOUGH CHEN 		 * to replace the standard one in sdhci_ops.
1628de3e1dd0SBOUGH CHEN 		 */
1629de3e1dd0SBOUGH CHEN 		host->mmc_host_ops.execute_tuning = usdhc_execute_tuning;
163069ed60e0SDong Aisheng 	}
1631f750ba9bSShawn Guo 
16326e9fd28eSDong Aisheng 	if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING)
16336e9fd28eSDong Aisheng 		sdhci_esdhc_ops.platform_execute_tuning =
16346e9fd28eSDong Aisheng 					esdhc_executing_tuning;
16358b2bb0adSDong Aisheng 
163618094430SDong Aisheng 	if (imx_data->socdata->flags & ESDHC_FLAG_ERR004536)
163718094430SDong Aisheng 		host->quirks |= SDHCI_QUIRK_BROKEN_ADMA;
163818094430SDong Aisheng 
163928b07674SHaibo Chen 	if (imx_data->socdata->flags & ESDHC_FLAG_HS400)
16402991ad76SLucas Stach 		host->mmc->caps2 |= MMC_CAP2_HS400;
164128b07674SHaibo Chen 
164274898cbcSHaibo Chen 	if (imx_data->socdata->flags & ESDHC_FLAG_BROKEN_AUTO_CMD23)
164374898cbcSHaibo Chen 		host->quirks2 |= SDHCI_QUIRK2_ACMD23_BROKEN;
164474898cbcSHaibo Chen 
1645029e2476SBOUGH CHEN 	if (imx_data->socdata->flags & ESDHC_FLAG_HS400_ES) {
1646029e2476SBOUGH CHEN 		host->mmc->caps2 |= MMC_CAP2_HS400_ES;
1647029e2476SBOUGH CHEN 		host->mmc_host_ops.hs400_enhanced_strobe =
1648029e2476SBOUGH CHEN 					esdhc_hs400_enhanced_strobe;
1649029e2476SBOUGH CHEN 	}
1650029e2476SBOUGH CHEN 
1651bb6e3581SBOUGH CHEN 	if (imx_data->socdata->flags & ESDHC_FLAG_CQHCI) {
1652bcdb5301SBOUGH CHEN 		host->mmc->caps2 |= MMC_CAP2_CQE | MMC_CAP2_CQE_DCMD;
1653bb6e3581SBOUGH CHEN 		cq_host = devm_kzalloc(&pdev->dev, sizeof(*cq_host), GFP_KERNEL);
16549a633f3bSWei Yongjun 		if (!cq_host) {
16559a633f3bSWei Yongjun 			err = -ENOMEM;
1656bb6e3581SBOUGH CHEN 			goto disable_ahb_clk;
1657bb6e3581SBOUGH CHEN 		}
1658bb6e3581SBOUGH CHEN 
1659bb6e3581SBOUGH CHEN 		cq_host->mmio = host->ioaddr + ESDHC_CQHCI_ADDR_OFFSET;
1660bb6e3581SBOUGH CHEN 		cq_host->ops = &esdhc_cqhci_ops;
1661bb6e3581SBOUGH CHEN 
1662bb6e3581SBOUGH CHEN 		err = cqhci_init(cq_host, host->mmc, false);
1663bb6e3581SBOUGH CHEN 		if (err)
1664bb6e3581SBOUGH CHEN 			goto disable_ahb_clk;
1665bb6e3581SBOUGH CHEN 	}
1666bb6e3581SBOUGH CHEN 
166791fa4252SDong Aisheng 	err = sdhci_esdhc_imx_probe_dt(pdev, host, imx_data);
166891fa4252SDong Aisheng 	if (err)
166917b1eb7fSFabio Estevam 		goto disable_ahb_clk;
1670ad93220dSDong Aisheng 
1671f3f5cf3dSDong Aisheng 	sdhci_esdhc_imx_hwinit(host);
1672f3f5cf3dSDong Aisheng 
167385d6509dSShawn Guo 	err = sdhci_add_host(host);
167485d6509dSShawn Guo 	if (err)
167517b1eb7fSFabio Estevam 		goto disable_ahb_clk;
167685d6509dSShawn Guo 
1677f62f7bccSHaibo Chen 	/*
1678f62f7bccSHaibo Chen 	 * Setup the wakeup capability here, let user to decide
1679f62f7bccSHaibo Chen 	 * whether need to enable this wakeup through sysfs interface.
1680f62f7bccSHaibo Chen 	 */
1681f62f7bccSHaibo Chen 	if ((host->mmc->pm_caps & MMC_PM_KEEP_POWER) &&
1682f62f7bccSHaibo Chen 			(host->mmc->pm_caps & MMC_PM_WAKE_SDIO_IRQ))
1683f62f7bccSHaibo Chen 		device_set_wakeup_capable(&pdev->dev, true);
1684f62f7bccSHaibo Chen 
168589d7e5c1SDong Aisheng 	pm_runtime_set_active(&pdev->dev);
168689d7e5c1SDong Aisheng 	pm_runtime_set_autosuspend_delay(&pdev->dev, 50);
168789d7e5c1SDong Aisheng 	pm_runtime_use_autosuspend(&pdev->dev);
168889d7e5c1SDong Aisheng 	pm_suspend_ignore_children(&pdev->dev, 1);
168977903c01SUlf Hansson 	pm_runtime_enable(&pdev->dev);
169089d7e5c1SDong Aisheng 
16917e29c306SWolfram Sang 	return 0;
16927e29c306SWolfram Sang 
169317b1eb7fSFabio Estevam disable_ahb_clk:
169452dac615SSascha Hauer 	clk_disable_unprepare(imx_data->clk_ahb);
169517b1eb7fSFabio Estevam disable_ipg_clk:
169617b1eb7fSFabio Estevam 	clk_disable_unprepare(imx_data->clk_ipg);
169717b1eb7fSFabio Estevam disable_per_clk:
169817b1eb7fSFabio Estevam 	clk_disable_unprepare(imx_data->clk_per);
1699e3af31c6SShawn Guo free_sdhci:
17001c4989b0SBOUGH CHEN 	if (imx_data->socdata->flags & ESDHC_FLAG_PMQOS)
1701d1b98305SRafael J. Wysocki 		cpu_latency_qos_remove_request(&imx_data->pm_qos_req);
170285d6509dSShawn Guo 	sdhci_pltfm_free(pdev);
170385d6509dSShawn Guo 	return err;
170495f25efeSWolfram Sang }
170595f25efeSWolfram Sang 
17066e0ee714SBill Pemberton static int sdhci_esdhc_imx_remove(struct platform_device *pdev)
170795f25efeSWolfram Sang {
170885d6509dSShawn Guo 	struct sdhci_host *host = platform_get_drvdata(pdev);
170995f25efeSWolfram Sang 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1710070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
1711a56f4413SFrank Li 	int dead;
171285d6509dSShawn Guo 
17130b414368SUlf Hansson 	pm_runtime_get_sync(&pdev->dev);
1714a56f4413SFrank Li 	dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff);
17150b414368SUlf Hansson 	pm_runtime_disable(&pdev->dev);
17160b414368SUlf Hansson 	pm_runtime_put_noidle(&pdev->dev);
17170b414368SUlf Hansson 
171885d6509dSShawn Guo 	sdhci_remove_host(host, dead);
17190c6d49ceSWolfram Sang 
172052dac615SSascha Hauer 	clk_disable_unprepare(imx_data->clk_per);
172152dac615SSascha Hauer 	clk_disable_unprepare(imx_data->clk_ipg);
172252dac615SSascha Hauer 	clk_disable_unprepare(imx_data->clk_ahb);
172352dac615SSascha Hauer 
17241c4989b0SBOUGH CHEN 	if (imx_data->socdata->flags & ESDHC_FLAG_PMQOS)
1725d1b98305SRafael J. Wysocki 		cpu_latency_qos_remove_request(&imx_data->pm_qos_req);
17261c4989b0SBOUGH CHEN 
172785d6509dSShawn Guo 	sdhci_pltfm_free(pdev);
172885d6509dSShawn Guo 
172985d6509dSShawn Guo 	return 0;
173095f25efeSWolfram Sang }
173195f25efeSWolfram Sang 
17322788ed42SUlf Hansson #ifdef CONFIG_PM_SLEEP
173304143fbaSDong Aisheng static int sdhci_esdhc_suspend(struct device *dev)
173404143fbaSDong Aisheng {
17353e3274abSUlf Hansson 	struct sdhci_host *host = dev_get_drvdata(dev);
1736a26a4f1bSHaibo Chen 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1737a26a4f1bSHaibo Chen 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
1738bb6e3581SBOUGH CHEN 	int ret;
1739bb6e3581SBOUGH CHEN 
1740bb6e3581SBOUGH CHEN 	if (host->mmc->caps2 & MMC_CAP2_CQE) {
1741bb6e3581SBOUGH CHEN 		ret = cqhci_suspend(host->mmc);
1742bb6e3581SBOUGH CHEN 		if (ret)
1743bb6e3581SBOUGH CHEN 			return ret;
1744bb6e3581SBOUGH CHEN 	}
17453e3274abSUlf Hansson 
1746a26a4f1bSHaibo Chen 	if ((imx_data->socdata->flags & ESDHC_FLAG_STATE_LOST_IN_LPMODE) &&
1747a26a4f1bSHaibo Chen 		(host->tuning_mode != SDHCI_TUNING_MODE_1)) {
1748a26a4f1bSHaibo Chen 		mmc_retune_timer_stop(host->mmc);
1749a26a4f1bSHaibo Chen 		mmc_retune_needed(host->mmc);
1750a26a4f1bSHaibo Chen 	}
1751a26a4f1bSHaibo Chen 
1752d38dcad4SAdrian Hunter 	if (host->tuning_mode != SDHCI_TUNING_MODE_3)
1753d38dcad4SAdrian Hunter 		mmc_retune_needed(host->mmc);
1754d38dcad4SAdrian Hunter 
1755af8fade4SHaibo Chen 	ret = sdhci_suspend_host(host);
1756f6140462SHaibo Chen 	if (ret)
1757f6140462SHaibo Chen 		return ret;
1758f6140462SHaibo Chen 
1759f6140462SHaibo Chen 	ret = pinctrl_pm_select_sleep_state(dev);
1760f6140462SHaibo Chen 	if (ret)
1761f6140462SHaibo Chen 		return ret;
1762f6140462SHaibo Chen 
1763f6140462SHaibo Chen 	ret = mmc_gpio_set_cd_wake(host->mmc, true);
1764af8fade4SHaibo Chen 
1765af8fade4SHaibo Chen 	return ret;
176604143fbaSDong Aisheng }
176704143fbaSDong Aisheng 
176804143fbaSDong Aisheng static int sdhci_esdhc_resume(struct device *dev)
176904143fbaSDong Aisheng {
1770cc17e129SDong Aisheng 	struct sdhci_host *host = dev_get_drvdata(dev);
1771bb6e3581SBOUGH CHEN 	int ret;
1772cc17e129SDong Aisheng 
1773af8fade4SHaibo Chen 	ret = pinctrl_pm_select_default_state(dev);
1774af8fade4SHaibo Chen 	if (ret)
1775af8fade4SHaibo Chen 		return ret;
1776af8fade4SHaibo Chen 
177719dbfdd3SDong Aisheng 	/* re-initialize hw state in case it's lost in low power mode */
177819dbfdd3SDong Aisheng 	sdhci_esdhc_imx_hwinit(host);
1779cc17e129SDong Aisheng 
1780bb6e3581SBOUGH CHEN 	ret = sdhci_resume_host(host);
1781bb6e3581SBOUGH CHEN 	if (ret)
1782bb6e3581SBOUGH CHEN 		return ret;
1783bb6e3581SBOUGH CHEN 
1784bb6e3581SBOUGH CHEN 	if (host->mmc->caps2 & MMC_CAP2_CQE)
1785bb6e3581SBOUGH CHEN 		ret = cqhci_resume(host->mmc);
1786bb6e3581SBOUGH CHEN 
1787f6140462SHaibo Chen 	if (!ret)
1788f6140462SHaibo Chen 		ret = mmc_gpio_set_cd_wake(host->mmc, false);
1789f6140462SHaibo Chen 
1790bb6e3581SBOUGH CHEN 	return ret;
179104143fbaSDong Aisheng }
17922788ed42SUlf Hansson #endif
179304143fbaSDong Aisheng 
17942788ed42SUlf Hansson #ifdef CONFIG_PM
179589d7e5c1SDong Aisheng static int sdhci_esdhc_runtime_suspend(struct device *dev)
179689d7e5c1SDong Aisheng {
179789d7e5c1SDong Aisheng 	struct sdhci_host *host = dev_get_drvdata(dev);
179889d7e5c1SDong Aisheng 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1799070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
180089d7e5c1SDong Aisheng 	int ret;
180189d7e5c1SDong Aisheng 
1802bb6e3581SBOUGH CHEN 	if (host->mmc->caps2 & MMC_CAP2_CQE) {
1803bb6e3581SBOUGH CHEN 		ret = cqhci_suspend(host->mmc);
1804bb6e3581SBOUGH CHEN 		if (ret)
1805bb6e3581SBOUGH CHEN 			return ret;
1806bb6e3581SBOUGH CHEN 	}
1807bb6e3581SBOUGH CHEN 
180889d7e5c1SDong Aisheng 	ret = sdhci_runtime_suspend_host(host);
1809371d39faSMichael Trimarchi 	if (ret)
1810371d39faSMichael Trimarchi 		return ret;
181189d7e5c1SDong Aisheng 
1812d38dcad4SAdrian Hunter 	if (host->tuning_mode != SDHCI_TUNING_MODE_3)
1813d38dcad4SAdrian Hunter 		mmc_retune_needed(host->mmc);
1814d38dcad4SAdrian Hunter 
18153602785bSMichael Trimarchi 	imx_data->actual_clock = host->mmc->actual_clock;
18163602785bSMichael Trimarchi 	esdhc_pltfm_set_clock(host, 0);
181789d7e5c1SDong Aisheng 	clk_disable_unprepare(imx_data->clk_per);
181889d7e5c1SDong Aisheng 	clk_disable_unprepare(imx_data->clk_ipg);
181989d7e5c1SDong Aisheng 	clk_disable_unprepare(imx_data->clk_ahb);
182089d7e5c1SDong Aisheng 
18211c4989b0SBOUGH CHEN 	if (imx_data->socdata->flags & ESDHC_FLAG_PMQOS)
1822d1b98305SRafael J. Wysocki 		cpu_latency_qos_remove_request(&imx_data->pm_qos_req);
18231c4989b0SBOUGH CHEN 
182489d7e5c1SDong Aisheng 	return ret;
182589d7e5c1SDong Aisheng }
182689d7e5c1SDong Aisheng 
182789d7e5c1SDong Aisheng static int sdhci_esdhc_runtime_resume(struct device *dev)
182889d7e5c1SDong Aisheng {
182989d7e5c1SDong Aisheng 	struct sdhci_host *host = dev_get_drvdata(dev);
183089d7e5c1SDong Aisheng 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1831070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
183217b1eb7fSFabio Estevam 	int err;
183389d7e5c1SDong Aisheng 
18341c4989b0SBOUGH CHEN 	if (imx_data->socdata->flags & ESDHC_FLAG_PMQOS)
1835d1b98305SRafael J. Wysocki 		cpu_latency_qos_add_request(&imx_data->pm_qos_req, 0);
18361c4989b0SBOUGH CHEN 
18375c11f1ffSHaibo Chen 	if (imx_data->socdata->flags & ESDHC_FLAG_CLK_RATE_LOST_IN_PM_RUNTIME)
18385c11f1ffSHaibo Chen 		clk_set_rate(imx_data->clk_per, pltfm_host->clock);
18395c11f1ffSHaibo Chen 
1840a0ad3087SMichael Trimarchi 	err = clk_prepare_enable(imx_data->clk_ahb);
1841a0ad3087SMichael Trimarchi 	if (err)
18421c4989b0SBOUGH CHEN 		goto remove_pm_qos_request;
1843a0ad3087SMichael Trimarchi 
184417b1eb7fSFabio Estevam 	err = clk_prepare_enable(imx_data->clk_per);
184517b1eb7fSFabio Estevam 	if (err)
1846a0ad3087SMichael Trimarchi 		goto disable_ahb_clk;
1847af5d2b7bSUlf Hansson 
184817b1eb7fSFabio Estevam 	err = clk_prepare_enable(imx_data->clk_ipg);
184917b1eb7fSFabio Estevam 	if (err)
185017b1eb7fSFabio Estevam 		goto disable_per_clk;
1851af5d2b7bSUlf Hansson 
18523602785bSMichael Trimarchi 	esdhc_pltfm_set_clock(host, imx_data->actual_clock);
1853a0ad3087SMichael Trimarchi 
1854c6303c5dSBaolin Wang 	err = sdhci_runtime_resume_host(host, 0);
185517b1eb7fSFabio Estevam 	if (err)
1856a0ad3087SMichael Trimarchi 		goto disable_ipg_clk;
185789d7e5c1SDong Aisheng 
1858bb6e3581SBOUGH CHEN 	if (host->mmc->caps2 & MMC_CAP2_CQE)
1859bb6e3581SBOUGH CHEN 		err = cqhci_resume(host->mmc);
1860bb6e3581SBOUGH CHEN 
1861bb6e3581SBOUGH CHEN 	return err;
186217b1eb7fSFabio Estevam 
186317b1eb7fSFabio Estevam disable_ipg_clk:
186417b1eb7fSFabio Estevam 	clk_disable_unprepare(imx_data->clk_ipg);
186517b1eb7fSFabio Estevam disable_per_clk:
186617b1eb7fSFabio Estevam 	clk_disable_unprepare(imx_data->clk_per);
1867a0ad3087SMichael Trimarchi disable_ahb_clk:
1868a0ad3087SMichael Trimarchi 	clk_disable_unprepare(imx_data->clk_ahb);
18691c4989b0SBOUGH CHEN remove_pm_qos_request:
18701c4989b0SBOUGH CHEN 	if (imx_data->socdata->flags & ESDHC_FLAG_PMQOS)
1871d1b98305SRafael J. Wysocki 		cpu_latency_qos_remove_request(&imx_data->pm_qos_req);
187217b1eb7fSFabio Estevam 	return err;
187389d7e5c1SDong Aisheng }
187489d7e5c1SDong Aisheng #endif
187589d7e5c1SDong Aisheng 
187689d7e5c1SDong Aisheng static const struct dev_pm_ops sdhci_esdhc_pmops = {
187704143fbaSDong Aisheng 	SET_SYSTEM_SLEEP_PM_OPS(sdhci_esdhc_suspend, sdhci_esdhc_resume)
187889d7e5c1SDong Aisheng 	SET_RUNTIME_PM_OPS(sdhci_esdhc_runtime_suspend,
187989d7e5c1SDong Aisheng 				sdhci_esdhc_runtime_resume, NULL)
188089d7e5c1SDong Aisheng };
188189d7e5c1SDong Aisheng 
188285d6509dSShawn Guo static struct platform_driver sdhci_esdhc_imx_driver = {
188385d6509dSShawn Guo 	.driver		= {
188485d6509dSShawn Guo 		.name	= "sdhci-esdhc-imx",
188521b2cec6SDouglas Anderson 		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
1886abfafc2dSShawn Guo 		.of_match_table = imx_esdhc_dt_ids,
188789d7e5c1SDong Aisheng 		.pm	= &sdhci_esdhc_pmops,
188885d6509dSShawn Guo 	},
188985d6509dSShawn Guo 	.probe		= sdhci_esdhc_imx_probe,
18900433c143SBill Pemberton 	.remove		= sdhci_esdhc_imx_remove,
189195f25efeSWolfram Sang };
189285d6509dSShawn Guo 
1893d1f81a64SAxel Lin module_platform_driver(sdhci_esdhc_imx_driver);
189485d6509dSShawn Guo 
189585d6509dSShawn Guo MODULE_DESCRIPTION("SDHCI driver for Freescale i.MX eSDHC");
1896035ff831SWolfram Sang MODULE_AUTHOR("Wolfram Sang <kernel@pengutronix.de>");
189785d6509dSShawn Guo MODULE_LICENSE("GPL v2");
1898