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)
1101e336aa0SHaibo Chen #define ESDHC_TUNING_STEP_DEFAULT	0x1
111260ecb3cSHaibo Chen #define ESDHC_TUNING_STEP_MASK		0x00070000
112d407e30bSHaibo Chen #define ESDHC_TUNING_STEP_SHIFT		16
1136e9fd28eSDong Aisheng 
114ad93220dSDong Aisheng /* pinctrl state */
115ad93220dSDong Aisheng #define ESDHC_PINCTRL_STATE_100MHZ	"state_100mhz"
116ad93220dSDong Aisheng #define ESDHC_PINCTRL_STATE_200MHZ	"state_200mhz"
117ad93220dSDong Aisheng 
11858ac8177SRichard Zhu /*
119af51079eSSascha Hauer  * Our interpretation of the SDHCI_HOST_CONTROL register
120af51079eSSascha Hauer  */
121af51079eSSascha Hauer #define ESDHC_CTRL_4BITBUS		(0x1 << 1)
122af51079eSSascha Hauer #define ESDHC_CTRL_8BITBUS		(0x2 << 1)
123af51079eSSascha Hauer #define ESDHC_CTRL_BUSWIDTH_MASK	(0x3 << 1)
12445334ee1SHaibo Chen #define USDHC_GET_BUSWIDTH(c) (c & ESDHC_CTRL_BUSWIDTH_MASK)
125af51079eSSascha Hauer 
126af51079eSSascha Hauer /*
127d04f8d5bSBenoît Thébaudeau  * There is an INT DMA ERR mismatch between eSDHC and STD SDHC SPEC:
12897e4ba6aSRichard Zhu  * Bit25 is used in STD SPEC, and is reserved in fsl eSDHC design,
12997e4ba6aSRichard Zhu  * but bit28 is used as the INT DMA ERR in fsl eSDHC design.
13097e4ba6aSRichard Zhu  * Define this macro DMA error INT for fsl eSDHC
13197e4ba6aSRichard Zhu  */
13260bf6396SShawn Guo #define ESDHC_INT_VENDOR_SPEC_DMA_ERR	(1 << 28)
13397e4ba6aSRichard Zhu 
134bb6e3581SBOUGH CHEN /* the address offset of CQHCI */
135bb6e3581SBOUGH CHEN #define ESDHC_CQHCI_ADDR_OFFSET		0x100
136bb6e3581SBOUGH CHEN 
13797e4ba6aSRichard Zhu /*
13858ac8177SRichard Zhu  * The CMDTYPE of the CMD register (offset 0xE) should be set to
13958ac8177SRichard Zhu  * "11" when the STOP CMD12 is issued on imx53 to abort one
14058ac8177SRichard Zhu  * open ended multi-blk IO. Otherwise the TC INT wouldn't
14158ac8177SRichard Zhu  * be generated.
14258ac8177SRichard Zhu  * In exact block transfer, the controller doesn't complete the
14358ac8177SRichard Zhu  * operations automatically as required at the end of the
14458ac8177SRichard Zhu  * transfer and remains on hold if the abort command is not sent.
14558ac8177SRichard Zhu  * As a result, the TC flag is not asserted and SW received timeout
146d04f8d5bSBenoît Thébaudeau  * exception. Bit1 of Vendor Spec register is used to fix it.
14758ac8177SRichard Zhu  */
14831fbb301SShawn Guo #define ESDHC_FLAG_MULTIBLK_NO_INT	BIT(1)
14931fbb301SShawn Guo /*
1509d61c009SShawn Guo  * The flag tells that the ESDHC controller is an USDHC block that is
1519d61c009SShawn Guo  * integrated on the i.MX6 series.
1529d61c009SShawn Guo  */
1539d61c009SShawn Guo #define ESDHC_FLAG_USDHC		BIT(3)
1546e9fd28eSDong Aisheng /* The IP supports manual tuning process */
1556e9fd28eSDong Aisheng #define ESDHC_FLAG_MAN_TUNING		BIT(4)
1566e9fd28eSDong Aisheng /* The IP supports standard tuning process */
1576e9fd28eSDong Aisheng #define ESDHC_FLAG_STD_TUNING		BIT(5)
1586e9fd28eSDong Aisheng /* The IP has SDHCI_CAPABILITIES_1 register */
1596e9fd28eSDong Aisheng #define ESDHC_FLAG_HAVE_CAP1		BIT(6)
16018094430SDong Aisheng /*
161d04f8d5bSBenoît Thébaudeau  * The IP has erratum ERR004536
16218094430SDong Aisheng  * uSDHC: ADMA Length Mismatch Error occurs if the AHB read access is slow,
16318094430SDong Aisheng  * when reading data from the card
164667123f6SBenoît Thébaudeau  * This flag is also set for i.MX25 and i.MX35 in order to get
165667123f6SBenoît Thébaudeau  * SDHCI_QUIRK_BROKEN_ADMA, but for different reasons (ADMA capability bits).
16618094430SDong Aisheng  */
16718094430SDong Aisheng #define ESDHC_FLAG_ERR004536		BIT(7)
1684245afffSDong Aisheng /* The IP supports HS200 mode */
1694245afffSDong Aisheng #define ESDHC_FLAG_HS200		BIT(8)
17028b07674SHaibo Chen /* The IP supports HS400 mode */
17128b07674SHaibo Chen #define ESDHC_FLAG_HS400		BIT(9)
172af6a50d4SBOUGH CHEN /*
173af6a50d4SBOUGH CHEN  * The IP has errata ERR010450
174af6a50d4SBOUGH CHEN  * uSDHC: Due to the I/O timing limit, for SDR mode, SD card clock can't
175af6a50d4SBOUGH CHEN  * exceed 150MHz, for DDR mode, SD card clock can't exceed 45MHz.
176af6a50d4SBOUGH CHEN  */
177af6a50d4SBOUGH CHEN #define ESDHC_FLAG_ERR010450		BIT(10)
178029e2476SBOUGH CHEN /* The IP supports HS400ES mode */
179029e2476SBOUGH CHEN #define ESDHC_FLAG_HS400_ES		BIT(11)
180bb6e3581SBOUGH CHEN /* The IP has Host Controller Interface for Command Queuing */
181bb6e3581SBOUGH CHEN #define ESDHC_FLAG_CQHCI		BIT(12)
1821c4989b0SBOUGH CHEN /* need request pmqos during low power */
1831c4989b0SBOUGH CHEN #define ESDHC_FLAG_PMQOS		BIT(13)
184a26a4f1bSHaibo Chen /* The IP state got lost in low power mode */
185a26a4f1bSHaibo Chen #define ESDHC_FLAG_STATE_LOST_IN_LPMODE		BIT(14)
1865c11f1ffSHaibo Chen /* The IP lost clock rate in PM_RUNTIME */
1875c11f1ffSHaibo Chen #define ESDHC_FLAG_CLK_RATE_LOST_IN_PM_RUNTIME	BIT(15)
18874898cbcSHaibo Chen /*
18974898cbcSHaibo Chen  * The IP do not support the ACMD23 feature completely when use ADMA mode.
19074898cbcSHaibo Chen  * In ADMA mode, it only use the 16 bit block count of the register 0x4
19174898cbcSHaibo Chen  * (BLOCK_ATT) as the CMD23's argument for ACMD23 mode, which means it will
19274898cbcSHaibo Chen  * ignore the upper 16 bit of the CMD23's argument. This will block the reliable
19374898cbcSHaibo Chen  * write operation in RPMB, because RPMB reliable write need to set the bit31
19474898cbcSHaibo Chen  * of the CMD23's argument.
19574898cbcSHaibo Chen  * imx6qpdl/imx6sx/imx6sl/imx7d has this limitation only for ADMA mode, SDMA
19674898cbcSHaibo Chen  * do not has this limitation. so when these SoC use ADMA mode, it need to
19774898cbcSHaibo Chen  * disable the ACMD23 feature.
19874898cbcSHaibo Chen  */
19974898cbcSHaibo Chen #define ESDHC_FLAG_BROKEN_AUTO_CMD23	BIT(16)
200e149860dSRichard Zhu 
2015c4f0062SChester Lin /* ERR004536 is not applicable for the IP  */
2025c4f0062SChester Lin #define ESDHC_FLAG_SKIP_ERR004536	BIT(17)
2035c4f0062SChester Lin 
2044a11cc64SFabio Estevam enum wp_types {
2054a11cc64SFabio Estevam 	ESDHC_WP_NONE,		/* no WP, neither controller nor gpio */
2064a11cc64SFabio Estevam 	ESDHC_WP_CONTROLLER,	/* mmc controller internal WP */
2074a11cc64SFabio Estevam 	ESDHC_WP_GPIO,		/* external gpio pin for WP */
2084a11cc64SFabio Estevam };
2094a11cc64SFabio Estevam 
2104a11cc64SFabio Estevam enum cd_types {
2114a11cc64SFabio Estevam 	ESDHC_CD_NONE,		/* no CD, neither controller nor gpio */
2124a11cc64SFabio Estevam 	ESDHC_CD_CONTROLLER,	/* mmc controller internal CD */
2134a11cc64SFabio Estevam 	ESDHC_CD_GPIO,		/* external gpio pin for CD */
2144a11cc64SFabio Estevam 	ESDHC_CD_PERMANENT,	/* no CD, card permanently wired to host */
2154a11cc64SFabio Estevam };
2164a11cc64SFabio Estevam 
2174a11cc64SFabio Estevam /*
2184a11cc64SFabio Estevam  * struct esdhc_platform_data - platform data for esdhc on i.MX
2194a11cc64SFabio Estevam  *
2204a11cc64SFabio Estevam  * ESDHC_WP(CD)_CONTROLLER type is not available on i.MX25/35.
2214a11cc64SFabio Estevam  *
2224a11cc64SFabio Estevam  * @wp_type:	type of write_protect method (see wp_types enum above)
2234a11cc64SFabio Estevam  * @cd_type:	type of card_detect method (see cd_types enum above)
2244a11cc64SFabio Estevam  */
2254a11cc64SFabio Estevam 
2264a11cc64SFabio Estevam struct esdhc_platform_data {
2274a11cc64SFabio Estevam 	enum wp_types wp_type;
2284a11cc64SFabio Estevam 	enum cd_types cd_type;
2294a11cc64SFabio Estevam 	int max_bus_width;
2304a11cc64SFabio Estevam 	unsigned int delay_line;
2314a11cc64SFabio Estevam 	unsigned int tuning_step;       /* The delay cell steps in tuning procedure */
2324a11cc64SFabio Estevam 	unsigned int tuning_start_tap;	/* The start delay cell point in tuning procedure */
2334a11cc64SFabio Estevam 	unsigned int strobe_dll_delay_target;	/* The delay cell for strobe pad (read clock) */
2344a11cc64SFabio Estevam };
2354a11cc64SFabio Estevam 
236f47c4bbfSShawn Guo struct esdhc_soc_data {
237f47c4bbfSShawn Guo 	u32 flags;
238f47c4bbfSShawn Guo };
239f47c4bbfSShawn Guo 
2404f100012SAndrey Smirnov static const struct esdhc_soc_data esdhc_imx25_data = {
241667123f6SBenoît Thébaudeau 	.flags = ESDHC_FLAG_ERR004536,
242f47c4bbfSShawn Guo };
243f47c4bbfSShawn Guo 
2444f100012SAndrey Smirnov static const struct esdhc_soc_data esdhc_imx35_data = {
245667123f6SBenoît Thébaudeau 	.flags = ESDHC_FLAG_ERR004536,
246f47c4bbfSShawn Guo };
247f47c4bbfSShawn Guo 
2484f100012SAndrey Smirnov static const struct esdhc_soc_data esdhc_imx51_data = {
249f47c4bbfSShawn Guo 	.flags = 0,
250f47c4bbfSShawn Guo };
251f47c4bbfSShawn Guo 
2524f100012SAndrey Smirnov static const struct esdhc_soc_data esdhc_imx53_data = {
253f47c4bbfSShawn Guo 	.flags = ESDHC_FLAG_MULTIBLK_NO_INT,
254f47c4bbfSShawn Guo };
255f47c4bbfSShawn Guo 
2564f100012SAndrey Smirnov static const struct esdhc_soc_data usdhc_imx6q_data = {
25774898cbcSHaibo Chen 	.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_MAN_TUNING
25874898cbcSHaibo Chen 			| ESDHC_FLAG_BROKEN_AUTO_CMD23,
2596e9fd28eSDong Aisheng };
2606e9fd28eSDong Aisheng 
2614f100012SAndrey Smirnov static const struct esdhc_soc_data usdhc_imx6sl_data = {
2626e9fd28eSDong Aisheng 	.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
2634245afffSDong Aisheng 			| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_ERR004536
26474898cbcSHaibo Chen 			| ESDHC_FLAG_HS200
26574898cbcSHaibo Chen 			| ESDHC_FLAG_BROKEN_AUTO_CMD23,
26674898cbcSHaibo Chen };
26774898cbcSHaibo Chen 
26874898cbcSHaibo Chen static const struct esdhc_soc_data usdhc_imx6sll_data = {
26974898cbcSHaibo Chen 	.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
27074898cbcSHaibo Chen 			| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
27186b59671SHaibo Chen 			| ESDHC_FLAG_HS400
27274898cbcSHaibo Chen 			| ESDHC_FLAG_STATE_LOST_IN_LPMODE,
27357ed3314SShawn Guo };
27457ed3314SShawn Guo 
2754f100012SAndrey Smirnov static const struct esdhc_soc_data usdhc_imx6sx_data = {
276913d4951SDong Aisheng 	.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
277a26a4f1bSHaibo Chen 			| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
27874898cbcSHaibo Chen 			| ESDHC_FLAG_STATE_LOST_IN_LPMODE
27974898cbcSHaibo Chen 			| ESDHC_FLAG_BROKEN_AUTO_CMD23,
280913d4951SDong Aisheng };
281913d4951SDong Aisheng 
282af6a50d4SBOUGH CHEN static const struct esdhc_soc_data usdhc_imx6ull_data = {
283af6a50d4SBOUGH CHEN 	.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
284af6a50d4SBOUGH CHEN 			| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
285a26a4f1bSHaibo Chen 			| ESDHC_FLAG_ERR010450
286a26a4f1bSHaibo Chen 			| ESDHC_FLAG_STATE_LOST_IN_LPMODE,
287af6a50d4SBOUGH CHEN };
288af6a50d4SBOUGH CHEN 
2894f100012SAndrey Smirnov static const struct esdhc_soc_data usdhc_imx7d_data = {
29028b07674SHaibo Chen 	.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
29128b07674SHaibo Chen 			| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
292a26a4f1bSHaibo Chen 			| ESDHC_FLAG_HS400
29374898cbcSHaibo Chen 			| ESDHC_FLAG_STATE_LOST_IN_LPMODE
29474898cbcSHaibo Chen 			| ESDHC_FLAG_BROKEN_AUTO_CMD23,
29528b07674SHaibo Chen };
29628b07674SHaibo Chen 
2975c4f0062SChester Lin static struct esdhc_soc_data usdhc_s32g2_data = {
2985c4f0062SChester Lin 	.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_MAN_TUNING
2995c4f0062SChester Lin 			| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
3005c4f0062SChester Lin 			| ESDHC_FLAG_HS400 | ESDHC_FLAG_HS400_ES
3015c4f0062SChester Lin 			| ESDHC_FLAG_SKIP_ERR004536,
3025c4f0062SChester Lin };
3035c4f0062SChester Lin 
3041c4989b0SBOUGH CHEN static struct esdhc_soc_data usdhc_imx7ulp_data = {
3051c4989b0SBOUGH CHEN 	.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
3061c4989b0SBOUGH CHEN 			| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
307a26a4f1bSHaibo Chen 			| ESDHC_FLAG_PMQOS | ESDHC_FLAG_HS400
308a26a4f1bSHaibo Chen 			| ESDHC_FLAG_STATE_LOST_IN_LPMODE,
3091c4989b0SBOUGH CHEN };
3102f4788f3SJesse Taube static struct esdhc_soc_data usdhc_imxrt1050_data = {
311d7a1830eSGiulio Benetti 	.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
312d7a1830eSGiulio Benetti 			| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200,
3132f4788f3SJesse Taube };
3141c4989b0SBOUGH CHEN 
315029e2476SBOUGH CHEN static struct esdhc_soc_data usdhc_imx8qxp_data = {
316029e2476SBOUGH CHEN 	.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
317029e2476SBOUGH CHEN 			| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
318bb6e3581SBOUGH CHEN 			| ESDHC_FLAG_HS400 | ESDHC_FLAG_HS400_ES
3195c11f1ffSHaibo Chen 			| ESDHC_FLAG_STATE_LOST_IN_LPMODE
3205c11f1ffSHaibo Chen 			| ESDHC_FLAG_CLK_RATE_LOST_IN_PM_RUNTIME,
321029e2476SBOUGH CHEN };
322029e2476SBOUGH CHEN 
323cde5e8e9SHaibo Chen static struct esdhc_soc_data usdhc_imx8mm_data = {
324cde5e8e9SHaibo Chen 	.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
325cde5e8e9SHaibo Chen 			| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
326cde5e8e9SHaibo Chen 			| ESDHC_FLAG_HS400 | ESDHC_FLAG_HS400_ES
327cde5e8e9SHaibo Chen 			| ESDHC_FLAG_STATE_LOST_IN_LPMODE,
328e149860dSRichard Zhu };
329e149860dSRichard Zhu 
330e149860dSRichard Zhu struct pltfm_imx_data {
331e149860dSRichard Zhu 	u32 scratchpad;
332e62d8b8fSDong Aisheng 	struct pinctrl *pinctrl;
333ad93220dSDong Aisheng 	struct pinctrl_state *pins_100mhz;
334ad93220dSDong Aisheng 	struct pinctrl_state *pins_200mhz;
335f47c4bbfSShawn Guo 	const struct esdhc_soc_data *socdata;
336842afc02SShawn Guo 	struct esdhc_platform_data boarddata;
33752dac615SSascha Hauer 	struct clk *clk_ipg;
33852dac615SSascha Hauer 	struct clk *clk_ahb;
33952dac615SSascha Hauer 	struct clk *clk_per;
3403602785bSMichael Trimarchi 	unsigned int actual_clock;
34152e4c32bSHaibo Chen 
34252e4c32bSHaibo Chen 	/*
34352e4c32bSHaibo Chen 	 * USDHC has one limition, require the SDIO device a different
34452e4c32bSHaibo Chen 	 * register setting. Driver has to recognize card type during
34552e4c32bSHaibo Chen 	 * the card init, but at this stage, mmc_host->card is not
34652e4c32bSHaibo Chen 	 * available. So involve this field to save the card type
34752e4c32bSHaibo Chen 	 * during card init through usdhc_init_card().
34852e4c32bSHaibo Chen 	 */
34952e4c32bSHaibo Chen 	unsigned int init_card_type;
35052e4c32bSHaibo Chen 
351361b8482SLucas Stach 	enum {
352361b8482SLucas Stach 		NO_CMD_PENDING,      /* no multiblock command pending */
353361b8482SLucas Stach 		MULTIBLK_IN_PROCESS, /* exact multiblock cmd in process */
354361b8482SLucas Stach 		WAIT_FOR_INT,        /* sent CMD12, waiting for response INT */
355361b8482SLucas Stach 	} multiblock_status;
356de5bdbffSDong Aisheng 	u32 is_ddr;
3571c4989b0SBOUGH CHEN 	struct pm_qos_request pm_qos_req;
358e149860dSRichard Zhu };
359e149860dSRichard Zhu 
360abfafc2dSShawn Guo static const struct of_device_id imx_esdhc_dt_ids[] = {
361f47c4bbfSShawn Guo 	{ .compatible = "fsl,imx25-esdhc", .data = &esdhc_imx25_data, },
362f47c4bbfSShawn Guo 	{ .compatible = "fsl,imx35-esdhc", .data = &esdhc_imx35_data, },
363f47c4bbfSShawn Guo 	{ .compatible = "fsl,imx51-esdhc", .data = &esdhc_imx51_data, },
364f47c4bbfSShawn Guo 	{ .compatible = "fsl,imx53-esdhc", .data = &esdhc_imx53_data, },
365913d4951SDong Aisheng 	{ .compatible = "fsl,imx6sx-usdhc", .data = &usdhc_imx6sx_data, },
3666e9fd28eSDong Aisheng 	{ .compatible = "fsl,imx6sl-usdhc", .data = &usdhc_imx6sl_data, },
36774898cbcSHaibo Chen 	{ .compatible = "fsl,imx6sll-usdhc", .data = &usdhc_imx6sll_data, },
368f47c4bbfSShawn Guo 	{ .compatible = "fsl,imx6q-usdhc", .data = &usdhc_imx6q_data, },
369af6a50d4SBOUGH CHEN 	{ .compatible = "fsl,imx6ull-usdhc", .data = &usdhc_imx6ull_data, },
37028b07674SHaibo Chen 	{ .compatible = "fsl,imx7d-usdhc", .data = &usdhc_imx7d_data, },
3711c4989b0SBOUGH CHEN 	{ .compatible = "fsl,imx7ulp-usdhc", .data = &usdhc_imx7ulp_data, },
372029e2476SBOUGH CHEN 	{ .compatible = "fsl,imx8qxp-usdhc", .data = &usdhc_imx8qxp_data, },
373cde5e8e9SHaibo Chen 	{ .compatible = "fsl,imx8mm-usdhc", .data = &usdhc_imx8mm_data, },
3742f4788f3SJesse Taube 	{ .compatible = "fsl,imxrt1050-usdhc", .data = &usdhc_imxrt1050_data, },
3755c4f0062SChester Lin 	{ .compatible = "nxp,s32g2-usdhc", .data = &usdhc_s32g2_data, },
376abfafc2dSShawn Guo 	{ /* sentinel */ }
377abfafc2dSShawn Guo };
378abfafc2dSShawn Guo MODULE_DEVICE_TABLE(of, imx_esdhc_dt_ids);
379abfafc2dSShawn Guo 
38057ed3314SShawn Guo static inline int is_imx25_esdhc(struct pltfm_imx_data *data)
38157ed3314SShawn Guo {
382f47c4bbfSShawn Guo 	return data->socdata == &esdhc_imx25_data;
38357ed3314SShawn Guo }
38457ed3314SShawn Guo 
38557ed3314SShawn Guo static inline int is_imx53_esdhc(struct pltfm_imx_data *data)
38657ed3314SShawn Guo {
387f47c4bbfSShawn Guo 	return data->socdata == &esdhc_imx53_data;
38857ed3314SShawn Guo }
38957ed3314SShawn Guo 
3909d61c009SShawn Guo static inline int esdhc_is_usdhc(struct pltfm_imx_data *data)
3919d61c009SShawn Guo {
392f47c4bbfSShawn Guo 	return !!(data->socdata->flags & ESDHC_FLAG_USDHC);
3939d61c009SShawn Guo }
3949d61c009SShawn Guo 
39595f25efeSWolfram Sang static inline void esdhc_clrset_le(struct sdhci_host *host, u32 mask, u32 val, int reg)
39695f25efeSWolfram Sang {
39795f25efeSWolfram Sang 	void __iomem *base = host->ioaddr + (reg & ~0x3);
39895f25efeSWolfram Sang 	u32 shift = (reg & 0x3) * 8;
39995f25efeSWolfram Sang 
40095f25efeSWolfram Sang 	writel(((readl(base) & ~(mask << shift)) | (val << shift)), base);
40195f25efeSWolfram Sang }
40295f25efeSWolfram Sang 
4033722c74cSHaibo Chen #define DRIVER_NAME "sdhci-esdhc-imx"
4043722c74cSHaibo Chen #define ESDHC_IMX_DUMP(f, x...) \
4053722c74cSHaibo Chen 	pr_err("%s: " DRIVER_NAME ": " f, mmc_hostname(host->mmc), ## x)
4063722c74cSHaibo Chen static void esdhc_dump_debug_regs(struct sdhci_host *host)
4073722c74cSHaibo Chen {
4083722c74cSHaibo Chen 	int i;
4093722c74cSHaibo Chen 	char *debug_status[7] = {
4103722c74cSHaibo Chen 				 "cmd debug status",
4113722c74cSHaibo Chen 				 "data debug status",
4123722c74cSHaibo Chen 				 "trans debug status",
4133722c74cSHaibo Chen 				 "dma debug status",
4143722c74cSHaibo Chen 				 "adma debug status",
4153722c74cSHaibo Chen 				 "fifo debug status",
4163722c74cSHaibo Chen 				 "async fifo debug status"
4173722c74cSHaibo Chen 	};
4183722c74cSHaibo Chen 
4193722c74cSHaibo Chen 	ESDHC_IMX_DUMP("========= ESDHC IMX DEBUG STATUS DUMP =========\n");
4203722c74cSHaibo Chen 	for (i = 0; i < 7; i++) {
4213722c74cSHaibo Chen 		esdhc_clrset_le(host, ESDHC_DEBUG_SEL_MASK,
4223722c74cSHaibo Chen 			ESDHC_DEBUG_SEL_CMD_STATE + i, ESDHC_DEBUG_SEL_REG);
4233722c74cSHaibo Chen 		ESDHC_IMX_DUMP("%s:  0x%04x\n", debug_status[i],
4243722c74cSHaibo Chen 			readw(host->ioaddr + ESDHC_DEBUG_SEL_AND_STATUS_REG));
4253722c74cSHaibo Chen 	}
4263722c74cSHaibo Chen 
4273722c74cSHaibo Chen 	esdhc_clrset_le(host, ESDHC_DEBUG_SEL_MASK, 0, ESDHC_DEBUG_SEL_REG);
4283722c74cSHaibo Chen 
4293722c74cSHaibo Chen }
4303722c74cSHaibo Chen 
431f581e909SHaibo Chen static inline void esdhc_wait_for_card_clock_gate_off(struct sdhci_host *host)
432f581e909SHaibo Chen {
433f581e909SHaibo Chen 	u32 present_state;
434f581e909SHaibo Chen 	int ret;
435f581e909SHaibo Chen 
436f581e909SHaibo Chen 	ret = readl_poll_timeout(host->ioaddr + ESDHC_PRSSTAT, present_state,
437f581e909SHaibo Chen 				(present_state & ESDHC_CLOCK_GATE_OFF), 2, 100);
438f581e909SHaibo Chen 	if (ret == -ETIMEDOUT)
439f581e909SHaibo Chen 		dev_warn(mmc_dev(host->mmc), "%s: card clock still not gate off in 100us!.\n", __func__);
440f581e909SHaibo Chen }
441f581e909SHaibo Chen 
44245334ee1SHaibo Chen /* Enable the auto tuning circuit to check the CMD line and BUS line */
443c8c49a5aSHaibo Chen static inline void usdhc_auto_tuning_mode_sel_and_en(struct sdhci_host *host)
44445334ee1SHaibo Chen {
44552e4c32bSHaibo Chen 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
44652e4c32bSHaibo Chen 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
44745334ee1SHaibo Chen 	u32 buswidth, auto_tune_buswidth;
448c8c49a5aSHaibo Chen 	u32 reg;
44945334ee1SHaibo Chen 
45045334ee1SHaibo Chen 	buswidth = USDHC_GET_BUSWIDTH(readl(host->ioaddr + SDHCI_HOST_CONTROL));
45145334ee1SHaibo Chen 
45245334ee1SHaibo Chen 	switch (buswidth) {
45345334ee1SHaibo Chen 	case ESDHC_CTRL_8BITBUS:
45445334ee1SHaibo Chen 		auto_tune_buswidth = ESDHC_VEND_SPEC2_AUTO_TUNE_8BIT_EN;
45545334ee1SHaibo Chen 		break;
45645334ee1SHaibo Chen 	case ESDHC_CTRL_4BITBUS:
45745334ee1SHaibo Chen 		auto_tune_buswidth = ESDHC_VEND_SPEC2_AUTO_TUNE_4BIT_EN;
45845334ee1SHaibo Chen 		break;
45945334ee1SHaibo Chen 	default:	/* 1BITBUS */
46045334ee1SHaibo Chen 		auto_tune_buswidth = ESDHC_VEND_SPEC2_AUTO_TUNE_1BIT_EN;
46145334ee1SHaibo Chen 		break;
46245334ee1SHaibo Chen 	}
46345334ee1SHaibo Chen 
46452e4c32bSHaibo Chen 	/*
46552e4c32bSHaibo Chen 	 * For USDHC, auto tuning circuit can not handle the async sdio
46652e4c32bSHaibo Chen 	 * device interrupt correctly. When sdio device use 4 data lines,
46752e4c32bSHaibo Chen 	 * async sdio interrupt will use the shared DAT[1], if enable auto
46852e4c32bSHaibo Chen 	 * tuning circuit check these 4 data lines, include the DAT[1],
46952e4c32bSHaibo Chen 	 * this circuit will detect this interrupt, take this as a data on
47052e4c32bSHaibo Chen 	 * DAT[1], and adjust the delay cell wrongly.
47152e4c32bSHaibo Chen 	 * This is the hardware design limitation, to avoid this, for sdio
47252e4c32bSHaibo Chen 	 * device, config the auto tuning circuit only check DAT[0] and CMD
47352e4c32bSHaibo Chen 	 * line.
47452e4c32bSHaibo Chen 	 */
47552e4c32bSHaibo Chen 	if (imx_data->init_card_type == MMC_TYPE_SDIO)
47652e4c32bSHaibo Chen 		auto_tune_buswidth = ESDHC_VEND_SPEC2_AUTO_TUNE_1BIT_EN;
47752e4c32bSHaibo Chen 
47845334ee1SHaibo Chen 	esdhc_clrset_le(host, ESDHC_VEND_SPEC2_AUTO_TUNE_MODE_MASK,
47945334ee1SHaibo Chen 			auto_tune_buswidth | ESDHC_VEND_SPEC2_AUTO_TUNE_CMD_EN,
48045334ee1SHaibo Chen 			ESDHC_VEND_SPEC2);
481c8c49a5aSHaibo Chen 
482c8c49a5aSHaibo Chen 	reg = readl(host->ioaddr + ESDHC_MIX_CTRL);
483c8c49a5aSHaibo Chen 	reg |= ESDHC_MIX_CTRL_AUTO_TUNE_EN;
484c8c49a5aSHaibo Chen 	writel(reg, host->ioaddr + ESDHC_MIX_CTRL);
48545334ee1SHaibo Chen }
48645334ee1SHaibo Chen 
4877e29c306SWolfram Sang static u32 esdhc_readl_le(struct sdhci_host *host, int reg)
4887e29c306SWolfram Sang {
489361b8482SLucas Stach 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
490070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
491913413c3SShawn Guo 	u32 val = readl(host->ioaddr + reg);
492913413c3SShawn Guo 
4930322191eSDong Aisheng 	if (unlikely(reg == SDHCI_PRESENT_STATE)) {
4940322191eSDong Aisheng 		u32 fsl_prss = val;
4950322191eSDong Aisheng 		/* save the least 20 bits */
4960322191eSDong Aisheng 		val = fsl_prss & 0x000FFFFF;
4970322191eSDong Aisheng 		/* move dat[0-3] bits */
4980322191eSDong Aisheng 		val |= (fsl_prss & 0x0F000000) >> 4;
4990322191eSDong Aisheng 		/* move cmd line bit */
5000322191eSDong Aisheng 		val |= (fsl_prss & 0x00800000) << 1;
5010322191eSDong Aisheng 	}
5020322191eSDong Aisheng 
50397e4ba6aSRichard Zhu 	if (unlikely(reg == SDHCI_CAPABILITIES)) {
5046b4fb671SDong Aisheng 		/* ignore bit[0-15] as it stores cap_1 register val for mx6sl */
5056b4fb671SDong Aisheng 		if (imx_data->socdata->flags & ESDHC_FLAG_HAVE_CAP1)
5066b4fb671SDong Aisheng 			val &= 0xffff0000;
5076b4fb671SDong Aisheng 
50897e4ba6aSRichard Zhu 		/* In FSL esdhc IC module, only bit20 is used to indicate the
50997e4ba6aSRichard Zhu 		 * ADMA2 capability of esdhc, but this bit is messed up on
51097e4ba6aSRichard Zhu 		 * some SOCs (e.g. on MX25, MX35 this bit is set, but they
51197e4ba6aSRichard Zhu 		 * don't actually support ADMA2). So set the BROKEN_ADMA
512d04f8d5bSBenoît Thébaudeau 		 * quirk on MX25/35 platforms.
51397e4ba6aSRichard Zhu 		 */
51497e4ba6aSRichard Zhu 
51597e4ba6aSRichard Zhu 		if (val & SDHCI_CAN_DO_ADMA1) {
51697e4ba6aSRichard Zhu 			val &= ~SDHCI_CAN_DO_ADMA1;
51797e4ba6aSRichard Zhu 			val |= SDHCI_CAN_DO_ADMA2;
51897e4ba6aSRichard Zhu 		}
51997e4ba6aSRichard Zhu 	}
52097e4ba6aSRichard Zhu 
5216e9fd28eSDong Aisheng 	if (unlikely(reg == SDHCI_CAPABILITIES_1)) {
5226e9fd28eSDong Aisheng 		if (esdhc_is_usdhc(imx_data)) {
5236e9fd28eSDong Aisheng 			if (imx_data->socdata->flags & ESDHC_FLAG_HAVE_CAP1)
5246e9fd28eSDong Aisheng 				val = readl(host->ioaddr + SDHCI_CAPABILITIES) & 0xFFFF;
5256e9fd28eSDong Aisheng 			else
5266e9fd28eSDong Aisheng 				/* imx6q/dl does not have cap_1 register, fake one */
5270322191eSDong Aisheng 				val = SDHCI_SUPPORT_DDR50 | SDHCI_SUPPORT_SDR104
528888824bbSDong Aisheng 					| SDHCI_SUPPORT_SDR50
529da0295ffSDong Aisheng 					| SDHCI_USE_SDR50_TUNING
530a8e809ecSMasahiro Yamada 					| FIELD_PREP(SDHCI_RETUNING_MODE_MASK,
531a8e809ecSMasahiro Yamada 						     SDHCI_TUNING_MODE_3);
53228b07674SHaibo Chen 
53392748beaSStefan Agner 			/*
53492748beaSStefan Agner 			 * Do not advertise faster UHS modes if there are no
53592748beaSStefan Agner 			 * pinctrl states for 100MHz/200MHz.
53692748beaSStefan Agner 			 */
53725e8b9ebSShawn Guo 			if (IS_ERR_OR_NULL(imx_data->pins_100mhz))
53825e8b9ebSShawn Guo 				val &= ~(SDHCI_SUPPORT_SDR50 | SDHCI_SUPPORT_DDR50);
53925e8b9ebSShawn Guo 			if (IS_ERR_OR_NULL(imx_data->pins_200mhz))
54025e8b9ebSShawn Guo 				val &= ~(SDHCI_SUPPORT_SDR104 | SDHCI_SUPPORT_HS400);
5416e9fd28eSDong Aisheng 		}
5426e9fd28eSDong Aisheng 	}
5430322191eSDong Aisheng 
5449d61c009SShawn Guo 	if (unlikely(reg == SDHCI_MAX_CURRENT) && esdhc_is_usdhc(imx_data)) {
5450322191eSDong Aisheng 		val = 0;
546804a65b3SMasahiro Yamada 		val |= FIELD_PREP(SDHCI_MAX_CURRENT_330_MASK, 0xFF);
547804a65b3SMasahiro Yamada 		val |= FIELD_PREP(SDHCI_MAX_CURRENT_300_MASK, 0xFF);
548804a65b3SMasahiro Yamada 		val |= FIELD_PREP(SDHCI_MAX_CURRENT_180_MASK, 0xFF);
5490322191eSDong Aisheng 	}
5500322191eSDong Aisheng 
55197e4ba6aSRichard Zhu 	if (unlikely(reg == SDHCI_INT_STATUS)) {
55260bf6396SShawn Guo 		if (val & ESDHC_INT_VENDOR_SPEC_DMA_ERR) {
55360bf6396SShawn Guo 			val &= ~ESDHC_INT_VENDOR_SPEC_DMA_ERR;
55497e4ba6aSRichard Zhu 			val |= SDHCI_INT_ADMA_ERROR;
55597e4ba6aSRichard Zhu 		}
556361b8482SLucas Stach 
557361b8482SLucas Stach 		/*
558361b8482SLucas Stach 		 * mask off the interrupt we get in response to the manually
559361b8482SLucas Stach 		 * sent CMD12
560361b8482SLucas Stach 		 */
561361b8482SLucas Stach 		if ((imx_data->multiblock_status == WAIT_FOR_INT) &&
562361b8482SLucas Stach 		    ((val & SDHCI_INT_RESPONSE) == SDHCI_INT_RESPONSE)) {
563361b8482SLucas Stach 			val &= ~SDHCI_INT_RESPONSE;
564361b8482SLucas Stach 			writel(SDHCI_INT_RESPONSE, host->ioaddr +
565361b8482SLucas Stach 						   SDHCI_INT_STATUS);
566361b8482SLucas Stach 			imx_data->multiblock_status = NO_CMD_PENDING;
567361b8482SLucas Stach 		}
56897e4ba6aSRichard Zhu 	}
56997e4ba6aSRichard Zhu 
5707e29c306SWolfram Sang 	return val;
5717e29c306SWolfram Sang }
5727e29c306SWolfram Sang 
5737e29c306SWolfram Sang static void esdhc_writel_le(struct sdhci_host *host, u32 val, int reg)
5747e29c306SWolfram Sang {
575e149860dSRichard Zhu 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
576070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
5770d58864bSTony Lin 	u32 data;
578e149860dSRichard Zhu 
57977da3da0SAaron Brice 	if (unlikely(reg == SDHCI_INT_ENABLE || reg == SDHCI_SIGNAL_ENABLE ||
58077da3da0SAaron Brice 			reg == SDHCI_INT_STATUS)) {
581b7321042SDong Aisheng 		if ((val & SDHCI_INT_CARD_INT) && !esdhc_is_usdhc(imx_data)) {
5820d58864bSTony Lin 			/*
5830d58864bSTony Lin 			 * Clear and then set D3CD bit to avoid missing the
584d04f8d5bSBenoît Thébaudeau 			 * card interrupt. This is an eSDHC controller problem
5850d58864bSTony Lin 			 * so we need to apply the following workaround: clear
5860d58864bSTony Lin 			 * and set D3CD bit will make eSDHC re-sample the card
5870d58864bSTony Lin 			 * interrupt. In case a card interrupt was lost,
5880d58864bSTony Lin 			 * re-sample it by the following steps.
5890d58864bSTony Lin 			 */
5900d58864bSTony Lin 			data = readl(host->ioaddr + SDHCI_HOST_CONTROL);
59160bf6396SShawn Guo 			data &= ~ESDHC_CTRL_D3CD;
5920d58864bSTony Lin 			writel(data, host->ioaddr + SDHCI_HOST_CONTROL);
59360bf6396SShawn Guo 			data |= ESDHC_CTRL_D3CD;
5940d58864bSTony Lin 			writel(data, host->ioaddr + SDHCI_HOST_CONTROL);
5950d58864bSTony Lin 		}
596915be485SDong Aisheng 
597915be485SDong Aisheng 		if (val & SDHCI_INT_ADMA_ERROR) {
598915be485SDong Aisheng 			val &= ~SDHCI_INT_ADMA_ERROR;
599915be485SDong Aisheng 			val |= ESDHC_INT_VENDOR_SPEC_DMA_ERR;
600915be485SDong Aisheng 		}
6010d58864bSTony Lin 	}
6020d58864bSTony Lin 
603f47c4bbfSShawn Guo 	if (unlikely((imx_data->socdata->flags & ESDHC_FLAG_MULTIBLK_NO_INT)
60458ac8177SRichard Zhu 				&& (reg == SDHCI_INT_STATUS)
60558ac8177SRichard Zhu 				&& (val & SDHCI_INT_DATA_END))) {
60658ac8177SRichard Zhu 			u32 v;
60760bf6396SShawn Guo 			v = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
60860bf6396SShawn Guo 			v &= ~ESDHC_VENDOR_SPEC_SDIO_QUIRK;
60960bf6396SShawn Guo 			writel(v, host->ioaddr + ESDHC_VENDOR_SPEC);
610361b8482SLucas Stach 
611361b8482SLucas Stach 			if (imx_data->multiblock_status == MULTIBLK_IN_PROCESS)
612361b8482SLucas Stach 			{
613361b8482SLucas Stach 				/* send a manual CMD12 with RESPTYP=none */
614361b8482SLucas Stach 				data = MMC_STOP_TRANSMISSION << 24 |
615361b8482SLucas Stach 				       SDHCI_CMD_ABORTCMD << 16;
616361b8482SLucas Stach 				writel(data, host->ioaddr + SDHCI_TRANSFER_MODE);
617361b8482SLucas Stach 				imx_data->multiblock_status = WAIT_FOR_INT;
618361b8482SLucas Stach 			}
61958ac8177SRichard Zhu 	}
62058ac8177SRichard Zhu 
6217e29c306SWolfram Sang 	writel(val, host->ioaddr + reg);
6227e29c306SWolfram Sang }
6237e29c306SWolfram Sang 
62495f25efeSWolfram Sang static u16 esdhc_readw_le(struct sdhci_host *host, int reg)
62595f25efeSWolfram Sang {
626ef4d0888SShawn Guo 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
627070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
6280322191eSDong Aisheng 	u16 ret = 0;
6290322191eSDong Aisheng 	u32 val;
630ef4d0888SShawn Guo 
63195a2482aSShawn Guo 	if (unlikely(reg == SDHCI_HOST_VERSION)) {
632ef4d0888SShawn Guo 		reg ^= 2;
6339d61c009SShawn Guo 		if (esdhc_is_usdhc(imx_data)) {
63495a2482aSShawn Guo 			/*
635ef4d0888SShawn Guo 			 * The usdhc register returns a wrong host version.
636ef4d0888SShawn Guo 			 * Correct it here.
63795a2482aSShawn Guo 			 */
638ef4d0888SShawn Guo 			return SDHCI_SPEC_300;
639ef4d0888SShawn Guo 		}
64095a2482aSShawn Guo 	}
64195f25efeSWolfram Sang 
6420322191eSDong Aisheng 	if (unlikely(reg == SDHCI_HOST_CONTROL2)) {
6430322191eSDong Aisheng 		val = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
6440322191eSDong Aisheng 		if (val & ESDHC_VENDOR_SPEC_VSELECT)
6450322191eSDong Aisheng 			ret |= SDHCI_CTRL_VDD_180;
6460322191eSDong Aisheng 
6479d61c009SShawn Guo 		if (esdhc_is_usdhc(imx_data)) {
6486e9fd28eSDong Aisheng 			if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING)
6490322191eSDong Aisheng 				val = readl(host->ioaddr + ESDHC_MIX_CTRL);
6506e9fd28eSDong Aisheng 			else if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING)
6516e9fd28eSDong Aisheng 				/* the std tuning bits is in ACMD12_ERR for imx6sl */
652869f8a69SAdrian Hunter 				val = readl(host->ioaddr + SDHCI_AUTO_CMD_STATUS);
6536e9fd28eSDong Aisheng 		}
6546e9fd28eSDong Aisheng 
6550322191eSDong Aisheng 		if (val & ESDHC_MIX_CTRL_EXE_TUNE)
6560322191eSDong Aisheng 			ret |= SDHCI_CTRL_EXEC_TUNING;
6570322191eSDong Aisheng 		if (val & ESDHC_MIX_CTRL_SMPCLK_SEL)
6580322191eSDong Aisheng 			ret |= SDHCI_CTRL_TUNED_CLK;
6590322191eSDong Aisheng 
6600322191eSDong Aisheng 		ret &= ~SDHCI_CTRL_PRESET_VAL_ENABLE;
6610322191eSDong Aisheng 
6620322191eSDong Aisheng 		return ret;
6630322191eSDong Aisheng 	}
6640322191eSDong Aisheng 
6657dd109efSDong Aisheng 	if (unlikely(reg == SDHCI_TRANSFER_MODE)) {
6667dd109efSDong Aisheng 		if (esdhc_is_usdhc(imx_data)) {
6677dd109efSDong Aisheng 			u32 m = readl(host->ioaddr + ESDHC_MIX_CTRL);
6687dd109efSDong Aisheng 			ret = m & ESDHC_MIX_CTRL_SDHCI_MASK;
6697dd109efSDong Aisheng 			/* Swap AC23 bit */
6707dd109efSDong Aisheng 			if (m & ESDHC_MIX_CTRL_AC23EN) {
6717dd109efSDong Aisheng 				ret &= ~ESDHC_MIX_CTRL_AC23EN;
6727dd109efSDong Aisheng 				ret |= SDHCI_TRNS_AUTO_CMD23;
6737dd109efSDong Aisheng 			}
6747dd109efSDong Aisheng 		} else {
6757dd109efSDong Aisheng 			ret = readw(host->ioaddr + SDHCI_TRANSFER_MODE);
6767dd109efSDong Aisheng 		}
6777dd109efSDong Aisheng 
6787dd109efSDong Aisheng 		return ret;
6797dd109efSDong Aisheng 	}
6807dd109efSDong Aisheng 
68195f25efeSWolfram Sang 	return readw(host->ioaddr + reg);
68295f25efeSWolfram Sang }
68395f25efeSWolfram Sang 
68495f25efeSWolfram Sang static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg)
68595f25efeSWolfram Sang {
68695f25efeSWolfram Sang 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
687070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
6880322191eSDong Aisheng 	u32 new_val = 0;
68995f25efeSWolfram Sang 
69095f25efeSWolfram Sang 	switch (reg) {
6910322191eSDong Aisheng 	case SDHCI_CLOCK_CONTROL:
6920322191eSDong Aisheng 		new_val = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
6930322191eSDong Aisheng 		if (val & SDHCI_CLOCK_CARD_EN)
6940322191eSDong Aisheng 			new_val |= ESDHC_VENDOR_SPEC_FRC_SDCLK_ON;
6950322191eSDong Aisheng 		else
6960322191eSDong Aisheng 			new_val &= ~ESDHC_VENDOR_SPEC_FRC_SDCLK_ON;
6970322191eSDong Aisheng 		writel(new_val, host->ioaddr + ESDHC_VENDOR_SPEC);
698f581e909SHaibo Chen 		if (!(new_val & ESDHC_VENDOR_SPEC_FRC_SDCLK_ON))
699f581e909SHaibo Chen 			esdhc_wait_for_card_clock_gate_off(host);
7000322191eSDong Aisheng 		return;
7010322191eSDong Aisheng 	case SDHCI_HOST_CONTROL2:
7020322191eSDong Aisheng 		new_val = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
7030322191eSDong Aisheng 		if (val & SDHCI_CTRL_VDD_180)
7040322191eSDong Aisheng 			new_val |= ESDHC_VENDOR_SPEC_VSELECT;
7050322191eSDong Aisheng 		else
7060322191eSDong Aisheng 			new_val &= ~ESDHC_VENDOR_SPEC_VSELECT;
7070322191eSDong Aisheng 		writel(new_val, host->ioaddr + ESDHC_VENDOR_SPEC);
708a0dbbdc2SHaibo Chen 		if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) {
709869f8a69SAdrian Hunter 			u32 v = readl(host->ioaddr + SDHCI_AUTO_CMD_STATUS);
7106e9fd28eSDong Aisheng 			u32 m = readl(host->ioaddr + ESDHC_MIX_CTRL);
7118b2bb0adSDong Aisheng 			if (val & SDHCI_CTRL_TUNED_CLK) {
7128b2bb0adSDong Aisheng 				v |= ESDHC_MIX_CTRL_SMPCLK_SEL;
7136e9fd28eSDong Aisheng 			} else {
7148b2bb0adSDong Aisheng 				v &= ~ESDHC_MIX_CTRL_SMPCLK_SEL;
7156e9fd28eSDong Aisheng 				m &= ~ESDHC_MIX_CTRL_FBCLK_SEL;
7166e9fd28eSDong Aisheng 			}
7176e9fd28eSDong Aisheng 
7188b2bb0adSDong Aisheng 			if (val & SDHCI_CTRL_EXEC_TUNING) {
7198b2bb0adSDong Aisheng 				v |= ESDHC_MIX_CTRL_EXE_TUNE;
7208b2bb0adSDong Aisheng 				m |= ESDHC_MIX_CTRL_FBCLK_SEL;
7218b2bb0adSDong Aisheng 			} else {
7228b2bb0adSDong Aisheng 				v &= ~ESDHC_MIX_CTRL_EXE_TUNE;
7238b2bb0adSDong Aisheng 			}
7246e9fd28eSDong Aisheng 
725869f8a69SAdrian Hunter 			writel(v, host->ioaddr + SDHCI_AUTO_CMD_STATUS);
7266e9fd28eSDong Aisheng 			writel(m, host->ioaddr + ESDHC_MIX_CTRL);
7276e9fd28eSDong Aisheng 		}
7280322191eSDong Aisheng 		return;
72995f25efeSWolfram Sang 	case SDHCI_TRANSFER_MODE:
730f47c4bbfSShawn Guo 		if ((imx_data->socdata->flags & ESDHC_FLAG_MULTIBLK_NO_INT)
73158ac8177SRichard Zhu 				&& (host->cmd->opcode == SD_IO_RW_EXTENDED)
73258ac8177SRichard Zhu 				&& (host->cmd->data->blocks > 1)
73358ac8177SRichard Zhu 				&& (host->cmd->data->flags & MMC_DATA_READ)) {
73458ac8177SRichard Zhu 			u32 v;
73560bf6396SShawn Guo 			v = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
73660bf6396SShawn Guo 			v |= ESDHC_VENDOR_SPEC_SDIO_QUIRK;
73760bf6396SShawn Guo 			writel(v, host->ioaddr + ESDHC_VENDOR_SPEC);
73858ac8177SRichard Zhu 		}
73969f54698SShawn Guo 
7409d61c009SShawn Guo 		if (esdhc_is_usdhc(imx_data)) {
7413fbd4322SAndrew Gabbasov 			u32 wml;
74269f54698SShawn Guo 			u32 m = readl(host->ioaddr + ESDHC_MIX_CTRL);
7432a15f981SShawn Guo 			/* Swap AC23 bit */
7442a15f981SShawn Guo 			if (val & SDHCI_TRNS_AUTO_CMD23) {
7452a15f981SShawn Guo 				val &= ~SDHCI_TRNS_AUTO_CMD23;
7462a15f981SShawn Guo 				val |= ESDHC_MIX_CTRL_AC23EN;
7472a15f981SShawn Guo 			}
7482a15f981SShawn Guo 			m = val | (m & ~ESDHC_MIX_CTRL_SDHCI_MASK);
74969f54698SShawn Guo 			writel(m, host->ioaddr + ESDHC_MIX_CTRL);
7503fbd4322SAndrew Gabbasov 
7513fbd4322SAndrew Gabbasov 			/* Set watermark levels for PIO access to maximum value
7523fbd4322SAndrew Gabbasov 			 * (128 words) to accommodate full 512 bytes buffer.
7533fbd4322SAndrew Gabbasov 			 * For DMA access restore the levels to default value.
7543fbd4322SAndrew Gabbasov 			 */
7553fbd4322SAndrew Gabbasov 			m = readl(host->ioaddr + ESDHC_WTMK_LVL);
756e534b82fSHaibo Chen 			if (val & SDHCI_TRNS_DMA) {
7573fbd4322SAndrew Gabbasov 				wml = ESDHC_WTMK_LVL_WML_VAL_DEF;
758e534b82fSHaibo Chen 			} else {
759e534b82fSHaibo Chen 				u8 ctrl;
7603fbd4322SAndrew Gabbasov 				wml = ESDHC_WTMK_LVL_WML_VAL_MAX;
761e534b82fSHaibo Chen 
762e534b82fSHaibo Chen 				/*
763e534b82fSHaibo Chen 				 * Since already disable DMA mode, so also need
764e534b82fSHaibo Chen 				 * to clear the DMASEL. Otherwise, for standard
765e534b82fSHaibo Chen 				 * tuning, when send tuning command, usdhc will
766e534b82fSHaibo Chen 				 * still prefetch the ADMA script from wrong
767e534b82fSHaibo Chen 				 * DMA address, then we will see IOMMU report
768e534b82fSHaibo Chen 				 * some error which show lack of TLB mapping.
769e534b82fSHaibo Chen 				 */
770e534b82fSHaibo Chen 				ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
771e534b82fSHaibo Chen 				ctrl &= ~SDHCI_CTRL_DMA_MASK;
772e534b82fSHaibo Chen 				sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
773e534b82fSHaibo Chen 			}
7743fbd4322SAndrew Gabbasov 			m &= ~(ESDHC_WTMK_LVL_RD_WML_MASK |
7753fbd4322SAndrew Gabbasov 			       ESDHC_WTMK_LVL_WR_WML_MASK);
7763fbd4322SAndrew Gabbasov 			m |= (wml << ESDHC_WTMK_LVL_RD_WML_SHIFT) |
7773fbd4322SAndrew Gabbasov 			     (wml << ESDHC_WTMK_LVL_WR_WML_SHIFT);
7783fbd4322SAndrew Gabbasov 			writel(m, host->ioaddr + ESDHC_WTMK_LVL);
77969f54698SShawn Guo 		} else {
78069f54698SShawn Guo 			/*
78169f54698SShawn Guo 			 * Postpone this write, we must do it together with a
78269f54698SShawn Guo 			 * command write that is down below.
78369f54698SShawn Guo 			 */
784e149860dSRichard Zhu 			imx_data->scratchpad = val;
78569f54698SShawn Guo 		}
78695f25efeSWolfram Sang 		return;
78795f25efeSWolfram Sang 	case SDHCI_COMMAND:
788361b8482SLucas Stach 		if (host->cmd->opcode == MMC_STOP_TRANSMISSION)
78958ac8177SRichard Zhu 			val |= SDHCI_CMD_ABORTCMD;
79095a2482aSShawn Guo 
791361b8482SLucas Stach 		if ((host->cmd->opcode == MMC_SET_BLOCK_COUNT) &&
792f47c4bbfSShawn Guo 		    (imx_data->socdata->flags & ESDHC_FLAG_MULTIBLK_NO_INT))
793361b8482SLucas Stach 			imx_data->multiblock_status = MULTIBLK_IN_PROCESS;
794361b8482SLucas Stach 
7959d61c009SShawn Guo 		if (esdhc_is_usdhc(imx_data))
79695a2482aSShawn Guo 			writel(val << 16,
79795a2482aSShawn Guo 			       host->ioaddr + SDHCI_TRANSFER_MODE);
79869f54698SShawn Guo 		else
799e149860dSRichard Zhu 			writel(val << 16 | imx_data->scratchpad,
80095f25efeSWolfram Sang 			       host->ioaddr + SDHCI_TRANSFER_MODE);
80195f25efeSWolfram Sang 		return;
80295f25efeSWolfram Sang 	case SDHCI_BLOCK_SIZE:
80395f25efeSWolfram Sang 		val &= ~SDHCI_MAKE_BLKSZ(0x7, 0);
80495f25efeSWolfram Sang 		break;
80595f25efeSWolfram Sang 	}
80695f25efeSWolfram Sang 	esdhc_clrset_le(host, 0xffff, val, reg);
80795f25efeSWolfram Sang }
80895f25efeSWolfram Sang 
80977da3da0SAaron Brice static u8 esdhc_readb_le(struct sdhci_host *host, int reg)
81077da3da0SAaron Brice {
81177da3da0SAaron Brice 	u8 ret;
81277da3da0SAaron Brice 	u32 val;
81377da3da0SAaron Brice 
81477da3da0SAaron Brice 	switch (reg) {
81577da3da0SAaron Brice 	case SDHCI_HOST_CONTROL:
81677da3da0SAaron Brice 		val = readl(host->ioaddr + reg);
81777da3da0SAaron Brice 
81877da3da0SAaron Brice 		ret = val & SDHCI_CTRL_LED;
81977da3da0SAaron Brice 		ret |= (val >> 5) & SDHCI_CTRL_DMA_MASK;
82077da3da0SAaron Brice 		ret |= (val & ESDHC_CTRL_4BITBUS);
82177da3da0SAaron Brice 		ret |= (val & ESDHC_CTRL_8BITBUS) << 3;
82277da3da0SAaron Brice 		return ret;
82377da3da0SAaron Brice 	}
82477da3da0SAaron Brice 
82577da3da0SAaron Brice 	return readb(host->ioaddr + reg);
82677da3da0SAaron Brice }
82777da3da0SAaron Brice 
82895f25efeSWolfram Sang static void esdhc_writeb_le(struct sdhci_host *host, u8 val, int reg)
82995f25efeSWolfram Sang {
8309a0985b7SWilson Callan 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
831070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
83281a0a8bcSBenoît Thébaudeau 	u32 new_val = 0;
833af51079eSSascha Hauer 	u32 mask;
83495f25efeSWolfram Sang 
83595f25efeSWolfram Sang 	switch (reg) {
83695f25efeSWolfram Sang 	case SDHCI_POWER_CONTROL:
83795f25efeSWolfram Sang 		/*
83895f25efeSWolfram Sang 		 * FSL put some DMA bits here
83995f25efeSWolfram Sang 		 * If your board has a regulator, code should be here
84095f25efeSWolfram Sang 		 */
84195f25efeSWolfram Sang 		return;
84295f25efeSWolfram Sang 	case SDHCI_HOST_CONTROL:
8436b40d182SShawn Guo 		/* FSL messed up here, so we need to manually compose it. */
844af51079eSSascha Hauer 		new_val = val & SDHCI_CTRL_LED;
8457122bbb0SMasanari Iida 		/* ensure the endianness */
84695f25efeSWolfram Sang 		new_val |= ESDHC_HOST_CONTROL_LE;
8479a0985b7SWilson Callan 		/* bits 8&9 are reserved on mx25 */
8489a0985b7SWilson Callan 		if (!is_imx25_esdhc(imx_data)) {
84995f25efeSWolfram Sang 			/* DMA mode bits are shifted */
85095f25efeSWolfram Sang 			new_val |= (val & SDHCI_CTRL_DMA_MASK) << 5;
8519a0985b7SWilson Callan 		}
85295f25efeSWolfram Sang 
853af51079eSSascha Hauer 		/*
854af51079eSSascha Hauer 		 * Do not touch buswidth bits here. This is done in
855af51079eSSascha Hauer 		 * esdhc_pltfm_bus_width.
856f6825748SMartin Fuzzey 		 * Do not touch the D3CD bit either which is used for the
857d04f8d5bSBenoît Thébaudeau 		 * SDIO interrupt erratum workaround.
858af51079eSSascha Hauer 		 */
859f6825748SMartin Fuzzey 		mask = 0xffff & ~(ESDHC_CTRL_BUSWIDTH_MASK | ESDHC_CTRL_D3CD);
860af51079eSSascha Hauer 
861af51079eSSascha Hauer 		esdhc_clrset_le(host, mask, new_val, reg);
86295f25efeSWolfram Sang 		return;
86381a0a8bcSBenoît Thébaudeau 	case SDHCI_SOFTWARE_RESET:
86481a0a8bcSBenoît Thébaudeau 		if (val & SDHCI_RESET_DATA)
86581a0a8bcSBenoît Thébaudeau 			new_val = readl(host->ioaddr + SDHCI_HOST_CONTROL);
86681a0a8bcSBenoît Thébaudeau 		break;
86795f25efeSWolfram Sang 	}
86895f25efeSWolfram Sang 	esdhc_clrset_le(host, 0xff, val, reg);
869913413c3SShawn Guo 
87081a0a8bcSBenoît Thébaudeau 	if (reg == SDHCI_SOFTWARE_RESET) {
87181a0a8bcSBenoît Thébaudeau 		if (val & SDHCI_RESET_ALL) {
872913413c3SShawn Guo 			/*
87381a0a8bcSBenoît Thébaudeau 			 * The esdhc has a design violation to SDHC spec which
87481a0a8bcSBenoît Thébaudeau 			 * tells that software reset should not affect card
87581a0a8bcSBenoît Thébaudeau 			 * detection circuit. But esdhc clears its SYSCTL
87681a0a8bcSBenoît Thébaudeau 			 * register bits [0..2] during the software reset. This
87781a0a8bcSBenoît Thébaudeau 			 * will stop those clocks that card detection circuit
87881a0a8bcSBenoît Thébaudeau 			 * relies on. To work around it, we turn the clocks on
87981a0a8bcSBenoît Thébaudeau 			 * back to keep card detection circuit functional.
880913413c3SShawn Guo 			 */
881913413c3SShawn Guo 			esdhc_clrset_le(host, 0x7, 0x7, ESDHC_SYSTEM_CONTROL);
88258c8c4fbSShawn Guo 			/*
88358c8c4fbSShawn Guo 			 * The reset on usdhc fails to clear MIX_CTRL register.
88458c8c4fbSShawn Guo 			 * Do it manually here.
88558c8c4fbSShawn Guo 			 */
886de5bdbffSDong Aisheng 			if (esdhc_is_usdhc(imx_data)) {
88781a0a8bcSBenoît Thébaudeau 				/*
88881a0a8bcSBenoît Thébaudeau 				 * the tuning bits should be kept during reset
88981a0a8bcSBenoît Thébaudeau 				 */
890d131a71cSDong Aisheng 				new_val = readl(host->ioaddr + ESDHC_MIX_CTRL);
891d131a71cSDong Aisheng 				writel(new_val & ESDHC_MIX_CTRL_TUNING_MASK,
892d131a71cSDong Aisheng 						host->ioaddr + ESDHC_MIX_CTRL);
893de5bdbffSDong Aisheng 				imx_data->is_ddr = 0;
894de5bdbffSDong Aisheng 			}
89581a0a8bcSBenoît Thébaudeau 		} else if (val & SDHCI_RESET_DATA) {
89681a0a8bcSBenoît Thébaudeau 			/*
89781a0a8bcSBenoît Thébaudeau 			 * The eSDHC DAT line software reset clears at least the
89881a0a8bcSBenoît Thébaudeau 			 * data transfer width on i.MX25, so make sure that the
89981a0a8bcSBenoît Thébaudeau 			 * Host Control register is unaffected.
90081a0a8bcSBenoît Thébaudeau 			 */
90181a0a8bcSBenoît Thébaudeau 			esdhc_clrset_le(host, 0xff, new_val,
90281a0a8bcSBenoît Thébaudeau 					SDHCI_HOST_CONTROL);
90381a0a8bcSBenoît Thébaudeau 		}
90458c8c4fbSShawn Guo 	}
90595f25efeSWolfram Sang }
90695f25efeSWolfram Sang 
9070ddf03c9SLucas Stach static unsigned int esdhc_pltfm_get_max_clock(struct sdhci_host *host)
9080ddf03c9SLucas Stach {
9090ddf03c9SLucas Stach 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
9100ddf03c9SLucas Stach 
911a974862fSDong Aisheng 	return pltfm_host->clock;
9120ddf03c9SLucas Stach }
9130ddf03c9SLucas Stach 
91495f25efeSWolfram Sang static unsigned int esdhc_pltfm_get_min_clock(struct sdhci_host *host)
91595f25efeSWolfram Sang {
91695f25efeSWolfram Sang 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
91795f25efeSWolfram Sang 
918a974862fSDong Aisheng 	return pltfm_host->clock / 256 / 16;
91995f25efeSWolfram Sang }
92095f25efeSWolfram Sang 
9218ba9580aSLucas Stach static inline void esdhc_pltfm_set_clock(struct sdhci_host *host,
9228ba9580aSLucas Stach 					 unsigned int clock)
9238ba9580aSLucas Stach {
9248ba9580aSLucas Stach 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
925070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
926a974862fSDong Aisheng 	unsigned int host_clock = pltfm_host->clock;
9275143c953SBenoît Thébaudeau 	int ddr_pre_div = imx_data->is_ddr ? 2 : 1;
9285143c953SBenoît Thébaudeau 	int pre_div = 1;
929d31fc00aSDong Aisheng 	int div = 1;
930f581e909SHaibo Chen 	int ret;
931fed2f6e2SDong Aisheng 	u32 temp, val;
9328ba9580aSLucas Stach 
9339d61c009SShawn Guo 	if (esdhc_is_usdhc(imx_data)) {
934fed2f6e2SDong Aisheng 		val = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
935fed2f6e2SDong Aisheng 		writel(val & ~ESDHC_VENDOR_SPEC_FRC_SDCLK_ON,
936fed2f6e2SDong Aisheng 			host->ioaddr + ESDHC_VENDOR_SPEC);
937f581e909SHaibo Chen 		esdhc_wait_for_card_clock_gate_off(host);
938fed2f6e2SDong Aisheng 	}
93973e736f8SStefan Agner 
94073e736f8SStefan Agner 	if (clock == 0) {
94173e736f8SStefan Agner 		host->mmc->actual_clock = 0;
942373073efSRussell King 		return;
943fed2f6e2SDong Aisheng 	}
944d31fc00aSDong Aisheng 
945499ed50fSBenoît Thébaudeau 	/* For i.MX53 eSDHCv3, SYSCTL.SDCLKFS may not be set to 0. */
946499ed50fSBenoît Thébaudeau 	if (is_imx53_esdhc(imx_data)) {
947499ed50fSBenoît Thébaudeau 		/*
948499ed50fSBenoît Thébaudeau 		 * According to the i.MX53 reference manual, if DLLCTRL[10] can
949499ed50fSBenoît Thébaudeau 		 * be set, then the controller is eSDHCv3, else it is eSDHCv2.
950499ed50fSBenoît Thébaudeau 		 */
951499ed50fSBenoît Thébaudeau 		val = readl(host->ioaddr + ESDHC_DLL_CTRL);
952499ed50fSBenoît Thébaudeau 		writel(val | BIT(10), host->ioaddr + ESDHC_DLL_CTRL);
953499ed50fSBenoît Thébaudeau 		temp = readl(host->ioaddr + ESDHC_DLL_CTRL);
954499ed50fSBenoît Thébaudeau 		writel(val, host->ioaddr + ESDHC_DLL_CTRL);
955499ed50fSBenoît Thébaudeau 		if (temp & BIT(10))
956499ed50fSBenoît Thébaudeau 			pre_div = 2;
957499ed50fSBenoît Thébaudeau 	}
958499ed50fSBenoît Thébaudeau 
959d31fc00aSDong Aisheng 	temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
960d31fc00aSDong Aisheng 	temp &= ~(ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN
961d31fc00aSDong Aisheng 		| ESDHC_CLOCK_MASK);
962d31fc00aSDong Aisheng 	sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
963d31fc00aSDong Aisheng 
964af6a50d4SBOUGH CHEN 	if (imx_data->socdata->flags & ESDHC_FLAG_ERR010450) {
965af6a50d4SBOUGH CHEN 		unsigned int max_clock;
966af6a50d4SBOUGH CHEN 
967af6a50d4SBOUGH CHEN 		max_clock = imx_data->is_ddr ? 45000000 : 150000000;
968af6a50d4SBOUGH CHEN 
969af6a50d4SBOUGH CHEN 		clock = min(clock, max_clock);
970af6a50d4SBOUGH CHEN 	}
971af6a50d4SBOUGH CHEN 
9725143c953SBenoît Thébaudeau 	while (host_clock / (16 * pre_div * ddr_pre_div) > clock &&
9735143c953SBenoît Thébaudeau 			pre_div < 256)
974d31fc00aSDong Aisheng 		pre_div *= 2;
975d31fc00aSDong Aisheng 
9765143c953SBenoît Thébaudeau 	while (host_clock / (div * pre_div * ddr_pre_div) > clock && div < 16)
977d31fc00aSDong Aisheng 		div++;
978d31fc00aSDong Aisheng 
9795143c953SBenoît Thébaudeau 	host->mmc->actual_clock = host_clock / (div * pre_div * ddr_pre_div);
980d31fc00aSDong Aisheng 	dev_dbg(mmc_dev(host->mmc), "desired SD clock: %d, actual: %d\n",
981e76b8559SDong Aisheng 		clock, host->mmc->actual_clock);
982d31fc00aSDong Aisheng 
983d31fc00aSDong Aisheng 	pre_div >>= 1;
984d31fc00aSDong Aisheng 	div--;
985d31fc00aSDong Aisheng 
986d31fc00aSDong Aisheng 	temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
987d31fc00aSDong Aisheng 	temp |= (ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN
988d31fc00aSDong Aisheng 		| (div << ESDHC_DIVIDER_SHIFT)
989d31fc00aSDong Aisheng 		| (pre_div << ESDHC_PREDIV_SHIFT));
990d31fc00aSDong Aisheng 	sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
991fed2f6e2SDong Aisheng 
992f581e909SHaibo Chen 	/* need to wait the bit 3 of the PRSSTAT to be set, make sure card clock is stable */
993f581e909SHaibo Chen 	ret = readl_poll_timeout(host->ioaddr + ESDHC_PRSSTAT, temp,
994f581e909SHaibo Chen 				(temp & ESDHC_CLOCK_STABLE), 2, 100);
995f581e909SHaibo Chen 	if (ret == -ETIMEDOUT)
996f581e909SHaibo Chen 		dev_warn(mmc_dev(host->mmc), "card clock still not stable in 100us!.\n");
997f581e909SHaibo Chen 
9989d61c009SShawn Guo 	if (esdhc_is_usdhc(imx_data)) {
999fed2f6e2SDong Aisheng 		val = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
1000fed2f6e2SDong Aisheng 		writel(val | ESDHC_VENDOR_SPEC_FRC_SDCLK_ON,
1001fed2f6e2SDong Aisheng 			host->ioaddr + ESDHC_VENDOR_SPEC);
1002fed2f6e2SDong Aisheng 	}
1003fed2f6e2SDong Aisheng 
10048ba9580aSLucas Stach }
10058ba9580aSLucas Stach 
1006913413c3SShawn Guo static unsigned int esdhc_pltfm_get_ro(struct sdhci_host *host)
1007913413c3SShawn Guo {
1008842afc02SShawn Guo 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1009070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
1010842afc02SShawn Guo 	struct esdhc_platform_data *boarddata = &imx_data->boarddata;
1011913413c3SShawn Guo 
1012913413c3SShawn Guo 	switch (boarddata->wp_type) {
1013913413c3SShawn Guo 	case ESDHC_WP_GPIO:
1014fbe5fdd1SShawn Guo 		return mmc_gpio_get_ro(host->mmc);
1015913413c3SShawn Guo 	case ESDHC_WP_CONTROLLER:
1016913413c3SShawn Guo 		return !(readl(host->ioaddr + SDHCI_PRESENT_STATE) &
1017913413c3SShawn Guo 			       SDHCI_WRITE_PROTECT);
1018913413c3SShawn Guo 	case ESDHC_WP_NONE:
1019913413c3SShawn Guo 		break;
1020913413c3SShawn Guo 	}
1021913413c3SShawn Guo 
1022913413c3SShawn Guo 	return -ENOSYS;
1023913413c3SShawn Guo }
1024913413c3SShawn Guo 
10252317f56cSRussell King static void esdhc_pltfm_set_bus_width(struct sdhci_host *host, int width)
1026af51079eSSascha Hauer {
1027af51079eSSascha Hauer 	u32 ctrl;
1028af51079eSSascha Hauer 
1029af51079eSSascha Hauer 	switch (width) {
1030af51079eSSascha Hauer 	case MMC_BUS_WIDTH_8:
1031af51079eSSascha Hauer 		ctrl = ESDHC_CTRL_8BITBUS;
1032af51079eSSascha Hauer 		break;
1033af51079eSSascha Hauer 	case MMC_BUS_WIDTH_4:
1034af51079eSSascha Hauer 		ctrl = ESDHC_CTRL_4BITBUS;
1035af51079eSSascha Hauer 		break;
1036af51079eSSascha Hauer 	default:
1037af51079eSSascha Hauer 		ctrl = 0;
1038af51079eSSascha Hauer 		break;
1039af51079eSSascha Hauer 	}
1040af51079eSSascha Hauer 
1041af51079eSSascha Hauer 	esdhc_clrset_le(host, ESDHC_CTRL_BUSWIDTH_MASK, ctrl,
1042af51079eSSascha Hauer 			SDHCI_HOST_CONTROL);
1043af51079eSSascha Hauer }
1044af51079eSSascha Hauer 
10454fb27869SHaibo Chen static void esdhc_reset_tuning(struct sdhci_host *host)
10464fb27869SHaibo Chen {
10474fb27869SHaibo Chen 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
10484fb27869SHaibo Chen 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
10494fb27869SHaibo Chen 	u32 ctrl;
10504fb27869SHaibo Chen 	int ret;
10514fb27869SHaibo Chen 
10524fb27869SHaibo Chen 	/* Reset the tuning circuit */
10534fb27869SHaibo Chen 	if (esdhc_is_usdhc(imx_data)) {
10544fb27869SHaibo Chen 		ctrl = readl(host->ioaddr + ESDHC_MIX_CTRL);
1055c8c49a5aSHaibo Chen 		ctrl &= ~ESDHC_MIX_CTRL_AUTO_TUNE_EN;
1056c8c49a5aSHaibo Chen 		if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING) {
10574fb27869SHaibo Chen 			ctrl &= ~ESDHC_MIX_CTRL_SMPCLK_SEL;
10584fb27869SHaibo Chen 			ctrl &= ~ESDHC_MIX_CTRL_FBCLK_SEL;
10594fb27869SHaibo Chen 			writel(ctrl, host->ioaddr + ESDHC_MIX_CTRL);
10604fb27869SHaibo Chen 			writel(0, host->ioaddr + ESDHC_TUNE_CTRL_STATUS);
10614fb27869SHaibo Chen 		} else if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) {
1062c8c49a5aSHaibo Chen 			writel(ctrl, host->ioaddr + ESDHC_MIX_CTRL);
10634fb27869SHaibo Chen 			ctrl = readl(host->ioaddr + SDHCI_AUTO_CMD_STATUS);
10644fb27869SHaibo Chen 			ctrl &= ~ESDHC_MIX_CTRL_SMPCLK_SEL;
10654fb27869SHaibo Chen 			ctrl &= ~ESDHC_MIX_CTRL_EXE_TUNE;
10664fb27869SHaibo Chen 			writel(ctrl, host->ioaddr + SDHCI_AUTO_CMD_STATUS);
10674fb27869SHaibo Chen 			/* Make sure ESDHC_MIX_CTRL_EXE_TUNE cleared */
10684fb27869SHaibo Chen 			ret = readl_poll_timeout(host->ioaddr + SDHCI_AUTO_CMD_STATUS,
10694fb27869SHaibo Chen 				ctrl, !(ctrl & ESDHC_MIX_CTRL_EXE_TUNE), 1, 50);
10704fb27869SHaibo Chen 			if (ret == -ETIMEDOUT)
10714fb27869SHaibo Chen 				dev_warn(mmc_dev(host->mmc),
10724fb27869SHaibo Chen 				 "Warning! clear execute tuning bit failed\n");
10734fb27869SHaibo Chen 			/*
10744fb27869SHaibo Chen 			 * SDHCI_INT_DATA_AVAIL is W1C bit, set this bit will clear the
10754fb27869SHaibo Chen 			 * usdhc IP internal logic flag execute_tuning_with_clr_buf, which
10764fb27869SHaibo Chen 			 * will finally make sure the normal data transfer logic correct.
10774fb27869SHaibo Chen 			 */
10784fb27869SHaibo Chen 			ctrl = readl(host->ioaddr + SDHCI_INT_STATUS);
10794fb27869SHaibo Chen 			ctrl |= SDHCI_INT_DATA_AVAIL;
10804fb27869SHaibo Chen 			writel(ctrl, host->ioaddr + SDHCI_INT_STATUS);
10814fb27869SHaibo Chen 		}
10824fb27869SHaibo Chen 	}
10834fb27869SHaibo Chen }
10844fb27869SHaibo Chen 
108552e4c32bSHaibo Chen static void usdhc_init_card(struct mmc_host *mmc, struct mmc_card *card)
108652e4c32bSHaibo Chen {
108752e4c32bSHaibo Chen 	struct sdhci_host *host = mmc_priv(mmc);
108852e4c32bSHaibo Chen 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
108952e4c32bSHaibo Chen 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
109052e4c32bSHaibo Chen 
109152e4c32bSHaibo Chen 	imx_data->init_card_type = card->type;
109252e4c32bSHaibo Chen }
109352e4c32bSHaibo Chen 
1094de3e1dd0SBOUGH CHEN static int usdhc_execute_tuning(struct mmc_host *mmc, u32 opcode)
1095de3e1dd0SBOUGH CHEN {
1096de3e1dd0SBOUGH CHEN 	struct sdhci_host *host = mmc_priv(mmc);
1097c8c49a5aSHaibo Chen 	int err;
1098de3e1dd0SBOUGH CHEN 
1099de3e1dd0SBOUGH CHEN 	/*
1100de3e1dd0SBOUGH CHEN 	 * i.MX uSDHC internally already uses a fixed optimized timing for
1101de3e1dd0SBOUGH CHEN 	 * DDR50, normally does not require tuning for DDR50 mode.
1102de3e1dd0SBOUGH CHEN 	 */
1103de3e1dd0SBOUGH CHEN 	if (host->timing == MMC_TIMING_UHS_DDR50)
1104de3e1dd0SBOUGH CHEN 		return 0;
1105de3e1dd0SBOUGH CHEN 
11064fb27869SHaibo Chen 	/*
11074fb27869SHaibo Chen 	 * Reset tuning circuit logic. If not, the previous tuning result
11084fb27869SHaibo Chen 	 * will impact current tuning, make current tuning can't set the
11094fb27869SHaibo Chen 	 * correct delay cell.
11104fb27869SHaibo Chen 	 */
11114fb27869SHaibo Chen 	esdhc_reset_tuning(host);
1112c8c49a5aSHaibo Chen 	err = sdhci_execute_tuning(mmc, opcode);
1113c8c49a5aSHaibo Chen 	/* If tuning done, enable auto tuning */
1114c8c49a5aSHaibo Chen 	if (!err && !host->tuning_err)
1115c8c49a5aSHaibo Chen 		usdhc_auto_tuning_mode_sel_and_en(host);
1116c8c49a5aSHaibo Chen 
1117c8c49a5aSHaibo Chen 	return err;
1118de3e1dd0SBOUGH CHEN }
1119de3e1dd0SBOUGH CHEN 
11200322191eSDong Aisheng static void esdhc_prepare_tuning(struct sdhci_host *host, u32 val)
11210322191eSDong Aisheng {
11220322191eSDong Aisheng 	u32 reg;
11230ac4f496SHaibo Chen 	u8 sw_rst;
11240ac4f496SHaibo Chen 	int ret;
11250322191eSDong Aisheng 
11260322191eSDong Aisheng 	/* FIXME: delay a bit for card to be ready for next tuning due to errors */
11270322191eSDong Aisheng 	mdelay(1);
11280322191eSDong Aisheng 
11290ac4f496SHaibo Chen 	/* IC suggest to reset USDHC before every tuning command */
11300ac4f496SHaibo Chen 	esdhc_clrset_le(host, 0xff, SDHCI_RESET_ALL, SDHCI_SOFTWARE_RESET);
11310ac4f496SHaibo Chen 	ret = readb_poll_timeout(host->ioaddr + SDHCI_SOFTWARE_RESET, sw_rst,
11320ac4f496SHaibo Chen 				!(sw_rst & SDHCI_RESET_ALL), 10, 100);
11330ac4f496SHaibo Chen 	if (ret == -ETIMEDOUT)
11340ac4f496SHaibo Chen 		dev_warn(mmc_dev(host->mmc),
11350ac4f496SHaibo Chen 		"warning! RESET_ALL never complete before sending tuning command\n");
11360ac4f496SHaibo Chen 
11370322191eSDong Aisheng 	reg = readl(host->ioaddr + ESDHC_MIX_CTRL);
11380322191eSDong Aisheng 	reg |= ESDHC_MIX_CTRL_EXE_TUNE | ESDHC_MIX_CTRL_SMPCLK_SEL |
11390322191eSDong Aisheng 			ESDHC_MIX_CTRL_FBCLK_SEL;
11400322191eSDong Aisheng 	writel(reg, host->ioaddr + ESDHC_MIX_CTRL);
11410322191eSDong Aisheng 	writel(val << 8, host->ioaddr + ESDHC_TUNE_CTRL_STATUS);
11420322191eSDong Aisheng 	dev_dbg(mmc_dev(host->mmc),
1143d04f8d5bSBenoît Thébaudeau 		"tuning with delay 0x%x ESDHC_TUNE_CTRL_STATUS 0x%x\n",
11440322191eSDong Aisheng 			val, readl(host->ioaddr + ESDHC_TUNE_CTRL_STATUS));
11450322191eSDong Aisheng }
11460322191eSDong Aisheng 
11470322191eSDong Aisheng static void esdhc_post_tuning(struct sdhci_host *host)
11480322191eSDong Aisheng {
11490322191eSDong Aisheng 	u32 reg;
11500322191eSDong Aisheng 
11510322191eSDong Aisheng 	reg = readl(host->ioaddr + ESDHC_MIX_CTRL);
11520322191eSDong Aisheng 	reg &= ~ESDHC_MIX_CTRL_EXE_TUNE;
11530322191eSDong Aisheng 	writel(reg, host->ioaddr + ESDHC_MIX_CTRL);
11540322191eSDong Aisheng }
11550322191eSDong Aisheng 
11560322191eSDong Aisheng static int esdhc_executing_tuning(struct sdhci_host *host, u32 opcode)
11570322191eSDong Aisheng {
11580322191eSDong Aisheng 	int min, max, avg, ret;
11590322191eSDong Aisheng 
11600322191eSDong Aisheng 	/* find the mininum delay first which can pass tuning */
11610322191eSDong Aisheng 	min = ESDHC_TUNE_CTRL_MIN;
11620322191eSDong Aisheng 	while (min < ESDHC_TUNE_CTRL_MAX) {
11630322191eSDong Aisheng 		esdhc_prepare_tuning(host, min);
11649979dbe5SChaotian Jing 		if (!mmc_send_tuning(host->mmc, opcode, NULL))
11650322191eSDong Aisheng 			break;
11660322191eSDong Aisheng 		min += ESDHC_TUNE_CTRL_STEP;
11670322191eSDong Aisheng 	}
11680322191eSDong Aisheng 
11690322191eSDong Aisheng 	/* find the maxinum delay which can not pass tuning */
11700322191eSDong Aisheng 	max = min + ESDHC_TUNE_CTRL_STEP;
11710322191eSDong Aisheng 	while (max < ESDHC_TUNE_CTRL_MAX) {
11720322191eSDong Aisheng 		esdhc_prepare_tuning(host, max);
11739979dbe5SChaotian Jing 		if (mmc_send_tuning(host->mmc, opcode, NULL)) {
11740322191eSDong Aisheng 			max -= ESDHC_TUNE_CTRL_STEP;
11750322191eSDong Aisheng 			break;
11760322191eSDong Aisheng 		}
11770322191eSDong Aisheng 		max += ESDHC_TUNE_CTRL_STEP;
11780322191eSDong Aisheng 	}
11790322191eSDong Aisheng 
11800322191eSDong Aisheng 	/* use average delay to get the best timing */
11810322191eSDong Aisheng 	avg = (min + max) / 2;
11820322191eSDong Aisheng 	esdhc_prepare_tuning(host, avg);
11839979dbe5SChaotian Jing 	ret = mmc_send_tuning(host->mmc, opcode, NULL);
11840322191eSDong Aisheng 	esdhc_post_tuning(host);
11850322191eSDong Aisheng 
1186d04f8d5bSBenoît Thébaudeau 	dev_dbg(mmc_dev(host->mmc), "tuning %s at 0x%x ret %d\n",
11870322191eSDong Aisheng 		ret ? "failed" : "passed", avg, ret);
11880322191eSDong Aisheng 
11890322191eSDong Aisheng 	return ret;
11900322191eSDong Aisheng }
11910322191eSDong Aisheng 
1192029e2476SBOUGH CHEN static void esdhc_hs400_enhanced_strobe(struct mmc_host *mmc, struct mmc_ios *ios)
1193029e2476SBOUGH CHEN {
1194029e2476SBOUGH CHEN 	struct sdhci_host *host = mmc_priv(mmc);
1195029e2476SBOUGH CHEN 	u32 m;
1196029e2476SBOUGH CHEN 
1197029e2476SBOUGH CHEN 	m = readl(host->ioaddr + ESDHC_MIX_CTRL);
1198029e2476SBOUGH CHEN 	if (ios->enhanced_strobe)
1199029e2476SBOUGH CHEN 		m |= ESDHC_MIX_CTRL_HS400_ES_EN;
1200029e2476SBOUGH CHEN 	else
1201029e2476SBOUGH CHEN 		m &= ~ESDHC_MIX_CTRL_HS400_ES_EN;
1202029e2476SBOUGH CHEN 	writel(m, host->ioaddr + ESDHC_MIX_CTRL);
1203029e2476SBOUGH CHEN }
1204029e2476SBOUGH CHEN 
1205ad93220dSDong Aisheng static int esdhc_change_pinstate(struct sdhci_host *host,
1206ad93220dSDong Aisheng 						unsigned int uhs)
1207ad93220dSDong Aisheng {
1208ad93220dSDong Aisheng 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1209070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
1210ad93220dSDong Aisheng 	struct pinctrl_state *pinctrl;
1211ad93220dSDong Aisheng 
1212ad93220dSDong Aisheng 	dev_dbg(mmc_dev(host->mmc), "change pinctrl state for uhs %d\n", uhs);
1213ad93220dSDong Aisheng 
1214ad93220dSDong Aisheng 	if (IS_ERR(imx_data->pinctrl) ||
1215ad93220dSDong Aisheng 		IS_ERR(imx_data->pins_100mhz) ||
1216ad93220dSDong Aisheng 		IS_ERR(imx_data->pins_200mhz))
1217ad93220dSDong Aisheng 		return -EINVAL;
1218ad93220dSDong Aisheng 
1219ad93220dSDong Aisheng 	switch (uhs) {
1220ad93220dSDong Aisheng 	case MMC_TIMING_UHS_SDR50:
12219f327845SHaibo Chen 	case MMC_TIMING_UHS_DDR50:
1222ad93220dSDong Aisheng 		pinctrl = imx_data->pins_100mhz;
1223ad93220dSDong Aisheng 		break;
1224ad93220dSDong Aisheng 	case MMC_TIMING_UHS_SDR104:
1225429a5b45SDong Aisheng 	case MMC_TIMING_MMC_HS200:
122628b07674SHaibo Chen 	case MMC_TIMING_MMC_HS400:
1227ad93220dSDong Aisheng 		pinctrl = imx_data->pins_200mhz;
1228ad93220dSDong Aisheng 		break;
1229ad93220dSDong Aisheng 	default:
1230ad93220dSDong Aisheng 		/* back to default state for other legacy timing */
12312480b720SUlf Hansson 		return pinctrl_select_default_state(mmc_dev(host->mmc));
1232ad93220dSDong Aisheng 	}
1233ad93220dSDong Aisheng 
1234ad93220dSDong Aisheng 	return pinctrl_select_state(imx_data->pinctrl, pinctrl);
1235ad93220dSDong Aisheng }
1236ad93220dSDong Aisheng 
123728b07674SHaibo Chen /*
1238d04f8d5bSBenoît Thébaudeau  * For HS400 eMMC, there is a data_strobe line. This signal is generated
123928b07674SHaibo Chen  * by the device and used for data output and CRC status response output
124028b07674SHaibo Chen  * in HS400 mode. The frequency of this signal follows the frequency of
1241d04f8d5bSBenoît Thébaudeau  * CLK generated by host. The host receives the data which is aligned to the
124228b07674SHaibo Chen  * edge of data_strobe line. Due to the time delay between CLK line and
124328b07674SHaibo Chen  * data_strobe line, if the delay time is larger than one clock cycle,
1244d04f8d5bSBenoît Thébaudeau  * then CLK and data_strobe line will be misaligned, read error shows up.
124528b07674SHaibo Chen  */
124628b07674SHaibo Chen static void esdhc_set_strobe_dll(struct sdhci_host *host)
124728b07674SHaibo Chen {
12485bd2acdcSHaibo Chen 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
12495bd2acdcSHaibo Chen 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
12505bd2acdcSHaibo Chen 	u32 strobe_delay;
125128b07674SHaibo Chen 	u32 v;
1252373e800bSHaibo Chen 	int ret;
125328b07674SHaibo Chen 
12547ac6da26SDong Aisheng 	/* disable clock before enabling strobe dll */
12557ac6da26SDong Aisheng 	writel(readl(host->ioaddr + ESDHC_VENDOR_SPEC) &
12567ac6da26SDong Aisheng 		~ESDHC_VENDOR_SPEC_FRC_SDCLK_ON,
12577ac6da26SDong Aisheng 		host->ioaddr + ESDHC_VENDOR_SPEC);
1258f581e909SHaibo Chen 	esdhc_wait_for_card_clock_gate_off(host);
12597ac6da26SDong Aisheng 
126028b07674SHaibo Chen 	/* force a reset on strobe dll */
126128b07674SHaibo Chen 	writel(ESDHC_STROBE_DLL_CTRL_RESET,
126228b07674SHaibo Chen 		host->ioaddr + ESDHC_STROBE_DLL_CTRL);
12632eaf5a53SBOUGH CHEN 	/* clear the reset bit on strobe dll before any setting */
12642eaf5a53SBOUGH CHEN 	writel(0, host->ioaddr + ESDHC_STROBE_DLL_CTRL);
12652eaf5a53SBOUGH CHEN 
126628b07674SHaibo Chen 	/*
126728b07674SHaibo Chen 	 * enable strobe dll ctrl and adjust the delay target
126828b07674SHaibo Chen 	 * for the uSDHC loopback read clock
126928b07674SHaibo Chen 	 */
12705bd2acdcSHaibo Chen 	if (imx_data->boarddata.strobe_dll_delay_target)
12715bd2acdcSHaibo Chen 		strobe_delay = imx_data->boarddata.strobe_dll_delay_target;
12725bd2acdcSHaibo Chen 	else
12735bd2acdcSHaibo Chen 		strobe_delay = ESDHC_STROBE_DLL_CTRL_SLV_DLY_TARGET_DEFAULT;
127428b07674SHaibo Chen 	v = ESDHC_STROBE_DLL_CTRL_ENABLE |
12752eaf5a53SBOUGH CHEN 		ESDHC_STROBE_DLL_CTRL_SLV_UPDATE_INT_DEFAULT |
12765bd2acdcSHaibo Chen 		(strobe_delay << ESDHC_STROBE_DLL_CTRL_SLV_DLY_TARGET_SHIFT);
127728b07674SHaibo Chen 	writel(v, host->ioaddr + ESDHC_STROBE_DLL_CTRL);
1278373e800bSHaibo Chen 
1279373e800bSHaibo Chen 	/* wait max 50us to get the REF/SLV lock */
1280373e800bSHaibo Chen 	ret = readl_poll_timeout(host->ioaddr + ESDHC_STROBE_DLL_STATUS, v,
1281373e800bSHaibo Chen 		((v & ESDHC_STROBE_DLL_STS_REF_LOCK) && (v & ESDHC_STROBE_DLL_STS_SLV_LOCK)), 1, 50);
1282373e800bSHaibo Chen 	if (ret == -ETIMEDOUT)
128328b07674SHaibo Chen 		dev_warn(mmc_dev(host->mmc),
1284373e800bSHaibo Chen 		"warning! HS400 strobe DLL status REF/SLV not lock in 50us, STROBE DLL status is %x!\n", v);
128528b07674SHaibo Chen }
128628b07674SHaibo Chen 
1287850a29b8SRussell King static void esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned timing)
1288ad93220dSDong Aisheng {
128928b07674SHaibo Chen 	u32 m;
1290ad93220dSDong Aisheng 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1291070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
1292602519b2SDong Aisheng 	struct esdhc_platform_data *boarddata = &imx_data->boarddata;
1293ad93220dSDong Aisheng 
129428b07674SHaibo Chen 	/* disable ddr mode and disable HS400 mode */
129528b07674SHaibo Chen 	m = readl(host->ioaddr + ESDHC_MIX_CTRL);
129628b07674SHaibo Chen 	m &= ~(ESDHC_MIX_CTRL_DDREN | ESDHC_MIX_CTRL_HS400_EN);
129728b07674SHaibo Chen 	imx_data->is_ddr = 0;
129828b07674SHaibo Chen 
1299850a29b8SRussell King 	switch (timing) {
1300ad93220dSDong Aisheng 	case MMC_TIMING_UHS_SDR12:
1301ad93220dSDong Aisheng 	case MMC_TIMING_UHS_SDR25:
1302ad93220dSDong Aisheng 	case MMC_TIMING_UHS_SDR50:
1303ad93220dSDong Aisheng 	case MMC_TIMING_UHS_SDR104:
1304de0a0decSBOUGH CHEN 	case MMC_TIMING_MMC_HS:
1305429a5b45SDong Aisheng 	case MMC_TIMING_MMC_HS200:
130628b07674SHaibo Chen 		writel(m, host->ioaddr + ESDHC_MIX_CTRL);
1307ad93220dSDong Aisheng 		break;
1308ad93220dSDong Aisheng 	case MMC_TIMING_UHS_DDR50:
130969f5bf38SAisheng Dong 	case MMC_TIMING_MMC_DDR52:
131028b07674SHaibo Chen 		m |= ESDHC_MIX_CTRL_DDREN;
131128b07674SHaibo Chen 		writel(m, host->ioaddr + ESDHC_MIX_CTRL);
1312de5bdbffSDong Aisheng 		imx_data->is_ddr = 1;
1313602519b2SDong Aisheng 		if (boarddata->delay_line) {
1314602519b2SDong Aisheng 			u32 v;
1315602519b2SDong Aisheng 			v = boarddata->delay_line <<
1316602519b2SDong Aisheng 				ESDHC_DLL_OVERRIDE_VAL_SHIFT |
1317602519b2SDong Aisheng 				(1 << ESDHC_DLL_OVERRIDE_EN_SHIFT);
1318602519b2SDong Aisheng 			if (is_imx53_esdhc(imx_data))
1319602519b2SDong Aisheng 				v <<= 1;
1320602519b2SDong Aisheng 			writel(v, host->ioaddr + ESDHC_DLL_CTRL);
1321602519b2SDong Aisheng 		}
1322ad93220dSDong Aisheng 		break;
132328b07674SHaibo Chen 	case MMC_TIMING_MMC_HS400:
132428b07674SHaibo Chen 		m |= ESDHC_MIX_CTRL_DDREN | ESDHC_MIX_CTRL_HS400_EN;
132528b07674SHaibo Chen 		writel(m, host->ioaddr + ESDHC_MIX_CTRL);
132628b07674SHaibo Chen 		imx_data->is_ddr = 1;
13277ac6da26SDong Aisheng 		/* update clock after enable DDR for strobe DLL lock */
13287ac6da26SDong Aisheng 		host->ops->set_clock(host, host->clock);
132928b07674SHaibo Chen 		esdhc_set_strobe_dll(host);
133028b07674SHaibo Chen 		break;
1331d9370424SHaibo Chen 	case MMC_TIMING_LEGACY:
1332d9370424SHaibo Chen 	default:
1333d9370424SHaibo Chen 		esdhc_reset_tuning(host);
1334d9370424SHaibo Chen 		break;
1335ad93220dSDong Aisheng 	}
1336ad93220dSDong Aisheng 
1337850a29b8SRussell King 	esdhc_change_pinstate(host, timing);
1338ad93220dSDong Aisheng }
1339ad93220dSDong Aisheng 
13400718e59aSRussell King static void esdhc_reset(struct sdhci_host *host, u8 mask)
13410718e59aSRussell King {
1342fb1dec44SBrian Norris 	sdhci_and_cqhci_reset(host, mask);
13430718e59aSRussell King 
13440718e59aSRussell King 	sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
13450718e59aSRussell King 	sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
13460718e59aSRussell King }
13470718e59aSRussell King 
134810fd0ad9SAisheng Dong static unsigned int esdhc_get_max_timeout_count(struct sdhci_host *host)
134910fd0ad9SAisheng Dong {
135010fd0ad9SAisheng Dong 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1351070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
135210fd0ad9SAisheng Dong 
1353d04f8d5bSBenoît Thébaudeau 	/* Doc Erratum: the uSDHC actual maximum timeout count is 1 << 29 */
13542fb0b02bSHaibo Chen 	return esdhc_is_usdhc(imx_data) ? 1 << 29 : 1 << 27;
135510fd0ad9SAisheng Dong }
135610fd0ad9SAisheng Dong 
1357e33eb8e2SAisheng Dong static void esdhc_set_timeout(struct sdhci_host *host, struct mmc_command *cmd)
1358e33eb8e2SAisheng Dong {
1359e33eb8e2SAisheng Dong 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1360070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
1361e33eb8e2SAisheng Dong 
1362e33eb8e2SAisheng Dong 	/* use maximum timeout counter */
1363a215186dSHaibo Chen 	esdhc_clrset_le(host, ESDHC_SYS_CTRL_DTOCV_MASK,
1364a215186dSHaibo Chen 			esdhc_is_usdhc(imx_data) ? 0xF : 0xE,
1365e33eb8e2SAisheng Dong 			SDHCI_TIMEOUT_CONTROL);
1366e33eb8e2SAisheng Dong }
1367e33eb8e2SAisheng Dong 
1368bb6e3581SBOUGH CHEN static u32 esdhc_cqhci_irq(struct sdhci_host *host, u32 intmask)
1369bb6e3581SBOUGH CHEN {
1370bb6e3581SBOUGH CHEN 	int cmd_error = 0;
1371bb6e3581SBOUGH CHEN 	int data_error = 0;
1372bb6e3581SBOUGH CHEN 
1373bb6e3581SBOUGH CHEN 	if (!sdhci_cqe_irq(host, intmask, &cmd_error, &data_error))
1374bb6e3581SBOUGH CHEN 		return intmask;
1375bb6e3581SBOUGH CHEN 
1376bb6e3581SBOUGH CHEN 	cqhci_irq(host->mmc, intmask, cmd_error, data_error);
1377bb6e3581SBOUGH CHEN 
1378bb6e3581SBOUGH CHEN 	return 0;
1379bb6e3581SBOUGH CHEN }
1380bb6e3581SBOUGH CHEN 
13816e9fd28eSDong Aisheng static struct sdhci_ops sdhci_esdhc_ops = {
1382e149860dSRichard Zhu 	.read_l = esdhc_readl_le,
13830c6d49ceSWolfram Sang 	.read_w = esdhc_readw_le,
138477da3da0SAaron Brice 	.read_b = esdhc_readb_le,
1385e149860dSRichard Zhu 	.write_l = esdhc_writel_le,
13860c6d49ceSWolfram Sang 	.write_w = esdhc_writew_le,
13870c6d49ceSWolfram Sang 	.write_b = esdhc_writeb_le,
13888ba9580aSLucas Stach 	.set_clock = esdhc_pltfm_set_clock,
13890ddf03c9SLucas Stach 	.get_max_clock = esdhc_pltfm_get_max_clock,
13900c6d49ceSWolfram Sang 	.get_min_clock = esdhc_pltfm_get_min_clock,
139110fd0ad9SAisheng Dong 	.get_max_timeout_count = esdhc_get_max_timeout_count,
1392913413c3SShawn Guo 	.get_ro = esdhc_pltfm_get_ro,
1393e33eb8e2SAisheng Dong 	.set_timeout = esdhc_set_timeout,
13942317f56cSRussell King 	.set_bus_width = esdhc_pltfm_set_bus_width,
1395ad93220dSDong Aisheng 	.set_uhs_signaling = esdhc_set_uhs_signaling,
13960718e59aSRussell King 	.reset = esdhc_reset,
1397bb6e3581SBOUGH CHEN 	.irq = esdhc_cqhci_irq,
13983722c74cSHaibo Chen 	.dump_vendor_regs = esdhc_dump_debug_regs,
13990c6d49ceSWolfram Sang };
14000c6d49ceSWolfram Sang 
14011db5eebfSLars-Peter Clausen static const struct sdhci_pltfm_data sdhci_esdhc_imx_pdata = {
140297e4ba6aSRichard Zhu 	.quirks = ESDHC_DEFAULT_QUIRKS | SDHCI_QUIRK_NO_HISPD_BIT
140397e4ba6aSRichard Zhu 			| SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC
140497e4ba6aSRichard Zhu 			| SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC
140585d6509dSShawn Guo 			| SDHCI_QUIRK_BROKEN_CARD_DETECTION,
140685d6509dSShawn Guo 	.ops = &sdhci_esdhc_ops,
140785d6509dSShawn Guo };
140885d6509dSShawn Guo 
1409f3f5cf3dSDong Aisheng static void sdhci_esdhc_imx_hwinit(struct sdhci_host *host)
1410f3f5cf3dSDong Aisheng {
1411f3f5cf3dSDong Aisheng 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1412f3f5cf3dSDong Aisheng 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
1413982cf37dSHaibo Chen 	struct cqhci_host *cq_host = host->mmc->cqe_private;
14141e336aa0SHaibo Chen 	u32 tmp;
1415f3f5cf3dSDong Aisheng 
1416f3f5cf3dSDong Aisheng 	if (esdhc_is_usdhc(imx_data)) {
1417f3f5cf3dSDong Aisheng 		/*
1418f3f5cf3dSDong Aisheng 		 * The imx6q ROM code will change the default watermark
1419f3f5cf3dSDong Aisheng 		 * level setting to something insane.  Change it back here.
1420f3f5cf3dSDong Aisheng 		 */
1421f3f5cf3dSDong Aisheng 		writel(ESDHC_WTMK_DEFAULT_VAL, host->ioaddr + ESDHC_WTMK_LVL);
1422f3f5cf3dSDong Aisheng 
1423f3f5cf3dSDong Aisheng 		/*
1424f3f5cf3dSDong Aisheng 		 * ROM code will change the bit burst_length_enable setting
1425d04f8d5bSBenoît Thébaudeau 		 * to zero if this usdhc is chosen to boot system. Change
1426f3f5cf3dSDong Aisheng 		 * it back here, otherwise it will impact the performance a
1427f3f5cf3dSDong Aisheng 		 * lot. This bit is used to enable/disable the burst length
1428d04f8d5bSBenoît Thébaudeau 		 * for the external AHB2AXI bridge. It's useful especially
1429f3f5cf3dSDong Aisheng 		 * for INCR transfer because without burst length indicator,
1430f3f5cf3dSDong Aisheng 		 * the AHB2AXI bridge does not know the burst length in
1431f3f5cf3dSDong Aisheng 		 * advance. And without burst length indicator, AHB INCR
1432f3f5cf3dSDong Aisheng 		 * transfer can only be converted to singles on the AXI side.
1433f3f5cf3dSDong Aisheng 		 */
1434f3f5cf3dSDong Aisheng 		writel(readl(host->ioaddr + SDHCI_HOST_CONTROL)
1435f3f5cf3dSDong Aisheng 			| ESDHC_BURST_LEN_EN_INCR,
1436f3f5cf3dSDong Aisheng 			host->ioaddr + SDHCI_HOST_CONTROL);
1437e30be063SBOUGH CHEN 
1438f3f5cf3dSDong Aisheng 		/*
1439d04f8d5bSBenoît Thébaudeau 		 * erratum ESDHC_FLAG_ERR004536 fix for MX6Q TO1.2 and MX6DL
1440f3f5cf3dSDong Aisheng 		 * TO1.1, it's harmless for MX6SL
1441f3f5cf3dSDong Aisheng 		 */
14425c4f0062SChester Lin 		if (!(imx_data->socdata->flags & ESDHC_FLAG_SKIP_ERR004536)) {
1443e30be063SBOUGH CHEN 			writel(readl(host->ioaddr + 0x6c) & ~BIT(7),
1444f3f5cf3dSDong Aisheng 				host->ioaddr + 0x6c);
14455c4f0062SChester Lin 		}
1446f3f5cf3dSDong Aisheng 
1447f3f5cf3dSDong Aisheng 		/* disable DLL_CTRL delay line settings */
1448f3f5cf3dSDong Aisheng 		writel(0x0, host->ioaddr + ESDHC_DLL_CTRL);
14492b16cf32SDong Aisheng 
1450bcdb5301SBOUGH CHEN 		/*
1451bcdb5301SBOUGH CHEN 		 * For the case of command with busy, if set the bit
1452bcdb5301SBOUGH CHEN 		 * ESDHC_VEND_SPEC2_EN_BUSY_IRQ, USDHC will generate a
1453bcdb5301SBOUGH CHEN 		 * transfer complete interrupt when busy is deasserted.
1454bcdb5301SBOUGH CHEN 		 * When CQHCI use DCMD to send a CMD need R1b respons,
1455bcdb5301SBOUGH CHEN 		 * CQHCI require to set ESDHC_VEND_SPEC2_EN_BUSY_IRQ,
1456bcdb5301SBOUGH CHEN 		 * otherwise DCMD will always meet timeout waiting for
1457bcdb5301SBOUGH CHEN 		 * hardware interrupt issue.
1458bcdb5301SBOUGH CHEN 		 */
1459bcdb5301SBOUGH CHEN 		if (imx_data->socdata->flags & ESDHC_FLAG_CQHCI) {
1460bcdb5301SBOUGH CHEN 			tmp = readl(host->ioaddr + ESDHC_VEND_SPEC2);
1461bcdb5301SBOUGH CHEN 			tmp |= ESDHC_VEND_SPEC2_EN_BUSY_IRQ;
1462bcdb5301SBOUGH CHEN 			writel(tmp, host->ioaddr + ESDHC_VEND_SPEC2);
1463bcdb5301SBOUGH CHEN 
1464bcdb5301SBOUGH CHEN 			host->quirks &= ~SDHCI_QUIRK_NO_BUSY_IRQ;
1465bcdb5301SBOUGH CHEN 		}
1466bcdb5301SBOUGH CHEN 
14672b16cf32SDong Aisheng 		if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) {
14682b16cf32SDong Aisheng 			tmp = readl(host->ioaddr + ESDHC_TUNING_CTRL);
14691e336aa0SHaibo Chen 			tmp |= ESDHC_STD_TUNING_EN;
14701e336aa0SHaibo Chen 
14711e336aa0SHaibo Chen 			/*
14721e336aa0SHaibo Chen 			 * ROM code or bootloader may config the start tap
14731e336aa0SHaibo Chen 			 * and step, unmask them first.
14741e336aa0SHaibo Chen 			 */
14751e336aa0SHaibo Chen 			tmp &= ~(ESDHC_TUNING_START_TAP_MASK | ESDHC_TUNING_STEP_MASK);
14761e336aa0SHaibo Chen 			if (imx_data->boarddata.tuning_start_tap)
14772b16cf32SDong Aisheng 				tmp |= imx_data->boarddata.tuning_start_tap;
14781e336aa0SHaibo Chen 			else
14791e336aa0SHaibo Chen 				tmp |= ESDHC_TUNING_START_TAP_DEFAULT;
14802b16cf32SDong Aisheng 
14812b16cf32SDong Aisheng 			if (imx_data->boarddata.tuning_step) {
14822b16cf32SDong Aisheng 				tmp |= imx_data->boarddata.tuning_step
14832b16cf32SDong Aisheng 					<< ESDHC_TUNING_STEP_SHIFT;
14841e336aa0SHaibo Chen 			} else {
14851e336aa0SHaibo Chen 				tmp |= ESDHC_TUNING_STEP_DEFAULT
14861e336aa0SHaibo Chen 					<< ESDHC_TUNING_STEP_SHIFT;
14872b16cf32SDong Aisheng 			}
148816e40e5bSHaibo Chen 
148916e40e5bSHaibo Chen 			/* Disable the CMD CRC check for tuning, if not, need to
149016e40e5bSHaibo Chen 			 * add some delay after every tuning command, because
149116e40e5bSHaibo Chen 			 * hardware standard tuning logic will directly go to next
149216e40e5bSHaibo Chen 			 * step once it detect the CMD CRC error, will not wait for
149316e40e5bSHaibo Chen 			 * the card side to finally send out the tuning data, trigger
149416e40e5bSHaibo Chen 			 * the buffer read ready interrupt immediately. If usdhc send
149516e40e5bSHaibo Chen 			 * the next tuning command some eMMC card will stuck, can't
149616e40e5bSHaibo Chen 			 * response, block the tuning procedure or the first command
149716e40e5bSHaibo Chen 			 * after the whole tuning procedure always can't get any response.
149816e40e5bSHaibo Chen 			 */
149916e40e5bSHaibo Chen 			tmp |= ESDHC_TUNING_CMD_CRC_CHECK_DISABLE;
15002b16cf32SDong Aisheng 			writel(tmp, host->ioaddr + ESDHC_TUNING_CTRL);
1501a98c557eSBOUGH CHEN 		} else if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING) {
1502a98c557eSBOUGH CHEN 			/*
1503a98c557eSBOUGH CHEN 			 * ESDHC_STD_TUNING_EN may be configed in bootloader
1504a98c557eSBOUGH CHEN 			 * or ROM code, so clear this bit here to make sure
1505a98c557eSBOUGH CHEN 			 * the manual tuning can work.
1506a98c557eSBOUGH CHEN 			 */
1507a98c557eSBOUGH CHEN 			tmp = readl(host->ioaddr + ESDHC_TUNING_CTRL);
1508a98c557eSBOUGH CHEN 			tmp &= ~ESDHC_STD_TUNING_EN;
1509a98c557eSBOUGH CHEN 			writel(tmp, host->ioaddr + ESDHC_TUNING_CTRL);
15102b16cf32SDong Aisheng 		}
1511982cf37dSHaibo Chen 
1512982cf37dSHaibo Chen 		/*
1513982cf37dSHaibo Chen 		 * On i.MX8MM, we are running Dual Linux OS, with 1st Linux using SD Card
15141ad0dcb9Swangjianli 		 * as rootfs storage, 2nd Linux using eMMC as rootfs storage. We let
1515982cf37dSHaibo Chen 		 * the 1st linux configure power/clock for the 2nd Linux.
1516982cf37dSHaibo Chen 		 *
1517982cf37dSHaibo Chen 		 * When the 2nd Linux is booting into rootfs stage, we let the 1st Linux
1518982cf37dSHaibo Chen 		 * to destroy the 2nd linux, then restart the 2nd linux, we met SDHCI dump.
1519982cf37dSHaibo Chen 		 * After we clear the pending interrupt and halt CQCTL, issue gone.
1520982cf37dSHaibo Chen 		 */
1521982cf37dSHaibo Chen 		if (cq_host) {
1522982cf37dSHaibo Chen 			tmp = cqhci_readl(cq_host, CQHCI_IS);
1523982cf37dSHaibo Chen 			cqhci_writel(cq_host, tmp, CQHCI_IS);
1524982cf37dSHaibo Chen 			cqhci_writel(cq_host, CQHCI_HALT, CQHCI_CTL);
1525982cf37dSHaibo Chen 		}
1526f3f5cf3dSDong Aisheng 	}
1527f3f5cf3dSDong Aisheng }
1528f3f5cf3dSDong Aisheng 
1529bb6e3581SBOUGH CHEN static void esdhc_cqe_enable(struct mmc_host *mmc)
1530bb6e3581SBOUGH CHEN {
1531bb6e3581SBOUGH CHEN 	struct sdhci_host *host = mmc_priv(mmc);
153285236d2bSBOUGH CHEN 	struct cqhci_host *cq_host = mmc->cqe_private;
1533bb6e3581SBOUGH CHEN 	u32 reg;
1534bb6e3581SBOUGH CHEN 	u16 mode;
1535bb6e3581SBOUGH CHEN 	int count = 10;
1536bb6e3581SBOUGH CHEN 
1537bb6e3581SBOUGH CHEN 	/*
1538bb6e3581SBOUGH CHEN 	 * CQE gets stuck if it sees Buffer Read Enable bit set, which can be
1539bb6e3581SBOUGH CHEN 	 * the case after tuning, so ensure the buffer is drained.
1540bb6e3581SBOUGH CHEN 	 */
1541bb6e3581SBOUGH CHEN 	reg = sdhci_readl(host, SDHCI_PRESENT_STATE);
1542bb6e3581SBOUGH CHEN 	while (reg & SDHCI_DATA_AVAILABLE) {
1543bb6e3581SBOUGH CHEN 		sdhci_readl(host, SDHCI_BUFFER);
1544bb6e3581SBOUGH CHEN 		reg = sdhci_readl(host, SDHCI_PRESENT_STATE);
1545bb6e3581SBOUGH CHEN 		if (count-- == 0) {
1546bb6e3581SBOUGH CHEN 			dev_warn(mmc_dev(host->mmc),
1547bb6e3581SBOUGH CHEN 				"CQE may get stuck because the Buffer Read Enable bit is set\n");
1548bb6e3581SBOUGH CHEN 			break;
1549bb6e3581SBOUGH CHEN 		}
1550bb6e3581SBOUGH CHEN 		mdelay(1);
1551bb6e3581SBOUGH CHEN 	}
1552bb6e3581SBOUGH CHEN 
1553bb6e3581SBOUGH CHEN 	/*
1554bb6e3581SBOUGH CHEN 	 * Runtime resume will reset the entire host controller, which
1555bb6e3581SBOUGH CHEN 	 * will also clear the DMAEN/BCEN of register ESDHC_MIX_CTRL.
1556bb6e3581SBOUGH CHEN 	 * Here set DMAEN and BCEN when enable CMDQ.
1557bb6e3581SBOUGH CHEN 	 */
1558bb6e3581SBOUGH CHEN 	mode = sdhci_readw(host, SDHCI_TRANSFER_MODE);
1559bb6e3581SBOUGH CHEN 	if (host->flags & SDHCI_REQ_USE_DMA)
1560bb6e3581SBOUGH CHEN 		mode |= SDHCI_TRNS_DMA;
1561bb6e3581SBOUGH CHEN 	if (!(host->quirks2 & SDHCI_QUIRK2_SUPPORT_SINGLE))
1562bb6e3581SBOUGH CHEN 		mode |= SDHCI_TRNS_BLK_CNT_EN;
1563bb6e3581SBOUGH CHEN 	sdhci_writew(host, mode, SDHCI_TRANSFER_MODE);
1564bb6e3581SBOUGH CHEN 
156585236d2bSBOUGH CHEN 	/*
156685236d2bSBOUGH CHEN 	 * Though Runtime resume reset the entire host controller,
156785236d2bSBOUGH CHEN 	 * but do not impact the CQHCI side, need to clear the
156885236d2bSBOUGH CHEN 	 * HALT bit, avoid CQHCI stuck in the first request when
156985236d2bSBOUGH CHEN 	 * system resume back.
157085236d2bSBOUGH CHEN 	 */
157185236d2bSBOUGH CHEN 	cqhci_writel(cq_host, 0, CQHCI_CTL);
1572a3cab1d2SSebastian Falbesoner 	if (cqhci_readl(cq_host, CQHCI_CTL) & CQHCI_HALT)
157385236d2bSBOUGH CHEN 		dev_err(mmc_dev(host->mmc),
157485236d2bSBOUGH CHEN 			"failed to exit halt state when enable CQE\n");
157585236d2bSBOUGH CHEN 
157685236d2bSBOUGH CHEN 
1577bb6e3581SBOUGH CHEN 	sdhci_cqe_enable(mmc);
1578bb6e3581SBOUGH CHEN }
1579bb6e3581SBOUGH CHEN 
1580bb6e3581SBOUGH CHEN static void esdhc_sdhci_dumpregs(struct mmc_host *mmc)
1581bb6e3581SBOUGH CHEN {
1582bb6e3581SBOUGH CHEN 	sdhci_dumpregs(mmc_priv(mmc));
1583bb6e3581SBOUGH CHEN }
1584bb6e3581SBOUGH CHEN 
1585bb6e3581SBOUGH CHEN static const struct cqhci_host_ops esdhc_cqhci_ops = {
1586bb6e3581SBOUGH CHEN 	.enable		= esdhc_cqe_enable,
1587bb6e3581SBOUGH CHEN 	.disable	= sdhci_cqe_disable,
1588bb6e3581SBOUGH CHEN 	.dumpregs	= esdhc_sdhci_dumpregs,
1589bb6e3581SBOUGH CHEN };
1590bb6e3581SBOUGH CHEN 
1591c3be1efdSBill Pemberton static int
1592abfafc2dSShawn Guo sdhci_esdhc_imx_probe_dt(struct platform_device *pdev,
159307bf2b54SSascha Hauer 			 struct sdhci_host *host,
159491fa4252SDong Aisheng 			 struct pltfm_imx_data *imx_data)
1595abfafc2dSShawn Guo {
1596abfafc2dSShawn Guo 	struct device_node *np = pdev->dev.of_node;
159791fa4252SDong Aisheng 	struct esdhc_platform_data *boarddata = &imx_data->boarddata;
15984800e87aSDong Aisheng 	int ret;
1599abfafc2dSShawn Guo 
1600*ca6b5fe2SRob Herring 	if (of_property_read_bool(np, "fsl,wp-controller"))
1601abfafc2dSShawn Guo 		boarddata->wp_type = ESDHC_WP_CONTROLLER;
1602abfafc2dSShawn Guo 
160374ff81e1SLinus Walleij 	/*
160474ff81e1SLinus Walleij 	 * If we have this property, then activate WP check.
160574ff81e1SLinus Walleij 	 * Retrieveing and requesting the actual WP GPIO will happen
160674ff81e1SLinus Walleij 	 * in the call to mmc_of_parse().
160774ff81e1SLinus Walleij 	 */
160874ff81e1SLinus Walleij 	if (of_property_read_bool(np, "wp-gpios"))
1609abfafc2dSShawn Guo 		boarddata->wp_type = ESDHC_WP_GPIO;
1610abfafc2dSShawn Guo 
1611d407e30bSHaibo Chen 	of_property_read_u32(np, "fsl,tuning-step", &boarddata->tuning_step);
1612d87fc966SDong Aisheng 	of_property_read_u32(np, "fsl,tuning-start-tap",
1613d87fc966SDong Aisheng 			     &boarddata->tuning_start_tap);
1614d407e30bSHaibo Chen 
16155bd2acdcSHaibo Chen 	of_property_read_u32(np, "fsl,strobe-dll-delay-target",
16165bd2acdcSHaibo Chen 				&boarddata->strobe_dll_delay_target);
1617*ca6b5fe2SRob Herring 	if (of_property_read_bool(np, "no-1-8-v"))
161886f495c5SStefan Agner 		host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V;
1619ad93220dSDong Aisheng 
1620602519b2SDong Aisheng 	if (of_property_read_u32(np, "fsl,delay-line", &boarddata->delay_line))
1621602519b2SDong Aisheng 		boarddata->delay_line = 0;
1622602519b2SDong Aisheng 
16236dab809bSAndy Shevchenko 	mmc_of_parse_voltage(host->mmc, &host->ocr_mask);
162407bf2b54SSascha Hauer 
1625f410ee0aSPeng Fan 	if (esdhc_is_usdhc(imx_data) && !IS_ERR(imx_data->pinctrl)) {
162691fa4252SDong Aisheng 		imx_data->pins_100mhz = pinctrl_lookup_state(imx_data->pinctrl,
162791fa4252SDong Aisheng 						ESDHC_PINCTRL_STATE_100MHZ);
162891fa4252SDong Aisheng 		imx_data->pins_200mhz = pinctrl_lookup_state(imx_data->pinctrl,
162991fa4252SDong Aisheng 						ESDHC_PINCTRL_STATE_200MHZ);
163091fa4252SDong Aisheng 	}
163191fa4252SDong Aisheng 
163215064119SFabio Estevam 	/* call to generic mmc_of_parse to support additional capabilities */
16334800e87aSDong Aisheng 	ret = mmc_of_parse(host->mmc);
16344800e87aSDong Aisheng 	if (ret)
16354800e87aSDong Aisheng 		return ret;
16364800e87aSDong Aisheng 
1637287980e4SArnd Bergmann 	if (mmc_gpio_get_cd(host->mmc) >= 0)
16384800e87aSDong Aisheng 		host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION;
16394800e87aSDong Aisheng 
16404800e87aSDong Aisheng 	return 0;
1641abfafc2dSShawn Guo }
1642abfafc2dSShawn Guo 
1643c3be1efdSBill Pemberton static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
164495f25efeSWolfram Sang {
164585d6509dSShawn Guo 	struct sdhci_pltfm_host *pltfm_host;
164685d6509dSShawn Guo 	struct sdhci_host *host;
1647bb6e3581SBOUGH CHEN 	struct cqhci_host *cq_host;
16480c6d49ceSWolfram Sang 	int err;
1649e149860dSRichard Zhu 	struct pltfm_imx_data *imx_data;
165095f25efeSWolfram Sang 
1651070e6d3fSJisheng Zhang 	host = sdhci_pltfm_init(pdev, &sdhci_esdhc_imx_pdata,
1652070e6d3fSJisheng Zhang 				sizeof(*imx_data));
165385d6509dSShawn Guo 	if (IS_ERR(host))
165485d6509dSShawn Guo 		return PTR_ERR(host);
165585d6509dSShawn Guo 
165685d6509dSShawn Guo 	pltfm_host = sdhci_priv(host);
165785d6509dSShawn Guo 
1658070e6d3fSJisheng Zhang 	imx_data = sdhci_pltfm_priv(pltfm_host);
165957ed3314SShawn Guo 
166091b3d2e5SFabio Estevam 	imx_data->socdata = device_get_match_data(&pdev->dev);
166185d6509dSShawn Guo 
16621c4989b0SBOUGH CHEN 	if (imx_data->socdata->flags & ESDHC_FLAG_PMQOS)
1663d1b98305SRafael J. Wysocki 		cpu_latency_qos_add_request(&imx_data->pm_qos_req, 0);
16641c4989b0SBOUGH CHEN 
166552dac615SSascha Hauer 	imx_data->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
166652dac615SSascha Hauer 	if (IS_ERR(imx_data->clk_ipg)) {
166752dac615SSascha Hauer 		err = PTR_ERR(imx_data->clk_ipg);
1668e3af31c6SShawn Guo 		goto free_sdhci;
166995f25efeSWolfram Sang 	}
167052dac615SSascha Hauer 
167152dac615SSascha Hauer 	imx_data->clk_ahb = devm_clk_get(&pdev->dev, "ahb");
167252dac615SSascha Hauer 	if (IS_ERR(imx_data->clk_ahb)) {
167352dac615SSascha Hauer 		err = PTR_ERR(imx_data->clk_ahb);
1674e3af31c6SShawn Guo 		goto free_sdhci;
167552dac615SSascha Hauer 	}
167652dac615SSascha Hauer 
167752dac615SSascha Hauer 	imx_data->clk_per = devm_clk_get(&pdev->dev, "per");
167852dac615SSascha Hauer 	if (IS_ERR(imx_data->clk_per)) {
167952dac615SSascha Hauer 		err = PTR_ERR(imx_data->clk_per);
1680e3af31c6SShawn Guo 		goto free_sdhci;
168152dac615SSascha Hauer 	}
168252dac615SSascha Hauer 
168352dac615SSascha Hauer 	pltfm_host->clk = imx_data->clk_per;
1684a974862fSDong Aisheng 	pltfm_host->clock = clk_get_rate(pltfm_host->clk);
168517b1eb7fSFabio Estevam 	err = clk_prepare_enable(imx_data->clk_per);
168617b1eb7fSFabio Estevam 	if (err)
168717b1eb7fSFabio Estevam 		goto free_sdhci;
168817b1eb7fSFabio Estevam 	err = clk_prepare_enable(imx_data->clk_ipg);
168917b1eb7fSFabio Estevam 	if (err)
169017b1eb7fSFabio Estevam 		goto disable_per_clk;
169117b1eb7fSFabio Estevam 	err = clk_prepare_enable(imx_data->clk_ahb);
169217b1eb7fSFabio Estevam 	if (err)
169317b1eb7fSFabio Estevam 		goto disable_ipg_clk;
169495f25efeSWolfram Sang 
1695ad93220dSDong Aisheng 	imx_data->pinctrl = devm_pinctrl_get(&pdev->dev);
16969e70ff99SHaibo Chen 	if (IS_ERR(imx_data->pinctrl))
1697b62eee9fSHaibo Chen 		dev_warn(mmc_dev(host->mmc), "could not get pinctrl\n");
1698e62d8b8fSDong Aisheng 
169969ed60e0SDong Aisheng 	if (esdhc_is_usdhc(imx_data)) {
170069ed60e0SDong Aisheng 		host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN;
170109c8192bSStefan Agner 		host->mmc->caps |= MMC_CAP_1_8V_DDR | MMC_CAP_3_3V_DDR;
1702f6140462SHaibo Chen 
1703f6140462SHaibo Chen 		/* GPIO CD can be set as a wakeup source */
1704f6140462SHaibo Chen 		host->mmc->caps |= MMC_CAP_CD_WAKE;
1705f6140462SHaibo Chen 
17064245afffSDong Aisheng 		if (!(imx_data->socdata->flags & ESDHC_FLAG_HS200))
17074245afffSDong Aisheng 			host->quirks2 |= SDHCI_QUIRK2_BROKEN_HS200;
1708a75dcbf4SDong Aisheng 
1709a75dcbf4SDong Aisheng 		/* clear tuning bits in case ROM has set it already */
1710a75dcbf4SDong Aisheng 		writel(0x0, host->ioaddr + ESDHC_MIX_CTRL);
1711869f8a69SAdrian Hunter 		writel(0x0, host->ioaddr + SDHCI_AUTO_CMD_STATUS);
1712a75dcbf4SDong Aisheng 		writel(0x0, host->ioaddr + ESDHC_TUNE_CTRL_STATUS);
1713de3e1dd0SBOUGH CHEN 
1714de3e1dd0SBOUGH CHEN 		/*
1715de3e1dd0SBOUGH CHEN 		 * Link usdhc specific mmc_host_ops execute_tuning function,
1716de3e1dd0SBOUGH CHEN 		 * to replace the standard one in sdhci_ops.
1717de3e1dd0SBOUGH CHEN 		 */
1718de3e1dd0SBOUGH CHEN 		host->mmc_host_ops.execute_tuning = usdhc_execute_tuning;
171952e4c32bSHaibo Chen 
172052e4c32bSHaibo Chen 		/*
172152e4c32bSHaibo Chen 		 * Link usdhc specific mmc_host_ops init card function,
172252e4c32bSHaibo Chen 		 * to distinguish the card type.
172352e4c32bSHaibo Chen 		 */
172452e4c32bSHaibo Chen 		host->mmc_host_ops.init_card = usdhc_init_card;
172569ed60e0SDong Aisheng 	}
1726f750ba9bSShawn Guo 
17271ed5c3b2SSascha Hauer 	err = sdhci_esdhc_imx_probe_dt(pdev, host, imx_data);
17281ed5c3b2SSascha Hauer 	if (err)
17291ed5c3b2SSascha Hauer 		goto disable_ahb_clk;
17301ed5c3b2SSascha Hauer 
17316e9fd28eSDong Aisheng 	if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING)
17326e9fd28eSDong Aisheng 		sdhci_esdhc_ops.platform_execute_tuning =
17336e9fd28eSDong Aisheng 					esdhc_executing_tuning;
17348b2bb0adSDong Aisheng 
173518094430SDong Aisheng 	if (imx_data->socdata->flags & ESDHC_FLAG_ERR004536)
173618094430SDong Aisheng 		host->quirks |= SDHCI_QUIRK_BROKEN_ADMA;
173718094430SDong Aisheng 
1738f002f45aSHaibo Chen 	if (host->mmc->caps & MMC_CAP_8_BIT_DATA &&
17391ed5c3b2SSascha Hauer 	    imx_data->socdata->flags & ESDHC_FLAG_HS400)
17402991ad76SLucas Stach 		host->mmc->caps2 |= MMC_CAP2_HS400;
174128b07674SHaibo Chen 
174274898cbcSHaibo Chen 	if (imx_data->socdata->flags & ESDHC_FLAG_BROKEN_AUTO_CMD23)
174374898cbcSHaibo Chen 		host->quirks2 |= SDHCI_QUIRK2_ACMD23_BROKEN;
174474898cbcSHaibo Chen 
1745f002f45aSHaibo Chen 	if (host->mmc->caps & MMC_CAP_8_BIT_DATA &&
17461ed5c3b2SSascha Hauer 	    imx_data->socdata->flags & ESDHC_FLAG_HS400_ES) {
1747029e2476SBOUGH CHEN 		host->mmc->caps2 |= MMC_CAP2_HS400_ES;
1748029e2476SBOUGH CHEN 		host->mmc_host_ops.hs400_enhanced_strobe =
1749029e2476SBOUGH CHEN 					esdhc_hs400_enhanced_strobe;
1750029e2476SBOUGH CHEN 	}
1751029e2476SBOUGH CHEN 
1752bb6e3581SBOUGH CHEN 	if (imx_data->socdata->flags & ESDHC_FLAG_CQHCI) {
1753bcdb5301SBOUGH CHEN 		host->mmc->caps2 |= MMC_CAP2_CQE | MMC_CAP2_CQE_DCMD;
1754bb6e3581SBOUGH CHEN 		cq_host = devm_kzalloc(&pdev->dev, sizeof(*cq_host), GFP_KERNEL);
17559a633f3bSWei Yongjun 		if (!cq_host) {
17569a633f3bSWei Yongjun 			err = -ENOMEM;
1757bb6e3581SBOUGH CHEN 			goto disable_ahb_clk;
1758bb6e3581SBOUGH CHEN 		}
1759bb6e3581SBOUGH CHEN 
1760bb6e3581SBOUGH CHEN 		cq_host->mmio = host->ioaddr + ESDHC_CQHCI_ADDR_OFFSET;
1761bb6e3581SBOUGH CHEN 		cq_host->ops = &esdhc_cqhci_ops;
1762bb6e3581SBOUGH CHEN 
1763bb6e3581SBOUGH CHEN 		err = cqhci_init(cq_host, host->mmc, false);
1764bb6e3581SBOUGH CHEN 		if (err)
1765bb6e3581SBOUGH CHEN 			goto disable_ahb_clk;
1766bb6e3581SBOUGH CHEN 	}
1767bb6e3581SBOUGH CHEN 
1768f3f5cf3dSDong Aisheng 	sdhci_esdhc_imx_hwinit(host);
1769f3f5cf3dSDong Aisheng 
177085d6509dSShawn Guo 	err = sdhci_add_host(host);
177185d6509dSShawn Guo 	if (err)
177217b1eb7fSFabio Estevam 		goto disable_ahb_clk;
177385d6509dSShawn Guo 
1774f62f7bccSHaibo Chen 	/*
1775f62f7bccSHaibo Chen 	 * Setup the wakeup capability here, let user to decide
1776f62f7bccSHaibo Chen 	 * whether need to enable this wakeup through sysfs interface.
1777f62f7bccSHaibo Chen 	 */
1778f62f7bccSHaibo Chen 	if ((host->mmc->pm_caps & MMC_PM_KEEP_POWER) &&
1779f62f7bccSHaibo Chen 			(host->mmc->pm_caps & MMC_PM_WAKE_SDIO_IRQ))
1780f62f7bccSHaibo Chen 		device_set_wakeup_capable(&pdev->dev, true);
1781f62f7bccSHaibo Chen 
178289d7e5c1SDong Aisheng 	pm_runtime_set_active(&pdev->dev);
178389d7e5c1SDong Aisheng 	pm_runtime_set_autosuspend_delay(&pdev->dev, 50);
178489d7e5c1SDong Aisheng 	pm_runtime_use_autosuspend(&pdev->dev);
178589d7e5c1SDong Aisheng 	pm_suspend_ignore_children(&pdev->dev, 1);
178677903c01SUlf Hansson 	pm_runtime_enable(&pdev->dev);
178789d7e5c1SDong Aisheng 
17887e29c306SWolfram Sang 	return 0;
17897e29c306SWolfram Sang 
179017b1eb7fSFabio Estevam disable_ahb_clk:
179152dac615SSascha Hauer 	clk_disable_unprepare(imx_data->clk_ahb);
179217b1eb7fSFabio Estevam disable_ipg_clk:
179317b1eb7fSFabio Estevam 	clk_disable_unprepare(imx_data->clk_ipg);
179417b1eb7fSFabio Estevam disable_per_clk:
179517b1eb7fSFabio Estevam 	clk_disable_unprepare(imx_data->clk_per);
1796e3af31c6SShawn Guo free_sdhci:
17971c4989b0SBOUGH CHEN 	if (imx_data->socdata->flags & ESDHC_FLAG_PMQOS)
1798d1b98305SRafael J. Wysocki 		cpu_latency_qos_remove_request(&imx_data->pm_qos_req);
179985d6509dSShawn Guo 	sdhci_pltfm_free(pdev);
180085d6509dSShawn Guo 	return err;
180195f25efeSWolfram Sang }
180295f25efeSWolfram Sang 
18036e0ee714SBill Pemberton static int sdhci_esdhc_imx_remove(struct platform_device *pdev)
180495f25efeSWolfram Sang {
180585d6509dSShawn Guo 	struct sdhci_host *host = platform_get_drvdata(pdev);
180695f25efeSWolfram Sang 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1807070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
1808a56f4413SFrank Li 	int dead;
180985d6509dSShawn Guo 
18100b414368SUlf Hansson 	pm_runtime_get_sync(&pdev->dev);
1811a56f4413SFrank Li 	dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff);
18120b414368SUlf Hansson 	pm_runtime_disable(&pdev->dev);
18130b414368SUlf Hansson 	pm_runtime_put_noidle(&pdev->dev);
18140b414368SUlf Hansson 
181585d6509dSShawn Guo 	sdhci_remove_host(host, dead);
18160c6d49ceSWolfram Sang 
181752dac615SSascha Hauer 	clk_disable_unprepare(imx_data->clk_per);
181852dac615SSascha Hauer 	clk_disable_unprepare(imx_data->clk_ipg);
181952dac615SSascha Hauer 	clk_disable_unprepare(imx_data->clk_ahb);
182052dac615SSascha Hauer 
18211c4989b0SBOUGH CHEN 	if (imx_data->socdata->flags & ESDHC_FLAG_PMQOS)
1822d1b98305SRafael J. Wysocki 		cpu_latency_qos_remove_request(&imx_data->pm_qos_req);
18231c4989b0SBOUGH CHEN 
182485d6509dSShawn Guo 	sdhci_pltfm_free(pdev);
182585d6509dSShawn Guo 
182685d6509dSShawn Guo 	return 0;
182795f25efeSWolfram Sang }
182895f25efeSWolfram Sang 
18292788ed42SUlf Hansson #ifdef CONFIG_PM_SLEEP
183004143fbaSDong Aisheng static int sdhci_esdhc_suspend(struct device *dev)
183104143fbaSDong Aisheng {
18323e3274abSUlf Hansson 	struct sdhci_host *host = dev_get_drvdata(dev);
1833a26a4f1bSHaibo Chen 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1834a26a4f1bSHaibo Chen 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
1835bb6e3581SBOUGH CHEN 	int ret;
1836bb6e3581SBOUGH CHEN 
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 	}
18423e3274abSUlf Hansson 
1843a26a4f1bSHaibo Chen 	if ((imx_data->socdata->flags & ESDHC_FLAG_STATE_LOST_IN_LPMODE) &&
1844a26a4f1bSHaibo Chen 		(host->tuning_mode != SDHCI_TUNING_MODE_1)) {
1845a26a4f1bSHaibo Chen 		mmc_retune_timer_stop(host->mmc);
1846a26a4f1bSHaibo Chen 		mmc_retune_needed(host->mmc);
1847a26a4f1bSHaibo Chen 	}
1848a26a4f1bSHaibo Chen 
1849d38dcad4SAdrian Hunter 	if (host->tuning_mode != SDHCI_TUNING_MODE_3)
1850d38dcad4SAdrian Hunter 		mmc_retune_needed(host->mmc);
1851d38dcad4SAdrian Hunter 
1852af8fade4SHaibo Chen 	ret = sdhci_suspend_host(host);
1853f6140462SHaibo Chen 	if (ret)
1854f6140462SHaibo Chen 		return ret;
1855f6140462SHaibo Chen 
1856f6140462SHaibo Chen 	ret = pinctrl_pm_select_sleep_state(dev);
1857f6140462SHaibo Chen 	if (ret)
1858f6140462SHaibo Chen 		return ret;
1859f6140462SHaibo Chen 
1860f6140462SHaibo Chen 	ret = mmc_gpio_set_cd_wake(host->mmc, true);
1861af8fade4SHaibo Chen 
1862af8fade4SHaibo Chen 	return ret;
186304143fbaSDong Aisheng }
186404143fbaSDong Aisheng 
186504143fbaSDong Aisheng static int sdhci_esdhc_resume(struct device *dev)
186604143fbaSDong Aisheng {
1867cc17e129SDong Aisheng 	struct sdhci_host *host = dev_get_drvdata(dev);
1868bb6e3581SBOUGH CHEN 	int ret;
1869cc17e129SDong Aisheng 
1870af8fade4SHaibo Chen 	ret = pinctrl_pm_select_default_state(dev);
1871af8fade4SHaibo Chen 	if (ret)
1872af8fade4SHaibo Chen 		return ret;
1873af8fade4SHaibo Chen 
187419dbfdd3SDong Aisheng 	/* re-initialize hw state in case it's lost in low power mode */
187519dbfdd3SDong Aisheng 	sdhci_esdhc_imx_hwinit(host);
1876cc17e129SDong Aisheng 
1877bb6e3581SBOUGH CHEN 	ret = sdhci_resume_host(host);
1878bb6e3581SBOUGH CHEN 	if (ret)
1879bb6e3581SBOUGH CHEN 		return ret;
1880bb6e3581SBOUGH CHEN 
1881bb6e3581SBOUGH CHEN 	if (host->mmc->caps2 & MMC_CAP2_CQE)
1882bb6e3581SBOUGH CHEN 		ret = cqhci_resume(host->mmc);
1883bb6e3581SBOUGH CHEN 
1884f6140462SHaibo Chen 	if (!ret)
1885f6140462SHaibo Chen 		ret = mmc_gpio_set_cd_wake(host->mmc, false);
1886f6140462SHaibo Chen 
1887bb6e3581SBOUGH CHEN 	return ret;
188804143fbaSDong Aisheng }
18892788ed42SUlf Hansson #endif
189004143fbaSDong Aisheng 
18912788ed42SUlf Hansson #ifdef CONFIG_PM
189289d7e5c1SDong Aisheng static int sdhci_esdhc_runtime_suspend(struct device *dev)
189389d7e5c1SDong Aisheng {
189489d7e5c1SDong Aisheng 	struct sdhci_host *host = dev_get_drvdata(dev);
189589d7e5c1SDong Aisheng 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1896070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
189789d7e5c1SDong Aisheng 	int ret;
189889d7e5c1SDong Aisheng 
1899bb6e3581SBOUGH CHEN 	if (host->mmc->caps2 & MMC_CAP2_CQE) {
1900bb6e3581SBOUGH CHEN 		ret = cqhci_suspend(host->mmc);
1901bb6e3581SBOUGH CHEN 		if (ret)
1902bb6e3581SBOUGH CHEN 			return ret;
1903bb6e3581SBOUGH CHEN 	}
1904bb6e3581SBOUGH CHEN 
190589d7e5c1SDong Aisheng 	ret = sdhci_runtime_suspend_host(host);
1906371d39faSMichael Trimarchi 	if (ret)
1907371d39faSMichael Trimarchi 		return ret;
190889d7e5c1SDong Aisheng 
1909d38dcad4SAdrian Hunter 	if (host->tuning_mode != SDHCI_TUNING_MODE_3)
1910d38dcad4SAdrian Hunter 		mmc_retune_needed(host->mmc);
1911d38dcad4SAdrian Hunter 
19123602785bSMichael Trimarchi 	imx_data->actual_clock = host->mmc->actual_clock;
19133602785bSMichael Trimarchi 	esdhc_pltfm_set_clock(host, 0);
191489d7e5c1SDong Aisheng 	clk_disable_unprepare(imx_data->clk_per);
191589d7e5c1SDong Aisheng 	clk_disable_unprepare(imx_data->clk_ipg);
191689d7e5c1SDong Aisheng 	clk_disable_unprepare(imx_data->clk_ahb);
191789d7e5c1SDong Aisheng 
19181c4989b0SBOUGH CHEN 	if (imx_data->socdata->flags & ESDHC_FLAG_PMQOS)
1919d1b98305SRafael J. Wysocki 		cpu_latency_qos_remove_request(&imx_data->pm_qos_req);
19201c4989b0SBOUGH CHEN 
192189d7e5c1SDong Aisheng 	return ret;
192289d7e5c1SDong Aisheng }
192389d7e5c1SDong Aisheng 
192489d7e5c1SDong Aisheng static int sdhci_esdhc_runtime_resume(struct device *dev)
192589d7e5c1SDong Aisheng {
192689d7e5c1SDong Aisheng 	struct sdhci_host *host = dev_get_drvdata(dev);
192789d7e5c1SDong Aisheng 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1928070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
192917b1eb7fSFabio Estevam 	int err;
193089d7e5c1SDong Aisheng 
19311c4989b0SBOUGH CHEN 	if (imx_data->socdata->flags & ESDHC_FLAG_PMQOS)
1932d1b98305SRafael J. Wysocki 		cpu_latency_qos_add_request(&imx_data->pm_qos_req, 0);
19331c4989b0SBOUGH CHEN 
19345c11f1ffSHaibo Chen 	if (imx_data->socdata->flags & ESDHC_FLAG_CLK_RATE_LOST_IN_PM_RUNTIME)
19355c11f1ffSHaibo Chen 		clk_set_rate(imx_data->clk_per, pltfm_host->clock);
19365c11f1ffSHaibo Chen 
1937a0ad3087SMichael Trimarchi 	err = clk_prepare_enable(imx_data->clk_ahb);
1938a0ad3087SMichael Trimarchi 	if (err)
19391c4989b0SBOUGH CHEN 		goto remove_pm_qos_request;
1940a0ad3087SMichael Trimarchi 
194117b1eb7fSFabio Estevam 	err = clk_prepare_enable(imx_data->clk_per);
194217b1eb7fSFabio Estevam 	if (err)
1943a0ad3087SMichael Trimarchi 		goto disable_ahb_clk;
1944af5d2b7bSUlf Hansson 
194517b1eb7fSFabio Estevam 	err = clk_prepare_enable(imx_data->clk_ipg);
194617b1eb7fSFabio Estevam 	if (err)
194717b1eb7fSFabio Estevam 		goto disable_per_clk;
1948af5d2b7bSUlf Hansson 
19493602785bSMichael Trimarchi 	esdhc_pltfm_set_clock(host, imx_data->actual_clock);
1950a0ad3087SMichael Trimarchi 
1951c6303c5dSBaolin Wang 	err = sdhci_runtime_resume_host(host, 0);
195217b1eb7fSFabio Estevam 	if (err)
1953a0ad3087SMichael Trimarchi 		goto disable_ipg_clk;
195489d7e5c1SDong Aisheng 
1955bb6e3581SBOUGH CHEN 	if (host->mmc->caps2 & MMC_CAP2_CQE)
1956bb6e3581SBOUGH CHEN 		err = cqhci_resume(host->mmc);
1957bb6e3581SBOUGH CHEN 
1958bb6e3581SBOUGH CHEN 	return err;
195917b1eb7fSFabio Estevam 
196017b1eb7fSFabio Estevam disable_ipg_clk:
196117b1eb7fSFabio Estevam 	clk_disable_unprepare(imx_data->clk_ipg);
196217b1eb7fSFabio Estevam disable_per_clk:
196317b1eb7fSFabio Estevam 	clk_disable_unprepare(imx_data->clk_per);
1964a0ad3087SMichael Trimarchi disable_ahb_clk:
1965a0ad3087SMichael Trimarchi 	clk_disable_unprepare(imx_data->clk_ahb);
19661c4989b0SBOUGH CHEN remove_pm_qos_request:
19671c4989b0SBOUGH CHEN 	if (imx_data->socdata->flags & ESDHC_FLAG_PMQOS)
1968d1b98305SRafael J. Wysocki 		cpu_latency_qos_remove_request(&imx_data->pm_qos_req);
196917b1eb7fSFabio Estevam 	return err;
197089d7e5c1SDong Aisheng }
197189d7e5c1SDong Aisheng #endif
197289d7e5c1SDong Aisheng 
197389d7e5c1SDong Aisheng static const struct dev_pm_ops sdhci_esdhc_pmops = {
197404143fbaSDong Aisheng 	SET_SYSTEM_SLEEP_PM_OPS(sdhci_esdhc_suspend, sdhci_esdhc_resume)
197589d7e5c1SDong Aisheng 	SET_RUNTIME_PM_OPS(sdhci_esdhc_runtime_suspend,
197689d7e5c1SDong Aisheng 				sdhci_esdhc_runtime_resume, NULL)
197789d7e5c1SDong Aisheng };
197889d7e5c1SDong Aisheng 
197985d6509dSShawn Guo static struct platform_driver sdhci_esdhc_imx_driver = {
198085d6509dSShawn Guo 	.driver		= {
198185d6509dSShawn Guo 		.name	= "sdhci-esdhc-imx",
198221b2cec6SDouglas Anderson 		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
1983abfafc2dSShawn Guo 		.of_match_table = imx_esdhc_dt_ids,
198489d7e5c1SDong Aisheng 		.pm	= &sdhci_esdhc_pmops,
198585d6509dSShawn Guo 	},
198685d6509dSShawn Guo 	.probe		= sdhci_esdhc_imx_probe,
19870433c143SBill Pemberton 	.remove		= sdhci_esdhc_imx_remove,
198895f25efeSWolfram Sang };
198985d6509dSShawn Guo 
1990d1f81a64SAxel Lin module_platform_driver(sdhci_esdhc_imx_driver);
199185d6509dSShawn Guo 
199285d6509dSShawn Guo MODULE_DESCRIPTION("SDHCI driver for Freescale i.MX eSDHC");
1993035ff831SWolfram Sang MODULE_AUTHOR("Wolfram Sang <kernel@pengutronix.de>");
199485d6509dSShawn Guo MODULE_LICENSE("GPL v2");
1995