xref: /openbmc/linux/drivers/mmc/host/sdhci-tegra.c (revision 21b2cec6)
19c92ab61SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
203d2bfc8SOlof Johansson /*
303d2bfc8SOlof Johansson  * Copyright (C) 2010 Google, Inc.
403d2bfc8SOlof Johansson  */
503d2bfc8SOlof Johansson 
6e5c63d91SLucas Stach #include <linux/delay.h>
7b960bc44SNicolin Chen #include <linux/dma-mapping.h>
803d2bfc8SOlof Johansson #include <linux/err.h>
996547f5dSPaul Gortmaker #include <linux/module.h>
1003d2bfc8SOlof Johansson #include <linux/init.h>
11e7c07148SAapo Vienamo #include <linux/iopoll.h>
1203d2bfc8SOlof Johansson #include <linux/platform_device.h>
1303d2bfc8SOlof Johansson #include <linux/clk.h>
1403d2bfc8SOlof Johansson #include <linux/io.h>
1555cd65e4SStephen Warren #include <linux/of.h>
163e44a1a7SStephen Warren #include <linux/of_device.h>
1786ac2f8bSAapo Vienamo #include <linux/pinctrl/consumer.h>
1886ac2f8bSAapo Vienamo #include <linux/regulator/consumer.h>
1920567be9SThierry Reding #include <linux/reset.h>
2003d2bfc8SOlof Johansson #include <linux/mmc/card.h>
2103d2bfc8SOlof Johansson #include <linux/mmc/host.h>
22c3c2384cSLucas Stach #include <linux/mmc/mmc.h>
230aacd23fSJoseph Lo #include <linux/mmc/slot-gpio.h>
242391b340SMylene JOSSERAND #include <linux/gpio/consumer.h>
2561dad40eSAapo Vienamo #include <linux/ktime.h>
2603d2bfc8SOlof Johansson 
2703d2bfc8SOlof Johansson #include "sdhci-pltfm.h"
283c4019f9SSowjanya Komatineni #include "cqhci.h"
2903d2bfc8SOlof Johansson 
30ca5879d3SPavan Kunapuli /* Tegra SDHOST controller vendor register definitions */
3174cd42bcSLucas Stach #define SDHCI_TEGRA_VENDOR_CLOCK_CTRL			0x100
32c3c2384cSLucas Stach #define SDHCI_CLOCK_CTRL_TAP_MASK			0x00ff0000
33c3c2384cSLucas Stach #define SDHCI_CLOCK_CTRL_TAP_SHIFT			16
3441a0b8d7SAapo Vienamo #define SDHCI_CLOCK_CTRL_TRIM_MASK			0x1f000000
3541a0b8d7SAapo Vienamo #define SDHCI_CLOCK_CTRL_TRIM_SHIFT			24
36c3c2384cSLucas Stach #define SDHCI_CLOCK_CTRL_SDR50_TUNING_OVERRIDE		BIT(5)
3774cd42bcSLucas Stach #define SDHCI_CLOCK_CTRL_PADPIPE_CLKEN_OVERRIDE		BIT(3)
3874cd42bcSLucas Stach #define SDHCI_CLOCK_CTRL_SPI_MODE_CLKEN_OVERRIDE	BIT(2)
3974cd42bcSLucas Stach 
40dfc9700cSAapo Vienamo #define SDHCI_TEGRA_VENDOR_SYS_SW_CTRL			0x104
41dfc9700cSAapo Vienamo #define SDHCI_TEGRA_SYS_SW_CTRL_ENHANCED_STROBE		BIT(31)
42dfc9700cSAapo Vienamo 
43f5313aaaSAapo Vienamo #define SDHCI_TEGRA_VENDOR_CAP_OVERRIDES		0x10c
44f5313aaaSAapo Vienamo #define SDHCI_TEGRA_CAP_OVERRIDES_DQS_TRIM_MASK		0x00003f00
45f5313aaaSAapo Vienamo #define SDHCI_TEGRA_CAP_OVERRIDES_DQS_TRIM_SHIFT	8
46f5313aaaSAapo Vienamo 
47ca5879d3SPavan Kunapuli #define SDHCI_TEGRA_VENDOR_MISC_CTRL			0x120
485e958e4aSSowjanya Komatineni #define SDHCI_MISC_CTRL_ERASE_TIMEOUT_LIMIT		BIT(0)
493145351aSAndrew Bresticker #define SDHCI_MISC_CTRL_ENABLE_SDR104			0x8
503145351aSAndrew Bresticker #define SDHCI_MISC_CTRL_ENABLE_SDR50			0x10
51ca5879d3SPavan Kunapuli #define SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300		0x20
523145351aSAndrew Bresticker #define SDHCI_MISC_CTRL_ENABLE_DDR50			0x200
53ca5879d3SPavan Kunapuli 
54bc5568bfSAapo Vienamo #define SDHCI_TEGRA_VENDOR_DLLCAL_CFG			0x1b0
55bc5568bfSAapo Vienamo #define SDHCI_TEGRA_DLLCAL_CALIBRATE			BIT(31)
56bc5568bfSAapo Vienamo 
57bc5568bfSAapo Vienamo #define SDHCI_TEGRA_VENDOR_DLLCAL_STA			0x1bc
58bc5568bfSAapo Vienamo #define SDHCI_TEGRA_DLLCAL_STA_ACTIVE			BIT(31)
59bc5568bfSAapo Vienamo 
60d4501d8eSAapo Vienamo #define SDHCI_VNDR_TUN_CTRL0_0				0x1c0
61d4501d8eSAapo Vienamo #define SDHCI_VNDR_TUN_CTRL0_TUN_HW_TAP			0x20000
62ea8fc595SSowjanya Komatineni #define SDHCI_VNDR_TUN_CTRL0_START_TAP_VAL_MASK		0x03fc0000
63ea8fc595SSowjanya Komatineni #define SDHCI_VNDR_TUN_CTRL0_START_TAP_VAL_SHIFT	18
64ea8fc595SSowjanya Komatineni #define SDHCI_VNDR_TUN_CTRL0_MUL_M_MASK			0x00001fc0
65ea8fc595SSowjanya Komatineni #define SDHCI_VNDR_TUN_CTRL0_MUL_M_SHIFT		6
66ea8fc595SSowjanya Komatineni #define SDHCI_VNDR_TUN_CTRL0_TUN_ITER_MASK		0x000e000
67ea8fc595SSowjanya Komatineni #define SDHCI_VNDR_TUN_CTRL0_TUN_ITER_SHIFT		13
68ea8fc595SSowjanya Komatineni #define TRIES_128					2
69ea8fc595SSowjanya Komatineni #define TRIES_256					4
70ea8fc595SSowjanya Komatineni #define SDHCI_VNDR_TUN_CTRL0_TUN_WORD_SEL_MASK		0x7
71ea8fc595SSowjanya Komatineni 
72ea8fc595SSowjanya Komatineni #define SDHCI_TEGRA_VNDR_TUN_CTRL1_0			0x1c4
73ea8fc595SSowjanya Komatineni #define SDHCI_TEGRA_VNDR_TUN_STATUS0			0x1C8
74ea8fc595SSowjanya Komatineni #define SDHCI_TEGRA_VNDR_TUN_STATUS1			0x1CC
75ea8fc595SSowjanya Komatineni #define SDHCI_TEGRA_VNDR_TUN_STATUS1_TAP_MASK		0xFF
76ea8fc595SSowjanya Komatineni #define SDHCI_TEGRA_VNDR_TUN_STATUS1_END_TAP_SHIFT	0x8
77ea8fc595SSowjanya Komatineni #define TUNING_WORD_BIT_SIZE				32
78d4501d8eSAapo Vienamo 
79e5c63d91SLucas Stach #define SDHCI_TEGRA_AUTO_CAL_CONFIG			0x1e4
80e5c63d91SLucas Stach #define SDHCI_AUTO_CAL_START				BIT(31)
81e5c63d91SLucas Stach #define SDHCI_AUTO_CAL_ENABLE				BIT(29)
8251b77c8eSAapo Vienamo #define SDHCI_AUTO_CAL_PDPU_OFFSET_MASK			0x0000ffff
83e5c63d91SLucas Stach 
849d548f11SAapo Vienamo #define SDHCI_TEGRA_SDMEM_COMP_PADCTRL			0x1e0
859d548f11SAapo Vienamo #define SDHCI_TEGRA_SDMEM_COMP_PADCTRL_VREF_SEL_MASK	0x0000000f
869d548f11SAapo Vienamo #define SDHCI_TEGRA_SDMEM_COMP_PADCTRL_VREF_SEL_VAL	0x7
87212b0cf1SAapo Vienamo #define SDHCI_TEGRA_SDMEM_COMP_PADCTRL_E_INPUT_E_PWRD	BIT(31)
88de25fa5aSSowjanya Komatineni #define SDHCI_COMP_PADCTRL_DRVUPDN_OFFSET_MASK		0x07FFF000
899d548f11SAapo Vienamo 
90e7c07148SAapo Vienamo #define SDHCI_TEGRA_AUTO_CAL_STATUS			0x1ec
91e7c07148SAapo Vienamo #define SDHCI_TEGRA_AUTO_CAL_ACTIVE			BIT(31)
92e7c07148SAapo Vienamo 
933e44a1a7SStephen Warren #define NVQUIRK_FORCE_SDHCI_SPEC_200			BIT(0)
943e44a1a7SStephen Warren #define NVQUIRK_ENABLE_BLOCK_GAP_DET			BIT(1)
95ca5879d3SPavan Kunapuli #define NVQUIRK_ENABLE_SDHCI_SPEC_300			BIT(2)
967ad2ed1dSLucas Stach #define NVQUIRK_ENABLE_SDR50				BIT(3)
977ad2ed1dSLucas Stach #define NVQUIRK_ENABLE_SDR104				BIT(4)
987ad2ed1dSLucas Stach #define NVQUIRK_ENABLE_DDR50				BIT(5)
9947fad46bSSowjanya Komatineni /*
10047fad46bSSowjanya Komatineni  * HAS_PADCALIB NVQUIRK is for SoC's supporting auto calibration of pads
10147fad46bSSowjanya Komatineni  * drive strength.
10247fad46bSSowjanya Komatineni  */
103e5c63d91SLucas Stach #define NVQUIRK_HAS_PADCALIB				BIT(6)
10447fad46bSSowjanya Komatineni /*
10547fad46bSSowjanya Komatineni  * NEEDS_PAD_CONTROL NVQUIRK is for SoC's having separate 3V3 and 1V8 pads.
10647fad46bSSowjanya Komatineni  * 3V3/1V8 pad selection happens through pinctrl state selection depending
10747fad46bSSowjanya Komatineni  * on the signaling mode.
10847fad46bSSowjanya Komatineni  */
10986ac2f8bSAapo Vienamo #define NVQUIRK_NEEDS_PAD_CONTROL			BIT(7)
110d4501d8eSAapo Vienamo #define NVQUIRK_DIS_CARD_CLK_CONFIG_TAP			BIT(8)
111c6e7ab90SSowjanya Komatineni #define NVQUIRK_CQHCI_DCMD_R1B_CMD_TIMING		BIT(9)
1123e44a1a7SStephen Warren 
1138048822bSSowjanya Komatineni /*
1148048822bSSowjanya Komatineni  * NVQUIRK_HAS_TMCLK is for SoC's having separate timeout clock for Tegra
1158048822bSSowjanya Komatineni  * SDMMC hardware data timeout.
1168048822bSSowjanya Komatineni  */
1178048822bSSowjanya Komatineni #define NVQUIRK_HAS_TMCLK				BIT(10)
1188048822bSSowjanya Komatineni 
1193c4019f9SSowjanya Komatineni /* SDMMC CQE Base Address for Tegra Host Ver 4.1 and Higher */
1203c4019f9SSowjanya Komatineni #define SDHCI_TEGRA_CQE_BASE_ADDR			0xF000
1213c4019f9SSowjanya Komatineni 
1223e44a1a7SStephen Warren struct sdhci_tegra_soc_data {
1231db5eebfSLars-Peter Clausen 	const struct sdhci_pltfm_data *pdata;
124b960bc44SNicolin Chen 	u64 dma_mask;
1253e44a1a7SStephen Warren 	u32 nvquirks;
126ea8fc595SSowjanya Komatineni 	u8 min_tap_delay;
127ea8fc595SSowjanya Komatineni 	u8 max_tap_delay;
1283e44a1a7SStephen Warren };
1293e44a1a7SStephen Warren 
13051b77c8eSAapo Vienamo /* Magic pull up and pull down pad calibration offsets */
13151b77c8eSAapo Vienamo struct sdhci_tegra_autocal_offsets {
13251b77c8eSAapo Vienamo 	u32 pull_up_3v3;
13351b77c8eSAapo Vienamo 	u32 pull_down_3v3;
13451b77c8eSAapo Vienamo 	u32 pull_up_3v3_timeout;
13551b77c8eSAapo Vienamo 	u32 pull_down_3v3_timeout;
13651b77c8eSAapo Vienamo 	u32 pull_up_1v8;
13751b77c8eSAapo Vienamo 	u32 pull_down_1v8;
13851b77c8eSAapo Vienamo 	u32 pull_up_1v8_timeout;
13951b77c8eSAapo Vienamo 	u32 pull_down_1v8_timeout;
14051b77c8eSAapo Vienamo 	u32 pull_up_sdr104;
14151b77c8eSAapo Vienamo 	u32 pull_down_sdr104;
14251b77c8eSAapo Vienamo 	u32 pull_up_hs400;
14351b77c8eSAapo Vienamo 	u32 pull_down_hs400;
14451b77c8eSAapo Vienamo };
14551b77c8eSAapo Vienamo 
1463e44a1a7SStephen Warren struct sdhci_tegra {
1473e44a1a7SStephen Warren 	const struct sdhci_tegra_soc_data *soc_data;
1482391b340SMylene JOSSERAND 	struct gpio_desc *power_gpio;
1498048822bSSowjanya Komatineni 	struct clk *tmclk;
150a8e326a9SLucas Stach 	bool ddr_signaling;
151e5c63d91SLucas Stach 	bool pad_calib_required;
15286ac2f8bSAapo Vienamo 	bool pad_control_available;
15320567be9SThierry Reding 
15420567be9SThierry Reding 	struct reset_control *rst;
15586ac2f8bSAapo Vienamo 	struct pinctrl *pinctrl_sdmmc;
15686ac2f8bSAapo Vienamo 	struct pinctrl_state *pinctrl_state_3v3;
15786ac2f8bSAapo Vienamo 	struct pinctrl_state *pinctrl_state_1v8;
158de25fa5aSSowjanya Komatineni 	struct pinctrl_state *pinctrl_state_3v3_drv;
159de25fa5aSSowjanya Komatineni 	struct pinctrl_state *pinctrl_state_1v8_drv;
16051b77c8eSAapo Vienamo 
16151b77c8eSAapo Vienamo 	struct sdhci_tegra_autocal_offsets autocal_offsets;
16261dad40eSAapo Vienamo 	ktime_t last_calib;
16385c0da17SAapo Vienamo 
16485c0da17SAapo Vienamo 	u32 default_tap;
16585c0da17SAapo Vienamo 	u32 default_trim;
166f5313aaaSAapo Vienamo 	u32 dqs_trim;
1673c4019f9SSowjanya Komatineni 	bool enable_hwcq;
168ea8fc595SSowjanya Komatineni 	unsigned long curr_clk_rate;
169ea8fc595SSowjanya Komatineni 	u8 tuned_tap_delay;
1703e44a1a7SStephen Warren };
1713e44a1a7SStephen Warren 
17203d2bfc8SOlof Johansson static u16 tegra_sdhci_readw(struct sdhci_host *host, int reg)
17303d2bfc8SOlof Johansson {
1743e44a1a7SStephen Warren 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1750734e79cSJisheng Zhang 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
1763e44a1a7SStephen Warren 	const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
1773e44a1a7SStephen Warren 
1783e44a1a7SStephen Warren 	if (unlikely((soc_data->nvquirks & NVQUIRK_FORCE_SDHCI_SPEC_200) &&
1793e44a1a7SStephen Warren 			(reg == SDHCI_HOST_VERSION))) {
18003d2bfc8SOlof Johansson 		/* Erratum: Version register is invalid in HW. */
18103d2bfc8SOlof Johansson 		return SDHCI_SPEC_200;
18203d2bfc8SOlof Johansson 	}
18303d2bfc8SOlof Johansson 
18403d2bfc8SOlof Johansson 	return readw(host->ioaddr + reg);
18503d2bfc8SOlof Johansson }
18603d2bfc8SOlof Johansson 
187352ee868SPavan Kunapuli static void tegra_sdhci_writew(struct sdhci_host *host, u16 val, int reg)
188352ee868SPavan Kunapuli {
189352ee868SPavan Kunapuli 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
190352ee868SPavan Kunapuli 
191352ee868SPavan Kunapuli 	switch (reg) {
192352ee868SPavan Kunapuli 	case SDHCI_TRANSFER_MODE:
193352ee868SPavan Kunapuli 		/*
194352ee868SPavan Kunapuli 		 * Postpone this write, we must do it together with a
195352ee868SPavan Kunapuli 		 * command write that is down below.
196352ee868SPavan Kunapuli 		 */
197352ee868SPavan Kunapuli 		pltfm_host->xfer_mode_shadow = val;
198352ee868SPavan Kunapuli 		return;
199352ee868SPavan Kunapuli 	case SDHCI_COMMAND:
200352ee868SPavan Kunapuli 		writel((val << 16) | pltfm_host->xfer_mode_shadow,
201352ee868SPavan Kunapuli 			host->ioaddr + SDHCI_TRANSFER_MODE);
202352ee868SPavan Kunapuli 		return;
203352ee868SPavan Kunapuli 	}
204352ee868SPavan Kunapuli 
205352ee868SPavan Kunapuli 	writew(val, host->ioaddr + reg);
206352ee868SPavan Kunapuli }
207352ee868SPavan Kunapuli 
20803d2bfc8SOlof Johansson static void tegra_sdhci_writel(struct sdhci_host *host, u32 val, int reg)
20903d2bfc8SOlof Johansson {
2103e44a1a7SStephen Warren 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
2110734e79cSJisheng Zhang 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
2123e44a1a7SStephen Warren 	const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
2133e44a1a7SStephen Warren 
21403d2bfc8SOlof Johansson 	/* Seems like we're getting spurious timeout and crc errors, so
21503d2bfc8SOlof Johansson 	 * disable signalling of them. In case of real errors software
21603d2bfc8SOlof Johansson 	 * timers should take care of eventually detecting them.
21703d2bfc8SOlof Johansson 	 */
21803d2bfc8SOlof Johansson 	if (unlikely(reg == SDHCI_SIGNAL_ENABLE))
21903d2bfc8SOlof Johansson 		val &= ~(SDHCI_INT_TIMEOUT|SDHCI_INT_CRC);
22003d2bfc8SOlof Johansson 
22103d2bfc8SOlof Johansson 	writel(val, host->ioaddr + reg);
22203d2bfc8SOlof Johansson 
2233e44a1a7SStephen Warren 	if (unlikely((soc_data->nvquirks & NVQUIRK_ENABLE_BLOCK_GAP_DET) &&
2243e44a1a7SStephen Warren 			(reg == SDHCI_INT_ENABLE))) {
22503d2bfc8SOlof Johansson 		/* Erratum: Must enable block gap interrupt detection */
22603d2bfc8SOlof Johansson 		u8 gap_ctrl = readb(host->ioaddr + SDHCI_BLOCK_GAP_CONTROL);
22703d2bfc8SOlof Johansson 		if (val & SDHCI_INT_CARD_INT)
22803d2bfc8SOlof Johansson 			gap_ctrl |= 0x8;
22903d2bfc8SOlof Johansson 		else
23003d2bfc8SOlof Johansson 			gap_ctrl &= ~0x8;
23103d2bfc8SOlof Johansson 		writeb(gap_ctrl, host->ioaddr + SDHCI_BLOCK_GAP_CONTROL);
23203d2bfc8SOlof Johansson 	}
23303d2bfc8SOlof Johansson }
23403d2bfc8SOlof Johansson 
23538a284d9SAapo Vienamo static bool tegra_sdhci_configure_card_clk(struct sdhci_host *host, bool enable)
23638a284d9SAapo Vienamo {
23738a284d9SAapo Vienamo 	bool status;
23838a284d9SAapo Vienamo 	u32 reg;
23938a284d9SAapo Vienamo 
24038a284d9SAapo Vienamo 	reg = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
24138a284d9SAapo Vienamo 	status = !!(reg & SDHCI_CLOCK_CARD_EN);
24238a284d9SAapo Vienamo 
24338a284d9SAapo Vienamo 	if (status == enable)
24438a284d9SAapo Vienamo 		return status;
24538a284d9SAapo Vienamo 
24638a284d9SAapo Vienamo 	if (enable)
24738a284d9SAapo Vienamo 		reg |= SDHCI_CLOCK_CARD_EN;
24838a284d9SAapo Vienamo 	else
24938a284d9SAapo Vienamo 		reg &= ~SDHCI_CLOCK_CARD_EN;
25038a284d9SAapo Vienamo 
25138a284d9SAapo Vienamo 	sdhci_writew(host, reg, SDHCI_CLOCK_CONTROL);
25238a284d9SAapo Vienamo 
25338a284d9SAapo Vienamo 	return status;
25438a284d9SAapo Vienamo }
25538a284d9SAapo Vienamo 
25638a284d9SAapo Vienamo static void tegra210_sdhci_writew(struct sdhci_host *host, u16 val, int reg)
25738a284d9SAapo Vienamo {
25838a284d9SAapo Vienamo 	bool is_tuning_cmd = 0;
25938a284d9SAapo Vienamo 	bool clk_enabled;
26038a284d9SAapo Vienamo 	u8 cmd;
26138a284d9SAapo Vienamo 
26238a284d9SAapo Vienamo 	if (reg == SDHCI_COMMAND) {
26338a284d9SAapo Vienamo 		cmd = SDHCI_GET_CMD(val);
26438a284d9SAapo Vienamo 		is_tuning_cmd = cmd == MMC_SEND_TUNING_BLOCK ||
26538a284d9SAapo Vienamo 				cmd == MMC_SEND_TUNING_BLOCK_HS200;
26638a284d9SAapo Vienamo 	}
26738a284d9SAapo Vienamo 
26838a284d9SAapo Vienamo 	if (is_tuning_cmd)
26938a284d9SAapo Vienamo 		clk_enabled = tegra_sdhci_configure_card_clk(host, 0);
27038a284d9SAapo Vienamo 
27138a284d9SAapo Vienamo 	writew(val, host->ioaddr + reg);
27238a284d9SAapo Vienamo 
27338a284d9SAapo Vienamo 	if (is_tuning_cmd) {
27438a284d9SAapo Vienamo 		udelay(1);
275ea8fc595SSowjanya Komatineni 		sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
27638a284d9SAapo Vienamo 		tegra_sdhci_configure_card_clk(host, clk_enabled);
27738a284d9SAapo Vienamo 	}
27838a284d9SAapo Vienamo }
27938a284d9SAapo Vienamo 
2800f686ca9SDmitry Osipenko static unsigned int tegra_sdhci_get_ro(struct sdhci_host *host)
2810f686ca9SDmitry Osipenko {
2820f686ca9SDmitry Osipenko 	/*
2830f686ca9SDmitry Osipenko 	 * Write-enable shall be assumed if GPIO is missing in a board's
2840f686ca9SDmitry Osipenko 	 * device-tree because SDHCI's WRITE_PROTECT bit doesn't work on
2850f686ca9SDmitry Osipenko 	 * Tegra.
2860f686ca9SDmitry Osipenko 	 */
2870f686ca9SDmitry Osipenko 	return mmc_gpio_get_ro(host->mmc);
2880f686ca9SDmitry Osipenko }
2890f686ca9SDmitry Osipenko 
29086ac2f8bSAapo Vienamo static bool tegra_sdhci_is_pad_and_regulator_valid(struct sdhci_host *host)
29186ac2f8bSAapo Vienamo {
29286ac2f8bSAapo Vienamo 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
29386ac2f8bSAapo Vienamo 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
29486ac2f8bSAapo Vienamo 	int has_1v8, has_3v3;
29586ac2f8bSAapo Vienamo 
29686ac2f8bSAapo Vienamo 	/*
29786ac2f8bSAapo Vienamo 	 * The SoCs which have NVQUIRK_NEEDS_PAD_CONTROL require software pad
29886ac2f8bSAapo Vienamo 	 * voltage configuration in order to perform voltage switching. This
29986ac2f8bSAapo Vienamo 	 * means that valid pinctrl info is required on SDHCI instances capable
30086ac2f8bSAapo Vienamo 	 * of performing voltage switching. Whether or not an SDHCI instance is
30186ac2f8bSAapo Vienamo 	 * capable of voltage switching is determined based on the regulator.
30286ac2f8bSAapo Vienamo 	 */
30386ac2f8bSAapo Vienamo 
30486ac2f8bSAapo Vienamo 	if (!(tegra_host->soc_data->nvquirks & NVQUIRK_NEEDS_PAD_CONTROL))
30586ac2f8bSAapo Vienamo 		return true;
30686ac2f8bSAapo Vienamo 
30786ac2f8bSAapo Vienamo 	if (IS_ERR(host->mmc->supply.vqmmc))
30886ac2f8bSAapo Vienamo 		return false;
30986ac2f8bSAapo Vienamo 
31086ac2f8bSAapo Vienamo 	has_1v8 = regulator_is_supported_voltage(host->mmc->supply.vqmmc,
31186ac2f8bSAapo Vienamo 						 1700000, 1950000);
31286ac2f8bSAapo Vienamo 
31386ac2f8bSAapo Vienamo 	has_3v3 = regulator_is_supported_voltage(host->mmc->supply.vqmmc,
31486ac2f8bSAapo Vienamo 						 2700000, 3600000);
31586ac2f8bSAapo Vienamo 
31686ac2f8bSAapo Vienamo 	if (has_1v8 == 1 && has_3v3 == 1)
31786ac2f8bSAapo Vienamo 		return tegra_host->pad_control_available;
31886ac2f8bSAapo Vienamo 
31986ac2f8bSAapo Vienamo 	/* Fixed voltage, no pad control required. */
32086ac2f8bSAapo Vienamo 	return true;
32186ac2f8bSAapo Vienamo }
32286ac2f8bSAapo Vienamo 
323c2c09678SAapo Vienamo static void tegra_sdhci_set_tap(struct sdhci_host *host, unsigned int tap)
324c2c09678SAapo Vienamo {
325c2c09678SAapo Vienamo 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
326c2c09678SAapo Vienamo 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
327c2c09678SAapo Vienamo 	const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
328c2c09678SAapo Vienamo 	bool card_clk_enabled = false;
329c2c09678SAapo Vienamo 	u32 reg;
330c2c09678SAapo Vienamo 
331c2c09678SAapo Vienamo 	/*
332c2c09678SAapo Vienamo 	 * Touching the tap values is a bit tricky on some SoC generations.
333c2c09678SAapo Vienamo 	 * The quirk enables a workaround for a glitch that sometimes occurs if
334c2c09678SAapo Vienamo 	 * the tap values are changed.
335c2c09678SAapo Vienamo 	 */
336c2c09678SAapo Vienamo 
337c2c09678SAapo Vienamo 	if (soc_data->nvquirks & NVQUIRK_DIS_CARD_CLK_CONFIG_TAP)
338c2c09678SAapo Vienamo 		card_clk_enabled = tegra_sdhci_configure_card_clk(host, false);
339c2c09678SAapo Vienamo 
340c2c09678SAapo Vienamo 	reg = sdhci_readl(host, SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
341c2c09678SAapo Vienamo 	reg &= ~SDHCI_CLOCK_CTRL_TAP_MASK;
342c2c09678SAapo Vienamo 	reg |= tap << SDHCI_CLOCK_CTRL_TAP_SHIFT;
343c2c09678SAapo Vienamo 	sdhci_writel(host, reg, SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
344c2c09678SAapo Vienamo 
345c2c09678SAapo Vienamo 	if (soc_data->nvquirks & NVQUIRK_DIS_CARD_CLK_CONFIG_TAP &&
346c2c09678SAapo Vienamo 	    card_clk_enabled) {
347c2c09678SAapo Vienamo 		udelay(1);
348c2c09678SAapo Vienamo 		sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
349c2c09678SAapo Vienamo 		tegra_sdhci_configure_card_clk(host, card_clk_enabled);
350c2c09678SAapo Vienamo 	}
351c2c09678SAapo Vienamo }
352c2c09678SAapo Vienamo 
353dfc9700cSAapo Vienamo static void tegra_sdhci_hs400_enhanced_strobe(struct mmc_host *mmc,
354dfc9700cSAapo Vienamo 					      struct mmc_ios *ios)
355dfc9700cSAapo Vienamo {
356dfc9700cSAapo Vienamo 	struct sdhci_host *host = mmc_priv(mmc);
357dfc9700cSAapo Vienamo 	u32 val;
358dfc9700cSAapo Vienamo 
359dfc9700cSAapo Vienamo 	val = sdhci_readl(host, SDHCI_TEGRA_VENDOR_SYS_SW_CTRL);
360dfc9700cSAapo Vienamo 
361dfc9700cSAapo Vienamo 	if (ios->enhanced_strobe)
362dfc9700cSAapo Vienamo 		val |= SDHCI_TEGRA_SYS_SW_CTRL_ENHANCED_STROBE;
363dfc9700cSAapo Vienamo 	else
364dfc9700cSAapo Vienamo 		val &= ~SDHCI_TEGRA_SYS_SW_CTRL_ENHANCED_STROBE;
365dfc9700cSAapo Vienamo 
366dfc9700cSAapo Vienamo 	sdhci_writel(host, val, SDHCI_TEGRA_VENDOR_SYS_SW_CTRL);
367dfc9700cSAapo Vienamo 
368dfc9700cSAapo Vienamo }
369dfc9700cSAapo Vienamo 
37003231f9bSRussell King static void tegra_sdhci_reset(struct sdhci_host *host, u8 mask)
371ca5879d3SPavan Kunapuli {
372ca5879d3SPavan Kunapuli 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
3730734e79cSJisheng Zhang 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
374ca5879d3SPavan Kunapuli 	const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
3759d548f11SAapo Vienamo 	u32 misc_ctrl, clk_ctrl, pad_ctrl;
376ca5879d3SPavan Kunapuli 
37703231f9bSRussell King 	sdhci_reset(host, mask);
37803231f9bSRussell King 
379ca5879d3SPavan Kunapuli 	if (!(mask & SDHCI_RESET_ALL))
380ca5879d3SPavan Kunapuli 		return;
381ca5879d3SPavan Kunapuli 
382c2c09678SAapo Vienamo 	tegra_sdhci_set_tap(host, tegra_host->default_tap);
383c2c09678SAapo Vienamo 
3841b84def8SLucas Stach 	misc_ctrl = sdhci_readl(host, SDHCI_TEGRA_VENDOR_MISC_CTRL);
3854f6aa326SJon Hunter 	clk_ctrl = sdhci_readl(host, SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
3864f6aa326SJon Hunter 
3874f6aa326SJon Hunter 	misc_ctrl &= ~(SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300 |
3884f6aa326SJon Hunter 		       SDHCI_MISC_CTRL_ENABLE_SDR50 |
3894f6aa326SJon Hunter 		       SDHCI_MISC_CTRL_ENABLE_DDR50 |
3904f6aa326SJon Hunter 		       SDHCI_MISC_CTRL_ENABLE_SDR104);
3914f6aa326SJon Hunter 
39241a0b8d7SAapo Vienamo 	clk_ctrl &= ~(SDHCI_CLOCK_CTRL_TRIM_MASK |
39341a0b8d7SAapo Vienamo 		      SDHCI_CLOCK_CTRL_SPI_MODE_CLKEN_OVERRIDE);
3944f6aa326SJon Hunter 
39586ac2f8bSAapo Vienamo 	if (tegra_sdhci_is_pad_and_regulator_valid(host)) {
396ca5879d3SPavan Kunapuli 		/* Erratum: Enable SDHCI spec v3.00 support */
3973145351aSAndrew Bresticker 		if (soc_data->nvquirks & NVQUIRK_ENABLE_SDHCI_SPEC_300)
398ca5879d3SPavan Kunapuli 			misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300;
3997ad2ed1dSLucas Stach 		/* Advertise UHS modes as supported by host */
4007ad2ed1dSLucas Stach 		if (soc_data->nvquirks & NVQUIRK_ENABLE_SDR50)
4017ad2ed1dSLucas Stach 			misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDR50;
4027ad2ed1dSLucas Stach 		if (soc_data->nvquirks & NVQUIRK_ENABLE_DDR50)
4037ad2ed1dSLucas Stach 			misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_DDR50;
4047ad2ed1dSLucas Stach 		if (soc_data->nvquirks & NVQUIRK_ENABLE_SDR104)
4057ad2ed1dSLucas Stach 			misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDR104;
406f571389cSMichał Mirosław 		if (soc_data->nvquirks & NVQUIRK_ENABLE_SDR50)
407c3c2384cSLucas Stach 			clk_ctrl |= SDHCI_CLOCK_CTRL_SDR50_TUNING_OVERRIDE;
4084f6aa326SJon Hunter 	}
4094f6aa326SJon Hunter 
41041a0b8d7SAapo Vienamo 	clk_ctrl |= tegra_host->default_trim << SDHCI_CLOCK_CTRL_TRIM_SHIFT;
41141a0b8d7SAapo Vienamo 
4124f6aa326SJon Hunter 	sdhci_writel(host, misc_ctrl, SDHCI_TEGRA_VENDOR_MISC_CTRL);
41374cd42bcSLucas Stach 	sdhci_writel(host, clk_ctrl, SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
41474cd42bcSLucas Stach 
4159d548f11SAapo Vienamo 	if (soc_data->nvquirks & NVQUIRK_HAS_PADCALIB) {
4169d548f11SAapo Vienamo 		pad_ctrl = sdhci_readl(host, SDHCI_TEGRA_SDMEM_COMP_PADCTRL);
4179d548f11SAapo Vienamo 		pad_ctrl &= ~SDHCI_TEGRA_SDMEM_COMP_PADCTRL_VREF_SEL_MASK;
4189d548f11SAapo Vienamo 		pad_ctrl |= SDHCI_TEGRA_SDMEM_COMP_PADCTRL_VREF_SEL_VAL;
4199d548f11SAapo Vienamo 		sdhci_writel(host, pad_ctrl, SDHCI_TEGRA_SDMEM_COMP_PADCTRL);
4209d548f11SAapo Vienamo 
421e5c63d91SLucas Stach 		tegra_host->pad_calib_required = true;
4229d548f11SAapo Vienamo 	}
423e5c63d91SLucas Stach 
424a8e326a9SLucas Stach 	tegra_host->ddr_signaling = false;
425ca5879d3SPavan Kunapuli }
426ca5879d3SPavan Kunapuli 
427212b0cf1SAapo Vienamo static void tegra_sdhci_configure_cal_pad(struct sdhci_host *host, bool enable)
428212b0cf1SAapo Vienamo {
429212b0cf1SAapo Vienamo 	u32 val;
430212b0cf1SAapo Vienamo 
431212b0cf1SAapo Vienamo 	/*
432212b0cf1SAapo Vienamo 	 * Enable or disable the additional I/O pad used by the drive strength
433212b0cf1SAapo Vienamo 	 * calibration process.
434212b0cf1SAapo Vienamo 	 */
435212b0cf1SAapo Vienamo 	val = sdhci_readl(host, SDHCI_TEGRA_SDMEM_COMP_PADCTRL);
436212b0cf1SAapo Vienamo 
437212b0cf1SAapo Vienamo 	if (enable)
438212b0cf1SAapo Vienamo 		val |= SDHCI_TEGRA_SDMEM_COMP_PADCTRL_E_INPUT_E_PWRD;
439212b0cf1SAapo Vienamo 	else
440212b0cf1SAapo Vienamo 		val &= ~SDHCI_TEGRA_SDMEM_COMP_PADCTRL_E_INPUT_E_PWRD;
441212b0cf1SAapo Vienamo 
442212b0cf1SAapo Vienamo 	sdhci_writel(host, val, SDHCI_TEGRA_SDMEM_COMP_PADCTRL);
443212b0cf1SAapo Vienamo 
444212b0cf1SAapo Vienamo 	if (enable)
445212b0cf1SAapo Vienamo 		usleep_range(1, 2);
446212b0cf1SAapo Vienamo }
447212b0cf1SAapo Vienamo 
44851b77c8eSAapo Vienamo static void tegra_sdhci_set_pad_autocal_offset(struct sdhci_host *host,
44951b77c8eSAapo Vienamo 					       u16 pdpu)
45051b77c8eSAapo Vienamo {
45151b77c8eSAapo Vienamo 	u32 reg;
45251b77c8eSAapo Vienamo 
45351b77c8eSAapo Vienamo 	reg = sdhci_readl(host, SDHCI_TEGRA_AUTO_CAL_CONFIG);
45451b77c8eSAapo Vienamo 	reg &= ~SDHCI_AUTO_CAL_PDPU_OFFSET_MASK;
45551b77c8eSAapo Vienamo 	reg |= pdpu;
45651b77c8eSAapo Vienamo 	sdhci_writel(host, reg, SDHCI_TEGRA_AUTO_CAL_CONFIG);
45751b77c8eSAapo Vienamo }
45851b77c8eSAapo Vienamo 
459de25fa5aSSowjanya Komatineni static int tegra_sdhci_set_padctrl(struct sdhci_host *host, int voltage,
460de25fa5aSSowjanya Komatineni 				   bool state_drvupdn)
461de25fa5aSSowjanya Komatineni {
462de25fa5aSSowjanya Komatineni 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
463de25fa5aSSowjanya Komatineni 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
464de25fa5aSSowjanya Komatineni 	struct sdhci_tegra_autocal_offsets *offsets =
465de25fa5aSSowjanya Komatineni 						&tegra_host->autocal_offsets;
466de25fa5aSSowjanya Komatineni 	struct pinctrl_state *pinctrl_drvupdn = NULL;
467de25fa5aSSowjanya Komatineni 	int ret = 0;
468de25fa5aSSowjanya Komatineni 	u8 drvup = 0, drvdn = 0;
469de25fa5aSSowjanya Komatineni 	u32 reg;
470de25fa5aSSowjanya Komatineni 
471de25fa5aSSowjanya Komatineni 	if (!state_drvupdn) {
472de25fa5aSSowjanya Komatineni 		/* PADS Drive Strength */
473de25fa5aSSowjanya Komatineni 		if (voltage == MMC_SIGNAL_VOLTAGE_180) {
474de25fa5aSSowjanya Komatineni 			if (tegra_host->pinctrl_state_1v8_drv) {
475de25fa5aSSowjanya Komatineni 				pinctrl_drvupdn =
476de25fa5aSSowjanya Komatineni 					tegra_host->pinctrl_state_1v8_drv;
477de25fa5aSSowjanya Komatineni 			} else {
478de25fa5aSSowjanya Komatineni 				drvup = offsets->pull_up_1v8_timeout;
479de25fa5aSSowjanya Komatineni 				drvdn = offsets->pull_down_1v8_timeout;
480de25fa5aSSowjanya Komatineni 			}
481de25fa5aSSowjanya Komatineni 		} else {
482de25fa5aSSowjanya Komatineni 			if (tegra_host->pinctrl_state_3v3_drv) {
483de25fa5aSSowjanya Komatineni 				pinctrl_drvupdn =
484de25fa5aSSowjanya Komatineni 					tegra_host->pinctrl_state_3v3_drv;
485de25fa5aSSowjanya Komatineni 			} else {
486de25fa5aSSowjanya Komatineni 				drvup = offsets->pull_up_3v3_timeout;
487de25fa5aSSowjanya Komatineni 				drvdn = offsets->pull_down_3v3_timeout;
488de25fa5aSSowjanya Komatineni 			}
489de25fa5aSSowjanya Komatineni 		}
490de25fa5aSSowjanya Komatineni 
491de25fa5aSSowjanya Komatineni 		if (pinctrl_drvupdn != NULL) {
492de25fa5aSSowjanya Komatineni 			ret = pinctrl_select_state(tegra_host->pinctrl_sdmmc,
493de25fa5aSSowjanya Komatineni 							pinctrl_drvupdn);
494de25fa5aSSowjanya Komatineni 			if (ret < 0)
495de25fa5aSSowjanya Komatineni 				dev_err(mmc_dev(host->mmc),
496de25fa5aSSowjanya Komatineni 					"failed pads drvupdn, ret: %d\n", ret);
497de25fa5aSSowjanya Komatineni 		} else if ((drvup) || (drvdn)) {
498de25fa5aSSowjanya Komatineni 			reg = sdhci_readl(host,
499de25fa5aSSowjanya Komatineni 					SDHCI_TEGRA_SDMEM_COMP_PADCTRL);
500de25fa5aSSowjanya Komatineni 			reg &= ~SDHCI_COMP_PADCTRL_DRVUPDN_OFFSET_MASK;
501de25fa5aSSowjanya Komatineni 			reg |= (drvup << 20) | (drvdn << 12);
502de25fa5aSSowjanya Komatineni 			sdhci_writel(host, reg,
503de25fa5aSSowjanya Komatineni 					SDHCI_TEGRA_SDMEM_COMP_PADCTRL);
504de25fa5aSSowjanya Komatineni 		}
505de25fa5aSSowjanya Komatineni 
506de25fa5aSSowjanya Komatineni 	} else {
507de25fa5aSSowjanya Komatineni 		/* Dual Voltage PADS Voltage selection */
508de25fa5aSSowjanya Komatineni 		if (!tegra_host->pad_control_available)
509de25fa5aSSowjanya Komatineni 			return 0;
510de25fa5aSSowjanya Komatineni 
511de25fa5aSSowjanya Komatineni 		if (voltage == MMC_SIGNAL_VOLTAGE_180) {
512de25fa5aSSowjanya Komatineni 			ret = pinctrl_select_state(tegra_host->pinctrl_sdmmc,
513de25fa5aSSowjanya Komatineni 						tegra_host->pinctrl_state_1v8);
514de25fa5aSSowjanya Komatineni 			if (ret < 0)
515de25fa5aSSowjanya Komatineni 				dev_err(mmc_dev(host->mmc),
516de25fa5aSSowjanya Komatineni 					"setting 1.8V failed, ret: %d\n", ret);
517de25fa5aSSowjanya Komatineni 		} else {
518de25fa5aSSowjanya Komatineni 			ret = pinctrl_select_state(tegra_host->pinctrl_sdmmc,
519de25fa5aSSowjanya Komatineni 						tegra_host->pinctrl_state_3v3);
520de25fa5aSSowjanya Komatineni 			if (ret < 0)
521de25fa5aSSowjanya Komatineni 				dev_err(mmc_dev(host->mmc),
522de25fa5aSSowjanya Komatineni 					"setting 3.3V failed, ret: %d\n", ret);
523de25fa5aSSowjanya Komatineni 		}
524de25fa5aSSowjanya Komatineni 	}
525de25fa5aSSowjanya Komatineni 
526de25fa5aSSowjanya Komatineni 	return ret;
527de25fa5aSSowjanya Komatineni }
528de25fa5aSSowjanya Komatineni 
529e5c63d91SLucas Stach static void tegra_sdhci_pad_autocalib(struct sdhci_host *host)
530e5c63d91SLucas Stach {
53151b77c8eSAapo Vienamo 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
53251b77c8eSAapo Vienamo 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
53351b77c8eSAapo Vienamo 	struct sdhci_tegra_autocal_offsets offsets =
53451b77c8eSAapo Vienamo 			tegra_host->autocal_offsets;
53551b77c8eSAapo Vienamo 	struct mmc_ios *ios = &host->mmc->ios;
536887bda8fSAapo Vienamo 	bool card_clk_enabled;
53751b77c8eSAapo Vienamo 	u16 pdpu;
538e7c07148SAapo Vienamo 	u32 reg;
539e7c07148SAapo Vienamo 	int ret;
540e5c63d91SLucas Stach 
54151b77c8eSAapo Vienamo 	switch (ios->timing) {
54251b77c8eSAapo Vienamo 	case MMC_TIMING_UHS_SDR104:
54351b77c8eSAapo Vienamo 		pdpu = offsets.pull_down_sdr104 << 8 | offsets.pull_up_sdr104;
54451b77c8eSAapo Vienamo 		break;
54551b77c8eSAapo Vienamo 	case MMC_TIMING_MMC_HS400:
54651b77c8eSAapo Vienamo 		pdpu = offsets.pull_down_hs400 << 8 | offsets.pull_up_hs400;
54751b77c8eSAapo Vienamo 		break;
54851b77c8eSAapo Vienamo 	default:
54951b77c8eSAapo Vienamo 		if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180)
55051b77c8eSAapo Vienamo 			pdpu = offsets.pull_down_1v8 << 8 | offsets.pull_up_1v8;
55151b77c8eSAapo Vienamo 		else
55251b77c8eSAapo Vienamo 			pdpu = offsets.pull_down_3v3 << 8 | offsets.pull_up_3v3;
55351b77c8eSAapo Vienamo 	}
55451b77c8eSAapo Vienamo 
555de25fa5aSSowjanya Komatineni 	/* Set initial offset before auto-calibration */
55651b77c8eSAapo Vienamo 	tegra_sdhci_set_pad_autocal_offset(host, pdpu);
55751b77c8eSAapo Vienamo 
558887bda8fSAapo Vienamo 	card_clk_enabled = tegra_sdhci_configure_card_clk(host, false);
559887bda8fSAapo Vienamo 
560212b0cf1SAapo Vienamo 	tegra_sdhci_configure_cal_pad(host, true);
561212b0cf1SAapo Vienamo 
562e7c07148SAapo Vienamo 	reg = sdhci_readl(host, SDHCI_TEGRA_AUTO_CAL_CONFIG);
563e7c07148SAapo Vienamo 	reg |= SDHCI_AUTO_CAL_ENABLE | SDHCI_AUTO_CAL_START;
564e7c07148SAapo Vienamo 	sdhci_writel(host, reg, SDHCI_TEGRA_AUTO_CAL_CONFIG);
565e5c63d91SLucas Stach 
566e7c07148SAapo Vienamo 	usleep_range(1, 2);
567e7c07148SAapo Vienamo 	/* 10 ms timeout */
568e7c07148SAapo Vienamo 	ret = readl_poll_timeout(host->ioaddr + SDHCI_TEGRA_AUTO_CAL_STATUS,
569e7c07148SAapo Vienamo 				 reg, !(reg & SDHCI_TEGRA_AUTO_CAL_ACTIVE),
570e7c07148SAapo Vienamo 				 1000, 10000);
571e7c07148SAapo Vienamo 
572212b0cf1SAapo Vienamo 	tegra_sdhci_configure_cal_pad(host, false);
573212b0cf1SAapo Vienamo 
574887bda8fSAapo Vienamo 	tegra_sdhci_configure_card_clk(host, card_clk_enabled);
575887bda8fSAapo Vienamo 
57651b77c8eSAapo Vienamo 	if (ret) {
577e7c07148SAapo Vienamo 		dev_err(mmc_dev(host->mmc), "Pad autocal timed out\n");
57851b77c8eSAapo Vienamo 
579de25fa5aSSowjanya Komatineni 		/* Disable automatic cal and use fixed Drive Strengths */
58051b77c8eSAapo Vienamo 		reg = sdhci_readl(host, SDHCI_TEGRA_AUTO_CAL_CONFIG);
58151b77c8eSAapo Vienamo 		reg &= ~SDHCI_AUTO_CAL_ENABLE;
58251b77c8eSAapo Vienamo 		sdhci_writel(host, reg, SDHCI_TEGRA_AUTO_CAL_CONFIG);
58351b77c8eSAapo Vienamo 
584de25fa5aSSowjanya Komatineni 		ret = tegra_sdhci_set_padctrl(host, ios->signal_voltage, false);
585de25fa5aSSowjanya Komatineni 		if (ret < 0)
586de25fa5aSSowjanya Komatineni 			dev_err(mmc_dev(host->mmc),
587de25fa5aSSowjanya Komatineni 				"Setting drive strengths failed: %d\n", ret);
58851b77c8eSAapo Vienamo 	}
58951b77c8eSAapo Vienamo }
59051b77c8eSAapo Vienamo 
59151b77c8eSAapo Vienamo static void tegra_sdhci_parse_pad_autocal_dt(struct sdhci_host *host)
59251b77c8eSAapo Vienamo {
59351b77c8eSAapo Vienamo 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
59451b77c8eSAapo Vienamo 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
59551b77c8eSAapo Vienamo 	struct sdhci_tegra_autocal_offsets *autocal =
59651b77c8eSAapo Vienamo 			&tegra_host->autocal_offsets;
59751b77c8eSAapo Vienamo 	int err;
59851b77c8eSAapo Vienamo 
59951b77c8eSAapo Vienamo 	err = device_property_read_u32(host->mmc->parent,
60051b77c8eSAapo Vienamo 			"nvidia,pad-autocal-pull-up-offset-3v3",
60151b77c8eSAapo Vienamo 			&autocal->pull_up_3v3);
60251b77c8eSAapo Vienamo 	if (err)
60351b77c8eSAapo Vienamo 		autocal->pull_up_3v3 = 0;
60451b77c8eSAapo Vienamo 
60551b77c8eSAapo Vienamo 	err = device_property_read_u32(host->mmc->parent,
60651b77c8eSAapo Vienamo 			"nvidia,pad-autocal-pull-down-offset-3v3",
60751b77c8eSAapo Vienamo 			&autocal->pull_down_3v3);
60851b77c8eSAapo Vienamo 	if (err)
60951b77c8eSAapo Vienamo 		autocal->pull_down_3v3 = 0;
61051b77c8eSAapo Vienamo 
61151b77c8eSAapo Vienamo 	err = device_property_read_u32(host->mmc->parent,
61251b77c8eSAapo Vienamo 			"nvidia,pad-autocal-pull-up-offset-1v8",
61351b77c8eSAapo Vienamo 			&autocal->pull_up_1v8);
61451b77c8eSAapo Vienamo 	if (err)
61551b77c8eSAapo Vienamo 		autocal->pull_up_1v8 = 0;
61651b77c8eSAapo Vienamo 
61751b77c8eSAapo Vienamo 	err = device_property_read_u32(host->mmc->parent,
61851b77c8eSAapo Vienamo 			"nvidia,pad-autocal-pull-down-offset-1v8",
61951b77c8eSAapo Vienamo 			&autocal->pull_down_1v8);
62051b77c8eSAapo Vienamo 	if (err)
62151b77c8eSAapo Vienamo 		autocal->pull_down_1v8 = 0;
62251b77c8eSAapo Vienamo 
62351b77c8eSAapo Vienamo 	err = device_property_read_u32(host->mmc->parent,
624aebbf577SSowjanya Komatineni 			"nvidia,pad-autocal-pull-up-offset-sdr104",
625aebbf577SSowjanya Komatineni 			&autocal->pull_up_sdr104);
626aebbf577SSowjanya Komatineni 	if (err)
627aebbf577SSowjanya Komatineni 		autocal->pull_up_sdr104 = autocal->pull_up_1v8;
628aebbf577SSowjanya Komatineni 
629aebbf577SSowjanya Komatineni 	err = device_property_read_u32(host->mmc->parent,
630aebbf577SSowjanya Komatineni 			"nvidia,pad-autocal-pull-down-offset-sdr104",
631aebbf577SSowjanya Komatineni 			&autocal->pull_down_sdr104);
632aebbf577SSowjanya Komatineni 	if (err)
633aebbf577SSowjanya Komatineni 		autocal->pull_down_sdr104 = autocal->pull_down_1v8;
634aebbf577SSowjanya Komatineni 
635aebbf577SSowjanya Komatineni 	err = device_property_read_u32(host->mmc->parent,
636aebbf577SSowjanya Komatineni 			"nvidia,pad-autocal-pull-up-offset-hs400",
637aebbf577SSowjanya Komatineni 			&autocal->pull_up_hs400);
638aebbf577SSowjanya Komatineni 	if (err)
639aebbf577SSowjanya Komatineni 		autocal->pull_up_hs400 = autocal->pull_up_1v8;
640aebbf577SSowjanya Komatineni 
641aebbf577SSowjanya Komatineni 	err = device_property_read_u32(host->mmc->parent,
642aebbf577SSowjanya Komatineni 			"nvidia,pad-autocal-pull-down-offset-hs400",
643aebbf577SSowjanya Komatineni 			&autocal->pull_down_hs400);
644aebbf577SSowjanya Komatineni 	if (err)
645aebbf577SSowjanya Komatineni 		autocal->pull_down_hs400 = autocal->pull_down_1v8;
646aebbf577SSowjanya Komatineni 
647aebbf577SSowjanya Komatineni 	/*
648aebbf577SSowjanya Komatineni 	 * Different fail-safe drive strength values based on the signaling
649aebbf577SSowjanya Komatineni 	 * voltage are applicable for SoCs supporting 3V3 and 1V8 pad controls.
650aebbf577SSowjanya Komatineni 	 * So, avoid reading below device tree properties for SoCs that don't
651aebbf577SSowjanya Komatineni 	 * have NVQUIRK_NEEDS_PAD_CONTROL.
652aebbf577SSowjanya Komatineni 	 */
653aebbf577SSowjanya Komatineni 	if (!(tegra_host->soc_data->nvquirks & NVQUIRK_NEEDS_PAD_CONTROL))
654aebbf577SSowjanya Komatineni 		return;
655aebbf577SSowjanya Komatineni 
656aebbf577SSowjanya Komatineni 	err = device_property_read_u32(host->mmc->parent,
65751b77c8eSAapo Vienamo 			"nvidia,pad-autocal-pull-up-offset-3v3-timeout",
6585ccf7f55SSowjanya Komatineni 			&autocal->pull_up_3v3_timeout);
659de25fa5aSSowjanya Komatineni 	if (err) {
660de25fa5aSSowjanya Komatineni 		if (!IS_ERR(tegra_host->pinctrl_state_3v3) &&
661de25fa5aSSowjanya Komatineni 			(tegra_host->pinctrl_state_3v3_drv == NULL))
662de25fa5aSSowjanya Komatineni 			pr_warn("%s: Missing autocal timeout 3v3-pad drvs\n",
663de25fa5aSSowjanya Komatineni 				mmc_hostname(host->mmc));
66451b77c8eSAapo Vienamo 		autocal->pull_up_3v3_timeout = 0;
665de25fa5aSSowjanya Komatineni 	}
66651b77c8eSAapo Vienamo 
66751b77c8eSAapo Vienamo 	err = device_property_read_u32(host->mmc->parent,
66851b77c8eSAapo Vienamo 			"nvidia,pad-autocal-pull-down-offset-3v3-timeout",
6695ccf7f55SSowjanya Komatineni 			&autocal->pull_down_3v3_timeout);
670de25fa5aSSowjanya Komatineni 	if (err) {
671de25fa5aSSowjanya Komatineni 		if (!IS_ERR(tegra_host->pinctrl_state_3v3) &&
672de25fa5aSSowjanya Komatineni 			(tegra_host->pinctrl_state_3v3_drv == NULL))
673de25fa5aSSowjanya Komatineni 			pr_warn("%s: Missing autocal timeout 3v3-pad drvs\n",
674de25fa5aSSowjanya Komatineni 				mmc_hostname(host->mmc));
67551b77c8eSAapo Vienamo 		autocal->pull_down_3v3_timeout = 0;
676de25fa5aSSowjanya Komatineni 	}
67751b77c8eSAapo Vienamo 
67851b77c8eSAapo Vienamo 	err = device_property_read_u32(host->mmc->parent,
67951b77c8eSAapo Vienamo 			"nvidia,pad-autocal-pull-up-offset-1v8-timeout",
6805ccf7f55SSowjanya Komatineni 			&autocal->pull_up_1v8_timeout);
681de25fa5aSSowjanya Komatineni 	if (err) {
682de25fa5aSSowjanya Komatineni 		if (!IS_ERR(tegra_host->pinctrl_state_1v8) &&
683de25fa5aSSowjanya Komatineni 			(tegra_host->pinctrl_state_1v8_drv == NULL))
684de25fa5aSSowjanya Komatineni 			pr_warn("%s: Missing autocal timeout 1v8-pad drvs\n",
685de25fa5aSSowjanya Komatineni 				mmc_hostname(host->mmc));
68651b77c8eSAapo Vienamo 		autocal->pull_up_1v8_timeout = 0;
687de25fa5aSSowjanya Komatineni 	}
68851b77c8eSAapo Vienamo 
68951b77c8eSAapo Vienamo 	err = device_property_read_u32(host->mmc->parent,
69051b77c8eSAapo Vienamo 			"nvidia,pad-autocal-pull-down-offset-1v8-timeout",
6915ccf7f55SSowjanya Komatineni 			&autocal->pull_down_1v8_timeout);
692de25fa5aSSowjanya Komatineni 	if (err) {
693de25fa5aSSowjanya Komatineni 		if (!IS_ERR(tegra_host->pinctrl_state_1v8) &&
694de25fa5aSSowjanya Komatineni 			(tegra_host->pinctrl_state_1v8_drv == NULL))
695de25fa5aSSowjanya Komatineni 			pr_warn("%s: Missing autocal timeout 1v8-pad drvs\n",
696de25fa5aSSowjanya Komatineni 				mmc_hostname(host->mmc));
69751b77c8eSAapo Vienamo 		autocal->pull_down_1v8_timeout = 0;
698de25fa5aSSowjanya Komatineni 	}
699e5c63d91SLucas Stach }
700e5c63d91SLucas Stach 
70161dad40eSAapo Vienamo static void tegra_sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
70261dad40eSAapo Vienamo {
70361dad40eSAapo Vienamo 	struct sdhci_host *host = mmc_priv(mmc);
70461dad40eSAapo Vienamo 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
70561dad40eSAapo Vienamo 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
70661dad40eSAapo Vienamo 	ktime_t since_calib = ktime_sub(ktime_get(), tegra_host->last_calib);
70761dad40eSAapo Vienamo 
70861dad40eSAapo Vienamo 	/* 100 ms calibration interval is specified in the TRM */
70961dad40eSAapo Vienamo 	if (ktime_to_ms(since_calib) > 100) {
71061dad40eSAapo Vienamo 		tegra_sdhci_pad_autocalib(host);
71161dad40eSAapo Vienamo 		tegra_host->last_calib = ktime_get();
71261dad40eSAapo Vienamo 	}
71361dad40eSAapo Vienamo 
71461dad40eSAapo Vienamo 	sdhci_request(mmc, mrq);
71561dad40eSAapo Vienamo }
71661dad40eSAapo Vienamo 
717f5313aaaSAapo Vienamo static void tegra_sdhci_parse_tap_and_trim(struct sdhci_host *host)
71885c0da17SAapo Vienamo {
71985c0da17SAapo Vienamo 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
72085c0da17SAapo Vienamo 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
72185c0da17SAapo Vienamo 	int err;
72285c0da17SAapo Vienamo 
72385c0da17SAapo Vienamo 	err = device_property_read_u32(host->mmc->parent, "nvidia,default-tap",
72485c0da17SAapo Vienamo 				       &tegra_host->default_tap);
72585c0da17SAapo Vienamo 	if (err)
72685c0da17SAapo Vienamo 		tegra_host->default_tap = 0;
72785c0da17SAapo Vienamo 
72885c0da17SAapo Vienamo 	err = device_property_read_u32(host->mmc->parent, "nvidia,default-trim",
72985c0da17SAapo Vienamo 				       &tegra_host->default_trim);
73085c0da17SAapo Vienamo 	if (err)
73185c0da17SAapo Vienamo 		tegra_host->default_trim = 0;
732f5313aaaSAapo Vienamo 
733f5313aaaSAapo Vienamo 	err = device_property_read_u32(host->mmc->parent, "nvidia,dqs-trim",
734f5313aaaSAapo Vienamo 				       &tegra_host->dqs_trim);
735f5313aaaSAapo Vienamo 	if (err)
736f5313aaaSAapo Vienamo 		tegra_host->dqs_trim = 0x11;
73785c0da17SAapo Vienamo }
73885c0da17SAapo Vienamo 
7393c4019f9SSowjanya Komatineni static void tegra_sdhci_parse_dt(struct sdhci_host *host)
7403c4019f9SSowjanya Komatineni {
7413c4019f9SSowjanya Komatineni 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
7423c4019f9SSowjanya Komatineni 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
7433c4019f9SSowjanya Komatineni 
7443c4019f9SSowjanya Komatineni 	if (device_property_read_bool(host->mmc->parent, "supports-cqe"))
7453c4019f9SSowjanya Komatineni 		tegra_host->enable_hwcq = true;
7463c4019f9SSowjanya Komatineni 	else
7473c4019f9SSowjanya Komatineni 		tegra_host->enable_hwcq = false;
7483c4019f9SSowjanya Komatineni 
7493c4019f9SSowjanya Komatineni 	tegra_sdhci_parse_pad_autocal_dt(host);
7503c4019f9SSowjanya Komatineni 	tegra_sdhci_parse_tap_and_trim(host);
7513c4019f9SSowjanya Komatineni }
7523c4019f9SSowjanya Komatineni 
753a8e326a9SLucas Stach static void tegra_sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
754a8e326a9SLucas Stach {
755a8e326a9SLucas Stach 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
7560734e79cSJisheng Zhang 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
757a8e326a9SLucas Stach 	unsigned long host_clk;
758a8e326a9SLucas Stach 
759a8e326a9SLucas Stach 	if (!clock)
7603491b690SLucas Stach 		return sdhci_set_clock(host, clock);
761a8e326a9SLucas Stach 
76257d1654eSAapo Vienamo 	/*
76357d1654eSAapo Vienamo 	 * In DDR50/52 modes the Tegra SDHCI controllers require the SDHCI
76457d1654eSAapo Vienamo 	 * divider to be configured to divided the host clock by two. The SDHCI
76557d1654eSAapo Vienamo 	 * clock divider is calculated as part of sdhci_set_clock() by
76657d1654eSAapo Vienamo 	 * sdhci_calc_clk(). The divider is calculated from host->max_clk and
76757d1654eSAapo Vienamo 	 * the requested clock rate.
76857d1654eSAapo Vienamo 	 *
76957d1654eSAapo Vienamo 	 * By setting the host->max_clk to clock * 2 the divider calculation
77057d1654eSAapo Vienamo 	 * will always result in the correct value for DDR50/52 modes,
77157d1654eSAapo Vienamo 	 * regardless of clock rate rounding, which may happen if the value
77257d1654eSAapo Vienamo 	 * from clk_get_rate() is used.
77357d1654eSAapo Vienamo 	 */
774a8e326a9SLucas Stach 	host_clk = tegra_host->ddr_signaling ? clock * 2 : clock;
775a8e326a9SLucas Stach 	clk_set_rate(pltfm_host->clk, host_clk);
776ea8fc595SSowjanya Komatineni 	tegra_host->curr_clk_rate = host_clk;
77757d1654eSAapo Vienamo 	if (tegra_host->ddr_signaling)
77857d1654eSAapo Vienamo 		host->max_clk = host_clk;
77957d1654eSAapo Vienamo 	else
780a8e326a9SLucas Stach 		host->max_clk = clk_get_rate(pltfm_host->clk);
781a8e326a9SLucas Stach 
782e5c63d91SLucas Stach 	sdhci_set_clock(host, clock);
783e5c63d91SLucas Stach 
784e5c63d91SLucas Stach 	if (tegra_host->pad_calib_required) {
785e5c63d91SLucas Stach 		tegra_sdhci_pad_autocalib(host);
786e5c63d91SLucas Stach 		tegra_host->pad_calib_required = false;
787e5c63d91SLucas Stach 	}
788a8e326a9SLucas Stach }
789a8e326a9SLucas Stach 
79044350993SAapo Vienamo static unsigned int tegra_sdhci_get_max_clock(struct sdhci_host *host)
79144350993SAapo Vienamo {
79244350993SAapo Vienamo 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
79344350993SAapo Vienamo 
79444350993SAapo Vienamo 	return clk_round_rate(pltfm_host->clk, UINT_MAX);
79544350993SAapo Vienamo }
79644350993SAapo Vienamo 
797f5313aaaSAapo Vienamo static void tegra_sdhci_set_dqs_trim(struct sdhci_host *host, u8 trim)
798f5313aaaSAapo Vienamo {
799f5313aaaSAapo Vienamo 	u32 val;
800f5313aaaSAapo Vienamo 
801f5313aaaSAapo Vienamo 	val = sdhci_readl(host, SDHCI_TEGRA_VENDOR_CAP_OVERRIDES);
802f5313aaaSAapo Vienamo 	val &= ~SDHCI_TEGRA_CAP_OVERRIDES_DQS_TRIM_MASK;
803f5313aaaSAapo Vienamo 	val |= trim << SDHCI_TEGRA_CAP_OVERRIDES_DQS_TRIM_SHIFT;
804f5313aaaSAapo Vienamo 	sdhci_writel(host, val, SDHCI_TEGRA_VENDOR_CAP_OVERRIDES);
805f5313aaaSAapo Vienamo }
806f5313aaaSAapo Vienamo 
807bc5568bfSAapo Vienamo static void tegra_sdhci_hs400_dll_cal(struct sdhci_host *host)
808bc5568bfSAapo Vienamo {
809bc5568bfSAapo Vienamo 	u32 reg;
810bc5568bfSAapo Vienamo 	int err;
811bc5568bfSAapo Vienamo 
812bc5568bfSAapo Vienamo 	reg = sdhci_readl(host, SDHCI_TEGRA_VENDOR_DLLCAL_CFG);
813bc5568bfSAapo Vienamo 	reg |= SDHCI_TEGRA_DLLCAL_CALIBRATE;
814bc5568bfSAapo Vienamo 	sdhci_writel(host, reg, SDHCI_TEGRA_VENDOR_DLLCAL_CFG);
815bc5568bfSAapo Vienamo 
816bc5568bfSAapo Vienamo 	/* 1 ms sleep, 5 ms timeout */
817bc5568bfSAapo Vienamo 	err = readl_poll_timeout(host->ioaddr + SDHCI_TEGRA_VENDOR_DLLCAL_STA,
818bc5568bfSAapo Vienamo 				 reg, !(reg & SDHCI_TEGRA_DLLCAL_STA_ACTIVE),
819bc5568bfSAapo Vienamo 				 1000, 5000);
820bc5568bfSAapo Vienamo 	if (err)
821bc5568bfSAapo Vienamo 		dev_err(mmc_dev(host->mmc),
822bc5568bfSAapo Vienamo 			"HS400 delay line calibration timed out\n");
823bc5568bfSAapo Vienamo }
824bc5568bfSAapo Vienamo 
825ea8fc595SSowjanya Komatineni static void tegra_sdhci_tap_correction(struct sdhci_host *host, u8 thd_up,
826ea8fc595SSowjanya Komatineni 				       u8 thd_low, u8 fixed_tap)
827ea8fc595SSowjanya Komatineni {
828ea8fc595SSowjanya Komatineni 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
829ea8fc595SSowjanya Komatineni 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
830ea8fc595SSowjanya Komatineni 	u32 val, tun_status;
831ea8fc595SSowjanya Komatineni 	u8 word, bit, edge1, tap, window;
832ea8fc595SSowjanya Komatineni 	bool tap_result;
833ea8fc595SSowjanya Komatineni 	bool start_fail = false;
834ea8fc595SSowjanya Komatineni 	bool start_pass = false;
835ea8fc595SSowjanya Komatineni 	bool end_pass = false;
836ea8fc595SSowjanya Komatineni 	bool first_fail = false;
837ea8fc595SSowjanya Komatineni 	bool first_pass = false;
838ea8fc595SSowjanya Komatineni 	u8 start_pass_tap = 0;
839ea8fc595SSowjanya Komatineni 	u8 end_pass_tap = 0;
840ea8fc595SSowjanya Komatineni 	u8 first_fail_tap = 0;
841ea8fc595SSowjanya Komatineni 	u8 first_pass_tap = 0;
842ea8fc595SSowjanya Komatineni 	u8 total_tuning_words = host->tuning_loop_count / TUNING_WORD_BIT_SIZE;
843ea8fc595SSowjanya Komatineni 
844ea8fc595SSowjanya Komatineni 	/*
845ea8fc595SSowjanya Komatineni 	 * Read auto-tuned results and extract good valid passing window by
846ea8fc595SSowjanya Komatineni 	 * filtering out un-wanted bubble/partial/merged windows.
847ea8fc595SSowjanya Komatineni 	 */
848ea8fc595SSowjanya Komatineni 	for (word = 0; word < total_tuning_words; word++) {
849ea8fc595SSowjanya Komatineni 		val = sdhci_readl(host, SDHCI_VNDR_TUN_CTRL0_0);
850ea8fc595SSowjanya Komatineni 		val &= ~SDHCI_VNDR_TUN_CTRL0_TUN_WORD_SEL_MASK;
851ea8fc595SSowjanya Komatineni 		val |= word;
852ea8fc595SSowjanya Komatineni 		sdhci_writel(host, val, SDHCI_VNDR_TUN_CTRL0_0);
853ea8fc595SSowjanya Komatineni 		tun_status = sdhci_readl(host, SDHCI_TEGRA_VNDR_TUN_STATUS0);
854ea8fc595SSowjanya Komatineni 		bit = 0;
855ea8fc595SSowjanya Komatineni 		while (bit < TUNING_WORD_BIT_SIZE) {
856ea8fc595SSowjanya Komatineni 			tap = word * TUNING_WORD_BIT_SIZE + bit;
857ea8fc595SSowjanya Komatineni 			tap_result = tun_status & (1 << bit);
858ea8fc595SSowjanya Komatineni 			if (!tap_result && !start_fail) {
859ea8fc595SSowjanya Komatineni 				start_fail = true;
860ea8fc595SSowjanya Komatineni 				if (!first_fail) {
861ea8fc595SSowjanya Komatineni 					first_fail_tap = tap;
862ea8fc595SSowjanya Komatineni 					first_fail = true;
863ea8fc595SSowjanya Komatineni 				}
864ea8fc595SSowjanya Komatineni 
865ea8fc595SSowjanya Komatineni 			} else if (tap_result && start_fail && !start_pass) {
866ea8fc595SSowjanya Komatineni 				start_pass_tap = tap;
867ea8fc595SSowjanya Komatineni 				start_pass = true;
868ea8fc595SSowjanya Komatineni 				if (!first_pass) {
869ea8fc595SSowjanya Komatineni 					first_pass_tap = tap;
870ea8fc595SSowjanya Komatineni 					first_pass = true;
871ea8fc595SSowjanya Komatineni 				}
872ea8fc595SSowjanya Komatineni 
873ea8fc595SSowjanya Komatineni 			} else if (!tap_result && start_fail && start_pass &&
874ea8fc595SSowjanya Komatineni 				   !end_pass) {
875ea8fc595SSowjanya Komatineni 				end_pass_tap = tap - 1;
876ea8fc595SSowjanya Komatineni 				end_pass = true;
877ea8fc595SSowjanya Komatineni 			} else if (tap_result && start_pass && start_fail &&
878ea8fc595SSowjanya Komatineni 				   end_pass) {
879ea8fc595SSowjanya Komatineni 				window = end_pass_tap - start_pass_tap;
880ea8fc595SSowjanya Komatineni 				/* discard merged window and bubble window */
881ea8fc595SSowjanya Komatineni 				if (window >= thd_up || window < thd_low) {
882ea8fc595SSowjanya Komatineni 					start_pass_tap = tap;
883ea8fc595SSowjanya Komatineni 					end_pass = false;
884ea8fc595SSowjanya Komatineni 				} else {
885ea8fc595SSowjanya Komatineni 					/* set tap at middle of valid window */
886ea8fc595SSowjanya Komatineni 					tap = start_pass_tap + window / 2;
887ea8fc595SSowjanya Komatineni 					tegra_host->tuned_tap_delay = tap;
888ea8fc595SSowjanya Komatineni 					return;
889ea8fc595SSowjanya Komatineni 				}
890ea8fc595SSowjanya Komatineni 			}
891ea8fc595SSowjanya Komatineni 
892ea8fc595SSowjanya Komatineni 			bit++;
893ea8fc595SSowjanya Komatineni 		}
894ea8fc595SSowjanya Komatineni 	}
895ea8fc595SSowjanya Komatineni 
896ea8fc595SSowjanya Komatineni 	if (!first_fail) {
897d96dc68eSDan Carpenter 		WARN(1, "no edge detected, continue with hw tuned delay.\n");
898ea8fc595SSowjanya Komatineni 	} else if (first_pass) {
899ea8fc595SSowjanya Komatineni 		/* set tap location at fixed tap relative to the first edge */
900ea8fc595SSowjanya Komatineni 		edge1 = first_fail_tap + (first_pass_tap - first_fail_tap) / 2;
901ea8fc595SSowjanya Komatineni 		if (edge1 - 1 > fixed_tap)
902ea8fc595SSowjanya Komatineni 			tegra_host->tuned_tap_delay = edge1 - fixed_tap;
903ea8fc595SSowjanya Komatineni 		else
904ea8fc595SSowjanya Komatineni 			tegra_host->tuned_tap_delay = edge1 + fixed_tap;
905ea8fc595SSowjanya Komatineni 	}
906ea8fc595SSowjanya Komatineni }
907ea8fc595SSowjanya Komatineni 
908ea8fc595SSowjanya Komatineni static void tegra_sdhci_post_tuning(struct sdhci_host *host)
909ea8fc595SSowjanya Komatineni {
910ea8fc595SSowjanya Komatineni 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
911ea8fc595SSowjanya Komatineni 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
912ea8fc595SSowjanya Komatineni 	const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
913ea8fc595SSowjanya Komatineni 	u32 avg_tap_dly, val, min_tap_dly, max_tap_dly;
914ea8fc595SSowjanya Komatineni 	u8 fixed_tap, start_tap, end_tap, window_width;
915ea8fc595SSowjanya Komatineni 	u8 thdupper, thdlower;
916ea8fc595SSowjanya Komatineni 	u8 num_iter;
917ea8fc595SSowjanya Komatineni 	u32 clk_rate_mhz, period_ps, bestcase, worstcase;
918ea8fc595SSowjanya Komatineni 
919ea8fc595SSowjanya Komatineni 	/* retain HW tuned tap to use incase if no correction is needed */
920ea8fc595SSowjanya Komatineni 	val = sdhci_readl(host, SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
921ea8fc595SSowjanya Komatineni 	tegra_host->tuned_tap_delay = (val & SDHCI_CLOCK_CTRL_TAP_MASK) >>
922ea8fc595SSowjanya Komatineni 				      SDHCI_CLOCK_CTRL_TAP_SHIFT;
923ea8fc595SSowjanya Komatineni 	if (soc_data->min_tap_delay && soc_data->max_tap_delay) {
924ea8fc595SSowjanya Komatineni 		min_tap_dly = soc_data->min_tap_delay;
925ea8fc595SSowjanya Komatineni 		max_tap_dly = soc_data->max_tap_delay;
926ea8fc595SSowjanya Komatineni 		clk_rate_mhz = tegra_host->curr_clk_rate / USEC_PER_SEC;
927ea8fc595SSowjanya Komatineni 		period_ps = USEC_PER_SEC / clk_rate_mhz;
928ea8fc595SSowjanya Komatineni 		bestcase = period_ps / min_tap_dly;
929ea8fc595SSowjanya Komatineni 		worstcase = period_ps / max_tap_dly;
930ea8fc595SSowjanya Komatineni 		/*
931ea8fc595SSowjanya Komatineni 		 * Upper and Lower bound thresholds used to detect merged and
932ea8fc595SSowjanya Komatineni 		 * bubble windows
933ea8fc595SSowjanya Komatineni 		 */
934ea8fc595SSowjanya Komatineni 		thdupper = (2 * worstcase + bestcase) / 2;
935ea8fc595SSowjanya Komatineni 		thdlower = worstcase / 4;
936ea8fc595SSowjanya Komatineni 		/*
937ea8fc595SSowjanya Komatineni 		 * fixed tap is used when HW tuning result contains single edge
938ea8fc595SSowjanya Komatineni 		 * and tap is set at fixed tap delay relative to the first edge
939ea8fc595SSowjanya Komatineni 		 */
940ea8fc595SSowjanya Komatineni 		avg_tap_dly = (period_ps * 2) / (min_tap_dly + max_tap_dly);
941ea8fc595SSowjanya Komatineni 		fixed_tap = avg_tap_dly / 2;
942ea8fc595SSowjanya Komatineni 
943ea8fc595SSowjanya Komatineni 		val = sdhci_readl(host, SDHCI_TEGRA_VNDR_TUN_STATUS1);
944ea8fc595SSowjanya Komatineni 		start_tap = val & SDHCI_TEGRA_VNDR_TUN_STATUS1_TAP_MASK;
945ea8fc595SSowjanya Komatineni 		end_tap = (val >> SDHCI_TEGRA_VNDR_TUN_STATUS1_END_TAP_SHIFT) &
946ea8fc595SSowjanya Komatineni 			  SDHCI_TEGRA_VNDR_TUN_STATUS1_TAP_MASK;
947ea8fc595SSowjanya Komatineni 		window_width = end_tap - start_tap;
948ea8fc595SSowjanya Komatineni 		num_iter = host->tuning_loop_count;
949ea8fc595SSowjanya Komatineni 		/*
950ea8fc595SSowjanya Komatineni 		 * partial window includes edges of the tuning range.
951ea8fc595SSowjanya Komatineni 		 * merged window includes more taps so window width is higher
952ea8fc595SSowjanya Komatineni 		 * than upper threshold.
953ea8fc595SSowjanya Komatineni 		 */
954ea8fc595SSowjanya Komatineni 		if (start_tap == 0 || (end_tap == (num_iter - 1)) ||
955ea8fc595SSowjanya Komatineni 		    (end_tap == num_iter - 2) || window_width >= thdupper) {
956ea8fc595SSowjanya Komatineni 			pr_debug("%s: Apply tuning correction\n",
957ea8fc595SSowjanya Komatineni 				 mmc_hostname(host->mmc));
958ea8fc595SSowjanya Komatineni 			tegra_sdhci_tap_correction(host, thdupper, thdlower,
959ea8fc595SSowjanya Komatineni 						   fixed_tap);
960ea8fc595SSowjanya Komatineni 		}
961ea8fc595SSowjanya Komatineni 	}
962ea8fc595SSowjanya Komatineni 
963ea8fc595SSowjanya Komatineni 	tegra_sdhci_set_tap(host, tegra_host->tuned_tap_delay);
964ea8fc595SSowjanya Komatineni }
965ea8fc595SSowjanya Komatineni 
966ea8fc595SSowjanya Komatineni static int tegra_sdhci_execute_hw_tuning(struct mmc_host *mmc, u32 opcode)
967ea8fc595SSowjanya Komatineni {
968ea8fc595SSowjanya Komatineni 	struct sdhci_host *host = mmc_priv(mmc);
969ea8fc595SSowjanya Komatineni 	int err;
970ea8fc595SSowjanya Komatineni 
971ea8fc595SSowjanya Komatineni 	err = sdhci_execute_tuning(mmc, opcode);
972ea8fc595SSowjanya Komatineni 	if (!err && !host->tuning_err)
973ea8fc595SSowjanya Komatineni 		tegra_sdhci_post_tuning(host);
974ea8fc595SSowjanya Komatineni 
975ea8fc595SSowjanya Komatineni 	return err;
976ea8fc595SSowjanya Komatineni }
977ea8fc595SSowjanya Komatineni 
978c2c09678SAapo Vienamo static void tegra_sdhci_set_uhs_signaling(struct sdhci_host *host,
979c2c09678SAapo Vienamo 					  unsigned timing)
980c3c2384cSLucas Stach {
981d4501d8eSAapo Vienamo 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
982d4501d8eSAapo Vienamo 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
983c2c09678SAapo Vienamo 	bool set_default_tap = false;
984f5313aaaSAapo Vienamo 	bool set_dqs_trim = false;
985bc5568bfSAapo Vienamo 	bool do_hs400_dll_cal = false;
986ea8fc595SSowjanya Komatineni 	u8 iter = TRIES_256;
987ea8fc595SSowjanya Komatineni 	u32 val;
988c3c2384cSLucas Stach 
98992cd1667SSowjanya Komatineni 	tegra_host->ddr_signaling = false;
990c2c09678SAapo Vienamo 	switch (timing) {
991c2c09678SAapo Vienamo 	case MMC_TIMING_UHS_SDR50:
992ea8fc595SSowjanya Komatineni 		break;
993c2c09678SAapo Vienamo 	case MMC_TIMING_UHS_SDR104:
994c2c09678SAapo Vienamo 	case MMC_TIMING_MMC_HS200:
995c2c09678SAapo Vienamo 		/* Don't set default tap on tunable modes. */
996ea8fc595SSowjanya Komatineni 		iter = TRIES_128;
997c2c09678SAapo Vienamo 		break;
998f5313aaaSAapo Vienamo 	case MMC_TIMING_MMC_HS400:
999f5313aaaSAapo Vienamo 		set_dqs_trim = true;
1000bc5568bfSAapo Vienamo 		do_hs400_dll_cal = true;
1001ea8fc595SSowjanya Komatineni 		iter = TRIES_128;
1002f5313aaaSAapo Vienamo 		break;
1003c2c09678SAapo Vienamo 	case MMC_TIMING_MMC_DDR52:
1004c2c09678SAapo Vienamo 	case MMC_TIMING_UHS_DDR50:
1005c2c09678SAapo Vienamo 		tegra_host->ddr_signaling = true;
1006c2c09678SAapo Vienamo 		set_default_tap = true;
1007c2c09678SAapo Vienamo 		break;
1008c2c09678SAapo Vienamo 	default:
1009c2c09678SAapo Vienamo 		set_default_tap = true;
1010c2c09678SAapo Vienamo 		break;
1011d4501d8eSAapo Vienamo 	}
1012c2c09678SAapo Vienamo 
1013ea8fc595SSowjanya Komatineni 	val = sdhci_readl(host, SDHCI_VNDR_TUN_CTRL0_0);
1014ea8fc595SSowjanya Komatineni 	val &= ~(SDHCI_VNDR_TUN_CTRL0_TUN_ITER_MASK |
1015ea8fc595SSowjanya Komatineni 		 SDHCI_VNDR_TUN_CTRL0_START_TAP_VAL_MASK |
1016ea8fc595SSowjanya Komatineni 		 SDHCI_VNDR_TUN_CTRL0_MUL_M_MASK);
1017ea8fc595SSowjanya Komatineni 	val |= (iter << SDHCI_VNDR_TUN_CTRL0_TUN_ITER_SHIFT |
1018ea8fc595SSowjanya Komatineni 		0 << SDHCI_VNDR_TUN_CTRL0_START_TAP_VAL_SHIFT |
1019ea8fc595SSowjanya Komatineni 		1 << SDHCI_VNDR_TUN_CTRL0_MUL_M_SHIFT);
1020ea8fc595SSowjanya Komatineni 	sdhci_writel(host, val, SDHCI_VNDR_TUN_CTRL0_0);
1021ea8fc595SSowjanya Komatineni 	sdhci_writel(host, 0, SDHCI_TEGRA_VNDR_TUN_CTRL1_0);
1022ea8fc595SSowjanya Komatineni 
1023ea8fc595SSowjanya Komatineni 	host->tuning_loop_count = (iter == TRIES_128) ? 128 : 256;
1024ea8fc595SSowjanya Komatineni 
1025c2c09678SAapo Vienamo 	sdhci_set_uhs_signaling(host, timing);
1026c2c09678SAapo Vienamo 
1027c2c09678SAapo Vienamo 	tegra_sdhci_pad_autocalib(host);
1028c2c09678SAapo Vienamo 
1029ea8fc595SSowjanya Komatineni 	if (tegra_host->tuned_tap_delay && !set_default_tap)
1030ea8fc595SSowjanya Komatineni 		tegra_sdhci_set_tap(host, tegra_host->tuned_tap_delay);
1031ea8fc595SSowjanya Komatineni 	else
1032c2c09678SAapo Vienamo 		tegra_sdhci_set_tap(host, tegra_host->default_tap);
1033f5313aaaSAapo Vienamo 
1034f5313aaaSAapo Vienamo 	if (set_dqs_trim)
1035f5313aaaSAapo Vienamo 		tegra_sdhci_set_dqs_trim(host, tegra_host->dqs_trim);
1036bc5568bfSAapo Vienamo 
1037bc5568bfSAapo Vienamo 	if (do_hs400_dll_cal)
1038bc5568bfSAapo Vienamo 		tegra_sdhci_hs400_dll_cal(host);
1039c3c2384cSLucas Stach }
1040c3c2384cSLucas Stach 
1041c3c2384cSLucas Stach static int tegra_sdhci_execute_tuning(struct sdhci_host *host, u32 opcode)
1042c3c2384cSLucas Stach {
1043c3c2384cSLucas Stach 	unsigned int min, max;
1044c3c2384cSLucas Stach 
1045c3c2384cSLucas Stach 	/*
1046c3c2384cSLucas Stach 	 * Start search for minimum tap value at 10, as smaller values are
1047c3c2384cSLucas Stach 	 * may wrongly be reported as working but fail at higher speeds,
1048c3c2384cSLucas Stach 	 * according to the TRM.
1049c3c2384cSLucas Stach 	 */
1050c3c2384cSLucas Stach 	min = 10;
1051c3c2384cSLucas Stach 	while (min < 255) {
1052c3c2384cSLucas Stach 		tegra_sdhci_set_tap(host, min);
1053c3c2384cSLucas Stach 		if (!mmc_send_tuning(host->mmc, opcode, NULL))
1054c3c2384cSLucas Stach 			break;
1055c3c2384cSLucas Stach 		min++;
1056c3c2384cSLucas Stach 	}
1057c3c2384cSLucas Stach 
1058c3c2384cSLucas Stach 	/* Find the maximum tap value that still passes. */
1059c3c2384cSLucas Stach 	max = min + 1;
1060c3c2384cSLucas Stach 	while (max < 255) {
1061c3c2384cSLucas Stach 		tegra_sdhci_set_tap(host, max);
1062c3c2384cSLucas Stach 		if (mmc_send_tuning(host->mmc, opcode, NULL)) {
1063c3c2384cSLucas Stach 			max--;
1064c3c2384cSLucas Stach 			break;
1065c3c2384cSLucas Stach 		}
1066c3c2384cSLucas Stach 		max++;
1067c3c2384cSLucas Stach 	}
1068c3c2384cSLucas Stach 
1069c3c2384cSLucas Stach 	/* The TRM states the ideal tap value is at 75% in the passing range. */
1070c3c2384cSLucas Stach 	tegra_sdhci_set_tap(host, min + ((max - min) * 3 / 4));
1071c3c2384cSLucas Stach 
1072c3c2384cSLucas Stach 	return mmc_send_tuning(host->mmc, opcode, NULL);
1073c3c2384cSLucas Stach }
1074c3c2384cSLucas Stach 
107586ac2f8bSAapo Vienamo static int sdhci_tegra_start_signal_voltage_switch(struct mmc_host *mmc,
107686ac2f8bSAapo Vienamo 						   struct mmc_ios *ios)
107786ac2f8bSAapo Vienamo {
107886ac2f8bSAapo Vienamo 	struct sdhci_host *host = mmc_priv(mmc);
107944babea2SAapo Vienamo 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
108044babea2SAapo Vienamo 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
108186ac2f8bSAapo Vienamo 	int ret = 0;
108286ac2f8bSAapo Vienamo 
108386ac2f8bSAapo Vienamo 	if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330) {
1084de25fa5aSSowjanya Komatineni 		ret = tegra_sdhci_set_padctrl(host, ios->signal_voltage, true);
108586ac2f8bSAapo Vienamo 		if (ret < 0)
108686ac2f8bSAapo Vienamo 			return ret;
108786ac2f8bSAapo Vienamo 		ret = sdhci_start_signal_voltage_switch(mmc, ios);
108886ac2f8bSAapo Vienamo 	} else if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180) {
108986ac2f8bSAapo Vienamo 		ret = sdhci_start_signal_voltage_switch(mmc, ios);
109086ac2f8bSAapo Vienamo 		if (ret < 0)
109186ac2f8bSAapo Vienamo 			return ret;
1092de25fa5aSSowjanya Komatineni 		ret = tegra_sdhci_set_padctrl(host, ios->signal_voltage, true);
109386ac2f8bSAapo Vienamo 	}
109486ac2f8bSAapo Vienamo 
109544babea2SAapo Vienamo 	if (tegra_host->pad_calib_required)
109644babea2SAapo Vienamo 		tegra_sdhci_pad_autocalib(host);
109744babea2SAapo Vienamo 
109886ac2f8bSAapo Vienamo 	return ret;
109986ac2f8bSAapo Vienamo }
110086ac2f8bSAapo Vienamo 
110186ac2f8bSAapo Vienamo static int tegra_sdhci_init_pinctrl_info(struct device *dev,
110286ac2f8bSAapo Vienamo 					 struct sdhci_tegra *tegra_host)
110386ac2f8bSAapo Vienamo {
110486ac2f8bSAapo Vienamo 	tegra_host->pinctrl_sdmmc = devm_pinctrl_get(dev);
110586ac2f8bSAapo Vienamo 	if (IS_ERR(tegra_host->pinctrl_sdmmc)) {
110686ac2f8bSAapo Vienamo 		dev_dbg(dev, "No pinctrl info, err: %ld\n",
110786ac2f8bSAapo Vienamo 			PTR_ERR(tegra_host->pinctrl_sdmmc));
110886ac2f8bSAapo Vienamo 		return -1;
110986ac2f8bSAapo Vienamo 	}
111086ac2f8bSAapo Vienamo 
1111de25fa5aSSowjanya Komatineni 	tegra_host->pinctrl_state_1v8_drv = pinctrl_lookup_state(
1112de25fa5aSSowjanya Komatineni 				tegra_host->pinctrl_sdmmc, "sdmmc-1v8-drv");
1113de25fa5aSSowjanya Komatineni 	if (IS_ERR(tegra_host->pinctrl_state_1v8_drv)) {
1114de25fa5aSSowjanya Komatineni 		if (PTR_ERR(tegra_host->pinctrl_state_1v8_drv) == -ENODEV)
1115de25fa5aSSowjanya Komatineni 			tegra_host->pinctrl_state_1v8_drv = NULL;
1116de25fa5aSSowjanya Komatineni 	}
1117de25fa5aSSowjanya Komatineni 
1118de25fa5aSSowjanya Komatineni 	tegra_host->pinctrl_state_3v3_drv = pinctrl_lookup_state(
1119de25fa5aSSowjanya Komatineni 				tegra_host->pinctrl_sdmmc, "sdmmc-3v3-drv");
1120de25fa5aSSowjanya Komatineni 	if (IS_ERR(tegra_host->pinctrl_state_3v3_drv)) {
1121de25fa5aSSowjanya Komatineni 		if (PTR_ERR(tegra_host->pinctrl_state_3v3_drv) == -ENODEV)
1122de25fa5aSSowjanya Komatineni 			tegra_host->pinctrl_state_3v3_drv = NULL;
1123de25fa5aSSowjanya Komatineni 	}
1124de25fa5aSSowjanya Komatineni 
112586ac2f8bSAapo Vienamo 	tegra_host->pinctrl_state_3v3 =
112686ac2f8bSAapo Vienamo 		pinctrl_lookup_state(tegra_host->pinctrl_sdmmc, "sdmmc-3v3");
112786ac2f8bSAapo Vienamo 	if (IS_ERR(tegra_host->pinctrl_state_3v3)) {
112886ac2f8bSAapo Vienamo 		dev_warn(dev, "Missing 3.3V pad state, err: %ld\n",
112986ac2f8bSAapo Vienamo 			 PTR_ERR(tegra_host->pinctrl_state_3v3));
113086ac2f8bSAapo Vienamo 		return -1;
113186ac2f8bSAapo Vienamo 	}
113286ac2f8bSAapo Vienamo 
113386ac2f8bSAapo Vienamo 	tegra_host->pinctrl_state_1v8 =
113486ac2f8bSAapo Vienamo 		pinctrl_lookup_state(tegra_host->pinctrl_sdmmc, "sdmmc-1v8");
113586ac2f8bSAapo Vienamo 	if (IS_ERR(tegra_host->pinctrl_state_1v8)) {
113686ac2f8bSAapo Vienamo 		dev_warn(dev, "Missing 1.8V pad state, err: %ld\n",
1137e5378247SYueHaibing 			 PTR_ERR(tegra_host->pinctrl_state_1v8));
113886ac2f8bSAapo Vienamo 		return -1;
113986ac2f8bSAapo Vienamo 	}
114086ac2f8bSAapo Vienamo 
114186ac2f8bSAapo Vienamo 	tegra_host->pad_control_available = true;
114286ac2f8bSAapo Vienamo 
114386ac2f8bSAapo Vienamo 	return 0;
114486ac2f8bSAapo Vienamo }
114586ac2f8bSAapo Vienamo 
1146e5c63d91SLucas Stach static void tegra_sdhci_voltage_switch(struct sdhci_host *host)
1147e5c63d91SLucas Stach {
1148e5c63d91SLucas Stach 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1149e5c63d91SLucas Stach 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
1150e5c63d91SLucas Stach 	const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
1151e5c63d91SLucas Stach 
1152e5c63d91SLucas Stach 	if (soc_data->nvquirks & NVQUIRK_HAS_PADCALIB)
1153e5c63d91SLucas Stach 		tegra_host->pad_calib_required = true;
1154e5c63d91SLucas Stach }
1155e5c63d91SLucas Stach 
1156b7754428SSowjanya Komatineni static void tegra_cqhci_writel(struct cqhci_host *cq_host, u32 val, int reg)
1157b7754428SSowjanya Komatineni {
1158b7754428SSowjanya Komatineni 	struct mmc_host *mmc = cq_host->mmc;
1159b7754428SSowjanya Komatineni 	u8 ctrl;
1160b7754428SSowjanya Komatineni 	ktime_t timeout;
1161b7754428SSowjanya Komatineni 	bool timed_out;
1162b7754428SSowjanya Komatineni 
1163b7754428SSowjanya Komatineni 	/*
1164b7754428SSowjanya Komatineni 	 * During CQE resume/unhalt, CQHCI driver unhalts CQE prior to
1165b7754428SSowjanya Komatineni 	 * cqhci_host_ops enable where SDHCI DMA and BLOCK_SIZE registers need
1166b7754428SSowjanya Komatineni 	 * to be re-configured.
1167b7754428SSowjanya Komatineni 	 * Tegra CQHCI/SDHCI prevents write access to block size register when
1168b7754428SSowjanya Komatineni 	 * CQE is unhalted. So handling CQE resume sequence here to configure
1169b7754428SSowjanya Komatineni 	 * SDHCI block registers prior to exiting CQE halt state.
1170b7754428SSowjanya Komatineni 	 */
1171b7754428SSowjanya Komatineni 	if (reg == CQHCI_CTL && !(val & CQHCI_HALT) &&
1172b7754428SSowjanya Komatineni 	    cqhci_readl(cq_host, CQHCI_CTL) & CQHCI_HALT) {
1173b7754428SSowjanya Komatineni 		sdhci_cqe_enable(mmc);
1174b7754428SSowjanya Komatineni 		writel(val, cq_host->mmio + reg);
1175b7754428SSowjanya Komatineni 		timeout = ktime_add_us(ktime_get(), 50);
1176b7754428SSowjanya Komatineni 		while (1) {
1177b7754428SSowjanya Komatineni 			timed_out = ktime_compare(ktime_get(), timeout) > 0;
1178b7754428SSowjanya Komatineni 			ctrl = cqhci_readl(cq_host, CQHCI_CTL);
1179b7754428SSowjanya Komatineni 			if (!(ctrl & CQHCI_HALT) || timed_out)
1180b7754428SSowjanya Komatineni 				break;
1181b7754428SSowjanya Komatineni 		}
1182b7754428SSowjanya Komatineni 		/*
1183b7754428SSowjanya Komatineni 		 * CQE usually resumes very quick, but incase if Tegra CQE
1184b7754428SSowjanya Komatineni 		 * doesn't resume retry unhalt.
1185b7754428SSowjanya Komatineni 		 */
1186b7754428SSowjanya Komatineni 		if (timed_out)
1187b7754428SSowjanya Komatineni 			writel(val, cq_host->mmio + reg);
1188b7754428SSowjanya Komatineni 	} else {
1189b7754428SSowjanya Komatineni 		writel(val, cq_host->mmio + reg);
1190b7754428SSowjanya Komatineni 	}
1191b7754428SSowjanya Komatineni }
1192b7754428SSowjanya Komatineni 
1193c6e7ab90SSowjanya Komatineni static void sdhci_tegra_update_dcmd_desc(struct mmc_host *mmc,
1194c6e7ab90SSowjanya Komatineni 					 struct mmc_request *mrq, u64 *data)
1195c6e7ab90SSowjanya Komatineni {
1196c6e7ab90SSowjanya Komatineni 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(mmc_priv(mmc));
1197c6e7ab90SSowjanya Komatineni 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
1198c6e7ab90SSowjanya Komatineni 	const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
1199c6e7ab90SSowjanya Komatineni 
1200c6e7ab90SSowjanya Komatineni 	if (soc_data->nvquirks & NVQUIRK_CQHCI_DCMD_R1B_CMD_TIMING &&
1201c6e7ab90SSowjanya Komatineni 	    mrq->cmd->flags & MMC_RSP_R1B)
1202c6e7ab90SSowjanya Komatineni 		*data |= CQHCI_CMD_TIMING(1);
1203c6e7ab90SSowjanya Komatineni }
1204c6e7ab90SSowjanya Komatineni 
12053c4019f9SSowjanya Komatineni static void sdhci_tegra_cqe_enable(struct mmc_host *mmc)
12063c4019f9SSowjanya Komatineni {
12073c4019f9SSowjanya Komatineni 	struct cqhci_host *cq_host = mmc->cqe_private;
1208b7754428SSowjanya Komatineni 	u32 val;
12093c4019f9SSowjanya Komatineni 
12103c4019f9SSowjanya Komatineni 	/*
1211b7754428SSowjanya Komatineni 	 * Tegra CQHCI/SDMMC design prevents write access to sdhci block size
1212b7754428SSowjanya Komatineni 	 * register when CQE is enabled and unhalted.
1213b7754428SSowjanya Komatineni 	 * CQHCI driver enables CQE prior to activation, so disable CQE before
1214b7754428SSowjanya Komatineni 	 * programming block size in sdhci controller and enable it back.
12153c4019f9SSowjanya Komatineni 	 */
1216b7754428SSowjanya Komatineni 	if (!cq_host->activated) {
1217b7754428SSowjanya Komatineni 		val = cqhci_readl(cq_host, CQHCI_CFG);
1218b7754428SSowjanya Komatineni 		if (val & CQHCI_ENABLE)
1219b7754428SSowjanya Komatineni 			cqhci_writel(cq_host, (val & ~CQHCI_ENABLE),
1220b7754428SSowjanya Komatineni 				     CQHCI_CFG);
12213c4019f9SSowjanya Komatineni 		sdhci_cqe_enable(mmc);
1222b7754428SSowjanya Komatineni 		if (val & CQHCI_ENABLE)
1223b7754428SSowjanya Komatineni 			cqhci_writel(cq_host, val, CQHCI_CFG);
1224b7754428SSowjanya Komatineni 	}
12253c4019f9SSowjanya Komatineni 
1226b7754428SSowjanya Komatineni 	/*
1227b7754428SSowjanya Komatineni 	 * CMD CRC errors are seen sometimes with some eMMC devices when status
1228b7754428SSowjanya Komatineni 	 * command is sent during transfer of last data block which is the
1229b7754428SSowjanya Komatineni 	 * default case as send status command block counter (CBC) is 1.
1230b7754428SSowjanya Komatineni 	 * Recommended fix to set CBC to 0 allowing send status command only
1231b7754428SSowjanya Komatineni 	 * when data lines are idle.
1232b7754428SSowjanya Komatineni 	 */
1233b7754428SSowjanya Komatineni 	val = cqhci_readl(cq_host, CQHCI_SSC1);
1234b7754428SSowjanya Komatineni 	val &= ~CQHCI_SSC1_CBC_MASK;
1235b7754428SSowjanya Komatineni 	cqhci_writel(cq_host, val, CQHCI_SSC1);
12363c4019f9SSowjanya Komatineni }
12373c4019f9SSowjanya Komatineni 
12383c4019f9SSowjanya Komatineni static void sdhci_tegra_dumpregs(struct mmc_host *mmc)
12393c4019f9SSowjanya Komatineni {
12403c4019f9SSowjanya Komatineni 	sdhci_dumpregs(mmc_priv(mmc));
12413c4019f9SSowjanya Komatineni }
12423c4019f9SSowjanya Komatineni 
12433c4019f9SSowjanya Komatineni static u32 sdhci_tegra_cqhci_irq(struct sdhci_host *host, u32 intmask)
12443c4019f9SSowjanya Komatineni {
12453c4019f9SSowjanya Komatineni 	int cmd_error = 0;
12463c4019f9SSowjanya Komatineni 	int data_error = 0;
12473c4019f9SSowjanya Komatineni 
12483c4019f9SSowjanya Komatineni 	if (!sdhci_cqe_irq(host, intmask, &cmd_error, &data_error))
12493c4019f9SSowjanya Komatineni 		return intmask;
12503c4019f9SSowjanya Komatineni 
12513c4019f9SSowjanya Komatineni 	cqhci_irq(host->mmc, intmask, cmd_error, data_error);
12523c4019f9SSowjanya Komatineni 
12533c4019f9SSowjanya Komatineni 	return 0;
12543c4019f9SSowjanya Komatineni }
12553c4019f9SSowjanya Komatineni 
12565e958e4aSSowjanya Komatineni static void tegra_sdhci_set_timeout(struct sdhci_host *host,
12575e958e4aSSowjanya Komatineni 				    struct mmc_command *cmd)
12585e958e4aSSowjanya Komatineni {
12595e958e4aSSowjanya Komatineni 	u32 val;
12605e958e4aSSowjanya Komatineni 
12615e958e4aSSowjanya Komatineni 	/*
12625e958e4aSSowjanya Komatineni 	 * HW busy detection timeout is based on programmed data timeout
12635e958e4aSSowjanya Komatineni 	 * counter and maximum supported timeout is 11s which may not be
12645e958e4aSSowjanya Komatineni 	 * enough for long operations like cache flush, sleep awake, erase.
12655e958e4aSSowjanya Komatineni 	 *
12665e958e4aSSowjanya Komatineni 	 * ERASE_TIMEOUT_LIMIT bit of VENDOR_MISC_CTRL register allows
12675e958e4aSSowjanya Komatineni 	 * host controller to wait for busy state until the card is busy
12685e958e4aSSowjanya Komatineni 	 * without HW timeout.
12695e958e4aSSowjanya Komatineni 	 *
12705e958e4aSSowjanya Komatineni 	 * So, use infinite busy wait mode for operations that may take
12715e958e4aSSowjanya Komatineni 	 * more than maximum HW busy timeout of 11s otherwise use finite
12725e958e4aSSowjanya Komatineni 	 * busy wait mode.
12735e958e4aSSowjanya Komatineni 	 */
12745e958e4aSSowjanya Komatineni 	val = sdhci_readl(host, SDHCI_TEGRA_VENDOR_MISC_CTRL);
12755e958e4aSSowjanya Komatineni 	if (cmd && cmd->busy_timeout >= 11 * HZ)
12765e958e4aSSowjanya Komatineni 		val |= SDHCI_MISC_CTRL_ERASE_TIMEOUT_LIMIT;
12775e958e4aSSowjanya Komatineni 	else
12785e958e4aSSowjanya Komatineni 		val &= ~SDHCI_MISC_CTRL_ERASE_TIMEOUT_LIMIT;
12795e958e4aSSowjanya Komatineni 	sdhci_writel(host, val, SDHCI_TEGRA_VENDOR_MISC_CTRL);
12805e958e4aSSowjanya Komatineni 
12815e958e4aSSowjanya Komatineni 	__sdhci_set_timeout(host, cmd);
12825e958e4aSSowjanya Komatineni }
12835e958e4aSSowjanya Komatineni 
12843c4019f9SSowjanya Komatineni static const struct cqhci_host_ops sdhci_tegra_cqhci_ops = {
1285b7754428SSowjanya Komatineni 	.write_l    = tegra_cqhci_writel,
12863c4019f9SSowjanya Komatineni 	.enable	= sdhci_tegra_cqe_enable,
12873c4019f9SSowjanya Komatineni 	.disable = sdhci_cqe_disable,
12883c4019f9SSowjanya Komatineni 	.dumpregs = sdhci_tegra_dumpregs,
1289c6e7ab90SSowjanya Komatineni 	.update_dcmd_desc = sdhci_tegra_update_dcmd_desc,
12903c4019f9SSowjanya Komatineni };
12913c4019f9SSowjanya Komatineni 
1292b960bc44SNicolin Chen static int tegra_sdhci_set_dma_mask(struct sdhci_host *host)
1293b960bc44SNicolin Chen {
1294b960bc44SNicolin Chen 	struct sdhci_pltfm_host *platform = sdhci_priv(host);
1295b960bc44SNicolin Chen 	struct sdhci_tegra *tegra = sdhci_pltfm_priv(platform);
1296b960bc44SNicolin Chen 	const struct sdhci_tegra_soc_data *soc = tegra->soc_data;
1297b960bc44SNicolin Chen 	struct device *dev = mmc_dev(host->mmc);
1298b960bc44SNicolin Chen 
1299b960bc44SNicolin Chen 	if (soc->dma_mask)
1300b960bc44SNicolin Chen 		return dma_set_mask_and_coherent(dev, soc->dma_mask);
1301b960bc44SNicolin Chen 
1302b960bc44SNicolin Chen 	return 0;
1303b960bc44SNicolin Chen }
1304b960bc44SNicolin Chen 
1305c915568dSLars-Peter Clausen static const struct sdhci_ops tegra_sdhci_ops = {
13060f686ca9SDmitry Osipenko 	.get_ro     = tegra_sdhci_get_ro,
130785d6509dSShawn Guo 	.read_w     = tegra_sdhci_readw,
130885d6509dSShawn Guo 	.write_l    = tegra_sdhci_writel,
1309a8e326a9SLucas Stach 	.set_clock  = tegra_sdhci_set_clock,
1310b960bc44SNicolin Chen 	.set_dma_mask = tegra_sdhci_set_dma_mask,
131114b04c6aSMichał Mirosław 	.set_bus_width = sdhci_set_bus_width,
131203231f9bSRussell King 	.reset      = tegra_sdhci_reset,
1313c3c2384cSLucas Stach 	.platform_execute_tuning = tegra_sdhci_execute_tuning,
1314a8e326a9SLucas Stach 	.set_uhs_signaling = tegra_sdhci_set_uhs_signaling,
1315e5c63d91SLucas Stach 	.voltage_switch = tegra_sdhci_voltage_switch,
131644350993SAapo Vienamo 	.get_max_clock = tegra_sdhci_get_max_clock,
131785d6509dSShawn Guo };
131803d2bfc8SOlof Johansson 
13191db5eebfSLars-Peter Clausen static const struct sdhci_pltfm_data sdhci_tegra20_pdata = {
132085d6509dSShawn Guo 	.quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
132185d6509dSShawn Guo 		  SDHCI_QUIRK_SINGLE_POWER_WRITE |
132285d6509dSShawn Guo 		  SDHCI_QUIRK_NO_HISPD_BIT |
1323f9260355SAndrew Bresticker 		  SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
1324f9260355SAndrew Bresticker 		  SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
132585d6509dSShawn Guo 	.ops  = &tegra_sdhci_ops,
132685d6509dSShawn Guo };
132785d6509dSShawn Guo 
1328d49d19c2SThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra20 = {
13293e44a1a7SStephen Warren 	.pdata = &sdhci_tegra20_pdata,
1330b960bc44SNicolin Chen 	.dma_mask = DMA_BIT_MASK(32),
13313e44a1a7SStephen Warren 	.nvquirks = NVQUIRK_FORCE_SDHCI_SPEC_200 |
13323e44a1a7SStephen Warren 		    NVQUIRK_ENABLE_BLOCK_GAP_DET,
13333e44a1a7SStephen Warren };
13343e44a1a7SStephen Warren 
13351db5eebfSLars-Peter Clausen static const struct sdhci_pltfm_data sdhci_tegra30_pdata = {
13363e44a1a7SStephen Warren 	.quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
13373e44a1a7SStephen Warren 		  SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
13383e44a1a7SStephen Warren 		  SDHCI_QUIRK_SINGLE_POWER_WRITE |
13393e44a1a7SStephen Warren 		  SDHCI_QUIRK_NO_HISPD_BIT |
1340f9260355SAndrew Bresticker 		  SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
1341f9260355SAndrew Bresticker 		  SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
1342127407e3SStefan Agner 	.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
1343726df1d5SStefan Agner 		   SDHCI_QUIRK2_BROKEN_HS200 |
1344726df1d5SStefan Agner 		   /*
1345726df1d5SStefan Agner 		    * Auto-CMD23 leads to "Got command interrupt 0x00010000 even
1346726df1d5SStefan Agner 		    * though no command operation was in progress."
1347726df1d5SStefan Agner 		    *
1348726df1d5SStefan Agner 		    * The exact reason is unknown, as the same hardware seems
1349726df1d5SStefan Agner 		    * to support Auto CMD23 on a downstream 3.1 kernel.
1350726df1d5SStefan Agner 		    */
1351726df1d5SStefan Agner 		   SDHCI_QUIRK2_ACMD23_BROKEN,
13523e44a1a7SStephen Warren 	.ops  = &tegra_sdhci_ops,
13533e44a1a7SStephen Warren };
13543e44a1a7SStephen Warren 
1355d49d19c2SThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra30 = {
13563e44a1a7SStephen Warren 	.pdata = &sdhci_tegra30_pdata,
1357b960bc44SNicolin Chen 	.dma_mask = DMA_BIT_MASK(32),
13583145351aSAndrew Bresticker 	.nvquirks = NVQUIRK_ENABLE_SDHCI_SPEC_300 |
13597ad2ed1dSLucas Stach 		    NVQUIRK_ENABLE_SDR50 |
1360e5c63d91SLucas Stach 		    NVQUIRK_ENABLE_SDR104 |
1361e5c63d91SLucas Stach 		    NVQUIRK_HAS_PADCALIB,
13623e44a1a7SStephen Warren };
13633e44a1a7SStephen Warren 
136401df7ecdSRhyland Klein static const struct sdhci_ops tegra114_sdhci_ops = {
13650f686ca9SDmitry Osipenko 	.get_ro     = tegra_sdhci_get_ro,
136601df7ecdSRhyland Klein 	.read_w     = tegra_sdhci_readw,
136701df7ecdSRhyland Klein 	.write_w    = tegra_sdhci_writew,
136801df7ecdSRhyland Klein 	.write_l    = tegra_sdhci_writel,
1369a8e326a9SLucas Stach 	.set_clock  = tegra_sdhci_set_clock,
1370b960bc44SNicolin Chen 	.set_dma_mask = tegra_sdhci_set_dma_mask,
137114b04c6aSMichał Mirosław 	.set_bus_width = sdhci_set_bus_width,
137201df7ecdSRhyland Klein 	.reset      = tegra_sdhci_reset,
1373c3c2384cSLucas Stach 	.platform_execute_tuning = tegra_sdhci_execute_tuning,
1374a8e326a9SLucas Stach 	.set_uhs_signaling = tegra_sdhci_set_uhs_signaling,
1375e5c63d91SLucas Stach 	.voltage_switch = tegra_sdhci_voltage_switch,
137644350993SAapo Vienamo 	.get_max_clock = tegra_sdhci_get_max_clock,
137701df7ecdSRhyland Klein };
137801df7ecdSRhyland Klein 
13791db5eebfSLars-Peter Clausen static const struct sdhci_pltfm_data sdhci_tegra114_pdata = {
13805ebf2552SRhyland Klein 	.quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
13815ebf2552SRhyland Klein 		  SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
13825ebf2552SRhyland Klein 		  SDHCI_QUIRK_SINGLE_POWER_WRITE |
13835ebf2552SRhyland Klein 		  SDHCI_QUIRK_NO_HISPD_BIT |
1384f9260355SAndrew Bresticker 		  SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
1385f9260355SAndrew Bresticker 		  SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
1386a8e326a9SLucas Stach 	.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
138701df7ecdSRhyland Klein 	.ops  = &tegra114_sdhci_ops,
13885ebf2552SRhyland Klein };
13895ebf2552SRhyland Klein 
1390d49d19c2SThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra114 = {
13915ebf2552SRhyland Klein 	.pdata = &sdhci_tegra114_pdata,
1392b960bc44SNicolin Chen 	.dma_mask = DMA_BIT_MASK(32),
13937bf037d6SJon Hunter };
13947bf037d6SJon Hunter 
13954ae12588SThierry Reding static const struct sdhci_pltfm_data sdhci_tegra124_pdata = {
13964ae12588SThierry Reding 	.quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
13974ae12588SThierry Reding 		  SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
13984ae12588SThierry Reding 		  SDHCI_QUIRK_SINGLE_POWER_WRITE |
13994ae12588SThierry Reding 		  SDHCI_QUIRK_NO_HISPD_BIT |
14004ae12588SThierry Reding 		  SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
14014ae12588SThierry Reding 		  SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
1402b960bc44SNicolin Chen 	.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
14034ae12588SThierry Reding 	.ops  = &tegra114_sdhci_ops,
14044ae12588SThierry Reding };
14054ae12588SThierry Reding 
14064ae12588SThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra124 = {
14074ae12588SThierry Reding 	.pdata = &sdhci_tegra124_pdata,
1408b960bc44SNicolin Chen 	.dma_mask = DMA_BIT_MASK(34),
14094ae12588SThierry Reding };
14104ae12588SThierry Reding 
14111070e83aSAapo Vienamo static const struct sdhci_ops tegra210_sdhci_ops = {
14120f686ca9SDmitry Osipenko 	.get_ro     = tegra_sdhci_get_ro,
14131070e83aSAapo Vienamo 	.read_w     = tegra_sdhci_readw,
141438a284d9SAapo Vienamo 	.write_w    = tegra210_sdhci_writew,
14151070e83aSAapo Vienamo 	.write_l    = tegra_sdhci_writel,
14161070e83aSAapo Vienamo 	.set_clock  = tegra_sdhci_set_clock,
1417b960bc44SNicolin Chen 	.set_dma_mask = tegra_sdhci_set_dma_mask,
14181070e83aSAapo Vienamo 	.set_bus_width = sdhci_set_bus_width,
14191070e83aSAapo Vienamo 	.reset      = tegra_sdhci_reset,
14201070e83aSAapo Vienamo 	.set_uhs_signaling = tegra_sdhci_set_uhs_signaling,
14211070e83aSAapo Vienamo 	.voltage_switch = tegra_sdhci_voltage_switch,
14221070e83aSAapo Vienamo 	.get_max_clock = tegra_sdhci_get_max_clock,
14235e958e4aSSowjanya Komatineni 	.set_timeout = tegra_sdhci_set_timeout,
14241070e83aSAapo Vienamo };
14251070e83aSAapo Vienamo 
1426b5a84ecfSThierry Reding static const struct sdhci_pltfm_data sdhci_tegra210_pdata = {
1427b5a84ecfSThierry Reding 	.quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
1428b5a84ecfSThierry Reding 		  SDHCI_QUIRK_SINGLE_POWER_WRITE |
1429b5a84ecfSThierry Reding 		  SDHCI_QUIRK_NO_HISPD_BIT |
1430a8e326a9SLucas Stach 		  SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
1431a8e326a9SLucas Stach 		  SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
1432a8e326a9SLucas Stach 	.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
14331070e83aSAapo Vienamo 	.ops  = &tegra210_sdhci_ops,
1434b5a84ecfSThierry Reding };
1435b5a84ecfSThierry Reding 
1436b5a84ecfSThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra210 = {
1437b5a84ecfSThierry Reding 	.pdata = &sdhci_tegra210_pdata,
1438b960bc44SNicolin Chen 	.dma_mask = DMA_BIT_MASK(34),
1439d943f6e9SAapo Vienamo 	.nvquirks = NVQUIRK_NEEDS_PAD_CONTROL |
1440d4501d8eSAapo Vienamo 		    NVQUIRK_HAS_PADCALIB |
14413559d4a6SAapo Vienamo 		    NVQUIRK_DIS_CARD_CLK_CONFIG_TAP |
14423559d4a6SAapo Vienamo 		    NVQUIRK_ENABLE_SDR50 |
14438048822bSSowjanya Komatineni 		    NVQUIRK_ENABLE_SDR104 |
14448048822bSSowjanya Komatineni 		    NVQUIRK_HAS_TMCLK,
1445ea8fc595SSowjanya Komatineni 	.min_tap_delay = 106,
1446ea8fc595SSowjanya Komatineni 	.max_tap_delay = 185,
1447b5a84ecfSThierry Reding };
1448b5a84ecfSThierry Reding 
144938a284d9SAapo Vienamo static const struct sdhci_ops tegra186_sdhci_ops = {
14500f686ca9SDmitry Osipenko 	.get_ro     = tegra_sdhci_get_ro,
145138a284d9SAapo Vienamo 	.read_w     = tegra_sdhci_readw,
145238a284d9SAapo Vienamo 	.write_l    = tegra_sdhci_writel,
145338a284d9SAapo Vienamo 	.set_clock  = tegra_sdhci_set_clock,
1454b960bc44SNicolin Chen 	.set_dma_mask = tegra_sdhci_set_dma_mask,
145538a284d9SAapo Vienamo 	.set_bus_width = sdhci_set_bus_width,
145638a284d9SAapo Vienamo 	.reset      = tegra_sdhci_reset,
145738a284d9SAapo Vienamo 	.set_uhs_signaling = tegra_sdhci_set_uhs_signaling,
145838a284d9SAapo Vienamo 	.voltage_switch = tegra_sdhci_voltage_switch,
145938a284d9SAapo Vienamo 	.get_max_clock = tegra_sdhci_get_max_clock,
14603c4019f9SSowjanya Komatineni 	.irq = sdhci_tegra_cqhci_irq,
14615e958e4aSSowjanya Komatineni 	.set_timeout = tegra_sdhci_set_timeout,
146238a284d9SAapo Vienamo };
146338a284d9SAapo Vienamo 
14644346b7c7SThierry Reding static const struct sdhci_pltfm_data sdhci_tegra186_pdata = {
14654346b7c7SThierry Reding 	.quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
14664346b7c7SThierry Reding 		  SDHCI_QUIRK_SINGLE_POWER_WRITE |
14674346b7c7SThierry Reding 		  SDHCI_QUIRK_NO_HISPD_BIT |
14684346b7c7SThierry Reding 		  SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
14694346b7c7SThierry Reding 		  SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
1470b960bc44SNicolin Chen 	.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
147138a284d9SAapo Vienamo 	.ops  = &tegra186_sdhci_ops,
14724346b7c7SThierry Reding };
14734346b7c7SThierry Reding 
14744346b7c7SThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra186 = {
14754346b7c7SThierry Reding 	.pdata = &sdhci_tegra186_pdata,
1476b960bc44SNicolin Chen 	.dma_mask = DMA_BIT_MASK(40),
1477d943f6e9SAapo Vienamo 	.nvquirks = NVQUIRK_NEEDS_PAD_CONTROL |
1478d4501d8eSAapo Vienamo 		    NVQUIRK_HAS_PADCALIB |
14792ad50051SAapo Vienamo 		    NVQUIRK_DIS_CARD_CLK_CONFIG_TAP |
14802ad50051SAapo Vienamo 		    NVQUIRK_ENABLE_SDR50 |
1481c6e7ab90SSowjanya Komatineni 		    NVQUIRK_ENABLE_SDR104 |
14828048822bSSowjanya Komatineni 		    NVQUIRK_HAS_TMCLK |
1483c6e7ab90SSowjanya Komatineni 		    NVQUIRK_CQHCI_DCMD_R1B_CMD_TIMING,
1484ea8fc595SSowjanya Komatineni 	.min_tap_delay = 84,
1485ea8fc595SSowjanya Komatineni 	.max_tap_delay = 136,
1486ea8fc595SSowjanya Komatineni };
1487ea8fc595SSowjanya Komatineni 
1488ea8fc595SSowjanya Komatineni static const struct sdhci_tegra_soc_data soc_data_tegra194 = {
1489ea8fc595SSowjanya Komatineni 	.pdata = &sdhci_tegra186_pdata,
1490b960bc44SNicolin Chen 	.dma_mask = DMA_BIT_MASK(39),
1491ea8fc595SSowjanya Komatineni 	.nvquirks = NVQUIRK_NEEDS_PAD_CONTROL |
1492ea8fc595SSowjanya Komatineni 		    NVQUIRK_HAS_PADCALIB |
1493ea8fc595SSowjanya Komatineni 		    NVQUIRK_DIS_CARD_CLK_CONFIG_TAP |
1494ea8fc595SSowjanya Komatineni 		    NVQUIRK_ENABLE_SDR50 |
14958048822bSSowjanya Komatineni 		    NVQUIRK_ENABLE_SDR104 |
14968048822bSSowjanya Komatineni 		    NVQUIRK_HAS_TMCLK,
1497ea8fc595SSowjanya Komatineni 	.min_tap_delay = 96,
1498ea8fc595SSowjanya Komatineni 	.max_tap_delay = 139,
14994346b7c7SThierry Reding };
15004346b7c7SThierry Reding 
1501498d83e7SBill Pemberton static const struct of_device_id sdhci_tegra_dt_match[] = {
1502ea8fc595SSowjanya Komatineni 	{ .compatible = "nvidia,tegra194-sdhci", .data = &soc_data_tegra194 },
15034346b7c7SThierry Reding 	{ .compatible = "nvidia,tegra186-sdhci", .data = &soc_data_tegra186 },
1504b5a84ecfSThierry Reding 	{ .compatible = "nvidia,tegra210-sdhci", .data = &soc_data_tegra210 },
15054ae12588SThierry Reding 	{ .compatible = "nvidia,tegra124-sdhci", .data = &soc_data_tegra124 },
15065ebf2552SRhyland Klein 	{ .compatible = "nvidia,tegra114-sdhci", .data = &soc_data_tegra114 },
15073e44a1a7SStephen Warren 	{ .compatible = "nvidia,tegra30-sdhci", .data = &soc_data_tegra30 },
15083e44a1a7SStephen Warren 	{ .compatible = "nvidia,tegra20-sdhci", .data = &soc_data_tegra20 },
1509275173b2SGrant Likely 	{}
1510275173b2SGrant Likely };
1511e4404fabSArnd Bergmann MODULE_DEVICE_TABLE(of, sdhci_tegra_dt_match);
1512275173b2SGrant Likely 
15133c4019f9SSowjanya Komatineni static int sdhci_tegra_add_host(struct sdhci_host *host)
15143c4019f9SSowjanya Komatineni {
15153c4019f9SSowjanya Komatineni 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
15163c4019f9SSowjanya Komatineni 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
15173c4019f9SSowjanya Komatineni 	struct cqhci_host *cq_host;
15183c4019f9SSowjanya Komatineni 	bool dma64;
15193c4019f9SSowjanya Komatineni 	int ret;
15203c4019f9SSowjanya Komatineni 
15213c4019f9SSowjanya Komatineni 	if (!tegra_host->enable_hwcq)
15223c4019f9SSowjanya Komatineni 		return sdhci_add_host(host);
15233c4019f9SSowjanya Komatineni 
15243c4019f9SSowjanya Komatineni 	sdhci_enable_v4_mode(host);
15253c4019f9SSowjanya Komatineni 
15263c4019f9SSowjanya Komatineni 	ret = sdhci_setup_host(host);
15273c4019f9SSowjanya Komatineni 	if (ret)
15283c4019f9SSowjanya Komatineni 		return ret;
15293c4019f9SSowjanya Komatineni 
15303c4019f9SSowjanya Komatineni 	host->mmc->caps2 |= MMC_CAP2_CQE | MMC_CAP2_CQE_DCMD;
15313c4019f9SSowjanya Komatineni 
15323c4019f9SSowjanya Komatineni 	cq_host = devm_kzalloc(host->mmc->parent,
15333c4019f9SSowjanya Komatineni 				sizeof(*cq_host), GFP_KERNEL);
15343c4019f9SSowjanya Komatineni 	if (!cq_host) {
15353c4019f9SSowjanya Komatineni 		ret = -ENOMEM;
15363c4019f9SSowjanya Komatineni 		goto cleanup;
15373c4019f9SSowjanya Komatineni 	}
15383c4019f9SSowjanya Komatineni 
15393c4019f9SSowjanya Komatineni 	cq_host->mmio = host->ioaddr + SDHCI_TEGRA_CQE_BASE_ADDR;
15403c4019f9SSowjanya Komatineni 	cq_host->ops = &sdhci_tegra_cqhci_ops;
15413c4019f9SSowjanya Komatineni 
15423c4019f9SSowjanya Komatineni 	dma64 = host->flags & SDHCI_USE_64_BIT_DMA;
15433c4019f9SSowjanya Komatineni 	if (dma64)
15443c4019f9SSowjanya Komatineni 		cq_host->caps |= CQHCI_TASK_DESC_SZ_128;
15453c4019f9SSowjanya Komatineni 
15463c4019f9SSowjanya Komatineni 	ret = cqhci_init(cq_host, host->mmc, dma64);
15473c4019f9SSowjanya Komatineni 	if (ret)
15483c4019f9SSowjanya Komatineni 		goto cleanup;
15493c4019f9SSowjanya Komatineni 
15503c4019f9SSowjanya Komatineni 	ret = __sdhci_add_host(host);
15513c4019f9SSowjanya Komatineni 	if (ret)
15523c4019f9SSowjanya Komatineni 		goto cleanup;
15533c4019f9SSowjanya Komatineni 
15543c4019f9SSowjanya Komatineni 	return 0;
15553c4019f9SSowjanya Komatineni 
15563c4019f9SSowjanya Komatineni cleanup:
15573c4019f9SSowjanya Komatineni 	sdhci_cleanup_host(host);
15583c4019f9SSowjanya Komatineni 	return ret;
15593c4019f9SSowjanya Komatineni }
15603c4019f9SSowjanya Komatineni 
1561c3be1efdSBill Pemberton static int sdhci_tegra_probe(struct platform_device *pdev)
156203d2bfc8SOlof Johansson {
15633e44a1a7SStephen Warren 	const struct of_device_id *match;
15643e44a1a7SStephen Warren 	const struct sdhci_tegra_soc_data *soc_data;
15653e44a1a7SStephen Warren 	struct sdhci_host *host;
156685d6509dSShawn Guo 	struct sdhci_pltfm_host *pltfm_host;
15673e44a1a7SStephen Warren 	struct sdhci_tegra *tegra_host;
156803d2bfc8SOlof Johansson 	struct clk *clk;
156903d2bfc8SOlof Johansson 	int rc;
157003d2bfc8SOlof Johansson 
15713e44a1a7SStephen Warren 	match = of_match_device(sdhci_tegra_dt_match, &pdev->dev);
1572b37f9d98SJoseph Lo 	if (!match)
1573b37f9d98SJoseph Lo 		return -EINVAL;
15743e44a1a7SStephen Warren 	soc_data = match->data;
15753e44a1a7SStephen Warren 
15760734e79cSJisheng Zhang 	host = sdhci_pltfm_init(pdev, soc_data->pdata, sizeof(*tegra_host));
157785d6509dSShawn Guo 	if (IS_ERR(host))
157885d6509dSShawn Guo 		return PTR_ERR(host);
157985d6509dSShawn Guo 	pltfm_host = sdhci_priv(host);
158085d6509dSShawn Guo 
15810734e79cSJisheng Zhang 	tegra_host = sdhci_pltfm_priv(pltfm_host);
1582a8e326a9SLucas Stach 	tegra_host->ddr_signaling = false;
1583e5c63d91SLucas Stach 	tegra_host->pad_calib_required = false;
158486ac2f8bSAapo Vienamo 	tegra_host->pad_control_available = false;
15853e44a1a7SStephen Warren 	tegra_host->soc_data = soc_data;
1586275173b2SGrant Likely 
158786ac2f8bSAapo Vienamo 	if (soc_data->nvquirks & NVQUIRK_NEEDS_PAD_CONTROL) {
158886ac2f8bSAapo Vienamo 		rc = tegra_sdhci_init_pinctrl_info(&pdev->dev, tegra_host);
158986ac2f8bSAapo Vienamo 		if (rc == 0)
159086ac2f8bSAapo Vienamo 			host->mmc_host_ops.start_signal_voltage_switch =
159186ac2f8bSAapo Vienamo 				sdhci_tegra_start_signal_voltage_switch;
159286ac2f8bSAapo Vienamo 	}
159386ac2f8bSAapo Vienamo 
159461dad40eSAapo Vienamo 	/* Hook to periodically rerun pad calibration */
159561dad40eSAapo Vienamo 	if (soc_data->nvquirks & NVQUIRK_HAS_PADCALIB)
159661dad40eSAapo Vienamo 		host->mmc_host_ops.request = tegra_sdhci_request;
159761dad40eSAapo Vienamo 
1598dfc9700cSAapo Vienamo 	host->mmc_host_ops.hs400_enhanced_strobe =
1599dfc9700cSAapo Vienamo 			tegra_sdhci_hs400_enhanced_strobe;
1600dfc9700cSAapo Vienamo 
1601ea8fc595SSowjanya Komatineni 	if (!host->ops->platform_execute_tuning)
1602ea8fc595SSowjanya Komatineni 		host->mmc_host_ops.execute_tuning =
1603ea8fc595SSowjanya Komatineni 				tegra_sdhci_execute_hw_tuning;
1604ea8fc595SSowjanya Komatineni 
16052391b340SMylene JOSSERAND 	rc = mmc_of_parse(host->mmc);
160647caa84fSSimon Baatz 	if (rc)
160747caa84fSSimon Baatz 		goto err_parse_dt;
16080e786102SStephen Warren 
16097ad2ed1dSLucas Stach 	if (tegra_host->soc_data->nvquirks & NVQUIRK_ENABLE_DDR50)
1610c3c2384cSLucas Stach 		host->mmc->caps |= MMC_CAP_1_8V_DDR;
1611c3c2384cSLucas Stach 
1612ff124c31SSowjanya Komatineni 	/* HW busy detection is supported, but R1B responses are required. */
1613ff124c31SSowjanya Komatineni 	host->mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY | MMC_CAP_NEED_RSP_BUSY;
1614d2f8bfa4SUlf Hansson 
16153c4019f9SSowjanya Komatineni 	tegra_sdhci_parse_dt(host);
161685c0da17SAapo Vienamo 
16172391b340SMylene JOSSERAND 	tegra_host->power_gpio = devm_gpiod_get_optional(&pdev->dev, "power",
16182391b340SMylene JOSSERAND 							 GPIOD_OUT_HIGH);
16192391b340SMylene JOSSERAND 	if (IS_ERR(tegra_host->power_gpio)) {
16202391b340SMylene JOSSERAND 		rc = PTR_ERR(tegra_host->power_gpio);
162185d6509dSShawn Guo 		goto err_power_req;
162203d2bfc8SOlof Johansson 	}
162303d2bfc8SOlof Johansson 
16248048822bSSowjanya Komatineni 	/*
16258048822bSSowjanya Komatineni 	 * Tegra210 has a separate SDMMC_LEGACY_TM clock used for host
16268048822bSSowjanya Komatineni 	 * timeout clock and SW can choose TMCLK or SDCLK for hardware
16278048822bSSowjanya Komatineni 	 * data timeout through the bit USE_TMCLK_FOR_DATA_TIMEOUT of
16288048822bSSowjanya Komatineni 	 * the register SDHCI_TEGRA_VENDOR_SYS_SW_CTRL.
16298048822bSSowjanya Komatineni 	 *
16308048822bSSowjanya Komatineni 	 * USE_TMCLK_FOR_DATA_TIMEOUT bit default is set to 1 and SDMMC uses
16318048822bSSowjanya Komatineni 	 * 12Mhz TMCLK which is advertised in host capability register.
16328048822bSSowjanya Komatineni 	 * With TMCLK of 12Mhz provides maximum data timeout period that can
16338048822bSSowjanya Komatineni 	 * be achieved is 11s better than using SDCLK for data timeout.
16348048822bSSowjanya Komatineni 	 *
16358048822bSSowjanya Komatineni 	 * So, TMCLK is set to 12Mhz and kept enabled all the time on SoC's
16368048822bSSowjanya Komatineni 	 * supporting separate TMCLK.
16378048822bSSowjanya Komatineni 	 */
16388048822bSSowjanya Komatineni 
16398048822bSSowjanya Komatineni 	if (soc_data->nvquirks & NVQUIRK_HAS_TMCLK) {
16408048822bSSowjanya Komatineni 		clk = devm_clk_get(&pdev->dev, "tmclk");
16418048822bSSowjanya Komatineni 		if (IS_ERR(clk)) {
16428048822bSSowjanya Komatineni 			rc = PTR_ERR(clk);
16438048822bSSowjanya Komatineni 			if (rc == -EPROBE_DEFER)
16448048822bSSowjanya Komatineni 				goto err_power_req;
16458048822bSSowjanya Komatineni 
16468048822bSSowjanya Komatineni 			dev_warn(&pdev->dev, "failed to get tmclk: %d\n", rc);
16478048822bSSowjanya Komatineni 			clk = NULL;
16488048822bSSowjanya Komatineni 		}
16498048822bSSowjanya Komatineni 
16508048822bSSowjanya Komatineni 		clk_set_rate(clk, 12000000);
16518048822bSSowjanya Komatineni 		rc = clk_prepare_enable(clk);
16528048822bSSowjanya Komatineni 		if (rc) {
16538048822bSSowjanya Komatineni 			dev_err(&pdev->dev,
16548048822bSSowjanya Komatineni 				"failed to enable tmclk: %d\n", rc);
16558048822bSSowjanya Komatineni 			goto err_power_req;
16568048822bSSowjanya Komatineni 		}
16578048822bSSowjanya Komatineni 
16588048822bSSowjanya Komatineni 		tegra_host->tmclk = clk;
16598048822bSSowjanya Komatineni 	}
16608048822bSSowjanya Komatineni 
1661e4f79d9cSKevin Hao 	clk = devm_clk_get(mmc_dev(host->mmc), NULL);
166203d2bfc8SOlof Johansson 	if (IS_ERR(clk)) {
1663180a4665SKrzysztof Kozlowski 		rc = dev_err_probe(&pdev->dev, PTR_ERR(clk),
1664180a4665SKrzysztof Kozlowski 				   "failed to get clock\n");
166585d6509dSShawn Guo 		goto err_clk_get;
166603d2bfc8SOlof Johansson 	}
16671e674bc6SPrashant Gaikwad 	clk_prepare_enable(clk);
166803d2bfc8SOlof Johansson 	pltfm_host->clk = clk;
166903d2bfc8SOlof Johansson 
16702cd6c49dSPhilipp Zabel 	tegra_host->rst = devm_reset_control_get_exclusive(&pdev->dev,
16712cd6c49dSPhilipp Zabel 							   "sdhci");
167220567be9SThierry Reding 	if (IS_ERR(tegra_host->rst)) {
167320567be9SThierry Reding 		rc = PTR_ERR(tegra_host->rst);
167420567be9SThierry Reding 		dev_err(&pdev->dev, "failed to get reset control: %d\n", rc);
167520567be9SThierry Reding 		goto err_rst_get;
167620567be9SThierry Reding 	}
167720567be9SThierry Reding 
167820567be9SThierry Reding 	rc = reset_control_assert(tegra_host->rst);
167920567be9SThierry Reding 	if (rc)
168020567be9SThierry Reding 		goto err_rst_get;
168120567be9SThierry Reding 
168220567be9SThierry Reding 	usleep_range(2000, 4000);
168320567be9SThierry Reding 
168420567be9SThierry Reding 	rc = reset_control_deassert(tegra_host->rst);
168520567be9SThierry Reding 	if (rc)
168620567be9SThierry Reding 		goto err_rst_get;
168720567be9SThierry Reding 
168820567be9SThierry Reding 	usleep_range(2000, 4000);
168920567be9SThierry Reding 
16903c4019f9SSowjanya Komatineni 	rc = sdhci_tegra_add_host(host);
169185d6509dSShawn Guo 	if (rc)
169285d6509dSShawn Guo 		goto err_add_host;
169385d6509dSShawn Guo 
169403d2bfc8SOlof Johansson 	return 0;
169503d2bfc8SOlof Johansson 
169685d6509dSShawn Guo err_add_host:
169720567be9SThierry Reding 	reset_control_assert(tegra_host->rst);
169820567be9SThierry Reding err_rst_get:
16991e674bc6SPrashant Gaikwad 	clk_disable_unprepare(pltfm_host->clk);
170085d6509dSShawn Guo err_clk_get:
17018048822bSSowjanya Komatineni 	clk_disable_unprepare(tegra_host->tmclk);
170285d6509dSShawn Guo err_power_req:
170347caa84fSSimon Baatz err_parse_dt:
170485d6509dSShawn Guo 	sdhci_pltfm_free(pdev);
170503d2bfc8SOlof Johansson 	return rc;
170603d2bfc8SOlof Johansson }
170703d2bfc8SOlof Johansson 
170820567be9SThierry Reding static int sdhci_tegra_remove(struct platform_device *pdev)
170920567be9SThierry Reding {
171020567be9SThierry Reding 	struct sdhci_host *host = platform_get_drvdata(pdev);
171120567be9SThierry Reding 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
171220567be9SThierry Reding 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
171320567be9SThierry Reding 
171420567be9SThierry Reding 	sdhci_remove_host(host, 0);
171520567be9SThierry Reding 
171620567be9SThierry Reding 	reset_control_assert(tegra_host->rst);
171720567be9SThierry Reding 	usleep_range(2000, 4000);
171820567be9SThierry Reding 	clk_disable_unprepare(pltfm_host->clk);
17198048822bSSowjanya Komatineni 	clk_disable_unprepare(tegra_host->tmclk);
172020567be9SThierry Reding 
172120567be9SThierry Reding 	sdhci_pltfm_free(pdev);
172220567be9SThierry Reding 
172320567be9SThierry Reding 	return 0;
172420567be9SThierry Reding }
172520567be9SThierry Reding 
172671c733c4SSowjanya Komatineni #ifdef CONFIG_PM_SLEEP
172771c733c4SSowjanya Komatineni static int __maybe_unused sdhci_tegra_suspend(struct device *dev)
172871c733c4SSowjanya Komatineni {
172971c733c4SSowjanya Komatineni 	struct sdhci_host *host = dev_get_drvdata(dev);
173071c733c4SSowjanya Komatineni 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
173171c733c4SSowjanya Komatineni 	int ret;
173271c733c4SSowjanya Komatineni 
173371c733c4SSowjanya Komatineni 	if (host->mmc->caps2 & MMC_CAP2_CQE) {
173471c733c4SSowjanya Komatineni 		ret = cqhci_suspend(host->mmc);
173571c733c4SSowjanya Komatineni 		if (ret)
173671c733c4SSowjanya Komatineni 			return ret;
173771c733c4SSowjanya Komatineni 	}
173871c733c4SSowjanya Komatineni 
173971c733c4SSowjanya Komatineni 	ret = sdhci_suspend_host(host);
174071c733c4SSowjanya Komatineni 	if (ret) {
174171c733c4SSowjanya Komatineni 		cqhci_resume(host->mmc);
174271c733c4SSowjanya Komatineni 		return ret;
174371c733c4SSowjanya Komatineni 	}
174471c733c4SSowjanya Komatineni 
174571c733c4SSowjanya Komatineni 	clk_disable_unprepare(pltfm_host->clk);
174671c733c4SSowjanya Komatineni 	return 0;
174771c733c4SSowjanya Komatineni }
174871c733c4SSowjanya Komatineni 
174971c733c4SSowjanya Komatineni static int __maybe_unused sdhci_tegra_resume(struct device *dev)
175071c733c4SSowjanya Komatineni {
175171c733c4SSowjanya Komatineni 	struct sdhci_host *host = dev_get_drvdata(dev);
175271c733c4SSowjanya Komatineni 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
175371c733c4SSowjanya Komatineni 	int ret;
175471c733c4SSowjanya Komatineni 
175571c733c4SSowjanya Komatineni 	ret = clk_prepare_enable(pltfm_host->clk);
175671c733c4SSowjanya Komatineni 	if (ret)
175771c733c4SSowjanya Komatineni 		return ret;
175871c733c4SSowjanya Komatineni 
175971c733c4SSowjanya Komatineni 	ret = sdhci_resume_host(host);
176071c733c4SSowjanya Komatineni 	if (ret)
176171c733c4SSowjanya Komatineni 		goto disable_clk;
176271c733c4SSowjanya Komatineni 
176371c733c4SSowjanya Komatineni 	if (host->mmc->caps2 & MMC_CAP2_CQE) {
176471c733c4SSowjanya Komatineni 		ret = cqhci_resume(host->mmc);
176571c733c4SSowjanya Komatineni 		if (ret)
176671c733c4SSowjanya Komatineni 			goto suspend_host;
176771c733c4SSowjanya Komatineni 	}
176871c733c4SSowjanya Komatineni 
176971c733c4SSowjanya Komatineni 	return 0;
177071c733c4SSowjanya Komatineni 
177171c733c4SSowjanya Komatineni suspend_host:
177271c733c4SSowjanya Komatineni 	sdhci_suspend_host(host);
177371c733c4SSowjanya Komatineni disable_clk:
177471c733c4SSowjanya Komatineni 	clk_disable_unprepare(pltfm_host->clk);
177571c733c4SSowjanya Komatineni 	return ret;
177671c733c4SSowjanya Komatineni }
177771c733c4SSowjanya Komatineni #endif
177871c733c4SSowjanya Komatineni 
177971c733c4SSowjanya Komatineni static SIMPLE_DEV_PM_OPS(sdhci_tegra_dev_pm_ops, sdhci_tegra_suspend,
178071c733c4SSowjanya Komatineni 			 sdhci_tegra_resume);
178171c733c4SSowjanya Komatineni 
178285d6509dSShawn Guo static struct platform_driver sdhci_tegra_driver = {
178385d6509dSShawn Guo 	.driver		= {
178485d6509dSShawn Guo 		.name	= "sdhci-tegra",
178521b2cec6SDouglas Anderson 		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
1786275173b2SGrant Likely 		.of_match_table = sdhci_tegra_dt_match,
178771c733c4SSowjanya Komatineni 		.pm	= &sdhci_tegra_dev_pm_ops,
178885d6509dSShawn Guo 	},
178985d6509dSShawn Guo 	.probe		= sdhci_tegra_probe,
179020567be9SThierry Reding 	.remove		= sdhci_tegra_remove,
179103d2bfc8SOlof Johansson };
179203d2bfc8SOlof Johansson 
1793d1f81a64SAxel Lin module_platform_driver(sdhci_tegra_driver);
179485d6509dSShawn Guo 
179585d6509dSShawn Guo MODULE_DESCRIPTION("SDHCI driver for Tegra");
179685d6509dSShawn Guo MODULE_AUTHOR("Google, Inc.");
179785d6509dSShawn Guo MODULE_LICENSE("GPL v2");
1798