1 /*
2 * Sunxi SD/MMC host driver
3 *
4 * Copyright (C) 2015 AllWinnertech Ltd.
5 * Author: lixiang <lixiang@allwinnertech>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 *
11 * This program is distributed "as is" WITHOUT ANY WARRANTY of any
12 * kind, whether express or implied; without even the implied warranty
13 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 */
16
17 #ifdef CONFIG_ARCH_SUN50IW1P1
18
19 #include <linux/clk.h>
20 #include <sunxi-clk.h>
21
22 #include <linux/gpio.h>
23 #include <linux/platform_device.h>
24 #include <linux/spinlock.h>
25 #include <linux/scatterlist.h>
26 #include <linux/dma-mapping.h>
27 #include <linux/slab.h>
28 #include <linux/reset.h>
29
30 #include <linux/of_address.h>
31 #include <linux/of_gpio.h>
32 #include <linux/of_platform.h>
33
34 #include <linux/mmc/host.h>
35 #include <linux/mmc/sd.h>
36 #include <linux/mmc/sdio.h>
37 #include <linux/mmc/mmc.h>
38 #include <linux/mmc/core.h>
39 #include <linux/mmc/card.h>
40 #include <linux/mmc/slot-gpio.h>
41
42 #include "sunxi-mmc.h"
43 #include "sunxi-mmc-sun50iw1p1-0.h"
44
45 /*reg*/
46 /*SMHC eMMC4.5 DDR Start Bit Detection Control Register */
47 /*SMHC CRC Status Detect Control Register */
48 /*SMHC Card Threshold Control Register */
49 /*SMHC Drive Delay Control Register */
50 /*SMHC Sample Delay Control Register */
51 /*SMHC Data Strobe Delay Control Register */
52 /*SMHC NewTiming Set Register */
53 #define SDXC_REG_EDSD (0x010C)
54 #define SDXC_REG_CSDC (0x0054)
55 #define SDXC_REG_THLD (0x0100)
56 #define SDXC_REG_DRV_DL (0x0140)
57 #define SDXC_REG_SAMP_DL (0x0144)
58 #define SDXC_REG_DS_DL (0x0148)
59 #define SDXC_REG_SD_NTSR (0x005C)
60
61 /*bit*/
62 #define SDXC_HS400_MD_EN (1U<<31)
63 #define SDXC_CARD_WR_THLD_ENB (1U<<2)
64 #define SDXC_CARD_RD_THLD_ENB (1U)
65
66 #define SDXC_DAT_DRV_PH_SEL (1U<<17)
67 #define SDXC_CMD_DRV_PH_SEL (1U<<16)
68 #define SDXC_SAMP_DL_SW_EN (1u<<7)
69 #define SDXC_DS_DL_SW_EN (1u<<7)
70
71 #define SDXC_2X_TIMING_MODE (1U<<31)
72
73 /*mask*/
74 #define SDXC_CRC_DET_PARA_MASK (0xf)
75 #define SDXC_CARD_RD_THLD_MASK (0x0FFF0000)
76 #define SDXC_TX_TL_MASK (0xff)
77 #define SDXC_RX_TL_MASK (0x00FF0000)
78
79 #define SDXC_SAMP_DL_SW_MASK (0x0000003F)
80 #define SDXC_DS_DL_SW_MASK (0x0000003F)
81
82 #define SDXC_SAM_TIMING_PH_MASK (0x00000030)
83
84 /*value*/
85 #define SDXC_CRC_DET_PARA_HS400 (6)
86 #define SDXC_CRC_DET_PARA_OTHER (3)
87 #define SDXC_FIFO_DETH (1024>>2)
88
89 /*size*/
90 #define SDXC_CARD_RD_THLD_SIZE (0x00000FFF)
91
92 /*shit*/
93 #define SDXC_CARD_RD_THLD_SIZE_SHIFT (16)
94
95 #define SDXC_SAM_TIMING_PH_SHIFT (4)
96
97 enum sunxi_mmc_clk_mode {
98 mmc_clk_400k = 0,
99 mmc_clk_26M,
100 mmc_clk_52M,
101 mmc_clk_52M_DDR4,
102 mmc_clk_52M_DDR8,
103 mmc_clk_104M,
104 mmc_clk_208M,
105 mmc_clk_104M_DDR,
106 mmc_clk_208M_DDR,
107 mmc_clk_mod_num,
108 };
109
110 struct sunxi_mmc_clk_dly {
111 enum sunxi_mmc_clk_mode cmod;
112 char *mod_str;
113 u32 cmd_drv_ph;
114 u32 dat_drv_ph;
115 u32 sam_dly;
116 u32 ds_dly;
117 u32 sam_ph;
118 };
119
120 /*sample delay and output deley setting */
121 static struct sunxi_mmc_clk_dly mmc_clk_dly[mmc_clk_mod_num] = {
122 [mmc_clk_400k] = {
123 .cmod = mmc_clk_400k,
124 .mod_str = "sunxi-dly-400k",
125 .cmd_drv_ph = 1,
126 .dat_drv_ph = 0,
127 .sam_dly = 0,
128 .ds_dly = 0,
129 .sam_ph = 0,
130 },
131 [mmc_clk_26M] = {
132 .cmod = mmc_clk_26M,
133 .mod_str = "sunxi-dly-26M",
134 .cmd_drv_ph = 1,
135 .dat_drv_ph = 0,
136 .sam_dly = 0,
137 .ds_dly = 0,
138 .sam_ph = 0,
139 },
140 [mmc_clk_52M] = {
141 .cmod = mmc_clk_52M,
142 .mod_str = "sunxi-dly-52M",
143 .cmd_drv_ph = 1,
144 .dat_drv_ph = 0,
145 .sam_dly = 0,
146 .ds_dly = 0,
147 .sam_ph = 0,
148 },
149 [mmc_clk_52M_DDR4] = {
150 .cmod = mmc_clk_52M_DDR4,
151 .mod_str = "sunxi-dly-52M-ddr4",
152 .cmd_drv_ph = 1,
153 .dat_drv_ph = 0,
154 .sam_dly = 0,
155 .ds_dly = 0,
156 .sam_ph = 0,
157 },
158 [mmc_clk_52M_DDR8] = {
159 .cmod = mmc_clk_52M_DDR8,
160 .mod_str = "sunxi-dly-52M-ddr8",
161 .cmd_drv_ph = 1,
162 .dat_drv_ph = 0,
163 .sam_dly = 0,
164 .ds_dly = 0,
165 .sam_ph = 0,
166 },
167 [mmc_clk_104M] = {
168 .cmod = mmc_clk_104M,
169 .mod_str = "sunxi-dly-104M",
170 .cmd_drv_ph = 1,
171 .dat_drv_ph = 0,
172 .sam_dly = 0,
173 .ds_dly = 0,
174 .sam_ph = 0,
175
176 },
177 [mmc_clk_208M] = {
178 .cmod = mmc_clk_208M,
179 .mod_str = "sunxi-dly-208M",
180 .cmd_drv_ph = 1,
181 .dat_drv_ph = 0,
182 .sam_dly = 0,
183 .ds_dly = 0,
184 .sam_ph = 0,
185 },
186 [mmc_clk_104M_DDR] = {
187 .cmod = mmc_clk_104M_DDR,
188 .mod_str = "sunxi-dly-104M-ddr",
189 .cmd_drv_ph = 1,
190 .dat_drv_ph = 0,
191 .sam_dly = 0,
192 .ds_dly = 0,
193 .sam_ph = 0,
194 },
195 [mmc_clk_208M_DDR] = {
196 .cmod = mmc_clk_208M_DDR,
197 .mod_str = "sunxi-dly-208M-ddr",
198 .cmd_drv_ph = 1,
199 .dat_drv_ph = 0,
200 .sam_dly = 0,
201 .ds_dly = 0,
202 .sam_ph = 0,
203 },
204 };
205
206 struct sunxi_mmc_spec_regs {
207 u32 drv_dl; /*REG_DRV_DL */
208 u32 samp_dl; /*REG_SAMP_DL */
209 u32 ds_dl; /*REG_DS_DL */
210 u32 sd_ntsr; /*REG_SD_NTSR */
211 };
212
213 static struct sunxi_mmc_spec_regs bak_spec_regs;
214
215
sunxi_mmc_set_clk_dly(struct sunxi_mmc_host * host,int clk,int bus_width,int timing)216 static void sunxi_mmc_set_clk_dly(struct sunxi_mmc_host *host, int clk,
217 int bus_width, int timing)
218 {
219 struct mmc_host *mhost = host->mmc;
220 u32 rval = 0;
221 enum sunxi_mmc_clk_mode cmod = mmc_clk_400k;
222 u32 in_clk_dly[5] = { 0 };
223 int ret = 0;
224 struct device_node *np = NULL;
225
226 if (!mhost->parent || !mhost->parent->of_node) {
227 SM_ERR(mmc_dev(host->mmc),
228 "no dts to parse clk dly,use default\n");
229 return;
230 }
231
232 np = mhost->parent->of_node;
233
234 if (clk <= 400 * 1000) {
235 cmod = mmc_clk_400k;
236 } else if (clk <= 26 * 1000 * 1000) {
237 cmod = mmc_clk_26M;
238 } else if (clk <= 52 * 1000 * 1000) {
239 if ((bus_width == MMC_BUS_WIDTH_4)
240 && sunxi_mmc_ddr_timing(timing)) {
241 cmod = mmc_clk_52M_DDR4;
242 } else if ((bus_width == MMC_BUS_WIDTH_8)
243 && (timing == MMC_TIMING_MMC_DDR52)) {
244 cmod = mmc_clk_52M_DDR8;
245 } else {
246 cmod = mmc_clk_52M;
247 }
248 } else if (clk <= 104 * 1000 * 1000) {
249 if ((bus_width == MMC_BUS_WIDTH_8)
250 && (timing == MMC_TIMING_MMC_HS400)) {
251 cmod = mmc_clk_104M_DDR;
252 } else {
253 cmod = mmc_clk_104M;
254 }
255 } else if (clk <= 208 * 1000 * 1000) {
256 if ((bus_width == MMC_BUS_WIDTH_8)
257 && (timing == MMC_TIMING_MMC_HS400)) {
258 cmod = mmc_clk_208M_DDR;
259 } else {
260 cmod = mmc_clk_208M;
261 }
262 } else {
263 SM_ERR(mmc_dev(mhost), "clk %d is out of range\n", clk);
264 return;
265 }
266
267 ret = of_property_read_u32_array(np, mmc_clk_dly[cmod].mod_str,
268 in_clk_dly, ARRAY_SIZE(in_clk_dly));
269 if (ret) {
270 SM_DBG(mmc_dev(host->mmc), "failded to get %s used default\n",
271 mmc_clk_dly[cmod].mod_str);
272 } else {
273 mmc_clk_dly[cmod].cmd_drv_ph = in_clk_dly[0];
274 mmc_clk_dly[cmod].dat_drv_ph = in_clk_dly[1];
275 /*mmc_clk_dly[cmod].sam_dly = in_clk_dly[2]; */
276 /*mmc_clk_dly[cmod].ds_dly = in_clk_dly[3]; */
277 mmc_clk_dly[cmod].sam_ph = in_clk_dly[4];
278 SM_DBG(mmc_dev(host->mmc), "Get %s clk dly ok\n",
279 mmc_clk_dly[cmod].mod_str);
280
281 }
282
283 SM_DBG(mmc_dev(host->mmc), "Try set %s clk dly ok\n",
284 mmc_clk_dly[cmod].mod_str);
285 SM_DBG(mmc_dev(host->mmc), "cmd_drv_ph %d\n",
286 mmc_clk_dly[cmod].cmd_drv_ph);
287 SM_DBG(mmc_dev(host->mmc), "dat_drv_ph %d\n",
288 mmc_clk_dly[cmod].dat_drv_ph);
289 /*SM_DBG(mmc_dev(host->mmc),
290 * "sam_dly %d\n",mmc_clk_dly[cmod].sam_dly);
291 */
292 /*SM_DBG(mmc_dev(host->mmc),
293 * "ds_dly %d\n",mmc_clk_dly[cmod].ds_dly);
294 */
295 SM_DBG(mmc_dev(host->mmc), "sam_ph %d\n",
296 mmc_clk_dly[cmod].sam_ph);
297
298 rval = mmc_readl(host, REG_DRV_DL);
299 if (mmc_clk_dly[cmod].cmd_drv_ph)
300 rval |= SDXC_CMD_DRV_PH_SEL; /*180 phase */
301 else
302 rval &= ~SDXC_CMD_DRV_PH_SEL; /*90 phase */
303
304
305 if (mmc_clk_dly[cmod].dat_drv_ph)
306 rval |= SDXC_DAT_DRV_PH_SEL; /*180 phase */
307 else
308 rval &= ~SDXC_DAT_DRV_PH_SEL; /*90 phase */
309
310 sunxi_r_op(host, mmc_writel(host, REG_DRV_DL, rval));
311
312 /*
313 * rval = mmc_readl(host,REG_SAMP_DL);
314 * rval &= ~SDXC_SAMP_DL_SW_MASK;
315 * rval |= mmc_clk_dly[cmod].sam_dly & SDXC_SAMP_DL_SW_MASK;
316 * rval |= SDXC_SAMP_DL_SW_EN;
317 * mmc_writel(host,REG_SAMP_DL,rval);
318 *
319 * rval = mmc_readl(host,REG_DS_DL);
320 * rval &= ~SDXC_DS_DL_SW_MASK;
321 * rval |= mmc_clk_dly[cmod].ds_dly & SDXC_DS_DL_SW_MASK;
322 * rval |= SDXC_DS_DL_SW_EN;
323 * mmc_writel(host,REG_DS_DL,rval);
324 */
325
326 rval = mmc_readl(host, REG_SD_NTSR);
327 rval &= ~SDXC_SAM_TIMING_PH_MASK;
328 rval |=
329 (mmc_clk_dly[cmod].
330 sam_ph << SDXC_SAM_TIMING_PH_SHIFT) & SDXC_SAM_TIMING_PH_MASK;
331 mmc_writel(host, REG_SD_NTSR, rval);
332
333 SM_DBG(mmc_dev(host->mmc), " REG_DRV_DL %08x\n",
334 mmc_readl(host, REG_DRV_DL));
335 SM_DBG(mmc_dev(host->mmc), " REG_SAMP_DL %08x\n",
336 mmc_readl(host, REG_SAMP_DL));
337 SM_DBG(mmc_dev(host->mmc), " REG_DS_DL %08x\n",
338 mmc_readl(host, REG_DS_DL));
339
340 }
341
__sunxi_mmc_do_oclk_onoff(struct sunxi_mmc_host * host,u32 oclk_en,u32 pwr_save,u32 ignore_dat0)342 static int __sunxi_mmc_do_oclk_onoff(struct sunxi_mmc_host *host, u32 oclk_en,
343 u32 pwr_save, u32 ignore_dat0)
344 {
345 unsigned long expire = jiffies + msecs_to_jiffies(250);
346 u32 rval;
347
348 rval = mmc_readl(host, REG_CLKCR);
349 rval &= ~(SDXC_CARD_CLOCK_ON | SDXC_LOW_POWER_ON | SDXC_MASK_DATA0);
350
351 if (oclk_en)
352 rval |= SDXC_CARD_CLOCK_ON;
353 if (pwr_save)
354 rval |= SDXC_LOW_POWER_ON;
355 if (ignore_dat0)
356 rval |= SDXC_MASK_DATA0;
357
358 mmc_writel(host, REG_CLKCR, rval);
359
360 SM_DBG(mmc_dev(host->mmc), "%s REG_CLKCR:%x\n", __func__,
361 mmc_readl(host, REG_CLKCR));
362
363 rval = SDXC_START | SDXC_UPCLK_ONLY | SDXC_WAIT_PRE_OVER;
364 mmc_writel(host, REG_CMDR, rval);
365
366 do {
367 rval = mmc_readl(host, REG_CMDR);
368 } while (time_before(jiffies, expire) && (rval & SDXC_START));
369
370 /* clear irq status bits set by the command */
371 /*? */
372 mmc_writel(host, REG_RINTR,
373 mmc_readl(host, REG_RINTR) & ~SDXC_SDIO_INTERRUPT);
374
375 if (rval & SDXC_START) {
376 SM_ERR(mmc_dev(host->mmc), "fatal err update clk timeout\n");
377 return -EIO;
378 }
379
380 /*only use mask data0 when update clk,clear it when not update clk */
381 if (ignore_dat0)
382 mmc_writel(host, REG_CLKCR,
383 mmc_readl(host, REG_CLKCR) & ~SDXC_MASK_DATA0);
384
385 return 0;
386 }
387
sunxi_mmc_oclk_onoff(struct sunxi_mmc_host * host,u32 oclk_en)388 static int sunxi_mmc_oclk_onoff(struct sunxi_mmc_host *host, u32 oclk_en)
389 {
390 struct device_node *np = NULL;
391 struct mmc_host *mmc = host->mmc;
392 int pwr_save = 0;
393 int len = 0;
394
395 if (!mmc->parent || !mmc->parent->of_node) {
396 SM_ERR(mmc_dev(host->mmc),
397 "no dts to parse power save mode\n");
398 return -EIO;
399 }
400
401 np = mmc->parent->of_node;
402 if (of_find_property(np, "sunxi-power-save-mode", &len))
403 pwr_save = 1;
404 return __sunxi_mmc_do_oclk_onoff(host, oclk_en, pwr_save, 1);
405 }
406
sunxi_mmc_oclk_onoff_sdmmc0(struct sunxi_mmc_host * host,u32 oclk_en)407 int sunxi_mmc_oclk_onoff_sdmmc0(struct sunxi_mmc_host *host, u32 oclk_en)
408 {
409 return sunxi_mmc_oclk_onoff(host, oclk_en);
410 }
411
sunxi_mmc_2xmod_onoff(struct sunxi_mmc_host * host,u32 newmode_en)412 static void sunxi_mmc_2xmod_onoff(struct sunxi_mmc_host *host, u32 newmode_en)
413 {
414 u32 rval = mmc_readl(host, REG_SD_NTSR);
415
416 if (newmode_en)
417 rval |= SDXC_2X_TIMING_MODE;
418 else
419 rval &= ~SDXC_2X_TIMING_MODE;
420
421 mmc_writel(host, REG_SD_NTSR, rval);
422
423 SM_DBG(mmc_dev(host->mmc), "REG_SD_NTSR: 0x%08x ,val %x\n",
424 mmc_readl(host, REG_SD_NTSR), rval);
425 }
426
sunxi_mmc_clk_set_rate_for_sdmmc0(struct sunxi_mmc_host * host,struct mmc_ios * ios)427 int sunxi_mmc_clk_set_rate_for_sdmmc0(struct sunxi_mmc_host *host,
428 struct mmc_ios *ios)
429 {
430 u32 mod_clk = 0;
431 u32 src_clk = 0;
432 u32 rval = 0;
433 s32 err = 0;
434 u32 rate = 0;
435 char *sclk_name = NULL;
436 struct clk *mclk = host->clk_mmc;
437 struct clk *sclk = NULL;
438 struct device *dev = mmc_dev(host->mmc);
439 int div = 0;
440
441 if (ios->clock == 0) {
442 __sunxi_mmc_do_oclk_onoff(host, 0, 0, 1);
443 return 0;
444 }
445
446 if (sunxi_mmc_ddr_timing(ios->timing)) {
447 mod_clk = ios->clock << 2;
448 div = 1;
449 } else {
450 mod_clk = ios->clock << 1;
451 div = 0;
452 }
453
454 if (ios->clock <= 400000) {
455 sclk = clk_get(dev, "osc24m");
456 sclk_name = "osc24m";
457 } else {
458 sclk = clk_get(dev, "pll_periph");
459 sclk_name = "pll_periph";
460 }
461 if (IS_ERR(sclk)) {
462 SM_ERR(mmc_dev(host->mmc), "Error to get source clock %s\n",
463 sclk_name);
464 return -1;
465 }
466
467 sunxi_mmc_oclk_onoff(host, 0);
468
469 err = clk_set_parent(mclk, sclk);
470 if (err) {
471 SM_ERR(mmc_dev(host->mmc), "set parent failed\n");
472 clk_put(sclk);
473 return -1;
474 }
475
476 rate = clk_round_rate(mclk, mod_clk);
477
478 SM_DBG(mmc_dev(host->mmc), "get round rate %d\n", rate);
479
480 clk_disable_unprepare(host->clk_mmc);
481
482 err = clk_set_rate(mclk, rate);
483 if (err) {
484 SM_ERR(mmc_dev(host->mmc), "set mclk rate error, rate %dHz\n",
485 rate);
486 clk_put(sclk);
487 return -1;
488 }
489
490 rval = clk_prepare_enable(host->clk_mmc);
491 if (rval) {
492 SM_ERR(mmc_dev(host->mmc), "Enable mmc clk err %d\n", rval);
493 return -1;
494 }
495
496 src_clk = clk_get_rate(sclk);
497 clk_put(sclk);
498
499 SM_DBG(mmc_dev(host->mmc), "set round clock %d, soure clk is %d\n",
500 rate, src_clk);
501
502 #ifdef MMC_FPGA
503 if (sunxi_mmc_ddr_timing(ios->timing)) {
504 /* clear internal divider */
505 rval = mmc_readl(host, REG_CLKCR);
506 rval &= ~0xff;
507 rval |= 1;
508 } else {
509 /* support internal divide clock under fpga environment */
510 rval = mmc_readl(host, REG_CLKCR);
511 rval &= ~0xff;
512 rval |= 24000000 / mod_clk / 2; /* =24M/400K/2=0x1E */
513 }
514 mmc_writel(host, REG_CLKCR, rval);
515 SM_INFO(mmc_dev(host->mmc), "--FPGA REG_CLKCR: 0x%08x\n",
516 mmc_readl(host, REG_CLKCR));
517 #else
518 /* clear internal divider */
519 rval = mmc_readl(host, REG_CLKCR);
520 rval &= ~0xff;
521 rval |= div;
522 mmc_writel(host, REG_CLKCR, rval);
523 #endif
524
525 /*sunxi_of_parse_clk_dly(host); */
526 sunxi_mmc_2xmod_onoff(host, 1);
527
528 if (sunxi_mmc_ddr_timing(ios->timing))
529 ios->clock = rate >> 2;
530 else
531 ios->clock = rate >> 1;
532
533
534 sunxi_mmc_set_clk_dly(host, ios->clock, ios->bus_width, ios->timing);
535
536 return sunxi_mmc_oclk_onoff(host, 1);
537 }
538
sunxi_mmc_thld_ctl_for_sdmmc0(struct sunxi_mmc_host * host,struct mmc_ios * ios,struct mmc_data * data)539 void sunxi_mmc_thld_ctl_for_sdmmc0(struct sunxi_mmc_host *host,
540 struct mmc_ios *ios, struct mmc_data *data)
541 {
542 u32 bsz = data->blksz;
543 /*unit:byte */
544 /*u32 tdtl = (host->dma_tl & SDXC_TX_TL_MASK)<<2; */
545 /*unit:byte*/
546 u32 rdtl = ((host->dma_tl & SDXC_RX_TL_MASK) >> 16) << 2;
547 u32 rval = 0;
548
549 if ((data->flags & MMC_DATA_READ)
550 && (bsz <= SDXC_CARD_RD_THLD_SIZE)
551 /*((SDXC_FIFO_DETH<<2)-bsz) >= (rdtl) */
552 && ((SDXC_FIFO_DETH << 2) >= (rdtl + bsz))
553 && (ios->timing == MMC_TIMING_MMC_HS200)) {
554 rval = mmc_readl(host, REG_THLD);
555 rval &= ~SDXC_CARD_RD_THLD_MASK;
556 rval |= data->blksz << SDXC_CARD_RD_THLD_SIZE_SHIFT;
557 rval |= SDXC_CARD_RD_THLD_ENB;
558 mmc_writel(host, REG_THLD, rval);
559 } else {
560 rval = mmc_readl(host, REG_THLD);
561 rval &= ~SDXC_CARD_RD_THLD_ENB;
562 mmc_writel(host, REG_THLD, rval);
563 }
564
565 SM_DBG(mmc_dev(host->mmc), "--SDXC_REG_THLD: 0x%08x\n",
566 mmc_readl(host, REG_THLD));
567
568 }
569
sunxi_mmc_save_spec_reg0(struct sunxi_mmc_host * host)570 void sunxi_mmc_save_spec_reg0(struct sunxi_mmc_host *host)
571 {
572 bak_spec_regs.drv_dl = mmc_readl(host, REG_DRV_DL);
573 bak_spec_regs.samp_dl = mmc_readl(host, REG_SAMP_DL);
574 bak_spec_regs.ds_dl = mmc_readl(host, REG_DS_DL);
575 bak_spec_regs.sd_ntsr = mmc_readl(host, REG_SD_NTSR);
576 }
577
sunxi_mmc_restore_spec_reg0(struct sunxi_mmc_host * host)578 void sunxi_mmc_restore_spec_reg0(struct sunxi_mmc_host *host)
579 {
580 sunxi_r_op(host, mmc_writel(host, REG_DRV_DL, bak_spec_regs.drv_dl));
581 mmc_writel(host, REG_SAMP_DL, bak_spec_regs.samp_dl);
582 mmc_writel(host, REG_DS_DL, bak_spec_regs.ds_dl);
583 mmc_writel(host, REG_SD_NTSR, bak_spec_regs.sd_ntsr);
584 }
585
586 #endif
587