xref: /openbmc/linux/drivers/mmc/host/sdhci-tegra.c (revision ea8fc595)
103d2bfc8SOlof Johansson /*
203d2bfc8SOlof Johansson  * Copyright (C) 2010 Google, Inc.
303d2bfc8SOlof Johansson  *
403d2bfc8SOlof Johansson  * This software is licensed under the terms of the GNU General Public
503d2bfc8SOlof Johansson  * License version 2, as published by the Free Software Foundation, and
603d2bfc8SOlof Johansson  * may be copied, distributed, and modified under those terms.
703d2bfc8SOlof Johansson  *
803d2bfc8SOlof Johansson  * This program is distributed in the hope that it will be useful,
903d2bfc8SOlof Johansson  * but WITHOUT ANY WARRANTY; without even the implied warranty of
1003d2bfc8SOlof Johansson  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1103d2bfc8SOlof Johansson  * GNU General Public License for more details.
1203d2bfc8SOlof Johansson  *
1303d2bfc8SOlof Johansson  */
1403d2bfc8SOlof Johansson 
15e5c63d91SLucas Stach #include <linux/delay.h>
1603d2bfc8SOlof Johansson #include <linux/err.h>
1796547f5dSPaul Gortmaker #include <linux/module.h>
1803d2bfc8SOlof Johansson #include <linux/init.h>
19e7c07148SAapo Vienamo #include <linux/iopoll.h>
2003d2bfc8SOlof Johansson #include <linux/platform_device.h>
2103d2bfc8SOlof Johansson #include <linux/clk.h>
2203d2bfc8SOlof Johansson #include <linux/io.h>
2355cd65e4SStephen Warren #include <linux/of.h>
243e44a1a7SStephen Warren #include <linux/of_device.h>
2586ac2f8bSAapo Vienamo #include <linux/pinctrl/consumer.h>
2686ac2f8bSAapo Vienamo #include <linux/regulator/consumer.h>
2720567be9SThierry Reding #include <linux/reset.h>
2803d2bfc8SOlof Johansson #include <linux/mmc/card.h>
2903d2bfc8SOlof Johansson #include <linux/mmc/host.h>
30c3c2384cSLucas Stach #include <linux/mmc/mmc.h>
310aacd23fSJoseph Lo #include <linux/mmc/slot-gpio.h>
322391b340SMylene JOSSERAND #include <linux/gpio/consumer.h>
3361dad40eSAapo Vienamo #include <linux/ktime.h>
3403d2bfc8SOlof Johansson 
3503d2bfc8SOlof Johansson #include "sdhci-pltfm.h"
363c4019f9SSowjanya Komatineni #include "cqhci.h"
3703d2bfc8SOlof Johansson 
38ca5879d3SPavan Kunapuli /* Tegra SDHOST controller vendor register definitions */
3974cd42bcSLucas Stach #define SDHCI_TEGRA_VENDOR_CLOCK_CTRL			0x100
40c3c2384cSLucas Stach #define SDHCI_CLOCK_CTRL_TAP_MASK			0x00ff0000
41c3c2384cSLucas Stach #define SDHCI_CLOCK_CTRL_TAP_SHIFT			16
4241a0b8d7SAapo Vienamo #define SDHCI_CLOCK_CTRL_TRIM_MASK			0x1f000000
4341a0b8d7SAapo Vienamo #define SDHCI_CLOCK_CTRL_TRIM_SHIFT			24
44c3c2384cSLucas Stach #define SDHCI_CLOCK_CTRL_SDR50_TUNING_OVERRIDE		BIT(5)
4574cd42bcSLucas Stach #define SDHCI_CLOCK_CTRL_PADPIPE_CLKEN_OVERRIDE		BIT(3)
4674cd42bcSLucas Stach #define SDHCI_CLOCK_CTRL_SPI_MODE_CLKEN_OVERRIDE	BIT(2)
4774cd42bcSLucas Stach 
48dfc9700cSAapo Vienamo #define SDHCI_TEGRA_VENDOR_SYS_SW_CTRL			0x104
49dfc9700cSAapo Vienamo #define SDHCI_TEGRA_SYS_SW_CTRL_ENHANCED_STROBE		BIT(31)
50dfc9700cSAapo Vienamo 
51f5313aaaSAapo Vienamo #define SDHCI_TEGRA_VENDOR_CAP_OVERRIDES		0x10c
52f5313aaaSAapo Vienamo #define SDHCI_TEGRA_CAP_OVERRIDES_DQS_TRIM_MASK		0x00003f00
53f5313aaaSAapo Vienamo #define SDHCI_TEGRA_CAP_OVERRIDES_DQS_TRIM_SHIFT	8
54f5313aaaSAapo Vienamo 
55ca5879d3SPavan Kunapuli #define SDHCI_TEGRA_VENDOR_MISC_CTRL			0x120
563145351aSAndrew Bresticker #define SDHCI_MISC_CTRL_ENABLE_SDR104			0x8
573145351aSAndrew Bresticker #define SDHCI_MISC_CTRL_ENABLE_SDR50			0x10
58ca5879d3SPavan Kunapuli #define SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300		0x20
593145351aSAndrew Bresticker #define SDHCI_MISC_CTRL_ENABLE_DDR50			0x200
60ca5879d3SPavan Kunapuli 
61bc5568bfSAapo Vienamo #define SDHCI_TEGRA_VENDOR_DLLCAL_CFG			0x1b0
62bc5568bfSAapo Vienamo #define SDHCI_TEGRA_DLLCAL_CALIBRATE			BIT(31)
63bc5568bfSAapo Vienamo 
64bc5568bfSAapo Vienamo #define SDHCI_TEGRA_VENDOR_DLLCAL_STA			0x1bc
65bc5568bfSAapo Vienamo #define SDHCI_TEGRA_DLLCAL_STA_ACTIVE			BIT(31)
66bc5568bfSAapo Vienamo 
67d4501d8eSAapo Vienamo #define SDHCI_VNDR_TUN_CTRL0_0				0x1c0
68d4501d8eSAapo Vienamo #define SDHCI_VNDR_TUN_CTRL0_TUN_HW_TAP			0x20000
69ea8fc595SSowjanya Komatineni #define SDHCI_VNDR_TUN_CTRL0_START_TAP_VAL_MASK		0x03fc0000
70ea8fc595SSowjanya Komatineni #define SDHCI_VNDR_TUN_CTRL0_START_TAP_VAL_SHIFT	18
71ea8fc595SSowjanya Komatineni #define SDHCI_VNDR_TUN_CTRL0_MUL_M_MASK			0x00001fc0
72ea8fc595SSowjanya Komatineni #define SDHCI_VNDR_TUN_CTRL0_MUL_M_SHIFT		6
73ea8fc595SSowjanya Komatineni #define SDHCI_VNDR_TUN_CTRL0_TUN_ITER_MASK		0x000e000
74ea8fc595SSowjanya Komatineni #define SDHCI_VNDR_TUN_CTRL0_TUN_ITER_SHIFT		13
75ea8fc595SSowjanya Komatineni #define TRIES_128					2
76ea8fc595SSowjanya Komatineni #define TRIES_256					4
77ea8fc595SSowjanya Komatineni #define SDHCI_VNDR_TUN_CTRL0_TUN_WORD_SEL_MASK		0x7
78ea8fc595SSowjanya Komatineni 
79ea8fc595SSowjanya Komatineni #define SDHCI_TEGRA_VNDR_TUN_CTRL1_0			0x1c4
80ea8fc595SSowjanya Komatineni #define SDHCI_TEGRA_VNDR_TUN_STATUS0			0x1C8
81ea8fc595SSowjanya Komatineni #define SDHCI_TEGRA_VNDR_TUN_STATUS1			0x1CC
82ea8fc595SSowjanya Komatineni #define SDHCI_TEGRA_VNDR_TUN_STATUS1_TAP_MASK		0xFF
83ea8fc595SSowjanya Komatineni #define SDHCI_TEGRA_VNDR_TUN_STATUS1_END_TAP_SHIFT	0x8
84ea8fc595SSowjanya Komatineni #define TUNING_WORD_BIT_SIZE				32
85d4501d8eSAapo Vienamo 
86e5c63d91SLucas Stach #define SDHCI_TEGRA_AUTO_CAL_CONFIG			0x1e4
87e5c63d91SLucas Stach #define SDHCI_AUTO_CAL_START				BIT(31)
88e5c63d91SLucas Stach #define SDHCI_AUTO_CAL_ENABLE				BIT(29)
8951b77c8eSAapo Vienamo #define SDHCI_AUTO_CAL_PDPU_OFFSET_MASK			0x0000ffff
90e5c63d91SLucas Stach 
919d548f11SAapo Vienamo #define SDHCI_TEGRA_SDMEM_COMP_PADCTRL			0x1e0
929d548f11SAapo Vienamo #define SDHCI_TEGRA_SDMEM_COMP_PADCTRL_VREF_SEL_MASK	0x0000000f
939d548f11SAapo Vienamo #define SDHCI_TEGRA_SDMEM_COMP_PADCTRL_VREF_SEL_VAL	0x7
94212b0cf1SAapo Vienamo #define SDHCI_TEGRA_SDMEM_COMP_PADCTRL_E_INPUT_E_PWRD	BIT(31)
95de25fa5aSSowjanya Komatineni #define SDHCI_COMP_PADCTRL_DRVUPDN_OFFSET_MASK		0x07FFF000
969d548f11SAapo Vienamo 
97e7c07148SAapo Vienamo #define SDHCI_TEGRA_AUTO_CAL_STATUS			0x1ec
98e7c07148SAapo Vienamo #define SDHCI_TEGRA_AUTO_CAL_ACTIVE			BIT(31)
99e7c07148SAapo Vienamo 
1003e44a1a7SStephen Warren #define NVQUIRK_FORCE_SDHCI_SPEC_200			BIT(0)
1013e44a1a7SStephen Warren #define NVQUIRK_ENABLE_BLOCK_GAP_DET			BIT(1)
102ca5879d3SPavan Kunapuli #define NVQUIRK_ENABLE_SDHCI_SPEC_300			BIT(2)
1037ad2ed1dSLucas Stach #define NVQUIRK_ENABLE_SDR50				BIT(3)
1047ad2ed1dSLucas Stach #define NVQUIRK_ENABLE_SDR104				BIT(4)
1057ad2ed1dSLucas Stach #define NVQUIRK_ENABLE_DDR50				BIT(5)
106e5c63d91SLucas Stach #define NVQUIRK_HAS_PADCALIB				BIT(6)
10786ac2f8bSAapo Vienamo #define NVQUIRK_NEEDS_PAD_CONTROL			BIT(7)
108d4501d8eSAapo Vienamo #define NVQUIRK_DIS_CARD_CLK_CONFIG_TAP			BIT(8)
1093e44a1a7SStephen Warren 
1103c4019f9SSowjanya Komatineni /* SDMMC CQE Base Address for Tegra Host Ver 4.1 and Higher */
1113c4019f9SSowjanya Komatineni #define SDHCI_TEGRA_CQE_BASE_ADDR			0xF000
1123c4019f9SSowjanya Komatineni 
1133e44a1a7SStephen Warren struct sdhci_tegra_soc_data {
1141db5eebfSLars-Peter Clausen 	const struct sdhci_pltfm_data *pdata;
1153e44a1a7SStephen Warren 	u32 nvquirks;
116ea8fc595SSowjanya Komatineni 	u8 min_tap_delay;
117ea8fc595SSowjanya Komatineni 	u8 max_tap_delay;
1183e44a1a7SStephen Warren };
1193e44a1a7SStephen Warren 
12051b77c8eSAapo Vienamo /* Magic pull up and pull down pad calibration offsets */
12151b77c8eSAapo Vienamo struct sdhci_tegra_autocal_offsets {
12251b77c8eSAapo Vienamo 	u32 pull_up_3v3;
12351b77c8eSAapo Vienamo 	u32 pull_down_3v3;
12451b77c8eSAapo Vienamo 	u32 pull_up_3v3_timeout;
12551b77c8eSAapo Vienamo 	u32 pull_down_3v3_timeout;
12651b77c8eSAapo Vienamo 	u32 pull_up_1v8;
12751b77c8eSAapo Vienamo 	u32 pull_down_1v8;
12851b77c8eSAapo Vienamo 	u32 pull_up_1v8_timeout;
12951b77c8eSAapo Vienamo 	u32 pull_down_1v8_timeout;
13051b77c8eSAapo Vienamo 	u32 pull_up_sdr104;
13151b77c8eSAapo Vienamo 	u32 pull_down_sdr104;
13251b77c8eSAapo Vienamo 	u32 pull_up_hs400;
13351b77c8eSAapo Vienamo 	u32 pull_down_hs400;
13451b77c8eSAapo Vienamo };
13551b77c8eSAapo Vienamo 
1363e44a1a7SStephen Warren struct sdhci_tegra {
1373e44a1a7SStephen Warren 	const struct sdhci_tegra_soc_data *soc_data;
1382391b340SMylene JOSSERAND 	struct gpio_desc *power_gpio;
139a8e326a9SLucas Stach 	bool ddr_signaling;
140e5c63d91SLucas Stach 	bool pad_calib_required;
14186ac2f8bSAapo Vienamo 	bool pad_control_available;
14220567be9SThierry Reding 
14320567be9SThierry Reding 	struct reset_control *rst;
14486ac2f8bSAapo Vienamo 	struct pinctrl *pinctrl_sdmmc;
14586ac2f8bSAapo Vienamo 	struct pinctrl_state *pinctrl_state_3v3;
14686ac2f8bSAapo Vienamo 	struct pinctrl_state *pinctrl_state_1v8;
147de25fa5aSSowjanya Komatineni 	struct pinctrl_state *pinctrl_state_3v3_drv;
148de25fa5aSSowjanya Komatineni 	struct pinctrl_state *pinctrl_state_1v8_drv;
14951b77c8eSAapo Vienamo 
15051b77c8eSAapo Vienamo 	struct sdhci_tegra_autocal_offsets autocal_offsets;
15161dad40eSAapo Vienamo 	ktime_t last_calib;
15285c0da17SAapo Vienamo 
15385c0da17SAapo Vienamo 	u32 default_tap;
15485c0da17SAapo Vienamo 	u32 default_trim;
155f5313aaaSAapo Vienamo 	u32 dqs_trim;
1563c4019f9SSowjanya Komatineni 	bool enable_hwcq;
157ea8fc595SSowjanya Komatineni 	unsigned long curr_clk_rate;
158ea8fc595SSowjanya Komatineni 	u8 tuned_tap_delay;
1593e44a1a7SStephen Warren };
1603e44a1a7SStephen Warren 
16103d2bfc8SOlof Johansson static u16 tegra_sdhci_readw(struct sdhci_host *host, int reg)
16203d2bfc8SOlof Johansson {
1633e44a1a7SStephen Warren 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1640734e79cSJisheng Zhang 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
1653e44a1a7SStephen Warren 	const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
1663e44a1a7SStephen Warren 
1673e44a1a7SStephen Warren 	if (unlikely((soc_data->nvquirks & NVQUIRK_FORCE_SDHCI_SPEC_200) &&
1683e44a1a7SStephen Warren 			(reg == SDHCI_HOST_VERSION))) {
16903d2bfc8SOlof Johansson 		/* Erratum: Version register is invalid in HW. */
17003d2bfc8SOlof Johansson 		return SDHCI_SPEC_200;
17103d2bfc8SOlof Johansson 	}
17203d2bfc8SOlof Johansson 
17303d2bfc8SOlof Johansson 	return readw(host->ioaddr + reg);
17403d2bfc8SOlof Johansson }
17503d2bfc8SOlof Johansson 
176352ee868SPavan Kunapuli static void tegra_sdhci_writew(struct sdhci_host *host, u16 val, int reg)
177352ee868SPavan Kunapuli {
178352ee868SPavan Kunapuli 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
179352ee868SPavan Kunapuli 
180352ee868SPavan Kunapuli 	switch (reg) {
181352ee868SPavan Kunapuli 	case SDHCI_TRANSFER_MODE:
182352ee868SPavan Kunapuli 		/*
183352ee868SPavan Kunapuli 		 * Postpone this write, we must do it together with a
184352ee868SPavan Kunapuli 		 * command write that is down below.
185352ee868SPavan Kunapuli 		 */
186352ee868SPavan Kunapuli 		pltfm_host->xfer_mode_shadow = val;
187352ee868SPavan Kunapuli 		return;
188352ee868SPavan Kunapuli 	case SDHCI_COMMAND:
189352ee868SPavan Kunapuli 		writel((val << 16) | pltfm_host->xfer_mode_shadow,
190352ee868SPavan Kunapuli 			host->ioaddr + SDHCI_TRANSFER_MODE);
191352ee868SPavan Kunapuli 		return;
192352ee868SPavan Kunapuli 	}
193352ee868SPavan Kunapuli 
194352ee868SPavan Kunapuli 	writew(val, host->ioaddr + reg);
195352ee868SPavan Kunapuli }
196352ee868SPavan Kunapuli 
19703d2bfc8SOlof Johansson static void tegra_sdhci_writel(struct sdhci_host *host, u32 val, int reg)
19803d2bfc8SOlof Johansson {
1993e44a1a7SStephen Warren 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
2000734e79cSJisheng Zhang 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
2013e44a1a7SStephen Warren 	const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
2023e44a1a7SStephen Warren 
20303d2bfc8SOlof Johansson 	/* Seems like we're getting spurious timeout and crc errors, so
20403d2bfc8SOlof Johansson 	 * disable signalling of them. In case of real errors software
20503d2bfc8SOlof Johansson 	 * timers should take care of eventually detecting them.
20603d2bfc8SOlof Johansson 	 */
20703d2bfc8SOlof Johansson 	if (unlikely(reg == SDHCI_SIGNAL_ENABLE))
20803d2bfc8SOlof Johansson 		val &= ~(SDHCI_INT_TIMEOUT|SDHCI_INT_CRC);
20903d2bfc8SOlof Johansson 
21003d2bfc8SOlof Johansson 	writel(val, host->ioaddr + reg);
21103d2bfc8SOlof Johansson 
2123e44a1a7SStephen Warren 	if (unlikely((soc_data->nvquirks & NVQUIRK_ENABLE_BLOCK_GAP_DET) &&
2133e44a1a7SStephen Warren 			(reg == SDHCI_INT_ENABLE))) {
21403d2bfc8SOlof Johansson 		/* Erratum: Must enable block gap interrupt detection */
21503d2bfc8SOlof Johansson 		u8 gap_ctrl = readb(host->ioaddr + SDHCI_BLOCK_GAP_CONTROL);
21603d2bfc8SOlof Johansson 		if (val & SDHCI_INT_CARD_INT)
21703d2bfc8SOlof Johansson 			gap_ctrl |= 0x8;
21803d2bfc8SOlof Johansson 		else
21903d2bfc8SOlof Johansson 			gap_ctrl &= ~0x8;
22003d2bfc8SOlof Johansson 		writeb(gap_ctrl, host->ioaddr + SDHCI_BLOCK_GAP_CONTROL);
22103d2bfc8SOlof Johansson 	}
22203d2bfc8SOlof Johansson }
22303d2bfc8SOlof Johansson 
22438a284d9SAapo Vienamo static bool tegra_sdhci_configure_card_clk(struct sdhci_host *host, bool enable)
22538a284d9SAapo Vienamo {
22638a284d9SAapo Vienamo 	bool status;
22738a284d9SAapo Vienamo 	u32 reg;
22838a284d9SAapo Vienamo 
22938a284d9SAapo Vienamo 	reg = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
23038a284d9SAapo Vienamo 	status = !!(reg & SDHCI_CLOCK_CARD_EN);
23138a284d9SAapo Vienamo 
23238a284d9SAapo Vienamo 	if (status == enable)
23338a284d9SAapo Vienamo 		return status;
23438a284d9SAapo Vienamo 
23538a284d9SAapo Vienamo 	if (enable)
23638a284d9SAapo Vienamo 		reg |= SDHCI_CLOCK_CARD_EN;
23738a284d9SAapo Vienamo 	else
23838a284d9SAapo Vienamo 		reg &= ~SDHCI_CLOCK_CARD_EN;
23938a284d9SAapo Vienamo 
24038a284d9SAapo Vienamo 	sdhci_writew(host, reg, SDHCI_CLOCK_CONTROL);
24138a284d9SAapo Vienamo 
24238a284d9SAapo Vienamo 	return status;
24338a284d9SAapo Vienamo }
24438a284d9SAapo Vienamo 
24538a284d9SAapo Vienamo static void tegra210_sdhci_writew(struct sdhci_host *host, u16 val, int reg)
24638a284d9SAapo Vienamo {
24738a284d9SAapo Vienamo 	bool is_tuning_cmd = 0;
24838a284d9SAapo Vienamo 	bool clk_enabled;
24938a284d9SAapo Vienamo 	u8 cmd;
25038a284d9SAapo Vienamo 
25138a284d9SAapo Vienamo 	if (reg == SDHCI_COMMAND) {
25238a284d9SAapo Vienamo 		cmd = SDHCI_GET_CMD(val);
25338a284d9SAapo Vienamo 		is_tuning_cmd = cmd == MMC_SEND_TUNING_BLOCK ||
25438a284d9SAapo Vienamo 				cmd == MMC_SEND_TUNING_BLOCK_HS200;
25538a284d9SAapo Vienamo 	}
25638a284d9SAapo Vienamo 
25738a284d9SAapo Vienamo 	if (is_tuning_cmd)
25838a284d9SAapo Vienamo 		clk_enabled = tegra_sdhci_configure_card_clk(host, 0);
25938a284d9SAapo Vienamo 
26038a284d9SAapo Vienamo 	writew(val, host->ioaddr + reg);
26138a284d9SAapo Vienamo 
26238a284d9SAapo Vienamo 	if (is_tuning_cmd) {
26338a284d9SAapo Vienamo 		udelay(1);
264ea8fc595SSowjanya Komatineni 		sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
26538a284d9SAapo Vienamo 		tegra_sdhci_configure_card_clk(host, clk_enabled);
26638a284d9SAapo Vienamo 	}
26738a284d9SAapo Vienamo }
26838a284d9SAapo Vienamo 
26986ac2f8bSAapo Vienamo static bool tegra_sdhci_is_pad_and_regulator_valid(struct sdhci_host *host)
27086ac2f8bSAapo Vienamo {
27186ac2f8bSAapo Vienamo 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
27286ac2f8bSAapo Vienamo 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
27386ac2f8bSAapo Vienamo 	int has_1v8, has_3v3;
27486ac2f8bSAapo Vienamo 
27586ac2f8bSAapo Vienamo 	/*
27686ac2f8bSAapo Vienamo 	 * The SoCs which have NVQUIRK_NEEDS_PAD_CONTROL require software pad
27786ac2f8bSAapo Vienamo 	 * voltage configuration in order to perform voltage switching. This
27886ac2f8bSAapo Vienamo 	 * means that valid pinctrl info is required on SDHCI instances capable
27986ac2f8bSAapo Vienamo 	 * of performing voltage switching. Whether or not an SDHCI instance is
28086ac2f8bSAapo Vienamo 	 * capable of voltage switching is determined based on the regulator.
28186ac2f8bSAapo Vienamo 	 */
28286ac2f8bSAapo Vienamo 
28386ac2f8bSAapo Vienamo 	if (!(tegra_host->soc_data->nvquirks & NVQUIRK_NEEDS_PAD_CONTROL))
28486ac2f8bSAapo Vienamo 		return true;
28586ac2f8bSAapo Vienamo 
28686ac2f8bSAapo Vienamo 	if (IS_ERR(host->mmc->supply.vqmmc))
28786ac2f8bSAapo Vienamo 		return false;
28886ac2f8bSAapo Vienamo 
28986ac2f8bSAapo Vienamo 	has_1v8 = regulator_is_supported_voltage(host->mmc->supply.vqmmc,
29086ac2f8bSAapo Vienamo 						 1700000, 1950000);
29186ac2f8bSAapo Vienamo 
29286ac2f8bSAapo Vienamo 	has_3v3 = regulator_is_supported_voltage(host->mmc->supply.vqmmc,
29386ac2f8bSAapo Vienamo 						 2700000, 3600000);
29486ac2f8bSAapo Vienamo 
29586ac2f8bSAapo Vienamo 	if (has_1v8 == 1 && has_3v3 == 1)
29686ac2f8bSAapo Vienamo 		return tegra_host->pad_control_available;
29786ac2f8bSAapo Vienamo 
29886ac2f8bSAapo Vienamo 	/* Fixed voltage, no pad control required. */
29986ac2f8bSAapo Vienamo 	return true;
30086ac2f8bSAapo Vienamo }
30186ac2f8bSAapo Vienamo 
302c2c09678SAapo Vienamo static void tegra_sdhci_set_tap(struct sdhci_host *host, unsigned int tap)
303c2c09678SAapo Vienamo {
304c2c09678SAapo Vienamo 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
305c2c09678SAapo Vienamo 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
306c2c09678SAapo Vienamo 	const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
307c2c09678SAapo Vienamo 	bool card_clk_enabled = false;
308c2c09678SAapo Vienamo 	u32 reg;
309c2c09678SAapo Vienamo 
310c2c09678SAapo Vienamo 	/*
311c2c09678SAapo Vienamo 	 * Touching the tap values is a bit tricky on some SoC generations.
312c2c09678SAapo Vienamo 	 * The quirk enables a workaround for a glitch that sometimes occurs if
313c2c09678SAapo Vienamo 	 * the tap values are changed.
314c2c09678SAapo Vienamo 	 */
315c2c09678SAapo Vienamo 
316c2c09678SAapo Vienamo 	if (soc_data->nvquirks & NVQUIRK_DIS_CARD_CLK_CONFIG_TAP)
317c2c09678SAapo Vienamo 		card_clk_enabled = tegra_sdhci_configure_card_clk(host, false);
318c2c09678SAapo Vienamo 
319c2c09678SAapo Vienamo 	reg = sdhci_readl(host, SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
320c2c09678SAapo Vienamo 	reg &= ~SDHCI_CLOCK_CTRL_TAP_MASK;
321c2c09678SAapo Vienamo 	reg |= tap << SDHCI_CLOCK_CTRL_TAP_SHIFT;
322c2c09678SAapo Vienamo 	sdhci_writel(host, reg, SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
323c2c09678SAapo Vienamo 
324c2c09678SAapo Vienamo 	if (soc_data->nvquirks & NVQUIRK_DIS_CARD_CLK_CONFIG_TAP &&
325c2c09678SAapo Vienamo 	    card_clk_enabled) {
326c2c09678SAapo Vienamo 		udelay(1);
327c2c09678SAapo Vienamo 		sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
328c2c09678SAapo Vienamo 		tegra_sdhci_configure_card_clk(host, card_clk_enabled);
329c2c09678SAapo Vienamo 	}
330c2c09678SAapo Vienamo }
331c2c09678SAapo Vienamo 
332dfc9700cSAapo Vienamo static void tegra_sdhci_hs400_enhanced_strobe(struct mmc_host *mmc,
333dfc9700cSAapo Vienamo 					      struct mmc_ios *ios)
334dfc9700cSAapo Vienamo {
335dfc9700cSAapo Vienamo 	struct sdhci_host *host = mmc_priv(mmc);
336dfc9700cSAapo Vienamo 	u32 val;
337dfc9700cSAapo Vienamo 
338dfc9700cSAapo Vienamo 	val = sdhci_readl(host, SDHCI_TEGRA_VENDOR_SYS_SW_CTRL);
339dfc9700cSAapo Vienamo 
340dfc9700cSAapo Vienamo 	if (ios->enhanced_strobe)
341dfc9700cSAapo Vienamo 		val |= SDHCI_TEGRA_SYS_SW_CTRL_ENHANCED_STROBE;
342dfc9700cSAapo Vienamo 	else
343dfc9700cSAapo Vienamo 		val &= ~SDHCI_TEGRA_SYS_SW_CTRL_ENHANCED_STROBE;
344dfc9700cSAapo Vienamo 
345dfc9700cSAapo Vienamo 	sdhci_writel(host, val, SDHCI_TEGRA_VENDOR_SYS_SW_CTRL);
346dfc9700cSAapo Vienamo 
347dfc9700cSAapo Vienamo }
348dfc9700cSAapo Vienamo 
34903231f9bSRussell King static void tegra_sdhci_reset(struct sdhci_host *host, u8 mask)
350ca5879d3SPavan Kunapuli {
351ca5879d3SPavan Kunapuli 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
3520734e79cSJisheng Zhang 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
353ca5879d3SPavan Kunapuli 	const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
3549d548f11SAapo Vienamo 	u32 misc_ctrl, clk_ctrl, pad_ctrl;
355ca5879d3SPavan Kunapuli 
35603231f9bSRussell King 	sdhci_reset(host, mask);
35703231f9bSRussell King 
358ca5879d3SPavan Kunapuli 	if (!(mask & SDHCI_RESET_ALL))
359ca5879d3SPavan Kunapuli 		return;
360ca5879d3SPavan Kunapuli 
361c2c09678SAapo Vienamo 	tegra_sdhci_set_tap(host, tegra_host->default_tap);
362c2c09678SAapo Vienamo 
3631b84def8SLucas Stach 	misc_ctrl = sdhci_readl(host, SDHCI_TEGRA_VENDOR_MISC_CTRL);
3644f6aa326SJon Hunter 	clk_ctrl = sdhci_readl(host, SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
3654f6aa326SJon Hunter 
3664f6aa326SJon Hunter 	misc_ctrl &= ~(SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300 |
3674f6aa326SJon Hunter 		       SDHCI_MISC_CTRL_ENABLE_SDR50 |
3684f6aa326SJon Hunter 		       SDHCI_MISC_CTRL_ENABLE_DDR50 |
3694f6aa326SJon Hunter 		       SDHCI_MISC_CTRL_ENABLE_SDR104);
3704f6aa326SJon Hunter 
37141a0b8d7SAapo Vienamo 	clk_ctrl &= ~(SDHCI_CLOCK_CTRL_TRIM_MASK |
37241a0b8d7SAapo Vienamo 		      SDHCI_CLOCK_CTRL_SPI_MODE_CLKEN_OVERRIDE);
3734f6aa326SJon Hunter 
37486ac2f8bSAapo Vienamo 	if (tegra_sdhci_is_pad_and_regulator_valid(host)) {
375ca5879d3SPavan Kunapuli 		/* Erratum: Enable SDHCI spec v3.00 support */
3763145351aSAndrew Bresticker 		if (soc_data->nvquirks & NVQUIRK_ENABLE_SDHCI_SPEC_300)
377ca5879d3SPavan Kunapuli 			misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300;
3787ad2ed1dSLucas Stach 		/* Advertise UHS modes as supported by host */
3797ad2ed1dSLucas Stach 		if (soc_data->nvquirks & NVQUIRK_ENABLE_SDR50)
3807ad2ed1dSLucas Stach 			misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDR50;
3817ad2ed1dSLucas Stach 		if (soc_data->nvquirks & NVQUIRK_ENABLE_DDR50)
3827ad2ed1dSLucas Stach 			misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_DDR50;
3837ad2ed1dSLucas Stach 		if (soc_data->nvquirks & NVQUIRK_ENABLE_SDR104)
3847ad2ed1dSLucas Stach 			misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDR104;
3857ad2ed1dSLucas Stach 		if (soc_data->nvquirks & SDHCI_MISC_CTRL_ENABLE_SDR50)
386c3c2384cSLucas Stach 			clk_ctrl |= SDHCI_CLOCK_CTRL_SDR50_TUNING_OVERRIDE;
3874f6aa326SJon Hunter 	}
3884f6aa326SJon Hunter 
38941a0b8d7SAapo Vienamo 	clk_ctrl |= tegra_host->default_trim << SDHCI_CLOCK_CTRL_TRIM_SHIFT;
39041a0b8d7SAapo Vienamo 
3914f6aa326SJon Hunter 	sdhci_writel(host, misc_ctrl, SDHCI_TEGRA_VENDOR_MISC_CTRL);
39274cd42bcSLucas Stach 	sdhci_writel(host, clk_ctrl, SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
39374cd42bcSLucas Stach 
3949d548f11SAapo Vienamo 	if (soc_data->nvquirks & NVQUIRK_HAS_PADCALIB) {
3959d548f11SAapo Vienamo 		pad_ctrl = sdhci_readl(host, SDHCI_TEGRA_SDMEM_COMP_PADCTRL);
3969d548f11SAapo Vienamo 		pad_ctrl &= ~SDHCI_TEGRA_SDMEM_COMP_PADCTRL_VREF_SEL_MASK;
3979d548f11SAapo Vienamo 		pad_ctrl |= SDHCI_TEGRA_SDMEM_COMP_PADCTRL_VREF_SEL_VAL;
3989d548f11SAapo Vienamo 		sdhci_writel(host, pad_ctrl, SDHCI_TEGRA_SDMEM_COMP_PADCTRL);
3999d548f11SAapo Vienamo 
400e5c63d91SLucas Stach 		tegra_host->pad_calib_required = true;
4019d548f11SAapo Vienamo 	}
402e5c63d91SLucas Stach 
403a8e326a9SLucas Stach 	tegra_host->ddr_signaling = false;
404ca5879d3SPavan Kunapuli }
405ca5879d3SPavan Kunapuli 
406212b0cf1SAapo Vienamo static void tegra_sdhci_configure_cal_pad(struct sdhci_host *host, bool enable)
407212b0cf1SAapo Vienamo {
408212b0cf1SAapo Vienamo 	u32 val;
409212b0cf1SAapo Vienamo 
410212b0cf1SAapo Vienamo 	/*
411212b0cf1SAapo Vienamo 	 * Enable or disable the additional I/O pad used by the drive strength
412212b0cf1SAapo Vienamo 	 * calibration process.
413212b0cf1SAapo Vienamo 	 */
414212b0cf1SAapo Vienamo 	val = sdhci_readl(host, SDHCI_TEGRA_SDMEM_COMP_PADCTRL);
415212b0cf1SAapo Vienamo 
416212b0cf1SAapo Vienamo 	if (enable)
417212b0cf1SAapo Vienamo 		val |= SDHCI_TEGRA_SDMEM_COMP_PADCTRL_E_INPUT_E_PWRD;
418212b0cf1SAapo Vienamo 	else
419212b0cf1SAapo Vienamo 		val &= ~SDHCI_TEGRA_SDMEM_COMP_PADCTRL_E_INPUT_E_PWRD;
420212b0cf1SAapo Vienamo 
421212b0cf1SAapo Vienamo 	sdhci_writel(host, val, SDHCI_TEGRA_SDMEM_COMP_PADCTRL);
422212b0cf1SAapo Vienamo 
423212b0cf1SAapo Vienamo 	if (enable)
424212b0cf1SAapo Vienamo 		usleep_range(1, 2);
425212b0cf1SAapo Vienamo }
426212b0cf1SAapo Vienamo 
42751b77c8eSAapo Vienamo static void tegra_sdhci_set_pad_autocal_offset(struct sdhci_host *host,
42851b77c8eSAapo Vienamo 					       u16 pdpu)
42951b77c8eSAapo Vienamo {
43051b77c8eSAapo Vienamo 	u32 reg;
43151b77c8eSAapo Vienamo 
43251b77c8eSAapo Vienamo 	reg = sdhci_readl(host, SDHCI_TEGRA_AUTO_CAL_CONFIG);
43351b77c8eSAapo Vienamo 	reg &= ~SDHCI_AUTO_CAL_PDPU_OFFSET_MASK;
43451b77c8eSAapo Vienamo 	reg |= pdpu;
43551b77c8eSAapo Vienamo 	sdhci_writel(host, reg, SDHCI_TEGRA_AUTO_CAL_CONFIG);
43651b77c8eSAapo Vienamo }
43751b77c8eSAapo Vienamo 
438de25fa5aSSowjanya Komatineni static int tegra_sdhci_set_padctrl(struct sdhci_host *host, int voltage,
439de25fa5aSSowjanya Komatineni 				   bool state_drvupdn)
440de25fa5aSSowjanya Komatineni {
441de25fa5aSSowjanya Komatineni 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
442de25fa5aSSowjanya Komatineni 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
443de25fa5aSSowjanya Komatineni 	struct sdhci_tegra_autocal_offsets *offsets =
444de25fa5aSSowjanya Komatineni 						&tegra_host->autocal_offsets;
445de25fa5aSSowjanya Komatineni 	struct pinctrl_state *pinctrl_drvupdn = NULL;
446de25fa5aSSowjanya Komatineni 	int ret = 0;
447de25fa5aSSowjanya Komatineni 	u8 drvup = 0, drvdn = 0;
448de25fa5aSSowjanya Komatineni 	u32 reg;
449de25fa5aSSowjanya Komatineni 
450de25fa5aSSowjanya Komatineni 	if (!state_drvupdn) {
451de25fa5aSSowjanya Komatineni 		/* PADS Drive Strength */
452de25fa5aSSowjanya Komatineni 		if (voltage == MMC_SIGNAL_VOLTAGE_180) {
453de25fa5aSSowjanya Komatineni 			if (tegra_host->pinctrl_state_1v8_drv) {
454de25fa5aSSowjanya Komatineni 				pinctrl_drvupdn =
455de25fa5aSSowjanya Komatineni 					tegra_host->pinctrl_state_1v8_drv;
456de25fa5aSSowjanya Komatineni 			} else {
457de25fa5aSSowjanya Komatineni 				drvup = offsets->pull_up_1v8_timeout;
458de25fa5aSSowjanya Komatineni 				drvdn = offsets->pull_down_1v8_timeout;
459de25fa5aSSowjanya Komatineni 			}
460de25fa5aSSowjanya Komatineni 		} else {
461de25fa5aSSowjanya Komatineni 			if (tegra_host->pinctrl_state_3v3_drv) {
462de25fa5aSSowjanya Komatineni 				pinctrl_drvupdn =
463de25fa5aSSowjanya Komatineni 					tegra_host->pinctrl_state_3v3_drv;
464de25fa5aSSowjanya Komatineni 			} else {
465de25fa5aSSowjanya Komatineni 				drvup = offsets->pull_up_3v3_timeout;
466de25fa5aSSowjanya Komatineni 				drvdn = offsets->pull_down_3v3_timeout;
467de25fa5aSSowjanya Komatineni 			}
468de25fa5aSSowjanya Komatineni 		}
469de25fa5aSSowjanya Komatineni 
470de25fa5aSSowjanya Komatineni 		if (pinctrl_drvupdn != NULL) {
471de25fa5aSSowjanya Komatineni 			ret = pinctrl_select_state(tegra_host->pinctrl_sdmmc,
472de25fa5aSSowjanya Komatineni 							pinctrl_drvupdn);
473de25fa5aSSowjanya Komatineni 			if (ret < 0)
474de25fa5aSSowjanya Komatineni 				dev_err(mmc_dev(host->mmc),
475de25fa5aSSowjanya Komatineni 					"failed pads drvupdn, ret: %d\n", ret);
476de25fa5aSSowjanya Komatineni 		} else if ((drvup) || (drvdn)) {
477de25fa5aSSowjanya Komatineni 			reg = sdhci_readl(host,
478de25fa5aSSowjanya Komatineni 					SDHCI_TEGRA_SDMEM_COMP_PADCTRL);
479de25fa5aSSowjanya Komatineni 			reg &= ~SDHCI_COMP_PADCTRL_DRVUPDN_OFFSET_MASK;
480de25fa5aSSowjanya Komatineni 			reg |= (drvup << 20) | (drvdn << 12);
481de25fa5aSSowjanya Komatineni 			sdhci_writel(host, reg,
482de25fa5aSSowjanya Komatineni 					SDHCI_TEGRA_SDMEM_COMP_PADCTRL);
483de25fa5aSSowjanya Komatineni 		}
484de25fa5aSSowjanya Komatineni 
485de25fa5aSSowjanya Komatineni 	} else {
486de25fa5aSSowjanya Komatineni 		/* Dual Voltage PADS Voltage selection */
487de25fa5aSSowjanya Komatineni 		if (!tegra_host->pad_control_available)
488de25fa5aSSowjanya Komatineni 			return 0;
489de25fa5aSSowjanya Komatineni 
490de25fa5aSSowjanya Komatineni 		if (voltage == MMC_SIGNAL_VOLTAGE_180) {
491de25fa5aSSowjanya Komatineni 			ret = pinctrl_select_state(tegra_host->pinctrl_sdmmc,
492de25fa5aSSowjanya Komatineni 						tegra_host->pinctrl_state_1v8);
493de25fa5aSSowjanya Komatineni 			if (ret < 0)
494de25fa5aSSowjanya Komatineni 				dev_err(mmc_dev(host->mmc),
495de25fa5aSSowjanya Komatineni 					"setting 1.8V failed, ret: %d\n", ret);
496de25fa5aSSowjanya Komatineni 		} else {
497de25fa5aSSowjanya Komatineni 			ret = pinctrl_select_state(tegra_host->pinctrl_sdmmc,
498de25fa5aSSowjanya Komatineni 						tegra_host->pinctrl_state_3v3);
499de25fa5aSSowjanya Komatineni 			if (ret < 0)
500de25fa5aSSowjanya Komatineni 				dev_err(mmc_dev(host->mmc),
501de25fa5aSSowjanya Komatineni 					"setting 3.3V failed, ret: %d\n", ret);
502de25fa5aSSowjanya Komatineni 		}
503de25fa5aSSowjanya Komatineni 	}
504de25fa5aSSowjanya Komatineni 
505de25fa5aSSowjanya Komatineni 	return ret;
506de25fa5aSSowjanya Komatineni }
507de25fa5aSSowjanya Komatineni 
508e5c63d91SLucas Stach static void tegra_sdhci_pad_autocalib(struct sdhci_host *host)
509e5c63d91SLucas Stach {
51051b77c8eSAapo Vienamo 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
51151b77c8eSAapo Vienamo 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
51251b77c8eSAapo Vienamo 	struct sdhci_tegra_autocal_offsets offsets =
51351b77c8eSAapo Vienamo 			tegra_host->autocal_offsets;
51451b77c8eSAapo Vienamo 	struct mmc_ios *ios = &host->mmc->ios;
515887bda8fSAapo Vienamo 	bool card_clk_enabled;
51651b77c8eSAapo Vienamo 	u16 pdpu;
517e7c07148SAapo Vienamo 	u32 reg;
518e7c07148SAapo Vienamo 	int ret;
519e5c63d91SLucas Stach 
52051b77c8eSAapo Vienamo 	switch (ios->timing) {
52151b77c8eSAapo Vienamo 	case MMC_TIMING_UHS_SDR104:
52251b77c8eSAapo Vienamo 		pdpu = offsets.pull_down_sdr104 << 8 | offsets.pull_up_sdr104;
52351b77c8eSAapo Vienamo 		break;
52451b77c8eSAapo Vienamo 	case MMC_TIMING_MMC_HS400:
52551b77c8eSAapo Vienamo 		pdpu = offsets.pull_down_hs400 << 8 | offsets.pull_up_hs400;
52651b77c8eSAapo Vienamo 		break;
52751b77c8eSAapo Vienamo 	default:
52851b77c8eSAapo Vienamo 		if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180)
52951b77c8eSAapo Vienamo 			pdpu = offsets.pull_down_1v8 << 8 | offsets.pull_up_1v8;
53051b77c8eSAapo Vienamo 		else
53151b77c8eSAapo Vienamo 			pdpu = offsets.pull_down_3v3 << 8 | offsets.pull_up_3v3;
53251b77c8eSAapo Vienamo 	}
53351b77c8eSAapo Vienamo 
534de25fa5aSSowjanya Komatineni 	/* Set initial offset before auto-calibration */
53551b77c8eSAapo Vienamo 	tegra_sdhci_set_pad_autocal_offset(host, pdpu);
53651b77c8eSAapo Vienamo 
537887bda8fSAapo Vienamo 	card_clk_enabled = tegra_sdhci_configure_card_clk(host, false);
538887bda8fSAapo Vienamo 
539212b0cf1SAapo Vienamo 	tegra_sdhci_configure_cal_pad(host, true);
540212b0cf1SAapo Vienamo 
541e7c07148SAapo Vienamo 	reg = sdhci_readl(host, SDHCI_TEGRA_AUTO_CAL_CONFIG);
542e7c07148SAapo Vienamo 	reg |= SDHCI_AUTO_CAL_ENABLE | SDHCI_AUTO_CAL_START;
543e7c07148SAapo Vienamo 	sdhci_writel(host, reg, SDHCI_TEGRA_AUTO_CAL_CONFIG);
544e5c63d91SLucas Stach 
545e7c07148SAapo Vienamo 	usleep_range(1, 2);
546e7c07148SAapo Vienamo 	/* 10 ms timeout */
547e7c07148SAapo Vienamo 	ret = readl_poll_timeout(host->ioaddr + SDHCI_TEGRA_AUTO_CAL_STATUS,
548e7c07148SAapo Vienamo 				 reg, !(reg & SDHCI_TEGRA_AUTO_CAL_ACTIVE),
549e7c07148SAapo Vienamo 				 1000, 10000);
550e7c07148SAapo Vienamo 
551212b0cf1SAapo Vienamo 	tegra_sdhci_configure_cal_pad(host, false);
552212b0cf1SAapo Vienamo 
553887bda8fSAapo Vienamo 	tegra_sdhci_configure_card_clk(host, card_clk_enabled);
554887bda8fSAapo Vienamo 
55551b77c8eSAapo Vienamo 	if (ret) {
556e7c07148SAapo Vienamo 		dev_err(mmc_dev(host->mmc), "Pad autocal timed out\n");
55751b77c8eSAapo Vienamo 
558de25fa5aSSowjanya Komatineni 		/* Disable automatic cal and use fixed Drive Strengths */
55951b77c8eSAapo Vienamo 		reg = sdhci_readl(host, SDHCI_TEGRA_AUTO_CAL_CONFIG);
56051b77c8eSAapo Vienamo 		reg &= ~SDHCI_AUTO_CAL_ENABLE;
56151b77c8eSAapo Vienamo 		sdhci_writel(host, reg, SDHCI_TEGRA_AUTO_CAL_CONFIG);
56251b77c8eSAapo Vienamo 
563de25fa5aSSowjanya Komatineni 		ret = tegra_sdhci_set_padctrl(host, ios->signal_voltage, false);
564de25fa5aSSowjanya Komatineni 		if (ret < 0)
565de25fa5aSSowjanya Komatineni 			dev_err(mmc_dev(host->mmc),
566de25fa5aSSowjanya Komatineni 				"Setting drive strengths failed: %d\n", ret);
56751b77c8eSAapo Vienamo 	}
56851b77c8eSAapo Vienamo }
56951b77c8eSAapo Vienamo 
57051b77c8eSAapo Vienamo static void tegra_sdhci_parse_pad_autocal_dt(struct sdhci_host *host)
57151b77c8eSAapo Vienamo {
57251b77c8eSAapo Vienamo 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
57351b77c8eSAapo Vienamo 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
57451b77c8eSAapo Vienamo 	struct sdhci_tegra_autocal_offsets *autocal =
57551b77c8eSAapo Vienamo 			&tegra_host->autocal_offsets;
57651b77c8eSAapo Vienamo 	int err;
57751b77c8eSAapo Vienamo 
57851b77c8eSAapo Vienamo 	err = device_property_read_u32(host->mmc->parent,
57951b77c8eSAapo Vienamo 			"nvidia,pad-autocal-pull-up-offset-3v3",
58051b77c8eSAapo Vienamo 			&autocal->pull_up_3v3);
58151b77c8eSAapo Vienamo 	if (err)
58251b77c8eSAapo Vienamo 		autocal->pull_up_3v3 = 0;
58351b77c8eSAapo Vienamo 
58451b77c8eSAapo Vienamo 	err = device_property_read_u32(host->mmc->parent,
58551b77c8eSAapo Vienamo 			"nvidia,pad-autocal-pull-down-offset-3v3",
58651b77c8eSAapo Vienamo 			&autocal->pull_down_3v3);
58751b77c8eSAapo Vienamo 	if (err)
58851b77c8eSAapo Vienamo 		autocal->pull_down_3v3 = 0;
58951b77c8eSAapo Vienamo 
59051b77c8eSAapo Vienamo 	err = device_property_read_u32(host->mmc->parent,
59151b77c8eSAapo Vienamo 			"nvidia,pad-autocal-pull-up-offset-1v8",
59251b77c8eSAapo Vienamo 			&autocal->pull_up_1v8);
59351b77c8eSAapo Vienamo 	if (err)
59451b77c8eSAapo Vienamo 		autocal->pull_up_1v8 = 0;
59551b77c8eSAapo Vienamo 
59651b77c8eSAapo Vienamo 	err = device_property_read_u32(host->mmc->parent,
59751b77c8eSAapo Vienamo 			"nvidia,pad-autocal-pull-down-offset-1v8",
59851b77c8eSAapo Vienamo 			&autocal->pull_down_1v8);
59951b77c8eSAapo Vienamo 	if (err)
60051b77c8eSAapo Vienamo 		autocal->pull_down_1v8 = 0;
60151b77c8eSAapo Vienamo 
60251b77c8eSAapo Vienamo 	err = device_property_read_u32(host->mmc->parent,
60351b77c8eSAapo Vienamo 			"nvidia,pad-autocal-pull-up-offset-3v3-timeout",
6045ccf7f55SSowjanya Komatineni 			&autocal->pull_up_3v3_timeout);
605de25fa5aSSowjanya Komatineni 	if (err) {
606de25fa5aSSowjanya Komatineni 		if (!IS_ERR(tegra_host->pinctrl_state_3v3) &&
607de25fa5aSSowjanya Komatineni 			(tegra_host->pinctrl_state_3v3_drv == NULL))
608de25fa5aSSowjanya Komatineni 			pr_warn("%s: Missing autocal timeout 3v3-pad drvs\n",
609de25fa5aSSowjanya Komatineni 				mmc_hostname(host->mmc));
61051b77c8eSAapo Vienamo 		autocal->pull_up_3v3_timeout = 0;
611de25fa5aSSowjanya Komatineni 	}
61251b77c8eSAapo Vienamo 
61351b77c8eSAapo Vienamo 	err = device_property_read_u32(host->mmc->parent,
61451b77c8eSAapo Vienamo 			"nvidia,pad-autocal-pull-down-offset-3v3-timeout",
6155ccf7f55SSowjanya Komatineni 			&autocal->pull_down_3v3_timeout);
616de25fa5aSSowjanya Komatineni 	if (err) {
617de25fa5aSSowjanya Komatineni 		if (!IS_ERR(tegra_host->pinctrl_state_3v3) &&
618de25fa5aSSowjanya Komatineni 			(tegra_host->pinctrl_state_3v3_drv == NULL))
619de25fa5aSSowjanya Komatineni 			pr_warn("%s: Missing autocal timeout 3v3-pad drvs\n",
620de25fa5aSSowjanya Komatineni 				mmc_hostname(host->mmc));
62151b77c8eSAapo Vienamo 		autocal->pull_down_3v3_timeout = 0;
622de25fa5aSSowjanya Komatineni 	}
62351b77c8eSAapo Vienamo 
62451b77c8eSAapo Vienamo 	err = device_property_read_u32(host->mmc->parent,
62551b77c8eSAapo Vienamo 			"nvidia,pad-autocal-pull-up-offset-1v8-timeout",
6265ccf7f55SSowjanya Komatineni 			&autocal->pull_up_1v8_timeout);
627de25fa5aSSowjanya Komatineni 	if (err) {
628de25fa5aSSowjanya Komatineni 		if (!IS_ERR(tegra_host->pinctrl_state_1v8) &&
629de25fa5aSSowjanya Komatineni 			(tegra_host->pinctrl_state_1v8_drv == NULL))
630de25fa5aSSowjanya Komatineni 			pr_warn("%s: Missing autocal timeout 1v8-pad drvs\n",
631de25fa5aSSowjanya Komatineni 				mmc_hostname(host->mmc));
63251b77c8eSAapo Vienamo 		autocal->pull_up_1v8_timeout = 0;
633de25fa5aSSowjanya Komatineni 	}
63451b77c8eSAapo Vienamo 
63551b77c8eSAapo Vienamo 	err = device_property_read_u32(host->mmc->parent,
63651b77c8eSAapo Vienamo 			"nvidia,pad-autocal-pull-down-offset-1v8-timeout",
6375ccf7f55SSowjanya Komatineni 			&autocal->pull_down_1v8_timeout);
638de25fa5aSSowjanya Komatineni 	if (err) {
639de25fa5aSSowjanya Komatineni 		if (!IS_ERR(tegra_host->pinctrl_state_1v8) &&
640de25fa5aSSowjanya Komatineni 			(tegra_host->pinctrl_state_1v8_drv == NULL))
641de25fa5aSSowjanya Komatineni 			pr_warn("%s: Missing autocal timeout 1v8-pad drvs\n",
642de25fa5aSSowjanya Komatineni 				mmc_hostname(host->mmc));
64351b77c8eSAapo Vienamo 		autocal->pull_down_1v8_timeout = 0;
644de25fa5aSSowjanya Komatineni 	}
64551b77c8eSAapo Vienamo 
64651b77c8eSAapo Vienamo 	err = device_property_read_u32(host->mmc->parent,
64751b77c8eSAapo Vienamo 			"nvidia,pad-autocal-pull-up-offset-sdr104",
64851b77c8eSAapo Vienamo 			&autocal->pull_up_sdr104);
64951b77c8eSAapo Vienamo 	if (err)
65051b77c8eSAapo Vienamo 		autocal->pull_up_sdr104 = autocal->pull_up_1v8;
65151b77c8eSAapo Vienamo 
65251b77c8eSAapo Vienamo 	err = device_property_read_u32(host->mmc->parent,
65351b77c8eSAapo Vienamo 			"nvidia,pad-autocal-pull-down-offset-sdr104",
65451b77c8eSAapo Vienamo 			&autocal->pull_down_sdr104);
65551b77c8eSAapo Vienamo 	if (err)
65651b77c8eSAapo Vienamo 		autocal->pull_down_sdr104 = autocal->pull_down_1v8;
65751b77c8eSAapo Vienamo 
65851b77c8eSAapo Vienamo 	err = device_property_read_u32(host->mmc->parent,
65951b77c8eSAapo Vienamo 			"nvidia,pad-autocal-pull-up-offset-hs400",
66051b77c8eSAapo Vienamo 			&autocal->pull_up_hs400);
66151b77c8eSAapo Vienamo 	if (err)
66251b77c8eSAapo Vienamo 		autocal->pull_up_hs400 = autocal->pull_up_1v8;
66351b77c8eSAapo Vienamo 
66451b77c8eSAapo Vienamo 	err = device_property_read_u32(host->mmc->parent,
66551b77c8eSAapo Vienamo 			"nvidia,pad-autocal-pull-down-offset-hs400",
66651b77c8eSAapo Vienamo 			&autocal->pull_down_hs400);
66751b77c8eSAapo Vienamo 	if (err)
66851b77c8eSAapo Vienamo 		autocal->pull_down_hs400 = autocal->pull_down_1v8;
669e5c63d91SLucas Stach }
670e5c63d91SLucas Stach 
67161dad40eSAapo Vienamo static void tegra_sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
67261dad40eSAapo Vienamo {
67361dad40eSAapo Vienamo 	struct sdhci_host *host = mmc_priv(mmc);
67461dad40eSAapo Vienamo 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
67561dad40eSAapo Vienamo 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
67661dad40eSAapo Vienamo 	ktime_t since_calib = ktime_sub(ktime_get(), tegra_host->last_calib);
67761dad40eSAapo Vienamo 
67861dad40eSAapo Vienamo 	/* 100 ms calibration interval is specified in the TRM */
67961dad40eSAapo Vienamo 	if (ktime_to_ms(since_calib) > 100) {
68061dad40eSAapo Vienamo 		tegra_sdhci_pad_autocalib(host);
68161dad40eSAapo Vienamo 		tegra_host->last_calib = ktime_get();
68261dad40eSAapo Vienamo 	}
68361dad40eSAapo Vienamo 
68461dad40eSAapo Vienamo 	sdhci_request(mmc, mrq);
68561dad40eSAapo Vienamo }
68661dad40eSAapo Vienamo 
687f5313aaaSAapo Vienamo static void tegra_sdhci_parse_tap_and_trim(struct sdhci_host *host)
68885c0da17SAapo Vienamo {
68985c0da17SAapo Vienamo 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
69085c0da17SAapo Vienamo 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
69185c0da17SAapo Vienamo 	int err;
69285c0da17SAapo Vienamo 
69385c0da17SAapo Vienamo 	err = device_property_read_u32(host->mmc->parent, "nvidia,default-tap",
69485c0da17SAapo Vienamo 				       &tegra_host->default_tap);
69585c0da17SAapo Vienamo 	if (err)
69685c0da17SAapo Vienamo 		tegra_host->default_tap = 0;
69785c0da17SAapo Vienamo 
69885c0da17SAapo Vienamo 	err = device_property_read_u32(host->mmc->parent, "nvidia,default-trim",
69985c0da17SAapo Vienamo 				       &tegra_host->default_trim);
70085c0da17SAapo Vienamo 	if (err)
70185c0da17SAapo Vienamo 		tegra_host->default_trim = 0;
702f5313aaaSAapo Vienamo 
703f5313aaaSAapo Vienamo 	err = device_property_read_u32(host->mmc->parent, "nvidia,dqs-trim",
704f5313aaaSAapo Vienamo 				       &tegra_host->dqs_trim);
705f5313aaaSAapo Vienamo 	if (err)
706f5313aaaSAapo Vienamo 		tegra_host->dqs_trim = 0x11;
70785c0da17SAapo Vienamo }
70885c0da17SAapo Vienamo 
7093c4019f9SSowjanya Komatineni static void tegra_sdhci_parse_dt(struct sdhci_host *host)
7103c4019f9SSowjanya Komatineni {
7113c4019f9SSowjanya Komatineni 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
7123c4019f9SSowjanya Komatineni 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
7133c4019f9SSowjanya Komatineni 
7143c4019f9SSowjanya Komatineni 	if (device_property_read_bool(host->mmc->parent, "supports-cqe"))
7153c4019f9SSowjanya Komatineni 		tegra_host->enable_hwcq = true;
7163c4019f9SSowjanya Komatineni 	else
7173c4019f9SSowjanya Komatineni 		tegra_host->enable_hwcq = false;
7183c4019f9SSowjanya Komatineni 
7193c4019f9SSowjanya Komatineni 	tegra_sdhci_parse_pad_autocal_dt(host);
7203c4019f9SSowjanya Komatineni 	tegra_sdhci_parse_tap_and_trim(host);
7213c4019f9SSowjanya Komatineni }
7223c4019f9SSowjanya Komatineni 
723a8e326a9SLucas Stach static void tegra_sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
724a8e326a9SLucas Stach {
725a8e326a9SLucas Stach 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
7260734e79cSJisheng Zhang 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
727a8e326a9SLucas Stach 	unsigned long host_clk;
728a8e326a9SLucas Stach 
729a8e326a9SLucas Stach 	if (!clock)
7303491b690SLucas Stach 		return sdhci_set_clock(host, clock);
731a8e326a9SLucas Stach 
73257d1654eSAapo Vienamo 	/*
73357d1654eSAapo Vienamo 	 * In DDR50/52 modes the Tegra SDHCI controllers require the SDHCI
73457d1654eSAapo Vienamo 	 * divider to be configured to divided the host clock by two. The SDHCI
73557d1654eSAapo Vienamo 	 * clock divider is calculated as part of sdhci_set_clock() by
73657d1654eSAapo Vienamo 	 * sdhci_calc_clk(). The divider is calculated from host->max_clk and
73757d1654eSAapo Vienamo 	 * the requested clock rate.
73857d1654eSAapo Vienamo 	 *
73957d1654eSAapo Vienamo 	 * By setting the host->max_clk to clock * 2 the divider calculation
74057d1654eSAapo Vienamo 	 * will always result in the correct value for DDR50/52 modes,
74157d1654eSAapo Vienamo 	 * regardless of clock rate rounding, which may happen if the value
74257d1654eSAapo Vienamo 	 * from clk_get_rate() is used.
74357d1654eSAapo Vienamo 	 */
744a8e326a9SLucas Stach 	host_clk = tegra_host->ddr_signaling ? clock * 2 : clock;
745a8e326a9SLucas Stach 	clk_set_rate(pltfm_host->clk, host_clk);
746ea8fc595SSowjanya Komatineni 	tegra_host->curr_clk_rate = host_clk;
74757d1654eSAapo Vienamo 	if (tegra_host->ddr_signaling)
74857d1654eSAapo Vienamo 		host->max_clk = host_clk;
74957d1654eSAapo Vienamo 	else
750a8e326a9SLucas Stach 		host->max_clk = clk_get_rate(pltfm_host->clk);
751a8e326a9SLucas Stach 
752e5c63d91SLucas Stach 	sdhci_set_clock(host, clock);
753e5c63d91SLucas Stach 
754e5c63d91SLucas Stach 	if (tegra_host->pad_calib_required) {
755e5c63d91SLucas Stach 		tegra_sdhci_pad_autocalib(host);
756e5c63d91SLucas Stach 		tegra_host->pad_calib_required = false;
757e5c63d91SLucas Stach 	}
758a8e326a9SLucas Stach }
759a8e326a9SLucas Stach 
76044350993SAapo Vienamo static unsigned int tegra_sdhci_get_max_clock(struct sdhci_host *host)
76144350993SAapo Vienamo {
76244350993SAapo Vienamo 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
76344350993SAapo Vienamo 
76444350993SAapo Vienamo 	return clk_round_rate(pltfm_host->clk, UINT_MAX);
76544350993SAapo Vienamo }
76644350993SAapo Vienamo 
767f5313aaaSAapo Vienamo static void tegra_sdhci_set_dqs_trim(struct sdhci_host *host, u8 trim)
768f5313aaaSAapo Vienamo {
769f5313aaaSAapo Vienamo 	u32 val;
770f5313aaaSAapo Vienamo 
771f5313aaaSAapo Vienamo 	val = sdhci_readl(host, SDHCI_TEGRA_VENDOR_CAP_OVERRIDES);
772f5313aaaSAapo Vienamo 	val &= ~SDHCI_TEGRA_CAP_OVERRIDES_DQS_TRIM_MASK;
773f5313aaaSAapo Vienamo 	val |= trim << SDHCI_TEGRA_CAP_OVERRIDES_DQS_TRIM_SHIFT;
774f5313aaaSAapo Vienamo 	sdhci_writel(host, val, SDHCI_TEGRA_VENDOR_CAP_OVERRIDES);
775f5313aaaSAapo Vienamo }
776f5313aaaSAapo Vienamo 
777bc5568bfSAapo Vienamo static void tegra_sdhci_hs400_dll_cal(struct sdhci_host *host)
778bc5568bfSAapo Vienamo {
779bc5568bfSAapo Vienamo 	u32 reg;
780bc5568bfSAapo Vienamo 	int err;
781bc5568bfSAapo Vienamo 
782bc5568bfSAapo Vienamo 	reg = sdhci_readl(host, SDHCI_TEGRA_VENDOR_DLLCAL_CFG);
783bc5568bfSAapo Vienamo 	reg |= SDHCI_TEGRA_DLLCAL_CALIBRATE;
784bc5568bfSAapo Vienamo 	sdhci_writel(host, reg, SDHCI_TEGRA_VENDOR_DLLCAL_CFG);
785bc5568bfSAapo Vienamo 
786bc5568bfSAapo Vienamo 	/* 1 ms sleep, 5 ms timeout */
787bc5568bfSAapo Vienamo 	err = readl_poll_timeout(host->ioaddr + SDHCI_TEGRA_VENDOR_DLLCAL_STA,
788bc5568bfSAapo Vienamo 				 reg, !(reg & SDHCI_TEGRA_DLLCAL_STA_ACTIVE),
789bc5568bfSAapo Vienamo 				 1000, 5000);
790bc5568bfSAapo Vienamo 	if (err)
791bc5568bfSAapo Vienamo 		dev_err(mmc_dev(host->mmc),
792bc5568bfSAapo Vienamo 			"HS400 delay line calibration timed out\n");
793bc5568bfSAapo Vienamo }
794bc5568bfSAapo Vienamo 
795ea8fc595SSowjanya Komatineni static void tegra_sdhci_tap_correction(struct sdhci_host *host, u8 thd_up,
796ea8fc595SSowjanya Komatineni 				       u8 thd_low, u8 fixed_tap)
797ea8fc595SSowjanya Komatineni {
798ea8fc595SSowjanya Komatineni 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
799ea8fc595SSowjanya Komatineni 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
800ea8fc595SSowjanya Komatineni 	u32 val, tun_status;
801ea8fc595SSowjanya Komatineni 	u8 word, bit, edge1, tap, window;
802ea8fc595SSowjanya Komatineni 	bool tap_result;
803ea8fc595SSowjanya Komatineni 	bool start_fail = false;
804ea8fc595SSowjanya Komatineni 	bool start_pass = false;
805ea8fc595SSowjanya Komatineni 	bool end_pass = false;
806ea8fc595SSowjanya Komatineni 	bool first_fail = false;
807ea8fc595SSowjanya Komatineni 	bool first_pass = false;
808ea8fc595SSowjanya Komatineni 	u8 start_pass_tap = 0;
809ea8fc595SSowjanya Komatineni 	u8 end_pass_tap = 0;
810ea8fc595SSowjanya Komatineni 	u8 first_fail_tap = 0;
811ea8fc595SSowjanya Komatineni 	u8 first_pass_tap = 0;
812ea8fc595SSowjanya Komatineni 	u8 total_tuning_words = host->tuning_loop_count / TUNING_WORD_BIT_SIZE;
813ea8fc595SSowjanya Komatineni 
814ea8fc595SSowjanya Komatineni 	/*
815ea8fc595SSowjanya Komatineni 	 * Read auto-tuned results and extract good valid passing window by
816ea8fc595SSowjanya Komatineni 	 * filtering out un-wanted bubble/partial/merged windows.
817ea8fc595SSowjanya Komatineni 	 */
818ea8fc595SSowjanya Komatineni 	for (word = 0; word < total_tuning_words; word++) {
819ea8fc595SSowjanya Komatineni 		val = sdhci_readl(host, SDHCI_VNDR_TUN_CTRL0_0);
820ea8fc595SSowjanya Komatineni 		val &= ~SDHCI_VNDR_TUN_CTRL0_TUN_WORD_SEL_MASK;
821ea8fc595SSowjanya Komatineni 		val |= word;
822ea8fc595SSowjanya Komatineni 		sdhci_writel(host, val, SDHCI_VNDR_TUN_CTRL0_0);
823ea8fc595SSowjanya Komatineni 		tun_status = sdhci_readl(host, SDHCI_TEGRA_VNDR_TUN_STATUS0);
824ea8fc595SSowjanya Komatineni 		bit = 0;
825ea8fc595SSowjanya Komatineni 		while (bit < TUNING_WORD_BIT_SIZE) {
826ea8fc595SSowjanya Komatineni 			tap = word * TUNING_WORD_BIT_SIZE + bit;
827ea8fc595SSowjanya Komatineni 			tap_result = tun_status & (1 << bit);
828ea8fc595SSowjanya Komatineni 			if (!tap_result && !start_fail) {
829ea8fc595SSowjanya Komatineni 				start_fail = true;
830ea8fc595SSowjanya Komatineni 				if (!first_fail) {
831ea8fc595SSowjanya Komatineni 					first_fail_tap = tap;
832ea8fc595SSowjanya Komatineni 					first_fail = true;
833ea8fc595SSowjanya Komatineni 				}
834ea8fc595SSowjanya Komatineni 
835ea8fc595SSowjanya Komatineni 			} else if (tap_result && start_fail && !start_pass) {
836ea8fc595SSowjanya Komatineni 				start_pass_tap = tap;
837ea8fc595SSowjanya Komatineni 				start_pass = true;
838ea8fc595SSowjanya Komatineni 				if (!first_pass) {
839ea8fc595SSowjanya Komatineni 					first_pass_tap = tap;
840ea8fc595SSowjanya Komatineni 					first_pass = true;
841ea8fc595SSowjanya Komatineni 				}
842ea8fc595SSowjanya Komatineni 
843ea8fc595SSowjanya Komatineni 			} else if (!tap_result && start_fail && start_pass &&
844ea8fc595SSowjanya Komatineni 				   !end_pass) {
845ea8fc595SSowjanya Komatineni 				end_pass_tap = tap - 1;
846ea8fc595SSowjanya Komatineni 				end_pass = true;
847ea8fc595SSowjanya Komatineni 			} else if (tap_result && start_pass && start_fail &&
848ea8fc595SSowjanya Komatineni 				   end_pass) {
849ea8fc595SSowjanya Komatineni 				window = end_pass_tap - start_pass_tap;
850ea8fc595SSowjanya Komatineni 				/* discard merged window and bubble window */
851ea8fc595SSowjanya Komatineni 				if (window >= thd_up || window < thd_low) {
852ea8fc595SSowjanya Komatineni 					start_pass_tap = tap;
853ea8fc595SSowjanya Komatineni 					end_pass = false;
854ea8fc595SSowjanya Komatineni 				} else {
855ea8fc595SSowjanya Komatineni 					/* set tap at middle of valid window */
856ea8fc595SSowjanya Komatineni 					tap = start_pass_tap + window / 2;
857ea8fc595SSowjanya Komatineni 					tegra_host->tuned_tap_delay = tap;
858ea8fc595SSowjanya Komatineni 					return;
859ea8fc595SSowjanya Komatineni 				}
860ea8fc595SSowjanya Komatineni 			}
861ea8fc595SSowjanya Komatineni 
862ea8fc595SSowjanya Komatineni 			bit++;
863ea8fc595SSowjanya Komatineni 		}
864ea8fc595SSowjanya Komatineni 	}
865ea8fc595SSowjanya Komatineni 
866ea8fc595SSowjanya Komatineni 	if (!first_fail) {
867ea8fc595SSowjanya Komatineni 		WARN_ON("no edge detected, continue with hw tuned delay.\n");
868ea8fc595SSowjanya Komatineni 	} else if (first_pass) {
869ea8fc595SSowjanya Komatineni 		/* set tap location at fixed tap relative to the first edge */
870ea8fc595SSowjanya Komatineni 		edge1 = first_fail_tap + (first_pass_tap - first_fail_tap) / 2;
871ea8fc595SSowjanya Komatineni 		if (edge1 - 1 > fixed_tap)
872ea8fc595SSowjanya Komatineni 			tegra_host->tuned_tap_delay = edge1 - fixed_tap;
873ea8fc595SSowjanya Komatineni 		else
874ea8fc595SSowjanya Komatineni 			tegra_host->tuned_tap_delay = edge1 + fixed_tap;
875ea8fc595SSowjanya Komatineni 	}
876ea8fc595SSowjanya Komatineni }
877ea8fc595SSowjanya Komatineni 
878ea8fc595SSowjanya Komatineni static void tegra_sdhci_post_tuning(struct sdhci_host *host)
879ea8fc595SSowjanya Komatineni {
880ea8fc595SSowjanya Komatineni 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
881ea8fc595SSowjanya Komatineni 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
882ea8fc595SSowjanya Komatineni 	const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
883ea8fc595SSowjanya Komatineni 	u32 avg_tap_dly, val, min_tap_dly, max_tap_dly;
884ea8fc595SSowjanya Komatineni 	u8 fixed_tap, start_tap, end_tap, window_width;
885ea8fc595SSowjanya Komatineni 	u8 thdupper, thdlower;
886ea8fc595SSowjanya Komatineni 	u8 num_iter;
887ea8fc595SSowjanya Komatineni 	u32 clk_rate_mhz, period_ps, bestcase, worstcase;
888ea8fc595SSowjanya Komatineni 
889ea8fc595SSowjanya Komatineni 	/* retain HW tuned tap to use incase if no correction is needed */
890ea8fc595SSowjanya Komatineni 	val = sdhci_readl(host, SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
891ea8fc595SSowjanya Komatineni 	tegra_host->tuned_tap_delay = (val & SDHCI_CLOCK_CTRL_TAP_MASK) >>
892ea8fc595SSowjanya Komatineni 				      SDHCI_CLOCK_CTRL_TAP_SHIFT;
893ea8fc595SSowjanya Komatineni 	if (soc_data->min_tap_delay && soc_data->max_tap_delay) {
894ea8fc595SSowjanya Komatineni 		min_tap_dly = soc_data->min_tap_delay;
895ea8fc595SSowjanya Komatineni 		max_tap_dly = soc_data->max_tap_delay;
896ea8fc595SSowjanya Komatineni 		clk_rate_mhz = tegra_host->curr_clk_rate / USEC_PER_SEC;
897ea8fc595SSowjanya Komatineni 		period_ps = USEC_PER_SEC / clk_rate_mhz;
898ea8fc595SSowjanya Komatineni 		bestcase = period_ps / min_tap_dly;
899ea8fc595SSowjanya Komatineni 		worstcase = period_ps / max_tap_dly;
900ea8fc595SSowjanya Komatineni 		/*
901ea8fc595SSowjanya Komatineni 		 * Upper and Lower bound thresholds used to detect merged and
902ea8fc595SSowjanya Komatineni 		 * bubble windows
903ea8fc595SSowjanya Komatineni 		 */
904ea8fc595SSowjanya Komatineni 		thdupper = (2 * worstcase + bestcase) / 2;
905ea8fc595SSowjanya Komatineni 		thdlower = worstcase / 4;
906ea8fc595SSowjanya Komatineni 		/*
907ea8fc595SSowjanya Komatineni 		 * fixed tap is used when HW tuning result contains single edge
908ea8fc595SSowjanya Komatineni 		 * and tap is set at fixed tap delay relative to the first edge
909ea8fc595SSowjanya Komatineni 		 */
910ea8fc595SSowjanya Komatineni 		avg_tap_dly = (period_ps * 2) / (min_tap_dly + max_tap_dly);
911ea8fc595SSowjanya Komatineni 		fixed_tap = avg_tap_dly / 2;
912ea8fc595SSowjanya Komatineni 
913ea8fc595SSowjanya Komatineni 		val = sdhci_readl(host, SDHCI_TEGRA_VNDR_TUN_STATUS1);
914ea8fc595SSowjanya Komatineni 		start_tap = val & SDHCI_TEGRA_VNDR_TUN_STATUS1_TAP_MASK;
915ea8fc595SSowjanya Komatineni 		end_tap = (val >> SDHCI_TEGRA_VNDR_TUN_STATUS1_END_TAP_SHIFT) &
916ea8fc595SSowjanya Komatineni 			  SDHCI_TEGRA_VNDR_TUN_STATUS1_TAP_MASK;
917ea8fc595SSowjanya Komatineni 		window_width = end_tap - start_tap;
918ea8fc595SSowjanya Komatineni 		num_iter = host->tuning_loop_count;
919ea8fc595SSowjanya Komatineni 		/*
920ea8fc595SSowjanya Komatineni 		 * partial window includes edges of the tuning range.
921ea8fc595SSowjanya Komatineni 		 * merged window includes more taps so window width is higher
922ea8fc595SSowjanya Komatineni 		 * than upper threshold.
923ea8fc595SSowjanya Komatineni 		 */
924ea8fc595SSowjanya Komatineni 		if (start_tap == 0 || (end_tap == (num_iter - 1)) ||
925ea8fc595SSowjanya Komatineni 		    (end_tap == num_iter - 2) || window_width >= thdupper) {
926ea8fc595SSowjanya Komatineni 			pr_debug("%s: Apply tuning correction\n",
927ea8fc595SSowjanya Komatineni 				 mmc_hostname(host->mmc));
928ea8fc595SSowjanya Komatineni 			tegra_sdhci_tap_correction(host, thdupper, thdlower,
929ea8fc595SSowjanya Komatineni 						   fixed_tap);
930ea8fc595SSowjanya Komatineni 		}
931ea8fc595SSowjanya Komatineni 	}
932ea8fc595SSowjanya Komatineni 
933ea8fc595SSowjanya Komatineni 	tegra_sdhci_set_tap(host, tegra_host->tuned_tap_delay);
934ea8fc595SSowjanya Komatineni }
935ea8fc595SSowjanya Komatineni 
936ea8fc595SSowjanya Komatineni static int tegra_sdhci_execute_hw_tuning(struct mmc_host *mmc, u32 opcode)
937ea8fc595SSowjanya Komatineni {
938ea8fc595SSowjanya Komatineni 	struct sdhci_host *host = mmc_priv(mmc);
939ea8fc595SSowjanya Komatineni 	int err;
940ea8fc595SSowjanya Komatineni 
941ea8fc595SSowjanya Komatineni 	err = sdhci_execute_tuning(mmc, opcode);
942ea8fc595SSowjanya Komatineni 	if (!err && !host->tuning_err)
943ea8fc595SSowjanya Komatineni 		tegra_sdhci_post_tuning(host);
944ea8fc595SSowjanya Komatineni 
945ea8fc595SSowjanya Komatineni 	return err;
946ea8fc595SSowjanya Komatineni }
947ea8fc595SSowjanya Komatineni 
948c2c09678SAapo Vienamo static void tegra_sdhci_set_uhs_signaling(struct sdhci_host *host,
949c2c09678SAapo Vienamo 					  unsigned timing)
950c3c2384cSLucas Stach {
951d4501d8eSAapo Vienamo 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
952d4501d8eSAapo Vienamo 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
953c2c09678SAapo Vienamo 	bool set_default_tap = false;
954f5313aaaSAapo Vienamo 	bool set_dqs_trim = false;
955bc5568bfSAapo Vienamo 	bool do_hs400_dll_cal = false;
956ea8fc595SSowjanya Komatineni 	u8 iter = TRIES_256;
957ea8fc595SSowjanya Komatineni 	u32 val;
958c3c2384cSLucas Stach 
95992cd1667SSowjanya Komatineni 	tegra_host->ddr_signaling = false;
960c2c09678SAapo Vienamo 	switch (timing) {
961c2c09678SAapo Vienamo 	case MMC_TIMING_UHS_SDR50:
962ea8fc595SSowjanya Komatineni 		break;
963c2c09678SAapo Vienamo 	case MMC_TIMING_UHS_SDR104:
964c2c09678SAapo Vienamo 	case MMC_TIMING_MMC_HS200:
965c2c09678SAapo Vienamo 		/* Don't set default tap on tunable modes. */
966ea8fc595SSowjanya Komatineni 		iter = TRIES_128;
967c2c09678SAapo Vienamo 		break;
968f5313aaaSAapo Vienamo 	case MMC_TIMING_MMC_HS400:
969f5313aaaSAapo Vienamo 		set_dqs_trim = true;
970bc5568bfSAapo Vienamo 		do_hs400_dll_cal = true;
971ea8fc595SSowjanya Komatineni 		iter = TRIES_128;
972f5313aaaSAapo Vienamo 		break;
973c2c09678SAapo Vienamo 	case MMC_TIMING_MMC_DDR52:
974c2c09678SAapo Vienamo 	case MMC_TIMING_UHS_DDR50:
975c2c09678SAapo Vienamo 		tegra_host->ddr_signaling = true;
976c2c09678SAapo Vienamo 		set_default_tap = true;
977c2c09678SAapo Vienamo 		break;
978c2c09678SAapo Vienamo 	default:
979c2c09678SAapo Vienamo 		set_default_tap = true;
980c2c09678SAapo Vienamo 		break;
981d4501d8eSAapo Vienamo 	}
982c2c09678SAapo Vienamo 
983ea8fc595SSowjanya Komatineni 	val = sdhci_readl(host, SDHCI_VNDR_TUN_CTRL0_0);
984ea8fc595SSowjanya Komatineni 	val &= ~(SDHCI_VNDR_TUN_CTRL0_TUN_ITER_MASK |
985ea8fc595SSowjanya Komatineni 		 SDHCI_VNDR_TUN_CTRL0_START_TAP_VAL_MASK |
986ea8fc595SSowjanya Komatineni 		 SDHCI_VNDR_TUN_CTRL0_MUL_M_MASK);
987ea8fc595SSowjanya Komatineni 	val |= (iter << SDHCI_VNDR_TUN_CTRL0_TUN_ITER_SHIFT |
988ea8fc595SSowjanya Komatineni 		0 << SDHCI_VNDR_TUN_CTRL0_START_TAP_VAL_SHIFT |
989ea8fc595SSowjanya Komatineni 		1 << SDHCI_VNDR_TUN_CTRL0_MUL_M_SHIFT);
990ea8fc595SSowjanya Komatineni 	sdhci_writel(host, val, SDHCI_VNDR_TUN_CTRL0_0);
991ea8fc595SSowjanya Komatineni 	sdhci_writel(host, 0, SDHCI_TEGRA_VNDR_TUN_CTRL1_0);
992ea8fc595SSowjanya Komatineni 
993ea8fc595SSowjanya Komatineni 	host->tuning_loop_count = (iter == TRIES_128) ? 128 : 256;
994ea8fc595SSowjanya Komatineni 
995c2c09678SAapo Vienamo 	sdhci_set_uhs_signaling(host, timing);
996c2c09678SAapo Vienamo 
997c2c09678SAapo Vienamo 	tegra_sdhci_pad_autocalib(host);
998c2c09678SAapo Vienamo 
999ea8fc595SSowjanya Komatineni 	if (tegra_host->tuned_tap_delay && !set_default_tap)
1000ea8fc595SSowjanya Komatineni 		tegra_sdhci_set_tap(host, tegra_host->tuned_tap_delay);
1001ea8fc595SSowjanya Komatineni 	else
1002c2c09678SAapo Vienamo 		tegra_sdhci_set_tap(host, tegra_host->default_tap);
1003f5313aaaSAapo Vienamo 
1004f5313aaaSAapo Vienamo 	if (set_dqs_trim)
1005f5313aaaSAapo Vienamo 		tegra_sdhci_set_dqs_trim(host, tegra_host->dqs_trim);
1006bc5568bfSAapo Vienamo 
1007bc5568bfSAapo Vienamo 	if (do_hs400_dll_cal)
1008bc5568bfSAapo Vienamo 		tegra_sdhci_hs400_dll_cal(host);
1009c3c2384cSLucas Stach }
1010c3c2384cSLucas Stach 
1011c3c2384cSLucas Stach static int tegra_sdhci_execute_tuning(struct sdhci_host *host, u32 opcode)
1012c3c2384cSLucas Stach {
1013c3c2384cSLucas Stach 	unsigned int min, max;
1014c3c2384cSLucas Stach 
1015c3c2384cSLucas Stach 	/*
1016c3c2384cSLucas Stach 	 * Start search for minimum tap value at 10, as smaller values are
1017c3c2384cSLucas Stach 	 * may wrongly be reported as working but fail at higher speeds,
1018c3c2384cSLucas Stach 	 * according to the TRM.
1019c3c2384cSLucas Stach 	 */
1020c3c2384cSLucas Stach 	min = 10;
1021c3c2384cSLucas Stach 	while (min < 255) {
1022c3c2384cSLucas Stach 		tegra_sdhci_set_tap(host, min);
1023c3c2384cSLucas Stach 		if (!mmc_send_tuning(host->mmc, opcode, NULL))
1024c3c2384cSLucas Stach 			break;
1025c3c2384cSLucas Stach 		min++;
1026c3c2384cSLucas Stach 	}
1027c3c2384cSLucas Stach 
1028c3c2384cSLucas Stach 	/* Find the maximum tap value that still passes. */
1029c3c2384cSLucas Stach 	max = min + 1;
1030c3c2384cSLucas Stach 	while (max < 255) {
1031c3c2384cSLucas Stach 		tegra_sdhci_set_tap(host, max);
1032c3c2384cSLucas Stach 		if (mmc_send_tuning(host->mmc, opcode, NULL)) {
1033c3c2384cSLucas Stach 			max--;
1034c3c2384cSLucas Stach 			break;
1035c3c2384cSLucas Stach 		}
1036c3c2384cSLucas Stach 		max++;
1037c3c2384cSLucas Stach 	}
1038c3c2384cSLucas Stach 
1039c3c2384cSLucas Stach 	/* The TRM states the ideal tap value is at 75% in the passing range. */
1040c3c2384cSLucas Stach 	tegra_sdhci_set_tap(host, min + ((max - min) * 3 / 4));
1041c3c2384cSLucas Stach 
1042c3c2384cSLucas Stach 	return mmc_send_tuning(host->mmc, opcode, NULL);
1043c3c2384cSLucas Stach }
1044c3c2384cSLucas Stach 
104586ac2f8bSAapo Vienamo static int sdhci_tegra_start_signal_voltage_switch(struct mmc_host *mmc,
104686ac2f8bSAapo Vienamo 						   struct mmc_ios *ios)
104786ac2f8bSAapo Vienamo {
104886ac2f8bSAapo Vienamo 	struct sdhci_host *host = mmc_priv(mmc);
104944babea2SAapo Vienamo 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
105044babea2SAapo Vienamo 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
105186ac2f8bSAapo Vienamo 	int ret = 0;
105286ac2f8bSAapo Vienamo 
105386ac2f8bSAapo Vienamo 	if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330) {
1054de25fa5aSSowjanya Komatineni 		ret = tegra_sdhci_set_padctrl(host, ios->signal_voltage, true);
105586ac2f8bSAapo Vienamo 		if (ret < 0)
105686ac2f8bSAapo Vienamo 			return ret;
105786ac2f8bSAapo Vienamo 		ret = sdhci_start_signal_voltage_switch(mmc, ios);
105886ac2f8bSAapo Vienamo 	} else if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180) {
105986ac2f8bSAapo Vienamo 		ret = sdhci_start_signal_voltage_switch(mmc, ios);
106086ac2f8bSAapo Vienamo 		if (ret < 0)
106186ac2f8bSAapo Vienamo 			return ret;
1062de25fa5aSSowjanya Komatineni 		ret = tegra_sdhci_set_padctrl(host, ios->signal_voltage, true);
106386ac2f8bSAapo Vienamo 	}
106486ac2f8bSAapo Vienamo 
106544babea2SAapo Vienamo 	if (tegra_host->pad_calib_required)
106644babea2SAapo Vienamo 		tegra_sdhci_pad_autocalib(host);
106744babea2SAapo Vienamo 
106886ac2f8bSAapo Vienamo 	return ret;
106986ac2f8bSAapo Vienamo }
107086ac2f8bSAapo Vienamo 
107186ac2f8bSAapo Vienamo static int tegra_sdhci_init_pinctrl_info(struct device *dev,
107286ac2f8bSAapo Vienamo 					 struct sdhci_tegra *tegra_host)
107386ac2f8bSAapo Vienamo {
107486ac2f8bSAapo Vienamo 	tegra_host->pinctrl_sdmmc = devm_pinctrl_get(dev);
107586ac2f8bSAapo Vienamo 	if (IS_ERR(tegra_host->pinctrl_sdmmc)) {
107686ac2f8bSAapo Vienamo 		dev_dbg(dev, "No pinctrl info, err: %ld\n",
107786ac2f8bSAapo Vienamo 			PTR_ERR(tegra_host->pinctrl_sdmmc));
107886ac2f8bSAapo Vienamo 		return -1;
107986ac2f8bSAapo Vienamo 	}
108086ac2f8bSAapo Vienamo 
1081de25fa5aSSowjanya Komatineni 	tegra_host->pinctrl_state_1v8_drv = pinctrl_lookup_state(
1082de25fa5aSSowjanya Komatineni 				tegra_host->pinctrl_sdmmc, "sdmmc-1v8-drv");
1083de25fa5aSSowjanya Komatineni 	if (IS_ERR(tegra_host->pinctrl_state_1v8_drv)) {
1084de25fa5aSSowjanya Komatineni 		if (PTR_ERR(tegra_host->pinctrl_state_1v8_drv) == -ENODEV)
1085de25fa5aSSowjanya Komatineni 			tegra_host->pinctrl_state_1v8_drv = NULL;
1086de25fa5aSSowjanya Komatineni 	}
1087de25fa5aSSowjanya Komatineni 
1088de25fa5aSSowjanya Komatineni 	tegra_host->pinctrl_state_3v3_drv = pinctrl_lookup_state(
1089de25fa5aSSowjanya Komatineni 				tegra_host->pinctrl_sdmmc, "sdmmc-3v3-drv");
1090de25fa5aSSowjanya Komatineni 	if (IS_ERR(tegra_host->pinctrl_state_3v3_drv)) {
1091de25fa5aSSowjanya Komatineni 		if (PTR_ERR(tegra_host->pinctrl_state_3v3_drv) == -ENODEV)
1092de25fa5aSSowjanya Komatineni 			tegra_host->pinctrl_state_3v3_drv = NULL;
1093de25fa5aSSowjanya Komatineni 	}
1094de25fa5aSSowjanya Komatineni 
109586ac2f8bSAapo Vienamo 	tegra_host->pinctrl_state_3v3 =
109686ac2f8bSAapo Vienamo 		pinctrl_lookup_state(tegra_host->pinctrl_sdmmc, "sdmmc-3v3");
109786ac2f8bSAapo Vienamo 	if (IS_ERR(tegra_host->pinctrl_state_3v3)) {
109886ac2f8bSAapo Vienamo 		dev_warn(dev, "Missing 3.3V pad state, err: %ld\n",
109986ac2f8bSAapo Vienamo 			 PTR_ERR(tegra_host->pinctrl_state_3v3));
110086ac2f8bSAapo Vienamo 		return -1;
110186ac2f8bSAapo Vienamo 	}
110286ac2f8bSAapo Vienamo 
110386ac2f8bSAapo Vienamo 	tegra_host->pinctrl_state_1v8 =
110486ac2f8bSAapo Vienamo 		pinctrl_lookup_state(tegra_host->pinctrl_sdmmc, "sdmmc-1v8");
110586ac2f8bSAapo Vienamo 	if (IS_ERR(tegra_host->pinctrl_state_1v8)) {
110686ac2f8bSAapo Vienamo 		dev_warn(dev, "Missing 1.8V pad state, err: %ld\n",
1107e5378247SYueHaibing 			 PTR_ERR(tegra_host->pinctrl_state_1v8));
110886ac2f8bSAapo Vienamo 		return -1;
110986ac2f8bSAapo Vienamo 	}
111086ac2f8bSAapo Vienamo 
111186ac2f8bSAapo Vienamo 	tegra_host->pad_control_available = true;
111286ac2f8bSAapo Vienamo 
111386ac2f8bSAapo Vienamo 	return 0;
111486ac2f8bSAapo Vienamo }
111586ac2f8bSAapo Vienamo 
1116e5c63d91SLucas Stach static void tegra_sdhci_voltage_switch(struct sdhci_host *host)
1117e5c63d91SLucas Stach {
1118e5c63d91SLucas Stach 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1119e5c63d91SLucas Stach 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
1120e5c63d91SLucas Stach 	const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
1121e5c63d91SLucas Stach 
1122e5c63d91SLucas Stach 	if (soc_data->nvquirks & NVQUIRK_HAS_PADCALIB)
1123e5c63d91SLucas Stach 		tegra_host->pad_calib_required = true;
1124e5c63d91SLucas Stach }
1125e5c63d91SLucas Stach 
11263c4019f9SSowjanya Komatineni static void sdhci_tegra_cqe_enable(struct mmc_host *mmc)
11273c4019f9SSowjanya Komatineni {
11283c4019f9SSowjanya Komatineni 	struct cqhci_host *cq_host = mmc->cqe_private;
11293c4019f9SSowjanya Komatineni 	u32 cqcfg = 0;
11303c4019f9SSowjanya Komatineni 
11313c4019f9SSowjanya Komatineni 	/*
11323c4019f9SSowjanya Komatineni 	 * Tegra SDMMC Controller design prevents write access to BLOCK_COUNT
11333c4019f9SSowjanya Komatineni 	 * registers when CQE is enabled.
11343c4019f9SSowjanya Komatineni 	 */
11353c4019f9SSowjanya Komatineni 	cqcfg = cqhci_readl(cq_host, CQHCI_CFG);
11363c4019f9SSowjanya Komatineni 	if (cqcfg & CQHCI_ENABLE)
11373c4019f9SSowjanya Komatineni 		cqhci_writel(cq_host, (cqcfg & ~CQHCI_ENABLE), CQHCI_CFG);
11383c4019f9SSowjanya Komatineni 
11393c4019f9SSowjanya Komatineni 	sdhci_cqe_enable(mmc);
11403c4019f9SSowjanya Komatineni 
11413c4019f9SSowjanya Komatineni 	if (cqcfg & CQHCI_ENABLE)
11423c4019f9SSowjanya Komatineni 		cqhci_writel(cq_host, cqcfg, CQHCI_CFG);
11433c4019f9SSowjanya Komatineni }
11443c4019f9SSowjanya Komatineni 
11453c4019f9SSowjanya Komatineni static void sdhci_tegra_dumpregs(struct mmc_host *mmc)
11463c4019f9SSowjanya Komatineni {
11473c4019f9SSowjanya Komatineni 	sdhci_dumpregs(mmc_priv(mmc));
11483c4019f9SSowjanya Komatineni }
11493c4019f9SSowjanya Komatineni 
11503c4019f9SSowjanya Komatineni static u32 sdhci_tegra_cqhci_irq(struct sdhci_host *host, u32 intmask)
11513c4019f9SSowjanya Komatineni {
11523c4019f9SSowjanya Komatineni 	int cmd_error = 0;
11533c4019f9SSowjanya Komatineni 	int data_error = 0;
11543c4019f9SSowjanya Komatineni 
11553c4019f9SSowjanya Komatineni 	if (!sdhci_cqe_irq(host, intmask, &cmd_error, &data_error))
11563c4019f9SSowjanya Komatineni 		return intmask;
11573c4019f9SSowjanya Komatineni 
11583c4019f9SSowjanya Komatineni 	cqhci_irq(host->mmc, intmask, cmd_error, data_error);
11593c4019f9SSowjanya Komatineni 
11603c4019f9SSowjanya Komatineni 	return 0;
11613c4019f9SSowjanya Komatineni }
11623c4019f9SSowjanya Komatineni 
11633c4019f9SSowjanya Komatineni static const struct cqhci_host_ops sdhci_tegra_cqhci_ops = {
11643c4019f9SSowjanya Komatineni 	.enable	= sdhci_tegra_cqe_enable,
11653c4019f9SSowjanya Komatineni 	.disable = sdhci_cqe_disable,
11663c4019f9SSowjanya Komatineni 	.dumpregs = sdhci_tegra_dumpregs,
11673c4019f9SSowjanya Komatineni };
11683c4019f9SSowjanya Komatineni 
1169c915568dSLars-Peter Clausen static const struct sdhci_ops tegra_sdhci_ops = {
117085d6509dSShawn Guo 	.read_w     = tegra_sdhci_readw,
117185d6509dSShawn Guo 	.write_l    = tegra_sdhci_writel,
1172a8e326a9SLucas Stach 	.set_clock  = tegra_sdhci_set_clock,
117314b04c6aSMichał Mirosław 	.set_bus_width = sdhci_set_bus_width,
117403231f9bSRussell King 	.reset      = tegra_sdhci_reset,
1175c3c2384cSLucas Stach 	.platform_execute_tuning = tegra_sdhci_execute_tuning,
1176a8e326a9SLucas Stach 	.set_uhs_signaling = tegra_sdhci_set_uhs_signaling,
1177e5c63d91SLucas Stach 	.voltage_switch = tegra_sdhci_voltage_switch,
117844350993SAapo Vienamo 	.get_max_clock = tegra_sdhci_get_max_clock,
117985d6509dSShawn Guo };
118003d2bfc8SOlof Johansson 
11811db5eebfSLars-Peter Clausen static const struct sdhci_pltfm_data sdhci_tegra20_pdata = {
118285d6509dSShawn Guo 	.quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
118385d6509dSShawn Guo 		  SDHCI_QUIRK_SINGLE_POWER_WRITE |
118485d6509dSShawn Guo 		  SDHCI_QUIRK_NO_HISPD_BIT |
1185f9260355SAndrew Bresticker 		  SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
1186f9260355SAndrew Bresticker 		  SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
118785d6509dSShawn Guo 	.ops  = &tegra_sdhci_ops,
118885d6509dSShawn Guo };
118985d6509dSShawn Guo 
1190d49d19c2SThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra20 = {
11913e44a1a7SStephen Warren 	.pdata = &sdhci_tegra20_pdata,
11923e44a1a7SStephen Warren 	.nvquirks = NVQUIRK_FORCE_SDHCI_SPEC_200 |
11933e44a1a7SStephen Warren 		    NVQUIRK_ENABLE_BLOCK_GAP_DET,
11943e44a1a7SStephen Warren };
11953e44a1a7SStephen Warren 
11961db5eebfSLars-Peter Clausen static const struct sdhci_pltfm_data sdhci_tegra30_pdata = {
11973e44a1a7SStephen Warren 	.quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
11983e44a1a7SStephen Warren 		  SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
11993e44a1a7SStephen Warren 		  SDHCI_QUIRK_SINGLE_POWER_WRITE |
12003e44a1a7SStephen Warren 		  SDHCI_QUIRK_NO_HISPD_BIT |
1201f9260355SAndrew Bresticker 		  SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
1202f9260355SAndrew Bresticker 		  SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
1203127407e3SStefan Agner 	.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
1204726df1d5SStefan Agner 		   SDHCI_QUIRK2_BROKEN_HS200 |
1205726df1d5SStefan Agner 		   /*
1206726df1d5SStefan Agner 		    * Auto-CMD23 leads to "Got command interrupt 0x00010000 even
1207726df1d5SStefan Agner 		    * though no command operation was in progress."
1208726df1d5SStefan Agner 		    *
1209726df1d5SStefan Agner 		    * The exact reason is unknown, as the same hardware seems
1210726df1d5SStefan Agner 		    * to support Auto CMD23 on a downstream 3.1 kernel.
1211726df1d5SStefan Agner 		    */
1212726df1d5SStefan Agner 		   SDHCI_QUIRK2_ACMD23_BROKEN,
12133e44a1a7SStephen Warren 	.ops  = &tegra_sdhci_ops,
12143e44a1a7SStephen Warren };
12153e44a1a7SStephen Warren 
1216d49d19c2SThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra30 = {
12173e44a1a7SStephen Warren 	.pdata = &sdhci_tegra30_pdata,
12183145351aSAndrew Bresticker 	.nvquirks = NVQUIRK_ENABLE_SDHCI_SPEC_300 |
12197ad2ed1dSLucas Stach 		    NVQUIRK_ENABLE_SDR50 |
1220e5c63d91SLucas Stach 		    NVQUIRK_ENABLE_SDR104 |
1221e5c63d91SLucas Stach 		    NVQUIRK_HAS_PADCALIB,
12223e44a1a7SStephen Warren };
12233e44a1a7SStephen Warren 
122401df7ecdSRhyland Klein static const struct sdhci_ops tegra114_sdhci_ops = {
122501df7ecdSRhyland Klein 	.read_w     = tegra_sdhci_readw,
122601df7ecdSRhyland Klein 	.write_w    = tegra_sdhci_writew,
122701df7ecdSRhyland Klein 	.write_l    = tegra_sdhci_writel,
1228a8e326a9SLucas Stach 	.set_clock  = tegra_sdhci_set_clock,
122914b04c6aSMichał Mirosław 	.set_bus_width = sdhci_set_bus_width,
123001df7ecdSRhyland Klein 	.reset      = tegra_sdhci_reset,
1231c3c2384cSLucas Stach 	.platform_execute_tuning = tegra_sdhci_execute_tuning,
1232a8e326a9SLucas Stach 	.set_uhs_signaling = tegra_sdhci_set_uhs_signaling,
1233e5c63d91SLucas Stach 	.voltage_switch = tegra_sdhci_voltage_switch,
123444350993SAapo Vienamo 	.get_max_clock = tegra_sdhci_get_max_clock,
123501df7ecdSRhyland Klein };
123601df7ecdSRhyland Klein 
12371db5eebfSLars-Peter Clausen static const struct sdhci_pltfm_data sdhci_tegra114_pdata = {
12385ebf2552SRhyland Klein 	.quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
12395ebf2552SRhyland Klein 		  SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
12405ebf2552SRhyland Klein 		  SDHCI_QUIRK_SINGLE_POWER_WRITE |
12415ebf2552SRhyland Klein 		  SDHCI_QUIRK_NO_HISPD_BIT |
1242f9260355SAndrew Bresticker 		  SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
1243f9260355SAndrew Bresticker 		  SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
1244a8e326a9SLucas Stach 	.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
124501df7ecdSRhyland Klein 	.ops  = &tegra114_sdhci_ops,
12465ebf2552SRhyland Klein };
12475ebf2552SRhyland Klein 
1248d49d19c2SThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra114 = {
12495ebf2552SRhyland Klein 	.pdata = &sdhci_tegra114_pdata,
12507bf037d6SJon Hunter };
12517bf037d6SJon Hunter 
12524ae12588SThierry Reding static const struct sdhci_pltfm_data sdhci_tegra124_pdata = {
12534ae12588SThierry Reding 	.quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
12544ae12588SThierry Reding 		  SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
12554ae12588SThierry Reding 		  SDHCI_QUIRK_SINGLE_POWER_WRITE |
12564ae12588SThierry Reding 		  SDHCI_QUIRK_NO_HISPD_BIT |
12574ae12588SThierry Reding 		  SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
12584ae12588SThierry Reding 		  SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
12594ae12588SThierry Reding 	.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
12604ae12588SThierry Reding 		   /*
12614ae12588SThierry Reding 		    * The TRM states that the SD/MMC controller found on
12624ae12588SThierry Reding 		    * Tegra124 can address 34 bits (the maximum supported by
12634ae12588SThierry Reding 		    * the Tegra memory controller), but tests show that DMA
12644ae12588SThierry Reding 		    * to or from above 4 GiB doesn't work. This is possibly
12654ae12588SThierry Reding 		    * caused by missing programming, though it's not obvious
12664ae12588SThierry Reding 		    * what sequence is required. Mark 64-bit DMA broken for
12674ae12588SThierry Reding 		    * now to fix this for existing users (e.g. Nyan boards).
12684ae12588SThierry Reding 		    */
12694ae12588SThierry Reding 		   SDHCI_QUIRK2_BROKEN_64_BIT_DMA,
12704ae12588SThierry Reding 	.ops  = &tegra114_sdhci_ops,
12714ae12588SThierry Reding };
12724ae12588SThierry Reding 
12734ae12588SThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra124 = {
12744ae12588SThierry Reding 	.pdata = &sdhci_tegra124_pdata,
12754ae12588SThierry Reding };
12764ae12588SThierry Reding 
12771070e83aSAapo Vienamo static const struct sdhci_ops tegra210_sdhci_ops = {
12781070e83aSAapo Vienamo 	.read_w     = tegra_sdhci_readw,
127938a284d9SAapo Vienamo 	.write_w    = tegra210_sdhci_writew,
12801070e83aSAapo Vienamo 	.write_l    = tegra_sdhci_writel,
12811070e83aSAapo Vienamo 	.set_clock  = tegra_sdhci_set_clock,
12821070e83aSAapo Vienamo 	.set_bus_width = sdhci_set_bus_width,
12831070e83aSAapo Vienamo 	.reset      = tegra_sdhci_reset,
12841070e83aSAapo Vienamo 	.set_uhs_signaling = tegra_sdhci_set_uhs_signaling,
12851070e83aSAapo Vienamo 	.voltage_switch = tegra_sdhci_voltage_switch,
12861070e83aSAapo Vienamo 	.get_max_clock = tegra_sdhci_get_max_clock,
12871070e83aSAapo Vienamo };
12881070e83aSAapo Vienamo 
1289b5a84ecfSThierry Reding static const struct sdhci_pltfm_data sdhci_tegra210_pdata = {
1290b5a84ecfSThierry Reding 	.quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
1291b5a84ecfSThierry Reding 		  SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
1292b5a84ecfSThierry Reding 		  SDHCI_QUIRK_SINGLE_POWER_WRITE |
1293b5a84ecfSThierry Reding 		  SDHCI_QUIRK_NO_HISPD_BIT |
1294a8e326a9SLucas Stach 		  SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
1295a8e326a9SLucas Stach 		  SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
1296a8e326a9SLucas Stach 	.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
12971070e83aSAapo Vienamo 	.ops  = &tegra210_sdhci_ops,
1298b5a84ecfSThierry Reding };
1299b5a84ecfSThierry Reding 
1300b5a84ecfSThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra210 = {
1301b5a84ecfSThierry Reding 	.pdata = &sdhci_tegra210_pdata,
1302d943f6e9SAapo Vienamo 	.nvquirks = NVQUIRK_NEEDS_PAD_CONTROL |
1303d4501d8eSAapo Vienamo 		    NVQUIRK_HAS_PADCALIB |
13043559d4a6SAapo Vienamo 		    NVQUIRK_DIS_CARD_CLK_CONFIG_TAP |
13053559d4a6SAapo Vienamo 		    NVQUIRK_ENABLE_SDR50 |
13063559d4a6SAapo Vienamo 		    NVQUIRK_ENABLE_SDR104,
1307ea8fc595SSowjanya Komatineni 	.min_tap_delay = 106,
1308ea8fc595SSowjanya Komatineni 	.max_tap_delay = 185,
1309b5a84ecfSThierry Reding };
1310b5a84ecfSThierry Reding 
131138a284d9SAapo Vienamo static const struct sdhci_ops tegra186_sdhci_ops = {
131238a284d9SAapo Vienamo 	.read_w     = tegra_sdhci_readw,
131338a284d9SAapo Vienamo 	.write_l    = tegra_sdhci_writel,
131438a284d9SAapo Vienamo 	.set_clock  = tegra_sdhci_set_clock,
131538a284d9SAapo Vienamo 	.set_bus_width = sdhci_set_bus_width,
131638a284d9SAapo Vienamo 	.reset      = tegra_sdhci_reset,
131738a284d9SAapo Vienamo 	.set_uhs_signaling = tegra_sdhci_set_uhs_signaling,
131838a284d9SAapo Vienamo 	.voltage_switch = tegra_sdhci_voltage_switch,
131938a284d9SAapo Vienamo 	.get_max_clock = tegra_sdhci_get_max_clock,
13203c4019f9SSowjanya Komatineni 	.irq = sdhci_tegra_cqhci_irq,
132138a284d9SAapo Vienamo };
132238a284d9SAapo Vienamo 
13234346b7c7SThierry Reding static const struct sdhci_pltfm_data sdhci_tegra186_pdata = {
13244346b7c7SThierry Reding 	.quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
13254346b7c7SThierry Reding 		  SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
13264346b7c7SThierry Reding 		  SDHCI_QUIRK_SINGLE_POWER_WRITE |
13274346b7c7SThierry Reding 		  SDHCI_QUIRK_NO_HISPD_BIT |
13284346b7c7SThierry Reding 		  SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
13294346b7c7SThierry Reding 		  SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
133068481a7eSKrishna Reddy 	.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
133168481a7eSKrishna Reddy 		   /* SDHCI controllers on Tegra186 support 40-bit addressing.
133268481a7eSKrishna Reddy 		    * IOVA addresses are 48-bit wide on Tegra186.
133368481a7eSKrishna Reddy 		    * With 64-bit dma mask used for SDHCI, accesses can
133468481a7eSKrishna Reddy 		    * be broken. Disable 64-bit dma, which would fall back
133568481a7eSKrishna Reddy 		    * to 32-bit dma mask. Ideally 40-bit dma mask would work,
133668481a7eSKrishna Reddy 		    * But it is not supported as of now.
133768481a7eSKrishna Reddy 		    */
133868481a7eSKrishna Reddy 		   SDHCI_QUIRK2_BROKEN_64_BIT_DMA,
133938a284d9SAapo Vienamo 	.ops  = &tegra186_sdhci_ops,
13404346b7c7SThierry Reding };
13414346b7c7SThierry Reding 
13424346b7c7SThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra186 = {
13434346b7c7SThierry Reding 	.pdata = &sdhci_tegra186_pdata,
1344d943f6e9SAapo Vienamo 	.nvquirks = NVQUIRK_NEEDS_PAD_CONTROL |
1345d4501d8eSAapo Vienamo 		    NVQUIRK_HAS_PADCALIB |
13462ad50051SAapo Vienamo 		    NVQUIRK_DIS_CARD_CLK_CONFIG_TAP |
13472ad50051SAapo Vienamo 		    NVQUIRK_ENABLE_SDR50 |
13482ad50051SAapo Vienamo 		    NVQUIRK_ENABLE_SDR104,
1349ea8fc595SSowjanya Komatineni 	.min_tap_delay = 84,
1350ea8fc595SSowjanya Komatineni 	.max_tap_delay = 136,
1351ea8fc595SSowjanya Komatineni };
1352ea8fc595SSowjanya Komatineni 
1353ea8fc595SSowjanya Komatineni static const struct sdhci_tegra_soc_data soc_data_tegra194 = {
1354ea8fc595SSowjanya Komatineni 	.pdata = &sdhci_tegra186_pdata,
1355ea8fc595SSowjanya Komatineni 	.nvquirks = NVQUIRK_NEEDS_PAD_CONTROL |
1356ea8fc595SSowjanya Komatineni 		    NVQUIRK_HAS_PADCALIB |
1357ea8fc595SSowjanya Komatineni 		    NVQUIRK_DIS_CARD_CLK_CONFIG_TAP |
1358ea8fc595SSowjanya Komatineni 		    NVQUIRK_ENABLE_SDR50 |
1359ea8fc595SSowjanya Komatineni 		    NVQUIRK_ENABLE_SDR104,
1360ea8fc595SSowjanya Komatineni 	.min_tap_delay = 96,
1361ea8fc595SSowjanya Komatineni 	.max_tap_delay = 139,
13624346b7c7SThierry Reding };
13634346b7c7SThierry Reding 
1364498d83e7SBill Pemberton static const struct of_device_id sdhci_tegra_dt_match[] = {
1365ea8fc595SSowjanya Komatineni 	{ .compatible = "nvidia,tegra194-sdhci", .data = &soc_data_tegra194 },
13664346b7c7SThierry Reding 	{ .compatible = "nvidia,tegra186-sdhci", .data = &soc_data_tegra186 },
1367b5a84ecfSThierry Reding 	{ .compatible = "nvidia,tegra210-sdhci", .data = &soc_data_tegra210 },
13684ae12588SThierry Reding 	{ .compatible = "nvidia,tegra124-sdhci", .data = &soc_data_tegra124 },
13695ebf2552SRhyland Klein 	{ .compatible = "nvidia,tegra114-sdhci", .data = &soc_data_tegra114 },
13703e44a1a7SStephen Warren 	{ .compatible = "nvidia,tegra30-sdhci", .data = &soc_data_tegra30 },
13713e44a1a7SStephen Warren 	{ .compatible = "nvidia,tegra20-sdhci", .data = &soc_data_tegra20 },
1372275173b2SGrant Likely 	{}
1373275173b2SGrant Likely };
1374e4404fabSArnd Bergmann MODULE_DEVICE_TABLE(of, sdhci_tegra_dt_match);
1375275173b2SGrant Likely 
13763c4019f9SSowjanya Komatineni static int sdhci_tegra_add_host(struct sdhci_host *host)
13773c4019f9SSowjanya Komatineni {
13783c4019f9SSowjanya Komatineni 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
13793c4019f9SSowjanya Komatineni 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
13803c4019f9SSowjanya Komatineni 	struct cqhci_host *cq_host;
13813c4019f9SSowjanya Komatineni 	bool dma64;
13823c4019f9SSowjanya Komatineni 	int ret;
13833c4019f9SSowjanya Komatineni 
13843c4019f9SSowjanya Komatineni 	if (!tegra_host->enable_hwcq)
13853c4019f9SSowjanya Komatineni 		return sdhci_add_host(host);
13863c4019f9SSowjanya Komatineni 
13873c4019f9SSowjanya Komatineni 	sdhci_enable_v4_mode(host);
13883c4019f9SSowjanya Komatineni 
13893c4019f9SSowjanya Komatineni 	ret = sdhci_setup_host(host);
13903c4019f9SSowjanya Komatineni 	if (ret)
13913c4019f9SSowjanya Komatineni 		return ret;
13923c4019f9SSowjanya Komatineni 
13933c4019f9SSowjanya Komatineni 	host->mmc->caps2 |= MMC_CAP2_CQE | MMC_CAP2_CQE_DCMD;
13943c4019f9SSowjanya Komatineni 
13953c4019f9SSowjanya Komatineni 	cq_host = devm_kzalloc(host->mmc->parent,
13963c4019f9SSowjanya Komatineni 				sizeof(*cq_host), GFP_KERNEL);
13973c4019f9SSowjanya Komatineni 	if (!cq_host) {
13983c4019f9SSowjanya Komatineni 		ret = -ENOMEM;
13993c4019f9SSowjanya Komatineni 		goto cleanup;
14003c4019f9SSowjanya Komatineni 	}
14013c4019f9SSowjanya Komatineni 
14023c4019f9SSowjanya Komatineni 	cq_host->mmio = host->ioaddr + SDHCI_TEGRA_CQE_BASE_ADDR;
14033c4019f9SSowjanya Komatineni 	cq_host->ops = &sdhci_tegra_cqhci_ops;
14043c4019f9SSowjanya Komatineni 
14053c4019f9SSowjanya Komatineni 	dma64 = host->flags & SDHCI_USE_64_BIT_DMA;
14063c4019f9SSowjanya Komatineni 	if (dma64)
14073c4019f9SSowjanya Komatineni 		cq_host->caps |= CQHCI_TASK_DESC_SZ_128;
14083c4019f9SSowjanya Komatineni 
14093c4019f9SSowjanya Komatineni 	ret = cqhci_init(cq_host, host->mmc, dma64);
14103c4019f9SSowjanya Komatineni 	if (ret)
14113c4019f9SSowjanya Komatineni 		goto cleanup;
14123c4019f9SSowjanya Komatineni 
14133c4019f9SSowjanya Komatineni 	ret = __sdhci_add_host(host);
14143c4019f9SSowjanya Komatineni 	if (ret)
14153c4019f9SSowjanya Komatineni 		goto cleanup;
14163c4019f9SSowjanya Komatineni 
14173c4019f9SSowjanya Komatineni 	return 0;
14183c4019f9SSowjanya Komatineni 
14193c4019f9SSowjanya Komatineni cleanup:
14203c4019f9SSowjanya Komatineni 	sdhci_cleanup_host(host);
14213c4019f9SSowjanya Komatineni 	return ret;
14223c4019f9SSowjanya Komatineni }
14233c4019f9SSowjanya Komatineni 
1424c3be1efdSBill Pemberton static int sdhci_tegra_probe(struct platform_device *pdev)
142503d2bfc8SOlof Johansson {
14263e44a1a7SStephen Warren 	const struct of_device_id *match;
14273e44a1a7SStephen Warren 	const struct sdhci_tegra_soc_data *soc_data;
14283e44a1a7SStephen Warren 	struct sdhci_host *host;
142985d6509dSShawn Guo 	struct sdhci_pltfm_host *pltfm_host;
14303e44a1a7SStephen Warren 	struct sdhci_tegra *tegra_host;
143103d2bfc8SOlof Johansson 	struct clk *clk;
143203d2bfc8SOlof Johansson 	int rc;
143303d2bfc8SOlof Johansson 
14343e44a1a7SStephen Warren 	match = of_match_device(sdhci_tegra_dt_match, &pdev->dev);
1435b37f9d98SJoseph Lo 	if (!match)
1436b37f9d98SJoseph Lo 		return -EINVAL;
14373e44a1a7SStephen Warren 	soc_data = match->data;
14383e44a1a7SStephen Warren 
14390734e79cSJisheng Zhang 	host = sdhci_pltfm_init(pdev, soc_data->pdata, sizeof(*tegra_host));
144085d6509dSShawn Guo 	if (IS_ERR(host))
144185d6509dSShawn Guo 		return PTR_ERR(host);
144285d6509dSShawn Guo 	pltfm_host = sdhci_priv(host);
144385d6509dSShawn Guo 
14440734e79cSJisheng Zhang 	tegra_host = sdhci_pltfm_priv(pltfm_host);
1445a8e326a9SLucas Stach 	tegra_host->ddr_signaling = false;
1446e5c63d91SLucas Stach 	tegra_host->pad_calib_required = false;
144786ac2f8bSAapo Vienamo 	tegra_host->pad_control_available = false;
14483e44a1a7SStephen Warren 	tegra_host->soc_data = soc_data;
1449275173b2SGrant Likely 
145086ac2f8bSAapo Vienamo 	if (soc_data->nvquirks & NVQUIRK_NEEDS_PAD_CONTROL) {
145186ac2f8bSAapo Vienamo 		rc = tegra_sdhci_init_pinctrl_info(&pdev->dev, tegra_host);
145286ac2f8bSAapo Vienamo 		if (rc == 0)
145386ac2f8bSAapo Vienamo 			host->mmc_host_ops.start_signal_voltage_switch =
145486ac2f8bSAapo Vienamo 				sdhci_tegra_start_signal_voltage_switch;
145586ac2f8bSAapo Vienamo 	}
145686ac2f8bSAapo Vienamo 
145761dad40eSAapo Vienamo 	/* Hook to periodically rerun pad calibration */
145861dad40eSAapo Vienamo 	if (soc_data->nvquirks & NVQUIRK_HAS_PADCALIB)
145961dad40eSAapo Vienamo 		host->mmc_host_ops.request = tegra_sdhci_request;
146061dad40eSAapo Vienamo 
1461dfc9700cSAapo Vienamo 	host->mmc_host_ops.hs400_enhanced_strobe =
1462dfc9700cSAapo Vienamo 			tegra_sdhci_hs400_enhanced_strobe;
1463dfc9700cSAapo Vienamo 
1464ea8fc595SSowjanya Komatineni 	if (!host->ops->platform_execute_tuning)
1465ea8fc595SSowjanya Komatineni 		host->mmc_host_ops.execute_tuning =
1466ea8fc595SSowjanya Komatineni 				tegra_sdhci_execute_hw_tuning;
1467ea8fc595SSowjanya Komatineni 
14682391b340SMylene JOSSERAND 	rc = mmc_of_parse(host->mmc);
146947caa84fSSimon Baatz 	if (rc)
147047caa84fSSimon Baatz 		goto err_parse_dt;
14710e786102SStephen Warren 
14727ad2ed1dSLucas Stach 	if (tegra_host->soc_data->nvquirks & NVQUIRK_ENABLE_DDR50)
1473c3c2384cSLucas Stach 		host->mmc->caps |= MMC_CAP_1_8V_DDR;
1474c3c2384cSLucas Stach 
14753c4019f9SSowjanya Komatineni 	tegra_sdhci_parse_dt(host);
147685c0da17SAapo Vienamo 
14772391b340SMylene JOSSERAND 	tegra_host->power_gpio = devm_gpiod_get_optional(&pdev->dev, "power",
14782391b340SMylene JOSSERAND 							 GPIOD_OUT_HIGH);
14792391b340SMylene JOSSERAND 	if (IS_ERR(tegra_host->power_gpio)) {
14802391b340SMylene JOSSERAND 		rc = PTR_ERR(tegra_host->power_gpio);
148185d6509dSShawn Guo 		goto err_power_req;
148203d2bfc8SOlof Johansson 	}
148303d2bfc8SOlof Johansson 
1484e4f79d9cSKevin Hao 	clk = devm_clk_get(mmc_dev(host->mmc), NULL);
148503d2bfc8SOlof Johansson 	if (IS_ERR(clk)) {
148603d2bfc8SOlof Johansson 		dev_err(mmc_dev(host->mmc), "clk err\n");
148703d2bfc8SOlof Johansson 		rc = PTR_ERR(clk);
148885d6509dSShawn Guo 		goto err_clk_get;
148903d2bfc8SOlof Johansson 	}
14901e674bc6SPrashant Gaikwad 	clk_prepare_enable(clk);
149103d2bfc8SOlof Johansson 	pltfm_host->clk = clk;
149203d2bfc8SOlof Johansson 
14932cd6c49dSPhilipp Zabel 	tegra_host->rst = devm_reset_control_get_exclusive(&pdev->dev,
14942cd6c49dSPhilipp Zabel 							   "sdhci");
149520567be9SThierry Reding 	if (IS_ERR(tegra_host->rst)) {
149620567be9SThierry Reding 		rc = PTR_ERR(tegra_host->rst);
149720567be9SThierry Reding 		dev_err(&pdev->dev, "failed to get reset control: %d\n", rc);
149820567be9SThierry Reding 		goto err_rst_get;
149920567be9SThierry Reding 	}
150020567be9SThierry Reding 
150120567be9SThierry Reding 	rc = reset_control_assert(tegra_host->rst);
150220567be9SThierry Reding 	if (rc)
150320567be9SThierry Reding 		goto err_rst_get;
150420567be9SThierry Reding 
150520567be9SThierry Reding 	usleep_range(2000, 4000);
150620567be9SThierry Reding 
150720567be9SThierry Reding 	rc = reset_control_deassert(tegra_host->rst);
150820567be9SThierry Reding 	if (rc)
150920567be9SThierry Reding 		goto err_rst_get;
151020567be9SThierry Reding 
151120567be9SThierry Reding 	usleep_range(2000, 4000);
151220567be9SThierry Reding 
15133c4019f9SSowjanya Komatineni 	rc = sdhci_tegra_add_host(host);
151485d6509dSShawn Guo 	if (rc)
151585d6509dSShawn Guo 		goto err_add_host;
151685d6509dSShawn Guo 
151703d2bfc8SOlof Johansson 	return 0;
151803d2bfc8SOlof Johansson 
151985d6509dSShawn Guo err_add_host:
152020567be9SThierry Reding 	reset_control_assert(tegra_host->rst);
152120567be9SThierry Reding err_rst_get:
15221e674bc6SPrashant Gaikwad 	clk_disable_unprepare(pltfm_host->clk);
152385d6509dSShawn Guo err_clk_get:
152485d6509dSShawn Guo err_power_req:
152547caa84fSSimon Baatz err_parse_dt:
152685d6509dSShawn Guo 	sdhci_pltfm_free(pdev);
152703d2bfc8SOlof Johansson 	return rc;
152803d2bfc8SOlof Johansson }
152903d2bfc8SOlof Johansson 
153020567be9SThierry Reding static int sdhci_tegra_remove(struct platform_device *pdev)
153120567be9SThierry Reding {
153220567be9SThierry Reding 	struct sdhci_host *host = platform_get_drvdata(pdev);
153320567be9SThierry Reding 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
153420567be9SThierry Reding 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
153520567be9SThierry Reding 
153620567be9SThierry Reding 	sdhci_remove_host(host, 0);
153720567be9SThierry Reding 
153820567be9SThierry Reding 	reset_control_assert(tegra_host->rst);
153920567be9SThierry Reding 	usleep_range(2000, 4000);
154020567be9SThierry Reding 	clk_disable_unprepare(pltfm_host->clk);
154120567be9SThierry Reding 
154220567be9SThierry Reding 	sdhci_pltfm_free(pdev);
154320567be9SThierry Reding 
154420567be9SThierry Reding 	return 0;
154520567be9SThierry Reding }
154620567be9SThierry Reding 
154785d6509dSShawn Guo static struct platform_driver sdhci_tegra_driver = {
154885d6509dSShawn Guo 	.driver		= {
154985d6509dSShawn Guo 		.name	= "sdhci-tegra",
1550275173b2SGrant Likely 		.of_match_table = sdhci_tegra_dt_match,
1551fa243f64SUlf Hansson 		.pm	= &sdhci_pltfm_pmops,
155285d6509dSShawn Guo 	},
155385d6509dSShawn Guo 	.probe		= sdhci_tegra_probe,
155420567be9SThierry Reding 	.remove		= sdhci_tegra_remove,
155503d2bfc8SOlof Johansson };
155603d2bfc8SOlof Johansson 
1557d1f81a64SAxel Lin module_platform_driver(sdhci_tegra_driver);
155885d6509dSShawn Guo 
155985d6509dSShawn Guo MODULE_DESCRIPTION("SDHCI driver for Tegra");
156085d6509dSShawn Guo MODULE_AUTHOR("Google, Inc.");
156185d6509dSShawn Guo MODULE_LICENSE("GPL v2");
1562