xref: /openbmc/linux/drivers/mmc/host/sdhci-tegra.c (revision 71c733c4)
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)
109c6e7ab90SSowjanya Komatineni #define NVQUIRK_CQHCI_DCMD_R1B_CMD_TIMING		BIT(9)
1103e44a1a7SStephen Warren 
1113c4019f9SSowjanya Komatineni /* SDMMC CQE Base Address for Tegra Host Ver 4.1 and Higher */
1123c4019f9SSowjanya Komatineni #define SDHCI_TEGRA_CQE_BASE_ADDR			0xF000
1133c4019f9SSowjanya Komatineni 
1143e44a1a7SStephen Warren struct sdhci_tegra_soc_data {
1151db5eebfSLars-Peter Clausen 	const struct sdhci_pltfm_data *pdata;
1163e44a1a7SStephen Warren 	u32 nvquirks;
117ea8fc595SSowjanya Komatineni 	u8 min_tap_delay;
118ea8fc595SSowjanya Komatineni 	u8 max_tap_delay;
1193e44a1a7SStephen Warren };
1203e44a1a7SStephen Warren 
12151b77c8eSAapo Vienamo /* Magic pull up and pull down pad calibration offsets */
12251b77c8eSAapo Vienamo struct sdhci_tegra_autocal_offsets {
12351b77c8eSAapo Vienamo 	u32 pull_up_3v3;
12451b77c8eSAapo Vienamo 	u32 pull_down_3v3;
12551b77c8eSAapo Vienamo 	u32 pull_up_3v3_timeout;
12651b77c8eSAapo Vienamo 	u32 pull_down_3v3_timeout;
12751b77c8eSAapo Vienamo 	u32 pull_up_1v8;
12851b77c8eSAapo Vienamo 	u32 pull_down_1v8;
12951b77c8eSAapo Vienamo 	u32 pull_up_1v8_timeout;
13051b77c8eSAapo Vienamo 	u32 pull_down_1v8_timeout;
13151b77c8eSAapo Vienamo 	u32 pull_up_sdr104;
13251b77c8eSAapo Vienamo 	u32 pull_down_sdr104;
13351b77c8eSAapo Vienamo 	u32 pull_up_hs400;
13451b77c8eSAapo Vienamo 	u32 pull_down_hs400;
13551b77c8eSAapo Vienamo };
13651b77c8eSAapo Vienamo 
1373e44a1a7SStephen Warren struct sdhci_tegra {
1383e44a1a7SStephen Warren 	const struct sdhci_tegra_soc_data *soc_data;
1392391b340SMylene JOSSERAND 	struct gpio_desc *power_gpio;
140a8e326a9SLucas Stach 	bool ddr_signaling;
141e5c63d91SLucas Stach 	bool pad_calib_required;
14286ac2f8bSAapo Vienamo 	bool pad_control_available;
14320567be9SThierry Reding 
14420567be9SThierry Reding 	struct reset_control *rst;
14586ac2f8bSAapo Vienamo 	struct pinctrl *pinctrl_sdmmc;
14686ac2f8bSAapo Vienamo 	struct pinctrl_state *pinctrl_state_3v3;
14786ac2f8bSAapo Vienamo 	struct pinctrl_state *pinctrl_state_1v8;
148de25fa5aSSowjanya Komatineni 	struct pinctrl_state *pinctrl_state_3v3_drv;
149de25fa5aSSowjanya Komatineni 	struct pinctrl_state *pinctrl_state_1v8_drv;
15051b77c8eSAapo Vienamo 
15151b77c8eSAapo Vienamo 	struct sdhci_tegra_autocal_offsets autocal_offsets;
15261dad40eSAapo Vienamo 	ktime_t last_calib;
15385c0da17SAapo Vienamo 
15485c0da17SAapo Vienamo 	u32 default_tap;
15585c0da17SAapo Vienamo 	u32 default_trim;
156f5313aaaSAapo Vienamo 	u32 dqs_trim;
1573c4019f9SSowjanya Komatineni 	bool enable_hwcq;
158ea8fc595SSowjanya Komatineni 	unsigned long curr_clk_rate;
159ea8fc595SSowjanya Komatineni 	u8 tuned_tap_delay;
1603e44a1a7SStephen Warren };
1613e44a1a7SStephen Warren 
16203d2bfc8SOlof Johansson static u16 tegra_sdhci_readw(struct sdhci_host *host, int reg)
16303d2bfc8SOlof Johansson {
1643e44a1a7SStephen Warren 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1650734e79cSJisheng Zhang 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
1663e44a1a7SStephen Warren 	const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
1673e44a1a7SStephen Warren 
1683e44a1a7SStephen Warren 	if (unlikely((soc_data->nvquirks & NVQUIRK_FORCE_SDHCI_SPEC_200) &&
1693e44a1a7SStephen Warren 			(reg == SDHCI_HOST_VERSION))) {
17003d2bfc8SOlof Johansson 		/* Erratum: Version register is invalid in HW. */
17103d2bfc8SOlof Johansson 		return SDHCI_SPEC_200;
17203d2bfc8SOlof Johansson 	}
17303d2bfc8SOlof Johansson 
17403d2bfc8SOlof Johansson 	return readw(host->ioaddr + reg);
17503d2bfc8SOlof Johansson }
17603d2bfc8SOlof Johansson 
177352ee868SPavan Kunapuli static void tegra_sdhci_writew(struct sdhci_host *host, u16 val, int reg)
178352ee868SPavan Kunapuli {
179352ee868SPavan Kunapuli 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
180352ee868SPavan Kunapuli 
181352ee868SPavan Kunapuli 	switch (reg) {
182352ee868SPavan Kunapuli 	case SDHCI_TRANSFER_MODE:
183352ee868SPavan Kunapuli 		/*
184352ee868SPavan Kunapuli 		 * Postpone this write, we must do it together with a
185352ee868SPavan Kunapuli 		 * command write that is down below.
186352ee868SPavan Kunapuli 		 */
187352ee868SPavan Kunapuli 		pltfm_host->xfer_mode_shadow = val;
188352ee868SPavan Kunapuli 		return;
189352ee868SPavan Kunapuli 	case SDHCI_COMMAND:
190352ee868SPavan Kunapuli 		writel((val << 16) | pltfm_host->xfer_mode_shadow,
191352ee868SPavan Kunapuli 			host->ioaddr + SDHCI_TRANSFER_MODE);
192352ee868SPavan Kunapuli 		return;
193352ee868SPavan Kunapuli 	}
194352ee868SPavan Kunapuli 
195352ee868SPavan Kunapuli 	writew(val, host->ioaddr + reg);
196352ee868SPavan Kunapuli }
197352ee868SPavan Kunapuli 
19803d2bfc8SOlof Johansson static void tegra_sdhci_writel(struct sdhci_host *host, u32 val, int reg)
19903d2bfc8SOlof Johansson {
2003e44a1a7SStephen Warren 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
2010734e79cSJisheng Zhang 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
2023e44a1a7SStephen Warren 	const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
2033e44a1a7SStephen Warren 
20403d2bfc8SOlof Johansson 	/* Seems like we're getting spurious timeout and crc errors, so
20503d2bfc8SOlof Johansson 	 * disable signalling of them. In case of real errors software
20603d2bfc8SOlof Johansson 	 * timers should take care of eventually detecting them.
20703d2bfc8SOlof Johansson 	 */
20803d2bfc8SOlof Johansson 	if (unlikely(reg == SDHCI_SIGNAL_ENABLE))
20903d2bfc8SOlof Johansson 		val &= ~(SDHCI_INT_TIMEOUT|SDHCI_INT_CRC);
21003d2bfc8SOlof Johansson 
21103d2bfc8SOlof Johansson 	writel(val, host->ioaddr + reg);
21203d2bfc8SOlof Johansson 
2133e44a1a7SStephen Warren 	if (unlikely((soc_data->nvquirks & NVQUIRK_ENABLE_BLOCK_GAP_DET) &&
2143e44a1a7SStephen Warren 			(reg == SDHCI_INT_ENABLE))) {
21503d2bfc8SOlof Johansson 		/* Erratum: Must enable block gap interrupt detection */
21603d2bfc8SOlof Johansson 		u8 gap_ctrl = readb(host->ioaddr + SDHCI_BLOCK_GAP_CONTROL);
21703d2bfc8SOlof Johansson 		if (val & SDHCI_INT_CARD_INT)
21803d2bfc8SOlof Johansson 			gap_ctrl |= 0x8;
21903d2bfc8SOlof Johansson 		else
22003d2bfc8SOlof Johansson 			gap_ctrl &= ~0x8;
22103d2bfc8SOlof Johansson 		writeb(gap_ctrl, host->ioaddr + SDHCI_BLOCK_GAP_CONTROL);
22203d2bfc8SOlof Johansson 	}
22303d2bfc8SOlof Johansson }
22403d2bfc8SOlof Johansson 
22538a284d9SAapo Vienamo static bool tegra_sdhci_configure_card_clk(struct sdhci_host *host, bool enable)
22638a284d9SAapo Vienamo {
22738a284d9SAapo Vienamo 	bool status;
22838a284d9SAapo Vienamo 	u32 reg;
22938a284d9SAapo Vienamo 
23038a284d9SAapo Vienamo 	reg = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
23138a284d9SAapo Vienamo 	status = !!(reg & SDHCI_CLOCK_CARD_EN);
23238a284d9SAapo Vienamo 
23338a284d9SAapo Vienamo 	if (status == enable)
23438a284d9SAapo Vienamo 		return status;
23538a284d9SAapo Vienamo 
23638a284d9SAapo Vienamo 	if (enable)
23738a284d9SAapo Vienamo 		reg |= SDHCI_CLOCK_CARD_EN;
23838a284d9SAapo Vienamo 	else
23938a284d9SAapo Vienamo 		reg &= ~SDHCI_CLOCK_CARD_EN;
24038a284d9SAapo Vienamo 
24138a284d9SAapo Vienamo 	sdhci_writew(host, reg, SDHCI_CLOCK_CONTROL);
24238a284d9SAapo Vienamo 
24338a284d9SAapo Vienamo 	return status;
24438a284d9SAapo Vienamo }
24538a284d9SAapo Vienamo 
24638a284d9SAapo Vienamo static void tegra210_sdhci_writew(struct sdhci_host *host, u16 val, int reg)
24738a284d9SAapo Vienamo {
24838a284d9SAapo Vienamo 	bool is_tuning_cmd = 0;
24938a284d9SAapo Vienamo 	bool clk_enabled;
25038a284d9SAapo Vienamo 	u8 cmd;
25138a284d9SAapo Vienamo 
25238a284d9SAapo Vienamo 	if (reg == SDHCI_COMMAND) {
25338a284d9SAapo Vienamo 		cmd = SDHCI_GET_CMD(val);
25438a284d9SAapo Vienamo 		is_tuning_cmd = cmd == MMC_SEND_TUNING_BLOCK ||
25538a284d9SAapo Vienamo 				cmd == MMC_SEND_TUNING_BLOCK_HS200;
25638a284d9SAapo Vienamo 	}
25738a284d9SAapo Vienamo 
25838a284d9SAapo Vienamo 	if (is_tuning_cmd)
25938a284d9SAapo Vienamo 		clk_enabled = tegra_sdhci_configure_card_clk(host, 0);
26038a284d9SAapo Vienamo 
26138a284d9SAapo Vienamo 	writew(val, host->ioaddr + reg);
26238a284d9SAapo Vienamo 
26338a284d9SAapo Vienamo 	if (is_tuning_cmd) {
26438a284d9SAapo Vienamo 		udelay(1);
265ea8fc595SSowjanya Komatineni 		sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
26638a284d9SAapo Vienamo 		tegra_sdhci_configure_card_clk(host, clk_enabled);
26738a284d9SAapo Vienamo 	}
26838a284d9SAapo Vienamo }
26938a284d9SAapo Vienamo 
27086ac2f8bSAapo Vienamo static bool tegra_sdhci_is_pad_and_regulator_valid(struct sdhci_host *host)
27186ac2f8bSAapo Vienamo {
27286ac2f8bSAapo Vienamo 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
27386ac2f8bSAapo Vienamo 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
27486ac2f8bSAapo Vienamo 	int has_1v8, has_3v3;
27586ac2f8bSAapo Vienamo 
27686ac2f8bSAapo Vienamo 	/*
27786ac2f8bSAapo Vienamo 	 * The SoCs which have NVQUIRK_NEEDS_PAD_CONTROL require software pad
27886ac2f8bSAapo Vienamo 	 * voltage configuration in order to perform voltage switching. This
27986ac2f8bSAapo Vienamo 	 * means that valid pinctrl info is required on SDHCI instances capable
28086ac2f8bSAapo Vienamo 	 * of performing voltage switching. Whether or not an SDHCI instance is
28186ac2f8bSAapo Vienamo 	 * capable of voltage switching is determined based on the regulator.
28286ac2f8bSAapo Vienamo 	 */
28386ac2f8bSAapo Vienamo 
28486ac2f8bSAapo Vienamo 	if (!(tegra_host->soc_data->nvquirks & NVQUIRK_NEEDS_PAD_CONTROL))
28586ac2f8bSAapo Vienamo 		return true;
28686ac2f8bSAapo Vienamo 
28786ac2f8bSAapo Vienamo 	if (IS_ERR(host->mmc->supply.vqmmc))
28886ac2f8bSAapo Vienamo 		return false;
28986ac2f8bSAapo Vienamo 
29086ac2f8bSAapo Vienamo 	has_1v8 = regulator_is_supported_voltage(host->mmc->supply.vqmmc,
29186ac2f8bSAapo Vienamo 						 1700000, 1950000);
29286ac2f8bSAapo Vienamo 
29386ac2f8bSAapo Vienamo 	has_3v3 = regulator_is_supported_voltage(host->mmc->supply.vqmmc,
29486ac2f8bSAapo Vienamo 						 2700000, 3600000);
29586ac2f8bSAapo Vienamo 
29686ac2f8bSAapo Vienamo 	if (has_1v8 == 1 && has_3v3 == 1)
29786ac2f8bSAapo Vienamo 		return tegra_host->pad_control_available;
29886ac2f8bSAapo Vienamo 
29986ac2f8bSAapo Vienamo 	/* Fixed voltage, no pad control required. */
30086ac2f8bSAapo Vienamo 	return true;
30186ac2f8bSAapo Vienamo }
30286ac2f8bSAapo Vienamo 
303c2c09678SAapo Vienamo static void tegra_sdhci_set_tap(struct sdhci_host *host, unsigned int tap)
304c2c09678SAapo Vienamo {
305c2c09678SAapo Vienamo 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
306c2c09678SAapo Vienamo 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
307c2c09678SAapo Vienamo 	const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
308c2c09678SAapo Vienamo 	bool card_clk_enabled = false;
309c2c09678SAapo Vienamo 	u32 reg;
310c2c09678SAapo Vienamo 
311c2c09678SAapo Vienamo 	/*
312c2c09678SAapo Vienamo 	 * Touching the tap values is a bit tricky on some SoC generations.
313c2c09678SAapo Vienamo 	 * The quirk enables a workaround for a glitch that sometimes occurs if
314c2c09678SAapo Vienamo 	 * the tap values are changed.
315c2c09678SAapo Vienamo 	 */
316c2c09678SAapo Vienamo 
317c2c09678SAapo Vienamo 	if (soc_data->nvquirks & NVQUIRK_DIS_CARD_CLK_CONFIG_TAP)
318c2c09678SAapo Vienamo 		card_clk_enabled = tegra_sdhci_configure_card_clk(host, false);
319c2c09678SAapo Vienamo 
320c2c09678SAapo Vienamo 	reg = sdhci_readl(host, SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
321c2c09678SAapo Vienamo 	reg &= ~SDHCI_CLOCK_CTRL_TAP_MASK;
322c2c09678SAapo Vienamo 	reg |= tap << SDHCI_CLOCK_CTRL_TAP_SHIFT;
323c2c09678SAapo Vienamo 	sdhci_writel(host, reg, SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
324c2c09678SAapo Vienamo 
325c2c09678SAapo Vienamo 	if (soc_data->nvquirks & NVQUIRK_DIS_CARD_CLK_CONFIG_TAP &&
326c2c09678SAapo Vienamo 	    card_clk_enabled) {
327c2c09678SAapo Vienamo 		udelay(1);
328c2c09678SAapo Vienamo 		sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
329c2c09678SAapo Vienamo 		tegra_sdhci_configure_card_clk(host, card_clk_enabled);
330c2c09678SAapo Vienamo 	}
331c2c09678SAapo Vienamo }
332c2c09678SAapo Vienamo 
333dfc9700cSAapo Vienamo static void tegra_sdhci_hs400_enhanced_strobe(struct mmc_host *mmc,
334dfc9700cSAapo Vienamo 					      struct mmc_ios *ios)
335dfc9700cSAapo Vienamo {
336dfc9700cSAapo Vienamo 	struct sdhci_host *host = mmc_priv(mmc);
337dfc9700cSAapo Vienamo 	u32 val;
338dfc9700cSAapo Vienamo 
339dfc9700cSAapo Vienamo 	val = sdhci_readl(host, SDHCI_TEGRA_VENDOR_SYS_SW_CTRL);
340dfc9700cSAapo Vienamo 
341dfc9700cSAapo Vienamo 	if (ios->enhanced_strobe)
342dfc9700cSAapo Vienamo 		val |= SDHCI_TEGRA_SYS_SW_CTRL_ENHANCED_STROBE;
343dfc9700cSAapo Vienamo 	else
344dfc9700cSAapo Vienamo 		val &= ~SDHCI_TEGRA_SYS_SW_CTRL_ENHANCED_STROBE;
345dfc9700cSAapo Vienamo 
346dfc9700cSAapo Vienamo 	sdhci_writel(host, val, SDHCI_TEGRA_VENDOR_SYS_SW_CTRL);
347dfc9700cSAapo Vienamo 
348dfc9700cSAapo Vienamo }
349dfc9700cSAapo Vienamo 
35003231f9bSRussell King static void tegra_sdhci_reset(struct sdhci_host *host, u8 mask)
351ca5879d3SPavan Kunapuli {
352ca5879d3SPavan Kunapuli 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
3530734e79cSJisheng Zhang 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
354ca5879d3SPavan Kunapuli 	const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
3559d548f11SAapo Vienamo 	u32 misc_ctrl, clk_ctrl, pad_ctrl;
356ca5879d3SPavan Kunapuli 
35703231f9bSRussell King 	sdhci_reset(host, mask);
35803231f9bSRussell King 
359ca5879d3SPavan Kunapuli 	if (!(mask & SDHCI_RESET_ALL))
360ca5879d3SPavan Kunapuli 		return;
361ca5879d3SPavan Kunapuli 
362c2c09678SAapo Vienamo 	tegra_sdhci_set_tap(host, tegra_host->default_tap);
363c2c09678SAapo Vienamo 
3641b84def8SLucas Stach 	misc_ctrl = sdhci_readl(host, SDHCI_TEGRA_VENDOR_MISC_CTRL);
3654f6aa326SJon Hunter 	clk_ctrl = sdhci_readl(host, SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
3664f6aa326SJon Hunter 
3674f6aa326SJon Hunter 	misc_ctrl &= ~(SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300 |
3684f6aa326SJon Hunter 		       SDHCI_MISC_CTRL_ENABLE_SDR50 |
3694f6aa326SJon Hunter 		       SDHCI_MISC_CTRL_ENABLE_DDR50 |
3704f6aa326SJon Hunter 		       SDHCI_MISC_CTRL_ENABLE_SDR104);
3714f6aa326SJon Hunter 
37241a0b8d7SAapo Vienamo 	clk_ctrl &= ~(SDHCI_CLOCK_CTRL_TRIM_MASK |
37341a0b8d7SAapo Vienamo 		      SDHCI_CLOCK_CTRL_SPI_MODE_CLKEN_OVERRIDE);
3744f6aa326SJon Hunter 
37586ac2f8bSAapo Vienamo 	if (tegra_sdhci_is_pad_and_regulator_valid(host)) {
376ca5879d3SPavan Kunapuli 		/* Erratum: Enable SDHCI spec v3.00 support */
3773145351aSAndrew Bresticker 		if (soc_data->nvquirks & NVQUIRK_ENABLE_SDHCI_SPEC_300)
378ca5879d3SPavan Kunapuli 			misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300;
3797ad2ed1dSLucas Stach 		/* Advertise UHS modes as supported by host */
3807ad2ed1dSLucas Stach 		if (soc_data->nvquirks & NVQUIRK_ENABLE_SDR50)
3817ad2ed1dSLucas Stach 			misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDR50;
3827ad2ed1dSLucas Stach 		if (soc_data->nvquirks & NVQUIRK_ENABLE_DDR50)
3837ad2ed1dSLucas Stach 			misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_DDR50;
3847ad2ed1dSLucas Stach 		if (soc_data->nvquirks & NVQUIRK_ENABLE_SDR104)
3857ad2ed1dSLucas Stach 			misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDR104;
3867ad2ed1dSLucas Stach 		if (soc_data->nvquirks & SDHCI_MISC_CTRL_ENABLE_SDR50)
387c3c2384cSLucas Stach 			clk_ctrl |= SDHCI_CLOCK_CTRL_SDR50_TUNING_OVERRIDE;
3884f6aa326SJon Hunter 	}
3894f6aa326SJon Hunter 
39041a0b8d7SAapo Vienamo 	clk_ctrl |= tegra_host->default_trim << SDHCI_CLOCK_CTRL_TRIM_SHIFT;
39141a0b8d7SAapo Vienamo 
3924f6aa326SJon Hunter 	sdhci_writel(host, misc_ctrl, SDHCI_TEGRA_VENDOR_MISC_CTRL);
39374cd42bcSLucas Stach 	sdhci_writel(host, clk_ctrl, SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
39474cd42bcSLucas Stach 
3959d548f11SAapo Vienamo 	if (soc_data->nvquirks & NVQUIRK_HAS_PADCALIB) {
3969d548f11SAapo Vienamo 		pad_ctrl = sdhci_readl(host, SDHCI_TEGRA_SDMEM_COMP_PADCTRL);
3979d548f11SAapo Vienamo 		pad_ctrl &= ~SDHCI_TEGRA_SDMEM_COMP_PADCTRL_VREF_SEL_MASK;
3989d548f11SAapo Vienamo 		pad_ctrl |= SDHCI_TEGRA_SDMEM_COMP_PADCTRL_VREF_SEL_VAL;
3999d548f11SAapo Vienamo 		sdhci_writel(host, pad_ctrl, SDHCI_TEGRA_SDMEM_COMP_PADCTRL);
4009d548f11SAapo Vienamo 
401e5c63d91SLucas Stach 		tegra_host->pad_calib_required = true;
4029d548f11SAapo Vienamo 	}
403e5c63d91SLucas Stach 
404a8e326a9SLucas Stach 	tegra_host->ddr_signaling = false;
405ca5879d3SPavan Kunapuli }
406ca5879d3SPavan Kunapuli 
407212b0cf1SAapo Vienamo static void tegra_sdhci_configure_cal_pad(struct sdhci_host *host, bool enable)
408212b0cf1SAapo Vienamo {
409212b0cf1SAapo Vienamo 	u32 val;
410212b0cf1SAapo Vienamo 
411212b0cf1SAapo Vienamo 	/*
412212b0cf1SAapo Vienamo 	 * Enable or disable the additional I/O pad used by the drive strength
413212b0cf1SAapo Vienamo 	 * calibration process.
414212b0cf1SAapo Vienamo 	 */
415212b0cf1SAapo Vienamo 	val = sdhci_readl(host, SDHCI_TEGRA_SDMEM_COMP_PADCTRL);
416212b0cf1SAapo Vienamo 
417212b0cf1SAapo Vienamo 	if (enable)
418212b0cf1SAapo Vienamo 		val |= SDHCI_TEGRA_SDMEM_COMP_PADCTRL_E_INPUT_E_PWRD;
419212b0cf1SAapo Vienamo 	else
420212b0cf1SAapo Vienamo 		val &= ~SDHCI_TEGRA_SDMEM_COMP_PADCTRL_E_INPUT_E_PWRD;
421212b0cf1SAapo Vienamo 
422212b0cf1SAapo Vienamo 	sdhci_writel(host, val, SDHCI_TEGRA_SDMEM_COMP_PADCTRL);
423212b0cf1SAapo Vienamo 
424212b0cf1SAapo Vienamo 	if (enable)
425212b0cf1SAapo Vienamo 		usleep_range(1, 2);
426212b0cf1SAapo Vienamo }
427212b0cf1SAapo Vienamo 
42851b77c8eSAapo Vienamo static void tegra_sdhci_set_pad_autocal_offset(struct sdhci_host *host,
42951b77c8eSAapo Vienamo 					       u16 pdpu)
43051b77c8eSAapo Vienamo {
43151b77c8eSAapo Vienamo 	u32 reg;
43251b77c8eSAapo Vienamo 
43351b77c8eSAapo Vienamo 	reg = sdhci_readl(host, SDHCI_TEGRA_AUTO_CAL_CONFIG);
43451b77c8eSAapo Vienamo 	reg &= ~SDHCI_AUTO_CAL_PDPU_OFFSET_MASK;
43551b77c8eSAapo Vienamo 	reg |= pdpu;
43651b77c8eSAapo Vienamo 	sdhci_writel(host, reg, SDHCI_TEGRA_AUTO_CAL_CONFIG);
43751b77c8eSAapo Vienamo }
43851b77c8eSAapo Vienamo 
439de25fa5aSSowjanya Komatineni static int tegra_sdhci_set_padctrl(struct sdhci_host *host, int voltage,
440de25fa5aSSowjanya Komatineni 				   bool state_drvupdn)
441de25fa5aSSowjanya Komatineni {
442de25fa5aSSowjanya Komatineni 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
443de25fa5aSSowjanya Komatineni 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
444de25fa5aSSowjanya Komatineni 	struct sdhci_tegra_autocal_offsets *offsets =
445de25fa5aSSowjanya Komatineni 						&tegra_host->autocal_offsets;
446de25fa5aSSowjanya Komatineni 	struct pinctrl_state *pinctrl_drvupdn = NULL;
447de25fa5aSSowjanya Komatineni 	int ret = 0;
448de25fa5aSSowjanya Komatineni 	u8 drvup = 0, drvdn = 0;
449de25fa5aSSowjanya Komatineni 	u32 reg;
450de25fa5aSSowjanya Komatineni 
451de25fa5aSSowjanya Komatineni 	if (!state_drvupdn) {
452de25fa5aSSowjanya Komatineni 		/* PADS Drive Strength */
453de25fa5aSSowjanya Komatineni 		if (voltage == MMC_SIGNAL_VOLTAGE_180) {
454de25fa5aSSowjanya Komatineni 			if (tegra_host->pinctrl_state_1v8_drv) {
455de25fa5aSSowjanya Komatineni 				pinctrl_drvupdn =
456de25fa5aSSowjanya Komatineni 					tegra_host->pinctrl_state_1v8_drv;
457de25fa5aSSowjanya Komatineni 			} else {
458de25fa5aSSowjanya Komatineni 				drvup = offsets->pull_up_1v8_timeout;
459de25fa5aSSowjanya Komatineni 				drvdn = offsets->pull_down_1v8_timeout;
460de25fa5aSSowjanya Komatineni 			}
461de25fa5aSSowjanya Komatineni 		} else {
462de25fa5aSSowjanya Komatineni 			if (tegra_host->pinctrl_state_3v3_drv) {
463de25fa5aSSowjanya Komatineni 				pinctrl_drvupdn =
464de25fa5aSSowjanya Komatineni 					tegra_host->pinctrl_state_3v3_drv;
465de25fa5aSSowjanya Komatineni 			} else {
466de25fa5aSSowjanya Komatineni 				drvup = offsets->pull_up_3v3_timeout;
467de25fa5aSSowjanya Komatineni 				drvdn = offsets->pull_down_3v3_timeout;
468de25fa5aSSowjanya Komatineni 			}
469de25fa5aSSowjanya Komatineni 		}
470de25fa5aSSowjanya Komatineni 
471de25fa5aSSowjanya Komatineni 		if (pinctrl_drvupdn != NULL) {
472de25fa5aSSowjanya Komatineni 			ret = pinctrl_select_state(tegra_host->pinctrl_sdmmc,
473de25fa5aSSowjanya Komatineni 							pinctrl_drvupdn);
474de25fa5aSSowjanya Komatineni 			if (ret < 0)
475de25fa5aSSowjanya Komatineni 				dev_err(mmc_dev(host->mmc),
476de25fa5aSSowjanya Komatineni 					"failed pads drvupdn, ret: %d\n", ret);
477de25fa5aSSowjanya Komatineni 		} else if ((drvup) || (drvdn)) {
478de25fa5aSSowjanya Komatineni 			reg = sdhci_readl(host,
479de25fa5aSSowjanya Komatineni 					SDHCI_TEGRA_SDMEM_COMP_PADCTRL);
480de25fa5aSSowjanya Komatineni 			reg &= ~SDHCI_COMP_PADCTRL_DRVUPDN_OFFSET_MASK;
481de25fa5aSSowjanya Komatineni 			reg |= (drvup << 20) | (drvdn << 12);
482de25fa5aSSowjanya Komatineni 			sdhci_writel(host, reg,
483de25fa5aSSowjanya Komatineni 					SDHCI_TEGRA_SDMEM_COMP_PADCTRL);
484de25fa5aSSowjanya Komatineni 		}
485de25fa5aSSowjanya Komatineni 
486de25fa5aSSowjanya Komatineni 	} else {
487de25fa5aSSowjanya Komatineni 		/* Dual Voltage PADS Voltage selection */
488de25fa5aSSowjanya Komatineni 		if (!tegra_host->pad_control_available)
489de25fa5aSSowjanya Komatineni 			return 0;
490de25fa5aSSowjanya Komatineni 
491de25fa5aSSowjanya Komatineni 		if (voltage == MMC_SIGNAL_VOLTAGE_180) {
492de25fa5aSSowjanya Komatineni 			ret = pinctrl_select_state(tegra_host->pinctrl_sdmmc,
493de25fa5aSSowjanya Komatineni 						tegra_host->pinctrl_state_1v8);
494de25fa5aSSowjanya Komatineni 			if (ret < 0)
495de25fa5aSSowjanya Komatineni 				dev_err(mmc_dev(host->mmc),
496de25fa5aSSowjanya Komatineni 					"setting 1.8V failed, ret: %d\n", ret);
497de25fa5aSSowjanya Komatineni 		} else {
498de25fa5aSSowjanya Komatineni 			ret = pinctrl_select_state(tegra_host->pinctrl_sdmmc,
499de25fa5aSSowjanya Komatineni 						tegra_host->pinctrl_state_3v3);
500de25fa5aSSowjanya Komatineni 			if (ret < 0)
501de25fa5aSSowjanya Komatineni 				dev_err(mmc_dev(host->mmc),
502de25fa5aSSowjanya Komatineni 					"setting 3.3V failed, ret: %d\n", ret);
503de25fa5aSSowjanya Komatineni 		}
504de25fa5aSSowjanya Komatineni 	}
505de25fa5aSSowjanya Komatineni 
506de25fa5aSSowjanya Komatineni 	return ret;
507de25fa5aSSowjanya Komatineni }
508de25fa5aSSowjanya Komatineni 
509e5c63d91SLucas Stach static void tegra_sdhci_pad_autocalib(struct sdhci_host *host)
510e5c63d91SLucas Stach {
51151b77c8eSAapo Vienamo 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
51251b77c8eSAapo Vienamo 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
51351b77c8eSAapo Vienamo 	struct sdhci_tegra_autocal_offsets offsets =
51451b77c8eSAapo Vienamo 			tegra_host->autocal_offsets;
51551b77c8eSAapo Vienamo 	struct mmc_ios *ios = &host->mmc->ios;
516887bda8fSAapo Vienamo 	bool card_clk_enabled;
51751b77c8eSAapo Vienamo 	u16 pdpu;
518e7c07148SAapo Vienamo 	u32 reg;
519e7c07148SAapo Vienamo 	int ret;
520e5c63d91SLucas Stach 
52151b77c8eSAapo Vienamo 	switch (ios->timing) {
52251b77c8eSAapo Vienamo 	case MMC_TIMING_UHS_SDR104:
52351b77c8eSAapo Vienamo 		pdpu = offsets.pull_down_sdr104 << 8 | offsets.pull_up_sdr104;
52451b77c8eSAapo Vienamo 		break;
52551b77c8eSAapo Vienamo 	case MMC_TIMING_MMC_HS400:
52651b77c8eSAapo Vienamo 		pdpu = offsets.pull_down_hs400 << 8 | offsets.pull_up_hs400;
52751b77c8eSAapo Vienamo 		break;
52851b77c8eSAapo Vienamo 	default:
52951b77c8eSAapo Vienamo 		if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180)
53051b77c8eSAapo Vienamo 			pdpu = offsets.pull_down_1v8 << 8 | offsets.pull_up_1v8;
53151b77c8eSAapo Vienamo 		else
53251b77c8eSAapo Vienamo 			pdpu = offsets.pull_down_3v3 << 8 | offsets.pull_up_3v3;
53351b77c8eSAapo Vienamo 	}
53451b77c8eSAapo Vienamo 
535de25fa5aSSowjanya Komatineni 	/* Set initial offset before auto-calibration */
53651b77c8eSAapo Vienamo 	tegra_sdhci_set_pad_autocal_offset(host, pdpu);
53751b77c8eSAapo Vienamo 
538887bda8fSAapo Vienamo 	card_clk_enabled = tegra_sdhci_configure_card_clk(host, false);
539887bda8fSAapo Vienamo 
540212b0cf1SAapo Vienamo 	tegra_sdhci_configure_cal_pad(host, true);
541212b0cf1SAapo Vienamo 
542e7c07148SAapo Vienamo 	reg = sdhci_readl(host, SDHCI_TEGRA_AUTO_CAL_CONFIG);
543e7c07148SAapo Vienamo 	reg |= SDHCI_AUTO_CAL_ENABLE | SDHCI_AUTO_CAL_START;
544e7c07148SAapo Vienamo 	sdhci_writel(host, reg, SDHCI_TEGRA_AUTO_CAL_CONFIG);
545e5c63d91SLucas Stach 
546e7c07148SAapo Vienamo 	usleep_range(1, 2);
547e7c07148SAapo Vienamo 	/* 10 ms timeout */
548e7c07148SAapo Vienamo 	ret = readl_poll_timeout(host->ioaddr + SDHCI_TEGRA_AUTO_CAL_STATUS,
549e7c07148SAapo Vienamo 				 reg, !(reg & SDHCI_TEGRA_AUTO_CAL_ACTIVE),
550e7c07148SAapo Vienamo 				 1000, 10000);
551e7c07148SAapo Vienamo 
552212b0cf1SAapo Vienamo 	tegra_sdhci_configure_cal_pad(host, false);
553212b0cf1SAapo Vienamo 
554887bda8fSAapo Vienamo 	tegra_sdhci_configure_card_clk(host, card_clk_enabled);
555887bda8fSAapo Vienamo 
55651b77c8eSAapo Vienamo 	if (ret) {
557e7c07148SAapo Vienamo 		dev_err(mmc_dev(host->mmc), "Pad autocal timed out\n");
55851b77c8eSAapo Vienamo 
559de25fa5aSSowjanya Komatineni 		/* Disable automatic cal and use fixed Drive Strengths */
56051b77c8eSAapo Vienamo 		reg = sdhci_readl(host, SDHCI_TEGRA_AUTO_CAL_CONFIG);
56151b77c8eSAapo Vienamo 		reg &= ~SDHCI_AUTO_CAL_ENABLE;
56251b77c8eSAapo Vienamo 		sdhci_writel(host, reg, SDHCI_TEGRA_AUTO_CAL_CONFIG);
56351b77c8eSAapo Vienamo 
564de25fa5aSSowjanya Komatineni 		ret = tegra_sdhci_set_padctrl(host, ios->signal_voltage, false);
565de25fa5aSSowjanya Komatineni 		if (ret < 0)
566de25fa5aSSowjanya Komatineni 			dev_err(mmc_dev(host->mmc),
567de25fa5aSSowjanya Komatineni 				"Setting drive strengths failed: %d\n", ret);
56851b77c8eSAapo Vienamo 	}
56951b77c8eSAapo Vienamo }
57051b77c8eSAapo Vienamo 
57151b77c8eSAapo Vienamo static void tegra_sdhci_parse_pad_autocal_dt(struct sdhci_host *host)
57251b77c8eSAapo Vienamo {
57351b77c8eSAapo Vienamo 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
57451b77c8eSAapo Vienamo 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
57551b77c8eSAapo Vienamo 	struct sdhci_tegra_autocal_offsets *autocal =
57651b77c8eSAapo Vienamo 			&tegra_host->autocal_offsets;
57751b77c8eSAapo Vienamo 	int err;
57851b77c8eSAapo Vienamo 
57951b77c8eSAapo Vienamo 	err = device_property_read_u32(host->mmc->parent,
58051b77c8eSAapo Vienamo 			"nvidia,pad-autocal-pull-up-offset-3v3",
58151b77c8eSAapo Vienamo 			&autocal->pull_up_3v3);
58251b77c8eSAapo Vienamo 	if (err)
58351b77c8eSAapo Vienamo 		autocal->pull_up_3v3 = 0;
58451b77c8eSAapo Vienamo 
58551b77c8eSAapo Vienamo 	err = device_property_read_u32(host->mmc->parent,
58651b77c8eSAapo Vienamo 			"nvidia,pad-autocal-pull-down-offset-3v3",
58751b77c8eSAapo Vienamo 			&autocal->pull_down_3v3);
58851b77c8eSAapo Vienamo 	if (err)
58951b77c8eSAapo Vienamo 		autocal->pull_down_3v3 = 0;
59051b77c8eSAapo Vienamo 
59151b77c8eSAapo Vienamo 	err = device_property_read_u32(host->mmc->parent,
59251b77c8eSAapo Vienamo 			"nvidia,pad-autocal-pull-up-offset-1v8",
59351b77c8eSAapo Vienamo 			&autocal->pull_up_1v8);
59451b77c8eSAapo Vienamo 	if (err)
59551b77c8eSAapo Vienamo 		autocal->pull_up_1v8 = 0;
59651b77c8eSAapo Vienamo 
59751b77c8eSAapo Vienamo 	err = device_property_read_u32(host->mmc->parent,
59851b77c8eSAapo Vienamo 			"nvidia,pad-autocal-pull-down-offset-1v8",
59951b77c8eSAapo Vienamo 			&autocal->pull_down_1v8);
60051b77c8eSAapo Vienamo 	if (err)
60151b77c8eSAapo Vienamo 		autocal->pull_down_1v8 = 0;
60251b77c8eSAapo Vienamo 
60351b77c8eSAapo Vienamo 	err = device_property_read_u32(host->mmc->parent,
60451b77c8eSAapo Vienamo 			"nvidia,pad-autocal-pull-up-offset-3v3-timeout",
6055ccf7f55SSowjanya Komatineni 			&autocal->pull_up_3v3_timeout);
606de25fa5aSSowjanya Komatineni 	if (err) {
607de25fa5aSSowjanya Komatineni 		if (!IS_ERR(tegra_host->pinctrl_state_3v3) &&
608de25fa5aSSowjanya Komatineni 			(tegra_host->pinctrl_state_3v3_drv == NULL))
609de25fa5aSSowjanya Komatineni 			pr_warn("%s: Missing autocal timeout 3v3-pad drvs\n",
610de25fa5aSSowjanya Komatineni 				mmc_hostname(host->mmc));
61151b77c8eSAapo Vienamo 		autocal->pull_up_3v3_timeout = 0;
612de25fa5aSSowjanya Komatineni 	}
61351b77c8eSAapo Vienamo 
61451b77c8eSAapo Vienamo 	err = device_property_read_u32(host->mmc->parent,
61551b77c8eSAapo Vienamo 			"nvidia,pad-autocal-pull-down-offset-3v3-timeout",
6165ccf7f55SSowjanya Komatineni 			&autocal->pull_down_3v3_timeout);
617de25fa5aSSowjanya Komatineni 	if (err) {
618de25fa5aSSowjanya Komatineni 		if (!IS_ERR(tegra_host->pinctrl_state_3v3) &&
619de25fa5aSSowjanya Komatineni 			(tegra_host->pinctrl_state_3v3_drv == NULL))
620de25fa5aSSowjanya Komatineni 			pr_warn("%s: Missing autocal timeout 3v3-pad drvs\n",
621de25fa5aSSowjanya Komatineni 				mmc_hostname(host->mmc));
62251b77c8eSAapo Vienamo 		autocal->pull_down_3v3_timeout = 0;
623de25fa5aSSowjanya Komatineni 	}
62451b77c8eSAapo Vienamo 
62551b77c8eSAapo Vienamo 	err = device_property_read_u32(host->mmc->parent,
62651b77c8eSAapo Vienamo 			"nvidia,pad-autocal-pull-up-offset-1v8-timeout",
6275ccf7f55SSowjanya Komatineni 			&autocal->pull_up_1v8_timeout);
628de25fa5aSSowjanya Komatineni 	if (err) {
629de25fa5aSSowjanya Komatineni 		if (!IS_ERR(tegra_host->pinctrl_state_1v8) &&
630de25fa5aSSowjanya Komatineni 			(tegra_host->pinctrl_state_1v8_drv == NULL))
631de25fa5aSSowjanya Komatineni 			pr_warn("%s: Missing autocal timeout 1v8-pad drvs\n",
632de25fa5aSSowjanya Komatineni 				mmc_hostname(host->mmc));
63351b77c8eSAapo Vienamo 		autocal->pull_up_1v8_timeout = 0;
634de25fa5aSSowjanya Komatineni 	}
63551b77c8eSAapo Vienamo 
63651b77c8eSAapo Vienamo 	err = device_property_read_u32(host->mmc->parent,
63751b77c8eSAapo Vienamo 			"nvidia,pad-autocal-pull-down-offset-1v8-timeout",
6385ccf7f55SSowjanya Komatineni 			&autocal->pull_down_1v8_timeout);
639de25fa5aSSowjanya Komatineni 	if (err) {
640de25fa5aSSowjanya Komatineni 		if (!IS_ERR(tegra_host->pinctrl_state_1v8) &&
641de25fa5aSSowjanya Komatineni 			(tegra_host->pinctrl_state_1v8_drv == NULL))
642de25fa5aSSowjanya Komatineni 			pr_warn("%s: Missing autocal timeout 1v8-pad drvs\n",
643de25fa5aSSowjanya Komatineni 				mmc_hostname(host->mmc));
64451b77c8eSAapo Vienamo 		autocal->pull_down_1v8_timeout = 0;
645de25fa5aSSowjanya Komatineni 	}
64651b77c8eSAapo Vienamo 
64751b77c8eSAapo Vienamo 	err = device_property_read_u32(host->mmc->parent,
64851b77c8eSAapo Vienamo 			"nvidia,pad-autocal-pull-up-offset-sdr104",
64951b77c8eSAapo Vienamo 			&autocal->pull_up_sdr104);
65051b77c8eSAapo Vienamo 	if (err)
65151b77c8eSAapo Vienamo 		autocal->pull_up_sdr104 = autocal->pull_up_1v8;
65251b77c8eSAapo Vienamo 
65351b77c8eSAapo Vienamo 	err = device_property_read_u32(host->mmc->parent,
65451b77c8eSAapo Vienamo 			"nvidia,pad-autocal-pull-down-offset-sdr104",
65551b77c8eSAapo Vienamo 			&autocal->pull_down_sdr104);
65651b77c8eSAapo Vienamo 	if (err)
65751b77c8eSAapo Vienamo 		autocal->pull_down_sdr104 = autocal->pull_down_1v8;
65851b77c8eSAapo Vienamo 
65951b77c8eSAapo Vienamo 	err = device_property_read_u32(host->mmc->parent,
66051b77c8eSAapo Vienamo 			"nvidia,pad-autocal-pull-up-offset-hs400",
66151b77c8eSAapo Vienamo 			&autocal->pull_up_hs400);
66251b77c8eSAapo Vienamo 	if (err)
66351b77c8eSAapo Vienamo 		autocal->pull_up_hs400 = autocal->pull_up_1v8;
66451b77c8eSAapo Vienamo 
66551b77c8eSAapo Vienamo 	err = device_property_read_u32(host->mmc->parent,
66651b77c8eSAapo Vienamo 			"nvidia,pad-autocal-pull-down-offset-hs400",
66751b77c8eSAapo Vienamo 			&autocal->pull_down_hs400);
66851b77c8eSAapo Vienamo 	if (err)
66951b77c8eSAapo Vienamo 		autocal->pull_down_hs400 = autocal->pull_down_1v8;
670e5c63d91SLucas Stach }
671e5c63d91SLucas Stach 
67261dad40eSAapo Vienamo static void tegra_sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
67361dad40eSAapo Vienamo {
67461dad40eSAapo Vienamo 	struct sdhci_host *host = mmc_priv(mmc);
67561dad40eSAapo Vienamo 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
67661dad40eSAapo Vienamo 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
67761dad40eSAapo Vienamo 	ktime_t since_calib = ktime_sub(ktime_get(), tegra_host->last_calib);
67861dad40eSAapo Vienamo 
67961dad40eSAapo Vienamo 	/* 100 ms calibration interval is specified in the TRM */
68061dad40eSAapo Vienamo 	if (ktime_to_ms(since_calib) > 100) {
68161dad40eSAapo Vienamo 		tegra_sdhci_pad_autocalib(host);
68261dad40eSAapo Vienamo 		tegra_host->last_calib = ktime_get();
68361dad40eSAapo Vienamo 	}
68461dad40eSAapo Vienamo 
68561dad40eSAapo Vienamo 	sdhci_request(mmc, mrq);
68661dad40eSAapo Vienamo }
68761dad40eSAapo Vienamo 
688f5313aaaSAapo Vienamo static void tegra_sdhci_parse_tap_and_trim(struct sdhci_host *host)
68985c0da17SAapo Vienamo {
69085c0da17SAapo Vienamo 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
69185c0da17SAapo Vienamo 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
69285c0da17SAapo Vienamo 	int err;
69385c0da17SAapo Vienamo 
69485c0da17SAapo Vienamo 	err = device_property_read_u32(host->mmc->parent, "nvidia,default-tap",
69585c0da17SAapo Vienamo 				       &tegra_host->default_tap);
69685c0da17SAapo Vienamo 	if (err)
69785c0da17SAapo Vienamo 		tegra_host->default_tap = 0;
69885c0da17SAapo Vienamo 
69985c0da17SAapo Vienamo 	err = device_property_read_u32(host->mmc->parent, "nvidia,default-trim",
70085c0da17SAapo Vienamo 				       &tegra_host->default_trim);
70185c0da17SAapo Vienamo 	if (err)
70285c0da17SAapo Vienamo 		tegra_host->default_trim = 0;
703f5313aaaSAapo Vienamo 
704f5313aaaSAapo Vienamo 	err = device_property_read_u32(host->mmc->parent, "nvidia,dqs-trim",
705f5313aaaSAapo Vienamo 				       &tegra_host->dqs_trim);
706f5313aaaSAapo Vienamo 	if (err)
707f5313aaaSAapo Vienamo 		tegra_host->dqs_trim = 0x11;
70885c0da17SAapo Vienamo }
70985c0da17SAapo Vienamo 
7103c4019f9SSowjanya Komatineni static void tegra_sdhci_parse_dt(struct sdhci_host *host)
7113c4019f9SSowjanya Komatineni {
7123c4019f9SSowjanya Komatineni 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
7133c4019f9SSowjanya Komatineni 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
7143c4019f9SSowjanya Komatineni 
7153c4019f9SSowjanya Komatineni 	if (device_property_read_bool(host->mmc->parent, "supports-cqe"))
7163c4019f9SSowjanya Komatineni 		tegra_host->enable_hwcq = true;
7173c4019f9SSowjanya Komatineni 	else
7183c4019f9SSowjanya Komatineni 		tegra_host->enable_hwcq = false;
7193c4019f9SSowjanya Komatineni 
7203c4019f9SSowjanya Komatineni 	tegra_sdhci_parse_pad_autocal_dt(host);
7213c4019f9SSowjanya Komatineni 	tegra_sdhci_parse_tap_and_trim(host);
7223c4019f9SSowjanya Komatineni }
7233c4019f9SSowjanya Komatineni 
724a8e326a9SLucas Stach static void tegra_sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
725a8e326a9SLucas Stach {
726a8e326a9SLucas Stach 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
7270734e79cSJisheng Zhang 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
728a8e326a9SLucas Stach 	unsigned long host_clk;
729a8e326a9SLucas Stach 
730a8e326a9SLucas Stach 	if (!clock)
7313491b690SLucas Stach 		return sdhci_set_clock(host, clock);
732a8e326a9SLucas Stach 
73357d1654eSAapo Vienamo 	/*
73457d1654eSAapo Vienamo 	 * In DDR50/52 modes the Tegra SDHCI controllers require the SDHCI
73557d1654eSAapo Vienamo 	 * divider to be configured to divided the host clock by two. The SDHCI
73657d1654eSAapo Vienamo 	 * clock divider is calculated as part of sdhci_set_clock() by
73757d1654eSAapo Vienamo 	 * sdhci_calc_clk(). The divider is calculated from host->max_clk and
73857d1654eSAapo Vienamo 	 * the requested clock rate.
73957d1654eSAapo Vienamo 	 *
74057d1654eSAapo Vienamo 	 * By setting the host->max_clk to clock * 2 the divider calculation
74157d1654eSAapo Vienamo 	 * will always result in the correct value for DDR50/52 modes,
74257d1654eSAapo Vienamo 	 * regardless of clock rate rounding, which may happen if the value
74357d1654eSAapo Vienamo 	 * from clk_get_rate() is used.
74457d1654eSAapo Vienamo 	 */
745a8e326a9SLucas Stach 	host_clk = tegra_host->ddr_signaling ? clock * 2 : clock;
746a8e326a9SLucas Stach 	clk_set_rate(pltfm_host->clk, host_clk);
747ea8fc595SSowjanya Komatineni 	tegra_host->curr_clk_rate = host_clk;
74857d1654eSAapo Vienamo 	if (tegra_host->ddr_signaling)
74957d1654eSAapo Vienamo 		host->max_clk = host_clk;
75057d1654eSAapo Vienamo 	else
751a8e326a9SLucas Stach 		host->max_clk = clk_get_rate(pltfm_host->clk);
752a8e326a9SLucas Stach 
753e5c63d91SLucas Stach 	sdhci_set_clock(host, clock);
754e5c63d91SLucas Stach 
755e5c63d91SLucas Stach 	if (tegra_host->pad_calib_required) {
756e5c63d91SLucas Stach 		tegra_sdhci_pad_autocalib(host);
757e5c63d91SLucas Stach 		tegra_host->pad_calib_required = false;
758e5c63d91SLucas Stach 	}
759a8e326a9SLucas Stach }
760a8e326a9SLucas Stach 
76144350993SAapo Vienamo static unsigned int tegra_sdhci_get_max_clock(struct sdhci_host *host)
76244350993SAapo Vienamo {
76344350993SAapo Vienamo 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
76444350993SAapo Vienamo 
76544350993SAapo Vienamo 	return clk_round_rate(pltfm_host->clk, UINT_MAX);
76644350993SAapo Vienamo }
76744350993SAapo Vienamo 
768f5313aaaSAapo Vienamo static void tegra_sdhci_set_dqs_trim(struct sdhci_host *host, u8 trim)
769f5313aaaSAapo Vienamo {
770f5313aaaSAapo Vienamo 	u32 val;
771f5313aaaSAapo Vienamo 
772f5313aaaSAapo Vienamo 	val = sdhci_readl(host, SDHCI_TEGRA_VENDOR_CAP_OVERRIDES);
773f5313aaaSAapo Vienamo 	val &= ~SDHCI_TEGRA_CAP_OVERRIDES_DQS_TRIM_MASK;
774f5313aaaSAapo Vienamo 	val |= trim << SDHCI_TEGRA_CAP_OVERRIDES_DQS_TRIM_SHIFT;
775f5313aaaSAapo Vienamo 	sdhci_writel(host, val, SDHCI_TEGRA_VENDOR_CAP_OVERRIDES);
776f5313aaaSAapo Vienamo }
777f5313aaaSAapo Vienamo 
778bc5568bfSAapo Vienamo static void tegra_sdhci_hs400_dll_cal(struct sdhci_host *host)
779bc5568bfSAapo Vienamo {
780bc5568bfSAapo Vienamo 	u32 reg;
781bc5568bfSAapo Vienamo 	int err;
782bc5568bfSAapo Vienamo 
783bc5568bfSAapo Vienamo 	reg = sdhci_readl(host, SDHCI_TEGRA_VENDOR_DLLCAL_CFG);
784bc5568bfSAapo Vienamo 	reg |= SDHCI_TEGRA_DLLCAL_CALIBRATE;
785bc5568bfSAapo Vienamo 	sdhci_writel(host, reg, SDHCI_TEGRA_VENDOR_DLLCAL_CFG);
786bc5568bfSAapo Vienamo 
787bc5568bfSAapo Vienamo 	/* 1 ms sleep, 5 ms timeout */
788bc5568bfSAapo Vienamo 	err = readl_poll_timeout(host->ioaddr + SDHCI_TEGRA_VENDOR_DLLCAL_STA,
789bc5568bfSAapo Vienamo 				 reg, !(reg & SDHCI_TEGRA_DLLCAL_STA_ACTIVE),
790bc5568bfSAapo Vienamo 				 1000, 5000);
791bc5568bfSAapo Vienamo 	if (err)
792bc5568bfSAapo Vienamo 		dev_err(mmc_dev(host->mmc),
793bc5568bfSAapo Vienamo 			"HS400 delay line calibration timed out\n");
794bc5568bfSAapo Vienamo }
795bc5568bfSAapo Vienamo 
796ea8fc595SSowjanya Komatineni static void tegra_sdhci_tap_correction(struct sdhci_host *host, u8 thd_up,
797ea8fc595SSowjanya Komatineni 				       u8 thd_low, u8 fixed_tap)
798ea8fc595SSowjanya Komatineni {
799ea8fc595SSowjanya Komatineni 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
800ea8fc595SSowjanya Komatineni 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
801ea8fc595SSowjanya Komatineni 	u32 val, tun_status;
802ea8fc595SSowjanya Komatineni 	u8 word, bit, edge1, tap, window;
803ea8fc595SSowjanya Komatineni 	bool tap_result;
804ea8fc595SSowjanya Komatineni 	bool start_fail = false;
805ea8fc595SSowjanya Komatineni 	bool start_pass = false;
806ea8fc595SSowjanya Komatineni 	bool end_pass = false;
807ea8fc595SSowjanya Komatineni 	bool first_fail = false;
808ea8fc595SSowjanya Komatineni 	bool first_pass = false;
809ea8fc595SSowjanya Komatineni 	u8 start_pass_tap = 0;
810ea8fc595SSowjanya Komatineni 	u8 end_pass_tap = 0;
811ea8fc595SSowjanya Komatineni 	u8 first_fail_tap = 0;
812ea8fc595SSowjanya Komatineni 	u8 first_pass_tap = 0;
813ea8fc595SSowjanya Komatineni 	u8 total_tuning_words = host->tuning_loop_count / TUNING_WORD_BIT_SIZE;
814ea8fc595SSowjanya Komatineni 
815ea8fc595SSowjanya Komatineni 	/*
816ea8fc595SSowjanya Komatineni 	 * Read auto-tuned results and extract good valid passing window by
817ea8fc595SSowjanya Komatineni 	 * filtering out un-wanted bubble/partial/merged windows.
818ea8fc595SSowjanya Komatineni 	 */
819ea8fc595SSowjanya Komatineni 	for (word = 0; word < total_tuning_words; word++) {
820ea8fc595SSowjanya Komatineni 		val = sdhci_readl(host, SDHCI_VNDR_TUN_CTRL0_0);
821ea8fc595SSowjanya Komatineni 		val &= ~SDHCI_VNDR_TUN_CTRL0_TUN_WORD_SEL_MASK;
822ea8fc595SSowjanya Komatineni 		val |= word;
823ea8fc595SSowjanya Komatineni 		sdhci_writel(host, val, SDHCI_VNDR_TUN_CTRL0_0);
824ea8fc595SSowjanya Komatineni 		tun_status = sdhci_readl(host, SDHCI_TEGRA_VNDR_TUN_STATUS0);
825ea8fc595SSowjanya Komatineni 		bit = 0;
826ea8fc595SSowjanya Komatineni 		while (bit < TUNING_WORD_BIT_SIZE) {
827ea8fc595SSowjanya Komatineni 			tap = word * TUNING_WORD_BIT_SIZE + bit;
828ea8fc595SSowjanya Komatineni 			tap_result = tun_status & (1 << bit);
829ea8fc595SSowjanya Komatineni 			if (!tap_result && !start_fail) {
830ea8fc595SSowjanya Komatineni 				start_fail = true;
831ea8fc595SSowjanya Komatineni 				if (!first_fail) {
832ea8fc595SSowjanya Komatineni 					first_fail_tap = tap;
833ea8fc595SSowjanya Komatineni 					first_fail = true;
834ea8fc595SSowjanya Komatineni 				}
835ea8fc595SSowjanya Komatineni 
836ea8fc595SSowjanya Komatineni 			} else if (tap_result && start_fail && !start_pass) {
837ea8fc595SSowjanya Komatineni 				start_pass_tap = tap;
838ea8fc595SSowjanya Komatineni 				start_pass = true;
839ea8fc595SSowjanya Komatineni 				if (!first_pass) {
840ea8fc595SSowjanya Komatineni 					first_pass_tap = tap;
841ea8fc595SSowjanya Komatineni 					first_pass = true;
842ea8fc595SSowjanya Komatineni 				}
843ea8fc595SSowjanya Komatineni 
844ea8fc595SSowjanya Komatineni 			} else if (!tap_result && start_fail && start_pass &&
845ea8fc595SSowjanya Komatineni 				   !end_pass) {
846ea8fc595SSowjanya Komatineni 				end_pass_tap = tap - 1;
847ea8fc595SSowjanya Komatineni 				end_pass = true;
848ea8fc595SSowjanya Komatineni 			} else if (tap_result && start_pass && start_fail &&
849ea8fc595SSowjanya Komatineni 				   end_pass) {
850ea8fc595SSowjanya Komatineni 				window = end_pass_tap - start_pass_tap;
851ea8fc595SSowjanya Komatineni 				/* discard merged window and bubble window */
852ea8fc595SSowjanya Komatineni 				if (window >= thd_up || window < thd_low) {
853ea8fc595SSowjanya Komatineni 					start_pass_tap = tap;
854ea8fc595SSowjanya Komatineni 					end_pass = false;
855ea8fc595SSowjanya Komatineni 				} else {
856ea8fc595SSowjanya Komatineni 					/* set tap at middle of valid window */
857ea8fc595SSowjanya Komatineni 					tap = start_pass_tap + window / 2;
858ea8fc595SSowjanya Komatineni 					tegra_host->tuned_tap_delay = tap;
859ea8fc595SSowjanya Komatineni 					return;
860ea8fc595SSowjanya Komatineni 				}
861ea8fc595SSowjanya Komatineni 			}
862ea8fc595SSowjanya Komatineni 
863ea8fc595SSowjanya Komatineni 			bit++;
864ea8fc595SSowjanya Komatineni 		}
865ea8fc595SSowjanya Komatineni 	}
866ea8fc595SSowjanya Komatineni 
867ea8fc595SSowjanya Komatineni 	if (!first_fail) {
868ea8fc595SSowjanya Komatineni 		WARN_ON("no edge detected, continue with hw tuned delay.\n");
869ea8fc595SSowjanya Komatineni 	} else if (first_pass) {
870ea8fc595SSowjanya Komatineni 		/* set tap location at fixed tap relative to the first edge */
871ea8fc595SSowjanya Komatineni 		edge1 = first_fail_tap + (first_pass_tap - first_fail_tap) / 2;
872ea8fc595SSowjanya Komatineni 		if (edge1 - 1 > fixed_tap)
873ea8fc595SSowjanya Komatineni 			tegra_host->tuned_tap_delay = edge1 - fixed_tap;
874ea8fc595SSowjanya Komatineni 		else
875ea8fc595SSowjanya Komatineni 			tegra_host->tuned_tap_delay = edge1 + fixed_tap;
876ea8fc595SSowjanya Komatineni 	}
877ea8fc595SSowjanya Komatineni }
878ea8fc595SSowjanya Komatineni 
879ea8fc595SSowjanya Komatineni static void tegra_sdhci_post_tuning(struct sdhci_host *host)
880ea8fc595SSowjanya Komatineni {
881ea8fc595SSowjanya Komatineni 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
882ea8fc595SSowjanya Komatineni 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
883ea8fc595SSowjanya Komatineni 	const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
884ea8fc595SSowjanya Komatineni 	u32 avg_tap_dly, val, min_tap_dly, max_tap_dly;
885ea8fc595SSowjanya Komatineni 	u8 fixed_tap, start_tap, end_tap, window_width;
886ea8fc595SSowjanya Komatineni 	u8 thdupper, thdlower;
887ea8fc595SSowjanya Komatineni 	u8 num_iter;
888ea8fc595SSowjanya Komatineni 	u32 clk_rate_mhz, period_ps, bestcase, worstcase;
889ea8fc595SSowjanya Komatineni 
890ea8fc595SSowjanya Komatineni 	/* retain HW tuned tap to use incase if no correction is needed */
891ea8fc595SSowjanya Komatineni 	val = sdhci_readl(host, SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
892ea8fc595SSowjanya Komatineni 	tegra_host->tuned_tap_delay = (val & SDHCI_CLOCK_CTRL_TAP_MASK) >>
893ea8fc595SSowjanya Komatineni 				      SDHCI_CLOCK_CTRL_TAP_SHIFT;
894ea8fc595SSowjanya Komatineni 	if (soc_data->min_tap_delay && soc_data->max_tap_delay) {
895ea8fc595SSowjanya Komatineni 		min_tap_dly = soc_data->min_tap_delay;
896ea8fc595SSowjanya Komatineni 		max_tap_dly = soc_data->max_tap_delay;
897ea8fc595SSowjanya Komatineni 		clk_rate_mhz = tegra_host->curr_clk_rate / USEC_PER_SEC;
898ea8fc595SSowjanya Komatineni 		period_ps = USEC_PER_SEC / clk_rate_mhz;
899ea8fc595SSowjanya Komatineni 		bestcase = period_ps / min_tap_dly;
900ea8fc595SSowjanya Komatineni 		worstcase = period_ps / max_tap_dly;
901ea8fc595SSowjanya Komatineni 		/*
902ea8fc595SSowjanya Komatineni 		 * Upper and Lower bound thresholds used to detect merged and
903ea8fc595SSowjanya Komatineni 		 * bubble windows
904ea8fc595SSowjanya Komatineni 		 */
905ea8fc595SSowjanya Komatineni 		thdupper = (2 * worstcase + bestcase) / 2;
906ea8fc595SSowjanya Komatineni 		thdlower = worstcase / 4;
907ea8fc595SSowjanya Komatineni 		/*
908ea8fc595SSowjanya Komatineni 		 * fixed tap is used when HW tuning result contains single edge
909ea8fc595SSowjanya Komatineni 		 * and tap is set at fixed tap delay relative to the first edge
910ea8fc595SSowjanya Komatineni 		 */
911ea8fc595SSowjanya Komatineni 		avg_tap_dly = (period_ps * 2) / (min_tap_dly + max_tap_dly);
912ea8fc595SSowjanya Komatineni 		fixed_tap = avg_tap_dly / 2;
913ea8fc595SSowjanya Komatineni 
914ea8fc595SSowjanya Komatineni 		val = sdhci_readl(host, SDHCI_TEGRA_VNDR_TUN_STATUS1);
915ea8fc595SSowjanya Komatineni 		start_tap = val & SDHCI_TEGRA_VNDR_TUN_STATUS1_TAP_MASK;
916ea8fc595SSowjanya Komatineni 		end_tap = (val >> SDHCI_TEGRA_VNDR_TUN_STATUS1_END_TAP_SHIFT) &
917ea8fc595SSowjanya Komatineni 			  SDHCI_TEGRA_VNDR_TUN_STATUS1_TAP_MASK;
918ea8fc595SSowjanya Komatineni 		window_width = end_tap - start_tap;
919ea8fc595SSowjanya Komatineni 		num_iter = host->tuning_loop_count;
920ea8fc595SSowjanya Komatineni 		/*
921ea8fc595SSowjanya Komatineni 		 * partial window includes edges of the tuning range.
922ea8fc595SSowjanya Komatineni 		 * merged window includes more taps so window width is higher
923ea8fc595SSowjanya Komatineni 		 * than upper threshold.
924ea8fc595SSowjanya Komatineni 		 */
925ea8fc595SSowjanya Komatineni 		if (start_tap == 0 || (end_tap == (num_iter - 1)) ||
926ea8fc595SSowjanya Komatineni 		    (end_tap == num_iter - 2) || window_width >= thdupper) {
927ea8fc595SSowjanya Komatineni 			pr_debug("%s: Apply tuning correction\n",
928ea8fc595SSowjanya Komatineni 				 mmc_hostname(host->mmc));
929ea8fc595SSowjanya Komatineni 			tegra_sdhci_tap_correction(host, thdupper, thdlower,
930ea8fc595SSowjanya Komatineni 						   fixed_tap);
931ea8fc595SSowjanya Komatineni 		}
932ea8fc595SSowjanya Komatineni 	}
933ea8fc595SSowjanya Komatineni 
934ea8fc595SSowjanya Komatineni 	tegra_sdhci_set_tap(host, tegra_host->tuned_tap_delay);
935ea8fc595SSowjanya Komatineni }
936ea8fc595SSowjanya Komatineni 
937ea8fc595SSowjanya Komatineni static int tegra_sdhci_execute_hw_tuning(struct mmc_host *mmc, u32 opcode)
938ea8fc595SSowjanya Komatineni {
939ea8fc595SSowjanya Komatineni 	struct sdhci_host *host = mmc_priv(mmc);
940ea8fc595SSowjanya Komatineni 	int err;
941ea8fc595SSowjanya Komatineni 
942ea8fc595SSowjanya Komatineni 	err = sdhci_execute_tuning(mmc, opcode);
943ea8fc595SSowjanya Komatineni 	if (!err && !host->tuning_err)
944ea8fc595SSowjanya Komatineni 		tegra_sdhci_post_tuning(host);
945ea8fc595SSowjanya Komatineni 
946ea8fc595SSowjanya Komatineni 	return err;
947ea8fc595SSowjanya Komatineni }
948ea8fc595SSowjanya Komatineni 
949c2c09678SAapo Vienamo static void tegra_sdhci_set_uhs_signaling(struct sdhci_host *host,
950c2c09678SAapo Vienamo 					  unsigned timing)
951c3c2384cSLucas Stach {
952d4501d8eSAapo Vienamo 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
953d4501d8eSAapo Vienamo 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
954c2c09678SAapo Vienamo 	bool set_default_tap = false;
955f5313aaaSAapo Vienamo 	bool set_dqs_trim = false;
956bc5568bfSAapo Vienamo 	bool do_hs400_dll_cal = false;
957ea8fc595SSowjanya Komatineni 	u8 iter = TRIES_256;
958ea8fc595SSowjanya Komatineni 	u32 val;
959c3c2384cSLucas Stach 
96092cd1667SSowjanya Komatineni 	tegra_host->ddr_signaling = false;
961c2c09678SAapo Vienamo 	switch (timing) {
962c2c09678SAapo Vienamo 	case MMC_TIMING_UHS_SDR50:
963ea8fc595SSowjanya Komatineni 		break;
964c2c09678SAapo Vienamo 	case MMC_TIMING_UHS_SDR104:
965c2c09678SAapo Vienamo 	case MMC_TIMING_MMC_HS200:
966c2c09678SAapo Vienamo 		/* Don't set default tap on tunable modes. */
967ea8fc595SSowjanya Komatineni 		iter = TRIES_128;
968c2c09678SAapo Vienamo 		break;
969f5313aaaSAapo Vienamo 	case MMC_TIMING_MMC_HS400:
970f5313aaaSAapo Vienamo 		set_dqs_trim = true;
971bc5568bfSAapo Vienamo 		do_hs400_dll_cal = true;
972ea8fc595SSowjanya Komatineni 		iter = TRIES_128;
973f5313aaaSAapo Vienamo 		break;
974c2c09678SAapo Vienamo 	case MMC_TIMING_MMC_DDR52:
975c2c09678SAapo Vienamo 	case MMC_TIMING_UHS_DDR50:
976c2c09678SAapo Vienamo 		tegra_host->ddr_signaling = true;
977c2c09678SAapo Vienamo 		set_default_tap = true;
978c2c09678SAapo Vienamo 		break;
979c2c09678SAapo Vienamo 	default:
980c2c09678SAapo Vienamo 		set_default_tap = true;
981c2c09678SAapo Vienamo 		break;
982d4501d8eSAapo Vienamo 	}
983c2c09678SAapo Vienamo 
984ea8fc595SSowjanya Komatineni 	val = sdhci_readl(host, SDHCI_VNDR_TUN_CTRL0_0);
985ea8fc595SSowjanya Komatineni 	val &= ~(SDHCI_VNDR_TUN_CTRL0_TUN_ITER_MASK |
986ea8fc595SSowjanya Komatineni 		 SDHCI_VNDR_TUN_CTRL0_START_TAP_VAL_MASK |
987ea8fc595SSowjanya Komatineni 		 SDHCI_VNDR_TUN_CTRL0_MUL_M_MASK);
988ea8fc595SSowjanya Komatineni 	val |= (iter << SDHCI_VNDR_TUN_CTRL0_TUN_ITER_SHIFT |
989ea8fc595SSowjanya Komatineni 		0 << SDHCI_VNDR_TUN_CTRL0_START_TAP_VAL_SHIFT |
990ea8fc595SSowjanya Komatineni 		1 << SDHCI_VNDR_TUN_CTRL0_MUL_M_SHIFT);
991ea8fc595SSowjanya Komatineni 	sdhci_writel(host, val, SDHCI_VNDR_TUN_CTRL0_0);
992ea8fc595SSowjanya Komatineni 	sdhci_writel(host, 0, SDHCI_TEGRA_VNDR_TUN_CTRL1_0);
993ea8fc595SSowjanya Komatineni 
994ea8fc595SSowjanya Komatineni 	host->tuning_loop_count = (iter == TRIES_128) ? 128 : 256;
995ea8fc595SSowjanya Komatineni 
996c2c09678SAapo Vienamo 	sdhci_set_uhs_signaling(host, timing);
997c2c09678SAapo Vienamo 
998c2c09678SAapo Vienamo 	tegra_sdhci_pad_autocalib(host);
999c2c09678SAapo Vienamo 
1000ea8fc595SSowjanya Komatineni 	if (tegra_host->tuned_tap_delay && !set_default_tap)
1001ea8fc595SSowjanya Komatineni 		tegra_sdhci_set_tap(host, tegra_host->tuned_tap_delay);
1002ea8fc595SSowjanya Komatineni 	else
1003c2c09678SAapo Vienamo 		tegra_sdhci_set_tap(host, tegra_host->default_tap);
1004f5313aaaSAapo Vienamo 
1005f5313aaaSAapo Vienamo 	if (set_dqs_trim)
1006f5313aaaSAapo Vienamo 		tegra_sdhci_set_dqs_trim(host, tegra_host->dqs_trim);
1007bc5568bfSAapo Vienamo 
1008bc5568bfSAapo Vienamo 	if (do_hs400_dll_cal)
1009bc5568bfSAapo Vienamo 		tegra_sdhci_hs400_dll_cal(host);
1010c3c2384cSLucas Stach }
1011c3c2384cSLucas Stach 
1012c3c2384cSLucas Stach static int tegra_sdhci_execute_tuning(struct sdhci_host *host, u32 opcode)
1013c3c2384cSLucas Stach {
1014c3c2384cSLucas Stach 	unsigned int min, max;
1015c3c2384cSLucas Stach 
1016c3c2384cSLucas Stach 	/*
1017c3c2384cSLucas Stach 	 * Start search for minimum tap value at 10, as smaller values are
1018c3c2384cSLucas Stach 	 * may wrongly be reported as working but fail at higher speeds,
1019c3c2384cSLucas Stach 	 * according to the TRM.
1020c3c2384cSLucas Stach 	 */
1021c3c2384cSLucas Stach 	min = 10;
1022c3c2384cSLucas Stach 	while (min < 255) {
1023c3c2384cSLucas Stach 		tegra_sdhci_set_tap(host, min);
1024c3c2384cSLucas Stach 		if (!mmc_send_tuning(host->mmc, opcode, NULL))
1025c3c2384cSLucas Stach 			break;
1026c3c2384cSLucas Stach 		min++;
1027c3c2384cSLucas Stach 	}
1028c3c2384cSLucas Stach 
1029c3c2384cSLucas Stach 	/* Find the maximum tap value that still passes. */
1030c3c2384cSLucas Stach 	max = min + 1;
1031c3c2384cSLucas Stach 	while (max < 255) {
1032c3c2384cSLucas Stach 		tegra_sdhci_set_tap(host, max);
1033c3c2384cSLucas Stach 		if (mmc_send_tuning(host->mmc, opcode, NULL)) {
1034c3c2384cSLucas Stach 			max--;
1035c3c2384cSLucas Stach 			break;
1036c3c2384cSLucas Stach 		}
1037c3c2384cSLucas Stach 		max++;
1038c3c2384cSLucas Stach 	}
1039c3c2384cSLucas Stach 
1040c3c2384cSLucas Stach 	/* The TRM states the ideal tap value is at 75% in the passing range. */
1041c3c2384cSLucas Stach 	tegra_sdhci_set_tap(host, min + ((max - min) * 3 / 4));
1042c3c2384cSLucas Stach 
1043c3c2384cSLucas Stach 	return mmc_send_tuning(host->mmc, opcode, NULL);
1044c3c2384cSLucas Stach }
1045c3c2384cSLucas Stach 
104686ac2f8bSAapo Vienamo static int sdhci_tegra_start_signal_voltage_switch(struct mmc_host *mmc,
104786ac2f8bSAapo Vienamo 						   struct mmc_ios *ios)
104886ac2f8bSAapo Vienamo {
104986ac2f8bSAapo Vienamo 	struct sdhci_host *host = mmc_priv(mmc);
105044babea2SAapo Vienamo 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
105144babea2SAapo Vienamo 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
105286ac2f8bSAapo Vienamo 	int ret = 0;
105386ac2f8bSAapo Vienamo 
105486ac2f8bSAapo Vienamo 	if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330) {
1055de25fa5aSSowjanya Komatineni 		ret = tegra_sdhci_set_padctrl(host, ios->signal_voltage, true);
105686ac2f8bSAapo Vienamo 		if (ret < 0)
105786ac2f8bSAapo Vienamo 			return ret;
105886ac2f8bSAapo Vienamo 		ret = sdhci_start_signal_voltage_switch(mmc, ios);
105986ac2f8bSAapo Vienamo 	} else if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180) {
106086ac2f8bSAapo Vienamo 		ret = sdhci_start_signal_voltage_switch(mmc, ios);
106186ac2f8bSAapo Vienamo 		if (ret < 0)
106286ac2f8bSAapo Vienamo 			return ret;
1063de25fa5aSSowjanya Komatineni 		ret = tegra_sdhci_set_padctrl(host, ios->signal_voltage, true);
106486ac2f8bSAapo Vienamo 	}
106586ac2f8bSAapo Vienamo 
106644babea2SAapo Vienamo 	if (tegra_host->pad_calib_required)
106744babea2SAapo Vienamo 		tegra_sdhci_pad_autocalib(host);
106844babea2SAapo Vienamo 
106986ac2f8bSAapo Vienamo 	return ret;
107086ac2f8bSAapo Vienamo }
107186ac2f8bSAapo Vienamo 
107286ac2f8bSAapo Vienamo static int tegra_sdhci_init_pinctrl_info(struct device *dev,
107386ac2f8bSAapo Vienamo 					 struct sdhci_tegra *tegra_host)
107486ac2f8bSAapo Vienamo {
107586ac2f8bSAapo Vienamo 	tegra_host->pinctrl_sdmmc = devm_pinctrl_get(dev);
107686ac2f8bSAapo Vienamo 	if (IS_ERR(tegra_host->pinctrl_sdmmc)) {
107786ac2f8bSAapo Vienamo 		dev_dbg(dev, "No pinctrl info, err: %ld\n",
107886ac2f8bSAapo Vienamo 			PTR_ERR(tegra_host->pinctrl_sdmmc));
107986ac2f8bSAapo Vienamo 		return -1;
108086ac2f8bSAapo Vienamo 	}
108186ac2f8bSAapo Vienamo 
1082de25fa5aSSowjanya Komatineni 	tegra_host->pinctrl_state_1v8_drv = pinctrl_lookup_state(
1083de25fa5aSSowjanya Komatineni 				tegra_host->pinctrl_sdmmc, "sdmmc-1v8-drv");
1084de25fa5aSSowjanya Komatineni 	if (IS_ERR(tegra_host->pinctrl_state_1v8_drv)) {
1085de25fa5aSSowjanya Komatineni 		if (PTR_ERR(tegra_host->pinctrl_state_1v8_drv) == -ENODEV)
1086de25fa5aSSowjanya Komatineni 			tegra_host->pinctrl_state_1v8_drv = NULL;
1087de25fa5aSSowjanya Komatineni 	}
1088de25fa5aSSowjanya Komatineni 
1089de25fa5aSSowjanya Komatineni 	tegra_host->pinctrl_state_3v3_drv = pinctrl_lookup_state(
1090de25fa5aSSowjanya Komatineni 				tegra_host->pinctrl_sdmmc, "sdmmc-3v3-drv");
1091de25fa5aSSowjanya Komatineni 	if (IS_ERR(tegra_host->pinctrl_state_3v3_drv)) {
1092de25fa5aSSowjanya Komatineni 		if (PTR_ERR(tegra_host->pinctrl_state_3v3_drv) == -ENODEV)
1093de25fa5aSSowjanya Komatineni 			tegra_host->pinctrl_state_3v3_drv = NULL;
1094de25fa5aSSowjanya Komatineni 	}
1095de25fa5aSSowjanya Komatineni 
109686ac2f8bSAapo Vienamo 	tegra_host->pinctrl_state_3v3 =
109786ac2f8bSAapo Vienamo 		pinctrl_lookup_state(tegra_host->pinctrl_sdmmc, "sdmmc-3v3");
109886ac2f8bSAapo Vienamo 	if (IS_ERR(tegra_host->pinctrl_state_3v3)) {
109986ac2f8bSAapo Vienamo 		dev_warn(dev, "Missing 3.3V pad state, err: %ld\n",
110086ac2f8bSAapo Vienamo 			 PTR_ERR(tegra_host->pinctrl_state_3v3));
110186ac2f8bSAapo Vienamo 		return -1;
110286ac2f8bSAapo Vienamo 	}
110386ac2f8bSAapo Vienamo 
110486ac2f8bSAapo Vienamo 	tegra_host->pinctrl_state_1v8 =
110586ac2f8bSAapo Vienamo 		pinctrl_lookup_state(tegra_host->pinctrl_sdmmc, "sdmmc-1v8");
110686ac2f8bSAapo Vienamo 	if (IS_ERR(tegra_host->pinctrl_state_1v8)) {
110786ac2f8bSAapo Vienamo 		dev_warn(dev, "Missing 1.8V pad state, err: %ld\n",
1108e5378247SYueHaibing 			 PTR_ERR(tegra_host->pinctrl_state_1v8));
110986ac2f8bSAapo Vienamo 		return -1;
111086ac2f8bSAapo Vienamo 	}
111186ac2f8bSAapo Vienamo 
111286ac2f8bSAapo Vienamo 	tegra_host->pad_control_available = true;
111386ac2f8bSAapo Vienamo 
111486ac2f8bSAapo Vienamo 	return 0;
111586ac2f8bSAapo Vienamo }
111686ac2f8bSAapo Vienamo 
1117e5c63d91SLucas Stach static void tegra_sdhci_voltage_switch(struct sdhci_host *host)
1118e5c63d91SLucas Stach {
1119e5c63d91SLucas Stach 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1120e5c63d91SLucas Stach 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
1121e5c63d91SLucas Stach 	const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
1122e5c63d91SLucas Stach 
1123e5c63d91SLucas Stach 	if (soc_data->nvquirks & NVQUIRK_HAS_PADCALIB)
1124e5c63d91SLucas Stach 		tegra_host->pad_calib_required = true;
1125e5c63d91SLucas Stach }
1126e5c63d91SLucas Stach 
1127b7754428SSowjanya Komatineni static void tegra_cqhci_writel(struct cqhci_host *cq_host, u32 val, int reg)
1128b7754428SSowjanya Komatineni {
1129b7754428SSowjanya Komatineni 	struct mmc_host *mmc = cq_host->mmc;
1130b7754428SSowjanya Komatineni 	u8 ctrl;
1131b7754428SSowjanya Komatineni 	ktime_t timeout;
1132b7754428SSowjanya Komatineni 	bool timed_out;
1133b7754428SSowjanya Komatineni 
1134b7754428SSowjanya Komatineni 	/*
1135b7754428SSowjanya Komatineni 	 * During CQE resume/unhalt, CQHCI driver unhalts CQE prior to
1136b7754428SSowjanya Komatineni 	 * cqhci_host_ops enable where SDHCI DMA and BLOCK_SIZE registers need
1137b7754428SSowjanya Komatineni 	 * to be re-configured.
1138b7754428SSowjanya Komatineni 	 * Tegra CQHCI/SDHCI prevents write access to block size register when
1139b7754428SSowjanya Komatineni 	 * CQE is unhalted. So handling CQE resume sequence here to configure
1140b7754428SSowjanya Komatineni 	 * SDHCI block registers prior to exiting CQE halt state.
1141b7754428SSowjanya Komatineni 	 */
1142b7754428SSowjanya Komatineni 	if (reg == CQHCI_CTL && !(val & CQHCI_HALT) &&
1143b7754428SSowjanya Komatineni 	    cqhci_readl(cq_host, CQHCI_CTL) & CQHCI_HALT) {
1144b7754428SSowjanya Komatineni 		sdhci_cqe_enable(mmc);
1145b7754428SSowjanya Komatineni 		writel(val, cq_host->mmio + reg);
1146b7754428SSowjanya Komatineni 		timeout = ktime_add_us(ktime_get(), 50);
1147b7754428SSowjanya Komatineni 		while (1) {
1148b7754428SSowjanya Komatineni 			timed_out = ktime_compare(ktime_get(), timeout) > 0;
1149b7754428SSowjanya Komatineni 			ctrl = cqhci_readl(cq_host, CQHCI_CTL);
1150b7754428SSowjanya Komatineni 			if (!(ctrl & CQHCI_HALT) || timed_out)
1151b7754428SSowjanya Komatineni 				break;
1152b7754428SSowjanya Komatineni 		}
1153b7754428SSowjanya Komatineni 		/*
1154b7754428SSowjanya Komatineni 		 * CQE usually resumes very quick, but incase if Tegra CQE
1155b7754428SSowjanya Komatineni 		 * doesn't resume retry unhalt.
1156b7754428SSowjanya Komatineni 		 */
1157b7754428SSowjanya Komatineni 		if (timed_out)
1158b7754428SSowjanya Komatineni 			writel(val, cq_host->mmio + reg);
1159b7754428SSowjanya Komatineni 	} else {
1160b7754428SSowjanya Komatineni 		writel(val, cq_host->mmio + reg);
1161b7754428SSowjanya Komatineni 	}
1162b7754428SSowjanya Komatineni }
1163b7754428SSowjanya Komatineni 
1164c6e7ab90SSowjanya Komatineni static void sdhci_tegra_update_dcmd_desc(struct mmc_host *mmc,
1165c6e7ab90SSowjanya Komatineni 					 struct mmc_request *mrq, u64 *data)
1166c6e7ab90SSowjanya Komatineni {
1167c6e7ab90SSowjanya Komatineni 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(mmc_priv(mmc));
1168c6e7ab90SSowjanya Komatineni 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
1169c6e7ab90SSowjanya Komatineni 	const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
1170c6e7ab90SSowjanya Komatineni 
1171c6e7ab90SSowjanya Komatineni 	if (soc_data->nvquirks & NVQUIRK_CQHCI_DCMD_R1B_CMD_TIMING &&
1172c6e7ab90SSowjanya Komatineni 	    mrq->cmd->flags & MMC_RSP_R1B)
1173c6e7ab90SSowjanya Komatineni 		*data |= CQHCI_CMD_TIMING(1);
1174c6e7ab90SSowjanya Komatineni }
1175c6e7ab90SSowjanya Komatineni 
11763c4019f9SSowjanya Komatineni static void sdhci_tegra_cqe_enable(struct mmc_host *mmc)
11773c4019f9SSowjanya Komatineni {
11783c4019f9SSowjanya Komatineni 	struct cqhci_host *cq_host = mmc->cqe_private;
1179b7754428SSowjanya Komatineni 	u32 val;
11803c4019f9SSowjanya Komatineni 
11813c4019f9SSowjanya Komatineni 	/*
1182b7754428SSowjanya Komatineni 	 * Tegra CQHCI/SDMMC design prevents write access to sdhci block size
1183b7754428SSowjanya Komatineni 	 * register when CQE is enabled and unhalted.
1184b7754428SSowjanya Komatineni 	 * CQHCI driver enables CQE prior to activation, so disable CQE before
1185b7754428SSowjanya Komatineni 	 * programming block size in sdhci controller and enable it back.
11863c4019f9SSowjanya Komatineni 	 */
1187b7754428SSowjanya Komatineni 	if (!cq_host->activated) {
1188b7754428SSowjanya Komatineni 		val = cqhci_readl(cq_host, CQHCI_CFG);
1189b7754428SSowjanya Komatineni 		if (val & CQHCI_ENABLE)
1190b7754428SSowjanya Komatineni 			cqhci_writel(cq_host, (val & ~CQHCI_ENABLE),
1191b7754428SSowjanya Komatineni 				     CQHCI_CFG);
11923c4019f9SSowjanya Komatineni 		sdhci_cqe_enable(mmc);
1193b7754428SSowjanya Komatineni 		if (val & CQHCI_ENABLE)
1194b7754428SSowjanya Komatineni 			cqhci_writel(cq_host, val, CQHCI_CFG);
1195b7754428SSowjanya Komatineni 	}
11963c4019f9SSowjanya Komatineni 
1197b7754428SSowjanya Komatineni 	/*
1198b7754428SSowjanya Komatineni 	 * CMD CRC errors are seen sometimes with some eMMC devices when status
1199b7754428SSowjanya Komatineni 	 * command is sent during transfer of last data block which is the
1200b7754428SSowjanya Komatineni 	 * default case as send status command block counter (CBC) is 1.
1201b7754428SSowjanya Komatineni 	 * Recommended fix to set CBC to 0 allowing send status command only
1202b7754428SSowjanya Komatineni 	 * when data lines are idle.
1203b7754428SSowjanya Komatineni 	 */
1204b7754428SSowjanya Komatineni 	val = cqhci_readl(cq_host, CQHCI_SSC1);
1205b7754428SSowjanya Komatineni 	val &= ~CQHCI_SSC1_CBC_MASK;
1206b7754428SSowjanya Komatineni 	cqhci_writel(cq_host, val, CQHCI_SSC1);
12073c4019f9SSowjanya Komatineni }
12083c4019f9SSowjanya Komatineni 
12093c4019f9SSowjanya Komatineni static void sdhci_tegra_dumpregs(struct mmc_host *mmc)
12103c4019f9SSowjanya Komatineni {
12113c4019f9SSowjanya Komatineni 	sdhci_dumpregs(mmc_priv(mmc));
12123c4019f9SSowjanya Komatineni }
12133c4019f9SSowjanya Komatineni 
12143c4019f9SSowjanya Komatineni static u32 sdhci_tegra_cqhci_irq(struct sdhci_host *host, u32 intmask)
12153c4019f9SSowjanya Komatineni {
12163c4019f9SSowjanya Komatineni 	int cmd_error = 0;
12173c4019f9SSowjanya Komatineni 	int data_error = 0;
12183c4019f9SSowjanya Komatineni 
12193c4019f9SSowjanya Komatineni 	if (!sdhci_cqe_irq(host, intmask, &cmd_error, &data_error))
12203c4019f9SSowjanya Komatineni 		return intmask;
12213c4019f9SSowjanya Komatineni 
12223c4019f9SSowjanya Komatineni 	cqhci_irq(host->mmc, intmask, cmd_error, data_error);
12233c4019f9SSowjanya Komatineni 
12243c4019f9SSowjanya Komatineni 	return 0;
12253c4019f9SSowjanya Komatineni }
12263c4019f9SSowjanya Komatineni 
12273c4019f9SSowjanya Komatineni static const struct cqhci_host_ops sdhci_tegra_cqhci_ops = {
1228b7754428SSowjanya Komatineni 	.write_l    = tegra_cqhci_writel,
12293c4019f9SSowjanya Komatineni 	.enable	= sdhci_tegra_cqe_enable,
12303c4019f9SSowjanya Komatineni 	.disable = sdhci_cqe_disable,
12313c4019f9SSowjanya Komatineni 	.dumpregs = sdhci_tegra_dumpregs,
1232c6e7ab90SSowjanya Komatineni 	.update_dcmd_desc = sdhci_tegra_update_dcmd_desc,
12333c4019f9SSowjanya Komatineni };
12343c4019f9SSowjanya Komatineni 
1235c915568dSLars-Peter Clausen static const struct sdhci_ops tegra_sdhci_ops = {
123685d6509dSShawn Guo 	.read_w     = tegra_sdhci_readw,
123785d6509dSShawn Guo 	.write_l    = tegra_sdhci_writel,
1238a8e326a9SLucas Stach 	.set_clock  = tegra_sdhci_set_clock,
123914b04c6aSMichał Mirosław 	.set_bus_width = sdhci_set_bus_width,
124003231f9bSRussell King 	.reset      = tegra_sdhci_reset,
1241c3c2384cSLucas Stach 	.platform_execute_tuning = tegra_sdhci_execute_tuning,
1242a8e326a9SLucas Stach 	.set_uhs_signaling = tegra_sdhci_set_uhs_signaling,
1243e5c63d91SLucas Stach 	.voltage_switch = tegra_sdhci_voltage_switch,
124444350993SAapo Vienamo 	.get_max_clock = tegra_sdhci_get_max_clock,
124585d6509dSShawn Guo };
124603d2bfc8SOlof Johansson 
12471db5eebfSLars-Peter Clausen static const struct sdhci_pltfm_data sdhci_tegra20_pdata = {
124885d6509dSShawn Guo 	.quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
124985d6509dSShawn Guo 		  SDHCI_QUIRK_SINGLE_POWER_WRITE |
125085d6509dSShawn Guo 		  SDHCI_QUIRK_NO_HISPD_BIT |
1251f9260355SAndrew Bresticker 		  SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
1252f9260355SAndrew Bresticker 		  SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
125385d6509dSShawn Guo 	.ops  = &tegra_sdhci_ops,
125485d6509dSShawn Guo };
125585d6509dSShawn Guo 
1256d49d19c2SThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra20 = {
12573e44a1a7SStephen Warren 	.pdata = &sdhci_tegra20_pdata,
12583e44a1a7SStephen Warren 	.nvquirks = NVQUIRK_FORCE_SDHCI_SPEC_200 |
12593e44a1a7SStephen Warren 		    NVQUIRK_ENABLE_BLOCK_GAP_DET,
12603e44a1a7SStephen Warren };
12613e44a1a7SStephen Warren 
12621db5eebfSLars-Peter Clausen static const struct sdhci_pltfm_data sdhci_tegra30_pdata = {
12633e44a1a7SStephen Warren 	.quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
12643e44a1a7SStephen Warren 		  SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
12653e44a1a7SStephen Warren 		  SDHCI_QUIRK_SINGLE_POWER_WRITE |
12663e44a1a7SStephen Warren 		  SDHCI_QUIRK_NO_HISPD_BIT |
1267f9260355SAndrew Bresticker 		  SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
1268f9260355SAndrew Bresticker 		  SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
1269127407e3SStefan Agner 	.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
1270726df1d5SStefan Agner 		   SDHCI_QUIRK2_BROKEN_HS200 |
1271726df1d5SStefan Agner 		   /*
1272726df1d5SStefan Agner 		    * Auto-CMD23 leads to "Got command interrupt 0x00010000 even
1273726df1d5SStefan Agner 		    * though no command operation was in progress."
1274726df1d5SStefan Agner 		    *
1275726df1d5SStefan Agner 		    * The exact reason is unknown, as the same hardware seems
1276726df1d5SStefan Agner 		    * to support Auto CMD23 on a downstream 3.1 kernel.
1277726df1d5SStefan Agner 		    */
1278726df1d5SStefan Agner 		   SDHCI_QUIRK2_ACMD23_BROKEN,
12793e44a1a7SStephen Warren 	.ops  = &tegra_sdhci_ops,
12803e44a1a7SStephen Warren };
12813e44a1a7SStephen Warren 
1282d49d19c2SThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra30 = {
12833e44a1a7SStephen Warren 	.pdata = &sdhci_tegra30_pdata,
12843145351aSAndrew Bresticker 	.nvquirks = NVQUIRK_ENABLE_SDHCI_SPEC_300 |
12857ad2ed1dSLucas Stach 		    NVQUIRK_ENABLE_SDR50 |
1286e5c63d91SLucas Stach 		    NVQUIRK_ENABLE_SDR104 |
1287e5c63d91SLucas Stach 		    NVQUIRK_HAS_PADCALIB,
12883e44a1a7SStephen Warren };
12893e44a1a7SStephen Warren 
129001df7ecdSRhyland Klein static const struct sdhci_ops tegra114_sdhci_ops = {
129101df7ecdSRhyland Klein 	.read_w     = tegra_sdhci_readw,
129201df7ecdSRhyland Klein 	.write_w    = tegra_sdhci_writew,
129301df7ecdSRhyland Klein 	.write_l    = tegra_sdhci_writel,
1294a8e326a9SLucas Stach 	.set_clock  = tegra_sdhci_set_clock,
129514b04c6aSMichał Mirosław 	.set_bus_width = sdhci_set_bus_width,
129601df7ecdSRhyland Klein 	.reset      = tegra_sdhci_reset,
1297c3c2384cSLucas Stach 	.platform_execute_tuning = tegra_sdhci_execute_tuning,
1298a8e326a9SLucas Stach 	.set_uhs_signaling = tegra_sdhci_set_uhs_signaling,
1299e5c63d91SLucas Stach 	.voltage_switch = tegra_sdhci_voltage_switch,
130044350993SAapo Vienamo 	.get_max_clock = tegra_sdhci_get_max_clock,
130101df7ecdSRhyland Klein };
130201df7ecdSRhyland Klein 
13031db5eebfSLars-Peter Clausen static const struct sdhci_pltfm_data sdhci_tegra114_pdata = {
13045ebf2552SRhyland Klein 	.quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
13055ebf2552SRhyland Klein 		  SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
13065ebf2552SRhyland Klein 		  SDHCI_QUIRK_SINGLE_POWER_WRITE |
13075ebf2552SRhyland Klein 		  SDHCI_QUIRK_NO_HISPD_BIT |
1308f9260355SAndrew Bresticker 		  SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
1309f9260355SAndrew Bresticker 		  SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
1310a8e326a9SLucas Stach 	.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
131101df7ecdSRhyland Klein 	.ops  = &tegra114_sdhci_ops,
13125ebf2552SRhyland Klein };
13135ebf2552SRhyland Klein 
1314d49d19c2SThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra114 = {
13155ebf2552SRhyland Klein 	.pdata = &sdhci_tegra114_pdata,
13167bf037d6SJon Hunter };
13177bf037d6SJon Hunter 
13184ae12588SThierry Reding static const struct sdhci_pltfm_data sdhci_tegra124_pdata = {
13194ae12588SThierry Reding 	.quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
13204ae12588SThierry Reding 		  SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
13214ae12588SThierry Reding 		  SDHCI_QUIRK_SINGLE_POWER_WRITE |
13224ae12588SThierry Reding 		  SDHCI_QUIRK_NO_HISPD_BIT |
13234ae12588SThierry Reding 		  SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
13244ae12588SThierry Reding 		  SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
13254ae12588SThierry Reding 	.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
13264ae12588SThierry Reding 		   /*
13274ae12588SThierry Reding 		    * The TRM states that the SD/MMC controller found on
13284ae12588SThierry Reding 		    * Tegra124 can address 34 bits (the maximum supported by
13294ae12588SThierry Reding 		    * the Tegra memory controller), but tests show that DMA
13304ae12588SThierry Reding 		    * to or from above 4 GiB doesn't work. This is possibly
13314ae12588SThierry Reding 		    * caused by missing programming, though it's not obvious
13324ae12588SThierry Reding 		    * what sequence is required. Mark 64-bit DMA broken for
13334ae12588SThierry Reding 		    * now to fix this for existing users (e.g. Nyan boards).
13344ae12588SThierry Reding 		    */
13354ae12588SThierry Reding 		   SDHCI_QUIRK2_BROKEN_64_BIT_DMA,
13364ae12588SThierry Reding 	.ops  = &tegra114_sdhci_ops,
13374ae12588SThierry Reding };
13384ae12588SThierry Reding 
13394ae12588SThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra124 = {
13404ae12588SThierry Reding 	.pdata = &sdhci_tegra124_pdata,
13414ae12588SThierry Reding };
13424ae12588SThierry Reding 
13431070e83aSAapo Vienamo static const struct sdhci_ops tegra210_sdhci_ops = {
13441070e83aSAapo Vienamo 	.read_w     = tegra_sdhci_readw,
134538a284d9SAapo Vienamo 	.write_w    = tegra210_sdhci_writew,
13461070e83aSAapo Vienamo 	.write_l    = tegra_sdhci_writel,
13471070e83aSAapo Vienamo 	.set_clock  = tegra_sdhci_set_clock,
13481070e83aSAapo Vienamo 	.set_bus_width = sdhci_set_bus_width,
13491070e83aSAapo Vienamo 	.reset      = tegra_sdhci_reset,
13501070e83aSAapo Vienamo 	.set_uhs_signaling = tegra_sdhci_set_uhs_signaling,
13511070e83aSAapo Vienamo 	.voltage_switch = tegra_sdhci_voltage_switch,
13521070e83aSAapo Vienamo 	.get_max_clock = tegra_sdhci_get_max_clock,
13531070e83aSAapo Vienamo };
13541070e83aSAapo Vienamo 
1355b5a84ecfSThierry Reding static const struct sdhci_pltfm_data sdhci_tegra210_pdata = {
1356b5a84ecfSThierry Reding 	.quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
1357b5a84ecfSThierry Reding 		  SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
1358b5a84ecfSThierry Reding 		  SDHCI_QUIRK_SINGLE_POWER_WRITE |
1359b5a84ecfSThierry Reding 		  SDHCI_QUIRK_NO_HISPD_BIT |
1360a8e326a9SLucas Stach 		  SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
1361a8e326a9SLucas Stach 		  SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
1362a8e326a9SLucas Stach 	.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
13631070e83aSAapo Vienamo 	.ops  = &tegra210_sdhci_ops,
1364b5a84ecfSThierry Reding };
1365b5a84ecfSThierry Reding 
1366b5a84ecfSThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra210 = {
1367b5a84ecfSThierry Reding 	.pdata = &sdhci_tegra210_pdata,
1368d943f6e9SAapo Vienamo 	.nvquirks = NVQUIRK_NEEDS_PAD_CONTROL |
1369d4501d8eSAapo Vienamo 		    NVQUIRK_HAS_PADCALIB |
13703559d4a6SAapo Vienamo 		    NVQUIRK_DIS_CARD_CLK_CONFIG_TAP |
13713559d4a6SAapo Vienamo 		    NVQUIRK_ENABLE_SDR50 |
13723559d4a6SAapo Vienamo 		    NVQUIRK_ENABLE_SDR104,
1373ea8fc595SSowjanya Komatineni 	.min_tap_delay = 106,
1374ea8fc595SSowjanya Komatineni 	.max_tap_delay = 185,
1375b5a84ecfSThierry Reding };
1376b5a84ecfSThierry Reding 
137738a284d9SAapo Vienamo static const struct sdhci_ops tegra186_sdhci_ops = {
137838a284d9SAapo Vienamo 	.read_w     = tegra_sdhci_readw,
137938a284d9SAapo Vienamo 	.write_l    = tegra_sdhci_writel,
138038a284d9SAapo Vienamo 	.set_clock  = tegra_sdhci_set_clock,
138138a284d9SAapo Vienamo 	.set_bus_width = sdhci_set_bus_width,
138238a284d9SAapo Vienamo 	.reset      = tegra_sdhci_reset,
138338a284d9SAapo Vienamo 	.set_uhs_signaling = tegra_sdhci_set_uhs_signaling,
138438a284d9SAapo Vienamo 	.voltage_switch = tegra_sdhci_voltage_switch,
138538a284d9SAapo Vienamo 	.get_max_clock = tegra_sdhci_get_max_clock,
13863c4019f9SSowjanya Komatineni 	.irq = sdhci_tegra_cqhci_irq,
138738a284d9SAapo Vienamo };
138838a284d9SAapo Vienamo 
13894346b7c7SThierry Reding static const struct sdhci_pltfm_data sdhci_tegra186_pdata = {
13904346b7c7SThierry Reding 	.quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
13914346b7c7SThierry Reding 		  SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
13924346b7c7SThierry Reding 		  SDHCI_QUIRK_SINGLE_POWER_WRITE |
13934346b7c7SThierry Reding 		  SDHCI_QUIRK_NO_HISPD_BIT |
13944346b7c7SThierry Reding 		  SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
13954346b7c7SThierry Reding 		  SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
139668481a7eSKrishna Reddy 	.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
139768481a7eSKrishna Reddy 		   /* SDHCI controllers on Tegra186 support 40-bit addressing.
139868481a7eSKrishna Reddy 		    * IOVA addresses are 48-bit wide on Tegra186.
139968481a7eSKrishna Reddy 		    * With 64-bit dma mask used for SDHCI, accesses can
140068481a7eSKrishna Reddy 		    * be broken. Disable 64-bit dma, which would fall back
140168481a7eSKrishna Reddy 		    * to 32-bit dma mask. Ideally 40-bit dma mask would work,
140268481a7eSKrishna Reddy 		    * But it is not supported as of now.
140368481a7eSKrishna Reddy 		    */
140468481a7eSKrishna Reddy 		   SDHCI_QUIRK2_BROKEN_64_BIT_DMA,
140538a284d9SAapo Vienamo 	.ops  = &tegra186_sdhci_ops,
14064346b7c7SThierry Reding };
14074346b7c7SThierry Reding 
14084346b7c7SThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra186 = {
14094346b7c7SThierry Reding 	.pdata = &sdhci_tegra186_pdata,
1410d943f6e9SAapo Vienamo 	.nvquirks = NVQUIRK_NEEDS_PAD_CONTROL |
1411d4501d8eSAapo Vienamo 		    NVQUIRK_HAS_PADCALIB |
14122ad50051SAapo Vienamo 		    NVQUIRK_DIS_CARD_CLK_CONFIG_TAP |
14132ad50051SAapo Vienamo 		    NVQUIRK_ENABLE_SDR50 |
1414c6e7ab90SSowjanya Komatineni 		    NVQUIRK_ENABLE_SDR104 |
1415c6e7ab90SSowjanya Komatineni 		    NVQUIRK_CQHCI_DCMD_R1B_CMD_TIMING,
1416ea8fc595SSowjanya Komatineni 	.min_tap_delay = 84,
1417ea8fc595SSowjanya Komatineni 	.max_tap_delay = 136,
1418ea8fc595SSowjanya Komatineni };
1419ea8fc595SSowjanya Komatineni 
1420ea8fc595SSowjanya Komatineni static const struct sdhci_tegra_soc_data soc_data_tegra194 = {
1421ea8fc595SSowjanya Komatineni 	.pdata = &sdhci_tegra186_pdata,
1422ea8fc595SSowjanya Komatineni 	.nvquirks = NVQUIRK_NEEDS_PAD_CONTROL |
1423ea8fc595SSowjanya Komatineni 		    NVQUIRK_HAS_PADCALIB |
1424ea8fc595SSowjanya Komatineni 		    NVQUIRK_DIS_CARD_CLK_CONFIG_TAP |
1425ea8fc595SSowjanya Komatineni 		    NVQUIRK_ENABLE_SDR50 |
1426ea8fc595SSowjanya Komatineni 		    NVQUIRK_ENABLE_SDR104,
1427ea8fc595SSowjanya Komatineni 	.min_tap_delay = 96,
1428ea8fc595SSowjanya Komatineni 	.max_tap_delay = 139,
14294346b7c7SThierry Reding };
14304346b7c7SThierry Reding 
1431498d83e7SBill Pemberton static const struct of_device_id sdhci_tegra_dt_match[] = {
1432ea8fc595SSowjanya Komatineni 	{ .compatible = "nvidia,tegra194-sdhci", .data = &soc_data_tegra194 },
14334346b7c7SThierry Reding 	{ .compatible = "nvidia,tegra186-sdhci", .data = &soc_data_tegra186 },
1434b5a84ecfSThierry Reding 	{ .compatible = "nvidia,tegra210-sdhci", .data = &soc_data_tegra210 },
14354ae12588SThierry Reding 	{ .compatible = "nvidia,tegra124-sdhci", .data = &soc_data_tegra124 },
14365ebf2552SRhyland Klein 	{ .compatible = "nvidia,tegra114-sdhci", .data = &soc_data_tegra114 },
14373e44a1a7SStephen Warren 	{ .compatible = "nvidia,tegra30-sdhci", .data = &soc_data_tegra30 },
14383e44a1a7SStephen Warren 	{ .compatible = "nvidia,tegra20-sdhci", .data = &soc_data_tegra20 },
1439275173b2SGrant Likely 	{}
1440275173b2SGrant Likely };
1441e4404fabSArnd Bergmann MODULE_DEVICE_TABLE(of, sdhci_tegra_dt_match);
1442275173b2SGrant Likely 
14433c4019f9SSowjanya Komatineni static int sdhci_tegra_add_host(struct sdhci_host *host)
14443c4019f9SSowjanya Komatineni {
14453c4019f9SSowjanya Komatineni 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
14463c4019f9SSowjanya Komatineni 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
14473c4019f9SSowjanya Komatineni 	struct cqhci_host *cq_host;
14483c4019f9SSowjanya Komatineni 	bool dma64;
14493c4019f9SSowjanya Komatineni 	int ret;
14503c4019f9SSowjanya Komatineni 
14513c4019f9SSowjanya Komatineni 	if (!tegra_host->enable_hwcq)
14523c4019f9SSowjanya Komatineni 		return sdhci_add_host(host);
14533c4019f9SSowjanya Komatineni 
14543c4019f9SSowjanya Komatineni 	sdhci_enable_v4_mode(host);
14553c4019f9SSowjanya Komatineni 
14563c4019f9SSowjanya Komatineni 	ret = sdhci_setup_host(host);
14573c4019f9SSowjanya Komatineni 	if (ret)
14583c4019f9SSowjanya Komatineni 		return ret;
14593c4019f9SSowjanya Komatineni 
14603c4019f9SSowjanya Komatineni 	host->mmc->caps2 |= MMC_CAP2_CQE | MMC_CAP2_CQE_DCMD;
14613c4019f9SSowjanya Komatineni 
14623c4019f9SSowjanya Komatineni 	cq_host = devm_kzalloc(host->mmc->parent,
14633c4019f9SSowjanya Komatineni 				sizeof(*cq_host), GFP_KERNEL);
14643c4019f9SSowjanya Komatineni 	if (!cq_host) {
14653c4019f9SSowjanya Komatineni 		ret = -ENOMEM;
14663c4019f9SSowjanya Komatineni 		goto cleanup;
14673c4019f9SSowjanya Komatineni 	}
14683c4019f9SSowjanya Komatineni 
14693c4019f9SSowjanya Komatineni 	cq_host->mmio = host->ioaddr + SDHCI_TEGRA_CQE_BASE_ADDR;
14703c4019f9SSowjanya Komatineni 	cq_host->ops = &sdhci_tegra_cqhci_ops;
14713c4019f9SSowjanya Komatineni 
14723c4019f9SSowjanya Komatineni 	dma64 = host->flags & SDHCI_USE_64_BIT_DMA;
14733c4019f9SSowjanya Komatineni 	if (dma64)
14743c4019f9SSowjanya Komatineni 		cq_host->caps |= CQHCI_TASK_DESC_SZ_128;
14753c4019f9SSowjanya Komatineni 
14763c4019f9SSowjanya Komatineni 	ret = cqhci_init(cq_host, host->mmc, dma64);
14773c4019f9SSowjanya Komatineni 	if (ret)
14783c4019f9SSowjanya Komatineni 		goto cleanup;
14793c4019f9SSowjanya Komatineni 
14803c4019f9SSowjanya Komatineni 	ret = __sdhci_add_host(host);
14813c4019f9SSowjanya Komatineni 	if (ret)
14823c4019f9SSowjanya Komatineni 		goto cleanup;
14833c4019f9SSowjanya Komatineni 
14843c4019f9SSowjanya Komatineni 	return 0;
14853c4019f9SSowjanya Komatineni 
14863c4019f9SSowjanya Komatineni cleanup:
14873c4019f9SSowjanya Komatineni 	sdhci_cleanup_host(host);
14883c4019f9SSowjanya Komatineni 	return ret;
14893c4019f9SSowjanya Komatineni }
14903c4019f9SSowjanya Komatineni 
1491c3be1efdSBill Pemberton static int sdhci_tegra_probe(struct platform_device *pdev)
149203d2bfc8SOlof Johansson {
14933e44a1a7SStephen Warren 	const struct of_device_id *match;
14943e44a1a7SStephen Warren 	const struct sdhci_tegra_soc_data *soc_data;
14953e44a1a7SStephen Warren 	struct sdhci_host *host;
149685d6509dSShawn Guo 	struct sdhci_pltfm_host *pltfm_host;
14973e44a1a7SStephen Warren 	struct sdhci_tegra *tegra_host;
149803d2bfc8SOlof Johansson 	struct clk *clk;
149903d2bfc8SOlof Johansson 	int rc;
150003d2bfc8SOlof Johansson 
15013e44a1a7SStephen Warren 	match = of_match_device(sdhci_tegra_dt_match, &pdev->dev);
1502b37f9d98SJoseph Lo 	if (!match)
1503b37f9d98SJoseph Lo 		return -EINVAL;
15043e44a1a7SStephen Warren 	soc_data = match->data;
15053e44a1a7SStephen Warren 
15060734e79cSJisheng Zhang 	host = sdhci_pltfm_init(pdev, soc_data->pdata, sizeof(*tegra_host));
150785d6509dSShawn Guo 	if (IS_ERR(host))
150885d6509dSShawn Guo 		return PTR_ERR(host);
150985d6509dSShawn Guo 	pltfm_host = sdhci_priv(host);
151085d6509dSShawn Guo 
15110734e79cSJisheng Zhang 	tegra_host = sdhci_pltfm_priv(pltfm_host);
1512a8e326a9SLucas Stach 	tegra_host->ddr_signaling = false;
1513e5c63d91SLucas Stach 	tegra_host->pad_calib_required = false;
151486ac2f8bSAapo Vienamo 	tegra_host->pad_control_available = false;
15153e44a1a7SStephen Warren 	tegra_host->soc_data = soc_data;
1516275173b2SGrant Likely 
151786ac2f8bSAapo Vienamo 	if (soc_data->nvquirks & NVQUIRK_NEEDS_PAD_CONTROL) {
151886ac2f8bSAapo Vienamo 		rc = tegra_sdhci_init_pinctrl_info(&pdev->dev, tegra_host);
151986ac2f8bSAapo Vienamo 		if (rc == 0)
152086ac2f8bSAapo Vienamo 			host->mmc_host_ops.start_signal_voltage_switch =
152186ac2f8bSAapo Vienamo 				sdhci_tegra_start_signal_voltage_switch;
152286ac2f8bSAapo Vienamo 	}
152386ac2f8bSAapo Vienamo 
152461dad40eSAapo Vienamo 	/* Hook to periodically rerun pad calibration */
152561dad40eSAapo Vienamo 	if (soc_data->nvquirks & NVQUIRK_HAS_PADCALIB)
152661dad40eSAapo Vienamo 		host->mmc_host_ops.request = tegra_sdhci_request;
152761dad40eSAapo Vienamo 
1528dfc9700cSAapo Vienamo 	host->mmc_host_ops.hs400_enhanced_strobe =
1529dfc9700cSAapo Vienamo 			tegra_sdhci_hs400_enhanced_strobe;
1530dfc9700cSAapo Vienamo 
1531ea8fc595SSowjanya Komatineni 	if (!host->ops->platform_execute_tuning)
1532ea8fc595SSowjanya Komatineni 		host->mmc_host_ops.execute_tuning =
1533ea8fc595SSowjanya Komatineni 				tegra_sdhci_execute_hw_tuning;
1534ea8fc595SSowjanya Komatineni 
15352391b340SMylene JOSSERAND 	rc = mmc_of_parse(host->mmc);
153647caa84fSSimon Baatz 	if (rc)
153747caa84fSSimon Baatz 		goto err_parse_dt;
15380e786102SStephen Warren 
15397ad2ed1dSLucas Stach 	if (tegra_host->soc_data->nvquirks & NVQUIRK_ENABLE_DDR50)
1540c3c2384cSLucas Stach 		host->mmc->caps |= MMC_CAP_1_8V_DDR;
1541c3c2384cSLucas Stach 
15423c4019f9SSowjanya Komatineni 	tegra_sdhci_parse_dt(host);
154385c0da17SAapo Vienamo 
15442391b340SMylene JOSSERAND 	tegra_host->power_gpio = devm_gpiod_get_optional(&pdev->dev, "power",
15452391b340SMylene JOSSERAND 							 GPIOD_OUT_HIGH);
15462391b340SMylene JOSSERAND 	if (IS_ERR(tegra_host->power_gpio)) {
15472391b340SMylene JOSSERAND 		rc = PTR_ERR(tegra_host->power_gpio);
154885d6509dSShawn Guo 		goto err_power_req;
154903d2bfc8SOlof Johansson 	}
155003d2bfc8SOlof Johansson 
1551e4f79d9cSKevin Hao 	clk = devm_clk_get(mmc_dev(host->mmc), NULL);
155203d2bfc8SOlof Johansson 	if (IS_ERR(clk)) {
155303d2bfc8SOlof Johansson 		dev_err(mmc_dev(host->mmc), "clk err\n");
155403d2bfc8SOlof Johansson 		rc = PTR_ERR(clk);
155585d6509dSShawn Guo 		goto err_clk_get;
155603d2bfc8SOlof Johansson 	}
15571e674bc6SPrashant Gaikwad 	clk_prepare_enable(clk);
155803d2bfc8SOlof Johansson 	pltfm_host->clk = clk;
155903d2bfc8SOlof Johansson 
15602cd6c49dSPhilipp Zabel 	tegra_host->rst = devm_reset_control_get_exclusive(&pdev->dev,
15612cd6c49dSPhilipp Zabel 							   "sdhci");
156220567be9SThierry Reding 	if (IS_ERR(tegra_host->rst)) {
156320567be9SThierry Reding 		rc = PTR_ERR(tegra_host->rst);
156420567be9SThierry Reding 		dev_err(&pdev->dev, "failed to get reset control: %d\n", rc);
156520567be9SThierry Reding 		goto err_rst_get;
156620567be9SThierry Reding 	}
156720567be9SThierry Reding 
156820567be9SThierry Reding 	rc = reset_control_assert(tegra_host->rst);
156920567be9SThierry Reding 	if (rc)
157020567be9SThierry Reding 		goto err_rst_get;
157120567be9SThierry Reding 
157220567be9SThierry Reding 	usleep_range(2000, 4000);
157320567be9SThierry Reding 
157420567be9SThierry Reding 	rc = reset_control_deassert(tegra_host->rst);
157520567be9SThierry Reding 	if (rc)
157620567be9SThierry Reding 		goto err_rst_get;
157720567be9SThierry Reding 
157820567be9SThierry Reding 	usleep_range(2000, 4000);
157920567be9SThierry Reding 
15803c4019f9SSowjanya Komatineni 	rc = sdhci_tegra_add_host(host);
158185d6509dSShawn Guo 	if (rc)
158285d6509dSShawn Guo 		goto err_add_host;
158385d6509dSShawn Guo 
158403d2bfc8SOlof Johansson 	return 0;
158503d2bfc8SOlof Johansson 
158685d6509dSShawn Guo err_add_host:
158720567be9SThierry Reding 	reset_control_assert(tegra_host->rst);
158820567be9SThierry Reding err_rst_get:
15891e674bc6SPrashant Gaikwad 	clk_disable_unprepare(pltfm_host->clk);
159085d6509dSShawn Guo err_clk_get:
159185d6509dSShawn Guo err_power_req:
159247caa84fSSimon Baatz err_parse_dt:
159385d6509dSShawn Guo 	sdhci_pltfm_free(pdev);
159403d2bfc8SOlof Johansson 	return rc;
159503d2bfc8SOlof Johansson }
159603d2bfc8SOlof Johansson 
159720567be9SThierry Reding static int sdhci_tegra_remove(struct platform_device *pdev)
159820567be9SThierry Reding {
159920567be9SThierry Reding 	struct sdhci_host *host = platform_get_drvdata(pdev);
160020567be9SThierry Reding 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
160120567be9SThierry Reding 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
160220567be9SThierry Reding 
160320567be9SThierry Reding 	sdhci_remove_host(host, 0);
160420567be9SThierry Reding 
160520567be9SThierry Reding 	reset_control_assert(tegra_host->rst);
160620567be9SThierry Reding 	usleep_range(2000, 4000);
160720567be9SThierry Reding 	clk_disable_unprepare(pltfm_host->clk);
160820567be9SThierry Reding 
160920567be9SThierry Reding 	sdhci_pltfm_free(pdev);
161020567be9SThierry Reding 
161120567be9SThierry Reding 	return 0;
161220567be9SThierry Reding }
161320567be9SThierry Reding 
161471c733c4SSowjanya Komatineni #ifdef CONFIG_PM_SLEEP
161571c733c4SSowjanya Komatineni static int __maybe_unused sdhci_tegra_suspend(struct device *dev)
161671c733c4SSowjanya Komatineni {
161771c733c4SSowjanya Komatineni 	struct sdhci_host *host = dev_get_drvdata(dev);
161871c733c4SSowjanya Komatineni 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
161971c733c4SSowjanya Komatineni 	int ret;
162071c733c4SSowjanya Komatineni 
162171c733c4SSowjanya Komatineni 	if (host->mmc->caps2 & MMC_CAP2_CQE) {
162271c733c4SSowjanya Komatineni 		ret = cqhci_suspend(host->mmc);
162371c733c4SSowjanya Komatineni 		if (ret)
162471c733c4SSowjanya Komatineni 			return ret;
162571c733c4SSowjanya Komatineni 	}
162671c733c4SSowjanya Komatineni 
162771c733c4SSowjanya Komatineni 	ret = sdhci_suspend_host(host);
162871c733c4SSowjanya Komatineni 	if (ret) {
162971c733c4SSowjanya Komatineni 		cqhci_resume(host->mmc);
163071c733c4SSowjanya Komatineni 		return ret;
163171c733c4SSowjanya Komatineni 	}
163271c733c4SSowjanya Komatineni 
163371c733c4SSowjanya Komatineni 	clk_disable_unprepare(pltfm_host->clk);
163471c733c4SSowjanya Komatineni 	return 0;
163571c733c4SSowjanya Komatineni }
163671c733c4SSowjanya Komatineni 
163771c733c4SSowjanya Komatineni static int __maybe_unused sdhci_tegra_resume(struct device *dev)
163871c733c4SSowjanya Komatineni {
163971c733c4SSowjanya Komatineni 	struct sdhci_host *host = dev_get_drvdata(dev);
164071c733c4SSowjanya Komatineni 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
164171c733c4SSowjanya Komatineni 	int ret;
164271c733c4SSowjanya Komatineni 
164371c733c4SSowjanya Komatineni 	ret = clk_prepare_enable(pltfm_host->clk);
164471c733c4SSowjanya Komatineni 	if (ret)
164571c733c4SSowjanya Komatineni 		return ret;
164671c733c4SSowjanya Komatineni 
164771c733c4SSowjanya Komatineni 	ret = sdhci_resume_host(host);
164871c733c4SSowjanya Komatineni 	if (ret)
164971c733c4SSowjanya Komatineni 		goto disable_clk;
165071c733c4SSowjanya Komatineni 
165171c733c4SSowjanya Komatineni 	if (host->mmc->caps2 & MMC_CAP2_CQE) {
165271c733c4SSowjanya Komatineni 		ret = cqhci_resume(host->mmc);
165371c733c4SSowjanya Komatineni 		if (ret)
165471c733c4SSowjanya Komatineni 			goto suspend_host;
165571c733c4SSowjanya Komatineni 	}
165671c733c4SSowjanya Komatineni 
165771c733c4SSowjanya Komatineni 	return 0;
165871c733c4SSowjanya Komatineni 
165971c733c4SSowjanya Komatineni suspend_host:
166071c733c4SSowjanya Komatineni 	sdhci_suspend_host(host);
166171c733c4SSowjanya Komatineni disable_clk:
166271c733c4SSowjanya Komatineni 	clk_disable_unprepare(pltfm_host->clk);
166371c733c4SSowjanya Komatineni 	return ret;
166471c733c4SSowjanya Komatineni }
166571c733c4SSowjanya Komatineni #endif
166671c733c4SSowjanya Komatineni 
166771c733c4SSowjanya Komatineni static SIMPLE_DEV_PM_OPS(sdhci_tegra_dev_pm_ops, sdhci_tegra_suspend,
166871c733c4SSowjanya Komatineni 			 sdhci_tegra_resume);
166971c733c4SSowjanya Komatineni 
167085d6509dSShawn Guo static struct platform_driver sdhci_tegra_driver = {
167185d6509dSShawn Guo 	.driver		= {
167285d6509dSShawn Guo 		.name	= "sdhci-tegra",
1673275173b2SGrant Likely 		.of_match_table = sdhci_tegra_dt_match,
167471c733c4SSowjanya Komatineni 		.pm	= &sdhci_tegra_dev_pm_ops,
167585d6509dSShawn Guo 	},
167685d6509dSShawn Guo 	.probe		= sdhci_tegra_probe,
167720567be9SThierry Reding 	.remove		= sdhci_tegra_remove,
167803d2bfc8SOlof Johansson };
167903d2bfc8SOlof Johansson 
1680d1f81a64SAxel Lin module_platform_driver(sdhci_tegra_driver);
168185d6509dSShawn Guo 
168285d6509dSShawn Guo MODULE_DESCRIPTION("SDHCI driver for Tegra");
168385d6509dSShawn Guo MODULE_AUTHOR("Google, Inc.");
168485d6509dSShawn Guo MODULE_LICENSE("GPL v2");
1685