162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Freescale eSDHC i.MX controller driver for the platform bus.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * derived from the OF-version.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Copyright (c) 2010 Pengutronix e.K.
862306a36Sopenharmony_ci *   Author: Wolfram Sang <kernel@pengutronix.de>
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/bitfield.h>
1262306a36Sopenharmony_ci#include <linux/io.h>
1362306a36Sopenharmony_ci#include <linux/iopoll.h>
1462306a36Sopenharmony_ci#include <linux/delay.h>
1562306a36Sopenharmony_ci#include <linux/err.h>
1662306a36Sopenharmony_ci#include <linux/clk.h>
1762306a36Sopenharmony_ci#include <linux/module.h>
1862306a36Sopenharmony_ci#include <linux/slab.h>
1962306a36Sopenharmony_ci#include <linux/pm_qos.h>
2062306a36Sopenharmony_ci#include <linux/mmc/host.h>
2162306a36Sopenharmony_ci#include <linux/mmc/mmc.h>
2262306a36Sopenharmony_ci#include <linux/mmc/sdio.h>
2362306a36Sopenharmony_ci#include <linux/mmc/slot-gpio.h>
2462306a36Sopenharmony_ci#include <linux/of.h>
2562306a36Sopenharmony_ci#include <linux/platform_device.h>
2662306a36Sopenharmony_ci#include <linux/pinctrl/consumer.h>
2762306a36Sopenharmony_ci#include <linux/pm_runtime.h>
2862306a36Sopenharmony_ci#include "sdhci-cqhci.h"
2962306a36Sopenharmony_ci#include "sdhci-pltfm.h"
3062306a36Sopenharmony_ci#include "sdhci-esdhc.h"
3162306a36Sopenharmony_ci#include "cqhci.h"
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#define ESDHC_SYS_CTRL_DTOCV_MASK	0x0f
3462306a36Sopenharmony_ci#define	ESDHC_CTRL_D3CD			0x08
3562306a36Sopenharmony_ci#define ESDHC_BURST_LEN_EN_INCR		(1 << 27)
3662306a36Sopenharmony_ci/* VENDOR SPEC register */
3762306a36Sopenharmony_ci#define ESDHC_VENDOR_SPEC		0xc0
3862306a36Sopenharmony_ci#define  ESDHC_VENDOR_SPEC_SDIO_QUIRK	(1 << 1)
3962306a36Sopenharmony_ci#define  ESDHC_VENDOR_SPEC_VSELECT	(1 << 1)
4062306a36Sopenharmony_ci#define  ESDHC_VENDOR_SPEC_FRC_SDCLK_ON	(1 << 8)
4162306a36Sopenharmony_ci#define ESDHC_DEBUG_SEL_AND_STATUS_REG		0xc2
4262306a36Sopenharmony_ci#define ESDHC_DEBUG_SEL_REG			0xc3
4362306a36Sopenharmony_ci#define ESDHC_DEBUG_SEL_MASK			0xf
4462306a36Sopenharmony_ci#define ESDHC_DEBUG_SEL_CMD_STATE		1
4562306a36Sopenharmony_ci#define ESDHC_DEBUG_SEL_DATA_STATE		2
4662306a36Sopenharmony_ci#define ESDHC_DEBUG_SEL_TRANS_STATE		3
4762306a36Sopenharmony_ci#define ESDHC_DEBUG_SEL_DMA_STATE		4
4862306a36Sopenharmony_ci#define ESDHC_DEBUG_SEL_ADMA_STATE		5
4962306a36Sopenharmony_ci#define ESDHC_DEBUG_SEL_FIFO_STATE		6
5062306a36Sopenharmony_ci#define ESDHC_DEBUG_SEL_ASYNC_FIFO_STATE	7
5162306a36Sopenharmony_ci#define ESDHC_WTMK_LVL			0x44
5262306a36Sopenharmony_ci#define  ESDHC_WTMK_DEFAULT_VAL		0x10401040
5362306a36Sopenharmony_ci#define  ESDHC_WTMK_LVL_RD_WML_MASK	0x000000FF
5462306a36Sopenharmony_ci#define  ESDHC_WTMK_LVL_RD_WML_SHIFT	0
5562306a36Sopenharmony_ci#define  ESDHC_WTMK_LVL_WR_WML_MASK	0x00FF0000
5662306a36Sopenharmony_ci#define  ESDHC_WTMK_LVL_WR_WML_SHIFT	16
5762306a36Sopenharmony_ci#define  ESDHC_WTMK_LVL_WML_VAL_DEF	64
5862306a36Sopenharmony_ci#define  ESDHC_WTMK_LVL_WML_VAL_MAX	128
5962306a36Sopenharmony_ci#define ESDHC_MIX_CTRL			0x48
6062306a36Sopenharmony_ci#define  ESDHC_MIX_CTRL_DDREN		(1 << 3)
6162306a36Sopenharmony_ci#define  ESDHC_MIX_CTRL_AC23EN		(1 << 7)
6262306a36Sopenharmony_ci#define  ESDHC_MIX_CTRL_EXE_TUNE	(1 << 22)
6362306a36Sopenharmony_ci#define  ESDHC_MIX_CTRL_SMPCLK_SEL	(1 << 23)
6462306a36Sopenharmony_ci#define  ESDHC_MIX_CTRL_AUTO_TUNE_EN	(1 << 24)
6562306a36Sopenharmony_ci#define  ESDHC_MIX_CTRL_FBCLK_SEL	(1 << 25)
6662306a36Sopenharmony_ci#define  ESDHC_MIX_CTRL_HS400_EN	(1 << 26)
6762306a36Sopenharmony_ci#define  ESDHC_MIX_CTRL_HS400_ES_EN	(1 << 27)
6862306a36Sopenharmony_ci/* Bits 3 and 6 are not SDHCI standard definitions */
6962306a36Sopenharmony_ci#define  ESDHC_MIX_CTRL_SDHCI_MASK	0xb7
7062306a36Sopenharmony_ci/* Tuning bits */
7162306a36Sopenharmony_ci#define  ESDHC_MIX_CTRL_TUNING_MASK	0x03c00000
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci/* dll control register */
7462306a36Sopenharmony_ci#define ESDHC_DLL_CTRL			0x60
7562306a36Sopenharmony_ci#define ESDHC_DLL_OVERRIDE_VAL_SHIFT	9
7662306a36Sopenharmony_ci#define ESDHC_DLL_OVERRIDE_EN_SHIFT	8
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci/* tune control register */
7962306a36Sopenharmony_ci#define ESDHC_TUNE_CTRL_STATUS		0x68
8062306a36Sopenharmony_ci#define  ESDHC_TUNE_CTRL_STEP		1
8162306a36Sopenharmony_ci#define  ESDHC_TUNE_CTRL_MIN		0
8262306a36Sopenharmony_ci#define  ESDHC_TUNE_CTRL_MAX		((1 << 7) - 1)
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci/* strobe dll register */
8562306a36Sopenharmony_ci#define ESDHC_STROBE_DLL_CTRL		0x70
8662306a36Sopenharmony_ci#define ESDHC_STROBE_DLL_CTRL_ENABLE	(1 << 0)
8762306a36Sopenharmony_ci#define ESDHC_STROBE_DLL_CTRL_RESET	(1 << 1)
8862306a36Sopenharmony_ci#define ESDHC_STROBE_DLL_CTRL_SLV_DLY_TARGET_DEFAULT	0x7
8962306a36Sopenharmony_ci#define ESDHC_STROBE_DLL_CTRL_SLV_DLY_TARGET_SHIFT	3
9062306a36Sopenharmony_ci#define ESDHC_STROBE_DLL_CTRL_SLV_UPDATE_INT_DEFAULT	(4 << 20)
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci#define ESDHC_STROBE_DLL_STATUS		0x74
9362306a36Sopenharmony_ci#define ESDHC_STROBE_DLL_STS_REF_LOCK	(1 << 1)
9462306a36Sopenharmony_ci#define ESDHC_STROBE_DLL_STS_SLV_LOCK	0x1
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci#define ESDHC_VEND_SPEC2		0xc8
9762306a36Sopenharmony_ci#define ESDHC_VEND_SPEC2_EN_BUSY_IRQ	(1 << 8)
9862306a36Sopenharmony_ci#define ESDHC_VEND_SPEC2_AUTO_TUNE_8BIT_EN	(1 << 4)
9962306a36Sopenharmony_ci#define ESDHC_VEND_SPEC2_AUTO_TUNE_4BIT_EN	(0 << 4)
10062306a36Sopenharmony_ci#define ESDHC_VEND_SPEC2_AUTO_TUNE_1BIT_EN	(2 << 4)
10162306a36Sopenharmony_ci#define ESDHC_VEND_SPEC2_AUTO_TUNE_CMD_EN	(1 << 6)
10262306a36Sopenharmony_ci#define ESDHC_VEND_SPEC2_AUTO_TUNE_MODE_MASK	(7 << 4)
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci#define ESDHC_TUNING_CTRL		0xcc
10562306a36Sopenharmony_ci#define ESDHC_STD_TUNING_EN		(1 << 24)
10662306a36Sopenharmony_ci/* NOTE: the minimum valid tuning start tap for mx6sl is 1 */
10762306a36Sopenharmony_ci#define ESDHC_TUNING_START_TAP_DEFAULT	0x1
10862306a36Sopenharmony_ci#define ESDHC_TUNING_START_TAP_MASK	0x7f
10962306a36Sopenharmony_ci#define ESDHC_TUNING_CMD_CRC_CHECK_DISABLE	(1 << 7)
11062306a36Sopenharmony_ci#define ESDHC_TUNING_STEP_DEFAULT	0x1
11162306a36Sopenharmony_ci#define ESDHC_TUNING_STEP_MASK		0x00070000
11262306a36Sopenharmony_ci#define ESDHC_TUNING_STEP_SHIFT		16
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci/* pinctrl state */
11562306a36Sopenharmony_ci#define ESDHC_PINCTRL_STATE_100MHZ	"state_100mhz"
11662306a36Sopenharmony_ci#define ESDHC_PINCTRL_STATE_200MHZ	"state_200mhz"
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci/*
11962306a36Sopenharmony_ci * Our interpretation of the SDHCI_HOST_CONTROL register
12062306a36Sopenharmony_ci */
12162306a36Sopenharmony_ci#define ESDHC_CTRL_4BITBUS		(0x1 << 1)
12262306a36Sopenharmony_ci#define ESDHC_CTRL_8BITBUS		(0x2 << 1)
12362306a36Sopenharmony_ci#define ESDHC_CTRL_BUSWIDTH_MASK	(0x3 << 1)
12462306a36Sopenharmony_ci#define USDHC_GET_BUSWIDTH(c) (c & ESDHC_CTRL_BUSWIDTH_MASK)
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci/*
12762306a36Sopenharmony_ci * There is an INT DMA ERR mismatch between eSDHC and STD SDHC SPEC:
12862306a36Sopenharmony_ci * Bit25 is used in STD SPEC, and is reserved in fsl eSDHC design,
12962306a36Sopenharmony_ci * but bit28 is used as the INT DMA ERR in fsl eSDHC design.
13062306a36Sopenharmony_ci * Define this macro DMA error INT for fsl eSDHC
13162306a36Sopenharmony_ci */
13262306a36Sopenharmony_ci#define ESDHC_INT_VENDOR_SPEC_DMA_ERR	(1 << 28)
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci/* the address offset of CQHCI */
13562306a36Sopenharmony_ci#define ESDHC_CQHCI_ADDR_OFFSET		0x100
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci/*
13862306a36Sopenharmony_ci * The CMDTYPE of the CMD register (offset 0xE) should be set to
13962306a36Sopenharmony_ci * "11" when the STOP CMD12 is issued on imx53 to abort one
14062306a36Sopenharmony_ci * open ended multi-blk IO. Otherwise the TC INT wouldn't
14162306a36Sopenharmony_ci * be generated.
14262306a36Sopenharmony_ci * In exact block transfer, the controller doesn't complete the
14362306a36Sopenharmony_ci * operations automatically as required at the end of the
14462306a36Sopenharmony_ci * transfer and remains on hold if the abort command is not sent.
14562306a36Sopenharmony_ci * As a result, the TC flag is not asserted and SW received timeout
14662306a36Sopenharmony_ci * exception. Bit1 of Vendor Spec register is used to fix it.
14762306a36Sopenharmony_ci */
14862306a36Sopenharmony_ci#define ESDHC_FLAG_MULTIBLK_NO_INT	BIT(1)
14962306a36Sopenharmony_ci/*
15062306a36Sopenharmony_ci * The flag tells that the ESDHC controller is an USDHC block that is
15162306a36Sopenharmony_ci * integrated on the i.MX6 series.
15262306a36Sopenharmony_ci */
15362306a36Sopenharmony_ci#define ESDHC_FLAG_USDHC		BIT(3)
15462306a36Sopenharmony_ci/* The IP supports manual tuning process */
15562306a36Sopenharmony_ci#define ESDHC_FLAG_MAN_TUNING		BIT(4)
15662306a36Sopenharmony_ci/* The IP supports standard tuning process */
15762306a36Sopenharmony_ci#define ESDHC_FLAG_STD_TUNING		BIT(5)
15862306a36Sopenharmony_ci/* The IP has SDHCI_CAPABILITIES_1 register */
15962306a36Sopenharmony_ci#define ESDHC_FLAG_HAVE_CAP1		BIT(6)
16062306a36Sopenharmony_ci/*
16162306a36Sopenharmony_ci * The IP has erratum ERR004536
16262306a36Sopenharmony_ci * uSDHC: ADMA Length Mismatch Error occurs if the AHB read access is slow,
16362306a36Sopenharmony_ci * when reading data from the card
16462306a36Sopenharmony_ci * This flag is also set for i.MX25 and i.MX35 in order to get
16562306a36Sopenharmony_ci * SDHCI_QUIRK_BROKEN_ADMA, but for different reasons (ADMA capability bits).
16662306a36Sopenharmony_ci */
16762306a36Sopenharmony_ci#define ESDHC_FLAG_ERR004536		BIT(7)
16862306a36Sopenharmony_ci/* The IP supports HS200 mode */
16962306a36Sopenharmony_ci#define ESDHC_FLAG_HS200		BIT(8)
17062306a36Sopenharmony_ci/* The IP supports HS400 mode */
17162306a36Sopenharmony_ci#define ESDHC_FLAG_HS400		BIT(9)
17262306a36Sopenharmony_ci/*
17362306a36Sopenharmony_ci * The IP has errata ERR010450
17462306a36Sopenharmony_ci * uSDHC: At 1.8V due to the I/O timing limit, for SDR mode, SD card
17562306a36Sopenharmony_ci * clock can't exceed 150MHz, for DDR mode, SD card clock can't exceed 45MHz.
17662306a36Sopenharmony_ci */
17762306a36Sopenharmony_ci#define ESDHC_FLAG_ERR010450		BIT(10)
17862306a36Sopenharmony_ci/* The IP supports HS400ES mode */
17962306a36Sopenharmony_ci#define ESDHC_FLAG_HS400_ES		BIT(11)
18062306a36Sopenharmony_ci/* The IP has Host Controller Interface for Command Queuing */
18162306a36Sopenharmony_ci#define ESDHC_FLAG_CQHCI		BIT(12)
18262306a36Sopenharmony_ci/* need request pmqos during low power */
18362306a36Sopenharmony_ci#define ESDHC_FLAG_PMQOS		BIT(13)
18462306a36Sopenharmony_ci/* The IP state got lost in low power mode */
18562306a36Sopenharmony_ci#define ESDHC_FLAG_STATE_LOST_IN_LPMODE		BIT(14)
18662306a36Sopenharmony_ci/* The IP lost clock rate in PM_RUNTIME */
18762306a36Sopenharmony_ci#define ESDHC_FLAG_CLK_RATE_LOST_IN_PM_RUNTIME	BIT(15)
18862306a36Sopenharmony_ci/*
18962306a36Sopenharmony_ci * The IP do not support the ACMD23 feature completely when use ADMA mode.
19062306a36Sopenharmony_ci * In ADMA mode, it only use the 16 bit block count of the register 0x4
19162306a36Sopenharmony_ci * (BLOCK_ATT) as the CMD23's argument for ACMD23 mode, which means it will
19262306a36Sopenharmony_ci * ignore the upper 16 bit of the CMD23's argument. This will block the reliable
19362306a36Sopenharmony_ci * write operation in RPMB, because RPMB reliable write need to set the bit31
19462306a36Sopenharmony_ci * of the CMD23's argument.
19562306a36Sopenharmony_ci * imx6qpdl/imx6sx/imx6sl/imx7d has this limitation only for ADMA mode, SDMA
19662306a36Sopenharmony_ci * do not has this limitation. so when these SoC use ADMA mode, it need to
19762306a36Sopenharmony_ci * disable the ACMD23 feature.
19862306a36Sopenharmony_ci */
19962306a36Sopenharmony_ci#define ESDHC_FLAG_BROKEN_AUTO_CMD23	BIT(16)
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci/* ERR004536 is not applicable for the IP  */
20262306a36Sopenharmony_ci#define ESDHC_FLAG_SKIP_ERR004536	BIT(17)
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_cienum wp_types {
20562306a36Sopenharmony_ci	ESDHC_WP_NONE,		/* no WP, neither controller nor gpio */
20662306a36Sopenharmony_ci	ESDHC_WP_CONTROLLER,	/* mmc controller internal WP */
20762306a36Sopenharmony_ci	ESDHC_WP_GPIO,		/* external gpio pin for WP */
20862306a36Sopenharmony_ci};
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_cienum cd_types {
21162306a36Sopenharmony_ci	ESDHC_CD_NONE,		/* no CD, neither controller nor gpio */
21262306a36Sopenharmony_ci	ESDHC_CD_CONTROLLER,	/* mmc controller internal CD */
21362306a36Sopenharmony_ci	ESDHC_CD_GPIO,		/* external gpio pin for CD */
21462306a36Sopenharmony_ci	ESDHC_CD_PERMANENT,	/* no CD, card permanently wired to host */
21562306a36Sopenharmony_ci};
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci/*
21862306a36Sopenharmony_ci * struct esdhc_platform_data - platform data for esdhc on i.MX
21962306a36Sopenharmony_ci *
22062306a36Sopenharmony_ci * ESDHC_WP(CD)_CONTROLLER type is not available on i.MX25/35.
22162306a36Sopenharmony_ci *
22262306a36Sopenharmony_ci * @wp_type:	type of write_protect method (see wp_types enum above)
22362306a36Sopenharmony_ci * @cd_type:	type of card_detect method (see cd_types enum above)
22462306a36Sopenharmony_ci */
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_cistruct esdhc_platform_data {
22762306a36Sopenharmony_ci	enum wp_types wp_type;
22862306a36Sopenharmony_ci	enum cd_types cd_type;
22962306a36Sopenharmony_ci	int max_bus_width;
23062306a36Sopenharmony_ci	unsigned int delay_line;
23162306a36Sopenharmony_ci	unsigned int tuning_step;       /* The delay cell steps in tuning procedure */
23262306a36Sopenharmony_ci	unsigned int tuning_start_tap;	/* The start delay cell point in tuning procedure */
23362306a36Sopenharmony_ci	unsigned int strobe_dll_delay_target;	/* The delay cell for strobe pad (read clock) */
23462306a36Sopenharmony_ci};
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_cistruct esdhc_soc_data {
23762306a36Sopenharmony_ci	u32 flags;
23862306a36Sopenharmony_ci};
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_cistatic const struct esdhc_soc_data esdhc_imx25_data = {
24162306a36Sopenharmony_ci	.flags = ESDHC_FLAG_ERR004536,
24262306a36Sopenharmony_ci};
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_cistatic const struct esdhc_soc_data esdhc_imx35_data = {
24562306a36Sopenharmony_ci	.flags = ESDHC_FLAG_ERR004536,
24662306a36Sopenharmony_ci};
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_cistatic const struct esdhc_soc_data esdhc_imx51_data = {
24962306a36Sopenharmony_ci	.flags = 0,
25062306a36Sopenharmony_ci};
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_cistatic const struct esdhc_soc_data esdhc_imx53_data = {
25362306a36Sopenharmony_ci	.flags = ESDHC_FLAG_MULTIBLK_NO_INT,
25462306a36Sopenharmony_ci};
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_cistatic const struct esdhc_soc_data usdhc_imx6q_data = {
25762306a36Sopenharmony_ci	.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_MAN_TUNING
25862306a36Sopenharmony_ci			| ESDHC_FLAG_BROKEN_AUTO_CMD23,
25962306a36Sopenharmony_ci};
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_cistatic const struct esdhc_soc_data usdhc_imx6sl_data = {
26262306a36Sopenharmony_ci	.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
26362306a36Sopenharmony_ci			| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_ERR004536
26462306a36Sopenharmony_ci			| ESDHC_FLAG_HS200
26562306a36Sopenharmony_ci			| ESDHC_FLAG_BROKEN_AUTO_CMD23,
26662306a36Sopenharmony_ci};
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_cistatic const struct esdhc_soc_data usdhc_imx6sll_data = {
26962306a36Sopenharmony_ci	.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
27062306a36Sopenharmony_ci			| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
27162306a36Sopenharmony_ci			| ESDHC_FLAG_HS400
27262306a36Sopenharmony_ci			| ESDHC_FLAG_STATE_LOST_IN_LPMODE,
27362306a36Sopenharmony_ci};
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_cistatic const struct esdhc_soc_data usdhc_imx6sx_data = {
27662306a36Sopenharmony_ci	.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
27762306a36Sopenharmony_ci			| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
27862306a36Sopenharmony_ci			| ESDHC_FLAG_STATE_LOST_IN_LPMODE
27962306a36Sopenharmony_ci			| ESDHC_FLAG_BROKEN_AUTO_CMD23,
28062306a36Sopenharmony_ci};
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_cistatic const struct esdhc_soc_data usdhc_imx6ull_data = {
28362306a36Sopenharmony_ci	.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
28462306a36Sopenharmony_ci			| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
28562306a36Sopenharmony_ci			| ESDHC_FLAG_ERR010450
28662306a36Sopenharmony_ci			| ESDHC_FLAG_STATE_LOST_IN_LPMODE,
28762306a36Sopenharmony_ci};
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_cistatic const struct esdhc_soc_data usdhc_imx7d_data = {
29062306a36Sopenharmony_ci	.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
29162306a36Sopenharmony_ci			| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
29262306a36Sopenharmony_ci			| ESDHC_FLAG_HS400
29362306a36Sopenharmony_ci			| ESDHC_FLAG_STATE_LOST_IN_LPMODE
29462306a36Sopenharmony_ci			| ESDHC_FLAG_BROKEN_AUTO_CMD23,
29562306a36Sopenharmony_ci};
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_cistatic struct esdhc_soc_data usdhc_s32g2_data = {
29862306a36Sopenharmony_ci	.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_MAN_TUNING
29962306a36Sopenharmony_ci			| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
30062306a36Sopenharmony_ci			| ESDHC_FLAG_HS400 | ESDHC_FLAG_HS400_ES
30162306a36Sopenharmony_ci			| ESDHC_FLAG_SKIP_ERR004536,
30262306a36Sopenharmony_ci};
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_cistatic struct esdhc_soc_data usdhc_imx7ulp_data = {
30562306a36Sopenharmony_ci	.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
30662306a36Sopenharmony_ci			| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
30762306a36Sopenharmony_ci			| ESDHC_FLAG_PMQOS | ESDHC_FLAG_HS400
30862306a36Sopenharmony_ci			| ESDHC_FLAG_STATE_LOST_IN_LPMODE,
30962306a36Sopenharmony_ci};
31062306a36Sopenharmony_cistatic struct esdhc_soc_data usdhc_imxrt1050_data = {
31162306a36Sopenharmony_ci	.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
31262306a36Sopenharmony_ci			| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200,
31362306a36Sopenharmony_ci};
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_cistatic struct esdhc_soc_data usdhc_imx8qxp_data = {
31662306a36Sopenharmony_ci	.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
31762306a36Sopenharmony_ci			| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
31862306a36Sopenharmony_ci			| ESDHC_FLAG_HS400 | ESDHC_FLAG_HS400_ES
31962306a36Sopenharmony_ci			| ESDHC_FLAG_STATE_LOST_IN_LPMODE
32062306a36Sopenharmony_ci			| ESDHC_FLAG_CLK_RATE_LOST_IN_PM_RUNTIME,
32162306a36Sopenharmony_ci};
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_cistatic struct esdhc_soc_data usdhc_imx8mm_data = {
32462306a36Sopenharmony_ci	.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
32562306a36Sopenharmony_ci			| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
32662306a36Sopenharmony_ci			| ESDHC_FLAG_HS400 | ESDHC_FLAG_HS400_ES
32762306a36Sopenharmony_ci			| ESDHC_FLAG_STATE_LOST_IN_LPMODE,
32862306a36Sopenharmony_ci};
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_cistruct pltfm_imx_data {
33162306a36Sopenharmony_ci	u32 scratchpad;
33262306a36Sopenharmony_ci	struct pinctrl *pinctrl;
33362306a36Sopenharmony_ci	struct pinctrl_state *pins_100mhz;
33462306a36Sopenharmony_ci	struct pinctrl_state *pins_200mhz;
33562306a36Sopenharmony_ci	const struct esdhc_soc_data *socdata;
33662306a36Sopenharmony_ci	struct esdhc_platform_data boarddata;
33762306a36Sopenharmony_ci	struct clk *clk_ipg;
33862306a36Sopenharmony_ci	struct clk *clk_ahb;
33962306a36Sopenharmony_ci	struct clk *clk_per;
34062306a36Sopenharmony_ci	unsigned int actual_clock;
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	/*
34362306a36Sopenharmony_ci	 * USDHC has one limition, require the SDIO device a different
34462306a36Sopenharmony_ci	 * register setting. Driver has to recognize card type during
34562306a36Sopenharmony_ci	 * the card init, but at this stage, mmc_host->card is not
34662306a36Sopenharmony_ci	 * available. So involve this field to save the card type
34762306a36Sopenharmony_ci	 * during card init through usdhc_init_card().
34862306a36Sopenharmony_ci	 */
34962306a36Sopenharmony_ci	unsigned int init_card_type;
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	enum {
35262306a36Sopenharmony_ci		NO_CMD_PENDING,      /* no multiblock command pending */
35362306a36Sopenharmony_ci		MULTIBLK_IN_PROCESS, /* exact multiblock cmd in process */
35462306a36Sopenharmony_ci		WAIT_FOR_INT,        /* sent CMD12, waiting for response INT */
35562306a36Sopenharmony_ci	} multiblock_status;
35662306a36Sopenharmony_ci	u32 is_ddr;
35762306a36Sopenharmony_ci	struct pm_qos_request pm_qos_req;
35862306a36Sopenharmony_ci};
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_cistatic const struct of_device_id imx_esdhc_dt_ids[] = {
36162306a36Sopenharmony_ci	{ .compatible = "fsl,imx25-esdhc", .data = &esdhc_imx25_data, },
36262306a36Sopenharmony_ci	{ .compatible = "fsl,imx35-esdhc", .data = &esdhc_imx35_data, },
36362306a36Sopenharmony_ci	{ .compatible = "fsl,imx51-esdhc", .data = &esdhc_imx51_data, },
36462306a36Sopenharmony_ci	{ .compatible = "fsl,imx53-esdhc", .data = &esdhc_imx53_data, },
36562306a36Sopenharmony_ci	{ .compatible = "fsl,imx6sx-usdhc", .data = &usdhc_imx6sx_data, },
36662306a36Sopenharmony_ci	{ .compatible = "fsl,imx6sl-usdhc", .data = &usdhc_imx6sl_data, },
36762306a36Sopenharmony_ci	{ .compatible = "fsl,imx6sll-usdhc", .data = &usdhc_imx6sll_data, },
36862306a36Sopenharmony_ci	{ .compatible = "fsl,imx6q-usdhc", .data = &usdhc_imx6q_data, },
36962306a36Sopenharmony_ci	{ .compatible = "fsl,imx6ull-usdhc", .data = &usdhc_imx6ull_data, },
37062306a36Sopenharmony_ci	{ .compatible = "fsl,imx7d-usdhc", .data = &usdhc_imx7d_data, },
37162306a36Sopenharmony_ci	{ .compatible = "fsl,imx7ulp-usdhc", .data = &usdhc_imx7ulp_data, },
37262306a36Sopenharmony_ci	{ .compatible = "fsl,imx8qxp-usdhc", .data = &usdhc_imx8qxp_data, },
37362306a36Sopenharmony_ci	{ .compatible = "fsl,imx8mm-usdhc", .data = &usdhc_imx8mm_data, },
37462306a36Sopenharmony_ci	{ .compatible = "fsl,imxrt1050-usdhc", .data = &usdhc_imxrt1050_data, },
37562306a36Sopenharmony_ci	{ .compatible = "nxp,s32g2-usdhc", .data = &usdhc_s32g2_data, },
37662306a36Sopenharmony_ci	{ /* sentinel */ }
37762306a36Sopenharmony_ci};
37862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, imx_esdhc_dt_ids);
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_cistatic inline int is_imx25_esdhc(struct pltfm_imx_data *data)
38162306a36Sopenharmony_ci{
38262306a36Sopenharmony_ci	return data->socdata == &esdhc_imx25_data;
38362306a36Sopenharmony_ci}
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_cistatic inline int is_imx53_esdhc(struct pltfm_imx_data *data)
38662306a36Sopenharmony_ci{
38762306a36Sopenharmony_ci	return data->socdata == &esdhc_imx53_data;
38862306a36Sopenharmony_ci}
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_cistatic inline int esdhc_is_usdhc(struct pltfm_imx_data *data)
39162306a36Sopenharmony_ci{
39262306a36Sopenharmony_ci	return !!(data->socdata->flags & ESDHC_FLAG_USDHC);
39362306a36Sopenharmony_ci}
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_cistatic inline void esdhc_clrset_le(struct sdhci_host *host, u32 mask, u32 val, int reg)
39662306a36Sopenharmony_ci{
39762306a36Sopenharmony_ci	void __iomem *base = host->ioaddr + (reg & ~0x3);
39862306a36Sopenharmony_ci	u32 shift = (reg & 0x3) * 8;
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	writel(((readl(base) & ~(mask << shift)) | (val << shift)), base);
40162306a36Sopenharmony_ci}
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci#define DRIVER_NAME "sdhci-esdhc-imx"
40462306a36Sopenharmony_ci#define ESDHC_IMX_DUMP(f, x...) \
40562306a36Sopenharmony_ci	pr_err("%s: " DRIVER_NAME ": " f, mmc_hostname(host->mmc), ## x)
40662306a36Sopenharmony_cistatic void esdhc_dump_debug_regs(struct sdhci_host *host)
40762306a36Sopenharmony_ci{
40862306a36Sopenharmony_ci	int i;
40962306a36Sopenharmony_ci	char *debug_status[7] = {
41062306a36Sopenharmony_ci				 "cmd debug status",
41162306a36Sopenharmony_ci				 "data debug status",
41262306a36Sopenharmony_ci				 "trans debug status",
41362306a36Sopenharmony_ci				 "dma debug status",
41462306a36Sopenharmony_ci				 "adma debug status",
41562306a36Sopenharmony_ci				 "fifo debug status",
41662306a36Sopenharmony_ci				 "async fifo debug status"
41762306a36Sopenharmony_ci	};
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	ESDHC_IMX_DUMP("========= ESDHC IMX DEBUG STATUS DUMP =========\n");
42062306a36Sopenharmony_ci	for (i = 0; i < 7; i++) {
42162306a36Sopenharmony_ci		esdhc_clrset_le(host, ESDHC_DEBUG_SEL_MASK,
42262306a36Sopenharmony_ci			ESDHC_DEBUG_SEL_CMD_STATE + i, ESDHC_DEBUG_SEL_REG);
42362306a36Sopenharmony_ci		ESDHC_IMX_DUMP("%s:  0x%04x\n", debug_status[i],
42462306a36Sopenharmony_ci			readw(host->ioaddr + ESDHC_DEBUG_SEL_AND_STATUS_REG));
42562306a36Sopenharmony_ci	}
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	esdhc_clrset_le(host, ESDHC_DEBUG_SEL_MASK, 0, ESDHC_DEBUG_SEL_REG);
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci}
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_cistatic inline void esdhc_wait_for_card_clock_gate_off(struct sdhci_host *host)
43262306a36Sopenharmony_ci{
43362306a36Sopenharmony_ci	u32 present_state;
43462306a36Sopenharmony_ci	int ret;
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	ret = readl_poll_timeout(host->ioaddr + ESDHC_PRSSTAT, present_state,
43762306a36Sopenharmony_ci				(present_state & ESDHC_CLOCK_GATE_OFF), 2, 100);
43862306a36Sopenharmony_ci	if (ret == -ETIMEDOUT)
43962306a36Sopenharmony_ci		dev_warn(mmc_dev(host->mmc), "%s: card clock still not gate off in 100us!.\n", __func__);
44062306a36Sopenharmony_ci}
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci/* Enable the auto tuning circuit to check the CMD line and BUS line */
44362306a36Sopenharmony_cistatic inline void usdhc_auto_tuning_mode_sel_and_en(struct sdhci_host *host)
44462306a36Sopenharmony_ci{
44562306a36Sopenharmony_ci	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
44662306a36Sopenharmony_ci	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
44762306a36Sopenharmony_ci	u32 buswidth, auto_tune_buswidth;
44862306a36Sopenharmony_ci	u32 reg;
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	buswidth = USDHC_GET_BUSWIDTH(readl(host->ioaddr + SDHCI_HOST_CONTROL));
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	switch (buswidth) {
45362306a36Sopenharmony_ci	case ESDHC_CTRL_8BITBUS:
45462306a36Sopenharmony_ci		auto_tune_buswidth = ESDHC_VEND_SPEC2_AUTO_TUNE_8BIT_EN;
45562306a36Sopenharmony_ci		break;
45662306a36Sopenharmony_ci	case ESDHC_CTRL_4BITBUS:
45762306a36Sopenharmony_ci		auto_tune_buswidth = ESDHC_VEND_SPEC2_AUTO_TUNE_4BIT_EN;
45862306a36Sopenharmony_ci		break;
45962306a36Sopenharmony_ci	default:	/* 1BITBUS */
46062306a36Sopenharmony_ci		auto_tune_buswidth = ESDHC_VEND_SPEC2_AUTO_TUNE_1BIT_EN;
46162306a36Sopenharmony_ci		break;
46262306a36Sopenharmony_ci	}
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	/*
46562306a36Sopenharmony_ci	 * For USDHC, auto tuning circuit can not handle the async sdio
46662306a36Sopenharmony_ci	 * device interrupt correctly. When sdio device use 4 data lines,
46762306a36Sopenharmony_ci	 * async sdio interrupt will use the shared DAT[1], if enable auto
46862306a36Sopenharmony_ci	 * tuning circuit check these 4 data lines, include the DAT[1],
46962306a36Sopenharmony_ci	 * this circuit will detect this interrupt, take this as a data on
47062306a36Sopenharmony_ci	 * DAT[1], and adjust the delay cell wrongly.
47162306a36Sopenharmony_ci	 * This is the hardware design limitation, to avoid this, for sdio
47262306a36Sopenharmony_ci	 * device, config the auto tuning circuit only check DAT[0] and CMD
47362306a36Sopenharmony_ci	 * line.
47462306a36Sopenharmony_ci	 */
47562306a36Sopenharmony_ci	if (imx_data->init_card_type == MMC_TYPE_SDIO)
47662306a36Sopenharmony_ci		auto_tune_buswidth = ESDHC_VEND_SPEC2_AUTO_TUNE_1BIT_EN;
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	esdhc_clrset_le(host, ESDHC_VEND_SPEC2_AUTO_TUNE_MODE_MASK,
47962306a36Sopenharmony_ci			auto_tune_buswidth | ESDHC_VEND_SPEC2_AUTO_TUNE_CMD_EN,
48062306a36Sopenharmony_ci			ESDHC_VEND_SPEC2);
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	reg = readl(host->ioaddr + ESDHC_MIX_CTRL);
48362306a36Sopenharmony_ci	reg |= ESDHC_MIX_CTRL_AUTO_TUNE_EN;
48462306a36Sopenharmony_ci	writel(reg, host->ioaddr + ESDHC_MIX_CTRL);
48562306a36Sopenharmony_ci}
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_cistatic u32 esdhc_readl_le(struct sdhci_host *host, int reg)
48862306a36Sopenharmony_ci{
48962306a36Sopenharmony_ci	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
49062306a36Sopenharmony_ci	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
49162306a36Sopenharmony_ci	u32 val = readl(host->ioaddr + reg);
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	if (unlikely(reg == SDHCI_PRESENT_STATE)) {
49462306a36Sopenharmony_ci		u32 fsl_prss = val;
49562306a36Sopenharmony_ci		/* save the least 20 bits */
49662306a36Sopenharmony_ci		val = fsl_prss & 0x000FFFFF;
49762306a36Sopenharmony_ci		/* move dat[0-3] bits */
49862306a36Sopenharmony_ci		val |= (fsl_prss & 0x0F000000) >> 4;
49962306a36Sopenharmony_ci		/* move cmd line bit */
50062306a36Sopenharmony_ci		val |= (fsl_prss & 0x00800000) << 1;
50162306a36Sopenharmony_ci	}
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	if (unlikely(reg == SDHCI_CAPABILITIES)) {
50462306a36Sopenharmony_ci		/* ignore bit[0-15] as it stores cap_1 register val for mx6sl */
50562306a36Sopenharmony_ci		if (imx_data->socdata->flags & ESDHC_FLAG_HAVE_CAP1)
50662306a36Sopenharmony_ci			val &= 0xffff0000;
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci		/* In FSL esdhc IC module, only bit20 is used to indicate the
50962306a36Sopenharmony_ci		 * ADMA2 capability of esdhc, but this bit is messed up on
51062306a36Sopenharmony_ci		 * some SOCs (e.g. on MX25, MX35 this bit is set, but they
51162306a36Sopenharmony_ci		 * don't actually support ADMA2). So set the BROKEN_ADMA
51262306a36Sopenharmony_ci		 * quirk on MX25/35 platforms.
51362306a36Sopenharmony_ci		 */
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci		if (val & SDHCI_CAN_DO_ADMA1) {
51662306a36Sopenharmony_ci			val &= ~SDHCI_CAN_DO_ADMA1;
51762306a36Sopenharmony_ci			val |= SDHCI_CAN_DO_ADMA2;
51862306a36Sopenharmony_ci		}
51962306a36Sopenharmony_ci	}
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	if (unlikely(reg == SDHCI_CAPABILITIES_1)) {
52262306a36Sopenharmony_ci		if (esdhc_is_usdhc(imx_data)) {
52362306a36Sopenharmony_ci			if (imx_data->socdata->flags & ESDHC_FLAG_HAVE_CAP1)
52462306a36Sopenharmony_ci				val = readl(host->ioaddr + SDHCI_CAPABILITIES) & 0xFFFF;
52562306a36Sopenharmony_ci			else
52662306a36Sopenharmony_ci				/* imx6q/dl does not have cap_1 register, fake one */
52762306a36Sopenharmony_ci				val = SDHCI_SUPPORT_DDR50 | SDHCI_SUPPORT_SDR104
52862306a36Sopenharmony_ci					| SDHCI_SUPPORT_SDR50
52962306a36Sopenharmony_ci					| SDHCI_USE_SDR50_TUNING
53062306a36Sopenharmony_ci					| FIELD_PREP(SDHCI_RETUNING_MODE_MASK,
53162306a36Sopenharmony_ci						     SDHCI_TUNING_MODE_3);
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci			/*
53462306a36Sopenharmony_ci			 * Do not advertise faster UHS modes if there are no
53562306a36Sopenharmony_ci			 * pinctrl states for 100MHz/200MHz.
53662306a36Sopenharmony_ci			 */
53762306a36Sopenharmony_ci			if (IS_ERR_OR_NULL(imx_data->pins_100mhz))
53862306a36Sopenharmony_ci				val &= ~(SDHCI_SUPPORT_SDR50 | SDHCI_SUPPORT_DDR50);
53962306a36Sopenharmony_ci			if (IS_ERR_OR_NULL(imx_data->pins_200mhz))
54062306a36Sopenharmony_ci				val &= ~(SDHCI_SUPPORT_SDR104 | SDHCI_SUPPORT_HS400);
54162306a36Sopenharmony_ci		}
54262306a36Sopenharmony_ci	}
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	if (unlikely(reg == SDHCI_MAX_CURRENT) && esdhc_is_usdhc(imx_data)) {
54562306a36Sopenharmony_ci		val = 0;
54662306a36Sopenharmony_ci		val |= FIELD_PREP(SDHCI_MAX_CURRENT_330_MASK, 0xFF);
54762306a36Sopenharmony_ci		val |= FIELD_PREP(SDHCI_MAX_CURRENT_300_MASK, 0xFF);
54862306a36Sopenharmony_ci		val |= FIELD_PREP(SDHCI_MAX_CURRENT_180_MASK, 0xFF);
54962306a36Sopenharmony_ci	}
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci	if (unlikely(reg == SDHCI_INT_STATUS)) {
55262306a36Sopenharmony_ci		if (val & ESDHC_INT_VENDOR_SPEC_DMA_ERR) {
55362306a36Sopenharmony_ci			val &= ~ESDHC_INT_VENDOR_SPEC_DMA_ERR;
55462306a36Sopenharmony_ci			val |= SDHCI_INT_ADMA_ERROR;
55562306a36Sopenharmony_ci		}
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci		/*
55862306a36Sopenharmony_ci		 * mask off the interrupt we get in response to the manually
55962306a36Sopenharmony_ci		 * sent CMD12
56062306a36Sopenharmony_ci		 */
56162306a36Sopenharmony_ci		if ((imx_data->multiblock_status == WAIT_FOR_INT) &&
56262306a36Sopenharmony_ci		    ((val & SDHCI_INT_RESPONSE) == SDHCI_INT_RESPONSE)) {
56362306a36Sopenharmony_ci			val &= ~SDHCI_INT_RESPONSE;
56462306a36Sopenharmony_ci			writel(SDHCI_INT_RESPONSE, host->ioaddr +
56562306a36Sopenharmony_ci						   SDHCI_INT_STATUS);
56662306a36Sopenharmony_ci			imx_data->multiblock_status = NO_CMD_PENDING;
56762306a36Sopenharmony_ci		}
56862306a36Sopenharmony_ci	}
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	return val;
57162306a36Sopenharmony_ci}
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_cistatic void esdhc_writel_le(struct sdhci_host *host, u32 val, int reg)
57462306a36Sopenharmony_ci{
57562306a36Sopenharmony_ci	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
57662306a36Sopenharmony_ci	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
57762306a36Sopenharmony_ci	u32 data;
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	if (unlikely(reg == SDHCI_INT_ENABLE || reg == SDHCI_SIGNAL_ENABLE ||
58062306a36Sopenharmony_ci			reg == SDHCI_INT_STATUS)) {
58162306a36Sopenharmony_ci		if ((val & SDHCI_INT_CARD_INT) && !esdhc_is_usdhc(imx_data)) {
58262306a36Sopenharmony_ci			/*
58362306a36Sopenharmony_ci			 * Clear and then set D3CD bit to avoid missing the
58462306a36Sopenharmony_ci			 * card interrupt. This is an eSDHC controller problem
58562306a36Sopenharmony_ci			 * so we need to apply the following workaround: clear
58662306a36Sopenharmony_ci			 * and set D3CD bit will make eSDHC re-sample the card
58762306a36Sopenharmony_ci			 * interrupt. In case a card interrupt was lost,
58862306a36Sopenharmony_ci			 * re-sample it by the following steps.
58962306a36Sopenharmony_ci			 */
59062306a36Sopenharmony_ci			data = readl(host->ioaddr + SDHCI_HOST_CONTROL);
59162306a36Sopenharmony_ci			data &= ~ESDHC_CTRL_D3CD;
59262306a36Sopenharmony_ci			writel(data, host->ioaddr + SDHCI_HOST_CONTROL);
59362306a36Sopenharmony_ci			data |= ESDHC_CTRL_D3CD;
59462306a36Sopenharmony_ci			writel(data, host->ioaddr + SDHCI_HOST_CONTROL);
59562306a36Sopenharmony_ci		}
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci		if (val & SDHCI_INT_ADMA_ERROR) {
59862306a36Sopenharmony_ci			val &= ~SDHCI_INT_ADMA_ERROR;
59962306a36Sopenharmony_ci			val |= ESDHC_INT_VENDOR_SPEC_DMA_ERR;
60062306a36Sopenharmony_ci		}
60162306a36Sopenharmony_ci	}
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	if (unlikely((imx_data->socdata->flags & ESDHC_FLAG_MULTIBLK_NO_INT)
60462306a36Sopenharmony_ci				&& (reg == SDHCI_INT_STATUS)
60562306a36Sopenharmony_ci				&& (val & SDHCI_INT_DATA_END))) {
60662306a36Sopenharmony_ci			u32 v;
60762306a36Sopenharmony_ci			v = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
60862306a36Sopenharmony_ci			v &= ~ESDHC_VENDOR_SPEC_SDIO_QUIRK;
60962306a36Sopenharmony_ci			writel(v, host->ioaddr + ESDHC_VENDOR_SPEC);
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci			if (imx_data->multiblock_status == MULTIBLK_IN_PROCESS)
61262306a36Sopenharmony_ci			{
61362306a36Sopenharmony_ci				/* send a manual CMD12 with RESPTYP=none */
61462306a36Sopenharmony_ci				data = MMC_STOP_TRANSMISSION << 24 |
61562306a36Sopenharmony_ci				       SDHCI_CMD_ABORTCMD << 16;
61662306a36Sopenharmony_ci				writel(data, host->ioaddr + SDHCI_TRANSFER_MODE);
61762306a36Sopenharmony_ci				imx_data->multiblock_status = WAIT_FOR_INT;
61862306a36Sopenharmony_ci			}
61962306a36Sopenharmony_ci	}
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci	writel(val, host->ioaddr + reg);
62262306a36Sopenharmony_ci}
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_cistatic u16 esdhc_readw_le(struct sdhci_host *host, int reg)
62562306a36Sopenharmony_ci{
62662306a36Sopenharmony_ci	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
62762306a36Sopenharmony_ci	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
62862306a36Sopenharmony_ci	u16 ret = 0;
62962306a36Sopenharmony_ci	u32 val;
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	if (unlikely(reg == SDHCI_HOST_VERSION)) {
63262306a36Sopenharmony_ci		reg ^= 2;
63362306a36Sopenharmony_ci		if (esdhc_is_usdhc(imx_data)) {
63462306a36Sopenharmony_ci			/*
63562306a36Sopenharmony_ci			 * The usdhc register returns a wrong host version.
63662306a36Sopenharmony_ci			 * Correct it here.
63762306a36Sopenharmony_ci			 */
63862306a36Sopenharmony_ci			return SDHCI_SPEC_300;
63962306a36Sopenharmony_ci		}
64062306a36Sopenharmony_ci	}
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci	if (unlikely(reg == SDHCI_HOST_CONTROL2)) {
64362306a36Sopenharmony_ci		val = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
64462306a36Sopenharmony_ci		if (val & ESDHC_VENDOR_SPEC_VSELECT)
64562306a36Sopenharmony_ci			ret |= SDHCI_CTRL_VDD_180;
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci		if (esdhc_is_usdhc(imx_data)) {
64862306a36Sopenharmony_ci			if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING)
64962306a36Sopenharmony_ci				val = readl(host->ioaddr + ESDHC_MIX_CTRL);
65062306a36Sopenharmony_ci			else if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING)
65162306a36Sopenharmony_ci				/* the std tuning bits is in ACMD12_ERR for imx6sl */
65262306a36Sopenharmony_ci				val = readl(host->ioaddr + SDHCI_AUTO_CMD_STATUS);
65362306a36Sopenharmony_ci		}
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci		if (val & ESDHC_MIX_CTRL_EXE_TUNE)
65662306a36Sopenharmony_ci			ret |= SDHCI_CTRL_EXEC_TUNING;
65762306a36Sopenharmony_ci		if (val & ESDHC_MIX_CTRL_SMPCLK_SEL)
65862306a36Sopenharmony_ci			ret |= SDHCI_CTRL_TUNED_CLK;
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci		ret &= ~SDHCI_CTRL_PRESET_VAL_ENABLE;
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci		return ret;
66362306a36Sopenharmony_ci	}
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci	if (unlikely(reg == SDHCI_TRANSFER_MODE)) {
66662306a36Sopenharmony_ci		if (esdhc_is_usdhc(imx_data)) {
66762306a36Sopenharmony_ci			u32 m = readl(host->ioaddr + ESDHC_MIX_CTRL);
66862306a36Sopenharmony_ci			ret = m & ESDHC_MIX_CTRL_SDHCI_MASK;
66962306a36Sopenharmony_ci			/* Swap AC23 bit */
67062306a36Sopenharmony_ci			if (m & ESDHC_MIX_CTRL_AC23EN) {
67162306a36Sopenharmony_ci				ret &= ~ESDHC_MIX_CTRL_AC23EN;
67262306a36Sopenharmony_ci				ret |= SDHCI_TRNS_AUTO_CMD23;
67362306a36Sopenharmony_ci			}
67462306a36Sopenharmony_ci		} else {
67562306a36Sopenharmony_ci			ret = readw(host->ioaddr + SDHCI_TRANSFER_MODE);
67662306a36Sopenharmony_ci		}
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci		return ret;
67962306a36Sopenharmony_ci	}
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	return readw(host->ioaddr + reg);
68262306a36Sopenharmony_ci}
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_cistatic void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg)
68562306a36Sopenharmony_ci{
68662306a36Sopenharmony_ci	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
68762306a36Sopenharmony_ci	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
68862306a36Sopenharmony_ci	u32 new_val = 0;
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	switch (reg) {
69162306a36Sopenharmony_ci	case SDHCI_CLOCK_CONTROL:
69262306a36Sopenharmony_ci		new_val = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
69362306a36Sopenharmony_ci		if (val & SDHCI_CLOCK_CARD_EN)
69462306a36Sopenharmony_ci			new_val |= ESDHC_VENDOR_SPEC_FRC_SDCLK_ON;
69562306a36Sopenharmony_ci		else
69662306a36Sopenharmony_ci			new_val &= ~ESDHC_VENDOR_SPEC_FRC_SDCLK_ON;
69762306a36Sopenharmony_ci		writel(new_val, host->ioaddr + ESDHC_VENDOR_SPEC);
69862306a36Sopenharmony_ci		if (!(new_val & ESDHC_VENDOR_SPEC_FRC_SDCLK_ON))
69962306a36Sopenharmony_ci			esdhc_wait_for_card_clock_gate_off(host);
70062306a36Sopenharmony_ci		return;
70162306a36Sopenharmony_ci	case SDHCI_HOST_CONTROL2:
70262306a36Sopenharmony_ci		new_val = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
70362306a36Sopenharmony_ci		if (val & SDHCI_CTRL_VDD_180)
70462306a36Sopenharmony_ci			new_val |= ESDHC_VENDOR_SPEC_VSELECT;
70562306a36Sopenharmony_ci		else
70662306a36Sopenharmony_ci			new_val &= ~ESDHC_VENDOR_SPEC_VSELECT;
70762306a36Sopenharmony_ci		writel(new_val, host->ioaddr + ESDHC_VENDOR_SPEC);
70862306a36Sopenharmony_ci		if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) {
70962306a36Sopenharmony_ci			u32 v = readl(host->ioaddr + SDHCI_AUTO_CMD_STATUS);
71062306a36Sopenharmony_ci			u32 m = readl(host->ioaddr + ESDHC_MIX_CTRL);
71162306a36Sopenharmony_ci			if (val & SDHCI_CTRL_TUNED_CLK) {
71262306a36Sopenharmony_ci				v |= ESDHC_MIX_CTRL_SMPCLK_SEL;
71362306a36Sopenharmony_ci			} else {
71462306a36Sopenharmony_ci				v &= ~ESDHC_MIX_CTRL_SMPCLK_SEL;
71562306a36Sopenharmony_ci				m &= ~ESDHC_MIX_CTRL_FBCLK_SEL;
71662306a36Sopenharmony_ci			}
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci			if (val & SDHCI_CTRL_EXEC_TUNING) {
71962306a36Sopenharmony_ci				v |= ESDHC_MIX_CTRL_EXE_TUNE;
72062306a36Sopenharmony_ci				m |= ESDHC_MIX_CTRL_FBCLK_SEL;
72162306a36Sopenharmony_ci			} else {
72262306a36Sopenharmony_ci				v &= ~ESDHC_MIX_CTRL_EXE_TUNE;
72362306a36Sopenharmony_ci			}
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci			writel(v, host->ioaddr + SDHCI_AUTO_CMD_STATUS);
72662306a36Sopenharmony_ci			writel(m, host->ioaddr + ESDHC_MIX_CTRL);
72762306a36Sopenharmony_ci		}
72862306a36Sopenharmony_ci		return;
72962306a36Sopenharmony_ci	case SDHCI_TRANSFER_MODE:
73062306a36Sopenharmony_ci		if ((imx_data->socdata->flags & ESDHC_FLAG_MULTIBLK_NO_INT)
73162306a36Sopenharmony_ci				&& (host->cmd->opcode == SD_IO_RW_EXTENDED)
73262306a36Sopenharmony_ci				&& (host->cmd->data->blocks > 1)
73362306a36Sopenharmony_ci				&& (host->cmd->data->flags & MMC_DATA_READ)) {
73462306a36Sopenharmony_ci			u32 v;
73562306a36Sopenharmony_ci			v = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
73662306a36Sopenharmony_ci			v |= ESDHC_VENDOR_SPEC_SDIO_QUIRK;
73762306a36Sopenharmony_ci			writel(v, host->ioaddr + ESDHC_VENDOR_SPEC);
73862306a36Sopenharmony_ci		}
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci		if (esdhc_is_usdhc(imx_data)) {
74162306a36Sopenharmony_ci			u32 wml;
74262306a36Sopenharmony_ci			u32 m = readl(host->ioaddr + ESDHC_MIX_CTRL);
74362306a36Sopenharmony_ci			/* Swap AC23 bit */
74462306a36Sopenharmony_ci			if (val & SDHCI_TRNS_AUTO_CMD23) {
74562306a36Sopenharmony_ci				val &= ~SDHCI_TRNS_AUTO_CMD23;
74662306a36Sopenharmony_ci				val |= ESDHC_MIX_CTRL_AC23EN;
74762306a36Sopenharmony_ci			}
74862306a36Sopenharmony_ci			m = val | (m & ~ESDHC_MIX_CTRL_SDHCI_MASK);
74962306a36Sopenharmony_ci			writel(m, host->ioaddr + ESDHC_MIX_CTRL);
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci			/* Set watermark levels for PIO access to maximum value
75262306a36Sopenharmony_ci			 * (128 words) to accommodate full 512 bytes buffer.
75362306a36Sopenharmony_ci			 * For DMA access restore the levels to default value.
75462306a36Sopenharmony_ci			 */
75562306a36Sopenharmony_ci			m = readl(host->ioaddr + ESDHC_WTMK_LVL);
75662306a36Sopenharmony_ci			if (val & SDHCI_TRNS_DMA) {
75762306a36Sopenharmony_ci				wml = ESDHC_WTMK_LVL_WML_VAL_DEF;
75862306a36Sopenharmony_ci			} else {
75962306a36Sopenharmony_ci				u8 ctrl;
76062306a36Sopenharmony_ci				wml = ESDHC_WTMK_LVL_WML_VAL_MAX;
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci				/*
76362306a36Sopenharmony_ci				 * Since already disable DMA mode, so also need
76462306a36Sopenharmony_ci				 * to clear the DMASEL. Otherwise, for standard
76562306a36Sopenharmony_ci				 * tuning, when send tuning command, usdhc will
76662306a36Sopenharmony_ci				 * still prefetch the ADMA script from wrong
76762306a36Sopenharmony_ci				 * DMA address, then we will see IOMMU report
76862306a36Sopenharmony_ci				 * some error which show lack of TLB mapping.
76962306a36Sopenharmony_ci				 */
77062306a36Sopenharmony_ci				ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
77162306a36Sopenharmony_ci				ctrl &= ~SDHCI_CTRL_DMA_MASK;
77262306a36Sopenharmony_ci				sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
77362306a36Sopenharmony_ci			}
77462306a36Sopenharmony_ci			m &= ~(ESDHC_WTMK_LVL_RD_WML_MASK |
77562306a36Sopenharmony_ci			       ESDHC_WTMK_LVL_WR_WML_MASK);
77662306a36Sopenharmony_ci			m |= (wml << ESDHC_WTMK_LVL_RD_WML_SHIFT) |
77762306a36Sopenharmony_ci			     (wml << ESDHC_WTMK_LVL_WR_WML_SHIFT);
77862306a36Sopenharmony_ci			writel(m, host->ioaddr + ESDHC_WTMK_LVL);
77962306a36Sopenharmony_ci		} else {
78062306a36Sopenharmony_ci			/*
78162306a36Sopenharmony_ci			 * Postpone this write, we must do it together with a
78262306a36Sopenharmony_ci			 * command write that is down below.
78362306a36Sopenharmony_ci			 */
78462306a36Sopenharmony_ci			imx_data->scratchpad = val;
78562306a36Sopenharmony_ci		}
78662306a36Sopenharmony_ci		return;
78762306a36Sopenharmony_ci	case SDHCI_COMMAND:
78862306a36Sopenharmony_ci		if (host->cmd->opcode == MMC_STOP_TRANSMISSION)
78962306a36Sopenharmony_ci			val |= SDHCI_CMD_ABORTCMD;
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci		if ((host->cmd->opcode == MMC_SET_BLOCK_COUNT) &&
79262306a36Sopenharmony_ci		    (imx_data->socdata->flags & ESDHC_FLAG_MULTIBLK_NO_INT))
79362306a36Sopenharmony_ci			imx_data->multiblock_status = MULTIBLK_IN_PROCESS;
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_ci		if (esdhc_is_usdhc(imx_data))
79662306a36Sopenharmony_ci			writel(val << 16,
79762306a36Sopenharmony_ci			       host->ioaddr + SDHCI_TRANSFER_MODE);
79862306a36Sopenharmony_ci		else
79962306a36Sopenharmony_ci			writel(val << 16 | imx_data->scratchpad,
80062306a36Sopenharmony_ci			       host->ioaddr + SDHCI_TRANSFER_MODE);
80162306a36Sopenharmony_ci		return;
80262306a36Sopenharmony_ci	case SDHCI_BLOCK_SIZE:
80362306a36Sopenharmony_ci		val &= ~SDHCI_MAKE_BLKSZ(0x7, 0);
80462306a36Sopenharmony_ci		break;
80562306a36Sopenharmony_ci	}
80662306a36Sopenharmony_ci	esdhc_clrset_le(host, 0xffff, val, reg);
80762306a36Sopenharmony_ci}
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_cistatic u8 esdhc_readb_le(struct sdhci_host *host, int reg)
81062306a36Sopenharmony_ci{
81162306a36Sopenharmony_ci	u8 ret;
81262306a36Sopenharmony_ci	u32 val;
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci	switch (reg) {
81562306a36Sopenharmony_ci	case SDHCI_HOST_CONTROL:
81662306a36Sopenharmony_ci		val = readl(host->ioaddr + reg);
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci		ret = val & SDHCI_CTRL_LED;
81962306a36Sopenharmony_ci		ret |= (val >> 5) & SDHCI_CTRL_DMA_MASK;
82062306a36Sopenharmony_ci		ret |= (val & ESDHC_CTRL_4BITBUS);
82162306a36Sopenharmony_ci		ret |= (val & ESDHC_CTRL_8BITBUS) << 3;
82262306a36Sopenharmony_ci		return ret;
82362306a36Sopenharmony_ci	}
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci	return readb(host->ioaddr + reg);
82662306a36Sopenharmony_ci}
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_cistatic void esdhc_writeb_le(struct sdhci_host *host, u8 val, int reg)
82962306a36Sopenharmony_ci{
83062306a36Sopenharmony_ci	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
83162306a36Sopenharmony_ci	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
83262306a36Sopenharmony_ci	u32 new_val = 0;
83362306a36Sopenharmony_ci	u32 mask;
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci	switch (reg) {
83662306a36Sopenharmony_ci	case SDHCI_POWER_CONTROL:
83762306a36Sopenharmony_ci		/*
83862306a36Sopenharmony_ci		 * FSL put some DMA bits here
83962306a36Sopenharmony_ci		 * If your board has a regulator, code should be here
84062306a36Sopenharmony_ci		 */
84162306a36Sopenharmony_ci		return;
84262306a36Sopenharmony_ci	case SDHCI_HOST_CONTROL:
84362306a36Sopenharmony_ci		/* FSL messed up here, so we need to manually compose it. */
84462306a36Sopenharmony_ci		new_val = val & SDHCI_CTRL_LED;
84562306a36Sopenharmony_ci		/* ensure the endianness */
84662306a36Sopenharmony_ci		new_val |= ESDHC_HOST_CONTROL_LE;
84762306a36Sopenharmony_ci		/* bits 8&9 are reserved on mx25 */
84862306a36Sopenharmony_ci		if (!is_imx25_esdhc(imx_data)) {
84962306a36Sopenharmony_ci			/* DMA mode bits are shifted */
85062306a36Sopenharmony_ci			new_val |= (val & SDHCI_CTRL_DMA_MASK) << 5;
85162306a36Sopenharmony_ci		}
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_ci		/*
85462306a36Sopenharmony_ci		 * Do not touch buswidth bits here. This is done in
85562306a36Sopenharmony_ci		 * esdhc_pltfm_bus_width.
85662306a36Sopenharmony_ci		 * Do not touch the D3CD bit either which is used for the
85762306a36Sopenharmony_ci		 * SDIO interrupt erratum workaround.
85862306a36Sopenharmony_ci		 */
85962306a36Sopenharmony_ci		mask = 0xffff & ~(ESDHC_CTRL_BUSWIDTH_MASK | ESDHC_CTRL_D3CD);
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci		esdhc_clrset_le(host, mask, new_val, reg);
86262306a36Sopenharmony_ci		return;
86362306a36Sopenharmony_ci	case SDHCI_SOFTWARE_RESET:
86462306a36Sopenharmony_ci		if (val & SDHCI_RESET_DATA)
86562306a36Sopenharmony_ci			new_val = readl(host->ioaddr + SDHCI_HOST_CONTROL);
86662306a36Sopenharmony_ci		break;
86762306a36Sopenharmony_ci	}
86862306a36Sopenharmony_ci	esdhc_clrset_le(host, 0xff, val, reg);
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ci	if (reg == SDHCI_SOFTWARE_RESET) {
87162306a36Sopenharmony_ci		if (val & SDHCI_RESET_ALL) {
87262306a36Sopenharmony_ci			/*
87362306a36Sopenharmony_ci			 * The esdhc has a design violation to SDHC spec which
87462306a36Sopenharmony_ci			 * tells that software reset should not affect card
87562306a36Sopenharmony_ci			 * detection circuit. But esdhc clears its SYSCTL
87662306a36Sopenharmony_ci			 * register bits [0..2] during the software reset. This
87762306a36Sopenharmony_ci			 * will stop those clocks that card detection circuit
87862306a36Sopenharmony_ci			 * relies on. To work around it, we turn the clocks on
87962306a36Sopenharmony_ci			 * back to keep card detection circuit functional.
88062306a36Sopenharmony_ci			 */
88162306a36Sopenharmony_ci			esdhc_clrset_le(host, 0x7, 0x7, ESDHC_SYSTEM_CONTROL);
88262306a36Sopenharmony_ci			/*
88362306a36Sopenharmony_ci			 * The reset on usdhc fails to clear MIX_CTRL register.
88462306a36Sopenharmony_ci			 * Do it manually here.
88562306a36Sopenharmony_ci			 */
88662306a36Sopenharmony_ci			if (esdhc_is_usdhc(imx_data)) {
88762306a36Sopenharmony_ci				/*
88862306a36Sopenharmony_ci				 * the tuning bits should be kept during reset
88962306a36Sopenharmony_ci				 */
89062306a36Sopenharmony_ci				new_val = readl(host->ioaddr + ESDHC_MIX_CTRL);
89162306a36Sopenharmony_ci				writel(new_val & ESDHC_MIX_CTRL_TUNING_MASK,
89262306a36Sopenharmony_ci						host->ioaddr + ESDHC_MIX_CTRL);
89362306a36Sopenharmony_ci				imx_data->is_ddr = 0;
89462306a36Sopenharmony_ci			}
89562306a36Sopenharmony_ci		} else if (val & SDHCI_RESET_DATA) {
89662306a36Sopenharmony_ci			/*
89762306a36Sopenharmony_ci			 * The eSDHC DAT line software reset clears at least the
89862306a36Sopenharmony_ci			 * data transfer width on i.MX25, so make sure that the
89962306a36Sopenharmony_ci			 * Host Control register is unaffected.
90062306a36Sopenharmony_ci			 */
90162306a36Sopenharmony_ci			esdhc_clrset_le(host, 0xff, new_val,
90262306a36Sopenharmony_ci					SDHCI_HOST_CONTROL);
90362306a36Sopenharmony_ci		}
90462306a36Sopenharmony_ci	}
90562306a36Sopenharmony_ci}
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_cistatic unsigned int esdhc_pltfm_get_max_clock(struct sdhci_host *host)
90862306a36Sopenharmony_ci{
90962306a36Sopenharmony_ci	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_ci	return pltfm_host->clock;
91262306a36Sopenharmony_ci}
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_cistatic unsigned int esdhc_pltfm_get_min_clock(struct sdhci_host *host)
91562306a36Sopenharmony_ci{
91662306a36Sopenharmony_ci	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci	return pltfm_host->clock / 256 / 16;
91962306a36Sopenharmony_ci}
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_cistatic inline void esdhc_pltfm_set_clock(struct sdhci_host *host,
92262306a36Sopenharmony_ci					 unsigned int clock)
92362306a36Sopenharmony_ci{
92462306a36Sopenharmony_ci	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
92562306a36Sopenharmony_ci	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
92662306a36Sopenharmony_ci	unsigned int host_clock = pltfm_host->clock;
92762306a36Sopenharmony_ci	int ddr_pre_div = imx_data->is_ddr ? 2 : 1;
92862306a36Sopenharmony_ci	int pre_div = 1;
92962306a36Sopenharmony_ci	int div = 1;
93062306a36Sopenharmony_ci	int ret;
93162306a36Sopenharmony_ci	u32 temp, val;
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci	if (esdhc_is_usdhc(imx_data)) {
93462306a36Sopenharmony_ci		val = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
93562306a36Sopenharmony_ci		writel(val & ~ESDHC_VENDOR_SPEC_FRC_SDCLK_ON,
93662306a36Sopenharmony_ci			host->ioaddr + ESDHC_VENDOR_SPEC);
93762306a36Sopenharmony_ci		esdhc_wait_for_card_clock_gate_off(host);
93862306a36Sopenharmony_ci	}
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci	if (clock == 0) {
94162306a36Sopenharmony_ci		host->mmc->actual_clock = 0;
94262306a36Sopenharmony_ci		return;
94362306a36Sopenharmony_ci	}
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci	/* For i.MX53 eSDHCv3, SYSCTL.SDCLKFS may not be set to 0. */
94662306a36Sopenharmony_ci	if (is_imx53_esdhc(imx_data)) {
94762306a36Sopenharmony_ci		/*
94862306a36Sopenharmony_ci		 * According to the i.MX53 reference manual, if DLLCTRL[10] can
94962306a36Sopenharmony_ci		 * be set, then the controller is eSDHCv3, else it is eSDHCv2.
95062306a36Sopenharmony_ci		 */
95162306a36Sopenharmony_ci		val = readl(host->ioaddr + ESDHC_DLL_CTRL);
95262306a36Sopenharmony_ci		writel(val | BIT(10), host->ioaddr + ESDHC_DLL_CTRL);
95362306a36Sopenharmony_ci		temp = readl(host->ioaddr + ESDHC_DLL_CTRL);
95462306a36Sopenharmony_ci		writel(val, host->ioaddr + ESDHC_DLL_CTRL);
95562306a36Sopenharmony_ci		if (temp & BIT(10))
95662306a36Sopenharmony_ci			pre_div = 2;
95762306a36Sopenharmony_ci	}
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci	temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
96062306a36Sopenharmony_ci	temp &= ~(ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN
96162306a36Sopenharmony_ci		| ESDHC_CLOCK_MASK);
96262306a36Sopenharmony_ci	sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci	if ((imx_data->socdata->flags & ESDHC_FLAG_ERR010450) &&
96562306a36Sopenharmony_ci	    (!(host->quirks2 & SDHCI_QUIRK2_NO_1_8_V))) {
96662306a36Sopenharmony_ci		unsigned int max_clock;
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_ci		max_clock = imx_data->is_ddr ? 45000000 : 150000000;
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_ci		clock = min(clock, max_clock);
97162306a36Sopenharmony_ci	}
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci	while (host_clock / (16 * pre_div * ddr_pre_div) > clock &&
97462306a36Sopenharmony_ci			pre_div < 256)
97562306a36Sopenharmony_ci		pre_div *= 2;
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci	while (host_clock / (div * pre_div * ddr_pre_div) > clock && div < 16)
97862306a36Sopenharmony_ci		div++;
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci	host->mmc->actual_clock = host_clock / (div * pre_div * ddr_pre_div);
98162306a36Sopenharmony_ci	dev_dbg(mmc_dev(host->mmc), "desired SD clock: %d, actual: %d\n",
98262306a36Sopenharmony_ci		clock, host->mmc->actual_clock);
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci	pre_div >>= 1;
98562306a36Sopenharmony_ci	div--;
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_ci	temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
98862306a36Sopenharmony_ci	temp |= (ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN
98962306a36Sopenharmony_ci		| (div << ESDHC_DIVIDER_SHIFT)
99062306a36Sopenharmony_ci		| (pre_div << ESDHC_PREDIV_SHIFT));
99162306a36Sopenharmony_ci	sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_ci	/* need to wait the bit 3 of the PRSSTAT to be set, make sure card clock is stable */
99462306a36Sopenharmony_ci	ret = readl_poll_timeout(host->ioaddr + ESDHC_PRSSTAT, temp,
99562306a36Sopenharmony_ci				(temp & ESDHC_CLOCK_STABLE), 2, 100);
99662306a36Sopenharmony_ci	if (ret == -ETIMEDOUT)
99762306a36Sopenharmony_ci		dev_warn(mmc_dev(host->mmc), "card clock still not stable in 100us!.\n");
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci	if (esdhc_is_usdhc(imx_data)) {
100062306a36Sopenharmony_ci		val = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
100162306a36Sopenharmony_ci		writel(val | ESDHC_VENDOR_SPEC_FRC_SDCLK_ON,
100262306a36Sopenharmony_ci			host->ioaddr + ESDHC_VENDOR_SPEC);
100362306a36Sopenharmony_ci	}
100462306a36Sopenharmony_ci
100562306a36Sopenharmony_ci}
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_cistatic unsigned int esdhc_pltfm_get_ro(struct sdhci_host *host)
100862306a36Sopenharmony_ci{
100962306a36Sopenharmony_ci	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
101062306a36Sopenharmony_ci	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
101162306a36Sopenharmony_ci	struct esdhc_platform_data *boarddata = &imx_data->boarddata;
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_ci	switch (boarddata->wp_type) {
101462306a36Sopenharmony_ci	case ESDHC_WP_GPIO:
101562306a36Sopenharmony_ci		return mmc_gpio_get_ro(host->mmc);
101662306a36Sopenharmony_ci	case ESDHC_WP_CONTROLLER:
101762306a36Sopenharmony_ci		return !(readl(host->ioaddr + SDHCI_PRESENT_STATE) &
101862306a36Sopenharmony_ci			       SDHCI_WRITE_PROTECT);
101962306a36Sopenharmony_ci	case ESDHC_WP_NONE:
102062306a36Sopenharmony_ci		break;
102162306a36Sopenharmony_ci	}
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci	return -ENOSYS;
102462306a36Sopenharmony_ci}
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_cistatic void esdhc_pltfm_set_bus_width(struct sdhci_host *host, int width)
102762306a36Sopenharmony_ci{
102862306a36Sopenharmony_ci	u32 ctrl;
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_ci	switch (width) {
103162306a36Sopenharmony_ci	case MMC_BUS_WIDTH_8:
103262306a36Sopenharmony_ci		ctrl = ESDHC_CTRL_8BITBUS;
103362306a36Sopenharmony_ci		break;
103462306a36Sopenharmony_ci	case MMC_BUS_WIDTH_4:
103562306a36Sopenharmony_ci		ctrl = ESDHC_CTRL_4BITBUS;
103662306a36Sopenharmony_ci		break;
103762306a36Sopenharmony_ci	default:
103862306a36Sopenharmony_ci		ctrl = 0;
103962306a36Sopenharmony_ci		break;
104062306a36Sopenharmony_ci	}
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_ci	esdhc_clrset_le(host, ESDHC_CTRL_BUSWIDTH_MASK, ctrl,
104362306a36Sopenharmony_ci			SDHCI_HOST_CONTROL);
104462306a36Sopenharmony_ci}
104562306a36Sopenharmony_ci
104662306a36Sopenharmony_cistatic void esdhc_reset_tuning(struct sdhci_host *host)
104762306a36Sopenharmony_ci{
104862306a36Sopenharmony_ci	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
104962306a36Sopenharmony_ci	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
105062306a36Sopenharmony_ci	u32 ctrl;
105162306a36Sopenharmony_ci	int ret;
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_ci	/* Reset the tuning circuit */
105462306a36Sopenharmony_ci	if (esdhc_is_usdhc(imx_data)) {
105562306a36Sopenharmony_ci		ctrl = readl(host->ioaddr + ESDHC_MIX_CTRL);
105662306a36Sopenharmony_ci		ctrl &= ~ESDHC_MIX_CTRL_AUTO_TUNE_EN;
105762306a36Sopenharmony_ci		if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING) {
105862306a36Sopenharmony_ci			ctrl &= ~ESDHC_MIX_CTRL_SMPCLK_SEL;
105962306a36Sopenharmony_ci			ctrl &= ~ESDHC_MIX_CTRL_FBCLK_SEL;
106062306a36Sopenharmony_ci			writel(ctrl, host->ioaddr + ESDHC_MIX_CTRL);
106162306a36Sopenharmony_ci			writel(0, host->ioaddr + ESDHC_TUNE_CTRL_STATUS);
106262306a36Sopenharmony_ci		} else if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) {
106362306a36Sopenharmony_ci			writel(ctrl, host->ioaddr + ESDHC_MIX_CTRL);
106462306a36Sopenharmony_ci			ctrl = readl(host->ioaddr + SDHCI_AUTO_CMD_STATUS);
106562306a36Sopenharmony_ci			ctrl &= ~ESDHC_MIX_CTRL_SMPCLK_SEL;
106662306a36Sopenharmony_ci			ctrl &= ~ESDHC_MIX_CTRL_EXE_TUNE;
106762306a36Sopenharmony_ci			writel(ctrl, host->ioaddr + SDHCI_AUTO_CMD_STATUS);
106862306a36Sopenharmony_ci			/* Make sure ESDHC_MIX_CTRL_EXE_TUNE cleared */
106962306a36Sopenharmony_ci			ret = readl_poll_timeout(host->ioaddr + SDHCI_AUTO_CMD_STATUS,
107062306a36Sopenharmony_ci				ctrl, !(ctrl & ESDHC_MIX_CTRL_EXE_TUNE), 1, 50);
107162306a36Sopenharmony_ci			if (ret == -ETIMEDOUT)
107262306a36Sopenharmony_ci				dev_warn(mmc_dev(host->mmc),
107362306a36Sopenharmony_ci				 "Warning! clear execute tuning bit failed\n");
107462306a36Sopenharmony_ci			/*
107562306a36Sopenharmony_ci			 * SDHCI_INT_DATA_AVAIL is W1C bit, set this bit will clear the
107662306a36Sopenharmony_ci			 * usdhc IP internal logic flag execute_tuning_with_clr_buf, which
107762306a36Sopenharmony_ci			 * will finally make sure the normal data transfer logic correct.
107862306a36Sopenharmony_ci			 */
107962306a36Sopenharmony_ci			ctrl = readl(host->ioaddr + SDHCI_INT_STATUS);
108062306a36Sopenharmony_ci			ctrl |= SDHCI_INT_DATA_AVAIL;
108162306a36Sopenharmony_ci			writel(ctrl, host->ioaddr + SDHCI_INT_STATUS);
108262306a36Sopenharmony_ci		}
108362306a36Sopenharmony_ci	}
108462306a36Sopenharmony_ci}
108562306a36Sopenharmony_ci
108662306a36Sopenharmony_cistatic void usdhc_init_card(struct mmc_host *mmc, struct mmc_card *card)
108762306a36Sopenharmony_ci{
108862306a36Sopenharmony_ci	struct sdhci_host *host = mmc_priv(mmc);
108962306a36Sopenharmony_ci	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
109062306a36Sopenharmony_ci	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_ci	imx_data->init_card_type = card->type;
109362306a36Sopenharmony_ci}
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_cistatic int usdhc_execute_tuning(struct mmc_host *mmc, u32 opcode)
109662306a36Sopenharmony_ci{
109762306a36Sopenharmony_ci	struct sdhci_host *host = mmc_priv(mmc);
109862306a36Sopenharmony_ci	int err;
109962306a36Sopenharmony_ci
110062306a36Sopenharmony_ci	/*
110162306a36Sopenharmony_ci	 * i.MX uSDHC internally already uses a fixed optimized timing for
110262306a36Sopenharmony_ci	 * DDR50, normally does not require tuning for DDR50 mode.
110362306a36Sopenharmony_ci	 */
110462306a36Sopenharmony_ci	if (host->timing == MMC_TIMING_UHS_DDR50)
110562306a36Sopenharmony_ci		return 0;
110662306a36Sopenharmony_ci
110762306a36Sopenharmony_ci	/*
110862306a36Sopenharmony_ci	 * Reset tuning circuit logic. If not, the previous tuning result
110962306a36Sopenharmony_ci	 * will impact current tuning, make current tuning can't set the
111062306a36Sopenharmony_ci	 * correct delay cell.
111162306a36Sopenharmony_ci	 */
111262306a36Sopenharmony_ci	esdhc_reset_tuning(host);
111362306a36Sopenharmony_ci	err = sdhci_execute_tuning(mmc, opcode);
111462306a36Sopenharmony_ci	/* If tuning done, enable auto tuning */
111562306a36Sopenharmony_ci	if (!err && !host->tuning_err)
111662306a36Sopenharmony_ci		usdhc_auto_tuning_mode_sel_and_en(host);
111762306a36Sopenharmony_ci
111862306a36Sopenharmony_ci	return err;
111962306a36Sopenharmony_ci}
112062306a36Sopenharmony_ci
112162306a36Sopenharmony_cistatic void esdhc_prepare_tuning(struct sdhci_host *host, u32 val)
112262306a36Sopenharmony_ci{
112362306a36Sopenharmony_ci	u32 reg;
112462306a36Sopenharmony_ci	u8 sw_rst;
112562306a36Sopenharmony_ci	int ret;
112662306a36Sopenharmony_ci
112762306a36Sopenharmony_ci	/* FIXME: delay a bit for card to be ready for next tuning due to errors */
112862306a36Sopenharmony_ci	mdelay(1);
112962306a36Sopenharmony_ci
113062306a36Sopenharmony_ci	/* IC suggest to reset USDHC before every tuning command */
113162306a36Sopenharmony_ci	esdhc_clrset_le(host, 0xff, SDHCI_RESET_ALL, SDHCI_SOFTWARE_RESET);
113262306a36Sopenharmony_ci	ret = readb_poll_timeout(host->ioaddr + SDHCI_SOFTWARE_RESET, sw_rst,
113362306a36Sopenharmony_ci				!(sw_rst & SDHCI_RESET_ALL), 10, 100);
113462306a36Sopenharmony_ci	if (ret == -ETIMEDOUT)
113562306a36Sopenharmony_ci		dev_warn(mmc_dev(host->mmc),
113662306a36Sopenharmony_ci		"warning! RESET_ALL never complete before sending tuning command\n");
113762306a36Sopenharmony_ci
113862306a36Sopenharmony_ci	reg = readl(host->ioaddr + ESDHC_MIX_CTRL);
113962306a36Sopenharmony_ci	reg |= ESDHC_MIX_CTRL_EXE_TUNE | ESDHC_MIX_CTRL_SMPCLK_SEL |
114062306a36Sopenharmony_ci			ESDHC_MIX_CTRL_FBCLK_SEL;
114162306a36Sopenharmony_ci	writel(reg, host->ioaddr + ESDHC_MIX_CTRL);
114262306a36Sopenharmony_ci	writel(val << 8, host->ioaddr + ESDHC_TUNE_CTRL_STATUS);
114362306a36Sopenharmony_ci	dev_dbg(mmc_dev(host->mmc),
114462306a36Sopenharmony_ci		"tuning with delay 0x%x ESDHC_TUNE_CTRL_STATUS 0x%x\n",
114562306a36Sopenharmony_ci			val, readl(host->ioaddr + ESDHC_TUNE_CTRL_STATUS));
114662306a36Sopenharmony_ci}
114762306a36Sopenharmony_ci
114862306a36Sopenharmony_cistatic void esdhc_post_tuning(struct sdhci_host *host)
114962306a36Sopenharmony_ci{
115062306a36Sopenharmony_ci	u32 reg;
115162306a36Sopenharmony_ci
115262306a36Sopenharmony_ci	reg = readl(host->ioaddr + ESDHC_MIX_CTRL);
115362306a36Sopenharmony_ci	reg &= ~ESDHC_MIX_CTRL_EXE_TUNE;
115462306a36Sopenharmony_ci	writel(reg, host->ioaddr + ESDHC_MIX_CTRL);
115562306a36Sopenharmony_ci}
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_cistatic int esdhc_executing_tuning(struct sdhci_host *host, u32 opcode)
115862306a36Sopenharmony_ci{
115962306a36Sopenharmony_ci	int min, max, avg, ret;
116062306a36Sopenharmony_ci
116162306a36Sopenharmony_ci	/* find the mininum delay first which can pass tuning */
116262306a36Sopenharmony_ci	min = ESDHC_TUNE_CTRL_MIN;
116362306a36Sopenharmony_ci	while (min < ESDHC_TUNE_CTRL_MAX) {
116462306a36Sopenharmony_ci		esdhc_prepare_tuning(host, min);
116562306a36Sopenharmony_ci		if (!mmc_send_tuning(host->mmc, opcode, NULL))
116662306a36Sopenharmony_ci			break;
116762306a36Sopenharmony_ci		min += ESDHC_TUNE_CTRL_STEP;
116862306a36Sopenharmony_ci	}
116962306a36Sopenharmony_ci
117062306a36Sopenharmony_ci	/* find the maxinum delay which can not pass tuning */
117162306a36Sopenharmony_ci	max = min + ESDHC_TUNE_CTRL_STEP;
117262306a36Sopenharmony_ci	while (max < ESDHC_TUNE_CTRL_MAX) {
117362306a36Sopenharmony_ci		esdhc_prepare_tuning(host, max);
117462306a36Sopenharmony_ci		if (mmc_send_tuning(host->mmc, opcode, NULL)) {
117562306a36Sopenharmony_ci			max -= ESDHC_TUNE_CTRL_STEP;
117662306a36Sopenharmony_ci			break;
117762306a36Sopenharmony_ci		}
117862306a36Sopenharmony_ci		max += ESDHC_TUNE_CTRL_STEP;
117962306a36Sopenharmony_ci	}
118062306a36Sopenharmony_ci
118162306a36Sopenharmony_ci	/* use average delay to get the best timing */
118262306a36Sopenharmony_ci	avg = (min + max) / 2;
118362306a36Sopenharmony_ci	esdhc_prepare_tuning(host, avg);
118462306a36Sopenharmony_ci	ret = mmc_send_tuning(host->mmc, opcode, NULL);
118562306a36Sopenharmony_ci	esdhc_post_tuning(host);
118662306a36Sopenharmony_ci
118762306a36Sopenharmony_ci	dev_dbg(mmc_dev(host->mmc), "tuning %s at 0x%x ret %d\n",
118862306a36Sopenharmony_ci		ret ? "failed" : "passed", avg, ret);
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_ci	return ret;
119162306a36Sopenharmony_ci}
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_cistatic void esdhc_hs400_enhanced_strobe(struct mmc_host *mmc, struct mmc_ios *ios)
119462306a36Sopenharmony_ci{
119562306a36Sopenharmony_ci	struct sdhci_host *host = mmc_priv(mmc);
119662306a36Sopenharmony_ci	u32 m;
119762306a36Sopenharmony_ci
119862306a36Sopenharmony_ci	m = readl(host->ioaddr + ESDHC_MIX_CTRL);
119962306a36Sopenharmony_ci	if (ios->enhanced_strobe)
120062306a36Sopenharmony_ci		m |= ESDHC_MIX_CTRL_HS400_ES_EN;
120162306a36Sopenharmony_ci	else
120262306a36Sopenharmony_ci		m &= ~ESDHC_MIX_CTRL_HS400_ES_EN;
120362306a36Sopenharmony_ci	writel(m, host->ioaddr + ESDHC_MIX_CTRL);
120462306a36Sopenharmony_ci}
120562306a36Sopenharmony_ci
120662306a36Sopenharmony_cistatic int esdhc_change_pinstate(struct sdhci_host *host,
120762306a36Sopenharmony_ci						unsigned int uhs)
120862306a36Sopenharmony_ci{
120962306a36Sopenharmony_ci	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
121062306a36Sopenharmony_ci	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
121162306a36Sopenharmony_ci	struct pinctrl_state *pinctrl;
121262306a36Sopenharmony_ci
121362306a36Sopenharmony_ci	dev_dbg(mmc_dev(host->mmc), "change pinctrl state for uhs %d\n", uhs);
121462306a36Sopenharmony_ci
121562306a36Sopenharmony_ci	if (IS_ERR(imx_data->pinctrl) ||
121662306a36Sopenharmony_ci		IS_ERR(imx_data->pins_100mhz) ||
121762306a36Sopenharmony_ci		IS_ERR(imx_data->pins_200mhz))
121862306a36Sopenharmony_ci		return -EINVAL;
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_ci	switch (uhs) {
122162306a36Sopenharmony_ci	case MMC_TIMING_UHS_SDR50:
122262306a36Sopenharmony_ci	case MMC_TIMING_UHS_DDR50:
122362306a36Sopenharmony_ci		pinctrl = imx_data->pins_100mhz;
122462306a36Sopenharmony_ci		break;
122562306a36Sopenharmony_ci	case MMC_TIMING_UHS_SDR104:
122662306a36Sopenharmony_ci	case MMC_TIMING_MMC_HS200:
122762306a36Sopenharmony_ci	case MMC_TIMING_MMC_HS400:
122862306a36Sopenharmony_ci		pinctrl = imx_data->pins_200mhz;
122962306a36Sopenharmony_ci		break;
123062306a36Sopenharmony_ci	default:
123162306a36Sopenharmony_ci		/* back to default state for other legacy timing */
123262306a36Sopenharmony_ci		return pinctrl_select_default_state(mmc_dev(host->mmc));
123362306a36Sopenharmony_ci	}
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_ci	return pinctrl_select_state(imx_data->pinctrl, pinctrl);
123662306a36Sopenharmony_ci}
123762306a36Sopenharmony_ci
123862306a36Sopenharmony_ci/*
123962306a36Sopenharmony_ci * For HS400 eMMC, there is a data_strobe line. This signal is generated
124062306a36Sopenharmony_ci * by the device and used for data output and CRC status response output
124162306a36Sopenharmony_ci * in HS400 mode. The frequency of this signal follows the frequency of
124262306a36Sopenharmony_ci * CLK generated by host. The host receives the data which is aligned to the
124362306a36Sopenharmony_ci * edge of data_strobe line. Due to the time delay between CLK line and
124462306a36Sopenharmony_ci * data_strobe line, if the delay time is larger than one clock cycle,
124562306a36Sopenharmony_ci * then CLK and data_strobe line will be misaligned, read error shows up.
124662306a36Sopenharmony_ci */
124762306a36Sopenharmony_cistatic void esdhc_set_strobe_dll(struct sdhci_host *host)
124862306a36Sopenharmony_ci{
124962306a36Sopenharmony_ci	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
125062306a36Sopenharmony_ci	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
125162306a36Sopenharmony_ci	u32 strobe_delay;
125262306a36Sopenharmony_ci	u32 v;
125362306a36Sopenharmony_ci	int ret;
125462306a36Sopenharmony_ci
125562306a36Sopenharmony_ci	/* disable clock before enabling strobe dll */
125662306a36Sopenharmony_ci	writel(readl(host->ioaddr + ESDHC_VENDOR_SPEC) &
125762306a36Sopenharmony_ci		~ESDHC_VENDOR_SPEC_FRC_SDCLK_ON,
125862306a36Sopenharmony_ci		host->ioaddr + ESDHC_VENDOR_SPEC);
125962306a36Sopenharmony_ci	esdhc_wait_for_card_clock_gate_off(host);
126062306a36Sopenharmony_ci
126162306a36Sopenharmony_ci	/* force a reset on strobe dll */
126262306a36Sopenharmony_ci	writel(ESDHC_STROBE_DLL_CTRL_RESET,
126362306a36Sopenharmony_ci		host->ioaddr + ESDHC_STROBE_DLL_CTRL);
126462306a36Sopenharmony_ci	/* clear the reset bit on strobe dll before any setting */
126562306a36Sopenharmony_ci	writel(0, host->ioaddr + ESDHC_STROBE_DLL_CTRL);
126662306a36Sopenharmony_ci
126762306a36Sopenharmony_ci	/*
126862306a36Sopenharmony_ci	 * enable strobe dll ctrl and adjust the delay target
126962306a36Sopenharmony_ci	 * for the uSDHC loopback read clock
127062306a36Sopenharmony_ci	 */
127162306a36Sopenharmony_ci	if (imx_data->boarddata.strobe_dll_delay_target)
127262306a36Sopenharmony_ci		strobe_delay = imx_data->boarddata.strobe_dll_delay_target;
127362306a36Sopenharmony_ci	else
127462306a36Sopenharmony_ci		strobe_delay = ESDHC_STROBE_DLL_CTRL_SLV_DLY_TARGET_DEFAULT;
127562306a36Sopenharmony_ci	v = ESDHC_STROBE_DLL_CTRL_ENABLE |
127662306a36Sopenharmony_ci		ESDHC_STROBE_DLL_CTRL_SLV_UPDATE_INT_DEFAULT |
127762306a36Sopenharmony_ci		(strobe_delay << ESDHC_STROBE_DLL_CTRL_SLV_DLY_TARGET_SHIFT);
127862306a36Sopenharmony_ci	writel(v, host->ioaddr + ESDHC_STROBE_DLL_CTRL);
127962306a36Sopenharmony_ci
128062306a36Sopenharmony_ci	/* wait max 50us to get the REF/SLV lock */
128162306a36Sopenharmony_ci	ret = readl_poll_timeout(host->ioaddr + ESDHC_STROBE_DLL_STATUS, v,
128262306a36Sopenharmony_ci		((v & ESDHC_STROBE_DLL_STS_REF_LOCK) && (v & ESDHC_STROBE_DLL_STS_SLV_LOCK)), 1, 50);
128362306a36Sopenharmony_ci	if (ret == -ETIMEDOUT)
128462306a36Sopenharmony_ci		dev_warn(mmc_dev(host->mmc),
128562306a36Sopenharmony_ci		"warning! HS400 strobe DLL status REF/SLV not lock in 50us, STROBE DLL status is %x!\n", v);
128662306a36Sopenharmony_ci}
128762306a36Sopenharmony_ci
128862306a36Sopenharmony_cistatic void esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned timing)
128962306a36Sopenharmony_ci{
129062306a36Sopenharmony_ci	u32 m;
129162306a36Sopenharmony_ci	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
129262306a36Sopenharmony_ci	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
129362306a36Sopenharmony_ci	struct esdhc_platform_data *boarddata = &imx_data->boarddata;
129462306a36Sopenharmony_ci
129562306a36Sopenharmony_ci	/* disable ddr mode and disable HS400 mode */
129662306a36Sopenharmony_ci	m = readl(host->ioaddr + ESDHC_MIX_CTRL);
129762306a36Sopenharmony_ci	m &= ~(ESDHC_MIX_CTRL_DDREN | ESDHC_MIX_CTRL_HS400_EN);
129862306a36Sopenharmony_ci	imx_data->is_ddr = 0;
129962306a36Sopenharmony_ci
130062306a36Sopenharmony_ci	switch (timing) {
130162306a36Sopenharmony_ci	case MMC_TIMING_UHS_SDR12:
130262306a36Sopenharmony_ci	case MMC_TIMING_UHS_SDR25:
130362306a36Sopenharmony_ci	case MMC_TIMING_UHS_SDR50:
130462306a36Sopenharmony_ci	case MMC_TIMING_UHS_SDR104:
130562306a36Sopenharmony_ci	case MMC_TIMING_MMC_HS:
130662306a36Sopenharmony_ci	case MMC_TIMING_MMC_HS200:
130762306a36Sopenharmony_ci		writel(m, host->ioaddr + ESDHC_MIX_CTRL);
130862306a36Sopenharmony_ci		break;
130962306a36Sopenharmony_ci	case MMC_TIMING_UHS_DDR50:
131062306a36Sopenharmony_ci	case MMC_TIMING_MMC_DDR52:
131162306a36Sopenharmony_ci		m |= ESDHC_MIX_CTRL_DDREN;
131262306a36Sopenharmony_ci		writel(m, host->ioaddr + ESDHC_MIX_CTRL);
131362306a36Sopenharmony_ci		imx_data->is_ddr = 1;
131462306a36Sopenharmony_ci		if (boarddata->delay_line) {
131562306a36Sopenharmony_ci			u32 v;
131662306a36Sopenharmony_ci			v = boarddata->delay_line <<
131762306a36Sopenharmony_ci				ESDHC_DLL_OVERRIDE_VAL_SHIFT |
131862306a36Sopenharmony_ci				(1 << ESDHC_DLL_OVERRIDE_EN_SHIFT);
131962306a36Sopenharmony_ci			if (is_imx53_esdhc(imx_data))
132062306a36Sopenharmony_ci				v <<= 1;
132162306a36Sopenharmony_ci			writel(v, host->ioaddr + ESDHC_DLL_CTRL);
132262306a36Sopenharmony_ci		}
132362306a36Sopenharmony_ci		break;
132462306a36Sopenharmony_ci	case MMC_TIMING_MMC_HS400:
132562306a36Sopenharmony_ci		m |= ESDHC_MIX_CTRL_DDREN | ESDHC_MIX_CTRL_HS400_EN;
132662306a36Sopenharmony_ci		writel(m, host->ioaddr + ESDHC_MIX_CTRL);
132762306a36Sopenharmony_ci		imx_data->is_ddr = 1;
132862306a36Sopenharmony_ci		/* update clock after enable DDR for strobe DLL lock */
132962306a36Sopenharmony_ci		host->ops->set_clock(host, host->clock);
133062306a36Sopenharmony_ci		esdhc_set_strobe_dll(host);
133162306a36Sopenharmony_ci		break;
133262306a36Sopenharmony_ci	case MMC_TIMING_LEGACY:
133362306a36Sopenharmony_ci	default:
133462306a36Sopenharmony_ci		esdhc_reset_tuning(host);
133562306a36Sopenharmony_ci		break;
133662306a36Sopenharmony_ci	}
133762306a36Sopenharmony_ci
133862306a36Sopenharmony_ci	esdhc_change_pinstate(host, timing);
133962306a36Sopenharmony_ci}
134062306a36Sopenharmony_ci
134162306a36Sopenharmony_cistatic void esdhc_reset(struct sdhci_host *host, u8 mask)
134262306a36Sopenharmony_ci{
134362306a36Sopenharmony_ci	sdhci_and_cqhci_reset(host, mask);
134462306a36Sopenharmony_ci
134562306a36Sopenharmony_ci	sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
134662306a36Sopenharmony_ci	sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
134762306a36Sopenharmony_ci}
134862306a36Sopenharmony_ci
134962306a36Sopenharmony_cistatic unsigned int esdhc_get_max_timeout_count(struct sdhci_host *host)
135062306a36Sopenharmony_ci{
135162306a36Sopenharmony_ci	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
135262306a36Sopenharmony_ci	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
135362306a36Sopenharmony_ci
135462306a36Sopenharmony_ci	/* Doc Erratum: the uSDHC actual maximum timeout count is 1 << 29 */
135562306a36Sopenharmony_ci	return esdhc_is_usdhc(imx_data) ? 1 << 29 : 1 << 27;
135662306a36Sopenharmony_ci}
135762306a36Sopenharmony_ci
135862306a36Sopenharmony_cistatic void esdhc_set_timeout(struct sdhci_host *host, struct mmc_command *cmd)
135962306a36Sopenharmony_ci{
136062306a36Sopenharmony_ci	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
136162306a36Sopenharmony_ci	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
136262306a36Sopenharmony_ci
136362306a36Sopenharmony_ci	/* use maximum timeout counter */
136462306a36Sopenharmony_ci	esdhc_clrset_le(host, ESDHC_SYS_CTRL_DTOCV_MASK,
136562306a36Sopenharmony_ci			esdhc_is_usdhc(imx_data) ? 0xF : 0xE,
136662306a36Sopenharmony_ci			SDHCI_TIMEOUT_CONTROL);
136762306a36Sopenharmony_ci}
136862306a36Sopenharmony_ci
136962306a36Sopenharmony_cistatic u32 esdhc_cqhci_irq(struct sdhci_host *host, u32 intmask)
137062306a36Sopenharmony_ci{
137162306a36Sopenharmony_ci	int cmd_error = 0;
137262306a36Sopenharmony_ci	int data_error = 0;
137362306a36Sopenharmony_ci
137462306a36Sopenharmony_ci	if (!sdhci_cqe_irq(host, intmask, &cmd_error, &data_error))
137562306a36Sopenharmony_ci		return intmask;
137662306a36Sopenharmony_ci
137762306a36Sopenharmony_ci	cqhci_irq(host->mmc, intmask, cmd_error, data_error);
137862306a36Sopenharmony_ci
137962306a36Sopenharmony_ci	return 0;
138062306a36Sopenharmony_ci}
138162306a36Sopenharmony_ci
138262306a36Sopenharmony_cistatic struct sdhci_ops sdhci_esdhc_ops = {
138362306a36Sopenharmony_ci	.read_l = esdhc_readl_le,
138462306a36Sopenharmony_ci	.read_w = esdhc_readw_le,
138562306a36Sopenharmony_ci	.read_b = esdhc_readb_le,
138662306a36Sopenharmony_ci	.write_l = esdhc_writel_le,
138762306a36Sopenharmony_ci	.write_w = esdhc_writew_le,
138862306a36Sopenharmony_ci	.write_b = esdhc_writeb_le,
138962306a36Sopenharmony_ci	.set_clock = esdhc_pltfm_set_clock,
139062306a36Sopenharmony_ci	.get_max_clock = esdhc_pltfm_get_max_clock,
139162306a36Sopenharmony_ci	.get_min_clock = esdhc_pltfm_get_min_clock,
139262306a36Sopenharmony_ci	.get_max_timeout_count = esdhc_get_max_timeout_count,
139362306a36Sopenharmony_ci	.get_ro = esdhc_pltfm_get_ro,
139462306a36Sopenharmony_ci	.set_timeout = esdhc_set_timeout,
139562306a36Sopenharmony_ci	.set_bus_width = esdhc_pltfm_set_bus_width,
139662306a36Sopenharmony_ci	.set_uhs_signaling = esdhc_set_uhs_signaling,
139762306a36Sopenharmony_ci	.reset = esdhc_reset,
139862306a36Sopenharmony_ci	.irq = esdhc_cqhci_irq,
139962306a36Sopenharmony_ci	.dump_vendor_regs = esdhc_dump_debug_regs,
140062306a36Sopenharmony_ci};
140162306a36Sopenharmony_ci
140262306a36Sopenharmony_cistatic const struct sdhci_pltfm_data sdhci_esdhc_imx_pdata = {
140362306a36Sopenharmony_ci	.quirks = ESDHC_DEFAULT_QUIRKS | SDHCI_QUIRK_NO_HISPD_BIT
140462306a36Sopenharmony_ci			| SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC
140562306a36Sopenharmony_ci			| SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC
140662306a36Sopenharmony_ci			| SDHCI_QUIRK_BROKEN_CARD_DETECTION,
140762306a36Sopenharmony_ci	.ops = &sdhci_esdhc_ops,
140862306a36Sopenharmony_ci};
140962306a36Sopenharmony_ci
141062306a36Sopenharmony_cistatic void sdhci_esdhc_imx_hwinit(struct sdhci_host *host)
141162306a36Sopenharmony_ci{
141262306a36Sopenharmony_ci	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
141362306a36Sopenharmony_ci	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
141462306a36Sopenharmony_ci	struct cqhci_host *cq_host = host->mmc->cqe_private;
141562306a36Sopenharmony_ci	u32 tmp;
141662306a36Sopenharmony_ci
141762306a36Sopenharmony_ci	if (esdhc_is_usdhc(imx_data)) {
141862306a36Sopenharmony_ci		/*
141962306a36Sopenharmony_ci		 * The imx6q ROM code will change the default watermark
142062306a36Sopenharmony_ci		 * level setting to something insane.  Change it back here.
142162306a36Sopenharmony_ci		 */
142262306a36Sopenharmony_ci		writel(ESDHC_WTMK_DEFAULT_VAL, host->ioaddr + ESDHC_WTMK_LVL);
142362306a36Sopenharmony_ci
142462306a36Sopenharmony_ci		/*
142562306a36Sopenharmony_ci		 * ROM code will change the bit burst_length_enable setting
142662306a36Sopenharmony_ci		 * to zero if this usdhc is chosen to boot system. Change
142762306a36Sopenharmony_ci		 * it back here, otherwise it will impact the performance a
142862306a36Sopenharmony_ci		 * lot. This bit is used to enable/disable the burst length
142962306a36Sopenharmony_ci		 * for the external AHB2AXI bridge. It's useful especially
143062306a36Sopenharmony_ci		 * for INCR transfer because without burst length indicator,
143162306a36Sopenharmony_ci		 * the AHB2AXI bridge does not know the burst length in
143262306a36Sopenharmony_ci		 * advance. And without burst length indicator, AHB INCR
143362306a36Sopenharmony_ci		 * transfer can only be converted to singles on the AXI side.
143462306a36Sopenharmony_ci		 */
143562306a36Sopenharmony_ci		writel(readl(host->ioaddr + SDHCI_HOST_CONTROL)
143662306a36Sopenharmony_ci			| ESDHC_BURST_LEN_EN_INCR,
143762306a36Sopenharmony_ci			host->ioaddr + SDHCI_HOST_CONTROL);
143862306a36Sopenharmony_ci
143962306a36Sopenharmony_ci		/*
144062306a36Sopenharmony_ci		 * erratum ESDHC_FLAG_ERR004536 fix for MX6Q TO1.2 and MX6DL
144162306a36Sopenharmony_ci		 * TO1.1, it's harmless for MX6SL
144262306a36Sopenharmony_ci		 */
144362306a36Sopenharmony_ci		if (!(imx_data->socdata->flags & ESDHC_FLAG_SKIP_ERR004536)) {
144462306a36Sopenharmony_ci			writel(readl(host->ioaddr + 0x6c) & ~BIT(7),
144562306a36Sopenharmony_ci				host->ioaddr + 0x6c);
144662306a36Sopenharmony_ci		}
144762306a36Sopenharmony_ci
144862306a36Sopenharmony_ci		/* disable DLL_CTRL delay line settings */
144962306a36Sopenharmony_ci		writel(0x0, host->ioaddr + ESDHC_DLL_CTRL);
145062306a36Sopenharmony_ci
145162306a36Sopenharmony_ci		/*
145262306a36Sopenharmony_ci		 * For the case of command with busy, if set the bit
145362306a36Sopenharmony_ci		 * ESDHC_VEND_SPEC2_EN_BUSY_IRQ, USDHC will generate a
145462306a36Sopenharmony_ci		 * transfer complete interrupt when busy is deasserted.
145562306a36Sopenharmony_ci		 * When CQHCI use DCMD to send a CMD need R1b respons,
145662306a36Sopenharmony_ci		 * CQHCI require to set ESDHC_VEND_SPEC2_EN_BUSY_IRQ,
145762306a36Sopenharmony_ci		 * otherwise DCMD will always meet timeout waiting for
145862306a36Sopenharmony_ci		 * hardware interrupt issue.
145962306a36Sopenharmony_ci		 */
146062306a36Sopenharmony_ci		if (imx_data->socdata->flags & ESDHC_FLAG_CQHCI) {
146162306a36Sopenharmony_ci			tmp = readl(host->ioaddr + ESDHC_VEND_SPEC2);
146262306a36Sopenharmony_ci			tmp |= ESDHC_VEND_SPEC2_EN_BUSY_IRQ;
146362306a36Sopenharmony_ci			writel(tmp, host->ioaddr + ESDHC_VEND_SPEC2);
146462306a36Sopenharmony_ci
146562306a36Sopenharmony_ci			host->quirks &= ~SDHCI_QUIRK_NO_BUSY_IRQ;
146662306a36Sopenharmony_ci		}
146762306a36Sopenharmony_ci
146862306a36Sopenharmony_ci		if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) {
146962306a36Sopenharmony_ci			tmp = readl(host->ioaddr + ESDHC_TUNING_CTRL);
147062306a36Sopenharmony_ci			tmp |= ESDHC_STD_TUNING_EN;
147162306a36Sopenharmony_ci
147262306a36Sopenharmony_ci			/*
147362306a36Sopenharmony_ci			 * ROM code or bootloader may config the start tap
147462306a36Sopenharmony_ci			 * and step, unmask them first.
147562306a36Sopenharmony_ci			 */
147662306a36Sopenharmony_ci			tmp &= ~(ESDHC_TUNING_START_TAP_MASK | ESDHC_TUNING_STEP_MASK);
147762306a36Sopenharmony_ci			if (imx_data->boarddata.tuning_start_tap)
147862306a36Sopenharmony_ci				tmp |= imx_data->boarddata.tuning_start_tap;
147962306a36Sopenharmony_ci			else
148062306a36Sopenharmony_ci				tmp |= ESDHC_TUNING_START_TAP_DEFAULT;
148162306a36Sopenharmony_ci
148262306a36Sopenharmony_ci			if (imx_data->boarddata.tuning_step) {
148362306a36Sopenharmony_ci				tmp |= imx_data->boarddata.tuning_step
148462306a36Sopenharmony_ci					<< ESDHC_TUNING_STEP_SHIFT;
148562306a36Sopenharmony_ci			} else {
148662306a36Sopenharmony_ci				tmp |= ESDHC_TUNING_STEP_DEFAULT
148762306a36Sopenharmony_ci					<< ESDHC_TUNING_STEP_SHIFT;
148862306a36Sopenharmony_ci			}
148962306a36Sopenharmony_ci
149062306a36Sopenharmony_ci			/* Disable the CMD CRC check for tuning, if not, need to
149162306a36Sopenharmony_ci			 * add some delay after every tuning command, because
149262306a36Sopenharmony_ci			 * hardware standard tuning logic will directly go to next
149362306a36Sopenharmony_ci			 * step once it detect the CMD CRC error, will not wait for
149462306a36Sopenharmony_ci			 * the card side to finally send out the tuning data, trigger
149562306a36Sopenharmony_ci			 * the buffer read ready interrupt immediately. If usdhc send
149662306a36Sopenharmony_ci			 * the next tuning command some eMMC card will stuck, can't
149762306a36Sopenharmony_ci			 * response, block the tuning procedure or the first command
149862306a36Sopenharmony_ci			 * after the whole tuning procedure always can't get any response.
149962306a36Sopenharmony_ci			 */
150062306a36Sopenharmony_ci			tmp |= ESDHC_TUNING_CMD_CRC_CHECK_DISABLE;
150162306a36Sopenharmony_ci			writel(tmp, host->ioaddr + ESDHC_TUNING_CTRL);
150262306a36Sopenharmony_ci		} else if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING) {
150362306a36Sopenharmony_ci			/*
150462306a36Sopenharmony_ci			 * ESDHC_STD_TUNING_EN may be configed in bootloader
150562306a36Sopenharmony_ci			 * or ROM code, so clear this bit here to make sure
150662306a36Sopenharmony_ci			 * the manual tuning can work.
150762306a36Sopenharmony_ci			 */
150862306a36Sopenharmony_ci			tmp = readl(host->ioaddr + ESDHC_TUNING_CTRL);
150962306a36Sopenharmony_ci			tmp &= ~ESDHC_STD_TUNING_EN;
151062306a36Sopenharmony_ci			writel(tmp, host->ioaddr + ESDHC_TUNING_CTRL);
151162306a36Sopenharmony_ci		}
151262306a36Sopenharmony_ci
151362306a36Sopenharmony_ci		/*
151462306a36Sopenharmony_ci		 * On i.MX8MM, we are running Dual Linux OS, with 1st Linux using SD Card
151562306a36Sopenharmony_ci		 * as rootfs storage, 2nd Linux using eMMC as rootfs storage. We let
151662306a36Sopenharmony_ci		 * the 1st linux configure power/clock for the 2nd Linux.
151762306a36Sopenharmony_ci		 *
151862306a36Sopenharmony_ci		 * When the 2nd Linux is booting into rootfs stage, we let the 1st Linux
151962306a36Sopenharmony_ci		 * to destroy the 2nd linux, then restart the 2nd linux, we met SDHCI dump.
152062306a36Sopenharmony_ci		 * After we clear the pending interrupt and halt CQCTL, issue gone.
152162306a36Sopenharmony_ci		 */
152262306a36Sopenharmony_ci		if (cq_host) {
152362306a36Sopenharmony_ci			tmp = cqhci_readl(cq_host, CQHCI_IS);
152462306a36Sopenharmony_ci			cqhci_writel(cq_host, tmp, CQHCI_IS);
152562306a36Sopenharmony_ci			cqhci_writel(cq_host, CQHCI_HALT, CQHCI_CTL);
152662306a36Sopenharmony_ci		}
152762306a36Sopenharmony_ci	}
152862306a36Sopenharmony_ci}
152962306a36Sopenharmony_ci
153062306a36Sopenharmony_cistatic void esdhc_cqe_enable(struct mmc_host *mmc)
153162306a36Sopenharmony_ci{
153262306a36Sopenharmony_ci	struct sdhci_host *host = mmc_priv(mmc);
153362306a36Sopenharmony_ci	struct cqhci_host *cq_host = mmc->cqe_private;
153462306a36Sopenharmony_ci	u32 reg;
153562306a36Sopenharmony_ci	u16 mode;
153662306a36Sopenharmony_ci	int count = 10;
153762306a36Sopenharmony_ci
153862306a36Sopenharmony_ci	/*
153962306a36Sopenharmony_ci	 * CQE gets stuck if it sees Buffer Read Enable bit set, which can be
154062306a36Sopenharmony_ci	 * the case after tuning, so ensure the buffer is drained.
154162306a36Sopenharmony_ci	 */
154262306a36Sopenharmony_ci	reg = sdhci_readl(host, SDHCI_PRESENT_STATE);
154362306a36Sopenharmony_ci	while (reg & SDHCI_DATA_AVAILABLE) {
154462306a36Sopenharmony_ci		sdhci_readl(host, SDHCI_BUFFER);
154562306a36Sopenharmony_ci		reg = sdhci_readl(host, SDHCI_PRESENT_STATE);
154662306a36Sopenharmony_ci		if (count-- == 0) {
154762306a36Sopenharmony_ci			dev_warn(mmc_dev(host->mmc),
154862306a36Sopenharmony_ci				"CQE may get stuck because the Buffer Read Enable bit is set\n");
154962306a36Sopenharmony_ci			break;
155062306a36Sopenharmony_ci		}
155162306a36Sopenharmony_ci		mdelay(1);
155262306a36Sopenharmony_ci	}
155362306a36Sopenharmony_ci
155462306a36Sopenharmony_ci	/*
155562306a36Sopenharmony_ci	 * Runtime resume will reset the entire host controller, which
155662306a36Sopenharmony_ci	 * will also clear the DMAEN/BCEN of register ESDHC_MIX_CTRL.
155762306a36Sopenharmony_ci	 * Here set DMAEN and BCEN when enable CMDQ.
155862306a36Sopenharmony_ci	 */
155962306a36Sopenharmony_ci	mode = sdhci_readw(host, SDHCI_TRANSFER_MODE);
156062306a36Sopenharmony_ci	if (host->flags & SDHCI_REQ_USE_DMA)
156162306a36Sopenharmony_ci		mode |= SDHCI_TRNS_DMA;
156262306a36Sopenharmony_ci	if (!(host->quirks2 & SDHCI_QUIRK2_SUPPORT_SINGLE))
156362306a36Sopenharmony_ci		mode |= SDHCI_TRNS_BLK_CNT_EN;
156462306a36Sopenharmony_ci	sdhci_writew(host, mode, SDHCI_TRANSFER_MODE);
156562306a36Sopenharmony_ci
156662306a36Sopenharmony_ci	/*
156762306a36Sopenharmony_ci	 * Though Runtime resume reset the entire host controller,
156862306a36Sopenharmony_ci	 * but do not impact the CQHCI side, need to clear the
156962306a36Sopenharmony_ci	 * HALT bit, avoid CQHCI stuck in the first request when
157062306a36Sopenharmony_ci	 * system resume back.
157162306a36Sopenharmony_ci	 */
157262306a36Sopenharmony_ci	cqhci_writel(cq_host, 0, CQHCI_CTL);
157362306a36Sopenharmony_ci	if (cqhci_readl(cq_host, CQHCI_CTL) & CQHCI_HALT)
157462306a36Sopenharmony_ci		dev_err(mmc_dev(host->mmc),
157562306a36Sopenharmony_ci			"failed to exit halt state when enable CQE\n");
157662306a36Sopenharmony_ci
157762306a36Sopenharmony_ci
157862306a36Sopenharmony_ci	sdhci_cqe_enable(mmc);
157962306a36Sopenharmony_ci}
158062306a36Sopenharmony_ci
158162306a36Sopenharmony_cistatic void esdhc_sdhci_dumpregs(struct mmc_host *mmc)
158262306a36Sopenharmony_ci{
158362306a36Sopenharmony_ci	sdhci_dumpregs(mmc_priv(mmc));
158462306a36Sopenharmony_ci}
158562306a36Sopenharmony_ci
158662306a36Sopenharmony_cistatic const struct cqhci_host_ops esdhc_cqhci_ops = {
158762306a36Sopenharmony_ci	.enable		= esdhc_cqe_enable,
158862306a36Sopenharmony_ci	.disable	= sdhci_cqe_disable,
158962306a36Sopenharmony_ci	.dumpregs	= esdhc_sdhci_dumpregs,
159062306a36Sopenharmony_ci};
159162306a36Sopenharmony_ci
159262306a36Sopenharmony_cistatic int
159362306a36Sopenharmony_cisdhci_esdhc_imx_probe_dt(struct platform_device *pdev,
159462306a36Sopenharmony_ci			 struct sdhci_host *host,
159562306a36Sopenharmony_ci			 struct pltfm_imx_data *imx_data)
159662306a36Sopenharmony_ci{
159762306a36Sopenharmony_ci	struct device_node *np = pdev->dev.of_node;
159862306a36Sopenharmony_ci	struct esdhc_platform_data *boarddata = &imx_data->boarddata;
159962306a36Sopenharmony_ci	int ret;
160062306a36Sopenharmony_ci
160162306a36Sopenharmony_ci	if (of_property_read_bool(np, "fsl,wp-controller"))
160262306a36Sopenharmony_ci		boarddata->wp_type = ESDHC_WP_CONTROLLER;
160362306a36Sopenharmony_ci
160462306a36Sopenharmony_ci	/*
160562306a36Sopenharmony_ci	 * If we have this property, then activate WP check.
160662306a36Sopenharmony_ci	 * Retrieveing and requesting the actual WP GPIO will happen
160762306a36Sopenharmony_ci	 * in the call to mmc_of_parse().
160862306a36Sopenharmony_ci	 */
160962306a36Sopenharmony_ci	if (of_property_read_bool(np, "wp-gpios"))
161062306a36Sopenharmony_ci		boarddata->wp_type = ESDHC_WP_GPIO;
161162306a36Sopenharmony_ci
161262306a36Sopenharmony_ci	of_property_read_u32(np, "fsl,tuning-step", &boarddata->tuning_step);
161362306a36Sopenharmony_ci	of_property_read_u32(np, "fsl,tuning-start-tap",
161462306a36Sopenharmony_ci			     &boarddata->tuning_start_tap);
161562306a36Sopenharmony_ci
161662306a36Sopenharmony_ci	of_property_read_u32(np, "fsl,strobe-dll-delay-target",
161762306a36Sopenharmony_ci				&boarddata->strobe_dll_delay_target);
161862306a36Sopenharmony_ci	if (of_property_read_bool(np, "no-1-8-v"))
161962306a36Sopenharmony_ci		host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V;
162062306a36Sopenharmony_ci
162162306a36Sopenharmony_ci	if (of_property_read_u32(np, "fsl,delay-line", &boarddata->delay_line))
162262306a36Sopenharmony_ci		boarddata->delay_line = 0;
162362306a36Sopenharmony_ci
162462306a36Sopenharmony_ci	mmc_of_parse_voltage(host->mmc, &host->ocr_mask);
162562306a36Sopenharmony_ci
162662306a36Sopenharmony_ci	if (esdhc_is_usdhc(imx_data) && !IS_ERR(imx_data->pinctrl)) {
162762306a36Sopenharmony_ci		imx_data->pins_100mhz = pinctrl_lookup_state(imx_data->pinctrl,
162862306a36Sopenharmony_ci						ESDHC_PINCTRL_STATE_100MHZ);
162962306a36Sopenharmony_ci		imx_data->pins_200mhz = pinctrl_lookup_state(imx_data->pinctrl,
163062306a36Sopenharmony_ci						ESDHC_PINCTRL_STATE_200MHZ);
163162306a36Sopenharmony_ci	}
163262306a36Sopenharmony_ci
163362306a36Sopenharmony_ci	/* call to generic mmc_of_parse to support additional capabilities */
163462306a36Sopenharmony_ci	ret = mmc_of_parse(host->mmc);
163562306a36Sopenharmony_ci	if (ret)
163662306a36Sopenharmony_ci		return ret;
163762306a36Sopenharmony_ci
163862306a36Sopenharmony_ci	/* HS400/HS400ES require 8 bit bus */
163962306a36Sopenharmony_ci	if (!(host->mmc->caps & MMC_CAP_8_BIT_DATA))
164062306a36Sopenharmony_ci		host->mmc->caps2 &= ~(MMC_CAP2_HS400 | MMC_CAP2_HS400_ES);
164162306a36Sopenharmony_ci
164262306a36Sopenharmony_ci	if (mmc_gpio_get_cd(host->mmc) >= 0)
164362306a36Sopenharmony_ci		host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION;
164462306a36Sopenharmony_ci
164562306a36Sopenharmony_ci	return 0;
164662306a36Sopenharmony_ci}
164762306a36Sopenharmony_ci
164862306a36Sopenharmony_cistatic int sdhci_esdhc_imx_probe(struct platform_device *pdev)
164962306a36Sopenharmony_ci{
165062306a36Sopenharmony_ci	struct sdhci_pltfm_host *pltfm_host;
165162306a36Sopenharmony_ci	struct sdhci_host *host;
165262306a36Sopenharmony_ci	struct cqhci_host *cq_host;
165362306a36Sopenharmony_ci	int err;
165462306a36Sopenharmony_ci	struct pltfm_imx_data *imx_data;
165562306a36Sopenharmony_ci
165662306a36Sopenharmony_ci	host = sdhci_pltfm_init(pdev, &sdhci_esdhc_imx_pdata,
165762306a36Sopenharmony_ci				sizeof(*imx_data));
165862306a36Sopenharmony_ci	if (IS_ERR(host))
165962306a36Sopenharmony_ci		return PTR_ERR(host);
166062306a36Sopenharmony_ci
166162306a36Sopenharmony_ci	pltfm_host = sdhci_priv(host);
166262306a36Sopenharmony_ci
166362306a36Sopenharmony_ci	imx_data = sdhci_pltfm_priv(pltfm_host);
166462306a36Sopenharmony_ci
166562306a36Sopenharmony_ci	imx_data->socdata = device_get_match_data(&pdev->dev);
166662306a36Sopenharmony_ci
166762306a36Sopenharmony_ci	if (imx_data->socdata->flags & ESDHC_FLAG_PMQOS)
166862306a36Sopenharmony_ci		cpu_latency_qos_add_request(&imx_data->pm_qos_req, 0);
166962306a36Sopenharmony_ci
167062306a36Sopenharmony_ci	imx_data->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
167162306a36Sopenharmony_ci	if (IS_ERR(imx_data->clk_ipg)) {
167262306a36Sopenharmony_ci		err = PTR_ERR(imx_data->clk_ipg);
167362306a36Sopenharmony_ci		goto free_sdhci;
167462306a36Sopenharmony_ci	}
167562306a36Sopenharmony_ci
167662306a36Sopenharmony_ci	imx_data->clk_ahb = devm_clk_get(&pdev->dev, "ahb");
167762306a36Sopenharmony_ci	if (IS_ERR(imx_data->clk_ahb)) {
167862306a36Sopenharmony_ci		err = PTR_ERR(imx_data->clk_ahb);
167962306a36Sopenharmony_ci		goto free_sdhci;
168062306a36Sopenharmony_ci	}
168162306a36Sopenharmony_ci
168262306a36Sopenharmony_ci	imx_data->clk_per = devm_clk_get(&pdev->dev, "per");
168362306a36Sopenharmony_ci	if (IS_ERR(imx_data->clk_per)) {
168462306a36Sopenharmony_ci		err = PTR_ERR(imx_data->clk_per);
168562306a36Sopenharmony_ci		goto free_sdhci;
168662306a36Sopenharmony_ci	}
168762306a36Sopenharmony_ci
168862306a36Sopenharmony_ci	pltfm_host->clk = imx_data->clk_per;
168962306a36Sopenharmony_ci	pltfm_host->clock = clk_get_rate(pltfm_host->clk);
169062306a36Sopenharmony_ci	err = clk_prepare_enable(imx_data->clk_per);
169162306a36Sopenharmony_ci	if (err)
169262306a36Sopenharmony_ci		goto free_sdhci;
169362306a36Sopenharmony_ci	err = clk_prepare_enable(imx_data->clk_ipg);
169462306a36Sopenharmony_ci	if (err)
169562306a36Sopenharmony_ci		goto disable_per_clk;
169662306a36Sopenharmony_ci	err = clk_prepare_enable(imx_data->clk_ahb);
169762306a36Sopenharmony_ci	if (err)
169862306a36Sopenharmony_ci		goto disable_ipg_clk;
169962306a36Sopenharmony_ci
170062306a36Sopenharmony_ci	imx_data->pinctrl = devm_pinctrl_get(&pdev->dev);
170162306a36Sopenharmony_ci	if (IS_ERR(imx_data->pinctrl))
170262306a36Sopenharmony_ci		dev_warn(mmc_dev(host->mmc), "could not get pinctrl\n");
170362306a36Sopenharmony_ci
170462306a36Sopenharmony_ci	if (esdhc_is_usdhc(imx_data)) {
170562306a36Sopenharmony_ci		host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN;
170662306a36Sopenharmony_ci		host->mmc->caps |= MMC_CAP_1_8V_DDR | MMC_CAP_3_3V_DDR;
170762306a36Sopenharmony_ci
170862306a36Sopenharmony_ci		/* GPIO CD can be set as a wakeup source */
170962306a36Sopenharmony_ci		host->mmc->caps |= MMC_CAP_CD_WAKE;
171062306a36Sopenharmony_ci
171162306a36Sopenharmony_ci		if (!(imx_data->socdata->flags & ESDHC_FLAG_HS200))
171262306a36Sopenharmony_ci			host->quirks2 |= SDHCI_QUIRK2_BROKEN_HS200;
171362306a36Sopenharmony_ci
171462306a36Sopenharmony_ci		/* clear tuning bits in case ROM has set it already */
171562306a36Sopenharmony_ci		writel(0x0, host->ioaddr + ESDHC_MIX_CTRL);
171662306a36Sopenharmony_ci		writel(0x0, host->ioaddr + SDHCI_AUTO_CMD_STATUS);
171762306a36Sopenharmony_ci		writel(0x0, host->ioaddr + ESDHC_TUNE_CTRL_STATUS);
171862306a36Sopenharmony_ci
171962306a36Sopenharmony_ci		/*
172062306a36Sopenharmony_ci		 * Link usdhc specific mmc_host_ops execute_tuning function,
172162306a36Sopenharmony_ci		 * to replace the standard one in sdhci_ops.
172262306a36Sopenharmony_ci		 */
172362306a36Sopenharmony_ci		host->mmc_host_ops.execute_tuning = usdhc_execute_tuning;
172462306a36Sopenharmony_ci
172562306a36Sopenharmony_ci		/*
172662306a36Sopenharmony_ci		 * Link usdhc specific mmc_host_ops init card function,
172762306a36Sopenharmony_ci		 * to distinguish the card type.
172862306a36Sopenharmony_ci		 */
172962306a36Sopenharmony_ci		host->mmc_host_ops.init_card = usdhc_init_card;
173062306a36Sopenharmony_ci	}
173162306a36Sopenharmony_ci
173262306a36Sopenharmony_ci	if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING)
173362306a36Sopenharmony_ci		sdhci_esdhc_ops.platform_execute_tuning =
173462306a36Sopenharmony_ci					esdhc_executing_tuning;
173562306a36Sopenharmony_ci
173662306a36Sopenharmony_ci	if (imx_data->socdata->flags & ESDHC_FLAG_ERR004536)
173762306a36Sopenharmony_ci		host->quirks |= SDHCI_QUIRK_BROKEN_ADMA;
173862306a36Sopenharmony_ci
173962306a36Sopenharmony_ci	if (imx_data->socdata->flags & ESDHC_FLAG_HS400)
174062306a36Sopenharmony_ci		host->mmc->caps2 |= MMC_CAP2_HS400;
174162306a36Sopenharmony_ci
174262306a36Sopenharmony_ci	if (imx_data->socdata->flags & ESDHC_FLAG_BROKEN_AUTO_CMD23)
174362306a36Sopenharmony_ci		host->quirks2 |= SDHCI_QUIRK2_ACMD23_BROKEN;
174462306a36Sopenharmony_ci
174562306a36Sopenharmony_ci	if (imx_data->socdata->flags & ESDHC_FLAG_HS400_ES) {
174662306a36Sopenharmony_ci		host->mmc->caps2 |= MMC_CAP2_HS400_ES;
174762306a36Sopenharmony_ci		host->mmc_host_ops.hs400_enhanced_strobe =
174862306a36Sopenharmony_ci					esdhc_hs400_enhanced_strobe;
174962306a36Sopenharmony_ci	}
175062306a36Sopenharmony_ci
175162306a36Sopenharmony_ci	if (imx_data->socdata->flags & ESDHC_FLAG_CQHCI) {
175262306a36Sopenharmony_ci		host->mmc->caps2 |= MMC_CAP2_CQE | MMC_CAP2_CQE_DCMD;
175362306a36Sopenharmony_ci		cq_host = devm_kzalloc(&pdev->dev, sizeof(*cq_host), GFP_KERNEL);
175462306a36Sopenharmony_ci		if (!cq_host) {
175562306a36Sopenharmony_ci			err = -ENOMEM;
175662306a36Sopenharmony_ci			goto disable_ahb_clk;
175762306a36Sopenharmony_ci		}
175862306a36Sopenharmony_ci
175962306a36Sopenharmony_ci		cq_host->mmio = host->ioaddr + ESDHC_CQHCI_ADDR_OFFSET;
176062306a36Sopenharmony_ci		cq_host->ops = &esdhc_cqhci_ops;
176162306a36Sopenharmony_ci
176262306a36Sopenharmony_ci		err = cqhci_init(cq_host, host->mmc, false);
176362306a36Sopenharmony_ci		if (err)
176462306a36Sopenharmony_ci			goto disable_ahb_clk;
176562306a36Sopenharmony_ci	}
176662306a36Sopenharmony_ci
176762306a36Sopenharmony_ci	err = sdhci_esdhc_imx_probe_dt(pdev, host, imx_data);
176862306a36Sopenharmony_ci	if (err)
176962306a36Sopenharmony_ci		goto disable_ahb_clk;
177062306a36Sopenharmony_ci
177162306a36Sopenharmony_ci	sdhci_esdhc_imx_hwinit(host);
177262306a36Sopenharmony_ci
177362306a36Sopenharmony_ci	err = sdhci_add_host(host);
177462306a36Sopenharmony_ci	if (err)
177562306a36Sopenharmony_ci		goto disable_ahb_clk;
177662306a36Sopenharmony_ci
177762306a36Sopenharmony_ci	/*
177862306a36Sopenharmony_ci	 * Setup the wakeup capability here, let user to decide
177962306a36Sopenharmony_ci	 * whether need to enable this wakeup through sysfs interface.
178062306a36Sopenharmony_ci	 */
178162306a36Sopenharmony_ci	if ((host->mmc->pm_caps & MMC_PM_KEEP_POWER) &&
178262306a36Sopenharmony_ci			(host->mmc->pm_caps & MMC_PM_WAKE_SDIO_IRQ))
178362306a36Sopenharmony_ci		device_set_wakeup_capable(&pdev->dev, true);
178462306a36Sopenharmony_ci
178562306a36Sopenharmony_ci	pm_runtime_set_active(&pdev->dev);
178662306a36Sopenharmony_ci	pm_runtime_set_autosuspend_delay(&pdev->dev, 50);
178762306a36Sopenharmony_ci	pm_runtime_use_autosuspend(&pdev->dev);
178862306a36Sopenharmony_ci	pm_suspend_ignore_children(&pdev->dev, 1);
178962306a36Sopenharmony_ci	pm_runtime_enable(&pdev->dev);
179062306a36Sopenharmony_ci
179162306a36Sopenharmony_ci	return 0;
179262306a36Sopenharmony_ci
179362306a36Sopenharmony_cidisable_ahb_clk:
179462306a36Sopenharmony_ci	clk_disable_unprepare(imx_data->clk_ahb);
179562306a36Sopenharmony_cidisable_ipg_clk:
179662306a36Sopenharmony_ci	clk_disable_unprepare(imx_data->clk_ipg);
179762306a36Sopenharmony_cidisable_per_clk:
179862306a36Sopenharmony_ci	clk_disable_unprepare(imx_data->clk_per);
179962306a36Sopenharmony_cifree_sdhci:
180062306a36Sopenharmony_ci	if (imx_data->socdata->flags & ESDHC_FLAG_PMQOS)
180162306a36Sopenharmony_ci		cpu_latency_qos_remove_request(&imx_data->pm_qos_req);
180262306a36Sopenharmony_ci	sdhci_pltfm_free(pdev);
180362306a36Sopenharmony_ci	return err;
180462306a36Sopenharmony_ci}
180562306a36Sopenharmony_ci
180662306a36Sopenharmony_cistatic void sdhci_esdhc_imx_remove(struct platform_device *pdev)
180762306a36Sopenharmony_ci{
180862306a36Sopenharmony_ci	struct sdhci_host *host = platform_get_drvdata(pdev);
180962306a36Sopenharmony_ci	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
181062306a36Sopenharmony_ci	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
181162306a36Sopenharmony_ci	int dead;
181262306a36Sopenharmony_ci
181362306a36Sopenharmony_ci	pm_runtime_get_sync(&pdev->dev);
181462306a36Sopenharmony_ci	dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff);
181562306a36Sopenharmony_ci	pm_runtime_disable(&pdev->dev);
181662306a36Sopenharmony_ci	pm_runtime_put_noidle(&pdev->dev);
181762306a36Sopenharmony_ci
181862306a36Sopenharmony_ci	sdhci_remove_host(host, dead);
181962306a36Sopenharmony_ci
182062306a36Sopenharmony_ci	clk_disable_unprepare(imx_data->clk_per);
182162306a36Sopenharmony_ci	clk_disable_unprepare(imx_data->clk_ipg);
182262306a36Sopenharmony_ci	clk_disable_unprepare(imx_data->clk_ahb);
182362306a36Sopenharmony_ci
182462306a36Sopenharmony_ci	if (imx_data->socdata->flags & ESDHC_FLAG_PMQOS)
182562306a36Sopenharmony_ci		cpu_latency_qos_remove_request(&imx_data->pm_qos_req);
182662306a36Sopenharmony_ci
182762306a36Sopenharmony_ci	sdhci_pltfm_free(pdev);
182862306a36Sopenharmony_ci}
182962306a36Sopenharmony_ci
183062306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
183162306a36Sopenharmony_cistatic int sdhci_esdhc_suspend(struct device *dev)
183262306a36Sopenharmony_ci{
183362306a36Sopenharmony_ci	struct sdhci_host *host = dev_get_drvdata(dev);
183462306a36Sopenharmony_ci	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
183562306a36Sopenharmony_ci	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
183662306a36Sopenharmony_ci	int ret;
183762306a36Sopenharmony_ci
183862306a36Sopenharmony_ci	if (host->mmc->caps2 & MMC_CAP2_CQE) {
183962306a36Sopenharmony_ci		ret = cqhci_suspend(host->mmc);
184062306a36Sopenharmony_ci		if (ret)
184162306a36Sopenharmony_ci			return ret;
184262306a36Sopenharmony_ci	}
184362306a36Sopenharmony_ci
184462306a36Sopenharmony_ci	if ((imx_data->socdata->flags & ESDHC_FLAG_STATE_LOST_IN_LPMODE) &&
184562306a36Sopenharmony_ci		(host->tuning_mode != SDHCI_TUNING_MODE_1)) {
184662306a36Sopenharmony_ci		mmc_retune_timer_stop(host->mmc);
184762306a36Sopenharmony_ci		mmc_retune_needed(host->mmc);
184862306a36Sopenharmony_ci	}
184962306a36Sopenharmony_ci
185062306a36Sopenharmony_ci	if (host->tuning_mode != SDHCI_TUNING_MODE_3)
185162306a36Sopenharmony_ci		mmc_retune_needed(host->mmc);
185262306a36Sopenharmony_ci
185362306a36Sopenharmony_ci	ret = sdhci_suspend_host(host);
185462306a36Sopenharmony_ci	if (ret)
185562306a36Sopenharmony_ci		return ret;
185662306a36Sopenharmony_ci
185762306a36Sopenharmony_ci	ret = pinctrl_pm_select_sleep_state(dev);
185862306a36Sopenharmony_ci	if (ret)
185962306a36Sopenharmony_ci		return ret;
186062306a36Sopenharmony_ci
186162306a36Sopenharmony_ci	ret = mmc_gpio_set_cd_wake(host->mmc, true);
186262306a36Sopenharmony_ci
186362306a36Sopenharmony_ci	return ret;
186462306a36Sopenharmony_ci}
186562306a36Sopenharmony_ci
186662306a36Sopenharmony_cistatic int sdhci_esdhc_resume(struct device *dev)
186762306a36Sopenharmony_ci{
186862306a36Sopenharmony_ci	struct sdhci_host *host = dev_get_drvdata(dev);
186962306a36Sopenharmony_ci	int ret;
187062306a36Sopenharmony_ci
187162306a36Sopenharmony_ci	ret = pinctrl_pm_select_default_state(dev);
187262306a36Sopenharmony_ci	if (ret)
187362306a36Sopenharmony_ci		return ret;
187462306a36Sopenharmony_ci
187562306a36Sopenharmony_ci	/* re-initialize hw state in case it's lost in low power mode */
187662306a36Sopenharmony_ci	sdhci_esdhc_imx_hwinit(host);
187762306a36Sopenharmony_ci
187862306a36Sopenharmony_ci	ret = sdhci_resume_host(host);
187962306a36Sopenharmony_ci	if (ret)
188062306a36Sopenharmony_ci		return ret;
188162306a36Sopenharmony_ci
188262306a36Sopenharmony_ci	if (host->mmc->caps2 & MMC_CAP2_CQE)
188362306a36Sopenharmony_ci		ret = cqhci_resume(host->mmc);
188462306a36Sopenharmony_ci
188562306a36Sopenharmony_ci	if (!ret)
188662306a36Sopenharmony_ci		ret = mmc_gpio_set_cd_wake(host->mmc, false);
188762306a36Sopenharmony_ci
188862306a36Sopenharmony_ci	return ret;
188962306a36Sopenharmony_ci}
189062306a36Sopenharmony_ci#endif
189162306a36Sopenharmony_ci
189262306a36Sopenharmony_ci#ifdef CONFIG_PM
189362306a36Sopenharmony_cistatic int sdhci_esdhc_runtime_suspend(struct device *dev)
189462306a36Sopenharmony_ci{
189562306a36Sopenharmony_ci	struct sdhci_host *host = dev_get_drvdata(dev);
189662306a36Sopenharmony_ci	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
189762306a36Sopenharmony_ci	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
189862306a36Sopenharmony_ci	int ret;
189962306a36Sopenharmony_ci
190062306a36Sopenharmony_ci	if (host->mmc->caps2 & MMC_CAP2_CQE) {
190162306a36Sopenharmony_ci		ret = cqhci_suspend(host->mmc);
190262306a36Sopenharmony_ci		if (ret)
190362306a36Sopenharmony_ci			return ret;
190462306a36Sopenharmony_ci	}
190562306a36Sopenharmony_ci
190662306a36Sopenharmony_ci	ret = sdhci_runtime_suspend_host(host);
190762306a36Sopenharmony_ci	if (ret)
190862306a36Sopenharmony_ci		return ret;
190962306a36Sopenharmony_ci
191062306a36Sopenharmony_ci	if (host->tuning_mode != SDHCI_TUNING_MODE_3)
191162306a36Sopenharmony_ci		mmc_retune_needed(host->mmc);
191262306a36Sopenharmony_ci
191362306a36Sopenharmony_ci	imx_data->actual_clock = host->mmc->actual_clock;
191462306a36Sopenharmony_ci	esdhc_pltfm_set_clock(host, 0);
191562306a36Sopenharmony_ci	clk_disable_unprepare(imx_data->clk_per);
191662306a36Sopenharmony_ci	clk_disable_unprepare(imx_data->clk_ipg);
191762306a36Sopenharmony_ci	clk_disable_unprepare(imx_data->clk_ahb);
191862306a36Sopenharmony_ci
191962306a36Sopenharmony_ci	if (imx_data->socdata->flags & ESDHC_FLAG_PMQOS)
192062306a36Sopenharmony_ci		cpu_latency_qos_remove_request(&imx_data->pm_qos_req);
192162306a36Sopenharmony_ci
192262306a36Sopenharmony_ci	return ret;
192362306a36Sopenharmony_ci}
192462306a36Sopenharmony_ci
192562306a36Sopenharmony_cistatic int sdhci_esdhc_runtime_resume(struct device *dev)
192662306a36Sopenharmony_ci{
192762306a36Sopenharmony_ci	struct sdhci_host *host = dev_get_drvdata(dev);
192862306a36Sopenharmony_ci	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
192962306a36Sopenharmony_ci	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
193062306a36Sopenharmony_ci	int err;
193162306a36Sopenharmony_ci
193262306a36Sopenharmony_ci	if (imx_data->socdata->flags & ESDHC_FLAG_PMQOS)
193362306a36Sopenharmony_ci		cpu_latency_qos_add_request(&imx_data->pm_qos_req, 0);
193462306a36Sopenharmony_ci
193562306a36Sopenharmony_ci	if (imx_data->socdata->flags & ESDHC_FLAG_CLK_RATE_LOST_IN_PM_RUNTIME)
193662306a36Sopenharmony_ci		clk_set_rate(imx_data->clk_per, pltfm_host->clock);
193762306a36Sopenharmony_ci
193862306a36Sopenharmony_ci	err = clk_prepare_enable(imx_data->clk_ahb);
193962306a36Sopenharmony_ci	if (err)
194062306a36Sopenharmony_ci		goto remove_pm_qos_request;
194162306a36Sopenharmony_ci
194262306a36Sopenharmony_ci	err = clk_prepare_enable(imx_data->clk_per);
194362306a36Sopenharmony_ci	if (err)
194462306a36Sopenharmony_ci		goto disable_ahb_clk;
194562306a36Sopenharmony_ci
194662306a36Sopenharmony_ci	err = clk_prepare_enable(imx_data->clk_ipg);
194762306a36Sopenharmony_ci	if (err)
194862306a36Sopenharmony_ci		goto disable_per_clk;
194962306a36Sopenharmony_ci
195062306a36Sopenharmony_ci	esdhc_pltfm_set_clock(host, imx_data->actual_clock);
195162306a36Sopenharmony_ci
195262306a36Sopenharmony_ci	err = sdhci_runtime_resume_host(host, 0);
195362306a36Sopenharmony_ci	if (err)
195462306a36Sopenharmony_ci		goto disable_ipg_clk;
195562306a36Sopenharmony_ci
195662306a36Sopenharmony_ci	if (host->mmc->caps2 & MMC_CAP2_CQE)
195762306a36Sopenharmony_ci		err = cqhci_resume(host->mmc);
195862306a36Sopenharmony_ci
195962306a36Sopenharmony_ci	return err;
196062306a36Sopenharmony_ci
196162306a36Sopenharmony_cidisable_ipg_clk:
196262306a36Sopenharmony_ci	clk_disable_unprepare(imx_data->clk_ipg);
196362306a36Sopenharmony_cidisable_per_clk:
196462306a36Sopenharmony_ci	clk_disable_unprepare(imx_data->clk_per);
196562306a36Sopenharmony_cidisable_ahb_clk:
196662306a36Sopenharmony_ci	clk_disable_unprepare(imx_data->clk_ahb);
196762306a36Sopenharmony_ciremove_pm_qos_request:
196862306a36Sopenharmony_ci	if (imx_data->socdata->flags & ESDHC_FLAG_PMQOS)
196962306a36Sopenharmony_ci		cpu_latency_qos_remove_request(&imx_data->pm_qos_req);
197062306a36Sopenharmony_ci	return err;
197162306a36Sopenharmony_ci}
197262306a36Sopenharmony_ci#endif
197362306a36Sopenharmony_ci
197462306a36Sopenharmony_cistatic const struct dev_pm_ops sdhci_esdhc_pmops = {
197562306a36Sopenharmony_ci	SET_SYSTEM_SLEEP_PM_OPS(sdhci_esdhc_suspend, sdhci_esdhc_resume)
197662306a36Sopenharmony_ci	SET_RUNTIME_PM_OPS(sdhci_esdhc_runtime_suspend,
197762306a36Sopenharmony_ci				sdhci_esdhc_runtime_resume, NULL)
197862306a36Sopenharmony_ci};
197962306a36Sopenharmony_ci
198062306a36Sopenharmony_cistatic struct platform_driver sdhci_esdhc_imx_driver = {
198162306a36Sopenharmony_ci	.driver		= {
198262306a36Sopenharmony_ci		.name	= "sdhci-esdhc-imx",
198362306a36Sopenharmony_ci		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
198462306a36Sopenharmony_ci		.of_match_table = imx_esdhc_dt_ids,
198562306a36Sopenharmony_ci		.pm	= &sdhci_esdhc_pmops,
198662306a36Sopenharmony_ci	},
198762306a36Sopenharmony_ci	.probe		= sdhci_esdhc_imx_probe,
198862306a36Sopenharmony_ci	.remove_new	= sdhci_esdhc_imx_remove,
198962306a36Sopenharmony_ci};
199062306a36Sopenharmony_ci
199162306a36Sopenharmony_cimodule_platform_driver(sdhci_esdhc_imx_driver);
199262306a36Sopenharmony_ci
199362306a36Sopenharmony_ciMODULE_DESCRIPTION("SDHCI driver for Freescale i.MX eSDHC");
199462306a36Sopenharmony_ciMODULE_AUTHOR("Wolfram Sang <kernel@pengutronix.de>");
199562306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
1996