xref: /linux/drivers/mmc/host/sdhci-esdhc-imx.c (revision 9e70ff99ee871ec85e17da7c9d05cb5fcc306050)
1a6e7e407SFabio Estevam // SPDX-License-Identifier: GPL-2.0
295f25efeSWolfram Sang /*
395f25efeSWolfram Sang  * Freescale eSDHC i.MX controller driver for the platform bus.
495f25efeSWolfram Sang  *
595f25efeSWolfram Sang  * derived from the OF-version.
695f25efeSWolfram Sang  *
795f25efeSWolfram Sang  * Copyright (c) 2010 Pengutronix e.K.
8035ff831SWolfram Sang  *   Author: Wolfram Sang <kernel@pengutronix.de>
995f25efeSWolfram Sang  */
1095f25efeSWolfram Sang 
11a8e809ecSMasahiro Yamada #include <linux/bitfield.h>
1295f25efeSWolfram Sang #include <linux/io.h>
13f581e909SHaibo Chen #include <linux/iopoll.h>
1495f25efeSWolfram Sang #include <linux/delay.h>
1595f25efeSWolfram Sang #include <linux/err.h>
1695f25efeSWolfram Sang #include <linux/clk.h>
1766506f76SShawn Guo #include <linux/module.h>
18e149860dSRichard Zhu #include <linux/slab.h>
191c4989b0SBOUGH CHEN #include <linux/pm_qos.h>
2095f25efeSWolfram Sang #include <linux/mmc/host.h>
2158ac8177SRichard Zhu #include <linux/mmc/mmc.h>
2258ac8177SRichard Zhu #include <linux/mmc/sdio.h>
23fbe5fdd1SShawn Guo #include <linux/mmc/slot-gpio.h>
24abfafc2dSShawn Guo #include <linux/of.h>
25abfafc2dSShawn Guo #include <linux/of_device.h>
26e62d8b8fSDong Aisheng #include <linux/pinctrl/consumer.h>
2782906b13SArnd Bergmann #include <linux/platform_data/mmc-esdhc-imx.h>
2889d7e5c1SDong Aisheng #include <linux/pm_runtime.h>
2995f25efeSWolfram Sang #include "sdhci-pltfm.h"
3095f25efeSWolfram Sang #include "sdhci-esdhc.h"
31bb6e3581SBOUGH CHEN #include "cqhci.h"
3295f25efeSWolfram Sang 
33a215186dSHaibo Chen #define ESDHC_SYS_CTRL_DTOCV_MASK	0x0f
3460bf6396SShawn Guo #define	ESDHC_CTRL_D3CD			0x08
35fd44954eSHaibo Chen #define ESDHC_BURST_LEN_EN_INCR		(1 << 27)
3658ac8177SRichard Zhu /* VENDOR SPEC register */
3760bf6396SShawn Guo #define ESDHC_VENDOR_SPEC		0xc0
3860bf6396SShawn Guo #define  ESDHC_VENDOR_SPEC_SDIO_QUIRK	(1 << 1)
390322191eSDong Aisheng #define  ESDHC_VENDOR_SPEC_VSELECT	(1 << 1)
40fed2f6e2SDong Aisheng #define  ESDHC_VENDOR_SPEC_FRC_SDCLK_ON	(1 << 8)
413722c74cSHaibo Chen #define ESDHC_DEBUG_SEL_AND_STATUS_REG		0xc2
423722c74cSHaibo Chen #define ESDHC_DEBUG_SEL_REG			0xc3
433722c74cSHaibo Chen #define ESDHC_DEBUG_SEL_MASK			0xf
443722c74cSHaibo Chen #define ESDHC_DEBUG_SEL_CMD_STATE		1
453722c74cSHaibo Chen #define ESDHC_DEBUG_SEL_DATA_STATE		2
463722c74cSHaibo Chen #define ESDHC_DEBUG_SEL_TRANS_STATE		3
473722c74cSHaibo Chen #define ESDHC_DEBUG_SEL_DMA_STATE		4
483722c74cSHaibo Chen #define ESDHC_DEBUG_SEL_ADMA_STATE		5
493722c74cSHaibo Chen #define ESDHC_DEBUG_SEL_FIFO_STATE		6
503722c74cSHaibo Chen #define ESDHC_DEBUG_SEL_ASYNC_FIFO_STATE	7
5160bf6396SShawn Guo #define ESDHC_WTMK_LVL			0x44
52cc17e129SDong Aisheng #define  ESDHC_WTMK_DEFAULT_VAL		0x10401040
533fbd4322SAndrew Gabbasov #define  ESDHC_WTMK_LVL_RD_WML_MASK	0x000000FF
543fbd4322SAndrew Gabbasov #define  ESDHC_WTMK_LVL_RD_WML_SHIFT	0
553fbd4322SAndrew Gabbasov #define  ESDHC_WTMK_LVL_WR_WML_MASK	0x00FF0000
563fbd4322SAndrew Gabbasov #define  ESDHC_WTMK_LVL_WR_WML_SHIFT	16
573fbd4322SAndrew Gabbasov #define  ESDHC_WTMK_LVL_WML_VAL_DEF	64
583fbd4322SAndrew Gabbasov #define  ESDHC_WTMK_LVL_WML_VAL_MAX	128
5960bf6396SShawn Guo #define ESDHC_MIX_CTRL			0x48
60de5bdbffSDong Aisheng #define  ESDHC_MIX_CTRL_DDREN		(1 << 3)
612a15f981SShawn Guo #define  ESDHC_MIX_CTRL_AC23EN		(1 << 7)
620322191eSDong Aisheng #define  ESDHC_MIX_CTRL_EXE_TUNE	(1 << 22)
630322191eSDong Aisheng #define  ESDHC_MIX_CTRL_SMPCLK_SEL	(1 << 23)
640b330e38SDong Aisheng #define  ESDHC_MIX_CTRL_AUTO_TUNE_EN	(1 << 24)
650322191eSDong Aisheng #define  ESDHC_MIX_CTRL_FBCLK_SEL	(1 << 25)
6628b07674SHaibo Chen #define  ESDHC_MIX_CTRL_HS400_EN	(1 << 26)
67029e2476SBOUGH CHEN #define  ESDHC_MIX_CTRL_HS400_ES_EN	(1 << 27)
682a15f981SShawn Guo /* Bits 3 and 6 are not SDHCI standard definitions */
692a15f981SShawn Guo #define  ESDHC_MIX_CTRL_SDHCI_MASK	0xb7
70d131a71cSDong Aisheng /* Tuning bits */
71d131a71cSDong Aisheng #define  ESDHC_MIX_CTRL_TUNING_MASK	0x03c00000
7258ac8177SRichard Zhu 
73602519b2SDong Aisheng /* dll control register */
74602519b2SDong Aisheng #define ESDHC_DLL_CTRL			0x60
75602519b2SDong Aisheng #define ESDHC_DLL_OVERRIDE_VAL_SHIFT	9
76602519b2SDong Aisheng #define ESDHC_DLL_OVERRIDE_EN_SHIFT	8
77602519b2SDong Aisheng 
780322191eSDong Aisheng /* tune control register */
790322191eSDong Aisheng #define ESDHC_TUNE_CTRL_STATUS		0x68
800322191eSDong Aisheng #define  ESDHC_TUNE_CTRL_STEP		1
810322191eSDong Aisheng #define  ESDHC_TUNE_CTRL_MIN		0
820322191eSDong Aisheng #define  ESDHC_TUNE_CTRL_MAX		((1 << 7) - 1)
830322191eSDong Aisheng 
8428b07674SHaibo Chen /* strobe dll register */
8528b07674SHaibo Chen #define ESDHC_STROBE_DLL_CTRL		0x70
8628b07674SHaibo Chen #define ESDHC_STROBE_DLL_CTRL_ENABLE	(1 << 0)
8728b07674SHaibo Chen #define ESDHC_STROBE_DLL_CTRL_RESET	(1 << 1)
885bd2acdcSHaibo Chen #define ESDHC_STROBE_DLL_CTRL_SLV_DLY_TARGET_DEFAULT	0x7
8928b07674SHaibo Chen #define ESDHC_STROBE_DLL_CTRL_SLV_DLY_TARGET_SHIFT	3
902eaf5a53SBOUGH CHEN #define ESDHC_STROBE_DLL_CTRL_SLV_UPDATE_INT_DEFAULT	(4 << 20)
9128b07674SHaibo Chen 
9228b07674SHaibo Chen #define ESDHC_STROBE_DLL_STATUS		0x74
9328b07674SHaibo Chen #define ESDHC_STROBE_DLL_STS_REF_LOCK	(1 << 1)
9428b07674SHaibo Chen #define ESDHC_STROBE_DLL_STS_SLV_LOCK	0x1
9528b07674SHaibo Chen 
96bcdb5301SBOUGH CHEN #define ESDHC_VEND_SPEC2		0xc8
97bcdb5301SBOUGH CHEN #define ESDHC_VEND_SPEC2_EN_BUSY_IRQ	(1 << 8)
98bcdb5301SBOUGH CHEN 
996e9fd28eSDong Aisheng #define ESDHC_TUNING_CTRL		0xcc
1006e9fd28eSDong Aisheng #define ESDHC_STD_TUNING_EN		(1 << 24)
1016e9fd28eSDong Aisheng /* NOTE: the minimum valid tuning start tap for mx6sl is 1 */
102d87fc966SDong Aisheng #define ESDHC_TUNING_START_TAP_DEFAULT	0x1
1031194be8cSHaibo Chen #define ESDHC_TUNING_START_TAP_MASK	0x7f
10416e40e5bSHaibo Chen #define ESDHC_TUNING_CMD_CRC_CHECK_DISABLE	(1 << 7)
105260ecb3cSHaibo Chen #define ESDHC_TUNING_STEP_MASK		0x00070000
106d407e30bSHaibo Chen #define ESDHC_TUNING_STEP_SHIFT		16
1076e9fd28eSDong Aisheng 
108ad93220dSDong Aisheng /* pinctrl state */
109ad93220dSDong Aisheng #define ESDHC_PINCTRL_STATE_100MHZ	"state_100mhz"
110ad93220dSDong Aisheng #define ESDHC_PINCTRL_STATE_200MHZ	"state_200mhz"
111ad93220dSDong Aisheng 
11258ac8177SRichard Zhu /*
113af51079eSSascha Hauer  * Our interpretation of the SDHCI_HOST_CONTROL register
114af51079eSSascha Hauer  */
115af51079eSSascha Hauer #define ESDHC_CTRL_4BITBUS		(0x1 << 1)
116af51079eSSascha Hauer #define ESDHC_CTRL_8BITBUS		(0x2 << 1)
117af51079eSSascha Hauer #define ESDHC_CTRL_BUSWIDTH_MASK	(0x3 << 1)
118af51079eSSascha Hauer 
119af51079eSSascha Hauer /*
120d04f8d5bSBenoît Thébaudeau  * There is an INT DMA ERR mismatch between eSDHC and STD SDHC SPEC:
12197e4ba6aSRichard Zhu  * Bit25 is used in STD SPEC, and is reserved in fsl eSDHC design,
12297e4ba6aSRichard Zhu  * but bit28 is used as the INT DMA ERR in fsl eSDHC design.
12397e4ba6aSRichard Zhu  * Define this macro DMA error INT for fsl eSDHC
12497e4ba6aSRichard Zhu  */
12560bf6396SShawn Guo #define ESDHC_INT_VENDOR_SPEC_DMA_ERR	(1 << 28)
12697e4ba6aSRichard Zhu 
127bb6e3581SBOUGH CHEN /* the address offset of CQHCI */
128bb6e3581SBOUGH CHEN #define ESDHC_CQHCI_ADDR_OFFSET		0x100
129bb6e3581SBOUGH CHEN 
13097e4ba6aSRichard Zhu /*
13158ac8177SRichard Zhu  * The CMDTYPE of the CMD register (offset 0xE) should be set to
13258ac8177SRichard Zhu  * "11" when the STOP CMD12 is issued on imx53 to abort one
13358ac8177SRichard Zhu  * open ended multi-blk IO. Otherwise the TC INT wouldn't
13458ac8177SRichard Zhu  * be generated.
13558ac8177SRichard Zhu  * In exact block transfer, the controller doesn't complete the
13658ac8177SRichard Zhu  * operations automatically as required at the end of the
13758ac8177SRichard Zhu  * transfer and remains on hold if the abort command is not sent.
13858ac8177SRichard Zhu  * As a result, the TC flag is not asserted and SW received timeout
139d04f8d5bSBenoît Thébaudeau  * exception. Bit1 of Vendor Spec register is used to fix it.
14058ac8177SRichard Zhu  */
14131fbb301SShawn Guo #define ESDHC_FLAG_MULTIBLK_NO_INT	BIT(1)
14231fbb301SShawn Guo /*
1439d61c009SShawn Guo  * The flag tells that the ESDHC controller is an USDHC block that is
1449d61c009SShawn Guo  * integrated on the i.MX6 series.
1459d61c009SShawn Guo  */
1469d61c009SShawn Guo #define ESDHC_FLAG_USDHC		BIT(3)
1476e9fd28eSDong Aisheng /* The IP supports manual tuning process */
1486e9fd28eSDong Aisheng #define ESDHC_FLAG_MAN_TUNING		BIT(4)
1496e9fd28eSDong Aisheng /* The IP supports standard tuning process */
1506e9fd28eSDong Aisheng #define ESDHC_FLAG_STD_TUNING		BIT(5)
1516e9fd28eSDong Aisheng /* The IP has SDHCI_CAPABILITIES_1 register */
1526e9fd28eSDong Aisheng #define ESDHC_FLAG_HAVE_CAP1		BIT(6)
15318094430SDong Aisheng /*
154d04f8d5bSBenoît Thébaudeau  * The IP has erratum ERR004536
15518094430SDong Aisheng  * uSDHC: ADMA Length Mismatch Error occurs if the AHB read access is slow,
15618094430SDong Aisheng  * when reading data from the card
157667123f6SBenoît Thébaudeau  * This flag is also set for i.MX25 and i.MX35 in order to get
158667123f6SBenoît Thébaudeau  * SDHCI_QUIRK_BROKEN_ADMA, but for different reasons (ADMA capability bits).
15918094430SDong Aisheng  */
16018094430SDong Aisheng #define ESDHC_FLAG_ERR004536		BIT(7)
1614245afffSDong Aisheng /* The IP supports HS200 mode */
1624245afffSDong Aisheng #define ESDHC_FLAG_HS200		BIT(8)
16328b07674SHaibo Chen /* The IP supports HS400 mode */
16428b07674SHaibo Chen #define ESDHC_FLAG_HS400		BIT(9)
165af6a50d4SBOUGH CHEN /*
166af6a50d4SBOUGH CHEN  * The IP has errata ERR010450
167af6a50d4SBOUGH CHEN  * uSDHC: Due to the I/O timing limit, for SDR mode, SD card clock can't
168af6a50d4SBOUGH CHEN  * exceed 150MHz, for DDR mode, SD card clock can't exceed 45MHz.
169af6a50d4SBOUGH CHEN  */
170af6a50d4SBOUGH CHEN #define ESDHC_FLAG_ERR010450		BIT(10)
171029e2476SBOUGH CHEN /* The IP supports HS400ES mode */
172029e2476SBOUGH CHEN #define ESDHC_FLAG_HS400_ES		BIT(11)
173bb6e3581SBOUGH CHEN /* The IP has Host Controller Interface for Command Queuing */
174bb6e3581SBOUGH CHEN #define ESDHC_FLAG_CQHCI		BIT(12)
1751c4989b0SBOUGH CHEN /* need request pmqos during low power */
1761c4989b0SBOUGH CHEN #define ESDHC_FLAG_PMQOS		BIT(13)
177a26a4f1bSHaibo Chen /* The IP state got lost in low power mode */
178a26a4f1bSHaibo Chen #define ESDHC_FLAG_STATE_LOST_IN_LPMODE		BIT(14)
1795c11f1ffSHaibo Chen /* The IP lost clock rate in PM_RUNTIME */
1805c11f1ffSHaibo Chen #define ESDHC_FLAG_CLK_RATE_LOST_IN_PM_RUNTIME	BIT(15)
18174898cbcSHaibo Chen /*
18274898cbcSHaibo Chen  * The IP do not support the ACMD23 feature completely when use ADMA mode.
18374898cbcSHaibo Chen  * In ADMA mode, it only use the 16 bit block count of the register 0x4
18474898cbcSHaibo Chen  * (BLOCK_ATT) as the CMD23's argument for ACMD23 mode, which means it will
18574898cbcSHaibo Chen  * ignore the upper 16 bit of the CMD23's argument. This will block the reliable
18674898cbcSHaibo Chen  * write operation in RPMB, because RPMB reliable write need to set the bit31
18774898cbcSHaibo Chen  * of the CMD23's argument.
18874898cbcSHaibo Chen  * imx6qpdl/imx6sx/imx6sl/imx7d has this limitation only for ADMA mode, SDMA
18974898cbcSHaibo Chen  * do not has this limitation. so when these SoC use ADMA mode, it need to
19074898cbcSHaibo Chen  * disable the ACMD23 feature.
19174898cbcSHaibo Chen  */
19274898cbcSHaibo Chen #define ESDHC_FLAG_BROKEN_AUTO_CMD23	BIT(16)
193e149860dSRichard Zhu 
194f47c4bbfSShawn Guo struct esdhc_soc_data {
195f47c4bbfSShawn Guo 	u32 flags;
196f47c4bbfSShawn Guo };
197f47c4bbfSShawn Guo 
1984f100012SAndrey Smirnov static const struct esdhc_soc_data esdhc_imx25_data = {
199667123f6SBenoît Thébaudeau 	.flags = ESDHC_FLAG_ERR004536,
200f47c4bbfSShawn Guo };
201f47c4bbfSShawn Guo 
2024f100012SAndrey Smirnov static const struct esdhc_soc_data esdhc_imx35_data = {
203667123f6SBenoît Thébaudeau 	.flags = ESDHC_FLAG_ERR004536,
204f47c4bbfSShawn Guo };
205f47c4bbfSShawn Guo 
2064f100012SAndrey Smirnov static const struct esdhc_soc_data esdhc_imx51_data = {
207f47c4bbfSShawn Guo 	.flags = 0,
208f47c4bbfSShawn Guo };
209f47c4bbfSShawn Guo 
2104f100012SAndrey Smirnov static const struct esdhc_soc_data esdhc_imx53_data = {
211f47c4bbfSShawn Guo 	.flags = ESDHC_FLAG_MULTIBLK_NO_INT,
212f47c4bbfSShawn Guo };
213f47c4bbfSShawn Guo 
2144f100012SAndrey Smirnov static const struct esdhc_soc_data usdhc_imx6q_data = {
21574898cbcSHaibo Chen 	.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_MAN_TUNING
21674898cbcSHaibo Chen 			| ESDHC_FLAG_BROKEN_AUTO_CMD23,
2176e9fd28eSDong Aisheng };
2186e9fd28eSDong Aisheng 
2194f100012SAndrey Smirnov static const struct esdhc_soc_data usdhc_imx6sl_data = {
2206e9fd28eSDong Aisheng 	.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
2214245afffSDong Aisheng 			| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_ERR004536
22274898cbcSHaibo Chen 			| ESDHC_FLAG_HS200
22374898cbcSHaibo Chen 			| ESDHC_FLAG_BROKEN_AUTO_CMD23,
22474898cbcSHaibo Chen };
22574898cbcSHaibo Chen 
22674898cbcSHaibo Chen static const struct esdhc_soc_data usdhc_imx6sll_data = {
22774898cbcSHaibo Chen 	.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
22874898cbcSHaibo Chen 			| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
22986b59671SHaibo Chen 			| ESDHC_FLAG_HS400
23074898cbcSHaibo Chen 			| ESDHC_FLAG_STATE_LOST_IN_LPMODE,
23157ed3314SShawn Guo };
23257ed3314SShawn Guo 
2334f100012SAndrey Smirnov static const struct esdhc_soc_data usdhc_imx6sx_data = {
234913d4951SDong Aisheng 	.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
235a26a4f1bSHaibo Chen 			| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
23674898cbcSHaibo Chen 			| ESDHC_FLAG_STATE_LOST_IN_LPMODE
23774898cbcSHaibo Chen 			| ESDHC_FLAG_BROKEN_AUTO_CMD23,
238913d4951SDong Aisheng };
239913d4951SDong Aisheng 
240af6a50d4SBOUGH CHEN static const struct esdhc_soc_data usdhc_imx6ull_data = {
241af6a50d4SBOUGH CHEN 	.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
242af6a50d4SBOUGH CHEN 			| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
243a26a4f1bSHaibo Chen 			| ESDHC_FLAG_ERR010450
244a26a4f1bSHaibo Chen 			| ESDHC_FLAG_STATE_LOST_IN_LPMODE,
245af6a50d4SBOUGH CHEN };
246af6a50d4SBOUGH CHEN 
2474f100012SAndrey Smirnov static const struct esdhc_soc_data usdhc_imx7d_data = {
24828b07674SHaibo Chen 	.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
24928b07674SHaibo Chen 			| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
250a26a4f1bSHaibo Chen 			| ESDHC_FLAG_HS400
25174898cbcSHaibo Chen 			| ESDHC_FLAG_STATE_LOST_IN_LPMODE
25274898cbcSHaibo Chen 			| ESDHC_FLAG_BROKEN_AUTO_CMD23,
25328b07674SHaibo Chen };
25428b07674SHaibo Chen 
2551c4989b0SBOUGH CHEN static struct esdhc_soc_data usdhc_imx7ulp_data = {
2561c4989b0SBOUGH CHEN 	.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
2571c4989b0SBOUGH CHEN 			| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
258a26a4f1bSHaibo Chen 			| ESDHC_FLAG_PMQOS | ESDHC_FLAG_HS400
259a26a4f1bSHaibo Chen 			| ESDHC_FLAG_STATE_LOST_IN_LPMODE,
2601c4989b0SBOUGH CHEN };
2611c4989b0SBOUGH CHEN 
262029e2476SBOUGH CHEN static struct esdhc_soc_data usdhc_imx8qxp_data = {
263029e2476SBOUGH CHEN 	.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
264029e2476SBOUGH CHEN 			| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
265bb6e3581SBOUGH CHEN 			| ESDHC_FLAG_HS400 | ESDHC_FLAG_HS400_ES
266a26a4f1bSHaibo Chen 			| ESDHC_FLAG_CQHCI
2675c11f1ffSHaibo Chen 			| ESDHC_FLAG_STATE_LOST_IN_LPMODE
2685c11f1ffSHaibo Chen 			| ESDHC_FLAG_CLK_RATE_LOST_IN_PM_RUNTIME,
269029e2476SBOUGH CHEN };
270029e2476SBOUGH CHEN 
271cde5e8e9SHaibo Chen static struct esdhc_soc_data usdhc_imx8mm_data = {
272cde5e8e9SHaibo Chen 	.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
273cde5e8e9SHaibo Chen 			| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
274cde5e8e9SHaibo Chen 			| ESDHC_FLAG_HS400 | ESDHC_FLAG_HS400_ES
275cde5e8e9SHaibo Chen 			| ESDHC_FLAG_CQHCI
276cde5e8e9SHaibo Chen 			| ESDHC_FLAG_STATE_LOST_IN_LPMODE,
277e149860dSRichard Zhu };
278e149860dSRichard Zhu 
279e149860dSRichard Zhu struct pltfm_imx_data {
280e149860dSRichard Zhu 	u32 scratchpad;
281e62d8b8fSDong Aisheng 	struct pinctrl *pinctrl;
282ad93220dSDong Aisheng 	struct pinctrl_state *pins_100mhz;
283ad93220dSDong Aisheng 	struct pinctrl_state *pins_200mhz;
284f47c4bbfSShawn Guo 	const struct esdhc_soc_data *socdata;
285842afc02SShawn Guo 	struct esdhc_platform_data boarddata;
28652dac615SSascha Hauer 	struct clk *clk_ipg;
28752dac615SSascha Hauer 	struct clk *clk_ahb;
28852dac615SSascha Hauer 	struct clk *clk_per;
2893602785bSMichael Trimarchi 	unsigned int actual_clock;
290361b8482SLucas Stach 	enum {
291361b8482SLucas Stach 		NO_CMD_PENDING,      /* no multiblock command pending */
292361b8482SLucas Stach 		MULTIBLK_IN_PROCESS, /* exact multiblock cmd in process */
293361b8482SLucas Stach 		WAIT_FOR_INT,        /* sent CMD12, waiting for response INT */
294361b8482SLucas Stach 	} multiblock_status;
295de5bdbffSDong Aisheng 	u32 is_ddr;
2961c4989b0SBOUGH CHEN 	struct pm_qos_request pm_qos_req;
297e149860dSRichard Zhu };
298e149860dSRichard Zhu 
299f8cbf461SKrzysztof Kozlowski static const struct platform_device_id imx_esdhc_devtype[] = {
30057ed3314SShawn Guo 	{
30157ed3314SShawn Guo 		.name = "sdhci-esdhc-imx25",
302f47c4bbfSShawn Guo 		.driver_data = (kernel_ulong_t) &esdhc_imx25_data,
30357ed3314SShawn Guo 	}, {
30457ed3314SShawn Guo 		.name = "sdhci-esdhc-imx35",
305f47c4bbfSShawn Guo 		.driver_data = (kernel_ulong_t) &esdhc_imx35_data,
30657ed3314SShawn Guo 	}, {
30757ed3314SShawn Guo 		.name = "sdhci-esdhc-imx51",
308f47c4bbfSShawn Guo 		.driver_data = (kernel_ulong_t) &esdhc_imx51_data,
30957ed3314SShawn Guo 	}, {
31057ed3314SShawn Guo 		/* sentinel */
31157ed3314SShawn Guo 	}
31257ed3314SShawn Guo };
31357ed3314SShawn Guo MODULE_DEVICE_TABLE(platform, imx_esdhc_devtype);
31457ed3314SShawn Guo 
315abfafc2dSShawn Guo static const struct of_device_id imx_esdhc_dt_ids[] = {
316f47c4bbfSShawn Guo 	{ .compatible = "fsl,imx25-esdhc", .data = &esdhc_imx25_data, },
317f47c4bbfSShawn Guo 	{ .compatible = "fsl,imx35-esdhc", .data = &esdhc_imx35_data, },
318f47c4bbfSShawn Guo 	{ .compatible = "fsl,imx51-esdhc", .data = &esdhc_imx51_data, },
319f47c4bbfSShawn Guo 	{ .compatible = "fsl,imx53-esdhc", .data = &esdhc_imx53_data, },
320913d4951SDong Aisheng 	{ .compatible = "fsl,imx6sx-usdhc", .data = &usdhc_imx6sx_data, },
3216e9fd28eSDong Aisheng 	{ .compatible = "fsl,imx6sl-usdhc", .data = &usdhc_imx6sl_data, },
32274898cbcSHaibo Chen 	{ .compatible = "fsl,imx6sll-usdhc", .data = &usdhc_imx6sll_data, },
323f47c4bbfSShawn Guo 	{ .compatible = "fsl,imx6q-usdhc", .data = &usdhc_imx6q_data, },
324af6a50d4SBOUGH CHEN 	{ .compatible = "fsl,imx6ull-usdhc", .data = &usdhc_imx6ull_data, },
32528b07674SHaibo Chen 	{ .compatible = "fsl,imx7d-usdhc", .data = &usdhc_imx7d_data, },
3261c4989b0SBOUGH CHEN 	{ .compatible = "fsl,imx7ulp-usdhc", .data = &usdhc_imx7ulp_data, },
327029e2476SBOUGH CHEN 	{ .compatible = "fsl,imx8qxp-usdhc", .data = &usdhc_imx8qxp_data, },
328cde5e8e9SHaibo Chen 	{ .compatible = "fsl,imx8mm-usdhc", .data = &usdhc_imx8mm_data, },
329abfafc2dSShawn Guo 	{ /* sentinel */ }
330abfafc2dSShawn Guo };
331abfafc2dSShawn Guo MODULE_DEVICE_TABLE(of, imx_esdhc_dt_ids);
332abfafc2dSShawn Guo 
33357ed3314SShawn Guo static inline int is_imx25_esdhc(struct pltfm_imx_data *data)
33457ed3314SShawn Guo {
335f47c4bbfSShawn Guo 	return data->socdata == &esdhc_imx25_data;
33657ed3314SShawn Guo }
33757ed3314SShawn Guo 
33857ed3314SShawn Guo static inline int is_imx53_esdhc(struct pltfm_imx_data *data)
33957ed3314SShawn Guo {
340f47c4bbfSShawn Guo 	return data->socdata == &esdhc_imx53_data;
34157ed3314SShawn Guo }
34257ed3314SShawn Guo 
34395a2482aSShawn Guo static inline int is_imx6q_usdhc(struct pltfm_imx_data *data)
34495a2482aSShawn Guo {
345f47c4bbfSShawn Guo 	return data->socdata == &usdhc_imx6q_data;
34695a2482aSShawn Guo }
34795a2482aSShawn Guo 
3489d61c009SShawn Guo static inline int esdhc_is_usdhc(struct pltfm_imx_data *data)
3499d61c009SShawn Guo {
350f47c4bbfSShawn Guo 	return !!(data->socdata->flags & ESDHC_FLAG_USDHC);
3519d61c009SShawn Guo }
3529d61c009SShawn Guo 
35395f25efeSWolfram Sang static inline void esdhc_clrset_le(struct sdhci_host *host, u32 mask, u32 val, int reg)
35495f25efeSWolfram Sang {
35595f25efeSWolfram Sang 	void __iomem *base = host->ioaddr + (reg & ~0x3);
35695f25efeSWolfram Sang 	u32 shift = (reg & 0x3) * 8;
35795f25efeSWolfram Sang 
35895f25efeSWolfram Sang 	writel(((readl(base) & ~(mask << shift)) | (val << shift)), base);
35995f25efeSWolfram Sang }
36095f25efeSWolfram Sang 
3613722c74cSHaibo Chen #define DRIVER_NAME "sdhci-esdhc-imx"
3623722c74cSHaibo Chen #define ESDHC_IMX_DUMP(f, x...) \
3633722c74cSHaibo Chen 	pr_err("%s: " DRIVER_NAME ": " f, mmc_hostname(host->mmc), ## x)
3643722c74cSHaibo Chen static void esdhc_dump_debug_regs(struct sdhci_host *host)
3653722c74cSHaibo Chen {
3663722c74cSHaibo Chen 	int i;
3673722c74cSHaibo Chen 	char *debug_status[7] = {
3683722c74cSHaibo Chen 				 "cmd debug status",
3693722c74cSHaibo Chen 				 "data debug status",
3703722c74cSHaibo Chen 				 "trans debug status",
3713722c74cSHaibo Chen 				 "dma debug status",
3723722c74cSHaibo Chen 				 "adma debug status",
3733722c74cSHaibo Chen 				 "fifo debug status",
3743722c74cSHaibo Chen 				 "async fifo debug status"
3753722c74cSHaibo Chen 	};
3763722c74cSHaibo Chen 
3773722c74cSHaibo Chen 	ESDHC_IMX_DUMP("========= ESDHC IMX DEBUG STATUS DUMP =========\n");
3783722c74cSHaibo Chen 	for (i = 0; i < 7; i++) {
3793722c74cSHaibo Chen 		esdhc_clrset_le(host, ESDHC_DEBUG_SEL_MASK,
3803722c74cSHaibo Chen 			ESDHC_DEBUG_SEL_CMD_STATE + i, ESDHC_DEBUG_SEL_REG);
3813722c74cSHaibo Chen 		ESDHC_IMX_DUMP("%s:  0x%04x\n", debug_status[i],
3823722c74cSHaibo Chen 			readw(host->ioaddr + ESDHC_DEBUG_SEL_AND_STATUS_REG));
3833722c74cSHaibo Chen 	}
3843722c74cSHaibo Chen 
3853722c74cSHaibo Chen 	esdhc_clrset_le(host, ESDHC_DEBUG_SEL_MASK, 0, ESDHC_DEBUG_SEL_REG);
3863722c74cSHaibo Chen 
3873722c74cSHaibo Chen }
3883722c74cSHaibo Chen 
389f581e909SHaibo Chen static inline void esdhc_wait_for_card_clock_gate_off(struct sdhci_host *host)
390f581e909SHaibo Chen {
391f581e909SHaibo Chen 	u32 present_state;
392f581e909SHaibo Chen 	int ret;
393f581e909SHaibo Chen 
394f581e909SHaibo Chen 	ret = readl_poll_timeout(host->ioaddr + ESDHC_PRSSTAT, present_state,
395f581e909SHaibo Chen 				(present_state & ESDHC_CLOCK_GATE_OFF), 2, 100);
396f581e909SHaibo Chen 	if (ret == -ETIMEDOUT)
397f581e909SHaibo Chen 		dev_warn(mmc_dev(host->mmc), "%s: card clock still not gate off in 100us!.\n", __func__);
398f581e909SHaibo Chen }
399f581e909SHaibo Chen 
4007e29c306SWolfram Sang static u32 esdhc_readl_le(struct sdhci_host *host, int reg)
4017e29c306SWolfram Sang {
402361b8482SLucas Stach 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
403070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
404913413c3SShawn Guo 	u32 val = readl(host->ioaddr + reg);
405913413c3SShawn Guo 
4060322191eSDong Aisheng 	if (unlikely(reg == SDHCI_PRESENT_STATE)) {
4070322191eSDong Aisheng 		u32 fsl_prss = val;
4080322191eSDong Aisheng 		/* save the least 20 bits */
4090322191eSDong Aisheng 		val = fsl_prss & 0x000FFFFF;
4100322191eSDong Aisheng 		/* move dat[0-3] bits */
4110322191eSDong Aisheng 		val |= (fsl_prss & 0x0F000000) >> 4;
4120322191eSDong Aisheng 		/* move cmd line bit */
4130322191eSDong Aisheng 		val |= (fsl_prss & 0x00800000) << 1;
4140322191eSDong Aisheng 	}
4150322191eSDong Aisheng 
41697e4ba6aSRichard Zhu 	if (unlikely(reg == SDHCI_CAPABILITIES)) {
4176b4fb671SDong Aisheng 		/* ignore bit[0-15] as it stores cap_1 register val for mx6sl */
4186b4fb671SDong Aisheng 		if (imx_data->socdata->flags & ESDHC_FLAG_HAVE_CAP1)
4196b4fb671SDong Aisheng 			val &= 0xffff0000;
4206b4fb671SDong Aisheng 
42197e4ba6aSRichard Zhu 		/* In FSL esdhc IC module, only bit20 is used to indicate the
42297e4ba6aSRichard Zhu 		 * ADMA2 capability of esdhc, but this bit is messed up on
42397e4ba6aSRichard Zhu 		 * some SOCs (e.g. on MX25, MX35 this bit is set, but they
42497e4ba6aSRichard Zhu 		 * don't actually support ADMA2). So set the BROKEN_ADMA
425d04f8d5bSBenoît Thébaudeau 		 * quirk on MX25/35 platforms.
42697e4ba6aSRichard Zhu 		 */
42797e4ba6aSRichard Zhu 
42897e4ba6aSRichard Zhu 		if (val & SDHCI_CAN_DO_ADMA1) {
42997e4ba6aSRichard Zhu 			val &= ~SDHCI_CAN_DO_ADMA1;
43097e4ba6aSRichard Zhu 			val |= SDHCI_CAN_DO_ADMA2;
43197e4ba6aSRichard Zhu 		}
43297e4ba6aSRichard Zhu 	}
43397e4ba6aSRichard Zhu 
4346e9fd28eSDong Aisheng 	if (unlikely(reg == SDHCI_CAPABILITIES_1)) {
4356e9fd28eSDong Aisheng 		if (esdhc_is_usdhc(imx_data)) {
4366e9fd28eSDong Aisheng 			if (imx_data->socdata->flags & ESDHC_FLAG_HAVE_CAP1)
4376e9fd28eSDong Aisheng 				val = readl(host->ioaddr + SDHCI_CAPABILITIES) & 0xFFFF;
4386e9fd28eSDong Aisheng 			else
4396e9fd28eSDong Aisheng 				/* imx6q/dl does not have cap_1 register, fake one */
4400322191eSDong Aisheng 				val = SDHCI_SUPPORT_DDR50 | SDHCI_SUPPORT_SDR104
441888824bbSDong Aisheng 					| SDHCI_SUPPORT_SDR50
442da0295ffSDong Aisheng 					| SDHCI_USE_SDR50_TUNING
443a8e809ecSMasahiro Yamada 					| FIELD_PREP(SDHCI_RETUNING_MODE_MASK,
444a8e809ecSMasahiro Yamada 						     SDHCI_TUNING_MODE_3);
44528b07674SHaibo Chen 
44628b07674SHaibo Chen 			if (imx_data->socdata->flags & ESDHC_FLAG_HS400)
44728b07674SHaibo Chen 				val |= SDHCI_SUPPORT_HS400;
44892748beaSStefan Agner 
44992748beaSStefan Agner 			/*
45092748beaSStefan Agner 			 * Do not advertise faster UHS modes if there are no
45192748beaSStefan Agner 			 * pinctrl states for 100MHz/200MHz.
45292748beaSStefan Agner 			 */
45392748beaSStefan Agner 			if (IS_ERR_OR_NULL(imx_data->pins_100mhz) ||
45492748beaSStefan Agner 			    IS_ERR_OR_NULL(imx_data->pins_200mhz))
45592748beaSStefan Agner 				val &= ~(SDHCI_SUPPORT_SDR50 | SDHCI_SUPPORT_DDR50
45692748beaSStefan Agner 					 | SDHCI_SUPPORT_SDR104 | SDHCI_SUPPORT_HS400);
4576e9fd28eSDong Aisheng 		}
4586e9fd28eSDong Aisheng 	}
4590322191eSDong Aisheng 
4609d61c009SShawn Guo 	if (unlikely(reg == SDHCI_MAX_CURRENT) && esdhc_is_usdhc(imx_data)) {
4610322191eSDong Aisheng 		val = 0;
462804a65b3SMasahiro Yamada 		val |= FIELD_PREP(SDHCI_MAX_CURRENT_330_MASK, 0xFF);
463804a65b3SMasahiro Yamada 		val |= FIELD_PREP(SDHCI_MAX_CURRENT_300_MASK, 0xFF);
464804a65b3SMasahiro Yamada 		val |= FIELD_PREP(SDHCI_MAX_CURRENT_180_MASK, 0xFF);
4650322191eSDong Aisheng 	}
4660322191eSDong Aisheng 
46797e4ba6aSRichard Zhu 	if (unlikely(reg == SDHCI_INT_STATUS)) {
46860bf6396SShawn Guo 		if (val & ESDHC_INT_VENDOR_SPEC_DMA_ERR) {
46960bf6396SShawn Guo 			val &= ~ESDHC_INT_VENDOR_SPEC_DMA_ERR;
47097e4ba6aSRichard Zhu 			val |= SDHCI_INT_ADMA_ERROR;
47197e4ba6aSRichard Zhu 		}
472361b8482SLucas Stach 
473361b8482SLucas Stach 		/*
474361b8482SLucas Stach 		 * mask off the interrupt we get in response to the manually
475361b8482SLucas Stach 		 * sent CMD12
476361b8482SLucas Stach 		 */
477361b8482SLucas Stach 		if ((imx_data->multiblock_status == WAIT_FOR_INT) &&
478361b8482SLucas Stach 		    ((val & SDHCI_INT_RESPONSE) == SDHCI_INT_RESPONSE)) {
479361b8482SLucas Stach 			val &= ~SDHCI_INT_RESPONSE;
480361b8482SLucas Stach 			writel(SDHCI_INT_RESPONSE, host->ioaddr +
481361b8482SLucas Stach 						   SDHCI_INT_STATUS);
482361b8482SLucas Stach 			imx_data->multiblock_status = NO_CMD_PENDING;
483361b8482SLucas Stach 		}
48497e4ba6aSRichard Zhu 	}
48597e4ba6aSRichard Zhu 
4867e29c306SWolfram Sang 	return val;
4877e29c306SWolfram Sang }
4887e29c306SWolfram Sang 
4897e29c306SWolfram Sang static void esdhc_writel_le(struct sdhci_host *host, u32 val, int reg)
4907e29c306SWolfram Sang {
491e149860dSRichard Zhu 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
492070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
4930d58864bSTony Lin 	u32 data;
494e149860dSRichard Zhu 
49577da3da0SAaron Brice 	if (unlikely(reg == SDHCI_INT_ENABLE || reg == SDHCI_SIGNAL_ENABLE ||
49677da3da0SAaron Brice 			reg == SDHCI_INT_STATUS)) {
497b7321042SDong Aisheng 		if ((val & SDHCI_INT_CARD_INT) && !esdhc_is_usdhc(imx_data)) {
4980d58864bSTony Lin 			/*
4990d58864bSTony Lin 			 * Clear and then set D3CD bit to avoid missing the
500d04f8d5bSBenoît Thébaudeau 			 * card interrupt. This is an eSDHC controller problem
5010d58864bSTony Lin 			 * so we need to apply the following workaround: clear
5020d58864bSTony Lin 			 * and set D3CD bit will make eSDHC re-sample the card
5030d58864bSTony Lin 			 * interrupt. In case a card interrupt was lost,
5040d58864bSTony Lin 			 * re-sample it by the following steps.
5050d58864bSTony Lin 			 */
5060d58864bSTony Lin 			data = readl(host->ioaddr + SDHCI_HOST_CONTROL);
50760bf6396SShawn Guo 			data &= ~ESDHC_CTRL_D3CD;
5080d58864bSTony Lin 			writel(data, host->ioaddr + SDHCI_HOST_CONTROL);
50960bf6396SShawn Guo 			data |= ESDHC_CTRL_D3CD;
5100d58864bSTony Lin 			writel(data, host->ioaddr + SDHCI_HOST_CONTROL);
5110d58864bSTony Lin 		}
512915be485SDong Aisheng 
513915be485SDong Aisheng 		if (val & SDHCI_INT_ADMA_ERROR) {
514915be485SDong Aisheng 			val &= ~SDHCI_INT_ADMA_ERROR;
515915be485SDong Aisheng 			val |= ESDHC_INT_VENDOR_SPEC_DMA_ERR;
516915be485SDong Aisheng 		}
5170d58864bSTony Lin 	}
5180d58864bSTony Lin 
519f47c4bbfSShawn Guo 	if (unlikely((imx_data->socdata->flags & ESDHC_FLAG_MULTIBLK_NO_INT)
52058ac8177SRichard Zhu 				&& (reg == SDHCI_INT_STATUS)
52158ac8177SRichard Zhu 				&& (val & SDHCI_INT_DATA_END))) {
52258ac8177SRichard Zhu 			u32 v;
52360bf6396SShawn Guo 			v = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
52460bf6396SShawn Guo 			v &= ~ESDHC_VENDOR_SPEC_SDIO_QUIRK;
52560bf6396SShawn Guo 			writel(v, host->ioaddr + ESDHC_VENDOR_SPEC);
526361b8482SLucas Stach 
527361b8482SLucas Stach 			if (imx_data->multiblock_status == MULTIBLK_IN_PROCESS)
528361b8482SLucas Stach 			{
529361b8482SLucas Stach 				/* send a manual CMD12 with RESPTYP=none */
530361b8482SLucas Stach 				data = MMC_STOP_TRANSMISSION << 24 |
531361b8482SLucas Stach 				       SDHCI_CMD_ABORTCMD << 16;
532361b8482SLucas Stach 				writel(data, host->ioaddr + SDHCI_TRANSFER_MODE);
533361b8482SLucas Stach 				imx_data->multiblock_status = WAIT_FOR_INT;
534361b8482SLucas Stach 			}
53558ac8177SRichard Zhu 	}
53658ac8177SRichard Zhu 
5377e29c306SWolfram Sang 	writel(val, host->ioaddr + reg);
5387e29c306SWolfram Sang }
5397e29c306SWolfram Sang 
54095f25efeSWolfram Sang static u16 esdhc_readw_le(struct sdhci_host *host, int reg)
54195f25efeSWolfram Sang {
542ef4d0888SShawn Guo 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
543070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
5440322191eSDong Aisheng 	u16 ret = 0;
5450322191eSDong Aisheng 	u32 val;
546ef4d0888SShawn Guo 
54795a2482aSShawn Guo 	if (unlikely(reg == SDHCI_HOST_VERSION)) {
548ef4d0888SShawn Guo 		reg ^= 2;
5499d61c009SShawn Guo 		if (esdhc_is_usdhc(imx_data)) {
55095a2482aSShawn Guo 			/*
551ef4d0888SShawn Guo 			 * The usdhc register returns a wrong host version.
552ef4d0888SShawn Guo 			 * Correct it here.
55395a2482aSShawn Guo 			 */
554ef4d0888SShawn Guo 			return SDHCI_SPEC_300;
555ef4d0888SShawn Guo 		}
55695a2482aSShawn Guo 	}
55795f25efeSWolfram Sang 
5580322191eSDong Aisheng 	if (unlikely(reg == SDHCI_HOST_CONTROL2)) {
5590322191eSDong Aisheng 		val = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
5600322191eSDong Aisheng 		if (val & ESDHC_VENDOR_SPEC_VSELECT)
5610322191eSDong Aisheng 			ret |= SDHCI_CTRL_VDD_180;
5620322191eSDong Aisheng 
5639d61c009SShawn Guo 		if (esdhc_is_usdhc(imx_data)) {
5646e9fd28eSDong Aisheng 			if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING)
5650322191eSDong Aisheng 				val = readl(host->ioaddr + ESDHC_MIX_CTRL);
5666e9fd28eSDong Aisheng 			else if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING)
5676e9fd28eSDong Aisheng 				/* the std tuning bits is in ACMD12_ERR for imx6sl */
568869f8a69SAdrian Hunter 				val = readl(host->ioaddr + SDHCI_AUTO_CMD_STATUS);
5696e9fd28eSDong Aisheng 		}
5706e9fd28eSDong Aisheng 
5710322191eSDong Aisheng 		if (val & ESDHC_MIX_CTRL_EXE_TUNE)
5720322191eSDong Aisheng 			ret |= SDHCI_CTRL_EXEC_TUNING;
5730322191eSDong Aisheng 		if (val & ESDHC_MIX_CTRL_SMPCLK_SEL)
5740322191eSDong Aisheng 			ret |= SDHCI_CTRL_TUNED_CLK;
5750322191eSDong Aisheng 
5760322191eSDong Aisheng 		ret &= ~SDHCI_CTRL_PRESET_VAL_ENABLE;
5770322191eSDong Aisheng 
5780322191eSDong Aisheng 		return ret;
5790322191eSDong Aisheng 	}
5800322191eSDong Aisheng 
5817dd109efSDong Aisheng 	if (unlikely(reg == SDHCI_TRANSFER_MODE)) {
5827dd109efSDong Aisheng 		if (esdhc_is_usdhc(imx_data)) {
5837dd109efSDong Aisheng 			u32 m = readl(host->ioaddr + ESDHC_MIX_CTRL);
5847dd109efSDong Aisheng 			ret = m & ESDHC_MIX_CTRL_SDHCI_MASK;
5857dd109efSDong Aisheng 			/* Swap AC23 bit */
5867dd109efSDong Aisheng 			if (m & ESDHC_MIX_CTRL_AC23EN) {
5877dd109efSDong Aisheng 				ret &= ~ESDHC_MIX_CTRL_AC23EN;
5887dd109efSDong Aisheng 				ret |= SDHCI_TRNS_AUTO_CMD23;
5897dd109efSDong Aisheng 			}
5907dd109efSDong Aisheng 		} else {
5917dd109efSDong Aisheng 			ret = readw(host->ioaddr + SDHCI_TRANSFER_MODE);
5927dd109efSDong Aisheng 		}
5937dd109efSDong Aisheng 
5947dd109efSDong Aisheng 		return ret;
5957dd109efSDong Aisheng 	}
5967dd109efSDong Aisheng 
59795f25efeSWolfram Sang 	return readw(host->ioaddr + reg);
59895f25efeSWolfram Sang }
59995f25efeSWolfram Sang 
60095f25efeSWolfram Sang static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg)
60195f25efeSWolfram Sang {
60295f25efeSWolfram Sang 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
603070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
6040322191eSDong Aisheng 	u32 new_val = 0;
60595f25efeSWolfram Sang 
60695f25efeSWolfram Sang 	switch (reg) {
6070322191eSDong Aisheng 	case SDHCI_CLOCK_CONTROL:
6080322191eSDong Aisheng 		new_val = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
6090322191eSDong Aisheng 		if (val & SDHCI_CLOCK_CARD_EN)
6100322191eSDong Aisheng 			new_val |= ESDHC_VENDOR_SPEC_FRC_SDCLK_ON;
6110322191eSDong Aisheng 		else
6120322191eSDong Aisheng 			new_val &= ~ESDHC_VENDOR_SPEC_FRC_SDCLK_ON;
6130322191eSDong Aisheng 		writel(new_val, host->ioaddr + ESDHC_VENDOR_SPEC);
614f581e909SHaibo Chen 		if (!(new_val & ESDHC_VENDOR_SPEC_FRC_SDCLK_ON))
615f581e909SHaibo Chen 			esdhc_wait_for_card_clock_gate_off(host);
6160322191eSDong Aisheng 		return;
6170322191eSDong Aisheng 	case SDHCI_HOST_CONTROL2:
6180322191eSDong Aisheng 		new_val = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
6190322191eSDong Aisheng 		if (val & SDHCI_CTRL_VDD_180)
6200322191eSDong Aisheng 			new_val |= ESDHC_VENDOR_SPEC_VSELECT;
6210322191eSDong Aisheng 		else
6220322191eSDong Aisheng 			new_val &= ~ESDHC_VENDOR_SPEC_VSELECT;
6230322191eSDong Aisheng 		writel(new_val, host->ioaddr + ESDHC_VENDOR_SPEC);
6246e9fd28eSDong Aisheng 		if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING) {
6250322191eSDong Aisheng 			new_val = readl(host->ioaddr + ESDHC_MIX_CTRL);
626da0295ffSDong Aisheng 			if (val & SDHCI_CTRL_TUNED_CLK) {
6270322191eSDong Aisheng 				new_val |= ESDHC_MIX_CTRL_SMPCLK_SEL;
628da0295ffSDong Aisheng 				new_val |= ESDHC_MIX_CTRL_AUTO_TUNE_EN;
629da0295ffSDong Aisheng 			} else {
6300322191eSDong Aisheng 				new_val &= ~ESDHC_MIX_CTRL_SMPCLK_SEL;
631da0295ffSDong Aisheng 				new_val &= ~ESDHC_MIX_CTRL_AUTO_TUNE_EN;
632da0295ffSDong Aisheng 			}
6330322191eSDong Aisheng 			writel(new_val , host->ioaddr + ESDHC_MIX_CTRL);
6346e9fd28eSDong Aisheng 		} else if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) {
635869f8a69SAdrian Hunter 			u32 v = readl(host->ioaddr + SDHCI_AUTO_CMD_STATUS);
6366e9fd28eSDong Aisheng 			u32 m = readl(host->ioaddr + ESDHC_MIX_CTRL);
6378b2bb0adSDong Aisheng 			if (val & SDHCI_CTRL_TUNED_CLK) {
6388b2bb0adSDong Aisheng 				v |= ESDHC_MIX_CTRL_SMPCLK_SEL;
6396e9fd28eSDong Aisheng 			} else {
6408b2bb0adSDong Aisheng 				v &= ~ESDHC_MIX_CTRL_SMPCLK_SEL;
6416e9fd28eSDong Aisheng 				m &= ~ESDHC_MIX_CTRL_FBCLK_SEL;
6420b330e38SDong Aisheng 				m &= ~ESDHC_MIX_CTRL_AUTO_TUNE_EN;
6436e9fd28eSDong Aisheng 			}
6446e9fd28eSDong Aisheng 
6458b2bb0adSDong Aisheng 			if (val & SDHCI_CTRL_EXEC_TUNING) {
6468b2bb0adSDong Aisheng 				v |= ESDHC_MIX_CTRL_EXE_TUNE;
6478b2bb0adSDong Aisheng 				m |= ESDHC_MIX_CTRL_FBCLK_SEL;
6480b330e38SDong Aisheng 				m |= ESDHC_MIX_CTRL_AUTO_TUNE_EN;
6498b2bb0adSDong Aisheng 			} else {
6508b2bb0adSDong Aisheng 				v &= ~ESDHC_MIX_CTRL_EXE_TUNE;
6518b2bb0adSDong Aisheng 			}
6526e9fd28eSDong Aisheng 
653869f8a69SAdrian Hunter 			writel(v, host->ioaddr + SDHCI_AUTO_CMD_STATUS);
6546e9fd28eSDong Aisheng 			writel(m, host->ioaddr + ESDHC_MIX_CTRL);
6556e9fd28eSDong Aisheng 		}
6560322191eSDong Aisheng 		return;
65795f25efeSWolfram Sang 	case SDHCI_TRANSFER_MODE:
658f47c4bbfSShawn Guo 		if ((imx_data->socdata->flags & ESDHC_FLAG_MULTIBLK_NO_INT)
65958ac8177SRichard Zhu 				&& (host->cmd->opcode == SD_IO_RW_EXTENDED)
66058ac8177SRichard Zhu 				&& (host->cmd->data->blocks > 1)
66158ac8177SRichard Zhu 				&& (host->cmd->data->flags & MMC_DATA_READ)) {
66258ac8177SRichard Zhu 			u32 v;
66360bf6396SShawn Guo 			v = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
66460bf6396SShawn Guo 			v |= ESDHC_VENDOR_SPEC_SDIO_QUIRK;
66560bf6396SShawn Guo 			writel(v, host->ioaddr + ESDHC_VENDOR_SPEC);
66658ac8177SRichard Zhu 		}
66769f54698SShawn Guo 
6689d61c009SShawn Guo 		if (esdhc_is_usdhc(imx_data)) {
6693fbd4322SAndrew Gabbasov 			u32 wml;
67069f54698SShawn Guo 			u32 m = readl(host->ioaddr + ESDHC_MIX_CTRL);
6712a15f981SShawn Guo 			/* Swap AC23 bit */
6722a15f981SShawn Guo 			if (val & SDHCI_TRNS_AUTO_CMD23) {
6732a15f981SShawn Guo 				val &= ~SDHCI_TRNS_AUTO_CMD23;
6742a15f981SShawn Guo 				val |= ESDHC_MIX_CTRL_AC23EN;
6752a15f981SShawn Guo 			}
6762a15f981SShawn Guo 			m = val | (m & ~ESDHC_MIX_CTRL_SDHCI_MASK);
67769f54698SShawn Guo 			writel(m, host->ioaddr + ESDHC_MIX_CTRL);
6783fbd4322SAndrew Gabbasov 
6793fbd4322SAndrew Gabbasov 			/* Set watermark levels for PIO access to maximum value
6803fbd4322SAndrew Gabbasov 			 * (128 words) to accommodate full 512 bytes buffer.
6813fbd4322SAndrew Gabbasov 			 * For DMA access restore the levels to default value.
6823fbd4322SAndrew Gabbasov 			 */
6833fbd4322SAndrew Gabbasov 			m = readl(host->ioaddr + ESDHC_WTMK_LVL);
684e534b82fSHaibo Chen 			if (val & SDHCI_TRNS_DMA) {
6853fbd4322SAndrew Gabbasov 				wml = ESDHC_WTMK_LVL_WML_VAL_DEF;
686e534b82fSHaibo Chen 			} else {
687e534b82fSHaibo Chen 				u8 ctrl;
6883fbd4322SAndrew Gabbasov 				wml = ESDHC_WTMK_LVL_WML_VAL_MAX;
689e534b82fSHaibo Chen 
690e534b82fSHaibo Chen 				/*
691e534b82fSHaibo Chen 				 * Since already disable DMA mode, so also need
692e534b82fSHaibo Chen 				 * to clear the DMASEL. Otherwise, for standard
693e534b82fSHaibo Chen 				 * tuning, when send tuning command, usdhc will
694e534b82fSHaibo Chen 				 * still prefetch the ADMA script from wrong
695e534b82fSHaibo Chen 				 * DMA address, then we will see IOMMU report
696e534b82fSHaibo Chen 				 * some error which show lack of TLB mapping.
697e534b82fSHaibo Chen 				 */
698e534b82fSHaibo Chen 				ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
699e534b82fSHaibo Chen 				ctrl &= ~SDHCI_CTRL_DMA_MASK;
700e534b82fSHaibo Chen 				sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
701e534b82fSHaibo Chen 			}
7023fbd4322SAndrew Gabbasov 			m &= ~(ESDHC_WTMK_LVL_RD_WML_MASK |
7033fbd4322SAndrew Gabbasov 			       ESDHC_WTMK_LVL_WR_WML_MASK);
7043fbd4322SAndrew Gabbasov 			m |= (wml << ESDHC_WTMK_LVL_RD_WML_SHIFT) |
7053fbd4322SAndrew Gabbasov 			     (wml << ESDHC_WTMK_LVL_WR_WML_SHIFT);
7063fbd4322SAndrew Gabbasov 			writel(m, host->ioaddr + ESDHC_WTMK_LVL);
70769f54698SShawn Guo 		} else {
70869f54698SShawn Guo 			/*
70969f54698SShawn Guo 			 * Postpone this write, we must do it together with a
71069f54698SShawn Guo 			 * command write that is down below.
71169f54698SShawn Guo 			 */
712e149860dSRichard Zhu 			imx_data->scratchpad = val;
71369f54698SShawn Guo 		}
71495f25efeSWolfram Sang 		return;
71595f25efeSWolfram Sang 	case SDHCI_COMMAND:
716361b8482SLucas Stach 		if (host->cmd->opcode == MMC_STOP_TRANSMISSION)
71758ac8177SRichard Zhu 			val |= SDHCI_CMD_ABORTCMD;
71895a2482aSShawn Guo 
719361b8482SLucas Stach 		if ((host->cmd->opcode == MMC_SET_BLOCK_COUNT) &&
720f47c4bbfSShawn Guo 		    (imx_data->socdata->flags & ESDHC_FLAG_MULTIBLK_NO_INT))
721361b8482SLucas Stach 			imx_data->multiblock_status = MULTIBLK_IN_PROCESS;
722361b8482SLucas Stach 
7239d61c009SShawn Guo 		if (esdhc_is_usdhc(imx_data))
72495a2482aSShawn Guo 			writel(val << 16,
72595a2482aSShawn Guo 			       host->ioaddr + SDHCI_TRANSFER_MODE);
72669f54698SShawn Guo 		else
727e149860dSRichard Zhu 			writel(val << 16 | imx_data->scratchpad,
72895f25efeSWolfram Sang 			       host->ioaddr + SDHCI_TRANSFER_MODE);
72995f25efeSWolfram Sang 		return;
73095f25efeSWolfram Sang 	case SDHCI_BLOCK_SIZE:
73195f25efeSWolfram Sang 		val &= ~SDHCI_MAKE_BLKSZ(0x7, 0);
73295f25efeSWolfram Sang 		break;
73395f25efeSWolfram Sang 	}
73495f25efeSWolfram Sang 	esdhc_clrset_le(host, 0xffff, val, reg);
73595f25efeSWolfram Sang }
73695f25efeSWolfram Sang 
73777da3da0SAaron Brice static u8 esdhc_readb_le(struct sdhci_host *host, int reg)
73877da3da0SAaron Brice {
73977da3da0SAaron Brice 	u8 ret;
74077da3da0SAaron Brice 	u32 val;
74177da3da0SAaron Brice 
74277da3da0SAaron Brice 	switch (reg) {
74377da3da0SAaron Brice 	case SDHCI_HOST_CONTROL:
74477da3da0SAaron Brice 		val = readl(host->ioaddr + reg);
74577da3da0SAaron Brice 
74677da3da0SAaron Brice 		ret = val & SDHCI_CTRL_LED;
74777da3da0SAaron Brice 		ret |= (val >> 5) & SDHCI_CTRL_DMA_MASK;
74877da3da0SAaron Brice 		ret |= (val & ESDHC_CTRL_4BITBUS);
74977da3da0SAaron Brice 		ret |= (val & ESDHC_CTRL_8BITBUS) << 3;
75077da3da0SAaron Brice 		return ret;
75177da3da0SAaron Brice 	}
75277da3da0SAaron Brice 
75377da3da0SAaron Brice 	return readb(host->ioaddr + reg);
75477da3da0SAaron Brice }
75577da3da0SAaron Brice 
75695f25efeSWolfram Sang static void esdhc_writeb_le(struct sdhci_host *host, u8 val, int reg)
75795f25efeSWolfram Sang {
7589a0985b7SWilson Callan 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
759070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
76081a0a8bcSBenoît Thébaudeau 	u32 new_val = 0;
761af51079eSSascha Hauer 	u32 mask;
76295f25efeSWolfram Sang 
76395f25efeSWolfram Sang 	switch (reg) {
76495f25efeSWolfram Sang 	case SDHCI_POWER_CONTROL:
76595f25efeSWolfram Sang 		/*
76695f25efeSWolfram Sang 		 * FSL put some DMA bits here
76795f25efeSWolfram Sang 		 * If your board has a regulator, code should be here
76895f25efeSWolfram Sang 		 */
76995f25efeSWolfram Sang 		return;
77095f25efeSWolfram Sang 	case SDHCI_HOST_CONTROL:
7716b40d182SShawn Guo 		/* FSL messed up here, so we need to manually compose it. */
772af51079eSSascha Hauer 		new_val = val & SDHCI_CTRL_LED;
7737122bbb0SMasanari Iida 		/* ensure the endianness */
77495f25efeSWolfram Sang 		new_val |= ESDHC_HOST_CONTROL_LE;
7759a0985b7SWilson Callan 		/* bits 8&9 are reserved on mx25 */
7769a0985b7SWilson Callan 		if (!is_imx25_esdhc(imx_data)) {
77795f25efeSWolfram Sang 			/* DMA mode bits are shifted */
77895f25efeSWolfram Sang 			new_val |= (val & SDHCI_CTRL_DMA_MASK) << 5;
7799a0985b7SWilson Callan 		}
78095f25efeSWolfram Sang 
781af51079eSSascha Hauer 		/*
782af51079eSSascha Hauer 		 * Do not touch buswidth bits here. This is done in
783af51079eSSascha Hauer 		 * esdhc_pltfm_bus_width.
784f6825748SMartin Fuzzey 		 * Do not touch the D3CD bit either which is used for the
785d04f8d5bSBenoît Thébaudeau 		 * SDIO interrupt erratum workaround.
786af51079eSSascha Hauer 		 */
787f6825748SMartin Fuzzey 		mask = 0xffff & ~(ESDHC_CTRL_BUSWIDTH_MASK | ESDHC_CTRL_D3CD);
788af51079eSSascha Hauer 
789af51079eSSascha Hauer 		esdhc_clrset_le(host, mask, new_val, reg);
79095f25efeSWolfram Sang 		return;
79181a0a8bcSBenoît Thébaudeau 	case SDHCI_SOFTWARE_RESET:
79281a0a8bcSBenoît Thébaudeau 		if (val & SDHCI_RESET_DATA)
79381a0a8bcSBenoît Thébaudeau 			new_val = readl(host->ioaddr + SDHCI_HOST_CONTROL);
79481a0a8bcSBenoît Thébaudeau 		break;
79595f25efeSWolfram Sang 	}
79695f25efeSWolfram Sang 	esdhc_clrset_le(host, 0xff, val, reg);
797913413c3SShawn Guo 
79881a0a8bcSBenoît Thébaudeau 	if (reg == SDHCI_SOFTWARE_RESET) {
79981a0a8bcSBenoît Thébaudeau 		if (val & SDHCI_RESET_ALL) {
800913413c3SShawn Guo 			/*
80181a0a8bcSBenoît Thébaudeau 			 * The esdhc has a design violation to SDHC spec which
80281a0a8bcSBenoît Thébaudeau 			 * tells that software reset should not affect card
80381a0a8bcSBenoît Thébaudeau 			 * detection circuit. But esdhc clears its SYSCTL
80481a0a8bcSBenoît Thébaudeau 			 * register bits [0..2] during the software reset. This
80581a0a8bcSBenoît Thébaudeau 			 * will stop those clocks that card detection circuit
80681a0a8bcSBenoît Thébaudeau 			 * relies on. To work around it, we turn the clocks on
80781a0a8bcSBenoît Thébaudeau 			 * back to keep card detection circuit functional.
808913413c3SShawn Guo 			 */
809913413c3SShawn Guo 			esdhc_clrset_le(host, 0x7, 0x7, ESDHC_SYSTEM_CONTROL);
81058c8c4fbSShawn Guo 			/*
81158c8c4fbSShawn Guo 			 * The reset on usdhc fails to clear MIX_CTRL register.
81258c8c4fbSShawn Guo 			 * Do it manually here.
81358c8c4fbSShawn Guo 			 */
814de5bdbffSDong Aisheng 			if (esdhc_is_usdhc(imx_data)) {
81581a0a8bcSBenoît Thébaudeau 				/*
81681a0a8bcSBenoît Thébaudeau 				 * the tuning bits should be kept during reset
81781a0a8bcSBenoît Thébaudeau 				 */
818d131a71cSDong Aisheng 				new_val = readl(host->ioaddr + ESDHC_MIX_CTRL);
819d131a71cSDong Aisheng 				writel(new_val & ESDHC_MIX_CTRL_TUNING_MASK,
820d131a71cSDong Aisheng 						host->ioaddr + ESDHC_MIX_CTRL);
821de5bdbffSDong Aisheng 				imx_data->is_ddr = 0;
822de5bdbffSDong Aisheng 			}
82381a0a8bcSBenoît Thébaudeau 		} else if (val & SDHCI_RESET_DATA) {
82481a0a8bcSBenoît Thébaudeau 			/*
82581a0a8bcSBenoît Thébaudeau 			 * The eSDHC DAT line software reset clears at least the
82681a0a8bcSBenoît Thébaudeau 			 * data transfer width on i.MX25, so make sure that the
82781a0a8bcSBenoît Thébaudeau 			 * Host Control register is unaffected.
82881a0a8bcSBenoît Thébaudeau 			 */
82981a0a8bcSBenoît Thébaudeau 			esdhc_clrset_le(host, 0xff, new_val,
83081a0a8bcSBenoît Thébaudeau 					SDHCI_HOST_CONTROL);
83181a0a8bcSBenoît Thébaudeau 		}
83258c8c4fbSShawn Guo 	}
83395f25efeSWolfram Sang }
83495f25efeSWolfram Sang 
8350ddf03c9SLucas Stach static unsigned int esdhc_pltfm_get_max_clock(struct sdhci_host *host)
8360ddf03c9SLucas Stach {
8370ddf03c9SLucas Stach 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
8380ddf03c9SLucas Stach 
839a974862fSDong Aisheng 	return pltfm_host->clock;
8400ddf03c9SLucas Stach }
8410ddf03c9SLucas Stach 
84295f25efeSWolfram Sang static unsigned int esdhc_pltfm_get_min_clock(struct sdhci_host *host)
84395f25efeSWolfram Sang {
84495f25efeSWolfram Sang 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
84595f25efeSWolfram Sang 
846a974862fSDong Aisheng 	return pltfm_host->clock / 256 / 16;
84795f25efeSWolfram Sang }
84895f25efeSWolfram Sang 
8498ba9580aSLucas Stach static inline void esdhc_pltfm_set_clock(struct sdhci_host *host,
8508ba9580aSLucas Stach 					 unsigned int clock)
8518ba9580aSLucas Stach {
8528ba9580aSLucas Stach 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
853070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
854a974862fSDong Aisheng 	unsigned int host_clock = pltfm_host->clock;
8555143c953SBenoît Thébaudeau 	int ddr_pre_div = imx_data->is_ddr ? 2 : 1;
8565143c953SBenoît Thébaudeau 	int pre_div = 1;
857d31fc00aSDong Aisheng 	int div = 1;
858f581e909SHaibo Chen 	int ret;
859fed2f6e2SDong Aisheng 	u32 temp, val;
8608ba9580aSLucas Stach 
8619d61c009SShawn Guo 	if (esdhc_is_usdhc(imx_data)) {
862fed2f6e2SDong Aisheng 		val = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
863fed2f6e2SDong Aisheng 		writel(val & ~ESDHC_VENDOR_SPEC_FRC_SDCLK_ON,
864fed2f6e2SDong Aisheng 			host->ioaddr + ESDHC_VENDOR_SPEC);
865f581e909SHaibo Chen 		esdhc_wait_for_card_clock_gate_off(host);
866fed2f6e2SDong Aisheng 	}
86773e736f8SStefan Agner 
86873e736f8SStefan Agner 	if (clock == 0) {
86973e736f8SStefan Agner 		host->mmc->actual_clock = 0;
870373073efSRussell King 		return;
871fed2f6e2SDong Aisheng 	}
872d31fc00aSDong Aisheng 
873499ed50fSBenoît Thébaudeau 	/* For i.MX53 eSDHCv3, SYSCTL.SDCLKFS may not be set to 0. */
874499ed50fSBenoît Thébaudeau 	if (is_imx53_esdhc(imx_data)) {
875499ed50fSBenoît Thébaudeau 		/*
876499ed50fSBenoît Thébaudeau 		 * According to the i.MX53 reference manual, if DLLCTRL[10] can
877499ed50fSBenoît Thébaudeau 		 * be set, then the controller is eSDHCv3, else it is eSDHCv2.
878499ed50fSBenoît Thébaudeau 		 */
879499ed50fSBenoît Thébaudeau 		val = readl(host->ioaddr + ESDHC_DLL_CTRL);
880499ed50fSBenoît Thébaudeau 		writel(val | BIT(10), host->ioaddr + ESDHC_DLL_CTRL);
881499ed50fSBenoît Thébaudeau 		temp = readl(host->ioaddr + ESDHC_DLL_CTRL);
882499ed50fSBenoît Thébaudeau 		writel(val, host->ioaddr + ESDHC_DLL_CTRL);
883499ed50fSBenoît Thébaudeau 		if (temp & BIT(10))
884499ed50fSBenoît Thébaudeau 			pre_div = 2;
885499ed50fSBenoît Thébaudeau 	}
886499ed50fSBenoît Thébaudeau 
887d31fc00aSDong Aisheng 	temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
888d31fc00aSDong Aisheng 	temp &= ~(ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN
889d31fc00aSDong Aisheng 		| ESDHC_CLOCK_MASK);
890d31fc00aSDong Aisheng 	sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
891d31fc00aSDong Aisheng 
892af6a50d4SBOUGH CHEN 	if (imx_data->socdata->flags & ESDHC_FLAG_ERR010450) {
893af6a50d4SBOUGH CHEN 		unsigned int max_clock;
894af6a50d4SBOUGH CHEN 
895af6a50d4SBOUGH CHEN 		max_clock = imx_data->is_ddr ? 45000000 : 150000000;
896af6a50d4SBOUGH CHEN 
897af6a50d4SBOUGH CHEN 		clock = min(clock, max_clock);
898af6a50d4SBOUGH CHEN 	}
899af6a50d4SBOUGH CHEN 
9005143c953SBenoît Thébaudeau 	while (host_clock / (16 * pre_div * ddr_pre_div) > clock &&
9015143c953SBenoît Thébaudeau 			pre_div < 256)
902d31fc00aSDong Aisheng 		pre_div *= 2;
903d31fc00aSDong Aisheng 
9045143c953SBenoît Thébaudeau 	while (host_clock / (div * pre_div * ddr_pre_div) > clock && div < 16)
905d31fc00aSDong Aisheng 		div++;
906d31fc00aSDong Aisheng 
9075143c953SBenoît Thébaudeau 	host->mmc->actual_clock = host_clock / (div * pre_div * ddr_pre_div);
908d31fc00aSDong Aisheng 	dev_dbg(mmc_dev(host->mmc), "desired SD clock: %d, actual: %d\n",
909e76b8559SDong Aisheng 		clock, host->mmc->actual_clock);
910d31fc00aSDong Aisheng 
911d31fc00aSDong Aisheng 	pre_div >>= 1;
912d31fc00aSDong Aisheng 	div--;
913d31fc00aSDong Aisheng 
914d31fc00aSDong Aisheng 	temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
915d31fc00aSDong Aisheng 	temp |= (ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN
916d31fc00aSDong Aisheng 		| (div << ESDHC_DIVIDER_SHIFT)
917d31fc00aSDong Aisheng 		| (pre_div << ESDHC_PREDIV_SHIFT));
918d31fc00aSDong Aisheng 	sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
919fed2f6e2SDong Aisheng 
920f581e909SHaibo Chen 	/* need to wait the bit 3 of the PRSSTAT to be set, make sure card clock is stable */
921f581e909SHaibo Chen 	ret = readl_poll_timeout(host->ioaddr + ESDHC_PRSSTAT, temp,
922f581e909SHaibo Chen 				(temp & ESDHC_CLOCK_STABLE), 2, 100);
923f581e909SHaibo Chen 	if (ret == -ETIMEDOUT)
924f581e909SHaibo Chen 		dev_warn(mmc_dev(host->mmc), "card clock still not stable in 100us!.\n");
925f581e909SHaibo Chen 
9269d61c009SShawn Guo 	if (esdhc_is_usdhc(imx_data)) {
927fed2f6e2SDong Aisheng 		val = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
928fed2f6e2SDong Aisheng 		writel(val | ESDHC_VENDOR_SPEC_FRC_SDCLK_ON,
929fed2f6e2SDong Aisheng 			host->ioaddr + ESDHC_VENDOR_SPEC);
930fed2f6e2SDong Aisheng 	}
931fed2f6e2SDong Aisheng 
9328ba9580aSLucas Stach }
9338ba9580aSLucas Stach 
934913413c3SShawn Guo static unsigned int esdhc_pltfm_get_ro(struct sdhci_host *host)
935913413c3SShawn Guo {
936842afc02SShawn Guo 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
937070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
938842afc02SShawn Guo 	struct esdhc_platform_data *boarddata = &imx_data->boarddata;
939913413c3SShawn Guo 
940913413c3SShawn Guo 	switch (boarddata->wp_type) {
941913413c3SShawn Guo 	case ESDHC_WP_GPIO:
942fbe5fdd1SShawn Guo 		return mmc_gpio_get_ro(host->mmc);
943913413c3SShawn Guo 	case ESDHC_WP_CONTROLLER:
944913413c3SShawn Guo 		return !(readl(host->ioaddr + SDHCI_PRESENT_STATE) &
945913413c3SShawn Guo 			       SDHCI_WRITE_PROTECT);
946913413c3SShawn Guo 	case ESDHC_WP_NONE:
947913413c3SShawn Guo 		break;
948913413c3SShawn Guo 	}
949913413c3SShawn Guo 
950913413c3SShawn Guo 	return -ENOSYS;
951913413c3SShawn Guo }
952913413c3SShawn Guo 
9532317f56cSRussell King static void esdhc_pltfm_set_bus_width(struct sdhci_host *host, int width)
954af51079eSSascha Hauer {
955af51079eSSascha Hauer 	u32 ctrl;
956af51079eSSascha Hauer 
957af51079eSSascha Hauer 	switch (width) {
958af51079eSSascha Hauer 	case MMC_BUS_WIDTH_8:
959af51079eSSascha Hauer 		ctrl = ESDHC_CTRL_8BITBUS;
960af51079eSSascha Hauer 		break;
961af51079eSSascha Hauer 	case MMC_BUS_WIDTH_4:
962af51079eSSascha Hauer 		ctrl = ESDHC_CTRL_4BITBUS;
963af51079eSSascha Hauer 		break;
964af51079eSSascha Hauer 	default:
965af51079eSSascha Hauer 		ctrl = 0;
966af51079eSSascha Hauer 		break;
967af51079eSSascha Hauer 	}
968af51079eSSascha Hauer 
969af51079eSSascha Hauer 	esdhc_clrset_le(host, ESDHC_CTRL_BUSWIDTH_MASK, ctrl,
970af51079eSSascha Hauer 			SDHCI_HOST_CONTROL);
971af51079eSSascha Hauer }
972af51079eSSascha Hauer 
973de3e1dd0SBOUGH CHEN static int usdhc_execute_tuning(struct mmc_host *mmc, u32 opcode)
974de3e1dd0SBOUGH CHEN {
975de3e1dd0SBOUGH CHEN 	struct sdhci_host *host = mmc_priv(mmc);
976de3e1dd0SBOUGH CHEN 
977de3e1dd0SBOUGH CHEN 	/*
978de3e1dd0SBOUGH CHEN 	 * i.MX uSDHC internally already uses a fixed optimized timing for
979de3e1dd0SBOUGH CHEN 	 * DDR50, normally does not require tuning for DDR50 mode.
980de3e1dd0SBOUGH CHEN 	 */
981de3e1dd0SBOUGH CHEN 	if (host->timing == MMC_TIMING_UHS_DDR50)
982de3e1dd0SBOUGH CHEN 		return 0;
983de3e1dd0SBOUGH CHEN 
984de3e1dd0SBOUGH CHEN 	return sdhci_execute_tuning(mmc, opcode);
985de3e1dd0SBOUGH CHEN }
986de3e1dd0SBOUGH CHEN 
9870322191eSDong Aisheng static void esdhc_prepare_tuning(struct sdhci_host *host, u32 val)
9880322191eSDong Aisheng {
9890322191eSDong Aisheng 	u32 reg;
9900ac4f496SHaibo Chen 	u8 sw_rst;
9910ac4f496SHaibo Chen 	int ret;
9920322191eSDong Aisheng 
9930322191eSDong Aisheng 	/* FIXME: delay a bit for card to be ready for next tuning due to errors */
9940322191eSDong Aisheng 	mdelay(1);
9950322191eSDong Aisheng 
9960ac4f496SHaibo Chen 	/* IC suggest to reset USDHC before every tuning command */
9970ac4f496SHaibo Chen 	esdhc_clrset_le(host, 0xff, SDHCI_RESET_ALL, SDHCI_SOFTWARE_RESET);
9980ac4f496SHaibo Chen 	ret = readb_poll_timeout(host->ioaddr + SDHCI_SOFTWARE_RESET, sw_rst,
9990ac4f496SHaibo Chen 				!(sw_rst & SDHCI_RESET_ALL), 10, 100);
10000ac4f496SHaibo Chen 	if (ret == -ETIMEDOUT)
10010ac4f496SHaibo Chen 		dev_warn(mmc_dev(host->mmc),
10020ac4f496SHaibo Chen 		"warning! RESET_ALL never complete before sending tuning command\n");
10030ac4f496SHaibo Chen 
10040322191eSDong Aisheng 	reg = readl(host->ioaddr + ESDHC_MIX_CTRL);
10050322191eSDong Aisheng 	reg |= ESDHC_MIX_CTRL_EXE_TUNE | ESDHC_MIX_CTRL_SMPCLK_SEL |
10060322191eSDong Aisheng 			ESDHC_MIX_CTRL_FBCLK_SEL;
10070322191eSDong Aisheng 	writel(reg, host->ioaddr + ESDHC_MIX_CTRL);
10080322191eSDong Aisheng 	writel(val << 8, host->ioaddr + ESDHC_TUNE_CTRL_STATUS);
10090322191eSDong Aisheng 	dev_dbg(mmc_dev(host->mmc),
1010d04f8d5bSBenoît Thébaudeau 		"tuning with delay 0x%x ESDHC_TUNE_CTRL_STATUS 0x%x\n",
10110322191eSDong Aisheng 			val, readl(host->ioaddr + ESDHC_TUNE_CTRL_STATUS));
10120322191eSDong Aisheng }
10130322191eSDong Aisheng 
10140322191eSDong Aisheng static void esdhc_post_tuning(struct sdhci_host *host)
10150322191eSDong Aisheng {
10160322191eSDong Aisheng 	u32 reg;
10170322191eSDong Aisheng 
10180322191eSDong Aisheng 	reg = readl(host->ioaddr + ESDHC_MIX_CTRL);
10190322191eSDong Aisheng 	reg &= ~ESDHC_MIX_CTRL_EXE_TUNE;
1020da0295ffSDong Aisheng 	reg |= ESDHC_MIX_CTRL_AUTO_TUNE_EN;
10210322191eSDong Aisheng 	writel(reg, host->ioaddr + ESDHC_MIX_CTRL);
10220322191eSDong Aisheng }
10230322191eSDong Aisheng 
10240322191eSDong Aisheng static int esdhc_executing_tuning(struct sdhci_host *host, u32 opcode)
10250322191eSDong Aisheng {
10260322191eSDong Aisheng 	int min, max, avg, ret;
10270322191eSDong Aisheng 
10280322191eSDong Aisheng 	/* find the mininum delay first which can pass tuning */
10290322191eSDong Aisheng 	min = ESDHC_TUNE_CTRL_MIN;
10300322191eSDong Aisheng 	while (min < ESDHC_TUNE_CTRL_MAX) {
10310322191eSDong Aisheng 		esdhc_prepare_tuning(host, min);
10329979dbe5SChaotian Jing 		if (!mmc_send_tuning(host->mmc, opcode, NULL))
10330322191eSDong Aisheng 			break;
10340322191eSDong Aisheng 		min += ESDHC_TUNE_CTRL_STEP;
10350322191eSDong Aisheng 	}
10360322191eSDong Aisheng 
10370322191eSDong Aisheng 	/* find the maxinum delay which can not pass tuning */
10380322191eSDong Aisheng 	max = min + ESDHC_TUNE_CTRL_STEP;
10390322191eSDong Aisheng 	while (max < ESDHC_TUNE_CTRL_MAX) {
10400322191eSDong Aisheng 		esdhc_prepare_tuning(host, max);
10419979dbe5SChaotian Jing 		if (mmc_send_tuning(host->mmc, opcode, NULL)) {
10420322191eSDong Aisheng 			max -= ESDHC_TUNE_CTRL_STEP;
10430322191eSDong Aisheng 			break;
10440322191eSDong Aisheng 		}
10450322191eSDong Aisheng 		max += ESDHC_TUNE_CTRL_STEP;
10460322191eSDong Aisheng 	}
10470322191eSDong Aisheng 
10480322191eSDong Aisheng 	/* use average delay to get the best timing */
10490322191eSDong Aisheng 	avg = (min + max) / 2;
10500322191eSDong Aisheng 	esdhc_prepare_tuning(host, avg);
10519979dbe5SChaotian Jing 	ret = mmc_send_tuning(host->mmc, opcode, NULL);
10520322191eSDong Aisheng 	esdhc_post_tuning(host);
10530322191eSDong Aisheng 
1054d04f8d5bSBenoît Thébaudeau 	dev_dbg(mmc_dev(host->mmc), "tuning %s at 0x%x ret %d\n",
10550322191eSDong Aisheng 		ret ? "failed" : "passed", avg, ret);
10560322191eSDong Aisheng 
10570322191eSDong Aisheng 	return ret;
10580322191eSDong Aisheng }
10590322191eSDong Aisheng 
1060029e2476SBOUGH CHEN static void esdhc_hs400_enhanced_strobe(struct mmc_host *mmc, struct mmc_ios *ios)
1061029e2476SBOUGH CHEN {
1062029e2476SBOUGH CHEN 	struct sdhci_host *host = mmc_priv(mmc);
1063029e2476SBOUGH CHEN 	u32 m;
1064029e2476SBOUGH CHEN 
1065029e2476SBOUGH CHEN 	m = readl(host->ioaddr + ESDHC_MIX_CTRL);
1066029e2476SBOUGH CHEN 	if (ios->enhanced_strobe)
1067029e2476SBOUGH CHEN 		m |= ESDHC_MIX_CTRL_HS400_ES_EN;
1068029e2476SBOUGH CHEN 	else
1069029e2476SBOUGH CHEN 		m &= ~ESDHC_MIX_CTRL_HS400_ES_EN;
1070029e2476SBOUGH CHEN 	writel(m, host->ioaddr + ESDHC_MIX_CTRL);
1071029e2476SBOUGH CHEN }
1072029e2476SBOUGH CHEN 
1073ad93220dSDong Aisheng static int esdhc_change_pinstate(struct sdhci_host *host,
1074ad93220dSDong Aisheng 						unsigned int uhs)
1075ad93220dSDong Aisheng {
1076ad93220dSDong Aisheng 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1077070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
1078ad93220dSDong Aisheng 	struct pinctrl_state *pinctrl;
1079ad93220dSDong Aisheng 
1080ad93220dSDong Aisheng 	dev_dbg(mmc_dev(host->mmc), "change pinctrl state for uhs %d\n", uhs);
1081ad93220dSDong Aisheng 
1082ad93220dSDong Aisheng 	if (IS_ERR(imx_data->pinctrl) ||
1083ad93220dSDong Aisheng 		IS_ERR(imx_data->pins_100mhz) ||
1084ad93220dSDong Aisheng 		IS_ERR(imx_data->pins_200mhz))
1085ad93220dSDong Aisheng 		return -EINVAL;
1086ad93220dSDong Aisheng 
1087ad93220dSDong Aisheng 	switch (uhs) {
1088ad93220dSDong Aisheng 	case MMC_TIMING_UHS_SDR50:
10899f327845SHaibo Chen 	case MMC_TIMING_UHS_DDR50:
1090ad93220dSDong Aisheng 		pinctrl = imx_data->pins_100mhz;
1091ad93220dSDong Aisheng 		break;
1092ad93220dSDong Aisheng 	case MMC_TIMING_UHS_SDR104:
1093429a5b45SDong Aisheng 	case MMC_TIMING_MMC_HS200:
109428b07674SHaibo Chen 	case MMC_TIMING_MMC_HS400:
1095ad93220dSDong Aisheng 		pinctrl = imx_data->pins_200mhz;
1096ad93220dSDong Aisheng 		break;
1097ad93220dSDong Aisheng 	default:
1098ad93220dSDong Aisheng 		/* back to default state for other legacy timing */
10992480b720SUlf Hansson 		return pinctrl_select_default_state(mmc_dev(host->mmc));
1100ad93220dSDong Aisheng 	}
1101ad93220dSDong Aisheng 
1102ad93220dSDong Aisheng 	return pinctrl_select_state(imx_data->pinctrl, pinctrl);
1103ad93220dSDong Aisheng }
1104ad93220dSDong Aisheng 
110528b07674SHaibo Chen /*
1106d04f8d5bSBenoît Thébaudeau  * For HS400 eMMC, there is a data_strobe line. This signal is generated
110728b07674SHaibo Chen  * by the device and used for data output and CRC status response output
110828b07674SHaibo Chen  * in HS400 mode. The frequency of this signal follows the frequency of
1109d04f8d5bSBenoît Thébaudeau  * CLK generated by host. The host receives the data which is aligned to the
111028b07674SHaibo Chen  * edge of data_strobe line. Due to the time delay between CLK line and
111128b07674SHaibo Chen  * data_strobe line, if the delay time is larger than one clock cycle,
1112d04f8d5bSBenoît Thébaudeau  * then CLK and data_strobe line will be misaligned, read error shows up.
111328b07674SHaibo Chen  */
111428b07674SHaibo Chen static void esdhc_set_strobe_dll(struct sdhci_host *host)
111528b07674SHaibo Chen {
11165bd2acdcSHaibo Chen 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
11175bd2acdcSHaibo Chen 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
11185bd2acdcSHaibo Chen 	u32 strobe_delay;
111928b07674SHaibo Chen 	u32 v;
1120373e800bSHaibo Chen 	int ret;
112128b07674SHaibo Chen 
11227ac6da26SDong Aisheng 	/* disable clock before enabling strobe dll */
11237ac6da26SDong Aisheng 	writel(readl(host->ioaddr + ESDHC_VENDOR_SPEC) &
11247ac6da26SDong Aisheng 		~ESDHC_VENDOR_SPEC_FRC_SDCLK_ON,
11257ac6da26SDong Aisheng 		host->ioaddr + ESDHC_VENDOR_SPEC);
1126f581e909SHaibo Chen 	esdhc_wait_for_card_clock_gate_off(host);
11277ac6da26SDong Aisheng 
112828b07674SHaibo Chen 	/* force a reset on strobe dll */
112928b07674SHaibo Chen 	writel(ESDHC_STROBE_DLL_CTRL_RESET,
113028b07674SHaibo Chen 		host->ioaddr + ESDHC_STROBE_DLL_CTRL);
11312eaf5a53SBOUGH CHEN 	/* clear the reset bit on strobe dll before any setting */
11322eaf5a53SBOUGH CHEN 	writel(0, host->ioaddr + ESDHC_STROBE_DLL_CTRL);
11332eaf5a53SBOUGH CHEN 
113428b07674SHaibo Chen 	/*
113528b07674SHaibo Chen 	 * enable strobe dll ctrl and adjust the delay target
113628b07674SHaibo Chen 	 * for the uSDHC loopback read clock
113728b07674SHaibo Chen 	 */
11385bd2acdcSHaibo Chen 	if (imx_data->boarddata.strobe_dll_delay_target)
11395bd2acdcSHaibo Chen 		strobe_delay = imx_data->boarddata.strobe_dll_delay_target;
11405bd2acdcSHaibo Chen 	else
11415bd2acdcSHaibo Chen 		strobe_delay = ESDHC_STROBE_DLL_CTRL_SLV_DLY_TARGET_DEFAULT;
114228b07674SHaibo Chen 	v = ESDHC_STROBE_DLL_CTRL_ENABLE |
11432eaf5a53SBOUGH CHEN 		ESDHC_STROBE_DLL_CTRL_SLV_UPDATE_INT_DEFAULT |
11445bd2acdcSHaibo Chen 		(strobe_delay << ESDHC_STROBE_DLL_CTRL_SLV_DLY_TARGET_SHIFT);
114528b07674SHaibo Chen 	writel(v, host->ioaddr + ESDHC_STROBE_DLL_CTRL);
1146373e800bSHaibo Chen 
1147373e800bSHaibo Chen 	/* wait max 50us to get the REF/SLV lock */
1148373e800bSHaibo Chen 	ret = readl_poll_timeout(host->ioaddr + ESDHC_STROBE_DLL_STATUS, v,
1149373e800bSHaibo Chen 		((v & ESDHC_STROBE_DLL_STS_REF_LOCK) && (v & ESDHC_STROBE_DLL_STS_SLV_LOCK)), 1, 50);
1150373e800bSHaibo Chen 	if (ret == -ETIMEDOUT)
115128b07674SHaibo Chen 		dev_warn(mmc_dev(host->mmc),
1152373e800bSHaibo Chen 		"warning! HS400 strobe DLL status REF/SLV not lock in 50us, STROBE DLL status is %x!\n", v);
115328b07674SHaibo Chen }
115428b07674SHaibo Chen 
1155d9370424SHaibo Chen static void esdhc_reset_tuning(struct sdhci_host *host)
1156d9370424SHaibo Chen {
1157d9370424SHaibo Chen 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1158d9370424SHaibo Chen 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
1159d9370424SHaibo Chen 	u32 ctrl;
1160d9370424SHaibo Chen 
1161d04f8d5bSBenoît Thébaudeau 	/* Reset the tuning circuit */
1162d9370424SHaibo Chen 	if (esdhc_is_usdhc(imx_data)) {
1163d9370424SHaibo Chen 		if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING) {
1164d9370424SHaibo Chen 			ctrl = readl(host->ioaddr + ESDHC_MIX_CTRL);
1165d9370424SHaibo Chen 			ctrl &= ~ESDHC_MIX_CTRL_SMPCLK_SEL;
1166d9370424SHaibo Chen 			ctrl &= ~ESDHC_MIX_CTRL_FBCLK_SEL;
1167d9370424SHaibo Chen 			writel(ctrl, host->ioaddr + ESDHC_MIX_CTRL);
1168d9370424SHaibo Chen 			writel(0, host->ioaddr + ESDHC_TUNE_CTRL_STATUS);
1169d9370424SHaibo Chen 		} else if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) {
1170869f8a69SAdrian Hunter 			ctrl = readl(host->ioaddr + SDHCI_AUTO_CMD_STATUS);
1171d9370424SHaibo Chen 			ctrl &= ~ESDHC_MIX_CTRL_SMPCLK_SEL;
1172869f8a69SAdrian Hunter 			writel(ctrl, host->ioaddr + SDHCI_AUTO_CMD_STATUS);
1173d9370424SHaibo Chen 		}
1174d9370424SHaibo Chen 	}
1175d9370424SHaibo Chen }
1176d9370424SHaibo Chen 
1177850a29b8SRussell King static void esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned timing)
1178ad93220dSDong Aisheng {
117928b07674SHaibo Chen 	u32 m;
1180ad93220dSDong Aisheng 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1181070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
1182602519b2SDong Aisheng 	struct esdhc_platform_data *boarddata = &imx_data->boarddata;
1183ad93220dSDong Aisheng 
118428b07674SHaibo Chen 	/* disable ddr mode and disable HS400 mode */
118528b07674SHaibo Chen 	m = readl(host->ioaddr + ESDHC_MIX_CTRL);
118628b07674SHaibo Chen 	m &= ~(ESDHC_MIX_CTRL_DDREN | ESDHC_MIX_CTRL_HS400_EN);
118728b07674SHaibo Chen 	imx_data->is_ddr = 0;
118828b07674SHaibo Chen 
1189850a29b8SRussell King 	switch (timing) {
1190ad93220dSDong Aisheng 	case MMC_TIMING_UHS_SDR12:
1191ad93220dSDong Aisheng 	case MMC_TIMING_UHS_SDR25:
1192ad93220dSDong Aisheng 	case MMC_TIMING_UHS_SDR50:
1193ad93220dSDong Aisheng 	case MMC_TIMING_UHS_SDR104:
1194de0a0decSBOUGH CHEN 	case MMC_TIMING_MMC_HS:
1195429a5b45SDong Aisheng 	case MMC_TIMING_MMC_HS200:
119628b07674SHaibo Chen 		writel(m, host->ioaddr + ESDHC_MIX_CTRL);
1197ad93220dSDong Aisheng 		break;
1198ad93220dSDong Aisheng 	case MMC_TIMING_UHS_DDR50:
119969f5bf38SAisheng Dong 	case MMC_TIMING_MMC_DDR52:
120028b07674SHaibo Chen 		m |= ESDHC_MIX_CTRL_DDREN;
120128b07674SHaibo Chen 		writel(m, host->ioaddr + ESDHC_MIX_CTRL);
1202de5bdbffSDong Aisheng 		imx_data->is_ddr = 1;
1203602519b2SDong Aisheng 		if (boarddata->delay_line) {
1204602519b2SDong Aisheng 			u32 v;
1205602519b2SDong Aisheng 			v = boarddata->delay_line <<
1206602519b2SDong Aisheng 				ESDHC_DLL_OVERRIDE_VAL_SHIFT |
1207602519b2SDong Aisheng 				(1 << ESDHC_DLL_OVERRIDE_EN_SHIFT);
1208602519b2SDong Aisheng 			if (is_imx53_esdhc(imx_data))
1209602519b2SDong Aisheng 				v <<= 1;
1210602519b2SDong Aisheng 			writel(v, host->ioaddr + ESDHC_DLL_CTRL);
1211602519b2SDong Aisheng 		}
1212ad93220dSDong Aisheng 		break;
121328b07674SHaibo Chen 	case MMC_TIMING_MMC_HS400:
121428b07674SHaibo Chen 		m |= ESDHC_MIX_CTRL_DDREN | ESDHC_MIX_CTRL_HS400_EN;
121528b07674SHaibo Chen 		writel(m, host->ioaddr + ESDHC_MIX_CTRL);
121628b07674SHaibo Chen 		imx_data->is_ddr = 1;
12177ac6da26SDong Aisheng 		/* update clock after enable DDR for strobe DLL lock */
12187ac6da26SDong Aisheng 		host->ops->set_clock(host, host->clock);
121928b07674SHaibo Chen 		esdhc_set_strobe_dll(host);
122028b07674SHaibo Chen 		break;
1221d9370424SHaibo Chen 	case MMC_TIMING_LEGACY:
1222d9370424SHaibo Chen 	default:
1223d9370424SHaibo Chen 		esdhc_reset_tuning(host);
1224d9370424SHaibo Chen 		break;
1225ad93220dSDong Aisheng 	}
1226ad93220dSDong Aisheng 
1227850a29b8SRussell King 	esdhc_change_pinstate(host, timing);
1228ad93220dSDong Aisheng }
1229ad93220dSDong Aisheng 
12300718e59aSRussell King static void esdhc_reset(struct sdhci_host *host, u8 mask)
12310718e59aSRussell King {
12320718e59aSRussell King 	sdhci_reset(host, mask);
12330718e59aSRussell King 
12340718e59aSRussell King 	sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
12350718e59aSRussell King 	sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
12360718e59aSRussell King }
12370718e59aSRussell King 
123810fd0ad9SAisheng Dong static unsigned int esdhc_get_max_timeout_count(struct sdhci_host *host)
123910fd0ad9SAisheng Dong {
124010fd0ad9SAisheng Dong 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1241070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
124210fd0ad9SAisheng Dong 
1243d04f8d5bSBenoît Thébaudeau 	/* Doc Erratum: the uSDHC actual maximum timeout count is 1 << 29 */
12442fb0b02bSHaibo Chen 	return esdhc_is_usdhc(imx_data) ? 1 << 29 : 1 << 27;
124510fd0ad9SAisheng Dong }
124610fd0ad9SAisheng Dong 
1247e33eb8e2SAisheng Dong static void esdhc_set_timeout(struct sdhci_host *host, struct mmc_command *cmd)
1248e33eb8e2SAisheng Dong {
1249e33eb8e2SAisheng Dong 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1250070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
1251e33eb8e2SAisheng Dong 
1252e33eb8e2SAisheng Dong 	/* use maximum timeout counter */
1253a215186dSHaibo Chen 	esdhc_clrset_le(host, ESDHC_SYS_CTRL_DTOCV_MASK,
1254a215186dSHaibo Chen 			esdhc_is_usdhc(imx_data) ? 0xF : 0xE,
1255e33eb8e2SAisheng Dong 			SDHCI_TIMEOUT_CONTROL);
1256e33eb8e2SAisheng Dong }
1257e33eb8e2SAisheng Dong 
1258bb6e3581SBOUGH CHEN static u32 esdhc_cqhci_irq(struct sdhci_host *host, u32 intmask)
1259bb6e3581SBOUGH CHEN {
1260bb6e3581SBOUGH CHEN 	int cmd_error = 0;
1261bb6e3581SBOUGH CHEN 	int data_error = 0;
1262bb6e3581SBOUGH CHEN 
1263bb6e3581SBOUGH CHEN 	if (!sdhci_cqe_irq(host, intmask, &cmd_error, &data_error))
1264bb6e3581SBOUGH CHEN 		return intmask;
1265bb6e3581SBOUGH CHEN 
1266bb6e3581SBOUGH CHEN 	cqhci_irq(host->mmc, intmask, cmd_error, data_error);
1267bb6e3581SBOUGH CHEN 
1268bb6e3581SBOUGH CHEN 	return 0;
1269bb6e3581SBOUGH CHEN }
1270bb6e3581SBOUGH CHEN 
12716e9fd28eSDong Aisheng static struct sdhci_ops sdhci_esdhc_ops = {
1272e149860dSRichard Zhu 	.read_l = esdhc_readl_le,
12730c6d49ceSWolfram Sang 	.read_w = esdhc_readw_le,
127477da3da0SAaron Brice 	.read_b = esdhc_readb_le,
1275e149860dSRichard Zhu 	.write_l = esdhc_writel_le,
12760c6d49ceSWolfram Sang 	.write_w = esdhc_writew_le,
12770c6d49ceSWolfram Sang 	.write_b = esdhc_writeb_le,
12788ba9580aSLucas Stach 	.set_clock = esdhc_pltfm_set_clock,
12790ddf03c9SLucas Stach 	.get_max_clock = esdhc_pltfm_get_max_clock,
12800c6d49ceSWolfram Sang 	.get_min_clock = esdhc_pltfm_get_min_clock,
128110fd0ad9SAisheng Dong 	.get_max_timeout_count = esdhc_get_max_timeout_count,
1282913413c3SShawn Guo 	.get_ro = esdhc_pltfm_get_ro,
1283e33eb8e2SAisheng Dong 	.set_timeout = esdhc_set_timeout,
12842317f56cSRussell King 	.set_bus_width = esdhc_pltfm_set_bus_width,
1285ad93220dSDong Aisheng 	.set_uhs_signaling = esdhc_set_uhs_signaling,
12860718e59aSRussell King 	.reset = esdhc_reset,
1287bb6e3581SBOUGH CHEN 	.irq = esdhc_cqhci_irq,
12883722c74cSHaibo Chen 	.dump_vendor_regs = esdhc_dump_debug_regs,
12890c6d49ceSWolfram Sang };
12900c6d49ceSWolfram Sang 
12911db5eebfSLars-Peter Clausen static const struct sdhci_pltfm_data sdhci_esdhc_imx_pdata = {
129297e4ba6aSRichard Zhu 	.quirks = ESDHC_DEFAULT_QUIRKS | SDHCI_QUIRK_NO_HISPD_BIT
129397e4ba6aSRichard Zhu 			| SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC
129497e4ba6aSRichard Zhu 			| SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC
129585d6509dSShawn Guo 			| SDHCI_QUIRK_BROKEN_CARD_DETECTION,
129685d6509dSShawn Guo 	.ops = &sdhci_esdhc_ops,
129785d6509dSShawn Guo };
129885d6509dSShawn Guo 
1299f3f5cf3dSDong Aisheng static void sdhci_esdhc_imx_hwinit(struct sdhci_host *host)
1300f3f5cf3dSDong Aisheng {
1301f3f5cf3dSDong Aisheng 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1302f3f5cf3dSDong Aisheng 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
1303982cf37dSHaibo Chen 	struct cqhci_host *cq_host = host->mmc->cqe_private;
13042b16cf32SDong Aisheng 	int tmp;
1305f3f5cf3dSDong Aisheng 
1306f3f5cf3dSDong Aisheng 	if (esdhc_is_usdhc(imx_data)) {
1307f3f5cf3dSDong Aisheng 		/*
1308f3f5cf3dSDong Aisheng 		 * The imx6q ROM code will change the default watermark
1309f3f5cf3dSDong Aisheng 		 * level setting to something insane.  Change it back here.
1310f3f5cf3dSDong Aisheng 		 */
1311f3f5cf3dSDong Aisheng 		writel(ESDHC_WTMK_DEFAULT_VAL, host->ioaddr + ESDHC_WTMK_LVL);
1312f3f5cf3dSDong Aisheng 
1313f3f5cf3dSDong Aisheng 		/*
1314f3f5cf3dSDong Aisheng 		 * ROM code will change the bit burst_length_enable setting
1315d04f8d5bSBenoît Thébaudeau 		 * to zero if this usdhc is chosen to boot system. Change
1316f3f5cf3dSDong Aisheng 		 * it back here, otherwise it will impact the performance a
1317f3f5cf3dSDong Aisheng 		 * lot. This bit is used to enable/disable the burst length
1318d04f8d5bSBenoît Thébaudeau 		 * for the external AHB2AXI bridge. It's useful especially
1319f3f5cf3dSDong Aisheng 		 * for INCR transfer because without burst length indicator,
1320f3f5cf3dSDong Aisheng 		 * the AHB2AXI bridge does not know the burst length in
1321f3f5cf3dSDong Aisheng 		 * advance. And without burst length indicator, AHB INCR
1322f3f5cf3dSDong Aisheng 		 * transfer can only be converted to singles on the AXI side.
1323f3f5cf3dSDong Aisheng 		 */
1324f3f5cf3dSDong Aisheng 		writel(readl(host->ioaddr + SDHCI_HOST_CONTROL)
1325f3f5cf3dSDong Aisheng 			| ESDHC_BURST_LEN_EN_INCR,
1326f3f5cf3dSDong Aisheng 			host->ioaddr + SDHCI_HOST_CONTROL);
1327e30be063SBOUGH CHEN 
1328f3f5cf3dSDong Aisheng 		/*
1329d04f8d5bSBenoît Thébaudeau 		 * erratum ESDHC_FLAG_ERR004536 fix for MX6Q TO1.2 and MX6DL
1330f3f5cf3dSDong Aisheng 		 * TO1.1, it's harmless for MX6SL
1331f3f5cf3dSDong Aisheng 		 */
1332e30be063SBOUGH CHEN 		writel(readl(host->ioaddr + 0x6c) & ~BIT(7),
1333f3f5cf3dSDong Aisheng 			host->ioaddr + 0x6c);
1334f3f5cf3dSDong Aisheng 
1335f3f5cf3dSDong Aisheng 		/* disable DLL_CTRL delay line settings */
1336f3f5cf3dSDong Aisheng 		writel(0x0, host->ioaddr + ESDHC_DLL_CTRL);
13372b16cf32SDong Aisheng 
1338bcdb5301SBOUGH CHEN 		/*
1339bcdb5301SBOUGH CHEN 		 * For the case of command with busy, if set the bit
1340bcdb5301SBOUGH CHEN 		 * ESDHC_VEND_SPEC2_EN_BUSY_IRQ, USDHC will generate a
1341bcdb5301SBOUGH CHEN 		 * transfer complete interrupt when busy is deasserted.
1342bcdb5301SBOUGH CHEN 		 * When CQHCI use DCMD to send a CMD need R1b respons,
1343bcdb5301SBOUGH CHEN 		 * CQHCI require to set ESDHC_VEND_SPEC2_EN_BUSY_IRQ,
1344bcdb5301SBOUGH CHEN 		 * otherwise DCMD will always meet timeout waiting for
1345bcdb5301SBOUGH CHEN 		 * hardware interrupt issue.
1346bcdb5301SBOUGH CHEN 		 */
1347bcdb5301SBOUGH CHEN 		if (imx_data->socdata->flags & ESDHC_FLAG_CQHCI) {
1348bcdb5301SBOUGH CHEN 			tmp = readl(host->ioaddr + ESDHC_VEND_SPEC2);
1349bcdb5301SBOUGH CHEN 			tmp |= ESDHC_VEND_SPEC2_EN_BUSY_IRQ;
1350bcdb5301SBOUGH CHEN 			writel(tmp, host->ioaddr + ESDHC_VEND_SPEC2);
1351bcdb5301SBOUGH CHEN 
1352bcdb5301SBOUGH CHEN 			host->quirks &= ~SDHCI_QUIRK_NO_BUSY_IRQ;
1353bcdb5301SBOUGH CHEN 		}
1354bcdb5301SBOUGH CHEN 
13552b16cf32SDong Aisheng 		if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) {
13562b16cf32SDong Aisheng 			tmp = readl(host->ioaddr + ESDHC_TUNING_CTRL);
13572b16cf32SDong Aisheng 			tmp |= ESDHC_STD_TUNING_EN |
13582b16cf32SDong Aisheng 				ESDHC_TUNING_START_TAP_DEFAULT;
13592b16cf32SDong Aisheng 			if (imx_data->boarddata.tuning_start_tap) {
13602b16cf32SDong Aisheng 				tmp &= ~ESDHC_TUNING_START_TAP_MASK;
13612b16cf32SDong Aisheng 				tmp |= imx_data->boarddata.tuning_start_tap;
13622b16cf32SDong Aisheng 			}
13632b16cf32SDong Aisheng 
13642b16cf32SDong Aisheng 			if (imx_data->boarddata.tuning_step) {
13652b16cf32SDong Aisheng 				tmp &= ~ESDHC_TUNING_STEP_MASK;
13662b16cf32SDong Aisheng 				tmp |= imx_data->boarddata.tuning_step
13672b16cf32SDong Aisheng 					<< ESDHC_TUNING_STEP_SHIFT;
13682b16cf32SDong Aisheng 			}
136916e40e5bSHaibo Chen 
137016e40e5bSHaibo Chen 			/* Disable the CMD CRC check for tuning, if not, need to
137116e40e5bSHaibo Chen 			 * add some delay after every tuning command, because
137216e40e5bSHaibo Chen 			 * hardware standard tuning logic will directly go to next
137316e40e5bSHaibo Chen 			 * step once it detect the CMD CRC error, will not wait for
137416e40e5bSHaibo Chen 			 * the card side to finally send out the tuning data, trigger
137516e40e5bSHaibo Chen 			 * the buffer read ready interrupt immediately. If usdhc send
137616e40e5bSHaibo Chen 			 * the next tuning command some eMMC card will stuck, can't
137716e40e5bSHaibo Chen 			 * response, block the tuning procedure or the first command
137816e40e5bSHaibo Chen 			 * after the whole tuning procedure always can't get any response.
137916e40e5bSHaibo Chen 			 */
138016e40e5bSHaibo Chen 			 tmp |= ESDHC_TUNING_CMD_CRC_CHECK_DISABLE;
13812b16cf32SDong Aisheng 			writel(tmp, host->ioaddr + ESDHC_TUNING_CTRL);
1382a98c557eSBOUGH CHEN 		} else if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING) {
1383a98c557eSBOUGH CHEN 			/*
1384a98c557eSBOUGH CHEN 			 * ESDHC_STD_TUNING_EN may be configed in bootloader
1385a98c557eSBOUGH CHEN 			 * or ROM code, so clear this bit here to make sure
1386a98c557eSBOUGH CHEN 			 * the manual tuning can work.
1387a98c557eSBOUGH CHEN 			 */
1388a98c557eSBOUGH CHEN 			tmp = readl(host->ioaddr + ESDHC_TUNING_CTRL);
1389a98c557eSBOUGH CHEN 			tmp &= ~ESDHC_STD_TUNING_EN;
1390a98c557eSBOUGH CHEN 			writel(tmp, host->ioaddr + ESDHC_TUNING_CTRL);
13912b16cf32SDong Aisheng 		}
1392982cf37dSHaibo Chen 
1393982cf37dSHaibo Chen 		/*
1394982cf37dSHaibo Chen 		 * On i.MX8MM, we are running Dual Linux OS, with 1st Linux using SD Card
1395982cf37dSHaibo Chen 		 * as rootfs storage, 2nd Linux using eMMC as rootfs storage. We let the
1396982cf37dSHaibo Chen 		 * the 1st linux configure power/clock for the 2nd Linux.
1397982cf37dSHaibo Chen 		 *
1398982cf37dSHaibo Chen 		 * When the 2nd Linux is booting into rootfs stage, we let the 1st Linux
1399982cf37dSHaibo Chen 		 * to destroy the 2nd linux, then restart the 2nd linux, we met SDHCI dump.
1400982cf37dSHaibo Chen 		 * After we clear the pending interrupt and halt CQCTL, issue gone.
1401982cf37dSHaibo Chen 		 */
1402982cf37dSHaibo Chen 		if (cq_host) {
1403982cf37dSHaibo Chen 			tmp = cqhci_readl(cq_host, CQHCI_IS);
1404982cf37dSHaibo Chen 			cqhci_writel(cq_host, tmp, CQHCI_IS);
1405982cf37dSHaibo Chen 			cqhci_writel(cq_host, CQHCI_HALT, CQHCI_CTL);
1406982cf37dSHaibo Chen 		}
1407f3f5cf3dSDong Aisheng 	}
1408f3f5cf3dSDong Aisheng }
1409f3f5cf3dSDong Aisheng 
1410bb6e3581SBOUGH CHEN static void esdhc_cqe_enable(struct mmc_host *mmc)
1411bb6e3581SBOUGH CHEN {
1412bb6e3581SBOUGH CHEN 	struct sdhci_host *host = mmc_priv(mmc);
141385236d2bSBOUGH CHEN 	struct cqhci_host *cq_host = mmc->cqe_private;
1414bb6e3581SBOUGH CHEN 	u32 reg;
1415bb6e3581SBOUGH CHEN 	u16 mode;
1416bb6e3581SBOUGH CHEN 	int count = 10;
1417bb6e3581SBOUGH CHEN 
1418bb6e3581SBOUGH CHEN 	/*
1419bb6e3581SBOUGH CHEN 	 * CQE gets stuck if it sees Buffer Read Enable bit set, which can be
1420bb6e3581SBOUGH CHEN 	 * the case after tuning, so ensure the buffer is drained.
1421bb6e3581SBOUGH CHEN 	 */
1422bb6e3581SBOUGH CHEN 	reg = sdhci_readl(host, SDHCI_PRESENT_STATE);
1423bb6e3581SBOUGH CHEN 	while (reg & SDHCI_DATA_AVAILABLE) {
1424bb6e3581SBOUGH CHEN 		sdhci_readl(host, SDHCI_BUFFER);
1425bb6e3581SBOUGH CHEN 		reg = sdhci_readl(host, SDHCI_PRESENT_STATE);
1426bb6e3581SBOUGH CHEN 		if (count-- == 0) {
1427bb6e3581SBOUGH CHEN 			dev_warn(mmc_dev(host->mmc),
1428bb6e3581SBOUGH CHEN 				"CQE may get stuck because the Buffer Read Enable bit is set\n");
1429bb6e3581SBOUGH CHEN 			break;
1430bb6e3581SBOUGH CHEN 		}
1431bb6e3581SBOUGH CHEN 		mdelay(1);
1432bb6e3581SBOUGH CHEN 	}
1433bb6e3581SBOUGH CHEN 
1434bb6e3581SBOUGH CHEN 	/*
1435bb6e3581SBOUGH CHEN 	 * Runtime resume will reset the entire host controller, which
1436bb6e3581SBOUGH CHEN 	 * will also clear the DMAEN/BCEN of register ESDHC_MIX_CTRL.
1437bb6e3581SBOUGH CHEN 	 * Here set DMAEN and BCEN when enable CMDQ.
1438bb6e3581SBOUGH CHEN 	 */
1439bb6e3581SBOUGH CHEN 	mode = sdhci_readw(host, SDHCI_TRANSFER_MODE);
1440bb6e3581SBOUGH CHEN 	if (host->flags & SDHCI_REQ_USE_DMA)
1441bb6e3581SBOUGH CHEN 		mode |= SDHCI_TRNS_DMA;
1442bb6e3581SBOUGH CHEN 	if (!(host->quirks2 & SDHCI_QUIRK2_SUPPORT_SINGLE))
1443bb6e3581SBOUGH CHEN 		mode |= SDHCI_TRNS_BLK_CNT_EN;
1444bb6e3581SBOUGH CHEN 	sdhci_writew(host, mode, SDHCI_TRANSFER_MODE);
1445bb6e3581SBOUGH CHEN 
144685236d2bSBOUGH CHEN 	/*
144785236d2bSBOUGH CHEN 	 * Though Runtime resume reset the entire host controller,
144885236d2bSBOUGH CHEN 	 * but do not impact the CQHCI side, need to clear the
144985236d2bSBOUGH CHEN 	 * HALT bit, avoid CQHCI stuck in the first request when
145085236d2bSBOUGH CHEN 	 * system resume back.
145185236d2bSBOUGH CHEN 	 */
145285236d2bSBOUGH CHEN 	cqhci_writel(cq_host, 0, CQHCI_CTL);
145385236d2bSBOUGH CHEN 	if (cqhci_readl(cq_host, CQHCI_CTL) && CQHCI_HALT)
145485236d2bSBOUGH CHEN 		dev_err(mmc_dev(host->mmc),
145585236d2bSBOUGH CHEN 			"failed to exit halt state when enable CQE\n");
145685236d2bSBOUGH CHEN 
145785236d2bSBOUGH CHEN 
1458bb6e3581SBOUGH CHEN 	sdhci_cqe_enable(mmc);
1459bb6e3581SBOUGH CHEN }
1460bb6e3581SBOUGH CHEN 
1461bb6e3581SBOUGH CHEN static void esdhc_sdhci_dumpregs(struct mmc_host *mmc)
1462bb6e3581SBOUGH CHEN {
1463bb6e3581SBOUGH CHEN 	sdhci_dumpregs(mmc_priv(mmc));
1464bb6e3581SBOUGH CHEN }
1465bb6e3581SBOUGH CHEN 
1466bb6e3581SBOUGH CHEN static const struct cqhci_host_ops esdhc_cqhci_ops = {
1467bb6e3581SBOUGH CHEN 	.enable		= esdhc_cqe_enable,
1468bb6e3581SBOUGH CHEN 	.disable	= sdhci_cqe_disable,
1469bb6e3581SBOUGH CHEN 	.dumpregs	= esdhc_sdhci_dumpregs,
1470bb6e3581SBOUGH CHEN };
1471bb6e3581SBOUGH CHEN 
1472abfafc2dSShawn Guo #ifdef CONFIG_OF
1473c3be1efdSBill Pemberton static int
1474abfafc2dSShawn Guo sdhci_esdhc_imx_probe_dt(struct platform_device *pdev,
147507bf2b54SSascha Hauer 			 struct sdhci_host *host,
147691fa4252SDong Aisheng 			 struct pltfm_imx_data *imx_data)
1477abfafc2dSShawn Guo {
1478abfafc2dSShawn Guo 	struct device_node *np = pdev->dev.of_node;
147991fa4252SDong Aisheng 	struct esdhc_platform_data *boarddata = &imx_data->boarddata;
14804800e87aSDong Aisheng 	int ret;
1481abfafc2dSShawn Guo 
1482abfafc2dSShawn Guo 	if (of_get_property(np, "fsl,wp-controller", NULL))
1483abfafc2dSShawn Guo 		boarddata->wp_type = ESDHC_WP_CONTROLLER;
1484abfafc2dSShawn Guo 
148574ff81e1SLinus Walleij 	/*
148674ff81e1SLinus Walleij 	 * If we have this property, then activate WP check.
148774ff81e1SLinus Walleij 	 * Retrieveing and requesting the actual WP GPIO will happen
148874ff81e1SLinus Walleij 	 * in the call to mmc_of_parse().
148974ff81e1SLinus Walleij 	 */
149074ff81e1SLinus Walleij 	if (of_property_read_bool(np, "wp-gpios"))
1491abfafc2dSShawn Guo 		boarddata->wp_type = ESDHC_WP_GPIO;
1492abfafc2dSShawn Guo 
1493d407e30bSHaibo Chen 	of_property_read_u32(np, "fsl,tuning-step", &boarddata->tuning_step);
1494d87fc966SDong Aisheng 	of_property_read_u32(np, "fsl,tuning-start-tap",
1495d87fc966SDong Aisheng 			     &boarddata->tuning_start_tap);
1496d407e30bSHaibo Chen 
14975bd2acdcSHaibo Chen 	of_property_read_u32(np, "fsl,strobe-dll-delay-target",
14985bd2acdcSHaibo Chen 				&boarddata->strobe_dll_delay_target);
1499ad93220dSDong Aisheng 	if (of_find_property(np, "no-1-8-v", NULL))
150086f495c5SStefan Agner 		host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V;
1501ad93220dSDong Aisheng 
1502602519b2SDong Aisheng 	if (of_property_read_u32(np, "fsl,delay-line", &boarddata->delay_line))
1503602519b2SDong Aisheng 		boarddata->delay_line = 0;
1504602519b2SDong Aisheng 
150507bf2b54SSascha Hauer 	mmc_of_parse_voltage(np, &host->ocr_mask);
150607bf2b54SSascha Hauer 
15072480b720SUlf Hansson 	if (esdhc_is_usdhc(imx_data)) {
150891fa4252SDong Aisheng 		imx_data->pins_100mhz = pinctrl_lookup_state(imx_data->pinctrl,
150991fa4252SDong Aisheng 						ESDHC_PINCTRL_STATE_100MHZ);
151091fa4252SDong Aisheng 		imx_data->pins_200mhz = pinctrl_lookup_state(imx_data->pinctrl,
151191fa4252SDong Aisheng 						ESDHC_PINCTRL_STATE_200MHZ);
151291fa4252SDong Aisheng 	}
151391fa4252SDong Aisheng 
151415064119SFabio Estevam 	/* call to generic mmc_of_parse to support additional capabilities */
15154800e87aSDong Aisheng 	ret = mmc_of_parse(host->mmc);
15164800e87aSDong Aisheng 	if (ret)
15174800e87aSDong Aisheng 		return ret;
15184800e87aSDong Aisheng 
1519287980e4SArnd Bergmann 	if (mmc_gpio_get_cd(host->mmc) >= 0)
15204800e87aSDong Aisheng 		host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION;
15214800e87aSDong Aisheng 
15224800e87aSDong Aisheng 	return 0;
1523abfafc2dSShawn Guo }
1524abfafc2dSShawn Guo #else
1525abfafc2dSShawn Guo static inline int
1526abfafc2dSShawn Guo sdhci_esdhc_imx_probe_dt(struct platform_device *pdev,
152707bf2b54SSascha Hauer 			 struct sdhci_host *host,
152891fa4252SDong Aisheng 			 struct pltfm_imx_data *imx_data)
1529abfafc2dSShawn Guo {
1530abfafc2dSShawn Guo 	return -ENODEV;
1531abfafc2dSShawn Guo }
1532abfafc2dSShawn Guo #endif
1533abfafc2dSShawn Guo 
153491fa4252SDong Aisheng static int sdhci_esdhc_imx_probe_nondt(struct platform_device *pdev,
153591fa4252SDong Aisheng 			 struct sdhci_host *host,
153691fa4252SDong Aisheng 			 struct pltfm_imx_data *imx_data)
153791fa4252SDong Aisheng {
153891fa4252SDong Aisheng 	struct esdhc_platform_data *boarddata = &imx_data->boarddata;
153991fa4252SDong Aisheng 	int err;
154091fa4252SDong Aisheng 
154191fa4252SDong Aisheng 	if (!host->mmc->parent->platform_data) {
154291fa4252SDong Aisheng 		dev_err(mmc_dev(host->mmc), "no board data!\n");
154391fa4252SDong Aisheng 		return -EINVAL;
154491fa4252SDong Aisheng 	}
154591fa4252SDong Aisheng 
154691fa4252SDong Aisheng 	imx_data->boarddata = *((struct esdhc_platform_data *)
154791fa4252SDong Aisheng 				host->mmc->parent->platform_data);
154891fa4252SDong Aisheng 	/* write_protect */
154991fa4252SDong Aisheng 	if (boarddata->wp_type == ESDHC_WP_GPIO) {
15509073d10bSMichał Mirosław 		host->mmc->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH;
15519073d10bSMichał Mirosław 
1552d0052ad9SMichał Mirosław 		err = mmc_gpiod_request_ro(host->mmc, "wp", 0, 0);
155391fa4252SDong Aisheng 		if (err) {
155491fa4252SDong Aisheng 			dev_err(mmc_dev(host->mmc),
155591fa4252SDong Aisheng 				"failed to request write-protect gpio!\n");
155691fa4252SDong Aisheng 			return err;
155791fa4252SDong Aisheng 		}
155891fa4252SDong Aisheng 	}
155991fa4252SDong Aisheng 
156091fa4252SDong Aisheng 	/* card_detect */
156191fa4252SDong Aisheng 	switch (boarddata->cd_type) {
156291fa4252SDong Aisheng 	case ESDHC_CD_GPIO:
1563d0052ad9SMichał Mirosław 		err = mmc_gpiod_request_cd(host->mmc, "cd", 0, false, 0);
156491fa4252SDong Aisheng 		if (err) {
156591fa4252SDong Aisheng 			dev_err(mmc_dev(host->mmc),
156691fa4252SDong Aisheng 				"failed to request card-detect gpio!\n");
156791fa4252SDong Aisheng 			return err;
156891fa4252SDong Aisheng 		}
1569df561f66SGustavo A. R. Silva 		fallthrough;
157091fa4252SDong Aisheng 
157191fa4252SDong Aisheng 	case ESDHC_CD_CONTROLLER:
157291fa4252SDong Aisheng 		/* we have a working card_detect back */
157391fa4252SDong Aisheng 		host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION;
157491fa4252SDong Aisheng 		break;
157591fa4252SDong Aisheng 
157691fa4252SDong Aisheng 	case ESDHC_CD_PERMANENT:
157791fa4252SDong Aisheng 		host->mmc->caps |= MMC_CAP_NONREMOVABLE;
157891fa4252SDong Aisheng 		break;
157991fa4252SDong Aisheng 
158091fa4252SDong Aisheng 	case ESDHC_CD_NONE:
158191fa4252SDong Aisheng 		break;
158291fa4252SDong Aisheng 	}
158391fa4252SDong Aisheng 
158491fa4252SDong Aisheng 	switch (boarddata->max_bus_width) {
158591fa4252SDong Aisheng 	case 8:
158691fa4252SDong Aisheng 		host->mmc->caps |= MMC_CAP_8_BIT_DATA | MMC_CAP_4_BIT_DATA;
158791fa4252SDong Aisheng 		break;
158891fa4252SDong Aisheng 	case 4:
158991fa4252SDong Aisheng 		host->mmc->caps |= MMC_CAP_4_BIT_DATA;
159091fa4252SDong Aisheng 		break;
159191fa4252SDong Aisheng 	case 1:
159291fa4252SDong Aisheng 	default:
159391fa4252SDong Aisheng 		host->quirks |= SDHCI_QUIRK_FORCE_1_BIT_DATA;
159491fa4252SDong Aisheng 		break;
159591fa4252SDong Aisheng 	}
159691fa4252SDong Aisheng 
159791fa4252SDong Aisheng 	return 0;
159891fa4252SDong Aisheng }
159991fa4252SDong Aisheng 
1600c3be1efdSBill Pemberton static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
160195f25efeSWolfram Sang {
1602abfafc2dSShawn Guo 	const struct of_device_id *of_id =
1603abfafc2dSShawn Guo 			of_match_device(imx_esdhc_dt_ids, &pdev->dev);
160485d6509dSShawn Guo 	struct sdhci_pltfm_host *pltfm_host;
160585d6509dSShawn Guo 	struct sdhci_host *host;
1606bb6e3581SBOUGH CHEN 	struct cqhci_host *cq_host;
16070c6d49ceSWolfram Sang 	int err;
1608e149860dSRichard Zhu 	struct pltfm_imx_data *imx_data;
160995f25efeSWolfram Sang 
1610070e6d3fSJisheng Zhang 	host = sdhci_pltfm_init(pdev, &sdhci_esdhc_imx_pdata,
1611070e6d3fSJisheng Zhang 				sizeof(*imx_data));
161285d6509dSShawn Guo 	if (IS_ERR(host))
161385d6509dSShawn Guo 		return PTR_ERR(host);
161485d6509dSShawn Guo 
161585d6509dSShawn Guo 	pltfm_host = sdhci_priv(host);
161685d6509dSShawn Guo 
1617070e6d3fSJisheng Zhang 	imx_data = sdhci_pltfm_priv(pltfm_host);
161857ed3314SShawn Guo 
1619f47c4bbfSShawn Guo 	imx_data->socdata = of_id ? of_id->data : (struct esdhc_soc_data *)
16203770ee8fSShawn Guo 						  pdev->id_entry->driver_data;
162185d6509dSShawn Guo 
16221c4989b0SBOUGH CHEN 	if (imx_data->socdata->flags & ESDHC_FLAG_PMQOS)
1623d1b98305SRafael J. Wysocki 		cpu_latency_qos_add_request(&imx_data->pm_qos_req, 0);
16241c4989b0SBOUGH CHEN 
162552dac615SSascha Hauer 	imx_data->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
162652dac615SSascha Hauer 	if (IS_ERR(imx_data->clk_ipg)) {
162752dac615SSascha Hauer 		err = PTR_ERR(imx_data->clk_ipg);
1628e3af31c6SShawn Guo 		goto free_sdhci;
162995f25efeSWolfram Sang 	}
163052dac615SSascha Hauer 
163152dac615SSascha Hauer 	imx_data->clk_ahb = devm_clk_get(&pdev->dev, "ahb");
163252dac615SSascha Hauer 	if (IS_ERR(imx_data->clk_ahb)) {
163352dac615SSascha Hauer 		err = PTR_ERR(imx_data->clk_ahb);
1634e3af31c6SShawn Guo 		goto free_sdhci;
163552dac615SSascha Hauer 	}
163652dac615SSascha Hauer 
163752dac615SSascha Hauer 	imx_data->clk_per = devm_clk_get(&pdev->dev, "per");
163852dac615SSascha Hauer 	if (IS_ERR(imx_data->clk_per)) {
163952dac615SSascha Hauer 		err = PTR_ERR(imx_data->clk_per);
1640e3af31c6SShawn Guo 		goto free_sdhci;
164152dac615SSascha Hauer 	}
164252dac615SSascha Hauer 
164352dac615SSascha Hauer 	pltfm_host->clk = imx_data->clk_per;
1644a974862fSDong Aisheng 	pltfm_host->clock = clk_get_rate(pltfm_host->clk);
164517b1eb7fSFabio Estevam 	err = clk_prepare_enable(imx_data->clk_per);
164617b1eb7fSFabio Estevam 	if (err)
164717b1eb7fSFabio Estevam 		goto free_sdhci;
164817b1eb7fSFabio Estevam 	err = clk_prepare_enable(imx_data->clk_ipg);
164917b1eb7fSFabio Estevam 	if (err)
165017b1eb7fSFabio Estevam 		goto disable_per_clk;
165117b1eb7fSFabio Estevam 	err = clk_prepare_enable(imx_data->clk_ahb);
165217b1eb7fSFabio Estevam 	if (err)
165317b1eb7fSFabio Estevam 		goto disable_ipg_clk;
165495f25efeSWolfram Sang 
1655ad93220dSDong Aisheng 	imx_data->pinctrl = devm_pinctrl_get(&pdev->dev);
1656*9e70ff99SHaibo Chen 	if (IS_ERR(imx_data->pinctrl))
1657b62eee9fSHaibo Chen 		dev_warn(mmc_dev(host->mmc), "could not get pinctrl\n");
1658e62d8b8fSDong Aisheng 
165969ed60e0SDong Aisheng 	if (esdhc_is_usdhc(imx_data)) {
166069ed60e0SDong Aisheng 		host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN;
166109c8192bSStefan Agner 		host->mmc->caps |= MMC_CAP_1_8V_DDR | MMC_CAP_3_3V_DDR;
1662f6140462SHaibo Chen 
1663f6140462SHaibo Chen 		/* GPIO CD can be set as a wakeup source */
1664f6140462SHaibo Chen 		host->mmc->caps |= MMC_CAP_CD_WAKE;
1665f6140462SHaibo Chen 
16664245afffSDong Aisheng 		if (!(imx_data->socdata->flags & ESDHC_FLAG_HS200))
16674245afffSDong Aisheng 			host->quirks2 |= SDHCI_QUIRK2_BROKEN_HS200;
1668a75dcbf4SDong Aisheng 
1669a75dcbf4SDong Aisheng 		/* clear tuning bits in case ROM has set it already */
1670a75dcbf4SDong Aisheng 		writel(0x0, host->ioaddr + ESDHC_MIX_CTRL);
1671869f8a69SAdrian Hunter 		writel(0x0, host->ioaddr + SDHCI_AUTO_CMD_STATUS);
1672a75dcbf4SDong Aisheng 		writel(0x0, host->ioaddr + ESDHC_TUNE_CTRL_STATUS);
1673de3e1dd0SBOUGH CHEN 
1674de3e1dd0SBOUGH CHEN 		/*
1675de3e1dd0SBOUGH CHEN 		 * Link usdhc specific mmc_host_ops execute_tuning function,
1676de3e1dd0SBOUGH CHEN 		 * to replace the standard one in sdhci_ops.
1677de3e1dd0SBOUGH CHEN 		 */
1678de3e1dd0SBOUGH CHEN 		host->mmc_host_ops.execute_tuning = usdhc_execute_tuning;
167969ed60e0SDong Aisheng 	}
1680f750ba9bSShawn Guo 
16816e9fd28eSDong Aisheng 	if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING)
16826e9fd28eSDong Aisheng 		sdhci_esdhc_ops.platform_execute_tuning =
16836e9fd28eSDong Aisheng 					esdhc_executing_tuning;
16848b2bb0adSDong Aisheng 
168518094430SDong Aisheng 	if (imx_data->socdata->flags & ESDHC_FLAG_ERR004536)
168618094430SDong Aisheng 		host->quirks |= SDHCI_QUIRK_BROKEN_ADMA;
168718094430SDong Aisheng 
168828b07674SHaibo Chen 	if (imx_data->socdata->flags & ESDHC_FLAG_HS400)
168928b07674SHaibo Chen 		host->quirks2 |= SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400;
169028b07674SHaibo Chen 
169174898cbcSHaibo Chen 	if (imx_data->socdata->flags & ESDHC_FLAG_BROKEN_AUTO_CMD23)
169274898cbcSHaibo Chen 		host->quirks2 |= SDHCI_QUIRK2_ACMD23_BROKEN;
169374898cbcSHaibo Chen 
1694029e2476SBOUGH CHEN 	if (imx_data->socdata->flags & ESDHC_FLAG_HS400_ES) {
1695029e2476SBOUGH CHEN 		host->mmc->caps2 |= MMC_CAP2_HS400_ES;
1696029e2476SBOUGH CHEN 		host->mmc_host_ops.hs400_enhanced_strobe =
1697029e2476SBOUGH CHEN 					esdhc_hs400_enhanced_strobe;
1698029e2476SBOUGH CHEN 	}
1699029e2476SBOUGH CHEN 
1700bb6e3581SBOUGH CHEN 	if (imx_data->socdata->flags & ESDHC_FLAG_CQHCI) {
1701bcdb5301SBOUGH CHEN 		host->mmc->caps2 |= MMC_CAP2_CQE | MMC_CAP2_CQE_DCMD;
1702bb6e3581SBOUGH CHEN 		cq_host = devm_kzalloc(&pdev->dev, sizeof(*cq_host), GFP_KERNEL);
17039a633f3bSWei Yongjun 		if (!cq_host) {
17049a633f3bSWei Yongjun 			err = -ENOMEM;
1705bb6e3581SBOUGH CHEN 			goto disable_ahb_clk;
1706bb6e3581SBOUGH CHEN 		}
1707bb6e3581SBOUGH CHEN 
1708bb6e3581SBOUGH CHEN 		cq_host->mmio = host->ioaddr + ESDHC_CQHCI_ADDR_OFFSET;
1709bb6e3581SBOUGH CHEN 		cq_host->ops = &esdhc_cqhci_ops;
1710bb6e3581SBOUGH CHEN 
1711bb6e3581SBOUGH CHEN 		err = cqhci_init(cq_host, host->mmc, false);
1712bb6e3581SBOUGH CHEN 		if (err)
1713bb6e3581SBOUGH CHEN 			goto disable_ahb_clk;
1714bb6e3581SBOUGH CHEN 	}
1715bb6e3581SBOUGH CHEN 
171691fa4252SDong Aisheng 	if (of_id)
171791fa4252SDong Aisheng 		err = sdhci_esdhc_imx_probe_dt(pdev, host, imx_data);
171891fa4252SDong Aisheng 	else
171991fa4252SDong Aisheng 		err = sdhci_esdhc_imx_probe_nondt(pdev, host, imx_data);
172091fa4252SDong Aisheng 	if (err)
172117b1eb7fSFabio Estevam 		goto disable_ahb_clk;
1722ad93220dSDong Aisheng 
1723f3f5cf3dSDong Aisheng 	sdhci_esdhc_imx_hwinit(host);
1724f3f5cf3dSDong Aisheng 
172585d6509dSShawn Guo 	err = sdhci_add_host(host);
172685d6509dSShawn Guo 	if (err)
172717b1eb7fSFabio Estevam 		goto disable_ahb_clk;
172885d6509dSShawn Guo 
172989d7e5c1SDong Aisheng 	pm_runtime_set_active(&pdev->dev);
173089d7e5c1SDong Aisheng 	pm_runtime_set_autosuspend_delay(&pdev->dev, 50);
173189d7e5c1SDong Aisheng 	pm_runtime_use_autosuspend(&pdev->dev);
173289d7e5c1SDong Aisheng 	pm_suspend_ignore_children(&pdev->dev, 1);
173377903c01SUlf Hansson 	pm_runtime_enable(&pdev->dev);
173489d7e5c1SDong Aisheng 
17357e29c306SWolfram Sang 	return 0;
17367e29c306SWolfram Sang 
173717b1eb7fSFabio Estevam disable_ahb_clk:
173852dac615SSascha Hauer 	clk_disable_unprepare(imx_data->clk_ahb);
173917b1eb7fSFabio Estevam disable_ipg_clk:
174017b1eb7fSFabio Estevam 	clk_disable_unprepare(imx_data->clk_ipg);
174117b1eb7fSFabio Estevam disable_per_clk:
174217b1eb7fSFabio Estevam 	clk_disable_unprepare(imx_data->clk_per);
1743e3af31c6SShawn Guo free_sdhci:
17441c4989b0SBOUGH CHEN 	if (imx_data->socdata->flags & ESDHC_FLAG_PMQOS)
1745d1b98305SRafael J. Wysocki 		cpu_latency_qos_remove_request(&imx_data->pm_qos_req);
174685d6509dSShawn Guo 	sdhci_pltfm_free(pdev);
174785d6509dSShawn Guo 	return err;
174895f25efeSWolfram Sang }
174995f25efeSWolfram Sang 
17506e0ee714SBill Pemberton static int sdhci_esdhc_imx_remove(struct platform_device *pdev)
175195f25efeSWolfram Sang {
175285d6509dSShawn Guo 	struct sdhci_host *host = platform_get_drvdata(pdev);
175395f25efeSWolfram Sang 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1754070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
175585d6509dSShawn Guo 	int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff);
175685d6509dSShawn Guo 
17570b414368SUlf Hansson 	pm_runtime_get_sync(&pdev->dev);
17580b414368SUlf Hansson 	pm_runtime_disable(&pdev->dev);
17590b414368SUlf Hansson 	pm_runtime_put_noidle(&pdev->dev);
17600b414368SUlf Hansson 
176185d6509dSShawn Guo 	sdhci_remove_host(host, dead);
17620c6d49ceSWolfram Sang 
176352dac615SSascha Hauer 	clk_disable_unprepare(imx_data->clk_per);
176452dac615SSascha Hauer 	clk_disable_unprepare(imx_data->clk_ipg);
176552dac615SSascha Hauer 	clk_disable_unprepare(imx_data->clk_ahb);
176652dac615SSascha Hauer 
17671c4989b0SBOUGH CHEN 	if (imx_data->socdata->flags & ESDHC_FLAG_PMQOS)
1768d1b98305SRafael J. Wysocki 		cpu_latency_qos_remove_request(&imx_data->pm_qos_req);
17691c4989b0SBOUGH CHEN 
177085d6509dSShawn Guo 	sdhci_pltfm_free(pdev);
177185d6509dSShawn Guo 
177285d6509dSShawn Guo 	return 0;
177395f25efeSWolfram Sang }
177495f25efeSWolfram Sang 
17752788ed42SUlf Hansson #ifdef CONFIG_PM_SLEEP
177604143fbaSDong Aisheng static int sdhci_esdhc_suspend(struct device *dev)
177704143fbaSDong Aisheng {
17783e3274abSUlf Hansson 	struct sdhci_host *host = dev_get_drvdata(dev);
1779a26a4f1bSHaibo Chen 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1780a26a4f1bSHaibo Chen 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
1781bb6e3581SBOUGH CHEN 	int ret;
1782bb6e3581SBOUGH CHEN 
1783bb6e3581SBOUGH CHEN 	if (host->mmc->caps2 & MMC_CAP2_CQE) {
1784bb6e3581SBOUGH CHEN 		ret = cqhci_suspend(host->mmc);
1785bb6e3581SBOUGH CHEN 		if (ret)
1786bb6e3581SBOUGH CHEN 			return ret;
1787bb6e3581SBOUGH CHEN 	}
17883e3274abSUlf Hansson 
1789a26a4f1bSHaibo Chen 	if ((imx_data->socdata->flags & ESDHC_FLAG_STATE_LOST_IN_LPMODE) &&
1790a26a4f1bSHaibo Chen 		(host->tuning_mode != SDHCI_TUNING_MODE_1)) {
1791a26a4f1bSHaibo Chen 		mmc_retune_timer_stop(host->mmc);
1792a26a4f1bSHaibo Chen 		mmc_retune_needed(host->mmc);
1793a26a4f1bSHaibo Chen 	}
1794a26a4f1bSHaibo Chen 
1795d38dcad4SAdrian Hunter 	if (host->tuning_mode != SDHCI_TUNING_MODE_3)
1796d38dcad4SAdrian Hunter 		mmc_retune_needed(host->mmc);
1797d38dcad4SAdrian Hunter 
1798af8fade4SHaibo Chen 	ret = sdhci_suspend_host(host);
1799f6140462SHaibo Chen 	if (ret)
1800f6140462SHaibo Chen 		return ret;
1801f6140462SHaibo Chen 
1802f6140462SHaibo Chen 	ret = pinctrl_pm_select_sleep_state(dev);
1803f6140462SHaibo Chen 	if (ret)
1804f6140462SHaibo Chen 		return ret;
1805f6140462SHaibo Chen 
1806f6140462SHaibo Chen 	ret = mmc_gpio_set_cd_wake(host->mmc, true);
1807af8fade4SHaibo Chen 
1808af8fade4SHaibo Chen 	return ret;
180904143fbaSDong Aisheng }
181004143fbaSDong Aisheng 
181104143fbaSDong Aisheng static int sdhci_esdhc_resume(struct device *dev)
181204143fbaSDong Aisheng {
1813cc17e129SDong Aisheng 	struct sdhci_host *host = dev_get_drvdata(dev);
1814bb6e3581SBOUGH CHEN 	int ret;
1815cc17e129SDong Aisheng 
1816af8fade4SHaibo Chen 	ret = pinctrl_pm_select_default_state(dev);
1817af8fade4SHaibo Chen 	if (ret)
1818af8fade4SHaibo Chen 		return ret;
1819af8fade4SHaibo Chen 
182019dbfdd3SDong Aisheng 	/* re-initialize hw state in case it's lost in low power mode */
182119dbfdd3SDong Aisheng 	sdhci_esdhc_imx_hwinit(host);
1822cc17e129SDong Aisheng 
1823bb6e3581SBOUGH CHEN 	ret = sdhci_resume_host(host);
1824bb6e3581SBOUGH CHEN 	if (ret)
1825bb6e3581SBOUGH CHEN 		return ret;
1826bb6e3581SBOUGH CHEN 
1827bb6e3581SBOUGH CHEN 	if (host->mmc->caps2 & MMC_CAP2_CQE)
1828bb6e3581SBOUGH CHEN 		ret = cqhci_resume(host->mmc);
1829bb6e3581SBOUGH CHEN 
1830f6140462SHaibo Chen 	if (!ret)
1831f6140462SHaibo Chen 		ret = mmc_gpio_set_cd_wake(host->mmc, false);
1832f6140462SHaibo Chen 
1833bb6e3581SBOUGH CHEN 	return ret;
183404143fbaSDong Aisheng }
18352788ed42SUlf Hansson #endif
183604143fbaSDong Aisheng 
18372788ed42SUlf Hansson #ifdef CONFIG_PM
183889d7e5c1SDong Aisheng static int sdhci_esdhc_runtime_suspend(struct device *dev)
183989d7e5c1SDong Aisheng {
184089d7e5c1SDong Aisheng 	struct sdhci_host *host = dev_get_drvdata(dev);
184189d7e5c1SDong Aisheng 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1842070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
184389d7e5c1SDong Aisheng 	int ret;
184489d7e5c1SDong Aisheng 
1845bb6e3581SBOUGH CHEN 	if (host->mmc->caps2 & MMC_CAP2_CQE) {
1846bb6e3581SBOUGH CHEN 		ret = cqhci_suspend(host->mmc);
1847bb6e3581SBOUGH CHEN 		if (ret)
1848bb6e3581SBOUGH CHEN 			return ret;
1849bb6e3581SBOUGH CHEN 	}
1850bb6e3581SBOUGH CHEN 
185189d7e5c1SDong Aisheng 	ret = sdhci_runtime_suspend_host(host);
1852371d39faSMichael Trimarchi 	if (ret)
1853371d39faSMichael Trimarchi 		return ret;
185489d7e5c1SDong Aisheng 
1855d38dcad4SAdrian Hunter 	if (host->tuning_mode != SDHCI_TUNING_MODE_3)
1856d38dcad4SAdrian Hunter 		mmc_retune_needed(host->mmc);
1857d38dcad4SAdrian Hunter 
18583602785bSMichael Trimarchi 	imx_data->actual_clock = host->mmc->actual_clock;
18593602785bSMichael Trimarchi 	esdhc_pltfm_set_clock(host, 0);
186089d7e5c1SDong Aisheng 	clk_disable_unprepare(imx_data->clk_per);
186189d7e5c1SDong Aisheng 	clk_disable_unprepare(imx_data->clk_ipg);
186289d7e5c1SDong Aisheng 	clk_disable_unprepare(imx_data->clk_ahb);
186389d7e5c1SDong Aisheng 
18641c4989b0SBOUGH CHEN 	if (imx_data->socdata->flags & ESDHC_FLAG_PMQOS)
1865d1b98305SRafael J. Wysocki 		cpu_latency_qos_remove_request(&imx_data->pm_qos_req);
18661c4989b0SBOUGH CHEN 
186789d7e5c1SDong Aisheng 	return ret;
186889d7e5c1SDong Aisheng }
186989d7e5c1SDong Aisheng 
187089d7e5c1SDong Aisheng static int sdhci_esdhc_runtime_resume(struct device *dev)
187189d7e5c1SDong Aisheng {
187289d7e5c1SDong Aisheng 	struct sdhci_host *host = dev_get_drvdata(dev);
187389d7e5c1SDong Aisheng 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1874070e6d3fSJisheng Zhang 	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
187517b1eb7fSFabio Estevam 	int err;
187689d7e5c1SDong Aisheng 
18771c4989b0SBOUGH CHEN 	if (imx_data->socdata->flags & ESDHC_FLAG_PMQOS)
1878d1b98305SRafael J. Wysocki 		cpu_latency_qos_add_request(&imx_data->pm_qos_req, 0);
18791c4989b0SBOUGH CHEN 
18805c11f1ffSHaibo Chen 	if (imx_data->socdata->flags & ESDHC_FLAG_CLK_RATE_LOST_IN_PM_RUNTIME)
18815c11f1ffSHaibo Chen 		clk_set_rate(imx_data->clk_per, pltfm_host->clock);
18825c11f1ffSHaibo Chen 
1883a0ad3087SMichael Trimarchi 	err = clk_prepare_enable(imx_data->clk_ahb);
1884a0ad3087SMichael Trimarchi 	if (err)
18851c4989b0SBOUGH CHEN 		goto remove_pm_qos_request;
1886a0ad3087SMichael Trimarchi 
188717b1eb7fSFabio Estevam 	err = clk_prepare_enable(imx_data->clk_per);
188817b1eb7fSFabio Estevam 	if (err)
1889a0ad3087SMichael Trimarchi 		goto disable_ahb_clk;
1890af5d2b7bSUlf Hansson 
189117b1eb7fSFabio Estevam 	err = clk_prepare_enable(imx_data->clk_ipg);
189217b1eb7fSFabio Estevam 	if (err)
189317b1eb7fSFabio Estevam 		goto disable_per_clk;
1894af5d2b7bSUlf Hansson 
18953602785bSMichael Trimarchi 	esdhc_pltfm_set_clock(host, imx_data->actual_clock);
1896a0ad3087SMichael Trimarchi 
1897c6303c5dSBaolin Wang 	err = sdhci_runtime_resume_host(host, 0);
189817b1eb7fSFabio Estevam 	if (err)
1899a0ad3087SMichael Trimarchi 		goto disable_ipg_clk;
190089d7e5c1SDong Aisheng 
1901bb6e3581SBOUGH CHEN 	if (host->mmc->caps2 & MMC_CAP2_CQE)
1902bb6e3581SBOUGH CHEN 		err = cqhci_resume(host->mmc);
1903bb6e3581SBOUGH CHEN 
1904bb6e3581SBOUGH CHEN 	return err;
190517b1eb7fSFabio Estevam 
190617b1eb7fSFabio Estevam disable_ipg_clk:
190717b1eb7fSFabio Estevam 	clk_disable_unprepare(imx_data->clk_ipg);
190817b1eb7fSFabio Estevam disable_per_clk:
190917b1eb7fSFabio Estevam 	clk_disable_unprepare(imx_data->clk_per);
1910a0ad3087SMichael Trimarchi disable_ahb_clk:
1911a0ad3087SMichael Trimarchi 	clk_disable_unprepare(imx_data->clk_ahb);
19121c4989b0SBOUGH CHEN remove_pm_qos_request:
19131c4989b0SBOUGH CHEN 	if (imx_data->socdata->flags & ESDHC_FLAG_PMQOS)
1914d1b98305SRafael J. Wysocki 		cpu_latency_qos_remove_request(&imx_data->pm_qos_req);
191517b1eb7fSFabio Estevam 	return err;
191689d7e5c1SDong Aisheng }
191789d7e5c1SDong Aisheng #endif
191889d7e5c1SDong Aisheng 
191989d7e5c1SDong Aisheng static const struct dev_pm_ops sdhci_esdhc_pmops = {
192004143fbaSDong Aisheng 	SET_SYSTEM_SLEEP_PM_OPS(sdhci_esdhc_suspend, sdhci_esdhc_resume)
192189d7e5c1SDong Aisheng 	SET_RUNTIME_PM_OPS(sdhci_esdhc_runtime_suspend,
192289d7e5c1SDong Aisheng 				sdhci_esdhc_runtime_resume, NULL)
192389d7e5c1SDong Aisheng };
192489d7e5c1SDong Aisheng 
192585d6509dSShawn Guo static struct platform_driver sdhci_esdhc_imx_driver = {
192685d6509dSShawn Guo 	.driver		= {
192785d6509dSShawn Guo 		.name	= "sdhci-esdhc-imx",
1928abfafc2dSShawn Guo 		.of_match_table = imx_esdhc_dt_ids,
192989d7e5c1SDong Aisheng 		.pm	= &sdhci_esdhc_pmops,
193085d6509dSShawn Guo 	},
193157ed3314SShawn Guo 	.id_table	= imx_esdhc_devtype,
193285d6509dSShawn Guo 	.probe		= sdhci_esdhc_imx_probe,
19330433c143SBill Pemberton 	.remove		= sdhci_esdhc_imx_remove,
193495f25efeSWolfram Sang };
193585d6509dSShawn Guo 
1936d1f81a64SAxel Lin module_platform_driver(sdhci_esdhc_imx_driver);
193785d6509dSShawn Guo 
193885d6509dSShawn Guo MODULE_DESCRIPTION("SDHCI driver for Freescale i.MX eSDHC");
1939035ff831SWolfram Sang MODULE_AUTHOR("Wolfram Sang <kernel@pengutronix.de>");
194085d6509dSShawn Guo MODULE_LICENSE("GPL v2");
1941