blob: 20ce81b57d320cff3f9db614f03ac1b6b13f9cdb [file] [log] [blame]
Olof Johansson03d2bfc2011-01-01 23:52:56 -05001/*
2 * Copyright (C) 2010 Google, Inc.
3 *
4 * This software is licensed under the terms of the GNU General Public
5 * License version 2, as published by the Free Software Foundation, and
6 * may be copied, distributed, and modified under those terms.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 */
14
15#include <linux/err.h>
Paul Gortmaker96547f52011-07-03 15:15:51 -040016#include <linux/module.h>
Olof Johansson03d2bfc2011-01-01 23:52:56 -050017#include <linux/init.h>
18#include <linux/platform_device.h>
19#include <linux/clk.h>
20#include <linux/io.h>
Stephen Warren55cd65e2011-08-30 13:17:16 -060021#include <linux/of.h>
Stephen Warren3e44a1a2012-02-01 16:30:55 -070022#include <linux/of_device.h>
Olof Johansson03d2bfc2011-01-01 23:52:56 -050023#include <linux/mmc/card.h>
24#include <linux/mmc/host.h>
Joseph Lo0aacd232013-03-11 14:44:11 -060025#include <linux/mmc/slot-gpio.h>
Mylene JOSSERAND2391b342015-03-30 23:39:25 +020026#include <linux/gpio/consumer.h>
Olof Johansson03d2bfc2011-01-01 23:52:56 -050027
Olof Johansson03d2bfc2011-01-01 23:52:56 -050028#include "sdhci-pltfm.h"
29
Pavan Kunapulica5879d2012-04-18 18:48:02 +053030/* Tegra SDHOST controller vendor register definitions */
Lucas Stach74cd42b2015-12-22 19:41:01 +010031#define SDHCI_TEGRA_VENDOR_CLOCK_CTRL 0x100
32#define SDHCI_CLOCK_CTRL_PADPIPE_CLKEN_OVERRIDE BIT(3)
33#define SDHCI_CLOCK_CTRL_SPI_MODE_CLKEN_OVERRIDE BIT(2)
34
Pavan Kunapulica5879d2012-04-18 18:48:02 +053035#define SDHCI_TEGRA_VENDOR_MISC_CTRL 0x120
Andrew Bresticker31453512014-05-22 08:55:35 -070036#define SDHCI_MISC_CTRL_ENABLE_SDR104 0x8
37#define SDHCI_MISC_CTRL_ENABLE_SDR50 0x10
Pavan Kunapulica5879d2012-04-18 18:48:02 +053038#define SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300 0x20
Andrew Bresticker31453512014-05-22 08:55:35 -070039#define SDHCI_MISC_CTRL_ENABLE_DDR50 0x200
Pavan Kunapulica5879d2012-04-18 18:48:02 +053040
Stephen Warren3e44a1a2012-02-01 16:30:55 -070041#define NVQUIRK_FORCE_SDHCI_SPEC_200 BIT(0)
42#define NVQUIRK_ENABLE_BLOCK_GAP_DET BIT(1)
Pavan Kunapulica5879d2012-04-18 18:48:02 +053043#define NVQUIRK_ENABLE_SDHCI_SPEC_300 BIT(2)
Andrew Bresticker31453512014-05-22 08:55:35 -070044#define NVQUIRK_DISABLE_SDR50 BIT(3)
45#define NVQUIRK_DISABLE_SDR104 BIT(4)
46#define NVQUIRK_DISABLE_DDR50 BIT(5)
Stephen Warren3e44a1a2012-02-01 16:30:55 -070047
48struct sdhci_tegra_soc_data {
Lars-Peter Clausen1db5eeb2013-03-13 19:26:03 +010049 const struct sdhci_pltfm_data *pdata;
Stephen Warren3e44a1a2012-02-01 16:30:55 -070050 u32 nvquirks;
51};
52
53struct sdhci_tegra {
Stephen Warren3e44a1a2012-02-01 16:30:55 -070054 const struct sdhci_tegra_soc_data *soc_data;
Mylene JOSSERAND2391b342015-03-30 23:39:25 +020055 struct gpio_desc *power_gpio;
Lucas Stacha8e326a2015-12-22 19:41:00 +010056 bool ddr_signaling;
Stephen Warren3e44a1a2012-02-01 16:30:55 -070057};
58
Olof Johansson03d2bfc2011-01-01 23:52:56 -050059static u16 tegra_sdhci_readw(struct sdhci_host *host, int reg)
60{
Stephen Warren3e44a1a2012-02-01 16:30:55 -070061 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
62 struct sdhci_tegra *tegra_host = pltfm_host->priv;
63 const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
64
65 if (unlikely((soc_data->nvquirks & NVQUIRK_FORCE_SDHCI_SPEC_200) &&
66 (reg == SDHCI_HOST_VERSION))) {
Olof Johansson03d2bfc2011-01-01 23:52:56 -050067 /* Erratum: Version register is invalid in HW. */
68 return SDHCI_SPEC_200;
69 }
70
71 return readw(host->ioaddr + reg);
72}
73
Pavan Kunapuli352ee862015-01-28 11:45:16 -050074static void tegra_sdhci_writew(struct sdhci_host *host, u16 val, int reg)
75{
76 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
Pavan Kunapuli352ee862015-01-28 11:45:16 -050077
Rhyland Klein01df7ec2015-02-11 12:55:51 -050078 switch (reg) {
79 case SDHCI_TRANSFER_MODE:
80 /*
81 * Postpone this write, we must do it together with a
82 * command write that is down below.
83 */
84 pltfm_host->xfer_mode_shadow = val;
85 return;
86 case SDHCI_COMMAND:
87 writel((val << 16) | pltfm_host->xfer_mode_shadow,
88 host->ioaddr + SDHCI_TRANSFER_MODE);
89 return;
Pavan Kunapuli352ee862015-01-28 11:45:16 -050090 }
91
92 writew(val, host->ioaddr + reg);
93}
94
Olof Johansson03d2bfc2011-01-01 23:52:56 -050095static void tegra_sdhci_writel(struct sdhci_host *host, u32 val, int reg)
96{
Stephen Warren3e44a1a2012-02-01 16:30:55 -070097 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
98 struct sdhci_tegra *tegra_host = pltfm_host->priv;
99 const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
100
Olof Johansson03d2bfc2011-01-01 23:52:56 -0500101 /* Seems like we're getting spurious timeout and crc errors, so
102 * disable signalling of them. In case of real errors software
103 * timers should take care of eventually detecting them.
104 */
105 if (unlikely(reg == SDHCI_SIGNAL_ENABLE))
106 val &= ~(SDHCI_INT_TIMEOUT|SDHCI_INT_CRC);
107
108 writel(val, host->ioaddr + reg);
109
Stephen Warren3e44a1a2012-02-01 16:30:55 -0700110 if (unlikely((soc_data->nvquirks & NVQUIRK_ENABLE_BLOCK_GAP_DET) &&
111 (reg == SDHCI_INT_ENABLE))) {
Olof Johansson03d2bfc2011-01-01 23:52:56 -0500112 /* Erratum: Must enable block gap interrupt detection */
113 u8 gap_ctrl = readb(host->ioaddr + SDHCI_BLOCK_GAP_CONTROL);
114 if (val & SDHCI_INT_CARD_INT)
115 gap_ctrl |= 0x8;
116 else
117 gap_ctrl &= ~0x8;
118 writeb(gap_ctrl, host->ioaddr + SDHCI_BLOCK_GAP_CONTROL);
119 }
120}
121
Stephen Warren3e44a1a2012-02-01 16:30:55 -0700122static unsigned int tegra_sdhci_get_ro(struct sdhci_host *host)
Olof Johansson03d2bfc2011-01-01 23:52:56 -0500123{
Joseph Lo0aacd232013-03-11 14:44:11 -0600124 return mmc_gpio_get_ro(host->mmc);
Olof Johansson03d2bfc2011-01-01 23:52:56 -0500125}
126
Russell King03231f92014-04-25 12:57:12 +0100127static void tegra_sdhci_reset(struct sdhci_host *host, u8 mask)
Pavan Kunapulica5879d2012-04-18 18:48:02 +0530128{
129 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
130 struct sdhci_tegra *tegra_host = pltfm_host->priv;
131 const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
Lucas Stach74cd42b2015-12-22 19:41:01 +0100132 u32 misc_ctrl, clk_ctrl;
Pavan Kunapulica5879d2012-04-18 18:48:02 +0530133
Russell King03231f92014-04-25 12:57:12 +0100134 sdhci_reset(host, mask);
135
Pavan Kunapulica5879d2012-04-18 18:48:02 +0530136 if (!(mask & SDHCI_RESET_ALL))
137 return;
138
Andrew Bresticker31453512014-05-22 08:55:35 -0700139 misc_ctrl = sdhci_readw(host, SDHCI_TEGRA_VENDOR_MISC_CTRL);
Pavan Kunapulica5879d2012-04-18 18:48:02 +0530140 /* Erratum: Enable SDHCI spec v3.00 support */
Andrew Bresticker31453512014-05-22 08:55:35 -0700141 if (soc_data->nvquirks & NVQUIRK_ENABLE_SDHCI_SPEC_300)
Pavan Kunapulica5879d2012-04-18 18:48:02 +0530142 misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300;
Andrew Bresticker31453512014-05-22 08:55:35 -0700143 /* Don't advertise UHS modes which aren't supported yet */
144 if (soc_data->nvquirks & NVQUIRK_DISABLE_SDR50)
145 misc_ctrl &= ~SDHCI_MISC_CTRL_ENABLE_SDR50;
146 if (soc_data->nvquirks & NVQUIRK_DISABLE_DDR50)
147 misc_ctrl &= ~SDHCI_MISC_CTRL_ENABLE_DDR50;
148 if (soc_data->nvquirks & NVQUIRK_DISABLE_SDR104)
149 misc_ctrl &= ~SDHCI_MISC_CTRL_ENABLE_SDR104;
150 sdhci_writew(host, misc_ctrl, SDHCI_TEGRA_VENDOR_MISC_CTRL);
Lucas Stacha8e326a2015-12-22 19:41:00 +0100151
Lucas Stach74cd42b2015-12-22 19:41:01 +0100152 clk_ctrl = sdhci_readl(host, SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
153 clk_ctrl &= ~SDHCI_CLOCK_CTRL_SPI_MODE_CLKEN_OVERRIDE;
154 sdhci_writel(host, clk_ctrl, SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
155
Lucas Stacha8e326a2015-12-22 19:41:00 +0100156 tegra_host->ddr_signaling = false;
Pavan Kunapulica5879d2012-04-18 18:48:02 +0530157}
158
Russell King2317f562014-04-25 12:57:07 +0100159static void tegra_sdhci_set_bus_width(struct sdhci_host *host, int bus_width)
Olof Johansson03d2bfc2011-01-01 23:52:56 -0500160{
Olof Johansson03d2bfc2011-01-01 23:52:56 -0500161 u32 ctrl;
162
Olof Johansson03d2bfc2011-01-01 23:52:56 -0500163 ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
Joseph Lo0aacd232013-03-11 14:44:11 -0600164 if ((host->mmc->caps & MMC_CAP_8_BIT_DATA) &&
165 (bus_width == MMC_BUS_WIDTH_8)) {
Olof Johansson03d2bfc2011-01-01 23:52:56 -0500166 ctrl &= ~SDHCI_CTRL_4BITBUS;
167 ctrl |= SDHCI_CTRL_8BITBUS;
168 } else {
169 ctrl &= ~SDHCI_CTRL_8BITBUS;
170 if (bus_width == MMC_BUS_WIDTH_4)
171 ctrl |= SDHCI_CTRL_4BITBUS;
172 else
173 ctrl &= ~SDHCI_CTRL_4BITBUS;
174 }
175 sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
Olof Johansson03d2bfc2011-01-01 23:52:56 -0500176}
177
Lucas Stacha8e326a2015-12-22 19:41:00 +0100178static void tegra_sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
179{
180 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
181 struct sdhci_tegra *tegra_host = pltfm_host->priv;
182 unsigned long host_clk;
183
184 if (!clock)
185 return;
186
187 host_clk = tegra_host->ddr_signaling ? clock * 2 : clock;
188 clk_set_rate(pltfm_host->clk, host_clk);
189 host->max_clk = clk_get_rate(pltfm_host->clk);
190
191 return sdhci_set_clock(host, clock);
192}
193
194static void tegra_sdhci_set_uhs_signaling(struct sdhci_host *host,
195 unsigned timing)
196{
197 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
198 struct sdhci_tegra *tegra_host = pltfm_host->priv;
199
200 if (timing == MMC_TIMING_UHS_DDR50)
201 tegra_host->ddr_signaling = true;
202
203 return sdhci_set_uhs_signaling(host, timing);
204}
205
206static unsigned int tegra_sdhci_get_max_clock(struct sdhci_host *host)
207{
208 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
209
210 /*
211 * DDR modes require the host to run at double the card frequency, so
212 * the maximum rate we can support is half of the module input clock.
213 */
214 return clk_round_rate(pltfm_host->clk, UINT_MAX) / 2;
215}
216
Lars-Peter Clausenc9155682013-03-13 19:26:05 +0100217static const struct sdhci_ops tegra_sdhci_ops = {
Shawn Guo85d65092011-05-27 23:48:12 +0800218 .get_ro = tegra_sdhci_get_ro,
Shawn Guo85d65092011-05-27 23:48:12 +0800219 .read_w = tegra_sdhci_readw,
220 .write_l = tegra_sdhci_writel,
Lucas Stacha8e326a2015-12-22 19:41:00 +0100221 .set_clock = tegra_sdhci_set_clock,
Russell King2317f562014-04-25 12:57:07 +0100222 .set_bus_width = tegra_sdhci_set_bus_width,
Russell King03231f92014-04-25 12:57:12 +0100223 .reset = tegra_sdhci_reset,
Lucas Stacha8e326a2015-12-22 19:41:00 +0100224 .set_uhs_signaling = tegra_sdhci_set_uhs_signaling,
225 .get_max_clock = tegra_sdhci_get_max_clock,
Shawn Guo85d65092011-05-27 23:48:12 +0800226};
Olof Johansson03d2bfc2011-01-01 23:52:56 -0500227
Lars-Peter Clausen1db5eeb2013-03-13 19:26:03 +0100228static const struct sdhci_pltfm_data sdhci_tegra20_pdata = {
Shawn Guo85d65092011-05-27 23:48:12 +0800229 .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
230 SDHCI_QUIRK_SINGLE_POWER_WRITE |
231 SDHCI_QUIRK_NO_HISPD_BIT |
Andrew Brestickerf9260352014-05-22 08:55:36 -0700232 SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
233 SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
Shawn Guo85d65092011-05-27 23:48:12 +0800234 .ops = &tegra_sdhci_ops,
235};
236
Thierry Redingd49d19c22015-11-16 10:27:14 +0100237static const struct sdhci_tegra_soc_data soc_data_tegra20 = {
Stephen Warren3e44a1a2012-02-01 16:30:55 -0700238 .pdata = &sdhci_tegra20_pdata,
239 .nvquirks = NVQUIRK_FORCE_SDHCI_SPEC_200 |
240 NVQUIRK_ENABLE_BLOCK_GAP_DET,
241};
Stephen Warren3e44a1a2012-02-01 16:30:55 -0700242
Lars-Peter Clausen1db5eeb2013-03-13 19:26:03 +0100243static const struct sdhci_pltfm_data sdhci_tegra30_pdata = {
Stephen Warren3e44a1a2012-02-01 16:30:55 -0700244 .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
245 SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
246 SDHCI_QUIRK_SINGLE_POWER_WRITE |
247 SDHCI_QUIRK_NO_HISPD_BIT |
Andrew Brestickerf9260352014-05-22 08:55:36 -0700248 SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
249 SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
Lucas Stacha8e326a2015-12-22 19:41:00 +0100250 .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
Stephen Warren3e44a1a2012-02-01 16:30:55 -0700251 .ops = &tegra_sdhci_ops,
252};
253
Thierry Redingd49d19c22015-11-16 10:27:14 +0100254static const struct sdhci_tegra_soc_data soc_data_tegra30 = {
Stephen Warren3e44a1a2012-02-01 16:30:55 -0700255 .pdata = &sdhci_tegra30_pdata,
Andrew Bresticker31453512014-05-22 08:55:35 -0700256 .nvquirks = NVQUIRK_ENABLE_SDHCI_SPEC_300 |
257 NVQUIRK_DISABLE_SDR50 |
258 NVQUIRK_DISABLE_SDR104,
Stephen Warren3e44a1a2012-02-01 16:30:55 -0700259};
Stephen Warren3e44a1a2012-02-01 16:30:55 -0700260
Rhyland Klein01df7ec2015-02-11 12:55:51 -0500261static const struct sdhci_ops tegra114_sdhci_ops = {
262 .get_ro = tegra_sdhci_get_ro,
263 .read_w = tegra_sdhci_readw,
264 .write_w = tegra_sdhci_writew,
265 .write_l = tegra_sdhci_writel,
Lucas Stacha8e326a2015-12-22 19:41:00 +0100266 .set_clock = tegra_sdhci_set_clock,
Rhyland Klein01df7ec2015-02-11 12:55:51 -0500267 .set_bus_width = tegra_sdhci_set_bus_width,
268 .reset = tegra_sdhci_reset,
Lucas Stacha8e326a2015-12-22 19:41:00 +0100269 .set_uhs_signaling = tegra_sdhci_set_uhs_signaling,
270 .get_max_clock = tegra_sdhci_get_max_clock,
Rhyland Klein01df7ec2015-02-11 12:55:51 -0500271};
272
Lars-Peter Clausen1db5eeb2013-03-13 19:26:03 +0100273static const struct sdhci_pltfm_data sdhci_tegra114_pdata = {
Rhyland Klein5ebf2552013-02-20 13:35:17 -0500274 .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
275 SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
276 SDHCI_QUIRK_SINGLE_POWER_WRITE |
277 SDHCI_QUIRK_NO_HISPD_BIT |
Andrew Brestickerf9260352014-05-22 08:55:36 -0700278 SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
279 SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
Lucas Stacha8e326a2015-12-22 19:41:00 +0100280 .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
Rhyland Klein01df7ec2015-02-11 12:55:51 -0500281 .ops = &tegra114_sdhci_ops,
Rhyland Klein5ebf2552013-02-20 13:35:17 -0500282};
283
Thierry Redingd49d19c22015-11-16 10:27:14 +0100284static const struct sdhci_tegra_soc_data soc_data_tegra114 = {
Rhyland Klein5ebf2552013-02-20 13:35:17 -0500285 .pdata = &sdhci_tegra114_pdata,
Andrew Bresticker31453512014-05-22 08:55:35 -0700286 .nvquirks = NVQUIRK_DISABLE_SDR50 |
287 NVQUIRK_DISABLE_DDR50 |
Rhyland Klein01df7ec2015-02-11 12:55:51 -0500288 NVQUIRK_DISABLE_SDR104,
Rhyland Klein5ebf2552013-02-20 13:35:17 -0500289};
290
Thierry Redingb5a84ec2015-11-16 10:27:15 +0100291static const struct sdhci_pltfm_data sdhci_tegra210_pdata = {
292 .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
293 SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
294 SDHCI_QUIRK_SINGLE_POWER_WRITE |
295 SDHCI_QUIRK_NO_HISPD_BIT |
Lucas Stacha8e326a2015-12-22 19:41:00 +0100296 SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
297 SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
298 .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
Thierry Redingb5a84ec2015-11-16 10:27:15 +0100299 .ops = &tegra114_sdhci_ops,
300};
301
302static const struct sdhci_tegra_soc_data soc_data_tegra210 = {
303 .pdata = &sdhci_tegra210_pdata,
304 .nvquirks = NVQUIRK_DISABLE_SDR50 |
305 NVQUIRK_DISABLE_DDR50 |
306 NVQUIRK_DISABLE_SDR104,
307};
308
Bill Pemberton498d83e2012-11-19 13:24:22 -0500309static const struct of_device_id sdhci_tegra_dt_match[] = {
Thierry Redingb5a84ec2015-11-16 10:27:15 +0100310 { .compatible = "nvidia,tegra210-sdhci", .data = &soc_data_tegra210 },
Stephen Warren67debea2014-01-06 11:17:47 -0700311 { .compatible = "nvidia,tegra124-sdhci", .data = &soc_data_tegra114 },
Rhyland Klein5ebf2552013-02-20 13:35:17 -0500312 { .compatible = "nvidia,tegra114-sdhci", .data = &soc_data_tegra114 },
Stephen Warren3e44a1a2012-02-01 16:30:55 -0700313 { .compatible = "nvidia,tegra30-sdhci", .data = &soc_data_tegra30 },
Stephen Warren3e44a1a2012-02-01 16:30:55 -0700314 { .compatible = "nvidia,tegra20-sdhci", .data = &soc_data_tegra20 },
Grant Likely275173b2011-08-23 12:15:33 -0600315 {}
316};
Arnd Bergmanne4404fa2013-04-23 15:05:57 -0400317MODULE_DEVICE_TABLE(of, sdhci_tegra_dt_match);
Grant Likely275173b2011-08-23 12:15:33 -0600318
Bill Pembertonc3be1ef2012-11-19 13:23:06 -0500319static int sdhci_tegra_probe(struct platform_device *pdev)
Olof Johansson03d2bfc2011-01-01 23:52:56 -0500320{
Stephen Warren3e44a1a2012-02-01 16:30:55 -0700321 const struct of_device_id *match;
322 const struct sdhci_tegra_soc_data *soc_data;
323 struct sdhci_host *host;
Shawn Guo85d65092011-05-27 23:48:12 +0800324 struct sdhci_pltfm_host *pltfm_host;
Stephen Warren3e44a1a2012-02-01 16:30:55 -0700325 struct sdhci_tegra *tegra_host;
Olof Johansson03d2bfc2011-01-01 23:52:56 -0500326 struct clk *clk;
327 int rc;
328
Stephen Warren3e44a1a2012-02-01 16:30:55 -0700329 match = of_match_device(sdhci_tegra_dt_match, &pdev->dev);
Joseph Lob37f9d92012-08-17 15:04:31 +0800330 if (!match)
331 return -EINVAL;
332 soc_data = match->data;
Stephen Warren3e44a1a2012-02-01 16:30:55 -0700333
Christian Daudt0e748232013-05-29 13:50:05 -0700334 host = sdhci_pltfm_init(pdev, soc_data->pdata, 0);
Shawn Guo85d65092011-05-27 23:48:12 +0800335 if (IS_ERR(host))
336 return PTR_ERR(host);
Shawn Guo85d65092011-05-27 23:48:12 +0800337 pltfm_host = sdhci_priv(host);
338
Stephen Warren3e44a1a2012-02-01 16:30:55 -0700339 tegra_host = devm_kzalloc(&pdev->dev, sizeof(*tegra_host), GFP_KERNEL);
340 if (!tegra_host) {
341 dev_err(mmc_dev(host->mmc), "failed to allocate tegra_host\n");
342 rc = -ENOMEM;
Stephen Warren0e786102013-02-15 15:07:19 -0700343 goto err_alloc_tegra_host;
Stephen Warren3e44a1a2012-02-01 16:30:55 -0700344 }
Lucas Stacha8e326a2015-12-22 19:41:00 +0100345 tegra_host->ddr_signaling = false;
Stephen Warren3e44a1a2012-02-01 16:30:55 -0700346 tegra_host->soc_data = soc_data;
Stephen Warren3e44a1a2012-02-01 16:30:55 -0700347 pltfm_host->priv = tegra_host;
Grant Likely275173b2011-08-23 12:15:33 -0600348
Mylene JOSSERAND2391b342015-03-30 23:39:25 +0200349 rc = mmc_of_parse(host->mmc);
Simon Baatz47caa842013-06-09 22:14:16 +0200350 if (rc)
351 goto err_parse_dt;
Stephen Warren0e786102013-02-15 15:07:19 -0700352
Mylene JOSSERAND2391b342015-03-30 23:39:25 +0200353 tegra_host->power_gpio = devm_gpiod_get_optional(&pdev->dev, "power",
354 GPIOD_OUT_HIGH);
355 if (IS_ERR(tegra_host->power_gpio)) {
356 rc = PTR_ERR(tegra_host->power_gpio);
357 goto err_power_req;
Olof Johansson03d2bfc2011-01-01 23:52:56 -0500358 }
359
Kevin Haoe4f79d92015-02-27 15:47:27 +0800360 clk = devm_clk_get(mmc_dev(host->mmc), NULL);
Olof Johansson03d2bfc2011-01-01 23:52:56 -0500361 if (IS_ERR(clk)) {
362 dev_err(mmc_dev(host->mmc), "clk err\n");
363 rc = PTR_ERR(clk);
Shawn Guo85d65092011-05-27 23:48:12 +0800364 goto err_clk_get;
Olof Johansson03d2bfc2011-01-01 23:52:56 -0500365 }
Prashant Gaikwad1e674bc2012-06-05 09:59:37 +0530366 clk_prepare_enable(clk);
Olof Johansson03d2bfc2011-01-01 23:52:56 -0500367 pltfm_host->clk = clk;
368
Shawn Guo85d65092011-05-27 23:48:12 +0800369 rc = sdhci_add_host(host);
370 if (rc)
371 goto err_add_host;
372
Olof Johansson03d2bfc2011-01-01 23:52:56 -0500373 return 0;
374
Shawn Guo85d65092011-05-27 23:48:12 +0800375err_add_host:
Prashant Gaikwad1e674bc2012-06-05 09:59:37 +0530376 clk_disable_unprepare(pltfm_host->clk);
Shawn Guo85d65092011-05-27 23:48:12 +0800377err_clk_get:
Shawn Guo85d65092011-05-27 23:48:12 +0800378err_power_req:
Simon Baatz47caa842013-06-09 22:14:16 +0200379err_parse_dt:
Stephen Warren0e786102013-02-15 15:07:19 -0700380err_alloc_tegra_host:
Shawn Guo85d65092011-05-27 23:48:12 +0800381 sdhci_pltfm_free(pdev);
Olof Johansson03d2bfc2011-01-01 23:52:56 -0500382 return rc;
383}
384
Shawn Guo85d65092011-05-27 23:48:12 +0800385static struct platform_driver sdhci_tegra_driver = {
386 .driver = {
387 .name = "sdhci-tegra",
Grant Likely275173b2011-08-23 12:15:33 -0600388 .of_match_table = sdhci_tegra_dt_match,
Manuel Lauss29495aa2011-11-03 11:09:45 +0100389 .pm = SDHCI_PLTFM_PMOPS,
Shawn Guo85d65092011-05-27 23:48:12 +0800390 },
391 .probe = sdhci_tegra_probe,
Kevin Haocaebcae2015-02-27 15:47:31 +0800392 .remove = sdhci_pltfm_unregister,
Olof Johansson03d2bfc2011-01-01 23:52:56 -0500393};
394
Axel Lind1f81a62011-11-26 12:55:43 +0800395module_platform_driver(sdhci_tegra_driver);
Shawn Guo85d65092011-05-27 23:48:12 +0800396
397MODULE_DESCRIPTION("SDHCI driver for Tegra");
Stephen Warren3e44a1a2012-02-01 16:30:55 -0700398MODULE_AUTHOR("Google, Inc.");
Shawn Guo85d65092011-05-27 23:48:12 +0800399MODULE_LICENSE("GPL v2");