1a6e7e407SFabio Estevam // SPDX-License-Identifier: GPL-2.0
295f25efeSWolfram Sang /*
395f25efeSWolfram Sang  * Freescale eSDHC i.MX controller driver for the platform bus.
495f25efeSWolfram Sang  *
595f25efeSWolfram Sang  * derived from the OF-version.
695f25efeSWolfram Sang  *
795f25efeSWolfram Sang  * Copyright (c) 2010 Pengutronix e.K.
8035ff831SWolfram Sang  *   Author: Wolfram Sang <kernel@pengutronix.de>
995f25efeSWolfram Sang  */
1095f25efeSWolfram Sang 
11a8e809ecSMasahiro Yamada #include <linux/bitfield.h>
1295f25efeSWolfram Sang #include <linux/io.h>
13f581e909SHaibo Chen #include <linux/iopoll.h>
1495f25efeSWolfram Sang #include <linux/delay.h>
1595f25efeSWolfram Sang #include <linux/err.h>
1695f25efeSWolfram Sang #include <linux/clk.h>
1766506f76SShawn Guo #include <linux/module.h>
18e149860dSRichard Zhu #include <linux/slab.h>
191c4989b0SBOUGH CHEN #include <linux/pm_qos.h>
2095f25efeSWolfram Sang #include <linux/mmc/host.h>
2158ac8177SRichard Zhu #include <linux/mmc/mmc.h>
2258ac8177SRichard Zhu #include <linux/mmc/sdio.h>
23fbe5fdd1SShawn Guo #include <linux/mmc/slot-gpio.h>
24abfafc2dSShawn Guo #include <linux/of.h>
25abfafc2dSShawn Guo #include <linux/of_device.h>
26e62d8b8fSDong Aisheng #include <linux/pinctrl/consumer.h>
2789d7e5c1SDong Aisheng #include <linux/pm_runtime.h>
2895f25efeSWolfram Sang #include "sdhci-pltfm.h"
2995f25efeSWolfram Sang #include "sdhci-esdhc.h"
30bb6e3581SBOUGH CHEN #include "cqhci.h"
3195f25efeSWolfram Sang 
32a215186dSHaibo Chen #define ESDHC_SYS_CTRL_DTOCV_MASK	0x0f
3360bf6396SShawn Guo #define	ESDHC_CTRL_D3CD			0x08
34fd44954eSHaibo Chen #define ESDHC_BURST_LEN_EN_INCR		(1 << 27)
3558ac8177SRichard Zhu /* VENDOR SPEC register */
3660bf6396SShawn Guo #define ESDHC_VENDOR_SPEC		0xc0
3760bf6396SShawn Guo #define  ESDHC_VENDOR_SPEC_SDIO_QUIRK	(1 << 1)
380322191eSDong Aisheng #define  ESDHC_VENDOR_SPEC_VSELECT	(1 << 1)
39fed2f6e2SDong Aisheng #define  ESDHC_VENDOR_SPEC_FRC_SDCLK_ON	(1 << 8)
403722c74cSHaibo Chen #define ESDHC_DEBUG_SEL_AND_STATUS_REG		0xc2
413722c74cSHaibo Chen #define ESDHC_DEBUG_SEL_REG			0xc3
423722c74cSHaibo Chen #define ESDHC_DEBUG_SEL_MASK			0xf
433722c74cSHaibo Chen #define ESDHC_DEBUG_SEL_CMD_STATE		1
443722c74cSHaibo Chen #define ESDHC_DEBUG_SEL_DATA_STATE		2
453722c74cSHaibo Chen #define ESDHC_DEBUG_SEL_TRANS_STATE		3
463722c74cSHaibo Chen #define ESDHC_DEBUG_SEL_DMA_STATE		4
473722c74cSHaibo Chen #define ESDHC_DEBUG_SEL_ADMA_STATE		5
483722c74cSHaibo Chen #define ESDHC_DEBUG_SEL_FIFO_STATE		6
493722c74cSHaibo Chen #define ESDHC_DEBUG_SEL_ASYNC_FIFO_STATE	7
5060bf6396SShawn Guo #define ESDHC_WTMK_LVL			0x44
51cc17e129SDong Aisheng #define  ESDHC_WTMK_DEFAULT_VAL		0x10401040
523fbd4322SAndrew Gabbasov #define  ESDHC_WTMK_LVL_RD_WML_MASK	0x000000FF
533fbd4322SAndrew Gabbasov #define  ESDHC_WTMK_LVL_RD_WML_SHIFT	0
543fbd4322SAndrew Gabbasov #define  ESDHC_WTMK_LVL_WR_WML_MASK	0x00FF0000
553fbd4322SAndrew Gabbasov #define  ESDHC_WTMK_LVL_WR_WML_SHIFT	16
563fbd4322SAndrew Gabbasov #define  ESDHC_WTMK_LVL_WML_VAL_DEF	64
573fbd4322SAndrew Gabbasov #define  ESDHC_WTMK_LVL_WML_VAL_MAX	128
5860bf6396SShawn Guo #define ESDHC_MIX_CTRL			0x48
59de5bdbffSDong Aisheng #define  ESDHC_MIX_CTRL_DDREN		(1 << 3)
602a15f981SShawn Guo #define  ESDHC_MIX_CTRL_AC23EN		(1 << 7)
610322191eSDong Aisheng #define  ESDHC_MIX_CTRL_EXE_TUNE	(1 << 22)
620322191eSDong Aisheng #define  ESDHC_MIX_CTRL_SMPCLK_SEL	(1 << 23)
630b330e38SDong Aisheng #define  ESDHC_MIX_CTRL_AUTO_TUNE_EN	(1 << 24)
640322191eSDong Aisheng #define  ESDHC_MIX_CTRL_FBCLK_SEL	(1 << 25)
6528b07674SHaibo Chen #define  ESDHC_MIX_CTRL_HS400_EN	(1 << 26)
66029e2476SBOUGH CHEN #define  ESDHC_MIX_CTRL_HS400_ES_EN	(1 << 27)
672a15f981SShawn Guo /* Bits 3 and 6 are not SDHCI standard definitions */
682a15f981SShawn Guo #define  ESDHC_MIX_CTRL_SDHCI_MASK	0xb7
69d131a71cSDong Aisheng /* Tuning bits */
70d131a71cSDong Aisheng #define  ESDHC_MIX_CTRL_TUNING_MASK	0x03c00000
7158ac8177SRichard Zhu 
72602519b2SDong Aisheng /* dll control register */
73602519b2SDong Aisheng #define ESDHC_DLL_CTRL			0x60
74602519b2SDong Aisheng #define ESDHC_DLL_OVERRIDE_VAL_SHIFT	9
75602519b2SDong Aisheng #define ESDHC_DLL_OVERRIDE_EN_SHIFT	8
76602519b2SDong Aisheng 
770322191eSDong Aisheng /* tune control register */
780322191eSDong Aisheng #define ESDHC_TUNE_CTRL_STATUS		0x68
790322191eSDong Aisheng #define  ESDHC_TUNE_CTRL_STEP		1
800322191eSDong Aisheng #define  ESDHC_TUNE_CTRL_MIN		0
810322191eSDong Aisheng #define  ESDHC_TUNE_CTRL_MAX		((1 << 7) - 1)
820322191eSDong Aisheng 
8328b07674SHaibo Chen /* strobe dll register */
8428b07674SHaibo Chen #define ESDHC_STROBE_DLL_CTRL		0x70
8528b07674SHaibo Chen #define ESDHC_STROBE_DLL_CTRL_ENABLE	(1 << 0)
8628b07674SHaibo Chen #define ESDHC_STROBE_DLL_CTRL_RESET	(1 << 1)
875bd2acdcSHaibo Chen #define ESDHC_STROBE_DLL_CTRL_SLV_DLY_TARGET_DEFAULT	0x7
8828b07674SHaibo Chen #define ESDHC_STROBE_DLL_CTRL_SLV_DLY_TARGET_SHIFT	3
892eaf5a53SBOUGH CHEN #define ESDHC_STROBE_DLL_CTRL_SLV_UPDATE_INT_DEFAULT	(4 << 20)
9028b07674SHaibo Chen 
9128b07674SHaibo Chen #define ESDHC_STROBE_DLL_STATUS		0x74
9228b07674SHaibo Chen #define ESDHC_STROBE_DLL_STS_REF_LOCK	(1 << 1)
9328b07674SHaibo Chen #define ESDHC_STROBE_DLL_STS_SLV_LOCK	0x1
9428b07674SHaibo Chen 
95bcdb5301SBOUGH CHEN #define ESDHC_VEND_SPEC2		0xc8
96bcdb5301SBOUGH CHEN #define ESDHC_VEND_SPEC2_EN_BUSY_IRQ	(1 << 8)
9745334ee1SHaibo Chen #define ESDHC_VEND_SPEC2_AUTO_TUNE_8BIT_EN	(1 << 4)
9845334ee1SHaibo Chen #define ESDHC_VEND_SPEC2_AUTO_TUNE_4BIT_EN	(0 << 4)
9945334ee1SHaibo Chen #define ESDHC_VEND_SPEC2_AUTO_TUNE_1BIT_EN	(2 << 4)
10045334ee1SHaibo Chen #define ESDHC_VEND_SPEC2_AUTO_TUNE_CMD_EN	(1 << 6)
10145334ee1SHaibo Chen #define ESDHC_VEND_SPEC2_AUTO_TUNE_MODE_MASK	(7 << 4)
102bcdb5301SBOUGH CHEN 
1036e9fd28eSDong Aisheng #define ESDHC_TUNING_CTRL		0xcc
1046e9fd28eSDong Aisheng #define ESDHC_STD_TUNING_EN		(1 << 24)
1056e9fd28eSDong Aisheng /* NOTE: the minimum valid tuning start tap for mx6sl is 1 */
106d87fc966SDong Aisheng #define ESDHC_TUNING_START_TAP_DEFAULT	0x1
1071194be8cSHaibo Chen #define ESDHC_TUNING_START_TAP_MASK	0x7f
10816e40e5bSHaibo Chen #define ESDHC_TUNING_CMD_CRC_CHECK_DISABLE	(1 << 7)
109260ecb3cSHaibo Chen #define ESDHC_TUNING_STEP_MASK		0x00070000
110d407e30bSHaibo Chen #define ESDHC_TUNING_STEP_SHIFT		16
1116e9fd28eSDong Aisheng 
112ad93220dSDong Aisheng /* pinctrl state */
113ad93220dSDong Aisheng #define ESDHC_PINCTRL_STATE_100MHZ	"state_100mhz"
114ad93220dSDong Aisheng #define ESDHC_PINCTRL_STATE_200MHZ	"state_200mhz"
115ad93220dSDong Aisheng 
11658ac8177SRichard Zhu /*
117af51079eSSascha Hauer  * Our interpretation of the SDHCI_HOST_CONTROL register
118af51079eSSascha Hauer  */
119af51079eSSascha Hauer #define ESDHC_CTRL_4BITBUS		(0x1 << 1)
120af51079eSSascha Hauer #define ESDHC_CTRL_8BITBUS		(0x2 << 1)
121af51079eSSascha Hauer #define ESDHC_CTRL_BUSWIDTH_MASK	(0x3 << 1)
12245334ee1SHaibo Chen #define USDHC_GET_BUSWIDTH(c) (c & ESDHC_CTRL_BUSWIDTH_MASK)
123af51079eSSascha Hauer 
124af51079eSSascha Hauer /*
125d04f8d5bSBenoît Thébaudeau  * There is an INT DMA ERR mismatch between eSDHC and STD SDHC SPEC:
12697e4ba6aSRichard Zhu  * Bit25 is used in STD SPEC, and is reserved in fsl eSDHC design,
12797e4ba6aSRichard Zhu  * but bit28 is used as the INT DMA ERR in fsl eSDHC design.
12897e4ba6aSRichard Zhu  * Define this macro DMA error INT for fsl eSDHC
12997e4ba6aSRichard Zhu  */
13060bf6396SShawn Guo #define ESDHC_INT_VENDOR_SPEC_DMA_ERR	(1 << 28)
13197e4ba6aSRichard Zhu 
132bb6e3581SBOUGH CHEN /* the address offset of CQHCI */
133bb6e3581SBOUGH CHEN #define ESDHC_CQHCI_ADDR_OFFSET		0x100
134bb6e3581SBOUGH CHEN 
13597e4ba6aSRichard Zhu /*
13658ac8177SRichard Zhu  * The CMDTYPE of the CMD register (offset 0xE) should be set to
13758ac8177SRichard Zhu  * "11" when the STOP CMD12 is issued on imx53 to abort one
13858ac8177SRichard Zhu  * open ended multi-blk IO. Otherwise the TC INT wouldn't
13958ac8177SRichard Zhu  * be generated.
14058ac8177SRichard Zhu  * In exact block transfer, the controller doesn't complete the
14158ac8177SRichard Zhu  * operations automatically as required at the end of the
14258ac8177SRichard Zhu  * transfer and remains on hold if the abort command is not sent.
14358ac8177SRichard Zhu  * As a result, the TC flag is not asserted and SW received timeout
144d04f8d5bSBenoît Thébaudeau  * exception. Bit1 of Vendor Spec register is used to fix it.
14558ac8177SRichard Zhu  */
14631fbb301SShawn Guo #define ESDHC_FLAG_MULTIBLK_NO_INT	BIT(1)
14731fbb301SShawn Guo /*
1489d61c009SShawn Guo  * The flag tells that the ESDHC controller is an USDHC block that is
1499d61c009SShawn Guo  * integrated on the i.MX6 series.
1509d61c009SShawn Guo  */
1519d61c009SShawn Guo #define ESDHC_FLAG_USDHC		BIT(3)
1526e9fd28eSDong Aisheng /* The IP supports manual tuning process */
1536e9fd28eSDong Aisheng #define ESDHC_FLAG_MAN_TUNING		BIT(4)
1546e9fd28eSDong Aisheng /* The IP supports standard tuning process */
1556e9fd28eSDong Aisheng #define ESDHC_FLAG_STD_TUNING		BIT(5)
1566e9fd28eSDong Aisheng /* The IP has SDHCI_CAPABILITIES_1 register */
1576e9fd28eSDong Aisheng #define ESDHC_FLAG_HAVE_CAP1		BIT(6)
15818094430SDong Aisheng /*
159d04f8d5bSBenoît Thébaudeau  * The IP has erratum ERR004536
16018094430SDong Aisheng  * uSDHC: ADMA Length Mismatch Error occurs if the AHB read access is slow,
16118094430SDong Aisheng  * when reading data from the card
162667123f6SBenoît Thébaudeau  * This flag is also set for i.MX25 and i.MX35 in order to get
163667123f6SBenoît Thébaudeau  * SDHCI_QUIRK_BROKEN_ADMA, but for different reasons (ADMA capability bits).
16418094430SDong Aisheng  */
16518094430SDong Aisheng #define ESDHC_FLAG_ERR004536		BIT(7)
1664245afffSDong Aisheng /* The IP supports HS200 mode */
1674245afffSDong Aisheng #define ESDHC_FLAG_HS200		BIT(8)
16828b07674SHaibo Chen /* The IP supports HS400 mode */
16928b07674SHaibo Chen #define ESDHC_FLAG_HS400		BIT(9)
170af6a50d4SBOUGH CHEN /*
171af6a50d4SBOUGH CHEN  * The IP has errata ERR010450
172af6a50d4SBOUGH CHEN  * uSDHC: Due to the I/O timing limit, for SDR mode, SD card clock can't
173af6a50d4SBOUGH CHEN  * exceed 150MHz, for DDR mode, SD card clock can't exceed 45MHz.
174af6a50d4SBOUGH CHEN  */
175af6a50d4SBOUGH CHEN #define ESDHC_FLAG_ERR010450		BIT(10)
176029e2476SBOUGH CHEN /* The IP supports HS400ES mode */
177029e2476SBOUGH CHEN #define ESDHC_FLAG_HS400_ES		BIT(11)
178bb6e3581SBOUGH CHEN /* The IP has Host Controller Interface for Command Queuing */
179bb6e3581SBOUGH CHEN #define ESDHC_FLAG_CQHCI		BIT(12)
1801c4989b0SBOUGH CHEN /* need request pmqos during low power */
1811c4989b0SBOUGH CHEN #define ESDHC_FLAG_PMQOS		BIT(13)
182a26a4f1bSHaibo Chen /* The IP state got lost in low power mode */
183a26a4f1bSHaibo Chen #define ESDHC_FLAG_STATE_LOST_IN_LPMODE		BIT(14)
1845c11f1ffSHaibo Chen /* The IP lost clock rate in PM_RUNTIME */
1855c11f1ffSHaibo Chen #define ESDHC_FLAG_CLK_RATE_LOST_IN_PM_RUNTIME	BIT(15)
18674898cbcSHaibo Chen /*
18774898cbcSHaibo Chen  * The IP do not support the ACMD23 feature completely when use ADMA mode.
18874898cbcSHaibo Chen  * In ADMA mode, it only use the 16 bit block count of the register 0x4
18974898cbcSHaibo Chen  * (BLOCK_ATT) as the CMD23's argument for ACMD23 mode, which means it will
19074898cbcSHaibo Chen  * ignore the upper 16 bit of the CMD23's argument. This will block the reliable
19174898cbcSHaibo Chen  * write operation in RPMB, because RPMB reliable write need to set the bit31
19274898cbcSHaibo Chen  * of the CMD23's argument.
19374898cbcSHaibo Chen  * imx6qpdl/imx6sx/imx6sl/imx7d has this limitation only for ADMA mode, SDMA
19474898cbcSHaibo Chen  * do not has this limitation. so when these SoC use ADMA mode, it need to
19574898cbcSHaibo Chen  * disable the ACMD23 feature.
19674898cbcSHaibo Chen  */
19774898cbcSHaibo Chen #define ESDHC_FLAG_BROKEN_AUTO_CMD23	BIT(16)
198e149860dSRichard Zhu 
199*5c4f0062SChester Lin /* ERR004536 is not applicable for the IP  */
200*5c4f0062SChester Lin #define ESDHC_FLAG_SKIP_ERR004536	BIT(17)
201*5c4f0062SChester Lin 
2024a11cc64SFabio Estevam enum wp_types {
2034a11cc64SFabio Estevam 	ESDHC_WP_NONE,		/* no WP, neither controller nor gpio */
2044a11cc64SFabio Estevam 	ESDHC_WP_CONTROLLER,	/* mmc controller internal WP */
2054a11cc64SFabio Estevam 	ESDHC_WP_GPIO,		/* external gpio pin for WP */
2064a11cc64SFabio Estevam };
2074a11cc64SFabio Estevam 
2084a11cc64SFabio Estevam enum cd_types {
2094a11cc64SFabio Estevam 	ESDHC_CD_NONE,		/* no CD, neither controller nor gpio */
2104a11cc64SFabio Estevam 	ESDHC_CD_CONTROLLER,	/* mmc controller internal CD */
2114a11cc64SFabio Estevam 	ESDHC_CD_GPIO,		/* external gpio pin for CD */
2124a11cc64SFabio Estevam 	ESDHC_CD_PERMANENT,	/* no CD, card permanently wired to host */
2134a11cc64SFabio Estevam };
2144a11cc64SFabio Estevam 
2154a11cc64SFabio Estevam /*
2164a11cc64SFabio Estevam  * struct esdhc_platform_data - platform data for esdhc on i.MX
2174a11cc64SFabio Estevam  *
2184a11cc64SFabio Estevam  * ESDHC_WP(CD)_CONTROLLER type is not available on i.MX25/35.
2194a11cc64SFabio Estevam  *
2204a11cc64SFabio Estevam  * @wp_type:	type of write_protect method (see wp_types enum above)
2214a11cc64SFabio Estevam  * @cd_type:	type of card_detect method (see cd_types enum above)
2224a11cc64SFabio Estevam  */
2234a11cc64SFabio Estevam 
2244a11cc64SFabio Estevam struct esdhc_platform_data {
2254a11cc64SFabio Estevam 	enum wp_types wp_type;
2264a11cc64SFabio Estevam 	enum cd_types cd_type;
2274a11cc64SFabio Estevam 	int max_bus_width;
2284a11cc64SFabio Estevam 	unsigned int delay_line;
2294a11cc64SFabio Estevam 	unsigned int tuning_step;       /* The delay cell steps in tuning procedure */
2304a11cc64SFabio Estevam 	unsigned int tuning_start_tap;	/* The start delay cell point in tuning procedure */
2314a11cc64SFabio Estevam 	unsigned int strobe_dll_delay_target;	/* The delay cell for strobe pad (read clock) */
2324a11cc64SFabio Estevam };
2334a11cc64SFabio Estevam 
234f47c4bbfSShawn Guo struct esdhc_soc_data {
235f47c4bbfSShawn Guo 	u32 flags;
236f47c4bbfSShawn Guo };
237f47c4bbfSShawn Guo 
2384f100012SAndrey Smirnov static const struct esdhc_soc_data esdhc_imx25_data = {
239667123f6SBenoît Thébaudeau 	.flags = ESDHC_FLAG_ERR004536,
240f47c4bbfSShawn Guo };
241f47c4bbfSShawn Guo 
2424f100012SAndrey Smirnov static const struct esdhc_soc_data esdhc_imx35_data = {
243667123f6SBenoît Thébaudeau 	.flags = ESDHC_FLAG_ERR004536,
244f47c4bbfSShawn Guo };
245f47c4bbfSShawn Guo 
2464f100012SAndrey Smirnov static const struct esdhc_soc_data esdhc_imx51_data = {
247f47c4bbfSShawn Guo 	.flags = 0,
248f47c4bbfSShawn Guo };
249f47c4bbfSShawn Guo 
2504f100012SAndrey Smirnov static const struct esdhc_soc_data esdhc_imx53_data = {
251f47c4bbfSShawn Guo 	.flags = ESDHC_FLAG_MULTIBLK_NO_INT,
252f47c4bbfSShawn Guo };
253f47c4bbfSShawn Guo 
2544f100012SAndrey Smirnov static const struct esdhc_soc_data usdhc_imx6q_data = {
25574898cbcSHaibo Chen 	.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_MAN_TUNING
25674898cbcSHaibo Chen 			| ESDHC_FLAG_BROKEN_AUTO_CMD23,
2576e9fd28eSDong Aisheng };
2586e9fd28eSDong Aisheng 
2594f100012SAndrey Smirnov static const struct esdhc_soc_data usdhc_imx6sl_data = {
2606e9fd28eSDong Aisheng 	.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
2614245afffSDong Aisheng 			| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_ERR004536
26274898cbcSHaibo Chen 			| ESDHC_FLAG_HS200
26374898cbcSHaibo Chen 			| ESDHC_FLAG_BROKEN_AUTO_CMD23,
26474898cbcSHaibo Chen };
26574898cbcSHaibo Chen 
26674898cbcSHaibo Chen static const struct esdhc_soc_data usdhc_imx6sll_data = {
26774898cbcSHaibo Chen 	.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
26874898cbcSHaibo Chen 			| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
26986b59671SHaibo Chen 			| ESDHC_FLAG_HS400
27074898cbcSHaibo Chen 			| ESDHC_FLAG_STATE_LOST_IN_LPMODE,
27157ed3314SShawn Guo };
27257ed3314SShawn Guo 
2734f100012SAndrey Smirnov static const struct esdhc_soc_data usdhc_imx6sx_data = {
274913d4951SDong Aisheng 	.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
275a26a4f1bSHaibo Chen 			| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
27674898cbcSHaibo Chen 			| ESDHC_FLAG_STATE_LOST_IN_LPMODE
27774898cbcSHaibo Chen 			| ESDHC_FLAG_BROKEN_AUTO_CMD23,
278913d4951SDong Aisheng };
279913d4951SDong Aisheng 
280af6a50d4SBOUGH CHEN static const struct esdhc_soc_data usdhc_imx6ull_data = {
281af6a50d4SBOUGH CHEN 	.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
282af6a50d4SBOUGH CHEN 			| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
283a26a4f1bSHaibo Chen 			| ESDHC_FLAG_ERR010450
284a26a4f1bSHaibo Chen 			| ESDHC_FLAG_STATE_LOST_IN_LPMODE,
285af6a50d4SBOUGH CHEN };
286af6a50d4SBOUGH CHEN 
2874f100012SAndrey Smirnov static const struct esdhc_soc_data usdhc_imx7d_data = {
28828b07674SHaibo Chen 	.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
28928b07674SHaibo Chen 			| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
290a26a4f1bSHaibo Chen 			| ESDHC_FLAG_HS400
29174898cbcSHaibo Chen 			| ESDHC_FLAG_STATE_LOST_IN_LPMODE
29274898cbcSHaibo Chen 			| ESDHC_FLAG_BROKEN_AUTO_CMD23,
29328b07674SHaibo Chen };
29428b07674SHaibo Chen 
295*5c4f0062SChester Lin static struct esdhc_soc_data usdhc_s32g2_data = {
296*5c4f0062SChester Lin 	.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_MAN_TUNING
297*5c4f0062SChester Lin 			| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
298*5c4f0062SChester Lin 			| ESDHC_FLAG_HS400 | ESDHC_FLAG_HS400_ES
299*5c4f0062SChester Lin 			| ESDHC_FLAG_SKIP_ERR004536,
300*5c4f0062SChester Lin };
301*5c4f0062SChester Lin 
3021c4989b0SBOUGH CHEN static struct esdhc_soc_data usdhc_imx7ulp_data = {
3031c4989b0SBOUGH CHEN 	.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
3041c4989b0SBOUGH CHEN 			| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
305a26a4f1bSHaibo Chen 			| ESDHC_FLAG_PMQOS | ESDHC_FLAG_HS400
306a26a4f1bSHaibo Chen 			| ESDHC_FLAG_STATE_LOST_IN_LPMODE,
3071c4989b0SBOUGH CHEN };
3081c4989b0SBOUGH CHEN 
309029e2476SBOUGH CHEN static struct esdhc_soc_data usdhc_imx8qxp_data = {
310029e2476SBOUGH CHEN 	.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
311029e2476SBOUGH CHEN 			| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
312bb6e3581SBOUGH CHEN 			| ESDHC_FLAG_HS400 | ESDHC_FLAG_HS400_ES
313a26a4f1bSHaibo Chen 			| ESDHC_FLAG_CQHCI
3145c11f1ffSHaibo Chen 			| ESDHC_FLAG_STATE_LOST_IN_LPMODE
3155c11f1ffSHaibo Chen 			| ESDHC_FLAG_CLK_RATE_LOST_IN_PM_RUNTIME,
316029e2476SBOUGH CHEN };
317029e2476SBOUGH CHEN 
318cde5e8e9SHaibo Chen static struct esdhc_soc_data usdhc_imx8mm_data = {
319cde5e8e9SHaibo Chen 	.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
320cde5e8e9SHaibo Chen 			| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
321cde5e8e9SHaibo Chen 			| ESDHC_FLAG_HS400 | ESDHC_FLAG_HS400_ES
322cde5e8e9SHaibo Chen 			| ESDHC_FLAG_CQHCI
323cde5e8e9SHaibo Chen 			| ESDHC_FLAG_STATE_LOST_IN_LPMODE,
324e149860dSRichard Zhu };
325e149860dSRichard Zhu 
326e149860dSRichard Zhu struct pltfm_imx_data {
327e149860dSRichard Zhu 	u32 scratchpad;
328e62d8b8fSDong Aisheng 	struct pinctrl *pinctrl;
329ad93220dSDong Aisheng 	struct pinctrl_state *pins_100mhz;
330ad93220dSDong Aisheng 	struct pinctrl_state *pins_200mhz;
331f47c4bbfSShawn Guo 	const struct esdhc_soc_data *socdata;
332842afc02SShawn Guo 	struct esdhc_platform_data boarddata;
33352dac615SSascha Hauer 	struct clk *clk_ipg;
33452dac615SSascha Hauer 	struct clk *clk_ahb;
33552dac615SSascha Hauer 	struct clk *clk_per;
3363602785bSMichael Trimarchi 	unsigned int actual_clock;
337361b8482SLucas Stach 	enum {
338361b8482SLucas Stach 		NO_CMD_PENDING,      /* no multiblock command pending */
339361b8482SLucas Stach 		MULTIBLK_IN_PROCESS, /* exact multiblock cmd in process */
340361b8482SLucas Stach 		WAIT_FOR_INT,        /* sent CMD12, waiting for response INT */
341361b8482SLucas Stach 	} multiblock_status;
342de5bdbffSDong Aisheng 	u32 is_ddr;
3431c4989b0SBOUGH CHEN 	struct pm_qos_request pm_qos_req;
344e149860dSRichard Zhu };
345e149860dSRichard Zhu 
346abfafc2dSShawn Guo static const struct of_device_id imx_esdhc_dt_ids[] = {
347f47c4bbfSShawn Guo 	{ .compatible = "fsl,imx25-esdhc", .data = &esdhc_imx25_data, },
348f47c4bbfSShawn Guo 	{ .compatible = "fsl,imx35-esdhc", .data = &esdhc_imx35_data, },
349f47c4bbfSShawn Guo 	{ .compatible = "fsl,imx51-esdhc", .data = &esdhc_imx51_data, },
350f47c4bbfSShawn Guo 	{ .compatible = "fsl,imx53-esdhc", .data = &esdhc_imx53_data, },
351913d4951SDong Aisheng 	{ .compatible = "fsl,imx6sx-usdhc", .data = &usdhc_imx6sx_data, },
3526e9fd28eSDong Aisheng 	{ .compatible = "fsl,imx6sl-usdhc", .data = &usdhc_imx6sl_data, },
35374898cbcSHaibo Chen 	{ .compatible = "fsl,imx6sll-usdhc", .data = &usdhc_imx6sll_data, },
354f47c4bbfSShawn Guo 	{ .compatible = "fsl,imx6q-usdhc", .data = &usdhc_imx6q_data, },
355af6a50d4SBOUGH CHEN 	{ .compatible = "fsl,imx6ull-usdhc", .data = &usdhc_imx6ull_data, },
35628b07674SHaibo Chen 	{ .compatible = "fsl,imx7d-usdhc", .data = &usdhc_imx7d_data, },
3571c4989b0SBOUGH CHEN 	{ .compatible = "fsl,imx7ulp-usdhc", .data = &usdhc_imx7ulp_data, },
358029e2476SBOUGH CHEN 	{ .compatible = "fsl,imx8qxp-usdhc", .data = &usdhc_imx8qxp_data, },
359cde5e8e9SHaibo Chen 	{ .compatible = "fsl,imx8mm-usdhc", .data = &usdhc_imx8mm_data, },
360*5c4f0062SChester Lin 	{ .compatible = "nxp,s32g2-usdhc", .data = &usdhc_s32g2_data, },
361abfafc2dSShawn Guo 	{ /* sentinel */ }
362abfafc2dSShawn Guo };
363abfafc2dSShawn Guo MODULE_DEVICE_TABLE(of, imx_esdhc_dt_ids);
364abfafc2dSShawn Guo 
36557ed3314SShawn Guo static inline int is_imx25_esdhc(struct pltfm_imx_data *data)
36657ed3314SShawn Guo {
367f47c4bbfSShawn Guo 	return data->socdata == &esdhc_imx25_data;
36857ed3314SShawn Guo }
36957ed3314SShawn Guo 
37057ed3314SShawn Guo static inline int is_imx53_esdhc(struct pltfm_imx_data *data)
37157ed3314SShawn Guo {
372f47c4bbfSShawn Guo 	return data->socdata == &esdhc_imx53_data;
37357ed3314SShawn Guo }
37457ed3314SShawn Guo 
3759d61c009SShawn Guo static inline int esdhc_is_usdhc(struct pltfm_imx_data *data)
3769d61c009SShawn Guo {
377f47c4bbfSShawn Guo 	return !!(data->socdata->flags & ESDHC_FLAG_USDHC);
3789d61c009SShawn Guo }
3799d61c009SShawn Guo 
38095f25efeSWolfram Sang static inline void esdhc_clrset_le(struct sdhci_host *host, u32 mask, u32 val, int reg)
38195f25efeSWolfram Sang {
38295f25efeSWolfram Sang 	void __iomem *base = host->ioaddr + (reg & ~0x3);
38395f25efeSWolfram Sang 	u32 shift = (reg & 0x3) * 8;
38495f25efeSWolfram Sang 
38595f25efeSWolfram Sang 	writel(((readl(base) & ~(mask << shift)) | (val << shift)), base);
38695f25efeSWolfram Sang }
38795f25efeSWolfram Sang 
3883722c74cSHaibo Chen #define DRIVER_NAME "sdhci-esdhc-imx"
3893722c74cSHaibo Chen #define ESDHC_IMX_DUMP(f, x...) \
3903722c74cSHaibo Chen 	pr_err("%s: " DRIVER_NAME ": " f, mmc_hostname(host->mmc), ## x)
3913722c74cSHaibo Chen static void esdhc_dump_debug_regs(struct sdhci_host *host)
3923722c74cSHaibo Chen {
3933722c74cSHaibo Chen 	int i;
3943722c74cSHaibo Chen 	char *debug_status[7] = {
3953722c74cSHaibo Chen 				 "cmd debug status",
3963722c74cSHaibo Chen 				 "data debug status",
3973722c74cSHaibo Chen 				 "trans debug status",
3983722c74cSHaibo Chen 				 "dma debug status",
3993722c74cSHaibo Chen 				 "adma debug status",
4003722c74cSHaibo Chen 				 "fifo debug status",
4013722c74cSHaibo Chen 				 "async fifo debug status"
4023722c74cSHaibo Chen 	};
4033722c74cSHaibo Chen 
4043722c74cSHaibo Chen 	ESDHC_IMX_DUMP("========= ESDHC IMX DEBUG STATUS DUMP =========\n");
4053722c74cSHaibo Chen 	for (i = 0; i < 7; i++) {
4063722c74cSHaibo Chen 		esdhc_clrset_le(host, ESDHC_DEBUG_SEL_MASK,
4073722c74cSHaibo Chen 			ESDHC_DEBUG_SEL_CMD_STATE + i, ESDHC_DEBUG_SEL_REG);
4083722c74cSHaibo Chen 		ESDHC_IMX_DUMP("%s:  0x%04x\n", debug_status[i],
4093722c74cSHaibo Chen 			readw(host->ioaddr + ESDHC_DEBUG_SEL_AND_STATUS_REG));
4103722c74cSHaibo Chen 	}
4113722c74cSHaibo Chen 
4123722c74cSHaibo Chen 	esdhc_clrset_le(host, ESDHC_DEBUG_SEL_MASK, 0, ESDHC_DEBUG_SEL_REG);
4133722c74cSHaibo Chen 
4143722c74cSHaibo Chen }
4153722c74cSHaibo Chen 
416f581e909SHaibo Chen static inline void esdhc_wait_for_card_clock_gate_off(struct sdhci_host *host)
417f581e909SHaibo Chen {
418f581e909SHaibo Chen 	u32 present_state;
419f581e909SHaibo Chen 	int ret;
420f581e909SHaibo Chen 
421f581e909SHaibo Chen 	ret = readl_poll_timeout(host->ioaddr + ESDHC_PRSSTAT, present_state,
422f581e909SHaibo Chen 				(present_state & ESDHC_CLOCK_GATE_OFF), 2, 100);
423f581e909SHaibo Chen 	if (ret == -ETIMEDOUT)
424f581e909SHaibo Chen 		dev_warn(mmc_dev(host->mmc), "%s: card clock still not gate off in 100us!.\n", __func__);
425f581e909SHaibo Chen }
426f581e909SHaibo Chen 
42745334ee1SHaibo Chen /* Enable the auto tuning circuit to check the CMD line and BUS line */
42845334ee1SHaibo Chen static inline void usdhc_auto_tuning_mode_sel(struct sdhci_host *host)
42945334ee1SHaibo Chen {
43045334ee1SHaibo Chen 	u32 buswidth, auto_tune_buswidth;
43145334ee1SHaibo Chen 
43245334ee1SHaibo Chen 	buswidth = USDHC_GET_BUSWIDTH(readl(host->ioaddr + SDHCI_HOST_CONTROL));
43345334ee1SHaibo Chen 
43445334ee1SHaibo Chen 	switch (buswidth) {
43545334ee1SHaibo Chen 	case ESDHC_CTRL_8BITBUS:
43645334ee1SHaibo Chen 		auto_tune_buswidth = ESDHC_VEND_SPEC2_AUTO_TUNE_8BIT_EN;
43745334ee1SHaibo Chen 		break;
43845334ee1SHaibo Chen 	case ESDHC_CTRL_4BITBUS:
43945334ee1SHaibo Chen 		auto_tune_buswidth = ESDHC_VEND_SPEC2_AUTO_TUNE_4BIT_EN;
44045334ee1SHaibo Chen 		break;
44145334ee1SHaibo Chen 	default:	/* 1BITBUS */
44245334ee1SHaibo Chen 		auto_tune_buswidth = ESDHC_VEND_SPEC2_AUTO_TUNE_1BIT_EN;
44345334ee1SHaibo Chen 		break;
44445334ee1SHaibo Chen 	}
44545334ee1SHaibo Chen 
44645334ee1SHaibo Chen 	esdhc_clrset_le(host, ESDHC_VEND_SPEC2_AUTO_TUNE_MODE_MASK,
44745334ee1SHaibo Chen 			auto_tune_buswidth | ESDHC_VEND_SPEC2_AUTO_TUNE_CMD_EN,
44845334ee1SHaibo Chen 			ESDHC_VEND_SPEC2);
44945334ee1SHaibo Chen }
45045334ee1SHaibo Chen 
4517e29c306SWolfram Sang static u32 esdhc_readl_le(struct sdhci_host *host, int reg)
4527e29c306SWolfram Sang {
453361b8482SLucas Stach 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
454070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
455913413c3SShawn Guo 	u32 val = readl(host->ioaddr + reg);
456913413c3SShawn Guo 
4570322191eSDong Aisheng 	if (unlikely(reg == SDHCI_PRESENT_STATE)) {
4580322191eSDong Aisheng 		u32 fsl_prss = val;
4590322191eSDong Aisheng 		/* save the least 20 bits */
4600322191eSDong Aisheng 		val = fsl_prss & 0x000FFFFF;
4610322191eSDong Aisheng 		/* move dat[0-3] bits */
4620322191eSDong Aisheng 		val |= (fsl_prss & 0x0F000000) >> 4;
4630322191eSDong Aisheng 		/* move cmd line bit */
4640322191eSDong Aisheng 		val |= (fsl_prss & 0x00800000) << 1;
4650322191eSDong Aisheng 	}
4660322191eSDong Aisheng 
46797e4ba6aSRichard Zhu 	if (unlikely(reg == SDHCI_CAPABILITIES)) {
4686b4fb671SDong Aisheng 		/* ignore bit[0-15] as it stores cap_1 register val for mx6sl */
4696b4fb671SDong Aisheng 		if (imx_data->socdata->flags & ESDHC_FLAG_HAVE_CAP1)
4706b4fb671SDong Aisheng 			val &= 0xffff0000;
4716b4fb671SDong Aisheng 
47297e4ba6aSRichard Zhu 		/* In FSL esdhc IC module, only bit20 is used to indicate the
47397e4ba6aSRichard Zhu 		 * ADMA2 capability of esdhc, but this bit is messed up on
47497e4ba6aSRichard Zhu 		 * some SOCs (e.g. on MX25, MX35 this bit is set, but they
47597e4ba6aSRichard Zhu 		 * don't actually support ADMA2). So set the BROKEN_ADMA
476d04f8d5bSBenoît Thébaudeau 		 * quirk on MX25/35 platforms.
47797e4ba6aSRichard Zhu 		 */
47897e4ba6aSRichard Zhu 
47997e4ba6aSRichard Zhu 		if (val & SDHCI_CAN_DO_ADMA1) {
48097e4ba6aSRichard Zhu 			val &= ~SDHCI_CAN_DO_ADMA1;
48197e4ba6aSRichard Zhu 			val |= SDHCI_CAN_DO_ADMA2;
48297e4ba6aSRichard Zhu 		}
48397e4ba6aSRichard Zhu 	}
48497e4ba6aSRichard Zhu 
4856e9fd28eSDong Aisheng 	if (unlikely(reg == SDHCI_CAPABILITIES_1)) {
4866e9fd28eSDong Aisheng 		if (esdhc_is_usdhc(imx_data)) {
4876e9fd28eSDong Aisheng 			if (imx_data->socdata->flags & ESDHC_FLAG_HAVE_CAP1)
4886e9fd28eSDong Aisheng 				val = readl(host->ioaddr + SDHCI_CAPABILITIES) & 0xFFFF;
4896e9fd28eSDong Aisheng 			else
4906e9fd28eSDong Aisheng 				/* imx6q/dl does not have cap_1 register, fake one */
4910322191eSDong Aisheng 				val = SDHCI_SUPPORT_DDR50 | SDHCI_SUPPORT_SDR104
492888824bbSDong Aisheng 					| SDHCI_SUPPORT_SDR50
493da0295ffSDong Aisheng 					| SDHCI_USE_SDR50_TUNING
494a8e809ecSMasahiro Yamada 					| FIELD_PREP(SDHCI_RETUNING_MODE_MASK,
495a8e809ecSMasahiro Yamada 						     SDHCI_TUNING_MODE_3);
49628b07674SHaibo Chen 
49792748beaSStefan Agner 			/*
49892748beaSStefan Agner 			 * Do not advertise faster UHS modes if there are no
49992748beaSStefan Agner 			 * pinctrl states for 100MHz/200MHz.
50092748beaSStefan Agner 			 */
50125e8b9ebSShawn Guo 			if (IS_ERR_OR_NULL(imx_data->pins_100mhz))
50225e8b9ebSShawn Guo 				val &= ~(SDHCI_SUPPORT_SDR50 | SDHCI_SUPPORT_DDR50);
50325e8b9ebSShawn Guo 			if (IS_ERR_OR_NULL(imx_data->pins_200mhz))
50425e8b9ebSShawn Guo 				val &= ~(SDHCI_SUPPORT_SDR104 | SDHCI_SUPPORT_HS400);
5056e9fd28eSDong Aisheng 		}
5066e9fd28eSDong Aisheng 	}
5070322191eSDong Aisheng 
5089d61c009SShawn Guo 	if (unlikely(reg == SDHCI_MAX_CURRENT) && esdhc_is_usdhc(imx_data)) {
5090322191eSDong Aisheng 		val = 0;
510804a65b3SMasahiro Yamada 		val |= FIELD_PREP(SDHCI_MAX_CURRENT_330_MASK, 0xFF);
511804a65b3SMasahiro Yamada 		val |= FIELD_PREP(SDHCI_MAX_CURRENT_300_MASK, 0xFF);
512804a65b3SMasahiro Yamada 		val |= FIELD_PREP(SDHCI_MAX_CURRENT_180_MASK, 0xFF);
5130322191eSDong Aisheng 	}
5140322191eSDong Aisheng 
51597e4ba6aSRichard Zhu 	if (unlikely(reg == SDHCI_INT_STATUS)) {
51660bf6396SShawn Guo 		if (val & ESDHC_INT_VENDOR_SPEC_DMA_ERR) {
51760bf6396SShawn Guo 			val &= ~ESDHC_INT_VENDOR_SPEC_DMA_ERR;
51897e4ba6aSRichard Zhu 			val |= SDHCI_INT_ADMA_ERROR;
51997e4ba6aSRichard Zhu 		}
520361b8482SLucas Stach 
521361b8482SLucas Stach 		/*
522361b8482SLucas Stach 		 * mask off the interrupt we get in response to the manually
523361b8482SLucas Stach 		 * sent CMD12
524361b8482SLucas Stach 		 */
525361b8482SLucas Stach 		if ((imx_data->multiblock_status == WAIT_FOR_INT) &&
526361b8482SLucas Stach 		    ((val & SDHCI_INT_RESPONSE) == SDHCI_INT_RESPONSE)) {
527361b8482SLucas Stach 			val &= ~SDHCI_INT_RESPONSE;
528361b8482SLucas Stach 			writel(SDHCI_INT_RESPONSE, host->ioaddr +
529361b8482SLucas Stach 						   SDHCI_INT_STATUS);
530361b8482SLucas Stach 			imx_data->multiblock_status = NO_CMD_PENDING;
531361b8482SLucas Stach 		}
53297e4ba6aSRichard Zhu 	}
53397e4ba6aSRichard Zhu 
5347e29c306SWolfram Sang 	return val;
5357e29c306SWolfram Sang }
5367e29c306SWolfram Sang 
5377e29c306SWolfram Sang static void esdhc_writel_le(struct sdhci_host *host, u32 val, int reg)
5387e29c306SWolfram Sang {
539e149860dSRichard Zhu 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
540070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
5410d58864bSTony Lin 	u32 data;
542e149860dSRichard Zhu 
54377da3da0SAaron Brice 	if (unlikely(reg == SDHCI_INT_ENABLE || reg == SDHCI_SIGNAL_ENABLE ||
54477da3da0SAaron Brice 			reg == SDHCI_INT_STATUS)) {
545b7321042SDong Aisheng 		if ((val & SDHCI_INT_CARD_INT) && !esdhc_is_usdhc(imx_data)) {
5460d58864bSTony Lin 			/*
5470d58864bSTony Lin 			 * Clear and then set D3CD bit to avoid missing the
548d04f8d5bSBenoît Thébaudeau 			 * card interrupt. This is an eSDHC controller problem
5490d58864bSTony Lin 			 * so we need to apply the following workaround: clear
5500d58864bSTony Lin 			 * and set D3CD bit will make eSDHC re-sample the card
5510d58864bSTony Lin 			 * interrupt. In case a card interrupt was lost,
5520d58864bSTony Lin 			 * re-sample it by the following steps.
5530d58864bSTony Lin 			 */
5540d58864bSTony Lin 			data = readl(host->ioaddr + SDHCI_HOST_CONTROL);
55560bf6396SShawn Guo 			data &= ~ESDHC_CTRL_D3CD;
5560d58864bSTony Lin 			writel(data, host->ioaddr + SDHCI_HOST_CONTROL);
55760bf6396SShawn Guo 			data |= ESDHC_CTRL_D3CD;
5580d58864bSTony Lin 			writel(data, host->ioaddr + SDHCI_HOST_CONTROL);
5590d58864bSTony Lin 		}
560915be485SDong Aisheng 
561915be485SDong Aisheng 		if (val & SDHCI_INT_ADMA_ERROR) {
562915be485SDong Aisheng 			val &= ~SDHCI_INT_ADMA_ERROR;
563915be485SDong Aisheng 			val |= ESDHC_INT_VENDOR_SPEC_DMA_ERR;
564915be485SDong Aisheng 		}
5650d58864bSTony Lin 	}
5660d58864bSTony Lin 
567f47c4bbfSShawn Guo 	if (unlikely((imx_data->socdata->flags & ESDHC_FLAG_MULTIBLK_NO_INT)
56858ac8177SRichard Zhu 				&& (reg == SDHCI_INT_STATUS)
56958ac8177SRichard Zhu 				&& (val & SDHCI_INT_DATA_END))) {
57058ac8177SRichard Zhu 			u32 v;
57160bf6396SShawn Guo 			v = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
57260bf6396SShawn Guo 			v &= ~ESDHC_VENDOR_SPEC_SDIO_QUIRK;
57360bf6396SShawn Guo 			writel(v, host->ioaddr + ESDHC_VENDOR_SPEC);
574361b8482SLucas Stach 
575361b8482SLucas Stach 			if (imx_data->multiblock_status == MULTIBLK_IN_PROCESS)
576361b8482SLucas Stach 			{
577361b8482SLucas Stach 				/* send a manual CMD12 with RESPTYP=none */
578361b8482SLucas Stach 				data = MMC_STOP_TRANSMISSION << 24 |
579361b8482SLucas Stach 				       SDHCI_CMD_ABORTCMD << 16;
580361b8482SLucas Stach 				writel(data, host->ioaddr + SDHCI_TRANSFER_MODE);
581361b8482SLucas Stach 				imx_data->multiblock_status = WAIT_FOR_INT;
582361b8482SLucas Stach 			}
58358ac8177SRichard Zhu 	}
58458ac8177SRichard Zhu 
5857e29c306SWolfram Sang 	writel(val, host->ioaddr + reg);
5867e29c306SWolfram Sang }
5877e29c306SWolfram Sang 
58895f25efeSWolfram Sang static u16 esdhc_readw_le(struct sdhci_host *host, int reg)
58995f25efeSWolfram Sang {
590ef4d0888SShawn Guo 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
591070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
5920322191eSDong Aisheng 	u16 ret = 0;
5930322191eSDong Aisheng 	u32 val;
594ef4d0888SShawn Guo 
59595a2482aSShawn Guo 	if (unlikely(reg == SDHCI_HOST_VERSION)) {
596ef4d0888SShawn Guo 		reg ^= 2;
5979d61c009SShawn Guo 		if (esdhc_is_usdhc(imx_data)) {
59895a2482aSShawn Guo 			/*
599ef4d0888SShawn Guo 			 * The usdhc register returns a wrong host version.
600ef4d0888SShawn Guo 			 * Correct it here.
60195a2482aSShawn Guo 			 */
602ef4d0888SShawn Guo 			return SDHCI_SPEC_300;
603ef4d0888SShawn Guo 		}
60495a2482aSShawn Guo 	}
60595f25efeSWolfram Sang 
6060322191eSDong Aisheng 	if (unlikely(reg == SDHCI_HOST_CONTROL2)) {
6070322191eSDong Aisheng 		val = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
6080322191eSDong Aisheng 		if (val & ESDHC_VENDOR_SPEC_VSELECT)
6090322191eSDong Aisheng 			ret |= SDHCI_CTRL_VDD_180;
6100322191eSDong Aisheng 
6119d61c009SShawn Guo 		if (esdhc_is_usdhc(imx_data)) {
6126e9fd28eSDong Aisheng 			if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING)
6130322191eSDong Aisheng 				val = readl(host->ioaddr + ESDHC_MIX_CTRL);
6146e9fd28eSDong Aisheng 			else if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING)
6156e9fd28eSDong Aisheng 				/* the std tuning bits is in ACMD12_ERR for imx6sl */
616869f8a69SAdrian Hunter 				val = readl(host->ioaddr + SDHCI_AUTO_CMD_STATUS);
6176e9fd28eSDong Aisheng 		}
6186e9fd28eSDong Aisheng 
6190322191eSDong Aisheng 		if (val & ESDHC_MIX_CTRL_EXE_TUNE)
6200322191eSDong Aisheng 			ret |= SDHCI_CTRL_EXEC_TUNING;
6210322191eSDong Aisheng 		if (val & ESDHC_MIX_CTRL_SMPCLK_SEL)
6220322191eSDong Aisheng 			ret |= SDHCI_CTRL_TUNED_CLK;
6230322191eSDong Aisheng 
6240322191eSDong Aisheng 		ret &= ~SDHCI_CTRL_PRESET_VAL_ENABLE;
6250322191eSDong Aisheng 
6260322191eSDong Aisheng 		return ret;
6270322191eSDong Aisheng 	}
6280322191eSDong Aisheng 
6297dd109efSDong Aisheng 	if (unlikely(reg == SDHCI_TRANSFER_MODE)) {
6307dd109efSDong Aisheng 		if (esdhc_is_usdhc(imx_data)) {
6317dd109efSDong Aisheng 			u32 m = readl(host->ioaddr + ESDHC_MIX_CTRL);
6327dd109efSDong Aisheng 			ret = m & ESDHC_MIX_CTRL_SDHCI_MASK;
6337dd109efSDong Aisheng 			/* Swap AC23 bit */
6347dd109efSDong Aisheng 			if (m & ESDHC_MIX_CTRL_AC23EN) {
6357dd109efSDong Aisheng 				ret &= ~ESDHC_MIX_CTRL_AC23EN;
6367dd109efSDong Aisheng 				ret |= SDHCI_TRNS_AUTO_CMD23;
6377dd109efSDong Aisheng 			}
6387dd109efSDong Aisheng 		} else {
6397dd109efSDong Aisheng 			ret = readw(host->ioaddr + SDHCI_TRANSFER_MODE);
6407dd109efSDong Aisheng 		}
6417dd109efSDong Aisheng 
6427dd109efSDong Aisheng 		return ret;
6437dd109efSDong Aisheng 	}
6447dd109efSDong Aisheng 
64595f25efeSWolfram Sang 	return readw(host->ioaddr + reg);
64695f25efeSWolfram Sang }
64795f25efeSWolfram Sang 
64895f25efeSWolfram Sang static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg)
64995f25efeSWolfram Sang {
65095f25efeSWolfram Sang 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
651070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
6520322191eSDong Aisheng 	u32 new_val = 0;
65395f25efeSWolfram Sang 
65495f25efeSWolfram Sang 	switch (reg) {
6550322191eSDong Aisheng 	case SDHCI_CLOCK_CONTROL:
6560322191eSDong Aisheng 		new_val = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
6570322191eSDong Aisheng 		if (val & SDHCI_CLOCK_CARD_EN)
6580322191eSDong Aisheng 			new_val |= ESDHC_VENDOR_SPEC_FRC_SDCLK_ON;
6590322191eSDong Aisheng 		else
6600322191eSDong Aisheng 			new_val &= ~ESDHC_VENDOR_SPEC_FRC_SDCLK_ON;
6610322191eSDong Aisheng 		writel(new_val, host->ioaddr + ESDHC_VENDOR_SPEC);
662f581e909SHaibo Chen 		if (!(new_val & ESDHC_VENDOR_SPEC_FRC_SDCLK_ON))
663f581e909SHaibo Chen 			esdhc_wait_for_card_clock_gate_off(host);
6640322191eSDong Aisheng 		return;
6650322191eSDong Aisheng 	case SDHCI_HOST_CONTROL2:
6660322191eSDong Aisheng 		new_val = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
6670322191eSDong Aisheng 		if (val & SDHCI_CTRL_VDD_180)
6680322191eSDong Aisheng 			new_val |= ESDHC_VENDOR_SPEC_VSELECT;
6690322191eSDong Aisheng 		else
6700322191eSDong Aisheng 			new_val &= ~ESDHC_VENDOR_SPEC_VSELECT;
6710322191eSDong Aisheng 		writel(new_val, host->ioaddr + ESDHC_VENDOR_SPEC);
672a0dbbdc2SHaibo Chen 		if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) {
673869f8a69SAdrian Hunter 			u32 v = readl(host->ioaddr + SDHCI_AUTO_CMD_STATUS);
6746e9fd28eSDong Aisheng 			u32 m = readl(host->ioaddr + ESDHC_MIX_CTRL);
6758b2bb0adSDong Aisheng 			if (val & SDHCI_CTRL_TUNED_CLK) {
6768b2bb0adSDong Aisheng 				v |= ESDHC_MIX_CTRL_SMPCLK_SEL;
6776e9fd28eSDong Aisheng 			} else {
6788b2bb0adSDong Aisheng 				v &= ~ESDHC_MIX_CTRL_SMPCLK_SEL;
6796e9fd28eSDong Aisheng 				m &= ~ESDHC_MIX_CTRL_FBCLK_SEL;
6800b330e38SDong Aisheng 				m &= ~ESDHC_MIX_CTRL_AUTO_TUNE_EN;
6816e9fd28eSDong Aisheng 			}
6826e9fd28eSDong Aisheng 
6838b2bb0adSDong Aisheng 			if (val & SDHCI_CTRL_EXEC_TUNING) {
6848b2bb0adSDong Aisheng 				v |= ESDHC_MIX_CTRL_EXE_TUNE;
6858b2bb0adSDong Aisheng 				m |= ESDHC_MIX_CTRL_FBCLK_SEL;
6860b330e38SDong Aisheng 				m |= ESDHC_MIX_CTRL_AUTO_TUNE_EN;
68745334ee1SHaibo Chen 				usdhc_auto_tuning_mode_sel(host);
6888b2bb0adSDong Aisheng 			} else {
6898b2bb0adSDong Aisheng 				v &= ~ESDHC_MIX_CTRL_EXE_TUNE;
6908b2bb0adSDong Aisheng 			}
6916e9fd28eSDong Aisheng 
692869f8a69SAdrian Hunter 			writel(v, host->ioaddr + SDHCI_AUTO_CMD_STATUS);
6936e9fd28eSDong Aisheng 			writel(m, host->ioaddr + ESDHC_MIX_CTRL);
6946e9fd28eSDong Aisheng 		}
6950322191eSDong Aisheng 		return;
69695f25efeSWolfram Sang 	case SDHCI_TRANSFER_MODE:
697f47c4bbfSShawn Guo 		if ((imx_data->socdata->flags & ESDHC_FLAG_MULTIBLK_NO_INT)
69858ac8177SRichard Zhu 				&& (host->cmd->opcode == SD_IO_RW_EXTENDED)
69958ac8177SRichard Zhu 				&& (host->cmd->data->blocks > 1)
70058ac8177SRichard Zhu 				&& (host->cmd->data->flags & MMC_DATA_READ)) {
70158ac8177SRichard Zhu 			u32 v;
70260bf6396SShawn Guo 			v = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
70360bf6396SShawn Guo 			v |= ESDHC_VENDOR_SPEC_SDIO_QUIRK;
70460bf6396SShawn Guo 			writel(v, host->ioaddr + ESDHC_VENDOR_SPEC);
70558ac8177SRichard Zhu 		}
70669f54698SShawn Guo 
7079d61c009SShawn Guo 		if (esdhc_is_usdhc(imx_data)) {
7083fbd4322SAndrew Gabbasov 			u32 wml;
70969f54698SShawn Guo 			u32 m = readl(host->ioaddr + ESDHC_MIX_CTRL);
7102a15f981SShawn Guo 			/* Swap AC23 bit */
7112a15f981SShawn Guo 			if (val & SDHCI_TRNS_AUTO_CMD23) {
7122a15f981SShawn Guo 				val &= ~SDHCI_TRNS_AUTO_CMD23;
7132a15f981SShawn Guo 				val |= ESDHC_MIX_CTRL_AC23EN;
7142a15f981SShawn Guo 			}
7152a15f981SShawn Guo 			m = val | (m & ~ESDHC_MIX_CTRL_SDHCI_MASK);
71669f54698SShawn Guo 			writel(m, host->ioaddr + ESDHC_MIX_CTRL);
7173fbd4322SAndrew Gabbasov 
7183fbd4322SAndrew Gabbasov 			/* Set watermark levels for PIO access to maximum value
7193fbd4322SAndrew Gabbasov 			 * (128 words) to accommodate full 512 bytes buffer.
7203fbd4322SAndrew Gabbasov 			 * For DMA access restore the levels to default value.
7213fbd4322SAndrew Gabbasov 			 */
7223fbd4322SAndrew Gabbasov 			m = readl(host->ioaddr + ESDHC_WTMK_LVL);
723e534b82fSHaibo Chen 			if (val & SDHCI_TRNS_DMA) {
7243fbd4322SAndrew Gabbasov 				wml = ESDHC_WTMK_LVL_WML_VAL_DEF;
725e534b82fSHaibo Chen 			} else {
726e534b82fSHaibo Chen 				u8 ctrl;
7273fbd4322SAndrew Gabbasov 				wml = ESDHC_WTMK_LVL_WML_VAL_MAX;
728e534b82fSHaibo Chen 
729e534b82fSHaibo Chen 				/*
730e534b82fSHaibo Chen 				 * Since already disable DMA mode, so also need
731e534b82fSHaibo Chen 				 * to clear the DMASEL. Otherwise, for standard
732e534b82fSHaibo Chen 				 * tuning, when send tuning command, usdhc will
733e534b82fSHaibo Chen 				 * still prefetch the ADMA script from wrong
734e534b82fSHaibo Chen 				 * DMA address, then we will see IOMMU report
735e534b82fSHaibo Chen 				 * some error which show lack of TLB mapping.
736e534b82fSHaibo Chen 				 */
737e534b82fSHaibo Chen 				ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
738e534b82fSHaibo Chen 				ctrl &= ~SDHCI_CTRL_DMA_MASK;
739e534b82fSHaibo Chen 				sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
740e534b82fSHaibo Chen 			}
7413fbd4322SAndrew Gabbasov 			m &= ~(ESDHC_WTMK_LVL_RD_WML_MASK |
7423fbd4322SAndrew Gabbasov 			       ESDHC_WTMK_LVL_WR_WML_MASK);
7433fbd4322SAndrew Gabbasov 			m |= (wml << ESDHC_WTMK_LVL_RD_WML_SHIFT) |
7443fbd4322SAndrew Gabbasov 			     (wml << ESDHC_WTMK_LVL_WR_WML_SHIFT);
7453fbd4322SAndrew Gabbasov 			writel(m, host->ioaddr + ESDHC_WTMK_LVL);
74669f54698SShawn Guo 		} else {
74769f54698SShawn Guo 			/*
74869f54698SShawn Guo 			 * Postpone this write, we must do it together with a
74969f54698SShawn Guo 			 * command write that is down below.
75069f54698SShawn Guo 			 */
751e149860dSRichard Zhu 			imx_data->scratchpad = val;
75269f54698SShawn Guo 		}
75395f25efeSWolfram Sang 		return;
75495f25efeSWolfram Sang 	case SDHCI_COMMAND:
755361b8482SLucas Stach 		if (host->cmd->opcode == MMC_STOP_TRANSMISSION)
75658ac8177SRichard Zhu 			val |= SDHCI_CMD_ABORTCMD;
75795a2482aSShawn Guo 
758361b8482SLucas Stach 		if ((host->cmd->opcode == MMC_SET_BLOCK_COUNT) &&
759f47c4bbfSShawn Guo 		    (imx_data->socdata->flags & ESDHC_FLAG_MULTIBLK_NO_INT))
760361b8482SLucas Stach 			imx_data->multiblock_status = MULTIBLK_IN_PROCESS;
761361b8482SLucas Stach 
7629d61c009SShawn Guo 		if (esdhc_is_usdhc(imx_data))
76395a2482aSShawn Guo 			writel(val << 16,
76495a2482aSShawn Guo 			       host->ioaddr + SDHCI_TRANSFER_MODE);
76569f54698SShawn Guo 		else
766e149860dSRichard Zhu 			writel(val << 16 | imx_data->scratchpad,
76795f25efeSWolfram Sang 			       host->ioaddr + SDHCI_TRANSFER_MODE);
76895f25efeSWolfram Sang 		return;
76995f25efeSWolfram Sang 	case SDHCI_BLOCK_SIZE:
77095f25efeSWolfram Sang 		val &= ~SDHCI_MAKE_BLKSZ(0x7, 0);
77195f25efeSWolfram Sang 		break;
77295f25efeSWolfram Sang 	}
77395f25efeSWolfram Sang 	esdhc_clrset_le(host, 0xffff, val, reg);
77495f25efeSWolfram Sang }
77595f25efeSWolfram Sang 
77677da3da0SAaron Brice static u8 esdhc_readb_le(struct sdhci_host *host, int reg)
77777da3da0SAaron Brice {
77877da3da0SAaron Brice 	u8 ret;
77977da3da0SAaron Brice 	u32 val;
78077da3da0SAaron Brice 
78177da3da0SAaron Brice 	switch (reg) {
78277da3da0SAaron Brice 	case SDHCI_HOST_CONTROL:
78377da3da0SAaron Brice 		val = readl(host->ioaddr + reg);
78477da3da0SAaron Brice 
78577da3da0SAaron Brice 		ret = val & SDHCI_CTRL_LED;
78677da3da0SAaron Brice 		ret |= (val >> 5) & SDHCI_CTRL_DMA_MASK;
78777da3da0SAaron Brice 		ret |= (val & ESDHC_CTRL_4BITBUS);
78877da3da0SAaron Brice 		ret |= (val & ESDHC_CTRL_8BITBUS) << 3;
78977da3da0SAaron Brice 		return ret;
79077da3da0SAaron Brice 	}
79177da3da0SAaron Brice 
79277da3da0SAaron Brice 	return readb(host->ioaddr + reg);
79377da3da0SAaron Brice }
79477da3da0SAaron Brice 
79595f25efeSWolfram Sang static void esdhc_writeb_le(struct sdhci_host *host, u8 val, int reg)
79695f25efeSWolfram Sang {
7979a0985b7SWilson Callan 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
798070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
79981a0a8bcSBenoît Thébaudeau 	u32 new_val = 0;
800af51079eSSascha Hauer 	u32 mask;
80195f25efeSWolfram Sang 
80295f25efeSWolfram Sang 	switch (reg) {
80395f25efeSWolfram Sang 	case SDHCI_POWER_CONTROL:
80495f25efeSWolfram Sang 		/*
80595f25efeSWolfram Sang 		 * FSL put some DMA bits here
80695f25efeSWolfram Sang 		 * If your board has a regulator, code should be here
80795f25efeSWolfram Sang 		 */
80895f25efeSWolfram Sang 		return;
80995f25efeSWolfram Sang 	case SDHCI_HOST_CONTROL:
8106b40d182SShawn Guo 		/* FSL messed up here, so we need to manually compose it. */
811af51079eSSascha Hauer 		new_val = val & SDHCI_CTRL_LED;
8127122bbb0SMasanari Iida 		/* ensure the endianness */
81395f25efeSWolfram Sang 		new_val |= ESDHC_HOST_CONTROL_LE;
8149a0985b7SWilson Callan 		/* bits 8&9 are reserved on mx25 */
8159a0985b7SWilson Callan 		if (!is_imx25_esdhc(imx_data)) {
81695f25efeSWolfram Sang 			/* DMA mode bits are shifted */
81795f25efeSWolfram Sang 			new_val |= (val & SDHCI_CTRL_DMA_MASK) << 5;
8189a0985b7SWilson Callan 		}
81995f25efeSWolfram Sang 
820af51079eSSascha Hauer 		/*
821af51079eSSascha Hauer 		 * Do not touch buswidth bits here. This is done in
822af51079eSSascha Hauer 		 * esdhc_pltfm_bus_width.
823f6825748SMartin Fuzzey 		 * Do not touch the D3CD bit either which is used for the
824d04f8d5bSBenoît Thébaudeau 		 * SDIO interrupt erratum workaround.
825af51079eSSascha Hauer 		 */
826f6825748SMartin Fuzzey 		mask = 0xffff & ~(ESDHC_CTRL_BUSWIDTH_MASK | ESDHC_CTRL_D3CD);
827af51079eSSascha Hauer 
828af51079eSSascha Hauer 		esdhc_clrset_le(host, mask, new_val, reg);
82995f25efeSWolfram Sang 		return;
83081a0a8bcSBenoît Thébaudeau 	case SDHCI_SOFTWARE_RESET:
83181a0a8bcSBenoît Thébaudeau 		if (val & SDHCI_RESET_DATA)
83281a0a8bcSBenoît Thébaudeau 			new_val = readl(host->ioaddr + SDHCI_HOST_CONTROL);
83381a0a8bcSBenoît Thébaudeau 		break;
83495f25efeSWolfram Sang 	}
83595f25efeSWolfram Sang 	esdhc_clrset_le(host, 0xff, val, reg);
836913413c3SShawn Guo 
83781a0a8bcSBenoît Thébaudeau 	if (reg == SDHCI_SOFTWARE_RESET) {
83881a0a8bcSBenoît Thébaudeau 		if (val & SDHCI_RESET_ALL) {
839913413c3SShawn Guo 			/*
84081a0a8bcSBenoît Thébaudeau 			 * The esdhc has a design violation to SDHC spec which
84181a0a8bcSBenoît Thébaudeau 			 * tells that software reset should not affect card
84281a0a8bcSBenoît Thébaudeau 			 * detection circuit. But esdhc clears its SYSCTL
84381a0a8bcSBenoît Thébaudeau 			 * register bits [0..2] during the software reset. This
84481a0a8bcSBenoît Thébaudeau 			 * will stop those clocks that card detection circuit
84581a0a8bcSBenoît Thébaudeau 			 * relies on. To work around it, we turn the clocks on
84681a0a8bcSBenoît Thébaudeau 			 * back to keep card detection circuit functional.
847913413c3SShawn Guo 			 */
848913413c3SShawn Guo 			esdhc_clrset_le(host, 0x7, 0x7, ESDHC_SYSTEM_CONTROL);
84958c8c4fbSShawn Guo 			/*
85058c8c4fbSShawn Guo 			 * The reset on usdhc fails to clear MIX_CTRL register.
85158c8c4fbSShawn Guo 			 * Do it manually here.
85258c8c4fbSShawn Guo 			 */
853de5bdbffSDong Aisheng 			if (esdhc_is_usdhc(imx_data)) {
85481a0a8bcSBenoît Thébaudeau 				/*
85581a0a8bcSBenoît Thébaudeau 				 * the tuning bits should be kept during reset
85681a0a8bcSBenoît Thébaudeau 				 */
857d131a71cSDong Aisheng 				new_val = readl(host->ioaddr + ESDHC_MIX_CTRL);
858d131a71cSDong Aisheng 				writel(new_val & ESDHC_MIX_CTRL_TUNING_MASK,
859d131a71cSDong Aisheng 						host->ioaddr + ESDHC_MIX_CTRL);
860de5bdbffSDong Aisheng 				imx_data->is_ddr = 0;
861de5bdbffSDong Aisheng 			}
86281a0a8bcSBenoît Thébaudeau 		} else if (val & SDHCI_RESET_DATA) {
86381a0a8bcSBenoît Thébaudeau 			/*
86481a0a8bcSBenoît Thébaudeau 			 * The eSDHC DAT line software reset clears at least the
86581a0a8bcSBenoît Thébaudeau 			 * data transfer width on i.MX25, so make sure that the
86681a0a8bcSBenoît Thébaudeau 			 * Host Control register is unaffected.
86781a0a8bcSBenoît Thébaudeau 			 */
86881a0a8bcSBenoît Thébaudeau 			esdhc_clrset_le(host, 0xff, new_val,
86981a0a8bcSBenoît Thébaudeau 					SDHCI_HOST_CONTROL);
87081a0a8bcSBenoît Thébaudeau 		}
87158c8c4fbSShawn Guo 	}
87295f25efeSWolfram Sang }
87395f25efeSWolfram Sang 
8740ddf03c9SLucas Stach static unsigned int esdhc_pltfm_get_max_clock(struct sdhci_host *host)
8750ddf03c9SLucas Stach {
8760ddf03c9SLucas Stach 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
8770ddf03c9SLucas Stach 
878a974862fSDong Aisheng 	return pltfm_host->clock;
8790ddf03c9SLucas Stach }
8800ddf03c9SLucas Stach 
88195f25efeSWolfram Sang static unsigned int esdhc_pltfm_get_min_clock(struct sdhci_host *host)
88295f25efeSWolfram Sang {
88395f25efeSWolfram Sang 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
88495f25efeSWolfram Sang 
885a974862fSDong Aisheng 	return pltfm_host->clock / 256 / 16;
88695f25efeSWolfram Sang }
88795f25efeSWolfram Sang 
8888ba9580aSLucas Stach static inline void esdhc_pltfm_set_clock(struct sdhci_host *host,
8898ba9580aSLucas Stach 					 unsigned int clock)
8908ba9580aSLucas Stach {
8918ba9580aSLucas Stach 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
892070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
893a974862fSDong Aisheng 	unsigned int host_clock = pltfm_host->clock;
8945143c953SBenoît Thébaudeau 	int ddr_pre_div = imx_data->is_ddr ? 2 : 1;
8955143c953SBenoît Thébaudeau 	int pre_div = 1;
896d31fc00aSDong Aisheng 	int div = 1;
897f581e909SHaibo Chen 	int ret;
898fed2f6e2SDong Aisheng 	u32 temp, val;
8998ba9580aSLucas Stach 
9009d61c009SShawn Guo 	if (esdhc_is_usdhc(imx_data)) {
901fed2f6e2SDong Aisheng 		val = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
902fed2f6e2SDong Aisheng 		writel(val & ~ESDHC_VENDOR_SPEC_FRC_SDCLK_ON,
903fed2f6e2SDong Aisheng 			host->ioaddr + ESDHC_VENDOR_SPEC);
904f581e909SHaibo Chen 		esdhc_wait_for_card_clock_gate_off(host);
905fed2f6e2SDong Aisheng 	}
90673e736f8SStefan Agner 
90773e736f8SStefan Agner 	if (clock == 0) {
90873e736f8SStefan Agner 		host->mmc->actual_clock = 0;
909373073efSRussell King 		return;
910fed2f6e2SDong Aisheng 	}
911d31fc00aSDong Aisheng 
912499ed50fSBenoît Thébaudeau 	/* For i.MX53 eSDHCv3, SYSCTL.SDCLKFS may not be set to 0. */
913499ed50fSBenoît Thébaudeau 	if (is_imx53_esdhc(imx_data)) {
914499ed50fSBenoît Thébaudeau 		/*
915499ed50fSBenoît Thébaudeau 		 * According to the i.MX53 reference manual, if DLLCTRL[10] can
916499ed50fSBenoît Thébaudeau 		 * be set, then the controller is eSDHCv3, else it is eSDHCv2.
917499ed50fSBenoît Thébaudeau 		 */
918499ed50fSBenoît Thébaudeau 		val = readl(host->ioaddr + ESDHC_DLL_CTRL);
919499ed50fSBenoît Thébaudeau 		writel(val | BIT(10), host->ioaddr + ESDHC_DLL_CTRL);
920499ed50fSBenoît Thébaudeau 		temp = readl(host->ioaddr + ESDHC_DLL_CTRL);
921499ed50fSBenoît Thébaudeau 		writel(val, host->ioaddr + ESDHC_DLL_CTRL);
922499ed50fSBenoît Thébaudeau 		if (temp & BIT(10))
923499ed50fSBenoît Thébaudeau 			pre_div = 2;
924499ed50fSBenoît Thébaudeau 	}
925499ed50fSBenoît Thébaudeau 
926d31fc00aSDong Aisheng 	temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
927d31fc00aSDong Aisheng 	temp &= ~(ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN
928d31fc00aSDong Aisheng 		| ESDHC_CLOCK_MASK);
929d31fc00aSDong Aisheng 	sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
930d31fc00aSDong Aisheng 
931af6a50d4SBOUGH CHEN 	if (imx_data->socdata->flags & ESDHC_FLAG_ERR010450) {
932af6a50d4SBOUGH CHEN 		unsigned int max_clock;
933af6a50d4SBOUGH CHEN 
934af6a50d4SBOUGH CHEN 		max_clock = imx_data->is_ddr ? 45000000 : 150000000;
935af6a50d4SBOUGH CHEN 
936af6a50d4SBOUGH CHEN 		clock = min(clock, max_clock);
937af6a50d4SBOUGH CHEN 	}
938af6a50d4SBOUGH CHEN 
9395143c953SBenoît Thébaudeau 	while (host_clock / (16 * pre_div * ddr_pre_div) > clock &&
9405143c953SBenoît Thébaudeau 			pre_div < 256)
941d31fc00aSDong Aisheng 		pre_div *= 2;
942d31fc00aSDong Aisheng 
9435143c953SBenoît Thébaudeau 	while (host_clock / (div * pre_div * ddr_pre_div) > clock && div < 16)
944d31fc00aSDong Aisheng 		div++;
945d31fc00aSDong Aisheng 
9465143c953SBenoît Thébaudeau 	host->mmc->actual_clock = host_clock / (div * pre_div * ddr_pre_div);
947d31fc00aSDong Aisheng 	dev_dbg(mmc_dev(host->mmc), "desired SD clock: %d, actual: %d\n",
948e76b8559SDong Aisheng 		clock, host->mmc->actual_clock);
949d31fc00aSDong Aisheng 
950d31fc00aSDong Aisheng 	pre_div >>= 1;
951d31fc00aSDong Aisheng 	div--;
952d31fc00aSDong Aisheng 
953d31fc00aSDong Aisheng 	temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
954d31fc00aSDong Aisheng 	temp |= (ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN
955d31fc00aSDong Aisheng 		| (div << ESDHC_DIVIDER_SHIFT)
956d31fc00aSDong Aisheng 		| (pre_div << ESDHC_PREDIV_SHIFT));
957d31fc00aSDong Aisheng 	sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
958fed2f6e2SDong Aisheng 
959f581e909SHaibo Chen 	/* need to wait the bit 3 of the PRSSTAT to be set, make sure card clock is stable */
960f581e909SHaibo Chen 	ret = readl_poll_timeout(host->ioaddr + ESDHC_PRSSTAT, temp,
961f581e909SHaibo Chen 				(temp & ESDHC_CLOCK_STABLE), 2, 100);
962f581e909SHaibo Chen 	if (ret == -ETIMEDOUT)
963f581e909SHaibo Chen 		dev_warn(mmc_dev(host->mmc), "card clock still not stable in 100us!.\n");
964f581e909SHaibo Chen 
9659d61c009SShawn Guo 	if (esdhc_is_usdhc(imx_data)) {
966fed2f6e2SDong Aisheng 		val = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
967fed2f6e2SDong Aisheng 		writel(val | ESDHC_VENDOR_SPEC_FRC_SDCLK_ON,
968fed2f6e2SDong Aisheng 			host->ioaddr + ESDHC_VENDOR_SPEC);
969fed2f6e2SDong Aisheng 	}
970fed2f6e2SDong Aisheng 
9718ba9580aSLucas Stach }
9728ba9580aSLucas Stach 
973913413c3SShawn Guo static unsigned int esdhc_pltfm_get_ro(struct sdhci_host *host)
974913413c3SShawn Guo {
975842afc02SShawn Guo 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
976070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
977842afc02SShawn Guo 	struct esdhc_platform_data *boarddata = &imx_data->boarddata;
978913413c3SShawn Guo 
979913413c3SShawn Guo 	switch (boarddata->wp_type) {
980913413c3SShawn Guo 	case ESDHC_WP_GPIO:
981fbe5fdd1SShawn Guo 		return mmc_gpio_get_ro(host->mmc);
982913413c3SShawn Guo 	case ESDHC_WP_CONTROLLER:
983913413c3SShawn Guo 		return !(readl(host->ioaddr + SDHCI_PRESENT_STATE) &
984913413c3SShawn Guo 			       SDHCI_WRITE_PROTECT);
985913413c3SShawn Guo 	case ESDHC_WP_NONE:
986913413c3SShawn Guo 		break;
987913413c3SShawn Guo 	}
988913413c3SShawn Guo 
989913413c3SShawn Guo 	return -ENOSYS;
990913413c3SShawn Guo }
991913413c3SShawn Guo 
9922317f56cSRussell King static void esdhc_pltfm_set_bus_width(struct sdhci_host *host, int width)
993af51079eSSascha Hauer {
994af51079eSSascha Hauer 	u32 ctrl;
995af51079eSSascha Hauer 
996af51079eSSascha Hauer 	switch (width) {
997af51079eSSascha Hauer 	case MMC_BUS_WIDTH_8:
998af51079eSSascha Hauer 		ctrl = ESDHC_CTRL_8BITBUS;
999af51079eSSascha Hauer 		break;
1000af51079eSSascha Hauer 	case MMC_BUS_WIDTH_4:
1001af51079eSSascha Hauer 		ctrl = ESDHC_CTRL_4BITBUS;
1002af51079eSSascha Hauer 		break;
1003af51079eSSascha Hauer 	default:
1004af51079eSSascha Hauer 		ctrl = 0;
1005af51079eSSascha Hauer 		break;
1006af51079eSSascha Hauer 	}
1007af51079eSSascha Hauer 
1008af51079eSSascha Hauer 	esdhc_clrset_le(host, ESDHC_CTRL_BUSWIDTH_MASK, ctrl,
1009af51079eSSascha Hauer 			SDHCI_HOST_CONTROL);
1010af51079eSSascha Hauer }
1011af51079eSSascha Hauer 
1012de3e1dd0SBOUGH CHEN static int usdhc_execute_tuning(struct mmc_host *mmc, u32 opcode)
1013de3e1dd0SBOUGH CHEN {
1014de3e1dd0SBOUGH CHEN 	struct sdhci_host *host = mmc_priv(mmc);
1015de3e1dd0SBOUGH CHEN 
1016de3e1dd0SBOUGH CHEN 	/*
1017de3e1dd0SBOUGH CHEN 	 * i.MX uSDHC internally already uses a fixed optimized timing for
1018de3e1dd0SBOUGH CHEN 	 * DDR50, normally does not require tuning for DDR50 mode.
1019de3e1dd0SBOUGH CHEN 	 */
1020de3e1dd0SBOUGH CHEN 	if (host->timing == MMC_TIMING_UHS_DDR50)
1021de3e1dd0SBOUGH CHEN 		return 0;
1022de3e1dd0SBOUGH CHEN 
1023de3e1dd0SBOUGH CHEN 	return sdhci_execute_tuning(mmc, opcode);
1024de3e1dd0SBOUGH CHEN }
1025de3e1dd0SBOUGH CHEN 
10260322191eSDong Aisheng static void esdhc_prepare_tuning(struct sdhci_host *host, u32 val)
10270322191eSDong Aisheng {
10280322191eSDong Aisheng 	u32 reg;
10290ac4f496SHaibo Chen 	u8 sw_rst;
10300ac4f496SHaibo Chen 	int ret;
10310322191eSDong Aisheng 
10320322191eSDong Aisheng 	/* FIXME: delay a bit for card to be ready for next tuning due to errors */
10330322191eSDong Aisheng 	mdelay(1);
10340322191eSDong Aisheng 
10350ac4f496SHaibo Chen 	/* IC suggest to reset USDHC before every tuning command */
10360ac4f496SHaibo Chen 	esdhc_clrset_le(host, 0xff, SDHCI_RESET_ALL, SDHCI_SOFTWARE_RESET);
10370ac4f496SHaibo Chen 	ret = readb_poll_timeout(host->ioaddr + SDHCI_SOFTWARE_RESET, sw_rst,
10380ac4f496SHaibo Chen 				!(sw_rst & SDHCI_RESET_ALL), 10, 100);
10390ac4f496SHaibo Chen 	if (ret == -ETIMEDOUT)
10400ac4f496SHaibo Chen 		dev_warn(mmc_dev(host->mmc),
10410ac4f496SHaibo Chen 		"warning! RESET_ALL never complete before sending tuning command\n");
10420ac4f496SHaibo Chen 
10430322191eSDong Aisheng 	reg = readl(host->ioaddr + ESDHC_MIX_CTRL);
10440322191eSDong Aisheng 	reg |= ESDHC_MIX_CTRL_EXE_TUNE | ESDHC_MIX_CTRL_SMPCLK_SEL |
10450322191eSDong Aisheng 			ESDHC_MIX_CTRL_FBCLK_SEL;
10460322191eSDong Aisheng 	writel(reg, host->ioaddr + ESDHC_MIX_CTRL);
10470322191eSDong Aisheng 	writel(val << 8, host->ioaddr + ESDHC_TUNE_CTRL_STATUS);
10480322191eSDong Aisheng 	dev_dbg(mmc_dev(host->mmc),
1049d04f8d5bSBenoît Thébaudeau 		"tuning with delay 0x%x ESDHC_TUNE_CTRL_STATUS 0x%x\n",
10500322191eSDong Aisheng 			val, readl(host->ioaddr + ESDHC_TUNE_CTRL_STATUS));
10510322191eSDong Aisheng }
10520322191eSDong Aisheng 
10530322191eSDong Aisheng static void esdhc_post_tuning(struct sdhci_host *host)
10540322191eSDong Aisheng {
10550322191eSDong Aisheng 	u32 reg;
10560322191eSDong Aisheng 
105745334ee1SHaibo Chen 	usdhc_auto_tuning_mode_sel(host);
105845334ee1SHaibo Chen 
10590322191eSDong Aisheng 	reg = readl(host->ioaddr + ESDHC_MIX_CTRL);
10600322191eSDong Aisheng 	reg &= ~ESDHC_MIX_CTRL_EXE_TUNE;
1061da0295ffSDong Aisheng 	reg |= ESDHC_MIX_CTRL_AUTO_TUNE_EN;
10620322191eSDong Aisheng 	writel(reg, host->ioaddr + ESDHC_MIX_CTRL);
10630322191eSDong Aisheng }
10640322191eSDong Aisheng 
10650322191eSDong Aisheng static int esdhc_executing_tuning(struct sdhci_host *host, u32 opcode)
10660322191eSDong Aisheng {
10670322191eSDong Aisheng 	int min, max, avg, ret;
10680322191eSDong Aisheng 
10690322191eSDong Aisheng 	/* find the mininum delay first which can pass tuning */
10700322191eSDong Aisheng 	min = ESDHC_TUNE_CTRL_MIN;
10710322191eSDong Aisheng 	while (min < ESDHC_TUNE_CTRL_MAX) {
10720322191eSDong Aisheng 		esdhc_prepare_tuning(host, min);
10739979dbe5SChaotian Jing 		if (!mmc_send_tuning(host->mmc, opcode, NULL))
10740322191eSDong Aisheng 			break;
10750322191eSDong Aisheng 		min += ESDHC_TUNE_CTRL_STEP;
10760322191eSDong Aisheng 	}
10770322191eSDong Aisheng 
10780322191eSDong Aisheng 	/* find the maxinum delay which can not pass tuning */
10790322191eSDong Aisheng 	max = min + ESDHC_TUNE_CTRL_STEP;
10800322191eSDong Aisheng 	while (max < ESDHC_TUNE_CTRL_MAX) {
10810322191eSDong Aisheng 		esdhc_prepare_tuning(host, max);
10829979dbe5SChaotian Jing 		if (mmc_send_tuning(host->mmc, opcode, NULL)) {
10830322191eSDong Aisheng 			max -= ESDHC_TUNE_CTRL_STEP;
10840322191eSDong Aisheng 			break;
10850322191eSDong Aisheng 		}
10860322191eSDong Aisheng 		max += ESDHC_TUNE_CTRL_STEP;
10870322191eSDong Aisheng 	}
10880322191eSDong Aisheng 
10890322191eSDong Aisheng 	/* use average delay to get the best timing */
10900322191eSDong Aisheng 	avg = (min + max) / 2;
10910322191eSDong Aisheng 	esdhc_prepare_tuning(host, avg);
10929979dbe5SChaotian Jing 	ret = mmc_send_tuning(host->mmc, opcode, NULL);
10930322191eSDong Aisheng 	esdhc_post_tuning(host);
10940322191eSDong Aisheng 
1095d04f8d5bSBenoît Thébaudeau 	dev_dbg(mmc_dev(host->mmc), "tuning %s at 0x%x ret %d\n",
10960322191eSDong Aisheng 		ret ? "failed" : "passed", avg, ret);
10970322191eSDong Aisheng 
10980322191eSDong Aisheng 	return ret;
10990322191eSDong Aisheng }
11000322191eSDong Aisheng 
1101029e2476SBOUGH CHEN static void esdhc_hs400_enhanced_strobe(struct mmc_host *mmc, struct mmc_ios *ios)
1102029e2476SBOUGH CHEN {
1103029e2476SBOUGH CHEN 	struct sdhci_host *host = mmc_priv(mmc);
1104029e2476SBOUGH CHEN 	u32 m;
1105029e2476SBOUGH CHEN 
1106029e2476SBOUGH CHEN 	m = readl(host->ioaddr + ESDHC_MIX_CTRL);
1107029e2476SBOUGH CHEN 	if (ios->enhanced_strobe)
1108029e2476SBOUGH CHEN 		m |= ESDHC_MIX_CTRL_HS400_ES_EN;
1109029e2476SBOUGH CHEN 	else
1110029e2476SBOUGH CHEN 		m &= ~ESDHC_MIX_CTRL_HS400_ES_EN;
1111029e2476SBOUGH CHEN 	writel(m, host->ioaddr + ESDHC_MIX_CTRL);
1112029e2476SBOUGH CHEN }
1113029e2476SBOUGH CHEN 
1114ad93220dSDong Aisheng static int esdhc_change_pinstate(struct sdhci_host *host,
1115ad93220dSDong Aisheng 						unsigned int uhs)
1116ad93220dSDong Aisheng {
1117ad93220dSDong Aisheng 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1118070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
1119ad93220dSDong Aisheng 	struct pinctrl_state *pinctrl;
1120ad93220dSDong Aisheng 
1121ad93220dSDong Aisheng 	dev_dbg(mmc_dev(host->mmc), "change pinctrl state for uhs %d\n", uhs);
1122ad93220dSDong Aisheng 
1123ad93220dSDong Aisheng 	if (IS_ERR(imx_data->pinctrl) ||
1124ad93220dSDong Aisheng 		IS_ERR(imx_data->pins_100mhz) ||
1125ad93220dSDong Aisheng 		IS_ERR(imx_data->pins_200mhz))
1126ad93220dSDong Aisheng 		return -EINVAL;
1127ad93220dSDong Aisheng 
1128ad93220dSDong Aisheng 	switch (uhs) {
1129ad93220dSDong Aisheng 	case MMC_TIMING_UHS_SDR50:
11309f327845SHaibo Chen 	case MMC_TIMING_UHS_DDR50:
1131ad93220dSDong Aisheng 		pinctrl = imx_data->pins_100mhz;
1132ad93220dSDong Aisheng 		break;
1133ad93220dSDong Aisheng 	case MMC_TIMING_UHS_SDR104:
1134429a5b45SDong Aisheng 	case MMC_TIMING_MMC_HS200:
113528b07674SHaibo Chen 	case MMC_TIMING_MMC_HS400:
1136ad93220dSDong Aisheng 		pinctrl = imx_data->pins_200mhz;
1137ad93220dSDong Aisheng 		break;
1138ad93220dSDong Aisheng 	default:
1139ad93220dSDong Aisheng 		/* back to default state for other legacy timing */
11402480b720SUlf Hansson 		return pinctrl_select_default_state(mmc_dev(host->mmc));
1141ad93220dSDong Aisheng 	}
1142ad93220dSDong Aisheng 
1143ad93220dSDong Aisheng 	return pinctrl_select_state(imx_data->pinctrl, pinctrl);
1144ad93220dSDong Aisheng }
1145ad93220dSDong Aisheng 
114628b07674SHaibo Chen /*
1147d04f8d5bSBenoît Thébaudeau  * For HS400 eMMC, there is a data_strobe line. This signal is generated
114828b07674SHaibo Chen  * by the device and used for data output and CRC status response output
114928b07674SHaibo Chen  * in HS400 mode. The frequency of this signal follows the frequency of
1150d04f8d5bSBenoît Thébaudeau  * CLK generated by host. The host receives the data which is aligned to the
115128b07674SHaibo Chen  * edge of data_strobe line. Due to the time delay between CLK line and
115228b07674SHaibo Chen  * data_strobe line, if the delay time is larger than one clock cycle,
1153d04f8d5bSBenoît Thébaudeau  * then CLK and data_strobe line will be misaligned, read error shows up.
115428b07674SHaibo Chen  */
115528b07674SHaibo Chen static void esdhc_set_strobe_dll(struct sdhci_host *host)
115628b07674SHaibo Chen {
11575bd2acdcSHaibo Chen 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
11585bd2acdcSHaibo Chen 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
11595bd2acdcSHaibo Chen 	u32 strobe_delay;
116028b07674SHaibo Chen 	u32 v;
1161373e800bSHaibo Chen 	int ret;
116228b07674SHaibo Chen 
11637ac6da26SDong Aisheng 	/* disable clock before enabling strobe dll */
11647ac6da26SDong Aisheng 	writel(readl(host->ioaddr + ESDHC_VENDOR_SPEC) &
11657ac6da26SDong Aisheng 		~ESDHC_VENDOR_SPEC_FRC_SDCLK_ON,
11667ac6da26SDong Aisheng 		host->ioaddr + ESDHC_VENDOR_SPEC);
1167f581e909SHaibo Chen 	esdhc_wait_for_card_clock_gate_off(host);
11687ac6da26SDong Aisheng 
116928b07674SHaibo Chen 	/* force a reset on strobe dll */
117028b07674SHaibo Chen 	writel(ESDHC_STROBE_DLL_CTRL_RESET,
117128b07674SHaibo Chen 		host->ioaddr + ESDHC_STROBE_DLL_CTRL);
11722eaf5a53SBOUGH CHEN 	/* clear the reset bit on strobe dll before any setting */
11732eaf5a53SBOUGH CHEN 	writel(0, host->ioaddr + ESDHC_STROBE_DLL_CTRL);
11742eaf5a53SBOUGH CHEN 
117528b07674SHaibo Chen 	/*
117628b07674SHaibo Chen 	 * enable strobe dll ctrl and adjust the delay target
117728b07674SHaibo Chen 	 * for the uSDHC loopback read clock
117828b07674SHaibo Chen 	 */
11795bd2acdcSHaibo Chen 	if (imx_data->boarddata.strobe_dll_delay_target)
11805bd2acdcSHaibo Chen 		strobe_delay = imx_data->boarddata.strobe_dll_delay_target;
11815bd2acdcSHaibo Chen 	else
11825bd2acdcSHaibo Chen 		strobe_delay = ESDHC_STROBE_DLL_CTRL_SLV_DLY_TARGET_DEFAULT;
118328b07674SHaibo Chen 	v = ESDHC_STROBE_DLL_CTRL_ENABLE |
11842eaf5a53SBOUGH CHEN 		ESDHC_STROBE_DLL_CTRL_SLV_UPDATE_INT_DEFAULT |
11855bd2acdcSHaibo Chen 		(strobe_delay << ESDHC_STROBE_DLL_CTRL_SLV_DLY_TARGET_SHIFT);
118628b07674SHaibo Chen 	writel(v, host->ioaddr + ESDHC_STROBE_DLL_CTRL);
1187373e800bSHaibo Chen 
1188373e800bSHaibo Chen 	/* wait max 50us to get the REF/SLV lock */
1189373e800bSHaibo Chen 	ret = readl_poll_timeout(host->ioaddr + ESDHC_STROBE_DLL_STATUS, v,
1190373e800bSHaibo Chen 		((v & ESDHC_STROBE_DLL_STS_REF_LOCK) && (v & ESDHC_STROBE_DLL_STS_SLV_LOCK)), 1, 50);
1191373e800bSHaibo Chen 	if (ret == -ETIMEDOUT)
119228b07674SHaibo Chen 		dev_warn(mmc_dev(host->mmc),
1193373e800bSHaibo Chen 		"warning! HS400 strobe DLL status REF/SLV not lock in 50us, STROBE DLL status is %x!\n", v);
119428b07674SHaibo Chen }
119528b07674SHaibo Chen 
1196d9370424SHaibo Chen static void esdhc_reset_tuning(struct sdhci_host *host)
1197d9370424SHaibo Chen {
1198d9370424SHaibo Chen 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1199d9370424SHaibo Chen 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
1200d9370424SHaibo Chen 	u32 ctrl;
12019af372dcSHaibo Chen 	int ret;
1202d9370424SHaibo Chen 
1203d04f8d5bSBenoît Thébaudeau 	/* Reset the tuning circuit */
1204d9370424SHaibo Chen 	if (esdhc_is_usdhc(imx_data)) {
1205d9370424SHaibo Chen 		if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING) {
1206d9370424SHaibo Chen 			ctrl = readl(host->ioaddr + ESDHC_MIX_CTRL);
1207d9370424SHaibo Chen 			ctrl &= ~ESDHC_MIX_CTRL_SMPCLK_SEL;
1208d9370424SHaibo Chen 			ctrl &= ~ESDHC_MIX_CTRL_FBCLK_SEL;
1209d9370424SHaibo Chen 			writel(ctrl, host->ioaddr + ESDHC_MIX_CTRL);
1210d9370424SHaibo Chen 			writel(0, host->ioaddr + ESDHC_TUNE_CTRL_STATUS);
1211d9370424SHaibo Chen 		} else if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) {
1212869f8a69SAdrian Hunter 			ctrl = readl(host->ioaddr + SDHCI_AUTO_CMD_STATUS);
1213d9370424SHaibo Chen 			ctrl &= ~ESDHC_MIX_CTRL_SMPCLK_SEL;
12149af372dcSHaibo Chen 			ctrl &= ~ESDHC_MIX_CTRL_EXE_TUNE;
1215869f8a69SAdrian Hunter 			writel(ctrl, host->ioaddr + SDHCI_AUTO_CMD_STATUS);
12169af372dcSHaibo Chen 			/* Make sure ESDHC_MIX_CTRL_EXE_TUNE cleared */
12179af372dcSHaibo Chen 			ret = readl_poll_timeout(host->ioaddr + SDHCI_AUTO_CMD_STATUS,
12189af372dcSHaibo Chen 				ctrl, !(ctrl & ESDHC_MIX_CTRL_EXE_TUNE), 1, 50);
12199af372dcSHaibo Chen 			if (ret == -ETIMEDOUT)
12209af372dcSHaibo Chen 				dev_warn(mmc_dev(host->mmc),
12219af372dcSHaibo Chen 				 "Warning! clear execute tuning bit failed\n");
12229af372dcSHaibo Chen 			/*
12239af372dcSHaibo Chen 			 * SDHCI_INT_DATA_AVAIL is W1C bit, set this bit will clear the
12249af372dcSHaibo Chen 			 * usdhc IP internal logic flag execute_tuning_with_clr_buf, which
12259af372dcSHaibo Chen 			 * will finally make sure the normal data transfer logic correct.
12269af372dcSHaibo Chen 			 */
12279af372dcSHaibo Chen 			ctrl = readl(host->ioaddr + SDHCI_INT_STATUS);
12289af372dcSHaibo Chen 			ctrl |= SDHCI_INT_DATA_AVAIL;
12299af372dcSHaibo Chen 			writel(ctrl, host->ioaddr + SDHCI_INT_STATUS);
1230d9370424SHaibo Chen 		}
1231d9370424SHaibo Chen 	}
1232d9370424SHaibo Chen }
1233d9370424SHaibo Chen 
1234850a29b8SRussell King static void esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned timing)
1235ad93220dSDong Aisheng {
123628b07674SHaibo Chen 	u32 m;
1237ad93220dSDong Aisheng 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1238070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
1239602519b2SDong Aisheng 	struct esdhc_platform_data *boarddata = &imx_data->boarddata;
1240ad93220dSDong Aisheng 
124128b07674SHaibo Chen 	/* disable ddr mode and disable HS400 mode */
124228b07674SHaibo Chen 	m = readl(host->ioaddr + ESDHC_MIX_CTRL);
124328b07674SHaibo Chen 	m &= ~(ESDHC_MIX_CTRL_DDREN | ESDHC_MIX_CTRL_HS400_EN);
124428b07674SHaibo Chen 	imx_data->is_ddr = 0;
124528b07674SHaibo Chen 
1246850a29b8SRussell King 	switch (timing) {
1247ad93220dSDong Aisheng 	case MMC_TIMING_UHS_SDR12:
1248ad93220dSDong Aisheng 	case MMC_TIMING_UHS_SDR25:
1249ad93220dSDong Aisheng 	case MMC_TIMING_UHS_SDR50:
1250ad93220dSDong Aisheng 	case MMC_TIMING_UHS_SDR104:
1251de0a0decSBOUGH CHEN 	case MMC_TIMING_MMC_HS:
1252429a5b45SDong Aisheng 	case MMC_TIMING_MMC_HS200:
125328b07674SHaibo Chen 		writel(m, host->ioaddr + ESDHC_MIX_CTRL);
1254ad93220dSDong Aisheng 		break;
1255ad93220dSDong Aisheng 	case MMC_TIMING_UHS_DDR50:
125669f5bf38SAisheng Dong 	case MMC_TIMING_MMC_DDR52:
125728b07674SHaibo Chen 		m |= ESDHC_MIX_CTRL_DDREN;
125828b07674SHaibo Chen 		writel(m, host->ioaddr + ESDHC_MIX_CTRL);
1259de5bdbffSDong Aisheng 		imx_data->is_ddr = 1;
1260602519b2SDong Aisheng 		if (boarddata->delay_line) {
1261602519b2SDong Aisheng 			u32 v;
1262602519b2SDong Aisheng 			v = boarddata->delay_line <<
1263602519b2SDong Aisheng 				ESDHC_DLL_OVERRIDE_VAL_SHIFT |
1264602519b2SDong Aisheng 				(1 << ESDHC_DLL_OVERRIDE_EN_SHIFT);
1265602519b2SDong Aisheng 			if (is_imx53_esdhc(imx_data))
1266602519b2SDong Aisheng 				v <<= 1;
1267602519b2SDong Aisheng 			writel(v, host->ioaddr + ESDHC_DLL_CTRL);
1268602519b2SDong Aisheng 		}
1269ad93220dSDong Aisheng 		break;
127028b07674SHaibo Chen 	case MMC_TIMING_MMC_HS400:
127128b07674SHaibo Chen 		m |= ESDHC_MIX_CTRL_DDREN | ESDHC_MIX_CTRL_HS400_EN;
127228b07674SHaibo Chen 		writel(m, host->ioaddr + ESDHC_MIX_CTRL);
127328b07674SHaibo Chen 		imx_data->is_ddr = 1;
12747ac6da26SDong Aisheng 		/* update clock after enable DDR for strobe DLL lock */
12757ac6da26SDong Aisheng 		host->ops->set_clock(host, host->clock);
127628b07674SHaibo Chen 		esdhc_set_strobe_dll(host);
127728b07674SHaibo Chen 		break;
1278d9370424SHaibo Chen 	case MMC_TIMING_LEGACY:
1279d9370424SHaibo Chen 	default:
1280d9370424SHaibo Chen 		esdhc_reset_tuning(host);
1281d9370424SHaibo Chen 		break;
1282ad93220dSDong Aisheng 	}
1283ad93220dSDong Aisheng 
1284850a29b8SRussell King 	esdhc_change_pinstate(host, timing);
1285ad93220dSDong Aisheng }
1286ad93220dSDong Aisheng 
12870718e59aSRussell King static void esdhc_reset(struct sdhci_host *host, u8 mask)
12880718e59aSRussell King {
12890718e59aSRussell King 	sdhci_reset(host, mask);
12900718e59aSRussell King 
12910718e59aSRussell King 	sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
12920718e59aSRussell King 	sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
12930718e59aSRussell King }
12940718e59aSRussell King 
129510fd0ad9SAisheng Dong static unsigned int esdhc_get_max_timeout_count(struct sdhci_host *host)
129610fd0ad9SAisheng Dong {
129710fd0ad9SAisheng Dong 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1298070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
129910fd0ad9SAisheng Dong 
1300d04f8d5bSBenoît Thébaudeau 	/* Doc Erratum: the uSDHC actual maximum timeout count is 1 << 29 */
13012fb0b02bSHaibo Chen 	return esdhc_is_usdhc(imx_data) ? 1 << 29 : 1 << 27;
130210fd0ad9SAisheng Dong }
130310fd0ad9SAisheng Dong 
1304e33eb8e2SAisheng Dong static void esdhc_set_timeout(struct sdhci_host *host, struct mmc_command *cmd)
1305e33eb8e2SAisheng Dong {
1306e33eb8e2SAisheng Dong 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1307070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
1308e33eb8e2SAisheng Dong 
1309e33eb8e2SAisheng Dong 	/* use maximum timeout counter */
1310a215186dSHaibo Chen 	esdhc_clrset_le(host, ESDHC_SYS_CTRL_DTOCV_MASK,
1311a215186dSHaibo Chen 			esdhc_is_usdhc(imx_data) ? 0xF : 0xE,
1312e33eb8e2SAisheng Dong 			SDHCI_TIMEOUT_CONTROL);
1313e33eb8e2SAisheng Dong }
1314e33eb8e2SAisheng Dong 
1315bb6e3581SBOUGH CHEN static u32 esdhc_cqhci_irq(struct sdhci_host *host, u32 intmask)
1316bb6e3581SBOUGH CHEN {
1317bb6e3581SBOUGH CHEN 	int cmd_error = 0;
1318bb6e3581SBOUGH CHEN 	int data_error = 0;
1319bb6e3581SBOUGH CHEN 
1320bb6e3581SBOUGH CHEN 	if (!sdhci_cqe_irq(host, intmask, &cmd_error, &data_error))
1321bb6e3581SBOUGH CHEN 		return intmask;
1322bb6e3581SBOUGH CHEN 
1323bb6e3581SBOUGH CHEN 	cqhci_irq(host->mmc, intmask, cmd_error, data_error);
1324bb6e3581SBOUGH CHEN 
1325bb6e3581SBOUGH CHEN 	return 0;
1326bb6e3581SBOUGH CHEN }
1327bb6e3581SBOUGH CHEN 
13286e9fd28eSDong Aisheng static struct sdhci_ops sdhci_esdhc_ops = {
1329e149860dSRichard Zhu 	.read_l = esdhc_readl_le,
13300c6d49ceSWolfram Sang 	.read_w = esdhc_readw_le,
133177da3da0SAaron Brice 	.read_b = esdhc_readb_le,
1332e149860dSRichard Zhu 	.write_l = esdhc_writel_le,
13330c6d49ceSWolfram Sang 	.write_w = esdhc_writew_le,
13340c6d49ceSWolfram Sang 	.write_b = esdhc_writeb_le,
13358ba9580aSLucas Stach 	.set_clock = esdhc_pltfm_set_clock,
13360ddf03c9SLucas Stach 	.get_max_clock = esdhc_pltfm_get_max_clock,
13370c6d49ceSWolfram Sang 	.get_min_clock = esdhc_pltfm_get_min_clock,
133810fd0ad9SAisheng Dong 	.get_max_timeout_count = esdhc_get_max_timeout_count,
1339913413c3SShawn Guo 	.get_ro = esdhc_pltfm_get_ro,
1340e33eb8e2SAisheng Dong 	.set_timeout = esdhc_set_timeout,
13412317f56cSRussell King 	.set_bus_width = esdhc_pltfm_set_bus_width,
1342ad93220dSDong Aisheng 	.set_uhs_signaling = esdhc_set_uhs_signaling,
13430718e59aSRussell King 	.reset = esdhc_reset,
1344bb6e3581SBOUGH CHEN 	.irq = esdhc_cqhci_irq,
13453722c74cSHaibo Chen 	.dump_vendor_regs = esdhc_dump_debug_regs,
13460c6d49ceSWolfram Sang };
13470c6d49ceSWolfram Sang 
13481db5eebfSLars-Peter Clausen static const struct sdhci_pltfm_data sdhci_esdhc_imx_pdata = {
134997e4ba6aSRichard Zhu 	.quirks = ESDHC_DEFAULT_QUIRKS | SDHCI_QUIRK_NO_HISPD_BIT
135097e4ba6aSRichard Zhu 			| SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC
135197e4ba6aSRichard Zhu 			| SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC
135285d6509dSShawn Guo 			| SDHCI_QUIRK_BROKEN_CARD_DETECTION,
135385d6509dSShawn Guo 	.ops = &sdhci_esdhc_ops,
135485d6509dSShawn Guo };
135585d6509dSShawn Guo 
1356f3f5cf3dSDong Aisheng static void sdhci_esdhc_imx_hwinit(struct sdhci_host *host)
1357f3f5cf3dSDong Aisheng {
1358f3f5cf3dSDong Aisheng 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1359f3f5cf3dSDong Aisheng 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
1360982cf37dSHaibo Chen 	struct cqhci_host *cq_host = host->mmc->cqe_private;
13612b16cf32SDong Aisheng 	int tmp;
1362f3f5cf3dSDong Aisheng 
1363f3f5cf3dSDong Aisheng 	if (esdhc_is_usdhc(imx_data)) {
1364f3f5cf3dSDong Aisheng 		/*
1365f3f5cf3dSDong Aisheng 		 * The imx6q ROM code will change the default watermark
1366f3f5cf3dSDong Aisheng 		 * level setting to something insane.  Change it back here.
1367f3f5cf3dSDong Aisheng 		 */
1368f3f5cf3dSDong Aisheng 		writel(ESDHC_WTMK_DEFAULT_VAL, host->ioaddr + ESDHC_WTMK_LVL);
1369f3f5cf3dSDong Aisheng 
1370f3f5cf3dSDong Aisheng 		/*
1371f3f5cf3dSDong Aisheng 		 * ROM code will change the bit burst_length_enable setting
1372d04f8d5bSBenoît Thébaudeau 		 * to zero if this usdhc is chosen to boot system. Change
1373f3f5cf3dSDong Aisheng 		 * it back here, otherwise it will impact the performance a
1374f3f5cf3dSDong Aisheng 		 * lot. This bit is used to enable/disable the burst length
1375d04f8d5bSBenoît Thébaudeau 		 * for the external AHB2AXI bridge. It's useful especially
1376f3f5cf3dSDong Aisheng 		 * for INCR transfer because without burst length indicator,
1377f3f5cf3dSDong Aisheng 		 * the AHB2AXI bridge does not know the burst length in
1378f3f5cf3dSDong Aisheng 		 * advance. And without burst length indicator, AHB INCR
1379f3f5cf3dSDong Aisheng 		 * transfer can only be converted to singles on the AXI side.
1380f3f5cf3dSDong Aisheng 		 */
1381f3f5cf3dSDong Aisheng 		writel(readl(host->ioaddr + SDHCI_HOST_CONTROL)
1382f3f5cf3dSDong Aisheng 			| ESDHC_BURST_LEN_EN_INCR,
1383f3f5cf3dSDong Aisheng 			host->ioaddr + SDHCI_HOST_CONTROL);
1384e30be063SBOUGH CHEN 
1385f3f5cf3dSDong Aisheng 		/*
1386d04f8d5bSBenoît Thébaudeau 		 * erratum ESDHC_FLAG_ERR004536 fix for MX6Q TO1.2 and MX6DL
1387f3f5cf3dSDong Aisheng 		 * TO1.1, it's harmless for MX6SL
1388f3f5cf3dSDong Aisheng 		 */
1389*5c4f0062SChester Lin 		if (!(imx_data->socdata->flags & ESDHC_FLAG_SKIP_ERR004536)) {
1390e30be063SBOUGH CHEN 			writel(readl(host->ioaddr + 0x6c) & ~BIT(7),
1391f3f5cf3dSDong Aisheng 				host->ioaddr + 0x6c);
1392*5c4f0062SChester Lin 		}
1393f3f5cf3dSDong Aisheng 
1394f3f5cf3dSDong Aisheng 		/* disable DLL_CTRL delay line settings */
1395f3f5cf3dSDong Aisheng 		writel(0x0, host->ioaddr + ESDHC_DLL_CTRL);
13962b16cf32SDong Aisheng 
1397bcdb5301SBOUGH CHEN 		/*
1398bcdb5301SBOUGH CHEN 		 * For the case of command with busy, if set the bit
1399bcdb5301SBOUGH CHEN 		 * ESDHC_VEND_SPEC2_EN_BUSY_IRQ, USDHC will generate a
1400bcdb5301SBOUGH CHEN 		 * transfer complete interrupt when busy is deasserted.
1401bcdb5301SBOUGH CHEN 		 * When CQHCI use DCMD to send a CMD need R1b respons,
1402bcdb5301SBOUGH CHEN 		 * CQHCI require to set ESDHC_VEND_SPEC2_EN_BUSY_IRQ,
1403bcdb5301SBOUGH CHEN 		 * otherwise DCMD will always meet timeout waiting for
1404bcdb5301SBOUGH CHEN 		 * hardware interrupt issue.
1405bcdb5301SBOUGH CHEN 		 */
1406bcdb5301SBOUGH CHEN 		if (imx_data->socdata->flags & ESDHC_FLAG_CQHCI) {
1407bcdb5301SBOUGH CHEN 			tmp = readl(host->ioaddr + ESDHC_VEND_SPEC2);
1408bcdb5301SBOUGH CHEN 			tmp |= ESDHC_VEND_SPEC2_EN_BUSY_IRQ;
1409bcdb5301SBOUGH CHEN 			writel(tmp, host->ioaddr + ESDHC_VEND_SPEC2);
1410bcdb5301SBOUGH CHEN 
1411bcdb5301SBOUGH CHEN 			host->quirks &= ~SDHCI_QUIRK_NO_BUSY_IRQ;
1412bcdb5301SBOUGH CHEN 		}
1413bcdb5301SBOUGH CHEN 
14142b16cf32SDong Aisheng 		if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) {
14152b16cf32SDong Aisheng 			tmp = readl(host->ioaddr + ESDHC_TUNING_CTRL);
14162b16cf32SDong Aisheng 			tmp |= ESDHC_STD_TUNING_EN |
14172b16cf32SDong Aisheng 				ESDHC_TUNING_START_TAP_DEFAULT;
14182b16cf32SDong Aisheng 			if (imx_data->boarddata.tuning_start_tap) {
14192b16cf32SDong Aisheng 				tmp &= ~ESDHC_TUNING_START_TAP_MASK;
14202b16cf32SDong Aisheng 				tmp |= imx_data->boarddata.tuning_start_tap;
14212b16cf32SDong Aisheng 			}
14222b16cf32SDong Aisheng 
14232b16cf32SDong Aisheng 			if (imx_data->boarddata.tuning_step) {
14242b16cf32SDong Aisheng 				tmp &= ~ESDHC_TUNING_STEP_MASK;
14252b16cf32SDong Aisheng 				tmp |= imx_data->boarddata.tuning_step
14262b16cf32SDong Aisheng 					<< ESDHC_TUNING_STEP_SHIFT;
14272b16cf32SDong Aisheng 			}
142816e40e5bSHaibo Chen 
142916e40e5bSHaibo Chen 			/* Disable the CMD CRC check for tuning, if not, need to
143016e40e5bSHaibo Chen 			 * add some delay after every tuning command, because
143116e40e5bSHaibo Chen 			 * hardware standard tuning logic will directly go to next
143216e40e5bSHaibo Chen 			 * step once it detect the CMD CRC error, will not wait for
143316e40e5bSHaibo Chen 			 * the card side to finally send out the tuning data, trigger
143416e40e5bSHaibo Chen 			 * the buffer read ready interrupt immediately. If usdhc send
143516e40e5bSHaibo Chen 			 * the next tuning command some eMMC card will stuck, can't
143616e40e5bSHaibo Chen 			 * response, block the tuning procedure or the first command
143716e40e5bSHaibo Chen 			 * after the whole tuning procedure always can't get any response.
143816e40e5bSHaibo Chen 			 */
143916e40e5bSHaibo Chen 			tmp |= ESDHC_TUNING_CMD_CRC_CHECK_DISABLE;
14402b16cf32SDong Aisheng 			writel(tmp, host->ioaddr + ESDHC_TUNING_CTRL);
1441a98c557eSBOUGH CHEN 		} else if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING) {
1442a98c557eSBOUGH CHEN 			/*
1443a98c557eSBOUGH CHEN 			 * ESDHC_STD_TUNING_EN may be configed in bootloader
1444a98c557eSBOUGH CHEN 			 * or ROM code, so clear this bit here to make sure
1445a98c557eSBOUGH CHEN 			 * the manual tuning can work.
1446a98c557eSBOUGH CHEN 			 */
1447a98c557eSBOUGH CHEN 			tmp = readl(host->ioaddr + ESDHC_TUNING_CTRL);
1448a98c557eSBOUGH CHEN 			tmp &= ~ESDHC_STD_TUNING_EN;
1449a98c557eSBOUGH CHEN 			writel(tmp, host->ioaddr + ESDHC_TUNING_CTRL);
14502b16cf32SDong Aisheng 		}
1451982cf37dSHaibo Chen 
1452982cf37dSHaibo Chen 		/*
1453982cf37dSHaibo Chen 		 * On i.MX8MM, we are running Dual Linux OS, with 1st Linux using SD Card
1454982cf37dSHaibo Chen 		 * as rootfs storage, 2nd Linux using eMMC as rootfs storage. We let the
1455982cf37dSHaibo Chen 		 * the 1st linux configure power/clock for the 2nd Linux.
1456982cf37dSHaibo Chen 		 *
1457982cf37dSHaibo Chen 		 * When the 2nd Linux is booting into rootfs stage, we let the 1st Linux
1458982cf37dSHaibo Chen 		 * to destroy the 2nd linux, then restart the 2nd linux, we met SDHCI dump.
1459982cf37dSHaibo Chen 		 * After we clear the pending interrupt and halt CQCTL, issue gone.
1460982cf37dSHaibo Chen 		 */
1461982cf37dSHaibo Chen 		if (cq_host) {
1462982cf37dSHaibo Chen 			tmp = cqhci_readl(cq_host, CQHCI_IS);
1463982cf37dSHaibo Chen 			cqhci_writel(cq_host, tmp, CQHCI_IS);
1464982cf37dSHaibo Chen 			cqhci_writel(cq_host, CQHCI_HALT, CQHCI_CTL);
1465982cf37dSHaibo Chen 		}
1466f3f5cf3dSDong Aisheng 	}
1467f3f5cf3dSDong Aisheng }
1468f3f5cf3dSDong Aisheng 
1469bb6e3581SBOUGH CHEN static void esdhc_cqe_enable(struct mmc_host *mmc)
1470bb6e3581SBOUGH CHEN {
1471bb6e3581SBOUGH CHEN 	struct sdhci_host *host = mmc_priv(mmc);
147285236d2bSBOUGH CHEN 	struct cqhci_host *cq_host = mmc->cqe_private;
1473bb6e3581SBOUGH CHEN 	u32 reg;
1474bb6e3581SBOUGH CHEN 	u16 mode;
1475bb6e3581SBOUGH CHEN 	int count = 10;
1476bb6e3581SBOUGH CHEN 
1477bb6e3581SBOUGH CHEN 	/*
1478bb6e3581SBOUGH CHEN 	 * CQE gets stuck if it sees Buffer Read Enable bit set, which can be
1479bb6e3581SBOUGH CHEN 	 * the case after tuning, so ensure the buffer is drained.
1480bb6e3581SBOUGH CHEN 	 */
1481bb6e3581SBOUGH CHEN 	reg = sdhci_readl(host, SDHCI_PRESENT_STATE);
1482bb6e3581SBOUGH CHEN 	while (reg & SDHCI_DATA_AVAILABLE) {
1483bb6e3581SBOUGH CHEN 		sdhci_readl(host, SDHCI_BUFFER);
1484bb6e3581SBOUGH CHEN 		reg = sdhci_readl(host, SDHCI_PRESENT_STATE);
1485bb6e3581SBOUGH CHEN 		if (count-- == 0) {
1486bb6e3581SBOUGH CHEN 			dev_warn(mmc_dev(host->mmc),
1487bb6e3581SBOUGH CHEN 				"CQE may get stuck because the Buffer Read Enable bit is set\n");
1488bb6e3581SBOUGH CHEN 			break;
1489bb6e3581SBOUGH CHEN 		}
1490bb6e3581SBOUGH CHEN 		mdelay(1);
1491bb6e3581SBOUGH CHEN 	}
1492bb6e3581SBOUGH CHEN 
1493bb6e3581SBOUGH CHEN 	/*
1494bb6e3581SBOUGH CHEN 	 * Runtime resume will reset the entire host controller, which
1495bb6e3581SBOUGH CHEN 	 * will also clear the DMAEN/BCEN of register ESDHC_MIX_CTRL.
1496bb6e3581SBOUGH CHEN 	 * Here set DMAEN and BCEN when enable CMDQ.
1497bb6e3581SBOUGH CHEN 	 */
1498bb6e3581SBOUGH CHEN 	mode = sdhci_readw(host, SDHCI_TRANSFER_MODE);
1499bb6e3581SBOUGH CHEN 	if (host->flags & SDHCI_REQ_USE_DMA)
1500bb6e3581SBOUGH CHEN 		mode |= SDHCI_TRNS_DMA;
1501bb6e3581SBOUGH CHEN 	if (!(host->quirks2 & SDHCI_QUIRK2_SUPPORT_SINGLE))
1502bb6e3581SBOUGH CHEN 		mode |= SDHCI_TRNS_BLK_CNT_EN;
1503bb6e3581SBOUGH CHEN 	sdhci_writew(host, mode, SDHCI_TRANSFER_MODE);
1504bb6e3581SBOUGH CHEN 
150585236d2bSBOUGH CHEN 	/*
150685236d2bSBOUGH CHEN 	 * Though Runtime resume reset the entire host controller,
150785236d2bSBOUGH CHEN 	 * but do not impact the CQHCI side, need to clear the
150885236d2bSBOUGH CHEN 	 * HALT bit, avoid CQHCI stuck in the first request when
150985236d2bSBOUGH CHEN 	 * system resume back.
151085236d2bSBOUGH CHEN 	 */
151185236d2bSBOUGH CHEN 	cqhci_writel(cq_host, 0, CQHCI_CTL);
151285236d2bSBOUGH CHEN 	if (cqhci_readl(cq_host, CQHCI_CTL) && CQHCI_HALT)
151385236d2bSBOUGH CHEN 		dev_err(mmc_dev(host->mmc),
151485236d2bSBOUGH CHEN 			"failed to exit halt state when enable CQE\n");
151585236d2bSBOUGH CHEN 
151685236d2bSBOUGH CHEN 
1517bb6e3581SBOUGH CHEN 	sdhci_cqe_enable(mmc);
1518bb6e3581SBOUGH CHEN }
1519bb6e3581SBOUGH CHEN 
1520bb6e3581SBOUGH CHEN static void esdhc_sdhci_dumpregs(struct mmc_host *mmc)
1521bb6e3581SBOUGH CHEN {
1522bb6e3581SBOUGH CHEN 	sdhci_dumpregs(mmc_priv(mmc));
1523bb6e3581SBOUGH CHEN }
1524bb6e3581SBOUGH CHEN 
1525bb6e3581SBOUGH CHEN static const struct cqhci_host_ops esdhc_cqhci_ops = {
1526bb6e3581SBOUGH CHEN 	.enable		= esdhc_cqe_enable,
1527bb6e3581SBOUGH CHEN 	.disable	= sdhci_cqe_disable,
1528bb6e3581SBOUGH CHEN 	.dumpregs	= esdhc_sdhci_dumpregs,
1529bb6e3581SBOUGH CHEN };
1530bb6e3581SBOUGH CHEN 
1531c3be1efdSBill Pemberton static int
1532abfafc2dSShawn Guo sdhci_esdhc_imx_probe_dt(struct platform_device *pdev,
153307bf2b54SSascha Hauer 			 struct sdhci_host *host,
153491fa4252SDong Aisheng 			 struct pltfm_imx_data *imx_data)
1535abfafc2dSShawn Guo {
1536abfafc2dSShawn Guo 	struct device_node *np = pdev->dev.of_node;
153791fa4252SDong Aisheng 	struct esdhc_platform_data *boarddata = &imx_data->boarddata;
15384800e87aSDong Aisheng 	int ret;
1539abfafc2dSShawn Guo 
1540abfafc2dSShawn Guo 	if (of_get_property(np, "fsl,wp-controller", NULL))
1541abfafc2dSShawn Guo 		boarddata->wp_type = ESDHC_WP_CONTROLLER;
1542abfafc2dSShawn Guo 
154374ff81e1SLinus Walleij 	/*
154474ff81e1SLinus Walleij 	 * If we have this property, then activate WP check.
154574ff81e1SLinus Walleij 	 * Retrieveing and requesting the actual WP GPIO will happen
154674ff81e1SLinus Walleij 	 * in the call to mmc_of_parse().
154774ff81e1SLinus Walleij 	 */
154874ff81e1SLinus Walleij 	if (of_property_read_bool(np, "wp-gpios"))
1549abfafc2dSShawn Guo 		boarddata->wp_type = ESDHC_WP_GPIO;
1550abfafc2dSShawn Guo 
1551d407e30bSHaibo Chen 	of_property_read_u32(np, "fsl,tuning-step", &boarddata->tuning_step);
1552d87fc966SDong Aisheng 	of_property_read_u32(np, "fsl,tuning-start-tap",
1553d87fc966SDong Aisheng 			     &boarddata->tuning_start_tap);
1554d407e30bSHaibo Chen 
15555bd2acdcSHaibo Chen 	of_property_read_u32(np, "fsl,strobe-dll-delay-target",
15565bd2acdcSHaibo Chen 				&boarddata->strobe_dll_delay_target);
1557ad93220dSDong Aisheng 	if (of_find_property(np, "no-1-8-v", NULL))
155886f495c5SStefan Agner 		host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V;
1559ad93220dSDong Aisheng 
1560602519b2SDong Aisheng 	if (of_property_read_u32(np, "fsl,delay-line", &boarddata->delay_line))
1561602519b2SDong Aisheng 		boarddata->delay_line = 0;
1562602519b2SDong Aisheng 
15636dab809bSAndy Shevchenko 	mmc_of_parse_voltage(host->mmc, &host->ocr_mask);
156407bf2b54SSascha Hauer 
1565f410ee0aSPeng Fan 	if (esdhc_is_usdhc(imx_data) && !IS_ERR(imx_data->pinctrl)) {
156691fa4252SDong Aisheng 		imx_data->pins_100mhz = pinctrl_lookup_state(imx_data->pinctrl,
156791fa4252SDong Aisheng 						ESDHC_PINCTRL_STATE_100MHZ);
156891fa4252SDong Aisheng 		imx_data->pins_200mhz = pinctrl_lookup_state(imx_data->pinctrl,
156991fa4252SDong Aisheng 						ESDHC_PINCTRL_STATE_200MHZ);
157091fa4252SDong Aisheng 	}
157191fa4252SDong Aisheng 
157215064119SFabio Estevam 	/* call to generic mmc_of_parse to support additional capabilities */
15734800e87aSDong Aisheng 	ret = mmc_of_parse(host->mmc);
15744800e87aSDong Aisheng 	if (ret)
15754800e87aSDong Aisheng 		return ret;
15764800e87aSDong Aisheng 
1577287980e4SArnd Bergmann 	if (mmc_gpio_get_cd(host->mmc) >= 0)
15784800e87aSDong Aisheng 		host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION;
15794800e87aSDong Aisheng 
15804800e87aSDong Aisheng 	return 0;
1581abfafc2dSShawn Guo }
1582abfafc2dSShawn Guo 
1583c3be1efdSBill Pemberton static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
158495f25efeSWolfram Sang {
158585d6509dSShawn Guo 	struct sdhci_pltfm_host *pltfm_host;
158685d6509dSShawn Guo 	struct sdhci_host *host;
1587bb6e3581SBOUGH CHEN 	struct cqhci_host *cq_host;
15880c6d49ceSWolfram Sang 	int err;
1589e149860dSRichard Zhu 	struct pltfm_imx_data *imx_data;
159095f25efeSWolfram Sang 
1591070e6d3fSJisheng Zhang 	host = sdhci_pltfm_init(pdev, &sdhci_esdhc_imx_pdata,
1592070e6d3fSJisheng Zhang 				sizeof(*imx_data));
159385d6509dSShawn Guo 	if (IS_ERR(host))
159485d6509dSShawn Guo 		return PTR_ERR(host);
159585d6509dSShawn Guo 
159685d6509dSShawn Guo 	pltfm_host = sdhci_priv(host);
159785d6509dSShawn Guo 
1598070e6d3fSJisheng Zhang 	imx_data = sdhci_pltfm_priv(pltfm_host);
159957ed3314SShawn Guo 
160091b3d2e5SFabio Estevam 	imx_data->socdata = device_get_match_data(&pdev->dev);
160185d6509dSShawn Guo 
16021c4989b0SBOUGH CHEN 	if (imx_data->socdata->flags & ESDHC_FLAG_PMQOS)
1603d1b98305SRafael J. Wysocki 		cpu_latency_qos_add_request(&imx_data->pm_qos_req, 0);
16041c4989b0SBOUGH CHEN 
160552dac615SSascha Hauer 	imx_data->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
160652dac615SSascha Hauer 	if (IS_ERR(imx_data->clk_ipg)) {
160752dac615SSascha Hauer 		err = PTR_ERR(imx_data->clk_ipg);
1608e3af31c6SShawn Guo 		goto free_sdhci;
160995f25efeSWolfram Sang 	}
161052dac615SSascha Hauer 
161152dac615SSascha Hauer 	imx_data->clk_ahb = devm_clk_get(&pdev->dev, "ahb");
161252dac615SSascha Hauer 	if (IS_ERR(imx_data->clk_ahb)) {
161352dac615SSascha Hauer 		err = PTR_ERR(imx_data->clk_ahb);
1614e3af31c6SShawn Guo 		goto free_sdhci;
161552dac615SSascha Hauer 	}
161652dac615SSascha Hauer 
161752dac615SSascha Hauer 	imx_data->clk_per = devm_clk_get(&pdev->dev, "per");
161852dac615SSascha Hauer 	if (IS_ERR(imx_data->clk_per)) {
161952dac615SSascha Hauer 		err = PTR_ERR(imx_data->clk_per);
1620e3af31c6SShawn Guo 		goto free_sdhci;
162152dac615SSascha Hauer 	}
162252dac615SSascha Hauer 
162352dac615SSascha Hauer 	pltfm_host->clk = imx_data->clk_per;
1624a974862fSDong Aisheng 	pltfm_host->clock = clk_get_rate(pltfm_host->clk);
162517b1eb7fSFabio Estevam 	err = clk_prepare_enable(imx_data->clk_per);
162617b1eb7fSFabio Estevam 	if (err)
162717b1eb7fSFabio Estevam 		goto free_sdhci;
162817b1eb7fSFabio Estevam 	err = clk_prepare_enable(imx_data->clk_ipg);
162917b1eb7fSFabio Estevam 	if (err)
163017b1eb7fSFabio Estevam 		goto disable_per_clk;
163117b1eb7fSFabio Estevam 	err = clk_prepare_enable(imx_data->clk_ahb);
163217b1eb7fSFabio Estevam 	if (err)
163317b1eb7fSFabio Estevam 		goto disable_ipg_clk;
163495f25efeSWolfram Sang 
1635ad93220dSDong Aisheng 	imx_data->pinctrl = devm_pinctrl_get(&pdev->dev);
16369e70ff99SHaibo Chen 	if (IS_ERR(imx_data->pinctrl))
1637b62eee9fSHaibo Chen 		dev_warn(mmc_dev(host->mmc), "could not get pinctrl\n");
1638e62d8b8fSDong Aisheng 
163969ed60e0SDong Aisheng 	if (esdhc_is_usdhc(imx_data)) {
164069ed60e0SDong Aisheng 		host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN;
164109c8192bSStefan Agner 		host->mmc->caps |= MMC_CAP_1_8V_DDR | MMC_CAP_3_3V_DDR;
1642f6140462SHaibo Chen 
1643f6140462SHaibo Chen 		/* GPIO CD can be set as a wakeup source */
1644f6140462SHaibo Chen 		host->mmc->caps |= MMC_CAP_CD_WAKE;
1645f6140462SHaibo Chen 
16464245afffSDong Aisheng 		if (!(imx_data->socdata->flags & ESDHC_FLAG_HS200))
16474245afffSDong Aisheng 			host->quirks2 |= SDHCI_QUIRK2_BROKEN_HS200;
1648a75dcbf4SDong Aisheng 
1649a75dcbf4SDong Aisheng 		/* clear tuning bits in case ROM has set it already */
1650a75dcbf4SDong Aisheng 		writel(0x0, host->ioaddr + ESDHC_MIX_CTRL);
1651869f8a69SAdrian Hunter 		writel(0x0, host->ioaddr + SDHCI_AUTO_CMD_STATUS);
1652a75dcbf4SDong Aisheng 		writel(0x0, host->ioaddr + ESDHC_TUNE_CTRL_STATUS);
1653de3e1dd0SBOUGH CHEN 
1654de3e1dd0SBOUGH CHEN 		/*
1655de3e1dd0SBOUGH CHEN 		 * Link usdhc specific mmc_host_ops execute_tuning function,
1656de3e1dd0SBOUGH CHEN 		 * to replace the standard one in sdhci_ops.
1657de3e1dd0SBOUGH CHEN 		 */
1658de3e1dd0SBOUGH CHEN 		host->mmc_host_ops.execute_tuning = usdhc_execute_tuning;
165969ed60e0SDong Aisheng 	}
1660f750ba9bSShawn Guo 
16616e9fd28eSDong Aisheng 	if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING)
16626e9fd28eSDong Aisheng 		sdhci_esdhc_ops.platform_execute_tuning =
16636e9fd28eSDong Aisheng 					esdhc_executing_tuning;
16648b2bb0adSDong Aisheng 
166518094430SDong Aisheng 	if (imx_data->socdata->flags & ESDHC_FLAG_ERR004536)
166618094430SDong Aisheng 		host->quirks |= SDHCI_QUIRK_BROKEN_ADMA;
166718094430SDong Aisheng 
166828b07674SHaibo Chen 	if (imx_data->socdata->flags & ESDHC_FLAG_HS400)
16692991ad76SLucas Stach 		host->mmc->caps2 |= MMC_CAP2_HS400;
167028b07674SHaibo Chen 
167174898cbcSHaibo Chen 	if (imx_data->socdata->flags & ESDHC_FLAG_BROKEN_AUTO_CMD23)
167274898cbcSHaibo Chen 		host->quirks2 |= SDHCI_QUIRK2_ACMD23_BROKEN;
167374898cbcSHaibo Chen 
1674029e2476SBOUGH CHEN 	if (imx_data->socdata->flags & ESDHC_FLAG_HS400_ES) {
1675029e2476SBOUGH CHEN 		host->mmc->caps2 |= MMC_CAP2_HS400_ES;
1676029e2476SBOUGH CHEN 		host->mmc_host_ops.hs400_enhanced_strobe =
1677029e2476SBOUGH CHEN 					esdhc_hs400_enhanced_strobe;
1678029e2476SBOUGH CHEN 	}
1679029e2476SBOUGH CHEN 
1680bb6e3581SBOUGH CHEN 	if (imx_data->socdata->flags & ESDHC_FLAG_CQHCI) {
1681bcdb5301SBOUGH CHEN 		host->mmc->caps2 |= MMC_CAP2_CQE | MMC_CAP2_CQE_DCMD;
1682bb6e3581SBOUGH CHEN 		cq_host = devm_kzalloc(&pdev->dev, sizeof(*cq_host), GFP_KERNEL);
16839a633f3bSWei Yongjun 		if (!cq_host) {
16849a633f3bSWei Yongjun 			err = -ENOMEM;
1685bb6e3581SBOUGH CHEN 			goto disable_ahb_clk;
1686bb6e3581SBOUGH CHEN 		}
1687bb6e3581SBOUGH CHEN 
1688bb6e3581SBOUGH CHEN 		cq_host->mmio = host->ioaddr + ESDHC_CQHCI_ADDR_OFFSET;
1689bb6e3581SBOUGH CHEN 		cq_host->ops = &esdhc_cqhci_ops;
1690bb6e3581SBOUGH CHEN 
1691bb6e3581SBOUGH CHEN 		err = cqhci_init(cq_host, host->mmc, false);
1692bb6e3581SBOUGH CHEN 		if (err)
1693bb6e3581SBOUGH CHEN 			goto disable_ahb_clk;
1694bb6e3581SBOUGH CHEN 	}
1695bb6e3581SBOUGH CHEN 
169691fa4252SDong Aisheng 	err = sdhci_esdhc_imx_probe_dt(pdev, host, imx_data);
169791fa4252SDong Aisheng 	if (err)
169817b1eb7fSFabio Estevam 		goto disable_ahb_clk;
1699ad93220dSDong Aisheng 
1700f3f5cf3dSDong Aisheng 	sdhci_esdhc_imx_hwinit(host);
1701f3f5cf3dSDong Aisheng 
170285d6509dSShawn Guo 	err = sdhci_add_host(host);
170385d6509dSShawn Guo 	if (err)
170417b1eb7fSFabio Estevam 		goto disable_ahb_clk;
170585d6509dSShawn Guo 
1706f62f7bccSHaibo Chen 	/*
1707f62f7bccSHaibo Chen 	 * Setup the wakeup capability here, let user to decide
1708f62f7bccSHaibo Chen 	 * whether need to enable this wakeup through sysfs interface.
1709f62f7bccSHaibo Chen 	 */
1710f62f7bccSHaibo Chen 	if ((host->mmc->pm_caps & MMC_PM_KEEP_POWER) &&
1711f62f7bccSHaibo Chen 			(host->mmc->pm_caps & MMC_PM_WAKE_SDIO_IRQ))
1712f62f7bccSHaibo Chen 		device_set_wakeup_capable(&pdev->dev, true);
1713f62f7bccSHaibo Chen 
171489d7e5c1SDong Aisheng 	pm_runtime_set_active(&pdev->dev);
171589d7e5c1SDong Aisheng 	pm_runtime_set_autosuspend_delay(&pdev->dev, 50);
171689d7e5c1SDong Aisheng 	pm_runtime_use_autosuspend(&pdev->dev);
171789d7e5c1SDong Aisheng 	pm_suspend_ignore_children(&pdev->dev, 1);
171877903c01SUlf Hansson 	pm_runtime_enable(&pdev->dev);
171989d7e5c1SDong Aisheng 
17207e29c306SWolfram Sang 	return 0;
17217e29c306SWolfram Sang 
172217b1eb7fSFabio Estevam disable_ahb_clk:
172352dac615SSascha Hauer 	clk_disable_unprepare(imx_data->clk_ahb);
172417b1eb7fSFabio Estevam disable_ipg_clk:
172517b1eb7fSFabio Estevam 	clk_disable_unprepare(imx_data->clk_ipg);
172617b1eb7fSFabio Estevam disable_per_clk:
172717b1eb7fSFabio Estevam 	clk_disable_unprepare(imx_data->clk_per);
1728e3af31c6SShawn Guo free_sdhci:
17291c4989b0SBOUGH CHEN 	if (imx_data->socdata->flags & ESDHC_FLAG_PMQOS)
1730d1b98305SRafael J. Wysocki 		cpu_latency_qos_remove_request(&imx_data->pm_qos_req);
173185d6509dSShawn Guo 	sdhci_pltfm_free(pdev);
173285d6509dSShawn Guo 	return err;
173395f25efeSWolfram Sang }
173495f25efeSWolfram Sang 
17356e0ee714SBill Pemberton static int sdhci_esdhc_imx_remove(struct platform_device *pdev)
173695f25efeSWolfram Sang {
173785d6509dSShawn Guo 	struct sdhci_host *host = platform_get_drvdata(pdev);
173895f25efeSWolfram Sang 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1739070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
1740a56f4413SFrank Li 	int dead;
174185d6509dSShawn Guo 
17420b414368SUlf Hansson 	pm_runtime_get_sync(&pdev->dev);
1743a56f4413SFrank Li 	dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff);
17440b414368SUlf Hansson 	pm_runtime_disable(&pdev->dev);
17450b414368SUlf Hansson 	pm_runtime_put_noidle(&pdev->dev);
17460b414368SUlf Hansson 
174785d6509dSShawn Guo 	sdhci_remove_host(host, dead);
17480c6d49ceSWolfram Sang 
174952dac615SSascha Hauer 	clk_disable_unprepare(imx_data->clk_per);
175052dac615SSascha Hauer 	clk_disable_unprepare(imx_data->clk_ipg);
175152dac615SSascha Hauer 	clk_disable_unprepare(imx_data->clk_ahb);
175252dac615SSascha Hauer 
17531c4989b0SBOUGH CHEN 	if (imx_data->socdata->flags & ESDHC_FLAG_PMQOS)
1754d1b98305SRafael J. Wysocki 		cpu_latency_qos_remove_request(&imx_data->pm_qos_req);
17551c4989b0SBOUGH CHEN 
175685d6509dSShawn Guo 	sdhci_pltfm_free(pdev);
175785d6509dSShawn Guo 
175885d6509dSShawn Guo 	return 0;
175995f25efeSWolfram Sang }
176095f25efeSWolfram Sang 
17612788ed42SUlf Hansson #ifdef CONFIG_PM_SLEEP
176204143fbaSDong Aisheng static int sdhci_esdhc_suspend(struct device *dev)
176304143fbaSDong Aisheng {
17643e3274abSUlf Hansson 	struct sdhci_host *host = dev_get_drvdata(dev);
1765a26a4f1bSHaibo Chen 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1766a26a4f1bSHaibo Chen 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
1767bb6e3581SBOUGH CHEN 	int ret;
1768bb6e3581SBOUGH CHEN 
1769bb6e3581SBOUGH CHEN 	if (host->mmc->caps2 & MMC_CAP2_CQE) {
1770bb6e3581SBOUGH CHEN 		ret = cqhci_suspend(host->mmc);
1771bb6e3581SBOUGH CHEN 		if (ret)
1772bb6e3581SBOUGH CHEN 			return ret;
1773bb6e3581SBOUGH CHEN 	}
17743e3274abSUlf Hansson 
1775a26a4f1bSHaibo Chen 	if ((imx_data->socdata->flags & ESDHC_FLAG_STATE_LOST_IN_LPMODE) &&
1776a26a4f1bSHaibo Chen 		(host->tuning_mode != SDHCI_TUNING_MODE_1)) {
1777a26a4f1bSHaibo Chen 		mmc_retune_timer_stop(host->mmc);
1778a26a4f1bSHaibo Chen 		mmc_retune_needed(host->mmc);
1779a26a4f1bSHaibo Chen 	}
1780a26a4f1bSHaibo Chen 
1781d38dcad4SAdrian Hunter 	if (host->tuning_mode != SDHCI_TUNING_MODE_3)
1782d38dcad4SAdrian Hunter 		mmc_retune_needed(host->mmc);
1783d38dcad4SAdrian Hunter 
1784af8fade4SHaibo Chen 	ret = sdhci_suspend_host(host);
1785f6140462SHaibo Chen 	if (ret)
1786f6140462SHaibo Chen 		return ret;
1787f6140462SHaibo Chen 
1788f6140462SHaibo Chen 	ret = pinctrl_pm_select_sleep_state(dev);
1789f6140462SHaibo Chen 	if (ret)
1790f6140462SHaibo Chen 		return ret;
1791f6140462SHaibo Chen 
1792f6140462SHaibo Chen 	ret = mmc_gpio_set_cd_wake(host->mmc, true);
1793af8fade4SHaibo Chen 
1794af8fade4SHaibo Chen 	return ret;
179504143fbaSDong Aisheng }
179604143fbaSDong Aisheng 
179704143fbaSDong Aisheng static int sdhci_esdhc_resume(struct device *dev)
179804143fbaSDong Aisheng {
1799cc17e129SDong Aisheng 	struct sdhci_host *host = dev_get_drvdata(dev);
1800bb6e3581SBOUGH CHEN 	int ret;
1801cc17e129SDong Aisheng 
1802af8fade4SHaibo Chen 	ret = pinctrl_pm_select_default_state(dev);
1803af8fade4SHaibo Chen 	if (ret)
1804af8fade4SHaibo Chen 		return ret;
1805af8fade4SHaibo Chen 
180619dbfdd3SDong Aisheng 	/* re-initialize hw state in case it's lost in low power mode */
180719dbfdd3SDong Aisheng 	sdhci_esdhc_imx_hwinit(host);
1808cc17e129SDong Aisheng 
1809bb6e3581SBOUGH CHEN 	ret = sdhci_resume_host(host);
1810bb6e3581SBOUGH CHEN 	if (ret)
1811bb6e3581SBOUGH CHEN 		return ret;
1812bb6e3581SBOUGH CHEN 
1813bb6e3581SBOUGH CHEN 	if (host->mmc->caps2 & MMC_CAP2_CQE)
1814bb6e3581SBOUGH CHEN 		ret = cqhci_resume(host->mmc);
1815bb6e3581SBOUGH CHEN 
1816f6140462SHaibo Chen 	if (!ret)
1817f6140462SHaibo Chen 		ret = mmc_gpio_set_cd_wake(host->mmc, false);
1818f6140462SHaibo Chen 
1819bb6e3581SBOUGH CHEN 	return ret;
182004143fbaSDong Aisheng }
18212788ed42SUlf Hansson #endif
182204143fbaSDong Aisheng 
18232788ed42SUlf Hansson #ifdef CONFIG_PM
182489d7e5c1SDong Aisheng static int sdhci_esdhc_runtime_suspend(struct device *dev)
182589d7e5c1SDong Aisheng {
182689d7e5c1SDong Aisheng 	struct sdhci_host *host = dev_get_drvdata(dev);
182789d7e5c1SDong Aisheng 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1828070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
182989d7e5c1SDong Aisheng 	int ret;
183089d7e5c1SDong Aisheng 
1831bb6e3581SBOUGH CHEN 	if (host->mmc->caps2 & MMC_CAP2_CQE) {
1832bb6e3581SBOUGH CHEN 		ret = cqhci_suspend(host->mmc);
1833bb6e3581SBOUGH CHEN 		if (ret)
1834bb6e3581SBOUGH CHEN 			return ret;
1835bb6e3581SBOUGH CHEN 	}
1836bb6e3581SBOUGH CHEN 
183789d7e5c1SDong Aisheng 	ret = sdhci_runtime_suspend_host(host);
1838371d39faSMichael Trimarchi 	if (ret)
1839371d39faSMichael Trimarchi 		return ret;
184089d7e5c1SDong Aisheng 
1841d38dcad4SAdrian Hunter 	if (host->tuning_mode != SDHCI_TUNING_MODE_3)
1842d38dcad4SAdrian Hunter 		mmc_retune_needed(host->mmc);
1843d38dcad4SAdrian Hunter 
18443602785bSMichael Trimarchi 	imx_data->actual_clock = host->mmc->actual_clock;
18453602785bSMichael Trimarchi 	esdhc_pltfm_set_clock(host, 0);
184689d7e5c1SDong Aisheng 	clk_disable_unprepare(imx_data->clk_per);
184789d7e5c1SDong Aisheng 	clk_disable_unprepare(imx_data->clk_ipg);
184889d7e5c1SDong Aisheng 	clk_disable_unprepare(imx_data->clk_ahb);
184989d7e5c1SDong Aisheng 
18501c4989b0SBOUGH CHEN 	if (imx_data->socdata->flags & ESDHC_FLAG_PMQOS)
1851d1b98305SRafael J. Wysocki 		cpu_latency_qos_remove_request(&imx_data->pm_qos_req);
18521c4989b0SBOUGH CHEN 
185389d7e5c1SDong Aisheng 	return ret;
185489d7e5c1SDong Aisheng }
185589d7e5c1SDong Aisheng 
185689d7e5c1SDong Aisheng static int sdhci_esdhc_runtime_resume(struct device *dev)
185789d7e5c1SDong Aisheng {
185889d7e5c1SDong Aisheng 	struct sdhci_host *host = dev_get_drvdata(dev);
185989d7e5c1SDong Aisheng 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1860070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
186117b1eb7fSFabio Estevam 	int err;
186289d7e5c1SDong Aisheng 
18631c4989b0SBOUGH CHEN 	if (imx_data->socdata->flags & ESDHC_FLAG_PMQOS)
1864d1b98305SRafael J. Wysocki 		cpu_latency_qos_add_request(&imx_data->pm_qos_req, 0);
18651c4989b0SBOUGH CHEN 
18665c11f1ffSHaibo Chen 	if (imx_data->socdata->flags & ESDHC_FLAG_CLK_RATE_LOST_IN_PM_RUNTIME)
18675c11f1ffSHaibo Chen 		clk_set_rate(imx_data->clk_per, pltfm_host->clock);
18685c11f1ffSHaibo Chen 
1869a0ad3087SMichael Trimarchi 	err = clk_prepare_enable(imx_data->clk_ahb);
1870a0ad3087SMichael Trimarchi 	if (err)
18711c4989b0SBOUGH CHEN 		goto remove_pm_qos_request;
1872a0ad3087SMichael Trimarchi 
187317b1eb7fSFabio Estevam 	err = clk_prepare_enable(imx_data->clk_per);
187417b1eb7fSFabio Estevam 	if (err)
1875a0ad3087SMichael Trimarchi 		goto disable_ahb_clk;
1876af5d2b7bSUlf Hansson 
187717b1eb7fSFabio Estevam 	err = clk_prepare_enable(imx_data->clk_ipg);
187817b1eb7fSFabio Estevam 	if (err)
187917b1eb7fSFabio Estevam 		goto disable_per_clk;
1880af5d2b7bSUlf Hansson 
18813602785bSMichael Trimarchi 	esdhc_pltfm_set_clock(host, imx_data->actual_clock);
1882a0ad3087SMichael Trimarchi 
1883c6303c5dSBaolin Wang 	err = sdhci_runtime_resume_host(host, 0);
188417b1eb7fSFabio Estevam 	if (err)
1885a0ad3087SMichael Trimarchi 		goto disable_ipg_clk;
188689d7e5c1SDong Aisheng 
1887bb6e3581SBOUGH CHEN 	if (host->mmc->caps2 & MMC_CAP2_CQE)
1888bb6e3581SBOUGH CHEN 		err = cqhci_resume(host->mmc);
1889bb6e3581SBOUGH CHEN 
1890bb6e3581SBOUGH CHEN 	return err;
189117b1eb7fSFabio Estevam 
189217b1eb7fSFabio Estevam disable_ipg_clk:
189317b1eb7fSFabio Estevam 	clk_disable_unprepare(imx_data->clk_ipg);
189417b1eb7fSFabio Estevam disable_per_clk:
189517b1eb7fSFabio Estevam 	clk_disable_unprepare(imx_data->clk_per);
1896a0ad3087SMichael Trimarchi disable_ahb_clk:
1897a0ad3087SMichael Trimarchi 	clk_disable_unprepare(imx_data->clk_ahb);
18981c4989b0SBOUGH CHEN remove_pm_qos_request:
18991c4989b0SBOUGH CHEN 	if (imx_data->socdata->flags & ESDHC_FLAG_PMQOS)
1900d1b98305SRafael J. Wysocki 		cpu_latency_qos_remove_request(&imx_data->pm_qos_req);
190117b1eb7fSFabio Estevam 	return err;
190289d7e5c1SDong Aisheng }
190389d7e5c1SDong Aisheng #endif
190489d7e5c1SDong Aisheng 
190589d7e5c1SDong Aisheng static const struct dev_pm_ops sdhci_esdhc_pmops = {
190604143fbaSDong Aisheng 	SET_SYSTEM_SLEEP_PM_OPS(sdhci_esdhc_suspend, sdhci_esdhc_resume)
190789d7e5c1SDong Aisheng 	SET_RUNTIME_PM_OPS(sdhci_esdhc_runtime_suspend,
190889d7e5c1SDong Aisheng 				sdhci_esdhc_runtime_resume, NULL)
190989d7e5c1SDong Aisheng };
191089d7e5c1SDong Aisheng 
191185d6509dSShawn Guo static struct platform_driver sdhci_esdhc_imx_driver = {
191285d6509dSShawn Guo 	.driver		= {
191385d6509dSShawn Guo 		.name	= "sdhci-esdhc-imx",
191421b2cec6SDouglas Anderson 		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
1915abfafc2dSShawn Guo 		.of_match_table = imx_esdhc_dt_ids,
191689d7e5c1SDong Aisheng 		.pm	= &sdhci_esdhc_pmops,
191785d6509dSShawn Guo 	},
191885d6509dSShawn Guo 	.probe		= sdhci_esdhc_imx_probe,
19190433c143SBill Pemberton 	.remove		= sdhci_esdhc_imx_remove,
192095f25efeSWolfram Sang };
192185d6509dSShawn Guo 
1922d1f81a64SAxel Lin module_platform_driver(sdhci_esdhc_imx_driver);
192385d6509dSShawn Guo 
192485d6509dSShawn Guo MODULE_DESCRIPTION("SDHCI driver for Freescale i.MX eSDHC");
1925035ff831SWolfram Sang MODULE_AUTHOR("Wolfram Sang <kernel@pengutronix.de>");
192685d6509dSShawn Guo MODULE_LICENSE("GPL v2");
1927