linux/drivers/mmc/host/dw_mmc-exynos.c
<<
>>
Prefs
   1/*
   2 * Exynos Specific Extensions for Synopsys DW Multimedia Card Interface driver
   3 *
   4 * Copyright (C) 2012, Samsung Electronics Co., Ltd.
   5 *
   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.
  10 */
  11
  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/of.h>
  18#include <linux/of_gpio.h>
  19#include <linux/pm_runtime.h>
  20#include <linux/slab.h>
  21
  22#include "dw_mmc.h"
  23#include "dw_mmc-pltfm.h"
  24#include "dw_mmc-exynos.h"
  25
  26/* Variations in Exynos specific dw-mshc controller */
  27enum dw_mci_exynos_type {
  28        DW_MCI_TYPE_EXYNOS4210,
  29        DW_MCI_TYPE_EXYNOS4412,
  30        DW_MCI_TYPE_EXYNOS5250,
  31        DW_MCI_TYPE_EXYNOS5420,
  32        DW_MCI_TYPE_EXYNOS5420_SMU,
  33        DW_MCI_TYPE_EXYNOS7,
  34        DW_MCI_TYPE_EXYNOS7_SMU,
  35};
  36
  37/* Exynos implementation specific driver private data */
  38struct dw_mci_exynos_priv_data {
  39        enum dw_mci_exynos_type         ctrl_type;
  40        u8                              ciu_div;
  41        u32                             sdr_timing;
  42        u32                             ddr_timing;
  43        u32                             hs400_timing;
  44        u32                             tuned_sample;
  45        u32                             cur_speed;
  46        u32                             dqs_delay;
  47        u32                             saved_dqs_en;
  48        u32                             saved_strobe_ctrl;
  49};
  50
  51static struct dw_mci_exynos_compatible {
  52        char                            *compatible;
  53        enum dw_mci_exynos_type         ctrl_type;
  54} exynos_compat[] = {
  55        {
  56                .compatible     = "samsung,exynos4210-dw-mshc",
  57                .ctrl_type      = DW_MCI_TYPE_EXYNOS4210,
  58        }, {
  59                .compatible     = "samsung,exynos4412-dw-mshc",
  60                .ctrl_type      = DW_MCI_TYPE_EXYNOS4412,
  61        }, {
  62                .compatible     = "samsung,exynos5250-dw-mshc",
  63                .ctrl_type      = DW_MCI_TYPE_EXYNOS5250,
  64        }, {
  65                .compatible     = "samsung,exynos5420-dw-mshc",
  66                .ctrl_type      = DW_MCI_TYPE_EXYNOS5420,
  67        }, {
  68                .compatible     = "samsung,exynos5420-dw-mshc-smu",
  69                .ctrl_type      = DW_MCI_TYPE_EXYNOS5420_SMU,
  70        }, {
  71                .compatible     = "samsung,exynos7-dw-mshc",
  72                .ctrl_type      = DW_MCI_TYPE_EXYNOS7,
  73        }, {
  74                .compatible     = "samsung,exynos7-dw-mshc-smu",
  75                .ctrl_type      = DW_MCI_TYPE_EXYNOS7_SMU,
  76        },
  77};
  78
  79static inline u8 dw_mci_exynos_get_ciu_div(struct dw_mci *host)
  80{
  81        struct dw_mci_exynos_priv_data *priv = host->priv;
  82
  83        if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4412)
  84                return EXYNOS4412_FIXED_CIU_CLK_DIV;
  85        else if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4210)
  86                return EXYNOS4210_FIXED_CIU_CLK_DIV;
  87        else if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
  88                        priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
  89                return SDMMC_CLKSEL_GET_DIV(mci_readl(host, CLKSEL64)) + 1;
  90        else
  91                return SDMMC_CLKSEL_GET_DIV(mci_readl(host, CLKSEL)) + 1;
  92}
  93
  94static void dw_mci_exynos_config_smu(struct dw_mci *host)
  95{
  96        struct dw_mci_exynos_priv_data *priv = host->priv;
  97
  98        /*
  99         * If Exynos is provided the Security management,
 100         * set for non-ecryption mode at this time.
 101         */
 102        if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS5420_SMU ||
 103                priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU) {
 104                mci_writel(host, MPSBEGIN0, 0);
 105                mci_writel(host, MPSEND0, SDMMC_ENDING_SEC_NR_MAX);
 106                mci_writel(host, MPSCTRL0, SDMMC_MPSCTRL_SECURE_WRITE_BIT |
 107                           SDMMC_MPSCTRL_NON_SECURE_READ_BIT |
 108                           SDMMC_MPSCTRL_VALID |
 109                           SDMMC_MPSCTRL_NON_SECURE_WRITE_BIT);
 110        }
 111}
 112
 113static int dw_mci_exynos_priv_init(struct dw_mci *host)
 114{
 115        struct dw_mci_exynos_priv_data *priv = host->priv;
 116
 117        dw_mci_exynos_config_smu(host);
 118
 119        if (priv->ctrl_type >= DW_MCI_TYPE_EXYNOS5420) {
 120                priv->saved_strobe_ctrl = mci_readl(host, HS400_DLINE_CTRL);
 121                priv->saved_dqs_en = mci_readl(host, HS400_DQS_EN);
 122                priv->saved_dqs_en |= AXI_NON_BLOCKING_WR;
 123                mci_writel(host, HS400_DQS_EN, priv->saved_dqs_en);
 124                if (!priv->dqs_delay)
 125                        priv->dqs_delay =
 126                                DQS_CTRL_GET_RD_DELAY(priv->saved_strobe_ctrl);
 127        }
 128
 129        host->bus_hz /= (priv->ciu_div + 1);
 130
 131        return 0;
 132}
 133
 134static void dw_mci_exynos_set_clksel_timing(struct dw_mci *host, u32 timing)
 135{
 136        struct dw_mci_exynos_priv_data *priv = host->priv;
 137        u32 clksel;
 138
 139        if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
 140                priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
 141                clksel = mci_readl(host, CLKSEL64);
 142        else
 143                clksel = mci_readl(host, CLKSEL);
 144
 145        clksel = (clksel & ~SDMMC_CLKSEL_TIMING_MASK) | timing;
 146
 147        if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
 148                priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
 149                mci_writel(host, CLKSEL64, clksel);
 150        else
 151                mci_writel(host, CLKSEL, clksel);
 152
 153        /*
 154         * Exynos4412 and Exynos5250 extends the use of CMD register with the
 155         * use of bit 29 (which is reserved on standard MSHC controllers) for
 156         * optionally bypassing the HOLD register for command and data. The
 157         * HOLD register should be bypassed in case there is no phase shift
 158         * applied on CMD/DATA that is sent to the card.
 159         */
 160        if (!SDMMC_CLKSEL_GET_DRV_WD3(clksel) && host->slot)
 161                set_bit(DW_MMC_CARD_NO_USE_HOLD, &host->slot->flags);
 162}
 163
 164#ifdef CONFIG_PM
 165static int dw_mci_exynos_runtime_resume(struct device *dev)
 166{
 167        struct dw_mci *host = dev_get_drvdata(dev);
 168
 169        dw_mci_exynos_config_smu(host);
 170        return dw_mci_runtime_resume(dev);
 171}
 172
 173/**
 174 * dw_mci_exynos_resume_noirq - Exynos-specific resume code
 175 *
 176 * On exynos5420 there is a silicon errata that will sometimes leave the
 177 * WAKEUP_INT bit in the CLKSEL register asserted.  This bit is 1 to indicate
 178 * that it fired and we can clear it by writing a 1 back.  Clear it to prevent
 179 * interrupts from going off constantly.
 180 *
 181 * We run this code on all exynos variants because it doesn't hurt.
 182 */
 183
 184static int dw_mci_exynos_resume_noirq(struct device *dev)
 185{
 186        struct dw_mci *host = dev_get_drvdata(dev);
 187        struct dw_mci_exynos_priv_data *priv = host->priv;
 188        u32 clksel;
 189
 190        if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
 191                priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
 192                clksel = mci_readl(host, CLKSEL64);
 193        else
 194                clksel = mci_readl(host, CLKSEL);
 195
 196        if (clksel & SDMMC_CLKSEL_WAKEUP_INT) {
 197                if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
 198                        priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
 199                        mci_writel(host, CLKSEL64, clksel);
 200                else
 201                        mci_writel(host, CLKSEL, clksel);
 202        }
 203
 204        return 0;
 205}
 206#else
 207#define dw_mci_exynos_resume_noirq      NULL
 208#endif /* CONFIG_PM */
 209
 210static void dw_mci_exynos_config_hs400(struct dw_mci *host, u32 timing)
 211{
 212        struct dw_mci_exynos_priv_data *priv = host->priv;
 213        u32 dqs, strobe;
 214
 215        /*
 216         * Not supported to configure register
 217         * related to HS400
 218         */
 219        if (priv->ctrl_type < DW_MCI_TYPE_EXYNOS5420) {
 220                if (timing == MMC_TIMING_MMC_HS400)
 221                        dev_warn(host->dev,
 222                                 "cannot configure HS400, unsupported chipset\n");
 223                return;
 224        }
 225
 226        dqs = priv->saved_dqs_en;
 227        strobe = priv->saved_strobe_ctrl;
 228
 229        if (timing == MMC_TIMING_MMC_HS400) {
 230                dqs |= DATA_STROBE_EN;
 231                strobe = DQS_CTRL_RD_DELAY(strobe, priv->dqs_delay);
 232        } else {
 233                dqs &= ~DATA_STROBE_EN;
 234        }
 235
 236        mci_writel(host, HS400_DQS_EN, dqs);
 237        mci_writel(host, HS400_DLINE_CTRL, strobe);
 238}
 239
 240static void dw_mci_exynos_adjust_clock(struct dw_mci *host, unsigned int wanted)
 241{
 242        struct dw_mci_exynos_priv_data *priv = host->priv;
 243        unsigned long actual;
 244        u8 div;
 245        int ret;
 246        /*
 247         * Don't care if wanted clock is zero or
 248         * ciu clock is unavailable
 249         */
 250        if (!wanted || IS_ERR(host->ciu_clk))
 251                return;
 252
 253        /* Guaranteed minimum frequency for cclkin */
 254        if (wanted < EXYNOS_CCLKIN_MIN)
 255                wanted = EXYNOS_CCLKIN_MIN;
 256
 257        if (wanted == priv->cur_speed)
 258                return;
 259
 260        div = dw_mci_exynos_get_ciu_div(host);
 261        ret = clk_set_rate(host->ciu_clk, wanted * div);
 262        if (ret)
 263                dev_warn(host->dev,
 264                        "failed to set clk-rate %u error: %d\n",
 265                        wanted * div, ret);
 266        actual = clk_get_rate(host->ciu_clk);
 267        host->bus_hz = actual / div;
 268        priv->cur_speed = wanted;
 269        host->current_speed = 0;
 270}
 271
 272static void dw_mci_exynos_set_ios(struct dw_mci *host, struct mmc_ios *ios)
 273{
 274        struct dw_mci_exynos_priv_data *priv = host->priv;
 275        unsigned int wanted = ios->clock;
 276        u32 timing = ios->timing, clksel;
 277
 278        switch (timing) {
 279        case MMC_TIMING_MMC_HS400:
 280                /* Update tuned sample timing */
 281                clksel = SDMMC_CLKSEL_UP_SAMPLE(
 282                                priv->hs400_timing, priv->tuned_sample);
 283                wanted <<= 1;
 284                break;
 285        case MMC_TIMING_MMC_DDR52:
 286                clksel = priv->ddr_timing;
 287                /* Should be double rate for DDR mode */
 288                if (ios->bus_width == MMC_BUS_WIDTH_8)
 289                        wanted <<= 1;
 290                break;
 291        default:
 292                clksel = priv->sdr_timing;
 293        }
 294
 295        /* Set clock timing for the requested speed mode*/
 296        dw_mci_exynos_set_clksel_timing(host, clksel);
 297
 298        /* Configure setting for HS400 */
 299        dw_mci_exynos_config_hs400(host, timing);
 300
 301        /* Configure clock rate */
 302        dw_mci_exynos_adjust_clock(host, wanted);
 303}
 304
 305static int dw_mci_exynos_parse_dt(struct dw_mci *host)
 306{
 307        struct dw_mci_exynos_priv_data *priv;
 308        struct device_node *np = host->dev->of_node;
 309        u32 timing[2];
 310        u32 div = 0;
 311        int idx;
 312        int ret;
 313
 314        priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL);
 315        if (!priv)
 316                return -ENOMEM;
 317
 318        for (idx = 0; idx < ARRAY_SIZE(exynos_compat); idx++) {
 319                if (of_device_is_compatible(np, exynos_compat[idx].compatible))
 320                        priv->ctrl_type = exynos_compat[idx].ctrl_type;
 321        }
 322
 323        if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4412)
 324                priv->ciu_div = EXYNOS4412_FIXED_CIU_CLK_DIV - 1;
 325        else if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4210)
 326                priv->ciu_div = EXYNOS4210_FIXED_CIU_CLK_DIV - 1;
 327        else {
 328                of_property_read_u32(np, "samsung,dw-mshc-ciu-div", &div);
 329                priv->ciu_div = div;
 330        }
 331
 332        ret = of_property_read_u32_array(np,
 333                        "samsung,dw-mshc-sdr-timing", timing, 2);
 334        if (ret)
 335                return ret;
 336
 337        priv->sdr_timing = SDMMC_CLKSEL_TIMING(timing[0], timing[1], div);
 338
 339        ret = of_property_read_u32_array(np,
 340                        "samsung,dw-mshc-ddr-timing", timing, 2);
 341        if (ret)
 342                return ret;
 343
 344        priv->ddr_timing = SDMMC_CLKSEL_TIMING(timing[0], timing[1], div);
 345
 346        ret = of_property_read_u32_array(np,
 347                        "samsung,dw-mshc-hs400-timing", timing, 2);
 348        if (!ret && of_property_read_u32(np,
 349                                "samsung,read-strobe-delay", &priv->dqs_delay))
 350                dev_dbg(host->dev,
 351                        "read-strobe-delay is not found, assuming usage of default value\n");
 352
 353        priv->hs400_timing = SDMMC_CLKSEL_TIMING(timing[0], timing[1],
 354                                                HS400_FIXED_CIU_CLK_DIV);
 355        host->priv = priv;
 356        return 0;
 357}
 358
 359static inline u8 dw_mci_exynos_get_clksmpl(struct dw_mci *host)
 360{
 361        struct dw_mci_exynos_priv_data *priv = host->priv;
 362
 363        if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
 364                priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
 365                return SDMMC_CLKSEL_CCLK_SAMPLE(mci_readl(host, CLKSEL64));
 366        else
 367                return SDMMC_CLKSEL_CCLK_SAMPLE(mci_readl(host, CLKSEL));
 368}
 369
 370static inline void dw_mci_exynos_set_clksmpl(struct dw_mci *host, u8 sample)
 371{
 372        u32 clksel;
 373        struct dw_mci_exynos_priv_data *priv = host->priv;
 374
 375        if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
 376                priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
 377                clksel = mci_readl(host, CLKSEL64);
 378        else
 379                clksel = mci_readl(host, CLKSEL);
 380        clksel = SDMMC_CLKSEL_UP_SAMPLE(clksel, sample);
 381        if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
 382                priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
 383                mci_writel(host, CLKSEL64, clksel);
 384        else
 385                mci_writel(host, CLKSEL, clksel);
 386}
 387
 388static inline u8 dw_mci_exynos_move_next_clksmpl(struct dw_mci *host)
 389{
 390        struct dw_mci_exynos_priv_data *priv = host->priv;
 391        u32 clksel;
 392        u8 sample;
 393
 394        if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
 395                priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
 396                clksel = mci_readl(host, CLKSEL64);
 397        else
 398                clksel = mci_readl(host, CLKSEL);
 399
 400        sample = (clksel + 1) & 0x7;
 401        clksel = SDMMC_CLKSEL_UP_SAMPLE(clksel, sample);
 402
 403        if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
 404                priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
 405                mci_writel(host, CLKSEL64, clksel);
 406        else
 407                mci_writel(host, CLKSEL, clksel);
 408
 409        return sample;
 410}
 411
 412static s8 dw_mci_exynos_get_best_clksmpl(u8 candiates)
 413{
 414        const u8 iter = 8;
 415        u8 __c;
 416        s8 i, loc = -1;
 417
 418        for (i = 0; i < iter; i++) {
 419                __c = ror8(candiates, i);
 420                if ((__c & 0xc7) == 0xc7) {
 421                        loc = i;
 422                        goto out;
 423                }
 424        }
 425
 426        for (i = 0; i < iter; i++) {
 427                __c = ror8(candiates, i);
 428                if ((__c & 0x83) == 0x83) {
 429                        loc = i;
 430                        goto out;
 431                }
 432        }
 433
 434out:
 435        return loc;
 436}
 437
 438static int dw_mci_exynos_execute_tuning(struct dw_mci_slot *slot, u32 opcode)
 439{
 440        struct dw_mci *host = slot->host;
 441        struct dw_mci_exynos_priv_data *priv = host->priv;
 442        struct mmc_host *mmc = slot->mmc;
 443        u8 start_smpl, smpl, candiates = 0;
 444        s8 found = -1;
 445        int ret = 0;
 446
 447        start_smpl = dw_mci_exynos_get_clksmpl(host);
 448
 449        do {
 450                mci_writel(host, TMOUT, ~0);
 451                smpl = dw_mci_exynos_move_next_clksmpl(host);
 452
 453                if (!mmc_send_tuning(mmc, opcode, NULL))
 454                        candiates |= (1 << smpl);
 455
 456        } while (start_smpl != smpl);
 457
 458        found = dw_mci_exynos_get_best_clksmpl(candiates);
 459        if (found >= 0) {
 460                dw_mci_exynos_set_clksmpl(host, found);
 461                priv->tuned_sample = found;
 462        } else {
 463                ret = -EIO;
 464        }
 465
 466        return ret;
 467}
 468
 469static int dw_mci_exynos_prepare_hs400_tuning(struct dw_mci *host,
 470                                        struct mmc_ios *ios)
 471{
 472        struct dw_mci_exynos_priv_data *priv = host->priv;
 473
 474        dw_mci_exynos_set_clksel_timing(host, priv->hs400_timing);
 475        dw_mci_exynos_adjust_clock(host, (ios->clock) << 1);
 476
 477        return 0;
 478}
 479
 480/* Common capabilities of Exynos4/Exynos5 SoC */
 481static unsigned long exynos_dwmmc_caps[4] = {
 482        MMC_CAP_1_8V_DDR | MMC_CAP_8_BIT_DATA | MMC_CAP_CMD23,
 483        MMC_CAP_CMD23,
 484        MMC_CAP_CMD23,
 485        MMC_CAP_CMD23,
 486};
 487
 488static const struct dw_mci_drv_data exynos_drv_data = {
 489        .caps                   = exynos_dwmmc_caps,
 490        .init                   = dw_mci_exynos_priv_init,
 491        .set_ios                = dw_mci_exynos_set_ios,
 492        .parse_dt               = dw_mci_exynos_parse_dt,
 493        .execute_tuning         = dw_mci_exynos_execute_tuning,
 494        .prepare_hs400_tuning   = dw_mci_exynos_prepare_hs400_tuning,
 495};
 496
 497static const struct of_device_id dw_mci_exynos_match[] = {
 498        { .compatible = "samsung,exynos4412-dw-mshc",
 499                        .data = &exynos_drv_data, },
 500        { .compatible = "samsung,exynos5250-dw-mshc",
 501                        .data = &exynos_drv_data, },
 502        { .compatible = "samsung,exynos5420-dw-mshc",
 503                        .data = &exynos_drv_data, },
 504        { .compatible = "samsung,exynos5420-dw-mshc-smu",
 505                        .data = &exynos_drv_data, },
 506        { .compatible = "samsung,exynos7-dw-mshc",
 507                        .data = &exynos_drv_data, },
 508        { .compatible = "samsung,exynos7-dw-mshc-smu",
 509                        .data = &exynos_drv_data, },
 510        {},
 511};
 512MODULE_DEVICE_TABLE(of, dw_mci_exynos_match);
 513
 514static int dw_mci_exynos_probe(struct platform_device *pdev)
 515{
 516        const struct dw_mci_drv_data *drv_data;
 517        const struct of_device_id *match;
 518        int ret;
 519
 520        match = of_match_node(dw_mci_exynos_match, pdev->dev.of_node);
 521        drv_data = match->data;
 522
 523        pm_runtime_get_noresume(&pdev->dev);
 524        pm_runtime_set_active(&pdev->dev);
 525        pm_runtime_enable(&pdev->dev);
 526
 527        ret = dw_mci_pltfm_register(pdev, drv_data);
 528        if (ret) {
 529                pm_runtime_disable(&pdev->dev);
 530                pm_runtime_set_suspended(&pdev->dev);
 531                pm_runtime_put_noidle(&pdev->dev);
 532
 533                return ret;
 534        }
 535
 536        return 0;
 537}
 538
 539static int dw_mci_exynos_remove(struct platform_device *pdev)
 540{
 541        pm_runtime_disable(&pdev->dev);
 542        pm_runtime_set_suspended(&pdev->dev);
 543        pm_runtime_put_noidle(&pdev->dev);
 544
 545        return dw_mci_pltfm_remove(pdev);
 546}
 547
 548static const struct dev_pm_ops dw_mci_exynos_pmops = {
 549        SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
 550                                pm_runtime_force_resume)
 551        SET_RUNTIME_PM_OPS(dw_mci_runtime_suspend,
 552                           dw_mci_exynos_runtime_resume,
 553                           NULL)
 554        .resume_noirq = dw_mci_exynos_resume_noirq,
 555        .thaw_noirq = dw_mci_exynos_resume_noirq,
 556        .restore_noirq = dw_mci_exynos_resume_noirq,
 557};
 558
 559static struct platform_driver dw_mci_exynos_pltfm_driver = {
 560        .probe          = dw_mci_exynos_probe,
 561        .remove         = dw_mci_exynos_remove,
 562        .driver         = {
 563                .name           = "dwmmc_exynos",
 564                .of_match_table = dw_mci_exynos_match,
 565                .pm             = &dw_mci_exynos_pmops,
 566        },
 567};
 568
 569module_platform_driver(dw_mci_exynos_pltfm_driver);
 570
 571MODULE_DESCRIPTION("Samsung Specific DW-MSHC Driver Extension");
 572MODULE_AUTHOR("Thomas Abraham <thomas.ab@samsung.com");
 573MODULE_LICENSE("GPL v2");
 574MODULE_ALIAS("platform:dwmmc_exynos");
 575