xref: /openbmc/linux/drivers/mmc/host/sdhci-tegra.c (revision 7ad2ed1d)
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);
663e44a1a7SStephen Warren 	struct sdhci_tegra *tegra_host = pltfm_host->priv;
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);
1023e44a1a7SStephen Warren 	struct sdhci_tegra *tegra_host = pltfm_host->priv;
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);
134ca5879d3SPavan Kunapuli 	struct sdhci_tegra *tegra_host = pltfm_host->priv;
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 
1433145351aSAndrew Bresticker 	misc_ctrl = sdhci_readw(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;
1507ad2ed1dSLucas Stach 	if (soc_data->nvquirks & NVQUIRK_ENABLE_DDR50)
1517ad2ed1dSLucas Stach 		misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_DDR50;
1527ad2ed1dSLucas Stach 	if (soc_data->nvquirks & NVQUIRK_ENABLE_SDR104)
1537ad2ed1dSLucas Stach 		misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDR104;
1543145351aSAndrew Bresticker 	sdhci_writew(host, misc_ctrl, SDHCI_TEGRA_VENDOR_MISC_CTRL);
155a8e326a9SLucas Stach 
15674cd42bcSLucas Stach 	clk_ctrl = sdhci_readl(host, SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
15774cd42bcSLucas Stach 	clk_ctrl &= ~SDHCI_CLOCK_CTRL_SPI_MODE_CLKEN_OVERRIDE;
1587ad2ed1dSLucas Stach 	if (soc_data->nvquirks & SDHCI_MISC_CTRL_ENABLE_SDR50)
159c3c2384cSLucas Stach 		clk_ctrl |= SDHCI_CLOCK_CTRL_SDR50_TUNING_OVERRIDE;
16074cd42bcSLucas Stach 	sdhci_writel(host, clk_ctrl, SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
16174cd42bcSLucas Stach 
162a8e326a9SLucas Stach 	tegra_host->ddr_signaling = false;
163ca5879d3SPavan Kunapuli }
164ca5879d3SPavan Kunapuli 
1652317f56cSRussell King static void tegra_sdhci_set_bus_width(struct sdhci_host *host, int bus_width)
16603d2bfc8SOlof Johansson {
16703d2bfc8SOlof Johansson 	u32 ctrl;
16803d2bfc8SOlof Johansson 
16903d2bfc8SOlof Johansson 	ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
1700aacd23fSJoseph Lo 	if ((host->mmc->caps & MMC_CAP_8_BIT_DATA) &&
1710aacd23fSJoseph Lo 	    (bus_width == MMC_BUS_WIDTH_8)) {
17203d2bfc8SOlof Johansson 		ctrl &= ~SDHCI_CTRL_4BITBUS;
17303d2bfc8SOlof Johansson 		ctrl |= SDHCI_CTRL_8BITBUS;
17403d2bfc8SOlof Johansson 	} else {
17503d2bfc8SOlof Johansson 		ctrl &= ~SDHCI_CTRL_8BITBUS;
17603d2bfc8SOlof Johansson 		if (bus_width == MMC_BUS_WIDTH_4)
17703d2bfc8SOlof Johansson 			ctrl |= SDHCI_CTRL_4BITBUS;
17803d2bfc8SOlof Johansson 		else
17903d2bfc8SOlof Johansson 			ctrl &= ~SDHCI_CTRL_4BITBUS;
18003d2bfc8SOlof Johansson 	}
18103d2bfc8SOlof Johansson 	sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
18203d2bfc8SOlof Johansson }
18303d2bfc8SOlof Johansson 
184a8e326a9SLucas Stach static void tegra_sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
185a8e326a9SLucas Stach {
186a8e326a9SLucas Stach 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
187a8e326a9SLucas Stach 	struct sdhci_tegra *tegra_host = pltfm_host->priv;
188a8e326a9SLucas Stach 	unsigned long host_clk;
189a8e326a9SLucas Stach 
190a8e326a9SLucas Stach 	if (!clock)
191a8e326a9SLucas Stach 		return;
192a8e326a9SLucas Stach 
193a8e326a9SLucas Stach 	host_clk = tegra_host->ddr_signaling ? clock * 2 : clock;
194a8e326a9SLucas Stach 	clk_set_rate(pltfm_host->clk, host_clk);
195a8e326a9SLucas Stach 	host->max_clk = clk_get_rate(pltfm_host->clk);
196a8e326a9SLucas Stach 
197a8e326a9SLucas Stach 	return sdhci_set_clock(host, clock);
198a8e326a9SLucas Stach }
199a8e326a9SLucas Stach 
200a8e326a9SLucas Stach static void tegra_sdhci_set_uhs_signaling(struct sdhci_host *host,
201a8e326a9SLucas Stach 					  unsigned timing)
202a8e326a9SLucas Stach {
203a8e326a9SLucas Stach 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
204a8e326a9SLucas Stach 	struct sdhci_tegra *tegra_host = pltfm_host->priv;
205a8e326a9SLucas Stach 
206a8e326a9SLucas Stach 	if (timing == MMC_TIMING_UHS_DDR50)
207a8e326a9SLucas Stach 		tegra_host->ddr_signaling = true;
208a8e326a9SLucas Stach 
209a8e326a9SLucas Stach 	return sdhci_set_uhs_signaling(host, timing);
210a8e326a9SLucas Stach }
211a8e326a9SLucas Stach 
212a8e326a9SLucas Stach static unsigned int tegra_sdhci_get_max_clock(struct sdhci_host *host)
213a8e326a9SLucas Stach {
214a8e326a9SLucas Stach 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
215a8e326a9SLucas Stach 
216a8e326a9SLucas Stach 	/*
217a8e326a9SLucas Stach 	 * DDR modes require the host to run at double the card frequency, so
218a8e326a9SLucas Stach 	 * the maximum rate we can support is half of the module input clock.
219a8e326a9SLucas Stach 	 */
220a8e326a9SLucas Stach 	return clk_round_rate(pltfm_host->clk, UINT_MAX) / 2;
221a8e326a9SLucas Stach }
222a8e326a9SLucas Stach 
223c3c2384cSLucas Stach static void tegra_sdhci_set_tap(struct sdhci_host *host, unsigned int tap)
224c3c2384cSLucas Stach {
225c3c2384cSLucas Stach 	u32 reg;
226c3c2384cSLucas Stach 
227c3c2384cSLucas Stach 	reg = sdhci_readl(host, SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
228c3c2384cSLucas Stach 	reg &= ~SDHCI_CLOCK_CTRL_TAP_MASK;
229c3c2384cSLucas Stach 	reg |= tap << SDHCI_CLOCK_CTRL_TAP_SHIFT;
230c3c2384cSLucas Stach 	sdhci_writel(host, reg, SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
231c3c2384cSLucas Stach }
232c3c2384cSLucas Stach 
233c3c2384cSLucas Stach static int tegra_sdhci_execute_tuning(struct sdhci_host *host, u32 opcode)
234c3c2384cSLucas Stach {
235c3c2384cSLucas Stach 	unsigned int min, max;
236c3c2384cSLucas Stach 
237c3c2384cSLucas Stach 	/*
238c3c2384cSLucas Stach 	 * Start search for minimum tap value at 10, as smaller values are
239c3c2384cSLucas Stach 	 * may wrongly be reported as working but fail at higher speeds,
240c3c2384cSLucas Stach 	 * according to the TRM.
241c3c2384cSLucas Stach 	 */
242c3c2384cSLucas Stach 	min = 10;
243c3c2384cSLucas Stach 	while (min < 255) {
244c3c2384cSLucas Stach 		tegra_sdhci_set_tap(host, min);
245c3c2384cSLucas Stach 		if (!mmc_send_tuning(host->mmc, opcode, NULL))
246c3c2384cSLucas Stach 			break;
247c3c2384cSLucas Stach 		min++;
248c3c2384cSLucas Stach 	}
249c3c2384cSLucas Stach 
250c3c2384cSLucas Stach 	/* Find the maximum tap value that still passes. */
251c3c2384cSLucas Stach 	max = min + 1;
252c3c2384cSLucas Stach 	while (max < 255) {
253c3c2384cSLucas Stach 		tegra_sdhci_set_tap(host, max);
254c3c2384cSLucas Stach 		if (mmc_send_tuning(host->mmc, opcode, NULL)) {
255c3c2384cSLucas Stach 			max--;
256c3c2384cSLucas Stach 			break;
257c3c2384cSLucas Stach 		}
258c3c2384cSLucas Stach 		max++;
259c3c2384cSLucas Stach 	}
260c3c2384cSLucas Stach 
261c3c2384cSLucas Stach 	/* The TRM states the ideal tap value is at 75% in the passing range. */
262c3c2384cSLucas Stach 	tegra_sdhci_set_tap(host, min + ((max - min) * 3 / 4));
263c3c2384cSLucas Stach 
264c3c2384cSLucas Stach 	return mmc_send_tuning(host->mmc, opcode, NULL);
265c3c2384cSLucas Stach }
266c3c2384cSLucas Stach 
267c915568dSLars-Peter Clausen static const struct sdhci_ops tegra_sdhci_ops = {
26885d6509dSShawn Guo 	.get_ro     = tegra_sdhci_get_ro,
26985d6509dSShawn Guo 	.read_w     = tegra_sdhci_readw,
27085d6509dSShawn Guo 	.write_l    = tegra_sdhci_writel,
271a8e326a9SLucas Stach 	.set_clock  = tegra_sdhci_set_clock,
2722317f56cSRussell King 	.set_bus_width = tegra_sdhci_set_bus_width,
27303231f9bSRussell King 	.reset      = tegra_sdhci_reset,
274c3c2384cSLucas Stach 	.platform_execute_tuning = tegra_sdhci_execute_tuning,
275a8e326a9SLucas Stach 	.set_uhs_signaling = tegra_sdhci_set_uhs_signaling,
276a8e326a9SLucas Stach 	.get_max_clock = tegra_sdhci_get_max_clock,
27785d6509dSShawn Guo };
27803d2bfc8SOlof Johansson 
2791db5eebfSLars-Peter Clausen static const struct sdhci_pltfm_data sdhci_tegra20_pdata = {
28085d6509dSShawn Guo 	.quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
28185d6509dSShawn Guo 		  SDHCI_QUIRK_SINGLE_POWER_WRITE |
28285d6509dSShawn Guo 		  SDHCI_QUIRK_NO_HISPD_BIT |
283f9260355SAndrew Bresticker 		  SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
284f9260355SAndrew Bresticker 		  SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
28585d6509dSShawn Guo 	.ops  = &tegra_sdhci_ops,
28685d6509dSShawn Guo };
28785d6509dSShawn Guo 
288d49d19c2SThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra20 = {
2893e44a1a7SStephen Warren 	.pdata = &sdhci_tegra20_pdata,
2903e44a1a7SStephen Warren 	.nvquirks = NVQUIRK_FORCE_SDHCI_SPEC_200 |
2913e44a1a7SStephen Warren 		    NVQUIRK_ENABLE_BLOCK_GAP_DET,
2923e44a1a7SStephen Warren };
2933e44a1a7SStephen Warren 
2941db5eebfSLars-Peter Clausen static const struct sdhci_pltfm_data sdhci_tegra30_pdata = {
2953e44a1a7SStephen Warren 	.quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
2963e44a1a7SStephen Warren 		  SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
2973e44a1a7SStephen Warren 		  SDHCI_QUIRK_SINGLE_POWER_WRITE |
2983e44a1a7SStephen Warren 		  SDHCI_QUIRK_NO_HISPD_BIT |
299f9260355SAndrew Bresticker 		  SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
300f9260355SAndrew Bresticker 		  SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
301a8e326a9SLucas Stach 	.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
3023e44a1a7SStephen Warren 	.ops  = &tegra_sdhci_ops,
3033e44a1a7SStephen Warren };
3043e44a1a7SStephen Warren 
305d49d19c2SThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra30 = {
3063e44a1a7SStephen Warren 	.pdata = &sdhci_tegra30_pdata,
3073145351aSAndrew Bresticker 	.nvquirks = NVQUIRK_ENABLE_SDHCI_SPEC_300 |
3087ad2ed1dSLucas Stach 		    NVQUIRK_ENABLE_SDR50 |
3097ad2ed1dSLucas Stach 		    NVQUIRK_ENABLE_SDR104,
3103e44a1a7SStephen Warren };
3113e44a1a7SStephen Warren 
31201df7ecdSRhyland Klein static const struct sdhci_ops tegra114_sdhci_ops = {
31301df7ecdSRhyland Klein 	.get_ro     = tegra_sdhci_get_ro,
31401df7ecdSRhyland Klein 	.read_w     = tegra_sdhci_readw,
31501df7ecdSRhyland Klein 	.write_w    = tegra_sdhci_writew,
31601df7ecdSRhyland Klein 	.write_l    = tegra_sdhci_writel,
317a8e326a9SLucas Stach 	.set_clock  = tegra_sdhci_set_clock,
31801df7ecdSRhyland Klein 	.set_bus_width = tegra_sdhci_set_bus_width,
31901df7ecdSRhyland Klein 	.reset      = tegra_sdhci_reset,
320c3c2384cSLucas Stach 	.platform_execute_tuning = tegra_sdhci_execute_tuning,
321a8e326a9SLucas Stach 	.set_uhs_signaling = tegra_sdhci_set_uhs_signaling,
322a8e326a9SLucas Stach 	.get_max_clock = tegra_sdhci_get_max_clock,
32301df7ecdSRhyland Klein };
32401df7ecdSRhyland Klein 
3251db5eebfSLars-Peter Clausen static const struct sdhci_pltfm_data sdhci_tegra114_pdata = {
3265ebf2552SRhyland Klein 	.quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
3275ebf2552SRhyland Klein 		  SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
3285ebf2552SRhyland Klein 		  SDHCI_QUIRK_SINGLE_POWER_WRITE |
3295ebf2552SRhyland Klein 		  SDHCI_QUIRK_NO_HISPD_BIT |
330f9260355SAndrew Bresticker 		  SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
331f9260355SAndrew Bresticker 		  SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
332a8e326a9SLucas Stach 	.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
33301df7ecdSRhyland Klein 	.ops  = &tegra114_sdhci_ops,
3345ebf2552SRhyland Klein };
3355ebf2552SRhyland Klein 
336d49d19c2SThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra114 = {
3375ebf2552SRhyland Klein 	.pdata = &sdhci_tegra114_pdata,
3387ad2ed1dSLucas Stach 	.nvquirks = NVQUIRK_ENABLE_SDR50 |
3397ad2ed1dSLucas Stach 		    NVQUIRK_ENABLE_DDR50 |
3407ad2ed1dSLucas Stach 		    NVQUIRK_ENABLE_SDR104,
3415ebf2552SRhyland Klein };
3425ebf2552SRhyland Klein 
343b5a84ecfSThierry Reding static const struct sdhci_pltfm_data sdhci_tegra210_pdata = {
344b5a84ecfSThierry Reding 	.quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
345b5a84ecfSThierry Reding 		  SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
346b5a84ecfSThierry Reding 		  SDHCI_QUIRK_SINGLE_POWER_WRITE |
347b5a84ecfSThierry Reding 		  SDHCI_QUIRK_NO_HISPD_BIT |
348a8e326a9SLucas Stach 		  SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
349a8e326a9SLucas Stach 		  SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
350a8e326a9SLucas Stach 	.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
351b5a84ecfSThierry Reding 	.ops  = &tegra114_sdhci_ops,
352b5a84ecfSThierry Reding };
353b5a84ecfSThierry Reding 
354b5a84ecfSThierry Reding static const struct sdhci_tegra_soc_data soc_data_tegra210 = {
355b5a84ecfSThierry Reding 	.pdata = &sdhci_tegra210_pdata,
356b5a84ecfSThierry Reding };
357b5a84ecfSThierry Reding 
358498d83e7SBill Pemberton static const struct of_device_id sdhci_tegra_dt_match[] = {
359b5a84ecfSThierry Reding 	{ .compatible = "nvidia,tegra210-sdhci", .data = &soc_data_tegra210 },
36067debea3SStephen Warren 	{ .compatible = "nvidia,tegra124-sdhci", .data = &soc_data_tegra114 },
3615ebf2552SRhyland Klein 	{ .compatible = "nvidia,tegra114-sdhci", .data = &soc_data_tegra114 },
3623e44a1a7SStephen Warren 	{ .compatible = "nvidia,tegra30-sdhci", .data = &soc_data_tegra30 },
3633e44a1a7SStephen Warren 	{ .compatible = "nvidia,tegra20-sdhci", .data = &soc_data_tegra20 },
364275173b2SGrant Likely 	{}
365275173b2SGrant Likely };
366e4404fabSArnd Bergmann MODULE_DEVICE_TABLE(of, sdhci_tegra_dt_match);
367275173b2SGrant Likely 
368c3be1efdSBill Pemberton static int sdhci_tegra_probe(struct platform_device *pdev)
36903d2bfc8SOlof Johansson {
3703e44a1a7SStephen Warren 	const struct of_device_id *match;
3713e44a1a7SStephen Warren 	const struct sdhci_tegra_soc_data *soc_data;
3723e44a1a7SStephen Warren 	struct sdhci_host *host;
37385d6509dSShawn Guo 	struct sdhci_pltfm_host *pltfm_host;
3743e44a1a7SStephen Warren 	struct sdhci_tegra *tegra_host;
37503d2bfc8SOlof Johansson 	struct clk *clk;
37603d2bfc8SOlof Johansson 	int rc;
37703d2bfc8SOlof Johansson 
3783e44a1a7SStephen Warren 	match = of_match_device(sdhci_tegra_dt_match, &pdev->dev);
379b37f9d98SJoseph Lo 	if (!match)
380b37f9d98SJoseph Lo 		return -EINVAL;
3813e44a1a7SStephen Warren 	soc_data = match->data;
3823e44a1a7SStephen Warren 
3830e748234SChristian Daudt 	host = sdhci_pltfm_init(pdev, soc_data->pdata, 0);
38485d6509dSShawn Guo 	if (IS_ERR(host))
38585d6509dSShawn Guo 		return PTR_ERR(host);
38685d6509dSShawn Guo 	pltfm_host = sdhci_priv(host);
38785d6509dSShawn Guo 
3883e44a1a7SStephen Warren 	tegra_host = devm_kzalloc(&pdev->dev, sizeof(*tegra_host), GFP_KERNEL);
3893e44a1a7SStephen Warren 	if (!tegra_host) {
3903e44a1a7SStephen Warren 		dev_err(mmc_dev(host->mmc), "failed to allocate tegra_host\n");
3913e44a1a7SStephen Warren 		rc = -ENOMEM;
3920e786102SStephen Warren 		goto err_alloc_tegra_host;
3933e44a1a7SStephen Warren 	}
394a8e326a9SLucas Stach 	tegra_host->ddr_signaling = false;
3953e44a1a7SStephen Warren 	tegra_host->soc_data = soc_data;
3963e44a1a7SStephen Warren 	pltfm_host->priv = tegra_host;
397275173b2SGrant Likely 
3982391b340SMylene JOSSERAND 	rc = mmc_of_parse(host->mmc);
39947caa84fSSimon Baatz 	if (rc)
40047caa84fSSimon Baatz 		goto err_parse_dt;
4010e786102SStephen Warren 
4027ad2ed1dSLucas Stach 	if (tegra_host->soc_data->nvquirks & NVQUIRK_ENABLE_DDR50)
403c3c2384cSLucas Stach 		host->mmc->caps |= MMC_CAP_1_8V_DDR;
404c3c2384cSLucas Stach 
4052391b340SMylene JOSSERAND 	tegra_host->power_gpio = devm_gpiod_get_optional(&pdev->dev, "power",
4062391b340SMylene JOSSERAND 							 GPIOD_OUT_HIGH);
4072391b340SMylene JOSSERAND 	if (IS_ERR(tegra_host->power_gpio)) {
4082391b340SMylene JOSSERAND 		rc = PTR_ERR(tegra_host->power_gpio);
40985d6509dSShawn Guo 		goto err_power_req;
41003d2bfc8SOlof Johansson 	}
41103d2bfc8SOlof Johansson 
412e4f79d9cSKevin Hao 	clk = devm_clk_get(mmc_dev(host->mmc), NULL);
41303d2bfc8SOlof Johansson 	if (IS_ERR(clk)) {
41403d2bfc8SOlof Johansson 		dev_err(mmc_dev(host->mmc), "clk err\n");
41503d2bfc8SOlof Johansson 		rc = PTR_ERR(clk);
41685d6509dSShawn Guo 		goto err_clk_get;
41703d2bfc8SOlof Johansson 	}
4181e674bc6SPrashant Gaikwad 	clk_prepare_enable(clk);
41903d2bfc8SOlof Johansson 	pltfm_host->clk = clk;
42003d2bfc8SOlof Johansson 
42185d6509dSShawn Guo 	rc = sdhci_add_host(host);
42285d6509dSShawn Guo 	if (rc)
42385d6509dSShawn Guo 		goto err_add_host;
42485d6509dSShawn Guo 
42503d2bfc8SOlof Johansson 	return 0;
42603d2bfc8SOlof Johansson 
42785d6509dSShawn Guo err_add_host:
4281e674bc6SPrashant Gaikwad 	clk_disable_unprepare(pltfm_host->clk);
42985d6509dSShawn Guo err_clk_get:
43085d6509dSShawn Guo err_power_req:
43147caa84fSSimon Baatz err_parse_dt:
4320e786102SStephen Warren err_alloc_tegra_host:
43385d6509dSShawn Guo 	sdhci_pltfm_free(pdev);
43403d2bfc8SOlof Johansson 	return rc;
43503d2bfc8SOlof Johansson }
43603d2bfc8SOlof Johansson 
43785d6509dSShawn Guo static struct platform_driver sdhci_tegra_driver = {
43885d6509dSShawn Guo 	.driver		= {
43985d6509dSShawn Guo 		.name	= "sdhci-tegra",
440275173b2SGrant Likely 		.of_match_table = sdhci_tegra_dt_match,
44129495aa0SManuel Lauss 		.pm	= SDHCI_PLTFM_PMOPS,
44285d6509dSShawn Guo 	},
44385d6509dSShawn Guo 	.probe		= sdhci_tegra_probe,
444caebcae9SKevin Hao 	.remove		= sdhci_pltfm_unregister,
44503d2bfc8SOlof Johansson };
44603d2bfc8SOlof Johansson 
447d1f81a64SAxel Lin module_platform_driver(sdhci_tegra_driver);
44885d6509dSShawn Guo 
44985d6509dSShawn Guo MODULE_DESCRIPTION("SDHCI driver for Tegra");
45085d6509dSShawn Guo MODULE_AUTHOR("Google, Inc.");
45185d6509dSShawn Guo MODULE_LICENSE("GPL v2");
452