xref: /openbmc/linux/drivers/mmc/host/sdhci-tegra.c (revision f571389c)
19c92ab61SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
203d2bfc8SOlof Johansson /*
303d2bfc8SOlof Johansson  * Copyright (C) 2010 Google, Inc.
403d2bfc8SOlof Johansson  */
503d2bfc8SOlof Johansson 
6e5c63d91SLucas Stach #include <linux/delay.h>
7b960bc44SNicolin Chen #include <linux/dma-mapping.h>
803d2bfc8SOlof Johansson #include <linux/err.h>
996547f5dSPaul Gortmaker #include <linux/module.h>
1003d2bfc8SOlof Johansson #include <linux/init.h>
11e7c07148SAapo Vienamo #include <linux/iopoll.h>
1203d2bfc8SOlof Johansson #include <linux/platform_device.h>
1303d2bfc8SOlof Johansson #include <linux/clk.h>
1403d2bfc8SOlof Johansson #include <linux/io.h>
1555cd65e4SStephen Warren #include <linux/of.h>
163e44a1a7SStephen Warren #include <linux/of_device.h>
1786ac2f8bSAapo Vienamo #include <linux/pinctrl/consumer.h>
1886ac2f8bSAapo Vienamo #include <linux/regulator/consumer.h>
1920567be9SThierry Reding #include <linux/reset.h>
2003d2bfc8SOlof Johansson #include <linux/mmc/card.h>
2103d2bfc8SOlof Johansson #include <linux/mmc/host.h>
22c3c2384cSLucas Stach #include <linux/mmc/mmc.h>
230aacd23fSJoseph Lo #include <linux/mmc/slot-gpio.h>
242391b340SMylene JOSSERAND #include <linux/gpio/consumer.h>
2561dad40eSAapo Vienamo #include <linux/ktime.h>
2603d2bfc8SOlof Johansson 
2703d2bfc8SOlof Johansson #include "sdhci-pltfm.h"
283c4019f9SSowjanya Komatineni #include "cqhci.h"
2903d2bfc8SOlof Johansson 
30ca5879d3SPavan Kunapuli /* Tegra SDHOST controller vendor register definitions */
3174cd42bcSLucas Stach #define SDHCI_TEGRA_VENDOR_CLOCK_CTRL			0x100
32c3c2384cSLucas Stach #define SDHCI_CLOCK_CTRL_TAP_MASK			0x00ff0000
33c3c2384cSLucas Stach #define SDHCI_CLOCK_CTRL_TAP_SHIFT			16
3441a0b8d7SAapo Vienamo #define SDHCI_CLOCK_CTRL_TRIM_MASK			0x1f000000
3541a0b8d7SAapo Vienamo #define SDHCI_CLOCK_CTRL_TRIM_SHIFT			24
36c3c2384cSLucas Stach #define SDHCI_CLOCK_CTRL_SDR50_TUNING_OVERRIDE		BIT(5)
3774cd42bcSLucas Stach #define SDHCI_CLOCK_CTRL_PADPIPE_CLKEN_OVERRIDE		BIT(3)
3874cd42bcSLucas Stach #define SDHCI_CLOCK_CTRL_SPI_MODE_CLKEN_OVERRIDE	BIT(2)
3974cd42bcSLucas Stach 
40dfc9700cSAapo Vienamo #define SDHCI_TEGRA_VENDOR_SYS_SW_CTRL			0x104
41dfc9700cSAapo Vienamo #define SDHCI_TEGRA_SYS_SW_CTRL_ENHANCED_STROBE		BIT(31)
42dfc9700cSAapo Vienamo 
43f5313aaaSAapo Vienamo #define SDHCI_TEGRA_VENDOR_CAP_OVERRIDES		0x10c
44f5313aaaSAapo Vienamo #define SDHCI_TEGRA_CAP_OVERRIDES_DQS_TRIM_MASK		0x00003f00
45f5313aaaSAapo Vienamo #define SDHCI_TEGRA_CAP_OVERRIDES_DQS_TRIM_SHIFT	8
46f5313aaaSAapo Vienamo 
47ca5879d3SPavan Kunapuli #define SDHCI_TEGRA_VENDOR_MISC_CTRL			0x120
483145351aSAndrew Bresticker #define SDHCI_MISC_CTRL_ENABLE_SDR104			0x8
493145351aSAndrew Bresticker #define SDHCI_MISC_CTRL_ENABLE_SDR50			0x10
50ca5879d3SPavan Kunapuli #define SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300		0x20
513145351aSAndrew Bresticker #define SDHCI_MISC_CTRL_ENABLE_DDR50			0x200
52ca5879d3SPavan Kunapuli 
53bc5568bfSAapo Vienamo #define SDHCI_TEGRA_VENDOR_DLLCAL_CFG			0x1b0
54bc5568bfSAapo Vienamo #define SDHCI_TEGRA_DLLCAL_CALIBRATE			BIT(31)
55bc5568bfSAapo Vienamo 
56bc5568bfSAapo Vienamo #define SDHCI_TEGRA_VENDOR_DLLCAL_STA			0x1bc
57bc5568bfSAapo Vienamo #define SDHCI_TEGRA_DLLCAL_STA_ACTIVE			BIT(31)
58bc5568bfSAapo Vienamo 
59d4501d8eSAapo Vienamo #define SDHCI_VNDR_TUN_CTRL0_0				0x1c0
60d4501d8eSAapo Vienamo #define SDHCI_VNDR_TUN_CTRL0_TUN_HW_TAP			0x20000
61ea8fc595SSowjanya Komatineni #define SDHCI_VNDR_TUN_CTRL0_START_TAP_VAL_MASK		0x03fc0000
62ea8fc595SSowjanya Komatineni #define SDHCI_VNDR_TUN_CTRL0_START_TAP_VAL_SHIFT	18
63ea8fc595SSowjanya Komatineni #define SDHCI_VNDR_TUN_CTRL0_MUL_M_MASK			0x00001fc0
64ea8fc595SSowjanya Komatineni #define SDHCI_VNDR_TUN_CTRL0_MUL_M_SHIFT		6
65ea8fc595SSowjanya Komatineni #define SDHCI_VNDR_TUN_CTRL0_TUN_ITER_MASK		0x000e000
66ea8fc595SSowjanya Komatineni #define SDHCI_VNDR_TUN_CTRL0_TUN_ITER_SHIFT		13
67ea8fc595SSowjanya Komatineni #define TRIES_128					2
68ea8fc595SSowjanya Komatineni #define TRIES_256					4
69ea8fc595SSowjanya Komatineni #define SDHCI_VNDR_TUN_CTRL0_TUN_WORD_SEL_MASK		0x7
70ea8fc595SSowjanya Komatineni 
71ea8fc595SSowjanya Komatineni #define SDHCI_TEGRA_VNDR_TUN_CTRL1_0			0x1c4
72ea8fc595SSowjanya Komatineni #define SDHCI_TEGRA_VNDR_TUN_STATUS0			0x1C8
73ea8fc595SSowjanya Komatineni #define SDHCI_TEGRA_VNDR_TUN_STATUS1			0x1CC
74ea8fc595SSowjanya Komatineni #define SDHCI_TEGRA_VNDR_TUN_STATUS1_TAP_MASK		0xFF
75ea8fc595SSowjanya Komatineni #define SDHCI_TEGRA_VNDR_TUN_STATUS1_END_TAP_SHIFT	0x8
76ea8fc595SSowjanya Komatineni #define TUNING_WORD_BIT_SIZE				32
77d4501d8eSAapo Vienamo 
78e5c63d91SLucas Stach #define SDHCI_TEGRA_AUTO_CAL_CONFIG			0x1e4
79e5c63d91SLucas Stach #define SDHCI_AUTO_CAL_START				BIT(31)
80e5c63d91SLucas Stach #define SDHCI_AUTO_CAL_ENABLE				BIT(29)
8151b77c8eSAapo Vienamo #define SDHCI_AUTO_CAL_PDPU_OFFSET_MASK			0x0000ffff
82e5c63d91SLucas Stach 
839d548f11SAapo Vienamo #define SDHCI_TEGRA_SDMEM_COMP_PADCTRL			0x1e0
849d548f11SAapo Vienamo #define SDHCI_TEGRA_SDMEM_COMP_PADCTRL_VREF_SEL_MASK	0x0000000f
859d548f11SAapo Vienamo #define SDHCI_TEGRA_SDMEM_COMP_PADCTRL_VREF_SEL_VAL	0x7
86212b0cf1SAapo Vienamo #define SDHCI_TEGRA_SDMEM_COMP_PADCTRL_E_INPUT_E_PWRD	BIT(31)
87de25fa5aSSowjanya Komatineni #define SDHCI_COMP_PADCTRL_DRVUPDN_OFFSET_MASK		0x07FFF000
889d548f11SAapo Vienamo 
89e7c07148SAapo Vienamo #define SDHCI_TEGRA_AUTO_CAL_STATUS			0x1ec
90e7c07148SAapo Vienamo #define SDHCI_TEGRA_AUTO_CAL_ACTIVE			BIT(31)
91e7c07148SAapo Vienamo 
923e44a1a7SStephen Warren #define NVQUIRK_FORCE_SDHCI_SPEC_200			BIT(0)
933e44a1a7SStephen Warren #define NVQUIRK_ENABLE_BLOCK_GAP_DET			BIT(1)
94ca5879d3SPavan Kunapuli #define NVQUIRK_ENABLE_SDHCI_SPEC_300			BIT(2)
957ad2ed1dSLucas Stach #define NVQUIRK_ENABLE_SDR50				BIT(3)
967ad2ed1dSLucas Stach #define NVQUIRK_ENABLE_SDR104				BIT(4)
977ad2ed1dSLucas Stach #define NVQUIRK_ENABLE_DDR50				BIT(5)
98e5c63d91SLucas Stach #define NVQUIRK_HAS_PADCALIB				BIT(6)
9986ac2f8bSAapo Vienamo #define NVQUIRK_NEEDS_PAD_CONTROL			BIT(7)
100d4501d8eSAapo Vienamo #define NVQUIRK_DIS_CARD_CLK_CONFIG_TAP			BIT(8)
101c6e7ab90SSowjanya Komatineni #define NVQUIRK_CQHCI_DCMD_R1B_CMD_TIMING		BIT(9)
1023e44a1a7SStephen Warren 
1033c4019f9SSowjanya Komatineni /* SDMMC CQE Base Address for Tegra Host Ver 4.1 and Higher */
1043c4019f9SSowjanya Komatineni #define SDHCI_TEGRA_CQE_BASE_ADDR			0xF000
1053c4019f9SSowjanya Komatineni 
1063e44a1a7SStephen Warren struct sdhci_tegra_soc_data {
1071db5eebfSLars-Peter Clausen 	const struct sdhci_pltfm_data *pdata;
108b960bc44SNicolin Chen 	u64 dma_mask;
1093e44a1a7SStephen Warren 	u32 nvquirks;
110ea8fc595SSowjanya Komatineni 	u8 min_tap_delay;
111ea8fc595SSowjanya Komatineni 	u8 max_tap_delay;
1123e44a1a7SStephen Warren };
1133e44a1a7SStephen Warren 
11451b77c8eSAapo Vienamo /* Magic pull up and pull down pad calibration offsets */
11551b77c8eSAapo Vienamo struct sdhci_tegra_autocal_offsets {
11651b77c8eSAapo Vienamo 	u32 pull_up_3v3;
11751b77c8eSAapo Vienamo 	u32 pull_down_3v3;
11851b77c8eSAapo Vienamo 	u32 pull_up_3v3_timeout;
11951b77c8eSAapo Vienamo 	u32 pull_down_3v3_timeout;
12051b77c8eSAapo Vienamo 	u32 pull_up_1v8;
12151b77c8eSAapo Vienamo 	u32 pull_down_1v8;
12251b77c8eSAapo Vienamo 	u32 pull_up_1v8_timeout;
12351b77c8eSAapo Vienamo 	u32 pull_down_1v8_timeout;
12451b77c8eSAapo Vienamo 	u32 pull_up_sdr104;
12551b77c8eSAapo Vienamo 	u32 pull_down_sdr104;
12651b77c8eSAapo Vienamo 	u32 pull_up_hs400;
12751b77c8eSAapo Vienamo 	u32 pull_down_hs400;
12851b77c8eSAapo Vienamo };
12951b77c8eSAapo Vienamo 
1303e44a1a7SStephen Warren struct sdhci_tegra {
1313e44a1a7SStephen Warren 	const struct sdhci_tegra_soc_data *soc_data;
1322391b340SMylene JOSSERAND 	struct gpio_desc *power_gpio;
133a8e326a9SLucas Stach 	bool ddr_signaling;
134e5c63d91SLucas Stach 	bool pad_calib_required;
13586ac2f8bSAapo Vienamo 	bool pad_control_available;
13620567be9SThierry Reding 
13720567be9SThierry Reding 	struct reset_control *rst;
13886ac2f8bSAapo Vienamo 	struct pinctrl *pinctrl_sdmmc;
13986ac2f8bSAapo Vienamo 	struct pinctrl_state *pinctrl_state_3v3;
14086ac2f8bSAapo Vienamo 	struct pinctrl_state *pinctrl_state_1v8;
141de25fa5aSSowjanya Komatineni 	struct pinctrl_state *pinctrl_state_3v3_drv;
142de25fa5aSSowjanya Komatineni 	struct pinctrl_state *pinctrl_state_1v8_drv;
14351b77c8eSAapo Vienamo 
14451b77c8eSAapo Vienamo 	struct sdhci_tegra_autocal_offsets autocal_offsets;
14561dad40eSAapo Vienamo 	ktime_t last_calib;
14685c0da17SAapo Vienamo 
14785c0da17SAapo Vienamo 	u32 default_tap;
14885c0da17SAapo Vienamo 	u32 default_trim;
149f5313aaaSAapo Vienamo 	u32 dqs_trim;
1503c4019f9SSowjanya Komatineni 	bool enable_hwcq;
151ea8fc595SSowjanya Komatineni 	unsigned long curr_clk_rate;
152ea8fc595SSowjanya Komatineni 	u8 tuned_tap_delay;
1533e44a1a7SStephen Warren };
1543e44a1a7SStephen Warren 
15503d2bfc8SOlof Johansson static u16 tegra_sdhci_readw(struct sdhci_host *host, int reg)
15603d2bfc8SOlof Johansson {
1573e44a1a7SStephen Warren 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1580734e79cSJisheng Zhang 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
1593e44a1a7SStephen Warren 	const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
1603e44a1a7SStephen Warren 
1613e44a1a7SStephen Warren 	if (unlikely((soc_data->nvquirks & NVQUIRK_FORCE_SDHCI_SPEC_200) &&
1623e44a1a7SStephen Warren 			(reg == SDHCI_HOST_VERSION))) {
16303d2bfc8SOlof Johansson 		/* Erratum: Version register is invalid in HW. */
16403d2bfc8SOlof Johansson 		return SDHCI_SPEC_200;
16503d2bfc8SOlof Johansson 	}
16603d2bfc8SOlof Johansson 
16703d2bfc8SOlof Johansson 	return readw(host->ioaddr + reg);
16803d2bfc8SOlof Johansson }
16903d2bfc8SOlof Johansson 
170352ee868SPavan Kunapuli static void tegra_sdhci_writew(struct sdhci_host *host, u16 val, int reg)
171352ee868SPavan Kunapuli {
172352ee868SPavan Kunapuli 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
173352ee868SPavan Kunapuli 
174352ee868SPavan Kunapuli 	switch (reg) {
175352ee868SPavan Kunapuli 	case SDHCI_TRANSFER_MODE:
176352ee868SPavan Kunapuli 		/*
177352ee868SPavan Kunapuli 		 * Postpone this write, we must do it together with a
178352ee868SPavan Kunapuli 		 * command write that is down below.
179352ee868SPavan Kunapuli 		 */
180352ee868SPavan Kunapuli 		pltfm_host->xfer_mode_shadow = val;
181352ee868SPavan Kunapuli 		return;
182352ee868SPavan Kunapuli 	case SDHCI_COMMAND:
183352ee868SPavan Kunapuli 		writel((val << 16) | pltfm_host->xfer_mode_shadow,
184352ee868SPavan Kunapuli 			host->ioaddr + SDHCI_TRANSFER_MODE);
185352ee868SPavan Kunapuli 		return;
186352ee868SPavan Kunapuli 	}
187352ee868SPavan Kunapuli 
188352ee868SPavan Kunapuli 	writew(val, host->ioaddr + reg);
189352ee868SPavan Kunapuli }
190352ee868SPavan Kunapuli 
19103d2bfc8SOlof Johansson static void tegra_sdhci_writel(struct sdhci_host *host, u32 val, int reg)
19203d2bfc8SOlof Johansson {
1933e44a1a7SStephen Warren 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1940734e79cSJisheng Zhang 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
1953e44a1a7SStephen Warren 	const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
1963e44a1a7SStephen Warren 
19703d2bfc8SOlof Johansson 	/* Seems like we're getting spurious timeout and crc errors, so
19803d2bfc8SOlof Johansson 	 * disable signalling of them. In case of real errors software
19903d2bfc8SOlof Johansson 	 * timers should take care of eventually detecting them.
20003d2bfc8SOlof Johansson 	 */
20103d2bfc8SOlof Johansson 	if (unlikely(reg == SDHCI_SIGNAL_ENABLE))
20203d2bfc8SOlof Johansson 		val &= ~(SDHCI_INT_TIMEOUT|SDHCI_INT_CRC);
20303d2bfc8SOlof Johansson 
20403d2bfc8SOlof Johansson 	writel(val, host->ioaddr + reg);
20503d2bfc8SOlof Johansson 
2063e44a1a7SStephen Warren 	if (unlikely((soc_data->nvquirks & NVQUIRK_ENABLE_BLOCK_GAP_DET) &&
2073e44a1a7SStephen Warren 			(reg == SDHCI_INT_ENABLE))) {
20803d2bfc8SOlof Johansson 		/* Erratum: Must enable block gap interrupt detection */
20903d2bfc8SOlof Johansson 		u8 gap_ctrl = readb(host->ioaddr + SDHCI_BLOCK_GAP_CONTROL);
21003d2bfc8SOlof Johansson 		if (val & SDHCI_INT_CARD_INT)
21103d2bfc8SOlof Johansson 			gap_ctrl |= 0x8;
21203d2bfc8SOlof Johansson 		else
21303d2bfc8SOlof Johansson 			gap_ctrl &= ~0x8;
21403d2bfc8SOlof Johansson 		writeb(gap_ctrl, host->ioaddr + SDHCI_BLOCK_GAP_CONTROL);
21503d2bfc8SOlof Johansson 	}
21603d2bfc8SOlof Johansson }
21703d2bfc8SOlof Johansson 
21838a284d9SAapo Vienamo static bool tegra_sdhci_configure_card_clk(struct sdhci_host *host, bool enable)
21938a284d9SAapo Vienamo {
22038a284d9SAapo Vienamo 	bool status;
22138a284d9SAapo Vienamo 	u32 reg;
22238a284d9SAapo Vienamo 
22338a284d9SAapo Vienamo 	reg = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
22438a284d9SAapo Vienamo 	status = !!(reg & SDHCI_CLOCK_CARD_EN);
22538a284d9SAapo Vienamo 
22638a284d9SAapo Vienamo 	if (status == enable)
22738a284d9SAapo Vienamo 		return status;
22838a284d9SAapo Vienamo 
22938a284d9SAapo Vienamo 	if (enable)
23038a284d9SAapo Vienamo 		reg |= SDHCI_CLOCK_CARD_EN;
23138a284d9SAapo Vienamo 	else
23238a284d9SAapo Vienamo 		reg &= ~SDHCI_CLOCK_CARD_EN;
23338a284d9SAapo Vienamo 
23438a284d9SAapo Vienamo 	sdhci_writew(host, reg, SDHCI_CLOCK_CONTROL);
23538a284d9SAapo Vienamo 
23638a284d9SAapo Vienamo 	return status;
23738a284d9SAapo Vienamo }
23838a284d9SAapo Vienamo 
23938a284d9SAapo Vienamo static void tegra210_sdhci_writew(struct sdhci_host *host, u16 val, int reg)
24038a284d9SAapo Vienamo {
24138a284d9SAapo Vienamo 	bool is_tuning_cmd = 0;
24238a284d9SAapo Vienamo 	bool clk_enabled;
24338a284d9SAapo Vienamo 	u8 cmd;
24438a284d9SAapo Vienamo 
24538a284d9SAapo Vienamo 	if (reg == SDHCI_COMMAND) {
24638a284d9SAapo Vienamo 		cmd = SDHCI_GET_CMD(val);
24738a284d9SAapo Vienamo 		is_tuning_cmd = cmd == MMC_SEND_TUNING_BLOCK ||
24838a284d9SAapo Vienamo 				cmd == MMC_SEND_TUNING_BLOCK_HS200;
24938a284d9SAapo Vienamo 	}
25038a284d9SAapo Vienamo 
25138a284d9SAapo Vienamo 	if (is_tuning_cmd)
25238a284d9SAapo Vienamo 		clk_enabled = tegra_sdhci_configure_card_clk(host, 0);
25338a284d9SAapo Vienamo 
25438a284d9SAapo Vienamo 	writew(val, host->ioaddr + reg);
25538a284d9SAapo Vienamo 
25638a284d9SAapo Vienamo 	if (is_tuning_cmd) {
25738a284d9SAapo Vienamo 		udelay(1);
258ea8fc595SSowjanya Komatineni 		sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
25938a284d9SAapo Vienamo 		tegra_sdhci_configure_card_clk(host, clk_enabled);
26038a284d9SAapo Vienamo 	}
26138a284d9SAapo Vienamo }
26238a284d9SAapo Vienamo 
2630f686ca9SDmitry Osipenko static unsigned int tegra_sdhci_get_ro(struct sdhci_host *host)
2640f686ca9SDmitry Osipenko {
2650f686ca9SDmitry Osipenko 	/*
2660f686ca9SDmitry Osipenko 	 * Write-enable shall be assumed if GPIO is missing in a board's
2670f686ca9SDmitry Osipenko 	 * device-tree because SDHCI's WRITE_PROTECT bit doesn't work on
2680f686ca9SDmitry Osipenko 	 * Tegra.
2690f686ca9SDmitry Osipenko 	 */
2700f686ca9SDmitry Osipenko 	return mmc_gpio_get_ro(host->mmc);
2710f686ca9SDmitry Osipenko }
2720f686ca9SDmitry Osipenko 
27386ac2f8bSAapo Vienamo static bool tegra_sdhci_is_pad_and_regulator_valid(struct sdhci_host *host)
27486ac2f8bSAapo Vienamo {
27586ac2f8bSAapo Vienamo 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
27686ac2f8bSAapo Vienamo 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
27786ac2f8bSAapo Vienamo 	int has_1v8, has_3v3;
27886ac2f8bSAapo Vienamo 
27986ac2f8bSAapo Vienamo 	/*
28086ac2f8bSAapo Vienamo 	 * The SoCs which have NVQUIRK_NEEDS_PAD_CONTROL require software pad
28186ac2f8bSAapo Vienamo 	 * voltage configuration in order to perform voltage switching. This
28286ac2f8bSAapo Vienamo 	 * means that valid pinctrl info is required on SDHCI instances capable
28386ac2f8bSAapo Vienamo 	 * of performing voltage switching. Whether or not an SDHCI instance is
28486ac2f8bSAapo Vienamo 	 * capable of voltage switching is determined based on the regulator.
28586ac2f8bSAapo Vienamo 	 */
28686ac2f8bSAapo Vienamo 
28786ac2f8bSAapo Vienamo 	if (!(tegra_host->soc_data->nvquirks & NVQUIRK_NEEDS_PAD_CONTROL))
28886ac2f8bSAapo Vienamo 		return true;
28986ac2f8bSAapo Vienamo 
29086ac2f8bSAapo Vienamo 	if (IS_ERR(host->mmc->supply.vqmmc))
29186ac2f8bSAapo Vienamo 		return false;
29286ac2f8bSAapo Vienamo 
29386ac2f8bSAapo Vienamo 	has_1v8 = regulator_is_supported_voltage(host->mmc->supply.vqmmc,
29486ac2f8bSAapo Vienamo 						 1700000, 1950000);
29586ac2f8bSAapo Vienamo 
29686ac2f8bSAapo Vienamo 	has_3v3 = regulator_is_supported_voltage(host->mmc->supply.vqmmc,
29786ac2f8bSAapo Vienamo 						 2700000, 3600000);
29886ac2f8bSAapo Vienamo 
29986ac2f8bSAapo Vienamo 	if (has_1v8 == 1 && has_3v3 == 1)
30086ac2f8bSAapo Vienamo 		return tegra_host->pad_control_available;
30186ac2f8bSAapo Vienamo 
30286ac2f8bSAapo Vienamo 	/* Fixed voltage, no pad control required. */
30386ac2f8bSAapo Vienamo 	return true;
30486ac2f8bSAapo Vienamo }
30586ac2f8bSAapo Vienamo 
306c2c09678SAapo Vienamo static void tegra_sdhci_set_tap(struct sdhci_host *host, unsigned int tap)
307c2c09678SAapo Vienamo {
308c2c09678SAapo Vienamo 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
309c2c09678SAapo Vienamo 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
310c2c09678SAapo Vienamo 	const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
311c2c09678SAapo Vienamo 	bool card_clk_enabled = false;
312c2c09678SAapo Vienamo 	u32 reg;
313c2c09678SAapo Vienamo 
314c2c09678SAapo Vienamo 	/*
315c2c09678SAapo Vienamo 	 * Touching the tap values is a bit tricky on some SoC generations.
316c2c09678SAapo Vienamo 	 * The quirk enables a workaround for a glitch that sometimes occurs if
317c2c09678SAapo Vienamo 	 * the tap values are changed.
318c2c09678SAapo Vienamo 	 */
319c2c09678SAapo Vienamo 
320c2c09678SAapo Vienamo 	if (soc_data->nvquirks & NVQUIRK_DIS_CARD_CLK_CONFIG_TAP)
321c2c09678SAapo Vienamo 		card_clk_enabled = tegra_sdhci_configure_card_clk(host, false);
322c2c09678SAapo Vienamo 
323c2c09678SAapo Vienamo 	reg = sdhci_readl(host, SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
324c2c09678SAapo Vienamo 	reg &= ~SDHCI_CLOCK_CTRL_TAP_MASK;
325c2c09678SAapo Vienamo 	reg |= tap << SDHCI_CLOCK_CTRL_TAP_SHIFT;
326c2c09678SAapo Vienamo 	sdhci_writel(host, reg, SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
327c2c09678SAapo Vienamo 
328c2c09678SAapo Vienamo 	if (soc_data->nvquirks & NVQUIRK_DIS_CARD_CLK_CONFIG_TAP &&
329c2c09678SAapo Vienamo 	    card_clk_enabled) {
330c2c09678SAapo Vienamo 		udelay(1);
331c2c09678SAapo Vienamo 		sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
332c2c09678SAapo Vienamo 		tegra_sdhci_configure_card_clk(host, card_clk_enabled);
333c2c09678SAapo Vienamo 	}
334c2c09678SAapo Vienamo }
335c2c09678SAapo Vienamo 
336dfc9700cSAapo Vienamo static void tegra_sdhci_hs400_enhanced_strobe(struct mmc_host *mmc,
337dfc9700cSAapo Vienamo 					      struct mmc_ios *ios)
338dfc9700cSAapo Vienamo {
339dfc9700cSAapo Vienamo 	struct sdhci_host *host = mmc_priv(mmc);
340dfc9700cSAapo Vienamo 	u32 val;
341dfc9700cSAapo Vienamo 
342dfc9700cSAapo Vienamo 	val = sdhci_readl(host, SDHCI_TEGRA_VENDOR_SYS_SW_CTRL);
343dfc9700cSAapo Vienamo 
344dfc9700cSAapo Vienamo 	if (ios->enhanced_strobe)
345dfc9700cSAapo Vienamo 		val |= SDHCI_TEGRA_SYS_SW_CTRL_ENHANCED_STROBE;
346dfc9700cSAapo Vienamo 	else
347dfc9700cSAapo Vienamo 		val &= ~SDHCI_TEGRA_SYS_SW_CTRL_ENHANCED_STROBE;
348dfc9700cSAapo Vienamo 
349dfc9700cSAapo Vienamo 	sdhci_writel(host, val, SDHCI_TEGRA_VENDOR_SYS_SW_CTRL);
350dfc9700cSAapo Vienamo 
351dfc9700cSAapo Vienamo }
352dfc9700cSAapo Vienamo 
35303231f9bSRussell King static void tegra_sdhci_reset(struct sdhci_host *host, u8 mask)
354ca5879d3SPavan Kunapuli {
355ca5879d3SPavan Kunapuli 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
3560734e79cSJisheng Zhang 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
357ca5879d3SPavan Kunapuli 	const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
3589d548f11SAapo Vienamo 	u32 misc_ctrl, clk_ctrl, pad_ctrl;
359ca5879d3SPavan Kunapuli 
36003231f9bSRussell King 	sdhci_reset(host, mask);
36103231f9bSRussell King 
362ca5879d3SPavan Kunapuli 	if (!(mask & SDHCI_RESET_ALL))
363ca5879d3SPavan Kunapuli 		return;
364ca5879d3SPavan Kunapuli 
365c2c09678SAapo Vienamo 	tegra_sdhci_set_tap(host, tegra_host->default_tap);
366c2c09678SAapo Vienamo 
3671b84def8SLucas Stach 	misc_ctrl = sdhci_readl(host, SDHCI_TEGRA_VENDOR_MISC_CTRL);
3684f6aa326SJon Hunter 	clk_ctrl = sdhci_readl(host, SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
3694f6aa326SJon Hunter 
3704f6aa326SJon Hunter 	misc_ctrl &= ~(SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300 |
3714f6aa326SJon Hunter 		       SDHCI_MISC_CTRL_ENABLE_SDR50 |
3724f6aa326SJon Hunter 		       SDHCI_MISC_CTRL_ENABLE_DDR50 |
3734f6aa326SJon Hunter 		       SDHCI_MISC_CTRL_ENABLE_SDR104);
3744f6aa326SJon Hunter 
37541a0b8d7SAapo Vienamo 	clk_ctrl &= ~(SDHCI_CLOCK_CTRL_TRIM_MASK |
37641a0b8d7SAapo Vienamo 		      SDHCI_CLOCK_CTRL_SPI_MODE_CLKEN_OVERRIDE);
3774f6aa326SJon Hunter 
37886ac2f8bSAapo Vienamo 	if (tegra_sdhci_is_pad_and_regulator_valid(host)) {
379ca5879d3SPavan Kunapuli 		/* Erratum: Enable SDHCI spec v3.00 support */
3803145351aSAndrew Bresticker 		if (soc_data->nvquirks & NVQUIRK_ENABLE_SDHCI_SPEC_300)
381ca5879d3SPavan Kunapuli 			misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300;
3827ad2ed1dSLucas Stach 		/* Advertise UHS modes as supported by host */
3837ad2ed1dSLucas Stach 		if (soc_data->nvquirks & NVQUIRK_ENABLE_SDR50)
3847ad2ed1dSLucas Stach 			misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDR50;
3857ad2ed1dSLucas Stach 		if (soc_data->nvquirks & NVQUIRK_ENABLE_DDR50)
3867ad2ed1dSLucas Stach 			misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_DDR50;
3877ad2ed1dSLucas Stach 		if (soc_data->nvquirks & NVQUIRK_ENABLE_SDR104)
3887ad2ed1dSLucas Stach 			misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDR104;
389f571389cSMichał Mirosław 		if (soc_data->nvquirks & NVQUIRK_ENABLE_SDR50)
390c3c2384cSLucas Stach 			clk_ctrl |= SDHCI_CLOCK_CTRL_SDR50_TUNING_OVERRIDE;
3914f6aa326SJon Hunter 	}
3924f6aa326SJon Hunter 
39341a0b8d7SAapo Vienamo 	clk_ctrl |= tegra_host->default_trim << SDHCI_CLOCK_CTRL_TRIM_SHIFT;
39441a0b8d7SAapo Vienamo 
3954f6aa326SJon Hunter 	sdhci_writel(host, misc_ctrl, SDHCI_TEGRA_VENDOR_MISC_CTRL);
39674cd42bcSLucas Stach 	sdhci_writel(host, clk_ctrl, SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
39774cd42bcSLucas Stach 
3989d548f11SAapo Vienamo 	if (soc_data->nvquirks & NVQUIRK_HAS_PADCALIB) {
3999d548f11SAapo Vienamo 		pad_ctrl = sdhci_readl(host, SDHCI_TEGRA_SDMEM_COMP_PADCTRL);
4009d548f11SAapo Vienamo 		pad_ctrl &= ~SDHCI_TEGRA_SDMEM_COMP_PADCTRL_VREF_SEL_MASK;
4019d548f11SAapo Vienamo 		pad_ctrl |= SDHCI_TEGRA_SDMEM_COMP_PADCTRL_VREF_SEL_VAL;
4029d548f11SAapo Vienamo 		sdhci_writel(host, pad_ctrl, SDHCI_TEGRA_SDMEM_COMP_PADCTRL);
4039d548f11SAapo Vienamo 
404e5c63d91SLucas Stach 		tegra_host->pad_calib_required = true;
4059d548f11SAapo Vienamo 	}
406e5c63d91SLucas Stach 
407a8e326a9SLucas Stach 	tegra_host->ddr_signaling = false;
408ca5879d3SPavan Kunapuli }
409ca5879d3SPavan Kunapuli 
410212b0cf1SAapo Vienamo static void tegra_sdhci_configure_cal_pad(struct sdhci_host *host, bool enable)
411212b0cf1SAapo Vienamo {
412212b0cf1SAapo Vienamo 	u32 val;
413212b0cf1SAapo Vienamo 
414212b0cf1SAapo Vienamo 	/*
415212b0cf1SAapo Vienamo 	 * Enable or disable the additional I/O pad used by the drive strength
416212b0cf1SAapo Vienamo 	 * calibration process.
417212b0cf1SAapo Vienamo 	 */
418212b0cf1SAapo Vienamo 	val = sdhci_readl(host, SDHCI_TEGRA_SDMEM_COMP_PADCTRL);
419212b0cf1SAapo Vienamo 
420212b0cf1SAapo Vienamo 	if (enable)
421212b0cf1SAapo Vienamo 		val |= SDHCI_TEGRA_SDMEM_COMP_PADCTRL_E_INPUT_E_PWRD;
422212b0cf1SAapo Vienamo 	else
423212b0cf1SAapo Vienamo 		val &= ~SDHCI_TEGRA_SDMEM_COMP_PADCTRL_E_INPUT_E_PWRD;
424212b0cf1SAapo Vienamo 
425212b0cf1SAapo Vienamo 	sdhci_writel(host, val, SDHCI_TEGRA_SDMEM_COMP_PADCTRL);
426212b0cf1SAapo Vienamo 
427212b0cf1SAapo Vienamo 	if (enable)
428212b0cf1SAapo Vienamo 		usleep_range(1, 2);
429212b0cf1SAapo Vienamo }
430212b0cf1SAapo Vienamo 
43151b77c8eSAapo Vienamo static void tegra_sdhci_set_pad_autocal_offset(struct sdhci_host *host,
43251b77c8eSAapo Vienamo 					       u16 pdpu)
43351b77c8eSAapo Vienamo {
43451b77c8eSAapo Vienamo 	u32 reg;
43551b77c8eSAapo Vienamo 
43651b77c8eSAapo Vienamo 	reg = sdhci_readl(host, SDHCI_TEGRA_AUTO_CAL_CONFIG);
43751b77c8eSAapo Vienamo 	reg &= ~SDHCI_AUTO_CAL_PDPU_OFFSET_MASK;
43851b77c8eSAapo Vienamo 	reg |= pdpu;
43951b77c8eSAapo Vienamo 	sdhci_writel(host, reg, SDHCI_TEGRA_AUTO_CAL_CONFIG);
44051b77c8eSAapo Vienamo }
44151b77c8eSAapo Vienamo 
442de25fa5aSSowjanya Komatineni static int tegra_sdhci_set_padctrl(struct sdhci_host *host, int voltage,
443de25fa5aSSowjanya Komatineni 				   bool state_drvupdn)
444de25fa5aSSowjanya Komatineni {
445de25fa5aSSowjanya Komatineni 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
446de25fa5aSSowjanya Komatineni 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
447de25fa5aSSowjanya Komatineni 	struct sdhci_tegra_autocal_offsets *offsets =
448de25fa5aSSowjanya Komatineni 						&tegra_host->autocal_offsets;
449de25fa5aSSowjanya Komatineni 	struct pinctrl_state *pinctrl_drvupdn = NULL;
450de25fa5aSSowjanya Komatineni 	int ret = 0;
451de25fa5aSSowjanya Komatineni 	u8 drvup = 0, drvdn = 0;
452de25fa5aSSowjanya Komatineni 	u32 reg;
453de25fa5aSSowjanya Komatineni 
454de25fa5aSSowjanya Komatineni 	if (!state_drvupdn) {
455de25fa5aSSowjanya Komatineni 		/* PADS Drive Strength */
456de25fa5aSSowjanya Komatineni 		if (voltage == MMC_SIGNAL_VOLTAGE_180) {
457de25fa5aSSowjanya Komatineni 			if (tegra_host->pinctrl_state_1v8_drv) {
458de25fa5aSSowjanya Komatineni 				pinctrl_drvupdn =
459de25fa5aSSowjanya Komatineni 					tegra_host->pinctrl_state_1v8_drv;
460de25fa5aSSowjanya Komatineni 			} else {
461de25fa5aSSowjanya Komatineni 				drvup = offsets->pull_up_1v8_timeout;
462de25fa5aSSowjanya Komatineni 				drvdn = offsets->pull_down_1v8_timeout;
463de25fa5aSSowjanya Komatineni 			}
464de25fa5aSSowjanya Komatineni 		} else {
465de25fa5aSSowjanya Komatineni 			if (tegra_host->pinctrl_state_3v3_drv) {
466de25fa5aSSowjanya Komatineni 				pinctrl_drvupdn =
467de25fa5aSSowjanya Komatineni 					tegra_host->pinctrl_state_3v3_drv;
468de25fa5aSSowjanya Komatineni 			} else {
469de25fa5aSSowjanya Komatineni 				drvup = offsets->pull_up_3v3_timeout;
470de25fa5aSSowjanya Komatineni 				drvdn = offsets->pull_down_3v3_timeout;
471de25fa5aSSowjanya Komatineni 			}
472de25fa5aSSowjanya Komatineni 		}
473de25fa5aSSowjanya Komatineni 
474de25fa5aSSowjanya Komatineni 		if (pinctrl_drvupdn != NULL) {
475de25fa5aSSowjanya Komatineni 			ret = pinctrl_select_state(tegra_host->pinctrl_sdmmc,
476de25fa5aSSowjanya Komatineni 							pinctrl_drvupdn);
477de25fa5aSSowjanya Komatineni 			if (ret < 0)
478de25fa5aSSowjanya Komatineni 				dev_err(mmc_dev(host->mmc),
479de25fa5aSSowjanya Komatineni 					"failed pads drvupdn, ret: %d\n", ret);
480de25fa5aSSowjanya Komatineni 		} else if ((drvup) || (drvdn)) {
481de25fa5aSSowjanya Komatineni 			reg = sdhci_readl(host,
482de25fa5aSSowjanya Komatineni 					SDHCI_TEGRA_SDMEM_COMP_PADCTRL);
483de25fa5aSSowjanya Komatineni 			reg &= ~SDHCI_COMP_PADCTRL_DRVUPDN_OFFSET_MASK;
484de25fa5aSSowjanya Komatineni 			reg |= (drvup << 20) | (drvdn << 12);
485de25fa5aSSowjanya Komatineni 			sdhci_writel(host, reg,
486de25fa5aSSowjanya Komatineni 					SDHCI_TEGRA_SDMEM_COMP_PADCTRL);
487de25fa5aSSowjanya Komatineni 		}
488de25fa5aSSowjanya Komatineni 
489de25fa5aSSowjanya Komatineni 	} else {
490de25fa5aSSowjanya Komatineni 		/* Dual Voltage PADS Voltage selection */
491de25fa5aSSowjanya Komatineni 		if (!tegra_host->pad_control_available)
492de25fa5aSSowjanya Komatineni 			return 0;
493de25fa5aSSowjanya Komatineni 
494de25fa5aSSowjanya Komatineni 		if (voltage == MMC_SIGNAL_VOLTAGE_180) {
495de25fa5aSSowjanya Komatineni 			ret = pinctrl_select_state(tegra_host->pinctrl_sdmmc,
496de25fa5aSSowjanya Komatineni 						tegra_host->pinctrl_state_1v8);
497de25fa5aSSowjanya Komatineni 			if (ret < 0)
498de25fa5aSSowjanya Komatineni 				dev_err(mmc_dev(host->mmc),
499de25fa5aSSowjanya Komatineni 					"setting 1.8V failed, ret: %d\n", ret);
500de25fa5aSSowjanya Komatineni 		} else {
501de25fa5aSSowjanya Komatineni 			ret = pinctrl_select_state(tegra_host->pinctrl_sdmmc,
502de25fa5aSSowjanya Komatineni 						tegra_host->pinctrl_state_3v3);
503de25fa5aSSowjanya Komatineni 			if (ret < 0)
504de25fa5aSSowjanya Komatineni 				dev_err(mmc_dev(host->mmc),
505de25fa5aSSowjanya Komatineni 					"setting 3.3V failed, ret: %d\n", ret);
506de25fa5aSSowjanya Komatineni 		}
507de25fa5aSSowjanya Komatineni 	}
508de25fa5aSSowjanya Komatineni 
509de25fa5aSSowjanya Komatineni 	return ret;
510de25fa5aSSowjanya Komatineni }
511de25fa5aSSowjanya Komatineni 
512e5c63d91SLucas Stach static void tegra_sdhci_pad_autocalib(struct sdhci_host *host)
513e5c63d91SLucas Stach {
51451b77c8eSAapo Vienamo 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
51551b77c8eSAapo Vienamo 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
51651b77c8eSAapo Vienamo 	struct sdhci_tegra_autocal_offsets offsets =
51751b77c8eSAapo Vienamo 			tegra_host->autocal_offsets;
51851b77c8eSAapo Vienamo 	struct mmc_ios *ios = &host->mmc->ios;
519887bda8fSAapo Vienamo 	bool card_clk_enabled;
52051b77c8eSAapo Vienamo 	u16 pdpu;
521e7c07148SAapo Vienamo 	u32 reg;
522e7c07148SAapo Vienamo 	int ret;
523e5c63d91SLucas Stach 
52451b77c8eSAapo Vienamo 	switch (ios->timing) {
52551b77c8eSAapo Vienamo 	case MMC_TIMING_UHS_SDR104:
52651b77c8eSAapo Vienamo 		pdpu = offsets.pull_down_sdr104 << 8 | offsets.pull_up_sdr104;
52751b77c8eSAapo Vienamo 		break;
52851b77c8eSAapo Vienamo 	case MMC_TIMING_MMC_HS400:
52951b77c8eSAapo Vienamo 		pdpu = offsets.pull_down_hs400 << 8 | offsets.pull_up_hs400;
53051b77c8eSAapo Vienamo 		break;
53151b77c8eSAapo Vienamo 	default:
53251b77c8eSAapo Vienamo 		if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180)
53351b77c8eSAapo Vienamo 			pdpu = offsets.pull_down_1v8 << 8 | offsets.pull_up_1v8;
53451b77c8eSAapo Vienamo 		else
53551b77c8eSAapo Vienamo 			pdpu = offsets.pull_down_3v3 << 8 | offsets.pull_up_3v3;
53651b77c8eSAapo Vienamo 	}
53751b77c8eSAapo Vienamo 
538de25fa5aSSowjanya Komatineni 	/* Set initial offset before auto-calibration */
53951b77c8eSAapo Vienamo 	tegra_sdhci_set_pad_autocal_offset(host, pdpu);
54051b77c8eSAapo Vienamo 
541887bda8fSAapo Vienamo 	card_clk_enabled = tegra_sdhci_configure_card_clk(host, false);
542887bda8fSAapo Vienamo 
543212b0cf1SAapo Vienamo 	tegra_sdhci_configure_cal_pad(host, true);
544212b0cf1SAapo Vienamo 
545e7c07148SAapo Vienamo 	reg = sdhci_readl(host, SDHCI_TEGRA_AUTO_CAL_CONFIG);
546e7c07148SAapo Vienamo 	reg |= SDHCI_AUTO_CAL_ENABLE | SDHCI_AUTO_CAL_START;
547e7c07148SAapo Vienamo 	sdhci_writel(host, reg, SDHCI_TEGRA_AUTO_CAL_CONFIG);
548e5c63d91SLucas Stach 
549e7c07148SAapo Vienamo 	usleep_range(1, 2);
550e7c07148SAapo Vienamo 	/* 10 ms timeout */
551e7c07148SAapo Vienamo 	ret = readl_poll_timeout(host->ioaddr + SDHCI_TEGRA_AUTO_CAL_STATUS,
552e7c07148SAapo Vienamo 				 reg, !(reg & SDHCI_TEGRA_AUTO_CAL_ACTIVE),
553e7c07148SAapo Vienamo 				 1000, 10000);
554e7c07148SAapo Vienamo 
555212b0cf1SAapo Vienamo 	tegra_sdhci_configure_cal_pad(host, false);
556212b0cf1SAapo Vienamo 
557887bda8fSAapo Vienamo 	tegra_sdhci_configure_card_clk(host, card_clk_enabled);
558887bda8fSAapo Vienamo 
55951b77c8eSAapo Vienamo 	if (ret) {
560e7c07148SAapo Vienamo 		dev_err(mmc_dev(host->mmc), "Pad autocal timed out\n");
56151b77c8eSAapo Vienamo 
562de25fa5aSSowjanya Komatineni 		/* Disable automatic cal and use fixed Drive Strengths */
56351b77c8eSAapo Vienamo 		reg = sdhci_readl(host, SDHCI_TEGRA_AUTO_CAL_CONFIG);
56451b77c8eSAapo Vienamo 		reg &= ~SDHCI_AUTO_CAL_ENABLE;
56551b77c8eSAapo Vienamo 		sdhci_writel(host, reg, SDHCI_TEGRA_AUTO_CAL_CONFIG);
56651b77c8eSAapo Vienamo 
567de25fa5aSSowjanya Komatineni 		ret = tegra_sdhci_set_padctrl(host, ios->signal_voltage, false);
568de25fa5aSSowjanya Komatineni 		if (ret < 0)
569de25fa5aSSowjanya Komatineni 			dev_err(mmc_dev(host->mmc),
570de25fa5aSSowjanya Komatineni 				"Setting drive strengths failed: %d\n", ret);
57151b77c8eSAapo Vienamo 	}
57251b77c8eSAapo Vienamo }
57351b77c8eSAapo Vienamo 
57451b77c8eSAapo Vienamo static void tegra_sdhci_parse_pad_autocal_dt(struct sdhci_host *host)
57551b77c8eSAapo Vienamo {
57651b77c8eSAapo Vienamo 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
57751b77c8eSAapo Vienamo 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
57851b77c8eSAapo Vienamo 	struct sdhci_tegra_autocal_offsets *autocal =
57951b77c8eSAapo Vienamo 			&tegra_host->autocal_offsets;
58051b77c8eSAapo Vienamo 	int err;
58151b77c8eSAapo Vienamo 
58251b77c8eSAapo Vienamo 	err = device_property_read_u32(host->mmc->parent,
58351b77c8eSAapo Vienamo 			"nvidia,pad-autocal-pull-up-offset-3v3",
58451b77c8eSAapo Vienamo 			&autocal->pull_up_3v3);
58551b77c8eSAapo Vienamo 	if (err)
58651b77c8eSAapo Vienamo 		autocal->pull_up_3v3 = 0;
58751b77c8eSAapo Vienamo 
58851b77c8eSAapo Vienamo 	err = device_property_read_u32(host->mmc->parent,
58951b77c8eSAapo Vienamo 			"nvidia,pad-autocal-pull-down-offset-3v3",
59051b77c8eSAapo Vienamo 			&autocal->pull_down_3v3);
59151b77c8eSAapo Vienamo 	if (err)
59251b77c8eSAapo Vienamo 		autocal->pull_down_3v3 = 0;
59351b77c8eSAapo Vienamo 
59451b77c8eSAapo Vienamo 	err = device_property_read_u32(host->mmc->parent,
59551b77c8eSAapo Vienamo 			"nvidia,pad-autocal-pull-up-offset-1v8",
59651b77c8eSAapo Vienamo 			&autocal->pull_up_1v8);
59751b77c8eSAapo Vienamo 	if (err)
59851b77c8eSAapo Vienamo 		autocal->pull_up_1v8 = 0;
59951b77c8eSAapo Vienamo 
60051b77c8eSAapo Vienamo 	err = device_property_read_u32(host->mmc->parent,
60151b77c8eSAapo Vienamo 			"nvidia,pad-autocal-pull-down-offset-1v8",
60251b77c8eSAapo Vienamo 			&autocal->pull_down_1v8);
60351b77c8eSAapo Vienamo 	if (err)
60451b77c8eSAapo Vienamo 		autocal->pull_down_1v8 = 0;
60551b77c8eSAapo Vienamo 
60651b77c8eSAapo Vienamo 	err = device_property_read_u32(host->mmc->parent,
60751b77c8eSAapo Vienamo 			"nvidia,pad-autocal-pull-up-offset-3v3-timeout",
6085ccf7f55SSowjanya Komatineni 			&autocal->pull_up_3v3_timeout);
609de25fa5aSSowjanya Komatineni 	if (err) {
610de25fa5aSSowjanya Komatineni 		if (!IS_ERR(tegra_host->pinctrl_state_3v3) &&
611de25fa5aSSowjanya Komatineni 			(tegra_host->pinctrl_state_3v3_drv == NULL))
612de25fa5aSSowjanya Komatineni 			pr_warn("%s: Missing autocal timeout 3v3-pad drvs\n",
613de25fa5aSSowjanya Komatineni 				mmc_hostname(host->mmc));
61451b77c8eSAapo Vienamo 		autocal->pull_up_3v3_timeout = 0;
615de25fa5aSSowjanya Komatineni 	}
61651b77c8eSAapo Vienamo 
61751b77c8eSAapo Vienamo 	err = device_property_read_u32(host->mmc->parent,
61851b77c8eSAapo Vienamo 			"nvidia,pad-autocal-pull-down-offset-3v3-timeout",
6195ccf7f55SSowjanya Komatineni 			&autocal->pull_down_3v3_timeout);
620de25fa5aSSowjanya Komatineni 	if (err) {
621de25fa5aSSowjanya Komatineni 		if (!IS_ERR(tegra_host->pinctrl_state_3v3) &&
622de25fa5aSSowjanya Komatineni 			(tegra_host->pinctrl_state_3v3_drv == NULL))
623de25fa5aSSowjanya Komatineni 			pr_warn("%s: Missing autocal timeout 3v3-pad drvs\n",
624de25fa5aSSowjanya Komatineni 				mmc_hostname(host->mmc));
62551b77c8eSAapo Vienamo 		autocal->pull_down_3v3_timeout = 0;
626de25fa5aSSowjanya Komatineni 	}
62751b77c8eSAapo Vienamo 
62851b77c8eSAapo Vienamo 	err = device_property_read_u32(host->mmc->parent,
62951b77c8eSAapo Vienamo 			"nvidia,pad-autocal-pull-up-offset-1v8-timeout",
6305ccf7f55SSowjanya Komatineni 			&autocal->pull_up_1v8_timeout);
631de25fa5aSSowjanya Komatineni 	if (err) {
632de25fa5aSSowjanya Komatineni 		if (!IS_ERR(tegra_host->pinctrl_state_1v8) &&
633de25fa5aSSowjanya Komatineni 			(tegra_host->pinctrl_state_1v8_drv == NULL))
634de25fa5aSSowjanya Komatineni 			pr_warn("%s: Missing autocal timeout 1v8-pad drvs\n",
635de25fa5aSSowjanya Komatineni 				mmc_hostname(host->mmc));
63651b77c8eSAapo Vienamo 		autocal->pull_up_1v8_timeout = 0;
637de25fa5aSSowjanya Komatineni 	}
63851b77c8eSAapo Vienamo 
63951b77c8eSAapo Vienamo 	err = device_property_read_u32(host->mmc->parent,
64051b77c8eSAapo Vienamo 			"nvidia,pad-autocal-pull-down-offset-1v8-timeout",
6415ccf7f55SSowjanya Komatineni 			&autocal->pull_down_1v8_timeout);
642de25fa5aSSowjanya Komatineni 	if (err) {
643de25fa5aSSowjanya Komatineni 		if (!IS_ERR(tegra_host->pinctrl_state_1v8) &&
644de25fa5aSSowjanya Komatineni 			(tegra_host->pinctrl_state_1v8_drv == NULL))
645de25fa5aSSowjanya Komatineni 			pr_warn("%s: Missing autocal timeout 1v8-pad drvs\n",
646de25fa5aSSowjanya Komatineni 				mmc_hostname(host->mmc));
64751b77c8eSAapo Vienamo 		autocal->pull_down_1v8_timeout = 0;
648de25fa5aSSowjanya Komatineni 	}
64951b77c8eSAapo Vienamo 
65051b77c8eSAapo Vienamo 	err = device_property_read_u32(host->mmc->parent,
65151b77c8eSAapo Vienamo 			"nvidia,pad-autocal-pull-up-offset-sdr104",
65251b77c8eSAapo Vienamo 			&autocal->pull_up_sdr104);
65351b77c8eSAapo Vienamo 	if (err)
65451b77c8eSAapo Vienamo 		autocal->pull_up_sdr104 = autocal->pull_up_1v8;
65551b77c8eSAapo Vienamo 
65651b77c8eSAapo Vienamo 	err = device_property_read_u32(host->mmc->parent,
65751b77c8eSAapo Vienamo 			"nvidia,pad-autocal-pull-down-offset-sdr104",
65851b77c8eSAapo Vienamo 			&autocal->pull_down_sdr104);
65951b77c8eSAapo Vienamo 	if (err)
66051b77c8eSAapo Vienamo 		autocal->pull_down_sdr104 = autocal->pull_down_1v8;
66151b77c8eSAapo Vienamo 
66251b77c8eSAapo Vienamo 	err = device_property_read_u32(host->mmc->parent,
66351b77c8eSAapo Vienamo 			"nvidia,pad-autocal-pull-up-offset-hs400",
66451b77c8eSAapo Vienamo 			&autocal->pull_up_hs400);
66551b77c8eSAapo Vienamo 	if (err)
66651b77c8eSAapo Vienamo 		autocal->pull_up_hs400 = autocal->pull_up_1v8;
66751b77c8eSAapo Vienamo 
66851b77c8eSAapo Vienamo 	err = device_property_read_u32(host->mmc->parent,
66951b77c8eSAapo Vienamo 			"nvidia,pad-autocal-pull-down-offset-hs400",
67051b77c8eSAapo Vienamo 			&autocal->pull_down_hs400);
67151b77c8eSAapo Vienamo 	if (err)
67251b77c8eSAapo Vienamo 		autocal->pull_down_hs400 = autocal->pull_down_1v8;
673e5c63d91SLucas Stach }
674e5c63d91SLucas Stach 
67561dad40eSAapo Vienamo static void tegra_sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
67661dad40eSAapo Vienamo {
67761dad40eSAapo Vienamo 	struct sdhci_host *host = mmc_priv(mmc);
67861dad40eSAapo Vienamo 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
67961dad40eSAapo Vienamo 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
68061dad40eSAapo Vienamo 	ktime_t since_calib = ktime_sub(ktime_get(), tegra_host->last_calib);
68161dad40eSAapo Vienamo 
68261dad40eSAapo Vienamo 	/* 100 ms calibration interval is specified in the TRM */
68361dad40eSAapo Vienamo 	if (ktime_to_ms(since_calib) > 100) {
68461dad40eSAapo Vienamo 		tegra_sdhci_pad_autocalib(host);
68561dad40eSAapo Vienamo 		tegra_host->last_calib = ktime_get();
68661dad40eSAapo Vienamo 	}
68761dad40eSAapo Vienamo 
68861dad40eSAapo Vienamo 	sdhci_request(mmc, mrq);
68961dad40eSAapo Vienamo }
69061dad40eSAapo Vienamo 
691f5313aaaSAapo Vienamo static void tegra_sdhci_parse_tap_and_trim(struct sdhci_host *host)
69285c0da17SAapo Vienamo {
69385c0da17SAapo Vienamo 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
69485c0da17SAapo Vienamo 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
69585c0da17SAapo Vienamo 	int err;
69685c0da17SAapo Vienamo 
69785c0da17SAapo Vienamo 	err = device_property_read_u32(host->mmc->parent, "nvidia,default-tap",
69885c0da17SAapo Vienamo 				       &tegra_host->default_tap);
69985c0da17SAapo Vienamo 	if (err)
70085c0da17SAapo Vienamo 		tegra_host->default_tap = 0;
70185c0da17SAapo Vienamo 
70285c0da17SAapo Vienamo 	err = device_property_read_u32(host->mmc->parent, "nvidia,default-trim",
70385c0da17SAapo Vienamo 				       &tegra_host->default_trim);
70485c0da17SAapo Vienamo 	if (err)
70585c0da17SAapo Vienamo 		tegra_host->default_trim = 0;
706f5313aaaSAapo Vienamo 
707f5313aaaSAapo Vienamo 	err = device_property_read_u32(host->mmc->parent, "nvidia,dqs-trim",
708f5313aaaSAapo Vienamo 				       &tegra_host->dqs_trim);
709f5313aaaSAapo Vienamo 	if (err)
710f5313aaaSAapo Vienamo 		tegra_host->dqs_trim = 0x11;
71185c0da17SAapo Vienamo }
71285c0da17SAapo Vienamo 
7133c4019f9SSowjanya Komatineni static void tegra_sdhci_parse_dt(struct sdhci_host *host)
7143c4019f9SSowjanya Komatineni {
7153c4019f9SSowjanya Komatineni 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
7163c4019f9SSowjanya Komatineni 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
7173c4019f9SSowjanya Komatineni 
7183c4019f9SSowjanya Komatineni 	if (device_property_read_bool(host->mmc->parent, "supports-cqe"))
7193c4019f9SSowjanya Komatineni 		tegra_host->enable_hwcq = true;
7203c4019f9SSowjanya Komatineni 	else
7213c4019f9SSowjanya Komatineni 		tegra_host->enable_hwcq = false;
7223c4019f9SSowjanya Komatineni 
7233c4019f9SSowjanya Komatineni 	tegra_sdhci_parse_pad_autocal_dt(host);
7243c4019f9SSowjanya Komatineni 	tegra_sdhci_parse_tap_and_trim(host);
7253c4019f9SSowjanya Komatineni }
7263c4019f9SSowjanya Komatineni 
727a8e326a9SLucas Stach static void tegra_sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
728a8e326a9SLucas Stach {
729a8e326a9SLucas Stach 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
7300734e79cSJisheng Zhang 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
731a8e326a9SLucas Stach 	unsigned long host_clk;
732a8e326a9SLucas Stach 
733a8e326a9SLucas Stach 	if (!clock)
7343491b690SLucas Stach 		return sdhci_set_clock(host, clock);
735a8e326a9SLucas Stach 
73657d1654eSAapo Vienamo 	/*
73757d1654eSAapo Vienamo 	 * In DDR50/52 modes the Tegra SDHCI controllers require the SDHCI
73857d1654eSAapo Vienamo 	 * divider to be configured to divided the host clock by two. The SDHCI
73957d1654eSAapo Vienamo 	 * clock divider is calculated as part of sdhci_set_clock() by
74057d1654eSAapo Vienamo 	 * sdhci_calc_clk(). The divider is calculated from host->max_clk and
74157d1654eSAapo Vienamo 	 * the requested clock rate.
74257d1654eSAapo Vienamo 	 *
74357d1654eSAapo Vienamo 	 * By setting the host->max_clk to clock * 2 the divider calculation
74457d1654eSAapo Vienamo 	 * will always result in the correct value for DDR50/52 modes,
74557d1654eSAapo Vienamo 	 * regardless of clock rate rounding, which may happen if the value
74657d1654eSAapo Vienamo 	 * from clk_get_rate() is used.
74757d1654eSAapo Vienamo 	 */
748a8e326a9SLucas Stach 	host_clk = tegra_host->ddr_signaling ? clock * 2 : clock;
749a8e326a9SLucas Stach 	clk_set_rate(pltfm_host->clk, host_clk);
750ea8fc595SSowjanya Komatineni 	tegra_host->curr_clk_rate = host_clk;
75157d1654eSAapo Vienamo 	if (tegra_host->ddr_signaling)
75257d1654eSAapo Vienamo 		host->max_clk = host_clk;
75357d1654eSAapo Vienamo 	else
754a8e326a9SLucas Stach 		host->max_clk = clk_get_rate(pltfm_host->clk);
755a8e326a9SLucas Stach 
756e5c63d91SLucas Stach 	sdhci_set_clock(host, clock);
757e5c63d91SLucas Stach 
758e5c63d91SLucas Stach 	if (tegra_host->pad_calib_required) {
759e5c63d91SLucas Stach 		tegra_sdhci_pad_autocalib(host);
760e5c63d91SLucas Stach 		tegra_host->pad_calib_required = false;
761e5c63d91SLucas Stach 	}
762a8e326a9SLucas Stach }
763a8e326a9SLucas Stach 
76444350993SAapo Vienamo static unsigned int tegra_sdhci_get_max_clock(struct sdhci_host *host)
76544350993SAapo Vienamo {
76644350993SAapo Vienamo 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
76744350993SAapo Vienamo 
76844350993SAapo Vienamo 	return clk_round_rate(pltfm_host->clk, UINT_MAX);
76944350993SAapo Vienamo }
77044350993SAapo Vienamo 
771f5313aaaSAapo Vienamo static void tegra_sdhci_set_dqs_trim(struct sdhci_host *host, u8 trim)
772f5313aaaSAapo Vienamo {
773f5313aaaSAapo Vienamo 	u32 val;
774f5313aaaSAapo Vienamo 
775f5313aaaSAapo Vienamo 	val = sdhci_readl(host, SDHCI_TEGRA_VENDOR_CAP_OVERRIDES);
776f5313aaaSAapo Vienamo 	val &= ~SDHCI_TEGRA_CAP_OVERRIDES_DQS_TRIM_MASK;
777f5313aaaSAapo Vienamo 	val |= trim << SDHCI_TEGRA_CAP_OVERRIDES_DQS_TRIM_SHIFT;
778f5313aaaSAapo Vienamo 	sdhci_writel(host, val, SDHCI_TEGRA_VENDOR_CAP_OVERRIDES);
779f5313aaaSAapo Vienamo }
780f5313aaaSAapo Vienamo 
781bc5568bfSAapo Vienamo static void tegra_sdhci_hs400_dll_cal(struct sdhci_host *host)
782bc5568bfSAapo Vienamo {
783bc5568bfSAapo Vienamo 	u32 reg;
784bc5568bfSAapo Vienamo 	int err;
785bc5568bfSAapo Vienamo 
786bc5568bfSAapo Vienamo 	reg = sdhci_readl(host, SDHCI_TEGRA_VENDOR_DLLCAL_CFG);
787bc5568bfSAapo Vienamo 	reg |= SDHCI_TEGRA_DLLCAL_CALIBRATE;
788bc5568bfSAapo Vienamo 	sdhci_writel(host, reg, SDHCI_TEGRA_VENDOR_DLLCAL_CFG);
789bc5568bfSAapo Vienamo 
790bc5568bfSAapo Vienamo 	/* 1 ms sleep, 5 ms timeout */
791bc5568bfSAapo Vienamo 	err = readl_poll_timeout(host->ioaddr + SDHCI_TEGRA_VENDOR_DLLCAL_STA,
792bc5568bfSAapo Vienamo 				 reg, !(reg & SDHCI_TEGRA_DLLCAL_STA_ACTIVE),
793bc5568bfSAapo Vienamo 				 1000, 5000);
794bc5568bfSAapo Vienamo 	if (err)
795bc5568bfSAapo Vienamo 		dev_err(mmc_dev(host->mmc),
796bc5568bfSAapo Vienamo 			"HS400 delay line calibration timed out\n");
797bc5568bfSAapo Vienamo }
798bc5568bfSAapo Vienamo 
799ea8fc595SSowjanya Komatineni static void tegra_sdhci_tap_correction(struct sdhci_host *host, u8 thd_up,
800ea8fc595SSowjanya Komatineni 				       u8 thd_low, u8 fixed_tap)
801ea8fc595SSowjanya Komatineni {
802ea8fc595SSowjanya Komatineni 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
803ea8fc595SSowjanya Komatineni 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
804ea8fc595SSowjanya Komatineni 	u32 val, tun_status;
805ea8fc595SSowjanya Komatineni 	u8 word, bit, edge1, tap, window;
806ea8fc595SSowjanya Komatineni 	bool tap_result;
807ea8fc595SSowjanya Komatineni 	bool start_fail = false;
808ea8fc595SSowjanya Komatineni 	bool start_pass = false;
809ea8fc595SSowjanya Komatineni 	bool end_pass = false;
810ea8fc595SSowjanya Komatineni 	bool first_fail = false;
811ea8fc595SSowjanya Komatineni 	bool first_pass = false;
812ea8fc595SSowjanya Komatineni 	u8 start_pass_tap = 0;
813ea8fc595SSowjanya Komatineni 	u8 end_pass_tap = 0;
814ea8fc595SSowjanya Komatineni 	u8 first_fail_tap = 0;
815ea8fc595SSowjanya Komatineni 	u8 first_pass_tap = 0;
816ea8fc595SSowjanya Komatineni 	u8 total_tuning_words = host->tuning_loop_count / TUNING_WORD_BIT_SIZE;
817ea8fc595SSowjanya Komatineni 
818ea8fc595SSowjanya Komatineni 	/*
819ea8fc595SSowjanya Komatineni 	 * Read auto-tuned results and extract good valid passing window by
820ea8fc595SSowjanya Komatineni 	 * filtering out un-wanted bubble/partial/merged windows.
821ea8fc595SSowjanya Komatineni 	 */
822ea8fc595SSowjanya Komatineni 	for (word = 0; word < total_tuning_words; word++) {
823ea8fc595SSowjanya Komatineni 		val = sdhci_readl(host, SDHCI_VNDR_TUN_CTRL0_0);
824ea8fc595SSowjanya Komatineni 		val &= ~SDHCI_VNDR_TUN_CTRL0_TUN_WORD_SEL_MASK;
825ea8fc595SSowjanya Komatineni 		val |= word;
826ea8fc595SSowjanya Komatineni 		sdhci_writel(host, val, SDHCI_VNDR_TUN_CTRL0_0);
827ea8fc595SSowjanya Komatineni 		tun_status = sdhci_readl(host, SDHCI_TEGRA_VNDR_TUN_STATUS0);
828ea8fc595SSowjanya Komatineni 		bit = 0;
829ea8fc595SSowjanya Komatineni 		while (bit < TUNING_WORD_BIT_SIZE) {
830ea8fc595SSowjanya Komatineni 			tap = word * TUNING_WORD_BIT_SIZE + bit;
831ea8fc595SSowjanya Komatineni 			tap_result = tun_status & (1 << bit);
832ea8fc595SSowjanya Komatineni 			if (!tap_result && !start_fail) {
833ea8fc595SSowjanya Komatineni 				start_fail = true;
834ea8fc595SSowjanya Komatineni 				if (!first_fail) {
835ea8fc595SSowjanya Komatineni 					first_fail_tap = tap;
836ea8fc595SSowjanya Komatineni 					first_fail = true;
837ea8fc595SSowjanya Komatineni 				}
838ea8fc595SSowjanya Komatineni 
839ea8fc595SSowjanya Komatineni 			} else if (tap_result && start_fail && !start_pass) {
840ea8fc595SSowjanya Komatineni 				start_pass_tap = tap;
841ea8fc595SSowjanya Komatineni 				start_pass = true;
842ea8fc595SSowjanya Komatineni 				if (!first_pass) {
843ea8fc595SSowjanya Komatineni 					first_pass_tap = tap;
844ea8fc595SSowjanya Komatineni 					first_pass = true;
845ea8fc595SSowjanya Komatineni 				}
846ea8fc595SSowjanya Komatineni 
847ea8fc595SSowjanya Komatineni 			} else if (!tap_result && start_fail && start_pass &&
848ea8fc595SSowjanya Komatineni 				   !end_pass) {
849ea8fc595SSowjanya Komatineni 				end_pass_tap = tap - 1;
850ea8fc595SSowjanya Komatineni 				end_pass = true;
851ea8fc595SSowjanya Komatineni 			} else if (tap_result && start_pass && start_fail &&
852ea8fc595SSowjanya Komatineni 				   end_pass) {
853ea8fc595SSowjanya Komatineni 				window = end_pass_tap - start_pass_tap;
854ea8fc595SSowjanya Komatineni 				/* discard merged window and bubble window */
855ea8fc595SSowjanya Komatineni 				if (window >= thd_up || window < thd_low) {
856ea8fc595SSowjanya Komatineni 					start_pass_tap = tap;
857ea8fc595SSowjanya Komatineni 					end_pass = false;
858ea8fc595SSowjanya Komatineni 				} else {
859ea8fc595SSowjanya Komatineni 					/* set tap at middle of valid window */
860ea8fc595SSowjanya Komatineni 					tap = start_pass_tap + window / 2;
861ea8fc595SSowjanya Komatineni 					tegra_host->tuned_tap_delay = tap;
862ea8fc595SSowjanya Komatineni 					return;
863ea8fc595SSowjanya Komatineni 				}
864ea8fc595SSowjanya Komatineni 			}
865ea8fc595SSowjanya Komatineni 
866ea8fc595SSowjanya Komatineni 			bit++;
867ea8fc595SSowjanya Komatineni 		}
868ea8fc595SSowjanya Komatineni 	}
869ea8fc595SSowjanya Komatineni 
870ea8fc595SSowjanya Komatineni 	if (!first_fail) {
871d96dc68eSDan Carpenter 		WARN(1, "no edge detected, continue with hw tuned delay.\n");
872ea8fc595SSowjanya Komatineni 	} else if (first_pass) {
873ea8fc595SSowjanya Komatineni 		/* set tap location at fixed tap relative to the first edge */
874ea8fc595SSowjanya Komatineni 		edge1 = first_fail_tap + (first_pass_tap - first_fail_tap) / 2;
875ea8fc595SSowjanya Komatineni 		if (edge1 - 1 > fixed_tap)
876ea8fc595SSowjanya Komatineni 			tegra_host->tuned_tap_delay = edge1 - fixed_tap;
877ea8fc595SSowjanya Komatineni 		else
878ea8fc595SSowjanya Komatineni 			tegra_host->tuned_tap_delay = edge1 + fixed_tap;
879ea8fc595SSowjanya Komatineni 	}
880ea8fc595SSowjanya Komatineni }
881ea8fc595SSowjanya Komatineni 
882ea8fc595SSowjanya Komatineni static void tegra_sdhci_post_tuning(struct sdhci_host *host)
883ea8fc595SSowjanya Komatineni {
884ea8fc595SSowjanya Komatineni 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
885ea8fc595SSowjanya Komatineni 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
886ea8fc595SSowjanya Komatineni 	const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
887ea8fc595SSowjanya Komatineni 	u32 avg_tap_dly, val, min_tap_dly, max_tap_dly;
888ea8fc595SSowjanya Komatineni 	u8 fixed_tap, start_tap, end_tap, window_width;
889ea8fc595SSowjanya Komatineni 	u8 thdupper, thdlower;
890ea8fc595SSowjanya Komatineni 	u8 num_iter;
891ea8fc595SSowjanya Komatineni 	u32 clk_rate_mhz, period_ps, bestcase, worstcase;
892ea8fc595SSowjanya Komatineni 
893ea8fc595SSowjanya Komatineni 	/* retain HW tuned tap to use incase if no correction is needed */
894ea8fc595SSowjanya Komatineni 	val = sdhci_readl(host, SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
895ea8fc595SSowjanya Komatineni 	tegra_host->tuned_tap_delay = (val & SDHCI_CLOCK_CTRL_TAP_MASK) >>
896ea8fc595SSowjanya Komatineni 				      SDHCI_CLOCK_CTRL_TAP_SHIFT;
897ea8fc595SSowjanya Komatineni 	if (soc_data->min_tap_delay && soc_data->max_tap_delay) {
898ea8fc595SSowjanya Komatineni 		min_tap_dly = soc_data->min_tap_delay;
899ea8fc595SSowjanya Komatineni 		max_tap_dly = soc_data->max_tap_delay;
900ea8fc595SSowjanya Komatineni 		clk_rate_mhz = tegra_host->curr_clk_rate / USEC_PER_SEC;
901ea8fc595SSowjanya Komatineni 		period_ps = USEC_PER_SEC / clk_rate_mhz;
902ea8fc595SSowjanya Komatineni 		bestcase = period_ps / min_tap_dly;
903ea8fc595SSowjanya Komatineni 		worstcase = period_ps / max_tap_dly;
904ea8fc595SSowjanya Komatineni 		/*
905ea8fc595SSowjanya Komatineni 		 * Upper and Lower bound thresholds used to detect merged and
906ea8fc595SSowjanya Komatineni 		 * bubble windows
907ea8fc595SSowjanya Komatineni 		 */
908ea8fc595SSowjanya Komatineni 		thdupper = (2 * worstcase + bestcase) / 2;
909ea8fc595SSowjanya Komatineni 		thdlower = worstcase / 4;
910ea8fc595SSowjanya Komatineni 		/*
911ea8fc595SSowjanya Komatineni 		 * fixed tap is used when HW tuning result contains single edge
912ea8fc595SSowjanya Komatineni 		 * and tap is set at fixed tap delay relative to the first edge
913ea8fc595SSowjanya Komatineni 		 */
914ea8fc595SSowjanya Komatineni 		avg_tap_dly = (period_ps * 2) / (min_tap_dly + max_tap_dly);
915ea8fc595SSowjanya Komatineni 		fixed_tap = avg_tap_dly / 2;
916ea8fc595SSowjanya Komatineni 
917ea8fc595SSowjanya Komatineni 		val = sdhci_readl(host, SDHCI_TEGRA_VNDR_TUN_STATUS1);
918ea8fc595SSowjanya Komatineni 		start_tap = val & SDHCI_TEGRA_VNDR_TUN_STATUS1_TAP_MASK;
919ea8fc595SSowjanya Komatineni 		end_tap = (val >> SDHCI_TEGRA_VNDR_TUN_STATUS1_END_TAP_SHIFT) &
920ea8fc595SSowjanya Komatineni 			  SDHCI_TEGRA_VNDR_TUN_STATUS1_TAP_MASK;
921ea8fc595SSowjanya Komatineni 		window_width = end_tap - start_tap;
922ea8fc595SSowjanya Komatineni 		num_iter = host->tuning_loop_count;
923ea8fc595SSowjanya Komatineni 		/*
924ea8fc595SSowjanya Komatineni 		 * partial window includes edges of the tuning range.
925ea8fc595SSowjanya Komatineni 		 * merged window includes more taps so window width is higher
926ea8fc595SSowjanya Komatineni 		 * than upper threshold.
927ea8fc595SSowjanya Komatineni 		 */
928ea8fc595SSowjanya Komatineni 		if (start_tap == 0 || (end_tap == (num_iter - 1)) ||
929ea8fc595SSowjanya Komatineni 		    (end_tap == num_iter - 2) || window_width >= thdupper) {
930ea8fc595SSowjanya Komatineni 			pr_debug("%s: Apply tuning correction\n",
931ea8fc595SSowjanya Komatineni 				 mmc_hostname(host->mmc));
932ea8fc595SSowjanya Komatineni 			tegra_sdhci_tap_correction(host, thdupper, thdlower,
933ea8fc595SSowjanya Komatineni 						   fixed_tap);
934ea8fc595SSowjanya Komatineni 		}
935ea8fc595SSowjanya Komatineni 	}
936ea8fc595SSowjanya Komatineni 
937ea8fc595SSowjanya Komatineni 	tegra_sdhci_set_tap(host, tegra_host->tuned_tap_delay);
938ea8fc595SSowjanya Komatineni }
939ea8fc595SSowjanya Komatineni 
940ea8fc595SSowjanya Komatineni static int tegra_sdhci_execute_hw_tuning(struct mmc_host *mmc, u32 opcode)
941ea8fc595SSowjanya Komatineni {
942ea8fc595SSowjanya Komatineni 	struct sdhci_host *host = mmc_priv(mmc);
943ea8fc595SSowjanya Komatineni 	int err;
944ea8fc595SSowjanya Komatineni 
945ea8fc595SSowjanya Komatineni 	err = sdhci_execute_tuning(mmc, opcode);
946ea8fc595SSowjanya Komatineni 	if (!err && !host->tuning_err)
947ea8fc595SSowjanya Komatineni 		tegra_sdhci_post_tuning(host);
948ea8fc595SSowjanya Komatineni 
949ea8fc595SSowjanya Komatineni 	return err;
950ea8fc595SSowjanya Komatineni }
951ea8fc595SSowjanya Komatineni 
952c2c09678SAapo Vienamo static void tegra_sdhci_set_uhs_signaling(struct sdhci_host *host,
953c2c09678SAapo Vienamo 					  unsigned timing)
954c3c2384cSLucas Stach {
955d4501d8eSAapo Vienamo 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
956d4501d8eSAapo Vienamo 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
957c2c09678SAapo Vienamo 	bool set_default_tap = false;
958f5313aaaSAapo Vienamo 	bool set_dqs_trim = false;
959bc5568bfSAapo Vienamo 	bool do_hs400_dll_cal = false;
960ea8fc595SSowjanya Komatineni 	u8 iter = TRIES_256;
961ea8fc595SSowjanya Komatineni 	u32 val;
962c3c2384cSLucas Stach 
96392cd1667SSowjanya Komatineni 	tegra_host->ddr_signaling = false;
964c2c09678SAapo Vienamo 	switch (timing) {
965c2c09678SAapo Vienamo 	case MMC_TIMING_UHS_SDR50:
966ea8fc595SSowjanya Komatineni 		break;
967c2c09678SAapo Vienamo 	case MMC_TIMING_UHS_SDR104:
968c2c09678SAapo Vienamo 	case MMC_TIMING_MMC_HS200:
969c2c09678SAapo Vienamo 		/* Don't set default tap on tunable modes. */
970ea8fc595SSowjanya Komatineni 		iter = TRIES_128;
971c2c09678SAapo Vienamo 		break;
972f5313aaaSAapo Vienamo 	case MMC_TIMING_MMC_HS400:
973f5313aaaSAapo Vienamo 		set_dqs_trim = true;
974bc5568bfSAapo Vienamo 		do_hs400_dll_cal = true;
975ea8fc595SSowjanya Komatineni 		iter = TRIES_128;
976f5313aaaSAapo Vienamo 		break;
977c2c09678SAapo Vienamo 	case MMC_TIMING_MMC_DDR52:
978c2c09678SAapo Vienamo 	case MMC_TIMING_UHS_DDR50:
979c2c09678SAapo Vienamo 		tegra_host->ddr_signaling = true;
980c2c09678SAapo Vienamo 		set_default_tap = true;
981c2c09678SAapo Vienamo 		break;
982c2c09678SAapo Vienamo 	default:
983c2c09678SAapo Vienamo 		set_default_tap = true;
984c2c09678SAapo Vienamo 		break;
985d4501d8eSAapo Vienamo 	}
986c2c09678SAapo Vienamo 
987ea8fc595SSowjanya Komatineni 	val = sdhci_readl(host, SDHCI_VNDR_TUN_CTRL0_0);
988ea8fc595SSowjanya Komatineni 	val &= ~(SDHCI_VNDR_TUN_CTRL0_TUN_ITER_MASK |
989ea8fc595SSowjanya Komatineni 		 SDHCI_VNDR_TUN_CTRL0_START_TAP_VAL_MASK |
990ea8fc595SSowjanya Komatineni 		 SDHCI_VNDR_TUN_CTRL0_MUL_M_MASK);
991ea8fc595SSowjanya Komatineni 	val |= (iter << SDHCI_VNDR_TUN_CTRL0_TUN_ITER_SHIFT |
992ea8fc595SSowjanya Komatineni 		0 << SDHCI_VNDR_TUN_CTRL0_START_TAP_VAL_SHIFT |
993ea8fc595SSowjanya Komatineni 		1 << SDHCI_VNDR_TUN_CTRL0_MUL_M_SHIFT);
994ea8fc595SSowjanya Komatineni 	sdhci_writel(host, val, SDHCI_VNDR_TUN_CTRL0_0);
995ea8fc595SSowjanya Komatineni 	sdhci_writel(host, 0, SDHCI_TEGRA_VNDR_TUN_CTRL1_0);
996ea8fc595SSowjanya Komatineni 
997ea8fc595SSowjanya Komatineni 	host->tuning_loop_count = (iter == TRIES_128) ? 128 : 256;
998ea8fc595SSowjanya Komatineni 
999c2c09678SAapo Vienamo 	sdhci_set_uhs_signaling(host, timing);
1000c2c09678SAapo Vienamo 
1001c2c09678SAapo Vienamo 	tegra_sdhci_pad_autocalib(host);
1002c2c09678SAapo Vienamo 
1003ea8fc595SSowjanya Komatineni 	if (tegra_host->tuned_tap_delay && !set_default_tap)
1004ea8fc595SSowjanya Komatineni 		tegra_sdhci_set_tap(host, tegra_host->tuned_tap_delay);
1005ea8fc595SSowjanya Komatineni 	else
1006c2c09678SAapo Vienamo 		tegra_sdhci_set_tap(host, tegra_host->default_tap);
1007f5313aaaSAapo Vienamo 
1008f5313aaaSAapo Vienamo 	if (set_dqs_trim)
1009f5313aaaSAapo Vienamo 		tegra_sdhci_set_dqs_trim(host, tegra_host->dqs_trim);
1010bc5568bfSAapo Vienamo 
1011bc5568bfSAapo Vienamo 	if (do_hs400_dll_cal)
1012bc5568bfSAapo Vienamo 		tegra_sdhci_hs400_dll_cal(host);
1013c3c2384cSLucas Stach }
1014c3c2384cSLucas Stach 
1015c3c2384cSLucas Stach static int tegra_sdhci_execute_tuning(struct sdhci_host *host, u32 opcode)
1016c3c2384cSLucas Stach {
1017c3c2384cSLucas Stach 	unsigned int min, max;
1018c3c2384cSLucas Stach 
1019c3c2384cSLucas Stach 	/*
1020c3c2384cSLucas Stach 	 * Start search for minimum tap value at 10, as smaller values are
1021c3c2384cSLucas Stach 	 * may wrongly be reported as working but fail at higher speeds,
1022c3c2384cSLucas Stach 	 * according to the TRM.
1023c3c2384cSLucas Stach 	 */
1024c3c2384cSLucas Stach 	min = 10;
1025c3c2384cSLucas Stach 	while (min < 255) {
1026c3c2384cSLucas Stach 		tegra_sdhci_set_tap(host, min);
1027c3c2384cSLucas Stach 		if (!mmc_send_tuning(host->mmc, opcode, NULL))
1028c3c2384cSLucas Stach 			break;
1029c3c2384cSLucas Stach 		min++;
1030c3c2384cSLucas Stach 	}
1031c3c2384cSLucas Stach 
1032c3c2384cSLucas Stach 	/* Find the maximum tap value that still passes. */
1033c3c2384cSLucas Stach 	max = min + 1;
1034c3c2384cSLucas Stach 	while (max < 255) {
1035c3c2384cSLucas Stach 		tegra_sdhci_set_tap(host, max);
1036c3c2384cSLucas Stach 		if (mmc_send_tuning(host->mmc, opcode, NULL)) {
1037c3c2384cSLucas Stach 			max--;
1038c3c2384cSLucas Stach 			break;
1039c3c2384cSLucas Stach 		}
1040c3c2384cSLucas Stach 		max++;
1041c3c2384cSLucas Stach 	}
1042c3c2384cSLucas Stach 
1043c3c2384cSLucas Stach 	/* The TRM states the ideal tap value is at 75% in the passing range. */
1044c3c2384cSLucas Stach 	tegra_sdhci_set_tap(host, min + ((max - min) * 3 / 4));
1045c3c2384cSLucas Stach 
1046c3c2384cSLucas Stach 	return mmc_send_tuning(host->mmc, opcode, NULL);
1047c3c2384cSLucas Stach }
1048c3c2384cSLucas Stach 
104986ac2f8bSAapo Vienamo static int sdhci_tegra_start_signal_voltage_switch(struct mmc_host *mmc,
105086ac2f8bSAapo Vienamo 						   struct mmc_ios *ios)
105186ac2f8bSAapo Vienamo {
105286ac2f8bSAapo Vienamo 	struct sdhci_host *host = mmc_priv(mmc);
105344babea2SAapo Vienamo 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
105444babea2SAapo Vienamo 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
105586ac2f8bSAapo Vienamo 	int ret = 0;
105686ac2f8bSAapo Vienamo 
105786ac2f8bSAapo Vienamo 	if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330) {
1058de25fa5aSSowjanya Komatineni 		ret = tegra_sdhci_set_padctrl(host, ios->signal_voltage, true);
105986ac2f8bSAapo Vienamo 		if (ret < 0)
106086ac2f8bSAapo Vienamo 			return ret;
106186ac2f8bSAapo Vienamo 		ret = sdhci_start_signal_voltage_switch(mmc, ios);
106286ac2f8bSAapo Vienamo 	} else if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180) {
106386ac2f8bSAapo Vienamo 		ret = sdhci_start_signal_voltage_switch(mmc, ios);
106486ac2f8bSAapo Vienamo 		if (ret < 0)
106586ac2f8bSAapo Vienamo 			return ret;
1066de25fa5aSSowjanya Komatineni 		ret = tegra_sdhci_set_padctrl(host, ios->signal_voltage, true);
106786ac2f8bSAapo Vienamo 	}
106886ac2f8bSAapo Vienamo 
106944babea2SAapo Vienamo 	if (tegra_host->pad_calib_required)
107044babea2SAapo Vienamo 		tegra_sdhci_pad_autocalib(host);
107144babea2SAapo Vienamo 
107286ac2f8bSAapo Vienamo 	return ret;
107386ac2f8bSAapo Vienamo }
107486ac2f8bSAapo Vienamo 
107586ac2f8bSAapo Vienamo static int tegra_sdhci_init_pinctrl_info(struct device *dev,
107686ac2f8bSAapo Vienamo 					 struct sdhci_tegra *tegra_host)
107786ac2f8bSAapo Vienamo {
107886ac2f8bSAapo Vienamo 	tegra_host->pinctrl_sdmmc = devm_pinctrl_get(dev);
107986ac2f8bSAapo Vienamo 	if (IS_ERR(tegra_host->pinctrl_sdmmc)) {
108086ac2f8bSAapo Vienamo 		dev_dbg(dev, "No pinctrl info, err: %ld\n",
108186ac2f8bSAapo Vienamo 			PTR_ERR(tegra_host->pinctrl_sdmmc));
108286ac2f8bSAapo Vienamo 		return -1;
108386ac2f8bSAapo Vienamo 	}
108486ac2f8bSAapo Vienamo 
1085de25fa5aSSowjanya Komatineni 	tegra_host->pinctrl_state_1v8_drv = pinctrl_lookup_state(
1086de25fa5aSSowjanya Komatineni 				tegra_host->pinctrl_sdmmc, "sdmmc-1v8-drv");
1087de25fa5aSSowjanya Komatineni 	if (IS_ERR(tegra_host->pinctrl_state_1v8_drv)) {
1088de25fa5aSSowjanya Komatineni 		if (PTR_ERR(tegra_host->pinctrl_state_1v8_drv) == -ENODEV)
1089de25fa5aSSowjanya Komatineni 			tegra_host->pinctrl_state_1v8_drv = NULL;
1090de25fa5aSSowjanya Komatineni 	}
1091de25fa5aSSowjanya Komatineni 
1092de25fa5aSSowjanya Komatineni 	tegra_host->pinctrl_state_3v3_drv = pinctrl_lookup_state(
1093de25fa5aSSowjanya Komatineni 				tegra_host->pinctrl_sdmmc, "sdmmc-3v3-drv");
1094de25fa5aSSowjanya Komatineni 	if (IS_ERR(tegra_host->pinctrl_state_3v3_drv)) {
1095de25fa5aSSowjanya Komatineni 		if (PTR_ERR(tegra_host->pinctrl_state_3v3_drv) == -ENODEV)
1096de25fa5aSSowjanya Komatineni 			tegra_host->pinctrl_state_3v3_drv = NULL;
1097de25fa5aSSowjanya Komatineni 	}
1098de25fa5aSSowjanya Komatineni 
109986ac2f8bSAapo Vienamo 	tegra_host->pinctrl_state_3v3 =
110086ac2f8bSAapo Vienamo 		pinctrl_lookup_state(tegra_host->pinctrl_sdmmc, "sdmmc-3v3");
110186ac2f8bSAapo Vienamo 	if (IS_ERR(tegra_host->pinctrl_state_3v3)) {
110286ac2f8bSAapo Vienamo 		dev_warn(dev, "Missing 3.3V pad state, err: %ld\n",
110386ac2f8bSAapo Vienamo 			 PTR_ERR(tegra_host->pinctrl_state_3v3));
110486ac2f8bSAapo Vienamo 		return -1;
110586ac2f8bSAapo Vienamo 	}
110686ac2f8bSAapo Vienamo 
110786ac2f8bSAapo Vienamo 	tegra_host->pinctrl_state_1v8 =
110886ac2f8bSAapo Vienamo 		pinctrl_lookup_state(tegra_host->pinctrl_sdmmc, "sdmmc-1v8");
110986ac2f8bSAapo Vienamo 	if (IS_ERR(tegra_host->pinctrl_state_1v8)) {
111086ac2f8bSAapo Vienamo 		dev_warn(dev, "Missing 1.8V pad state, err: %ld\n",
1111e5378247SYueHaibing 			 PTR_ERR(tegra_host->pinctrl_state_1v8));
111286ac2f8bSAapo Vienamo 		return -1;
111386ac2f8bSAapo Vienamo 	}
111486ac2f8bSAapo Vienamo 
111586ac2f8bSAapo Vienamo 	tegra_host->pad_control_available = true;
111686ac2f8bSAapo Vienamo 
111786ac2f8bSAapo Vienamo 	return 0;
111886ac2f8bSAapo Vienamo }
111986ac2f8bSAapo Vienamo 
1120e5c63d91SLucas Stach static void tegra_sdhci_voltage_switch(struct sdhci_host *host)
1121e5c63d91SLucas Stach {
1122e5c63d91SLucas Stach 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1123e5c63d91SLucas Stach 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
1124e5c63d91SLucas Stach 	const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
1125e5c63d91SLucas Stach 
1126e5c63d91SLucas Stach 	if (soc_data->nvquirks & NVQUIRK_HAS_PADCALIB)
1127e5c63d91SLucas Stach 		tegra_host->pad_calib_required = true;
1128e5c63d91SLucas Stach }
1129e5c63d91SLucas Stach 
1130b7754428SSowjanya Komatineni static void tegra_cqhci_writel(struct cqhci_host *cq_host, u32 val, int reg)
1131b7754428SSowjanya Komatineni {
1132b7754428SSowjanya Komatineni 	struct mmc_host *mmc = cq_host->mmc;
1133b7754428SSowjanya Komatineni 	u8 ctrl;
1134b7754428SSowjanya Komatineni 	ktime_t timeout;
1135b7754428SSowjanya Komatineni 	bool timed_out;
1136b7754428SSowjanya Komatineni 
1137b7754428SSowjanya Komatineni 	/*
1138b7754428SSowjanya Komatineni 	 * During CQE resume/unhalt, CQHCI driver unhalts CQE prior to
1139b7754428SSowjanya Komatineni 	 * cqhci_host_ops enable where SDHCI DMA and BLOCK_SIZE registers need
1140b7754428SSowjanya Komatineni 	 * to be re-configured.
1141b7754428SSowjanya Komatineni 	 * Tegra CQHCI/SDHCI prevents write access to block size register when
1142b7754428SSowjanya Komatineni 	 * CQE is unhalted. So handling CQE resume sequence here to configure
1143b7754428SSowjanya Komatineni 	 * SDHCI block registers prior to exiting CQE halt state.
1144b7754428SSowjanya Komatineni 	 */
1145b7754428SSowjanya Komatineni 	if (reg == CQHCI_CTL && !(val & CQHCI_HALT) &&
1146b7754428SSowjanya Komatineni 	    cqhci_readl(cq_host, CQHCI_CTL) & CQHCI_HALT) {
1147b7754428SSowjanya Komatineni 		sdhci_cqe_enable(mmc);
1148b7754428SSowjanya Komatineni 		writel(val, cq_host->mmio + reg);
1149b7754428SSowjanya Komatineni 		timeout = ktime_add_us(ktime_get(), 50);
1150b7754428SSowjanya Komatineni 		while (1) {
1151b7754428SSowjanya Komatineni 			timed_out = ktime_compare(ktime_get(), timeout) > 0;
1152b7754428SSowjanya Komatineni 			ctrl = cqhci_readl(cq_host, CQHCI_CTL);
1153b7754428SSowjanya Komatineni 			if (!(ctrl & CQHCI_HALT) || timed_out)
1154b7754428SSowjanya Komatineni 				break;
1155b7754428SSowjanya Komatineni 		}
1156b7754428SSowjanya Komatineni 		/*
1157b7754428SSowjanya Komatineni 		 * CQE usually resumes very quick, but incase if Tegra CQE
1158b7754428SSowjanya Komatineni 		 * doesn't resume retry unhalt.
1159b7754428SSowjanya Komatineni 		 */
1160b7754428SSowjanya Komatineni 		if (timed_out)
1161b7754428SSowjanya Komatineni 			writel(val, cq_host->mmio + reg);
1162b7754428SSowjanya Komatineni 	} else {
1163b7754428SSowjanya Komatineni 		writel(val, cq_host->mmio + reg);
1164b7754428SSowjanya Komatineni 	}
1165b7754428SSowjanya Komatineni }
1166b7754428SSowjanya Komatineni 
1167c6e7ab90SSowjanya Komatineni static void sdhci_tegra_update_dcmd_desc(struct mmc_host *mmc,
1168c6e7ab90SSowjanya Komatineni 					 struct mmc_request *mrq, u64 *data)
1169c6e7ab90SSowjanya Komatineni {
1170c6e7ab90SSowjanya Komatineni 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(mmc_priv(mmc));
1171c6e7ab90SSowjanya Komatineni 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
1172c6e7ab90SSowjanya Komatineni 	const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
1173c6e7ab90SSowjanya Komatineni 
1174c6e7ab90SSowjanya Komatineni 	if (soc_data->nvquirks & NVQUIRK_CQHCI_DCMD_R1B_CMD_TIMING &&
1175c6e7ab90SSowjanya Komatineni 	    mrq->cmd->flags & MMC_RSP_R1B)
1176c6e7ab90SSowjanya Komatineni 		*data |= CQHCI_CMD_TIMING(1);
1177c6e7ab90SSowjanya Komatineni }
1178c6e7ab90SSowjanya Komatineni 
11793c4019f9SSowjanya Komatineni static void sdhci_tegra_cqe_enable(struct mmc_host *mmc)
11803c4019f9SSowjanya Komatineni {
11813c4019f9SSowjanya Komatineni 	struct cqhci_host *cq_host = mmc->cqe_private;
1182b7754428SSowjanya Komatineni 	u32 val;
11833c4019f9SSowjanya Komatineni 
11843c4019f9SSowjanya Komatineni 	/*
1185b7754428SSowjanya Komatineni 	 * Tegra CQHCI/SDMMC design prevents write access to sdhci block size
1186b7754428SSowjanya Komatineni 	 * register when CQE is enabled and unhalted.
1187b7754428SSowjanya Komatineni 	 * CQHCI driver enables CQE prior to activation, so disable CQE before
1188b7754428SSowjanya Komatineni 	 * programming block size in sdhci controller and enable it back.
11893c4019f9SSowjanya Komatineni 	 */
1190b7754428SSowjanya Komatineni 	if (!cq_host->activated) {
1191b7754428SSowjanya Komatineni 		val = cqhci_readl(cq_host, CQHCI_CFG);
1192b7754428SSowjanya Komatineni 		if (val & CQHCI_ENABLE)
1193b7754428SSowjanya Komatineni 			cqhci_writel(cq_host, (val & ~CQHCI_ENABLE),
1194b7754428SSowjanya Komatineni 				     CQHCI_CFG);
11953c4019f9SSowjanya Komatineni 		sdhci_cqe_enable(mmc);
1196b7754428SSowjanya Komatineni 		if (val & CQHCI_ENABLE)
1197b7754428SSowjanya Komatineni 			cqhci_writel(cq_host, val, CQHCI_CFG);
1198b7754428SSowjanya Komatineni 	}
11993c4019f9SSowjanya Komatineni 
1200b7754428SSowjanya Komatineni 	/*
1201b7754428SSowjanya Komatineni 	 * CMD CRC errors are seen sometimes with some eMMC devices when status
1202b7754428SSowjanya Komatineni 	 * command is sent during transfer of last data block which is the
1203b7754428SSowjanya Komatineni 	 * default case as send status command block counter (CBC) is 1.
1204b7754428SSowjanya Komatineni 	 * Recommended fix to set CBC to 0 allowing send status command only
1205b7754428SSowjanya Komatineni 	 * when data lines are idle.
1206b7754428SSowjanya Komatineni 	 */
1207b7754428SSowjanya Komatineni 	val = cqhci_readl(cq_host, CQHCI_SSC1);
1208b7754428SSowjanya Komatineni 	val &= ~CQHCI_SSC1_CBC_MASK;
1209b7754428SSowjanya Komatineni 	cqhci_writel(cq_host, val, CQHCI_SSC1);
12103c4019f9SSowjanya Komatineni }
12113c4019f9SSowjanya Komatineni 
12123c4019f9SSowjanya Komatineni static void sdhci_tegra_dumpregs(struct mmc_host *mmc)
12133c4019f9SSowjanya Komatineni {
12143c4019f9SSowjanya Komatineni 	sdhci_dumpregs(mmc_priv(mmc));
12153c4019f9SSowjanya Komatineni }
12163c4019f9SSowjanya Komatineni 
12173c4019f9SSowjanya Komatineni static u32 sdhci_tegra_cqhci_irq(struct sdhci_host *host, u32 intmask)
12183c4019f9SSowjanya Komatineni {
12193c4019f9SSowjanya Komatineni 	int cmd_error = 0;
12203c4019f9SSowjanya Komatineni 	int data_error = 0;
12213c4019f9SSowjanya Komatineni 
12223c4019f9SSowjanya Komatineni 	if (!sdhci_cqe_irq(host, intmask, &cmd_error, &data_error))
12233c4019f9SSowjanya Komatineni 		return intmask;
12243c4019f9SSowjanya Komatineni 
12253c4019f9SSowjanya Komatineni 	cqhci_irq(host->mmc, intmask, cmd_error, data_error);
12263c4019f9SSowjanya Komatineni 
12273c4019f9SSowjanya Komatineni 	return 0;
12283c4019f9SSowjanya Komatineni }
12293c4019f9SSowjanya Komatineni 
12303c4019f9SSowjanya Komatineni static const struct cqhci_host_ops sdhci_tegra_cqhci_ops = {
1231b7754428SSowjanya Komatineni 	.write_l    = tegra_cqhci_writel,
12323c4019f9SSowjanya Komatineni 	.enable	= sdhci_tegra_cqe_enable,
12333c4019f9SSowjanya Komatineni 	.disable = sdhci_cqe_disable,
12343c4019f9SSowjanya Komatineni 	.dumpregs = sdhci_tegra_dumpregs,
1235c6e7ab90SSowjanya Komatineni 	.update_dcmd_desc = sdhci_tegra_update_dcmd_desc,
12363c4019f9SSowjanya Komatineni };
12373c4019f9SSowjanya Komatineni 
1238b960bc44SNicolin Chen static int tegra_sdhci_set_dma_mask(struct sdhci_host *host)
1239b960bc44SNicolin Chen {
1240b960bc44SNicolin Chen 	struct sdhci_pltfm_host *platform = sdhci_priv(host);
1241b960bc44SNicolin Chen 	struct sdhci_tegra *tegra = sdhci_pltfm_priv(platform);
1242b960bc44SNicolin Chen 	const struct sdhci_tegra_soc_data *soc = tegra->soc_data;
1243b960bc44SNicolin Chen 	struct device *dev = mmc_dev(host->mmc);
1244b960bc44SNicolin Chen 
1245b960bc44SNicolin Chen 	if (soc->dma_mask)
1246b960bc44SNicolin Chen 		return dma_set_mask_and_coherent(dev, soc->dma_mask);
1247b960bc44SNicolin Chen 
1248b960bc44SNicolin Chen 	return 0;
1249b960bc44SNicolin Chen }
1250b960bc44SNicolin Chen 
1251c915568dSLars-Peter Clausen static const struct sdhci_ops tegra_sdhci_ops = {
12520f686ca9SDmitry Osipenko 	.get_ro     = tegra_sdhci_get_ro,
125385d6509dSShawn Guo 	.read_w     = tegra_sdhci_readw,
125485d6509dSShawn Guo 	.write_l    = tegra_sdhci_writel,
1255a8e326a9SLucas Stach 	.set_clock  = tegra_sdhci_set_clock,
1256b960bc44SNicolin Chen 	.set_dma_mask = tegra_sdhci_set_dma_mask,
125714b04c6aSMichał Mirosław 	.set_bus_width = sdhci_set_bus_width,
125803231f9bSRussell King 	.reset      = tegra_sdhci_reset,
1259c3c2384cSLucas Stach 	.platform_execute_tuning = tegra_sdhci_execute_tuning,
1260a8e326a9SLucas Stach 	.set_uhs_signaling = tegra_sdhci_set_uhs_signaling,
1261e5c63d91SLucas Stach 	.voltage_switch = tegra_sdhci_voltage_switch,
126244350993SAapo Vienamo 	.get_max_clock = tegra_sdhci_get_max_clock,
126385d6509dSShawn Guo };
126403d2bfc8SOlof Johansson 
12651db5eebfSLars-Peter Clausen static const struct sdhci_pltfm_data sdhci_tegra20_pdata = {
126685d6509dSShawn Guo 	.quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
126785d6509dSShawn Guo 		  SDHCI_QUIRK_SINGLE_POWER_WRITE |
126885d6509dSShawn Guo 		  SDHCI_QUIRK_NO_HISPD_BIT |
1269f9260355SAndrew Bresticker 		  SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
1270f9260355SAndrew Bresticker 		  SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
127185d6509dSShawn Guo 	.ops  = &tegra_sdhci_ops,
127285d6509dSShawn Guo };
127385d6509dSShawn Guo 
1274d49d19c2SThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra20 = {
12753e44a1a7SStephen Warren 	.pdata = &sdhci_tegra20_pdata,
1276b960bc44SNicolin Chen 	.dma_mask = DMA_BIT_MASK(32),
12773e44a1a7SStephen Warren 	.nvquirks = NVQUIRK_FORCE_SDHCI_SPEC_200 |
12783e44a1a7SStephen Warren 		    NVQUIRK_ENABLE_BLOCK_GAP_DET,
12793e44a1a7SStephen Warren };
12803e44a1a7SStephen Warren 
12811db5eebfSLars-Peter Clausen static const struct sdhci_pltfm_data sdhci_tegra30_pdata = {
12823e44a1a7SStephen Warren 	.quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
12833e44a1a7SStephen Warren 		  SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
12843e44a1a7SStephen Warren 		  SDHCI_QUIRK_SINGLE_POWER_WRITE |
12853e44a1a7SStephen Warren 		  SDHCI_QUIRK_NO_HISPD_BIT |
1286f9260355SAndrew Bresticker 		  SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
1287f9260355SAndrew Bresticker 		  SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
1288127407e3SStefan Agner 	.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
1289726df1d5SStefan Agner 		   SDHCI_QUIRK2_BROKEN_HS200 |
1290726df1d5SStefan Agner 		   /*
1291726df1d5SStefan Agner 		    * Auto-CMD23 leads to "Got command interrupt 0x00010000 even
1292726df1d5SStefan Agner 		    * though no command operation was in progress."
1293726df1d5SStefan Agner 		    *
1294726df1d5SStefan Agner 		    * The exact reason is unknown, as the same hardware seems
1295726df1d5SStefan Agner 		    * to support Auto CMD23 on a downstream 3.1 kernel.
1296726df1d5SStefan Agner 		    */
1297726df1d5SStefan Agner 		   SDHCI_QUIRK2_ACMD23_BROKEN,
12983e44a1a7SStephen Warren 	.ops  = &tegra_sdhci_ops,
12993e44a1a7SStephen Warren };
13003e44a1a7SStephen Warren 
1301d49d19c2SThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra30 = {
13023e44a1a7SStephen Warren 	.pdata = &sdhci_tegra30_pdata,
1303b960bc44SNicolin Chen 	.dma_mask = DMA_BIT_MASK(32),
13043145351aSAndrew Bresticker 	.nvquirks = NVQUIRK_ENABLE_SDHCI_SPEC_300 |
13057ad2ed1dSLucas Stach 		    NVQUIRK_ENABLE_SDR50 |
1306e5c63d91SLucas Stach 		    NVQUIRK_ENABLE_SDR104 |
1307e5c63d91SLucas Stach 		    NVQUIRK_HAS_PADCALIB,
13083e44a1a7SStephen Warren };
13093e44a1a7SStephen Warren 
131001df7ecdSRhyland Klein static const struct sdhci_ops tegra114_sdhci_ops = {
13110f686ca9SDmitry Osipenko 	.get_ro     = tegra_sdhci_get_ro,
131201df7ecdSRhyland Klein 	.read_w     = tegra_sdhci_readw,
131301df7ecdSRhyland Klein 	.write_w    = tegra_sdhci_writew,
131401df7ecdSRhyland Klein 	.write_l    = tegra_sdhci_writel,
1315a8e326a9SLucas Stach 	.set_clock  = tegra_sdhci_set_clock,
1316b960bc44SNicolin Chen 	.set_dma_mask = tegra_sdhci_set_dma_mask,
131714b04c6aSMichał Mirosław 	.set_bus_width = sdhci_set_bus_width,
131801df7ecdSRhyland Klein 	.reset      = tegra_sdhci_reset,
1319c3c2384cSLucas Stach 	.platform_execute_tuning = tegra_sdhci_execute_tuning,
1320a8e326a9SLucas Stach 	.set_uhs_signaling = tegra_sdhci_set_uhs_signaling,
1321e5c63d91SLucas Stach 	.voltage_switch = tegra_sdhci_voltage_switch,
132244350993SAapo Vienamo 	.get_max_clock = tegra_sdhci_get_max_clock,
132301df7ecdSRhyland Klein };
132401df7ecdSRhyland Klein 
13251db5eebfSLars-Peter Clausen static const struct sdhci_pltfm_data sdhci_tegra114_pdata = {
13265ebf2552SRhyland Klein 	.quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
13275ebf2552SRhyland Klein 		  SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
13285ebf2552SRhyland Klein 		  SDHCI_QUIRK_SINGLE_POWER_WRITE |
13295ebf2552SRhyland Klein 		  SDHCI_QUIRK_NO_HISPD_BIT |
1330f9260355SAndrew Bresticker 		  SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
1331f9260355SAndrew Bresticker 		  SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
1332a8e326a9SLucas Stach 	.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
133301df7ecdSRhyland Klein 	.ops  = &tegra114_sdhci_ops,
13345ebf2552SRhyland Klein };
13355ebf2552SRhyland Klein 
1336d49d19c2SThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra114 = {
13375ebf2552SRhyland Klein 	.pdata = &sdhci_tegra114_pdata,
1338b960bc44SNicolin Chen 	.dma_mask = DMA_BIT_MASK(32),
13397bf037d6SJon Hunter };
13407bf037d6SJon Hunter 
13414ae12588SThierry Reding static const struct sdhci_pltfm_data sdhci_tegra124_pdata = {
13424ae12588SThierry Reding 	.quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
13434ae12588SThierry Reding 		  SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
13444ae12588SThierry Reding 		  SDHCI_QUIRK_SINGLE_POWER_WRITE |
13454ae12588SThierry Reding 		  SDHCI_QUIRK_NO_HISPD_BIT |
13464ae12588SThierry Reding 		  SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
13474ae12588SThierry Reding 		  SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
1348b960bc44SNicolin Chen 	.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
13494ae12588SThierry Reding 	.ops  = &tegra114_sdhci_ops,
13504ae12588SThierry Reding };
13514ae12588SThierry Reding 
13524ae12588SThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra124 = {
13534ae12588SThierry Reding 	.pdata = &sdhci_tegra124_pdata,
1354b960bc44SNicolin Chen 	.dma_mask = DMA_BIT_MASK(34),
13554ae12588SThierry Reding };
13564ae12588SThierry Reding 
13571070e83aSAapo Vienamo static const struct sdhci_ops tegra210_sdhci_ops = {
13580f686ca9SDmitry Osipenko 	.get_ro     = tegra_sdhci_get_ro,
13591070e83aSAapo Vienamo 	.read_w     = tegra_sdhci_readw,
136038a284d9SAapo Vienamo 	.write_w    = tegra210_sdhci_writew,
13611070e83aSAapo Vienamo 	.write_l    = tegra_sdhci_writel,
13621070e83aSAapo Vienamo 	.set_clock  = tegra_sdhci_set_clock,
1363b960bc44SNicolin Chen 	.set_dma_mask = tegra_sdhci_set_dma_mask,
13641070e83aSAapo Vienamo 	.set_bus_width = sdhci_set_bus_width,
13651070e83aSAapo Vienamo 	.reset      = tegra_sdhci_reset,
13661070e83aSAapo Vienamo 	.set_uhs_signaling = tegra_sdhci_set_uhs_signaling,
13671070e83aSAapo Vienamo 	.voltage_switch = tegra_sdhci_voltage_switch,
13681070e83aSAapo Vienamo 	.get_max_clock = tegra_sdhci_get_max_clock,
13691070e83aSAapo Vienamo };
13701070e83aSAapo Vienamo 
1371b5a84ecfSThierry Reding static const struct sdhci_pltfm_data sdhci_tegra210_pdata = {
1372b5a84ecfSThierry Reding 	.quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
1373b5a84ecfSThierry Reding 		  SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
1374b5a84ecfSThierry Reding 		  SDHCI_QUIRK_SINGLE_POWER_WRITE |
1375b5a84ecfSThierry Reding 		  SDHCI_QUIRK_NO_HISPD_BIT |
1376a8e326a9SLucas Stach 		  SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
1377a8e326a9SLucas Stach 		  SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
1378a8e326a9SLucas Stach 	.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
13791070e83aSAapo Vienamo 	.ops  = &tegra210_sdhci_ops,
1380b5a84ecfSThierry Reding };
1381b5a84ecfSThierry Reding 
1382b5a84ecfSThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra210 = {
1383b5a84ecfSThierry Reding 	.pdata = &sdhci_tegra210_pdata,
1384b960bc44SNicolin Chen 	.dma_mask = DMA_BIT_MASK(34),
1385d943f6e9SAapo Vienamo 	.nvquirks = NVQUIRK_NEEDS_PAD_CONTROL |
1386d4501d8eSAapo Vienamo 		    NVQUIRK_HAS_PADCALIB |
13873559d4a6SAapo Vienamo 		    NVQUIRK_DIS_CARD_CLK_CONFIG_TAP |
13883559d4a6SAapo Vienamo 		    NVQUIRK_ENABLE_SDR50 |
13893559d4a6SAapo Vienamo 		    NVQUIRK_ENABLE_SDR104,
1390ea8fc595SSowjanya Komatineni 	.min_tap_delay = 106,
1391ea8fc595SSowjanya Komatineni 	.max_tap_delay = 185,
1392b5a84ecfSThierry Reding };
1393b5a84ecfSThierry Reding 
139438a284d9SAapo Vienamo static const struct sdhci_ops tegra186_sdhci_ops = {
13950f686ca9SDmitry Osipenko 	.get_ro     = tegra_sdhci_get_ro,
139638a284d9SAapo Vienamo 	.read_w     = tegra_sdhci_readw,
139738a284d9SAapo Vienamo 	.write_l    = tegra_sdhci_writel,
139838a284d9SAapo Vienamo 	.set_clock  = tegra_sdhci_set_clock,
1399b960bc44SNicolin Chen 	.set_dma_mask = tegra_sdhci_set_dma_mask,
140038a284d9SAapo Vienamo 	.set_bus_width = sdhci_set_bus_width,
140138a284d9SAapo Vienamo 	.reset      = tegra_sdhci_reset,
140238a284d9SAapo Vienamo 	.set_uhs_signaling = tegra_sdhci_set_uhs_signaling,
140338a284d9SAapo Vienamo 	.voltage_switch = tegra_sdhci_voltage_switch,
140438a284d9SAapo Vienamo 	.get_max_clock = tegra_sdhci_get_max_clock,
14053c4019f9SSowjanya Komatineni 	.irq = sdhci_tegra_cqhci_irq,
140638a284d9SAapo Vienamo };
140738a284d9SAapo Vienamo 
14084346b7c7SThierry Reding static const struct sdhci_pltfm_data sdhci_tegra186_pdata = {
14094346b7c7SThierry Reding 	.quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
14104346b7c7SThierry Reding 		  SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
14114346b7c7SThierry Reding 		  SDHCI_QUIRK_SINGLE_POWER_WRITE |
14124346b7c7SThierry Reding 		  SDHCI_QUIRK_NO_HISPD_BIT |
14134346b7c7SThierry Reding 		  SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
14144346b7c7SThierry Reding 		  SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
1415b960bc44SNicolin Chen 	.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
141638a284d9SAapo Vienamo 	.ops  = &tegra186_sdhci_ops,
14174346b7c7SThierry Reding };
14184346b7c7SThierry Reding 
14194346b7c7SThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra186 = {
14204346b7c7SThierry Reding 	.pdata = &sdhci_tegra186_pdata,
1421b960bc44SNicolin Chen 	.dma_mask = DMA_BIT_MASK(40),
1422d943f6e9SAapo Vienamo 	.nvquirks = NVQUIRK_NEEDS_PAD_CONTROL |
1423d4501d8eSAapo Vienamo 		    NVQUIRK_HAS_PADCALIB |
14242ad50051SAapo Vienamo 		    NVQUIRK_DIS_CARD_CLK_CONFIG_TAP |
14252ad50051SAapo Vienamo 		    NVQUIRK_ENABLE_SDR50 |
1426c6e7ab90SSowjanya Komatineni 		    NVQUIRK_ENABLE_SDR104 |
1427c6e7ab90SSowjanya Komatineni 		    NVQUIRK_CQHCI_DCMD_R1B_CMD_TIMING,
1428ea8fc595SSowjanya Komatineni 	.min_tap_delay = 84,
1429ea8fc595SSowjanya Komatineni 	.max_tap_delay = 136,
1430ea8fc595SSowjanya Komatineni };
1431ea8fc595SSowjanya Komatineni 
1432ea8fc595SSowjanya Komatineni static const struct sdhci_tegra_soc_data soc_data_tegra194 = {
1433ea8fc595SSowjanya Komatineni 	.pdata = &sdhci_tegra186_pdata,
1434b960bc44SNicolin Chen 	.dma_mask = DMA_BIT_MASK(39),
1435ea8fc595SSowjanya Komatineni 	.nvquirks = NVQUIRK_NEEDS_PAD_CONTROL |
1436ea8fc595SSowjanya Komatineni 		    NVQUIRK_HAS_PADCALIB |
1437ea8fc595SSowjanya Komatineni 		    NVQUIRK_DIS_CARD_CLK_CONFIG_TAP |
1438ea8fc595SSowjanya Komatineni 		    NVQUIRK_ENABLE_SDR50 |
1439ea8fc595SSowjanya Komatineni 		    NVQUIRK_ENABLE_SDR104,
1440ea8fc595SSowjanya Komatineni 	.min_tap_delay = 96,
1441ea8fc595SSowjanya Komatineni 	.max_tap_delay = 139,
14424346b7c7SThierry Reding };
14434346b7c7SThierry Reding 
1444498d83e7SBill Pemberton static const struct of_device_id sdhci_tegra_dt_match[] = {
1445ea8fc595SSowjanya Komatineni 	{ .compatible = "nvidia,tegra194-sdhci", .data = &soc_data_tegra194 },
14464346b7c7SThierry Reding 	{ .compatible = "nvidia,tegra186-sdhci", .data = &soc_data_tegra186 },
1447b5a84ecfSThierry Reding 	{ .compatible = "nvidia,tegra210-sdhci", .data = &soc_data_tegra210 },
14484ae12588SThierry Reding 	{ .compatible = "nvidia,tegra124-sdhci", .data = &soc_data_tegra124 },
14495ebf2552SRhyland Klein 	{ .compatible = "nvidia,tegra114-sdhci", .data = &soc_data_tegra114 },
14503e44a1a7SStephen Warren 	{ .compatible = "nvidia,tegra30-sdhci", .data = &soc_data_tegra30 },
14513e44a1a7SStephen Warren 	{ .compatible = "nvidia,tegra20-sdhci", .data = &soc_data_tegra20 },
1452275173b2SGrant Likely 	{}
1453275173b2SGrant Likely };
1454e4404fabSArnd Bergmann MODULE_DEVICE_TABLE(of, sdhci_tegra_dt_match);
1455275173b2SGrant Likely 
14563c4019f9SSowjanya Komatineni static int sdhci_tegra_add_host(struct sdhci_host *host)
14573c4019f9SSowjanya Komatineni {
14583c4019f9SSowjanya Komatineni 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
14593c4019f9SSowjanya Komatineni 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
14603c4019f9SSowjanya Komatineni 	struct cqhci_host *cq_host;
14613c4019f9SSowjanya Komatineni 	bool dma64;
14623c4019f9SSowjanya Komatineni 	int ret;
14633c4019f9SSowjanya Komatineni 
14643c4019f9SSowjanya Komatineni 	if (!tegra_host->enable_hwcq)
14653c4019f9SSowjanya Komatineni 		return sdhci_add_host(host);
14663c4019f9SSowjanya Komatineni 
14673c4019f9SSowjanya Komatineni 	sdhci_enable_v4_mode(host);
14683c4019f9SSowjanya Komatineni 
14693c4019f9SSowjanya Komatineni 	ret = sdhci_setup_host(host);
14703c4019f9SSowjanya Komatineni 	if (ret)
14713c4019f9SSowjanya Komatineni 		return ret;
14723c4019f9SSowjanya Komatineni 
14733c4019f9SSowjanya Komatineni 	host->mmc->caps2 |= MMC_CAP2_CQE | MMC_CAP2_CQE_DCMD;
14743c4019f9SSowjanya Komatineni 
14753c4019f9SSowjanya Komatineni 	cq_host = devm_kzalloc(host->mmc->parent,
14763c4019f9SSowjanya Komatineni 				sizeof(*cq_host), GFP_KERNEL);
14773c4019f9SSowjanya Komatineni 	if (!cq_host) {
14783c4019f9SSowjanya Komatineni 		ret = -ENOMEM;
14793c4019f9SSowjanya Komatineni 		goto cleanup;
14803c4019f9SSowjanya Komatineni 	}
14813c4019f9SSowjanya Komatineni 
14823c4019f9SSowjanya Komatineni 	cq_host->mmio = host->ioaddr + SDHCI_TEGRA_CQE_BASE_ADDR;
14833c4019f9SSowjanya Komatineni 	cq_host->ops = &sdhci_tegra_cqhci_ops;
14843c4019f9SSowjanya Komatineni 
14853c4019f9SSowjanya Komatineni 	dma64 = host->flags & SDHCI_USE_64_BIT_DMA;
14863c4019f9SSowjanya Komatineni 	if (dma64)
14873c4019f9SSowjanya Komatineni 		cq_host->caps |= CQHCI_TASK_DESC_SZ_128;
14883c4019f9SSowjanya Komatineni 
14893c4019f9SSowjanya Komatineni 	ret = cqhci_init(cq_host, host->mmc, dma64);
14903c4019f9SSowjanya Komatineni 	if (ret)
14913c4019f9SSowjanya Komatineni 		goto cleanup;
14923c4019f9SSowjanya Komatineni 
14933c4019f9SSowjanya Komatineni 	ret = __sdhci_add_host(host);
14943c4019f9SSowjanya Komatineni 	if (ret)
14953c4019f9SSowjanya Komatineni 		goto cleanup;
14963c4019f9SSowjanya Komatineni 
14973c4019f9SSowjanya Komatineni 	return 0;
14983c4019f9SSowjanya Komatineni 
14993c4019f9SSowjanya Komatineni cleanup:
15003c4019f9SSowjanya Komatineni 	sdhci_cleanup_host(host);
15013c4019f9SSowjanya Komatineni 	return ret;
15023c4019f9SSowjanya Komatineni }
15033c4019f9SSowjanya Komatineni 
1504c3be1efdSBill Pemberton static int sdhci_tegra_probe(struct platform_device *pdev)
150503d2bfc8SOlof Johansson {
15063e44a1a7SStephen Warren 	const struct of_device_id *match;
15073e44a1a7SStephen Warren 	const struct sdhci_tegra_soc_data *soc_data;
15083e44a1a7SStephen Warren 	struct sdhci_host *host;
150985d6509dSShawn Guo 	struct sdhci_pltfm_host *pltfm_host;
15103e44a1a7SStephen Warren 	struct sdhci_tegra *tegra_host;
151103d2bfc8SOlof Johansson 	struct clk *clk;
151203d2bfc8SOlof Johansson 	int rc;
151303d2bfc8SOlof Johansson 
15143e44a1a7SStephen Warren 	match = of_match_device(sdhci_tegra_dt_match, &pdev->dev);
1515b37f9d98SJoseph Lo 	if (!match)
1516b37f9d98SJoseph Lo 		return -EINVAL;
15173e44a1a7SStephen Warren 	soc_data = match->data;
15183e44a1a7SStephen Warren 
15190734e79cSJisheng Zhang 	host = sdhci_pltfm_init(pdev, soc_data->pdata, sizeof(*tegra_host));
152085d6509dSShawn Guo 	if (IS_ERR(host))
152185d6509dSShawn Guo 		return PTR_ERR(host);
152285d6509dSShawn Guo 	pltfm_host = sdhci_priv(host);
152385d6509dSShawn Guo 
15240734e79cSJisheng Zhang 	tegra_host = sdhci_pltfm_priv(pltfm_host);
1525a8e326a9SLucas Stach 	tegra_host->ddr_signaling = false;
1526e5c63d91SLucas Stach 	tegra_host->pad_calib_required = false;
152786ac2f8bSAapo Vienamo 	tegra_host->pad_control_available = false;
15283e44a1a7SStephen Warren 	tegra_host->soc_data = soc_data;
1529275173b2SGrant Likely 
153086ac2f8bSAapo Vienamo 	if (soc_data->nvquirks & NVQUIRK_NEEDS_PAD_CONTROL) {
153186ac2f8bSAapo Vienamo 		rc = tegra_sdhci_init_pinctrl_info(&pdev->dev, tegra_host);
153286ac2f8bSAapo Vienamo 		if (rc == 0)
153386ac2f8bSAapo Vienamo 			host->mmc_host_ops.start_signal_voltage_switch =
153486ac2f8bSAapo Vienamo 				sdhci_tegra_start_signal_voltage_switch;
153586ac2f8bSAapo Vienamo 	}
153686ac2f8bSAapo Vienamo 
153761dad40eSAapo Vienamo 	/* Hook to periodically rerun pad calibration */
153861dad40eSAapo Vienamo 	if (soc_data->nvquirks & NVQUIRK_HAS_PADCALIB)
153961dad40eSAapo Vienamo 		host->mmc_host_ops.request = tegra_sdhci_request;
154061dad40eSAapo Vienamo 
1541dfc9700cSAapo Vienamo 	host->mmc_host_ops.hs400_enhanced_strobe =
1542dfc9700cSAapo Vienamo 			tegra_sdhci_hs400_enhanced_strobe;
1543dfc9700cSAapo Vienamo 
1544ea8fc595SSowjanya Komatineni 	if (!host->ops->platform_execute_tuning)
1545ea8fc595SSowjanya Komatineni 		host->mmc_host_ops.execute_tuning =
1546ea8fc595SSowjanya Komatineni 				tegra_sdhci_execute_hw_tuning;
1547ea8fc595SSowjanya Komatineni 
15482391b340SMylene JOSSERAND 	rc = mmc_of_parse(host->mmc);
154947caa84fSSimon Baatz 	if (rc)
155047caa84fSSimon Baatz 		goto err_parse_dt;
15510e786102SStephen Warren 
15527ad2ed1dSLucas Stach 	if (tegra_host->soc_data->nvquirks & NVQUIRK_ENABLE_DDR50)
1553c3c2384cSLucas Stach 		host->mmc->caps |= MMC_CAP_1_8V_DDR;
1554c3c2384cSLucas Stach 
15553c4019f9SSowjanya Komatineni 	tegra_sdhci_parse_dt(host);
155685c0da17SAapo Vienamo 
15572391b340SMylene JOSSERAND 	tegra_host->power_gpio = devm_gpiod_get_optional(&pdev->dev, "power",
15582391b340SMylene JOSSERAND 							 GPIOD_OUT_HIGH);
15592391b340SMylene JOSSERAND 	if (IS_ERR(tegra_host->power_gpio)) {
15602391b340SMylene JOSSERAND 		rc = PTR_ERR(tegra_host->power_gpio);
156185d6509dSShawn Guo 		goto err_power_req;
156203d2bfc8SOlof Johansson 	}
156303d2bfc8SOlof Johansson 
1564e4f79d9cSKevin Hao 	clk = devm_clk_get(mmc_dev(host->mmc), NULL);
156503d2bfc8SOlof Johansson 	if (IS_ERR(clk)) {
156603d2bfc8SOlof Johansson 		rc = PTR_ERR(clk);
156700a9584eSThierry Reding 
156800a9584eSThierry Reding 		if (rc != -EPROBE_DEFER)
156900a9584eSThierry Reding 			dev_err(&pdev->dev, "failed to get clock: %d\n", rc);
157000a9584eSThierry Reding 
157185d6509dSShawn Guo 		goto err_clk_get;
157203d2bfc8SOlof Johansson 	}
15731e674bc6SPrashant Gaikwad 	clk_prepare_enable(clk);
157403d2bfc8SOlof Johansson 	pltfm_host->clk = clk;
157503d2bfc8SOlof Johansson 
15762cd6c49dSPhilipp Zabel 	tegra_host->rst = devm_reset_control_get_exclusive(&pdev->dev,
15772cd6c49dSPhilipp Zabel 							   "sdhci");
157820567be9SThierry Reding 	if (IS_ERR(tegra_host->rst)) {
157920567be9SThierry Reding 		rc = PTR_ERR(tegra_host->rst);
158020567be9SThierry Reding 		dev_err(&pdev->dev, "failed to get reset control: %d\n", rc);
158120567be9SThierry Reding 		goto err_rst_get;
158220567be9SThierry Reding 	}
158320567be9SThierry Reding 
158420567be9SThierry Reding 	rc = reset_control_assert(tegra_host->rst);
158520567be9SThierry Reding 	if (rc)
158620567be9SThierry Reding 		goto err_rst_get;
158720567be9SThierry Reding 
158820567be9SThierry Reding 	usleep_range(2000, 4000);
158920567be9SThierry Reding 
159020567be9SThierry Reding 	rc = reset_control_deassert(tegra_host->rst);
159120567be9SThierry Reding 	if (rc)
159220567be9SThierry Reding 		goto err_rst_get;
159320567be9SThierry Reding 
159420567be9SThierry Reding 	usleep_range(2000, 4000);
159520567be9SThierry Reding 
15963c4019f9SSowjanya Komatineni 	rc = sdhci_tegra_add_host(host);
159785d6509dSShawn Guo 	if (rc)
159885d6509dSShawn Guo 		goto err_add_host;
159985d6509dSShawn Guo 
160003d2bfc8SOlof Johansson 	return 0;
160103d2bfc8SOlof Johansson 
160285d6509dSShawn Guo err_add_host:
160320567be9SThierry Reding 	reset_control_assert(tegra_host->rst);
160420567be9SThierry Reding err_rst_get:
16051e674bc6SPrashant Gaikwad 	clk_disable_unprepare(pltfm_host->clk);
160685d6509dSShawn Guo err_clk_get:
160785d6509dSShawn Guo err_power_req:
160847caa84fSSimon Baatz err_parse_dt:
160985d6509dSShawn Guo 	sdhci_pltfm_free(pdev);
161003d2bfc8SOlof Johansson 	return rc;
161103d2bfc8SOlof Johansson }
161203d2bfc8SOlof Johansson 
161320567be9SThierry Reding static int sdhci_tegra_remove(struct platform_device *pdev)
161420567be9SThierry Reding {
161520567be9SThierry Reding 	struct sdhci_host *host = platform_get_drvdata(pdev);
161620567be9SThierry Reding 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
161720567be9SThierry Reding 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
161820567be9SThierry Reding 
161920567be9SThierry Reding 	sdhci_remove_host(host, 0);
162020567be9SThierry Reding 
162120567be9SThierry Reding 	reset_control_assert(tegra_host->rst);
162220567be9SThierry Reding 	usleep_range(2000, 4000);
162320567be9SThierry Reding 	clk_disable_unprepare(pltfm_host->clk);
162420567be9SThierry Reding 
162520567be9SThierry Reding 	sdhci_pltfm_free(pdev);
162620567be9SThierry Reding 
162720567be9SThierry Reding 	return 0;
162820567be9SThierry Reding }
162920567be9SThierry Reding 
163071c733c4SSowjanya Komatineni #ifdef CONFIG_PM_SLEEP
163171c733c4SSowjanya Komatineni static int __maybe_unused sdhci_tegra_suspend(struct device *dev)
163271c733c4SSowjanya Komatineni {
163371c733c4SSowjanya Komatineni 	struct sdhci_host *host = dev_get_drvdata(dev);
163471c733c4SSowjanya Komatineni 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
163571c733c4SSowjanya Komatineni 	int ret;
163671c733c4SSowjanya Komatineni 
163771c733c4SSowjanya Komatineni 	if (host->mmc->caps2 & MMC_CAP2_CQE) {
163871c733c4SSowjanya Komatineni 		ret = cqhci_suspend(host->mmc);
163971c733c4SSowjanya Komatineni 		if (ret)
164071c733c4SSowjanya Komatineni 			return ret;
164171c733c4SSowjanya Komatineni 	}
164271c733c4SSowjanya Komatineni 
164371c733c4SSowjanya Komatineni 	ret = sdhci_suspend_host(host);
164471c733c4SSowjanya Komatineni 	if (ret) {
164571c733c4SSowjanya Komatineni 		cqhci_resume(host->mmc);
164671c733c4SSowjanya Komatineni 		return ret;
164771c733c4SSowjanya Komatineni 	}
164871c733c4SSowjanya Komatineni 
164971c733c4SSowjanya Komatineni 	clk_disable_unprepare(pltfm_host->clk);
165071c733c4SSowjanya Komatineni 	return 0;
165171c733c4SSowjanya Komatineni }
165271c733c4SSowjanya Komatineni 
165371c733c4SSowjanya Komatineni static int __maybe_unused sdhci_tegra_resume(struct device *dev)
165471c733c4SSowjanya Komatineni {
165571c733c4SSowjanya Komatineni 	struct sdhci_host *host = dev_get_drvdata(dev);
165671c733c4SSowjanya Komatineni 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
165771c733c4SSowjanya Komatineni 	int ret;
165871c733c4SSowjanya Komatineni 
165971c733c4SSowjanya Komatineni 	ret = clk_prepare_enable(pltfm_host->clk);
166071c733c4SSowjanya Komatineni 	if (ret)
166171c733c4SSowjanya Komatineni 		return ret;
166271c733c4SSowjanya Komatineni 
166371c733c4SSowjanya Komatineni 	ret = sdhci_resume_host(host);
166471c733c4SSowjanya Komatineni 	if (ret)
166571c733c4SSowjanya Komatineni 		goto disable_clk;
166671c733c4SSowjanya Komatineni 
166771c733c4SSowjanya Komatineni 	if (host->mmc->caps2 & MMC_CAP2_CQE) {
166871c733c4SSowjanya Komatineni 		ret = cqhci_resume(host->mmc);
166971c733c4SSowjanya Komatineni 		if (ret)
167071c733c4SSowjanya Komatineni 			goto suspend_host;
167171c733c4SSowjanya Komatineni 	}
167271c733c4SSowjanya Komatineni 
167371c733c4SSowjanya Komatineni 	return 0;
167471c733c4SSowjanya Komatineni 
167571c733c4SSowjanya Komatineni suspend_host:
167671c733c4SSowjanya Komatineni 	sdhci_suspend_host(host);
167771c733c4SSowjanya Komatineni disable_clk:
167871c733c4SSowjanya Komatineni 	clk_disable_unprepare(pltfm_host->clk);
167971c733c4SSowjanya Komatineni 	return ret;
168071c733c4SSowjanya Komatineni }
168171c733c4SSowjanya Komatineni #endif
168271c733c4SSowjanya Komatineni 
168371c733c4SSowjanya Komatineni static SIMPLE_DEV_PM_OPS(sdhci_tegra_dev_pm_ops, sdhci_tegra_suspend,
168471c733c4SSowjanya Komatineni 			 sdhci_tegra_resume);
168571c733c4SSowjanya Komatineni 
168685d6509dSShawn Guo static struct platform_driver sdhci_tegra_driver = {
168785d6509dSShawn Guo 	.driver		= {
168885d6509dSShawn Guo 		.name	= "sdhci-tegra",
1689275173b2SGrant Likely 		.of_match_table = sdhci_tegra_dt_match,
169071c733c4SSowjanya Komatineni 		.pm	= &sdhci_tegra_dev_pm_ops,
169185d6509dSShawn Guo 	},
169285d6509dSShawn Guo 	.probe		= sdhci_tegra_probe,
169320567be9SThierry Reding 	.remove		= sdhci_tegra_remove,
169403d2bfc8SOlof Johansson };
169503d2bfc8SOlof Johansson 
1696d1f81a64SAxel Lin module_platform_driver(sdhci_tegra_driver);
169785d6509dSShawn Guo 
169885d6509dSShawn Guo MODULE_DESCRIPTION("SDHCI driver for Tegra");
169985d6509dSShawn Guo MODULE_AUTHOR("Google, Inc.");
170085d6509dSShawn Guo MODULE_LICENSE("GPL v2");
1701