linux/drivers/mmc/host/sdhci-xenon.c
<<
>>
Prefs
   1/*
   2 * Driver for Marvell Xenon SDHC as a platform device
   3 *
   4 * Copyright (C) 2016 Marvell, All Rights Reserved.
   5 *
   6 * Author:      Hu Ziji <huziji@marvell.com>
   7 * Date:        2016-8-24
   8 *
   9 * This program is free software; you can redistribute it and/or
  10 * modify it under the terms of the GNU General Public License as
  11 * published by the Free Software Foundation version 2.
  12 *
  13 * Inspired by Jisheng Zhang <jszhang@marvell.com>
  14 * Special thanks to Video BG4 project team.
  15 */
  16
  17#include <linux/delay.h>
  18#include <linux/ktime.h>
  19#include <linux/module.h>
  20#include <linux/of.h>
  21#include <linux/pm.h>
  22#include <linux/pm_runtime.h>
  23
  24#include "sdhci-pltfm.h"
  25#include "sdhci-xenon.h"
  26
  27static int xenon_enable_internal_clk(struct sdhci_host *host)
  28{
  29        u32 reg;
  30        ktime_t timeout;
  31
  32        reg = sdhci_readl(host, SDHCI_CLOCK_CONTROL);
  33        reg |= SDHCI_CLOCK_INT_EN;
  34        sdhci_writel(host, reg, SDHCI_CLOCK_CONTROL);
  35        /* Wait max 20 ms */
  36        timeout = ktime_add_ms(ktime_get(), 20);
  37        while (!((reg = sdhci_readw(host, SDHCI_CLOCK_CONTROL))
  38                        & SDHCI_CLOCK_INT_STABLE)) {
  39                if (ktime_after(ktime_get(), timeout)) {
  40                        dev_err(mmc_dev(host->mmc), "Internal clock never stabilised.\n");
  41                        return -ETIMEDOUT;
  42                }
  43                usleep_range(900, 1100);
  44        }
  45
  46        return 0;
  47}
  48
  49/* Set SDCLK-off-while-idle */
  50static void xenon_set_sdclk_off_idle(struct sdhci_host *host,
  51                                     unsigned char sdhc_id, bool enable)
  52{
  53        u32 reg;
  54        u32 mask;
  55
  56        reg = sdhci_readl(host, XENON_SYS_OP_CTRL);
  57        /* Get the bit shift basing on the SDHC index */
  58        mask = (0x1 << (XENON_SDCLK_IDLEOFF_ENABLE_SHIFT + sdhc_id));
  59        if (enable)
  60                reg |= mask;
  61        else
  62                reg &= ~mask;
  63
  64        sdhci_writel(host, reg, XENON_SYS_OP_CTRL);
  65}
  66
  67/* Enable/Disable the Auto Clock Gating function */
  68static void xenon_set_acg(struct sdhci_host *host, bool enable)
  69{
  70        u32 reg;
  71
  72        reg = sdhci_readl(host, XENON_SYS_OP_CTRL);
  73        if (enable)
  74                reg &= ~XENON_AUTO_CLKGATE_DISABLE_MASK;
  75        else
  76                reg |= XENON_AUTO_CLKGATE_DISABLE_MASK;
  77        sdhci_writel(host, reg, XENON_SYS_OP_CTRL);
  78}
  79
  80/* Enable this SDHC */
  81static void xenon_enable_sdhc(struct sdhci_host *host,
  82                              unsigned char sdhc_id)
  83{
  84        u32 reg;
  85
  86        reg = sdhci_readl(host, XENON_SYS_OP_CTRL);
  87        reg |= (BIT(sdhc_id) << XENON_SLOT_ENABLE_SHIFT);
  88        sdhci_writel(host, reg, XENON_SYS_OP_CTRL);
  89
  90        host->mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY;
  91        /*
  92         * Force to clear BUS_TEST to
  93         * skip bus_test_pre and bus_test_post
  94         */
  95        host->mmc->caps &= ~MMC_CAP_BUS_WIDTH_TEST;
  96}
  97
  98/* Disable this SDHC */
  99static void xenon_disable_sdhc(struct sdhci_host *host,
 100                               unsigned char sdhc_id)
 101{
 102        u32 reg;
 103
 104        reg = sdhci_readl(host, XENON_SYS_OP_CTRL);
 105        reg &= ~(BIT(sdhc_id) << XENON_SLOT_ENABLE_SHIFT);
 106        sdhci_writel(host, reg, XENON_SYS_OP_CTRL);
 107}
 108
 109/* Enable Parallel Transfer Mode */
 110static void xenon_enable_sdhc_parallel_tran(struct sdhci_host *host,
 111                                            unsigned char sdhc_id)
 112{
 113        u32 reg;
 114
 115        reg = sdhci_readl(host, XENON_SYS_EXT_OP_CTRL);
 116        reg |= BIT(sdhc_id);
 117        sdhci_writel(host, reg, XENON_SYS_EXT_OP_CTRL);
 118}
 119
 120/* Mask command conflict error */
 121static void xenon_mask_cmd_conflict_err(struct sdhci_host *host)
 122{
 123        u32  reg;
 124
 125        reg = sdhci_readl(host, XENON_SYS_EXT_OP_CTRL);
 126        reg |= XENON_MASK_CMD_CONFLICT_ERR;
 127        sdhci_writel(host, reg, XENON_SYS_EXT_OP_CTRL);
 128}
 129
 130static void xenon_retune_setup(struct sdhci_host *host)
 131{
 132        struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
 133        struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
 134        u32 reg;
 135
 136        /* Disable the Re-Tuning Request functionality */
 137        reg = sdhci_readl(host, XENON_SLOT_RETUNING_REQ_CTRL);
 138        reg &= ~XENON_RETUNING_COMPATIBLE;
 139        sdhci_writel(host, reg, XENON_SLOT_RETUNING_REQ_CTRL);
 140
 141        /* Disable the Re-tuning Interrupt */
 142        reg = sdhci_readl(host, SDHCI_SIGNAL_ENABLE);
 143        reg &= ~SDHCI_INT_RETUNE;
 144        sdhci_writel(host, reg, SDHCI_SIGNAL_ENABLE);
 145        reg = sdhci_readl(host, SDHCI_INT_ENABLE);
 146        reg &= ~SDHCI_INT_RETUNE;
 147        sdhci_writel(host, reg, SDHCI_INT_ENABLE);
 148
 149        /* Force to use Tuning Mode 1 */
 150        host->tuning_mode = SDHCI_TUNING_MODE_1;
 151        /* Set re-tuning period */
 152        host->tuning_count = 1 << (priv->tuning_count - 1);
 153}
 154
 155/*
 156 * Operations inside struct sdhci_ops
 157 */
 158/* Recover the Register Setting cleared during SOFTWARE_RESET_ALL */
 159static void xenon_reset_exit(struct sdhci_host *host,
 160                             unsigned char sdhc_id, u8 mask)
 161{
 162        /* Only SOFTWARE RESET ALL will clear the register setting */
 163        if (!(mask & SDHCI_RESET_ALL))
 164                return;
 165
 166        /* Disable tuning request and auto-retuning again */
 167        xenon_retune_setup(host);
 168
 169        xenon_set_acg(host, true);
 170
 171        xenon_set_sdclk_off_idle(host, sdhc_id, false);
 172
 173        xenon_mask_cmd_conflict_err(host);
 174}
 175
 176static void xenon_reset(struct sdhci_host *host, u8 mask)
 177{
 178        struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
 179        struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
 180
 181        sdhci_reset(host, mask);
 182        xenon_reset_exit(host, priv->sdhc_id, mask);
 183}
 184
 185/*
 186 * Xenon defines different values for HS200 and HS400
 187 * in Host_Control_2
 188 */
 189static void xenon_set_uhs_signaling(struct sdhci_host *host,
 190                                    unsigned int timing)
 191{
 192        u16 ctrl_2;
 193
 194        ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
 195        /* Select Bus Speed Mode for host */
 196        ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
 197        if (timing == MMC_TIMING_MMC_HS200)
 198                ctrl_2 |= XENON_CTRL_HS200;
 199        else if (timing == MMC_TIMING_UHS_SDR104)
 200                ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
 201        else if (timing == MMC_TIMING_UHS_SDR12)
 202                ctrl_2 |= SDHCI_CTRL_UHS_SDR12;
 203        else if (timing == MMC_TIMING_UHS_SDR25)
 204                ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
 205        else if (timing == MMC_TIMING_UHS_SDR50)
 206                ctrl_2 |= SDHCI_CTRL_UHS_SDR50;
 207        else if ((timing == MMC_TIMING_UHS_DDR50) ||
 208                 (timing == MMC_TIMING_MMC_DDR52))
 209                ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
 210        else if (timing == MMC_TIMING_MMC_HS400)
 211                ctrl_2 |= XENON_CTRL_HS400;
 212        sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
 213}
 214
 215static void xenon_set_power(struct sdhci_host *host, unsigned char mode,
 216                unsigned short vdd)
 217{
 218        struct mmc_host *mmc = host->mmc;
 219        u8 pwr = host->pwr;
 220
 221        sdhci_set_power_noreg(host, mode, vdd);
 222
 223        if (host->pwr == pwr)
 224                return;
 225
 226        if (host->pwr == 0)
 227                vdd = 0;
 228
 229        if (!IS_ERR(mmc->supply.vmmc))
 230                mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, vdd);
 231}
 232
 233static void xenon_voltage_switch(struct sdhci_host *host)
 234{
 235        /* Wait for 5ms after set 1.8V signal enable bit */
 236        usleep_range(5000, 5500);
 237}
 238
 239static const struct sdhci_ops sdhci_xenon_ops = {
 240        .voltage_switch         = xenon_voltage_switch,
 241        .set_clock              = sdhci_set_clock,
 242        .set_power              = xenon_set_power,
 243        .set_bus_width          = sdhci_set_bus_width,
 244        .reset                  = xenon_reset,
 245        .set_uhs_signaling      = xenon_set_uhs_signaling,
 246        .get_max_clock          = sdhci_pltfm_clk_get_max_clock,
 247};
 248
 249static const struct sdhci_pltfm_data sdhci_xenon_pdata = {
 250        .ops = &sdhci_xenon_ops,
 251        .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC |
 252                  SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER |
 253                  SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
 254};
 255
 256/*
 257 * Xenon Specific Operations in mmc_host_ops
 258 */
 259static void xenon_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
 260{
 261        struct sdhci_host *host = mmc_priv(mmc);
 262        struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
 263        struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
 264        u32 reg;
 265
 266        /*
 267         * HS400/HS200/eMMC HS doesn't have Preset Value register.
 268         * However, sdhci_set_ios will read HS400/HS200 Preset register.
 269         * Disable Preset Value register for HS400/HS200.
 270         * eMMC HS with preset_enabled set will trigger a bug in
 271         * get_preset_value().
 272         */
 273        if ((ios->timing == MMC_TIMING_MMC_HS400) ||
 274            (ios->timing == MMC_TIMING_MMC_HS200) ||
 275            (ios->timing == MMC_TIMING_MMC_HS)) {
 276                host->preset_enabled = false;
 277                host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN;
 278                host->flags &= ~SDHCI_PV_ENABLED;
 279
 280                reg = sdhci_readw(host, SDHCI_HOST_CONTROL2);
 281                reg &= ~SDHCI_CTRL_PRESET_VAL_ENABLE;
 282                sdhci_writew(host, reg, SDHCI_HOST_CONTROL2);
 283        } else {
 284                host->quirks2 &= ~SDHCI_QUIRK2_PRESET_VALUE_BROKEN;
 285        }
 286
 287        sdhci_set_ios(mmc, ios);
 288        xenon_phy_adj(host, ios);
 289
 290        if (host->clock > XENON_DEFAULT_SDCLK_FREQ)
 291                xenon_set_sdclk_off_idle(host, priv->sdhc_id, true);
 292}
 293
 294static int xenon_start_signal_voltage_switch(struct mmc_host *mmc,
 295                                             struct mmc_ios *ios)
 296{
 297        struct sdhci_host *host = mmc_priv(mmc);
 298
 299        /*
 300         * Before SD/SDIO set signal voltage, SD bus clock should be
 301         * disabled. However, sdhci_set_clock will also disable the Internal
 302         * clock in mmc_set_signal_voltage().
 303         * If Internal clock is disabled, the 3.3V/1.8V bit can not be updated.
 304         * Thus here manually enable internal clock.
 305         *
 306         * After switch completes, it is unnecessary to disable internal clock,
 307         * since keeping internal clock active obeys SD spec.
 308         */
 309        xenon_enable_internal_clk(host);
 310
 311        xenon_soc_pad_ctrl(host, ios->signal_voltage);
 312
 313        /*
 314         * If Vqmmc is fixed on platform, vqmmc regulator should be unavailable.
 315         * Thus SDHCI_CTRL_VDD_180 bit might not work then.
 316         * Skip the standard voltage switch to avoid any issue.
 317         */
 318        if (PTR_ERR(mmc->supply.vqmmc) == -ENODEV)
 319                return 0;
 320
 321        return sdhci_start_signal_voltage_switch(mmc, ios);
 322}
 323
 324/*
 325 * Update card type.
 326 * priv->init_card_type will be used in PHY timing adjustment.
 327 */
 328static void xenon_init_card(struct mmc_host *mmc, struct mmc_card *card)
 329{
 330        struct sdhci_host *host = mmc_priv(mmc);
 331        struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
 332        struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
 333
 334        /* Update card type*/
 335        priv->init_card_type = card->type;
 336}
 337
 338static int xenon_execute_tuning(struct mmc_host *mmc, u32 opcode)
 339{
 340        struct sdhci_host *host = mmc_priv(mmc);
 341
 342        if (host->timing == MMC_TIMING_UHS_DDR50 ||
 343                host->timing == MMC_TIMING_MMC_DDR52)
 344                return 0;
 345
 346        /*
 347         * Currently force Xenon driver back to support mode 1 only,
 348         * even though Xenon might claim to support mode 2 or mode 3.
 349         * It requires more time to test mode 2/mode 3 on more platforms.
 350         */
 351        if (host->tuning_mode != SDHCI_TUNING_MODE_1)
 352                xenon_retune_setup(host);
 353
 354        return sdhci_execute_tuning(mmc, opcode);
 355}
 356
 357static void xenon_enable_sdio_irq(struct mmc_host *mmc, int enable)
 358{
 359        struct sdhci_host *host = mmc_priv(mmc);
 360        struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
 361        struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
 362        u32 reg;
 363        u8 sdhc_id = priv->sdhc_id;
 364
 365        sdhci_enable_sdio_irq(mmc, enable);
 366
 367        if (enable) {
 368                /*
 369                 * Set SDIO Card Inserted indication
 370                 * to enable detecting SDIO async irq.
 371                 */
 372                reg = sdhci_readl(host, XENON_SYS_CFG_INFO);
 373                reg |= (1 << (sdhc_id + XENON_SLOT_TYPE_SDIO_SHIFT));
 374                sdhci_writel(host, reg, XENON_SYS_CFG_INFO);
 375        } else {
 376                /* Clear SDIO Card Inserted indication */
 377                reg = sdhci_readl(host, XENON_SYS_CFG_INFO);
 378                reg &= ~(1 << (sdhc_id + XENON_SLOT_TYPE_SDIO_SHIFT));
 379                sdhci_writel(host, reg, XENON_SYS_CFG_INFO);
 380        }
 381}
 382
 383static void xenon_replace_mmc_host_ops(struct sdhci_host *host)
 384{
 385        host->mmc_host_ops.set_ios = xenon_set_ios;
 386        host->mmc_host_ops.start_signal_voltage_switch =
 387                        xenon_start_signal_voltage_switch;
 388        host->mmc_host_ops.init_card = xenon_init_card;
 389        host->mmc_host_ops.execute_tuning = xenon_execute_tuning;
 390        host->mmc_host_ops.enable_sdio_irq = xenon_enable_sdio_irq;
 391}
 392
 393/*
 394 * Parse Xenon specific DT properties:
 395 * sdhc-id: the index of current SDHC.
 396 *          Refer to XENON_SYS_CFG_INFO register
 397 * tun-count: the interval between re-tuning
 398 */
 399static int xenon_probe_dt(struct platform_device *pdev)
 400{
 401        struct device_node *np = pdev->dev.of_node;
 402        struct sdhci_host *host = platform_get_drvdata(pdev);
 403        struct mmc_host *mmc = host->mmc;
 404        struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
 405        struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
 406        u32 sdhc_id, nr_sdhc;
 407        u32 tuning_count;
 408
 409        /* Disable HS200 on Armada AP806 */
 410        if (of_device_is_compatible(np, "marvell,armada-ap806-sdhci"))
 411                host->quirks2 |= SDHCI_QUIRK2_BROKEN_HS200;
 412
 413        sdhc_id = 0x0;
 414        if (!of_property_read_u32(np, "marvell,xenon-sdhc-id", &sdhc_id)) {
 415                nr_sdhc = sdhci_readl(host, XENON_SYS_CFG_INFO);
 416                nr_sdhc &= XENON_NR_SUPPORTED_SLOT_MASK;
 417                if (unlikely(sdhc_id > nr_sdhc)) {
 418                        dev_err(mmc_dev(mmc), "SDHC Index %d exceeds Number of SDHCs %d\n",
 419                                sdhc_id, nr_sdhc);
 420                        return -EINVAL;
 421                }
 422        }
 423        priv->sdhc_id = sdhc_id;
 424
 425        tuning_count = XENON_DEF_TUNING_COUNT;
 426        if (!of_property_read_u32(np, "marvell,xenon-tun-count",
 427                                  &tuning_count)) {
 428                if (unlikely(tuning_count >= XENON_TMR_RETUN_NO_PRESENT)) {
 429                        dev_err(mmc_dev(mmc), "Wrong Re-tuning Count. Set default value %d\n",
 430                                XENON_DEF_TUNING_COUNT);
 431                        tuning_count = XENON_DEF_TUNING_COUNT;
 432                }
 433        }
 434        priv->tuning_count = tuning_count;
 435
 436        return xenon_phy_parse_dt(np, host);
 437}
 438
 439static int xenon_sdhc_prepare(struct sdhci_host *host)
 440{
 441        struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
 442        struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
 443        u8 sdhc_id = priv->sdhc_id;
 444
 445        /* Enable SDHC */
 446        xenon_enable_sdhc(host, sdhc_id);
 447
 448        /* Enable ACG */
 449        xenon_set_acg(host, true);
 450
 451        /* Enable Parallel Transfer Mode */
 452        xenon_enable_sdhc_parallel_tran(host, sdhc_id);
 453
 454        /* Disable SDCLK-Off-While-Idle before card init */
 455        xenon_set_sdclk_off_idle(host, sdhc_id, false);
 456
 457        xenon_mask_cmd_conflict_err(host);
 458
 459        return 0;
 460}
 461
 462static void xenon_sdhc_unprepare(struct sdhci_host *host)
 463{
 464        struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
 465        struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
 466        u8 sdhc_id = priv->sdhc_id;
 467
 468        /* disable SDHC */
 469        xenon_disable_sdhc(host, sdhc_id);
 470}
 471
 472static int xenon_probe(struct platform_device *pdev)
 473{
 474        struct sdhci_pltfm_host *pltfm_host;
 475        struct sdhci_host *host;
 476        struct xenon_priv *priv;
 477        int err;
 478
 479        host = sdhci_pltfm_init(pdev, &sdhci_xenon_pdata,
 480                                sizeof(struct xenon_priv));
 481        if (IS_ERR(host))
 482                return PTR_ERR(host);
 483
 484        pltfm_host = sdhci_priv(host);
 485        priv = sdhci_pltfm_priv(pltfm_host);
 486
 487        /*
 488         * Link Xenon specific mmc_host_ops function,
 489         * to replace standard ones in sdhci_ops.
 490         */
 491        xenon_replace_mmc_host_ops(host);
 492
 493        pltfm_host->clk = devm_clk_get(&pdev->dev, "core");
 494        if (IS_ERR(pltfm_host->clk)) {
 495                err = PTR_ERR(pltfm_host->clk);
 496                dev_err(&pdev->dev, "Failed to setup input clk: %d\n", err);
 497                goto free_pltfm;
 498        }
 499        err = clk_prepare_enable(pltfm_host->clk);
 500        if (err)
 501                goto free_pltfm;
 502
 503        priv->axi_clk = devm_clk_get(&pdev->dev, "axi");
 504        if (IS_ERR(priv->axi_clk)) {
 505                err = PTR_ERR(priv->axi_clk);
 506                if (err == -EPROBE_DEFER)
 507                        goto err_clk;
 508        } else {
 509                err = clk_prepare_enable(priv->axi_clk);
 510                if (err)
 511                        goto err_clk;
 512        }
 513
 514        err = mmc_of_parse(host->mmc);
 515        if (err)
 516                goto err_clk_axi;
 517
 518        sdhci_get_of_property(pdev);
 519
 520        xenon_set_acg(host, false);
 521
 522        /* Xenon specific dt parse */
 523        err = xenon_probe_dt(pdev);
 524        if (err)
 525                goto err_clk_axi;
 526
 527        err = xenon_sdhc_prepare(host);
 528        if (err)
 529                goto err_clk_axi;
 530
 531        pm_runtime_get_noresume(&pdev->dev);
 532        pm_runtime_set_active(&pdev->dev);
 533        pm_runtime_set_autosuspend_delay(&pdev->dev, 50);
 534        pm_runtime_use_autosuspend(&pdev->dev);
 535        pm_runtime_enable(&pdev->dev);
 536        pm_suspend_ignore_children(&pdev->dev, 1);
 537
 538        err = sdhci_add_host(host);
 539        if (err)
 540                goto remove_sdhc;
 541
 542        pm_runtime_put_autosuspend(&pdev->dev);
 543
 544        return 0;
 545
 546remove_sdhc:
 547        pm_runtime_disable(&pdev->dev);
 548        pm_runtime_put_noidle(&pdev->dev);
 549        xenon_sdhc_unprepare(host);
 550err_clk_axi:
 551        clk_disable_unprepare(priv->axi_clk);
 552err_clk:
 553        clk_disable_unprepare(pltfm_host->clk);
 554free_pltfm:
 555        sdhci_pltfm_free(pdev);
 556        return err;
 557}
 558
 559static int xenon_remove(struct platform_device *pdev)
 560{
 561        struct sdhci_host *host = platform_get_drvdata(pdev);
 562        struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
 563        struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
 564
 565        pm_runtime_get_sync(&pdev->dev);
 566        pm_runtime_disable(&pdev->dev);
 567        pm_runtime_put_noidle(&pdev->dev);
 568
 569        sdhci_remove_host(host, 0);
 570
 571        xenon_sdhc_unprepare(host);
 572        clk_disable_unprepare(priv->axi_clk);
 573        clk_disable_unprepare(pltfm_host->clk);
 574
 575        sdhci_pltfm_free(pdev);
 576
 577        return 0;
 578}
 579
 580#ifdef CONFIG_PM_SLEEP
 581static int xenon_suspend(struct device *dev)
 582{
 583        struct sdhci_host *host = dev_get_drvdata(dev);
 584        struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
 585        struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
 586        int ret;
 587
 588        ret = pm_runtime_force_suspend(dev);
 589
 590        priv->restore_needed = true;
 591        return ret;
 592}
 593#endif
 594
 595#ifdef CONFIG_PM
 596static int xenon_runtime_suspend(struct device *dev)
 597{
 598        struct sdhci_host *host = dev_get_drvdata(dev);
 599        struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
 600        struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
 601        int ret;
 602
 603        ret = sdhci_runtime_suspend_host(host);
 604        if (ret)
 605                return ret;
 606
 607        if (host->tuning_mode != SDHCI_TUNING_MODE_3)
 608                mmc_retune_needed(host->mmc);
 609
 610        clk_disable_unprepare(pltfm_host->clk);
 611        /*
 612         * Need to update the priv->clock here, or when runtime resume
 613         * back, phy don't aware the clock change and won't adjust phy
 614         * which will cause cmd err
 615         */
 616        priv->clock = 0;
 617        return 0;
 618}
 619
 620static int xenon_runtime_resume(struct device *dev)
 621{
 622        struct sdhci_host *host = dev_get_drvdata(dev);
 623        struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
 624        struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
 625        int ret;
 626
 627        ret = clk_prepare_enable(pltfm_host->clk);
 628        if (ret) {
 629                dev_err(dev, "can't enable mainck\n");
 630                return ret;
 631        }
 632
 633        if (priv->restore_needed) {
 634                ret = xenon_sdhc_prepare(host);
 635                if (ret)
 636                        goto out;
 637                priv->restore_needed = false;
 638        }
 639
 640        ret = sdhci_runtime_resume_host(host);
 641        if (ret)
 642                goto out;
 643        return 0;
 644out:
 645        clk_disable_unprepare(pltfm_host->clk);
 646        return ret;
 647}
 648#endif /* CONFIG_PM */
 649
 650static const struct dev_pm_ops sdhci_xenon_dev_pm_ops = {
 651        SET_SYSTEM_SLEEP_PM_OPS(xenon_suspend,
 652                                pm_runtime_force_resume)
 653        SET_RUNTIME_PM_OPS(xenon_runtime_suspend,
 654                           xenon_runtime_resume,
 655                           NULL)
 656};
 657
 658static const struct of_device_id sdhci_xenon_dt_ids[] = {
 659        { .compatible = "marvell,armada-ap806-sdhci",},
 660        { .compatible = "marvell,armada-cp110-sdhci",},
 661        { .compatible = "marvell,armada-3700-sdhci",},
 662        {}
 663};
 664MODULE_DEVICE_TABLE(of, sdhci_xenon_dt_ids);
 665
 666static struct platform_driver sdhci_xenon_driver = {
 667        .driver = {
 668                .name   = "xenon-sdhci",
 669                .of_match_table = sdhci_xenon_dt_ids,
 670                .pm = &sdhci_xenon_dev_pm_ops,
 671        },
 672        .probe  = xenon_probe,
 673        .remove = xenon_remove,
 674};
 675
 676module_platform_driver(sdhci_xenon_driver);
 677
 678MODULE_DESCRIPTION("SDHCI platform driver for Marvell Xenon SDHC");
 679MODULE_AUTHOR("Hu Ziji <huziji@marvell.com>");
 680MODULE_LICENSE("GPL v2");
 681