1a10e763bSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 23a3748dbSHu Ziji /* 33a3748dbSHu Ziji * Driver for Marvell Xenon SDHC as a platform device 43a3748dbSHu Ziji * 53a3748dbSHu Ziji * Copyright (C) 2016 Marvell, All Rights Reserved. 63a3748dbSHu Ziji * 73a3748dbSHu Ziji * Author: Hu Ziji <huziji@marvell.com> 83a3748dbSHu Ziji * Date: 2016-8-24 93a3748dbSHu Ziji * 103a3748dbSHu Ziji * Inspired by Jisheng Zhang <jszhang@marvell.com> 113a3748dbSHu Ziji * Special thanks to Video BG4 project team. 123a3748dbSHu Ziji */ 133a3748dbSHu Ziji 14*471cf454SMarcin Wojtas #include <linux/acpi.h> 153a3748dbSHu Ziji #include <linux/delay.h> 163a3748dbSHu Ziji #include <linux/ktime.h> 173a3748dbSHu Ziji #include <linux/module.h> 183a3748dbSHu Ziji #include <linux/of.h> 19a027b2c5SZhoujie Wu #include <linux/pm.h> 20a027b2c5SZhoujie Wu #include <linux/pm_runtime.h> 213a3748dbSHu Ziji 223a3748dbSHu Ziji #include "sdhci-pltfm.h" 233a3748dbSHu Ziji #include "sdhci-xenon.h" 243a3748dbSHu Ziji 253a3748dbSHu Ziji static int xenon_enable_internal_clk(struct sdhci_host *host) 263a3748dbSHu Ziji { 273a3748dbSHu Ziji u32 reg; 283a3748dbSHu Ziji ktime_t timeout; 293a3748dbSHu Ziji 303a3748dbSHu Ziji reg = sdhci_readl(host, SDHCI_CLOCK_CONTROL); 313a3748dbSHu Ziji reg |= SDHCI_CLOCK_INT_EN; 323a3748dbSHu Ziji sdhci_writel(host, reg, SDHCI_CLOCK_CONTROL); 333a3748dbSHu Ziji /* Wait max 20 ms */ 343a3748dbSHu Ziji timeout = ktime_add_ms(ktime_get(), 20); 350e6e7c2fSAdrian Hunter while (1) { 360e6e7c2fSAdrian Hunter bool timedout = ktime_after(ktime_get(), timeout); 370e6e7c2fSAdrian Hunter 380e6e7c2fSAdrian Hunter reg = sdhci_readw(host, SDHCI_CLOCK_CONTROL); 390e6e7c2fSAdrian Hunter if (reg & SDHCI_CLOCK_INT_STABLE) 400e6e7c2fSAdrian Hunter break; 410e6e7c2fSAdrian Hunter if (timedout) { 423a3748dbSHu Ziji dev_err(mmc_dev(host->mmc), "Internal clock never stabilised.\n"); 433a3748dbSHu Ziji return -ETIMEDOUT; 443a3748dbSHu Ziji } 453a3748dbSHu Ziji usleep_range(900, 1100); 463a3748dbSHu Ziji } 473a3748dbSHu Ziji 483a3748dbSHu Ziji return 0; 493a3748dbSHu Ziji } 503a3748dbSHu Ziji 513a3748dbSHu Ziji /* Set SDCLK-off-while-idle */ 523a3748dbSHu Ziji static void xenon_set_sdclk_off_idle(struct sdhci_host *host, 533a3748dbSHu Ziji unsigned char sdhc_id, bool enable) 543a3748dbSHu Ziji { 553a3748dbSHu Ziji u32 reg; 563a3748dbSHu Ziji u32 mask; 573a3748dbSHu Ziji 583a3748dbSHu Ziji reg = sdhci_readl(host, XENON_SYS_OP_CTRL); 593a3748dbSHu Ziji /* Get the bit shift basing on the SDHC index */ 603a3748dbSHu Ziji mask = (0x1 << (XENON_SDCLK_IDLEOFF_ENABLE_SHIFT + sdhc_id)); 613a3748dbSHu Ziji if (enable) 623a3748dbSHu Ziji reg |= mask; 633a3748dbSHu Ziji else 643a3748dbSHu Ziji reg &= ~mask; 653a3748dbSHu Ziji 663a3748dbSHu Ziji sdhci_writel(host, reg, XENON_SYS_OP_CTRL); 673a3748dbSHu Ziji } 683a3748dbSHu Ziji 693a3748dbSHu Ziji /* Enable/Disable the Auto Clock Gating function */ 703a3748dbSHu Ziji static void xenon_set_acg(struct sdhci_host *host, bool enable) 713a3748dbSHu Ziji { 723a3748dbSHu Ziji u32 reg; 733a3748dbSHu Ziji 743a3748dbSHu Ziji reg = sdhci_readl(host, XENON_SYS_OP_CTRL); 753a3748dbSHu Ziji if (enable) 763a3748dbSHu Ziji reg &= ~XENON_AUTO_CLKGATE_DISABLE_MASK; 773a3748dbSHu Ziji else 783a3748dbSHu Ziji reg |= XENON_AUTO_CLKGATE_DISABLE_MASK; 793a3748dbSHu Ziji sdhci_writel(host, reg, XENON_SYS_OP_CTRL); 803a3748dbSHu Ziji } 813a3748dbSHu Ziji 823a3748dbSHu Ziji /* Enable this SDHC */ 833a3748dbSHu Ziji static void xenon_enable_sdhc(struct sdhci_host *host, 843a3748dbSHu Ziji unsigned char sdhc_id) 853a3748dbSHu Ziji { 863a3748dbSHu Ziji u32 reg; 873a3748dbSHu Ziji 883a3748dbSHu Ziji reg = sdhci_readl(host, XENON_SYS_OP_CTRL); 893a3748dbSHu Ziji reg |= (BIT(sdhc_id) << XENON_SLOT_ENABLE_SHIFT); 903a3748dbSHu Ziji sdhci_writel(host, reg, XENON_SYS_OP_CTRL); 913a3748dbSHu Ziji 923a3748dbSHu Ziji host->mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY; 933a3748dbSHu Ziji /* 943a3748dbSHu Ziji * Force to clear BUS_TEST to 953a3748dbSHu Ziji * skip bus_test_pre and bus_test_post 963a3748dbSHu Ziji */ 973a3748dbSHu Ziji host->mmc->caps &= ~MMC_CAP_BUS_WIDTH_TEST; 983a3748dbSHu Ziji } 993a3748dbSHu Ziji 1003a3748dbSHu Ziji /* Disable this SDHC */ 1013a3748dbSHu Ziji static void xenon_disable_sdhc(struct sdhci_host *host, 1023a3748dbSHu Ziji unsigned char sdhc_id) 1033a3748dbSHu Ziji { 1043a3748dbSHu Ziji u32 reg; 1053a3748dbSHu Ziji 1063a3748dbSHu Ziji reg = sdhci_readl(host, XENON_SYS_OP_CTRL); 1073a3748dbSHu Ziji reg &= ~(BIT(sdhc_id) << XENON_SLOT_ENABLE_SHIFT); 1083a3748dbSHu Ziji sdhci_writel(host, reg, XENON_SYS_OP_CTRL); 1093a3748dbSHu Ziji } 1103a3748dbSHu Ziji 1113a3748dbSHu Ziji /* Enable Parallel Transfer Mode */ 1123a3748dbSHu Ziji static void xenon_enable_sdhc_parallel_tran(struct sdhci_host *host, 1133a3748dbSHu Ziji unsigned char sdhc_id) 1143a3748dbSHu Ziji { 1153a3748dbSHu Ziji u32 reg; 1163a3748dbSHu Ziji 1173a3748dbSHu Ziji reg = sdhci_readl(host, XENON_SYS_EXT_OP_CTRL); 1183a3748dbSHu Ziji reg |= BIT(sdhc_id); 1193a3748dbSHu Ziji sdhci_writel(host, reg, XENON_SYS_EXT_OP_CTRL); 1203a3748dbSHu Ziji } 1213a3748dbSHu Ziji 1223a3748dbSHu Ziji /* Mask command conflict error */ 1233a3748dbSHu Ziji static void xenon_mask_cmd_conflict_err(struct sdhci_host *host) 1243a3748dbSHu Ziji { 1253a3748dbSHu Ziji u32 reg; 1263a3748dbSHu Ziji 1273a3748dbSHu Ziji reg = sdhci_readl(host, XENON_SYS_EXT_OP_CTRL); 1283a3748dbSHu Ziji reg |= XENON_MASK_CMD_CONFLICT_ERR; 1293a3748dbSHu Ziji sdhci_writel(host, reg, XENON_SYS_EXT_OP_CTRL); 1303a3748dbSHu Ziji } 1313a3748dbSHu Ziji 1323a3748dbSHu Ziji static void xenon_retune_setup(struct sdhci_host *host) 1333a3748dbSHu Ziji { 1343a3748dbSHu Ziji struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 1353a3748dbSHu Ziji struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host); 1363a3748dbSHu Ziji u32 reg; 1373a3748dbSHu Ziji 1383a3748dbSHu Ziji /* Disable the Re-Tuning Request functionality */ 1393a3748dbSHu Ziji reg = sdhci_readl(host, XENON_SLOT_RETUNING_REQ_CTRL); 1403a3748dbSHu Ziji reg &= ~XENON_RETUNING_COMPATIBLE; 1413a3748dbSHu Ziji sdhci_writel(host, reg, XENON_SLOT_RETUNING_REQ_CTRL); 1423a3748dbSHu Ziji 1433a3748dbSHu Ziji /* Disable the Re-tuning Interrupt */ 1443a3748dbSHu Ziji reg = sdhci_readl(host, SDHCI_SIGNAL_ENABLE); 1453a3748dbSHu Ziji reg &= ~SDHCI_INT_RETUNE; 1463a3748dbSHu Ziji sdhci_writel(host, reg, SDHCI_SIGNAL_ENABLE); 1473a3748dbSHu Ziji reg = sdhci_readl(host, SDHCI_INT_ENABLE); 1483a3748dbSHu Ziji reg &= ~SDHCI_INT_RETUNE; 1493a3748dbSHu Ziji sdhci_writel(host, reg, SDHCI_INT_ENABLE); 1503a3748dbSHu Ziji 1513a3748dbSHu Ziji /* Force to use Tuning Mode 1 */ 1523a3748dbSHu Ziji host->tuning_mode = SDHCI_TUNING_MODE_1; 1533a3748dbSHu Ziji /* Set re-tuning period */ 1543a3748dbSHu Ziji host->tuning_count = 1 << (priv->tuning_count - 1); 1553a3748dbSHu Ziji } 1563a3748dbSHu Ziji 1573a3748dbSHu Ziji /* 1583a3748dbSHu Ziji * Operations inside struct sdhci_ops 1593a3748dbSHu Ziji */ 1603a3748dbSHu Ziji /* Recover the Register Setting cleared during SOFTWARE_RESET_ALL */ 1613a3748dbSHu Ziji static void xenon_reset_exit(struct sdhci_host *host, 1623a3748dbSHu Ziji unsigned char sdhc_id, u8 mask) 1633a3748dbSHu Ziji { 1643a3748dbSHu Ziji /* Only SOFTWARE RESET ALL will clear the register setting */ 1653a3748dbSHu Ziji if (!(mask & SDHCI_RESET_ALL)) 1663a3748dbSHu Ziji return; 1673a3748dbSHu Ziji 1683a3748dbSHu Ziji /* Disable tuning request and auto-retuning again */ 1693a3748dbSHu Ziji xenon_retune_setup(host); 1703a3748dbSHu Ziji 1713a3748dbSHu Ziji xenon_set_acg(host, true); 1723a3748dbSHu Ziji 1733a3748dbSHu Ziji xenon_set_sdclk_off_idle(host, sdhc_id, false); 1743a3748dbSHu Ziji 1753a3748dbSHu Ziji xenon_mask_cmd_conflict_err(host); 1763a3748dbSHu Ziji } 1773a3748dbSHu Ziji 1783a3748dbSHu Ziji static void xenon_reset(struct sdhci_host *host, u8 mask) 1793a3748dbSHu Ziji { 1803a3748dbSHu Ziji struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 1813a3748dbSHu Ziji struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host); 1823a3748dbSHu Ziji 1833a3748dbSHu Ziji sdhci_reset(host, mask); 1843a3748dbSHu Ziji xenon_reset_exit(host, priv->sdhc_id, mask); 1853a3748dbSHu Ziji } 1863a3748dbSHu Ziji 1873a3748dbSHu Ziji /* 1883a3748dbSHu Ziji * Xenon defines different values for HS200 and HS400 1893a3748dbSHu Ziji * in Host_Control_2 1903a3748dbSHu Ziji */ 1913a3748dbSHu Ziji static void xenon_set_uhs_signaling(struct sdhci_host *host, 1923a3748dbSHu Ziji unsigned int timing) 1933a3748dbSHu Ziji { 1943a3748dbSHu Ziji u16 ctrl_2; 1953a3748dbSHu Ziji 1963a3748dbSHu Ziji ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2); 1973a3748dbSHu Ziji /* Select Bus Speed Mode for host */ 1983a3748dbSHu Ziji ctrl_2 &= ~SDHCI_CTRL_UHS_MASK; 1993a3748dbSHu Ziji if (timing == MMC_TIMING_MMC_HS200) 2003a3748dbSHu Ziji ctrl_2 |= XENON_CTRL_HS200; 2013a3748dbSHu Ziji else if (timing == MMC_TIMING_UHS_SDR104) 2023a3748dbSHu Ziji ctrl_2 |= SDHCI_CTRL_UHS_SDR104; 2033a3748dbSHu Ziji else if (timing == MMC_TIMING_UHS_SDR12) 2043a3748dbSHu Ziji ctrl_2 |= SDHCI_CTRL_UHS_SDR12; 2053a3748dbSHu Ziji else if (timing == MMC_TIMING_UHS_SDR25) 2063a3748dbSHu Ziji ctrl_2 |= SDHCI_CTRL_UHS_SDR25; 2073a3748dbSHu Ziji else if (timing == MMC_TIMING_UHS_SDR50) 2083a3748dbSHu Ziji ctrl_2 |= SDHCI_CTRL_UHS_SDR50; 2093a3748dbSHu Ziji else if ((timing == MMC_TIMING_UHS_DDR50) || 2103a3748dbSHu Ziji (timing == MMC_TIMING_MMC_DDR52)) 2113a3748dbSHu Ziji ctrl_2 |= SDHCI_CTRL_UHS_DDR50; 2123a3748dbSHu Ziji else if (timing == MMC_TIMING_MMC_HS400) 2133a3748dbSHu Ziji ctrl_2 |= XENON_CTRL_HS400; 2143a3748dbSHu Ziji sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2); 2153a3748dbSHu Ziji } 2163a3748dbSHu Ziji 21799c14fc3SZhoujie Wu static void xenon_set_power(struct sdhci_host *host, unsigned char mode, 21899c14fc3SZhoujie Wu unsigned short vdd) 21999c14fc3SZhoujie Wu { 22099c14fc3SZhoujie Wu struct mmc_host *mmc = host->mmc; 22199c14fc3SZhoujie Wu u8 pwr = host->pwr; 22299c14fc3SZhoujie Wu 22399c14fc3SZhoujie Wu sdhci_set_power_noreg(host, mode, vdd); 22499c14fc3SZhoujie Wu 22599c14fc3SZhoujie Wu if (host->pwr == pwr) 22699c14fc3SZhoujie Wu return; 22799c14fc3SZhoujie Wu 22899c14fc3SZhoujie Wu if (host->pwr == 0) 22999c14fc3SZhoujie Wu vdd = 0; 23099c14fc3SZhoujie Wu 23199c14fc3SZhoujie Wu if (!IS_ERR(mmc->supply.vmmc)) 23299c14fc3SZhoujie Wu mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, vdd); 23399c14fc3SZhoujie Wu } 23499c14fc3SZhoujie Wu 2358d876bf4SZhoujie Wu static void xenon_voltage_switch(struct sdhci_host *host) 2368d876bf4SZhoujie Wu { 2378d876bf4SZhoujie Wu /* Wait for 5ms after set 1.8V signal enable bit */ 2388d876bf4SZhoujie Wu usleep_range(5000, 5500); 239bb32e198SMarek Behún 240bb32e198SMarek Behún /* 241bb32e198SMarek Behún * For some reason the controller's Host Control2 register reports 242bb32e198SMarek Behún * the bit representing 1.8V signaling as 0 when read after it was 243bb32e198SMarek Behún * written as 1. Subsequent read reports 1. 244bb32e198SMarek Behún * 245bb32e198SMarek Behún * Since this may cause some issues, do an empty read of the Host 246bb32e198SMarek Behún * Control2 register here to circumvent this. 247bb32e198SMarek Behún */ 248bb32e198SMarek Behún sdhci_readw(host, SDHCI_HOST_CONTROL2); 2498d876bf4SZhoujie Wu } 2508d876bf4SZhoujie Wu 25115424880SMarcin Wojtas static unsigned int xenon_get_max_clock(struct sdhci_host *host) 25215424880SMarcin Wojtas { 25315424880SMarcin Wojtas struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 25415424880SMarcin Wojtas 25515424880SMarcin Wojtas if (pltfm_host->clk) 25615424880SMarcin Wojtas return sdhci_pltfm_clk_get_max_clock(host); 25715424880SMarcin Wojtas else 25815424880SMarcin Wojtas return pltfm_host->clock; 25915424880SMarcin Wojtas } 26015424880SMarcin Wojtas 2613a3748dbSHu Ziji static const struct sdhci_ops sdhci_xenon_ops = { 2628d876bf4SZhoujie Wu .voltage_switch = xenon_voltage_switch, 2633a3748dbSHu Ziji .set_clock = sdhci_set_clock, 26499c14fc3SZhoujie Wu .set_power = xenon_set_power, 2653a3748dbSHu Ziji .set_bus_width = sdhci_set_bus_width, 2663a3748dbSHu Ziji .reset = xenon_reset, 2673a3748dbSHu Ziji .set_uhs_signaling = xenon_set_uhs_signaling, 26815424880SMarcin Wojtas .get_max_clock = xenon_get_max_clock, 2693a3748dbSHu Ziji }; 2703a3748dbSHu Ziji 2713a3748dbSHu Ziji static const struct sdhci_pltfm_data sdhci_xenon_pdata = { 2723a3748dbSHu Ziji .ops = &sdhci_xenon_ops, 2733a3748dbSHu Ziji .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC | 2743a3748dbSHu Ziji SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER | 2753a3748dbSHu Ziji SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, 2763a3748dbSHu Ziji }; 2773a3748dbSHu Ziji 2783a3748dbSHu Ziji /* 2793a3748dbSHu Ziji * Xenon Specific Operations in mmc_host_ops 2803a3748dbSHu Ziji */ 2813a3748dbSHu Ziji static void xenon_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) 2823a3748dbSHu Ziji { 2833a3748dbSHu Ziji struct sdhci_host *host = mmc_priv(mmc); 2843a3748dbSHu Ziji struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 2853a3748dbSHu Ziji struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host); 2863a3748dbSHu Ziji u32 reg; 2873a3748dbSHu Ziji 2883a3748dbSHu Ziji /* 2893a3748dbSHu Ziji * HS400/HS200/eMMC HS doesn't have Preset Value register. 2903a3748dbSHu Ziji * However, sdhci_set_ios will read HS400/HS200 Preset register. 2913a3748dbSHu Ziji * Disable Preset Value register for HS400/HS200. 2923a3748dbSHu Ziji * eMMC HS with preset_enabled set will trigger a bug in 2933a3748dbSHu Ziji * get_preset_value(). 2943a3748dbSHu Ziji */ 2953a3748dbSHu Ziji if ((ios->timing == MMC_TIMING_MMC_HS400) || 2963a3748dbSHu Ziji (ios->timing == MMC_TIMING_MMC_HS200) || 2973a3748dbSHu Ziji (ios->timing == MMC_TIMING_MMC_HS)) { 2983a3748dbSHu Ziji host->preset_enabled = false; 2993a3748dbSHu Ziji host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN; 3003a3748dbSHu Ziji host->flags &= ~SDHCI_PV_ENABLED; 3013a3748dbSHu Ziji 3023a3748dbSHu Ziji reg = sdhci_readw(host, SDHCI_HOST_CONTROL2); 3033a3748dbSHu Ziji reg &= ~SDHCI_CTRL_PRESET_VAL_ENABLE; 3043a3748dbSHu Ziji sdhci_writew(host, reg, SDHCI_HOST_CONTROL2); 3053a3748dbSHu Ziji } else { 3063a3748dbSHu Ziji host->quirks2 &= ~SDHCI_QUIRK2_PRESET_VALUE_BROKEN; 3073a3748dbSHu Ziji } 3083a3748dbSHu Ziji 3093a3748dbSHu Ziji sdhci_set_ios(mmc, ios); 31006c8b667SHu Ziji xenon_phy_adj(host, ios); 3113a3748dbSHu Ziji 3123a3748dbSHu Ziji if (host->clock > XENON_DEFAULT_SDCLK_FREQ) 3133a3748dbSHu Ziji xenon_set_sdclk_off_idle(host, priv->sdhc_id, true); 3143a3748dbSHu Ziji } 3153a3748dbSHu Ziji 3163a3748dbSHu Ziji static int xenon_start_signal_voltage_switch(struct mmc_host *mmc, 3173a3748dbSHu Ziji struct mmc_ios *ios) 3183a3748dbSHu Ziji { 3193a3748dbSHu Ziji struct sdhci_host *host = mmc_priv(mmc); 3203a3748dbSHu Ziji 3213a3748dbSHu Ziji /* 3223a3748dbSHu Ziji * Before SD/SDIO set signal voltage, SD bus clock should be 3233a3748dbSHu Ziji * disabled. However, sdhci_set_clock will also disable the Internal 3243a3748dbSHu Ziji * clock in mmc_set_signal_voltage(). 3253a3748dbSHu Ziji * If Internal clock is disabled, the 3.3V/1.8V bit can not be updated. 3263a3748dbSHu Ziji * Thus here manually enable internal clock. 3273a3748dbSHu Ziji * 3283a3748dbSHu Ziji * After switch completes, it is unnecessary to disable internal clock, 3293a3748dbSHu Ziji * since keeping internal clock active obeys SD spec. 3303a3748dbSHu Ziji */ 3313a3748dbSHu Ziji xenon_enable_internal_clk(host); 3323a3748dbSHu Ziji 333298269c6SHu Ziji xenon_soc_pad_ctrl(host, ios->signal_voltage); 334298269c6SHu Ziji 3353a3748dbSHu Ziji /* 3363a3748dbSHu Ziji * If Vqmmc is fixed on platform, vqmmc regulator should be unavailable. 3373a3748dbSHu Ziji * Thus SDHCI_CTRL_VDD_180 bit might not work then. 3383a3748dbSHu Ziji * Skip the standard voltage switch to avoid any issue. 3393a3748dbSHu Ziji */ 3403a3748dbSHu Ziji if (PTR_ERR(mmc->supply.vqmmc) == -ENODEV) 3413a3748dbSHu Ziji return 0; 3423a3748dbSHu Ziji 3433a3748dbSHu Ziji return sdhci_start_signal_voltage_switch(mmc, ios); 3443a3748dbSHu Ziji } 3453a3748dbSHu Ziji 3463a3748dbSHu Ziji /* 3473a3748dbSHu Ziji * Update card type. 3483a3748dbSHu Ziji * priv->init_card_type will be used in PHY timing adjustment. 3493a3748dbSHu Ziji */ 3503a3748dbSHu Ziji static void xenon_init_card(struct mmc_host *mmc, struct mmc_card *card) 3513a3748dbSHu Ziji { 3523a3748dbSHu Ziji struct sdhci_host *host = mmc_priv(mmc); 3533a3748dbSHu Ziji struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 3543a3748dbSHu Ziji struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host); 3553a3748dbSHu Ziji 3563a3748dbSHu Ziji /* Update card type*/ 3573a3748dbSHu Ziji priv->init_card_type = card->type; 3583a3748dbSHu Ziji } 3593a3748dbSHu Ziji 3603a3748dbSHu Ziji static int xenon_execute_tuning(struct mmc_host *mmc, u32 opcode) 3613a3748dbSHu Ziji { 3623a3748dbSHu Ziji struct sdhci_host *host = mmc_priv(mmc); 3633a3748dbSHu Ziji 364468c6483SZhoujie Wu if (host->timing == MMC_TIMING_UHS_DDR50 || 365468c6483SZhoujie Wu host->timing == MMC_TIMING_MMC_DDR52) 3663a3748dbSHu Ziji return 0; 3673a3748dbSHu Ziji 3683a3748dbSHu Ziji /* 3693a3748dbSHu Ziji * Currently force Xenon driver back to support mode 1 only, 3703a3748dbSHu Ziji * even though Xenon might claim to support mode 2 or mode 3. 3713a3748dbSHu Ziji * It requires more time to test mode 2/mode 3 on more platforms. 3723a3748dbSHu Ziji */ 3733a3748dbSHu Ziji if (host->tuning_mode != SDHCI_TUNING_MODE_1) 3743a3748dbSHu Ziji xenon_retune_setup(host); 3753a3748dbSHu Ziji 3763a3748dbSHu Ziji return sdhci_execute_tuning(mmc, opcode); 3773a3748dbSHu Ziji } 3783a3748dbSHu Ziji 3793a3748dbSHu Ziji static void xenon_enable_sdio_irq(struct mmc_host *mmc, int enable) 3803a3748dbSHu Ziji { 3813a3748dbSHu Ziji struct sdhci_host *host = mmc_priv(mmc); 3823a3748dbSHu Ziji struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 3833a3748dbSHu Ziji struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host); 3843a3748dbSHu Ziji u32 reg; 3853a3748dbSHu Ziji u8 sdhc_id = priv->sdhc_id; 3863a3748dbSHu Ziji 3873a3748dbSHu Ziji sdhci_enable_sdio_irq(mmc, enable); 3883a3748dbSHu Ziji 3893a3748dbSHu Ziji if (enable) { 3903a3748dbSHu Ziji /* 3913a3748dbSHu Ziji * Set SDIO Card Inserted indication 3923a3748dbSHu Ziji * to enable detecting SDIO async irq. 3933a3748dbSHu Ziji */ 3943a3748dbSHu Ziji reg = sdhci_readl(host, XENON_SYS_CFG_INFO); 3953a3748dbSHu Ziji reg |= (1 << (sdhc_id + XENON_SLOT_TYPE_SDIO_SHIFT)); 3963a3748dbSHu Ziji sdhci_writel(host, reg, XENON_SYS_CFG_INFO); 3973a3748dbSHu Ziji } else { 3983a3748dbSHu Ziji /* Clear SDIO Card Inserted indication */ 3993a3748dbSHu Ziji reg = sdhci_readl(host, XENON_SYS_CFG_INFO); 4003a3748dbSHu Ziji reg &= ~(1 << (sdhc_id + XENON_SLOT_TYPE_SDIO_SHIFT)); 4013a3748dbSHu Ziji sdhci_writel(host, reg, XENON_SYS_CFG_INFO); 4023a3748dbSHu Ziji } 4033a3748dbSHu Ziji } 4043a3748dbSHu Ziji 4053a3748dbSHu Ziji static void xenon_replace_mmc_host_ops(struct sdhci_host *host) 4063a3748dbSHu Ziji { 4073a3748dbSHu Ziji host->mmc_host_ops.set_ios = xenon_set_ios; 4083a3748dbSHu Ziji host->mmc_host_ops.start_signal_voltage_switch = 4093a3748dbSHu Ziji xenon_start_signal_voltage_switch; 4103a3748dbSHu Ziji host->mmc_host_ops.init_card = xenon_init_card; 4113a3748dbSHu Ziji host->mmc_host_ops.execute_tuning = xenon_execute_tuning; 4123a3748dbSHu Ziji host->mmc_host_ops.enable_sdio_irq = xenon_enable_sdio_irq; 4133a3748dbSHu Ziji } 4143a3748dbSHu Ziji 4153a3748dbSHu Ziji /* 4163a3748dbSHu Ziji * Parse Xenon specific DT properties: 4173a3748dbSHu Ziji * sdhc-id: the index of current SDHC. 4183a3748dbSHu Ziji * Refer to XENON_SYS_CFG_INFO register 4193a3748dbSHu Ziji * tun-count: the interval between re-tuning 4203a3748dbSHu Ziji */ 421f29bf660SMarcin Wojtas static int xenon_probe_params(struct platform_device *pdev) 4223a3748dbSHu Ziji { 423f29bf660SMarcin Wojtas struct device *dev = &pdev->dev; 4243a3748dbSHu Ziji struct sdhci_host *host = platform_get_drvdata(pdev); 4253a3748dbSHu Ziji struct mmc_host *mmc = host->mmc; 4263a3748dbSHu Ziji struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 4273a3748dbSHu Ziji struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host); 4283a3748dbSHu Ziji u32 sdhc_id, nr_sdhc; 4293a3748dbSHu Ziji u32 tuning_count; 4303a3748dbSHu Ziji 4313a3748dbSHu Ziji /* Disable HS200 on Armada AP806 */ 432f75fda37SMarcin Wojtas if (priv->hw_version == XENON_AP806) 4333a3748dbSHu Ziji host->quirks2 |= SDHCI_QUIRK2_BROKEN_HS200; 4343a3748dbSHu Ziji 4353a3748dbSHu Ziji sdhc_id = 0x0; 436f29bf660SMarcin Wojtas if (!device_property_read_u32(dev, "marvell,xenon-sdhc-id", &sdhc_id)) { 4373a3748dbSHu Ziji nr_sdhc = sdhci_readl(host, XENON_SYS_CFG_INFO); 4383a3748dbSHu Ziji nr_sdhc &= XENON_NR_SUPPORTED_SLOT_MASK; 4393a3748dbSHu Ziji if (unlikely(sdhc_id > nr_sdhc)) { 4403a3748dbSHu Ziji dev_err(mmc_dev(mmc), "SDHC Index %d exceeds Number of SDHCs %d\n", 4413a3748dbSHu Ziji sdhc_id, nr_sdhc); 4423a3748dbSHu Ziji return -EINVAL; 4433a3748dbSHu Ziji } 4443a3748dbSHu Ziji } 4453a3748dbSHu Ziji priv->sdhc_id = sdhc_id; 4463a3748dbSHu Ziji 4473a3748dbSHu Ziji tuning_count = XENON_DEF_TUNING_COUNT; 448f29bf660SMarcin Wojtas if (!device_property_read_u32(dev, "marvell,xenon-tun-count", 4493a3748dbSHu Ziji &tuning_count)) { 4503a3748dbSHu Ziji if (unlikely(tuning_count >= XENON_TMR_RETUN_NO_PRESENT)) { 4513a3748dbSHu Ziji dev_err(mmc_dev(mmc), "Wrong Re-tuning Count. Set default value %d\n", 4523a3748dbSHu Ziji XENON_DEF_TUNING_COUNT); 4533a3748dbSHu Ziji tuning_count = XENON_DEF_TUNING_COUNT; 4543a3748dbSHu Ziji } 4553a3748dbSHu Ziji } 4563a3748dbSHu Ziji priv->tuning_count = tuning_count; 4573a3748dbSHu Ziji 458f29bf660SMarcin Wojtas return xenon_phy_parse_params(dev, host); 4593a3748dbSHu Ziji } 4603a3748dbSHu Ziji 4613a3748dbSHu Ziji static int xenon_sdhc_prepare(struct sdhci_host *host) 4623a3748dbSHu Ziji { 4633a3748dbSHu Ziji struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 4643a3748dbSHu Ziji struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host); 4653a3748dbSHu Ziji u8 sdhc_id = priv->sdhc_id; 4663a3748dbSHu Ziji 4673a3748dbSHu Ziji /* Enable SDHC */ 4683a3748dbSHu Ziji xenon_enable_sdhc(host, sdhc_id); 4693a3748dbSHu Ziji 4703a3748dbSHu Ziji /* Enable ACG */ 4713a3748dbSHu Ziji xenon_set_acg(host, true); 4723a3748dbSHu Ziji 4733a3748dbSHu Ziji /* Enable Parallel Transfer Mode */ 4743a3748dbSHu Ziji xenon_enable_sdhc_parallel_tran(host, sdhc_id); 4753a3748dbSHu Ziji 4763a3748dbSHu Ziji /* Disable SDCLK-Off-While-Idle before card init */ 4773a3748dbSHu Ziji xenon_set_sdclk_off_idle(host, sdhc_id, false); 4783a3748dbSHu Ziji 4793a3748dbSHu Ziji xenon_mask_cmd_conflict_err(host); 4803a3748dbSHu Ziji 4813a3748dbSHu Ziji return 0; 4823a3748dbSHu Ziji } 4833a3748dbSHu Ziji 4843a3748dbSHu Ziji static void xenon_sdhc_unprepare(struct sdhci_host *host) 4853a3748dbSHu Ziji { 4863a3748dbSHu Ziji struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 4873a3748dbSHu Ziji struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host); 4883a3748dbSHu Ziji u8 sdhc_id = priv->sdhc_id; 4893a3748dbSHu Ziji 4903a3748dbSHu Ziji /* disable SDHC */ 4913a3748dbSHu Ziji xenon_disable_sdhc(host, sdhc_id); 4923a3748dbSHu Ziji } 4933a3748dbSHu Ziji 4943a3748dbSHu Ziji static int xenon_probe(struct platform_device *pdev) 4953a3748dbSHu Ziji { 4963a3748dbSHu Ziji struct sdhci_pltfm_host *pltfm_host; 49715424880SMarcin Wojtas struct device *dev = &pdev->dev; 4983a3748dbSHu Ziji struct sdhci_host *host; 499bb16ea17SGregory CLEMENT struct xenon_priv *priv; 5003a3748dbSHu Ziji int err; 5013a3748dbSHu Ziji 5023a3748dbSHu Ziji host = sdhci_pltfm_init(pdev, &sdhci_xenon_pdata, 5033a3748dbSHu Ziji sizeof(struct xenon_priv)); 5043a3748dbSHu Ziji if (IS_ERR(host)) 5053a3748dbSHu Ziji return PTR_ERR(host); 5063a3748dbSHu Ziji 5073a3748dbSHu Ziji pltfm_host = sdhci_priv(host); 508bb16ea17SGregory CLEMENT priv = sdhci_pltfm_priv(pltfm_host); 5093a3748dbSHu Ziji 510f75fda37SMarcin Wojtas priv->hw_version = (unsigned long)device_get_match_data(&pdev->dev); 511f75fda37SMarcin Wojtas 5123a3748dbSHu Ziji /* 5133a3748dbSHu Ziji * Link Xenon specific mmc_host_ops function, 5143a3748dbSHu Ziji * to replace standard ones in sdhci_ops. 5153a3748dbSHu Ziji */ 5163a3748dbSHu Ziji xenon_replace_mmc_host_ops(host); 5173a3748dbSHu Ziji 51815424880SMarcin Wojtas if (dev->of_node) { 5193a3748dbSHu Ziji pltfm_host->clk = devm_clk_get(&pdev->dev, "core"); 5203a3748dbSHu Ziji if (IS_ERR(pltfm_host->clk)) { 5213a3748dbSHu Ziji err = PTR_ERR(pltfm_host->clk); 5223a3748dbSHu Ziji dev_err(&pdev->dev, "Failed to setup input clk: %d\n", err); 5233a3748dbSHu Ziji goto free_pltfm; 5243a3748dbSHu Ziji } 5253a3748dbSHu Ziji err = clk_prepare_enable(pltfm_host->clk); 5263a3748dbSHu Ziji if (err) 5273a3748dbSHu Ziji goto free_pltfm; 5283a3748dbSHu Ziji 529bb16ea17SGregory CLEMENT priv->axi_clk = devm_clk_get(&pdev->dev, "axi"); 530bb16ea17SGregory CLEMENT if (IS_ERR(priv->axi_clk)) { 531bb16ea17SGregory CLEMENT err = PTR_ERR(priv->axi_clk); 532bb16ea17SGregory CLEMENT if (err == -EPROBE_DEFER) 533bb16ea17SGregory CLEMENT goto err_clk; 534bb16ea17SGregory CLEMENT } else { 535bb16ea17SGregory CLEMENT err = clk_prepare_enable(priv->axi_clk); 5363a3748dbSHu Ziji if (err) 5373a3748dbSHu Ziji goto err_clk; 538bb16ea17SGregory CLEMENT } 53915424880SMarcin Wojtas } 540bb16ea17SGregory CLEMENT 541bb16ea17SGregory CLEMENT err = mmc_of_parse(host->mmc); 542bb16ea17SGregory CLEMENT if (err) 543bb16ea17SGregory CLEMENT goto err_clk_axi; 5443a3748dbSHu Ziji 545f29bf660SMarcin Wojtas sdhci_get_property(pdev); 5463a3748dbSHu Ziji 5473a3748dbSHu Ziji xenon_set_acg(host, false); 5483a3748dbSHu Ziji 549f29bf660SMarcin Wojtas /* Xenon specific parameters parse */ 550f29bf660SMarcin Wojtas err = xenon_probe_params(pdev); 5513a3748dbSHu Ziji if (err) 552bb16ea17SGregory CLEMENT goto err_clk_axi; 5533a3748dbSHu Ziji 5543a3748dbSHu Ziji err = xenon_sdhc_prepare(host); 5553a3748dbSHu Ziji if (err) 556bb16ea17SGregory CLEMENT goto err_clk_axi; 5573a3748dbSHu Ziji 558a027b2c5SZhoujie Wu pm_runtime_get_noresume(&pdev->dev); 559a027b2c5SZhoujie Wu pm_runtime_set_active(&pdev->dev); 560a027b2c5SZhoujie Wu pm_runtime_set_autosuspend_delay(&pdev->dev, 50); 561a027b2c5SZhoujie Wu pm_runtime_use_autosuspend(&pdev->dev); 562a027b2c5SZhoujie Wu pm_runtime_enable(&pdev->dev); 563a027b2c5SZhoujie Wu pm_suspend_ignore_children(&pdev->dev, 1); 564a027b2c5SZhoujie Wu 5653a3748dbSHu Ziji err = sdhci_add_host(host); 5663a3748dbSHu Ziji if (err) 5673a3748dbSHu Ziji goto remove_sdhc; 5683a3748dbSHu Ziji 569a027b2c5SZhoujie Wu pm_runtime_put_autosuspend(&pdev->dev); 570a027b2c5SZhoujie Wu 5713a3748dbSHu Ziji return 0; 5723a3748dbSHu Ziji 5733a3748dbSHu Ziji remove_sdhc: 574a027b2c5SZhoujie Wu pm_runtime_disable(&pdev->dev); 575a027b2c5SZhoujie Wu pm_runtime_put_noidle(&pdev->dev); 5763a3748dbSHu Ziji xenon_sdhc_unprepare(host); 577bb16ea17SGregory CLEMENT err_clk_axi: 578bb16ea17SGregory CLEMENT clk_disable_unprepare(priv->axi_clk); 5793a3748dbSHu Ziji err_clk: 5803a3748dbSHu Ziji clk_disable_unprepare(pltfm_host->clk); 5813a3748dbSHu Ziji free_pltfm: 5823a3748dbSHu Ziji sdhci_pltfm_free(pdev); 5833a3748dbSHu Ziji return err; 5843a3748dbSHu Ziji } 5853a3748dbSHu Ziji 5863a3748dbSHu Ziji static int xenon_remove(struct platform_device *pdev) 5873a3748dbSHu Ziji { 5883a3748dbSHu Ziji struct sdhci_host *host = platform_get_drvdata(pdev); 5893a3748dbSHu Ziji struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 590bb16ea17SGregory CLEMENT struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host); 5913a3748dbSHu Ziji 592a027b2c5SZhoujie Wu pm_runtime_get_sync(&pdev->dev); 593a027b2c5SZhoujie Wu pm_runtime_disable(&pdev->dev); 594a027b2c5SZhoujie Wu pm_runtime_put_noidle(&pdev->dev); 595a027b2c5SZhoujie Wu 5963a3748dbSHu Ziji sdhci_remove_host(host, 0); 5973a3748dbSHu Ziji 5984cc59dffSHu Ziji xenon_sdhc_unprepare(host); 599bb16ea17SGregory CLEMENT clk_disable_unprepare(priv->axi_clk); 6003a3748dbSHu Ziji clk_disable_unprepare(pltfm_host->clk); 6013a3748dbSHu Ziji 6023a3748dbSHu Ziji sdhci_pltfm_free(pdev); 6033a3748dbSHu Ziji 6043a3748dbSHu Ziji return 0; 6053a3748dbSHu Ziji } 6063a3748dbSHu Ziji 607a0fd95b3SHu Ziji #ifdef CONFIG_PM_SLEEP 608a0fd95b3SHu Ziji static int xenon_suspend(struct device *dev) 609a0fd95b3SHu Ziji { 610a0fd95b3SHu Ziji struct sdhci_host *host = dev_get_drvdata(dev); 611a0fd95b3SHu Ziji struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 612a027b2c5SZhoujie Wu struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host); 613a0fd95b3SHu Ziji int ret; 614a0fd95b3SHu Ziji 615a027b2c5SZhoujie Wu ret = pm_runtime_force_suspend(dev); 616a027b2c5SZhoujie Wu 617a027b2c5SZhoujie Wu priv->restore_needed = true; 618a0fd95b3SHu Ziji return ret; 619a0fd95b3SHu Ziji } 620a0fd95b3SHu Ziji #endif 621a0fd95b3SHu Ziji 622a027b2c5SZhoujie Wu #ifdef CONFIG_PM 623a027b2c5SZhoujie Wu static int xenon_runtime_suspend(struct device *dev) 624a027b2c5SZhoujie Wu { 625a027b2c5SZhoujie Wu struct sdhci_host *host = dev_get_drvdata(dev); 626a027b2c5SZhoujie Wu struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 627a027b2c5SZhoujie Wu struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host); 628a027b2c5SZhoujie Wu int ret; 629a027b2c5SZhoujie Wu 630a027b2c5SZhoujie Wu ret = sdhci_runtime_suspend_host(host); 631a027b2c5SZhoujie Wu if (ret) 632a027b2c5SZhoujie Wu return ret; 633a027b2c5SZhoujie Wu 634a027b2c5SZhoujie Wu if (host->tuning_mode != SDHCI_TUNING_MODE_3) 635a027b2c5SZhoujie Wu mmc_retune_needed(host->mmc); 636a027b2c5SZhoujie Wu 637a027b2c5SZhoujie Wu clk_disable_unprepare(pltfm_host->clk); 638a027b2c5SZhoujie Wu /* 639a027b2c5SZhoujie Wu * Need to update the priv->clock here, or when runtime resume 640a027b2c5SZhoujie Wu * back, phy don't aware the clock change and won't adjust phy 641a027b2c5SZhoujie Wu * which will cause cmd err 642a027b2c5SZhoujie Wu */ 643a027b2c5SZhoujie Wu priv->clock = 0; 644a027b2c5SZhoujie Wu return 0; 645a027b2c5SZhoujie Wu } 646a027b2c5SZhoujie Wu 647a027b2c5SZhoujie Wu static int xenon_runtime_resume(struct device *dev) 648a027b2c5SZhoujie Wu { 649a027b2c5SZhoujie Wu struct sdhci_host *host = dev_get_drvdata(dev); 650a027b2c5SZhoujie Wu struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 651a027b2c5SZhoujie Wu struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host); 652a027b2c5SZhoujie Wu int ret; 653a027b2c5SZhoujie Wu 654a027b2c5SZhoujie Wu ret = clk_prepare_enable(pltfm_host->clk); 655a027b2c5SZhoujie Wu if (ret) { 656a027b2c5SZhoujie Wu dev_err(dev, "can't enable mainck\n"); 657a027b2c5SZhoujie Wu return ret; 658a027b2c5SZhoujie Wu } 659a027b2c5SZhoujie Wu 660a027b2c5SZhoujie Wu if (priv->restore_needed) { 661a027b2c5SZhoujie Wu ret = xenon_sdhc_prepare(host); 662a027b2c5SZhoujie Wu if (ret) 663a027b2c5SZhoujie Wu goto out; 664a027b2c5SZhoujie Wu priv->restore_needed = false; 665a027b2c5SZhoujie Wu } 666a027b2c5SZhoujie Wu 667c6303c5dSBaolin Wang ret = sdhci_runtime_resume_host(host, 0); 668a027b2c5SZhoujie Wu if (ret) 669a027b2c5SZhoujie Wu goto out; 670a027b2c5SZhoujie Wu return 0; 671a027b2c5SZhoujie Wu out: 672a027b2c5SZhoujie Wu clk_disable_unprepare(pltfm_host->clk); 673a027b2c5SZhoujie Wu return ret; 674a027b2c5SZhoujie Wu } 675a027b2c5SZhoujie Wu #endif /* CONFIG_PM */ 676a027b2c5SZhoujie Wu 677a027b2c5SZhoujie Wu static const struct dev_pm_ops sdhci_xenon_dev_pm_ops = { 678a027b2c5SZhoujie Wu SET_SYSTEM_SLEEP_PM_OPS(xenon_suspend, 679a027b2c5SZhoujie Wu pm_runtime_force_resume) 680a027b2c5SZhoujie Wu SET_RUNTIME_PM_OPS(xenon_runtime_suspend, 681a027b2c5SZhoujie Wu xenon_runtime_resume, 682a027b2c5SZhoujie Wu NULL) 683a027b2c5SZhoujie Wu }; 684a0fd95b3SHu Ziji 6853a3748dbSHu Ziji static const struct of_device_id sdhci_xenon_dt_ids[] = { 686f75fda37SMarcin Wojtas { .compatible = "marvell,armada-ap806-sdhci", .data = (void *)XENON_AP806}, 687f75fda37SMarcin Wojtas { .compatible = "marvell,armada-cp110-sdhci", .data = (void *)XENON_CP110}, 688f75fda37SMarcin Wojtas { .compatible = "marvell,armada-3700-sdhci", .data = (void *)XENON_A3700}, 6893a3748dbSHu Ziji {} 6903a3748dbSHu Ziji }; 6913a3748dbSHu Ziji MODULE_DEVICE_TABLE(of, sdhci_xenon_dt_ids); 6923a3748dbSHu Ziji 693*471cf454SMarcin Wojtas #ifdef CONFIG_ACPI 694*471cf454SMarcin Wojtas static const struct acpi_device_id sdhci_xenon_acpi_ids[] = { 695*471cf454SMarcin Wojtas { .id = "MRVL0002", XENON_AP806}, 696*471cf454SMarcin Wojtas { .id = "MRVL0003", XENON_AP807}, 697*471cf454SMarcin Wojtas { .id = "MRVL0004", XENON_CP110}, 698*471cf454SMarcin Wojtas {} 699*471cf454SMarcin Wojtas }; 700*471cf454SMarcin Wojtas MODULE_DEVICE_TABLE(acpi, sdhci_xenon_acpi_ids); 701*471cf454SMarcin Wojtas #endif 702*471cf454SMarcin Wojtas 7033a3748dbSHu Ziji static struct platform_driver sdhci_xenon_driver = { 7043a3748dbSHu Ziji .driver = { 7053a3748dbSHu Ziji .name = "xenon-sdhci", 7067320915cSDouglas Anderson .probe_type = PROBE_PREFER_ASYNCHRONOUS, 7073a3748dbSHu Ziji .of_match_table = sdhci_xenon_dt_ids, 708*471cf454SMarcin Wojtas .acpi_match_table = ACPI_PTR(sdhci_xenon_acpi_ids), 709a027b2c5SZhoujie Wu .pm = &sdhci_xenon_dev_pm_ops, 7103a3748dbSHu Ziji }, 7113a3748dbSHu Ziji .probe = xenon_probe, 7123a3748dbSHu Ziji .remove = xenon_remove, 7133a3748dbSHu Ziji }; 7143a3748dbSHu Ziji 7153a3748dbSHu Ziji module_platform_driver(sdhci_xenon_driver); 7163a3748dbSHu Ziji 7173a3748dbSHu Ziji MODULE_DESCRIPTION("SDHCI platform driver for Marvell Xenon SDHC"); 7183a3748dbSHu Ziji MODULE_AUTHOR("Hu Ziji <huziji@marvell.com>"); 7193a3748dbSHu Ziji MODULE_LICENSE("GPL v2"); 720