xref: /openbmc/linux/drivers/mmc/host/sdhci-tegra.c (revision d4501d8e)
103d2bfc8SOlof Johansson /*
203d2bfc8SOlof Johansson  * Copyright (C) 2010 Google, Inc.
303d2bfc8SOlof Johansson  *
403d2bfc8SOlof Johansson  * This software is licensed under the terms of the GNU General Public
503d2bfc8SOlof Johansson  * License version 2, as published by the Free Software Foundation, and
603d2bfc8SOlof Johansson  * may be copied, distributed, and modified under those terms.
703d2bfc8SOlof Johansson  *
803d2bfc8SOlof Johansson  * This program is distributed in the hope that it will be useful,
903d2bfc8SOlof Johansson  * but WITHOUT ANY WARRANTY; without even the implied warranty of
1003d2bfc8SOlof Johansson  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1103d2bfc8SOlof Johansson  * GNU General Public License for more details.
1203d2bfc8SOlof Johansson  *
1303d2bfc8SOlof Johansson  */
1403d2bfc8SOlof Johansson 
15e5c63d91SLucas Stach #include <linux/delay.h>
1603d2bfc8SOlof Johansson #include <linux/err.h>
1796547f5dSPaul Gortmaker #include <linux/module.h>
1803d2bfc8SOlof Johansson #include <linux/init.h>
19e7c07148SAapo Vienamo #include <linux/iopoll.h>
2003d2bfc8SOlof Johansson #include <linux/platform_device.h>
2103d2bfc8SOlof Johansson #include <linux/clk.h>
2203d2bfc8SOlof Johansson #include <linux/io.h>
2355cd65e4SStephen Warren #include <linux/of.h>
243e44a1a7SStephen Warren #include <linux/of_device.h>
2586ac2f8bSAapo Vienamo #include <linux/pinctrl/consumer.h>
2686ac2f8bSAapo Vienamo #include <linux/regulator/consumer.h>
2720567be9SThierry Reding #include <linux/reset.h>
2803d2bfc8SOlof Johansson #include <linux/mmc/card.h>
2903d2bfc8SOlof Johansson #include <linux/mmc/host.h>
30c3c2384cSLucas Stach #include <linux/mmc/mmc.h>
310aacd23fSJoseph Lo #include <linux/mmc/slot-gpio.h>
322391b340SMylene JOSSERAND #include <linux/gpio/consumer.h>
3303d2bfc8SOlof Johansson 
3403d2bfc8SOlof Johansson #include "sdhci-pltfm.h"
3503d2bfc8SOlof Johansson 
36ca5879d3SPavan Kunapuli /* Tegra SDHOST controller vendor register definitions */
3774cd42bcSLucas Stach #define SDHCI_TEGRA_VENDOR_CLOCK_CTRL			0x100
38c3c2384cSLucas Stach #define SDHCI_CLOCK_CTRL_TAP_MASK			0x00ff0000
39c3c2384cSLucas Stach #define SDHCI_CLOCK_CTRL_TAP_SHIFT			16
40c3c2384cSLucas Stach #define SDHCI_CLOCK_CTRL_SDR50_TUNING_OVERRIDE		BIT(5)
4174cd42bcSLucas Stach #define SDHCI_CLOCK_CTRL_PADPIPE_CLKEN_OVERRIDE		BIT(3)
4274cd42bcSLucas Stach #define SDHCI_CLOCK_CTRL_SPI_MODE_CLKEN_OVERRIDE	BIT(2)
4374cd42bcSLucas Stach 
44ca5879d3SPavan Kunapuli #define SDHCI_TEGRA_VENDOR_MISC_CTRL			0x120
453145351aSAndrew Bresticker #define SDHCI_MISC_CTRL_ENABLE_SDR104			0x8
463145351aSAndrew Bresticker #define SDHCI_MISC_CTRL_ENABLE_SDR50			0x10
47ca5879d3SPavan Kunapuli #define SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300		0x20
483145351aSAndrew Bresticker #define SDHCI_MISC_CTRL_ENABLE_DDR50			0x200
49ca5879d3SPavan Kunapuli 
50d4501d8eSAapo Vienamo #define SDHCI_VNDR_TUN_CTRL0_0				0x1c0
51d4501d8eSAapo Vienamo #define SDHCI_VNDR_TUN_CTRL0_TUN_HW_TAP			0x20000
52d4501d8eSAapo Vienamo 
53e5c63d91SLucas Stach #define SDHCI_TEGRA_AUTO_CAL_CONFIG			0x1e4
54e5c63d91SLucas Stach #define SDHCI_AUTO_CAL_START				BIT(31)
55e5c63d91SLucas Stach #define SDHCI_AUTO_CAL_ENABLE				BIT(29)
5651b77c8eSAapo Vienamo #define SDHCI_AUTO_CAL_PDPU_OFFSET_MASK			0x0000ffff
57e5c63d91SLucas Stach 
589d548f11SAapo Vienamo #define SDHCI_TEGRA_SDMEM_COMP_PADCTRL			0x1e0
599d548f11SAapo Vienamo #define SDHCI_TEGRA_SDMEM_COMP_PADCTRL_VREF_SEL_MASK	0x0000000f
609d548f11SAapo Vienamo #define SDHCI_TEGRA_SDMEM_COMP_PADCTRL_VREF_SEL_VAL	0x7
61212b0cf1SAapo Vienamo #define SDHCI_TEGRA_SDMEM_COMP_PADCTRL_E_INPUT_E_PWRD	BIT(31)
629d548f11SAapo Vienamo 
63e7c07148SAapo Vienamo #define SDHCI_TEGRA_AUTO_CAL_STATUS			0x1ec
64e7c07148SAapo Vienamo #define SDHCI_TEGRA_AUTO_CAL_ACTIVE			BIT(31)
65e7c07148SAapo Vienamo 
663e44a1a7SStephen Warren #define NVQUIRK_FORCE_SDHCI_SPEC_200			BIT(0)
673e44a1a7SStephen Warren #define NVQUIRK_ENABLE_BLOCK_GAP_DET			BIT(1)
68ca5879d3SPavan Kunapuli #define NVQUIRK_ENABLE_SDHCI_SPEC_300			BIT(2)
697ad2ed1dSLucas Stach #define NVQUIRK_ENABLE_SDR50				BIT(3)
707ad2ed1dSLucas Stach #define NVQUIRK_ENABLE_SDR104				BIT(4)
717ad2ed1dSLucas Stach #define NVQUIRK_ENABLE_DDR50				BIT(5)
72e5c63d91SLucas Stach #define NVQUIRK_HAS_PADCALIB				BIT(6)
7386ac2f8bSAapo Vienamo #define NVQUIRK_NEEDS_PAD_CONTROL			BIT(7)
74d4501d8eSAapo Vienamo #define NVQUIRK_DIS_CARD_CLK_CONFIG_TAP			BIT(8)
753e44a1a7SStephen Warren 
763e44a1a7SStephen Warren struct sdhci_tegra_soc_data {
771db5eebfSLars-Peter Clausen 	const struct sdhci_pltfm_data *pdata;
783e44a1a7SStephen Warren 	u32 nvquirks;
793e44a1a7SStephen Warren };
803e44a1a7SStephen Warren 
8151b77c8eSAapo Vienamo /* Magic pull up and pull down pad calibration offsets */
8251b77c8eSAapo Vienamo struct sdhci_tegra_autocal_offsets {
8351b77c8eSAapo Vienamo 	u32 pull_up_3v3;
8451b77c8eSAapo Vienamo 	u32 pull_down_3v3;
8551b77c8eSAapo Vienamo 	u32 pull_up_3v3_timeout;
8651b77c8eSAapo Vienamo 	u32 pull_down_3v3_timeout;
8751b77c8eSAapo Vienamo 	u32 pull_up_1v8;
8851b77c8eSAapo Vienamo 	u32 pull_down_1v8;
8951b77c8eSAapo Vienamo 	u32 pull_up_1v8_timeout;
9051b77c8eSAapo Vienamo 	u32 pull_down_1v8_timeout;
9151b77c8eSAapo Vienamo 	u32 pull_up_sdr104;
9251b77c8eSAapo Vienamo 	u32 pull_down_sdr104;
9351b77c8eSAapo Vienamo 	u32 pull_up_hs400;
9451b77c8eSAapo Vienamo 	u32 pull_down_hs400;
9551b77c8eSAapo Vienamo };
9651b77c8eSAapo Vienamo 
973e44a1a7SStephen Warren struct sdhci_tegra {
983e44a1a7SStephen Warren 	const struct sdhci_tegra_soc_data *soc_data;
992391b340SMylene JOSSERAND 	struct gpio_desc *power_gpio;
100a8e326a9SLucas Stach 	bool ddr_signaling;
101e5c63d91SLucas Stach 	bool pad_calib_required;
10286ac2f8bSAapo Vienamo 	bool pad_control_available;
10320567be9SThierry Reding 
10420567be9SThierry Reding 	struct reset_control *rst;
10586ac2f8bSAapo Vienamo 	struct pinctrl *pinctrl_sdmmc;
10686ac2f8bSAapo Vienamo 	struct pinctrl_state *pinctrl_state_3v3;
10786ac2f8bSAapo Vienamo 	struct pinctrl_state *pinctrl_state_1v8;
10851b77c8eSAapo Vienamo 
10951b77c8eSAapo Vienamo 	struct sdhci_tegra_autocal_offsets autocal_offsets;
1103e44a1a7SStephen Warren };
1113e44a1a7SStephen Warren 
11203d2bfc8SOlof Johansson static u16 tegra_sdhci_readw(struct sdhci_host *host, int reg)
11303d2bfc8SOlof Johansson {
1143e44a1a7SStephen Warren 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1150734e79cSJisheng Zhang 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
1163e44a1a7SStephen Warren 	const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
1173e44a1a7SStephen Warren 
1183e44a1a7SStephen Warren 	if (unlikely((soc_data->nvquirks & NVQUIRK_FORCE_SDHCI_SPEC_200) &&
1193e44a1a7SStephen Warren 			(reg == SDHCI_HOST_VERSION))) {
12003d2bfc8SOlof Johansson 		/* Erratum: Version register is invalid in HW. */
12103d2bfc8SOlof Johansson 		return SDHCI_SPEC_200;
12203d2bfc8SOlof Johansson 	}
12303d2bfc8SOlof Johansson 
12403d2bfc8SOlof Johansson 	return readw(host->ioaddr + reg);
12503d2bfc8SOlof Johansson }
12603d2bfc8SOlof Johansson 
127352ee868SPavan Kunapuli static void tegra_sdhci_writew(struct sdhci_host *host, u16 val, int reg)
128352ee868SPavan Kunapuli {
129352ee868SPavan Kunapuli 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
130352ee868SPavan Kunapuli 
131352ee868SPavan Kunapuli 	switch (reg) {
132352ee868SPavan Kunapuli 	case SDHCI_TRANSFER_MODE:
133352ee868SPavan Kunapuli 		/*
134352ee868SPavan Kunapuli 		 * Postpone this write, we must do it together with a
135352ee868SPavan Kunapuli 		 * command write that is down below.
136352ee868SPavan Kunapuli 		 */
137352ee868SPavan Kunapuli 		pltfm_host->xfer_mode_shadow = val;
138352ee868SPavan Kunapuli 		return;
139352ee868SPavan Kunapuli 	case SDHCI_COMMAND:
140352ee868SPavan Kunapuli 		writel((val << 16) | pltfm_host->xfer_mode_shadow,
141352ee868SPavan Kunapuli 			host->ioaddr + SDHCI_TRANSFER_MODE);
142352ee868SPavan Kunapuli 		return;
143352ee868SPavan Kunapuli 	}
144352ee868SPavan Kunapuli 
145352ee868SPavan Kunapuli 	writew(val, host->ioaddr + reg);
146352ee868SPavan Kunapuli }
147352ee868SPavan Kunapuli 
14803d2bfc8SOlof Johansson static void tegra_sdhci_writel(struct sdhci_host *host, u32 val, int reg)
14903d2bfc8SOlof Johansson {
1503e44a1a7SStephen Warren 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1510734e79cSJisheng Zhang 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
1523e44a1a7SStephen Warren 	const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
1533e44a1a7SStephen Warren 
15403d2bfc8SOlof Johansson 	/* Seems like we're getting spurious timeout and crc errors, so
15503d2bfc8SOlof Johansson 	 * disable signalling of them. In case of real errors software
15603d2bfc8SOlof Johansson 	 * timers should take care of eventually detecting them.
15703d2bfc8SOlof Johansson 	 */
15803d2bfc8SOlof Johansson 	if (unlikely(reg == SDHCI_SIGNAL_ENABLE))
15903d2bfc8SOlof Johansson 		val &= ~(SDHCI_INT_TIMEOUT|SDHCI_INT_CRC);
16003d2bfc8SOlof Johansson 
16103d2bfc8SOlof Johansson 	writel(val, host->ioaddr + reg);
16203d2bfc8SOlof Johansson 
1633e44a1a7SStephen Warren 	if (unlikely((soc_data->nvquirks & NVQUIRK_ENABLE_BLOCK_GAP_DET) &&
1643e44a1a7SStephen Warren 			(reg == SDHCI_INT_ENABLE))) {
16503d2bfc8SOlof Johansson 		/* Erratum: Must enable block gap interrupt detection */
16603d2bfc8SOlof Johansson 		u8 gap_ctrl = readb(host->ioaddr + SDHCI_BLOCK_GAP_CONTROL);
16703d2bfc8SOlof Johansson 		if (val & SDHCI_INT_CARD_INT)
16803d2bfc8SOlof Johansson 			gap_ctrl |= 0x8;
16903d2bfc8SOlof Johansson 		else
17003d2bfc8SOlof Johansson 			gap_ctrl &= ~0x8;
17103d2bfc8SOlof Johansson 		writeb(gap_ctrl, host->ioaddr + SDHCI_BLOCK_GAP_CONTROL);
17203d2bfc8SOlof Johansson 	}
17303d2bfc8SOlof Johansson }
17403d2bfc8SOlof Johansson 
1753e44a1a7SStephen Warren static unsigned int tegra_sdhci_get_ro(struct sdhci_host *host)
17603d2bfc8SOlof Johansson {
1770aacd23fSJoseph Lo 	return mmc_gpio_get_ro(host->mmc);
17803d2bfc8SOlof Johansson }
17903d2bfc8SOlof Johansson 
18086ac2f8bSAapo Vienamo static bool tegra_sdhci_is_pad_and_regulator_valid(struct sdhci_host *host)
18186ac2f8bSAapo Vienamo {
18286ac2f8bSAapo Vienamo 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
18386ac2f8bSAapo Vienamo 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
18486ac2f8bSAapo Vienamo 	int has_1v8, has_3v3;
18586ac2f8bSAapo Vienamo 
18686ac2f8bSAapo Vienamo 	/*
18786ac2f8bSAapo Vienamo 	 * The SoCs which have NVQUIRK_NEEDS_PAD_CONTROL require software pad
18886ac2f8bSAapo Vienamo 	 * voltage configuration in order to perform voltage switching. This
18986ac2f8bSAapo Vienamo 	 * means that valid pinctrl info is required on SDHCI instances capable
19086ac2f8bSAapo Vienamo 	 * of performing voltage switching. Whether or not an SDHCI instance is
19186ac2f8bSAapo Vienamo 	 * capable of voltage switching is determined based on the regulator.
19286ac2f8bSAapo Vienamo 	 */
19386ac2f8bSAapo Vienamo 
19486ac2f8bSAapo Vienamo 	if (!(tegra_host->soc_data->nvquirks & NVQUIRK_NEEDS_PAD_CONTROL))
19586ac2f8bSAapo Vienamo 		return true;
19686ac2f8bSAapo Vienamo 
19786ac2f8bSAapo Vienamo 	if (IS_ERR(host->mmc->supply.vqmmc))
19886ac2f8bSAapo Vienamo 		return false;
19986ac2f8bSAapo Vienamo 
20086ac2f8bSAapo Vienamo 	has_1v8 = regulator_is_supported_voltage(host->mmc->supply.vqmmc,
20186ac2f8bSAapo Vienamo 						 1700000, 1950000);
20286ac2f8bSAapo Vienamo 
20386ac2f8bSAapo Vienamo 	has_3v3 = regulator_is_supported_voltage(host->mmc->supply.vqmmc,
20486ac2f8bSAapo Vienamo 						 2700000, 3600000);
20586ac2f8bSAapo Vienamo 
20686ac2f8bSAapo Vienamo 	if (has_1v8 == 1 && has_3v3 == 1)
20786ac2f8bSAapo Vienamo 		return tegra_host->pad_control_available;
20886ac2f8bSAapo Vienamo 
20986ac2f8bSAapo Vienamo 	/* Fixed voltage, no pad control required. */
21086ac2f8bSAapo Vienamo 	return true;
21186ac2f8bSAapo Vienamo }
21286ac2f8bSAapo Vienamo 
21303231f9bSRussell King static void tegra_sdhci_reset(struct sdhci_host *host, u8 mask)
214ca5879d3SPavan Kunapuli {
215ca5879d3SPavan Kunapuli 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
2160734e79cSJisheng Zhang 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
217ca5879d3SPavan Kunapuli 	const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
2189d548f11SAapo Vienamo 	u32 misc_ctrl, clk_ctrl, pad_ctrl;
219ca5879d3SPavan Kunapuli 
22003231f9bSRussell King 	sdhci_reset(host, mask);
22103231f9bSRussell King 
222ca5879d3SPavan Kunapuli 	if (!(mask & SDHCI_RESET_ALL))
223ca5879d3SPavan Kunapuli 		return;
224ca5879d3SPavan Kunapuli 
2251b84def8SLucas Stach 	misc_ctrl = sdhci_readl(host, SDHCI_TEGRA_VENDOR_MISC_CTRL);
2264f6aa326SJon Hunter 	clk_ctrl = sdhci_readl(host, SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
2274f6aa326SJon Hunter 
2284f6aa326SJon Hunter 	misc_ctrl &= ~(SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300 |
2294f6aa326SJon Hunter 		       SDHCI_MISC_CTRL_ENABLE_SDR50 |
2304f6aa326SJon Hunter 		       SDHCI_MISC_CTRL_ENABLE_DDR50 |
2314f6aa326SJon Hunter 		       SDHCI_MISC_CTRL_ENABLE_SDR104);
2324f6aa326SJon Hunter 
2334f6aa326SJon Hunter 	clk_ctrl &= ~SDHCI_CLOCK_CTRL_SPI_MODE_CLKEN_OVERRIDE;
2344f6aa326SJon Hunter 
23586ac2f8bSAapo Vienamo 	if (tegra_sdhci_is_pad_and_regulator_valid(host)) {
236ca5879d3SPavan Kunapuli 		/* Erratum: Enable SDHCI spec v3.00 support */
2373145351aSAndrew Bresticker 		if (soc_data->nvquirks & NVQUIRK_ENABLE_SDHCI_SPEC_300)
238ca5879d3SPavan Kunapuli 			misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300;
2397ad2ed1dSLucas Stach 		/* Advertise UHS modes as supported by host */
2407ad2ed1dSLucas Stach 		if (soc_data->nvquirks & NVQUIRK_ENABLE_SDR50)
2417ad2ed1dSLucas Stach 			misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDR50;
2427ad2ed1dSLucas Stach 		if (soc_data->nvquirks & NVQUIRK_ENABLE_DDR50)
2437ad2ed1dSLucas Stach 			misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_DDR50;
2447ad2ed1dSLucas Stach 		if (soc_data->nvquirks & NVQUIRK_ENABLE_SDR104)
2457ad2ed1dSLucas Stach 			misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDR104;
2467ad2ed1dSLucas Stach 		if (soc_data->nvquirks & SDHCI_MISC_CTRL_ENABLE_SDR50)
247c3c2384cSLucas Stach 			clk_ctrl |= SDHCI_CLOCK_CTRL_SDR50_TUNING_OVERRIDE;
2484f6aa326SJon Hunter 	}
2494f6aa326SJon Hunter 
2504f6aa326SJon Hunter 	sdhci_writel(host, misc_ctrl, SDHCI_TEGRA_VENDOR_MISC_CTRL);
25174cd42bcSLucas Stach 	sdhci_writel(host, clk_ctrl, SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
25274cd42bcSLucas Stach 
2539d548f11SAapo Vienamo 	if (soc_data->nvquirks & NVQUIRK_HAS_PADCALIB) {
2549d548f11SAapo Vienamo 		pad_ctrl = sdhci_readl(host, SDHCI_TEGRA_SDMEM_COMP_PADCTRL);
2559d548f11SAapo Vienamo 		pad_ctrl &= ~SDHCI_TEGRA_SDMEM_COMP_PADCTRL_VREF_SEL_MASK;
2569d548f11SAapo Vienamo 		pad_ctrl |= SDHCI_TEGRA_SDMEM_COMP_PADCTRL_VREF_SEL_VAL;
2579d548f11SAapo Vienamo 		sdhci_writel(host, pad_ctrl, SDHCI_TEGRA_SDMEM_COMP_PADCTRL);
2589d548f11SAapo Vienamo 
259e5c63d91SLucas Stach 		tegra_host->pad_calib_required = true;
2609d548f11SAapo Vienamo 	}
261e5c63d91SLucas Stach 
262a8e326a9SLucas Stach 	tegra_host->ddr_signaling = false;
263ca5879d3SPavan Kunapuli }
264ca5879d3SPavan Kunapuli 
265212b0cf1SAapo Vienamo static void tegra_sdhci_configure_cal_pad(struct sdhci_host *host, bool enable)
266212b0cf1SAapo Vienamo {
267212b0cf1SAapo Vienamo 	u32 val;
268212b0cf1SAapo Vienamo 
269212b0cf1SAapo Vienamo 	/*
270212b0cf1SAapo Vienamo 	 * Enable or disable the additional I/O pad used by the drive strength
271212b0cf1SAapo Vienamo 	 * calibration process.
272212b0cf1SAapo Vienamo 	 */
273212b0cf1SAapo Vienamo 	val = sdhci_readl(host, SDHCI_TEGRA_SDMEM_COMP_PADCTRL);
274212b0cf1SAapo Vienamo 
275212b0cf1SAapo Vienamo 	if (enable)
276212b0cf1SAapo Vienamo 		val |= SDHCI_TEGRA_SDMEM_COMP_PADCTRL_E_INPUT_E_PWRD;
277212b0cf1SAapo Vienamo 	else
278212b0cf1SAapo Vienamo 		val &= ~SDHCI_TEGRA_SDMEM_COMP_PADCTRL_E_INPUT_E_PWRD;
279212b0cf1SAapo Vienamo 
280212b0cf1SAapo Vienamo 	sdhci_writel(host, val, SDHCI_TEGRA_SDMEM_COMP_PADCTRL);
281212b0cf1SAapo Vienamo 
282212b0cf1SAapo Vienamo 	if (enable)
283212b0cf1SAapo Vienamo 		usleep_range(1, 2);
284212b0cf1SAapo Vienamo }
285212b0cf1SAapo Vienamo 
286887bda8fSAapo Vienamo static bool tegra_sdhci_configure_card_clk(struct sdhci_host *host, bool enable)
287887bda8fSAapo Vienamo {
288887bda8fSAapo Vienamo 	bool status;
289887bda8fSAapo Vienamo 	u32 reg;
290887bda8fSAapo Vienamo 
291887bda8fSAapo Vienamo 	reg = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
292887bda8fSAapo Vienamo 	status = !!(reg & SDHCI_CLOCK_CARD_EN);
293887bda8fSAapo Vienamo 
294887bda8fSAapo Vienamo 	if (status == enable)
295887bda8fSAapo Vienamo 		return status;
296887bda8fSAapo Vienamo 
297887bda8fSAapo Vienamo 	if (enable)
298887bda8fSAapo Vienamo 		reg |= SDHCI_CLOCK_CARD_EN;
299887bda8fSAapo Vienamo 	else
300887bda8fSAapo Vienamo 		reg &= ~SDHCI_CLOCK_CARD_EN;
301887bda8fSAapo Vienamo 
302887bda8fSAapo Vienamo 	sdhci_writew(host, reg, SDHCI_CLOCK_CONTROL);
303887bda8fSAapo Vienamo 
304887bda8fSAapo Vienamo 	return status;
305887bda8fSAapo Vienamo }
306887bda8fSAapo Vienamo 
30751b77c8eSAapo Vienamo static void tegra_sdhci_set_pad_autocal_offset(struct sdhci_host *host,
30851b77c8eSAapo Vienamo 					       u16 pdpu)
30951b77c8eSAapo Vienamo {
31051b77c8eSAapo Vienamo 	u32 reg;
31151b77c8eSAapo Vienamo 
31251b77c8eSAapo Vienamo 	reg = sdhci_readl(host, SDHCI_TEGRA_AUTO_CAL_CONFIG);
31351b77c8eSAapo Vienamo 	reg &= ~SDHCI_AUTO_CAL_PDPU_OFFSET_MASK;
31451b77c8eSAapo Vienamo 	reg |= pdpu;
31551b77c8eSAapo Vienamo 	sdhci_writel(host, reg, SDHCI_TEGRA_AUTO_CAL_CONFIG);
31651b77c8eSAapo Vienamo }
31751b77c8eSAapo Vienamo 
318e5c63d91SLucas Stach static void tegra_sdhci_pad_autocalib(struct sdhci_host *host)
319e5c63d91SLucas Stach {
32051b77c8eSAapo Vienamo 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
32151b77c8eSAapo Vienamo 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
32251b77c8eSAapo Vienamo 	struct sdhci_tegra_autocal_offsets offsets =
32351b77c8eSAapo Vienamo 			tegra_host->autocal_offsets;
32451b77c8eSAapo Vienamo 	struct mmc_ios *ios = &host->mmc->ios;
325887bda8fSAapo Vienamo 	bool card_clk_enabled;
32651b77c8eSAapo Vienamo 	u16 pdpu;
327e7c07148SAapo Vienamo 	u32 reg;
328e7c07148SAapo Vienamo 	int ret;
329e5c63d91SLucas Stach 
33051b77c8eSAapo Vienamo 	switch (ios->timing) {
33151b77c8eSAapo Vienamo 	case MMC_TIMING_UHS_SDR104:
33251b77c8eSAapo Vienamo 		pdpu = offsets.pull_down_sdr104 << 8 | offsets.pull_up_sdr104;
33351b77c8eSAapo Vienamo 		break;
33451b77c8eSAapo Vienamo 	case MMC_TIMING_MMC_HS400:
33551b77c8eSAapo Vienamo 		pdpu = offsets.pull_down_hs400 << 8 | offsets.pull_up_hs400;
33651b77c8eSAapo Vienamo 		break;
33751b77c8eSAapo Vienamo 	default:
33851b77c8eSAapo Vienamo 		if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180)
33951b77c8eSAapo Vienamo 			pdpu = offsets.pull_down_1v8 << 8 | offsets.pull_up_1v8;
34051b77c8eSAapo Vienamo 		else
34151b77c8eSAapo Vienamo 			pdpu = offsets.pull_down_3v3 << 8 | offsets.pull_up_3v3;
34251b77c8eSAapo Vienamo 	}
34351b77c8eSAapo Vienamo 
34451b77c8eSAapo Vienamo 	tegra_sdhci_set_pad_autocal_offset(host, pdpu);
34551b77c8eSAapo Vienamo 
346887bda8fSAapo Vienamo 	card_clk_enabled = tegra_sdhci_configure_card_clk(host, false);
347887bda8fSAapo Vienamo 
348212b0cf1SAapo Vienamo 	tegra_sdhci_configure_cal_pad(host, true);
349212b0cf1SAapo Vienamo 
350e7c07148SAapo Vienamo 	reg = sdhci_readl(host, SDHCI_TEGRA_AUTO_CAL_CONFIG);
351e7c07148SAapo Vienamo 	reg |= SDHCI_AUTO_CAL_ENABLE | SDHCI_AUTO_CAL_START;
352e7c07148SAapo Vienamo 	sdhci_writel(host, reg, SDHCI_TEGRA_AUTO_CAL_CONFIG);
353e5c63d91SLucas Stach 
354e7c07148SAapo Vienamo 	usleep_range(1, 2);
355e7c07148SAapo Vienamo 	/* 10 ms timeout */
356e7c07148SAapo Vienamo 	ret = readl_poll_timeout(host->ioaddr + SDHCI_TEGRA_AUTO_CAL_STATUS,
357e7c07148SAapo Vienamo 				 reg, !(reg & SDHCI_TEGRA_AUTO_CAL_ACTIVE),
358e7c07148SAapo Vienamo 				 1000, 10000);
359e7c07148SAapo Vienamo 
360212b0cf1SAapo Vienamo 	tegra_sdhci_configure_cal_pad(host, false);
361212b0cf1SAapo Vienamo 
362887bda8fSAapo Vienamo 	tegra_sdhci_configure_card_clk(host, card_clk_enabled);
363887bda8fSAapo Vienamo 
36451b77c8eSAapo Vienamo 	if (ret) {
365e7c07148SAapo Vienamo 		dev_err(mmc_dev(host->mmc), "Pad autocal timed out\n");
36651b77c8eSAapo Vienamo 
36751b77c8eSAapo Vienamo 		if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180)
36851b77c8eSAapo Vienamo 			pdpu = offsets.pull_down_1v8_timeout << 8 |
36951b77c8eSAapo Vienamo 			       offsets.pull_up_1v8_timeout;
37051b77c8eSAapo Vienamo 		else
37151b77c8eSAapo Vienamo 			pdpu = offsets.pull_down_3v3_timeout << 8 |
37251b77c8eSAapo Vienamo 			       offsets.pull_up_3v3_timeout;
37351b77c8eSAapo Vienamo 
37451b77c8eSAapo Vienamo 		/* Disable automatic calibration and use fixed offsets */
37551b77c8eSAapo Vienamo 		reg = sdhci_readl(host, SDHCI_TEGRA_AUTO_CAL_CONFIG);
37651b77c8eSAapo Vienamo 		reg &= ~SDHCI_AUTO_CAL_ENABLE;
37751b77c8eSAapo Vienamo 		sdhci_writel(host, reg, SDHCI_TEGRA_AUTO_CAL_CONFIG);
37851b77c8eSAapo Vienamo 
37951b77c8eSAapo Vienamo 		tegra_sdhci_set_pad_autocal_offset(host, pdpu);
38051b77c8eSAapo Vienamo 	}
38151b77c8eSAapo Vienamo }
38251b77c8eSAapo Vienamo 
38351b77c8eSAapo Vienamo static void tegra_sdhci_parse_pad_autocal_dt(struct sdhci_host *host)
38451b77c8eSAapo Vienamo {
38551b77c8eSAapo Vienamo 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
38651b77c8eSAapo Vienamo 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
38751b77c8eSAapo Vienamo 	struct sdhci_tegra_autocal_offsets *autocal =
38851b77c8eSAapo Vienamo 			&tegra_host->autocal_offsets;
38951b77c8eSAapo Vienamo 	int err;
39051b77c8eSAapo Vienamo 
39151b77c8eSAapo Vienamo 	err = device_property_read_u32(host->mmc->parent,
39251b77c8eSAapo Vienamo 			"nvidia,pad-autocal-pull-up-offset-3v3",
39351b77c8eSAapo Vienamo 			&autocal->pull_up_3v3);
39451b77c8eSAapo Vienamo 	if (err)
39551b77c8eSAapo Vienamo 		autocal->pull_up_3v3 = 0;
39651b77c8eSAapo Vienamo 
39751b77c8eSAapo Vienamo 	err = device_property_read_u32(host->mmc->parent,
39851b77c8eSAapo Vienamo 			"nvidia,pad-autocal-pull-down-offset-3v3",
39951b77c8eSAapo Vienamo 			&autocal->pull_down_3v3);
40051b77c8eSAapo Vienamo 	if (err)
40151b77c8eSAapo Vienamo 		autocal->pull_down_3v3 = 0;
40251b77c8eSAapo Vienamo 
40351b77c8eSAapo Vienamo 	err = device_property_read_u32(host->mmc->parent,
40451b77c8eSAapo Vienamo 			"nvidia,pad-autocal-pull-up-offset-1v8",
40551b77c8eSAapo Vienamo 			&autocal->pull_up_1v8);
40651b77c8eSAapo Vienamo 	if (err)
40751b77c8eSAapo Vienamo 		autocal->pull_up_1v8 = 0;
40851b77c8eSAapo Vienamo 
40951b77c8eSAapo Vienamo 	err = device_property_read_u32(host->mmc->parent,
41051b77c8eSAapo Vienamo 			"nvidia,pad-autocal-pull-down-offset-1v8",
41151b77c8eSAapo Vienamo 			&autocal->pull_down_1v8);
41251b77c8eSAapo Vienamo 	if (err)
41351b77c8eSAapo Vienamo 		autocal->pull_down_1v8 = 0;
41451b77c8eSAapo Vienamo 
41551b77c8eSAapo Vienamo 	err = device_property_read_u32(host->mmc->parent,
41651b77c8eSAapo Vienamo 			"nvidia,pad-autocal-pull-up-offset-3v3-timeout",
41751b77c8eSAapo Vienamo 			&autocal->pull_up_3v3);
41851b77c8eSAapo Vienamo 	if (err)
41951b77c8eSAapo Vienamo 		autocal->pull_up_3v3_timeout = 0;
42051b77c8eSAapo Vienamo 
42151b77c8eSAapo Vienamo 	err = device_property_read_u32(host->mmc->parent,
42251b77c8eSAapo Vienamo 			"nvidia,pad-autocal-pull-down-offset-3v3-timeout",
42351b77c8eSAapo Vienamo 			&autocal->pull_down_3v3);
42451b77c8eSAapo Vienamo 	if (err)
42551b77c8eSAapo Vienamo 		autocal->pull_down_3v3_timeout = 0;
42651b77c8eSAapo Vienamo 
42751b77c8eSAapo Vienamo 	err = device_property_read_u32(host->mmc->parent,
42851b77c8eSAapo Vienamo 			"nvidia,pad-autocal-pull-up-offset-1v8-timeout",
42951b77c8eSAapo Vienamo 			&autocal->pull_up_1v8);
43051b77c8eSAapo Vienamo 	if (err)
43151b77c8eSAapo Vienamo 		autocal->pull_up_1v8_timeout = 0;
43251b77c8eSAapo Vienamo 
43351b77c8eSAapo Vienamo 	err = device_property_read_u32(host->mmc->parent,
43451b77c8eSAapo Vienamo 			"nvidia,pad-autocal-pull-down-offset-1v8-timeout",
43551b77c8eSAapo Vienamo 			&autocal->pull_down_1v8);
43651b77c8eSAapo Vienamo 	if (err)
43751b77c8eSAapo Vienamo 		autocal->pull_down_1v8_timeout = 0;
43851b77c8eSAapo Vienamo 
43951b77c8eSAapo Vienamo 	err = device_property_read_u32(host->mmc->parent,
44051b77c8eSAapo Vienamo 			"nvidia,pad-autocal-pull-up-offset-sdr104",
44151b77c8eSAapo Vienamo 			&autocal->pull_up_sdr104);
44251b77c8eSAapo Vienamo 	if (err)
44351b77c8eSAapo Vienamo 		autocal->pull_up_sdr104 = autocal->pull_up_1v8;
44451b77c8eSAapo Vienamo 
44551b77c8eSAapo Vienamo 	err = device_property_read_u32(host->mmc->parent,
44651b77c8eSAapo Vienamo 			"nvidia,pad-autocal-pull-down-offset-sdr104",
44751b77c8eSAapo Vienamo 			&autocal->pull_down_sdr104);
44851b77c8eSAapo Vienamo 	if (err)
44951b77c8eSAapo Vienamo 		autocal->pull_down_sdr104 = autocal->pull_down_1v8;
45051b77c8eSAapo Vienamo 
45151b77c8eSAapo Vienamo 	err = device_property_read_u32(host->mmc->parent,
45251b77c8eSAapo Vienamo 			"nvidia,pad-autocal-pull-up-offset-hs400",
45351b77c8eSAapo Vienamo 			&autocal->pull_up_hs400);
45451b77c8eSAapo Vienamo 	if (err)
45551b77c8eSAapo Vienamo 		autocal->pull_up_hs400 = autocal->pull_up_1v8;
45651b77c8eSAapo Vienamo 
45751b77c8eSAapo Vienamo 	err = device_property_read_u32(host->mmc->parent,
45851b77c8eSAapo Vienamo 			"nvidia,pad-autocal-pull-down-offset-hs400",
45951b77c8eSAapo Vienamo 			&autocal->pull_down_hs400);
46051b77c8eSAapo Vienamo 	if (err)
46151b77c8eSAapo Vienamo 		autocal->pull_down_hs400 = autocal->pull_down_1v8;
462e5c63d91SLucas Stach }
463e5c63d91SLucas Stach 
464a8e326a9SLucas Stach static void tegra_sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
465a8e326a9SLucas Stach {
466a8e326a9SLucas Stach 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
4670734e79cSJisheng Zhang 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
468a8e326a9SLucas Stach 	unsigned long host_clk;
469a8e326a9SLucas Stach 
470a8e326a9SLucas Stach 	if (!clock)
4713491b690SLucas Stach 		return sdhci_set_clock(host, clock);
472a8e326a9SLucas Stach 
47357d1654eSAapo Vienamo 	/*
47457d1654eSAapo Vienamo 	 * In DDR50/52 modes the Tegra SDHCI controllers require the SDHCI
47557d1654eSAapo Vienamo 	 * divider to be configured to divided the host clock by two. The SDHCI
47657d1654eSAapo Vienamo 	 * clock divider is calculated as part of sdhci_set_clock() by
47757d1654eSAapo Vienamo 	 * sdhci_calc_clk(). The divider is calculated from host->max_clk and
47857d1654eSAapo Vienamo 	 * the requested clock rate.
47957d1654eSAapo Vienamo 	 *
48057d1654eSAapo Vienamo 	 * By setting the host->max_clk to clock * 2 the divider calculation
48157d1654eSAapo Vienamo 	 * will always result in the correct value for DDR50/52 modes,
48257d1654eSAapo Vienamo 	 * regardless of clock rate rounding, which may happen if the value
48357d1654eSAapo Vienamo 	 * from clk_get_rate() is used.
48457d1654eSAapo Vienamo 	 */
485a8e326a9SLucas Stach 	host_clk = tegra_host->ddr_signaling ? clock * 2 : clock;
486a8e326a9SLucas Stach 	clk_set_rate(pltfm_host->clk, host_clk);
48757d1654eSAapo Vienamo 	if (tegra_host->ddr_signaling)
48857d1654eSAapo Vienamo 		host->max_clk = host_clk;
48957d1654eSAapo Vienamo 	else
490a8e326a9SLucas Stach 		host->max_clk = clk_get_rate(pltfm_host->clk);
491a8e326a9SLucas Stach 
492e5c63d91SLucas Stach 	sdhci_set_clock(host, clock);
493e5c63d91SLucas Stach 
494e5c63d91SLucas Stach 	if (tegra_host->pad_calib_required) {
495e5c63d91SLucas Stach 		tegra_sdhci_pad_autocalib(host);
496e5c63d91SLucas Stach 		tegra_host->pad_calib_required = false;
497e5c63d91SLucas Stach 	}
498a8e326a9SLucas Stach }
499a8e326a9SLucas Stach 
500a8e326a9SLucas Stach static void tegra_sdhci_set_uhs_signaling(struct sdhci_host *host,
501a8e326a9SLucas Stach 					  unsigned timing)
502a8e326a9SLucas Stach {
503a8e326a9SLucas Stach 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
5040734e79cSJisheng Zhang 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
505a8e326a9SLucas Stach 
506e300149eSStefan Agner 	if (timing == MMC_TIMING_UHS_DDR50 ||
507e300149eSStefan Agner 	    timing == MMC_TIMING_MMC_DDR52)
508a8e326a9SLucas Stach 		tegra_host->ddr_signaling = true;
509a8e326a9SLucas Stach 
510cf56c819SAapo Vienamo 	sdhci_set_uhs_signaling(host, timing);
511a8e326a9SLucas Stach }
512a8e326a9SLucas Stach 
51344350993SAapo Vienamo static unsigned int tegra_sdhci_get_max_clock(struct sdhci_host *host)
51444350993SAapo Vienamo {
51544350993SAapo Vienamo 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
51644350993SAapo Vienamo 
51744350993SAapo Vienamo 	return clk_round_rate(pltfm_host->clk, UINT_MAX);
51844350993SAapo Vienamo }
51944350993SAapo Vienamo 
520c3c2384cSLucas Stach static void tegra_sdhci_set_tap(struct sdhci_host *host, unsigned int tap)
521c3c2384cSLucas Stach {
522d4501d8eSAapo Vienamo 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
523d4501d8eSAapo Vienamo 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
524d4501d8eSAapo Vienamo 	const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
525d4501d8eSAapo Vienamo 	bool card_clk_enabled = false;
526c3c2384cSLucas Stach 	u32 reg;
527c3c2384cSLucas Stach 
528d4501d8eSAapo Vienamo 	/*
529d4501d8eSAapo Vienamo 	 * Touching the tap values is a bit tricky on some SoC generations.
530d4501d8eSAapo Vienamo 	 * The quirk enables a workaround for a glitch that sometimes occurs if
531d4501d8eSAapo Vienamo 	 * the tap values are changed.
532d4501d8eSAapo Vienamo 	 */
533d4501d8eSAapo Vienamo 
534d4501d8eSAapo Vienamo 	if (soc_data->nvquirks & NVQUIRK_DIS_CARD_CLK_CONFIG_TAP)
535d4501d8eSAapo Vienamo 		card_clk_enabled = tegra_sdhci_configure_card_clk(host, false);
536d4501d8eSAapo Vienamo 
537c3c2384cSLucas Stach 	reg = sdhci_readl(host, SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
538c3c2384cSLucas Stach 	reg &= ~SDHCI_CLOCK_CTRL_TAP_MASK;
539c3c2384cSLucas Stach 	reg |= tap << SDHCI_CLOCK_CTRL_TAP_SHIFT;
540c3c2384cSLucas Stach 	sdhci_writel(host, reg, SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
541d4501d8eSAapo Vienamo 
542d4501d8eSAapo Vienamo 	if (soc_data->nvquirks & NVQUIRK_DIS_CARD_CLK_CONFIG_TAP &&
543d4501d8eSAapo Vienamo 	    card_clk_enabled) {
544d4501d8eSAapo Vienamo 		usleep_range(1, 2);
545d4501d8eSAapo Vienamo 		sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
546d4501d8eSAapo Vienamo 		tegra_sdhci_configure_card_clk(host, card_clk_enabled);
547d4501d8eSAapo Vienamo 	}
548c3c2384cSLucas Stach }
549c3c2384cSLucas Stach 
550c3c2384cSLucas Stach static int tegra_sdhci_execute_tuning(struct sdhci_host *host, u32 opcode)
551c3c2384cSLucas Stach {
552c3c2384cSLucas Stach 	unsigned int min, max;
553c3c2384cSLucas Stach 
554c3c2384cSLucas Stach 	/*
555c3c2384cSLucas Stach 	 * Start search for minimum tap value at 10, as smaller values are
556c3c2384cSLucas Stach 	 * may wrongly be reported as working but fail at higher speeds,
557c3c2384cSLucas Stach 	 * according to the TRM.
558c3c2384cSLucas Stach 	 */
559c3c2384cSLucas Stach 	min = 10;
560c3c2384cSLucas Stach 	while (min < 255) {
561c3c2384cSLucas Stach 		tegra_sdhci_set_tap(host, min);
562c3c2384cSLucas Stach 		if (!mmc_send_tuning(host->mmc, opcode, NULL))
563c3c2384cSLucas Stach 			break;
564c3c2384cSLucas Stach 		min++;
565c3c2384cSLucas Stach 	}
566c3c2384cSLucas Stach 
567c3c2384cSLucas Stach 	/* Find the maximum tap value that still passes. */
568c3c2384cSLucas Stach 	max = min + 1;
569c3c2384cSLucas Stach 	while (max < 255) {
570c3c2384cSLucas Stach 		tegra_sdhci_set_tap(host, max);
571c3c2384cSLucas Stach 		if (mmc_send_tuning(host->mmc, opcode, NULL)) {
572c3c2384cSLucas Stach 			max--;
573c3c2384cSLucas Stach 			break;
574c3c2384cSLucas Stach 		}
575c3c2384cSLucas Stach 		max++;
576c3c2384cSLucas Stach 	}
577c3c2384cSLucas Stach 
578c3c2384cSLucas Stach 	/* The TRM states the ideal tap value is at 75% in the passing range. */
579c3c2384cSLucas Stach 	tegra_sdhci_set_tap(host, min + ((max - min) * 3 / 4));
580c3c2384cSLucas Stach 
581c3c2384cSLucas Stach 	return mmc_send_tuning(host->mmc, opcode, NULL);
582c3c2384cSLucas Stach }
583c3c2384cSLucas Stach 
58486ac2f8bSAapo Vienamo static int tegra_sdhci_set_padctrl(struct sdhci_host *host, int voltage)
58586ac2f8bSAapo Vienamo {
58686ac2f8bSAapo Vienamo 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
58786ac2f8bSAapo Vienamo 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
58886ac2f8bSAapo Vienamo 	int ret;
58986ac2f8bSAapo Vienamo 
59086ac2f8bSAapo Vienamo 	if (!tegra_host->pad_control_available)
59186ac2f8bSAapo Vienamo 		return 0;
59286ac2f8bSAapo Vienamo 
59386ac2f8bSAapo Vienamo 	if (voltage == MMC_SIGNAL_VOLTAGE_180) {
59486ac2f8bSAapo Vienamo 		ret = pinctrl_select_state(tegra_host->pinctrl_sdmmc,
59586ac2f8bSAapo Vienamo 					   tegra_host->pinctrl_state_1v8);
59686ac2f8bSAapo Vienamo 		if (ret < 0)
59786ac2f8bSAapo Vienamo 			dev_err(mmc_dev(host->mmc),
59886ac2f8bSAapo Vienamo 				"setting 1.8V failed, ret: %d\n", ret);
59986ac2f8bSAapo Vienamo 	} else {
60086ac2f8bSAapo Vienamo 		ret = pinctrl_select_state(tegra_host->pinctrl_sdmmc,
60186ac2f8bSAapo Vienamo 					   tegra_host->pinctrl_state_3v3);
60286ac2f8bSAapo Vienamo 		if (ret < 0)
60386ac2f8bSAapo Vienamo 			dev_err(mmc_dev(host->mmc),
60486ac2f8bSAapo Vienamo 				"setting 3.3V failed, ret: %d\n", ret);
60586ac2f8bSAapo Vienamo 	}
60686ac2f8bSAapo Vienamo 
60786ac2f8bSAapo Vienamo 	return ret;
60886ac2f8bSAapo Vienamo }
60986ac2f8bSAapo Vienamo 
61086ac2f8bSAapo Vienamo static int sdhci_tegra_start_signal_voltage_switch(struct mmc_host *mmc,
61186ac2f8bSAapo Vienamo 						   struct mmc_ios *ios)
61286ac2f8bSAapo Vienamo {
61386ac2f8bSAapo Vienamo 	struct sdhci_host *host = mmc_priv(mmc);
61444babea2SAapo Vienamo 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
61544babea2SAapo Vienamo 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
61686ac2f8bSAapo Vienamo 	int ret = 0;
61786ac2f8bSAapo Vienamo 
61886ac2f8bSAapo Vienamo 	if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330) {
61986ac2f8bSAapo Vienamo 		ret = tegra_sdhci_set_padctrl(host, ios->signal_voltage);
62086ac2f8bSAapo Vienamo 		if (ret < 0)
62186ac2f8bSAapo Vienamo 			return ret;
62286ac2f8bSAapo Vienamo 		ret = sdhci_start_signal_voltage_switch(mmc, ios);
62386ac2f8bSAapo Vienamo 	} else if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180) {
62486ac2f8bSAapo Vienamo 		ret = sdhci_start_signal_voltage_switch(mmc, ios);
62586ac2f8bSAapo Vienamo 		if (ret < 0)
62686ac2f8bSAapo Vienamo 			return ret;
62786ac2f8bSAapo Vienamo 		ret = tegra_sdhci_set_padctrl(host, ios->signal_voltage);
62886ac2f8bSAapo Vienamo 	}
62986ac2f8bSAapo Vienamo 
63044babea2SAapo Vienamo 	if (tegra_host->pad_calib_required)
63144babea2SAapo Vienamo 		tegra_sdhci_pad_autocalib(host);
63244babea2SAapo Vienamo 
63386ac2f8bSAapo Vienamo 	return ret;
63486ac2f8bSAapo Vienamo }
63586ac2f8bSAapo Vienamo 
63686ac2f8bSAapo Vienamo static int tegra_sdhci_init_pinctrl_info(struct device *dev,
63786ac2f8bSAapo Vienamo 					 struct sdhci_tegra *tegra_host)
63886ac2f8bSAapo Vienamo {
63986ac2f8bSAapo Vienamo 	tegra_host->pinctrl_sdmmc = devm_pinctrl_get(dev);
64086ac2f8bSAapo Vienamo 	if (IS_ERR(tegra_host->pinctrl_sdmmc)) {
64186ac2f8bSAapo Vienamo 		dev_dbg(dev, "No pinctrl info, err: %ld\n",
64286ac2f8bSAapo Vienamo 			PTR_ERR(tegra_host->pinctrl_sdmmc));
64386ac2f8bSAapo Vienamo 		return -1;
64486ac2f8bSAapo Vienamo 	}
64586ac2f8bSAapo Vienamo 
64686ac2f8bSAapo Vienamo 	tegra_host->pinctrl_state_3v3 =
64786ac2f8bSAapo Vienamo 		pinctrl_lookup_state(tegra_host->pinctrl_sdmmc, "sdmmc-3v3");
64886ac2f8bSAapo Vienamo 	if (IS_ERR(tegra_host->pinctrl_state_3v3)) {
64986ac2f8bSAapo Vienamo 		dev_warn(dev, "Missing 3.3V pad state, err: %ld\n",
65086ac2f8bSAapo Vienamo 			 PTR_ERR(tegra_host->pinctrl_state_3v3));
65186ac2f8bSAapo Vienamo 		return -1;
65286ac2f8bSAapo Vienamo 	}
65386ac2f8bSAapo Vienamo 
65486ac2f8bSAapo Vienamo 	tegra_host->pinctrl_state_1v8 =
65586ac2f8bSAapo Vienamo 		pinctrl_lookup_state(tegra_host->pinctrl_sdmmc, "sdmmc-1v8");
65686ac2f8bSAapo Vienamo 	if (IS_ERR(tegra_host->pinctrl_state_1v8)) {
65786ac2f8bSAapo Vienamo 		dev_warn(dev, "Missing 1.8V pad state, err: %ld\n",
65886ac2f8bSAapo Vienamo 			 PTR_ERR(tegra_host->pinctrl_state_3v3));
65986ac2f8bSAapo Vienamo 		return -1;
66086ac2f8bSAapo Vienamo 	}
66186ac2f8bSAapo Vienamo 
66286ac2f8bSAapo Vienamo 	tegra_host->pad_control_available = true;
66386ac2f8bSAapo Vienamo 
66486ac2f8bSAapo Vienamo 	return 0;
66586ac2f8bSAapo Vienamo }
66686ac2f8bSAapo Vienamo 
667e5c63d91SLucas Stach static void tegra_sdhci_voltage_switch(struct sdhci_host *host)
668e5c63d91SLucas Stach {
669e5c63d91SLucas Stach 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
670e5c63d91SLucas Stach 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
671e5c63d91SLucas Stach 	const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
672e5c63d91SLucas Stach 
673e5c63d91SLucas Stach 	if (soc_data->nvquirks & NVQUIRK_HAS_PADCALIB)
674e5c63d91SLucas Stach 		tegra_host->pad_calib_required = true;
675e5c63d91SLucas Stach }
676e5c63d91SLucas Stach 
677c915568dSLars-Peter Clausen static const struct sdhci_ops tegra_sdhci_ops = {
67885d6509dSShawn Guo 	.get_ro     = tegra_sdhci_get_ro,
67985d6509dSShawn Guo 	.read_w     = tegra_sdhci_readw,
68085d6509dSShawn Guo 	.write_l    = tegra_sdhci_writel,
681a8e326a9SLucas Stach 	.set_clock  = tegra_sdhci_set_clock,
68214b04c6aSMichał Mirosław 	.set_bus_width = sdhci_set_bus_width,
68303231f9bSRussell King 	.reset      = tegra_sdhci_reset,
684c3c2384cSLucas Stach 	.platform_execute_tuning = tegra_sdhci_execute_tuning,
685a8e326a9SLucas Stach 	.set_uhs_signaling = tegra_sdhci_set_uhs_signaling,
686e5c63d91SLucas Stach 	.voltage_switch = tegra_sdhci_voltage_switch,
68744350993SAapo Vienamo 	.get_max_clock = tegra_sdhci_get_max_clock,
68885d6509dSShawn Guo };
68903d2bfc8SOlof Johansson 
6901db5eebfSLars-Peter Clausen static const struct sdhci_pltfm_data sdhci_tegra20_pdata = {
69185d6509dSShawn Guo 	.quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
69285d6509dSShawn Guo 		  SDHCI_QUIRK_SINGLE_POWER_WRITE |
69385d6509dSShawn Guo 		  SDHCI_QUIRK_NO_HISPD_BIT |
694f9260355SAndrew Bresticker 		  SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
695f9260355SAndrew Bresticker 		  SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
69685d6509dSShawn Guo 	.ops  = &tegra_sdhci_ops,
69785d6509dSShawn Guo };
69885d6509dSShawn Guo 
699d49d19c2SThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra20 = {
7003e44a1a7SStephen Warren 	.pdata = &sdhci_tegra20_pdata,
7013e44a1a7SStephen Warren 	.nvquirks = NVQUIRK_FORCE_SDHCI_SPEC_200 |
7023e44a1a7SStephen Warren 		    NVQUIRK_ENABLE_BLOCK_GAP_DET,
7033e44a1a7SStephen Warren };
7043e44a1a7SStephen Warren 
7051db5eebfSLars-Peter Clausen static const struct sdhci_pltfm_data sdhci_tegra30_pdata = {
7063e44a1a7SStephen Warren 	.quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
7073e44a1a7SStephen Warren 		  SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
7083e44a1a7SStephen Warren 		  SDHCI_QUIRK_SINGLE_POWER_WRITE |
7093e44a1a7SStephen Warren 		  SDHCI_QUIRK_NO_HISPD_BIT |
710f9260355SAndrew Bresticker 		  SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
711f9260355SAndrew Bresticker 		  SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
712127407e3SStefan Agner 	.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
713726df1d5SStefan Agner 		   SDHCI_QUIRK2_BROKEN_HS200 |
714726df1d5SStefan Agner 		   /*
715726df1d5SStefan Agner 		    * Auto-CMD23 leads to "Got command interrupt 0x00010000 even
716726df1d5SStefan Agner 		    * though no command operation was in progress."
717726df1d5SStefan Agner 		    *
718726df1d5SStefan Agner 		    * The exact reason is unknown, as the same hardware seems
719726df1d5SStefan Agner 		    * to support Auto CMD23 on a downstream 3.1 kernel.
720726df1d5SStefan Agner 		    */
721726df1d5SStefan Agner 		   SDHCI_QUIRK2_ACMD23_BROKEN,
7223e44a1a7SStephen Warren 	.ops  = &tegra_sdhci_ops,
7233e44a1a7SStephen Warren };
7243e44a1a7SStephen Warren 
725d49d19c2SThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra30 = {
7263e44a1a7SStephen Warren 	.pdata = &sdhci_tegra30_pdata,
7273145351aSAndrew Bresticker 	.nvquirks = NVQUIRK_ENABLE_SDHCI_SPEC_300 |
7287ad2ed1dSLucas Stach 		    NVQUIRK_ENABLE_SDR50 |
729e5c63d91SLucas Stach 		    NVQUIRK_ENABLE_SDR104 |
730e5c63d91SLucas Stach 		    NVQUIRK_HAS_PADCALIB,
7313e44a1a7SStephen Warren };
7323e44a1a7SStephen Warren 
73301df7ecdSRhyland Klein static const struct sdhci_ops tegra114_sdhci_ops = {
73401df7ecdSRhyland Klein 	.get_ro     = tegra_sdhci_get_ro,
73501df7ecdSRhyland Klein 	.read_w     = tegra_sdhci_readw,
73601df7ecdSRhyland Klein 	.write_w    = tegra_sdhci_writew,
73701df7ecdSRhyland Klein 	.write_l    = tegra_sdhci_writel,
738a8e326a9SLucas Stach 	.set_clock  = tegra_sdhci_set_clock,
73914b04c6aSMichał Mirosław 	.set_bus_width = sdhci_set_bus_width,
74001df7ecdSRhyland Klein 	.reset      = tegra_sdhci_reset,
741c3c2384cSLucas Stach 	.platform_execute_tuning = tegra_sdhci_execute_tuning,
742a8e326a9SLucas Stach 	.set_uhs_signaling = tegra_sdhci_set_uhs_signaling,
743e5c63d91SLucas Stach 	.voltage_switch = tegra_sdhci_voltage_switch,
74444350993SAapo Vienamo 	.get_max_clock = tegra_sdhci_get_max_clock,
74501df7ecdSRhyland Klein };
74601df7ecdSRhyland Klein 
7471db5eebfSLars-Peter Clausen static const struct sdhci_pltfm_data sdhci_tegra114_pdata = {
7485ebf2552SRhyland Klein 	.quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
7495ebf2552SRhyland Klein 		  SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
7505ebf2552SRhyland Klein 		  SDHCI_QUIRK_SINGLE_POWER_WRITE |
7515ebf2552SRhyland Klein 		  SDHCI_QUIRK_NO_HISPD_BIT |
752f9260355SAndrew Bresticker 		  SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
753f9260355SAndrew Bresticker 		  SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
754a8e326a9SLucas Stach 	.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
75501df7ecdSRhyland Klein 	.ops  = &tegra114_sdhci_ops,
7565ebf2552SRhyland Klein };
7575ebf2552SRhyland Klein 
758d49d19c2SThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra114 = {
7595ebf2552SRhyland Klein 	.pdata = &sdhci_tegra114_pdata,
7607bf037d6SJon Hunter };
7617bf037d6SJon Hunter 
7624ae12588SThierry Reding static const struct sdhci_pltfm_data sdhci_tegra124_pdata = {
7634ae12588SThierry Reding 	.quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
7644ae12588SThierry Reding 		  SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
7654ae12588SThierry Reding 		  SDHCI_QUIRK_SINGLE_POWER_WRITE |
7664ae12588SThierry Reding 		  SDHCI_QUIRK_NO_HISPD_BIT |
7674ae12588SThierry Reding 		  SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
7684ae12588SThierry Reding 		  SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
7694ae12588SThierry Reding 	.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
7704ae12588SThierry Reding 		   /*
7714ae12588SThierry Reding 		    * The TRM states that the SD/MMC controller found on
7724ae12588SThierry Reding 		    * Tegra124 can address 34 bits (the maximum supported by
7734ae12588SThierry Reding 		    * the Tegra memory controller), but tests show that DMA
7744ae12588SThierry Reding 		    * to or from above 4 GiB doesn't work. This is possibly
7754ae12588SThierry Reding 		    * caused by missing programming, though it's not obvious
7764ae12588SThierry Reding 		    * what sequence is required. Mark 64-bit DMA broken for
7774ae12588SThierry Reding 		    * now to fix this for existing users (e.g. Nyan boards).
7784ae12588SThierry Reding 		    */
7794ae12588SThierry Reding 		   SDHCI_QUIRK2_BROKEN_64_BIT_DMA,
7804ae12588SThierry Reding 	.ops  = &tegra114_sdhci_ops,
7814ae12588SThierry Reding };
7824ae12588SThierry Reding 
7834ae12588SThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra124 = {
7844ae12588SThierry Reding 	.pdata = &sdhci_tegra124_pdata,
7854ae12588SThierry Reding };
7864ae12588SThierry Reding 
787b5a84ecfSThierry Reding static const struct sdhci_pltfm_data sdhci_tegra210_pdata = {
788b5a84ecfSThierry Reding 	.quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
789b5a84ecfSThierry Reding 		  SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
790b5a84ecfSThierry Reding 		  SDHCI_QUIRK_SINGLE_POWER_WRITE |
791b5a84ecfSThierry Reding 		  SDHCI_QUIRK_NO_HISPD_BIT |
792a8e326a9SLucas Stach 		  SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
793a8e326a9SLucas Stach 		  SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
794a8e326a9SLucas Stach 	.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
795b5a84ecfSThierry Reding 	.ops  = &tegra114_sdhci_ops,
796b5a84ecfSThierry Reding };
797b5a84ecfSThierry Reding 
798b5a84ecfSThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra210 = {
799b5a84ecfSThierry Reding 	.pdata = &sdhci_tegra210_pdata,
800d943f6e9SAapo Vienamo 	.nvquirks = NVQUIRK_NEEDS_PAD_CONTROL |
801d4501d8eSAapo Vienamo 		    NVQUIRK_HAS_PADCALIB |
802d4501d8eSAapo Vienamo 		    NVQUIRK_DIS_CARD_CLK_CONFIG_TAP,
803b5a84ecfSThierry Reding };
804b5a84ecfSThierry Reding 
8054346b7c7SThierry Reding static const struct sdhci_pltfm_data sdhci_tegra186_pdata = {
8064346b7c7SThierry Reding 	.quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
8074346b7c7SThierry Reding 		  SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
8084346b7c7SThierry Reding 		  SDHCI_QUIRK_SINGLE_POWER_WRITE |
8094346b7c7SThierry Reding 		  SDHCI_QUIRK_NO_HISPD_BIT |
8104346b7c7SThierry Reding 		  SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
8114346b7c7SThierry Reding 		  SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
81268481a7eSKrishna Reddy 	.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
81368481a7eSKrishna Reddy 		   /* SDHCI controllers on Tegra186 support 40-bit addressing.
81468481a7eSKrishna Reddy 		    * IOVA addresses are 48-bit wide on Tegra186.
81568481a7eSKrishna Reddy 		    * With 64-bit dma mask used for SDHCI, accesses can
81668481a7eSKrishna Reddy 		    * be broken. Disable 64-bit dma, which would fall back
81768481a7eSKrishna Reddy 		    * to 32-bit dma mask. Ideally 40-bit dma mask would work,
81868481a7eSKrishna Reddy 		    * But it is not supported as of now.
81968481a7eSKrishna Reddy 		    */
82068481a7eSKrishna Reddy 		   SDHCI_QUIRK2_BROKEN_64_BIT_DMA,
8214346b7c7SThierry Reding 	.ops  = &tegra114_sdhci_ops,
8224346b7c7SThierry Reding };
8234346b7c7SThierry Reding 
8244346b7c7SThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra186 = {
8254346b7c7SThierry Reding 	.pdata = &sdhci_tegra186_pdata,
826d943f6e9SAapo Vienamo 	.nvquirks = NVQUIRK_NEEDS_PAD_CONTROL |
827d4501d8eSAapo Vienamo 		    NVQUIRK_HAS_PADCALIB |
828d4501d8eSAapo Vienamo 		    NVQUIRK_DIS_CARD_CLK_CONFIG_TAP,
8294346b7c7SThierry Reding };
8304346b7c7SThierry Reding 
831498d83e7SBill Pemberton static const struct of_device_id sdhci_tegra_dt_match[] = {
8324346b7c7SThierry Reding 	{ .compatible = "nvidia,tegra186-sdhci", .data = &soc_data_tegra186 },
833b5a84ecfSThierry Reding 	{ .compatible = "nvidia,tegra210-sdhci", .data = &soc_data_tegra210 },
8344ae12588SThierry Reding 	{ .compatible = "nvidia,tegra124-sdhci", .data = &soc_data_tegra124 },
8355ebf2552SRhyland Klein 	{ .compatible = "nvidia,tegra114-sdhci", .data = &soc_data_tegra114 },
8363e44a1a7SStephen Warren 	{ .compatible = "nvidia,tegra30-sdhci", .data = &soc_data_tegra30 },
8373e44a1a7SStephen Warren 	{ .compatible = "nvidia,tegra20-sdhci", .data = &soc_data_tegra20 },
838275173b2SGrant Likely 	{}
839275173b2SGrant Likely };
840e4404fabSArnd Bergmann MODULE_DEVICE_TABLE(of, sdhci_tegra_dt_match);
841275173b2SGrant Likely 
842c3be1efdSBill Pemberton static int sdhci_tegra_probe(struct platform_device *pdev)
84303d2bfc8SOlof Johansson {
8443e44a1a7SStephen Warren 	const struct of_device_id *match;
8453e44a1a7SStephen Warren 	const struct sdhci_tegra_soc_data *soc_data;
8463e44a1a7SStephen Warren 	struct sdhci_host *host;
84785d6509dSShawn Guo 	struct sdhci_pltfm_host *pltfm_host;
8483e44a1a7SStephen Warren 	struct sdhci_tegra *tegra_host;
84903d2bfc8SOlof Johansson 	struct clk *clk;
85003d2bfc8SOlof Johansson 	int rc;
85103d2bfc8SOlof Johansson 
8523e44a1a7SStephen Warren 	match = of_match_device(sdhci_tegra_dt_match, &pdev->dev);
853b37f9d98SJoseph Lo 	if (!match)
854b37f9d98SJoseph Lo 		return -EINVAL;
8553e44a1a7SStephen Warren 	soc_data = match->data;
8563e44a1a7SStephen Warren 
8570734e79cSJisheng Zhang 	host = sdhci_pltfm_init(pdev, soc_data->pdata, sizeof(*tegra_host));
85885d6509dSShawn Guo 	if (IS_ERR(host))
85985d6509dSShawn Guo 		return PTR_ERR(host);
86085d6509dSShawn Guo 	pltfm_host = sdhci_priv(host);
86185d6509dSShawn Guo 
8620734e79cSJisheng Zhang 	tegra_host = sdhci_pltfm_priv(pltfm_host);
863a8e326a9SLucas Stach 	tegra_host->ddr_signaling = false;
864e5c63d91SLucas Stach 	tegra_host->pad_calib_required = false;
86586ac2f8bSAapo Vienamo 	tegra_host->pad_control_available = false;
8663e44a1a7SStephen Warren 	tegra_host->soc_data = soc_data;
867275173b2SGrant Likely 
86886ac2f8bSAapo Vienamo 	if (soc_data->nvquirks & NVQUIRK_NEEDS_PAD_CONTROL) {
86986ac2f8bSAapo Vienamo 		rc = tegra_sdhci_init_pinctrl_info(&pdev->dev, tegra_host);
87086ac2f8bSAapo Vienamo 		if (rc == 0)
87186ac2f8bSAapo Vienamo 			host->mmc_host_ops.start_signal_voltage_switch =
87286ac2f8bSAapo Vienamo 				sdhci_tegra_start_signal_voltage_switch;
87386ac2f8bSAapo Vienamo 	}
87486ac2f8bSAapo Vienamo 
8752391b340SMylene JOSSERAND 	rc = mmc_of_parse(host->mmc);
87647caa84fSSimon Baatz 	if (rc)
87747caa84fSSimon Baatz 		goto err_parse_dt;
8780e786102SStephen Warren 
8797ad2ed1dSLucas Stach 	if (tegra_host->soc_data->nvquirks & NVQUIRK_ENABLE_DDR50)
880c3c2384cSLucas Stach 		host->mmc->caps |= MMC_CAP_1_8V_DDR;
881c3c2384cSLucas Stach 
88251b77c8eSAapo Vienamo 	tegra_sdhci_parse_pad_autocal_dt(host);
88351b77c8eSAapo Vienamo 
8842391b340SMylene JOSSERAND 	tegra_host->power_gpio = devm_gpiod_get_optional(&pdev->dev, "power",
8852391b340SMylene JOSSERAND 							 GPIOD_OUT_HIGH);
8862391b340SMylene JOSSERAND 	if (IS_ERR(tegra_host->power_gpio)) {
8872391b340SMylene JOSSERAND 		rc = PTR_ERR(tegra_host->power_gpio);
88885d6509dSShawn Guo 		goto err_power_req;
88903d2bfc8SOlof Johansson 	}
89003d2bfc8SOlof Johansson 
891e4f79d9cSKevin Hao 	clk = devm_clk_get(mmc_dev(host->mmc), NULL);
89203d2bfc8SOlof Johansson 	if (IS_ERR(clk)) {
89303d2bfc8SOlof Johansson 		dev_err(mmc_dev(host->mmc), "clk err\n");
89403d2bfc8SOlof Johansson 		rc = PTR_ERR(clk);
89585d6509dSShawn Guo 		goto err_clk_get;
89603d2bfc8SOlof Johansson 	}
8971e674bc6SPrashant Gaikwad 	clk_prepare_enable(clk);
89803d2bfc8SOlof Johansson 	pltfm_host->clk = clk;
89903d2bfc8SOlof Johansson 
9002cd6c49dSPhilipp Zabel 	tegra_host->rst = devm_reset_control_get_exclusive(&pdev->dev,
9012cd6c49dSPhilipp Zabel 							   "sdhci");
90220567be9SThierry Reding 	if (IS_ERR(tegra_host->rst)) {
90320567be9SThierry Reding 		rc = PTR_ERR(tegra_host->rst);
90420567be9SThierry Reding 		dev_err(&pdev->dev, "failed to get reset control: %d\n", rc);
90520567be9SThierry Reding 		goto err_rst_get;
90620567be9SThierry Reding 	}
90720567be9SThierry Reding 
90820567be9SThierry Reding 	rc = reset_control_assert(tegra_host->rst);
90920567be9SThierry Reding 	if (rc)
91020567be9SThierry Reding 		goto err_rst_get;
91120567be9SThierry Reding 
91220567be9SThierry Reding 	usleep_range(2000, 4000);
91320567be9SThierry Reding 
91420567be9SThierry Reding 	rc = reset_control_deassert(tegra_host->rst);
91520567be9SThierry Reding 	if (rc)
91620567be9SThierry Reding 		goto err_rst_get;
91720567be9SThierry Reding 
91820567be9SThierry Reding 	usleep_range(2000, 4000);
91920567be9SThierry Reding 
92085d6509dSShawn Guo 	rc = sdhci_add_host(host);
92185d6509dSShawn Guo 	if (rc)
92285d6509dSShawn Guo 		goto err_add_host;
92385d6509dSShawn Guo 
92403d2bfc8SOlof Johansson 	return 0;
92503d2bfc8SOlof Johansson 
92685d6509dSShawn Guo err_add_host:
92720567be9SThierry Reding 	reset_control_assert(tegra_host->rst);
92820567be9SThierry Reding err_rst_get:
9291e674bc6SPrashant Gaikwad 	clk_disable_unprepare(pltfm_host->clk);
93085d6509dSShawn Guo err_clk_get:
93185d6509dSShawn Guo err_power_req:
93247caa84fSSimon Baatz err_parse_dt:
93385d6509dSShawn Guo 	sdhci_pltfm_free(pdev);
93403d2bfc8SOlof Johansson 	return rc;
93503d2bfc8SOlof Johansson }
93603d2bfc8SOlof Johansson 
93720567be9SThierry Reding static int sdhci_tegra_remove(struct platform_device *pdev)
93820567be9SThierry Reding {
93920567be9SThierry Reding 	struct sdhci_host *host = platform_get_drvdata(pdev);
94020567be9SThierry Reding 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
94120567be9SThierry Reding 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
94220567be9SThierry Reding 
94320567be9SThierry Reding 	sdhci_remove_host(host, 0);
94420567be9SThierry Reding 
94520567be9SThierry Reding 	reset_control_assert(tegra_host->rst);
94620567be9SThierry Reding 	usleep_range(2000, 4000);
94720567be9SThierry Reding 	clk_disable_unprepare(pltfm_host->clk);
94820567be9SThierry Reding 
94920567be9SThierry Reding 	sdhci_pltfm_free(pdev);
95020567be9SThierry Reding 
95120567be9SThierry Reding 	return 0;
95220567be9SThierry Reding }
95320567be9SThierry Reding 
95485d6509dSShawn Guo static struct platform_driver sdhci_tegra_driver = {
95585d6509dSShawn Guo 	.driver		= {
95685d6509dSShawn Guo 		.name	= "sdhci-tegra",
957275173b2SGrant Likely 		.of_match_table = sdhci_tegra_dt_match,
958fa243f64SUlf Hansson 		.pm	= &sdhci_pltfm_pmops,
95985d6509dSShawn Guo 	},
96085d6509dSShawn Guo 	.probe		= sdhci_tegra_probe,
96120567be9SThierry Reding 	.remove		= sdhci_tegra_remove,
96203d2bfc8SOlof Johansson };
96303d2bfc8SOlof Johansson 
964d1f81a64SAxel Lin module_platform_driver(sdhci_tegra_driver);
96585d6509dSShawn Guo 
96685d6509dSShawn Guo MODULE_DESCRIPTION("SDHCI driver for Tegra");
96785d6509dSShawn Guo MODULE_AUTHOR("Google, Inc.");
96885d6509dSShawn Guo MODULE_LICENSE("GPL v2");
969