root/drivers/mmc/host/dw_mmc-exynos.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. dw_mci_exynos_get_ciu_div
  2. dw_mci_exynos_config_smu
  3. dw_mci_exynos_priv_init
  4. dw_mci_exynos_set_clksel_timing
  5. dw_mci_exynos_runtime_resume
  6. dw_mci_exynos_suspend_noirq
  7. dw_mci_exynos_resume_noirq
  8. dw_mci_exynos_config_hs400
  9. dw_mci_exynos_adjust_clock
  10. dw_mci_exynos_set_ios
  11. dw_mci_exynos_parse_dt
  12. dw_mci_exynos_get_clksmpl
  13. dw_mci_exynos_set_clksmpl
  14. dw_mci_exynos_move_next_clksmpl
  15. dw_mci_exynos_get_best_clksmpl
  16. dw_mci_exynos_execute_tuning
  17. dw_mci_exynos_prepare_hs400_tuning
  18. dw_mci_exynos_probe
  19. dw_mci_exynos_remove

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

/* [<][>][^][v][top][bottom][index][help] */