xref: /openbmc/linux/drivers/mmc/host/sdhci-tegra.c (revision 7bf037d6)
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 
1503d2bfc8SOlof Johansson #include <linux/err.h>
1696547f5dSPaul Gortmaker #include <linux/module.h>
1703d2bfc8SOlof Johansson #include <linux/init.h>
1803d2bfc8SOlof Johansson #include <linux/platform_device.h>
1903d2bfc8SOlof Johansson #include <linux/clk.h>
2003d2bfc8SOlof Johansson #include <linux/io.h>
2155cd65e4SStephen Warren #include <linux/of.h>
223e44a1a7SStephen Warren #include <linux/of_device.h>
2303d2bfc8SOlof Johansson #include <linux/mmc/card.h>
2403d2bfc8SOlof Johansson #include <linux/mmc/host.h>
25c3c2384cSLucas Stach #include <linux/mmc/mmc.h>
260aacd23fSJoseph Lo #include <linux/mmc/slot-gpio.h>
272391b340SMylene JOSSERAND #include <linux/gpio/consumer.h>
2803d2bfc8SOlof Johansson 
2903d2bfc8SOlof Johansson #include "sdhci-pltfm.h"
3003d2bfc8SOlof Johansson 
31ca5879d3SPavan Kunapuli /* Tegra SDHOST controller vendor register definitions */
3274cd42bcSLucas Stach #define SDHCI_TEGRA_VENDOR_CLOCK_CTRL			0x100
33c3c2384cSLucas Stach #define SDHCI_CLOCK_CTRL_TAP_MASK			0x00ff0000
34c3c2384cSLucas Stach #define SDHCI_CLOCK_CTRL_TAP_SHIFT			16
35c3c2384cSLucas Stach #define SDHCI_CLOCK_CTRL_SDR50_TUNING_OVERRIDE		BIT(5)
3674cd42bcSLucas Stach #define SDHCI_CLOCK_CTRL_PADPIPE_CLKEN_OVERRIDE		BIT(3)
3774cd42bcSLucas Stach #define SDHCI_CLOCK_CTRL_SPI_MODE_CLKEN_OVERRIDE	BIT(2)
3874cd42bcSLucas Stach 
39ca5879d3SPavan Kunapuli #define SDHCI_TEGRA_VENDOR_MISC_CTRL		0x120
403145351aSAndrew Bresticker #define SDHCI_MISC_CTRL_ENABLE_SDR104		0x8
413145351aSAndrew Bresticker #define SDHCI_MISC_CTRL_ENABLE_SDR50		0x10
42ca5879d3SPavan Kunapuli #define SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300	0x20
433145351aSAndrew Bresticker #define SDHCI_MISC_CTRL_ENABLE_DDR50		0x200
44ca5879d3SPavan Kunapuli 
453e44a1a7SStephen Warren #define NVQUIRK_FORCE_SDHCI_SPEC_200	BIT(0)
463e44a1a7SStephen Warren #define NVQUIRK_ENABLE_BLOCK_GAP_DET	BIT(1)
47ca5879d3SPavan Kunapuli #define NVQUIRK_ENABLE_SDHCI_SPEC_300	BIT(2)
487ad2ed1dSLucas Stach #define NVQUIRK_ENABLE_SDR50		BIT(3)
497ad2ed1dSLucas Stach #define NVQUIRK_ENABLE_SDR104		BIT(4)
507ad2ed1dSLucas Stach #define NVQUIRK_ENABLE_DDR50		BIT(5)
513e44a1a7SStephen Warren 
523e44a1a7SStephen Warren struct sdhci_tegra_soc_data {
531db5eebfSLars-Peter Clausen 	const struct sdhci_pltfm_data *pdata;
543e44a1a7SStephen Warren 	u32 nvquirks;
553e44a1a7SStephen Warren };
563e44a1a7SStephen Warren 
573e44a1a7SStephen Warren struct sdhci_tegra {
583e44a1a7SStephen Warren 	const struct sdhci_tegra_soc_data *soc_data;
592391b340SMylene JOSSERAND 	struct gpio_desc *power_gpio;
60a8e326a9SLucas Stach 	bool ddr_signaling;
613e44a1a7SStephen Warren };
623e44a1a7SStephen Warren 
6303d2bfc8SOlof Johansson static u16 tegra_sdhci_readw(struct sdhci_host *host, int reg)
6403d2bfc8SOlof Johansson {
653e44a1a7SStephen Warren 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
660734e79cSJisheng Zhang 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
673e44a1a7SStephen Warren 	const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
683e44a1a7SStephen Warren 
693e44a1a7SStephen Warren 	if (unlikely((soc_data->nvquirks & NVQUIRK_FORCE_SDHCI_SPEC_200) &&
703e44a1a7SStephen Warren 			(reg == SDHCI_HOST_VERSION))) {
7103d2bfc8SOlof Johansson 		/* Erratum: Version register is invalid in HW. */
7203d2bfc8SOlof Johansson 		return SDHCI_SPEC_200;
7303d2bfc8SOlof Johansson 	}
7403d2bfc8SOlof Johansson 
7503d2bfc8SOlof Johansson 	return readw(host->ioaddr + reg);
7603d2bfc8SOlof Johansson }
7703d2bfc8SOlof Johansson 
78352ee868SPavan Kunapuli static void tegra_sdhci_writew(struct sdhci_host *host, u16 val, int reg)
79352ee868SPavan Kunapuli {
80352ee868SPavan Kunapuli 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
81352ee868SPavan Kunapuli 
82352ee868SPavan Kunapuli 	switch (reg) {
83352ee868SPavan Kunapuli 	case SDHCI_TRANSFER_MODE:
84352ee868SPavan Kunapuli 		/*
85352ee868SPavan Kunapuli 		 * Postpone this write, we must do it together with a
86352ee868SPavan Kunapuli 		 * command write that is down below.
87352ee868SPavan Kunapuli 		 */
88352ee868SPavan Kunapuli 		pltfm_host->xfer_mode_shadow = val;
89352ee868SPavan Kunapuli 		return;
90352ee868SPavan Kunapuli 	case SDHCI_COMMAND:
91352ee868SPavan Kunapuli 		writel((val << 16) | pltfm_host->xfer_mode_shadow,
92352ee868SPavan Kunapuli 			host->ioaddr + SDHCI_TRANSFER_MODE);
93352ee868SPavan Kunapuli 		return;
94352ee868SPavan Kunapuli 	}
95352ee868SPavan Kunapuli 
96352ee868SPavan Kunapuli 	writew(val, host->ioaddr + reg);
97352ee868SPavan Kunapuli }
98352ee868SPavan Kunapuli 
9903d2bfc8SOlof Johansson static void tegra_sdhci_writel(struct sdhci_host *host, u32 val, int reg)
10003d2bfc8SOlof Johansson {
1013e44a1a7SStephen Warren 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1020734e79cSJisheng Zhang 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
1033e44a1a7SStephen Warren 	const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
1043e44a1a7SStephen Warren 
10503d2bfc8SOlof Johansson 	/* Seems like we're getting spurious timeout and crc errors, so
10603d2bfc8SOlof Johansson 	 * disable signalling of them. In case of real errors software
10703d2bfc8SOlof Johansson 	 * timers should take care of eventually detecting them.
10803d2bfc8SOlof Johansson 	 */
10903d2bfc8SOlof Johansson 	if (unlikely(reg == SDHCI_SIGNAL_ENABLE))
11003d2bfc8SOlof Johansson 		val &= ~(SDHCI_INT_TIMEOUT|SDHCI_INT_CRC);
11103d2bfc8SOlof Johansson 
11203d2bfc8SOlof Johansson 	writel(val, host->ioaddr + reg);
11303d2bfc8SOlof Johansson 
1143e44a1a7SStephen Warren 	if (unlikely((soc_data->nvquirks & NVQUIRK_ENABLE_BLOCK_GAP_DET) &&
1153e44a1a7SStephen Warren 			(reg == SDHCI_INT_ENABLE))) {
11603d2bfc8SOlof Johansson 		/* Erratum: Must enable block gap interrupt detection */
11703d2bfc8SOlof Johansson 		u8 gap_ctrl = readb(host->ioaddr + SDHCI_BLOCK_GAP_CONTROL);
11803d2bfc8SOlof Johansson 		if (val & SDHCI_INT_CARD_INT)
11903d2bfc8SOlof Johansson 			gap_ctrl |= 0x8;
12003d2bfc8SOlof Johansson 		else
12103d2bfc8SOlof Johansson 			gap_ctrl &= ~0x8;
12203d2bfc8SOlof Johansson 		writeb(gap_ctrl, host->ioaddr + SDHCI_BLOCK_GAP_CONTROL);
12303d2bfc8SOlof Johansson 	}
12403d2bfc8SOlof Johansson }
12503d2bfc8SOlof Johansson 
1263e44a1a7SStephen Warren static unsigned int tegra_sdhci_get_ro(struct sdhci_host *host)
12703d2bfc8SOlof Johansson {
1280aacd23fSJoseph Lo 	return mmc_gpio_get_ro(host->mmc);
12903d2bfc8SOlof Johansson }
13003d2bfc8SOlof Johansson 
13103231f9bSRussell King static void tegra_sdhci_reset(struct sdhci_host *host, u8 mask)
132ca5879d3SPavan Kunapuli {
133ca5879d3SPavan Kunapuli 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1340734e79cSJisheng Zhang 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
135ca5879d3SPavan Kunapuli 	const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
13674cd42bcSLucas Stach 	u32 misc_ctrl, clk_ctrl;
137ca5879d3SPavan Kunapuli 
13803231f9bSRussell King 	sdhci_reset(host, mask);
13903231f9bSRussell King 
140ca5879d3SPavan Kunapuli 	if (!(mask & SDHCI_RESET_ALL))
141ca5879d3SPavan Kunapuli 		return;
142ca5879d3SPavan Kunapuli 
1431b84def8SLucas Stach 	misc_ctrl = sdhci_readl(host, SDHCI_TEGRA_VENDOR_MISC_CTRL);
144ca5879d3SPavan Kunapuli 	/* Erratum: Enable SDHCI spec v3.00 support */
1453145351aSAndrew Bresticker 	if (soc_data->nvquirks & NVQUIRK_ENABLE_SDHCI_SPEC_300)
146ca5879d3SPavan Kunapuli 		misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300;
1477ad2ed1dSLucas Stach 	/* Advertise UHS modes as supported by host */
1487ad2ed1dSLucas Stach 	if (soc_data->nvquirks & NVQUIRK_ENABLE_SDR50)
1497ad2ed1dSLucas Stach 		misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDR50;
1507bf037d6SJon Hunter 	else
1517bf037d6SJon Hunter 		misc_ctrl &= ~SDHCI_MISC_CTRL_ENABLE_SDR50;
1527ad2ed1dSLucas Stach 	if (soc_data->nvquirks & NVQUIRK_ENABLE_DDR50)
1537ad2ed1dSLucas Stach 		misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_DDR50;
1547bf037d6SJon Hunter 	else
1557bf037d6SJon Hunter 		misc_ctrl &= ~SDHCI_MISC_CTRL_ENABLE_DDR50;
1567ad2ed1dSLucas Stach 	if (soc_data->nvquirks & NVQUIRK_ENABLE_SDR104)
1577ad2ed1dSLucas Stach 		misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDR104;
1587bf037d6SJon Hunter 	else
1597bf037d6SJon Hunter 		misc_ctrl &= ~SDHCI_MISC_CTRL_ENABLE_SDR104;
1601b84def8SLucas Stach 	sdhci_writel(host, misc_ctrl, SDHCI_TEGRA_VENDOR_MISC_CTRL);
161a8e326a9SLucas Stach 
16274cd42bcSLucas Stach 	clk_ctrl = sdhci_readl(host, SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
16374cd42bcSLucas Stach 	clk_ctrl &= ~SDHCI_CLOCK_CTRL_SPI_MODE_CLKEN_OVERRIDE;
1647ad2ed1dSLucas Stach 	if (soc_data->nvquirks & SDHCI_MISC_CTRL_ENABLE_SDR50)
165c3c2384cSLucas Stach 		clk_ctrl |= SDHCI_CLOCK_CTRL_SDR50_TUNING_OVERRIDE;
16674cd42bcSLucas Stach 	sdhci_writel(host, clk_ctrl, SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
16774cd42bcSLucas Stach 
168a8e326a9SLucas Stach 	tegra_host->ddr_signaling = false;
169ca5879d3SPavan Kunapuli }
170ca5879d3SPavan Kunapuli 
1712317f56cSRussell King static void tegra_sdhci_set_bus_width(struct sdhci_host *host, int bus_width)
17203d2bfc8SOlof Johansson {
17303d2bfc8SOlof Johansson 	u32 ctrl;
17403d2bfc8SOlof Johansson 
17503d2bfc8SOlof Johansson 	ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
1760aacd23fSJoseph Lo 	if ((host->mmc->caps & MMC_CAP_8_BIT_DATA) &&
1770aacd23fSJoseph Lo 	    (bus_width == MMC_BUS_WIDTH_8)) {
17803d2bfc8SOlof Johansson 		ctrl &= ~SDHCI_CTRL_4BITBUS;
17903d2bfc8SOlof Johansson 		ctrl |= SDHCI_CTRL_8BITBUS;
18003d2bfc8SOlof Johansson 	} else {
18103d2bfc8SOlof Johansson 		ctrl &= ~SDHCI_CTRL_8BITBUS;
18203d2bfc8SOlof Johansson 		if (bus_width == MMC_BUS_WIDTH_4)
18303d2bfc8SOlof Johansson 			ctrl |= SDHCI_CTRL_4BITBUS;
18403d2bfc8SOlof Johansson 		else
18503d2bfc8SOlof Johansson 			ctrl &= ~SDHCI_CTRL_4BITBUS;
18603d2bfc8SOlof Johansson 	}
18703d2bfc8SOlof Johansson 	sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
18803d2bfc8SOlof Johansson }
18903d2bfc8SOlof Johansson 
190a8e326a9SLucas Stach static void tegra_sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
191a8e326a9SLucas Stach {
192a8e326a9SLucas Stach 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
1930734e79cSJisheng Zhang 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
194a8e326a9SLucas Stach 	unsigned long host_clk;
195a8e326a9SLucas Stach 
196a8e326a9SLucas Stach 	if (!clock)
197a8e326a9SLucas Stach 		return;
198a8e326a9SLucas Stach 
199a8e326a9SLucas Stach 	host_clk = tegra_host->ddr_signaling ? clock * 2 : clock;
200a8e326a9SLucas Stach 	clk_set_rate(pltfm_host->clk, host_clk);
201a8e326a9SLucas Stach 	host->max_clk = clk_get_rate(pltfm_host->clk);
202a8e326a9SLucas Stach 
203a8e326a9SLucas Stach 	return sdhci_set_clock(host, clock);
204a8e326a9SLucas Stach }
205a8e326a9SLucas Stach 
206a8e326a9SLucas Stach static void tegra_sdhci_set_uhs_signaling(struct sdhci_host *host,
207a8e326a9SLucas Stach 					  unsigned timing)
208a8e326a9SLucas Stach {
209a8e326a9SLucas Stach 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
2100734e79cSJisheng Zhang 	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
211a8e326a9SLucas Stach 
212a8e326a9SLucas Stach 	if (timing == MMC_TIMING_UHS_DDR50)
213a8e326a9SLucas Stach 		tegra_host->ddr_signaling = true;
214a8e326a9SLucas Stach 
215a8e326a9SLucas Stach 	return sdhci_set_uhs_signaling(host, timing);
216a8e326a9SLucas Stach }
217a8e326a9SLucas Stach 
218a8e326a9SLucas Stach static unsigned int tegra_sdhci_get_max_clock(struct sdhci_host *host)
219a8e326a9SLucas Stach {
220a8e326a9SLucas Stach 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
221a8e326a9SLucas Stach 
222a8e326a9SLucas Stach 	/*
223a8e326a9SLucas Stach 	 * DDR modes require the host to run at double the card frequency, so
224a8e326a9SLucas Stach 	 * the maximum rate we can support is half of the module input clock.
225a8e326a9SLucas Stach 	 */
226a8e326a9SLucas Stach 	return clk_round_rate(pltfm_host->clk, UINT_MAX) / 2;
227a8e326a9SLucas Stach }
228a8e326a9SLucas Stach 
229c3c2384cSLucas Stach static void tegra_sdhci_set_tap(struct sdhci_host *host, unsigned int tap)
230c3c2384cSLucas Stach {
231c3c2384cSLucas Stach 	u32 reg;
232c3c2384cSLucas Stach 
233c3c2384cSLucas Stach 	reg = sdhci_readl(host, SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
234c3c2384cSLucas Stach 	reg &= ~SDHCI_CLOCK_CTRL_TAP_MASK;
235c3c2384cSLucas Stach 	reg |= tap << SDHCI_CLOCK_CTRL_TAP_SHIFT;
236c3c2384cSLucas Stach 	sdhci_writel(host, reg, SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
237c3c2384cSLucas Stach }
238c3c2384cSLucas Stach 
239c3c2384cSLucas Stach static int tegra_sdhci_execute_tuning(struct sdhci_host *host, u32 opcode)
240c3c2384cSLucas Stach {
241c3c2384cSLucas Stach 	unsigned int min, max;
242c3c2384cSLucas Stach 
243c3c2384cSLucas Stach 	/*
244c3c2384cSLucas Stach 	 * Start search for minimum tap value at 10, as smaller values are
245c3c2384cSLucas Stach 	 * may wrongly be reported as working but fail at higher speeds,
246c3c2384cSLucas Stach 	 * according to the TRM.
247c3c2384cSLucas Stach 	 */
248c3c2384cSLucas Stach 	min = 10;
249c3c2384cSLucas Stach 	while (min < 255) {
250c3c2384cSLucas Stach 		tegra_sdhci_set_tap(host, min);
251c3c2384cSLucas Stach 		if (!mmc_send_tuning(host->mmc, opcode, NULL))
252c3c2384cSLucas Stach 			break;
253c3c2384cSLucas Stach 		min++;
254c3c2384cSLucas Stach 	}
255c3c2384cSLucas Stach 
256c3c2384cSLucas Stach 	/* Find the maximum tap value that still passes. */
257c3c2384cSLucas Stach 	max = min + 1;
258c3c2384cSLucas Stach 	while (max < 255) {
259c3c2384cSLucas Stach 		tegra_sdhci_set_tap(host, max);
260c3c2384cSLucas Stach 		if (mmc_send_tuning(host->mmc, opcode, NULL)) {
261c3c2384cSLucas Stach 			max--;
262c3c2384cSLucas Stach 			break;
263c3c2384cSLucas Stach 		}
264c3c2384cSLucas Stach 		max++;
265c3c2384cSLucas Stach 	}
266c3c2384cSLucas Stach 
267c3c2384cSLucas Stach 	/* The TRM states the ideal tap value is at 75% in the passing range. */
268c3c2384cSLucas Stach 	tegra_sdhci_set_tap(host, min + ((max - min) * 3 / 4));
269c3c2384cSLucas Stach 
270c3c2384cSLucas Stach 	return mmc_send_tuning(host->mmc, opcode, NULL);
271c3c2384cSLucas Stach }
272c3c2384cSLucas Stach 
273c915568dSLars-Peter Clausen static const struct sdhci_ops tegra_sdhci_ops = {
27485d6509dSShawn Guo 	.get_ro     = tegra_sdhci_get_ro,
27585d6509dSShawn Guo 	.read_w     = tegra_sdhci_readw,
27685d6509dSShawn Guo 	.write_l    = tegra_sdhci_writel,
277a8e326a9SLucas Stach 	.set_clock  = tegra_sdhci_set_clock,
2782317f56cSRussell King 	.set_bus_width = tegra_sdhci_set_bus_width,
27903231f9bSRussell King 	.reset      = tegra_sdhci_reset,
280c3c2384cSLucas Stach 	.platform_execute_tuning = tegra_sdhci_execute_tuning,
281a8e326a9SLucas Stach 	.set_uhs_signaling = tegra_sdhci_set_uhs_signaling,
282a8e326a9SLucas Stach 	.get_max_clock = tegra_sdhci_get_max_clock,
28385d6509dSShawn Guo };
28403d2bfc8SOlof Johansson 
2851db5eebfSLars-Peter Clausen static const struct sdhci_pltfm_data sdhci_tegra20_pdata = {
28685d6509dSShawn Guo 	.quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
28785d6509dSShawn Guo 		  SDHCI_QUIRK_SINGLE_POWER_WRITE |
28885d6509dSShawn Guo 		  SDHCI_QUIRK_NO_HISPD_BIT |
289f9260355SAndrew Bresticker 		  SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
290f9260355SAndrew Bresticker 		  SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
29185d6509dSShawn Guo 	.ops  = &tegra_sdhci_ops,
29285d6509dSShawn Guo };
29385d6509dSShawn Guo 
294d49d19c2SThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra20 = {
2953e44a1a7SStephen Warren 	.pdata = &sdhci_tegra20_pdata,
2963e44a1a7SStephen Warren 	.nvquirks = NVQUIRK_FORCE_SDHCI_SPEC_200 |
2973e44a1a7SStephen Warren 		    NVQUIRK_ENABLE_BLOCK_GAP_DET,
2983e44a1a7SStephen Warren };
2993e44a1a7SStephen Warren 
3001db5eebfSLars-Peter Clausen static const struct sdhci_pltfm_data sdhci_tegra30_pdata = {
3013e44a1a7SStephen Warren 	.quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
3023e44a1a7SStephen Warren 		  SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
3033e44a1a7SStephen Warren 		  SDHCI_QUIRK_SINGLE_POWER_WRITE |
3043e44a1a7SStephen Warren 		  SDHCI_QUIRK_NO_HISPD_BIT |
305f9260355SAndrew Bresticker 		  SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
306f9260355SAndrew Bresticker 		  SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
307a8e326a9SLucas Stach 	.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
3083e44a1a7SStephen Warren 	.ops  = &tegra_sdhci_ops,
3093e44a1a7SStephen Warren };
3103e44a1a7SStephen Warren 
311d49d19c2SThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra30 = {
3123e44a1a7SStephen Warren 	.pdata = &sdhci_tegra30_pdata,
3133145351aSAndrew Bresticker 	.nvquirks = NVQUIRK_ENABLE_SDHCI_SPEC_300 |
3147ad2ed1dSLucas Stach 		    NVQUIRK_ENABLE_SDR50 |
3157ad2ed1dSLucas Stach 		    NVQUIRK_ENABLE_SDR104,
3163e44a1a7SStephen Warren };
3173e44a1a7SStephen Warren 
31801df7ecdSRhyland Klein static const struct sdhci_ops tegra114_sdhci_ops = {
31901df7ecdSRhyland Klein 	.get_ro     = tegra_sdhci_get_ro,
32001df7ecdSRhyland Klein 	.read_w     = tegra_sdhci_readw,
32101df7ecdSRhyland Klein 	.write_w    = tegra_sdhci_writew,
32201df7ecdSRhyland Klein 	.write_l    = tegra_sdhci_writel,
323a8e326a9SLucas Stach 	.set_clock  = tegra_sdhci_set_clock,
32401df7ecdSRhyland Klein 	.set_bus_width = tegra_sdhci_set_bus_width,
32501df7ecdSRhyland Klein 	.reset      = tegra_sdhci_reset,
326c3c2384cSLucas Stach 	.platform_execute_tuning = tegra_sdhci_execute_tuning,
327a8e326a9SLucas Stach 	.set_uhs_signaling = tegra_sdhci_set_uhs_signaling,
328a8e326a9SLucas Stach 	.get_max_clock = tegra_sdhci_get_max_clock,
32901df7ecdSRhyland Klein };
33001df7ecdSRhyland Klein 
3311db5eebfSLars-Peter Clausen static const struct sdhci_pltfm_data sdhci_tegra114_pdata = {
3325ebf2552SRhyland Klein 	.quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
3335ebf2552SRhyland Klein 		  SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
3345ebf2552SRhyland Klein 		  SDHCI_QUIRK_SINGLE_POWER_WRITE |
3355ebf2552SRhyland Klein 		  SDHCI_QUIRK_NO_HISPD_BIT |
336f9260355SAndrew Bresticker 		  SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
337f9260355SAndrew Bresticker 		  SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
338a8e326a9SLucas Stach 	.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
33901df7ecdSRhyland Klein 	.ops  = &tegra114_sdhci_ops,
3405ebf2552SRhyland Klein };
3415ebf2552SRhyland Klein 
342d49d19c2SThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra114 = {
3435ebf2552SRhyland Klein 	.pdata = &sdhci_tegra114_pdata,
3447bf037d6SJon Hunter };
3457bf037d6SJon Hunter 
3467bf037d6SJon Hunter static const struct sdhci_tegra_soc_data soc_data_tegra124 = {
3477bf037d6SJon Hunter 	.pdata = &sdhci_tegra114_pdata,
3487ad2ed1dSLucas Stach 	.nvquirks = NVQUIRK_ENABLE_SDR50 |
3497ad2ed1dSLucas Stach 		    NVQUIRK_ENABLE_DDR50 |
3507ad2ed1dSLucas Stach 		    NVQUIRK_ENABLE_SDR104,
3515ebf2552SRhyland Klein };
3525ebf2552SRhyland Klein 
353b5a84ecfSThierry Reding static const struct sdhci_pltfm_data sdhci_tegra210_pdata = {
354b5a84ecfSThierry Reding 	.quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
355b5a84ecfSThierry Reding 		  SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
356b5a84ecfSThierry Reding 		  SDHCI_QUIRK_SINGLE_POWER_WRITE |
357b5a84ecfSThierry Reding 		  SDHCI_QUIRK_NO_HISPD_BIT |
358a8e326a9SLucas Stach 		  SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
359a8e326a9SLucas Stach 		  SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
360a8e326a9SLucas Stach 	.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
361b5a84ecfSThierry Reding 	.ops  = &tegra114_sdhci_ops,
362b5a84ecfSThierry Reding };
363b5a84ecfSThierry Reding 
364b5a84ecfSThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra210 = {
365b5a84ecfSThierry Reding 	.pdata = &sdhci_tegra210_pdata,
366b5a84ecfSThierry Reding };
367b5a84ecfSThierry Reding 
368498d83e7SBill Pemberton static const struct of_device_id sdhci_tegra_dt_match[] = {
369b5a84ecfSThierry Reding 	{ .compatible = "nvidia,tegra210-sdhci", .data = &soc_data_tegra210 },
3707bf037d6SJon Hunter 	{ .compatible = "nvidia,tegra124-sdhci", .data = &soc_data_tegra124 },
3715ebf2552SRhyland Klein 	{ .compatible = "nvidia,tegra114-sdhci", .data = &soc_data_tegra114 },
3723e44a1a7SStephen Warren 	{ .compatible = "nvidia,tegra30-sdhci", .data = &soc_data_tegra30 },
3733e44a1a7SStephen Warren 	{ .compatible = "nvidia,tegra20-sdhci", .data = &soc_data_tegra20 },
374275173b2SGrant Likely 	{}
375275173b2SGrant Likely };
376e4404fabSArnd Bergmann MODULE_DEVICE_TABLE(of, sdhci_tegra_dt_match);
377275173b2SGrant Likely 
378c3be1efdSBill Pemberton static int sdhci_tegra_probe(struct platform_device *pdev)
37903d2bfc8SOlof Johansson {
3803e44a1a7SStephen Warren 	const struct of_device_id *match;
3813e44a1a7SStephen Warren 	const struct sdhci_tegra_soc_data *soc_data;
3823e44a1a7SStephen Warren 	struct sdhci_host *host;
38385d6509dSShawn Guo 	struct sdhci_pltfm_host *pltfm_host;
3843e44a1a7SStephen Warren 	struct sdhci_tegra *tegra_host;
38503d2bfc8SOlof Johansson 	struct clk *clk;
38603d2bfc8SOlof Johansson 	int rc;
38703d2bfc8SOlof Johansson 
3883e44a1a7SStephen Warren 	match = of_match_device(sdhci_tegra_dt_match, &pdev->dev);
389b37f9d98SJoseph Lo 	if (!match)
390b37f9d98SJoseph Lo 		return -EINVAL;
3913e44a1a7SStephen Warren 	soc_data = match->data;
3923e44a1a7SStephen Warren 
3930734e79cSJisheng Zhang 	host = sdhci_pltfm_init(pdev, soc_data->pdata, sizeof(*tegra_host));
39485d6509dSShawn Guo 	if (IS_ERR(host))
39585d6509dSShawn Guo 		return PTR_ERR(host);
39685d6509dSShawn Guo 	pltfm_host = sdhci_priv(host);
39785d6509dSShawn Guo 
3980734e79cSJisheng Zhang 	tegra_host = sdhci_pltfm_priv(pltfm_host);
399a8e326a9SLucas Stach 	tegra_host->ddr_signaling = false;
4003e44a1a7SStephen Warren 	tegra_host->soc_data = soc_data;
401275173b2SGrant Likely 
4022391b340SMylene JOSSERAND 	rc = mmc_of_parse(host->mmc);
40347caa84fSSimon Baatz 	if (rc)
40447caa84fSSimon Baatz 		goto err_parse_dt;
4050e786102SStephen Warren 
4067ad2ed1dSLucas Stach 	if (tegra_host->soc_data->nvquirks & NVQUIRK_ENABLE_DDR50)
407c3c2384cSLucas Stach 		host->mmc->caps |= MMC_CAP_1_8V_DDR;
408c3c2384cSLucas Stach 
4092391b340SMylene JOSSERAND 	tegra_host->power_gpio = devm_gpiod_get_optional(&pdev->dev, "power",
4102391b340SMylene JOSSERAND 							 GPIOD_OUT_HIGH);
4112391b340SMylene JOSSERAND 	if (IS_ERR(tegra_host->power_gpio)) {
4122391b340SMylene JOSSERAND 		rc = PTR_ERR(tegra_host->power_gpio);
41385d6509dSShawn Guo 		goto err_power_req;
41403d2bfc8SOlof Johansson 	}
41503d2bfc8SOlof Johansson 
416e4f79d9cSKevin Hao 	clk = devm_clk_get(mmc_dev(host->mmc), NULL);
41703d2bfc8SOlof Johansson 	if (IS_ERR(clk)) {
41803d2bfc8SOlof Johansson 		dev_err(mmc_dev(host->mmc), "clk err\n");
41903d2bfc8SOlof Johansson 		rc = PTR_ERR(clk);
42085d6509dSShawn Guo 		goto err_clk_get;
42103d2bfc8SOlof Johansson 	}
4221e674bc6SPrashant Gaikwad 	clk_prepare_enable(clk);
42303d2bfc8SOlof Johansson 	pltfm_host->clk = clk;
42403d2bfc8SOlof Johansson 
42585d6509dSShawn Guo 	rc = sdhci_add_host(host);
42685d6509dSShawn Guo 	if (rc)
42785d6509dSShawn Guo 		goto err_add_host;
42885d6509dSShawn Guo 
42903d2bfc8SOlof Johansson 	return 0;
43003d2bfc8SOlof Johansson 
43185d6509dSShawn Guo err_add_host:
4321e674bc6SPrashant Gaikwad 	clk_disable_unprepare(pltfm_host->clk);
43385d6509dSShawn Guo err_clk_get:
43485d6509dSShawn Guo err_power_req:
43547caa84fSSimon Baatz err_parse_dt:
43685d6509dSShawn Guo 	sdhci_pltfm_free(pdev);
43703d2bfc8SOlof Johansson 	return rc;
43803d2bfc8SOlof Johansson }
43903d2bfc8SOlof Johansson 
44085d6509dSShawn Guo static struct platform_driver sdhci_tegra_driver = {
44185d6509dSShawn Guo 	.driver		= {
44285d6509dSShawn Guo 		.name	= "sdhci-tegra",
443275173b2SGrant Likely 		.of_match_table = sdhci_tegra_dt_match,
44429495aa0SManuel Lauss 		.pm	= SDHCI_PLTFM_PMOPS,
44585d6509dSShawn Guo 	},
44685d6509dSShawn Guo 	.probe		= sdhci_tegra_probe,
447caebcae9SKevin Hao 	.remove		= sdhci_pltfm_unregister,
44803d2bfc8SOlof Johansson };
44903d2bfc8SOlof Johansson 
450d1f81a64SAxel Lin module_platform_driver(sdhci_tegra_driver);
45185d6509dSShawn Guo 
45285d6509dSShawn Guo MODULE_DESCRIPTION("SDHCI driver for Tegra");
45385d6509dSShawn Guo MODULE_AUTHOR("Google, Inc.");
45485d6509dSShawn Guo MODULE_LICENSE("GPL v2");
455