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>
28fb1dec44SBrian Norris #include "sdhci-cqhci.h"
2995f25efeSWolfram Sang #include "sdhci-pltfm.h"
3095f25efeSWolfram Sang #include "sdhci-esdhc.h"
31bb6e3581SBOUGH CHEN #include "cqhci.h"
3295f25efeSWolfram Sang 
33a215186dSHaibo Chen #define ESDHC_SYS_CTRL_DTOCV_MASK	0x0f
3460bf6396SShawn Guo #define	ESDHC_CTRL_D3CD			0x08
35fd44954eSHaibo Chen #define ESDHC_BURST_LEN_EN_INCR		(1 << 27)
3658ac8177SRichard Zhu /* VENDOR SPEC register */
3760bf6396SShawn Guo #define ESDHC_VENDOR_SPEC		0xc0
3860bf6396SShawn Guo #define  ESDHC_VENDOR_SPEC_SDIO_QUIRK	(1 << 1)
390322191eSDong Aisheng #define  ESDHC_VENDOR_SPEC_VSELECT	(1 << 1)
40fed2f6e2SDong Aisheng #define  ESDHC_VENDOR_SPEC_FRC_SDCLK_ON	(1 << 8)
413722c74cSHaibo Chen #define ESDHC_DEBUG_SEL_AND_STATUS_REG		0xc2
423722c74cSHaibo Chen #define ESDHC_DEBUG_SEL_REG			0xc3
433722c74cSHaibo Chen #define ESDHC_DEBUG_SEL_MASK			0xf
443722c74cSHaibo Chen #define ESDHC_DEBUG_SEL_CMD_STATE		1
453722c74cSHaibo Chen #define ESDHC_DEBUG_SEL_DATA_STATE		2
463722c74cSHaibo Chen #define ESDHC_DEBUG_SEL_TRANS_STATE		3
473722c74cSHaibo Chen #define ESDHC_DEBUG_SEL_DMA_STATE		4
483722c74cSHaibo Chen #define ESDHC_DEBUG_SEL_ADMA_STATE		5
493722c74cSHaibo Chen #define ESDHC_DEBUG_SEL_FIFO_STATE		6
503722c74cSHaibo Chen #define ESDHC_DEBUG_SEL_ASYNC_FIFO_STATE	7
5160bf6396SShawn Guo #define ESDHC_WTMK_LVL			0x44
52cc17e129SDong Aisheng #define  ESDHC_WTMK_DEFAULT_VAL		0x10401040
533fbd4322SAndrew Gabbasov #define  ESDHC_WTMK_LVL_RD_WML_MASK	0x000000FF
543fbd4322SAndrew Gabbasov #define  ESDHC_WTMK_LVL_RD_WML_SHIFT	0
553fbd4322SAndrew Gabbasov #define  ESDHC_WTMK_LVL_WR_WML_MASK	0x00FF0000
563fbd4322SAndrew Gabbasov #define  ESDHC_WTMK_LVL_WR_WML_SHIFT	16
573fbd4322SAndrew Gabbasov #define  ESDHC_WTMK_LVL_WML_VAL_DEF	64
583fbd4322SAndrew Gabbasov #define  ESDHC_WTMK_LVL_WML_VAL_MAX	128
5960bf6396SShawn Guo #define ESDHC_MIX_CTRL			0x48
60de5bdbffSDong Aisheng #define  ESDHC_MIX_CTRL_DDREN		(1 << 3)
612a15f981SShawn Guo #define  ESDHC_MIX_CTRL_AC23EN		(1 << 7)
620322191eSDong Aisheng #define  ESDHC_MIX_CTRL_EXE_TUNE	(1 << 22)
630322191eSDong Aisheng #define  ESDHC_MIX_CTRL_SMPCLK_SEL	(1 << 23)
640b330e38SDong Aisheng #define  ESDHC_MIX_CTRL_AUTO_TUNE_EN	(1 << 24)
650322191eSDong Aisheng #define  ESDHC_MIX_CTRL_FBCLK_SEL	(1 << 25)
6628b07674SHaibo Chen #define  ESDHC_MIX_CTRL_HS400_EN	(1 << 26)
67029e2476SBOUGH CHEN #define  ESDHC_MIX_CTRL_HS400_ES_EN	(1 << 27)
682a15f981SShawn Guo /* Bits 3 and 6 are not SDHCI standard definitions */
692a15f981SShawn Guo #define  ESDHC_MIX_CTRL_SDHCI_MASK	0xb7
70d131a71cSDong Aisheng /* Tuning bits */
71d131a71cSDong Aisheng #define  ESDHC_MIX_CTRL_TUNING_MASK	0x03c00000
7258ac8177SRichard Zhu 
73602519b2SDong Aisheng /* dll control register */
74602519b2SDong Aisheng #define ESDHC_DLL_CTRL			0x60
75602519b2SDong Aisheng #define ESDHC_DLL_OVERRIDE_VAL_SHIFT	9
76602519b2SDong Aisheng #define ESDHC_DLL_OVERRIDE_EN_SHIFT	8
77602519b2SDong Aisheng 
780322191eSDong Aisheng /* tune control register */
790322191eSDong Aisheng #define ESDHC_TUNE_CTRL_STATUS		0x68
800322191eSDong Aisheng #define  ESDHC_TUNE_CTRL_STEP		1
810322191eSDong Aisheng #define  ESDHC_TUNE_CTRL_MIN		0
820322191eSDong Aisheng #define  ESDHC_TUNE_CTRL_MAX		((1 << 7) - 1)
830322191eSDong Aisheng 
8428b07674SHaibo Chen /* strobe dll register */
8528b07674SHaibo Chen #define ESDHC_STROBE_DLL_CTRL		0x70
8628b07674SHaibo Chen #define ESDHC_STROBE_DLL_CTRL_ENABLE	(1 << 0)
8728b07674SHaibo Chen #define ESDHC_STROBE_DLL_CTRL_RESET	(1 << 1)
885bd2acdcSHaibo Chen #define ESDHC_STROBE_DLL_CTRL_SLV_DLY_TARGET_DEFAULT	0x7
8928b07674SHaibo Chen #define ESDHC_STROBE_DLL_CTRL_SLV_DLY_TARGET_SHIFT	3
902eaf5a53SBOUGH CHEN #define ESDHC_STROBE_DLL_CTRL_SLV_UPDATE_INT_DEFAULT	(4 << 20)
9128b07674SHaibo Chen 
9228b07674SHaibo Chen #define ESDHC_STROBE_DLL_STATUS		0x74
9328b07674SHaibo Chen #define ESDHC_STROBE_DLL_STS_REF_LOCK	(1 << 1)
9428b07674SHaibo Chen #define ESDHC_STROBE_DLL_STS_SLV_LOCK	0x1
9528b07674SHaibo Chen 
96bcdb5301SBOUGH CHEN #define ESDHC_VEND_SPEC2		0xc8
97bcdb5301SBOUGH CHEN #define ESDHC_VEND_SPEC2_EN_BUSY_IRQ	(1 << 8)
9845334ee1SHaibo Chen #define ESDHC_VEND_SPEC2_AUTO_TUNE_8BIT_EN	(1 << 4)
9945334ee1SHaibo Chen #define ESDHC_VEND_SPEC2_AUTO_TUNE_4BIT_EN	(0 << 4)
10045334ee1SHaibo Chen #define ESDHC_VEND_SPEC2_AUTO_TUNE_1BIT_EN	(2 << 4)
10145334ee1SHaibo Chen #define ESDHC_VEND_SPEC2_AUTO_TUNE_CMD_EN	(1 << 6)
10245334ee1SHaibo Chen #define ESDHC_VEND_SPEC2_AUTO_TUNE_MODE_MASK	(7 << 4)
103bcdb5301SBOUGH CHEN 
1046e9fd28eSDong Aisheng #define ESDHC_TUNING_CTRL		0xcc
1056e9fd28eSDong Aisheng #define ESDHC_STD_TUNING_EN		(1 << 24)
1066e9fd28eSDong Aisheng /* NOTE: the minimum valid tuning start tap for mx6sl is 1 */
107d87fc966SDong Aisheng #define ESDHC_TUNING_START_TAP_DEFAULT	0x1
1081194be8cSHaibo Chen #define ESDHC_TUNING_START_TAP_MASK	0x7f
10916e40e5bSHaibo Chen #define ESDHC_TUNING_CMD_CRC_CHECK_DISABLE	(1 << 7)
110260ecb3cSHaibo Chen #define ESDHC_TUNING_STEP_MASK		0x00070000
111d407e30bSHaibo Chen #define ESDHC_TUNING_STEP_SHIFT		16
1126e9fd28eSDong Aisheng 
113ad93220dSDong Aisheng /* pinctrl state */
114ad93220dSDong Aisheng #define ESDHC_PINCTRL_STATE_100MHZ	"state_100mhz"
115ad93220dSDong Aisheng #define ESDHC_PINCTRL_STATE_200MHZ	"state_200mhz"
116ad93220dSDong Aisheng 
11758ac8177SRichard Zhu /*
118af51079eSSascha Hauer  * Our interpretation of the SDHCI_HOST_CONTROL register
119af51079eSSascha Hauer  */
120af51079eSSascha Hauer #define ESDHC_CTRL_4BITBUS		(0x1 << 1)
121af51079eSSascha Hauer #define ESDHC_CTRL_8BITBUS		(0x2 << 1)
122af51079eSSascha Hauer #define ESDHC_CTRL_BUSWIDTH_MASK	(0x3 << 1)
12345334ee1SHaibo Chen #define USDHC_GET_BUSWIDTH(c) (c & ESDHC_CTRL_BUSWIDTH_MASK)
124af51079eSSascha Hauer 
125af51079eSSascha Hauer /*
126d04f8d5bSBenoît Thébaudeau  * There is an INT DMA ERR mismatch between eSDHC and STD SDHC SPEC:
12797e4ba6aSRichard Zhu  * Bit25 is used in STD SPEC, and is reserved in fsl eSDHC design,
12897e4ba6aSRichard Zhu  * but bit28 is used as the INT DMA ERR in fsl eSDHC design.
12997e4ba6aSRichard Zhu  * Define this macro DMA error INT for fsl eSDHC
13097e4ba6aSRichard Zhu  */
13160bf6396SShawn Guo #define ESDHC_INT_VENDOR_SPEC_DMA_ERR	(1 << 28)
13297e4ba6aSRichard Zhu 
133bb6e3581SBOUGH CHEN /* the address offset of CQHCI */
134bb6e3581SBOUGH CHEN #define ESDHC_CQHCI_ADDR_OFFSET		0x100
135bb6e3581SBOUGH CHEN 
13697e4ba6aSRichard Zhu /*
13758ac8177SRichard Zhu  * The CMDTYPE of the CMD register (offset 0xE) should be set to
13858ac8177SRichard Zhu  * "11" when the STOP CMD12 is issued on imx53 to abort one
13958ac8177SRichard Zhu  * open ended multi-blk IO. Otherwise the TC INT wouldn't
14058ac8177SRichard Zhu  * be generated.
14158ac8177SRichard Zhu  * In exact block transfer, the controller doesn't complete the
14258ac8177SRichard Zhu  * operations automatically as required at the end of the
14358ac8177SRichard Zhu  * transfer and remains on hold if the abort command is not sent.
14458ac8177SRichard Zhu  * As a result, the TC flag is not asserted and SW received timeout
145d04f8d5bSBenoît Thébaudeau  * exception. Bit1 of Vendor Spec register is used to fix it.
14658ac8177SRichard Zhu  */
14731fbb301SShawn Guo #define ESDHC_FLAG_MULTIBLK_NO_INT	BIT(1)
14831fbb301SShawn Guo /*
1499d61c009SShawn Guo  * The flag tells that the ESDHC controller is an USDHC block that is
1509d61c009SShawn Guo  * integrated on the i.MX6 series.
1519d61c009SShawn Guo  */
1529d61c009SShawn Guo #define ESDHC_FLAG_USDHC		BIT(3)
1536e9fd28eSDong Aisheng /* The IP supports manual tuning process */
1546e9fd28eSDong Aisheng #define ESDHC_FLAG_MAN_TUNING		BIT(4)
1556e9fd28eSDong Aisheng /* The IP supports standard tuning process */
1566e9fd28eSDong Aisheng #define ESDHC_FLAG_STD_TUNING		BIT(5)
1576e9fd28eSDong Aisheng /* The IP has SDHCI_CAPABILITIES_1 register */
1586e9fd28eSDong Aisheng #define ESDHC_FLAG_HAVE_CAP1		BIT(6)
15918094430SDong Aisheng /*
160d04f8d5bSBenoît Thébaudeau  * The IP has erratum ERR004536
16118094430SDong Aisheng  * uSDHC: ADMA Length Mismatch Error occurs if the AHB read access is slow,
16218094430SDong Aisheng  * when reading data from the card
163667123f6SBenoît Thébaudeau  * This flag is also set for i.MX25 and i.MX35 in order to get
164667123f6SBenoît Thébaudeau  * SDHCI_QUIRK_BROKEN_ADMA, but for different reasons (ADMA capability bits).
16518094430SDong Aisheng  */
16618094430SDong Aisheng #define ESDHC_FLAG_ERR004536		BIT(7)
1674245afffSDong Aisheng /* The IP supports HS200 mode */
1684245afffSDong Aisheng #define ESDHC_FLAG_HS200		BIT(8)
16928b07674SHaibo Chen /* The IP supports HS400 mode */
17028b07674SHaibo Chen #define ESDHC_FLAG_HS400		BIT(9)
171af6a50d4SBOUGH CHEN /*
172af6a50d4SBOUGH CHEN  * The IP has errata ERR010450
173af6a50d4SBOUGH CHEN  * uSDHC: Due to the I/O timing limit, for SDR mode, SD card clock can't
174af6a50d4SBOUGH CHEN  * exceed 150MHz, for DDR mode, SD card clock can't exceed 45MHz.
175af6a50d4SBOUGH CHEN  */
176af6a50d4SBOUGH CHEN #define ESDHC_FLAG_ERR010450		BIT(10)
177029e2476SBOUGH CHEN /* The IP supports HS400ES mode */
178029e2476SBOUGH CHEN #define ESDHC_FLAG_HS400_ES		BIT(11)
179bb6e3581SBOUGH CHEN /* The IP has Host Controller Interface for Command Queuing */
180bb6e3581SBOUGH CHEN #define ESDHC_FLAG_CQHCI		BIT(12)
1811c4989b0SBOUGH CHEN /* need request pmqos during low power */
1821c4989b0SBOUGH CHEN #define ESDHC_FLAG_PMQOS		BIT(13)
183a26a4f1bSHaibo Chen /* The IP state got lost in low power mode */
184a26a4f1bSHaibo Chen #define ESDHC_FLAG_STATE_LOST_IN_LPMODE		BIT(14)
1855c11f1ffSHaibo Chen /* The IP lost clock rate in PM_RUNTIME */
1865c11f1ffSHaibo Chen #define ESDHC_FLAG_CLK_RATE_LOST_IN_PM_RUNTIME	BIT(15)
18774898cbcSHaibo Chen /*
18874898cbcSHaibo Chen  * The IP do not support the ACMD23 feature completely when use ADMA mode.
18974898cbcSHaibo Chen  * In ADMA mode, it only use the 16 bit block count of the register 0x4
19074898cbcSHaibo Chen  * (BLOCK_ATT) as the CMD23's argument for ACMD23 mode, which means it will
19174898cbcSHaibo Chen  * ignore the upper 16 bit of the CMD23's argument. This will block the reliable
19274898cbcSHaibo Chen  * write operation in RPMB, because RPMB reliable write need to set the bit31
19374898cbcSHaibo Chen  * of the CMD23's argument.
19474898cbcSHaibo Chen  * imx6qpdl/imx6sx/imx6sl/imx7d has this limitation only for ADMA mode, SDMA
19574898cbcSHaibo Chen  * do not has this limitation. so when these SoC use ADMA mode, it need to
19674898cbcSHaibo Chen  * disable the ACMD23 feature.
19774898cbcSHaibo Chen  */
19874898cbcSHaibo Chen #define ESDHC_FLAG_BROKEN_AUTO_CMD23	BIT(16)
199e149860dSRichard Zhu 
2005c4f0062SChester Lin /* ERR004536 is not applicable for the IP  */
2015c4f0062SChester Lin #define ESDHC_FLAG_SKIP_ERR004536	BIT(17)
2025c4f0062SChester Lin 
2034a11cc64SFabio Estevam enum wp_types {
2044a11cc64SFabio Estevam 	ESDHC_WP_NONE,		/* no WP, neither controller nor gpio */
2054a11cc64SFabio Estevam 	ESDHC_WP_CONTROLLER,	/* mmc controller internal WP */
2064a11cc64SFabio Estevam 	ESDHC_WP_GPIO,		/* external gpio pin for WP */
2074a11cc64SFabio Estevam };
2084a11cc64SFabio Estevam 
2094a11cc64SFabio Estevam enum cd_types {
2104a11cc64SFabio Estevam 	ESDHC_CD_NONE,		/* no CD, neither controller nor gpio */
2114a11cc64SFabio Estevam 	ESDHC_CD_CONTROLLER,	/* mmc controller internal CD */
2124a11cc64SFabio Estevam 	ESDHC_CD_GPIO,		/* external gpio pin for CD */
2134a11cc64SFabio Estevam 	ESDHC_CD_PERMANENT,	/* no CD, card permanently wired to host */
2144a11cc64SFabio Estevam };
2154a11cc64SFabio Estevam 
2164a11cc64SFabio Estevam /*
2174a11cc64SFabio Estevam  * struct esdhc_platform_data - platform data for esdhc on i.MX
2184a11cc64SFabio Estevam  *
2194a11cc64SFabio Estevam  * ESDHC_WP(CD)_CONTROLLER type is not available on i.MX25/35.
2204a11cc64SFabio Estevam  *
2214a11cc64SFabio Estevam  * @wp_type:	type of write_protect method (see wp_types enum above)
2224a11cc64SFabio Estevam  * @cd_type:	type of card_detect method (see cd_types enum above)
2234a11cc64SFabio Estevam  */
2244a11cc64SFabio Estevam 
2254a11cc64SFabio Estevam struct esdhc_platform_data {
2264a11cc64SFabio Estevam 	enum wp_types wp_type;
2274a11cc64SFabio Estevam 	enum cd_types cd_type;
2284a11cc64SFabio Estevam 	int max_bus_width;
2294a11cc64SFabio Estevam 	unsigned int delay_line;
2304a11cc64SFabio Estevam 	unsigned int tuning_step;       /* The delay cell steps in tuning procedure */
2314a11cc64SFabio Estevam 	unsigned int tuning_start_tap;	/* The start delay cell point in tuning procedure */
2324a11cc64SFabio Estevam 	unsigned int strobe_dll_delay_target;	/* The delay cell for strobe pad (read clock) */
2334a11cc64SFabio Estevam };
2344a11cc64SFabio Estevam 
235f47c4bbfSShawn Guo struct esdhc_soc_data {
236f47c4bbfSShawn Guo 	u32 flags;
237f47c4bbfSShawn Guo };
238f47c4bbfSShawn Guo 
2394f100012SAndrey Smirnov static const struct esdhc_soc_data esdhc_imx25_data = {
240667123f6SBenoît Thébaudeau 	.flags = ESDHC_FLAG_ERR004536,
241f47c4bbfSShawn Guo };
242f47c4bbfSShawn Guo 
2434f100012SAndrey Smirnov static const struct esdhc_soc_data esdhc_imx35_data = {
244667123f6SBenoît Thébaudeau 	.flags = ESDHC_FLAG_ERR004536,
245f47c4bbfSShawn Guo };
246f47c4bbfSShawn Guo 
2474f100012SAndrey Smirnov static const struct esdhc_soc_data esdhc_imx51_data = {
248f47c4bbfSShawn Guo 	.flags = 0,
249f47c4bbfSShawn Guo };
250f47c4bbfSShawn Guo 
2514f100012SAndrey Smirnov static const struct esdhc_soc_data esdhc_imx53_data = {
252f47c4bbfSShawn Guo 	.flags = ESDHC_FLAG_MULTIBLK_NO_INT,
253f47c4bbfSShawn Guo };
254f47c4bbfSShawn Guo 
2554f100012SAndrey Smirnov static const struct esdhc_soc_data usdhc_imx6q_data = {
25674898cbcSHaibo Chen 	.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_MAN_TUNING
25774898cbcSHaibo Chen 			| ESDHC_FLAG_BROKEN_AUTO_CMD23,
2586e9fd28eSDong Aisheng };
2596e9fd28eSDong Aisheng 
2604f100012SAndrey Smirnov static const struct esdhc_soc_data usdhc_imx6sl_data = {
2616e9fd28eSDong Aisheng 	.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
2624245afffSDong Aisheng 			| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_ERR004536
26374898cbcSHaibo Chen 			| ESDHC_FLAG_HS200
26474898cbcSHaibo Chen 			| ESDHC_FLAG_BROKEN_AUTO_CMD23,
26574898cbcSHaibo Chen };
26674898cbcSHaibo Chen 
26774898cbcSHaibo Chen static const struct esdhc_soc_data usdhc_imx6sll_data = {
26874898cbcSHaibo Chen 	.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
26974898cbcSHaibo Chen 			| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
27086b59671SHaibo Chen 			| ESDHC_FLAG_HS400
27174898cbcSHaibo Chen 			| ESDHC_FLAG_STATE_LOST_IN_LPMODE,
27257ed3314SShawn Guo };
27357ed3314SShawn Guo 
2744f100012SAndrey Smirnov static const struct esdhc_soc_data usdhc_imx6sx_data = {
275913d4951SDong Aisheng 	.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
276a26a4f1bSHaibo Chen 			| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
27774898cbcSHaibo Chen 			| ESDHC_FLAG_STATE_LOST_IN_LPMODE
27874898cbcSHaibo Chen 			| ESDHC_FLAG_BROKEN_AUTO_CMD23,
279913d4951SDong Aisheng };
280913d4951SDong Aisheng 
281af6a50d4SBOUGH CHEN static const struct esdhc_soc_data usdhc_imx6ull_data = {
282af6a50d4SBOUGH CHEN 	.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
283af6a50d4SBOUGH CHEN 			| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
284a26a4f1bSHaibo Chen 			| ESDHC_FLAG_ERR010450
285a26a4f1bSHaibo Chen 			| ESDHC_FLAG_STATE_LOST_IN_LPMODE,
286af6a50d4SBOUGH CHEN };
287af6a50d4SBOUGH CHEN 
2884f100012SAndrey Smirnov static const struct esdhc_soc_data usdhc_imx7d_data = {
28928b07674SHaibo Chen 	.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
29028b07674SHaibo Chen 			| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
291a26a4f1bSHaibo Chen 			| ESDHC_FLAG_HS400
29274898cbcSHaibo Chen 			| ESDHC_FLAG_STATE_LOST_IN_LPMODE
29374898cbcSHaibo Chen 			| ESDHC_FLAG_BROKEN_AUTO_CMD23,
29428b07674SHaibo Chen };
29528b07674SHaibo Chen 
2965c4f0062SChester Lin static struct esdhc_soc_data usdhc_s32g2_data = {
2975c4f0062SChester Lin 	.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_MAN_TUNING
2985c4f0062SChester Lin 			| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
2995c4f0062SChester Lin 			| ESDHC_FLAG_HS400 | ESDHC_FLAG_HS400_ES
3005c4f0062SChester Lin 			| ESDHC_FLAG_SKIP_ERR004536,
3015c4f0062SChester Lin };
3025c4f0062SChester Lin 
3031c4989b0SBOUGH CHEN static struct esdhc_soc_data usdhc_imx7ulp_data = {
3041c4989b0SBOUGH CHEN 	.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
3051c4989b0SBOUGH CHEN 			| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
306a26a4f1bSHaibo Chen 			| ESDHC_FLAG_PMQOS | ESDHC_FLAG_HS400
307a26a4f1bSHaibo Chen 			| ESDHC_FLAG_STATE_LOST_IN_LPMODE,
3081c4989b0SBOUGH CHEN };
3092f4788f3SJesse Taube static struct esdhc_soc_data usdhc_imxrt1050_data = {
310*d7a1830eSGiulio Benetti 	.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
311*d7a1830eSGiulio Benetti 			| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200,
3122f4788f3SJesse Taube };
3131c4989b0SBOUGH CHEN 
314029e2476SBOUGH CHEN static struct esdhc_soc_data usdhc_imx8qxp_data = {
315029e2476SBOUGH CHEN 	.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
316029e2476SBOUGH CHEN 			| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
317bb6e3581SBOUGH CHEN 			| ESDHC_FLAG_HS400 | ESDHC_FLAG_HS400_ES
3185c11f1ffSHaibo Chen 			| ESDHC_FLAG_STATE_LOST_IN_LPMODE
3195c11f1ffSHaibo Chen 			| ESDHC_FLAG_CLK_RATE_LOST_IN_PM_RUNTIME,
320029e2476SBOUGH CHEN };
321029e2476SBOUGH CHEN 
322cde5e8e9SHaibo Chen static struct esdhc_soc_data usdhc_imx8mm_data = {
323cde5e8e9SHaibo Chen 	.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
324cde5e8e9SHaibo Chen 			| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
325cde5e8e9SHaibo Chen 			| ESDHC_FLAG_HS400 | ESDHC_FLAG_HS400_ES
326cde5e8e9SHaibo Chen 			| ESDHC_FLAG_STATE_LOST_IN_LPMODE,
327e149860dSRichard Zhu };
328e149860dSRichard Zhu 
329e149860dSRichard Zhu struct pltfm_imx_data {
330e149860dSRichard Zhu 	u32 scratchpad;
331e62d8b8fSDong Aisheng 	struct pinctrl *pinctrl;
332ad93220dSDong Aisheng 	struct pinctrl_state *pins_100mhz;
333ad93220dSDong Aisheng 	struct pinctrl_state *pins_200mhz;
334f47c4bbfSShawn Guo 	const struct esdhc_soc_data *socdata;
335842afc02SShawn Guo 	struct esdhc_platform_data boarddata;
33652dac615SSascha Hauer 	struct clk *clk_ipg;
33752dac615SSascha Hauer 	struct clk *clk_ahb;
33852dac615SSascha Hauer 	struct clk *clk_per;
3393602785bSMichael Trimarchi 	unsigned int actual_clock;
340361b8482SLucas Stach 	enum {
341361b8482SLucas Stach 		NO_CMD_PENDING,      /* no multiblock command pending */
342361b8482SLucas Stach 		MULTIBLK_IN_PROCESS, /* exact multiblock cmd in process */
343361b8482SLucas Stach 		WAIT_FOR_INT,        /* sent CMD12, waiting for response INT */
344361b8482SLucas Stach 	} multiblock_status;
345de5bdbffSDong Aisheng 	u32 is_ddr;
3461c4989b0SBOUGH CHEN 	struct pm_qos_request pm_qos_req;
347e149860dSRichard Zhu };
348e149860dSRichard Zhu 
349abfafc2dSShawn Guo static const struct of_device_id imx_esdhc_dt_ids[] = {
350f47c4bbfSShawn Guo 	{ .compatible = "fsl,imx25-esdhc", .data = &esdhc_imx25_data, },
351f47c4bbfSShawn Guo 	{ .compatible = "fsl,imx35-esdhc", .data = &esdhc_imx35_data, },
352f47c4bbfSShawn Guo 	{ .compatible = "fsl,imx51-esdhc", .data = &esdhc_imx51_data, },
353f47c4bbfSShawn Guo 	{ .compatible = "fsl,imx53-esdhc", .data = &esdhc_imx53_data, },
354913d4951SDong Aisheng 	{ .compatible = "fsl,imx6sx-usdhc", .data = &usdhc_imx6sx_data, },
3556e9fd28eSDong Aisheng 	{ .compatible = "fsl,imx6sl-usdhc", .data = &usdhc_imx6sl_data, },
35674898cbcSHaibo Chen 	{ .compatible = "fsl,imx6sll-usdhc", .data = &usdhc_imx6sll_data, },
357f47c4bbfSShawn Guo 	{ .compatible = "fsl,imx6q-usdhc", .data = &usdhc_imx6q_data, },
358af6a50d4SBOUGH CHEN 	{ .compatible = "fsl,imx6ull-usdhc", .data = &usdhc_imx6ull_data, },
35928b07674SHaibo Chen 	{ .compatible = "fsl,imx7d-usdhc", .data = &usdhc_imx7d_data, },
3601c4989b0SBOUGH CHEN 	{ .compatible = "fsl,imx7ulp-usdhc", .data = &usdhc_imx7ulp_data, },
361029e2476SBOUGH CHEN 	{ .compatible = "fsl,imx8qxp-usdhc", .data = &usdhc_imx8qxp_data, },
362cde5e8e9SHaibo Chen 	{ .compatible = "fsl,imx8mm-usdhc", .data = &usdhc_imx8mm_data, },
3632f4788f3SJesse Taube 	{ .compatible = "fsl,imxrt1050-usdhc", .data = &usdhc_imxrt1050_data, },
3645c4f0062SChester Lin 	{ .compatible = "nxp,s32g2-usdhc", .data = &usdhc_s32g2_data, },
365abfafc2dSShawn Guo 	{ /* sentinel */ }
366abfafc2dSShawn Guo };
367abfafc2dSShawn Guo MODULE_DEVICE_TABLE(of, imx_esdhc_dt_ids);
368abfafc2dSShawn Guo 
36957ed3314SShawn Guo static inline int is_imx25_esdhc(struct pltfm_imx_data *data)
37057ed3314SShawn Guo {
371f47c4bbfSShawn Guo 	return data->socdata == &esdhc_imx25_data;
37257ed3314SShawn Guo }
37357ed3314SShawn Guo 
37457ed3314SShawn Guo static inline int is_imx53_esdhc(struct pltfm_imx_data *data)
37557ed3314SShawn Guo {
376f47c4bbfSShawn Guo 	return data->socdata == &esdhc_imx53_data;
37757ed3314SShawn Guo }
37857ed3314SShawn Guo 
3799d61c009SShawn Guo static inline int esdhc_is_usdhc(struct pltfm_imx_data *data)
3809d61c009SShawn Guo {
381f47c4bbfSShawn Guo 	return !!(data->socdata->flags & ESDHC_FLAG_USDHC);
3829d61c009SShawn Guo }
3839d61c009SShawn Guo 
38495f25efeSWolfram Sang static inline void esdhc_clrset_le(struct sdhci_host *host, u32 mask, u32 val, int reg)
38595f25efeSWolfram Sang {
38695f25efeSWolfram Sang 	void __iomem *base = host->ioaddr + (reg & ~0x3);
38795f25efeSWolfram Sang 	u32 shift = (reg & 0x3) * 8;
38895f25efeSWolfram Sang 
38995f25efeSWolfram Sang 	writel(((readl(base) & ~(mask << shift)) | (val << shift)), base);
39095f25efeSWolfram Sang }
39195f25efeSWolfram Sang 
3923722c74cSHaibo Chen #define DRIVER_NAME "sdhci-esdhc-imx"
3933722c74cSHaibo Chen #define ESDHC_IMX_DUMP(f, x...) \
3943722c74cSHaibo Chen 	pr_err("%s: " DRIVER_NAME ": " f, mmc_hostname(host->mmc), ## x)
3953722c74cSHaibo Chen static void esdhc_dump_debug_regs(struct sdhci_host *host)
3963722c74cSHaibo Chen {
3973722c74cSHaibo Chen 	int i;
3983722c74cSHaibo Chen 	char *debug_status[7] = {
3993722c74cSHaibo Chen 				 "cmd debug status",
4003722c74cSHaibo Chen 				 "data debug status",
4013722c74cSHaibo Chen 				 "trans debug status",
4023722c74cSHaibo Chen 				 "dma debug status",
4033722c74cSHaibo Chen 				 "adma debug status",
4043722c74cSHaibo Chen 				 "fifo debug status",
4053722c74cSHaibo Chen 				 "async fifo debug status"
4063722c74cSHaibo Chen 	};
4073722c74cSHaibo Chen 
4083722c74cSHaibo Chen 	ESDHC_IMX_DUMP("========= ESDHC IMX DEBUG STATUS DUMP =========\n");
4093722c74cSHaibo Chen 	for (i = 0; i < 7; i++) {
4103722c74cSHaibo Chen 		esdhc_clrset_le(host, ESDHC_DEBUG_SEL_MASK,
4113722c74cSHaibo Chen 			ESDHC_DEBUG_SEL_CMD_STATE + i, ESDHC_DEBUG_SEL_REG);
4123722c74cSHaibo Chen 		ESDHC_IMX_DUMP("%s:  0x%04x\n", debug_status[i],
4133722c74cSHaibo Chen 			readw(host->ioaddr + ESDHC_DEBUG_SEL_AND_STATUS_REG));
4143722c74cSHaibo Chen 	}
4153722c74cSHaibo Chen 
4163722c74cSHaibo Chen 	esdhc_clrset_le(host, ESDHC_DEBUG_SEL_MASK, 0, ESDHC_DEBUG_SEL_REG);
4173722c74cSHaibo Chen 
4183722c74cSHaibo Chen }
4193722c74cSHaibo Chen 
420f581e909SHaibo Chen static inline void esdhc_wait_for_card_clock_gate_off(struct sdhci_host *host)
421f581e909SHaibo Chen {
422f581e909SHaibo Chen 	u32 present_state;
423f581e909SHaibo Chen 	int ret;
424f581e909SHaibo Chen 
425f581e909SHaibo Chen 	ret = readl_poll_timeout(host->ioaddr + ESDHC_PRSSTAT, present_state,
426f581e909SHaibo Chen 				(present_state & ESDHC_CLOCK_GATE_OFF), 2, 100);
427f581e909SHaibo Chen 	if (ret == -ETIMEDOUT)
428f581e909SHaibo Chen 		dev_warn(mmc_dev(host->mmc), "%s: card clock still not gate off in 100us!.\n", __func__);
429f581e909SHaibo Chen }
430f581e909SHaibo Chen 
43145334ee1SHaibo Chen /* Enable the auto tuning circuit to check the CMD line and BUS line */
43245334ee1SHaibo Chen static inline void usdhc_auto_tuning_mode_sel(struct sdhci_host *host)
43345334ee1SHaibo Chen {
43445334ee1SHaibo Chen 	u32 buswidth, auto_tune_buswidth;
43545334ee1SHaibo Chen 
43645334ee1SHaibo Chen 	buswidth = USDHC_GET_BUSWIDTH(readl(host->ioaddr + SDHCI_HOST_CONTROL));
43745334ee1SHaibo Chen 
43845334ee1SHaibo Chen 	switch (buswidth) {
43945334ee1SHaibo Chen 	case ESDHC_CTRL_8BITBUS:
44045334ee1SHaibo Chen 		auto_tune_buswidth = ESDHC_VEND_SPEC2_AUTO_TUNE_8BIT_EN;
44145334ee1SHaibo Chen 		break;
44245334ee1SHaibo Chen 	case ESDHC_CTRL_4BITBUS:
44345334ee1SHaibo Chen 		auto_tune_buswidth = ESDHC_VEND_SPEC2_AUTO_TUNE_4BIT_EN;
44445334ee1SHaibo Chen 		break;
44545334ee1SHaibo Chen 	default:	/* 1BITBUS */
44645334ee1SHaibo Chen 		auto_tune_buswidth = ESDHC_VEND_SPEC2_AUTO_TUNE_1BIT_EN;
44745334ee1SHaibo Chen 		break;
44845334ee1SHaibo Chen 	}
44945334ee1SHaibo Chen 
45045334ee1SHaibo Chen 	esdhc_clrset_le(host, ESDHC_VEND_SPEC2_AUTO_TUNE_MODE_MASK,
45145334ee1SHaibo Chen 			auto_tune_buswidth | ESDHC_VEND_SPEC2_AUTO_TUNE_CMD_EN,
45245334ee1SHaibo Chen 			ESDHC_VEND_SPEC2);
45345334ee1SHaibo Chen }
45445334ee1SHaibo Chen 
4557e29c306SWolfram Sang static u32 esdhc_readl_le(struct sdhci_host *host, int reg)
4567e29c306SWolfram Sang {
457361b8482SLucas Stach 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
458070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
459913413c3SShawn Guo 	u32 val = readl(host->ioaddr + reg);
460913413c3SShawn Guo 
4610322191eSDong Aisheng 	if (unlikely(reg == SDHCI_PRESENT_STATE)) {
4620322191eSDong Aisheng 		u32 fsl_prss = val;
4630322191eSDong Aisheng 		/* save the least 20 bits */
4640322191eSDong Aisheng 		val = fsl_prss & 0x000FFFFF;
4650322191eSDong Aisheng 		/* move dat[0-3] bits */
4660322191eSDong Aisheng 		val |= (fsl_prss & 0x0F000000) >> 4;
4670322191eSDong Aisheng 		/* move cmd line bit */
4680322191eSDong Aisheng 		val |= (fsl_prss & 0x00800000) << 1;
4690322191eSDong Aisheng 	}
4700322191eSDong Aisheng 
47197e4ba6aSRichard Zhu 	if (unlikely(reg == SDHCI_CAPABILITIES)) {
4726b4fb671SDong Aisheng 		/* ignore bit[0-15] as it stores cap_1 register val for mx6sl */
4736b4fb671SDong Aisheng 		if (imx_data->socdata->flags & ESDHC_FLAG_HAVE_CAP1)
4746b4fb671SDong Aisheng 			val &= 0xffff0000;
4756b4fb671SDong Aisheng 
47697e4ba6aSRichard Zhu 		/* In FSL esdhc IC module, only bit20 is used to indicate the
47797e4ba6aSRichard Zhu 		 * ADMA2 capability of esdhc, but this bit is messed up on
47897e4ba6aSRichard Zhu 		 * some SOCs (e.g. on MX25, MX35 this bit is set, but they
47997e4ba6aSRichard Zhu 		 * don't actually support ADMA2). So set the BROKEN_ADMA
480d04f8d5bSBenoît Thébaudeau 		 * quirk on MX25/35 platforms.
48197e4ba6aSRichard Zhu 		 */
48297e4ba6aSRichard Zhu 
48397e4ba6aSRichard Zhu 		if (val & SDHCI_CAN_DO_ADMA1) {
48497e4ba6aSRichard Zhu 			val &= ~SDHCI_CAN_DO_ADMA1;
48597e4ba6aSRichard Zhu 			val |= SDHCI_CAN_DO_ADMA2;
48697e4ba6aSRichard Zhu 		}
48797e4ba6aSRichard Zhu 	}
48897e4ba6aSRichard Zhu 
4896e9fd28eSDong Aisheng 	if (unlikely(reg == SDHCI_CAPABILITIES_1)) {
4906e9fd28eSDong Aisheng 		if (esdhc_is_usdhc(imx_data)) {
4916e9fd28eSDong Aisheng 			if (imx_data->socdata->flags & ESDHC_FLAG_HAVE_CAP1)
4926e9fd28eSDong Aisheng 				val = readl(host->ioaddr + SDHCI_CAPABILITIES) & 0xFFFF;
4936e9fd28eSDong Aisheng 			else
4946e9fd28eSDong Aisheng 				/* imx6q/dl does not have cap_1 register, fake one */
4950322191eSDong Aisheng 				val = SDHCI_SUPPORT_DDR50 | SDHCI_SUPPORT_SDR104
496888824bbSDong Aisheng 					| SDHCI_SUPPORT_SDR50
497da0295ffSDong Aisheng 					| SDHCI_USE_SDR50_TUNING
498a8e809ecSMasahiro Yamada 					| FIELD_PREP(SDHCI_RETUNING_MODE_MASK,
499a8e809ecSMasahiro Yamada 						     SDHCI_TUNING_MODE_3);
50028b07674SHaibo Chen 
50192748beaSStefan Agner 			/*
50292748beaSStefan Agner 			 * Do not advertise faster UHS modes if there are no
50392748beaSStefan Agner 			 * pinctrl states for 100MHz/200MHz.
50492748beaSStefan Agner 			 */
50525e8b9ebSShawn Guo 			if (IS_ERR_OR_NULL(imx_data->pins_100mhz))
50625e8b9ebSShawn Guo 				val &= ~(SDHCI_SUPPORT_SDR50 | SDHCI_SUPPORT_DDR50);
50725e8b9ebSShawn Guo 			if (IS_ERR_OR_NULL(imx_data->pins_200mhz))
50825e8b9ebSShawn Guo 				val &= ~(SDHCI_SUPPORT_SDR104 | SDHCI_SUPPORT_HS400);
5096e9fd28eSDong Aisheng 		}
5106e9fd28eSDong Aisheng 	}
5110322191eSDong Aisheng 
5129d61c009SShawn Guo 	if (unlikely(reg == SDHCI_MAX_CURRENT) && esdhc_is_usdhc(imx_data)) {
5130322191eSDong Aisheng 		val = 0;
514804a65b3SMasahiro Yamada 		val |= FIELD_PREP(SDHCI_MAX_CURRENT_330_MASK, 0xFF);
515804a65b3SMasahiro Yamada 		val |= FIELD_PREP(SDHCI_MAX_CURRENT_300_MASK, 0xFF);
516804a65b3SMasahiro Yamada 		val |= FIELD_PREP(SDHCI_MAX_CURRENT_180_MASK, 0xFF);
5170322191eSDong Aisheng 	}
5180322191eSDong Aisheng 
51997e4ba6aSRichard Zhu 	if (unlikely(reg == SDHCI_INT_STATUS)) {
52060bf6396SShawn Guo 		if (val & ESDHC_INT_VENDOR_SPEC_DMA_ERR) {
52160bf6396SShawn Guo 			val &= ~ESDHC_INT_VENDOR_SPEC_DMA_ERR;
52297e4ba6aSRichard Zhu 			val |= SDHCI_INT_ADMA_ERROR;
52397e4ba6aSRichard Zhu 		}
524361b8482SLucas Stach 
525361b8482SLucas Stach 		/*
526361b8482SLucas Stach 		 * mask off the interrupt we get in response to the manually
527361b8482SLucas Stach 		 * sent CMD12
528361b8482SLucas Stach 		 */
529361b8482SLucas Stach 		if ((imx_data->multiblock_status == WAIT_FOR_INT) &&
530361b8482SLucas Stach 		    ((val & SDHCI_INT_RESPONSE) == SDHCI_INT_RESPONSE)) {
531361b8482SLucas Stach 			val &= ~SDHCI_INT_RESPONSE;
532361b8482SLucas Stach 			writel(SDHCI_INT_RESPONSE, host->ioaddr +
533361b8482SLucas Stach 						   SDHCI_INT_STATUS);
534361b8482SLucas Stach 			imx_data->multiblock_status = NO_CMD_PENDING;
535361b8482SLucas Stach 		}
53697e4ba6aSRichard Zhu 	}
53797e4ba6aSRichard Zhu 
5387e29c306SWolfram Sang 	return val;
5397e29c306SWolfram Sang }
5407e29c306SWolfram Sang 
5417e29c306SWolfram Sang static void esdhc_writel_le(struct sdhci_host *host, u32 val, int reg)
5427e29c306SWolfram Sang {
543e149860dSRichard Zhu 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
544070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
5450d58864bSTony Lin 	u32 data;
546e149860dSRichard Zhu 
54777da3da0SAaron Brice 	if (unlikely(reg == SDHCI_INT_ENABLE || reg == SDHCI_SIGNAL_ENABLE ||
54877da3da0SAaron Brice 			reg == SDHCI_INT_STATUS)) {
549b7321042SDong Aisheng 		if ((val & SDHCI_INT_CARD_INT) && !esdhc_is_usdhc(imx_data)) {
5500d58864bSTony Lin 			/*
5510d58864bSTony Lin 			 * Clear and then set D3CD bit to avoid missing the
552d04f8d5bSBenoît Thébaudeau 			 * card interrupt. This is an eSDHC controller problem
5530d58864bSTony Lin 			 * so we need to apply the following workaround: clear
5540d58864bSTony Lin 			 * and set D3CD bit will make eSDHC re-sample the card
5550d58864bSTony Lin 			 * interrupt. In case a card interrupt was lost,
5560d58864bSTony Lin 			 * re-sample it by the following steps.
5570d58864bSTony Lin 			 */
5580d58864bSTony Lin 			data = readl(host->ioaddr + SDHCI_HOST_CONTROL);
55960bf6396SShawn Guo 			data &= ~ESDHC_CTRL_D3CD;
5600d58864bSTony Lin 			writel(data, host->ioaddr + SDHCI_HOST_CONTROL);
56160bf6396SShawn Guo 			data |= ESDHC_CTRL_D3CD;
5620d58864bSTony Lin 			writel(data, host->ioaddr + SDHCI_HOST_CONTROL);
5630d58864bSTony Lin 		}
564915be485SDong Aisheng 
565915be485SDong Aisheng 		if (val & SDHCI_INT_ADMA_ERROR) {
566915be485SDong Aisheng 			val &= ~SDHCI_INT_ADMA_ERROR;
567915be485SDong Aisheng 			val |= ESDHC_INT_VENDOR_SPEC_DMA_ERR;
568915be485SDong Aisheng 		}
5690d58864bSTony Lin 	}
5700d58864bSTony Lin 
571f47c4bbfSShawn Guo 	if (unlikely((imx_data->socdata->flags & ESDHC_FLAG_MULTIBLK_NO_INT)
57258ac8177SRichard Zhu 				&& (reg == SDHCI_INT_STATUS)
57358ac8177SRichard Zhu 				&& (val & SDHCI_INT_DATA_END))) {
57458ac8177SRichard Zhu 			u32 v;
57560bf6396SShawn Guo 			v = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
57660bf6396SShawn Guo 			v &= ~ESDHC_VENDOR_SPEC_SDIO_QUIRK;
57760bf6396SShawn Guo 			writel(v, host->ioaddr + ESDHC_VENDOR_SPEC);
578361b8482SLucas Stach 
579361b8482SLucas Stach 			if (imx_data->multiblock_status == MULTIBLK_IN_PROCESS)
580361b8482SLucas Stach 			{
581361b8482SLucas Stach 				/* send a manual CMD12 with RESPTYP=none */
582361b8482SLucas Stach 				data = MMC_STOP_TRANSMISSION << 24 |
583361b8482SLucas Stach 				       SDHCI_CMD_ABORTCMD << 16;
584361b8482SLucas Stach 				writel(data, host->ioaddr + SDHCI_TRANSFER_MODE);
585361b8482SLucas Stach 				imx_data->multiblock_status = WAIT_FOR_INT;
586361b8482SLucas Stach 			}
58758ac8177SRichard Zhu 	}
58858ac8177SRichard Zhu 
5897e29c306SWolfram Sang 	writel(val, host->ioaddr + reg);
5907e29c306SWolfram Sang }
5917e29c306SWolfram Sang 
59295f25efeSWolfram Sang static u16 esdhc_readw_le(struct sdhci_host *host, int reg)
59395f25efeSWolfram Sang {
594ef4d0888SShawn Guo 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
595070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
5960322191eSDong Aisheng 	u16 ret = 0;
5970322191eSDong Aisheng 	u32 val;
598ef4d0888SShawn Guo 
59995a2482aSShawn Guo 	if (unlikely(reg == SDHCI_HOST_VERSION)) {
600ef4d0888SShawn Guo 		reg ^= 2;
6019d61c009SShawn Guo 		if (esdhc_is_usdhc(imx_data)) {
60295a2482aSShawn Guo 			/*
603ef4d0888SShawn Guo 			 * The usdhc register returns a wrong host version.
604ef4d0888SShawn Guo 			 * Correct it here.
60595a2482aSShawn Guo 			 */
606ef4d0888SShawn Guo 			return SDHCI_SPEC_300;
607ef4d0888SShawn Guo 		}
60895a2482aSShawn Guo 	}
60995f25efeSWolfram Sang 
6100322191eSDong Aisheng 	if (unlikely(reg == SDHCI_HOST_CONTROL2)) {
6110322191eSDong Aisheng 		val = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
6120322191eSDong Aisheng 		if (val & ESDHC_VENDOR_SPEC_VSELECT)
6130322191eSDong Aisheng 			ret |= SDHCI_CTRL_VDD_180;
6140322191eSDong Aisheng 
6159d61c009SShawn Guo 		if (esdhc_is_usdhc(imx_data)) {
6166e9fd28eSDong Aisheng 			if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING)
6170322191eSDong Aisheng 				val = readl(host->ioaddr + ESDHC_MIX_CTRL);
6186e9fd28eSDong Aisheng 			else if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING)
6196e9fd28eSDong Aisheng 				/* the std tuning bits is in ACMD12_ERR for imx6sl */
620869f8a69SAdrian Hunter 				val = readl(host->ioaddr + SDHCI_AUTO_CMD_STATUS);
6216e9fd28eSDong Aisheng 		}
6226e9fd28eSDong Aisheng 
6230322191eSDong Aisheng 		if (val & ESDHC_MIX_CTRL_EXE_TUNE)
6240322191eSDong Aisheng 			ret |= SDHCI_CTRL_EXEC_TUNING;
6250322191eSDong Aisheng 		if (val & ESDHC_MIX_CTRL_SMPCLK_SEL)
6260322191eSDong Aisheng 			ret |= SDHCI_CTRL_TUNED_CLK;
6270322191eSDong Aisheng 
6280322191eSDong Aisheng 		ret &= ~SDHCI_CTRL_PRESET_VAL_ENABLE;
6290322191eSDong Aisheng 
6300322191eSDong Aisheng 		return ret;
6310322191eSDong Aisheng 	}
6320322191eSDong Aisheng 
6337dd109efSDong Aisheng 	if (unlikely(reg == SDHCI_TRANSFER_MODE)) {
6347dd109efSDong Aisheng 		if (esdhc_is_usdhc(imx_data)) {
6357dd109efSDong Aisheng 			u32 m = readl(host->ioaddr + ESDHC_MIX_CTRL);
6367dd109efSDong Aisheng 			ret = m & ESDHC_MIX_CTRL_SDHCI_MASK;
6377dd109efSDong Aisheng 			/* Swap AC23 bit */
6387dd109efSDong Aisheng 			if (m & ESDHC_MIX_CTRL_AC23EN) {
6397dd109efSDong Aisheng 				ret &= ~ESDHC_MIX_CTRL_AC23EN;
6407dd109efSDong Aisheng 				ret |= SDHCI_TRNS_AUTO_CMD23;
6417dd109efSDong Aisheng 			}
6427dd109efSDong Aisheng 		} else {
6437dd109efSDong Aisheng 			ret = readw(host->ioaddr + SDHCI_TRANSFER_MODE);
6447dd109efSDong Aisheng 		}
6457dd109efSDong Aisheng 
6467dd109efSDong Aisheng 		return ret;
6477dd109efSDong Aisheng 	}
6487dd109efSDong Aisheng 
64995f25efeSWolfram Sang 	return readw(host->ioaddr + reg);
65095f25efeSWolfram Sang }
65195f25efeSWolfram Sang 
65295f25efeSWolfram Sang static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg)
65395f25efeSWolfram Sang {
65495f25efeSWolfram Sang 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
655070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
6560322191eSDong Aisheng 	u32 new_val = 0;
65795f25efeSWolfram Sang 
65895f25efeSWolfram Sang 	switch (reg) {
6590322191eSDong Aisheng 	case SDHCI_CLOCK_CONTROL:
6600322191eSDong Aisheng 		new_val = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
6610322191eSDong Aisheng 		if (val & SDHCI_CLOCK_CARD_EN)
6620322191eSDong Aisheng 			new_val |= ESDHC_VENDOR_SPEC_FRC_SDCLK_ON;
6630322191eSDong Aisheng 		else
6640322191eSDong Aisheng 			new_val &= ~ESDHC_VENDOR_SPEC_FRC_SDCLK_ON;
6650322191eSDong Aisheng 		writel(new_val, host->ioaddr + ESDHC_VENDOR_SPEC);
666f581e909SHaibo Chen 		if (!(new_val & ESDHC_VENDOR_SPEC_FRC_SDCLK_ON))
667f581e909SHaibo Chen 			esdhc_wait_for_card_clock_gate_off(host);
6680322191eSDong Aisheng 		return;
6690322191eSDong Aisheng 	case SDHCI_HOST_CONTROL2:
6700322191eSDong Aisheng 		new_val = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
6710322191eSDong Aisheng 		if (val & SDHCI_CTRL_VDD_180)
6720322191eSDong Aisheng 			new_val |= ESDHC_VENDOR_SPEC_VSELECT;
6730322191eSDong Aisheng 		else
6740322191eSDong Aisheng 			new_val &= ~ESDHC_VENDOR_SPEC_VSELECT;
6750322191eSDong Aisheng 		writel(new_val, host->ioaddr + ESDHC_VENDOR_SPEC);
676a0dbbdc2SHaibo Chen 		if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) {
677869f8a69SAdrian Hunter 			u32 v = readl(host->ioaddr + SDHCI_AUTO_CMD_STATUS);
6786e9fd28eSDong Aisheng 			u32 m = readl(host->ioaddr + ESDHC_MIX_CTRL);
6798b2bb0adSDong Aisheng 			if (val & SDHCI_CTRL_TUNED_CLK) {
6808b2bb0adSDong Aisheng 				v |= ESDHC_MIX_CTRL_SMPCLK_SEL;
6816e9fd28eSDong Aisheng 			} else {
6828b2bb0adSDong Aisheng 				v &= ~ESDHC_MIX_CTRL_SMPCLK_SEL;
6836e9fd28eSDong Aisheng 				m &= ~ESDHC_MIX_CTRL_FBCLK_SEL;
6840b330e38SDong Aisheng 				m &= ~ESDHC_MIX_CTRL_AUTO_TUNE_EN;
6856e9fd28eSDong Aisheng 			}
6866e9fd28eSDong Aisheng 
6878b2bb0adSDong Aisheng 			if (val & SDHCI_CTRL_EXEC_TUNING) {
6888b2bb0adSDong Aisheng 				v |= ESDHC_MIX_CTRL_EXE_TUNE;
6898b2bb0adSDong Aisheng 				m |= ESDHC_MIX_CTRL_FBCLK_SEL;
6900b330e38SDong Aisheng 				m |= ESDHC_MIX_CTRL_AUTO_TUNE_EN;
69145334ee1SHaibo Chen 				usdhc_auto_tuning_mode_sel(host);
6928b2bb0adSDong Aisheng 			} else {
6938b2bb0adSDong Aisheng 				v &= ~ESDHC_MIX_CTRL_EXE_TUNE;
6948b2bb0adSDong Aisheng 			}
6956e9fd28eSDong Aisheng 
696869f8a69SAdrian Hunter 			writel(v, host->ioaddr + SDHCI_AUTO_CMD_STATUS);
6976e9fd28eSDong Aisheng 			writel(m, host->ioaddr + ESDHC_MIX_CTRL);
6986e9fd28eSDong Aisheng 		}
6990322191eSDong Aisheng 		return;
70095f25efeSWolfram Sang 	case SDHCI_TRANSFER_MODE:
701f47c4bbfSShawn Guo 		if ((imx_data->socdata->flags & ESDHC_FLAG_MULTIBLK_NO_INT)
70258ac8177SRichard Zhu 				&& (host->cmd->opcode == SD_IO_RW_EXTENDED)
70358ac8177SRichard Zhu 				&& (host->cmd->data->blocks > 1)
70458ac8177SRichard Zhu 				&& (host->cmd->data->flags & MMC_DATA_READ)) {
70558ac8177SRichard Zhu 			u32 v;
70660bf6396SShawn Guo 			v = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
70760bf6396SShawn Guo 			v |= ESDHC_VENDOR_SPEC_SDIO_QUIRK;
70860bf6396SShawn Guo 			writel(v, host->ioaddr + ESDHC_VENDOR_SPEC);
70958ac8177SRichard Zhu 		}
71069f54698SShawn Guo 
7119d61c009SShawn Guo 		if (esdhc_is_usdhc(imx_data)) {
7123fbd4322SAndrew Gabbasov 			u32 wml;
71369f54698SShawn Guo 			u32 m = readl(host->ioaddr + ESDHC_MIX_CTRL);
7142a15f981SShawn Guo 			/* Swap AC23 bit */
7152a15f981SShawn Guo 			if (val & SDHCI_TRNS_AUTO_CMD23) {
7162a15f981SShawn Guo 				val &= ~SDHCI_TRNS_AUTO_CMD23;
7172a15f981SShawn Guo 				val |= ESDHC_MIX_CTRL_AC23EN;
7182a15f981SShawn Guo 			}
7192a15f981SShawn Guo 			m = val | (m & ~ESDHC_MIX_CTRL_SDHCI_MASK);
72069f54698SShawn Guo 			writel(m, host->ioaddr + ESDHC_MIX_CTRL);
7213fbd4322SAndrew Gabbasov 
7223fbd4322SAndrew Gabbasov 			/* Set watermark levels for PIO access to maximum value
7233fbd4322SAndrew Gabbasov 			 * (128 words) to accommodate full 512 bytes buffer.
7243fbd4322SAndrew Gabbasov 			 * For DMA access restore the levels to default value.
7253fbd4322SAndrew Gabbasov 			 */
7263fbd4322SAndrew Gabbasov 			m = readl(host->ioaddr + ESDHC_WTMK_LVL);
727e534b82fSHaibo Chen 			if (val & SDHCI_TRNS_DMA) {
7283fbd4322SAndrew Gabbasov 				wml = ESDHC_WTMK_LVL_WML_VAL_DEF;
729e534b82fSHaibo Chen 			} else {
730e534b82fSHaibo Chen 				u8 ctrl;
7313fbd4322SAndrew Gabbasov 				wml = ESDHC_WTMK_LVL_WML_VAL_MAX;
732e534b82fSHaibo Chen 
733e534b82fSHaibo Chen 				/*
734e534b82fSHaibo Chen 				 * Since already disable DMA mode, so also need
735e534b82fSHaibo Chen 				 * to clear the DMASEL. Otherwise, for standard
736e534b82fSHaibo Chen 				 * tuning, when send tuning command, usdhc will
737e534b82fSHaibo Chen 				 * still prefetch the ADMA script from wrong
738e534b82fSHaibo Chen 				 * DMA address, then we will see IOMMU report
739e534b82fSHaibo Chen 				 * some error which show lack of TLB mapping.
740e534b82fSHaibo Chen 				 */
741e534b82fSHaibo Chen 				ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
742e534b82fSHaibo Chen 				ctrl &= ~SDHCI_CTRL_DMA_MASK;
743e534b82fSHaibo Chen 				sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
744e534b82fSHaibo Chen 			}
7453fbd4322SAndrew Gabbasov 			m &= ~(ESDHC_WTMK_LVL_RD_WML_MASK |
7463fbd4322SAndrew Gabbasov 			       ESDHC_WTMK_LVL_WR_WML_MASK);
7473fbd4322SAndrew Gabbasov 			m |= (wml << ESDHC_WTMK_LVL_RD_WML_SHIFT) |
7483fbd4322SAndrew Gabbasov 			     (wml << ESDHC_WTMK_LVL_WR_WML_SHIFT);
7493fbd4322SAndrew Gabbasov 			writel(m, host->ioaddr + ESDHC_WTMK_LVL);
75069f54698SShawn Guo 		} else {
75169f54698SShawn Guo 			/*
75269f54698SShawn Guo 			 * Postpone this write, we must do it together with a
75369f54698SShawn Guo 			 * command write that is down below.
75469f54698SShawn Guo 			 */
755e149860dSRichard Zhu 			imx_data->scratchpad = val;
75669f54698SShawn Guo 		}
75795f25efeSWolfram Sang 		return;
75895f25efeSWolfram Sang 	case SDHCI_COMMAND:
759361b8482SLucas Stach 		if (host->cmd->opcode == MMC_STOP_TRANSMISSION)
76058ac8177SRichard Zhu 			val |= SDHCI_CMD_ABORTCMD;
76195a2482aSShawn Guo 
762361b8482SLucas Stach 		if ((host->cmd->opcode == MMC_SET_BLOCK_COUNT) &&
763f47c4bbfSShawn Guo 		    (imx_data->socdata->flags & ESDHC_FLAG_MULTIBLK_NO_INT))
764361b8482SLucas Stach 			imx_data->multiblock_status = MULTIBLK_IN_PROCESS;
765361b8482SLucas Stach 
7669d61c009SShawn Guo 		if (esdhc_is_usdhc(imx_data))
76795a2482aSShawn Guo 			writel(val << 16,
76895a2482aSShawn Guo 			       host->ioaddr + SDHCI_TRANSFER_MODE);
76969f54698SShawn Guo 		else
770e149860dSRichard Zhu 			writel(val << 16 | imx_data->scratchpad,
77195f25efeSWolfram Sang 			       host->ioaddr + SDHCI_TRANSFER_MODE);
77295f25efeSWolfram Sang 		return;
77395f25efeSWolfram Sang 	case SDHCI_BLOCK_SIZE:
77495f25efeSWolfram Sang 		val &= ~SDHCI_MAKE_BLKSZ(0x7, 0);
77595f25efeSWolfram Sang 		break;
77695f25efeSWolfram Sang 	}
77795f25efeSWolfram Sang 	esdhc_clrset_le(host, 0xffff, val, reg);
77895f25efeSWolfram Sang }
77995f25efeSWolfram Sang 
78077da3da0SAaron Brice static u8 esdhc_readb_le(struct sdhci_host *host, int reg)
78177da3da0SAaron Brice {
78277da3da0SAaron Brice 	u8 ret;
78377da3da0SAaron Brice 	u32 val;
78477da3da0SAaron Brice 
78577da3da0SAaron Brice 	switch (reg) {
78677da3da0SAaron Brice 	case SDHCI_HOST_CONTROL:
78777da3da0SAaron Brice 		val = readl(host->ioaddr + reg);
78877da3da0SAaron Brice 
78977da3da0SAaron Brice 		ret = val & SDHCI_CTRL_LED;
79077da3da0SAaron Brice 		ret |= (val >> 5) & SDHCI_CTRL_DMA_MASK;
79177da3da0SAaron Brice 		ret |= (val & ESDHC_CTRL_4BITBUS);
79277da3da0SAaron Brice 		ret |= (val & ESDHC_CTRL_8BITBUS) << 3;
79377da3da0SAaron Brice 		return ret;
79477da3da0SAaron Brice 	}
79577da3da0SAaron Brice 
79677da3da0SAaron Brice 	return readb(host->ioaddr + reg);
79777da3da0SAaron Brice }
79877da3da0SAaron Brice 
79995f25efeSWolfram Sang static void esdhc_writeb_le(struct sdhci_host *host, u8 val, int reg)
80095f25efeSWolfram Sang {
8019a0985b7SWilson Callan 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
802070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
80381a0a8bcSBenoît Thébaudeau 	u32 new_val = 0;
804af51079eSSascha Hauer 	u32 mask;
80595f25efeSWolfram Sang 
80695f25efeSWolfram Sang 	switch (reg) {
80795f25efeSWolfram Sang 	case SDHCI_POWER_CONTROL:
80895f25efeSWolfram Sang 		/*
80995f25efeSWolfram Sang 		 * FSL put some DMA bits here
81095f25efeSWolfram Sang 		 * If your board has a regulator, code should be here
81195f25efeSWolfram Sang 		 */
81295f25efeSWolfram Sang 		return;
81395f25efeSWolfram Sang 	case SDHCI_HOST_CONTROL:
8146b40d182SShawn Guo 		/* FSL messed up here, so we need to manually compose it. */
815af51079eSSascha Hauer 		new_val = val & SDHCI_CTRL_LED;
8167122bbb0SMasanari Iida 		/* ensure the endianness */
81795f25efeSWolfram Sang 		new_val |= ESDHC_HOST_CONTROL_LE;
8189a0985b7SWilson Callan 		/* bits 8&9 are reserved on mx25 */
8199a0985b7SWilson Callan 		if (!is_imx25_esdhc(imx_data)) {
82095f25efeSWolfram Sang 			/* DMA mode bits are shifted */
82195f25efeSWolfram Sang 			new_val |= (val & SDHCI_CTRL_DMA_MASK) << 5;
8229a0985b7SWilson Callan 		}
82395f25efeSWolfram Sang 
824af51079eSSascha Hauer 		/*
825af51079eSSascha Hauer 		 * Do not touch buswidth bits here. This is done in
826af51079eSSascha Hauer 		 * esdhc_pltfm_bus_width.
827f6825748SMartin Fuzzey 		 * Do not touch the D3CD bit either which is used for the
828d04f8d5bSBenoît Thébaudeau 		 * SDIO interrupt erratum workaround.
829af51079eSSascha Hauer 		 */
830f6825748SMartin Fuzzey 		mask = 0xffff & ~(ESDHC_CTRL_BUSWIDTH_MASK | ESDHC_CTRL_D3CD);
831af51079eSSascha Hauer 
832af51079eSSascha Hauer 		esdhc_clrset_le(host, mask, new_val, reg);
83395f25efeSWolfram Sang 		return;
83481a0a8bcSBenoît Thébaudeau 	case SDHCI_SOFTWARE_RESET:
83581a0a8bcSBenoît Thébaudeau 		if (val & SDHCI_RESET_DATA)
83681a0a8bcSBenoît Thébaudeau 			new_val = readl(host->ioaddr + SDHCI_HOST_CONTROL);
83781a0a8bcSBenoît Thébaudeau 		break;
83895f25efeSWolfram Sang 	}
83995f25efeSWolfram Sang 	esdhc_clrset_le(host, 0xff, val, reg);
840913413c3SShawn Guo 
84181a0a8bcSBenoît Thébaudeau 	if (reg == SDHCI_SOFTWARE_RESET) {
84281a0a8bcSBenoît Thébaudeau 		if (val & SDHCI_RESET_ALL) {
843913413c3SShawn Guo 			/*
84481a0a8bcSBenoît Thébaudeau 			 * The esdhc has a design violation to SDHC spec which
84581a0a8bcSBenoît Thébaudeau 			 * tells that software reset should not affect card
84681a0a8bcSBenoît Thébaudeau 			 * detection circuit. But esdhc clears its SYSCTL
84781a0a8bcSBenoît Thébaudeau 			 * register bits [0..2] during the software reset. This
84881a0a8bcSBenoît Thébaudeau 			 * will stop those clocks that card detection circuit
84981a0a8bcSBenoît Thébaudeau 			 * relies on. To work around it, we turn the clocks on
85081a0a8bcSBenoît Thébaudeau 			 * back to keep card detection circuit functional.
851913413c3SShawn Guo 			 */
852913413c3SShawn Guo 			esdhc_clrset_le(host, 0x7, 0x7, ESDHC_SYSTEM_CONTROL);
85358c8c4fbSShawn Guo 			/*
85458c8c4fbSShawn Guo 			 * The reset on usdhc fails to clear MIX_CTRL register.
85558c8c4fbSShawn Guo 			 * Do it manually here.
85658c8c4fbSShawn Guo 			 */
857de5bdbffSDong Aisheng 			if (esdhc_is_usdhc(imx_data)) {
85881a0a8bcSBenoît Thébaudeau 				/*
85981a0a8bcSBenoît Thébaudeau 				 * the tuning bits should be kept during reset
86081a0a8bcSBenoît Thébaudeau 				 */
861d131a71cSDong Aisheng 				new_val = readl(host->ioaddr + ESDHC_MIX_CTRL);
862d131a71cSDong Aisheng 				writel(new_val & ESDHC_MIX_CTRL_TUNING_MASK,
863d131a71cSDong Aisheng 						host->ioaddr + ESDHC_MIX_CTRL);
864de5bdbffSDong Aisheng 				imx_data->is_ddr = 0;
865de5bdbffSDong Aisheng 			}
86681a0a8bcSBenoît Thébaudeau 		} else if (val & SDHCI_RESET_DATA) {
86781a0a8bcSBenoît Thébaudeau 			/*
86881a0a8bcSBenoît Thébaudeau 			 * The eSDHC DAT line software reset clears at least the
86981a0a8bcSBenoît Thébaudeau 			 * data transfer width on i.MX25, so make sure that the
87081a0a8bcSBenoît Thébaudeau 			 * Host Control register is unaffected.
87181a0a8bcSBenoît Thébaudeau 			 */
87281a0a8bcSBenoît Thébaudeau 			esdhc_clrset_le(host, 0xff, new_val,
87381a0a8bcSBenoît Thébaudeau 					SDHCI_HOST_CONTROL);
87481a0a8bcSBenoît Thébaudeau 		}
87558c8c4fbSShawn Guo 	}
87695f25efeSWolfram Sang }
87795f25efeSWolfram Sang 
8780ddf03c9SLucas Stach static unsigned int esdhc_pltfm_get_max_clock(struct sdhci_host *host)
8790ddf03c9SLucas Stach {
8800ddf03c9SLucas Stach 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
8810ddf03c9SLucas Stach 
882a974862fSDong Aisheng 	return pltfm_host->clock;
8830ddf03c9SLucas Stach }
8840ddf03c9SLucas Stach 
88595f25efeSWolfram Sang static unsigned int esdhc_pltfm_get_min_clock(struct sdhci_host *host)
88695f25efeSWolfram Sang {
88795f25efeSWolfram Sang 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
88895f25efeSWolfram Sang 
889a974862fSDong Aisheng 	return pltfm_host->clock / 256 / 16;
89095f25efeSWolfram Sang }
89195f25efeSWolfram Sang 
8928ba9580aSLucas Stach static inline void esdhc_pltfm_set_clock(struct sdhci_host *host,
8938ba9580aSLucas Stach 					 unsigned int clock)
8948ba9580aSLucas Stach {
8958ba9580aSLucas Stach 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
896070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
897a974862fSDong Aisheng 	unsigned int host_clock = pltfm_host->clock;
8985143c953SBenoît Thébaudeau 	int ddr_pre_div = imx_data->is_ddr ? 2 : 1;
8995143c953SBenoît Thébaudeau 	int pre_div = 1;
900d31fc00aSDong Aisheng 	int div = 1;
901f581e909SHaibo Chen 	int ret;
902fed2f6e2SDong Aisheng 	u32 temp, val;
9038ba9580aSLucas Stach 
9049d61c009SShawn Guo 	if (esdhc_is_usdhc(imx_data)) {
905fed2f6e2SDong Aisheng 		val = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
906fed2f6e2SDong Aisheng 		writel(val & ~ESDHC_VENDOR_SPEC_FRC_SDCLK_ON,
907fed2f6e2SDong Aisheng 			host->ioaddr + ESDHC_VENDOR_SPEC);
908f581e909SHaibo Chen 		esdhc_wait_for_card_clock_gate_off(host);
909fed2f6e2SDong Aisheng 	}
91073e736f8SStefan Agner 
91173e736f8SStefan Agner 	if (clock == 0) {
91273e736f8SStefan Agner 		host->mmc->actual_clock = 0;
913373073efSRussell King 		return;
914fed2f6e2SDong Aisheng 	}
915d31fc00aSDong Aisheng 
916499ed50fSBenoît Thébaudeau 	/* For i.MX53 eSDHCv3, SYSCTL.SDCLKFS may not be set to 0. */
917499ed50fSBenoît Thébaudeau 	if (is_imx53_esdhc(imx_data)) {
918499ed50fSBenoît Thébaudeau 		/*
919499ed50fSBenoît Thébaudeau 		 * According to the i.MX53 reference manual, if DLLCTRL[10] can
920499ed50fSBenoît Thébaudeau 		 * be set, then the controller is eSDHCv3, else it is eSDHCv2.
921499ed50fSBenoît Thébaudeau 		 */
922499ed50fSBenoît Thébaudeau 		val = readl(host->ioaddr + ESDHC_DLL_CTRL);
923499ed50fSBenoît Thébaudeau 		writel(val | BIT(10), host->ioaddr + ESDHC_DLL_CTRL);
924499ed50fSBenoît Thébaudeau 		temp = readl(host->ioaddr + ESDHC_DLL_CTRL);
925499ed50fSBenoît Thébaudeau 		writel(val, host->ioaddr + ESDHC_DLL_CTRL);
926499ed50fSBenoît Thébaudeau 		if (temp & BIT(10))
927499ed50fSBenoît Thébaudeau 			pre_div = 2;
928499ed50fSBenoît Thébaudeau 	}
929499ed50fSBenoît Thébaudeau 
930d31fc00aSDong Aisheng 	temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
931d31fc00aSDong Aisheng 	temp &= ~(ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN
932d31fc00aSDong Aisheng 		| ESDHC_CLOCK_MASK);
933d31fc00aSDong Aisheng 	sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
934d31fc00aSDong Aisheng 
935af6a50d4SBOUGH CHEN 	if (imx_data->socdata->flags & ESDHC_FLAG_ERR010450) {
936af6a50d4SBOUGH CHEN 		unsigned int max_clock;
937af6a50d4SBOUGH CHEN 
938af6a50d4SBOUGH CHEN 		max_clock = imx_data->is_ddr ? 45000000 : 150000000;
939af6a50d4SBOUGH CHEN 
940af6a50d4SBOUGH CHEN 		clock = min(clock, max_clock);
941af6a50d4SBOUGH CHEN 	}
942af6a50d4SBOUGH CHEN 
9435143c953SBenoît Thébaudeau 	while (host_clock / (16 * pre_div * ddr_pre_div) > clock &&
9445143c953SBenoît Thébaudeau 			pre_div < 256)
945d31fc00aSDong Aisheng 		pre_div *= 2;
946d31fc00aSDong Aisheng 
9475143c953SBenoît Thébaudeau 	while (host_clock / (div * pre_div * ddr_pre_div) > clock && div < 16)
948d31fc00aSDong Aisheng 		div++;
949d31fc00aSDong Aisheng 
9505143c953SBenoît Thébaudeau 	host->mmc->actual_clock = host_clock / (div * pre_div * ddr_pre_div);
951d31fc00aSDong Aisheng 	dev_dbg(mmc_dev(host->mmc), "desired SD clock: %d, actual: %d\n",
952e76b8559SDong Aisheng 		clock, host->mmc->actual_clock);
953d31fc00aSDong Aisheng 
954d31fc00aSDong Aisheng 	pre_div >>= 1;
955d31fc00aSDong Aisheng 	div--;
956d31fc00aSDong Aisheng 
957d31fc00aSDong Aisheng 	temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
958d31fc00aSDong Aisheng 	temp |= (ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN
959d31fc00aSDong Aisheng 		| (div << ESDHC_DIVIDER_SHIFT)
960d31fc00aSDong Aisheng 		| (pre_div << ESDHC_PREDIV_SHIFT));
961d31fc00aSDong Aisheng 	sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
962fed2f6e2SDong Aisheng 
963f581e909SHaibo Chen 	/* need to wait the bit 3 of the PRSSTAT to be set, make sure card clock is stable */
964f581e909SHaibo Chen 	ret = readl_poll_timeout(host->ioaddr + ESDHC_PRSSTAT, temp,
965f581e909SHaibo Chen 				(temp & ESDHC_CLOCK_STABLE), 2, 100);
966f581e909SHaibo Chen 	if (ret == -ETIMEDOUT)
967f581e909SHaibo Chen 		dev_warn(mmc_dev(host->mmc), "card clock still not stable in 100us!.\n");
968f581e909SHaibo Chen 
9699d61c009SShawn Guo 	if (esdhc_is_usdhc(imx_data)) {
970fed2f6e2SDong Aisheng 		val = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
971fed2f6e2SDong Aisheng 		writel(val | ESDHC_VENDOR_SPEC_FRC_SDCLK_ON,
972fed2f6e2SDong Aisheng 			host->ioaddr + ESDHC_VENDOR_SPEC);
973fed2f6e2SDong Aisheng 	}
974fed2f6e2SDong Aisheng 
9758ba9580aSLucas Stach }
9768ba9580aSLucas Stach 
977913413c3SShawn Guo static unsigned int esdhc_pltfm_get_ro(struct sdhci_host *host)
978913413c3SShawn Guo {
979842afc02SShawn Guo 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
980070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
981842afc02SShawn Guo 	struct esdhc_platform_data *boarddata = &imx_data->boarddata;
982913413c3SShawn Guo 
983913413c3SShawn Guo 	switch (boarddata->wp_type) {
984913413c3SShawn Guo 	case ESDHC_WP_GPIO:
985fbe5fdd1SShawn Guo 		return mmc_gpio_get_ro(host->mmc);
986913413c3SShawn Guo 	case ESDHC_WP_CONTROLLER:
987913413c3SShawn Guo 		return !(readl(host->ioaddr + SDHCI_PRESENT_STATE) &
988913413c3SShawn Guo 			       SDHCI_WRITE_PROTECT);
989913413c3SShawn Guo 	case ESDHC_WP_NONE:
990913413c3SShawn Guo 		break;
991913413c3SShawn Guo 	}
992913413c3SShawn Guo 
993913413c3SShawn Guo 	return -ENOSYS;
994913413c3SShawn Guo }
995913413c3SShawn Guo 
9962317f56cSRussell King static void esdhc_pltfm_set_bus_width(struct sdhci_host *host, int width)
997af51079eSSascha Hauer {
998af51079eSSascha Hauer 	u32 ctrl;
999af51079eSSascha Hauer 
1000af51079eSSascha Hauer 	switch (width) {
1001af51079eSSascha Hauer 	case MMC_BUS_WIDTH_8:
1002af51079eSSascha Hauer 		ctrl = ESDHC_CTRL_8BITBUS;
1003af51079eSSascha Hauer 		break;
1004af51079eSSascha Hauer 	case MMC_BUS_WIDTH_4:
1005af51079eSSascha Hauer 		ctrl = ESDHC_CTRL_4BITBUS;
1006af51079eSSascha Hauer 		break;
1007af51079eSSascha Hauer 	default:
1008af51079eSSascha Hauer 		ctrl = 0;
1009af51079eSSascha Hauer 		break;
1010af51079eSSascha Hauer 	}
1011af51079eSSascha Hauer 
1012af51079eSSascha Hauer 	esdhc_clrset_le(host, ESDHC_CTRL_BUSWIDTH_MASK, ctrl,
1013af51079eSSascha Hauer 			SDHCI_HOST_CONTROL);
1014af51079eSSascha Hauer }
1015af51079eSSascha Hauer 
1016de3e1dd0SBOUGH CHEN static int usdhc_execute_tuning(struct mmc_host *mmc, u32 opcode)
1017de3e1dd0SBOUGH CHEN {
1018de3e1dd0SBOUGH CHEN 	struct sdhci_host *host = mmc_priv(mmc);
1019de3e1dd0SBOUGH CHEN 
1020de3e1dd0SBOUGH CHEN 	/*
1021de3e1dd0SBOUGH CHEN 	 * i.MX uSDHC internally already uses a fixed optimized timing for
1022de3e1dd0SBOUGH CHEN 	 * DDR50, normally does not require tuning for DDR50 mode.
1023de3e1dd0SBOUGH CHEN 	 */
1024de3e1dd0SBOUGH CHEN 	if (host->timing == MMC_TIMING_UHS_DDR50)
1025de3e1dd0SBOUGH CHEN 		return 0;
1026de3e1dd0SBOUGH CHEN 
1027de3e1dd0SBOUGH CHEN 	return sdhci_execute_tuning(mmc, opcode);
1028de3e1dd0SBOUGH CHEN }
1029de3e1dd0SBOUGH CHEN 
10300322191eSDong Aisheng static void esdhc_prepare_tuning(struct sdhci_host *host, u32 val)
10310322191eSDong Aisheng {
10320322191eSDong Aisheng 	u32 reg;
10330ac4f496SHaibo Chen 	u8 sw_rst;
10340ac4f496SHaibo Chen 	int ret;
10350322191eSDong Aisheng 
10360322191eSDong Aisheng 	/* FIXME: delay a bit for card to be ready for next tuning due to errors */
10370322191eSDong Aisheng 	mdelay(1);
10380322191eSDong Aisheng 
10390ac4f496SHaibo Chen 	/* IC suggest to reset USDHC before every tuning command */
10400ac4f496SHaibo Chen 	esdhc_clrset_le(host, 0xff, SDHCI_RESET_ALL, SDHCI_SOFTWARE_RESET);
10410ac4f496SHaibo Chen 	ret = readb_poll_timeout(host->ioaddr + SDHCI_SOFTWARE_RESET, sw_rst,
10420ac4f496SHaibo Chen 				!(sw_rst & SDHCI_RESET_ALL), 10, 100);
10430ac4f496SHaibo Chen 	if (ret == -ETIMEDOUT)
10440ac4f496SHaibo Chen 		dev_warn(mmc_dev(host->mmc),
10450ac4f496SHaibo Chen 		"warning! RESET_ALL never complete before sending tuning command\n");
10460ac4f496SHaibo Chen 
10470322191eSDong Aisheng 	reg = readl(host->ioaddr + ESDHC_MIX_CTRL);
10480322191eSDong Aisheng 	reg |= ESDHC_MIX_CTRL_EXE_TUNE | ESDHC_MIX_CTRL_SMPCLK_SEL |
10490322191eSDong Aisheng 			ESDHC_MIX_CTRL_FBCLK_SEL;
10500322191eSDong Aisheng 	writel(reg, host->ioaddr + ESDHC_MIX_CTRL);
10510322191eSDong Aisheng 	writel(val << 8, host->ioaddr + ESDHC_TUNE_CTRL_STATUS);
10520322191eSDong Aisheng 	dev_dbg(mmc_dev(host->mmc),
1053d04f8d5bSBenoît Thébaudeau 		"tuning with delay 0x%x ESDHC_TUNE_CTRL_STATUS 0x%x\n",
10540322191eSDong Aisheng 			val, readl(host->ioaddr + ESDHC_TUNE_CTRL_STATUS));
10550322191eSDong Aisheng }
10560322191eSDong Aisheng 
10570322191eSDong Aisheng static void esdhc_post_tuning(struct sdhci_host *host)
10580322191eSDong Aisheng {
10590322191eSDong Aisheng 	u32 reg;
10600322191eSDong Aisheng 
106145334ee1SHaibo Chen 	usdhc_auto_tuning_mode_sel(host);
106245334ee1SHaibo Chen 
10630322191eSDong Aisheng 	reg = readl(host->ioaddr + ESDHC_MIX_CTRL);
10640322191eSDong Aisheng 	reg &= ~ESDHC_MIX_CTRL_EXE_TUNE;
1065da0295ffSDong Aisheng 	reg |= ESDHC_MIX_CTRL_AUTO_TUNE_EN;
10660322191eSDong Aisheng 	writel(reg, host->ioaddr + ESDHC_MIX_CTRL);
10670322191eSDong Aisheng }
10680322191eSDong Aisheng 
10690322191eSDong Aisheng static int esdhc_executing_tuning(struct sdhci_host *host, u32 opcode)
10700322191eSDong Aisheng {
10710322191eSDong Aisheng 	int min, max, avg, ret;
10720322191eSDong Aisheng 
10730322191eSDong Aisheng 	/* find the mininum delay first which can pass tuning */
10740322191eSDong Aisheng 	min = ESDHC_TUNE_CTRL_MIN;
10750322191eSDong Aisheng 	while (min < ESDHC_TUNE_CTRL_MAX) {
10760322191eSDong Aisheng 		esdhc_prepare_tuning(host, min);
10779979dbe5SChaotian Jing 		if (!mmc_send_tuning(host->mmc, opcode, NULL))
10780322191eSDong Aisheng 			break;
10790322191eSDong Aisheng 		min += ESDHC_TUNE_CTRL_STEP;
10800322191eSDong Aisheng 	}
10810322191eSDong Aisheng 
10820322191eSDong Aisheng 	/* find the maxinum delay which can not pass tuning */
10830322191eSDong Aisheng 	max = min + ESDHC_TUNE_CTRL_STEP;
10840322191eSDong Aisheng 	while (max < ESDHC_TUNE_CTRL_MAX) {
10850322191eSDong Aisheng 		esdhc_prepare_tuning(host, max);
10869979dbe5SChaotian Jing 		if (mmc_send_tuning(host->mmc, opcode, NULL)) {
10870322191eSDong Aisheng 			max -= ESDHC_TUNE_CTRL_STEP;
10880322191eSDong Aisheng 			break;
10890322191eSDong Aisheng 		}
10900322191eSDong Aisheng 		max += ESDHC_TUNE_CTRL_STEP;
10910322191eSDong Aisheng 	}
10920322191eSDong Aisheng 
10930322191eSDong Aisheng 	/* use average delay to get the best timing */
10940322191eSDong Aisheng 	avg = (min + max) / 2;
10950322191eSDong Aisheng 	esdhc_prepare_tuning(host, avg);
10969979dbe5SChaotian Jing 	ret = mmc_send_tuning(host->mmc, opcode, NULL);
10970322191eSDong Aisheng 	esdhc_post_tuning(host);
10980322191eSDong Aisheng 
1099d04f8d5bSBenoît Thébaudeau 	dev_dbg(mmc_dev(host->mmc), "tuning %s at 0x%x ret %d\n",
11000322191eSDong Aisheng 		ret ? "failed" : "passed", avg, ret);
11010322191eSDong Aisheng 
11020322191eSDong Aisheng 	return ret;
11030322191eSDong Aisheng }
11040322191eSDong Aisheng 
1105029e2476SBOUGH CHEN static void esdhc_hs400_enhanced_strobe(struct mmc_host *mmc, struct mmc_ios *ios)
1106029e2476SBOUGH CHEN {
1107029e2476SBOUGH CHEN 	struct sdhci_host *host = mmc_priv(mmc);
1108029e2476SBOUGH CHEN 	u32 m;
1109029e2476SBOUGH CHEN 
1110029e2476SBOUGH CHEN 	m = readl(host->ioaddr + ESDHC_MIX_CTRL);
1111029e2476SBOUGH CHEN 	if (ios->enhanced_strobe)
1112029e2476SBOUGH CHEN 		m |= ESDHC_MIX_CTRL_HS400_ES_EN;
1113029e2476SBOUGH CHEN 	else
1114029e2476SBOUGH CHEN 		m &= ~ESDHC_MIX_CTRL_HS400_ES_EN;
1115029e2476SBOUGH CHEN 	writel(m, host->ioaddr + ESDHC_MIX_CTRL);
1116029e2476SBOUGH CHEN }
1117029e2476SBOUGH CHEN 
1118ad93220dSDong Aisheng static int esdhc_change_pinstate(struct sdhci_host *host,
1119ad93220dSDong Aisheng 						unsigned int uhs)
1120ad93220dSDong Aisheng {
1121ad93220dSDong Aisheng 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1122070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
1123ad93220dSDong Aisheng 	struct pinctrl_state *pinctrl;
1124ad93220dSDong Aisheng 
1125ad93220dSDong Aisheng 	dev_dbg(mmc_dev(host->mmc), "change pinctrl state for uhs %d\n", uhs);
1126ad93220dSDong Aisheng 
1127ad93220dSDong Aisheng 	if (IS_ERR(imx_data->pinctrl) ||
1128ad93220dSDong Aisheng 		IS_ERR(imx_data->pins_100mhz) ||
1129ad93220dSDong Aisheng 		IS_ERR(imx_data->pins_200mhz))
1130ad93220dSDong Aisheng 		return -EINVAL;
1131ad93220dSDong Aisheng 
1132ad93220dSDong Aisheng 	switch (uhs) {
1133ad93220dSDong Aisheng 	case MMC_TIMING_UHS_SDR50:
11349f327845SHaibo Chen 	case MMC_TIMING_UHS_DDR50:
1135ad93220dSDong Aisheng 		pinctrl = imx_data->pins_100mhz;
1136ad93220dSDong Aisheng 		break;
1137ad93220dSDong Aisheng 	case MMC_TIMING_UHS_SDR104:
1138429a5b45SDong Aisheng 	case MMC_TIMING_MMC_HS200:
113928b07674SHaibo Chen 	case MMC_TIMING_MMC_HS400:
1140ad93220dSDong Aisheng 		pinctrl = imx_data->pins_200mhz;
1141ad93220dSDong Aisheng 		break;
1142ad93220dSDong Aisheng 	default:
1143ad93220dSDong Aisheng 		/* back to default state for other legacy timing */
11442480b720SUlf Hansson 		return pinctrl_select_default_state(mmc_dev(host->mmc));
1145ad93220dSDong Aisheng 	}
1146ad93220dSDong Aisheng 
1147ad93220dSDong Aisheng 	return pinctrl_select_state(imx_data->pinctrl, pinctrl);
1148ad93220dSDong Aisheng }
1149ad93220dSDong Aisheng 
115028b07674SHaibo Chen /*
1151d04f8d5bSBenoît Thébaudeau  * For HS400 eMMC, there is a data_strobe line. This signal is generated
115228b07674SHaibo Chen  * by the device and used for data output and CRC status response output
115328b07674SHaibo Chen  * in HS400 mode. The frequency of this signal follows the frequency of
1154d04f8d5bSBenoît Thébaudeau  * CLK generated by host. The host receives the data which is aligned to the
115528b07674SHaibo Chen  * edge of data_strobe line. Due to the time delay between CLK line and
115628b07674SHaibo Chen  * data_strobe line, if the delay time is larger than one clock cycle,
1157d04f8d5bSBenoît Thébaudeau  * then CLK and data_strobe line will be misaligned, read error shows up.
115828b07674SHaibo Chen  */
115928b07674SHaibo Chen static void esdhc_set_strobe_dll(struct sdhci_host *host)
116028b07674SHaibo Chen {
11615bd2acdcSHaibo Chen 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
11625bd2acdcSHaibo Chen 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
11635bd2acdcSHaibo Chen 	u32 strobe_delay;
116428b07674SHaibo Chen 	u32 v;
1165373e800bSHaibo Chen 	int ret;
116628b07674SHaibo Chen 
11677ac6da26SDong Aisheng 	/* disable clock before enabling strobe dll */
11687ac6da26SDong Aisheng 	writel(readl(host->ioaddr + ESDHC_VENDOR_SPEC) &
11697ac6da26SDong Aisheng 		~ESDHC_VENDOR_SPEC_FRC_SDCLK_ON,
11707ac6da26SDong Aisheng 		host->ioaddr + ESDHC_VENDOR_SPEC);
1171f581e909SHaibo Chen 	esdhc_wait_for_card_clock_gate_off(host);
11727ac6da26SDong Aisheng 
117328b07674SHaibo Chen 	/* force a reset on strobe dll */
117428b07674SHaibo Chen 	writel(ESDHC_STROBE_DLL_CTRL_RESET,
117528b07674SHaibo Chen 		host->ioaddr + ESDHC_STROBE_DLL_CTRL);
11762eaf5a53SBOUGH CHEN 	/* clear the reset bit on strobe dll before any setting */
11772eaf5a53SBOUGH CHEN 	writel(0, host->ioaddr + ESDHC_STROBE_DLL_CTRL);
11782eaf5a53SBOUGH CHEN 
117928b07674SHaibo Chen 	/*
118028b07674SHaibo Chen 	 * enable strobe dll ctrl and adjust the delay target
118128b07674SHaibo Chen 	 * for the uSDHC loopback read clock
118228b07674SHaibo Chen 	 */
11835bd2acdcSHaibo Chen 	if (imx_data->boarddata.strobe_dll_delay_target)
11845bd2acdcSHaibo Chen 		strobe_delay = imx_data->boarddata.strobe_dll_delay_target;
11855bd2acdcSHaibo Chen 	else
11865bd2acdcSHaibo Chen 		strobe_delay = ESDHC_STROBE_DLL_CTRL_SLV_DLY_TARGET_DEFAULT;
118728b07674SHaibo Chen 	v = ESDHC_STROBE_DLL_CTRL_ENABLE |
11882eaf5a53SBOUGH CHEN 		ESDHC_STROBE_DLL_CTRL_SLV_UPDATE_INT_DEFAULT |
11895bd2acdcSHaibo Chen 		(strobe_delay << ESDHC_STROBE_DLL_CTRL_SLV_DLY_TARGET_SHIFT);
119028b07674SHaibo Chen 	writel(v, host->ioaddr + ESDHC_STROBE_DLL_CTRL);
1191373e800bSHaibo Chen 
1192373e800bSHaibo Chen 	/* wait max 50us to get the REF/SLV lock */
1193373e800bSHaibo Chen 	ret = readl_poll_timeout(host->ioaddr + ESDHC_STROBE_DLL_STATUS, v,
1194373e800bSHaibo Chen 		((v & ESDHC_STROBE_DLL_STS_REF_LOCK) && (v & ESDHC_STROBE_DLL_STS_SLV_LOCK)), 1, 50);
1195373e800bSHaibo Chen 	if (ret == -ETIMEDOUT)
119628b07674SHaibo Chen 		dev_warn(mmc_dev(host->mmc),
1197373e800bSHaibo Chen 		"warning! HS400 strobe DLL status REF/SLV not lock in 50us, STROBE DLL status is %x!\n", v);
119828b07674SHaibo Chen }
119928b07674SHaibo Chen 
1200d9370424SHaibo Chen static void esdhc_reset_tuning(struct sdhci_host *host)
1201d9370424SHaibo Chen {
1202d9370424SHaibo Chen 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1203d9370424SHaibo Chen 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
1204d9370424SHaibo Chen 	u32 ctrl;
12059af372dcSHaibo Chen 	int ret;
1206d9370424SHaibo Chen 
1207d04f8d5bSBenoît Thébaudeau 	/* Reset the tuning circuit */
1208d9370424SHaibo Chen 	if (esdhc_is_usdhc(imx_data)) {
1209d9370424SHaibo Chen 		if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING) {
1210d9370424SHaibo Chen 			ctrl = readl(host->ioaddr + ESDHC_MIX_CTRL);
1211d9370424SHaibo Chen 			ctrl &= ~ESDHC_MIX_CTRL_SMPCLK_SEL;
1212d9370424SHaibo Chen 			ctrl &= ~ESDHC_MIX_CTRL_FBCLK_SEL;
1213d9370424SHaibo Chen 			writel(ctrl, host->ioaddr + ESDHC_MIX_CTRL);
1214d9370424SHaibo Chen 			writel(0, host->ioaddr + ESDHC_TUNE_CTRL_STATUS);
1215d9370424SHaibo Chen 		} else if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) {
1216869f8a69SAdrian Hunter 			ctrl = readl(host->ioaddr + SDHCI_AUTO_CMD_STATUS);
1217d9370424SHaibo Chen 			ctrl &= ~ESDHC_MIX_CTRL_SMPCLK_SEL;
12189af372dcSHaibo Chen 			ctrl &= ~ESDHC_MIX_CTRL_EXE_TUNE;
1219869f8a69SAdrian Hunter 			writel(ctrl, host->ioaddr + SDHCI_AUTO_CMD_STATUS);
12209af372dcSHaibo Chen 			/* Make sure ESDHC_MIX_CTRL_EXE_TUNE cleared */
12219af372dcSHaibo Chen 			ret = readl_poll_timeout(host->ioaddr + SDHCI_AUTO_CMD_STATUS,
12229af372dcSHaibo Chen 				ctrl, !(ctrl & ESDHC_MIX_CTRL_EXE_TUNE), 1, 50);
12239af372dcSHaibo Chen 			if (ret == -ETIMEDOUT)
12249af372dcSHaibo Chen 				dev_warn(mmc_dev(host->mmc),
12259af372dcSHaibo Chen 				 "Warning! clear execute tuning bit failed\n");
12269af372dcSHaibo Chen 			/*
12279af372dcSHaibo Chen 			 * SDHCI_INT_DATA_AVAIL is W1C bit, set this bit will clear the
12289af372dcSHaibo Chen 			 * usdhc IP internal logic flag execute_tuning_with_clr_buf, which
12299af372dcSHaibo Chen 			 * will finally make sure the normal data transfer logic correct.
12309af372dcSHaibo Chen 			 */
12319af372dcSHaibo Chen 			ctrl = readl(host->ioaddr + SDHCI_INT_STATUS);
12329af372dcSHaibo Chen 			ctrl |= SDHCI_INT_DATA_AVAIL;
12339af372dcSHaibo Chen 			writel(ctrl, host->ioaddr + SDHCI_INT_STATUS);
1234d9370424SHaibo Chen 		}
1235d9370424SHaibo Chen 	}
1236d9370424SHaibo Chen }
1237d9370424SHaibo Chen 
1238850a29b8SRussell King static void esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned timing)
1239ad93220dSDong Aisheng {
124028b07674SHaibo Chen 	u32 m;
1241ad93220dSDong Aisheng 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1242070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
1243602519b2SDong Aisheng 	struct esdhc_platform_data *boarddata = &imx_data->boarddata;
1244ad93220dSDong Aisheng 
124528b07674SHaibo Chen 	/* disable ddr mode and disable HS400 mode */
124628b07674SHaibo Chen 	m = readl(host->ioaddr + ESDHC_MIX_CTRL);
124728b07674SHaibo Chen 	m &= ~(ESDHC_MIX_CTRL_DDREN | ESDHC_MIX_CTRL_HS400_EN);
124828b07674SHaibo Chen 	imx_data->is_ddr = 0;
124928b07674SHaibo Chen 
1250850a29b8SRussell King 	switch (timing) {
1251ad93220dSDong Aisheng 	case MMC_TIMING_UHS_SDR12:
1252ad93220dSDong Aisheng 	case MMC_TIMING_UHS_SDR25:
1253ad93220dSDong Aisheng 	case MMC_TIMING_UHS_SDR50:
1254ad93220dSDong Aisheng 	case MMC_TIMING_UHS_SDR104:
1255de0a0decSBOUGH CHEN 	case MMC_TIMING_MMC_HS:
1256429a5b45SDong Aisheng 	case MMC_TIMING_MMC_HS200:
125728b07674SHaibo Chen 		writel(m, host->ioaddr + ESDHC_MIX_CTRL);
1258ad93220dSDong Aisheng 		break;
1259ad93220dSDong Aisheng 	case MMC_TIMING_UHS_DDR50:
126069f5bf38SAisheng Dong 	case MMC_TIMING_MMC_DDR52:
126128b07674SHaibo Chen 		m |= ESDHC_MIX_CTRL_DDREN;
126228b07674SHaibo Chen 		writel(m, host->ioaddr + ESDHC_MIX_CTRL);
1263de5bdbffSDong Aisheng 		imx_data->is_ddr = 1;
1264602519b2SDong Aisheng 		if (boarddata->delay_line) {
1265602519b2SDong Aisheng 			u32 v;
1266602519b2SDong Aisheng 			v = boarddata->delay_line <<
1267602519b2SDong Aisheng 				ESDHC_DLL_OVERRIDE_VAL_SHIFT |
1268602519b2SDong Aisheng 				(1 << ESDHC_DLL_OVERRIDE_EN_SHIFT);
1269602519b2SDong Aisheng 			if (is_imx53_esdhc(imx_data))
1270602519b2SDong Aisheng 				v <<= 1;
1271602519b2SDong Aisheng 			writel(v, host->ioaddr + ESDHC_DLL_CTRL);
1272602519b2SDong Aisheng 		}
1273ad93220dSDong Aisheng 		break;
127428b07674SHaibo Chen 	case MMC_TIMING_MMC_HS400:
127528b07674SHaibo Chen 		m |= ESDHC_MIX_CTRL_DDREN | ESDHC_MIX_CTRL_HS400_EN;
127628b07674SHaibo Chen 		writel(m, host->ioaddr + ESDHC_MIX_CTRL);
127728b07674SHaibo Chen 		imx_data->is_ddr = 1;
12787ac6da26SDong Aisheng 		/* update clock after enable DDR for strobe DLL lock */
12797ac6da26SDong Aisheng 		host->ops->set_clock(host, host->clock);
128028b07674SHaibo Chen 		esdhc_set_strobe_dll(host);
128128b07674SHaibo Chen 		break;
1282d9370424SHaibo Chen 	case MMC_TIMING_LEGACY:
1283d9370424SHaibo Chen 	default:
1284d9370424SHaibo Chen 		esdhc_reset_tuning(host);
1285d9370424SHaibo Chen 		break;
1286ad93220dSDong Aisheng 	}
1287ad93220dSDong Aisheng 
1288850a29b8SRussell King 	esdhc_change_pinstate(host, timing);
1289ad93220dSDong Aisheng }
1290ad93220dSDong Aisheng 
12910718e59aSRussell King static void esdhc_reset(struct sdhci_host *host, u8 mask)
12920718e59aSRussell King {
1293fb1dec44SBrian Norris 	sdhci_and_cqhci_reset(host, mask);
12940718e59aSRussell King 
12950718e59aSRussell King 	sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
12960718e59aSRussell King 	sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
12970718e59aSRussell King }
12980718e59aSRussell King 
129910fd0ad9SAisheng Dong static unsigned int esdhc_get_max_timeout_count(struct sdhci_host *host)
130010fd0ad9SAisheng Dong {
130110fd0ad9SAisheng Dong 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1302070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
130310fd0ad9SAisheng Dong 
1304d04f8d5bSBenoît Thébaudeau 	/* Doc Erratum: the uSDHC actual maximum timeout count is 1 << 29 */
13052fb0b02bSHaibo Chen 	return esdhc_is_usdhc(imx_data) ? 1 << 29 : 1 << 27;
130610fd0ad9SAisheng Dong }
130710fd0ad9SAisheng Dong 
1308e33eb8e2SAisheng Dong static void esdhc_set_timeout(struct sdhci_host *host, struct mmc_command *cmd)
1309e33eb8e2SAisheng Dong {
1310e33eb8e2SAisheng Dong 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1311070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
1312e33eb8e2SAisheng Dong 
1313e33eb8e2SAisheng Dong 	/* use maximum timeout counter */
1314a215186dSHaibo Chen 	esdhc_clrset_le(host, ESDHC_SYS_CTRL_DTOCV_MASK,
1315a215186dSHaibo Chen 			esdhc_is_usdhc(imx_data) ? 0xF : 0xE,
1316e33eb8e2SAisheng Dong 			SDHCI_TIMEOUT_CONTROL);
1317e33eb8e2SAisheng Dong }
1318e33eb8e2SAisheng Dong 
1319bb6e3581SBOUGH CHEN static u32 esdhc_cqhci_irq(struct sdhci_host *host, u32 intmask)
1320bb6e3581SBOUGH CHEN {
1321bb6e3581SBOUGH CHEN 	int cmd_error = 0;
1322bb6e3581SBOUGH CHEN 	int data_error = 0;
1323bb6e3581SBOUGH CHEN 
1324bb6e3581SBOUGH CHEN 	if (!sdhci_cqe_irq(host, intmask, &cmd_error, &data_error))
1325bb6e3581SBOUGH CHEN 		return intmask;
1326bb6e3581SBOUGH CHEN 
1327bb6e3581SBOUGH CHEN 	cqhci_irq(host->mmc, intmask, cmd_error, data_error);
1328bb6e3581SBOUGH CHEN 
1329bb6e3581SBOUGH CHEN 	return 0;
1330bb6e3581SBOUGH CHEN }
1331bb6e3581SBOUGH CHEN 
13326e9fd28eSDong Aisheng static struct sdhci_ops sdhci_esdhc_ops = {
1333e149860dSRichard Zhu 	.read_l = esdhc_readl_le,
13340c6d49ceSWolfram Sang 	.read_w = esdhc_readw_le,
133577da3da0SAaron Brice 	.read_b = esdhc_readb_le,
1336e149860dSRichard Zhu 	.write_l = esdhc_writel_le,
13370c6d49ceSWolfram Sang 	.write_w = esdhc_writew_le,
13380c6d49ceSWolfram Sang 	.write_b = esdhc_writeb_le,
13398ba9580aSLucas Stach 	.set_clock = esdhc_pltfm_set_clock,
13400ddf03c9SLucas Stach 	.get_max_clock = esdhc_pltfm_get_max_clock,
13410c6d49ceSWolfram Sang 	.get_min_clock = esdhc_pltfm_get_min_clock,
134210fd0ad9SAisheng Dong 	.get_max_timeout_count = esdhc_get_max_timeout_count,
1343913413c3SShawn Guo 	.get_ro = esdhc_pltfm_get_ro,
1344e33eb8e2SAisheng Dong 	.set_timeout = esdhc_set_timeout,
13452317f56cSRussell King 	.set_bus_width = esdhc_pltfm_set_bus_width,
1346ad93220dSDong Aisheng 	.set_uhs_signaling = esdhc_set_uhs_signaling,
13470718e59aSRussell King 	.reset = esdhc_reset,
1348bb6e3581SBOUGH CHEN 	.irq = esdhc_cqhci_irq,
13493722c74cSHaibo Chen 	.dump_vendor_regs = esdhc_dump_debug_regs,
13500c6d49ceSWolfram Sang };
13510c6d49ceSWolfram Sang 
13521db5eebfSLars-Peter Clausen static const struct sdhci_pltfm_data sdhci_esdhc_imx_pdata = {
135397e4ba6aSRichard Zhu 	.quirks = ESDHC_DEFAULT_QUIRKS | SDHCI_QUIRK_NO_HISPD_BIT
135497e4ba6aSRichard Zhu 			| SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC
135597e4ba6aSRichard Zhu 			| SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC
135685d6509dSShawn Guo 			| SDHCI_QUIRK_BROKEN_CARD_DETECTION,
135785d6509dSShawn Guo 	.ops = &sdhci_esdhc_ops,
135885d6509dSShawn Guo };
135985d6509dSShawn Guo 
1360f3f5cf3dSDong Aisheng static void sdhci_esdhc_imx_hwinit(struct sdhci_host *host)
1361f3f5cf3dSDong Aisheng {
1362f3f5cf3dSDong Aisheng 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1363f3f5cf3dSDong Aisheng 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
1364982cf37dSHaibo Chen 	struct cqhci_host *cq_host = host->mmc->cqe_private;
13652b16cf32SDong Aisheng 	int tmp;
1366f3f5cf3dSDong Aisheng 
1367f3f5cf3dSDong Aisheng 	if (esdhc_is_usdhc(imx_data)) {
1368f3f5cf3dSDong Aisheng 		/*
1369f3f5cf3dSDong Aisheng 		 * The imx6q ROM code will change the default watermark
1370f3f5cf3dSDong Aisheng 		 * level setting to something insane.  Change it back here.
1371f3f5cf3dSDong Aisheng 		 */
1372f3f5cf3dSDong Aisheng 		writel(ESDHC_WTMK_DEFAULT_VAL, host->ioaddr + ESDHC_WTMK_LVL);
1373f3f5cf3dSDong Aisheng 
1374f3f5cf3dSDong Aisheng 		/*
1375f3f5cf3dSDong Aisheng 		 * ROM code will change the bit burst_length_enable setting
1376d04f8d5bSBenoît Thébaudeau 		 * to zero if this usdhc is chosen to boot system. Change
1377f3f5cf3dSDong Aisheng 		 * it back here, otherwise it will impact the performance a
1378f3f5cf3dSDong Aisheng 		 * lot. This bit is used to enable/disable the burst length
1379d04f8d5bSBenoît Thébaudeau 		 * for the external AHB2AXI bridge. It's useful especially
1380f3f5cf3dSDong Aisheng 		 * for INCR transfer because without burst length indicator,
1381f3f5cf3dSDong Aisheng 		 * the AHB2AXI bridge does not know the burst length in
1382f3f5cf3dSDong Aisheng 		 * advance. And without burst length indicator, AHB INCR
1383f3f5cf3dSDong Aisheng 		 * transfer can only be converted to singles on the AXI side.
1384f3f5cf3dSDong Aisheng 		 */
1385f3f5cf3dSDong Aisheng 		writel(readl(host->ioaddr + SDHCI_HOST_CONTROL)
1386f3f5cf3dSDong Aisheng 			| ESDHC_BURST_LEN_EN_INCR,
1387f3f5cf3dSDong Aisheng 			host->ioaddr + SDHCI_HOST_CONTROL);
1388e30be063SBOUGH CHEN 
1389f3f5cf3dSDong Aisheng 		/*
1390d04f8d5bSBenoît Thébaudeau 		 * erratum ESDHC_FLAG_ERR004536 fix for MX6Q TO1.2 and MX6DL
1391f3f5cf3dSDong Aisheng 		 * TO1.1, it's harmless for MX6SL
1392f3f5cf3dSDong Aisheng 		 */
13935c4f0062SChester Lin 		if (!(imx_data->socdata->flags & ESDHC_FLAG_SKIP_ERR004536)) {
1394e30be063SBOUGH CHEN 			writel(readl(host->ioaddr + 0x6c) & ~BIT(7),
1395f3f5cf3dSDong Aisheng 				host->ioaddr + 0x6c);
13965c4f0062SChester Lin 		}
1397f3f5cf3dSDong Aisheng 
1398f3f5cf3dSDong Aisheng 		/* disable DLL_CTRL delay line settings */
1399f3f5cf3dSDong Aisheng 		writel(0x0, host->ioaddr + ESDHC_DLL_CTRL);
14002b16cf32SDong Aisheng 
1401bcdb5301SBOUGH CHEN 		/*
1402bcdb5301SBOUGH CHEN 		 * For the case of command with busy, if set the bit
1403bcdb5301SBOUGH CHEN 		 * ESDHC_VEND_SPEC2_EN_BUSY_IRQ, USDHC will generate a
1404bcdb5301SBOUGH CHEN 		 * transfer complete interrupt when busy is deasserted.
1405bcdb5301SBOUGH CHEN 		 * When CQHCI use DCMD to send a CMD need R1b respons,
1406bcdb5301SBOUGH CHEN 		 * CQHCI require to set ESDHC_VEND_SPEC2_EN_BUSY_IRQ,
1407bcdb5301SBOUGH CHEN 		 * otherwise DCMD will always meet timeout waiting for
1408bcdb5301SBOUGH CHEN 		 * hardware interrupt issue.
1409bcdb5301SBOUGH CHEN 		 */
1410bcdb5301SBOUGH CHEN 		if (imx_data->socdata->flags & ESDHC_FLAG_CQHCI) {
1411bcdb5301SBOUGH CHEN 			tmp = readl(host->ioaddr + ESDHC_VEND_SPEC2);
1412bcdb5301SBOUGH CHEN 			tmp |= ESDHC_VEND_SPEC2_EN_BUSY_IRQ;
1413bcdb5301SBOUGH CHEN 			writel(tmp, host->ioaddr + ESDHC_VEND_SPEC2);
1414bcdb5301SBOUGH CHEN 
1415bcdb5301SBOUGH CHEN 			host->quirks &= ~SDHCI_QUIRK_NO_BUSY_IRQ;
1416bcdb5301SBOUGH CHEN 		}
1417bcdb5301SBOUGH CHEN 
14182b16cf32SDong Aisheng 		if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) {
14192b16cf32SDong Aisheng 			tmp = readl(host->ioaddr + ESDHC_TUNING_CTRL);
14202b16cf32SDong Aisheng 			tmp |= ESDHC_STD_TUNING_EN |
14212b16cf32SDong Aisheng 				ESDHC_TUNING_START_TAP_DEFAULT;
14222b16cf32SDong Aisheng 			if (imx_data->boarddata.tuning_start_tap) {
14232b16cf32SDong Aisheng 				tmp &= ~ESDHC_TUNING_START_TAP_MASK;
14242b16cf32SDong Aisheng 				tmp |= imx_data->boarddata.tuning_start_tap;
14252b16cf32SDong Aisheng 			}
14262b16cf32SDong Aisheng 
14272b16cf32SDong Aisheng 			if (imx_data->boarddata.tuning_step) {
14282b16cf32SDong Aisheng 				tmp &= ~ESDHC_TUNING_STEP_MASK;
14292b16cf32SDong Aisheng 				tmp |= imx_data->boarddata.tuning_step
14302b16cf32SDong Aisheng 					<< ESDHC_TUNING_STEP_SHIFT;
14312b16cf32SDong Aisheng 			}
143216e40e5bSHaibo Chen 
143316e40e5bSHaibo Chen 			/* Disable the CMD CRC check for tuning, if not, need to
143416e40e5bSHaibo Chen 			 * add some delay after every tuning command, because
143516e40e5bSHaibo Chen 			 * hardware standard tuning logic will directly go to next
143616e40e5bSHaibo Chen 			 * step once it detect the CMD CRC error, will not wait for
143716e40e5bSHaibo Chen 			 * the card side to finally send out the tuning data, trigger
143816e40e5bSHaibo Chen 			 * the buffer read ready interrupt immediately. If usdhc send
143916e40e5bSHaibo Chen 			 * the next tuning command some eMMC card will stuck, can't
144016e40e5bSHaibo Chen 			 * response, block the tuning procedure or the first command
144116e40e5bSHaibo Chen 			 * after the whole tuning procedure always can't get any response.
144216e40e5bSHaibo Chen 			 */
144316e40e5bSHaibo Chen 			tmp |= ESDHC_TUNING_CMD_CRC_CHECK_DISABLE;
14442b16cf32SDong Aisheng 			writel(tmp, host->ioaddr + ESDHC_TUNING_CTRL);
1445a98c557eSBOUGH CHEN 		} else if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING) {
1446a98c557eSBOUGH CHEN 			/*
1447a98c557eSBOUGH CHEN 			 * ESDHC_STD_TUNING_EN may be configed in bootloader
1448a98c557eSBOUGH CHEN 			 * or ROM code, so clear this bit here to make sure
1449a98c557eSBOUGH CHEN 			 * the manual tuning can work.
1450a98c557eSBOUGH CHEN 			 */
1451a98c557eSBOUGH CHEN 			tmp = readl(host->ioaddr + ESDHC_TUNING_CTRL);
1452a98c557eSBOUGH CHEN 			tmp &= ~ESDHC_STD_TUNING_EN;
1453a98c557eSBOUGH CHEN 			writel(tmp, host->ioaddr + ESDHC_TUNING_CTRL);
14542b16cf32SDong Aisheng 		}
1455982cf37dSHaibo Chen 
1456982cf37dSHaibo Chen 		/*
1457982cf37dSHaibo Chen 		 * On i.MX8MM, we are running Dual Linux OS, with 1st Linux using SD Card
1458982cf37dSHaibo Chen 		 * as rootfs storage, 2nd Linux using eMMC as rootfs storage. We let the
1459982cf37dSHaibo Chen 		 * the 1st linux configure power/clock for the 2nd Linux.
1460982cf37dSHaibo Chen 		 *
1461982cf37dSHaibo Chen 		 * When the 2nd Linux is booting into rootfs stage, we let the 1st Linux
1462982cf37dSHaibo Chen 		 * to destroy the 2nd linux, then restart the 2nd linux, we met SDHCI dump.
1463982cf37dSHaibo Chen 		 * After we clear the pending interrupt and halt CQCTL, issue gone.
1464982cf37dSHaibo Chen 		 */
1465982cf37dSHaibo Chen 		if (cq_host) {
1466982cf37dSHaibo Chen 			tmp = cqhci_readl(cq_host, CQHCI_IS);
1467982cf37dSHaibo Chen 			cqhci_writel(cq_host, tmp, CQHCI_IS);
1468982cf37dSHaibo Chen 			cqhci_writel(cq_host, CQHCI_HALT, CQHCI_CTL);
1469982cf37dSHaibo Chen 		}
1470f3f5cf3dSDong Aisheng 	}
1471f3f5cf3dSDong Aisheng }
1472f3f5cf3dSDong Aisheng 
1473bb6e3581SBOUGH CHEN static void esdhc_cqe_enable(struct mmc_host *mmc)
1474bb6e3581SBOUGH CHEN {
1475bb6e3581SBOUGH CHEN 	struct sdhci_host *host = mmc_priv(mmc);
147685236d2bSBOUGH CHEN 	struct cqhci_host *cq_host = mmc->cqe_private;
1477bb6e3581SBOUGH CHEN 	u32 reg;
1478bb6e3581SBOUGH CHEN 	u16 mode;
1479bb6e3581SBOUGH CHEN 	int count = 10;
1480bb6e3581SBOUGH CHEN 
1481bb6e3581SBOUGH CHEN 	/*
1482bb6e3581SBOUGH CHEN 	 * CQE gets stuck if it sees Buffer Read Enable bit set, which can be
1483bb6e3581SBOUGH CHEN 	 * the case after tuning, so ensure the buffer is drained.
1484bb6e3581SBOUGH CHEN 	 */
1485bb6e3581SBOUGH CHEN 	reg = sdhci_readl(host, SDHCI_PRESENT_STATE);
1486bb6e3581SBOUGH CHEN 	while (reg & SDHCI_DATA_AVAILABLE) {
1487bb6e3581SBOUGH CHEN 		sdhci_readl(host, SDHCI_BUFFER);
1488bb6e3581SBOUGH CHEN 		reg = sdhci_readl(host, SDHCI_PRESENT_STATE);
1489bb6e3581SBOUGH CHEN 		if (count-- == 0) {
1490bb6e3581SBOUGH CHEN 			dev_warn(mmc_dev(host->mmc),
1491bb6e3581SBOUGH CHEN 				"CQE may get stuck because the Buffer Read Enable bit is set\n");
1492bb6e3581SBOUGH CHEN 			break;
1493bb6e3581SBOUGH CHEN 		}
1494bb6e3581SBOUGH CHEN 		mdelay(1);
1495bb6e3581SBOUGH CHEN 	}
1496bb6e3581SBOUGH CHEN 
1497bb6e3581SBOUGH CHEN 	/*
1498bb6e3581SBOUGH CHEN 	 * Runtime resume will reset the entire host controller, which
1499bb6e3581SBOUGH CHEN 	 * will also clear the DMAEN/BCEN of register ESDHC_MIX_CTRL.
1500bb6e3581SBOUGH CHEN 	 * Here set DMAEN and BCEN when enable CMDQ.
1501bb6e3581SBOUGH CHEN 	 */
1502bb6e3581SBOUGH CHEN 	mode = sdhci_readw(host, SDHCI_TRANSFER_MODE);
1503bb6e3581SBOUGH CHEN 	if (host->flags & SDHCI_REQ_USE_DMA)
1504bb6e3581SBOUGH CHEN 		mode |= SDHCI_TRNS_DMA;
1505bb6e3581SBOUGH CHEN 	if (!(host->quirks2 & SDHCI_QUIRK2_SUPPORT_SINGLE))
1506bb6e3581SBOUGH CHEN 		mode |= SDHCI_TRNS_BLK_CNT_EN;
1507bb6e3581SBOUGH CHEN 	sdhci_writew(host, mode, SDHCI_TRANSFER_MODE);
1508bb6e3581SBOUGH CHEN 
150985236d2bSBOUGH CHEN 	/*
151085236d2bSBOUGH CHEN 	 * Though Runtime resume reset the entire host controller,
151185236d2bSBOUGH CHEN 	 * but do not impact the CQHCI side, need to clear the
151285236d2bSBOUGH CHEN 	 * HALT bit, avoid CQHCI stuck in the first request when
151385236d2bSBOUGH CHEN 	 * system resume back.
151485236d2bSBOUGH CHEN 	 */
151585236d2bSBOUGH CHEN 	cqhci_writel(cq_host, 0, CQHCI_CTL);
1516a3cab1d2SSebastian Falbesoner 	if (cqhci_readl(cq_host, CQHCI_CTL) & CQHCI_HALT)
151785236d2bSBOUGH CHEN 		dev_err(mmc_dev(host->mmc),
151885236d2bSBOUGH CHEN 			"failed to exit halt state when enable CQE\n");
151985236d2bSBOUGH CHEN 
152085236d2bSBOUGH CHEN 
1521bb6e3581SBOUGH CHEN 	sdhci_cqe_enable(mmc);
1522bb6e3581SBOUGH CHEN }
1523bb6e3581SBOUGH CHEN 
1524bb6e3581SBOUGH CHEN static void esdhc_sdhci_dumpregs(struct mmc_host *mmc)
1525bb6e3581SBOUGH CHEN {
1526bb6e3581SBOUGH CHEN 	sdhci_dumpregs(mmc_priv(mmc));
1527bb6e3581SBOUGH CHEN }
1528bb6e3581SBOUGH CHEN 
1529bb6e3581SBOUGH CHEN static const struct cqhci_host_ops esdhc_cqhci_ops = {
1530bb6e3581SBOUGH CHEN 	.enable		= esdhc_cqe_enable,
1531bb6e3581SBOUGH CHEN 	.disable	= sdhci_cqe_disable,
1532bb6e3581SBOUGH CHEN 	.dumpregs	= esdhc_sdhci_dumpregs,
1533bb6e3581SBOUGH CHEN };
1534bb6e3581SBOUGH CHEN 
1535c3be1efdSBill Pemberton static int
1536abfafc2dSShawn Guo sdhci_esdhc_imx_probe_dt(struct platform_device *pdev,
153707bf2b54SSascha Hauer 			 struct sdhci_host *host,
153891fa4252SDong Aisheng 			 struct pltfm_imx_data *imx_data)
1539abfafc2dSShawn Guo {
1540abfafc2dSShawn Guo 	struct device_node *np = pdev->dev.of_node;
154191fa4252SDong Aisheng 	struct esdhc_platform_data *boarddata = &imx_data->boarddata;
15424800e87aSDong Aisheng 	int ret;
1543abfafc2dSShawn Guo 
1544abfafc2dSShawn Guo 	if (of_get_property(np, "fsl,wp-controller", NULL))
1545abfafc2dSShawn Guo 		boarddata->wp_type = ESDHC_WP_CONTROLLER;
1546abfafc2dSShawn Guo 
154774ff81e1SLinus Walleij 	/*
154874ff81e1SLinus Walleij 	 * If we have this property, then activate WP check.
154974ff81e1SLinus Walleij 	 * Retrieveing and requesting the actual WP GPIO will happen
155074ff81e1SLinus Walleij 	 * in the call to mmc_of_parse().
155174ff81e1SLinus Walleij 	 */
155274ff81e1SLinus Walleij 	if (of_property_read_bool(np, "wp-gpios"))
1553abfafc2dSShawn Guo 		boarddata->wp_type = ESDHC_WP_GPIO;
1554abfafc2dSShawn Guo 
1555d407e30bSHaibo Chen 	of_property_read_u32(np, "fsl,tuning-step", &boarddata->tuning_step);
1556d87fc966SDong Aisheng 	of_property_read_u32(np, "fsl,tuning-start-tap",
1557d87fc966SDong Aisheng 			     &boarddata->tuning_start_tap);
1558d407e30bSHaibo Chen 
15595bd2acdcSHaibo Chen 	of_property_read_u32(np, "fsl,strobe-dll-delay-target",
15605bd2acdcSHaibo Chen 				&boarddata->strobe_dll_delay_target);
1561ad93220dSDong Aisheng 	if (of_find_property(np, "no-1-8-v", NULL))
156286f495c5SStefan Agner 		host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V;
1563ad93220dSDong Aisheng 
1564602519b2SDong Aisheng 	if (of_property_read_u32(np, "fsl,delay-line", &boarddata->delay_line))
1565602519b2SDong Aisheng 		boarddata->delay_line = 0;
1566602519b2SDong Aisheng 
15676dab809bSAndy Shevchenko 	mmc_of_parse_voltage(host->mmc, &host->ocr_mask);
156807bf2b54SSascha Hauer 
1569f410ee0aSPeng Fan 	if (esdhc_is_usdhc(imx_data) && !IS_ERR(imx_data->pinctrl)) {
157091fa4252SDong Aisheng 		imx_data->pins_100mhz = pinctrl_lookup_state(imx_data->pinctrl,
157191fa4252SDong Aisheng 						ESDHC_PINCTRL_STATE_100MHZ);
157291fa4252SDong Aisheng 		imx_data->pins_200mhz = pinctrl_lookup_state(imx_data->pinctrl,
157391fa4252SDong Aisheng 						ESDHC_PINCTRL_STATE_200MHZ);
157491fa4252SDong Aisheng 	}
157591fa4252SDong Aisheng 
157615064119SFabio Estevam 	/* call to generic mmc_of_parse to support additional capabilities */
15774800e87aSDong Aisheng 	ret = mmc_of_parse(host->mmc);
15784800e87aSDong Aisheng 	if (ret)
15794800e87aSDong Aisheng 		return ret;
15804800e87aSDong Aisheng 
1581287980e4SArnd Bergmann 	if (mmc_gpio_get_cd(host->mmc) >= 0)
15824800e87aSDong Aisheng 		host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION;
15834800e87aSDong Aisheng 
15844800e87aSDong Aisheng 	return 0;
1585abfafc2dSShawn Guo }
1586abfafc2dSShawn Guo 
1587c3be1efdSBill Pemberton static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
158895f25efeSWolfram Sang {
158985d6509dSShawn Guo 	struct sdhci_pltfm_host *pltfm_host;
159085d6509dSShawn Guo 	struct sdhci_host *host;
1591bb6e3581SBOUGH CHEN 	struct cqhci_host *cq_host;
15920c6d49ceSWolfram Sang 	int err;
1593e149860dSRichard Zhu 	struct pltfm_imx_data *imx_data;
159495f25efeSWolfram Sang 
1595070e6d3fSJisheng Zhang 	host = sdhci_pltfm_init(pdev, &sdhci_esdhc_imx_pdata,
1596070e6d3fSJisheng Zhang 				sizeof(*imx_data));
159785d6509dSShawn Guo 	if (IS_ERR(host))
159885d6509dSShawn Guo 		return PTR_ERR(host);
159985d6509dSShawn Guo 
160085d6509dSShawn Guo 	pltfm_host = sdhci_priv(host);
160185d6509dSShawn Guo 
1602070e6d3fSJisheng Zhang 	imx_data = sdhci_pltfm_priv(pltfm_host);
160357ed3314SShawn Guo 
160491b3d2e5SFabio Estevam 	imx_data->socdata = device_get_match_data(&pdev->dev);
160585d6509dSShawn Guo 
16061c4989b0SBOUGH CHEN 	if (imx_data->socdata->flags & ESDHC_FLAG_PMQOS)
1607d1b98305SRafael J. Wysocki 		cpu_latency_qos_add_request(&imx_data->pm_qos_req, 0);
16081c4989b0SBOUGH CHEN 
160952dac615SSascha Hauer 	imx_data->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
161052dac615SSascha Hauer 	if (IS_ERR(imx_data->clk_ipg)) {
161152dac615SSascha Hauer 		err = PTR_ERR(imx_data->clk_ipg);
1612e3af31c6SShawn Guo 		goto free_sdhci;
161395f25efeSWolfram Sang 	}
161452dac615SSascha Hauer 
161552dac615SSascha Hauer 	imx_data->clk_ahb = devm_clk_get(&pdev->dev, "ahb");
161652dac615SSascha Hauer 	if (IS_ERR(imx_data->clk_ahb)) {
161752dac615SSascha Hauer 		err = PTR_ERR(imx_data->clk_ahb);
1618e3af31c6SShawn Guo 		goto free_sdhci;
161952dac615SSascha Hauer 	}
162052dac615SSascha Hauer 
162152dac615SSascha Hauer 	imx_data->clk_per = devm_clk_get(&pdev->dev, "per");
162252dac615SSascha Hauer 	if (IS_ERR(imx_data->clk_per)) {
162352dac615SSascha Hauer 		err = PTR_ERR(imx_data->clk_per);
1624e3af31c6SShawn Guo 		goto free_sdhci;
162552dac615SSascha Hauer 	}
162652dac615SSascha Hauer 
162752dac615SSascha Hauer 	pltfm_host->clk = imx_data->clk_per;
1628a974862fSDong Aisheng 	pltfm_host->clock = clk_get_rate(pltfm_host->clk);
162917b1eb7fSFabio Estevam 	err = clk_prepare_enable(imx_data->clk_per);
163017b1eb7fSFabio Estevam 	if (err)
163117b1eb7fSFabio Estevam 		goto free_sdhci;
163217b1eb7fSFabio Estevam 	err = clk_prepare_enable(imx_data->clk_ipg);
163317b1eb7fSFabio Estevam 	if (err)
163417b1eb7fSFabio Estevam 		goto disable_per_clk;
163517b1eb7fSFabio Estevam 	err = clk_prepare_enable(imx_data->clk_ahb);
163617b1eb7fSFabio Estevam 	if (err)
163717b1eb7fSFabio Estevam 		goto disable_ipg_clk;
163895f25efeSWolfram Sang 
1639ad93220dSDong Aisheng 	imx_data->pinctrl = devm_pinctrl_get(&pdev->dev);
16409e70ff99SHaibo Chen 	if (IS_ERR(imx_data->pinctrl))
1641b62eee9fSHaibo Chen 		dev_warn(mmc_dev(host->mmc), "could not get pinctrl\n");
1642e62d8b8fSDong Aisheng 
164369ed60e0SDong Aisheng 	if (esdhc_is_usdhc(imx_data)) {
164469ed60e0SDong Aisheng 		host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN;
164509c8192bSStefan Agner 		host->mmc->caps |= MMC_CAP_1_8V_DDR | MMC_CAP_3_3V_DDR;
1646f6140462SHaibo Chen 
1647f6140462SHaibo Chen 		/* GPIO CD can be set as a wakeup source */
1648f6140462SHaibo Chen 		host->mmc->caps |= MMC_CAP_CD_WAKE;
1649f6140462SHaibo Chen 
16504245afffSDong Aisheng 		if (!(imx_data->socdata->flags & ESDHC_FLAG_HS200))
16514245afffSDong Aisheng 			host->quirks2 |= SDHCI_QUIRK2_BROKEN_HS200;
1652a75dcbf4SDong Aisheng 
1653a75dcbf4SDong Aisheng 		/* clear tuning bits in case ROM has set it already */
1654a75dcbf4SDong Aisheng 		writel(0x0, host->ioaddr + ESDHC_MIX_CTRL);
1655869f8a69SAdrian Hunter 		writel(0x0, host->ioaddr + SDHCI_AUTO_CMD_STATUS);
1656a75dcbf4SDong Aisheng 		writel(0x0, host->ioaddr + ESDHC_TUNE_CTRL_STATUS);
1657de3e1dd0SBOUGH CHEN 
1658de3e1dd0SBOUGH CHEN 		/*
1659de3e1dd0SBOUGH CHEN 		 * Link usdhc specific mmc_host_ops execute_tuning function,
1660de3e1dd0SBOUGH CHEN 		 * to replace the standard one in sdhci_ops.
1661de3e1dd0SBOUGH CHEN 		 */
1662de3e1dd0SBOUGH CHEN 		host->mmc_host_ops.execute_tuning = usdhc_execute_tuning;
166369ed60e0SDong Aisheng 	}
1664f750ba9bSShawn Guo 
16651ed5c3b2SSascha Hauer 	err = sdhci_esdhc_imx_probe_dt(pdev, host, imx_data);
16661ed5c3b2SSascha Hauer 	if (err)
16671ed5c3b2SSascha Hauer 		goto disable_ahb_clk;
16681ed5c3b2SSascha Hauer 
16696e9fd28eSDong Aisheng 	if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING)
16706e9fd28eSDong Aisheng 		sdhci_esdhc_ops.platform_execute_tuning =
16716e9fd28eSDong Aisheng 					esdhc_executing_tuning;
16728b2bb0adSDong Aisheng 
167318094430SDong Aisheng 	if (imx_data->socdata->flags & ESDHC_FLAG_ERR004536)
167418094430SDong Aisheng 		host->quirks |= SDHCI_QUIRK_BROKEN_ADMA;
167518094430SDong Aisheng 
1676f002f45aSHaibo Chen 	if (host->mmc->caps & MMC_CAP_8_BIT_DATA &&
16771ed5c3b2SSascha Hauer 	    imx_data->socdata->flags & ESDHC_FLAG_HS400)
16782991ad76SLucas Stach 		host->mmc->caps2 |= MMC_CAP2_HS400;
167928b07674SHaibo Chen 
168074898cbcSHaibo Chen 	if (imx_data->socdata->flags & ESDHC_FLAG_BROKEN_AUTO_CMD23)
168174898cbcSHaibo Chen 		host->quirks2 |= SDHCI_QUIRK2_ACMD23_BROKEN;
168274898cbcSHaibo Chen 
1683f002f45aSHaibo Chen 	if (host->mmc->caps & MMC_CAP_8_BIT_DATA &&
16841ed5c3b2SSascha Hauer 	    imx_data->socdata->flags & ESDHC_FLAG_HS400_ES) {
1685029e2476SBOUGH CHEN 		host->mmc->caps2 |= MMC_CAP2_HS400_ES;
1686029e2476SBOUGH CHEN 		host->mmc_host_ops.hs400_enhanced_strobe =
1687029e2476SBOUGH CHEN 					esdhc_hs400_enhanced_strobe;
1688029e2476SBOUGH CHEN 	}
1689029e2476SBOUGH CHEN 
1690bb6e3581SBOUGH CHEN 	if (imx_data->socdata->flags & ESDHC_FLAG_CQHCI) {
1691bcdb5301SBOUGH CHEN 		host->mmc->caps2 |= MMC_CAP2_CQE | MMC_CAP2_CQE_DCMD;
1692bb6e3581SBOUGH CHEN 		cq_host = devm_kzalloc(&pdev->dev, sizeof(*cq_host), GFP_KERNEL);
16939a633f3bSWei Yongjun 		if (!cq_host) {
16949a633f3bSWei Yongjun 			err = -ENOMEM;
1695bb6e3581SBOUGH CHEN 			goto disable_ahb_clk;
1696bb6e3581SBOUGH CHEN 		}
1697bb6e3581SBOUGH CHEN 
1698bb6e3581SBOUGH CHEN 		cq_host->mmio = host->ioaddr + ESDHC_CQHCI_ADDR_OFFSET;
1699bb6e3581SBOUGH CHEN 		cq_host->ops = &esdhc_cqhci_ops;
1700bb6e3581SBOUGH CHEN 
1701bb6e3581SBOUGH CHEN 		err = cqhci_init(cq_host, host->mmc, false);
1702bb6e3581SBOUGH CHEN 		if (err)
1703bb6e3581SBOUGH CHEN 			goto disable_ahb_clk;
1704bb6e3581SBOUGH CHEN 	}
1705bb6e3581SBOUGH CHEN 
1706f3f5cf3dSDong Aisheng 	sdhci_esdhc_imx_hwinit(host);
1707f3f5cf3dSDong Aisheng 
170885d6509dSShawn Guo 	err = sdhci_add_host(host);
170985d6509dSShawn Guo 	if (err)
171017b1eb7fSFabio Estevam 		goto disable_ahb_clk;
171185d6509dSShawn Guo 
1712f62f7bccSHaibo Chen 	/*
1713f62f7bccSHaibo Chen 	 * Setup the wakeup capability here, let user to decide
1714f62f7bccSHaibo Chen 	 * whether need to enable this wakeup through sysfs interface.
1715f62f7bccSHaibo Chen 	 */
1716f62f7bccSHaibo Chen 	if ((host->mmc->pm_caps & MMC_PM_KEEP_POWER) &&
1717f62f7bccSHaibo Chen 			(host->mmc->pm_caps & MMC_PM_WAKE_SDIO_IRQ))
1718f62f7bccSHaibo Chen 		device_set_wakeup_capable(&pdev->dev, true);
1719f62f7bccSHaibo Chen 
172089d7e5c1SDong Aisheng 	pm_runtime_set_active(&pdev->dev);
172189d7e5c1SDong Aisheng 	pm_runtime_set_autosuspend_delay(&pdev->dev, 50);
172289d7e5c1SDong Aisheng 	pm_runtime_use_autosuspend(&pdev->dev);
172389d7e5c1SDong Aisheng 	pm_suspend_ignore_children(&pdev->dev, 1);
172477903c01SUlf Hansson 	pm_runtime_enable(&pdev->dev);
172589d7e5c1SDong Aisheng 
17267e29c306SWolfram Sang 	return 0;
17277e29c306SWolfram Sang 
172817b1eb7fSFabio Estevam disable_ahb_clk:
172952dac615SSascha Hauer 	clk_disable_unprepare(imx_data->clk_ahb);
173017b1eb7fSFabio Estevam disable_ipg_clk:
173117b1eb7fSFabio Estevam 	clk_disable_unprepare(imx_data->clk_ipg);
173217b1eb7fSFabio Estevam disable_per_clk:
173317b1eb7fSFabio Estevam 	clk_disable_unprepare(imx_data->clk_per);
1734e3af31c6SShawn Guo free_sdhci:
17351c4989b0SBOUGH CHEN 	if (imx_data->socdata->flags & ESDHC_FLAG_PMQOS)
1736d1b98305SRafael J. Wysocki 		cpu_latency_qos_remove_request(&imx_data->pm_qos_req);
173785d6509dSShawn Guo 	sdhci_pltfm_free(pdev);
173885d6509dSShawn Guo 	return err;
173995f25efeSWolfram Sang }
174095f25efeSWolfram Sang 
17416e0ee714SBill Pemberton static int sdhci_esdhc_imx_remove(struct platform_device *pdev)
174295f25efeSWolfram Sang {
174385d6509dSShawn Guo 	struct sdhci_host *host = platform_get_drvdata(pdev);
174495f25efeSWolfram Sang 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1745070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
1746a56f4413SFrank Li 	int dead;
174785d6509dSShawn Guo 
17480b414368SUlf Hansson 	pm_runtime_get_sync(&pdev->dev);
1749a56f4413SFrank Li 	dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff);
17500b414368SUlf Hansson 	pm_runtime_disable(&pdev->dev);
17510b414368SUlf Hansson 	pm_runtime_put_noidle(&pdev->dev);
17520b414368SUlf Hansson 
175385d6509dSShawn Guo 	sdhci_remove_host(host, dead);
17540c6d49ceSWolfram Sang 
175552dac615SSascha Hauer 	clk_disable_unprepare(imx_data->clk_per);
175652dac615SSascha Hauer 	clk_disable_unprepare(imx_data->clk_ipg);
175752dac615SSascha Hauer 	clk_disable_unprepare(imx_data->clk_ahb);
175852dac615SSascha Hauer 
17591c4989b0SBOUGH CHEN 	if (imx_data->socdata->flags & ESDHC_FLAG_PMQOS)
1760d1b98305SRafael J. Wysocki 		cpu_latency_qos_remove_request(&imx_data->pm_qos_req);
17611c4989b0SBOUGH CHEN 
176285d6509dSShawn Guo 	sdhci_pltfm_free(pdev);
176385d6509dSShawn Guo 
176485d6509dSShawn Guo 	return 0;
176595f25efeSWolfram Sang }
176695f25efeSWolfram Sang 
17672788ed42SUlf Hansson #ifdef CONFIG_PM_SLEEP
176804143fbaSDong Aisheng static int sdhci_esdhc_suspend(struct device *dev)
176904143fbaSDong Aisheng {
17703e3274abSUlf Hansson 	struct sdhci_host *host = dev_get_drvdata(dev);
1771a26a4f1bSHaibo Chen 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1772a26a4f1bSHaibo Chen 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
1773bb6e3581SBOUGH CHEN 	int ret;
1774bb6e3581SBOUGH CHEN 
1775bb6e3581SBOUGH CHEN 	if (host->mmc->caps2 & MMC_CAP2_CQE) {
1776bb6e3581SBOUGH CHEN 		ret = cqhci_suspend(host->mmc);
1777bb6e3581SBOUGH CHEN 		if (ret)
1778bb6e3581SBOUGH CHEN 			return ret;
1779bb6e3581SBOUGH CHEN 	}
17803e3274abSUlf Hansson 
1781a26a4f1bSHaibo Chen 	if ((imx_data->socdata->flags & ESDHC_FLAG_STATE_LOST_IN_LPMODE) &&
1782a26a4f1bSHaibo Chen 		(host->tuning_mode != SDHCI_TUNING_MODE_1)) {
1783a26a4f1bSHaibo Chen 		mmc_retune_timer_stop(host->mmc);
1784a26a4f1bSHaibo Chen 		mmc_retune_needed(host->mmc);
1785a26a4f1bSHaibo Chen 	}
1786a26a4f1bSHaibo Chen 
1787d38dcad4SAdrian Hunter 	if (host->tuning_mode != SDHCI_TUNING_MODE_3)
1788d38dcad4SAdrian Hunter 		mmc_retune_needed(host->mmc);
1789d38dcad4SAdrian Hunter 
1790af8fade4SHaibo Chen 	ret = sdhci_suspend_host(host);
1791f6140462SHaibo Chen 	if (ret)
1792f6140462SHaibo Chen 		return ret;
1793f6140462SHaibo Chen 
1794f6140462SHaibo Chen 	ret = pinctrl_pm_select_sleep_state(dev);
1795f6140462SHaibo Chen 	if (ret)
1796f6140462SHaibo Chen 		return ret;
1797f6140462SHaibo Chen 
1798f6140462SHaibo Chen 	ret = mmc_gpio_set_cd_wake(host->mmc, true);
1799af8fade4SHaibo Chen 
1800af8fade4SHaibo Chen 	return ret;
180104143fbaSDong Aisheng }
180204143fbaSDong Aisheng 
180304143fbaSDong Aisheng static int sdhci_esdhc_resume(struct device *dev)
180404143fbaSDong Aisheng {
1805cc17e129SDong Aisheng 	struct sdhci_host *host = dev_get_drvdata(dev);
1806bb6e3581SBOUGH CHEN 	int ret;
1807cc17e129SDong Aisheng 
1808af8fade4SHaibo Chen 	ret = pinctrl_pm_select_default_state(dev);
1809af8fade4SHaibo Chen 	if (ret)
1810af8fade4SHaibo Chen 		return ret;
1811af8fade4SHaibo Chen 
181219dbfdd3SDong Aisheng 	/* re-initialize hw state in case it's lost in low power mode */
181319dbfdd3SDong Aisheng 	sdhci_esdhc_imx_hwinit(host);
1814cc17e129SDong Aisheng 
1815bb6e3581SBOUGH CHEN 	ret = sdhci_resume_host(host);
1816bb6e3581SBOUGH CHEN 	if (ret)
1817bb6e3581SBOUGH CHEN 		return ret;
1818bb6e3581SBOUGH CHEN 
1819bb6e3581SBOUGH CHEN 	if (host->mmc->caps2 & MMC_CAP2_CQE)
1820bb6e3581SBOUGH CHEN 		ret = cqhci_resume(host->mmc);
1821bb6e3581SBOUGH CHEN 
1822f6140462SHaibo Chen 	if (!ret)
1823f6140462SHaibo Chen 		ret = mmc_gpio_set_cd_wake(host->mmc, false);
1824f6140462SHaibo Chen 
1825bb6e3581SBOUGH CHEN 	return ret;
182604143fbaSDong Aisheng }
18272788ed42SUlf Hansson #endif
182804143fbaSDong Aisheng 
18292788ed42SUlf Hansson #ifdef CONFIG_PM
183089d7e5c1SDong Aisheng static int sdhci_esdhc_runtime_suspend(struct device *dev)
183189d7e5c1SDong Aisheng {
183289d7e5c1SDong Aisheng 	struct sdhci_host *host = dev_get_drvdata(dev);
183389d7e5c1SDong Aisheng 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1834070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
183589d7e5c1SDong Aisheng 	int ret;
183689d7e5c1SDong Aisheng 
1837bb6e3581SBOUGH CHEN 	if (host->mmc->caps2 & MMC_CAP2_CQE) {
1838bb6e3581SBOUGH CHEN 		ret = cqhci_suspend(host->mmc);
1839bb6e3581SBOUGH CHEN 		if (ret)
1840bb6e3581SBOUGH CHEN 			return ret;
1841bb6e3581SBOUGH CHEN 	}
1842bb6e3581SBOUGH CHEN 
184389d7e5c1SDong Aisheng 	ret = sdhci_runtime_suspend_host(host);
1844371d39faSMichael Trimarchi 	if (ret)
1845371d39faSMichael Trimarchi 		return ret;
184689d7e5c1SDong Aisheng 
1847d38dcad4SAdrian Hunter 	if (host->tuning_mode != SDHCI_TUNING_MODE_3)
1848d38dcad4SAdrian Hunter 		mmc_retune_needed(host->mmc);
1849d38dcad4SAdrian Hunter 
18503602785bSMichael Trimarchi 	imx_data->actual_clock = host->mmc->actual_clock;
18513602785bSMichael Trimarchi 	esdhc_pltfm_set_clock(host, 0);
185289d7e5c1SDong Aisheng 	clk_disable_unprepare(imx_data->clk_per);
185389d7e5c1SDong Aisheng 	clk_disable_unprepare(imx_data->clk_ipg);
185489d7e5c1SDong Aisheng 	clk_disable_unprepare(imx_data->clk_ahb);
185589d7e5c1SDong Aisheng 
18561c4989b0SBOUGH CHEN 	if (imx_data->socdata->flags & ESDHC_FLAG_PMQOS)
1857d1b98305SRafael J. Wysocki 		cpu_latency_qos_remove_request(&imx_data->pm_qos_req);
18581c4989b0SBOUGH CHEN 
185989d7e5c1SDong Aisheng 	return ret;
186089d7e5c1SDong Aisheng }
186189d7e5c1SDong Aisheng 
186289d7e5c1SDong Aisheng static int sdhci_esdhc_runtime_resume(struct device *dev)
186389d7e5c1SDong Aisheng {
186489d7e5c1SDong Aisheng 	struct sdhci_host *host = dev_get_drvdata(dev);
186589d7e5c1SDong Aisheng 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1866070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
186717b1eb7fSFabio Estevam 	int err;
186889d7e5c1SDong Aisheng 
18691c4989b0SBOUGH CHEN 	if (imx_data->socdata->flags & ESDHC_FLAG_PMQOS)
1870d1b98305SRafael J. Wysocki 		cpu_latency_qos_add_request(&imx_data->pm_qos_req, 0);
18711c4989b0SBOUGH CHEN 
18725c11f1ffSHaibo Chen 	if (imx_data->socdata->flags & ESDHC_FLAG_CLK_RATE_LOST_IN_PM_RUNTIME)
18735c11f1ffSHaibo Chen 		clk_set_rate(imx_data->clk_per, pltfm_host->clock);
18745c11f1ffSHaibo Chen 
1875a0ad3087SMichael Trimarchi 	err = clk_prepare_enable(imx_data->clk_ahb);
1876a0ad3087SMichael Trimarchi 	if (err)
18771c4989b0SBOUGH CHEN 		goto remove_pm_qos_request;
1878a0ad3087SMichael Trimarchi 
187917b1eb7fSFabio Estevam 	err = clk_prepare_enable(imx_data->clk_per);
188017b1eb7fSFabio Estevam 	if (err)
1881a0ad3087SMichael Trimarchi 		goto disable_ahb_clk;
1882af5d2b7bSUlf Hansson 
188317b1eb7fSFabio Estevam 	err = clk_prepare_enable(imx_data->clk_ipg);
188417b1eb7fSFabio Estevam 	if (err)
188517b1eb7fSFabio Estevam 		goto disable_per_clk;
1886af5d2b7bSUlf Hansson 
18873602785bSMichael Trimarchi 	esdhc_pltfm_set_clock(host, imx_data->actual_clock);
1888a0ad3087SMichael Trimarchi 
1889c6303c5dSBaolin Wang 	err = sdhci_runtime_resume_host(host, 0);
189017b1eb7fSFabio Estevam 	if (err)
1891a0ad3087SMichael Trimarchi 		goto disable_ipg_clk;
189289d7e5c1SDong Aisheng 
1893bb6e3581SBOUGH CHEN 	if (host->mmc->caps2 & MMC_CAP2_CQE)
1894bb6e3581SBOUGH CHEN 		err = cqhci_resume(host->mmc);
1895bb6e3581SBOUGH CHEN 
1896bb6e3581SBOUGH CHEN 	return err;
189717b1eb7fSFabio Estevam 
189817b1eb7fSFabio Estevam disable_ipg_clk:
189917b1eb7fSFabio Estevam 	clk_disable_unprepare(imx_data->clk_ipg);
190017b1eb7fSFabio Estevam disable_per_clk:
190117b1eb7fSFabio Estevam 	clk_disable_unprepare(imx_data->clk_per);
1902a0ad3087SMichael Trimarchi disable_ahb_clk:
1903a0ad3087SMichael Trimarchi 	clk_disable_unprepare(imx_data->clk_ahb);
19041c4989b0SBOUGH CHEN remove_pm_qos_request:
19051c4989b0SBOUGH CHEN 	if (imx_data->socdata->flags & ESDHC_FLAG_PMQOS)
1906d1b98305SRafael J. Wysocki 		cpu_latency_qos_remove_request(&imx_data->pm_qos_req);
190717b1eb7fSFabio Estevam 	return err;
190889d7e5c1SDong Aisheng }
190989d7e5c1SDong Aisheng #endif
191089d7e5c1SDong Aisheng 
191189d7e5c1SDong Aisheng static const struct dev_pm_ops sdhci_esdhc_pmops = {
191204143fbaSDong Aisheng 	SET_SYSTEM_SLEEP_PM_OPS(sdhci_esdhc_suspend, sdhci_esdhc_resume)
191389d7e5c1SDong Aisheng 	SET_RUNTIME_PM_OPS(sdhci_esdhc_runtime_suspend,
191489d7e5c1SDong Aisheng 				sdhci_esdhc_runtime_resume, NULL)
191589d7e5c1SDong Aisheng };
191689d7e5c1SDong Aisheng 
191785d6509dSShawn Guo static struct platform_driver sdhci_esdhc_imx_driver = {
191885d6509dSShawn Guo 	.driver		= {
191985d6509dSShawn Guo 		.name	= "sdhci-esdhc-imx",
192021b2cec6SDouglas Anderson 		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
1921abfafc2dSShawn Guo 		.of_match_table = imx_esdhc_dt_ids,
192289d7e5c1SDong Aisheng 		.pm	= &sdhci_esdhc_pmops,
192385d6509dSShawn Guo 	},
192485d6509dSShawn Guo 	.probe		= sdhci_esdhc_imx_probe,
19250433c143SBill Pemberton 	.remove		= sdhci_esdhc_imx_remove,
192695f25efeSWolfram Sang };
192785d6509dSShawn Guo 
1928d1f81a64SAxel Lin module_platform_driver(sdhci_esdhc_imx_driver);
192985d6509dSShawn Guo 
193085d6509dSShawn Guo MODULE_DESCRIPTION("SDHCI driver for Freescale i.MX eSDHC");
1931035ff831SWolfram Sang MODULE_AUTHOR("Wolfram Sang <kernel@pengutronix.de>");
193285d6509dSShawn Guo MODULE_LICENSE("GPL v2");
1933