2 * Rockchip Specific Extensions for Synopsys DW Multimedia Card Interface driver
4 * Copyright (C) 2014, Rockchip Electronics Co., Ltd.
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
12 #include <linux/module.h>
13 #include <linux/platform_device.h>
14 #include <linux/clk.h>
15 #include <linux/mmc/host.h>
16 #include <linux/mmc/mmc.h>
17 #include <linux/mmc/rk_mmc.h>
19 #include <linux/of_gpio.h>
20 #include <linux/slab.h>
21 #include <linux/rockchip/cpu.h>
22 #include <linux/rockchip/cru.h>
23 #include <linux/delay.h>
25 #include "dw_mmc-pltfm.h"
26 #include "../../clk/rockchip/clk-ops.h"
28 #include "rk_sdmmc_dbg.h"
32 * sdmmc,sdio0,sdio1,emmc id=0~3
33 * cclk_in_drv, cclk_in_sample i=0,1
36 static u32 cru_tuning_base = 0;
38 #define CRU_SDMMC_CON(id, tuning_type) (cru_tuning_base + ((id) * 8) + ((tuning_type) * 4))
40 #define MAX_DELAY_LINE (0xff)
41 #define FREQ_REF_150MHZ (150000000)
42 #define PRECISE_ADJUST (0)
44 #define SDMMC_TUNING_SEL(tuning_type) ( tuning_type? 10:11 )
45 #define SDMMC_TUNING_DELAYNUM(tuning_type) ( tuning_type? 2:3 )
46 #define SDMMC_TUNING_DEGREE(tuning_type) ( tuning_type? 0:1 )
47 #define SDMMC_TUNING_INIT_STATE (0)
50 SDMMC_SHIFT_DEGREE_0 = 0,
51 SDMMC_SHIFT_DEGREE_90,
52 SDMMC_SHIFT_DEGREE_180,
53 SDMMC_SHIFT_DEGREE_270,
54 SDMMC_SHIFT_DEGREE_INVALID,
57 const char *phase_desc[SDMMC_SHIFT_DEGREE_INVALID + 1] = {
58 "SDMMC_SHIFT_DEGREE_0",
59 "SDMMC_SHIFT_DEGREE_90",
60 "SDMMC_SHIFT_DEGREE_180",
61 "SDMMC_SHIFT_DEGREE_270",
62 "SDMMC_SHIFT_DEGREE_INVALID",
66 USE_CLK_AFTER_PHASE = 0,
67 USE_CLK_AFTER_PHASE_AND_DELAY_LINE = 1,
82 /* Variations in Rockchip specific dw-mshc controller */
83 enum dw_mci_rockchip_type {
90 /* Rockchip implementation specific driver private data */
91 struct dw_mci_rockchip_priv_data {
92 enum dw_mci_rockchip_type ctrl_type;
99 static struct dw_mci_rockchip_compatible {
101 enum dw_mci_rockchip_type ctrl_type;
102 } rockchip_compat[] = {
104 .compatible = "rockchip,rk31xx-sdmmc",
105 .ctrl_type = DW_MCI_TYPE_RK3188,
107 .compatible = "rockchip,rk32xx-sdmmc",
108 .ctrl_type = DW_MCI_TYPE_RK3288,
110 .compatible = "rockchip,rk3036-sdmmc",
111 .ctrl_type = DW_MCI_TYPE_RK3036,
113 .compatible = "rockchip,rk312x-sdmmc",
114 .ctrl_type = DW_MCI_TYPE_RK312X,
118 static int dw_mci_rockchip_priv_init(struct dw_mci *host)
120 struct dw_mci_rockchip_priv_data *priv;
123 priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL);
125 dev_err(host->dev, "mem alloc failed for private data\n");
129 for(idx = 0; idx < ARRAY_SIZE(rockchip_compat); idx++){
130 if(of_device_is_compatible(host->dev->of_node,
131 rockchip_compat[idx].compatible))
132 priv->ctrl_type = rockchip_compat[idx].ctrl_type;
139 static int dw_mci_rockchip_setup_clock(struct dw_mci *host)
141 struct dw_mci_rockchip_priv_data *priv = host->priv;
143 if ((priv->ctrl_type == DW_MCI_TYPE_RK3288) ||
144 (priv->ctrl_type == DW_MCI_TYPE_RK3036) ||
145 (priv->ctrl_type == DW_MCI_TYPE_RK312X))
146 host->bus_hz /= (priv->ciu_div + 1);
151 static void dw_mci_rockchip_prepare_command(struct dw_mci *host, u32 *cmdr)
156 static void dw_mci_rockchip_set_ios(struct dw_mci *host, struct mmc_ios *ios)
161 static int dw_mci_rockchip_parse_dt(struct dw_mci *host)
165 static inline u8 dw_mci_rockchip_get_delaynum(struct dw_mci *host, u8 con_id, u8 tuning_type)
170 regs = cru_readl(CRU_SDMMC_CON(con_id, tuning_type));
171 delaynum = ((regs>>SDMMC_TUNING_DELAYNUM(tuning_type)) & 0xff);
176 static inline void dw_mci_rockchip_set_delaynum(struct dw_mci *host, u8 con_id, u8 tuning_type, u8 delaynum)
179 regs = cru_readl(CRU_SDMMC_CON(con_id, tuning_type));
180 regs &= ~( 0xff << SDMMC_TUNING_DELAYNUM(tuning_type));
181 regs |= (delaynum << SDMMC_TUNING_DELAYNUM(tuning_type));
182 regs |= (0xff << (SDMMC_TUNING_DELAYNUM(tuning_type)+16));
184 MMC_DBG_INFO_FUNC(host->mmc,"tuning_result[delayline]: con_id = %d, tuning_type = %d, CRU_CON = 0x%x. [%s]",
185 con_id, tuning_type, regs, mmc_hostname(host->mmc));
187 cru_writel(regs, CRU_SDMMC_CON(con_id, tuning_type));
190 static inline void dw_mci_rockchip_set_degree(struct dw_mci *host, u8 con_id, u8 tuning_type, u8 phase)
194 regs = cru_readl(CRU_SDMMC_CON(con_id, tuning_type));
195 regs &= ~( 0x3 << SDMMC_TUNING_DEGREE(tuning_type));
196 regs |= (phase << SDMMC_TUNING_DEGREE(tuning_type));
197 regs |= (0x3 << (SDMMC_TUNING_DEGREE(tuning_type)+16));
199 MMC_DBG_INFO_FUNC(host->mmc,"tuning_result[phase]: con_id = %d, tuning_type= %d, CRU_CON = 0x%x. [%s]",
200 con_id, tuning_type, regs, mmc_hostname(host->mmc));
202 cru_writel(regs, CRU_SDMMC_CON(con_id, tuning_type));
205 static inline void dw_mci_rockchip_turning_sel(struct dw_mci *host, u8 con_id, u8 tuning_type, u8 mode)
208 regs = cru_readl(CRU_SDMMC_CON(con_id, tuning_type)) ;
209 regs &= ~( 0x1 << SDMMC_TUNING_SEL(tuning_type));
210 regs |= (mode << SDMMC_TUNING_SEL(tuning_type));
211 regs |= (0x1 << (SDMMC_TUNING_SEL(tuning_type)+16));
213 MMC_DBG_INFO_FUNC(host->mmc,"tuning_sel: con_id = %d, tuning_type = %d, CRU_CON = 0x%x. [%s]",
214 con_id, tuning_type, regs, mmc_hostname(host->mmc));
216 cru_writel(regs, CRU_SDMMC_CON(con_id, tuning_type));
220 static inline u8 dw_mci_rockchip_get_phase(struct dw_mci *host, u8 con_id, u8 tuning_type)
225 static inline u8 dw_mci_rockchip_move_next_clksmpl(struct dw_mci *host, u8 con_id, u8 tuning_type, u8 val)
229 regs = cru_readl(CRU_SDMMC_CON(con_id, tuning_type)) ;
232 val = ((regs>>SDMMC_TUNING_DELAYNUM(tuning_type)) & 0xff);
238 static void dw_mci_rockchip_load_signal_integrity(struct dw_mci *host, u32 sr, u32 drv)
240 if (unlikely((drv > IO_DRV_12MA) || (sr > SLEW_RATE_FAST))) {
241 MMC_DBG_ERR_FUNC(host->mmc,"wrong signal integrity setting: drv = %d, sr = %d ![%s]",
242 drv, sr, mmc_hostname(host->mmc));
247 /*Note 00: 2ma 01:4ma 10:8ma 11:12ma
248 For consider line loading and IP's slew rate,
249 we should match these by every board depends for signal integrity.
250 slew rate >= 2*pi*f*Vpeak = max(|d'(Vpeak)/dt|)
252 if (host->mmc->restrict_caps & RESTRICT_CARD_TYPE_SDIO) {
253 grf_writel(0xff005500 | (drv << 14) | (drv << 12) |
254 (drv << 10) | (drv << 8), 0x01f8); /* GPIO4C4-C7 */
255 grf_writel(0x000f0000 | (drv << 0) | (drv << 2), 0x01fc); /* GPIO4D0-D1 */
256 grf_writel(0x03f00000 | (sr << 4) | (sr << 5) | (sr << 6) |
257 (sr << 7) | (sr << 8) | (sr << 9) , 0x011c); /* slew rate*/
258 }else if (host->mmc->restrict_caps & RESTRICT_CARD_TYPE_SD) {
259 grf_writel(0x3fff0000 | (drv << 0) | (drv << 2) | (drv << 4) |
260 (drv << 6) | (drv << 8) | (drv << 10) |
261 (drv << 12), 0x0218); /* GPIO6C0-C6 */
262 grf_writel(0x003f0000 | (sr << 0) | (sr << 1) | (sr << 2) |
263 (sr << 3) | (sr << 4) | (sr << 5), 0x012c); /* slew rate */
264 }else if (host->mmc->restrict_caps & RESTRICT_CARD_TYPE_EMMC) {
265 /* emmc hardware relative addr match requirement, assume 4ma not slow slew rate */
266 grf_writel(0xffff5555, 0x01e0); /* GPIO3A0-A7 */
267 grf_writel(0x000c0006, 0x01e4); /* GPIO3B1 */
268 grf_writel(0x003f0015, 0x01e8); /* GPIO3C2-C0 */
273 static void dw_mci_rockchip_load_tuning_base(void)
275 /* load tuning base */
277 cru_tuning_base = RK3288_CRU_SDMMC_CON0;
280 static int inline __dw_mci_rockchip_execute_tuning(struct dw_mci_slot *slot, u32 opcode,
281 u8 *blk_test, unsigned int blksz)
283 struct dw_mci *host = slot->host;
284 struct mmc_host *mmc = slot->mmc;
285 struct mmc_request mrq = {NULL};
286 struct mmc_command cmd = {0};
287 struct mmc_command stop = {0};
288 struct mmc_data data = {0};
289 struct scatterlist sg;
293 cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
294 stop.opcode = MMC_STOP_TRANSMISSION;
296 stop.flags = MMC_RSP_R1B | MMC_CMD_AC;
299 data.flags = MMC_DATA_READ;
303 sg_init_one(&sg, blk_test, blksz);
308 mci_writel(host, TMOUT, ~0);
310 mmc_wait_for_req(mmc, &mrq);
311 if(!cmd.error && !data.error){
315 "Tuning error: cmd.error:%d, data.error:%d\n",cmd.error, data.error);
322 static int dw_mci_rockchip_execute_tuning(struct dw_mci_slot *slot, u32 opcode,
323 struct dw_mci_tuning_data *tuning_data)
326 struct dw_mci *host = slot->host;
328 u8 candidates_delayline[MAX_DELAY_LINE] = {0};
329 u8 candidates_degree[SDMMC_SHIFT_DEGREE_INVALID] = {4,4,4,4};
333 u32 start_delayline = 0;
334 const u8 *blk_pattern = tuning_data->blk_pattern;
338 unsigned int blksz = tuning_data->blksz;
340 MMC_DBG_INFO_FUNC(host->mmc,"execute tuning: [%s]", mmc_hostname(host->mmc));
342 dw_mci_rockchip_load_tuning_base();
344 blk_test = kmalloc(blksz, GFP_KERNEL);
347 MMC_DBG_ERR_FUNC(host->mmc,"execute tuning: blk_test kmalloc failed[%s]",
348 mmc_hostname(host->mmc));
352 /* Select use delay line*/
353 dw_mci_rockchip_turning_sel(host, tuning_data->con_id, tuning_data->tuning_type,
354 USE_CLK_AFTER_PHASE_AND_DELAY_LINE);
356 /* For RK32XX signoff 150M clk, 1 cycle = 6.66ns , and 1/4 phase = 1.66ns.
357 Netlist level sample LT: 10.531ns / 42.126ps WC: 19.695ns / 76.936ps.
358 So we take average --- 60ps, (1.66ns/ 2) = 0.83(middle-value),TAKE 0.9
359 0.9 / 60ps = 15 delayline
361 if (cpu_is_rk3288() && !(rockchip_get_cpu_version() > 0)) {
362 /* RK3288, non-eco */
363 ref = DIV_ROUND_UP(FREQ_REF_150MHZ, host->bus_hz);
366 if (step > MAX_DELAY_LINE) {
367 step = MAX_DELAY_LINE;
368 MMC_DBG_WARN_FUNC(host->mmc,
369 "execute tuning: TOO LARGE STEP![%s]", mmc_hostname(host->mmc));
371 MMC_DBG_INFO_FUNC(host->mmc,
372 "execute tuning: SOC is RK3288, ref = %d, step = %d[%s]",
373 ref, step, mmc_hostname(host->mmc));
376 step = (15 * ((FREQ_REF_150MHZ / host->bus_hz) * 100)) / 100;
378 if (step > MAX_DELAY_LINE) {
379 step = MAX_DELAY_LINE;
380 MMC_DBG_WARN_FUNC(host->mmc,
381 "execute tuning: TOO LARGE STEP![%s]", mmc_hostname(host->mmc));
383 MMC_DBG_INFO_FUNC(host->mmc,
384 "execute tuning: SOC is UNKNOWN, step = %d[%s]",
385 step, mmc_hostname(host->mmc));
389 /* calcute slew rate & drv strength in timing tuning */
390 if(host->mmc->restrict_caps & RESTRICT_CARD_TYPE_SD)
391 default_drv = IO_DRV_4MA;
393 default_drv = IO_DRV_8MA;
395 dw_mci_rockchip_load_signal_integrity(host, SLEW_RATE_SLOW, default_drv);
396 /* Loop degree from 0 ~ 270 */
397 for(start_degree = SDMMC_SHIFT_DEGREE_0; start_degree < SDMMC_SHIFT_DEGREE_270; start_degree++){
398 dw_mci_rockchip_set_degree(host, tuning_data->con_id, tuning_data->tuning_type, start_degree);
399 if(0 == __dw_mci_rockchip_execute_tuning(slot, opcode, blk_test, blksz)){
400 if(!memcmp(blk_pattern, blk_test, blksz)){
401 /* Successfully tuning in this condition*/
402 candidates_degree[index] = start_degree;
406 /* eMMC spec does not require a delay between tuning cycles
407 * but eMMC should be guaranteed to complete a sequence of 40 times CMD21
408 * withnin 150ms, some eMMC may limit 4ms gap between any two sequential CMD21
410 if (opcode == MMC_SEND_TUNING_BLOCK)
413 /* MMC_SEND_TUNING_BLOCK_HS200 */
417 MMC_DBG_BOOT_FUNC(host->mmc,"\n execute tuning: candidates_degree = %s \t%s \t%s \t%s[%s]",
418 phase_desc[candidates_degree[0]], phase_desc[candidates_degree[1]],
419 phase_desc[candidates_degree[2]], phase_desc[candidates_degree[3]],
420 mmc_hostname(host->mmc));
423 if((candidates_degree[0] == SDMMC_SHIFT_DEGREE_0)
424 && (candidates_degree[1] == SDMMC_SHIFT_DEGREE_90)
425 && (candidates_degree[2] == SDMMC_SHIFT_DEGREE_180)){
427 MMC_DBG_INFO_FUNC(host->mmc,
428 "execute tuning: candidates_degree = SDMMC_SHIFT_DEGREE_90 [%s]",
429 mmc_hostname(host->mmc));
431 dw_mci_rockchip_set_degree(host, tuning_data->con_id,
432 tuning_data->tuning_type, SDMMC_SHIFT_DEGREE_90);
435 }else if((candidates_degree[0] == SDMMC_SHIFT_DEGREE_90)
436 && (candidates_degree[1] == SDMMC_SHIFT_DEGREE_180)
437 && (candidates_degree[2] == SDMMC_SHIFT_DEGREE_270)){
438 MMC_DBG_INFO_FUNC(host->mmc,
439 "execute tuning: candidates_degree = SDMMC_SHIFT_DEGREE_180 [%s]",
440 mmc_hostname(host->mmc));
441 dw_mci_rockchip_set_degree(host, tuning_data->con_id,
442 tuning_data->tuning_type, SDMMC_SHIFT_DEGREE_180);
445 }else if((candidates_degree[0] == SDMMC_SHIFT_DEGREE_0)
446 && (candidates_degree[1] == SDMMC_SHIFT_DEGREE_90)
447 && (candidates_degree[2] == SDMMC_SHIFT_DEGREE_INVALID)){
449 MMC_DBG_INFO_FUNC(host->mmc,
450 "execute tuning: candidates_degree = SDMMC_SHIFT_DEGREE_0 ~ SDMMC_SHIFT_DEGREE_90[%s]",
451 mmc_hostname(host->mmc));
453 dw_mci_rockchip_set_degree(host, tuning_data->con_id, tuning_data->tuning_type, SDMMC_SHIFT_DEGREE_0);
457 dw_mci_rockchip_set_delaynum(host, tuning_data->con_id, tuning_data->tuning_type, step);
461 }else if((candidates_degree[0]==SDMMC_SHIFT_DEGREE_0)
462 && (candidates_degree[1]==SDMMC_SHIFT_DEGREE_180)){
464 MMC_DBG_INFO_FUNC(host->mmc,
465 "execute tuning: candidates_degree = SDMMC_SHIFT_DEGREE_0 AND SDMMC_SHIFT_DEGREE_180[%s]",
466 mmc_hostname(host->mmc));
468 /* FixMe: NO sense any signal indicator make this case happen*/
469 dw_mci_rockchip_set_degree(host, tuning_data->con_id, tuning_data->tuning_type, SDMMC_SHIFT_DEGREE_0);
471 }else if((candidates_degree[0] == SDMMC_SHIFT_DEGREE_90)
472 && (candidates_degree[1] == SDMMC_SHIFT_DEGREE_180)
473 && (candidates_degree[2] == SDMMC_SHIFT_DEGREE_INVALID)){
475 MMC_DBG_INFO_FUNC(host->mmc,
476 "execute tuning: candidates_degree = SDMMC_SHIFT_DEGREE_90 ~ SDMMC_SHIFT_DEGREE_180[%s]",
477 mmc_hostname(host->mmc));
479 dw_mci_rockchip_set_degree(host, tuning_data->con_id, tuning_data->tuning_type, SDMMC_SHIFT_DEGREE_90);
483 dw_mci_rockchip_set_delaynum(host, tuning_data->con_id, tuning_data->tuning_type, step);
487 }else if((candidates_degree[0] == SDMMC_SHIFT_DEGREE_180)
488 && (candidates_degree[1] == SDMMC_SHIFT_DEGREE_270)){
490 MMC_DBG_INFO_FUNC(host->mmc,
491 "execute tuning: candidates_degree = SDMMC_SHIFT_DEGREE_180 ~ SDMMC_SHIFT_DEGREE_270[%s]",
492 mmc_hostname(host->mmc));
494 dw_mci_rockchip_set_degree(host, tuning_data->con_id, tuning_data->tuning_type, SDMMC_SHIFT_DEGREE_180);
498 dw_mci_rockchip_set_delaynum(host, tuning_data->con_id, tuning_data->tuning_type, step);
502 }else if((candidates_degree[0] == SDMMC_SHIFT_DEGREE_180)
503 && (candidates_degree[1] == SDMMC_SHIFT_DEGREE_INVALID)){
505 MMC_DBG_INFO_FUNC(host->mmc,
506 "execute tuning: candidates_degree = [SDMMC_SHIFT_DEGREE_90 + n ~ SDMMC_SHIFT_DEGREE_180][%s]",
507 mmc_hostname(host->mmc));
509 dw_mci_rockchip_set_degree(host, tuning_data->con_id, tuning_data->tuning_type, SDMMC_SHIFT_DEGREE_90);
515 //dw_mci_rockchip_set_delaynum(host, tuning_data->con_id, tuning_data->tuning_type, step);
519 }else if((candidates_degree[0] == SDMMC_SHIFT_DEGREE_90)
520 && (candidates_degree[1] == SDMMC_SHIFT_DEGREE_INVALID)){
522 MMC_DBG_INFO_FUNC(host->mmc,
523 "execute tuning: candidates_degree = [SDMMC_SHIFT_DEGREE_0 + n ~ SDMMC_SHIFT_DEGREE_90][%s]",
524 mmc_hostname(host->mmc));
526 dw_mci_rockchip_set_degree(host, tuning_data->con_id, tuning_data->tuning_type, SDMMC_SHIFT_DEGREE_0);
532 //dw_mci_rockchip_set_delaynum(host, tuning_data->con_id, tuning_data->tuning_type, step);
536 }else if((candidates_degree[0] == SDMMC_SHIFT_DEGREE_270)){
538 MMC_DBG_INFO_FUNC(host->mmc,
539 "execute tuning: candidates_degree = SDMMC_SHIFT_DEGREE_270 [%s]",
540 mmc_hostname(host->mmc));
542 /*FixME: so urgly signal indicator, HW engineer help!*/
544 //dw_mci_rockchip_set_degree(host, tuning_data->con_id, tuning_data->tuning_type, SDMMC_SHIFT_DEGREE_180);
550 //dw_mci_rockchip_set_delaynum(host, tuning_data->con_id, tuning_data->tuning_type, step);
555 MMC_DBG_ERR_FUNC(host->mmc,
556 "execute tuning: candidates_degree beyong limited case! [%s]",
557 mmc_hostname(host->mmc));
560 if(host->mmc->restrict_caps & RESTRICT_CARD_TYPE_EMMC)
567 for(start_delayline = 0; start_delayline <= MAX_DELAY_LINE; start_delayline += step){
569 dw_mci_rockchip_set_delaynum(host, tuning_data->con_id,
570 tuning_data->tuning_type, start_delayline);
571 if(0 == __dw_mci_rockchip_execute_tuning(slot, opcode, blk_test, blksz)){
572 if(!memcmp(blk_pattern, blk_test, blksz)){
573 /* Successfully tuning in this condition*/
574 candidates_delayline[index] = start_delayline;
579 if((index < 2) && (index != 0)) {
580 /* setup 400ps, consider line loading, at least 600ps wc.
581 for 150M, 15 steps =900ps ,too larger scale, should step smaller in principle
583 MMC_DBG_INFO_FUNC(host->mmc,
584 "execute tuning: candidates_delayline failed for no enough elements [%s]",
585 mmc_hostname(host->mmc));
587 /* Make step smaller, and re-calculate */
591 }else if(index >= 2){
593 MMC_DBG_INFO_FUNC(host->mmc,
594 "execute tuning: candidates_delayline calculate successfully [%s]",
595 mmc_hostname(host->mmc));
597 dw_mci_rockchip_set_delaynum(host, tuning_data->con_id,
598 tuning_data->tuning_type, candidates_delayline[index/2]);
610 /* Common capabilities of RK32XX SoC */
611 static unsigned long rockchip_dwmmc_caps[4] = {
618 unsigned int rockchip_dwmmc_hold_reg[4] = {1,0,0,0};
620 static const struct dw_mci_drv_data rockchip_drv_data = {
621 .caps = rockchip_dwmmc_caps,
622 .hold_reg_flag = rockchip_dwmmc_hold_reg,
623 .init = dw_mci_rockchip_priv_init,
624 .setup_clock = dw_mci_rockchip_setup_clock,
625 .prepare_command = dw_mci_rockchip_prepare_command,
626 .set_ios = dw_mci_rockchip_set_ios,
627 .parse_dt = dw_mci_rockchip_parse_dt,
628 .execute_tuning = dw_mci_rockchip_execute_tuning,
631 static const struct of_device_id dw_mci_rockchip_match[] = {
632 { .compatible = "rockchip,rk_mmc",
633 .data = &rockchip_drv_data, },
636 MODULE_DEVICE_TABLE(of, dw_mci_rockchip_match);
638 static int dw_mci_rockchip_probe(struct platform_device *pdev)
640 const struct dw_mci_drv_data *drv_data;
641 const struct of_device_id *match;
643 match = of_match_node(dw_mci_rockchip_match, pdev->dev.of_node);
644 drv_data = match->data;
645 return dw_mci_pltfm_register(pdev, drv_data);
648 static struct platform_driver dw_mci_rockchip_pltfm_driver = {
649 .probe = dw_mci_rockchip_probe,
650 .remove = __exit_p(dw_mci_pltfm_remove),
652 .name = "dwmmc_rockchip",
653 .of_match_table = dw_mci_rockchip_match,
654 .pm = &dw_mci_pltfm_pmops,
658 module_platform_driver(dw_mci_rockchip_pltfm_driver);
660 MODULE_DESCRIPTION("Rockchip Specific DW-SDMMC Driver Extension");
661 MODULE_AUTHOR("Shawn Lin <lintao@rock-chips.com>");
662 MODULE_AUTHOR("Bangwang Xie <xbw@rock-chips.com>");
663 MODULE_LICENSE("GPL v2");
664 MODULE_ALIAS("platform:dwmmc-rockchip");