13a3748dbSHu Ziji /* 23a3748dbSHu Ziji * Driver for Marvell Xenon SDHC as a platform device 33a3748dbSHu Ziji * 43a3748dbSHu Ziji * Copyright (C) 2016 Marvell, All Rights Reserved. 53a3748dbSHu Ziji * 63a3748dbSHu Ziji * Author: Hu Ziji <huziji@marvell.com> 73a3748dbSHu Ziji * Date: 2016-8-24 83a3748dbSHu Ziji * 93a3748dbSHu Ziji * This program is free software; you can redistribute it and/or 103a3748dbSHu Ziji * modify it under the terms of the GNU General Public License as 113a3748dbSHu Ziji * published by the Free Software Foundation version 2. 123a3748dbSHu Ziji * 133a3748dbSHu Ziji * Inspired by Jisheng Zhang <jszhang@marvell.com> 143a3748dbSHu Ziji * Special thanks to Video BG4 project team. 153a3748dbSHu Ziji */ 163a3748dbSHu Ziji 173a3748dbSHu Ziji #include <linux/delay.h> 183a3748dbSHu Ziji #include <linux/ktime.h> 193a3748dbSHu Ziji #include <linux/module.h> 203a3748dbSHu Ziji #include <linux/of.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); 353a3748dbSHu Ziji while (!((reg = sdhci_readw(host, SDHCI_CLOCK_CONTROL)) 363a3748dbSHu Ziji & SDHCI_CLOCK_INT_STABLE)) { 373a3748dbSHu Ziji if (ktime_after(ktime_get(), timeout)) { 383a3748dbSHu Ziji dev_err(mmc_dev(host->mmc), "Internal clock never stabilised.\n"); 393a3748dbSHu Ziji return -ETIMEDOUT; 403a3748dbSHu Ziji } 413a3748dbSHu Ziji usleep_range(900, 1100); 423a3748dbSHu Ziji } 433a3748dbSHu Ziji 443a3748dbSHu Ziji return 0; 453a3748dbSHu Ziji } 463a3748dbSHu Ziji 473a3748dbSHu Ziji /* Set SDCLK-off-while-idle */ 483a3748dbSHu Ziji static void xenon_set_sdclk_off_idle(struct sdhci_host *host, 493a3748dbSHu Ziji unsigned char sdhc_id, bool enable) 503a3748dbSHu Ziji { 513a3748dbSHu Ziji u32 reg; 523a3748dbSHu Ziji u32 mask; 533a3748dbSHu Ziji 543a3748dbSHu Ziji reg = sdhci_readl(host, XENON_SYS_OP_CTRL); 553a3748dbSHu Ziji /* Get the bit shift basing on the SDHC index */ 563a3748dbSHu Ziji mask = (0x1 << (XENON_SDCLK_IDLEOFF_ENABLE_SHIFT + sdhc_id)); 573a3748dbSHu Ziji if (enable) 583a3748dbSHu Ziji reg |= mask; 593a3748dbSHu Ziji else 603a3748dbSHu Ziji reg &= ~mask; 613a3748dbSHu Ziji 623a3748dbSHu Ziji sdhci_writel(host, reg, XENON_SYS_OP_CTRL); 633a3748dbSHu Ziji } 643a3748dbSHu Ziji 653a3748dbSHu Ziji /* Enable/Disable the Auto Clock Gating function */ 663a3748dbSHu Ziji static void xenon_set_acg(struct sdhci_host *host, bool enable) 673a3748dbSHu Ziji { 683a3748dbSHu Ziji u32 reg; 693a3748dbSHu Ziji 703a3748dbSHu Ziji reg = sdhci_readl(host, XENON_SYS_OP_CTRL); 713a3748dbSHu Ziji if (enable) 723a3748dbSHu Ziji reg &= ~XENON_AUTO_CLKGATE_DISABLE_MASK; 733a3748dbSHu Ziji else 743a3748dbSHu Ziji reg |= XENON_AUTO_CLKGATE_DISABLE_MASK; 753a3748dbSHu Ziji sdhci_writel(host, reg, XENON_SYS_OP_CTRL); 763a3748dbSHu Ziji } 773a3748dbSHu Ziji 783a3748dbSHu Ziji /* Enable this SDHC */ 793a3748dbSHu Ziji static void xenon_enable_sdhc(struct sdhci_host *host, 803a3748dbSHu Ziji unsigned char sdhc_id) 813a3748dbSHu Ziji { 823a3748dbSHu Ziji u32 reg; 833a3748dbSHu Ziji 843a3748dbSHu Ziji reg = sdhci_readl(host, XENON_SYS_OP_CTRL); 853a3748dbSHu Ziji reg |= (BIT(sdhc_id) << XENON_SLOT_ENABLE_SHIFT); 863a3748dbSHu Ziji sdhci_writel(host, reg, XENON_SYS_OP_CTRL); 873a3748dbSHu Ziji 883a3748dbSHu Ziji host->mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY; 893a3748dbSHu Ziji /* 903a3748dbSHu Ziji * Force to clear BUS_TEST to 913a3748dbSHu Ziji * skip bus_test_pre and bus_test_post 923a3748dbSHu Ziji */ 933a3748dbSHu Ziji host->mmc->caps &= ~MMC_CAP_BUS_WIDTH_TEST; 943a3748dbSHu Ziji } 953a3748dbSHu Ziji 963a3748dbSHu Ziji /* Disable this SDHC */ 973a3748dbSHu Ziji static void xenon_disable_sdhc(struct sdhci_host *host, 983a3748dbSHu Ziji unsigned char sdhc_id) 993a3748dbSHu Ziji { 1003a3748dbSHu Ziji u32 reg; 1013a3748dbSHu Ziji 1023a3748dbSHu Ziji reg = sdhci_readl(host, XENON_SYS_OP_CTRL); 1033a3748dbSHu Ziji reg &= ~(BIT(sdhc_id) << XENON_SLOT_ENABLE_SHIFT); 1043a3748dbSHu Ziji sdhci_writel(host, reg, XENON_SYS_OP_CTRL); 1053a3748dbSHu Ziji } 1063a3748dbSHu Ziji 1073a3748dbSHu Ziji /* Enable Parallel Transfer Mode */ 1083a3748dbSHu Ziji static void xenon_enable_sdhc_parallel_tran(struct sdhci_host *host, 1093a3748dbSHu Ziji unsigned char sdhc_id) 1103a3748dbSHu Ziji { 1113a3748dbSHu Ziji u32 reg; 1123a3748dbSHu Ziji 1133a3748dbSHu Ziji reg = sdhci_readl(host, XENON_SYS_EXT_OP_CTRL); 1143a3748dbSHu Ziji reg |= BIT(sdhc_id); 1153a3748dbSHu Ziji sdhci_writel(host, reg, XENON_SYS_EXT_OP_CTRL); 1163a3748dbSHu Ziji } 1173a3748dbSHu Ziji 1183a3748dbSHu Ziji /* Mask command conflict error */ 1193a3748dbSHu Ziji static void xenon_mask_cmd_conflict_err(struct sdhci_host *host) 1203a3748dbSHu Ziji { 1213a3748dbSHu Ziji u32 reg; 1223a3748dbSHu Ziji 1233a3748dbSHu Ziji reg = sdhci_readl(host, XENON_SYS_EXT_OP_CTRL); 1243a3748dbSHu Ziji reg |= XENON_MASK_CMD_CONFLICT_ERR; 1253a3748dbSHu Ziji sdhci_writel(host, reg, XENON_SYS_EXT_OP_CTRL); 1263a3748dbSHu Ziji } 1273a3748dbSHu Ziji 1283a3748dbSHu Ziji static void xenon_retune_setup(struct sdhci_host *host) 1293a3748dbSHu Ziji { 1303a3748dbSHu Ziji struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 1313a3748dbSHu Ziji struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host); 1323a3748dbSHu Ziji u32 reg; 1333a3748dbSHu Ziji 1343a3748dbSHu Ziji /* Disable the Re-Tuning Request functionality */ 1353a3748dbSHu Ziji reg = sdhci_readl(host, XENON_SLOT_RETUNING_REQ_CTRL); 1363a3748dbSHu Ziji reg &= ~XENON_RETUNING_COMPATIBLE; 1373a3748dbSHu Ziji sdhci_writel(host, reg, XENON_SLOT_RETUNING_REQ_CTRL); 1383a3748dbSHu Ziji 1393a3748dbSHu Ziji /* Disable the Re-tuning Interrupt */ 1403a3748dbSHu Ziji reg = sdhci_readl(host, SDHCI_SIGNAL_ENABLE); 1413a3748dbSHu Ziji reg &= ~SDHCI_INT_RETUNE; 1423a3748dbSHu Ziji sdhci_writel(host, reg, SDHCI_SIGNAL_ENABLE); 1433a3748dbSHu Ziji reg = sdhci_readl(host, SDHCI_INT_ENABLE); 1443a3748dbSHu Ziji reg &= ~SDHCI_INT_RETUNE; 1453a3748dbSHu Ziji sdhci_writel(host, reg, SDHCI_INT_ENABLE); 1463a3748dbSHu Ziji 1473a3748dbSHu Ziji /* Force to use Tuning Mode 1 */ 1483a3748dbSHu Ziji host->tuning_mode = SDHCI_TUNING_MODE_1; 1493a3748dbSHu Ziji /* Set re-tuning period */ 1503a3748dbSHu Ziji host->tuning_count = 1 << (priv->tuning_count - 1); 1513a3748dbSHu Ziji } 1523a3748dbSHu Ziji 1533a3748dbSHu Ziji /* 1543a3748dbSHu Ziji * Operations inside struct sdhci_ops 1553a3748dbSHu Ziji */ 1563a3748dbSHu Ziji /* Recover the Register Setting cleared during SOFTWARE_RESET_ALL */ 1573a3748dbSHu Ziji static void xenon_reset_exit(struct sdhci_host *host, 1583a3748dbSHu Ziji unsigned char sdhc_id, u8 mask) 1593a3748dbSHu Ziji { 1603a3748dbSHu Ziji /* Only SOFTWARE RESET ALL will clear the register setting */ 1613a3748dbSHu Ziji if (!(mask & SDHCI_RESET_ALL)) 1623a3748dbSHu Ziji return; 1633a3748dbSHu Ziji 1643a3748dbSHu Ziji /* Disable tuning request and auto-retuning again */ 1653a3748dbSHu Ziji xenon_retune_setup(host); 1663a3748dbSHu Ziji 1673a3748dbSHu Ziji xenon_set_acg(host, true); 1683a3748dbSHu Ziji 1693a3748dbSHu Ziji xenon_set_sdclk_off_idle(host, sdhc_id, false); 1703a3748dbSHu Ziji 1713a3748dbSHu Ziji xenon_mask_cmd_conflict_err(host); 1723a3748dbSHu Ziji } 1733a3748dbSHu Ziji 1743a3748dbSHu Ziji static void xenon_reset(struct sdhci_host *host, u8 mask) 1753a3748dbSHu Ziji { 1763a3748dbSHu Ziji struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 1773a3748dbSHu Ziji struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host); 1783a3748dbSHu Ziji 1793a3748dbSHu Ziji sdhci_reset(host, mask); 1803a3748dbSHu Ziji xenon_reset_exit(host, priv->sdhc_id, mask); 1813a3748dbSHu Ziji } 1823a3748dbSHu Ziji 1833a3748dbSHu Ziji /* 1843a3748dbSHu Ziji * Xenon defines different values for HS200 and HS400 1853a3748dbSHu Ziji * in Host_Control_2 1863a3748dbSHu Ziji */ 1873a3748dbSHu Ziji static void xenon_set_uhs_signaling(struct sdhci_host *host, 1883a3748dbSHu Ziji unsigned int timing) 1893a3748dbSHu Ziji { 1903a3748dbSHu Ziji u16 ctrl_2; 1913a3748dbSHu Ziji 1923a3748dbSHu Ziji ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2); 1933a3748dbSHu Ziji /* Select Bus Speed Mode for host */ 1943a3748dbSHu Ziji ctrl_2 &= ~SDHCI_CTRL_UHS_MASK; 1953a3748dbSHu Ziji if (timing == MMC_TIMING_MMC_HS200) 1963a3748dbSHu Ziji ctrl_2 |= XENON_CTRL_HS200; 1973a3748dbSHu Ziji else if (timing == MMC_TIMING_UHS_SDR104) 1983a3748dbSHu Ziji ctrl_2 |= SDHCI_CTRL_UHS_SDR104; 1993a3748dbSHu Ziji else if (timing == MMC_TIMING_UHS_SDR12) 2003a3748dbSHu Ziji ctrl_2 |= SDHCI_CTRL_UHS_SDR12; 2013a3748dbSHu Ziji else if (timing == MMC_TIMING_UHS_SDR25) 2023a3748dbSHu Ziji ctrl_2 |= SDHCI_CTRL_UHS_SDR25; 2033a3748dbSHu Ziji else if (timing == MMC_TIMING_UHS_SDR50) 2043a3748dbSHu Ziji ctrl_2 |= SDHCI_CTRL_UHS_SDR50; 2053a3748dbSHu Ziji else if ((timing == MMC_TIMING_UHS_DDR50) || 2063a3748dbSHu Ziji (timing == MMC_TIMING_MMC_DDR52)) 2073a3748dbSHu Ziji ctrl_2 |= SDHCI_CTRL_UHS_DDR50; 2083a3748dbSHu Ziji else if (timing == MMC_TIMING_MMC_HS400) 2093a3748dbSHu Ziji ctrl_2 |= XENON_CTRL_HS400; 2103a3748dbSHu Ziji sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2); 2113a3748dbSHu Ziji } 2123a3748dbSHu Ziji 21399c14fc3SZhoujie Wu static void xenon_set_power(struct sdhci_host *host, unsigned char mode, 21499c14fc3SZhoujie Wu unsigned short vdd) 21599c14fc3SZhoujie Wu { 21699c14fc3SZhoujie Wu struct mmc_host *mmc = host->mmc; 21799c14fc3SZhoujie Wu u8 pwr = host->pwr; 21899c14fc3SZhoujie Wu 21999c14fc3SZhoujie Wu sdhci_set_power_noreg(host, mode, vdd); 22099c14fc3SZhoujie Wu 22199c14fc3SZhoujie Wu if (host->pwr == pwr) 22299c14fc3SZhoujie Wu return; 22399c14fc3SZhoujie Wu 22499c14fc3SZhoujie Wu if (host->pwr == 0) 22599c14fc3SZhoujie Wu vdd = 0; 22699c14fc3SZhoujie Wu 22799c14fc3SZhoujie Wu if (!IS_ERR(mmc->supply.vmmc)) 22899c14fc3SZhoujie Wu mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, vdd); 22999c14fc3SZhoujie Wu } 23099c14fc3SZhoujie Wu 2313a3748dbSHu Ziji static const struct sdhci_ops sdhci_xenon_ops = { 2323a3748dbSHu Ziji .set_clock = sdhci_set_clock, 23399c14fc3SZhoujie Wu .set_power = xenon_set_power, 2343a3748dbSHu Ziji .set_bus_width = sdhci_set_bus_width, 2353a3748dbSHu Ziji .reset = xenon_reset, 2363a3748dbSHu Ziji .set_uhs_signaling = xenon_set_uhs_signaling, 2373a3748dbSHu Ziji .get_max_clock = sdhci_pltfm_clk_get_max_clock, 2383a3748dbSHu Ziji }; 2393a3748dbSHu Ziji 2403a3748dbSHu Ziji static const struct sdhci_pltfm_data sdhci_xenon_pdata = { 2413a3748dbSHu Ziji .ops = &sdhci_xenon_ops, 2423a3748dbSHu Ziji .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC | 2433a3748dbSHu Ziji SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER | 2443a3748dbSHu Ziji SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, 2453a3748dbSHu Ziji }; 2463a3748dbSHu Ziji 2473a3748dbSHu Ziji /* 2483a3748dbSHu Ziji * Xenon Specific Operations in mmc_host_ops 2493a3748dbSHu Ziji */ 2503a3748dbSHu Ziji static void xenon_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) 2513a3748dbSHu Ziji { 2523a3748dbSHu Ziji struct sdhci_host *host = mmc_priv(mmc); 2533a3748dbSHu Ziji struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 2543a3748dbSHu Ziji struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host); 2553a3748dbSHu Ziji u32 reg; 2563a3748dbSHu Ziji 2573a3748dbSHu Ziji /* 2583a3748dbSHu Ziji * HS400/HS200/eMMC HS doesn't have Preset Value register. 2593a3748dbSHu Ziji * However, sdhci_set_ios will read HS400/HS200 Preset register. 2603a3748dbSHu Ziji * Disable Preset Value register for HS400/HS200. 2613a3748dbSHu Ziji * eMMC HS with preset_enabled set will trigger a bug in 2623a3748dbSHu Ziji * get_preset_value(). 2633a3748dbSHu Ziji */ 2643a3748dbSHu Ziji if ((ios->timing == MMC_TIMING_MMC_HS400) || 2653a3748dbSHu Ziji (ios->timing == MMC_TIMING_MMC_HS200) || 2663a3748dbSHu Ziji (ios->timing == MMC_TIMING_MMC_HS)) { 2673a3748dbSHu Ziji host->preset_enabled = false; 2683a3748dbSHu Ziji host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN; 2693a3748dbSHu Ziji host->flags &= ~SDHCI_PV_ENABLED; 2703a3748dbSHu Ziji 2713a3748dbSHu Ziji reg = sdhci_readw(host, SDHCI_HOST_CONTROL2); 2723a3748dbSHu Ziji reg &= ~SDHCI_CTRL_PRESET_VAL_ENABLE; 2733a3748dbSHu Ziji sdhci_writew(host, reg, SDHCI_HOST_CONTROL2); 2743a3748dbSHu Ziji } else { 2753a3748dbSHu Ziji host->quirks2 &= ~SDHCI_QUIRK2_PRESET_VALUE_BROKEN; 2763a3748dbSHu Ziji } 2773a3748dbSHu Ziji 2783a3748dbSHu Ziji sdhci_set_ios(mmc, ios); 27906c8b667SHu Ziji xenon_phy_adj(host, ios); 2803a3748dbSHu Ziji 2813a3748dbSHu Ziji if (host->clock > XENON_DEFAULT_SDCLK_FREQ) 2823a3748dbSHu Ziji xenon_set_sdclk_off_idle(host, priv->sdhc_id, true); 2833a3748dbSHu Ziji } 2843a3748dbSHu Ziji 2853a3748dbSHu Ziji static int xenon_start_signal_voltage_switch(struct mmc_host *mmc, 2863a3748dbSHu Ziji struct mmc_ios *ios) 2873a3748dbSHu Ziji { 2883a3748dbSHu Ziji struct sdhci_host *host = mmc_priv(mmc); 2893a3748dbSHu Ziji 2903a3748dbSHu Ziji /* 2913a3748dbSHu Ziji * Before SD/SDIO set signal voltage, SD bus clock should be 2923a3748dbSHu Ziji * disabled. However, sdhci_set_clock will also disable the Internal 2933a3748dbSHu Ziji * clock in mmc_set_signal_voltage(). 2943a3748dbSHu Ziji * If Internal clock is disabled, the 3.3V/1.8V bit can not be updated. 2953a3748dbSHu Ziji * Thus here manually enable internal clock. 2963a3748dbSHu Ziji * 2973a3748dbSHu Ziji * After switch completes, it is unnecessary to disable internal clock, 2983a3748dbSHu Ziji * since keeping internal clock active obeys SD spec. 2993a3748dbSHu Ziji */ 3003a3748dbSHu Ziji xenon_enable_internal_clk(host); 3013a3748dbSHu Ziji 302298269c6SHu Ziji xenon_soc_pad_ctrl(host, ios->signal_voltage); 303298269c6SHu Ziji 3043a3748dbSHu Ziji /* 3053a3748dbSHu Ziji * If Vqmmc is fixed on platform, vqmmc regulator should be unavailable. 3063a3748dbSHu Ziji * Thus SDHCI_CTRL_VDD_180 bit might not work then. 3073a3748dbSHu Ziji * Skip the standard voltage switch to avoid any issue. 3083a3748dbSHu Ziji */ 3093a3748dbSHu Ziji if (PTR_ERR(mmc->supply.vqmmc) == -ENODEV) 3103a3748dbSHu Ziji return 0; 3113a3748dbSHu Ziji 3123a3748dbSHu Ziji return sdhci_start_signal_voltage_switch(mmc, ios); 3133a3748dbSHu Ziji } 3143a3748dbSHu Ziji 3153a3748dbSHu Ziji /* 3163a3748dbSHu Ziji * Update card type. 3173a3748dbSHu Ziji * priv->init_card_type will be used in PHY timing adjustment. 3183a3748dbSHu Ziji */ 3193a3748dbSHu Ziji static void xenon_init_card(struct mmc_host *mmc, struct mmc_card *card) 3203a3748dbSHu Ziji { 3213a3748dbSHu Ziji struct sdhci_host *host = mmc_priv(mmc); 3223a3748dbSHu Ziji struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 3233a3748dbSHu Ziji struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host); 3243a3748dbSHu Ziji 3253a3748dbSHu Ziji /* Update card type*/ 3263a3748dbSHu Ziji priv->init_card_type = card->type; 3273a3748dbSHu Ziji } 3283a3748dbSHu Ziji 3293a3748dbSHu Ziji static int xenon_execute_tuning(struct mmc_host *mmc, u32 opcode) 3303a3748dbSHu Ziji { 3313a3748dbSHu Ziji struct sdhci_host *host = mmc_priv(mmc); 3323a3748dbSHu Ziji 3333a3748dbSHu Ziji if (host->timing == MMC_TIMING_UHS_DDR50) 3343a3748dbSHu Ziji return 0; 3353a3748dbSHu Ziji 3363a3748dbSHu Ziji /* 3373a3748dbSHu Ziji * Currently force Xenon driver back to support mode 1 only, 3383a3748dbSHu Ziji * even though Xenon might claim to support mode 2 or mode 3. 3393a3748dbSHu Ziji * It requires more time to test mode 2/mode 3 on more platforms. 3403a3748dbSHu Ziji */ 3413a3748dbSHu Ziji if (host->tuning_mode != SDHCI_TUNING_MODE_1) 3423a3748dbSHu Ziji xenon_retune_setup(host); 3433a3748dbSHu Ziji 3443a3748dbSHu Ziji return sdhci_execute_tuning(mmc, opcode); 3453a3748dbSHu Ziji } 3463a3748dbSHu Ziji 3473a3748dbSHu Ziji static void xenon_enable_sdio_irq(struct mmc_host *mmc, int enable) 3483a3748dbSHu Ziji { 3493a3748dbSHu Ziji struct sdhci_host *host = mmc_priv(mmc); 3503a3748dbSHu Ziji struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 3513a3748dbSHu Ziji struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host); 3523a3748dbSHu Ziji u32 reg; 3533a3748dbSHu Ziji u8 sdhc_id = priv->sdhc_id; 3543a3748dbSHu Ziji 3553a3748dbSHu Ziji sdhci_enable_sdio_irq(mmc, enable); 3563a3748dbSHu Ziji 3573a3748dbSHu Ziji if (enable) { 3583a3748dbSHu Ziji /* 3593a3748dbSHu Ziji * Set SDIO Card Inserted indication 3603a3748dbSHu Ziji * to enable detecting SDIO async irq. 3613a3748dbSHu Ziji */ 3623a3748dbSHu Ziji reg = sdhci_readl(host, XENON_SYS_CFG_INFO); 3633a3748dbSHu Ziji reg |= (1 << (sdhc_id + XENON_SLOT_TYPE_SDIO_SHIFT)); 3643a3748dbSHu Ziji sdhci_writel(host, reg, XENON_SYS_CFG_INFO); 3653a3748dbSHu Ziji } else { 3663a3748dbSHu Ziji /* Clear SDIO Card Inserted indication */ 3673a3748dbSHu Ziji reg = sdhci_readl(host, XENON_SYS_CFG_INFO); 3683a3748dbSHu Ziji reg &= ~(1 << (sdhc_id + XENON_SLOT_TYPE_SDIO_SHIFT)); 3693a3748dbSHu Ziji sdhci_writel(host, reg, XENON_SYS_CFG_INFO); 3703a3748dbSHu Ziji } 3713a3748dbSHu Ziji } 3723a3748dbSHu Ziji 3733a3748dbSHu Ziji static void xenon_replace_mmc_host_ops(struct sdhci_host *host) 3743a3748dbSHu Ziji { 3753a3748dbSHu Ziji host->mmc_host_ops.set_ios = xenon_set_ios; 3763a3748dbSHu Ziji host->mmc_host_ops.start_signal_voltage_switch = 3773a3748dbSHu Ziji xenon_start_signal_voltage_switch; 3783a3748dbSHu Ziji host->mmc_host_ops.init_card = xenon_init_card; 3793a3748dbSHu Ziji host->mmc_host_ops.execute_tuning = xenon_execute_tuning; 3803a3748dbSHu Ziji host->mmc_host_ops.enable_sdio_irq = xenon_enable_sdio_irq; 3813a3748dbSHu Ziji } 3823a3748dbSHu Ziji 3833a3748dbSHu Ziji /* 3843a3748dbSHu Ziji * Parse Xenon specific DT properties: 3853a3748dbSHu Ziji * sdhc-id: the index of current SDHC. 3863a3748dbSHu Ziji * Refer to XENON_SYS_CFG_INFO register 3873a3748dbSHu Ziji * tun-count: the interval between re-tuning 3883a3748dbSHu Ziji */ 3893a3748dbSHu Ziji static int xenon_probe_dt(struct platform_device *pdev) 3903a3748dbSHu Ziji { 3913a3748dbSHu Ziji struct device_node *np = pdev->dev.of_node; 3923a3748dbSHu Ziji struct sdhci_host *host = platform_get_drvdata(pdev); 3933a3748dbSHu Ziji struct mmc_host *mmc = host->mmc; 3943a3748dbSHu Ziji struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 3953a3748dbSHu Ziji struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host); 3963a3748dbSHu Ziji u32 sdhc_id, nr_sdhc; 3973a3748dbSHu Ziji u32 tuning_count; 3983a3748dbSHu Ziji 3993a3748dbSHu Ziji /* Disable HS200 on Armada AP806 */ 4003a3748dbSHu Ziji if (of_device_is_compatible(np, "marvell,armada-ap806-sdhci")) 4013a3748dbSHu Ziji host->quirks2 |= SDHCI_QUIRK2_BROKEN_HS200; 4023a3748dbSHu Ziji 4033a3748dbSHu Ziji sdhc_id = 0x0; 4043a3748dbSHu Ziji if (!of_property_read_u32(np, "marvell,xenon-sdhc-id", &sdhc_id)) { 4053a3748dbSHu Ziji nr_sdhc = sdhci_readl(host, XENON_SYS_CFG_INFO); 4063a3748dbSHu Ziji nr_sdhc &= XENON_NR_SUPPORTED_SLOT_MASK; 4073a3748dbSHu Ziji if (unlikely(sdhc_id > nr_sdhc)) { 4083a3748dbSHu Ziji dev_err(mmc_dev(mmc), "SDHC Index %d exceeds Number of SDHCs %d\n", 4093a3748dbSHu Ziji sdhc_id, nr_sdhc); 4103a3748dbSHu Ziji return -EINVAL; 4113a3748dbSHu Ziji } 4123a3748dbSHu Ziji } 4133a3748dbSHu Ziji priv->sdhc_id = sdhc_id; 4143a3748dbSHu Ziji 4153a3748dbSHu Ziji tuning_count = XENON_DEF_TUNING_COUNT; 4163a3748dbSHu Ziji if (!of_property_read_u32(np, "marvell,xenon-tun-count", 4173a3748dbSHu Ziji &tuning_count)) { 4183a3748dbSHu Ziji if (unlikely(tuning_count >= XENON_TMR_RETUN_NO_PRESENT)) { 4193a3748dbSHu Ziji dev_err(mmc_dev(mmc), "Wrong Re-tuning Count. Set default value %d\n", 4203a3748dbSHu Ziji XENON_DEF_TUNING_COUNT); 4213a3748dbSHu Ziji tuning_count = XENON_DEF_TUNING_COUNT; 4223a3748dbSHu Ziji } 4233a3748dbSHu Ziji } 4243a3748dbSHu Ziji priv->tuning_count = tuning_count; 4253a3748dbSHu Ziji 42606c8b667SHu Ziji return xenon_phy_parse_dt(np, host); 4273a3748dbSHu Ziji } 4283a3748dbSHu Ziji 4293a3748dbSHu Ziji static int xenon_sdhc_prepare(struct sdhci_host *host) 4303a3748dbSHu Ziji { 4313a3748dbSHu Ziji struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 4323a3748dbSHu Ziji struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host); 4333a3748dbSHu Ziji u8 sdhc_id = priv->sdhc_id; 4343a3748dbSHu Ziji 4353a3748dbSHu Ziji /* Enable SDHC */ 4363a3748dbSHu Ziji xenon_enable_sdhc(host, sdhc_id); 4373a3748dbSHu Ziji 4383a3748dbSHu Ziji /* Enable ACG */ 4393a3748dbSHu Ziji xenon_set_acg(host, true); 4403a3748dbSHu Ziji 4413a3748dbSHu Ziji /* Enable Parallel Transfer Mode */ 4423a3748dbSHu Ziji xenon_enable_sdhc_parallel_tran(host, sdhc_id); 4433a3748dbSHu Ziji 4443a3748dbSHu Ziji /* Disable SDCLK-Off-While-Idle before card init */ 4453a3748dbSHu Ziji xenon_set_sdclk_off_idle(host, sdhc_id, false); 4463a3748dbSHu Ziji 4473a3748dbSHu Ziji xenon_mask_cmd_conflict_err(host); 4483a3748dbSHu Ziji 4493a3748dbSHu Ziji return 0; 4503a3748dbSHu Ziji } 4513a3748dbSHu Ziji 4523a3748dbSHu Ziji static void xenon_sdhc_unprepare(struct sdhci_host *host) 4533a3748dbSHu Ziji { 4543a3748dbSHu Ziji struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 4553a3748dbSHu Ziji struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host); 4563a3748dbSHu Ziji u8 sdhc_id = priv->sdhc_id; 4573a3748dbSHu Ziji 4583a3748dbSHu Ziji /* disable SDHC */ 4593a3748dbSHu Ziji xenon_disable_sdhc(host, sdhc_id); 4603a3748dbSHu Ziji } 4613a3748dbSHu Ziji 4623a3748dbSHu Ziji static int xenon_probe(struct platform_device *pdev) 4633a3748dbSHu Ziji { 4643a3748dbSHu Ziji struct sdhci_pltfm_host *pltfm_host; 4653a3748dbSHu Ziji struct sdhci_host *host; 4663a3748dbSHu Ziji struct xenon_priv *priv; 4673a3748dbSHu Ziji int err; 4683a3748dbSHu Ziji 4693a3748dbSHu Ziji host = sdhci_pltfm_init(pdev, &sdhci_xenon_pdata, 4703a3748dbSHu Ziji sizeof(struct xenon_priv)); 4713a3748dbSHu Ziji if (IS_ERR(host)) 4723a3748dbSHu Ziji return PTR_ERR(host); 4733a3748dbSHu Ziji 4743a3748dbSHu Ziji pltfm_host = sdhci_priv(host); 4753a3748dbSHu Ziji priv = sdhci_pltfm_priv(pltfm_host); 4763a3748dbSHu Ziji 4773a3748dbSHu Ziji /* 4783a3748dbSHu Ziji * Link Xenon specific mmc_host_ops function, 4793a3748dbSHu Ziji * to replace standard ones in sdhci_ops. 4803a3748dbSHu Ziji */ 4813a3748dbSHu Ziji xenon_replace_mmc_host_ops(host); 4823a3748dbSHu Ziji 4833a3748dbSHu Ziji pltfm_host->clk = devm_clk_get(&pdev->dev, "core"); 4843a3748dbSHu Ziji if (IS_ERR(pltfm_host->clk)) { 4853a3748dbSHu Ziji err = PTR_ERR(pltfm_host->clk); 4863a3748dbSHu Ziji dev_err(&pdev->dev, "Failed to setup input clk: %d\n", err); 4873a3748dbSHu Ziji goto free_pltfm; 4883a3748dbSHu Ziji } 4893a3748dbSHu Ziji err = clk_prepare_enable(pltfm_host->clk); 4903a3748dbSHu Ziji if (err) 4913a3748dbSHu Ziji goto free_pltfm; 4923a3748dbSHu Ziji 4933a3748dbSHu Ziji err = mmc_of_parse(host->mmc); 4943a3748dbSHu Ziji if (err) 4953a3748dbSHu Ziji goto err_clk; 4963a3748dbSHu Ziji 4973a3748dbSHu Ziji sdhci_get_of_property(pdev); 4983a3748dbSHu Ziji 4993a3748dbSHu Ziji xenon_set_acg(host, false); 5003a3748dbSHu Ziji 5013a3748dbSHu Ziji /* Xenon specific dt parse */ 5023a3748dbSHu Ziji err = xenon_probe_dt(pdev); 5033a3748dbSHu Ziji if (err) 5043a3748dbSHu Ziji goto err_clk; 5053a3748dbSHu Ziji 5063a3748dbSHu Ziji err = xenon_sdhc_prepare(host); 5073a3748dbSHu Ziji if (err) 508bae3dee0SJisheng Zhang goto err_clk; 5093a3748dbSHu Ziji 5103a3748dbSHu Ziji err = sdhci_add_host(host); 5113a3748dbSHu Ziji if (err) 5123a3748dbSHu Ziji goto remove_sdhc; 5133a3748dbSHu Ziji 5143a3748dbSHu Ziji return 0; 5153a3748dbSHu Ziji 5163a3748dbSHu Ziji remove_sdhc: 5173a3748dbSHu Ziji xenon_sdhc_unprepare(host); 5183a3748dbSHu Ziji err_clk: 5193a3748dbSHu Ziji clk_disable_unprepare(pltfm_host->clk); 5203a3748dbSHu Ziji free_pltfm: 5213a3748dbSHu Ziji sdhci_pltfm_free(pdev); 5223a3748dbSHu Ziji return err; 5233a3748dbSHu Ziji } 5243a3748dbSHu Ziji 5253a3748dbSHu Ziji static int xenon_remove(struct platform_device *pdev) 5263a3748dbSHu Ziji { 5273a3748dbSHu Ziji struct sdhci_host *host = platform_get_drvdata(pdev); 5283a3748dbSHu Ziji struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 5293a3748dbSHu Ziji 5303a3748dbSHu Ziji sdhci_remove_host(host, 0); 5313a3748dbSHu Ziji 5324cc59dffSHu Ziji xenon_sdhc_unprepare(host); 5334cc59dffSHu Ziji 5343a3748dbSHu Ziji clk_disable_unprepare(pltfm_host->clk); 5353a3748dbSHu Ziji 5363a3748dbSHu Ziji sdhci_pltfm_free(pdev); 5373a3748dbSHu Ziji 5383a3748dbSHu Ziji return 0; 5393a3748dbSHu Ziji } 5403a3748dbSHu Ziji 5413a3748dbSHu Ziji static const struct of_device_id sdhci_xenon_dt_ids[] = { 5423a3748dbSHu Ziji { .compatible = "marvell,armada-ap806-sdhci",}, 5433a3748dbSHu Ziji { .compatible = "marvell,armada-cp110-sdhci",}, 5443a3748dbSHu Ziji { .compatible = "marvell,armada-3700-sdhci",}, 5453a3748dbSHu Ziji {} 5463a3748dbSHu Ziji }; 5473a3748dbSHu Ziji MODULE_DEVICE_TABLE(of, sdhci_xenon_dt_ids); 5483a3748dbSHu Ziji 5493a3748dbSHu Ziji static struct platform_driver sdhci_xenon_driver = { 5503a3748dbSHu Ziji .driver = { 5513a3748dbSHu Ziji .name = "xenon-sdhci", 5523a3748dbSHu Ziji .of_match_table = sdhci_xenon_dt_ids, 5533a3748dbSHu Ziji .pm = &sdhci_pltfm_pmops, 5543a3748dbSHu Ziji }, 5553a3748dbSHu Ziji .probe = xenon_probe, 5563a3748dbSHu Ziji .remove = xenon_remove, 5573a3748dbSHu Ziji }; 5583a3748dbSHu Ziji 5593a3748dbSHu Ziji module_platform_driver(sdhci_xenon_driver); 5603a3748dbSHu Ziji 5613a3748dbSHu Ziji MODULE_DESCRIPTION("SDHCI platform driver for Marvell Xenon SDHC"); 5623a3748dbSHu Ziji MODULE_AUTHOR("Hu Ziji <huziji@marvell.com>"); 5633a3748dbSHu Ziji MODULE_LICENSE("GPL v2"); 564