Commit | Line | Data |
---|---|---|
03d2bfc8 OJ |
1 | /* |
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 | ||
e5c63d91 | 15 | #include <linux/delay.h> |
03d2bfc8 | 16 | #include <linux/err.h> |
96547f5d | 17 | #include <linux/module.h> |
03d2bfc8 OJ |
18 | #include <linux/init.h> |
19 | #include <linux/platform_device.h> | |
20 | #include <linux/clk.h> | |
21 | #include <linux/io.h> | |
55cd65e4 | 22 | #include <linux/of.h> |
3e44a1a7 | 23 | #include <linux/of_device.h> |
03d2bfc8 OJ |
24 | #include <linux/mmc/card.h> |
25 | #include <linux/mmc/host.h> | |
c3c2384c | 26 | #include <linux/mmc/mmc.h> |
0aacd23f | 27 | #include <linux/mmc/slot-gpio.h> |
2391b340 | 28 | #include <linux/gpio/consumer.h> |
03d2bfc8 | 29 | |
03d2bfc8 OJ |
30 | #include "sdhci-pltfm.h" |
31 | ||
ca5879d3 | 32 | /* Tegra SDHOST controller vendor register definitions */ |
74cd42bc | 33 | #define SDHCI_TEGRA_VENDOR_CLOCK_CTRL 0x100 |
c3c2384c LS |
34 | #define SDHCI_CLOCK_CTRL_TAP_MASK 0x00ff0000 |
35 | #define SDHCI_CLOCK_CTRL_TAP_SHIFT 16 | |
36 | #define SDHCI_CLOCK_CTRL_SDR50_TUNING_OVERRIDE BIT(5) | |
74cd42bc LS |
37 | #define SDHCI_CLOCK_CTRL_PADPIPE_CLKEN_OVERRIDE BIT(3) |
38 | #define SDHCI_CLOCK_CTRL_SPI_MODE_CLKEN_OVERRIDE BIT(2) | |
39 | ||
ca5879d3 | 40 | #define SDHCI_TEGRA_VENDOR_MISC_CTRL 0x120 |
3145351a AB |
41 | #define SDHCI_MISC_CTRL_ENABLE_SDR104 0x8 |
42 | #define SDHCI_MISC_CTRL_ENABLE_SDR50 0x10 | |
ca5879d3 | 43 | #define SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300 0x20 |
3145351a | 44 | #define SDHCI_MISC_CTRL_ENABLE_DDR50 0x200 |
ca5879d3 | 45 | |
e5c63d91 LS |
46 | #define SDHCI_TEGRA_AUTO_CAL_CONFIG 0x1e4 |
47 | #define SDHCI_AUTO_CAL_START BIT(31) | |
48 | #define SDHCI_AUTO_CAL_ENABLE BIT(29) | |
49 | ||
3e44a1a7 SW |
50 | #define NVQUIRK_FORCE_SDHCI_SPEC_200 BIT(0) |
51 | #define NVQUIRK_ENABLE_BLOCK_GAP_DET BIT(1) | |
ca5879d3 | 52 | #define NVQUIRK_ENABLE_SDHCI_SPEC_300 BIT(2) |
7ad2ed1d LS |
53 | #define NVQUIRK_ENABLE_SDR50 BIT(3) |
54 | #define NVQUIRK_ENABLE_SDR104 BIT(4) | |
55 | #define NVQUIRK_ENABLE_DDR50 BIT(5) | |
e5c63d91 | 56 | #define NVQUIRK_HAS_PADCALIB BIT(6) |
3e44a1a7 SW |
57 | |
58 | struct sdhci_tegra_soc_data { | |
1db5eebf | 59 | const struct sdhci_pltfm_data *pdata; |
3e44a1a7 SW |
60 | u32 nvquirks; |
61 | }; | |
62 | ||
63 | struct sdhci_tegra { | |
3e44a1a7 | 64 | const struct sdhci_tegra_soc_data *soc_data; |
2391b340 | 65 | struct gpio_desc *power_gpio; |
a8e326a9 | 66 | bool ddr_signaling; |
e5c63d91 | 67 | bool pad_calib_required; |
3e44a1a7 SW |
68 | }; |
69 | ||
03d2bfc8 OJ |
70 | static u16 tegra_sdhci_readw(struct sdhci_host *host, int reg) |
71 | { | |
3e44a1a7 | 72 | struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); |
0734e79c | 73 | struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); |
3e44a1a7 SW |
74 | const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data; |
75 | ||
76 | if (unlikely((soc_data->nvquirks & NVQUIRK_FORCE_SDHCI_SPEC_200) && | |
77 | (reg == SDHCI_HOST_VERSION))) { | |
03d2bfc8 OJ |
78 | /* Erratum: Version register is invalid in HW. */ |
79 | return SDHCI_SPEC_200; | |
80 | } | |
81 | ||
82 | return readw(host->ioaddr + reg); | |
83 | } | |
84 | ||
352ee868 PK |
85 | static void tegra_sdhci_writew(struct sdhci_host *host, u16 val, int reg) |
86 | { | |
87 | struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); | |
352ee868 | 88 | |
01df7ecd RK |
89 | switch (reg) { |
90 | case SDHCI_TRANSFER_MODE: | |
91 | /* | |
92 | * Postpone this write, we must do it together with a | |
93 | * command write that is down below. | |
94 | */ | |
95 | pltfm_host->xfer_mode_shadow = val; | |
96 | return; | |
97 | case SDHCI_COMMAND: | |
98 | writel((val << 16) | pltfm_host->xfer_mode_shadow, | |
99 | host->ioaddr + SDHCI_TRANSFER_MODE); | |
100 | return; | |
352ee868 PK |
101 | } |
102 | ||
103 | writew(val, host->ioaddr + reg); | |
104 | } | |
105 | ||
03d2bfc8 OJ |
106 | static void tegra_sdhci_writel(struct sdhci_host *host, u32 val, int reg) |
107 | { | |
3e44a1a7 | 108 | struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); |
0734e79c | 109 | struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); |
3e44a1a7 SW |
110 | const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data; |
111 | ||
03d2bfc8 OJ |
112 | /* Seems like we're getting spurious timeout and crc errors, so |
113 | * disable signalling of them. In case of real errors software | |
114 | * timers should take care of eventually detecting them. | |
115 | */ | |
116 | if (unlikely(reg == SDHCI_SIGNAL_ENABLE)) | |
117 | val &= ~(SDHCI_INT_TIMEOUT|SDHCI_INT_CRC); | |
118 | ||
119 | writel(val, host->ioaddr + reg); | |
120 | ||
3e44a1a7 SW |
121 | if (unlikely((soc_data->nvquirks & NVQUIRK_ENABLE_BLOCK_GAP_DET) && |
122 | (reg == SDHCI_INT_ENABLE))) { | |
03d2bfc8 OJ |
123 | /* Erratum: Must enable block gap interrupt detection */ |
124 | u8 gap_ctrl = readb(host->ioaddr + SDHCI_BLOCK_GAP_CONTROL); | |
125 | if (val & SDHCI_INT_CARD_INT) | |
126 | gap_ctrl |= 0x8; | |
127 | else | |
128 | gap_ctrl &= ~0x8; | |
129 | writeb(gap_ctrl, host->ioaddr + SDHCI_BLOCK_GAP_CONTROL); | |
130 | } | |
131 | } | |
132 | ||
3e44a1a7 | 133 | static unsigned int tegra_sdhci_get_ro(struct sdhci_host *host) |
03d2bfc8 | 134 | { |
0aacd23f | 135 | return mmc_gpio_get_ro(host->mmc); |
03d2bfc8 OJ |
136 | } |
137 | ||
03231f9b | 138 | static void tegra_sdhci_reset(struct sdhci_host *host, u8 mask) |
ca5879d3 PK |
139 | { |
140 | struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); | |
0734e79c | 141 | struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); |
ca5879d3 | 142 | const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data; |
74cd42bc | 143 | u32 misc_ctrl, clk_ctrl; |
ca5879d3 | 144 | |
03231f9b RK |
145 | sdhci_reset(host, mask); |
146 | ||
ca5879d3 PK |
147 | if (!(mask & SDHCI_RESET_ALL)) |
148 | return; | |
149 | ||
1b84def8 | 150 | misc_ctrl = sdhci_readl(host, SDHCI_TEGRA_VENDOR_MISC_CTRL); |
ca5879d3 | 151 | /* Erratum: Enable SDHCI spec v3.00 support */ |
3145351a | 152 | if (soc_data->nvquirks & NVQUIRK_ENABLE_SDHCI_SPEC_300) |
ca5879d3 | 153 | misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300; |
7ad2ed1d LS |
154 | /* Advertise UHS modes as supported by host */ |
155 | if (soc_data->nvquirks & NVQUIRK_ENABLE_SDR50) | |
156 | misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDR50; | |
7bf037d6 JH |
157 | else |
158 | misc_ctrl &= ~SDHCI_MISC_CTRL_ENABLE_SDR50; | |
7ad2ed1d LS |
159 | if (soc_data->nvquirks & NVQUIRK_ENABLE_DDR50) |
160 | misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_DDR50; | |
7bf037d6 JH |
161 | else |
162 | misc_ctrl &= ~SDHCI_MISC_CTRL_ENABLE_DDR50; | |
7ad2ed1d LS |
163 | if (soc_data->nvquirks & NVQUIRK_ENABLE_SDR104) |
164 | misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDR104; | |
7bf037d6 JH |
165 | else |
166 | misc_ctrl &= ~SDHCI_MISC_CTRL_ENABLE_SDR104; | |
1b84def8 | 167 | sdhci_writel(host, misc_ctrl, SDHCI_TEGRA_VENDOR_MISC_CTRL); |
a8e326a9 | 168 | |
74cd42bc LS |
169 | clk_ctrl = sdhci_readl(host, SDHCI_TEGRA_VENDOR_CLOCK_CTRL); |
170 | clk_ctrl &= ~SDHCI_CLOCK_CTRL_SPI_MODE_CLKEN_OVERRIDE; | |
7ad2ed1d | 171 | if (soc_data->nvquirks & SDHCI_MISC_CTRL_ENABLE_SDR50) |
c3c2384c | 172 | clk_ctrl |= SDHCI_CLOCK_CTRL_SDR50_TUNING_OVERRIDE; |
74cd42bc LS |
173 | sdhci_writel(host, clk_ctrl, SDHCI_TEGRA_VENDOR_CLOCK_CTRL); |
174 | ||
e5c63d91 LS |
175 | if (soc_data->nvquirks & NVQUIRK_HAS_PADCALIB) |
176 | tegra_host->pad_calib_required = true; | |
177 | ||
a8e326a9 | 178 | tegra_host->ddr_signaling = false; |
ca5879d3 PK |
179 | } |
180 | ||
2317f56c | 181 | static void tegra_sdhci_set_bus_width(struct sdhci_host *host, int bus_width) |
03d2bfc8 | 182 | { |
03d2bfc8 OJ |
183 | u32 ctrl; |
184 | ||
03d2bfc8 | 185 | ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); |
0aacd23f JL |
186 | if ((host->mmc->caps & MMC_CAP_8_BIT_DATA) && |
187 | (bus_width == MMC_BUS_WIDTH_8)) { | |
03d2bfc8 OJ |
188 | ctrl &= ~SDHCI_CTRL_4BITBUS; |
189 | ctrl |= SDHCI_CTRL_8BITBUS; | |
190 | } else { | |
191 | ctrl &= ~SDHCI_CTRL_8BITBUS; | |
192 | if (bus_width == MMC_BUS_WIDTH_4) | |
193 | ctrl |= SDHCI_CTRL_4BITBUS; | |
194 | else | |
195 | ctrl &= ~SDHCI_CTRL_4BITBUS; | |
196 | } | |
197 | sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); | |
03d2bfc8 OJ |
198 | } |
199 | ||
e5c63d91 LS |
200 | static void tegra_sdhci_pad_autocalib(struct sdhci_host *host) |
201 | { | |
202 | u32 val; | |
203 | ||
204 | mdelay(1); | |
205 | ||
206 | val = sdhci_readl(host, SDHCI_TEGRA_AUTO_CAL_CONFIG); | |
207 | val |= SDHCI_AUTO_CAL_ENABLE | SDHCI_AUTO_CAL_START; | |
208 | sdhci_writel(host,val, SDHCI_TEGRA_AUTO_CAL_CONFIG); | |
209 | } | |
210 | ||
a8e326a9 LS |
211 | static void tegra_sdhci_set_clock(struct sdhci_host *host, unsigned int clock) |
212 | { | |
213 | struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); | |
0734e79c | 214 | struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); |
a8e326a9 LS |
215 | unsigned long host_clk; |
216 | ||
217 | if (!clock) | |
3491b690 | 218 | return sdhci_set_clock(host, clock); |
a8e326a9 LS |
219 | |
220 | host_clk = tegra_host->ddr_signaling ? clock * 2 : clock; | |
221 | clk_set_rate(pltfm_host->clk, host_clk); | |
222 | host->max_clk = clk_get_rate(pltfm_host->clk); | |
223 | ||
e5c63d91 LS |
224 | sdhci_set_clock(host, clock); |
225 | ||
226 | if (tegra_host->pad_calib_required) { | |
227 | tegra_sdhci_pad_autocalib(host); | |
228 | tegra_host->pad_calib_required = false; | |
229 | } | |
a8e326a9 LS |
230 | } |
231 | ||
232 | static void tegra_sdhci_set_uhs_signaling(struct sdhci_host *host, | |
233 | unsigned timing) | |
234 | { | |
235 | struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); | |
0734e79c | 236 | struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); |
a8e326a9 LS |
237 | |
238 | if (timing == MMC_TIMING_UHS_DDR50) | |
239 | tegra_host->ddr_signaling = true; | |
240 | ||
241 | return sdhci_set_uhs_signaling(host, timing); | |
242 | } | |
243 | ||
244 | static unsigned int tegra_sdhci_get_max_clock(struct sdhci_host *host) | |
245 | { | |
246 | struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); | |
247 | ||
248 | /* | |
249 | * DDR modes require the host to run at double the card frequency, so | |
250 | * the maximum rate we can support is half of the module input clock. | |
251 | */ | |
252 | return clk_round_rate(pltfm_host->clk, UINT_MAX) / 2; | |
253 | } | |
254 | ||
c3c2384c LS |
255 | static void tegra_sdhci_set_tap(struct sdhci_host *host, unsigned int tap) |
256 | { | |
257 | u32 reg; | |
258 | ||
259 | reg = sdhci_readl(host, SDHCI_TEGRA_VENDOR_CLOCK_CTRL); | |
260 | reg &= ~SDHCI_CLOCK_CTRL_TAP_MASK; | |
261 | reg |= tap << SDHCI_CLOCK_CTRL_TAP_SHIFT; | |
262 | sdhci_writel(host, reg, SDHCI_TEGRA_VENDOR_CLOCK_CTRL); | |
263 | } | |
264 | ||
265 | static int tegra_sdhci_execute_tuning(struct sdhci_host *host, u32 opcode) | |
266 | { | |
267 | unsigned int min, max; | |
268 | ||
269 | /* | |
270 | * Start search for minimum tap value at 10, as smaller values are | |
271 | * may wrongly be reported as working but fail at higher speeds, | |
272 | * according to the TRM. | |
273 | */ | |
274 | min = 10; | |
275 | while (min < 255) { | |
276 | tegra_sdhci_set_tap(host, min); | |
277 | if (!mmc_send_tuning(host->mmc, opcode, NULL)) | |
278 | break; | |
279 | min++; | |
280 | } | |
281 | ||
282 | /* Find the maximum tap value that still passes. */ | |
283 | max = min + 1; | |
284 | while (max < 255) { | |
285 | tegra_sdhci_set_tap(host, max); | |
286 | if (mmc_send_tuning(host->mmc, opcode, NULL)) { | |
287 | max--; | |
288 | break; | |
289 | } | |
290 | max++; | |
291 | } | |
292 | ||
293 | /* The TRM states the ideal tap value is at 75% in the passing range. */ | |
294 | tegra_sdhci_set_tap(host, min + ((max - min) * 3 / 4)); | |
295 | ||
296 | return mmc_send_tuning(host->mmc, opcode, NULL); | |
297 | } | |
298 | ||
e5c63d91 LS |
299 | static void tegra_sdhci_voltage_switch(struct sdhci_host *host) |
300 | { | |
301 | struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); | |
302 | struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); | |
303 | const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data; | |
304 | ||
305 | if (soc_data->nvquirks & NVQUIRK_HAS_PADCALIB) | |
306 | tegra_host->pad_calib_required = true; | |
307 | } | |
308 | ||
c915568d | 309 | static const struct sdhci_ops tegra_sdhci_ops = { |
85d6509d | 310 | .get_ro = tegra_sdhci_get_ro, |
85d6509d SG |
311 | .read_w = tegra_sdhci_readw, |
312 | .write_l = tegra_sdhci_writel, | |
a8e326a9 | 313 | .set_clock = tegra_sdhci_set_clock, |
2317f56c | 314 | .set_bus_width = tegra_sdhci_set_bus_width, |
03231f9b | 315 | .reset = tegra_sdhci_reset, |
c3c2384c | 316 | .platform_execute_tuning = tegra_sdhci_execute_tuning, |
a8e326a9 | 317 | .set_uhs_signaling = tegra_sdhci_set_uhs_signaling, |
e5c63d91 | 318 | .voltage_switch = tegra_sdhci_voltage_switch, |
a8e326a9 | 319 | .get_max_clock = tegra_sdhci_get_max_clock, |
85d6509d SG |
320 | }; |
321 | ||
1db5eebf | 322 | static const struct sdhci_pltfm_data sdhci_tegra20_pdata = { |
3e44a1a7 SW |
323 | .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | |
324 | SDHCI_QUIRK_SINGLE_POWER_WRITE | | |
325 | SDHCI_QUIRK_NO_HISPD_BIT | | |
f9260355 AB |
326 | SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC | |
327 | SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, | |
3e44a1a7 SW |
328 | .ops = &tegra_sdhci_ops, |
329 | }; | |
330 | ||
d49d19c2 | 331 | static const struct sdhci_tegra_soc_data soc_data_tegra20 = { |
3e44a1a7 SW |
332 | .pdata = &sdhci_tegra20_pdata, |
333 | .nvquirks = NVQUIRK_FORCE_SDHCI_SPEC_200 | | |
334 | NVQUIRK_ENABLE_BLOCK_GAP_DET, | |
335 | }; | |
3e44a1a7 | 336 | |
1db5eebf | 337 | static const struct sdhci_pltfm_data sdhci_tegra30_pdata = { |
85d6509d | 338 | .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | |
3e44a1a7 | 339 | SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | |
85d6509d SG |
340 | SDHCI_QUIRK_SINGLE_POWER_WRITE | |
341 | SDHCI_QUIRK_NO_HISPD_BIT | | |
f9260355 AB |
342 | SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC | |
343 | SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, | |
a8e326a9 | 344 | .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, |
85d6509d SG |
345 | .ops = &tegra_sdhci_ops, |
346 | }; | |
03d2bfc8 | 347 | |
d49d19c2 | 348 | static const struct sdhci_tegra_soc_data soc_data_tegra30 = { |
3e44a1a7 | 349 | .pdata = &sdhci_tegra30_pdata, |
3145351a | 350 | .nvquirks = NVQUIRK_ENABLE_SDHCI_SPEC_300 | |
7ad2ed1d | 351 | NVQUIRK_ENABLE_SDR50 | |
e5c63d91 LS |
352 | NVQUIRK_ENABLE_SDR104 | |
353 | NVQUIRK_HAS_PADCALIB, | |
3e44a1a7 | 354 | }; |
3e44a1a7 | 355 | |
01df7ecd RK |
356 | static const struct sdhci_ops tegra114_sdhci_ops = { |
357 | .get_ro = tegra_sdhci_get_ro, | |
358 | .read_w = tegra_sdhci_readw, | |
359 | .write_w = tegra_sdhci_writew, | |
360 | .write_l = tegra_sdhci_writel, | |
a8e326a9 | 361 | .set_clock = tegra_sdhci_set_clock, |
01df7ecd RK |
362 | .set_bus_width = tegra_sdhci_set_bus_width, |
363 | .reset = tegra_sdhci_reset, | |
c3c2384c | 364 | .platform_execute_tuning = tegra_sdhci_execute_tuning, |
a8e326a9 | 365 | .set_uhs_signaling = tegra_sdhci_set_uhs_signaling, |
e5c63d91 | 366 | .voltage_switch = tegra_sdhci_voltage_switch, |
a8e326a9 | 367 | .get_max_clock = tegra_sdhci_get_max_clock, |
01df7ecd RK |
368 | }; |
369 | ||
1db5eebf | 370 | static const struct sdhci_pltfm_data sdhci_tegra114_pdata = { |
5ebf2552 RK |
371 | .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | |
372 | SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | | |
373 | SDHCI_QUIRK_SINGLE_POWER_WRITE | | |
374 | SDHCI_QUIRK_NO_HISPD_BIT | | |
f9260355 AB |
375 | SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC | |
376 | SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, | |
a8e326a9 | 377 | .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, |
01df7ecd | 378 | .ops = &tegra114_sdhci_ops, |
5ebf2552 RK |
379 | }; |
380 | ||
d49d19c2 | 381 | static const struct sdhci_tegra_soc_data soc_data_tegra114 = { |
5ebf2552 | 382 | .pdata = &sdhci_tegra114_pdata, |
7bf037d6 JH |
383 | }; |
384 | ||
b5a84ecf TR |
385 | static const struct sdhci_pltfm_data sdhci_tegra210_pdata = { |
386 | .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | | |
387 | SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | | |
388 | SDHCI_QUIRK_SINGLE_POWER_WRITE | | |
389 | SDHCI_QUIRK_NO_HISPD_BIT | | |
a8e326a9 LS |
390 | SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC | |
391 | SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, | |
392 | .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, | |
b5a84ecf TR |
393 | .ops = &tegra114_sdhci_ops, |
394 | }; | |
395 | ||
396 | static const struct sdhci_tegra_soc_data soc_data_tegra210 = { | |
397 | .pdata = &sdhci_tegra210_pdata, | |
b5a84ecf TR |
398 | }; |
399 | ||
498d83e7 | 400 | static const struct of_device_id sdhci_tegra_dt_match[] = { |
b5a84ecf | 401 | { .compatible = "nvidia,tegra210-sdhci", .data = &soc_data_tegra210 }, |
70ad7f7e | 402 | { .compatible = "nvidia,tegra124-sdhci", .data = &soc_data_tegra114 }, |
5ebf2552 | 403 | { .compatible = "nvidia,tegra114-sdhci", .data = &soc_data_tegra114 }, |
3e44a1a7 | 404 | { .compatible = "nvidia,tegra30-sdhci", .data = &soc_data_tegra30 }, |
3e44a1a7 | 405 | { .compatible = "nvidia,tegra20-sdhci", .data = &soc_data_tegra20 }, |
275173b2 GL |
406 | {} |
407 | }; | |
e4404fab | 408 | MODULE_DEVICE_TABLE(of, sdhci_tegra_dt_match); |
275173b2 | 409 | |
c3be1efd | 410 | static int sdhci_tegra_probe(struct platform_device *pdev) |
03d2bfc8 | 411 | { |
3e44a1a7 SW |
412 | const struct of_device_id *match; |
413 | const struct sdhci_tegra_soc_data *soc_data; | |
414 | struct sdhci_host *host; | |
85d6509d | 415 | struct sdhci_pltfm_host *pltfm_host; |
3e44a1a7 | 416 | struct sdhci_tegra *tegra_host; |
03d2bfc8 OJ |
417 | struct clk *clk; |
418 | int rc; | |
419 | ||
3e44a1a7 | 420 | match = of_match_device(sdhci_tegra_dt_match, &pdev->dev); |
b37f9d98 JL |
421 | if (!match) |
422 | return -EINVAL; | |
423 | soc_data = match->data; | |
3e44a1a7 | 424 | |
0734e79c | 425 | host = sdhci_pltfm_init(pdev, soc_data->pdata, sizeof(*tegra_host)); |
85d6509d SG |
426 | if (IS_ERR(host)) |
427 | return PTR_ERR(host); | |
85d6509d SG |
428 | pltfm_host = sdhci_priv(host); |
429 | ||
0734e79c | 430 | tegra_host = sdhci_pltfm_priv(pltfm_host); |
a8e326a9 | 431 | tegra_host->ddr_signaling = false; |
e5c63d91 | 432 | tegra_host->pad_calib_required = false; |
3e44a1a7 | 433 | tegra_host->soc_data = soc_data; |
275173b2 | 434 | |
2391b340 | 435 | rc = mmc_of_parse(host->mmc); |
47caa84f SB |
436 | if (rc) |
437 | goto err_parse_dt; | |
0e786102 | 438 | |
7ad2ed1d | 439 | if (tegra_host->soc_data->nvquirks & NVQUIRK_ENABLE_DDR50) |
c3c2384c LS |
440 | host->mmc->caps |= MMC_CAP_1_8V_DDR; |
441 | ||
2391b340 MJ |
442 | tegra_host->power_gpio = devm_gpiod_get_optional(&pdev->dev, "power", |
443 | GPIOD_OUT_HIGH); | |
444 | if (IS_ERR(tegra_host->power_gpio)) { | |
445 | rc = PTR_ERR(tegra_host->power_gpio); | |
446 | goto err_power_req; | |
03d2bfc8 OJ |
447 | } |
448 | ||
e4f79d9c | 449 | clk = devm_clk_get(mmc_dev(host->mmc), NULL); |
03d2bfc8 OJ |
450 | if (IS_ERR(clk)) { |
451 | dev_err(mmc_dev(host->mmc), "clk err\n"); | |
452 | rc = PTR_ERR(clk); | |
85d6509d | 453 | goto err_clk_get; |
03d2bfc8 | 454 | } |
1e674bc6 | 455 | clk_prepare_enable(clk); |
03d2bfc8 OJ |
456 | pltfm_host->clk = clk; |
457 | ||
85d6509d SG |
458 | rc = sdhci_add_host(host); |
459 | if (rc) | |
460 | goto err_add_host; | |
461 | ||
03d2bfc8 OJ |
462 | return 0; |
463 | ||
85d6509d | 464 | err_add_host: |
1e674bc6 | 465 | clk_disable_unprepare(pltfm_host->clk); |
85d6509d | 466 | err_clk_get: |
85d6509d | 467 | err_power_req: |
47caa84f | 468 | err_parse_dt: |
85d6509d | 469 | sdhci_pltfm_free(pdev); |
03d2bfc8 OJ |
470 | return rc; |
471 | } | |
472 | ||
85d6509d SG |
473 | static struct platform_driver sdhci_tegra_driver = { |
474 | .driver = { | |
475 | .name = "sdhci-tegra", | |
275173b2 | 476 | .of_match_table = sdhci_tegra_dt_match, |
29495aa0 | 477 | .pm = SDHCI_PLTFM_PMOPS, |
85d6509d SG |
478 | }, |
479 | .probe = sdhci_tegra_probe, | |
caebcae9 | 480 | .remove = sdhci_pltfm_unregister, |
03d2bfc8 OJ |
481 | }; |
482 | ||
d1f81a64 | 483 | module_platform_driver(sdhci_tegra_driver); |
85d6509d SG |
484 | |
485 | MODULE_DESCRIPTION("SDHCI driver for Tegra"); | |
3e44a1a7 | 486 | MODULE_AUTHOR("Google, Inc."); |
85d6509d | 487 | MODULE_LICENSE("GPL v2"); |