]>
Commit | Line | Data |
---|---|---|
21ef6a10 TW |
1 | /* |
2 | * (C) Copyright 2009 SAMSUNG Electronics | |
3 | * Minkyu Kang <mk7.kang@samsung.com> | |
4 | * Jaehoon Chung <jh80.chung@samsung.com> | |
6a474db4 | 5 | * Portions Copyright 2011-2016 NVIDIA Corporation |
21ef6a10 | 6 | * |
1a459660 | 7 | * SPDX-License-Identifier: GPL-2.0+ |
21ef6a10 TW |
8 | */ |
9 | ||
19815399 | 10 | #include <bouncebuf.h> |
21ef6a10 | 11 | #include <common.h> |
9d922450 | 12 | #include <dm.h> |
915ffa52 | 13 | #include <errno.h> |
49cb9308 | 14 | #include <mmc.h> |
9877841f | 15 | #include <asm/gpio.h> |
21ef6a10 | 16 | #include <asm/io.h> |
150c2493 | 17 | #include <asm/arch-tegra/tegra_mmc.h> |
21ef6a10 | 18 | |
c9aa831e | 19 | DECLARE_GLOBAL_DATA_PTR; |
21ef6a10 | 20 | |
0e513e78 SG |
21 | struct tegra_mmc_plat { |
22 | struct mmc_config cfg; | |
23 | struct mmc mmc; | |
24 | }; | |
25 | ||
f53c4e4b SW |
26 | struct tegra_mmc_priv { |
27 | struct tegra_mmc *reg; | |
f53c4e4b SW |
28 | struct reset_ctl reset_ctl; |
29 | struct clk clk; | |
f53c4e4b SW |
30 | struct gpio_desc cd_gpio; /* Change Detect GPIO */ |
31 | struct gpio_desc pwr_gpio; /* Power GPIO */ | |
32 | struct gpio_desc wp_gpio; /* Write Protect GPIO */ | |
33 | unsigned int version; /* SDHCI spec. version */ | |
34 | unsigned int clock; /* Current clock (MHz) */ | |
f53c4e4b SW |
35 | }; |
36 | ||
f53c4e4b SW |
37 | static void tegra_mmc_set_power(struct tegra_mmc_priv *priv, |
38 | unsigned short power) | |
2d348a16 TW |
39 | { |
40 | u8 pwr = 0; | |
41 | debug("%s: power = %x\n", __func__, power); | |
42 | ||
43 | if (power != (unsigned short)-1) { | |
44 | switch (1 << power) { | |
45 | case MMC_VDD_165_195: | |
46 | pwr = TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V1_8; | |
47 | break; | |
48 | case MMC_VDD_29_30: | |
49 | case MMC_VDD_30_31: | |
50 | pwr = TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V3_0; | |
51 | break; | |
52 | case MMC_VDD_32_33: | |
53 | case MMC_VDD_33_34: | |
54 | pwr = TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V3_3; | |
55 | break; | |
56 | } | |
57 | } | |
58 | debug("%s: pwr = %X\n", __func__, pwr); | |
59 | ||
60 | /* Set the bus voltage first (if any) */ | |
f53c4e4b | 61 | writeb(pwr, &priv->reg->pwrcon); |
2d348a16 TW |
62 | if (pwr == 0) |
63 | return; | |
64 | ||
65 | /* Now enable bus power */ | |
66 | pwr |= TEGRA_MMC_PWRCTL_SD_BUS_POWER; | |
f53c4e4b | 67 | writeb(pwr, &priv->reg->pwrcon); |
2d348a16 TW |
68 | } |
69 | ||
f53c4e4b SW |
70 | static void tegra_mmc_prepare_data(struct tegra_mmc_priv *priv, |
71 | struct mmc_data *data, | |
72 | struct bounce_buffer *bbstate) | |
21ef6a10 TW |
73 | { |
74 | unsigned char ctrl; | |
75 | ||
21ef6a10 | 76 | |
19815399 SW |
77 | debug("buf: %p (%p), data->blocks: %u, data->blocksize: %u\n", |
78 | bbstate->bounce_buffer, bbstate->user_buffer, data->blocks, | |
79 | data->blocksize); | |
80 | ||
f53c4e4b | 81 | writel((u32)(unsigned long)bbstate->bounce_buffer, &priv->reg->sysad); |
21ef6a10 TW |
82 | /* |
83 | * DMASEL[4:3] | |
84 | * 00 = Selects SDMA | |
85 | * 01 = Reserved | |
86 | * 10 = Selects 32-bit Address ADMA2 | |
87 | * 11 = Selects 64-bit Address ADMA2 | |
88 | */ | |
f53c4e4b | 89 | ctrl = readb(&priv->reg->hostctl); |
8e42f0d6 A |
90 | ctrl &= ~TEGRA_MMC_HOSTCTL_DMASEL_MASK; |
91 | ctrl |= TEGRA_MMC_HOSTCTL_DMASEL_SDMA; | |
f53c4e4b | 92 | writeb(ctrl, &priv->reg->hostctl); |
21ef6a10 TW |
93 | |
94 | /* We do not handle DMA boundaries, so set it to max (512 KiB) */ | |
f53c4e4b SW |
95 | writew((7 << 12) | (data->blocksize & 0xFFF), &priv->reg->blksize); |
96 | writew(data->blocks, &priv->reg->blkcnt); | |
21ef6a10 TW |
97 | } |
98 | ||
f53c4e4b SW |
99 | static void tegra_mmc_set_transfer_mode(struct tegra_mmc_priv *priv, |
100 | struct mmc_data *data) | |
21ef6a10 TW |
101 | { |
102 | unsigned short mode; | |
103 | debug(" mmc_set_transfer_mode called\n"); | |
104 | /* | |
105 | * TRNMOD | |
106 | * MUL1SIN0[5] : Multi/Single Block Select | |
107 | * RD1WT0[4] : Data Transfer Direction Select | |
108 | * 1 = read | |
109 | * 0 = write | |
110 | * ENACMD12[2] : Auto CMD12 Enable | |
111 | * ENBLKCNT[1] : Block Count Enable | |
112 | * ENDMA[0] : DMA Enable | |
113 | */ | |
8e42f0d6 A |
114 | mode = (TEGRA_MMC_TRNMOD_DMA_ENABLE | |
115 | TEGRA_MMC_TRNMOD_BLOCK_COUNT_ENABLE); | |
116 | ||
21ef6a10 | 117 | if (data->blocks > 1) |
8e42f0d6 A |
118 | mode |= TEGRA_MMC_TRNMOD_MULTI_BLOCK_SELECT; |
119 | ||
21ef6a10 | 120 | if (data->flags & MMC_DATA_READ) |
8e42f0d6 | 121 | mode |= TEGRA_MMC_TRNMOD_DATA_XFER_DIR_SEL_READ; |
21ef6a10 | 122 | |
f53c4e4b | 123 | writew(mode, &priv->reg->trnmod); |
21ef6a10 TW |
124 | } |
125 | ||
f53c4e4b SW |
126 | static int tegra_mmc_wait_inhibit(struct tegra_mmc_priv *priv, |
127 | struct mmc_cmd *cmd, | |
128 | struct mmc_data *data, | |
129 | unsigned int timeout) | |
21ef6a10 | 130 | { |
21ef6a10 TW |
131 | /* |
132 | * PRNSTS | |
0963ff3a A |
133 | * CMDINHDAT[1] : Command Inhibit (DAT) |
134 | * CMDINHCMD[0] : Command Inhibit (CMD) | |
21ef6a10 | 135 | */ |
0963ff3a | 136 | unsigned int mask = TEGRA_MMC_PRNSTS_CMD_INHIBIT_CMD; |
21ef6a10 TW |
137 | |
138 | /* | |
139 | * We shouldn't wait for data inhibit for stop commands, even | |
140 | * though they might use busy signaling | |
141 | */ | |
0963ff3a A |
142 | if ((data == NULL) && (cmd->resp_type & MMC_RSP_BUSY)) |
143 | mask |= TEGRA_MMC_PRNSTS_CMD_INHIBIT_DAT; | |
21ef6a10 | 144 | |
f53c4e4b | 145 | while (readl(&priv->reg->prnsts) & mask) { |
21ef6a10 TW |
146 | if (timeout == 0) { |
147 | printf("%s: timeout error\n", __func__); | |
148 | return -1; | |
149 | } | |
150 | timeout--; | |
151 | udelay(1000); | |
152 | } | |
153 | ||
0963ff3a A |
154 | return 0; |
155 | } | |
156 | ||
0e513e78 | 157 | static int tegra_mmc_send_cmd_bounced(struct udevice *dev, struct mmc_cmd *cmd, |
f53c4e4b SW |
158 | struct mmc_data *data, |
159 | struct bounce_buffer *bbstate) | |
0963ff3a | 160 | { |
0e513e78 | 161 | struct tegra_mmc_priv *priv = dev_get_priv(dev); |
0963ff3a A |
162 | int flags, i; |
163 | int result; | |
60e242ed | 164 | unsigned int mask = 0; |
0963ff3a A |
165 | unsigned int retry = 0x100000; |
166 | debug(" mmc_send_cmd called\n"); | |
167 | ||
f53c4e4b | 168 | result = tegra_mmc_wait_inhibit(priv, cmd, data, 10 /* ms */); |
0963ff3a A |
169 | |
170 | if (result < 0) | |
171 | return result; | |
172 | ||
21ef6a10 | 173 | if (data) |
f53c4e4b | 174 | tegra_mmc_prepare_data(priv, data, bbstate); |
21ef6a10 TW |
175 | |
176 | debug("cmd->arg: %08x\n", cmd->cmdarg); | |
f53c4e4b | 177 | writel(cmd->cmdarg, &priv->reg->argument); |
21ef6a10 TW |
178 | |
179 | if (data) | |
f53c4e4b | 180 | tegra_mmc_set_transfer_mode(priv, data); |
21ef6a10 TW |
181 | |
182 | if ((cmd->resp_type & MMC_RSP_136) && (cmd->resp_type & MMC_RSP_BUSY)) | |
183 | return -1; | |
184 | ||
185 | /* | |
186 | * CMDREG | |
187 | * CMDIDX[13:8] : Command index | |
188 | * DATAPRNT[5] : Data Present Select | |
189 | * ENCMDIDX[4] : Command Index Check Enable | |
190 | * ENCMDCRC[3] : Command CRC Check Enable | |
191 | * RSPTYP[1:0] | |
192 | * 00 = No Response | |
193 | * 01 = Length 136 | |
194 | * 10 = Length 48 | |
195 | * 11 = Length 48 Check busy after response | |
196 | */ | |
197 | if (!(cmd->resp_type & MMC_RSP_PRESENT)) | |
8e42f0d6 | 198 | flags = TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_NO_RESPONSE; |
21ef6a10 | 199 | else if (cmd->resp_type & MMC_RSP_136) |
8e42f0d6 | 200 | flags = TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_136; |
21ef6a10 | 201 | else if (cmd->resp_type & MMC_RSP_BUSY) |
8e42f0d6 | 202 | flags = TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_48_BUSY; |
21ef6a10 | 203 | else |
8e42f0d6 | 204 | flags = TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_48; |
21ef6a10 TW |
205 | |
206 | if (cmd->resp_type & MMC_RSP_CRC) | |
8e42f0d6 | 207 | flags |= TEGRA_MMC_TRNMOD_CMD_CRC_CHECK; |
21ef6a10 | 208 | if (cmd->resp_type & MMC_RSP_OPCODE) |
8e42f0d6 | 209 | flags |= TEGRA_MMC_TRNMOD_CMD_INDEX_CHECK; |
21ef6a10 | 210 | if (data) |
8e42f0d6 | 211 | flags |= TEGRA_MMC_TRNMOD_DATA_PRESENT_SELECT_DATA_TRANSFER; |
21ef6a10 TW |
212 | |
213 | debug("cmd: %d\n", cmd->cmdidx); | |
214 | ||
f53c4e4b | 215 | writew((cmd->cmdidx << 8) | flags, &priv->reg->cmdreg); |
21ef6a10 TW |
216 | |
217 | for (i = 0; i < retry; i++) { | |
f53c4e4b | 218 | mask = readl(&priv->reg->norintsts); |
21ef6a10 | 219 | /* Command Complete */ |
8e42f0d6 | 220 | if (mask & TEGRA_MMC_NORINTSTS_CMD_COMPLETE) { |
21ef6a10 | 221 | if (!data) |
f53c4e4b | 222 | writel(mask, &priv->reg->norintsts); |
21ef6a10 TW |
223 | break; |
224 | } | |
225 | } | |
226 | ||
227 | if (i == retry) { | |
228 | printf("%s: waiting for status update\n", __func__); | |
f53c4e4b | 229 | writel(mask, &priv->reg->norintsts); |
915ffa52 | 230 | return -ETIMEDOUT; |
21ef6a10 TW |
231 | } |
232 | ||
8e42f0d6 | 233 | if (mask & TEGRA_MMC_NORINTSTS_CMD_TIMEOUT) { |
21ef6a10 TW |
234 | /* Timeout Error */ |
235 | debug("timeout: %08x cmd %d\n", mask, cmd->cmdidx); | |
f53c4e4b | 236 | writel(mask, &priv->reg->norintsts); |
915ffa52 | 237 | return -ETIMEDOUT; |
8e42f0d6 | 238 | } else if (mask & TEGRA_MMC_NORINTSTS_ERR_INTERRUPT) { |
21ef6a10 TW |
239 | /* Error Interrupt */ |
240 | debug("error: %08x cmd %d\n", mask, cmd->cmdidx); | |
f53c4e4b | 241 | writel(mask, &priv->reg->norintsts); |
21ef6a10 TW |
242 | return -1; |
243 | } | |
244 | ||
245 | if (cmd->resp_type & MMC_RSP_PRESENT) { | |
246 | if (cmd->resp_type & MMC_RSP_136) { | |
247 | /* CRC is stripped so we need to do some shifting. */ | |
248 | for (i = 0; i < 4; i++) { | |
f53c4e4b SW |
249 | unsigned long offset = (unsigned long) |
250 | (&priv->reg->rspreg3 - i); | |
21ef6a10 TW |
251 | cmd->response[i] = readl(offset) << 8; |
252 | ||
253 | if (i != 3) { | |
254 | cmd->response[i] |= | |
255 | readb(offset - 1); | |
256 | } | |
257 | debug("cmd->resp[%d]: %08x\n", | |
258 | i, cmd->response[i]); | |
259 | } | |
260 | } else if (cmd->resp_type & MMC_RSP_BUSY) { | |
261 | for (i = 0; i < retry; i++) { | |
262 | /* PRNTDATA[23:20] : DAT[3:0] Line Signal */ | |
f53c4e4b | 263 | if (readl(&priv->reg->prnsts) |
21ef6a10 TW |
264 | & (1 << 20)) /* DAT[0] */ |
265 | break; | |
266 | } | |
267 | ||
268 | if (i == retry) { | |
269 | printf("%s: card is still busy\n", __func__); | |
f53c4e4b | 270 | writel(mask, &priv->reg->norintsts); |
915ffa52 | 271 | return -ETIMEDOUT; |
21ef6a10 TW |
272 | } |
273 | ||
f53c4e4b | 274 | cmd->response[0] = readl(&priv->reg->rspreg0); |
21ef6a10 TW |
275 | debug("cmd->resp[0]: %08x\n", cmd->response[0]); |
276 | } else { | |
f53c4e4b | 277 | cmd->response[0] = readl(&priv->reg->rspreg0); |
21ef6a10 TW |
278 | debug("cmd->resp[0]: %08x\n", cmd->response[0]); |
279 | } | |
280 | } | |
281 | ||
282 | if (data) { | |
9b3d1873 A |
283 | unsigned long start = get_timer(0); |
284 | ||
21ef6a10 | 285 | while (1) { |
f53c4e4b | 286 | mask = readl(&priv->reg->norintsts); |
21ef6a10 | 287 | |
8e42f0d6 | 288 | if (mask & TEGRA_MMC_NORINTSTS_ERR_INTERRUPT) { |
21ef6a10 | 289 | /* Error Interrupt */ |
f53c4e4b | 290 | writel(mask, &priv->reg->norintsts); |
21ef6a10 TW |
291 | printf("%s: error during transfer: 0x%08x\n", |
292 | __func__, mask); | |
293 | return -1; | |
8e42f0d6 | 294 | } else if (mask & TEGRA_MMC_NORINTSTS_DMA_INTERRUPT) { |
5a762e25 A |
295 | /* |
296 | * DMA Interrupt, restart the transfer where | |
297 | * it was interrupted. | |
298 | */ | |
f53c4e4b | 299 | unsigned int address = readl(&priv->reg->sysad); |
5a762e25 | 300 | |
21ef6a10 | 301 | debug("DMA end\n"); |
5a762e25 | 302 | writel(TEGRA_MMC_NORINTSTS_DMA_INTERRUPT, |
f53c4e4b SW |
303 | &priv->reg->norintsts); |
304 | writel(address, &priv->reg->sysad); | |
8e42f0d6 | 305 | } else if (mask & TEGRA_MMC_NORINTSTS_XFER_COMPLETE) { |
21ef6a10 TW |
306 | /* Transfer Complete */ |
307 | debug("r/w is done\n"); | |
308 | break; | |
09fb7361 | 309 | } else if (get_timer(start) > 8000UL) { |
f53c4e4b | 310 | writel(mask, &priv->reg->norintsts); |
9b3d1873 A |
311 | printf("%s: MMC Timeout\n" |
312 | " Interrupt status 0x%08x\n" | |
313 | " Interrupt status enable 0x%08x\n" | |
314 | " Interrupt signal enable 0x%08x\n" | |
315 | " Present status 0x%08x\n", | |
316 | __func__, mask, | |
f53c4e4b SW |
317 | readl(&priv->reg->norintstsen), |
318 | readl(&priv->reg->norintsigen), | |
319 | readl(&priv->reg->prnsts)); | |
9b3d1873 | 320 | return -1; |
21ef6a10 TW |
321 | } |
322 | } | |
f53c4e4b | 323 | writel(mask, &priv->reg->norintsts); |
21ef6a10 TW |
324 | } |
325 | ||
326 | udelay(1000); | |
327 | return 0; | |
328 | } | |
329 | ||
0e513e78 | 330 | static int tegra_mmc_send_cmd(struct udevice *dev, struct mmc_cmd *cmd, |
f53c4e4b | 331 | struct mmc_data *data) |
19815399 SW |
332 | { |
333 | void *buf; | |
334 | unsigned int bbflags; | |
335 | size_t len; | |
336 | struct bounce_buffer bbstate; | |
337 | int ret; | |
338 | ||
339 | if (data) { | |
340 | if (data->flags & MMC_DATA_READ) { | |
341 | buf = data->dest; | |
342 | bbflags = GEN_BB_WRITE; | |
343 | } else { | |
344 | buf = (void *)data->src; | |
345 | bbflags = GEN_BB_READ; | |
346 | } | |
347 | len = data->blocks * data->blocksize; | |
348 | ||
349 | bounce_buffer_start(&bbstate, buf, len, bbflags); | |
350 | } | |
351 | ||
0e513e78 | 352 | ret = tegra_mmc_send_cmd_bounced(dev, cmd, data, &bbstate); |
19815399 SW |
353 | |
354 | if (data) | |
355 | bounce_buffer_stop(&bbstate); | |
356 | ||
357 | return ret; | |
358 | } | |
359 | ||
f53c4e4b | 360 | static void tegra_mmc_change_clock(struct tegra_mmc_priv *priv, uint clock) |
21ef6a10 | 361 | { |
e8adca9e | 362 | ulong rate; |
4ed59e70 | 363 | int div; |
21ef6a10 TW |
364 | unsigned short clk; |
365 | unsigned long timeout; | |
4ed59e70 | 366 | |
21ef6a10 TW |
367 | debug(" mmc_change_clock called\n"); |
368 | ||
4ed59e70 | 369 | /* |
2d348a16 | 370 | * Change Tegra SDMMCx clock divisor here. Source is PLLP_OUT0 |
4ed59e70 | 371 | */ |
21ef6a10 TW |
372 | if (clock == 0) |
373 | goto out; | |
e8adca9e SW |
374 | |
375 | rate = clk_set_rate(&priv->clk, clock); | |
376 | div = (rate + clock - 1) / clock; | |
4ed59e70 | 377 | debug("div = %d\n", div); |
21ef6a10 | 378 | |
f53c4e4b | 379 | writew(0, &priv->reg->clkcon); |
21ef6a10 | 380 | |
21ef6a10 TW |
381 | /* |
382 | * CLKCON | |
383 | * SELFREQ[15:8] : base clock divided by value | |
384 | * ENSDCLK[2] : SD Clock Enable | |
385 | * STBLINTCLK[1] : Internal Clock Stable | |
386 | * ENINTCLK[0] : Internal Clock Enable | |
387 | */ | |
4ed59e70 | 388 | div >>= 1; |
8e42f0d6 A |
389 | clk = ((div << TEGRA_MMC_CLKCON_SDCLK_FREQ_SEL_SHIFT) | |
390 | TEGRA_MMC_CLKCON_INTERNAL_CLOCK_ENABLE); | |
f53c4e4b | 391 | writew(clk, &priv->reg->clkcon); |
21ef6a10 TW |
392 | |
393 | /* Wait max 10 ms */ | |
394 | timeout = 10; | |
f53c4e4b | 395 | while (!(readw(&priv->reg->clkcon) & |
8e42f0d6 | 396 | TEGRA_MMC_CLKCON_INTERNAL_CLOCK_STABLE)) { |
21ef6a10 TW |
397 | if (timeout == 0) { |
398 | printf("%s: timeout error\n", __func__); | |
399 | return; | |
400 | } | |
401 | timeout--; | |
402 | udelay(1000); | |
403 | } | |
404 | ||
8e42f0d6 | 405 | clk |= TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE; |
f53c4e4b | 406 | writew(clk, &priv->reg->clkcon); |
21ef6a10 TW |
407 | |
408 | debug("mmc_change_clock: clkcon = %08X\n", clk); | |
21ef6a10 TW |
409 | |
410 | out: | |
f53c4e4b | 411 | priv->clock = clock; |
21ef6a10 TW |
412 | } |
413 | ||
0e513e78 | 414 | static int tegra_mmc_set_ios(struct udevice *dev) |
21ef6a10 | 415 | { |
0e513e78 SG |
416 | struct tegra_mmc_priv *priv = dev_get_priv(dev); |
417 | struct mmc *mmc = mmc_get_mmc_dev(dev); | |
21ef6a10 TW |
418 | unsigned char ctrl; |
419 | debug(" mmc_set_ios called\n"); | |
420 | ||
421 | debug("bus_width: %x, clock: %d\n", mmc->bus_width, mmc->clock); | |
422 | ||
423 | /* Change clock first */ | |
f53c4e4b | 424 | tegra_mmc_change_clock(priv, mmc->clock); |
21ef6a10 | 425 | |
f53c4e4b | 426 | ctrl = readb(&priv->reg->hostctl); |
21ef6a10 TW |
427 | |
428 | /* | |
429 | * WIDE8[5] | |
430 | * 0 = Depend on WIDE4 | |
431 | * 1 = 8-bit mode | |
432 | * WIDE4[1] | |
433 | * 1 = 4-bit mode | |
434 | * 0 = 1-bit mode | |
435 | */ | |
436 | if (mmc->bus_width == 8) | |
437 | ctrl |= (1 << 5); | |
438 | else if (mmc->bus_width == 4) | |
439 | ctrl |= (1 << 1); | |
440 | else | |
542b5f85 | 441 | ctrl &= ~(1 << 1 | 1 << 5); |
21ef6a10 | 442 | |
f53c4e4b | 443 | writeb(ctrl, &priv->reg->hostctl); |
21ef6a10 | 444 | debug("mmc_set_ios: hostctl = %08X\n", ctrl); |
07b0b9c0 JC |
445 | |
446 | return 0; | |
21ef6a10 TW |
447 | } |
448 | ||
f53c4e4b | 449 | static void tegra_mmc_pad_init(struct tegra_mmc_priv *priv) |
6b83588e SW |
450 | { |
451 | #if defined(CONFIG_TEGRA30) | |
6b83588e SW |
452 | u32 val; |
453 | ||
f53c4e4b | 454 | debug("%s: sdmmc address = %08x\n", __func__, (unsigned int)priv->reg); |
6b83588e SW |
455 | |
456 | /* Set the pad drive strength for SDMMC1 or 3 only */ | |
f53c4e4b SW |
457 | if (priv->reg != (void *)0x78000000 && |
458 | priv->reg != (void *)0x78000400) { | |
6b83588e SW |
459 | debug("%s: settings are only valid for SDMMC1/SDMMC3!\n", |
460 | __func__); | |
461 | return; | |
462 | } | |
463 | ||
f53c4e4b | 464 | val = readl(&priv->reg->sdmemcmppadctl); |
6b83588e SW |
465 | val &= 0xFFFFFFF0; |
466 | val |= MEMCOMP_PADCTRL_VREF; | |
f53c4e4b | 467 | writel(val, &priv->reg->sdmemcmppadctl); |
6b83588e | 468 | |
f53c4e4b | 469 | val = readl(&priv->reg->autocalcfg); |
6b83588e SW |
470 | val &= 0xFFFF0000; |
471 | val |= AUTO_CAL_PU_OFFSET | AUTO_CAL_PD_OFFSET | AUTO_CAL_ENABLED; | |
f53c4e4b | 472 | writel(val, &priv->reg->autocalcfg); |
6b83588e SW |
473 | #endif |
474 | } | |
475 | ||
f53c4e4b | 476 | static void tegra_mmc_reset(struct tegra_mmc_priv *priv, struct mmc *mmc) |
21ef6a10 TW |
477 | { |
478 | unsigned int timeout; | |
479 | debug(" mmc_reset called\n"); | |
480 | ||
481 | /* | |
482 | * RSTALL[0] : Software reset for all | |
483 | * 1 = reset | |
484 | * 0 = work | |
485 | */ | |
f53c4e4b | 486 | writeb(TEGRA_MMC_SWRST_SW_RESET_FOR_ALL, &priv->reg->swrst); |
21ef6a10 | 487 | |
f53c4e4b | 488 | priv->clock = 0; |
21ef6a10 TW |
489 | |
490 | /* Wait max 100 ms */ | |
491 | timeout = 100; | |
492 | ||
493 | /* hw clears the bit when it's done */ | |
f53c4e4b | 494 | while (readb(&priv->reg->swrst) & TEGRA_MMC_SWRST_SW_RESET_FOR_ALL) { |
21ef6a10 TW |
495 | if (timeout == 0) { |
496 | printf("%s: timeout error\n", __func__); | |
497 | return; | |
498 | } | |
499 | timeout--; | |
500 | udelay(1000); | |
501 | } | |
2d348a16 TW |
502 | |
503 | /* Set SD bus voltage & enable bus power */ | |
f53c4e4b | 504 | tegra_mmc_set_power(priv, fls(mmc->cfg->voltages) - 1); |
2d348a16 | 505 | debug("%s: power control = %02X, host control = %02X\n", __func__, |
f53c4e4b | 506 | readb(&priv->reg->pwrcon), readb(&priv->reg->hostctl)); |
2d348a16 TW |
507 | |
508 | /* Make sure SDIO pads are set up */ | |
f53c4e4b | 509 | tegra_mmc_pad_init(priv); |
21ef6a10 TW |
510 | } |
511 | ||
0e513e78 | 512 | static int tegra_mmc_init(struct udevice *dev) |
21ef6a10 | 513 | { |
0e513e78 SG |
514 | struct tegra_mmc_priv *priv = dev_get_priv(dev); |
515 | struct mmc *mmc = mmc_get_mmc_dev(dev); | |
21ef6a10 | 516 | unsigned int mask; |
6a474db4 | 517 | debug(" tegra_mmc_init called\n"); |
21ef6a10 | 518 | |
f53c4e4b | 519 | tegra_mmc_reset(priv, mmc); |
21ef6a10 | 520 | |
4119b709 MZ |
521 | #if defined(CONFIG_TEGRA124_MMC_DISABLE_EXT_LOOPBACK) |
522 | /* | |
523 | * Disable the external clock loopback and use the internal one on | |
524 | * SDMMC3 as per the SDMMC_VENDOR_MISC_CNTRL_0 register's SDMMC_SPARE1 | |
525 | * bits being set to 0xfffd according to the TRM. | |
526 | * | |
527 | * TODO(marcel.ziswiler@toradex.com): Move to device tree controlled | |
528 | * approach once proper kernel integration made it mainline. | |
529 | */ | |
530 | if (priv->reg == (void *)0x700b0400) { | |
531 | mask = readl(&priv->reg->venmiscctl); | |
532 | mask &= ~TEGRA_MMC_MISCON_ENABLE_EXT_LOOPBACK; | |
533 | writel(mask, &priv->reg->venmiscctl); | |
534 | } | |
535 | #endif | |
536 | ||
f53c4e4b SW |
537 | priv->version = readw(&priv->reg->hcver); |
538 | debug("host version = %x\n", priv->version); | |
21ef6a10 TW |
539 | |
540 | /* mask all */ | |
f53c4e4b SW |
541 | writel(0xffffffff, &priv->reg->norintstsen); |
542 | writel(0xffffffff, &priv->reg->norintsigen); | |
21ef6a10 | 543 | |
f53c4e4b | 544 | writeb(0xe, &priv->reg->timeoutcon); /* TMCLK * 2^27 */ |
21ef6a10 TW |
545 | /* |
546 | * NORMAL Interrupt Status Enable Register init | |
547 | * [5] ENSTABUFRDRDY : Buffer Read Ready Status Enable | |
548 | * [4] ENSTABUFWTRDY : Buffer write Ready Status Enable | |
5a762e25 | 549 | * [3] ENSTADMAINT : DMA boundary interrupt |
21ef6a10 TW |
550 | * [1] ENSTASTANSCMPLT : Transfre Complete Status Enable |
551 | * [0] ENSTACMDCMPLT : Command Complete Status Enable | |
552 | */ | |
f53c4e4b | 553 | mask = readl(&priv->reg->norintstsen); |
21ef6a10 | 554 | mask &= ~(0xffff); |
8e42f0d6 A |
555 | mask |= (TEGRA_MMC_NORINTSTSEN_CMD_COMPLETE | |
556 | TEGRA_MMC_NORINTSTSEN_XFER_COMPLETE | | |
5a762e25 | 557 | TEGRA_MMC_NORINTSTSEN_DMA_INTERRUPT | |
8e42f0d6 A |
558 | TEGRA_MMC_NORINTSTSEN_BUFFER_WRITE_READY | |
559 | TEGRA_MMC_NORINTSTSEN_BUFFER_READ_READY); | |
f53c4e4b | 560 | writel(mask, &priv->reg->norintstsen); |
21ef6a10 TW |
561 | |
562 | /* | |
563 | * NORMAL Interrupt Signal Enable Register init | |
564 | * [1] ENSTACMDCMPLT : Transfer Complete Signal Enable | |
565 | */ | |
f53c4e4b | 566 | mask = readl(&priv->reg->norintsigen); |
21ef6a10 | 567 | mask &= ~(0xffff); |
8e42f0d6 | 568 | mask |= TEGRA_MMC_NORINTSIGEN_XFER_COMPLETE; |
f53c4e4b | 569 | writel(mask, &priv->reg->norintsigen); |
21ef6a10 TW |
570 | |
571 | return 0; | |
572 | } | |
573 | ||
0e513e78 | 574 | static int tegra_mmc_getcd(struct udevice *dev) |
bf83662b | 575 | { |
0e513e78 | 576 | struct tegra_mmc_priv *priv = dev_get_priv(dev); |
bf83662b | 577 | |
29f3e3f2 | 578 | debug("tegra_mmc_getcd called\n"); |
bf83662b | 579 | |
f53c4e4b SW |
580 | if (dm_gpio_is_valid(&priv->cd_gpio)) |
581 | return dm_gpio_get_value(&priv->cd_gpio); | |
bf83662b TR |
582 | |
583 | return 1; | |
584 | } | |
585 | ||
0e513e78 | 586 | static const struct dm_mmc_ops tegra_mmc_ops = { |
ab769f22 PA |
587 | .send_cmd = tegra_mmc_send_cmd, |
588 | .set_ios = tegra_mmc_set_ios, | |
0e513e78 | 589 | .get_cd = tegra_mmc_getcd, |
ab769f22 PA |
590 | }; |
591 | ||
6a474db4 | 592 | static int tegra_mmc_probe(struct udevice *dev) |
21ef6a10 | 593 | { |
6a474db4 | 594 | struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); |
0e513e78 | 595 | struct tegra_mmc_plat *plat = dev_get_platdata(dev); |
6a474db4 | 596 | struct tegra_mmc_priv *priv = dev_get_priv(dev); |
0e513e78 | 597 | struct mmc_config *cfg = &plat->cfg; |
e8adca9e | 598 | int bus_width, ret; |
21ef6a10 | 599 | |
0e513e78 | 600 | cfg->name = dev->name; |
21ef6a10 | 601 | |
49cb9308 | 602 | bus_width = dev_read_u32_default(dev, "bus-width", 1); |
6a474db4 | 603 | |
0e513e78 SG |
604 | cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195; |
605 | cfg->host_caps = 0; | |
6a474db4 | 606 | if (bus_width == 8) |
0e513e78 | 607 | cfg->host_caps |= MMC_MODE_8BIT; |
6a474db4 | 608 | if (bus_width >= 4) |
0e513e78 SG |
609 | cfg->host_caps |= MMC_MODE_4BIT; |
610 | cfg->host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS; | |
21ef6a10 TW |
611 | |
612 | /* | |
613 | * min freq is for card identification, and is the highest | |
614 | * low-speed SDIO card frequency (actually 400KHz) | |
615 | * max freq is highest HS eMMC clock as per the SD/MMC spec | |
616 | * (actually 52MHz) | |
21ef6a10 | 617 | */ |
0e513e78 SG |
618 | cfg->f_min = 375000; |
619 | cfg->f_max = 48000000; | |
21ef6a10 | 620 | |
0e513e78 | 621 | cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT; |
93bfd616 | 622 | |
49cb9308 | 623 | priv->reg = (void *)dev_read_addr(dev); |
c9aa831e | 624 | |
6a474db4 TW |
625 | ret = reset_get_by_name(dev, "sdhci", &priv->reset_ctl); |
626 | if (ret) { | |
627 | debug("reset_get_by_name() failed: %d\n", ret); | |
628 | return ret; | |
c0493076 | 629 | } |
6a474db4 TW |
630 | ret = clk_get_by_index(dev, 0, &priv->clk); |
631 | if (ret) { | |
632 | debug("clk_get_by_index() failed: %d\n", ret); | |
633 | return ret; | |
634 | } | |
635 | ||
636 | ret = reset_assert(&priv->reset_ctl); | |
637 | if (ret) | |
638 | return ret; | |
639 | ret = clk_enable(&priv->clk); | |
640 | if (ret) | |
641 | return ret; | |
642 | ret = clk_set_rate(&priv->clk, 20000000); | |
643 | if (IS_ERR_VALUE(ret)) | |
644 | return ret; | |
645 | ret = reset_deassert(&priv->reset_ctl); | |
646 | if (ret) | |
647 | return ret; | |
c9aa831e | 648 | |
6a474db4 | 649 | /* These GPIOs are optional */ |
49cb9308 SG |
650 | gpio_request_by_name(dev, "cd-gpios", 0, &priv->cd_gpio, GPIOD_IS_IN); |
651 | gpio_request_by_name(dev, "wp-gpios", 0, &priv->wp_gpio, GPIOD_IS_IN); | |
652 | gpio_request_by_name(dev, "power-gpios", 0, &priv->pwr_gpio, | |
653 | GPIOD_IS_OUT); | |
6a474db4 TW |
654 | if (dm_gpio_is_valid(&priv->pwr_gpio)) |
655 | dm_gpio_set_value(&priv->pwr_gpio, 1); | |
c9aa831e | 656 | |
0e513e78 | 657 | upriv->mmc = &plat->mmc; |
c9aa831e | 658 | |
0e513e78 SG |
659 | return tegra_mmc_init(dev); |
660 | } | |
c9aa831e | 661 | |
0e513e78 SG |
662 | static int tegra_mmc_bind(struct udevice *dev) |
663 | { | |
664 | struct tegra_mmc_plat *plat = dev_get_platdata(dev); | |
665 | ||
666 | return mmc_bind(dev, &plat->mmc, &plat->cfg); | |
c9aa831e TW |
667 | } |
668 | ||
6a474db4 TW |
669 | static const struct udevice_id tegra_mmc_ids[] = { |
670 | { .compatible = "nvidia,tegra20-sdhci" }, | |
671 | { .compatible = "nvidia,tegra30-sdhci" }, | |
672 | { .compatible = "nvidia,tegra114-sdhci" }, | |
673 | { .compatible = "nvidia,tegra124-sdhci" }, | |
674 | { .compatible = "nvidia,tegra210-sdhci" }, | |
675 | { .compatible = "nvidia,tegra186-sdhci" }, | |
676 | { } | |
677 | }; | |
c9aa831e | 678 | |
6a474db4 TW |
679 | U_BOOT_DRIVER(tegra_mmc_drv) = { |
680 | .name = "tegra_mmc", | |
681 | .id = UCLASS_MMC, | |
682 | .of_match = tegra_mmc_ids, | |
0e513e78 | 683 | .bind = tegra_mmc_bind, |
6a474db4 | 684 | .probe = tegra_mmc_probe, |
0e513e78 SG |
685 | .ops = &tegra_mmc_ops, |
686 | .platdata_auto_alloc_size = sizeof(struct tegra_mmc_plat), | |
6a474db4 TW |
687 | .priv_auto_alloc_size = sizeof(struct tegra_mmc_priv), |
688 | }; |